PBR渲染案例:babylonjs 中的PBR渲染

一、核心思想 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
}
相关推荐
BoBoZz1920 小时前
DiscreteMarchingCubes离散等值面提取算法
python·vtk·图形渲染·图形处理
BoBoZz1920 小时前
ExtractLargestIsosurface 提取最大连通域
python·vtk·图形渲染·图形处理
BoBoZz191 天前
CutWithScalars根据标量利用vtkContourFilter得到等值线
python·vtk·图形渲染·图形处理
聊天QQ:276998852 天前
机器人路径规划:基于Q-learning算法的移动机器人路径规划的,可以自定义地图,修改起始点
图形渲染
明洞日记2 天前
【VTK手册021】VTK碰撞检测核心:vtkCollisionDetectionFilter深度解析与实战
c++·图像处理·vtk·图形渲染
拿我格子衫来2 天前
图形编辑器基于Paper.js教程32:绘制贝塞尔曲线,并进行二次编辑
javascript·图像处理·编辑器·图形渲染
咨询QQ688238862 天前
飞轮储能的“电子芭蕾“:当永磁电机遇上双PWM变流器
图形渲染
米芝鱼3 天前
Unity自定义TextImage,鼠标悬浮显示信息
算法·ui·unity·编辑器·游戏引擎·图形渲染
BoBoZz193 天前
ContourTriangulator从一个PNG图像中提取2D等值线(isoline)
python·vtk·图形渲染·图形处理