# 网格

网格Mesh用于存储和管理顶点、索引数据,一般使用流程而言,其直接在模型导入工具的时候会被解析并存储,不需要特别关心。但有时候开发者会有自己构建 Mesh 的需求,让我们看看它的构造方法。

对于美术开发的网格模型,开发者可以查看资源导入的文档,把FBX,unity中的资源导入到项目中。

# 创建

constructor(
  vertexLayout: VertexLayout, vBuffer: ArrayBufferView,
  iBuffer: ArrayBufferView, indexType: EIndexType = EIndexType.UINT16
); 

可见构造一个Mesh需要提供好几个参数,他们是:

# VertexLayout

首先是VertexLayout,用于描述顶点布局,举个例子来说:

const myLayout = new engine.VertexLayout({
  attributes: [
    {
      name: "a_position",
      format: engine.EVertexFormat.FLOAT2,
      offset: 0,
      usage: engine.EVertexLayoutUsage.POSITION,
    },
    {
      name: "a_texCoord",
      offset: 8,
      format: engine.EVertexFormat.FLOAT2,
      usage: engine.EVertexLayoutUsage.UV0,
    },
    {
      name: "a_color",
      format: engine.EVertexFormat.UBYTE4,
      offset: 16,
      usage: engine.EVertexLayoutUsage.COLOR,
    }
  ],
  stride: 20
});

这里定义了一个自定义的VertexLayout,其中有两个参数:

  1. attributes:描述了顶点的结构,即如何 GPU 将如何理解传入的顶点数据,比如第一个元素,其表示此顶点属性在 shader 中名字为a_position,格式是FLOAT2,在顶点 Buffer 中偏移为0,并且用做POSITION
  2. stride:描述了每个顶点所占带宽的字节数。

# Buffer和Type

后面三个参数分别是顶点数据索引数据索引格式,顶点数据按照布局结构存储着整个 Mesh 的顶点数据,索引数据存储着图元对顶点的索引,而索引格式则是决定了索引数据的存储格式,其可以为UINT16或者UINT32,如果为UINT16,则索引数据中的顶点索引值不得超过65535

# 设置参数

在创建完一个新的 Mesh 后,开发者还需要设置一些参数来让它正确得运作起来,主要是 SubMesh 和包围球。

# SubMesh

有了数据和布局后,Mesh还需要提供一些信息去让渲染器知道如何使用这些数据,我们提供了叫做SubMesh的抽象,来将 Mesh 分割为数个部分:

// 添加一个SubMesh,其索引数据长度为`length`个顶点,第一个索引偏移为`offset`
mesh.addSubMesh(length, offset);

// 修改第`subMeshIndex`位置的 SubMesh 信息
mesh.modifySubMesh(subMeshIndex, length, offset);

这样分割的原因主要是即便是同一个Mesh,也可能拥有不同的材质来处理不同的部分,材质详见后面的章节。

# 包围球

对于每个 Mesh 来说,在相机的剔除阶段,都需要一个包围球来决定其是否在可视范围内,开发者可以通过以下方案来设置包围球:

// `center`为球心相对于模型原点的偏移,`radius`是球的半径
mesh.setBoundBall(center, radius);

# 更新数据

在某些场合,开发者还会需要去动态更新顶点或索引数据,比如粒子系统,我们也提供了一些方案来完成这种需求:

// 从字节偏移`offset`开始,将`buffer`整个更新到顶点数据。
mesh.uploadVertexBuffer(offset, buffer);

// 从字节偏移`offset`开始,将`buffer`整个更新到索引数据。
mesh.uploadIndexBuffer(offset, buffer);

注意这种更新操作只能在 Mesh 已经被提交到 GPU 后使用!