孤舟蓑笠翁,独钓寒江雪

OpenGL ES -- 基础知识

概述

OpenGL ES是基于OpenGL三维图形API的子集,主要是针对手机及PDA(掌上电脑)等嵌入式设备设计的。
经过多年的发展,OpenGL ES主要分为两个主版本,基本情况如下。
一个是OpenGL ES 1.x(主要包括1.0与1.1),其采用的是固定渲染管线,可以由硬件GPU支持或用软件模拟实现,渲染能力有限,在纯软件模拟情况下性能也较弱。
另一个是OpenGL ES 2.0,其采用的是可编程渲染管线,渲染能力大大提高。OpenGL ES2.0要求设备中必须有相应的GPU硬件支持,目前不支持在设备上用软件模拟实现。
接下来的一系列关于 OpenGL ES 的文章,都是基于 OpenGL ES 2.0 来介绍。

  • 渲染管线 Graphics Pipeline:
  • 顶点 Vertex:
  • 着色器 Shader:

基本图形

在 OpenGL 里,只能绘制点,直线以及三角形。
点和直线可以用于某些效果,但是只有三角形才能用来构造拥有复杂的对象和纹理的场景。在 OpenGL 里,我们把单独的点放在一个组里构建出三角形,再告诉 OpenGL 如何连接这些点。我们想要构建的所有东西都要用点,直线和三角形定义,如果想构建更复杂的图形,例如拱形,那我们就需要用足够的点拟合这样的曲线。

渲染管线

我们把定义的的一些数据复制到 OpenGL 本地内存之后,到完成屏幕渲染之前,这些数据需要再 OpenGL 的管道中进行传递。
读取顶点数据 - 执行顶点着色器 - 组装图元 - 光栅化图元 - 执行片段着色器 - 写入帧缓冲区 - 显示在屏幕上

顶点

OpenGL中,我们使用独立的点集合来构建物体,这些点称为顶点。每个物体的构建都是通过顶点的聚合形成点、直线和三角形。
一个顶点就是一个代表几何对象的拐角的点,这个点又很多附加属性,最重要的就是位置,它代表了这个顶点在空间中的位置。

着色器

OpenGL中,我们把定义的一些数据绘制到屏幕上之前,它需要在 OpenGL 的渲染管线中传递,那么我们就需要使用着色器。这些着色器会告诉 GPU 如何绘制数据。
着色器分为顶点着色器和片段着色器。在绘制任何内容到屏幕之前,我们都需要定义它们。
顶点着色器(vertex shader)生成每个顶点的最终位置,针对每个顶点,它都会执行一次,一旦位置确定了,OpenGL 就可以把这些可见顶点的集合组装成点、直线以及三角形。
片段着色器(fragment shader)为组成点、直线或者三角形的每个片段生成最终的颜色,针对每个片段,它都会执行一次,一个片段是一个小的、颜色单一的长方形区域、类似于计算机屏幕上的一个像素。
一旦最后的颜色生成了,OpenGL 就会把他们写到一块称为帧缓冲区(frame buffer)的内存块中,然后,Android 会把这个帧缓冲区显示到屏幕上。
我们为什么要使用着色器呢?
在着色器出现之前,OpenGL 只能使用一个固定的方法集合控制很少而有限的事情,这些固定的 API 很容易使用,但是他们很难扩展,几乎不能添加一些自定义效果。在 OpenGL ES 2.0 里,设计人员使用着色器加入了可编程 API,为了保持简洁,他们把那些固定的 API 完全删除了,因此,开发者必须使用着色器。
现在我们可以使用着色器控制每个顶点应该如何绘制到屏幕上,我们也控制所有点、直线和三角形上每个片段应该如何绘制。只要我们可以用着色器语言表达出来,就可以加入任何理想的自定义效果。

光栅化技术

我们使用片段着色器为每个片段生成颜色之前,需要生成这些片段,那么这时就用到了光栅化技术。
移动设备的显示屏是由成千上万个小的,独立的部件组成,它们被称为像素;这些像素中的每一个都有能力显示几百万种不同颜色范围中的一种。然而,这实际上是一种视觉技巧:大多数显示器并不能真正创造几百万种颜色,所以每个像素通常是由三个单独的子构建构成的,它们发出红色,绿色,和蓝色的光,因为每个像素都非常小,人的眼睛会把红色,绿色和蓝色的光混合在一起,从而创造出巨量的颜色范围;把足够多的单独的像素放在一起,就能显示出多种颜色。
OpenGL通过“光栅化”的过程把每个点,直线及三角形分解成大量的小片段,它们可以映射到移动设备显示屏上,从而生成一幅图像。这些片段类似于显示屏上的像素,每个都包含单一的纯色。为了表示颜色,每个片段都有4个分量:其中红色,绿色,蓝色用来表示颜色,阿尔法分量用来表示透明度,
显示系统通常会把这些片段直接映射到屏幕上的像素,结果一个片段就对应一个像素。然而,事情并不总是这样,一个超高分辨率的设备可能需要较大的片段,以减少 GPU 的工作负荷。

OpenGL 的坐标归一化

对于顶点坐标而言,无论是 x 坐标还是 y 坐标,OpenGL 都会把屏幕映射到 [-1, 1] 这个范围内。这也就以为着屏幕的左边对应着 x 轴的 -1 ,而屏幕右边对应 +1,屏幕底边对应 y 轴的 -1,屏幕顶部对应 +1 。不管屏幕大小和形状,这个坐标范围都是一样的,如果我们要在屏幕上显示任何东西,都要在这个范围内绘制。
因此,在定义一些顶点坐标时,我们就需要把屏幕的坐标转换成 OpenGL 的坐标。

跨距Stride

如果我们分别要描述位置和颜色信息,我们可以用两个数组来表示,这样必须保证两个数组中顶点的位置和颜色是一一对应的。
除了这种方式,还可以选择一个数组存储两种信息的方式:“顶点1位置+顶点1颜色+顶点2位置+顶点2颜色+”,这样就引出了跨距的概念,即一个顶点和颜色占用的空间个数之和。
设置了跨距,我们调用 glVertexAttribPointer 就可以找到对应的顶点信息和颜色信息。
具体可以参考

绘制方法

glDrawArrays 顶点法和 glDrawElements 索引法都是从一个数据数组中提取数据渲染基本图元。

glDrawElements 只能用在 OPENGL1.1 或者更高的版本。

glDrawArraysglDrawElements的性能区别

glDrawArrays传输或指定的数据是最终的真实数据,在绘制时效能更好。
glDrawElements指定的是真实数据的调用索引,在内存/显存占用上更节省。

glDrawArraysglDrawElements 的损耗说明及其使用场景

glDrawArrays 主要讲数据空间损耗在顶点的定义处;
glDrawElements 主要讲数据空间损耗在顶点索引的定义处;
如果在你的工程中,画的图形较少或者,图形虽多但很多相同的,则可采用glDrawArrays更节省数据占用的空间;相反,如果图形多,而且形状大不相同的时候,可以优先考虑采用glDrawElements函数。

绘制模式

绘制模式有如下几种:

  • GL_POINTS
  • GL_LINES
  • GL_LINE_LOOP
  • GL_LINE_STRIP
  • GL_TRIANGLES
  • GL_TRIANGLE_STRIP
  • GL_TRIANGLE_FAN

一些工具类

GLES20
GLUtils

学习资料

《OpenGL ES 应用开发实践指南 Android 卷》
《OpenGL ES 2.0游戏开发》
学习着色器:https://thebookofshaders.com/?lan=ch
一些特效:https://glslsandbox.com/
GAPID:Graphics 调试工具:可以抓取一些opengl特效的shader