antvis / g-webgl-compute

A GPGPU implementation based on WebGL.
MIT License
144 stars 15 forks source link

支持在单个 canvas 上下文中渲染多个场景 #29

Closed xiaoiver closed 3 years ago

xiaoiver commented 3 years ago

问题背景

渲染多个场景是常见的需求。然而 WebGL 上下文个数是有限制的,超过 8 个再创建就会触发 context lost 事件(目前我们也还没针对该事件进行处理)。 来自:https://threejsfundamentals.org/threejs/lessons/threejs-multiple-scenes.html

因此相比创建多个 canvas 以及 WebGL 上下文,在单个 canvas 中分别渲染多个场景是更好的做法。

实现思路

最直接的思路是分别渲染每个场景到纹理中,最后在指定位置渲染纹理。显然这样需要渲染两次: https://webglfundamentals.org/webgl/lessons/webgl-multiple-views.html

更好的做法是使用 viewport + scissor。在 Three.js 中是这样实现的:

首先关闭 scissor test 完成清屏:

function render(time) { 
  renderer.setScissorTest(false);
  renderer.clear(true, true);
  renderer.setScissorTest(true);

  renderSceneInfo(sceneInfo1);
  renderSceneInfo(sceneInfo2);

  requestAnimationFrame(render);
}

然后在渲染每个场景前:

function renderSceneInfo(sceneInfo) {
  const {scene, camera, elem} = sceneInfo;

  const {left, right, top, bottom, width, height} =
      elem.getBoundingClientRect();

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

  if (isOffscreen) {
    return;
  }

  camera.aspect = width / height;
  camera.updateProjectionMatrix();

  const positiveYUpBottom = canvasRect.height - bottom;
  renderer.setScissor(left, positiveYUpBottom, width, height);
  renderer.setViewport(left, positiveYUpBottom, width, height);

  renderer.render(scene, camera);
}

具体实现

每个 Viewport 可以有独立的相机和交互。

效果如下: