文档章节

【Ray Tracing The Next Week 超详解】 光线追踪2-8 Volume

o
 osc_feymneeg
发布于 2019/01/25 20:59
字数 1796
阅读 17
收藏 0

行业解决方案、产品招募中!想赚钱就来传!>>>

 

 Preface

今天有两个东东,一个是体积烟雾,一个是封面图

下一篇我们总结项目代码

 

Chapter 8:Volumes

我们需要为我们的光线追踪器添加新的物体——烟、雾,也称为participating media。 我们还需要补充一个材质——次表面散射材质,它有点像物体内的浓雾。

体渲染通常的做法是,在体的内部有很多随机表面,来实现散射的效果。比如一束烟可以表示为,在这束烟的内部任意位置,都可以存在一个面,以此来实现烟、雾

 

对于一个恒定密度体,一条光线通过其中的时候,在烟雾体中传播的时候也会发生散射,光线在烟雾体中能传播多远,也是由烟雾体的密度决定的,密度越高,光线穿透性越差,光线传播的距离也越短。从而实现烟雾的透光性。

引用书中一张图(光线可穿透可散射)

当光线通过体积时,它可能在任何点散射。 光线在任何小距离dL中散射的概率为:

概率= C * dL,其中C与体积的光密度成比例。

对于恒定体积,我们只需要密度C和边界。 

 

/// isotropic.hpp

// -----------------------------------------------------
// [author]        lv
// [begin ]        2019.1
// [brief ]        the isotropic-class for the ray-tracing project
//                from the 《ray tracing the next week》
// -----------------------------------------------------

#pragma once


namespace rt
{

class isotropic :public material
    {
public:
    isotropic(texture* tex) :_albedo(tex) {  }

    virtual bool scatter(const ray& InRay, const hitInfo& info, rtvec& attenuation, ray& scattered)const override
        {
        scattered = ray(info._p, lvgm::random_unit_sphere());
        attenuation = _albedo->value(info._u, info._v, info._p);
        return true;
        }

private:
    texture * _albedo;
    };

} // rt namespace

 

这个材质的散射原理和漫反射磨砂材质的大同小异,均属于碰撞点转换为新视点,沿任意方向发射新的视线,只不过漫反射的视线方向向量指向外相切球体表面,而isotropic的视线方向指向以碰撞点为球心的单位球体表面

区别就在于

漫反射的散射光线不可能指到物体内部,它一定是散射到表面外部(视线方向指向外切球体表面)

isotropic材质的散射光线可以沿原来的方向一往前,以此视线透光性

因为烟雾内部只是颗粒而不存在真正不可穿透的几何实体,所以漫反射实体不可穿透,只能散射到表面外部,而烟雾可穿透

 

接下来我们看一下烟雾体

/// constant_medium.hpp

// -----------------------------------------------------
// [author]        lv
// [begin ]        2019.1
// [brief ]        the constant_dedium-class for the ray-tracing project
//                from the 《ray tracing the next week》
// -----------------------------------------------------


#pragma once

namespace rt
{

class constant_medium :public intersect
    {
public:
    constant_medium(intersect* p, rtvar d, texture* tex);

    virtual bool hit(const ray& sight, rtvar t_min, rtvar t_max, hitInfo& info)const override;

    virtual aabb getbox()const override;

private:
    intersect* _item;

    rtvar _density;    //烟雾密度

    material* _materialp;
    };



inline constant_medium::constant_medium(intersect* p, rtvar d, texture* tex)
    :_item(p)
    ,_density(d)
    ,_materialp(new isotropic(tex))
    {
    }

aabb constant_medium::getbox()const
    {
    return _item->getbox();
    }

bool constant_medium::hit(const ray& sight, rtvar t_min, rtvar t_max, hitInfo& info)const 
    {
    hitInfo info1, info2;
    if (_item->hit(sight, -rtInf(), rtInf(), info1)) {
        if (_item->hit(sight, info1._t + 0.0001, rtInf(), info2)) {
            if (info1._t < t_min)
                info1._t = t_min;
            if (info2._t > t_max)
                info2._t = t_max;
            if (info1._t >= info2._t)
                return false;
            if (info1._t < 0)
                info1._t = 0;
            float distance_inside_boundary = (info2._t - info1._t)*sight.direction().normal();
            float hit_distance = -(1 / _density)*log(lvgm::rand01());
            if (hit_distance < distance_inside_boundary) {
                info._t = info1._t + hit_distance / sight.direction().normal();
                info._p = sight.go(info._t);
                info._n = rtvec(1, 0, 0);  // arbitrary
                info._materialp = _materialp;
                return true;
                }
            }
        }
    return false;
    }

} // rt namespace

 

 

hit函数里面是一些边界合法性检测

 

场景测试代码

intersect* cornell_smoke()
{
    intersect ** list = new intersect*[9];

    int cnt = 0;
    material* red = new lambertian(new constant_texture(rtvec(0.65, 0.05, 0.05)));
    material * blue = new lambertian(new constant_texture(rtvec(0.05, 0.05, 0.73)));
    material* white = new lambertian(new constant_texture(rtvec(0.73, 0.73, 0.73)));
    material* green = new lambertian(new constant_texture(rtvec(0.12, 0.45, 0.15)));
    material* light = new areaLight(new constant_texture(rtvec(7, 7, 7)));

    list[cnt++] = new xz_rect(113, 443, 127, 432, 550, light);
    list[cnt++] = new flip_normal(new xz_rect(113, 443, 127, 432, 550, light));
    list[cnt++] = new flip_normal(new yz_rect(0, 555, 0, 555, 555, green));
    list[cnt++] = new yz_rect(0, 555, 0, 555, 0, red);
    list[cnt++] = new flip_normal(new xz_rect(0, 555, 0, 555, 555, white));
    list[cnt++] = new xz_rect(0, 555, 0, 555, 0, white);
    list[cnt++] = new flip_normal(new xy_rect(0, 555, 0, 555, 555, blue));

    intersect* box1 = new translate(new rotate_y(new box(rtvec(), rtvec(165, 165, 165), white), -18), rtvec(130, 0, 65));
    intersect* box2 = new translate(new rotate_y(new box(rtvec(), rtvec(165, 320, 165), white), 15), rtvec(265, 0, 295));

    list[cnt++] = new constant_medium(box2, 0.006, new constant_texture(rtvec(0.8, 0.58, 0.)));
    list[cnt++] = new constant_medium(box1, 0.008, new constant_texture(rtvec(0.9, 0.2, 0.72)));

    return new intersections(list, cnt);
}

 

下面是效果:sample -> 1000

 

注释 // arbitrary处为随机方向,之前为(1,0,0)

我觉得改为(rand01(),rand01(),rand01())较为合适,下面是改之后的效果

 

Chapter 9:A Scene Testing All New Features

最后的封面图是这样一个场景:

体积雾:有一个蓝色的次表面散射球体,但是这个东西并没有单独实现,而是把它包裹在了一个玻璃球内。

体积雾:整个场景是由层薄薄的雾气笼盖着的

长方体:地面是一堆随机长方体

玻璃球:一个作为蓝色烟雾的容器,一个是单纯的玻璃球

映射纹理:地球纹理球体

过程纹理:柏林噪声纹理球体

运动模糊球体:有一个棕色的运动球体

镜面球体:银白色的镜面球

区域光照:顶部是一个长方形的区域光源

其他未说明材质的都是漫反射

渲染器中剩下的最大缺陷是没有阴影射线,但这就是为什么我们容易获得焦散和次表面散射效果的原因。

 

下面是渲染代码,关于相机参数设置还需等待渲染结果出来才能公布

VS四开,渲染了一天还没完,我也实属无奈

 

intersect* finalScene()
{
    int nb = 20;
    intersect ** list = new intersect*[30];
    intersect ** boxlist = new intersect*[2000];
    intersect ** boxlist2 = new intersect*[2000];
    
    material * white = new lambertian(new constant_texture(rtvec(0.73, 0.73, 0.73)));
    material * ground = new lambertian(new constant_texture(rtvec(0.48, 0.83, 0.53)));

    int b = 0;
    for (int i = 0; i < nb; ++i)
        for (int j = 0; j < nb; ++j)
        {
            rtvar w = 100;
            rtvar x0 = -1000 + i*w;
            rtvar z0 = -1000 + j*w;
            rtvar y0 = 0;
            rtvar x1 = x0 + w;
            rtvar y1 = 100 * (lvgm::rand01() + 0.01);
            rtvar z1 = z0 + w;
            boxlist[b++] = new box(rtvec(x0, y0, z0), rtvec(x1, y1, z1), ground);
        }

    int l = 0;
    list[l++] = new bvh_node(boxlist, b, 0, 1);
    material * light = new areaLight(new constant_texture(rtvec(10, 10, 10)));
    list[l++] = new xz_rect(123, 423, 147, 412, 550, light);
    rtvec center(400, 400, 200);
    list[l++] = new moving_sphere(center, center + rtvec(30, 0, 0), 0, 1, 50, new lambertian(new constant_texture(rtvec(0.7, 0.3, 0.1))));
    list[l++] = new sphere(rtvec(260, 150, 45), 50, new dielectric(1.5));
    list[l++] = new sphere(rtvec(0, 150, 145), 50, new metal(new constant_texture(rtvec(0.8, 0.8, 0.9)), 10.0));
        
    intersect * boundary = new sphere(rtvec(360, 150, 145), 70, new dielectric(1.5));
    list[l++] = boundary;
    list[l++] = new constant_medium(boundary, 0.2, new constant_texture(rtvec(0.2, 0.4, 0.9)));
    boundary = new sphere(rtvec(), 5000, new dielectric(1.5));
    list[l++] = new constant_medium(boundary, 0.0011, new constant_texture(rtvec(1., 1., 1.)));
    
    int x, y, n;
    unsigned char * tex = stbi_load("earthmap.jpg", &x, &y, &n, 0);
    material * emat = new lambertian(new image_texture(tex, x, y));
    list[l++] = new sphere(rtvec(400, 200, 400), 100, emat);
    texture * pertext = new noise_texture(0.1);
    list[l++] = new sphere(rtvec(220, 280, 300), 80, new lambertian(pertext));
    int ns = 1000;
    for (int j = 0; j < ns; ++j)
        boxlist2[j] = new sphere(rtvec(165 * lvgm::rand01(), 165 * lvgm::rand01(), lvgm::rand01()), 10, white);

    list[l++] = new translate(new rotate_y(new bvh_node(boxlist2, ns, 0, 1), 15), rtvec(-100, 270, 395));

    return new intersections(list, l);
}

 

/-----------------------更新线-------------------------------/

对应的相机参数

 

 

效果

其中,薄雾效果太重了,雾气参数应该小一点,大约在1e-4左右较好

vfov可能太大了,45°应该更好一点吧

镜头应该更靠近些

 

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

 

原文出处:https://www.cnblogs.com/lv-anchoret/p/10321623.html

o
粉丝 0
博文 500
码字总数 0
作品 0
私信 提问
加载中
请先登录后再评论。
树莓派(Raspberry Pi):完美的家用服务器

自从树莓派发布后,所有在互联网上的网站为此激动人心的设备提供了很多有趣和具有挑战性的使用方法。虽然这些想法都很棒,但树莓派( RPi )最明显却又是最不吸引人的用处是:创建你的完美家用...

异次元
2013/11/09
5.8K
8
PHP web 服务器--YACS

YACS 是一个强大的 PHP 脚本,可以让你维护一个动态的 Web 服务器。 特性: - Runs on your own server, or on a shared web site - Post articles with web forms, by e-mail, or remotely ......

匿名
2013/03/18
847
0
超快的css selector引擎--kquery 2.0

kquery是一款基于javascript完整实现css3选择器引擎. 兼容所有主流浏览器ie6+/chrome/firefox/opera/safari, 压缩和gzip之后仅8kb大小。智能编译引擎优化去除不必要的逻辑, 查询速度远胜于...

aaron.xiao
2012/10/23
652
0
字符集转换库--libtranscript

libtranscript 是一个字符集转换库,类似 iconv。主要用于内部使用 Unicode、UTF-8, UTF-16 or UTF-32/UCS-4 编码的程序。 libtranscript 当前提供的编码转换包括: Unicode UTF-{7,8,16LE,...

匿名
2012/11/04
991
0
UTF-8字符串生成工具--utfout

utfout是一个命令行工具,可以通过多种多样的方式产生UTF - 8(Unicode)字符串并且直接将它们用于标准输出,标准错误,或直接传递到终端而不需要shell的支持。字符串可以是重复,推迟,随机生成的,...

书一
2012/11/09
715
0

没有更多内容

加载失败,请刷新页面

加载更多

代理服务器和反向代理服务器之间有什么区别? - What's the difference between proxy server and reverse proxy server?

问题: 代理服务器和反向代理服务器有什么区别? 解决方案: 参考一: https://stackoom.com/question/wRc/代理服务器和反向代理服务器之间有什么区别 参考二: https://oldbug.net/q/wRc/W...

技术盛宴
今天
16
0
第八讲:配置外界可以访问虚拟机里面的HDFS

本节通过配置实现外界访问虚拟机Centos6.4里面的HDFS。为后续的java读写HDFS做准备 步骤有: 1、修改主机Windos7的网络配置 2、修改虚拟机Centos6.4里面的网络配置 3、修改虚拟机Centos6.4里...

刘日辉
今天
26
0
OSChina 周四乱弹 —— 不劳而获的饭好吃么?好吃!非常好吃!

Osc乱弹歌单(2020)请戳(这里) 【今日歌曲】 小小编辑推荐:《世界上不存在的歌 (2020重唱版)》- 陈奕迅 《世界上不存在的歌 (2020重唱版)》- 陈奕迅 手机党少年们想听歌,请使劲儿戳(这...

小小编辑
今天
31
3
从 GPU、TPU,到 Web 端、移动端,深度学习框架部署训练开始变简单

本文作者:o****0 早些时候的统计显示,今年3月,深度学习框架集中爆发。5月,有人发布可以直接在 iphone11上训练神经网络的开源项目。日前,百度开源国内首个可直接运行在 Web 端的深度学习...

百度开发者中心
昨天
16
0
如何从Git存储库中删除文件? - How can I delete a file from a Git repository?

问题: I have added a file named "file1.txt" to a Git repository. 我已将名为"file1.txt"的文件添加到Git存储库中。 After that, I committed it, added a couple of directories called......

富含淀粉
今天
31
0

没有更多内容

加载失败,请刷新页面

加载更多

返回顶部
顶部