跳到主要内容

网格实例

MeshInstance 表示网格在世界中的一次放置:它将网格与材质,以及通过世界变换决定其位置的节点配对。网格实例不会自行渲染,只有加入渲染组件或图层后才会渲染。其网格和材质可以与其他网格实例共享,因此是否投射阴影等针对单次放置的设置保存在网格实例上。

创建网格实例​

使用网格、材质和可选的节点创建网格实例:

import { MeshInstance } from 'playcanvas';

const meshInstance = new MeshInstance(mesh, material, node);

随时可通过给 mesh 和 material 属性赋值,更改网格实例的网格和材质。

渲染网格实例​

网格实例有两种渲染方式:作为渲染组件的一部分,或直接添加到图层。

在渲染组件中​

将网格实例赋给渲染组件的 meshInstances。组件将网格实例注册到其 layers 列表中的各图层,默认包含 World 图层。相机的 layers 包含其中任一图层时,就会渲染它们。参见图层。

如下例所示,创建网格实例时不指定节点,以便由组件所在实体决定位置。如果网格实例已有节点,会保留该节点,由它而非实体决定位置:

import { BoxGeometry, Color, Entity, Mesh, MeshInstance, StandardMaterial } from 'playcanvas';

const mesh = Mesh.fromGeometry(app.graphicsDevice, new BoxGeometry());

const material = new StandardMaterial();
material.diffuse = new Color(1, 0.5, 0);
material.update();

const entity = new Entity('Box');
entity.addComponent('render', {
meshInstances: [new MeshInstance(mesh, material)]
});
app.root.addChild(entity);

网格实例属于渲染组件时,只要组件具有对应属性,就应在组件上修改这些设置:castShadows、receiveShadows、shadowCascadeMask、renderStyle、lightmapped 和 customAabb。组件会将它们传给每个网格实例。直接在单个实例上设置的值只持续到组件对应属性变化或网格实例重新赋值为止,此时会被覆盖。组件还通过 castShadows 将其网格实例注册为所属图层的阴影投射者。

在图层中​

网格实例也可直接添加到图层,无需实体或组件。此时由应用控制节点、图层归属和生命周期,并在网格实例本身设置下述所有属性。参见不使用实体进行渲染。

访问渲染组件的网格实例​

渲染组件会自行创建基础形状和加载模型的网格实例,可从 meshInstances 访问。加载模型中,网格的每个图元对应一个网格实例,各自具有材质:

for (const meshInstance of entity.render.meshInstances) {
meshInstance.material = highlightMaterial;
}

共享网格​

任意数量的网格实例可以使用同一网格,每个实例具有自己的节点和材质:

const red = new Entity('Red');
red.addComponent('render', { meshInstances: [new MeshInstance(mesh, redMaterial)] });

const blue = new Entity('Blue');
blue.addComponent('render', { meshInstances: [new MeshInstance(mesh, blueMaterial)] });

它们都渲染同一几何体,因此修改网格顶点会影响全部实例。参见共享网格。

销毁网格实例​

  • **渲染组件拥有赋给它的网格实例。**为 meshInstances 赋予不同数组、更改 type,或销毁组件及其实体时,这些实例会被销毁。之后不要再使用它们。
  • MeshInstance.destroy() 释放网格引用,当没有其他网格实例或渲染资源使用该网格时将其销毁,因此无需单独销毁网格。蒙皮和变形实例也会一起销毁。
  • **网格实例不会销毁其使用的材质。**自己创建的材质不再被使用后,应自行销毁。

可见性和剔除​

  • visible 可跳过网格实例的绘制,而不将其移出图层。要停止绘制整个渲染组件,应禁用组件或所在实体。
  • cull 控制启用视锥体剔除的相机,能否跳过视野之外的网格实例。默认值为 true。

剔除使用网格局部空间的包围盒 mesh.aabb。引擎通过节点的世界变换转换它,并将结果作为网格实例的世界空间包围盒 meshInstance.aabb 返回。如果着色器将顶点移出网格边界,例如风动动画,网格实例可能仍在视野内却被剔除。应扩大局部空间包围盒以覆盖运动范围:按创建网格所述设置 mesh.aabb,或在渲染组件上设置 customAabb,为其所有网格实例覆盖包围盒。

customAabb 对蒙皮网格尤其有用。未设置时,会每帧根据骨骼位置精确计算蒙皮网格实例的包围盒,开销较高。设置足以覆盖动画所有姿态的自定义包围盒,可以避免这项工作。

阴影​

  • castShadows 将网格实例纳入灯光的阴影贴图,使其投射阴影。
  • receiveShadows 使网格实例接收其他网格实例投射的阴影。
  • shadowCascadeMask 选择网格实例向平行光的哪些级联投射阴影,例如不让小对象进入远处级联。

自行将网格实例添加到图层时,应在添加前设置 castShadows,参见不使用实体进行渲染。配置灯光参见阴影。

排序顺序​

图层绘制网格实例的顺序取决于排序模式,参见图层。多个网格实例属性参与排序:

  • drawOrder 在采用 SORTMODE_MANUAL 的图层中直接设置顺序。
  • drawBucket 先对实例分组,再由其他排序模式在各组内排序。
  • calculateSortDistance 替换 SORTMODE_BACK2FRONT 和 SORTMODE_FRONT2BACK 使用的距离。默认距离是包围盒中心沿相机方向的深度。

对于包围盒中心不能准确反映排序关系的透明对象,例如相机可进入其内部的大型网格,自定义排序距离可以改善绘制顺序:

meshInstance.calculateSortDistance = (instance, cameraPosition, cameraForward) => {
// sort by the distance to the camera instead of the depth along its direction
return instance.aabb.center.distance(cameraPosition);
};

渲染样式​

renderStyle 将三角网格绘制为实心三角形(默认 RENDERSTYLE_SOLID)、边缘线框(RENDERSTYLE_WIREFRAME),或顶点位置的点(RENDERSTYLE_POINTS)。线框在首次需要时根据网格生成。

着色器参数​

setParameter() 为单个网格实例的绘制设置着色器 uniform 值。材质不必设置相同参数;如果设置了,网格实例上的值优先,因此实例可共享材质,同时在少量值上有所不同。下面为单个网格实例设置自定义着色器的 tint uniform:

meshInstance.setParameter('tint', [0, 1, 0]);

关于覆盖 StandardMaterial 属性及其开销,参见着色器参数。

蒙皮和变形​

带有 skin 或 morph 的网格按实例执行动画:skinInstance 保存决定姿态的骨骼,morphInstance 保存变形目标权重。对于加载模型中的蒙皮和变形网格,渲染组件会建立这些实例,由 anim 组件驱动。

网格变形

绘制多个副本​

每个网格实例通常对应一次绘制调用。要更高效地绘制同一网格的多个副本,参见:

  • 硬件实例化:通过单个网格实例绘制多个副本,每个副本可包含变换等独立数据。
  • 合批:将共享材质的网格实例合并为更少的绘制调用。
  • 多重绘制:在一次调用中,将单个网格实例的网格作为多个子绘制渲染。
  • 间接绘制:计算着色器在 GPU 上写入绘制参数。