1 <template>
2 <div class="badylon-canvas2">
3 <canvas ref="renderCanvas2" id="render-canvas2"></canvas>
4 </div>
5 </template>
6
7 <script setup lang="ts">
8 import * as BABYLON from "babylonjs";
9 import "babylonjs-loaders";
10 import Assets from "../../utils/babylon/babylon-asset.js";
11 import type { Engine, Scene } from "babylonjs";
12 import { ref, onMounted } from "vue";
13
14 type EngineType = Engine | null;
15 type SceneType = Scene | null;
16
17 const renderCanvas2 = ref<HTMLCanvasElement | null>(null);
18 let engine: EngineType = null;
19 let scene: SceneType = null;
20 let sceneToRender: SceneType = null;
21
22 // 渲染循环
23 function startRenderLoop(engine: Engine) {
24 /**
25 * runRenderLoop(renderFunction: (() => void)): void
26 * 注册并执行一个渲染循环。引擎可以有多个渲染函数
27 * renderFunction: (() => void) --- 定义要持续执行的函数(不停的执行)
28 */
29 engine.runRenderLoop(() => {
30 console.log("runRenderLoop...");
31 // 如果没有runRenderLoop函数,就无法在移动图像时更新“相机”的数据,简单来说就是图像不会动了。
32 if (sceneToRender && sceneToRender.activeCamera) {
33 /**
34 * render(updateCameras?: boolean, ignoreAnimations?: boolean): void
35 * updateCameras: 定义一个布尔值,指示摄像机是否必须根据其输入进行更新(默认为true)
36 * ignoreAnimations: 定义一个布尔值,指示动画是否不应该执行(默认为false)
37 */
38 sceneToRender.render(); // 如果没有这个不会出现图像;渲染scene
39 // sceneToRender.debugLayer.show(); // 用来调试babylon的
40 }
41 });
42 }
43
44 // 创建场景
45 function createScene() {
46 if (engine === null) return null;
47
48 /**
49 * new Scene(engine: Engine, options?: SceneOptions): Scene
50 * 表示要由引擎渲染的场景。
51 * engine: Engine --- 定义用于渲染此场景的引擎
52 * options: 可选; https://doc.babylonjs.com/typedoc/interfaces/BABYLON.SceneOptions
53 *
54 */
55 const scene = new BABYLON.Scene(engine);
56
57 /**
58 * new ArcRotateCamera(name: string, alpha: number, beta: number, radius: number, target: Vector3, scene?: Scene, setActiveOnSceneIfNoneActive?: boolean): ArcRotateCamera
59 * 在给定的场景中实例化一个新的ArcRotateCamera
60 * name: 名字
61 * alpha: 定义相机沿纵向轴的旋转---一开始显示的角度
62 * beta: 定义相机沿纬度轴的旋转---一开始显示的角度
63 * radius: 定义相机与目标的距离
64 * target: Vector3 // 控制相机的视角
65 * setActiveOnSceneIfNoneActive: 定义如果未定义其他活动相机,则是否应将该相机标记为活动的
66 *
67 * new Tools(): Tools
68 * ToRadians(angle: number): number
69 * 将角度的角度转换为弧度
70 */
71 const camera = new BABYLON.ArcRotateCamera(
72 "camera",
73 -Math.PI / 2,
74 Math.PI / 2.5,
75 15,
76 new BABYLON.Vector3(0, 0, 0),
77 scene
78 );
79 // https://doc.babylonjs.com/typedoc/classes/BABYLON.ArcRotateCamera#constructor
80 camera.attachControl(renderCanvas2.value, true);
81
82 /**
83 * new HemisphericLight(name: string, direction: Vector3, scene: Scene): HemisphericLight
84 * 根据传递的方向(Vector3)在场景中创建一个半球光对象。
85 半球光模拟周围环境光,所以通过的方向是光的反射方向,而不是入射方向。
86 半球光不能投射阴影。
87 */
88
89 /**
90 * name: 灯光的名字
91 * direction: 光反射的方向
92 * scene: 灯光所属的场景
93 */
94 const light = new BABYLON.HemisphericLight(
95 "light",
96 /**
97 * new Vector3(x?: number, y?: number, z?: number): Vector3
98 * 从给定的x, y, z (float)坐标创建一个新的Vector3对象。三维向量
99 * x: number; 定义第一个坐标(在X轴上)
100 * y: number
101 * z: number
102 */
103 new BABYLON.Vector3(1, 1, 0),
104 scene
105 );
106
107 /**
108 * 添加声音
109 * new Sound(name: string, urlOrArrayBuffer: any, scene?: Nullable<Scene>, readyToPlayCallback?: Nullable<(() => void)>, options?: ISoundOptions): Sound
110 * urlOrArrayBuffer: Url到声音加载异步或ArrayBuffer,它也与MediaStreams和AudioBuffers一起工作
111 * readyToPlayCallback: 如果你想在声音准备好播放后加载你的代码,提供一个回调函数
112 */
113 // const music = new BABYLON.Sound(
114 // "cello",
115 // "sounds/cellolong.wav",
116 // scene,
117 // null,
118 // { loop: true, autoplay: true }
119 // );
120
121 // 3秒一次
122 const bounce = new BABYLON.Sound("bounce", "sounds/bounce.wav", scene);
123 setInterval(() => bounce.play(), 3000);
124
125 /**
126 * new SceneLoader(): SceneLoader
127 * ImportMeshAsync(meshNames: any, rootUrl: string, sceneFilename?: string | File, scene?: Nullable<Scene>, onProgress?: Nullable<((event: ISceneLoaderProgressEvent) => void)>, pluginExtension?: Nullable<string>): Promise<ISceneLoaderAsyncResult>
128 * meshNames: 网格名称的数组,单个网格名称,或用于过滤导入网格的所有网格的空字符串, string | Array
129 * rootUrl: 一个字符串,它定义了场景和资源的根url或rootURL和filename的连接
130 * sceneFilename: 定义场景文件名的字符串或以"data "开头的字符串:后面是场景的字符串版本或file对象(默认:空字符串)
131 * scene: 巴比伦的例子。附加到的场景
132 * onProgress: 一个回调函数,为正在加载的每个文件提供一个进度事件
133 * pluginExtension: 扩展用于确定插件
134 */
135 // 无法解析.fbx文件
136
137 // 只加载一个
138 // BABYLON.SceneLoader.ImportMeshAsync(
139 // "semi_house",
140 // "https://assets.babylonjs.com/meshes/",
141 // "both_houses_scene.babylon"
142 // );
143
144 // 一次性加载多个模型
145 // BABYLON.SceneLoader.ImportMeshAsync(
146 // ["ground", "semi_house"],
147 // "https://assets.babylonjs.com/meshes/",
148 // "both_houses_scene.babylon"
149 // );
150
151 // 加载后修改模型
152 BABYLON.SceneLoader.ImportMeshAsync(
153 "",
154 "https://assets.babylonjs.com/meshes/",
155 "both_houses_scene.babylon"
156 ).then((result) => {
157 const house1 = scene.getMeshByName("detached_house");
158 house1 && (house1.position.y = 2);
159 const house2 = result.meshes[2];
160 house2.position.y = 1;
161 });
162
163 return scene;
164 }
165
166 function createDefaultEngine() {
167 /**
168 * new Engine(canvas, antialias, options, adaptToDeviceRatio) 创建一个引擎
169 * canvas: 现实容器,可以是Canvas标签...
170 * antialias: boolean, 默认是false,启动后可以防止图像出现“锯齿”
171 * optiosn: 选项,
172 * adaptToDeviceRatio: false, // 定义是否适应设备的视口特征
173 audioEngine: false, // 定义是否也应该初始化webaudio
174 audioEngineOptions: {}, // 指定音频引擎的选项
175 autoEnableWebVR: false, // 定义是否应该自动启用webvr
176 deterministicLockstep: false, // 定义动画是否应该使用确定性锁定步骤运行
177 * adaptoToDeviceRatio: boolean,默认false;定义是否适应视口
178 ....
179 https://doc.babylonjs.com/typedoc/interfaces/BABYLON.EngineOptions
180 */
181 return new BABYLON.Engine(renderCanvas2.value, true, {});
182 }
183
184 async function initFunction() {
185 async function asyncEngineCreation() {
186 try {
187 return createDefaultEngine(); // 先创建容器
188 } catch (e) {
189 console.log(
190 "the available createEngine function failed. Creating the default engine instead"
191 );
192
193 return createDefaultEngine();
194 }
195 }
196
197 engine = await asyncEngineCreation(); // 先创建容器
198 if (!engine) throw "engine should not be null.";
199 startRenderLoop(engine); // 如果没有这个不会出现图像
200 scene = createScene(); // 再创建渲染场景
201 // 创建容器 ---> 再创建渲染场景放进容器 ---> 再创建"相机","灯光","球体","地面"放进场景
202 scene && scene.render();
203 }
204
205 onMounted(() => {
206 if (renderCanvas2.value) {
207 console.log("ttttt");
208 initFunction().then(() => {
209 sceneToRender = scene;
210 });
211
212 // Resize
213 window.addEventListener("resize", function () {
214 engine && engine.resize();
215 });
216 }
217 });
218 </script>
219
220 <style lang="scss" scoped>
221 .badylon-canvas2 {
222 width: 100%;
223 height: 100%;
224
225 #render-canvas2 {
226 width: 100%;
227 height: 100%;
228 // touch-action 用在触摸屏,缩放
229 touch-action: none;
230 }
231 }
232 </style>