初次接触着色器
着色器是现代图形编程中不可或缺的工具,它们能够让我们精确控制图形渲染的每个细节。本文将介绍如何使用 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;
}
这段代码的核心是如何创建波浪效果:
uFrequency
:控制波浪的频率,可以分别调整 x 和 y 方向的频率。uTime
:随时间变化,使波浪动起来。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;
}
这里的关键点是:
- 使用
texture2D
函数采样纹理。 - 利用从顶点着色器传来的
vElevation
值调整颜色亮度,创造出波浪的明暗变化效果。
在 Three.js 中使用着色器
- 要在 Three.js 中使用自定义着色器,我们需要创建一个
RawShaderMaterial
: - 使用了
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()