多个场景与文字交错

时间:2019-02-22 17:39:17

标签: javascript three.js

我正在考虑将three.js用于my 3D model visualization Sphinx extension。该扩展程序使您可以轻松地可视化文档中的交互式3D对象,但是currently uses an oudated and unmaintained library for that

three.js库包含大量示例。其中,有一个引起了我的注意:WebGL multiple elements text示例。

这是一个很好的例子,因为它满足了我对Sphinx扩展的需求:

  • 它允许在一个页面中显示多个场景
  • 所有场景共享同一个WebGLRenderer,因此您可以将多个场景放在一个页面中
  • 它允许插入文本和场景

现在,它只有一个警告:CPU / GPU消耗。即使您既不滚动也不与3D可视化交互时,CPU / GPU似乎也很忙。

起初,我认为可能是由于每个场景中的动画所致。我不需要为扩展程序添加动画,而只需要交互,因此我决定删除它们并仅在场景用户交互上强制重新渲染:

diff --git a/examples/webgl_multiple_elements_text.html b/examples/webgl_multiple_elements_text.html
index 289e4935a..e9fe7e087 100644
--- a/examples/webgl_multiple_elements_text.html
+++ b/examples/webgl_multiple_elements_text.html
@@ -196,6 +196,7 @@
                    scene.userData.camera = camera;

                    var controls = new THREE.OrbitControls( camera, views[ n ] );
+                   controls.addEventListener( 'change', render );
                    scene.userData.controls = controls;

                    scenes.push( scene );
@@ -223,7 +224,6 @@
            function animate() {

                render();
-               requestAnimationFrame( animate );

            }

有了一个非常简单的更改:

  • 动画已停止,我不介意
  • CPU / GPU消耗也停止了,这是我想要的
  • 场景仍然是交互式的(即:您可以移动摄像机),并且如果我与它们积极交互,我当然不介意使用CPU / GPU

但是,现在存在一个问题:如果尝试向下滚动页面,场景将不会随着文本滚动。

我该如何解决?

注意事项:

  • 目标是在用户不滚动或不与场景进行交互的同时使CPU / GPU消耗为“ 0”(毕竟,它打算用于文档页面中,期望用户阅读最多的文档页面)时间)
  • 如果滚动不会对CPU / GPU产生明显影响,那就太好了;那有可能吗?还是我们需要在滚动时重新绘制所有场景,就像在与它们交互时一样?

1 个答案:

答案 0 :(得分:1)

添加队列以在scroll事件中调用渲染

window.addEventListerner('scroll', queueRenderIfNotQueued);

let renderQueued = false;
function render() {
  renderQueued = false;
  ...
}

function queueRenderIfNotQueued() {
  if (!renderQueued) {
    renderQueued = true;
    requestAnimationFrame(render);
  }
}

您可能还希望以调整大小进行渲染

window.addEventListerner('resize', queueRenderIfNotQueued);

这是一个根据this pagethis page

的想法修改而成的示例

'use strict';

/* global THREE */

function main() {
  const canvas = document.querySelector('#c');
  const renderer = new THREE.WebGLRenderer({
    canvas: canvas,
    alpha: true
  });

  const sceneElements = [];

  function addScene(elem, fn) {
    sceneElements.push({
      elem,
      fn
    });
  }

  function makeScene(elem) {
    const scene = new THREE.Scene();

    const fov = 45;
    const aspect = 2; // the canvas default
    const near = 0.1;
    const far = 5;
    const camera = new THREE.PerspectiveCamera(fov, aspect, near, far);
    camera.position.set(0, 1, 2);
    camera.lookAt(0, 0, 0);

    const controls = new THREE.OrbitControls(camera, elem);
    controls.enableZoom = false;
    controls.enablePan = false;

    {
      const color = 0xFFFFFF;
      const intensity = 1;
      const light = new THREE.DirectionalLight(color, intensity);
      light.position.set(-1, 2, 4);
      scene.add(light);
    }

    const geometry = new THREE.BoxBufferGeometry(1, 1, 1);
    const material = new THREE.MeshPhongMaterial({color:'red'});
    const mesh = new THREE.Mesh(geometry, material);
    scene.add(mesh);
    
    function render(rect) {
      camera.aspect = rect.width / rect.height;
      camera.updateProjectionMatrix();
      renderer.render(scene, camera);    
    }
    
    function renderWithBounds() {
      const rect = elem.getBoundingClientRect();
      const {
        left,
        right,
        top,
        bottom,
        width,
        height
      } = rect;

      renderer.setViewport(left, top, width, height);
      renderer.setScissor(left, top, width, height);
      render(rect);
    }
    
    controls.addEventListener('change', renderWithBounds);

    addScene(elem, render);
  }

  [...document.querySelectorAll('.diagram')].forEach(makeScene);
 

  function resizeRendererToDisplaySize(renderer) {
    const canvas = renderer.domElement;
    const width = canvas.clientWidth;
    const height = canvas.clientHeight;
    const needResize = canvas.width !== width || canvas.height !== height;
    if (needResize) {
      renderer.setSize(width, height, false);
    }
    return needResize;
  }

  const clearColor = new THREE.Color('#000');

  let renderQueued = false;
  function render() {
    renderQueued = false;
    resizeRendererToDisplaySize(renderer);

    renderer.setScissorTest(false);
    renderer.setClearColor(clearColor, 0);
    renderer.clear(true, true);
    renderer.setScissorTest(true);

    const transform = `translateY(${window.scrollY}px)`;
    renderer.domElement.style.transform = transform;

    for (const {
        elem,
        fn
      } of sceneElements) {
      // get the viewport relative position opf this element
      const rect = elem.getBoundingClientRect();
      const {
        left,
        right,
        top,
        bottom,
        width,
        height
      } = rect;

      const isOffscreen =
        bottom < 0 ||
        top > renderer.domElement.clientHeight ||
        right < 0 ||
        left > renderer.domElement.clientWidth;

      if (!isOffscreen) {
        renderer.setViewport(left, top, width, height);
        renderer.setScissor(left, top, width, height);

        fn(rect);
      }
    }
  }

  function queueRenderIfNotQueued() {
    if (!renderQueued) {
      renderQueued = true;
      requestAnimationFrame(render);
    }
  }
  
  queueRenderIfNotQueued();
  window.addEventListener('scroll', queueRenderIfNotQueued);
  window.addEventListener('resize', queueRenderIfNotQueued);
}

main();
#c {
  position: absolute;
  left: 0;
  top: 0;
  width: 100vw;
  height: 100vh;
  display: block;
  z-index: -1;
}

.diagram {
  display: inline-block;
  width: 5em;
  height: 3em;
}

.left {
  float: left;
  margin-right: .25em;
}

.right {
  float: right;
  margin-left: .25em;
}

p {
  margin: 1em auto;
  max-width: 500px;
  font-size: xx-large;
}
<canvas id="c"></canvas>
<p>
  <span class="diagram left"></span>Lorem ipsum dolor sit amet, consectetur adipiscing elit. Vivamus mi turpis, pellentesque sed aliquam vel, tincidunt eget massa. Lorem ipsum dolor sit amet, consectetur adipiscing elit.
</p>
<p>
  <span class="diagram right"></span>Lorem ipsum dolor sit amet, consectetur adipiscing elit. Vivamus mi turpis, pellentesque sed aliquam vel, tincidunt eget massa. Lorem ipsum dolor sit amet, consectetur adipiscing elit.
</p>
<p>
  <span class="diagram left"></span>Lorem ipsum dolor sit amet, consectetur adipiscing elit. Vivamus mi turpis, pellentesque sed aliquam vel, tincidunt eget massa. Lorem ipsum dolor sit amet, consectetur adipiscing elit.
</p>
<p>
  <span class="diagram right"></span>Lorem ipsum dolor sit amet, consectetur adipiscing elit. Vivamus mi turpis, pellentesque sed aliquam vel, tincidunt eget massa. Lorem ipsum dolor sit amet, consectetur adipiscing elit.
</p>
<p>
  <span class="diagram left"></span>Lorem ipsum dolor sit amet, consectetur adipiscing elit. Vivamus mi turpis, pellentesque sed aliquam vel, tincidunt eget massa. Lorem ipsum dolor sit amet, consectetur adipiscing elit.
</p>
<p>
  <span class="diagram right"></span>Lorem ipsum dolor sit amet, consectetur adipiscing elit. Vivamus mi turpis, pellentesque sed aliquam vel, tincidunt eget massa. Lorem ipsum dolor sit amet, consectetur adipiscing elit.
</p>

<script src="https://threejsfundamentals.org/threejs/resources/threejs/r98/three.min.js"></script>
<script src="https://threejsfundamentals.org/threejs/resources/threejs/r98/js/controls/OrbitControls.js"></script>