原创作品

Unity Shader——被遮挡的部分半透高亮显示

唐福幸 发表于   2021-10-22 11:22:57
3159
2
6

我们在玩游戏的时候,经常会看到这样的效果:当我们的角色走到墙壁后边的时候,角色被挡住的部分会半透明高亮显示,没有被挡住的部分会正常显示。

这样的效果是怎么实现的呢?主要分为以下两部分:

1.默认情况下,模型如果有一部分被其他物体遮挡,在进行深度测试的时候是不会被通过的,所以最终不会被渲染出来。如果想让被遮挡的部分渲染出来,那就只能让他通过深度测试,然后跟已存在的像素进行混合,从而产生半透明效果。


2.模型未被遮挡的部分走正常渲染流程即可。


开放变量

Shader "Custom/OcclusionShader"{
   Properties
   {
       //part of being occlusion        _Color("Occlusion Color", Color) = (0,1,1,1)
       _Width("Occlusion Width", Range(0, 10)) = 1
       _Intensity("Occlusion Intensity",Range(0, 10)) = 1

       //ordinary part        _Albedo("Albedo", 2D) = "white"{}
       _Specular("Specular (RGB-A)", 2D) = "black"{}
       _Normal("Nromal", 2D) = "bump"{}
       _AO("AO", 2D) = "white"{}
   }

上半部分的变量用于控制被遮挡部分。_Color用于控制颜色;_Width控制边缘高亮的宽度;_Intensity控制高亮的亮度。

下半部分是正常PBR Specular工作流的部分,为了节省资源,我把Shininess放在了_Specular的a通道里。

被遮挡部分

SubShader{
   Tags{"Queue" = "Transparent"}
   Pass
   {
       ZTest Greater
       ZWrite Off

       Blend SrcAlpha OneMinusSrcAlpha

       CGPROGRAM
       #pragma vertex vert        #pragma fragment frag        #include "UnityCG.cginc"
       struct v2f
       {
           float4 worldPos : SV_POSITION;
           float3 viewDir : TEXCOORD0;
           float3 worldNor : TEXCOORD1;
       };

       fixed4 _Color;
       fixed _Width;
       half _Intensity;

       v2f vert(appdata_base v)
       {
           v2f o;
           o.worldPos = UnityObjectToClipPos(v.vertex);
           o.viewDir = normalize(WorldSpaceViewDir(v.vertex));
           o.worldNor = UnityObjectToWorldNormal(v.normal);

           return o;
       }

       float4 frag(v2f i) : SV_Target
       {
           half NDotV = saturate( dot(i.worldNor, i.viewDir));
           NDotV = pow(1 - NDotV, _Width) * _Intensity;

           fixed4 color;
           color.rgb = _Color.rgb;
           color.a = NDotV;
           return color;
       }

       ENDCG
   }

这一部分主要做了以下操作:

1.我们想要被遮挡的部分透明显示,所以把shader的渲染序列设为Transparent


2.我们想要被遮挡的部分仍然通过深度测试,所以把测试方式改为Greater,也就是说当前面有物体挡住它的时候,它就会显示出来


3.我们只需要使用普通的透明混合方式即可,所以使用Blend SrcAlpha OneMinusSrcAlpha


4.考虑到性能,我们在这一部分使用顶点-片段着色器。顶点着色器中使用内置的appdata_base结构体作为输入,然后输出包含“裁切空间顶点坐标”、“世界空间视线向量”和“世界空间法线向量”的v2f结构体。


5.在片段着色器中,我们把视线向量和法线向量进行点乘,然后使用saturate函数得到范围[0,1]内的数值,从而得到中间白边缘黑的效果。1减去这个结果,得到反相效果,也就是边缘白,中间黑的效果。使用pow函数,控制边缘的宽度,然后乘以一个变量控制边缘最终的亮度,最终输入给透明度,实现半透效果。


6.把_Color输出给颜色。

第二部分

        CGPROGRAM
       #pragma surface surf StandardSpecular        #pragma target 3.0
       struct Input
       {
           float2 uv_Albedo;
       };

       sampler2D _Albedo;
       sampler2D _Specular;
       sampler2D _Normal;
       sampler2D _AO;
       sampler2D _Emission;

       void surf(Input IN, inout SurfaceOutputStandardSpecular o)
       {
           o.Albedo = tex2D(_Albedo, IN.uv_Albedo).rgb;

           fixed4 specular = tex2D(_Specular, IN.uv_Albedo);
           o.Specular = specular.rgb;
           o.Smoothness = specular.a;

           o.Normal = UnpackNormal(tex2D(_Normal, IN.uv_Albedo));

           o.Occlusion = tex2D(_AO, IN.uv_Albedo).a;
       }
       ENDCG
   }
   Fallback "Diffuse"}

第二部分就是普通的Surface Shader了,没什么特殊的,这里就不累述了。

材质面板

最终效果

soogif(3).gif



没有标签
确定
评论(2)
跪舔大神
大佬可否分享一下这个shder啊,
回复
281天前
虚幻吴彦祖
透视挂是不是就是这样实现的
回复
1364天前
没有更多啦~