billboarding vertices in the vertex shader

前端 未结 2 1542
别那么骄傲
别那么骄傲 2021-01-03 00:57

Code demonstrating issue (comment/uncomment out the gl_Position lines in the vertex shader)

相关标签:
2条回答
  • 2021-01-03 01:50

    It is also worth looking at how it is done in Three.js and their SpriteMaterial: sprite_vert.glsl

    Here is an annotated snippet:

    // optional: pass 2D rotation angle as an uniform
    uniform float rotation;
    // optional: pass 2D center point as an uniform
    uniform vec2 center;
    
    // optional: use this define to scale the model according to distance from the camera
    #define USE_SIZEATTENUATION
    
    // [skipped includes]
    
    void main() {
        // discard rotation and scale
        vec4 mvPosition = modelViewMatrix * vec4( 0.0, 0.0, 0.0, 1.0 );
    
        // extract model's scale
        vec2 scale;
        scale.x = length( vec3( modelMatrix[ 0 ].x, modelMatrix[ 0 ].y, modelMatrix[ 0 ].z ) );
        scale.y = length( vec3( modelMatrix[ 1 ].x, modelMatrix[ 1 ].y, modelMatrix[ 1 ].z ) );
    
        // if not defined, keep model the same size regardless of distance from the camera
        #ifndef USE_SIZEATTENUATION
            bool isPerspective = isPerspectiveMatrix( projectionMatrix );
            if ( isPerspective ) scale *= - mvPosition.z;
        #endif
    
        // if center is not passed as uniform, create vec2 center = vec2(0.0);
    
        // aligned with the camera [and scaled]
        vec2 alignedPosition = ( position.xy - ( center - vec2( 0.5 ) ) ) * scale;
    
        // if rotation is not passed as uniform, skip the next block
    
        // rotate 2D
        vec2 rotatedPosition;
        rotatedPosition.x = cos( rotation ) * alignedPosition.x - sin( rotation ) * alignedPosition.y;
        rotatedPosition.y = sin( rotation ) * alignedPosition.x + cos( rotation ) * alignedPosition.y;
    
        // billboard
        mvPosition.xy += rotatedPosition;
    
        gl_Position = projectionMatrix * mvPosition;
    
        // [skipped includes]
    }
    
    0 讨论(0)
  • 2021-01-03 01:54

    I think you want

    gl_Position = projectionMatrix * 
                  (modelViewMatrix * vec4(labelpos, 1) +
                   vec4(position.xy, 0, 0));
    

    and you need to not add in the position to the vertices

    geo.vertices.push(
      new THREE.Vector3(j * oneDotOne + 0.05, ln * oneDotOne + 0.05, 0),
      new THREE.Vector3(j * oneDotOne + 1.05, ln * oneDotOne + 0.05, 0),
      new THREE.Vector3(j * oneDotOne + 1.05, ln * oneDotOne + 1.05, 0),
      new THREE.Vector3(j * oneDotOne + 0.05, ln * oneDotOne + 1.05, 0));
    

    Otherwise you'd be putting in the position twice.

    Because all your labels are in the same mesh then there's only 1 draw call which means you won't get a different location for each label unless you pass it in (which you were in labelpos but you weren't using it)

    In which case modelViewMatrix * vec4(0,0,0,1) is the same as just saying modelViewMatrix[3] All you're doing is getting the translation of the model that contains all the labels. That would work if each label was a separate mesh and had its own matrix but since you've put them all in one mesh it won't work.

    Your fix was the pass in the location of each label in a separate attribute which you had already included, you just needed to use it.

    modelViewMatrix * vec4(labelpos, 1)
    

    gets you the root of the label

    vec4(position.x, position.y, 0.0, 0.0)
    

    adds in the corners in view space

    var scene;
        var book;
        var shaderMaterial;
    
        var renderer = new THREE.WebGLRenderer({
            antialias: true
        });
        renderer.setClearColor(0x000000);
        document.body.appendChild(renderer.domElement);
    
        var camera = new THREE.PerspectiveCamera(55, 1, 0.1, 40000);
    
    
        window.onresize = function () {
            renderer.setSize(window.innerWidth, window.innerHeight);
            camera.aspect = window.innerWidth / window.innerHeight;
            camera.updateProjectionMatrix();
        };
        window.onresize();
    
        scene = new THREE.Scene();
    
        camera.position.z = 25;
        camera.position.y = 15;
        scene.add(camera);
    
    
        var grid = new THREE.GridHelper(100, 10);
        scene.add(grid);
    
    
        var controls = new THREE.OrbitControls(camera);
        controls.damping = 0.2;
        var lettersPerSide = 16;
    
        function createGlpyhSheet() {
    
            var fontSize = 64;
    
            var c = document.createElement('canvas');
            c.width = c.height = fontSize * lettersPerSide;
            var ctx = c.getContext('2d');
            ctx.font = fontSize + 'px Monospace';
            var i = 0;
    
            for (var y = 0; y < lettersPerSide; y++) {
                for (var x = 0; x < lettersPerSide; x++, i++) {
                    var ch = String.fromCharCode(i);
                    ctx.fillText(ch, x * fontSize, -(8 / 32) * fontSize + (y + 1) * fontSize);
                }
            }
    
            var tex = new THREE.Texture(c);
            tex.flipY = false;
            tex.needsUpdate = true;
    
            return tex;
        }
    
    
        function createLabels(textArrays, positions) {
            //console.log(textArrays, positions);
    
            var master_geometry = new THREE.Geometry();
    
    
            for (var k = 0; k < textArrays.length; k++) {
    
                var geo = new THREE.Geometry();
                geo.dynamic = true;
    
                var str = textArrays[k];
                var vec = positions[k];
                //console.log(shaderMaterial);
    
                //console.log('str is', str, 'vec is', vec);
    
    
                var j = 0,
                    ln = 0;
    
                for (i = 0; i < str.length; i++) {
    
                    //console.log('creating glyph', str[i]);
    
                    var code = str.charCodeAt(i);
                    var cx = code % lettersPerSide;
                    var cy = Math.floor(code / lettersPerSide);
                    var oneDotOne = .55;
    
                    geo.vertices.push(
                    new THREE.Vector3(j * oneDotOne + 0.05, ln * oneDotOne + 0.05, 0),
                    new THREE.Vector3(j * oneDotOne + 1.05, ln * oneDotOne + 0.05, 0),
                    new THREE.Vector3(j * oneDotOne + 1.05, ln * oneDotOne + 1.05, 0),
                    new THREE.Vector3(j * oneDotOne + 0.05, ln * oneDotOne + 1.05, 0));
                    shaderMaterial.attributes.labelpos.value.push(vec);
                    shaderMaterial.attributes.labelpos.value.push(vec);
                    shaderMaterial.attributes.labelpos.value.push(vec);
                    shaderMaterial.attributes.labelpos.value.push(vec);
    
                    var face = new THREE.Face3(i * 4 + 0, i * 4 + 1, i * 4 + 2);
                    geo.faces.push(face);
                    face = new THREE.Face3(i * 4 + 0, i * 4 + 2, i * 4 + 3);
                    geo.faces.push(face);
    
                    var ox = (cx + 0.05) / lettersPerSide;
                    var oy = (cy + 0.05) / lettersPerSide;
                    var off = 0.9 / lettersPerSide;
    
                    geo.faceVertexUvs[0].push([
                    new THREE.Vector2(ox, oy + off),
                    new THREE.Vector2(ox + off, oy + off),
                    new THREE.Vector2(ox + off, oy)]);
                    geo.faceVertexUvs[0].push([
                    new THREE.Vector2(ox, oy + off),
                    new THREE.Vector2(ox + off, oy),
                    new THREE.Vector2(ox, oy)]);
                    if (code == 10) {
                        ln--;
                        j = 0;
                    } else {
                        j++;
                    }
                }
    
                // i can only get this working with merge.
                // Building one giant geometry doesn't work for some reason
                master_geometry.merge(geo);
    
            }
    
            console.log(shaderMaterial);
            shaderMaterial.attributes.labelpos.needsUpdate = true;
    
            book = new THREE.Mesh(
            master_geometry,
            shaderMaterial);
    
            //book.doubleSided = true;
            scene.add(book);
    
        }
    
    
        var uniforms = {
            map: {
                type: "t",
                value: createGlpyhSheet()
            }
        };
    
        var attributes = {
            labelpos: {
                type: 'v3',
                value: []
            }
        };
    
        shaderMaterial = new THREE.ShaderMaterial({
            attributes: attributes,
            uniforms: uniforms,
            vertexShader: document.querySelector('#vertex').textContent,
            fragmentShader: document.querySelector('#fragment').textContent
        });
        shaderMaterial.transparent = true;
        shaderMaterial.depthTest = false;
    
    
        strings = [];
        vectors = [];
        var sizeOfWorld = 100;
        var halfSize = sizeOfWorld * 0.5;
    
        for (var i = 0; i < 500; i++) {
    
            strings.push('test' + i);
            var vector = new THREE.Vector3();
            vector.x = Math.random() * sizeOfWorld - halfSize;
            vector.y = Math.random() * sizeOfWorld - halfSize;
            vector.z = Math.random() * sizeOfWorld - halfSize;
            vectors.push(vector);
    
        }
    
        console.log('creating labels');
        createLabels(strings, vectors);
    
        function animate() {
            controls.update();
            renderer.render(scene, camera);
            requestAnimationFrame(animate, renderer.domElement);
        }
    
        animate();
    html {
                background-color: #ffffff;
            }
            * {
                margin: 0;
                padding: 0;
            }
    <script src="https://cdnjs.cloudflare.com/ajax/libs/three.js/69/three.min.js"></script>
    <script src="https://cdn.rawgit.com/mrdoob/three.js/4862f5f1111346a957ac3e0cb0858be1568d0e03/examples/js/controls/OrbitControls.js"></script>
    <script id="vertex" type="text/x-glsl-vert">
        varying vec2 vUv;
        attribute vec3 labelpos;
    
        void main() {
            vUv = uv;
    
    
            gl_Position = projectionMatrix * 
                          (modelViewMatrix * vec4(labelpos, 1) +
                           vec4(position.xy, 0, 0));
    
        }
    </script>
    <script id="fragment" type="text/x-glsl-frag">
        varying vec2 vUv;
        uniform sampler2D map;
        void main() {
            vec4 diffuse = texture2D(map, vUv);
            vec4 letters = mix(diffuse, vec4(1.0, 1.0, 1.0, diffuse.a), 1.0);
            gl_FragColor = vec4(1.0, 1.0, 1.0, 1.0) * letters;
        }
    </script>

    0 讨论(0)
提交回复
热议问题