一、核心思想 IBL
在IBL 下无法对某个像素的半球体进行积分,采用预计算采样等方式进行 DFG 计算
1_ D项:法线分布函数
作用: 估算在受到表面粗糙度的影响下,朝向方向与半程向量一致的微平面的数量。
处理方式: 在计算环境反射 reflectionBlcok 中, 采样反射贴图 sampleReflectionTexture 时,将粗糙度 (alphaG) 映射为 LOD 层级
这样虽然没有法线分布函数的准确积分数值,但因为已经将环境图纹理进行相应模糊,所以采样结果可近似为符合法线分布的光照结果
2_ F项:菲涅尔系数
作用:描述在不同的表面角下表面所反射的光线所占的比率。
处理方式: 不直接计算菲涅尔系数,而是通过 BRDF LUT 查表获取两个系数 environmentBrdf.x (Scale) 和 environmentBrdf.y (Bias)
然后通过公式计算最终反射率 F_total:
Ftotal=F0×Scale+F90×BiasF_{total} = F_0 \times Scale + F_{90} \times BiasFtotal=F0×Scale+F90×Bias
最终反射率的还原过程中,即考虑了菲涅尔效应的反射强度。 (G项 同理)
3_ G项:几何函数
作用:描述了微平面自成阴影的属性。当一个平面相对比较粗糙的时候,平面表面上的微平面有可能挡住其他的微平面从而减少表面所反射的光线。。
处理方式: BRDF LUT 的采样结果中已经包含了G项, 过程同 F 项
二、当具有真实光源时
则分别计算每个光源的DFG
调用 DFG 函数 (实时计算):这里不再查表,而是直接调用代码里已经写好的数学函数:
D 项: 调用 normalDistributionFunction_TrowbridgeReitzGGX(NdotH, alphaG)。算高光点:看 HHH 和法线 NNN 是否重合。
F 项: 调用 fresnelSchlickGGX(VdotH, f0, f90)。算反射率:看视线和 HHH 的夹角。
G 项: 调用 smithVisibility_GGXCorrelated(NdotL, NdotV, alphaG)。算遮挡:看光线和视线是否被粗糙表面挡住。
直接相乘:结果=D×F×G4×(N⋅V)×(N⋅L)×LightColor×(N⋅L)结果 = \frac{D \times F \times G}{4 \times (N \cdot V) \times (N \cdot L)} \times \text{LightColor} \times (N \cdot L)结果=4×(N⋅V)×(N⋅L)D×F×G×LightColor×(N⋅L)只需简单的乘除法,一次搞定。
三、主函数代码(无真实光源)
c
void main(void) {
vec4 fragOut1 = vec4(vNormalV, 1.0);
vec4 fragOut2 = vec4(vNormalV, 1.0);
//---->>>> 计算视线、法线等相关变量
#define CUSTOM_FRAGMENT_MAIN_BEGIN
vec3 viewDirectionW = normalize(vEyePosition.xyz-vPositionW);
vec3 normalW = normalize(vNormalW);
vec3 geometricNormalW = normalW;
geometricNormalW = gl_FrontFacing ? geometricNormalW : -geometricNormalW;
vec2 uvOffset = vec2(0.0, 0.0);
#define CUSTOM_WATER
normalW = gl_FrontFacing ? normalW : -normalW;
//---->>>> 基础色、透明度等变量拆解到 albedoOpacityOut
albedoOpacityOutParams albedoOpacityOut;
albedoOpacityBlock(
vAlbedoColor, albedoOpacityOut
);
vec3 surfaceAlbedo = albedoOpacityOut.surfaceAlbedo;
float alpha = albedoOpacityOut.alpha;
#define CUSTOM_FRAGMENT_UPDATE_ALPHA
#define CUSTOM_FRAGMENT_BEFORE_LIGHTS
//---->>>> 环境光遮蔽等变量拆解到 aoOut
ambientOcclusionOutParams aoOut;
ambientOcclusionBlock(
aoOut
);
//---->>>> 反射率等变量拆解到 reflectivityOut (经过相应计算)
vec3 baseColor = surfaceAlbedo;
reflectivityOutParams reflectivityOut;
vec4 metallicReflectanceFactors = vMetallicReflectanceFactors; //金属反射率系数
reflectivityBlock(
vReflectivityColor, surfaceAlbedo, metallicReflectanceFactors, reflectivityOut
); //ReflectivityColor:反射率颜色
float microSurface = reflectivityOut.microSurface;
float roughness = reflectivityOut.roughness;
surfaceAlbedo = reflectivityOut.surfaceAlbedo;
// 1. 计算视线与法线的夹角 (NdotV)
float NdotVUnclamped = dot(normalW, viewDirectionW);
float NdotV = absEps(NdotVUnclamped);
//2. 将感性粗糙度转换为几何斜率 alphaG (用于数学公式)
float alphaG = convertRoughnessToAverageSlope(roughness); //粗糙度平方 + 0.0005
vec2 AARoughnessFactors = getAARoughnessFactors(normalW.xyz); //抗锯齿相关,为 vec2(0.0, 0.0)
//3. BRDF 查表LUT: 根据 NdotV 和粗糙度 roughness 查表获取环境BRDF相关参数 scale、bias (强度系数、菲涅尔缩放因子)
//即 获取不同粗糙度、不同视线下 对环境光的镜面反射率
vec3 environmentBrdf = getBRDFLookup(NdotV, roughness);
//4. 计算环境光遮蔽因子 seo (环境光遮蔽系数)
float ambientMonochrome = getLuminance(aoOut.ambientOcclusionColor);
float seo = environmentRadianceOcclusion(ambientMonochrome, NdotVUnclamped);
//5. 计算环境反射
// 计算环境反射的辐射度(环境镜面反射)和辐照度(环境漫反射)
reflectionOutParams reflectionOut;
reflectionBlock(
vPositionW, normalW, alphaG, vReflectionMicrosurfaceInfos, vReflectionInfos, vReflectionColor, reflectionSampler, reflectionMatrix, reflectionOut
);
//最大反射率、F0、F90
float reflectance = max(max(reflectivityOut.surfaceReflectivityColor.r, reflectivityOut.surfaceReflectivityColor.g), reflectivityOut.surfaceReflectivityColor.b);
vec3 specularEnvironmentR0 = reflectivityOut.surfaceReflectivityColor.rgb;
vec3 specularEnvironmentR90 = vec3(metallicReflectanceFactors.a);
clearcoatOutParams clearcoatOut;
clearcoatOut.specularEnvironmentR0 = specularEnvironmentR0;
//6. 计算最终反射率; 公式:(F90 - F0)* brdf.x + F0 * brdf.y
vec3 specularEnvironmentReflectance = getReflectanceFromBRDFLookup(clearcoatOut.specularEnvironmentR0, specularEnvironmentR90, environmentBrdf);
specularEnvironmentReflectance *= seo;
subSurfaceOutParams subSurfaceOut;
subSurfaceOut.specularEnvironmentReflectance = specularEnvironmentReflectance;
vec3 diffuseBase = vec3(0., 0., 0.);
preLightingInfo preInfo;
lightingInfo info;
float shadow = 1.;
float aggShadow = 0.;
float numLights = 0.;
aggShadow = aggShadow/numLights;
//7. 计算能量守恒因子
vec3 energyConservationFactor = getEnergyConservationFactor(clearcoatOut.specularEnvironmentR0, environmentBrdf);
//---->>>> 各颜色混合
vec3 finalIrradiance = reflectionOut.environmentIrradiance;
finalIrradiance *= surfaceAlbedo.rgb;
finalIrradiance *= vLightingIntensity.z;
finalIrradiance *= aoOut.ambientOcclusionColor;
vec3 finalRadiance = reflectionOut.environmentRadiance.rgb;
finalRadiance *= subSurfaceOut.specularEnvironmentReflectance;
vec3 finalRadianceScaled = finalRadiance*vLightingIntensity.z;
finalRadianceScaled *= energyConservationFactor;
vec3 finalDiffuse = diffuseBase;
finalDiffuse *= surfaceAlbedo.rgb;
finalDiffuse = max(finalDiffuse, 0.0);
finalDiffuse *= vLightingIntensity.x;
vec3 finalAmbient = vAmbientColor;
finalAmbient *= surfaceAlbedo.rgb;
vec3 finalEmissive = vEmissiveColor;
finalEmissive *= vLightingIntensity.y;
vec3 ambientOcclusionForDirectDiffuse = aoOut.ambientOcclusionColor;
finalAmbient *= aoOut.ambientOcclusionColor;
finalDiffuse *= ambientOcclusionForDirectDiffuse;
#define CUSTOM_FRAGMENT_BEFORE_FINALCOLORCOMPOSITION
vec4 finalColor = vec4(
finalIrradiance +
finalRadianceScaled +
finalAmbient +
finalDiffuse, alpha);
finalColor.rgb += finalEmissive;
#define CUSTOM_FRAGMENT_BEFORE_FOG
finalColor = max(finalColor, 0.0);
finalColor = applyImageProcessing(finalColor);
finalColor.a *= visibility;
// bool reflectionEnable = bool(ssrStrength);
//
// int normalVR = int(vNormalV.r * 255.0);
// normalVR = (normalVR & ~1) | int(reflectionEnable);
// float newR = float(normalVR) / 255.0;
//
// // vNormalVClone.r = newR;
//
// if(reflectionEnable){
// vNormalVClone.rgb = vec3(1.0);
// }
// else{
// vNormalVClone.r = 0.0;
// }
fragOut1.rgb = (normalW + 1.0) / 2.0;
fragOut2.r = ssrStrength;
#define CUSTOM_FRAGMENT_BEFORE_FRAGCOLOR
#define CUSTOM_COAT
// glFragColor = finalColor;
glFragData[0] = finalColor;
glFragData[1] = fragOut1;
// glFragData[2] = fragOut2;
if(fragOut2.r >= 0.0) glFragData[2] = fragOut2;
//部分不写入反射率
//插入END之前
#define CUSTOM_FRAGMENT_MAIN_END
}