mirror of
https://github.com/galacean/engine.git
synced 2026-05-08 15:57:13 +08:00
192 lines
5.8 KiB
TypeScript
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;
|
|
}
|