介绍
three.js 之 Loader 几何体
# 灯光前言
添加灯光其实和添加其它3D对象一样简单,首先实例化一个灯光,然后通过scene.add
将其添加到场景中。
首先我们来准备一个基础场景(一个Sphere
球体、一个Box
立方体、一个Torus
圆环和一个Plane
平面作为地板)。
MeshStandardMaterial
标准网格材质。我们将材质的粗糙度roughness
设置为0.4,这样我们就能观察到光的反射。
由于我们使用了对光有反应的材质,所以在没有任何灯光的情况下,我们的3D世界将只有一片黑暗!
关于旋转
- 如果是
mesh
物体旋转, 那么只是物体的自转, 灯光是不进行旋转的, 会根据物体的自身的旋转显示对应的光照效果 - 如果是
scene
场景旋转, 那么场景中所有的物体和灯光都会进行同步的旋转效果, 不会根据物体自身的旋转显示对应的光照效果(因为光源也在同步转)
# 环境光AmbientLight
环境光将在场景内的所有几何图形上应用全向照明(四面八方的光)。它的第一个参数是颜色,第二个参数是光照强度。我们可以在实例化的时候直接设置属性,也可以在以后更改它们, 作为整个场景的明暗强弱, 通常环境光可以配合环境贴图, 实现一个反射的明暗效果
- 环境光没有阴影效果
const ambientLight = new THREE.AmbientLight(0xffffff, 0.5)
scene.add(ambientLight)
// Equals
const ambientLight = new THREE.AmbientLight()
ambientLight.color = new THREE.Color(0xffffff)
ambientLight.intensity = 0.5
scene.add(ambientLight)
就像我们在前面课程中所学到的,我们可以将灯光的属性添加到调试UI里,这样将极大的方便我们测试不同属性下灯光和材质的表现。
gui.add(ambientLight, 'intensity').min(0).max(1).step(0.001)
如果整个场景中,只有一个环境光,那么所有几何体的材质看上去都和BasicMaterial
一样,因为所有几何体上的面都将被平均点亮。在现实生活中,当一个物体被照亮时,物体背面不会完全是黑色的,因为光线会在墙壁和其他物体上反射到物体背面。出于性能原因,Three.js
不支持光反射,但是我们可以使用AmbientLight
来模拟这种光反射。
# 平行光/直线光DirectionalLight
平行光/直线光DirectionalLight
通常用来模拟太阳光,第一个参数是颜色,第二个参数是强度:必须设置光源位置
- 平行光/直线光的.shadow (opens new window) 阴影是通过OrthographicCamera (opens new window) 正交相机计算生成的
const directionalLight = new THREE.DirectionalLight(0x00fffc, 0.3)
// 设置光的位置
directionalLight.position.set(10, 10, 10)
scene.add(directionalLight)
默认情况下,光线由正上方向下照射。我们也可以使用位置position
属性来移动光源
directionalLight.position.set(1, 0.25, 0)
光源和物体的距离暂时无关,因为平行光只需要关心照射角度。
# 半球光HemisphereLight
半球光的效果类似于环境光,区别在于它可以设置天空和地面两种颜色,面向天空的面将被天空颜色照亮,而另一面将被地面色照亮。
第一个参数是天空颜色,第二个参数是地面色,第三个参数是光照强度:
const hemisphereLight = new THREE.HemisphereLight(0xff0000, 0x0000ff, 0.3)
scene.add(hemisphereLight)
# 点光源PointLight
点光源就像一个看不见的电灯泡,电灯泡对周围所有的物体雨露均沾,光线均匀传播。第一个参数是颜色,第二个参数是强度:必须设置光源位置
const pointLight = new THREE.PointLight(0xff9000, 0.5)
// 设置光的位置
pointLight.position.set(10, 10, 10)
scene.add(pointLight)
我们同样可以移动它的坐标来改变光源的位置
pointLight.position.set(1, - 0.5, 1)
默认情况下,光照的范围是无限大的。但是我们也可以设置光照的最大距离distance
和衰减decay
属性,设置他们就像是精细的控制电灯泡的瓦数。
const pointLight = new THREE.PointLight(0xff9000, 0.5, 10, 2)
# 矩形面光源(平面光源)RectAreaLight
矩形面光源就像是你家客厅里的方形吸顶灯。第一个参数是颜色,第二个参数是强度,第三和第四个参数是矩形的宽度和高度:
- 平面光源没有阴影
const rectAreaLight = new THREE.RectAreaLight(0x4e00ff, 2, 1, 1)
scene.add(rectAreaLight)
矩形面光源仅适用于 MeshStandardMaterial
和MeshPhysicalMaterial
。我们可以移动和旋转它:
rectAreaLight.position.set(- 1.5, 0, 1.5)
rectAreaLight.lookAt(new THREE.Vector3())
“
没有传递任何参数的Vector3其实x,y和z是0,也就是场景的中心。
”
# 聚光灯SpotLight
聚光灯就像手电筒,光线从一个点朝一个方向射出,形成光束。
- 聚光灯的.shadow (opens new window) 阴影是通过SpotLightShadow (opens new window) 透视相机PerspectiveCamera (opens new window) 计算生成的
创建时的参数列表:
color
: 光的颜色intensity
: 光照强度distance
: 光照最大距离angle
: 光束的大小penumbra
: 光束边缘的清晰度decay
: 光照的衰减速度
const spotLight = new THREE.SpotLight(0x78ff00, 0.5, 10, Math.PI * 0.1, 0.25, 1)
spotLight.position.set(0, 2, 3)
scene.add(spotLight)
当我们想旋转聚光灯的时候略有点麻烦,但聚光灯的实例有一个名为target
的属性,它也是一个Object3D
对象。聚光灯会总是看向那个target
所在的位置。
但是,当我们尝试直接更改target
的位置时,似乎聚光灯不会发生什么变化:
spotLight.target.position.x = - 0.75
这是因为我们的目标不在场景中,要使其工作,我们还需要将target
添加到场景中:
scene.add(spotLight.target)
# 设置聚光灯聚焦大小
- .angle (opens new window) 可以设置聚光灯 聚焦大小 数值越小 范围也就越大 最大范围应该不超过
Math.PI/2
。默认值为Math.PI/3
。
// 聚光灯
const SpotLight = new THREE.SpotLight(0xffffff, 0.5)
// 设置聚光灯的位置
SpotLight.position.set(5, 5, 5)
// 开启聚光灯阴影
SpotLight.castShadow = true
//TODO 设置聚光灯的角度
SpotLight.angle = Math.PI / 4
// 添加灯光到场景
this.scene.add(SpotLight)
# 设置聚光灯光线距离
- .distance (opens new window) 可以设置光线的距离 默认是
0
空值 空值就是没有衰减- 如果从非
0
开始 范围是:1~100
之间 值越大光线距离越长 - 光线距离越短 衰弱的效果就越强
- 如果从非
// 设置聚光灯光线距离
SpotLight.distance = 20
# 设置聚光灯半影的衰减
- .penumbra (opens new window) 可以设置聚光灯的半影衰减 默认是
0
范围是0~1
值越大半影衰减越明显 会显得聚焦感降低 更虚化看上去更真实
// 设置聚光灯半影的衰减
SpotLight.penumbra = 0.5
# 设置灯光的强度(亮度)
- 可以通过聚光灯的构造器生成强度 也可以通过
.intensity
二次设置灯光强度 它会覆盖构造器的灯光强度
// 聚光灯
const SpotLight = new THREE.SpotLight(0xffffff, 0.5) // 通过构造器设置聚光灯强度
// 设置灯光强度
SpotLight.intensity = 1 // 通过对.intensity 赋值修改灯光的强度 它会覆盖构造器的灯光强度
# 设置物理光照和衰减量
这里用聚光灯SpotLight (opens new window) 进行演示 物理衰减对性能的要求较大 灯光会根据距离的长短进行不同效果的展示 越远越暗 越近越亮
- .physicallyCorrectLights (opens new window) 可以开启WebGLRenderer (opens new window) 渲染器的物理光照效果, 需要在
renderer
中开启 - .decay (opens new window) 可以设置物理光照的衰减量 如果不开启.physicallyCorrectLights (opens new window) 物理光照将不会有效果
- decay 设置为等于
2
将实现现实世界的光衰减。缺省(默认)值为1
。
- decay 设置为等于
// 聚光灯
const SpotLight = new THREE.SpotLight(0xffffff, 0.6)
// 设置聚光灯的位置
SpotLight.position.set(5, 5, 5)
// 开启聚光灯阴影
SpotLight.castShadow = true
// TODO 设置物理光照的衰减量
SpotLight.decay = 0.2
// 添加灯光到场景
this.scene.add(SpotLight)
// 设置渲染器(画布)的大小 通过setSize()设置
this.renderer.setSize(window.innerWidth, window.innerHeight) // setSize(画布宽度, 画布高度)
// TODO 开启物理光照效果
this.renderer.physicallyCorrectLights = true
# 性能相关
3D世界有了灯光之后才更加真实。但是灯光也会带来更多的性能消耗。
所以我们应该尽可能少用灯,或者仔细思考是否可以用性能消耗更少的灯光来满足需求。
少量性能消耗的灯光:
AmbientLight
环境光HemisphereLight
半球光
中等性能消耗的灯光:
DirectionalLight
平行光PointLight
点光源
大量性能消耗的灯光:
SpotLight
聚光灯RectAreaLight
矩形面光源
# 灯光烘焙
还有一种技术,既可以保留灯光的效果,又几乎没有性能消耗,这就是灯光烘焙。这种技术可以将灯光对材质的表现固化到纹理贴图上。通常我们在3D建模软件中来完成烘焙。
“
注意,烘焙只能用于不会运动的光源和物体。
”
# 灯光辅助
灯光会对3D场景中产生影响,但其本身是不可见的,所以移动或是旋转时就很麻烦,但我们可以使用灯光辅助类来使得灯光对象变得可见
- HemisphereLightHelper (opens new window) 半球形光辅助
- DirectionalLightHelper (opens new window) 平行光辅助
- PointLightHelper 点光源辅助
- RectAreaLightHelper (opens new window) 自定义光源辅助
- SpotLightHelper (opens new window) 聚光灯辅助
实例化这些类,使用相应的光源的实例作为参数,并将它们添加到场景中。第二个参数用于改变灯光辅助对象的大小:
// 导入three.js
import * as THREE from 'three'
// 半球形辅助
const hemisphereLightHelper = new THREE.HemisphereLightHelper(hemisphereLight, 0.2)
scene.add(hemisphereLightHelper)
// 平行光辅助
const directionalLightHelper = new THREE.DirectionalLightHelper(directionalLight, 0.2)
scene.add(directionalLightHelper)'=
// 点光源辅助
const pointLightHelper = new THREE.PointLightHelper(pointLight, 0.2)
scene.add(pointLightHelper)
对于SpotLightHelper
而言,没有大小参数。另外,在移动target
后,我们需要调用update(...)
方法来更新它:
const spotLightHelper = new THREE.SpotLightHelper(spotLight)
scene.add(spotLightHelper)
window.requestAnimationFrame(() =>
{
spotLightHelper.update()
})
RectAreaLightHelper
没有内置在Three.js
中,像导入OrbitControls (opens new window)一样导入后才可以使用它
jsm
文件是three.js的ts扩展, 如果你使用的ts就必须通过jsm
文件导入
import { RectAreaLightHelper } from 'three/examples/jsm/helpers/RectAreaLightHelper'
const rectAreaLightHelper = new RectAreaLightHelper(rectAreaLight)
scene.add(rectAreaLightHelper)
# 开启灯光阴影
除了一些不支持阴影的光源(比如:
AmbientLight
环境光)以外 开启灯光阴影 当灯光打在物体上 可以实现阴影效果 阴影效果比较消耗性能 所以three.js默认是关闭阴影的 但是可以通过以下步骤打开阴影效果:阴影的基类LightShadow (opens new window) 可以设置阴影的一些属性 0. 材质和光源要满足对光照的反应 比如
AmbientLight
环境光 和MeshBasicMaterial
基础网格材质 就没有光照反应 所以设置阴影无效- 设置WebGLRenderer.shadowMap (opens new window)渲染器开启对阴影的计算
renderer.shadowMap.enabled = true
- 设置灯光投射阴影 (比如 平型光DirectionalLight.castShadow (opens new window))
.castShadow = true
- 设置
Object3D
物体投射阴影(需要阴影的物体).castShadow (opens new window)Object3D.castShadow = true
- 设置
Object3D
物体接收阴影(阴影投射的物体).receiveShadow (opens new window)Object3D.receiveShadow= true
- 设置WebGLRenderer.shadowMap (opens new window)渲染器开启对阴影的计算
// 声明一个球体并具备阴影效果
const sphere = new THREE.SphereGeometry(1, 20, 20)
// 声明一个标准材质
const mmaterial = new THREE.MeshStandardMaterial()
// 创建网格模型
const sphereMesh = new THREE.Mesh(sphere, mmaterial)
// TODO 开启物体投射阴影
sphereMesh.castShadow = true
// 添加到场景
scene.add(sphereMesh)
// 声明一个平面用来接收物体阴影
const plane = new THREE.PlaneGeometry(10, 10)
// 声明一个标准材质
const pmaterial = new THREE.MeshStandardMaterial()
// 创建网格模型
const planeMesh = new THREE.Mesh(plane, pmaterial)
// 定位平面
planeMesh.position.set(0, -1, 0)
// 旋转平面到底部
planeMesh.rotation.x = -Math.PI / 2
// TODO 开启物体接收阴影
planeMesh.receiveShadow = true
// 添加到场景
scene.add(planeMesh)
// 环境光
const light = new THREE.AmbientLight(0xffffff, 0.5) // soft white light
scene.add(light)
// 平行光
const directionalLight = new THREE.DirectionalLight(0xffffff, 0.5)
directionalLight.position.set(10, 10, 10)
// TODO 设置灯光投射阴影
directionalLight.castShadow = true
scene.add(directionalLight)
// 创建一个辅助线
const axesHelper = new THREE.AxesHelper(20)
scene.add(axesHelper)
// 4. 设置渲染器(画布)的大小 通过setSize()设置
renderer.setSize(window.innerWidth, window.innerHeight) // setSize(画布宽度, 画布高度)
// TODO 设置WebGLRenderer渲染器开启对阴影的计算
renderer.shadowMap.enabled = true
// 5. 将webgl渲染到指定的页面元素中去 (比如body 也可以设置其他页面Dom元素)
nameCanvas.appendChild(renderer.domElement)
- 开启阴影的效果
- 设置阴影的属性 可以通过
灯光变量.shadow.阴影属性
进行设置 详细可以看阴影基类LightShadow (opens new window)
# 设置阴影的模糊度
- .radius (opens new window)设置阴影的模糊度 可以降低性能消耗(?) 但是越高的模糊度 会显得阴影不够细致有重影 可以通过.mapSize (opens new window)设置阴影的质量
- 阴影模糊度默认值是
1
- 阴影模糊度默认值是
// 平行光
const directionalLight = new THREE.DirectionalLight(0xffffff, 0.5)
// 设置光源位置
directionalLight.position.set(10, 10, 10)
// 开启光照投射阴影
directionalLight.castShadow = true
// TODO 设置阴影的模糊度
directionalLight.shadow.radius = 10 // 默认值是1
// 添加到场景
scene.add(directionalLight)
- 模糊度对比效果
# 设置阴影的质量
- 阴影可以设置其质量 阴影质量越高 吃的性能就越多 通过光源对象进行设置
- .mapSize (opens new window)设置阴影的质量 阴影的质量必须是2的幂 默认是: (512,512)
- (512,512) 是低质量阴影 也是默认阴影的默认质量
- (1024,1024) 中等质量阴影
- (2048,2048) 高质量阴影
- (4096,4096) 超高质量阴影
// 平行光
const directionalLight = new THREE.DirectionalLight(0xffffff, 0.5)
// 设置光源位置
directionalLight.position.set(10, 10, 10)
// 开启光照投射阴影
directionalLight.castShadow = true
// TODO 设置阴影模糊度
directionalLight.shadow.mapSize.set(1024, 1024)
// 添加到场景
scene.add(directionalLight)
# 设置灯光物体跟踪
- .target (opens new window) 设置灯管对物体的跟踪 大部分的灯光都支持物体跟踪
- 设置灯光对应的物体(网格模型
mesh
) 这样物体移动的时候灯光也会跟着物体移动
- 设置灯光对应的物体(网格模型
// 声明一个球体
const sphere = new THREE.SphereGeometry(1, 20, 20)
// 声明一个标准材质
const mmaterial = new THREE.MeshStandardMaterial()
// 创建网格模型
const sphereMesh = new THREE.Mesh(sphere, mmaterial)
// 开启阴影
sphereMesh.castShadow = true
// 添加到场景
this.scene.add(sphereMesh)
// 设置聚光灯
const SpotLight = new THREE.SpotLight(0xffffff, 0.5)
// 设置聚光灯的位置
SpotLight.position.set(5, 5, 5)
// 开启聚光灯阴影
SpotLight.castShadow = true
// TODO 设置灯光对应的物体(网格模型mesh) 这样物体移动的时候灯光也会跟着物体移动
SpotLight.target = sphereMesh
// 添加灯光到场景
this.scene.add(SpotLight)
# 灯光添加物体中
- 创建后的灯光 如果直接添加到
mesh
场景中 那么是场景灯光 也可以把灯光添加到物体中 那么就是物体灯光 (比如创建一个球体模拟太阳)
// 创建场景
const scene = new THREE.Scene()
// 创建一个灯光小球
const lightBall = new THREE.Mesh(
// 创建小球
new THREE.SphereGeometry(0.1, 20, 20),
// 创建标准材质
new THREE.MeshBasicMaterial({ color: '#ff3040' })
)
// 物体添加到场景中
scene.add(lightBall)
// 创建点光源
const directionalLight = new THREE.PointLight('#ff3040', 1)
// 把点光源添加到小球中
lightBall.add(directionalLight)
- 效果图
- 那么随着灯光物体变化 物体的光照效果(影子)也会发生变化
# 二次修改灯光强度
.intensity (opens new window) 光照的强度属性, 可以查看当前灯光强度, 也可以进行光亮的修改
const directionalLight = new THREE.DirectionalLight(1, '#FFFFFF')
// 修改光亮强度
directionalLight.intensity = 0.4