GLSL 着色器语言入门教程(珍藏版)

GLSL(OpenGL Shading Language)是一种用于 OpenGL 图形 API 的着色语言,专门用于编写顶点着色器和片段着色器(像素着色器)的代码,GLSL 采用了类似于 C 语言的语法结构,使得熟悉 C 语言的程序员能够较快地上手。

GLSL 基本类型

GLSL 着色器语言入门教程(珍藏版)

标量

构造标量的方式和 C 语言一致:

float floatValue = 1.0;
bool booleanValue = false;

向量

构造向量时的规则如下:

  • 若向向量构造器提供了一个标量,则向量的所有值都会设定为该标量值。
  • 若提供多个标量值或向量,则从左到右使用提供的值赋值。前提是标量或向量的数量之和要等于向量构造器的数量。
vec4 myVec4 = vec4(1.0);              // myVec4 = {1.0, 1.0, 1.0, 1.0}
vec2 myVec2 = vec2(0.5, 0.5);         // myVec2 = {0.5, 0.5}
vec4 newVec4 = vec4(1.0, 1.0, myVec2);// newVec4 = {1.0, 1.0, 0.5, 0.5}

向量可以通过 rgba、stpq 或 xyzw 进行访问,向量可能代表了一个空间坐标(x,y,z,w),或者代表了一个颜色(r,g,b,a),再或者代表一个纹理坐标(s,t,p,q) 。

可以同时访问多个角标,非常灵活:

vec4 myVec4_0 = vec4(1.0); // myVec4_0 = { 1.0, 1.0, 1.0, 1.0 }
vec4 myVec4 = vec4(1.0, 2.0, 3.0, 4.0); // myVec4 = { 1.0, 2.0, 3.0, 4.0 }
float x = myVec4.x;       // x = 1.0;
vec3 myVec3_0 = myVec4.xyz; // myVec3_0 = { 1.0, 2.0, 3.0 }
vec3 myVec3_1 = myVec4.rgb; // myVec3_1 = { 1.0, 2.0, 3.0 }
vec3 myVec3_2 = myVec4.zyx; // myVec3_2 = { 3.0, 2.0, 1.0 }
vec3 myVec3_3 = myVec4.xxx; // myVec3_3 = { 1.0, 1.0, 1.0 }

矩阵

在 GLSL 内可构造 mat[2..4] 来表示 2 阶到 4 阶的矩阵。

矩阵构造有如下的规则:

  • 若只为矩阵构造器提供了一个标量,则该值会构造矩阵对角线上的值
  • 矩阵可以由多个向量构造
  • 矩阵可以由单个标量从左到右进行构造
mat4 marixt4x4 = mat4(1.0); // marixt4x4 = { 1.0, 0.0, 0.0, 0.0, 
//                0.0, 1.0, 0.0, 0.0 
//                0.0, 0.0, 1.0, 0.0                    
//                0.0, 0.0, 0.0, 1.0 }

vec2 col1 = vec2(1.0, 0.0);
vec2 col2 = vec2(1.0, 0.0);

mat2 matrix2x2 = mat2(coll1, col2);

// GLSL 是列矩阵存储,因此构造时,构造器会按照列顺序进行填充
mat3 matrix3x3 = mat3(0.0, 0.0, 0.0,   // 第一列
                      0.0, 0.0, 0.0,   // 第二列
                      0.0, 0.0, 0.0);  // 第三列

矩阵可以通过索引访问不同的列:

mat2 matrix2x2 = mat2(0.0, 0.0, 0.0, 0.0);
vec4 myVec4 = vec4(matrix2x2[0], matrix2x2[1]);
vec2 myVec2 = matrix2x2[0];

// 访问第一列的第一个元素
float value = matrix2x2[0][0];
matrix2x2[1][1] = 2.0;

结构体

结构体的形成和 C 语言类似,可由不同数据类型聚合而成:

struct myStruct
{
    vec4 position;
    vec4 color;
    vec2 uv;
};

构造结构体的代码示例如下:

myStruct structVar = myStruct(vec4(0.0, 0.0,0.0,0.0), vec4(1.0, 1.0, 1.0, 1.0), vec2(0.5, 0.5));

数组

数组的用法和 C 语言类似,规则如下:

  • 数组必须声明长度
  • 数组不能在声明的同时初始化
  • 数组必须由常量表达式初始化
  • 数组不能用 const 修饰
  • 不支持多维数组

数组声明和初始化的代码示例如下:

float array[4];
for(int i =0; i < 4; i ++)
{
    array[i] = 0.0;
}

基础类型运算

glsl 没有隐式类型转换。

int a = 2.0; //错误
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;//错误

下面来分别说说可能遇到的情况:

1.float 与 int:

float 与 float , int 与int 之间是可以直接运算的, 但 float 与 int 不行,它们需要进行一次显示转换。

以下表达式都是正确的:

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进行运算, 这就是所谓的逐分量运算。

下面枚举了几种 float 与 vec,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*vec2  vec4+vec3 等都是不行的。

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)
GLSL 着色器语言入门教程(珍藏版)

4. 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 着色器语言入门教程(珍藏版)

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

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

在mat与mat的运算中, 除了乘法是线性代数中的矩阵乘法外, 其余的运算任为逐分量运算。

简单说就是只有乘法是特殊的,其余都和vec与vec运算类似。

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

矩阵乘法规则如下:

GLSL 着色器语言入门教程(珍藏版)

限定符

精度限定符

GLSL 引入了精度限定符,用于指定整型或浮点型变量的精度。

在 Shader 头部声明的精度应用于整个 Shader,是所有基于浮点型的变量的默认精度,同时也可以定义单个变量的精度。

在 Shader 中如果没有指定默认精度,则所有的整型和浮点型变量都采用高精度计算。

GLSL 支持的精度限定符包括以下几种:

GLSL 着色器语言入门教程(珍藏版)

代码示例如下:

highp mat4 cc_matWorld;
mediump vec2 dir;
lowp vec4 cc_shadowColor;

存储限定符

存储限定符用于描述变量在管线中的作用。

GLSL 着色器语言入门教程(珍藏版)

uniform

在一个渲染过程内声明的 uniform 不能重复。

例如在顶点着色器中定义了变量 variableA,variableA 也会存在于片段着色器且值相同,那么也就是 variableA 不能在片元着色器中再次定义。

varying

varying 是由顶点着色器输出并传输给片元着色器的变量。

在管线的作用下,变量值并不会和顶点着色器输出的保持一致,而是由管线进行插值,

参数限定符

GLSL 中函数的参数限定符包括以下几种:

GLSL 着色器语言入门教程(珍藏版)

限定符举例:

void scaleSize(inout vec2 size, in float scale){
    size = size * scale;
}

void scaleSize(out vec2 oSize, vec2 iSize, float scale){
    oSize = iSize * scale;
}

vec2 scaleSize(vec2 iSize, float scale){
    return iSize * scale;
}

内置变量

glsl 程序使用一些特殊的内置变量与硬件进行沟通。

他们大致分成两种 一种是 input类型,他负责向硬件(渲染管线)发送数据. 另一种是output类型,负责向程序回传数据以便编程时需要。

在 vertex Shader 中:

output 类型的内置变量:

GLSL 着色器语言入门教程(珍藏版)

在 fragment Shader 中:

input 类型的内置变量:

GLSL 着色器语言入门教程(珍藏版)

output 类型的内置变量:

GLSL 着色器语言入门教程(珍藏版)

内置函数

GLSL 提供了非常丰富的函数库供我们使用。

通用函数:

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

GLSL 着色器语言入门教程(珍藏版)

角度&三角函数:

GLSL 着色器语言入门教程(珍藏版)

指数函数:

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

GLSL 着色器语言入门教程(珍藏版)

几何函数:

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

GLSL 着色器语言入门教程(珍藏版)

进技术交流群,扫码添加我的微信:Byte-Flow

字节流动

本文来自作者投稿,版权归原作者所有。如需转载,请注明出处:https://www.nxrte.com/jishu/52894.html

(0)

相关推荐

发表回复

登录后才能评论