【Unity Shader】高光反射光照模型的两种实现方法:Phong光照模型和Blinn-Phong光照模型

1 高光反射

是一种经验模型,并不完全符合现实中的高光发射现象。可用于计算那些沿着完全镜面反射方向被反射的光线。可以让物体看起来比较有光泽,比如金属材质。

可以利用Phong光照模型、Blinn-Phong光照模型计算高光

在这里插入图片描述

2 Phong光照模型

2.1 Phong模型公式

C s p e c u l a r = ( C l i g h t ⋅ M s p e c u l a r ) m a x ( 0 , d o t ( V ⃗ , R ⃗ ) ) M g l o s s C_{specular}=(C_{light} \cdot M_{specular})max(0, dot(\vec{V},\vec{R}))^{M_{gloss}} Cspecular=(ClightMspecular)max(0,dot(V ,R ))Mgloss

从以上公式可以看出,要计算高光反射需要知道4个参数:

  • 入射光线的颜色和强度 C l i g h t C_{light} Clight
  • 材质的高光反射颜色和强度 M s p e c u l a r M_{specular} Mspecular
  • 视角方向 V ⃗ \vec{V} V
  • 反射方向 R ⃗ \vec{R} R
    除了以上4个参数,还有一个常量 M g l o s s M_{gloss} Mgloss,是材质的光泽度gloss),也被称为反光度shininess)。它用于控制高光区域的“亮点”有多宽, M g l o s s M_{gloss} Mgloss越大,亮点就越小。

2.2 反射方向 R ⃗ \vec{R} R 计算公式

R ⃗ = 2 ( N ⃗ ⋅ L ⃗ ) N ⃗ − L ⃗ \vec{R}=2(\vec{N} \cdot \vec{L})\vec{N}-\vec{L} R =2(N L )N L

其中 L ⃗ \vec{L} L 为光源方向, N ⃗ 为 平 面 法 线 方 向 \vec{N}为平面法线方向 N 线。CG语言中提供了reflect函数可以用来计算反射方向。

在这里插入图片描述

2.3 Unity Shader代码

Phong高光反射光照模型(逐像素实现)

// Phong高光反射光照模型 (逐像素实现)
Shader "LShaders/PhongSpecular"
{
    Properties
    {
        _DiffuseColor("Diffuse Color", Color) = (1,1,1,1)
        _Specular("Specular Color", Color) = (1,1,1,1)
        _Gloss("Gloss", Range(0, 100)) = 1
    }
    SubShader
    {
        Pass
        {
            Tags {"LightMode"="ForwardBase"}
            CGPROGRAM
            #pragma vertex vert
            #pragma fragment frag
            #include "UnityCG.cginc"
            #include "Lighting.cginc"
           
            fixed4 _DiffuseColor;
            fixed4 _Specular;
            float _Gloss;

            struct appdata
            {
                float4 vertex : POSITION; 
                float3 normal : NORMAL;     // 顶点法线
            };
            struct v2f
            {
                float4 pos : SV_POSITION;
                fixed3 worldNormal: TEXCOORD0;
                float3 worldPos: TEXCOORD1;
            };
            v2f vert (appdata v)
            {
                v2f o;
                o.pos = UnityObjectToClipPos(v.vertex);
                o.worldNormal = normalize(mul(v.normal, (float3x3)unity_WorldToObject));
                o.worldPos = mul(unity_ObjectToWorld, v.vertex).xyz;
                return o;
            }
            fixed4 frag (v2f i) : SV_Target
            {
                // 环境光
                fixed3 ambient = UNITY_LIGHTMODEL_AMBIENT.xyz;
                // 世界空间下的法线
                fixed3 worldNormal = i.worldNormal;
                // 世界空间下光源方向
                fixed3 worldLightDir = normalize(_WorldSpaceLightPos0.xyz);
                // 半兰伯特光照模型:Clight * Mdiffuse * (dot(worldNormal, worldLightDir) * 0.5 + 0.5)
                fixed halfLambert = saturate(dot(worldNormal, worldLightDir) * 0.5 + 0.2);
                // 漫反射 = 光源颜色 * 漫反射颜色 * (顶点法线和光源方向的点积,归一化)
                fixed3 diffuse = _LightColor0.rgb * _DiffuseColor.rgb * halfLambert;
                // 世界空间下的反射方向
                fixed3 reflectDir = normalize(reflect(-worldLightDir, worldNormal));
                // 世界空间下的视角方向
                fixed3 viewDir = normalize(_WorldSpaceCameraPos.xyz - i.worldPos.xyz);
                // 高光反射 = 光源颜色 * 高光颜色 * (反射方向和视角方向的点积,根据光泽度_Gloss进行幂运算)
                fixed3 specular = _LightColor0.rgb * _Specular.rgb * pow(saturate(dot(reflectDir, viewDir)), _Gloss);
                // 最终颜色 = 环境光 + 漫反射光 + 高光
                fixed3 color = ambient + diffuse + specular;
                return fixed4(color, 1.0);
            }
            ENDCG
        }
    }
}

3 Blinn-Phong光照模型

Blinn-Phong光照模型与Phong光照模型不同的是,Blinn模型没有使用反射方向,而实引入一个新的矢量 H ⃗ \vec{H} H

3.1 Blinn-Phong模型公式

C s p e c u l a r = ( C l i g h t ⋅ M s p e c u l a r ) m a x ( 0 , d o t ( N ⃗ , H ⃗ ) ) M g l o s s C_{specular}=(C_{light} \cdot M_{specular})max(0, dot(\vec{N},\vec{H}))^{M_{gloss}} Cspecular=(ClightMspecular)max(0,dot(N ,H ))Mgloss

公式与Phong模型公式相似,不同的是Phong中是视角方向 V ⃗ \vec{V} V 和反射方向 R ⃗ \vec{R} R 的点积,Blinn-Phong中则是法线方向 N ⃗ \vec{N} N 和矢量 H ⃗ \vec{H} H 的点积。

其中矢量 H ⃗ \vec{H} H ,是通过对视角方向 V ⃗ \vec{V} V 和光照方向 L ⃗ \vec{L} L 相加后再归一化得到的。即:

H ⃗ = V ⃗ + L ⃗ ∣ V ⃗ + L ⃗ ∣ \vec{H}= {\vec{V} + \vec{L} \over | \vec{V}+\vec{L}|} H =V +L V +L

3.2 Unity Shader代码

Blinn-Phong高光反射光照模型(逐像素实现)

// Blinn-Phong高光反射光照模型 (逐像素实现)
Shader "LShaders/BlinnPhongSpecular"
{
    Properties
    {
        _DiffuseColor("Diffuse Color", Color) = (1,1,1,1)
        _Specular("Specular Color", Color) = (1,1,1,1)
        _Gloss("Gloss", Range(1, 200)) = 50
    }
    SubShader
    {
        Pass
        {
            Tags {"LightMode"="ForwardBase"}
            CGPROGRAM
            #pragma vertex vert
            #pragma fragment frag
            #include "UnityCG.cginc"
            #include "Lighting.cginc"
           
            fixed4 _DiffuseColor;
            fixed4 _Specular;
            float _Gloss;

            struct appdata
            {
                float4 vertex : POSITION; 
                float3 normal : NORMAL;     // 顶点法线
            };
            struct v2f
            {
                float4 pos : SV_POSITION;
                fixed3 worldNormal: TEXCOORD0;
                float3 worldPos: TEXCOORD1;
            };
            v2f vert (appdata v)
            {
                v2f o;
                o.pos = UnityObjectToClipPos(v.vertex);
                o.worldNormal = normalize(mul(v.normal, (float3x3)unity_WorldToObject));
                o.worldPos = mul(unity_ObjectToWorld, v.vertex).xyz;
                return o;
            }
            fixed4 frag (v2f i) : SV_Target
            {
                // 环境光
                fixed3 ambient = UNITY_LIGHTMODEL_AMBIENT.xyz;
                // 世界空间下的法线
                fixed3 worldNormal = i.worldNormal;
                // 世界空间下光源方向
                fixed3 worldLightDir = normalize(_WorldSpaceLightPos0.xyz);
                // 半兰伯特光照模型:Clight * Mdiffuse * (dot(worldNormal, worldLightDir) * 0.5 + 0.5)
                fixed halfLambert = saturate(dot(worldNormal, worldLightDir) * 0.5 + 0.2);
                // 漫反射 = 光源颜色 * 漫反射颜色 * (顶点法线和光源方向的点积,归一化)
                fixed3 diffuse = _LightColor0.rgb * _DiffuseColor.rgb * halfLambert;
                // 世界空间下的视角方向
                fixed3 viewDir = normalize(_WorldSpaceCameraPos.xyz - i.worldPos.xyz);
                // 世界空间下的half方向
                fixed3 halfDir = normalize(worldLightDir + viewDir);
                // 高光反射 = 光源颜色 * 高光颜色 * (反射方向和视角方向的点积,根据光泽度_Gloss进行幂运算)
                fixed3 specular = _LightColor0.rgb * _Specular.rgb * pow(saturate(dot(halfDir, worldNormal)), _Gloss);
                // 最终颜色 = 环境光 + 漫反射光 + 高光
                fixed3 color = ambient + diffuse + specular;
                return fixed4(color, 1.0);
            }
            ENDCG
        }
    }
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

路易斯·李

点个赞再走呗 :)

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值