When does homogenization occur?

Hey All,

I’ll start by outlining the issue, and then afterwards provide some code samples to give context to my problem. However, the meat of my question is this: at what point in the graphics pipeline does homogenization occur, and is the developer expected to do this manually?

Basically I’ve got some 2-D squares that I can draw by creating a VAO and using a simple vertex/fragment shader. As of now each square has a MV matrix, and there is one projection matrix. The projection matrix is orthographic, and this seems to work fine for getting things onto the screen.

However, if I want my objects to move back and forth in the z direction I’ve got to use a different projection, and I’ve used GLM’s frustum projection to give myself a volume for my squares to exist (GLM_GTC_matrix_transform: Matrix transform functions.). Using this matrix makes my objects fail to appear on screen.

By printing out both my orthographic and frustum projection matrices I can see that they look reasonable, given the information I’ve found on the subject (OpenGL Projection Matrix).

However, I notice the following: the orthographic matrix is constructed in such a way that any operation of a homogeneous point (i,e point.w=1) should preserve that property after the operation. This is not the case with the frustum matrix (nor is it with perspective, I think), which leads me to believe I need to take care of it myself (i.e divide out the w component from my final result.)

Adding that simple division to my vertex shader code did not fix my issue, but I feel as though I’m on the right path. It’s difficult for me to be sure, however; it could be that I’m forgetting to enable something like GL_DEPTH_TEST or setting a depth function.

Here is the code I use to create the VAO (note that Drawable is a class of my own design that contains the VAO as well as the MV matrix and a few other pertinents, and JShader is the shader program I am using. the “getHandle” methods return handles to the position and texture coordinate variables in the shader)


Drawable initQuad(JShader& shader){
   const int nVert=4, dim=3, nIndices=4;
   const int vStride = nVert*dim*sizeof(GLint);
   const int tStride = nVert*2*sizeof(GLfloat);
   const int iStride = nIndices*sizeof(GLuint);

   Drawable dr;

   const GLint vertices[nVert][dim] = {
      {0, 0, 0}, {40, 0, 0},
      {0, 40, 0}, {40, 40, 0}
   };

   //why 3?
   const GLfloat texCoords[nVert][3] = {
      {0.f, 0.f}, {1.f, 0.f},
      {0.f, 1.f}, {1.f, 1.f}
   };
   const GLuint indices[nIndices] = {0, 1, 2, 3};

   GLuint tmpVAO;
   glGenVertexArrays(1, &tmpVAO);
   glBindVertexArray(tmpVAO);

   GLuint buffers[3];
   glGenBuffers(3, buffers);

   //vertices
   glBindBuffer(GL_ARRAY_BUFFER, buffers[0]);
   glBufferData(GL_ARRAY_BUFFER, vStride, vertices, GL_STATIC_DRAW);
   glEnableVertexAttribArray(shader.getPosHandle());
   glVertexAttribPointer(shader.getPosHandle(), dim, GL_INT, 0, 0, 0);

   //tex coords
   glBindBuffer(GL_ARRAY_BUFFER, buffers[1]);
   glBufferData(GL_ARRAY_BUFFER, tStride, texCoords, GL_STATIC_DRAW);
   glEnableVertexAttribArray(shader.getTexCoordHandle());
   glVertexAttribPointer(shader.getTexCoordHandle(), dim, GL_FLOAT, 0, 0, 0);

   //indices
   glBindBuffer( GL_ELEMENT_ARRAY_BUFFER, buffers[2] );
   glBufferData( GL_ELEMENT_ARRAY_BUFFER, iStride, indices, GL_STATIC_DRAW );

   glBindVertexArray(0);

   dr.setVAO(tmpVAO);

   return dr;
}

I initialize my projection matrices like so:


glm::mat4 proj = glm::ortho<GLfloat>(-200.f, 200.f, -200.f, 200.0, -200.f, 200.f);
glm::mat4 proj_F = glm::frustum<GLfloat>(-200.f, 200.f, -200.f, 200.0, -200.f, 200.f);

And when printed they look like this

Ortho:
[ 0.005, 0, 0, 0, ]
[ 0, 0.005, 0, 0, ]
[ 0, 0, -0.005, 0, ]
[ -0, -0, -0, 1, ]

Frustum:
[ -1, 0, 0, 0, ]
[ 0, -1, 0, 0, ]
[ 0, 0, -0, -1, ]
[ 0, 0, 200, 0, ]

Note that, aside from sign differences(which could be the issue) the factor of 200 should ensure similar mappings in x and y into screen space (although the frustum matrix should divide out 200 * z from everything.) After declaring this matrix I immediately send it to the shader.

I draw things with glDrawElements after sending the MV matrix to the shader. My shader code, initially, looked like this:

Vertex:


#version 130

uniform mat4 projMat;
uniform mat4 MVMat;

attribute vec4 vPosition;
attribute vec2 a_TexCoord;
varying vec2 v_TexCoord;

void main(){
   v_TexCoord = a_TexCoord;
   gl_Position = projMat * MVMat * vPosition;
}

Fragment:


#version 130

precision mediump float;

varying vec2 v_TexCoord;
uniform sampler2D u_Texture;

uniform vec4 fColor;

void main(){
   gl_FragColor = fColor * texture2D(u_Texture, v_TexCoord);
}

After trying to manually homogenize, my vertex shader looks like


#version 130

uniform mat4 projMat;
uniform mat4 MVMat;

attribute vec4 vPosition;
attribute vec2 a_TexCoord;
varying vec2 v_TexCoord;

void main(){
   v_TexCoord = a_TexCoord;
   gl_Position = projMat * MVMat * vPosition;
   gl_Position /= gl_Position.w;
}

To no avail. I had a hypothesis that my vPosition variable had a w value of 0 by default, but I tried manually setting it to 1 and got nowhere.

If you actually read all that I appreciate it. This is a pretty simply issue: basically I’m trying to move into 3-D having got what I needed from basic 2-D examples, but the projection matrices are tripping me up. It could be the fact that things aren’t getting homogenized, but it could also be me not enabling some OpenGL functionality during initialization (or a million other things.)

I’ve consulted the following links, but the information I found there seemed a bit contradictory.

Any advice helps. Also if you have any code pointers/GLSL pointers (I know people use in and out these days…) I’d like to hear those as well.

Thanks,

John

Disclaimer: I already posted this in the Beginner forum, so if that’s a no no please let me know. When does homogenization occur? - OpenGL: Basic Coding - Khronos Forums

Hey All,

I’ll start by outlining the issue, and then afterwards provide some code samples to give context to my problem. However, the meat of my question is this: at what point in the graphics pipeline does homogenization occur, and is the developer expected to do this manually?

Basically I’ve got some 2-D squares that I can draw by creating a VAO and using a simple vertex/fragment shader. As of now each square has a MV matrix, and there is one projection matrix. The projection matrix is orthographic, and this seems to work fine for getting things onto the screen.

However, if I want my objects to move back and forth in the z direction I’ve got to use a different projection, and I’ve used GLM’s frustum projection to give myself a volume for my squares to exist (GLM_GTC_matrix_transform: Matrix transform functions.). Using this matrix makes my objects fail to appear on screen.

By printing out both my orthographic and frustum projection matrices I can see that they look reasonable, given the information I’ve found on the subject (OpenGL Projection Matrix).

However, I notice the following: the orthographic matrix is constructed in such a way that any operation of a homogeneous point (i,e point.w=1) should preserve that property after the operation. This is not the case with the frustum matrix (nor is it with perspective, I think), which leads me to believe I need to take care of it myself (i.e divide out the w component from my final result.)

Adding that simple division to my vertex shader code did not fix my issue, but I feel as though I’m on the right path. It’s difficult for me to be sure, however; it could be that I’m forgetting to enable something like GL_DEPTH_TEST or setting a depth function.

Here is the code I use to create the VAO (note that Drawable is a class of my own design that contains the VAO as well as the MV matrix and a few other pertinents, and JShader is the shader program I am using. the “getHandle” methods return handles to the position and texture coordinate variables in the shader)


Drawable initQuad(JShader& shader){
   const int nVert=4, dim=3, nIndices=4;
   const int vStride = nVert*dim*sizeof(GLint);
   const int tStride = nVert*2*sizeof(GLfloat);
   const int iStride = nIndices*sizeof(GLuint);

   Drawable dr;

   const GLint vertices[nVert][dim] = {
      {0, 0, 0}, {40, 0, 0},
      {0, 40, 0}, {40, 40, 0}
   };

   //why 3?
   const GLfloat texCoords[nVert][3] = {
      {0.f, 0.f}, {1.f, 0.f},
      {0.f, 1.f}, {1.f, 1.f}
   };
   const GLuint indices[nIndices] = {0, 1, 2, 3};

   GLuint tmpVAO;
   glGenVertexArrays(1, &tmpVAO);
   glBindVertexArray(tmpVAO);

   GLuint buffers[3];
   glGenBuffers(3, buffers);

   //vertices
   glBindBuffer(GL_ARRAY_BUFFER, buffers[0]);
   glBufferData(GL_ARRAY_BUFFER, vStride, vertices, GL_STATIC_DRAW);
   glEnableVertexAttribArray(shader.getPosHandle());
   glVertexAttribPointer(shader.getPosHandle(), dim, GL_INT, 0, 0, 0);

   //tex coords
   glBindBuffer(GL_ARRAY_BUFFER, buffers[1]);
   glBufferData(GL_ARRAY_BUFFER, tStride, texCoords, GL_STATIC_DRAW);
   glEnableVertexAttribArray(shader.getTexCoordHandle());
   glVertexAttribPointer(shader.getTexCoordHandle(), dim, GL_FLOAT, 0, 0, 0);

   //indices
   glBindBuffer( GL_ELEMENT_ARRAY_BUFFER, buffers[2] );
   glBufferData( GL_ELEMENT_ARRAY_BUFFER, iStride, indices, GL_STATIC_DRAW );

   glBindVertexArray(0);

   dr.setVAO(tmpVAO);

   return dr;
}

I initialize my projection matrices like so:


glm::mat4 proj = glm::ortho<GLfloat>(-200.f, 200.f, -200.f, 200.0, -200.f, 200.f);
glm::mat4 proj_F = glm::frustum<GLfloat>(-200.f, 200.f, -200.f, 200.0, -200.f, 200.f);

And when printed they look like this

Ortho:
[ 0.005, 0, 0, 0, ]
[ 0, 0.005, 0, 0, ]
[ 0, 0, -0.005, 0, ]
[ -0, -0, -0, 1, ]

Frustum:
[ -1, 0, 0, 0, ]
[ 0, -1, 0, 0, ]
[ 0, 0, -0, -1, ]
[ 0, 0, 200, 0, ]

Note that, aside from sign differences(which could be the issue) the factor of 200 should ensure similar mappings in x and y into screen space (although the frustum matrix should divide out 200 * z from everything.) After declaring this matrix I immediately send it to the shader.

I draw things with glDrawElements after sending the MV matrix to the shader. My shader code, initially, looked like this:

Vertex:


#version 130

uniform mat4 projMat;
uniform mat4 MVMat;

attribute vec4 vPosition;
attribute vec2 a_TexCoord;
varying vec2 v_TexCoord;

void main(){
   v_TexCoord = a_TexCoord;
   gl_Position = projMat * MVMat * vPosition;
}

Fragment:


#version 130

precision mediump float;

varying vec2 v_TexCoord;
uniform sampler2D u_Texture;

uniform vec4 fColor;

void main(){
   gl_FragColor = fColor * texture2D(u_Texture, v_TexCoord);
}

After trying to manually homogenize, my vertex shader looks like


#version 130

uniform mat4 projMat;
uniform mat4 MVMat;

attribute vec4 vPosition;
attribute vec2 a_TexCoord;
varying vec2 v_TexCoord;

void main(){
   v_TexCoord = a_TexCoord;
   gl_Position = projMat * MVMat * vPosition;
   gl_Position /= gl_Position.w;
}

To no avail. I had a hypothesis that my vPosition variable had a w value of 0 by default, but I tried manually setting it to 1 and got nowhere.

If you actually read all that I appreciate it. This is a pretty simply issue: basically I’m trying to move into 3-D having got what I needed from basic 2-D examples, but the projection matrices are tripping me up. It could be the fact that things aren’t getting homogenized, but it could also be me not enabling some OpenGL functionality during initialization (or a million other things.)

I’ve consulted the following links, but the information I found there seemed a bit contradictory.

Any advice helps. Also if you have any code pointers/GLSL pointers (I know people use in and out these days…) I’d like to hear those as well.

Thanks,

John

Because you are writing to gl_Position in the vertex shader, the perspective divide by w happens automatically by GL after the Vertex shader executes and before the fragment shader, or other shader stages. Therefore you do not need gl_Position /= gl_Position.w

As you are not ‘seeing’ anything I would start by checking the formation of your MV matrix, projection matrix and then ensure you understand the effect of using a translate (x,y,z) as part of your MV matrix.

However, I notice the following: the orthographic matrix is constructed in such a way that any operation of a homogeneous point (i,e point.w=1) should preserve that property after the operation. This is not the case with the frustum matrix (nor is it with perspective, I think), which leads me to believe I need to take care of it myself (i.e divide out the w component from my final result.)

This homogenous conversion is called “perspective divide” and it is automatically handled after the vertex shader. What happens is that the vertices’ w value is returned to 1 by dividing x, y, and z by w, which causes the perspective effect. Mathematically this is a 4D skew operation to stretch the projection frustum back into a cube from (-1,-1,-1) to (1,1,1). This is why w is 1 in a orthographic projection–because orthographic projections don’t show depth since an orthographic volume isn’t a frustum, it’s a rectangular prism and the change can occur with only scaling.

It looks like you are specifying a negative “near” plane range in your frustum. This should be positive since it how much distance from the apex of the frustum you cut off to where the near plane is. I’ve always used something like glm::perspective to create frustums since to me it helps me better visualize how the projection for the camera is acting.

On a side note, you can’t use two VBOs (one for positions and one for texture coordinates) since they both bind to GL_ARRAY_BUFFER and only one is bound at a time. You should “interleave” the data by writing (position0)(texCoord0)(position1)(texCoord1)…etc. into one contiguous float array and then specify the texCoord offset with

glVertexAttribPointer(shader.getTexCoordHandle(), dim, GL_FLOAT, 0, 0, sizeof(float)*3);

to point to it in your data.

[QUOTE=MtRoad;1260645]
On a side note, you can’t use two VBOs (one for positions and one for texture coordinates) since they both bind to GL_ARRAY_BUFFER and only one is bound at a time. You should “interleave” the data by writing (position0)(texCoord0)(position1)(texCoord1)…etc. into one contiguous float array and then specify the texCoord offset with

glVertexAttribPointer(shader.getTexCoordHandle(), dim, GL_FLOAT, 0, 0, sizeof(float)*3);

to point to it in your data.[/QUOTE]

This is incorrect. You certainly can use multiple buffers to supply streams for each vertex attribute. I understand where your confusion is coming from: you are binding a buffer to GL_ARRAY_ BUFFER, so it must by definition unbind the previous one, right? Well, what you are missing is the way VAOs work.

As the documentation in the man pages ( OpenGL 4 Reference Pages ) for glVertexAttribPointer states:

If pointer is not NULL, a non-zero named buffer object must be bound to the GL_ARRAY_BUFFER target (see glBindBuffer), otherwise an error is generated. pointer is treated as a byte offset into the buffer object’s data store. The buffer object binding (GL_ARRAY_BUFFER_BINDING) is saved as generic vertex attribute array state (GL_VERTEX_ATTRIB_ARRAY_BUFFER_BINDING) for index index.

Keyword here being saved. Once this method is called, the current buffer ID bound to GL_ARRAY_BUFFER is associated to the vertex attribute and this association is stored as VAO state, so the next time you bind a VAO it will rebind all the right buffers to the right vertex streams.

On another note, interleaved buffers (which is what you proposes) have a number of useful reasons to exist and in general better cache coherence is cited as one of the strong points. Individual streams also have their uses, one of which is that (at least in theory) it would allow to reduce the bandwidth required if you use the same data source with different shaders whose attribute inputs vary (for example in one shader you only draw with the positions while in another you use a full set with multiple tex coords and animation weights, etc).

I have yet to find a situation where using one or the other has given me any visible advantage, so I assume that as with everything this is a situational choice.

Thanks for correcting me and bringing me up to speed on that. Awesome, I really need to use VAOs.

It also answers a question I had on if you could do implement something similar to GPU Gems 2: “Optimizing Resource Management with Multi-Streaming” in OpenGL.

Thanks for the reply. Just for the record, it turns out that the matrices I was printing out were the transposes of the actual matrices (does glm do this by default?)

Anyway, I’ve got it working now. Basically the frustum matrix I was creating had a zrange that requires negative values, which I can’t explain at all. In other words, a projection matrix generated by


glm::frustum<GLfloat>(0.f, 400.f, 0.f, 300.f, 10.f, 100.f);

will ensure that a point at say (200, 150, -55) will show up. I’ll take the fact that I’m seeing something as a small victory and now try to build up some intuition on these matrices.

Neat, glad that got cleared up. I may try interleaving my data just for kicks.

MtRoad, I think you were correct in that I wasn’t declaring the frustum matrix properly. I did some tests and found that the frustum matrix was mapping points outside of the acceptable screen range (between <-1, -1, -1, 1> and <1, 1, 1, 1> I assume?)

I will try using the perspective function soon as I agree that is a bit better in terms of understanding how the scene is seen (ha), but can you give me some details on how the frustum matrix works? Basically the way I got it working was by making the zrange between 10 and 100, but in order for things to be visible I need to keep them at negative z values. Furthermore my x and y bounds have become completely altered, as though the entire scene has been rotated somehow. Looking at the frustum matrix, however, it appears to be a pretty standard perspective projection (as per songho’s post.)

Sorry, none of that really made sense. I’m glad to be seeing something, I just need to understand it. It seems that a projection matrix generated by


glm::frustum<GLfloat>(0.f, 400.f, 0.f, 300.f, 10.f, 100.f);

will ensure that a point at say (200, 150, -55) will show up. I don’t quite understand that given the range I specified, but I’ll take the fact that I’m seeing something as a small victory and now try to build up some intuition on these matrices.