Skip to content

放入模型与材质

接着第一个应用的空场景,往里放东西。

网格 = 图元 + 材质

场景里可见的物体是 Mesh,由两部分组成:

  • 图元(primitive) —— 顶点数据,决定形状。
  • 材质(material) —— 决定表面如何响应光照。

内置了几种常用图元:BoxShapeSphereShapePlaneShapeCylinderShapeTorusShape

js
  // Create scene and light
  const scene = new Scene();
  const light = new DirectionalLight(scene);
  light.lookAt(Vector3.one(), Vector3.zero(), Vector3.axisPY());

  // Create a lambert material
  const material = new LambertMaterial();
  material.albedoColor = new Vector4(1, 0, 0, 1);
  // Create a sphere mesh
  new Mesh(scene, new SphereShape(), material);

  // Create camera
  scene.mainCamera = new PerspectiveCamera(scene, Math.PI / 3, 1, 100);
  scene.mainCamera.lookAt(new Vector3(0, 0, 4), Vector3.zero(), new Vector3(0, 1, 0));
  scene.mainCamera.controller = new OrbitCameraController();

注意第 24–25 行加了一个方向光。大多数材质在没有光源时是全黑的,所以放物体的同时 要记得打光。

新建的网格默认在世界坐标原点,所以把相机放到 (0, 0, 4)lookAt() 原点。 lookAt(eye, target, up) 定义在 SceneNode 上,相机和普通节点(包括这里的方向光)都能用。

选择材质

引擎内置了多种材质,入门阶段先认这三个:

材质用途
UnlitMaterial不受光照,用于纯色标记、辅助显示、特效
LambertMaterial简单漫反射,开销低,适合风格化或性能敏感场景
PBRMetallicRoughnessMaterial基于物理的渲染,写实效果的默认选择

引擎还提供皮肤、头发、眼睛、卡通(MToon)等专用材质,以及用编辑器蓝图自定义的材质, 这些等熟悉基础之后再看。

加贴图

PBR 材质配上贴图才能出效果:

js
  // Create a PBR material
  const material = new PBRMetallicRoughnessMaterial();
  // metallic 0.9
  material.metallic = 0.9;
  // roughness 0.6
  material.roughness = 0.6;
  // Load albedo map and normal map
  getEngine()
    .resourceManager.fetchTexture('https://cdn.zephyr3d.org/doc/assets/images/earthcolor.jpg')
    .then((texture) => {
      material.albedoTexture = /** @type {import('@zephyr3d/device').Texture2D} */ (texture);
    });
  getEngine()
    .resourceManager.fetchTexture('https://cdn.zephyr3d.org/doc/assets/images/earthnormal.png', {
      linearColorSpace: true
    })
    .then((texture) => {
      material.normalTexture = /** @type {import('@zephyr3d/device').Texture2D} */ (texture);
    });

metallic 越高越像金属,roughness 越高高光越发散。

注意法线贴图传了 linearColorSpace: true,而颜色贴图没有。 这不是风格问题:颜色贴图存的是 sRGB 编码的颜色,需要转换到线性空间参与光照;而法线贴图、金属粗糙度贴图、遮罩、高度图存的是 数据而不是颜色,做 sRGB 转换会得到错误结果。漏掉这个选项通常表现为凹凸方向和强度不对。

贴图加载是异步的,fetchTexture() 返回 Promise。已加载过的资源会被缓存,同一路径不会重复请求。

加载模型

实际项目里的物体大多来自模型文件,而不是内置图元。这里有两条路径:

  1. 加载预制体(推荐) —— 先在编辑器里导入模型并保存为 .zprefab,运行时用 instantiatePrefab() 加载。
  2. 直接加载源模型 —— 装 @zephyr3d/loaders 并注册导入器,用 fetchModel() 读 glTF/GLB/FBX。

推荐第一条:@zephyr3d/scene 核心不含任何模型格式的解析代码,走预制体可以不把导入器 打进产品包;预制体保存的是序列化后的引擎对象图,你在编辑器里改过的材质、节点属性、脚本 都会一并恢复。需要在运行时加载用户上传的任意模型时才用第二条。详见 资源加载与模型导入

js
import { HttpFS, Vector3 } from '@zephyr3d/base';
import {
  Scene,
  Application,
  OrbitCameraController,
  PerspectiveCamera,
  DirectionalLight,
  getInput,
  getEngine
} from '@zephyr3d/scene';
import { backendWebGL2 } from '@zephyr3d/backend-webgl';

const myApp = new Application({
  backend: backendWebGL2,
  canvas: document.querySelector('#my-canvas'),
  runtimeOptions: {
    // When using the editor workflow, the asset path must be correctly configured
    VFS: new HttpFS('https://cdn.zephyr3d.org/doc/tut-10')
  }
});

myApp.ready().then(function () {
  // Create scene and light
  const scene = new Scene();
  const light = new DirectionalLight(scene);
  light.lookAt(Vector3.one(), Vector3.zero(), Vector3.axisPY());

  // Load a model
  getEngine()
    .resourceManager.instantiatePrefab(scene.rootNode, '/assets/Duck.zprefab')
    .then((model) => {
      model.position.setXYZ(0, -0.5, 0);
    });

  // Create camera
  scene.mainCamera = new PerspectiveCamera(scene, Math.PI / 3, 1, 100);
  scene.mainCamera.lookAt(new Vector3(0, 0, 3), Vector3.zero(), new Vector3(0, 1, 0));
  scene.mainCamera.controller = new OrbitCameraController();

  getInput().use(scene.mainCamera.handleEvent, scene.mainCamera);

  getEngine().setRenderable(scene, 0);

  myApp.run();
});

加载编辑器产出的资产必须先配好 VFS(第 18–21 行)——instantiatePrefab() 的路径是 VFS 路径而不是 URL,上例用 HttpFS 把它映射到一个 HTTP 根目录。VFS 的其他实现(内存、 IndexedDB)见虚拟文件系统

组织场景层级

节点以树形结构组织,每个节点的变换都相对于父节点。移动父节点,所有后代跟着动:

javascript
child.parent = parent;
child.position.setXYZ(0, 2, 0);   // 相对 parent 向上 2 个单位

这套机制以及遍历、查找、显示隐藏、包围盒等内容见场景图及节点

下一步

Released under the MIT License.