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.mzph.cn/news/829644.shtml

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

相关文章

R语言高级数据管理

一&#xff0c;数学函数 绝对值函数abs(x) sqrt(x) 开平方根 不小于某个数的最小整数ceiling(x) 不大于某个数的最大整数floor(x) 四舍五入round(x) sin(x) cos(x) log(x) 二&#xff0c;统计函数 求平均值 > x<-c(2,3,4,5,6,7,8,9,10) > mean(x) 求和 &g…

RustGUI学习(iced)之小部件(二):如何使用滑动条部件

前言 本专栏是学习Rust的GUI库iced的合集&#xff0c;将介绍iced涉及的各个小部件分别介绍&#xff0c;最后会汇总为一个总的程序。 iced是RustGUI中比较强大的一个&#xff0c;目前处于发展中&#xff08;即版本可能会改变&#xff09;&#xff0c;本专栏基于版本0.12.1. 概述…

抓包理解协议

用的Wireshark 抓包 1.抓包网卡选择 - WLAN 无线网卡&#xff0c;其他是本地虚拟机的网卡 这里分别是开始捕获、停止捕获、重新捕获、网卡选择&#xff0c;下面是可以过滤选择 过滤tcp包 3次握手&#xff1a; source是源地址&#xff0c; destination是目标地址&#xff0c;in…

QT文件上传带进度条实例(含源码)

一、Qt上传知识点 QNetworkAccessManager QNetworkAccessManager 是 Qt 中用于执行网络操作的类,它提供了一种方便的方式来发送网络请求并处理响应。以下是关于 QNetworkAccessManager 的一些重要知识点: 发送网络请求:QNetworkAccessManager 可以通过不同的方法发送网络请…

CSS3(响应式布局)

#过渡# 属性连写&#xff1a; transition: width 2s linear 1s; //前一个时间用于表示过渡效果持续时间&#xff0c;后一个时间用于表示过渡效果的延迟。 #转换# #2D转换# 和 #3D转换# 注意&#xff1a;其中angle对应单位为&#xff1a;deg #圆角# #边框# …

使用Docker部署Jupyter Notebook并结合花生壳的内网穿透实现远程访问(详文)

一、前言 本文主要介绍如何利用宝塔面板中的Docker 3.9.3管理器,使用Docker本地部署Jupyter Notebook,并结合花生壳内网穿透工具实现任意浏览器公网远程访问Jupyter登录界面。 安装完成后在宝塔面板中图例 Jupyter Notebook是一个交互式笔记本,支持运行40多种编程语言。…

会声会影2024破解电脑版下载附带永久免费激活码

会声会影2024是一款功能强大的视频编辑软件&#xff0c;它凭借直观易用的界面、全面的编辑工具以及丰富的特效和转场效果&#xff0c;受到了广大视频编辑爱好者和专业人士的喜爱。以下是对会声会影2024的详细介绍&#xff1a; 会声会影最新一键安装包下载如下&#xff1a; ht…

sqlserver在执行维护计划备份时报错,重新设置维护任务,也同样出错,另一计划的异地备份又正常。

出错提示&#xff1a;已以用户 NT Service\SQLSERVERAGENT 的身份执行。 最大值(1)&#xff0c;因此导致失败。当错误数达到 MaximumErrorCount 中指定的数目时将发生这种情况。请更改 MaximumErrorCount 或纠正这些错误。 警告结束 DTExec: 已返回包执行 DTSER_FAILURE (1)。…

PotatoPie 4.0 实验教程(23) —— FPGA实现摄像头图像伽马(Gamma)变换

为什么要进行Gamma校正 图像的 gamma 校正是一种图像处理技术&#xff0c;用于调整图像的亮度和对比度&#xff0c;让显示设备显示的亮度和对比度更符合人眼的感知。Gamma 校正主要用于修正显示设备的非线性响应&#xff0c;以及在图像处理中进行色彩校正和图像增强。 以前&am…

《从零开始的Java世界》11网络编程

《从零开始的Java世界》系列主要讲解Javase部分&#xff0c;从最简单的程序设计到面向对象编程&#xff0c;再到异常处理、常用API的使用&#xff0c;最后到注解、反射&#xff0c;涵盖Java基础所需的所有知识点。学习者应该从学会如何使用&#xff0c;到知道其实现原理全方位式…

LangChain入门:24.通过Baby AGI实现自动生成和执行任务

随着 ChatGPT 的崭露头角,我们迎来了一种新型的代理——Autonomous Agents(自治代理或自主代理)。 这些代理的设计初衷就是能够独立地执行任务,并持续地追求长期目标。 在 LangChain 的代理、工具和记忆这些组件的支持下,它们能够在无需外部干预的情况下自主运行,这在真…

冯唐成事心法笔记 —— 知己

系列文章目录 冯唐成事心法笔记 —— 知己 冯唐成事心法笔记 —— 知人 冯唐成事心法笔记 —— 知世 冯唐成事心法笔记 —— 知智慧 文章目录 系列文章目录卷首语 管理是一生的日常&#xff0c;成事是一生的修行PART 1 知己 用好自己的天赋如何管理自我第一&#xff0c;如何…

C++学习随笔(12)—— list

本章我们来了解一下list 目录 1. list的介绍及使用 1.1 list的介绍 1.2 list的使用 1.2.1 list的构造 1.2.2 list iterator的使用 1.2.3 list capacity 1.2.4 list element access 1.2.5 list modifiers 1.2.6 list的迭代器失效 1. list的介绍及使用 1.1 list的介绍…

【Unity常用插件】Dotween插件API详解【一】

&#x1f468;‍&#x1f4bb;个人主页&#xff1a;元宇宙-秩沅 &#x1f468;‍&#x1f4bb; hallo 欢迎 点赞&#x1f44d; 收藏⭐ 留言&#x1f4dd; 加关注✅! &#x1f468;‍&#x1f4bb; 本文由 秩沅 原创 &#x1f468;‍&#x1f4bb; 收录于专栏&#xff1a;UI_…

LabVIEW飞机机电系统综合测试平台

LabVIEW飞机机电系统综合测试平台 在现代航空领域&#xff0c;机电系统的准确性与可靠性对飞行安全至关重要。针对飞机机电管理计算机&#xff08;UMC&#xff09;复杂度增加、测试覆盖率低、效率不高等问题&#xff0c;开发了一套基于LabVIEW的机电系统综合测试平台。平台通过…

内网穿透及公网解析说明

内网穿透释义&#xff1a; 自己在本地搭建服务器时&#xff0c;本地网络有多种环境&#xff0c;如没有公网IP、没有路由映射权限、网络被NAT转发等情况。在需要外网访问内网服务器资源时&#xff0c;就需要用到内网穿透。内网穿透&#xff0c;即内网映射&#xff0c;内网IP地址…

PotatoPie 4.0 实验教程(21) —— FPGA实现摄像头图像二值化(RGB2Gray2Bin)

PotatoPie 4.0开发板教程目录&#xff08;2024/04/21&#xff09; 为什么要进行图像的二值化&#xff1f; 当我们处理图像时&#xff0c;常常需要将其转换为二值图像。这是因为在很多应用中&#xff0c;我们只对图像中的某些特定部分感兴趣&#xff0c;而不需要考虑所有像素的…

如何进行域名解析?如何清理DNS缓存?(附源码)

目录 1、什么是域名&#xff1f; 2、为什么使用域名&#xff1f; 3、域名解析的完整流程 4、调用gethostbyname系统接口将域名解析成IP地址 5、为什么需要清理系统DNS缓存&#xff1f; 6、使用cmd命令清理DNS缓存 7、通过代码去清除系统DNS缓存 C软件异常排查从入门到精…

PeLK: 大卷积核强势回归,高达101 × 101,提出了外围卷积

paper&#xff1a;https://arxiv.org/pdf/2403.07589 code&#xff1a;暂无 目录 0. 摘要 1. 引言 2. 相关工作 2.1. Large Kernel Convolutional Networks 2.2. Peripheral Vision for Machine Learning 3. 密集卷积优于条纹卷积 4. 参数高效的大核卷积神经网络 4.1. …

粒子群算法与优化储能策略python实践

粒子群优化算法&#xff08;Particle Swarm Optimization&#xff0c;简称PSO&#xff09;, 是1995年J. Kennedy博士和R. C. Eberhart博士一起提出的&#xff0c;它是源于对鸟群捕食行为的研究。粒子群优化算法的基本核心是利用群体中的个体对信息的共享从而使得整个群体的运动…