Deconstructing the OpenGL Pipeline in illiTorpot

28oct12

 


 

1. Introduction

This lesson teases the OpenGL pipeline structure from the code for illiTorpot, which itself is a development of the standard illiSkel. The latter has been the standard template code for almost all C/OpenGL projects for illiMath in the decade 1994-2006. No complete illiSkel exists at this time for OpenGL/Python, although it should be straightforward to develop the torus.py with the missing functionality.

1.1. Prerequisites

The reader should be able to read C-code at the beginner's level, and should have access to a way of recompiling skel.cpp and torpot.cpp on their computer. On a PC running Windows, the torpot.cpp adapted for Windows compiles with the native Windows compiler mvc98 (provided in the illiMath Winaid package) using the win.mf makefile filter. On a mac, the mac.mf will recompile the unix adapted torpot.cpp. The mac.mf filter has been adapted in the past to a variety of Linux distros as lnx.mf , but these are very specific to the particular way your flavor of Linux handles the graphics system on your PC.

1.2. Torpot versus Skel

The illiSkel, shortened to skel.c, skel.cpp, skel.py etc and compiled to just skel in Unix, and to skel.exe in Windows, has a simple pipeline, whose deconstruction we leave to the reader as an exercise in understanding the present lesson.

1.3. Versions

On Windows PCs, use repo:
class198f12/resources/tecraD/geodesy/skel.c
with mvc98 or other Windows compilers (modify win.mf to suit). The commandline entry for mvc98, reads:
nmake -f win.mf skel.exe

 

On a Mac (at least through OSX10.6) use repo:
class198f12/resources/illimac/skel/skel.c and
class198f12/resources/illimac/toport/toport.c .
The commandline entry on a prepared mac reads:
make -f mac.mf torpot .

 

2. Analysis of the Code

2.1. Where do we start the deconstruction?

The function drawcons() must be the display master for two reasons:
  • The command swapbuffers() is called at its very end. This signals OpenGL to switch display buffers at the next opportunity.
  • The overall master function main() identifies drawcons() as the display callback .
So we start we always start with the main() function.

 

If you understood the terms in this introduction, skip the following explanantion of them. OpenGL specifies that your graphics card is double buffered. That means, OpenGL draws data (pixel color, transparency etc) into one display buffer, while the other display buffer is repeatedly copied to your video system.When this is complete (anywhere from 100 milliseconds or less, to hours for Hollywood quality computer animations), OpenGL redirects the computer's video system to display the new display buffer. The old is dealt with by your program. By default, it is erased and everything is redrawn from scratch. You can alter that for special effects or learning experiments when you know more OpenGL.

 

A callback function in an OpenGL program is a modification of a default function inside the so-called display loop . Once the display loop is started by the OpenGL command glutMainLoop() , the only way you can interact with it in real time is through the callbacks. For C-adepts, callbacks work with function pointers. In object oriented programming (OOP), overloading classes serves a similar purpose.

2.2. Notational convention

Recall that we use a simple meta syntax. Pfoo refers to an OpenGL matrix (or product of matrices), qualified by the subscript "foo". Similarly Dbar refers to phrase in the pipeline to be elaborated later.

 

Square brackets designate a push/pop pair for the current matrix on the stack. Braces are comments not otherwise part of the meta syntax. Recall that the glPushMatrix() duplicates the top entry in the stack so that it can be modified and applied to a drawing without altering the previous top matrix. The glPopMatrix() removes the top matrix on the stack and restores the prior one to the top. The brackets must be properly nested , i.e. while there can be brackets within brackets, they must not overlap. This way, the brackets can also be interpreted as branches in a tree graph.

 

Finally, Xobj is a function that actually draws vertices of an object on the screen. It represents a leaf in the tree graph. A pipeline graph is well formed if the push-pop brackets are properly nested. It is complete , if it begins with an Π X.

 

3. Deconstruction of drawcons()

For a first pass, assume the binocular flag, binoc == 0 i.e. "is false", so that the second eye's viewport is not drawn. The double == is part of C-syntax and means "really equal", not just setting the LHS to the RHS, as in binoc=0 . OpenGL has many stacks. We are concerned only with the Projection stack and the Modelview stack . On the projection stack we load the identity I and multiply by the OpenGL matrix Πfrust corresponding to a frustum of a rectangular viewing window (use of "window" predates Microsoft and belongs to Silicon Graphics!). The exact meaning of the parameters are discussed elsewhere.

 

Immediately afterwards we draw the stars with drawstar() to get the initial phrase in the pipeline:

 

ΠfrustDstars.

 

On the Modelview stack, we load the identity to avoid using junk already theres, then multiply by one of four maintained matrices by flag whichmat :
Pvu=Pcam or Paff or Ptor-1 or Ptea-1
depending on your choice. In the original illiSkel, there are no choices. Only Paff is maintained. In the customary C/C++ OpenGL codes, this matrix is hidden from the applications programmer. We draw all that is to be drawn. So we can see what we're doing, there is a final drawing of the messages on the heads-up display. The heads-up needs its own viewport and projection matrix, see below. We have the entire pipeline without the details:
ΠfrustDstarsPvuDallDmsg.
We next unpack this summary of the OpenGL geometry pipeline for this RTICA.

3.1. Deconstruction of Component Functions

What we do next should remind you of unraveling symbols in your calculus course, as in integration by substitution, or the chain rule.

3.1.1. The Stars

Dstars=[PstarsXdots].
The stars in the basic version of this code are just random dots placed very far away. They do not move. When the observer's head is turned, then the stars are turned by a rotation matrix. Note that the observer's translation (forward, sideways, up/down motion) has no effect on the stars.

3.1.2. All the Drawings

Dall=[PtorDtor][PteaDpot].
Since we want to place the torus and the teapot independently into the same coordinate system, we use a pair of push/pops. If, for example, you want a second torus to link the first and stay linked, the first bracket would become [PtorDtorPtor2Dtor] , where the second torus is rotated by 90 degrees and translated to one side, before it is drawn again. Try it!

3.1.3. Martin Newell's Utah Teapot

Dpot=[Xutah].
It is worth your while to google the Wikipedia article on the teapot. It comes with the GLUT distribution as a pre-computed object for your convenient. By all means, make the second object in illiTorpot something less of a cliche, as an exercise.

 

Also note, the push/pop in this instance is unnecessary, because no placement of the teapot is made. But you can certainly place the teapot somewhere else. So the brackets are already in the code. Just construct your own place matrix.

3.1.4. The Torus

Dtorus=Xtorus.
This is the placeholder for the primary object you want to draw. This part of the code is discussed elsewhere.

 

3.2. The Messages

Dmsg={newViewport}[Πortho[Xlabels]].
This drawing function calls for a new viewport. Since this is an unusual and thing, we don't invent special symbols. And it isn't part of the geometry pipeline, though it effects what you see on the screen. All are by now familiar with what is just called a window . You open windows with a mouse, resize them, move them around, closed them. There is even a Microsoft product called ``Windows", although Microsoft didn't invent windowing. Neither did Apple. It was invented at Xerox-Parc, and used a 3-button mouse. Apple, initially decided that business men (for whom the MacIntosh was intended) are too clumsy to handle a three button mouse, and provided only a 1-button mouse. Microsoft offered a 2-button mouse to one-up Apple. But for the past decade, everybody saw the wisdom of Xerox and uses pretty much the same windowing system and 3 button mouse.

 

The OpenGL viewport is almost always synonymous with a window. But Silicon Graphics (which invented OpenGL) knew better. What if you want to display a mosaic of many views inside the same window e.g. a stereopair, or an overlay on the entire screen, as with our messages() That's when you specifiy new viewports.

 

3.3. Full Elaboration

We now put it all together, the original
ΠfrustDstarsPvuDallDmsg
becomes
{viewpt}Πfrust[PstarsXdots]Pvu[PtorXtor][Ptea[Xutah]]{newvp}[Πortho[Xmsgs]]

 

4. Navigation with Chaptrack

So far we only know the structure of the pipeline, not how to the user input with mouse and keypad is effected on the various placement matrices in the pipeline. Here we discuss only the most difficult one, the native navigator for illiStuff. In the illiSkel chaptrack is called in the idle function . This callback is, on a multiprocessor computer, done by a separate CPU so as not to impede the graphics engines with computation. There, the input devices are polled asynchronously from the graphics cards, and the processed information ( the changed entries in the various placement matrices) is made available to the pipeline when it is requires. On a single processor machine, the idle() function is done between successive display calls. Thus, heavy duty calculations can slow down the animation, though not in this case.

 

4.1. Chaptrack

This subroutine borrows the graphics pipeline to do some matrix arithmetic. This is generally a bad idea, but here it is meant to teach you the pipeline features. If you do not want to write your own matrix arithmetic functions you can also use the third stack ordinarily used for textures. Because we push the Modelview stack, nothing else is affected by this occasional loan of the stack here.

 

Note that the input for the function chaptrack(MAT, PAW, XX, YY, SHFT) , where MAT= PM is an arbitrary matrix. We can expand PM=[U,m] , but the brackets do not mean push/pop. They remind you that the components of an OpenGL matrix is a Euclidean rotation U followed by a translation by m . So U is a 3x3 orthogonal matrix and m a 3-vector. Reading by columns, remember an OpenGL matrix a flat array of 16 floats, m==(P[12],P[13],P[14]) . More later, or elsewhere, on OpenGL matrices. The remaining three inputs contain the mouse button situation, the x-y coordinates of the mouse when polled, and the state of the shift-key respectively.

 

Chaptrack continues by computing Paff-1 if chapmat==3 . It next calculates a small displacement dx, dy of the mouse from the center of the viewing window, but with "dead zone". Then it modifies various placement matrices from the left by constructing a matrix on a clean stack by multiplying the identity matrix on the right. We use short lines to leave room for comments. But this is one long push/pop.

 

[ I if3{ Paff-1 }
ifturn{ Tm }
RdxRdy ifshift{faster}
iffly{[ PstarsPstars ]}
ifarrows{other translations}
ifturn{ T-m }
PaffPMPM ]

 

and finishes up by rotating the sun (light source) in the opposite direction to keep the bright spot from the same direction on a turning object.

 

4.2. Explanation

The best way to understand this lesson is to choose various of the the (greater than 26 ) cases and write down what Geometry Pipeline matrix product (as in the Full Elaboration above) actually looks like in each case you considered.

 

Examples follow. I would appreciate it if you looked for and reported typos, especially misinterpretations of the code into symbols.