由于《OpenGL ES 2.0 Programming Guide》原书第12章并没有提供相关的示例,为了加深理解,遂自己实现了一份C语言版本作为练习,希望能够帮助到同样喜欢OpenGL ES 2.0的同学。
废话不多说,直接上代码:
#include
#include
#include "esUtil.h"
#include "userData.h"
#define SIZE 512
///
// Load texture from disk
//
GLuint LoadTexture ( char *fileName )
{
int width, height;
char *buffer = esLoadTGA ( fileName, &width, &height );
GLuint texId;
if ( buffer == NULL )
{
esLogMessage ( "Error loading (%s) image.\n", fileName );
return 0;
}
glGenTextures ( 1, &texId );
glBindTexture ( GL_TEXTURE_2D, texId );
glTexImage2D ( GL_TEXTURE_2D, 0, GL_RGB, width, height, 0, GL_RGB, GL_UNSIGNED_BYTE, buffer );
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 );
free ( buffer );
return texId;
}
int initFBO(ESContext *esContext, GLint width, GLint height)
{
GLenum status;
GLint maxRenderbufferSize;
UserData *userData = (UserData *)esContext->userData;
glGetIntegerv(GL_MAX_RENDERBUFFER_SIZE, &maxRenderbufferSize);
// check if GL_MAX_RENDERBUFFER_SIZE is >= texWidth and texHeight
if ((maxRenderbufferSize <= width) || (maxRenderbufferSize <= height))
{
// cannot use framebuffer objects as we need to create
// a depth buffer as a renderbuffer object
printf("Cannot use framebuffer objects!\n");
exit(EXIT_FAILURE);
return FALSE;
}
// generate the framebuffer, renderbuffer names
glGenFramebuffers(1, &userData->frameBuffer);
glGenRenderbuffers(1, &userData->depthRenderBuffer);
// bind renderbuffer and create a 16-bit depth buffer
// width and height of renderbuffer = width and height of
// the texture
glBindRenderbuffer(GL_RENDERBUFFER, userData->depthRenderBuffer);
glRenderbufferStorage(GL_RENDERBUFFER, GL_DEPTH_COMPONENT16, width, height);
// Texture
glGenTextures(1, &userData->textureId);
glBindTexture(GL_TEXTURE_2D, userData->textureId);
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, width, height, 0,
GL_RGB, GL_UNSIGNED_SHORT_5_6_5, NULL);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST );
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST );
// bind the framebuffer
glBindFramebuffer(GL_FRAMEBUFFER, userData->frameBuffer);
// ☆ specify texture as color attachment ☆
glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, userData->textureId, 0);
// specify depth_renderbufer as depth attachment
glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_RENDERBUFFER, userData->depthRenderBuffer);
// check for framebuffer complete
status = glCheckFramebufferStatus(GL_FRAMEBUFFER);
if (status != GL_FRAMEBUFFER_COMPLETE)
{
printf("Framebuffer object is not complete!\n");
exit(EXIT_FAILURE);
return FALSE;
}
//glBindFramebuffer(GL_FRAMEBUFFER, 0);
return TRUE;
}
int InitFBOShader(ESContext *esContext)
{
UserData *userData = (UserData *)esContext->userData;
const char vShaderStr[] =
"attribute vec4 a_position; \n"
"attribute vec2 a_texCoord; \n"
"varying vec2 v_texCoord; \n"
"void main() \n"
"{ \n"
" gl_Position = a_position; \n"
" v_texCoord = a_texCoord; \n"
"} \n";
const char fShaderStr[] =
"precision mediump float; \n"
"varying vec2 v_texCoord; \n"
"uniform sampler2D s_texture; \n"
"const vec2 texSize = vec2(640., 640.); \n"
"const vec2 mosaicSize = vec2(16., 16.); \n"
"void main() \n"
"{ \n"
" vec4 color; \n"
" vec2 xy = vec2(v_texCoord.x * texSize.x, v_texCoord.y * texSize.y); \n"
" vec2 xyMosaic = vec2(floor(xy.x / mosaicSize.x) * mosaicSize.x, \n"
" floor(xy.y / mosaicSize.y) * mosaicSize.y ); \n"
" //第几块mosaic \n"
" vec2 xyFloor = vec2(floor(mod(xy.x, mosaicSize.x)), \n"
" floor(mod(xy.y, mosaicSize.y))); \n"
" vec2 uvMosaic = vec2(xyMosaic.x / texSize.x, xyMosaic.y / texSize.y);\n"
" color = texture2D( s_texture, uvMosaic ); \n"
" gl_FragColor = color; \n"
"} \n";
userData->programFBOObject = esLoadProgram(vShaderStr, fShaderStr);
// Bind vPosition to attribute 0
//glBindAttribLocation(userData->programFBOObject, 0, "a_position");
userData->positionFBOLoc = glGetAttribLocation ( userData->programFBOObject, "a_position" );
userData->mvpFBOLoc = glGetUniformLocation( userData->programFBOObject, "u_mvpMatrix" );
userData->texcoordFBOLoc = glGetAttribLocation(userData->programFBOObject, "a_texCoord");
userData->samplerFBOLoc = glGetUniformLocation(userData->programFBOObject, "s_texture");
// Load the texture ☆
userData->textureFBOId = LoadTexture("../beard.tga");
// VBO Id
userData->vboFBOIds[0] = 0;
userData->vboFBOIds[1] = 0;
return TRUE;
}
int initShader(ESContext *esContext)
{
UserData *userData = (UserData *)esContext->userData;
const char vShaderStr[] =
"uniform mat4 u_mvpMatrix; \n"
"attribute vec4 a_position; \n"
"attribute vec2 a_texCoord; \n"
"varying vec2 v_texCoord; \n"
"void main() \n"
"{ \n"
" gl_Position = u_mvpMatrix * a_position; \n"
" v_texCoord = a_texCoord; \n"
"} \n";
const char fShaderStr[] =
"precision mediump float; \n"
"varying vec2 v_texCoord; \n"
"uniform sampler2D s_texture; \n"
"void main() \n"
"{ \n"
" vec4 color = texture2D(s_texture, v_texCoord); \n"
" gl_FragColor = color; \n"
"} \n";
// Load the shaders and get a linked program object
userData->programObject = esLoadProgram ( vShaderStr, fShaderStr );
// Get the attribute locations
userData->positionLoc = glGetAttribLocation ( userData->programObject, "a_position" );
// Get the uniform locations
userData->mvpLoc = glGetUniformLocation( userData->programObject, "u_mvpMatrix" );
// Get the texture attribute locations
userData->texcoordLoc = glGetAttribLocation(userData->programObject, "a_texCoord");
userData->samplerLoc = glGetUniformLocation(userData->programObject, "s_texture");
// VBO Id
userData->vboIds[0] = 0;
userData->vboIds[1] = 0;
userData->vboIds[2] = 0;
return TRUE;
}
int Init(ESContext *esContext)
{
UserData *userData = (UserData *)esContext->userData;
if (!InitFBOShader(esContext))
{
printf("initFBOShader exception ! \n");
return FALSE;
}
if (!initShader(esContext))
{
printf("initShader exception ! \n");
return FALSE;
}
initFBO(esContext, SIZE, SIZE);
// Generate the vertex data
userData->numIndices = esGenCube( 1.0, &userData->vertices,
NULL, &userData->texcoords, &userData->indices );
// Starting rotation angle for the cube
userData->angle = 45.0f;
glClearColor ( 0.0f, 0.0f, 0.0f, 0.0f );
glClearDepthf( 1.0f );
glEnable(GL_DEPTH_TEST);
glEnable(GL_CULL_FACE);
//glEnable(GL_BLEND);
//glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
return TRUE;
}
// Update the mvp matrix
void Update(ESContext *esContext, float deltaTime)
{
UserData *userData = (UserData *) esContext->userData;
ESMatrix perspective;
ESMatrix modelview;
float aspect;
// Compute a rotation angle based on time to rotate the cube
userData->angle += ( deltaTime * 40.0f );
if( userData->angle >= 360.0f )
userData->angle -= 360.0f;
// Compute the window aspect ratio
aspect = (GLfloat) esContext->width / (GLfloat) esContext->height;
// Generate a perspective matrix with a 60 degree FOV
esMatrixLoadIdentity( &perspective );
esPerspective( &perspective, 60.0f, aspect, 1.0f, 20.0f );
// Generate a model view matrix to rotate/translate the cube
esMatrixLoadIdentity( &modelview );
// Translate away from the viewer
esTranslate( &modelview, 0.0, 0.0, -2.0 );
// Rotate the cube
esRotate( &modelview, userData->angle, 1.0, 0.0, 1.0 );
// Compute the final MVP by multiplying the
// modevleiw and perspective matrices together
esMatrixMultiply( &userData->mvpMatrix, &modelview, &perspective );
}
void DrawToFBO(ESContext *esContext)
{
UserData *userData = (UserData *)esContext->userData;
const GLfloat vVertices[] =
{
-1.f, 1.f, 0.0f, // Position 0
0.0f, 0.0f, // TexCoord 0
-1.f, -1.f, 0.0f, // Position 1
0.0f, 1.0f, // TexCoord 1
1.f, -1.f, 0.0f, // Position 2
1.0f, 1.0f, // TexCoord 2
1.f, 1.f, 0.0f, // Position 3
1.0f, 0.0f // TexCoord 3
};
const GLushort indices[] = { 0, 1, 2, 0, 2, 3 };
const int numIndices = 6;
const int numVertices = 4;
glBindFramebuffer(GL_FRAMEBUFFER, userData->frameBuffer);
// ----------------------------------------------------------
// Set the viewport
glViewport ( 0, 0, esContext->width, esContext->height );
// Clear the color buffer
glClearColor ( 1.0f, 1.0f, 1.0f, 1.0f );
glClear ( GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
// Use the program object
glUseProgram ( userData->programFBOObject );
// ----------------------------------------------------------
// Setup VBO
if(userData->vboFBOIds[0] == 0 &&
userData->vboFBOIds[1] == 0)
{
// Only allocate on the first draw
glGenBuffers ( 2, userData->vboFBOIds );
glBindBuffer ( GL_ARRAY_BUFFER, userData->vboFBOIds[0] );
glBufferData ( GL_ARRAY_BUFFER, (3 + 2) * sizeof(GLfloat) * numVertices,
vVertices, GL_STATIC_DRAW );
glBindBuffer ( GL_ELEMENT_ARRAY_BUFFER, userData->vboFBOIds[1] );
glBufferData ( GL_ELEMENT_ARRAY_BUFFER, numIndices * sizeof(GLushort),
indices, GL_STATIC_DRAW );
}
// -----------------------------------------------------------
// Load the vertex position
//glVertexAttribPointer ( userData->positionFBOLoc, 3, GL_FLOAT,
// GL_FALSE, 5 * sizeof(GLfloat), vVertices);
glBindBuffer ( GL_ARRAY_BUFFER, userData->vboFBOIds[0] );
glVertexAttribPointer ( userData->positionFBOLoc, 3, GL_FLOAT,
GL_FALSE, (3 + 2) * sizeof(GLfloat), 0 );
glEnableVertexAttribArray ( userData->positionFBOLoc );
// -----------------------------------------------------------
// Load the texture coordinate
//glVertexAttribPointer(userData->texcoordFBOLoc, 2, GL_FLOAT,
// GL_FALSE, 5 * sizeof(GLfloat), &vVertices[3]);
glBindBuffer ( GL_ARRAY_BUFFER, userData->vboFBOIds[0] );
glVertexAttribPointer ( userData->texcoordFBOLoc, 2, GL_FLOAT,
GL_FALSE, (3 + 2) * sizeof(GLfloat), (void *)(3 * sizeof(GLfloat)));
glEnableVertexAttribArray(userData->texcoordFBOLoc);
// -----------------------------------------------------------
// Bind the FBO texture
// Set the sampler texture unit to 0
glActiveTexture(GL_TEXTURE0);
glBindTexture(GL_TEXTURE_2D, userData->textureFBOId);
glUniform1i(userData->samplerFBOLoc, 0);
// -----------------------------------------------------------
// Draw the quad
glBindBuffer ( GL_ELEMENT_ARRAY_BUFFER, userData->vboFBOIds[1] );
//glDrawElements ( GL_TRIANGLES, numIndices, GL_UNSIGNED_SHORT, indices );
glDrawElements ( GL_TRIANGLES, numIndices, GL_UNSIGNED_SHORT, 0 );
// -----------------------------------------------------------
// Clean
glBindTexture(GL_TEXTURE_2D, 0);
glBindBuffer ( GL_ARRAY_BUFFER, 0 );
glBindBuffer ( GL_ELEMENT_ARRAY_BUFFER, 0 );
glDisableVertexAttribArray ( userData->positionFBOLoc );
glDisableVertexAttribArray ( userData->texcoordFBOLoc );
}
void Draw(ESContext *esContext)
{
UserData *userData = (UserData *)esContext->userData;
DrawToFBO(esContext);
glBindFramebuffer(GL_FRAMEBUFFER, 0);
// ----------------------------------------------------------
// Set the viewport
glViewport ( 0, 0, esContext->width, esContext->height );
// Clear the color buffer
glClearColor (0.0f, 0.0f, 0.0f, 0.0f);
glClear ( GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
// Use the program object
glUseProgram ( userData->programObject );
// ----------------------------------------------------------
const int numVertices = 24;
// Setup VBO
if(userData->vboIds[0] == 0 &&
userData->vboIds[1] == 0 &&
userData->vboIds[2] == 0)
{
// Only allocate on the first draw
glGenBuffers ( 3, userData->vboIds );
glBindBuffer ( GL_ARRAY_BUFFER, userData->vboIds[0] );
glBufferData ( GL_ARRAY_BUFFER, 3 * sizeof(GLfloat) * numVertices,
userData->vertices, GL_STATIC_DRAW );
glBindBuffer ( GL_ARRAY_BUFFER, userData->vboIds[1] );
glBufferData ( GL_ARRAY_BUFFER, 2 * sizeof(GLfloat) * numVertices,
userData->texcoords, GL_STATIC_DRAW );
glBindBuffer ( GL_ELEMENT_ARRAY_BUFFER, userData->vboIds[2] );
glBufferData ( GL_ELEMENT_ARRAY_BUFFER, userData->numIndices * sizeof(GLuint),
userData->indices, GL_STATIC_DRAW );
}
// -----------------------------------------------------------
// Load the vertex position
//glVertexAttribPointer ( userData->positionLoc, 3, GL_FLOAT,
// GL_FALSE, 3 * sizeof(GLfloat), userData->vertices );
glBindBuffer ( GL_ARRAY_BUFFER, userData->vboIds[0] );
glVertexAttribPointer ( userData->positionLoc, 3, GL_FLOAT,
GL_FALSE, 3 * sizeof(GLfloat), 0 );
glEnableVertexAttribArray ( userData->positionLoc );
// -----------------------------------------------------------
// Load the texture coordinate
//glVertexAttribPointer(userData->texcoordLoc, 2, GL_FLOAT,
// GL_FALSE, 2 * sizeof(GLfloat), userData->texcoords);
glBindBuffer ( GL_ARRAY_BUFFER, userData->vboIds[1] );
glVertexAttribPointer(userData->texcoordLoc, 2, GL_FLOAT,
GL_FALSE, 2 * sizeof(GLfloat), 0);
glEnableVertexAttribArray(userData->texcoordLoc);
// -----------------------------------------------------------
// Load the MVP matrix
glUniformMatrix4fv( userData->mvpLoc, 1, GL_FALSE,
(GLfloat *) &userData->mvpMatrix.m[0][0] );
// -----------------------------------------------------------
// Bind the new texture
// Set the sampler texture unit to 0
glActiveTexture(GL_TEXTURE0);
glBindTexture(GL_TEXTURE_2D, userData->textureId);
glUniform1i(userData->samplerLoc, 0);
// -----------------------------------------------------------
// Draw the cube
glBindBuffer ( GL_ELEMENT_ARRAY_BUFFER, userData->vboIds[2] );
//glDrawElements ( GL_TRIANGLES, userData->numIndices, GL_UNSIGNED_INT, userData->indices );
glDrawElements ( GL_TRIANGLES, userData->numIndices, GL_UNSIGNED_INT, 0 );
eglSwapBuffers ( esContext->eglDisplay, esContext->eglSurface );
// -----------------------------------------------------------
// Unbind the texture
glBindTexture(GL_TEXTURE_2D, 0);
glBindBuffer ( GL_ARRAY_BUFFER, 0 );
glBindBuffer ( GL_ELEMENT_ARRAY_BUFFER, 0 );
glDisableVertexAttribArray ( userData->positionLoc );
glDisableVertexAttribArray ( userData->texcoordLoc );
}
void ShutDown(ESContext *esContext)
{
UserData *userData = (UserData *)esContext->userData;
//glDisable(GL_BLEND);
if ( userData->vertices != NULL )
{
free ( userData->vertices );
}
if ( userData->texcoords != NULL )
{
free ( userData->texcoords );
}
if ( userData->indices != NULL )
{
free ( userData->indices );
}
// Delete program object
glDeleteProgram (userData->programObject);
glDeleteProgram(userData->programFBOObject);
// Delete texture
glDeleteTextures(1, &userData->textureId);
glDeleteTextures(1, &userData->textureFBOId);
glDeleteRenderbuffers(1, &userData->depthRenderBuffer);
glDeleteFramebuffers(1, &userData->frameBuffer);
// Delete VBO
glDeleteBuffers ( 2, userData->vboFBOIds );
glDeleteBuffers ( 3, userData->vboIds );
}
int main ( int argc, char *argv[] )
{
ESContext esContext;
UserData userData;
esInitContext ( &esContext );
esContext.userData = &userData;
esCreateWindow ( &esContext, "Simple FBO", SIZE, SIZE, ES_WINDOW_RGB | ES_WINDOW_MULTISAMPLE );
if ( !Init ( &esContext ) )
return 0;
esRegisterDrawFunc ( &esContext, Draw );
esRegisterUpdateFunc ( &esContext, Update );
esMainLoop ( &esContext );
ShutDown ( &esContext );
}