文章目录

  • 整体运行效果
  • 思路
    • Shader
  • 问题
    • Z-Fighting,解决:按法线方向偏移一丢丢
    • 绘制 Alpha 混合重叠,解决:使用 stencil buffer 来规避
      • 为何出现这个问题
      • 解决方法
  • 阴影距离淡出
  • 用在 URP 中的 Base Shadow 中
  • 完整 Pass
  • Planar Shadow 的优缺点
  • Backup Project
  • References

整体运行效果


思路

将模型压扁后,绘制即可

Shader

写成 shader 代码就是

// 平面上的任意一点
float4 _PlanePos;
// 平面法线
float4 _PlaneNor;
...
float3 OffsetToPlanarShadowPos(float3 wpos) {// 方向光的位置方位,(单位向量)float3 L = _WorldSpaceLightPos0.xyz;// 平面法线,(单位向量)float3 N = _PlaneNor;float d1 = dot(L, N);float d2 = dot(wpos - _PlanePos.xyz, N);return wpos + -L * (d2 / d1);
}

为了简单起见,我们直接使用加多一个 pass 来绘制 Planner Shadow 即可

看看执行效果:

右侧的图,可以看到阴影绘制的是一个网格


问题

从上面的效果图可以发现两个问题,我们看看下面 Gif 动画会更明显:

  • Planar Shadow 与 平面地表得 z-fighting 问题
  • 绘制半透明阴影的重叠问题

Z-Fighting,解决:按法线方向偏移一丢丢

Shader

// 平面上的任意一点
float4 _PlanePos;
// 平面法线
float4 _PlaneNor;
...
float3 OffsetToPlanarShadowPos(float3 wpos) {// 方向光的位置方位,(单位向量)float3 L = _WorldSpaceLightPos0.xyz;// 平面法线,(单位向量)float3 N = _PlaneNor;float d1 = dot(L, N);float d2 = dot(wpos - _PlanePos.xyz, N);return wpos + -L * (d2 / d1) + N * _PlaneNor.w;
}

注意上面添加了一个:+ N * _PlaneNor.w_PlaneNor.w 通常很小的值就可以了,我这填了个:0.01

再绘制看看效果:

OK,Z-Fighting 解决了


绘制 Alpha 混合重叠,解决:使用 stencil buffer 来规避

但是发现半透明阴影的 alpha 区域重叠了


为何出现这个问题

如果对象只有单一一个模型,那么这个问题稍微没那么好重现
刚刚好我们这个模型有好几个 SubMesh:身体、头发、皮肤、武器,等

所以不同的网格的绘制都是半透明混合:Blend SrcAlpha OneMinusSrcAlpha 的话,肯定会有上述的重叠区域的问题


解决方法

使用 Stencil(模板测试、模板缓存) 来解决

这样我们再运行看看效果:

OK,没问题了

关于 stencil 的文章我之前也有写过:

  • Unity Shader - 模板简单测试 - 实现镂空/遮罩、描边
  • Unity Shader - ShaderLab: Stencil 模板(缓存)
  • 颜色缓冲区、深度缓冲区、模板缓冲区和累积缓冲区

阴影距离淡出

在玩王者荣耀的时候,我们都可以发现:阴影显示的不是完整的,根据了一个固定的距离就淡出了

Shader 代码主要查看:函数的返回值 fade

fixed OffsetToPlanarShadowPosAndFade(inout float3 wpos) {// 方向光的位置方位,(单位向量)float3 L = _WorldSpaceLightPos0.xyz;// 平面法线,(单位向量)float3 N = _PlaneNor;float d1 = dot(L, N);float d2 = dot(wpos - _PlanePos.xyz, N);float new_pos_offset_len = d2 / d1;wpos += -L * new_pos_offset_len;// 在平面法线发向上偏移一丢丢wpos += N * _PlaneNor.w;// 计算 fade (这部分也可以抽离到另一个函数来处理,但是 L 也可以使用上面的,所以暂时写在这里)if (_PlanePos.w > 0) {float3 wpos_h_0 = -N * d2;float3 wpos_h_0_2_new_wpos = wpos - wpos_h_0;float len = pow(dot(wpos_h_0_2_new_wpos, wpos_h_0_2_new_wpos), 2);float cfg_len = _PlanePos.w * _PlanePos.w;fixed fade = smoothstep(0, 1, 1 - saturate(len / cfg_len));// fixed fade = 1 - saturate(len / cfg_len);return fade;} else {return 1;}
}

结合下图来看

思路就是:v=wp′−p′v = wp'-p'v=wp′−p′ 中,就得 vvv 的长度,再除以 _PlanePos.w 即可
最终 fade 值:fade = length(v) / _PlanePos.w

_PlanePos.w 目前我传入了 30 的值

但上面的 shader 中,我们去掉 length(v) 的运算来优化性能,因为length 里面有 sqrt, div 除法

再运行看看效果:


用在 URP 中的 Base Shadow 中

因为上述的方式是添加额外的 pass 来处理的,但是添加 pass 的话,就会打断合批

而我们可以使用 CommandBuffer 来指定在某个提供的事件来绘制所有 Planar Shadow,而且 URP 的合拼严格上来说不是为了减少 DC,而是为了减少 Draw API 调用前的 SetState API

因为 SetState (设置绘制状态值)的数据比较多,而且不能一次设置好,要分多次

所以 URP 使用了 CBuffer 只设置与上次绘制对象使用的 shader 中不同的 CBuffer 数据来 SetState
(意思:如果相同的 Shader,相同的参数,可以不用 SetState 就绘制下一个对象,只是再次调用一次 Draw API 即可,如果不同网格,只要设置好使用的 VBO 即可)

Unity 中得操作如下:

一般的再 URP 的话,可以直接将材质设置到 URP Renderer 中的 Base Shadow 的 Overrides->Materials 材质槽即可

注意在 Event 中选择:After Rendering Opaques(在所有的不透明对象渲染完后再绘制阴影的意思)
这个 Event 的功能在 Unity 的底层使用了 CommandBuffer 的来处理的

CommadnBuffer 相关得文章我之前也写过一些,想了解的话,可以看看:

  • Unity CommandBuffer 测试自定义替换渲染
  • Unity Shader PostProcessing - 6 - GaussianBlur 高斯模糊+CommandBuffer使用做一些其他的特效
  • Unity CommandBuffer或是Camera重定向RenderTarget的ColorBuffer & DepthBuffer
  • Unity CommandBuffer.SetViewProjectionMatrixes & CommandBuffer.DrawMesh 测试对Shader中默认的MVP设置

完整 Pass

        Pass { // Planar ShadowName "PlanarShadow"Blend SrcAlpha OneMinusSrcAlphaCull FrontZWrite OffStencil {Ref [_PlanarStencilValue]Comp NotEqualPass ReplaceFail Keep}CGPROGRAM#pragma vertex vert#pragma fragment frag#include "UnityCG.cginc"#pragma fragmentoption ARB_precision_hint_fastest       // 最快速精度// 平面上的任意一点float4 _PlanePos;// 平面法线float4 _PlaneNor;fixed4 _PlanarShadowColor;struct appdata {float4 vertex : POSITION;};struct v2f {float4 pos : SV_POSITION;fixed fade : TEXCOORD0;};fixed OffsetToPlanarShadowPosAndFade(inout float3 wpos) {// 方向光的位置方位,(单位向量)float3 L = _WorldSpaceLightPos0.xyz;// 平面法线,(单位向量)float3 N = _PlaneNor;float d1 = dot(L, N);float d2 = dot(wpos - _PlanePos.xyz, N);float new_pos_offset_len = d2 / d1;wpos += -L * new_pos_offset_len;// 在平面法线发向上偏移一丢丢wpos += N * _PlaneNor.w;// 计算 fade (这部分也可以抽离到另一个函数来处理,但是 L 也可以使用上面的,所以暂时写在这里)if (_PlanePos.w > 0) {float3 wpos_h_0 = -N * d2;float3 wpos_h_0_2_new_wpos = wpos - wpos_h_0;float len = pow(dot(wpos_h_0_2_new_wpos, wpos_h_0_2_new_wpos), 2);float cfg_len = _PlanePos.w * _PlanePos.w;fixed fade = smoothstep(0, 1, 1 - saturate(len / cfg_len));// fixed fade = 1 - saturate(len / cfg_len);return fade;} else {return 1;}}v2f vert (appdata v) {v2f o;float3 wpos = mul(unity_ObjectToWorld, v.vertex).xyz;fixed fade = OffsetToPlanarShadowPosAndFade(wpos);o.pos = UnityWorldToClipPos(wpos);o.fade = fade;return o;}fixed4 frag (v2f i) : SV_Target {fixed4 col = _PlanarShadowColor;col.a *= i.fade;return col;}ENDCG}

Planar Shadow 的优缺点

  • 优点:阴影边界可以很清晰,很 sharp,有点 shadowVolume 那种精细度,还可以节省 RT(RenderTexture,相比 shadowMap 就需要 RT)
  • 缺点:如果游戏中地形有高低,就不适合这种方式,王者荣耀之所以适合,是因为整个可行走地形都是水平的,就不会出现:腾空与低地表,或是陷于高地表内的情况

Backup Project

备份用,不公开模型,代码再上面都公开了
testing_planar_shadow


References

  • 详解平面阴影 Planar Shadow (概念篇) - 这几篇后续有使用构建压扁矩阵的构建,说是更加优雅的方式,但我个人觉得没有上面画图上的几何意义的来得直观
  • 详解平面阴影 Planar Shadow(方向光篇) - 还没去看过,后续再看
  • 详解平面阴影 Planar Shadow(点光源篇) - 还没去看过,后续再看
  • Planar Shadow中Shadow Matrix的推导
  • 《3D数学基础:图形与游戏开发》- 8.4.2 向任意直线或平面投影 - page 100
  • 即時 3D 繪圖的陰影效果 [Part 1]

Unity Shader - Planar Shadow - 平面阴影相关推荐

  1. 【Unity】Planar Shadows平面阴影的实现

    Plannar Shadows,即平面阴影,是一个适用于平坦地形的假阴影技术.要求阴影的Receiver为平面,Occluder不与其他物体穿插. 实现效果 1.定向光源Planar Shadows ...

  2. Real-Time Rendering——7.1 Planar Shadows平面阴影7.1.1 Projection Shadows投影阴影

    7.1 Planar Shadows平面阴影 A simple case of shadowing occurs when objects cast shadows onto a planar sur ...

  3. unity shader 实现随意平面裁剪

    unity shader 实现随意平面裁剪 shader文件: Shader "Tut/Shader/ClipObj" {Properties {_MainTex ("T ...

  4. Unity URP Planar reflection 平面反射

    利用双休天写的,代码和shader还有不少问题 修复中.... 一开始是用模板测试和后处理写的,后面改成屏幕坐标,更方便了,加入到我的水体康康 主要思想是动态创建一个翻转的反射摄像机 ,但是不打开这个 ...

  5. Unity shader 关于卡通渲染阴影闭塞的实现

    想实现一个胸部的动态阴影效果 这种 推测应该是,AO贴图 + 边缘光的算法 + 梯度图lerp.我没用AO贴图(嫌麻烦

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

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

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

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

  8. 【Unity Shader】关于Stencil的理解小记

    写在前面:本文只介绍Stencil最基本的三种参数:Ref Comp Pass ,其他的略略带过. 碎碎念:之前用过Stencil,但是没有完全理解,原因在于虽然理解了每项的意思,但是实际的使用效果不 ...

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

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

最新文章

  1. Java基础-OOP特性之封装、继承、多态、抽象
  2. RxAndroid/java小记
  3. MPO文件类型解码(四)3D图像整体结构
  4. 模糊神经网络PID控制的一个例子
  5. 九、oracle 事务
  6. AD20学习笔记2---原理图绘制及编译检查
  7. 离职是一件利国利民的大好事
  8. ubintu yum装mysql_Ubuntu 下安装Mysql centos8 下安装mysql8.0
  9. 2018.07.25 bzoj2125: 最短路(圆方树+倍增)
  10. mysql的常见命令与语法规范
  11. 我的团长我的团第四十三集
  12. windows提示“为了对电脑进行保护,已经阻止此应用”的解决方案 mmc.exe
  13. 1521 一维战舰 水题
  14. 巴菲特致股东的一封信:2000年
  15. pstl连接数据库时出现ORA-12154:TNS:could not resolve service name错误。
  16. Java实现二维码制作
  17. android侧滑删除框架,Android 侧滑菜单(侧滑删除)总结
  18. 如何修改云服务器的远程连接密码?
  19. 在hadoop中伪分布式部署的命令与步骤
  20. 光学红外雨量IFR202型传感器应用于智慧灌溉智能家居地质灾害等行业

热门文章

  1. js图片截图粘贴和上传
  2. 如何在word文档里复选框里打勾?
  3. Python深度学习-快速指南
  4. 将excel内列表每行数据分别填充到另一个word模板里-批量生成同一模板下的不同文档,word邮件功能
  5. 【tools】信息收集-灯塔资产扫描 ARL
  6. Unity实用案例之——动画压缩
  7. Phonetic symbol 清辅音 -- s
  8. 太可怕,有码变高清!AI 一秒还原马赛克?
  9. 从朴素贝叶斯的角度推导logistic模型
  10. android_rooting_tools 项目介绍(CVE-2012-4220)