three.js 坐标系、camera位置属性、点、线、面

Posted by 姚飞亮 on 2019-01-16

three.js 坐标系、camera位置属性、点、线、面

知识补充:坐标系

右手坐标系

图中右边那个手对应的坐标系,就是右手坐标系。在Threejs中,坐标和右边的坐标完全一样。x轴正方向向右,y轴正方向向上,z轴由屏幕从里向外。

线条的深入理解

在Threejs中,一条线由材质颜色组成。

Threejs中没有提供单独画点的函数,它必须被放到一个THREE.Geometry形状中,这个结构中包含一个数组vertices,这个vertices就是存放无数的点的数组。这个表示可以如下图所示:

为了绘制一条直线,首先我们需要定义两个点,如下代码所示:

1
2
var p1 = new THREE.Vector3( -100, 0, 100);
var p2 = new THREE.Vector3( 100, 0, -100);

请大家思考一下,这两个点在坐标系的什么位置,然后我们声明一个THREE.Geometry,并把点加进入,代码如下所示:

THREE.Geometry形状

1
2
3
var geometry = new THREE.Geometry();
geometry.vertices.push(p1);
geometry.vertices.push(p2);

THREE.LineBasicMaterial线材质

geometry.vertices的能够使用push方法,是因为geometry.vertices是一个数组。这样geometry 中就有了2个点了。
然后我们需要给线加一种材质,可以使用专为线准备的材质,THREE.LineBasicMaterial
最终我们通过THREE.Line绘制了一条线,如下代码所示:

1
2
var material = new THREE.LineBasicMaterial( { color: 0x0000ff } );
var line = new THREE.Line(geometry,material);

也可以写成

1
2
3
4
5
6
7
8
var geometry = new THREE.Geometry();
geometry.vertices.push(new THREE.Vector3(-100, 0, 100);
geometry.vertices.push(new THREE.Vector3(100, 0, -100);
var material = new THREE.LineBasicMaterial( { color: 0x0000ff } );
var color1 = new THREE.Color( 0x444444 ),
color2 = new THREE.Color( 0xFF0000 );
geometry.colors.push( color1, color2 );
var line = new THREE.Line(geometry,material);

画坐标平面


它横竖分别绘制了20条线段,在摄像机的照射下,就形成了这般模样。

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
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Three框架</title>
<style>
body { margin: 0; }
canvas { width: 100%; height: 100% }
</style>
<script src="/javascripts/three.min.js"></script>

</head>
<body>
<script>
var renderer;
function initThree() {
renderer = new THREE.WebGLRenderer({ antialias: true });
renderer.setSize( window.innerWidth, window.innerHeight );
renderer.setClearColor(0xFFFFFF, 1.0);
document.body.appendChild( renderer.domElement );
}
var camera;
function initCamera() {
camera = new THREE.PerspectiveCamera( 45, window.innerWidth / window.innerHeight, 1, 10000 );
camera.position.z = 0;
camera.position.x = 0;
camera.position.y = 1000;
camera.rotation.x = -89.5 ;
}
var scene;
function initScene() {
scene = new THREE.Scene();
}
var light;
function initLight() {
light = new THREE.DirectionalLight(0xFF0000, 1.0, 0);
light.position.set(100, 100, 200);
scene.add(light);
}
var cube;
function initObject() {
var geometry = new THREE.Geometry();
geometry.vertices.push( new THREE.Vector3( -500, 0, 0 ) );
geometry.vertices.push( new THREE.Vector3( 500, 0, 0 ) );
var line = new THREE.Line( geometry, new THREE.LineBasicMaterial( { color: 0x0000ff, opacity: 0.2 } ) );
scene.add( line );
for ( var i = 0; i <= 20; i ++ ) {
var line = new THREE.Line( geometry, new THREE.LineBasicMaterial( { color: 0x0000ff, opacity: 0.2 } ) );
line.position.z = ( i * 50 ) - 500;
scene.add( line );
var line = new THREE.Line( geometry, new THREE.LineBasicMaterial( { color: 0x0000ff, opacity: 0.2 } ) );
line.position.x = ( i * 50 ) - 500;
line.rotation.y = 90 * Math.PI / 180;
scene.add( line );
}
}
function threeStart() {
initThree();
initCamera();
initScene();
initLight();
initObject();
renderer.clear();
renderer.render(scene, camera);
}
threeStart();
</script>
</body>
</html>

画网格关键之处initObject函数中,我们不浪费纸,但是浪费一些电,在下面重复一下上面的代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
var cube;
function initObject() {
var geometry = new THREE.Geometry();
geometry.vertices.push( new THREE.Vector3( -500, 0, 0 ) );
geometry.vertices.push( new THREE.Vector3( 500, 0, 0 ) );
var line = new THREE.Line( geometry, new THREE.LineBasicMaterial( { color: 0x0000ff, opacity: 0.2 } ) );
scene.add( line );
for ( var i = 0; i <= 20; i ++ ) {
var line = new THREE.Line( geometry, new THREE.LineBasicMaterial( { color: 0x0000ff, opacity: 0.2 } ) );
line.position.z = ( i * 50 ) - 500;
scene.add( line );
var line = new THREE.Line( geometry, new THREE.LineBasicMaterial( { color: 0x0000ff, opacity: 0.2 } ) );
line.position.x = ( i * 50 ) - 500;
line.rotation.y = 90 * Math.PI / 180;
scene.add( line );
}
}

思路:我们要画一个网格的坐标,那么我们就应该找到线的点。把网格虚拟成正方形,在正方形边界上找到几个等分点,用这些点两两连接,就能够画出整个网格来。

1、定义2个点

在x轴上定义两个点p1(-500,0,0),p2(500,0,0)。

1
2
geometry.vertices.push( new THREE.Vector3( -500, 0, 0 ) );
geometry.vertices.push( new THREE.Vector3( 500, 0, 0 ) );

2、算法

这两个点决定了x轴上的一条线段,将这条线段复制20次,分别平行移动到z轴的不同位置,就能够形成一组平行的线段。
同理,将p1p2这条线先围绕y轴旋转90度,然后再复制20份,平行于z轴移动到不同的位置,也能形成一组平行线。
经过上面的步骤,就能够得到坐标网格了。代码如下:

1
2
3
4
5
6
7
8
9
for ( var i = 0; i <= 20; i ++ ) {
var line = new THREE.Line( geometry, new THREE.LineBasicMaterial( { color: 0x000000, opacity: 0.2 } ) );
line.position.z = ( i * 50 ) - 500;
scene.add( line );
var line = new THREE.Line( geometry, new THREE.LineBasicMaterial( { color: 0x000000, opacity: 0.2 } ) );
line.position.x = ( i * 50 ) - 500;
line.rotation.y = 90 * Math.PI / 180; // 旋转90度
scene.add( line );
}

相机camera位置属性设置up、position、lookat

看了中文文档很早之前的例子

1
2
3
4
5
6
7
8
9
10
11
12
camera = new THREE.PerspectiveCamera(45, width / height, 1, 10000);
camera.position.x = 0;
camera.position.y = 1000;
camera.position.z = 0;
camera.up.x = 0;
camera.up.y = 0;
camera.up.z = 1;
camera.lookAt({
x : 0,
y : 0,
z : 0
});

开始很懵逼,完全不能理解,有个position,还要up和lookAt干嘛。
自己换了个方式实现

1
2
3
4
5
camera = new THREE.PerspectiveCamera( 45, window.innerWidth / window.innerHeight, 1, 10000 );
camera.position.z = 0;
camera.position.x = 0;
camera.position.y = 1000;
camera.rotation.x = -89.5 ;

既然是位置属性不明白,还是要端正学习态度,把它搞懂。

先来第一个position属性,可以设置xyz。
假设设置y为 1000
position(0,1000,0);

单独设置xz轴都和以上类似,位置z或者x轴某个位置。
那lookAt,字面上的意思就是,看向哪里。
相机默认是由正z轴看像-z轴(相机镜头对着-z轴方向拍),就是我们由屏幕外向屏幕内看一样。

1
2
3
4
5
camera.lookAt({//相机看向哪个坐标
                    x : 0,
                    y : 0,
                    z : 0
                });

相机看向原点0,0,0。(相机位置position(500,500,1000))

相机的位置在x轴500上,y轴500,z轴1000。默认是看向-z轴,所以只能看到y轴500位置的线条了。
那up属性又是什么鬼,相机位置代码如下设置情况下:

1
2
3
4
5
6
7
8
9
10
11
12
camera = new THREE.PerspectiveCamera(45, width / height, 1, 10000);
                camera.position.x = 500;//相机的位置
                camera.position.y = 500;
                camera.position.z = 1000;
//                camera.up.x = 0;//相机以哪个方向为上方
//                camera.up.y = 1;
//                camera.up.z = 0;
                camera.lookAt({//相机看向哪个坐标
                    x : 0,
                    y : 0,
                    z : 0
                });

一切正常。
加个up属性试试:

1
2
3
4
5
6
7
8
9
10
11
12
camera = new THREE.PerspectiveCamera(45, width / height, 1, 10000);
                camera.position.x = 500;//相机的位置
                camera.position.y = 500;
                camera.position.z = 1000;
                camera.up.x = 0;//相机以哪个方向为上方
                camera.up.y = 0;
                camera.up.z = 1;
                camera.lookAt({//相机看向哪个坐标
                    x : 0,
                    y : 0,
                    z : 0
                });

代码设置z轴为1,表示以z轴为相机的上方。(默认y轴为上方)
就是躺着看,趴着看,侧着看的区别(我是这么认为的)
还不明白就是,手机正着拍,倒着拍,旋转拍~~~



Ω