顶点着色器中的缓冲几何广告牌顶点

时间:2018-02-17 17:46:56

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

我遇到了this 真正如何通过顶点着色器实现广告牌的优秀示例,以卸载绘制和旋转大量标签以便始终面向相机的艰苦工作。



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();
&#13;
html {
            background-color: #ffffff;
        }
        * {
            margin: 0;
            padding: 0;
        }
&#13;
<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>
&#13;
&#13;
&#13;

代码是在三个js转换为允许制服中的属性之前创建的,并强制执行我们现在使用缓冲的几何。经过一些挖掘后,我发现您可以使用以下方法从标准几何体轻松创建缓冲几何体

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

多酷啊! - 工作一个款待,但我无法弄清楚如何或在哪里将属性vec3的长列表传递给着色器告诉它我的每个标签的中点应该在哪里,以达到与旧示例相同的效果给出。

有没有人对如何解决这个问题有任何想法?发布的示例完全我追求的是什么,但我真的不想在剩下的时间里使用旧版本的THREE ...

非常感谢任何建议:)

FR

1 个答案:

答案 0 :(得分:2)

所以在很多实验之后,我自己想出来了 - 去吧。

使用前面提到的fromGeometry()函数将旧的Geometry对象转换为THREE.BufferGeometry(),为每个顶点创建每个标签x,y,z坐标位置的Float32Array数组,并将该数组传递给通过addAttribute函数的BufferGeometry,着色器知道在旋转相机时绘制标签的位置和旋转位置,使用最新版本的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);
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://threejs.org/build/three.min.js"></script>
  <script src="https://threejs.org/examples/js/controls/OrbitControls.js"></script>
  <script src="https://threejs.org/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>

相关问题