最新消息:雨落星辰是一个专注网站SEO优化、网站SEO诊断、搜索引擎研究、网络营销推广、网站策划运营及站长类的自媒体原创博客

javascript - Display multiple instances of three.js in a single page - Stack Overflow

programmeradmin1浏览0评论

I have a web app where I need to display multiple 3D objects in different containers. For the moment, I have been instantiating multiple three.js renderers, one per container. However, I get this error message: "WARNING: Too many active WebGL contexts. Oldest context will be lost" and after ~10 renderers I can't open any more. Here's an example

How should I go to have multiple three.js containers in a single web page? Is it possible to have a single renderer with multiple scenes and draw each scene in a different container (a different renderer.domElement) ?

Thank you!

I have a web app where I need to display multiple 3D objects in different containers. For the moment, I have been instantiating multiple three.js renderers, one per container. However, I get this error message: "WARNING: Too many active WebGL contexts. Oldest context will be lost" and after ~10 renderers I can't open any more. Here's an example

http://brainspell/article/24996404

How should I go to have multiple three.js containers in a single web page? Is it possible to have a single renderer with multiple scenes and draw each scene in a different container (a different renderer.domElement) ?

Thank you!

Share Improve this question asked Nov 27, 2015 at 14:28 robertoroberto 5075 silver badges12 bronze badges 2
  • 4 See threejs/examples/webgl_multiple_elements.html – WestLangley Commented Nov 27, 2015 at 18:00
  • 1 Possible duplicate of Is it possible to enable unbounded number of renderers in THREE.js? – user128511 Commented Nov 29, 2015 at 4:01
Add a ment  | 

1 Answer 1

Reset to default 7

This has been covered elsewhere but the easiest way is to just use one instance of three.js, make it cover the entire window, put place holder divs where you want to draw things, and then use element.getClientBoundingRect to set the scissor and viewport for each scene you want to draw in each element

There's an example here.

Here's the answer in StackOverflow from which that sample originates

https://stackoverflow./a/30633132/128511


Another solution would be to use another not visible canvas and pass it to all instances of three.js (which will mean they all use the same WebGL context), then copy the results to individual 2D canvas.

Example:

canvas { width: 128px; height: 128px; display: block; }
.outer { border: 1px solid black; margin: 5px; display: inline-block; }
<script type="module">
import * as THREE from 'https://threejsfundamentals/threejs/resources/threejs/r132/build/three.module.js';
import {OrbitControls} from 'https://threejsfundamentals/threejs/resources/threejs/r132/examples/jsm/controls/OrbitControls.js';

function main(visibleCanvas, offscreenSharedCanvas) {
  const visibleCanvas2DContext = visibleCanvas.getContext('2d');
  const renderer = new THREE.WebGLRenderer({canvas: offscreenSharedCanvas});

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

  const controls = new OrbitControls(camera, visibleCanvas);
  controls.target.set(0, 5, 0);
  controls.update();

  const scene = new THREE.Scene();
  scene.background = new THREE.Color('black');

  {
    const planeSize = 40;

    const loader = new THREE.TextureLoader();
    const texture = loader.load('https://threejsfundamentals/threejs/resources/images/checker.png');
    texture.wrapS = THREE.RepeatWrapping;
    texture.wrapT = THREE.RepeatWrapping;
    texture.magFilter = THREE.NearestFilter;
    const repeats = planeSize / 2;
    texture.repeat.set(repeats, repeats);

    const planeGeo = new THREE.PlaneGeometry(planeSize, planeSize);
    const planeMat = new THREE.MeshPhongMaterial({
      map: texture,
      side: THREE.DoubleSide,
    });
    const mesh = new THREE.Mesh(planeGeo, planeMat);
    mesh.rotation.x = Math.PI * -.5;
    scene.add(mesh);
  }
  {
    const cubeSize = 4;
    const cubeGeo = new THREE.BoxGeometry(cubeSize, cubeSize, cubeSize);
    const cubeMat = new THREE.MeshPhongMaterial({color: '#8AC'});
    const mesh = new THREE.Mesh(cubeGeo, cubeMat);
    mesh.position.set(cubeSize + 1, cubeSize / 2, 0);
    scene.add(mesh);
  }
  {
    const sphereRadius = 3;
    const sphereWidthDivisions = 32;
    const sphereHeightDivisions = 16;
    const sphereGeo = new THREE.SphereGeometry(sphereRadius, sphereWidthDivisions, sphereHeightDivisions);
    const sphereMat = new THREE.MeshPhongMaterial({color: '#CA8'});
    const mesh = new THREE.Mesh(sphereGeo, sphereMat);
    mesh.position.set(-sphereRadius - 1, sphereRadius + 2, 0);
    scene.add(mesh);
  }

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

  // need to resize both the visibleCanvas and the offscreenSharedCanvas
  function resizeRendererToDisplaySize(renderer) {
    const canvas = renderer.domElement;
    const width = visibleCanvas.clientWidth;
    const height = visibleCanvas.clientHeight;
    const needResize = offscreenSharedCanvas.width !== width || offscreenSharedCanvas.height !== height ||
                       visibleCanvas.width !== width || visibleCanvas.height !== height;
    if (needResize) {
      renderer.setSize(width, height, false);
      visibleCanvas.width = width;
      visibleCanvas.height = height;
    }
    return needResize;
  }

  let requestId;
  let visible;
  function render() {
    requestId = undefined;

    if (resizeRendererToDisplaySize(renderer)) {
      camera.aspect = visibleCanvas.clientWidth / visibleCanvas.clientHeight;
      camera.updateProjectionMatrix();
    }

    renderer.render(scene, camera);
    
    // copy the offscreenSharedCanvas to the visible canvas
    visibleCanvas2DContext.drawImage(offscreenSharedCanvas, 0, 0);

    if (visible) {
      startRendering();
    }
  }
  
  function startRendering() {
    if (!requestId) {
      requestId = requestAnimationFrame(render);
    }
  }

  // use an intersection observer to only render this canvas when on screen
  const intersectionObserver = new IntersectionObserver(entries => {
    if (entries[0].isIntersecting) {
      visible = true;
      startRendering();
    } else {
      visible = false;
    }
  });
  intersectionObserver.observe(visibleCanvas);
}

const offscreenSharedCanvas = document.createElement('canvas');
for (let i = 0; i < 20; ++i) {
  const outer = document.createElement('div');
  outer.className = 'outer';
  document.body.appendChild(outer);
  const canvas = document.createElement('canvas');
  outer.appendChild(canvas);
  main(canvas, offscreenSharedCanvas);
}

</script>

The copying might make it slow. Ideally you'd use an IntersectionObserver to start/stop any canvas offscreen from updating.

发布评论

评论列表(0)

  1. 暂无评论