【问题标题】:Transparency within instanced shapes实例化形状内的透明度
【发布时间】:2017-04-20 03:58:54
【问题描述】:

我一直在玩THREE.InstancedBufferGeometry。我终于有了一个可以工作的例子,现在一直在玩着色器。我尝试的第一件事是设置透明度。我的示例代码如下。

初始状态,以及从许多其他摄像机角度(例如,将鼠标拖到左侧),透明度似乎没有任何效果。但是在其他摄像机角度(例如重新加载并将鼠标向右拖动)时,形状明显重叠,这是我所期望的。

对于实例形状,深度排序的处理方式是否不同,或者我做错了什么,或者遗漏了什么?我是否需要以某种方式更新形状,以便相机知道它们在场景中的正确深度?

var cubeGeo = new THREE.InstancedBufferGeometry().copy(new THREE.BoxBufferGeometry(10, 10, 10));
//cubeGeo.maxInstancedCount = 8;

cubeGeo.addAttribute("cubePos", new THREE.InstancedBufferAttribute(new Float32Array([
  25, 25, 25,
  25, 25, -25, -25, 25, 25, -25, 25, -25,
  25, -25, 25,
  25, -25, -25, -25, -25, 25, -25, -25, -25
]), 3, 1));

var vertexShader = [
  "precision highp float;",
  "",
  "uniform mat4 modelViewMatrix;",
  "uniform mat4 projectionMatrix;",
  "",
  "attribute vec3 position;",
  "attribute vec3 cubePos;",
  "",
  "void main() {",
  "",
  "	gl_Position = projectionMatrix * modelViewMatrix * vec4( cubePos + position, 1.0 );",
  "",
  "}"
].join("\n");
var fragmentShader = [
  "precision highp float;",
  "",
  "void main() {",
  "",
  "	gl_FragColor = vec4(1.0, 0.0, 0.0, 0.5);",
  "",
  "}"
].join("\n");

var mat = new THREE.RawShaderMaterial({
  uniforms: {},
  vertexShader: vertexShader,
  fragmentShader: fragmentShader,
  transparent: true
});

var mesh = new THREE.Mesh(cubeGeo, mat);

scene.add(mesh);
html * {
  padding: 0;
  margin: 0;
  width: 100%;
  overflow: hidden;
}

#host {
  width: 100%;
  height: 100%;
}
<script src="http://threejs.org/build/three.js"></script>
<script src="http://threejs.org/examples/js/controls/TrackballControls.js"></script>
<script src="http://threejs.org/examples/js/libs/stats.min.js"></script>
<div id="host"></div>

<script>
  var WIDTH = window.innerWidth,
    HEIGHT = window.innerHeight,
    FOV = 35,
    NEAR = 1,
    FAR = 1000;

  var renderer = new THREE.WebGLRenderer({
    antialias: true
  });
  renderer.setSize(WIDTH, HEIGHT);
  document.getElementById('host').appendChild(renderer.domElement);

  var stats = new Stats();
  stats.domElement.style.position = 'absolute';
  stats.domElement.style.top = '0';
  document.body.appendChild(stats.domElement);


  var camera = new THREE.PerspectiveCamera(FOV, WIDTH / HEIGHT, NEAR, FAR);
  camera.position.z = 250;

  var trackballControl = new THREE.TrackballControls(camera, renderer.domElement);
  trackballControl.rotateSpeed = 2.0; // need to speed it up a little

  var scene = new THREE.Scene();

  var light = new THREE.PointLight(0xffffff, 1, Infinity);
  camera.add(light);

  scene.add(light);

  function render() {
    if (typeof updateVertices !== "undefined") {
      updateVertices();
    }
    renderer.render(scene, camera);
    stats.update();
  }

  function animate() {
    requestAnimationFrame(animate);
    trackballControl.update();
    render();
  }

  animate();
</script>

【问题讨论】:

    标签: three.js


    【解决方案1】:

    您正在使用带有半透明网格的 InstancedBufferGeometry

    实例按照它们在缓冲区中出现的顺序呈现。每个实例的面都按照几何指定的顺序进行渲染。

    因此,如果您使用半透明的实例化,您可能会根据视角出现伪影。

    根据您的用例,您可以尝试设置material.depthWrite = false,但这可能会导致其他工件。

    如果您的网格纹理具有完全透明的区域(而不是部分透明),您应该能够使用 material.alphaTest 丢弃不需要的片段而不会产生伪影。

    three.js r.84

    【讨论】:

    • 如果我理解正确,唯一真正的方法就是以深度排序的方式重新排列我的cubePos 缓冲区,对吗?并且包括这样的每帧任务可能会扼杀通过使用实例获得的任何性能优势(尽管内存仍然会受益)。 :(
    • 不要使用几何面:使用实现的背面-正面技巧here。不过,您可能需要对实例重新排序。
    • 关于不改变几何形状,我们意见一致。 cubePos 我的实例,我实际上让它以这种方式工作——它只是效率低下(我会用该代码发布答案)。我会尝试背面 - 正面技巧,但由于向场景中添加了更多网格,我并不热衷于此。
    【解决方案2】:

    在与 cmets 中的 WestLangley 讨论后,我为我的实例添加了一个分类器。它根据实例与相机的距离对实例位置进行排序。

    (旁注:如果我还有其他 THREE.InstancedBufferAttributes 我需要同时重新订购它们。)

    这样做的最大缺点是,随着场景变得越来越大,实例化和非实例化形状都会变得越来越昂贵。

    // Instances Sorter, called each frame
    function sortObjectInstances(obj) {
      if (obj.geometry) {
        if (obj.geometry instanceof THREE.InstancedBufferGeometry) {
          var array = obj.geometry.attributes.cubePos.array,
            vecArray = [];
          for (var i = 0, l = array.length / 3; i < l; ++i) {
            vecArray.push(new THREE.Vector3(array[(i * 3)], array[(i * 3) + 1], array[(i * 3) + 2]));
          }
          vecArray.sort(function(a, b) {
            if (a.distanceTo(camera.position) > b.distanceTo(camera.position)) {
              return -1;
            }
            if (a.distanceTo(camera.position) < b.distanceTo(camera.position)) {
              return 1;
            }
            return 0;
          });
          for (var i = 0, l = vecArray.length; i < l; ++i) {
            array[(i * 3)] = vecArray[i].x;
            array[(i * 3) + 1] = vecArray[i].y;
            array[(i * 3) + 2] = vecArray[i].z;
          }
          obj.geometry.attributes.cubePos.needsUpdate = true;
        }
      } else {
        for (var i = 0, l = obj.children.length; i < l; ++i) {
          sortObjectInstances(obj.children[i]);
        }
      }
    }
    
    var cubeGeo = new THREE.InstancedBufferGeometry().copy(new THREE.BoxBufferGeometry(10, 10, 10));
    //cubeGeo.maxInstancedCount = 8;
    
    cubeGeo.addAttribute("cubePos", new THREE.InstancedBufferAttribute(new Float32Array([
      25, 25, 25,
      25, 25, -25, -25, 25, 25, -25, 25, -25,
      25, -25, 25,
      25, -25, -25, -25, -25, 25, -25, -25, -25
    ]), 3, 1));
    
    var vertexShader = [
      "precision highp float;",
      "",
      "uniform mat4 modelViewMatrix;",
      "uniform mat4 projectionMatrix;",
      "",
      "attribute vec3 position;",
      "attribute vec3 cubePos;",
      "",
      "void main() {",
      "",
      "	gl_Position = projectionMatrix * modelViewMatrix * vec4( cubePos + position, 1.0 );",
      "",
      "}"
    ].join("\n");
    var fragmentShader = [
      "precision highp float;",
      "",
      "void main() {",
      "",
      "	gl_FragColor = vec4(1.0, 0.0, 0.0, 0.5);",
      "",
      "}"
    ].join("\n");
    
    var mat = new THREE.RawShaderMaterial({
      uniforms: {},
      vertexShader: vertexShader,
      fragmentShader: fragmentShader,
      transparent: true
    });
    
    var mesh = new THREE.Mesh(cubeGeo, mat);
    
    scene.add(mesh);
    html * {
      padding: 0;
      margin: 0;
      width: 100%;
      overflow: hidden;
    }
    
    #host {
      width: 100%;
      height: 100%;
    }
    <script src="http://threejs.org/build/three.js"></script>
    <script src="http://threejs.org/examples/js/controls/TrackballControls.js"></script>
    <script src="http://threejs.org/examples/js/libs/stats.min.js"></script>
    <div id="host"></div>
    
    <script>
      var WIDTH = window.innerWidth,
        HEIGHT = window.innerHeight,
        FOV = 35,
        NEAR = 1,
        FAR = 1000;
    
      var renderer = new THREE.WebGLRenderer({
        antialias: true
      });
      renderer.setSize(WIDTH, HEIGHT);
      document.getElementById('host').appendChild(renderer.domElement);
    
      var stats = new Stats();
      stats.domElement.style.position = 'absolute';
      stats.domElement.style.top = '0';
      document.body.appendChild(stats.domElement);
    
    
      var camera = new THREE.PerspectiveCamera(FOV, WIDTH / HEIGHT, NEAR, FAR);
      camera.position.z = 250;
    
      var trackballControl = new THREE.TrackballControls(camera, renderer.domElement);
      trackballControl.rotateSpeed = 2.0; // need to speed it up a little
    
      var scene = new THREE.Scene();
    
      var light = new THREE.PointLight(0xffffff, 1, Infinity);
      camera.add(light);
    
      scene.add(light);
    
      function render() {
        if (typeof sortObjectInstances !== "undefined") {
          sortObjectInstances(scene); // Sort the instances
        }
        renderer.render(scene, camera);
        stats.update();
      }
    
      function animate() {
        requestAnimationFrame(animate);
        trackballControl.update();
        render();
      }
    
      animate();
    </script>

    【讨论】:

    • 一个小的效率变化是将THREE.Vector3对象的数组存储在自己的网格上,而不是每次都生成它。
    猜你喜欢
    • 2014-02-14
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2022-08-23
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多