Search code examples
c#openglopentk

OpenGL transformations not working as expected


I'm writing an OpenGL application (C# and OpenTK) and I'm confused about how OpenGL transformations work.

I've set up a perspective projection and I'm at the default location with the default orientation (+X to the right, +Y up, +Z coming at me.) Now I draw a quad in my XY plane, on the Z-axis at -10.

GL.Begin(PrimitiveType.Quads);
GL.Color3(Color.Green);
GL.Vertex3(-1, 1, -10);
GL.Vertex3(1, 1, -10);
GL.Vertex3(1, -1, -10);
GL.Vertex3(-1, -1, -10);
GL.End();

This works as expected. But now I want to rotate the quad along its local Y-axis, so I added in a rotate, which is being applied to my identity matrix. Here's the relevant section of code:

GL.MatrixMode(MatrixMode.Modelview);
GL.LoadIdentity();

GL.Rotate(10, 0, 1, 0);

GL.Begin(PrimitiveType.Quads);
GL.Color3(Color.Green);
GL.Vertex3(-1, 1, -10);
GL.Vertex3(1, 1, -10);
GL.Vertex3(1, -1, -10);
GL.Vertex3(-1, -1, -10);
GL.End();

But this will rotate the plane around my "world" Y-axis, so now the plane is no longer speared through my -Z axis, but it's at an angle.

Question

How can I keep the object at the desired location (at -10 along my Z axis) but have it rotated about its own axis?

What I've tried

I've tried first translating to the origin, performing the rotation, drawing, then moving back, but this doesn't work either, presumably because once I've rotated, now I'm no longer translating along the "world" axis, but the rotated axis. This is the code:

GL.MatrixMode(MatrixMode.Modelview);
GL.LoadIdentity();

GL.Translate(0, 0, 10);
GL.Rotate(10, 0, 1, 0);

GL.Begin(PrimitiveType.Quads);
GL.Color3(Color.Green);
GL.Vertex3(-1, 1, -10);
GL.Vertex3(1, 1, -10);
GL.Vertex3(1, -1, -10);
GL.Vertex3(-1, -1, -10);
GL.End();

GL.Translate(0, 0, -10);

I have a feeling I need to be using PushMatrix and PopMatrix, but I'm not quite sure I understand how they come into play. If I push my matrix before performing any operations, then pop it, shouldn't my view return back to normal? If so, why doesn't this work:

GL.MatrixMode(MatrixMode.Modelview);
GL.LoadIdentity();

// Push current matrix onto the stack
GL.PushMatrix();

// Perform operations on newly pushed matrix
GL.Translate(0, 0, 10);
GL.Rotate(10, 0, 1, 0);

GL.Begin(PrimitiveType.Quads);
GL.Color3(Color.Green);
GL.Vertex3(-1, 1, -10);
GL.Vertex3(1, 1, -10);
GL.Vertex3(1, -1, -10);
GL.Vertex3(-1, -1, -10);
GL.End();

// Pop it off, so return to my previous matrix
GL.PopMatrix();

Solution

  • You were on the right track with your second attempt, but it still misses a couple of aspects:

    • All transformations need to be specified before you start drawing. Only transformations that are active at the time the draw call is made will be applied to the coordinates in that draw call. Having a translation after glEnd() will do nothing.
    • The order is backwards. The transformation specified last is applied to the vertices first. So the translation that moves your geometry to the origin needs to be specified last, and the one that moves it back to its position needs to be specified first.

    The code sequence will then look like this:

    GL.Translate(0, 0, -10);
    GL.Rotate(10, 0, 1, 0);
    GL.Translate(0, 0, 10);
    
    GL.Begin(PrimitiveType.Quads);
    ...
    GL.End();
    

    The PushMatrix() and PopMatrix() are not needed in this code example because you call LoadIdentity() at the start of your draw function, which will restore the current transformation to the identity transform. And at least based on what's shown, you are not drawing anything after this code, so there is no need to restore the previous matrix.

    An alternative is to not call LoadIdentity() for each frame, but instead bracket the code segment above with PushMatrix() at the start and PopMatrix() at the end. That's in fact more extendable, because it restores the matrices for additional rendering if needed.

    I explained some of this in more detail in an answer to a similar question here: Rotating an object around a fixed point using glMultMatrix.