码迷,mamicode.com
首页 > 编程语言 > 详细

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

时间:2016-11-18 19:03:53      阅读:314      评论:0      收藏:0      [点我收藏+]

标签:test   awt   glin   lte   语言   memset   argv   raw   fbi   

由于《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 512

typedef 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 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;

    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

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

标签:test   awt   glin   lte   语言   memset   argv   raw   fbi   

原文地址:http://blog.csdn.net/panda1234lee/article/details/52429200

(0)
(0)
   
举报
评论 一句话评论(0
登录后才能评论!
© 2014 mamicode.com 版权所有  联系我们:gaon5@hotmail.com
迷上了代码!