This page aims to provide a collection of tips for handling issues that arise when trying to port GL applications to GLES2.

GL_CLAMP_TO_BORDER

The GL_CLAMP_TO_BORDER wrapping method doesn't exist in GLES2. The only way to provide similar functionality is by using fragment shaders.

With GL_NEAREST filtering

In the case that nearest-neighbor filtering is used (GL_NEAREST) then it is enough to use the border color instead of the texel color when the normalized texcoords are outside [0.0, 1.0]:

   1 uniform vec4 border_color;
   2 uniform sampler2D sampler;
   3 
   4 varying vec2 texcoords;
   5 
   6 float clamp_to_border_factor (vec2 coords)
   7 {
   8     bvec2 out1 = greaterThan (coords, vec2 (1,1));
   9     bvec2 out2 = lessThan (coords, vec2 (0,0));
  10     bool do_clamp = (any (out1) || any (out2));
  11     return float (!do_clamp);
  12 }
  13 
  14 main()
  15 {
  16     vec4 texel = texture2D (sampler, texcoords);
  17     float f = clamp_to_border_factor (texcoords);
  18     gl_FragColor = mix (border_color, texel, f);
  19 }

With GL_LINEAR filtering

When using GL_LINEAR filtering the texture sampler returns binearly filtered values taking into account nearby texels. For texcoords near the edges the returned value will be affected by whatever texels the current wrapping method dictates, which is usually not what we want.

To simulate GL_CLAMP_TO_BORDER 100% correctly we need to use GL_NEAREST instead and perform the bilinear filtering plus clamp to border tweaks in the fragment shader. Unfortunately this method is computationally expensive.

Another approach, when 100% correctness is not needed, is to provide just the effect of the linear interpolation of the texture to the border color near the edges using the already filtered values:

   1 uniform sampler2D sampler;
   2 uniform vec4 border_color;
   3 uniform vec2 texdims; // texture dimensions (in pixels/texels)
   4 
   5 varying vec2 texcoords;
   6 
   7 float clamp_to_border_factor (vec2 coords, vec2 dims)
   8 {
   9     vec2 f = clamp (-abs (dims * (coords - 0.5)) + (dims + vec2(1.0)) * 0.5, 0.0, 1.0);
  10     return f.x * f.y; // Good enough in most cases
  11     // return 1.0 - clamp(length(vec2(1.0) - f), 0.0, 1.0);
  12 }
  13 
  14 main()
  15 {   
  16     vec4 texel = texture2D (sampler, texcoords);
  17     float f = clamp_to_border_factor (texcoords, texdims);
  18     gl_FragColor = mix (border_color, texel, f)
  19 }

1D Textures

1D Textures are not supported in GLES2. Just use 2D Textures with a height of 1.

Polygon Stipples and Bitmaps

As there are no bitmaps or polygon stipples in modern OpenGL or OpenGL ES, a simple solution (you may find something more elegant) is to define an alpha texture, and handle the rotation and sampling in the fragment shader. The following represents a replacement of an instance of polygon stipple enabled rendering from an old OpenGL demo.

In your actual OpenGL [ES] program source, setup the texture image like this:

   1     // Initialize the stipple pattern
   2     GLubyte textureImage[32][32];
   3     const unsigned int textureResolution(32);
   4     static const unsigned int patterns[] = { 0xaaaaaaaa, 0x55555555 };
   5     for (unsigned int i = 0; i < textureResolution; i++)
   6     {
   7         for (unsigned int j = 0; j < textureResolution; j++)
   8         {
   9             // Alternate the pattern every other line.
  10             unsigned int curMask(1 << j);
  11             unsigned int curPattern(patterns[i % 2]);
  12             textureImage[i][j] = ((curPattern & curMask) >> j) * 255;
  13         }
  14     }
  15 
  16     // Setup our the texture that the shadow program will use...
  17     GLuint textureName;
  18     glGenTextures(1, &textureName);
  19     glBindTexture(GL_TEXTURE_2D, textureName);
  20     glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT);
  21     glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT);
  22     glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
  23     glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
  24     glTexImage2D(GL_TEXTURE_2D, 0, GL_ALPHA8,
  25                  textureResolution, textureResolution,
  26                  0, GL_ALPHA, GL_UNSIGNED_BYTE, textureImage);
  27 
  28     // Make sure alpha test is setup correctly...
  29     glAlphaFunc(GL_GEQUAL, 0.5);
  30     glEnable(GL_ALPHA_TEST);

In your fragment shader, handle the pattern rotation and sampling like this:

   1 uniform sampler2D tex;
   2 out varying vec4 fragColor;
   3 
   4 void main()
   5 {
   6     vec2 curPos;
   7     curPos.x = float(int(gl_FragCoord.x) % 32) / 32.0;
   8     curPos.y = float(int(gl_FragCoord.y) % 32) / 32.0;
   9     vec4 color = texture(tex, curPos);
  10     fragColor = color;
  11 }

NOTE: This example is for a recent version of GLSL. For GLSL ES, simply remove the declaration of "fragColor" and replace with "gl_FragColor".

WorkingGroups/Middleware/Graphics/GLES2PortingTips (last modified 2011-10-20 14:58:54)