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;
}
}%
图集设置

