Let me show a method of implementing texture atlas. Texture region is a sub area of the actual texture.
It is used for performance benefit, in that texture need not be switched.
With texture region also as a user we will be specifying the same texture coordinate(from (0, 0) to (1, 1)),
but the shader implementation internally will convert or transform it to the texture coordinate to match the region's.
How we do that is by simply transforming the texture coordinate in the fragment shader. We will create a 3x3 matrix
corresponding to the texture region and multiply it with the texture coordinates.
There is no additional logic in vertex shader from that of drawing a simple texture.
It is in the fragment shader that we use the texture matrix which transforms the original texture coordinates to the required coordinates.
As you are seeing we have declared the uniform variable uTextureMatrix for transforming the texture coordinate.
As you can see in line 9 instead directly passing the texture coordinate to texture2D function we are transforming it
using the matrix uTextureMatrix to the required coordinates.
We are doing it by multiplying the matrix uTextureMatrix with the texture coordinate, last value is 1.0 because
we are using homogeneous coordinate system in two dimension.
Complete source code is pasted below for your reference. You need glut to compile it.
It is used for performance benefit, in that texture need not be switched.
With texture region also as a user we will be specifying the same texture coordinate(from (0, 0) to (1, 1)),
but the shader implementation internally will convert or transform it to the texture coordinate to match the region's.
How we do that is by simply transforming the texture coordinate in the fragment shader. We will create a 3x3 matrix
corresponding to the texture region and multiply it with the texture coordinates.
Texture Region class
This class creates the 3x3 matrix which transforms the original texture coordinates to texture region coordinates.
It is provided with the original texture size and a sub rectangle within the texture.
Source code for the complete class,
It is provided with the original texture size and a sub rectangle within the texture.
Source code for the complete class,
1 #ifndef TEXTUREREGION_H 2 #define TEXTUREREGION_H 3 4 #include <algorithm> 5 6 class TextureRegion { 7 private: 8 int mTextureWidth; 9 int mTextureHeight; 10 int mX; 11 int mY; 12 int mWidth; 13 int mHeight; 14 float mMatrix[3*3]; 15 16 public: 17 TextureRegion() 18 {} 19 TextureRegion(int textureWidth, int textureHeight, int x, int y, int width, int height) { 20 mTextureWidth = textureWidth; 21 mTextureHeight = textureHeight; 22 23 if (x < 0) { 24 x = 0; 25 } 26 if (x < 0) { 27 x = 0; 28 } 29 30 if (width > textureWidth) { 31 width = textureWidth; 32 } 33 34 if (height > textureHeight) { 35 height = textureHeight; 36 } 37 38 mX = x; 39 mY = y; 40 mWidth = width; 41 mHeight = height; 42 43 createMatrix(); 44 } 45 const float * getMatrix() { 46 return mMatrix; 47 } 48 private: 49 void createMatrix() { 50 // translation factor 51 float tx = (float)(mX - 0) / (float)mTextureWidth; 52 float ty = (float)(mY - 0) / (float)mTextureHeight; 53 54 // scaling factor 55 float sx = (float)mWidth / (float)mTextureWidth; 56 float sy = (float)mHeight / (float)mTextureHeight; 57 58 // 3x3 matrix 59 float mat[] = { 60 sx, 0, 0, 61 0, sy, 0, 62 tx, ty, 1.0f 63 }; 64 std::copy(mat, mat+9, mMatrix); 65 } 66 }; 67 68 #endif 69
Vertex shader
There is no additional logic in vertex shader from that of drawing a simple texture.
1 attribute vec4 aPosition; 2 attribute vec2 aTexCoord; 3 varying vec2 vTexCoord; 4 void main() { 5 vTexCoord = aTexCoord; 6 gl_Position = aPosition; 7 } 8
Fragment shader
It is in the fragment shader that we use the texture matrix which transforms the original texture coordinates to the required coordinates.
1 #ifdef GL_ES 2 precision mediump float; 3 #endif 4 varying vec2 vTexCoord; 5 uniform mat3 uTextureMatrix; 6 uniform vec4 uColor; 7 uniform sampler2D uTexture; 8 void main() { 9 vec3 texCoord = uTextureMatrix * vec3(vTexCoord.x, vTexCoord.y, 1.0f); 10 gl_FragColor = uColor * texture2D(uTexture, texCoord.xy); 11 }
As you are seeing we have declared the uniform variable uTextureMatrix for transforming the texture coordinate.
As you can see in line 9 instead directly passing the texture coordinate to texture2D function we are transforming it
using the matrix uTextureMatrix to the required coordinates.
We are doing it by multiplying the matrix uTextureMatrix with the texture coordinate, last value is 1.0 because
we are using homogeneous coordinate system in two dimension.
Complete source code is pasted below for your reference. You need glut to compile it.
1 #include <GL/glut.h> 2 #include <iostream> 3 #include <string> 4 5 #include "textureregion.h" 6 7 bool init(); 8 void display(); 9 void reshape(int w, int h); 10 int loadShader(int shaderType, std::string pSource); 11 int createProgram(std::string vertexSource, std::string fragmentSource); 12 13 static const std::string VertexShader = 14 "attribute vec4 aPosition;\n" 15 "attribute vec2 aTexCoord;\n" 16 "varying vec2 vTexCoord;\n" 17 "void main() {\n" 18 " vTexCoord = aTexCoord;\n" 19 " gl_Position = aPosition;\n" 20 "}\n"; 21 22 static const std::string FragmentShader = 23 "#ifdef GL_ES\n" 24 "precision mediump float;\n" 25 "#endif\n" 26 "varying vec2 vTexCoord;\n" 27 "uniform mat3 uTextureMatrix;\n" 28 "uniform vec4 uColor;\n" 29 "uniform sampler2D uTexture;\n" 30 "void main() {\n" 31 " vec3 texCoord = uTextureMatrix * vec3(vTexCoord.x, vTexCoord.y, 1.0f);\n" 32 " gl_FragColor = uColor * texture2D(uTexture, texCoord.xy);\n" 33 "}\n"; 34 35 int mProgram; 36 37 GLuint mTextureId; 38 39 int mPosLoc; 40 int mTexCoordLoc; 41 int mTexMatrixLoc; 42 int mSampleLoc; 43 int mColorLoc; 44 45 float mVerticesBuffer[8]; 46 float mTexcoordBuffer[8]; 47 48 TextureRegion mTextureRegion; 49 50 int loadShader(int shaderType, std::string pSource) 51 { 52 int shader = glCreateShader(shaderType); 53 if (shader != 0) { 54 const char *vsSource = pSource.c_str(); 55 glShaderSource(shader, 1, &vsSource, 0); 56 glCompileShader(shader); 57 // not interested in checking status 58 } 59 60 return shader; 61 } 62 63 int createProgram(std::string vertexSource, std::string fragmentSource) 64 { 65 int vertexShader = loadShader(GL_VERTEX_SHADER, vertexSource); 66 if (vertexShader == 0) { 67 return 0; 68 } 69 70 int pixelShader = loadShader(GL_FRAGMENT_SHADER, fragmentSource); 71 if (pixelShader == 0) { 72 return 0; 73 } 74 75 int program = glCreateProgram(); 76 if (program != 0) { 77 glAttachShader(program, vertexShader); 78 glAttachShader(program, pixelShader); 79 glLinkProgram(program); 80 // not interested in checking status 81 } 82 return program; 83 } 84 85 bool init() 86 { 87 glClearColor(0.0f, 1.0f, 1.0f, 1.0f); 88 89 mProgram = createProgram(VertexShader, FragmentShader); 90 91 // retrieve locations 92 glUseProgram(mProgram); 93 94 mPosLoc = glGetAttribLocation(mProgram, "aPosition"); 95 mTexCoordLoc = glGetAttribLocation(mProgram, "aTexCoord"); 96 mSampleLoc = glGetUniformLocation(mProgram, "uTexture"); 97 mColorLoc = glGetUniformLocation(mProgram, "uColor"); 98 mTexMatrixLoc = glGetUniformLocation(mProgram, "uTextureMatrix"); 99 100 GLubyte texdata[] = { 101 0xFF, 0x00, 0x00, 0xFF, 102 0x00, 0xFF, 0x00, 0xFF, 103 0x00, 0x00, 0xFF, 0xFF, 104 0xFF, 0xFF, 0x00, 0xFF 105 }; 106 107 glGenTextures(1, &mTextureId); 108 109 glBindTexture(GL_TEXTURE_2D, mTextureId); 110 glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); 111 glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST); 112 113 glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT); 114 glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT); 115 116 glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, 2, 2, 0, GL_RGBA, GL_UNSIGNED_BYTE, texdata); 117 118 // first color 119 mTextureRegion = TextureRegion(2, 2, 1, 1, 1, 1); 120 121 glEnable(GL_TEXTURE_2D); 122 123 // create vertices buffer 124 float vertices[] = { 125 -0.75f, -0.75f, 126 0.75f, -0.75f, 127 0.75f, 0.75f, 128 -0.75f, 0.75f 129 }; 130 131 std::copy(vertices, vertices+8, mVerticesBuffer); 132 133 // create texcoord buffer 134 float texcoords[] = { 135 0, 1, 136 1, 1, 137 1, 0, 138 0, 0 139 }; 140 141 std::copy(texcoords, texcoords+8, mTexcoordBuffer); 142 143 glEnable(GL_BLEND); 144 145 glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); 146 147 return true; 148 } 149 150 void display() 151 { 152 glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); 153 154 glUseProgram(mProgram); 155 156 glActiveTexture(GL_TEXTURE0); 157 glBindTexture(GL_TEXTURE_2D, mTextureId); 158 glUniform1i(mSampleLoc, 0); 159 160 glUniform4f(mColorLoc, 1.0f, 1.0f, 1.0f, 1.0f); 161 162 glVertexAttribPointer(mPosLoc, 2, GL_FLOAT, false, 0, mVerticesBuffer); 163 glVertexAttribPointer(mTexCoordLoc, 2, GL_FLOAT, false, 0, mTexcoordBuffer); 164 165 glUniformMatrix3fv(mTexMatrixLoc, 1, false, mTextureRegion.getMatrix()); 166 167 glEnableVertexAttribArray(mPosLoc); 168 glEnableVertexAttribArray(mTexCoordLoc); 169 170 glDrawArrays(GL_TRIANGLE_FAN, 0, 4); 171 172 glutSwapBuffers(); 173 } 174 175 void reshape(int w, int h) 176 { 177 glViewport(0, 0, w, h); 178 } 179 180 int main(int argc, char **argv) 181 { 182 glutInit(&argc, argv); 183 glutInitDisplayMode(GLUT_DEPTH | GLUT_DOUBLE | GLUT_RGBA); 184 glutInitWindowPosition(100, 100); 185 glutInitWindowSize(320, 420); 186 187 glutCreateWindow("Texture region example"); 188 189 glutDisplayFunc(display); 190 glutReshapeFunc(reshape); 191 192 if (init()) { 193 glutMainLoop(); 194 } else { 195 std::cerr << "Initialization failed" << std::endl; 196 return 1; 197 } 198 199 return 0; 200 } 201 202
Source code is also available at https://github.com/chikkutechie/glexamples
Compiling the sources
The source code can be compiled using the below command. main.cpp contals the above pasted code.
g++ main.cpp -o textreg -DGL_GLEXT_PROTOTYPES -lGL -lglut
Hello There,
ReplyDeleteYour writing shines! There is no room for gibberish here clearly you have explained about Texture atlas implementation in OpenGL ES 2.0 using shaders Keep writing!
I'd really like to know what type of arguments does this function take, and what happens in the for loop.
I realize that first 2 are arrays, the next one is an unsigned number, what about the last two?
It was cool to see your article pop up in my Google search for the process yesterday. Great Guide.
Keep up the good work!
Grazie,
Mahi