1. 多视口
1.1. 什么是视口及实现方法
WebGL视口就是窗口上绘制模型的地方, WebGL一般情况下只有一个窗口,但是可以有多个视口, 有时候我们需要多个视口显示同一个模型的不同角度,这个时候就要用到多视口,在three.js中实现多视口有两种方法,第一种用多个相机,每个相机关联一个视口,显示每个相机的时候,都要重新定义视口在页面上的位置, 第二种只有一个相机,但是这个相机关联多个视口,然后同一个相机分多次绘制,就可以把相机内部的模型绘制到不同的视口内部。
1.2. 多视口的原理
就是每次Render都改变视口矩阵,视口矩阵改变了,模型在屏幕上绘制的位置也就改变了,所以实现多视口可以用多个相机或者一个相机,但是每次视口矩阵都要不同。opengl可以使用glViewPort()改变视口矩阵,three.js可以使用renderer.setViewport( left, bottom, width, height )改变视口矩阵,一般情况下,视口矩阵的参数会被规格化到0和1之间。
也就是把屏幕的长宽定为1.0, 然后根据比例输入参数。
1.3. 案例
three.js中的一个例子webgl_multiple_views.html
最左边的视口定义如下,窗口长宽规格化为1,以左下角为原点,
{
left: 0, 视口在X方向的坐标
bottom: 0, 视口在Y方向的坐标
width: 0.5, 视口宽0.5,长宽都规格化到0和1之间
height: 1.0, 视口高1.0
background: new THREE.Color().setRGB( 0.5, 0.5, 0.7 ), 背景色
eye: [ 0, 300, 1800 ], 眼睛的位置
up: [ 0, 1, 0 ], 向上向量
fov: 30, 抬头的角度
updateCamera: function ( camera, scene, mouseX, mouseY ) {
camera.position.x += mouseX * 0.05;
camera.position.x = Math.max( Math.min( camera.position.x, 2000 ), -2000 );
camera.lookAt( scene.position ); //mousreX, mouseY,鼠标在屏幕上移动的时候的坐标,根据坐标的变化,实现球的运动
}
},
定义完第一个视口后,用同样的方法定义其它两个视口,只要改变left, bottom, width, height,就可以改变视口的位置。
这个例子同样可以形成阴影,方法如下,先形成一个二维的画布,并且在上面绘制一个填充的矩形
var canvas = document.createElement( 'canvas' );
canvas.width = 128;
canvas.height = 128;
形成阴影纹理,这是three.js中的实现阴影的方式
var shadowMaterial = new THREE.MeshBasicMaterial({ map: shadowTexture, transparent: true });
var shadowGeo = new THREE.PlaneBufferGeometry( 300, 300, 1, 1 );
然后把球挂场景上
group1 = THREE.SceneUtils.createMultiMaterialObject( geometry, materials );
group1.position.x = -400; 改变位置
group1.rotation.x = -1.87;
scene.add( group1 );
然后在不同的视口,使用不同的相机绘制,然后就形成了多视口
for ( var ii = 0; ii < views.length; ++ii ) {
view = views[ii];
camera = view.camera;
view.updateCamera( camera, scene, mouseX, mouseY );
//从规格化的视口转变为屏幕坐标
var left = Math.floor( windowWidth * view.left );
var bottom = Math.floor( windowHeight * view.bottom );
var width = Math.floor( windowWidth * view.width );
var height = Math.floor( windowHeight * view.height );
//设置视口在屏幕上的位置
renderer.setViewport( left, bottom, width, height );
renderer.setScissor( left, bottom, width, height );
renderer.enableScissorTest ( true );
renderer.setClearColor( view.background );
camera.aspect = width / height;
camera.updateProjectionMatrix();
renderer.render( scene, camera );
}