光线追踪12 - Defocus Blur(虚焦模糊)

    现在我们的最后一个特性是虚化模糊。注意,摄影师通常称之为景深,所以请确保在光线追踪的朋友中只使用虚化模糊这个术语。
真实相机具有虚化模糊是因为它们需要一个大孔(而不仅仅是针孔)来收集光线。一个大孔会导致所有物体失去焦点,但是如果我们在胶片/传感器前放置一块透镜,就会出现一定距离上所有物体都能够聚焦的情况。放置在该距离上的物体将呈现出清晰,而距离该距离越远的物体将呈现出线性模糊。您可以这样理解透镜:所有来自焦点距离上特定点的光线,当碰到透镜时将被折回到图像传感器上的一个点。
我们将摄像机中心与一切都处于完美聚焦状态的平面之间的距离称为焦距。请注意,焦距通常与焦距长度不同-焦距长度是摄像机中心与图像平面之间的距离。然而,在我们的模型中,这两个值将相等,因为我们将将像素网格放在焦平面上,距离摄像机中心焦距远。
在实体相机中,焦距由镜头与胶片/传感器之间的距离控制。这就是为什么当您改变对焦物体时,镜头会相对于相机移动的原因(您手机相机可能也会出现这种情况,但是传感器移动)。"光圈"是控制镜头有效大小的孔洞。对于真实相机,如果需要更多光线,可以使光圈更大,并且远离焦距的物体将产生更多的模糊效果。对于我们的虚拟相机,我们可以拥有完美的传感器并且从不需要更多光线,因此只在需要虚化模糊时使用光圈。
13.1 A Thin Lens Approximation
一台真实的相机配备有复杂的复合镜头。对于我们的代码,我们可以按顺序模拟传感器、透镜和光圈的工作方式。然后我们可以确定光线的传播路径,并在计算完成后翻转图像(图像在底片上是上下颠倒的)。而图形专家通常使用薄透镜近似:

Figure 21: Camera lens model

为了渲染相机外部的图像,我们不需要模拟相机内部的任何部分,这将增加不必要的复杂性。相反,我通常从一个无限薄的圆形“lens”(镜头)开始发射光线,将它们发送到焦平面上感兴趣的像素位置(focal_length away from the lens 距离镜头焦距的位置),在那个平面上,3D世界中的所有物体都是完全聚焦的。
实际上,我们通过将视口放置在这个平面上来实现这一点。将所有内容整合起来:
焦平面垂直于相机视角方向。
焦距是相机中心与焦平面之间的距离。
视口位于焦平面上,以相机视角方向向量为中心。
像素位置的网格位于视口内部(位于3D世界中)。
随机图像样本位置从当前像素位置周围的区域选择。
相机从镜头上的随机点通过当前图像样本位置发射光线。

Figure 22: Camera focus plane


13.2 Generating Sample Rays

    没有虚化模糊时,所有场景光线都起源于相机中心(或lookfrom)。为了实现虚化模糊,我们构建一个以相机中心为中心的圆盘。半径越大,虚化模糊越明显。你可以将我们原始的相机看作具有半径为零的虚化圆盘(完全没有模糊),因此所有光线起源于圆盘中心(lookfrom)。
那么,虚化圆盘应该有多大?由于此圆盘的大小控制了我们获得的虚化模糊程度,这应该是相机类的一个参数。我们可以将圆盘的半径作为相机参数,但模糊程度将取决于投影距离。一个稍微简单一点的参数是指定以视口中心为顶点、以相机中心为底(虚化圆盘)的圆锥体的角度。这样,在变化焦距的情况下,可以获得更一致的结果。
由于我们将从虚化圆盘中选择随机点,因此需要一个函数来实现:random_in_unit_disk()。这个函数使用与random_in_unit_sphere()相同类型的方法,只是在二维平面上使用。

inline vec3 unit_vector(vec3 u) {
return v / v.length();
}inline vec3 random_in_unit_disk() {
while (true) {auto p = vec3(random_double(-1,1), random_double(-1,1), 0);if (p.length_squared() < 1)return p;}
}

Listing 79: [vec3.h] Generate random point inside unit disk


现在让我们更新相机,使光线起源于虚化圆盘:

class camera {public:
double aspect_ratio      = 1.0;  // Ratio of image width over height
int    image_width       = 100;  // Rendered image width in pixel count
int    samples_per_pixel = 10;   // Count of random samples for each pixel
int    max_depth         = 10;   // Maximum number of ray bounces into scene
double vfov     = 90;              // Vertical view angle (field of view)
point3 lookfrom = point3(0,0,-1);  // Point camera is looking from
point3 lookat   = point3(0,0,0);   // Point camera is looking at
vec3   vup      = vec3(0,1,0);     // Camera-relative "up" directiondouble defocus_angle = 0;  // Variation angle of rays through each pixel
double focus_dist = 10;// Distance from camera lookfrom point to plane of perfect focus...
private:int    image_height;    // Rendered image height
point3 center;          // Camera centerpoint3 pixel00_loc;     // Location of pixel 0, 0vec3   pixel_delta_u;   // Offset to pixel to the rightvec3   pixel_delta_v;   // Offset to pixel belowvec3   u, v, w;         // Camera frame basis vectorsvec3   defocus_disk_u;  // Defocus disk horizontal radiusvec3   defocus_disk_v;  // Defocus disk vertical radiusvoid initialize() {image_height = static_cast<int>(image_width / aspect_ratio);image_height = (image_height < 1) ? 1 : image_height;center = lookfrom;// Determine viewport dimensions.auto focal_length = (lookfrom - lookat).length();auto theta = degrees_to_radians(vfov);auto h = tan(theta/2);auto viewport_height = 2 * h * focus_dist;auto viewport_width = viewport_height * (static_cast<double>(image_width)/image_height);        // Calculate the u,v,w unit basis vectors for the camera coordinate frame.w = unit_vector(lookfrom - lookat);u = unit_vector(cross(vup, w));v = cross(w, u);        // Calculate the vectors across the horizontal and down the vertical viewport edges.vec3 viewport_u = viewport_width * u;    // Vector across viewport horizontal edgevec3 viewport_v = viewport_height * -v;  // Vector down viewport vertical edge// Calculate the horizontal and vertical delta vectors to the next pixel.pixel_delta_u = viewport_u / image_width;pixel_delta_v = viewport_v / image_height;// Calculate the location of the upper left pixel.auto viewport_upper_left = center - (focus_dist * w) - viewport_u/2 - viewport_v/2;pixel00_loc = viewport_upper_left + 0.5 * (pixel_delta_u + pixel_delta_v);// Calculate the camera defocus disk basis vectors.auto defocus_radius = focus_dist * tan(degrees_to_radians(defocus_angle / 2));defocus_disk_u = u * defocus_radius;defocus_disk_v = v * defocus_radius;
}ray get_ray(int i, int j) const {
// Get a randomly-sampled camera ray for the pixel at location i,j, originating from
// the camera defocus disk.auto pixel_center = pixel00_loc + (i * pixel_delta_u) + (j * pixel_delta_v);auto pixel_sample = pixel_center + pixel_sample_square();auto ray_origin = (defocus_angle <= 0) ? center : defocus_disk_sample();auto ray_direction = pixel_sample - ray_origin;return ray(ray_origin, ray_direction);
}...
point3 defocus_disk_sample() const {// Returns a random point in the camera defocus disk.auto p = random_in_unit_disk();return center + (p[0] * defocus_disk_u) + (p[1] * defocus_disk_v);
}color ray_color(const ray& r, int depth, const hittable& world) const {...
};

Listing 80: [camera.h] Camera with adjustable depth-of-field (dof)

使用大光圈:

int main() {
...
camera cam;
cam.aspect_ratio      = 16.0 / 9.0;
cam.image_width       = 400;
cam.samples_per_pixel = 100;
cam.max_depth         = 50;
cam.vfov     = 20;
cam.lookfrom = point3(-2,2,1);
cam.lookat   = point3(0,0,-1);
cam.vup      = vec3(0,1,0);cam.defocus_angle = 10.0;cam.focus_dist    = 3.4;
cam.render(world);
}

得到:


Image 22: Spheres with depth-of-field

 

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

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

相关文章

javascript中字符串处理,常用的方法汇总

✨✨ 欢迎大家来到景天科技苑✨✨ &#x1f388;&#x1f388; 养成好习惯&#xff0c;先赞后看哦~&#x1f388;&#x1f388; 所属专栏&#xff1a;前端泛海 景天的主页&#xff1a;景天科技苑 文章目录 字符串对象的的相关方法1.获取字符串长度 length2.通过索引获取元素 …

HyperAI超神经 x DT新材料丨AI技术在化学与材料科学科研中应用研讨会开启报名

人工智能 (AI)是近年来发展最快的领域之一&#xff0c;其在科研领域显示出巨大潜力与争议。AI 可以通过自动化分析和处理数据&#xff0c;提高研究效率、减少人为误差、挖掘出更深层次的知识&#xff0c;现在的研究者可以大幅减少搜索和阅读文献所花费的精力&#xff0c;可以以…

如何在Win系统本地部署Jupyter Notbook交互笔记并结合内网穿透实现公网远程使用

文章目录 1.前言2.Jupyter Notebook的安装2.1 Jupyter Notebook下载安装2.2 Jupyter Notebook的配置2.3 Cpolar下载安装 3.Cpolar端口设置3.1 Cpolar云端设置3.2.Cpolar本地设置 4.公网访问测试5.结语 1.前言 在数据分析工作中&#xff0c;使用最多的无疑就是各种函数、图表、…

vulhub中ThinkPHP 多语言本地文件包含漏洞复现

ThinkPHP是一个在中国使用较多的PHP框架。在其6.0.13版本及以前&#xff0c;存在一处本地文件包含漏洞。当多语言特性被开启时&#xff0c;攻击者可以使用lang参数来包含任意PHP文件。 虽然只能包含本地PHP文件&#xff0c;但在开启了register_argc_argv且安装了pcel/pear的环…

PostgreSQL开发与实战(6.2)体系结构2

作者&#xff1a;太阳 二、逻辑架构 graph TD A[database] -->B(schema) B -->C[表] B -->D[视图] B -->E[触发器] C -->F[索引] tablespace 三、内存结构 Postgres内存结构主要分为 共享内存 与 本地内存 两部分。共享内存为所有的 background process提供内…

Intellij IDE 中复制多个服务

Intellij IDE 中复制多个服务 添加此选项 选择启动的端口和运行时的环境 之后就可以了。

Linux——进程间通信

目录 进程间通信介绍 什么是进程间通信 为什么要进行进程间通信 怎么做到进程间通信 管道 管道的原理 匿名管道 pipe函数 简单线程池 管道读写的规则 命名管道 创建一个管道文件 在代码中创建管道 在代码中删除管道 命名管道实现serve与client通信 system V共享…

3、Redis Cluster集群运维与核心原理剖析

Redis集群方案比较 哨兵模式 在redis3.0以前的版本要实现集群一般是借助哨兵sentinel工具来监控master节点的状态&#xff0c;如果master节点异常&#xff0c;则会做主从切换&#xff0c;将某一台slave作为master&#xff0c;哨兵的配置略微复杂&#xff0c;并且性能和高可用性…

如何理解Redis中的缓存雪崩,缓存穿透,缓存击穿?

目录 一、缓存雪崩 1.1 解决缓存雪崩问题 二、缓存穿透 2.1 解决缓存穿透 三、缓存击穿 3.1 解决缓存击穿 3.2 如何保证数据一致性问题&#xff1f; 一、缓存雪崩 缓存雪崩是指短时间内&#xff0c;有大量缓存同时过期&#xff0c;导致大量的请求直接查询数据库&#xf…

VS2022打包C#安装包(最新、最全)

开发c#的一个小工具到打包环境碰壁了&#xff0c;在网上找了很多资料耶踩了很多坑&#xff0c;耗时1hour才打包完毕&#xff0c;避免以后碰到类似的问题再次记录&#xff0c;自认为步骤比较全面&#xff0c;如果有帮助麻烦点个赞呗&#xff01;&#xff01;&#xff01; 一、Mi…

【异常处理】BadSqlGrammarException低级SQL语法异常

报错 org.springframework.jdbc.BadSqlGrammarException: ### Error querying database. Cause: java.sql.SQLSyntaxErrorException: You have an error in your SQL syntax; check the manual that corresponds to your MySQL server version for the right syntax to use …

极狐GitLab 如何设置 Markdown 中的图片大小

GitLab 是一个全球知名的一体化 DevOps 平台&#xff0c;很多人都通过私有化部署 GitLab 来进行源代码托管。极狐GitLab 是 GitLab 在中国的发行版&#xff0c;专门为中国程序员服务。可以一键式部署极狐GitLab。 使用极狐GitLab 进行代码托管或者 CI/CD&#xff0c;都避免不了…