ThreeJS学习笔记五:二维几何体元素及穿梭动画

二维几何体

ThreeJS可以创建三种二维几何体,包括CircleGeometry(圆形),PlaneGeometry(矩形),ShapeGeometry(自定义形状)。

创建二维几何体和创建三维几何体差不多,同样由形状和材质两个参数,拥有的属性也和三维几何体一样。 
new THREE.Mesh(new THREE.PlaneGeometry(width, height, 1, 1 ),new THREE.MeshBasicMaterial(MaterialParam ));

需要注意的是,由于贴图的尺寸必须是(2的幂数)X (2的幂数),如:1024*512,所以为了防止贴图变形,平面的宽度比例需要与贴图的比例一致。 
代码示例如下:

function createPlane(options){
//      options={          
//          width:0,
//          height:0,
//          pic:"",
//          transparent:true,
//          opacity:1
//          blending:false
//      }
       if(typeof options.pic=="string"){//传入的材质是图片路径,使用 textureloader加载图片作为材质
           var loader = new THREE.TextureLoader();
           loader.setCrossOrigin( this.crossOrigin );
           var texture = loader.load( options.pic, function() {}, undefined, function(){});
       }else{传入的材质是canvas
           var texture= new THREE.CanvasTexture( options.pic )
       }
       var MaterParam={//材质的参数
               map:texture,
               overdraw: true,
               side: THREE.FrontSide,
//              blending: THREE.AdditiveBlending,
               transparent: options.transparent,
               //needsUpdate:true,
               //premultipliedAlpha: true,
               opacity:options.opacity
           }
       if(options.blending){
           MaterParam.blending=THREE.AdditiveBlending//使用饱和度叠加渲染
       }
       var plane = new THREE.Mesh(
           new THREE.PlaneGeometry( options.width, options.height, 1, 1 ),
           new THREE.MeshBasicMaterial(MaterParam  )
       );
       return plane;
   }

在3维空间中还原平面元素在设计稿上的大小和位置

设计稿如下图,要求三维空间中,A元素和背景处于不同的深度,A元素位于背景前方

1472558137624.png

那么在三维空间中,与相机所在位置的相对空间位置如下图所示,

1472558264171.png

A在该平面元素在三维空间中的位置如上图所示,面B为A平面最终渲染在屏幕上的区域,此区域应该与设计稿上的A的大小一致,那必然就有一个问题,在三维空间中,A元素的大小和坐标要如何设置,才能使A在屏幕上的投影B能与设计稿上的一致? 
这里我们需要做一些计算,计算中会用到以下已知数值:

  • fov:创建相机时设置的垂直方向的夹角,

  • W:canvas的width,这里以1920为示例

  • H:canvas的height,这里以1080为示例

  • D:相机与屏幕所在平面的距离,

  • d:相机与元素A的距离,

  • aw: 元素A在设计稿上的宽度

  • ah: 元素A在设计稿上的高度

  • ax: 元素A中点心在设计稿上的中心点X轴上的偏移值

  • ay: 元素A中点心在设计稿上的中心点Y轴上的偏移值

其中D可以视为在屏幕上投影面积为1920(W)*1080(H)的平面元素离相机的距离 
计算公式为:D=H/(2*Math.tan(fov/2));

平面A在三维空间中尺寸与投影大小(即设计稿上的尺寸)的缩放倍数为 
zoom=d/D=d*2*Math.tan(fov/2)/H 
由此可得出平面元素A在三维空间中的大小设置为 
w=aw*d*2*Math.tan(fov/2)/H; 
h=ah*d*2*Math.tan(fov/2)/H; 
位置坐标为 
x=ax*d*2*Math.tan(fov/2)/H; 
y=ay*d*2*Math.tan(fov/2)/H;

以下创建多个平面的代码示例:

var fov=45;
var camera = new THREE.PerspectiveCamera( fov, window.innerWidth / window.innerHeight, 1, 300000 );
camera.position.set(0, 0, 1500);
var planes=[
   ["ship1","../resource/part1/ship1.png",1024,1024,700,-400,0,1],//name,pic,w,h,x,y,z,透明度
   ["plan1","../resource/part1/plan_1.png",1024,256,-170,50,-2000,1],
   ["air","../resource/part1/airplane_1.png",512,512,150,100,-3000,1],
   ["star1","../resource/part1/star_1.png",2317,979,150,0,-4500,1],
   ["star2","../resource/part1/star_2.png",256,128,500,-100,-6000,1],
   ["light","../resource/part1/light.png",1152,1024,200,50,-7500,1],
   ["part1_bg","../resource/part1/part1_bg.jpg",2048*1.5,1024*1.5,0,0,-18000,1]
]
planes.forEach(function(planeSet){      
   var scale=(camera.position.z-planeSet[6])*2*Math.tan(fov/2*Math.PI/180)/1080;//此处的1080为设计稿的高度
   var plane=createPlane({        
       width:parseInt(planeSet[2]*scale),
       height:parseInt(planeSet[3]*scale),
       pic:planeSet[1],
       transparent:true,
       opacity:1//planeSet[7]
   })
   plane.position.set(parseInt(planeSet[4]*scale),parseInt(planeSet[5]*scale),planeSet[6]);

   plane.name=planeSet[0];

   scene.add(plane);
})

穿梭动画

穿梭动画是通过修改camera.position.z坐标来实现的。 
需要说明的是,要改变position.z的时候还要注意camera.lookAt的坐标点,若camera.position.z超过了lookAt.z,画面会以180度翻转过来,所以要保持在一个纵向深度可穿越的话,需要将camera.lookAt的z坐标设置比所有可见元素的Z坐标还要大一点。 
同时,还要注意camera的最大可视深度。

window.addEventListener( 'mousewheel', onMouseWheel, false );
function onMouseWheel(event){
   if(event.wheelDelta>0){
       new TWEEN.Tween( camera.position )
       .to( {z:camera.position.z+2500}, 500 )
       .start();
   }
   if(event.wheelDelta<0){
       new TWEEN.Tween( camera.position )
       .to( {z:camera.position.z-2500}, 500 )
       .start();
   }
}

function render() {
   camera.position.x += ( mouseX - camera.position.x ) ;
   camera.position.y += ( mouseY - camera.position.y ) ;
   camera.lookAt( new THREE.Vector3(0,0,-20000) );//此处-20000要设置得比最远的平面元素更大一些。
   renderer.render( scene, camera );

}

本章DEMO

<!DOCTYPE html>
<html>
   <head>
       <meta charset="UTF-8">
       <title></title>
   </head>
   <body>
   <div id="space"></div>  
   <script   src="https://code.jquery.com/jquery-1.12.4.min.js"   integrity="sha256-ZosEbRLbNQzLpnKIkEdrPv7lOy9C27hHQ+Xp8a4MxAQ="   crossorigin="anonymous"></script>
   <script src="../js/lib/threejs/three.js"></script>
   <script src="../js/lib/Tween.js"></script>

   <script>


           var container, stats;

           var camera, scene, renderer;

           var mouseX = 0, mouseY = 0;

           var windowHalfX = window.innerWidth / 2;
           var windowHalfY = window.innerHeight / 2;

           var planes=[
               ["ship1","../resource/part1/ship1.png",1024,1024,700,-400,0,1],//name,pic,w,h,x,y,z,透明度
               ["plan1","../resource/part1/plan_1.png",1024,256,-170,50,-2000,1],
               ["air","../resource/part1/airplane_1.png",512,512,150,100,-3000,1],
               ["star1","../resource/part1/star_1.png",2317,979,150,0,-4500,1],
               ["star2","../resource/part1/star_2.png",256,128,500,-100,-6000,1],
               ["light","../resource/part1/light.png",1152,1024,200,50,-7500,1],
               ["part1_bg","../resource/part1/part1_bg.jpg",2048*1.5,1024*1.5,0,0,-18000,1]
           ]
           init();
           animate();
           var mesh;
           function createPlane(options){
           //      options={          
           //          width:0,
           //          height:0,
           //          pic:"",
           //          transparent:true,
           //          opacity:1
           //          blending:false
           //      }
               if(typeof options.pic=="string"){
                   var loader = new THREE.TextureLoader();
                   loader.setCrossOrigin( this.crossOrigin );
                   var texture = loader.load( options.pic, function() {}, undefined, function(){});
               }else{
                   var texture= new THREE.CanvasTexture( options.pic )
               }
               var MaterParam={
                       map:texture,
                       overdraw: true,
                       side: THREE.FrontSide,
       //              blending: THREE.AdditiveBlending,
                       transparent: options.transparent,
                       //needsUpdate:true,
                       //premultipliedAlpha: true,
                       opacity:options.opacity
                   }
               if(options.blending){
                   MaterParam.blending=THREE.AdditiveBlending
               }
               var plane = new THREE.Mesh(
                   new THREE.PlaneGeometry( options.width, options.height, 1, 1 ),
                   new THREE.MeshBasicMaterial(MaterParam  )
               );
               return plane;
           }
           function addPlanes(){

               planes.forEach(function(planeSet){

                   var scale=(1500-planeSet[6])*2*Math.tan(22.5*Math.PI/180)/1080;
                   var plane=createPlane({        
                       width:parseInt(planeSet[2]*scale),
                       height:parseInt(planeSet[3]*scale),
                       pic:planeSet[1],
                       transparent:true,
                       opacity:1//planeSet[7]
                   })
                   plane.position.set(parseInt(planeSet[4]*scale),parseInt(planeSet[5]*scale),planeSet[6]);

                   plane.name=planeSet[0];
                   //plane.visible=false;
                   scene.add(plane);
               })
           }
           function init() {

               container = document.getElementById("space")
               camera = new THREE.PerspectiveCamera( 45, window.innerWidth / window.innerHeight, 1, 500000 );
               camera.position.set(0, 0, 1500);

               scene = new THREE.Scene();

               var ambient = new THREE.AmbientLight( 0xffffff );
               scene.add( ambient );


               var directionalLight = new THREE.DirectionalLight( 0xffffff );
               directionalLight.position.set( -5, 5, 5).normalize();
               scene.add( directionalLight );

               addPlanes();
               renderer = new THREE.WebGLRenderer();
               renderer.setPixelRatio( window.devicePixelRatio );
               renderer.setSize( window.innerWidth, window.innerHeight );
               container.appendChild( renderer.domElement );

               document.addEventListener( 'mousemove', onDocumentMouseMove, false );
               window.addEventListener( 'resize', onWindowResize, false );
               window.addEventListener( 'mousewheel', onMouseWheel, false );
           }

           function onWindowResize() {

               windowHalfX = window.innerWidth / 2;
               windowHalfY = window.innerHeight / 2;

               camera.aspect = window.innerWidth / window.innerHeight;
               camera.updateProjectionMatrix();
               renderer.setSize( window.innerWidth, window.innerHeight );

           }
           function onMouseWheel(event){
               if(event.wheelDelta>0){
                   new TWEEN.Tween( camera.position )
                   .to( {z:camera.position.z+2500}, 1500 )
                   .start();
               }
               if(event.wheelDelta<0){
                   new TWEEN.Tween( camera.position )
                   .to( {z:camera.position.z-2500}, 1500 )
                   .start();
               }
           }
           function onDocumentMouseMove( event ) {

               mouseX = ( event.clientX - windowHalfX ) / 2;
               mouseY = ( event.clientY - windowHalfY ) / 2;

           }

           //

           function animate() {

               requestAnimationFrame( animate );
               render();
               TWEEN.update();
           }

           function render() {

               camera.position.x += ( mouseX - camera.position.x ) ;
               camera.position.y += ( mouseY - camera.position.y ) ;

               camera.lookAt( new THREE.Vector3(0,0,-20000) );

               renderer.render( scene, camera );

           }

   
</script>
   </body>
</html>



本文由 w3cmark_前端笔记 版权所有,转载时请注明出处。
注明出处格式:w3cmark (http://www.w3cmark.com/2016/threejs-mark-05.html)

分享到:

关注w3cmark
微信公众号 w3cmark_com