村子里的常年灯光会让睡眠变得非常困难,所以我们将通过关灯来介绍夜间。当然,人们不想在晚上完全黑暗中行走,所以我们将添加一些路灯。由于永恒的夜晚并不比永恒的白天好,我们将添加一个界面来从一个转换到另一个。我们想晚上关灯,白天开灯。让我们添加一个 GUI 控件来执行此操作。有光的地方也有阴影,Babylon.js 也可以添加它们。
到目前为止,我们只是在所有场景中使用了半球光。有一系列灯,但目前我们将介绍的唯一新灯是聚光灯。这可以放置在任何地方,并给出一个发光的方向。光的传播由以弧度为单位的角度给出,角度越大传播越广。最后一个参数表示光线消失的速度,数字越大,光线照射的距离越短。可以为聚光灯赋予颜色。
我们将为路灯添加聚光灯。为了创建灯柱,我们引入了另一种通过沿路径挤出形状来创建网格的方法。
我们创建形状轮廓以仅使用 x、y 平面中的点通过一系列 vector3 进行挤出。
然后我们再次使用 vector3s 设置挤出路径。路径不必限于 x、y 平面,它可以使用完整的 3D 空间进行描述。
为了使灯光更明显,我们调低了半球光的强度。
//创建聚光灯,
//参数1: 名字
//参数2: 位置
//参数3: 方向
//参数4: 角度
//参数5: 光线传播的距离
const lampLight = new BABYLON.SpotLight("name", position, direction, angle of spread, speed of disipation);
//设置聚光灯颜色
lampLight.diffuse = BABYLON.Color3.Yellow();
//设置路灯的灯的形状
const lampShape = [];
for(let i = 0; i < 20; i++) {
lampShape.push(new BABYLON.Vector3(Math.cos(i * Math.PI / 10), Math.sin(i * Math.PI / 10), 0));
}
lampShape.push(lampShape[0]); //close shape
//设置路灯的灯杆形状
const lampPath = [];
lampPath.push(new BABYLON.Vector3(0, 0, 0));
lampPath.push(new BABYLON.Vector3(0, 10, 0));
for(let i = 0; i < 20; i++) {
lampPath.push(new BABYLON.Vector3(1 + Math.cos(Math.PI - i * Math.PI / 40), 10 + Math.sin(Math.PI - i * Math.PI / 40), 0));
}
lampPath.push(new BABYLON.Vector3(3, 11, 0));
//网格创建器生成路灯的形状
const lamp = BABYLON.MeshBuilder.ExtrudeShape("lamp", {cap: BABYLON.Mesh.CAP_END, shape: lampShape, path: lampPath, scale: 0.5});
//让主光源的强度减弱
light.intensity = 0.5;
现在,我们想要通过UI空间控制小镇从白天到夜晚。
UI控件-滑块
将图形用户界面添加到场景的一种有用方法是 Babylon.js GUI。在虚拟现实中工作时,此 GUI 是必需的,因为它被设计为位于 Babylon.js 场景画布内,而不是 HTML 文档的一部分。此 GUI 已预加载到操场中,但它是一个附加脚本,可加载到您自己的项目中
<script>https://cdn.babylonjs.com/gui/babylon.gui.min.js</script>
对于村庄世界,我们将使用一个包含两个 GUI 元素的 GUI 堆栈面板。标题的文本块和通过设置光强度来改变白天到黑夜和返回的滑块。
我们需要做的第一件事是创建一个特殊的纹理,称为AdvancedDynamicTexture,GUI 元素将在其上绘制。
对于我们的世界,GUI 将是基于全屏的。
我们创建容器面板来保存屏幕右下角的其他元素。然后将其添加到高级动态纹理中。
创建文本块,滑块并将其添加到面板。
向滑块添加一个 observable 事件以更改光强度。
//特殊纹理,高级动态纹理,GUI元素在上面绘制。
const adt = BABYLON.GUI.AdvancedDynamicTexture.CreateFullscreenUI("UI");
//创建GUI面板设置位置
const panel = new BABYLON.GUI.StackPanel();
panel.width = "220px";
panel.top = "-50px";
panel.horizontalAlignment = BABYLON.GUI.Control.HORIZONTAL_ALIGNMENT_RIGHT;
panel.verticalAlignment = BABYLON.GUI.Control.VERTICAL_ALIGNMENT_BOTTOM;
adt.addControl(panel);
//创建文本
const header = new BABYLON.GUI.TextBlock();
header.text = "Night to Day";
header.height = "30px";
header.color = "white";
panel.addControl(header);
//创建滑动条
const slider = new BABYLON.GUI.Slider();
slider.minimum = 0;
slider.maximum = 1;
slider.borderColor = "black";
slider.color = "#AAAAAA";
slider.background = "#white";
slider.value = 1;
slider.height = "20px";
slider.width = "200px";
panel.addControl(slider);
//监听滑块回调
slider.onValueChangedObservable.add((value) => {
if (light) {
light.intensity = value;
}
});
现在可以模拟白天到晚上光线变化的过程了,
但现在光还无法产生阴影,显得不真实。
添加阴影
到目前为止,我们一直使用的光HemisphericLight提供环境背景光,不适合产生阴影。我们可以使用聚光灯,但是它们产生的阴影可能很微弱,因此我们将引入定向光。
像往常一样,方向是一个vector3,场景参数是可选的。
const light = new BABYLON.DirectionalLight("dir", direction, scene);
设置其位置将影响任何创建的阴影的方向和长度。
light.position = new BABYLON.Vector3(0, 15, -30);
阴影只会在创建ShadowGenerator对象时出现,给定一个投射阴影的网格,并且将投射阴影的网格设置为接收阴影。
const shadowGenerator = new BABYLON.ShadowGenerator(1024, light);
第一个参数是阴影贴图的大小和产生阴影的灯光。
我们还需要添加一个可以投射阴影的网格。
shadowGenerator.addShadowCaster(casting mesh, true);
可选的第二个参数,其默认值为 false,会将网格的任何子项添加到阴影投射器。
最后,我们还必须告诉投射阴影的网格来接收它。
const shadowGenerator = new BABYLON.ShadowGenerator(1024, light);
shadowGenerator.addShadowCaster(dude, true);
ground.receiveShadows = true;
现在可以成功的产生阴影。
但我们看世界的方式,还可以扩展更多。