【Unity Shader】半兰伯特光照模型的两种实现方法:逐顶点和逐像素

1 漫反射

是投射在粗糙表面上的光向各个方向反射的现象。当一束平行的入射光线射到粗糙的表面时,表面会把光线向着四面八方反射,所以入射线虽然互相平行,由于各点的法线方向不一致,造成反射光线向不同的方向无规则地反射,这种反射称之为“漫反射”或“漫射”。这种反射的光称为漫射光。

在这里插入图片描述

漫反射光照符合兰伯特定律

2 兰伯特光照模型

2.1 兰伯特光照公式

C d i f f u s e = ( C l i g h t ⋅ M d i f f u s e ) m a x ( 0 , d o t ( N ⃗ , L ⃗ ) ) C_{diffuse} = (C_{light} \cdot M_{diffuse})max(0, dot(\vec{N}, \vec{L})) Cdiffuse=(ClightMdiffuse)max(0,dot(N ,L ))

N ⃗ \vec{N} N 是表面法线, L ⃗ \vec{L} L 是光源方向的单位矢量, d o t ( N ⃗ , L ⃗ ) dot(\vec{N}, \vec{L}) dot(N ,L )则是取两者的内积。 M d i f f u s e M_{diffuse} Mdiffuse是材质的漫反射颜色, C l i g h t C_{light} Clight是光源颜色。使用取最大值函数 m a x ( ) max() max()来将其截取到0,还可以防止物体被从后面来的光源照亮。

2.2 内积公式

d o t ( N ⃗ , L ⃗ ) = ∣ N ⃗ ∣ ⋅ ∣ L ⃗ ∣ ⋅ cos ⁡ θ dot(\vec{N}, \vec{L})=|\vec{N}|\cdot|\vec{L}|\cdot\cos\theta dot(N ,L )=N L cosθ

内积的几何意义是向量 N ⃗ \vec{N} N 在向量 L ⃗ \vec{L} L 上的投影,如果两者为单位向量,则内积即两向量夹角的余弦值。

在这里插入图片描述

3 半兰伯特光照模型

参考博客:https://www.cnblogs.com/xhg986/p/7510372.html

C d i f f u s e = ( C l i g h t ⋅ M d i f f u s e ) m a x ( 0 , d o t ( N ⃗ , L ⃗ ) ) × 0.5 + 0.5 C_{diffuse} = (C_{light} \cdot M_{diffuse})max(0, dot(\vec{N}, \vec{L})) \times 0.5 + 0.5 Cdiffuse=(ClightMdiffuse)max(0,dot(N ,L ))×0.5+0.5

即在兰伯特光照模型基础上乘以0.5再加上0.5

4 Unity Shader代码

逐像素实现要比逐顶点更加平滑

4.1 逐顶点实现

// 半兰伯特光照模型 (逐顶点实现)
Shader "LShaders/LambertVert"
{
    Properties
    {
        _DiffuseColor("Diffuse Color", Color) = (1,1,1,1)
    }
    SubShader
    {
        Pass
        {
            Tags {"LightMode"="ForwardBase"}
            CGPROGRAM
            #pragma vertex vert
            #pragma fragment frag
            #include "UnityCG.cginc"
            #include "Lighting.cginc"
           
            fixed4 _DiffuseColor;
            struct appdata
            {
                float4 vertex : POSITION; 
                float3 normal : NORMAL;     // 顶点法线
            };
            struct v2f
            {
                float4 pos : SV_POSITION;
                fixed3 color: COLOR;
            };
            v2f vert (appdata v)
            {
                v2f o;
                // 环境光
                fixed3 ambient = UNITY_LIGHTMODEL_AMBIENT.xyz;
                // 世界空间下的法线
                fixed3 worldNormal = normalize(mul(v.normal, (float3x3)unity_WorldToObject));
                // 世界空间下光源方向
                fixed3 worldLightDir = normalize(_WorldSpaceLightPos0.xyz);
                // 半兰伯特光照模型:Clight * Mdiffuse * (dot(worldNormal, worldLightDir) * 0.5 + 0.5)
                fixed halfLambert = dot(worldNormal, worldLightDir) * 0.5 + 0.5;
                // 漫反射 = 光源颜色 * 漫反射颜色 * (顶点法线和光源方向的点积,归一化)
                fixed3 diffuse = _LightColor0.rgb * _DiffuseColor.rgb * halfLambert;
                // 最终颜色 = 环境光 + 漫反射光
                fixed3 color = ambient + diffuse;
                o.pos = UnityObjectToClipPos(v.vertex);
                o.color = color;
                return o;
            }
            fixed4 frag (v2f i) : SV_Target
            {
                return fixed4(i.color, 1.0);
            }
            ENDCG
        }
    }
}

4.2 逐像素实现

// 半兰伯特光照模型 (逐像素实现)
Shader "LShaders/DiffuseHalfLambertLighting"
{
    Properties
    {
        _DiffuseColor("Diffuse Color", Color) = (1,1,1,1)
    }
    SubShader
    {
        Pass
        {
            Tags {"LightMode"="ForwardBase"}
            CGPROGRAM
            #pragma vertex vert
            #pragma fragment frag
            #include "UnityCG.cginc"
            #include "Lighting.cginc"
           
            fixed4 _DiffuseColor;
            struct appdata
            {
                float4 vertex : POSITION; 
                float3 normal : NORMAL;     // 顶点法线
            };
            struct v2f
            {
                float4 pos : SV_POSITION;
                fixed3 worldNormal: TEXCOORD0;
            };
            v2f vert (appdata v)
            {
                v2f o;
                o.pos = UnityObjectToClipPos(v.vertex);
                o.worldNormal = normalize(mul(v.normal, (float3x3)unity_WorldToObject));
                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 = dot(worldNormal, worldLightDir) * 0.5 + 0.5;
                // 漫反射 = 光源颜色 * 漫反射颜色 * (顶点法线和光源方向的点积,归一化)
                fixed3 diffuse = _LightColor0.rgb * _DiffuseColor.rgb * halfLambert;
                // 最终颜色 = 环境光 + 漫反射光
                fixed3 color = ambient + diffuse;
                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、付费专栏及课程。

余额充值