使用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平面都会呈一定的角度。
在摄像机旋转时,摄像机位置不改变,仅改变朝向。
- 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();
}
实现效果
【画质感人】
源码及demo
戳这里