实现漫反射光照模型
漫反射光照符合兰伯特定律(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
}
}
}