《OpenGL ES 2.0 Programming Guide》第12章 “最简单的本地纹理+FBO+VBO”示例代码【C语言版】

简介: 《OpenGL ES 2.0 Programming Guide》第12章 “最简单的本地纹理+FBO+VBO”示例代码【C语言版】

由于《OpenGL ES 2.0 Programming Guide》原书第12章并没有提供相关的示例,为了加深理解,遂自己实现了一份C语言版本作为练习,希望能够帮助到同样喜欢OpenGL ES 2.0的同学。


废话不多说,直接上代码:

#include <stdlib.h>
#include <stdio.h>
#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 );
}

效果图:

image.png

目录
相关文章
|
2月前
|
C语言
对链表使用插入排序的C语言实现示例
对链表使用插入排序的C语言实现示例
|
2月前
|
算法 C语言
在C语言中函数的递归调用及应用示例
在C语言中函数的递归调用及应用示例
30 1
|
2月前
|
C语言
最简单的C语言程序示例
最简单的C语言程序示例
36 0
|
2月前
|
C语言
在C语言中数组作为函数参数的应用与示例
在C语言中数组作为函数参数的应用与示例
30 0
|
2月前
|
C语言
在C语言中多维数组名作为函数参数的应用与示例
在C语言中多维数组名作为函数参数的应用与示例
26 0
|
1月前
|
测试技术 C语言
数据结构学习记录——树习题—Tree Traversals Again(题目描述、输入输出示例、解题思路、解题方法C语言、解析)
数据结构学习记录——树习题—Tree Traversals Again(题目描述、输入输出示例、解题思路、解题方法C语言、解析)
22 1
|
1月前
|
编译器 C语言
C语言尾递归知识及代码示例
C语言尾递归知识及代码示例
17 0
|
2月前
|
C语言
在C语言中内部函数的应用与示例
在C语言中内部函数的应用与示例
29 2
|
2月前
|
C语言
在C语言中函数的返回值及其应用示例
在C语言中函数的返回值及其应用示例
26 2
|
2月前
|
C语言
在C语言中调用函数的基本原理及示例
在C语言中调用函数的基本原理及示例
68 0