Ray_Tracing_The_Rest_of_Your_Life上

这一部是优化之前的渲染,我们之前的渲染有很多噪声,我们只有提高采样点数量才行,但是这会降低性能,那么如何才能在性能和画面做权衡?

1·蒙特卡洛Monte Carlo, 二维MC积分

两种随机算法:

Monte Carlo算法可能给出近似解(有偏的,但可以收敛到正确解),算法的运行时间通常是固定的,会以概率形式给出近似值

而Las Vegas算法总是提供精确解,算法的运行时间是不确定的,取决于找到正确答案所需的时间,比如代码中的random_in_unit_sphere()函数

收敛速度:花费越少的样本,更快求得近似解

例子:用蒙特卡洛方法估计PI:

我们知道圆形和方形的面积比,当r==1时== π / 4,πr^2 / (2r)^2 = π / 4,面积比就是概率比

π == 4 * (圆形和方形的概率比),总样本为N个(落在方形的概率),落在单位球面的样本为inside_circle == if (x*x + y*y < 1)个

int main() {
    std::cout << std::fixed << std::setprecision(12);

    int inside_circle = 0;
    int N = 100000;

    for (int i = 0; i < N; i++) {
        auto x = random_double(-1,1);
        auto y = random_double(-1,1);
        if (x*x + y*y < 1)
            inside_circle++;
    }

    std::cout << "Estimate of Pi = " << (4.0 * inside_circle) / N << '\n';
}

显示收敛效果:

我们可以改为一直运行,当运行到100000的倍速次打印PI值,这样我们可以观测收敛结果,可以发现,当样本总数越大,结果越准确

int inside_circle = 0;
    int runs = 0;
    while (true) {
        runs++;
        auto x = random_double(-1,1);
        auto y = random_double(-1,1);
        if (x*x + y*y < 1)
            inside_circle++;

        if (runs % 100000 == 0)
            std::cout << "\rEstimate of Pi = " << (4.0 * inside_circle) / runs;

分层采样:

我们把正方形分成100万个网格,在每个像素内生成随机采样点,

((i + random_double()) / sqrt_N),首先在每个像素生成随机位置,通过 / sqrt_N变换到0--1的正方形内

通过*2-1变换到-1---1的单位立方体内,这样的结果更准确

int inside_circle = 0;
int inside_circle_stratified = 0;
int sqrt_N = 1000;

for (int i = 0; i < sqrt_N; i++) {
    for (int j = 0; j < sqrt_N; j++) {
        x = 2*((i + random_double()) / sqrt_N) - 1;
        y = 2*((j + random_double()) / sqrt_N) - 1;
        if (x*x + y*y < 1)
            inside_circle_stratified++;
    }
}

std::cout
    << "Stratified Estimate of Pi = "
    << (4.0 * inside_circle_stratified) / (sqrt_N*sqrt_N) << '\n';

好了,让我们的追踪器使用蒙特卡洛吧!

我们不再使用for (int sample = 0; sample < samples_per_pixel; sample++) 在像素内随机采样

使用,其中sqrt_spp是原来samples_per_pixel像素采样次数的平方根(嵌套后还是原来的采样次数),recip_sqrt_spp = 1.0 / sqrt_spp;

for (int s_j = 0; s_j < sqrt_spp; s_j++)
{
    for (int s_i = 0; s_i < sqrt_spp; s_i++)
    {
        ray r = get_ray(i, j, s_i, s_j);
        pixel_color += ray_color(r, max_depth, world);
    }
}

然后用蒙特卡洛方式随机采样,首先通过* recip_sqrt_spp变换到0--1的范围内,然后- 0.5变换到-0.5---0.5的范围

vec3 sample_square_stratified(int s_i, int s_j) const
{
    auto px = ((s_i + random_double()) * recip_sqrt_spp) - 0.5;
    auto py = ((s_j + random_double()) * recip_sqrt_spp) - 0.5;
    return vec3(px, py, 0);
}

原有的:

 

蒙特卡洛的: 

 

低差异序列:(比如Hammersley序列)

样本分布更均匀:具有更快的收敛速度

2·一维MC

 

蒙特卡洛积分:是一种求解积分的方式,采用离散的思想,方便了那些无法解的积分

从a到b区间的积分(dx是积分的标准写法),fx被积函数

N是随机采样的样本数,1/N是权重,fxk为随机点的高,pxk为随机点的概率(PDF(Probability Density Function,概率密度函数))

用蒙特卡洛解积分:例如被积函数x^2(在0--2之间)

相当于在区间的面积 I,或者2 * 平均值(MC方法采样求平均值)

int main() 
{
    int N = 1000000;
    auto sum = 0.0;
    for (int i = 0; i < N; i++) 
    {
        auto x = random_double(0,2);
        sum += x*x;
    }
    std::cout << std::fixed << std::setprecision(12);
    std::cout << "I = " << 2.0 * (sum/(float)N) << '\n'; 
}

让我们回到追踪器,我们之前的采样方式是均匀 的,每个值的概率都是相同的,这次我们重要性采样,以便加快收敛速度

概率密度函数PDF:

3·球面上的蒙特卡洛

4·光散射

5·重要性采样

6·随机方向

7·标准正交基

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值