Files
engine/examples/buffer-mesh-instance.ts
2024-07-04 18:10:43 +08:00

192 lines
5.8 KiB
TypeScript

/**
* @title Buffer Mesh Instance
* @category Mesh
* @thumbnail https://mdn.alipayobjects.com/merchant_appfe/afts/img/A*jjZMTrp-vU8AAAAAAAAAAAAADiR2AQ/original
*/
import { OrbitControl } from "@galacean/engine-toolkit-controls";
import {
Buffer,
BufferBindFlag,
BufferMesh,
BufferUsage,
Camera,
Engine,
IndexFormat,
Material,
Mesh,
MeshRenderer,
Shader,
Vector3,
VertexElement,
VertexElementFormat,
WebGLEngine,
} from "@galacean/engine";
// Create engine
WebGLEngine.create({ canvas: "canvas" }).then((engine) => {
engine.canvas.resizeByClientSize();
// Get scene and root entity
const scene = engine.sceneManager.activeScene;
const rootEntity = scene.createRootEntity("Root");
// Init instance shader
const shader = initCustomShader();
// Create camera
const cameraEntity = rootEntity.createChild("Camera");
const camera = cameraEntity.addComponent(Camera);
cameraEntity.addComponent(OrbitControl);
cameraEntity.transform.setPosition(0, 10, 160);
cameraEntity.transform.lookAt(new Vector3(0, 0, 0));
camera.farClipPlane = 300;
// Create Instance Cube
const cubeEntity = rootEntity.createChild("Cube");
const cubeRenderer = cubeEntity.addComponent(MeshRenderer);
const material = new Material(engine, shader);
cubeEntity.transform.rotate(0, 60, 0);
cubeRenderer.mesh = createCustomMesh(engine, 1.0); // Use `createCustomMesh()` to create custom instance cube mesh.
cubeRenderer.setMaterial(material);
// Run engine.
engine.run();
});
/**
* Create cube geometry with custom BufferGeometry.
* @param engine - Engine
* @param size - Cube size
* @returns Cube mesh
*/
function createCustomMesh(engine: Engine, size: number): Mesh {
const geometry = new BufferMesh(engine, "CustomCubeGeometry");
// Create vertices data.
// prettier-ignore
const vertices: Float32Array = new Float32Array([
// Up
-size, size, -size, 0, 1, 0, size, size, -size, 0, 1, 0, size, size, size, 0, 1, 0, -size, size, size, 0, 1, 0,
// Down
-size, -size, -size, 0, -1, 0, size, -size, -size, 0, -1, 0, size, -size, size, 0, -1, 0, -size, -size, size, 0, -1, 0,
// Left
-size, size, -size, -1, 0, 0, -size, size, size, -1, 0, 0, -size, -size, size, -1, 0, 0, -size, -size, -size, -1, 0, 0,
// Right
size, size, -size, 1, 0, 0, size, size, size, 1, 0, 0, size, -size, size, 1, 0, 0, size, -size, -size, 1, 0, 0,
// Front
-size, size, size, 0, 0, 1, size, size, size, 0, 0, 1, size, -size, size, 0, 0, 1, -size, -size, size, 0, 0, 1,
// Back
-size, size, -size, 0, 0, -1, size, size, -size, 0, 0, -1, size, -size, -size, 0, 0, -1, -size, -size, -size, 0, 0, -1]);
// Create instance data.
const instanceCount = 4000;
const instanceStride = 6;
const instanceData: Float32Array = new Float32Array(
instanceCount * instanceStride
);
for (let i = 0; i < instanceCount; i++) {
const offset = i * instanceStride;
// instance offset
instanceData[offset] = (Math.random() - 0.5) * 60;
instanceData[offset + 1] = (Math.random() - 0.5) * 60;
instanceData[offset + 2] = (Math.random() - 0.5) * 60;
// instance color
instanceData[offset + 3] = Math.random();
instanceData[offset + 4] = Math.random();
instanceData[offset + 5] = Math.random();
}
// Create indices data.
// prettier-ignore
const indices: Uint16Array = new Uint16Array([
// Up
0, 2, 1, 2, 0, 3,
// Down
4, 6, 7, 6, 4, 5,
// Left
8, 10, 9, 10, 8, 11,
// Right
12, 14, 15, 14, 12, 13,
// Front
16, 18, 17, 18, 16, 19,
// Back
20, 22, 23, 22, 20, 21]);
// Create gpu vertex buffer and index buffer.
const vertexBuffer = new Buffer(
engine,
BufferBindFlag.VertexBuffer,
vertices,
BufferUsage.Static
);
const instanceVertexBuffer = new Buffer(
engine,
BufferBindFlag.VertexBuffer,
instanceData,
BufferUsage.Static
);
const indexBuffer = new Buffer(
engine,
BufferBindFlag.IndexBuffer,
indices,
BufferUsage.Static
);
// Bind buffer
geometry.setVertexBufferBinding(vertexBuffer, 24, 0);
geometry.setVertexBufferBinding(instanceVertexBuffer, 24, 1);
geometry.setIndexBufferBinding(indexBuffer, IndexFormat.UInt16);
// Add vertexElements
geometry.setVertexElements([
new VertexElement("POSITION", 0, VertexElementFormat.Vector3, 0, 0), // Bind to VertexBuffer 0
new VertexElement("NORMAL", 12, VertexElementFormat.Vector3, 0, 0), // Bind to VertexBuffer 0
new VertexElement("INSTANCE_OFFSET", 0, VertexElementFormat.Vector3, 1, 1), // Bind instance offset to VertexBuffer 1, and enable instance by set instanceStepRate with 1
new VertexElement("INSTANCE_COLOR", 12, VertexElementFormat.Vector3, 1, 1), // Bind instance color to VertexBuffer 1, and enable instance by set instanceStepRate with 1
]);
// Add one sub geometry.
geometry.addSubMesh(0, indices.length);
geometry.instanceCount = instanceCount;
return geometry;
}
/**
* Create custom instance shader.
*/
function initCustomShader(): Shader {
const shader = Shader.create(
"CustomShader",
`uniform mat4 renderer_MVPMat;
attribute vec4 POSITION;
attribute vec3 INSTANCE_OFFSET;
attribute vec3 INSTANCE_COLOR;
uniform mat4 renderer_MVMat;
varying vec3 v_position;
varying vec3 v_color;
void main() {
vec4 position = POSITION;
position.xyz += INSTANCE_OFFSET;
gl_Position = renderer_MVPMat * position;
v_color = INSTANCE_COLOR;
}`,
`
varying vec3 v_color;
uniform vec4 u_color;
void main() {
vec4 color = vec4(v_color,1.0);
gl_FragColor = color;
}
`
);
return shader;
}