DXT纹理压缩,Multiple Render Targets

游戏中对于3D物体表面细节的表现最重要的还是靠贴图来实现的,那么越是高分辨率越是真彩色的贴图自然表现力也是越强,但是同时带来的问题是所需占用的内存会成倍的上升,而节省内存这一点在目前的游戏中还是非常非常重要的。 
   
所以各个平台上都在使用纹理压缩的技术,让纹理贴图在内存占用和显示效果能达到一个尽可能的平衡。在DirectX中,使用一种叫做DXT的纹理压缩技术,目前这种技术被大部分显卡所支持,通过对DXT的了解,我们可以对纹理压缩技术管中窥豹。 
    DXT
是一种DirectDraw表面,它以压缩形式存储图形数据,该表面可以节省大量的系统带宽和内存。即使不直接使用DXT表面渲染,也可以通过 DXT格式创建纹理的方法节省磁盘空间。Direct3D提供了D3DFMT_DXT1 ~ D3DFMT_DXT55种压缩纹理格式。其中,D3DFMT_DXT1支持15RGB1alpha图形格式,D3DFMT_DXT2D3DFMT_DXT3支持12RGB4alphaD3DFMT_DXT4D3DFMT_DXT5则采取了线性插值方式生成alpha 
DXT纹理压缩,Multiple Render Targets
DXT1 
    DXT1
格式主要适用于不具透明度的贴图或仅具一位Alpha的贴图(非完全透明则即完全不透明),对于完全RGB565格式的贴图,DXT1具有41的压缩比,即平均每个像素颜色占4位,虽然压缩比并不是很好,但是DXT的特性使得它更适合用于实时游戏之中。 
    DXT1
将每4×4个像素块视为一个压缩单位,压缩后的4×4个像素块占用64位,其中有216位的RGB颜色和162位索引,格式描绘如下图所示: 
DXT纹理压缩,Multiple Render Targets
    DXT1
中的两个RGB颜色负责表示所在压缩的4×4像素块中颜色的两个极端值,然后通过线性插值我们可以再计算出两个中间颜色值,而162位索引则表明了这4×4个像素块所在像素的颜色值,2位可以表示4种状态,刚好可以完整表示color_0color_1以及我们通过插值计算出的中间颜色值color_2color_3,而对于具有一位Alpha的贴图,则只计算一个中间颜色值,color_3用来表示完全透明。 
   
对于如何判断DXT1格式是表示不透明还是具有1alpha的贴图,则是通过两个颜色值color_0color_1来实现的,如果color_0的数值大于color_1则表示贴图是完全不透明的,反之则表示具有一位透明信息。 
DXT2
DXT3 
    DXT2
DXT3可以表示具有更复杂的透明信息的贴图,这两种格式采用的是显式的Alpha表示,我们知道了在DXT1中,我们使用64位数据来描述4*4的像素块的颜色信息,在DXT2DXT3中,这部分颜色信息是不变的,而是通过另附加64位数据也就是每个像素4位来表示他们的Alpha透明信息,而这4位的Alpha的信息通常情况下我们可以采用直接编码的方式来表示即可。 
   
这样每个4×4像素块占用128位也就是8个字,0~3字表示透明信息;4~7表示前面描述的颜色的信息。 
    DXT2
DXT3的不同之处在于,DXT2中颜色是已经完成了Premultiplied by alpha操作(已完成颜色与alpha的混合,当透明度发生改变时,直接改变整体颜色值,不必再单独复合),DXT3Alpha信息则是相对独立的,之所以要区分开了则是为了适应不同的需要,因为有些场合需要独立的Alpha信息。 
DXT纹理压缩,Multiple Render Targets
DXT4
DXT5 
    DXT4
DXT5也是用于表示具有复杂的透明信息的贴图,与23不同的是45Alpha信息是通过线性插值计算所得,类似于DXT1的颜色信息。同样的,每4×4的像素块的透明信息占用64位,所不同的是,64位中采用了28位的alpha值和163位的索引值,既然每个像素的索引占3位,那么可以表示8种不同的透明状态。 
   
在这里插值的方法有两种,一种用于表示具有完全透明和完全不透明的状态,另一种则是仅在给出的极端值alpha_0alpha_1中进行插值。区分的方法也是通过比较alpha_0alpha_1的大小来实现的,如果alpha_0大于alpha_1,则通过插值计算剩下的6个中间alpha值;否则,只通过插值计算4个中间alpha值,alpha_6直接赋值0alpha_7直接赋值255 
    DXT4
DXT5的区别同DXT2DXT3的区别相同,DXT4的颜色值是理解为已经完成Premultiplied by alpha操作的。 
   
另外需要注意的是,所有的压缩纹理格式都是2的幂,因为纹理压缩的单位是4×4像素,所以如果贴图的大小位16×2或者8×1这样的比例,系统会同样采用4×4的单位进行压缩,会造成一定的空间浪费,同样的大小会被占用,只是不会参与使用而已

 

 

[DirectX]MultipleRender Targets

Multiple Render TargetMRT)是一种指可以使绘制程序在单帧中同时渲染多个Render Target,也就是一次Draw可以将不同的信息分别画入多个Surface。是利用Pixel Shader实现Post-Process效果中很重要的一部分。 
   
如在上一篇Pixel Motion Blur中,单帧渲染中返回了场景中原有的画面颜色同时还有该像素点的运动速度,超出单一Surface所能表现的数据宽度的部分就是通过Multiple RenderTarget渲染到另一个Surface上,有了MultipleRender Target的支持,我们在Pixel Shader中就可以返回更多的数据。 
   
不过Multiple Render Target有很多限制,我没有试验过,体会也不深,以下翻译自DirectX的文档,往后有机会接触多了,再有新的再即时更新: 
   
  Multiple Render Target中的所有的RenderTarget表面必须具有相同的位宽(比如都是32位)但可以拥有不同的格式(比如同样是32位可以使用D3DFMT_A8R8G8B8或者D3DFMT_G16R16F),不过DX允许我们通过设置D3DPMISCCAPS_MRTINDEPENDENTBITDEPTHS表示来使得RenderTarget之间可以拥有不同的位宽。 
   
  Multiple Render Target中的所有的RenderTarget应该拥有相同的宽和高 。 
   
  反锯齿是不被支持。 
   
另外还有两点关于post-pixel shader操作和标志位D3DRS_COLORWRITEENABLE的注意事项,情况比较特殊,需要时再具体查询DX文档吧。

 

Render Target

  Render TargetDirect3D中的一个称谓,主要用于在各种Post Process处理中担任中间过渡的角色。 
   
现在游戏中很多的特效类似于电影中的后期处理,也就是说当影片拍摄出来以后,再进行润色处理。同样的,如果游戏中的某个特效也需要类似的流程,效果的实现并非在多边形绘制的同时进行,而是在整个场景绘制结束之后再进行效果处理的话,我们的场景就不能直接绘制到屏幕缓冲了,而Render to Texture就是指将整个场景绘制到一块材质上,这样做,就需要用到RenderTarget 
   
其实仅仅是一个概念而已,没有算法上的东西,不过在Direct3D里使用Render Target有很多需要注意的细节,以后可能总会用到,所以记在这里,聊为笔记。 
   
要在D3D中实现将场景绘制到Texture,我们首先需要的——当然是一个Texture对象,这个Texture对象不同于通常概念的Texture主要在于创建方式的不同。另外我们需要两个Surface对象(一个用于连接Render Target Texture,另一个用于暂时保存离屏缓冲) 
   
变量声明: 
   LPDIRECT3DTEXTURE9   g_pFullScreenRenderTarget;    //
全屏RT 
   LPDIRECT3DSURFACE9   g_pFullScreenRenderTargetSurf,pOriginalRenderTargetSurf;    //
全屏RTSurface 
   
创建纹理: 
    D3DXCreateTexture(pd3dDevice, pBackBufferSurfaceDesc->Width,pBackBufferSurfaceDesc->Height,1, D3DUSAGE_RENDERTARGET,D3DFMT_A8R8G8B8,D3DPOOL_DEFAULT, &g_pFullScreenRenderTarget ); 
   g_pFullScreenRenderTarget->GetSurfaceLevel(0,&g_pFullScreenRenderTargetSurf ); 
   
这里CreateTextur函数的参数中需要特别注意的是Usage参数需要为D3DUSAGE_RENDERTARGET,而D3DPOOL则必须为D3DPOOL_DEFAULT 
   
颜色格式的设置通常情况下是D3DFMT_A8R8G8B8等常用色彩格式,但如果利用Render Target来实现特殊计算需要用到浮点数,则要相应使用D3DFMT_G16R16F等浮点数格式 
   
另外如果材质的大小同离屏缓冲的高宽不相同的话,切换Render Target进行绘制的时候要重新设置投影矩阵,否则绘制的图像会比例不对,还有种说法是纹理不能太大,否则会绘制不出任何东西,不过我没有做过实验。 
    GetSurfaceLevel
的作用将Surface对象同作为Render TargetTexture链接起来,以便后面访问 
   
渲染: 
   
渲染之前我们首先需要备份原本的离屏缓冲 
   pd3dDevice->GetRenderTarget(0,&pOriginalRenderTargetSurf); 
    if( SUCCEEDED(pd3dDevice->BeginScene() ) ) 
    { 
        pd3dDevice->SetRenderTarget(0,g_pFullScreenRenderTargetSurf); 
       pd3dDevice->Clear(0, NULL, D3DCLEAR_TARGET | D3DCLEAR_ZBUFFER,D3DXCOLOR(0.0f,0.00f,0.00f,1.00f), 1.0f, 0); 
        //
在这里对Render Target Texture进行渲染 
       pd3dDevice->EndScene(); 
    } 
   pd3dDevice->SetRenderTarget(0, pOriginalRenderTargetSurf);//
恢复原有离屏缓冲 
   SAFE_RELEASE(pOriginalRenderTargetSurf);//Get
获取的Render Target要记得释放掉,否则会内存泄漏 

 

 DXT纹理压缩,Multiple Render Targets