6

我是 THREEJS 的新手。过去,我使用过 AFRAME、CESIUM、XEOGL 和 BABYLONJS。但最终,由于内存消耗和性能,我意识到制作 CAD 可视化器的最佳产品是 THREEJS。

BABYLONJS 加载一个大的 GLTF 文件 (400MB) 需要 4 多分钟,而 THREEJS 只需要 30 秒。BABYLONJS 占用的内存是 THREEJS 的 4 倍。

我知道在 THREEJS 中从加载的 GLTF 文件创建实例 (GPU) 仍然存在一些问题,但我只需要更改每个实例中的位置和旋转,不需要任何动画。

我尝试过使用 GLTF1.0 和 GLTF2.0,问题是一样的。当我加载 GLTF 模型时,我得到了一个场景。从这个场景中,我试图从 children 数组中获取 buffergeometry。但是,当我尝试创建一个实例时,它不起作用。我的对象是静态的(根本没有动画)。

有什么方法可以创建 Object3D 的实例或从它的缓冲几何?

在 BABYLONJS 中,从加载的 GLTF 文件创建实例非常简单。我真的需要使用实例来节省 RAM 并使用 GPU 而不是 CPU 资源。我的场景需要加载相同对象的多个副本来复合场景。

我使用 GLFT 加载程序看到的一些问题:

    1. 您必须识别所有包含有效几何图形的 object3D。在此示例中位于第 335 行:

    var geo = data.scene.children[0].children[0].children[0].children[0].geometry

    1. 合并示例不起作用。它在原始示例中也不起作用。
    1. IT 似乎实例化根本不会提高性能:
  • 10000 个对象,多材质 --> 4FPS

  • 10000 个对象,单一材质--> 4FPS

  • 10000 个对象,实例化 --> 4FPS

  • 10000 个对象,合并 --> 不工作

    1. 选择实例化后,几何图形未正确渲染。

这里有我使用 Duck GLTF 示例的代码:

<!DOCTYPE html>
<html lang="en">
  <head>
    <title>three.js webgl - interactive instances (gpu)</title>
    <meta charset="utf-8" />
    <meta
      name="viewport"
      content="width=device-width, user-scalable=no, minimum-scale=1.0, maximum-scale=1.0"
    />
    <style>
      body {
        font-family: Monospace;
        background-color: #f0f0f0;
        margin: 0px;
        overflow: hidden;
      }
      .info {
        position: absolute;
        background-color: black;
        opacity: 0.8;
        color: white;
        text-align: center;
        top: 0px;
        width: 100%;
      }
      .info a {
        color: #00ffff;
      }
      #notSupported {
        width: 50%;
        margin: auto;
        border: 2px red solid;
        margin-top: 20px;
        padding: 10px;
      }
    </style>
  </head>
  <body>
    <div class="info">
      <a href="http://threejs.org" target="_blank" rel="noopener">three.js</a>
      webgl - gpu picking of geometry instances
      <div id="notSupported" style="display: none">
        Sorry your graphics card + browser does not support hardware instancing
      </div>
      <br /><br />
      <div>
        This demo compares different methods of constructing and rendering many
        instances of a single geometry.
      </div>
      <br />
      <div>
        <div style="display: inline-block">
          <span>number of<br />geometry instances</span>
          <br />
          <select id="instanceCount">
            <option>100</option>
            <option>500</option>
            <option selected>1000</option>
            <option>2000</option>
            <option>3000</option>
            <option>5000</option>
            <option>10000</option>
            <option>20000</option>
            <option>30000</option>
            <option>50000</option>
            <option>100000</option>
          </select>
        </div>
        &nbsp;&nbsp;&nbsp;
        <div style="display: inline-block">
          <span>method of<br />construction/rendering</span>
          <br />
          <select id="method">
            <option>instanced</option>
            <option>merged</option>
            <option selected>singleMaterial</option>
            <option>multiMaterial</option>
          </select>
        </div>
        &nbsp;&nbsp;&nbsp;
        <div style="display: inline-block">
          <span>render continuously<br />(to get fps reading)</span>
          <br />
          <input id="animate" type="checkbox" />
        </div>
        &nbsp;&nbsp;&nbsp;
        <div style="display: inline-block">
          <span
            >use override material<br />(only effects singleMaterial
            method)</span
          >
          <br />
          <input id="override" type="checkbox" checked />
        </div>
        &nbsp;&nbsp;&nbsp;
        <div style="display: inline-block">
          <span>construct anew<br />(to get additional timings)</span>
          <br />
          <button id="construct" type="button">do it</button>
        </div>
      </div>
      <br />
      <div>
        <span>Materials: #<span id="materialCount"></span></span>
        &nbsp;&nbsp;&nbsp;
        <span>Objects: #<span id="objectCount"></span></span>
        &nbsp;&nbsp;&nbsp;
        <span>Drawcalls: #<span id="drawcalls"></span></span>
        &nbsp;&nbsp;&nbsp;
        <span>Construction time: <span id="initTime"></span>&nbsp;ms</span>
        &nbsp;&nbsp;&nbsp;
      </div>
    </div>
    <div id="container"></div>
    <script src="../build/three.js"></script>
    <script src="js/controls/TrackballControls.js"></script>
    <script src="js/libs/stats.min.js"></script>
    <script src="js/loaders/GLTF2Loader.js"></script>
    <script id="vertMerged" type="x-shader/x-vertex">
      #define SHADER_NAME vertMerged
      precision highp float;
      uniform mat4 modelViewMatrix;
      uniform mat4 projectionMatrix;
      attribute vec3 position;
      #ifdef PICKING
      attribute vec3 pickingColor;
      #else
      attribute vec3 color;
      varying vec3 vPosition;
      #endif
      varying vec3 vColor;
      void main()   {
      vec3 positionEye = ( modelViewMatrix * vec4( position, 1.0 ) ).xyz;
      #ifdef PICKING
      vColor = pickingColor;
      #else
      vColor = color;
      vPosition = positionEye;
      #endif
      gl_Position = projectionMatrix * vec4( positionEye, 1.0 );
      }
    </script>
    <script id="fragMerged" type="x-shader/x-fragment">
      #define SHADER_NAME fragMerged
      #extension GL_OES_standard_derivatives : enable
      precision highp float;
      varying vec3 vColor;
      #ifndef PICKING
      varying vec3 vPosition;
      #endif
      void main()   {
      #ifdef PICKING
      gl_FragColor = vec4( vColor, 1.0 );
      #else
      vec3 fdx = dFdx( vPosition );
      vec3 fdy = dFdy( vPosition );
      vec3 normal = normalize( cross( fdx, fdy ) );
      float diffuse = dot( normal, vec3( 0.0, 0.0, 1.0 ) );
      gl_FragColor = vec4( diffuse * vColor, 1.0 );
      #endif
      }
    </script>
    <script id="vertInstanced" type="x-shader/x-vertex">
      #define SHADER_NAME vertInstanced
      precision highp float;
      uniform mat4 modelViewMatrix;
      uniform mat4 projectionMatrix;
      attribute vec3 position;
      attribute vec3 mcol0;
      attribute vec3 mcol1;
      attribute vec3 mcol2;
      attribute vec3 mcol3;
      #ifdef PICKING
      attribute vec3 pickingColor;
      #else
      attribute vec3 color;
      varying vec3 vPosition;
      #endif
      varying vec3 vColor;
      void main()   {
      mat4 matrix = mat4(
      vec4( mcol0, 0 ),
      vec4( mcol1, 0 ),
      vec4( mcol2, 0 ),
      vec4( mcol3, 1 )
      );
      vec3 positionEye = ( modelViewMatrix * matrix * vec4( position, 1.0 ) ).xyz;
      #ifdef PICKING
      vColor = pickingColor;
      #else
      vColor = color;
      vPosition = positionEye;
      #endif
      gl_Position = projectionMatrix * vec4( positionEye, 1.0 );
      }
    </script>
    <script id="fragInstanced" type="x-shader/x-fragment">
      #define SHADER_NAME fragInstanced
      #extension GL_OES_standard_derivatives : enable
      precision highp float;
      varying vec3 vColor;
      #ifndef PICKING
      varying vec3 vPosition;
      #endif
      void main()   {
      #ifdef PICKING
      gl_FragColor = vec4( vColor, 1.0 );
      #else
      vec3 fdx = dFdx( vPosition );
      vec3 fdy = dFdy( vPosition );
      vec3 normal = normalize( cross( fdx, fdy ) );
      float diffuse = dot( normal, vec3( 0.0, 0.0, 1.0 ) );
      gl_FragColor = vec4( diffuse * vColor, 1.0 );
      #endif
      }
    </script>
    <script id="vertMaterial" type="x-shader/x-vertex">
      #define SHADER_NAME vertMaterial
      precision highp float;
      uniform mat4 modelViewMatrix;
      uniform mat4 projectionMatrix;
      attribute vec3 position;
      #ifndef PICKING
      varying vec3 vPosition;
      #endif
      void main()   {
      vec3 positionEye = ( modelViewMatrix * vec4( position, 1.0 ) ).xyz;
      #ifndef PICKING
      vPosition = positionEye;
      #endif
      gl_Position = projectionMatrix * vec4( positionEye, 1.0 );
      }
    </script>
    <script id="fragMaterial" type="x-shader/x-fragment">
      #define SHADER_NAME fragMaterial
      #extension GL_OES_standard_derivatives : enable
      precision highp float;
      #ifdef PICKING
      uniform vec3 pickingColor;
      #else
      uniform vec3 color;
      varying vec3 vPosition;
      #endif
      void main()   {
      #ifdef PICKING
      gl_FragColor = vec4( pickingColor, 1.0 );
      #else
      vec3 fdx = dFdx( vPosition );
      vec3 fdy = dFdy( vPosition );
      vec3 normal = normalize( cross( fdx, fdy ) );
      float diffuse = dot( normal, vec3( 0.0, 0.0, 1.0 ) );
      gl_FragColor = vec4( diffuse * color, 1.0 );
      #endif
      }
    </script>
    <script>
      var container, stats;
      var camera, controls, scene, renderer;
      var pickingData, pickingRenderTarget, pickingScene;
      var useOverrideMaterial = true;
      var singleMaterial, singlePickingMaterial;
      var highlightBox;
      var materialList = [];
      var geometryList = [];
      var objectCount = 0;
      var geometrySize;
      var mouse = new THREE.Vector2();
      var scale = 1.03;
      var loader = new THREE.GLTF2Loader();
      var pixelBuffer = new Uint8Array(4);
      var instanceCount, method, doAnimate;
      gui();
      init();
      initMesh();
      if (doAnimate) animate();
      function gui() {
        var instanceCountElm = document.getElementById("instanceCount");
        instanceCount = parseInt(instanceCountElm.value);
        instanceCountElm.addEventListener("change", function () {
          instanceCount = parseInt(instanceCountElm.value);
          initMesh();
        });
        var methodElm = document.getElementById("method");
        method = methodElm.value;
        methodElm.addEventListener("change", function () {
          method = methodElm.value;
          initMesh();
        });
        var animateElm = document.getElementById("animate");
        doAnimate = animateElm.checked;
        animateElm.addEventListener("click", function () {
          doAnimate = animateElm.checked;
          animate();
        });
        var overrideElm = document.getElementById("override");
        useOverrideMaterial = overrideElm.checked;
        overrideElm.addEventListener("click", function () {
          useOverrideMaterial = overrideElm.checked;
          initMesh();
        });
        var constructElm = document.getElementById("construct");
        constructElm.addEventListener("click", function () {
          initMesh();
        });
      }
      function clean() {
        THREE.Cache.clear();
        materialList.forEach(function (m) {
          m.dispose();
        });
        geometryList.forEach(function (g) {
          g.dispose();
        });
        scene = new THREE.Scene();
        scene.background = new THREE.Color(0xffffff);
        scene.add(camera);
        scene.add(highlightBox);
        pickingScene = new THREE.Scene();
        pickingData = {};
        materialList = [];
        geometryList = [];
        objectCount = 0;
        singleMaterial = undefined;
        singlePickingMaterial = undefined;
      }
      var randomizeMatrix = (function () {
        var position = new THREE.Vector3();
        var rotation = new THREE.Euler();
        var quaternion = new THREE.Quaternion();
        var scale = new THREE.Vector3();
        return function (matrix) {
          position.x = Math.random() * 40 - 20;
          position.y = Math.random() * 40 - 20;
          position.z = Math.random() * 40 - 20;
          rotation.x = Math.random() * 2 * Math.PI;
          rotation.y = Math.random() * 2 * Math.PI;
          rotation.z = Math.random() * 2 * Math.PI;
          quaternion.setFromEuler(rotation, false);
          scale.x = scale.y = scale.z = 0.001;
          matrix.compose(position, quaternion, scale);
        };
      })();
      function initMesh() {
        clean();
        loader.load("models/gltf/Duck/glTF-Binary/Duck.glb", function (data) {
          console.log(data);
          var geo =
            data.scene.children[0].children[0].children[0].children[0].geometry;
          console.log("geo:");
          console.log(geo);
          geo.computeBoundingBox();
          geometrySize = geo.boundingBox.getSize();
          geometryList.push(geo);
          var start = window.performance.now();
          switch (method) {
            case "merged":
              makeMerged(geo);
              break;
            case "instanced":
              makeInstanced(geo);
              break;
            case "singleMaterial":
              makeSingleMaterial(geo);
              break;
            case "multiMaterial":
              makeMultiMaterial(geo);
              break;
          }
          render();
          var end = window.performance.now();
          document.getElementById("materialCount").innerText =
            materialList.length;
          document.getElementById("objectCount").innerText = objectCount;
          document.getElementById("drawcalls").innerText =
            renderer.info.render.calls;
          document.getElementById("initTime").innerText = (end - start).toFixed(
            2
          );
        });
      }
      function makeMultiMaterial(geo) {
        var vert = document.getElementById("vertMaterial").textContent;
        var frag = document.getElementById("fragMaterial").textContent;
        var material = new THREE.RawShaderMaterial({
          vertexShader: vert,
          fragmentShader: frag,
          uniforms: {
            color: {
              value: new THREE.Color(),
            },
          },
        });
        var pickingMaterial = new THREE.RawShaderMaterial({
          vertexShader: "#define PICKING\n" + vert,
          fragmentShader: "#define PICKING\n" + frag,
          uniforms: {
            pickingColor: {
              value: new THREE.Color(),
            },
          },
        });
        var matrix = new THREE.Matrix4();
        for (var i = 0; i < instanceCount; i++) {
          var object = new THREE.Mesh(geo, material);
          objectCount++;
          randomizeMatrix(matrix);
          object.applyMatrix(matrix);
          var pickingObject = object.clone();
          objectCount++;
          object.material = material.clone();
          object.material.uniforms.color.value.setHex(Math.random() * 0xffffff);
          materialList.push(object.material);
          pickingObject.material = pickingMaterial.clone();
          pickingObject.material.uniforms.pickingColor.value.setHex(i + 1);
          materialList.push(pickingObject.material);
          pickingData[i + 1] = object;
          scene.add(object);
          pickingScene.add(pickingObject);
        }
        material.dispose();
        pickingMaterial.dispose();
      }
      function makeSingleMaterial(geo) {
        var vert = document.getElementById("vertMaterial").textContent;
        var frag = document.getElementById("fragMaterial").textContent;
        var material = new THREE.RawShaderMaterial({
          vertexShader: vert,
          fragmentShader: frag,
          uniforms: {
            color: {
              value: new THREE.Color(),
            },
          },
        });
        materialList.push(material);
        var pickingMaterial = new THREE.RawShaderMaterial({
          vertexShader: "#define PICKING\n" + vert,
          fragmentShader: "#define PICKING\n" + frag,
          uniforms: {
            pickingColor: {
              value: new THREE.Color(),
            },
          },
        });
        materialList.push(pickingMaterial);
        if (useOverrideMaterial) {
          singleMaterial = material;
          singlePickingMaterial = pickingMaterial;
        }
        var matrix = new THREE.Matrix4();
        function onBeforeRender(
          renderer,
          scene,
          camera,
          geometry,
          material,
          group
        ) {
          var updateList = [];
          var u = material.uniforms;
          var d = this.userData;
          if (u.pickingColor) {
            u.pickingColor.value.setHex(d.pickingColor);
            updateList.push("pickingColor");
          }
          if (u.color) {
            u.color.value.setHex(d.color);
            updateList.push("color");
          }
          if (updateList.length) {
            var materialProperties = renderer.properties.get(material);
            if (materialProperties.program) {
              var gl = renderer.getContext();
              var p = materialProperties.program;
              gl.useProgram(p.program);
              var pu = p.getUniforms();
              updateList.forEach(function (name) {
                pu.setValue(gl, name, u[name].value);
              });
            }
          }
        }
        for (var i = 0; i < instanceCount; i++) {
          var object = new THREE.Mesh(geo, material);
          objectCount++;
          randomizeMatrix(matrix);
          object.applyMatrix(matrix);
          var pickingObject;
          if (!useOverrideMaterial) {
            pickingObject = object.clone();
            objectCount++;
          }
          object.material = material;
          object.userData["color"] = Math.random() * 0xffffff;
          if (useOverrideMaterial) {
            object.userData["pickingColor"] = i + 1;
            object.onBeforeRender = onBeforeRender;
          } else {
            pickingObject.material = pickingMaterial;
            pickingObject.userData["pickingColor"] = i + 1;
            pickingObject.onBeforeRender = onBeforeRender;
          }
          pickingData[i + 1] = object;
          scene.add(object);
          if (!useOverrideMaterial) pickingScene.add(pickingObject);
        }
      }
      function makeMerged(geo) {
        var vert = document.getElementById("vertMerged").textContent;
        var frag = document.getElementById("fragMerged").textContent;
        var material = new THREE.RawShaderMaterial({
          vertexShader: vert,
          fragmentShader: frag,
        });
        materialList.push(material);
        var pickingMaterial = new THREE.RawShaderMaterial({
          vertexShader: "#define PICKING\n" + vert,
          fragmentShader: "#define PICKING\n" + frag,
        });
        materialList.push(pickingMaterial);
        var bgeo = geo.clone();
        geometryList.push(bgeo);
        var mgeo = new THREE.BufferGeometry();
        geometryList.push(mgeo);
        var pos = bgeo.attributes.position;
        var posLen = bgeo.attributes.position.count * 3;
        var vertices = new THREE.BufferAttribute(
          new Float32Array(instanceCount * posLen),
          3
        );

        var matrix = new THREE.Matrix4();
        for (var i = 0, ul = instanceCount; i < ul; i++) {
          randomizeMatrix(matrix);
          var object = new THREE.Object3D();
          objectCount++;
          object.applyMatrix(matrix);
          pickingData[i + 1] = object;
          vertices.set(pos.array, i * posLen);
          //matrix.applyToVector3Array( vertices.array, i * posLen, posLen )
        }
        mgeo.addAttribute("position", vertices);
        var colCount = posLen / 3;
        var colors = new THREE.BufferAttribute(
          new Float32Array(instanceCount * colCount * 3),
          3
        );

        var randCol = function () {
          return Math.random();
        };
        for (var i = 0, ul = instanceCount; i < ul; i++) {
          var r = randCol(),
            g = randCol(),
            b = randCol();
          for (var j = i * colCount, jl = (i + 1) * colCount; j < jl; j++) {
            colors.setXYZ(j, r, g, b);
          }
        }
        mgeo.addAttribute("color", colors);
        var col = new THREE.Color();
        var pickingColors = new THREE.BufferAttribute(
          new Float32Array(instanceCount * colCount * 3),
          3
        );
        for (var i = 0, ul = instanceCount; i < ul; i++) {
          col.setHex(i + 1);
          for (var j = i * colCount, jl = (i + 1) * colCount; j < jl; j++) {
            pickingColors.setXYZ(j, col.r, col.g, col.b);
          }
        }
        mgeo.addAttribute("pickingColor", pickingColors);
        var mesh = new THREE.Mesh(mgeo, material);
        scene.add(mesh);
        var pickingMesh = new THREE.Mesh(mgeo, pickingMaterial);
        pickingScene.add(pickingMesh);
      }
      function makeInstanced(geo) {
        var vert = document.getElementById("vertInstanced").textContent;
        var frag = document.getElementById("fragInstanced").textContent;
        var material = new THREE.RawShaderMaterial({
          vertexShader: vert,
          fragmentShader: frag,
        });
        materialList.push(material);
        var pickingMaterial = new THREE.RawShaderMaterial({
          vertexShader: "#define PICKING\n" + vert,
          fragmentShader: "#define PICKING\n" + frag,
        });
        materialList.push(pickingMaterial);
        var bgeo = geo.clone();
        geometryList.push(bgeo);
        var igeo = new THREE.InstancedBufferGeometry();
        geometryList.push(igeo);
        var vertices = bgeo.attributes.position.clone();
        igeo.addAttribute("position", vertices);
        var mcol0 = new THREE.InstancedBufferAttribute(
          new Float32Array(instanceCount * 3),
          3,
          1
        );
        var mcol1 = new THREE.InstancedBufferAttribute(
          new Float32Array(instanceCount * 3),
          3,
          1
        );
        var mcol2 = new THREE.InstancedBufferAttribute(
          new Float32Array(instanceCount * 3),
          3,
          1
        );
        var mcol3 = new THREE.InstancedBufferAttribute(
          new Float32Array(instanceCount * 3),
          3,
          1
        );
        var matrix = new THREE.Matrix4();
        var me = matrix.elements;
        for (var i = 0, ul = mcol0.count; i < ul; i++) {
          randomizeMatrix(matrix);
          var object = new THREE.Object3D();
          objectCount++;
          object.applyMatrix(matrix);
          pickingData[i + 1] = object;
          mcol0.setXYZ(i, me[0], me[1], me[2]);
          mcol1.setXYZ(i, me[4], me[5], me[6]);
          mcol2.setXYZ(i, me[8], me[9], me[10]);
          mcol3.setXYZ(i, me[12], me[13], me[14]);
        }
        igeo.addAttribute("mcol0", mcol0);
        igeo.addAttribute("mcol1", mcol1);
        igeo.addAttribute("mcol2", mcol2);
        igeo.addAttribute("mcol3", mcol3);
        var randCol = function () {
          return Math.random();
        };
        var colors = new THREE.InstancedBufferAttribute(
          new Float32Array(instanceCount * 3),
          3,
          1
        );
        for (var i = 0, ul = colors.count; i < ul; i++) {
          colors.setXYZ(i, randCol(), randCol(), randCol());
        }
        igeo.addAttribute("color", colors);
        var col = new THREE.Color();
        var pickingColors = new THREE.InstancedBufferAttribute(
          new Float32Array(instanceCount * 3),
          3,
          1
        );
        for (var i = 0, ul = pickingColors.count; i < ul; i++) {
          col.setHex(i + 1);
          pickingColors.setXYZ(i, col.r, col.g, col.b);
        }
        igeo.addAttribute("pickingColor", pickingColors);
        var mesh = new THREE.Mesh(igeo, material);
        scene.add(mesh);
        var pickingMesh = new THREE.Mesh(igeo, pickingMaterial);
        pickingScene.add(pickingMesh);
      }
      function init() {
        camera = new THREE.PerspectiveCamera(
          70,
          window.innerWidth / window.innerHeight,
          1,
          100
        );
        camera.position.z = 40;
        pickingRenderTarget = new THREE.WebGLRenderTarget(
          window.innerWidth,
          window.innerHeight
        );
        pickingRenderTarget.texture.generateMipmaps = false;
        pickingRenderTarget.texture.minFilter = THREE.NearestFilter;
        highlightBox = new THREE.Mesh(
          new THREE.BoxGeometry(1, 1, 1),
          new THREE.MeshLambertMaterial({
            emissive: 0xffff00,
            transparent: true,
            opacity: 0.5,
            side: THREE.FrontSide,
          })
        );
        container = document.getElementById("container");
        renderer = new THREE.WebGLRenderer({
          antialias: true,
          alpha: true,
        });
        if (renderer.extensions.get("ANGLE_instanced_arrays") === false) {
          document.getElementById("notSupported").style.display = "";
          return;
        }
        renderer.setPixelRatio(window.devicePixelRatio);
        renderer.setSize(window.innerWidth, window.innerHeight);
        container.appendChild(renderer.domElement);
        if (renderer.extensions.get("ANGLE_instanced_arrays") === false) {
          throw "ANGLE_instanced_arrays not supported";
        }
        controls = new THREE.TrackballControls(camera, renderer.domElement);
        controls.staticMoving = true;
        stats = new Stats();
        container.appendChild(stats.dom);
        renderer.domElement.addEventListener("mousemove", onMouseMove);
        window.addEventListener("resize", onWindowResize, false);
      }
      //
      function onMouseMove(e) {
        mouse.x = e.clientX;
        mouse.y = e.clientY;
        controls.update();
        requestAnimationFrame(render);
      }
      function onWindowResize(event) {
        camera.aspect = window.innerWidth / window.innerHeight;
        camera.updateProjectionMatrix();
        renderer.setSize(window.innerWidth, window.innerHeight);
        pickingRenderTarget.setSize(window.innerWidth, window.innerHeight);
      }
      function animate() {
        if (doAnimate) {
          requestAnimationFrame(animate);
        }
        controls.update();
        stats.update();
        document.getElementById("materialCount").innerText =
          materialList.length;
        document.getElementById("objectCount").innerText = objectCount;
        document.getElementById("drawcalls").innerText =
          renderer.info.render.calls;
        render();
      }
      function pick() {
        highlightBox.visible = false;
        if (singlePickingMaterial) {
          scene.overrideMaterial = singlePickingMaterial;
          renderer.render(scene, camera, pickingRenderTarget);
          scene.overrideMaterial = null;
        } else {
          renderer.render(pickingScene, camera, pickingRenderTarget);
        }
        renderer.readRenderTargetPixels(
          pickingRenderTarget,
          mouse.x,
          pickingRenderTarget.height - mouse.y,
          1,
          1,
          pixelBuffer
        );
        var id =
          (pixelBuffer[0] << 16) | (pixelBuffer[1] << 8) | pixelBuffer[2];
        var object = pickingData[id];
        if (object) {
          if (object.position && object.rotation && object.scale) {
            highlightBox.position.copy(object.position);
            highlightBox.rotation.copy(object.rotation);
            highlightBox.scale
              .copy(object.scale)
              .multiply(geometrySize)
              .multiplyScalar(scale);
            highlightBox.visible = true;
          }
        } else {
          highlightBox.visible = false;
        }
      }
      function render() {
        pick();
        renderer.render(scene, camera);
      }
    </script>
  </body>
</html>
4

2 回答 2

7

看来您的问题主要是,如何在 three.js 中进行实例化?一旦你加载了一个模型,你用什么格式来创建它并不重要。

在这种情况下,您可能只想查看three.js 实例化示例或使用其中一个帮助程序,例如three-instanced-mesh

第二个链接显示了从模型中获取几何图形后如何继续:

// Assumes your model only contains one mesh.
var geometry;
model.traverse(function (node) => {
  if (node.isMesh) {
    geometry = node.geometry;
  }
});

//material that the geometry will use 
var material = new THREE.MeshPhongMaterial();

//the instance group 
var cluster = new THREE.InstancedMesh( 
  geometry,
  material, 
  10000, //instance count 
  false, //is it dynamic 
  false  //does it have color 
  true,  //uniform scale
);

var _v3 = new THREE.Vector3();
var _q = new THREE.Quaternion();

for ( var i ; i < 10000 ; i ++ ) {

  cluster.setQuaternionAt( i , _q );
  cluster.setPositionAt( i , v3.set( Math.random() , Math.random(), Math.random() ) );
  cluster.setScaleAt( i , v3.set(1,1,1) );

}

scene.add( cluster );
于 2017-08-14T23:38:35.127 回答
1

After some time of investigation I discovered why using instancedbuffergeometries were not working with the buffergeometries found in my GLTF files.

The problem is that GLTF format uses indexedbuffergeometries and the workaround is very simple, just convert them with toNonIndexed() method.

Issue fixed.

Best regards

于 2017-09-05T07:36:43.620 回答