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

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

2、 学会绘制复杂的图形

1、因为精彩,所以值得

还记得2000年左右有一个很不错的OpenGL教程《Nehe OpenGL》,大学时,我才有机会读到那么好的教程。那时,废寝忘食,花了一个月的时间看完了整个教程。觉得不过瘾,又读了一遍师兄翻译的中文教程。在当时,Nehe教程实为经典,第一个原因是当时OpenGL的资料非常罕见,无论是新华书店,还是互联网上都很难找到一本详尽的OpenGL教程。第二个原因是NeHe教程的作者确实非常用心,写出来很精彩的实例,并浅显易懂的讲解了它。

我一直认为,我写《WebGL中文网》的目的是为3D爱好者们提供一套精彩的入门和精通资料,这套资料不仅包含基础知识,而且包含工程级别的实例,更重要的是,那能让大家理解图形学知识。长此以来,很多同学在学习了计算机图形学后,对图形学的知识还是不能应用。有很大的原因,就是缺少理论化的实践,所以,这点,我们也考虑到了,一定在讲解示例的过程中,将其中的图形学原理也给阐述清楚。

以使大家认真学习后,能够在较短的时间学到我所学习的知识。

关于图形学这些知识,我是用了很多时间去摸索的,在资料匮乏的时候去掌握一门新的知识,确实是很费时间和脑力的事情,但是现在,有了本教程,你可以直通光明顶,学到可以胜任WebGL工作的技能。我想,这也是《WebGL中文网》的宗旨吧。

2、不从画鸡蛋开始,我们从画点开始

睁开您的眼睛,看看你的周围,它是一个多么美妙的3D世界啊。3D世界由什么组成,除了上帝,还有谁能够回答呢?

是的,没有人能够回答这个问题。但是在计算机世界里,3D世界由什么组成就很好回答。

1、3D世界的组成

在计算机世界里,3D世界是由点组成,两个点能够组成一条直线,三个不在一条直线上的点就能够组成一个三角形面,无数三角形面就能够组成各种形状的物体,如下图:three.js网格模型

我们通常把这种网格模型叫做Mesh模型。给物体贴上皮肤,或者专业点就叫做纹理,那么这个物体就活灵活现了。最后无数的物体就组成了我们的3D世界。

那么3D世界的组成,是否真的这样简单?是的,从编程的角度,目前为此,你只需要知道这些。下一节,我们从点说起。

2、在Threejs中定义一个点

在三维空间中的某一个点可以用一个坐标点来表示。一个坐标点由x,y,z三个分量构成。在three.js中,点可以在右手坐标系中表示:

空间几何中,点可以用一个向量来表示,在Three.js中也是用一个向量来表示的,代码如下所示:

THREE.Vector3 = function ( x, y, z ) {

this.x = x || 0;
this.y = y || 0;
this.z = z || 0;

};


我们来分析这段代码:前面我们已经知道了THREE是Three.js引擎的一个全局变量。只要你想用它,就可以在任何地方用它。有点充气娃娃的意思,不需要你同意,你想用就用吧。

那么THREE.Vector3呢,就是表示Vector3是定义在THREE下面的一个类。以后要用Vector3,就必须要加THREE前缀。当然Three.js的设计者,也可以不加THREE这个前缀,但是他们预见到,Three.js引擎中会有很多类型,最好给这些类型加一个前缀,以免与开发者的代码产生冲突。

THREE.Vector3被赋值为一个函数。这个函数有3个参数,分别代表x坐标,y坐标和z坐标的分量。函数体内的代码将他们分别赋值给成员变量x,y,z。看看上面的代码,中间使用了一个“||”(或)运算符,就是当x=null或者undefine时,this.x的值应该取0。

3、点的操作

在3D世界中点可以用THREE.Vector3D来表示。对应源码为/src/math/Vector3.js(注意:源码所在的位置,可能不同版本不一样,请自己搜索Vector3关键词来确定)。在您继续学习之前,你可以打开该文件浏览一下,推荐使用WebStorm,如果没有,你也可以用NotePad++。

现在来看看怎么定义个点,假设有一个点x=4,y=8,z=9。你可以这样定义它:

var point1 = new THREE.Vecotr3(4,8,9);

另外你也可以使用set方法,代码如下:

var point1 = new THREE.Vector3();

point1.set(4,8,9);

我们这里使用了set方法,为了以后深入学习的方便,这里将Vector3的常用方法列出如下,为了不影响文章的连贯性,我们专门列出了一个网页来介绍它。

3、实例:画一条彩色线

初中数学中有一个定理:两个不重合的点能够决定一条直线。在three.js中,也可以通过定义两个点,来画一条直线。我们先看看,这一节,我们要完成实例的效果图:

例子说明:这是一条每个点不同颜色的线条

这个例子的代码如下所示,可以在“【初级教程\chapter2\2-1.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();
                var material = new THREE.LineBasicMaterial( { vertexColors: true } );
                var color1 = new THREE.Color( 0x444444 ), color2 = new THREE.Color( 0xFF0000 );

                // 线的材质可以由2点的颜色决定
                var p1 = new THREE.Vector3( -100, 0, 100 );
                var p2 = new THREE.Vector3(  100, 0, -100 );
                geometry.vertices.push(p1);
                geometry.vertices.push(p2);
                geometry.colors.push( color1, color2 );

                var line = new THREE.Line( geometry, material, THREE.LinePieces );
                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之间的代码,就是画线的代码。

1、首先,我们声明了一个几何体geometry,如下:

var geometry = new THREE.Geometry();

几何体里面有一个vertices变量,可以用来存放点。

2、定义一种线条的材质,使用THREE.LineBasicMaterial类型来定义,它接受一个集合作为参数,其原型如下:

LineBasicMaterial( parameters )

Parameters是一个定义材质外观的对象,它包含多个属性来定义材质,这些属性是:

Color:线条的颜色,用16进制来表示,默认的颜色是白色。

Linewidth:线条的宽度,默认时候1个单位宽度。

Linecap:线条两端的外观,默认是圆角端点,当线条较粗的时候才看得出效果,如果线条很细,那么你几乎看不出效果了。

Linejoin:两个线条的连接点处的外观,默认是“round”,表示圆角。

VertexColors:定义线条材质是否使用顶点颜色,这是一个boolean值。意思是,线条各部分的颜色会根据顶点的颜色来进行插值。(如果关于插值不是很明白,可以QQ问我,QQ在前言中你一定能够找到,嘿嘿,虽然没有明确写出)。

Fog:定义材质的颜色是否受全局雾效的影响。

好了,介绍完这些参数,你可以试一试了,在课后,我们会展示不同同学的杰出作品。下面,接着上面的讲,我们这里使用了顶点颜色vertexColors: THREE.VertexColors,就是线条的颜色会根据顶点来计算。

var material = new THREE.LineBasicMaterial( { vertexColors: THREE.VertexColors } );

3、接下来,定义两种颜色,分别表示线条两个端点的颜色,如下所示:

var color1 = new THREE.Color( 0x444444 ),

color2 = new THREE.Color( 0xFF0000 );

4、定义2个顶点的位置,并放到geometry中,代码如下:

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

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

geometry.vertices.push(p1);

geometry.vertices.push(p2);

5、为4中定义的2个顶点,设置不同的颜色,代码如下所示:

geometry.colors.push( color1, color2 );

geometry中colors表示顶点的颜色,必须材质中vertexColors等于THREE.VertexColors 时,颜色才有效,如果vertexColors等于THREE.NoColors时,颜色就没有效果了。那么就会去取材质中color的值,这个很重要,大家一定记住。

6、定义一条线

定义线条,使用THREE.Line类,代码如下所示:

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

第一个参数是几何体geometry,里面包含了2个顶点和顶点的颜色。第二个参数是线条的材质,或者是线条的属性,表示线条以哪种方式取色。第三个参数是一组点的连接方式,我们会在后面详细讲解。

然后,将这条线加入到场景中,代码如下:

scene.add(line);

这样,场景中就会出现刚才的那条线段了。

各位,先休息一下吧,可以在线编辑一下本课的代码,会有实时的效果显示在你的眼前。哈哈,这可是我们精心为您准备的在线编辑功能啊。本课未完,未完部分见下章

[1楼] mr.t** 2016-06-24 14:33

Linewidth参数怎么不行呢

WebGL中文网老师回答:

在3D世界中是没有单位的概念的,Linewidth设置为100,webgl也不知道应该在屏幕中占据多少像素。 WebGLRenderer是不支持线宽度的,所以如果要画线并设置宽度请用CanvasRenderer,它是用二维canvas画布模拟3D效果,所以能够设置宽度。

[2楼] ade1** 2016-06-26 22:48

介绍VertexColors:定义线条材质是否使用顶点颜色,这是一个boolean值。这个参数值,教程中代码中是true。但是在后边介绍的时候为什么出现了取值为THREE.VertexColors和THREE.NoColors的情况呢?

WebGL中文网老师回答:

最新版本的定义是:

NoColors: 0,

FaceColors: 1,

VertexColors: 2,

其实就是一个整形,请以相应的版本为准,不同版本的定义不一样。

[3楼] Puls** 2016-07-10 12:08

width\height 都没有提前声明,看的好纠结。。。

WebGL中文网老师回答:

js代码可以事先声明也可以不用声明,纠结的事情很多,请将心情放平,放到重要的事情上。

[4楼] muse** 2016-07-29 09:49

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

这几个参数是什么意思呢?

[5楼] huan** 2016-08-15 11:00

有些问题,学员之间也可以相互沟通解决,建议普通注册用户也能回复其他用户的问题。WebGL中文网老师对答的不好的问题可以补充回答,这样方便问题更快速的解决。

[6楼] ange** 2016-08-29 16:23

有点不理解,up是z轴,那么转换成二维,应该是右边X轴正方向,上方是Z轴正方向,画出来的应该是从左向右斜向下的线条啊

[7楼] ange** 2016-08-29 17:27

明白了,因为y的position是1000,所以是从Y轴正方向向负方向看的,还是谢谢

[8楼] drea** 2016-11-01 17:31

老师您好,我请问个问题,我现在版本是最新版83的,运行代码出现一下黄色警告:

three.js:23268 THREE.Line: parameter THREE.LinePieces no longer supported. Created THREE.LineSegments instead.

画面只有一条线段,请问如何修改呢

[9楼] Blac** 2016-11-02 18:03

@8楼


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

换成

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

应该是类库升级

[10楼] woni** 2016-12-03 20:15

大家好,6楼的问题我看了7楼的回答还没搞明白为什么不是从左上方指向右下方的线条 啊?camera.up.z=1是什么意思?y轴的正方是垂直于屏幕向内还是向外?

[11楼] woni** 2016-12-03 21:54

10楼的问题解决了,如果还有不懂的去百度 “three.js中camera的属性设置” 就了解了,不然的话后续的教程会越看越迷糊的。谢谢。

[12楼] ging** 2017-01-09 20:26

看看A begin 和A end之间的代码,就是画线的代码。——哪里有A begin和A end 啊?怎么后面的问题都没有老师回答了?

[13楼] fian** 2017-02-08 11:47

camera.position相机的位置

camera.up相机以哪个方向为上方

camera.lookAt相机看向哪个坐标

谢谢11楼的解答~

[14楼] wqiz** 2017-03-12 15:39

<!DOCTYPE html>
<html lang="en">

<head>
    <meta charset="UTF-8">
    <title>点线面</title>
</head>

<body>
    <div id="wq">
    </div>
    <script type="text/javascript" src="../../three.js-master/build/three.js"></script>
    <script type="text/javascript">
    var renderer;

    function initThree() {
        width = document.getElementById('wq').clientWidth;
        height = document.getElementById('wq').clientHeight;
        renderer = new THREE.WebGLRenderer({
                    antialias : true
                });

        /**
         * 设置渲染器的大小
         */
        renderer.setSize(400, 300);

        document.getElementById('wq').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();
        var material = new THREE.LineBasicMaterial({
            vertexColors: true
        });
        var color1 = new THREE.Color(0x444444),
            color2 = new THREE.Color(0xFF0000);

        // 线的材质可以由2点的颜色决定
        var p1 = new THREE.Vector3(-100, 0, 100);
        var p2 = new THREE.Vector3(100, 0, -100);
        geometry.vertices.push(p1);
        geometry.vertices.push(p2);
        geometry.colors.push(color1, color2);

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

    function threeStart() {
        initThree();
        initCamera();
        initScene();
        initLight();
        initObject();
        renderer.clear();
        renderer.render(scene, camera);
    }
    threeStart();
    </script>
</body>

</html>

为什么我这么写不显示,请老师帮忙看下,哪里出错了

[15楼] ohuf** 2017-03-23 10:03

课程代码里好多大小写弄错了,比如LineBasicMaterial( parameters) 的parameters参数,第一个字母都是小写,不应该是大写,例如Color应该是color,大小写写错的话是不会执行的。

[16楼] 梦梦wm** 2017-05-07 14:41

看见好想回 14 <body onload="threeStart()">  写上这个试试

[17楼] bigo** 2017-05-25 15:39

和14楼一样,也是出不来,没报错,只有一个LinePieces的警告,改成了segment,也出不来

[18楼] qaz8** 2017-06-09 15:17

为啥所有 应该有超链接的地方 都没有超链接。。。。这文章体验有问题啊

[19楼] yerl** 2017-07-04 11:58

@14楼

我觉得应该是没有给容器设置初始大小导致的,我给那个div设置高度之后就可以正常显示了

<div id="wq" style="height:300px;">

[20楼] yerl** 2017-07-04 12:01

@16楼

14楼他在</script>前已经执行了threeStart方法

[21楼] joel** 2017-08-02 14:13

@14楼

难道不是<script type = "text/javascript">< /script>的问题么,改成<script>< /script>后不报错,但是不显示,不显示的原因就是没有设置你的canvas高度...加了一个高度打开浏览器,这下可以了

<!DOCTYPE html>

<html lang="en">

<head>

    <meta charset="UTF-8">

    <title>点线面</title>

</head>

<style type="text/css">

#wq{

height: 600px;

width: 100%;

}

</style>

<body>

    <div id="wq">

    </div>

    <script type="text/javascript" src="js/three.min.js" ></script>

    <script>

    var renderer;

    function initThree() {

        width = document.getElementById('wq').clientWidth;

        height = document.getElementById('wq').clientHeight;

        renderer = new THREE.WebGLRenderer({

                    antialias : true

                });

        /**

         * 设置渲染器的大小

         */

        renderer.setSize(400, 300);

        document.getElementById('wq').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();

        var material = new THREE.LineBasicMaterial({

            vertexColors: true

        });

        var color1 = new THREE.Color(0x444444),

            color2 = new THREE.Color(0xFF0000);

        // 线的材质可以由2点的颜色决定

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

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

        geometry.vertices.push(p1);

        geometry.vertices.push(p2);

        geometry.colors.push(color1, color2);

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

        scene.add(line);

    }

    function threeStart() {

        initThree();

        initCamera();

        initScene();

        initLight();

        initObject();

        renderer.clear();

        renderer.render(scene, camera);

    }

    threeStart();

    </script>

</body>

</html>

[22楼] ment** 2017-08-16 22:51

"我们这里使用了set方法,为了以后深入学习的方便,这里将Vector3的常用方法列出如下,为了不影响文章的连贯性,我们专门列出了一个网页来介绍它。"  我怎么看不到这个网页链接呢?

[23楼] zch0** 2017-08-28 13:40

老师你好,var p1 = new THREE.Vector3( -100, 0, 100 );这个x,y,z的坐标轴到底是怎样的,不是和c3 3d动画的坐标轴一样的吗,z轴不是垂直于面向我们的平面吗,x轴不是向右越来越大,y轴向下越来越大吗;

[24楼] 飞飞22** 2017-10-17 16:00

您好,这个用最新的three.js就显示不出来线条,需要用以前的three.js才可以。是由于哪块更新了吗?

[25楼] knig** 2017-10-21 17:26

24楼的你的线条显示出来了吗?我的也显示不出来

[26楼] Wich** 2017-10-22 16:47

第二大节 的 第3小节  Vector 写错了

[27楼] cala** 2017-10-23 16:54

更新到最新版(我的是88dev)后无法显示,同楼上,哪里能看到最新版的更新内容

[28楼] echo** 2017-11-01 20:01

应该是camera.lookAt()的方法错了,如果是88dev的版本的话,应该写成:camera.lookAt(0, 0, 0)。官方的文档描述是这样的https://threejs.org/docs/#api/cameras/Camera

[29楼] echo** 2017-11-01 20:11

应该是camera.lookAt()的方法错了,如果是88dev的版本的话,应该写成:camera.lookAt(0, 0, 0)。官方的文档描述是这样的https://threejs.org/docs/#api/cameras/Camera

[30楼] gosa** 2017-11-14 17:36


难怪看不到人把能正确显示的code贴出来。。。原来会超出字数哇。。。

通过看各位大神的评论,可以显示出线的正确版本的修改点:

1.THREE.LinePieces 换成 THREE.LineSegments

2.camera.lookAt(0, 0, 0)的写法

[31楼] Barr** 2017-11-16 16:49

30楼正解

提问或评论

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