天天看點

OpenGL10-骨骼動畫原理篇(3)-Shader版本代碼已經上傳

視訊教程請關注 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

繼續閱讀