視頻教程請關注 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優化版本代碼下載(稍后上傳,敬請關注)