1
votes

I'm attempting to adopt the threejs example for wireframes ( https://threejs.org/examples/?q=wire#webgl_materials_wireframe ) using WebGL instancing.

This simple reproduction codepen ( https://codepen.io/ubermario/pen/gzByjP?editors=1000 ) shows that a wireframed cube is rendered when a 'center' attribute is passed to the vertex/fragment shaders with THREE.BufferAttribute.

However, it is not renedered as wireframe when the 'center' attribute is passed to the same shaders with THREE.InstancedBufferAttribute.

No errors are generated. Ideas?

<html lang="en">
    <head>
        <title>Adopted from three.js webgl - materials - wireframe</title>
        <meta charset="utf-8">
        <meta name="viewport" content="width=device-width, user-scalable=no, minimum-scale=1.0, maximum-scale=1.0">
        <style>
            body {
                margin: 0px;
                background-color: #000000;
                overflow: hidden;
            }
        </style>
    </head>
    <body>

  <script src="https://cdnjs.cloudflare.com/ajax/libs/three.js/r83/three.js"></script>  

        <script type="x-shader/x-vertex" id="vertexShader"> 
            attribute vec3 center;
            varying vec3 vCenter;

            void main() {
                vCenter = center;
                gl_Position = projectionMatrix * modelViewMatrix * vec4( position, 1.0 ); 
            }
        </script>

        <script type="x-shader/x-fragment" id="fragmentShader">
            varying vec3 vCenter;

            float edgeFactorTri() {
                vec3 d = fwidth( vCenter.xyz ); 
                vec3 a3 = smoothstep( vec3( 0.0 ), d * 1.5, vCenter.xyz );  
                return min( min( a3.x, a3.y ), a3.z );
            }

            void main() {           
                gl_FragColor.rgb = mix( vec3( 1.0 ), vec3( 0.0 ), edgeFactorTri() );                
                gl_FragColor.a = 1.0;   
            }
        </script>

        <script>

            var camera, scene, renderer;

            init();

            renderer.render( scene, camera );

            function init() {

                var bufferGeometry, material, mesh;
                camera = new THREE.PerspectiveCamera( 40, window.innerWidth / window.innerHeight, 1, 2000 );
                camera.position.z = 700;
                scene = new THREE.Scene();

                var geometry1 =  new THREE.BoxBufferGeometry( 100, 100,100 );
                geometry1.addAttribute( 'center', new THREE.BufferAttribute( fnGetFloat32ArrayCenters( geometry1 ), 3 ) );

                var material_1 = new THREE.ShaderMaterial( {
                        uniforms: {},                             
                        vertexShader: document.getElementById( 'vertexShader' ).textContent,
                        fragmentShader: document.getElementById( 'fragmentShader' ).textContent

                    } );
                material_1.extensions.derivatives = true;   

                mesh1 = new THREE.Mesh( geometry1, material_1 );
                mesh1.position.x = -100;
                scene.add( mesh1 );


                var  bufferGeometry = new THREE.BoxBufferGeometry( 100, 100,100 );
                var geometry2 = new THREE.InstancedBufferGeometry();
                    geometry2.index = bufferGeometry.index;
                    geometry2.attributes.position = bufferGeometry.attributes.position;   
                    geometry2.attributes.uv = bufferGeometry.attributes.uv;         

                //Now with instancing
                geometry2.addAttribute( 'center', new THREE.InstancedBufferAttribute(  fnGetFloat32ArrayCenters( geometry2 ), 3 ) );    


                var material_2 = new THREE.ShaderMaterial( {
                        uniforms: {},                             
                        vertexShader: document.getElementById( 'vertexShader' ).textContent,
                        fragmentShader: document.getElementById( 'fragmentShader' ).textContent

                    } );
                material_2.extensions.derivatives = true;   

                mesh2 = new THREE.Mesh( geometry2, material_2 );
                mesh2.position.x = 100;
                scene.add( mesh2 );


                // renderer

                renderer = new THREE.WebGLRenderer( { antialias: true } );
                renderer.setPixelRatio( window.devicePixelRatio );
                renderer.setSize( window.innerWidth, window.innerHeight );
                document.body.appendChild( renderer.domElement );

                // events

                window.addEventListener( 'resize', onWindowResize, false );

                function fnGetFloat32ArrayCenters( geometry ) {


                    var vectors = [
                        new THREE.Vector3( 1, 0, 0 ),
                        new THREE.Vector3( 0, 1, 0 ),
                        new THREE.Vector3( 0, 0, 1 )
                    ];

                    var position = geometry.attributes.position;
                    var centers = new Float32Array( position.count * 3 );


                    for ( var i = 0, l = position.count; i < l; i ++ ) {

                        vectors[ i % 3 ].toArray( centers, i * 3 );

                    }

                    return centers;


                } //fnGetFloat32ArrayCenters                


            } //init




            function onWindowResize() {

                camera.aspect = window.innerWidth / window.innerHeight;
                camera.updateProjectionMatrix();

                renderer.setSize( window.innerWidth, window.innerHeight );

            }



        </script>

    </body>
</html>
1

1 Answers

1
votes

Solution: The problem appears to be a bug in three.js r83 with meshes created on BoxBufferGeometry vs InstancedBufferGeometry; see the updated codepen that illustrates this in the comments.

The wireframe attribute for material_1 is not applied when its value is false. It appears to be 'stuck' on 'true'; forcing it to 'false' will have no affect on the first cube.

This lead to the misperception that the cube based on material_2 was in error when it was right all along; because it was respecting the default wireframe attribute value of 'false'. When set to 'true' it will display as a wireframed cube.

<html lang="en">
    <head>
        <title>Adopted from three.js webgl - materials - wireframe</title>
        <meta charset="utf-8">
        <meta name="viewport" content="width=device-width, user-scalable=no, minimum-scale=1.0, maximum-scale=1.0">
        <style>
            body {
                margin: 0px;
                background-color: #000000;
                overflow: hidden;
            }
        </style>
    </head>
    <body>

  <script src="https://cdnjs.cloudflare.com/ajax/libs/three.js/r83/three.js"></script>  

        <script type="x-shader/x-vertex" id="vertexShader"> 
            attribute vec3 center;
            varying vec3 vCenter;

            void main() {
                vCenter = center;
                gl_Position = projectionMatrix * modelViewMatrix * vec4( position, 1.0 ); 
            }
        </script>

        <script type="x-shader/x-fragment" id="fragmentShader">
            varying vec3 vCenter;

            float edgeFactorTri() {
                vec3 d = fwidth( vCenter.xyz ); 
                vec3 a3 = smoothstep( vec3( 0.0 ), d * 1.5, vCenter.xyz );  
                return min( min( a3.x, a3.y ), a3.z );
            }

            void main() {           
                gl_FragColor.rgb = mix( vec3( 1.0 ), vec3( 0.0 ), edgeFactorTri() );                
                gl_FragColor.a = 1.0;   
            }
        </script>

        <script>

            var camera, scene, renderer;

            init();

            renderer.render( scene, camera );

            function init() {

                var bufferGeometry, material, mesh;
                camera = new THREE.PerspectiveCamera( 40, window.innerWidth / window.innerHeight, 1, 2000 );
                camera.position.z = 700;
                scene = new THREE.Scene();

                var geometry1 =  new THREE.BoxBufferGeometry( 100, 100,100 );
                geometry1.addAttribute( 'center', new THREE.BufferAttribute( fnGetFloat32ArrayCenters( geometry1 ), 3 ) );

                var material_1 = new THREE.ShaderMaterial( {
                        uniforms: {},                             
                        vertexShader: document.getElementById( 'vertexShader' ).textContent,
                        fragmentShader: document.getElementById( 'fragmentShader' ).textContent,
            wireframe: false //bug:For r83/three.js, property is not applied when the THREE.Mesh constructor is invoked. https://threejs.org/docs/index.html#api/materials/ShaderMaterial.wireframe

                    } );
                material_1.extensions.derivatives = true;   

                mesh1 = new THREE.Mesh( geometry1, material_1 );  //bug: material_1's wireframe:false property is not applied for r83/three.js
                mesh1.position.x = -100;
                scene.add( mesh1 );


                var  bufferGeometry = new THREE.BoxBufferGeometry( 100, 100,100 );
                var geometry2 = new THREE.InstancedBufferGeometry();
                    geometry2.index = bufferGeometry.index;
                    geometry2.attributes.position = bufferGeometry.attributes.position;   
                    geometry2.attributes.uv = bufferGeometry.attributes.uv;         

                //Now with instancing
                geometry2.addAttribute( 'center', new THREE.InstancedBufferAttribute(  fnGetFloat32ArrayCenters( geometry2 ), 3 ) );    


                var material_2 = new THREE.ShaderMaterial( {
                        uniforms: {},                             
                        vertexShader: document.getElementById( 'vertexShader' ).textContent,
                        fragmentShader: document.getElementById( 'fragmentShader' ).textContent,
            wireframe:true  //See: documentation: https://threejs.org/docs/index.html#api/materials/ShaderMaterial.wireframe

                    } );
                material_2.extensions.derivatives = true;   

                mesh2 = new THREE.Mesh( geometry2, material_2 );
                mesh2.position.x = 100;
                scene.add( mesh2 );


                // renderer

                renderer = new THREE.WebGLRenderer( { antialias: true } );
                renderer.setPixelRatio( window.devicePixelRatio );
                renderer.setSize( window.innerWidth, window.innerHeight );
                document.body.appendChild( renderer.domElement );

                // events

                window.addEventListener( 'resize', onWindowResize, false );

                function fnGetFloat32ArrayCenters( geometry ) {


                    var vectors = [
                        new THREE.Vector3( 1, 0, 0 ),
                        new THREE.Vector3( 0, 1, 0 ),
                        new THREE.Vector3( 0, 0, 1 )
                    ];

                    var position = geometry.attributes.position;
                    var centers = new Float32Array( position.count * 3 );


                    for ( var i = 0, l = position.count; i < l; i ++ ) {

                        vectors[ i % 3 ].toArray( centers, i * 3 );

                    }

                    return centers;


                } //fnGetFloat32ArrayCenters                


            } //init




            function onWindowResize() {

                camera.aspect = window.innerWidth / window.innerHeight;
                camera.updateProjectionMatrix();

                renderer.setSize( window.innerWidth, window.innerHeight );

            }



        </script>

    </body>
</html>