自定义渲染管线球面光阴影多光源以及阴影图集支持

demo
瞄一眼原来聚光阴影的渲染 _addForwardSingleRadiancePass

 public addLightQueues(pass: rendering.BasicRenderPassBuilder,
        camera: renderer.scene.Camera, maxNumShadowMaps: number): void {
        this._addLightQueues(camera, pass);
        let i = 0;
        for (const light of this.shadowEnabledSpotLights) {
            // Add spot-light pass
            // Save last RenderPass to the `pass` variable
            // TODO(zhouzhenglong): Fix per queue addTexture
            pass.addTexture(`cc_spotShadowMap${i}`, 'cc_spotShadowMap');//
            const queue = pass.addQueue(rendering.QueueHint.BLEND, 'forward-add');
            queue.addScene(camera, rendering.SceneFlags.BLEND, light);
            ++i;
            if (i >= maxNumShadowMaps) {
                break;
            }
        }
    }

好像大概是这样 只有一个pass 执行了多次 pass.addTexture那只能这个阴影图绑定只对最后一次是有效的 相当于覆盖了, maxNumShadowMaps是1,那好像也没毛病,他_addForwardMultipleRadiancePasses,里面的聚光阴影图渲染到了渲染到了平行光阴影图里面去?原来的代码没保留,好像多个灯光还会触发合并bug,他这样搞的话DAG好像有点乱了,
直接构建简单的单向依赖逻辑就行,参考他原来的addLightQueues用多pass来处理
阴影贴图渲染

public addSpotlightShadowPasses(
        ppl: rendering.BasicPipeline,
        camera: renderer.scene.Camera,
        maxNumShadowMaps: number,
        cameraConfigs: Readonly<CameraConfigs & ForwardPassConfigs>
    ): void {
        const atlasSize = cameraConfigs.spotShadowAtlasSize;
        const shadowSize = cameraConfigs.spotShadowMapSize;
        let i = 0;
        for (const light of this.shadowEnabledSpotLights) {
            const uuid = light.node!.uuid;
            const view = ShadowAtlasManager.inst.getShadowView(uuid, 'SpotShadowMap', shadowSize);
            if (!view) break;
            const shadowPass = ppl.addRenderPass(atlasSize, atlasSize, 'default');
            shadowPass.name = `SpotLightShadow_${uuid}`;
            shadowPass.addRenderTarget(view.atlasName, LoadOp.CLEAR, StoreOp.STORE, new Color(1, 1, 1, 1));
            shadowPass.addDepthStencil(`Depth${view.atlasName}`,  LoadOp.CLEAR, StoreOp.STORE,1);
            shadowPass.setViewport(new Viewport(view.view.x, view.view.y, view.view.width, view.view.height));

           
            shadowPass.addQueue(rendering.QueueHint.NONE, 'shadow-caster')
                .addScene(camera, rendering.SceneFlags.OPAQUE | rendering.SceneFlags.MASK | rendering.SceneFlags.SHADOW_CASTER,light);
            ++i;
        }
    }

向前阶段渲染

 for (const light of this.shadowEnabledSpotLights) {
            const uuid = light.node!.uuid;
            const shadowSize =cameraConfigs.spotShadowMapSize ;
            const view = ShadowAtlasManager.inst.getShadowView(uuid, 'SpotShadowMap', shadowSize);
            if (!view) break;

            let _pass = ppl.addRenderPass(width, height, 'default');
            _pass.name = 'SpotlightWithShadowMap';
            _pass.setViewport(viewport);
            _pass.addRenderTarget(colorName, LoadOp.LOAD,StoreOp.STORE);
            _pass.addDepthStencil(depthStencilName, LoadOp.LOAD, StoreOp.STORE);
            _pass.addTexture(view.atlasName, 'cc_spotShadowMap');
            _pass.setVec4('custom_atlasUV', view.uv);
          
            const queue = _pass.addQueue(rendering.QueueHint.BLEND, 'forward-add');
            queue.addScene(
                camera,
                rendering.SceneFlags.BLEND,
                light,
            );
           
           shadowPassCount++;
           if(shadowPassCount === totalShadowPasses){
            pass=_pass;
           }
        }

球面光也是类似的处理,还有就是 他原来的相机配置用的单例,编辑器会有多个相机会覆盖配置改成map,这样就不会出各种奇怪的问题了
效果:


shder:

// Effect Syntax Guide: https://docs.cocos.com/creator/manual/zh/shader/index.html

CCEffect %{
  techniques:
  - name: opaque
    passes:
    - vert: vs-main:vert
      frag: fs-main:frag
      depthStencilState:
        depthFunc: less_equal
        depthTest: true
        depthWrite: true
      properties:
        _Color: { value: [1, 1, 1, 1], editor: { type: color } }
    - vert: vs-other:vert
      frag: fs-other:frag
      phase: forward-add
      depthStencilState:
        depthFunc: less_equal
        depthTest: true
        depthWrite: false
      blendState:
        targets:
        - blend: true
          blendSrc: one
          blendDst: one
      properties:
        _Color: { value: [1, 1, 1, 1], editor: { type: color } }
    - &shadow-caster
      vert: shadow-caster-vs:vert
      frag: shadow-caster-fs:frag
      phase: shadow-caster
      rasterizerState:
        cullMode: front
}%

CCProgram vs-main %{
  precision highp float;
  #include <builtin/uniforms/cc-global>
  #include <builtin/uniforms/cc-local>

  in vec3 a_position;
  in vec3 a_normal;

  out vec3 v_worldNormal;
  out vec3 v_worldPos;

  vec4 vert () {
    vec4 vertex = vec4(a_position, 1.0);
    v_worldPos = (cc_matWorld * vertex).xyz;
    v_worldNormal = mat3(cc_matWorldIT) * a_normal;
    return cc_matProj * cc_matView * vec4(v_worldPos, 1.0);
  }
}%

CCProgram fs-main %{
  precision highp float;
  #include <legacy/output>
  #include <builtin/uniforms/cc-global>
  #include <builtin/uniforms/cc-shadow>
  #include <builtin/uniforms/cc-shadow-map>
  #pragma define CC_RECEIVE_SHADOW 1
  #include <builtin/functionalities/shadow-map>

  in vec3 v_worldNormal;
  in vec3 v_worldPos;

  uniform ConstantF {
    vec4 _Color;
  };

  vec4 frag () {
    vec3 worldNormal = normalize(v_worldNormal);
    vec3 worldLightDir = normalize(-cc_mainLitDir.xyz);
    float NdotL = max(dot(worldNormal, worldLightDir), 0.0);
    // 主平行光阴影(CSM级联自动选择)
    float shadow = 1.0;
    if (NdotL > 0.0) {
      shadow = CCCSMFactorBase(v_worldPos.xyz, worldNormal.xyz, vec2(0.0));
    }
    vec3 diffuse = cc_mainLitColor.rgb * _Color.rgb * NdotL *shadow;
    vec3 ambient = cc_ambientSky.rgb * _Color.rgb;
    // return CCFragOutput(vec4(0.,0.,0.,1.));
    return CCFragOutput(vec4(ambient + diffuse, 1.));
  }
}%

CCProgram vs-other %{
  precision highp float;
  #include <builtin/uniforms/cc-global>
  #include <builtin/uniforms/cc-local>

  in vec3 a_position;
  in vec3 a_normal;

  out vec3 v_worldNormal;
  out vec3 v_worldPos;

  vec4 vert () {
    vec4 vertex = vec4(a_position, 1.0);
    v_worldPos = (cc_matWorld * vertex).xyz;
    v_worldNormal = mat3(cc_matWorldIT) * a_normal;
    return cc_matProj * cc_matView * vec4(v_worldPos, 1.0);
  }
}%

CCProgram fs-other %{
  precision highp float;
  #include <legacy/output>
  #include <builtin/uniforms/cc-global>
  #include <builtin/uniforms/cc-forward-light>
  #include <builtin/uniforms/cc-shadow>
  #include <builtin/uniforms/cc-shadow-map>
  #include <builtin/functionalities/shadow-map>

  in vec3 v_worldNormal;
  in vec3 v_worldPos;
  #pragma rate custom_rangedShadowMap pass
  uniform highp sampler2D custom_rangedShadowMap;
  #pragma rate RangedShadowInfo pass
  uniform RangedShadowInfo {
    vec4 custom_atlasUV;
    vec4 custom_rangedShadowInfo;
  };
  #pragma rate custom_sphereShadowMap pass
  uniform highp sampler2D custom_sphereShadowMap;
  #pragma rate SphereShadowInfo pass
  uniform SphereShadowInfo {
    vec4 custom_sphereShadowInfo;
    mat4 custom_sphereFaceVP[6];
    vec4 custom_sphereFaceUV[6];
  };



 


  // #pragma rate cf pass
  // uniform ConstantF {
  //   mat custm_matLightViewProj;
  // };
  uniform ConstantF {
    vec4 _Color;
  };



float unpackDepthFromRGBA(vec4 rgba) {
    return dot(rgba, vec4(1.0, 1.0/255.0, 1.0/65025.0, 1.0/16581375.0));
}
float readShadowDepth(sampler2D map, vec2 uv) {
  #if CC_SHADOWMAP_FORMAT == SHADOWMAP_FORMAT_RGBA8
    return unpackDepthFromRGBA(texture(map, uv));
  #else
    return texture(map, uv).r;
  #endif
}

float sampleShadowPCF(sampler2D shadowMap, vec2 uv, float depth, float texelSize) {
    float s = 0.0;
    float r = texelSize * 2.0;
    s += step(depth, texture(shadowMap, uv + vec2(-r, -r)).r);
    s += step(depth, texture(shadowMap, uv + vec2( r, -r)).r);
    s += step(depth, texture(shadowMap, uv + vec2(-r,  r)).r);
    s += step(depth, texture(shadowMap, uv + vec2( r,  r)).r);
    s += step(depth, texture(shadowMap, uv + vec2( 0, -r * 2.0)).r);
    s += step(depth, texture(shadowMap, uv + vec2( 0,  r * 2.0)).r);
    s += step(depth, texture(shadowMap, uv + vec2(-r * 2.0, 0)).r);
    s += step(depth, texture(shadowMap, uv + vec2( r * 2.0, 0)).r);
    return s / 8.0;
}


  float GetAngleAtt (vec3 L, vec3 litDir, float litAngleScale, float litAngleOffset) {
    float cd = dot(litDir, L);
    float attenuation = clamp(cd * litAngleScale + litAngleOffset, 0.0, 1.0);
    return (attenuation * attenuation);
  }

  float SmoothDistAtt (float distSqr, float invSqrAttRadius) {
    float factor = distSqr * invSqrAttRadius;
    float smoothFactor = clamp(1.0 - factor * factor, 0.0, 1.0);
    return smoothFactor * smoothFactor;
  }

  float GetDistAtt (float distSqr, float invSqrAttRadius) {
    float attenuation = 1.0 / max(distSqr, 0.01*0.01);
    attenuation *= SmoothDistAtt(distSqr, invSqrAttRadius);
    return attenuation;
  }

  float GetOutOfRange (vec3 worldPos, vec3 lightPos, vec3 lookAt, vec3 right, vec3 BoundingHalfSizeVS) {
    vec3 v = vec3(0.0);
    vec3 up = cross(right, lookAt);
    worldPos -= lightPos;
    v.x = dot(worldPos, right);
    v.y = dot(worldPos, up);
    v.z = dot(worldPos, lookAt);
    vec3 result = step(abs(v), BoundingHalfSizeVS);
    return result.x * result.y * result.z;
  }

  vec4 frag () {
    vec3 worldNormal = normalize(v_worldNormal);
    vec3 outputColor = vec3(0.0);

    float lightType = cc_lightPos[0].w;
    vec3 lightDir;
    float atten = 1.0;

    // ========== LIGHTING ==========
    if (lightType == 4.0) {
      // 范围平行光:方向固定,距离衰减
      lightDir = normalize(-cc_lightDir[0].xyz);
      float dist = distance(cc_lightPos[0].xyz, v_worldPos);
      float range = cc_lightSizeRangeAngle[0].y;
      if (range > 0.0) atten = clamp(1.0 - dist / range, 0.0, 1.0);
    }
    if (lightType == 2.0) {
      // 聚光:距离衰减 + 锥体衰减
      vec3 lightVec = cc_lightPos[0].xyz - v_worldPos;
      lightDir = normalize(lightVec);
      float distSqr = dot(lightVec, lightVec);
      float attRadiusSqrInv = 1.0 / max(cc_lightSizeRangeAngle[0].y, 0.01);
      attRadiusSqrInv *= attRadiusSqrInv;
      atten = GetDistAtt(distSqr, attRadiusSqrInv);
      float cosInner = max(dot(-cc_lightDir[0].xyz, lightDir), 0.01);
      float cosOuter = cc_lightSizeRangeAngle[0].z;
      float litAngleScale = 1.0 / max(0.001, cosInner - cosOuter);
      float litAngleOffset = -cosOuter * litAngleScale;
      atten *= GetAngleAtt(lightDir, -cc_lightDir[0].xyz, litAngleScale, litAngleOffset);
    }
    if (lightType ==1.) {
      // 点光/球面光:距离衰减
      vec3 lightVec = cc_lightPos[0].xyz - v_worldPos;
      lightDir = normalize(lightVec);
      float distSqr = dot(lightVec, lightVec);
      float attRadiusSqrInv = 1.0 / max(cc_lightSizeRangeAngle[0].y, 0.01);
      attRadiusSqrInv *= attRadiusSqrInv;
      atten = GetDistAtt(distSqr, attRadiusSqrInv);
    }

    float NdotL = max(dot(worldNormal, lightDir), 0.0);

    // ========== SHADOW ==========
    float shadow = 1.0;

    // 范围平行光阴影
    if (lightType == 4.0) {
      vec4 shadowPos = cc_matLightViewProj * vec4(v_worldPos, 1.0);
      vec3 ndc = shadowPos.xyz / shadowPos.w * 0.5 + 0.5;
      ndc.xy = cc_cameraPos.w == 1.0 ? vec2(ndc.x, 1.0 - ndc.y) : ndc.xy;
      if (all(greaterThanEqual(ndc, vec3(0.0))) && all(lessThanEqual(ndc, vec3(1.0)))) {
        vec2 atlasUV = mix(custom_atlasUV.xy, custom_atlasUV.zw, ndc.xy);
        shadow = step(ndc.z, readShadowDepth(custom_rangedShadowMap, atlasUV));
      }
    }

    // 聚光阴影
    if (cc_lightSizeRangeAngle[0].w > 0.0 && lightType == 2.0) {
      vec4 shadowPos = cc_matLightViewProj * vec4(v_worldPos, 1.0);
      vec3 ndc = shadowPos.xyz / shadowPos.w * 0.5 + 0.5;
      //ndc.xy = cc_cameraPos.w == 1.0 ? vec2(ndc.x, 1.0 - ndc.y) : ndc.xy;
      if (all(greaterThanEqual(ndc, vec3(0.0))) && all(lessThanEqual(ndc, vec3(1.0)))) {
        vec2 atlasUV = mix(custom_atlasUV.xy, custom_atlasUV.zw, ndc.xy);
        shadow = step(ndc.z, readShadowDepth(cc_spotShadowMap, atlasUV));
        //shadow=readShadowDepth(cc_spotShadowMap, atlasUV);
      }
    }

    // 球面光阴影(6面 cube map,if-else常量索引)
    if (lightType == 1.0 && custom_sphereShadowInfo.x > 0.0) {
    vec3 dir = normalize(cc_lightPos[0].xyz- v_worldPos ); // 光源→表面
      vec3 absDir = abs(dir);
      int face = 0;
      if (absDir.x >= absDir.y && absDir.x >= absDir.z) {
        face = dir.x >= 0.0 ? 0 : 1;
      } else if (absDir.y >= absDir.z) {
        face = dir.y >= 0.0 ? 2 : 3;
      } else {
        face = dir.z >= 0.0 ? 4 : 5;
      }
      mat4 faceVP;
      vec4 faceUV;
      if (face == 0) { faceVP = custom_sphereFaceVP[0]; faceUV = custom_sphereFaceUV[0]; }
      else if (face == 1) { faceVP = custom_sphereFaceVP[1]; faceUV = custom_sphereFaceUV[1]; }
      else if (face == 2) { faceVP = custom_sphereFaceVP[2]; faceUV = custom_sphereFaceUV[2]; }
      else if (face == 3) { faceVP = custom_sphereFaceVP[3]; faceUV = custom_sphereFaceUV[3]; }
      else if (face == 4) { faceVP = custom_sphereFaceVP[4]; faceUV = custom_sphereFaceUV[4]; }
      else { faceVP = custom_sphereFaceVP[5]; faceUV = custom_sphereFaceUV[5]; }
      vec4 shadowPos = faceVP * vec4(v_worldPos, 1.0);
      vec3 ndc = shadowPos.xyz / shadowPos.w * 0.5 + 0.5;
      //ndc.xy = cc_cameraPos.w == 1.0 ? vec2(ndc.x, 1.0 - ndc.y) : ndc.xy;
      if (all(greaterThanEqual(ndc, vec3(0.0))) && all(lessThanEqual(ndc, vec3(1.0)))) {
        vec2 atlasUV = mix(faceUV.xy, faceUV.zw, ndc.xy);
        shadow = step(ndc.z, readShadowDepth(custom_sphereShadowMap, atlasUV));
        // vec3 outputColor=vec3(0.,0.,0.);
  //     if (face == 0) outputColor = vec3(1.,0,0);
	// else if (face == 1) outputColor = vec3(0,1.,0);
	// else if (face == 2) outputColor = vec3(0,0,1.);
	// else if (face == 3) outputColor = vec3(1.,1.,0);
	// else if (face == 4) outputColor = vec3(1.,0,1.);
	// else outputColor = vec3(0,1,1);
	// return CCFragOutput(vec4(vec3(ndc), 1.0));
    }

    }
      

    // ========== COMBINE ==========
    outputColor += cc_lightColor[0].rgb * _Color.rgb * NdotL * atten * shadow;
   
    return CCFragOutput(vec4(outputColor, 1.0));
  }
}%

CCProgram shadow-caster-vs %{
  precision highp float;
  #include <builtin/uniforms/cc-global>
  #include <builtin/uniforms/cc-local>
  #include <builtin/uniforms/cc-shadow>

  in vec3 a_position;
  in vec3 a_normal;

  out highp vec4 v_localPos;
  out vec3 v_worldPos;
  out vec4 v_normal;
  out highp vec2 v_clip_depth;
  out highp vec4 v_clipPos;
  vec4 vert()
  {
    vec4 vertex = vec4(a_position, 1.0);
    v_localPos = vertex;
    v_worldPos = (cc_matWorld * vertex).xyz;
    v_normal = normalize((cc_matWorldIT * vec4(a_normal, 0.0)));
    v_clipPos = cc_matLightViewProj * vec4(v_worldPos.xyz, 1.0);
    v_clip_depth = v_clipPos.zw;
    return v_clipPos;
  }
}%

CCProgram shadow-caster-fs %{
  precision highp float;
  #include <builtin/functionalities/shadow-map>

  in highp vec2 v_clip_depth;
  in vec3 v_worldPos;


  vec4 frag () {
    vec4 depthColor;
    vec2 clip_depth=v_clip_depth;
    highp float clipDepth = clip_depth.x / clip_depth.y * 0.5 + 0.5;
    // 球光用线性深度(caster+receiver 全手动控制一致),聚光/主光保持原始clip depth
    // if (IS_SPHERE_LIGHT(cc_shadowLPNNInfo.x)) {
    //   clipDepth = CCGetLinearDepth(v_worldPos.xyz);
    //  }
    #if CC_SHADOWMAP_FORMAT == SHADOWMAP_FORMAT_RGBA8
      depthColor = packDepthToRGBA(clipDepth);
    #else
      depthColor = vec4(clipDepth, 1.0, 1.0, 1.0);
    #endif
      return depthColor;
    }
}%

图集设置
image

4赞