Shader笔记——描边效果

2021-11-18  本文已影响0人  莫忘初心_倒霉熊

在游戏中,为了表现道具的选中效果,通常会在被选中的物体添加一圈描边效果。
那么如何通过Shader实现物体的描边效果呢?

1.0实现原理

描边Shader有多种实现方式,可以通过后期处理实现,也可以通过MatCap实现。本案例通过俩个Pass实现的。

  1. 第一个Pass,将模型的顶点位置沿着法线膨胀一段距离,然后再为膨胀之后的模型指定一个纯色进行着色,着色不需参与任何灯光交互。
  2. 第二个Pass,模型以正常效果进行显示。

在正常情况下,第一个Pass膨胀之后的模型会将第二个Pass模型完全笼罩起来,因此第二个Pass模型无法通过深度测试,最终只会显示第一个Paas之后的效果。为了解决这个问题,需想办法让第二个Pass模型通过深度测试。方法有如下俩种:

  1. 更改第二个Pass的深度值比较方法(如,Always,Greater),但是这样会导致其他物体进行深度测试时出现错误,因此排除该方法。
  2. 关闭第一个Pass的深度写入,如此一来,深度缓存中没有第一个Pass模型的深度值,第二个Pass模型自然就可以通过深度测试了。
    因此,最终选择第二种方法。但是这又会引发另外一个问题,当物体关闭深度写入之后,后面被遮挡的物体就无法知道自己被遮挡住,本该测试失败的情况,现在反而通过了深度测试,因此绘制图像的时候就会遮挡掉已经绘制好的第一个Pass模型。
    为了避免出现这个问题,需要使该模型在所有不透明物体绘制完成之后再进行绘制,因此更改Shader渲染队列为Transparent,如此以来绘制的图像就不会被后面的物体覆盖了。

2.0实现逻辑

Shader代码如下:

Shader "Samples/Outline"
{
    Properties
    {
        [Header(Texture Group)]
        [Space(10)]
        _Albedo ("Albedo", 2D) = "white" {}
        [NoScaleOffset]_Specular ("Specular (RGB-A)", 2D) = "black" {}
        [NoScaleOffset]_Normal ("Normal", 2D) = "bump" {}
        [NoScaleOffset]_AO ("Ambient Occlusion", 2D) = "white" {}

        [Header(Outline Properties)]
        [Space(10)]
        _OutlineColor ("Outline Color", Color) = (1,0,1,1)
        _OutlineWidth ("Outline Width", Range(0, 0.1)) = 0.01
    }

    SubShader
    {
        // 本Shader属于不透明效果,因此将渲染类型设置为Opaque
        // 为了使物体在所有不透明物体之后在进行绘制,需要将渲染队列设置为"Transparent"
        Tags { "RenderType"="Opaque" "Queue" = "Transparent"}

        //---------- 描边效果 ----------
        Pass
        {
            // 为了不遮挡住后面的Pass,需要将深度写入关闭
            ZWrite Off

            CGPROGRAM
            #pragma vertex vert
            #pragma fragment frag
            #include "UnityCG.cginc"

            struct v2f
            {
                float4 vertex : SV_POSITION;
            };

            fixed4 _OutlineColor;
            fixed _OutlineWidth;

            v2f vert(appdata_base v)
            {
                v2f o;
                v.vertex.xyz += v.normal * _OutlineWidth;
                o.vertex = UnityObjectToClipPos(v.vertex);

                return o;
            }

            fixed4 frag(v2f i) : SV_Target
            {
                return _OutlineColor;
            }

            ENDCG
        }

        //---------- 正常Shader效果 ----------
        CGPROGRAM
        // 添加fullforwardshadows指令,是物体支持所有灯光类型的投影
        #pragma surface surf StandardSpecular fullforwardshadows

        struct Input
        {
            float2 uv_Albedo;
        };

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

        void surf (Input IN, inout SurfaceOutputStandardSpecular o)
        {
            fixed4 c = tex2D (_Albedo, IN.uv_Albedo);
            o.Albedo = c.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);
        }

        ENDCG
    }
}

效果如下:


描边效果

3.0 描边算法缺陷

该描边算法只适合顶点法线方向各异的模型(如球),而对于一些顶点法线方向比较一致的模型(如立方体)会出现如下缺陷。


描边算法缺陷
上一篇 下一篇

猜你喜欢

热点阅读