第5章 五彩的光源(一)

1、本章你将了解怎么使用光源

2、以及各种光源的区别,这些区别会通过实例对比来给大家讲清楚。这种对比方式应该是讲解最好的思路。

1 世界有了光,就不在黑暗

宇宙间的物体有的是发光的,有的是不发光的,我们把发光的物体叫做光源。太阳、电灯、燃烧着的蜡烛等都是光源。

在Threejs的世界里,有了光,就不会在黑暗。

2 Threejs中的各种光源

作为3D技术的发展趋势,浏览器端3D技术越来越被一些技术公司重视。由此,Threejs非常注重3D渲染效果的真实性,对渲染真实性来说,使用光源是比不可少的技巧。Threejs,在光源方面提供了多种光源供选择。

1、 光源基类

在Threejs中,光源用Light表示,它是所有光源的基类。它的构造函数是:

THREE.Light ( hex )

它有一个参数hex,接受一个16进制的颜色值。例如要定义一种红色的光源,我们可以这样来定义:

Var redLight = new THREE.Light(0xFF0000);

2、 由基类派生出来的其他种类光源

THREE.Light只是其他所有光源的基类,要让光源除了具有颜色的特性之外,我们需要其他光源。看看,下面的类图,是目前光源的继承结构。

可以看出,所有的具体光源都继承与THREE.Light类。下面我们来具体看一下,其他光源。

3 环境光

环境光是经过多次反射而来的光称为环境光,无法确定其最初的方向。环境光是一种无处不在的光。环境光源放出的光线被认为来自任何方向。因此,当你仅为场景指定环境光时,所有的物体无论法向量如何,都将表现为同样的明暗程度。 (这是因为,反射光可以从各个方向进入您的眼睛)

环境光用THREE.AmbientLight来表示,它的构造函数如下所示:

THREE.AmbientLight( hex )

它仍然接受一个16进制的颜色值,作为光源的颜色。环境光将照射场景中的所有物体,让物体显示出某种颜色。环境光的使用例子如下所示:

var light = new THREE.AmbientLight( 0xff0000 );

scene.add( light );

只需要将光源加入场景,场景就能够通过光源渲染出好的效果来了。

4 点光源

点光源:由这种光源放出的光线来自同一点,且方向辐射自四面八方。例如蜡烛放出的光,萤火虫放出的光。

点光源用PointLight来表示,它的构造函数如下所示:

PointLight( color, intensity, distance )

这个类的参数稍微复杂一些,我们花点时间来解释一下:

Color:光的颜色

Intensity:光的强度,默认是1.0,就是说是100%强度的灯光,

distance:光的距离,从光源所在的位置,经过distance这段距离之后,光的强度将从Intensity衰减为0。 默认情况下,这个值为0.0,表示光源强度不衰减。

5 聚光灯

聚光灯:这种光源的光线从一个锥体中射出,在被照射的物体上产生聚光的效果。使用这种光源需要指定光的射出方向以及锥体的顶角α。聚光灯示例如图所示:

聚光灯的构造函数是:

THREE.SpotLight( hex, intensity, distance, angle, exponent )

函数的参数如下所示:

Hex:聚光灯发出的颜色,如0xFFFFFF

Intensity:光源的强度,默认是1.0,如果为0.5,则强度是一半,意思是颜色会淡一些。和上面点光源一样。

Distance:光线的强度,从最大值衰减到0,需要的距离。 默认为0,表示光不衰减,如果非0,则表示从光源的位置到Distance的距离,光都在线性衰减。到离光源距离Distance时,光源强度为0.

Angle:聚光灯着色的角度,用弧度作为单位,这个角度是和光源的方向形成的角度。

exponent:光源模型中,衰减的一个参数,越大衰减约快。

6 材质与光源的关系

材质与光源有什么关系,这是一个容易傻傻分不清的问题。在没有深入讲解前,我们只能说它们是相互联系,相互依托的关系。

我们会在后面的章节专门来解释什么是材质,不过这里也需要简单的给你介绍一下。

1、 材质的真相

材质是啥子(四川话),材质就是物体的质地。我们可以用撤分文字的方法来理解。材质就是材料和质感的完美结合。

如果你还不理解,那么看看下面我引用的这段话:

在渲染程序中,它是表面各可视属性的结合,这些可视属性是指表面的色彩、纹理、光滑度、透明度、反射率、折射率、发光度等。正是有了这些属性,才能让我们识别三维中的模型是什么做成的,也正是有了这些属性,我们计算机三维的虚拟世界才会和真实世界一样缤纷多彩。 

这就是材质的真相吗?答案是否定的。不要奇怪,我们必须仔细分析产生不同材质的原因,才能让我们更好的把握质感。那么,材质的真相到底是什么呢?仍然是光,离开光材质是无法体现的。举例来说,借助夜晚微弱的天空光,我们往往很难分辨物体的材质,因为他们很多都表现出黑色,我们难以区分是铝合金,还是塑料的。而在正常的照明条件下,则很容易分辨。另外,在彩色光源的照射下,我们也很难分辨物体表面的颜色,在白色光源的照射下则很容易。这种情况表明了物体的材质与光的微妙关系。下面,我们将具体分析两者间的相互作用。

首先,我们来看一些例子。这些例子是一系类的,掌握一个,我们就印下了一个脚

印。

7 脚印一:不带任何光源的物体

我们首先在屏幕上画一个物体,不带任何的光源,定义物体的颜色为黑色,其值为0x000000,定义材质如下:

var material = new THREE.MeshLambertMaterial( { color:0x000000} ); // 这是兰伯特材质,材质中的一种

先看看最终的运行截图,如下所示:

由这幅图得出结论,当没有任何光源的时候,最终的颜色将是材质的颜色。但是这个结论目前来说,并没有依据。

代码如下,你可以在5-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 = 600;
                camera.position.y = 0;
                camera.position.z = 600;
                camera.up.x = 0;
                camera.up.y = 1;
                camera.up.z = 0;
                camera.lookAt({
                    x : 0,
                    y : 0,
                    z : 0
                });
            }

            var scene;
            function initScene() {
                scene = new THREE.Scene();
            }

            var light;
            function initLight() {
            }

            var cube;
            function initObject() {
                var geometry = new THREE.CubeGeometry( 200, 100, 50,4,4);
                var material = new THREE.MeshLambertMaterial( { color:0xFFFFFF} );
                var mesh = new THREE.Mesh( geometry,material);
                mesh.position = new THREE.Vector3(0,0,0);
                scene.add(mesh);
            }

            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>

现在我们来解析一下:

1、 在A处,关于灯光的代码,什么也没有做。也就是Threejs中没有添加任何灯光。

2、 在B处,我们使用了兰伯特材质,并将这种材质赋予了黑色,所以,你才会发现最后的效果是黑色。如果,我们把材质颜色设置为红色,那么物体是不是就会显示红色呢?

答案是否定的,这是因为,在场景中没有任何光源的情况下,物体不能反射光源到人的眼里,所以物体应该是黑色的。这与物体的材质颜色几乎没有关系。打个比方,在月高风黑夜,伸手不见五指的夜晚,一群穿着彩衣的美女在你面前跳舞,你能分辨出他们是穿的彩色衣服吗?不能。

结论:当没有任何光源的时候,最终的颜色将是黑色,无论材质是什么颜色。

8 脚印二:兰伯特材质与光源

最常见的材质之一就是Lambert材质,这是在灰暗的或不光滑的表面产生均匀散射而形成的材质类型。比如一张纸就是Lambert表面。 首先它粗糙不均匀,不会产生镜面效果。我们在阅读书籍的时候,没有发现书上一处亮,一处不亮吧,它非常均匀,这就是兰伯特材质。

有的朋友觉得纸不粗糙啊,你怎么说它粗糙吗?人的肉眼是不好分辨出来,它粗不粗糙的。

Lambert材质表面会在所有方向上均匀地散射灯光,这就会使颜色看上去比较均匀。想想一张纸,无论什么颜色,是不是纸的各个部分颜色都比较均匀呢。

Lambert材质的图例如下所示:

Lambert材质会受环境光的影响,呈现环境光的颜色,与材质本身颜色关系不大。

我们现在来做一个例子

例子:红色环境光照射下的长方体,它用的是淡红色(0x880000)的兰伯特材质。效果如下图:

我们来看看代码,你可以在5-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 = 600;
                camera.position.y = 0;
                camera.position.z = 600;
                camera.up.x = 0;
                camera.up.y = 1;
                camera.up.z = 0;
                camera.lookAt({
                    x : 0,
                    y : 0,
                    z : 0
                });
            }

            var scene;
            function initScene() {
                scene = new THREE.Scene();
            }

            var light;
            function initLight() {
            // A start
                light = new THREE.AmbientLight(0xFF0000);
                light.position.set(100, 100, 200);
                scene.add(light);
            // A end

            }

            var cube;
            function initObject() {
                var geometry = new THREE.CubeGeometry( 200, 100, 50,4,4);
                // B start
                var material = new THREE.MeshLambertMaterial( { color:0x880000} );
                // B end
                var mesh = new THREE.Mesh( geometry,material);
                mesh.position = new THREE.Vector3(0,0,0);
                scene.add(mesh);
            }

            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>


好了,我们来分析一下这段代码。

1、 在A处,我们设置了一个红色的环境光,并把它放在了一个位置上。

2、 在B处,我们使用了淡红色的兰伯特材质。

最后整个效果中,长方体呈现的是红色。我们要说的是,长方体显示红色,是因为长方体反射了红色的光,长方体本身的颜色是0x880000,光源的颜色是0xFF0000,红色的光照在物体上,物体反射了红色的光,所以呈现红色。

我们现在一直在使用环境光,从环境光的构造函数来看,它只有颜色,其位置对场景中的物体并没有影响,因为他是均匀的反射到物体的表面的。

9 脚印三:环境光对物体的影响

环境光就是在场景中无处不在的光,它对物体的影响是均匀的,也就是无论你从物体的那个角度观察,物体的颜色都是一样的,这就是伟大的环境光。

你可以把环境光放在任何一个位置,它的光线是不会衰减的,是永恒的某个强度的一种光源。

给WebGL中文网团队的女程序员"小果妹妹"发一个鸡腿吧,微信扫一扫赞赏,感谢。

亲爱的读者,如果你觉得WebGL中文网的课程不错,您可以购买《WebGL中文网视频课程》 课程支持我们哦,购买后记得给我们好评哦!我们强烈建议您不要在iphone上的网易云课堂软件中购买,这样苹果会收取31%左右的服务费,虽然这是明码标价,我们也表示认可和理解,具体选择权在您自己了。

感谢大家的支持,下面是课程的截图之一

[1楼] hong** 2016-07-22 11:17

我这儿显示的是材质本事的颜色,threejs版本号是79.

[2楼] zhao** 2016-09-20 11:49

我的也是材质本身的颜色,版本是73。也就是说材质影响了环境光。如果材质是白色的,那显示的是环境光映射的颜色。我打开5-2的例子也是材质本身的颜色。这是为什么?求解

[3楼] Crai** 2016-10-13 15:55

应该是颜色也有个强度的吧。黑色最强(000000),白色最弱(FFFFFF),如果环境光比材质的颜色还强的话就显示环境光的颜色,如果是材质的颜色比较强的话那么就显示的材质的光。

[4楼] Crai** 2016-10-13 16:03

好像也不是简单的非此即彼的颜色,尝试了好几个颜色之后发现并不是要么是材质的颜色要么是环境光的颜色,还有别的情况。没有摸索出来规律,但是感觉像是带了环境光颜色的眼镜看有色的物体。

[5楼] 王红12** 2016-10-27 14:48

<script src="js/three.js"></script>老师,请问这个要怎么引用啊,我每次在gith那里面下载都是失败,我还可以怎么办啊,初学者,不太懂,学习

[6楼] 王红12** 2016-10-27 15:02

<script src="js/three.js"></script>老师,请问这个要怎么引用啊,我每次在gith那里面下载都是失败,我还可以怎么办啊,初学者,不太懂,学习

[7楼] LML1** 2016-11-04 21:36

老师你好,我想问一下:当光源是环境光时,light.position.set(100, 100, 200);是什么意思呀?另外,我把这行代码删去之后发现没什么影响呀?

[8楼] anjo** 2016-11-29 18:57

材质颜色和环境光颜色应该是叠加的吧~老师

[9楼] woni** 2016-12-04 16:45

显示物体的本色好像是因为0x880000是深红色,并不是教程说的淡红色。淡红色应该是0xFC9D9A,你们换一下就对了。教程没错的。

[10楼] 罗小米** 2017-02-09 11:57

RGB光的反射原理: 减色法是物体表面反射光线的原理, 减色法能让我们看见周围物体的色彩,譬如:一个绿球,在白光中出现绿色是因为此球吸收红、蓝波长,而反射出绿色。当然,若光源中只发出红、蓝光(或是品红光),此球将出现黑色,因为绿球上没有绿波长可反射出来。

[11楼] laoq** 2017-03-28 00:13

物体在环境光影响下,最终表现出来的颜色的向量值,应该是环境光颜色向量和物体本身颜色向量的向量积。

[12楼] laoq** 2017-03-28 00:15

<img src="null" onerror="alert(1)"/>

[13楼] laoq** 2017-03-28 00:17


Image

[14楼] laoq** 2017-03-28 00:19

<p><br><img alt="Image" class="selected" src="null"  onerror="alert(1)" width="184" height="166" data-image-size="184,166"><br></p>

[15楼] laoq** 2017-03-28 00:20

Image

[16楼] laoq** 2017-03-28 00:21

<p><img alt="Image" src="null" width="184" height="166"><br></p>

[17楼] laoq** 2017-03-28 00:22

<p><img alt="Image" src="http://p1.music.126.net/ppPVW5zVYeIMd3ppIxbU9w==/18864320997778054.jpg?param=180y180" width="184" height="166"><br></p>

[18楼] yerl** 2017-07-05 10:34

楼上这位兄弟太顽强了,哈哈

[19楼] wqel** 2017-11-08 16:54

白色的光,照射在红色的物体上,所以是红色的。请修改一下。

WebGL中文网老师回答:

是的。

[20楼] gosa** 2017-11-24 16:01

尝试了一下,在光源(0xFF0000)不变的前提下,

当本体颜色设置为黑色(0x000000)时,结果是黑色,

当本体颜色设置为白色(0xFFFFFF)时,结果是红色。

所以3楼的可能有道理?

[21楼] zzuu** 2017-12-05 19:20

我试了一下,物体是白色的情况下,颜色是随着环境光改变。物体自带颜色的话,就不一定和环境光一样了,应该是共同作用的结果。

[22楼] zzuu** 2017-12-05 19:20

我试了一下,物体是白色的情况下,颜色是随着环境光改变。物体自带颜色的话,就不一定和环境光一样了,应该是共同作用的结果。

[23楼] zhao** 2018-03-15 17:02

我想知道代码里面的A注释在哪

[24楼] seve** 2018-06-20 14:57

10楼的理解是正确的,原理是减色法

[25楼] zhan** 2018-09-03 18:53

看了关于光源和材质颜色的评论,实在忍不住,注册了个号来回复一下

首先感谢10楼同学的正解,不明白的同学请看看我下面的简要分析:(RGB色拆分)

我理解的材质颜色:材质在白色光中呈现的颜色

我理解的材质颜色原理:白色光源经过材质过滤后,不能被吸收掉的颜色(可拆分为RGB色)

再举例说明一下:

例1:如果光源为000000,那么不管什么材质,颜色都是000000,因为没有光(RGB)进入材质,也就不存在光被过滤的情况,所以看到是黑色

例2:如果光源为红色FF0000,材质为888888,光源经过材质过滤,因为R色FF比88强,而材质最强只能反射88的R光,多余的R光会被材质吸收掉,而光源中G和B根本就没,所以材质不会存在G和B光,即呈现880000

例3:如果光源为888888,材质为FF0000,因光源R部分比材质弱,即使材质吸收G光的能力为0,所以反射的R光为88,同理GB被材质吸收,反射00,最后呈现880000

(结论:呈现颜色=材质颜色+光源颜色的并集(即取小),即FF0000 U 888888 = 880000)

RGB色取拆分后,再取各自的:并集、并集、并集(重要的事情说三遍)

[26楼] mudc** 2019-04-30 10:20

感谢25楼朋友的讲解。

对于此部分内容我进行了测试,发现运行后的结果都是显示环境光的颜色,而与材质无关,不知什么原因?

版本为52。

[27楼] shan** 2020-02-07 00:14

感谢10楼和25楼的朋友的讲解。

我这里对25楼的说法进行了测试,也回答26楼的问题。

首先,光源和材质之间是有相互作用的,这个是确定的。

-------------------------------以下例子证明10楼和25楼的观点大方向正确-----------------------

两种色彩丰富的光源和材质,混合在一起,看到的是两种颜色一起作用的结果。

例如:分别定义光源

light = new THREE.AmbientLight(0xFFCCFF);

和材质的颜色

var material = new THREE.MeshLambertMaterial({
    //定义材质颜色    
    color: 0xFFB6C1});

则显示的既不是光源的颜色,也不是材质的颜色,而是另外一个颜色。


-------------------------------以下个人看法纠正一下25楼的细节,并且回答26楼的问题-----------------------

但是做测试之后,我也用25楼的例子进行了测试,并非是简单的And交集∩,更加不是Or并集∪,

而是一个更加复杂的颜色。

所以说结论有两个:

1. 环境光和材质颜色一起作用,这个结果就是呈现出来的结果。

2. 呈现出来的颜色结果,不是环境光和材质颜色的交集,但是怎么计算出来的,还没有测试出来。

[28楼] shan** 2020-02-07 00:15

感谢10楼和25楼的朋友的讲解。

我这里对25楼的说法进行了测试,也回答26楼的问题。

首先,光源和材质之间是有相互作用的,这个是确定的。

-------------------------------以下例子证明10楼和25楼的观点大方向正确-----------------------

两种色彩丰富的光源和材质,混合在一起,看到的是两种颜色一起作用的结果。

例如:分别定义光源

light = new THREE.AmbientLight(0xFFCCFF);

和材质的颜色

var material = new THREE.MeshLambertMaterial({
    //定义材质颜色    
    color: 0xFFB6C1});

则显示的既不是光源的颜色,也不是材质的颜色,而是另外一个颜色。


-------------------------------以下个人看法纠正一下25楼的细节,并且回答26楼的问题-----------------------

但是做测试之后,我也用25楼的例子进行了测试,并非是简单的And交集∩,更加不是Or并集∪,

而是一个更加复杂的颜色。

所以说结论有两个:

1. 环境光和材质颜色一起作用,这个结果就是呈现出来的结果。

2. 呈现出来的颜色结果,不是环境光和材质颜色的交集,但是怎么计算出来的,还没有测试出来。

提问或评论

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