Unity Shader 模板测试绘制被遮挡物体的轮廓

原文链接 https://blog.csdn.net/yangxuan0261/article/details/79686192

主要分为三个步骤:

  1. 第一步:正常渲染物体,开启深度测试即LEqual,同时往Stancli buffer 里写入一个参考值,强制写入(即使深度测试失败的值也要写入,保证 角色所有像素在模板缓冲区的值 都是 参考值 Ref=1)。
  2. 绘制被遮挡的部分轮廓:首先在View空间将顶点沿着法线方向扩张(其实这里需要注意的是如果在View空间做法线扩张可能导致摄像机的远近会改变轮廓的大小,具体解决方案https://zhuanlan.zhihu.com/p/109101851)。
  3. 只绘制被挡住的部分,将第二个pass的Ref设为0,并且比较方式改为不相等;
  4. 然后绘制的时候只绘制被挡住的部分,所以ZTest改为greater。
Shader "Custom/Unlit-Texture-Outline" {
    Properties{
        _MainTex("Base (RGB)", 2D) = "white" {}
        _OutlineColor("Outline Color", Color) = (1,1,0,1) //描边颜色
        _Outline("Outline width", Range(0.0, 0.5)) = 0.03 // 描边宽度
    }

    CGINCLUDE
    #include "UnityCG.cginc"
    struct appdata_t {
        float4 vertex : POSITION;
        float2 texcoord : TEXCOORD0;
        float3 normal : NORMAL;
    };

    struct v2f {
        float4 vertex : SV_POSITION;
        half2 texcoord : TEXCOORD0;
    };

    sampler2D _MainTex;
    float4 _MainTex_ST;
    float _Outline;
    float4 _OutlineColor;

    v2f vert(appdata_t v) //第一个pass的顶点变换就是普通变换
    {
        v2f o;
        o.vertex = UnityObjectToClipPos(v.vertex);
        o.texcoord = TRANSFORM_TEX(v.texcoord, _MainTex);

        return o;
    }

    v2f vert_outline(appdata_t v) //第二个Pass的顶点变换,做法线扩张
    {
        v2f o;
        // 方式一,观察空间 下往法线偏移顶点
        float4 viewPos = mul(UNITY_MATRIX_MV, v.vertex);
        //float3 viewNorm = mul((float3x3)UNITY_MATRIX_IT_MV, v.normal);
        float3 viewNorm = mul(v.normal, (float3x3)UNITY_MATRIX_T_MV);
        float3 offset = normalize(viewNorm) * _Outline;
        viewPos.xyz += offset;
        o.vertex = mul(UNITY_MATRIX_P, viewPos);

        //方式二,世界空间 下往法线偏移顶点
        //float4 worldPos = mul(unity_ObjectToWorld, v.vertex);
        //float3 worldNormal = UnityObjectToWorldNormal(v.normal);
        //float3 offset = normalize(worldNormal) * _Outline;
        //worldPos.xyz += offset;
        //o.vertex = mul(UNITY_MATRIX_VP, worldPos);
        return o;
    }

    ENDCG

    SubShader{
        Tags{ "Queue" = "Transparent" "RenderType" = "Opaque" }

        Pass{ // 正常绘制
            Stencil
            {
                Ref 1
                Comp Always  //Comp comparisonFunction,这个比较重要,这个是比较方式。大致有Greater,GEqual,Equal等八种比较方式,具体待会列图。
                Pass Replace //Pass stencilOperation,这个是当stencil测试和深度测试都通过的时候,进行的stencilOperation操作方法。注意是都通过的时候!
                ZFail Replace //ZFail stencilOperation,这个是在stencil测试通过,但是深度测试没有通过的时候执行的stencilOperation方法。
                //Fail stencilOperation,这个是在stencil测试通过的时候执行的stencilOperation方法。这里只要stencil测试通过就可以了
            }

            ZTest LEqual 
            CGPROGRAM
            #pragma vertex vert
            #pragma fragment frag

            fixed4 frag(v2f i) : SV_Target
            {
                fixed4 col = tex2D(_MainTex, i.texcoord);
                return col;
            }

            ENDCG
        }

        Pass{ // 遮挡部分绘制描边
            ZTest Greater  //只绘制被遮挡的部分
            ZWrite Off //关闭深度写入
            //Blend DstAlpha OneMinusDstAlpha

            Stencil{
                Ref 1
                Comp NotEqual

            }
            CGPROGRAM
            #pragma vertex vert_outline
            #pragma fragment frag
            half4 frag(v2f i) :COLOR
            {
                return _OutlineColor;
            }
            ENDCG
        }
    }
}
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值