定制着色器和渲染后期處理


1.設置后期處理

    設置Three.js庫為后期處理做准備,我們需要通過以下步驟對當前的配置進行修改:

    1)創建一個EffectComposer(效果組合器)對象,然后在該對象上添加后期處理通道。

    2)配置該對象,使它可以渲染我們的場景,並應用額外的后期處理步驟。

    3)在render循環中,使用EffectComposer渲染場景、應用通道,並輸出結果。

   要使用后期處理,需要引入一些javaSscript文件。這些文件可以在Three.js發布包里找到。路徑是examples/js/postprocessing和example/js/shaders。至少包含下面的文件:

<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,如下所示:

var composer = new THREE.EffectComposer(webGLRenderer);

    接下來我們要在這個組合器中添加各種通道。第一個要加入的通道是RenderPass。這個通道會渲染場景,但不會講渲染結果輸出到屏幕上。

var renderPass = new THREE.RenderPass(scene, camera);
...
var composer = new THREE.EffectComposer(webGLRenderer);
 composer.addPass(renderPass);

    接下來我們要添加一個可以將結果輸出到屏幕上的通道。這里使用FilmPass,我們想創建該對象,然后添加到效果組合器中。代碼如下:

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。最后,還需要修渲染循環:

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輸出渲染。composer2、composer3、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。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM