galacean / engine-toolkit

Some out-of-the-box utility features based on the Galacean engine.
https://oasisengine.cn
MIT License
72 stars 25 forks source link

feat(DrawCallHook):hook functions of `drawElementsInstanced` and `drawArraysInstanced` #266

Closed jtabibito closed 3 months ago

jtabibito commented 4 months ago

What is the new behavior (if this is a feature change)?

Before: 截屏2024-03-06 18 27 48 Now: 截屏2024-03-06 18 27 29

Example:

`

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";
import { Stats } from "@galacean/engine-toolkit-stats";
import { ShaderLab } from "@galacean/engine-shader-lab";

// Create engine
WebGLEngine.create({ canvas: "canvas", shaderLab: new ShaderLab() }).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 = initCustomShaderByShaderLab();

  // Create camera
  const cameraEntity = rootEntity.createChild("Camera");
  const camera = cameraEntity.addComponent(Camera);
  cameraEntity.addComponent(OrbitControl);
  cameraEntity.addComponent(Stats);
  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, 5.0); // Use 'createCustomMesh()' to create custom instance cube mesh.
  cubeRenderer.setMaterial(material);

  // Run engine.
  engine.run();
});

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 = 1;
  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;
}

function initCustomShaderByShaderLab(): Shader {
  const shader = '
    Shader "Custom" {
      SubShader "default" {
        Pass "0" {
          VertexShader = vert;
          FragmentShader = frag;

          mat4 renderer_MVPMat;
          mat4 renderer_MVMat;

          struct a2v {
            vec4 POSITION;
            vec3 INSTANCE_OFFSET;
            vec3 INSTANCE_COLOR;
          }

          struct v2f {
            vec3 v_color;
          }

          v2f vert(a2v v) {
            vec4 position = v.POSITION;
            position.xyz += v.INSTANCE_OFFSET;
            gl_Position = renderer_MVPMat * position;

            v2f o;
            o.v_color = v.INSTANCE_COLOR;
            return o;
          }

          void frag(v2f i) {
            vec4 color = vec4(i.v_color,1.0);
            gl_FragColor = color;
          }
        }
        Pass "1" {
          VertexShader = vert;
          FragmentShader = frag;

          mat4 renderer_MVPMat;
          mat4 renderer_MVMat;

          struct a2v {
            vec4 POSITION;
            vec3 INSTANCE_OFFSET;
            vec3 INSTANCE_COLOR;
          }

          struct v2f {
            vec3 v_color;
          }

          v2f vert(a2v v) {
            vec4 position = v.POSITION;
            position.xyz += v.INSTANCE_OFFSET;
            position += vec4(100.0, 3.0, 0.0, 0.0);
            gl_Position = renderer_MVPMat * position;

            v2f o;
            o.v_color = v.INSTANCE_COLOR;
            return o;
          }

          void frag(v2f i) {
            vec4 color = vec4(i.v_color,1.0);
            gl_FragColor = color;
          }
        }
      }
    }
  ';
  return Shader.create(shader);
}

`

codecov[bot] commented 3 months ago

Codecov Report

All modified and coverable lines are covered by tests :white_check_mark:

Project coverage is 17.14%. Comparing base (d173cd1) to head (115a01d).

Additional details and impacted files ```diff @@ Coverage Diff @@ ## main #266 +/- ## ======================================= Coverage 17.14% 17.14% ======================================= Files 16 16 Lines 665 665 Branches 90 90 ======================================= Hits 114 114 Misses 547 547 Partials 4 4 ```

:umbrella: View full report in Codecov by Sentry.
:loudspeaker: Have feedback on the report? Share it here.