定製着色器和渲染後期處理

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。首先看下運行效果:

image

    上圖標左上角運用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。

相關文章
相關標籤/搜索