《Ray Tracing in One Weekend》完结篇

最近课程上机实验,封面图渲染时间也超长,所以写东西就落下了,见谅

这篇之后,我会继续《Ray Tracing The Next Week》,还请多多关注

这几天我在渲染这本书的封面图,封面图还没出,不算结束,刚好安排了10节

今天呢,有两件事:

1.阐述整个工程的文件组织即内容

2.阐述封面,完结

12.1工程文件组织

试过很多方法,问过很多老师,无奈,子类继承实现的父类纯虚函数实在无法和类声明分成两个文件(即声明放于.h,其他实现放在.cpp中),室友说,纯虚函数继承实现和模板很类似

所以,我们在合适的时候使用hpp

在学习过程中,我们遇到了诸如反射、折射之类的函数,它们并不应该属于某个具体子类,或者抽象基类

所以,我把它们写在了泛型3D数学库里面了

C++泛型3D数学库是我们学光线追踪的数学专用库了吧算是

向量库

基础光学几何函数库

在回头看我们的光线追踪的项目代码

1.工程定义文件

我们之前是在ray这个最基本的类中定义了一些基本的命名,尔后,发现,所有的东西都要用ray::val_type诸如此类的代码去描述光线追踪所用到的一些普遍类型,这个非常麻烦,代码也长,后来,我们将它们移出了ray-class,放在了namespace rt中,但是,仍然放在ray文件中,这个很不合理,所以我们定义了一个RTdef.h,专门用于定义一些光线追踪的常量和命名

/// RTdef.h

// -----------------------------------------------------
// [author] lv
// [begin ] 2019.1.1
// [brief ] the basic concept of rt
// -----------------------------------------------------
#pragma once #include <lvgm\type_vec\type_vec.h> //https://www.cnblogs.com/lv-anchoret/p/10163085.html
#include <lvgm\opticsfunc.hpp> //https://www.cnblogs.com/lv-anchoret/p/10241904.html
#include <lvgm\randfunc.hpp> //https://www.cnblogs.com/lv-anchoret/p/10241904.html namespace rt
{
using rtvar = lvgm::precision; using rtvec = lvgm::vec3<rtvar>; constexpr static rtvar rtInf() { return static_cast<rtvar>(0x3f3f3f3f); } //最大值 constexpr rtvar π = 3.1415926; }

2.光线类

/// ray.h

// -----------------------------------------------------
// [author] lv
// [begin ] 2018.12
// [brief ] the ray-class for the ray-tracing project
// from the 《ray tracing in one week》
// -----------------------------------------------------
#pragma once #include "RTdef.h" namespace rt
{ class ray
{
public:
ray()
:_a{ rtvec() }
, _b{ rtvec() }
{ } ray(const rtvec& a, const rtvec& b)
:_a(a)
, _b(b)
{ } ray(const ray& r)
:_a(r._a)
, _b(r._b)
{ } inline rtvec origin()const { return _a; } inline rtvec direction()const { return _b; } inline rtvec go(const rtvar t)const { return _a + t * _b; } private:
rtvec _a; rtvec _b; };
}

3.相机类

/// camera.h
//https://www.cnblogs.com/lv-anchoret/p/10221058.html
// -----------------------------------------------------
// [author] lv
// [begin ] 2019.1
// [brief ] the camera-class for the ray-tracing project
// from the 《ray tracing in one week》
// ----------------------------------------------------- #pragma once #include "ray.h" namespace rt
{ class camera
{
public:
camera(rtvec lookfrom, rtvec lookat, rtvec vup, rtvar vfov, rtvar aspect, rtvar aperture, rtvar focus)
:_eye(lookfrom)
, _lens_radius(aperture / )
{
rtvar theta = vfov * π / ;
rtvar half_height = tan(theta / ) * focus; //tan(theta/2) = (height/2) / 焦距
rtvar half_width = aspect * half_height;
_w = (lookfrom - lookat).ret_unitization();
_u = cross(vup, _w).ret_unitization();
_v = cross(_w, _u); //向量运算
_start = _eye - half_width * _u - half_height * _v - focus * _w;//高和宽都乘了焦距,w也要乘,不然公式是错的
_horizontal = * half_width * _u;
_vertical = * half_height * _v;
} const ray get_ray(const rtvar u, const rtvar v)const
{
rtvec rd = rtvec(_lens_radius * lvgm::random_unit_plane());
rtvec offset = _u * rd.x() + _v * rd.y();
return ray{ _eye + offset, _start + u*_horizontal + v*_vertical - (_eye + offset) };
} const ray get_ray(const lvgm::vec2<rtvar>& para)const { return get_ray(para.u(), para.v()); } inline const rtvec& eye()const { return _eye; } inline const rtvec& start()const { return _start; } inline const rtvec& horizontal()const { return _horizontal; } inline const rtvec& vertical()const { return _vertical; } inline const rtvec& u()const { return _u; } inline const rtvec& v()const { return _v; } inline const rtvec& w()const { return _w; } inline const rtvar lens_r()const { return _lens_radius; } private:
rtvec _u; rtvec _v; rtvec _w; rtvec _eye; rtvec _start; //left-bottom rtvec _horizontal; rtvec _vertical; rtvar _lens_radius; //the radius of lens }; }

4.碰撞相交部分

有一个碰撞相交基类

/// intersect.h
//https://www.cnblogs.com/lv-anchoret/p/10190092.html
// -----------------------------------------------------
// [author] lv
// [begin ] 2018.12
// [brief ] the intersect-class for the ray-tracing project
// from the 《ray tracing in one week》
// -----------------------------------------------------
#pragma once namespace rt
{
class material; struct hitInfo
{
lvgm::precision _t; //ray 中的系数t
rtvec _p; //相交点、撞击点
rtvec _n; //_p点的表面法线
material* materialp; //材质
}; class intersect
{
public:
intersect() { } /*
@brief: 撞击函数,求取撞击点相关记录信息
@param: sight->视线
系数t的上下界->筛选撞击点
rec->返回撞击点信息
@retur: 是否存在合法撞击点
*/
virtual bool hit(const ray& sight, rtvar t_min, rtvar t_max, hitInfo& rec)const = ; virtual ~intersect() { }
}; }

后面有一个子类intersections是用于处理一组碰撞相交的类,类比于容器

/// intersections.h
// https://www.cnblogs.com/lv-anchoret/p/10190092.html // -----------------------------------------------------
// [author] lv
// [begin ] 2018.12
// [brief ] the intersections-class for the ray-tracing project
// from the 《ray tracing in one week》
// -----------------------------------------------------
#pragma once namespace rt
{ class intersections :public intersect
{
public:
intersections() { } intersections(intersect** list, size_t n) :_list(list), _size(n) { } virtual bool hit(const ray& sight, rtvar t_min, rtvar t_max, hitInfo& rec)const override; private:
intersect** _list; size_t _size;
}; bool intersections::hit(const ray& sight, rtvar t_min, rtvar t_max, hitInfo& rec)const
{
hitInfo t_rec;
bool hitSomething = false;
rtvar far = t_max; //刚开始可以看到无限远
for (int i = ; i < _size; ++i)
{
if (_list[i]->hit(sight, t_min, far, t_rec))
{
hitSomething = true;
far = t_rec._t; //将上一次的最近撞击点作为视线可达最远处
rec = t_rec;
}
}
return hitSomething;
} }

还有一个子类sphere是一种几何体用来做自身的碰撞检测的,之后,我们可能还会加入心形几何体类

/// sphere.h
// https://www.cnblogs.com/lv-anchoret/p/10190092.html
// -----------------------------------------------------
// [author] lv
// [begin ] 2018.1.1
// [brief ] the sphere-class for the ray-tracing project
// from the 《ray tracing in one week》
// -----------------------------------------------------
#pragma once namespace rt
{ class sphere :public intersect
{
public:
sphere() { } /*
@para1: 球心坐标
@para2: 球半径
@para3: 材质
*/
sphere(const rtvec& h, rtvar r, material* ma) :_heart(h), _radius(r), _materialp(ma) { } ~sphere() { if (_materialp) delete _materialp; } virtual bool hit(const ray& sight, rtvar t_min, rtvar t_max, hitInfo& rec)const override; inline const rtvar r()const { return _radius; } inline const rtvec& heart()const { return _heart; } inline rtvar& r() { return _radius; } inline rtvec& heart() { return _heart; } private:
rtvec _heart; rtvar _radius; material* _materialp;
}; bool sphere::hit(const ray& sight, rtvar t_min, rtvar t_max, hitInfo& rec)const
{
rtvec trace = sight.origin() - _heart;
rtvar a = dot(sight.direction(), sight.direction());
rtvar b = 2.0 * dot(trace, sight.direction());
rtvar c = dot(trace, trace) - _radius * _radius;
rtvar delt = b*b - 4.0*a*c;
if (delt > )
{
rec.materialp = _materialp;
rtvar x = (-b - sqrt(delt)) / (2.0*a);
if (x < t_max && x > t_min)
{
rec._t = x;
rec._p = sight.go(rec._t);
rec._n = (rec._p - _heart) / _radius;
return true;
}
x = (-b + sqrt(delt)) / (2.0*a);
if (x < t_max && x > t_min)
{
rec._t = x;
rec._p = sight.go(x);
rec._n = (rec._p - _heart) / _radius;
return true;
}
}
return false;
} }

一个总文件

/// RThit.h
// https://www.cnblogs.com/lv-anchoret/p/10190092.html // -----------------------------------------------------
// [author] lv
// [begin ] 2019.1
// [brief ] some intersects
// intersections
// sphere
// heart
// ----------------------------------------------------- #pragma once #include "ray.h"
#include "intersect.h" #include "sphere.hpp"
#include "intersections.hpp"

5.材质类

材质有一个基类和三个子类

/// material.h

// -----------------------------------------------------
// [author] lv
// [begin ] 2018.12
// [brief ] the material-class for the ray-tracing project
// from the 《ray tracing in one week》
// -----------------------------------------------------
#pragma once namespace rt
{ //abstract basic class
class material
{
public: /*
@brief: produce a scattered ray
@param: InRay -> Incident light
info -> the information of intersect-point(hit-point)
attenuation -> when scattered, how much the ray should be attenuated by tis reflectance R
scattered -> as we talk, it is a new sight; or
it is the scattered ray with the intersect-point
@retur: the function calculate a scattered ray or not
*/
virtual bool scatter(const ray& InRay, const hitInfo& info, rtvec& attenuation, ray& scattered)const = ; }; }
/// diffuse.hpp
// https://www.cnblogs.com/lv-anchoret/p/10198423.html // -----------------------------------------------------
// [author] lv
// [begin ] 2018.12
// [brief ] one of the materials
// ----------------------------------------------------- #pragma once namespace rt
{
//diffuse material
class lambertian : public material
{
public:
lambertian(const rtvec& a) :_albedo(a) { } bool scatter(const ray& rIn, const hitInfo& info, rtvec& attenuation, ray& scattered)const override
{
rtvec target = info._p + info._n + lvgm::random_unit_sphere();
scattered = ray{ info._p, target - info._p };
attenuation = _albedo;
return true;
}
protected: rtvec _albedo;
}; }
/// metal.hpp
// https://www.cnblogs.com/lv-anchoret/p/10206773.html // -----------------------------------------------------
// [author] lv
// [begin ] 2018.12
// [brief ] one of the materials
// ----------------------------------------------------- #pragma once namespace rt
{
//metal material
class metal :public material
{
public: metal(const rtvec& a, const rtvar f = .) :_albedo(a)
{
if (f < && f >= )_fuzz = f;
else _fuzz = ;
} virtual bool scatter(const ray& rIn, const hitInfo& info, rtvec& attenuation, ray& scattered)const
{
rtvec target = reflect(rIn.direction().ret_unitization(), info._n);
scattered = ray{ info._p, target + _fuzz * lvgm::random_unit_sphere() };
attenuation = _albedo;
return dot(scattered.direction(), info._n) != ;
} inline static rtvec reflect(const rtvec& in, const rtvec& n) { return in - * dot(in, n)*n; } protected: rtvec _albedo; rtvar _fuzz;
}; }
/// dielectric.hpp
// https://www.cnblogs.com/lv-anchoret/p/10217719.html // -----------------------------------------------------
// [author] lv
// [begin ] 2019.1
// [brief ] one of the materials
// -----------------------------------------------------
#pragma once namespace rt
{
class dielectric :public material
{
public:
dielectric(const rtvar RI) :_RI(RI) { } virtual bool scatter(const ray& InRay, const hitInfo& info, rtvec& attenuation, ray& scattered)const override; protected:
rtvar _RI; inline rtvar schlick(const rtvar cosine)const;
}; bool dielectric::scatter(const ray& InRay, const hitInfo& info, rtvec& attenuation, ray& scattered)const
{
rtvec outward_normal;
rtvec refracted;
rtvec reflected = reflect(InRay.direction(), info._n);
rtvar eta;
rtvar reflect_prob;
rtvar cos;
attenuation = rtvec(., ., .); if (dot(InRay.direction(), info._n) > )
{
outward_normal = -info._n;
eta = _RI;
cos = _RI * dot(InRay.direction(), info._n) / InRay.direction().normal();
}
else
{
outward_normal = info._n;
eta = 1.0 / _RI;
cos = -dot(InRay.direction(), info._n) / InRay.direction().normal();
} if (refract(InRay.direction(), outward_normal, eta, refracted))
reflect_prob = schlick(cos); //如果有折射,计算反射系数
else
reflect_prob = 1.0; //如果没有折射,那么为全反射 if (lvgm::rand01() < reflect_prob)
scattered = ray(info._p, reflected);
else
scattered = ray(info._p, refracted); return true;
} inline rtvar dielectric::schlick(const rtvar cosine)const
{
rtvar r0 = (. - _RI) / (. + _RI);
r0 *= r0;
return r0 + ( - r0)*pow(( - cosine), );
}
}

总文件

/// RTmaterial.h

// -----------------------------------------------------
// [author] lv
// [begin ] 2019.1
// [brief ] some materials
// diffuse
// metal
// dielectric
// -----------------------------------------------------
#pragma once #include "ray.h"
#include "intersect.h"
#include "material.h" #include "diffuse.hpp"
#include "metal.hpp"
#include "dielectric.hpp"

我们所有的文件就写完了

12.2封面完结

这个图让我学会了分段渲染。。。

这个图非常好看,于是乎,整了一个600*400的,整整渲染了两天(....),内心是崩溃的

我试过并发编程,结果效果不好(自己也不怎么会上锁。。。)

所以,就只能单线程处理,时间超过十个小时左右吧,VS那个时间过程诊断框就坏死了。。。

有时候,不能一直渲染,这个时候,被迫结束后,只需要读取已有文件的行数,然后计算出渲染了多少个点了,然后在渲染的双重for循环中从下一个点开始渲染写入文件即可,就可以随时随地想停就停,想渲染就渲染,因为图像本事就是一个一个像素点,我们只需要24w个点的文件数据即可,你也可以并发写入多个文件,最后拼在一起

我们采用的相机参数是这样的

据说是官方的,我也不清楚

还有一个文章写得非常好,是写相机的参数测试的,大家可以阅读一下,对相机的参数有一个更直观深入的了解

相机各个参数测试效果

因为这个图实在是渲染了好久,所以也没有出一些其他的效果图,可能之后会更,大家可以自己设置球体以及相机,欢迎在评论区发出你的渲染图~

下面是代码:

#define LOWPRECISION

#include <fstream>
#include "RTmaterial.h"
#include "RThit.h"
#include "camera.h"
#define stds std::
using namespace rt; rtvec lerp(const ray& sight, intersect* world, int depth)
{
hitInfo info;
if (world->hit(sight, (rtvar)0.001, rtInf(), info))
{
ray scattered;
rtvec attenuation;
if (depth < && info.materialp->scatter(sight, info, attenuation, scattered))
return attenuation * lerp(scattered, world, depth + );
else
return rtvec(, , );
}
else
{
rtvec unit_dir = sight.direction().ret_unitization();
rtvar t = 0.5*(unit_dir.y() + .);
return (. - t)*rtvec(., ., .) + t*rtvec(0.5, 0.7, 1.0);
}
} intersect* random_sphere()
{
int cnt = ;
intersect **list = new intersect*[cnt + ];
list[] = new sphere(rtvec(, -, ), , new lambertian(rtvec(0.5, 0.5, 0.5)));
int size = ;
for (int a = -; a < ; ++a)
for (int b = -; b < ; ++b)
{
rtvar choose_mat = lvgm::rand01();
rtvec center(a + 0.9 * lvgm::rand01(), 0.2, b + 0.9*lvgm::rand01());
if ((center - rtvec(, 0.2, )).normal()>0.9)
{
if (choose_mat < 0.75)
{
list[size++] = new sphere(center, 0.2, new lambertian(rtvec(lvgm::rand01()*lvgm::rand01(), lvgm::rand01()*lvgm::rand01(), lvgm::rand01()*lvgm::rand01())));
}
else if (choose_mat < 0.9)
{
list[size++] = new sphere(center, 0.2, new metal(rtvec(0.5*( + lvgm::rand01()), 0.5*( + lvgm::rand01()), 0.5*( + lvgm::rand01())), 0.5*lvgm::rand01()));
}
else
{
list[size++] = new sphere(center, 0.2, new dielectric(1.5));
}
}
} list[size++] = new sphere(rtvec(, , ), 1.0, new dielectric(1.5));
list[size++] = new sphere(rtvec(-, , ), 1.0, new lambertian(rtvec(0.4, 0.2, 0.1)));
list[size++] = new sphere(rtvec(, , ), 1.0, new metal(rtvec(0.7, 0.6, 0.5), .)); return new intersections(list, size);
} void build_12_1()
{
stds ofstream file("graph12-1.ppm");
size_t W = , H = , sample = ; if (file.is_open())
{
file << "P3\n" << W << " " << H << "\n255\n" << stds endl; intersect* world = random_sphere(); rtvec lookfrom(, , );
rtvec lookat(, , );
float dist_to_focus = (lookfrom - lookat).normal();
float aperture = 0.0;
camera cma(lookfrom, lookat, rtvec(, , ), , rtvar(W) / rtvar(H), aperture, 0.7*dist_to_focus); for (int y = H - ; y >= ; --y)
for (int x = ; x < W; ++x)
{
rtvec color;
for (int cnt = ; cnt < sample; ++cnt)
{
lvgm::vec2<rtvar> para{
(lvgm::rand01() + x) / W,
(lvgm::rand01() + y) / H };
color += lerp(cma.get_ray(para), world, );
}
color /= sample;
color = rtvec(sqrt(color.r()), sqrt(color.g()), sqrt(color.b())); //gamma 校正
int r = int(255.99 * color.r());
int g = int(255.99 * color.g());
int b = int(255.99 * color.b());
file << r << " " << g << " " << b << stds endl;
}
file.close(); if (world)delete world; stds cout << "complished" << stds endl;
}
else
stds cerr << "open file error" << stds endl;
} int main()
{
build_12_1();
}

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

【Ray Tracing in One Weekend 超详解】 光线追踪1-10的更多相关文章

  1. 【Ray Tracing The Next Week 超详解】 光线追踪2-9

    我们来整理一下项目的代码 目录 ----include --hit --texture --material ----RTdef.hpp ----ray.hpp ----camera.hpp ---- ...

  2. 【Ray Tracing The Next Week 超详解】 光线追踪2-6 Cornell box

    Chapter 6:Rectangles and Lights 今天,我们来学习长方形区域光照  先看效果 light 首先我们需要设计一个发光的材质 /// light.hpp // ------- ...

  3. 【Ray Tracing in One Weekend 超详解】 光线追踪1-4

    我们上一篇写了Chapter5 的第一个部分表面法线,那么我们来学剩下的部分,以及Chapter6. Chapter5:Surface normals and multiple objects. 我们 ...

  4. 【Ray Tracing The Next Week 超详解】 光线追踪2-7 任意长方体 && 场景案例

    上一篇比较简单,很久才发是因为做了一些好玩的场景,后来发现这一章是专门写场景例子的,所以就安排到了这一篇 Preface 这一篇要介绍的内容有: 1. 自己做的光照例子 2. Cornell box画 ...

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

     Preface 今天有两个东东,一个是体积烟雾,一个是封面图 下一篇我们总结项目代码 Chapter 8:Volumes 我们需要为我们的光线追踪器添加新的物体——烟.雾,也称为participat ...

  6. 【Ray Tracing The Next Week 超详解】 光线追踪2-5

    Chapter 5:Image Texture Mapping 先看效果: 我们之前的纹理是利用的是撞击点p处的位置信息,比如大理石纹理 而我们今天的图片映射纹理采用2D(u,v)纹理坐标来进行. 在 ...

  7. 【Ray Tracing in One Weekend 超详解】 光线追踪1-8 自定义相机设计

    今天,我们来学习如何设计自定义位置的相机 ready 我们只需要了解我们之前的坐标体系,或者说是相机位置 先看效果   Chapter10:Positionable camera 这一章我们直接用概念 ...

  8. 【Ray Tracing The Next Week 超详解】 光线追踪2-4 Perlin noise

     Preface 为了得到更好的纹理,很多人采用各种形式的柏林噪声(该命名来自于发明人 Ken Perlin) 柏林噪声是一种比较模糊的白噪声的东西:(引用书中一张图) 柏林噪声是用来生成一些看似杂乱 ...

  9. 【Ray Tracing The Next Week 超详解】 光线追踪2-3

     Preface 终于到了激动人心的纹理章节了 然鹅,看了下,并不激动 因为我们之前就接触过 当初有一个 attenuation 吗? 对了,这就是我们的rgb分量过滤器,我们画出的红色.蓝色.绿色等 ...

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

    Chapter 2:Bounding Volume Hierarchies 今天我们来讲层次包围盒,乍一看比较难,篇幅也多,但是咱们一步一步来,相信大家应该都能听懂 BVH 和 Perlin text ...

随机推荐

  1. 枚举类型---java基础代码

    package com.mon11.day4; /** * 类说明 :定义枚举 * @author 作者 : chenyanlong * @version 创建时间:2017年11月4日 */ pub ...

  2. Python 内置变量

    Python 隐藏变量 __doc__ # 表示本文件的注释__file__ # 表示本文件的路径 __package__ # 导入的py文件所在的文件夹路径__cached__ # 导入文件的缓存_ ...

  3. Java基础-变量的定义以及作用域详解

    Java基础-变量的定义以及作用域详解 作者:尹正杰 版权声明:原创作品,谢绝转载!否则将追究法律责任. 一.字面量 常量(字面量)表示不能改变的数值(程序中直接出现的值).字面量有时也称为直接量,包 ...

  4. Java基础-原码反码补码

    Java基础-原码反码补码 作者:尹正杰 版权声明:原创作品,谢绝转载!否则将追究法律责任. 注意,我们这里举列的原码和反码只是为了求负数的补码,在计算机中没有原码,反码的存在,只有补码. 一.原码 ...

  5. Swift学习笔记2

    1.函数参数都有一个外部参数名(external parameter name)和一个局部参数名(local parameter name).外部参数名用于在函数调用时标注传递给函数的参数,局部参数名 ...

  6. python 喜马拉雅 音乐下载 演示代码

    1.主程序文件 import os import json import requests from contextlib import closing from progressbar import ...

  7. raphael.js 给元素 hover 添加glow() 外发光

    用raphael.js 给 svg画布里面添加个元素,嗯就圓好了,男人一般都喜欢圆形的东西,比如xx ,  xxx , 还有xxx $(document).ready(function() { var ...

  8. HDU 2544 最短路 最短路问题

    解题报告: 这题就是求两个单源点之间的最小距离,属于最短路问题,由于数据量很小,只有100,所以这题可以用弗洛伊德也可以用迪杰斯特拉,都可以过,但是用迪杰斯特拉会快一点,但用弗洛伊德的代码会稍短一点, ...

  9. 基于Window10搭建android开发环境

    一.安装JDK 1.下载(网页链接) 2.双击安装文件进行安装,安装在合适目录,例如:D:\Java\jdk1.8.0_201与D:\Java\jre1.8.0_201 3.设置环境变量 3.1.JA ...

  10. C++ socket 网络编程 简单聊天室

    操作系统里的进程通讯方式有6种:(有名/匿名)管道.信号.消息队列.信号量.内存(最快).套接字(最常用),这里我们来介绍用socket来实现进程通讯. 1.简单实现一个单向发送与接收 这是套接字的工 ...