OpenGL ES 着色器
- OpenGL ES 着色器
- 着色器语言
- 向量
- 矩阵
- 向量和矩阵的分量
- 采样器
- 数组
- 类型转换
- 变量限定符
- varying变量的原理
- 获取着色器变量
- 内建变量
- 顶点着色器
- 片元着色器
- 函数
- 片元着色器浮点变量精度
- 着色器程序
1.着色器语言
着色器语言是一种高级图形编程语言,和C/C++语言很类似,但存在很大差别,比如,不支持double,byte
,short,不支持unin,enum,unsigned以及位运算等,但其加入了很多原生的数据类型,如向量,矩阵等。
数据类型可分为标量、向量、矩阵、采样器、结构体、数组等
Variable Class | Types | Description |
---|---|---|
Scalars | float, int, bool | 标量数据类型浮点数、整形数、布尔值 |
Floating-point Vectors | float, vec2, vec3, vec4 | 浮点型向量,1、2、3、4 维 |
Integer vector | int, ivec2, ivec3, ivec4 | 整形向量,1、2、3、4 维 |
Boolean vector | bool,bvec2,bvec3,bvec4 | 布尔向量,1、2、3、4维 |
Matrices | mat2, mat3, mat4 | 浮点类型矩阵 2×2,3×3,4×4 |
向量
向量传递参数,如果只提供一个标量,这个值用于设置所有向量的值;如果输入是多个标量或者是矢量,从左到右设置矢量变量的参数,如果多个矢量作为参数,那么至少要有和变量一样多的分量
vec4 myVec4 = vec4(1.0); // myVec4 = {1.0, 1.0, 1.0, 1.0}
vec3 myVec3 = vec3(1.0, 0.0, 0.5); // myVec3 = {1.0, 0.0, 0.5}
vec3 temp = vec3(myVec3); // temp = myVec3
vec2 myVec2 = vec2(myVec3); // myVec2 = {myVec3.x, myVec3.y}
myVec4 = vec4(myVec2, temp); // myVec4 = {myVec2.x, myVec2.y, temp.x, temp.y}
矩阵
矩阵操作在OpenGL ES中的使用非常广泛,涉及到图形的平移缩放旋转等操作都是由矩阵来实现的.
向矩阵传递参数:
- 提供的是一个标量,那么标量复制给与矩阵的主对角线
- 一个矩阵能被多个向量赋值,如,mat2可以用两个vec2赋值
- 一个的矩阵被多个标量赋值,按列赋值
向量和矩阵的分量
向量一般用来存储位置、颜色纹理坐标等包含不止一个的量,访问向量中某个分量的方法为:<向量名.分量名>
- 将向量看做颜色对待,四个分量为r、g、b、a,分别代表红、绿、蓝、透明度
- 将向量看做位置对待,四个分量为x、y、z、w,分别代表x轴、y轴、z轴、w
- 将向量看做纹理坐标对待,四个分量为s、t、p、q,分别代表纹理坐标的不同分量
这三种不同的命名方案不能混合使用,除此之外还可以将向量当做数组看待,用下表来访问。
vec3 myVec3 = vec3(0.0, 1.0, 2.0); // myVec3 = {0.0, 1.0, 2.0}
float x = myVec3.x;
vec3 temp;
temp = myVec3.xyz; // temp = {0.0, 1.0, 2.0}
temp = myVec3.xxx; // temp = {0.0, 0.0, 0.0}
temp = myVec3.zyx; // temp = {2.0, 1.0, 0.0}
vec4 temp2 = myVec3.xxyz; // temp2 = {0.0, 0.0, 1.0, 2.0}
对矩阵的访问当成一个二维数组即可,矩阵可以认为是由多个向量组成的
mat4 myMat4 = mat4(1.0); // Initialize diagonal to 1.0 (identity)
vec4 col0 = myMat4[0]; // Get col0 vector out of the matrix
float m1_1 = myMat4[1][1]; // Get element at [1][1] in matrix
float m2_2 = myMat4[2].z; // Get element at [2][2] in matrix
采样器
采样器专门用于进行纹理采样的相关操作,一般情况下一个采样器变量代表了衣服纹理切贴图。
sampler2D/sampler3D/samplerCube
采样器变量不是在着色器中初始化的,一般是由主程序传递进来的。
数组
声明数组时指定数组大小,反之,访问数组时的下表必须是编译时常量,这样的话,编译器会自动创建适当大小的数组
类型转换
着色器语言没有自动提升的功能,也不能强制转换,只能用构造器完成类型转换,每中内建变量类型都有一组相关的构造器。
float f = 1; // error
int i = 0.0; // error
float f = 1.0 // ok
bool b = bool(f) // ok,非0当做true
float f = float(b) // ok,bool转为浮点数,true转为1.0,false转为0.0
int i = 0; //ok
bool b = bool(i) // ok,int转为bool
变量限定符
const:常量,编译时常量,其值不可变,可以提高运行效率
attribute:属性变量,仅仅用在顶点着色器,用该限定符修饰的变量用来接受从宿主程序传进渲染管线的变量。一般用于每个顶点都不相同的量,比如顶点位置,颜色,法线等
uniform:统一变量,一般用于对同一组顶点组成的一个物体所有顶点都相同的量,比如光源位置,转换矩阵,颜色,光照等
varying:变量被用来存储顶点着色器的输出和片元着色器的输入,每个顶点着色器把输出数据转变成一个或更多片元着色器的输入,在光栅化阶段就会插值生成一系列变量
varying变量的原理
获取着色器变量
获取attribute类型变量。对于attribute限定符修饰的变量的值是由宿主程序传入渲染管线的,使用glGetAttribLocation函数获得着色器中某属性变量的引用
public static native int glGetAttribLocation(
int program, // 创建的程序对象
String name // 着色器中变量名
);
然后使用glVertexAttribPointer函数将数据传递到glGetAttribLocation返回的着色器变量引用所代表的变量中去
public static void glVertexAttribPointer(
int indx, // 属性变量的引用
int size, //每个顶点的数据个数,比如x、y、z就是3
int type, // 数据类型,如GLES20.GL_FLOAT
boolean normalized, // 是否规格化,只有使用整形数据才有意义
int stride, // 跨距,一个数组存储多个属性才有意义,指的是两个点之间有多少个字节
java.nio.Buffer ptr // 存放顶点数据缓冲
)
获取uniform类型的变量。使用glGetUniformLocation函数获得着色器中某统一变量的引用
public static native int glGetUniformLocation(
int program,
String name
);
然后使用glUniformXXX函数将数据传递到着色器中,比如glUniformMatrix4fv函数
public static native void glUniformMatrix4fv(
int location, // 统一变量的引用
int count, // 指明要更改的元素个数。如果变量不是数组,这个值应该设为1
boolean transpose, // 是否要转置矩阵,并将它作为uniform变量的值。必须为false
float[] value, // 传递给统一变量的数组元素
int offset // 偏移,取0
);
glUniformNf/glUniformNfv:将N个浮点数传入管线
glUniformNi/glUniformNiv:将N个整数传入管线
glUniformMatrixNfv:将N个整数传入管线,将N*N矩阵传入管线
内建变量
内建变量不需要声明即可使用,内建变量分为两种,输入与输出变量。
输入变量负责将渲染管线中固定功能部分生成的信息传递进着色器以供程序员使用,输出变量负责将着色器产生的信息传递给渲染管线中的固定功能。
顶点着色器
顶点着色器的内建变量主要是输出变量,即将着色器产生的值传递给渲染管线,因此在顶点着色器中要对这些内建变量赋值,包括gl_Position、gl_PointSize等。
- gl_Position:在顶点着色器对获取到的定点原始数据进行平移缩放旋转等变换后,生成新的位置,新的顶点位置通过该变量传递给渲染管线的后续操作。
- gl_PointSize:顶点着色器中可以计算一个点的大小,单位为像素,默认值为1,一般对点绘制方式有意义。
片元着色器
片元着色器中的内建输入变量,gl_FragCoord、gl_FrontFacing,并且还是只读的,是由渲染管线片元着色器之前阶段生成的。
- gl_FragCoord:vec4类型数据,含有当前片元相对窗口位置的坐标。
- gl_FrontFacing:bool类型的内建输入变量,该值表明当前正在处理的片元是否属于在光栅化阶段生成此片元对应图元的正面。点、线段没有正反面之分的图元。其生成的偏远都会被默认为是正面,三角形图元其正面取决于程序中队卷绕的设置及图元中顶点的具体卷绕情况。
片元着色器中的内建输出变量gl_FragColor、gl_FragData,在片元着色器中给这两个内建变量写入值。
- gl_FragColo:vec4变量,用来传入由片元着色器计算出来的片元颜色值。
函数
和其他语言一样,差别在于参数可以指定用途,具体的有in,out,inout修饰符表明该参数是入参还是出参。
片元着色器浮点变量精度
片元着色器中的浮点类型数据必须制定精度,不指定精度可能引起编译错误。有三种精度类型:lowp、mediump、highp,一般使用mediump类型即可。如果在开发中同一个片元着色器中浮点类型变凉都是同一种精度类型,可以整个指定着色器中浮点类型默认精度。
precision <精度> <类型>
precision mediump float;
2.着色器程序
需要创建两个对象才能用着色器进行渲染:着色器对象和程序对象。
着色器源代码被编译成一个目标形式(类似obj文件),编译之后,着色器对象可以连接到一个程序对象,程序对象可以连接多个着色器对象。
获得连接后的着色器对象的过程:
- 创建一个顶点着色器和一个片元着色器:
- 将源代码连接到每个着色器对象
- 编译着色器对象
- 创建一个程序对象
- 将编译后的着色器对象连接到程序对象
- 连接程序对象
如果没有出错,就可以在后面使用这个程序了,如从程序获取某个着色器变量,接下来为其传递值等操作。
- 创建着色器对象
public static native int glCreateShader(
int type // 着色器类型,GLES20.GL_VERTEX_SHADER或GLES20.GL_FRAGMENT_SHADER
);
- 连接源代码到着色器对象
public static native void glShaderSource(
int shader,
String string // 着色器源码
);
- 编译着色器对象
public static native void glCompileShader(
int shader
);
- 创建程序对象
mProgram = GLES20.glCreateProgram();
- 将编译后的着色器对象连接到程序对象
public static native void glAttachShader(
int program,
int shader
);
- 连接程序对象
public static native void glLinkProgram(
int program
);