Mipmap 选择过于详细

这篇文章主要探讨了 GPU 上纹理采样时如何选择 mipmap 级别,包含了以下主要内容:

  • Mipmapping 基础知识:在着色器中,纹理采样常用Texture2D.Sample()函数,它接收采样位置和采样器状态作为输入。常规的片段着色器通过采样纹理来给每个像素着色,但会出现纹理锯齿(aliasing)问题,mipmapping 是常见的解决方案,它通过生成一系列分辨率逐渐降低的纹理(mipmaps)来近似积分,当屏幕像素覆盖太多纹素导致采样模糊时,会选择较低分辨率的 mipmap 来减轻这种情况。
  • 像素导数(Pixel derivatives):片段着色器通常被视为在屏幕上每个像素上并行执行的代码,但实际上是在 2x2 的像素块(像素四边形)上执行。在 HLSL 中,ddx()ddy()函数用于计算屏幕空间中沿 X 和 Y 轴的偏导数。通过可视化纹理采样的 UV 坐标的 Y 轴偏导数,发现偏导数的大小与纹理采样导致丑陋锯齿的情况相关,Texture2D.Sample()可以看作是更通用的Texture2D.SampleGrad()函数的语法糖。
  • 从导数到 mipmap 级别(Derivatives to mipmap levels)Texture2D.SampleGrad()内部逻辑将采样位置的偏导数映射到特定的 mipmap 级别,这对应 GPU 上的硬件指令,难以直接查看代码。概念上,mipmap 级别计算方式为MipLevel(x, y) = log₂(ρ(x, y)),其中ρ是根据采样位置的偏导数计算得出的“比例因子”。但实际硬件实现中,不同厂商存在差异,通过实验发现各厂商的实现大致相似但不完全相同,且在不同硬件系列中保持一致。
  • 探索完整的 4D 映射函数(Exploring the full 4D mapping function):通过可视化部分导数到 mipmap 级别映射函数的影响,发现硬件实现与软件实现存在差异,硬件实现在 X 和 Y 轴偏导数都不为零时会产生某种椭圆形状,与软件实现的完美圆形不同。进一步研究 DirectX 11.3 功能规范,了解到椭圆变换的相关内容,包括如何通过雅各比矩阵(jacobian matrix)来描述纹理采样的投影变换,以及如何对角化椭圆以正确计算 mipmap 级别。
  • 三线性过滤(Trilinear filtering):之前的可视化都是在没有任何硬件纹理过滤的情况下进行的,启用三线性过滤会使 mipmap 级别选择发生变化,从之前的硬切换变为平滑渐变,软件实现在使用三线性过滤时仍然有效。
  • 各向异性过滤(Anisotropic filtering):大多数 GPU 支持各向异性过滤,它通过在像素足迹不均匀拉伸时从更高分辨率的 mipmap 中进行多次纹理采样来减轻 mipmapping 导致的模糊,与常规 mipmapping 选择 mipmap 级别的方式不同。在 DirectX 11 规范中,有关于各向异性过滤的 mipmap 级别计算的伪代码,通过扩展交互式椭圆图可以直观地看到各向异性过滤对 mipmap 级别选择的影响。
  • 额外内容(Bonus):通过编写小着色器对比软件和硬件实现的 mipmap 级别选择,以及实现各向异性过滤的 shader 代码,进一步了解两者的差异和效果。还研究了 Nvidia 的近似 mipmap 级别选择方法,其通过一些特定的计算来近似硬件实现。
  • 总结:作者希望通过这篇文章满足对 GPU 功能细节信息的需求,分享了自己在研究 mipmap 级别选择过程中的学习和发现,包括各种硬件实现细节、不同过滤方式的影响等。
阅读 16
0 条评论