GLSL语言OpenGLES(二)

前言

opengl es通过顶点着色器程序和片段着色器程序来实现可编程的渲染,着色器程序也有自己的编程语言简称为GLSL,GLSL语言和C语言比较类似,接下来就是对GLSL语言特性的记录。

GLSL(着色器编程语言)

  • 1、介绍
    是一个和C语言语法比较类似的着色器变成语言
  • 2、注释
    和C语言一样
// this is a comment
/**
  *   this is muti comment
*/

备注:GLSL语言必须由ASCII码字符组成,如果包括非ascii码编译会出错

  • 3、变量命名
    GLSL的变量名必须以字母或者下划线开头,由英文字母,数字,组成,且不能是gl或__开头(都是系统预留的),也不能是系统关键字
  • 4、预处理指令
    预处理指令以#开头,#号之前不能有除了空白字符之外的任何字符。每一个指令独占一行。内置的预处理指令如下:
#define
#undef
#if
#ifdef
#ifndef
#else
#elif
#endif
#error
#pragma
#extension
#version
#line
#pragma
// 编译指示。用来控制编译器的一些行为,开发和调试时可以设置为off,默认设为on。
#pragma optimize(on)
#pragma optimize(off)
// 开发和调试时可以打开debug选项,以便获取更多的调试信息。默认设为off。
#pragma debug(on)
#pragma debug(off)
#extension
// 如果想使用GLGL默认不支持的操作,则必须启用对应的扩展,启用一个扩展可以使用下面的命令:
#extension : behavior
#extension all : behavior
其中,extension_name是扩展的名称,all是指所有的编译器支持的扩展。
behavior是指对该扩展的具体操作。比如启用、禁用等等。详情如下:
behavior                       作用                             
require                         启用该扩展。如果不支持,则报错。
enable                          启用该扩展。如果不支持,则会警告。extension_name是all的时候会报错。
warn                            启用该扩展。但是会检测到所有使用该扩展的地方,提出警告。
disable                         禁用该扩展。如果该扩展不被支持,则提出警告。
  • 5、预定义的变量
    除此之外,还预定义了一些变量:
    _LINE_ :int类型,当前的行号,也就是在Source String中是第一行
    _FILE_ :int类型,当前Source String的唯一ID标识
    _VERSION_ :int类型,GLGL的版本
    GL_ES :对于嵌入式系统(Embed System,简称 ES),它的值为1,否则为0

  • 6、运算符及其优先级
    序号|运算符|描述|优先级
    --|:--:|--:|--:
    | 1 | 小括号 |()| 从右往左 |
    | 2 | 乘除求余 | * / % | 从左往右 |
    | 3 | 加减法 | + - | 从左往右 |
    | 4 | 位运算 移位 | 左移<< 和 右移 >> | 从左往右 |
    | 5 | 大小关系 | < > <= >= | 从左往右 |
    | 6 | 相等性判断 | == != | 从左往右 |
    | 7 | 位运算 与 | & | 从左往右 |
    | 8 | 位运算 非 | ^ | 从左往右 |
    | 9 | 位运算 或 | | | 从左往右 |
    | 10 | 逻辑与 | && | 从左往右 |
    | 11 | 逻辑或 | || | 从左往右 |

  • 7、关键词
    列举一下GLSL中的关键词,这些全部是系统保留的,不可私自篡改。

attribute const uniform varying
break continue do for while
if else
in out inout
float int void bool true false
lowp mediump highp precision invariant
discard return
mat2 mat3 mat4
vec2 vec3 vec4 ivec2 ivec3 ivec4 bvec2 bvec3 bvec4
sampler2D samplerCube
struct

asm
class union enum typedef template this packed
goto switch default
inline noinline volatile public static extern external interface flat
long short double half fixed unsigned superp
input output
hvec2 hvec3 hvec4 dvec2 dvec3 dvec4 fvec2 fvec3 fvec4
sampler1D sampler3D
sampler1DShadow sampler2DShadow
sampler2DRect sampler3DRect sampler2DRectShadow
sizeof cast
namespace using

除此之外,所有的以__开头的变量全部是预留的,自定义的变量不能以__开头。

精度限定符

先看如下片段着色器的GLSL代码

uniform sampler2D colorMap;
varying lowp vec2 varyingCoordPos;
void main() {
    vec4 color = texture2D(colorMap,varyingCoordPos);
    gl_FragColor = color;
}

这段代码编译时会提示"declaration must include a precision qualifier for type",意思就是声明变量时没有指定精度限定符

  • 1、精度范围
    对于单个变量使用精度限定符的语法为
    存储限定符 精度限定符 数据类型 变量名,比如:
    lowp float color;
    varying mediump vec2 Coord;
    highp mat4 m;
    浮点型变量的精度范围
    highp (-2的62次方, 2的62次方);
    mediump (-2的14次方, 2的14次方);
    lowp (-2,2);
    整型变量的精度范围
    highp (-2的16次方, 2的16次方);
    mediump (-2的10次方, 2的10次方);
    lowp (-2的8次方, 2的8次方);
    字符型和布尔型是没有精度范围的
    所以对于高精度和中精度,整型类变量(包括标量和向量)可以准确的的转化为同样精度修饰符修饰的浮点型类变量,例如highp int 可以转换为 highp float ,mediump int 可以转换为 mediump float;但是lowp int 缺不可以转化为 lowp float,因为会产生数据溢出。
  • 2、默认精度
    通过precision关键字来指定默认精度,这样就不用每一个变量前面都声明精度限定符了,具体语法如下:
    precision highp|mediump|lowp type;
    那么从该语句之后所有的没有精度限定符修饰的type数据类型的变量都将采用此默认精度,包括全局变量声明、函数返回值声明、函数参数声明以及本地变量声明等,只要是没有精度修饰符的变量将使用和它最近的precision语句中的精度。
    例如:
precision highp float;

代表之后所有和float有关的没有精度限定符修饰的变量的精度都为float

  • 3、预定义精度
    GLSL为我们预先设定了一些数据类型的默认精度,顶点着色器的预定义全局默认精度语句:
    precision highp float;
    precision highp int;
    precision lowp sampler2D;
    precision lowp samplerCube;
    片元着色器预定义全局默认精度:
    precision mediump int;
    precision lowp sampler2D;
    precision lowp samplerCube;
    所以片元着色器对于浮点型变量是没有设置预定义精度的,需要用前面的方式手动指定,这里也解释了为什么前面的代码编译会出错的原因。

GLSL中矩阵和向量相关运算

引用GLSL官方文档中的例子,这里以三维向量和三维矩阵为例,二维和四维类似

  • 例子1

向量与单个变量相加,将向量中的每个元素分别于这个变量相加,结果仍然为向量,且不区分向量和单个变量的顺序即:
vec3 v, u;
float f;

v = u + f;
等价于
v.x = u.x + f;
v.y = u.y + f;
v.z = u.z + f;

v = f + u;
等价于
v.x = f + u.x;
v.y = f + u.y;
v.z = f + u.z;
备注:向量与单个变量的乘法一样。减法和除法则可以换成等价的加法和乘法

  • 例子2

两个向量相加,分别将两个向量对应位置的元素相加,结果仍然为向量,且不区分向量和单个变量的顺序即:
vec3 v, u, w;

w = v + u;
等价于
w.x = v.x + u.x;
w.y = v.y + u.y;
w.z = v.z + u.z;
乘法一样

  • 例子3

向量乘以矩阵,分别将向量与矩阵的每一列相乘,结果为行向量(这里向量只有一行)
vec3 v, u;
mat3 m;
u = v * m;
等价于
u.x = v.x * m[0].x + v.y * m[0].y + v.z * m[0].z;
u.y = v.x * m[1].x + v.y * m[1].y + v.z * m[1].z;
u.z = v.x * m[2].x + v.y * m[2].y + v.z * m[2].z;

  • 例子4

矩阵乘以向量,分别矩阵的每一行与向量相乘,结果为列向量(这里向量只有一列)
vec3 v, u;
mat3 m;
u = m * v;
等价于
u.x = m[0].x * v.x + m[1].x * v.y + m[2].x * v.z;
u.y = m[0].y * v.x + m[1].y * v.y + m[2].y * v.z;
u.z = m[0].z * v.x + m[1].z * v.y + m[2].z * v.z;

这里与例子3的区别就是它的最终结果变成了列向量

  • 例子5
    矩阵乘以矩阵,满足高度代数中矩阵相乘的算法
    mat m, n, r;
    r = m * n;
    等价于
    r[0].x = m[0].x * n[0].x + m[1].x * n[0].y + m[2].x * n[0].z;
    r[1].x = m[0].x * n[1].x + m[1].x * n[1].y + m[2].x * n[1].z;
    r[2].x = m[0].x * n[2].x + m[1].x * n[2].y + m[2].x * n[2].z;
    r[0].y = m[0].y * n[0].x + m[1].y * n[0].y + m[2].y * n[0].z;
    r[1].y = m[0].y * n[1].x + m[1].y * n[1].y + m[2].y * n[1].z;
    r[2].y = m[0].y * n[2].x + m[1].y * n[2].y + m[2].y * n[2].z;
    r[0].z = m[0].z * n[0].x + m[1].z * n[0].y + m[2].z * n[0].z;
    r[1].z = m[0].z * n[1].x + m[1].z * n[1].y + m[2].z * n[1].z;
    r[2].z = m[0].z * n[2].x + m[1].z * n[2].y + m[2].z * n[2].z;

你可能感兴趣的:(GLSL语言OpenGLES(二))