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

news2025/1/11 21:40:20

    现在我们的最后一个特性是虚化模糊。注意,摄影师通常称之为景深,所以请确保在光线追踪的朋友中只使用虚化模糊这个术语。
真实相机具有虚化模糊是因为它们需要一个大孔(而不仅仅是针孔)来收集光线。一个大孔会导致所有物体失去焦点,但是如果我们在胶片/传感器前放置一块透镜,就会出现一定距离上所有物体都能够聚焦的情况。放置在该距离上的物体将呈现出清晰,而距离该距离越远的物体将呈现出线性模糊。您可以这样理解透镜:所有来自焦点距离上特定点的光线,当碰到透镜时将被折回到图像传感器上的一个点。
我们将摄像机中心与一切都处于完美聚焦状态的平面之间的距离称为焦距。请注意,焦距通常与焦距长度不同-焦距长度是摄像机中心与图像平面之间的距离。然而,在我们的模型中,这两个值将相等,因为我们将将像素网格放在焦平面上,距离摄像机中心焦距远。
在实体相机中,焦距由镜头与胶片/传感器之间的距离控制。这就是为什么当您改变对焦物体时,镜头会相对于相机移动的原因(您手机相机可能也会出现这种情况,但是传感器移动)。"光圈"是控制镜头有效大小的孔洞。对于真实相机,如果需要更多光线,可以使光圈更大,并且远离焦距的物体将产生更多的模糊效果。对于我们的虚拟相机,我们可以拥有完美的传感器并且从不需要更多光线,因此只在需要虚化模糊时使用光圈。
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" direction

double 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 center
    point3 pixel00_loc;     // Location of pixel 0, 0
    vec3   pixel_delta_u;   // Offset to pixel to the right
    vec3   pixel_delta_v;   // Offset to pixel below
    vec3   u, v, w;         // Camera frame basis vectors
    vec3   defocus_disk_u;  // Defocus disk horizontal radius
    vec3   defocus_disk_v;  // Defocus disk vertical radius
    void 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 edge
        vec3 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.coloradmin.cn/o/1499003.html

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

相关文章

[HackMyVM]Quick 2

kali:192.168.56.104 主机发现 arp-scan -l # arp-scan -l Interface: eth0, type: EN10MB, MAC: 00:0c:29:d2:e0:49, IPv4: 192.168.56.104 Starting arp-scan 1.10.0 with 256 hosts (https://github.com/royhills/arp-scan) 192.168.56.1 0a:00:27:00:00:05 (Un…

MySQl基础入门⑥

上一章知识内容 1.数据类型的属性 2.MySql的约束 mysql的约束时指对数据表中数据的一种约束行为&#xff0c;约束主要完成对数据的检验&#xff0c;如果有互相依赖数据&#xff0c;保证该数据不被删除。它能够帮助数据库管理员更好地管理数据库&#xff0c;并且能够确保数据库…

算法打卡day11|栈与队列篇03|Leetcode 239. 滑动窗口最大值、347.前 K 个高频元素

小顶堆和大顶堆 小顶堆&#xff08;Min Heap&#xff09;和大顶堆&#xff08;Max Heap&#xff09;是两种特殊的完全二叉树&#xff0c;它们遵循特定的堆属性&#xff0c;即父节点的值总是小于或等于&#xff08;小顶堆&#xff09;或者大于或等于&#xff08;大顶堆&#xf…

微信小程序开发系列(二十二)·wxml语法·双向数据绑定model:的用法

目录 1. 单向数据绑定 2. 双向数据绑定 3. 代码 在 WXML 中&#xff0c;普通属性的绑定是单向的&#xff0c;例如&#xff1a;<input value"((value))"/> 如果希望用户输入数据的同时改变 data 中的数据&#xff0c;可以借助简易双向绑定机制。在对应属性…

文心一言 VS 讯飞星火 VS chatgpt (210)-- 算法导论16.1 1题

一、根据递归式(16.2)为活动选择问题设计一个动态规划算法。算法应该按前文定义计算最大兼容活动集的大小 c[i,j]并生成最大集本身。假定输入的活动已按公式(16.1)排好序。比较你的算法和GREEDY-ACTIVITY-SELECTOR的运行时间。如何要写代码&#xff0c;请用go语言。 文心一言&…

阿里云和腾讯云区别价格表,云服务器费用对比2024年最新

2024年阿里云服务器和腾讯云服务器价格战已经打响&#xff0c;阿里云服务器优惠61元一年起&#xff0c;腾讯云服务器61元一年&#xff0c;2核2G3M、2核4G、4核8G、4核16G、8核16G、16核32G、16核64G等配置价格对比&#xff0c;阿腾云atengyun.com整理阿里云和腾讯云服务器详细配…

利用tree命令自动保存文件层级结构

tree命令的使用 为了将上图左侧的文件目录&#xff0c;生成上图右侧中的文件夹结构列表&#xff0c;保存在txt中&#xff0c;使用了如下cmd命令&#xff1a; C:\armadillo-12.8.0>tree .>list.txt以上tree命令分为3部分&#xff1a; tree 命令. 在当前目录>list.tx…

ChatGPT:人工智能的革命与未来

引言 随着人工智能技术的飞速发展&#xff0c;ChatGPT作为OpenAI推出的一款语言模型&#xff0c;已经引起了广泛的关注和讨论。它不仅改变了我们与机器交流的方式&#xff0c;还为众多行业的发展带来了革命性的影响。本文将深入探讨ChatGPT的技术原理、应用场景以及它对未来的…

一些硬件知识(六)

防反接设计&#xff1a; 同步电路和异步电路的区别: 同步电路:存储电路中所有触发器的时钟输入端都接同一个时钟脉冲源&#xff0c;因而所有触发器的状态的变化都与所加的时钟脉冲信号同步。 异步电路:电路没有统一的时钟&#xff0c;有些触发器的时钟输入端与时钟脉冲源相连…

【HarmonyOS】ArkTS-箭头函数

箭头函数 箭头函数是 比普通函数 更简洁 的一种函数写法 () > {}() > {// 函数体 }let 函数名 () > {// 函数体 }let 函数名 () > {// 函数体 } 函数名(实参1, 实参2)let 函数名 (形参1: 类型, 形参2: 类型) > {// 函数体 } 函数名(实参1, 实参2)let 函数名 …

【嵌入式】嵌入式系统稳定性建设:静态代码扫描的稳定性提升术

1. 概述 在嵌入式系统开发过程中&#xff0c;代码的稳定性和可靠性至关重要。静态代码扫描工具作为一种自动化的代码质量检查手段&#xff0c;能够帮助开发者在编译前发现潜在的缺陷和错误&#xff0c;从而增强系统的稳定性。本文将介绍如何在嵌入式C/C开发中使用静态代码扫描…

嵌入式学习第二十五天!(网络的概念、UDP编程)

网络&#xff1a; 可以用来&#xff1a;数据传输、数据共享 1. 网络协议模型&#xff1a; 1. OSI协议模型&#xff1a; 应用层实际收发的数据表示层发送的数据是否加密会话层是否建立会话连接传输层数据传输的方式&#xff08;数据包&#xff0c;流式&#xff09;网络层数据的…

Day22:安全开发-PHP应用留言板功能超全局变量数据库操作第三方插件引用

目录 开发环境 数据导入-mysql架构&库表列 数据库操作-mysqli函数&增删改查 数据接收输出-html混编&超全局变量 第三方插件引用-js传参&函数对象调用 完整源码 思维导图 PHP知识点&#xff1a; 功能&#xff1a;新闻列表&#xff0c;会员中心&#xff0…

Python爬虫——scrapy-3

目录 免责声明 任务 文件简介 爬取当当网内容单管道 pipelines.py items.py setting dang.py 当当网多管道下载图片 pipelines.py settings 当当网多页下载 dang.py pielines.py settings items.py 总结 免责声明 该文章用于学习&#xff0c;无任何商业用途 …

深度学习-2.3损失函数

文章目录 损失函数深度学习优化思想回归&#xff1a;误差平方和SSE二分类交叉熵损失函数1. 极大似然函数估计求解二分类交叉熵函数2.用tensor实现二分类交叉熵损失 多分类交叉熵损失函数1.由二分类推广到多分类2.用PyTorch实现多分类交叉熵损失 损失函数 在之前的文章中&#…

OpenAI劲敌吹新风! Claude 3正式发布,Claude3使用指南

Claude 3是什么&#xff1f; 是Anthropic 实验室近期推出的 Claude 3 大规模语言模型&#xff08;Large Language Model&#xff0c;LLM&#xff09;系列&#xff0c;代表了人工智能技术的一个显著飞跃。 该系列包括三个不同定位的子模型&#xff1a;Claude 3 Haiku、Claude 3…

Chapter20-Ideal gases-CIE课本要点摘录、总结(编辑中)

20.1 Particles of a gas Brownian motion Fast modules 速率的数值大概了解下&#xff1a; average speed of the molecules:400m/s speed of sound:approximately 330m/s at STP&#xff08;standard temperature and pressure&#xff09; Standard Temperature and Pres…

如何使用WinSCP结合Cpolar实现公网远程访问内网Linux服务器

文章目录 1. 简介2. 软件下载安装&#xff1a;3. SSH链接服务器4. WinSCP使用公网TCP地址链接本地服务器5. WinSCP使用固定公网TCP地址访问服务器 1. 简介 ​ Winscp是一个支持SSH(Secure SHell)的可视化SCP(Secure Copy)文件传输软件&#xff0c;它的主要功能是在本地与远程计…

linux上安装fastdfs及配置

一、基础环境准备 1、所需软件 名称说明libfastcommonfastdfs分离出的一些公用函数包fastdfsfastdas软件包fastdfs-nginx-modulefastdfst和nginx的关联模块nginxnginxl软件包 2、编辑环境 安装一些基础的支持环境 yum install git gccc gcc-c make automake autoconf libto…

图遍历算法

图的遍历算法有两种&#xff1a;深度优先遍历、广度优先遍历算法。 深度优先遍历算法就是从起始结点开始&#xff0c;只要有相邻且未被访问的结点就要直接进行访问&#xff0c;直到最后不能向下遍历为止&#xff0c;再回溯寻找下一个策略。 广度优先遍历算法&#xff0c;就是从…