OpenGL笔记十四:GLSL语法

前言

期待您移步上篇:OpenGL笔记十三:GLSL加载纹理颠倒六种方案

概述

GLSL 全称 OpenGL Shading Language,是用来在 OpenGL 中着色编程的语言,即开发人员写的自定义程序代码。是执行在 GPU上的,代替了固定的渲染管线的一部分,使渲染管线中不同层次具有可编程性。
GLSL 其使用 C 语言作为基础高阶着色语言,避免了使用汇编语言或硬件规格语言的复杂性。
GLSL 的变量命名方式与 C 语言类似,可使用字母,数字以及下划线,不能以数字开头。还需要注意的是,变量名不能以 gl_ 作为前缀,这个是 GLSL 保留的前缀,用于 GLSL 的内部变量。

数据类型

1.基本数据类型

类型 描述
void 表示空类型,作为函数的返回类型,表示这个函数不返回值。
bool 布尔类型,true or false,以及能产生布尔型的表达式。
int 有符号整型
uint 无符号整型
float 浮点型

2.纹理采样类型

类型 描述
sampler1D 用于内建的纹理函数中引用指定的1D纹理的句柄。只可以作为一致变量或者函数参数使用
sampler2D 二维纹理
sampler3D 三维纹理
samplerCube 盒纹理 cube mapped texture
sampler1DShadow 一维深度纹理
sampler2DShadow 二维深度纹理

3.向量类型

类型 描述
vec2, vec3, vec4 n维浮点数向量 n-component floating point vector
ivec2,ivec3,ivec4 n维整数向量 signed integer vector
uvec2,uvec3,uvec4 n维无符号整数向量 unsigned integer vector
bvec2,bvec3,bvec4 n维布尔向量 Boolean vector
  • 向量声明-4分量float 类型向量
vec4 V1;
  • 声明并对其构造初始化
vec4 V2 = vec4(1.0, 2.0, 3.0, 4.0);
  • 向量运算
vec4 v1;
vec4 v2 = vec4(1.0,2.0,3.0,4.0);
vec4 v3 = vec4(1.0,2.0,3.0,4.0);
//注意:接下来假设所有参与运算的变量已定义并赋值。
v1 = v2 + v3;
v1 = v4;
v1 += vec4(1.0,1.0,1.0,1.0);
v1 = v1 * v2;
v1 *= 5.0;
  • 向量元素的获取(成分选择)
    4分向量对应的意义可分为: {x,y,z,w}、{r,g,b,a} 、 {s,t,p,q} 。分别对应于 顶点坐标,颜色,纹理坐标。
    因此就可以单独获取、赋值某单一元素:
//通过x,y,z,w来获取向量中元素值
v1.x = 3.0f;
v1.xy = vec2(3.0f,4.0f); 
v1.xyz = vec3(3,0f,4,0f,5.0f);

//通过颜色r,g,b,a
v1.r = 3.0f; 
v1.rgba = vec4(1.0f,1.0f,1.0f,1.0f);

//纹理坐标s,t,p,q
v1.st = vec2(1.0f,0.0f);

不可以混合使用

v1.st = v2.xt;//这是不可以的,Error !!!
  • 向量swizzle
    向量支持swizzle操作,2个及2个以上的向量元素交换。
v1.rgba = v2.bgra; 
v2.bgra = v1.rgba;
v1.rgba = v2.brga;
  • 向量赋值、一次性对所有分量操作
//赋值
v1.r = v2.b; 
v1.g = v2.g; 
v1.b = v2.r; 
v1.a = v2.a;

//向量还支持一次性对所有分量操作
v1.x = v2.x +5.0f; 
v1.y = v2.y +4.0f; 
v1.z = v2.z +3.0f;

v1.xyz = v2.xyz + vec3(5.0f,4.0f,3.0f);

4.矩阵类型

类型 描述
mat2、mat2x2 2x2的浮点数矩阵类型
mat3、mat3x3 3x3的浮点数矩阵类型
mat4、mat4x4 4x4的浮点矩阵
mat2x3 2列3行的浮点矩阵(OpenGL的矩阵是列主顺序的)
mat2x4 2列4行的浮点矩阵
mat3x2 3列2行的浮点矩阵
mat3x4 3列4行的浮点矩阵
mat4x2 4列2行的浮点矩阵
mat4x3 4列3行的浮点矩阵
  • 创建矩阵:
mat4 m1,m2,m3;
  • 构造单元矩阵:
mat4 m4 = mat4(1.0f,0.0f,0.0f,0.0f
                    0.0f,1.0f,0.0f,0.0f,
                    0.0f,0.0f,1.0f,0.0f,
                    0.0f,0.0f,0.0f,1.0f);

//或者
mat4 m5 = mat4(1.0f);

5.结构体

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

struct fogStruct {
 vec4 color;
 float start;
 float end;
 vec3 points[3]; // 固定大小的数组是合法的
} fogVar;

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

fogVar = fogStruct(vec4(1.0,0.0,0.0,1.0),0.5,2.0);
vec4 color = fogVar.color;
float start = fogVar.start;

6.数组

float floatArray[4]; 
vec4 vecArray[2];
//注意
float a[4] = float[](1.0,2.0,3.0,4.0); 
vec2 c[2] = vec2[2](vec2(1.0,2.0),vec2(3.0,4.0));

修饰符

*存储变量限定符

限定符 描述
none (默认的可省略)本地变量,可读可写,外部不可见,函数的输入参数既是这种类型
const 常量值必须在声明时初始化,它是只读的不可修改的
attribute 表示只读的顶点数据,只能存在于vertex shader中,一般用于保存顶点或法线数据,它可以在数据缓冲区中读取数据。数据来自当前的顶点状态或者顶点数组。它必须是全局范围声明的,不能再函数内部。一个attribute可以是浮点数类型的标量,向量,或者矩阵。不可以是数组或则结构体。
varying 顶点着色器的输出,主要负责在 vertex 和 fragment 之间传递变量。例如颜色或者纹理坐标,(插值后的数据)作为片段着色器的只读输入数据。必须是全局范围声明的全局变量。可以是浮点数类型的标量,向量,矩阵。不能是数组或者结构体。
centorid varying 在没有多重采样的情况下,与varying是一样的意思。在多重采样时,centorid varying在光栅化的图形内部进行求值而不是在片段中心的固定位置求值。
uniform 一致变量。在着色器执行期间一致变量的值是不变的, 一般用来放置程序传递给shader的变换矩阵,材质,光照参数等等。与const常量不同的是,这个值在编译时期是未知的是由着色器外部初始化的。一致变量在顶点着色器和片段着色器之间是共享的。它也只能在全局范围进行声明。
invariant (不变量)用于表示顶点着色器的输出和任何匹配片段着色器的输入,在不同的着色器中计算产生的值必须是一致的。所有的数据流和控制流,写入一个invariant变量的是一致的。编译器为了保证结果是完全一致的,需要放弃那些可能会导致不一致值的潜在的优化。除非必要,不要使用这个修饰符。在多通道渲染中避免z-fighting可能会使用到。
  • 函数参数限定符
    GLSL 允许自定义函数,但参数默认是以值形式(in 限定符)传入的,也就是说任何变量在传入时都会被拷贝一份,若想以引用方式传参,需要增加函数参数限定符。
限定符 描述
< none: default > 默认使用 in 限定符
in 用在函数的参数中,表示这个参数是输入的,在函数中改变这个值,并不会影响对调用的函数产生副作用。(相当于C语言的传值),这个是函数参数默认的修饰符
out 用在函数的参数中,表示该参数是输出参数,值是会改变的。
inout 用在函数的参数,表示这个参数即是输入参数也是输出参数。

其中使用 inout 方式传递的参数便与其他 OOP 语言中的引用传递类似,参数可读写,函数内对参数的修改会影响到传入参数本身。

vec4 getPosition(out vec4 p){ 
    p = vec4(0.,0.,0.,1.);
    return v4;
}

void doubleSize(inout float size){
    size= size * 3.0  ;
}

GLSL 中的运算

  • 逐分量运算

vec,mat 这些类型其实是由 float 复合而成的,当它们与float 运算时,其实就是在每一个分量上分别与 float 进行运算,这就是所谓的 逐分量运算。GLSL 里,大部分涉及 vec,mat 的运算都是逐分量运算,但也并不全是。下文中就会讲到特例。
逐分量运算 是线性的,这就是说 vec 与 float 的运算结果是还是 vec。

注意 GLSL 中没有隐式转换。
因此 int 与 vec,mat 之间是不可运算的,因为 vec 和 mat 中的每一个分量都是 float 类型的,无法与 int 进行逐分量计算。

vec3 a  = vec3(1.0, 2.0, 3.0);
mat3 m  = mat3(1.0);
float s = 10.0;

vec3 b  = s * a; // vec3(10.0, 20.0, 30.0)
vec3 c  = a * s; // vec3(10.0, 20.0, 30.0)
mat3 m2 = s * m; // = mat3(10.0)
mat3 m3 = m * s; // = mat3(10.0)
  • vec(向量) 与 vec(向量)运算
    两向量间的运算首先要保证操作数的阶数都相同,否则不能计算。例如: vec3*vec2 和 vec4+vec3等等都是不行的。
    它们的计算方式是两操作数在同位置上的分量分别进行运算,其本质还是逐分量进行的,这和上面所说的 float 类型的逐分量运算可能有一点点差异,相同的是 vec 与 vec 运算结果还是 vec,且阶数不变。
  • vec(向量) 与 mat(矩阵)
    要保证操作数的阶数相同(这里的矩阵是方阵),且 vec 与 mat 间只存在乘法运算。
vec2 v = vec2(10., 20.);
mat2 m = mat2(1., 2.,  3., 4.);
vec2 w = m * v; // = vec2(1. * 10. + 3. * 20., 2. * 10. + 4. * 20.)

vec2 v = vec2(10., 20.);
mat2 m = mat2(1., 2.,  3., 4.);
vec2 w = v * m; // = vec2(1. * 10. + 2. * 20., 3. * 10. + 4. * 20.)

精度限定

GLSL 在进行光栅化着色的时候,会产生大量的浮点数运算,这些运算可能是当前设备所不能承受的,所以 GLSL 提供了 3 种浮点数精度:highpmediumplowp ,我们可以根据不同的设备来使用合适的精度。
在变量前面加上 highp、mediump、lowp 即可完成对该变量的精度声明:

lowp float color;
varying mediump vec2 Coord;
lowp ivec2 foo(lowp mat3);
highp mat4 m;

在顶点着色器(vertexment shader)里默认是 highp 精度的。
在片元着色器(fragment shader)中是没有默认精度的。可以统一:precision mediump float;设置精度,也可以单独对每一个变量各自设置精度。

控制语句

在语法上,GLSL 与 C 非常相似, 也有 if else、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);

GLSL 还多了一种特殊的控制语句 discard,它会立即跳出片元着色器,并不在向下任何语句。也就不执行后面的片段着色操作,片段也不会写入帧缓冲区。注 GLSL 函数中没有递归!

    if (true) {
       discard;
    }

期待您移步下篇:OpenGL笔记十五:GLSL光照计算

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 156,907评论 4 360
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 66,546评论 1 289
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 106,705评论 0 238
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 43,624评论 0 203
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 51,940评论 3 285
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 40,371评论 1 210
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 31,672评论 2 310
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 30,396评论 0 195
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 34,069评论 1 238
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 30,350评论 2 242
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 31,876评论 1 256
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 28,243评论 2 251
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 32,847评论 3 231
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 26,004评论 0 8
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 26,755评论 0 192
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 35,378评论 2 269
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 35,266评论 2 259