|
PBR工作流
URP管线的PBR有两个工作流,分别是:
- Metallic(金属工作流)
- Specular(高光工作流)
Metallic(金属工作流)
- BaseMap:表示物体颜色,最终用来计算物体表面的漫反射颜色和镜面反射颜色。
- MetallicMap:表示金属度,范围0-1,最终用来计算物体表面的漫反射率和镜面反射率。
- Smoothness:表示光滑度,范围0-1,最终用来计算物体表面的粗糙度。
//universal/ShaderLibrary/BRDF.hlsl
#define kDielectricSpec half4(0.04, 0.04, 0.04, 1.0 - 0.04)
//计算漫反射率和镜面反射率
half oneMinusReflectivity = OneMinusReflectivityMetallic(metallic); //计算漫反射率,范围0.96-0
half reflectivity = half(1.0) - oneMinusReflectivity; //计算反射率,范围0.04-1
half3 brdfDiffuse = albedo * oneMinusReflectivity; //计算漫反射颜色,oneMinusReflectivity为漫反射率
half3 brdfSpecular = lerp(kDieletricSpec.rgb, albedo, metallic); //计算镜面反射颜色,范围0.04-albedo
InitializeBRDFDataDirect(albedo, brdfDiffuse, brdfSpecular, reflectivity, oneMinusReflectivity, smoothness, alpha, outBRDFData);
//计算漫反射率(metallic范围0-1,计算结果为0.96-0)
half OneMinusReflectivityMetallic(half metallic)
{
half oneMinusDielectricSpec = kDielectricSpec.a;
return oneMinusDielectricSpec - metallic * oneMinusDielectricSpec;
}漫反射率 = 0.96 - metallic * 0.96
高光反射率 = 1 - 漫反射率
漫反射颜色 = BaseMap * 漫反射率
高光反射颜色 = lerp(half4(0.04, 0.04, 0.04, 0.96), BaseMap, metallic)
注意1:当Metallic为0时,高光反射率为0.04(所以依然会有微弱的高光)。
注意2:当Metallic为1时,高光反射率为1,漫反射颜色为黑色,高光的颜色就是BaseMap,这也符合金属的性质(金属的高光颜色就是金属自身的颜色,和灯光颜色无关)。
Specular(高光工作流)
- BaseMap:表示物体颜色,最终用来计算物体表面的漫反射颜色和镜面反射颜色。
- SpecularMap:表示高光,最终用来计算物体表面的漫反射率和镜面反射率。
- Smoothness:表示光滑度,范围0-1,最终用来计算物体表面的粗糙度。
//universal/ShaderLibrary/BRDF.hlsl
#define kDielectricSpec half4(0.04, 0.04, 0.04, 1.0 - 0.04)
//计算漫反射率和镜面反射率
half reflectivity = ReflectivitySpecular(specular); //计算镜面反射率,范围0-1
half oneMinusReflectivity = half(1.0) - reflectivity; //计算漫反射率,范围0-1
half3 brdfDiffuse = albedo * (half3(1.0, 1.0, 1.0) - specular); //计算漫反射颜色
half3 brdfSpecular = specular; //计算镜面反射颜色
InitializeBRDFDataDirect(albedo, brdfDiffuse, brdfSpecular, reflectivity, oneMinusReflectivity, smoothness, alpha, outBRDFData);
//计算反射率(GLES就用简单的算法,否则使用较复杂的算法)
half ReflectivitySpecular(half3 specular)
{
#if defined(SHADER_API_GLES)
return specular.r; // Red channel - because most metals are either monocrhome or with redish/yellowish tint
#else
return Max3(specular.r, specular.g, specular.b);
#endif
}高光反射率 = Max3(specular.r, specular.g, specular.b)
漫反射率 = 1 - 高光反射率
漫反射颜色 = BaseMap * (half3(1.0, 1.0, 1.0) - specular)
高光反射颜色 = specular
注意:当specular为黑色时,高光颜色为黑色,漫反射颜色为BaseMap的颜色,当specular为1时,高光颜色为specular颜色,漫反射颜色为黑色。
计算BRDFData
经过如上计算,我们获得了漫反射率、高光反射率、漫反射颜色、高光反射颜色,接下来Unity会根据这些数据构建一个数据结构BRDFData,用来进行接下来的BRDF计算。
//universal/ShaderLibrary/BRDF.hlsl
// 根据之前结算的漫反射率、高光反射率、漫反射颜色、高光反射颜色,光滑度,计算一个数据结构,用来计算BRDF。
inline void InitializeBRDFDataDirect(half3 albedo, half3 diffuse, half3 specular, half reflectivity, half oneMinusReflectivity, half smoothness, inout half alpha, out BRDFData outBRDFData)
{
outBRDFData = (BRDFData)0;
outBRDFData.albedo = albedo;
outBRDFData.diffuse = diffuse;
outBRDFData.specular = specular;
outBRDFData.reflectivity = reflectivity;
outBRDFData.perceptualRoughness = PerceptualSmoothnessToPerceptualRoughness(smoothness);//1-smoothness
outBRDFData.roughness = max(PerceptualRoughnessToRoughness(outBRDFData.perceptualRoughness), HALF_MIN_SQRT);//取平方
outBRDFData.roughness2 = max(outBRDFData.roughness * outBRDFData.roughness, HALF_MIN);//再取平方
outBRDFData.grazingTerm = saturate(smoothness + reflectivity);
outBRDFData.normalizationTerm = outBRDFData.roughness * half(4.0) + half(2.0);
outBRDFData.roughness2MinusOne = outBRDFData.roughness2 - half(1.0);
#ifdef _ALPHAPREMULTIPLY_ON
outBRDFData.diffuse *= alpha;
alpha = alpha * oneMinusReflectivity + reflectivity; // NOTE: alpha modified and propagated up.
#endif
}
/////////////////////////////////////////////////////////////////////////////
core/ShaderLibrary/CommonMaterial.hlsl
// 计算粗糙度
real PerceptualSmoothnessToPerceptualRoughness(real perceptualSmoothness)
{
return (1.0 - perceptualSmoothness);
}
// 计算粗糙度的平方
real PerceptualRoughnessToRoughness(real perceptualRoughness)
{
return perceptualRoughness * perceptualRoughness;
}
/////////////////////////////////////////////////////////////////////////////
core/ShaderLibrary\Macros.hlsl
#define HALF_MIN_SQRT 0.0078125注意:Unity用粗糙度的平方作为粗糙度roughness,用粗糙度的4次方作为roughness2。
计算高光BRDF
Unity的高光BRDF使用了CookTorrance模型,其由表面法线分布D、集合遮蔽V、菲涅耳反射F组成,下面三个公式为Unity简化版。
- BRDF = \frac{DVF}{4}
- D = \frac{roughness^2}{( NH^2 \cdot (roughness^2 - 1) + 1 )^2}
- V \cdot F = \frac{1.0}{ LH^2 \cdot (roughness + 0.5) }
// universal/ShaderLibrary/BRDF.hlsl
// 真正计算BRDF
// Computes the scalar specular term for Minimalist CookTorrance BRDF
// NOTE: needs to be multiplied with reflectance f0, i.e. specular color to complete
half DirectBRDFSpecular(BRDFData brdfData, half3 normalWS, half3 lightDirectionWS, half3 viewDirectionWS)
{
float3 lightDirectionWSFloat3 = float3(lightDirectionWS);
float3 halfDir = SafeNormalize(lightDirectionWSFloat3 + float3(viewDirectionWS));
float NoH = saturate(dot(float3(normalWS), halfDir));
half LoH = half(saturate(dot(lightDirectionWSFloat3, halfDir)));
// GGX Distribution multiplied by combined approximation of Visibility and Fresnel
// BRDFspec = (D * V * F) / 4.0
// D = roughness^2 / ( NoH^2 * (roughness^2 - 1) + 1 )^2
// V * F = 1.0 / ( LoH^2 * (roughness + 0.5) )
// See "Optimizing PBR for Mobile" from Siggraph 2015 moving mobile graphics course
// https://community.arm.com/events/1155
// Final BRDFspec = roughness^2 / ( NoH^2 * (roughness^2 - 1) + 1 )^2 * (LoH^2 * (roughness + 0.5) * 4.0)
// We further optimize a few light invariant terms
// brdfData.normalizationTerm = (roughness + 0.5) * 4.0 rewritten as roughness * 4.0 + 2.0 to a fit a MAD.
float d = NoH * NoH * brdfData.roughness2MinusOne + 1.00001f;
half d2 = half(d * d);
half LoH2 = LoH * LoH;
half specularTerm = brdfData.roughness2 / (d2 * max(half(0.1), LoH2) * brdfData.normalizationTerm);
// On platforms where half actually means something, the denominator has a risk of overflow
// clamp below was added specifically to "fix" that, but dx compiler (we convert bytecode to metal/gles)
// sees that specularTerm have only non-negative terms, so it skips max(0,..) in clamp (leaving only min(100,...))
#if defined (SHADER_API_MOBILE) || defined (SHADER_API_SWITCH)
specularTerm = specularTerm - HALF_MIN;
specularTerm = clamp(specularTerm, 0.0, 100.0); // Prevent FP16 overflow on mobiles
#endif
return specularTerm;
}
// core/ShaderLibrary\Macros.hlsl
#define HALF_MIN 6.103515625e-5计算直接光
物体表面颜色(直接光)= 漫反射颜色 + 高光BRDF * 高光颜色
// Based on Minimalist CookTorrance BRDF
// Implementation is slightly different from original derivation: http://www.thetenthplanet.de/archives/255
//
// * NDF [Modified] GGX
// * Modified Kelemen and Szirmay-Kalos for Visibility term
// * Fresnel approximated with 1/LdotH
half3 DirectBDRF(BRDFData brdfData, half3 normalWS, half3 lightDirectionWS, half3 viewDirectionWS, bool specularHighlightsOff)
{
// Can still do compile-time optimisation.
// If no compile-time optimized, extra overhead if branch taken is around +2.5% on some untethered platforms, -10% if not taken.
[branch] if (!specularHighlightsOff)
{
half specularTerm = DirectBRDFSpecular(brdfData, normalWS, lightDirectionWS, viewDirectionWS);
half3 color = brdfData.diffuse + specularTerm * brdfData.specular;
return color;
}
else
return brdfData.diffuse;
}
// Based on Minimalist CookTorrance BRDF
// Implementation is slightly different from original derivation: http://www.thetenthplanet.de/archives/255
//
// * NDF [Modified] GGX
// * Modified Kelemen and Szirmay-Kalos for Visibility term
// * Fresnel approximated with 1/LdotH
half3 DirectBRDF(BRDFData brdfData, half3 normalWS, half3 lightDirectionWS, half3 viewDirectionWS)
{
#ifndef _SPECULARHIGHLIGHTS_OFF
return brdfData.diffuse + DirectBRDFSpecular(brdfData, normalWS, lightDirectionWS, viewDirectionWS) * brdfData.specular;
#else
return brdfData.diffuse;
#endif
} |
|