PDA

View Full Version : (3.2) Inline Replacement for the Matrix Stack?



TylerH
11-04-2009, 07:58 PM
Hello there,

This is my first post on these kindly provided forums, but I wanted to note that I am in no way new to OpenGL programming; I have studied and used the OpenGL API for about 5-6 years now.

Anyways, on to my question, or dilemma rather:

I have been in the process of writing a modernized OpenGL-based game engine, and have made fair levels of progress. I recently (yesterday) decided to switch the engine to use OpenGL 3.2 Core Profile entirely, and drop the depreciated features in this particular build of the engine (My personal view on the depreciation, etc. is irrelevant, and I am in fact maintaining a 3.2 Compatibility Profile branch of the engine).

I took notice to the removal of all of the matrix stack functionality, including glLoadMatrix, "MultMatrix, "Translate, "Scale, "Rotate, etc. and have been a bit at a loss now.

I was curious if anyone here could point me in the right direction to write all of my own user-defined stack functionality to replace this?

I presently have an entirely from-scratch math library, and matrix code that supports everything necessary (Ortho, Perspective, Frustum, Rotate, Translate, Scale, Project, Unproject, etc.).

Basically, I wish to find a way to handle a Model View Matrix, Projection Matrix, and Texture Matrix [Normal Matrix as well, if necessary] and be able to pass them properly to my shader pipeline.

I would appreciate anyone's concern or effort on the matter.

Thank You,
Tyler

marshats
11-04-2009, 11:04 PM
This is the question everyone is asking :) I haven't found an inline replacement for matrix stack in the gl3. I have been using the GLM Mathematics library (http://glm.g-truc.net/). There are some simple GLM samples (http://www.g-truc.net/post-0204.html) too. When it comes to set the matix in the shader you will see code like:


glUniformMatrix4fv(UniformMVP, 1, GL_FALSE, &MVP[0][0]);
or equivalently
glUniformMatrix4fv(UniformMVP, 1, GL_FALSE, address(MVP));


I like GLM because it makes explicit the operation rotate/scale/translate/perspective/lookat are really matrix operations. Code using GLM is easy to follow relative to the days prior to gl3 way. If you are good with the way openGL manipulated the matrix stacks before then this will map 1:1. The only added step is that you have to set the uniform in the shader explicitly as mentioned above -- pseudo code will end up looking like


mat4 glm_ProjectionMatrix;
mat4 glm_ModelViewMatrix;

void reshape(GLfloat w, GLfloat h)
{
glViewport(0,0,w,h); // Reset The Current Viewport

//glMatrixMode(GL_PROJECTION); // explicit using glm as follows
glm_ProjectionMatrix = mat4(1.0); //glLoadIdentity
//glm_ProjectionMatrix *= ortho3D(0.f, 2.f, 0.f, 1.f, 0.f, 1.f); // identical to glOrtho(0.f, 2.f, 0.f, 1.f, 0.f, 1.f);
//glm_ProjectionMatrix *= frustum(1.f, 2.f, 1.f, 2.f, 1.0e-45f, 2.f); // identical glFrustum(1.f, 2.f, 1.f, 2.f, 1.f, 2.f)
glm_ProjectionMatrix *= perspective(20.0f,w/h,1.0f,21.0f);

glUniformMatrix4fv(glm_ProjectionMatrix_id, 1, false, &glm_ProjectionMatrix[0][0] );
}

void Draw() // Here's Where We Do All The Drawing
{
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT );

//glMatrixMode(GL_MODELVIEW); // explicit using glm as follows
glm_ModelViewMatrix = mat4(1.0); //glLoadIdentity

glm_ModelViewMatrix *= translate( vec3(0.0, 0.0, 0.0) );
glm_ModelViewMatrix *= rotate(angle, vec3(0.0, 1.0, 0.0) );
glm_ModelViewMatrix *= scale( vec3(0.5, 0.5, 0.5) );
glUniformMatrix4fv(glm_ModelViewMatrix_id, 1, false, address(glm_ModelViewMatrix) ); // set the rotation/translation/scale matrix

glBindVertexArray(vao_id[0]); // select the vertex array object:vao_id[0] by definiton using vertices0,normals0,colors0
glDrawArrays(GL_TRIANGLES, 0, vao_elementcount[0]); // draw the array (at the speed of light)
}



To get the glPush/glPop functionality though I have been using C++ stacks (#include <stack>). So not everything is there in the GLM library.


std::stack<mat4> glm_ModelViewMatrix; //cpu-side

glm_ModelViewMatrix.push(glm_ModelViewMatrix.top() );
glm_ModelViewMatrix.top() *= translate( vec3(0.5, 0.5, 0.0) );
glm_ModelViewMatrix.top() *= rotate(45.0f, vec3(0.0, 0.0, 1.0) );
glm_ModelViewMatrix.top() *= scale( vec3(0.2, 0.2, 0.2) );

glUniformMatrix4fv(glm_ModelViewMatrix_id, 1, false, address(glm_ModelViewMatrix.top()) ); // set the rotation/translation/scale matrix

Draw_vao(0);

glm_ModelViewMatrix.pop();

TylerH
11-05-2009, 03:37 AM
This is very great indeed, thank you!

I hate to start another thread, but I will if I have to:

I am assuming that all the client states have been removed, and VertexAttribPointer has to be used for Position, Normal, Color, etc?

i.e. Bind all your vertex attributes manually?

Thanks Again,
Tyler

Heiko
11-05-2009, 05:23 AM
Indeed, but you will be able to do this within a VertexArrayObject. Meaning at render time you only have to bind the vertex array object and give the draw command.

Setting up vertex attribute pointers and enabling the vertex attribute pointers is done at vertex array object creation time. The vertex array object will store all state related to vertex attributes.

Further: manual is relative of course.
Using a combination of glActiveAttribute, glAttribLocation, glBindAttribLocation, glGetProgramiv (with GL_ACTIVE_ATTRIBUTES and GL_ACTIVE_ATTRIBUTE_MAX_LENGTH) and some consistent vertex attribute naming in your glsl shader code, you should be able to automate this process largely.

TylerH
11-05-2009, 06:57 AM
Thanks Heiko.

I have known how to use VBOs, binding each one and using them as vertex attribute pointers, and then I just draw everything using DrawArrays.

I noticed these Vertex Array Objects, so I will give them a look see when I am back home on my dev PC.

Cheers,
Tyler

Heiko
11-05-2009, 07:59 AM
Using them is quite easy, this example code should get you fired up quickly. Whether you will see performance increases has to be seen though. For what I've read about it so far it doesn't make much difference with not using the VAO's, ymmv of course.

On creation time:

// generate buffers for vertex attributes
GLuint vAttribs[2];
glGenBuffers(2, vAttribs);

// fill first buffer
glBindBuffer(GL_ARRAY_BUFFER, vAttribs[0]);
glBufferData(GL_ARRAY_BUFFER, dataSize[0], dataPtr[0], GL_STATIC_DRAW);

// fill second buffer with indices (optional of course)
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, vAttribs[1]);
glBufferData(GL_ELEMENT_ARRAY_BUFFER, dataSize[1], dataPtr[1], GL_STATIC_DRAW);

// now generate a vertex array object
GLuint vao = 0;
glGenVertexArrays(1, &amp;vao);
glBindVertexArray(vao);

// bind the buffers you want to use for drawing with this VAO
glBindBuffer(GL_ARRAY_BUFFER, vAttribs[0]);

// set your vertex attribute pointers for this buffer
glVertexAttribPointer( ... );

// enable the vertex attribute pointers you are using
glEnableVertexAttribArray( ... );

// bind the element buffer
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, vAttribs[1]);

Now you are ready to use your VAO.
When drawing:


glBindVertexArray(vao);
glDrawElements( ... ); // or other draw commands like glDrawArrays

TylerH
11-05-2009, 05:10 PM
Awesome, thanks.

I originally used glDrawElements, but since I can't pass indices per attribute type, it defeats the purpose in my opinion to use it, so I just call glDrawArrays.

Heiko
11-06-2009, 03:24 AM
Awesome, thanks.

I originally used glDrawElements, but since I can't pass indices per attribute type, it defeats the purpose in my opinion to use it, so I just call glDrawArrays.

Yes, thats a problem indeed. There is a nice discussion about this elsewhere on the forum:
http://www.opengl.org/discussion_boards/ubbthreads.php?ubb=showflat&amp;Number=265744&amp;page=1

I've implemented the calculation of normals for flat shading in the geometry shader as suggested in that topic. Works nice, but probably won't solve all problems.