【Ray Tracing in One Weekend 超详解】 光线追踪1-10
《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的更多相关文章
- 【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-7 任意长方体 && 场景案例
上一篇比较简单,很久才发是因为做了一些好玩的场景,后来发现这一章是专门写场景例子的,所以就安排到了这一篇 Preface 这一篇要介绍的内容有: 1. 自己做的光照例子 2. Cornell box画 ...
- 【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 ...
随机推荐
- 枚举类型---java基础代码
package com.mon11.day4; /** * 类说明 :定义枚举 * @author 作者 : chenyanlong * @version 创建时间:2017年11月4日 */ pub ...
- Python 内置变量
Python 隐藏变量 __doc__ # 表示本文件的注释__file__ # 表示本文件的路径 __package__ # 导入的py文件所在的文件夹路径__cached__ # 导入文件的缓存_ ...
- Java基础-变量的定义以及作用域详解
Java基础-变量的定义以及作用域详解 作者:尹正杰 版权声明:原创作品,谢绝转载!否则将追究法律责任. 一.字面量 常量(字面量)表示不能改变的数值(程序中直接出现的值).字面量有时也称为直接量,包 ...
- Java基础-原码反码补码
Java基础-原码反码补码 作者:尹正杰 版权声明:原创作品,谢绝转载!否则将追究法律责任. 注意,我们这里举列的原码和反码只是为了求负数的补码,在计算机中没有原码,反码的存在,只有补码. 一.原码 ...
- Swift学习笔记2
1.函数参数都有一个外部参数名(external parameter name)和一个局部参数名(local parameter name).外部参数名用于在函数调用时标注传递给函数的参数,局部参数名 ...
- python 喜马拉雅 音乐下载 演示代码
1.主程序文件 import os import json import requests from contextlib import closing from progressbar import ...
- raphael.js 给元素 hover 添加glow() 外发光
用raphael.js 给 svg画布里面添加个元素,嗯就圓好了,男人一般都喜欢圆形的东西,比如xx , xxx , 还有xxx $(document).ready(function() { var ...
- HDU 2544 最短路 最短路问题
解题报告: 这题就是求两个单源点之间的最小距离,属于最短路问题,由于数据量很小,只有100,所以这题可以用弗洛伊德也可以用迪杰斯特拉,都可以过,但是用迪杰斯特拉会快一点,但用弗洛伊德的代码会稍短一点, ...
- 基于Window10搭建android开发环境
一.安装JDK 1.下载(网页链接) 2.双击安装文件进行安装,安装在合适目录,例如:D:\Java\jdk1.8.0_201与D:\Java\jre1.8.0_201 3.设置环境变量 3.1.JA ...
- C++ socket 网络编程 简单聊天室
操作系统里的进程通讯方式有6种:(有名/匿名)管道.信号.消息队列.信号量.内存(最快).套接字(最常用),这里我们来介绍用socket来实现进程通讯. 1.简单实现一个单向发送与接收 这是套接字的工 ...