实现高光反射光照模型
Phong高光反射模型
\(C_{specular}=(C_{light} \cdot M_{specular})max(0,\hat{V} \cdot \hat{R})^{M_{gloss}}\)
其中,\(C_{light}\) 是光源的颜色,\(M_{specular}\) 是材质的高光反射颜色,\(\hat{V}\) 是由顶点指向相机的单位向量,\(\hat{R}\) 是光源通过顶点的反射单位向量,\(M_{gloss}\) 是材质的光泽度(gloss),下例的取值范围[8,256]。同样,需要max函数防止 \(\hat{V} \cdot \hat{R}\) 点积的结果为负值。
逐顶点光照:
CG:https://github.com/ding-yan-qing/Unity_Shaders_Book/blob/master/Assets/Shaders/Chapter6/Chapter6-SpecularVertexLevel.shader
HLSL:
Shader "Unity Shaders Book/Chapter 6/Specular Vertex-Level" {
Properties {
_Diffuse ("Diffuse", Color) = (1, 1, 1, 1)
_Specular ("Specular", Color) = (1, 1, 1, 1)
_Gloss ("Gloss", Range(8.0, 256)) = 20
}
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 _Diffuse;
half4 _Specular;
float _Gloss;
CBUFFER_END
struct Attributes
{
float4 positionOS : POSITION;
half3 normal:NORMAL;
};
struct Varyings
{
float4 positionCS : SV_POSITION;
half3 color:COLOR;
};
Varyings vert(Attributes input)
{
Varyings output;
output.positionCS = TransformObjectToHClip(input.positionOS.xyz);
half3 ambient = UNITY_LIGHTMODEL_AMBIENT.xyz;
half3 worldNormal = TransformObjectToWorldNormal(input.normal);
Light mainLight = GetMainLight();
half3 diffuse = mainLight.color * _Diffuse.rgb * saturate(dot(worldNormal, mainLight.direction));
// 注意此处的光源方向默认是由顶点指向光源,因此需要取反
half3 reflectDir = normalize(reflect(-mainLight.direction, worldNormal));
half3 viewDir = normalize(GetCameraPositionWS() - TransformObjectToWorld(input.positionOS.xyz));
half3 specular = mainLight.color * _Specular.rgb * pow(saturate(dot(reflectDir, viewDir)), _Gloss);
output.color = ambient + diffuse + specular;
return output;
}
half4 frag(Varyings input): SV_Target
{
return half4(input.color, 1.0);
}
ENDHLSL
}
}
}
逐像素光照:
CG:https://github.com/ding-yan-qing/Unity_Shaders_Book/blob/master/Assets/Shaders/Chapter6/Chapter6-SpecularPixelLevel.shader
HLSL:
Shader "Unity Shaders Book/Chapter 6/Specular Pixel-Level" {
Properties {
_Diffuse ("Diffuse", Color) = (1, 1, 1, 1)
_Specular ("Specular", Color) = (1, 1, 1, 1)
_Gloss ("Gloss", Range(8.0, 256)) = 20
}
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 _Diffuse;
half4 _Specular;
float _Gloss;
CBUFFER_END
struct Attributes
{
float4 positionOS : POSITION;
half3 normal:NORMAL;
};
struct Varyings
{
float4 positionCS : SV_POSITION;
float3 worldNormal : TEXCOORD0;
float3 worldPos : TEXCOORD1;
};
Varyings vert(Attributes input)
{
Varyings output;
output.positionCS = TransformObjectToHClip(input.positionOS.xyz);
output.worldNormal = TransformObjectToWorldNormal(input.normal);
output.worldPos = TransformObjectToWorld(input.positionOS.xyz);
return output;
}
half4 frag(Varyings input): SV_Target
{
half3 ambient = UNITY_LIGHTMODEL_AMBIENT.xyz;
half3 worldNormal = input.worldNormal;
Light mainLight = GetMainLight();
half3 diffuse = mainLight.color * _Diffuse.rgb * saturate(dot(worldNormal, mainLight.direction));
// 注意此处的光源方向默认是由顶点指向光源,因此需要取反
half3 reflectDir = normalize(reflect(-mainLight.direction, worldNormal));
half3 viewDir = normalize(GetCameraPositionWS() - input.worldPos.xyz);
half3 specular = mainLight.color * _Specular.rgb * pow(saturate(dot(reflectDir, viewDir)), _Gloss);
half3 color = ambient + diffuse + specular;
return half4(color, 1.0);
}
ENDHLSL
}
}
}
Blinn-Phong高光反射模型:
Blinn模型引入了一个新的向量\(\hat{H}\)
\(\hat{H}=\frac{\hat{V}+I}{|\hat{V}+I|}\)
然后,使用\(\hat{N}\)和\(\hat{H}\)之间的夹角进行计算,而非\(\hat{V}\)和\(\hat{R}\)之间的夹角。
\(C_{specular}=(C_{light} \cdot M_{specular})max(0,\hat{N} \cdot \hat{H})^{M_{gloss}}\)
其中,\(C_{light}\) 是光源的颜色,\(M_{specular}\) 是材质的高光反射颜色,\(\hat{N}\) 是顶点的单位化法线向量,\(M_{gloss}\) 是材质的光泽度(gloss),下例的取值范围[8,256]。同样,需要max函数防止 \(\hat{N} \cdot \hat{H}\) 点积的结果为负值。
CG:https://github.com/ding-yan-qing/Unity_Shaders_Book/blob/master/Assets/Shaders/Chapter6/Chapter6-BlinnPhong.shader
HLSL:
Shader "Unity Shaders Book/Chapter 6/Blinn Phong" {
Properties {
_Diffuse ("Diffuse", Color) = (1, 1, 1, 1)
_Specular ("Specular", Color) = (1, 1, 1, 1)
_Gloss ("Gloss", Range(8.0, 256)) = 20
}
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 _Diffuse;
half4 _Specular;
float _Gloss;
CBUFFER_END
struct Attributes
{
float4 positionOS : POSITION;
half3 normal:NORMAL;
};
struct Varyings
{
float4 positionCS : SV_POSITION;
float3 worldNormal : TEXCOORD0;
float3 worldPos : TEXCOORD1;
};
Varyings vert(Attributes input)
{
Varyings output;
output.positionCS = TransformObjectToHClip(input.positionOS.xyz);
output.worldNormal = TransformObjectToWorldNormal(input.normal);
output.worldPos = TransformObjectToWorld(input.positionOS.xyz);
return output;
}
half4 frag(Varyings input): SV_Target
{
half3 ambient = UNITY_LIGHTMODEL_AMBIENT.xyz;
half3 worldNormal = input.worldNormal;
Light mainLight = GetMainLight();
half3 diffuse = mainLight.color * _Diffuse.rgb * saturate(dot(worldNormal, mainLight.direction));
// 注意此处的光源方向默认是由顶点指向光源,因此需要取反
half3 reflectDir = normalize(reflect(-mainLight.direction, worldNormal));
half3 viewDir = normalize(GetCameraPositionWS() - input.worldPos.xyz);
half3 halfDir = normalize(mainLight.direction + viewDir);
half3 specular = mainLight.color * _Specular.rgb * pow(saturate(dot(worldNormal, halfDir)), _Gloss);
half3 color = ambient + diffuse + specular;
return half4(color, 1.0);
}
ENDHLSL
}
}
}