OpenGL學習進程(12)第九課:矩陣乘法實現3D變換

    本節是OpenGL學習的第九個課時,下面將詳細介紹OpenGL的多種3D變換和如何操做矩陣堆棧。html

 

    (1)3D變換:windows

    OpenGL中繪製3D世界的空間變換包括:模型變換、視圖變換、投影變換和視口變換。函數

    現實世界是一個3維空間,若是咱們要觀察一個物體,咱們能夠:oop

1.從不一樣的位置去觀察它。(視圖變換) 2.移動或者旋轉它,固然了,若是它只是計算機裏面的物體,咱們還能夠放大或縮小它。(模型變換) 3.若是把物體畫下來,咱們能夠選擇:是否須要一種「近大遠小」的透視效果。另外,咱們可能只但願看到物體的一部分,而不是所有(剪裁)。(投影變換) 4.咱們可能但願把整個看到的圖形畫下來,但它只佔據紙張的一部分,而不是所有。(視口變換)

    實現原理:學習

    OpenGL變換其實是經過矩陣乘法來實現。不管是移動、旋轉仍是縮放大小,都是經過在當前矩陣的基礎上乘以一個新的矩陣來達到目的。測試

    1)模型變換和視圖變換:動畫

    從"相對移動"的觀點來看,改變觀察點的位置與方向和改變物體自己的位置與方向具備等效性。所以這裏視圖變換和模型變換就一塊兒講,甚至在OpenGL中實現這兩行總功能所用的函數都是相同的。spa

    如何進行模型或視圖視圖變換:設計

    1.1.1準備:3d

1.glMatrixMode(GL_MODELVIEW);//先設置當前操做的矩陣爲「模型視圖矩陣」;
2.glLoadIdentity();//一般須要把此矩陣設爲單位矩陣;

    函數glMatrixMode(GLenum mode):

    mode告訴計算機哪個矩陣堆棧將是下面矩陣操做的目標,即將什麼矩陣設置爲當前矩陣,它的可選值有:

1.GL_MODELVIEW:表示接下來的矩陣操做都是針對模型視景矩陣堆棧,直到下一次調用這個函數並更改參數爲止。   
2.GL_PROJECTION:表示接下來的矩陣操做都是針對投影矩陣堆棧,直到下一次調用這個函數並更改參數爲止。
3.GL_TEXTURE:表示接下來的矩陣操做都是針對紋理矩陣堆棧,直到下一次調用這個函數並更改參數爲止。

    注意:在設置好當前矩陣以後,任何能夠作的操做只能是針對當前矩陣的。

    函數glLoadIdentity()

    將當前的用戶座標系的原點移到了屏幕中心,相似於一個復位操做:(若是進行過空間變換後再次調用這個函數則視圖回到初始狀態)

1.X座標軸從左至右,Y座標軸從下至上,Z座標軸從裏至外。
2.OpenGL屏幕中心的座標值是X和Y軸上的0.0f點。 
3.中心左面的座標值是負值,右面是正值。移向屏幕頂端是正值,移向屏幕底端是負值。 移入屏幕深處是負值,移出屏幕則是正值。

    1.1.2變換:

    進行模型和視圖變換,主要涉及到這幾個函數:

模型變換函數三個:縮放glScalef(),移動glTranslatef(),旋轉glRotatef().//模型變換的目的是設置模型的位置和方向
視圖變換函數一個:觀察gluLookAt().

    函數void glScalef()

    glScalef(2.0f,3.0f,4.0f):將模型按x,y,z方向分別拉伸了2,3,4倍。

    glScalef(1.0f,1.0f,-1.0f):將模型關於z軸翻轉了180°(即關於xy軸所在平面對稱)。

    函數glTranslatef()

    glTranslatef(-1.5f,0.0f,-6.0f):將模型向x軸負方向平移1.5,z軸負方向平移6.0。

    函數glRotatef()

    glRotatef(45,0.0f,0.0f,0.0f):將模型沿x軸旋轉45°。

    函數gluLookAt()

void gluLookAt(GLdouble eyex,GLdouble eyey,GLdouble eyez,//相機在世界座標的位置
               GLdouble centerx,GLdouble centery,GLdouble centerz,//相機鏡頭對準的物體在世界座標的位置
               GLdouble upx,GLdouble upy,GLdouble upz);//相機向上的方向在世界座標中的方向

    gluLookAt (0.0, 0.0, 2.0, 0.0, 0.0, 0.0, 1.0, 1.0, 0.0):相機在z軸上,歪45度(攝像機頂朝着(1.0,1.0,0.0)這個方向)看向原點。

    gluLookAt(0.0,0.0,5.0, 0.0,0.0,0.0, 0.0,1.0,0.0):相機在z軸上,攝像機頂朝向y軸(即上方),看向座標原點。 

    示例:將3D物體變換後從不一樣位置觀察:(glViewport()和glOrtho()http://www.cnblogs.com/yxnchinahlj/archive/2010/10/30/1865298.html)

#include <GL/glut.h>
#pragma comment(linker,"/subsystem:\"windows\" /entry:\"mainCRTStartup\"")

void axis(double length)
{
    glColor3f(1.0f, 1.0f, 1.0f);
    glPushMatrix();
    glBegin(GL_LINES);
    glVertex3d(0.0, 0.0, 0.0);
    glVertex3d(0.0, 0.0, length);
    glEnd();
    //將當前操做點移到指定位置
    glTranslated(0.0, 0.0, length - 0.2);
    glColor3f(1.0, 0.0, 0.0);
    glutWireCone(0.04, 0.3, 8, 8);
    glPopMatrix();
}
void SetupRC()
{
    glClearColor(0.5,0.5,0.5,1.0);//設置背景色爲灰色
}
void paint(void)
{
    glClear(GL_COLOR_BUFFER_BIT|GL_DEPTH_BUFFER_BIT);
    glPointSize(1.0f);
    glMatrixMode(GL_PROJECTION);
    glLoadIdentity();
    glOrtho(-3.0, 3.0, -3.0, 3.0, -3, 3);
    //gluLookAt(0, 0, 2, 0, 0, 0, 0, 1, 0);//正視圖
    gluLookAt(1, 1, 0.5, 0, 0, 0, 0, 0, 1);//斜視圖
    //gluLookAt(0, 2, 0, 0, 0, 0, 1, 0, 0);//俯視

    //畫座標系
    axis(2.0);

    glPushMatrix();
    glRotated(90.0, 0, 1.0, 0);//繞y軸正方向旋轉90度
    axis(2.0);
    glPopMatrix();

    glPushMatrix();
    glRotated(-90.0, 1.0, 0.0, 0.0);//繞x軸負方向旋轉
    axis(2.0);
    glPopMatrix();

    glRotated(90, 0, 1, 0);
    glutWireTeapot(0.5);

    glFlush();
}
void changeSize(int width, int height)
{
    /*GLfloat mid = width > height ? height : width;
    glViewport(0, 0, mid, mid);*/
    glMatrixMode(GL_PROJECTION);
    glLoadIdentity();
    if (width <= height)
        glOrtho(-3, 3, -3 * (GLfloat)height/(GLfloat)width, 3 * (GLfloat)height/(GLfloat)width, -3, 3);
    else
        glOrtho(-3 * (GLfloat)width / (GLfloat)height, 3 * (GLfloat)width / (GLfloat)height, -3, 3, -3, 3);
}
int main(int argc,char *argv[])
{
    glutInit(&argc,argv);
    glutInitDisplayMode(GLUT_SINGLE | GLUT_RGB | GLUT_DEPTH);
    glutInitWindowSize(400,400);
    glutInitWindowPosition(200,200);
    glutCreateWindow("模型視圖變換");
    SetupRC();
    glutReshapeFunc(changeSize);
    glutDisplayFunc(paint);
    glutMainLoop();
}

    遇到的問題:

    這三句話是一塊兒的,若是隻用第三句會出現"每在窗體點擊一下,窗體中的物體就會在當前視圖狀態下發生改變"的狀況。

glMatrixMode(GL_PROJECTION);
glLoadIdentity();//glLoadIdentity()函數設置爲單位矩陣,阻止了連續變換產生的累積效果
glOrtho(-3.0, 3.0, -3.0, 3.0, -3, 3);

    防止當窗體變大時圖形發生變形有兩種解決方法:

GLfloat mid = width > height ? height : width;
glViewport(0, 0, mid, mid);
glMatrixMode(GL_PROJECTION);
 glLoadIdentity();
 if (width <= height)
    glOrtho(-3, 3, -3 * (GLfloat)height/(GLfloat)width, 3 *(GLfloat)height/(GLfloat)width, -3, 3);
 else
    glOrtho(-3 * (GLfloat)width / (GLfloat)height, 3 * (GLfloat)width / (GLfloat)height, -3, 3, -3, 3);

    2)投影變換:

    投影變換就是定義一個可視空間(視景體),可視空間之外的物體不會被繪製到屏幕上。投影矩陣指定了可視區域的大小和形狀,在未使用投影變換時,默承認見座標範圍爲(-1.0,1.0),使用後能夠自定義在窗體顯示的空間座標範圍。

    準備:

    要對當前矩陣進行投影變換,必須首先指定當前矩陣爲投影矩陣並初始化單位矩陣:

glMatrixMode(GL_PROJECTION);
glLoadIdentity();

    OpenGL支持兩種類型的投影變換,即透視投影和正投影:

    1.2.1正投影(Orthographic Projection):(示例如上)

    正投影一般用於2D繪圖,它的最大一個特色是不管物體距離相機多遠,投影后的物體大小尺寸不變。這種投影一般用在建築藍圖繪製和計算機輔助設計等方面,這些行業要求投影后的物體尺寸及相互間的角度不變,以便施工或製造時物體比例大小正確。

    此種模式下,不須要設定照相機位置、方向以及視點的位置,也就是說能夠不須要gluLookAt函數。(此時默認是正投影)

    OpenGL正射投影函數共有兩個:

    void glOrtho(GLdouble left,GLdouble right,GLdouble bottom,GLdouble top, GLdouble near,GLdouble far)

    它建立一個平行視景體。實際上這個函數的操做是建立一個正射投影矩陣,而且用這個矩陣乘以當前矩陣。其中近裁剪平面是一個矩形,矩形左下角點三維空間座標是(left,bottom,-near),右上角點是(right,top,-near);遠裁剪平面也是一個矩形,左下角點空間座標是(left,bottom,-far),右上角點是(right,top,-far)。全部的near和far值同時爲正或同時爲負。若是沒有其餘變換,正射投影的方向平行於Z軸,且視點朝向Z負軸。這意味着物體在視點前面時far和near都爲負值,物體在視點後面時far和near都爲正值。

    void gluOrtho2D(GLdouble left,GLdouble right,GLdouble bottom,GLdouble top)

    它是一個特殊的正射投影函數,主要用於二維圖像到二維屏幕上的投影。它的near和far缺省值分別爲-1.0和1.0,全部二維物體的Z座標都爲0.0。所以它的裁剪面是一個左下角點爲(left,bottom)、右上角點爲(right,top)的矩形。

    實例以下:

#include <GL/glut.h>
#include <stdlib.h>
#pragma comment(linker,"/subsystem:\"windows\" /entry:\"mainCRTStartup\"")

void display(void)
{
    glClearColor(0.0, 0.0, 0.0, 0.0);
    glClear(GL_COLOR_BUFFER_BIT);
    glColor4f(1.0, 0.0, 0.0, 1.0);
    glRectf(-0.9, 0.9, 0.9, -0.9);
    glFlush();
}
void init(void)
{
    glClearColor(0.5, 0.5, 0.5, 0.5);
    glShadeModel(GL_FLAT);
}
void reshape(int w, int h)
{
    glViewport(0, 0, (GLsizei)w, (GLsizei)h);
    glMatrixMode(GL_PROJECTION);
    glLoadIdentity();
    //仍然是原來1:1的比例,因此起不到任何做用
    gluOrtho2D(-(GLdouble)h / h, (GLdouble)h / h, -(GLdouble)h / h, (GLdouble)h / h);
}
void changeSize(int w, int h)
{  /*至關於按窗體的顯示區域動態定義裁剪區域的大小
   而後填充到顯示區域時圖像比例並不發生變化。*/
    glViewport(0, 0, w, h);
    glMatrixMode(GL_PROJECTION);
    glLoadIdentity();
    float mid = w > h ? (w*1.0) / h : (h*1.0) / w;
    if (w<h)
        gluOrtho2D(-1.0, 1.0, -mid, mid);
    else
        gluOrtho2D(-mid, mid, -1.0, 1.0);
    glMatrixMode(GL_MODELVIEW);
    glLoadIdentity();
}
int main(int argc, char* argv[])
{
    glutInit(&argc, argv);
    glutInitDisplayMode(GLUT_SINGLE | GLUT_RGB);
    glutInitWindowPosition(200, 200);
    glutInitWindowSize(400, 400);
    glutCreateWindow("2D正投影變換");
    init();
    glutDisplayFunc(display);
    //glutReshapeFunc(reshape);此語句中的reshape函數實際上起不到任何做用
    glutReshapeFunc(changeSize);
    glutMainLoop();
    return 0;
}

    1.2.2透視投影:

    透視投影符合人們心理習慣:即離視點近的物體大,離視點遠的物體小,遠到極點即爲消失,成爲滅點。它的視景體相似於一個頂部和底部都被切除掉的棱椎,也就是棱臺。這個投影一般用於動畫、視覺仿真以及其它許多具備真實性反映的方面。

    運用透視投影時須要用gluLookAt設定照相機位置、照相機方向(通常設置爲(0,1,0))、以及視點位置。

    透視投影函數也有兩個:

    void glFrustum(GLdouble left,GLdouble Right,GLdouble bottom,GLdouble top, GLdouble near,GLdouble far);

    它建立一個透視視景體。其操做是建立一個透視投影矩陣,而且用這個矩陣乘以當前矩陣。這個函數的參數只定義近裁剪平面的左下角點和右上角點的三維空間座標,即(left,bottom,-near)和(right,top,-near);最後一個參數far是遠裁剪平面的Z負值,其左下角點和右上角點空間座標由函數根據透視投影原理自動生成。near和far表示離視點的遠近,它們總爲正值。

    void gluPerspective(GLdouble fovy,GLdouble aspect,GLdouble zNear, GLdouble zFar);

    它也建立一個對稱透視視景體,但它的參數定義於前面的不一樣,如圖所示。其操做是建立一個對稱的透視投影矩陣,而且用這個矩陣乘以當前矩陣。參數fovy定義視野在X-Z平面的角度,範圍是[0.0,180.0];參數aspect是投影平面寬度與高度的比率;參數zNear和Far分別是遠近裁剪面到眼睛的距離,它們總爲正值。

  

     透視變換的實例:

#include <GL/glut.h>
#include <math.h>
#pragma comment(linker,"/subsystem:\"windows\" /entry:\"mainCRTStartup\"")
//旋轉的步進值
static float fMoonRot = 0.0f;
static float fEarthRot = 0.0f;

void SetupRC()
{
    glClearColor(0.0f, 0.0f, 0.0f, 1.0f);
    glColor3f(0.0f, 1.0f, 0.0f);
    //打開深度測試,通常運用在繪製3D場景中
    glEnable(GL_DEPTH_TEST);
}

// 繪製場景(顯示回調函數)
void RenderScene()
{
    glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
    // 保存矩陣狀態(模型視圖矩陣)
    glPushMatrix();
    glMatrixMode(GL_MODELVIEW);
    glLoadIdentity();
    // 平移座標系,注意是相對於視覺座標的位置
    glTranslatef(0.0f, 0.0f, -300.0f);

    // 繪製紅色的太陽
    glColor3f(1.0f, 0.0f, 0.0f);//等價於glColor3ub(255, 0, 0);
    glutSolidSphere(25.0f, 30, 30);

    // 旋轉座標系(此座標系的中心是太陽)
    glRotatef(fEarthRot, 0.0f, 1.0f, 0.0f);
    // 繪製藍色的地球
    glColor3ub(0, 0, 255);
    // 平移座標系
    glTranslatef(100.0f, 0.0f, 0.0f);//代表太陽與地球的距離是100
    // 設置地球的旋轉步進
    fEarthRot += 5.0f;
    if (fEarthRot > 360.0f) {
        fEarthRot = 0.0f;
    }
    glutSolidSphere(15.0f, 30, 30);

    // 繪製月球
    glColor3ub(200, 200, 255);
    // 旋轉座標系(此座標系的中心是地球)
    glRotatef(fMoonRot, 0.0f, 1.0f, 0.0f);
    // 平移座標系
    glTranslatef(30.0f, 0.0f, 0.0f);
    // 設置月亮的旋轉步進
    fMoonRot += 20.0f;
    if (fMoonRot > 360.0f) {
        fMoonRot = 0.0f;
    }
    glutSolidSphere(6.0f, 30, 30);
    glPopMatrix();

    glutSwapBuffers();
}


void ChangeSize(GLsizei w, GLsizei h)//typdef int GLsizei 
{
    GLfloat fAspect;
    // 防止被0除
    if (0 == h) {
        h = 1;
    }
    glViewport(0, 0, w, h);
    fAspect = (GLfloat)w / (GLfloat)h;

    glMatrixMode(GL_PROJECTION);
    glLoadIdentity();
    // 定義平截頭體, 45度視野,近、遠平面爲1.0和425.0
    //gluPerspective(45.0f, fAspect, 100.0, 500);能達到相同的效果
    glFrustum(-100,100,-100,100,200,500);
}

// 計時器函數
void TimerFunc(int value)
{
    //每100毫秒調用一次函數
    glutPostRedisplay();
    glutTimerFunc(100, TimerFunc, 1);
}

int main(int argc, char *argv[])
{
    glutInit(&argc, argv);
    glutInitDisplayMode(GLUT_DOUBLE | GLUT_RGB);
    glutInitWindowPosition(200,200);
    glutInitWindowSize(600, 450);
    glutCreateWindow("透視測試");
    glutDisplayFunc(RenderScene);
    glutReshapeFunc(ChangeSize);
    // 設置計時器函數
    glutTimerFunc(100, TimerFunc, 1);
    SetupRC();
    glutMainLoop();

    return 0;
}

    遇到的問題:

    這裏顯示的是正面的透視視圖,視平線與z軸重合,而我想要觀察傾斜的俯視圖,用學習進程(9)的方法(使用glulookat函數)並無達到效果,而是用了下面的方法:

void ChangeSize(GLsizei w, GLsizei h)//typdef int GLsizei 
{
    GLfloat fAspect;
    // 防止被0除
    if (0 == h) {
        h = 1;
    }
    glViewport(0, 0, w, h);
    fAspect = (GLfloat)w / (GLfloat)h;

    glMatrixMode(GL_PROJECTION);
    glLoadIdentity();
    // 定義平截頭體, 45度視野,近、遠平面爲1.0和425.0
 gluLookAt(0,0.5,1,0,0,0,0,-1,0);
    gluPerspective(45.0f, fAspect, 100.0, 500);
}

    在學習進程(9)中用glulookat()函數改換視點時,是這樣作的:(在本例中並無任何效果)

void reshape(int w, int h)
{
    glViewport(0, 0, (GLsizei)w, (GLsizei)h);
    glMatrixMode(GL_PROJECTION);
    glLoadIdentity();
    gluPerspective(60.0, (GLfloat)w / (GLfloat)h, 1.0, 20.0);
    glMatrixMode(GL_MODELVIEW);
    glLoadIdentity();
    gluLookAt(0.0, 6.0, 5.0, 0.0, 0.0, 0.0, 0.0, 1.0, 0.0);
}

    兩個程序的另外一個不一樣點在於:繪製場景時,以前的程序每繪製一次物體就會有一次進出棧的操做,只有一個相對座標系;而當前程序一次完成,每進行一次座標系平移或旋轉就換一個相對座標系,效果是累積的。它解決了月亮繞地球轉時不需考慮太陽的依賴關係。(詳解見後文)

    glulookat()函數和透視變換函數的組合用法依然不清晰。

    3)視口變換:(示例:http://www.cnblogs.com/MenAngel/p/5630475.html)

    當一切工做已經就緒,只須要把像素繪製到屏幕上了。這時候還剩最後一個問題:應該把像素繪製到窗口的哪一個區域呢?一般狀況下,默認是完整的填充整個窗口,但咱們徹底能夠只填充一半。(即:把整個圖象填充到一半的窗口內)

    Viewport(int x, int y, int width, int height)

    OpenGL使用此函數來定義視口區域,它表明了即將繪製的圖形顯示在2D窗體的位置和大小。

 

    (2)操做矩陣堆棧:

    OpenGL中對圖形的變換是基於矩陣操做的,有可能須要先保存某個矩陣,過一段時間再恢復它:

glPushMatrix()
glPopMatrix()

    OpenGL規定堆棧的容量至少能夠容納32個矩陣。

    爲何要使用矩陣堆棧:

    通常咱們將須要執行的縮放、平移等操做放在glPushMatrix和glPopMatrix之間。glPushMatrix()和glPopMatrix()的配對使用能夠消除上一次的變換對本次變換的影響,使本次變換是以世界座標系的原點爲參考點進行。

    矩陣堆棧對複雜模型運動過程當中的多個變換操做之間的聯繫與獨立十分有利。由於全部矩陣操做函數如glLoadMatrix()、glMultMatrix()、glLoadIdentity()等只處理當前矩陣或堆棧頂部矩陣,這樣堆棧中下面的其它矩陣就不受影響。

    原理詳解:

1.OpenGL中模型視圖矩陣變換是一個馬爾科夫過程:上一次的變換結果對本次變換有影響,上次模型視圖變換後物體在世界座標系下的位置是本次模型變換的起點。默認時本次變換和上次變換不獨立。
2.OpenGL物體建模其實是分兩步走的。第一步,在世界座標系的原點位置繪製出該物體;第二步,經過模型視圖變換矩陣對世界座標系原點處的物體進行仿射變換,將該物體移動到世界座標系的目標位置處。
3.將模型視圖變換放在glPushMatrix和glPopMatrix之間可使本次變換和上次變換獨立。
4.凡是使用glPushMatrix()和glPopMatrix()的程序通常能夠斷定是採用世界座標系建模。既世界座標系固定,模型視圖矩陣移動物體。

     示例:

#include<GL/glut.h>
#pragma comment(linker,"/subsystem:\"windows\" /entry:\"mainCRTStartup\"")

static int spin = 0;
void init()
{
    glClearColor(0.3,0.3,0.3,1.0);
    glShadeModel(GL_SMOOTH);
    glEnable(GL_LIGHTING);
    glEnable(GL_LIGHT0);
    glEnable(GL_DEPTH_TEST);
}
void display()
{
    glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
    GLfloat position[] = { 0.0, 0.0, 1.5, 1.0 };
    //OpenGL中的模型視圖變換矩陣全是右乘當前變換矩陣
    glPushMatrix(); //1.保存矩陣狀態
    //先畫燈
    glTranslatef(0.0, 0.0, -5.0);
    glPushMatrix();//2.因爲前後畫的物體使用同一個世界座標系,所以這裏入棧消除本次操做對下次操做的影響 
    //將0號光源和矩形線框繪製在一塊兒
    glRotated((GLdouble)spin, 1.0, 0.0, 0.0);
    glLightfv(GL_LIGHT0, GL_POSITION, position);
    glTranslated(0.0, 0.0, 1.5);
    glDisable(GL_LIGHTING);
    glColor3f(1.0, 0.0, 0.0);
    glLineWidth(2.0f);
    glutWireCube(0.1);
    glEnable(GL_LIGHTING);
    //再畫被照物體
    glPopMatrix(); 
    //直接繪製在世界座標的(0,0,0)位置
    glutSolidSphere(0.5, 40, 40);

    glPopMatrix(); //取出矩陣狀態,返回到單位矩陣
    glFlush();
}
void reshape(int w, int h)
{
    glViewport(0, 0, (GLsizei)w, (GLsizei)h);
    glMatrixMode(GL_PROJECTION);
    glLoadIdentity();
 //gluLookAt(1.0, 0, 0, 0, 0, 0, 0, 1, 10);這句話取消註釋後是左視圖,俯瞰旋轉圖。
    gluPerspective(40.0, (GLfloat)w / (GLfloat)h, 3, 20.0);
    glMatrixMode(GL_MODELVIEW);
    glLoadIdentity();

}
void mouse(int button, int state, int x, int y)
{
    switch (button)
    {
    case GLUT_LEFT_BUTTON://鼠標左鍵點擊事件
        if (state == GLUT_DOWN)
        {
            spin = (spin + 30) % 360;
            glutPostRedisplay();//場景從新繪製
        }
        break;
    default:
        break;
    }
}
int main(int argc, char * argv[])
{
    glutInit(&argc, argv);
    glutInitDisplayMode(GLUT_RGB | GLUT_SINGLE | GLUT_DEPTH);
    glutInitWindowPosition(100, 100);
    glutInitWindowSize(500, 500);
    glutCreateWindow("矩陣堆棧操做");
    init();
    glutDisplayFunc(display);
    glutReshapeFunc(reshape);
    glutMouseFunc(mouse);
    glutMainLoop();
    return 0;
}

 

 

    (3)空間變換繪製實例:(空間變換矩陣堆棧綜合應用,僅做了解)

#include <GL/glut.h>
#include <glaux.h>
#pragma comment(linker,"/subsystem:\"windows\" /entry:\"mainCRTStartup\"")
void myinit(void);
void drawPlane(void);
void CALLBACK elbowAdd(void);
void CALLBACK elbowSubtract(void);
void CALLBACK shoulderAdd(void);
void CALLBACK shoulderSubtract(void);
void CALLBACK display(void);
void CALLBACK myReshape(GLsizei w, GLsizei h);

static int shoulder = 0, elbow = 0;
void CALLBACK elbowAdd(void)
{
    elbow = (elbow + 5) % 360;
}
void CALLBACK elbowSubtract(void)
{
    elbow = (elbow - 5) % 360;
}
void CALLBACK shoulderAdd(void)
{
    shoulder = (shoulder + 5) % 360;
}
void CALLBACK shoulderSubtract(void)
{
    shoulder = (shoulder - 5) % 360;
}
void CALLBACK display(void)
{
    glClear(GL_COLOR_BUFFER_BIT);
    glColor3f(0.0, 1.0, 1.0);
    glPushMatrix(); // 將此句註釋掉後能夠發現上一次的變換結果對當前變換有影響,加上了glPushMatrix的目的是讓各次變換相互獨立。
    glTranslatef(-0.5, 0.0, 0.0); // 將旋轉後的Wirebox向左移動0.5個單位
    glRotatef((GLfloat)shoulder, 0.0, 0.0, 1.0); //看到shoulder是相對於0的絕對角度,不是基於上一次位置的相對角度。
    glTranslatef(1.0, 0.0, 0.0); //Step 1將WireBox向右移動一個單位
    // void auxWireBox(GLdouble width,GLdouble height,GLdouble depth)
    auxWireBox(2.0, 0.2, 0.5); //這個WireBox以x=1爲中心,width=2從該中心開始向兩邊各延伸1個單位。
    glTranslatef(1.0, 0.0, 0.0);
    glRotatef((GLfloat)elbow, 0.0, 0.0, 1.0);
    glTranslatef(0.8, 0.0, 0.0);
    auxWireBox(1.6, 0.2, 0.5);
    glPopMatrix(); // 能夠看出glPushMatrix和glPopMatrix之間的變換效果被消除。清除上一次對modelview矩陣的修改。
    glFlush();
}
void myinit(void)
{
    glShadeModel(GL_FLAT);
}
void CALLBACK myReshape(GLsizei w, GLsizei h)
{
    glViewport(0, 0, w, h);
    glMatrixMode(GL_PROJECTION);
    glLoadIdentity();
    gluPerspective(65.0, (GLfloat)w / (GLfloat)h, 1.0, 20.0);
    glMatrixMode(GL_MODELVIEW);
    glLoadIdentity();
    glTranslatef(0.0, 0.0, -5.0);
}
void main(void)
{
    auxInitDisplayMode(AUX_SINGLE | AUX_RGBA);
    auxInitPosition(200,200, 600, 400);
    auxInitWindow("機械手臂");
    myinit();
    auxKeyFunc(AUX_LEFT, shoulderSubtract);
    auxKeyFunc(AUX_RIGHT, shoulderAdd);
    auxKeyFunc(AUX_UP, elbowAdd);
    auxKeyFunc(AUX_DOWN, elbowSubtract);
    auxReshapeFunc(myReshape);
    auxMainLoop(display);
}

 

    (4)相關知識:

    1)CALLBACK標誌:(#define CALLBACK _stdcall)

    用CALLBACK標誌一個函數爲持續回調函數。

    void CALLBACK changeSize(void)等價於void changeSize()。

    2)auxInitPosition(int,int,int,int):

    有初始化窗體的位置和大小兩重功能:

auxInitPosition(200,200, 600, 400);
等價於
glutInitWindowPosition(200, 200);
glutInitWindowSize(600, 400);

    3)auxKeyFunc();

    向鍵盤按鍵上綁定函數指針,每一次按鍵操做都會引起此函數的一次回調。按鍵包括:

AUX_LEFT
AUX_RIGHT
AUX_UP
AUX_DOWN
相關文章
相關標籤/搜索