Three.js: gl.clear not working with cached state masks

Created on 27 Feb 2018  路  6Comments  路  Source: mrdoob/three.js

Description of the problem

gl.clear would not clear render target if state mask is currently set (i.e. by previous material)

https://github.com/mrdoob/three.js/blob/e2e6b079e00f811cf41755ee8342914ed77ea107/src/renderers/WebGL2Renderer.js#L101

Three.js version
  • [ ] Dev
  • [x] r90
  • [ ] ...
Browser
  • [x] All of them
  • [ ] Chrome
  • [ ] Firefox
  • [ ] Internet Explorer
OS
  • [x] All of them
  • [ ] Windows
  • [ ] macOS
  • [ ] Linux
  • [ ] Android
  • [ ] iOS
Hardware Requirements (graphics card, VR Device, ...)

Most helpful comment

Can you please provide sufficient context so the issue can be reproduced? A live demo would be helpful.

All 6 comments

Can you please provide sufficient context so the issue can be reproduced? A live demo would be helpful.

Our case is a bit complex, but you can easily reproduce the issue in render-to-texture webgl example (code below).
We are using renderBufferDirect along with regular scene rendering to prepare some small stuff (lots of dynamic tiles) in the background and avoid scene graph traversal. The result is tile material override the masks and clear not working

            if ( ! Detector.webgl ) Detector.addGetWebGLMessage();

            var container, stats;

            var cameraRTT, camera, sceneRTT, sceneScreen, scene, renderer, zmesh1, zmesh2;

            var mouseX = 0, mouseY = 0;

            var windowHalfX = window.innerWidth / 2;
            var windowHalfY = window.innerHeight / 2;

            var rtTexture, material, quad;

            var delta = 0.01;

            //n3d:
            var noDepthMaterial = null
            var noDepthGeometry = null
            var noDepthMesh = null

            init();
            animate();

            function init() {

                container = document.getElementById( 'container' );

                camera = new THREE.PerspectiveCamera( 30, window.innerWidth / window.innerHeight, 1, 10000 );
                camera.position.z = 100;

                cameraRTT = new THREE.OrthographicCamera( window.innerWidth / - 2, window.innerWidth / 2, window.innerHeight / 2, window.innerHeight / - 2, -10000, 10000 );
                cameraRTT.position.z = 100;

                //

                scene = new THREE.Scene();
                sceneRTT = new THREE.Scene();
                sceneScreen = new THREE.Scene();

                var light = new THREE.DirectionalLight( 0xffffff );
                light.position.set( 0, 0, 1 ).normalize();
                sceneRTT.add( light );

                light = new THREE.DirectionalLight( 0xffaaaa, 1.5 );
                light.position.set( 0, 0, -1 ).normalize();
                sceneRTT.add( light );

                rtTexture = new THREE.WebGLRenderTarget( window.innerWidth, window.innerHeight, { minFilter: THREE.LinearFilter, magFilter: THREE.NearestFilter, format: THREE.RGBFormat } );

                material = new THREE.ShaderMaterial( {

                    uniforms: { time: { value: 0.0 } },
                    vertexShader: document.getElementById( 'vertexShader' ).textContent,
                    fragmentShader: document.getElementById( 'fragment_shader_pass_1' ).textContent

                } );

                var materialScreen = new THREE.ShaderMaterial( {

                    uniforms: { tDiffuse: { value: rtTexture.texture } },
                    vertexShader: document.getElementById( 'vertexShader' ).textContent,
                    fragmentShader: document.getElementById( 'fragment_shader_screen' ).textContent,

                    depthWrite: false

                } );

                var plane = new THREE.PlaneBufferGeometry( window.innerWidth, window.innerHeight );

                quad = new THREE.Mesh( plane, material );
                quad.position.z = -100;
                sceneRTT.add( quad );

                var geometry = new THREE.TorusGeometry( 100, 25, 15, 30 );

                var mat1 = new THREE.MeshPhongMaterial( { color: 0x555555, specular: 0xffaa00, shininess: 5 } );
                var mat2 = new THREE.MeshPhongMaterial( { color: 0x550000, specular: 0xff2200, shininess: 5 } );

                zmesh1 = new THREE.Mesh( geometry, mat1 );
                zmesh1.position.set( 0, 0, 100 );
                zmesh1.scale.set( 1.5, 1.5, 1.5 );
                sceneRTT.add( zmesh1 );

                zmesh2 = new THREE.Mesh( geometry, mat2 );
                zmesh2.position.set( 0, 150, 100 );
                zmesh2.scale.set( 0.75, 0.75, 0.75 );
                sceneRTT.add( zmesh2 );

                quad = new THREE.Mesh( plane, materialScreen );
                quad.position.z = -100;
                sceneScreen.add( quad );

                var n = 5,
                    geometry = new THREE.SphereGeometry( 10, 64, 32 ),
                    material2 = new THREE.MeshBasicMaterial( { color: 0xffffff, map: rtTexture.texture } );

                for( var j = 0; j < n; j ++ ) {

                    for( var i = 0; i < n; i ++ ) {

                        var mesh = new THREE.Mesh( geometry, material2 );

                        mesh.position.x = ( i - ( n - 1 ) / 2 ) * 20;
                        mesh.position.y = ( j - ( n - 1 ) / 2 ) * 20;
                        mesh.position.z = 0;

                        mesh.rotation.y = - Math.PI / 2;

                        scene.add( mesh );

                    }

                }

                //n3d:
                var vertices = new Float32Array( [
                    -1.0, -1.0,  1.0,
                     1.0, -1.0,  1.0,
                     1.0,  1.0,  1.0,

                     1.0,  1.0,  1.0,
                    -1.0,  1.0,  1.0,
                    -1.0, -1.0,  1.0
                ] );
                noDepthGeometry = new THREE.BufferGeometry()
                noDepthGeometry.addAttribute( 'position', new THREE.BufferAttribute( vertices, 3 ) )
                noDepthMaterial = new THREE.MeshBasicMaterial( { color: 0xffffff } );
                noDepthMaterial.depthWrite = false
                noDepthMesh = new THREE.Mesh( noDepthGeometry, noDepthMaterial )
                noDepthMesh.position.z = 10

                renderer = new THREE.WebGLRenderer();
                renderer.setPixelRatio( window.devicePixelRatio );
                renderer.setSize( window.innerWidth, window.innerHeight );
                renderer.autoClear = false;

                document.body.appendChild( renderer.domElement );

                stats = new Stats();
                document.body.appendChild( stats.dom );

                document.addEventListener( 'mousemove', onDocumentMouseMove, false );

            }

            function onDocumentMouseMove( event ) {

                mouseX = ( event.clientX - windowHalfX );
                mouseY = ( event.clientY - windowHalfY );

            }

            //

            function animate() {

                requestAnimationFrame( animate );

                render();
                stats.update();

            }

            function render() {

                var time = Date.now() * 0.0015;

                camera.position.x += ( mouseX - camera.position.x ) * .05;
                camera.position.y += ( - mouseY - camera.position.y ) * .05;

                camera.lookAt( scene.position );

                if ( zmesh1 && zmesh2 ) {

                    zmesh1.rotation.y = - time;
                    zmesh2.rotation.y = - time + Math.PI / 2;

                }

                if ( material.uniforms.time.value > 1 || material.uniforms.time.value < 0 ) {

                    delta *= -1;

                }

                material.uniforms.time.value += delta;

                renderer.clear();

                // Render first scene into texture

                renderer.render( sceneRTT, cameraRTT, rtTexture, true );

                // Render full screen quad with generated texture

                renderer.render( sceneScreen, cameraRTT );

                //n3d:
                scene.onAfterRender = () => {
                    renderer.renderBufferDirect( camera, null, noDepthGeometry, noDepthMaterial, noDepthMesh, null )
                }

                // Render second scene to screen
                // (using first scene as regular texture)

                renderer.render( scene, camera );

            }

I've tried to create a fiddle of your code but i've realized you've missed to copy the corresponding vertex and fragment shader.

Please setup a live example so it's easier for us to analyze your problem: https://jsfiddle.net/f2Lommf5/

I revisited this issue and I think there is a bug in three.js. The problem is that the renderer executes Scene.onAfterRender() too late. If users performs rendering operations in the callback, they overwrite the final settings for the depth and color buffer (see comment):

https://github.com/mrdoob/three.js/blob/20378c580057b6dde297dddc3f9e2064485a9757/src/renderers/WebGLRenderer.js#L1220-L1228

The problem is the OP performs a rendering with a material where depthWrite is set to false and thus overwrites the setting. Subsequent clears do not work properly anymore. The solution is to execute Scene.onAfterRender() a little bit earlier.

@szhuchkov Sorry that it takes over a year to fix the issue 馃槄 . Better late than never^^!

Was this page helpful?
0 / 5 - 0 ratings