【OpenGL ES】Hello Triangle
来源:互联网 发布:tgp优化dnf 编辑:程序博客网 时间:2024/06/18 13:38
像Hello World一样,Hello Triangle是OpenGL ES的一个入门级例子,OpenGL ES 3.0完全基于着色器,如果没有绑定和加载合适的着色器,就无法绘制任何几何形状。下面介绍Hello Triangle的一般步骤,如何用OpenGL ES绘制一个三角形,需要做哪些事情,而每个步骤的详细原理则不作介绍,具体源码可参考https://github.com/geminy/aidear/tree/master/graphics/mu/examples/opengles3。
1、main
首先从main函数开始,main函数在esUtil.c文件中定义,代码如下所示:
// esUtil.cint main ( int argc, char *argv[] ){ // 1 // ESContext是一个很重要的struct // 用于OpenGL ES的上下文管理 // 贯穿程序始终 // 稍后详细介绍ESContext ESContext esContext; // 2 // 初始化ESContext memset ( &esContext, 0, sizeof( esContext ) ); // 3 // 通过esMain函数设置ESContext // esMain函数还作了其它的许多工作 // 稍后详细介绍esMain if ( esMain ( &esContext ) != GL_TRUE ) return 1; // 4 // 进入程序主循环WinLoop函数 // 稍后详细介绍WinLoop WinLoop ( &esContext ); // 5 // shutdown callback if ( esContext.shutdownFunc != NULL ) esContext.shutdownFunc ( &esContext ); // 6 // 释放内存 if ( esContext.userData != NULL ) free ( esContext.userData ); // 7 // 程序结束 return 0;}
程序运行起来时,初始界面如下:
2、ESContext
// esUtil.hstruct ESContext{ void *platformData; // 平台数据 void *userData; // 用户数据 GLint width; // 窗口宽度 GLint height; // 窗口高度 EGLNativeDisplayType eglNativeDisplay; // egl display handle EGLNativeWindowType eglNativeWindow; // egl window handle EGLDisplay eglDisplay; // egl display EGLContext eglContext; // egl context EGLSurface eglSurface; // egl surface void ( *drawFunc ) ( ESContext * ); // draw callback void ( *shutdownFunc ) ( ESContext * ); // shutdown callback void ( *keyFunc ) ( ESContext *, unsigned char, int, int ); // key callback void ( *updateFunc ) ( ESContext *, float deltaTime ); // update callback};
ESContext的几个callback函数通过如下函数进行注册:
// esUtil.c// 注册draw callbackvoid esRegisterDrawFunc ( ESContext *esContext, void ( *drawFunc ) ( ESContext * ) ){ esContext->drawFunc = drawFunc;}// 注册shutdown callbackvoid esRegisterShutdownFunc ( ESContext *esContext, void ( *shutdownFunc ) ( ESContext * ) ){ esContext->shutdownFunc = shutdownFunc;}// 注册update callbackvoid esRegisterUpdateFunc ( ESContext *esContext, void ( *updateFunc ) ( ESContext *, float ) ){ esContext->updateFunc = updateFunc;}// 注册key callbackvoid ESUTIL_API esRegisterKeyFunc ( ESContext *esContext, void ( *keyFunc ) ( ESContext *, unsigned char, int, int ) ){ esContext->keyFunc = keyFunc;}
3、HelloTriangle
esMain——
HelloTriangle从上面提到的esMain函数开始,代码如下所示:
// Hello_Triangle.ctypedef struct{ GLuint programObject; // opengl es program object handle} UserData;int esMain ( ESContext *esContext ){ // 1 // 给用户数据分配内存 esContext->userData = malloc ( sizeof ( UserData ) ); // 2 // 创建窗口 // 窗口标题为Hello Triangle // 窗口宽x高为320x240 // 窗口颜色缓冲区使用RGB通道 // 同时还更新了ESContext // 稍后详细介绍esCreateWindow函数 esCreateWindow ( esContext, "Hello Triangle", 320, 240, ES_WINDOW_RGB ); // 3 // 初始化绘制三角形所需的opengl es shader和program if ( !Init ( esContext ) ) { return GL_FALSE; } // 4 注册shutdown和draw callback esRegisterShutdownFunc ( esContext, Shutdown ); esRegisterDrawFunc ( esContext, Draw ); return GL_TRUE;}
Init——
初始化绘制三角形所需的opengl es shader和program的Init函数主要包括四个步骤,LoadShader、glCreateProgram、glAttachShader和glLinkProgram,如下:
// Hello_Triangle.cint Init ( ESContext *esContext ){ // 在此之前已经给用户数据分配了内存 UserData *userData = esContext->userData; // 顶点着色器 char vShaderStr[] = "#version 300 es \n" "layout(location = 0) in vec4 vPosition; \n" "void main() \n" "{ \n" " gl_Position = vPosition; \n" "} \n"; // 片段着色器 // fragColor = vec4 ( 1.0, 0.0, 0.0, 1.0 );表示三角形颜色为黄色 char fShaderStr[] = "#version 300 es \n" "precision mediump float; \n" "out vec4 fragColor; \n" "void main() \n" "{ \n" " fragColor = vec4 ( 1.0, 0.0, 0.0, 1.0 ); \n" "} \n"; GLuint vertexShader; GLuint fragmentShader; GLuint programObject; GLint linked; // 加载顶点着色器和片段着色器 // 稍后详细介绍LoadShader函数 vertexShader = LoadShader ( GL_VERTEX_SHADER, vShaderStr ); fragmentShader = LoadShader ( GL_FRAGMENT_SHADER, fShaderStr ); // 创建程序对象 programObject = glCreateProgram ( ); if ( programObject == 0 ) { return 0; } // 把加载好的顶点着色器和片段着色器与刚创建的程序对象绑定起来 glAttachShader ( programObject, vertexShader ); glAttachShader ( programObject, fragmentShader ); // 链接程序对象 glLinkProgram ( programObject ); // 检查程序对象链接状态 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; } // 存储程序对象 userData->programObject = programObject; // 设置背景颜色为蓝色 glClearColor ( 0.0f, 1.0f, 0.0f, 0.0f ); return TRUE;}
LoadShader——
LoadShader函数用于加载指定的着色器,主要包括三个步骤,glCreateShader、glShaderSource和glCompileShader,如下:
// Hello_Triangle.cGLuint LoadShader ( GLenum type, const char *shaderSrc ){ GLuint shader; GLint compiled; // 创建指定类型的着色器 // type为GL_VERTEX_SHADER或GL_FRAGMENT_SHADER shader = glCreateShader ( type ); if ( shader == 0 ) { return 0; } // 加载着色器源码 glShaderSource ( shader, 1, &shaderSrc, NULL ); // 编译着色器 glCompileShader ( shader ); // 检查着色器编译状态 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;}
Draw——
在Draw回调函数中,glViewPort设置一个矩形观察区域,glClear用之前在片段着色器中设置的黄色清除三角形的颜色缓冲区(这是必需的),glUseProgram使用之前在Init函数中创建的程序对象,vVertices定义一个顶点数组,用于设置三角形的三个顶点的坐标,坐标原点在屏幕中央,水平X轴正方向向右(可视坐标从-1.0到正1.0),竖直Y轴正方向向上(可视坐标从-1.0到正1.0),glVertexAttribPointer和glEnableVertexAttribArray对顶点数组进行处理,最后通过glDrawArrays绘制三角形,代码如下所示:
// Hello_Triangle.cvoid 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 }; glViewport ( 0, 0, esContext->width, esContext->height ); glClear ( GL_COLOR_BUFFER_BIT ); glUseProgram ( userData->programObject ); glVertexAttribPointer ( 0, 3, GL_FLOAT, GL_FALSE, 0, vVertices ); glEnableVertexAttribArray ( 0 ); glDrawArrays ( GL_TRIANGLES, 0, 3 );}
Shutdown——
Shutdown回调只是通过glDeleteProgram删除之前创建的程序对象,避免内存泄漏,代码如下所示:
// Hello_Triangle.cvoid Shutdown ( ESContext *esContext ){ UserData *userData = esContext->userData; glDeleteProgram ( userData->programObject );}
4、CreateWindow
esCreateWindow——
下面介绍上面esMain函数中使用的esCreateWindow函数,主要就是通过libEGL和libX11中的API创建窗口,代码如下:
// esUtil.cGLboolean esCreateWindow ( ESContext *esContext, const char *title, GLint width, GLint height, GLuint flags ){ EGLConfig config; EGLint majorVersion; EGLint minorVersion; // eglCreateContext函数用到的属性 // 通过EGL_CONTEXT_CLIENT_VERSION指定了opengl es版本为3 EGLint contextAttribs[] = { EGL_CONTEXT_CLIENT_VERSION, 3, EGL_NONE }; if ( esContext == NULL ) { return GL_FALSE; } // 设置宽和高 esContext->width = width; esContext->height = height; // 通过WinCreate函数创建窗口 // 稍后详细介绍WinCreate if ( !WinCreate ( esContext, title ) ) { return GL_FALSE; } // 获取egl display // egl native display在上面的WinCreate函数中设置 esContext->eglDisplay = eglGetDisplay( esContext->eglNativeDisplay ); if ( esContext->eglDisplay == EGL_NO_DISPLAY ) { return GL_FALSE; } // 初始化egl if ( !eglInitialize ( esContext->eglDisplay, &majorVersion, &minorVersion ) ) { return GL_FALSE; } { // 配置egl EGLint numConfigs = 0; 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_RENDERABLE_TYPE, GetContextRenderableType ( esContext->eglDisplay ), EGL_NONE }; if ( !eglChooseConfig ( esContext->eglDisplay, attribList, &config, 1, &numConfigs ) ) { return GL_FALSE; } if ( numConfigs < 1 ) { return GL_FALSE; } } // 创建egl window surface // egl native window在上面的WinCreate函数中设置 esContext->eglSurface = eglCreateWindowSurface ( esContext->eglDisplay, config, esContext->eglNativeWindow, NULL ); if ( esContext->eglSurface == EGL_NO_SURFACE ) { return GL_FALSE; } // 创建egl context esContext->eglContext = eglCreateContext ( esContext->eglDisplay, config, EGL_NO_CONTEXT, contextAttribs ); if ( esContext->eglContext == EGL_NO_CONTEXT ) { return GL_FALSE; } // make current if ( !eglMakeCurrent ( esContext->eglDisplay, esContext->eglSurface, esContext->eglSurface, esContext->eglContext ) ) { return GL_FALSE; } return GL_TRUE;}
WinCreate——
WinCreate函数创建X11窗口,同时设置egl native display和window,代码如下:
// esUtil_X11.cstatic Display *x_display = NULL;static Atom s_wmDeleteMessage;EGLBoolean WinCreate(ESContext *esContext, const char *title){ Window root; XSetWindowAttributes swa; XSetWindowAttributes xattr; Atom wm_state; XWMHints hints; XEvent xev; Window win; /* * X11 native display initialization */ x_display = XOpenDisplay(NULL); if ( x_display == NULL ) { return EGL_FALSE; } root = DefaultRootWindow(x_display); swa.event_mask = ExposureMask | PointerMotionMask | KeyPressMask; win = XCreateWindow( x_display, root, 0, 0, esContext->width, esContext->height, 0, CopyFromParent, InputOutput, CopyFromParent, CWEventMask, &swa ); s_wmDeleteMessage = XInternAtom(x_display, "WM_DELETE_WINDOW", False); XSetWMProtocols(x_display, win, &s_wmDeleteMessage, 1); xattr.override_redirect = FALSE; XChangeWindowAttributes ( x_display, win, CWOverrideRedirect, &xattr ); hints.input = TRUE; hints.flags = InputHint; XSetWMHints(x_display, win, &hints); // make the window visible on the screen XMapWindow (x_display, win); XStoreName (x_display, win, title); // get identifiers for the provided atom name strings wm_state = XInternAtom (x_display, "_NET_WM_STATE", FALSE); memset ( &xev, 0, sizeof(xev) ); xev.type = ClientMessage; xev.xclient.window = win; xev.xclient.message_type = wm_state; xev.xclient.format = 32; xev.xclient.data.l[0] = 1; xev.xclient.data.l[1] = FALSE; XSendEvent ( x_display, DefaultRootWindow ( x_display ), FALSE, SubstructureNotifyMask, &xev ); esContext->eglNativeWindow = (EGLNativeWindowType) win; esContext->eglNativeDisplay = (EGLNativeDisplayType) x_display; return EGL_TRUE;}
GetContextRenderableType——
EGLint GetContextRenderableType ( EGLDisplay eglDisplay ){#ifdef EGL_KHR_create_context const char *extensions = eglQueryString ( eglDisplay, EGL_EXTENSIONS ); if ( extensions != NULL && strstr( extensions, "EGL_KHR_create_context" ) ) { return EGL_OPENGL_ES3_BIT_KHR; }#endif return EGL_OPENGL_ES2_BIT;}
5、WinLoop
前面在main函数中提到了程序主循环WinLoop函数,在这个函数中,循环实现基于libX11的事件系统,在循环中执行我们注册的回调函数和swap buffer,代码如下:
// esUtil_X11.cvoid WinLoop ( ESContext *esContext ){ struct timeval t1, t2; struct timezone tz; float deltatime; gettimeofday ( &t1 , &tz ); while(userInterrupt(esContext) == GL_FALSE) { gettimeofday(&t2, &tz); deltatime = (float)(t2.tv_sec - t1.tv_sec + (t2.tv_usec - t1.tv_usec) * 1e-6); t1 = t2; if (esContext->updateFunc != NULL) esContext->updateFunc(esContext, deltatime); if (esContext->drawFunc != NULL) esContext->drawFunc(esContext); eglSwapBuffers(esContext->eglDisplay, esContext->eglSurface); }}GLboolean userInterrupt(ESContext *esContext){ XEvent xev; KeySym key; GLboolean userinterrupt = GL_FALSE; char text; // Pump all messages from X server. Keypresses are directed to keyfunc (if defined) while ( XPending ( x_display ) ) { XNextEvent( x_display, &xev ); if ( xev.type == KeyPress ) { if (XLookupString(&xev.xkey,&text,1,&key,0)==1) { if (esContext->keyFunc != NULL) esContext->keyFunc(esContext, text, 0, 0); } } if (xev.type == ClientMessage) { if (xev.xclient.data.l[0] == s_wmDeleteMessage) { userinterrupt = GL_TRUE; } } if ( xev.type == DestroyNotify ) userinterrupt = GL_TRUE; } return userinterrupt;}
- 【OpenGL ES】Hello Triangle
- Hello Triangle:OpenGL ES 2.0 版的“Hello world”
- Android OpenGL ES->Textured Triangle
- OpenGL step by step - tutorial_3 "hello triangle"
- Android OpenGL ES(十):绘制三角形Triangle
- Android OpenGL ES 开发教程(11):绘制三角形Triangle
- Android OpenGL ES 开发教程(11):绘制三角形Triangle
- Android ApiDemos示例解析(200):Graphics->OpenGL ES->Textured Triangle
- Android OpenGL ES 开发教程(11):绘制三角形Triangle
- Android ApiDemos示例解析(200):Graphics->OpenGL ES->Textured Triangle
- Android OpenGL ES 开发教程(11):绘制三角形Triangle
- OpenGL ES系列 之 基本-3:Hello,EGL
- OPENGL ES
- OpenGL ES
- OpenGL ES
- opengl es
- OpenGL ES
- OpenGL ES
- Main函数中参数argc,argv说明
- poj 1179 循环dp 类似矩阵连乘
- D3.js中初始化缩放比例
- 虚拟机中的Ubuntu16.04启动时自动设置分辨率1920x1080
- Linux 编译安装nginx
- 【OpenGL ES】Hello Triangle
- java_高级_文件与流
- bzoj[1191]: [HNOI2006]超级英雄Hero
- linux下用户的增删
- hiho最小生成树一(prim)算法
- JS开发规范
- JQuery之动态绑定事件
- 让Xcode8.0支持iOS10.3设备真机测试(不断更新真机支持包)
- 数据结构实验——基于数组的栈基本操作