MRT (Multiple Render Targets) — это техника, позволяющая рендерить сцену одновременно в несколько текстур за один проход. В контексте Three.js это особенно важно для реализации сложных графических эффектов, таких как deferred shading, постобработка, SSAO (Screen Space Ambient Occlusion) и кастомные визуальные фильтры. Вместо последовательного рендера сцены в несколько буферов, MRT сокращает количество draw calls и повышает производительность.
В Three.js MRT реализуется с помощью объектов WebGLMultipleRenderTargets, которые создают несколько текстур, связанных с одним фреймбуфером.
import * as THREE from 'three';
const width = 1024;
const height = 1024;
const count = 3; // количество текстур
const renderTargets = new THREE.WebGLMultipleRenderTargets(width, height, count);
renderTargets.texture[0].name = "Albedo";
renderTargets.texture[1].name = "Normal";
renderTargets.texture[2].name = "Position";
renderTargets.texture.forEach(tex => {
tex.minFilter = THREE.NearestFilter;
tex.magFilter = THREE.NearestFilter;
tex.type = THREE.FloatType;
});
count определяет количество текстур, в которые будет
рендер сцены.WebGLMultipleRenderTargets автоматически создаёт
фреймбуфер и прикрепляет к нему все текстуры.Для использования MRT необходимо модифицировать Fragment
Shader, чтобы выводить данные сразу в несколько буферов. В GLSL
для этого применяется массив gl_FragData[].
#version 300 es
precision highp float;
layout(location = 0) out vec4 outAlbedo;
layout(location = 1) out vec4 outNormal;
layout(location = 2) out vec4 outPosition;
uniform vec3 uColor;
in vec3 vNormal;
in vec3 vPosition;
void main() {
outAlbedo = vec4(uColor, 1.0);
outNormal = vec4(normalize(vNormal) * 0.5 + 0.5, 1.0);
outPosition = vec4(vPosition, 1.0);
}
layout(location = X) указывает номер текстуры в
MRT.outAlbedo, outNormal,
outPosition записывают разные данные в разные текстуры за
один проход.[0,1], чтобы корректно
сохраняться в текстуре.RawShaderMaterialThree.js поддерживает MRT через RawShaderMaterial, так как стандартные материалы (MeshStandardMaterial, MeshBasicMaterial) напрямую не предоставляют возможность вывода в несколько текстур. Пример:
const material = new THREE.RawShaderMaterial({
vertexShader: document.getElementById('vertexShader').textContent,
fragmentShader: document.getElementById('fragmentShader').textContent,
uniforms: {
uColor: { value: new THREE.Color(0xff0000) }
}
});
const mesh = new THREE.Mesh(geometry, material);
scene.add(mesh);
varying.layout(location = X) для
записи в несколько текстур.WebGLMultipleRenderTargets, который затем может
использоваться в постобработке.Deferred shading — классический сценарий применения MRT. Сначала создаётся G-Buffer, который хранит:
После создания G-Buffer можно производить освещение вне основного рендера сцены, обходя необходимость перерендеривать геометрию.
const gBuffer = new THREE.WebGLMultipleRenderTargets(width, height, 3);
gBuffer.texture.forEach(tex => tex.type = THREE.FloatType);
renderer.setRenderTarget(gBuffer);
renderer.render(scene, camera);
renderer.setRenderTarget(null);
// Постобработка освещения
lightingPass.uniforms.albedoMap.value = gBuffer.texture[0];
lightingPass.uniforms.normalMap.value = gBuffer.texture[1];
lightingPass.uniforms.positionMap.value = gBuffer.texture[2];
renderer.render(lightingScene, camera);
Тип текстур Для хранения нормалей, позиции и
других векторов лучше использовать THREE.FloatType или
THREE.HalfFloatType, чтобы избежать потери
точности.
Фильтрация MRT чаще всего используют с
NearestFilter, так как линейная фильтрация может искажать
данные (особенно нормали и позиции).
Размеры текстур Для больших сцен стоит подбирать разрешение G-Buffer с компромиссом между качеством и производительностью.
Совместимость WebGL2 MRT требует WebGL2. В
WebGL1 поддержка ограничена и требует расширений
(WEBGL_draw_buffers). Three.js автоматически проверяет
поддержку MRT через WebGLRenderer.
Использование MRT в Three.js открывает возможности для высокопроизводительных визуализаций и сложной графики, обеспечивая гибкость и масштабируемость современных 3D-приложений.