OpenGL ES 2.0学习之路---2.Hello Triangel:一个OpenGL ES 2.0例子

该例子主要包括以下内容:

  • 使用EGL创造一个显示渲染窗口平面
  • 装载顶点和片段着色器
  • 创造一个项目,联系顶点和片段着色器,链接项目
  • 设置视窗
  • 清除颜色缓冲区
  • 最基本的渲染
  • 在EGL窗口显示颜色缓冲区的内容

因为OpenGL ES 2.0是以可编程着色器为基础的,意味着绘制的任何图形都必须有一个合适的着色器装载和绑定,比使用固定管线的桌面版本有更多的代码。
Hello Ttiangle例子

#include "esUtil.h
typedef  struct
{
    //建立一个程序对象
    GLuint programObject;
  } UserData;
  //创造一个着色器对象,载入着色资源和编译着色器
  GLuint LoadShader(const char *shaderSrc,GLenum type)
  {
     GLuint shader;
     GLint  compiled;
    //创建着色器对象
    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(size(char)*infoLen);
           glGetShaderInfoLog(shader,infoLen,NULL,infoLog);
           esLogMessage("Error  compile  shader:\n%s\n",infoLog);
           free(infoLog);
}
   glDeleteShader(shader);
   return 0;
}
return  shader;
}
//初始化着色器和程序对象
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";
GLunit  vertexShader;
GLunit  fragmentShader;
GLuint  programObject;
GLint  linked;
//载入顶点、片段着色器
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);
//结合 vPosition(顶点位置信息)到attribute 0
glBindAttribLocation(programObject,0,"vPosition");
//链接程序
glLinkProgram(programObject);
//检查链接状态
glGetProgramiv(programObject,GL_LINK_STATUS,&linked);
if(!linked)
{
   GLint  infoLen=0;
   glGetProgramiv(programObject,GL_INFO_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,0.0f,0.0f,1.0f);
return TRUE;
}
//创建一个三角形并使用着色器对创建进行初始化
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};
   //设置视口
   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);
   eglSwapBuffers(esContext->eglDisplay,esContext->eglSurface);
}
 int main(int argc,char* argv[])
 {
   ESContext  exContext;
   UserData   userData;
   esInitialize(&esContext);
   esContext.userData=&userData;
   esCreateWindow(&esContext,"Hello Triangle",320,240,ES_WINDOW_RGB);
   if(!Init(&esContext))
      return 0;
      esRegisterDrawFunc(&esContext,Draw);
 esMainLoop(&esContext);
}
   

在main函数里,有几个ES函数,主函数的第一件事情是定义一个ESContext,并初始化。

ESContext  esContext;
UserData   userData;
esInitialize(&esContext);
esContext.userData=&userData;

ESContext被所有的ES架构引入,它包含着ES架构需要的所有必要信息。使用ESContext的原因为ES代码可以不再必须使用全局数据。
ESContext有一个名为userData的成员变量,它的类型为void*指针,每个程序都用它来存储数据,esInitialize用来初始化context和ES代码架构。其它的EXContext结构成员在头文件中描述,程序只能读取这些值,这些结构成员包括窗口宽和高、EGLcontext、回调函数指针等。
main函数接下来的工作是创建窗口,初始化绘制回调函数,进入主循环。

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

esCreateWindow调用创造一个指定宽度和高度的窗口(320*240),最后一个参数是指定窗口创造的位数(可选),本例中我们使用的是RGB帧缓冲。这个函数使用EGL创造一个可以宣誓联系着窗口的平面,EGL是独立于平台的创造渲染平面和上下文环境的API。
之后初始化哪些对项目必须的东西,然后再注册一个Draw回调函数,这将引起窗口渲染开始,最后程序进入主循环,知道项目窗口被关闭。
创建一个简单的顶点和片段着色器
在做任何渲染前,必须有顶点和片段着色器。初始化最大的任务是装载顶点和片段着色器,顶点着色器在项目中是很简单的:

  GLbyte  vShaderStr[]=" attribute vec4 vPosition;   \n"
    "void  main()           \n"
    "{                   \n"
    "gl_Position=vPosition;\n"
    "}                  \n";

顶点着色器定义一个输入attribute,它是4个成员的矢量vPosition。后面的Draw函数将为每个顶点设置位置变量值。主函数声明着色器开始执行。着色器主体非常简单,它复制输入vPosition属性到gl_Position输出变量中,每个顶点着色器必须输出位置值到gl_Position变量中,这个变量传入到管线的下一个阶段中。
片段着色器也非常简单:

GLbyte fShaderStr[]=
"precision mediump float;          \n"
"void main()      \n"
"{                  \n"
"   gl_FragColor=vec4(1.0,0.0,0.0,1.0);   \n"
"}                  \n";

第一行声明着色器默认的浮点变量精度,它的输出值(1.0,0.0,0.0,1.0)赋给变量gl_FragColor,gl_FragColor是片段着色器最终的输出值,本例中的输出值是红色。
编译和装载着色器
定义了着色器源码后,我们将着色器装入OpenGL ES,这由LoadShader函数完成,检查没有错误后,这个函数返回着色器对象,这个对象在后面被用于链接项目对象。
我们看一下LoadShader函数,首先使用glCreateShader创建着色器对象,它的语法如下:

 GLuint LoadShader(const char *shaderSrc,GLenum type)
  {
     GLuint shader;
     GLint  compiled;
    //创建着色器对象
    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(size(char)*infoLen);
           glGetShaderInfoLog(shader,infoLen,NULL,infoLog);
           esLogMessage("Error  compile  shader:\n%s\n",infoLog);
           free(infoLog);
}
   glDeleteShader(shader);
   return 0;
}
return  shader;
}

如果着色器对象装载成功,一个新的着色器对象被返回,在后面将连接到项目对象上。
创建项目对象链接着色器
一旦应用程序已经创建了顶点、片段着色器对象,它需要去创建项目对象,项目是最终的连接对象,每个着色器在被绘制前都应该联系到项目或者项目对象。
第一步创建项目对象,链接顶点着色器和片段着色器。

 //创建程序对象
programObject=glCreateProgram();
if(programObject==0)
return 0;
glAttachShader(programObject,vertexShader);
glAttachShader(programObject,fragmentShader);
//结合 vPosition(顶点位置信息)到attribute 0
glBindAttribLocation(programObject,0,"vPosition");

所有这些步骤后,编译着色器,检查错误,附加上着色器,链接项目,检查链接错误。成功后可以使用项目对象去渲染。为使用项目对象去渲染,我们使用glUseprogram绑定。

glUseProgram(userData->programObject);

使用glUseProgram调用项目句柄后,所有的以后的渲染将使用顶点着色器和片段着色器去联系项目对象。
设定窗口和清除缓冲区
我们已经用EGL创建了渲染平面初始化并装载了着色器,准备去绘制实际的护体,Draw回调函数绘制帧。glViewport定义了OpenGL ES将要绘制物体的2D窗口的坐标原点和窗口宽度和高度。在OpenGL ES中viewport定义渲染操作将要显示的2D长方形显示区域。

 glViewport(0,0,esContext->width,esContext->height);

viewport设定窗口的原点origin(x,y)、宽度和高度。
设定窗口后,下一步是清除屏幕,在OpenGL ES中,有多中需要绘制的缓冲区类型,颜色、深度和模板。本例中仅仅颜色缓冲区被使用。开始每帧绘制前,我们使用glClear清除颜色缓冲区。

glClear(GL_COLOR_BUFFER_BIT);

缓冲区将被glClearColor函数的颜色参数值清除,初始化结束后,清除颜色被设定为(0.0,0.0,0.0,1.0),于是屏幕变成黑色,清除颜色通过glClear设定。
装载几何图像绘制基元
我们要绘制的是三角形,三角形顶点的坐标通过vVertices矩阵设定三个(x,y,z)坐标。

  GLfloat  vVertices[]={0.0f,0.5f,0.0f,-0.5f,-0.5f,0.0f,0.5f,-0.5f,0.0f};
  glVertexAttribPointer(0,3,GL_FLOAT,GL_FALSE,0,vVertices);
getEnableVertexAttribArry(0);
glDrawArrys(GL_TRIANGLES,0,3);

绘制三角形最后一步是调用OpenGL ES去绘制基元,本例中使用glDrawArrays函数。这个函数绘制三角形,直线或带状物等基元。
最后谈谈缓冲区是如何显示到屏幕上的
讨论之前,先介绍一点双缓冲区概念。
在显示屏上看到的帧缓冲区是2维的空间的像素数据,可能想到的方法是简单更新可见缓冲区中的数据,直接更新显示缓冲区中的数据有困难,对显示屏幕缓冲区内存更新要有固定的频率。如果两次时间不同,将看到闪烁的痕迹。
为解决这个问题,系统一般使用双缓冲区:前缓冲区和后缓冲区,所有的渲染发生在后缓冲区,它是屏幕是不可见的缓冲区内存,渲染完成后,交换前后缓冲区,即在下一帧时前缓冲区变成后缓冲区。
通过EGL实现,使用的函数是eglSwapBuffers;

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

此函数交换前后缓冲区,eglSwapBuffers的输入参数是EGL显示区和窗口,这两个参数代表了实际的物理显示区和渲染区,交换缓冲区后,显示了三角形。

另外,想要通过学习算法来提高面试/笔试竞争力的同学可以关注一下我的公众号:
在这里插入图片描述

  • 2
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值