GLSL语法基础


简介#

GLSLOpenGL Shading Language) 全称 OpenGL 着色语言,是用来在 OpenGL 中着色编程的语言,也即开发人员写的短小的自定义程序,他们是在图形卡的 GPU上执行的,代替了固定的渲染管线的一部分,使渲染管线中不同层次具有可编程性。 GLSL 其使用 C 语言作为基础高阶着色语言,避免了使用汇编语言或硬件规格语言的复杂性。

基础语法#

注释#

单行注释:// 多行注释:/* */

变量命名

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

变量命名#

GLSL 的变量命名方式与 C 语言类似,可使用字母,数字以及下划线,不能以数字开头。还需要注意的是,变量名不能以 gl_ 作为前缀,这个是 GLSL 保留的前缀,用于 GLSL 的内部变量。

表达式#

运算符

优先级(越小越高) 运算符 说明 结合性
1 () 聚组:a*(b+c) N/A
2 [] () . ++ -- 数组下标__[],方法参数__fun(arg1,arg2,arg3),属性访问a.b,自增/减后缀a++ a-- L - R
3 ++ -- + - ! 自增/减前缀++a --a,正负号(一般正号不写)a ,-a,取反!false R - L
4 * / 乘除数学运算 L - R
5 + - 加减数学运算 L - R
7 < > <= >= 关系运算符 L - R
8 == != 相等性运算符 L - R
12 && 逻辑与 L - R
13 ^^ 逻辑排他或(用处基本等于!=) L - R
14 II 逻辑或 L - R
15 ? : 三目运算符 L - R
16 = += -= *= /= 赋值与复合赋值 L - R
17 , 顺序分配运算 L - R

左值与右值:

左值:表示一个储存位置,可以是变量,也可以是表达式,但表达式最后的结果必须是一个储存位置.

右值:表示一个值, 可以是一个变量或者表达式再或者纯粹的值.

操作符的优先级:决定含有多个操作符的表达式的求值顺序,每个操作的优先级不同.

操作符的结合性:决定相同优先级的操作符是从左到右计算,还是从右到左计算。

操作符

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

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

求地址的& 和 解引用的 * 操作符不再GLSL中出现,因为GLSL不能直接操作地址。

类型转换操作也是不允许的。

位操作符(&,|,^,~, <<, >> ,&=, |=, ^=, <<=, >>=)是GLSL保留的操作符,将来可能会被使用。

求模操作(%,%=)也是保留的。


数组访问

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

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

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

语句#

流控制

glsl的流控制和c语言非常相似,这里不必再做过多说明,唯一不同的是片段着色器中有一种特殊的控制流discard. 使用discard会退出片段着色器,不执行后面的片段着色操作。片段也不会写入帧缓冲区。

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 (true)
    discard;

discard

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

if (ture)
	discard;

分量访问

分量访问符 符号描述
(x,y,z,w) 与位置相关的分量
(r,g,b,a) 与颜色相关的分量
(s,t,p,q) 与纹理坐标相关的分量

GLSL函数#

glsl允许在程序的最外部声明函数.函数不能嵌套,不能递归调用,且必须声明返回值类型(无返回值时声明为void) 在其他方面glsl函数与c函数非常类似.

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

void doubleSize(inout float size){
    size= size*2.0  ;
}
void main() {
    float psize= 10.0;
    doubleSize(psize);
    gl_Position = getPosition();
    gl_PointSize = psize;
}

GLSL预编译指令#

以 # 开头的是预编译指令,常用的有:

#define #undef #if #ifdef #ifndef #else
#elif #endif #error #pragma #extension #version #line

比如 #version 100 他的意思是规定当前shader使用 GLSL ES 1.00标准进行编译,如果使用这条预编译指令,则他必须出现在程序的最开始位置.

内置的宏:

__LINE__ : 当前源码中的行号.

__VERSION__ : 一个整数,指示当前的glsl版本 比如 100 ps: 100 = v1.00

GL_ES : 如果当前是在 OPGL ES 环境中运行则 GL_ES 被设置成1,一般用来检查当前环境是不是 OPENGL ES.

GL_FRAGMENT_PRECISION_HIGH : 如果当前系统glsl的片元着色器支持高浮点精度,则设置为1.一般用于检查着色器精度.

实例:

1.如何通过判断系统环境,来选择合适的精度:

#ifdef GL_ES //
#ifdef GL_FRAGMENT_PRECISION_HIGH
precision highp float;
#else
precision mediump float;
#endif
#endif

2.自定义宏:

#define NUM 100
#if NUM==100
#endif

数据类型#

基础数据类型#

类型 描述
void C 语言的 void 类似,表示空类型。作为函数的返回类型,表示这个函数不返回值。
bool 布尔类型truefalse,以及可以产生布尔型的表达式。
int 有符号整型
uint 无符号整形
float 浮点型

纹理采样类型#

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

聚合类型(向量和矩阵类型)#

向量类型

类型 描述
vec2,vec3,vec4 2分量、3分量和4分量浮点向量
ivec2,ivec3,ivec4 2分量、3分量和4分量整数向量
uvec2,uvec3,uvec4 2分量、3分量和4分量无符号整数向量
bvec2,vbec3,bvec4 2分量、3分量和4分量布尔向量

A、向量声明--4分量的float 类型向量

vec4 V1;

B、声明向量并对其进行构造

vec4 V2 = vec4(1,2,3,4);

C、向量运算

vec4 v;
vec4 vOldPos = vec4(1,2,3,4);
vec4 vOffset = vec4(1,2,3,4);
//注意:接下来假设所有参与运算的变量已定义并赋值。
v = vOldPos + vOffset;
v = vNewPos;
v += vec4(10,10,10,10);
v = vOldPos * vOffset;
v *= 5;

D、向量元素的获取(成分选择)

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

vec3 v1 = {0.5, 0.35, 0.7};
vec4 v2 = {0.1, 0.2, 0.3, 0.4};

可以通过 {x,y,z,w}, {r,g,b,a} 或者 {s,t,p,q} 来取出向量中的元素值。 通过 x,y,z,w

v2.x = 3.0f;
v2.xy = vec2(3.0f,4.0f);
v2.xyz = vec3(3,0f,4,0f,5.0f);

通过 r,g,b,a

v2.r = 3.0f;
v2.rgba = vec4(1.0f,1.0f,1.0f,1.0f);

通过 s,t,q,r

v2.stqr = vec2(1.0f, 0.0f, 0.0f, 1.0f);

错误示例:

float myQ = v1.q;// 出错,数组越界访问,q代表第四个元素
float myRY = v1.ry; // 不合法,混合使用记法

向量还支持一次性对所有分量操作

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);

(2)矩阵类型

类型 描述
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 m2 = 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 m4 = mat4(1.0f);

向量矩阵运算#

1、不同类型 float 与 int 间的运算:**

floatint 之间进行运算,需要进行一次显示转换,以下表达式都是正确的:

int a = int(2.0);
float a = float(2);

int a = int(2.0)*2 + 1;
float a = float(2)*6.0+2.3;
复制代码

2、float 与 vec(向量)、mat(矩阵) 的运算:

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

intvec,mat 之间是不可运算的,因为 vecmat 中的每一个分量都是 float 类型的,无法与 int 进行逐分量计算。

下面枚举了几种 floatvec,mat 运算的情况:

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)
复制代码

3、vec(向量) 与 vec(向量)运算:

两向量间的运算首先要保证操作数的阶数都相同,否则不能计算。例如: vec3\*vec2vec4+vec3 等等都是不行的。

它们的计算方式是两操作数在同位置上的分量分别进行运算,其本质还是逐分量进行的,这和上面所说的 float 类型的逐分量运算可能有一点点差异,相同的是 vecvec 运算结果还是 vec,且阶数不变。

vec3 a = vec3(1.0, 2.0, 3.0);
vec3 b = vec3(0.1, 0.2, 0.3);
vec3 c = a + b; // = vec3(1.1, 2.2, 3.3);
vec3 d = a * b; // = vec3(0.1, 0.4, 0.9);

AB=[a1,1a1,2a2,1a2,2][b1,1b1,2b2,1b2,2]=[a1,1b1,1+a1,2b2,1a1,1b1,2+a1,2b2,2a2,1b1,1+a2,2b2,1a2,1b1,2+a2,2b2,2]

4、vec(向量) 与 mat(矩阵):

要保证操作数的阶数相同,且 vecmat 间只存在乘法运算。 它们的计算方式和线性代数中的矩阵乘法相同,不是逐分量运算

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.)

5、mat(矩阵) 与 mat(矩阵):

要保证操作数的阶数相同。

matmat 的运算中,除了乘法是线性代数中的矩阵乘法外,其余的运算仍为逐分量运算。简单说就是只有乘法是特殊的,其余都和 vecvec 运算类似。

mat2 a = mat2(1., 2.,  3., 4.);
mat2 b = mat2(10., 20.,  30., 40.);
mat2 c = a * b; // mat2(1.*10.+3.*20.,2.*10.+4.*20.,1.* 30.+3.*40.,2.* 30.+4.*40.);

mat2 d = a+b;// mat2(1.+10.,2.+20.,3.+30.,4.+40);

结构体#

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

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;

构造函数#

glsl中变量可以在声明的时候初始化,float pSize = 10.0 也可以先声明然后等需要的时候在进行赋值.

聚合类型对象如(向量,矩阵,数组,结构) 需要使用其构造函数来进行初始化. vec4 color = vec4(0.0, 1.0, 0.0, 1.0);

//一般类型
float pSize = 10.0;
float pSize1;
pSize1=10.0;
...

//复合类型
vec4 color = vec4(0.0, 1.0, 0.0, 1.0);
vec4 color1;
color1 =vec4(0.0, 1.0, 0.0, 1.0);
...

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

//数组
const float c[3] = float[3](5.0, 7.2, 1.1);

数组#

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

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));

数组类型内建了一个length()函数,可以返回数组的长度。

lightPositions.length() // 返回数组的长度

类型转换#

glsl可以使用构造函数进行显式类型转换,各值如下:

bool t= true;
bool f = false;

int a = int(t); //true转换为1或1.0
int a1 = int(f);//false转换为0或0.0

float b = float(t);
float b1 = float(f);

bool c = bool(0);//0或0.0转换为false
bool c1 = bool(1);//非0转换为true

bool d = bool(0.0);
bool d1 = bool(1.0);

glsl中,没有隐式类型转换,原则上glsl要求任何表达式左右两侧(l-value),(r-value)的类型必须一致 也就是说以下表达式都是错误的:

int a =2.0; //错误,r-value为float 而 lvalue 为int.
int a =1.0+2;
float a =2;
float a =2.0+1;
bool a = 0; 
vec3 a = vec3(1.0, 2.0, 3.0) * 2;

精度限定#

glsl在进行光栅化着色的时候,会产生大量的浮点数运算,这些运算可能是当前设备所不能承受的,所以glsl提供了3种浮点数精度,我们可以根据不同的设备来使用合适的精度.

在变量前面加上 highp mediump lowp 即可完成对该变量的精度声明.

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

我们一般在片元着色器(fragment shader)最开始的地方加上 precision mediump float; 便设定了默认的精度.这样所有没有显式表明精度的变量 都会按照设定好的默认精度来处理.

如何确定精度:

变量的精度首先是由精度限定符决定的,如果没有精度限定符,则要寻找其右侧表达式中,已经确定精度的变量,一旦找到,那么整个表达式都将在该精度下运行.如果找到多个, 则选择精度较高的那种,如果一个都找不到,则使用默认或更大的精度类型.

uniform highp float h1;
highp float h2 = 2.3 * 4.7; //运算过程和结果都 是高精度
mediump float m;
m = 3.7 * h1 * h2; //运算过程 是高精度
h2 = m * h1; //运算过程 是高精度
m = h2 – h1; //运算过程 是高精度
h2 = m + m; //运算过程和结果都 是中等精度
void f(highp float p); // 形参 p 是高精度
f(3.3); //传入的 3.3是高精度

invariant关键字:

由于shader在编译时会进行一些内部优化,可能会导致同样的运算在不同shader里结果不一定精确相等.这会引起一些问题,尤其是vertx shader向fragmeng shader传值的时候. 所以我们需要使用invariant 关键字来显式要求计算结果必须精确一致. 当然我们也可使用 #pragma STDGL invariant(all)来命令所有输出变量必须精确一致, 但这样会限制编译器优化程度,降低性能.

#pragma STDGL invariant(all) //所有输出变量为 invariant
invariant varying texCoord; //varying在传递数据的时候声明为invariant

限定符的顺序:

当需要用到多个限定符的时候要遵循以下顺序:

1.在一般变量中: invariant > storage > precision

2.在参数中: storage > parameter > precision

我们来举例说明:

invariant varying lowp float color; // invariant > storage > precision

void doubleSize(const in lowp float s){ //storage > parameter > precision
    float s1=s;
}

修饰符#

1、变量存储限定符

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

2、函数参数限定符

GLSL 允许自定义函数,但参数默认是以值形式(in 限定符)传入的,也就是说任何变量在传入时都会被拷贝一份,若想以引用方式传参,需要增加函数参数限定符。

限定符 描述
in 用在函数的参数中,表示这个参数是输入的,在函数中改变这个值,并不会影响对调用的函数产生副作用。(相当于C语言的传值),这个是函数参数默认的修饰符
out 用在函数的参数中,表示该参数是输出参数,值是会改变的。
inout 用在函数的参数,表示这个参数即是输入参数也是输出参数。

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

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

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

内置变量和函数#

内置变量#

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

顶点着色器可用的内置变量

名称 类型 描述
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提供了非常丰富的函数库,供我们使用,这些功能都是非常有用且会经常用到的. 这些函数按功能区分大改可以分成7类:

通用函数:

下文中的 类型 T可以是 float, vec2, vec3, vec4,且可以逐分量操作.

方法 说明
T abs(T x) 返回x的绝对值
T sign(T x) 比较x与0的值,大于,等于,小于 分别返回 1.0 ,0.0,-1.0
T floor(T x) 返回<=x的最大整数
T ceil(T x) 返回>=等于x的最小整数
T fract(T x) 获取x的小数部分
T mod(T x, T y) T mod(T x, float y) 取x,y的余数
T min(T x, T y) T min(T x, float y) 取x,y的最小值
T max(T x, T y) T max(T x, float y) 取x,y的最大值
T clamp(T x, T minVal, T maxVal) T clamp(T x, float minVal,float maxVal) min(max(x, minVal), maxVal),返回值被限定在 minVal,maxVal之间
T mix(T x, T y, T a) T mix(T x, T y, float a) 取x,y的线性混合,x(1-a)+ya
T step(T edge, T x) T step(float edge, T x) 如果 x<edge 返回 0.0 否则返回1.0
T smoothstep(T edge0, T edge1, T x) T smoothstep(float edge0,float edge1, T x) 如果x<edge0 返回 0.0 如果x>edge1返回1.0, 否则返回Hermite插值

角度&三角函数:

下文中的 类型 T可以是 float, vec2, vec3, vec4,且可以逐分量操作.

方法 说明
T radians(T degrees) 角度转弧度
T degrees(T radians) 弧度转角度
T sin(T angle) 正弦函数,角度是弧度
T cos(T angle) 余弦函数,角度是弧度
T tan(T angle) 正切函数,角度是弧度
T asin(T x) 反正弦函数,返回值是弧度
T acos(T x) 反余弦函数,返回值是弧度
T atan(T y, T x) T atan(T y_over_x) 反正切函数,返回值是弧度

指数函数:

下文中的 类型 T可以是 float, vec2, vec3, vec4,且可以逐分量操作.

方法 说明
T pow(T x, T y) 返回x的y次幂 xy
T exp(T x) 返回x的自然指数幂 ex
T log(T x) 返回x的自然对数 ln
T exp2(T x) 返回2的x次幂 2x
T log2(T x) 返回2为底的对数 log2
T sqrt(T x) 开根号 √x
T inversesqrt(T x) 先开根号,在取倒数,就是 1/√x

几何函数:

下文中的 类型 T可以是 float, vec2, vec3, vec4,且可以逐分量操作.

方法 说明
float length(T x) 返回矢量x的长度
float distance(T p0, T p1) 返回p0 p1两点的距离
float dot(T x, T y) 返回x y的点积
vec3 cross(vec3 x, vec3 y) 返回x y的叉积
T normalize(T x) 对x进行归一化,保持向量方向不变但长度变为1
T faceforward(T N, T I, T Nref) 根据 矢量 N 与Nref 调整法向量
T reflect(T I, T N) 返回 I - 2 * dot(N,I) * N, 结果是入射矢量 I 关于法向量N的 镜面反射矢量
T refract(T I, T N, float eta) 返回入射矢量I关于法向量N的折射矢量,折射率为eta

矩阵函数:

mat可以为任意类型矩阵.

方法 说明
mat matrixCompMult(mat x, mat y) 将矩阵 x 和 y的元素逐分量相乘

向量函数:

下文中的 类型 T可以是 vec2, vec3, vec4, 且可以逐分量操作.

bvec指的是由bool类型组成的一个向量:

vec3 v3= vec3(0.,0.,0.);
vec3 v3_1= vec3(1.,1.,1.);
bvec3 aa= lessThan(v3,v3_1); //bvec3(true,true,true)
方法 说明
bvec lessThan(T x, T y) 逐分量比较x < y,将结果写入bvec对应位置
bvec lessThanEqual(T x, T y) 逐分量比较 x <= y,将结果写入bvec对应位置
bvec greaterThan(T x, T y) 逐分量比较 x > y,将结果写入bvec对应位置
bvec greaterThanEqual(T x, T y) 逐分量比较 x >= y,将结果写入bvec对应位置
bvec equal(T x, T y) bvec equal(bvec x, bvec y) 逐分量比较 x == y,将结果写入bvec对应位置
bvec notEqual(T x, T y) bvec notEqual(bvec x, bvec y) 逐分量比较 x!= y,将结果写入bvec对应位置
bool any(bvec x) 如果x的任意一个分量是true,则结果为true
bool all(bvec x) 如果x的所有分量是true,则结果为true
bvec not(bvec x) bool矢量的逐分量取反

纹理查询函数:

图像纹理有两种 一种是平面2d纹理,另一种是盒纹理,针对不同的纹理类型有不同访问方法.

纹理查询的最终目的是从sampler中提取指定坐标的颜色信息. 函数中带有Cube字样的是指 需要传入盒状纹理. 带有Proj字样的是指带投影的版本.

以下函数只在vertex shader中可用:

vec4 texture2DLod(sampler2D sampler, vec2 coord, float lod);
vec4 texture2DProjLod(sampler2D sampler, vec3 coord, float lod);
vec4 texture2DProjLod(sampler2D sampler, vec4 coord, float lod);
vec4 textureCubeLod(samplerCube sampler, vec3 coord, float lod);

以下函数只在fragment shader中可用:

vec4 texture2D(sampler2D sampler, vec2 coord, float bias);
vec4 texture2DProj(sampler2D sampler, vec3 coord, float bias);
vec4 texture2DProj(sampler2D sampler, vec4 coord, float bias);
vec4 textureCube(samplerCube sampler, vec3 coord, float bias);

在 vertex shader 与 fragment shader 中都可用:

vec4 texture2D(sampler2D sampler, vec2 coord);
vec4 texture2DProj(sampler2D sampler, vec3 coord);
vec4 texture2DProj(sampler2D sampler, vec4 coord);
vec4 textureCube(samplerCube sampler, vec3 coord);

相关链接

GLSL-Card

https://www.khronos.org/registry/OpenGL/specs/gl/GLSLangSpec.1.20.pdf

https://www.khronos.org/registry/OpenGL-Refpages/gl4/

GLSL基础

着色语言 GLSL 语法介绍

1、作者吃代码的兔子窝的《初探 GLSL 着色器(一)》

[3、作者吃代码的兔子窝的《初探 GLSL 着色器(二)》](

3、作者吃代码的兔子窝的《初探 GLSL 着色器(二)》

posted @   straywriter  阅读(3266)  评论(0编辑  收藏  举报
编辑推荐:
· AI与.NET技术实操系列:基于图像分类模型对图像进行分类
· go语言实现终端里的倒计时
· 如何编写易于单元测试的代码
· 10年+ .NET Coder 心语,封装的思维:从隐藏、稳定开始理解其本质意义
· .NET Core 中如何实现缓存的预热?
阅读排行:
· 分享一个免费、快速、无限量使用的满血 DeepSeek R1 模型,支持深度思考和联网搜索!
· 基于 Docker 搭建 FRP 内网穿透开源项目(很简单哒)
· ollama系列01:轻松3步本地部署deepseek,普通电脑可用
· 25岁的心里话
· 按钮权限的设计及实现
点击右上角即可分享
微信分享提示
主题色彩