Custom rendering pipeline

Started by Daslee, May 02, 2014, 13:21:51

Previous topic - Next topic

Daslee

Hello. I'm trying to make my own rendering pipeline, but can't get it working. I was reading many articles and trying to make my own, but I got problem and I think it is in modelViewProjection matrix calculation. Here is my codes:

public class Main {	
	//Triangle
	private FloatBuffer verticesData;
	private final int strideBytes = 7 * 4;
	private final int positionOffset = 0;
	private final int positionSize = 3;
	private final int colorOffset = 3;
	private final int colorSize = 4;
	
	public Main(){
		try{
			Display.setDisplayMode(new DisplayMode(640, 480));
			Display.create();
		}catch(LWJGLException e){
			e.printStackTrace();
			System.exit(0);
		}
		
		initOpenGL();
		
		while(!Display.isCloseRequested()){
			glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
			
			Pipeline.getInstance().modelIdentity();
			
			verticesData.position(positionOffset);
			glVertexAttribPointer(Pipeline.getInstance().getPositionLocation(), positionSize, false, strideBytes, verticesData);
			glEnableVertexAttribArray(Pipeline.getInstance().getPositionLocation());
			
			verticesData.position(colorOffset);
			glVertexAttribPointer(Pipeline.getInstance().getColorLocation(), colorSize, false, strideBytes, verticesData);
			glEnableVertexAttribArray(Pipeline.getInstance().getColorLocation());
			
			Pipeline.getInstance().updateShaders();
			glDrawArrays(GL_TRIANGLES, 0, 3);
			
			Display.update();
			Display.sync(60);
		}
		Display.destroy();
		System.exit(0);
	}
	
	private void initOpenGL(){
		glViewport(0, 0, 640, 480);
		glEnable(GL_DEPTH_TEST);
		
		//Triangle
		verticesData = BufferUtils.createFloatBuffer(21);
		verticesData.put(new float[]{
				-2.5f, -2.5f, -10,
				1, 0, 0, 1,
				2.5f, -2.5f, -10,
				0, 1, 0, 1,
				0, 2.5f, -10,
				0, 0, 1, 1
		}).position(0);
		
		new Pipeline();
		Pipeline.getInstance().setPerspective(45, 640.0f / 480.0f, 0.1f, 500.0f);
		Pipeline.getInstance().updateShaders();
	}
	
	public static void main(String[] args){
		new Main();
	}
}


public class Pipeline {
	private static Pipeline INSTANCE;
	
	private int program;
	private int MVPMatrixLocation;
	private int positionLocation;
	private int colorLocation;
	
	private Matrix4f view;
	private Matrix4f model;
	private Matrix4f projection;
	
	private FloatBuffer viewBuffer;
	private FloatBuffer modelBuffer;
	private FloatBuffer projectionBuffer;
	private FloatBuffer MVPBuffer;
	
	public Pipeline(){
		INSTANCE = this;
		
		loadShaders();
		
		view = new Matrix4f();
		model = new Matrix4f();
		projection = new Matrix4f();
		
		viewBuffer = BufferUtils.createFloatBuffer(16);
		modelBuffer = BufferUtils.createFloatBuffer(16);
		projectionBuffer = BufferUtils.createFloatBuffer(16);
		MVPBuffer = BufferUtils.createFloatBuffer(16);
	}
	
	private void loadShaders(){
		int vertexShaderHandle = createShader(GL_VERTEX_SHADER, ShaderUtils.loadFile("vertex.vert"));
		int fragmentShaderHandle = createShader(GL_FRAGMENT_SHADER, ShaderUtils.loadFile("fragment.frag"));
		
		int program = glCreateProgram();
		if(program == 0) throw new RuntimeException("Error creating program.");
		glAttachShader(program, vertexShaderHandle);
		glAttachShader(program, fragmentShaderHandle);
		glBindAttribLocation(program, 0, "a_Position");
		glBindAttribLocation(program, 1, "a_Color");
		glLinkProgram(program);
		final int linkStatus = glGetProgrami(program, GL_LINK_STATUS);
		if(linkStatus == 0){
			glDeleteProgram(program);
			throw new RuntimeException("Error linking program.");
		}
		MVPMatrixLocation = glGetUniformLocation(program, "MVPMatrix");
		positionLocation = glGetAttribLocation(program, "a_Position");
		colorLocation = glGetAttribLocation(program, "a_Color");
		glUseProgram(program);
	}
	
	private int createShader(int type, String code){
		int shaderHandle = glCreateShader(type);
		if(shaderHandle == 0) throw new RuntimeException("Error creating "+(type==GL_VERTEX_SHADER?"vertex":"fragment")+" shader.");
		glShaderSource(shaderHandle, code);
		glCompileShader(shaderHandle);
		final int compileStatus = glGetShaderi(shaderHandle, GL_COMPILE_STATUS);
		if(compileStatus == 0){
			glDeleteShader(shaderHandle);
			throw new RuntimeException("Error compiling "+(type==GL_VERTEX_SHADER?"vertex":"fragment")+" shader.");
		}
		return shaderHandle;
	}
	
	public void updateShaders(){
		MVPBuffer.clear();
		calculateMVPMatrix().store(MVPBuffer);
		glUniformMatrix4(MVPMatrixLocation, false, MVPBuffer);
		
		//Print MVP matrix
		/*Matrix4f temp = new Matrix4f();
		Matrix4f.mul(projection, view, temp);
		Matrix4f.mul(temp, model, temp);
		FloatBuffer mvpTemp = BufferUtils.createFloatBuffer(16);
		temp.store(mvpTemp);
		for(int i=0; i<16; i++) System.out.print(mvpTemp.get(i) + " ");
		System.out.println("\n");*/
	}
	
	private Matrix4f calculateMVPMatrix(){
		Matrix4f result = new Matrix4f();
		
		Matrix4f.mul(projection, model, result);
		Matrix4f.mul(result, view, result);
		return result;
	}
	
	/** PROJECTION MATRIX */
	public void setPerspective(float fovy, float aspect, float near, float far){
		/*[cot(fov/2)/a 0          0                      0]
		  [0            cot(fov/2) 0                      0]
		  [0            0          -(f+n)/(f-n)          -1]
		  [0            0          -(2*f*n)/(f-n)         0]
		projection.m00 = (float)(1 / Math.tan(Math.toRadians(fovy / 2)) / aspect);
		projection.m11 = (float)(1 / Math.tan(Math.toRadians(fovy / 2)));
		projection.m22 = (float)(-(far + near) / (far - near));
		projection.m23 = -1;
		projection.m33 = (float)(-(2 * far * near) / (far - near));*/
		
		/*[cot(fov/2)/a 0          0                  0]
		  [0            cot(fov/2) 0                  0]
		  [0            0          -f/(f-n)          -1]
		  [0            0          -f*n/(f-n)         0]*/
		projection.m00 = (float)(1 / Math.tan(Math.toRadians(fovy / 2)) / aspect);
		projection.m11 = (float)(1 / Math.tan(Math.toRadians(fovy / 2)));
		projection.m22 = (float)(-far / (far-near));
		projection.m23 = -1;
		projection.m32 = (float)(-far * near / (far - near));
		projection.m33 = 0;
	}
	
	/** MODEL MATRIX */
	public void modelTranslate(float x, float y, float z){
		model.translate(new Vector3f(x, y, z));
	}
	
	public void modelRotate(float angle, float x, float y, float z){
		model.rotate(angle, new Vector3f(x, y, z));
	}
	
	public void modelIdentity(){
		model.setIdentity();
	}
		
	/** VIEW MATRIX */
	public void cameraTranslate(float x, float y, float z){
		view.translate(new Vector3f(x, y, z));
	}
	
	public void cameraRotate(float angle, float x, float y, float z){
		view.rotate(angle, new Vector3f(x, y, z));
	}
	
	public void cameraIdentity(){
		view.setIdentity();
	}
	
	
	public int getPositionLocation(){
		return positionLocation;
	}
	public int getColorLocation(){
		return colorLocation;
	}
	
	public static Pipeline getInstance(){
		return INSTANCE;
	}
}


Vertex shader:
uniform mat4 MVPMatrix;

attribute vec4 a_Position;
attribute vec4 a_Color;

varying vec4 v_Color;

void main()
{
	v_Color = a_Color;
	gl_Position = MVPMatrix * a_Position;
}


Fragment shader:
varying vec4 v_Color;

void main()
{
	gl_FragColor = v_Color;
}


Now I have only black screen, but if I set projection matrix with gluPerspective and use gl_ModelViewProjectionMatrix in vertex shader instead of MVPMatrix I can see the triangle, but then is used default opengl pipeline where I want to use my own.

And there shouldn't be problem in setting up perspective, because I checked matrices differences between gluPerspective and method which I use, found somewhere in internet. Here is the results:

gluPerspective:
1.81066 0.0 0.0 0.0 0.0 2.4142134 0.0 0.0 0.0 0.0 -1.0004001 -1.0 0.0 0.0 -0.20004001 0.0

My method:
1.8106601 0.0 0.0 0.0 0.0 2.4142137 0.0 0.0 0.0 0.0 -1.0002 -1.0 0.0 0.0 -0.10002001 0.0


There is only 0.1 difference in before last number, but I think that it's not the reason why it won't show me the triangle. So maybe someone knows where did I made mistake?

broumbroum

You may use GLcheckError() to list any errors that can occur within your rendering pipeline. At least one check at end of the loop is recommended, because opengl doesn't "throw exceptions" like Java does.

glcheckError may be called as often as necessary, that is after each block of code, e.g. after the matrix manipulations and after binding a shader program.

Thereby you log errors, and you may be able to find where your code is bugging.
If you intend to externalize the pipeline into another process, then
Display.makeCurrent();
performs a Thread change. By default the rendering Thread is the one that
Display.create();
has called.

Daslee

Okay, I've tried to put Util.checkGLError(); at the end of main loop, glGetShaderInfoLog in creating shaders, glGetProgramInfoLog in creating program, but they print only empty lines, so I think that there is no opengl errors. I even tried to put glGetError at the end of main loop, but it's result is always 0.

broumbroum

I followed steps of your setPerspective.. It turns out that your calculations are wrong, because you must be careful with such type casting like you do
(float)(1/cot(fov/2))*aspect)


These code typings with primitive types lead to precision loss. The following gives better results :
1f/(float)cot(fov/2f)*aspect

Thereby the calculations are processed on the floating point unit, straight to the floating point address with a reduced loss of precision.

Daslee

I found my problem, it was here:

public void updateShaders(){
	MVPBuffer.clear();
	calculateMVPMatrix().store(MVPBuffer);
	glUniformMatrix4(MVPMatrixLocation, false, MVPBuffer);
}


After storing mvp matrix to mvp matrix's FloatBuffer, I must flip the buffer before calling glUniformMatrix4.  ;D

Perspective calculations was correct, but still thanks for those type casting explanations.  :)