【RAY TRACING THE REST OF YOUR LIFE 超详解】 光线追踪 3-6 直接光源采样

 

Chapter7 Sample Lights Directlyhtml

 

 Preface函数

今天咱们来说这个还算牛逼的技术——直接光源采样测试

以前咱们提到过,在2-7优化

 

前两篇咱们也提到要减小噪点,就是图片上的黑点点,因此,全部的矛头都指向了这一篇。spa

简单说一下为何会有那么多小点点,就是由于光线路径中没有触碰到光源,路径计算以后就会是黑色的点,能够经过发射大量的光线,好比计算每一个像素点的时候发射8k~1w条采样光线进行路径计算;也能够路径计算方面作文章,好比加深路径计算递归深度;等等诸如此类。可是上述方法都是暴力解决法,至关耗时,咱们能够运用数学对其进行优化,从而实现画质和效率的双面提高,这就是咱们今天要讲的——直接光源采样!code

 

 Readyhtm

可能您须要如下基础:blog

1.微分递归

2.立体角 (蒙特卡罗(三))图片

没了,剩下全靠想象

 

 content

简明扼要。

咱们朝光源方向发送光线或者生成朝向光源的随机方向都是很容易实现的,可是咱们须要知道的是,pdf(direction)是什么呢?

引用书上一张图:

对于一个光源区域A,若是咱们均匀采样该区域,那么这个pdf就等于1/A,意思就是每一个点的几率均等

可是和咱们的单位球体结合在一块儿的话,就比较麻烦了,见上图

?为何总是提到单位球体呢??

由于咱们的光线和物体表面的交点,会做为下一个eye,而后新的视线方向是表面单位球随机产生的方向,具体见1-5中的diagram7-3

 

 

好了,渊源就是酱紫,咱们继续

若是那个小的微分区域dA的采样几率为

  p_q(q)*dA(采样比例乘以微分区域),也就是dA/A

而对应到单位球体表面的很小的区域,即咱们所述的方位角。方位角微分dΩ对应的采样几率为

  p(direction)*dΩ

这里有一个用来描述dΩ 和 dA 的表达式:

= dA cosα / (distance(p,q^2)

即:方位角微分区域:光源微分区域分红(球心到A中心距离平方)份,取其中的cosα表明的份额数

由于这个dA 和 dΩ的几率是相同的,因此就有以下等式

p(direction) * cosα * dA / (distance(p,q)^2) = p_q(q) * dA = dA / A

因此

p(direction) = distance(p,q)^2 / (cosα * A)

 

咱们接下来就检验一下这个数学公式是否正确

可是代码可能很是丑

咱们须要以前的光源的区域参数

 

    list[cnt++] = new xz_rect(200, 350, 220, 340, 550, light);

 

 

 

因此咱们有如下的代码

 

rtvec lerp(const ray& sight, intersect* world, int depth)
{
    hitInfo info;
    if (world->hit(sight, (rtvar)0.001, rtInf(), info))
    {
        ray scattered;
        rtvec emitted = info._materialp->emitted(info._u, info._v, info._p);
        rtvar pdf;
        rtvec albedo;
        if (depth < 50 && info._materialp->scatter(sight, info, albedo, scattered, pdf))
        {
            rtvec on_light = rtvec(213 + lvgm::rand01() * (343 - 213), 554, 227 + lvgm::rand01() * (332 - 227));
            rtvec to_light = on_light - info._p;
            double distance_squared = to_light.squar();
            to_light.self_unitization();
            if (dot(to_light, info._n) < 0)    
                return emitted;
            double light_area = (343 - 213)*(332 - 227);
            double light_cosine = fabs(to_light.y());
            if (light_cosine < 1e-6)    
                return emitted;
            pdf = distance_squared / (light_cosine*light_area);
            scattered = ray(info._p, to_light, sight.time());
            return emitted + albedo *info._materialp->scatter_pdf(sight, info, scattered)*lerp(scattered, world, depth + 1) / pdf;
        }
        else
            return emitted;
    }
    else
        return rtvec();
}

 

 

 

以下图:

 

由于咱们一路作测试,作图形分析对比,因此咱们上图是sample为250时候的效果

听说,sample为10时,效果依旧很好

因此又超快速运行了一个sample为10的

 

无论怎样,咱们的图形噪点已经作到了比较不错的境地了,sample为10!!!

再看看以前的sample为250的图形效果

简直噪出天际线

 

关于本篇的那个图

天花板上灯光周围的噪声是因为灯光是双面的,灯光和天花板之间有一个狭窄空间。

咱们能够经过将灯光法向量调至垂直向下来解决这一问题,同时让咱们的灯光发射函数也作相应的处理

    virtual rtvec emitted(const ray& rIn, const hitInfo& info, const rtvar u, rtvar v, const rtvec& p)const 
        {
        if(dot(info._n,rIn.direction())<0.)
            return _emit->value(u, v, p); 
        else 
            return rtvec();
        }

 

记得一块儿改了material基类,以及lerp的emit函数调用根据上述参数描述

因此咱们又获得了一个sample为10的新图

没什么大的变化

只是灯光周围的噪点少了,解释:

由于灯光的法向量垂直向下,而咱们的反射光线与反射以后与法向量的夹角为锐角的时候才进行纹理计算

而来自屋顶上面的光线与灯光区域碰撞反射的方向与法向量呈钝角(注意是反射以后的新方向不是入射光方向)则不计算返回黑色,默认光没法到达

 

感谢您的阅读,生活愉快~

相关文章
相关标签/搜索