【Ray Tracing The Next Week 超详解】 光线追踪2-7 任意长方体 && 场景案例
上一篇比较简单,很久才发是因为做了一些好玩的场景,后来发现这一章是专门写场景例子的,所以就安排到了这一篇
Preface
这一篇要介绍的内容有:
1. 自己做的光照例子
2. Cornell box画质问题及优化方案
3. 新的场景几何体——长方体
轴平行长方体
任意长方体
我们这一篇重实践轻理论阐述
ready
1. 需要上一章的知识
但是,上一章的Cornell box画质优化仅限于盒子本身,如果作为场景和其他物体放在一起就不能那么优化画质
即,Cornell box像素计算失败应该返回黑色点而非白色
2. 需要图形学基本仿射变换知识
3. 玻璃球镂空技术,如有忘记,请移步此处
先看效果
光照案例
图7-1
Cornell box案例(最初步)
图7-2
最终版
任意轴旋转
正文
学了光照就迫不及待地整了一堆东西
终于脱开了蓝色插值背景转到正儿八经的光了
在还没学长方形之前,就先用球体做了光源
注:坐标轴按照光线追踪坐标系描述(y轴位于垂直向上方向,z轴垂直屏幕向外)
1. 图7-1 第二行左
该图是最开始的一张图,相机仍然在(13,3,2),第一卦限
而球体是一个半径为1的漫反射白球,置于原点处
下面仍然是一个大的镜面球(metal),y轴-1000处,半径999,正好和小球相切
红色灯光则置于第三卦限,例如:(-3,3,-3)
整个场景的背景为黑色,即光线路径计算失败后返回黑色
如上,则会看到球体表面有一抹红色的色泽,然后大球镜面反射也有一部分
图7-3
2. 图7-1 第二行中
在上图的基础上添加一个位于第四卦限的蓝色光源
就会形成漫反射球左侧为蓝色表面光泽右侧为红色表面光泽的效果
3. 图7-1 第一行左
上面两个当然很没意思了,但是一直以来都是蓝色背景亮堂堂的,第一次黑不溜秋的地方用灯照着东西,感觉挺真实的,光线追踪效果也很不错,所以上面两张图是新世纪的开端!
我们在(0,0,2)处,放一个半径为1的镜面球,在原点对称处放一个半径为1的玻璃球
下面的大球改为漫反射
哇,想想就刺激,结果不出所料
镜面球在黑乎乎的环境下只映出了蓝色光源和红色光源,以及旁边的漫反射球的相关部分,而玻璃球就更有意思了,透了红光照在漫反射大球表面上,还透了微弱的蓝光,也照在了右侧的地面上
4. 图7-1 第一行中
突然想到一个绝妙的主意,玻璃球可以镂空
于是设置了一个镂空球(0,0,-2),半径为-0.8
之后,想着把镜面球和磨砂小球离远一点,再观察磨砂小球在镜面球中的影,于是乎就成了上面这张图
镜面球依旧映这磨砂小球和灯光的影,然而玻璃球只有上面一丝丝的明亮,着实看着不尽人意
可能是镂空的太多了,于是有了右边那张图
5. 图7-1 第一行右
把镂空球半径设置小一点,想了想就-0.2吧
果然,不负吾望,还真是着实好看,不仅可以往地上透光,形状更有意思,像个立体环!!
其实,我是想调一个把左边两个特点合二为一的图,即既有第一张图的底面透光,又有第二图上表面那个明亮的高光
6. 图7-1 第二行右
其实是为了凑齐6张图,思来想去,没啥整的了,老是调个镂空半径没啥意思,渲染时间还长,后来想了下,不如把大球改成原来的镜面,这样下面三张图都是镜子大地,上面三张都是磨砂大地
于是乎,emmm,貌似还完成了上面提到的梦想,上表面”高光”以及底面的透光,不仅如此,而且镂空内表面还有透光,还映在了大地上,强无敌嘞~
上述场景代码
intersect* light()
{
texture * perlintex = new noise_texture(6.3);
material* redlight = new areaLight(new constant_texture(rtvec(0.98, 0.1, 0.08)));
material* bluelight = new areaLight(new constant_texture(rtvec(0.05, 0.05, .))); intersect**list = new intersect*[];
list[] = new sphere(rtvec(-, , -), 1.5, redlight);
list[] = new sphere(rtvec(-2.2, 3.2, 2.8), 1.5, bluelight);
list[] = new sphere(rtvec(, , 2.2), , new metal(new constant_texture(rtvec(, , ))));
list[] = new sphere(rtvec(), , new lambertian(new constant_texture(rtvec(, , ))));
list[] = new sphere(rtvec(, , -), , new dielectric(1.5));
list[] = new sphere(rtvec(, , -), -0.18, new dielectric(1.5));
list[] = new sphere(rtvec(, -, ), ,
new dielectric(1.5));
return new intersections(list, );
}
Chapter 7:Instance
我们来进行正常的章节学习,emmmm
现在先来学习轴平行的长方体,这个东西呢我知道的目前有两种方法
第一种是球坐标系下多个方位角和长宽高参数确定的长方体
此图引用于https://blog.csdn.net/libing_zeng/article/details/54561605
如果想要学习的话可以去学习一下这种方法
第二种方法自然就是顶点确定形体,左下-右上顶点确定形体,不仅适用于2D的形,同样适用于3D的体
心里罗列一下我们现有的零件,是否能够整一个长方体出来,好像可以
我们已经弄好了长方形,那么就可以粘成长方体
不错,只要把各个长方形的法线指向外部即可,而第一种方法也要求取每个面的法线
所以,我们这种方法还是比较好的,毕竟我们就是用6个法线构建的,第一种还需要方位角转换运算求取
那么我们就写成了如下代码
/// box.hpp // -----------------------------------------------------
// [author] lv
// [begin ] 2019.1
// [brief ] the box-class for the ray-tracing project
// from the 《ray tracing the next week》
// ----------------------------------------------------- #pragma once namespace rt
{ // the statement of box class class box: public intersect
{
public:
box() { } box(const rtvec& pointmin, const rtvec& pointmax, material * mat); virtual bool hit(const ray& sight, rtvar t_min, rtvar t_max, hitInfo& info)const override; virtual aabb getbox()const override; private:
rtvec _min; rtvec _max; intersect* _list;
}; // the implementation of box class inline box::box(const rtvec& pointmin, const rtvec& pointmax, material * mat)
:_min(pointmin)
,_max(pointmax)
{
intersect ** list = new intersect*[];
list[] = new xy_rect(_min.x(), _max.x(), _min.y(), _max.y(), _max.z(), mat);
list[] = new flip_normal(new xy_rect(_min.x(), _max.x(), _min.y(), _max.y(), _min.z(), mat));
list[] = new xz_rect(_min.x(), _max.x(), _min.z(), _max.z(), _max.y(), mat);
list[] = new flip_normal(new xz_rect(_min.x(), _max.x(), _min.z(), _max.z(), _min.y(), mat));
list[] = new yz_rect(_min.y(), _max.y(), _min.z(), _max.z(), _max.x(), mat);
list[] = new flip_normal(new yz_rect(_min.y(), _max.y(), _min.z(), _max.z(), _min.x(), mat));
_list = new intersections(list, );
} bool box::hit(const ray& sight, rtvar t_min, rtvar t_max, hitInfo& info)const
{
return _list->hit(sight, t_min, t_max, info);
} aabb box::getbox()const
{
return aabb(_min, _max);
} } // rt namespace
根据最小点坐标和最大点坐标构建六个面
于是我们来做开篇第二张图
在上一篇文章的Cornell box的场景中添加上面两个box
如果你的代码中,上一篇的仍然是背景为白色(即光线路径计算失败后返回白色)
那么将是下面这个
图7-4
面向我们的两个物体面是光线追踪几乎计算不到的地方,所以基本是纯白色
我们迫不得已再把背景改为黑色
如第34行所示
但是我们一想到上一篇的一堆黑点噪声就。。。真是把一张美图糟蹋了
图7-5
如何优化呢?
思来想去,有下列几种方法
1. 把区域光面积调大
2. 把光源与顶部距离调大,因为房间的每一面墙壁都是边长为555的正方形,敢问,距离为一个像素的光如何把偌大的平面照亮,于是乎,我改成了距离5....
3. 相机距离房间门口800像素,我们调为700像素
则修改后的图像为:
图7-6
还有一个最重要的改进方式,增加采样点,即增加光线条数
可以对比,sample为10的时候(之前是sample为100)
图7-7
从《Ray Tracing From the Ground Up》中得知,最简单粗暴的方法是发出万条光线做路径计算可以得到我们想要的图片
于是我将sample改为了2w,跑了一夜,现在是这样的
图7-8
可以看出来是相当清晰了
书中还提到了,对光线路径和光源本身同时进行采样计算的直接光照和间接光照结合方法优化画质,比上述的暴力法效率更好
但是目前不会对光源进行采样计算以及间接光照相关技术,所以不能为大家提供代码和效果
好了,我们继续章节学习——旋转和平移
我们知道,平移比较简单,但是在光线追踪中如何实现物体平移呢?
它并没有顶点集合,它只有一个几何体方程以及碰撞检测,怎么平移呢
对了,就是碰撞检测这里!
我们对每一个碰撞点进行变换计算,也就把整个理想化的物体实例化且做了变换
1. 平移
对于平移,我们可以对每个碰撞点进行移动也可以在计算碰撞点的时候把eye往反方向移动,进而,求取碰撞点,也可以实现平移
我们采取第二种
/// translate.hpp // -----------------------------------------------------
// [author] lv
// [begin ] 2019.1
// [brief ] the translate-class for the ray-tracing project
// from the 《ray tracing the next week》
// ----------------------------------------------------- #pragma once namespace rt
{ class translate :public intersect
{
public:
translate(intersect* p, const rtvec& offset); virtual bool hit(const ray& sight, rtvar t_min, rtvar t_max, hitInfo& info)const override; virtual aabb getbox()const override; private:
intersect* _item; rtvec _offset; }; translate::translate(intersect* p, const rtvec& offset)
:_item(p)
, _offset(offset)
{
} bool translate::hit(const ray& sight, rtvar t_min, rtvar t_max, hitInfo& info)const
{
ray movedRay(sight.origin() - _offset, sight.direction(), sight.time());
if (_item->hit(movedRay, t_min, t_max, info))
{
info._p += _offset;
return true;
}
return false;
} aabb translate::getbox()const
{
aabb box = _item->getbox();
return aabb(box.min() + _offset, box.max() + _offset);
} }// rt namespace
这个比较简单
2. 旋转
我们来复习一下图形学中仿射变换的知识
关于旋转:(引用书上一张图)
则
x' = cosθ * x - sinθ * y
y' = sinθ * x + cosθ * y
那么写成惯用的矩阵形式(采用列向量表示法),则是(绕z轴转)
同理,绕y轴转:
绕x轴转:
那么,我们来写绕y轴转的类
/// rotate.hpp // -----------------------------------------------------
// [author] lv
// [begin ] 2019.1
// [brief ] the rotate-class for the ray-tracing project
// from the 《ray tracing the next week》
// ----------------------------------------------------- #pragma once namespace rt
{ // the statement of rotate class class rotate :public intersect
{
public:
rotate(intersect* p, rtvar angle); 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 _sinθ; rtvar _cosθ; aabb _box; }; // the implementation of rotate class rotate::rotate(intersect* p, rtvar angle)
:_item(p)
{
rtvar radians = (π / .) * angle;
_sinθ = sin(radians);
_cosθ = cos(radians);
rtvec min(rtInf(), rtInf(), rtInf());
rtvec max = -min;
for (int i = ; i < ; ++i)
for (int j = ; j < ; ++j)
for (int k = ; k < ; ++k)
{
rtvar x = i * _box.max().x() + ( - i)*_box.min().x();
rtvar y = j * _box.max().y() + ( - j)*_box.min().y();
rtvar z = k * _box.max().z() + ( - k)*_box.min().z();
rtvar newx = _cosθ * x + _sinθ * z;
rtvar newz = -_sinθ * x + _cosθ * z;
rtvec tester(newx, y, newz);
for (int c = ; c < ; ++c)
{
if (tester[c] > max[c])
max[c] = tester[c];
if (tester[c] < min[c])
min[c] = tester[c];
}
}
_box = aabb(min, max);
} bool rotate::hit(const ray& sight, rtvar t_min, rtvar t_max, hitInfo& info)const
{
rtvec eye = sight.origin();
rtvec direction = sight.direction();
eye[] = _cosθ * sight.origin()[] - _sinθ * sight.origin()[];
eye[] = _sinθ * sight.origin()[] + _cosθ * sight.origin()[];
direction[] = _cosθ * sight.direction()[] - _sinθ * sight.direction()[];
direction[] = _sinθ * sight.direction()[] + _cosθ * sight.direction()[];
ray rotatedRay(eye, direction, sight.time());
if (_item->hit(rotatedRay, t_min, t_max, info))
{
rtvec p = info._p;
rtvec n = info._n;
p[] = _cosθ * info._p[] + _sinθ * info._p[];
p[] = -_sinθ * info._p[] + _cosθ * info._p[];
n[] = _cosθ * info._n[] + _sinθ * info._n[];
n[] = -_sinθ * info._n[] + _cosθ * info._n[];
info._p = p;
info._n = n;
return true;
}
return false;
} aabb rotate::getbox()const
{
return _box;
} } // rt namespace
我们来写图7-7的场景
intersect* Cornell()
{
intersect ** list = new intersect*[];
size_t cnt = ;
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.88, 0.88, 0.88)));
material * green = new lambertian(new constant_texture(rtvec(0.12, 0.45, 0.15)));
material * light = new areaLight(new constant_texture(rtvec(, , ))); list[cnt++] = new flip_normal(new yz_rect(, , , , , green));
list[cnt++] = new yz_rect(, , , , , red);
list[cnt++] = new xz_rect(, , , , , light);
list[cnt++] = new flip_normal(new xz_rect(, , , , , light));
list[cnt++] = new flip_normal(new xz_rect(, , , , , white));
list[cnt++] = new xz_rect(, , , , , white);
list[cnt++] = new flip_normal(new xy_rect(, , , , , blue)); list[cnt++] = new translate(new rotate(new box(rtvec(), rtvec(, , ), white), -), rtvec(, , ));
list[cnt++] = new translate(new rotate(new box(rtvec(), rtvec(, , ), white), ), rtvec(, , )); return new intersections(list, cnt);
}
图7-8是图7-7的高清版,暂时还没跑完,渲染完之后我在此处放上此场景的高清版,以及任意轴旋转的扩充代码
敬请期待。。。
**************************** 更新线 ******************************************
图7-8已经更新,程序终于跑完了
一张图片分了四部分一起跑还跑了两天,心累。。
感觉计算机也累,心疼1s
关于任意轴旋转
上述说明了y轴旋转的代码
旋转类中有三处需要做变换,向量运算也好,矩阵运算也罢
第一处是构造函数中的newx和newz(对称轴剩余两个分量)
第二处是hit函数中的eye运算和direction运算
第三处是hit函数中的p和n向量的运算
其中第一处和第三处用的是变换矩阵的原形(你可以先把上面的三个轴对应变换表达式转化为矩阵形式,如第一个的z轴旋转公式)
而第二处用的是对应变换矩阵的转置
如此续写其他两个轴的旋转类即可达成效果
当然你也可以写个场景测试一下
至于空间任意变换,无疑就是轴旋转和平移搭配结合所形成的效果
可以先在原点处构建物体,经多个轴旋转而后平移到目标位置以代替空间物体沿任意轴旋转的效果
场景测试代码
我们来对比一下各种采样数的效果对比
sample为100
sample为500
sample为1000(该场景为下一章的体积烟雾)
sample为20000
感谢您的阅读,生活愉快~
【Ray Tracing The Next Week 超详解】 光线追踪2-7 任意长方体 && 场景案例的更多相关文章
- 【Ray Tracing The Next Week 超详解】 光线追踪2-9
我们来整理一下项目的代码 目录 ----include --hit --texture --material ----RTdef.hpp ----ray.hpp ----camera.hpp ---- ...
- 【Ray Tracing The Next Week 超详解】 光线追踪2-6 Cornell box
Chapter 6:Rectangles and Lights 今天,我们来学习长方形区域光照 先看效果 light 首先我们需要设计一个发光的材质 /// light.hpp // ------- ...
- 【Ray Tracing in One Weekend 超详解】 光线追踪1-4
我们上一篇写了Chapter5 的第一个部分表面法线,那么我们来学剩下的部分,以及Chapter6. Chapter5:Surface normals and multiple objects. 我们 ...
- 【Ray Tracing The Next Week 超详解】 光线追踪2-8 Volume
Preface 今天有两个东东,一个是体积烟雾,一个是封面图 下一篇我们总结项目代码 Chapter 8:Volumes 我们需要为我们的光线追踪器添加新的物体——烟.雾,也称为participat ...
- 【Ray Tracing The Next Week 超详解】 光线追踪2-5
Chapter 5:Image Texture Mapping 先看效果: 我们之前的纹理是利用的是撞击点p处的位置信息,比如大理石纹理 而我们今天的图片映射纹理采用2D(u,v)纹理坐标来进行. 在 ...
- 【Ray Tracing in One Weekend 超详解】 光线追踪1-8 自定义相机设计
今天,我们来学习如何设计自定义位置的相机 ready 我们只需要了解我们之前的坐标体系,或者说是相机位置 先看效果 Chapter10:Positionable camera 这一章我们直接用概念 ...
- 【Ray Tracing The Next Week 超详解】 光线追踪2-4 Perlin noise
Preface 为了得到更好的纹理,很多人采用各种形式的柏林噪声(该命名来自于发明人 Ken Perlin) 柏林噪声是一种比较模糊的白噪声的东西:(引用书中一张图) 柏林噪声是用来生成一些看似杂乱 ...
- 【Ray Tracing The Next Week 超详解】 光线追踪2-3
Preface 终于到了激动人心的纹理章节了 然鹅,看了下,并不激动 因为我们之前就接触过 当初有一个 attenuation 吗? 对了,这就是我们的rgb分量过滤器,我们画出的红色.蓝色.绿色等 ...
- 【Ray Tracing The Next Week 超详解】 光线追踪2-2
Chapter 2:Bounding Volume Hierarchies 今天我们来讲层次包围盒,乍一看比较难,篇幅也多,但是咱们一步一步来,相信大家应该都能听懂 BVH 和 Perlin text ...
随机推荐
- B. Light It Up
题目链接:http://codeforces.com/problemset/problem/1000/B 代码: #include<iostream> #include<cstrin ...
- java读取视频文件时长
1.下载jar包:http://www.sauronsoftware.it/projects/jave/index.php 2.上代码 @RequestMapping(value = "am ...
- linux matlab2016 安装
1. 下载Matlab 2016b 下载文件夹中包含三个文件:Matlab 2016b Linux64 Crack.rar,R2016b_glnxa64_dvd1.iso,R2016b_glnxa64 ...
- python3之Django多数据库
1.定义数据库 在django项目中, 一个工程中存在多个APP应用很常见:有时候希望不同的APP连接不同的数据库,这个时候需要建立多个数据库连接.在Django的setting中使用DATABASE ...
- Protobuf使用手册
Protobuf使用手册 第1章 定义.proto 文件 首先我们需要编写一个 proto 文件,定义我们程序中需要处理的结构化数据,在 protobuf 的术语中,结构化数据被称为 Message. ...
- freeRTOS中文实用教程3--中断管理之中断嵌套
1.前言 最新的 FreeRTOS 移植中允许中断嵌套.中断嵌套需要在 FreeRTOSConfig.h 中设置configKERNEL_INTERRUPT_PRIORITY 和configMAX_S ...
- Raw Socket vs Stream Socket vs datagram socket,原始套接字与流式套接字与数据报套接字
https://opensourceforu.com/2015/03/a-guide-to-using-raw-sockets/ In this tutorial, lets take a look ...
- saltstack自动化运维系列⑦SaltStack实践配置管理安装zabbix
saltstack自动化运维系列⑥SaltStack实践配置管理安装zabbix 1.添加管理zabbix的sls文件# vim /srv/salt/base/init/zabbix_agent.sl ...
- OneNET麒麟座应用开发之三:获取温湿度数据
对于大气环境监测来说温湿度也是重要的指标.原本计划使用SHT15来采集温湿度数据,不过在OneNET麒麟开发板上,我们发现已经集成有SHT20温湿度传感器,于是我们就使用它了.如下图的红框处: 我们还 ...
- SpringMVC(4.1):Controller接口控制器详解(1)
原文出处: 张开涛 4.1.Controller简介 Controller控制器,是MVC中的部分C,为什么是部分呢?因为此处的控制器主要负责功能处理部分: 1.收集.验证请求参数并绑定到命令对象: ...