Migrate ssao to shaderlab (#2903)

* refactor: support SAO in shaderlab
This commit is contained in:
zhuxudong
2026-02-28 10:42:38 +08:00
committed by GitHub
parent 42df0afe45
commit 0c91c29004
9 changed files with 92 additions and 21 deletions

View File

@@ -4,8 +4,10 @@
*/
import {
AmbientLight,
AmbientOcclusionQuality,
AssetType,
BackgroundMode,
BlendFactor,
Camera,
Color,
DirectLight,
@@ -13,20 +15,33 @@ import {
MeshRenderer,
PBRMaterial,
PrimitiveMesh,
RenderQueueType,
Shader,
SkyBoxMaterial,
AmbientOcclusionQuality,
Vector3,
WebGLEngine,
WebGLMode
} from "@galacean/engine";
import { PBRSource, registerIncludes } from "@galacean/engine-shader";
import { ShaderLab } from "@galacean/engine-shaderlab";
import { initScreenshot, updateForE2E } from "./.mockForE2E";
Logger.enable();
WebGLEngine.create({ canvas: "canvas", graphicDeviceOptions: { webGLMode: WebGLMode.WebGL1 } }).then((engine) => {
registerIncludes();
// Create engine
WebGLEngine.create({
canvas: "canvas",
shaderLab: new ShaderLab(),
graphicDeviceOptions: { webGLMode: WebGLMode.WebGL1 }
}).then((engine) => {
engine.canvas.resizeByClientSize(2);
const scene = engine.sceneManager.activeScene;
const rootEntity = scene.createRootEntity();
const { ambientLight, background } = scene;
const pbrShader = Shader.create(PBRSource);
// camera
const cameraEntity = rootEntity.createChild("camera_node");
@@ -34,46 +49,62 @@ WebGLEngine.create({ canvas: "canvas", graphicDeviceOptions: { webGLMode: WebGLM
const camera = cameraEntity.addComponent(Camera);
scene.ambientOcclusion.enabled = true;
// scene.ambientOcclusion.radius = 0.4;
// scene.ambientOcclusion.intensity = 3;
// scene.ambientOcclusion.power = 1.0;
// scene.ambientOcclusion.bias = 0.0005;
// scene.ambientOcclusion.bilateralThreshold = 0.01;
scene.ambientOcclusion.quality = AmbientOcclusionQuality.High;
const lightNode = rootEntity.createChild("light_node");
lightNode.addComponent(DirectLight).color = new Color(1, 1, 1);
lightNode.transform.rotate(new Vector3(-45, 60, 0));
const { background } = scene;
const sky = background.sky;
const skyMaterial = new SkyBoxMaterial(engine);
background.mode = BackgroundMode.Sky;
sky.material = skyMaterial;
sky.mesh = PrimitiveMesh.createCuboid(engine, 1, 1, 1);
// Sphere
const sphereMaterial = new PBRMaterial(engine);
sphereMaterial.baseColor = new Color(1, 1, 1, 1);
sphereMaterial.shader = pbrShader;
sphereMaterial.shaderData.setInt("depthWriteEnabled", 1);
const sphere = rootEntity.createChild("sphere");
const { transform } = sphere;
transform.setPosition(0, 1, 0);
transform.setRotation(45, 45, 0);
sphere.transform.setPosition(0, 1, 0);
sphere.transform.setRotation(45, 45, 0);
const meshRenderer = sphere.addComponent(MeshRenderer);
meshRenderer.mesh = PrimitiveMesh.createSubdivisionSurfaceSphere(engine);
meshRenderer.setMaterial(sphereMaterial);
const box = rootEntity.createChild("box");
// Box
const boxMaterial = new PBRMaterial(engine);
boxMaterial.baseColor = new Color(1, 1, 1, 1);
boxMaterial.shader = pbrShader;
boxMaterial.shaderData.setInt("depthWriteEnabled", 1);
const box = rootEntity.createChild("box");
box.transform.setPosition(1, 0.9, 0.1);
box.transform.setRotation(30, 30, 0);
const boxMeshRenderer = box.addComponent(MeshRenderer);
boxMeshRenderer.mesh = PrimitiveMesh.createCuboid(engine);
boxMeshRenderer.setMaterial(boxMaterial);
const capsule = rootEntity.createChild("capsule");
// Capsule (transparent)
const capsuleMaterial = new PBRMaterial(engine);
capsuleMaterial.isTransparent = true;
capsuleMaterial.baseColor = new Color(1, 1, 1, 0.5);
capsuleMaterial.isTransparent = true;
capsuleMaterial.shader = pbrShader;
{
const shaderData = capsuleMaterial.shaderData;
shaderData.setInt("depthWriteEnabled", 1);
shaderData.setInt("blendEnabled", 1);
shaderData.setInt("renderQueueType", RenderQueueType.Transparent);
shaderData.enableMacro("MATERIAL_IS_TRANSPARENT");
shaderData.setInt("sourceColorBlendFactor", BlendFactor.SourceAlpha);
shaderData.setInt("destinationColorBlendFactor", BlendFactor.OneMinusSourceAlpha);
shaderData.setInt("sourceAlphaBlendFactor", BlendFactor.One);
shaderData.setInt("destinationAlphaBlendFactor", BlendFactor.OneMinusSourceAlpha);
}
const capsule = rootEntity.createChild("capsule");
capsule.transform.setPosition(1, 0.9, 0.1);
capsule.transform.setRotation(30, 30, 0);
const capsuleMeshRenderer = capsule.addComponent(MeshRenderer);

View File

@@ -69,10 +69,26 @@ vec4 outputSRGBCorrection(vec4 linearIn){
vec4 camera_DepthBufferParams;
float remapDepthBufferLinear01(float z){
return 1.0/ (camera_DepthBufferParams.x * z + camera_DepthBufferParams.y);
float remapDepthBufferLinear01(float depth){
return 1.0 / (camera_DepthBufferParams.x * depth + camera_DepthBufferParams.y);
}
float remapDepthBufferEyeDepth(float depth){
#ifdef CAMERA_ORTHOGRAPHIC
return camera_ProjectionParams.y + (camera_ProjectionParams.z - camera_ProjectionParams.y) * depth;
#else
return 1.0 / (camera_DepthBufferParams.z * depth + camera_DepthBufferParams.w);
#endif
}
// From Next Generation Post Processing in Call of Duty: Advanced Warfare [Jimenez 2014]
// http://advances.realtimerendering.com/s2014/index.html
// sampleCoord must not be normalized (e.g. window coordinates)
float interleavedGradientNoise(vec2 sampleCoord)
{
const vec3 magic = vec3(0.06711056, 0.00583715, 52.9829189);
return fract(magic.z * fract(dot(sampleCoord, magic.xy)));
}
#ifdef GRAPHICS_API_WEBGL2
#define INVERSE_MAT(mat) inverse(mat)

View File

@@ -90,6 +90,7 @@ Shader "PBRShaderName" {
SubShader "Default" {
UsePass "pbr/Default/ShadowCaster"
UsePass "pbr/Default/DepthOnly"
Pass "Forward Pass" {
Tags { pipelineStage = "Forward"}

View File

@@ -26,6 +26,7 @@ struct SurfaceData{
// geometry
vec3 position;
vec4 positionCS;
vec3 normal;
#ifdef NEED_TANGENT
@@ -426,6 +427,17 @@ vec3 envBRDFApprox(vec3 f0, float f90, float roughness, float dotNV ) {
}
#endif
#ifdef SCENE_ENABLE_AMBIENT_OCCLUSION
sampler2D camera_AOTexture;
float evaluateAmbientOcclusion(vec2 uv){
#ifdef MATERIAL_IS_TRANSPARENT
return 1.0;
#else
return texture2D(camera_AOTexture, uv).r;
#endif
}
#endif
void initBSDFData(SurfaceData surfaceData, out BSDFData bsdfData){
vec3 albedoColor = surfaceData.albedoColor;
float metallic = surfaceData.metallic;
@@ -463,6 +475,11 @@ void initBSDFData(SurfaceData surfaceData, out BSDFData bsdfData){
bsdfData.diffuseAO = surfaceData.ambientOcclusion;
#ifdef SCENE_ENABLE_AMBIENT_OCCLUSION
float ambientAO = evaluateAmbientOcclusion((surfaceData.positionCS.xy / surfaceData.positionCS.w) * 0.5 + 0.5);
bsdfData.diffuseAO = min(bsdfData.diffuseAO, ambientAO);
#endif
#ifdef MATERIAL_ENABLE_CLEAR_COAT
bsdfData.clearCoatRoughness = max(MIN_PERCEPTUAL_ROUGHNESS, min(surfaceData.clearCoatRoughness + getAARoughnessFactor(surfaceData.clearCoatNormal), 1.0));
bsdfData.clearCoatSpecularColor = vec3(0.04);

View File

@@ -55,6 +55,8 @@ Varyings PBRVertex(Attributes attributes) {
gl_Position = renderer_MVPMat * vertexInputs.positionOS;
varyings.positionCS = gl_Position;
return varyings;
}

View File

@@ -174,6 +174,7 @@ SurfaceData getSurfaceData(Varyings v, vec2 aoUV, bool isFrontFacing){
// Geometry
surfaceData.position = v.positionWS;
surfaceData.positionCS = v.positionCS;
#ifdef CAMERA_ORTHOGRAPHIC
surfaceData.viewDir = -camera_Forward;

View File

@@ -45,10 +45,10 @@ vec3 getLightProbeRadiance(SurfaceData surfaceData, vec3 normal, float roughness
float evaluateSpecularOcclusion(float dotNV, float diffuseAO, float roughness){
float specularAOFactor = 1.0;
#if defined(MATERIAL_HAS_OCCLUSION_TEXTURE) && defined(SCENE_USE_SPECULAR_ENV)
#if (defined(MATERIAL_HAS_OCCLUSION_TEXTURE) || defined(SCENE_ENABLE_AMBIENT_OCCLUSION)) && defined(SCENE_USE_SPECULAR_ENV)
specularAOFactor = saturate( pow(dotNV + diffuseAO, exp2( - 16.0 * roughness - 1.0 ) ) - 1.0 + diffuseAO );
#endif
return specularAOFactor;
}
}
#endif

View File

@@ -29,6 +29,8 @@ struct Varyings{
#if defined(NEED_CALCULATE_SHADOWS) && (SCENE_SHADOW_CASCADED_COUNT == 1)
vec3 shadowCoord;
#endif
vec4 positionCS;
};

View File

@@ -34,17 +34,18 @@ describe("ShaderLab", async () => {
const shader = shaderLabVerbose._parseShaderSource(PBRSource);
const subShader = shader.subShaders[0];
const passList = subShader.passes;
const pass1 = passList[1];
const pass1 = passList[2];
// shader name
expect(shader.name).to.equal("PBRShaderName");
expect(subShader.name).to.equal("Default");
expect(pass1.name).to.equal("Forward Pass");
expect(passList.length).to.eq(2);
expect(passList.length).to.eq(3);
// Pass
expect(passList[0].isUsePass).to.be.true;
expect(passList[1].name).eq("Forward Pass");
expect(passList[1].isUsePass).to.be.true;
expect(passList[2].name).eq("Forward Pass");
// renderState
expect(pass1.renderStates).not.be.null;