Unity Shader Lambert(兰伯特)光照模型

发表于2018-07-10
评论0 7.2k浏览
伯特光照模型是目前最简单通用的模拟漫反射的光照模型,这也是本篇文章要重点介绍的内容。

一、漫反射简介

一束平行光入射到凹凸不平的表面上,光线被反射向四面八方的现象叫做漫反射。

当一束平行光触及光滑物体表面时,光线则发生规律性反射,反射后的光线也相互平行,这种规律性反射称为光的单向反射或镜面反射。但物体的光滑程度是相对的,而一般物体的表面多粗糙不平,入射线虽然为平行光线,但反射后的光线则向各个方向分散,此种现象为光的漫反射。

现实中的物体,如植物、墙壁、衣服等,其表面粗看起来似乎是平滑,但用放大镜仔细观察,就会看到其表面是凹凸不平的,人眼之所以能看清物体的全貌,主要是靠漫反射光在眼内的成像。

漫反射的特点

1.光照强度与观察角度没有关系
从各个角度观看灯光时,它都具有相同明显的强度

2.光照强度跟灯光的入射角有关系
如果改变光的入射光方向可以看到模型表面的光照强度发生了变化

二、漫反射光照模型-Lambert(兰伯特)光照模型

lambert光照模型属于经验模型,主要用来简单模拟粗糙物体表面的光照现象。

此模型假设物体表面为理想漫反射体(也就是只产生漫反射现象,也成为Lambert反射体),同时,场景中存在两种光,一种为环境光,一种为方向光,然后我们分别计算这两种光照射到粗糙物体表面所产生的光照现象,最后再将两个结果相加,得出反射后的光强值。

首先是计算环境光的公式:
I_inDirectionDiffuse = K_d * I_a;
其中,K_d为粗糙物体表面材质对光的反射系数,这个系数由程序编写者在宿主程序中给出,I_a为环境光的光强,也就是环境光的颜色数值,一般是一个float3型的变量

然后是计算方向光的公式:
I_directionDiffuse = K_d * I_l * max(0,dot(N, L));
其中I_l为方向光的光强,也就是其颜色值;N为顶点的单位法向量;L为入射光的单位法向量(注意,光照向量是从顶点指向光源的向量;也就是,它与线的传播方向正好相反)。

这个公式与计算环境光的不同,对于环境光,我们不关心它的方向,因为环境光也没有方向,它给予物体的光照在各个顶点处均是一样的。而方向光则需要关注其方向,例如一个聚光灯,灯从不同的角度来照射物体所产生的效果也是不一样的,光线方向越靠近法线,漫反射出来的光就越强,反之则越弱。 方向光的漫反射强度遵循Lambert余弦定理。

综上,得出漫反射后的光强为:
I_diff = K_d * I_a + K_d * I_l * max(0,dot(N, L));

Lambert 余弦定理

模型表面的明亮度直接取决于光线向量(light vector)和表面法线(normal)两个向量将夹角的余弦值。
f(θ) = max(cosθ,0) = max(L•n,0)
关于Lambert余弦定理的推导可参考:DirectX11 兰伯特余弦定理(Lambert)

三、自定义UnityShader实现漫反射(Diffuse)效果

写法
Shader "Hidden/NewImageEffectShader"
{
    Properties
    {
        _MainTex ("Texture", 2D) = "white" {}
        _DiffusePower("Diffuse Power", Float) = 1.0
    }
    SubShader
    {
        Pass
        {
            CGPROGRAM
            #pragma vertex vert
            #pragma fragment frag
            #include "UnityCG.cginc"
            #include "Lighting.cginc"
            #include "AutoLight.cginc"
            struct appdata
            {
                float4 vertex : POSITION;
                float2 uv : TEXCOORD0;
                float3 normal : NORMAL;
            };
            struct v2f
            {
                float2 uv : TEXCOORD0;
                float4 vertex : SV_POSITION;
                float3 normalDir : TEXCOORD1;
            };
            v2f vert (appdata v)
            {
                v2f o;
                o.vertex = UnityObjectToClipPos(v.vertex);
                o.uv = v.uv;
                // 将物体法线从物体坐标系转换到世界坐标系
                o.normalDir = UnityObjectToWorldNormal(v.normal);
                return o;
            }
            sampler2D _MainTex;
            float _DiffusePower;
            fixed4 frag (v2f i) : SV_Target
            {
                // 法线方向
                float3 normalDirection = normalize(i.normalDir);
                // 灯光方向
                float lightDirection = normalize(_WorldSpaceLightPos0.xyz);
                // 灯光颜色
                float3 lightColor = _LightColor0.rgb;
                // 计算灯光衰减
                float attenuation = LIGHT_ATTENUATION(i);
                float3 attenColor = attenuation * _LightColor0.xyz;
                // 基于兰伯特模型计算灯光
                float NdotL = max(0,dot(normalDirection,lightDirection));
                // 方向光
                float3 directionDiffuse = pow(NdotL, _DiffusePower) * attenColor;
                // 环境光  
                float3 inDirectionDiffuse = float3(0,0,0)+UNITY_LIGHTMODEL_AMBIENT.rgb;
                // 灯光与材质球表面颜色进行作用
                float3 texColor = tex2D(_MainTex, i.uv).rgb;
                float3 diffuseColor = texColor *(directionDiffuse+inDirectionDiffuse);
                float4 finalColor = float4(diffuseColor,1);
                return finalColor;
            }
            ENDCG
        }
    }
}

效果展示:

如社区发表内容存在侵权行为,您可以点击这里查看侵权投诉指引