4

For the last while I've been putting together an OpenGL program, and I've reached the point where I'm coding in my transformation matrices (the model transform, camera transform, and perspective transform). So far, I've been computing my model transform and sending to a uniform and multiplying it in vertex shader. Recently I added the camera and perspective transforms to the matrix that is sent to the uniform in the shader.

But now, a lot of things aren't working and there's a few things I can't understand:

Firstly, this webpage says that OpenGL automatically divides everything by the Z component of position (for use with the perspective transform), but I can't figure out where this happens, and secondly:

There are a number of resources that mention OpenGL functions such as glFrustumf(), glMatrixMode(), glPushMatrix(), glPopMatrix(), glLoadIdentity(), gRotatef() etc. All these functions that seem to pass and modify matrices inside OpenGL, where I wouldn't need to bother with them in the vertex shader at all.

So now I'm thoroughly lost as to how transformations are done in OpenGL. Do we compute them ourselves and multiply them in the vertex shader, or do we send parameters to OpenGL and let it do all the work behind the API, or something else completely different?

Thanks

Community
  • 1
  • 1
Marco Merlini
  • 875
  • 7
  • 29
  • You do the transformations yourself in the vertex shader. Pass the model,view and proj matrix to the vertex shader and set `gl_position=proj*view*model*vec4(in_vertex,1);` – dari Jul 20 '14 at 17:22
  • So then what are all the glFrustumf(), glMatrixMode() etc. functions for? And where does this "divide by Z" happen? – Marco Merlini Jul 20 '14 at 17:24
  • glFrustumf(), glMatrixMode() is from from Opengl 1.x. In modern Opengl (2.0+) you don't use this functions. "Dividing by Z" happens right before the fragment shader. It actually divides by the 'w' coordinate from a vec4. So `vec4(x,y,z,w) -> vec4(x/w,y/w,z/w,1)` – dari Jul 20 '14 at 17:30
  • And how does this "divide by Z" happen then? Do I have to do that myself, as well? – Marco Merlini Jul 20 '14 at 17:32
  • No you don't have to do it yourself. In my first comment you see that `gl_position` is a vec4 and after the transformations the `w` coordinate is not 1. The dividing by `w` happens implicitly. – dari Jul 20 '14 at 17:34
  • and the projection matrix will swap `z` and `w` by its design – ratchet freak Jul 20 '14 at 17:50

2 Answers2

4

Perspective Divison

Once you have transformed your vertices by the ModelViewProjection matrix, they are in clipping space. Without dividing the x, y, and z components by the w component the values are within the range -w to w. Everything outside that range is clipped.

Perspective division (dividing by the w component) brings that range to -1 to 1, known as "normalized device coordinates". This is done automatically by OpenGL on the vertex shader output gl_Position. This (if I recall correctly) is done for the hardware so that all subsequent calculations do not have to account for variable ranges.

Fixed vs. Programmable Pipeline

If you are using uniforms to pass your own transformations to vertex shaders then you are using the modern OpenGL pipeline (yay!). This gives you full control over the transformations of your vertices.

If you are using deprecated functions such as glFrustumf(), glMatrixMode(), glPushMatrix(), glPopMatrix(), glLoadIdentity(), gRotatef(), etc, then you are using the older fixed function pipeline.

Community
  • 1
  • 1
kbirk
  • 3,906
  • 7
  • 48
  • 72
4

glMatrixMode() is used to specify what build-in matrix you are going to manipulate in the subsequent code (i.e. using glTranslate(), glRotate(), etc.). The common parameters are GL_MODELVIEW, GL_PROJECTION, which means model view matrix and projection matrix repectively.

glFrustumf() is used to specify your camera frustum. In OpenGL, anything outside of the frustum is not visible and will be culled, thus helping save a lot of GPU cycles.

Speaking about "divide by Z". It's supposed to be "divide by W", the forth component of the homogenous coordinates. Well, to put it simply, in 3D transformation, OpenGL uses 4-component homogenous coordinates (x,y,z,w) instead of (x,y,z). The transformation matrices, such as model view matrix and projection matrix are all 4 by 4 matrices. The reason to do so is that the 3D transformation like translation, rotation and perspective projection can all be nicely expressed in terms of 4x4 matrix multiplication.

You don't need to worry about dividing by w, either you use fixed pipeline or programmable GLSL pipeline. OpenGL will handle it for you. If you do want to know where it happens, it happens right after multiplying the model view projection matrix with a vertex position. In vertex shader, it happens after:

gl_position=proj*view*model*vec4(in_vertex,1);

somewhere, in the pipeline, between vertex shader and fragment shader. To be exactly, before rasterization happens. Once divide by w, the vertex coordinates are transformed into Normalized Device Coordinates, where the coordinates of all the 3D objects inside the frustum, which you just specified by glFrustum(), are normalized within a [-1,1]x[-1,1]x[-1,1] box. This makes subsequent computation more easier.

Stone
  • 1,119
  • 9
  • 17
  • +1 for correct order of multiplication (matrix by matrix by matrix by column). Math always has been teached with that order, I don't know why someone inverted it at some point :D – CoffeDeveloper Jul 21 '14 at 18:02