Shader 边缘光

使用菲涅尔公式实现边缘光

边缘光的实现参考菲涅尔公式,Empricial 菲涅耳近似等式:
在这里插入图片描述
v 是视角方向, n 是表面法线,bias, scale 和 power 是控制项
对上面的公式略作修改得到边缘光的计算公式

RimLight = (1.0 - dot(v, n))^power * intensity * RimColor

在这里插入图片描述

在物体边缘,视角方向和法线方向是垂直的,两者点积为0,(1.0 - dot(v, n)) 就使得靠近边缘处数值趋近于1,RimColor是边缘光的颜色,power可以理解为中心到边缘颜色过渡的快慢,intensity是边缘光颜色的强度。
下面是边缘光的shader,这里为了简单,只考虑边缘光,实际应该叠加模型贴图颜色,漫反射,环境光等。

Shader "MyCustom/RimLight"
{
    
    
    Properties
    {
    
    
        _RimLightColor     ("_RimLightColor", Color)            = (1, 1, 1, 1)
        _RimLightPower     ("_RimLightPower", Range(0, 10))     = 1
        _RimLightIntensity ("_RimLightIntensity", Range(0, 10)) = 1
    }
    SubShader
    {
    
    
        Tags {
    
     "RenderType"="Opaque" }
        LOD 100

        Pass
        {
    
    
            CGPROGRAM
            #pragma vertex vert
            #pragma fragment frag

            #include "UnityCG.cginc"

            float4 _RimLightColor;
            float _RimLightPower;
            float _RimLightIntensity;

            struct appdata
            {
    
    
                float4 vertex       : POSITION;
                float3 normal       : NORMAL;
            };

            struct v2f
            {
    
    
                float4 vertex       : SV_POSITION;
                float3 worldNormal  : TEXCOORD0;
                float3 worldView    : TEXCOORD1;
            };

            v2f vert (appdata v)
            {
    
    
                v2f o;
                o.vertex = UnityObjectToClipPos(v.vertex);
				//法线转换到世界空间
                //UnityObjectToWorldNormal支持物体的非均匀缩放,即scale不一致
                o.worldNormal = UnityObjectToWorldNormal(v.normal);
                float4 worldPos = mul(unity_ObjectToWorld, v.vertex);
                //世界空间中视角方向
                o.worldView = normalize(UnityWorldSpaceViewDir(worldPos));
                return o;
            }

            fixed4 frag (v2f i) : SV_Target
            {
    
    
                //点乘返回-1到1的值,用max截取0到1的值
                float nv = max(0, dot(i.worldNormal, i.worldView));
                //套用上面公式
                float3 rimLight = pow(1 - nv, _RimLightPower) * _RimLightIntensity * _RimLightColor.rgb;
                return float4(rimLight, 1);
            }
            ENDCG
        }
    }
    FallBack "Diffuse"
}

使用贴图实现边缘光

在这里插入图片描述
在这里插入图片描述
通过贴图可以设置物体两侧边缘光的颜色,模拟在两侧打光的效果,更换贴图就可以实现不同效果,方便美术控制,上面左侧是采样的贴图。
原理是将世界空间中法线的x,y分量作为贴图的uv坐标对贴图进行采样,对上面的shader进行简单的修改就可以实现。

Shader "MyCustom/FakeRimLight"
{
    
    
    Properties
    {
    
    
        _FakeRimLightTex    ("_FakeRimLightTex",    2D)             = "white" {
    
    }
        _RimLightColor      ("_RimLightColor",      Color)          = (1, 1, 1, 1)
        _RimLightPower      ("_RimLightPower",      Range(0, 10))   = 1
        _RimLightIntensity  ("_RimLightIntensity",  Range(0, 10))   = 1
    }
    SubShader
    {
    
    
        Tags {
    
     "RenderType"="Opaque" }
        LOD 100

        Pass
        {
    
    
            CGPROGRAM
            #pragma vertex vert
            #pragma fragment frag

            #include "UnityCG.cginc"

            float4 _RimLightColor;
            float _RimLightPower;
            float _RimLightIntensity;
            sampler2D _FakeRimLightTex;

            struct appdata
            {
    
    
                float4 vertex           : POSITION;
                float2 uv               : TEXCOORD0;
                float3 normal           : NORMAL;
            };

            struct v2f
            {
    
    
                float4 vertex           : SV_POSITION;
                float2 uv               : TEXCOORD0;
                float3 worldNormal      : TEXCOORD1;
                float3 worldView        : TEXCOORD2;
            };

            v2f vert (appdata v)
            {
    
    
                v2f o;
                o.vertex = UnityObjectToClipPos(v.vertex);
                o.uv = v.uv;
                o.worldNormal = UnityObjectToWorldNormal(v.normal);
                float4 worldPos = mul(unity_ObjectToWorld, v.vertex);
                o.worldView = normalize(UnityWorldSpaceViewDir(worldPos));
                return o;
            }

            fixed4 frag (v2f i) : SV_Target
            {
    
    
                //点乘返回-1到1的值,用max截取0到1的值
                float nv = max(0, dot(i.worldNormal, i.worldView));
                float3 rimLight = pow(1 - nv, _RimLightPower) * _RimLightIntensity * _RimLightColor.rgb;

                //将worldNormal的xy分量作为贴图的uv坐标,并从-1到1转到0到1
                float u = i.worldNormal.x * 0.5 + 0.5;
                float v = i.worldNormal.y * 0.5 + 0.5;
                float3 fakeRimLight = tex2D(_FakeRimLightTex, float2(u, v)).rgb;
                float3 finalColor = fakeRimLight * rimLight;
                return float4(finalColor, 1);
            }
            ENDCG
        }
    }
    FallBack "Diffuse"
}

猜你喜欢

转载自blog.csdn.net/sinat_34014668/article/details/127418702