I’m porting Crush the Castle from a fixed-function, OpenGL 1 renderer to a programmable, OpenGL 2 renderer. In the last couple of posts I described how I separated my initial fixed-function renderer into three distinct classes:
- An abstract Renderer interface that the game itself talks to
- A concrete implementation of the Renderer interface that uses fixed-function OpenGL 1 (RendererGLES1)
- Another implementation of Renderer that uses programmable OpenGL 2 (RendererGLES2)
Today I’d like to show you what I had to do to port the fixed-function version of the renderer to use the programmable pipeline in OpenGL ES 2.0.
More Power, More Responsibility
The good news about a programmable rendering pipeline is that it offers far greater power. With a fixed-function pipeline you can only do what the API lets you do. If the API supports bump mapping, you can do bump mapping. If the API supports fog, you can apply fog. But with a programmable pipeline, your engine can render anything that you can code. You’re in total control of how every vertex and pixel is displayed.
The bad news about a programmable pipeline is that it requires a lot more work. Fixed-function pipelines do a lot for you. Call glRotate(), for example, and OpenGL will set up a rotation matrix for you and concatenate it with the existing matrix. Request that a light be applied to your models and suddenly they’re glowing with the sunset. Not so in a programmable pipeline. If you want to see sunlight, you’ve got to code it. If you want transform matrices (and believe me, you do), you’ve got to program them.
So a programmable pipeline offers more power. But it also puts more responsibility on you, the programmer.
This is even more true in OpenGL ES 2.0—the rendering API that the iPad and later iPhones/iPod Touches use. All new versions of OpenGL add support for vertex and fragment programs. But the “ES” version of OpenGL 2.0—the mobile, stripped-down version—not only adds this support, it strips out support for anything else.
This is the hardest part about moving from a fixed function renderer to a programmable renderer on the iPhone/iPad. The move to shaders takes away some of the functionality that OpenGL ES 1.0 provided.
Changes from OpenGL ES 1.0 to OpenGL ES 2.0
The biggest changes that I encountered when moving Crush the Castle from version 1 to version 2 of OpenGL ES involved how vertex attributes are sent to the hardware, the self-management of matrices, and of course, programming the actual shaders. Here’s a list of all the changes I had to make to my fixed-function renderer. A lot of these might not make sense now, but I’ll talk about them all in more detail later.
- Implemented my own 4×4 matrix class with support for translation, rotation, scaling, and concatenation
- Implemented my own orthographic and perspective projection math
- Created a class to implement matrix stacks
- Replaced occasional calls to glColor4f() (used to apply a single color to all vertices in a draw call) with a vertex program “uniform” variable
- Got rid of glEnableClientState() calls
- Converted gl*Pointer() calls (like glVertexPointer() and glTexCoordPointer()) to glVertexAttribPointer() calls
- Added the loading, compiling, linking, validating, and using of vertex and fragment shaders
Some features stayed just the same.
- Texture creation, loading, and binding
- Alpha blending settings
- Vertex buffer object creation with glBufferData() and the like
- Setting the viewport with glViewport()
- glClear(), glClearColor(), and the like
- glDrawArrays() and glDrawElements()
Now Crush the Castle does not use every feature in OpenGL ES 1. No doubt there are many other fixed-function features that I would have needed to convert if I had been using them. So mileage may vary. But these are the ones I had to convert, and next time I’ll go into more detail about what I had to do to convert each one.