常见的两种Camera实现
在游戏中有两种常见的Camera。基于OpenGL,使用glm数学库,给出简单的实现。
两种Camera都继承自 BaseBamera
。代码如下:
#pragma once #include <glm/glm.hpp> #include <glm/gtc/matrix_transform.hpp> class BaseCamera { public: BaseCamera(const glm::vec3 & _postion, const glm::vec3 & _target, const glm::vec3 & _look, const glm::vec3 & _up); virtual ~BaseCamera(); virtual void update() = 0; void rotate(const float _pitch, const float _yaw, const float _roll); glm::mat4 get_rotate_matrix() const; void set_projection(const float _fov, const float _aspec_ratio, const float _z_near, const float _z_far); void set_fov(const float f); float get_fov() const; float get_aspect_ratio() const; glm::mat4 get_view_matrix() const; glm::mat4 get_projection_matrix() const; //void cal_frustum_planes(); //void get_frustum_planes(glm::vec4 planes[6]); //bool is_point_in_frustum(const glm::vec3 & point); //bool is_box_in_frustum(const glm::vec3 & min, const glm::vec3 & box); //bool is_sphere_in_frustum(const glm::vec3 & center, const float radius); protected: float pitch, yaw, roll; // 欧拉角,描述一个相机的方位,pitch表示绕x轴旋转角度,yaw表示绕y轴旋转角度,pitch表示绕z轴旋转角度 float fovy, aspect_ratio, z_near, z_far; // 相机 viewfrustum 描述 // frustum 平头截体的近平面、远平面 glm::vec3 far_pts[4]; glm::vec3 near_pts[4]; // 相机描述 const glm::vec3 init_look, init_up; // 初始化的 look 和 up 方向 glm::vec3 position, target, up; // camera position, lookat, up glm::vec3 look; // the direction of camera to target glm::vec3 right; // the direction of camera's right glm::mat4 V; // view matrix glm::mat4 P; // projection matrix };
#include <glm/gtx/euler_angles.hpp> #include <math.h> #include <algorithm> BaseCamera::BaseCamera(const glm::vec3 & _postion, const glm::vec3 & _target, const glm::vec3 & _look, const glm::vec3 & _up) : position(_postion), target(_target), look(_look), up(_up), init_look(_look), init_up(_up) { right = glm::cross(look, up); pitch = yaw = roll = 0.0; } BaseCamera::~BaseCamera() { } void BaseCamera::rotate(const float _pitch, const float _yaw, const float _roll) { pitch = glm::radians(_pitch); yaw = glm::radians(_yaw); roll = glm::radians(_roll); update(); } glm::mat4 BaseCamera::get_rotate_matrix() const { return glm::yawPitchRoll(yaw, pitch, roll); } void BaseCamera::set_projection(const float _fovy, const float _aspec_ratio, const float _z_near, const float _z_far) { fovy = _fovy; aspect_ratio = _aspec_ratio; z_near = _z_near; z_far = _z_far; P = glm::perspective(fovy, aspect_ratio, z_near, z_far); } void BaseCamera::set_fov(const float f) { fovy = f; } float BaseCamera::get_fov() const { return fovy; } float BaseCamera::get_aspect_ratio() const { return aspect_ratio; } glm::mat4 BaseCamera::get_view_matrix() const { return V; } glm::mat4 BaseCamera::get_projection_matrix() const { return P; }
Free Camera
参考 OpenGL Development Cookbook - Chapter 2
Free Camera,没有固定的 target,初始化 position、look、up;
target 可以根据 position 以及 look 计算出来,target = position + look * distance
;
move camera 移动相机位置;
rotate camera 会绕着相机的 position 旋转,look方向也会改变;
move camera 移动 camera,不会影响 look 方向;
zoom camera 缩短 camera 与 target 的距离,注意 camera 的位置不变,相当于看得更近/远一些;
第一人称游戏可以采用 FreeCamera。把camera与模型绑定在一起,模型移动、旋转,相机也会相应移动、旋转。
代码如下:
class FreeCamera : public BaseCamera { public: FreeCamera(const glm::vec3 & _position, const glm::vec3 & _look, const glm::vec3 & _up, const float _dist, const float _speed); virtual ~FreeCamera(); virtual void update(); void rotate(const float _pitch, const float _yaw, const float _roll); void walk(const float dt); void strafe(const float dt); void lift(const float dt); void zoom(const float d); // set position,重新设置相机位置 // 联想守望先锋观战模式,切换不同玩家,是在切换相机位置 // 切换相机位置后,translation 和 欧拉角 重新设置为 0 void set_position(const glm::vec3 & _position); glm::vec3 get_position() const; void set_look(const glm::vec3 & _look); glm::vec3 get_look() const; void set_speed(const float & s); float get_speed() const; protected: glm::vec3 translation; float dist; // target 与 position 的距离 float speed; };
/** Free Camera **/ #include <glm/gtx/euler_angles.hpp> #include <math.h> #include <algorithm> FreeCamera::FreeCamera(const glm::vec3 & _position, const glm::vec3 & _look, const glm::vec3 & _up, const float _dist, const float _speed) : BaseCamera(_position, _position + _look * _dist, _look, _up), dist(_dist), speed(_speed) { translation = glm::vec3(0.0); update(); } FreeCamera::~FreeCamera() { } void FreeCamera::update() { position += translation; translation=glm::vec3(0); glm::mat4 R = glm::yawPitchRoll(yaw, pitch, roll); look = glm::vec3(R * glm::vec4(init_look, 0.0)); up = glm::vec3(R * glm::vec4(init_up, 0.0)); right = glm::cross(look, up); target = position + look * dist; V = glm::lookAt(position, target, up); } void FreeCamera::rotate(const float _pitch, const float _yaw, const float _roll) { BaseCamera::rotate(_pitch, _yaw, _roll); } void FreeCamera::walk(const float dt) { translation += (look * speed * dt); update(); } void FreeCamera::strafe(const float dt) { translation += (right * speed * dt); update(); } void FreeCamera::lift(const float dt) { translation += (up * speed * dt); update(); } void FreeCamera::zoom(const float d) { dist += d; update(); } void FreeCamera::set_position(const glm::vec3 & _position) { position = _position; translation = glm::vec3(0.0); yaw = pitch = roll = 0.0; update(); } glm::vec3 FreeCamera::get_position() const { return position; } void FreeCamera::set_look(const glm::vec3 & _look) { look = _look; yaw = pitch = roll = 0.0; update(); } glm::vec3 FreeCamera::get_look() const { return look; } void FreeCamera::set_speed(const float & s) { speed = s; } float FreeCamera::get_speed() const { return speed; }
Target Camera
参考 OpenGL Development Cookbook - Chapter 2
Target Camera,有着固定的 target;
rotate camera 会绕着 target 旋转;
move camera 相机position以及target都会移动;
zoom camera 拉近相机与target的距离;
第三人称游戏可以采用 Target Camera。 考虑一般的RPG游戏,此处以 dota2 举例。回忆 dota2或者war3或者其他RPG游戏:
游戏中,相机视角是固定的保持不变;
移动角色,相机的位置会跟随角色移动,但是视角不变;
选中角色情况下,相机的 target 会固定到角色模型;
如果移动鼠标或者鼠标点击游戏场景其他位置,这时相机的 position、target 会变,会发现相机的视角仍然是没有变化的;
游戏中还可以滚动鼠标中键,会调整相机到场景的距离,这实际也就是 target camera 的 zoom 操作。
代码如下:
class TargetCamera : public BaseCamera { public: TargetCamera(const glm::vec3 & _positon, const glm::vec3 & _target, const glm::vec3 & _up, const float _min_ry = -60.0, const float _max_ry = 60.0, const float _min_dist = 1.0, const float _max_dist = 10.0); virtual ~TargetCamera(); virtual void update(); void rotate(const float _pitch, const float _yaw, const float _roll); // set target,重新设置相机 target // 联想 dota2,鼠标点击场景或者其他英雄,重新设置了相机的 target // 一般情况下,重置 target,仍保持原视角 void set_target(const glm::vec3 & _target); glm::vec3 get_target() const; void pan(const float dx, const float dy); void move(const float dx, const float dy, const float dz); void zoom(const float d); protected: float min_ry, max_ry; float dist; float min_dist, max_dist; };
/** Target Camera **/ #include <glm/gtx/euler_angles.hpp> #include <math.h> #include <algorithm> TargetCamera::TargetCamera(const glm::vec3 & _positon, const glm::vec3 & _target, const glm::vec3 & _up, const float _min_ry /*= -60.0*/, const float _max_ry /*= 60.0*/, const float _min_dist /*= 1.0*/, const float _max_dist /*= 10.0*/) : BaseCamera(_positon, _target, glm::normalize(_target - _positon), _up), min_ry(_min_ry), max_ry(_max_ry), min_dist(_min_dist), max_dist(_max_dist) { dist = glm::distance(position, target); dist = std::max(min_dist, std::min(dist, max_dist)); update(); } TargetCamera::~TargetCamera() { } void TargetCamera::update() { glm::mat4 R = glm::yawPitchRoll(yaw, pitch, roll); glm::vec3 T = init_look * dist; T = glm::vec3(R * glm::vec4(T, 0.0f)); position = target - T; look = glm::normalize(target - position); up = glm::vec3(R * glm::vec4(init_up, 0.0)); right = glm::cross(look, up); V = glm::lookAt(position, target, up); } void TargetCamera::rotate(const float _pitch, const float _yaw, const float _roll) { float p = std::min(std::max(_pitch, min_ry), max_ry); BaseCamera::rotate(p, _yaw, _roll); } void TargetCamera::set_target(const glm::vec3 & _target) { target = _target; update(); } glm::vec3 TargetCamera::get_target() const { return target; } void TargetCamera::pan(const float dx, const float dy) { glm::vec3 X = right * dx; glm::vec3 Y = up * dy; position += X + Y; target += X + Y; update(); } void TargetCamera::move(const float dx, const float dy, const float dz) { glm::vec3 X = right * dx; glm::vec3 Y = up * dy; glm::vec3 Z = look * dz; position += X + Y + Z; target += X + Y + Z; update(); } void TargetCamera::zoom(const float d) { position += look * d; dist = glm::distance(position, target); dist = std::max(min_dist, std::min(dist, max_dist)); update(); }
如果有什么问题,欢迎留言评论。
goudan-er GRAPHICS
Graphics OpenGL