跳到主要内容

初次接触着色器

着色器是现代图形编程中不可或缺的工具,它们能够让我们精确控制图形渲染的每个细节。本文将介绍如何使用 Three.js 和 GLSL 创建一个波浪效果。

顶点着色器

顶点着色器负责处理每个顶点的位置和属性。让我们来看看关键部分:

./shaders/test/vertex.glsl
uniform mat4 projectionMatrix;
uniform mat4 viewMatrix;
uniform mat4 modelMatrix;

uniform vec2 uFrequency;
uniform float uTime;

attribute vec3 position;
attribute vec2 uv;

varying float vRandom;
varying vec2 vUv;
varying float vElevation;

void main() {
vec4 modelPosition = modelMatrix * vec4(position, 1.0);

float elevation = sin(modelPosition.x * uFrequency.x + uTime) * 0.1;
elevation += sin(modelPosition.y * uFrequency.y + uTime) * 0.1;
modelPosition.z += elevation;

vec4 viewPosition = viewMatrix * modelPosition;
vec4 projectedPosition = projectionMatrix * viewPosition;

gl_Position = projectedPosition;
vUv = uv;
vElevation = elevation;
}

这段代码的核心是如何创建波浪效果:

  1. uFrequency:控制波浪的频率,可以分别调整 x 和 y 方向的频率。
  2. uTime:随时间变化,使波浪动起来。
  3. sin 函数:用于生成周期性的波动,其值域为 [-1, 1],非常适合创建平滑的波浪。

通过调整 sin 函数的输入(频率、振幅、相位),我们可以精确控制波浪的形状和运动。

片段着色器

片段着色器负责为每个像素着色。我们的片段着色器如下:

./shaders/test/fragment.glsl
precision mediump float;
uniform vec3 uColor;
uniform sampler2D uTexture;
varying vec2 vUv;
varying float vElevation;

void main() {
vec4 textureColor = texture2D(uTexture, vUv);
textureColor.rgb *= vElevation * 2.0 + 0.9;
gl_FragColor = textureColor;
}

这里的关键点是:

  1. 使用texture2D函数采样纹理。
  2. 利用从顶点着色器传来的vElevation值调整颜色亮度,创造出波浪的明暗变化效果。

在 Three.js 中使用着色器

  1. 要在 Three.js 中使用自定义着色器,我们需要创建一个RawShaderMaterial
  2. 使用了vite-plugin-glsl来引入 着色器代码glsl 文件,
import testVertexShader from './shaders/test/vertex.glsl'
import testfragmentShader from './shaders/test/fragment.glsl'

const material = new THREE.RawShaderMaterial({
vertexShader: testVertexShader,
fragmentShader: testfragmentShader,
uniforms: {
uFrequency: {
value: new THREE.Vector2(4, 0)
},
uTime: {
value: 0
},
uColor: { value: new THREE.Color("#ff2318") },
uTexture: { value: flagTexture }
}
})

为了使波浪动起来,我们需要在每一帧更新 uTime uniform:

const clock = new THREE.Clock()

const tick = () => {
const elapsedTime = clock.getElapsedTime()
material.uniforms.uTime.value = elapsedTime;
// ...
}

tick()