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"
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
Unity中实现边缘发光的一种方式是使用Shader。以下是一个基础的边缘发光Shader示例: ``` Shader "Custom/EdgeGlow" { Properties { _MainTex ("Texture", 2D) = "white" {} _Threshold ("Threshold", Range(0, 1)) = 0.5 _Color ("Glow Color", Color) = (1,1,1,1) _Glow ("Glow Amount", Range(0, 1)) = 0.5 } SubShader { Tags {"Queue"="Transparent" "RenderType"="Transparent"} Pass { ZWrite off ColorMask RGB 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; float _Threshold; float4 _Color; float _Glow; v2f vert (appdata v) { v2f o; o.vertex = UnityObjectToClipPos(v.vertex); o.uv = v.uv; return o; } fixed4 frag (v2f i) : SV_Target { fixed4 col = tex2D(_MainTex, i.uv); float4 sum = 0; sum += tex2D(_MainTex, i.uv + float2(-0.01, -0.01)) - col; sum += tex2D(_MainTex, i.uv + float2( 0.00, -0.01)) - col; sum += tex2D(_MainTex, i.uv + float2( 0.01, -0.01)) - col; sum += tex2D(_MainTex, i.uv + float2(-0.01, 0.00)) - col; sum += tex2D(_MainTex, i.uv + float2( 0.01, 0.00)) - col; sum += tex2D(_MainTex, i.uv + float2(-0.01, 0.01)) - col; sum += tex2D(_MainTex, i.uv + float2( 0.00, 0.01)) - col; sum += tex2D(_MainTex, i.uv + float2( 0.01, 0.01)) - col; if (length(sum) > _Threshold) { fixed4 glow = _Color; glow.a = _Glow; return lerp(col, glow, _Glow); } return col; } ENDCG } } } ``` 在此示例中,我们使用了一个名为“_Threshold”的阈值属性,该属性定义了什么颜色被视为边缘。我们还使用了一个名为“_Color”的颜色属性,该属性定义了边缘发光的颜色。最后,我们使用了一个名为“_Glow”的属性,该属性定义了发光的强度。 在使用此Shader时,将其应用于一个具有透明度的材质上,并将其放置在需要发光的对象上即可。例如,你可以在场景中放置一个球体,并将具有此Shader的材质应用于该球体,以使其周围发出发光效果。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值