RawShaderMaterial: без встроенных uniform Three.js

RawShaderMaterial — это материал в Three.js, позволяющий полностью управлять шейдерами без добавления автоматических uniform и макросов, которые присутствуют в стандартных материалах, таких как ShaderMaterial. В отличие от ShaderMaterial, RawShaderMaterial не внедряет стандартные uniform вроде projectionMatrix, modelViewMatrix и других, что даёт полный контроль над графическим конвейером.

Применение этого материала оправдано, когда необходима строгая кастомизация шейдеров, использование нестандартных матриц, уникальных вычислений или оптимизация под специфические задачи рендеринга.


Создание RawShaderMaterial

Создание материала требует определения двух основных шейдеров: вершинного (vertexShader) и фрагментного (fragmentShader).

import * as THREE from 'three';

const material = new THREE.RawShaderMaterial({
    vertexShader: `
        precision highp float;
        attribute vec3 position;
        uniform mat4 myCustomMatrix;
        void main() {
            gl_Position = myCustomMatrix * vec4(position, 1.0);
        }
    `,
    fragmentShader: `
        precision highp float;
        uniform vec3 color;
        void main() {
            gl_FragColor = vec4(color, 1.0);
        }
    `,
    uniforms: {
        myCustomMatrix: { value: new THREE.Matrix4() },
        color: { value: new THREE.Color(0xff0000) }
    }
});

Ключевые моменты:

  • precision highp float; необходим в каждом шейдере для корректной работы с плавающей точкой.
  • Все uniform и атрибуты должны быть явно объявлены.
  • RawShaderMaterial не добавляет стандартные переменные, такие как modelViewMatrix или projectionMatrix.

Атрибуты и uniform

В RawShaderMaterial нет автоматических атрибутов. Для передачи данных вершин необходимо использовать:

  • position — координаты вершин (обычно BufferGeometry содержит этот атрибут).
  • normal — нормали, если требуется освещение.
  • uv — координаты текстур.

Все дополнительные данные необходимо передавать вручную через uniforms или создавать свои BufferAttribute для geometry:

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));

Управление матрицами и преобразованиями

В отличие от ShaderMaterial, RawShaderMaterial не предоставляет автоматические матрицы:

  • modelViewMatrix
  • projectionMatrix
  • normalMatrix

Если необходимо использовать трансформации сцены, их следует передавать вручную через uniform:

const customMatrix = new THREE.Matrix4();
mesh.updateMatrixWorld();
customMatrix.multiplyMatrices(camera.projectionMatrix, mesh.matrixWorld);

material.uniforms.myCustomMatrix.value.copy(customMatrix);

Это даёт полный контроль над тем, какие матрицы и в каком порядке применяются к вершинам.


Освещение и нормали

Для реализации освещения вручную необходимо передавать нормали и позиции источников света через uniform:

uniforms: {
    lightPosition: { value: new THREE.Vector3(10, 10, 10) },
    normalMatrix: { value: new THREE.Matrix3() }
}

В вершинном шейдере нормали преобразуются с помощью normalMatrix:

vec3 transformedNormal = normalize(normalMatrix * normal);

Во фрагментном шейдере можно вычислить диффузное освещение:

float diffuse = max(dot(transformedNormal, normalize(lightPosition - fragPosition)), 0.0);
gl_FragColor = vec4(diffuse * color, 1.0);

Текстуры

Работа с текстурами полностью ручная. Необходимо объявлять uniform sampler2D и передавать координаты uv через атрибут:

uniform sampler2D myTexture;
varying vec2 vUv;

void main() {
    vec4 texColor = texture2D(myTexture, vUv);
    gl_FragColor = texColor;
}

Передача текстуры из Jav * aScript:

material.uniforms.myTexture = { value: new THREE.TextureLoader().load('texture.png') };

Преимущества использования RawShaderMaterial

  1. Полный контроль над шейдерами — отсутствие встроенных uniform позволяет реализовать нестандартные эффекты.
  2. Оптимизация — можно исключить все ненужные вычисления и uniform, что снижает нагрузку на GPU.
  3. Совместимость с нестандартными конвейерами — легко интегрируется с пост-обработкой, нестандартными координатами и вычислениями на GPU.

Важные рекомендации

  • Всегда указывать precision (highp, mediump) в шейдерах.
  • Использовать BufferGeometry для передачи любых атрибутов.
  • Обновлять uniform вручную при изменении матриц, цвета или освещения.
  • Для сложного освещения и теней разработать собственный конвейер, так как стандартные материалы здесь не применимы.

RawShaderMaterial является идеальным инструментом для проектов, где требуется максимальная кастомизация рендеринга и отказ от стандартных uniform Three.js, создавая полностью контролируемый графический конвейер.