GLSL入门

 

OpenGL

OpenGL(Open Graphics Library)是一个跨平台的图形渲染API,用于2D/3D图形渲染。它提供了一系列函数,允许开发者操作GPU进行高效绘图,广泛应用于游戏、CAD、可视化等领域。

GLSL

GLSL(OpenGL Shading Language)是OpenGL的着色器编程语言,用于编写在GPU上运行的着色器程序。它类似于C语言,专门用于处理图形渲染中的光照、颜色、纹理等效果。

饥荒中的GLSL

文件路径:data/databundles/shaders.zip

后缀名:.ksh(klei shader)

加载类型:SHADER

Asset("SHADER", "shaders/overheat.ksh")

在线编辑器

基于WebGL,可直接在浏览器内运行。

https://thebookofshaders.com/edit.php

第一个GLSL程序


          precision mediump float;

          void main() {
              vec4 color = vec4(1.0, 0.0, 0.0, 1.0);
              gl_FragColor = color;
          }
        

设置浮点数精度,可选:lowp, mediump, highp

main函数

变量声明和赋值

通过gl_FragColor设置颜色

基本类型

类型 描述 示例
int 整数 int a = 1;
float 浮点数 float b = 1.0;
bool 布尔值 bool c = true;
vec2 二维向量 vec2 d = vec2(1.0, 2.0);
vec3 三维向量 vec3 e = vec3(1.0, 2.0, 3.0);
vec4 四维向量 vec4 f = vec4(1.0, 2.0, 3.0, 1.0);

基本类型

类型 描述 示例
mat2 2×2矩阵 mat2 g = mat2(1.0, 2.0, 3.0, 4.0);
mat3 3×3矩阵 mat3 h = mat3(1.0, 2.0, 3.0, 4.0, 5.0, 6.0, 7.0, 8.0, 9.0);
mat4 4×4矩阵 mat4 i = mat4(1.0, 2.0, 3.0, 4.0, ..., 13.0, 14.0, 15.0, 16.0);

变量初始化


          int number = 10;
          float alpha = 0.5;
          bool is_red = true;

          vec3 red = vec3(1.0, 0.0, 0.0);
          vec3 black = vec3(0.0); // 等价于vec3(0.0, 0.0, 0.0)
          vec4 color = vec4(red, alpha);

          float r = color[0]; // color.r, color.x
          float g = color[1]; // color.g, color.y
          float b = color[2]; // color.b, color.z
          float a = color[3]; // color.a, color.w
          vec3 rgb = color.rgb; // color.xyz
          vec3 bgr = color.bgr; // color.zyx
        

矩阵操作


          mat4 m = mat4(
              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
          );

          m[0] = vec4(1.0, 0.0, 0.0, 0.0); // 第一行
          m[0][0] = 0.5; // 第一行第一列

          mat4 m2 = mat4(
              m[0],
              m[1],
              vec4(0.0, 0.0, 0.0, 1.0),
              vec4(0.0, 0.0, 0.0, 1.0)
          );
        

基本类型(不常用)

类型 描述 示例
bvec2, bvec3, bvec4 布尔向量 bvec2 a = bvec2(true, false);
ivec2, ivec3, ivec4 整数向量 ivec3 b = ivec3(1, 2, 3);

纹理类型

  • Sampler1D
  • Sampler2D
  • Sampler3D

数组


          float array[3];

          void main() {
              array[0] = 1.0;
              array[1] = 2.0;
              array[2] = 3.0;

              gl_FragColor = vec4(array[0], 0.0, 0.0, 1.0);
          }
        

数组成员只能在函数内部赋值。

结构体


          struct positionInfo {
              vec2 coord;
              float value;
          };
        

基本用不到。

类型转换


          float a = 1.0;
          int b = int(a);
          float c = float(b);
        

注释

单行注释://

多行注释:/* */

uniform修饰符

一致变量,在所有线程中保持一致 + 只读。

由CPU传给GPU,控制着色器的行为。


          uniform float time;
          uniform vec2 resolution;
          uniform sampler2D texture;
        

uniform变量

在线编辑器里可以使用的uniform变量:


          uniform float u_time;     // 时间(加载后的秒数)
          uniform vec2 u_resolution; // 画布尺寸(宽,高)1000x1000
          uniform vec2 u_mouse;      // 鼠标位置(在屏幕上哪个像素)
        

注意:这些变量的名字是人为指定的,在不同的平台不一致。

获取像素坐标

vec4 gl_FragCoord: 当前像素的坐标。

vec4(x, y, 0.0, 1.0)

标准坐标:vec2 gl_FragCoord.xy / resolution.xy (范围0.0–1.0)

例子1:十字光标


          precision mediump float;

          uniform vec2 u_mouse;

          void main() {
              vec4 color = vec4(1.0);
              gl_FragColor = color;
          }
        

逻辑运算符

&& 与操作

|| 或操作

! 取反

优先级:! > && > ||

例子2:风车


          precision mediump float;

          uniform vec2 u_mouse;
          uniform vec2 u_resolution;

          void main() {
              vec2 st = gl_FragCoord.xy / u_resolution;
              vec4 color = vec4(0.0);
              
              gl_FragColor = color;
          }
        

if语句


          if (条件1) {
              // 代码1
          } else if (条件2) {
              // 代码2
          } else {
              // 代码3
          }

          // 如果语句只有一个,可以省略大括号
          if (st.x < .5 && st.y < .5)
              color = vec4(1.0, 0.0, 0.0, 1.0);
          else
              color = vec4(0.0, 1.0, 0.0, 1.0);
              aaa = bbb; // 这句无论如何都会执行
        

GLSL内建函数

自带的一些数学计算函数,有硬件加速,性能高。

abs(x) 绝对值
sign(x) 符号,返回-1.0, 0.0, 1.0
min(x, y) 最小值
max(x, y) 最大值
clamp(x, minValue, maxValue) 限制范围,等价于min(max(x, minValue), maxValue)
floor(x) 向下取整
ceil(x) 向上取整
fract(x) 小数部分,等价于x - floor(x),总是返回非负值
mod(x, y) 取模,返回余数

GLSL内建函数

sin(a) 正弦
cos(a) 余弦
tan(a) 正切
asin(x) 反正弦
acos(x) 反余弦
atan(y, x) 或者 atan(y_over_x) 反正切

GLSL内建函数

step(a, x) 阶梯函数,判断x是否比a大,返回1.0或0.0
smoothstep(a, b, x) 平滑阶梯函数,返回0.0到1.0之间的值

step和smoothstep可以检测是否跨越了某个值。


          precision mediump float;

          uniform vec2 u_resolution;
          
          void main() {
              vec2 st = gl_FragCoord.xy / u_resolution;
              float y = st.x * st.x;
              vec3 color = vec3(step(st.y, y));
              
              gl_FragColor = vec4(color, 1.0);
          }
        

GLSL内建函数

部分数学计算函数可以用于vec2、vec3和vec4。


          vec2 a = vec2(1.5, 2.5);
          vec2 b = fract(a); // vec2(0.5, 0.5)

          vec3 c = vec3(0.0, -1.0, 2.0);
          vec3 d = sign(c); // vec3(0.0, -1.0, 1.0)

          vec2 e = step(
              vec2(0.5, 0.5),
              vec2(1.0, 0.0)
          );
          // vec2(1.0, 0.0)
        

画一个风车

但是,不要使用if语句,<,>x,&&和||

提示1:首先计算像素和中心点的角度

提示2:注意图形的周期性,有什么函数可以处理周期?


          precision mediump float;

          uniform vec2 u_resolution;
          uniform float u_time;

          void main() {
              vec2 st = gl_FragCoord.xy / u_resolution;
              vec2 offset = st - vec2(0.5);
              float angle = atan(offset.y, offset.x) * 180.0 / 3.1416;
              float color = step(mod(angle, 90.0), 45.0);
              
              gl_FragColor = vec4(color);
          }
        

自定义函数

返回值类型 函数名(参数) { 函数体 }


          precision mediump float;

          uniform vec2 u_resolution;

          float rect(vec2 st, vec2 center, float size) {
              float v = size * 0.5;
              vec2 offset = st - center;
              return step(abs(offset.x), v) * step(abs(offset.y), v);
          }

          float circle(vec2 st, vec2 center, float radius) {
              return step(length(st - center), radius);
          }

          void main() {
              vec2 st = gl_FragCoord.xy / u_resolution;
              float color = 0.0;
              color = max(color, rect(st, vec2(0.5, 0.5), 0.1));
              color = max(color, rect(st, vec2(0.2, 0.7), 0.3));
              color = max(color, circle(st, vec2(0.1, 0.1), 0.1));
              color = max(color, circle(st, vec2(0.8, 0.1), 0.2));
              gl_FragColor = vec4(color);
          }
        

void 空类型


          precision mediump float;

          float data[5];

          void setup() {
              data[0] = 1.0;
              data[1] = 2.0;
              data[2] = 3.0;
              data[3] = 4.0;
              data[4] = 5.0;
          }

          void main() {
              setup();
              /* ... */
          }
        

坐标变换

有时候,我们需要对已绘制的图形进行移动/缩放/旋转...

试试直接修改st,会发生什么?


          precision mediump float;

          uniform vec2 u_resolution;
          uniform float u_time;

          float test(vec2 st) {
              vec2 v = step(vec2(0.01), mod(st, 0.33));
              float rect = step(0.0, st.x) * step(st.x, 0.33) * step(0.0, st.y) * step(st.y, 0.33);    
              return v[0] * v[1] - rect * 0.5;
          }

          void main() {
              vec2 st = gl_FragCoord.xy / u_resolution;
              gl_FragColor = vec4(test(st));
          }