Skip to content

路径追踪

蒙特卡洛路径追踪是一种全局光照算法,通过求解渲染方程产生逼真的光照效果。

渲染方程

路径追踪的核心是求解 Kajiya 渲染方程:

$$L_o(x, \omega_o) = L_e(x, \omega_o) + \int_{\Omega} f_r(x, \omega_i, \omega_o) L_i(x, \omega_i) (\omega_i \cdot n) d\omega_i$$

其中:

  • $L_o$ 是出射辐射亮度
  • $L_e$ 是自发光
  • $f_r$ 是 BRDF
  • $L_i$ 是入射辐射亮度
  • $\omega_i \cdot n$ 是余弦项

蒙特卡洛积分

使用蒙特卡洛方法估计积分:

$$\int f(x) dx \approx \frac{1}{N} \sum_{i=1}^{N} \frac{f(X_i)}{p(X_i)}$$

应用到渲染方程:

$$L_o \approx L_e + \frac{1}{N} \sum_{i=1}^{N} \frac{f_r L_i (\omega_i \cdot n)}{p(\omega_i)}$$

余弦加权采样

对于 Lambertian 材质,使用余弦加权半球采样:

$$p(\omega) = \frac{\cos\theta}{\pi}$$

采样方向生成:

cpp
__device__ vec3 cosine_weighted_hemisphere(curandState* state) {
    float r1 = curand_uniform(state);
    float r2 = curand_uniform(state);

    float phi = 2.0f * M_PI * r1;
    float sqrt_r2 = sqrtf(r2);

    float x = cosf(phi) * sqrt_r2;
    float y = sinf(phi) * sqrt_r2;
    float z = sqrtf(1.0f - r2);

    return vec3(x, y, z);
}

俄罗斯轮盘赌

为避免无限递归,使用俄罗斯轮盘赌终止:

$$L = \frac{L_{direct} + p_{rr} \cdot L_{indirect}}{p_{rr}}$$

当路径长度 > 3 时,以概率 $p_{rr} = 0.8$ 继续追踪:

cpp
__device__ bool russian_roulette(curandState* state, int depth) {
    if (depth <= 3) return true;
    return curand_uniform(state) < 0.8f;
}

完整路径追踪 kernel

cpp
__device__ vec3 trace_path(
    const Ray& ray,
    const Scene& scene,
    curandState* state,
    int max_depth
) {
    vec3 radiance = vec3(0.0f);
    vec3 throughput = vec3(1.0f);
    Ray current_ray = ray;

    for (int depth = 0; depth < max_depth; ++depth) {
        HitRecord hit;
        if (!scene.intersect(current_ray, hit)) {
            radiance += throughput * scene.background(current_ray);
            break;
        }

        // 发光材质
        radiance += throughput * hit.material->emitted();

        // 俄罗斯轮盘赌
        if (depth > 3 && !russian_roulette(state, depth)) break;

        // 采样 BSDF
        vec3 wi;
        float pdf;
        vec3 f = hit.material->sample(state, hit, wi, pdf);

        // 更新吞吐量
        throughput *= f * fabsf(dot(wi, hit.normal)) / pdf;

        // 更新光线
        current_ray = Ray(hit.p, wi);
    }

    return radiance;
}

降噪

高采样数收敛慢,可使用降噪技术:

  • 时域累积
  • 空间滤波
  • AI 降噪(OptiX Denoiser)

参考资料

  • [Kajiya 1986] "The Rendering Equation"
  • [Pharr 2016] "Physically Based Rendering"

Technical Whitepaper · Built with VitePress