kingBook

导航

实现漫反射光照模型

image

漫反射光照符合兰伯特定律(Lambert's law),基本光照模型中漫反射计算公式:
\(C_{diffuse}=(C_{light} \cdot M_{diffuse})max(0,\hat{N} \cdot I )\)
其中,\(C_{light}\) 是光源的颜色,\(M_{diffuse}\) 是材质的漫反射颜色,\(\hat{N}\) 是单位化的表面法线,\(I\) 是指向光源的单位向量。需要注意的是,需要防止法线和光源方向点乘的结果为负值,为此,使用取最大值函数来将其截取到0,这可以防止物体被从后面的光源照亮。

逐顶点:
CG:

点击查看代码
Shader "Unity Shaders Book/Chapter 6/Diffuse Vertex-Level" {
    Properties {
        _Diffuse ("Diffuse", Color) = (1, 1, 1, 1)
    }
    SubShader {
        Pass { 
            Tags { "LightMode"="ForwardBase" }
        
            CGPROGRAM
            
            #pragma vertex vert
            #pragma fragment frag
            
            #include "Lighting.cginc"
            
            fixed4 _Diffuse;
            
            struct a2v {
                float4 vertex : POSITION;
                float3 normal : NORMAL;
            };
            
            struct v2f {
                float4 pos : SV_POSITION;
                fixed3 color : COLOR;
            };
            
            v2f vert(a2v v) {
                v2f o;
                // Transform the vertex from object space to projection space
                o.pos = mul(UNITY_MATRIX_MVP, v.vertex);
                
                // Get ambient term
                fixed3 ambient = UNITY_LIGHTMODEL_AMBIENT.xyz;
                
                // Transform the normal from object space to world space
                fixed3 worldNormal = normalize(mul(v.normal, (float3x3)_World2Object));
                // Get the light direction in world space
                fixed3 worldLight = normalize(_WorldSpaceLightPos0.xyz);
                // Compute diffuse term
                fixed3 diffuse = _LightColor0.rgb * _Diffuse.rgb * saturate(dot(worldNormal, worldLight));
                
                o.color = ambient + diffuse;
                
                return o;
            }
            
            fixed4 frag(v2f i) : SV_Target {
                return fixed4(i.color, 1.0);
            }
            
            ENDCG
        }
    }
    FallBack "Diffuse"
}

HLSL:

Shader "Unity Shaders Book/Chapter 6/Diffuse Vertex-Level" {
    Properties {
        _BaseColor ("BaseColor", Color) = (1, 1, 1, 1)
    }

    SubShader {
        Tags {
            "RenderType"="Opaque"
            "RenderPipeline"="UniversalPipeline"
        }

        Pass {
            HLSLPROGRAM
            #pragma vertex vert
            #pragma fragment frag

            #include "Packages/com.unity.render-pipelines.universal/ShaderLibrary/Core.hlsl"
            #include "Packages/com.unity.render-pipelines.universal/ShaderLibrary/Lighting.hlsl"

            CBUFFER_START(UnityPerMaterial)

            half4 _BaseColor;
            CBUFFER_END

            struct Attributes
            {
                float4 positionOS : POSITION;
                float3 normal:NORMAL;
            };

            struct Varyings
            {
                float4 positionCS : SV_POSITION;
                half3 color:COLOR;
            };

            Varyings vert(Attributes input)
            {
                Varyings output;

                //output.positionCS = mul(UNITY_MATRIX_MVP, input.positionOS.xyz); // 旧方法
                output.positionCS = TransformObjectToHClip(input.positionOS.xyz);

                half3 ambient = UNITY_LIGHTMODEL_AMBIENT.xyz;

                float3 worldNormal = TransformObjectToWorldNormal(input.normal);
                //float3 worldNormal = mul(input.normal, (float3x3)GetWorldToObjectMatrix());
                //float3 worldNormal = mul(input.normal, (float3x3)UNITY_MATRIX_I_M);

                float3 worldLight = normalize(_MainLightPosition.xyz);
                //float3 worldLight = normalize(GetMainLight().direction);

                half3 lightColor = _MainLightColor.rgb;
                //half3 lightColor = GetMainLight().color;

                half3 diffuse = lightColor * _BaseColor.rgb * saturate(dot(worldNormal, worldLight));
                // 也可以直接使用LightingLambert方法
                //half3 diffuse = _BaseColor.rgb * LightingLambert(lightColor, worldLight, worldNormal);

                output.color = ambient + diffuse;
                return output;
            }

            half4 frag(Varyings input): SV_Target
            {
                return half4(input.color, 1.0);
            }
            ENDHLSL
        }
    }
}

逐像素:
CG:

点击查看代码
Shader "Unity Shaders Book/Chapter 6/Diffuse Pixel-Level" {
    Properties {
        _Diffuse ("Diffuse", Color) = (1, 1, 1, 1)
    }
    SubShader {
        Pass { 
            Tags { "LightMode"="ForwardBase" }
        
            CGPROGRAM
            
            #pragma vertex vert
            #pragma fragment frag
            
            #include "Lighting.cginc"
            
            fixed4 _Diffuse;
            
            struct a2v {
                float4 vertex : POSITION;
                float3 normal : NORMAL;
            };
            
            struct v2f {
                float4 pos : SV_POSITION;
                float3 worldNormal : TEXCOORD0;
            };
            
            v2f vert(a2v v) {
                v2f o;
                // Transform the vertex from object space to projection space
                o.pos = mul(UNITY_MATRIX_MVP, v.vertex);

                // Transform the normal from object space to world space
                o.worldNormal = mul(v.normal, (float3x3)_World2Object);

                return o;
            }
            
            fixed4 frag(v2f i) : SV_Target {
                // Get ambient term
                fixed3 ambient = UNITY_LIGHTMODEL_AMBIENT.xyz;
                
                // Get the normal in world space
                fixed3 worldNormal = normalize(i.worldNormal);
                // Get the light direction in world space
                fixed3 worldLightDir = normalize(_WorldSpaceLightPos0.xyz);
                
                // Compute diffuse term
                fixed3 diffuse = _LightColor0.rgb * _Diffuse.rgb * saturate(dot(worldNormal, worldLightDir));
                
                fixed3 color = ambient + diffuse;
                
                return fixed4(color, 1.0);
            }
            
            ENDCG
        }
    } 
    FallBack "Diffuse"
}

HLSL:

Shader "Unity Shaders Book/Chapter 6/Diffuse Pixel-Level" {
    Properties {
        _BaseColor ("BaseColor", Color) = (1, 1, 1, 1)
    }

    SubShader {
        Tags {
            "RenderType"="Opaque"
            "RenderPipeline"="UniversalPipeline"
        }

        Pass {
            HLSLPROGRAM
            #pragma vertex vert
            #pragma fragment frag

            #include "Packages/com.unity.render-pipelines.universal/ShaderLibrary/Core.hlsl"
            #include "Packages/com.unity.render-pipelines.universal/ShaderLibrary/Lighting.hlsl"

            CBUFFER_START(UnityPerMaterial)

            half4 _BaseColor;
            CBUFFER_END

            struct Attributes
            {
                float4 positionOS : POSITION;
                float3 normal:NORMAL;
            };

            struct Varyings
            {
                float4 positionCS : SV_POSITION;
                float3 worldNormal:TEXCOORD0;
            };

            Varyings vert(Attributes input)
            {
                Varyings output;

                output.positionCS = TransformObjectToHClip(input.positionOS.xyz);
                output.worldNormal = TransformObjectToWorldNormal(input.normal);

                return output;
            }

            half4 frag(Varyings input): SV_Target
            {
                half3 ambient = UNITY_LIGHTMODEL_AMBIENT.xyz;

                float3 worldLight = normalize(_MainLightPosition.xyz);
                //float3 worldLight = normalize(GetMainLight().direction);

                half3 lightColor = _MainLightColor.rgb;
                //half3 lightColor = GetMainLight().color;

                half3 diffuse = lightColor * _BaseColor.rgb * saturate(dot(input.worldNormal, worldLight));
                // 也可以直接使用LightingLambert方法
                //half3 diffuse = _BaseColor.rgb * LightingLambert(lightColor, worldLight, input.worldNormal);

                half3 color = ambient + diffuse;
                return half4(color, 1.0);
            }
            ENDHLSL
        }
    }
}

半兰伯特模型:
广义的半兰伯特光照模型的公式如下:
\(C_{diffuse}=(C_{light} \cdot M_{diffuse})(\alpha (\hat{N} \cdot I)+ \beta)\)
半兰伯特光照模型没有使用max操作来防止\(\hat{N} \cdot I\)点积为负值,而是对其结果进行了一个\(\alpha\)倍的绽放再加一个\(\beta\)大小的偏移。
\(C_{diffuse}=(C_{light} \cdot M_{diffuse})(0.5 (\hat{N} \cdot I)+ 0.5)\)
因为\(\hat{N} \cdot I\)的值域为[-1,1],缩放0.5倍再加0.5正好能映射到[0,1]的范围内,在半兰伯特模型中,背光面也可以有明暗变化。
需要注意的是,半兰伯特模型是没有任何物理依据的,它仅仅是一个视觉加强技术。
CG:

点击查看代码
Shader "Unity Shaders Book/Chapter 6/Half Lambert" {
    Properties {
        _Diffuse ("Diffuse", Color) = (1, 1, 1, 1)
    }
    SubShader {
        Pass { 
            Tags { "LightMode"="ForwardBase" }
        
            CGPROGRAM
            
            #pragma vertex vert
            #pragma fragment frag
            
            #include "Lighting.cginc"
            
            fixed4 _Diffuse;
            
            struct a2v {
                float4 vertex : POSITION;
                float3 normal : NORMAL;
            };
            
            struct v2f {
                float4 pos : SV_POSITION;
                float3 worldNormal : TEXCOORD0;
            };
            
            v2f vert(a2v v) {
                v2f o;
                // Transform the vertex from object space to projection space
                o.pos = mul(UNITY_MATRIX_MVP, v.vertex);
                
                // Transform the normal from object space to world space
                o.worldNormal = mul(v.normal, (float3x3)_World2Object);
                
                return o;
            }
            
            fixed4 frag(v2f i) : SV_Target {
                // Get ambient term
                fixed3 ambient = UNITY_LIGHTMODEL_AMBIENT.xyz;
                
                // Get the normal in world space
                fixed3 worldNormal = normalize(i.worldNormal);
                // Get the light direction in world space
                fixed3 worldLightDir = normalize(_WorldSpaceLightPos0.xyz);
                
                // Compute diffuse term
                fixed halfLambert = dot(worldNormal, worldLightDir) * 0.5 + 0.5;
                fixed3 diffuse = _LightColor0.rgb * _Diffuse.rgb * halfLambert;
                
                fixed3 color = ambient + diffuse;
                
                return fixed4(color, 1.0);
            }
            
            ENDCG
        }
    } 
    FallBack "Diffuse"
}

HLSL:

Shader "Unity Shaders Book/Chapter 6/Half Lambert" {
    Properties {
        _BaseColor ("BaseColor", Color) = (1, 1, 1, 1)
    }

    SubShader {
        Tags {
            "RenderType"="Opaque"
            "RenderPipeline"="UniversalPipeline"
        }

        Pass {
            HLSLPROGRAM
            #pragma vertex vert
            #pragma fragment frag

            #include "Packages/com.unity.render-pipelines.universal/ShaderLibrary/Core.hlsl"
            #include "Packages/com.unity.render-pipelines.universal/ShaderLibrary/Lighting.hlsl"

            CBUFFER_START(UnityPerMaterial)

            half4 _BaseColor;
            CBUFFER_END

            struct Attributes
            {
                float4 positionOS : POSITION;
                float3 normal:NORMAL;
            };


            struct Varyings
            {
                float4 positionCS : SV_POSITION;
                float3 worldNormal:TEXCOORD0;
            };

            Varyings vert(Attributes input)
            {
                Varyings output;

                output.positionCS = TransformObjectToHClip(input.positionOS.xyz);
                output.worldNormal = TransformObjectToWorldNormal(input.normal);

                return output;
            }

            half4 frag(Varyings input): SV_Target
            {
                half3 ambient = UNITY_LIGHTMODEL_AMBIENT.xyz;

                float3 worldLight = normalize(_MainLightPosition.xyz);
                half3 lightColor = _MainLightColor.rgb;

                half3 halfLambert = dot(input.worldNormal, worldLight) * 0.5 + 0.5;
                half3 diffuse = lightColor * _BaseColor.rgb * halfLambert;

                half3 color = ambient + diffuse;
                return half4(color, 1.0);
            }
            ENDHLSL
        }
    }
}

posted on 2023-12-15 07:36  kingBook  阅读(25)  评论(0编辑  收藏  举报