Using LWJGL's Matrix4f class for rotation/translation

Started by TranquilMarmot, January 19, 2012, 01:42:21

Previous topic - Next topic

TranquilMarmot

I'm trying to draw a triangle using only shaders. That means no glRotate(), glTranslate(), or anything else relating to the immediate mode matrices.
I noticed thatLWJGL's Matrix4f class has all the methods that I need (rotate(), translate(), etc.) and a convenient way to store it into a FloatBuffer (store()).
I could render a triangle just fine, but when I tried to start using a rotation matrix things stopped working (the dreaded blank screen of doom).

In my vertex shader, I have
uniform mat4 RotationMatrix


Every rendering loop I set it to a rotated Matrix4f (stored in a FloatBuffer) using the following code:
// create a new matrix
Matrix4f rotationMatrix = new Matrix4f();
// set it to the identity matrix
rotationMatrix.setIdentity();
// rotate it by angle along the Z axis
rotationMatrix = rotationMatrix.rotate(angle, new Vector3f(0.0f, 0.0f, 1.0f));

// buffer for transferring matrix to shader
FloatBuffer rotBuffer = BufferUtils.createFloatBuffer(16);
// store the matrix in the buffer
rotationMatrix.store(rotBuffer);

// get the location of RotationMatrix
int location = GL20.glGetUniformLocation(program.getHandle(), "RotationMatrix");

if(location >= 0){
	// set the rotation matrix
	GL20.glUniformMatrix4(location, false, rotBuffer);
}


To calculate the position in the vertex shader, I use
gl_Position = RotationMatrix * vec4(VertexPosition,1.0);

I know that the shaders are all compiled and linked properly, and the RotationMatrix shows up in the program's active uniforms list.

So what am I doing wrong here? I tried using the transpose of the matrix and it didn't help. Is using LWJGL's Matrix4f class a bad idea for this sort of stuff?

Here's my vertex shader (I know my fragment shader works):
#version 400

layout (location = 0) in vec3 VertexPosition;
layout (location = 1) in vec3 VertexColor;

out vec3 Color;

uniform mat4 RotationMatrix;

void main()
{
    Color = VertexColor;
    gl_Position = RotationMatrix * vec4(VertexPosition,1.0);
}


And my initialization and rendering loop (window is created elsewhere, GLSLProgram and GLSLShader are classes I wrote to compile and link shaders with):
package spaceguts.graphics.glsl;

import java.nio.FloatBuffer;
import java.nio.IntBuffer;

import org.lwjgl.BufferUtils;
import org.lwjgl.opengl.GL11;
import org.lwjgl.opengl.GL15;
import org.lwjgl.opengl.GL20;
import org.lwjgl.opengl.GL30;
import org.lwjgl.util.vector.Matrix4f;
import org.lwjgl.util.vector.Vector3f;

import spaceguts.util.DisplayHelper;
import spaceguts.util.resources.Paths;

public class GLSLRender {
	static int vaoHandle = 0;
	
	private static GLSLProgram program;
	private static float angle = 30.0f;
	
	public static void render(){
		GL11.glClear(GL11.GL_COLOR_BUFFER_BIT);
		
		/* BEGIN CODE THAT DOESN'T WORK */
		// create a new matrix
		Matrix4f rotationMatrix = new Matrix4f();
		// set it to the identity matrix
		rotationMatrix.setIdentity();
		// rotate it by angle along the Z axis
		rotationMatrix = rotationMatrix.rotate(angle, new Vector3f(0.0f, 0.0f, 1.0f));
		// increase angle
		//angle += 0.05f;
		
		// buffer for transferring matrix to shader
		FloatBuffer rotBuffer = BufferUtils.createFloatBuffer(16);
		// store the matrix in the buffer
		rotationMatrix.store(rotBuffer);
		
		// get the location of RotationMatrix
		int location = GL20.glGetUniformLocation(program.getHandle(), "RotationMatrix");
		
		if(location >= 0){
			// set the rotation matrix
			GL20.glUniformMatrix4(location, false, rotBuffer);
		} else{
			System.out.println("RotationMatrix uniform not found");
		}
		/* END CODE THAT DOESN'T WORK */
		
		// draw triangle
		GL30.glBindVertexArray(vaoHandle);
		GL11.glDrawArrays(GL11.GL_TRIANGLES, 0, 3);
	}
	
	public static void initGL(){
		// set the viewport
		GL11.glViewport(0, 0, DisplayHelper.windowWidth, DisplayHelper.windowHeight);
		
		// set the clear color
		GL11.glClearColor(0.2f,0.2f,0.2f,1.0f);
		
		// create vertex shader
		GLSLShader vertShader = new GLSLShader(ShaderTypes.VERTEX);
		String vertFile = Paths.SHADER_PATH.path() + "basic_uniform.vert";
		if(!vertShader.compileShaderFromFile(vertFile))
			System.out.println(vertShader.log());
		
		// create fragment shader
		GLSLShader fragShader = new GLSLShader(ShaderTypes.FRAGMENT);
		String fragFile = Paths.SHADER_PATH.path() + "basic_uniform.frag";
		if(!fragShader.compileShaderFromFile(fragFile)){
			System.out.println(fragShader.log());
		}
		
		// create and use program
		program = new GLSLProgram();
		program.addShader(fragShader);
		program.addShader(vertShader);
		program.link();
		program.use();
		
		program.printActiveUniforms();
		program.printActiveAttribs();
		
		/* BEGIN VERTEX ARRAY */
		// create vertex buffer object handles
		IntBuffer vboHandles = BufferUtils.createIntBuffer(2);
		GL15.glGenBuffers(vboHandles);
		
		// create poisition data
		int positionBufferHandle = vboHandles.get(0);
		FloatBuffer positionBuffer = BufferUtils.createFloatBuffer(9);
		float[] positionData = {
				-0.8f, -0.8f, 0.0f,
		         0.8f, -0.8f, 0.0f,
		         0.0f,  0.8f, 0.0f };
		positionBuffer.put(positionData);
		positionBuffer.rewind();
		// point the vertex buffer at the position data
		GL15.glBindBuffer(GL15.GL_ARRAY_BUFFER, positionBufferHandle);
		GL15.glBufferData(GL15.GL_ARRAY_BUFFER, positionBuffer, GL15.GL_STATIC_DRAW);
		
		// create color data
		int colorBufferHandle = vboHandles.get(1);
		FloatBuffer colorBuffer = BufferUtils.createFloatBuffer(9);
		float[] colorData = {
		        1.0f, 0.0f, 0.0f,
		        0.0f, 1.0f, 0.0f,
		        0.0f, 0.0f, 1.0f };
		colorBuffer.put(colorData);
		colorBuffer.rewind();
		// point the vertex buffer at the color data
		GL15.glBindBuffer(GL15.GL_ARRAY_BUFFER, colorBufferHandle);
		GL15.glBufferData(GL15.GL_ARRAY_BUFFER, colorBuffer, GL15.GL_STATIC_DRAW);
		
		// generate vertex array object
		vaoHandle = GL30.glGenVertexArrays();
		GL30.glBindVertexArray(vaoHandle);
		
		// enable 0 (VertexPosition) and 1 (VertexColor) - see bindAttribLocation call
		GL20.glEnableVertexAttribArray(0);
		GL20.glEnableVertexAttribArray(1);
		
		GL15.glBindBuffer(GL15.GL_ARRAY_BUFFER, positionBufferHandle);
		// 0 (VertexPosition) has three elements per vertex, is of type float, is not normalized, the data is tightly packed (0 stride), and there's no offset
		GL20.glVertexAttribPointer(0, 3, GL11.GL_FLOAT, false, 0, 0L);
		
		GL15.glBindBuffer(GL15.GL_ARRAY_BUFFER, colorBufferHandle);
		// 1 (VertexColor) has three elements per vertex, is of type float, is not normalized, it tightly packed, and there's no offset
		GL20.glVertexAttribPointer(1, 3, GL11.GL_FLOAT, false, 0, 0L);
	}
}

TranquilMarmot

Nevermind, I figured it out using quaternions (which I'm using to represent rotation anyway). Here's the code, if anybody is interested (don't you hate when somebody figures something out and then just posts a reply that says 'Nevermind, I figured it out'? If you figured it out, help the poor soul that stumbled into your thread!)

Here's my render method again, working this time!
public static void render(){
GL11.glClear(GL11.GL_COLOR_BUFFER_BIT);

// create a new matrix
Matrix4f rotationMatrix = new Matrix4f();
// set it to the identity matrix
rotationMatrix.setIdentity();
// rotate it by angle along the Z axis
rotationMatrix = rotationMatrix.rotate((float) Math.toDegrees((double)angle), new Vector3f(0.0f, 0.0f, 1.0f));
// increase angle
//angle += 0.05f;

// buffer for transferring matrix to shader
FloatBuffer rotBuffer = BufferUtils.createFloatBuffer(16);

// create a quaternion and rotate it
Quaternion quat = new Quaternion(0.0f, 0.0f, 0.0f, 1.0f);
quat = QuaternionHelper.rotateZ(quat, angle);
QuaternionHelper.toFloatBuffer(quat, rotBuffer);

// get the location of RotationMatrix
int location = GL20.glGetUniformLocation(program.getHandle(), "RotationMatrix");

if(location >= 0){
	// set the rotation matrix
	GL20.glUniformMatrix4(location, false, rotBuffer);
} else{
	System.out.println("RotationMatrix uniform not found");
}

// draw triangle
GL30.glBindVertexArray(vaoHandle);
GL11.glDrawArrays(GL11.GL_TRIANGLES, 0, 3);
}


Here's QuaternionHelper.rotateZ() (this can easily be adapted for any axis by changing where 'sinVal' is in the second quaternion)
public static Quaternion rotateZ(Quaternion quat, float amount) {
	Quaternion ret = new Quaternion(0.0f, 0.0f, 0.0f, 1.0f);
	double radHalfAngle = Math.toRadians((double) amount) / 2.0;
	float sinVal = (float) Math.sin(radHalfAngle);
	float cosVal = (float) Math.cos(radHalfAngle);
	Quaternion rot = new Quaternion(0.0f, 0.0f, sinVal, cosVal);
	Quaternion.mul(quat, rot, ret);
	return ret;
}


And here's QuaternionHelper.toFloatBuffer()
public static void toFloatBuffer(Quaternion quat, FloatBuffer dest) {
	if (!dest.isDirect()) {
		System.out
				.println("QuaternionHelper toFloatBuffer was passed an indirect FloatBuffer!");
	} else if (dest.capacity() != 16) {
		System.out
				.println("QuaternionHelper toFloatBuffer was passed a buffer of the incorrect size!");
	} else {
		dest.clear();

		float x = quat.x;
		float y = quat.y;
		float z = quat.z;
		float w = quat.w;

		float x2 = x * x;
		float y2 = y * y;
		float z2 = z * z;
		float xy = x * y;
		float xz = x * z;
		float yz = y * z;
		float wx = w * x;
		float wy = w * y;
		float wz = w * z;

		dest.put(1.0f - 2.0f * (y2 + z2));
		dest.put(2.0f * (xy - wz));
		dest.put(2.0f * (xz + wy));
		dest.put(0.0f);
		dest.put(2.0f * (xy + wz));
		dest.put(1.0f - 2.0f * (x2 + z2));
		dest.put(2.0f * (yz - wx));
		dest.put(0.0f);
		dest.put(2.0f * (xz - wy));
		dest.put(2.0f * (yz + wx));
		dest.put(1.0f - 2.0f * (x2 + y2));
		dest.put(0.0f);
		dest.put(0.0f);
		dest.put(0.0f);
		dest.put(0.0f);
		dest.put(1.0f);

		dest.rewind();
	}
}