2d shader unity 阴影_【Unity Shader】平面阴影(Planar Shadow)
来介绍一种适用于移动平台的高性能实时阴影解决方案——平面阴影(Planar Shadow)。
由于Unity内置的实时阴影实现方式是屏幕空间阴影贴图(Screen Space Shadow Map)非常消耗性能,所以移动端的阴影一般需要使用更加性能友好的替代方案。常用的方案有简单贴图法、平面阴影、投影生成法和环境遮挡法,常用的的插件有Dynamic Shadow Projector和Fast Shadow Receiver。有兴趣的同学可以在网上很容易找到相关的详细介绍,或者看一下文末的拓展阅读部分。
这篇主要来分享一下平面阴影Unity Shader的实现并总结优缺点,平面阴影又称作顶点投射阴影,手游《王者荣耀》中就用了类似的技术。
原理及实现
具体原理和实现方式可参考:
喵喵Mya:使用顶点投射的方法制作实时阴影zhuanlan.zhihu.com
我在这个shader的基础上又做了两个修改:
- 一个是从场景中读取主光源的方向来调整阴影
- 一个是支持了透明度剔除Alpha Clipping
(其实还加了个地面高度属性_GroundHeight)
修改一:从场景中读取主光源的方向
//灯光方向
// float3 lightDir = normalize(_LightDir.xyz);
float3 lightDir = normalize(_WorldSpaceLightPos0.xyz);
原来是在Shader属性中手动调整,现在如果场景中有多个物体时改变光源方向就不需要一个一个调Shader了。
修改二:支持了透明度剔除Alpha Clipping
if (_Clipping) {float alpha = tex2D(_MainTex, i.uv.xy).a;i.color.a *= step(_Cutoff, alpha);
}
提取主贴图的A通道做透明度剔除。
优缺点
这种平面阴影的优点是性能消耗小,阴影品质较高,简单好实现,非常适合MOBA类、俯视视角类型的游戏(角色和相机有一定距离)。
但是此方法的局限性也很大,最明显的缺点是阴影只投影在特定平面上,会插到墙体等障碍物中。另外它模拟的并不是真实的物理阴影和实际差别很大,阴影边缘很硬,真实的边缘会有虚化,如果镜头距离较近的话缺陷就会比较明显。
修改后的Shader(只有平面阴影Pass)
PlanarShadow.shader
Shader "Custom/PlanarShadow"
{Properties{_Tint("_Tint", Color) = (1,1,1,1)_MainTex("_MainTex (albedo)", 2D) = "white" {}[Header(Alpha)][Toggle(_CLIPPING)] _Clipping ("Alpha Clipping", Float) = 1_Cutoff("_Cutoff (Alpha Cutoff)", Range(0.0, 1.0)) = 0.5 // alpha clip threshold[Header(Shadow)]// _GroundHeight("_GroundHeight", Range(-100, 100)) = 0_GroundHeight("_GroundHeight", Float) = 0_ShadowColor("_ShadowColor", Color) = (0,0,0,1)_ShadowFalloff("_ShadowFalloff", Range(0,1)) = 0.05// Blending state[HideInInspector] _SrcBlend("__src", Float) = 1.0[HideInInspector] _DstBlend("__dst", Float) = 0.0[HideInInspector] _ZWrite("__zw", Float) = 1.0[HideInInspector] _Cull("__cull", Float) = 2.0}SubShader{Pass {// 其他Pass请自行实现}// Planar Shadows平面阴影Pass{Name "PlanarShadow"//用使用模板测试以保证alpha显示正确Stencil{Ref 0Comp equalPass incrWrapFail keepZFail keep}Cull Off//透明混合模式Blend SrcAlpha OneMinusSrcAlpha//关闭深度写入ZWrite off//深度稍微偏移防止阴影与地面穿插Offset -1 , 0CGPROGRAM#pragma shader_feature _CLIPPING#pragma shader_feature _ALPHATEST_ON#pragma shader_feature _ALPHAPREMULTIPLY_ON#include "UnityCG.cginc"#pragma vertex vert#pragma fragment fragfloat _GroundHeight;float4 _ShadowColor;float _ShadowFalloff;half4 _Tint;sampler2D _MainTex;float4 _MainTex_ST;float _Clipping;half _Cutoff;struct appdata{float4 vertex : POSITION;float2 uv : TEXCOORD0;};struct v2f{float4 vertex : SV_POSITION;float4 color : COLOR;float2 uv : TEXCOORD0;};float3 ShadowProjectPos(float4 vertPos){float3 shadowPos;//得到顶点的世界空间坐标float3 worldPos = mul(unity_ObjectToWorld , vertPos).xyz;//灯光方向// float3 lightDir = normalize(_LightDir.xyz);float3 lightDir = normalize(_WorldSpaceLightPos0.xyz);//阴影的世界空间坐标(低于地面的部分不做改变)shadowPos.y = min(worldPos .y , _GroundHeight);shadowPos.xz = worldPos .xz - lightDir.xz * max(0 , worldPos .y - _GroundHeight) / lightDir.y; return shadowPos;}float GetAlpha (v2f i) {float alpha = _Tint.a * tex2D(_MainTex, i.uv.xy).a;return alpha;}v2f vert (appdata v){v2f o;//得到阴影的世界空间坐标float3 shadowPos = ShadowProjectPos(v.vertex);//转换到裁切空间o.vertex = UnityWorldToClipPos(shadowPos);//得到中心点世界坐标float3 center = float3(unity_ObjectToWorld[0].w , _GroundHeight , unity_ObjectToWorld[2].w);//计算阴影衰减float falloff = 1-saturate(distance(shadowPos , center) * _ShadowFalloff);//阴影颜色o.color = _ShadowColor;o.color.a *= falloff;o.uv = TRANSFORM_TEX(v.uv, _MainTex);return o;}fixed4 frag (v2f i) : SV_Target{if (_Clipping){float alpha = GetAlpha(i);i.color.a *= step(_Cutoff, alpha);}return i.color;}ENDCG}}// FallBack "Diffuse"
}
URP 版本
替换了一下引用和API,物体自身着色使用URP自带的ForwardLit,可以直接使用看效果。
PlanarShadow_URP.shader
Shader "Custom/PlanarShadow_URP"
{Properties{[Header(Base Color)][MainColor] _BaseColor("_BaseColor", Color) = (1,1,1,1)[MainTexture] _BaseMap("_BaseMap (albedo)", 2D) = "white" {}[Header(Alpha)][Toggle(_CLIPPING)] _Clipping ("Alpha Clipping", Float) = 1_Cutoff("_Cutoff (Alpha Cutoff)", Range(0.0, 1.0)) = 0.5 // alpha clip threshold[Header(Shadow)]// _GroundHeight("_GroundHeight", Range(-100, 100)) = 0_GroundHeight("_GroundHeight", Float) = 0_ShadowColor("_ShadowColor", Color) = (0,0,0,1)_ShadowFalloff("_ShadowFalloff", Range(0,1)) = 0.05// Blending state[HideInInspector] _SrcBlend("__src", Float) = 1.0[HideInInspector] _DstBlend("__dst", Float) = 0.0[HideInInspector] _ZWrite("__zw", Float) = 1.0[HideInInspector] _Cull("__cull", Float) = 2.0}SubShader{ Tags {"RenderPipeline" = "UniversalPipeline" "RenderType" = "Transparent" "Queue" = "Transparent"}LOD 300// 物体自身着色使用URP自带的ForwardLit passUSEPASS "Universal Render Pipeline/Lit/ForwardLit"// Planar Shadows平面阴影Pass{Name "PlanarShadow"//用使用模板测试以保证alpha显示正确Stencil{Ref 0Comp equalPass incrWrapFail keepZFail keep}Cull Off//透明混合模式Blend SrcAlpha OneMinusSrcAlpha//关闭深度写入ZWrite off//深度稍微偏移防止阴影与地面穿插Offset -1 , 0HLSLPROGRAM#pragma shader_feature _CLIPPING#pragma shader_feature _ALPHATEST_ON#pragma shader_feature _ALPHAPREMULTIPLY_ON#include "Packages/com.unity.render-pipelines.universal/ShaderLibrary/Core.hlsl"#include "Packages/com.unity.render-pipelines.universal/ShaderLibrary/Lighting.hlsl"#pragma vertex vert#pragma fragment fragfloat _GroundHeight;float4 _ShadowColor;float _ShadowFalloff;half4 _BaseColor;sampler2D _BaseMap;float4 _BaseMap_ST;float _Clipping;half _Cutoff;struct appdata{float4 vertex : POSITION;float2 uv : TEXCOORD0;};struct v2f{float4 vertex : SV_POSITION;float4 color : COLOR;float2 uv : TEXCOORD0;};float3 ShadowProjectPos(float4 vertPos){float3 shadowPos;//得到顶点的世界空间坐标float3 worldPos = mul(unity_ObjectToWorld , vertPos).xyz;//灯光方向Light mainLight = GetMainLight();float3 lightDir = normalize(mainLight.direction);//阴影的世界空间坐标(低于地面的部分不做改变)shadowPos.y = min(worldPos .y , _GroundHeight);shadowPos.xz = worldPos .xz - lightDir.xz * max(0 , worldPos .y - _GroundHeight) / lightDir.y; return shadowPos;}float GetAlpha (v2f i) {float alpha = _BaseColor.a;alpha *= tex2D(_BaseMap, i.uv.xy).a;return alpha;}v2f vert (appdata v){v2f o;//得到阴影的世界空间坐标float3 shadowPos = ShadowProjectPos(v.vertex);//转换到裁切空间// o.vertex = UnityWorldToClipPos(shadowPos);o.vertex = TransformWorldToHClip(shadowPos);//得到中心点世界坐标float3 center = float3(unity_ObjectToWorld[0].w , _GroundHeight , unity_ObjectToWorld[2].w);//计算阴影衰减float falloff = 1-saturate(distance(shadowPos , center) * _ShadowFalloff);//阴影颜色o.color = _ShadowColor;o.color.a *= falloff;o.uv = TRANSFORM_TEX(v.uv, _BaseMap);return o;}half4 frag (v2f i) : SV_Target{if (_Clipping){float alpha = GetAlpha(i);// clip(alpha - _Cutoff);i.color.a *= step(_Cutoff, alpha);// i.color.a = alpha;}return i.color;}ENDHLSL}}
}
拓展阅读
关于Unity屏幕空间阴影贴图(Screen Space Shadow Map)的实现原理,推荐阅读《Shader入门精要》9.4 Unity的阴影,网上也有很多文章有介绍。
Unity手游开发札记——使用Fast Shadow Receiver优化渲染效率 84
Funny David:Unity手游开发札记——使用Fast Shadow Receiver优化渲染效率zhuanlan.zhihu.com
Unity3D游戏制作(三)——移动平台上的角色阴影制作
Unity3D游戏制作(三)--移动平台上的角色阴影制作_游戏点滴-CSDN博客blog.csdn.net
Unity实现简易体积光/体积阴影 155
zzeero:Unity实现简易体积光/体积阴影zhuanlan.zhihu.com
屏幕空间深度阴影贴图(Screen Space Deep Shadow Maps) 185
凯奥斯:屏幕空间深度阴影贴图(Screen Space Deep Shadow Maps)zhuanlan.zhihu.com
Unity3D 基于ShadowMap的平滑硬阴影
Unity3D 基于ShadowMap的平滑硬阴影www.cnblogs.com
浅谈Unity的实时阴影 7
will Yan:浅谈Unity的实时阴影zhuanlan.zhihu.com
Unity实时阴影实现——Screen Space Shadow Mapping 54
YOung:Unity实时阴影实现——Screen Space Shadow Mappingzhuanlan.zhihu.com
有收获的话不妨点个赞支持一下啊~
2d shader unity 阴影_【Unity Shader】平面阴影(Planar Shadow)相关推荐
- Shader山下(二十七)平面阴影
以下摘录自<Unity 3D ShaderLab开发实战详解>,第10章. 转注:这是一个非常简单且廉价的实时假阴影,比较常用到,据我所知,王者荣耀中就使用了类似的手法.缺点就是比较假,只 ...
- 边坡稳定性分析软件slope/w用户指南_岩石边坡平面滑动(Planar Sliding)稳定性分析...
1 引言 在<边坡工程---岩体边坡的破坏模式>一文中,简要描述了岩石边坡的破坏模式,主要有平面破坏,楔形破坏,倾倒破坏和圆形破坏四种破坏模式.作为<边坡工程>课程的一部分,本 ...
- 8.3实例程序:平面阴影
在场景中被灯光照射的地方会产生阴影,这将使场景变的更真实.在这一部分我们将演示怎样实现平面阴影,即在平面上的阴影(如图8.5). 使用这种阴影只是一种权宜之计,虽然它增强了场景的真实效果,但是这并不是 ...
- Unity Shader - Planar Shadow - 平面阴影
文章目录 整体运行效果 思路 Shader 问题 Z-Fighting,解决:按法线方向偏移一丢丢 绘制 Alpha 混合重叠,解决:使用 stencil buffer 来规避 为何出现这个问题 解决 ...
- Unity Shader - URP ShadowCast ShadowRecieve - 投影 和 接受阴影
文章目录 Shadow Caster Using URP Shadow Caster Pass Using Custom Shadow Caster Pass 先来看看 [没有] apply shad ...
- unity 半透明混合问题_Unity 实时 半透明 阴影 shader
简单阴影制作思路: 1:在角色脚底 放置一块平板 2:shader中 根据平板传入的矩阵 以及 光照 对角色进行变换 3: 得到投影在地面上的阴影 4:阴影直接渲染到 屏幕上 缺点: 上面的阴影无法 ...
- unity 2020 怎么写shader使其接受光照?_如何在Unity中造一个PBR Shader轮子
之前有业界大佬建议我去了解下Unity的PBR.说来惭愧,我查找了下资料才发现自己在这方面的知识居然是一片空白.经过几周的学习与尝试我对这一块算是有了初步的了解,于是写了这篇文章,一方面对自己学到的东 ...
- shader 反射 水面_【Unity Shader】模拟水面包含折射与反射与波浪动画
最近研究了一下Unity官方的BoatAttack案例,他们模拟的海浪效果很厉害,用的是Gerstner波来模拟水面起伏和波峰的白浪还有浮力系统,还做加入了焦散效果(Caustics)和平面反射(Pl ...
- Unity 2D图片的3D效果(1)——阴影
项目为方便编辑地图,使用的是Unity自带的Tilemap功能.因为使用的是俯视视角,拼好第一个地图后发现2D图片展现的效果太平了,地面和障碍物很难分辨.所以才有了"把2d图片展现出3d效果 ...
最新文章
- FORTRAN学习记录(持续更新)
- 【易创课堂】第3期,最后2天报名啦!
- innodb_flush_log_at_trx_commit配置
- ldap数据库--ODSEE--复制协议
- 为什么MCU也要支持AI功能?
- Android之提示Cannot call this method while RecyclerView is computing a layout or scrolling
- python编写命令行框架_Python2和3的面向命令的命令行框架是什么?
- 网上图片的几种保存方法
- iis7.5 php isapi映射,IIS7、iis7.5让ISAPI扩展DLL执行的方法
- MDM数据分析设计方案
- 色织物数据集YDFID-1
- java 实现限流器,可用于Rest接口请求处理 | Java工具类
- 微信公众号开发—入门系列(一)
- Nuxt.js框架启动报错✖ 224 problems (146 errors, 78 warnings) 146 errors and 74 warnings potentially fixab
- 保研面试中常见的英语问题有哪些?
- TC-Traffic Control in Linux
- apache虚拟主机配置(壹)
- 青云志 java_青云志手游纯手工架设含本地注册教程
- 诺奖罗杰.彭罗斯的量子意识及其他(含朱清时-科学与佛学 77分钟视频)
- 微信小程序带清除按钮和搜索记录的实时搜索页面
热门文章
- 大一新生开学考计算机知识点,2018年大一新生入学考试科目及考试资料和内容解读...
- Android进阶:框架打造之IOC框架
- 【译】如何写出一份优秀的软件设计文档
- 【李宏毅2020 ML/DL】P83 Generative Adversarial Network | Evaluation
- 【Computer Organization笔记17】大实验讨论:各组数据通路展示
- 【JAVA基础知识总结】JAVA对象转型之上转型对象与下转型对象
- 深入PHP内核之ZVAL
- [转载]Jquery mobile 新手问题总汇
- android socket编程实例
- 正经人一辈子都用不到的 JavaScript 方法总结 (二)