[轉]頂點數據壓縮

http://www.cnblogs.com/oiramario/archive/2012/09/26/2703277.htmlphp

看過敏敏的http://www.klayge.org/2012/09/21/%E5%8E%8B%E7%BC%A9tangent-frame/html

今年二、3月份曾經整過這玩意,作到用tangent.w來存handedness,解決了uv mirror的問題post

沒想到頂點數據壓縮還有這麼深的學問,因而乎按照資料對max插件進行了修改,效果超出想象this

目前作到使用unsigned char x 4來存normal和tangent,short x 2來存texcoord,咱們能夠大體算一下spa

以前是normal = float x 3,tangent = float x 4,texcoord = float x 2(還要看一共有幾層uv) ,一共是12 + 16 + 8 = 36.net

壓縮以後變成normal = unsigned char x 4,tangent = unsigned char x 4,texcoord = short x 2,一共是4 + 4 + 4 = 12插件

每一個頂點從36字節減小到12字節,少了一半多,經過觀察一個20000多面的模型,mesh的大小從1388KB減小到552KB,壓縮後是原大小的0.39倍code

尚未像文中介紹的那樣將tangent frame壓縮到僅用8個字節的程度orm

其優勢是數據量大大減小,這樣vertex cache的命中率會提升,據觀察fps有約5%的提升htm

其缺點是vs中的計算量稍微增長了一些,另外壓縮致使精度上會有損失 

複製代碼
float f =  0.1234567f;
unsigned  char uc = (unsigned  char)((f *  0.5f +  0.5f) *  255);
short s = ( short)((f *  0.5f +  0.5f) *  32767.0f);

float unpackuc = uc *  2.0f /  255.0f -  1.0f;
float unpacks = s *  2.0f /  32767.0f -  1.0f;


unpackuc =  0.12156863
unpacks =  0.12344737
複製代碼

 

參考資料:

http://www.humus.name/Articles/Persson_CreatingVastGameWorlds.pdf 

http://www.crytek.com/download/izfrey_siggraph2011.pdf 

http://fabiensanglard.net/dEngine/index.php 

http://oddeffects.blogspot.com/2010/09/optimizing-vertex-formats.html

注意:

在聲明頂點元素時,使用UBYTE4或者SHORT4。

D3DVERTEXELEMENT9 declExt[] = {
 // stream, offset, type, method, usage, usageIndex
 { 0, 0, D3DDECLTYPE_FLOAT3, D3DDECLMETHOD_DEFAULT, D3DDECLUSAGE_POSITION, 0 },
 { 0, 12, D3DDECLTYPE_UBYTE4, D3DDECLMETHOD_DEFAULT, D3DDECLUSAGE_NORMAL, 0 },
 // 2d uv
 { 0, 16, D3DDECLTYPE_FLOAT2, D3DDECLMETHOD_DEFAULT, D3DDECLUSAGE_TEXCOORD, 0 },
 { 0, 24, D3DDECLTYPE_SHORT2N, D3DDECLMETHOD_DEFAULT, D3DDECLUSAGE_TEXCOORD, 1 },
 // tangent
 { 0, 28, D3DDECLTYPE_UBYTE4, D3DDECLMETHOD_DEFAULT, D3DDECLUSAGE_TEXCOORD, 2 },
 D3DDECL_END()
};

可是在着色器中直接使用float4做爲輸入,GPU會自動轉換。

float4 normal : NORMAL;

或:
float4 normal : BLENDINDICES;

有些顯卡不支持UBYTE4類型的NORMAL語法輸入,可嘗試做爲BLENDINDICES使用。這也是UBYTE4經常使用的方式。

================================================

Pack the normals into the w value of the position of each vertex, then you should be able to do something similar to this to read it back, and then you just need to convert it back to a normal vector in the shader (multiply by 2, then subtract 1).

To pack the normal into a float you should be able to use something like this (not tested and should probably use the proper casts instead of C style casts, and the normal needs to be normalized):

float PackNormal(const Vector3& normal)
{
   //Use 127.99999f instead of 128 so that if the value was 1 it won't be 256 which screws things up
   unsigned int packed = (unsigned int)((normal.x + 1.0f) * 127.99999f);
   packed += (unsigned int)((normal.y + 1.0f) * 127.99999f) << 8;
   packed += (unsigned int)((normal.z + 1.0f) * 127.99999f) << 16;

   return *((float*)(&packed));
}
相關文章
相關標籤/搜索