当前位置:   article > 正文

Unity Shader-Decal贴花(SelfDecal,Alpha Blend,Mesh Decal,Projector,Deferred Decal)_unity urp decal projector 全景

unity urp decal projector 全景

前言

最近通关了《What Remains of Edith Finch》(艾迪芬奇的记忆),总体来说应该算是一个剧情+解密向的游戏,故事+表现手法十分出色。

游戏主要是叙述一个神秘的家族遭遇了一系列类似《死神来了》的故事,家族的人离奇死亡,每一个成员的故事都是不同的游戏模式和表现风格,比如下面的连环画小游戏的卡通渲染风格,游戏中的书,书中的游戏:

虽然我在入手之前还曾经犹豫过,看到大多数玩家的游戏时长才两个多小时,敢卖六十八块大洋,不过这两个小时玩下来,整体给我的视觉+感受冲击一波高过一波,到最后的罐头厂工人和他的理想王国那一段给我的印象最为深刻,甚至有种《千与千寻》的那种天马行空的感觉,不得不说开发者的脑洞太给力啦。

恩,游戏体验记录到此结束,下面才是本文的正题。今天来玩一个好玩的效果,Decal。

简介

Decal(贴花)。这个效果其实非常常见,比如人物脚底的选中圈,还有最著名的就是CS里面的喷漆效果了,当年大战的时候没少和人互喷,要是说到CS(Quake),那这个技术真的是相当古老了。不过Decal的实现方式有很多种,今天来分别实现一下Self Decal,Additive贴片叠加,Projector投影,Mesh Decal,Deferred Decal(Depth Normal Decal)这几种方式。

Self Decal

我在网上查了半天,似乎也没有对这种贴花方式的命名,索性就叫它Self Decal吧。也可能是这种方式太捞(low)了,大佬们都不care了。所谓Self,也就是直接在同一个Shader里面在叠加一张贴图方式。可以直接采样uv,也可以将mesh制作第二套uv。相比于直接把贴图都做在Albedo中,使用Decal可以更加方便的替换贴图,而且二套uv方便控制特殊的效果,比如捏脸系统里面经常有的脑门上画个咒印之类的。

关于这种Decal,不做太多介绍了。Unity官方资源也包含一个名叫Decal的Surface Shader,我这里就直接简单粗暴地用Unlit类型的vf shader了。最后的叠加使用的是类似Alpha Blend的SrcAlpha OneMinusSrcAlpha,将Decal的颜色和原始颜色根据Decal的Alpha值进行插值:

  1. /********************************************************************
  2. FileName: SelfDecal.shader
  3. Description: SelfDecal二套uv自身贴花效果
  4. history: 12:7:2018 by puppet_master
  5. https://blog.csdn.net/puppet_master
  6. *********************************************************************/
  7. Shader "Decal/SelfDecal"
  8. {
  9. Properties
  10. {
  11. _MainTex ("Texture", 2D) = "white" {}
  12. _DecalTex ("Decal", 2D) = "black" {}
  13. }
  14. SubShader
  15. {
  16. Tags { "RenderType"="Opaque" }
  17. LOD 100
  18. Pass
  19. {
  20. CGPROGRAM
  21. #pragma vertex vert
  22. #pragma fragment frag
  23. #include "UnityCG.cginc"
  24. struct appdata
  25. {
  26. float4 vertex : POSITION;
  27. float2 uv : TEXCOORD0;
  28. float2 uv_decal : TEXCOORD1;
  29. };
  30. struct v2f
  31. {
  32. float4 uv : TEXCOORD0;
  33. float4 vertex : SV_POSITION;
  34. };
  35. sampler2D _MainTex;
  36. sampler2D _DecalTex;
  37. float4 _DecalTex_ST;
  38. v2f vert (appdata v)
  39. {
  40. v2f o;
  41. o.vertex = UnityObjectToClipPos(v.vertex);
  42. o.uv.xy = v.uv;
  43. //offset,tiling二套uv调整不同的decal
  44. o.uv.zw = TRANSFORM_TEX(v.uv_decal, _DecalTex);
  45. return o;
  46. }
  47. fixed4 frag (v2f i) : SV_Target
  48. {
  49. fixed4 col = tex2D(_MainTex, i.uv.xy);
  50. fixed4 decal = tex2D(_DecalTex, i.uv.zw);
  51. col.rgb = lerp(col.rgb, decal.rgb, decal.a);
  52. return col;
  53. }
  54. ENDCG
  55. }
  56. }
  57. }

这里我们直接用二套uv进行decal贴花,可以更加容易控制需要细节部分的贴花同时也不影响主纹理的效果。于是我掏出了使用十分不熟练的3DMax给我最近常用的小狮子展了二套uv,贴图的话就使用我最喜欢的羊驼表情包啦:

效果如下,在石狮子的胸前就贴上了羊驼的头像,2333:

由于我们通过TRANSFORM_TEX对二套uv进行了处理,所以我们可以很容易地通过二套uv的Tiling和Offset来修改贴花显示的内容,来一个羊驼表情幻灯片:

AlphaBlend贴片Decal

上面我们实现了自身Decal效果。但是也有很大的限制,比如我们希望在两个对象连接处叠加一个Decal,我们不方便给每个东西都额外去展uv,直接用世界空间或者物体空间坐标采样也不一定靠谱。其实我们可以考虑一种更简单的方法实现Decal,甚至不需要写代码。因为这个Shader实在太常见啦。可以说是最简单粗暴,而又很容易实现贴花细节的一种方法。

我们可以使用Particle Alpha Blended或者Mobile版本的Alpha Blended Shader,简单来说就是最基本的采样贴图输出的AlphaBlend的Shader就可以达到效果,比如下面的Shader:

  1. /********************************************************************
  2. FileName: AlphaBlendedDecal.shader
  3. Description: AlphaBlend Shader
  4. history: 7:12:2018 by puppet_master
  5. https://blog.csdn.net/puppet_master
  6. *********************************************************************/
  7. Shader "Decal/AlphaBlendedDecal"
  8. {
  9. Properties
  10. {
  11. _MainTex ("Texture", 2D) = "white" {}
  12. }
  13. SubShader
  14. {
  15. Tags { "Queue"="Transparent" "IgnoreProjector"="True" "RenderType"="Transparent" "PreviewType"="Plane" }
  16. Blend SrcAlpha OneMinusSrcAlpha
  17. Lighting Off
  18. ZWrite Off
  19. Pass
  20. {
  21. CGPROGRAM
  22. #pragma vertex vert
  23. #pragma fragment frag
  24. #include "UnityCG.cginc"
  25. struct appdata
  26. {
  27. float4 vertex : POSITION;
  28. float2 uv : TEXCOORD0;
  29. };
  30. struct v2f
  31. {
  32. float2 uv : TEXCOORD0;
  33. float4 vertex : SV_POSITION;
  34. };
  35. sampler2D _MainTex;
  36. float4 _MainTex_ST;
  37. v2f vert (appdata v)
  38. {
  39. v2f o;
  40. o.vertex = UnityObjectToClipPos(v.vertex);
  41. o.uv = TRANSFORM_TEX(v.uv, _MainTex);
  42. return o;
  43. }
  44. fixed4 frag (v2f i) : SV_Target
  45. {
  46. fixed4 col = tex2D(_MainTex, i.uv);
  47. return col;
  48. }
  49. ENDCG
  50. }
  51. }
  52. }

这一次,我们就可以找一个面片,然后把我们的需要作为Decal的内容赋给面片,然后把面片贴在地面上或者墙上就可以得到效果啦。很多游戏场景中的地面,墙面细节,如特殊的污渍,血迹,如果不用SelfDecal或者地形刷混合权重的方式的话,直接叠上去一个面片也是一个不错的选择。比如下面我在地面上叠了两个血迹的Decal,即使在两个地砖接缝也可以使用:

Projector Decal

上面的两种Decal可以实现很多贴花的效果了,但是二者都有自身的局限。Self Decal可以贴合任意物体,但是需要物体自身展uv,无法实现一个Decal覆盖多个物体,仅适用于一些预制的贴花替换;而Alpha Blend叠片方式的Decal,可以任意摆放位置,也可以跨物体摆放,但是有一个很重要的问题,如果是平面,如墙面和地面,我们可以直接使用一个面片叠加,而如果是复杂物体之间的贴花效果,直接用面片叠加无法完美贴合,自己做一个贴合的模型也不现实。所以,是时候找一找普适性更高一些的贴花方法了(不过,上面两种贴花也很常用,毕竟如果用简单的方法就能达到效果,何必要浪费时间和性能呢)。

Unity内置的Projector是一个不错的选择。Projector可以做很多好玩的效果,不仅包括贴花,还可以实现阴影等效果。

Projector的使用

使用Projector,相当于Unity会对需要投影的对象使用投影材质重新渲染一遍。不过这个材质的Shader需要是特制的,而不是随便的Shader都可以得到正确的效果。因为我们需要在Shader里面根据投影器传入的Project矩阵进行uv计算,得到采样的uv值。我们可以参考Unity官方aras-p大佬的Projector Shader来编写我们自己的投影Shader,我将Blend模式改为了Blend SrcAlpha OneMinusSrcAlpha,然后直接输出颜色。Shader中采样并没有使用正常的uv坐标,而是通过两个Unity内置矩阵变换顶点,进而在fragment shader中采样,关于这两个内置矩阵的解释,可以参考这里。另外,我们可以通过FalloffTex实现在投影距离内的颜色渐变,通过采样一张falloff贴图,通过纹理的uv横向实现投影的衰减,使用Falloff有两个好处,一方面可以防止Projector背面物体也被投影,出现穿帮的问题;另一方面,通过远距离衰减可以得到更加自然的投影效果,而且也可以防止远处超出投影“视锥体”远裁剪面时出现的调变问题。投影的Shader代码如下:

  1. /********************************************************************
  2. FileName: ProjectorDecal.shader
  3. Description: Projector Decal Shader
  4. history: 7:12:2018 by puppet_master
  5. https://blog.csdn.net/puppet_master
  6. *********************************************************************/
  7. Shader "Decal/ProjectorDecal"
  8. {
  9. Properties
  10. {
  11. _Color ("Main Color", Color) = (1,1,1,1)
  12. _DecalTex ("Cookie", 2D) = "" {}
  13. _FalloffTex ("FallOff", 2D) = "white" {}
  14. }
  15. Subshader
  16. {
  17. Pass
  18. {
  19. ZWrite Off
  20. Fog { Color (0, 0, 0) }
  21. ColorMask RGB
  22. Blend SrcAlpha OneMinusSrcAlpha
  23. Offset -1, -1
  24. CGPROGRAM
  25. #pragma vertex vert
  26. #pragma fragment frag
  27. #include "UnityCG.cginc"
  28. struct v2f
  29. {
  30. float4 uvDecal : TEXCOORD0;
  31. float4 uvFalloff : TEXCOORD1;
  32. float4 pos : SV_POSITION;
  33. };
  34. float4x4 unity_Projector;
  35. float4x4 unity_ProjectorClip;
  36. fixed4 _Color;
  37. sampler2D _DecalTex;
  38. sampler2D _FalloffTex;
  39. v2f vert (float4 vertex : POSITION)
  40. {
  41. v2f o;
  42. o.pos = UnityObjectToClipPos (vertex);
  43. o.uvDecal = mul (unity_Projector, vertex);
  44. o.uvFalloff = mul (unity_ProjectorClip, vertex);
  45. return o;
  46. }
  47. fixed4 frag (v2f i) : SV_Target
  48. {
  49. fixed4 decal = tex2Dproj (_DecalTex, UNITY_PROJ_COORD(i.uvDecal));
  50. decal *= _Color;
  51. fixed falloff = tex2Dproj (_FalloffTex, UNITY_PROJ_COORD(i.uvFalloff)).r;
  52. decal *= falloff;
  53. return decal;
  54. }
  55. ENDCG
  56. }
  57. }
  58. }

这样,我们直接在场景中挂一个Projector组件,然后使用上面Shader的材质,将投影器对准我们需要投影的对象,就可以得到完美贴合多个不规则对象的投影效果了,如下图,血迹的效果完美贴合在了地面和石狮子上:

通过FrameDebugger我们可以看到,Projector的Decal是通过额外的Pass渲染的,上图的石狮子和地面都额外进行了一次渲染,所以Projector也是由一定的性能代价的,如果一个Projector覆盖太多的物体可能会导致批次上升:

Projector的原理

Unity内置的Projector基本已经可以满足我们的需求了,不过还是了解一下Projec

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/我家小花儿/article/detail/98135
推荐阅读
相关标签
  

闽ICP备14008679号