Всё, что вы рендерите в Three.js, по умолчанию отображается на экране. Зачем вообще нужен рендеринг, если нельзя увидеть результат? Оказывается, затем, чтобы собрать данные, прежде чем они выведутся на экран (и, следовательно, будут утеряны). Это намного упрощает применение эффектов постобработки, таких как коррекция/искажение цветов и размытие изображения, а также очень полезно для шейдерных эффектов. Такой прием называется рендеринг в текстуру или рендеринг в буфер кадра. Конечный результат сохраняется в текстуре, которую затем можно отобразить на экране. В этой статье я покажу, как это делается, на конкретном примере рендеринга анимированного куба на поверхность другого анимированного куба.
Примечание: эта статья рассчитана на тех, кто уже обладает базовыми знаниями Three.js. Если вы не знакомы с этой библиотекой, для начала рекомендую прочесть статью How to Learn Three.js for Game Development.
Базовая реализация
В интернете есть много похожих примеров, которые показывают более сложные эффекты. Но в этой статье представлен лишь самый необходимый минимум.
// @автор: Омар Шехата. 2016.
// Загружаем библиотеку Three.js по ссылке:
// http://cdnjs.com/libraries/three.js/
//// Установки обычной сцены ////
var scene = new THREE.Scene();
var width, height = window.innerWidth, window.innerHeight;
var camera = new THREE.PerspectiveCamera( 70, width/height, 1, 1000 );
var renderer = new THREE.WebGLRenderer();
renderer.setSize( width,height);
document.body.appendChild( renderer.domElement );
//// Создаем внеэкранный буфер ////
// Создаем новую сцену, в которой будут храниться буферные объекты
var bufferScene = new THREE.Scene();
// Создаем текстуру, в которой будет храниться результат рендера
var bufferTexture = new THREE.WebGLRenderTarget( window.innerWidth, window.innerHeight, { minFilter: THREE.LinearFilter, magFilter: THREE.NearestFilter});
////
// Сюда добавляем любые объекты для отрисовки в bufferScene //
////
function render() {
requestAnimationFrame( render );
// Устанавливаем рендер во внеэкранную текстуру
renderer.render(bufferScene, camera, bufferTexture);
// И, наконец, отрисовываем результат на экране
renderer.render( scene, camera );
}
render(); // Готово!
Для начала у нас есть установки обычной сцены. Затем мы создаем еще одну – bufferScene. Любой объект, добавленный в эту сцену, будет рендериться не на экран, а во внеэкранный буфер. Затем мы создаем текстуру bufferTexture, представляющую собой буфер WebGLRenderTarget, который используется в Three.js для внеэкранного рендеринга. Осталось задать отрисовку bufferScene:
renderer.render(bufferScene, camera, bufferTexture);
По сути, это всё равно что рендерить обычную сцену, только с указанием третьего аргумента – буфера. Итак, основные этапы:
1. Создание сцены, в которой будут храниться объекты.
2. Создание текстуры, в которой будет храниться результат рендера.
3. Рендеринг сцены в текстуру.
Вот, собственно, и всё. Но пока ничего не видно. Даже если вы добавили объекты в bufferScene, вы ничего не увидите, потому что сначала нужно визуализировать созданную текстуру в основной сцене. Далее мы посмотрим, как это сделать.
Пример использования
Мы создадим куб, отрисуем его в текстуру, а затем используем в качестве текстуры для нового куба.
bufferTexture – экран
1. Начинаем с обычной сцены
Вот так выглядит наша сцена с вращающимся красным кубом на голубом фоне. Если вам интересно, можете посмотреть код, переключаясь между вкладками CSS и JS на CodePen.
2. Рендерим сцену в текстуру
Теперь отрисуем получившуюся сцену в текстуру. Для этого нужно всего лишь создать bufferScene, как описано в предыдущем разделе, и добавить в нее объекты.
Даже если вы сделали всё правильно, вы ничего не увидите, потому что сейчас наша сцена рендерится не на экран, а в bufferTexture.
3. Рендерим текстуру куба
bufferTexture ничем не отличается от любой другой текстуры. Мы можем просто создать новый объект и использовать его в качестве текстуры:
var boxMaterial = new THREE.MeshBasicMaterial({map:bufferTexture});
var boxGeometry2 = new THREE.BoxGeometry( 5, 5, 5 );
var mainBoxObject = new THREE.Mesh(boxGeometry2,boxMaterial);
// Move it back so we can see it
mainBoxObject.position.z = -10;
// Add it to the main scene
scene.add(mainBoxObject);
В движении объект можно посмотреть здесь.
На первой текстуре можно нарисовать что угодно и отрендерить ее на любую поверхность.
Варианты использования
Такой рендеринг очень хорошо подходит для любых эффектов постобработки. Допустим, вы хотите сделать коррекцию или искажение цветов в сцене. Вместо того чтобы изменять каждый отдельный объект, вы можете отрендерить всю сцену в текстуру и применить к ней нужный эффект, прежде чем отображать на экране.
Рендер в текстуру также может использоваться в любом шейдере, состоящем из нескольких проходов (например Blur). В предыдущей статье я объяснял, как использовать кадровые буферы для создания эффекта дыма.
Автор: Plarium