GLSL语言基础

from http://www.kankanews.com/ICkengine/archives/120870.shtmlhtml

变量

GLSL的变量命名方式与C语言相似。变量的名称可使用字母,数字以及下划线,但变量名不能以数字开头,还有变量名不能以gl_做为前缀,这个是GLSL保留的前缀,用于GLSL的内部变量。固然还有一些GLSL保留的名称是不可以做为变量的名称的。数组

 

基本类型

除了布尔型,整型,浮点型基本类型外,GLSL还引入了一些在着色器中常常用到的类型做为基本类型。这些基本类型均可以做为结构体内部的类型。以下表:函数

 

类型 描述
void 跟C语言的void相似,表示空类型。做为函数的返回类型,表示这个函数不返回值。
bool 布尔类型,能够是true 和false,以及能够产生布尔型的表达式。
int 整型 表明至少包含16位的有符号的整数。能够是十进制的,十六进制的,八进制的。
float 浮点型
bvec2 包含2个布尔成分的向量
bvec3 包含3个布尔成分的向量
bvec4 包含4个布尔成分的向量
ivec2 包含2个整型成分的向量
ivec3 包含3个整型成分的向量
ivec4 包含4个整型成分的向量
mat2 或者 mat2x2 2×2的浮点数矩阵类型
mat3或者mat3x3 3×3的浮点数矩阵类型
mat4x4 4×4的浮点矩阵
mat2x3 2列3行的浮点矩阵(OpenGL的矩阵是列主顺序的)
mat2x4 2列4行的浮点矩阵
mat3x2 3列2行的浮点矩阵
mat3x4 3列4行的浮点矩阵
mat4x2 4列2行的浮点矩阵
mat4x3 4列3行的浮点矩阵
sampler1D 用于内建的纹理函数中引用指定的1D纹理的句柄。只能够做为一致变量或者函数参数使用
sampler2D 二维纹理句柄
sampler3D 三维纹理句柄
samplerCube cube map纹理句柄
sampler1DShadow 一维深度纹理句柄
sampler2DShadow 二维深度纹理句柄

结构体优化

 

结构体

结构体能够组合基本类型和数组来造成用户自定义的类型。在定义一个结构体的同时,你能够定义一个结构体实例。或者后面再定义。spa

struct surface {float indexOfRefraction;

vec3 color;float turbulence;

} mySurface;

surface secondeSurface;

你能够经过=为结构体赋值,或者使用 ==,!=来判断两个结构体是否相等。orm

mySurface = secondSurface;htm

mySurface == secondSurface;递归

只有结构体中的每一个成分都相等,那么这两个结构体才是相等的。访问结构体的内部成员使用. 来访问。ip

vec3 color = mySurface.color + secondSurface.color;ci

结构体至少包含一个成员。固定大小的数组也能够被包含在结构体中。GLSL的结构体不支持嵌套定义。只有预先声明的结构体能够嵌套其中。

struct myStruct {

  vec3 points[3]; //固定大小的数组是合法的

  surface surf;  //能够,以前已经定义了

  struct velocity {  //不合法float speed;

    vec3 direction;

  } velo;

  subSurface sub; //不合法,没有预先声明;};struct subSurface {  int id;
};

 

数组

GLSL中只可使用一维的数组。数组的类型能够是一切基本类型或者结构体。下面的几种数组声明是合法的:

surface mySurfaces[];
vec4 lightPositions[8];
vec4 lightPos[] = lightPositions;const int numSurfaces = 5;
surface myFiveSurfaces[numSurfaces];float[5] values;

指定显示大小的数组能够做为函数的参数或者使返回值,也能够做为结构体的成员.数组类型内建了一个length()函数,能够返回数组的长度。

lightPositions.length() //返回数组的大小 8

最后,你不能定义数组的数组。

 

修饰符

变量的声明可使用以下的修饰符。

修饰符 描述
const 常量值必须在声明是初始化。它是只读的不可修改的。
attribute 表示只读的顶点数据,只用在顶点着色器中。数据来自当前的顶点状态或者顶点数组。它必须是全局范围声明的,不能再函数内部。一个attribute能够是浮点数类型的标量,向量,或者矩阵。不能够是数组或则结构体
uniform 一致变量。在着色器执行期间一致变量的值是不变的。与const常量不一样的是,这个值在编译时期是未知的是由着色器外部初始化的。一致变量在顶点着色器和片断着色器之间是共享的。它也只能在全局范围进行声明。
varying 顶点着色器的输出。例如颜色或者纹理坐标,(插值后的数据)做为片断着色器的只读输入数据。必须是全局范围声明的全局变量。能够是浮点数类型的标量,向量,矩阵。不能是数组或者结构体。
centorid varying 在没有多重采样的状况下,与varying是同样的意思。在多重采样时,centorid varying在光栅化的图形内部进行求值而不是在片断中心的固定位置求值。
invariant (不变量)用于表示顶点着色器的输出和任何匹配片断着色器的输入,在不一样的着色器中计算产生的值必须是一致的。全部的数据流和控制流,写入一个invariant变量的是一致的。编译器为了保证结果是彻底一致的,须要放弃那些可能会致使不一致值的潜在的优化。除非必要,不要使用这个修饰符。在多通道渲染中避免z-fighting可能会使用到。
in 用在函数的参数中,表示这个参数是输入的,在函数中改变这个值,并不会影响对调用的函数产生反作用。(至关于C语言的传值),这个是函数参数默认的修饰符
out 用在函数的参数中,表示该参数是输出参数,值是会改变的。
inout 用在函数的参数,表示这个参数便是输入参数也是输出参数。

 

内置变量

内置变量能够与固定函数功能进行交互。在使用前不须要声明。顶点着色器可用的内置变量以下表:

名称 类型 描述
gl_Color vec4 输入属性-表示顶点的主颜色
gl_SecondaryColor vec4 输入属性-表示顶点的辅助颜色
gl_Normal vec3 输入属性-表示顶点的法线值
gl_Vertex vec4 输入属性-表示物体空间的顶点位置
gl_MultiTexCoordn vec4 输入属性-表示顶点的第n个纹理的坐标
gl_FogCoord float 输入属性-表示顶点的雾坐标
gl_Position vec4 输出属性-变换后的顶点的位置,用于后面的固定的裁剪等操做。全部的顶点着色器都必须写这个值。
gl_ClipVertex vec4 输出坐标,用于用户裁剪平面的裁剪
gl_PointSize float 点的大小
gl_FrontColor vec4 正面的主颜色的varying输出
gl_BackColor vec4 背面主颜色的varying输出
gl_FrontSecondaryColor vec4 正面的辅助颜色的varying输出
gl_BackSecondaryColor vec4 背面的辅助颜色的varying输出
gl_TexCoord[] vec4 纹理坐标的数组varying输出
gl_FogFragCoord float 雾坐标的varying输出

片断着色器的内置变量以下表:

名称 类型 描述
gl_Color vec4 包含主颜色的插值只读输入
gl_SecondaryColor vec4 包含辅助颜色的插值只读输入
gl_TexCoord[] vec4 包含纹理坐标数组的插值只读输入
gl_FogFragCoord float 包含雾坐标的插值只读输入
gl_FragCoord vec4 只读输入,窗口的x,y,z和1/w
gl_FrontFacing bool 只读输入,若是是窗口正面图元的一部分,则这个值为true
gl_PointCoord vec2 点精灵的二维空间坐标范围在(0.0, 0.0)到(1.0, 1.0)之间,仅用于点图元和点精灵开启的状况下。
gl_FragData[] vec4 使用glDrawBuffers输出的数据数组。不能与gl_FragColor结合使用。
gl_FragColor vec4 输出的颜色用于随后的像素操做
gl_FragDepth float 输出的深度用于随后的像素操做,若是这个值没有被写,则使用固定功能管线的深度值代替

 

表达式

 

操做符

GLSL语言的操做符与C语言类似。以下表(操做符的优先级从高到低排列)

操做符 描述
() 用于表达式组合,函数调用,构造
[] 数组下标,向量或矩阵的选择器
. 结构体和向量的成员选择
++ – 前缀或后缀的自增自减操做符
+ – ! 一元操做符,表示正 负 逻辑非
* / 乘 除操做符
+ - 二元操做符 表示加 减操做
<> <= >= == != 小于,大于,小于等于, 大于等于,等于,不等于 判断符
&& || ^^ 逻辑与 ,或,  异或
?: 条件判断符
= += –= *=  /= 赋值操做符
, 表示序列

像 求地址的& 和 解引用的 * 操做符再也不GLSL中出现,由于GLSL不能直接操做地址。类型转换操做也是不容许的。 位操做符(&,|,^,~, <<, >> ,&=, |=, ^=, <<=, >>=)是GLSL保留的操做符,未来可能会被使用。还有求模操做(%,%=)也是保留的。

 

数组访问

数组的下标从0开始。合理的范围是[0, size - 1]。跟C语言同样。若是数组访问越界了,那行为是未定义的。若是着色器的编译器在编译时知道数组访问越界了,就会提示编译失败。

vec4 myColor, ambient, diffuse[6], specular[6];

myColor = ambient + diffuse[4] + specular[4];

 

构造函数

构造函数能够用于初始化包含多个成员的变量,包括数组和结构体。构造函数也能够用在表达式中。调用方式以下:

vec3 myNormal = vec3(1.0, 1.0, 1.0);

greenTint = myColor + vec3(0.0, 1.0, 0.0);

ivec4 myColor = ivec4(255);

还可使用混合标量和向量的方式来构造,只要你的元素足以填满该向量。

vec4 color = vec4(1.0, vec2(0.0, 1.0), 1.0);

vec3 v = vec3(1.0, 10.0, 1.0);

vec3 v1 = vec3(v);

vec2 fv = vec2(5.0, 6.0);

float f = float(fv); //用x值2.5构造,y值被舍弃

对于矩阵,OpenGL中矩阵是列主顺序的。若是只传了一个值,则会构形成对角矩阵,其他的元素为0.

mat3 m3 = mat3(1.0);

构造出来的矩阵式:

1.0 0.0 0.0

0.0 1.0 0.0

0.0 0.0 1.0

mat2 matrix1 = mat2(1.0, 0.0, 0.0, 1.0);

mat2 matrix2 = mat2(vec2(1.0, 0.0), vec2(0.0, 1.0));

mat2 matrix3 = mat2(1.0); 

mat2 matrix4 = mat2(mat4(2.0)); //会取 4×4矩阵左上角的2×2矩阵。

构造函数能够用于标量数据类型的转换。GLSL不支持隐式或显示的转换,只能经过构造函数来转。其中int转为float值是同样的。float转为int则小数部分被丢弃。int或float转为bool,0和0.0转为false,其他的值转为true. bool转为int或float,false值转为0和0.0,true转为1和1.0.

float f = 1.7;

int I = int(f); // I = 1

数组的初始化,能够在构造函数中传入值来初始化数组中对应的每个值。

ivec2 position[3] = ivec2[3]((0,0), (1,1), (2,2));

ivec2 pos2[3] = ivec2[]((3,3), (2,1), (3,1));

构造函数也能够对结构体进行初始化。其中顺序和类型要一一对应。

struct surface {  int  index;
  vec3 color;  float rotate;
};

surface mySurface = surface(3, vec3(red, green, blue), 0.5);

 

成分选择

向量中单独的成分能够经过{x,y,z,w},{r,g,b,a}或者{s,t,p,q}的记法来表示。这些不一样的记法用于顶点,颜色,纹理坐标。在成分选择中,你不能够混合使用这些记法。其中{s,t,p,q}中的p替换了纹理的r坐标,由于与颜色r重复了。下面是用法举例:

vec3 myVec = {0.5, 0.35, 0.7};float r = myVec.r;float myYz = myVec.yz;float myQ = myVec.q;//出错,数组越界访问,q表明第四个元素float myRY = myVec.ry; //不合法,混合使用记法

较特殊的使用方式,你能够重复向量中的元素,或者颠倒其顺序。如:

vec3 yxz = myVec.yxz; //调换顺序vec4 mySSTT = myVec.sstt; //重复其中的值

在赋值是,也能够选择你想要的顺序,可是不能重复其中的成分。

vec4 myColor = {0.0, 1.0, 2.0, 1.0};
myColor.x = -1.0;
myColor.yz = vec2(3.0, 5.0);
myColor.wx = vec2(1.0, 3.0);
myColor.zz = vec2(2.0, 3.0); //不合法

咱们也能够经过使用下标来访问向量或矩阵中的元素。若是越界那行为将是未定义的。

float myY = myVec[1];

在矩阵中,能够经过一维的下标来得到该列的向量(OpenGL的矩阵是列主顺序的)。二维的小标来得到向量中的元素。

mat3 myMat = mat3(1.0);
vec3 myVec = myMat[0]; //得到第一列向量 1.0, 0.0, 0.0float f = myMat[0][0]; // 第一列的第一个向量。

 

控制流

 

循环

与C和C++类似,GLSL语言也提供了for, while, do/while的循环方式。使用continue跳入下一次循环,break结束循环。

for (l = 0; l < numLights; l++)
{if (!lightExists[l])continue;
    color += light[l];
}while (i < num)
{
    sum += color[i];
    i++;
}do{
    color += light[lightNum];
    lightNum--;
}while (lightNum > 0)

 

if/else

color = unlitColor;if (numLights > 0)
{
    color = litColor;
}else{
    color = unlitColor;
}

 

discard

片断着色器中有一种特殊的控制流成为discard。使用discard会退出片断着色器,不执行后面的片断着色操做。片断也不会写入帧缓冲区。

if (color.a < 0.9)

discard;

 

函数

在每一个shader中必须有一个main函数。main函数中的void参数是可选的,但返回值是void时必须的。

void main(void)
{
 ...
}

GLSL中的函数,必须是在全局范围定义和声明的。不能在函数定义中声明或定义函数。函数必须有返回类型,参数是可选的。参数的修饰符(in, out, inout, const等)是可选的。

//函数声明bool isAnyNegative(const vec4 v);//函数调用void main(void)
{bool isNegative = isAnyNegative(gl_Color);
    ...
}//定义bool isAnyNegative(const vec4 v)
{if (v.x < 0.0 || v.y < 0.0 || v.z < 0.0 || v.w < 0.0)return true;elsereturn false;
}

结构体和数组也能够做为函数的参数。若是是数组做为函数的参数,则必须制定其大小。在调用传参时,只传数组名就能够了。

vec4 sumVectors(int sumSize, vec4 v[10]);void main()
{
    vec4 myColors[10];
    ...
    vec4 sumColor = sumVectors(5, myColors);
}

vec4 sumVectors(int sumSize, vec4 v[10])
{int i = 0;
    vec4 sum = vec4(0.0);for(; i < sumSize; ++i)
    {
        sum += v[i]; 
    }return sum;
}

GLSL的函数是支持重载的。函数能够同名但其参数类型或者参数个数不一样便可。

float sum(float a, float b)
{return a + b;
}

vec3 sum(vec3 v1, vec3 v2)
{return v1 + v2;
}

GLSL中函数递归是不被容许的。其行为是未定义的。

GLSL中提供了许多内建的函数,来方便咱们的使用。能够在官方手册中查找相关的函数http://www.opengl.org/sdk/docs/man/

GLSL指南 http://www.opengl.org/registry/doc/GLSLangSpec.Full.1.20.8.pdf

相关文章
相关标签/搜索