赞
踩
现在试试实现SSSM。
先拿到 光源空间下的 ShadowMap,具体,可以参考:Unity Shader - Custom DirectionalLight ShadowMap 自定义方向光的ShadowMap
再拿到屏幕空间的深度图,具体,可以参考:Unity Shader - 获取BuiltIn深度纹理和自定义深度纹理的数据
参考之前写的一个:Unity Shader - 模仿RenderImage制作全屏Quad
fixed4 frag (v2f i) : SV_Target {
// to world from depth
float linear01depth = (DecodeFloatRG(tex2D(_CustomDepthMap, i.uv).rg));
float3 wp = _WorldSpaceCameraPos.xyz + i.ray * linear01depth;
// return float4(wp, 1);
// world to lightspace
// 取得当前绘制顶点相对光源空间下的坐标,即:阴影映射坐标
float4 shadowCoord = mul(_CustomShadowMapLightSpaceMatrix, float4(wp, 1));
// output SSSM atten
return GetAtten(shadowCoord);
}
参考:Unity Shader - 根据片段深度重建片段的世界坐标
// to world from depth
float linear01depth = (DecodeFloatRG(tex2D(_CustomDepthMap, i.uv).rg));
float3 wp = _WorldSpaceCameraPos.xyz + i.ray * linear01depth;
// return float4(wp, 1);
// world to lightspace
// 取得当前绘制顶点相对光源空间下的坐标,即:阴影映射坐标
float4 shadowCoord = mul(_CustomShadowMapLightSpaceMatrix, float4(wp, 1));
// output SSSM atten
return GetAtten(shadowCoord);
// jave.lin 2020.04.14 - 根据屏幕深度重构世界坐标 - output SSSM atten Shader "Custom/ReconstructSSSMFromDepth" { SubShader { Pass { CGPROGRAM #pragma vertex vert #pragma fragment frag #include "UnityCG.cginc" struct appdata { float4 vertex : POSITION; float2 uv : TEXCOORD0; uint vid : SV_VertexID; }; struct v2f { float4 vertex : SV_POSITION; float2 uv : TEXCOORD0; float3 ray : TEXCOORD1; }; // reconstruct depth variables sampler2D _CustomDepthMap; float4x4 _Ray; // shadow map veriables float4 _CustomShadowMap_TexelSize; sampler2D _CustomShadowMap; float4x4 _CustomShadowMapLightSpaceMatrix; float _CustomShadowStrengthen; float _CustomShadowBias; int _CustomShadowSmoothType; float _CustomShadowPCFSpread; float _CustomShadowBlurDistance; float _CustomShadowBlurWeight[25]; // 获取光照衰减系数 float GetAtten(float4 shadowCoord) { float2 uv = shadowCoord.xy / shadowCoord.w; uv = uv * 0.5 + 0.5; // (-1,1)->(0,1) // clamp to edge color : 1 if (uv.x > 1 || uv.y > 1 || uv.x < 0 || uv.y < 0) return 1; float fragDepth = shadowCoord.z / shadowCoord.w; #if defined (SHADER_TARGET_GLSL) fragDepth = fragDepth * 0.5 + 0.5; // (-1,1)->(0,1) #elif defined (UNITY_REVERSED_Z) fragDepth = 1 - fragDepth; // (1,0)->(0,1) #endif if (fragDepth > 1) return 1; float strengthen = _CustomShadowStrengthen; float atten = 1; if (_CustomShadowSmoothType == 0) {// hard float shadowMapDepth = DecodeFloatRG(tex2D(_CustomShadowMap, uv).xy); if (fragDepth - _CustomShadowBias > shadowMapDepth) { atten = 1 - strengthen; } } else if (_CustomShadowSmoothType == 1) {// PCFs_Like float2 offset = 0; float minus_step = strengthen / 9.0; for(int i = -1; i < 2; ++i) { for(int j = -1; j < 2; ++j) { offset = float2(i, j) * _CustomShadowMap_TexelSize.xy * _CustomShadowPCFSpread; float shadowMapDepth = DecodeFloatRG(tex2D(_CustomShadowMap, uv + offset).xy); if (fragDepth - _CustomShadowBias > shadowMapDepth) { atten -= minus_step; } } } } else if (_CustomShadowSmoothType == 2) {// SDFs_Like float center_shadowMapDepth = DecodeFloatRG(tex2D(_CustomShadowMap, uv).xy); float distance = 1; if (fragDepth - _CustomShadowBias > center_shadowMapDepth) { distance = saturate(fragDepth- _CustomShadowBias - center_shadowMapDepth); distance /= _CustomShadowBlurDistance; } float2 offset = 0; float w = 0; float idx = 0; int i =0, j=0; for(i = -2; i < 3; ++i) { for(j = -2; j < 3; ++j) { idx = (j+2) + (i+2) * 5; w = _CustomShadowBlurWeight[idx]; w *= strengthen; offset = float2(i, j) * _CustomShadowMap_TexelSize.xy * _CustomShadowPCFSpread * distance; float shadowMapDepth = DecodeFloatRG(tex2D(_CustomShadowMap, uv + offset).xy); if (fragDepth - _CustomShadowBias > shadowMapDepth) { atten -= w; } } } } return atten; } v2f vert (appdata v) { v2f o; // CSharp层脚本的第三个z分量最好不要在这里设置 // 最好在shader中判断是GL还是DX平台来设置为近截面的z值就好 // jave.lin : 在这里我们处理GL与DX的差异 // GL的Z:-1~1,DX的Z:0~1 #if defined (SHADER_TARGET_GLSL) v.vertex.z = -1; #else v.vertex.z = 0; #endif o.vertex = v.vertex; o.uv = v.uv; o.ray = _Ray[v.vid]; return o; } fixed4 frag (v2f i) : SV_Target { // to world from depth float linear01depth = (DecodeFloatRG(tex2D(_CustomDepthMap, i.uv).rg)); float3 wp = _WorldSpaceCameraPos.xyz + i.ray * linear01depth; // return float4(wp, 1); // world to lightspace // 取得当前绘制顶点相对光源空间下的坐标,即:阴影映射坐标 float4 shadowCoord = mul(_CustomShadowMapLightSpaceMatrix, float4(wp, 1)); // output SSSM atten return GetAtten(shadowCoord); } ENDCG } } }
// jave.lin 2020.04.14 接收SSSM(Screen Space Shadow Map)阴影 Shader "Custom/ReceiveSSSM" { Properties { _MainTex ("MainTex", 2D) = "white" {} _MainColor ("MainColor", Color) = (1, 1, 1, 1) } CGINCLUDE #include "UnityCG.cginc" #include"Lighting.cginc" sampler2D _MainTex; fixed4 _MainColor; // shadow sampler2D _ScreenSpaceShadowMap; // sssm int _CustomShadowSmoothType; // shadow 的边界平滑模式 int _CustomShadowEnable; // shaodw 是否开启 // lighting int _CustomLightEnable; // 光照是否开启 int _CustomLightHalfLambert; // 光照是否开启 half lambert int _CustomShadowMixToLight; // shadow 混合到光照 struct a2v { float4 vertex : POSITION; half3 normal : NORMAL; float2 uv : TEXCOORD0; }; struct v2f { float4 vertex : SV_POSITION; float2 uv : TEXCOORD0; float4 shadowCoord : TEXCOORD1; half3 worldNormal : TEXCOORD2; float3 worldPos : TEXCOORD3; float4 screenPos : TEXCOORD4; }; // 获取SSSM光照衰减系数 float GetAtten(v2f i) { return tex2Dproj(_ScreenSpaceShadowMap, i.screenPos).r; } // 着色处理 fixed4 shading(v2f i, float atten) { if (_CustomLightEnable) { // code here: // ambient // diffuse // specular // etc ... // albedo fixed4 albedo = tex2D(_MainTex, i.uv); i.worldNormal = normalize(i.worldNormal); //viewDir后面高光用 half3 viewDir = normalize(_WorldSpaceCameraPos.xyz - i.worldPos); half3 lightDir = normalize(_WorldSpaceLightPos0.xyz); // ambient fixed3 ambient = UNITY_LIGHTMODEL_AMBIENT.rgb * albedo.rgb; // diffuse fixed LdotN = dot(lightDir, i.worldNormal); fixed diffuse = _CustomLightHalfLambert ? LdotN * 0.5 + 0.5 : max(0, LdotN); // specular fixed specular = 0; bool appliedAtten = appliedAtten = atten == 1; // Hard if (_CustomShadowMixToLight) { // PCFs_Like || SDFs_Like if (_CustomShadowSmoothType == 1 || _CustomShadowSmoothType == 2) { appliedAtten = true; // 意味着模糊边缘的话,specular会乘上atten来衰减 } } if (LdotN > 0 && appliedAtten) { half3 hDir = normalize(viewDir + lightDir); fixed HdotN = max(0, dot(hDir, i.worldNormal)); specular = pow(HdotN, 64); } fixed4 combinedCol = 0; // ambient 是模拟各个方向的光所以不需要atten combinedCol.xyz = ambient + diffuse * atten * albedo.rgb * _LightColor0.rgb * _MainColor.rgb + specular * atten * _LightColor0.rgb; return combinedCol; } else { return tex2D(_MainTex, i.uv) * _MainColor * atten; } } v2f vert (a2v v) { v2f o; o.vertex = UnityObjectToClipPos(v.vertex); o.uv = v.uv; o.worldNormal = UnityObjectToWorldNormal(v.normal); o.worldPos = mul(unity_ObjectToWorld, v.vertex); o.screenPos = ComputeScreenPos(o.vertex); return o; } fixed4 frag (v2f i) : SV_Target { float atten = _CustomShadowEnable ? GetAtten(i) : 1; return shading(i, atten); } ENDCG SubShader { Tags { "RenderType"="Opaque" "MyShadowMap"="1" } Pass { CGPROGRAM #pragma vertex vert #pragma fragment frag ENDCG } } }
但是Scene视图中的阴影就有问题了。
具体什么原因不知道,但我就不纠结了。如下图:
貌似还不知道有点有啥用,为了研究一下而学习。(有可能是为了可以根据 深度容差来区别阴影边缘 + 阴影模糊,就可以比较轻松的做到“软”阴影的效果)
但看到有些文章说是为了所CSM(cascade shadow map)而用的(那意思:不在屏幕空间下做不可以了?显然不是的,因为CSM的整体思路就是对绘制的片段在世界坐标下与镜头的距离来做SM的层级的选择确定后再采样的,所以屏幕空间阴影有什么优点,我也暂时没有兴趣了解,后面那天需要用到,我再去细究)
没写如深度的物体间接受不了阴影。如:半透明。
在非SSSM中,我们可以对半透明物体采样Shadow map来着色阴影的,如下图:
虽然可以接收阴影了。
但是半透明的物体,没有投射阴影是很奇怪的。
backup : UnityShader_CustomShadow_includeSSSM_2018.3.0f2
2021/10/21 在阅读 Unity 2019.4.30f1 的 URP 7.7.1 版本的 shader : Packages/com.unity.render-pipeline.universal/Shaders/Utils/ScreenSpaceShadows.shader
中的代码,发现有一段很简单的就描述了如何生成 SSSM:(已添加了一些注释便于阅读理解)
half4 Fragment(Varyings input) : SV_Target { UNITY_SETUP_STEREO_EYE_INDEX_POST_VERTEX(input); // jave.lin : 获取相机拍摄深度 float deviceDepth = SAMPLE_TEXTURE2D_X(_CameraDepthTexture, sampler_CameraDepthTexture, input.uv.xy).r; // jave.lin : 根据查看下图了解,不同的一些渲染平台,深度正值增长方向都可能有所不同 #if UNITY_REVERSED_Z deviceDepth = 1 - deviceDepth; #endif deviceDepth = 2 * deviceDepth - 1; //NOTE: Currently must massage depth before computing CS position. // jave.lin : 将深度值 转换到 view space pos float3 vpos = ComputeViewSpacePosition(input.uv.zw, deviceDepth, unity_CameraInvProjection); // jave.lin : 将 view space pos 转到 world space pos float3 wpos = mul(unity_CameraToWorld, float4(vpos, 1)).xyz; // jave.lin : 将 world space world 转到 shadow/light space pos //Fetch shadow coordinates for cascade. float4 coords = TransformWorldToShadowCoord(wpos); // Screenspace shadowmap is only used for directional lights which use orthogonal projection. ShadowSamplingData shadowSamplingData = GetMainLightShadowSamplingData(); half4 shadowParams = GetMainLightShadowParams(); // 根据相关配置参数 + shadow space pos 采样 + 判断是否中阴影中 return SampleShadowmap(TEXTURE2D_ARGS(_MainLightShadowmapTexture, sampler_MainLightShadowmapTexture), coords, shadowSamplingData, shadowParams, false); }
UNITY_REVERSED_Z
的宏定义在下面几个文件中有定义,可以自行搜索:
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。