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。