UVa12313 A Tiny Raytracer

UVa12313 A Tiny Raytracer

  • 题目链接
  • 题意
  • 分析
  • AC 代码

题目链接

    UVA - 12313 A Tiny Raytracer

题意

   给出 《训练指南》题意翻译

   本题的任务是实现一个小型光线追踪渲染器。场景由若干三角形网格(triangle mesh)组成,有且仅有一个点光源(point-light)。
   相机模型
   本题使用的相机是透视相机,位置在camera_pos,指向camera_target,up向量为camera_up,横向FOV等于 f f f度,拍摄出来的图片是W×H像素,如下图所示。相机模型
   上图中,假想在相机的正前方有一个矩形(称为图像平面),代表着场景在相机中的影像,则它的离散形式就是渲染器的输出。在本题中,所有像素都是正方形,因此图像平面的宽度与高度之比总是W:H。
   从camera_pos出发指向camera_target 的射线穿过图像平面的中心,而图像平面的局部y轴就是camera_up。横向FOV 是指图像平面左右边界相当于相机的张角。
   为了计算三维场景中一个点在最终图像中的位置,只需从相机出发引一条射线穿过该点,则该射线与图像平面的交点就是所求。如果没有交点,则该点不可见。不难证明,图像平面离相机的距离无关紧要,只要y轴方向为camera_up,从camera_pos出发指向camera_target的射线穿过图像平面的中心即可。
   光线追踪原理
   对于最终图像中的每个像素,我们考虑一条射线,从眼睛(也就是相机,下同)出发,穿过像素的中心。只要跟踪这条射线,看它在场景中碰到了什么颜色的物体,根据光路可逆原理,就能知道这个像素是什么颜色的。
   上述原理是高度简化的,不过足以解决本题。在最简单的情况下,所有物体既不反光也不透明,则每当射线碰到一个物体时,可以直接计算这个物体的颜色,方法是连接碰撞点和光源(本题只有一个光源),如果连线被其他物体挡住,说明这个点处于阴影中,否则用随后介绍的着色算法计算这个点的颜色。
   在真实场景中,由于玻璃和水这样的物体存在,我们需要考虑光线和物体的多次碰撞,以处理反射(reflection)和折射(refraction),方法如下:如果射线碰到了一个反射性物体,则派生出一条新的反射光线,从碰撞点射出,指向碰撞表面的外部。同理,如果射线碰到了一个有一定透明度的物体,则派生出一条新的折射光线,从碰撞点射出,指向碰撞表面的内部。如果碰撞面两侧物体的折射系数(index of refraction)不同,则光线的方向将发生改变,改变方式遵守snell定律 n 1 sin ⁡ θ 1 = n 2 sin ⁡ θ 2 n_1\sin\theta_1=n_2\sin\theta_2 n1sinθ1=n2sinθ2
   注意,如果发生了全反射(total internal reflection),应当停止跟踪该光线,而不是派生出一条反射光线。
   事实上,反射光线和折射光线本身还能继续派生出新的光线,所以我们实际上拥有一棵光线树。为了避免无穷无尽的递归下去,我们规定树的最大高度(在本题中总是等于4),这样,每个叶子要么在深度上达到了最大值,要么碰到了一个既不反射也不折射的物体(或者什么都没射到)。
   递归过程伪代码如下。

Color trace_ray(int depth, Ray ray) {Color point_color = BLACK, reflect_color = BLACK, refract_color = BLACK;Intersection i = get_first_intersection(ray);if(i.objID >= 0) { //与某物体相交double refl = scene.obj[i.objID].refl;double refr = scene.obj[i.objID].refr;point_color = get_point_color(i) * (1 - refl - refr);if(depth < maxdepth && refl > 0)reflect_color = trace_ray(depth+1, get_reflected_ray(ray, i)) * refl;if(depth < maxdepth && refr > 0)refract_color = trace_ray(depth+1, get_refracted_ray(ray, i)) * refr;}return point_color + reflect_color + refract_color;
}

   注意,只要深度没有达到最大值,不管反射系数是多么小的正数,都应该跟踪反射光线;折射光线也是如此。两个颜色的加法将在随后定义。
   着色
   前面遗留了一个问题,就是如何计算碰撞点的颜色(即上面的get_point_color函数)。在本题中,用Lambertian着色法(也叫余弦着色法),即根据碰撞表面的法线和从碰撞点到光源的向量的点积计算亮度。当夹角增大时,亮度按照余弦函数减小。
   如果点积等于0,说明两向量垂直,此时我们并不希望这个点完全呈黑色,而是要给它一点所谓的“环境光”。我们用ambient_coefficient 来表示这个系数,而diffuse_coefficient =1-ambient_coefficient表示漫反射系数。在本题中,三角形都看成是双面反光的,因此点积部分取了绝对值。object_color是物体的一个属性,即完全照明时的颜色。着色伪代码如下。

double shade;
if(is_shadowed(i)) //判断交点i是否在阴影中shade = 0;
elseshade = fabs(Dot(light_vector, normal_vector)); //注意两个向量都应归一化
return object_color * light_color * (ambient_coeff + diffuse_coeff*shade);

   简单起见,只要连接交点和光源的线段被一个物体阻挡(即使该物体反光或者透明),就算作该交点在阴影中。这样做的确会让渲染结果不正确,但在本题中请忽略这个Bug。
   在本题中,颜色用三元组(r, g, b)表示,其中实数r, g, b满足0≤r,g,b≤1。颜色加法和向量加法一样,也是每一维分别相加。不难发现,如果严格按照上面的规则编写代码,颜色加法的结果总是合法的(即相加后的结果仍满足0≤r,g,b≤1)。

分析

    按照中文翻译即可清晰地写出代码,说几点需要注意的:1、“发生全反射(total internal reflection)时应该停止追踪该光线”指的是停止追踪折射光线,反射光线依然要追踪的,也就是说入射角满足全反射时要将refr系数置为0;2、与0比的阈值eps,推荐设置成1e-10(太小,比如1e-12会导致WA);3、射线(Ray)需要记录所在媒介的介质系数(初始在真空,取值1),后面如果折射进入了某个object,则介质系数为此object的介质系数,然后再反射时介质系数不变,但再折射出去时介质系数回到1;4、题目说最大深度4,要注意初始射线的深度为0。
    给一份测试数据。

AC 代码

#include <iostream>
#include <cmath>
using namespace std;
struct Point3 {double x, y, z;Point3(double x = 0., double y = 0., double z = 0.): x(x), y(y), z(z) {}void Normalize() {double l = sqrt(x*x + y*y + z*z); x /= l; y /= l; z /= l;}
};
typedef Point3 Vector3;Vector3 operator+ (const Vector3& A, const Vector3& B) {return Vector3(A.x + B.x, A.y + B.y, A.z + B.z);
}Vector3 operator- (const Vector3& A, const Vector3& B) {return Vector3(A.x - B.x, A.y - B.y, A.z - B.z);
}Vector3 operator* (const Vector3& A, double p) {return Vector3(A.x * p, A.y * p, A.z * p);
}double Dot(const Vector3& A, const Vector3& B) {return A.x * B.x + A.y * B.y + A.z * B.z;
}Vector3 Cross(const Vector3& A, const Vector3& B) {return Vector3(A.y * B.z - A.z * B.y, A.z * B.x - A.x * B.z, A.x * B.y - A.y * B.x);
}#define eps 1e-10
#define M 202
#define N 22
#define P 26
#define T 52
#define X 4
struct {Point3 v[P], n[T], c; int f[T][3], p, t; double l, r, m;} obj[N];
struct Ray {Point3 p; Vector3 v; double m;Ray(const Point3& p, const Vector3 v, double m): p(p), v(v), m(m) {this->v.Normalize();}
};
struct Ints {Point3 p; int i, j; Ints():i(-1){}};
Point3 img[M][M], p, t, g, c; Vector3 up; double a, f; int w, h, n, q;Ints get_ints(const Point3& p, const Vector3& v) {double t = -1.; Ints it;for (int i=0; i<n; ++i) for (int j=0; j<obj[i].t; ++j) {if (abs(Dot(v, obj[i].n[j])) < eps) continue;const Point3 &a = obj[i].v[obj[i].f[j][0]], &b = obj[i].v[obj[i].f[j][1]], &c = obj[i].v[obj[i].f[j][2]];const Vector3 &n = obj[i].n[j]; double q = Dot(n, a-p) / Dot(n, v);if (q < eps || (t>eps && q>=t)) continue;Point3 r = p + v*q; Vector3 c1 = Cross(b-a, r-a), c2 = Cross(c-b, r-b), c3 = Cross(a-c, r-c);if (Dot(c1, c2)>0. && Dot(c1, c3)>eps) t = q, it.p = r, it.i = i, it.j = j;}return it;
}double shade(const Point3& p, const Vector3& norm) {Vector3 v = g-p; double l = sqrt(v.x*v.x + v.y*v.y + v.z*v.z); v.x /= l; v.y /= l; v.z /= l;for (int i=0; i<n; ++i) for (int j=0; j<obj[i].t; ++j) {if (abs(Dot(v, obj[i].n[j])) < eps) continue;const Point3 &a = obj[i].v[obj[i].f[j][0]], &b = obj[i].v[obj[i].f[j][1]], &c = obj[i].v[obj[i].f[j][2]];const Vector3 &n = obj[i].n[j]; double q = Dot(n, a-p) / Dot(n, v);if (q < eps || q > l-eps) continue;Point3 r = p + v*q; Vector3 c1 = Cross(b-a, r-a), c2 = Cross(c-b, r-b), c3 = Cross(a-c, r-c);if (Dot(c1, c2)>eps && Dot(c1, c3)>eps) return 0.;}return abs(Dot(v, norm));
}Vector3 get_point_color(const Point3& p, const Vector3& v, const Vector3& oc) {return Vector3(oc.x*c.x, oc.y*c.y, oc.z*c.z) * (a + (1.-a)*shade(p, v));
}bool tir(const Vector3& v, const Vector3& n, double m, double q) {double c = Dot(v, n);return m*sqrt(1.-c*c) >= q*(1.-eps);
}Vector3 fl_vec(const Vector3& v, const Vector3& n) {return v - n * (2.*Dot(v, n));
}Vector3 fr_vec(const Vector3& v, const Vector3& n, double m,  double q) {double c = Dot(v, n), s = m*sqrt(1.-c*c)/q; Vector3 n1 = n*c, t = v-n1; n1.Normalize(); t.Normalize();return n1 * sqrt(1.-s*s) + t * s;
}Vector3 trace_ray(int d, const Ray& ray) {Vector3 c, l, r; Ints it = get_ints(ray.p, ray.v);if (it.i >= 0) {double fl = obj[it.i].l, fr = obj[it.i].r, m = ray.m==1. ? obj[it.i].m : 1.;c = get_point_color(it.p, obj[it.i].n[it.j], obj[it.i].c) * (1. - fl - fr);if (d == X) return c;if (tir(ray.v, obj[it.i].n[it.j], ray.m, obj[it.i].m)) fr = 0.;if (fl > 0.) l = trace_ray(d+1, Ray(it.p, fl_vec(ray.v, obj[it.i].n[it.j]), ray.m)) * fl;if (fr > 0.) r = trace_ray(d+1, Ray(it.p, fr_vec(ray.v, obj[it.i].n[it.j], ray.m, m), m)) * fr;}return c + l + r;
}void print(double c) {int v = int(c*255.+.5), a = v>>4, b = v&15;a < 10 ? cout << a : cout << char('a'+a-10); b < 10 ? cout << b : cout << char('a'+b-10);
}void print(const Vector3& c) {print(c.x); print(c.y); print(c.z); cout << ' ';
}void solve() {for (int i=0; i<n; ++i) {cin >> obj[i].p;for (int j=0; j<obj[i].p; ++j) cin >> obj[i].v[j].x >> obj[i].v[j].y >> obj[i].v[j].z;cin >> obj[i].t;for (int j=0; j<obj[i].t; ++j) {cin >> obj[i].f[j][0] >> obj[i].f[j][1] >> obj[i].f[j][2];const Point3 &a = obj[i].v[obj[i].f[j][0]], &b = obj[i].v[obj[i].f[j][1]], &c = obj[i].v[obj[i].f[j][2]];Vector3& v = obj[i].n[j] = Cross(b-a, c-a); v.Normalize();}cin >> obj[i].c.x >> obj[i].c.y >> obj[i].c.z >> obj[i].l >> obj[i].r >> obj[i].m;}cin >> g.x >> g.y >> g.z >> a >> c.x >> c.y >> c.z >> q;while (q--) {cin >> p.x >> p.y >> p.z >> t.x >> t.y >> t.z >> up.x >> up.y >> up.z >> f >> w >> h;Vector3 z = t-p; z.Normalize(); Vector3 x = Cross(z, up);double d = tan(f*M_PI/360.)/w, xi = d*(1-w), y0 = d*(h-1); d *= 2.;for (int i=0; i<w; ++i, xi+=d) {double yi = y0;for (int j=0; j<h; ++j, yi-=d) img[i][j] = trace_ray(0, Ray(p, x*xi + up*yi + z, 1.));}cout << w << ' ' << h << endl;for (int i=0; i<h; ++i) {for (int j=0; j<w; ++j) print(img[j][i]);cout << endl;}}
}int main() {while (cin >> n && n) solve();return 0;
}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.rhkb.cn/news/314609.html

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

ESP32开发

目录 1、简介 1.1 种类 1.2 特点 1.3 管脚功能 1.4 接线方式 1.5 工作模式 2、基础AT指令介绍 2.1 AT指令类型 2.2 基础指令及其描述 2.3 使用AT指令需要注意的事 3、AT指令分类和提示信息 3.1 选择是否保存到Flash的区别 3.2 提示信息 3.3 其他会保存到Flash的A…

界面组件DevExpress Blazor UI v23.2 - 支持.NET 8、全新的项目模版

DevExpress Blazor UI组件使用了C#为Blazor Server和Blazor WebAssembly创建高影响力的用户体验&#xff0c;这个UI自建库提供了一套全面的原生Blazor UI组件&#xff08;包括Pivot Grid、调度程序、图表、数据编辑器和报表等&#xff09;。 DevExpress Blazor控件目前已经升级…

RISC-V CVA6 在 Linux 下相关环境下载与安装

RISC-V CVA6 在 Linux 下相关环境下载与安装 所需环境与源码下载 CVA6 源码下载 首先&#xff0c;我们可以直接从 GitHub 一次性拉取所有源码&#xff1a; git clone --recursive https://github.com/openhwgroup/cva6.git如果这里遇到网络问题&#xff0c;拉取失败&#x…

阿里云企业邮箱API的使用方法?调用限制?

阿里云企业邮箱API性能如何优化&#xff1f;配置邮箱API的优势&#xff1f; 阿里云企业邮箱以其稳定、高效和安全的特点&#xff0c;受到了众多企业的青睐。而阿里云企业邮箱API的开放&#xff0c;更是为企业提供了更加灵活、便捷的管理和操作方式。下面&#xff0c;我AokSend…

Linux的学习之路:22、线程(2)

摘要 本章继续讲一下线程的东西 目录 摘要 一、抢票 二、加锁保护 三、死锁 1、死锁四个必要条件 2、避免死锁 四、同步 1、常见的线程安全的情况 2、常见不可重入的情况 3、常见可重入的情况 4、可重入与线程安全联系 5、可重入与线程安全区别 一、抢票 这里回…

启动 UE4编辑器报 加载 Plugin 失败

启动 UE4编辑器报 加载 Plugin 失败&#xff0c;报如下错误&#xff1a; Plugin ‘SteamVR’ failer to load because module ‘SteamVR’ could not be found. Please ensure the plugin is properly installed, otherwise consider disabling the plugin for this project. …

新时代凌迟:考研

我不喜欢上班&#xff0c;但我很欣赏老板的品味&#xff0c;因为咱们公司竟然还在订阅报纸&#xff0c;而且只有一份&#xff0c;《中国青年报》。 这份报纸我最喜欢看的是“冰点周刊”专栏&#xff0c;因为这个栏目能让读者相信&#xff1a;报纸远远可以超越一天的生命。 昨天…

前端框架编译器之模板编译

编译原理概述 编译原理&#xff1a;是计算机科学的一个分支&#xff0c;研究如何将 高级程序语言 转换为 计算机可执行的目标代码 的技术和理论。 高级程序语言&#xff1a;Python、Java、JavaScript、TypeScript、C、C、Go 等。计算机可执行的目标代码&#xff1a;机器码、汇…

JavaEE初阶——多线程(六)——线程池

T04BF &#x1f44b;专栏: 算法|JAVA|MySQL|C语言 &#x1faf5; 小比特 大梦想 此篇文章与大家分享多线程的第六篇文章,关于线程池 如果有不足的或者错误的请您指出! 目录 3.线程池3.1标准库的线程池3.2 标准库自己提供的几个工厂类3.3自己实现一个线程池完成大体框架接下来完…

OpenHarmony实战开发-使用SmartPerf-Host分析应用性能

简介 SmartPerf-Host是一款深入挖掘数据、细粒度展示数据的性能功耗调优工具&#xff0c;可采集CPU调度、频点、进程线程时间片、堆内存、帧率等数据&#xff0c;采集的数据通过泳道图清晰地呈现给开发者&#xff0c;同时通过GUI以可视化的方式进行分析。该工具当前为开发者提…

LayuiMini使用时候初始化模板修改(下载源码)

忘记加了 下载 地址 &#xff1a; layui-mini: layuimini&#xff0c;后台admin前端模板&#xff0c;基于 layui 编写的最简洁、易用的后台框架模板。只需提供一个接口就直接初始化整个框架&#xff0c;无需复杂操作。 LayuiMini使用时候初始化模板官网给的是&#xff1a; layu…

Fluent.Ribbon创建Office的RibbonWindow菜单

链接&#xff1a; Fluent.Ribbon文档 优势&#xff1a; 1. 可以创建类似Office办公软件的复杂窗口&#xff1b; 2. 可以应用自定义主题风格界面

航拍图像拼接 | 使用C++实现的无人机航拍图像拼接

项目应用场景 面向无人机航拍图像拼接场景&#xff0c;项目使用 C 实现&#xff0c;使用 harris 角点查找特征点 非极大值抑制&#xff0c;由于航拍图像没有严重的尺度旋转变化&#xff0c;使用了 berief 描述子&#xff0c;然后使用 RANSAC 求 H&#xff0c;最后进行图像拼接…

Tomcat架构设计精髓分析-Connector高内聚低耦合设计

优秀的模块化设计通常都会采用高内聚、低耦合 高内聚是指相关度比较高的功能要尽可能集中&#xff0c;不要分散。低耦合是指两个相关的模块要尽可能减少依赖的部分和降低依赖的程序&#xff0c;不要让两个模块产中强依赖。 Tomca连接器需要实现的功能: 监听网络端口 接受网络…

知识图谱嵌入领域的重要研究:编辑基于语言模型的知识图谱嵌入

今天&#xff0c;向大家介绍一篇在知识图谱嵌入领域具有重要意义的研究论文——Editing Language Model-based Knowledge Graph Embeddings。这项工作由浙江大学和腾讯公司的研究人员联合完成&#xff0c;为我们在动态更新知识图谱嵌入方面提供了新的视角和方法。 研究背景 在…

中电金信:向“新”而行——探索融合架构的项目管理在保险行业的应用

近年来&#xff0c;险企在政策推动、市场牵引、自身发展、新技术应用日趋成熟等内外部因素的驱动下&#xff0c;积极投身到数字化转型的浪潮中。在拜访各类保险客户和合作项目的过程中&#xff0c;我们发现不少险企在数字化转型中或多或少都面临着战略如何落地、技术如何承接和…

详解Qt中实现树状结构图

在Qt中&#xff0c;实现树状结构图通常采用QTreeWidget或QTreeView组件。这两个组件都允许我们创建具有层次结构的列表&#xff0c;但它们之间存在一些差异。QTreeWidget提供了更简单的API&#xff0c;适用于轻量级、快速开发的需求&#xff1b;而QTreeView则更为灵活和可定制&…

【jQuery】看一眼就会用的jquery库之续章!

jQuery&#xff08;js框架&#xff09; 17、操作节点 创建节点&#xff1a; 创建节点只需要将元素放在jQuery的工厂函数中//创建一个button按钮let $btn$("<input typebutton>");//创建一个列表项let $li$("<li>选项</li>");添加节点…

知识分享之cookie

http协议中的cookie&#xff0c;什么是cookie如何获取cookie 一、什么是Cookie Cookie&#xff08;曲奇&#xff0c;小甜饼的译名&#xff09;在互联网技术领域中&#xff0c;是指一种小型文本文件&#xff0c;它由网站服务器发送给用户的浏览器&#xff0c;并被浏览器存储在用…

鸿蒙官网学习3

鸿蒙官网学习3 每日小提示项目的模块类型跨设备预览调试阶段应用的替换方式有两种 打开老的demo工程报错UIAbility 每日小提示 项目的模块类型 moduleType分为三种&#xff0c;只有1&#xff0c;2的模块支持直接调试和运行 entryfeaturehar 跨设备预览 需要手动在config.j…