《OpenGL ES 2.0 Programming Guide》第9章“最简单的MipMap”示例代码【C语言版】

简介: 《OpenGL ES 2.0 Programming Guide》第9章“最简单的MipMap”示例代码【C语言版】

由于《OpenGL ES 2.0 Programming Guide》原书第9章的示例代码使用的纹理是程序生成的,作者还自己实现了Mipmap的过程,对于理解Mipmap的原理很有帮助,但是并不实用,遂自己实现了一份C语言版本的(加载本地纹理+调用glGenerateMipmap),希望能够帮助到同样喜欢OpenGL ES 2.0的同学。

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

// MipMap2D.c
//
//    This is a simple example that demonstrates generating a mipmap chain
//    and rendering with it
//
#include <stdlib.h>
#include "esUtil.h"
typedef struct
{
   // Handle to a program object
   GLuint programObject;
   // Attribute locations
   GLint  positionLoc;
   GLint  texCoordLoc;
   // Sampler location
   GLint samplerLoc;
   // Offset location
   GLint offsetLoc;
   // Texture handle
   GLuint textureId;
} UserData;
///
//  From an RGB8 source image, generate the next level mipmap
//
GLboolean GenMipMap2D( GLubyte *src, GLubyte **dst, int srcWidth, int srcHeight, int *dstWidth, int *dstHeight )
{
   int x,
       y;
   int texelSize = 3;
   *dstWidth = srcWidth / 2;
   if ( *dstWidth <= 0 )
      *dstWidth = 1;
   *dstHeight = srcHeight / 2;
   if ( *dstHeight <= 0 )
      *dstHeight = 1;
   *dst = malloc ( sizeof(GLubyte) * texelSize * (*dstWidth) * (*dstHeight) );
   if ( *dst == NULL )
      return GL_FALSE;
   for ( y = 0; y < *dstHeight; y++ )
   {
      for( x = 0; x < *dstWidth; x++ )
      {
         int srcIndex[4];
         float r = 0.0f,
               g = 0.0f,
               b = 0.0f;
         int sample;
         // Compute the offsets for 2x2 grid of pixels in previous
         // image to perform box filter
         srcIndex[0] = 
            (((y * 2) * srcWidth) + (x * 2)) * texelSize;
         srcIndex[1] = 
            (((y * 2) * srcWidth) + (x * 2 + 1)) * texelSize; 
         srcIndex[2] = 
            ((((y * 2) + 1) * srcWidth) + (x * 2)) * texelSize;
         srcIndex[3] = 
            ((((y * 2) + 1) * srcWidth) + (x * 2 + 1)) * texelSize;
         // Sum all pixels
         for ( sample = 0; sample < 4; sample++ )
         {
            r += src[srcIndex[sample]];
            g += src[srcIndex[sample] + 1];
            b += src[srcIndex[sample] + 2];
         }
         // Average results
         r /= 4.0;
         g /= 4.0;
         b /= 4.0;
         // Store resulting pixels
         (*dst)[ ( y * (*dstWidth) + x ) * texelSize ] = (GLubyte)( r );
         (*dst)[ ( y * (*dstWidth) + x ) * texelSize + 1] = (GLubyte)( g );
         (*dst)[ ( y * (*dstWidth) + x ) * texelSize + 2] = (GLubyte)( b );
      }
   }
   return GL_TRUE;
}
///
//  Generate an RGB8 checkerboard image
//
GLubyte* GenCheckImage( int width, int height, int checkSize )
{
   int x,
       y;
   GLubyte *pixels = malloc( width * height * 3 );
   if ( pixels == NULL )
      return NULL;
   for ( y = 0; y < height; y++ )
      for ( x = 0; x < width; x++ )
      {
         GLubyte rColor = 0;
         GLubyte bColor = 0;
         if ( ( x / checkSize ) % 2 == 0 )
         {
            rColor = 255 * ( ( y / checkSize ) % 2 );
            bColor = 255 * ( 1 - ( ( y / checkSize ) % 2 ) );
         }
         else
         {
            bColor = 255 * ( ( y / checkSize ) % 2 );
            rColor = 255 * ( 1 - ( ( y / checkSize ) % 2 ) );
         }
         pixels[(y * height + x) * 3] = rColor;
         pixels[(y * height + x) * 3 + 1] = 0;
         pixels[(y * height + x) * 3 + 2] = bColor; 
      } 
   return pixels;
}
///
// 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;
}
///
// Create a mipmapped 2D texture image 
//
GLuint CreateMipMappedTexture2D( )
{
   // Texture object handle
   GLuint textureId;
#if 0
   int    width = 256,
          height = 256;
   int    level;
   GLubyte *pixels;
   pixels = GenCheckImage( width, height, 8 );
   if ( pixels == NULL )
      return 0;
   // Generate a texture object
   glGenTextures ( 1, &textureId );
   // Bind the texture object
   glBindTexture ( GL_TEXTURE_2D, textureId );
   // Load mipmap level 0
   glTexImage2D ( GL_TEXTURE_2D, 0, GL_RGB, width, height, 
                  0, GL_RGB, GL_UNSIGNED_BYTE, pixels );
   GLubyte *prevImage;
   GLubyte *newImage;
   level = 1;
   prevImage = &pixels[0];
   while ( width > 1 && height > 1 )
   {
      int newWidth,
          newHeight;
      // Generate the next mipmap level
    GenMipMap2D( prevImage, &newImage, width, height, 
      &newWidth, &newHeight );
      // Load the mipmap level
      glTexImage2D( GL_TEXTURE_2D, level, GL_RGB, 
                    newWidth, newHeight, 0, GL_RGB,
                    GL_UNSIGNED_BYTE, newImage );
      // Free the previous image
      free ( prevImage );
      // Set the previous image for the next iteration
      prevImage = newImage;
      level++;
      // Half the width and height
      width = newWidth;
      height = newHeight;
   }
   free ( newImage );
#else
   char *fileName = "D:/Projects/Visual Studio 2012/OpenGL_Demo/opengles-book-samples-master/Windows/Chapter_9/Simple_Texture2D/Fieldstone.tga";
   textureId = LoadTexture (fileName);
   glGenerateMipmap(GL_TEXTURE_2D);
#endif
   // Set the filtering mode
   glTexParameteri ( GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST_MIPMAP_NEAREST );
   glTexParameteri ( GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR );
   return textureId;
}
///
// Initialize the shader and program object
//
int Init ( ESContext *esContext )
{
   UserData *userData = esContext->userData;
   GLbyte vShaderStr[] =
      "uniform float u_offset;      \n"
      "attribute vec4 a_position;   \n"
      "attribute vec2 a_texCoord;   \n"
      "varying vec2 v_texCoord;     \n"
      "void main()                  \n"
      "{                            \n"
      "   gl_Position = a_position; \n"
      "   gl_Position.x += u_offset;\n"
      "   v_texCoord = a_texCoord;  \n"
      "}                            \n";
   GLbyte fShaderStr[] =  
      "precision mediump float;                            \n"
      "varying vec2 v_texCoord;                            \n"
      "uniform sampler2D s_texture;                        \n"
      "void main()                                         \n"
      "{                                                   \n"
      "  gl_FragColor = texture2D( s_texture, v_texCoord );\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" );
   userData->texCoordLoc = glGetAttribLocation ( userData->programObject, "a_texCoord" );
   // Get the sampler location
   userData->samplerLoc = glGetUniformLocation ( userData->programObject, "s_texture" );
   // Get the offset location
   userData->offsetLoc = glGetUniformLocation( userData->programObject, "u_offset" );
   // Load the texture
   userData->textureId = CreateMipMappedTexture2D ();
   glClearColor ( 0.0f, 0.0f, 0.0f, 0.0f );
   return TRUE;
}
///
// Draw a triangle using the shader pair created in Init()
//
void Draw ( ESContext *esContext )
{
   UserData *userData = esContext->userData;
   GLfloat vVertices[] = { -0.5f,  0.5f, 0.0f, 1.5f,  // Position 0
                            0.0f,  0.0f,              // TexCoord 0 
                           -0.5f, -0.5f, 0.0f, 0.75f, // Position 1
                            0.0f,  1.0f,              // TexCoord 1
                            0.5f, -0.5f, 0.0f, 0.75f, // Position 2
                            1.0f,  1.0f,              // TexCoord 2
                            0.5f,  0.5f, 0.0f, 1.5f,  // Position 3
                            1.0f,  0.0f               // TexCoord 3
                         };
   GLushort indices[] = { 0, 1, 2, 0, 2, 3 };
   // Set the viewport
   glViewport ( 0, 0, esContext->width, esContext->height );
   // Clear the color buffer
   glClear ( GL_COLOR_BUFFER_BIT );
   // Use the program object
   glUseProgram ( userData->programObject );
   // Load the vertex position
   glVertexAttribPointer ( userData->positionLoc, 4, GL_FLOAT, 
                           GL_FALSE, 6 * sizeof(GLfloat), vVertices );  // 偏移量 4+2
   // Load the texture coordinate
   glVertexAttribPointer ( userData->texCoordLoc, 2, GL_FLOAT,
                           GL_FALSE, 6 * sizeof(GLfloat), &vVertices[4] );
   glEnableVertexAttribArray ( userData->positionLoc );
   glEnableVertexAttribArray ( userData->texCoordLoc );
   // Bind the texture
   glActiveTexture ( GL_TEXTURE0 ); // 激活 texture unit 0
   glBindTexture ( GL_TEXTURE_2D, userData->textureId );
   // Set the sampler texture unit to 0
   glUniform1i ( userData->samplerLoc, 0 );
   // Draw quad with nearest sampling (LEFT)
   glTexParameteri ( GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST );
   glUniform1f ( userData->offsetLoc, -0.6f );   
   glDrawElements ( GL_TRIANGLES, 6, GL_UNSIGNED_SHORT, indices );
   // Draw quad with trilinear filtering (RIGHT)
   glTexParameteri ( GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_LINEAR );
   glUniform1f ( userData->offsetLoc, 0.6f );
   glDrawElements ( GL_TRIANGLES, 6, GL_UNSIGNED_SHORT, indices );
   eglSwapBuffers ( esContext->eglDisplay, esContext->eglSurface );
}
///
// Cleanup
//
void ShutDown ( ESContext *esContext )
{
   UserData *userData = esContext->userData;
   // Delete texture object
   glDeleteTextures ( 1, &userData->textureId );
   // Delete program object
   glDeleteProgram ( userData->programObject );
}
int main ( int argc, char *argv[] )
{
   ESContext esContext;
   UserData  userData;
   esInitContext ( &esContext );
   esContext.userData = &userData;
   esCreateWindow ( &esContext, "MipMap 2D", 640, 480, ES_WINDOW_RGB );
   if ( !Init ( &esContext ) )
      return 0;
   esRegisterDrawFunc ( &esContext, Draw );
   esMainLoop ( &esContext );
   ShutDown ( &esContext );
}

效果如图:

image.png

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