计算机图形学 08:怎样减少锯齿与纹理闪烁
斜边变得平滑以后,地板上的细棋盘仍可能随着相机移动而闪烁。这两种现象都涉及采样,但需要平均的对象不同:轮廓处要估计像素有多少面积被图元覆盖;图元内部则要估计一个像素对应的纹理区域里有哪些颜色。
第07篇已经修正透视UV插值。本篇继续区分“采样位置正确”与“采样区域充分”,用两个互相独立的CPU实验验证覆盖采样和纹理预滤波。它们采用明确的规则网格,不代表某块GPU的实际多采样布局。
一个像素需要的是区域信息
先选盒式像素滤波器,把像素看成单位正方形。对黑色背景上的白色图元,理想线性颜色等于图元与像素相交的面积,范围为[0,1]。若恰好覆盖四分之一,颜色应为0.25,再经过sRGB编码用于显示。
在像素中心只问一次“是否在三角形内”,只能得到0或1。沿斜边移动时,这个判断会突然翻转。增加覆盖样本可以得到更多中间值,但有限点集仍是面积的近似,不等于精确积分。
本实验的64×64视口中,白色区域在直线上方,屏幕Y仍向下。它由两个三角形组成,四角为(0,0)、(64,0)、(64,32.25)、(0,8.25),继续使用第05篇的固定格与top-left覆盖规则。
每个像素分别放置1×1、2×2、8×8个规则中心样本。2×2的偏移为(0.25,0.25)、(0.75,0.25)、(0.25,0.75)、(0.75,0.75)。所有坐标可由1/256固定格精确表示,因此不会把不同量化误差混入比较。
用解析面积检查覆盖结果
像素左上角为整数(x,y),横向局部坐标为s,取值[0,1]。像素内的白色高度是:
覆盖面积为。令,并定义:
由于,可以直接得到。这个参考路径不调用三角形覆盖函数,也不靠更密的采样近似自身。
实际输出的线性覆盖误差如下。平均绝对误差除以整个视口4096个像素;多数像素远离边缘,误差为零,所以同时保留最大误差。
| 每像素样本 | 平均绝对误差 | 最大绝对误差 |
|---|---|---|
| 1 | 0.00406901041667 | 0.4375 |
| 4 | 0.00113932291667 | 0.0833333333333 |
| 64 | 0.0000406901041667 | 0.00520833333333 |
三张图来自实际程序的64×64 PPM,经sips转换PNG,按最近邻方式放大。颜色平均发生在线性域,最后只编码一次。不能从PNG码值直接计算上表的面积误差,也不能把当前斜率的结果当成所有轮廓的误差上界。
SSAA和MSAA分别重复什么
SSAA在多个子样本位置计算覆盖和着色,再合并成一个像素。纹理坐标、纹理查询以及其他着色计算都可能重复。MSAA通常为多个样本保存覆盖与深度,却允许一个图元在像素内只执行一次着色,把结果写给该图元覆盖的样本。
对白色常量图元,两种方法在使用相同覆盖点和合并权重时得到相同结果:每个被覆盖样本都是白色,重复计算颜色没有新增信息。前面的实验只检查这一覆盖积分问题,没有测量GPU着色次数或性能。
若一个图元完全覆盖像素,而其内部纹理颜色变化很快,一次着色仍只提供一个颜色。将它复制到四个样本再平均,结果仍是原值。普通的逐像素着色MSAA因此不能保证解决纹理缩小混叠。实际API还支持逐样本着色等选择,不能把“一次着色”当成MSAA永远不可改变的定义。
覆盖、深度和着色也不总在同一个位置计算。这里固定规则中心位置,只解释最小差别;GPU篇会按实际API和硬件行为验收,当前CPU结果不计作GPU验证。
双线性过滤为什么仍会闪烁
考虑256×256的黑白纹理,每个纹素与上下左右邻居异色,线性值为0或1,边界周期重复。一个屏幕像素覆盖4×4纹素大小的正方形。这个区域在两个方向都覆盖完整的周期,无论平移多少,盒式平均恒为0.5。
基础层双线性过滤只读取查询点附近四个纹素,并按距离加权。它解决了相邻纹素间的连续过渡,却没有因为像素覆盖范围变成4×4而自动扩大滤波范围。连续变化的错误颜色仍是错误积分。
纹素中心采用。程序以纹素单位选择查询中心,令相位从0到0.875,每次增加0.125。这个平移改变纹理与像素的相对位置,不改变足迹面积。
八个基础层双线性结果依次为0.375、0.3125、0.25、0.3125、0.375、0.4375、0.5、0.5625。峰谷差为0.3125,而正确区域平均始终是0.5。如果把这些数按时间显示,同一像素会改变明暗;以下图像把八个时刻并列成八条竖带,便于静态核查。竖带并不是纹理空间中真实存在的条纹。
这个场景也构成少量规则SSAA的反例。2×2子样本在纹理中相隔2个纹素,恰好重复同一个周期相位,四点平均仍等于上面的错误值。8×8子样本间隔0.5纹素,本次八个相位都得到0.5。增加样本有效与否还取决于信号和布局,不能只看样本数。
检查器同时输出逐像素着色MSAA4的CPU模型值,即把中心颜色复制四次后的平均。它与基础层结果相同。这是模型的数值对照,没有声称调用了硬件MSAA。
Mipmap提前保存不同范围的平均
Mipmap把原纹理作为第0层,每次将2×2纹素在线性域平均成一个纹素。256平方的纹理依次变成128平方、64平方,直到1平方。第L层一个纹素代表基础层约的区域。
当前黑白纹理的每个2×2块都有两个0和两个1,所以第1层及以后全为0.5。选择第2层对应4×4基础纹素的范围,八个相位实际都输出0.5,最大绝对误差为0。
这个精确结果来自特意选择的周期棋盘,不表示Mipmap能精确积分任意像素足迹。一般纹理在层内仍需要插值,像素足迹可能旋转、拉长或弯曲,预存正方形平均只能近似。实验的作用是把基础层小范围插值和大范围预滤波明确分开。
本实现mipmap.hpp只接受不超过4096的正方形、二次幂尺寸以及[0,1]线性标量,采用repeat寻址。这样能用很少代码验证滤波,不隐含支持任意尺寸、彩色文件解码或GPU纹理格式。RGB可以逐通道使用相同线性运算,但sRGB文件应先解码再生成平均值。
层级选择与三线性过滤
对于尺寸W×H的纹理,若局部映射的屏幕导数已知,一种教学用尺度估计是:
这里的x、y以屏幕像素为单位。每移动一个像素跨过4个纹素,则,正是本实验的选择。程序直接传入由固定映射得到的L=2,没有实现透视表面的自动导数估计,也没有把这个公式说成PBRT的原样实现。
当L=2.3时,可以在第2层和第3层分别双线性查询,按0.7与0.3混合,这称为三线性过滤。第三次线性插值发生在层级方向,不是读取三维纹理。本次代码用L=0.5检查两层结果的中间值,并检查常量纹理在不同LOD、负UV和越界LOD钳制后保持常量。
各向同性层级只有一个尺度。当斜视表面的足迹一个方向跨16纹素、另一个方向只跨1纹素,按最大尺度选层会在短方向过度模糊。各向异性过滤或EWA需要进一步利用足迹形状。本篇没有实现这些算法,也不将当前正方形实验推广成倾斜地面的完整过滤器。
复跑、练习与自检
在仓库根目录执行:
1 | |
输出目录须存在。结果包含三张覆盖PPM、两张相位PPM和控制台数值;累计检查还复跑00–07。日志为writing-plans/computer-graphics/evidence/08-cpu.txt。实验没有随机数,覆盖固定1、4、64点,纹理固定八个相位与4、64点,不使用自适应终止,也没有性能数据。
练习一:一个常量白色图元覆盖四个MSAA样本中的一个,背景为黑色。线性合并值是多少?先把白黑sRGB码值平均再当作线性值,为什么不对?
答案:线性值为1/4。输出sRGB编码约0.5371,即8位码约137。码值直接平均得到约64,显示会明显偏暗。样本合并与颜色编码的顺序不能互换。
练习二:纹理映射在x方向每像素跨8纹素,y方向跨2纹素,上面的尺度公式选哪一层?为什么它可能模糊?
答案:。第3层约平均8×8基础纹素,而短方向实际只需要跨2纹素,额外平均损失了短方向细节。MSAA增加覆盖点不会自动修正这个足迹形状问题;需要更合适的纹理过滤。
系列导航与资料
前篇:07:为什么贴图会扭曲。系列入口。下一篇:09:法线怎样决定明暗。
Microsoft:Rasterization Rules,Multisample Anti-Aliasing Rasterization Rules,核对覆盖、深度与着色频率的区别。此处引用概念,不将Direct3D的具体规则冒充WebGPU规范。
PBRT 4ed §10.4 Image Texture,10.4.3–10.4.4及图10.16,核对MIPMap与图像纹理过滤。资料核验记录在evidence/research-05-08.md;本文的斜边积分、周期棋盘和确定性实验独立构造。






