《OpenGL ES 2.0 Programming Guide》第12章 “最简单的Multi-Pass+VBO”示例代码【C语言版】

简介: 《OpenGL ES 2.0 Programming Guide》第12章 “最简单的Multi-Pass+VBO”示例代码【C语言版】

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


在实现的时候遇到的问题——FBO的 Ping Pong技术:


文章提到有三种可能的方法


image.png

image.png由于ES 2.0的 Frame Buffer 只支持一个Color Attachement,所以不能使用OpenGL 的 Multi-Attachment的方式(速度最快的方法3),然后尝试方法2,通过实时切换FBO 绑定的Color Attachement,但是没有成功(如果有人成功的话,希望指点一下),所以只好用了“最慢”的多FBO的方式(方法1)。



废话不多说,直接上代码(本例使用了2个Pass):

#include <stdlib.h>
#include <stdio.h>
#include "esUtil.h"
#include "utils.h"
#include "userData.h"
#define SIZE 512
GLint InitFbo(ESContext *esContext, GLint width, GLint height,
        GLuint *pFrameBuffer, GLuint *pTextureId, GLuint *pDepthRenderBuffer)
{
  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, pFrameBuffer);
  glGenRenderbuffers(1, pDepthRenderBuffer);
  // bind renderbuffer and create a 16-bit depth buffer
  // width and height of renderbuffer = width and height of
  // the texture
  glBindRenderbuffer(GL_RENDERBUFFER, *pDepthRenderBuffer);
  glRenderbufferStorage(GL_RENDERBUFFER, GL_DEPTH_COMPONENT16, width, height);
  // Dst Texture
  glGenTextures(1, pTextureId);
  glBindTexture(GL_TEXTURE_2D, *pTextureId);
  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, *pFrameBuffer);
  // ☆ specify texture as color attachment ☆
  glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, *pTextureId, 0);
  // specify depth_renderbufer as depth attachment
  glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_RENDERBUFFER, *pDepthRenderBuffer);
  // 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;
  }
  return TRUE;
}
GLint InitFboShader(ESContext *esContext, 
          const char* vFboShader, const char* fFboShader, 
          GLuint* pProgramObj, GLint* pPositionLoc, 
          GLint* pTexcoordLoc, GLint* pSamplerLoc)
{
  UserData *userData = (UserData *)esContext->userData;
  const char *vShaderStr = NULL, *fShaderStr = NULL;
  vShaderStr = (const char *)ReadShader(vFboShader);
  fShaderStr = (const char *)ReadShader(fFboShader);
  *pProgramObj = esLoadProgram(vShaderStr, fShaderStr);
  *pPositionLoc = glGetAttribLocation (*pProgramObj, "a_position" );
  *pTexcoordLoc = glGetAttribLocation(*pProgramObj, "a_texCoord");
  *pSamplerLoc = glGetUniformLocation(*pProgramObj, "s_texture");
  return TRUE;
}
GLint InitFboVbo(ESContext *esContext, GLuint* pVboFboIds)
{
  UserData *userData = (UserData *)esContext->userData;
  // VBO Id
  pVboFboIds[0] = 0;
  pVboFboIds[1] = 0;
        // Quad
  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;
  // Setup VBO
  glGenBuffers ( 2, pVboFboIds );
  glBindBuffer ( GL_ARRAY_BUFFER, pVboFboIds[0] );
  glBufferData ( GL_ARRAY_BUFFER, (3 + 2) * sizeof(GLfloat) * numVertices,
    vVertices, GL_STATIC_DRAW );
  glBindBuffer ( GL_ELEMENT_ARRAY_BUFFER, pVboFboIds[1] );
  glBufferData ( GL_ELEMENT_ARRAY_BUFFER, numIndices * sizeof(GLushort),
    indices, GL_STATIC_DRAW );
  return TRUE;
}
GLint InitShader(ESContext *esContext)
{
  UserData *userData = (UserData *)esContext->userData;
  const char *vShaderStr = NULL, *fShaderStr = NULL;
  vShaderStr = (const char *)ReadShader("shaders/vertex_shader.glsl");
  fShaderStr = (const char *)ReadShader("shaders/fragment_shader.glsl");
  // 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;
}
GLint InitVbo(ESContext *esContext)
{
  UserData *userData = (UserData *)esContext->userData;
  // VBO Id
  userData->vboIds[0] = 0;
  userData->vboIds[1] = 0;
  userData->vboIds[2] = 0;
  const int numVertices = 24;
  // Setup VBO
  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 );
  return TRUE;
}
GLint Init(ESContext *esContext)
{
  UserData *userData = (UserData *)esContext->userData;
  // Load the texture ☆
  userData->textureFBOId = LoadTexture("../beard.tga");
  // Pass 0
  if (!InitFboShader(esContext, 
    "shaders/vertex_fbo_shader.glsl", "shaders/fragment_fbo_shader.glsl",
    &userData->programFBOObject, &userData->positionFBOLoc, 
    &userData->texcoordFBOLoc, &userData->samplerFBOLoc))
  {
    printf("InitFboShader exception ! \n");
    return FALSE;
  }
  // Pass 1
  if (!InitFboShader(esContext, 
    "shaders/vertex_fbo_shader_.glsl", "shaders/fragment_fbo_shader_.glsl",
    &userData->programFBOObject_, &userData->positionFBOLoc_, 
    &userData->texcoordFBOLoc_, &userData->samplerFBOLoc_))
  {
    printf("InitFboShader exception ! \n");
    return FALSE;
  }
  // --------
  if (!InitShader(esContext))
  {
    printf("InitShader exception ! \n");
    return FALSE;
  }
  // --------
  // Pass 0
  InitFbo(esContext, SIZE, SIZE, 
    &userData->frameBuffer, 
    &userData->textureFBOId_, // dst texture 0
    &userData->depthRenderBuffer);
  InitFboVbo(esContext, userData->vboFBOIds);
  // Pass 1
  InitFbo(esContext, SIZE, SIZE, 
    &userData->frameBuffer_, 
    &userData->textureId,   // dst texture 1
    &userData->depthRenderBuffer_);
  InitFboVbo(esContext, userData->vboFBOIds_);
  // Generate the vertex data
  userData->numIndices = esGenCube( 1.0, &userData->vertices,
    NULL, &userData->texcoords, &userData->indices );
  InitVbo(esContext);
  // 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, 
         GLuint frameBuffer, GLuint programObj, GLuint texId, GLuint* pVboIds,
         GLint positionLoc, GLint texcoordLoc, GLint samplerLoc)
{
  UserData *userData = (UserData *)esContext->userData;
  const int numIndices = 6;
  glBindFramebuffer(GL_FRAMEBUFFER, frameBuffer);
  // ----------------------------------------------------------
  PrepareForDraw(esContext, programObj);
  // -----------------------------------------------------------
  // Load the vertex position
  glBindBuffer ( GL_ARRAY_BUFFER, pVboIds[0] );
  glVertexAttribPointer ( positionLoc, 3, GL_FLOAT,
    GL_FALSE, (3 + 2) * sizeof(GLfloat), 0 );
  glEnableVertexAttribArray ( positionLoc );
  // -----------------------------------------------------------
  // Load the texture coordinate
  glBindBuffer ( GL_ARRAY_BUFFER, pVboIds[0] );
  glVertexAttribPointer ( texcoordLoc, 2, GL_FLOAT,
    GL_FALSE, (3 + 2) * sizeof(GLfloat), (void *)(3 * sizeof(GLfloat)));
  glEnableVertexAttribArray(texcoordLoc);
  // -----------------------------------------------------------
  // Bind the FBO texture
  // Set the sampler texture unit to 0
  glActiveTexture(GL_TEXTURE0);
  glBindTexture(GL_TEXTURE_2D, texId);
  glUniform1i(samplerLoc, 0);
  // -----------------------------------------------------------
  // Draw the quad
  glBindBuffer ( GL_ELEMENT_ARRAY_BUFFER, pVboIds[1] );
  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 ( positionLoc );
  glDisableVertexAttribArray ( texcoordLoc );
}
void Draw(ESContext *esContext)
{
  UserData *userData = (UserData *)esContext->userData;
  // ----------------------------------------------------------
  // Pass 0
  DrawToFbo(esContext, 
    userData->frameBuffer, userData->programFBOObject, userData->textureFBOId, userData->vboFBOIds,
    userData->positionFBOLoc, userData->texcoordFBOLoc, userData->samplerFBOLoc);
  // Pass 1
  DrawToFbo(esContext, 
    userData->frameBuffer_, userData->programFBOObject_, userData->textureFBOId_, userData->vboFBOIds_,
    userData->positionFBOLoc_, userData->texcoordFBOLoc_, userData->samplerFBOLoc_);
  // ----------------------------------------------------------
  glBindFramebuffer(GL_FRAMEBUFFER, 0);
  // ----------------------------------------------------------
  PrepareForDraw(esContext, userData->programObject);
  // ----------------------------------------------------------
  // Load the vertex position
  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
  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, 0 );
  eglSwapBuffers ( esContext->eglDisplay, esContext->eglSurface );
  // -----------------------------------------------------------
  // Clean
  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 Multi Pass", SIZE, SIZE, ES_WINDOW_RGB | ES_WINDOW_MULTISAMPLE );
  if ( !Init ( &esContext ) )
    return 0;
  esRegisterDrawFunc ( &esContext, Draw );
  esRegisterUpdateFunc ( &esContext, Update );
  esMainLoop ( &esContext );
  ShutDown ( &esContext );
}

效果图:

20160905211910586.gif

目录
相关文章
|
4月前
|
XML 小程序 Java
【Android App】三维投影OpenGL ES的讲解及着色器实现(附源码和演示 超详细)
【Android App】三维投影OpenGL ES的讲解及着色器实现(附源码和演示 超详细)
51 0
|
存储 编解码 算法
Opengl ES之LUT滤镜(上)
Opengl ES之连载系列
337 0
|
数据安全/隐私保护 开发者
OpenGL ES 多目标渲染(MRT)
Opengl ES连载系列
218 0
|
数据安全/隐私保护 索引
Opengl ES之纹理数组
Opengl ES连载系列
172 0
|
数据安全/隐私保护
Opengl ES之水印贴图
Opengl ES之连载系列
86 0
|
Java 数据安全/隐私保护 Android开发
Opengl ES之矩阵变换(下)
Opengl ES连载系列
83 0
|
Java API 数据安全/隐私保护
Opengl ES之矩阵变换(上)
Opengl ES连载系列
82 0
|
存储
Opengl ES之踩坑记
Opengl ES之连载系列
90 0
|
存储 编解码 算法
Opengl ES之RGB转NV21
Opengl ES连载系列
115 0
|
并行计算 C++
Opengl ES之YUV数据渲染
Opengl ES连载系列
130 0