0%

OpenGL-着色器

OpenGL分为两个部分:CPU上C++编写,GPU采用GLSL语言编写,后者就是着色器的编写方式。

基本数据类型

类型 描述
float ieee 32 位浮点数
double ieee 64 位浮点数
int 有符号二进制补码的 32 位整数
uint 无符号的32位整数
bool 布尔值

变量初始化

所有变量都必须在声明的同时进行初始化

1
2
3
4
int i,nums = 1500;
float force,g = -9.8;
bool falling = true;
double pi = 3.145555LF;

构造函数

所有其他的数值转换都需要提供显示的转换构造函数

1
2
float f = 10.0;
int ten = int(f);

聚合类型

GLSL的向量与矩阵类型

基本数据类型 2D向量 3D向量 矩阵类型
float vec2 vec3 ..
double dvec2 dvec3 ..
int ivec2 ivec3 ..
uint uvec2 uvec3 -
bool bvec2 bvec3 -

类型声明的变量的初始化:

1
vec3 velocity = vec3(0.0, 2.0, 3.0);

类型之间的等价转换:

1
ivec3 steps = ivec3(velocity);

向量之间的转换:

1
2
3
4
5
6
7
8
# 减短
vec4 color;
vec3 RGB = vec3(color);//只取了color的前面三个分量R、G、B

# 加长
vec3 white = vec3(1.0);//white = (1.0, 1.0, 1.0)

vec4 translate = vec4(white, 0.5);

注意⚠️:传入的数据将首先填充列,再填充行。

访问向量和矩阵中的元素

向量中的各个分量可以通过名称访问:

1
2
float red = color.r;
float v_y = velocity.y;

或者通过一个从0开始的索引:

1
2
float red = color[0];
float v_y = velocity[1];

向量分量的访问符

分量访问符 符号描述
(X,Y,Z,W) 与位置相关的分量
(r,g,b,a) 与颜色相关的分量
(s,t,p,q) 与纹理坐标相关的分量
1
2
3
vec3 luminance = color.rrr; # 输入颜色的红色分量来设置一个亮度值

color = colot.abgr; # 反转color的每个分量

结构体

1
2
3
4
5
6
7
struct Partical {
float lifetime;
vec3 position;
vec3 velocity;
}

Partical p = Partical(1.0, pos, vel); # pos、 vel均为vec3类型

数组

数组的声明

1
2
3
float coffee[3];        #有三个float元素的数组
float[3] coffee; #同上
int indices; #为定义维数

静态初始化一个数组的值

1
float coffee[3]  = float[3](2.00, 3.00, 5.23);

操作一个数组中的所有值

1
2
3
for(int i = 0; i<coffee.length(); i++){
coffee[i] *= 2.0;
}

获取列数

1
2
3
mat3x4 m;
int c = m.length(); # m包含的列数为3
int r = m[0].length(); # 第0个列向量中分量的个数为4

存储限制符

类型修饰符 描述
const 将一个变量定义为只读形式。如果它初始化时用的是一个编译时变量,那么它本身也会成为编译时常量
in 设置这个变量为着色器阶段的输入变量
out 设置这个变量为着色器阶段的输出变量
uniform 设置这个变量为用户应用程序传递给着色器的数据,它对于给定的图元而言是一个常量
buffer 设置应用程序共享的一块可读写的内存。这块内存也作为着色器中的存储缓存使用
shared 设置变量是本地工作组中共享的。它只能用于计算着色器中

获取uniform变量的索引并且设置具体值

1
2
3
4
GLint timeLoc;                                      #着色器中的uniform变量time的索引
GLfloat timeValue; #程序运行的时间
timeLoc = glGetUniformLocation(program, "time") #返回着色器中nuiform变量time对应的索引值
glUniformlf(timeLoc, timeValue); #设置uniform变量timeLoc的值

算术操作符

优先级 操作符 可用类型 描述
1 () - 成组的操作
2
[]

f()

.

++、–

[数组、矩阵、向量]

函数

结构体

算术类型

数组的下标

函数的调用、构造函数

访问结构体的域变量/方法

后置递增/递减
3
++、–

+、-

~

!

算术类型

算术类型

整型

布尔型

前置递增/递减






4 *、/、%



5 +、-



6 <<、>>



7 <、>、<=、>=



8 ==、!=



9 &



10 ^



11 ` `

12 &&



13 ^^



14 ` `
15 a?b:c



16
==

+=、-=

*=、/=

%=、<<=、>>=

`&=、^=、
=`

任意

算术类型

算术类型

整型

整型
17 , 任意 操作符序列

操作符重载

流控制

语句 描述
break 终止循环体的运行,并且继续执行循环体外的内容
continue 终止循环体内当前迭代过程的执行,跳转到代码块开始部分并继续执行下一次迭代的内容
return 从当前子例程返回,可以待会一个函数返回值
discard 丢弃当前的片元,终止着色器的执行。discard语句只在片元着色器中有效

函数

1
2
3
4
5
float HornerEval(float coffee[10], float x);

float HornerEval(float coffee[10], float x){
...+
}

计算的不变性

GLSL无法保证在不同的着色器中,两个完全相同的计算式会得到完全一样的结果。为了解决这个问题,需要用到invariant或者precise关键字。

invariant

将一个内置的输出变量声明为invariant,也可以声明一个用户自定义的变量为invariant

1
2
invariant gl_Position;
invariant centroid out vec3 Color

在调试过程中,可能需要将着色器中的所有可变量都设置为invariant。可以通过顶点着色器的预编译命令pragma来完成:

1
#pragma STDGL invariant(all)

precise

precise限制符可以设置任何计算中的变量或者函数的返回值。它的作用是增加计算的可复用性

着色器的预处理器

预处理命令

控制常量与宏的定义

1
2
3
4
5
6
7
8
9
10
11
12
13
14
#define
#undef
```

代码的条件编译


```bash
#if
#ifdef
#ifndef
#else
#elif
#endif

强制比那一起将text文字内容插入到着色器的信息日志中

1
#error text

控制编译器的选项

1
#pragma options

设置编译器支持特定GLSL扩展功能

1
#extension options

设置当前使用的GLSL版本名称

1
#version number

设置诊断行号

1
#line options

宏定义

1
#define NUM_ELEMENTS 10

取消之前定义过的宏

1
#undef LPos

数据快接口

着色器与应用程序之间,或者着色器个阶段之间共享的变量可以组织为变量块的形式。

1
2
3
4
uniform b {     //限定符可以为:uniform、in、out、buffer
vec4 v1; //块中的变量列表
vec1 v2; //
}; //访问匿名块成员时使用v1、v2

或者

1
2
3
4
uniform b {     //
vec4 v1; //
vec1 v2; //
}name; ////访问匿名块成员时使用name.v1、name.v2

着色器的编译

创建着色器对象且通过链接生成可执行着色器程序的流程:(两部分)

(第一部分)对于每个着色器都会:

  • 创建一个着色器对象。
  • 将着色器源代码编译为对象。
  • 验证着色器的比那一是否成功。

(第二部分)将多个着色器对象链接为一个着色器程序:

  • 创建一个着色器程序。
  • 将着色器对象关联到着色器程序。
  • 链接着色器程序。
  • 判断着色器的链接过程是否成功完成。
  • 使用着色器来处理顶点和片元。

着色器子程序

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
//第一步
subroutine vec4 LightFunc(vec4);

//第二步
subroutine (LightFunc) vec4 ambient(vec3 n)
{
return Materials.ambient;
}

subroutine (LightFunc) vec4 diffuse(vec3 n)
{
return Materials.diffuse * max(dot (normalize(n), LightVec.xyz), 0.0);
}

//第三步
subroutine uniform LightFunc materialShader;