Three.js使用帧缓冲作为纹理

13

我正在使用一个canvas元素中的图像作为Three.js中纹理的贴图,使用JavaScript在canvas上执行图像操作,然后在纹理上调用needsUpdate()。这样做可以实现目标,但速度很慢。

我希望能够在片段着色器中执行图像计算。我已经找到许多几乎可以实现这一点的示例:

编辑: 下面是另一个示例:

据我所知,理想情况下,我应该能够制作一个具有自己片段着色器的新帧缓冲对象,单独进行渲染,并使用其输出作为另一个材质的片段着色器的纹理统一变量。这可行吗?

编辑2: 看起来我可能在寻找类似于此的内容:Shader Materials and GL Framebuffers in THREE.js...尽管问题似乎没有被解决。


1
在three.js框架中,实现你想要的方式是使用自定义的ShaderMaterial将包含单个平面和正交相机的场景渲染到纹理中,然后将该纹理作为第二个ShaderMaterial的uniform。 - WestLangley
1
即使您的问题非常有教育意义,它也包含了一些非常有用的链接和好的示例。 - M -
1个回答

24

渲染到纹理渲染到另一个场景如上所述是相同的东西,也是您想要的技术。解释一下:

在普通的WebGL中,您可以通过从头创建帧缓冲对象(FBO),将纹理绑定到它,并使用所选着色器进行渲染来完成此类操作。不涉及“场景”和“相机”等概念,这是一个有点复杂的过程。以下是一个示例:

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

但这也恰好是使用Three.js渲染带有相机的场景时所发生的事情:渲染器输出到一个帧缓冲区,其基本用法直接传递到屏幕上。因此,如果您指示它渲染到一个新的WebGLRenderTarget,那么您可以将相机看到的任何内容用作第二个材质的输入纹理。所有复杂的东西仍然在幕后发生,这就是Three.js的美妙之处。 :)

因此:要复制评论中提到的包含单个渲染纹理的FBO的WebGL设置,只需创建一个新场景,其中包含一个正交相机和一个使用所需纹理的材质的单个平面,然后使用自定义着色器渲染到新的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 就可以作为另一个材质中的纹理在主场景中使用。请注意,您可能希望使用 loadTexture() 调用中的回调函数触发第一次 render,以便它在源图像加载完成之前不会尝试进行渲染。

2
正交相机的近平面不应该在相机后面,而应该是一个正值。另外,THREE.RenderTargetWrapping是什么?你在这种情况下使用type = THREE.FloatType的原因是什么? - WestLangley
3
不冒犯,谢谢你的帮助。我不知道那些行是用来干什么的。它们是遗传自祖先的残留物,在编程和进化中,代码如果没有明显地影响生存能力,则往往会保留下来。-_- 我将编辑示例以减少对未来世代的混淆。 - meetar

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接