赞
踩
最近通关了《What Remains of Edith Finch》(艾迪芬奇的记忆),总体来说应该算是一个剧情+解密向的游戏,故事+表现手法十分出色。
游戏主要是叙述一个神秘的家族遭遇了一系列类似《死神来了》的故事,家族的人离奇死亡,每一个成员的故事都是不同的游戏模式和表现风格,比如下面的连环画小游戏的卡通渲染风格,游戏中的书,书中的游戏:
虽然我在入手之前还曾经犹豫过,看到大多数玩家的游戏时长才两个多小时,敢卖六十八块大洋,不过这两个小时玩下来,整体给我的视觉+感受冲击一波高过一波,到最后的罐头厂工人和他的理想王国那一段给我的印象最为深刻,甚至有种《千与千寻》的那种天马行空的感觉,不得不说开发者的脑洞太给力啦。
恩,游戏体验记录到此结束,下面才是本文的正题。今天来玩一个好玩的效果,Decal。
Decal(贴花)。这个效果其实非常常见,比如人物脚底的选中圈,还有最著名的就是CS里面的喷漆效果了,当年大战的时候没少和人互喷,要是说到CS(Quake),那这个技术真的是相当古老了。不过Decal的实现方式有很多种,今天来分别实现一下Self Decal,Additive贴片叠加,Projector投影,Mesh Decal,Deferred Decal(Depth Normal 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值进行插值:
- /********************************************************************
- FileName: SelfDecal.shader
- Description: SelfDecal二套uv自身贴花效果
- history: 12:7:2018 by puppet_master
- https://blog.csdn.net/puppet_master
- *********************************************************************/
- Shader "Decal/SelfDecal"
- {
- Properties
- {
- _MainTex ("Texture", 2D) = "white" {}
- _DecalTex ("Decal", 2D) = "black" {}
- }
-
- SubShader
- {
- Tags { "RenderType"="Opaque" }
- LOD 100
-
- Pass
- {
- CGPROGRAM
- #pragma vertex vert
- #pragma fragment frag
-
- #include "UnityCG.cginc"
-
- struct appdata
- {
- float4 vertex : POSITION;
- float2 uv : TEXCOORD0;
- float2 uv_decal : TEXCOORD1;
- };
-
- struct v2f
- {
- float4 uv : TEXCOORD0;
- float4 vertex : SV_POSITION;
- };
-
- sampler2D _MainTex;
- sampler2D _DecalTex;
- float4 _DecalTex_ST;
-
- v2f vert (appdata v)
- {
- v2f o;
- o.vertex = UnityObjectToClipPos(v.vertex);
- o.uv.xy = v.uv;
- //offset,tiling二套uv调整不同的decal
- o.uv.zw = TRANSFORM_TEX(v.uv_decal, _DecalTex);
- return o;
- }
-
- fixed4 frag (v2f i) : SV_Target
- {
- fixed4 col = tex2D(_MainTex, i.uv.xy);
- fixed4 decal = tex2D(_DecalTex, i.uv.zw);
-
- col.rgb = lerp(col.rgb, decal.rgb, decal.a);
-
- return col;
- }
- ENDCG
- }
- }
- }
![](https://csdnimg.cn/release/blogv2/dist/pc/img/newCodeMoreWhite.png)
这里我们直接用二套uv进行decal贴花,可以更加容易控制需要细节部分的贴花同时也不影响主纹理的效果。于是我掏出了使用十分不熟练的3DMax给我最近常用的小狮子展了二套uv,贴图的话就使用我最喜欢的羊驼表情包啦:
效果如下,在石狮子的胸前就贴上了羊驼的头像,2333:
由于我们通过TRANSFORM_TEX对二套uv进行了处理,所以我们可以很容易地通过二套uv的Tiling和Offset来修改贴花显示的内容,来一个羊驼表情幻灯片:
上面我们实现了自身Decal效果。但是也有很大的限制,比如我们希望在两个对象连接处叠加一个Decal,我们不方便给每个东西都额外去展uv,直接用世界空间或者物体空间坐标采样也不一定靠谱。其实我们可以考虑一种更简单的方法实现Decal,甚至不需要写代码。因为这个Shader实在太常见啦。可以说是最简单粗暴,而又很容易实现贴花细节的一种方法。
我们可以使用Particle Alpha Blended或者Mobile版本的Alpha Blended Shader,简单来说就是最基本的采样贴图输出的AlphaBlend的Shader就可以达到效果,比如下面的Shader:
- /********************************************************************
- FileName: AlphaBlendedDecal.shader
- Description: AlphaBlend Shader
- history: 7:12:2018 by puppet_master
- https://blog.csdn.net/puppet_master
- *********************************************************************/
- Shader "Decal/AlphaBlendedDecal"
- {
- Properties
- {
- _MainTex ("Texture", 2D) = "white" {}
- }
- SubShader
- {
- Tags { "Queue"="Transparent" "IgnoreProjector"="True" "RenderType"="Transparent" "PreviewType"="Plane" }
-
- Blend SrcAlpha OneMinusSrcAlpha
- Lighting Off
- ZWrite Off
-
- Pass
- {
- CGPROGRAM
- #pragma vertex vert
- #pragma fragment frag
-
- #include "UnityCG.cginc"
-
- struct appdata
- {
- float4 vertex : POSITION;
- float2 uv : TEXCOORD0;
- };
-
- struct v2f
- {
- float2 uv : TEXCOORD0;
- float4 vertex : SV_POSITION;
- };
-
- sampler2D _MainTex;
- float4 _MainTex_ST;
-
- v2f vert (appdata v)
- {
- v2f o;
- o.vertex = UnityObjectToClipPos(v.vertex);
- o.uv = TRANSFORM_TEX(v.uv, _MainTex);
- return o;
- }
-
- fixed4 frag (v2f i) : SV_Target
- {
- fixed4 col = tex2D(_MainTex, i.uv);
- return col;
- }
- ENDCG
- }
- }
- }
![](https://csdnimg.cn/release/blogv2/dist/pc/img/newCodeMoreWhite.png)
这一次,我们就可以找一个面片,然后把我们的需要作为Decal的内容赋给面片,然后把面片贴在地面上或者墙上就可以得到效果啦。很多游戏场景中的地面,墙面细节,如特殊的污渍,血迹,如果不用SelfDecal或者地形刷混合权重的方式的话,直接叠上去一个面片也是一个不错的选择。比如下面我在地面上叠了两个血迹的Decal,即使在两个地砖接缝也可以使用:
上面的两种Decal可以实现很多贴花的效果了,但是二者都有自身的局限。Self Decal可以贴合任意物体,但是需要物体自身展uv,无法实现一个Decal覆盖多个物体,仅适用于一些预制的贴花替换;而Alpha Blend叠片方式的Decal,可以任意摆放位置,也可以跨物体摆放,但是有一个很重要的问题,如果是平面,如墙面和地面,我们可以直接使用一个面片叠加,而如果是复杂物体之间的贴花效果,直接用面片叠加无法完美贴合,自己做一个贴合的模型也不现实。所以,是时候找一找普适性更高一些的贴花方法了(不过,上面两种贴花也很常用,毕竟如果用简单的方法就能达到效果,何必要浪费时间和性能呢)。
Unity内置的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代码如下:
- /********************************************************************
- FileName: ProjectorDecal.shader
- Description: Projector Decal Shader
- history: 7:12:2018 by puppet_master
- https://blog.csdn.net/puppet_master
- *********************************************************************/
- Shader "Decal/ProjectorDecal"
- {
- Properties
- {
- _Color ("Main Color", Color) = (1,1,1,1)
- _DecalTex ("Cookie", 2D) = "" {}
- _FalloffTex ("FallOff", 2D) = "white" {}
- }
-
- Subshader
- {
- Pass
- {
- ZWrite Off
- Fog { Color (0, 0, 0) }
- ColorMask RGB
- Blend SrcAlpha OneMinusSrcAlpha
- Offset -1, -1
-
- CGPROGRAM
- #pragma vertex vert
- #pragma fragment frag
- #include "UnityCG.cginc"
-
- struct v2f
- {
- float4 uvDecal : TEXCOORD0;
- float4 uvFalloff : TEXCOORD1;
- float4 pos : SV_POSITION;
- };
-
- float4x4 unity_Projector;
- float4x4 unity_ProjectorClip;
- fixed4 _Color;
- sampler2D _DecalTex;
- sampler2D _FalloffTex;
-
- v2f vert (float4 vertex : POSITION)
- {
- v2f o;
- o.pos = UnityObjectToClipPos (vertex);
- o.uvDecal = mul (unity_Projector, vertex);
- o.uvFalloff = mul (unity_ProjectorClip, vertex);
- return o;
- }
-
- fixed4 frag (v2f i) : SV_Target
- {
- fixed4 decal = tex2Dproj (_DecalTex, UNITY_PROJ_COORD(i.uvDecal));
- decal *= _Color;
-
- fixed falloff = tex2Dproj (_FalloffTex, UNITY_PROJ_COORD(i.uvFalloff)).r;
- decal *= falloff;
- return decal;
- }
-
- ENDCG
- }
- }
- }
![](https://csdnimg.cn/release/blogv2/dist/pc/img/newCodeMoreWhite.png)
这样,我们直接在场景中挂一个Projector组件,然后使用上面Shader的材质,将投影器对准我们需要投影的对象,就可以得到完美贴合多个不规则对象的投影效果了,如下图,血迹的效果完美贴合在了地面和石狮子上:
通过FrameDebugger我们可以看到,Projector的Decal是通过额外的Pass渲染的,上图的石狮子和地面都额外进行了一次渲染,所以Projector也是由一定的性能代价的,如果一个Projector覆盖太多的物体可能会导致批次上升:
Unity内置的Projector基本已经可以满足我们的需求了,不过还是了解一下Projec
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。