2
votes

I'm creating basic OpenGL scene and I have problem with manipulating with my object. Each has different transformation matrix, there's also modelview/translation/scaling matrix for whole scene.

How do I bind this data tomy object before executing calculations from vertex shader? I've read about gl(Push|Pop)Matrix(), but these functions are deprecated from what I understood.

A bit of my code. Position from vertex shader:

gl_Position = gl_ProjectionMatrix * gl_ModelViewMatrix * gl_Vertex;

And C++ function to display objects:

// Clear etc...

mat4 lookAt = glm::lookAt();
glLoadMatrixf(&lookAt[0][0]);
mat4 combined = lookAt * (mat4) sceneTranslation * (mat4) sceneScale;
glLoadMatrixf(&combined[0][0]);

mat4 objectTransform(1.0);
// Transformations...

// No idea if it works, but objects are affected by camera position but not individually scaled, moved etc.
GLuint gl_ModelViewMatrix = glGetUniformLocation(shaderprogram, "gl_ModelViewMatrix");
glUniformMatrix4fv(gl_ModelViewMatrix, 1, GL_FALSE, &objectTransform[0][0]);

// For example
glutSolidCube(1.0);

glutSwapBuffers();
2

2 Answers

6
votes

Well, you dont have to use glLoadMatrix and other built in matrix functions, because it could be even more difficult than handling your own matrixes.

A simple camera example without controlling it, its a static camera:

glm::mat4x4 view_matrix = glm::lookAt(
    cameraPosition,
    cameraPosition+directionVector, //or the focus point the camera is pointing to
    upVector);

It returns a 4x4 matrix, this is the view matrix.

glm::mat4x4 projection_matrix = 
glm::perspective(60.0f, float(screenWidth)/float(screenHeight), 1.0f, 1000.0f);

this is the projection matrix

so now you have the view and projection matrixes, and you can send it to the shader:

gShader->bindShader();
gShader->sendUniform4x4("view_matrix",glm::value_ptr(view_matrix));
gShader->sendUniform4x4("projection_matrix",glm::value_ptr(projection_matrix));

the bindshader is the simple glUseProgram(shaderprog); the uniform program is

void sendUniform4x4(const string& name, const float* matrix, bool transpose=false)
{
    GLuint location = getUniformLocation(name);
    glUniformMatrix4fv(location, 1, transpose, matrix);
}

Your model matrix is individual for each of your objects:

glm::mat4x4 model_matrix= glm::mat4(1); //this is the identity matrix, so its static
model_matrix= glm::rotate(model_matrix,
                          rotationdegree,
                          vec3(axis)); //same as opengl function.

This created a model matrix, and you can send it to your shader too

 gShader->bindShader();
 gShader->sendUniform4x4("model_matrix",glm::value_ptr(model_matrix));

the glm::value_ptr(...) creates a 2 dimensional array of your matrix.

in your shader code don't use the glModelViewMatrix and gl_ProjectionMatrix, matrixes are sent via uniforms.

uniform mat4 projection_matrix;
uniform mat4 view_matrix;
uniform mat4 model_matrix;

void main(){
gl_Position = projection_matrix*view_matrix*model_matrix*gl_Vertex;
//i wrote gl_Vertex because of the glutSolidTeapot.
}

I've never used this build in mesh function so i dont know how it works, supposing it is sending the vertexes to the shader with immediate mode use gl_Vertex. If you create your own meshes use VBO vertexattribpointer and drawarrays/elements.

Don't forget to bind the shader before sending uniforms.

So with a complete example:

glm::mat4x4 view_matrix = glm::lookAt(2,4,2,-1,-1,-1,0,1,0);

glm::mat4x4 projection_matrix = 
    glm::perspective(60.0f, float(screenWidth)/float(screenHeight), 1.0f, 10.0f);

glm::mat4x4 model_matrix= glm::mat4(1); //this remains unchanged
glm::mat4x4 teapot_model_matrix= glm::rotate(model_matrix, //this is the teapots model matrix, apply transformations to this
                              45,
                              glm::vec3(1,1,1));
teapot_model_matrix = glm::scale(teapot_model_matrix,vec3(2,2,2);


gShader->bindShader();
gShader->sendUniform4x4("model_matrix",glm::value_ptr(model_matrix));
gShader->sendUniform4x4("view_matrix",glm::value_ptr(view_matrix));
gShader->sendUniform4x4("projection_matrix",glm::value_ptr(projection_matrix));

glutSolidCube(0.0); //i don't know what the (0.0) stands for :/
glutSwapBuffers();

///////////////////////////////////

in your shader:

uniform mat4 projection_matrix; //these are the matrixes you've sent
uniform mat4 view_matrix;
uniform mat4 model_matrix;

void main(){
gl_Position = projection_matrix*view_matrix*model_matrix*vec4(gl_Vertex.xyz,1);
}

Now you should have a camera positioned at 2,4,2, focusing at -1,-1,-1, and the up vector is pointing up:)

A teapot is rotated by 45 degrees around the (1,1,1) vector, and scaled by 2 in every direction.

After changing the model matrix, send it to the shader, so if you have more objects to render send it after each if you want to have different transformations applied to each mesh. A pseudocode for this looks like:

camera.lookat(camerapostion,focuspoint,updirection); //sets the view
camera.project(fov,aspect ratio,near plane, far plane)  //and projection matrix
camera.sendviewmatrixtoshader;
camera.sendprojectionmatrixtoshader;

obj1.rotate(45 degrees, 1,1,1); //these functions should transform the model matrix of the object. Make sure each one has its own.
obj1.sendmodelmatrixtoshader;
obj2.scale(2,1,1);
obj2.sendmodelmatrixtoshader;

If it doesn't work try it with a vertexBuffer, and a simple triangle or cube created by yourself.

3
votes

You should use a math library, I recommend GLM. It has its matrix functions just like in OpenGL, and uses column major matrixes so you can calculate your owns, and apply them for objects.

First, you should have a matrix class for your scene, which calculates your view matrix, and projection matrix. (glm::lookAt, and glm::project). They work the same as in openGL. You can send them as uniforms to the vertex shader.

For the obejcts, you calculate your own marixes, and send them as the model matrix to the shader(s).

In the shader or on cpu you calculate the mv matrix:

vp = proj*view.

You send your individual model matrixes to the shader and calculate the final position:

gl_Position = vp*m*vec4(vertex.xyz,1);

MODEL MATRIX

with glm, you can easily calculate, transform you matrixes. You create a simple identity matrix:

glm::mat4x4(1) //identity

you can translate, rotate, scale it.

glm::scale
glm::rotate
glm::translate

They work like in immediate mode in opengl.

after you have your matrix send it via the uniform.

MORE MODEL MATRIX

shader->senduniform("proj", camera.projectionmatrix);
shader->senduniform("view", camera.viewmatrix);
glm::mat4 model(1);

obj1.modelmatrix = glm::translate(model,vec3(1,2,1));
shader->senduniform("model", obj1.modelmatrix);
objectloader.render(obj1);

obj2.modelmatrix = glm::rotate(model,obj2.degrees,vec3(obj2.rotationaxis));
shader->senduniform("model", obj2.modelmatrix);
objectloader.render(obj2);

This is just one way to do this. You can write a class for push/pop matrix calculations, automate the method above like this:

obj1.rotate(degrees,vec3(axis)); //this calculates the obj1.modelmatrix for example rotating the identity matrix.
obj1.translate(vec3(x,y,z))//more transform
obj1.render();
//continue with object 2

VIEW MATRIX

the view matrix almost the same as model matrix. Use this to control the global "model matrix", the camera. This transforms your screen globally, and you can have model matrixes for your objects individually.

In my camera class I calculate this with the glm::lookAt(the same as opengl) then send it via uniform to all shaders I use.

Then when I render something I can manipulate its model matrix, rotating or scaling it, but the view matrix is global.

If you want a static object, you don't have to use model matrix on it, you can calculate the position with only:

gl_Position = projmatrix*viewmatrix*staticobjectvertex;

GLOBAL MODEL MATRIX

You can have a global model matrix too.

Use it like

renderer.globmodel.rotate(axis,degree);
renderer.globmodel.scale(x,y,z);

Send it as uniform too, and apply it after the objects' model matrix. (I've used it to render ocean reflections to texture.)

To sum up:

  • create a global view(camera) matrix
  • create a model matrix for each of your sceens, meshes or objects
  • transform the objects' matrixes individually
  • send the projection, model and view matrixes via uniforms to the shader
  • calculate the final position: proj*camera*model*vertex
  • move your objects, and move your camera

I'm not saying there aren't any better way to do this, but this works for me well.

PS: if you'd like some camera class tuts I have a pretty good one;).