3. Enabling Textures with ES1::RenderingEngineAs always, let’s start with the ES 1.1
rendering engine since the 2.0 variant is more complex. The first step
is adding a pointer to the resource manager as shown in Example 7. Note we’re also adding a
GLuint for the grid texture. Much like framebuffer
objects and vertex buffer objects, OpenGL textures have integer
names. Example 7. RenderingEngine.ES1.cppclass RenderingEngine : public IRenderingEngine { public: RenderingEngine(IResourceManager* resourceManager); void Initialize(const vector<ISurface*>& surfaces); void Render(const vector<Visual>& visuals) const; private: vector<Drawable> m_drawables; GLuint m_colorRenderbuffer; GLuint m_depthRenderbuffer; mat4 m_translation; GLuint m_gridTexture; IResourceManager* m_resourceManager; }; IRenderingEngine* CreateRenderingEngine(IResourceManager* resourceManager) { return new RenderingEngine(resourceManager); }
RenderingEngine::RenderingEngine(IResourceManager* resourceManager) { m_resourceManager = resourceManager; glGenRenderbuffersOES(1, &m_colorRenderbuffer); glBindRenderbufferOES(GL_RENDERBUFFER_OES, m_colorRenderbuffer); }
|
Example 8 shows
the code for loading the texture, followed by a detailed
explanation. Example 8. Creating the OpenGL texture void RenderingEngine::Initialize(const vector<ISurface*>& surfaces) { vector<ISurface*>::const_iterator surface; for (surface = surfaces.begin(); surface != surfaces.end(); ++surface) {
// Create the VBO for the vertices. vector<float> vertices; (*surface)->GenerateVertices(vertices, VertexFlagsNormals |VertexFlagsTexCoords);
// ...
// Load the texture. glGenTextures(1, &m_gridTexture); glBindTexture(GL_TEXTURE_2D, m_gridTexture); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
m_resourceManager->LoadPngImage("Grid16.png"); void* pixels = m_resourceManager->GetImageData(); ivec2 size = m_resourceManager->GetImageSize(); glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, size.x, size.y, 0, GL_RGBA, GL_UNSIGNED_BYTE, pixels); m_resourceManager->UnloadImage();
// Set up various GL state. glEnableClientState(GL_VERTEX_ARRAY); glEnableClientState(GL_NORMAL_ARRAY); glEnableClientState(GL_TEXTURE_COORD_ARRAY); glEnable(GL_LIGHTING); glEnable(GL_LIGHT0); glEnable(GL_DEPTH_TEST); glEnable(GL_TEXTURE_2D);
... }
|
Example 8
introduces the glTexImage2D function, which
unfortunately has more parameters than it needs because of historical
reasons. Don’t be intimidated by the eight parameters; it’s much easier
to use than it appears. Here’s the formal declaration: void glTexImage2D(GLenum target, GLint level, GLint internalformat, GLsizei width, GLsizei height, GLint border, GLenum format, GLenum type, const GLvoid* pixels);
- target
This specifies which binding point to
upload the texture to. For ES 1.1, this must be
GL_TEXTURE_2D. - level
This specifies the mipmap level. We’ll
learn more about mipmaps soon. For now, use zero for this. - internalFormat
This specifies the format of the
texture. We’re using GL_RGBA for now, and other
formats will be covered shortly. It’s declared as a
GLint rather than a GLenum
for historical reasons. - width, height
This specifies the size of the image
being uploaded. - border
Set this to zero; texture borders are
not supported in OpenGL ES. Be happy, because that’s one less
thing you have to remember! - format
In OpenGL ES, this has to match
internalFormat. The argument may seem
redundant, but it’s yet another carryover from desktop OpenGL,
which supports format conversion. Again, be happy; this is a
simpler API. - type
This describes the type of each color
component. This is commonly GL_UNSIGNED_BYTE,
but we’ll learn about some other types later. - pixels
This is the pointer to the raw data
that gets uploaded.
Next let’s go over the
Render method. The only difference is that the vertex
stride is larger, and we need to call
glTexCoordPointer to give OpenGL the correct offset
into the VBO. See Example 9. Example 9. ES1::RenderingEngine::Render with texturevoid RenderingEngine::Render(const vector<Visual>& visuals) const { glClearColor(0.5f, 0.5f, 0.5f, 1); glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); vector<Visual>::const_iterator visual = visuals.begin(); for (int visualIndex = 0; visual != visuals.end(); ++visual, ++visualIndex) {
// ...
// Draw the surface. int stride = sizeof(vec3) + sizeof(vec3) + sizeof(vec2); const GLvoid* texCoordOffset = (const GLvoid*) (2 * sizeof(vec3)); const Drawable& drawable = m_drawables[visualIndex]; glBindBuffer(GL_ARRAY_BUFFER, drawable.VertexBuffer); glVertexPointer(3, GL_FLOAT, stride, 0); const GLvoid* normalOffset = (const GLvoid*) sizeof(vec3); glNormalPointer(GL_FLOAT, stride, normalOffset); glTexCoordPointer(2, GL_FLOAT, stride, texCoordOffset); glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, drawable.IndexBuffer); glDrawElements(GL_TRIANGLES, drawable.IndexCount, GL_UNSIGNED_SHORT, 0); } }
|
That’s it for the ES 1.1 backend!
Incidentally, in more complex applications you should take care to
delete your textures after you’re done with them; textures can be one of
the biggest resource hogs in OpenGL. Deleting a texture is done like
so: glDeleteTextures(1, &m_gridTexture)
This function is similar to
glGenTextures in that it takes a count and a list of
names. Incidentally, vertex buffer objects are deleted in a similar
manner using glDeleteBuffers.
|