《OpenGL ES 2.0 Programming Guide》第12章“最简单的ReadPixels并保存为BMP”示例代码【C语言版】

来源:互联网 发布:数据库考点总结 编辑:程序博客网 时间:2024/05/22 06:16
由于《OpenGL ES 2.0 Programming Guide》原书并没有提供第12章的示例代码,书上的代码也只提到关键的步骤,而网上大多是Android/iOS版本的示例,C/C++的大都基于OpenGL或OpenGL ES 3.0,为了加深理解,遂自己实现了一份C语言版本的,希望能够帮助到同样喜欢OpenGL ES 2.0的同学。

废话不多说,直接上代码


#include "stdafx.h"#include "esUtil.h"#include <stdlib.h>#include <stdio.h>#define SIZE 512typedef struct{    GLuint programFBOObject;    GLuint programObject;    GLuint texture;    GLuint frameBuffer;    GLuint depthRenderBuffer;    GLint positionFBOLoc;    GLint mvpFBOLoc;    GLint positionLoc;    GLint mvpLoc;    GLint texcoordLoc;    GLint samplerLoc;    GLfloat *vertices;    GLfloat *texcoords;    GLuint *indices;    int numIndices;    GLfloat angle;    ESMatrix mvpMatrix;} UserData;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->texture);    glBindTexture(GL_TEXTURE_2D, userData->texture);    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_MAG_FILTER, GL_LINEAR);    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_LINEAR);    //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->texture, 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[] =        "uniform mat4 u_mvpMatrix;                   \n"        "attribute vec4 a_position;                  \n"        "void main()                                 \n"        "{                                           \n"        "   gl_Position = u_mvpMatrix * a_position;  \n"        "}                                           \n";    const char fShaderStr[] =        "precision mediump float;                            \n"        "void main()                                         \n"        "{                                                   \n"        "  gl_FragColor = vec4(1.0, 0.0, 0.0, 1.0);          \n"        "}                                                   \n";    userData->programFBOObject = esLoadProgram(vShaderStr, fShaderStr);    if (!userData->programFBOObject)        return FALSE;    // 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" );    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");    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);    return TRUE;}// Update the mvp matrixvoid 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;    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 );    // -----------------------------------------------------------    // Load the vertex position    glVertexAttribPointer ( userData->positionFBOLoc, 3, GL_FLOAT,                            GL_FALSE, 3 * sizeof(GLfloat), userData->vertices );    glEnableVertexAttribArray ( userData->positionFBOLoc );    // -----------------------------------------------------------    // Load the texture coordinate    //glVertexAttribPointer(userData->texcoordLoc, 2, GL_FLOAT,    //GL_FALSE, 2 * sizeof(GLfloat), userData->texcoords);    //glEnableVertexAttribArray(userData->texcoordLoc);    // -----------------------------------------------------------    // Load the MVP matrix    glUniformMatrix4fv( userData->mvpFBOLoc, 1, GL_FALSE,                        (GLfloat *) &userData->mvpMatrix.m[0][0] );    // -----------------------------------------------------------    // Draw the cube    glDrawElements ( GL_TRIANGLES, userData->numIndices, GL_UNSIGNED_INT, userData->indices );}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 );    // -----------------------------------------------------------    // Load the vertex position    glVertexAttribPointer ( userData->positionLoc, 3, GL_FLOAT,                            GL_FALSE, 3 * sizeof(GLfloat), userData->vertices );    glEnableVertexAttribArray ( userData->positionLoc );    // -----------------------------------------------------------    // Load the texture coordinate    glVertexAttribPointer(userData->texcoordLoc, 2, GL_FLOAT,                          GL_FALSE, 2 * sizeof(GLfloat), userData->texcoords);    glEnableVertexAttribArray(userData->texcoordLoc);    // -----------------------------------------------------------    // Load the MVP matrix    glUniformMatrix4fv( userData->mvpLoc, 1, GL_FALSE,                        (GLfloat *) &userData->mvpMatrix.m[0][0] );    // -----------------------------------------------------------    // Bind the texture    // Set the sampler texture unit to 0    glActiveTexture(GL_TEXTURE0);    glBindTexture(GL_TEXTURE_2D, userData->texture);    glGenerateMipmap(GL_TEXTURE_2D);    glUniform1i(userData->samplerLoc, 0);    // -----------------------------------------------------------    // Draw the cube    glDrawElements ( GL_TRIANGLES, userData->numIndices, GL_UNSIGNED_INT, userData->indices );    eglSwapBuffers ( esContext->eglDisplay, esContext->eglSurface );    glBindTexture(GL_TEXTURE_2D, 0);}void ShutDown(ESContext *esContext){    UserData *userData = (UserData *)esContext->userData;    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->texture);    glDeleteRenderbuffers(1, &userData->depthRenderBuffer);    glDeleteFramebuffers(1, &userData->frameBuffer);}BOOL SaveBMP(const char *lpFileName){    GLint viewport[4];    glGetIntegerv(GL_VIEWPORT, viewport);    int width = viewport[2];    int height = viewport[3];    glPixelStorei(GL_PACK_ALIGNMENT, 4);    int nAlignWidth = (width * 24 + 31) / 32;    unsigned char *pdata = new unsigned char[nAlignWidth * height * 4];    memset(pdata, 0, nAlignWidth * height * 4);// 从当前绑定的 frame buffer 读取 pixels    glReadPixels(0, 0, width, height, GL_RGB, GL_UNSIGNED_BYTE, pdata);    //由RGB变BGR    for(int i = 0 ; i < width * height * 3 ; i += 3)    {        unsigned char tmpRGB;        tmpRGB     = pdata[i];        pdata[i]   = pdata[i + 2];        pdata[i + 2] = tmpRGB;    }    ////四字节对齐    //int nAlignWidth = (width*24+31)/32;    //unsigned char *pDataAfterAlign = (unsigned char *) malloc(4 * nAlignWidth * height);    //memset(pDataAfterAlign, 0, 4 * nAlignWidth * height);    //int nZero = 4*nAlignWidth - 3*width;    //for(int j=0; j<height; j++)    //{    //  for(int i=0; i<width*3; i++)    //  {    //     pDataAfterAlign[j*(nZero+3*width)+i] = pdata[3*width*j+i];    //  }    //}    BITMAPFILEHEADER Header;    BITMAPINFOHEADER HeaderInfo;    Header.bfType = 0x4D42;    Header.bfReserved1 = 0;    Header.bfReserved2 = 0;    Header.bfOffBits = (DWORD)(                           sizeof(BITMAPFILEHEADER) + sizeof(BITMAPINFOHEADER));    Header.bfSize = (DWORD)(                        sizeof(BITMAPFILEHEADER) + sizeof(BITMAPINFOHEADER)                        + nAlignWidth * height * 4);    HeaderInfo.biSize = sizeof(BITMAPINFOHEADER);    HeaderInfo.biWidth = width;    HeaderInfo.biHeight = height;    HeaderInfo.biPlanes = 1;    HeaderInfo.biBitCount = 24;    HeaderInfo.biCompression = 0;    HeaderInfo.biSizeImage = 4 * nAlignWidth * height;    HeaderInfo.biXPelsPerMeter = 0;    HeaderInfo.biYPelsPerMeter = 0;    HeaderInfo.biClrUsed = 0;    HeaderInfo.biClrImportant = 0;    FILE *pfile;    if (!(pfile = fopen(lpFileName, "wb+")))    {        printf("保存图像失败!\n");        return FALSE;    }    fwrite(&Header, 1, sizeof(BITMAPFILEHEADER), pfile);    fwrite(&HeaderInfo, 1, sizeof(BITMAPINFOHEADER), pfile);    fwrite(pdata, 1, HeaderInfo.biSizeImage, pfile);    fclose(pfile);    delete[] pdata;    return TRUE;}void Keyboard(ESContext *esContext, unsigned char key, int winPosX, int winPosY){    //esLogMessage ( "Keyboard: %c, %d, %d", key, winPosX, winPosY );    if (key == 'c' || key == 'C')    {        esLogMessage ( "KeyPressed: %c\n", key);        // TEST : copy from READ_BUFFER to texture        //glCopyTexSubImage2D(GL_TEXTURE_2D, 0, 10, 10, 15, 15, 100, 100);        SaveBMP("screenshot.bmp");    }}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 );    esRegisterKeyFunc( &esContext, Keyboard );    esMainLoop ( &esContext );    ShutDown ( &esContext );}


按 c 或 C 截屏,图片保存为 screenshot.bmp

0 0