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;
else
    shade = 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;
}

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

三苯基膦以及副产物的处理方式

参考文献&#xff1a; Easy Removal of Triphenylphosphine Oxide from Reaction Mixtures by Precipitation with CaBr2 | Organic Process Research & Development (acs.org) Triphenylphosphine Oxide Removal from Reactions: The Role of Solvent and Temperature |…

分享一些实用的工具

1、amCharts5&#xff1a;模拟航线飞行/业务分布图/k线/数据分析/地图等 网址&#xff1a; JavaScript mapping library: amCharts 5https://www.amcharts.com/javascript-maps/ Demo地址&#xff1a;Chart Demos - amChartshttps://www.amcharts.com/demos/#maps 他分为amC…

[华为OD] 给航天器一侧加装长方形或正方形的太阳能板 100

给航天器一侧加装长方形或正方形的太阳能板&#xff08;图中的红色斜线区域&#xff09;&#xff0c;需要先安装两个支 柱&#xff08;图中的黑色竖条&#xff09;&#xff0c;再在支柱的中间部分固定太阳能板。但航天器不同位置的支柱长度 不同&#xff0c;太阳能板的安装面…

学习笔记-数据结构-线性表(2024-04-18)- 单向链表选择排序

试以单向链表为存储结构实现简单选择排序的算法。 实现递增排序&#xff0c;首先选择一个元素作为第一个比较值&#xff0c;遍历其他所有的元素&#xff0c;如果发现其他元素中有比它小的元素&#xff0c;则交换两个元素&#xff0c;这样每一趟都能找到符合要求的最小值 正经…

selenium 自动化测试课上实操指南1——百度搜索

1.环境准备 下面的所有资源可以从超星班级资料中下载&#xff0c;机房的同学在收到的文件夹中可以找到文件 非本校同学&#xff0c;免费加入学银在线课程&#xff0c;就可以在资料 根目录 > 02 课件新 > week09 web自动化测试02 里下载本次实操资料 1&#xff09;安…

K8S探针分享

一&#xff0c;探针介绍 1 探针类型 livenessProbe&#xff1a;存活探针&#xff0c;用于判断容器是不是健康&#xff1b;如果探测失败&#xff0c;Kubernetes就会重启容器。 readinessProbe&#xff1a;就绪探针&#xff0c;用于判断是否可以将容器加入到Service负载均衡池…

使用Tortoise 创建远程分支

1。首先创建本地分支branch1&#xff0c;右键tortoise git->创建分支&#xff0c;输入分支名称branch1&#xff0c;确定。 2。右键tortoise git->推送&#xff0c;按下图设置&#xff0c;确定&#xff0c;git会判断远程有没有分支branch1&#xff0c;如果没有会自动创建…

C++学习随笔(11)——vector

本章我们来学习一下vector&#xff01; 目录 1.vector的介绍及使用 1.1 vector的介绍 1.2 vector的使用 1.2.1 vector的定义 1.2.2 vector iterator 的使用 1.2.3 vector 空间增长问题 1.2.4 vector 增删查改 1.2.5 vector 迭代器失效问题。 1.vector的介绍及使用 1…

ANSYS WorkBench基础说明

1引入CAE产品设计流程 2有限元法简介 有限元法的基本概念:把一个原来是连续的物体划分为有限个单元&#xff0c;这些单元通过有限个节点相互连接&#xff0c;承受与实际载荷等效的节点载荷&#xff0c;根据力的平衡条件进行分析&#xff0c;并根据变形协调条件把这些单元重新组…

【Redis 开发】全局ID生成器

全局ID生成器 为了增加ID的安全性&#xff0c;我们可以不直接使用Redis自增的数值&#xff0c;而是拼接一些其他信息&#xff1a; ID的组成部分&#xff1a; 符号位:1bit,一直为0 时间戳:31bit&#xff0c;一秒为单位&#xff0c;可以使用69年 序列号:32bit&#xff0c;秒内…

idea 通过maven构建无法使用@SpringBootApplication

问题描述 SpringBootApplication标红&#xff0c;没有提示&#xff0c;无法启动springboot使用maven构建。通过idea的标准版本构建 原因 springboot构建启动依赖spring-boot-maven-plugin idea的标准版本没有指定构建版本&#xff0c;然后在springboot-parent里面没有指定默…

(已解决)PyQT5问题:控件和背景色同色,如何解决?

一、问题复现 难看到吐是不是 二、解决方案 拖动一个widget&#xff0c;设置其背景色&#xff0c;并且右键放到最后一层 三、解决结果 如上图所示 四、问题原因 直接在窗体右键设置背景色&#xff0c;由于Form是父类&#xff0c;即其下所有控件都与其同色&#xff0c;即使…

知识在人工智能中的核心作用:连接主义与符号主义的交融

知识在人工智能中的核心作用&#xff1a;连接主义与符号主义的交融 一、连接主义与深度学习的崛起二、感知与认知&#xff1a;AI的双眼与大脑三、知识的多元表示与处理四、符号主义与知识工程的实践五、知识在AI中的核心地位六、AI的具体应用案例分析七、知识图谱&#xff1a;认…

Linux多线程(一) 线程的创建与控制

一、线程概述 线程是轻量级的进程&#xff08;LWP&#xff1a;light weight process&#xff09;&#xff0c;在Linux环境下线程的本质仍是进程。在计算机上运行的程序是一组指令及指令参数的组合&#xff0c;指令按照既定的逻辑控制计算机运行。操作系统会以进程为单位&#…

element-ui et -i 编译默认主题报错:ReferenceError: primordials is not defined

报错信息如下 fs.js:40 } primordials;^ ReferenceError: primordials is not defined导致这个问题的原因&#xff1a;node和gulp版本冲突&#xff01;&#xff01; 我使用的是node 14版本 解决方法&#xff1a; 看了好几个帖子&#xff0c;都推荐使用node 11.15.0版本&am…

css中新型的边框设置属性border-block

border-block 是 CSS 中的一个属性&#xff0c;主要用于在样式表中一次性设置元素的逻辑块向边框的属性值。这个属性是简写属性&#xff0c;可以同时设置 border-block-width、border-block-style 和 border-block-color。其中&#xff0c;border-block-start 用于设置元素的开…

物联网应用技术综合实训室解决方案

一、背景 随着物联网技术的快速发展和广泛应用&#xff0c;物联网产业已经成为新的经济增长点&#xff0c;对于推动产业升级、提高社会信息化水平具有重要意义。因此&#xff0c;培养具备物联网技术应用能力的高素质人才成为了迫切需求。 传统的教育模式往往注重理论教学&…

新科技辅助器具赋能视障生活:让盲人出行融入日常

随着科技日新月异的发展&#xff0c;一款名为蝙蝠避障专为改善盲人日常生活的盲人日常生活辅助器具应运而生&#xff0c;它通过巧妙整合实时避障与拍照识别功能&#xff0c;成功改变了盲人朋友们的生活格局&#xff0c;为他们提供了更为便捷、高效的生活体验。 这款非同…

DevOps(十五)如何创建参数化的Jenkins Job

一、Jenkins参数化 在Jenkins中创建参数化的Job允许你在构建过程中动态输入一些值&#xff0c;这样可以让构建过程更加灵活和通用。以下是创建参数化Jenkins Job的步骤&#xff1a; 1、 创建新的Job 登录到Jenkins控制台。点击左侧的“新建任务”或“Create new jobs”。输入…

RocketMQ 部署

RocketMQ 部署 1、安装依赖&#xff08;Java&#xff09; [rootMicroservices ~]# mkdir -p /data/businessServer/ [rootMicroservices ~]# cd /data/businessServer/# 获取安装包&#xff08;下载较慢&#xff09; [rootMicroservices businessServer]# wget https://githu…