【问题标题】:Buffered Geometry billboarding vertices in the vertex shader顶点着色器中的缓冲几何广告牌顶点
【发布时间】:2018-07-28 09:19:02
【问题描述】:

我遇到了this 真正很好的例子,说明了如何通过顶点着色器实现广告牌,以减轻绘制和旋转大量标签以始终面向相机的繁重工作。 p>

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>

代码是在三个 js 从允许制服中的属性过渡到强制我们现在使用缓冲几何之前创建的。经过一番挖掘,我发现您可以使用以下方法轻松地从标准几何创建缓冲几何:

buffGeometry = new THREE.BufferGeometry().fromGeometry( <my old Geometry object> );

这太酷了! - 很好用,但是我不知道如何或在何处将属性 vec3 的长列表传递给着色器,以告诉它每个标签的中点应该在哪里,以达到与给出的旧示例相同的效果。

有没有人知道如何解决这个问题?发布的示例正是我所追求的,但我真的不想在剩下的时间里一直使用旧版本的三...

非常感谢您的任何建议:)

FR

【问题讨论】:

    标签: three.js glsl webgl shader vertex-shader


    【解决方案1】:

    所以经过 大量 实验后,我自己想通了 - 走吧。

    您使用前面提到的 fromGeometry() 函数将旧的 Geometry 对象转换为 THREE.BufferGeometry(),为每个顶点创建一个 Float32Array 数组,其中包含每个标签 x、y、z 坐标的位置,并将该数组传递给BufferGeometry 通过 addAttribute 函数,着色器知道在哪里绘制标签和旋转相机时在哪里旋转,使用最新版本的 THREE.js 重新创建广告牌效果。 8)查看附加的代码示例,希望其他人觉得这很有用! :)

    var scene;
    var book;
    var shaderMaterial;
    var stats;
    var container;
    
    container = document.createElement('div');
    document.body.appendChild(container);
    
    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 labelPosArray = [];
    
    var grid = new THREE.GridHelper(100, 10);
    scene.add(grid);
    
    stats = new Stats();
    container.appendChild(stats.dom);
    container.appendChild(renderer.domElement);
    
    var controls = new THREE.OrbitControls(camera, renderer.domElement);
    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) {
    
      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];
    
        var j = 0,
          ln = 0;
    
        for (i = 0; i < str.length; 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));
    
          labelPosArray.push(vec);
          labelPosArray.push(vec);
          labelPosArray.push(vec);
          labelPosArray.push(vec);
          labelPosArray.push(vec);
          labelPosArray.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++;
          }
        }
    
        master_geometry.merge(geo);
    
      }
    
      var lps = new Float32Array(labelPosArray.length * 3);
      var cnt = 0;
    
      for (i = 0; i < labelPosArray.length; i++) {
    
        lps[cnt++] = labelPosArray[i].x;
        lps[cnt++] = labelPosArray[i].y;
        lps[cnt++] = labelPosArray[i].z;
    
      } // for
    
      buffGeometry = new THREE.BufferGeometry().fromGeometry(master_geometry);
    
      buffGeometry.addAttribute('labelpos', new THREE.BufferAttribute(lps, 3));
    
      book = new THREE.Mesh(
        buffGeometry,
        shaderMaterial);
    
      scene.add(book);
    
    }
    
    var uniforms = {
      map: {
        type: "t",
        value: createGlpyhSheet()
      }
    };
    
    shaderMaterial = new THREE.ShaderMaterial({
      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('label ' + 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);
      stats.update();
    
    }
    
    animate();
    html {
      background-color: #ffffff;
    }
    
    * {
      margin: 0;
      padding: 0;
    }
    <script src="https://raw.githack.com/mrdoob/three.js/r124/build/three.js"></script>
    <script src="https://raw.githack.com/mrdoob/three.js/r124/examples/js/controls/OrbitControls.js"></script>
    
      <script src="https://raw.githack.com/mrdoob/three.js/r124/examples/js/libs/stats.min.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>

    【讨论】:

      猜你喜欢
      • 2015-09-16
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2013-11-23
      • 1970-01-01
      相关资源
      最近更新 更多