如何相对于世界坐标系设置相机四元数的Pitch、Yaw、Roll
基于世界坐标系设置相机四元数的Pitch/Yaw/Roll参数
我有一个存储相机朝向的四元数,希望相对于世界坐标系设置该四元数的Pitch(俯仰)、Yaw(偏航)、Roll(滚转)参数。
场景说明
想象四周墙面上各有一个指向世界上方向的箭头。无论当前四元数的姿态如何:
- 当看向任意箭头(任意Yaw角度)但带有一定Roll(箭头不指向向上)时,调用
setRoll(0°),相机视角中箭头应变为向上; - 调用
setRoll(90°)时,无论调用多少次,相机的滚转角度都被固定为90°,箭头指向左侧。
现有代码
以下是完整的相机类代码,核心待实现的是setPitch、setYaw、setRoll函数:
Camera.hpp
#ifndef CAMERA_HPP #define CAMERA_HPP // GLAD #include <glad/glad.h> // GLM #include <glm/glm.hpp> #include <glm/gtc/matrix_transform.hpp> #include <glm/gtc/type_ptr.hpp> #include <glm/gtc/quaternion.hpp> class QuaternionCamera { public: QuaternionCamera(glm::vec3 position, float zNear, float zFar); QuaternionCamera(glm::vec3 position, glm::quat orientation, float zNear, float zFar); QuaternionCamera(glm::vec3 position, glm::vec3 orientation, float zNear, float zFar); // 设置世界坐标系下的位置 void setPosition(glm::vec3 position); // 设置世界坐标系下的朝向 void setOrientation(glm::quat orientation); // 设置世界坐标系下的朝向(欧拉角) void setOrientation(glm::vec3 orientation); // 设置世界坐标系下的俯仰角 void setPitch(float amount); // 设置世界坐标系下的偏航角 void setYaw(float amount); // 设置世界坐标系下的滚转角 void setRoll(float amount); // 设置相机近裁剪面 void setZNear(float zNear); // 设置相机远裁剪面 void setZFar(float zFar); // 相对于当前相机坐标系移动 void move(glm::vec3 movement); // 相对于世界坐标系移动 void moveAxis(glm::vec3 translation); // 相对于当前姿态旋转 void rotate(glm::quat rotation); // 相对于当前姿态旋转(欧拉角) void rotate(glm::vec3 rotation); // 相对于当前姿态俯仰 void pitch(float amount); // 相对于当前姿态偏航 void yaw(float amount); // 相对于当前姿态滚转 void roll(float amount); // 获取世界坐标系下的位置 glm::vec3 getPosition() const; // 获取世界坐标系下的相机朝向 glm::vec3 getDirection() const; // 获取相机右向量 glm::vec3 getRight() const; // 获取相机上向量 glm::vec3 getUp() const; // 获取世界坐标系下的朝向四元数 glm::quat getOrientation() const; // 获取近裁剪面距离 float getZNear() const; // 获取远裁剪面距离 float getZFar() const; // 获取视图矩阵 glm::mat4 getViewMatrix() const; // 获取投影矩阵 glm::mat4 getProjectionMatrix() const; // 获取视图投影矩阵 glm::mat4 getViewProjectionMatrix() const; protected: // updateProjectionMatrix为虚函数,由派生类实现(透视/正交相机) virtual void updateProjectionMatrix() = 0; void updateViewProjectionMatrix(); float zNear; float zFar; glm::mat4 projectionMatrix; glm::mat4 viewProjectionMatrix; private: void updateCameraVectors(); void updateViewMatrix(); glm::mat4 viewMatrix; glm::vec3 position; glm::quat orientation; glm::vec3 direction; // 相机朝向 glm::vec3 right; glm::vec3 up; }; #endif
Camera.cpp
QuaternionCamera::QuaternionCamera(glm::vec3 position, float zNear, float zFar) { this->position = position; this->orientation = glm::quat(1.0f, 0.0f, 0.0f, 0.0f); // glm四元数构造参数为(w,x,y,z),存储为(x,y,z,w) right = glm::vec3( 1, 0, 0); up = glm::vec3( 0, 1, 0); direction = glm::vec3( 0, 0, -1); this->zNear = zNear; this->zFar = zFar; updateViewMatrix(); updateViewProjectionMatrix(); } QuaternionCamera::QuaternionCamera(glm::vec3 position, glm::quat orientation, float zNear, float zFar) { this->position = position; this->orientation = orientation; right = glm::vec3( 1, 0, 0); up = glm::vec3( 0, 1, 0); direction = glm::vec3( 0, 0, -1); this->zNear = zNear; this->zFar = zFar; updateViewMatrix(); updateViewProjectionMatrix(); } QuaternionCamera::QuaternionCamera(glm::vec3 position, glm::vec3 orientation, float zNear, float zFar) { this->position = position; this->orientation = glm::quat(glm::vec3(glm::radians(orientation.x), glm::radians(orientation.y), glm::radians(orientation.z))); right = glm::vec3( 1, 0, 0); up = glm::vec3( 0, 1, 0); direction = glm::vec3( 0, 0, -1); this->zNear = zNear; this->zFar = zFar; updateCameraVectors(); updateViewMatrix(); updateViewProjectionMatrix(); } void QuaternionCamera::setPosition(glm::vec3 position) { this->position = position; } void QuaternionCamera::setOrientation(glm::quat orientation) { this->orientation = orientation; } void QuaternionCamera::setOrientation(glm::vec3 orientation) { glm::quat orientationQuat = glm::quat(orientation); this->orientation = orientationQuat; } void QuaternionCamera::setPitch(float amount) { // TODO: 实现基于世界坐标系的俯仰角设置 updateCameraVectors(); updateViewMatrix(); updateViewProjectionMatrix(); } void QuaternionCamera::setYaw(float amount) { // TODO: 实现基于世界坐标系的偏航角设置 updateCameraVectors(); updateViewMatrix(); updateViewProjectionMatrix(); } void QuaternionCamera::setRoll(float amount) { // TODO: 实现基于世界坐标系的滚转角设置 updateCameraVectors(); updateViewMatrix(); updateViewProjectionMatrix(); } void QuaternionCamera::setZNear(float zNear) { this->zNear = zNear; updateProjectionMatrix(); } void QuaternionCamera::setZFar(float zFar) { this->zFar = zFar; updateProjectionMatrix(); } void QuaternionCamera::move(glm::vec3 movement) { position += (orientation * glm::vec3(1, 0, 0)) * movement.x + (orientation * glm::vec3(0, 1, 0)) * movement.y + (orientation * glm::vec3(0, 0, -1)) * movement.z; updateViewMatrix(); updateViewProjectionMatrix(); } void QuaternionCamera::moveAxis(glm::vec3 translation) { position += translation; updateViewMatrix(); updateViewProjectionMatrix(); } void QuaternionCamera::rotate(glm::quat rotation) { orientation *= rotation; updateCameraVectors(); updateViewMatrix(); updateViewProjectionMatrix(); } void QuaternionCamera::rotate(glm::vec3 rotation) { glm::quat rotationQuat = glm::quat(rotation); orientation *= rotationQuat; updateCameraVectors(); updateViewMatrix(); updateViewProjectionMatrix(); } void QuaternionCamera::pitch(float amount) { glm::quat rotation = glm::angleAxis(glm::radians(amount), glm::vec3(1, 0, 0)); orientation *= rotation; updateCameraVectors(); updateViewMatrix(); updateViewProjectionMatrix(); } void QuaternionCamera::yaw(float amount) { glm::quat rotation = glm::angleAxis(glm::radians(-amount), glm::vec3(0, 1, 0)); orientation *= rotation; updateCameraVectors(); updateViewMatrix(); updateViewProjectionMatrix(); } void QuaternionCamera::roll(float amount) { glm::quat rotation = glm::angleAxis(glm::radians(amount), glm::vec3(0, 0, -1)); orientation *= rotation; updateCameraVectors(); updateViewMatrix(); updateViewProjectionMatrix(); } glm::vec3 QuaternionCamera::getPosition() const { return position; } glm::vec3 QuaternionCamera::getDirection() const { return direction; } glm::vec3 QuaternionCamera::getRight() const { return right; } glm::vec3 QuaternionCamera::getUp() const { return up; } glm::quat QuaternionCamera::getOrientation() const { return orientation; } float QuaternionCamera::getZNear() const { return zNear; } float QuaternionCamera::getZFar() const { return zFar; } glm::mat4 QuaternionCamera::getViewMatrix() const { return viewMatrix; } glm::mat4 QuaternionCamera::getProjectionMatrix() const { return projectionMatrix; } glm::mat4 QuaternionCamera::getViewProjectionMatrix() const { return viewProjectionMatrix; } void QuaternionCamera::updateViewProjectionMatrix() { viewProjectionMatrix = getProjectionMatrix() * getViewMatrix(); } void QuaternionCamera::updateCameraVectors() { right = glm::normalize(orientation * glm::vec3(1, 0, 0)); up = glm::normalize(orientation * glm::vec3(0, 1, 0)); direction = glm::normalize(orientation * glm::vec3(0, 0, -1)); if (glm::dot(up, glm::cross(right, direction)) < 0) { up *= -1; } } void QuaternionCamera::updateViewMatrix() { viewMatrix = glm::lookAt(position, position + direction, up/*glm::cross(right, direction)*/); } PerspectiveCamera::PerspectiveCamera(glm::vec3 position, float fov, float aspectRatio, float zNear, float zFar) : QuaternionCamera(position, zNear, zFar) { this->fov = fov; this->aspectRatio = aspectRatio; updateProjectionMatrix(); } PerspectiveCamera::PerspectiveCamera(glm::vec3 position, glm::quat rotation, float fov, float aspectRatio, float zNear, float zFar) : QuaternionCamera(position, rotation, zNear, zFar) { this->fov = fov; this->aspectRatio = aspectRatio; updateProjectionMatrix(); } PerspectiveCamera::PerspectiveCamera(glm::vec3 position, glm::vec3 rotation, float fov, float aspectRatio, float zNear, float zFar) : QuaternionCamera(position, rotation, zNear, zFar) { this->fov = fov; this->aspectRatio = aspectRatio; updateProjectionMatrix(); } void PerspectiveCamera::setFOV(float fov) { this->fov = fov; updateProjectionMatrix(); } void PerspectiveCamera::setAspectRatio(float aspectRatio) { this->aspectRatio = aspectRatio; updateProjectionMatrix(); } void PerspectiveCamera::setAspectRatio(float width, float height) { this->aspectRatio = height / width; updateProjectionMatrix(); } float PerspectiveCamera::getFOV() const { return fov; } float PerspectiveCamera::getAspectRatio() const { return aspectRatio; } void PerspectiveCamera::updateProjectionMatrix() { projectionMatrix = glm::perspective(glm::radians(fov), aspectRatio, zNear, zFar); }
实现方案
核心思路是:保留当前姿态中除目标轴外的其他分量,再叠加目标轴的固定世界坐标系角度,最后重新构造四元数。
setRoll实现
void QuaternionCamera::setRoll(float amount) { const glm::vec3 worldUp = glm::vec3(0, 1, 0); const glm::vec3 forward = glm::normalize(direction); // 计算无滚转的正交向量组 glm::vec3 rightNoRoll = glm::normalize(glm::cross(worldUp, forward)); glm::vec3 upNoRoll = glm::cross(forward, rightNoRoll); // 应用目标滚转角 const float rad = glm::radians(amount); const glm::quat rollQuat = glm::angleAxis(rad, forward); const glm::vec3 newRight = rollQuat * rightNoRoll; const glm::vec3 newUp = rollQuat * upNoRoll; // 从正交向量构造四元数 orientation = glm::quat(glm::mat3(newRight, newUp, -forward)); updateCameraVectors(); updateViewMatrix(); updateViewProjectionMatrix(); }
setYaw实现
void QuaternionCamera::setYaw(float amount) { const float rad = glm::radians(amount); // 计算目标偏航的水平朝向 const glm::vec3 yawDir = glm::normalize(glm::vec3(glm::sin(rad), 0, -glm::cos(rad))); // 保留当前俯仰角 const float pitchAngle = glm::asin(direction.y); const glm::vec3 forward = glm::normalize(glm::vec3( yawDir.x * glm::cos(pitchAngle), glm::sin(pitchAngle), yawDir.z * glm::cos(pitchAngle) )); // 构造无滚转正交向量组 const glm::vec3 worldUp = glm::vec3(0, 1, 0); glm::vec3 right = glm::normalize(glm::cross(worldUp, forward)); glm::vec3 up = glm::cross(forward, right); // 保留当前滚转角 const glm::vec3 originalRight = getRight(); float rollAngle = glm::acos(glm::clamp(glm::dot(originalRight, right), -1.0f, 1.0f)); if (glm::dot(up, originalRight) < 0) { rollAngle = -rollAngle; } const glm::quat rollQuat = glm::angleAxis(rollAngle, forward); right = rollQuat * right; up = rollQuat * up; // 构造最终四元数 orientation = glm::quat(glm::mat3(right, up, -forward)); updateCameraVectors(); updateViewMatrix(); updateViewProjectionMatrix(); }
setPitch实现
void QuaternionCamera::setPitch(float amount) { const float rad = glm::radians(amount); // 保留当前偏航角 const glm::vec3 horizontalDir = glm::normalize(glm::vec3(direction.x, 0, direction.z)); float yawAngle = 0.0f; if (glm::length(horizontalDir) > 0.0001f) { yawAngle = glm::atan(horizontalDir.x, -horizontalDir.z); } // 构造带有固定俯仰的朝向向量 const glm::vec3 forward = glm::normalize(glm::vec3( glm::sin(yawAngle) * glm::cos(rad), glm::sin(rad), -glm::cos(yawAngle) * glm::cos(rad) )); // 构造无滚转正交向量组 const glm::vec3 worldUp = glm::vec3(0, 1, 0); glm::vec3 right = glm::normalize(glm::cross(
相关产品推荐
相关产品推荐

