网格

Model Mesh

ModelMesh 是一个用于描述几何体轮廓的网状渲染数据类,主要包含了顶点(位置、法线和 UV 等)、索引和混合形状等数据。不仅可以使用建模软件制作并导出 glTF 在引擎中解析还原,还可以方便的使用脚本直接写入数据创建。

代码示例

const entity = rootEntity.createChild("mesh-example");
const meshRenderer = entity.addComponent(MeshRenderer);
 
const modelMesh = new ModelMesh(engine);
 
// Set vertieces data
const positions = [
  new Vector3(-1.0, -1.0, 1.0),
  new Vector3(1.0, -1.0, 1.0),
  new Vector3(1.0, 1.0, 1.0),
  new Vector3(1.0, 1.0, 1.0),
  new Vector3(-1.0, 1.0, 1.0),
  new Vector3(-1.0, -1.0, 1.0),
];
modelMesh.setPositions(positions);
 
// Add SubMesh
modelMesh.addSubMesh(0, 6);
 
// Upload data
modelMesh.uploadData(false);
 
meshRenderer.mesh = modelMesh;
meshRenderer.setMaterial(new UnlitMaterial(engine));

详细介绍

ModelMesh 的使用分为以下几步:

设置数据

ModelMesh 可以通过高级数据低级数据设置顶点数据,也可以根据需求选择性设置,但需要注意位置是必要数据且需要最先设置。

通过高级数据设置

可以直接通过设置 position, normal , uv高级数据生成 ModelMesh,然后调用 uploadData 方法统一上传数据至 GPU 完成应用。

const positions = new Array<Vector3>(4);
positions[0] = new Vector3(-1, 1, 1);
positions[1] = new Vector3(1, 1, 1);
positions[2] = new Vector3(1, -1, 1);
positions[3] = new Vector3(-1, -1, 1);
const uvs = new Array<Vector2>(4);
uvs[0] = new Vector2(0, 0);
uvs[1] = new Vector2(1, 0);
uvs[2] = new Vector2(1, 1);
uvs[3] = new Vector2(0, 1);
 
modelMesh.setPositions(positions);
modelMesh.setUVs(uvs);
modelMesh.uploadData(false);

设置高级数据的 API 有:

API说明
setPositions设置顶点坐标
setIndices设置索引数据
setNormals设置逐顶点法线数据
setColors设置逐顶点颜色数据
setTangents设置逐顶点切线
setBoneWeights设置逐顶点骨骼权重
setBoneIndices设置逐顶点骨骼索引数据
setUVs设置逐顶点 uv 数据

通过低级数据设置

相比于高级数据,通过低级接口设置数据可以自由操作顶点缓冲数据,不仅灵活还可能带来性能提升。但需要理解 Vertex Buffer 和 Vertex Element 之间的关系,如下图:

image.png

const pos = new Float32Array([1, 0, 0, 0, 0, 1, 1, 1, 1, 0, 0, 0]);
const posBuffer = new Buffer(
  engine,
  BufferBindFlag.VertexBuffer,
  pos,
  BufferUsage.Static,
  true
);
const mesh = new ModelMesh(engine);
mesh.setVertexBufferBinding(posBuffer, 12, 0);
const vertexElements = [
  new VertexElement(
    VertexAttribute.Position,
    0,
    VertexElementFormat.Vector3,
    0
  ),
];
mesh.setVertexElements(vertexElements);
mesh.uploadData(false);

添加 SubMesh

SubMesh 主要包含了绘制范围和绘制方式等信息。调用 addSubMesh

modelMesh.addSubMesh(0, 2, MeshTopology.Triangles);

上传数据

调用 uploadData() 方法。

如果不再需要修改 ModelMesh 数据,releaseData 参数设置为 true

modelMesh.uploadData(true);

如果需要持续修改 ModelMesh 数据,releaseData 参数设置为 false

modelMesh.uploadData(false);

读取高级数据

若要让 ModelMesh 中的顶点数据可读,需注意:

  • 在上传数据时将 releaseData 参数设置为 false
  • 若顶点数据是通过低级数据设置的,低级数据的可读属性(readable)需设置为 true
const pos = new Float32Array([1, 0, 0, 0, 0, 1, 1, 1, 1, 0, 0, 0]);
const posBuffer = new Buffer(
  engine,
  BufferBindFlag.VertexBuffer,
  pos,
  BufferUsage.Static,
  true
);
const mesh = new ModelMesh(engine);
mesh.setVertexBufferBinding(posBuffer, 12, 0);
const vertexElements = [
  new VertexElement(
    VertexAttribute.Position,
    0,
    VertexElementFormat.Vector3,
    0
  ),
];
mesh.setVertexElements(vertexElements);
mesh.uploadData(false);
// 期望得到的高级数据
const result = mesh.getPositions();

脚本添加 BlendShape 动画

BlendShape 通常用于制作精细程度非常高的动画,比如表情动画等。其原理也比较简单,主要通过权重混合基础形状和目标形状的网格数据来表现形状之间过渡的动画效果。

glTF 导入 BlendShape 动画案例:

脚本自定义 BlendShape 动画案例:

详细步骤

组织BlendShape数据

首先我们先创建一个BlendShape 对象,然后调用 addFrame()添加混合形状的帧数据,一个 BlendShape 可以添加多个关键帧,每一帧由权重几何体偏移数据组成 其中偏移位置是必要数据,偏移法线偏移切线为可选数据。

然后我们通过MeshaddBlendShape() 方法添加创建好的BlendShape

// Add BlendShape
const deltaPositions = [
  new Vector3(0.0, 0.0, 0.0),
  new Vector3(0.0, 0.0, 0.0),
  new Vector3(-1.0, 0.0, 0.0),
  new Vector3(-1.0, 0.0, 0.0),
  new Vector3(1.0, 0.0, 0.0),
  new Vector3(0.0, 0.0, 0.0),
];
const blendShape = new BlendShape("BlendShapeA");
blendShape.addFrame(1.0, deltaPositions);
modelMesh.addBlendShape(blendShape);

通过权重调整至目标 BlendShape

现在我们要将网格的形状完全调整为刚才添加的BlendShape,我们需要设置一个权重数组,由于我们只添加了一个BlendShape,所以权重数组长度为 1 即可,并把第一个元素的值设置为 1.0。

// Use `blendShapeWeights` property to adjust the mesh to the target BlendShape
skinnedMeshRenderer.blendShapeWeights = new Float32Array([1.0]);
最后更新于 七月 11, 2024

这篇文档对您有帮助吗?