lishengzxc / bblog

My Blog
https://github.com/lishengzxc/bblog/issues
178 stars 8 forks source link

Three.js 光源 #37

Open lishengzxc opened 7 years ago

lishengzxc commented 7 years ago

光源

各种光源

Three.js 提供了以下一系列光源,每种光源都有其特殊的行为和用途。

光源名称 描述
AmbientLight ['æmbɪənt] (环境光) 这是最基础的光源,它的颜色会添加到整个场景和所有对象的当前颜色上
PointLight (点光源) 空间中的一个点,朝所有的方向发射的光线
SpotLight (聚光灯光源) 这种光源有聚光效果,类似手电筒
DirectionalLight (平行光) 从这种光源发出的光线可以看作是平行的。例如:太阳光
HemisphereLight ['hemɪsfɪə](半球光) 用来创建更加自然的室外光线,模拟反光面和光线微弱的天空
RectAreaLight(面光源) 使用这种光源可以指定散发光线的平面,可不是空间中的一点
... ...

接下来我们具体看下各光源的用法和效果。

AmbientLight — 影响整个场景的光源

https://threejs.org/docs/index.html#api/lights/AmbientLight

var light = new THREE.AmbientLight( 0x404040 ); // soft white light
scene.add( light );

构造函数

AmbientLight( color, intensity )

这将创建一个具有给定颜色和强度的环境光。

AmbientLight 的光线没有特点的来源,因此它也不会影响阴影的生产,一般来说你不会只是用 AmbientLight,你应该讲它去其他光源一起使用,目的是弱化其他光源造成的阴影或添加一些颜色。

我们在下面这个 demo 中给 AmbientLight 设置了 #0C0C0C 的光源颜色,(这是该颜色的十六进制表示法,如果你对十六进制表示颜色还不是很熟悉,可以参考这个链接十六进制数字表示方法了解它),来弱化对象在地面上产生的生硬的阴影,你可以尝试调整右上角的 dat.GUI 工具调整 AmbientLight 的颜色来观察 AmbientLight 对场景内所有对象的影响。(demo 中的阴影是通过 SpotLight 所产生的

https://lishengzxc.github.io/learning-threejs/chapter-03/01-ambient-light.html

// 修改 AmbientLight 的光源颜色
light.color = new THREE.Color(...);

使用 THREE.Color()

在讲述下一种光源之前,我们要先快速了解下 THREE.Color() 方法。因为当构造 Three.js 中的对象时,通常情况下,我们可以直接使用十六进制字符串(#0c0c0c)或者十六进制数指定颜色。然而,在对象构造完成之后,如要需要更改颜色,你就不得不使用 THREE.Color() 。 https://threejs.org/docs/index.html#api/math/Color

PointLight — 向所有方向发射光的光源

https://threejs.org/docs/index.html#api/lights/PointLight

var light = new THREE.PointLight( 0xff0000, 1, 100 );
light.position.set( 50, 50, 50 );
scene.add( light );

构造函数

PointLight( color, intensity, distance, decay )

在场景中的特定位置创建一个光源。光线照射在各个方向上(好比一个灯泡)。

PointLight 是需要设定位置,在不同位置下的 PointLight 对其他对象都有着不同的影响,可以查看下面的 demo,除此以外,我们注意下 distance 属性,它的默认值是 0,表示光线的亮度不会随着距离的增加而递减,如果 distance 有具体值,那光线的亮度会在指定的距离处降至 0。我们可以在 demo 中将 intensity 设置比较高,然后设置 distance 为一个大于 0 的比较小的值比如 4。

https://lishengzxc.github.io/learning-threejs/chapter-03/02-point-light.html

其他属性

.power

光功率。 在“物理正确”模式中,表示以“流明(光通量单位)”为单位的光功率。 默认值 = 4PI。

.decay

沿着光照距离的衰退量。 在“物理正确”模式中,decay = 2 将实现现实世界的光衰减。 默认值 = 1。

.shadow

此属性存储用来渲染光照阴影的所有相关信息。

什么是“物理正确”模式:https://threejs.org/docs/index.html#api/renderers/WebGLRenderer.physicallyCorrectLights

SpotLight — 具有锥形效果的光源

https://threejs.org/docs/index.html#api/lights/SpotLight

SpotLight 是很常用的光源,也是我们目前提到的第一个常用的可以产生阴影的光源。

// 白色聚光灯从侧面发光,投射阴影

var spotLight = new THREE.SpotLight( 0xffffff );
spotLight.position.set( 100, 1000, 100 );

spotLight.castShadow = true;

spotLight.shadow.mapSize.width = 1024;
spotLight.shadow.mapSize.height = 1024;

spotLight.shadow.camera.near = 500;
spotLight.shadow.camera.far = 4000;
spotLight.shadow.camera.fov = 30;

scene.add( spotLight );

构造函数

SpotLight( color, intensity, distance, angle, penumbra, decay )

SpotLight 和 PointLight 的主要区别在于,我们需要为它设置 castShadow 属性为 true(如果我们需要阴影的话),以及它的 target 属性。

.target

聚光灯的焦点位于 target.position 处。

默认值 — (0,0,0)。

注意:目前为了target属性正常工作,它必须是 scene 的一部分,也就是通过如下方法添加到场景中:

.castShadow

如果设置为 true ,光照将投射动态阴影。

警告:该操作是很耗费计算资源的,并且需要精心调整才能正确工作。

默认值 — false.

在下面的这个 demo 中,我们试试设置 SpotLight 一些独有属性,如果把 target 设置为那个蓝色的小球(sphere 对象),那么这个光源就会一直瞄准这个球的中心,即使它在不断移动,当然我们也可以指定 SpotLight 瞄准空间中任意一点,此时我们需要使用 THREE.Object3D() 实例:

var target = new THREE.Object3D();
target.positon = new THREE.Vector3(5, 0, 0);

spotLight.target = target;

我们通过设置 distance 和 angle 设置光锥的形状,比如:

其中,光锥的宽度是通过 angle 和 distance 计算而得出的:

var coneLength = light.distance || 10000;
var coneWidth = coneLength * Math.tan(light / 2) * 2;

一般而言,我们不需要设置这些值,因为它们的默认值都是比较合适的,但是我们如果有需求,是可以方便的通过设置这些创建出一个比如光柱很细和光强递减很快的 SpotLight 。

在介绍下一个光源之前,我们再看看几个和阴影相关的熟悉(最新的 Three.js 已经将所有的阴影熟悉一直到了独立的 shadow 上)。要显示阴影,你需要设置 castShadow 为 true。我们可以通过 shadow.camera.near、shadow.camera.far、shadow.camera.fov 来微调阴影最后的展现形式,其工作原理和 PerspectiveCamera 一样,我们还可以设置 shadow.camera.visible 为 true 来调试它。

关于阴影,我们在开发过程中还有些是我们关心的,比如:

https://lishengzxc.github.io/learning-threejs/chapter-03/03-spot-light.html

DirectionalLight — 模拟远处类似太阳的光源

https://threejs.org/docs/index.html#api/lights/shadows/DirectionalLightShadow

太阳离我们很远,以至于它到达地球的时候几乎每一个光线互相都是平行的,DirectionalLight 和 SpotLight 的主要区别在于,被 DirectionalLight 照射的区域(距离相同的地方)接受到的光强是一样的。

和 SpotLight,你需要设置 position 和 target 来控制光源的方向,在设置 castShadow 为 true 后,来显示阴影。

// 从正上方照射过来的白色平行光,0.5的光强。

var directionalLight = new THREE.DirectionalLight( 0xffffff, 0.5 );
directionalLight.position.set( 0, 1, 0 );
scene.add( directionalLight );

因为 DirectionalLight 的平行光特性,所以没有了光锥,取而代之的是一个方块,在这个方块内的对象都可以产生和接受阴影。

HemisphereLight — 更加自然的光照效果

https://threejs.org/docs/#api/lights/HemisphereLight

如果我们需要模拟室外光照,我们可以使用一个 DirectionalLight 来模拟太阳,再添加一个 AmbientLight 来为场景提供基础色,但是这样看上去还是不怎么真实。其实当你在室外的时候,并不是所有的光照都来自上方;很多来自空气的散射,地面的反射,和各种其他物体的反射。HemisphereLight 就是用来处理这个情况的。

var light = new THREE.HemisphereLight( 0xffffbb, 0x080820, 1 );
scene.add( light );
构造函数

HemisphereLight( skyColor, groundColor, intensity )

下面 demo 展示了 HemisphereLight 效果,可以通过打开或者关闭 HemisphereLight,从而可以观察到对象在 HemisphereLight 会反射来自天空和地面的颜色。

https://lishengzxc.github.io/learning-threejs/chapter-03/05-hemisphere-light.html

RectAreaLight — 一个平面散发出的光线

https://threejs.org/docs/#api/lights/RectAreaLight

在过去,RectAreaLight 并不在 Three.js 的标准库中,而是一个拓展。现在已被 Three.js 直接支持。

var width = 2;
var height = 10;
var rectLight = new THREE.RectAreaLight( 0xffffff, undefined,  width, height );
rectLight.intensity = 70.0;
rectLight.position.set( 5, 5, 0 );
rectLight.rotation.set( -Math.PI / 2, 0, 0 );
scene.add( rectLight )

rectLightHelper = new THREE.RectAreaLightHelper( rectLight );
scene.add( rectLightHelper );
构造函数

RectAreaLight( color, intensity, width, height )

在下面这个 demo 中,我们设置了光源颜色为 #ff0000,光强为 3,和其他光源一样,需要为其设置 position,同时在设置你自己想要的 rotation 来控制光源的方向,从而在 castShadow 为 true 的情况下来影响阴影。第一次使用该光源,你可能感到奇怪,你会发现你放置光源的地方什么都看不到,这是因为你不能看到光源本身,只能看到它发出来的光(阴影或者它对其他对象的影响),所以你可以在光源的相同位置放置一个平面来“显示”出光源本身。

https://lishengzxc.github.io/learning-threejs/chapter-03/06-area-light.html

镜头炫光效果

最后来实现一个镜头炫光效果,例如,当你直接朝向太阳拍照时出现的镜头炫光,虽然在拍照的时候,我们可能要尽量避免出现这种情况情形,但是对于游戏来说,它提供了一种很好的效果,让场景看上去更加的真实。

Three.js 也支持镜头炫光。可以通过实例化 THREE。LensFlare 对象创建镜头炫光。THREE.LensFlare 对象接受如下参数:

让我们看看如何创建这个对象

var light = new THREE.PointLight( 0xffffff, 1.5, 2000 );

var textureLoader = new THREE.TextureLoader();

var textureFlare = textureLoader.load( "textures/lensflare/lensflare.png" );

var flareColor = new THREE.Color( 0xffffff );
flareColor.setHSL( h, s, l + 0.5 );

var lensFlare = new THREE.LensFlare( textureFlare, 700, 0.0, THREE.AdditiveBlending, flareColor );
lensFlare.position.copy( light.position );

scene.add( lensFlare );

创建完 THREE.LensFlare 对象后,可以将它放在光源处。同时为了更加真实的模拟炫光的效果,我们再增加几个“炫光”,不过我们不需要重新实例化,只需要调用刚刚炫光对象上的 add() 方法就行了。

lensFlare.add(textureFlare, 60, 0.6);
...

https://lishengzxc.github.io/learning-threejs/chapter-03/07-lensflares.html

总结

其实光源对对象产生的效果,还跟“材质”有着千丝万缕的联系,有些材质是完全不受光源影响的,比如 MeshBasicMaterial,它只会以指定的颜色渲染物体,我们会在下一篇章中,看看 Three.js 中的材质。

最后上面所有的 demo 都关联的 dat.GUI,因此通过它调整一些关键属性可以帮助你更好的理解上面提到的知识点。