Lambert (Lambert) lighting model summary

The Lambert illumination model is an empirical model, which is mainly used to simulate the illumination phenomenon on the surface of rough objects, that is, diffuse reflection.
Diffuse reflection characteristics
1: The reflection intensity has nothing to do with the angle of the observer
2: The reflection intensity has a relationship with the incident angle of the light
Diffuse reflection illumination conforms to Lambert's law (Lambert's law): the intensity of the reflected light is related to the surface normal and the direction of the light source The cosine value of the included angle is proportional to the value, the larger the included angle, the less the amount of light exposure received, when the included angle is greater than 90 degrees, the light illuminates the back of the object, and the light intensity is considered to be 0 at this time.

insert image description here
Diffuse reflection formula: Diffuse reflection = light source color x model base color x (normal line light source direction), here use the max function to prevent the result from being negative, and can also be multiplied by a coefficient to control the final result. Here, the dot product of the normal line and the direction of the light source indicates the angle between the two. The larger the angle, the smaller the point multiplication value.
insert image description here

Diffuse, Lambert per-vertex lighting

Shader "MyCustom/Diffuse_Lambert_Vertex"
{
    
    
    Properties
    {
    
    
        //材质的漫反射颜色
        _Color("Base Color", color) = (1.0, 1.0, 1.0, 1.0)
        //漫反射系数
        _kD("kD", Range(0, 1)) = 1
    }
    SubShader
    {
    
    
        Tags {
    
     "RenderType"="Opaque" }
        LOD 100

        Pass
        {
    
    
            CGPROGRAM
            #pragma vertex vert
            #pragma fragment frag
            // make fog work
            #pragma multi_compile_fog
            //引入Unity内置的一些变量
            #include "UnityCG.cginc"

            //UnityCG.cginc中定义了结构体appdata_base,appdata_tan,appdata_full用于顶点着色器输入
            //这里自定义appdata类型,只包含需要的数据
            struct appdata
            {
    
    
                float4 vertex : POSITION; //顶点位置
                float3 normal : NORMAL;   //法线
            };

            //顶点着色器输出
            struct v2f
            {
    
    
                //SV_POSITION是裁剪空间中的顶点坐标,它是DirectX 10中引入的系统数值语义,在大多数平台上,它和POSITION语义是等价的,
                //但在某些平台(例如PS4)上必须使用SV_POSITION来修饰顶点着色器的输出,否则无法让Shader正常工作
                //SV_POSITION一旦被作为顶点着色器的输出语义,那么顶点位置就被固定了,后续不能再被改变它的空间位置
                float4 vertex : SV_POSITION;
                float4 col : COLOR;
            };

            //为了使用Properties语义块中声明的属性,需要定义一个和该属性类型相匹配的变量
            //由于颜色属性的范围在0到1之间,因此我们可以使用fixed精度的变量来存储它
            float4 _Color;
            float _kD;
            //表示这个变量的初始值来自于外部的其他环境
            uniform float4 _LightColor0;

            v2f vert (appdata v)
            {
    
    
                v2f o;

                //Unity内置 模型 * 世界 * 投影矩阵 UNITY_MATRIX_MVP,把顶点位置从模型空间转换到裁剪空间中
                o.vertex = UnityObjectToClipPos(v.vertex);

                //在计算法线和光线之间的点积时,只有两者处于同一坐标系下,它们的点积才有意义,
                //所以需要把顶点的法线从模型空间转到世界空间,unity_WorldToObject是4*4
                float3 worldNormal = normalize(mul(float4(v.normal, 0.0), unity_WorldToObject).xyz);
                //也可以这么写
                // float3 worldNormal = normalize(mul(v.normal, (float3x3)unity_WorldToObject));

                //获取光源方向(们假设场景中只有一个光源且该光源的类型是平行光)
                float3 worldLight = normalize(_WorldSpaceLightPos0.xyz);

                //当法线和光线之间夹角大于90时,光线是在照射物体的背面,此时光照强度为0
                float lambert = max(dot(worldNormal, worldLight), 0.0);
                //也可以使用saturate函数把参数截取到[0, 1]的范围内,防止负值
                // float lambert = saturate(dot(worldNormal, worldLight));
                
                float3 diffuse = _kD * lambert * _Color.rgb * _LightColor0.rgb;

                //环境光
                float3 ambient = UNITY_LIGHTMODEL_AMBIENT.xyz;
                float3 finalColor = diffuse + ambient;
                o.col = float4(finalColor, 1.0);
                return o;
            }

            fixed4 frag (v2f i) : SV_Target
            {
    
    
                return i.col;
            }
            ENDCG
        }
    }
}

Diffuse, Lambert per-pixel lighting

Shader "MyCustom/Diffuse_Lambert_Pixel"
{
    
    
    Properties
    {
    
    
        _Color("Base Color", color) = (1.0, 1.0, 1.0, 1.0)
        _kD("kD", Range(0, 1)) = 1
    }
    SubShader
    {
    
    
        Tags {
    
     "RenderType"="Opaque" }
        LOD 100

        Pass
        {
    
    
            CGPROGRAM
            #pragma vertex vert
            #pragma fragment frag
            // make fog work
            #pragma multi_compile_fog

            #include "UnityCG.cginc"

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

            struct v2f
            {
    
    
                float4 vertex : SV_POSITION;
                //顶点着色器中计算世界空间下法线,传递给片元着色器
                float3 worldNormal : TEXCOORD0;
            };

            float4 _Color;
            float _kD;
            uniform float4 _LightColor0;

            v2f vert (appdata v)
            {
    
    
                v2f o;
                o.vertex = UnityObjectToClipPos(v.vertex);
                o.worldNormal = normalize(mul(v.normal, (float3x3)unity_WorldToObject));
                return o;
            }

            //将光照的计算转移到片元着色器中
            fixed4 frag (v2f i) : SV_Target
            {
    
    
                float3 worldNormal = i.worldNormal;
                float3 worldLight = normalize(_WorldSpaceLightPos0.xyz);

                float lambert = max(dot(worldNormal, worldLight), 0.0);
                float3 diffuse = _kD * lambert * _Color.rgb * _LightColor0.rgb;
                float3 ambient = UNITY_LIGHTMODEL_AMBIENT.xyz;
                float3 finalColor = diffuse + ambient;

                return float4(finalColor, 1.0);
            }
            ENDCG
        }
    }
}

Half Lambert per-pixel lighting

Shader "MyCustom/HalfLambert"
{
    
    
    Properties
    {
    
    
        _Color ("Base Color", color) = (1.0, 1.0, 1.0, 1.0)
        _kD ("kD", Range(0, 1)) = 1
    }
    SubShader
    {
    
    
        Tags {
    
     "RenderType"="Opaque" }
        LOD 100

        Pass
        {
    
    
            CGPROGRAM
            #pragma vertex vert
            #pragma fragment frag
            // make fog work
            #pragma multi_compile_fog

            #include "UnityCG.cginc"

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

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

            float4 _Color;
            float _kD;
            uniform float4 _LightColor0;

            v2f vert (appdata v)
            {
    
    
                v2f o;
                o.vertex = UnityObjectToClipPos(v.vertex);
                o.worldNormal = normalize(mul(v.normal, (float3x3)unity_WorldToObject));
                return o;
            }

            fixed4 frag (v2f i) : SV_Target
            {
    
    
                float3 worldNormal = i.worldNormal;
                float3 worldLight = normalize(_WorldSpaceLightPos0.xyz);
                
                // 广义上的半兰伯特 half lambert = light * diffuse * (α* (n * l) + β)
                // half lambert = light * diffuse * (0.5 * (n * l) + 0.5)
                float halfLambert = 0.5 * dot(worldNormal, worldLight) + 0.5;
                
                float3 diffuse = _kD * halfLambert * _Color.rgb * _LightColor0.rgb;
                float3 ambient = UNITY_LIGHTMODEL_AMBIENT.xyz;
                float3 finalColor = diffuse + ambient;

                return float4(finalColor, 1.0);
            }
            ENDCG
        }
    }
}

actual comparison

insert image description here
Vertex-by-vertex lighting : The amount of calculation is small, and the edges of the transition between light and dark are jagged, which is not natural enough. Moreover, since per-vertex lighting interpolates the vertex color inside the rendered primitive, this will cause the color inside the rendered primitive to always be darker than the highest color value at the vertex, which in some cases will produce obvious angularity

Pixel-by-pixel lighting : the amount of calculation is larger, and the lighting effect is smoother, but one problem still exists. On the surface to be illuminated, the appearance of the model is usually completely black without any change in light and shade. It can be seen from the formula max(dot(n, l), 0) that the color of the surface to be illuminated is 0, which will make the backlight of the model The region looks like a plane, losing model detail. For this reason, an improvement technique has been proposed, which is the semi-Lambert illumination model

Half Lambert lighting : In order to solve the problem that Lambert lighting is not bright enough, the value range of n * l is [-1, 1], and the value range of 0.5 * dot(n * l) + 0.5 becomes [0 after processing , 1]

Refer to "Introduction to Unity Shader Essentials"

Guess you like

Origin blog.csdn.net/sinat_34014668/article/details/125592172