简体   繁体   中英

Getting SSAO shader working with SkinnedMesh

I've been attempting to get the SSAO post-processing shader to work with the latest (r77) version of three.js. I've been using the EffectComposer, with the code entirely duplicated from the example page here:

http://threejs.org/examples/webgl_postprocessing_ssao.html

The relevant code being:

var renderPass = new THREE.RenderPass( Engine.scene, Engine.camera );
ssaoPass = new THREE.ShaderPass( THREE.SSAOShader );
ssaoPass.renderToScreen = true;
// ...various ShaderPass setup parameters

Engine.effectComposer = new THREE.EffectComposer( Engine.renderer );

Engine.effectComposer.addPass(renderPass);
Engine.effectComposer.addPass(ssaoPass);

The issue I have been having is that that SSAO doesn't seem to work with SkinnedMeshes. It seems to take into account the position of the meshes before the skinning calculations are performed.

It looks like this:

Problem with SSAO on SkinnedMesh

Does anyone have any experience with this on the latest version? I've looked all over the place, but can't find any documentation about how to start fixing this at all.

I found mention of a fix for this in another SO post ( ThreeJS SSAO Shader w/ Skinned/Animated Models ), but the solution has been deprecated.

Thanks in advance, and happy to go into more detail if needed.

As requested, here is the complete code for the simple demo page :

<!DOCTYPE html>
<html>
    <head>
        <meta charset="UTF-8">

        <script type="text/javascript" src="js/libs/jquery.min.js"></script>
        <script type="text/javascript" src="js/libs/three.min.js"></script>

        <script type="text/javascript" src="js/libs/postprocessing/CopyShader.js"></script>
        <script type="text/javascript" src="js/libs/postprocessing/EffectComposer.js"></script>
        <script type="text/javascript" src="js/libs/postprocessing/MaskPass.js"></script>
        <script type="text/javascript" src="js/libs/postprocessing/RenderPass.js"></script>
        <script type="text/javascript" src="js/libs/postprocessing/ShaderPass.js"></script>
        <script type="text/javascript" src="js/libs/postprocessing/DotScreenShader.js"></script>
        <script type="text/javascript" src="js/libs/postprocessing/SSAOShader.js"></script>

        <link rel="stylesheet" type="text/css" href="demo.css" />
    </head>
    <body>
        <div id="demo-container"></div>
    </body>
</html>
<script>

$(document).ready(function() {

    window.doPostPro = 0;

    scene = new THREE.Scene();

    camera = new THREE.PerspectiveCamera(75, window.innerWidth/window.innerHeight, 1, 1000);
    camera.position.set(0, 200, 200);
    camera.lookAt(new THREE.Vector3(0, 0, 0));

    clock = new THREE.Clock();

    // Setup the renderer.
    renderer = new THREE.WebGLRenderer({
        antialias: true
    });
    renderer.setPixelRatio( window.devicePixelRatio );
    renderer.setSize( window.innerWidth, window.innerHeight );
    renderer.setClearColor( 0xFFFFFF );

    function setupPostProcessing() {

        // Setup render pass
        var renderPass = new THREE.RenderPass( scene, camera );

        // Setup depth pass
        depthMaterial = new THREE.MeshDepthMaterial();
        depthMaterial.depthPacking = THREE.RGBADepthPacking;
        depthMaterial.blending = THREE.NoBlending;

        depthRenderTarget = new THREE.WebGLRenderTarget( window.innerWidth, window.innerHeight, {
            minFilter:      THREE.LinearFilter, 
            magFilter:      THREE.LinearFilter,
            stencilBuffer:  true
        });

        // Setup SSAO pass
        ssaoPass = new THREE.ShaderPass( THREE.SSAOShader );
        ssaoPass.renderToScreen = true;
        //ssaoPass.uniforms[ "tDiffuse" ].value will be set by ShaderPass
        ssaoPass.uniforms[ "tDepth" ].value = depthRenderTarget.texture;
        ssaoPass.uniforms[ 'size' ].value.set( window.innerWidth, window.innerHeight );
        ssaoPass.uniforms[ 'cameraNear' ].value = camera.near;
        ssaoPass.uniforms[ 'cameraFar' ].value = camera.far;
        //ssaoPass.uniforms[ 'onlyAO' ].value = ( postprocessing.renderMode == 1 );
        ssaoPass.uniforms[ 'aoClamp' ].value = 0.3;
        ssaoPass.uniforms[ 'lumInfluence' ].value = 0.5;

        // Add pass to effect composer
        effectComposer = new THREE.EffectComposer( renderer );
        effectComposer.addPass( renderPass );
        effectComposer.addPass( ssaoPass );
    };

    setupPostProcessing();

    // Load the mesh.
    var loader = new THREE.JSONLoader();
    loader.load( "js/zombie.js", function( geometry, materials ) {

        var originalMaterial = materials[ 0 ];
        originalMaterial.skinning = true;

        geometry.computeVertexNormals();

        var mesh = new THREE.SkinnedMesh(geometry, originalMaterial);

        window.animMixer = new THREE.AnimationMixer(mesh);
        var animAction = animMixer.clipAction(geometry.animations[0]);
        animAction.play();

        scene.add(mesh);
    });

    var ambientLight = new THREE.AmbientLight( 0xCCCCCC );
    scene.add( ambientLight );

    $("#demo-container").append( renderer.domElement );

    function render() {

        if ( doPostPro ) {

            // Render depth into depthRenderTarget
            scene.overrideMaterial = depthMaterial;
            renderer.render( scene, camera, depthRenderTarget, true );

            // Render renderPass and SSAO shaderPass
            scene.overrideMaterial = null;
            effectComposer.render();
        }
        else {

            renderer.render( scene, camera );
        }
    }

    function animate() {

        requestAnimationFrame( animate );

        if ( window.animMixer != undefined ) {

            var deltaTime = clock.getDelta();
            animMixer.update(deltaTime);            
        }

        render();
    }

    animate();

    $(document).keyup(function(e) {

        // P is pressed.
        if ( e.which == 80 ) {

            window.doPostPro = !window.doPostPro;
        }
    });

});



</script>

If you are skinning, and using this pattern in your render loop

// Render depth into depthRenderTarget
scene.overrideMaterial = depthMaterial;
renderer.render( scene, camera, depthRenderTarget, true );

you have to make sure to set

depthMaterial.skinning = true;

Thing is, if there are other elements in the scene that are not skinned, doing so will throw errors. In which case, this may be a three.js design issue that will have to be addressed.

three.js r.77

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM