mirror of
https://github.com/galacean/engine.git
synced 2026-07-02 02:54:19 +08:00
187 lines
8.3 KiB
Plaintext
187 lines
8.3 KiB
Plaintext
---
|
||
order: 2
|
||
title: Buffer Mesh
|
||
type: 图形
|
||
group: 网格
|
||
label: Graphics/Mesh
|
||
---
|
||
|
||
[BufferMesh](/apis/core/#BufferMesh) 可以自由操作顶点缓冲和索引缓冲数据,以及一些与几何体绘制相关的指令。具备高效、灵活、简洁等特点。开发者如果想高效灵活的实现自定义几何体就可以使用该类。
|
||
|
||
## 原理图
|
||
|
||
我们先概览一下 `BufferMesh` 的原理图
|
||
|
||
<Image src="https://gw.alipayobjects.com/mdn/rms_7c464e/afts/img/A*piB3Q4501loAAAAAAAAAAAAAARQnAQ" />
|
||
|
||
`BufferMesh` 有三大核心元素分别是:
|
||
|
||
| 名称 | 解释 |
|
||
| :---------------------------------------------------- | :----------------------------------------------------------------------- |
|
||
| [VertexBufferBinding](/apis/core/#VertexBufferBinding) | 顶点缓冲绑定,用于将顶点缓冲和顶点跨度(字节)打包。 |
|
||
| [VertexElement](/apis/core/#VertexElement) | 顶点元素,用于描述顶点语义、顶点偏移、顶点格式和顶点缓冲绑定索引等信息。 |
|
||
| [IndexBufferBinding](/apis/core/#IndexBufferBinding) | 索引缓冲绑定(可选),用于将索引缓冲和索引格式打包。 |
|
||
|
||
其中 [IndexBufferBinding](/apis/core/#IndexBufferBinding) 为可选,也就是说必要核心元素只有两个,分别通过 [setVertexBufferBindings()](/apis/core/#BufferMesh-setVertexBufferBindings) 接口和 [setVertexElements()](/apis/core/#BufferMesh-setVertexElements) 接口设置。 最后一项就是通过 [addSubMesh](/apis/core/#BufferMesh-addSubMesh) 添加子 [SubMesh](/apis/core/#SubMesh),并设置顶点或索引绘制数量, [SubMesh](/apis/core/#SubMesh) 包含三个属性分别是起始绘制偏移、绘制数量、图元拓扑,并且开发者可以自行添加多个 [SubMesh](/apis/core/#SubMesh),每个子几何体均可对应独立的材质。
|
||
|
||
## 常用案例
|
||
|
||
这里列举几个 [MeshRenderer](/apis/core/#MeshRenderer) 和 [BufferMesh](/apis/core/#BufferMesh) 的常用使用场景,因为这个类的功能偏底层和灵活,所以这里给出了比较详细的代码。
|
||
|
||
### 交错顶点缓冲
|
||
|
||
常用方式,比如自定义 Mesh、Particle 等实现,具有显存紧凑,每帧 CPU 数据上传至 GPU 次数少等优势。这个案例的主要特点是多个 [VertexElement](/apis/core/#VertexElement) 对应一个 *VertexBuffer* ([Buffer](/apis/core/#Buffer)),仅使用一个 *VertexBuffer* 就可以将不同顶点元素与 Shader 关联。
|
||
|
||
```typescript
|
||
// add MeshRenderer component
|
||
const renderer = entity.addComponent(MeshRenderer);
|
||
|
||
// create mesh
|
||
const mesh = new BufferMesh(engine);
|
||
|
||
// create vertices.
|
||
const vertices = new ArrayBuffer(vertexByteCount);
|
||
|
||
// create vertexBuffer and upload vertices.
|
||
const vertexBuffer = new Buffer(engine, BufferBindFlag.VertexBuffer, vertices);
|
||
|
||
// bind vertexBuffer with stride, stride is every vertex byte length,so the value is 16.
|
||
mesh.setVertexBufferBinding(vertexBuffer, 16);
|
||
|
||
// add vertexElement to tell GPU how to read vertex from vertexBuffer.
|
||
mesh.setVertexElements([
|
||
new VertexElement("POSITION", 0, VertexElementFormat.Vector3, 0),
|
||
new VertexElement("COLOR", 12, VertexElementFormat.NormalizedUByte4, 0),
|
||
]);
|
||
|
||
// add one subMesh and set how many vertex you want to render.
|
||
mesh.addSubMesh(0, vertexCount);
|
||
|
||
// set mesh
|
||
renderer.mesh = mesh;
|
||
```
|
||
|
||
### 独立顶点缓冲
|
||
|
||
动态顶点 buffer 和静态顶点 buffer 混用时具有优势,比如 _position_ 为静态,但 _color_ 为动态,独立顶点缓冲可以仅更新颜色数据至 GPU。这个案例的主要特点是一个 [VertexElement](/apis/core/#VertexElement) 对应一个 _VertexBuffer_ ,可以分别调用 [Buffer](/apis/core/#Buffer) 对象的 [setData](/apis/core/#Buffer-setData) 方法独立更新数据。
|
||
|
||
```typescript
|
||
// add MeshRenderer component
|
||
const renderer = entity.addComponent(MeshRenderer);
|
||
|
||
// create mesh
|
||
const mesh = new BufferMesh(engine);
|
||
|
||
// create vertices.
|
||
const positions = new Float32Array(vertexCount);
|
||
const colors = new Uint8Array(vertexCount);
|
||
|
||
// create vertexBuffer and upload vertices.
|
||
const positionBuffer = new Buffer(
|
||
engine,
|
||
BufferBindFlag.VertexBuffer,
|
||
positions
|
||
);
|
||
const colorBuffer = new Buffer(engine, BufferBindFlag.VertexBuffer, colors);
|
||
|
||
// bind vertexBuffer with stride,stride is every vertex byte length,so the value is 12.
|
||
mesh.setVertexBufferBindings([
|
||
new VertexBufferBinding(positionBuffer, 12),
|
||
new VertexBufferBinding(colorBuffer, 4),
|
||
]);
|
||
|
||
// add vertexElement to tell GPU how to read vertex from vertexBuffer.
|
||
mesh.setVertexElements([
|
||
new VertexElement("POSITION", 0, VertexElementFormat.Vector3, 0),
|
||
new VertexElement("COLOR", 0, VertexElementFormat.NormalizedUByte4, 1),
|
||
]);
|
||
|
||
// add one subMesh and set how many vertex you want to render.
|
||
mesh.addSubMesh(0, vertexCount);
|
||
|
||
// set mesh
|
||
renderer.mesh = mesh;
|
||
```
|
||
|
||
### Instance 渲染
|
||
|
||
GPU Instance 渲染是三维引擎的常用技术,比如可以把相同几何体形状的物体一次性渲染到不同的位置,可以大幅提升渲染性能。这个案例的主要特点是使用了 [VertexElement](/apis/core/#VertexElement) 的实例功能,其构造函数的最后一个参数表示实例步频(在缓冲中每前进一个顶点绘制的实例数量,非实例元素必须为 0),[BufferMesh](/apis/core/#BufferMesh) 的 [instanceCount](/apis/core/#BufferMesh-instanceCount) 表示实例数量。
|
||
|
||
```typescript
|
||
// add MeshRenderer component
|
||
const renderer = entity.addComponent(MeshRenderer);
|
||
|
||
// create mesh
|
||
const mesh = new BufferMesh(engine);
|
||
|
||
// create vertices.
|
||
const vertices = new ArrayBuffer( vertexByteLength );
|
||
|
||
// create instance data.
|
||
const instances = new Float32Array( instanceDataLength );
|
||
|
||
// create vertexBuffer and upload vertex data.
|
||
const vertexBuffer = new Buffer( engine, BufferBindFlag.VertexBuffer, vertices );
|
||
|
||
// create instance buffer and upload instance data.
|
||
const instanceBuffer = new Buffer( engine, BufferBindFlag.VertexBuffer, instances );
|
||
|
||
// bind vertexBuffer with stride, stride is every vertex byte length,so the value is 16.
|
||
mesh.setVertexBufferBindings([new VertexBufferBinding( vertexBuffer, 16 ),
|
||
new VertexBufferBinding( instanceBuffer, 12 )]);
|
||
|
||
// add vertexElement to tell GPU how to read vertex from vertexBuffer.
|
||
mesh.setVertexElements([new VertexElement( "POSITION", 0, VertexElementFormat.Vector3, 0 ),
|
||
new VertexElement( "COLOR", 12, VertexElementFormat.NormalizedUByte4, 0 ),
|
||
new VertexElement( "INSTANCE_OFFSET", 0, VertexElementFormat.Vector3, 1 , 1 ),
|
||
new VertexElement( "INSTANCE_ROTATION", 12, VertexElementFormat.Vector3, 1 , 1 )]]);
|
||
|
||
// add one sub mesh and set how many vertex you want to render, here is full vertexCount.
|
||
mesh.addSubMesh(0, vertexCount);
|
||
|
||
// set mesh
|
||
renderer.mesh = mesh;
|
||
```
|
||
|
||
## 索引缓冲
|
||
|
||
使用索引缓冲可以复用顶点缓冲内的顶点,从而达到节省显存的目的。其使用方式很简单,就是在原基础上增加了索引缓冲对象,以下代码是在第一个 **交错顶点缓冲** 案例的基础上修改而来的
|
||
|
||
```typescript
|
||
// add MeshRenderer component
|
||
const renderer = entity.addComponent(MeshRenderer);
|
||
|
||
// create mesh
|
||
const mesh = new BufferMesh(engine);
|
||
|
||
// create vertices.
|
||
const vertices = new ArrayBuffer(vertexByteCount);
|
||
|
||
// create indices.
|
||
const indices = new Uint16Array(indexCount);
|
||
|
||
// create vertexBuffer and upload vertices.
|
||
const vertexBuffer = new Buffer(engine, BufferBindFlag.VertexBuffer, vertices);
|
||
|
||
// create indexBuffer and upload indices.
|
||
const indexBuffer = new Buffer(engine, BufferBindFlag.IndexBuffer, indices);
|
||
|
||
// bind vertexBuffer with stride, stride is every vertex byte length,so the value is 16.
|
||
mesh.setVertexBufferBinding(vertexBuffer, 16);
|
||
|
||
// bind vertexBuffer with format.
|
||
mesh.setIndexBufferBinding(indexBuffer, IndexFormat.UInt16);
|
||
|
||
// add vertexElement to tell GPU how to read vertex from vertexBuffer.
|
||
mesh.setVertexElements([
|
||
new VertexElement("POSITION", 0, VertexElementFormat.Vector3, 0),
|
||
new VertexElement("COLOR", 12, VertexElementFormat.NormalizedUByte4, 0),
|
||
]);
|
||
|
||
// add one subMesh and set how many vertex you want to render.
|
||
mesh.addSubMesh(0, vertexCount);
|
||
|
||
// set mesh
|
||
renderer.mesh = mesh;
|
||
```
|