1.设置后期处理javascript
设置Three.js库为后期处理作准备,咱们须要经过如下步骤对当前的配置进行修改:html
1)建立一个EffectComposer(效果组合器)对象,而后在该对象上添加后期处理通道。java
2)配置该对象,使它能够渲染咱们的场景,并应用额外的后期处理步骤。web
3)在render循环中,使用EffectComposer渲染场景、应用通道,并输出结果。app
要使用后期处理,须要引入一些javaSscript文件。这些文件能够在Three.js发布包里找到。路径是examples/js/postprocessing和example/js/shaders。至少包含下面的文件:composer
<script type="text/javascript" src="../libs/postprocessing/ShaderPass.js"></script> <script type="text/javascript" src="../libs/shaders/CopyShader.js"></script> <script type="text/javascript" src="../libs/postprocessing/EffectComposer.js"></script> <script type="text/javascript" src="../libs/postprocessing/MaskPass.js"></script> <script type="text/javascript" src="../libs/postprocessing/FilmPass.js"></script> <script type="text/javascript" src="../libs/shaders/FilmShader.js"></script> <script type="text/javascript" src="../libs/postprocessing/RenderPass.js"></script>
首先咱们建立一个EffectComposer对象,你能够在这个对象的构造函数里出入WebGL-Renderer,以下所示:dom
var composer = new THREE.EffectComposer(webGLRenderer);
接下来咱们要在这个组合器中添加各类通道。第一个要加入的通道是RenderPass。这个通道会渲染场景,但不会讲渲染结果输出到屏幕上。函数
var renderPass = new THREE.RenderPass(scene, camera); ... var composer = new THREE.EffectComposer(webGLRenderer); composer.addPass(renderPass);
接下来咱们要添加一个能够将结果输出到屏幕上的通道。这里使用FilmPass,咱们想建立该对象,而后添加到效果组合器中。代码以下:post
var effectFilm = new THREE.FilmPass(0.8, 0.325, 256, false); effectFilm.renderToScreen = true; var composer = new THREE.EffectComposer(webGLRenderer); composer.addPass(effectFilm);
正如你所看到的,咱们建立了一个FilmPass对象,并将它的renderToScreen属性设置为true。最后,还须要修渲染循环:ui
function render(){ stats.update(); var delta = clock.getDelta(); orbitControl.update(delta); sphere.rotation.y += 0.002; requestAnimationFrame(render); composer.render(delta); }
这个代码里咱们移出了“webGLRenderer.render(scene, camera);”,用“composer.render(delta)”代替。这将调用EffectComposer的render()函数。因为咱们已经将FilmPass的renderToScreen属性设置为true,因此FilmPass的结果将会输出到屏幕上。
2.后期处理通道
Three.js库提供了几个后期处理通道,你能够直接将其添加到EffectComposer对象。下表是这些通道的概览。
通道/描述
BloomPass/该通道会使得明亮区域渗入较暗的区域。模拟相机找到过多亮点的情形
DotScreenPass/将一层黑点贴到表明原始图片的屏幕上
FilmPass/经过扫描线和失真模拟电视屏幕
MaskPass/在当前图片上贴一层掩膜,后续通道只会影响被贴的区域
RenderPass/该通道在指定的场景和相机的基础上渲染出一个新场景
ShaderPass/使用该通道你能够传入一个自定义的着色器,用来生成高级的、自定义的后期处理通道
TexturePass/该通道能够将效果组合器的当前状态保存为一个纹理,而后能够在其余EffectComposer对象中将该纹理做为输入参数
下面的例子使用了以上通道的中的BloomPass、DotScreenPass、FilmPass、RenderPass、ShaderPass、TexturePass。首先看下运行效果:
上图标左上角运用BloomPass、右上角运用FilmPass、左下角运用DotScreenPass、右下角运用原始渲染结果。渲染处理代码以下:
var renderPass = new THREE.RenderPass(scene, camera); //保存渲染结果,但不会输出到屏幕 var effectCopy = new THREE.ShaderPass(THREE.CopyShader); //传入了CopyShader着色器,用于拷贝渲染结果 effectCopy.renderToScreen = true; //设置输出到屏幕上 var bloomPass = new THREE.BloomPass(3, 25, 5.0, 256); //BloomPass通道效果 var effectFilm = new THREE.FilmPass(0.8, 0.325, 256, false); //FilmPass通道效果 effectFilm.renderToScreen = true; //设置输出到屏幕上 var dotScreenPass = new THREE.DotScreenPass(); // DotScrrenPass通道效果 //渲染目标 var composer = new THREE.EffectComposer(webGLRenderer); composer.addPass(renderPass); composer.addPass(effectCopy); var renderScene = new THREE.TexturePass(composer.renderTarget2); //左下角 var composer1 = new THREE.EffectComposer(webGLRenderer); composer1.addPass(renderScene); composer1.addPass(dotScreenPass); composer1.addPass(effectCopy); //右下角 var composer2 = new THREE.EffectComposer(webGLRenderer); composer2.addPass(renderScene); composer2.addPass(effectCopy); //左上角 var composer3 = new THREE.EffectComposer(webGLRenderer); composer3.addPass(renderScene); composer3.addPass(bloomPass); composer3.addPass(effectCopy); //右上角 var composer4 = new THREE.EffectComposer(webGLRenderer); composer4.addPass(renderScene); composer4.addPass(effectFilm);
代码原理比较简单,composer的做用是渲染最原始的效果,传入了renderPass和ShaderPass,renderPass运来建立一个新场景,ShaderPass用来输出结果到屏幕。接下来建立了一个类型为TexturePass的通道对象renderScene。能够将当前状态保存一份做为纹理。供后面的几个compoer使用。
composer1首先使用renderScene建立新场景,而后添加dotScreenPass通道,最后使用effectCopy输出渲染。composer二、composer三、composer4类似。设置了处理通道后,在每次循环渲染时还得分别调用每一个composer的render函数从新渲染:
function render() { stats.update(); //sphere.rotation.y=step+=0.01; var delta = clock.getDelta(); orbitControls.update(delta); sphere.rotation.y += 0.002; // render using requestAnimationFrame requestAnimationFrame(render); webGLRenderer.autoClear = false; webGLRenderer.clear(); webGLRenderer.setViewport(0, 0, 2 * halfWidth, 2 * halfHeight); composer.render(delta); webGLRenderer.setViewport(0, 0, halfWidth, halfHeight); composer1.render(delta); webGLRenderer.setViewport(halfWidth, 0, halfWidth, halfHeight); composer2.render(delta); webGLRenderer.setViewport(0, halfHeight, halfWidth, halfHeight); composer3.render(delta); webGLRenderer.setViewport(halfWidth, halfHeight, halfWidth, halfHeight); composer4.render(delta); }
3.各类通道属性
FilmPass用来建立相似电视的效果。通道属性的属性可经过构造函数传入,也能够直接修改uiniforms上对应的属性。属性说明以下:
属性/描述
noiseIntensity/经过该属性能够控制屏幕的颗粒程度
scanlineIntensity/FilmPass会在屏幕上添加一些扫描线。经过该属性,能够指定扫描线的显著程度
scanlineCount/该属性能够控制显示出来的扫描线数量
grayscale/若是设置为true,输出结果将会转换为灰度图
BoomPass用来在场景中添加泛光效果。属性可经过构造函数传入,也可直接修改uniforms属性。属性说明以下:
属性/描述
Strength/该属性定义的是泛光效果强度。值越高,明亮区域越明亮。并且渗入较暗区域的也就越多
kernelSize/该属性控制的是泛光效果的偏移量
sigma/能够控制泛光的锐利程度。值越高,泛光越模糊
resolution/泛光效果的解析图。值过低,那么结果的方块会比较严重
DotSreenPass用来将场景输出成点集。属性以下:
center/经过center属性,能够微调点的偏移量
angle/经过angle,能够更改对齐方式
scale/该属性设置全部点的大小。scale越小,则点越大
4.使用掩膜的高级效果组合器
Three.js库具备在特定区域应用通道的能力。咱们采起以下步骤实现一个特定区域效果。例以下图的地图和火星。咱们想在火星上应用一个彩色效果、在地球上应用褐色效果。咱们采起以下步骤:
1)建立一个座位背景图的场景。
2)建立一个场景,里边有一个看上去像地球的球体。
3)建立一个场景,里边有一个看上去像火星的球体。
4)建立一个EffectComposer对象,将这三个场景渲染到一个图片里。
5)在渲染成火星的球体上应用一个彩色效果。
6)在渲染成地球的球体上应用褐色效果。
下面是初始化的代码:
var sceneEarth = new THREE.Scene(); var sceneMars = new THREE.Scene(); var sceneBG = new THREE.Scene(); var camera = new THREE.PerspectiveCamera(45, window.innerWidth/window.innerHeight, 0.1, 1000); var cameraBG = new THREE.OrthographicCamera(-window.innerWidth, window.innerWidth, window.innerHeight, -window.innerHeight, -10000, 10000); cameraBG.position.z = 50; var webGLRenderer = new THREE.WebGLRenderer(); webGLRenderer.setClearColor(new THREE.Color(0x000, 1.0)); webGLRenderer.setSize(window.innerWidth, window.innerHeight); webGLRenderer.shadowMapEnabled = true; var sphere = createEarthMesh(new THREE.SphereGeometry(10, 40, 40)); sphere.position.x = -10; var sphere2 = createMarshMesh(new THREE.SphereGeometry(5, 40, 40)); sphere2.position.x = 10; sceneEarth.add(sphere); sceneMars.add(sphere2); camera.position.set(-10, 15, 25); camera.lookAt(new THREE.Vector3(0, 0, 0)); var orbitControls = new THREE.OrbitControls(camera); orbitControls.autoRotate = false; var clock = new THREE.Clock(); var ambi = new THREE.AmbientLight(0x181818); var ambi2 = new THREE.AmbientLight(0x181818); sceneEarth.add(ambi); sceneMars.add(ambi2); var spotLight = new THREE.DirectionalLight(0xffffff); spotLight.position.set(550, 100, 550); spotLight.intensity = 0.6; var spotLight2 = new THREE.DirectionalLight(0xffffff); spotLight2.position.set(550, 100, 550); spotLight2.intensity = 0.6; sceneEarth.add(spotLight); sceneMars.add(spotLight2); var materialColor = new THREE.MeshBasicMaterial({ map: THREE.ImageUtils.loadTexture("../assets/textures/starry-deep-outer-space-galaxy.jpg"), depthWrite: false }); var bgPlane = new THREE.Mesh(new THREE.PlaneGeometry(1, 1), materialColor); bgPlane.position.z = -100; bgPlane.scale.set(window.innerWidth * 2, window.innerHeight * 2, 1); sceneBG.add(bgPlane); // add the output of the renderer to the html element document.getElementById("WebGL-output").appendChild(webGLRenderer.domElement); var bgPass = new THREE.RenderPass(sceneBG, cameraBG); var renderPass = new THREE.RenderPass(sceneEarth, camera); renderPass.clear = false; var renderPass2 = new THREE.RenderPass(sceneMars, camera); renderPass2.clear = false; var effectCopy = new THREE.ShaderPass(THREE.CopyShader); effectCopy.renderToScreen = true; var clearMash = new THREE.ClearMaskPass(); var earthMask = new THREE.MaskPass(sceneEarth, camera); var marsMask = new THREE.MaskPass(sceneMars, camera); var effectSepia = new THREE.ShaderPass(THREE.SepiaShader); effectSepia.uniforms["amount"].value = 0.8; var effectColorify = new THREE.ShaderPass(THREE.ColorifyShader); effectColorify.uniforms["color"].value.setRGB(0.5, 0.5, 1); var composer = new THREE.EffectComposer(webGLRenderer); composer.renderTarget1.stencilBuffer = true; composer.renderTarget2.stencilBuffer = true; composer.addPass(bgPass); // 添加背景渲染新场景通道 composer.addPass(renderPass); // 添加地球渲染的新场景通道 composer.addPass(renderPass2); // 添加月球渲染的新场景通道 composer.addPass(marsMask); // 添加月球掩膜通道,之后全部的通道效果都只对月球有效,直到clearMask通道 composer.addPass(effectColorify); // 添加颜色着色器通道 composer.addPass(clearMash); //清理掩膜通道 composer.addPass(earthMask); //添加地图掩膜通道,以后的全部通道效果都只对月球有效,直到clearMash通道 composer.addPass(effectSepia); // 添加一种自定义着色器通道 composer.addPass(clearMash); // 清理掩膜通道 composer.addPass(effectCopy);
下面是渲染代码:
function render() { webGLRenderer.autoClear = false; stats.update(); var delta = clock.getDelta(); orbitControls.update(delta); sphere.rotation.y += 0.002; sphere2.rotation.y += 0.002; requestAnimationFrame(render); composer.render(delta); }
5.用ShaderPass定制效果
经过ShaderPass,咱们能够传递一个自定义的着色器,将大量额外的效果应用到场景中。Three.js扩展的着色器主要分为简单着色器、模糊着色器、高级效果着色器。
简单着色器列表:
MirrorShader/该着色器能够为部分屏幕建立镜面效果
HueStaturationShader/该着色器能够改变颜色的色调和饱和度
VignetteShader/该着色器能够添加晕映效果。该效果能够在图片中央的周围显示黑色的边框
ColorCorrectionShader/经过这个着色器,你能够调整颜色的分布
RGBShiftSader/该着色器能够将构成颜色的红、绿、蓝分开
BrightnessContrasShader/该着色器能够更改图片的亮度和对比度
ColorifyShader/能够在屏幕上蒙上一层颜色
SepiaShader/能够在屏幕上建立出相似乌贼墨的效果
模糊效果着色器:
HorizontalBlurShader和VerticalBlurShader/这两个着色器在场景中应用模糊效果
HorizontalTiltShiftShader和VerticalTiltShiftShader/这两个着色器能够建立出移轴效果。在移轴效果中只有部分图片显示得比较锐利,从而建立出一个看上去像是微缩景观的场景
TriangleBlurShader/该着色器使用基于三角形的方法,咱场景中应用模糊效果
高级效果的着色器:
BleachBypassShader/该着色器能够建立一种漂白效果。在该效果下,图片上像素是镀了一层银
EdgeShader/该着色器能够探测图片中的锐利边界,并突出显示这些边界
FXAAShader/该着色器能够在后期处理阶段应用锯齿效果。若是在渲染抗锯齿影响效率,那么久可使用该着色器
FocusShader/这是一个简单的着色器,其结果是中央区域渲染得比较锐利,但周围比较模糊
6.定制自定义着色器
为Three.js库建立自定义的着色器,须要实现两个组件:vertexShader和fragmentShader。组件vertexShader能够用来调整每一个顶点的位置,组件fragmentShader能够历来决定每一个像素的颜色。对于后期处理着色器来讲,咱们只要实现fragmentShader便可,而后使用Three.js提供的额、默认的vertexShader。
咱们以定制一个灰度着色器为例。首先要建立一个js文件,存放着色器源代码,这里咱们命名一个custom-shader.js文件。内容以下:
THREE.CustomGrayScaleShader = { uniforms: { "tDiffuse": {type: "t", value: null}, "rPower": {type: "f", value: 0.2126}, "gPower": {type: "f", value: 0.7152}, "bPower": {type: "f", value: 0.0722} }, vertexShader: [ "varying vec2 vUv;", "void main(){", "vUv = uv;", "gl_Position = projectionMatrix * modelViewMatrix * vec4(position, 1.0);", "}" ].join("\n"), fragmentShader: [ "uniform float rPower;", "uniform float gPower;", "uniform float bPower;", "uniform sampler2D tDiffuse;", "varying vec2 vUv;", "void main(){", "vec4 texel = texture2D(tDiffuse, vUv);", "float gray = texel.r * rPower + texel.g * gPower + texel.b * bPower;", "gl_FragColor = vec4(vec3(gray), texel.a);", "}" ].join("\n") }
咱们为Threee.js建立了一个叫作CustomGrayScaleShader的自定义着色器该对象包含uniforms、vertexShader和fragmentShader三个属性。uniforms定义了经过javascript外部传入的变量,vertexShader定义了顶点着色器,fragmentShader定义了片元着色器代码。
这里须要注意的是顶点着色器中的uv变量,是着色器代码的内部变量,表示纹理上的texel(纹理上的像素),铜鼓varying vec2 vUv变量传递给片元着色器。着色器代码定义好后,咱们就能够经过如下形式在javascript代码中使用了:
var renderPass = new THREE.RenderPass(scene, camera); var effectCopy = new THREE.ShaderPass(THREE.CopyShader); effectCopy.renderToScreen = true; var shaderPass = new THREE.ShaderPass(THREE.CustomGrayScaleShader); shaderPass.enabled = false; var bitPass = new THREE.ShaderPass(THREE.CustomBitShader); bitPass.enabled = false; var composer = new THREE.EffectComposer(webGLRenderer); composer.addPass(renderPass); composer.addPass(shaderPass); composer.addPass(bitPass); composer.addPass(effectCopy);
代码中建立了两个着色器通道,分别适合shaderPass和bitPass。shaderPass正好使用的是咱们建立的自定义着色器THREE.CustomGrayScaleShader。