在Three.js中使用光源
在第1章《使用Three.js创建你的第一个3D场景》中, 您了解了Three.js的基础知识,而在第2章《构成Three.js应用程序的基本组件》中, 我们深入研究了场景的最重要部分:几何体、网格和相机。 您可能注意到,在该章中,我们跳过了探讨光源的细节,尽管光源构成了每个Three.js场景的重要部分。 没有光源,我们将看不到任何渲染的内容(除非使用基本或线框材质)。 由于Three.js包含多种不同的光源,每种光源都具有特定的用途, 我们将利用这一章节来解释光源的各种细节,并为即将到来的关于材质使用的章节做好准备。 在本章结束时,您将了解可用光源之间的区别,并能够为您的场景选择和配置正确的光源。
WebGL本身并不原生支持照明。 如果没有Three.js,您将不得不编写特定的WebGL着色器程序来模拟这些类型的光,这是相当困难的。 关于在WebGL中从头开始模拟照明的良好介绍可以 在此找到
在本章中,我们将涵盖以下主题:
- Three.js中提供的不同类型的照明
- 使用基本光源
- 使用特殊光源
与所有章节一样,我们有很多示例供您用来尝试光的行为。
本章中显示的示例可以在提供的源代码的chapter-03文件夹中找到。
Three.js提供了哪些类型的照明?
在Three.js中有多种不同的光源,它们都具有特定的行为和用途。在本章中,我们将讨论以下一组光源:
THREE.AmbientLight:这是一种基本光源,其颜色添加到场景中对象的当前颜色。THREE.PointLight:这是空间中的单个点,从该点向所有方向发出光。此光可用于创建阴影。THREE.SpotLight:这种光源具有类似于台灯、天花板聚光灯或火炬的锥形效果。此光可投射阴影。THREE.DirectionalLight:也称为无限光。此光的光线可以被视为平行的,类似于太阳的光。此光也可用于创建阴影。THREE.HemisphereLight:这是一种特殊的光,可用于通过模拟反射表面和微弱照亮的天空来创建更自然的室外照明。此光也不提供任何与阴影相关的 功能。THREE.RectAreaLight:使用此光源,您可以指定从其发出光的区域,而不是空间中的单个点。THREE.RectAreaLight不投射任何阴影。THREE.LightProbe:这是一种特殊类型的光源,基于使用的环境贴图,创建一个动态环境光源来照亮场景。THREE.LensFlare:这不是光源,但使用THREE.LensFlare,您可以向场景中的光源添加镜头耀斑效果。
本章分为两个主要部分。
首先,我们将查看基本光源:
THREE.AmbientLight、
THREE.PointLight、
THREE.SpotLight和
THREE.DirectionalLight。
所有这些光都扩展了基本的THREE.Light对象,提供了共享功能。
这里提到的光是简单的光,需要很少的设置,可以用来重新创建大多数所需的照明场景。
在第二部分中,我们将查看一些特殊用途的光源和效果:
THREE.HemisphereLight、
THREE.RectAreaLight、
THREE.LightProbe和
THREE.LensFlare。
您可能只在非常特定的情况下需要这些光。
使用基本光源
我们将从最基本的光源开始:THREE.AmbientLight。
THREE.AmbientLight
当您创建THREE.AmbientLight时,颜色会全局应用。
此光没有特定的方向,THREE.AmbientLight不对任何阴影产生影响。
通常,您不会将THREE.AmbientLight用作场景中唯一的光源,
因为它以相同的方式将其颜色应用于场景中的所有对象,而不考虑网格的形状。
您将其与其他光源一起使用,例如
THREE.SpotLight或
THREE.DirectionalLight,
以软化阴影或向场景添加一些额外的颜色。
理解这一点最简单的方法是查看ambient-light.html示例,
该示例位于chapter-03文件夹中。
在此示例中,您将获得一个简单的用户界面,可用于修改此场景中可用的THREE.AmbientLight对象。
在以下截图中,您可以看到我们使用了一个简单的瀑布模型,
并使所使用的THREE.AmbientLight对象的颜色和强度属性可配置。
在第一个截图中,您可以看到当我们将光的颜色设置为红色时会发生什么:
如您所见,场景中的每个元素现在都添加了红色到其原始颜色。 如果我们将颜色更改为蓝色,我们将得到类似于这样的结果:
正如这个截图所示,蓝色应用于所有对象,并在整个场景上投下一道光。
在使用此光时应记住的是,您在指定颜色时应该非常保守。
如果指定的颜色太亮,您很快就会得到一幅完全过饱和的图像。
除了颜色,我们还可以设置光的强度属性。
此属性确定THREE.AmbientLight对场景中颜色的影响程度。
如果将其调低,光的颜色仅应用于场景中的对象的一小部分。
如果将其调高,场景会变得非常明亮:
既然我们已经看到了它的效果,让我们看看如何创建和使用THREE.AmbientLight。
以下代码行向您展示如何创建THREE.AmbientLight:
const color = new THREE.Color(0xffffff);
const light = new THREE.AmbientLight(color);
scene.add(light);
创建THREE.AmbientLight非常简单,只需几个步骤。
THREE.AmbientLight没有位置,全局应用,因此我们只需要指定颜色并将此光添加到场景中。
可选地,我们还可以在此构造函数中为此光的强度提供一个附加值。
由于我们在此未指定它,它使用默认强度为1。
请注意,在前面的代码片段中,
我们将THREE.AmbientLight的构造函数传递给了明确的THREE.Color对象。
我们也可以将颜色作为字符串传递 - 例如,"rgb(255, 0, 0)"或"hsl(0, 100%, 50%)" -
或作为数字,就像我们在前几章中所做的那样:0xff0000。
关于这一点的更多信息可以在使用THREE.Color对象一节中找到。
在我们讨论
THREE.PointLight、
THREE.SpotLight和
THREE.DirectionalLight之前,
首先让我们强调它们的主要区别 - 即它们如何发出光。
以下图表显示了这三种光源如何发出光:
您可以从此图表中看到以下内容:
THREE.PointLight从特定点向所有方向发出光THREE.SpotLight从特定点以锥形形状发出光THREE.DirectionalLight不从单一点发出光,而是从一个二维平面发出光,光线是彼此平行的,类似于太阳的光
我们将在接下来的几节中更详细地查看这些光源。让我们从THREE.SpotLight开始。
THREE.SpotLight
THREE.SpotLight是您经常会使用的光源之一(特别是如果您想使用阴影的话)。THREE.SpotLight是一种具有锥形效果的光源。您可以将其与手电筒或灯笼进行比较。此光源具有方向和产生光的角度。以下截图显示了THREE.SpotLight的外观(spotlight.html):
下表列出了您可以用来微调THREE.SpotLight的所有属性。首先,我们将查看与光源行为相关的属性:
| 名称 | 描述 |
|---|---|
Angle | 决定光线传播出光的宽度。宽度以弧度表示,默认为Math.PI/3。 |
castShadow | 如果设置为true,则应用该属性的光源将产生阴影。有关如何配置阴影,请参阅下表。 |
Color | 指示光源颜色。 |
decay | 指示光强度随着您远离光源的距离而减小的量。衰减为2会导致更逼真的光,而默认值为1。此属性仅在在WebGLRenderer上设置physicallyCorrectLights属性时有效。 |
distance | 当将此属性设置为非0值时,光的强度将从光的位置以线性方式减小到指定距离处的0。 |
intensity | 指示光的照射强度。属性的默认值为1。 |
penumbra | 指示在聚光灯的边缘处进行平滑(模糊)的角度百分比。它采用一个介于0和1之间的值,默认值为0。 |
power | 在以物理正确模式渲染时,指示光的功率(通过在WebGLRenderer上设置physicallyCorrectLights属性启用此模式)。此属性以流明为单位,其默认值为4*Math.PI。 |
position | 指示光在THREE.Scene中的位置。 |
target | 对于THREE.SpotLight,光线指向的方向很重要。通过target属性,您可以指定THREE.SpotLight要看向场景中的特定对象或位置。请注意,此属性需要一个THREE.Object3D对象(例如THREE.Mesh)。这与我们在第2章中看到的相机不同,相机在其lookAt函数中使用THREE.Vector3。 |
visible | 如果将此属性设置为true(默认值),则灯光是打开的,而如果将其设置为false,则灯光是关闭的。 |
启用THREE.SpotLight的阴影后,
您可以通过THREE.SpotLight的shadow属性来控制阴影的渲染。
shadow属性包括以下内容:
-
shadow.bias:将投射的阴影移动远离或靠近投射阴影的对象。 您可以使用此属性解决在处理非常薄的对象时出现的一些奇怪效果。 如果在模型上看到奇怪的阴影效果,通常可以通过将此属性设置为较小的值(例如0.01)来解决问题。 此属性的默认值为0。 -
shadow.camera.far:确定从光源创建阴影的距离。默认值为5000。 请注意,您还可以设置THREE.PerspectiveCamera提供的所有其他属性,我们在第2章中已经展示过。 -
shadow.camera.fov:确定用于创建阴影的视场的大小(请参阅第2章中的为不同场景使用不同摄像机部分)。 默认值为50。 -
shadow.camera.near:确定从光源创建阴影的距离。默认值为50。 -
shadow.mapSize.width和shadow.mapSize.height:确定用于创建阴影的像素数。 当阴影边缘有锯齿状边缘或看起来不平滑时,可以增加这些值。 这在场景渲染后无法更改。两者的默认值都为512。 -
shadow.radius:当此值设置为大于1时,阴影的边缘将变得模糊。 如果THREE.WebGLRenderer的shadowMap.type属性设置为THREE.BasicShadowMap, 则此选项不起作用。
创建THREE.SpotLight非常简单。只需指定颜色,设置所需的属性,并将其添加到场景中,
如下所示:
const spotLight = new THREE.SpotLight("#ffffff")
spotLight.penumbra = 0.4;
spotLight.position.set(10, 14, 5);
spotLight.castShadow = true;
spotLight.intensity = 1;
spotLight.shadow.camera.near = 10;
spotLight.shadow.camera.far = 25;
spotLight.shadow.mapSize.width = 2048;
spotLight.shadow.mapSize.height = 2048;
spotLight.shadow.bias = -0.01;
scene.add(spotLight.target);
在这里,我们创建了一个THREE.SpotLight实例并设置各种属性以配置光照。
我们还将castShadow属性明确设置为true,因为我们想要阴影。
我们还需要将THREE.SpotLight指向某个地方,我们使用target属性完成这个任务。
在使用此属性之前,我们首先需要将光源的默认目标添加到场景中,
如下所示:
scene.add(spotLight.target);
默认情况下,目标将设置为(0, 0, 0)。
在本节的示例中,您可以更改目标属性的位置,并查看光源跟随此对象的位置:
请注意,您还可以将光源的目标设置为场景中的对象。 在这种情况下,光线的方向将指向该对象。如果光照指向的对象移动,光线将继续指向该对象。
在本节开始时的表格中,我们展示了一些可用于控制THREE.SpotLight发出光线的属性。
distance和angle属性定义了光锥的形状。
angle属性定义了光锥的宽度,而distance属性则设置了光锥的长度。
以下图表解释了这两个值如何定义接收THREE.SpotLight光线的区域:
通常,您不需要设置这些值,因为它们带有合理的默认值,但是您可以使用这些属性,
例如,创建一个具有非常窄光束或迅速减小光强度的THREE.SpotLight实例。
您可以使用的最后一个属性来更改THREE.SpotLight产生光线的方式是penumbra属性。
使用此属性,您设置光锥边缘的强度在光锥边缘的位置下降到何种程度。
在下图中,您可以看到penumbra属性的效果。我们有一个非常明亮的光(高强度),
当它达到锥形边缘时,其强度迅速下降:
有时,仅通过查看渲染的场景,可能很难确定光源的正确设置。
您可能希望出于性能原因微调受照区域,或者尝试将光源移动到非常具体的位置。
通过使用THREE.SpotLightHelper, 可以实现这一点:
const spotLightHelper = new THREE.SpotLightHelper(spotLight);
scene.add(spotLightHelper);
// 在渲染循环中
spotLightHelper.update();
通过上面的代码,您会得到一个显示聚光灯细节的轮廓,有助于调试和正确定位和配置您的光源。
在转到下一个光源之前,我们将快速查看THREE.SpotLight对象的与阴影相关的属性。
您已经学到,通过将THREE.SpotLight实例的castShadow属性设置为true,可以获得阴影。
您还了解到THREE.Mesh对象有两个与阴影相关的属性。
您为应该投射阴影的对象设置castShadow属性,
并使用receiveShadow属性为应该显示阴影的对象设置。
Three.js还允许您对阴影的渲染进行非常精细的控制。
这是通过本节开始时我们解释的一些属性 完成的。
通过shadow.camera.near、shadow.camera.far和shadow.camera.fov,
您可以控制此光源在何处以及如何投射阴影。
对于THREE.SpotLight实例,您不能直接设置shadow.camera.fov属性。
此属性基于THREE.SpotLight的angle属性,
它与我们在第2章中解释的透视摄像机的视场的方式相同。
查看THREE.CameraHelper的最简单方法是查看它的工作方式;
您可以通过检查菜单中的shadow-helper复选框并调整相机设置来执行此操作。
如下图所示,选择此复选框将显示用于确定此光源阴影的区域:
在调试阴影问题时,添加THREE.CameraHelper是有用的。
要执行此操作,只需添加以下行:
const shadowCameraHelper = new THREE.CameraHelper(spotLight.shadow.camera);
scene.add(shadowCameraHelper);
// 在渲染循环中
shadowCameraHelper.update();
在此结束本节时,我将提供一些建议,以防您在阴影方面遇到问题。
如果阴影看起来有块状,请增加shadow.mapSize.width和shadow.mapSize.Height属性,
并确保用于计算阴影的区域紧密包围您的对象。
您可以使用
shadow.camera.near、
shadow.camera.far和
shadow.camera.fov属性来配置此区域。
请记住,您不仅必须告诉光源投射阴影,
还必须通过设置castShadow和receiveShadow属性告诉每个几何体是否将投射和/或接收阴影。
阴影偏移
如果在场景中使用薄对象,则在渲染阴影时可能会看到奇怪的伪影。您可以使用shadow.bias属性轻微偏移阴影,这通常可以解决这些问题。
如果要获得柔和的阴影,可以在THREE.WebGLRenderer上设置shadowMapType的不同值。
默认情况下,此属性设置为THREE.PCFShadowMap;
如果将此属性设置为PCFSoftShadowMap,则将获得柔和的阴影。
现在,让我们看一下列表中的下一个光源:THREE.PointLight。
THREE.PointLight
THREE.PointLight是一种从单个点向所有方向发出光的光源。
一个好的例子是射向夜空的信号箭或者篝火。
与所有光源一样,我们有一个具体的示例,您可以使用它来体验THREE.PointLight。
如果查看chapter-03文件夹中的point-light.html,
您可以找到一个示例,其中THREE.PointLight在我们用于其他光源的同一场景中使用:
如前一个截图所示,这个光源向所有方向发出光。 就像我们之前看到的聚光灯一样,这个光源也有一个辅助工具,您可以以相同的方式使用。 您可以将其视为场景中心的线框:
const pointLightHelper = new THREE.PointLightHelper(pointLight);
scene.add(pointLightHelper)
// 在渲染循环中
pointLightHelper.update();
THREE.PointLight与THREE.SpotLight共享多个属性,
您可以使用这些属性来配置此光源的行为:
| 属性 | 描述 |
|---|---|
| color | 此光源发射的光的颜色。 |
| distance | 指示光照的距离。默认值为0,意味着光照的强度不会基于距离而减小。 |
| intensity | 指示光照强度的属性。默认为1。 |
| position | 指示光在THREE.Scene中的位置。 |
| visible | 确定光源是关闭还是开启。如果将此属性设置为true(默认值),则该光源打开,如果设置为false,则关闭。 |
| decay | 指示光照强度随着远离光源的距离而减小的量。衰减为2导致更逼真的光, 默认值为1。 |
此属性仅在设置了physicallyCorrectLights属性的情况下对WebGLRenderer有效。 | |
| power | 在以物理正确模式渲染时,指的是光的功率(通过在WebGLRenderer上设置physicallyCorrectLights属性启用此模式)。 |
此属性以流明为单位测量,默认值为4*Math.PI。功率也直接与intensity属性相关(power = intensity * 4π)。 |
除了这些属性,THREE.PointLight对象的阴影可以以与THREE.SpotLight相同的方式进行配置。
在接下来的几个示例和截图中,我们将展示这些属性如何用于THREE.PointLight。
首先,让我们看看如何创建THREE.PointLight:
const pointLight = new THREE.PointLight();
scene.add(pointLight);
这里没有什么特别的 - 我们只是定义了光并将其添加到场景中;
当然,您也可以设置我们刚刚展示的任何属性。
THREE.PointLight对象的两个主要属性是distance和intensity。
使用distance,您可以指定在光减弱为0之前发出光的距离。
例如,在以下截图中,我们将distance属性设置为较低的值,并稍微增加了intensity属性,
以模拟树木之间的篝火:
在这个例子中,您无法设置power和decay属性;这些属性在您想模拟真实场景时非常有用。
可以在Three.js网站上找到一个很好的例子。
THREE.PointLight还使用相机来确定在何处绘制阴影,
因此您可以使用THREE.CameraHelper来显示相机覆盖的部分。
此外,THREE.PointLight提供了一个辅助工具,THREE.PointLightHelper,
用于显示THREE.PointLight发出光的位置。
启用这两者后,您将获得以下非常有用的调试信息:
如果您仔细观察前面的截图(图3.16),
您可能会注意到阴影是在阴影相机显示的区域之外创建的。
这是因为阴影助手只显示从点光源位置向下投射的阴影。
您可以将THREE.PointLight想象成一个立方体,
其中每一面都发射光并且可以投射阴影。
在这种情况下,THREE.ShadowCameraHelper仅显示向下投射的阴影。
我们将讨论的基本灯光中的最后一个是THREE.DirectionalLight。
THREE.DirectionalLight
这种类型的光源可以被视为非常遥远的光源。它发出的所有光线都是彼此平行的。
一个很好的例子是太阳。太阳离我们非常遥远,以至于我们在地球上接收到的光线(几乎)是平行的。
THREE.DirectionalLight与之前看到的THREE.SpotLight的主要区别在于,
与THREE.SpotLight不同,
这种光源在离源点越远时不会减弱(您可以使用distance和exponent参数来微调这一点)。
由THREE.DirectionalLight照亮的完整区域接收相同强度的光。
要在实际操作中看到这一点,请查看以下directional-light.html示例:
正如您所看到的,使用THREE.DirectionalLight很容易模拟日落等场景。
与THREE.SpotLight一样,您可以在此光源上设置一些属性。
例如,您可以设置光的intensity属性以及它产生阴影的方式。
THREE.DirectionalLight有很多与THREE.SpotLight相同的属性:
position、
target、
intensity、
castShadow、
shadow.camera.near、
shadow.camera.far