我报名参加金石计划1期挑战——瓜分10万奖池,这是我的第2篇文章,点击查看活动详情
后期处理,就是你想的那个,就是修图,Ps。 postprocess。
既然要修图,那首先得有一张图,three默认当然是直接绘制到屏幕上的。 要想不直接绘制到屏幕上,自然要设置渲染目标。
最简单的pass
renderPass,这个pass什么处理都没有,仅仅是执行了渲染. 可以看到,虽然做了一系列的判断,但是这些判断都是初始化实例时的参数,现在就假设只传了scene camera ,那么这些条件都不会通过,最终也就是执行了
renderer.setRenderTarget( this.renderToScreen ? null : readBuffer ); renderer.render( this.scene, this.camera ); 这两行代码。
也就是说,这个pass仅仅就是设置了渲染目标并渲染,而已 。 是否渲染到屏幕上?,一般使用了这个pass都是false。
所以通常, renderPass就做了这么一件事, 把rendertarget设为readBuffer,把像素绘制到绑定的纹理上了。
我们再回过头看Pass类,发现真的是没啥好看的,因为pass一般情况是放在 composer里使用的。所以我们去看composer
简单看一看composer源码
其实源码本身也很简单,就不到两百行。
初始化的时候,设置了纹理的尺寸,pass的数组, 实例化了两个RenderTarget,一个读一个写。但是读写为什么要分开呢,后面再说。
关于这个copyPass,后面一起说, copyShader是一个一套极简的使用纹理贴图的着色器。
重点 render
重点就在这里,其他函数都很简单,意义也很明确。 这个函数主要就是遍历 pass数组,挨个儿调用他们的render方法。
循环体逻辑如下:
graph TD
A[pass] --> B{pass.enble}
B--是--> C[ 如果是最后一个可用的pass就渲染到屏幕]
B--否--> U[下一个pass]
C--> D[调用pass.render]
D--needSwap--> F[交换两个渲染目标]
D--needSwap 且 maskactive--> E[设置模板测试]
后面还有条件也是和其他处理有关的,我们也不关心模板测试。 注意needSwap这个值默认是true的。
所以这个渲染逻辑就是,一个pass在渲染的时候从 readerBuffer上获取纹理,并渲染到 writeBuffer上。
渲染结束后,交换读写渲染目标。
最后一个可用的 pass 会直接绘制到屏幕上。
为什么要默认交换读写缓冲对象
看到这里有没有疑惑,为什么需要两个缓冲区,难道就因为显存大,任性? 只要一个rendertarget,从上面读取纹理,然后渲染到这个缓冲区绑定的纹理上, 就是说我在更新这个纹理之前读它的值,这是非常常规的一种操作。
但是,真的不行,如果你这么干,Chrome会warn,实际上这个警告会打断渲染,所以你不能无视它。
Feedback loop formed between Framebuffer and active Texture.
原因是chrome采取了一种比较保守的策略,作为的渲染目标纹理,是不能同在一个渲染过程中作为shader的纹理数据输入源。 所以,他才需要两个换着用,我知道这个是因为我在使用pass的时候遇到这个问题。 正常使用不会遇到。
我当时还不熟悉pass的流程, 我在自己写的pass里面将渲染目标设为了 readBuffer, 然后我(粗心的)发现,composer上的读写缓冲区似乎就没变过,我下个pass就直接从readBufer上读,也写入它,于是,有了这个警告。
至于,我为何不写入 writeBuffer,而是readBuffer。 这是因为,我参照的是renderPass这个例子,它里面就是这么写的,但是,它的 needSwap 设置为了false,所以下一个pass正常从readBuffer里读,写入writeBuffer是没问题的。
为什么这里只要三个点
再接着看shaderpass,这个pass可能是我们自定义pass处理图像会用的pass, 就是传入自定义的着色器,对图像进行处理。 既然要对图像进行处理,那当然要在着色器里获取到图像了,就是纹理贴图。
而copyShader可以说是最简单的读取纹理的着色了, 着色器代码越精简,性能越好,尤其是片元着色器,因为它的执行次数是插值的后片元数 。 copyshader常用作一个基础模版。
虽然绘制一个纹理只要一个平面就足够了,但也是要一个平面的,FullScreenQuad 这个类就是一个极简的平面Mesh . 我开始想的就是两个三角形拼成一个矩形,应该就很简单了。 但是,看了这个之后发现,我的思维还是被之前的东西束缚住了。
_geometry.setAttribute( 'position', new THREE.Float32BufferAttribute( [ - 1, 3, 0, - 1, - 1, 0, 3, - 1, 0 ], 3 ) );
这个几何体就只有三个点,也就是说只能画出一个三角形。 我看的的第一眼觉得,这不可能吧,除非。。。,这个三角形囊括了 裁剪空间的那个矩形, 实际上就是如此。 因为超出裁剪空间的顶点会被排除,所以我下意识就在内部取点,可以说是画地为牢了,虽然这样是对的,甚至更好,因为裁剪这个三角形实际上又会多出几个顶点。 所以到底是四个顶点更好,还是少一个顶点更好我也不知道。
我只是想说,有时候自己的思路真的很受自己的经验的限制。