做有态度的前端团队

网易FEG前端团队

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

二维几何体

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

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

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

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
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;

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

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
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的最大可视深度。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
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

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
<!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>

手机阅读请扫描下方二维码:

分享到:

    已有 54 条评论

    1. 1

      1 ali-40.gif ali-40.gif ali-41.gif ali-41.gif ali-42.gif ali-42.gif ali-43.gif ali-43.gif ali-44.gif ali-44.gif ali-45.gif ali-45.gif ali-46.gif ali-46.gif ali-47.gif ali-47.gif ali-48.gif ali-48.gif ali-49.gif ali-49.gif ali-50.gif ali-50.gif ali-51.gif ali-51.gif ali-52.gif ali-52.gif ali-53.gif ali-53.gif ali-54.gif ali-54.gif ali-55.gif ali-55.gif ali-56.gif ali-56.gif ali-57.gif ali-57.gif ali-58.gif ali-58.gif ali-59.gif ali-59.gif ali-60.gif ali-60.gif ali-61.gif ali-61.gif

    2. 1

      1 ali-40.gif ali-40.gif ali-41.gif ali-41.gif ali-42.gif ali-42.gif ali-43.gif ali-43.gif ali-44.gif ali-44.gif ali-45.gif ali-45.gif ali-46.gif ali-46.gif ali-47.gif ali-47.gif ali-48.gif ali-48.gif ali-49.gif ali-49.gif ali-50.gif ali-50.gif ali-51.gif ali-51.gif ali-52.gif ali-52.gif ali-53.gif ali-53.gif ali-54.gif ali-54.gif ali-55.gif ali-55.gif ali-56.gif ali-56.gif ali-57.gif ali-57.gif ali-58.gif ali-58.gif ali-59.gif ali-59.gif ali-60.gif ali-60.gif ali-61.gif ali-61.gif

    3. 1

      555

    4. 1

      1 ali-40.gif ali-40.gif ali-41.gif ali-41.gif ali-42.gif ali-42.gif ali-43.gif ali-43.gif ali-44.gif ali-44.gif ali-45.gif ali-45.gif ali-46.gif ali-46.gif ali-47.gif ali-47.gif ali-48.gif ali-48.gif ali-49.gif ali-49.gif ali-50.gif ali-50.gif ali-51.gif ali-51.gif ali-52.gif ali-52.gif ali-53.gif ali-53.gif ali-54.gif ali-54.gif ali-55.gif ali-55.gif ali-56.gif ali-56.gif ali-57.gif ali-57.gif ali-58.gif ali-58.gif ali-59.gif ali-59.gif ali-60.gif ali-60.gif ali-61.gif ali-61.gif

    5. 1

      1 ali-40.gif ali-40.gif ali-41.gif ali-41.gif ali-42.gif ali-42.gif ali-43.gif ali-43.gif ali-44.gif ali-44.gif ali-45.gif ali-45.gif ali-46.gif ali-46.gif ali-47.gif ali-47.gif ali-48.gif ali-48.gif ali-49.gif ali-49.gif ali-50.gif ali-50.gif ali-51.gif ali-51.gif ali-52.gif ali-52.gif ali-53.gif ali-53.gif ali-54.gif ali-54.gif ali-55.gif ali-55.gif ali-56.gif ali-56.gif ali-57.gif ali-57.gif ali-58.gif ali-58.gif ali-59.gif ali-59.gif ali-60.gif ali-60.gif ali-61.gif ali-61.gif

    6. 1

      1

    7. 1

      1

    8. 1

      1

    9. 1

      1

    10. 1

      1

    11. 1

      1

    12. 1

      1

    13. 1

      1

    14. 1

      1

    15. 1

      1

    16. 1

      1

    17. 1

      1

    18. 1

      1

    19. 1

      1

    20. 1

      1

    21. 1

      1

    22. 1

      1

    23. 1

      1

    24. 1

      1

    25. 1

      1

    26. 1

      1

    27. 1

      1

    28. 1

      1

    添加新评论

    ali-40.gifali-41.gifali-42.gifali-43.gifali-44.gifali-45.gifali-46.gifali-47.gifali-48.gifali-49.gifali-50.gifali-51.gifali-52.gifali-53.gifali-54.gifali-55.gifali-56.gifali-57.gifali-58.gifali-59.gifali-60.gifali-61.gif