Uniform
在 Three.js 中用于在自定义着色器中传递全局变量。通过 Uniform
,我们可以将 JavaScript 层的数据传递给 WebGL 着色器(Shader),并在着色器程序的运行过程中保持这些数据的一致性。在复杂的三维场景渲染中,Uniform
对于处理材质效果、动画、光照和交互等操作非常关键。
本文将详细介绍 Uniform
的使用,包括属性、方法、示例代码,以及如何结合其他组件使用 Uniform
,帮助你掌握在 Three.js 中使用 Uniform
的技巧。
Uniform
Uniform
是 WebGL 中用于在顶点和片段着色器中传递一致数据的变量。与 attribute
和 varying
不同,Uniform
是全局变量,每次渲染时,它们的值对所有顶点或片段都保持一致。这意味着 Uniform
的值是每一帧在 JavaScript 层面更新后传递到 GPU 的,在着色器中,它们不会随着顶点或片段的变化而变化。
在 Three.js 中,Uniform
可以用于控制诸如光照强度、材质属性、时间变量等。
Uniform
的基本使用Three.js 提供了一个 Uniform
类,并且自定义着色器材质(如 ShaderMaterial
)中的 uniforms
属性就是通过它来定义的。以下是定义和使用 Uniform
的基本步骤。
当我们使用 ShaderMaterial
时,可以在 uniforms
对象中定义所有需要传递给着色器的全局变量。每个 uniform
都需要指定类型和值。
const uniforms = {
time: { value: 1.0 },
resolution: { value: new THREE.Vector2() },
color: { value: new THREE.Color(0xff0000) }
};
time
:这是一个浮点数类型的 uniform
,它的值随着时间变化。resolution
:这是一个 Vector2
类型的 uniform
,表示屏幕的分辨率。color
:这是一个 Color
类型的 uniform
,表示一个颜色值。ShaderMaterial
中使用 uniforms
在创建 ShaderMaterial
时,我们可以将 uniforms
传递给它,并在顶点和片段着色器中使用这些 uniform
变量。
const material = new THREE.ShaderMaterial({
uniforms: uniforms,
vertexShader: vertexShaderCode,
fragmentShader: fragmentShaderCode
});
vertexShaderCode
和 fragmentShaderCode
分别表示顶点着色器和片段着色器的代码。
Uniform
传递数据在 Three.js 的渲染循环中,我们可以通过修改 uniforms
对象中的值来控制着色器的行为。例如,我们可以根据时间动态更新 time
uniform。
function animate() {
requestAnimationFrame(animate);
// 动态更新 time uniform
uniforms.time.value += 0.05;
renderer.render(scene, camera);
}
animate();
在每一帧中,uniforms.time.value
的值都会增加,这样我们就可以在着色器中根据时间来控制动画效果。
Uniform
的数据类型在 WebGL 中,Uniform
支持多种数据类型,每种类型对应着不同的值结构。以下是 Three.js 中常用的 Uniform
数据类型及其对应的 JavaScript 类型。
float
:对应 THREE.Uniform({ value: 0.0 })
,表示一个浮点数。vec2
:对应 THREE.Uniform({ value: new THREE.Vector2(x, y) })
,表示一个二维向量。vec3
:对应 THREE.Uniform({ value: new THREE.Vector3(x, y, z) })
,表示一个三维向量。vec4
:对应 THREE.Uniform({ value: new THREE.Vector4(x, y, z, w) })
,表示一个四维向量。mat3
:对应 THREE.Uniform({ value: new THREE.Matrix3() })
,表示一个 3x3 矩阵。mat4
:对应 THREE.Uniform({ value: new THREE.Matrix4() })
,表示一个 4x4 矩阵。sampler2D
:对应 THREE.Uniform({ value: texture })
,表示一个二维纹理。const uniforms = {
time: { value: 0.0 },
color: { value: new THREE.Color(0x00ff00) },
texture: { value: new THREE.TextureLoader().load('path/to/texture.jpg') }
};
在这个例子中,我们定义了三个 uniforms
:一个时间变量 time
、一个颜色变量 color
和一个纹理变量 texture
。
Uniform
的属性和方法Three.js 中的 Uniform
主要有两个属性:value
和 needsUpdate
。
value
value
是 Uniform
的核心属性,它表示传递给着色器的值。你可以随时更新 value
,它的变化会直接影响着色器中的变量。
uniforms.time.value = performance.now() / 1000; // 根据时间更新 time
needsUpdate
needsUpdate
是一个布尔值,用于告诉渲染器当前 Uniform
的值是否需要更新。在大多数情况下,Three.js 会自动检测 Uniform
是否需要更新,但在一些特殊情况下,你可以手动设置 needsUpdate = true
来强制更新。
uniforms.texture.needsUpdate = true; // 强制更新纹理
Uniform
使用的完整示例下面是一个完整的示例,展示了如何在 Three.js 中使用 Uniform
动态控制着色器中的动画效果。
Uniform
控制时间动画// 创建场景、相机和渲染器
const scene = new THREE.Scene();
const camera = new THREE.PerspectiveCamera(75, window.innerWidth / window.innerHeight, 0.1, 1000);
const renderer = new THREE.WebGLRenderer();
renderer.setSize(window.innerWidth, window.innerHeight);
document.body.appendChild(renderer.domElement);
// 定义 uniforms
const uniforms = {
time: { value: 0.0 },
resolution: { value: new THREE.Vector2(window.innerWidth, window.innerHeight) }
};
// 定义着色器
const vertexShader = `
void main() {
gl_Position = projectionMatrix * modelViewMatrix * vec4(position, 1.0);
}
`;
const fragmentShader = `
uniform float time;
uniform vec2 resolution;
void main() {
vec2 uv = gl_FragCoord.xy / resolution;
vec3 color = vec3(0.5 + 0.5 * cos(time + uv.xyx + vec3(0, 2, 4)));
gl_FragColor = vec4(color, 1.0);
}
`;
// 创建材质
const material = new THREE.ShaderMaterial({
uniforms: uniforms,
vertexShader: vertexShader,
fragmentShader: fragmentShader
});
// 创建几何体和网格对象
const geometry = new THREE.PlaneGeometry(2, 2);
const mesh = new THREE.Mesh(geometry, material);
scene.add(mesh);
// 设置相机位置
camera.position.z = 1;
// 渲染循环
function animate() {
requestAnimationFrame(animate);
// 更新时间 uniform
uniforms.time.value += 0.05;
renderer.render(scene, camera);
}
animate();
在这个示例中,我们创建了一个简单的片段着色器,通过 time
uniform 实现了颜色随时间变化的动画效果。resolution
uniform 则确保了片段着色器根据屏幕分辨率正确绘制。
Uniform
TextureLoader
一起使用Uniform
经常用于传递纹理数据到着色器中。例如,我们可以使用 THREE.TextureLoader
来加载纹理,并将其传递给着色器。
const textureLoader = new THREE.TextureLoader();
const texture = textureLoader.load('path/to/texture.jpg');
const uniforms = {
texture: { value: texture }
};
在着色器中,我们可以通过 sampler2D
类型的 uniform
来访问这个纹理,并根据纹理的颜色信息进行渲染。
Clock
一起使用Three.js 中的 Clock
对象非常适合与 Uniform
结合使用来实现基于时间的动画。通过 Clock.getElapsedTime()
,
我们可以获取程序运行的总时间,并将其传递给 time
uniform。
const clock = new THREE.Clock();
function animate() {
requestAnimationFrame(animate);
// 使用 Clock 获取经过的时间
uniforms.time.value = clock.getElapsedTime();
renderer.render(scene, camera);
}
animate();
Uniform
是 Three.js 中用于在 JavaScript 和着色器之间传递全局数据的关键机制。通过 Uniform
,我们可以动态控制渲染效果、动画、光照等场景中的重要元素。本文详细介绍了 Uniform
的基本概念、使用方法、属性,以及如何与其他组件结合使用的示例。
希望通过本文的学习,你能够熟练掌握 Uniform
在 Three.js 中的使用,并在项目中创造更加丰富的视觉效果。如果你在项目中需要自定义着色器或动态控制场景元素,Uniform
将是不可或缺的工具。