1.1 环境搭建与第一帧


1.1 环境搭建与第一帧

本节摘要:环境搭建的目标只有一个——让引擎画出第一帧。本节给出最小可跑工程的"六件套"(画布、引擎、场景、相机、灯光、循环),逐件解释它在帧流水线里的岗位,并用一个"从 CDN 到工程化"的完整案例演示搭建过程与常见翻车点。本节是全册地基,后续每一节都在这个骨架上加零件。

学习目标

读完后你应当能够:

  1. 独立写出画出第一帧的最小工程,不抄示例
  2. 说出六件套各自的职责边界,尤其是引擎与场景的分工
  3. 把演示级脚本改造成模块化工程结构
  4. 用六件套检查清单快速定位"画面全黑"类问题

从一个空白画布说起

浏览器原本不会画 3D。它只提供一个矩形绘图区和一个底层的图形接口,剩下的全靠引擎。Babylon.js 做的事,就是在你写的几行 JavaScript 和这块矩形之间架一条流水线:每帧把你的场景描述翻译成几百条图形指令,交给显卡执行,再把结果像素贴回矩形。

所以"环境搭建"不是装软件,而是把流水线的两端接通。一端是画布,另一端是引擎实例。先看最小工程长什么样:

<!-- 页面里只放一块画布:它就是每一帧像素的最终去向 --> <canvas id="renderCanvas"></canvas> <!-- 画布要占满视口,避免出现"半屏渲染"的错觉 --> <style> html, body { margin: 0; padding: 0; overflow: hidden; width: 100%; height: 100%; } #renderCanvas { width: 100%; height: 100%; display: block; touch-action: none; } </style>
<!-- 通过包管理器安装引擎后,用构建工具引入;快速试验时也可用 CDN 全局脚本 --> <script src="https://cdn.babylonjs.com/babylon.js"></script>

画布有了,接通引擎。下面这段脚本就是著名的"三行起步"展开版,注释里标明了每行在流水线上的岗位:

// 1 画布:像素输出的目的地 const canvas = document.getElementById("renderCanvas"); // 2 引擎:接管画布,建立与 WebGL 的连接;第二个参数开启抗锯齿 const engine = new BABYLON.Engine(canvas, true); // 3 场景:所有相机、灯光、网格的容器,帧流水线要遍历的就是它 const scene = new BABYLON.Scene(engine); // 4 相机:决定这一帧"从哪里看"。参数依次是名称、水平角、垂直角、距离、目标点 const camera = new BABYLON.ArcRotateCamera("cam", -Math.PI / 2, Math.PI / 2.4, 8, BABYLON.Vector3.Zero(), scene); camera.attachControl(canvas, true); // 把鼠标拖拽、滚轮缩放交给相机处理 // 5 灯光:决定这一帧"看得见什么"。没有它,多数材质渲染出来是纯黑 const light = new BABYLON.HemisphericLight("sun", new BABYLON.Vector3(0, 1, 0), scene); // 6 循环:每帧调用一次场景渲染,引擎负责按显示器节拍驱动这个循环 engine.runRenderLoop(() => { scene.render(); // 一帧的全部工作都在这行里发生 }); // 窗口尺寸变化时重建画布分辨率,否则拉伸会糊 window.addEventListener("resize", () => engine.resize());

跑起来后往场景里加一个球体,就能看到第一帧以及它后面每秒约 60 次的重复绘制:

// 画第一帧的主角:一个直径 2 的球,放在世界原点 const sphere = BABYLON.MeshBuilder.CreateSphere("ball", { diameter: 2, segments: 24 }, scene); sphere.position.y = 1; // 抬高一点,别陷入地面

六件套的岗位边界

六件套里最容易混淆的是引擎与场景。用工厂打比方:引擎是厂房与电力系统——GPU 上下文、纹理上传、着色器编译这些"重资产"都归它管,一个页面通常只有一个;场景是生产线上的工装与物料——相机、灯光、网格这些"每帧要用的东西"都挂在它名下,一个引擎可以带多个场景。判断某个 API 该去哪找,先问它属于"厂房"还是"物料"。

相机与灯光则是一帧的两个输入源:相机产出"从哪看"的矩阵,灯光产出"照多亮"的数据,两者每帧都会被流水线读取。少了相机,引擎不知道往哪个视锥里投影;少了灯光,标准材质没有光可反射,画面不是"暗",是黑。

最后是循环本身。它由浏览器底层的刷新信号驱动,页面切到后台时信号暂停,循环自动休眠——这是引擎替你做的省电设计,也是后文谈帧率时的重要伏笔。

案例:从演示脚本到工程结构

背景:产品组要一个能转动的机械零件展示页。实习生把上面那段演示脚本直接贴进页面,能用,但两周后零件加到 40 个、还要加载 glTF 模型时,主脚本膨胀到八百行,改一处崩三处。

操作:重构只做一件事——按六件套的岗位拆文件。入口模块负责创建引擎与启动循环;场景模块负责装配相机、灯光与模型;业务模块只往场景里加自己的零件。入口处的核心代码收敛成:

// 入口模块:只关心流水线的通电与节拍 import { createEngine } from "./engineSetup.js"; import { buildScene } from "./sceneSetup.js"; const canvas = document.getElementById("renderCanvas"); const engine = createEngine(canvas); // 厂房归它 const scene = buildScene(engine); // 物料装配归它 engine.runRenderLoop(() => scene.render());

结果:零件装配逻辑与引擎配置解耦,帧率问题排查时可以先断开业务模块验证"空场景是否流畅",定位时间从半天缩到几分钟。

解读:六件套不只是入门清单,它是天然的模块边界。引擎层几乎不再改动,场景层随业务膨胀,两者分离后每次改动的影响范围都可预期。

变式:如果是 React 或 Vue 项目,同一套边界对应"组件只持有场景引用、引擎与循环放到应用顶层单例";框架卸载组件时只清理自己加的网格,不动循环。这套思路在第6章综合实战里还会再次出现。

⚠️ 常见坑:全黑画面九成出在六件套缺件。排查顺序固定为:画布尺寸是否为零、相机是否 attach、灯光是否存在、循环是否启动。按清单走一遍,比盯着着色器猜快得多。

本节要点回顾

  • 环境搭建的本质:接通画布与引擎两端的流水线,画出第一帧即验收通过
  • 引擎管厂房、场景管物料:一个页面一个引擎,一个引擎可挂多个场景
  • 相机是帧的眼睛、灯光是帧的能源:缺相机无处投影,缺灯光渲染出黑
  • 循环由浏览器刷新信号驱动:页面隐藏时自动休眠,这是后文帧率讨论的前提
  • 六件套即模块边界:演示脚本到工程化,先按岗位拆分再谈架构

第一帧已经画出来了,但那一行场景渲染里到底依次发生了什么?下一节把这一帧切开,画出全册反复使用的流水线八段图。


作者与出处
原作者: 灏天文库
来源:灏天文库
整理: 灏天文库整理
由灏天文库平台收录,内容或由平台用户上传,仅供学习交流
发布者: 作者: 灏天文库 转发
评论区 (0)
U