导语 :渲染管线(渲染流水线),一般由显示芯片(GPU)内部处理图形信号的并行处理单元组成。这些并行处理单元两两之间相互独立。不同的型号硬件上独立处理单元的数量有很大差异。 与CPU串行执行不同,渲染管线中的各个处理单元并行处理,渲染效率可以得到极大地提升。
[ OpenGl ES1.0 渲染管线 ]
该阶段设定3D空间中物体的顶点坐标,顶点对应颜色,顶点的纹理坐标等属性。并且之指定绘制方式:点绘制,线绘制,三角形绘制。
这部分功能在程序中是可选的。对于某些场景下顶点的基本数据不变的情况。可以在初始化阶段将顶点数据经过基本处理后直接送入顶点缓冲对象。在绘制每一帧时就可以直接从缓冲对象中取顶点数据,一定程度上节省了GPU的IO带宽和提升渲染效率吧。
线绘制方式需要两个顶点,此方式下每两个顶点构成一个图元。
[ 从不同距离不同角度观察正四面体 ]
由于虚拟3D世界当中物体的几何信息一般采用连续的数学量来表示。但是目前的显示设备屏幕都是离散化的(由一个个像素组成)因此还需要讲投影结果离散化,将其分解成一个个离散化的小单元,这些小单元一般称为片元。这些片元都对应帧缓冲区中的一个像素。
[ 投影后图元离散化 ]
物体预先在帧缓冲区中进行绘制,每绘制完一帧再将绘制完的结果交换到屏幕上。因此每次绘制新的一帧时需要清除缓冲区中的相关数据,否则有可能产生不正确的绘制效果。
[ OpenGl ES2.0 渲染管线 ]
OpenGL ES2.0 中“顶点着色器”取代了OpenGL ES1.0渲染管线的“光照和变换”阶段。
OpenGL ES2.0中“片元着色器”取代了OpenGL ES1.0渲染管线中的“纹理环境和颜色求和”,“雾”,“Alpha测试”等阶段。
其工作过程为首先将原始的顶点几何信息及其他属性传送到顶点着色器中,经过自己开发的顶点着色器处理后产生纹理坐标,颜色,点位置等后续流程需要的各项顶点属性信息,然后将其传递给图元装配阶段。
[ 顶点着色器工作原理 ]
顶点着色器的输入包括:
顶点着色器的输出包括:
片元着色器是用于处理片元值及其相关数据的可编程单元,其可以执行纹理的采样,颜色的汇总,计算雾颜色等操作,每片元执行一次。片元着色器主要功能为通过重复执行(每片元一次),将3D物体中的图元光栅化后产生的每个片元的颜色等属性计算出来送入后继阶段。
[ 片元着色器工作原理 ]
片元着色器的输入包括:
片元着色器的输出包括:
顶点着色器中可以直接声明使用浮点类型变量,而片元着色器中需要指定浮点类型变量的精度,否则会产生编译错误。 3种精度类型:
向量类型 | 说明 | 向量类型 | 说明 |
---|---|---|---|
vec2 | 包含2个浮点数的向量 | ivec4 | 包含4个浮点数的向量 |
vec3 | 包含3个浮点数的向量 | bvec2 | 包含2个布尔值的向量 |
vec4 | 包含4个浮点数的向量 | bvec3 | 包含3个布尔值的向量 |
ivec2 | 包含2个整数的向量 | bvec4 | 包含4个布尔值的向量 |
ivec3 | 包含3个整数的向量 |
分向量访问方式:
3D场景中的移位,旋转,缩放等变换都是由矩阵的运算来实现的。
矩阵类型 | 说明 |
---|---|
mat2 | 2x2的浮点矩阵 |
mat3 | 3x3的浮点矩阵 |
mat4 | 4x4的浮点矩阵 |
采样器变量不能在着色器中初始化。一般情况下采样器变量都是用uniform限定符来修饰,从宿主语言传递进着色器的值。
采样器类型 | 说明 |
---|---|
sampler2D | 用于访问二维纹理 |
sampler3D | 用于访问三维纹理 |
samplerCube | 用于访问立方贴纸纹理 |
//定义结构体CubeInfo
struct CubeInfo{
vec3 color;
vec3 position;
vec2 textureCoor;
}
//声明一个CubeInfo类型的变量
CubeInfo info;
内建变量都是以“gl_”开头,因此用户自定义的变量不允许使用“gl_”开头。
属性变量,一致变量和易变变量在声明的时候一定不能进行初始化。
限定符 | 说明 |
---|---|
attribute | 一般用于每个顶点都各不相同的量。如顶点位置,颜色等 |
uniform | 一般用于对同一组顶点组成的单个3D物体中所有顶点都相同的量,如当前的光源位置 |
varying | 用于从顶点着色器传递到片元着色器的变量 |
const | 用于声明常量 |
attribute 变量用于接收渲染管线传递进顶点着色器的当前待处理顶点的各种属性值。这些属性值每个顶点各自拥有独立的副本,用于描述顶点的各项特征:顶点坐标,法向量,颜色,纹理坐标等。 attribute限定符只能用于顶点着色器中,不能在片元着色器中使用。且attribute限定符只能用来修饰浮点数标量,浮点数向量以及矩阵变量,不能用来修饰其他类型变量。
若需要渲染的3D物体中有很多顶点,顶点着色器就需要执行很多次。因此当今主流的GPU中都配置了不止一套顶点着色器的硬件,数量从几套到几百套不等。通过这些顶点着色器的并发执行,可以大大提高渲染效率。
attribute变量的值只能由宿主程序传入渲染管线,相关代码如下:
//获取顶点位置属性引用的值
int maPositionHandle = GLES20.glGetAttribLocation(mProgram, "aPosition");
//将顶点位置数据传进渲染管线
GLES20.glVertexAttribPointer(
maPositionHandle, //顶点位置属性引用
3, //一个顶点的数据个数(x, y, z)
GLES20.GL_FLOAT; //数据类型
false, //是否规格化
3*4, //一个顶点的数据尺寸(每个浮点数4字节,共3*4字节)
mVertexBuffer //存放了数据的缓冲区
);
//启用顶点位置数据
GLES20.glEnableVertexAttribArray(maPositionHandle);
uniform为一致变量限定符,一致变量指的是对于同一组顶点组成的单个3D物体中所有顶点都相同的量。uniform变量可以用于顶点着色器和片元着色器中,支持用来修饰所有的基本数据类型。 uniform变量的值只能由宿主程序传入渲染管线,相关代码如下:
//获取着色器程序中总变换矩阵一致变量的引用
int muMVPMatrixHandle = GLES20.glGetUniformLocation(mProgram, "mMVPMatrix");
//通过一致变量的引用将一致变量值传入渲染管线
GLES20.glUniformMatrix4fv(muMVPMatrixHandle, 1, false, Triangle.getFinalMatrix(mMMatrix), 0);
不同类型的一致变量传入渲染管线的方法也是不同的:
varying变量用于将数据从顶点着色器传递到片元着色器。
[ 易变变量工作原理 ]
首先顶点着色器在每个顶点中都对易变变量vPosition进行赋值,接着在片元着色器中接收到的易变变量vPosition其实并不是某个顶点赋的特定值,而是根据片元所在位置及图元中各个顶点的位置进行插值计算产生的值。
从上述描述中可以知道,光栅化后产生多少个片元,就会插值计算出多少套的易变变量,同时,渲染管线就会调用多少次的片元着色器。对于一个3D物体,片元着色器的执行次数远远大于顶点着色器的执行次数。所以GPU硬件中配置的片元着色器数量远远大于顶点着色器数量。
一个着色器程序一般由3部分组成:全局变量声明,自定义函数和main函数。
uniform mat4 uMVPMatrix;
attribute vec3 aPosition;
attribute vec2 aTexCoord;
varying vec2 vTexCoord;
void positionShift(){
gl_Position = uMVPMatrix * vec4(aPosition, 1);
}
void main(){
positionShift();
vTexCoord = vTexCoord;
}
着色器程序中要求被调用的函数必须在被调用之前声明。
[ gl_FragCoord包含坐标信息 ]