Зеркала и порталы через RenderTarget

WebGLRenderTarget — мощный инструмент в Three.js, позволяющий рендерить сцену не на экран напрямую, а в текстуру, которая затем может использоваться в качестве материала на объекте. Это открывает возможности для реализации зеркал, порталов, экранов видеонаблюдения и других эффектов динамического отображения сцены внутри сцены.


Настройка WebGLRenderTarget

Для создания рендер-таргета используется конструктор THREE.WebGLRenderTarget. Основные параметры:

const renderTarget = new THREE.WebGLRenderTarget(width, height, {
    minFilter: THREE.LinearFilter,
    magFilter: THREE.LinearFilter,
    format: THREE.RGBAFormat,
    depthBuffer: true,
});
  • width и height определяют разрешение текстуры. Для зеркал и порталов лучше выбирать квадратные размеры степени двойки (например, 512×512, 1024×1024) для оптимальной производительности.
  • minFilter и magFilter управляют фильтрацией при масштабировании текстуры.
  • format задаёт формат пикселей. Обычно используют THREE.RGBAFormat.
  • depthBuffer указывает, нужен ли буфер глубины, что важно для корректного наложения объектов.

Использование рендер-таргета в материале

После создания таргета его текстуру можно использовать в качестве карты в материале:

const mirrorMaterial = new THREE.MeshBasicMaterial({
    map: renderTarget.texture,
});

Для зеркала можно использовать MeshBasicMaterial, чтобы избежать влияния освещения, так как текстура уже содержит готовый рендер сцены.


Настройка камеры для зеркал

Чтобы реалистично отразить сцену в зеркале, необходимо создать вторую камеру, которая копирует положение и направление основной камеры с учётом отражения:

const mirrorCamera = new THREE.PerspectiveCamera(
    mainCamera.fov,
    renderTarget.width / renderTarget.height,
    mainCamera.near,
    mainCamera.far
);

mirrorCamera.position.copy(mainCamera.position);
mirrorCamera.position.reflect(mirrorPlane.normal);
mirrorCamera.lookAt(mirrorPlane.position);
  • mirrorPlane.normal — нормаль плоскости зеркала.
  • Метод reflect отражает вектор относительно плоскости.
  • mirrorCamera.lookAt направляет камеру на точку отражения, чтобы правильно сформировать перспективу.

Рендеринг сцены в текстуру

Основной шаг — рендеринг сцены в созданный WebGLRenderTarget вместо экрана:

renderer.setRenderTarget(renderTarget);
renderer.render(scene, mirrorCamera);
renderer.setRenderTarget(null);
  • setRenderTarget(null) возвращает рендеринг обратно на основной экран.
  • Этот подход позволяет обновлять текстуру зеркала каждый кадр, создавая динамический эффект.

Создание порталов

Порталы работают по аналогии с зеркалами, но вместо отражения может использоваться другое место сцены. Основные шаги:

  1. Создать WebGLRenderTarget для портала.
  2. Настроить камеру портала с нужной позицией и направлением.
  3. Отрендерить сцену через камеру портала в текстуру.
  4. Присвоить текстуру материалу портала.
portalMaterial.map = portalRenderTarget.texture;

Можно добавлять эффекты смещения, вращения или искажения камеры для создания фантастических эффектов перехода между пространствами.


Оптимизация производительности

  • Размер текстуры: меньшие текстуры снижают нагрузку на GPU, но могут потерять детализацию.
  • Фрейм-частота: рендерить порталы не обязательно каждый кадр, достаточно каждые несколько кадров.
  • Скрытие объектов: исключение объектов за пределами камеры портала сокращает количество рендеринга.

Продвинутые техники

  • Множественные зеркала: при нескольких зеркалах важно избегать рекурсивного рендеринга, иначе сцена будет рендериться бесконечно. Для этого используют ограничение глубины отражений.
  • Каскадные порталы: можно создавать последовательные порталы, где каждый следующий рендерится в текстуру предыдущего. Требует аккуратного управления порядком рендеринга.
  • Использование шейдеров: для порталов можно применять пользовательские шейдеры на основе ShaderMaterial для эффекта размытия, водяной поверхности или искажения перспективы.

Пример структуры кода

// Инициализация рендер-таргета
const renderTarget = new THREE.WebGLRenderTarget(1024, 1024);

// Создание зеркала
const mirrorGeometry = new THREE.PlaneGeometry(5, 5);
const mirrorMaterial = new THREE.MeshBasicMaterial({ map: renderTarget.texture });
const mirrorMesh = new THREE.Mesh(mirrorGeometry, mirrorMaterial);
scene.add(mirrorMesh);

// Камера для зеркала
const mirrorCamera = new THREE.PerspectiveCamera(mainCamera.fov, 1, mainCamera.near, mainCamera.far);

// Анимация
function animate() {
    requestAnimationFrame(animate);

    // Обновление зеркала
    mirrorCamera.position.copy(mainCamera.position);
    mirrorCamera.position.reflect(mirrorPlane.normal);
    mirrorCamera.lookAt(mirrorPlane.position);

    renderer.setRenderTarget(renderTarget);
    renderer.render(scene, mirrorCamera);
    renderer.setRenderTarget(null);

    // Рендер основной сцены
    renderer.render(scene, mainCamera);
}

animate();

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