《用两天学习光线追踪》4.封装成类

2023-11-26 09:50

本文主要是介绍《用两天学习光线追踪》4.封装成类,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

本项目参考自教程《Ray Tracing in One Weekend》,在跑通了所有例子之后,加上了自己的理解写成笔记,项目使用CPU多线程提速,并增加了GUI进度显示。
项目链接:https://github.com/maijiaquan/ray-tracing-with-imgui

目录:
《用两天学习光线追踪》1.项目介绍和ppm图片输出
《用两天学习光线追踪》2.射线、简单相机和背景输出
《用两天学习光线追踪》3.球体和表面法向量
《用两天学习光线追踪》4.封装成类
《用两天学习光线追踪》5.抗锯齿
《用两天学习光线追踪》6.漫反射材质
《用两天学习光线追踪》7.反射向量和金属材质
《用两天学习光线追踪》8.折射向量和电介质
《用两天学习光线追踪》9.可放置相机
《用两天学习光线追踪》10.散焦模糊

《用一周学习光线追踪》1.动态模糊
《用一周学习光线追踪》2.BVH树、AABB相交检测
《用一周学习光线追踪》3.纯色纹理和棋盘纹理
《用一周学习光线追踪》4.柏林噪声
《用一周学习光线追踪》5.球面纹理贴图
《用一周学习光线追踪》6.光照和轴对齐矩形
《用一周学习光线追踪》7.长方体和平移旋转


本节目标

场景中,如果有很多个球体怎么办呢?如果光线命中特殊材质后发射了反射怎么办?解决方案如下:
1.将球体封装成类。
2.一条射线的路径上可能会命中好几个球,因此要选出射线能命中的最近的球。
3.用数组存储这些球。
4.如果发射了反射等行为,则要记录上一条射线的命中信息,例如命中终点、命中点的法向量等。

上述需求实现后,我们会利用封装好的类,在数组中增加一个大球,放在上一节的小球下面,效果如下:

本节代码:main4.cpp


命中信息记录

我们可以用以下结构体来记录命中信息:

struct hit_record
{float t;     //命中射线的长度vec3 p;      //命中终点坐标vec3 normal; //命中点的法向量
};

如果想选出射线所命中的最近的球,可以循环遍历所有能命中的球,然后每一趟更新t值,最终能得到最小的t值。


定义物体的虚基类

物体的虚基类hittable只定义了一个虚函数hit(),该函数用于记录射线的命中信息,并返回是否击中。
t_mint_max区间用于限定射线的长度,用于排除我们不想被命中的较远处的物体。

class hittable
{
public:virtual bool hit(const ray &r, float t_min, float t_max, hit_record &rec) const = 0;
};

实现球类

球类sphere继承了物体hittable,并实现了hit()函数,作用是记录射线在t_mint_max区间内的命中信息

class sphere : public hittable
{
public:vec3 center;float radius;sphere() {}sphere(vec3 cen, float r) : center(cen), radius(r){};//如果命中了,命中记录保存到recvirtual bool hit(const ray &r, float t_min, float t_max, hit_record &rec) const{vec3 oc = r.origin() - center;float a = dot(r.direction(), r.direction());float b = dot(oc, r.direction());float c = dot(oc, oc) - radius * radius;float discriminant = b * b - a * c;if (discriminant > 0){float temp = (-b - sqrt(discriminant)) / a; //小根if (temp < t_max && temp > t_min){rec.t = temp;rec.p = r.point_at_parameter(rec.t);rec.normal = (rec.p - center) / radius;return true;}temp = (-b + sqrt(discriminant)) / a; //大根if (temp < t_max && temp > t_min){rec.t = temp;rec.p = r.point_at_parameter(rec.t);rec.normal = (rec.p - center) / radius;return true;}}return false;}
};

实现物体列表类

物体列表类hittable_list同样继承自hittable,成员函数有一个物体虚基类数组的指针。实现了hit()函数,作用是遍历数组中的所有物体,记录目前当前射线命中的最近的球,然后返回是否命中。

class hittable_list : public hittable
{
public:hittable **list;int list_size;hittable_list() {}hittable_list(hittable **l, int n){list = l;list_size = n;}//如果命中了,命中记录保存到recvirtual bool hit(const ray &r, float t_min, float t_max, hit_record &rec) const{hit_record temp_rec;bool hit_anything = false;double closest_so_far = t_max; //记录目前最近的t值for (int i = 0; i < list_size; i++){if (list[i]->hit(r, t_min, closest_so_far, temp_rec)){hit_anything = true;closest_so_far = temp_rec.t;rec = temp_rec; //只记录打到的最近的球}}return hit_anything;}
};

更新入口函数

下面注释为//new的地方为新增代码。
我们声明了一个名为world的变量,类型为hittable_list,用于存放的场景中的所有球类。
更新了之后,物体数组中除了上一节的小球,还添加一个半径为100的大球体,放在小球体的下面,且恰好跟小球相切,以突出球体顶部附近法向量坐标对应的颜色。

hittable *list[2];	//new
hittable *world;	//newvec3 lower_left_corner(-2.0, -1.0, -1.0); //左下角
vec3 horizontal(4.0, 0.0, 0.0);
vec3 vertical(0.0, 2.0, 0.0);
vec3 origin(0.0, 0.0, 0.0); // 相机原点void RayTracing()
{list[0] = new sphere(vec3(0, 0, -1), 0.5);	//newlist[1] = new sphere(vec3(0, -100.5, -1), 100);	//newworld = new hittable_list(list, 2);	//newfor (int j = ny - 1; j >= 0; j --){for (int i = 0; i < nx; i++){float u = float(i) / float(nx);float v = float(j) / float(ny);vec3 dir = lower_left_corner + u * horizontal + v * vertical - origin;ray r(origin, dir);vec3 col = color(r, world);	//new...}}
}

更新color()函数

更新之后,参数多了个world,也就是刚刚入口函数定义的一个hittable_list变量。对一条射线执行world->hit(),从而将该射线命中的最近的物体信息,记录到rec中,进行相应的处理,目前的处理仍然为上一节的法向量可视化。

//发射一条射线,并采样该射线最终输出到屏幕的颜色值
vec3 color(const ray &r, hittable *world)
{hit_record rec;if (world->hit(r, 0.001, MAXFLOAT, rec)) //射线命中物体{return 0.5 * vec3(rec.normal.x() + 1, rec.normal.y() + 1, rec.normal.z() + 1); //可视化RGB通道}else{vec3 unit_direction = unit_vector(r.direction());float t = 0.5 * (unit_direction.y() + 1.0);return (1.0 - t) * vec3(1.0, 1.0, 1.0) + t * vec3(0.5, 0.7, 1.0);}
}

最终效果如下:


参考资料:《Ray Tracing in One Weekend》

这篇关于《用两天学习光线追踪》4.封装成类的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/425476

相关文章

Java深度学习库DJL实现Python的NumPy方式

《Java深度学习库DJL实现Python的NumPy方式》本文介绍了DJL库的背景和基本功能,包括NDArray的创建、数学运算、数据获取和设置等,同时,还展示了如何使用NDArray进行数据预处理... 目录1 NDArray 的背景介绍1.1 架构2 JavaDJL使用2.1 安装DJL2.2 基本操

C++实现封装的顺序表的操作与实践

《C++实现封装的顺序表的操作与实践》在程序设计中,顺序表是一种常见的线性数据结构,通常用于存储具有固定顺序的元素,与链表不同,顺序表中的元素是连续存储的,因此访问速度较快,但插入和删除操作的效率可能... 目录一、顺序表的基本概念二、顺序表类的设计1. 顺序表类的成员变量2. 构造函数和析构函数三、顺序表

Go语言利用泛型封装常见的Map操作

《Go语言利用泛型封装常见的Map操作》Go语言在1.18版本中引入了泛型,这是Go语言发展的一个重要里程碑,它极大地增强了语言的表达能力和灵活性,本文将通过泛型实现封装常见的Map操作,感... 目录什么是泛型泛型解决了什么问题Go泛型基于泛型的常见Map操作代码合集总结什么是泛型泛型是一种编程范式,允

SpringBoot如何使用TraceId日志链路追踪

《SpringBoot如何使用TraceId日志链路追踪》文章介绍了如何使用TraceId进行日志链路追踪,通过在日志中添加TraceId关键字,可以将同一次业务调用链上的日志串起来,本文通过实例代码... 目录项目场景:实现步骤1、pom.XML 依赖2、整合logback,打印日志,logback-sp

HarmonyOS学习(七)——UI(五)常用布局总结

自适应布局 1.1、线性布局(LinearLayout) 通过线性容器Row和Column实现线性布局。Column容器内的子组件按照垂直方向排列,Row组件中的子组件按照水平方向排列。 属性说明space通过space参数设置主轴上子组件的间距,达到各子组件在排列上的等间距效果alignItems设置子组件在交叉轴上的对齐方式,且在各类尺寸屏幕上表现一致,其中交叉轴为垂直时,取值为Vert

Ilya-AI分享的他在OpenAI学习到的15个提示工程技巧

Ilya(不是本人,claude AI)在社交媒体上分享了他在OpenAI学习到的15个Prompt撰写技巧。 以下是详细的内容: 提示精确化:在编写提示时,力求表达清晰准确。清楚地阐述任务需求和概念定义至关重要。例:不用"分析文本",而用"判断这段话的情感倾向:积极、消极还是中性"。 快速迭代:善于快速连续调整提示。熟练的提示工程师能够灵活地进行多轮优化。例:从"总结文章"到"用

【前端学习】AntV G6-08 深入图形与图形分组、自定义节点、节点动画(下)

【课程链接】 AntV G6:深入图形与图形分组、自定义节点、节点动画(下)_哔哩哔哩_bilibili 本章十吾老师讲解了一个复杂的自定义节点中,应该怎样去计算和绘制图形,如何给一个图形制作不间断的动画,以及在鼠标事件之后产生动画。(有点难,需要好好理解) <!DOCTYPE html><html><head><meta charset="UTF-8"><title>06

学习hash总结

2014/1/29/   最近刚开始学hash,名字很陌生,但是hash的思想却很熟悉,以前早就做过此类的题,但是不知道这就是hash思想而已,说白了hash就是一个映射,往往灵活利用数组的下标来实现算法,hash的作用:1、判重;2、统计次数;

零基础学习Redis(10) -- zset类型命令使用

zset是有序集合,内部除了存储元素外,还会存储一个score,存储在zset中的元素会按照score的大小升序排列,不同元素的score可以重复,score相同的元素会按照元素的字典序排列。 1. zset常用命令 1.1 zadd  zadd key [NX | XX] [GT | LT]   [CH] [INCR] score member [score member ...]

【机器学习】高斯过程的基本概念和应用领域以及在python中的实例

引言 高斯过程(Gaussian Process,简称GP)是一种概率模型,用于描述一组随机变量的联合概率分布,其中任何一个有限维度的子集都具有高斯分布 文章目录 引言一、高斯过程1.1 基本定义1.1.1 随机过程1.1.2 高斯分布 1.2 高斯过程的特性1.2.1 联合高斯性1.2.2 均值函数1.2.3 协方差函数(或核函数) 1.3 核函数1.4 高斯过程回归(Gauss