为了避免XY问题,让我解释一下我来自哪里。 我想使用THREE.js绘制使用相同时间轴堆叠在一起的大量波形。波形只是THREE.Line和我正在通过修改正交相机的视角来实现这些波形的缩放/平移/缩放。
我最初尝试完成此操作后,我创建了多个具有固定高度的画布元素,彼此堆叠在一起,并将THREE.WebGLRenderer附加到每个画布上。 这非常有效,直到我尝试将其缩放到15个左右的波形,其中THREE.js向我发出警告“太多活动的webgl上下文”,并开始删除旧的上下文。
我认为这是一种体面的做法,考虑到它采用的技术相同:http://threejs.org/examples/#webgl_multiple_canvases_grid
在此示例中,创建了4个WebGLRenderer,每个画布一个。
那么,是否有可能以某种方式覆盖此警告,并创建无限数量的canvas元素,每个元素都有自己的渲染器?
ASIDE:
我考虑过使用一个场景并在其中相应地定位波形,并使用多个摄像头,其方法类似于http://threejs.org/examples/#webgl_multiple_views。
问题有两方面:
(1)我失去了对每个波形进行dom操作并轻松连接键和鼠标监听器的能力。
(2)这个解决方案似乎也没有扩展。一旦渲染器的高度超过6000px高度,它就会开始进入某种类型的腐败状态,并且部分场景不会出现,其余内容会出现拉伸以进行补偿。
感谢任何可以提供帮助的人!
答案 0 :(得分:9)
您可以使用一个非滚动的全窗口大小画布,并为您的波形放置持有者DIV。然后使用1个渲染器,每个波形有1个场景,并在渲染每个场景之前调用renderer.setViewport和renderer.setScissor以及每个div的位置。
有效地喜欢这个
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 ) ;
}
&#13;
* {
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;
}
}
&#13;
<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>
&#13;
这里的原始解决方案使用了带有position: fixed
的画布,这意味着画布没有滚动。下面的新解决方案将其更改为position: absolute; top: 0
,然后每帧设置画布变换
canvas.style.transform = `translateY(${window.scrollY}px`;
这样做的好处是,即使我们无法每帧更新画布,画布也会随页面滚动,直到我们有机会更新它。这使滚动保持同步。
您可以将old solution与new solution进行比较。两者都设置为每4帧渲染一次以夸大问题。向上和向下滚动它们,差异应该是清楚的。