opengl es学习篇五:图元装配跟光栅化
资料来源:
OpenGL ES3.0编程指南
图元支持的几何类型
在opengl es中,图元可以用glDrawArrays()
,glDrawElements()
,glDrawRangeElements()
等命令绘制几何图像。图元有一组表示顶点位置的顶点描述。
图元总共三部分:
- 三角形
- 直线
- 点精灵
三角形
支持的三角形图元有GL_TRIANGLES
,GL_TRIANGLE_STRIP
,GL_TRIANGLE_FAN
三种。现根据整理如下:
- GL_TRIANGLES: Vertices 0, 1, and 2 form a triangle. Vertices 3, 4, and 5 form a triangle. And so on.
- GL_TRIANGLE_STRIP: Every group of 3 adjacent vertices forms a triangle. The of the strip is determined by the winding of the first triangle. Each successive triangle will have its effective face order reversed, so the system compensates for that by testing it in the opposite way. A vertex stream of n length will generate n-2 triangles.
- GL_TRIANGLE_FAN: The first vertex is always held fixed. From there on, every group of 2 adjacent vertices form a triangle with the first. So with a vertex stream, you get a list of triangles like so: (0, 1, 2) (0, 2, 3), (0, 3, 4), etc. A vertex stream of n length will generate n-2 triangles.
GL_TRIANGLES
GL_TRIANGLES
而言,就是直接绘制一系列的单独的三角形,以如下图形举例,顶点V0,V1,V2组成一个三角形,顶点V3,V4,V5组成一个三角形。通过顶点数组可以组成三角形的数目为数组长度/3
在Anroid中代码:
Demo地址 查看test for gl_triangle的提交
static float triangleCoords[] = { // in counterclockwise order: 0.0f, 0.622008459f, 0.0f, // top -0.5f, 0f, 0.0f, // bottom left 0.5f, 0f, 0.0f, // bottom right 0.5f, 0f, 0.0f, -0.5f, 0f, 0.0f, 0.0f, -0.622008459f, 0.0f, }; float color[] = { 1.0f, 0.0f, 0.0f,1f, 1.0f, 0.0f, 0.0f,1f, 1.0f, 0.0f, 0.0f,1f, 0f, 1.0f, 0.0f,1f, 0f, 1.0f, 0.0f,1f, 0f, 1.0f, 0.0f,1f}; public void onDraw() { // Add program to OpenGL ES environment GLES30.glUseProgram(mProgram); // get handle to vertex shader's vPosition member mPositionHandle = GLES30.glGetAttribLocation(mProgram, "vPosition");// createVertextBuffer(); //注意,这里注释掉了VBO,是因为打开后有Bug,具体是什么问题有待发现 // Enable a handle to th、e triangle vertices GLES30.glEnableVertexAttribArray(mPositionHandle); // Prepare the triangle coordinate data GLES30.glVertexAttribPointer(mPositionHandle, COORDS_PER_VERTEX, GLES30.GL_FLOAT, false, 0, vertexBuffer); // get handle to fragment shader's vColor member mColorHandle = GLES30.glGetAttribLocation(mProgram, "aColor"); // Set color for drawing the triangle// GLES30.glVertexAttrib4fv(mColorHandle, color, 0); GLES30.glEnableVertexAttribArray(mColorHandle); GLES30.glVertexAttribPointer(mColorHandle, 4, GLES30.GL_FLOAT, false, 0, colorBuffer); // Draw the triangle GLES30.glDrawArrays(GLES30.GL_TRIANGLES, 0, vertexCount); // Disable vertex array GLES30.glDisableVertexAttribArray(mPositionHandle); }
效果图如下:
GL_TRIANGLE_STRIP
GL_TRIANGLE_STRIP
而言,英文翻译过来就是每个小组的三个临近的顶点组成一个三角形,一如下图形举例:
看见英文一大堆就晕了,其实简单的就是V0,V1,V2组成一个三角形,V1,V2,V3组成一个三角形,V2,V3,V4组成一个三角形,以此类推。通过顶点数组可以组成三角形的数目为数组长度-2
Android代码使用如下:
static float triangleCoords[] = { // in counterclockwise order: 0.0f, 0.622008459f, 0.0f, // top -0.5f, 0f, 0.0f, // bottom left 0.5f, 0f, 0.0f, // bottom right 0.0f, -0.622008459f, 0.0f, }; private final int mProgram; // Set color with red, green, blue and alpha (opacity) values float color[] = { 1.0f, 0.0f, 0.0f,1f, 0f, 1.0f, 0.0f,1f, 0f, 0f, 1.0f,1f, 0f, 0.0f, 0.0f,1f}; public void onDraw() { ... GLES30.glDrawArrays(GLES30.GL_TRIANGLE_STRIP, 0, vertexCount); // Disable vertex array GLES30.glDisableVertexAttribArray(mPositionHandle); }
与上面代码的区别主要是在GL_TRIANGLE_STRIP参数的设置以及顶点数组数目的改变。实现效果如下所示:
GL_TRIANGLE_FAN
GL_TRIANGLE_FAN
而言,表示一个扇形的三角形组成,看底下的图就能明白:
顶点V0为圆点,呈扇形画出,即V0,V1,V2一个三角形,V0,V2,V3一个三角形,以此类推。 Android代码示例:
static float triangleCoords[] = { // in counterclockwise order: -0.5f, 0f, 0.0f, // bottom left 0.0f, 0.622008459f, 0.0f, // top 0.5f, 0f, 0.0f, // bottom right 0.0f, -0.622008459f, 0.0f, };private final int mProgram; // Set color with red, green, blue and alpha (opacity) valuesfloat color[] = { 1.0f, 0.0f, 0.0f,1f, 0f, 1.0f, 0.0f,1f, 0f, 0f, 1.0f,1f, 0f, 0.0f, 0.0f,1f};public void onDraw() { .... GLES30.glDrawArrays(GLES30.GL_TRIANGLE_FAN, 0, vertexCount); // Disable vertex array GLES30.glDisableVertexAttribArray(mPositionHandle); }
主要就是GL_TRIANGLE_FAN的设置以及数组位置的改变。实现效果与GL_TRIANGLE_STRIP相同。
直线
opengl es中支持的直线有GL_LINES
,GL_LINE_STRIP
,GL_LINE_LOOP
三大类,三个变量的区别如下图所示:
n指的是直线顶点数组的长度
- GL_LINES:绘制单独的线段,不相连接,总共绘制n/2条直线。
- GL_LINE_STRIP:绘制一系列连接的线段,总共绘制n-1条。
- GL_LINE_LOOP:会在最后一点连接起始点,总共绘制n条线段。
比较简单就不举例子了。
点精灵
点精灵是针对顶点进行绘制。点精灵通常将粒子效果作为点而非正方形绘制,从而实现高效渲染。点精灵是指定位置和半径的屏幕对齐的正方形,位置描述正方形的中心,半径用于计算点精灵的四个角坐标。
注意opengles窗口的坐标范围是:从左下角(原点)到右上角。
而点精灵的原点是在左上角 坐标值 从0-1.
gl_PointSize()
是可用于在顶点着色器输出点半径的内建变量。与点图元相关的顶点着色器输出gl_PointSize很重要,否则,会被视为未定义,很可能会造成绘图错误。顶点着色器的输出gl_PointSize收到opengl es 3.0实现所支持的非平滑点尺寸范围的限制,可以用如下命令查询:
GLfloat pointSizeRange[2];glGetFloatv(GL_ALIASED_POINT_SIZE_RANGE,pointSizeRange);//android中的表示GLES30.glGetFloatv();
gl_PointCoord
是只能在渲染图元为点精灵的时候用于片段着色器的内部建量。他用一个mediump精度限定符申明为一个vec2变量。gl_PointCoord属于区间[0,1],超出的则不显示。
图元的绘制
图元总共有五个绘制方法:
- glDrawArrays
- glDrawElements
- glDrawRangeElements(不介绍)
- glDrawArraysInstanced(不介绍)
- glDrawElementsInstanced(不介绍)
在Android中只支持第一种跟第二种,Android native层有待考证支持的类型。
glDrawArrays用元素索引为first到first+count-1的元素指定的顶点绘制mode指定的图元。 查看上述使用GL_TRIANGLES
写Demo的例子中的使用:
static float triangleCoords[] = { // in counterclockwise order: 0.0f, 0.622008459f, 0.0f, // top -0.5f, 0f, 0.0f, // bottom left 0.5f, 0f, 0.0f, // bottom right 0.5f, 0f, 0.0f, -0.5f, 0f, 0.0f, 0.0f, -0.622008459f, 0.0f, }; GLES30.glDrawArrays(GLES30.GL_TRIANGLES, 0, vertexCount);//vertexCount=6
first等于0,first+count-1=5,所以取(0,1,2)和(3,4,5)作为两个三角形的顶点进行绘制。支持的model就是我们上面锁讲述的一系列几何类型。
如果是一系列顺序元素索引描述的图元,且几何图形顶点不共享,那么glDrawArrays
是能支持的也很好用,但是游戏或者其他3D的应用程序的典型对象由多个三角形网格组成,其中元素不一定按照顺序,顶点通常在网格的三角形之间共享。使用glDrawElements可以比调用glDrawArrays更简单的将这些功能实现,而且内存消耗更小。
###图元装配
通过上述的glDraw*提供的顶点由顶点着色器执行,顶点着色器变换每个顶点的顶点位置。图元类型和顶点索引确定将被渲染的单独图元。对于每个单独图元与其对应的顶点,装配过程如上图所示。
下图中展示了通过顶点着色器和图元装配后的坐标系统:
顶点以物体或者本地坐标空间输入到opengl es中,在顶点着色器执行完后,顶点位置是被认为执行在裁剪空间之内的。顶点位置从本地坐标系统到裁剪坐标的变换通过加载执行对应矩阵来完成。
关于opengl es中的坐标系统需要单独写一篇文章。下面只是记录书中的内容
####裁剪 裁剪坐标的意义在于将判断顶点是否落在裁剪坐标空间内。裁剪坐标是由(x,y,z,w)指定的同类坐标,在裁剪空间(x,y,z,w)中定义的顶点坐标根据视景体(又称裁剪体)裁剪。 视景体由六个平面组成而成,这些平面被称作远,近,左,右,上,下裁剪平面,在裁剪坐标中,裁剪体如下:
- -w<=x<=w
- -w<=y<=w
- -w<=z<=w
裁剪体如下所示:
####透视分割 透视分割取得裁剪坐标(x,y,z,w)指定的点,并将其投影到屏幕或者视口上。这个操作通过将(x,y,z)除以w进行,执行(x/w),(y/w),(z/w)操作之后,得到规范化的设备坐标(q,w,e),他们落在[-1.0,1.0]中,这些坐标会根据视口大小转换成真正的屏幕坐标。规范化的z坐标可以使用glDepthRangf()
指定的near和far深度值转换成屏幕的z值,这些转换在视口变换时候进行。
####视口变换 视口是一个二维矩形窗口区域,是所有opengl es渲染最终显示的地方,可调用如下api进行设置:
void glViewport(GLint x,GLint y,GLsizei w,Glsizei h)
从规范化坐标(x,w,z)到窗口坐标(q,w,e)的转换变化如下:
上述变化中Ox=x+w/2,Oy=y+h/2,n和f代表所需要的深度范围,可使用glDepthRangf()
设置。