GLSL语言基础

转自:


 小楼一夜听春雨、http://www.kankanews.com/ICkengine/archives/120870.shtml

 


变量


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



基本类型

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



结构体


结构体可以组合基本类型和数组来形成用户自定义的类型。在定义一个结构体的同时,你可以定义一个结构体实例。

[cpp]  view plain  copy
  1. struct surface   
  2. {  
  3.   float indexOfRefraction;  
  4.   
  5.   vec3 color;float turbulence;  
  6.   
  7. } mySurface;  


或者后面再定义。

[cpp]  view plain  copy
  1. surface secondeSurface;  


你可以通过=为结构体赋值,或者使用  ==,!= 来判断两个结构体是否相等。

[cpp]  view plain  copy
  1. mySurface = secondSurface;  
  2.   
  3. mySurface == secondSurface;  



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

[cpp]  view plain  copy
  1. vec3 color = mySurface.color + secondSurface.color;  



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

[cpp]  view plain  copy
  1. struct myStruct   
  2. {  
  3.   
  4. vec3 points[3]; //固定大小的数组是合法的  
  5.   
  6. surface surf; //可以,之前已经定义了  
  7.   
  8. struct velocity   
  9. {  //不合法float speed;  
  10.   
  11.   vec3 direction;  
  12.   
  13.  } velo;  
  14.   
  15.  subSurface sub; //不合法,没有预先声明  
  16. };  
  17. struct subSurface   
  18. {  
  19.   int id;  
  20. };  



数组


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

[cpp]  view plain  copy
  1. surface mySurfaces[];  
  2. vec4 lightPositions[8];  
  3. vec4 lightPos[] = lightPositions;  
  4.   
  5. const int numSurfaces = 5;  
  6. surface myFiveSurfaces[numSurfaces];  
  7.   
  8. float[5] values;  


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

[cpp]  view plain  copy
  1. lightPositions.length() //返回数组的大小 8  



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



修饰符


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




内置变量


内置变量可以与固定函数功能进行交互。在使用前不需要声明。

顶点着色器可用的内置变量如下表:




片段着色器的内置变量如下表:





表达式

操作符

GLSL语言的操作符与C语言相似。如下表(操作符的优先级从高到低排列)



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


数组访问


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

[cpp]  view plain  copy
  1. vec4 myColor, ambient, diffuse[6], specular[6];  
  2.   
  3. myColor = ambient + diffuse[4] + specular[4];  

构造函数


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

[cpp]  view plain  copy
  1. vec3 myNormal = vec3(1.0, 1.0, 1.0);  
  2.   
  3. greenTint = myColor + vec3(0.0, 1.0, 0.0);  
  4.   
  5. ivec4 myColor = ivec4(255);  



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

[cpp]  view plain  copy
  1. vec4 color = vec4(1.0, vec2(0.0, 1.0), 1.0);  
  2.   
  3. vec3 v = vec3(1.0, 10.0, 1.0);  
  4.   
  5. vec3 v1 = vec3(v);  
  6.   
  7. vec2 fv = vec2(5.0, 6.0);  
  8.   
  9. float f = float(fv); //用x值2.5构造,y值被舍弃  



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

[cpp]  view plain  copy
  1. mat3 m3 = mat3(1.0);  



构造出来的矩阵式:

1.0 0.0 0.0

0.0 1.0 0.0

0.0 0.0 1.0



[cpp]  view plain  copy
  1. mat2 matrix1 = mat2(1.0, 0.0, 0.0, 1.0);  
  2.   
  3. mat2 matrix2 = mat2(vec2(1.0, 0.0), vec2(0.0, 1.0));  
  4.   
  5. mat2 matrix3 = mat2(1.0);   
  6.   
  7. 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.

[cpp]  view plain  copy
  1. float f = 1.7;  
  2.   
  3. int I = int(f); // I = 1  



数组的初始化

可以在构造函数中传入值来初始化数组中对应的每一个值。

[cpp]  view plain  copy
  1. ivec2 position[3] = ivec2[3]((0,0), (1,1), (2,2));  
  2.   
  3. ivec2 pos2[3] = ivec2[]((3,3), (2,1), (3,1));  



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

[cpp]  view plain  copy
  1. struct surface   
  2. {   
  3.  int index;  
  4.  vec3 color;   
  5.  float rotate;  
  6. };  
  7.   
  8. 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重复了。下面是用法举例:

[cpp]  view plain  copy
  1. vec3 myVec = {0.5, 0.35, 0.7};  
  2. float r = myVec.r;  
  3. float myYz = myVec.yz;  
  4. float myQ = myVec.q;//出错,数组越界访问,q代表第四个元素float myRY = myVec.ry; //不合法  

混合使用记法
较特殊的使用方式,你 可以重复 向量中的元素,或者颠倒其顺序。如:

[cpp]  view plain  copy
  1. vec3 yxz = myVec.yxz; //调换顺序  
  2. vec4 mySSTT = myVec.sstt; //重复其中的值  


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

[cpp]  view plain  copy
  1. vec4 myColor = {0.0, 1.0, 2.0, 1.0};  
  2. myColor.x = -1.0;  
  3. myColor.yz = vec2(3.0, 5.0);  
  4. myColor.wx = vec2(1.0, 3.0);  
  5. myColor.zz = vec2(2.0, 3.0); //不合法  


我们也可以通过使用下标来访问向量或矩阵中的元素。如果越界那行为将是 未定义 的。

[cpp]  view plain  copy
  1. float myY = myVec[1];  



在矩阵中,可以通过一维的下标来获得该列的向量(OpenGL的矩阵是 列主顺序 的)。 二维的小标 来获得向量中的元素。

[cpp]  view plain  copy
  1. mat3 myMat = mat3(1.0);  
  2. vec3 myVec = myMat[0]; //获得第一列向量 1.0, 0.0, 0.0  
  3. float f = myMat[0][0]; // 第一列的第一个向量  




控制流




循环

与C和C++相似,GLSL语言也提供了for, while, do/while的循环方式。使用continue跳入下一次循环,break结束循环。
[cpp]  view plain  copy
  1. for (l = 0; l < numLights; l++)  
  2. {   
  3.  if(!lightExists[l])  
  4.     continue;  
  5.  color += light[l];  
  6. }while (i < num)  
  7. {  
  8.  sum += color[i];  
  9.  i++;  
  10. }do  
  11. {  
  12.  color += light[lightNum];  
  13.  lightNum--;  
  14. }while (lightNum > 0)  


if/else

[cpp]  view plain  copy
  1. color = unlitColor;if (numLights > 0)  
  2. {  
  3.  color = litColor;  
  4. }  
  5. else  
  6. {  
  7.  color = unlitColor;  
  8. }  

discard

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

[cpp]  view plain  copy
  1. if (color.a < 0.9)  
  2.   
  3. discard;  


函数

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

[cpp]  view plain  copy
  1. void main(void)  
  2. {  
  3. ...  
  4. }  


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

[cpp]  view plain  copy
  1. //函数声明  
  2. bool isAnyNegative(const vec4 v);  
  3.   
  4. //函数调用  
  5. void main(void)  
  6. {  
  7.   bool isNegative = isAnyNegative(gl_Color);  
  8.   ...  
  9. }  
  10.   
  11. //定义  
  12. bool isAnyNegative(const vec4 v)  
  13. {  
  14.   if (v.x < 0.0 || v.y < 0.0 || v.z < 0.0 || v.w < 0.0)  
  15.     return true;  
  16.   else  
  17.    return false;  
  18. }  


结构体和数组也可以作为函数的参数 。如果是数组作为函数的参数,则 必须制定其大小 在调用传参时,只传数组名就可以了

[cpp]  view plain  copy
  1. vec4 sumVectors(int sumSize, vec4 v[10]);  
  2. void main()  
  3. {  
  4.  vec4 myColors[10];  
  5.  ...  
  6.  vec4 sumColor = sumVectors(5, myColors);  
  7. }  
  8.   
  9. vec4 sumVectors(int sumSize, vec4 v[10])  
  10. {  
  11.  int i = 0;  
  12.  vec4 sum = vec4(0.0);  
  13.  for(; i < sumSize; ++i)  
  14.  {  
  15.   sum += v[i];   
  16.  }  
  17.  return sum;  
  18. }  



GLSL的函数是支持 重载 的。函数可以同名但其参数类型或者参数个数不同即可。

[cpp]  view plain  copy
  1. float sum(float a, float b)  
  2. {  
  3.   return a + b;  
  4. }  
  5.   
  6. vec3 sum(vec3 v1, vec3 v2)  
  7. {  
  8.   return v1 + v2;  
  9. }  


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



GLSL中提供了许多内建的函数,来方便我们的使用。

可以在官方手册中查找相关的函数


GLSL指南


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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值