【RAY TRACING THE REST OF YOUR LIFE 超详解】 光线追踪 3-7 混合几率密度

 

 Prefacehtml

注:鉴于不少网站随意爬取数据,可能致使内容残缺以及引用失效等问题,影响阅读,请认准原创网址:app

https://www.cnblogs.com/lv-anchoret/category/1368696.html框架

 

咱们这节主要讲把以前的几率密度作混合,以获得更好的效果dom

咱们上一篇之前常常用关于cos函数的pdf,上一节用的是与光源采样相关的pdf,那么,咱们把二者结合到一块儿,协调它们之间的比例,咱们就能够获得一个有着两种几率密度模型的pdf,这每每是更贴近生活的,那么咱们今天就来学习测试一下。函数

 

 Ready学习

这一节就是把前几篇的几率密度作混合,因此,须要的就是熟悉以前的内容。测试

固然,以前的框架代码也比较丑,基本都是在lerp函数里面作调整,因此,咱们顺便把框架搭得更好一点网站

 

 正文ui

咱们都知道,设计pdf的一个很重要的原则就是使得累积几率密度达到且只达到1,因此,咱们先采用一种很是简单的比例协调方式混合两个pdf。this

例如咱们有以下的混合密度方程

pdf_mixture(direction) = 1/2 * pdf_reflection(direction) + 1/2 * pdf_light(direction)

即,二者各占一半

要实现二者,代码描述也很简单:

if ( rand01() < 0.5 )
    pdf_reflection();  ...
else
    pdf_light();  ...
可是评估pdf_mixture会稍微有点微妙。 咱们须要同时评估pdf_reflection和pdf_light,由于有一些方向能够生成pdf方向。 例如,咱们可使用pdf_reflection生成朝向光的方向
 
若是咱们回顾以前的内容,你会发现,这一部分主要解决两个问题:
1.此处的pdf函数值
2.按照某个随机模型产生一个随机数
 
咱们抽象出这些操做以后,就能够写一个关于咱们的pdf的一个基类:
///pdf.hpp

// -----------------------------------------------------
// [author]        lv
// [ time ]        2019.3
// [brief ]        In the Monte Carlo system, pdf acts as the
//                most important element of Important-Sample
// -----------------------------------------------------


#pragma once


namespace rt
{

// the basic class of pdf system
class pdf
    {
public:
    /*
    @brief: we get the value of pdf function by this interface
    @param: the direction of location
    @retur: the value of the pdf function
    */
    virtual rtvar value(const rtvec & direction)const = 0;

    /*
    @brief: generate a random number with a Probability model
    @param: none
    @retur: the Three-dimensional random vector
    */
    virtual rtvec generate()const = 0;
    };


}//rt namespace

 

咱们来实现关于它的一些子类

首先咱们来实现关于cosine 几率密度的模型

///cosine_pdf.hpp

// -----------------------------------------------------
// [author]        lv
// [ time ]        2019.3
// [brief ]        one of the pdf' forms
// -----------------------------------------------------


#pragma once


namespace rt
{

class cosine_pdf :public pdf
    {
public:
    //constructor
    cosine_pdf(const rtvec& w);            

    /*
    @brief: we get the value of pdf function by this interface
    @param:    the direction of location
    @retur: the value of the pdf function
    */
    virtual rtvar value(const rtvec& direction)const;


    /*
    @brief: generate a random number with a Probability model
    @param: none
    @retur:    the Three-dimensional random vector
    */
    virtual rtvec generate()const;

private:

    onb _uvw;
    };

inline cosine_pdf::cosine_pdf(const rtvec& w)
    {
    _uvw.build_from_w(w);
    }

rtvar cosine_pdf::value(const rtvec& direction)const
    {
    rtvar cosine = dot(direction.ret_unitization(), _uvw.w());
    if (cosine > 0.)
        return cosine / π;
    else
        return 0.;
    }

rtvec cosine_pdf::generate()const
    {
    return _uvw.local(random_cosine_direction());
    }
}

这个模型以前细说过,cosine大于0的时候返回cosine/π,反之,则返回0。由于光线反射以后若是和表面法线的夹角为钝角的时候,违反反射规律,不以反射。生成随机数的那个以前也讲过,在上上一篇

 

其实这些都不是新东西,就是把以前讲的的那一套整合了一下

获得结果也就是以前的效果

咱们把主函数里面的lerp()也改一下

 

每一个像素点采样100次,取均值,即sample 为 100时

 

 

 这是代码敲错了,意外获得的一张图

 

 如今咱们尝试,光源采样,即

///hit_pdf.hpp

// -----------------------------------------------------
// [author]        lv
// [ time ]        2019.3
// [brief ]        toward to the hitable
// -----------------------------------------------------

#pragma once


namespace rt
{

class hit_pdf :public pdf
    {
public:

    /*
    @param: info -> Geometry information
            origion -> the point of intersection
    */
    hit_pdf(intersect* info, const rtvec& origion)
        :_intersectp(info)
        ,_o(origion)
    {
    }


    /*
    @brief: we get the value of pdf function by this interface
    @param:    the direction of location
    @retur: the value of the pdf function
    */
    virtual rtvar value(const rtvec& direction)const
        {
        return _intersectp->pdf_value(_o, direction);
        }
    

    /*
    @brief: generate a random number with a Probability model
    @param: none
    @retur:    the Three-dimensional random vector
    */
    virtual rtvec generate()const
        {
        return _intersectp->random(_o);
        }

private:
        
    rtvec _o;

    intersect * _intersectp;
    };

}// rt namespace

 

对应的intersect类也要改一下

/// intersect.hpp
//https://www.cnblogs.com/lv-anchoret/p/10190092.html
// -----------------------------------------------------
// [author]        lv
// [begin ]        2018.12
// [refre ]        2019.3
// [brief ]        the intersect-class for the ray-tracing project
//                from the 《ray tracing in one week》
// -----------------------------------------------------

#pragma once

#include "E:\OpenGL\光线追踪\code\ray tracing 1-3\ray tracing 1-3\ray.hpp"


namespace rt
{
class material;
class aabb;


// the infomation of intersection point

struct hitInfo
    {
    lvgm::precision _t;        //ray 中的系数t
    rtvec _p;                //相交点、撞击点
    rtvec _n;                //_p点的表面法线
    material* _materialp;    //材质
    rtvar _u;                //texture-u
    rtvar _v;                //texture-v
    };


// the statement of intersect class

class intersect
    {
public:

    /*
    @brief: 撞击函数,求取撞击点相关记录信息
    @param: sight->视线
    系数t的上下界->筛选撞击点
    info->返回撞击点信息
    @retur: 是否存在合法撞击点
    */
    virtual bool hit(const ray& sight, rtvar t_min, rtvar t_max, hitInfo& info)const = 0;

    /*
    @brief: get the box of Geometry
    */
    virtual aabb getbox()const = 0;

    /*
    Get the value of pdf function
    */
    virtual rtvar pdf_value(const rtvec& o, const rtvec& v)const
        {
        return 0.;
        }

    /*
    generate the random number
    */
    virtual rtvec random(const rtvec& o)const
        {
        return rtvec(1, 0, 0);
        }


    };

}// rt namespace

 

由于咱们如今只是拿区域光源作实验,并非全部的几何体派生类都要继承pdf相关的方法,因此,它们两个以虚函数的形式存在便可。

那么就剩下xz长方形了

rtvar xz_rect::pdf_value(const rtvec& o, const rtvec& v)const
    {
    hitInfo rec;
    if (this->hit(ray(o, v), 1e-3, rt::rtInf(), rec))
        {
        rtvar area = (_x2 - _x1)*(_z2 - _z1);
        rtvar distance_squared = rec._t * rec._t * v.squar();
        rtvar cosine = fabs(dot(v, rec._n) / v.normal());
        return distance_squared / (cosine*area);
        }
    else
        return 0.;
    }

rtvec xz_rect::random(const rtvec& o)const
    {
    rtvec random_point = rtvec(_x1 + lvgm::rand01() * (_x2 - _x1), _other, _z1 + lvgm::rand01()*(_z2 - _z1));
    return random_point - o;
    }

 

 把上一篇写在lerp函数里面的一大堆东西整合到类里面

那么咱们的lerp就统一化了:

咱们取sample为10,便可获得很好的效果:

 

如今咱们将写一个关于混合几率密度的类:

///mixture_pdf.hpp

// -----------------------------------------------------
// [author]        lv
// [ time ]        2019.3
// [brief ]        mixture pdfs
// -----------------------------------------------------


#pragma once


namespace rt
{

class mixture_pdf :public pdf
    {
public:

    mixture_pdf(pdf * p1, pdf* p2)
        {
        _p[0] = p1;
        _p[1] = p2;
        }


    /*
    @brief: we get the value of pdf function by this interface
    @param:    the direction of location
    @retur: the value of the pdf function
    */
    virtual rtvar value(const rtvec& direction)const
        {
        return 0.5*_p[0]->value(direction) + 0.5*_p[1]->value(direction);
        }


    /*
    @brief: generate a random number with a Probability model
    @param: none
    @retur:    the Three-dimensional random vector
    */
    virtual rtvec generate()const
        {
        if (lvgm::rand01() < 0.5)
            return _p[0]->generate();
        else
            return _p[1]->generate();
        }

private:

    pdf* _p[2];

    };


}// rt namespace

 

咱们的lerp函数以下:

 

咱们采样10次获得:

可是以为效果不是很理想,咱们来作一些测试

 

1. pdf 方程修改成 mixture_pdf = 1/3 * hit_pdf + 2/3  * cosine_pdf

 

2. pdf 方程修改成 mixture_pdf = 2/3 * hit_pdf + 1/3  * cosine_pdf

 

3. random修改  2/3 取 hit_pdf产生的随机值, 1/3 取 cosine_pdf 产生的随机值

 

4. random修改  1/3 取 hit_pdf产生的随机值, 2/3 取 cosine_pdf 产生的随机值

 

咱们去上述方案的三、1,即:

 

获得图:

这张图显然比均分的效果要好

 

这里咱们看不出究竟是random起做用仍是value,咱们不妨取二、3组合

3把2的彩色噪声消除了些,可是这张图和原始的均分图差很少同样

 

因此结论,random和value的比例交叉比较好

 

咱们采样1000次获得:

 渲染中。。。。(就是清晰了点)

/***********************************************************************************/

跑了一夜爬起来发现除零错误了,又抽空跑完了

/************************************************************************************/

 

本书第九章(下一章)介绍了一些关于当前渲染器的见解

做者在描述阴影光线和混合密度设计时,做者我的更偏向于混合密度设计,因此并无在渲染器中采用阴影光线

做者描述了关于lerp函数中内存问题以及编码的不足

做者描述了关于玻璃材质和镜面的一些处理方法

做者还描述了关于HDR的0~1浮点表示以及RGB分组的0~255表示,还说明了这个渲染器是RGB的且基于物理的,还有一种是基于光谱的,以及二者结合的,但作起来很难,因此咱们坚持RGB且基于物理的渲染器。

 

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

相关文章
相关标签/搜索