文章目录

  • 思路
  • 实践
    • 获取光源空间ShadowMap[A]
    • 获取屏幕空间的深度图[B]
    • 获取SSSM(Screen Space Shadow Map)
      • 绘制一个全屏的Quad[C]
      • 输出SSSM RT Shader
        • 在全屏Quad[C]里,制作将屏幕空间深度重建屏幕世界坐标[D]
        • 在将屏幕的世界坐标[D]转换到光源空间下的坐标[E]
        • 比较[E]与[A]对应的Shadow Map深度,确定是阴影的将在Quad[C]写入屏幕阴影像素
        • 完成的输出SSSM RT Shader
    • 接收阴影处理:对SSSM采样
  • 运行效果
    • FrameDebugger查看三大绘制过程
  • 屏幕空间的优缺点
    • 优点
    • 缺点
  • Project

刚学习了Shadow Map的原理: Unity Shader - Custom DirectionalLight ShadowMap 自定义方向光的ShadowMap

现在试试实现SSSM。

思路

  • 先拿到 光源空间下的 ShadowMap,具体,可以参考:Unity Shader - Custom DirectionalLight ShadowMap 自定义方向光的ShadowMap
  • 再拿到屏幕空间的深度图,具体,可以参考:Unity Shader - 获取BuiltIn深度纹理和自定义深度纹理的数据
  • 绘制一个全屏的Quad,并且将 屏幕空间的深度图 与 光源空间的ShadowMap阴影图,计算出屏幕空间的阴影图。进一步说明就是:将屏幕空间的深度 重构一下 世界坐标,再构建出世界坐标懂光源你空间的坐标,将深度的世界坐标通过世界到光源矩阵转换光源空间下,这时再比较深度来判定是否在阴影中。最后将这个全屏Quad的渲染目标到一个RT,叫做:SSSM(Screen Space Shadow Map)阴影图。
  • 接收阴影处理:对SSSM采样。将SSSM阴影图进shader全局uniform,渲染其他的接收阴影的对象时,将顶点坐标做到屏幕坐标,传入FS,插值后的屏幕坐标来采样SSS阴影图,采样得到的衰减数据与光照做混合计算即可。

实践

获取光源空间ShadowMap[A]

先拿到 光源空间下的 ShadowMap,具体,可以参考:Unity Shader - Custom DirectionalLight ShadowMap 自定义方向光的ShadowMap

获取屏幕空间的深度图[B]

再拿到屏幕空间的深度图,具体,可以参考:Unity Shader - 获取BuiltIn深度纹理和自定义深度纹理的数据

获取SSSM(Screen Space Shadow Map)

绘制一个全屏的Quad[C]

参考之前写的一个:Unity Shader - 模仿RenderImage制作全屏Quad

输出SSSM RT Shader

            fixed4 frag (v2f i) : SV_Target {// to world from depthfloat 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 attenreturn GetAtten(shadowCoord);}

在全屏Quad[C]里,制作将屏幕空间深度重建屏幕世界坐标[D]

参考:Unity Shader - 根据片段深度重建片段的世界坐标

                // to world from depthfloat linear01depth = (DecodeFloatRG(tex2D(_CustomDepthMap, i.uv).rg));float3 wp = _WorldSpaceCameraPos.xyz + i.ray * linear01depth;// return float4(wp, 1);

在将屏幕的世界坐标[D]转换到光源空间下的坐标[E]

                // world to lightspace// 取得当前绘制顶点相对光源空间下的坐标,即:阴影映射坐标float4 shadowCoord = mul(_CustomShadowMapLightSpaceMatrix, float4(wp, 1));

比较[E]与[A]对应的Shadow Map深度,确定是阴影的将在Quad[C]写入屏幕阴影像素

                // output SSSM attenreturn GetAtten(shadowCoord);

完成的输出SSSM RT Shader

// 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 variablessampler2D _CustomDepthMap;float4x4 _Ray;// shadow map veriablesfloat4 _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 : 1if (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)#endifif (fragDepth > 1) return 1;float strengthen = _CustomShadowStrengthen;float atten = 1;if (_CustomShadowSmoothType == 0) {// hardfloat shadowMapDepth = DecodeFloatRG(tex2D(_CustomShadowMap, uv).xy);if (fragDepth - _CustomShadowBias > shadowMapDepth) {atten = 1 - strengthen;}} else if (_CustomShadowSmoothType == 1) {// PCFs_Likefloat2 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_Likefloat 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;#elsev.vertex.z = 0;#endifo.vertex = v.vertex;o.uv = v.uv;o.ray = _Ray[v.vid];return o;}fixed4 frag (v2f i) : SV_Target {// to world from depthfloat 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 attenreturn GetAtten(shadowCoord);}ENDCG}}
}

接收阴影处理:对SSSM采样

// 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;// shadowsampler2D _ScreenSpaceShadowMap;            // sssmint _CustomShadowSmoothType;                // shadow 的边界平滑模式int _CustomShadowEnable;                    // shaodw 是否开启// lightingint _CustomLightEnable;                     // 光照是否开启int _CustomLightHalfLambert;                // 光照是否开启 half lambertint _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 ...// albedofixed4 albedo = tex2D(_MainTex, i.uv);i.worldNormal = normalize(i.worldNormal);//viewDir后面高光用half3 viewDir = normalize(_WorldSpaceCameraPos.xyz - i.worldPos);half3 lightDir = normalize(_WorldSpaceLightPos0.xyz);// ambientfixed3 ambient = UNITY_LIGHTMODEL_AMBIENT.rgb * albedo.rgb;// diffusefixed LdotN = dot(lightDir, i.worldNormal);fixed diffuse = _CustomLightHalfLambert ? LdotN * 0.5 + 0.5 : max(0, LdotN);// specularfixed specular = 0;bool appliedAtten = appliedAtten = atten == 1; // Hardif (_CustomShadowMixToLight) {// PCFs_Like || SDFs_Likeif (_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 是模拟各个方向的光所以不需要attencombinedCol.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);}ENDCGSubShader {Tags { "RenderType"="Opaque" "MyShadowMap"="1" }Pass {CGPROGRAM#pragma vertex vert#pragma fragment fragENDCG}}
}

运行效果

但是Scene视图中的阴影就有问题了。
具体什么原因不知道,但我就不纠结了。如下图:

FrameDebugger查看三大绘制过程

屏幕空间的优缺点

优点

貌似还不知道有点有啥用,为了研究一下而学习。(有可能是为了可以根据 深度容差来区别阴影边缘 + 阴影模糊,就可以比较轻松的做到“软”阴影的效果)

但看到有些文章说是为了所CSM(cascade shadow map)而用的(那意思:不在屏幕空间下做不可以了?显然不是的,因为CSM的整体思路就是对绘制的片段在世界坐标下与镜头的距离来做SM的层级的选择确定后再采样的,所以屏幕空间阴影有什么优点,我也暂时没有兴趣了解,后面那天需要用到,我再去细究)

缺点

没写如深度的物体间接受不了阴影。如:半透明。

在非SSSM中,我们可以对半透明物体采样Shadow map来着色阴影的,如下图:

虽然可以接收阴影了。
但是半透明的物体,没有投射阴影是很奇怪的。


Project

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_ZdeviceDepth = 1 - deviceDepth;
#endifdeviceDepth = 2 * deviceDepth - 1; //NOTE: Currently must massage depth before computing CS position.// jave.lin : 将深度值 转换到 view space posfloat3 vpos = ComputeViewSpacePosition(input.uv.zw, deviceDepth, unity_CameraInvProjection);// jave.lin : 将 view space pos 转到 world space posfloat3 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 的宏定义在下面几个文件中有定义,可以自行搜索:

Unity Shader - Custom SSSM(Screen Space Shadow Map) 自定义屏幕空间阴影图相关推荐

  1. Unity Shader - Custom DirectionalLight ShadowMap 自定义方向光的ShadowMap

    文章目录 思路 实践 在方向光的位置,放一个正交相机 调整光源相机参数 将光源投影空间的正交视锥体画出来 投射阴影 接收阴影 改进 超出Shadow map的默认为光照 添加光照处理 添加PCF柔滑整 ...

  2. Unity Shader法线贴图(Normal Map)及其原理

    简介 以前经常听说"模型不好看啊,怎么办啊?"答曰"加法线","做了个高模,准备烘一下法线贴图","有的美术特别屌,直接画法线贴图 ...

  3. [Unity] Canvas 设置为 Screen Space - Camera 时,UI 被场景物体遮挡的解决办法:设置 Canvas 的 Plane Distance 为一个较小的数

    Canvas 设置为 Screen Space - Camera 时,UI 可能被场景物体遮挡,如下图所示 只要设置 Canvas 的 Plane Distance 为一个很小的值,它就几乎永远不会被 ...

  4. Unity Shader入门学习(5):基础屏幕后处理

    1.后处理基类 //屏幕后处理,顾名思义,通常指的是在渲染完整个场景得到屏幕图像后,再对这个图像进行一系列操作,实现各种屏幕特效. //基类的作用有二:检测平台是否支持后处理效果,及创建一个用于处理渲 ...

  5. 2d shader unity 阴影_【Unity Shader】平面阴影(Planar Shadow)

    来介绍一种适用于移动平台的高性能实时阴影解决方案--平面阴影(Planar Shadow). 由于Unity内置的实时阴影实现方式是屏幕空间阴影贴图(Screen Space Shadow Map)非 ...

  6. Unity Shader - 模仿RenderImage制作全屏Quad,可以制作自定义后处理的流程

    文章目录 先尝试GL类来制作 Shader CSharp 画个三角型 画个全屏的Quad 发现GL没有RenderTarget之类的 使用CommandBuffer来绘制全屏的Quad GL渲染到目标 ...

  7. Unity Shader - URP ShadowCast ShadowRecieve - 投影 和 接受阴影

    文章目录 Shadow Caster Using URP Shadow Caster Pass Using Custom Shadow Caster Pass 先来看看 [没有] apply shad ...

  8. Unity Shader 阴影系列(1)--内置阴影处理

    阴影效果以及手机端阴影处理 什么是阴影 Unity中如何模拟阴影 上面的说的就是ShadowMap技术 屏幕空间阴影 Unity深度图的获取 对比分析 shadowmap的弊端 Shadow acne ...

  9. 阴影映射(Shadow Map)的研究(一)

    阴影映射(Shadow Map)的研究(一) 这段时间在搭好自己的框架后,就开始马不停蹄地研究阴影映射的内容了,说起阴影映射,倒不如说shadow map更容易被专业人士所接受.shadow map是 ...

最新文章

  1. 查询Oracle中字段名带.的数据
  2. [SCOI2005]栅栏(贪心+二分+dfs)难度⭐⭐⭐⭐
  3. 关于Laravel中使用response()方法调用json()返回数据unicode编码转换的问题解决
  4. Apollo 5.0,GitHub热榜第四
  5. 获取go语言官方文档的两个方法
  6. 【二叉树详解】二叉树的创建、遍历、查找以及删除等-数据结构05
  7. 游戏企业的“逆袭”,从用好这套解决方案开始 →
  8. C#控件跨线程内容更新
  9. 一看就懂!卡尔曼滤波通俗解释
  10. python实验收获与反思 100字_期中考试总结与反思100字4篇
  11. 拓端tecdat|R语言基于温度对城市层次聚类、kmean聚类、主成分分析和Voronoi图可视化
  12. photoshop标尺工具_RulersGuides.js – Web上的Photoshop样式标尺和指南
  13. 用Python中的VTK库导入并显示Assembly的STL文件
  14. 海龟作图python小猪佩奇_python海龟做图20秒完成小猪佩奇,附源码!
  15. 工作人员必备的计算机知识,工作必备计算机技巧知识
  16. 百度编辑器如何在html引入,网页制作中如何集成百度Ueditor网页编辑器
  17. 深度学习与TensorFlow实战(十)卷积神经网络—VGG(16)神经网络
  18. 一个学习FreeBSD不错的中文网站
  19. 蜂鸟E203图像识别--未完待续
  20. 混合动作空间(Dis_Conti_Hybrid)

热门文章

  1. https是绝对安全的吗?
  2. 《极客时间·每日一课》笔记
  3. 图文并茂解释Transformer--The Illustrated Transformer翻译精简
  4. matlab计算器设计流程图_MATLAB科学计算器设计.pdf
  5. 如何下载高德电子地图
  6. 2020最新版Android一步一步教轻松通过ArcSoft虹软平台实现人脸识别功能,保姆级别教程?
  7. 如何使用容联SDK,以及如何使用回调简单示例
  8. 【GIS开发】Leaflet入门学习(Javascript库)
  9. CentOS-7-x86_64-DVD-1810 制作usb启动盘注意事项
  10. 【微信引流推广单页模板】微信引流分享裂变html代码