Unity3D 双面渲染Shader实现

概述

在建模软件中可以为每个顶点设置法线,三个顶点可以组成一个三角面,三角面的法线是根据其顶点的法线插值而来,这个法线朝向就代表了这个平面的正面。
而在现代大部分引擎和游戏中,会默认只显示平面的正面,即对每个三角面进行一次面法线和摄像机出射向量的点积,判断正负,如果当前平面是背面朝向摄像机,则不渲染该面片。
Unity 的standard shader就是这样,我们只要创建一个平面就能看出这种效果。

创建一个平面,默认此平面正面朝上。
请添加图片描述
我们调整角度从下往上看,平面完全不渲染了。除了一个选中的什么都看不见(当然大家都知道这个选中框肯定不会在游戏中渲染出来)
请添加图片描述

这种作法主要是为了提高性能,因为除了极个别图形例如上面的单纯平面之外,很多模型的面片背面是完全看不见的。
想象一下正方形就可以得知,正方形六个面每个的背面,都完全被包裹起来隐藏在正方形内部,正常情况是完全看不见的,那么此时直接不渲染背面也完全没有影响,还能足足减少一半需要渲染的面片。

但是总有时候是会需要渲染背面面片的,常见情况有渲染一些破损的模型,可以从模型外部看到内部,或者是半透明的效果,也需要能看到一个模型的内部。

这种效果俗称为双面渲染,意思就是同时渲染模型正面和背面,在unity3d里实现双面渲染相当简单,第一种做法是用两个pass,第一个渲染正面第二个渲染背面。

第一个pass如下,因为破损的模型不太好找,我们直接用一张贴图加上clip函数来实现可以看到一个模型内部的效果。
首先开启 Cull Back来只渲染正面

		Cull Back
        void surf(Input IN, inout SurfaceOutputStandard o)
        {
    
    
            clip(tex2D(_Mask, IN.uv_Mask).a - _MaskClipValue);
            fixed4 c = tex2D(_BackTex, IN.uv_BackTex) * _Color;
            o.Albedo = c.rgb;
            o.Metallic = _Metallic;
            o.Smoothness = _Glossiness;
            o.Alpha = c.a;
        }

此时的效果很怪,因为只渲染了正面,但是此时是可以看到背面的。
请添加图片描述
接下来我们把背面渲染出来,代码几乎和上面的一模一样,只是cull back改成cull front。
最后得到双面渲染效果。
请添加图片描述

除了直接渲染两个pass之外,还可以使用VFACE这个语义来直接获取顶点朝向,大于0则为朝向摄像机,小于0为背向摄像机。但是该语义需要支持shader model 3.0。
使用vface语义的话就不可避免的需要写if语句,虽然在shader里做if判断会影响性能 但是比跑两个pass还是要好,而且两个pass要跑两次clip
主要修改就是两个pass变成一个pass,还有要使用Cull Off关闭背面裁剪。

        void surf(Input IN, inout SurfaceOutputStandard o)
        {
    
    
            clip(tex2D(_Mask, IN.uv_Mask).a - _MaskClipValue);

            fixed4 frontC = tex2D(_FrontTex, IN.uv_FrontTex) * _Color;
            fixed4 backC = tex2D(_BackTex, IN.uv_FrontTex) * _Color;

            //虽然要做if判断会影响性能  但是比跑两个pass还是要好,而且两个pass如果里面有clip操作的话要跑两次clip
            if (IN.face > 0)
            {
    
    
                o.Albedo = frontC.rgb;
                o.Alpha = frontC.a;
            }
            else
            {
    
    
                o.Albedo = backC.rgb;
                o.Alpha = backC.a;
            }

            o.Metallic = _Metallic;
            o.Smoothness = _Glossiness;
        }

参数面板
请添加图片描述

完整代码

完整代码如下
两个pass的方法

Shader "Custom/twoSide2"
{
    
    
    Properties
    {
    
    
        _Color ("Color", Color) = (1,1,1,1)
        _FrontTex ("FrontTex", 2D) = "white" {
    
    }
        _BackTex ("BackTex", 2D) = "white" {
    
    }
        _Glossiness ("Smoothness", Range(0,1)) = 0.5
        _Mask ("Mask", 2D) = "white" {
    
    }
        _Metallic ("Metallic", Range(0,1)) = 0.0
        _MaskClipValue ("MaskClipValue", range(0,1)) = 0
    }
    SubShader
    {
    
    
        Tags
        {
    
    
            "RenderType"="Opaque"
        }
        LOD 200

        Cull Back
        CGPROGRAM
        #pragma surface surf Standard fullforwardshadows
        #pragma target 3.0

        sampler2D _FrontTex;
        sampler2D _Mask;

        struct Input
        {
    
    
            float2 uv_FrontTex;
            float2 uv_Mask;
        };

        half _Glossiness;
        half _Metallic;
        fixed4 _Color;
        float _MaskClipValue;

        void surf(Input IN, inout SurfaceOutputStandard o)
        {
    
    
            clip(tex2D(_Mask, IN.uv_Mask).a - _MaskClipValue);
            fixed4 c = tex2D(_FrontTex, IN.uv_FrontTex) * _Color;
            o.Albedo = c.rgb;
            o.Metallic = _Metallic;
            o.Smoothness = _Glossiness;
            o.Alpha = c.a;
        }
        ENDCG
        
        Cull Front
        CGPROGRAM
        #pragma surface surf Standard fullforwardshadows
        #pragma target 3.0

        sampler2D _BackTex;
        sampler2D _Mask;

        struct Input
        {
    
    
            float2 uv_BackTex;
            float2 uv_Mask;
        };

        half _Glossiness;
        half _Metallic;
        fixed4 _Color;
        float _MaskClipValue;

        void surf(Input IN, inout SurfaceOutputStandard o)
        {
    
    
            clip(tex2D(_Mask, IN.uv_Mask).a - _MaskClipValue);
            fixed4 c = tex2D(_BackTex, IN.uv_BackTex) * _Color;
            o.Albedo = c.rgb;
            o.Metallic = _Metallic;
            o.Smoothness = _Glossiness;
            o.Alpha = c.a;
        }
        ENDCG


    }
    FallBack "Diffuse"
}

完整代码2
使用vface语义的代码

Shader "LX/twoSide1"
{
    
    
    Properties
    {
    
    
        _Color ("Color", Color) = (1,1,1,1)
        _FrontTex ("FrontTex", 2D) = "white" {
    
    }
        _BackTex ("BackTex", 2D) = "white" {
    
    }
        _Glossiness ("Smoothness", Range(0,1)) = 0.5
        _Metallic ("Metallic", Range(0,1)) = 0.0
        _Mask ("Mask", 2D) = "white" {
    
    }
        _MaskClipValue ("MaskClipValue", range(0,1)) = 0
    }
    SubShader
    {
    
    
        Tags
        {
    
    
            "RenderType"="Opaque"
        }
        LOD 200
        Cull Off
        CGPROGRAM
        #pragma surface surf Standard fullforwardshadows
        #pragma target 3.0

        sampler2D _FrontTex;
        sampler2D _BackTex;
        sampler2D _Mask;

        struct Input
        {
    
    
            float2 uv_FrontTex;
            float2 uv_BackTex;
            float2 uv_Mask;
            fixed face : VFACE;
        };

        half _Glossiness;
        half _Metallic;
        fixed4 _Color;
        float _MaskClipValue;


        void surf(Input IN, inout SurfaceOutputStandard o)
        {
    
    
            clip(tex2D(_Mask, IN.uv_Mask).a - _MaskClipValue);

            fixed4 frontC = tex2D(_FrontTex, IN.uv_FrontTex) * _Color;
            fixed4 backC = tex2D(_BackTex, IN.uv_FrontTex) * _Color;

            //虽然要做if判断会影响性能  但是比跑两个pass还是要好,而且两个pass如果里面有clip操作的话要跑两次clip
            if (IN.face > 0)
            {
    
    
                o.Albedo = frontC.rgb;
                o.Alpha = frontC.a;
            }
            else
            {
    
    
                o.Albedo = backC.rgb;
                o.Alpha = backC.a;
            }

            o.Metallic = _Metallic;
            o.Smoothness = _Glossiness;
        }
        ENDCG
    }
    FallBack "Diffuse"
}

另外代码也传到github仓库里了,大家也可以关注一下哦~
我的github

注:此shader只是演示了如何实现一个最简单的双面渲染效果,实际项目中肯定是要对以前使用的shader进行改造使其支持双面渲染的,所以肯定还有其他地方要修改。

猜你喜欢

转载自blog.csdn.net/o83290102o5/article/details/120602567
今日推荐