原帖地址:
http://ogldev.atspace.co.uk/www/tutorial11/tutorial11.html
http://blog.csdn.net/cordova/article/details/52571920
複合變換
在之前的教程中,我們已經學會了如何使用變換矩陣來移動場景中的物體,如何縮放你的物體到合適的大小,以及旋轉物體到合適的角度。不過我們每次都只進行了一種操作。通過讓頂點與各個變換矩陣相乘來得到最終的位置,縮放或旋轉角度。
M3*(M2*(M1*v)) = V
通過乘法的結合律,我們可以將M3,M2,M1相乘的結果再乘以v,進而得到與上式相同的結果。
N = M3*M2*M1
N*v = V
這意味這我們可以一次性的計算出N,然後在把這個N傳給shader中的uniform variable。我們的頂點是從乘以矩陣M1開始的,然後依次從右向左乘以M2,M3。在3D圖形之中,我們通常會先縮放物體,然後旋轉物體最後再移動物體。對最終的物體進行相機變換以及投影到2D屏幕的計算,得到的繪製結果。如果我們對一個物體先執行旋轉操作,在執行移動操作的話,如圖:
如果先移動,再旋轉的話,結果如下:
正如上圖中看到的那樣,如果我們先對一個物體移動之後在旋轉的話,將會很難去設定他的位置,因爲我們的旋轉是以原點爲參照點的,如果最後異步是旋轉,因爲物體已經原理原點了,這次的旋轉操作將會促使物體發生移動,也就是說這個操作既有旋轉又有移動,這就會變得很難操控。通過先旋轉在移動的方式,我們保存了這兩種操作的獨立性。
代碼
#define ToRadian(x) ((x) * M_PI / 180.0f)
#define ToDegree(x) ((x) * 180.0f / M_PI)
定義了兩個宏,用來完成度和弧度之間的轉換。
inline Matrix4f operator*(const Matrix4f& Right) const
{
Matrix4f Ret;
for (unsigned int i = 0 ; i < 4 ; i++) {
for (unsigned int j = 0 ; j < 4 ; j++) {
Ret.m[i][j] = m[i][0] * Right.m[0][j] +
m[i][1] * Right.m[1][j] +
m[i][2] * Right.m[2][j] +
m[i][3] * Right.m[3][j];
}
}
return Ret;
}
我們重載了*運算符,用來完成矩陣的乘法運算。
class Pipeline
{
public:
Pipeline() { ... }
void Scale(float ScaleX, float ScaleY, float ScaleZ) { ... }
void WorldPos(float x, float y, float z) { ... }
void Rotate(float RotateX, float RotateY, float RotateZ) { ... }
const Matrix4f* GetTrans();
private:
Vector3f m_scale;
Vector3f m_worldPos;
Vector3f m_rotateInfo;
Matrix4f m_transformation;
};
定義一個叫做Pipeline的類用來管理我們的縮放矩陣,平移矩陣以及旋轉矩陣,m_transformation代表我們的各個變換矩陣相乘後的結果。記得m_transformation = m_worldPos*m_rotateInfo*m_scale,相乘的順序很重要。
const Matrix4f* Pipeline::GetTrans()
{
Matrix4f ScaleTrans, RotateTrans, TranslationTrans;
InitScaleTransform(ScaleTrans);
InitRotateTransform(RotateTrans);
InitTranslationTransform(TranslationTrans);
m_transformation = TranslationTrans * RotateTrans * ScaleTrans;
return &m_transformation;
}
用於獲取我們的變換矩陣的函數。
Pipeline p;
p.Scale(sinf(Scale * 0.1f), sinf(Scale * 0.1f), sinf(Scale * 0.1f));
p.WorldPos(sinf(Scale), 0.0f, 0.0f);
p.Rotate(sinf(Scale) * 90.0f, sinf(Scale) * 90.0f, sinf(Scale) * 90.0f);
glUniformMatrix4fv(gWorldLocation, 1, GL_TRUE, (const GLfloat*)p.GetTrans());
我們定義了一個Pipeline類型的p變量,這個變量的縮放矩陣,平移矩陣以及旋轉矩陣傳入的參數都包括Scale變量,所以每一幀這些矩陣都在變化,也就是說每一幀我們傳給vertex shader的矩陣都不一樣,這就會導致我們渲染出來的圖形出現動畫。