You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

路径追踪中如何柔化硬阴影?附当前实现代码及渲染效果

如何在路径追踪中实现柔和的软阴影

看起来你遇到了路径追踪里硬阴影的问题——这在刚实现直接光照采样时很常见!你的代码已经具备了面光源多采样的基础,但阴影边缘过硬主要是因为采样策略或采样数量的问题,下面我来一步步告诉你怎么修改:

1. 先搞懂硬阴影的根源

你的代码里,对于非delta光源(比如面光源)已经做了多采样,但每个采样点的可见性是全有或全无的二进制判断:要么这个光源采样点完全可见,要么完全被遮挡。当采样数量不足时,阴影边缘就会显得生硬,甚至出现噪点;而点光源(delta光源)本身就是硬阴影,要做软阴影得模拟成小面积光源。

2. 核心修改方案

(1)增加面光源的采样数量

最直接的方法是提升面光源的采样数,比如把ns_area_light从8调整到32、64甚至更高。更多的采样点会让光源的可见区域计算更精准,阴影边缘过渡更平滑。你可以把这个参数做成可配置的,方便在渲染质量和速度之间权衡:

// 调高采样数,也可以做成可配置的全局变量
int num_light_samples = light->is_delta_light() ? 1 : 64; 
double scale = 1.0 / num_light_samples; 

(2)用分层采样替代随机采样(优化阴影质量)

纯随机采样的光源点分布可能不均匀,导致相同采样数下阴影还是有噪点。你可以修改light->sample_L方法,实现分层采样:把光源表面分成均匀的网格,每个网格采样一个点,让采样分布更规则,阴影过渡更自然。

如果你的光源类支持传入采样索引和总采样数,修改后的代码示例:

for (int i = 0; i < num_light_samples; i++) { 
    // 传递采样索引和总数,让光源实现分层采样
    Spectrum light_L = light->sample_L(hit_p, &dir_to_light, &dist_to_light, &pdf, i, num_light_samples); 
    Vector3D w_in = w2o * dir_to_light; 
    double cos_theta = std::max(0.0, w_in[2]); 
    
    // 提前跳过无效采样,提升渲染效率
    if (cos_theta == 0 || isect.bsdf->f(w_out, w_in).is_black()) {
        continue;
    }
    
    Spectrum f = isect.bsdf->f(w_out, w_in); 
    // 调整阴影射线的t范围,避免自相交问题
    Ray shadow_ray(hit_p + EPS_D * dir_to_light, dir_to_light, dist_to_light - 2*EPS_D, 0); 
    bool is_visible = !bvh->intersect(shadow_ray); 
    
    if (is_visible) { 
        L_out += (f * light_L * (cos_theta * scale / pdf)); 
    } 
}

(3)给点光源模拟软阴影

如果你的场景里有点光源(is_delta_light()返回true),它们天生会产生硬阴影。要给点光源添加软阴影效果,你需要把点光源模拟成一个微小的面光源(比如小圆盘或小矩形),然后取消它的delta光源标记,用多采样来计算光照,这样就能得到柔和的阴影。

3. 额外优化建议

  • 调整EPS_D的大小:如果这个值太小,可能会导致阴影射线和自身表面相交;太大则可能漏过近处的遮挡物,建议设置在1e-4到1e-3之间。
  • 渐进式采样:如果高采样数导致渲染时间太长,可以用渐进式采样,逐步增加采样数并实时更新画面,直到达到满意的阴影质量。
  • 重要性采样:对于大面光源,可以优先采样靠近视线方向的区域,这样在相同采样数下能得到更平滑的阴影效果。

内容的提问来源于stack exchange,提问作者bin guo

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.15 08:19:03