Replacement for gl_Position = gl_ModelViewProjectionMatrix * gl_Vertex;

12,164

Solution 1

I find that when thinking about modern OpenGL it is best to forget that glMatrixMode ever existed.

With that in mind, let's go over what you need for the most basic draw operation: a replacement for gl_ModelViewProjectionMatrix. As it's name implies this is a combination of 3 different matrices: the model matrix, the view matrix, and the projection matrix.

So what you'll need in your shader to accomodate this is 3 uniform variables of type mat4. Which you'll use like so:

uniform mat4 projMat;
uniform mat4 viewMat;
uniform mat4 modelMat;

layout (location = 0) in vec3 position;

void main()
{
    gl_Position = projMat * viewMat * modelMat * vec4(position, 1.0);
}

This bit of shader code performs the same function as the one you had above. What changed is the built-in gl_ModelViewProjectionMatrix was replaced by 3 uniform variables (which could be combined as one if you make sure to multiply them yourself on the C++ side before passing it in). And the builtin gl_Vertex was replaced by an input variable.

On the C++ side you will need to do 2 things. First you'll need to get the location for each of these uniforms:

GLuint modelMatIdx = glGetUniformLocation(shaderProgId, "modelMat");
GLuint viewMatIdx = glGetUniformLocation(shaderProgId, "viewMat");
GLuint projMatIdx = glGetUniformLocation(shaderProgId, "projMat");

And with this in hand you can now pass in the values for each uniform right before drawing using glUniformMatrix4fv.

One particular library which makes this particularly easy is glm. For example to get the same projection matrix as in your example you would do:

glm::mat4 projMat = glm::frustum(-RProjZ, +RProjZ, -Aspect*RProjZ, +Aspect*RProjZ, 1.0, 32768.0);

and you would pass it in like so:

glUniformMatrix4fv(projMatIdx, 1, GL_FALSE, glm::value_ptr(projMat));

Now that you know how, I'd like to address the issue of "when". You said you weren't clear about the matrix mode stuff and that brings me back to my earlier assertion of "forget about it". The matrix mode was there so that you could tell opengl which built in should be affected by calls to OpenGL matrix operations such as glTranslate, glFrustum and so on, but all of this is gone now. You are now in charge of managing the (possibly many) matrices involved. All you have to do is pass them in before you draw (as I've shown above) and you'll be fine. Just make sure the program is bound before you attempt to modify its uniforms.

Here's a working example (if you're suprised by gl::... instead of gl... it's because I'm using an opengl header generated by glLoadGen which puts all of the opengl API functions in the gl namespace).

GLuint simpleProgramID;
// load the shader and make the program

GLuint modelMatIdx = gl::GetUniformLocation(simpleProgramID, "modelMat");
GLuint viewMatIdx = gl::GetUniformLocation(simpleProgramID, "viewMat");
GLuint projMatIdx = gl::GetUniformLocation(simpleProgramID, "projMat");

GLuint vaoID;
gl::GenVertexArrays(1, &vaoID);
gl::BindVertexArray(vaoID);

GLuint vertBufferID, indexBufferID;
gl::GenBuffers(1, &vertBufferID);
gl::GenBuffers(1, &indexBufferID);

struct Vec2 { float x, y; };
struct Vec3 { float x, y, z; };
struct Vert { Vec3 pos; Vec2 tex; };

std::array<Vert, 8> cubeVerts = {{
    { {  0.5f,  0.5f,  0.5f }, { 1.0f, 0.0f } }, { {  0.5f,  0.5f, -0.5f }, { 1.0f, 1.0f } },
    { {  0.5f, -0.5f, -0.5f }, { 0.0f, 1.0f } }, { {  0.5f, -0.5f,  0.5f }, { 0.0f, 0.0f } },
    { { -0.5f,  0.5f,  0.5f }, { 0.0f, 0.0f } }, { { -0.5f,  0.5f, -0.5f }, { 0.0f, 1.0f } },
    { { -0.5f, -0.5f, -0.5f }, { 1.0f, 1.0f } }, { { -0.5f, -0.5f,  0.5f }, { 1.0f, 0.0f } }
}};

std::array<unsigned int, 36> cubeIdxs = {{ 
    0, 2, 1, 0, 3, 2, // Right
    4, 5, 6, 4, 6, 7, // Left
    0, 7, 3, 0, 4, 7, // Top
    1, 6, 2, 1, 5, 6, // Bottom
    0, 5, 1, 0, 4, 5, // Front
    3, 7, 6, 3, 6, 2  // Back
}};

// Vertex buffer
gl::BindBuffer(gl::ARRAY_BUFFER, vertBufferID);
gl::BufferData(gl::ARRAY_BUFFER, sizeof(Vert) * cubeVerts.size(), cubeVerts.data(), gl::STATIC_DRAW);
gl::EnableVertexAttribArray(0); // Matches layout (location = 0)
gl::VertexAttribPointer(0, 3, gl::FLOAT, gl::FALSE_, sizeof(Vert), 0);
gl::EnableVertexAttribArray(1); // Matches layout (location = 1)
gl::VertexAttribPointer(1, 2, gl::FLOAT, gl::FALSE_, sizeof(Vert), (GLvoid*)sizeof(Vec3));

// Index buffer
gl::BindBuffer(gl::ELEMENT_ARRAY_BUFFER, indexBufferID);
gl::BufferData(gl::ELEMENT_ARRAY_BUFFER, sizeof(unsigned int) * cubeIdxs.size(), cubeIdxs.data(), gl::STATIC_DRAW);
gl::BindVertexArray(0);

glm::mat4 projMat = glm::perspective(56.25f, 16.0f/9.0f, 0.1f, 100.0f);
glm::mat4 viewMat = glm::lookAt(glm::vec3(5, 5, 5), glm::vec3(0, 0, 0), glm::vec3(0, 0, 1));
glm::mat4 modelMat; // identity

while (!glfwWindowShouldClose(window))
{
    gl::Clear(gl::COLOR_BUFFER_BIT | gl::DEPTH_BUFFER_BIT);

    gl::UseProgram(simpleProgramID);
    gl::UniformMatrix4fv(projMatIdx, 1, gl::FALSE_, glm::value_ptr(projMat));
    gl::UniformMatrix4fv(viewMatIdx, 1, gl::FALSE_, glm::value_ptr(viewMat));
    gl::UniformMatrix4fv(modelMatIdx, 1, gl::FALSE_, glm::value_ptr(modelMat));

    gl::BindVertexArray(vaoID);
    gl::DrawElements(gl::TRIANGLES, 36, gl::UNSIGNED_INT, 0);
    gl::BindVertexArray(0);

    gl::UseProgram(0);

    glfwSwapBuffers(window);
    glfwPollEvents();
}

Associated Vertex Shader:

//[VERTEX SHADER]
#version 430

uniform mat4 projMat;
uniform mat4 viewMat;
uniform mat4 modelMat;

layout (location = 0) in vec3 in_position; // matches gl::EnableVertexAttribArray(0);
layout (location = 1) in vec2 in_uv; // matches gl::EnableVertexAttribArray(1);

out vec2 uv;

void main()
{
    gl_Position = projMat * viewMat * modelMat * vec4(in_position, 1.0);
    uv = in_uv;
}

And finally Fragment shader:

//[FRAGMENT SHADER]
#version 430

in vec2 uv;

out vec4 color;

void main()
{
    color = vec4(uv, 0.0, 1.0);
}

The resulting image is:

enter image description here

Solution 2

Well, I agree that most OpenGL tutorials confuse bits of deprecated and non-deprecated stuff. To get you in the right direction, let me explain.

gl_ModelViewProjectionMatrix, gl_ModeView, glMatrixMode() and the matrix stack glPushMatrix() glPopMatrix() are deprecated. You need to define your own matrices as a uniform variables then set and pass them to the shader using glUniform*.

gl_Vertex is also deprecated, actually the whole fixed attributes names are deprecated. Alternatively you need to define your own attribute names and bind them to specific locations. Then you can set their values using glVertexAttribPointer by passing the attribute location to it (Full explanation here). For example:

glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 0, vertices); // for vertices
glVertexAttribPointer(1, 4, GL_FLOAT, GL_FALSE, 0, color); // for color

And for the shader code

layout (location = 0) in vec4 vertex;
layout (location = 1) in vec4 color;

uniform mat4 modelview;
uniform mat4 projection;

void main()
{
gl_Position = projection* modelview* vertex;
}

For the attributes locations you can set them in the shader code as I did, or from OpenGL API using glBindAttribLocation.

Managing uniform variables can be somehow tricky if you are used to the old OpenGL globals variables such as gl_ModelView I wrote an article that hopefully can help you manage uniform variables for a big project.

Share:
12,164
Gnampf
Author by

Gnampf

hobby coder for non commercial projects, just for fun and to keep my brain busy.

Updated on June 04, 2022

Comments

  • Gnampf
    Gnampf almost 2 years

    There are a couple of questions like this, but I still haven't really understood. I was coding with OpenGL over 10 years ago and noticed how difficult it is to get into modern OpenGL. The OpenGL.org page is a horrible mess when it comes to examples, you never know what version it is, any version seems to be mixed up in various code examples. Alright, I have an old code I want to update to OpenGL >3 at least. So first thing I did was to move on from glVertex3fv to finally make it with glVertexAttribPointer (over a step with glVertexPointer until I read this is deprecated now as well). This works out fine, but when trying to place textures I got stuck quickly and I assume it is because of wrong positioning and I wanted to get rid of c++ code :

    glMatrixMode( GL_PROJECTION );
    glLoadIdentity();
    glFrustum( -RProjZ, +RProjZ, -Aspect*RProjZ, +Aspect*RProjZ, 1.0, 32768.0 );
    

    and to draw it

    // bind vertex buffer
    glBindBuffer(GL_ARRAY_BUFFER, VertBuffer);
    glBufferData(GL_ARRAY_BUFFER, sizeof(float) * size, verts, GL_STATIC_DRAW);
    
    // enable arrays
    glEnableVertexAttribArray(0); 
    
    // set pointers
    glVertexAttribPointer(0,3,GL_FLOAT, GL_FALSE, sizeof(float) * floatsPerVertex, 0);
    
    // render ComplexSurface
    glDrawArrays(GL_TRIANGLE_FAN, 0, size);
    glDisableVertexAttribArray(0);
    

    with in the vertexshader

    gl_Position = gl_ModelViewProjectionMatrix * gl_Vertex; 
    

    And everything is working magically. Now don't get me wrong, I'm a big fan of magic, but... Then I found a couple of matrix conversions which can be used to get a matrix to replace glFrustum, but whenever I try to replace it, it fails badly (although I think I understood the maths behind glFrustum and the conversion into the matrix).

    What tried is something like

    buildPerspProjMat(g_ProjView,FovAngle,Aspect,1.0,32768.0 );
    
    glUseProgram(g_program);
    glUniformMatrix4fv(g_programFrustum, 1, GL_FALSE, g_ProjView );
    glUseProgram(0);
    

    and using the position in the shader from the buffer above with the projection matix, but this doesn't work out at all.

    So what I plain don't get now is where to replace this and with what in the shader. I don't know at which point the glMatrixMode takes place and "when" to replace it with some uniform matrix (passing the args as uniform ain't the problem here). I can't count how many tutorials I read already, but I always get confused over all the mixed versions. I am always happy about some code examples, but please OpenGL 3 or higher.

    The next would be a replacement for glTexCoord2f for texturing, but that's a different story :)