opengl學習——攝像機

剛開始學習opengl,作的第一個實驗,就是顯示圓柱體html

這個經過opengl庫中的api函數gluCylinder()就能夠顯示出來,可是極其蛋疼的是,徹底看不出它是一個圓柱啊api

雖然能夠經過reshape()來從新定視角,可是每次運行程序,只能顯示一個視角,多麻煩啊。ide

第一個想作的就是解決攝像機問題,讓咱們能夠經過鼠標鍵盤交互,實現360度旋轉和放大縮小。函數

 

opengl中的投影有兩種,一個是(平行投影),一個是(透視投影)學習

(透視投影)符合人們心理習慣,近大遠小spa

因此如下的說明都是基於(透視投影)的。 ps:其實我還不懂平行投影.net

 

1. 使用透視投影code

首先main函數中添加orm

    glutReshapeFunc(reshape);

參數reshape()是函數名,接下來是reshape()函數htm

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(5.0, 5.0, 5.0, 0.0, 0.0, 0.0, 0, 1, 0);
}

而gluLookAt()函數,其實你能夠理解成攝像機,

前3個參數是你的眼睛,中間3個參數是你看着的地方,最後3個參數是你的相機的正上方

具體說明能夠參考這位大佬的文章

 http://blog.csdn.net/wangqinghao/article/details/14002077

 

我的理解,gluLookAt()定義了一個視景矩陣,把(世界座標系)轉換成(攝像機座標系),

而後由(攝像機座標系)來解釋(世界座標系)中物體的座標。

具體轉換說明,能夠參考

如下網址

http://www.360doc.com/content/14/1028/10/19175681_420515511.shtml

 或者我寫的數學基礎知識03

 

2.矩陣替換

opengl中支持使用編寫的uvn視景矩陣。

建立一個Point類(點),建立一個Vector類(向量),建立一個Camera類(攝像機)

根據gluLookAt()的9個參數,咱們一樣能夠計算出對應的uvn座標系

事先說明,這個攝像機是以世界座標系(0, 0, 0)爲中心來360度移動的,因此若是變換了中心,以後的函數都要作相對應的修改。

void setCamera(float eyex, float eyey, float eyez,
                   float centerx, float centery, float centerz,
                   float upx, float upy, float upz) 
    {
        eye.set(eyex, eyey, eyez);
        center.set(centerx, centery, centerz);
        up.setV(upx, upy, upz);

        n.setV(center, eye);
        n.normalize();
        u = n.cross(up);
        u.normalize();
        v = u.cross(n);
        v.normalize();

        R = eye.getDist();

        setModeViewMatrix();
    }

center這個點必須是(0, 0, 0);

若是不是(0,0,0),後面旋轉函數要作相對應的修改

參數說明:

eye是世界座標系的點,座標系變換涉及到了平移,因此必須保存下來。

R是視點中心到攝像機的距離,

函數說明:

normalize()是規範化函數,即令向量的模變爲1

cross()是叉乘函數,即求出兩個不平行向量決定的平面的(法向量)。

最終目的是使uvn兩兩垂直。

 

而後是矩陣設置

void setModeViewMatrix() {
        Vector pointV(eye.x, eye.y, eye.z);
        M[0] = u.x; M[1] = v.x; M[2] = -n.x; M[3] = 0;
        M[4] = u.y; M[5] = v.y; M[6] = -n.y; M[7] = 0;
        M[8] = u.z; M[9] = v.z; M[10] = -n.z; M[11] = 0;
        M[12] = -pointV.dot(u);
        M[13] = -pointV.dot(v);
        M[14] = pointV.dot(n);
        M[15] = 1;

        glMatrixMode(GL_MODELVIEW);
        glLoadIdentity();
//        gluLookAt(5.0, 5.0, 5.0, 0.0, 0.0, 0.0, 0, 1, 0);
//        gluLookAt(eye.x, eye.y, eye.z, 0.0, 0.0, 0.0, up.x, up.y, up.z);

        glMultMatrixf(M);  //這句話就是把矩陣M設爲視景矩陣
    }

函數說明:

dot()是點乘函數

 

這裏的矩陣爲何出現負號,和

數學基礎知識03

不同,由於向量是有方向的,方向有所改變,因此,必定要注意喔。

 

好了,而後咱們能夠看看效果,能夠發現和gluLookAt()沒有明顯的差別

 

3.攝像機旋轉

首先要肯定有多少種旋轉方式,

根據查找的資料,能夠分爲roll,yaw,pitch,具體說明,能夠參考大佬的文章

http://blog.csdn.net/lovehota/article/details/17374303

http://blog.csdn.net/hobbit1988/article/details/7956838

 

和上面的大佬們的鼠標交互代碼實現不同,本人沒有使用slide

如下是我修改後的yaw()函數和pitch()函數

void yaw(float angle) {
        float cs = cos(angle*PI / 180);
        float sn = sin(angle*PI / 180);
        Vector t(n);
        Vector s(u);
        n.setV(cs*t.x - sn*s.x, cs*t.y - sn*s.y, cs*t.z - sn*s.z);
        u.setV(sn*t.x + cs*s.x, sn*t.y + cs*s.y, sn*t.z + cs*s.z);

        eye.set(-R*n.x, -R*n.y, -R*n.z);

        setModeViewMatrix();
    }
void pitch(float angle) {
        float cs = cos(angle*PI / 180);
        float sn = sin(angle*PI / 180);
        Vector t(v);
        Vector s(n);
        v.setV(cs*t.x - sn*s.x, cs*t.y - sn*s.y, cs*t.z - sn*s.z);
        n.setV(sn*t.x + cs*s.x, sn*t.y + cs*s.y, sn*t.z + cs*s.z);

        eye.set(-R*n.x, -R*n.y, -R*n.z);

        setModeViewMatrix();
    }

這兩個函數,都添加了修改攝像機的點再世界座標系下的座標

舉個例子,你把攝像機往上移動,若是你的眼睛不跟上的攝像機,你能看見東西嗎?

 

鼠標交互函數

    glutMouseFunc(onMouse);
    glutMotionFunc(onMouseMove);

具體函數實現

void onMouse(int button, int state, int x, int y)
{
    if (button == GLUT_LEFT_BUTTON && state == GLUT_DOWN ) {
        LeftMouseOn = true;
        x11 = x;
        y11 = y;
    }
    else if (button == GLUT_RIGHT_BUTTON && state == GLUT_DOWN) {
        RightMouseOn = true;
        x11 = x;
        y11 = y;
    }
    else {
        LeftMouseOn = false;
        RightMouseOn = false;
    }
}

變量說明:

x11,y11是鼠標按下去的時候,從屏幕得到的點座標

 

而後是鼠標移動的相關函數

void onMouseMove(int x, int y)
{
    int dx = x - x11;
    int dy = y - y11;
//    int cnt = 10000;
    if (LeftMouseOn == true) {
        RotateX(dx);
        RotateY(dy);
    }
    else if (RightMouseOn == true) {
        RotateRoll(dx);
    }
    x11 = x;
    y11 = y;
    //x11 = x21;
    //y11 = y21;
}

重點說明:

只要鼠標在移動,都要修改當前鼠標的座標,

舉個例子:你從起點出發,向右跑出去了,可是若是你忽然向左,你算是向左嗎,不會,相對於起點,你仍是向右的。

 

函數說明:

RotateX()是水平方向旋轉

RotateY()是豎直方向旋轉

這裏的水平和豎直指的是屏幕的水平和豎直,不是內部物體的

RotateRoll()是攝像機自身的旋轉,n不變,旋轉u和v,即roll

void RotateX(float x_move)
{
    float part_theta = 30;
    float theta = x_move*part_theta*3.14/180;
    cam.yaw(theta);
}

void RotateY(float y_move)
{
    float part_theta = 30;
    float theta = y_move*part_theta*3.14 / 180;
    cam.pitch(theta);
    /*
    theta = theta / cnt;
    for (; cnt != 0; cnt--) {
        cam.pitch(theta);
    }*/
}

void RotateRoll(float x_move)
{
    float part_theta = 30;
    float theta = x_move*part_theta*3.14 / 180;
    cam.roll(theta);
}

數聽說明:

part_theta是(旋轉角度/每1單位移動距離)

 

4.攝像機放大縮小(僞)

放大縮小的原理,我是用攝像機和視點距離的遠近變化來理解的。

因此我實現的這個函數,與其說攝像機放大縮小,還不如說是你拿着攝像機向視點走過去。(由於近大遠小嘛)

 

首先是鍵盤交互函數

glutKeyboardFunc(keyboard);

而後是鍵盤操做函數

void keyboard(unsigned char key, int x, int y)
{
    if (key == 109){
        cam.bsChange(1);
    }
    else if (key == 110){
        cam.bsChange(-1);
    }
}
void bsChange(int d)
    {
        if (d > 0){
            R--;
            eye.set(-R*n.x, -R*n.y, -R*n.z);
        }
        else{
            R++;
            eye.set(-R*n.x, -R*n.y, -R*n.z);
        }

        setModeViewMatrix();
    }

109和110就是某兩個按鍵的碼,你們能夠經過printf找出另外2個鍵來使用

再次提醒:每次座標系變換(不管是旋轉仍是平移),都要從新設置視景矩陣

相關文章
相關標籤/搜索