opengles2.0 headfirst sample triangle

在这篇文章里记录笔者开始阅读《OpenGL(R) ES 2.0 Programming Guide》得到的一些基础知识,比较零散。
int main ( int argc, char *argv[] )
{
   ESContext esContext;
   UserData  userData;

   esInitContext ( &esContext );
   esContext.userData = &userData;

   esCreateWindow ( &esContext, "Hello Triangle", 320, 240, ES_WINDOW_RGB );
   
   if ( !Init ( &esContext ) )
      return 0;

   esRegisterDrawFunc ( &esContext, Draw );
   
   esMainLoop ( &esContext );
}

//
// Book:      OpenGL(R) ES 2.0 Programming Guide
// Authors:   Aaftab Munshi, Dan Ginsburg, Dave Shreiner
// ISBN-10:   0321502795
// ISBN-13:   9780321502797
// Publisher: Addison-Wesley Professional
// URLs:      http://safari.informit.com/9780321563835
//            http://www.opengles-book.com
//


// Hello_Triangle.c
//
//    This is a simple example that draws a single triangle with
//    a minimal vertex/fragment shader.  The purpose of this 
//    example is to demonstrate the basic concepts of 
//    OpenGL ES 2.0 rendering.
#include <stdlib.h>
#include "esUtil.h"


typedef struct
{
   // Handle to a program object
   GLuint programObject;


} UserData;


///
// Create a shader object, load the shader source, and
// compile the shader.
//
GLuint LoadShader ( GLenum type, const char *shaderSrc )
{
   GLuint shader;
   GLint compiled;
   
   // Create the shader object
   shader = glCreateShader ( type );


   if ( shader == 0 )
   	return 0;


   // Load the shader source
   glShaderSource ( shader, 1, &shaderSrc, NULL );
   
   // Compile the shader
   glCompileShader ( shader );


   // Check the compile status
   glGetShaderiv ( shader, GL_COMPILE_STATUS, &compiled );


   if ( !compiled ) 
   {
      GLint infoLen = 0;


      glGetShaderiv ( shader, GL_INFO_LOG_LENGTH, &infoLen );
      
      if ( infoLen > 1 )
      {
         char* infoLog = malloc (sizeof(char) * infoLen );


         glGetShaderInfoLog ( shader, infoLen, NULL, infoLog );
         esLogMessage ( "Error compiling shader:\n%s\n", infoLog );            
         
         free ( infoLog );
      }


      glDeleteShader ( shader );
      return 0;
   }


   return shader;


}


///
// Initialize the shader and program object
//
int Init ( ESContext *esContext )
{
   UserData *userData = esContext->userData;
   GLbyte vShaderStr[] =  
      "attribute vec4 vPosition;    \n"
      "void main()                  \n"
      "{                            \n"
      "   gl_Position = vPosition;  \n"
      "}                            \n";
   
   GLbyte fShaderStr[] =  
      "precision mediump float;\n"\
      "void main()                                  \n"
      "{                                            \n"
      "  gl_FragColor = vec4 ( 1.0, 0.0, 0.0, 1.0 );\n"
      "}                                            \n";


   GLuint vertexShader;
   GLuint fragmentShader;
   GLuint programObject;
   GLint linked;


   // Load the vertex/fragment shaders / 加载后的shader处于编译但未链接的状态
   vertexShader = LoadShader ( GL_VERTEX_SHADER, vShaderStr );
   fragmentShader = LoadShader ( GL_FRAGMENT_SHADER, fShaderStr );


   // Create the program object
   programObject = glCreateProgram ( );
   
   if ( programObject == 0 )
      return 0;


   glAttachShader ( programObject, vertexShader );
   glAttachShader ( programObject, fragmentShader );


   // Bind vPosition to attribute 0   
   glBindAttribLocation ( programObject, 0, "vPosition" );


   // Link the program
   glLinkProgram ( programObject );


   // Check the link status
   glGetProgramiv ( programObject, GL_LINK_STATUS, &linked );


   if ( !linked ) 
   {
      GLint infoLen = 0;


      glGetProgramiv ( programObject, GL_INFO_LOG_LENGTH, &infoLen );
      
      if ( infoLen > 1 )
      {
         char* infoLog = malloc (sizeof(char) * infoLen );


         glGetProgramInfoLog ( programObject, infoLen, NULL, infoLog );
         esLogMessage ( "Error linking program:\n%s\n", infoLog );            
         
         free ( infoLog );
      }


      glDeleteProgram ( programObject );
      return FALSE;
   }


   // Store the program object
   userData->programObject = programObject;


   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.0f,  0.5f, 0.0f, 
                           -0.5f, -0.5f, 0.0f,
                            0.5f, -0.5f, 0.0f };
      
   // Set the viewport /设置视口,用在vertex shader的下一阶段,比如裁减,和光栅化 。经过裁剪等处理后到达渲染管线的Fragment shader阶段
   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 data / 使用index 关联之前生成的vertex shader属性到 vVertices
   glVertexAttribPointer ( 0, 3, GL_FLOAT, GL_FALSE, 0, vVertices );
   glEnableVertexAttribArray ( 0 );


   glDrawArrays ( GL_TRIANGLES, 0, 3 );


   eglSwapBuffers ( esContext->eglDisplay, esContext->eglSurface );
}




int main ( int argc, char *argv[] )
{
   ESContext esContext;
   UserData  userData;


   //这是本书提供的ES框架下的通用调用,
   //在方法间传递context是因为一些平台不允许声明全局变量,比如塞班。甚至在ANDROID平台上使用类是单例等全局状态也会有一些问题
   //这里context包含一些屏幕参数和函数指针,一个比较重要的成员是userData,这里用来记录程序需要使用的全局变量
   esInitContext ( &esContext );
   esContext.userData = &userData;


   esCreateWindow ( &esContext, "Hello Triangle", 320, 240, ES_WINDOW_RGB );
   
   //初始化函数,包括创建program, 编译链接shader程序;
   if ( !Init ( &esContext ) )
      return 0;


   //注册绘制回调函数
   esRegisterDrawFunc ( &esContext, Draw );
   
   esMainLoop ( &esContext );
}

代码取自《OpenGL(R) ES 2.0 Programming Guide》,中文注释是笔者的理解,有误非常感谢指正!代码片断主要注释了对笔者而言比较薄弱的知识点,一些glxxx函数的定义和使用读者可以自行参考opengles api 或者上面的书名得到详细的说明;上面的代码是opengles的基本操作步骤,下面来看下egl的接口和使用方法。

EGL是为了和系统做交互的中间层协议,具体可以参考书本。

这里我们关注main方法下的窗口操作,尝试理解EGL的接口。

int main ( int argc, char *argv[] )
{
   ESContext esContext;
   UserData  userData;

   //给Context分配内存
   esInitContext ( &esContext );
   esContext.userData = &userData;

   //这个函数很重要,负责创建窗口
   esCreateWindow ( &esContext, "Hello Triangle", 320, 240, ES_WINDOW_RGB );
   
   if ( !Init ( &esContext ) )
      return 0;

   esRegisterDrawFunc ( &esContext, Draw );
   
   esMainLoop ( &esContext );
}
接下来我们先看
esInitContext
///
//  esInitContext()
//
//      Initialize ES utility context.  This must be called before calling any other
//      functions.
//
void ESUTIL_API esInitContext ( ESContext *esContext )
{
   if ( esContext != NULL )
   {
      memset( esContext, 0, sizeof( ESContext) );
   }
}
就是初始化;

再看 ESContext,这里就是对EGL的一个封装结构体

typedef struct
{
   /// Put your user data here...
   void*       userData;


   /// Window width
   GLint       width;


   /// Window height
   GLint       height;


   /// Window handle
   EGLNativeWindowType  hWnd;


   /// EGL display
   EGLDisplay  eglDisplay;
      
   /// EGL context
   EGLContext  eglContext;


   /// EGL surface
   EGLSurface  eglSurface;


   /// Callbacks
   void (ESCALLBACK *drawFunc) ( void* );
   void (ESCALLBACK *keyFunc) ( void*, unsigned char, int, int );
   void (ESCALLBACK *updateFunc) ( void*, float deltaTime );
} ESContext;

我们重点从
esCreateWindow
中看怎么使用
eglDisplay
eglContext
eglSurface
///
//  esCreateWindow()
//
//      title - name for title bar of window
//      width - width of window to create
//      height - height of window to create
//      flags  - bitwise or of window creation flags 
//          ES_WINDOW_ALPHA       - specifies that the framebuffer should have alpha
//          ES_WINDOW_DEPTH       - specifies that a depth buffer should be created
//          ES_WINDOW_STENCIL     - specifies that a stencil buffer should be created
//          ES_WINDOW_MULTISAMPLE - specifies that a multi-sample buffer should be created
//
GLboolean ESUTIL_API esCreateWindow ( ESContext *esContext, const char* title, GLint width, GLint height, GLuint flags )
{
   EGLint attribList[] =
   {
       EGL_RED_SIZE,       5,
       EGL_GREEN_SIZE,     6,
       EGL_BLUE_SIZE,      5,
       EGL_ALPHA_SIZE,     (flags & ES_WINDOW_ALPHA) ? 8 : EGL_DONT_CARE,
       EGL_DEPTH_SIZE,     (flags & ES_WINDOW_DEPTH) ? 8 : EGL_DONT_CARE,
       EGL_STENCIL_SIZE,   (flags & ES_WINDOW_STENCIL) ? 8 : EGL_DONT_CARE,
       EGL_SAMPLE_BUFFERS, (flags & ES_WINDOW_MULTISAMPLE) ? 1 : 0,
       EGL_NONE
   };
   
   if ( esContext == NULL )
   {
      return GL_FALSE;
   }

   esContext->width = width;
   esContext->height = height;

   if ( !WinCreate ( esContext, title) )
   {
      return GL_FALSE;
   }

  
   if ( !CreateEGLContext ( esContext->hWnd,
                            &esContext->eglDisplay,
                            &esContext->eglContext,
                            &esContext->eglSurface,
                            attribList) )
   {
      return GL_FALSE;
   }
   

   return GL_TRUE;
}

这里先调用了WinCreate ,然后调用了CreateEGLContext。WinCreate:

///
//  WinCreate()
//
//      Create Win32 instance and window
//
GLboolean WinCreate ( ESContext *esContext, const char *title )
{
   WNDCLASS wndclass = {0}; 
   DWORD    wStyle   = 0;
   RECT     windowRect;
   HINSTANCE hInstance = GetModuleHandle(NULL);


   wndclass.style         = CS_OWNDC;
   wndclass.lpfnWndProc   = (WNDPROC)ESWindowProc; 
   wndclass.hInstance     = hInstance; 
   wndclass.hbrBackground = (HBRUSH)GetStockObject(BLACK_BRUSH); 
   wndclass.lpszClassName = "opengles2.0"; 

   if (!RegisterClass (&wndclass) ) 
      return FALSE; 

   wStyle = WS_VISIBLE | WS_POPUP | WS_BORDER | WS_SYSMENU | WS_CAPTION;
   
   // Adjust the window rectangle so that the client area has
   // the correct number of pixels
   windowRect.left = 0;
   windowRect.top = 0;
   windowRect.right = esContext->width;
   windowRect.bottom = esContext->height;

   AdjustWindowRect ( &windowRect, wStyle, FALSE );


   //这里要做的是赋值到方法返回句柄
   esContext->hWnd = CreateWindow(
                         "opengles2.0",
                         title,
                         wStyle,
                         0,
                         0,
                         windowRect.right - windowRect.left,
                         windowRect.bottom - windowRect.top,
                         NULL,
                         NULL,
                         hInstance,
                         NULL);

   // Set the ESContext* to the GWL_USERDATA so that it is available to the 
   // ESWindowProc
   SetWindowLongPtr (  esContext->hWnd, GWL_USERDATA, (LONG) (LONG_PTR) esContext );


   if ( esContext->hWnd == NULL )
      return GL_FALSE;

   ShowWindow ( esContext->hWnd, TRUE );

   return GL_TRUE;
}

通过WinCreate,得到了window返回的句柄,接下来看CreateEGLContext:

///
// CreateEGLContext()
// 这里是初始化生成所有EGL元素和context的方法,opengles根据这些状态来进行渲染管线的处理
// Creates an EGL rendering context and all associated elements
// 
EGLBoolean CreateEGLContext ( EGLNativeWindowType hWnd, EGLDisplay* eglDisplay,
                              EGLContext* eglContext, EGLSurface* eglSurface,
                              EGLint attribList[])
{
   EGLint numConfigs;
   EGLint majorVersion;
   EGLint minorVersion;
   EGLDisplay display;
   EGLContext context;
   EGLSurface surface;
   EGLConfig config;
   EGLint contextAttribs[] = { EGL_CONTEXT_CLIENT_VERSION, 2, EGL_NONE, EGL_NONE };

   // Get Display
   display = eglGetDisplay(GetDC(hWnd));
   if ( display == EGL_NO_DISPLAY )
   {
      return EGL_FALSE;
   }

   // Initialize EGL
   if ( !eglInitialize(display, &majorVersion, &minorVersion) )
   {
      return EGL_FALSE;
   }

   // Get configs
   if ( !eglGetConfigs(display, NULL, 0, &numConfigs) )
   {
      return EGL_FALSE;
   }

   // Choose config
   if ( !eglChooseConfig(display, attribList, &config, 1, &numConfigs) )
   {
      return EGL_FALSE;
   }

   //Create a surface
   //这里可以选择使用eglCreateWindowSurface实现界面渲染,也可以选择Pbuffers,相对于Framebuffer,Pbuffers可以用来做非界面渲染,也常常用来作用与位图,
   //Framebuffer后面书里会讲解到,使用eglCreatePbufferSurface实现非界面渲染,Pbuffers不能直接渲染到界面上,但可以通过swapping buffers的方式使用,可以用copy或   //者bind的方式使用Pbuffers。
 surface = eglCreateWindowSurface(display, config, (EGLNativeWindowType)hWnd, NULL);
   if ( surface == EGL_NO_SURFACE ) { return EGL_FALSE; } 
   // Create a GL context 包含在所有操作需要数据的上下文,比如vertex shader和fragment shader的数据,还有顶点数据。 
   context = eglCreateContext(display, config, EGL_NO_CONTEXT, contextAttribs ); 
   if ( context == EGL_NO_CONTEXT ) { return EGL_FALSE; }
   // Make the context current 
   if ( !eglMakeCurrent(display, surface, surface, context) ) { return EGL_FALSE; } 
   *eglDisplay = display; 
   *eglSurface = surface; 
   *eglContext = context; 
   return EGL_TRUE;
}


至此,我们在main函数里面,关于EGL的操作就结束了。(这里我们只了解下EGL的使用,不深入到窗口系统调用EGL的绘制循环,后面补一篇文章来总结窗口和EGL的绘制机制)。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值