Computer-Graphic06-纹理采样
顶点插值属性不仅仅是颜色值,而是某种可以进行纹理映射的纹理坐标,那么什么是纹理映射?纹理映射一个很好的类比就是包装一份礼物,我有一些彩纸,上面绘制了一些有趣的图案,还有一个平平无奇的立方体,只是一个扁平的盒子,我想把这个有趣的图案放在盒子上,该怎么做呢?把纸包裹在盒子上就行了,这本质上就是纹理映射,将二维图像包裹在三维表面上的方法。
顶点插值属性不仅仅是颜色值,而是某种可以进行纹理映射的纹理坐标,那么什么是纹理映射?纹理映射一个很好的类比就是包装一份礼物,我有一些彩纸,上面绘制了一些有趣的图案,还有一个平平无奇的立方体,只是一个扁平的盒子,我想把这个有趣的图案放在盒子上,该怎么做呢?把纸包裹在盒子上就行了,这本质上就是纹理映射,将二维图像包裹在三维表面上的方法。
mvp空间变换:世界空间 -> 相机空间,相机空间 -> NDC空间,NDC空间 -> 屏幕空间
上一篇文章中介绍了旋转和反射变换,这章我们开始介绍剩下几种变换:缩放/平移/错切。
本章将开始学习空间变换,空间变换在图形学中有非常广泛的应用,对于深入了解图形学底层有重要意义,特别在光栅化渲染管线中, 我们知道最初输入的顶点坐标都是在对象局部空间的($Local Space$), 需要经过一系列的变换将对象从局部空间变换到世界空间(物体摆放在场景的哪里,物体的相对位置关系),接下来再变换到相机空间(即我们观看的视角,就像拿个相机拍照一样,我们是从哪个角度对场景进行观察的),即$mvp$矩阵中的"$m(model Matrix)$和$v(view Matrix)$".这一系列操作即光栅化渲染管线的第一个阶段,也是理解光栅化背后原理的基础.本章将介绍涉及图形学的基础变换。
本系列文章将一步步深入介绍计算机在渲染输出呈现一张图片经历的各种步骤细节以及涉及到的相关其他领域的知识串联,例如:几何,渲染方程, 采样, 蒙特卡洛积分,重要性采样, 辐射度量学, 光照计算, 基于物理的渲染等等.本系列文章不涉及API(openGL directX vulkan 等),皆在带你理解这些API背后的图形学原理,理解图形API函数调用的背后到底在做些什么,有了这些知识做铺垫,可以快速上手API的学习,并且发现bug后也可以快速定位问题所在,而不会被GPU的黑盒搞得一头雾水,因为不同的API虽然架构不同,实现不一,但背后的底层逻辑都是一致的。
在计算机图形学中,需要处理多种类型数据,这些数据可能看起来不像一个小箭头,比如多项式,图像,辐射亮度(radiance)等,但它们的行为表现却是和向量一致的,所以这个小箭头仍然是一个有用的思维模式。
在图形学中,通常要处理些连续变量的函数:图片是你见过的第一个例子,但是当你继续探索图形学的时候,你会遇到很多类似的情况.就其本质而言,计算机无法直接表达连续的函数,必须使用有限的位数表达他们- 采样(将函数的值离散的存储起来,在需要时进行重建).
本章首先用数字音频的具体一维例子来总结采样和重构。然后,我们继续介绍在一维和二维的采样和重构基础上的基本数学和算法。最后,我们深入频域观点的细节,它为这些算法的行为提供了许多见解。
尽管采样在电信领域已经应用了很多年,但随着数字音频的使用增加,1982年cd的推出是采样在消费者中的第一次大规模应用.
在录音过程中,麦克风将空气中以压力波形式存在的声音转换成时变的电压,这相当于在麦克风所在的位置测量空气压力的变化。这种电信号需要以某种方式储存起来,以便在以后的某个时间播放,并发送到扬声器中,通过与电压同步移动膜片,将电压转换回压力波.
录制音频的过程中使用了采样技术, 模数转换器 analog-to-digital converter (A/D converter, or ADC)每秒钟数千次测量电压,生成容易被存储下来的数据流(例如记录在计算机硬盘中).
在回放时,数据流以适当的速率被读取,并被发送到数模转换器 digital-to-analog converter (D/A converter, or DAC),DAC根据它接收到的数字产生一个电压,并且如果我们取足够多的样本来表示电压的变化,所得到的电信号,都是相同的输入信号.
事实证明,每秒钟需要多少样本来完成一次良好的录制,取决于我们试图记录的声音有多高。一个可以很好录制弦乐低音或者鼓的采样率,应用于录制短笛或铙钹,就会产生奇怪的结果;但这些声音在较高的采样率下被录制得很好。为了避免这些采样不足的影响,数字音频记录器对ADC的输入进行滤波,去除可能导致问题的高频。
另一种问题出现在输出端。DAC产生的电压在新采样进入时发生变化,但在下一个采样进入前保持不变,产生阶梯型的波形。这些楼梯就像噪音一样,增加了一种高频的、依赖于信号的嗡嗡声。为了消除这种重建带来的问题,数字音频播放器过滤DAC的输出以平滑波形.
数字音频记录链可以作为采样和重建过程的具体模型,发生在图形中。同样的欠采样和重构伪影也会发生在图像或图形中的其他采样信号上,解决方法是一样的:采样前进行滤波,重构时再进行滤波.
上图显示了一个由过低的采样频率导致的走样具体例子。在这里,我们用两种不同的采样频率对一个简单的正弦波进行采样:顶部的高频采样和底部的低频采样。高频采样显然能够更好的还原信号,但是由低采样率产生的样本与低频正弦波的样本是无法区分的.
一旦采样完成,就无法区分两个信号——快正弦波和慢正弦波——哪个是原始信号,因此没有单一的方法可以在这两种情况下正确地重建信号。因为高频信号可以“假装”成低频信号,这种现象被称为失真。
在图形领域,失真经常表现为:
采样和重构的基本问题可以简单地根据特征太小或太大来理解,但一些更定量的问题很难回答:
1985年,$Ken\;Perlin$发表了一篇名为《A Image Synthetizer》的Siggraph学术论文。提出了一种类似于之前介绍的噪声函数算法,但是表现更好。柏林噪声和之前的噪声函数很相似,和$Value\;Noise$一样,它也依赖于网格系统。在网格的每个顶点处定义随机值,然后对其进行插值以计算空间中某个位置的噪声值。之前我们详细介绍了1D和2D$Value\;Noise$的创建。本章将实现$Perlin\;Nosie$的3D版本。那么$Perlin\;Nosie$和$Value\;Noise$有什么区别呢?对于$Value\;Noise$来说,我们只需在网格的顶点处分配随机数,采样点对所在单元格四个顶点做双线性插值即可。在$Perlin\;Nosie$中,$Ken\;Perlin$建议将网格顶点的随机值替换为梯度(归一化的三维向量),方法是在$[0,1]$范围内生成三个随机浮点数,再将这些随机数重映射到$[-1,1]$范围内,最后对向量进行归一化。
1 | const tableSize = 10; |
相比通过随机数插值的$Value\;Noise$,$Perlin\;Nosie$在网格顶点分布的是三维向量,由于噪声函数需要返回一个浮点数,如何通过三维向量生成浮点数呢?$Ken\;Perlin$建议计算每个网格顶点到我们要计算的点$p$的方向向量,我们可以得到8个3D向量(一个立方体有8个角或顶点)和4个2D向量(二维)。然后计算网格顶点的梯度和从该顶点到$p$点向量的点积。由于向量点积返回一个浮点数,因此,我们将梯度或向量转换为了浮点数。与2D情况一样,为了计算点$p$在3D网格中的坐标,我们将点坐标从浮点数转换为整数值($floor$),然后将这些整数坐标取模$N$,即随机方向的数组(N = 256)。正如在上一章中解释的那样,我们不想生成256x256x256方向的网格。使用包含256个随机方向的单个一维数组,并使用置换表通过将点的整数坐标转换为带有哈希的置换表的索引来“随机拾取”存储在该表中的方向之一。
代码实现如下:
1 | const tableSize = 256; |
为了平滑插值,我们在使用tx ty tz之前对其分别使用smoothStep函数重新映射为uvw。
通过代码可以看出,$Perlin\;Nosie$和$Value\;Noise$非常类似,唯一不同的是我们将网格顶点的随机值替换为随机的单位向量,然后计算顶点梯度和顶点到当前点的方向向量的点积。
在上一章中,我们介绍并解释了实现一维噪声函数的大部分技术和方法。创建更高维度的噪声和一维噪声并没有本质上的区别,因为它们都是基于同样的方法和技术。之前提到过,所有的噪声函数都会返回一个浮点数,无论输入参数是浮点数,二维点还是三维点,至于一维噪声 二维或三维噪声仅与其输入值有关。二维噪声是将二维点作为输入参数的噪声函数。
对于二维噪声,我们会在网格顶点处分布随机值。噪声函数的2D版本以2D点作为输入,假设当前要求的点为$p$。与一维版本类似,我们先得找到点$p$在网格中的位置,如果点在网格之外,可以通过相同的取模技巧重新映射$p$点的位置,得到网格上点$p$的新坐标,记作$Pnoise$。
之前的章节中讲过插值相关的很多内容。

如下图所示:点$Pnoise$被一个单元上的四个顶点所包围。使用之前学到过的双线性插值技术可以轻松得到点$Pnoise$的值(周围四个点的加权平均):

为此我们首先计算$s和t$,它们是一维噪声中t的对应物:

代码实现如下:
本章将介绍噪声的基础内容,包括噪声是什么,它的属性以及可以用来做什么。噪声不是一个难以理解的复杂概念,但它有许多微妙之处需要注意。 正确使用它需要了解它的工作原理和实现方式。 为了创建一些图像并使用各种参数进行实验,我们将实现一个简单(但功能齐全)的噪声函数,称为$Value Noise$。 本章我们将忽略许多过于复杂而无法在此处全面研究的技术,只是对噪声及其一些应用的初步介绍。
噪声是在80年代中期发明的,起初是作为图像纹理的替代方法。主要原因在于80年代中期的电脑内存有限,无法容纳用于纹理映射的图片,于是人们开始寻找替代解决方案。用纯色渲染物体看起来太无趣了,需要通过调整物体的表面材质属性来打破这种干净的外观。在编程中,我们通常在需要创建随机时使用伪随机数生成器。然而使用$RNG(Random\;number\;generator)$是远远不够的。我们在自然界中观察到的随机模式通常是很自然的,物体表面上距离很近的两个点通常看起来会比较相似。但是同一物体上相距很远的两个点却差异很大。换句话说:局部变化是细微的,是渐进的,而全局变化则是很大的。$RNG$无法满足这个要求,因为每次调用随机数生成器它会返回和其它数值完全不相干的随机数。因此调用这个函数会产生两个完全不同的数字,最终产生杂乱无章的白噪声。下面是一个例子:让我们观察一块真实岩石的图像,假设我们想要复现该CG图像。这个例子很有趣,因为我们可以看到岩石图案由三种颜色组成:绿色,粉色和灰色。这些颜色或多或少分布在岩石表面上。我们首先使用随机数生成器的版本:
1 | void GenerateRandPattern() |

上图中间的图像是程序生成的,结果并不理想,实际上这个模式有个名字,白噪声($white\;Noise$)(稍后会解释什么是白噪声)。使用$RNG$为程序生成纹理的每个像素随机选择一种颜色,使得结果中每个像素的颜色变化很大。为了改进结果,我们复制了纹理的一小块区域(10x10像素),将其调整到原始图像的尺寸(256x256),进行高斯模糊处理,生成的图像如上右所示,局部有了较小的变化,而全局变化比较明显。
这个实验结论告诉我们,为了创建平滑的随机图案,需要在网格的固定位置上使用$RNG$分配随机数,然后使用高斯模糊来模糊这些值。下章将详细介绍如何对随机数做模糊,现在只需要记住:噪声(在图形学上下文中)是一个用于模糊在网格上生成的随机数的函数。
目前应用最广泛的噪声版本是由$Ken\;Perlin$在1983年实现的,称为柏林噪声($Perlin\;Noise$)当时他正在制作1982年版本的电影 Tron(© Walt Disney Pictures)。肯·柏林 (Ken Perlin) 于1997年获得美国电影艺术与科学学院颁发的奥斯卡技术成就奖,以表彰他对这部电影的贡献。 他于1984年在Siggraph展示了他的工作,并于1985年发表了一篇论文《An Image Synthesiser》,这是程序化纹理开创性的成果。Perlin噪声会在后续章节中进行解释。