基于OpenGL的三維攝像機(jī)實(shí)現(xiàn)

使用gluLookAt確定攝像機(jī)位置

函數(shù)原型

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

參數(shù)說明

第一組eyex, eyey,eyez 相機(jī)在世界坐標(biāo)的位置
第二組centerx,centery,centerz 相機(jī)鏡頭對準(zhǔn)的物體在世界坐標(biāo)的位置
第三組upx,upy,upz 相機(jī)向上的方向在世界坐標(biāo)中的方向

攝像機(jī)移動

攝像機(jī)移動時(shí),在自身坐標(biāo)系中的朝向并不改變,因此只需要改變攝像機(jī)在世界坐標(biāo)系中的位置坐標(biāo)即可。

攝像機(jī)旋轉(zhuǎn)

關(guān)于OpenGL坐標(biāo)系,簡單地說就是窗口中心是原點(diǎn),水平向右是x軸正方向,垂直向上是y軸正方向,垂直屏幕向外是z軸正方向。
攝像機(jī)方向與xOy平面,xOz平面,yOz平面都會呈一定的角度。
在攝像機(jī)旋轉(zhuǎn)時(shí),攝像機(jī)位置不改變,僅改變朝向。


  • pitch是圍繞X軸旋轉(zhuǎn),也叫做俯仰角
  • yaw是圍繞Y軸旋轉(zhuǎn),也叫偏航角
  • roll是圍繞Z軸旋轉(zhuǎn),也叫翻滾角

如圖,攝像機(jī)的旋轉(zhuǎn)有俯仰(pitch),偏航(yaw)和翻滾(roll)三種方式。

攝像機(jī)實(shí)現(xiàn)

class Camera
{
public:
    Camera();

    float camera_x, camera_y, camera_z;  // 攝像機(jī)位置坐標(biāo)
    float lookat_x, lookat_y, lookat_z;  // 攝像機(jī)朝向坐標(biāo)

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

    float angle;  // 每次旋轉(zhuǎn)角度
    float speed;  // 每次移動距離
    float sight;  // 視野
    float rad_yz, rad_xz;  // 攝像機(jī)朝向與yOz平面,xOz平面所成弧度
    float rotate_yz, rotate_xz;  //攝像機(jī)朝向與yOz平面,xOz平面所成角度

    float PI;
};

由于操作按鍵(WASD與方向鍵)的原因,沒有實(shí)現(xiàn)roll方法與上下移動方法。

Camera::Camera()  // 攝像機(jī)的構(gòu)造函數(shù)
{
    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方法實(shí)現(xiàn)
{
    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方法實(shí)現(xiàn)
{
    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)  // 前后移動方法實(shí)現(xiàn)
{
    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)  // 左右移動方法實(shí)現(xiàn)
{
    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);
}

相關(guān)計(jì)算方法就是高中數(shù)學(xué)的立體幾何+三角函數(shù)知識

在OpenGL中實(shí)現(xiàn)

#include <stdlib.h>
#define GLUT_DISABLE_ATEXIT_HACK
#include <glut.h>
#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();
}

實(shí)現(xiàn)效果

【畫質(zhì)感人】


源碼及demo

戳這里

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

  • OpenGL本身沒有攝像機(jī)的概念,但我們可以通過把場景中的所有物體往相反方向移動的方式來模擬出攝像機(jī),這樣感覺就像...
    IceMJ閱讀 2,781評論 0 7
  • 版本記錄 前言 OpenGL 圖形庫項(xiàng)目中一直也沒用過,最近也想學(xué)著使用這個(gè)圖形庫,感覺還是很有意思,也就自然想著...
    刀客傳奇閱讀 3,298評論 0 3
  • 本文主要解決一個(gè)問題: 如何創(chuàng)建一個(gè)FPS攝像機(jī)? 引言 在前一章中,我們討論了觀察矩陣以及如何使用變換矩陣移動場...
    閃電的藍(lán)熊貓閱讀 11,310評論 18 9
  • 1 前言 OpenGL渲染3D模型離不開空間幾何的數(shù)學(xué)理論知識,而本篇文章的目的就是對空間幾何進(jìn)行簡單的介紹,并對...
    RichardJieChen閱讀 7,555評論 1 11
  • 天渺渺而望兮,乾坤浩瀚應(yīng)無垠。 時(shí)滔滔而逝兮,幼兒倏忽欲加冠。 飔徐徐兮撫我面,余心惶惶以為眠。 年歲歲兮...
    樂清居士閱讀 530評論 2 2

友情鏈接更多精彩內(nèi)容