【问题标题】:Three.js - Geometry on top of anotherThree.js - 几何在另一个之上
【发布时间】:2012-10-29 20:16:22
【问题描述】:

是否可以在 Three.js 中始终在场景顶部渲染网格,即使它的位置在所有对象的后面?我正在使用网格实现套索选择,我需要将选择框呈现在场景的其余部分之上。

【问题讨论】:

    标签: javascript three.js


    【解决方案1】:

    是的。

    首先这样做:

    renderer.autoClear = false;
    

    然后创建第二个场景,其中仅包含您想要放置在顶部的对象。然后,在您的渲染循环中:

    renderer.clear();                     // clear buffers
    renderer.render( scene, camera );     // render scene 1
    renderer.clearDepth();                // clear depth buffer
    renderer.render( scene2, camera );    // render scene 2
    

    编辑:另一种解决方案是只有一个场景,但使用这种模式:

    mesh.renderOrder = 999;
    mesh.onBeforeRender = function( renderer ) { renderer.clearDepth(); };
    

    如果网格有单一材质,它将“在顶部”渲染。

    【讨论】:

    • 这是唯一的方法吗?我试图用 mesh.renderDepth、material.depthTest 和 material.depthWrite 达到相同的效果,但无济于事。
    • @Andrew 如果您有任何问题,请发帖。
    • @Andrew 正在尝试做同样的事情,看着 stackoverflow.com/questions/16247280/…
    • @trusktr 答案仍然有效。在你的codepen中,做object2.renderOrder = 999; object2.onBeforeRender = function( renderer ) { renderer.clearDepth(); };如果你有问题,请创建一个新问题。
    • @WestLangley 谢谢!我有一个错误。答案有效。删除了我的评论。
    【解决方案2】:

    如果您只想在前面渲染一个网格,您还可以通过将该对象的材质设置为depthTest 来管理false

    var onTopMaterial = new THREE.MeshStandardMaterial({
      color: 'red',
      depthTest: false
    });
    
    mesh.material = onTopMaterial;
    

    this fiddle查看演示


    注意:确保将renderer.sortObjects 设置为默认的true 值。

    【讨论】:

    • 不是一个好主意。有很多陷阱。其一,在蓝色立方体上设置transparent: true。但最大的问题是,通过设置depthTest = false,所谓的“on top mesh”将不再对自身进行深度测试,这会导致渲染伪影。
    • @WestLangley 感谢您的反馈。在我的情况下,它与透明设置冲突是可以接受的,但关于工件我不确定。我该如何测试这个?我只需要在mouseover 上将一个对象临时移动到所有其他人的前面,然后再将其移回到mousout 上。在我看来,在这种情况下暂时将其添加到不同的场景是不可取的。
    • 除非您的用例非常有限,否则我会遵循已接受答案中的方法。
    【解决方案3】:

    除了设置 object.renderOrder 之外,您还必须在相关对象上将 material.depthTest 设置为 false。

    var spriteMaterial = new THREE.SpriteMaterial( { map: texture1, depthTest: false} );
    
        this.context1 = context1;
        this.texture1 = texture1;
    
        var sprite1 = new THREE.Sprite( spriteMaterial );
        sprite1.scale.set(30,15,1);
        sprite1.center.x=0;
        sprite1.center.y=0;
        sprite1.position.set( 0, 0, 0 );
        this.scene.add( sprite1 );
    

    【讨论】:

      【解决方案4】:

      以下是一个工作示例,它有一个 VisualLayers 类用于管理任意数量的层,它使用 renderer.autoClear = false 和像 West Langley 在他的回答中提到的清除深度技术。

      这种方法很好,因为 renderOrder 的对象没有被修改(这是另一种方法),因此不会引入其他不同的问题,您可以将其保存用于与分层分离的其他用例。

      尝试使用 UI 中的选项来看看它的作用:

      // @ts-check
      
      ////////////////////////
      // LAYER SYSTEM
      ////////////////////////
      
      /** @typedef {{name: string, backingScene: THREE.Scene, order: number}} Layer */
      
      class VisualLayers {
          /**
           * @type {Array<Layer>}
           * @private
           */
          __layers = [];
      
          constructor(
              /** @private @type {THREE.WebGLRenderer} */ __renderer,
              /** @private @type {typeof THREE.Scene} */ __Scene = THREE.Scene
          ) {
              this.__renderer = __renderer;
              this.__Scene = __Scene;
          }
      
          defineLayer(/** @type {string} */ name, /** @type {number=} */ order = 0) {
              const layer = this.__getLayer(name);
      
              // The default layer always has order 0.
              const previousOrder = layer.order;
              layer.order = name === "default" ? 0 : order;
      
              // Sort only if order changed.
              if (previousOrder !== layer.order)
                  this.__layers.sort((a, b) => a.order - b.order);
      
              return layer;
          }
      
          /**
           * Get a layer by name (if it doesn't exist, creates it with default order 0).
           * @private
           */
          __getLayer(/** @type {string} */ name) {
              let layer = this.__layers.find((l) => l.name === name);
      
              if (!layer) {
                  layer = { name, backingScene: new this.__Scene(), order: 0 };
                  layer.backingScene.autoUpdate = false;
                  this.__layers.push(layer);
              }
      
              return layer;
          }
      
          removeLayer(/** @type {string} */ name) {
              const index = this.__layers.findIndex((l) => {
                  if (l.name === name) {
                      l.backingScene.children.length = 0;
                      return true;
                  }
      
                  return false;
              });
      
              if (index >= 0) this.__layers.splice(index, 1);
          }
      
          hasLayer(/** @type {string} */ name) {
              return this.__layers.some((l) => l.name === name);
          }
      
          /** @readonly */
          get layerCount() {
              return this.__layers.length;
          }
      
          addObjectToLayer(
              /** @type {THREE.Object3D} */ obj,
              /** @type {string | string[]} */ layers
          ) {
              if (Array.isArray(layers)) {
                  for (const name of layers) this.__addObjectToLayer(obj, name);
                  return;
              }
      
              this.__addObjectToLayer(obj, layers);
          }
      
          addObjectsToLayer(
              /** @type {THREE.Object3D[]} */ objects,
              /** @type {string | string[]} */ layers
          ) {
              for (const obj of objects) {
                  this.addObjectToLayer(obj, layers);
              }
          }
      
          /** @private @readonly */
          __emptyArray = Object.freeze([]);
      
          /** @private */
          __addObjectToLayer(
              /** @type {THREE.Object3D} */ obj,
              /** @type {string} */ name
          ) {
              const layer = this.__getLayer(name);
              const proxy = Object.create(obj, {
                  children: { get: () => this.__emptyArray }
              });
              layer.backingScene.children.push(proxy);
          }
      
          removeObjectFromLayer(
              /** @type {THREE.Object3D} */ obj,
              /** @type {string | string[]} */ nameOrNames
          ) {
              if (Array.isArray(nameOrNames)) {
                  for (const name of nameOrNames) {
                      const layer = this.__layers.find((l) => l.name === name);
                      if (!layer) continue;
                      this.__removeObjectFromLayer(obj, layer);
                  }
                  return;
              }
      
              const layer = this.__layers.find((l) => l.name === nameOrNames);
              if (!layer) return;
              this.__removeObjectFromLayer(obj, layer);
          }
      
          /** @private */
          __removeObjectFromLayer(
              /** @type {THREE.Object3D} */ obj,
              /** @type {Layer} */ layer
          ) {
              const children = layer.backingScene.children;
              const index = children.findIndex(
                  (proxy) => /** @type {any} */ (proxy).__proto__ === obj
              );
      
              if (index >= 0) {
                  children[index] = children[children.length - 1];
                  children.pop();
              }
          }
      
          removeObjectsFromAllLayers(/** @type {THREE.Object3D[]} */ objects) {
              for (const layer of this.__layers) {
                  for (const obj of objects) {
                      this.__removeObjectFromLayer(obj, layer);
                  }
              }
          }
      
          render(
              /** @type {THREE.Camera} */ camera,
              /** @type {(layerName: string) => void} */ beforeEach,
              /** @type {(layerName: string) => void} */ afterEach
          ) {
              for (const layer of this.__layers) {
                  beforeEach(layer.name);
                  this.__renderer.render(layer.backingScene, camera);
                  afterEach(layer.name);
              }
          }
      }
      
      //////////////////////
      // VARS
      //////////////////////
      
      let camera, stats, geometry, material, object, object2, root;
      let time = 0;
      /** @type {THREE.Scene} */
      let scene;
      /** @type {THREE.WebGLRenderer} */
      let renderer;
      /** @type {VisualLayers} */
      let visualLayers;
      const clock = new THREE.Clock();
      const greenColor = "#27ae60";
      const options = {
          useLayers: true,
          showMiddleBox: true,
          rotate: true,
          layer2Order: 2
      };
      
      //////////////////////
      // INIT
      //////////////////////
      
      ~(function init() {
          setup3D();
          renderLoop();
      })();
      
      ////////////////////////////////
      // SETUP 3D
      ////////////////////////////////
      
      function setup3D() {
          const container = document.createElement("div");
          container.id = "container";
          document.body.appendChild(container);
      
          // CAMERA
          camera = new THREE.PerspectiveCamera(
              70,
              window.innerWidth / window.innerHeight,
              1,
              10000
          );
          camera.position.x = 0;
          camera.position.z = 500;
          camera.position.y = 0;
      
          scene = new THREE.Scene();
      
          // RENDERERS
      
          renderer = new THREE.WebGLRenderer({ antialias: true, alpha: true });
          renderer.setClearColor(0x111111);
          container.appendChild(renderer.domElement);
      
          // LAYERS
      
          visualLayers = new VisualLayers(renderer);
          // Layers don't have to be defined. Adding an object to a layer will
          // automatically create the layer with order 0. But let's define layers with
          // order values.
          visualLayers.defineLayer("layer1", 1);
          visualLayers.defineLayer("layer2", 2);
          visualLayers.defineLayer("layer3", 3);
      
          // LIGHTS
      
          const directionalLight = new THREE.DirectionalLight(0xffffff, 0.5);
          directionalLight.position.set(300, 0, 300);
          scene.add(directionalLight);
          visualLayers.addObjectToLayer(directionalLight, [
              "layer1",
              "layer2",
              "layer3"
          ]);
      
          const ambientLight = new THREE.AmbientLight(0xffffff, 0.4);
          scene.add(ambientLight);
          visualLayers.addObjectToLayer(ambientLight, ["layer1", "layer2", "layer3"]);
      
          // GEOMETRY
      
          root = new THREE.Object3D();
          scene.add(root);
      
          geometry = new THREE.BoxGeometry(100, 100, 100);
          material = new THREE.MeshPhongMaterial({
              color: greenColor,
              transparent: false,
              opacity: 1
          });
      
          object = new THREE.Mesh(geometry, material);
          root.add(object);
          visualLayers.addObjectToLayer(object, "layer1");
          object.position.y = 80;
          object.position.z = -20;
          // object.rotation.y = -Math.PI / 5
      
          object2 = new THREE.Mesh(geometry, material);
          object.add(object2);
          visualLayers.addObjectToLayer(object2, "layer2");
          object2.position.y -= 80;
          object2.position.z = -20;
          object2.rotation.y = -Math.PI / 5;
      
          const object3 = new THREE.Mesh(geometry, material);
          object2.add(object3);
          visualLayers.addObjectToLayer(object3, "layer3");
          object3.position.y -= 80;
          object3.position.z = -20;
          object3.rotation.y = -Math.PI / 5;
      
          // GUI
      
          const pane = new Tweakpane({
              title: "VisualLayers"
          });
          pane.addInput(options, "useLayers", { label: "use layers" });
          pane.addInput(options, "showMiddleBox", { label: "show middle box" });
          pane.addInput(options, "rotate");
          pane
              .addInput(options, "layer2Order", {
                  label: "layer2 order",
                  options: {
                      0: 0,
                      2: 2,
                      4: 4
                  }
              })
              .on("change", () => visualLayers.defineLayer("layer2", options.layer2Order));
      
          // STATS
          // SEE: https://github.com/mrdoob/stats.js
      
          stats = new Stats();
          stats.domElement.style.position = "absolute";
          stats.domElement.style.left = "0px";
          stats.domElement.style.top = "0px";
          stats.setMode(0);
          document.body.appendChild(stats.domElement);
      }
      
      //////////////////////
      // RESIZE
      //////////////////////
      
      (window.onresize = function (event) {
          camera.aspect = window.innerWidth / window.innerHeight;
          camera.updateProjectionMatrix();
          renderer.setSize(window.innerWidth, window.innerHeight);
      })();
      
      //////////////////////
      // RAF RENDER LOOP
      //////////////////////
      
      function render() {
          stats.begin();
      
          if (options.rotate) {
              time += clock.getDelta();
              object.rotation.y += 0.02;
              root.rotation.y = Math.PI / 2 + (Math.PI / 6) * Math.sin(time * 0.001);
          }
      
          object2.visible = options.showMiddleBox;
      
          if (options.useLayers) {
              scene.updateWorldMatrix(true, true);
              renderer.autoClear = false;
              renderer.clear();
              visualLayers.render(camera, beforeEachLayerRender, afterEachLayerRender);
          } else {
              renderer.autoClear = true;
              renderer.render(scene, camera);
          }
      
          stats.end();
      }
      
      function renderLoop() {
          render();
          requestAnimationFrame(renderLoop);
      }
      
      function beforeEachLayerRender(layer) {}
      function afterEachLayerRender(layer) {
          renderer.clearDepth();
      }
      html,
      body,
      #container {
          margin: 0px;
          padding: 0px;
          width: 100%;
          height: 100%;
      }
      
      canvas {
          background: transparent;
          display: block;
          width: 100%;
          height: 100%;
          position: absolute;
          left: 0;
          top: 0;
      }
      <script src="https://cdn.jsdelivr.net/npm/tweakpane@1.5.5/dist/tweakpane.min.js"></script>
      <script src="//cdnjs.cloudflare.com/ajax/libs/stats.js/r11/Stats.min.js"></script>
      <script src="//unpkg.com/three@0.121.1/build/three.min.js"></script>
      <script src="//unpkg.com/postprocessing@6.17.4/build/postprocessing.js"></script>

      (example on codepen)

      【讨论】:

        猜你喜欢
        • 2019-07-30
        • 1970-01-01
        • 2016-03-14
        • 2015-04-16
        • 2019-10-09
        • 1970-01-01
        • 2018-10-24
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多