预过滤环境光贴图制作教程:第三阶段 - GGX 分布预过滤

发布于:2025-07-31 ⋅ 阅读:(21) ⋅ 点赞:(0)

核心目标

GGX 分布是 PBR 中模拟粗糙表面高光反射的主流模型,其核心是通过统计分布描述微表面的朝向概率。本阶段的目标是:

  1. 基于第一阶段生成的环境图集,预计算 6 个级别的 GGX 过滤结果(对应不同粗糙度);
  2. 使用蒙特卡洛采样(Monte Carlo Sampling)加速 GGX 卷积计算;
  3. 将预过滤结果存储在环境图集的指定区域,与 Stage 1 的基础数据形成完整的环境数据集合。

这些预计算数据将使实时渲染时无需动态计算复杂的 GGX 卷积,只需直接采样即可获得符合物理规律的高光反射颜色。

准备工作

  • 前置条件:已完成 Stage 1,获得包含基础环境数据的envAtlas
  • 输入资源:sourceCube(原始立方体贴图)、envAtlas(待写入预过滤结果的图集);
  • 工具依赖:Three.js 环境(rendererscenecamera)、样本生成工具(SampleGenerator);
  • 理论基础:了解 GGX 分布模型、蒙特卡洛采样、PBR 高光反射原理。

GGX 分布与预过滤原理

在 PBR 中,表面的高光反射效果由微表面分布函数(NDF) 决定,GGX 是其中应用最广泛的模型,其分布函数为:

对于环境光反射,需要对整个环境贴图按 GGX 分布进行卷积(加权平均),得到该粗糙度下的 “模糊” 环境贴图。由于实时计算这一卷积成本极高,我们通过预计算(离线完成)并存储结果,实时渲染时直接采样,大幅提升性能。

实现步骤详解

步骤 1:明确图集存储区域

预过滤结果将存储在envAtlas的另一部分区域,与上阶段的基础数据分区存放。以 512x512 图集为例,布局如下:

级别(i) 对应粗糙度 图集内位置(x,y) 分辨率(宽 x 高) specularPower(高光强度)
1 (0, 256*s) 256x128 512
2 中低 (0, 256s + 128s) 128x64 128
3 (0, 256s + 128s + 64*s) 64x32 32
4 中高 ... 32x16 8
5 ... 16x8 2
6 极高 ... 8x4 1

  • 级别递增对应粗糙度递增(specularPower递减);
  • 分辨率随级别递增减半(与 Stage 1 逻辑一致,粗糙度越高,所需细节越少)。

步骤 2:顶点着色器(UV 坐标处理)

与 Stage 1 类似,顶点着色器负责传递调整后的 UV 坐标,支持接缝处理:

uniform vec4 uvMod;  // UV调整参数:(scaleU, scaleV, offsetU, offsetV)
varying vec2 vUv;    // 传递给片段着色器的UV

void main() {
    // 标准顶点变换:投影到裁剪空间
    gl_Position = projectionMatrix * modelViewMatrix * vec4(position, 1.0);
    // 计算UV:扩展范围以包含接缝像素
    vUv = (position.xy * 0.5 + 0.5) * uvMod.xy + uvMod.zw;
}

  • 复用 Stage 1 的 UV 扩展逻辑,通过uvMod确保渲染内容包含接缝像素,避免边缘采样误差。

步骤 3:片段着色器(GGX 预过滤核心逻辑)

片段着色器是本阶段的核心,实现基于蒙特卡洛采样的 GGX 卷积计算,包含样本解码、方向转换、立方体贴图采样等关键步骤。

3.1 基础变量与常量
precision highp float;
varying vec2 vUv;                  // 顶点传递的UV
uniform samplerCube sourceCube;    // 原始立方体贴图
uniform sampler2D samplesTex;      // 预生成的GGX样本纹理
uniform vec2 samplesTexInverseSize;// 样本纹理尺寸的倒数(用于计算UV)
uniform vec4 params;               // [_, specularPower, 接缝缩放系数, _]
const float PI = 3.141592653589793;
const int NUM_SAMPLES = 1024;      // 每个像素的采样数量(平衡质量与性能)
3.2 RGBP 编码(复用 Stage 1 逻辑)

预过滤结果仍采用 RGBP 编码存储,确保 HDR 数据高效压缩:

vec4 encodeRGBP(vec3 source) {
    vec3 gamma = pow(source, vec3(0.5));  // gamma校正(平方根)
    float maxVal = min(8.0, max(1.0, max(gamma.x, max(gamma.y, gamma.z))));  // 限制最大范围
    float v = 1.0 - ((maxVal - 1.0) / 7.0);  // 编码缩放因子
    v = ceil(v * 255.0) / 255.0;  // 确保8位精度存储
    return vec4(gamma / (-v * 7.0 + 8.0), v);  // 缩放颜色并返回
}
3.3 方向计算与接缝处理(复用与扩展)

与 Stage 1 相同,需将等矩形 UV 转换为三维方向向量,并处理立方体贴图接缝:

// 球坐标转三维方向向量
vec3 fromSpherical(vec2 uv) {
    return vec3(
        cos(uv.y) * sin(uv.x),  // x分量
        sin(uv.y),              // y分量
        cos(uv.y) * cos(uv.x)   // z分量
    );
}

// 从等矩形UV计算方向向量(指向环境中的采样点)
vec3 getDirectionEquirect() {
    // 转换UV范围至球坐标:U→[-π,π],V→[π/2,-π/2](翻转V轴)
    vec2 spherical = (vec2(vUv.x, 1.0 - vUv.y) * 2.0 - 1.0) * vec2(PI, PI * 0.5);
    return fromSpherical(spherical);
}

// 调整方向向量以减轻立方体贴图接缝
vec3 modifySeams(vec3 dir, float scale

网站公告

今日签到

点亮在社区的每一天
去签到