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中的計算量稍微增長了一些,另外壓縮致使精度上會有損失
參考資料:
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)); }