Three.js использует фреймбуфер как текстуру

Я использую изображение в элементе canvas как текстуру в Three.js, выполняя манипуляции с изображениями на холсте с помощью JavaScript, а затем вызываю requireUpdate() на текстуре. Это работает, но это довольно медленно.

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

  • Шейдерные материалы: http://mrdoob.github.io/three.js/examples/webgl_shader2.html В этом примере показаны манипуляции с изображениями, выполняемые в шейдере фрагментов, но этот шейдер функционирует как фрагментарный шейдер всего материала. Я только хочу использовать шейдер на текстуре, а затем использовать текстуру как компонент второго материала.

  • Рендеринг текстуры: https://threejsdoc.appspot.com/doc/three.js/examples/webgl_rtt.html Это показывает рендеринг всей сцены в WebGLRenderTarget и используя это как текстуру в материале. Я хочу только предварительно обработать изображение, а не отображать всю сцену.

  • Композитор эффектов: http://www.airtightinteractive.com/demos/js/shaders/preview/ Это показывает применение шейдеров в качестве постпроцесса для целая сцена.

Изменить: Здесь еще один:

  • Отдать в другую сцену: http://relicweb.com/webgl/rt.html В этом примере ссылка на Three.js Извлечение данных из WebGLRenderTarget (water sim), использует вторую сцену с собственной орфографической камерой для рендеринга динамической текстуры в WebGLRenderTarget, которая затем используется как текстура в основной сцене. Я предполагаю, что это особый случай первого примера "отнести к текстуре", приведенного выше, и, вероятно, будет работать для меня, но кажется сложным.

Как я понимаю, в идеале я мог бы создать новый объект framebuffer со своим собственным шейдером фрагментов, сделать его сам по себе и использовать его вывод как однородную текстуру для другого шейдера фрагмента материала. Возможно ли это?

Изменить 2: Похоже, я мог бы спросить что-то похожее на это: Shader Materials и GL Framebuffers в THREE.js. хотя вопрос, похоже, не был разрешен.

Ответ 1

Render to texture и Render to another scene, как указано выше, - это одно и то же, и техника, которую вы хотите. Объяснить:

В vanilla WebGL, как вы это делаете, это создание объекта фреймбуфера (FBO) с нуля, привязка текстуры к нему и рендеринг его с помощью вашего шейдера по вашему выбору. Такие понятия, как "сцена" и "камера", не задействованы, и это своего рода сложный процесс. Вот пример:

http://learningwebgl.com/blog/?p=1786

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

Итак: Чтобы воспроизвести настройку WebGL FBO, содержащую одну визуализированную текстуру, как указано в комментариях, просто создайте новую сцену, содержащую орфографическую камеру и одну плоскость с материалом с использованием нужной текстуры, затем отрисуйте новый WebGLRenderTarget с использованием пользовательского шейдера:

// new render-to-texture scene
myScene = new THREE.Scene();

// you may need to modify these parameters
var renderTargetParams = {
  minFilter:THREE.LinearFilter,
  stencilBuffer:false,
  depthBuffer:false
};

myImage = THREE.ImageUtils.loadTexture( 'path/to/texture.png',
  new THREE.UVMapping(), function() { myCallbackFunction(); } );

imageWidth = myImage.image.width;
imageHeight = myImage.image.height;

// create buffer
myTexture = new THREE.WebGLRenderTarget( width, height, renderTargetParams );

// custom RTT materials
myUniforms = {
  colorMap: { type: "t", value: myImage },
};
myTextureMat = new THREE.ShaderMaterial({
  uniforms: myUniforms,
  vertexShader: document.getElementById( 'my_custom_vs' ).textContent,
  fragmentShader: document.getElementById( 'my_custom_fs' ).textContent
});

// Setup render-to-texture scene
myCamera = new THREE.OrthographicCamera( imageWidth / - 2,
  imageWidth / 2,
  imageHeight / 2,
  imageHeight / - 2, -10000, 10000 );

var myTextureGeo = new THREE.PlaneGeometry( imageWidth, imageHeight );
myTextureMesh = new THREE.Mesh( myTextureGeo, myTextureMat );
myTextureMesh.position.z = -100;
myScene.add( myTextureMesh );

renderer.render( myScene, myCamera, myTexture, true );

После рендеринга новой сцены myTexture будет доступен для использования в качестве текстуры в другом материале вашей основной сцены. Обратите внимание, что вы можете запустить первый render с функцией обратного вызова в вызове loadTexture(), чтобы он не пытался отображать до загрузки исходного изображения.