mirror of
https://github.com/galacean/engine.git
synced 2026-05-23 01:40:11 +08:00
feat: add GPU instancing e2e tests and complete matrix uniform support
- Add e2e cases for gpu-instancing-auto-batch and gpu-instancing-custom-data - Add all missing matrix uniform types (mat2, mat3, mat2x3, mat2x4, mat3x2, mat3x4, mat4x2, mat4x3) to ShaderUniform and ShaderProgram - Skip UBO members (location === null) in uniform reflection - Restore throw for truly unsupported uniform types in default branch
This commit is contained in:
68
e2e/case/gpu-instancing-auto-batch.ts
Normal file
68
e2e/case/gpu-instancing-auto-batch.ts
Normal file
@@ -0,0 +1,68 @@
|
||||
/**
|
||||
* @title GPU Instancing Auto Batch
|
||||
* @category Mesh
|
||||
*/
|
||||
import {
|
||||
AmbientLight,
|
||||
AssetType,
|
||||
Camera,
|
||||
Color,
|
||||
DirectLight,
|
||||
GLTFResource,
|
||||
Logger,
|
||||
Vector3,
|
||||
WebGLEngine
|
||||
} from "@galacean/engine";
|
||||
import { initScreenshot, updateForE2E } from "./.mockForE2E";
|
||||
|
||||
Logger.enable();
|
||||
WebGLEngine.create({ canvas: "canvas" }).then(async (engine) => {
|
||||
engine.canvas.resizeByClientSize(2);
|
||||
|
||||
const scene = engine.sceneManager.activeScene;
|
||||
const rootEntity = scene.createRootEntity("Root");
|
||||
|
||||
// Camera
|
||||
const cameraEntity = rootEntity.createChild("Camera");
|
||||
cameraEntity.transform.setPosition(0, 10, 80);
|
||||
cameraEntity.transform.lookAt(new Vector3(0, 0, 0));
|
||||
const camera = cameraEntity.addComponent(Camera);
|
||||
camera.farClipPlane = 300;
|
||||
|
||||
// Light
|
||||
const lightEntity = rootEntity.createChild("Light");
|
||||
lightEntity.transform.setRotation(-45, -45, 0);
|
||||
lightEntity.addComponent(DirectLight).color = new Color(1, 1, 1, 1);
|
||||
|
||||
// Load Duck model and ambient light
|
||||
const [glTF, ambientLight] = await Promise.all([
|
||||
engine.resourceManager.load<GLTFResource>({
|
||||
url: "https://gw.alipayobjects.com/os/bmw-prod/6cb8f543-285c-491a-8cfd-57a1160dc9ab.glb",
|
||||
type: AssetType.GLTF
|
||||
}),
|
||||
engine.resourceManager.load<AmbientLight>({
|
||||
url: "https://mdn.alipayobjects.com/oasis_be/afts/file/A*eRJ8QKzf5zAAAAAAgBAAAAgAekp5AQ/ambient.ambLight",
|
||||
type: AssetType.AmbientLight
|
||||
})
|
||||
]);
|
||||
scene.ambientLight = ambientLight;
|
||||
|
||||
// Clone ducks with fixed seed positions for deterministic output
|
||||
const count = 500;
|
||||
const spread = 50;
|
||||
for (let i = 0; i < count; i++) {
|
||||
const duck = glTF.instantiateSceneRoot();
|
||||
// Use deterministic positions based on index
|
||||
const t = i / count;
|
||||
duck.transform.setPosition(
|
||||
Math.sin(t * 137.5) * spread * 0.5,
|
||||
Math.cos(t * 97.3) * spread * 0.5,
|
||||
Math.sin(t * 59.1) * spread * 0.5
|
||||
);
|
||||
duck.transform.setRotation(t * 360, t * 720, t * 1080);
|
||||
rootEntity.addChild(duck);
|
||||
}
|
||||
|
||||
updateForE2E(engine);
|
||||
initScreenshot(engine, camera);
|
||||
});
|
||||
100
e2e/case/gpu-instancing-custom-data.ts
Normal file
100
e2e/case/gpu-instancing-custom-data.ts
Normal file
@@ -0,0 +1,100 @@
|
||||
/**
|
||||
* @title GPU Instancing Custom Data
|
||||
* @category Mesh
|
||||
*/
|
||||
import {
|
||||
Camera,
|
||||
Color,
|
||||
DirectLight,
|
||||
Logger,
|
||||
Material,
|
||||
MeshRenderer,
|
||||
PrimitiveMesh,
|
||||
Shader,
|
||||
ShaderProperty,
|
||||
Vector3,
|
||||
Vector4,
|
||||
WebGLEngine
|
||||
} from "@galacean/engine";
|
||||
import { initScreenshot, updateForE2E } from "./.mockForE2E";
|
||||
|
||||
Logger.enable();
|
||||
|
||||
// Custom shader: uses renderer_CustomColor (per-instance) for fragment output
|
||||
Shader.create(
|
||||
"CustomInstanceShader",
|
||||
`
|
||||
#include <transform_declare>
|
||||
attribute vec3 POSITION;
|
||||
attribute vec3 NORMAL;
|
||||
|
||||
varying vec3 v_normal;
|
||||
|
||||
void main() {
|
||||
gl_Position = renderer_MVPMat * vec4(POSITION, 1.0);
|
||||
v_normal = normalize((renderer_NormalMat * vec4(NORMAL, 0.0)).xyz);
|
||||
}
|
||||
`,
|
||||
`
|
||||
uniform vec4 renderer_CustomColor;
|
||||
|
||||
varying vec3 v_normal;
|
||||
|
||||
void main() {
|
||||
vec3 lightDir = normalize(vec3(1.0, 1.0, 1.0));
|
||||
float NdotL = max(dot(v_normal, lightDir), 0.2);
|
||||
gl_FragColor = vec4(renderer_CustomColor.rgb * NdotL, 1.0);
|
||||
}
|
||||
`
|
||||
);
|
||||
|
||||
WebGLEngine.create({ canvas: "canvas" }).then((engine) => {
|
||||
engine.canvas.resizeByClientSize(2);
|
||||
|
||||
const scene = engine.sceneManager.activeScene;
|
||||
const rootEntity = scene.createRootEntity("Root");
|
||||
|
||||
// Camera
|
||||
const cameraEntity = rootEntity.createChild("Camera");
|
||||
cameraEntity.transform.setPosition(0, 10, 80);
|
||||
cameraEntity.transform.lookAt(new Vector3(0, 0, 0));
|
||||
const camera = cameraEntity.addComponent(Camera);
|
||||
camera.farClipPlane = 300;
|
||||
|
||||
// Light
|
||||
const lightEntity = rootEntity.createChild("Light");
|
||||
lightEntity.transform.setRotation(-45, -45, 0);
|
||||
lightEntity.addComponent(DirectLight).color = new Color(1, 1, 1, 1);
|
||||
|
||||
// Shared mesh and material
|
||||
const mesh = PrimitiveMesh.createCuboid(engine, 1, 1, 1);
|
||||
const material = new Material(engine, Shader.find("CustomInstanceShader"));
|
||||
const customColorProperty = ShaderProperty.getByName("renderer_CustomColor");
|
||||
|
||||
// Create cubes with deterministic positions and colors
|
||||
const count = 500;
|
||||
const spread = 50;
|
||||
for (let i = 0; i < count; i++) {
|
||||
const entity = rootEntity.createChild("Cube" + i);
|
||||
const t = i / count;
|
||||
entity.transform.setPosition(
|
||||
Math.sin(t * 137.5) * spread * 0.5,
|
||||
Math.cos(t * 97.3) * spread * 0.5,
|
||||
Math.sin(t * 59.1) * spread * 0.5
|
||||
);
|
||||
entity.transform.setRotation(t * 360, t * 720, t * 1080);
|
||||
|
||||
const renderer = entity.addComponent(MeshRenderer);
|
||||
renderer.mesh = mesh;
|
||||
renderer.setMaterial(material);
|
||||
|
||||
// Deterministic colors based on index
|
||||
renderer.shaderData.setVector4(
|
||||
customColorProperty,
|
||||
new Vector4(Math.sin(t * 6.28) * 0.5 + 0.5, Math.cos(t * 4.71) * 0.5 + 0.5, Math.sin(t * 3.14) * 0.5 + 0.5, 1.0)
|
||||
);
|
||||
}
|
||||
|
||||
updateForE2E(engine);
|
||||
initScreenshot(engine, camera);
|
||||
});
|
||||
@@ -135,7 +135,7 @@ export const E2E_CONFIG = {
|
||||
category: "Material",
|
||||
caseFileName: "material-pbr",
|
||||
threshold: 0,
|
||||
diffPercentage: 0.0080
|
||||
diffPercentage: 0.008
|
||||
},
|
||||
shaderLab: {
|
||||
category: "Material",
|
||||
@@ -464,6 +464,20 @@ export const E2E_CONFIG = {
|
||||
diffPercentage: 0.03
|
||||
}
|
||||
},
|
||||
GPUInstancing: {
|
||||
autoBatch: {
|
||||
category: "GPUInstancing",
|
||||
caseFileName: "gpu-instancing-auto-batch",
|
||||
threshold: 0,
|
||||
diffPercentage: 0
|
||||
},
|
||||
customData: {
|
||||
category: "GPUInstancing",
|
||||
caseFileName: "gpu-instancing-custom-data",
|
||||
threshold: 0,
|
||||
diffPercentage: 0
|
||||
}
|
||||
},
|
||||
SpriteMask: {
|
||||
CustomStencil: {
|
||||
category: "SpriteMask",
|
||||
|
||||
@@ -0,0 +1,3 @@
|
||||
version https://git-lfs.github.com/spec/v1
|
||||
oid sha256:f5424ba406ffe08f68ecfb28174027deb171efff7d55d8ca1be63caaea3bf898
|
||||
size 590077
|
||||
@@ -0,0 +1,3 @@
|
||||
version https://git-lfs.github.com/spec/v1
|
||||
oid sha256:baece2c4437803e1474699eb4e99214b38caa05393f747fe0f7e47bd6469a693
|
||||
size 427005
|
||||
Reference in New Issue
Block a user