posts - 225, comments - 62, trackbacks - 0, articles - 0
   :: 首页 :: 新随笔 :: 联系 :: 聚合  :: 管理

纹理采样过滤方式

Posted on 2011-04-04 22:54 魔のkyo 阅读(2011) 评论(0)  编辑 收藏 引用 所属分类: Graphics
在DirectX设置纹理采样过滤方式是通过 IDirect3DDevice9::SetSamplerState

IDirect3DDevice9::SetSamplerState

Sets the sampler state value.

HRESULT SetSamplerState(
DWORD Sampler,
D3DSAMPLERSTATETYPE Type,
DWORD Value
);
通过第二个参数Type可以分别传递
D3DSAMP_MAGFILTER = 5, 放大时的过滤方式
D3DSAMP_MINFILTER = 6, 缩小时的过滤方式
D3DSAMP_MIPFILTER = 7, MipMap的过滤方式

第三个参数Value可以传递
D3DTEXF_NONE = 0,  //只用于MipMap的过滤方式,表示不启用MipMap
D3DTEXF_POINT = 1, //对于放大或缩小过滤时表示最近点过滤,对于MipMap过滤表示选择最近层
D3DTEXF_LINEAR = 2, //对于放大或缩小过滤时表示线性过滤,对于MipMap过滤表示对最近的上下两层过滤后的值做线性插值
D3DTEXF_ANISOTROPIC = 3, //各向异性过滤,只用于缩小
D3DTEXF_PYRAMIDALQUAD = 6, //没用过
D3DTEXF_GAUSSIANQUAD = 7, //没用过
D3DTEXF_CONVOLUTIONMONO = 8, //仅用于单色纹理,没用过

最基本的过滤方式就是最近点过滤直接把插值出来的uv通过公式计算到图素坐标,可以参考http://www.cnitblog.com/luckydmz/archive/2010/10/08/69779.html
因为算出来的可能是小数,就四舍五入,这个"四舍五入"就是最近点过滤。


    图:最近点过滤
如果用u对应小数部分做权重对左右线性插值,再用v对应的小数部分进行上下线性插值,这个就叫线性过滤。
线性过滤可以较好的解决纹理被放大时像“马赛克”一样的问题,但对纹理被缩小时的走样就没什么用了(至少我感觉是这样的)。
为了解决缩小时的走样就出现了MipMap,就是把纹理做预处理,平均一下计算出对应的1/4大的一张(横竖各1/2)作为MipMap的一层,再平均一下算出1/16大的一层,...,然后采样时就到预计算好的层上去采,这样又快效果又好。对放大缩小和MipMap都使用线性过滤的方式一般被称作3线性过滤。


    图:3线性过滤

MipMap的主要问题是,当纹理的纵横缩放比差很大的时候会很模糊,当然其实也可能控制采样的层使它不模糊,但是又会出现和不使用MipMap时一样的走样问题,这很容易理解,因为MipMap预处理的时候是横纵一起缩放的,要大都大,要小都小,后来HP发明了一种rimipmap的东西,可以分别对横纵控制缩放比,不过最终没有被硬件广泛支持,可能是因为开销比较大(相当于需要预处理生成logW*logH层而且要多消耗原纹理空间的3倍,mipmap只要多消耗1/3倍)


    图: 各项异性过滤
所以后来又提出了一个基于MipMap的技术,就是各向异性,各项异性简单的可以这样理解:一个屏幕像素被反映射到纹理上的区域是一块近似的平行四边形(应该不是标准的平行四边形),然后用短边决定MipMap的层,再在长边的方向上多采样几次做混合,用短边决定MipMap层就使得层数不会太深,然后再用长边多次采样混合,这样就缓解了上面提到的模糊问题。

附:设置采样器状态的代码
    { //最近点过滤且不使用MipMap
        m_pRenderDevice->SetSamplerState(0, SAMP_MAGFILTER, TEXF_POINT);
        m_pRenderDevice->SetSamplerState(0, SAMP_MINFILTER, TEXF_POINT);
        m_pRenderDevice->SetSamplerState(0, SAMP_MIPFILTER, TEXF_NONE);
    }
    { //3线性过滤
        m_pRenderDevice->SetSamplerState(0, SAMP_MAGFILTER, TEXF_LINEAR);
        m_pRenderDevice->SetSamplerState(0, SAMP_MINFILTER, TEXF_LINEAR);
        m_pRenderDevice->SetSamplerState(0, SAMP_MIPFILTER, TEXF_LINEAR);
    }
    { //各向异性过滤
        m_pRenderDevice->SetSamplerState(0, SAMP_MAGFILTER, TEXF_LINEAR);
        m_pRenderDevice->SetSamplerState(0, SAMP_MINFILTER, TEXF_ANISOTROPIC);
        m_pRenderDevice->SetSamplerState(0, SAMP_MIPFILTER, TEXF_LINEAR);
        m_pRenderDevice->SetSamplerState(0, SAMP_MAXANISOTROPY, 4); //设置沿着长抽最多采样几次,默认是1次就和普通3线性采样没区别了
    }

只有注册用户登录后才能发表评论。