1. 发光的太阳
1.1 各种光照的含义
首先,我们列举出不同种类的光照及其影响。好选择对应种类的光照应用。
| 种类 | 特点 | 用途 |
|---|---|---|
| AmbientLight 环境光 | 为场景中所有物体提供均匀的基础光照,没有方向性 | 用于补充其他光源,创造更自然的整体光照效果 |
| DirectionalLight平行光 | 模拟无限远处的光源,如太阳光,具有方向性 | 适用于户外场景和室内大型场景的主光源,可以产生清晰的阴影 |
| PointLight点光源 | 模拟一个点光源,从该点向四面八方发射光线 | 适用于室内场景的局部照明,可以产生柔和的阴影 |
| SpotLight聚光灯 | 模拟手电筒等聚光灯,有特定的光照范围和角度 | 适用于模拟头灯、台灯等聚光效果,可以产生清晰的聚焦阴影 |
| HemisphereLight半球光 | 模拟天空和地面两个半球面的不同颜色光源 | 适用于营造自然的环境光,如户外场景的天空和地面的光照差异 |
| RectLight区域光 | 模拟一个矩形区域的光源,光照更加自然和柔和 | 适用于模拟室内的窗户、灯箱等矩形光源 |
1.2 添加点光源-PointLight
上一篇文章中,给太阳以及其他星球增加了贴图,但是太阳是发光的呀,这个特点要展示出来。对照上述表格,可在太阳的中心位置放置一个点光源(PointLight)。
PointLight( color : Color, intensity : Float, distance : Number, decay : Float )
- color -(可选)一个表示颜色的 Color 的实例、字符串或数字,默认为一个白色(0xffffff)的 Color 对象。
- intensity-(可选)光照强度。默认值为 1。
- distance- 光源照射的最大距离。默认值为 0(无限远)。
- decay - 沿着光照距离的衰退量。默认值为 2。
//模拟太阳发光,添加点光源
const pointLight = new THREE.PointLight(0xFFFFFF,3700);
scene.add(pointLight);
添加点光源后,可以看到以太阳为中心发射光线了。而太阳的展示必须不受到点光源的影响,所以太阳的材质可为MeshBasicMaterial不变。
1.3 添加环境光-AmbientLight
但是,星球背光的地方实在是太暗了,最好是再加上一个环境光(AmbientLight)
AmbientLight( color Color, intensity : Float )
- color-(可选)一个表示颜色的 Color 的实例、字符串或数字,默认为一个白色(0xffffff)的 Color对象。
- intensity- (可选)光照的强度。默认值为 1
//添加强度为0.1的整体环境光
const ambientLight = new THREE.AmbientLight(0xffffff,0.1);
scene.add(ambientLight);
添加环境光后,即使是星球的背面,也不至于看上去漆黑一片。
2. 星球的自转与公转
2.1 自转
在我们搭建完场景之后,剩下的就是让星球们都动起来,每个星球包括太阳都要完成自转,除了太阳,其他星球要围绕着轨道进行公转。
自转比较好处理,也就是原来的物体围绕着每个轴进行旋转。
function animate() {
requestAnimationFrame(animate);
//太阳围绕着Z轴进行每帧0.004的速度自转
sun.rotateZ(0.004);
renderer.render(scene,camera);
}
2.2 公转
在处理公转之前,先仔细理解下星球自转与公转的关系。为了让每个对象的结构更加清晰,并易于操作的时候,我们引入组(Group)概念。
THREE.Group() 是 Three.js 中非常有用的一个类,它可以用来对多个 Object3D 对象进行组织和管理。下面我们来详细了解一下它的用法:
作用: THREE.Group() 可以将多个 Object3D 对象(如 Mesh、Light、Camera 等)聚合到一个组中进行统一管理。 这样可以方便地对整个组进行位置、旋转、缩放等变换操作,而不需要逐个对每个对象进行操作。
使用方法:
//创建一个 `THREE.Group()` 对象
const group = new THREE.Group();
//将对象添加到组中
group.add(mesh1);
group.add(mesh2);
group.add(light);
//可对整个组进行变换操作
group.position.set(x, y, z);
group.rotation.set(rx, ry, rz);
group.scale.set(sx, sy, sz);
//将整个组添加到场景中
scene.add(group);
回到我们当前的案例,就可以用这种父子关系解决自转与公转的关系。在原来的函数中,我们对实例的mesh添加rotateZ方法,实现星球的自转。
function animate() {
requestAnimationFrame(animate);
//太阳围绕着Z轴进行每帧0.004的速度自转
sun.rotateZ(0.004);
mercury.mesh.rotateZ(0.004);
venus.mesh.rotateZ(0.002);
earth.mesh.rotateZ(0.02);
mars.mesh.rotateZ(0.018)
renderer.render(scene,camera);
}
实现公转,就要添加组的概念到createPlanete函数中
function createPlanete(size,radius,rotateAngle,texture) {
//……省略没有改动的代码
const mesh = new THREE.Mesh(geometry,material);
const group = new THREE.Group();
group.add(mesh);
scene.add(group);
//……省略没有改动的代码
return { mesh,group }
}
function animate() {
requestAnimationFrame(animate);
//太阳围绕着Z轴进行每帧0.004的速度自转
sun.rotateZ(0.004);
//自转
mercury.mesh.rotateZ(0.004);
venus.mesh.rotateZ(0.002);
earth.mesh.rotateZ(0.02);
mars.mesh.rotateZ(0.018);
//公转
mercury.group.rotateZ(0.04);
venus.group.rotateZ(0.015);
earth.group.rotateZ(0.01);
mars.group.rotateZ(0.008);
renderer.render(scene,camera);
}
如此,便实现了自传与公转。那是否会有疑问,为啥这里的mesh和group对象的旋转围绕的中心点不一样?从group.add(mesh)这行代码看,是group这个组里面里面包含了mesh对象,group为父级整体,而mesh为子级。可以举个例子:
const Human = new THREE.Group();
Human.add(arm);
Human.add(head);
Human.add(foot);
……
如上述代码,定义一个人为一个组,组内包含胳膊、头、脚等等。当我们要移动这个人的位置,那只需要移动human这个父类对象,它的子集也会一起移动;当我们抬起这个人的胳膊,只需要对arm对象进行操作,其他子类对象不会受到影响。
THREE.Group() 是 Three.js 中非常有用的一个类,它可以帮助我们更好地组织和管理复杂的 3D 场景。合理使用 THREE.Group() 可以使代码更加清晰、易维护,并提高渲染性能。
3. 总结
在本篇文章中,我们主要介绍了PointLight点光源、AmbientLight环境光以及分组Group()的概念。枯燥的概念列举总是缺乏说服力,而且概念容易混淆,所以八青妹都是在一个个的案例中,摸索遇到的新的概念,进行记录和总结。学以致用,才能融会贯通。这个简易的星系,我们就介绍到这里了。这里我们将完整的星系代码奉上(为了方便访问,这里将图片压缩后转成了base64)。感兴趣的同学,可以在这个基础上,多创建一些行星,给土星增加土星环,使用的是THREE.RingGeometry()。