顶点着色器中的缓冲几何广告牌顶点 [英] Buffered Geometry billboarding vertices in the vertex shader

查看:74
本文介绍了顶点着色器中的缓冲几何广告牌顶点的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我遇到了这个真正优秀如何通过顶点着色器实现广告牌的示例,以减轻绘制和旋转大量标签以始终面向相机的繁重工作.

var 场景;var 书;var shaderMaterial;var renderer = new THREE.WebGLRenderer({抗锯齿:真});renderer.setClearColor(0x000000);document.body.appendChild(renderer.domElement);var camera = new THREE.PerspectiveCamera(55, 1, 0.1, 40000);window.onresize = 函数 () {renderer.setSize(window.innerWidth, window.innerHeight);camera.aspect = window.innerWidth/window.innerHeight;相机.updateProjectionMatrix();};window.onresize();场景 = 新的 THREE.Scene();相机.位置.z = 25;相机位置 y = 15;场景.添加(相机);var grid = new THREE.GridHelper(100, 10);场景添加(网格);var controls = new THREE.OrbitControls(camera);控制.阻尼 = 0.2;var 字母PerSide = 16;函数 createGlpyhSheet() {var fontSize = 64;var c = document.createElement('canvas');c.width = c.height = fontSize * letterPerSide;var ctx = c.getContext('2d');ctx.font = fontSize + 'px Monospace';变量 i = 0;for (var y = 0; y 

html {背景色:#ffffff;}* {边距:0;填充: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">不同的 vec2 vUv;属性 vec3 labelpos;无效主(){vUv = uv;gl_Position = 投影矩阵 *(modelViewMatrix * vec4(labelpos, 1) +vec4(position.xy, 0, 0));}<script id="fragment" type="text/x-glsl-frag">不同的 vec2 vUv;统一的sampler2D地图;无效主(){vec4 漫反射 = 纹理 2D(地图,vUv);vec4 字母 = mix(diffuse, vec4(1.0, 1.0, 1.0,diffuse.a), 1.0);gl_FragColor = vec4(1.0, 1.0, 1.0, 1.0) * 字母;}</script>

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

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

多酷啊!- 很好用,但是我无法弄清楚如何或在何处将一长串属性 vec3 传递给着色器,以告诉它每个标签的中点应该在哪里,以达到与给出的旧示例相同的效果.

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

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

FR

解决方案

所以经过多次实验后,我自己弄明白了 - 去我吧.

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

var 场景;var 书;var shaderMaterial;变量统计;var 容器;container = document.createElement('div');document.body.appendChild(container);var renderer = new THREE.WebGLRenderer({抗锯齿:真});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;相机.updateProjectionMatrix();};window.onresize();场景 = 新的 THREE.Scene();相机.位置.z = 25;相机位置 y = 15;场景.添加(相机);var labelPosArray = [];var grid = new THREE.GridHelper(100, 10);场景添加(网格);统计=新统计();container.appendChild(stats.dom);container.appendChild(renderer.domElement);var controls = new THREE.OrbitControls(camera, renderer.domElement);控制.阻尼 = 0.2;var 字母PerSide = 16;函数 createGlpyhSheet() {var fontSize = 64;var c = document.createElement('canvas');c.width = c.height = fontSize * letterPerSide;var ctx = c.getContext('2d');ctx.font = fontSize + 'px Monospace';变量 i = 0;for (var y = 0; y 

html {背景色:#ffffff;}* {边距:0;填充: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">不同的 vec2 vUv;属性 vec3 labelpos;void main() { vUv = uv;gl_Position = 投影矩阵 * (modelViewMatrix * vec4(labelpos, 1) + vec4(position.xy, 0, 0));}<script id="fragment" type="text/x-glsl-frag">不同的 vec2 vUv;统一的sampler2D地图;void main() { vec4 漫反射 = 纹理 2D(地图,vUv);vec4 字母 = mix(diffuse, vec4(1.0, 1.0, 1.0,diffuse.a), 1.0);gl_FragColor = vec4(1.0, 1.0, 1.0, 1.0) * 字母;}</script>

I came across this truly excellent example of how to implement billboarding via a vertex shader to offload the hard work of drawing and rotating a large number of labels to always face the camera.

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>

The code was created before THREE js transitioned away from allowing attributes in uniforms, and enforcing we now use buffered Geometry instead. After some digging I found you can easily create a buffered Geometry from a standard geometry using:

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

How cool is that! - works a treat, however I cannot work out how or where to pass the long list of attribute vec3's to the shader to tell it where my mid point for each label should be, to achieve the same effect as the older example given.

Has anyone got any ideas on how to solve this? The example posted is exactly what I am after, but I really don't want to be stuck using an old version of THREE for the rest of time...

Many thanks for any suggestions :)

FR

解决方案

So after much experimentation I figured it out myself - go me.

You convert the old Geometry object to a THREE.BufferGeometry() using the aforementioned fromGeometry() function, create an Float32Array array of the location of each labels x,y,z coordinates for each and every vertices and pass that array to the BufferGeometry via the addAttribute function, the shader knows both where to draw the labels and where to pivot when rotating the camera, re-creating the billboard effect using the latest version of THREE.js. 8) See working attached code example, hope someone else finds this useful! :)

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>

这篇关于顶点着色器中的缓冲几何广告牌顶点的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆