Three.js引擎中自带了效果组合器和多种后期处理通道,利用渲染到纹理的技术可以方便渲染出各种特效。
一、效果组合器
效果组合器(EffectComposer)是处理通道的容器,实现渲染到纹理效果,只需要将通道添加到效果组合器。创建效果组合器需要在Three.js发布中添加 如下文件:
EffectComposer.js 提供组合器对象
CopyShader.js,MaskPass.js,ShaderPass.js 组合器内部使用的文件
RenderPass.js 在组合器上添加效果通道
使用方法:
(1) 引入上述文件
(2)创建和配置组合器并添加通道 ,代码如下:
1 var composer = new THREE.EffectComposer(webGLRenderer); //新建效果混合器
2 var renderPass = new THREE.RenderPass(scene, camera); //新建处理通道
3 composer.addPass(renderPass); //添加处理通道
(3)渲染到纹理(组合器提供的渲染方法),关键代码如下:
1 var clock = new THREE.Clock(); //新建时钟对象
2 function render() { //使用渲染到纹理的渲染方法
3 var delta = clock.getDelta(); //得到两次调用之间的时间间隔
4 composer.render(delta); //效果组合器的渲染方法
5 requestAnimationFrame(render); //请求绘制下一帧
6 }
效果组合器提供的渲染方法比较特殊的地方在于,需要向其中传入两次调用之间的时间间隔。而Three.js中提供了时钟对象,只需调用getDelta()方法即可。
二、FilmPass通道
它可以为画面添加颗粒效果和扫描线等,从而模拟出老式电影的感觉。
示例渲染出的地球模型的整体效果就像它是在老式电视上显示出来的一样。但是由于此通道会对画面进行颗粒化处理,对物体的细节表现会有一定影响,所以不适合需要进行精细绘制的情况。
开发步骤如下所示。
(1)引入FilmShader.js文件。
(2)新建FilmPass通道并添加到效果组合器中。
需要注意的是,要想使渲染结果呈现在屏幕上,必须将此通道的renderToScreen属性置为true,
具体代码如下。
1 function addFilmPass(){ //新建并添加FilmPass通道的方法
2 var renderPass = new THREE.RenderPass(scene, camera); //新建RenderPass通道
3 var effectFilm = new THREE.FilmPass(0.8, 0.325, 256, false); //新建FilmPass通道
4 effectFilm.renderToScreen = true; //渲染到屏幕上
5 composer = new THREE.EffectComposer(webGLRenderer); //新建效果组合器
6 composer.addPass(renderPass); //添加RenderPass通道
7 composer.addPass(effectFilm); //添加FilmPass通道
8 }
上面的代码中只增加了创建FilmPass通道和将其添加到效果组合对象的相关代码。
三、 BloomPass通道
需要渲染出较为明亮的场景,则除了调整光源的光照强度之外,还可以使用Three.js中自带的BloomPass通道对画面进行处理。处理后的画面中明亮区域将会变得更加明显,而较暗区域的亮度也会有很大的提升。
示例开发步骤如下
(1)引入ConvolutionShader.js文件。
(2)新建BloomPass通道添加到组合器
关键代码如下:
1 function addBloomPass(){
2 var renderPass = new THREE.RenderPass(scene, camera); //新建RenderPass通道
3 var bloomPass = new THREE.BloomPass(2.5, 25, 0.1,1024); //新建BloomPass通道
4 //新建effectCopy通道
5 var effectCopy = new THREE.ShaderPass(THREE.CopyShader);
6 effectCopy.renderToScreen = true; //渲染到屏幕
7 composer = new THREE.EffectComposer(webGLRenderer); //新建效果组合器
8 composer.addPass(renderPass); //添加RenderPass通道
9 composer.addPass(bloomPass); //添加BloomPass通道
10 composer.addPass(effectCopy); //添加effectCopy通道
11 }
需要注意的是,由于BloomPass通道中不能直接将渲染结果呈现到屏幕上,而effectCopy通道没有任何特殊效果,只是输出处理结果。所以此方法中增加了新建、添加effectCopy通道的相关代码。
四、 DotScreenPass通道
其可以将若干个不同大小的斑点按照一定的对齐方式绘制到画面中,并且经过处理过的画面将变成黑白的,趣味感和复古感将大大提升。DotScreenPass通道中可以对center、angle、scale等属性进行修改,以此达到渲染需求。其使用如同上例,先引入文件,再创建通道添加到效果组合器 。
五 SSAOPass通道
其在渲染到纹理技术中是使用频率很高的。尤其在比较大的游戏场景中,使用SSAO通道进行渲染能大大增强场景的深度感和真实感,能让画面更细腻,让场景细节更加明显。在SSAOPass通道中,可以对renderToScreen、onlyA0、lumInfluence等属性进行修改,其使用如同上例,先引入文件,再创建通道添加到效果组合器 。
六、 ShaderPass通道
ShaderPass通道,由于该通道并没有渲染画面的功能,所以需要在新建渲染通道时编写合适的着色器并传入此渲染通道。Three.js引擎中带有实现各种效果的着色器,可以很方便地对其进行了解和应用,如果自带的着色器不能满足实际的开发需要,则还可自行开发出对应的着色器以完成特定效果的渲染。
示例使用自定义着色器进行渲染到纹理后,整个画面失去了原本的颜色并且具有了浮雕效果,具有很强的艺术气息。
开发步骤如下所示。
(1)进行自定义着色器部分的开发。其大体思路是将渲染到纹理之前的画面作为一张图片,然后使用实现浮雕效果的卷积进行数字图像处理操作,最终进行灰度化处理。
其具体代码如下所示
1 THREE.CustomShader = { //规定着色器的名称
2 uniforms: { /定义着色器中用到的uniforms变量
3 "tDiffuse": {type: "t", value: null}, //将渲染到纹理之前的画面作为一张图片
4 },
5 vertexShader: [ //定义顶点着色器
6 "varying vec2 vUv; ", //UV坐标数据
7 "void main() {", //顶点着色器的main方法
8 "vUv = uv; ", //将 UV坐标数据传入片元着色器
9 "gl_Position = projectionMatrix * modelViewMatrix * vec4( position, 1.0 ); ",
//顶点位置
10 "}"
11 ].join("\n"),
12 fragmentShader: [ //定义顶点着色器
13 "precision mediump float; //给出默认的浮点精度",
14 "varying vec2 vUv; //从顶点着色器传递过来的 UV坐标",
15 "uniform sampler2D tDiffuse; //纹理内容数据",
16 "void main(){", //片元着色器的main方法
17 //给出卷积内核中各个元素对应像素相对于待处理像素的纹理坐标偏移量"
18 "vec2 offset0=vec2(-1.0, -1.0); vec2 offset1=vec2(0.0, -1.0); ”,
19 "vec2 offset2=vec2(1.0, -1.0); vec2 offset3=vec2(-1.0,0.0); ”,
20 "vec2 offset4=vec2(0.0,0.0); vec2 offset5=vec2(1.0,0.0); ",
21 "vec2 offset6=vec2(-1.0,1.0); vec2 offset7=vec2(0.0,1.0); ”,
22 "vec2 offset8=vec2(1.0,1.0); ",
23 "const float scaleFactor = 1.0; //给出最终求和时的加权因子(为调整亮度)",
24 //卷积内核中各个位置的值"
25 "float kernelValue0 = 2.0; float kernelValue1 = 0.0; float kernelValue2 = 2.0; ",
26 "float kernelValue3 = 0.0; float kernelValue4 = 0.0; float kernelValue5 = 0.0; ",
27 "float kernelValue6 = 3.0; float kernelValue7 = 0.0; float kernelValue8 = -6.0; ",
28 "vec4 sum; //最终的颜色和",
29 //获取卷积内核中各个元素对应像素的颜色值"
30 "vec4 cTemp0, cTemp1, cTemp2, cTemp3, cTemp4, cTemp5, cTemp6, cTemp7, cTemp8; ",
31 "cTemp0=texture2D(tDiffuse, vUv.st + offset0.xy/512.0); ",
32 "cTemp1=texture2D(tDiffuse, vUv.st + offset1.xy/512.0); ",
33 "cTemp2=texture2D(tDiffuse, vUv.st + offset2.xy/512.0); ",
34 "cTemp3=texture2D(tDiffuse, vUv.st + offset3.xy/512.0); ",
35 "cTemp4=texture2D(tDiffuse, vUv.st + offset4.xy/512.0); ",
36 "cTemp5=texture2D(tDiffuse, vUv.st + offset5.xy/512.0); ",
37 "cTemp6=texture2D(tDiffuse, vUv.st + offset6.xy/512.0); ",
38 "cTemp7=texture2D(tDiffuse, vUv.st + offset7.xy/512.0); ",
39 "cTemp8=texture2D(tDiffuse, vUv.st + offset8.xy/512.0); ",
40 //颜色求和
41 "sum =kernelValue0*cTemp0+kernelValue1*cTemp1+kernelValue2*cTemp2+",
42 "kernelValue3*cTemp3+kernelValue4*cTemp4+kernelValue5*cTemp5+",
43 "kernelValue6*cTemp6+kernelValue7*cTemp7+kernelValue8*cTemp8; " ,
44 //进行灰度化处理
45 "float hd=(sum.r+sum.g+sum.b)/3.0; ",
46 "gl_FragColor = vec4(hd) * scaleFactor; //进行亮度加权后将最终颜色传递给管线",
47 "}"
48 ].join("\n")
49 };
❑ 第5~10行为顶点着色器部分的相关代码。从中可以看出,它并没有特殊功能,只是计算出顶点位置后将UV坐标数据传入片元着色器中。
❑ 第18~39行为卷积计算的相关代码。其中第25~27行为实现浮雕效果的卷积内核。开发人员还可以更改卷积内核,开发出平滑过滤、边缘检测、锐化处理等高级特效。
❑ 第41~43行为对9个采样点进行卷积计算的相关代码。从中可以看出,其特殊的地方在于某一片元的颜色是由周围多个片元共同决定的。
❑ 第44~47行为进行灰度化处理的相关代码。其大体思路是对片元的R、G、B值求取平均值,并将计算结果赋值给此片元的各个颜色通道。
(2)首先引入custom-shader.js文件,然后在创建ShaderPass通道时传入上面的着色器,最后向效果组合器中添加此通道,
具体代码如下所示:
1 function addShaderPass(){//添加使用自定义着色器的ShaderPass通道
2 //新建RenderPass通道
3 var renderPass=new THREE.RenderPass(scene, camera);
4 //新建绑定自定义着色器的ShaderPass通道
5 var shaderPass=new THREE.ShaderPass(THREE. CustomShader );
6 //新建effectCopy通道
7 var effectCopy=new THREE.ShaderPass(THREE.CopyShader);
8 effectCopy.renderToScreen = true; //将结果呈现到屏幕上
9 composer = new THREE.EffectComposer(webGLRenderer); //新建效果组合器
10 composer.addPass(renderPass); //添加RenderPass通道
11 composer.addPass(shaderPass); //添加ShaderPass通道
12 composer.addPass(effectCopy); //添加effectCopy通道
13 }
通过上面的代码可以看出,使用自定义着色器实现渲染到纹理时,只需将其名称传入ShaderPass通道即可,这与前面的方式十分类似。如需要使用引擎中自带的着色器,则可直接用其名称替换掉第5行代码中的“THREE. CopyShader”。