场景创建与管理
关于多场景
- 建议使用单个 Engine 实例和多个 Scene 实例
- (需要处理不同场景之间的状态传递和数据共享)
- 使用多个 Engine 实例(不推荐)
- 占用更多资源
- 使用场景栈(注意内存泄漏,无法满足复杂需求)
- 场景管理器 (Scene Manager)
- 需要设计和实现场景管理器框架,增加了开发工作量
场景创建
const sceneA = new BABYLON.Scene(engine);
const sceneB = new BABYLON.Scene(engine); 场景切换
engine.activeScene = sceneB;
///
engine.setActiveScene(sceneB); 场景图与层次结构
- 场景图 是组织 3D 对象的重要工具
- 父子关系js
const parent = BABYLON.MeshBuilder.CreateBox("parent", {}, scene); const child = BABYLON.MeshBuilder.CreateSphere("child", {}, scene); child.parent = parent; // 调用父对象的 addChild(child) 方法,将子对象添加到父对象下 parent.addChild(child); // 调用父对象的 addChild(child) 方法,将子对象添加到父对象下。 child.setParent(parent); - 变换继承js
parent.position = new BABYLON.Vector3(1, 0, 0); child.position = new BABYLON.Vector3(0, 1, 0); // child 的实际位置为 (1, 1, 0) - 访问子对象js
parent.children; // [child] - 遍历场景图js
const traverse = (node: BABYLON.Node) => { console.log(node.name); node.children.forEach(child => traverse(child)); }; traverse(scene);
动态更新
render loop 是 BabylonJS 应用的核心循环,它负责渲染场景并调用 scene 的 update 方法。
- scene.render(): 除了渲染场景,还会调用 scene.onBeforeRenderObservable 和 scene.onAfterRenderObservable 事件。
- scene.onBeforeRenderObservable: 在渲染之前触发,可以在这里执行对象的更新逻辑。
- scene.onAfterRenderObservable: 在渲染之后触发,可以在这里执行清理工作或后续处理。
scene.onBeforeRenderObservable.add(() => {
// 更新对象状态,例如位置、旋转、动画等
sphere.position.y = sphere.position.y += 0.01;
});状态机更新
根据不同的状态执行不同的更新逻辑
enum State {
IDLE,
MOVING,
JUMPING
}
let currentState: State = State.IDLE;
scene.onBeforeRenderObservable.add(() => {
switch (currentState) {
case State.IDLE:
// 执行空闲状态逻辑
break;
case State.MOVING:
// 执行移动状态逻辑
break;
case State.JUMPING:
// 执行跳跃状态逻辑
break;
}
});时间轴更新
根据时间或帧数来控制对象的动画和行为。
const timeline = new BABYLON.Animation("timeline", "position.y", 60, BABYLON.Animation.ANIMATIONTYPE_FLOAT, BABYLON.Animation.ANIMATIONLOOPMODE_NONE);
const keys = [
{ frame: 0, value: 0 },
{ frame: 60, value: 5 },
{ frame: 120, value: 0 }
];
timeline.setKeys(keys);
box.animations.push(timeline);
scene.onBeforeRenderObservable.add(() => {
// 更新时间轴进度
});冻结机制 (Freezing) 减少计算开销
被冻结时,worldMatrix(世界矩阵)被缓存起来,不再需要每帧重新计算。
对于静态对象(建筑物、地形等)它们的位置、旋转和缩放不会改变
// 创建球体
const sphere = BABYLON.MeshBuilder.CreateSphere("sphere",{ diameter: 2, segments: 32 },scene);
// 解冻对象
mesh.freezeWorldMatrix();
// 冻结对象
mesh.thawWorldMatrix(); 资源复用
资源管理器 (Resource Manager) AssetsManager 类可以用来管理资源的加载和复用。
const assetsManager = new BABYLON.AssetsManager(scene);
const textureTask = assetsManager.addTextureTask("task1", "path/to/texture.png");
const meshTask = assetsManager.addMeshTask("task2", "", "path/to/model/", "LittlestTokyo.glb");
assetsManager.onFinish = () => {
// 所有资源加载完成
};
assetsManager.load();
textureTask.onSuccess = (task) => {
const texture = task.texture;
// 复用 texture,例如应用到多个材质中
};
meshTask.onSuccess = (task) => {
const meshes = task.loadedMeshes;
// 复用 meshes,例如添加到场景中或存储到对象池中
};优点:
- 集中管理: 所有资源加载任务都集中在 AssetsManager 中,便于管理。
- 事件驱动: 可以监听资源加载完成、错误等事件。
- 并发加载: 可以同时加载多个资源,提高加载效率。
缺点:
- 复杂性: 对于非常简单的应用,使用 AssetsManager 可能有些过于复杂。
- 学习曲线: 需要学习如何使用 AssetsManager。
对象池 (Object Pooling)
class ObjectPool {
private pool: BABYLON.Mesh[] = [];
private create: () => BABYLON.Mesh;
private maxSize: number;
constructor(create: () => BABYLON.Mesh, maxSize: number = 10) {
this.create = create;
this.maxSize = maxSize;
}
getObject() {
if (this.pool.length > 0) {
return this.pool.pop();
} else {
return this.create();
}
}
releaseObject(mesh: BABYLON.Mesh) {
if (this.pool.length < this.maxSize) {
this.pool.push(mesh);
} else {
mesh.dispose();
}
}
}
// 使用示例
const pool = new ObjectPool(() => {
return BABYLON.MeshBuilder.CreateBox("pooledBox", {}, scene);
}, 20);
const mesh1 = pool.getObject();
mesh1.position = new BABYLON.Vector3(1, 0, 0);
scene.addMesh(mesh1);
const mesh2 = pool.getObject();
mesh2.position = new BABYLON.Vector3(2, 0, 0);
scene.addMesh(mesh2);
// 释放对象
pool.releaseObject(mesh1);优点:
- 性能优化: 避免频繁创建和销毁对象,减少垃圾回收开销。
- 内存管理: 控制对象池的大小,防止内存泄漏。
缺点:
- 管理复杂性: 需要手动管理对象的获取和释放。
- 状态管理: 需要注意对象的状态,例如位置、旋转、缩放等。
共享资源 (Shared Resources) 多个场景共享的资源,可以将其存储在公共的位置
示例: 使用单例模式管理共享资源
class SharedResources {
public static instance: SharedResources;
public textures: Map<string, BABYLON.Texture> = new Map();
public meshes: Map<string, BABYLON.Mesh> = new Map();
private constructor() {}
static getInstance() {
if (!SharedResources.instance) {
SharedResources.instance = new SharedResources();
}
return SharedResources.instance;
}
getTexture(name: string, url: string) {
if (!this.textures.has(name)) {
this.textures.set(name, new BABYLON.Texture(url, scene));
}
return this.textures.get(name);
}
getMesh(name: string, url: string) {
if (!this.meshes.has(name)) {
BABYLON.SceneLoader.Load(
"",
url,
scene,
(loadedScene) => {
const mesh = loadedScene.meshes[0];
this.meshes.set(name, mesh);
},
(error) => {
console.error(error);
}
);
}
return this.meshes.get(name);
}
}
// 使用示例
const sharedResources = SharedResources.getInstance();
const texture = sharedResources.getTexture("myTexture", "path/to/texture.png");
const mesh = sharedResources.getMesh("myMesh", "path/to/model.glb");优点:
- 集中管理: 所有共享资源都集中在 SharedResources 类中,便于管理。
- 复用性: 多个场景可以共享相同的资源,避免重复加载。
缺点:
- 全局状态: 使用全局变量或单例模式可能会导致代码耦合度增加。
- 线程安全: 需要注意多线程环境下的资源访问。
场景过渡与动画
淡入淡出 (Fade In/Out)
// 创建淡入淡出覆盖层
const createFadeOverlay = (scene: BABYLON.Scene) => {
const advancedTexture = BABYLON.GUI.AdvancedDynamicTexture.CreateFullscreenUI("FadeUI", true, scene);
const fadeRectangle = new BABYLON.GUI.Rectangle();
fadeRectangle.width = 1;
fadeRectangle.height = 1;
fadeRectangle.color = "black";
fadeRectangle.alpha = 0; // 初始透明
fadeRectangle.horizontalAlignment = BABYLON.GUI.HorizontalAlignment.Center;
fadeRectangle.verticalAlignment = BABYLON.GUI.VerticalAlignment.Center;
advancedTexture.addControl(fadeRectangle);
return fadeRectangle;
};
// 淡入函数
const fadeIn = (fadeRectangle: BABYLON.GUI.Rectangle, duration: number = 1000) => {
return new Promise<void>((resolve) => {
BABYLON.Tools.TransitionTo(
"alpha",
fadeRectangle,
1,
duration / 1000,
() => {
resolve();
}
);
});
};
// 淡出函数
const fadeOut = (fadeRectangle: BABYLON.GUI.Rectangle, duration: number = 1000) => {
return new Promise<void>((resolve) => {
BABYLON.Tools.TransitionTo(
"alpha",
fadeRectangle,
0,
duration / 1000,
() => {
resolve();
}
);
});
};
// 使用示例
const sceneManager = new SceneManager(engine);
const fadeRectangle = createFadeOverlay(scene);
// 切换到新场景并执行淡出淡入过渡
const switchSceneWithFade = async (newSceneName: string) => {
// 执行淡出
await fadeOut(fadeRectangle, 1000);
// 切换场景
sceneManager.switchScene(newSceneName);
// 执行淡入
await fadeIn(fadeRectangle, 1000);
};
// 监听用户输入,例如按键切换场景
window.addEventListener("keydown", (event) => {
if (event.key === "1") {
switchSceneWithFade("Scene1");
} else if (event.key === "2") {
switchSceneWithFade("Scene2");
}
});滑动 (Slide)
// 滑动过渡函数
const switchSceneWithSlide = async (newSceneName: string, direction: "left" | "right" | "up" | "down", distance: number = 10, duration: number = 1000) => {
// 计算目标位置
const originalPosition = camera.position.clone();
let targetPosition: BABYLON.Vector3;
switch (direction) {
case "left":
targetPosition = new BABYLON.Vector3(originalPosition.x - distance, originalPosition.y, originalPosition.z);
break;
case "right":
targetPosition = new BABYLON.Vector3(originalPosition.x + distance, originalPosition.y, originalPosition.z);
break;
case "up":
targetPosition = new BABYLON.Vector3(originalPosition.x, originalPosition.y + distance, originalPosition.z);
break;
case "down":
targetPosition = new BABYLON.Vector3(originalPosition.x, originalPosition.y - distance, originalPosition.z);
break;
}
// 创建滑动动画
const animation = new BABYLON.Animation("slideAnimation", "position", 60, BABYLON.Animation.ANIMATIONTYPE_VECTOR3, BABYLON.Animation.ANIMATIONLOOPMODE_NONE);
const keys = [
{ frame: 0, value: originalPosition },
{ frame: duration / 16.666, value: targetPosition }
];
animation.setKeys(keys);
camera.animations.push(animation);
// 等待动画完成
await new Promise((resolve) => {
scene.onAfterRenderObservable.addOnce(() => {
resolve();
});
});
// 切换场景
sceneManager.switchScene(newSceneName);
};旋转 (Rotation)
// 旋转过渡函数
const switchSceneWithRotation = async (newSceneName: string, axis: "x" | "y" | "z" = "y", angle: number = Math.PI / 2, duration: number = 1000) => {
// 计算目标旋转
const originalRotation = camera.rotation.clone();
let targetRotation: BABYLON.Vector3;
switch (axis) {
case "x":
targetRotation = new BABYLON.Vector3(originalRotation.x + angle, originalRotation.y, originalRotation.z);
break;
case "y":
targetRotation = new BABYLON.Vector3(originalRotation.x, originalRotation.y + angle, originalRotation.z);
break;
case "z":
targetRotation = new BABYLON.Vector3(originalRotation.x, originalRotation.y, originalRotation.z + angle);
break;
}
// 创建旋转动画
const animation = new BABYLON.Animation("rotationAnimation", "rotation", 60, BABYLON.Animation.ANIMATIONTYPE_VECTOR3, BABYLON.Animation.ANIMATIONLOOPMODE_NONE);
const keys = [
{ frame: 0, value: originalRotation },
{ frame: duration / 16.666, value: targetRotation }
];
animation.setKeys(keys);
camera.animations.push(animation);
// 等待动画完成
await new Promise((resolve) => {
scene.onAfterRenderObservable.addOnce(() => {
resolve();
});
});
// 切换场景
sceneManager.switchScene(newSceneName);
};缩放 (Scaling)
// 缩放过渡函数
const switchSceneWithScale = async (newSceneName: string, scale: number = 2, duration: number = 1000) => {
// 计算目标缩放
const originalScale = camera.zoom;
const targetScale = originalScale * scale;
// 创建缩放动画
const animation = new BABYLON.Animation("scaleAnimation", "zoom", 60, BABYLON.Animation.ANIMATIONTYPE_FLOAT, BABYLON.Animation.ANIMATIONLOOPMODE_NONE);
const keys = [
{ frame: 0, value: originalScale },
{ frame: duration / 16.666, value: targetScale }
];
animation.setKeys(keys);
camera.animations.push(animation);
// 等待动画完成
await new Promise((resolve) => {
scene.onAfterRenderObservable.addOnce(() => {
resolve();
});
});
// 切换场景
sceneManager.switchScene(newSceneName);
};组合过渡 (Combined Transitions)
// 淡入淡出 + 滑动过渡函数
const switchSceneWithFadeAndSlide = async (newSceneName: string, direction: "left" | "right" | "up" | "down", distance: number = 10, duration: number = 1000) => {
// 执行淡出
await fadeOut(fadeRectangle, duration / 2);
// 执行滑动
await switchSceneWithSlide(newSceneName, direction, distance, duration / 2);
// 执行淡入
await fadeIn(fadeRectangle, duration / 2);
};
// 淡入淡出 + 旋转过渡函数
const switchSceneWithFadeAndRotation = async (newSceneName: string, axis: "x" | "y" | "z" = "y", angle: number = Math.PI / 2, duration: number = 1000) => {
// 执行淡出
await fadeOut(fadeRectangle, duration / 2);
// 执行旋转
await switchSceneWithRotation(newSceneName, axis, angle, duration / 2);
// 执行淡入
await fadeIn(fadeRectangle, duration / 2);
};相机系统与优化
1. ArcRotateCamera(弧旋转相机)
特点:
- 旋转: 允许用户通过鼠标或触摸旋转视角。
- 缩放: 支持缩放功能,例如鼠标滚轮或捏合手势。
- 平移: 可以通过键盘或触摸手势进行平移。
适用场景:
- D 模型查看器: 适用于需要用户自由旋转和查看 3D 模型的场景。
- 游戏: 适用于第三人称视角的游戏,例如角色扮演游戏、动作游戏等。
- 建筑可视化: 适用于建筑和室内设计的可视化展示。
const camera = new BABYLON.ArcRotateCamera("arcCamera", Math.PI / 2, Math.PI / 4, 10, BABYLON.Vector3.Zero(), scene);
camera.attachControl(canvas, true);2. UniversalCamera(通用相机)
特点:
- 多功能: 结合了 FreeCamera 和 TouchCamera 的功能,支持鼠标、触摸和键盘输入。
- 第一人称视角: 适用于第一人称视角的应用,例如第一人称射击游戏、虚拟现实等。
适用场景:
- b第一人称游戏: 适用于需要用户以第一人称视角探索 3D 世界的游戏。
- b虚拟现实: 适用于虚拟现实应用,提供沉浸式的体验。
- b模拟器: 适用于驾驶模拟器、飞行模拟器等应用。
const camera = new BABYLON.UniversalCamera("universalCamera", new BABYLON.Vector3(0, 1, -5), scene);
camera.attachControl(canvas, true);3. FreeCamera(自由相机)
特点:
- 自由移动: 允许用户自由移动相机的位置和方向。
- 键盘控制: 主要通过键盘控制相机的移动,例如 WASD 键。
适用场景:
- 飞行模拟: 适用于需要用户自由飞行和探索的场景。
- 3D 平台游戏: 适用于需要用户跳跃和移动的平台游戏。
const camera = new BABYLON.FreeCamera("freeCamera", new BABYLON.Vector3(0, 1, -5), scene);
camera.attachControl(canvas, true);4. FollowCamera(跟随相机)
特点:
- 目标跟踪: 相机始终跟随一个目标对象,例如玩家角色。
- 平滑过渡: 相机移动具有平滑的过渡效果。
适用场景:
- 第三人称游戏: 适用于需要相机始终跟随玩家角色的第三人称游戏。
- 运动游戏: 适用于需要跟踪运动对象的游戏,例如赛车游戏、足球游戏等。
const camera = new BABYLON.FollowCamera("followCamera", new BABYLON.Vector3(0, 10, -20), scene);
camera.radius = 30; // 相机与目标的距离
camera.heightOffset = 10; // 相机与目标的高度差
camera.rotationOffset = Math.PI / 4; // 相机与目标的水平旋转角度
camera.attachControl(canvas, true);
// 设置目标对象
const target = BABYLON.MeshBuilder.CreateBox("target", {}, scene);
camera.lockedTarget = target;5. AnaglyphCamera(红蓝 3D 相机)
特点:
- 立体视觉: 通过红蓝滤镜实现立体视觉效果。
- 3D 效果: 适用于需要 3D 效果的场景,例如 3D 电影、虚拟现实等。
适用场景:
- 3D 电影: 适用于需要立体视觉效果的 3D 电影。
- 虚拟现实: 适用于简单的虚拟现实应用。
const camera = new BABYLON.AnaglyphCamera("anaglyphCamera", 0.033, 0.033, 0.5, BABYLON.Vector3.Zero(), scene);
camera.attachControl(canvas, true);相机优化
1. 视锥体裁剪 (Frustum Culling)
概念: 视锥体裁剪是一种剔除不可见对象的技术。相机有一个视锥体,只有位于视锥体内的对象才会被渲染。
实现:
BabylonJS 默认情况下会进行视锥体裁剪。 手动优化: 可以通过调整相机的 fov (视场角)、aspectRatio (宽高比)、near 和 far (近裁剪面和远裁剪面) 来优化视锥体。
const camera = new BABYLON.ArcRotateCamera("arcCamera", Math.PI / 2, Math.PI / 4, 10, BABYLON.Vector3.Zero(), scene);
camera.fov = Math.PI / 3; // 调整视场角
camera.minZ = 0.1; // 设置近裁剪面
camera.maxZ = 100; // 设置远裁剪面2. 多视口渲染 (Multiple Viewports)
概念: 在一个画布上渲染多个视口,每个视口使用不同的相机。
适用场景:
分屏游戏: 例如双人游戏,每个玩家有自己的视角。
监控场景: 例如在虚拟城市中显示不同区域的视角。
// 创建第一个视口
const camera1 = new BABYLON.ArcRotateCamera("camera1", Math.PI / 2, Math.PI / 4, 10, BABYLON.Vector3.Zero(), scene);
camera1.attachControl(canvas, true);
// 创建第二个视口
const camera2 = new BABYLON.ArcRotateCamera("camera2", Math.PI / 2, Math.PI / 4, 10, BABYLON.Vector3.Zero(), scene);
camera2.attachControl(canvas, true);
// 设置视口大小
const viewport1 = {
x: 0,
y: 0,
width: 0.5,
height: 1
};
const viewport2 = {
x: 0.5,
y: 0,
width: 0.5,
height: 1
};
// 渲染两个视口
engine.runRenderLoop(() => {
scene.render(camera1, viewport1);
scene.render(camera2, viewport2);
});3. 相机碰撞检测 (Camera Collision Detection) 防止相机穿过物体或穿过场景边界。
BabylonJS 提供了 collision 和 physics 模块,可以用来实现相机碰撞检测。
camera.checkCollisions = true;
camera.applyGravity = true;
scene.collisionsEnabled = true;
// 创建地面并启用碰撞
const ground = BABYLON.MeshBuilder.CreateGround("ground", { width: 100, height: 100 }, scene);
ground.checkCollisions = true;4. 相机动画 (Camera Animation)
使用 BabylonJS 的动画系统:
const animCam = new BABYLON.Animation("cameraAnimation", "position", 60, BABYLON.Animation.ANIMATIONTYPE_VECTOR3, BABYLON.Animation.ANIMATIONLOOPMODE_NONE);
const keys = [
{ frame: 0, value: new BABYLON.Vector3(0, 1, -10) },
{ frame: 60, value: new BABYLON.Vector3(0, 1, -20) }
];
animCam.setKeys(keys);
camera.animations.push(animCam);使用 Path3D 和 FollowCamera 实现路径跟踪:
const points = [
new BABYLON.Vector3(0, 1, -10),
new BABYLON.Vector3(10, 1, -10),
new BABYLON.Vector3(10, 1, -20),
new BABYLON.Vector3(0, 1, -20)
];
const path = new BABYLON.Path3D(points);
const followCamera = new BABYLON.FollowCamera("followCamera", points[0], scene);
followCamera.attachControl(canvas, true);
followCamera.lockedTarget = mesh;
followCamera.speed = 2;
followCamera.radius = 5;
followCamera.heightOffset = 2;性能优化
- 减少相机数量: 尽量使用较少的相机,避免不必要的渲染开销。
- 优化相机参数: 调整相机的 fov, near 和 far 值,以减少渲染对象的数量。
- 使用 LOD (Level of Detail) 技术: 根据相机与对象的距离,切换不同细节级别的模型。
- 实例化: 对于大量相同的对象,使用实例化技术来提高渲染效率。
相机类型概览
BabylonJS 提供了多种相机类型,每种相机都有其独特的功能和适用场景。以下是一些常见的相机类型:
1.ArcRotateCamera(弧旋转相机)
2.UniversalCamera(通用相机)
3.FreeCamera(自由相机)
4.FollowCamera(跟随相机)
5.AnaglyphCamera(红蓝 3D 相机)
6.DeviceOrientationCamera(设备方向相机)
7.VirtualJoysticksCamera(虚拟摇杆相机)
8.VRDeviceOrientationCamera(VR 设备方向相机)
光源
1. HemisphericLight(半球光):环境光
const hemisphericLight = new BABYLON.HemisphericLight("hemiLight", new BABYLON.Vector3(0, 1, 0), scene);
hemisphericLight.intensity = 0.5; // 调整光照强度
hemisphericLight.diffuse = new BABYLON.Color3(1, 1, 1); // 设置光照颜色2. DirectionalLight(方向光)
const directionalLight = new BABYLON.DirectionalLight("dirLight", new BABYLON.Vector3(-1, -2, -1), scene);
directionalLight.intensity = 1.0; // 调整光照强度
directionalLight.diffuse = new BABYLON.Color3(1, 1, 1); // 设置光照颜色3. PointLight(点光源)
const pointLight = new BABYLON.PointLight("pointLight", new BABYLON.Vector3(0, 10, 0), scene);
pointLight.intensity = 0.8; // 调整光照强度
pointLight.diffuse = new BABYLON.Color3(1, 1, 1); // 设置光照颜色4. SpotLight(聚光灯)
const spotLight = new BABYLON.SpotLight("spotLight", new BABYLON.Vector3(0, 10, 0), new BABYLON.Vector3(0, -1, 0), Math.PI / 3, 2, scene);
spotLight.intensity = 0.7; // 调整光照强度
spotLight.diffuse = new BABYLON.Color3(1, 1, 1); // 设置光照颜色阴影
阴影是光照的“影子”,它们为 3D 世界增添了深度、层次感和真实感。
1. 阴影生成器 (ShadowGenerator)
// 创建 ShadowGenerator 实例
// 第一个参数: 阴影贴图的大小(分辨率),例如 1024。
// 第二个参数: 产生阴影的光源对象。
const shadowGenerator = new BABYLON.ShadowGenerator(1024, directionalLight);
// 添加投射阴影的对象
// 第一个参数: 需要投射阴影的网格对象。
// 第二个参数(可选): 是否包含子对象。
shadowGenerator.addShadowCaster(mesh, true);
// 设置接收阴影的对象:
mesh.receiveShadows = true;2. 阴影类型
- 标准阴影贴图 (Standard Shadow Map):
- 优点: 实现简单,计算成本较低。
- 缺点: 阴影质量较低,存在锯齿和阴影模糊问题。
- 适用场景: 简单场景或对阴影质量要求不高的应用。
- 百分比更近过滤 (PCF, Percentage Closer Filtering):
- 优点: 阴影质量较高,边缘平滑。
- 缺点: 计算成本较高。
- 适用场景: 需要更高质量的阴影,例如建筑可视化、3D 建模等。
- 指数阴影贴图 (ESM, Exponential Shadow Map):
- 优点: 阴影质量高,边缘平滑,计算成本适中。
- 缺点: 可能会出现阴影漏光问题。
- 适用场景: 需要高质量阴影且对性能有一定要求的场景。
- 模糊指数阴影贴图 (BESM, Blurred Exponential Shadow Map):
- 优点: 阴影质量更高,阴影边缘更加柔和。
- 缺点: 计算成本较高。
- 适用场景: 需要非常柔和的阴影效果,例如室内场景、角色模型等。
- 方差阴影贴图 (VSM, Variance Shadow Map):
- 优点: 阴影质量高,支持软阴影。
- 缺点: 可能会出现阴影漏光问题,计算成本较高。
- 适用场景: 需要高质量软阴影的场景,例如户外场景、开放世界等。
shadowGenerator.useBlurExponentialShadowMap = true;
shadowGenerator.blurScale = 2; // 调整模糊强度3. 阴影优化
- 降低阴影贴图的分辨率,例如从 1024 降低到 512,可以减少计算成本,但会降低阴影质量。
- 将视锥体划分为多个区域,每个区域使用不同分辨率的阴影贴图。
- 调整阴影偏移值,可以减少自阴影 (self-shadowing) 问题和阴影漏光问题。
shadowGenerator.bias = 0.001;// 调整阴影偏移值
柔和的光照: 使用 HemisphericLight 或 PointLight 来模拟柔和的自然光。
const hemisphericLight = new BABYLON.HemisphericLight("hemiLight", new BABYLON.Vector3(0, 1, 0), scene);
hemisphericLight.intensity = 0.3;
hemisphericLight.diffuse = new BABYLON.Color3(1, 0.8, 0.6); // 暖黄色柔和的阴影: 使用 BESM 或 PCF 阴影类型,并调整阴影偏移和过滤参数,以获得柔和的阴影效果。
shadowGenerator.useBlurExponentialShadowMap = true;
shadowGenerator.blurScale = 3; // 增加模糊强度昏暗的光照: 使用 DirectionalLight 或 SpotLight 来模拟昏暗的光线。
const directionalLight = new BABYLON.DirectionalLight("dirLight", new BABYLON.Vector3(-1, -1, -1), scene);
directionalLight.intensity = 0.5;
directionalLight.diffuse = new BABYLON.Color3(0.3, 0.3, 0.5); // 冷蓝色强烈的阴影
shadowGenerator.useStandardShadowMap = true;
shadowGenerator.bias = 0.0001; // 调整阴影偏移值动态的光照: 使用 PointLight 或 SpotLight 并添加动态变化,例如闪烁、颜色变化等。
const pointLight = new BABYLON.PointLight("pointLight", new BABYLON.Vector3(0, 10, 0), scene);
pointLight.intensity = 1.0;
pointLight.diffuse = new BABYLON.Color3(0.5, 0.8, 1); // 蓝白色
// 添加闪烁动画
const animateLight = () => {
pointLight.intensity = 0.5 + 0.5 * Math.sin(Date.now() / 500);
requestAnimationFrame(animateLight);
};
animateLight();光晕效果: 使用 Post-Processing 效果,例如 Bloom 或 Glow,来模拟光晕效果。
const postProcess = new BABYLON.PostProcess("glow", "glow", ["offset"], ["textureSampler"], 1, camera);
postProcess.onApply = (effect) => {
effect.setFloat("offset", 0.5);
effect.setTexture("textureSampler", scene.activeCamera.getActivePostProcess().texture);
};生命周期
BABYLON.Engine 引擎
- 创建
const engine = new BABYLON.Engine(canvas, true); // 启用抗锯齿
- 启动渲染循环
engine.runRenderLoop(() => { scene.render();});
- 渲染与更新
scene.onBeforeRenderObservable更新场景scene.onAfterRenderObservable渲染场景
- 暂停与恢复
engine.pauseRenderLoop()暂停engine.resumeRenderLoop()恢复
- 销毁与释放
engine.dispose();
Scene 场景
- 创建
const scene = new BABYLON.Scene(engine);
- 场景图与对象管理
- 渲染与更新
scene.render()渲染scene.onBeforeRenderObservable更新场景scene.onAfterRenderObservable渲染场景
- 销毁与释放
scene.dispose();
Camera 相机
- 创建
const camera = new BABYLON.ArcRotateCamera("camera1", Math.PI / 2, Math.PI / 4, 5, BABYLON.Vector3.Zero(), scene);
- 相机控制
camera.attachControl(canvas, true);将相机与画布关联,启用用户输入控制。
- 销毁与释放
camera.dispose();
Light 光源
- 创建
const light = new BABYLON.DirectionalLight("light1", new BABYLON.Vector3(-1, -2, -1), scene);
- 阴影生成js
const shadowGenerator = new BABylon.ShadowGenerator(1024, light); shadowGenerator.addShadowCaster(mesh, true); - 销毁与释放
light.dispose();
Mesh 网格
- 创建
const mesh = BABYLON.MeshBuilder.CreateBox("box1", {}, scene);
- 网格变换
- 销毁与释放
mesh.dispose();
Resources 资源
- 创建
const texture = new BABYLON.Texture("path/to/texture.png", scene);
- 资源复用
- 销毁与释放
texture.dispose();