Win32 OpenGL編程(8) 3D模型變換及其組合應用
write by 九天雁翎(JTianLing) -- blog.csdn.net/vagrxie
提要
在前文(系列文章(7),以下簡稱XO7,系列其他文章類似)中的照相機比喻中提到了4種3D變換,如下:
1.確定照相機的位置的過程對應于“視圖變換”(Viewing Transformations)
2.確定物體位置的過程對應于“模型變換”(Modeling Transformations)
3.確定照相機放大倍數的過程對應于“投影變換”(Projection Transformations)
4.確定照片大小的過程對應于“視口變換”(Viewport Transformations)
XO7中我們講的是第一種變換視圖變換,即改變觀察者本身的位置,視角等的變換效果,本文開始繼續按順序講解下一個3D變換過程,模型變換。
模型變換
模型變換指的是以設置模型(即我們觀察的物體)的位置,方向為目的的變換過程,還在2D時,XO4一文的內容就已經涉及模型變換的知識了,(我還提供了一個2D中使用glTranslate*和glRotate*的較復雜例子,見《 Win32 OpenGL編程系列 2D例子 -- 七巧板圖形繪制 》)只不過那時候我們純粹是在2D空間中思考這個問題,并且描述如移動,旋轉等變換時提及的對象是圖元,其實那就是3D的模型變換在2D世界的一個簡化版而已,我們可以將以前的知識在3D空間擴展開來,各種變換的效果和意義還是類似的。glTranslate*,glRotate*的使用方式與2D是類似的,僅僅是多考慮一個Z軸的值而已,比如,glTranslate*函數就可能將模型移出原來的x-y平面(即Z=0的平面),而glRotate*的旋轉也可能使模型不再在原x-y平面上,對變換角度的計算時,不要漏了就可以了。
其實當時講2D時還有個模型變換函數沒有講到,那就是改變模型大小的glScale*函數。
glScale — multiply the current matrix by a general scaling matrix
C Specification
void glScaled( GLdouble x,
GLdouble y,
GLdouble z);
void glScalef( GLfloat x,
GLfloat y,
GLfloat z);
Parametersx, y, z
Specify scale factors along the x, y, and z axes, respectively.
此函數的使用起來很簡單,我用一個動畫效果來演示此函數。此例是從XO7中的glPolygonFace例子改過來的,已經處理好了正面,背面和視角,我僅僅添加了幾句glScale*函數的調用
//這里進行所有的繪圖工作
void SceneShow(GLvoid)
{
glClear(GL_COLOR_BUFFER_BIT);
glColor3f(1.0, 0.0, 0.0);
static GLfloat fScaleY = 1.0;
static GLboolean bScaleWay = true;
if (bScaleWay)
{
if (fScaleY <= 1.5)
{
fScaleY += 0.01;
}
else
{
bScaleWay = false;
}
}
else
{
if (fScaleY >= 0.5)
{
fScaleY -= 0.01;
}
else
{
bScaleWay = true;
}
}
glPushMatrix();
glScalef(1.0, fScaleY, 1.0);
DrawSmoothColorPyramid(0.5);
glPopMatrix();
glLoadIdentity();
gluLookAt(gViewPosX, gViewPosY, gViewPosZ, gViewDirX, gViewDirY, gViewDirZ, gViewUpDirX, gViewUpDirY, gViewUpDirZ);
glFlush();
}
此時模型的高度在0.5與1.5倍之間來回變換,加上以前的框架,你可以變換各種角度來查看。glScale*的使用在此例中就很明顯了,x,y,z值分別對應原模型的變換倍數,1.0即不變,1.5即增長一半,以此類推。
為節省篇幅僅貼出關鍵片段,完整源代碼見我博客源代碼的2009-10-26/glScaleSample 目錄,獲取方式見文章最后關于獲取博客完整源代碼的說明。
模型變換的組合
以上3個模型變換可以一起組合使用,實現復雜的效果,七巧板一例中就是完全靠glTranslate*河glRotate*來為各塊圖形定位和控制旋轉的。
原七巧板中的CShape繪制圖形時使用的代碼如下:
void Draw()
{
glLoadIdentity();
glColor4fv(mfvColor);
glPushMatrix();
glTranslatef(mfPosX, mfPosY, 0.0);
glRotatef(mfDegree, 0.0, 0.0, 1.0);
DrawImp();
glPopMatrix();
}
現在看起來很簡單,無非就是先將一塊七巧板通過glTranslatef移動到某個確定的X,Y位置,然后通過glRotatef旋轉,那么,我們能不能先旋轉好了角度,然后再移動到確定的位置去呢?從理論上來說這條路和以前應該一樣嘛。比如下列代碼,僅僅更改了glTranslatef和glRotatef的先后順序,效果會一樣嗎?
void Draw()
{
glLoadIdentity();
glColor4fv(mfvColor);
glPushMatrix();
glRotatef(mfDegree, 0.0, 0.0, 1.0);
glTranslatef(mfPosX, mfPosY, 0.0);
DrawImp();
glPopMatrix();
}
但是最后的效果與我們所想的并不一樣。
原因嘛,需要好好解釋一下,正如我提及模型變換常常說是坐標系的變換,因為我喜歡從這個角度來看模型變換,模型變換改變的不是模型本身,改變的是模型所處的坐標系,此時,移動,改變的僅僅是坐標系的原點,轉動的話,坐標系的方向都變了,縮放的話,坐標系的單位長度變化了。這樣考慮的話就知道為什么上述例子中兩個函數的調用順序改變會導致結果改變了,先轉動時,坐標系已經轉動了,而我們移動的時候,數據還是相對于原來OpenGL默認的坐標系的,自然就不對了,為了更形象的看到結果,這里我給出一個同時顯示三個不同處理三角錐的例子:
//這里進行所有的繪圖工作
void SceneShow(GLvoid)
{
glClear(GL_COLOR_BUFFER_BIT);
glColor3f(1.0, 0.0, 0.0);
// 原三角錐
DrawSmoothColorPyramid(0.5);
// 先轉動后移動的三角錐
glPushMatrix();
glRotatef(90, 0.0, 0.0, 1.0);
glTranslatef(0.0, 0.5, 0.0);
DrawSmoothColorPyramid(0.5);
glPopMatrix();
// 先移動后轉動的三角錐
glPushMatrix();
glTranslatef(0.0, -0.5, 0.0);
glRotatef(90, 0.0, 0.0, 1.0);
DrawSmoothColorPyramid(0.5);
glPopMatrix();
glLoadIdentity();
gluLookAt(gViewPosX, gViewPosY, gViewPosZ, gViewDirX, gViewDirY, gViewDirZ, gViewUpDirX, gViewUpDirY, gViewUpDirZ);
glFlush();
}
運行效果如下圖所示:
例子還是在以前三角錐繪制代碼上改動而來,運行效果如下圖,中間的三角錐即是沒有經過任何變換的原始三角錐。左邊的那個三角錐是先轉動后移動產生的三角錐,理解一下是怎么通過上述操作得出來的,首先,逆時針轉動90度,此時此三角錐的局部坐標的Y正軸已經是指向屏幕的正左邊了,所以,此時通過glTranslate*將其向Y正方向移動0.5,導致其移動到了屏幕的左邊。下邊那個三角錐即是先移動,后轉動產生的,由于移動操作僅影響坐標軸的原點位置,不改變方向和單位長度,所以很好理解。主要注意那個先旋轉后移動的三角錐,移動的方向已經不是屏幕的全局坐標系了。
為節省篇幅僅貼出關鍵片段,完整源代碼見我博客源代碼的2009-10-26/glComposModelTrans 目錄,獲取方式見文章最后關于獲取博客完整源代碼的說明。
glScale*與glRotate*類似,先調用的話也會對坐標系產生影響,導致以后的操作都會按照影響的結果而改變,比如,先將坐標系放大1倍,那么以前移動0.5的操作將會變成移動1,這一點由于和glRotate*組合使用的效果一致,見下面的例子:
//這里進行所有的繪圖工作
void SceneShow(GLvoid)
{
glClear(GL_COLOR_BUFFER_BIT);
glColor3f(1.0, 0.0, 0.0);
// 原三角錐
DrawSmoothColorPyramid(0.5);
// 先縮放,后移動
glPushMatrix();
glScalef(1.5, 1.5, 1.5);
glTranslatef(0.0, 0.3, 0.0);
DrawSmoothColorPyramid(0.5);
glPopMatrix();
// 先移動,后縮放
glPushMatrix();
glTranslatef(0.0, 0.3, 0.0);
glScalef(1.5, 1.5, 1.5);
DrawSmoothColorPyramid(0.5);
glPopMatrix();
glLoadIdentity();
gluLookAt(gViewPosX, gViewPosY, gViewPosZ, gViewDirX, gViewDirY, gViewDirZ, gViewUpDirX, gViewUpDirY, gViewUpDirZ);
glFlush();
}
作為比較對象,留下來的原三角錐在最下面(原中間位置),中間的那個大三角錐是按照默認大小坐標系上移了0.3距離,而上面大三角錐在程序中也是上移0.3距離,但是此距離因為首先經過了glScale*放大,所以可以看出實際效果上移動的要比默認的0.3要遠,所以導致圖像無法重合。
為節省篇幅僅貼出關鍵片段,完整源代碼見我博客源代碼的2009-10-26/glComposModelTrans2 目錄,獲取方式見文章最后關于獲取博客完整源代碼的說明。
最后,作為額外的例子,展示幾個相關有趣的圖以及代碼,因為不牽涉到新知識,不做過多的解釋。
有趣的組合效果,一個放大了1倍的三角錐和一個移動到上面的三角錐組合而成。
void SceneShow(GLvoid)
{
glClear(GL_COLOR_BUFFER_BIT);
glColor3f(1.0, 0.0, 0.0);
glPushMatrix();
glScalef(2.0, 2.0, 2.0);
DrawSmoothColorPyramid(0.5);
glPopMatrix();
glPushMatrix();
glTranslatef(0.0, 0.5, 0.0);
DrawSmoothColorPyramid(0.5);
glPopMatrix();
glLoadIdentity();
gluLookAt(gViewPosX, gViewPosY, gViewPosZ, gViewDirX, gViewDirY, gViewDirZ, gViewUpDirX, gViewUpDirY, gViewUpDirZ);
glFlush();
}
參考資料
1. 《 OpenGL Reference Manual 》,OpenGL參考手冊
2. 《OpenGL 編程指南》(《 OpenGL Programming Guide 》),Dave Shreiner,Mason Woo,Jackie Neider,Tom Davis 著,徐波譯,機械工業出版社
3. 《Nehe OpenGL Tutorials》,Nehe著,在 http://nehe.gamedev.net/ 上可以找到教程及相關的代碼下載,(有PDF版本教程下載)Nehe自己還做了一個面向對象的框架,作為演示程序來說,這樣的框架非常合適。也有 中文版 ,各取所需吧。
4. 《OpenGL入門學習》 ,eastcowboy著,這是我在網上找到的一個比較好的教程,較為完善,而且非常通俗。這是第一篇的地址: http://bbs.pfan.cn/post-184355.html
本OpenGL系列其他文章
1. 《 Win32 OpenGL 編程(1)Win32下的OpenGL編程必須步驟 》
2. 《 Win32 OpenGL編程(2) 尋找缺失的OpenGL函數 》
3. 《 Win32 OpenGL編程(3) 基本圖元(點,直線,多邊形)的繪制 》
4. 《 Win32 OpenGL編程(4) 2D圖形基礎(顏色及坐標體系進階知識) 》
5. 《 Win32 OpenGL編程(5)頂點數組詳細介紹 》
6.《 Win32 OpenGL編程(6) 踏入3D世界 》
7.《 Win32 OpenGL編程(7) 3D視圖變換——真3D的關鍵 》
應用舉例:《 Win32 OpenGL編程系列 2D例子 -- 七巧板圖形繪制 》
完整源代碼獲取說明
由于篇幅限制,本文一般僅貼出代碼的主要關心的部分,代碼帶工程(或者makefile)完整版(如果有的話)都能用Mercurial在Google Code中下載。文章以博文發表的日期分目錄存放,請直接使用Mercurial克隆下庫:
https://blog-sample-code.jtianling.googlecode.com/hg/
Mercurial使用方法見《 分布式的,新一代版本控制系統Mercurial的介紹及簡要入門 》
要是僅僅想瀏覽全部代碼也可以直接到google code上去看,在下面的地址:
http://code.google.com/p/jtianling/source/browse?repo=blog-sample-code
原創文章作者保留版權 轉載請注明原作者 并給出鏈接
write by 九天雁翎(JTianLing) -- blog.csdn.net/vagrxie
更多文章、技術交流、商務合作、聯系博主
微信掃碼或搜索:z360901061

微信掃一掃加我為好友
QQ號聯系: 360901061
您的支持是博主寫作最大的動力,如果您喜歡我的文章,感覺我的文章對您有幫助,請用微信掃描下面二維碼支持博主2元、5元、10元、20元等您想捐的金額吧,狠狠點擊下面給點支持吧,站長非常感激您!手機微信長按不能支付解決辦法:請將微信支付二維碼保存到相冊,切換到微信,然后點擊微信右上角掃一掃功能,選擇支付二維碼完成支付。
【本文對您有幫助就好】元
