Graphics Programming
Textures
Thorsten Thormählen
November 29, 2021
Part 7, Chapter 1
Thorsten Thormählen
November 29, 2021
Part 7, Chapter 1
This is the print version of the slides.
Advance slides with the → key or
by clicking on the right border of the slide
Slides can also be advanced by clicking on the left or right border of the slide.
Type | Font | Examples |
---|---|---|
Variables (scalars) | italics | $a, b, x, y$ |
Functions | upright | $\mathrm{f}, \mathrm{g}(x), \mathrm{max}(x)$ |
Vectors | bold, elements row-wise | $\mathbf{a}, \mathbf{b}= \begin{pmatrix}x\\y\end{pmatrix} = (x, y)^\top,$ $\mathbf{B}=(x, y, z)^\top$ |
Matrices | Typewriter | $\mathtt{A}, \mathtt{B}= \begin{bmatrix}a & b\\c & d\end{bmatrix}$ |
Sets | calligraphic | $\mathcal{A}, B=\{a, b\}, b \in \mathcal{B}$ |
Number systems, Coordinate spaces | double-struck | $\mathbb{N}, \mathbb{Z}, \mathbb{R}^2, \mathbb{R}^3$ |
$(s, t)^\top \quad \longmapsto \quad (x,y,z)^\top$
glTexCoord2f
... glBegin(GL_POLYGON); glTexCoord2f(0.25f,0.50f); glVertex3f(-1.0f, 1.0f, 1.0f); glTexCoord2f(0.25f,0.25f); glVertex3f(-1.0f,-1.0f, 1.0f); glTexCoord2f(0.50f,0.25f); glVertex3f( 1.0f,-1.0f, 1.0f); glTexCoord2f(0.50f,0.50f); glVertex3f( 1.0f, 1.0f, 1.0f); glEnd(); ...
init()
glBindTexture(GL_TEXTURE_2D, texID); glTexImage2D(GL_TEXTURE_2D, texLevel, texFormat, inWidth, inHeight, inBorder, inFormat, inType, inData);
texID
is an integer value that serves as an unique identifier ("ID") for the generated texture.
With the function glGenTextures(1, &texID)
such an unique identifier can be generated
glEnable(GL_TEXTURE_2D);
glBindTexture(GL_TEXTURE_2D, texID);
GL_TEXTURE_2D
glBindTexture
has several functions: when first called with a new ID, a new texture is generated, otherwise an existing texture is activated
class Renderer { public: float t; private: GLuint texID; public: // constructor Renderer() : t(0.0), texID(0) {} // destructor ~Renderer() { if(texID !=0) glDeleteTextures( 1, &texID); } public: void init() { glEnable(GL_DEPTH_TEST); std::string fileName("dice_texture_flip.ppm"); texID = loadTexture(fileName); } void resize(int w, int h) { glViewport(0, 0, w, h); glMatrixMode(GL_PROJECTION); glLoadIdentity(); gluPerspective (30.0, (float)w/(float)h, 0.1, 50.0); } void display() { glClearColor(0.0f, 0.0f, 0.0f, 0.0f); glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); glMatrixMode(GL_MODELVIEW); glLoadIdentity(); // set camera gluLookAt(8.0, -2.0, 5.0, 0.0, 0.0, 0.0, 0.0, 0.0, 1.0); // draw scene glRotatef(t, 0.0f, 0.0f, 1.0f); drawTexturedCube(); } private: // returns a valid textureID on success, otherwise 0 GLuint loadTexture(std::string &filename) { unsigned width; unsigned height; int level = 0; int border = 0; std::vector<unsigned char> imgData; // load image data if(!loadPPMImageFlipped(filename, width, height, imgData)) return 0; // data is aligned in byte order glPixelStorei(GL_UNPACK_ALIGNMENT, 1); //request textureID GLuint textureID; glGenTextures( 1, &textureID); // bind texture glBindTexture( GL_TEXTURE_2D, textureID); //define how to filter the texture (important but ignore for now) glTexParameteri (GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); glTexParameteri (GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); //texture colors should replace the original color values glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_REPLACE); //GL_MODULATE // specify the 2D texture map glTexImage2D(GL_TEXTURE_2D, level, GL_RGB, width, height, border, GL_RGB, GL_UNSIGNED_BYTE, &imgData[0]); // return unique texture identifier return textureID; } void drawTexturedCube() { glEnable(GL_TEXTURE_2D); glBindTexture(GL_TEXTURE_2D, texID); glColor3f(1.0f,0.0f,0.0f); glBegin(GL_POLYGON); // three glTexCoord2f(0.25f,0.50f); glVertex3f(-1.0f, 1.0f, 1.0f); glTexCoord2f(0.25f,0.25f); glVertex3f(-1.0f,-1.0f, 1.0f); glTexCoord2f(0.50f,0.25f); glVertex3f( 1.0f,-1.0f, 1.0f); glTexCoord2f(0.50f,0.50f); glVertex3f( 1.0f, 1.0f, 1.0f); glEnd(); glColor3f(1.0f,1.0f,0.0f); glBegin(GL_POLYGON); // five glTexCoord2f(0.00f,0.50f); glVertex3f(-1.0f, 1.0f,-1.0f); glTexCoord2f(0.00f,0.25f); glVertex3f(-1.0f,-1.0f,-1.0f); glTexCoord2f(0.25f,0.25f); glVertex3f(-1.0f,-1.0f, 1.0f); glTexCoord2f(0.25f,0.50f); glVertex3f(-1.0f, 1.0f, 1.0f); glEnd(); glColor3f(0.0f,0.0f,1.0f); glBegin(GL_POLYGON); // two glTexCoord2f(0.50f,0.50f); glVertex3f( 1.0f, 1.0f, 1.0f); glTexCoord2f(0.50f,0.25f); glVertex3f( 1.0f,-1.0f, 1.0f); glTexCoord2f(0.75f,0.25f); glVertex3f( 1.0f,-1.0f,-1.0f); glTexCoord2f(0.75f,0.50f); glVertex3f( 1.0f, 1.0f,-1.0f); glEnd(); glColor3f(1.0f,1.0f,1.0f); glBegin(GL_POLYGON); // six glTexCoord2f(0.25f,0.75f); glVertex3f(-1.0f, 1.0f,-1.0f); glTexCoord2f(0.25f,0.50f); glVertex3f(-1.0f, 1.0f, 1.0f); glTexCoord2f(0.50f,0.50f); glVertex3f( 1.0f, 1.0f, 1.0f); glTexCoord2f(0.50f,0.75f); glVertex3f( 1.0f, 1.0f,-1.0f); glEnd(); glColor3f(0.0f,1.0f,1.0f); glBegin(GL_POLYGON); // one glTexCoord2f(0.25f,0.25f); glVertex3f(-1.0f, -1.0f, 1.0f); glTexCoord2f(0.25f,0.00f); glVertex3f(-1.0f, -1.0f,-1.0f); glTexCoord2f(0.50f,0.00f); glVertex3f( 1.0f, -1.0f,-1.0f); glTexCoord2f(0.50f,0.25f); glVertex3f( 1.0f, -1.0f, 1.0f); glEnd(); glColor3f(0.0f,1.0f,0.0f); glBegin(GL_POLYGON); //four glTexCoord2f(0.75f,0.50f); glVertex3f( 1.0f, 1.0f,-1.0f); glTexCoord2f(0.75f,0.25f); glVertex3f( 1.0f,-1.0f,-1.0f); glTexCoord2f(1.00f,0.25f); glVertex3f(-1.0f,-1.0f,-1.0f); glTexCoord2f(1.00f,0.50f); glVertex3f(-1.0f, 1.0f,-1.0f); glEnd(); glDisable(GL_TEXTURE_2D); } ... };
GL_TEXTURE_WRAP
parameter defines how the texture mapping behaves if the texture coordinate $s$ or $t$ leaves the range $[0;1]$
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT);
GL_CLAMP
, GL_REPEAT
, GL_CLAMP_TO_BORDER
, GL_CLAMP_TO_EDGE
,
oder GL_MIRRORED_REPEAT
GL_TEXTURE_MIN_FILTER
sets the filter for reduction in size and GL_TEXTURE_MAG_FILTER
for enlargements:glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
GL_NEAREST
or GL_LINEAR
GL_NEAREST
the color value of the pixel is used that has the shortest distance to the texture coordinate
(nearest-neighbor)
$\begin{align} x_{\small \mathrm{nearest}} &= \mathrm{int}(s * \mathrm{width} + 0.5)\\ y_{\small \mathrm{nearest}} &= \mathrm{int}(t * \mathrm{height} + 0.5)\\ f_{\small \mathrm{nearest}} &= f(x_{\small \mathrm{nearest}}, y_{\small \mathrm{nearest}} ) \end{align}$
GL_LINEAR
the color value is determined by bilinear interpolation
$\begin{align} f(\mathbf{p}) = \Big((x_2-x_1)(y_2-y_1)\Big)^{-1} \Big( &(x_2-x)(y_2-y)\, f(x_1,y_1) +\\\ & (x-x_1)(y_2-y) \,f(x_2,y_1) +\\ & (x_2-x)(y-y_1) \,f(x_1,y_2) +\\ & (x-x_1)(y-y_1) \,f(x_2,y_2) \Big) \end{align}$
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_LINEAR); glTexParameteri(GL_TEXTURE_2D, GL_GENERATE_MIPMAP, GL_TRUE); glTexImage2D (...);
GL_TEXTURE_MIN_FILTER
are: GL_NEAREST_MIPMAP_NEAREST
, GL_LINEAR_MIPMAP_NEAREST
,
GL_NEAREST_MIPMAP_LINEAR
, und GL_LINEAR_MIPMAP_LINEAR
GL_TEXTURE_MAG_FILTER
the use of mipmaps does not make sense and is not supported
class Renderer { public: float t; int wrapS; int wrapT; int magFilter; int minFilter; int selectedTexID; private: GLuint texID0; GLuint texID1; public: // constructor Renderer() : t(-90.0f), wrapS(GL_REPEAT), wrapT(GL_REPEAT), magFilter(GL_LINEAR), minFilter(GL_LINEAR_MIPMAP_LINEAR), selectedTexID(0), texID0(0), texID1(0) {} // destructor ~Renderer() { if(texID0 !=0) glDeleteTextures( 1, &texID0); if(texID1 !=0) glDeleteTextures( 1, &texID1); } public: void init() { glEnable(GL_DEPTH_TEST); std::string fileName0("checker_texture_512.ppm"); texID0 = loadTexture(fileName0); std::string fileName1("checker_texture_32.ppm"); texID1 = loadTexture(fileName1); } void resize(int w, int h) { glViewport(0, 0, w, h); glMatrixMode(GL_PROJECTION); glLoadIdentity(); gluPerspective (30.0, (float)w/(float)h, 0.1, 50.0); } void display() { glClearColor(0.7f, 0.7f, 0.7f, 0.0f); glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); glMatrixMode(GL_MODELVIEW); glLoadIdentity(); // set camera gluLookAt(8.0, -2.0, 1.0, 0.0, 0.0, 0.0, 0.0, 0.0, 1.0); // draw scene glRotatef(t, 0.0f, 0.0f, 1.0f); drawTexturedPlane(); } // returns a valid textureID on success, otherwise 0 GLuint loadTexture(std::string &filename) { unsigned width; unsigned height; int level = 0; int border = 0; std::vector <unsigned char> imgData; // load image data if(!loadPPMImageFlipped(filename, width, height, imgData)) return 0; // data is aligned in byte order glPixelStorei(GL_UNPACK_ALIGNMENT, 1); //request textureID GLuint textureID; glGenTextures(1, &textureID); // bind texture glBindTexture(GL_TEXTURE_2D, textureID); // parameters that define how to warp the texture glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT); GLfloat borderColor[4] = {1.0f, 1.0f, 0.0f, 1.0f}; glTexParameterfv(GL_TEXTURE_2D, GL_TEXTURE_BORDER_COLOR, borderColor); // parameters that define how to filter the texture glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_LINEAR); glTexParameteri(GL_TEXTURE_2D, GL_GENERATE_MIPMAP, GL_TRUE); // texture colors should replace the original color values glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_REPLACE); //GL_MODULATE // specify the 2D texture map glTexImage2D (GL_TEXTURE_2D, level, GL_RGB, width, height, border, GL_RGB, GL_UNSIGNED_BYTE, &imgData[0]); // return unique texture identifier return textureID; } void setTextureParameters() { glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, wrapS); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, wrapT); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, magFilter); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, minFilter); } void drawTexturedPlane() { glEnable(GL_TEXTURE_2D); if(selectedTexID == 0) { glBindTexture(GL_TEXTURE_2D, texID0); }else{ glBindTexture(GL_TEXTURE_2D, texID1); } setTextureParameters(); glColor3f(1.0,0.0,0.0); glBegin(GL_POLYGON); glTexCoord2f(5 .00f, 5.00f); glVertex3f(-10.0f, 10.0f, 0.0f); glTexCoord2f( 5.00f, -4.00f); glVertex3f(-10.0f,-10.0f, 0.0f); glTexCoord2f(-4.00f, -4.00f); glVertex3f( 10.0f,-10.0f, 0.0f); glTexCoord2f(-4.00f, 5.00f); glVertex3f( 10.0f, 10.0f, 0.0f); glEnd(); glDisable(GL_TEXTURE_2D); } ... };
glTexEnv(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_REPLACE);controls how the texture color $C_s$ and its alpha value $A_s$ is combined with the current fragment
GL_REPLACE
, GL_MODULATE
, GL_DECAL
, GL_BLEND
,
oder GL_ADD
GL_RGB | GL_RGBA | |
---|---|---|
GL_REPLACE | $C=C_s$ $A=A_f$ | $C=C_s$ $A=A_s$ |
GL_MODULATE | $C=C_fC_s$ $A=A_f$ | $C=C_f C_s$ $A=A_f A_s$ |
GL_DECAL | $C=C_s$ $A=A_f$ | $C=C_f (1-A_s) + C_s A_s$ $A=A_f$ |
GL_BLEND | $C=C_f(1-C_s)+C_c C_s$ $A=A_f$ | $C=C_f(1-C_s)+C_c C_s$ $A=A_f A_s$ |
GL_ADD | $C=C_f+ C_s$ $A=A_f$ | $C=C_f+ C_s$ $A=A_f A_s$ |
$\begin{align} s &= \frac{x-x_{\small \mathrm{min}} }{ x_{\small \mathrm{max}} - x_{\small \mathrm{min}} }\\ t &= \frac{y-y_{\small \mathrm{min}} }{ y_{\small \mathrm{max}} - y_{\small \mathrm{min}} } \end{align}$
$\begin{align} s &= h = \frac{z-z_{\small \mathrm{min}} }{ z_{\small \mathrm{max}} - z_{\small \mathrm{min}} }\\ t &= \frac{1}{2\pi} \phi = \frac{1}{2\pi} \arctan\left(\frac{y}{x}\right) \end{align}$
Notes: Since in practice $x=0$ can occur, it makes sense to replace the $\arctan$ with the $\mathrm{atan2}$
$\begin{align} s &= \frac{1}{2\pi} \phi = \frac{1}{2\pi} \arctan\left(\frac{y}{x}\right)\\ t &= 1.0 - \frac{1}{\pi} \theta \\ &= 1.0 - \frac{1}{\pi} \arccos(z) \\ &= \frac{1}{\pi} \arccos(-z)\\ \end{align}$
glTexImage2D
becomes glTexImage3D
and GL_TEXTURE_2D
becomes GL_TEXTURE_3D
, etc.
glTexImage3D
the memory layout can be interpreted as a series of 2D textures data
width
texel values, and in $r$-direction only after width*height
texel values
class Renderer { public: float t; private: GLuint texID; std::vector <float> terrain; public: // constructor Renderer() : t(0.0), texID(0) {} //destructor ~Renderer() { if(texID !=0) glDeleteTextures( 1, &texID); } public: void init() { glEnable(GL_DEPTH_TEST); glTexImage3D = (PFNGLTEXIMAGE3DPROC) wglGetProcAddress("glTexImage3D"); if(glTexImage3D == NULL) { QMessageBox msgBox; msgBox.setText("Can not load the glTexImage3D function"); msgBox.exec(); } std::vector< std::string > filenames; filenames.push_back("deep_water.ppm"); filenames.push_back("shallow_water.ppm"); filenames.push_back("shore.ppm"); filenames.push_back("fields.ppm"); filenames.push_back("rocks.ppm"); filenames.push_back("snow.ppm"); texID = loadTexture3D(filenames); rebuildTerrain(); } void resize(int w, int h) { glViewport(0, 0, w, h); glMatrixMode(GL_PROJECTION); glLoadIdentity(); gluPerspective (30.0, (float)w/(float)h, 0.1, 50.0); } void display() { glClearColor(0.0f, 0.0f, 0.0f, 0.0f); glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); glMatrixMode(GL_MODELVIEW); glLoadIdentity(); // set camera gluLookAt(1.5, -1.0, 1.5, 0.0, 0.0, 0.0, 0.0, 0.0, 1.0); // draw scene glRotatef(t, 0.0f, 0.0f, 1.0f); drawTerrain(); } void rebuildTerrain() { //create random values int dim = 40; terrain.resize(dim*dim); for(int r=0; r < dim*dim; r++) { int rval = rand(); terrain[r] = (fabs(float(rval))/float(RAND_MAX)); } if(true) { // generate smooth terrain values std::vector<float≷ smoothTerrain(dim*dim); for(unsigned k=0; k < 5; k++){ float maxVal = 0.0f; float minVal = 1.0f; for(int x = 0; x < dim; x++) { for(int y = 0; y < dim; y++) { if(x == 0 || x == dim-1) terrain[x*dim+y] = 0.0f; else if (y == 0 || y == dim-1) { terrain[x*dim+y] = 0.0f; }else { float a = 0.0f; int counter = 0; for(int s=-1; s <= 1; s++) { for(int r=-1; r <= 1; r++) { a += terrain[(x+s)*dim+(y+r)]; counter++; } } float val = a / float(counter); smoothTerrain[x*dim+y] = val; if(val > maxVal) maxVal = val; if(val < minVal) minVal = val; } } } for(int r=0; r < dim*dim; r++) { terrain[r] = (smoothTerrain[r] - minVal) / (maxVal-minVal); } } } } private: // returns a valid textureID on success, otherwise 0 GLuint loadTexture3D(std::vector< std::string > &filenames) { unsigned width = 0; unsigned height = 0; unsigned depth = unsigned(filenames.size()); int level = 0; int border = 0; std::vector<unsigned char> imgData; std::vector<unsigned char> data3d; unsigned prevWidth = 0; unsigned prevHeight = 0; for(unsigned i=0; i < depth; i++) { // load image data if(!loadPPMImageFlipped(filenames[i], width, height, imgData)) return 0; if(i != 0 && (prevWidth != width || prevHeight != height)) return 0; // pack 2D images subsequently into a large 3D buffer data3d.insert(data3d.end(), imgData.begin(), imgData.end()); prevWidth = width; prevHeight = height; } // data is aligned in byte order glPixelStorei(GL_UNPACK_ALIGNMENT, 1); //request textureID GLuint textureID; glGenTextures( 1, &textureID); // bind texture glBindTexture(GL_TEXTURE_3D, textureID); //parameters the define how to warp the texture glTexParameteri(GL_TEXTURE_3D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_BORDER); glTexParameteri(GL_TEXTURE_3D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_BORDER); glTexParameteri(GL_TEXTURE_3D, GL_TEXTURE_WRAP_R, GL_CLAMP_TO_BORDER); GLfloat borderColor[4] = {0.0f,0.0f,0.0f,1.0f}; glTexParameterfv(GL_TEXTURE_3D, GL_TEXTURE_BORDER_COLOR, borderColor); //define how to filter the texture glTexParameteri (GL_TEXTURE_3D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); glTexParameteri (GL_TEXTURE_3D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); //texture colors should replace the original color values glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_REPLACE); //GL_MODULATE // specify the 2D texture map glTexImage3D(GL_TEXTURE_3D, level, GL_RGB, width, height, depth, border, GL_RGB, GL_UNSIGNED_BYTE, &data3d[0]); // return unique texture identifier return textureID; } void drawTerrain() { glEnable(GL_TEXTURE_3D); glBindTexture(GL_TEXTURE_3D, texID); glColor3f(1.0f,0.0f,0.0f); unsigned dim = unsigned(sqrt(terrain.size())); float maxHeight = 0.2f; float texHeight = 0.9f; for(unsigned x = 1; x < dim; x++) { for(unsigned y = 1; y < dim; y++) { glBegin(GL_POLYGON); glTexCoord3f(float(x-1)/float(dim), float(y-1)/float(dim), terrain[(x-1)*dim+(y-1)]*texHeight); glVertex3f(float(x-1)/float(dim)-0.5f, float(y-1)/float(dim)-0.5f, terrain[(x-1)*dim+(y-1)]*maxHeight); glTexCoord3f(float(x)/float(dim), float(y-1)/float(dim), terrain[x*dim+(y-1)]*texHeight); glVertex3f(float(x)/float(dim)-0.5f, float(y-1)/float(dim)-0.5f, terrain[x*dim+(y-1)]*maxHeight); glTexCoord3f(float(x)/float(dim), float(y)/float(dim), terrain[x*dim+y]*texHeight); glVertex3f(float(x)/float(dim)-0.5f, float(y)/float(dim)-0.5f, terrain[x*dim+y]*maxHeight); glTexCoord3f(float(x-1)/float(dim), float(y)/float(dim), terrain[(x-1)*dim+y]*texHeight); glVertex3f(float(x-1)/float(dim)-0.5f, float(y)/float(dim)-0.5f, terrain[(x-1)*dim+y]*maxHeight); glEnd(); } } glDisable(GL_TEXTURE_3D); } ... };
glGetIntegerv(GL_MAX_TEXTURE_IMAGE_UNITS, &noOfTextureUnits);
glActiveTexture
sets the currently modified texture unit:
glActiveTexture(GL_TEXTURE0); glBindTexture(GL_TEXTURE_2D, texA); ... glActiveTexture(GL_TEXTURE1); glBindTexture(GL_TEXTURE_2D, texB); ...
glMatrixMode(GL_TEXTURE)
glGenFramebuffers(1, &fboID); glBindFramebuffer(GL_FRAMEBUFFER, fboID);
glFramebufferTexture2D(...)
glGenTextures (1, &texID); glBindTexture(GL_TEXTURE_2D, texID); glTexImage2D(GL_TEXTURE_2D, texLevel, texFormat, inWidth, inHeight, inBorder, inFormat, inType, NULL); glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, texID, texLevel);
GL_COLOR_ATTACHMENT0
, GL_COLOR_ATTACHMENT1
, ..., GL_DEPTH_ATTACHMENT
or GL_STENCIL_ATTACHMENT
glFramebufferRenderbuffer(..)
and then passed
with glFramebufferRenderbuffer(..)
to the FBO
glGenRenderbuffers(1, &depthID); glBindRenderbuffer(GL_RENDERBUFFER, depthID); glRenderbufferStorage(GL_RENDERBUFFER, format, width, height); glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_RENDERBUFFER, depthID);
format
should be chosen such that it fits the purpose,
that is: GL_RGBA
for GL_COLOR_ATTACHMENT0
or GL_DEPTH_COMPONENT
for GL_DEPTH_ATTACHMENT
, etc.
class Renderer { public: float t; int mode; private: GLuint fboID; GLuint texID; GLuint depthID; int fboTexSize; int width; int height; public: // constructor Renderer() : t(0.0), mode(1), fboID(0), texID(0), depthID(0), fboTexSize(512), width(0), height(0) {} // destructor ~Renderer() { if(texID !=0) glDeleteTextures( 1, &texID); if(fboID !=0) glDeleteFramebuffers(1, &fboID); if(depthID != 0) glDeleteRenderbuffers(1, &depthID); } public: void init() { glEnable(GL_DEPTH_TEST); // create a frame buffer object glGenFramebuffers(1, &fboID); // bind the frame buffer glBindFramebuffer(GL_FRAMEBUFFER, fboID); // Generate render texture glGenTextures (1, &texID); glBindTexture(GL_TEXTURE_2D, texID); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); int level = 0; glTexImage2D(GL_TEXTURE_2D, level, GL_RGBA, fboTexSize, fboTexSize, 0, GL_RGBA, GL_UNSIGNED_BYTE, NULL); glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_REPLACE); // Attach the texture to the fbo glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, texID, level); if(!checkFramebufferStatus()) exit(1); // generate a renderbuffer for the depth buffer glGenRenderbuffers(1, &depthID); glBindRenderbuffer(GL_RENDERBUFFER, depthID); glRenderbufferStorage(GL_RENDERBUFFER, GL_DEPTH_COMPONENT, fboTexSize, fboTexSize); // Attach the depth buffer to the fbo glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_RENDERBUFFER, depthID); if(!checkFramebufferStatus()) exit(1); // unbind texture glBindTexture(GL_TEXTURE_2D, 0); //unbind fbo glBindFramebuffer(GL_FRAMEBUFFER, 0); } void resize(int w, int h) { width = w; height = h; changeViewport(w, h); } void display() { if(mode == 1) { // bind the fbo glBindFramebuffer(GL_FRAMEBUFFER, fboID); changeViewport(fboTexSize,fboTexSize); } glClearColor(0.5f, 0.5f, 0.5f, 1.0f); // render to attached fbo drawScene(); if(mode == 1) { // unbind frame buffer glBindFramebuffer(GL_FRAMEBUFFER, 0); changeViewport(width, height); glClearColor(0.0f, 0.0f, 0.0f, 1.0f); glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); // using first render pass result as texture glEnable(GL_TEXTURE_2D); glBindTexture(GL_TEXTURE_2D, texID); // second render pass drawScene(); glDisable(GL_TEXTURE_2D); } } private: void changeViewport(int w, int h) { glViewport(0, 0, w, h); glMatrixMode(GL_PROJECTION); glLoadIdentity(); gluPerspective (30.0, (float)w/(float)h, 0.1, 50.0); } void drawScene() { glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); glMatrixMode(GL_MODELVIEW); glLoadIdentity(); // set camera gluLookAt(8.0, -2.0, 5.0, 0.0, 0.0, 0.0, 0.0, 0.0, 1.0); // draw scene glRotatef(t, 0.0f, 0.0f, 1.0f); drawTexturedCube(); } bool checkFramebufferStatus() { GLenum status; status = (GLenum) glCheckFramebufferStatus(GL_FRAMEBUFFER); switch(status) { case GL_FRAMEBUFFER_COMPLETE: return true; case GL_FRAMEBUFFER_INCOMPLETE_ATTACHMENT: printf("Framebuffer incomplete, incomplete attachment\n"); return false; case GL_FRAMEBUFFER_UNSUPPORTED: printf("Unsupported framebuffer format\n"); return false; case GL_FRAMEBUFFER_INCOMPLETE_MISSING_ATTACHMENT: printf("Framebuffer incomplete, missing attachment\n"); return false; case GL_FRAMEBUFFER_INCOMPLETE_DRAW_BUFFER: printf("Framebuffer incomplete, missing draw buffer\n"); return false; case GL_FRAMEBUFFER_INCOMPLETE_READ_BUFFER: printf("Framebuffer incomplete, missing read buffer\n"); return false; } return false; } void drawTexturedCube() { glColor3f(1.0f,0.0f,0.0f); glBegin(GL_POLYGON); glTexCoord2f(0.00f,0.00f); glVertex3f(-1.0f, 1.0f, 1.0f); glTexCoord2f(1.00f,0.00f); glVertex3f(-1.0f,-1.0f, 1.0f); glTexCoord2f(1.00f,1.00f); glVertex3f( 1.0f,-1.0f, 1.0f); glTexCoord2f(0.00f,1.00f); glVertex3f( 1.0f, 1.0f, 1.0f); glEnd(); glColor3f(1.0f,1.0f,0.0f); glBegin(GL_POLYGON); glTexCoord2f(0.00f,0.00f); glVertex3f(-1.0f, 1.0f,-1.0f); glTexCoord2f(1.00f,0.00f); glVertex3f(-1.0f,-1.0f,-1.0f); glTexCoord2f(1.00f,1.00f); glVertex3f(-1.0f,-1.0f, 1.0f); glTexCoord2f(0.00f,1.00f); glVertex3f(-1.0f, 1.0f, 1.0f); glEnd(); glColor3f(0.0f,0.0f,1.0f); glBegin(GL_POLYGON); glTexCoord2f(0.00f,0.00f); glVertex3f( 1.0f, 1.0f, 1.0f); glTexCoord2f(1.00f,0.00f); glVertex3f( 1.0f,-1.0f, 1.0f); glTexCoord2f(1.00f,1.00f); glVertex3f( 1.0f,-1.0f,-1.0f); glTexCoord2f(0.00f,1.00f); glVertex3f( 1.0f, 1.0f,-1.0f); glEnd(); glColor3f(1.0f,1.0f,1.0f); glBegin(GL_POLYGON); glTexCoord2f(0.00f,0.00f); glVertex3f(-1.0f, 1.0f,-1.0f); glTexCoord2f(1.00f,0.00f); glVertex3f(-1.0f, 1.0f, 1.0f); glTexCoord2f(1.00f,1.00f); glVertex3f( 1.0f, 1.0f, 1.0f); glTexCoord2f(0.00f,1.00f); glVertex3f( 1.0f, 1.0f,-1.0f); glEnd(); glColor3f(0.0f,1.0f,1.0f); glBegin(GL_POLYGON); glTexCoord2f(0.00f,0.00f); glVertex3f(-1.0f, -1.0f, 1.0f); glTexCoord2f(1.00f,0.00f); glVertex3f(-1.0f, -1.0f,-1.0f); glTexCoord2f(1.00f,1.00f); glVertex3f( 1.0f, -1.0f,-1.0f); glTexCoord2f(0.00f,1.00f); glVertex3f( 1.0f, -1.0f, 1.0f); glEnd(); glColor3f(0.0f,1.0f,0.0f); glBegin(GL_POLYGON); glTexCoord2f(0.00f,0.00f); glVertex3f( 1.0f, 1.0f,-1.0f); glTexCoord2f(1.00f,0.00f); glVertex3f( 1.0f,-1.0f,-1.0f); glTexCoord2f(1.00f,1.00f); glVertex3f(-1.0f,-1.0f,-1.0f); glTexCoord2f(0.00f,1.00f); glVertex3f(-1.0f, 1.0f,-1.0f); glEnd(); } };
class Renderer { public: float t; int mode; private: GLuint fboID; GLuint depthID; int fboTexSize; int width; int height; public: // constructor Renderer() : t(0.0), mode(1), fboID(0), depthID(0), fboTexSize(512), width(0), height(0) {} // destructor ~Renderer() { if(fboID !=0) glDeleteFramebuffers(1, &fboID); if(depthID != 0) glDeleteTextures(1, &depthID); } public: void init() { glEnable(GL_DEPTH_TEST); // create a frame buffer object glGenFramebuffers(1, &fboID); // bind the frame buffer glBindFramebuffer(GL_FRAMEBUFFER, fboID); // Generate depth render texture glGenTextures (1, &depthID); glBindTexture(GL_TEXTURE_2D, depthID); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); int level = 0; glTexImage2D(GL_TEXTURE_2D, level, GL_DEPTH_COMPONENT, fboTexSize, fboTexSize, 0, GL_DEPTH_COMPONENT, GL_FLOAT, NULL); glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_REPLACE); // Attach the texture to the fbo glFramebufferTexture2D(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_TEXTURE_2D, depthID, level); if(!checkFramebufferStatus()) exit(1); // unbind texture glBindTexture(GL_TEXTURE_2D, 0); //unbind fbo glBindFramebuffer(GL_FRAMEBUFFER, 0); } void resize(int w, int h) { width = w; height = h; changeViewport(w, h); } void display() { if(mode > 0) { // bind the fbo glBindFramebuffer(GL_FRAMEBUFFER, fboID); changeViewport(fboTexSize,fboTexSize); }else{ changeViewport(width, height); } glClearColor(0.0f, 0.0f, 0.0f, 1.0f); // render to attached fbo drawScene(); if(mode == 1) { // unbind frame buffer glBindFramebuffer(GL_FRAMEBUFFER, 0); changeViewportOrtho(width, height); // using first render pass result as texture glEnable(GL_TEXTURE_2D); glBindTexture(GL_TEXTURE_2D, depthID); //draw quad with depth texture glColor3f(1.0f,0.0f,0.0f); glBegin(GL_POLYGON); glTexCoord2f(0.00f,0.00f); glVertex3f(-1.0f,-1.0f, 0.0f); glTexCoord2f(1.00f,0.00f); glVertex3f( 1.0f,-1.0f, 0.0f); glTexCoord2f(1.00f,1.00f); glVertex3f( 1.0f, 1.0f, 0.0f); glTexCoord2f(0.00f,1.00f); glVertex3f(-1.0f, 1.0f, 0.0f); glEnd(); glDisable(GL_TEXTURE_2D); } } private: void changeViewport(int w, int h) { glViewport(0, 0, w, h); glMatrixMode(GL_PROJECTION); glLoadIdentity(); gluPerspective (30.0, (float)w/(float)h, 7.0, 12.0); } void changeViewportOrtho(int w, int h) { float aspect = (float)w/(float)h; glViewport(0, 0, width, height); glMatrixMode(GL_PROJECTION); glLoadIdentity(); glOrtho(-1.0*aspect, 1.0*aspect, -1.0, 1.0, -1.0, 1.0); glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); glMatrixMode(GL_MODELVIEW); glLoadIdentity(); } void drawScene() { glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); glMatrixMode(GL_MODELVIEW); glLoadIdentity(); // set camera gluLookAt(8.0, -2.0, 5.0, 0.0, 0.0, 0.0, 0.0, 0.0, 1.0); // draw scene glRotatef(t, 0.0f, 0.0f, 1.0f); drawTexturedCube(); } ... };
Please notify me by e-mail if you have questions, suggestions for improvement, or found typos: Contact