基于OpenGL的三维摄像机实现

使用gluLookAt确定摄像机位置

函数原型

void gluLookAt(GLdouble eyex,GLdouble eyey,GLdouble eyez,GLdouble centerx,GLdouble centery,GLdouble centerz,GLdouble upx,GLdouble upy,GLdouble upz);

参数说明

第一组eyex, eyey,eyez 相机在世界坐标的位置
第二组centerx,centery,centerz 相机镜头对准的物体在世界坐标的位置
第三组upx,upy,upz 相机向上的方向在世界坐标中的方向

摄像机移动

摄像机移动时,在自身坐标系中的朝向并不改变,因此只需要改变摄像机在世界坐标系中的位置坐标即可。

摄像机旋转

关于OpenGL坐标系,简单地说就是窗口中心是原点,水平向右是x轴正方向,垂直向上是y轴正方向,垂直屏幕向外是z轴正方向。
摄像机方向与xOy平面,xOz平面,yOz平面都会呈一定的角度。
在摄像机旋转时,摄像机位置不改变,仅改变朝向。


基于OpenGL的三维摄像机实现_第1张图片
  • pitch是围绕X轴旋转,也叫做俯仰角
  • yaw是围绕Y轴旋转,也叫偏航角
  • roll是围绕Z轴旋转,也叫翻滚角

如图,摄像机的旋转有俯仰(pitch),偏航(yaw)和翻滚(roll)三种方式。

摄像机实现

class Camera
{
public:
    Camera();

    float camera_x, camera_y, camera_z;  // 摄像机位置坐标
    float lookat_x, lookat_y, lookat_z;  // 摄像机朝向坐标

    void YawCamera(float fAngle);  // yaw方法
    void PitchCamera(float fAngle);  // pitch方法
    void WalkStraight(float fSpeed);  // 前后移动方法
    void WalkTransverse(float fSpeed); // 左右移动方法

    float angle;  // 每次旋转角度
    float speed;  // 每次移动距离
    float sight;  // 视野
    float rad_yz, rad_xz;  // 摄像机朝向与yOz平面,xOz平面所成弧度
    float rotate_yz, rotate_xz;  //摄像机朝向与yOz平面,xOz平面所成角度

    float PI;
};

由于操作按键(WASD与方向键)的原因,没有实现roll方法与上下移动方法。

Camera::Camera()  // 摄像机的构造函数
{
    PI = 3.1415;

    angle = 3;
    speed = 0.3;
    sight = 100;

    rotate_yz = 0.0f;
    rotate_xz = -90.0f;
    rad_yz = rotate_yz*PI / 180.0f;
    rad_xz = rotate_xz*PI / 180.0f;

    camera_x = 0.0f;
    camera_y = 0.0f;
    camera_z = 8.0f;

    lookat_x = camera_x + sight*cos(rad_yz)*cos(rad_xz);
    lookat_y = camera_y + sight*sin(rad_yz);
    lookat_z = camera_z + sight*cos(rad_yz)*sin(rad_xz);
}
void Camera::YawCamera(float fAngle)  // yaw方法实现
{
    rotate_xz = (int)(rotate_xz + fAngle) % 360;
    rad_xz = rotate_xz*PI / 180.0f;

    lookat_x = camera_x + sight*cos(rad_yz)*cos(rad_xz);
    lookat_y = camera_y + sight*sin(rad_yz);
    lookat_z = camera_z + sight*cos(rad_yz)*sin(rad_xz);
}
void Camera::PitchCamera(float fAngle)  // pitch方法实现
{
    rotate_yz = (int)(rotate_yz + fAngle) % 360;
    rad_yz = rotate_yz*PI / 180.0f;

    lookat_x = camera_x + sight*cos(rad_yz)*cos(rad_xz);
    lookat_y = camera_y + sight*sin(rad_yz);
    lookat_z = camera_z + sight*cos(rad_yz)*sin(rad_xz);
}

void Camera::WalkStraight(float fSpeed)  // 前后移动方法实现
{
    camera_x += fSpeed*cos(rad_yz)*cos(rad_xz);
    camera_y += fSpeed*sin(rad_yz);
    camera_z += fSpeed*cos(rad_yz)*sin(rad_xz);

    lookat_x = camera_x + sight*cos(rad_yz)*cos(rad_xz);
    lookat_y = camera_y + sight*sin(rad_yz);
    lookat_z = camera_z + sight*cos(rad_yz)*sin(rad_xz);
}
void Camera::WalkTransverse(float fSpeed)  // 左右移动方法实现
{
    camera_x += fSpeed*cos(rad_yz)*sin(rad_xz);
    camera_z -= fSpeed*cos(rad_yz)*cos(rad_xz);

    lookat_x = camera_x + sight*cos(rad_yz)*cos(rad_xz);
    lookat_y = camera_y + sight*sin(rad_yz);
    lookat_z = camera_z + sight*cos(rad_yz)*sin(rad_xz);
}

相关计算方法就是高中数学的立体几何+三角函数知识

在OpenGL中实现

#include 
#define GLUT_DISABLE_ATEXIT_HACK
#include 
#include "Camera.h"

int window_width = 640;
int window_height = 480;

Camera camera;

void setProjection()
{
    if (0 == window_height)
    {
        window_height = 1;
    }
    double ratio = 1.0 * window_width / window_height;

    glMatrixMode(GL_PROJECTION);
    glLoadIdentity();
    gluPerspective(45, ratio, 0.01, 100);
    glViewport(0, 0, window_width, window_height);
    glMatrixMode(GL_MODELVIEW);
    glLoadIdentity();
    gluLookAt(camera.camera_x, camera.camera_y, camera.camera_z, camera.lookat_x, camera.lookat_y, camera.lookat_z, 0.0f, 1.0f, 0.0f);
}

void setLight()
{
    GLfloat ambient[] = { 1.0,1.0,1.0,1.0 };
    GLfloat diffuse[] = { 1.0,1.0,1.0,1.0 };
    GLfloat position[] = { 10.0,10.0,0.0,1.0 };

    glLightfv(GL_LIGHT0, GL_AMBIENT, ambient);
    glLightfv(GL_LIGHT0, GL_DIFFUSE, diffuse);
    glLightfv(GL_LIGHT0, GL_POSITION, position);

    glEnable(GL_LIGHT0);
    glEnable(GL_LIGHTING);
}

void init(void)
{
    glClearColor(0.0, 0.0, 0.0, 0.0);
    glDepthFunc(GL_LESS);
    glEnable(GL_DEPTH_TEST);
    glPixelStorei(GL_UNPACK_ALIGNMENT, 1);
    setProjection();
    setLight();
}

void draw()
{
    glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
    glEnable(GL_DEPTH_TEST);
    glutSolidTeapot(1);
    glFlush();
}

void display()
{
    glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
    glPixelStorei(GL_UNPACK_ALIGNMENT, 1);
    init();
    draw();
}

void SpecialKeys(int key, int x, int y)
{
    if (key == GLUT_KEY_UP)
        camera.PitchCamera(camera.angle);

    if (key == GLUT_KEY_DOWN)
        camera.PitchCamera(-camera.angle);

    if (key == GLUT_KEY_LEFT)
        camera.YawCamera(-camera.angle);

    if (key == GLUT_KEY_RIGHT)
        camera.YawCamera(camera.angle);
    
    glutPostRedisplay(); 
}

void KeyboardKeys(unsigned char key, int x, int y)
{
    if (key == 'w')
        camera.WalkStraight(camera.speed);

    if (key == 's')
        camera.WalkStraight(-camera.speed);

    if (key == 'a')
        camera.WalkTransverse(camera.speed);

    if (key == 'd')
        camera.WalkTransverse(-camera.speed);

    glutPostRedisplay();
}

void main()
{
    glutInitDisplayMode(GLUT_SINGLE | GLUT_RGB | GLUT_DEPTH);
    glutInitWindowSize(640, 480);
    glutCreateWindow("Teapot");
    glutDisplayFunc(display);
    glutSpecialFunc(SpecialKeys);
    glutKeyboardFunc(KeyboardKeys);
    glutMainLoop();
}

实现效果

【画质感人】

基于OpenGL的三维摄像机实现_第2张图片

基于OpenGL的三维摄像机实现_第3张图片

源码及demo

戳这里

你可能感兴趣的:(基于OpenGL的三维摄像机实现)