uniform 与动画
目标:用最少概念讲清 TSL 动画的两条路径(内置 time、从 React 写入的 uniform),并提供最小可运行片段。
1. 动画的两条最简单路径
2. 示例:时间节点驱动
用 time + 数学节点(sin、positionLocal、color)在 Shader Graph 内直接组合,无需 JS 写入。 (time, sin, positionLocal, color)
import { Canvas, extend } from '@react-three/fiber'
import { WebGPURenderer, MeshBasicNodeMaterial } from 'three/webgpu'
import { color, time, sin, positionLocal } from 'three/tsl'
import { useMemo } from 'react'
extend({ MeshBasicNodeMaterial })
function TimeDrivenPlane() {
const colorNode = useMemo(() => {
// 基于位置与时间的正弦色彩
const R = sin(positionLocal.x.add(time)).add(1).mul(0.5)
const G = sin(positionLocal.y.add(time)).add(1).mul(0.5)
const B = sin(positionLocal.x.add(time.mul(1.5))).add(1).mul(0.5)
return color(R, G, B)
}, [])
return (
<mesh>
<planeGeometry />
<meshBasicNodeMaterial colorNode={colorNode} />
</mesh>
)
}
export default function App() {
return (
<Canvas
gl={async (p) => { const r = new WebGPURenderer(p); await r.init?.(); return r }}
camera={{ position: [0, 0, 2.5], fov: 50 }}
style={{ width:'100%', height:'100vh', background:'black' }}
>
<TimeDrivenPlane />
</Canvas>
)
}3. 示例:uniform 驱动
创建 uniform(0),在 useFrame 更新 uniform.value,再在节点图中使用它参与计算。
import { Canvas, useFrame, extend } from '@react-three/fiber'
import { WebGPURenderer, MeshBasicNodeMaterial } from 'three/webgpu'
import { color, uniform, sin, mix } from 'three/tsl'
import { useMemo } from 'react'
extend({ MeshBasicNodeMaterial })
function UniformDrivenPlane() {
const uTime = useMemo(() => uniform(0.0), [])
const colorNode = useMemo(() => {
const t = sin(uTime).add(1).mul(0.5) // 映射到 [0,1]
return mix(color(1.0, 0.4, 0.2), color(0.1, 0.4, 1.0), t)
}, [uTime])
useFrame((_, delta) => { uTime.value += delta }) // 帧循环更新 uniform
return (
<mesh>
<planeGeometry />
<meshBasicNodeMaterial colorNode={colorNode} />
</mesh>
)
}
export default function App() {
return (
<Canvas
gl={async (p) => { const r = new WebGPURenderer(p); await r.init?.(); return r }}
camera={{ position:[0,0,2.5], fov:50 }}
style={{ width:'100%', height:'100vh', background:'black' }}
>
<UniformDrivenPlane />
</Canvas>
)
}4. 可调参数
多个 uniform 协同,如速度、强度等;这些值可连接到 GUI 或交互事件。
import { useFrame } from '@react-three/fiber'
import { uniform } from 'three/tsl'
// 额外的速率参数(可来自 GUI)
const uTime = uniform(0.0)
const uSpeed = uniform(1.0) // 缩放动画速度
useFrame((_, delta) => {
uTime.value += delta * uSpeed.value
})5. 常见陷阱与建议
节点只创建一次;使用 Node 材质的 colorNode;WebGPU 需 init();确保存在帧循环。
// ✅ Node/Uniform 只创建一次,避免每帧重建
const u = useMemo(() => uniform(0.0), [])
// ✅ 使用 NodeMaterial 的 colorNode,而不是普通材质的 color
// <meshBasicNodeMaterial colorNode={...} />
// ✅ WebGPU 需显式 init
const r = new WebGPURenderer(props)
await r.init?.()
// ✅ 帧循环存在,才会看到动画(R3F 默认 continuous)