Mipmap与纹理过滤
为了加快渲染速度和减少纹理锯齿(Texture Aliasing or Shimmering,纹理反走样或闪烁),贴图被处理成由一系列被预先计算和优化过的图片组成的文件,这样的贴图被称为Mipmap。
使用DirectX Texture Tool(DX自带工具)预生成Mipmap Chain
Original | Mip1 | Mip2 | Mip3 | Mip4 | Mip5 | Mip6 | Mip7 | Mip8 |
256x256 | 128x128 | 64x64 | 32x32 | 16x16 | 8x8 | 4x4 | 2x2 | 1x1 |
注:ue4的mip级别,1x1级别为1,2x2为2,4x4为3,8x8为4,16x16为5,32x32为6,64x64为7,128x128为8,256x256为9。。。 即:size=2^(级别-1)
级别<=7不会进行streaming。64x64的级别也可以用-1表示(内部转换时,当为-1时会将级别修改为7)
为了突破这个限制,ua项目在TextureLODGroups结构上增加了NoStreamMipCount成员,用来配置各贴图组的最低可streaming的级别
(1)Mipmap宽高值不一定要相等,但需要为2的n次幂,最低精度为1x1
(2)从原始高精度的Mipmap宽高减半,逐级生成低精度的Mipmap层级;所有低精度层级加起来,会增加1/3存储空间的占用
① Mipmap贴图文件占用更多硬盘空间 -- 解决方法:使用dds进行压缩存储
② Mipmap纹理占用更多内存、显存空间
DDS文件
DDS(DirectDraw Surface):一种使用S3 Texture Compression (S3TC,sometimes also called DXTn or DXTC)算法来压缩存储Mipmap层级的数据格式,
DXTn(DXT1、DXT2、DXT3、DXT4及DXT5)是一种基于把像素放入到使用了调色板颜色和插值颜色的4x4 块中的有损压缩。
算法可被GPU硬件解压缩,这也使得DDS文件被广泛用来存储纹理数据,其中DXT2与DXT4为过渡算法,用得不多。
当然,DDS也可存储不具有mipmap级别、及未压缩的像素格式的贴图。
算法 | 说明 |
DXT1 |
压缩比为1:8(最高),只有1bit Alpha(可选),Alpha通道信息几乎完全丧失。 每个4x4的块可以根据需要有或没有这个透明通道。不需要alpha通道时,每个块可以有四种颜色(其中两个是插值得到的); 需要alpha通道时,则只能有三种颜色,11 被保留用来描述透明的点。 一般将不带Alpha通道的图片压缩成这种格式。 |
DXT3 |
压缩比为1:4 在DXT1的基础上,增加了4bit的alpha通道(可以有16个Alpha值),每个4x4块多用了64bit来保存这些alpha通道信息 可很好地用于Alpha通道锐利、对比强烈的半透和镂空材质。 |
DXT5 |
压缩比为1:4 相对与DXT3,DXT5 对 alpha 通道的储存作了改进。它依旧用 64bit储存16个alpha 信息,但存储格式有些不同。 前面2个字节(16bit)保存了当前块的最大alpha值和最小alpha值。接下来的48bit ,每个像素占用3bit空间,刚好描述4x4个像素。 特别适合Alpha通道柔和的材质,如高光掩码材质。 |
DirectX Texture Tool工具也可以编辑DDS文件的DXT压缩方式;关于DXT的更多信息可参考:DXT 图片压缩
UE3的Texture2D对象
Epic Game公司在UE3中在DDS文件的基础上,开发出了Texture2D对象(如下图),用于储存贴图数据。
Texture2D对象增加了渲染相关的配置(蓝色的框)与生成Mipmap相关的配置(红色的框)。
在生成Mipmap时,Texture2D提供如下选项:
① TMGS_FromTextureGroup // 使用LODGroup中定义的Mipmap生成算法
② TMGS_SimpleAverage // 两次线性插值算法
③ TMGS_Sharpen0、TMGS_Sharpen1、... 、TMGS_Sharpen10 // 不同阈值的两次立方插值算法
④ TMGS_NoMipmaps // 不生成Mipmaps层级
⑤ TMGS_LeaveExistingMips // 使用现有的Mipmaps层级
图像重采样插值算法简介
算法 |
说明 |
最近相邻插值 (Nearest Neighbour Interpolation) |
优点是计算量很小,算法简单,运算速度较快。但它仅使用离待测采样点最近的像素的颜色值作为该采样点的颜色值,而没考虑其他相邻像素点的影响, 因而重新采样后颜色值有明显的不连续性,图像质量损失较大,会产生明显的马赛克和锯齿现象。 |
两次线性插值 (Bilinear Interpolation) 2x2 average average four pixels |
计算量稍大,基本克服了最近相邻插值算法颜色值不连续的特点,因为它考虑了待测采样点周围四个直接邻点对该采样点的相关性影响。 但是,此方法仅考虑待测样点周围四个直接邻点颜色值的影响, 而未考虑到各邻点间颜色值变化率的影响, 因此具有低通滤波器的性质, 从而导致缩放后图像的高频分量受到损失, 图像边缘在一定程度上变得较为模糊。 |
两次立方插值 (Bicubic Interpolation) 8x8 [with sharpening] 4x4 filter [with sharpening] |
计算量最大,算法也最为复杂的。在几何运算中,两次线性插值算法的平滑作用可能会使图像的细节产生退化,在进行放大处理时,这种影响更为明显。 在其他应用中,两次线性插值算法的斜率不连续性会产生不希望的结果。两次立方插值算法不仅考虑到周围四个直接相邻像素点颜色值的影响,还考虑到它们颜色值变化率的影响。 因此克服了前两种方法的不足之处,能够产生比两次线性插值更为平滑的边缘,计算精度很高,处理后的图像像质损失最少,效果是最佳的。 |
Adobe PhotoShop CS6在图像缩放时,提供了以上三种算法供用户选择:
D3D9中使用Mipmap
① 载入Mipmap贴图文件到内存
void* MipData[MAX_TEXTURE_MIP_COUNT]; LoadMipmapFile(“Maya_Basho_A_Leaf_sm_D.dds”, &MipData);
② 创建Mipmap纹理,并绑定数据
IDirect3DTexture9 * pMipMap; // 第3个参数 5 表示从256向下生成5个Mipmap层级(含256);该参数为0表示使用D3D来生成所有层级的Mipmap m_pD3DDevice->CreateTexture(256, 256, 5, 0, D3DFMT_R8G8B8, D3DPOOL_MANAGED, &pMipMap); for( INT MipIndex=0; MipIndex<5; MipIndex++ ) { D3DLOCKED_RECT LockedRect; pMipMap->LockRect( MipIndex, &LockedRect, NULL, D3DLOCK_NOSYSLOCK ); GetTexDataFromMipData( MipData, MipIndex, LockedRect.pBits, LockedRect.Pitch ); pTexture->UnlockRect( MipIndex ); }
③ 设置Mipmap纹理采样器参数
m_pD3DDevice->SetTexture(SamplerIdx, pMipMap); Direct3DDevice->SetSamplerState(SamplerIdx,D3DSAMP_ADDRESSU, D3DTADDRESS_CLAMP); Direct3DDevice->SetSamplerState(SamplerIdx,D3DSAMP_ADDRESSV, D3DTADDRESS_MIRROR); Direct3DDevice->SetSamplerState(SamplerIdx,D3DSAMP_ADDRESSW, D3DTADDRESS_WRAP); Direct3DDevice->SetSamplerState(SamplerIdx,D3DSAMP_MAGFILTER, D3DTEXF_LINEAR); Direct3DDevice->SetSamplerState(SamplerIdx,D3DSAMP_MINFILTER, D3DTEXF_ANISOTROPIC); Direct3DDevice->SetSamplerState(SamplerIdx,D3DSAMP_MIPFILTER, D3DTEXF_POINT); Direct3DDevice->SetSamplerState(SamplerIdx,D3DSAMP_MIPMAPLODBIAS, 1);// Mipmap层级向高精度偏移1个层级 Direct3DDevice->SetSamplerState(SamplerIdx,D3DSAMP_MAXANISOTROPY, 4);// 最大异性采样阈值设置为4
纹理地址模式
UE的纹理坐标的原点在左上角像素的中心,u轴向右 v轴向下,范围为:[0, 1] x [0, 1] 。若顶点u和v大于1或小于0时,则需要用相应的纹理地址模式来寻址,以确定该顶点所应采用的纹理像素颜色值。
常用纹理地址模式有:包裹模式(Wrap) 镜像模式(Mirror) 夹子模式(Clamp)。
注1:UE3、UE4的纹理坐标与D3D10+坐标是一致的
注2:Unity的纹理坐标与OpenGL的坐标是一致的,坐标原点为左下角像素的中心 u轴向右 v轴向上
注3:D3D9的纹理坐标原点为左上角像素的左上角 u轴向右 v轴向下
纹理映射
定义:使用uv坐标将纹理像素(texel)映射到屏幕像素(pixel)的过程。
纹理过滤
由于相机不一定正对着物体,以及透视投影为非正交投影,纹理并不会1:1的投影到物体表面并最终呈现到实际的屏幕像素上。
当最后呈现到屏幕中的尺寸比纹理实际尺寸大的时候,此时需要对纹理进行放大(Texture Magnification),反之需要缩小(Texture Minification)。
纹理放大采样的时候如果不经处理,则会出现马赛克等方块等情况,如果纹理缩小采样的时候不经处理,那么就会产生纹理锯齿。因此,纹理过滤就变得非常必要。
从上图像素点被缩放的程度上看,当屏幕像素(pixel):纹理像素(texel)为n:1时,则进行纹理放大;为1:n时,则进行纹理缩小。 注:n>1
纹理过滤算法
各向同性(isotropic):屏幕像素(pixel)在u、v方向上使用相同精度纹理像素(texel)
各向异性(anisotropic):屏幕像素(pixel)在u、v方向上使用不同精度纹理像素(texel)
MIPFILTER说明
① 为D3DTEXF_NONE,表示不使用MipMap
② 为D3DTEXF_POINT,在一层Mipmap进行纹理采样
③ 为D3DTEXT_LINEAR,在两层Mipmap进行纹理采样,然后在两层之间做线性融合,因此在不同层级的Mipmap之间有更好地过度
Mipmap层级选择 -- 各向同性
① 计算得到映射比【屏幕像素(pixel):纹理像素(texel)】最小P(min)的屏幕像素点A
② 计算像素点A的uv在屏幕空间的对应方向,并沿着该方向得到屏幕像素区域的最小外接平行四边形MNOP
③ 计算n=Min{m=x*P(min)(u), n=y*P(min)(v)} 注:x、y为平行四边形MNOP的u、v对应方向上的边长;m、n为u、v方向的纹理像素值
④ 通过n<=h && Min{h1, h2, ...}条件,得到MIPFILTER=D3DTEXF_POINT的Mipmap层级L(上图中为: 64)
⑤ 若MIPFILTER=D3DTEXF_LINEAR,除了选择层级L外,还要选择L低一级的Mipmap层级(上图中为: 32)
Mipmap层级选择 -- 各向异性
① 计算得到映射比【屏幕像素(pixel):纹理像素(texel)】最大P(max)的屏幕像素点A
② 计算像素点A的uv在屏幕空间的对应方向,并沿着该方向得到屏幕像素区域的最小外接平行四边形MNOP
③ 计算m=Max{m=x*P(max)(u), n=y*P(max)(v)} 注:x、y为平行四边形MNOP的u、v对应方向上的边长;m、n为u、v方向的纹理像素值
④ 通过m<=w && Min{w1, w2, ...}条件,得到MIPFILTER=D3DTEXF_POINT的Mipmap层级L(上图中为: 256)
⑤ 若MIPFILTER=D3DTEXF_LINEAR,除了选择层级L外,还要选择L低一级的Mipmap层级(上图中为: 128)
各向异性纹理采样
以MIPFILTER=D3DTEXF_POINT为例
① 根据上面“Mipmap层级选择 -- 各向异性”的规则,得到Mipmap层级L: 256
② 计算任意像素点K的uv在屏幕空间的对应方向,并沿着该方向得到屏幕像素区域的最小外接平行四边形MNOP
③ 分别计算uv方向的纹理像素值 m=x*P(k)(u), n=y*P(k)(v) 注:x、y为平行四边形MNOP的u、v对应方向上的边长
④ 分别计算S(u)=Min{MaxAnisotropy, L/m},S(v)=Min{MaxAnisotropy, L/n}
⑤ 对S(u),S(v)进行四舍五入取整 (上图中为: S(u)=2 S(v)=4)
⑥ 屏幕像素K对应纹理像素点所在的2x4的区域内采样,并线性插值计算屏幕像素K的颜色值
最大MaxAnisotropy阈值
(1) 使用DirectX Caps Viewer查看显卡的MaxAnisotropy阈值
(2) 另外要注意控制面板中的显卡全局设置(① 是否由3D应用程序决定 ②若由显卡决定,看看MaxAnisotropy设置大小)
不同MaxAnisotropy效果对比
MaxAnisotropy设置得越大,远处的画面细节保留得越多(上图中离视点越远的地方,各向异性程度越高)
常用的纹理过滤参数组合
过滤方式 |
MinFilter |
MagFilter |
MipFilter |
采样数 |
Point(最近点采样) |
D3DTEXF_POINT |
D3DTEXF_POINT | D3DTEXF_POINT | 1 |
Bilinear(双线性插值) |
D3DTEXF_LINEAR |
D3DTEXF_LINEAR | D3DTEXF_POINT | 4 |
Trilinear(三线性插值) |
D3DTEXF_LINEAR | D3DTEXF_LINEAR | D3DTEXF_LINEAR | 8 |
AnisotropicPoint(各向异性) |
D3DTEXF_ANISOTROPIC |
D3DTEXF_LINEAR | D3DTEXF_POINT | 4 x n |
AnisotropicLinear(各向异性) |
D3DTEXF_ANISOTROPIC | D3DTEXF_LINEAR | D3DTEXF_LINEAR | 8 x n |
① 采样数可以反映出算法的复杂度:采样数越大,耗费的计算越多
② Point、Bilinear、Trilinear画面效果基本差不多;细看的话,Trilinear > Bilinear > Point,Trilinear在Mipmap层级间过渡更自然平滑些
③ AnisotropicLinear效果最好,在Mipmap层级间过渡方面要好于AnisotropicPoint,不过基本上看不出来AnisotropicPoint与AnisotropicLinear的差别了
参考
各项异性滤波简介Anisotropic Filtering(AF)