iPhone 3D Programming : Adding Textures to ModelViewer (part 4) - Enabling Textures with ES2::RenderingEngine

1/21/2011 7:44:22 PM

4. Enabling Textures with ES2::RenderingEngine

The ES 2.0 backend requires some changes to both the vertex shader (to pass along the texture coordinate) and the fragment shader (to apply the texel color). You do not call glEnable(GL_TEXTURE_2D) with ES 2.0; it simply depends on what your fragment shader does.

Let’s start with the vertex shader, shown in Example 10.

Example 10. SimpleLighting.vert with texture

attribute vec4 Position;
attribute vec3 Normal;
attribute vec3 DiffuseMaterial;
attribute vec2 TextureCoord;

uniform mat4 Projection;
uniform mat4 Modelview;
uniform mat3 NormalMatrix;
uniform vec3 LightPosition;
uniform vec3 AmbientMaterial;
uniform vec3 SpecularMaterial;
uniform float Shininess;

varying vec4 DestinationColor;
varying vec2 TextureCoordOut;

void main(void)
vec3 N = NormalMatrix * Normal;
vec3 L = normalize(LightPosition);
vec3 E = vec3(0, 0, 1);
vec3 H = normalize(L + E);

float df = max(0.0, dot(N, L));
float sf = max(0.0, dot(N, H));
sf = pow(sf, Shininess);

vec3 color = AmbientMaterial + df * DiffuseMaterial + sf * SpecularMaterial;

DestinationColor = vec4(color, 1);
gl_Position = Projection * Modelview * Position;
TextureCoordOut = TextureCoord;


To try these, you can replace the contents of your existing .vert and .frag files. Just be sure not to delete the first line with STRINGIFY or the last line with the closing parenthesis and semicolon.

Example 10 simply passes the texture coordinates through, but you can achieve many interesting effects by manipulating the texture coordinates, or even generating them from scratch. For example, to achieve a “movie projector” effect, simply replace the last line in Example 5-10 with this:

TextureCoordOut = gl_Position.xy * 2.0;

For now, let’s stick with the boring pass-through shader because it better emulates the behavior of ES 1.1. The new fragment shader is a bit more interesting; see Example 11.

Example 11. Simple.frag with texture
varying lowp vec4 DestinationColor;
varying mediump vec2 TextureCoordOut;

uniform sampler2D Sampler;

void main(void)
gl_FragColor = texture2D(Sampler, TextureCoordOut) * DestinationColor;

When setting a uniform sampler from within your application, a common mistake is to set it to the handle of the texture object you’d like to sample:

glBindTexture(GL_TEXTURE_2D, textureHandle);
GLint location = glGetUniformLocation(programHandle, "Sampler");

glUniform1i(location, textureHandle); // Incorrect
glUniform1i(location, 0); // Correct

The correct value of the sampler is the stage index that you’d like to sample from, not the handle. Since all uniforms default to zero, it’s fine to not bother setting sampler values if you’re not using multitexturing (we’ll cover multitexturing later in this book).


Uniform samplers should be set to the stage index, not the texture handle.

Newly introduced in Example 5-11 is the texture2D function call. For input, it takes a uniform sampler and a vec2 texture coordinate. Its return value is always a vec4, regardless of the texture format.


The OpenGL ES specification stipulates that texture2D can be called from vertex shaders as well, but on many platforms, including the iPhone, it’s actually limited to fragment shaders only.

Note that Example 5-11 uses multiplication to combine the lighting color and texture color; this often produces good results. Multiplying two colors in this way is called modulation, and it’s the default method used in ES 1.1.

Now let’s make the necessary changes to the C++ code. First we need to add new class members to store the texture ID and resource manager pointer, but that’s the same as ES 1.1, so I won’t repeat it here. I also won’t repeat the texture-loading code because it’s the same with both APIs.

One new thing we need for the ES 2.0 backend is an attribute ID for texture coordinates. See Example 12. Note the lack of a glEnable for texturing; remember, there’s no need for it in ES 2.0.

Example 12. RenderingEngine.ES2.cpp
struct AttributeHandles {
GLint Position;
GLint Normal;
GLint Ambient;
GLint Diffuse;
GLint Specular;
GLint Shininess;
GLint TextureCoord;


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);

// ...

m_attributes.TextureCoord = glGetAttribLocation(program, "TextureCoord");

// Load the texture.
glGenTextures(1, &m_gridTexture);
glBindTexture(GL_TEXTURE_2D, m_gridTexture);

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);

// Initialize various state.


You may have noticed that the fragment shader declared a sampler uniform, but we’re not setting it to anything in our C++ code. There’s actually no need to set it; all uniforms default to zero, which is what we want for the sampler’s value anyway.

Next up is the Render method, which is pretty straightforward (Example 5-13). The only way it differs from its ES 1.1 counterpart is that it makes three calls to glVertexAttribPointer rather than glVertexPointer, glColorPointer, and glTexCoordPointer. (Replace everything from // Draw the surface to the end of the method with the corresponding code.)


You must also make the same changes to the ES 2.0 renderer that were shown earlier in Example 7.

Example 13. ES2::RenderingEngine::Render with texture
void RenderingEngine::Render(const vector<Visual>& visuals) const
glClearColor(0.5f, 0.5f, 0.5f, 1);

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* normalOffset = (const GLvoid*) sizeof(vec3);
const GLvoid* texCoordOffset = (const GLvoid*) (2 * sizeof(vec3));
GLint position = m_attributes.Position;
GLint normal = m_attributes.Normal;
GLint texCoord = m_attributes.TextureCoord;
const Drawable& drawable = m_drawables[visualIndex];
glBindBuffer(GL_ARRAY_BUFFER, drawable.VertexBuffer);
glVertexAttribPointer(position, 3, GL_FLOAT, GL_FALSE, stride, 0);
glVertexAttribPointer(normal, 3, GL_FLOAT, GL_FALSE, stride, normalOffset);
glVertexAttribPointer(texCoord, 2, GL_FLOAT, GL_FALSE, stride, texCoordOffset);
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, drawable.IndexBuffer);
glDrawElements(GL_TRIANGLES, drawable.IndexCount, GL_UNSIGNED_SHORT, 0);

That’s it! You now have a textured model viewer. Before you build and run it, select BuildClean All Targets (we’ve made a lot of changes to various parts of this app, and this will help avoid any surprises by building the app from a clean slate). I’ll explain some of the details in the sections to come.

  •  Programming with DirectX : Shading and Surfaces - Implementing Texture Mapping (part 2) - Multi Texture Demo
  •  Programming with DirectX : Shading and Surfaces - Implementing Texture Mapping (part 1) - 2D Texture Mapping Demo
  •  Building Out Of Browser Silverlight Applications - Using COM Interoperability and File System Access
  •  Building Out Of Browser Silverlight Applications - Controlling the Application Window
  •  iPhone 3D Programming : Adding Depth and Realism - Loading Geometry from OBJ Files
  •  iPhone 3D Programming : Adding Depth and Realism - Better Wireframes Using Polygon Offset
  •  Programming with DirectX : Textures in Direct3D 10 (part 2)
  •  Programming with DirectX : Textures in Direct3D 10 (part 1) - Textures Coordinates
  •  Programming with DirectX : Shading and Surfaces - Types of Textures
  •  iPhone 3D Programming : Adding Shaders to ModelViewer (part 2)
    Most View
    Scythe Katana 4 And Thermalright TRUE Spirit 90 (Part 2)
    Asus GeForce GTX 660 DirectCU II OC 2 GB Graphics Card Review (Part 4)
    HTC First – Smartphone With Facebook Home (Part 3)
    Why Choose LED Displays?
    The State Of Mobile Processors (Part 3) - The SnapDragons of 2014
    Easy DVD Creator 2.5.8 - Easily Create DVDs In Your Own Home
    Managing Windows 8 native applications (part 3) - Controlling applications by using AppLocker
    17 Killer Mac Apps Under $20 (Part 5) : Dropmark, Popclip, Snapheal, Ensoul contacts
    Windows 7 : Programming WMI Support (part 1) - WMI Architecture, Registering as a WMI Data Provider, Handling WMI Requests
    Panasonic Lumix DMC-FT5 Digital Camera Review (Part 2)
    Top 10
    Windows Server 2008 and Windows Vista : Troubleshooting GPOs - Group Policy Troubleshooting Essentials
    Windows Server 2008 and Windows Vista : Creating and Using the ADMX Central Store
    Windows Server 2008 and Windows Vista : Migrating .adm Templates to ADMX Files
    Windows Server 2008 and Windows Vista : ADMX Files,Default ADMX Files, Using Both .adm Templates and ADMX Files
    Windows 8 : Configuring networking (part 7) - Managing network settings - Managing a wireless network
    Windows 8 : Configuring networking (part 6) - Managing network settings - Adding a second default gateway,Connecting to a wireless network
    Windows 8 : Configuring networking (part 5) - Managing network settings - Understanding the dual TCP/IP stack in Windows 8, Configuring name resolution
    Windows 8 : Configuring networking (part 4) - Managing network settings - Configuring IP settings
    Windows 8 : Configuring networking (part 3) - Managing network settings - Managing network profiles
    Windows 8 : Configuring networking (part 2) - Managing network settings - Using the Network and Sharing Center