The basic strategy is to set up a 2D
projection for drawing controls. You can do this either on top of your 3D
rendering or in overlay planes. If you do so on top of a 3D rendering, you'll
need to redraw the controls at the end of every frame (immediately before
swapping buffers). If you draw into the overlay planes, you only need to redraw
the controls if you're updating them.
To set up a 2D projection, you need to change
the Projection matrix. Normally, it's convenient to set up the projection so one
world coordinate unit is equal to one screen pixel, as follows:
glMatrixMode
(GL_PROJECTION); glLoadIdentity (); gluOrtho2D (0, windowWidth, 0,
windowHeight);
gluOrtho2D() sets up a Z range of -1 to 1, so
you need to use one of the glVertex2*() functions to ensure your geometry isn't
clipped by the zNear or zFar clipping planes.
Normally, the ModelView matrix is set to the
identity when drawing 2D controls, though you may find it convenient to do
otherwise (for example, you can draw repeated controls with interleaved
translation matrices).
If exact pixelization is required, you might
want to put a small translation in the ModelView matrix, as shown below:
glMatrixMode
(GL_MODELVIEW); glLoadIdentity (); glTranslatef (0.375, 0.375, 0.);
If you're drawing on top of a 3D-depth
buffered image, you'll need to somehow disable depth testing while drawing your
2D geometry. You can do this by calling glDisable(GL_DEPTH_TEST) or glDepthFunc
(GL_ALWAYS). Depending on your application, you might also simply clear the
depth buffer before starting the 2D rendering. Finally, drawing all 2D geometry
with a minimum Z coordinate is also a solution.
After the 2D projection is established as
above, you can render normal OpenGL primitives to the screen, specifying their
coordinates with XY pixel addresses (using OpenGL-centric screen coordinates,
with (0,0) in the lower left).