opengl学习——摄像机

刚开始学习opengl,做的第一个实验,就是显示圆柱体

这个通过opengl库中的api函数gluCylinder()就可以显示出来,但是极其蛋疼的是,完全看不出它是一个圆柱啊

虽然可以通过reshape()来重新定视角,但是每次运行程序,只能显示一个视角,多麻烦啊。

第一个想做的就是解决摄像机问题,让我们可以通过鼠标键盘交互,实现360度旋转和放大缩小。

 

opengl中的投影有两种,一个是(平行投影),一个是(透视投影)

(透视投影)符合人们心理习惯,近大远小

所以以下的说明都是基于(透视投影)的。 ps:其实我还不懂平行投影

 

1. 使用透视投影

首先main函数中添加

    glutReshapeFunc(reshape);

参数reshape()是函数名,接下来是reshape()函数

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个键来使用

再次提醒:每次坐标系变换(无论是旋转还是平移),都要重新设置视景矩阵

 

转载于:https://www.cnblogs.com/keyncoin/p/5492099.html

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值