美文网首页
ShadersRoom - 遮挡透明

ShadersRoom - 遮挡透明

作者: 雨天到处晃 | 来源:发表于2019-11-20 12:09 被阅读0次

    先上一张效果图:


    OutLine.gif

    物体在被遮挡后,被遮挡的部分边缘高亮显示,实现这效果大致思路是:
      使用2个pass块来渲染这个物体,没有被遮挡的部分正常显示,被遮挡的部分则使用边缘光显示。

    这里需要引入一个知识点就是深度值深度缓存(z-buffer)
    在场景中的每一个物体都有自己的深度值,距离摄像机越近深度越小,越远越大;
    而在屏幕上显示的每个像素点都有一个深度缓存(z-buffer),会对当前需要渲染像素的深度值进行排序,默认情况下是深度值小的像素覆盖深度值大的像素;
    所以说,在渲染队列一样的情况下,近距离的像素也就是深度值小的像素会被渲染。

    Unity中提供了ZWrite 和 ZTest对应深度写入和深度测试:
      ZTest控制深度测试的模式,有如下参数,默认是LEqual:


    ZTest

      ZWrite则用来控制,当测试通过时,是否需要写入到深度缓存(z-buffer)中,On为写入,off不写入,如果未通过测试的话也不会写入。


    ZWrite

      好了,有上述的解释后,接下来实现一shader的两个pass块,第一个pass用来显示被遮挡的模型(注意代码中的注释解释),第二个pass则正常显示;

    Properties
        {
            _MainTex ("Texture", 2D) = "white" {}
            _EdgeColor("Edge Color", Color) = (1,1,1,1)
        }
     SubShader
        {
            Tags { "RenderType"="Opaque" }
    
            Pass
            {
    
                Name "Edge pass"      //显示被遮挡时的pass
                ZTest Greater         //Greater表示像素点深度值大的通过,这样就可以显示被遮挡住的模型了。
    
                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;
                float4 _EdgeColor;
                
                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
                {
                    return _EdgeColor;
                }
                ENDCG
            }
    
            Pass
            {
            
                Name "Model pass"
                ZTest Less 
    
                CGPROGRAM
                #pragma vertex vert
                #pragma fragment frag
    
                
                #include "UnityCG.cginc"
    
                struct appdata
                {
                    float4 vertex : POSITION;
                    float2 uv : TEXCOORD0;
                };
    
                struct v2f
                {
                    float2 uv : TEXCOORD0;
                    UNITY_FOG_COORDS(1)
                    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);
                    UNITY_TRANSFER_FOG(o,o.vertex);
                    return o;
                }
                
                fixed4 frag (v2f i) : SV_Target
                {
                    fixed4 col = tex2D(_MainTex, i.uv);
                    return col;
                }
                ENDCG
            }
        }
    }
    

    完成上述功能后,显示被遮挡模型的功能就实现啦:

    OutLine_2.gif

      然后在优化一下被遮挡的显示效果,单纯的显示这个颜色显然是不太好看的,这里我们使用到的是边缘光,同时和遮挡的物体做一个透明度混合。
      首先是透明度混合,在第一个pass块中加入blend one one:

        Pass
            {
    
                Name "Edge pass"
                ZTest Greater
                Blend One One
    
    

    透明度混合常用的几种方式如下,可以自行测试一下:

              Blend SrcAlpha OneMinusSrcAlpha // 传统透明度
              Blend One OneMinusSrcAlpha // 预乘透明度
              Blend One One // 叠加
              Blend OneMinusDstColor One // 柔和叠加
              Blend DstColor Zero // 相乘——正片叠底
              Blend DstColor SrcColor // 两倍相乘
    

      接下来就是边缘光的实现:
      正常来说,物体法线与视线(从顶点至相机的方向)角度越一致,则表示越接近能被玩家看见的中间,物体的边缘一般与视线垂直,所以通过物体法线和视线的点乘即可计算轮廓光,即1-dot(normal,view).

    dot(normal,view)的值越到中心越大,现在我们需要的是越到边缘越大,则使用1减去该值即可。

    下面添加一些计算边缘光的代码参数:
      加入一个控制边缘光范围的参数,

        Properties
        {
            _MainTex ("Texture", 2D) = "white" {}
            _EdgeColor("Edge Color", Color) = (1,1,1,1)
            _OutLine("OutTime",range(0.5,2)) = 1   //在属性面板中加入一个控制边缘光范围的参数
        }
    

      加入法线和视线方向,

    struct appdata
                {
                    float4 vertex : POSITION;
                    float2 uv : TEXCOORD0;
    
                    float3 normal : NORMAL;   //取得模型的法线
    
                };
                struct v2f
                {
                    float2 uv : TEXCOORD0;
                    float4 vertex : SV_POSITION;
    
                    float3 normal : NORMAL;  //法线的方向
                    float3 viewDir : TEXCOORD1;//视线的方向
                };
    

      在顶点函数中进行坐标系的转化以及计算视野方向,

      v2f vert (appdata v)
                {
                    v2f o;
                    o.vertex = UnityObjectToClipPos(v.vertex);
                    o.uv = TRANSFORM_TEX(v.uv, _MainTex);
    
                    o.normal = UnityObjectToWorldNormal(v.normal);   //将法线坐标转化到世界坐标
                    o.viewDir = normalize(_WorldSpaceCameraPos.xyz - mul(unity_ObjectToWorld, v.vertex).xyz);  //计算视野的方向
                    return o;
                }
    

      最后在片元函数中计算边缘光的颜色,

    fixed4 frag (v2f i) : SV_Target
                {
                    float NdotV = 1 - dot(i.normal, i.viewDir) * _OutLine;  //计算边缘光,OutLine越大靠近边缘
                    return _EdgeColor * NdotV;
                }
    

    完成到这一步,遮挡透明的效果就和一开始的动图一样啦,下面附上完整的shader:

    Shader "ShadersRoom/OutLine" {
        Properties
        {
            _MainTex ("Texture", 2D) = "white" {}
            _EdgeColor("Edge Color", Color) = (1,1,1,1)
            _OutLine("OutTime",range(0.5,2)) = 1
        }
        SubShader
        {
            Tags { "RenderType"="Opaque" }
    
            Pass
            {
    
                Name "Edge pass"
                ZTest Greater
                Blend One One
    
    
                CGPROGRAM
                #pragma vertex vert
                #pragma fragment frag
    
                
                #include "UnityCG.cginc"
    
                struct appdata
                {
                    float4 vertex : POSITION;
                    float2 uv : TEXCOORD0;
    
                    float3 normal : NORMAL;
    
                };
    
                struct v2f
                {
                    float2 uv : TEXCOORD0;
                    float4 vertex : SV_POSITION;
    
                    float3 normal : NORMAL;
                    float3 viewDir : TEXCOORD1;
                };
    
                sampler2D _MainTex;
                float4 _MainTex_ST;
                float4 _EdgeColor;
                float _OutLine;
                
                v2f vert (appdata v)
                {
                    v2f o;
                    o.vertex = UnityObjectToClipPos(v.vertex);
                    o.uv = TRANSFORM_TEX(v.uv, _MainTex);
    
                    o.normal = UnityObjectToWorldNormal(v.normal);   //将法线坐标转化到世界坐标
                    o.viewDir = normalize(_WorldSpaceCameraPos.xyz - mul(unity_ObjectToWorld, v.vertex).xyz);  //计算视野的方向
    
                    return o;
                }
                
                fixed4 frag (v2f i) : SV_Target
                {
                    float NdotV = 1 - dot(i.normal, i.viewDir) * _OutLine;  //计算边缘光,OutLine越大靠近边缘
                    return _EdgeColor * NdotV;
                }
                ENDCG
            }
    
            Pass
            {
            
                Name "Model pass"
                ZTest Less 
    
                CGPROGRAM
                #pragma vertex vert
                #pragma fragment frag
    
                
                #include "UnityCG.cginc"
    
                struct appdata
                {
                    float4 vertex : POSITION;
                    float2 uv : TEXCOORD0;
                };
    
                struct v2f
                {
                    float2 uv : TEXCOORD0;
                    UNITY_FOG_COORDS(1)
                    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);
                    UNITY_TRANSFER_FOG(o,o.vertex);
                    return o;
                }
                
                fixed4 frag (v2f i) : SV_Target
                {
                    fixed4 col = tex2D(_MainTex, i.uv);
                    return col;
                }
                ENDCG
            }
        }
    }
    
    

    相关文章

      网友评论

          本文标题:ShadersRoom - 遮挡透明

          本文链接:https://www.haomeiwen.com/subject/wyinqqtx.html