光线追踪7 - 抗锯齿(Antialiasing)

    目前为止,如果你放大渲染出的图像,可能会注意到图像边缘的明显“阶梯状”效果。这种阶梯效果通常被称为“走样”或“锯齿”。当真实相机拍摄图片时,边缘通常没有锯齿,因为边缘像素是一些前景和一些背景的混合。请考虑,与我们渲染的图像不同,真实世界的图像是连续的。换句话说,世界(及其真实图像)具有无限的分辨率。我们可以通过对每个像素进行多次采样来获得相同的效果。

通过每个像素中心传递单一光线,我们正在进行常见的点采样。点采样的问题可以通过远处渲染一个小的棋盘格来说明。如果该棋盘由一个8×8的黑白格子组成,但只有四条光线射到它上面,那么这四条光线可能只会与白色格子相交,或只与黑色格子相交,或者是一些奇怪的组合。在真实世界中,当我们用眼睛远处观察一个棋盘时,我们会感知到它是灰色的,而不是黑白的尖点。那是因为我们的眼睛自然而然地完成了我们希望光线追踪完成的任务:将落在所渲染图像的特定(离散的)区域上的(连续函数的)光线进行积分。

显然,通过多次对像素中心进行相同光线的重新采样,并不能带来任何好处——我们每次都会得到相同的结果。相反,我们想要采样像素周围的光线,然后对这些采样进行积分以近似真正的连续结果。那么,我们如何积分像素周围的光线呢?

我们将采用最简单的模型:采样以像素为中心、向四个相邻像素各自延伸一半距离的正方形区域。这并不是最优的方法,但它是最直接的方法。(请参阅 A Pixel is Not a Little Square  以深入了解此主题。)

Figure 8: Pixel samples
 

Some Random Number Utilities

    我们需要一个能返回真正随机数的随机数生成器。这个函数应该返回一个经典的随机数,按照惯例应在0≤n<1的范围内。其中“小于”符号在1之前很重要,因为有时我们会利用这一点。

实现这个的一个简单方法是使用在 <cstdlib> 中找到的 rand() 函数,它返回0到RAND_MAX 之间的随机整数。因此,我们可以使用以下代码片段将其转换为所需的真正随机数,并将其添加到 rtweekend.h 中:

#include <cmath>
#include <cstdlib>
#include <limits>
#include <memory>
...
// Utility Functions
inline double degrees_to_radians(double degrees) {
return degrees * pi / 180.0;
}
inline double random_double() {
// Returns a random real in [0,1).
return rand() / (RAND_MAX + 1.0);
}inline double random_double(double min, double max) {
// Returns a random real in [min,max).
return min + (max-min)*random_double();
}

 Listing 36: [rtweekend.h] random_double() functions

传统上,C++ 并没有一个标准的随机数生成器,但是较新版本的 C++ 通过 <random> 头文件解决了这个问题(尽管根据一些专家的说法,它并不完美)。如果你想使用它,你可以按照以下方式获取满足我们需求的随机数:

#include <random>
inline double random_double() {
static std::uniform_real_distribution<double> distribution(0.0, 1.0);
static std::mt19937 generator;
return distribution(generator);
}

Listing 37: [rtweekend.h] random_double(), alternate implemenation


8.2 Generating Pixels with Multiple Samples

对于由多个样本组成的单个像素,我们将从像素周围的区域选择样本,并将得到的光(颜色)值进行平均。

首先,我们将更新write_color()函数以考虑我们使用的样本数:我们需要找到所有取样的平均值。为此,我们将在每次迭代中添加完整的颜色,然后在最后进行一次除法(除以样本数),然后再写入颜色。为了确保最终结果的颜色分量保持在正确的[0,1]范围内,我们将添加并使用一个小的辅助函数:interval::clamp(x),用于限制值x在指定范围内。​​​​​​

class interval {public:...bool surrounds(double x) const {return min < x && x < max;}
double clamp(double x) const {if (x < min) return min;if (x > max) return max;return x;}
...
};

Listing 38: [interval.h] The interval::clamp() utility function


以下是更新后的write_color()函数,它接受像素的所有光的总和和涉及的样本数量:

void write_color(std::ostream &out, color pixel_color, int samples_per_pixel) {    
auto r = pixel_color.x();
auto g = pixel_color.y();
auto b = pixel_color.z();
// Divide the color by the number of samples.
auto scale = 1.0 / samples_per_pixel;
r *= scale;
g *= scale;
b *= scale;
// Write the translated [0,255] value of each color component.
static const interval intensity(0.000, 0.999);
out << static_cast<int>(256 * intensity.clamp(r))<<' '
<< static_cast<int>(256 * intensity.clamp(g))<<' '
<<static_cast<int>(256 * intensity.clamp(b))<<'\n';
}

Listing 39: [color.h] The multi-sample write_color() function

现在让我们更新相机类,定义并使用一个新的camera::get_ray(i,j)函数,该函数将为每个像素生成不同的样本。该函数将使用一个新的辅助函数pixel_sample_square(),该函数生成一个随机样本点,在以原点为中心的单位正方形内。然后,我们将这个随机样本从理想的正方形转换回当前正在采样的特定像素。

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 pixelvoid render(const hittable& world) {initialize();std::cout << "P3\n" << image_width << ' ' << image_height << "\n255\n";for (int j = 0; j < image_height; ++j) {std::clog << "\rScanlines remaining: " << (image_height - j) << ' '<< std::flush;for (int i = 0; i < image_width; ++i) {color pixel_color(0,0,0);for (int sample = 0; sample < samples_per_pixel; ++sample){ray r = get_ray(i, j);pixel_color += ray_color(r, world);}write_color(std::cout, pixel_color, samples_per_pixel );}}std::clog << "\rDone.                 \n";}
...
private:
...
void initialize() {...
}ray get_ray(int i, int j) const {// Get a randomly sampled camera ray for the pixel at location i,j.auto pixel_center = pixel00_loc + (i * pixel_delta_u) + (j * pixel_delta_v);auto pixel_sample = pixel_center + pixel_sample_square();auto ray_origin = center;auto ray_direction = pixel_sample - ray_origin;return ray(ray_origin, ray_direction);}vec3 pixel_sample_square() const {// Returns a random point in the square surrounding a pixel at the origin.auto px = -0.5 + random_double();auto py = -0.5 + random_double();return (px * pixel_delta_u) + (py * pixel_delta_v);}...
};#endif

Listing 40: [camera.h] Camera with samples-per-pixel parameter


 

(除了上面的新的pixel_sample_square()函数之外,在Github源代码中还可以找到pixel_sample_disk()函数。这是为了在非正方形像素上进行实验而包含的,但在本书中我们不会使用它。pixel_sample_disk()依赖于稍后定义的random_in_unit_disk()函数。)

更新Main函数以设置新的相机参数。

int main() {
...
camera cam;cam.aspect_ratio = 16.0 / 9.0;
cam.image_width = 400;
cam.samples_per_pixel = 100;
cam.render(world);
}

Listing 41: [main.cc] Setting the new samples-per-pixel parameter



放大图像后,我们可以看到边缘像素的差异。

     Image 6:抗锯齿前后对比。

 

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

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

相关文章

5. 链接和加载(linker and loader)

链接和加载(linker and loader)&#xff1a; linker即链接器&#xff0c;它负责将多个.c编译生成的.o文件&#xff0c;链接成一个可执行文件或者是库文件&#xff1b; loader即加载器&#xff0c;它原本的功能很单一只是将可执行文件的段拷贝到编译确定的内存地址即可&#x…

英福康INFICON残余气体RGA General Chinese中文培训PPT课件

英福康INFICON残余气体RGA General Chinese中文培训PPT课件

【树上倍增】【割点】 【换根法】3067. 在带权树网络中统计可连接服务器对数目

作者推荐 视频算法专题 本文涉及知识点 树上倍增 树 图论 并集查找 换根法 深度优先 割点 LeetCode3067. 在带权树网络中统计可连接服务器对数目 给你一棵无根带权树&#xff0c;树中总共有 n 个节点&#xff0c;分别表示 n 个服务器&#xff0c;服务器从 0 到 n - 1 编号…

Java | 在消息对话框中显示文本

首先需要导入JOptionPane类&#xff0c;JOptionPane类属于Swing组件中的一种&#xff0c;其导入方式如下&#xff1a; import javax.swing.JOptionPane;可以使用JOptionPane的showMessageDialog方法显示消息文本。 参数格式&#xff1a; JOptionPane.showMessageDialog(paren…

【C语言】指针详细解读2

1.const 修饰指针 1.1 const修饰变量 变量是可以修改的&#xff0c;如果把变量的地址交给⼀个指针变量&#xff0c;通过指针变量的也可以修改这个变量。 但是如果我们希望⼀个变量加上⼀些限制&#xff0c;不能被修改&#xff0c;怎么做呢&#xff1f;这就是const的作⽤。 …

RK3568平台开发系列讲解(基础篇)注册字符设备

🚀返回专栏总目录 文章目录 一、字符设备初始化二、字符设备的注册和注销三、实验代码沉淀、分享、成长,让自己和他人都能有所收获!😄 注册字符设备可以分为两个步骤: 字符设备初始化字符设备的添加一、字符设备初始化 字符设备初始化所用到的函数为 cdev_init(…),在对…

解决QMYSQL driver not loaded问题

前言 之前都是在Qt5.51上开发&#xff0c;连接mysql数据库一直没有问题&#xff0c;换到5.15.2后一直报错 一查才发现\5.15.2\msvc2019_64\plugins\sqldrivers目录下没有qsqlmysql了&#xff0c;5.5.1是有的&#xff0c;5.15.2是要自己编译的。。。 下载源码 安装qt的时候没…

查看kafka消息消费堆积情况

查看主题命令 展示topic列表 ./kafka-topics.sh --list --zookeeper zookeeper_ip:2181描述topic ./kafka-topics.sh --describe --zookeeper zookeeper_ip:2181 --topic topic_name查看topic某分区偏移量最大&#xff08;小&#xff09;值 ./kafka-run-class.sh kafka.too…

旧物回收小程序开发:环保与科技的创新结合

随着科技的飞速发展&#xff0c;我们的日常生活越来越离不开手机应用程序。而在环保日益成为社会焦点的今天&#xff0c;如何将科技与环保相结合&#xff0c;成为了一个值得深思的问题。今天&#xff0c;我们将探讨旧物回收小程序的开发&#xff0c;它如何助力环保&#xff0c;…

【重要公告】BSV区块链上线TypeScript SDK,未来将支持更多开发语言

​​发表时间&#xff1a;2024年2月21日 BSV区块链协会宣布上线JavaScript和TypeScript SDK&#xff08;即“标准开发工具包”&#xff09;。TypeScript SDK旨在为开发者提供新版统一核心代码库&#xff0c;以便利开发者在BSV区块链上开发能够任意扩容的应用程序。新上线的SDK替…

掌握Python操作Word:从基础到高级全覆盖

掌握Python操作Word&#xff1a;从基础到高级全覆盖 引言Python操作Word的基础文档的创建与打开文档的基本操作 创建和打开Word文档创建新的Word文档打开现有文档读取文档内容修改现有文档 编辑文档内容添加和编辑文本设置格式插入标题 处理文档结构操作段落列表的处理表格的操…

深入理解 Vuex:从基础到应用场景

前言 在之前的文章中&#xff0c;我们已经对 Vue.js 有了一定的了解。今天我们要对Vue官方的状态共享管理器Vuex进行详细讲解&#xff0c;将其基本吃透&#xff0c;目标是面对大多数业务需求&#xff1b; 一、介绍 Vuex 是一个专为 Vue.js 应用程序开发的状态管理模式。它采用…

『操作系统OS笔记』MAC(m1芯片)电脑安装FFmpeg

MAC(m1芯片)电脑安装FFmpeg mac电脑安装ffmpeg两种方法 文章目录 1. brew安装FFmpeg2. 官网下载FFmpeg压缩包3. 使用FFmpeg将音频和视频合并 1. brew安装FFmpeg brew install ffmpeg # 需要等比较久的时间&#xff0c;安装很多东西&#xff0c;安装过程中如果遇到报错对应解决…

MES数据采集设备

在智能制造日益盛行的今天&#xff0c;MES&#xff08;制造执行系统&#xff09;作为连接计划与生产现场的关键环节&#xff0c;其重要性不言而喻。而MES数据采集设备则是MES系统的核心组件&#xff0c;负责实时、准确地获取生产现场的各种数据&#xff0c;为企业的生产决策提供…

信息系统项目管理师--范围管理

项⽬范围管理 产品范围&#xff1a;指某项产品、服务或成果所具有的特征和功能。产品范围的完成情况是根据产品需求来衡量的。“需求”是指根据特定协议或其他强制性规范&#xff0c;产品、服务或成果 必须具备的条件或能⼒。 项⽬范围&#xff1a;包括产品范围&#xff0c;是为…

探索HTTP/2

文章目录 http/1.1http/2疑惑 探索1. 连接前言2. 帧结构2.1 帧类型 Type 3. 帧详情3.1 SETTINGS 帧3.2 WINDOW_UPDATE 帧3.3 PRIORITY 帧3.4 HEADERS 帧3.5 DATA 帧3.6 PING3.7 GOAWAY 帧3.8 RST_STREAM 帧3.9 PUSH_PROMISE 帧3.10 CONTINUATION 帧 你对http2了解多少&#xff…

基于 EfficientNetV2 实现判别MNIST 手写模型分类

pytorch深度学习项目实战100例 的学习记录 我的环境&#xff1a; 白票大王&#xff1a; google colab 用其他的话&#xff0c;其实实现也行&#xff0c;但是让小白来重环境来开始安装的话&#xff0c;浪费时间 论文速读 EfficientNetV2是由 Google Research&#xff0c;Br…

华为配置智能升级功能升级设备示例

配置智能升级功能升级设备示例 组网图形 图1 配置智能升级功能组网图 背景信息组网需求配置思路前提条件操作步骤操作结果 背景信息 为了方便用户及时了解设备主流运行版本&#xff0c;快速完成升级修复&#xff0c;华为设备支持自动下载、自助升级功能。用户在设备Web网管…

【HTML】HTML基础7.2(有序列表)

目录 标签 效果 注意 标签 <ol> <li>列表内容</li> <li>列表内容</li> <li>列表内容</li> <li>列表内容</li> 。。。。。。 </ol> 效果 代码 <ol><li>银河护卫队 10000000000</li><l…

C++ LRU缓存

题目&#xff1a; //构建双向链表的节点结构&#xff08;要有两个构造函数&#xff09; struct Node{int key, val;Node* pre;Node* next;Node():key(0), val(0), pre(nullptr), next(nullptr) {}Node(int _key, int _val): key(_key), val(_val), pre(nullptr), next(nullpt…