В Three.js взаимодействие с шейдерами осуществляется через механизм uniform-переменных и атрибутов. Uniform-переменные позволяют передавать в шейдер данные, которые остаются постоянными на протяжении одного рендера, но могут меняться между кадрами, а атрибуты определяют значения для отдельных вершин или фрагментов.
Для создания анимаций в шейдерах часто используется время. В Three.js время обычно передается в виде float через uniform-переменную. Пример создания материала с пользовательским шейдером, учитывающим время:
const uniforms = {
uTime: { value: 0.0 }
};
const material = new THREE.ShaderMaterial({
uniforms: uniforms,
vertexShader: /* glsl */`
uniform float uTime;
varying vec2 vUv;
void main() {
vUv = uv;
vec3 pos = position;
pos.y += sin(uTime + position.x) * 0.5;
gl_Position = projectionMatrix * modelViewMatrix * vec4(pos, 1.0);
}
`,
fragmentShader: /* glsl */`
varying vec2 vUv;
void main() {
gl_FragColor = vec4(vUv, 0.5 + 0.5*sin(uTime), 1.0);
}
`
});
Обновление значения времени осуществляется в цикле анимации:
function animate(time) {
requestAnimationFrame(animate);
uniforms.uTime.value = time * 0.001; // перевод миллисекунд в секунды
renderer.render(scene, camera);
}
animate();
Ключевой момент — uTime.value изменяется каждый кадр, создавая динамическое поведение объектов.
Помимо времени, в шейдер можно передавать любые данные: числа, векторы, текстуры. Примеры типов uniform-переменных:
float — одно числоvec2, vec3, vec4 — двумерные,
трёхмерные и четырёхмерные векторыmat3, mat4 — матрицыsampler2D — текстураПример передачи пользовательских данных:
const uniforms = {
uColor: { value: new THREE.Color(0xff0000) },
uIntensity: { value: 1.0 }
};
const material = new THREE.ShaderMaterial({
uniforms: uniforms,
vertexShader: /* glsl */`
varying vec3 vColor;
uniform float uIntensity;
void main() {
vColor = position * uIntensity;
gl_Position = projectionMatrix * modelViewMatrix * vec4(position, 1.0);
}
`,
fragmentShader: /* glsl */`
varying vec3 vColor;
uniform vec3 uColor;
void main() {
gl_FragColor = vec4(vColor * uColor, 1.0);
}
`
});
Изменение данных в рантайме:
uniforms.uIntensity.value = Math.sin(Date.now() * 0.001);
uniforms.uColor.value.setHSL(Math.random(), 0.5, 0.5);
Для передачи информации на уровне вершин используются BufferGeometry и кастомные атрибуты:
const geometry = new THREE.BufferGeometry();
const vertices = new Float32Array([
-1, -1, 0,
1, -1, 0,
0, 1, 0
]);
geometry.setAttribute('position', new THREE.BufferAttribute(vertices, 3));
const customValues = new Float32Array([
0.1, 0.5, 0.9
]);
geometry.setAttribute('aValue', new THREE.BufferAttribute(customValues, 1));
const material = new THREE.ShaderMaterial({
vertexShader: /* glsl */`
attribute float aValue;
varying float vValue;
void main() {
vValue = aValue;
gl_Position = projectionMatrix * modelViewMatrix * vec4(position, 1.0);
}
`,
fragmentShader: /* glsl */`
varying float vValue;
void main() {
gl_FragColor = vec4(vValue, vValue, vValue, 1.0);
}
`
});
Каждой вершине можно присвоить собственные значения, которые затем используются в вершинном и фрагментном шейдерах.
Uniform-переменные позволяют напрямую интегрировать шейдер с пользовательскими интерфейсами или данными из приложения. Например, при использовании dat.GUI можно менять uniform-переменные в реальном времени:
const params = { speed: 1.0 };
const gui = new dat.GUI();
gui.add(params, 'speed', 0, 5).onChange(value => {
uniforms.uSpeed.value = value;
});
Такое управление позволяет создавать интерактивные эффекты: деформации, цветовые анимации, реагирование на пользовательский ввод.
uniforms.Использование этих методов позволяет создавать сложные динамические визуальные эффекты в Three.js, делая шейдеры полностью управляемыми как через внутренние переменные сцены, так и через пользовательские данные.