OpenGL ES 基础知识总结

一、GLSL着色器语言
1、渲染管线
渲染管线有时也被称为渲染流水线,一般是由显示芯片(GPU)内部处理图形信号的并行处理单元组成。这些并行处理单元两两之间是相互独立的,在不同型号的硬件上独立处理单元的数量也有很大的差异。一般越高端的硬件,其中独立处理单元的数量也就越多。
下图为OpenGL可编程渲染管线:
这里写图片描述

1.1 顶点着色器:顶点着色器是一个可编程的处理单元。(接收顶点缓存对象的顶点数据,单独处理每个顶点。)
1.1.1功能:为执行顶点的变换、光照、材质的应用与计算等顶点的相关操作,其每顶点执行一次。
1.1.2工作过程为:首先将原始的顶点几何信息及其他属性传递到顶点着色器中,经过自己开发的顶点着色器处理后产生纹理坐标、颜色、点位置等后继流程需要的各项顶点属性信息,然后将其传递给图元装配阶段。
1.1.3 顶点着色器的工作原理:
这里写图片描述
(1) 顶点着色器的输入主要为待处理顶点相应的 attribute(属性) 变量、uniform(全局) 变量、采样器以及临时变量,输出主要为经过顶点着色器后生产的varying(易变) 变量及一些内建输出变量。
(2) attribute 变量指的是3D物体中每个顶点各自不同的信息所属的变量,一般顶点的位置、颜色、法向量等每个顶点各自不同的信息都是以attribute 变量的方式传入顶点着色器的。
(3)uniform 变量指的是对于同一组顶点组成的单个3D物体中所有顶点都相同的量,一般为场景中当前的光源位置、当前的摄像机位置、投影系列矩阵等。
(4) varying 变量(易变变量)是从顶点着色器计算产生并传递到片元着色器的数据变量。顶点着色器可以使用易变变量来传递需要插值到片元的颜色、法向量、纹理坐标等任意值。
(5) 内建输出变量 gl_Position、gl_FrontFacing 和 gl_PointSize等。
gl_Position 是经过变换矩阵变换、投影后的顶点的最终位置。
gl_FrontFacing 是片元所在面的朝向。
gl_PointSize 是点的大小。
1.1.4 易变变量的工作原理:
这里写图片描述
易变变量在顶点着色器赋值后并不是直接将赋的值送入到后继的片元着色器中,而是在光栅化阶段由管线根据片元所属图元各个顶点对应的顶点着色器对此易变变量的赋值情况及片元与各个顶点的位置关系插值产生。如上图说明了这个问题。
提示:对每个片元进行一次插值计算将会非常耗费时间,严重影响性能。幸运的是,OpenGL ES2.0 的设计者也会考虑到这个问题,这些插值操作都是由GPU中的专用硬件实现的,因此速度很快,不影响性能。

1.2 片元着色器:片元着色器是用于处理片元值及其相关数据的可编程单元,其可以执行纹理的采样,颜色的汇总,计算雾颜色等操作,每片元执行一次。
1.2.1 功能:是通过重复执行(每片元一次),将3D物体中的图元光栅化后产生的每个片元的颜色属性计算出来送入后继阶段,如剪裁测试、深度测试及模板测试等。
1.2.2 片元着色器的工作原理
这里写图片描述
从渲染管线的图中可以看出,可编程片元着色器替代了纹理,颜色求和,雾以及Alpha 测试等阶段。与定点着色器类似,被其替代的功能系统将不再提供,需要完全由开发人员用着色器语言编程完成。这在提高了灵活性的同时也增加了开发的难度。

  • Varying0 ~ n 指的是从顶点着色器传递到片元着色器的易变数据变量,如前面所介绍,由系统在顶点着色器后的光栅化阶段自动插值产生。其个数是不一定的,取决于具体的需要。
  • gl_FragColor 指的是计算后此片元的颜色。一般在片元着色器的最后都需要对 gl_FragColor进行赋值。

提示: 经过光栅化、顶点着色器与片元着色器的介绍,可以看出顶点着色器每顶点一执行,而片元着色器每片元一执行,片元着色器的执行次数明显大于顶点着色器的执行次数。因此在开发中,应尽量减少片元着色器的运算量,将一些复杂运算尽量放在顶点着色器中执行。

1.3 细分着色器:接收来自顶点着色阶段的输出数据,并对收到的数据进一步处理。(用Patch描述一个物体的形状,在OpenGL管线内部生成新的几何体,并且模型的外观也会变得更加平顺,细分着色器会用到两个阶段来分别管理Patch数据,并生成最终状态)。

1.4 几何着色器:在管线内部会对所有的图元进行修改,改变几何图元的类型,或者放弃所有的几何体。如果启用,那么输入可能会来自顶点着色阶段完成的几何图元,也可能来自细分阶段的图元数据。

1.5 光栅化:将输入图元的数学描述转换为与屏幕位置对应的像素片元。
虽然虚拟3D世界中的几何信息是三维的,但是由于目前用于显示的设备都是二维的 。因此在真正执行光栅化工作之前,首先需要将虚拟 3D 世界中的物体投影到视平面上。需要注意的是,由于观察位置的不同,同一个 3D 场景中的物体投影到视平面可能会产生不同的效果,如下图:
光栅化阶段投影到视口
光栅化阶段投影到视口
另外,由于虚拟 3D 世界当中物体的几何信息一般采用连续的数学量来表示,因此投影的平面结果也是用连续数学量来表示的。但目前的显示设备屏幕都是离散化的(由一个一个的像素组成),因此还需要将投影的结果离散化。将其分解为一个一个离散化的小单元,这些小单元一般称为片元。具体如下图:
投影后图元离散化
这里写图片描述
其实每个片元都对应于帧缓冲中的一个像素,之所以不直接称之为像素是因为 3D 空间中的物体是可以相互遮挡的。而一个 3D 场景最终显示到屏幕上虽然是一个整体,但每个 3D 物体的每个图元是独立处理的。这就可能出现这样的情况,系统先处理的是位于离观察点较远的图元,其光栅化为了一组片元,暂时送入帧缓冲的对应位置。
但后面继续处理离观察点较近的图元时也光栅化出了一组片元,两组片元中有对应到帧缓冲中同一个位置的,这时距离近的片元将覆盖距离远的片元(如何覆盖的检测是在深度检测阶段完成)。因此某片元就不一定能成为最终屏幕上的像素,称为像素就不准确了,可以将其理解为候选像素
提示:每个片元包含其对应的顶点坐标、顶点颜色、顶点纹理坐标以及顶点的深度等信息,这些信息是系统根据投影前此片元对应的 3D 空间中的位置及与此片元相关的图元的各顶点信息进行插值计算而生成的。

2、语言基础(基本数据类型、聚合类型、结构体和采样器、数组、类型修饰符、内置变量)

2.1 数据类型概述
2.1.1 标量
- 布尔型—bool
- 整形–int
- 浮点型–float
2.1.2 向量

向量类型说明
vec2包含了2个浮点数的向量
vec3包含了3个浮点数的向量
vec4包含了4个浮点数的向量
向量类型说明
ivec2包含了2个整数的向量
ivec3包含了3个整数的向量
ivec4包含了4个整数的向量
向量类型说明
bvec2包含了2个布尔数的向量
bvec3包含了3个布尔数的向量
bvec4包含了4个布尔数的向量

向量的运算
vec4 a; vec4 b;
a-b =A和B分量相减

加长取短
加长:vec2 a;
vec4 c=vec4( a,1000,2 );
取短 :vec4 rgba;
vec3 rgb=vec3( rgba.rgb);

2.1.3 矩阵
矩阵的类型及说明
特点:先填充列,然后填充行。

矩阵类型说明
mat22 x 2 的浮点矩阵
mat33 x 3 的浮点矩阵
mat44 x 4 的浮点矩阵
147
258
369

mat3 m = mat(1,2,3,4,5,6,7,8,9);
mat3 m = mat(vec3(1,2,3), vec3(4,5,6), vec3(7,8,9));

2.1.4 采样器
采样器是着色器语言中不同于C语言的一种特殊的基本数据类型,其专门用来进行纹理采样的相关操作。一般情况下,一个采样器变量代表一幅或一套纹理贴图。
其具体情况如下表:

采样器类型说明
sampler2D用于访问二维纹理
sampler3D用于访问三维纹理
samplerCube用于访问立体贴图纹理

特点:
① 采样器变量不能再着色器中初始化。
② 一般情况下采样器变量都用uniform限定符来修饰,从寄主语言(如java)接收传递进着色器的值。
③ sampler3D 并不是在所有的OpenGL ES 实现中都支持,因此,如要使用必须首先在着色器代码中进行设置,打开相应的扩展。

2.1.5 结构体
OpenGL ES 着色器还提供了类似于c语言中的用户自定义结构体,同样也是使用struce关键字进行声明,基本用法如下所示。

struct info{           //声明一个结构体info
    vec3 color;        //颜色成员
    vec3 position;     //位置成员
    vec2 textureCoor;  //纹理坐标成员
}

声明了info类型的结构体之后,就可以像使用内健数据类型一样使用这个用户自定义的数据类型了,如:

info CubeInfo;   //声明了一个info类型的变量CubeInfo

对于结构体来说,其他的使用方法都与C语言基本类似。

2.1.6 数组
声明数组的方式:
(1)在声明数组同时,指定数组的大小:

vec3 position[20]; //声明了一个包含20个vec3的数组,索引从0开始。

(2)在声明数组时,也可以不指定数组的大小,但是必须符合下列两种情况之一。

  • 引用数组之前,要再次使用第一种声明方式来声明该数组,具体代码如下:
vec3 position[];  //声明了一个大小不定的vec3型数组。
vec3 pisition[]5; //再次声明该数组,并且指定大小。

提示:再次声明数组,指定大小之后,就不能再进行声明了。

  • 代码中访问数组的下标都是编译时常量(如:字面常量),这时编译器会自动创建适当大小的数组,使得数组尺寸足够存储编译器看到的最大索引值对应的元素,代码如下:
vec3 position[];  //声明了一个大小不定的vec3型数组。
pisition[3] = vec3(3.0);//position需要为一个大小为4的数组
position[20] = vec3(6.0);//position需要为一个大小为21的数组

说明:上述代码的第2行需要尺寸为4的数组,代码的第3行需要尺寸为21的数组,若后面没有更大的下标出现,则编译器自动创建position为尺寸21的数组。这种由系统量体裁衣、自动分配的方法若恰当采用可以提高硬件的使用效率,降低对硬件资源的消耗。

2.1.7 类型修饰符

修饰符说明
in输入变量
out输出变量
inout输入输出变量
attribute一般用于每个顶点都各不相同的量,如顶点位置、颜色等(只能修饰顶点着色器的变量)
uniform一般用于对同一组顶点组成的单个3D物体中所有顶点都相同的量,如当前的光源位置 {一致变量修饰符,(顶点和片元都可以用)}
varying用于从顶点着色器传递到片元着色器的量(如果说要传递,变量名一致)
const用于声明常量

3、绘制方式
OpenGL ES 中支持的绘制方式大致分3类,包括点、线段、三角形,每类中包含一种或多种具体的绘制方式,各种具体绘制方式的说明如下所列。

① GL_POINTS:点类下唯一的绘制方式,用来绘制点。
② GL_LINES:将着色器传入的顶点按顺序两个一组来绘制成线段。
③ GL_LINES_STRIP:按照顶点顺序连接顶点。(不封口)
④ GL_LINES_LOOP:按照顶点顺序连接顶点,并将第一个顶点和最后一个顶点相连。(封口)
⑤ GL_TRIANGLES:按照顶点顺序每3个点组成三角形进行绘制。
⑥ GL_TRIANGLE_STRIP:顶点按照顺序依次组织成三角形进行绘制, 最后实际形成的是一个三角形带。若有 N个顶点将绘制出n-2个三角形 (节省顶点空间)
⑦ GL_TRIANGLE_FAN:将第一个点作为中心点其他点作为边缘点,绘制一系列组成扇形的相邻三角形
⑧ glDrawArrays:按照传入渲染管线顶点本身的顺序及选用的绘制方式将顶点组织成图元进行绘制。称之 为顶点法( 特点:方便 ,但是出现了很多的重复顶点。 )
⑨ glDrawElements:绘制时不但要将顶点序列传入渲染管线,还需要将索引序列传入管线,绘制时跟据索引序列取出顶点值,并根据当前选用的绘制方式组织成图元。

区别:
用GL_TRIANGLES绘制方式(绘制一个正方形)
1.glDrawArrays需要6个顶点
2.glDrawElements需要四个顶点

4、纹理映射

4.1 纹理映射的原理
① 为顶点指定纹理坐标;
② 通过纹理坐标在纹理图上确定纹理区域;
③ 将选定的区域根据纹理坐标映射到图元上。

4.2 纹理的注意事项
① 纹理坐标用浮点数来表示,范围一般从0.0~1.0;
② 规定纹理的图片的宽高必须是2的n次方;

4.3 纹理拉伸和截取
4.3.1 两种拉伸方式的概述
无论是 S 轴还是 T 轴的纹理坐标都是在0.0~1.0中,这满足了大多数情况。但是在特定的情况下,也可以设置大于1的纹理坐标。当纹理坐标大于1以后,设置的拉伸方式就会起作用了。下面对两种拉伸方式进行单独介绍:
(1) 重复拉伸方式
如果设置的拉伸方式为重复,当顶点纹理坐标大于1时则实际起作用的纹理坐标为纹理坐标的小数部分。也就是说若纹理坐标为3.3,则起作用的纹理坐标为0.3,这种情况下会产生重复的效果。如下图所示:
这里写图片描述

   //重复拉伸
   GLES20.glTexParameterf(GLES20.GL_TEXTURE_2D
                         ,GLES20.GL_TEXTURE_WRAP_S //设置S轴的拉伸方式为重复
                         ,GLES20.GL_REPEAT);

   GLES20.glTexParameterf(GLES20.GL_TEXTURE_2D
                          ,GLES20.GL_TEXTURE_WRAP_T  //设置S轴的拉伸方式为重复
                           ,GLES20.GL_REPEAT);

提示:开发中 S 与 T 两个轴的拉伸方式是独立设置的,但在一般情况下两个轴都会设置同样的选项。

重复拉伸的方式在很多大场景地形的纹理贴图中很有作用,如将大块地面重复铺满草皮纹理、将大片水面重复铺满水波纹等。如果没有重复拉伸方式,则开发人员只能将大块面积切割为一块一块的小面积矩形,对每一块矩形单独设置0.0 ~ 1.0 内的纹理坐标。这样开发不但繁琐,而且大大增加了顶点的数量,程序运行的效率也会受到很大的影响。因此开发中要注意重复拉伸方式的灵活运用,可以使用重复拉伸方式时就不要去无畏地增加顶点数量了。

(2) 截取拉伸方式
截取拉伸方式中当纹理坐标的值大于1时都看作 1,因此会产生边缘被拉伸的效果。具体情况如下图所示。
这里写图片描述
从图中可以看出,需要纹理映射的矩形中4个顶点纹理坐标分别为(0,0)、( 4,0)、(0,4)、( 4,4),因此矩形中的各片元纹理坐标范围为S 轴方向 0 ~ 4,T 轴方向0 ~ 4.由于在此种拉伸方式下,大于1的纹理坐标都看作1,因此产生了纹理横向和纵向边缘被拉伸的效果。

   //截取拉伸
   GLES20.glTexParameterf(GLES20.GL_TEXTURE_2D,
                          GLES20.GL_TEXTURE_WRAP_S,//设置S轴的拉伸方式为截取
                          LES20.GL_CLAMP_TO_EDGE);

   GLES20.glTexParameterf(GLES20.GL_TEXTURE_2D,
                         GLES20.GL_TEXTURE_WRAP_T,//设置T轴的拉伸方式为截取
                         LES20.GL_CLAMP_TO_EDGE);

4.4 纹理采样
所谓纹理采样就是:根据片元的纹理坐标到纹理图中提取对应位置颜色的过程。但是由于被渲染图元中的片元中的片元数量与其对应纹理区域中像素的数量并不一定相同,也就是说图元中的片元与纹理图中的像素并不总是–对应的。

例如,将较小的纹理图映射到较大的图元或将较大的纹理映射到较小的图元时这种情况就会产生。因此通过纹理坐标在纹理图中并不一定能找到与之完全对应的像素,这时候需要采用一些策略使得纹理采样可以顺利进行下去。通常采用的策略有最近点采样、线性采样两种。

4.4.1 最近点采样
最近点采样是最简单的一种采样算法,其速度在各种采样算法中也是最快的。
(1)基本原理
这里写图片描述
从图中可以看出两点:

  • 纹理图的横边和竖边的范围都是0 ~ 1,而纹理本身是由一个一个的像素组成的。若将每一个像素看作是一个小方块,则每个像素都占一定的纹理坐标范围。如上图中纹理图最左上侧的像素纹理坐标的范围为:S 方向 0.0 ~ 0.025,T 方向 0.0 ~ 0.025.

  • 根据片元的纹理坐标可以很容易地计算出片元对应的纹理坐标点点位于纹理图中的哪个像素(小方格)中,最近点采样就直接取此像素的颜色值为采样值。

(2)效果特点
从前面的介绍中可以看出,最近点采样很简单,计算量也小。但最近点采样也有一个明显的缺点,那就是如果把较小的纹理映射到较大的图元上时容易产生很明显的锯齿,如下图:
这里写图片描述
需要注意的是,将较大的纹理映射到较小的图元时,也会有锯齿产生,但是由于图元整体较小,视觉上就不那么明显了。

加载纹理时,设置采用最近点采样方式的代码如下:

   GLES20.glTexParameterf(GLES20.GL_TEXTURE_2D,
                          GLES20.GL_TEXTURE_MIN_FILTER, //设置MIN时为最近点采样
                          GLES20.GL_NEAREST);

   GLES20.glTexParameterf(GLES20.GL_TEXTURE_2D
                          ,GLES20.GL_TEXTURE_MAG_FILTER //设置MAG时为最近点采样
                          ,GLES20.GL_NEAREST);

4.4.2 线性纹理采样
在某些情况下,最近点采样不能满足高视觉效果的要求,这时可以选用更复杂一些的线性纹理采样算法。
(1)基本原理
这里写图片描述
线性采样时的结果颜色并不一定仅来自纹理图中的一个像素,其在采样时会考虑到片元对于的纹理坐标点附近的几个像素。如上图所示,右侧片元的纹理坐标对应的纹理点在纹理图中的小黑点位置,此时可以认为纹理点位于采样范围的中央。因此采样范围涉及了4个像素,但由于纹理点并没有位于四个像素的交叉点上,从而4个像素的颜色在结果中所占的比例也不尽相同。
一般是根据涉及的像素在采样范围内的面积比例加权计算出最终的采样结果,单具体采样时使用的采样范围可能因厂商的不同而有所不同。上述示意图中的采样范围只是对原理的说明。

(2)效果特点
由于采样时对采样范围内的多个像素进行了加权平均,因此在将较小的纹理图映射到较大的图元时,不再会有据此的现象,而是平滑过渡的。如下图所示:
这里写图片描述
提示:平滑过渡解决了锯齿的问题,但有时线条边缘会很模糊,因此实际开发中采用哪种采样策略,需要根据具体的需求来确定。
加载纹理时,设置采用线性采样方式代码如下。

   GLES20.glTexParameterf(GLES20.GL_TEXTURE_2D,
                          GLES20.GL_TEXTURE_MIN_FILTER,  //设置MIN时为线性采样
                          GLES20.GL_LINEAR);

   GLES20.glTexParameterf(GLES20.GL_TEXTURE_2D,
                          GLES20.GL_TEXTURE_MAG_FILTER, //设置MAG时为线性采样
                          GLES20.GL_LINEAR);

4.4.3 MIN 与 MAG 采样
无论采用哪种采样方式,都需要对 MIN(GL_TEXTURE_MIN_FILTER) 与 MAG(GL_TEXTURE_MAG_FILTER) 两种情况分别进行设置。
MIN 与 MAG 两种纹理采样情况的具体含义,其原理图分别如下:

图1:MIN采样
MIN采样

图2:MAG采样
MAG采样

从图1 和 图2 可以看出,当纹理图比需要映射的图元尺寸大时系统采用MIN对应的纹理采样算法设置,而当纹理图比需要映射的图元尺寸小时系统采用MAG对应的纹理采样算法设置。
更准确的定义应该是:当纹理图中的一个像素对应到待映射图元上的多个片元时,采用MAG采样;反之则采用MIN采样。

提示:由于最近点采样计算速度快,在MIN情况下一般锯齿也不明显,综合效益高;而MAG方式下若采用最近点采样则锯齿会很明显,严重影响视觉效果。因此实际开发中一般情况下往往采用将MIN情况设置为最近点采样,将MAG情况设置为线性采样的组合。

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值