跳转到内容

模型

引擎画 mesh,而它只加载一种 mesh 格式:.esmesh.gltf / .glb源文件,不是资源 ——把它带进项目会跑一次导入,写出场景真正能引用的那些资源。

本页讲的就是这次导入。画出结果的那个组件见组件参考; 材质整体见材质

三个入口,同一次导入:

方式 会发生什么
.gltf/.glb 拖进内容浏览器 源文件(连同它引用的文件)被拷进项目并导入。
直接放进项目目录 编辑器会发现并导入它 —— git checkout、访达里拷贝、Blender 重新导出,都算。
estella import-gltf <file> [outDir] --project <dir> 命令行里的同一次导入。--scale <n> 决定大小。

产物落在源文件旁边,而不是内容浏览器当前显示的目录:重新导入必须覆盖同一批文件。

以带两个 primitive、一张内嵌图的 robot.gltf 为例:

产物 是什么
robot_0_0.esmeshrobot_1_0.esmesh 每个 primitive 一个几何文件,命名 <stem>_<mesh>_<primitive>(只有一个时就是 <stem>)。
robot_0.png 文件内嵌的图片。已经在磁盘上的图片只被引用,不会复制。
robot_m0.esmaterial 仅当源材质说了 Mesh2D 装不下的东西时才写 —— 见材质
robot_Walk.estimeline 源里每个动画一个,命名 <stem>_<动画名> —— 见动画
robot.esprefab 各部分怎么组装:节点层级,每个 Mesh2D 带上自己的 mesh、贴图、tint 和开关。

prefab 拖进场景。单个 .esmesh 也是资源(Mesh2D.mesh 字段可以直接引用), 但 prefab 才是作者组装好的那个模型。

glTF 以为单位。一个世界单位是一个设计像素。所以一个 1.7 米高的角色进来只有 1.7 个单位高 —— 几个像素 —— 除非你另行指定。

导入器不猜这个系数。模型进来很小的时候它会说出来,而修正它的那个数字就是源文件导入设置里的 Scale(在内容浏览器里选中 .gltf)。保存它会重新导入这个模型。

Scale 落在 prefab 根节点的 Transform 上,不会烘进几何 —— .esmesh 保持与源一致, 而那个数字始终看得见、改得动。

以下情况会重新跑一次导入:

  • 源文件变了(重新导出覆盖了旧的);
  • 在内容浏览器里对它选择 Reimport
  • 保存了一项导入设置。

资源 uuid 会保留,所以引用这些产物的场景和 prefab 继续有效。

一个 glTF 材质按「谁装得下」拆开。

落在 Mesh2D 组件上 —— base color,因为引擎的 mesh 通路本来就是 texture(uv) × 顶点色 × tint

glTF 组件字段
baseColorTexture texture
baseColorFactor color
alphaMode 不是 BLEND opaque
doubleSided: false cullBackfaces
NORMAL 属性 lit: true

落在 .esmaterial —— 着色,那是逐材质的常量和采样器,组件没有地方放。 只在源文件用到它们时才写,基于内建的 Model shader:

glTF 材质参数
normalTexture u_normalMap
emissiveFactor / emissiveTexture u_emissive / u_emissiveMap
occlusionTexture(含 strength u_occlusionMap / u_occlusionStrength
alphaMode: MASK + alphaCutoff u_alphaCutoff
metallicFactor / roughnessFactor / metallicRoughnessTexture u_metallic / u_roughness / u_metallicRoughnessMap

金属度与粗糙度即使等于引擎自己的默认值也会写出来:glTF 把两个系数都默认成 1(完全粗糙的 金属),而 shader 把金属度默认成 0 —— 省略它们的产物会描述出一个源文件没有说过的表面。

材质还会把这次 draw 的混合、深度和剔除重述一遍 —— 因为材质会替换它们, 这样一个被要求自我遮挡的模型,在有了材质之后仍然自我遮挡。

源里的每个动画会在 mesh 旁边写出一个 .estimeline,prefab 的根挂一个 TimelinePlayer 指向第一个,且 playing 是关的 —— 播哪一个是场景的决定。打开 playing,或调用 timeline API,它就开始播。

一条 clip 驱动的是它当初针对的那些节点,按名字路径在 prefab 根之下寻址。同一父节点下 重名的兄弟节点会在导入时被改成不同的名字,好让一条轨道不会驱动错对象;prefab 里显示的就是 那个名字。

LINEARSTEPCUBICSPLINE 都会带过来,含切线。

绑定好的模型会带着绑定进来:mesh 携带它的骨骼索引、权重和绑定姿态,prefab 给实体挂一个 MeshSkin,按那些矩阵的顺序列出骨骼实体。移动骨骼 —— 手动,或者由上面的动画驱动 —— mesh 就会变形。

蒙皮 mesh 自己的 transform 会被忽略,这是 glTF 的要求:它的骨骼已经摆在世界里了, 所以移动 mesh 实体不会有任何效果。要移动的是骨架的根,或者骨骼本身。

限制 为什么
每个 mesh 最多 64 根骨骼 姿态走 uniform 块,64 根是 4KB —— 在 WebGL2 对一个块的保证范围内。超过的 mesh 按不变形绘制。
骨骼数必须与绑定姿态一致 MeshSkin 列出的骨骼数对不上时按不变形绘制,而不是让顶点去解析错误的矩阵。

EXT_meshopt_compressionKHR_draco_mesh_compression 在导入时解码,所以压缩过的导出件 产出与未压缩件相同的 .esmesh。运行时什么都不变:压缩是源文件的属性,不是产物的属性。

导入器不会无声丢弃任何东西 —— 它带不过来的,都会在导入时作为提示出现。

不导入 为什么
morph target、weights 动画通道 在多个形状之间混合需要那些 target 本身,而它们没有被带过来。
第二套 UV(TEXCOORD_1)、KHR_texture_transform UV 按作者写的原样使用。
非三角形 primitive(mode ≠ 4) 渲染器画的是三角形列表。
normalTexture.scale 法线贴图按全强度应用。
  • 点光的距离在 XY 平面上算(2D 光照模型固有);方向光是精确的,而那通常正是着色一个模型需要的。
  • 自己写了 vertex stage 的材质不会被重定向到 mesh 几何上 —— 那样它会无视 per-object transform。 只写 fragment 的材质两边都能用。
  • 没有 NORMAL 属性的几何仍然会受光,落在 2D 表面那个常量法线上 —— 平,但不是黑的。