Процедурные текстуры создаются с помощью математических функций и
алгоритмов, а не загружаются как готовые изображения. Такой подход
позволяет динамически генерировать текстуры различной сложности,
изменять их в реальном времени и экономить память, особенно для больших
сцен. В Three.js процедурные текстуры реализуются через
шейдеры на GLSL, с использованием ShaderMaterial или
модификации встроенных материалов через
onBeforeCompile.
ShaderMaterial позволяет полностью контролировать
вершины и фрагменты рендеринга. Основная структура материала выглядит
так:
const material = new THREE.ShaderMaterial({
uniforms: {
u_time: { value: 0 },
u_resolution: { value: new THREE.Vector2(window.innerWidth, window.innerHeight) }
},
vertexShader: /* glsl */`
varying vec2 vUv;
void main() {
vUv = uv;
gl_Position = projectionMatrix * modelViewMatrix * vec4(position, 1.0);
}
`,
fragmentShader: /* glsl */`
uniform float u_time;
uniform vec2 u_resolution;
varying vec2 vUv;
void main() {
vec2 st = vUv;
vec3 color = vec3(st.x, st.y, abs(sin(u_time)));
gl_FragColor = vec4(color, 1.0);
}
`
});
uniforms позволяют передавать в шейдеры параметры с
JavaScript.vUv используется для передачи координат текстуры из
вершинного шейдера во фрагментный.abs(sin(u_time)) демонстрирует динамическую
анимацию цвета.Для реалистичных процедурных текстур применяются фрактальные функции и шумы, такие как Perlin Noise или Simplex Noise. В GLSL можно подключить их через функции:
float random(vec2 st) {
return fract(sin(dot(st.xy, vec2(12.9898,78.233))) * 43758.5453123);
}
float noise(vec2 st) {
vec2 i = floor(st);
vec2 f = fract(st);
float a = random(i);
float b = random(i + vec2(1.0, 0.0));
float c = random(i + vec2(0.0, 1.0));
float d = random(i + vec2(1.0, 1.0));
vec2 u = f*f*(3.0-2.0*f);
return mix(a, b, u.x) +
(c - a)* u.y * (1.0 - u.x) +
(d - b) * u.x * u.y;
}
Эта функция создаёт гладкий шум на плоскости. Использование таких функций позволяет имитировать текстуры дерева, воды, облаков и других природных поверхностей.
Для сложных текстур шумы комбинируются на разных масштабах, создавая фрактальные шумы:
float fbm(vec2 st) {
float value = 0.0;
float amplitude = 0.5;
for (int i = 0; i < 5; i++) {
value += amplitude * noise(st);
st *= 2.0;
amplitude *= 0.5;
}
return value;
}
Такой подход позволяет генерировать текстуры с разными деталями: крупные формы задаются на первых итерациях, мелкие — на последующих.
Процедурная текстура может быть напрямую встроена в
ShaderMaterial, либо использоваться как data
texture. Например, для генерации текстуры облаков:
const size = 256;
const data = new Uint8Array(size * size * 3);
for (let i = 0; i < size * size; i++) {
const x = i % size;
const y = Math.floor(i / size);
const value = Math.floor(fbm(new THREE.Vector2(x, y) / 50.0) * 255);
data[i * 3 + 0] = value;
data[i * 3 + 1] = value;
data[i * 3 + 2] = value;
}
const texture = new THREE.DataTexture(data, size, size, THREE.RGBFormat);
texture.needsUpdate = true;
Такой подход позволяет использовать процедурные данные как обычную
текстуру для MeshStandardMaterial или других материалов
Three.js.
Процедурные текстуры легко анимировать через uniforms.
Например, смещение координат шума по времени создаёт эффект движения
облаков или воды:
vec2 st = vUv;
st += vec2(u_time * 0.05, u_time * 0.1);
float n = fbm(st * 3.0);
gl_FragColor = vec4(vec3(n), 1.0);
Использование временных параметров позволяет добиться динамичных эффектов без загрузки дополнительных ресурсов.
DataTexture.fbm и использование
lowp для переменных помогает снизить нагрузку на GPU.Процедурные текстуры активно применяются для:
Эффективное сочетание GLSL,
ShaderMaterial и процедурных алгоритмов открывает
возможности для создания уникальных визуальных решений без ограничения
памяти и зависимости от внешних изображений.