前端技术, 开发

Shader 学习笔记

在 Three.js 中提供了两种类型的 Shader 对象

  • ShaderMaterial 将自动添加一些基础代码到着色器中
  • RawShaderMaterial 不任何基础代码

着色器中同样也可以使用材质对象中的属性,如 wirefreme, side, transparent, flagShading 等。但 map, alphaMap, opacity, color 等属性将失效。

基础用法

const material = new THREE.RawShaderMaterial({
  vertexShader: `
    uniform mat4 projectionMatrix;
    uniform mat4 viewMatrix;
    uniform mat4 modelMatrix;

    attribute vec3 position;

    void main()
    {
      gl_Position = projectionMatrix * viewMatrix * modelMatrix * vec4(position, 1.0);
    }
  `,
  fragmentShader: `
    precision mediump float;
    void main()
    {
      gl_FragColor = vec4(1.0, 0.0, 0.0, 1.0);
    }
  `,
});

GLSL 语法

1、不能使用 console.log 进行打印

2、每句代码的末尾必须使用分号断开,一行可以写多句代码,但没句代码结束都需要分号断开。uniform mat4 viewMatrix; uniform mat4 modelMatrix;

3、变量声明必须提供类型

float fooBar = 4.0;

4、浮点数不能与整数一起运算,如需要运算则需将数据类型进行转换

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

数据类型

float 浮点数

float foo = 1.0;

int 整数

float a = 1;
float b = 2;

bool 布尔值

float foo = true;

vec2 二维向量

// X, Y 均为 1.0
vec2 foo = vec2(1.0);

// X 为 -1.0, Y 为 2.0
vec2 foo = vec2(- 1.0, 2.0);
// 运算,x与y相乘再乘以 2.0
foo *= 2.0 //(结果为4.0)
// 重新赋值
foo.x = 2.0
foo.y = 3.0

vec3 三位向量

可以是 X, Y, Z 或者 R, G, B

// X, Y, Z 均为 1.0
vec3 foo = vec3(1.0);

// X 为 -1.0, Y 为 2.0,Z 为 3.0
vec3 foo = vec3(- 1.0, 2.0, 3.0);

// 通过二维向量数据设置三位向量
vec2 foo = vec2(1.0, 2.0);
vec3 bar = vec3(foo, 3.0);

// 通过三位向量数据设置二维向量
vec3 foo = vec3(1.0, 2.0, 3.0);
vec2 a = foo.xy;
vec2 b = foo.xz;
vec2 c = foo.zy;

vec4 四维向量

分别为 X, Y, Z, W 或者 R, G, B, A

vec4 foo = vec4(1.0, 2.0, 3.0, 4.0);

mat2 二维矩阵

mat3 三维矩阵

mat4 四维矩阵

sampler2D

2D采样器,用于纹理等。

运算

支持使用 + – * /,但不支持 %,可用内置函数 mod 代替

函数

// 有返回值
float name(float a, float b) {
  return a * b;
}
float result = name(1.0, 2.0);

// 没有返回值,只是一个方法
void name() {
  ...
}

内置函数

sin, cos, max, min, pow, exp, mod, clamp, cross, dot, mix, step, smoothstep, length, distance, reflect, refract, normalize

mod()

取模

float foo = mod(vUv.y * 10.0, 1.0);

step()

float foo = step(0.5, mod(vUv.y * 10.0, 1.0)); // 大于0.5时为1,否则为0

abs()

绝对值

float foo = abs(vUv.x - 0.5);

min()

最小值

float foo = min(abs(vUv.x - 0.5), abs(vUv.y - 0.5));

max()

最大值

float foo = max(abs(vUv.x - 0.5), abs(vUv.y - 0.5));

round()

四舍五入

floor()

length()

可用于绘制径向渐变

float strength = length(vUv);

distance()

float strength = distance(vUv, vec2(0.5));

atan()

mix()

用于混合颜色

float strength = step(0.9, sin(cnoise(vUv * 10.0) * 20.0));
vec3 blackColor = vec3(0.0);
vec3 uvColor = vec3(vUV, 1.0);
vec3 mixedColor = mix(blackColor, uvColor, strength);

clamp()

float strength = clamp(strength, 0.0, 1.0);

自定义函数

创建自定义函数需在 main 函数前创建。

random()

仅是一个伪随机

float random(vec2 st)
{
  return fract(sin(dot(st.xy, vec2(12.9898, 78.233))) * 43758.5453123);
}
// main 内调用
float foo= random(vUv);

rotate()

用于旋转uv

vec2 rotate(vec2 uv, float rotation, vec2 mid)
{
  return vec2(
    cos(rotation) * (uv.x - mid.x) + sin(rotation) * (uv.y - mid.y) + mid.x,
    cos(rotation) * (uv.y - mid.y) - sin(rotation) * (uv.x - mid.x) + mid.y
  );
}

vec2 rotatedUv = rotate(vUv, 1.0, vec(0.5));

相关网站

Shaderific

Kronos Group registery

Book of Shaders glossary

噪波

https://gist.github.com/patriciogonzalezvivo/670c22f3966e662d2f83

Classic Perlin Noise

void main

该函数将自动调用,不会返回任何值

顶点着色器 vertexShader

gl_Position

一个包含顶点在屏幕上位置的变量,该变量已经存在,无需重新声明,是一个四维向量,他将顶点定位在屏幕上的正确位置。

位置属性 Postion Attribute

每个顶点之间变化的数据,是一个三维向量。该属性来自几何图形中,值由 javascript 提供。

attribute vec3 postion;

modelMatrix

模型矩阵,将相对于网格位置、旋转和缩放对每个顶点应用变换。

viewMatrix

关于相机的,

projectionMatrix

投影矩阵会将坐标转换为最终的剪辑空间坐标

uniform mat4 projectionMatrix;
uniform mat4 viewMatrix;
uniform mat4 modelMatrix;

attribute vec3 position;

void main()
{
  vec4 modelPosition = modelMatrix * vec4(position, 1.0);
  modelPosition.z += sin(modelPosition.x * 10.0) * 0.1;
  vec4 viewPostion = viewMatrix * modelPosition;
  vec4 projectedPosition = projectionMatrix * viewPostion;
  gl_Position = projectedPosition;
}

varying

通过顶点着色器声明变量并赋值,并传递给片段着色器,再在片段着色器中使用该变量

const vertexShader = 
uniform mat4 projectionMatrix;
uniform mat4 viewMatrix;
uniform mat4 modelMatrix;

attribute vec3 position;
attribute float aRandom;

// 需要传递到片段着色器的变量
varying float vRandom;

void main()
{
  vec4 modelPosition = modelMatrix * vec4(position, 1.0);
  modelPosition.z += aRandom * 0.1;
  vec4 viewPostion = viewMatrix * modelPosition;
  vec4 projectedPosition = projectionMatrix * viewPostion;
  gl_Position = projectedPosition;

  vRandom = aRandom;
}
`;
const fragmentShader = `
precision mediump float;

// 从顶点着色器获取变量
varying float vRandom;

void main()
{
  gl_FragColor = vec4(0.5, vRandom, 1.0, 1.0);
}
`;

const geometry = new THREE.PlaneGeometry(1, 1, 32, 32);
const { count } = geometry.attributes.position;
// 添加自定义变量
const randoms = new Float32Array(count);
for (let i = 0; i < count; i++) {
  randoms[i] = Math.random();
}
geometry.setAttribute('aRandom', new THREE.BufferAttribute(randoms, 1));

const material = new THREE.RawShaderMaterial({
  vertexShader,
  fragmentShader,
});
const mesh = new THREE.Mesh(geometry, material);
scene.add(mesh);

uniform

可在顶点着色器与片段着色器中使用。

  • 使用同一个着色器,但产生不同的结果
  • 可调整这些值
  • 可制作动画
  • 给uniforms赋值必须使用对象,且为value
const vertexShader = 
uniform mat4 projectionMatrix;
uniform mat4 viewMatrix;
uniform mat4 modelMatrix;
uniform float uFrequency;

attribute vec3 position;

void main()
{
  vec4 modelPosition = modelMatrix * vec4(position, 1.0);
  modelPosition.z += sin(modelPosition.x * uFrequency) * 0.1;
  vec4 viewPostion = viewMatrix * modelPosition;
  vec4 projectedPosition = projectionMatrix * viewPostion;
  gl_Position = projectedPosition;
}
;
const fragmentShader = `
precision mediump float;

uniform vec3 uColor;

void main()
{
  gl_FragColor = vec4(uColor, 1.0);
}
`;

const material = new THREE.RawShaderMaterial({
    vertexShader,
    fragmentShader,
    uniforms: {
      uFrequency: { value: 10 },
      uColor: { value: new THREE.Color('orange') }
    },
  });

片段着色器 fragmentShader

gl_FragColor

precision

浮点数的精度,包含 highp, mediump, lowp。精度越高越影响性能,但细节更丰富。

precision mediump float;
void main()
{
  gl_FragColor = vec4(1.0, 0.0, 0.0, 1.0);
}

ShaderMaterial

主要属性

  • uniforms 全局变量
  • vertexShader 顶点着色器,定义位置信息
  • fragmentShader 片段着色器,定义颜色

shader 着色器,是一段 glsl (opengl shading language)语言,在gpu上运行,性能比cpu更好。

用处:

  • 现有的material无法满足要求
  • 绑定多个对象到一个 buffergeometry,已提高性能

注意点

  • 只能使用 webglrenderer, vertexshader 和 fragmentshader 代码只能使用 webgl 编译在gpu 上运行
  • 必须配套使用 buffergeometry,并且使用bufferattribute定义attributes
  • 可以使用 texture property

vertex shader 与 fragmentshader

  • vertex shader 首先运行,接受 attributes,计算每个顶点的位置,并把其他数据varying传递到fragment shader
  • fragment shader 接着运行,设置每个片段的颜色

着色器有三种类型的变量

  • uniforms 所有顶点相同值的变量,能被两种着色器访问
  • attributes 与每个顶点关联的变量,如顶点位置
  • varying 从vertex shader 传递到 fragment shader 的变量
内容目录

PUJI Design 朴及设计 (c) 2024. 沪ICP备17052229号