第2章 还记得点、线、面吗(二)

1、 学会画点、画线、画面的知识

2、 学会绘制复杂的图形

4、知识补充:坐标系

我们下面会学习使用直线画一个网格出来,为了更好的理解这个网格在空间中的位置,我们是时候,讲一讲空间坐标系了。

1、右手坐标系

Threejs使用的是右手坐标系,这源于opengl默认情况下,也是右手坐标系。下面是右手坐标系的图例,如果对这个概念不理解,可以百度一下,我保证你伸出手比划的那一瞬间你就明白了,如果不明白请给作者留言,我会尽快补上关于坐标系的知识。three.js坐标系

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

5、线条的深入理解

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

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

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

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

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

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

var geometry = new THREE.Geometry();

geometry.vertices.push(p1);

geometry.vertices.push(p2);

geometry.vertices的能够使用push方法,是因为geometry.vertices是一个数组。这样geometry 中就有了2个点了。

然后我们需要给线加一种材质,可以使用专为线准备的材质,THREE.LineBasicMaterial。

最终我们通过THREE.Line绘制了一条线,如下代码所示:

var line = new THREE.Line( geometry, material, THREE.LinePieces );

ok,line就是我们要的线条了。

6、画高中时深爱的坐标平面

我还深爱着高中时的那个坐标平面,它勾起了我关于前排同学的细细长发的回忆…

这个平面的效果如下所示:three.js画坐标系

它横竖分别绘制了20条线段,在摄像机的照射下,就形成了这般模样。你可以在[初级教程\chapter2\2-2.html]发现这些代码:

<!DOCTYPE html>
<html>
	<head>
		<meta charset="UTF-8">
		<title>Three框架</title>
		<script src="js/Three.js"></script>
		<style type="text/css">
			div#canvas-frame {
				border: none;
				cursor: pointer;
				width: 100%;
				height: 600px;
				background-color: #EEEEEE;
			}

		</style>
		<script>
            var renderer;
            function initThree() {
                width = document.getElementById('canvas-frame').clientWidth;
                height = document.getElementById('canvas-frame').clientHeight;
                renderer = new THREE.WebGLRenderer({
                    antialias : true
                });
                renderer.setSize(width, height);
                document.getElementById('canvas-frame').appendChild(renderer.domElement);
                renderer.setClearColor(0xFFFFFF, 1.0);
            }

            var camera;
            function initCamera() {
                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
                });
            }

            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 ) );

                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;
                    scene.add( line );

                }
            }

            function threeStart() {
                initThree();
                initCamera();
                initScene();
                initLight();
                initObject();
                renderer.clear();
                renderer.render(scene, camera);
            }

		</script>
	</head>

	<body onload="threeStart();">
		<div id="canvas-frame"></div>
	</body>
</html>


画网格关键之处在上面的A begin 和A end 之间,我们不浪费纸,但是浪费一些点,在下面重复一下上面的代码:

var cube;
function initObject() {
	var geometry = new THREE.Geometry();
	// B begin
	geometry.vertices.push( new THREE.Vector3( - 500, 0, 0 ) );
	geometry.vertices.push( new THREE.Vector3( 500, 0, 0 ) );
	// B end

	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;
		scene.add( line );

	}
}


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

1、定义2个点

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

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轴移动到不同的位置,也能形成一组平行线。

经过上面的步骤,就能够得到坐标网格了。代码如下:

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 );

}


好了,本节课讲完了,感觉自己写了好久好久。要兼顾深度和初学者,确实有些困难。最后,希望您喜欢。

[1楼] mayi** 2016-06-12 23:20

老师,我看不懂算法这部分(

这两个点决定了x轴上的一条线段,将这条线段复制20次,分别平行移动到z轴的不同位置,就能够形成一组平行的线段。

同理,将p1p2这条线先围绕y轴旋转90度,然后再复制20份,平行于z轴移动到不同的位置,也能形成一组平行线。

WebGL中文网老师回答:

不明白问题的意思。 注意相机的up方向,本课并不是y轴。

[2楼] dibi** 2016-06-23 13:55

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
});
老师,您好!

1.up & position & lookAt的区别是什么?相机默认位置面向Z轴,camera.up.z = 1之后发现面向X轴了,不太明白。

2.camera.lookAt({ x : 0, y : 0, z : 0 });这句的意思应该是重置当前位置为相机的初始位置,这里是将position设置为初始位置还是将up设置为初始位置?

3.请问,camera.lookAt({ x : 0, y : 0, z : 0 });重置之后,是否相机面向还是Z还是新设定的位置呢?

谢谢!

WebGL中文网老师回答:

首先要说的是up、position、lookat在初级视频课程中有详细的讲解,不明白可以看,是免费的。

再说position,不用说也知道是相机的位置。如果吧人头比作相机,那么就是人头的中心的位置

up是头顶的方向,大多数时候,是朝天空的。少部分时候,是朝床头的

lookat是眼睛,看的方向,或者说是眼睛的聚焦点,不要告诉我,你眼睛可以同时聚焦2个点。

最后要说明的是 up 和lookat这两个方向必须垂直,无论怎么设置,他们必须互相垂直。不然相机看到的结果无法预知。

上面的问题很误导人,position在英文中是位置的意思,所以不要怀疑up是位置

另外,相机没有朝向的说法,只有lookat,就是它看到的那一个聚焦点,就像眼睛看到的聚焦点一样。

[3楼] mr.t** 2016-06-24 15:17

camera.position.y 的值从1000到1208之间,值越大锯齿网格上线多出来的东西越少;

超过1208就没有多出的部分,我不确定是相机视角的问题还是其他的问题。

WebGL中文网老师回答:

这个图片放得越大,越不清晰一个道理,可以尝试设置WebGLRenderer的参数来抗锯齿。代码如下:

new THREE.WebGLRenderer({antialias:true})

[4楼] Mang** 2016-07-12 11:21

THREE.Line在google中出现警告,google测试提示,让更换成LineSegments,不过两种都可以显示

WebGL中文网老师回答:

新旧版本的three.js版本更更换,说明在未来的版本中,该类会被删除

[5楼] fion** 2016-07-19 11:03

老师你好,请问一下

line.rotation.y = 90 * Math.PI / 180; // 旋转90

这里表示围绕着y轴旋转90度,那么改为围绕着z轴旋转90度的话,

line.rotation.z = 90 * Math.PI / 180; // 旋转90

看到的效果不应该是20个相隔的点吗,为什么效果是两条中间有间隙的直线呢,不太懂这个旋转的方向。。。

___________    ___________

[6楼] gera** 2016-07-27 10:22

关于这个地方,我发表下自己的看法,看对不对:

console.log("正常的右手坐标系是x向右,y向上,z向屏幕外");
console.log("分析横线的画法可以得出当前坐标系:x向左,y向屏幕外,z向上");
console.log("分析竖线的画法可以得出:绕y轴旋转90°,方向是逆时针");

不知道是不是这样呢?

[7楼] sher** 2016-08-04 17:42

老师,我一直不明白这个坐标原点具体是在哪里?或者以照相机为参照,照相机是原点吗?

[8楼] sunr** 2016-08-04 21:29

初级免费教程的压缩包有密码?

[9楼] Medi** 2016-08-16 18:34

这个地方我看过教程之后发现,可能坐标的描述是有问题的,屏幕向右是x的正方向,垂直屏幕向外是y轴正方向,沿屏幕向上是z轴正方向,点的描述中点的坐标表示为THREE.Vector3(x,y,z),个人觉得基本是这样的。

[10楼] syla** 2016-08-18 11:49

其实我也跟9楼一样的问题,坐标系究竟是怎样的,为什么网格是z轴位移的?是不是跟1楼提问的关系有关?摄像机究竟怎样摆放

[11楼] bool** 2016-09-01 23:14

其实我觉得如下设置camera更容易理解(y向上,x向右坐标系):

      function initCamera() {

                camera = new THREE.PerspectiveCamera(45, width / height, 1, 10000);

                camera.position.x = 0;

                camera.position.y = 0;

                camera.position.z = 1000;

                camera.up.x = 0;

                camera.up.y = 1; 

                camera.up.z = 0;

                camera.lookAt({

                    x : 0,

                    y : 0,

                    z : 0

                });

            }

因为屏幕2D空间大多数程序都是这样设置的,相应修正initObject:

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 ) );
    for ( var i = 0; i <= 1; i ++ ) {

        var line = new THREE.Line( geometry, new THREE.LineBasicMaterial( { color: 0xffffff, opacity: 1.0 } ) );
        line.position.y = ( 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.z = 90 * Math.PI / 180;
        scene.add( line );
    }
}

不知道老师为什么喜欢用z向上的坐标系呢?

[12楼] bool** 2016-09-01 23:17

其实我觉得如下设置camera更容易理解(y向上,x向右坐标系):

function initCamera() {
    camera = new THREE.PerspectiveCamera(45, width / height, 1, 10000);
    camera.position.x = 0;
    camera.position.y = 0;
    camera.position.z = 1000;
    camera.up.x = 0;
    camera.up.y = 1; 
    camera.up.z = 0;
    camera.lookAt({
        x : 0,
        y : 0,
        z : 0
    });
}

因为屏幕2D空间大多数程序都是这样设置的,相应修正initObject:

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 ) );
    for ( var i = 0; i <= 1; i ++ ) {

        var line = new THREE.Line( geometry, new THREE.LineBasicMaterial( { color: 0xffffff, opacity: 1.0 } ) );
        line.position.y = ( 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.z = 90 * Math.PI / 180;
        scene.add( line );
    }
}

不知道老师为什么喜欢用z向上的坐标系呢?

[13楼] bool** 2016-09-01 23:19

这里评论后不能修改也不能删除吗?

不好意思,上面两个重复了,请帮删除第一个吧,谢谢

[14楼] cgh2** 2016-09-07 11:26

给5楼,你的想法是对的,你说的两条线其实是就是中间两个点的侧面,但因为相机离平面太近你只看到两条线在你左右。

[15楼] csgx** 2016-09-09 16:44

总的来说看这篇文章的价值不然看评论。

[16楼] xiao** 2016-09-21 14:12

尝试了简化 initObject() :

function initObject() {
    var material = new THREE.LineBasicMaterial({
        color: 'black'
    });

    var geometry = new THREE.Geometry();
    geometry.vertices.push(
        new THREE.Vector3(-500,0,0),
        new THREE.Vector3(500,0,0)
    );

    var createLine = function (x,z,isVertical) {
                    var line = new THREE.Line( geometry, material );
                    if (x) {
                        line.position.x = x;
                    }
                    if (z) {
                        line.position.z = z;
                    }
                    if (isVertical) {
                        line.rotation.y = 90 * Math.PI / 180;
                    }
                    scene.add( line );
    }
    for (var i = 1, length = 22, half = length / 2; i < length; i++) {
        createLine(0, (i - half) * 50);
        createLine((i - half) * 50, 0, true);
    }
}

[17楼] shuh** 2016-11-25 14:33

总感觉相机camera.up.z=1有些别扭——相当于头顶朝向屏幕外面往下看,但咱们开发者都是看向屏幕里面,低头往下看的话也是头顶朝向屏幕内,感觉z=-1更舒服些...

[18楼] woni** 2016-12-03 21:16

老师你好,我想问的是为什么横线只有17根,明明设置的是21根嘛。我调试过好多次,而且您这个网页上贴的图也是只有17根,我数过了的。

[19楼] woni** 2016-12-03 21:18

画出来的网格并不是正方形的,是长<高的长方形。为什么不是20*20个正方形呢?

[20楼] woni** 2016-12-03 21:32

额。。。18楼19楼的问题解决了:是因为camera.position.y的值设置小了,这好比就是相机的视野小了,所以上下分别有两根竖线在视野之外,相机看不到。因此只需要把camera.position.y设置大一点(1208以上)就可以了。

[21楼] gaog** 2017-01-04 19:00

老师我是初学者 ,我想知道怎么增加网格??

[22楼] haha** 2017-05-12 10:46

请问在已经生成线体之后能否改变线的两个端点的坐标呢,还是只能修改线本身的坐标位置

[23楼] heya** 2017-05-25 16:12

看来还是有很多人不明白坐标系,老师把up.z设为1是按照现实生活来的,把地面作为xy平面,那么高度就由z轴确定了,我理解了一下,你把显示器屏幕朝上举着,眼睛从显示器右边水平看过去就是这种情况了,具体可以看一下http://www.cnblogs.com/heyach/p/6902948.html,写的不好请各位大佬多多指教。

提问或评论

登陆后才可留言或提问哦:) 登陆 | 注册 登陆后请返回本课提问
用户名
密   码
验证码