OpenGL-旋轉平移與縮放

OpenGL裏面的旋轉縮放與平移其實就是座標系的相關變換,不過由於初學者(像我)很容易被OpenGL裏面各種座標系搞暈而難以理解,現在將我的理解記錄如下。PS: 由於學校課程要求,使用的是過時的固定管線。

爲了清楚地理解這三種變換,我們只關心兩個座標系:建模座標系世界座標系。眼座標之類的使用默認值。文末有一段程序代碼,繪製了一個正方體,並實現了這三種變換。

世界座標系(WC):你現在正對繪圖窗口,右方是世界座標系的x軸,上方是y軸,從屏幕指向你的是z軸。

模型座標系(MC):使用glVertex() 之類的函數定義圖元時用的座標系,初始時MC和WC是重合的。

下文中的圖,用藍色表示MC,黑色表示WC

coordinate

旋轉

旋轉時世界座標系不變,但是建模座標系會旋轉,所以旋轉之後MC和WC是不重合的。

旋轉需要選定一個向量以及一個角度,相應的函數爲glRotatef()注意:這個向量指的是建模座標系下的一個向量,建模座標系繞着這個向量旋轉。

例如:繞y軸旋轉30度,向量爲(0, 1, 0),角度爲30。角度可正可負,opengl裏面的是右手座標系,所以旋轉的正方向就是右手大拇指朝向旋轉向量所指方向時,四個手指所指方向。下圖爲繞(0, 1, 0)旋轉30度。

繞y旋轉30度之後

這樣其實也不難理解,但是當時被自己寫的代碼坑到了。

文末的代碼中,旋轉是在一對glPushMatrix()glPopMatrix() 中的,所以你在屏幕上看到的圖形是由最開始的MC旋轉得到的,而不是由上次旋轉後的MC再次旋轉得到的。

在我寫的程序中顯示的圖形是由繞x, y, z軸依次旋轉而得到的(當然也可以不繞着座標軸),按下xyz鍵會改變繞xyz軸旋轉的角度r_x, r_y, r_z,然後依次地繞xyz軸旋轉而得到最終顯示的圖形。

現在有一種情形,繞xyz軸旋轉的角度爲r_x, r_y, r_z且均不爲0,圖形已經繪製好了,我現在按着y鍵,使r_y一直增加,我看到的是圖形繞着y軸轉動了一定角度嗎?

不是。由於是依次繞着xyz軸旋轉,r_y增加,所以受影響的只是基於最開始的MC繪製時,繞y軸旋轉的角度,但是繞y軸之後還有繞z軸轉動啊,最終的到的圖形與之前旋轉了r_x,r_y,r_z時相比,並不是繞y軸旋轉。如果想直觀觀察到,可以編譯運行下面的代碼,先胡亂按xyz,然後按着y鍵不放。

平移

平移時MC相對與WC的位置不變,但是MC裏面所有的座標都將在原來的基礎上加上偏移量(dx, dy, dz)。使用的函數爲glTranslatef() ,平移相對來說比較容易理解,不詳細敘述了。

縮放

先說說現實中的縮放,如果你想要一個物體變小,有兩個方法:

  • 增加你和物體之間的距離
  • 對物體施加魔法,改變它的實際尺寸

同理,OpenGL裏面你如果想要對一個圖形進行縮放,可以用glOrtho() 之類的函數調整投影區域,也可以使用glScalef() 等將圖形的各個座標縮小或放大。

#include <iostream>
#include <GL/gl.h>
#include <GL/glu.h>
#include <GL/glut.h>
#include <math.h>
#include <unistd.h>

/*---------------全局變量---------------------*/
//--------------------------------------------
const double PI = 3.14159;
//旋轉角度
GLfloat r_xyz[3] = {0.0f};

//平移偏移量
GLfloat dx=0, dy=0, dz=0;

//縮放比例
double radio = 1.0d;

int cx = 200, cy = 200; //中心座標窗口
//窗口大小
int w = 400;
int h = 400;

//標記是否着色,默認着色
int mode = 1;

/*------------------函數聲明--------------------*/
//---------------------------------------------
void onDisplay();
void onReshape(int , int);
void Drawing();
void myRotate();
void rgb(int r, int g, int b);
void specialKeys(int key, int x, int y);


void rgb(int r, int g, int b)
{
    glColor3f(r/255.0f, g/255.0f, b/255.0f);
}


void onDisplay()
{
    glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
    glEnable(GL_DEPTH_TEST);
    Drawing();
    glutSwapBuffers();
}

void onReshape(int width, int height)
{
    GLfloat aspect = (GLfloat) w/(GLfloat)h;
    GLfloat nRange = 400;
    w = width;
    h = height;
    int min = w > h ? h : w;
    glViewport(0, 0, min, min);
    glMatrixMode(GL_PROJECTION);
    glLoadIdentity();
    glOrtho(-nRange, nRange, -nRange , nRange, -nRange, nRange);
    glMatrixMode(GL_MODELVIEW);
    glLoadIdentity();
    glutSwapBuffers();
    glutPostRedisplay();
}

void myRotate()
{
    int vec[3][3] = {
        {1, 0, 0},
        {0, 1, 0},
        {0, 0, 1}
    };
    for (int i = 0 ; i <= 2 ; i ++)
    {
        glRotatef(r_xyz[i], vec[i][0], vec[i][1], vec[i][2]);
    }
}
void Drawing()
{
    glPushMatrix();
    glLoadIdentity();
    //旋轉
    myRotate();
    glScalef(radio, radio, radio);
    glTranslatef(dx, dy, dz);

    if (mode == 2)
    {
        glPolygonMode(GL_FRONT_AND_BACK, GL_LINE);
        glLineWidth(3);
    }
    else
    {
        glPolygonMode(GL_FRONT_AND_BACK, GL_FILL);
    }

    //頂點向量
    //下面表示一個正方體
    int A[] = {-100, -100, 100};
    int a[] = {-100, -100, -100};
    int B[] = {100, -100, 100};
    int b[] = {100, -100, -100};
    int C[] = {100, 100, 100};
    int c[] = {100, 100, -100};
    int D[] = {-100, 100, 100};
    int d[] = {-100, 100, -100};


    glBegin(GL_QUADS);
        //front  tomato
        rgb(234, 67, 53);
        glVertex3iv(A);
        glVertex3iv(B);
        glVertex3iv(C);
        glVertex3iv(D);

        //fruit salad
        rgb(52, 168, 83);
        glVertex3iv(D);
        glVertex3iv(C);
        glVertex3iv(c);
        glVertex3iv(d);

        //gray
        rgb(120, 120, 120);
        glVertex3iv(d);
        glVertex3iv(c);
        glVertex3iv(b);
        glVertex3iv(a);

        //white
        rgb(255, 255, 255);
        glVertex3iv(a);
        glVertex3iv(b);
        glVertex3iv(B);
        glVertex3iv(A);

        //yellow
        rgb(251, 188, 5);
        glVertex3iv(a);
        glVertex3iv(A);
        glVertex3iv(D);
        glVertex3iv(d);

        //blue
        rgb(66, 132, 243);
        glVertex3iv(B);
        glVertex3iv(C);
        glVertex3iv(c);
        glVertex3iv(b);

    glMatrixMode(GL_MODELVIEW);
    glLoadIdentity();

    glEnd();
    glPopMatrix();
    glutSwapBuffers();
}

void keyFunc(unsigned char key, int x, int y)
{
    switch(key)
    {
        case 'z': r_xyz[2] += 2; break;
        case 'Z': r_xyz[2] -= 2; break;
        case 'y': r_xyz[1] += 2; break;
        case 'Y': r_xyz[1] -= 2; break;
        case 'x': r_xyz[0] += 2; break;
        case 'X': r_xyz[0] -= 2; break;

        case '-': if (radio > 0)radio -= 0.01; break;
        case '+': radio += 0.01; break;
        case 'q': exit(0);

        case '1': mode = 1;break;
        case '2': mode = 2; break;
    }
    glutPostRedisplay();
}

void specialKeys(int key, int x, int y)
{
    switch(key)
    {
        case GLUT_KEY_UP: dy += 4;break;
        case GLUT_KEY_DOWN: dy -= 4; break;
        case GLUT_KEY_LEFT: dx -= 4; break;
        case GLUT_KEY_RIGHT: dx += 4; break;
    }
    glutPostRedisplay();
}

int main(int argc, char** argv)
{
    glutInit(&argc, argv);
    glutInitDisplayMode(GLUT_DOUBLE | GLUT_RGB | GLUT_DEPTH);
    glutInitWindowPosition(10, 10);
    glutInitWindowSize(400, 400);
    glutCreateWindow("this is not what I wanted");

    glutDisplayFunc(onDisplay);
    glutReshapeFunc(onReshape);
    glutKeyboardFunc(keyFunc);
    glutSpecialFunc(specialKeys);

    glutMainLoop();
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章