Hello Triangle: An OpenGL ES 2.0 Example [con't]
Using the OpenGL ES 2.0 Framework
main function in Hello Triangle, you will see calls into several ES utility functions. The first thing the
main function does is declare an
ESContext and initialize it:
Every example program in this book does the same thing. The ESContext is passed into all of the ES framework utility functions and contains all of the necessary information about the program that the ES framework needs. The reason for passing around a context is that the sample programs and the ES code framework do not need to use any global data.
Many handheld platforms do not allow applications to declare global static data in their applications. Examples of platforms that do not allow this include BREW and Symbian. As such, we avoid declaring global data in either the sample programs or the code framework by passing a context between functions.
ESContexthas a member variable named
userDatathat is a
void*. Each of the sample programs will store any of the data that are needed for the application in
esInitializefunction is called by the sample program to initialize the context and the ES code framework. The other elements in the
ESContextstructure are described in the header file and are intended only to be read by the user application. Other data in the
ESContextstructure include information such as the window width and height, EGL context, and callback function pointers.
The rest of the
main function is responsible for creating the window, initializing the draw callback function, and entering the main loop:
The call to
esCreateWindow creates a window of the specified width and height (in this case, 320 ÃÂ 240). The last parameter is a bit field that specifies options for the window creation. In this case, we request an RGB framebuffer. In Chapter 3, "An Introduction to EGL," we discuss what
esCreateWindow does in more detail. This function uses EGL to create an on-screen render surface that is attached to a window. EGL is a platform independent API for creating rendering surfaces and contexts. For now, we will simply say that this function creates a rendering surface and leave the details on how it works for the next chapter.
esCreateWindow, the next thing the main function does is to call
Init to initialize everything needed to run the program. Finally, it registers a callback function,
Draw, that will be called to render the frame. The final call,
esMainLoop, enters into the main message processing loop until the window is closed.
Creating a Simple Vertex and Fragment Shader
In OpenGL ES 2.0, nothing can be drawn unless a valid vertex and fragment shader have been loaded. In Chapter 1, "Introduction to OpenGL ES 2.0," we covered the basics of the OpenGL ES 2.0 programmable pipeline. There you learned about the concepts of a vertex and fragment shader. These two shader programs describe the transformation of vertices and drawing of fragments. To do any rendering at all, an OpenGL ES 2.0 program must have both a vertex and fragment shader.
The biggest task that the
Init function in Hello Triangle accomplishes is the loading of a vertex and fragment shader. The vertex shader that is given in the program is very simple:
This shader declares one input
attribute that is a four-component vector named
vPosition. Later on, the
Draw function in Hello Triangle will send in positions for each vertex that will be placed in this variable. The shader declares a main function that marks the beginning of execution of the shader. The body of the shader is very simple; it copies the
vPosition input attribute into a special output variable named
gl_Position. Every vertex shader must output a position into the
gl_Position variable. This variable defines the position that is passed through to the next stage in the pipeline. The topic of writing shaders is a large part of what we cover in this book, but for now we just want to give you a flavor of what a vertex shader looks like. In Chapter 5, "OpenGL ES Shading Language," we cover the OpenGL ES shading language and in Chapter 8, "Vertex Shaders," we specifically cover how to write vertex shaders.
The fragment shader in the example is also very simple:
The first statement in the fragment shader declares the default precision for float variables in the shader. For more details on this, please see the section on precision qualifiers in Chapter 5. For now, simply pay attention to the
main function, which outputs a value of (1.0, 0.0, 0.0, 1.0) into the
gl_FragColor is a special built-in variable that contains the final output color for the fragment shader. In this case, the shader is outputting a color of red for all fragments. The details of developing fragment shaders are covered in Chapter 9, "Texturing," and Chapter 10, "Fragment Shaders." Again, here we are just showing you what a fragment shader looks like.
Typically, a game or application would not inline shader source strings in the way we have done in this example. In most real applications, the shader would be loaded from some sort of text or data file and then loaded to the API. However, for simplicity and having the example program be self-contained, we provide the shader source strings directly in the program code.