視訊教程請關注 http://edu.csdn.net/lecturer/lecturer_detail?lecturer_id=440
接上一個例程OpenGL10-骨骼動畫原理篇(2),對骨骼動畫的基本原理做了介紹,接下來
要對之前做的工作做一個分析和優化,骨骼動畫要做大量的數學計算,當一個模型的頂點
與骨骼的數量都很多的情況下,會消耗大量的cpu時間,接下來要做的事情就是對程式進行
優化,從上面的計算過程,可以得出,有兩個地方的計算量比較大,首先是矩陣和頂點相乘
,其次是每一幀要插值新的骨骼出來,相對定點計算來講,骨骼的插值計算量應該算是較小
的,一個人物模型少則1000個頂點,多則幾千個頂點。是以我們優化就從定點的計算開始
當然,我們有三種方案,甚至更多(我隻用其中的兩個)。
一:使用CPU優化
二:使用Shader(glsl)優化
三:使用cuda或者OpenCL進行優化
先來看第一種,說道cpu優化,大家可能想到兩件事情,一是從算法角度出發去優化算法,
二是使用使用更好的CPU指令進行優化,沒錯,我們就是要這樣做,矩陣相乘的算法優化的空
間可能已經不大,但是我們采用SIMD指令集和對浮點運算做優化空間還是很大的。
SIMD:
(Single Instruction Multiple Data,單指令多資料流)能夠複制多個操作數,并把它們打包
在大型寄存器的一組指令集,例:3DNow!、SSE。以同步方式,在同一時間内執行同一條指令。以
浮點計算來說,基本上可以到達四倍的加速比。是以采用SIMD可以大幅度的提高性能。cup上使用SIMD
指令:
__m128 sse_mul_ps(__m128 v, __m128 const m[4])
{
__m128 i0 = m[0];
__m128 i1 = m[1];
__m128 i2 = m[2];
__m128 i3 = m[3];
__m128 m0 = _mm_mul_ps(v, i0);
__m128 m1 = _mm_mul_ps(v, i1);
__m128 m2 = _mm_mul_ps(v, i2);
__m128 m3 = _mm_mul_ps(v, i3);
__m128 u0 = _mm_unpacklo_ps(m0, m1);
__m128 u1 = _mm_unpackhi_ps(m0, m1);
__m128 a0 = _mm_add_ps(u0, u1);
__m128 u2 = _mm_unpacklo_ps(m2, m3);
__m128 u3 = _mm_unpackhi_ps(m2, m3);
__m128 a1 = _mm_add_ps(u2, u3);
__m128 f0 = _mm_movelh_ps(a0, a1);
__m128 f1 = _mm_movehl_ps(a1, a0);
__m128 f2 = _mm_add_ps(f0, f1);
return f2;
}
采用Shader優化,将矩陣與頂點的計算工作放到丁點Shader中完成,這樣做以後,cpu機會完全
的解放出來,計算量可以忽略不計。然而這種方案也有些弊端,我們知道shader中,不能像cpu中編寫
c++代碼那種去動态的申請空間,所有的工作必須提前配置設定好。看下面的shader實作代碼:
//! 必須提前配置設定足夠大的空間
uniform mat4 boneMatrices[2];
attribute vec4 weights;
attribute vec4 matrixIndices;
attribute vec4 numBones;
void main( void )
{
vec4 index = matrixIndices;
vec4 weight = weights;
vec4 position = vec4( 0.0, 0.0, 0.0, 0.0 );
for( float i = 0.0; i < numBones.x; i += 1.0 )
{
position = position + weight.x * (boneMatrices[int(index.x)] * gl_Vertex);
index = index.yzwx;
weight = weight.yzwx;
}
gl_Position = gl_ModelViewProjectionMatrix * position;
}
這裡不再針對shader做特别介紹,後面将補沖shader相關的例程
将矩陣資料傳遞給shader
//! 使用shader計算頂點的位置
glUseProgramObjectARB( _programObj );
glUniformMatrix4fvARB( _boneMatrices_0, 1, false, frame._bone[0].data());
glUniformMatrix4fvARB( _boneMatrices_1, 1, false, frame._bone[1].data());
将每一個定點的權重,矩陣的索引,以及矩陣的個數給shader:
//! 傳遞權重
fWeights[0] = g_quadVertices[i * 4 + x].weights[0];
fWeights[1] = g_quadVertices[i * 4 + x].weights[1];
glVertexAttrib4fvARB(_weights, fWeights );
//! 傳遞索引
fMatrixIndices[0] = g_quadVertices[i * 4 + x].matrixIndices[0];
fMatrixIndices[1] = g_quadVertices[i * 4 + x].matrixIndices[1];
glVertexAttrib4fvARB(_matrixIndices, fMatrixIndices );
//! 傳遞數量
fNumBones[0] = g_quadVertices[i * 4 + x].numBones;
glVertexAttrib4fvARB(_numBones, fNumBones );
雖然有這樣的缺點,但我們是可以避免的,采用的方式就是:根據動畫的骨骼的數量,權重的數量去動态的
産生shader代碼然後進行編譯執行,這是一個解決方案,當然我們還有另外一個解決方式,就是首先預先配置設定
一些方案,例如當骨頭數量小于32個的時候,我們調用一個shader,當在64一下的時候調用另外一個,在多一
點就調用128的 shader,....
第三種方案,對你的顯示卡有很高的要求,必須支援OpenCL或者cuda,才可以去使用他,當然OpenCL或者
cuda可以直接的通路OpenGL的資料,效率上來說與shader相當(我還沒有進行這樣的實作),有興趣的可以進行
嘗試。
CPU優化版本代碼下載下傳(稍後上傳,敬請關注)
Shader優化版本代碼下載下傳(稍後上傳,敬請關注)
我的QQ13697826