是否有可能在THREE.js中启用无限数量的渲染器? [英] Is it possible to enable unbounded number of renderers in THREE.js?

查看:193
本文介绍了是否有可能在THREE.js中启用无限数量的渲染器?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

为了避免XY问题,让我解释一下我来自哪里。
我想使用THREE.js绘制使用相同时间轴堆叠在一起的大量波形。波形只是THREE.Line和我正在通过修改正交相机的视图边界来实现这些波形的缩放/平移/缩放。

In order to avoid the XY problem, let me explain where I'm coming from. I would like to plot a large number of waveforms stacked on top of each other using the same time axis, using THREE.js. The waveforms are simply THREE.Line's and I am implementing zoom/pan/scaling of these waveforms by modifying the view bounds of an Orthographic camera.

我最初尝试完成此操作引导我创建多个具有固定高度的画布元素,彼此叠加,并将THREE.WebGLRenderer附加到每个画布。
这很好用,直到我尝试将其缩放到15个左右的波形,其中THREE.js给了我一个警告太多活动的webgl上下文,并开始删除旧的上下文。

My initial attempt at accomplishing this lead me to create multiple canvas elements with fixed height, stacked on top of each other, and attach a THREE.WebGLRenderer to each canvas. This worked perfectly, until I tried scaling it past 15 or so waveforms, where THREE.js gave me a warning "too many active webgl contexts", and started deleting old contexts.

我认为这是一种不错的做法,考虑到它采用的技术相同: http:// threejs.org/examples/#webgl_multiple_canvases_grid

I feel like this is decent practice, considering it's the same technique applied here: http://threejs.org/examples/#webgl_multiple_canvases_grid

在此示例中,创建了4个WebGLRenderers,每个画布一个。

In this example, 4 WebGLRenderers are created, one for each canvas.

那么,是否有可能以某种方式覆盖此警告,并创建无限数量的canvas元素,每个元素都有自己的渲染器?

So, is it possible to override this warning somehow, and create an unbounded number of canvas elements, each with their own renderer?

ASIDE:

我考虑过使用一个场景并相应地定位波形,并使用多个摄像头,其方法类似于 http://threejs.org/examples/#webgl_multiple_views

I have considered using one scene and positioning waveforms accordingly within it, and using multiple cameras with an approach similar to http://threejs.org/examples/#webgl_multiple_views.

问题有两个:

(1)我失去了操作和轻松附加密钥的能力基于每个波形的鼠标监听器。

(1) I lose the ability to dom-manipulate and easily attach key and mouse listeners on a per-waveform basis.

(2)此解决方案似乎也没有扩展。一旦渲染器的高度超过6000px高度,它就会开始进入某种类型的腐败状态,并且部分场景不会出现,其余内容会出现拉伸以进行补偿。

(2) This solution doesn't seem to scale either. Once the renderer's height passes somewhere around 6000px height, it starts to enter some type of corrupt state and part of the scene doesn't appear, with the rest of the content appearing stretched to compensate.

感谢任何可以提供帮助的人!

Thanks to anyone who can help!

推荐答案

您可以使用一个非滚动的全窗口大小画布,并且占位符DIV为你的波形。然后使用1个渲染器,每个波形有1个场景,并在渲染每个场景之前调用renderer.setViewport和renderer.setScissor以及每个div的位置。

You can use one non-scrolling full window size canvas, and place holders DIVs for your wave forms. Then with 1 renderer have 1 scene per waveform and call renderer.setViewport and renderer.setScissor with the location of each div before rendering each scene.

实际上像这样

renderer.setScissorTest( true );
scenes.forEach( function( scene ) {

  // get the element that is a place holder for where we want to
  // draw the scene
  var viewElement = scene.viewElement;

  // get its position relative to the page's viewport
  var rect = viewElement.getBoundingClientRect();

  // check if it's offscreen. If so skip it
  if ( rect.bottom < 0 || rect.top  > renderer.domElement.clientHeight ||
     rect.right  < 0 || rect.left > renderer.domElement.clientWidth ) {
    return;  // it's off screen
  }

  // set the viewport
  var width  = rect.right - rect.left;
  var height = rect.bottom - rect.top;
  var left   = rect.left;
  var top    = rect.top;

  renderer.setViewport( left, top, width, height );
  renderer.setScissor( left, top, width, height );

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

  renderer.render( scene, camera );
} );
renderer.setScissorTest( false );

示例:

var canvas;

var scenes = [], camera, renderer, emptyScene;

init();
animate();

function init() {

  canvas = document.getElementById( "c" );

  camera = new THREE.PerspectiveCamera( 75, 1, 0.1, 100 );
  camera.position.z = 1.5;

  var geometries = [
    new THREE.BoxGeometry( 1, 1, 1 ),
    new THREE.SphereGeometry( 0.5, 12, 12 ),
    new THREE.DodecahedronGeometry( 0.5 ),
    new THREE.CylinderGeometry( 0.5, 0.5, 1, 12 ),
  ];

  var template = document.getElementById("template").text;
  var content = document.getElementById("content");

  var emptyScene = new THREE.Scene();

  var numScenes = 100;

  for ( var ii =  0; ii < numScenes; ++ii ) {

    var scene = new THREE.Scene();

    // make a list item.
    var element = document.createElement( "div" );
    element.innerHTML = template;
    element.className = "list-item";

    // Look up the element that represents the area
    // we want to render the scene
    scene.element = element.querySelector(".scene");
    content.appendChild(element);

    // add one random mesh to each scene
    var geometry = geometries[ geometries.length * Math.random() | 0 ];
    var material = new THREE.MeshLambertMaterial( { color: randColor() } );

    scene.add( new THREE.Mesh( geometry, material ) );

    light = new THREE.DirectionalLight( 0xffffff );
    light.position.set( 0.5, 0.8, 1 );
    scene.add( light );

    light = new THREE.DirectionalLight( 0xffffff );
    light.position.set( -0.5, -0.8, -1 );
    scene.add( light );

    scenes.push( scene );
  }


  renderer = new THREE.WebGLRenderer( { canvas: canvas, antialias: true } );
  renderer.setClearColor( 0xFFFFFF );

}

function updateSize() {

  var width = canvas.clientWidth;
  var height = canvas.clientHeight;

  if ( canvas.width !== width || canvas.height != height ) {

    renderer.setSize ( width, height, false );

  }

}

function animate() {

  render();

  requestAnimationFrame( animate );
}

function render() {

  updateSize();
  
  canvas.style.transform = `translateY(${window.scrollY}px`;

  renderer.setClearColor( 0xFFFFFF );
  renderer.clear( true );
  renderer.setClearColor( 0xE0E0E0 );

  renderer.setScissorTest( true );
  scenes.forEach( function( scene ) {
    // so something moves
    scene.children[0].rotation.x = Date.now() * 0.00111;
    scene.children[0].rotation.z = Date.now() * 0.001;

    // get the element that is a place holder for where we want to
    // draw the scene
    var element = scene.element;

    // get its position relative to the page's viewport
    var rect = element.getBoundingClientRect();

    // check if it's offscreen. If so skip it
    if ( rect.bottom < 0 || rect.top  > renderer.domElement.clientHeight ||
       rect.right  < 0 || rect.left > renderer.domElement.clientWidth ) {
      return;  // it's off screen
    }

    // set the viewport
    var width  = rect.right - rect.left;
    var height = rect.bottom - rect.top;
    var left   = rect.left;
    var top    = rect.top;

    renderer.setViewport( left, top, width, height );
    renderer.setScissor( left, top, width, height );

    camera.aspect = width / height;
    camera.updateProjectionMatrix();
    renderer.setViewport( left, top, width, height );
    renderer.setScissor( left, top, width, height );
    renderer.render( scene, camera );

  } );
  renderer.setScissorTest( false );

}

function rand( min, max ) {
  if ( max == undefined ) {
    max = min;
    min = 0;
  }

  return Math.random() * ( max - min ) + min;
}

function randColor() {
  var colors = [ rand( 256 ), rand ( 256 ), rand( 256 ) ];
  colors[ Math.random() * 3 | 0 ] = 255;
  return ( colors[0] << 16 ) |
       ( colors[1] <<  8 ) |
       ( colors[2] <<  0 ) ;
}

* {
  box-sizing: border-box;
  -moz-box-sizing: border-box;
}

body {
  color: #000;
  font-family:Monospace;
  font-size:13px;

  background-color: #fff;
  margin: 0;
}


#content {
  position: absolute;
  top: 0; width: 100%;
  z-index: 1;
  padding: 2em;
}

#c {
  position: absolute;
  left: 0;
  top: 0;
  width: 100%;
  height: 100%;
}

.list-item {
  margin: 1em;
  padding: 2em;
  display: -webkit-flex;
  display: flex;
  flex-direction: row;
  -webkit-flex-direction: row;
}

.list-item .scene {
  width: 200px;
  height: 200px;
  flex: 0 0 auto;
  -webkit-flex: 0 0 auto;
}
.list-item .description {
  font-family: sans-serif;
  font-size: large;
  padding-left: 2em;
  flex: 1 1 auto;
  -webkit-flex: 1 1 auto;
}

@media only screen and (max-width : 600px) {
  #content {
    width: 100%;
  }
  .list-item {
    margin: 0.5em;
    padding: 0.5em;
    flex-direction: column;
    -webkit-flex-direction: column;
  }
  .list-item .description {
    padding-left: 0em;
  }
}

<canvas id="c"></canvas>
<div id="content">
</div>
<script id="template" type="notjs">
			<div class="scene"></div>
			<div class="description">some random text about this object, scene, whatever</div>
</script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/three.js/94/three.min.js"></script>

此处的原始解决方案使用了带位置的画布:固定意味着画布没有不滚动。下面的新解决方案将其更改为 position:absolute;顶部:0 然后每帧设置画布的变换

The original solution here used a canvas with position: fixed meaning the canvas did not scroll. The new solution below changes it to position: absolute; top: 0 and then sets the canvas's transform every frame

  canvas.style.transform = `translateY(${window.scrollY}px`;

这样做的好处是,即使我们不能在画布将随页面滚动的每一帧更新画布,直到我们有机会更新它。这使得滚动保持同步。

This has the advantage that even if we can't update the canvas every frame the canvas will scroll with the page until we get a chance to update it. This makes the scrolling stay in sync.

你可以比较旧解决方案新解决方案。两者都设置为每4帧渲染一次以夸大问题。向上和向下滚动它们,差异应该清晰。

You can compare the old solution to the new solution. Both are set to only render every 4th frame to exaggerate the issue. Scroll them up and down and the difference should be clear.

这篇关于是否有可能在THREE.js中启用无限数量的渲染器?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆