OpenGL ES2 学习教程4——Shader语言

RichardXG

shader语言可以看成是特殊的C语言,专门用来创建顶点片段着色器程序的语言。像第二节里的程序

// vertex shader
attribute vec4 vPosition;
void main() {
  gl_Position = vPosition;
}
// fragment shader
precision mediump float;
void main() {
  gl_FragColor = vec4(0.0, 1.0, 0.0, 1.0);
}

基本类型

  • 标量 float int bool

  • 浮点矢量 float vec2 vec3 vec4

  • 整数矢量 int ivec2 ivec3 ivec4

  • 布尔矢量 bool bvec2 bvec3 bvec4

  • 矩阵 mat2 mat3 mat4(分别是22,33,4*4的浮点型矩阵)

  • void类型(表示没函数返回或空的参数)void

  • 采样器(用于指定纹理)sampler2D samplerCube

结构体和数组

struct type-name { // 结构体不支持嵌套
    members
} struct-name;  // 结构体也可以声明为数组,struct-name[]

float foo[3];
// 只支持一维数组

修饰符(限定符)

  • none 默认限定符:默认定义变量都是none,局部可读写变量或参数。

  • const 常量限定符:编译时的常量,或表示只读的函数参数,结构体的成员不能声明为常量,结构体变量可以。

  • attribute 属性限定符:可以由应用程序指定通过GLES传给顶点着色器的每一个顶点数据,只能用在顶点着色器。值为只读,只能被是定为float vec2 vec3 vec4 mat2 mat3 mat4,不能被定义为数组和结构体。GLES规定至少支持8个属性,每个属性被存储为4个分量的存储单元,因此可以将float这样的变量组合为一个vec4来节约空间。典型的被用来储存位置、法线、贴图坐标和颜色数据。着色器里被声明为属性的变量是只读资源,不能被修改。

  • uniform 全局限定符:在图元处理期间不会被改变的值,可以由应用程序通过GLES传给顶点片段着色器的全局变量。被所有的着色器共享。uniform存储各种着色器需要的数据,例如:转换矩阵、光照参数或者颜色。基本上各种输入着色器的常量参数像顶点和片段(但在编译时并不知道)都应该是uniform。他们在硬件中被放在称为“常量存储区(constant store)“中。同样它也有数量限制的,GLES规定至少128个顶点着色器uniform和16个片段着色器uniform。

  • varying 易变限定符:经过插值的易变变量,由顶点着色器传给片段着色器,易变变量在顶点着色器和片段着色器中要使用相同的类型和名字。只有在片段着色器中静态使用的易变变量才需要在顶点着色器中成对定义。不能用结构体定义,只能声明为浮点型标量,矢量和矩阵。同样有数量限制。包括属性和全局变量最大值都由glGetIntegerv查询(GL_MAX_VERTEX_ATTRIBUTE,GL_MAX_VERTEX_UNIFORM_VECTORS,GL_MAX_FRAGMENT_UNIFORM_VECTORS,GL_MAX_VARYING_VECTORS)。

GLint maxVertexAttribs;
GLint maxVertexUniforms;
GLint maxFragmentUniforms;
GLint maxVaryings;
glGetIntegerv(GL_MAX_VERTEX_ATTRIBS, &maxVertexAttribs); 
glGetIntegerv(GL_MAX_VERTEX_UNIFORM_VECTORS, &maxVertexUniforms);
glGetIntegerv(GL_MAX_FRAGMENT_UNIFORM_VECTORS, &maxFragmentUniforms);
glGetIntegerv(GL_MAX_VARYING_VECTORS, &maxVaryings);
//在我手机上的输出:
//I/GLES-Tutorial(18378): maxVertexAttribs: 16
//I/GLES-Tutorial(18378): maxVertexUniforms: 256
//I/GLES-Tutorial(18378): maxFragmentUniforms: 224
//I/GLES-Tutorial(18378): maxVaryings: 16

构造器

shader语言有着非常严格的类型判断,不支持隐式转化,类型的转化必须用构造器。

float fA = 1.0;
bool bB = true;
int iC = 0;
fA = float(bB); // convert bool to float

vec4 myVec4 = vec4(1.0); // myVec4 = {1.0, 1.0, 1.0, 1.0}
vec3 myVec3 = vec3(1.0, 0.0, 0.5);
vec3 temp = vec3(myVec3);

struct light {
    float intensity;
    vec3 position;
};
light lightVar = light(3.0, vec3(1.0));

分量

矢量的分量名称:{x,y,z,w}表示顶点; {r,g,b,a}表示颜色;{s,t,p,q}表示纹理坐标。

每个分量用点号连接(例如v2.x(一个float变量),v3.rg(一个vec2变量))。

不同组的分量不能混合使用(例如v4.xgba不合法),但是顺序可以任意,可以重复(例如v3.yyxx(一个vec4变量)),但不能超过4个,因为编译器不知道转化成什么类型(例如v4.xyzwxy不合法)。

矩阵的分量就是用下标表示,注意的是矩阵是以列优先的。

mat4 m;
m[1] = vec4(1.0); // 矩阵第二列设置为{1.0, 1.0, 1.0, 1.0}
m[2][0] = 1.0;

操作符

和C语言类似,但是更加严格。执行操作的变量必须是相同的基本类型,二进制运算加减乘除必须是浮点型或整型。

函数

函数参数需要加上限定词,指示这个变量是否能够被函数修改。

举个栗子:

vec4 myFunc(inout float myFloat, // inout parameter 
            mat4 myMat4,         // in parameter (default)
            out vec4 myVec4);    // out parameter
  • in 默认修饰符,表示为输入,不能被函数修改

  • inout 表示为引用,可以被修改

  • out 表示不是函数输入,它的值在函数返回后将被修改

shader语言提供许多内置函数,例如dot

流程控制语句

Shader语言支持简单的if-else;但是条件结果必须是布尔值。

循环语句限制非常严格:必须有循环迭代变量,他必须是增加或减少的,停止的条件必须匹配索引并且为常量表达式,在循环内部不能改变迭代的值。这些限制使得编译器能够把循环语句展开。

// 合法的栗子
for (int i = 0; i < 5; i++)
{
    sum += i;
}
// 不合法的栗子1
float myArray[5];
for (int i = 0; i < 5; i++)
{
    sum += myArray[i] // 不允许出现非常量表达式
}
// 不合法的栗子2
uniform in loopIter;
for (int i = 0; i < loopIter; i++) // 停止条件不是常量表达式
{
    sum += i;
}

预处理指令

#define  #undef  #if  #ifdef  #ifndef  #else  #elif  #endif
__LINE__    // 在着色器中的当前行号
__FILE__    // GLES2中总是0
__VERSION__ // GLES的版本 (e.g., 100)
GL_ES       // 被定义为1

#version 100   // GLES Shading Language v1.00, 总是要放在最开头

#extension extension_name : behavior
// behavior可以是require, enable, warn,disable
#extension GL_OES_texture_3D : enable  // 如果3D贴图扩展不被支持让预编译产生警告

注意和C++不同,宏不能带参数。

精度控制

精度控制能够被使用在任何浮点和整型数变量。可以设置为高highp,中mediump,低lowp。

highp vec4 position;
varying lowp vec4 color;
// 默认的精度可以这么设置在头部
precision highp float;
precision mediump int;

不变性

GLES中,invariance是被用于任何顶点着色器的变量输出的关键字。由于着色器编译时可能进行优化,一些指令被重新整理,可能两个着色器中间相同的计算产出不同的值来。在多通道着色器的特殊情况下,同样的物体使用透明混合(alpha blending)来绘制时能造成清晰度的不同。引入invariance,着色器会让同样的输入计算后输出的值相同。

invariant gl_Position;  // 声明不变性输出

// 可以使用#pragma来保证全局变量的不变性
#pragma STDGL invariant(all)  // 它将限制编译器的优化,除非必要最好不用,会导致性能下降

编辑工具

在Mac下可以使用Shader Builder(apple-opengl找到 graphics tools的链接,去下载对应你XCode版本的)来编写、运行、测试OpenGL shaders

阅读 6.8k

代码游戏
记录各类编程技术
337 声望
18 粉丝
0 条评论
337 声望
18 粉丝
文章目录
宣传栏