<p>Attach your camera to some pivot (in a transformation hierarchical sense). Rotate the pivot rather than the camera (do not use look at). It boils down to using <span class="math-container">$T_{pivot} R_{pivot}  T_{cam}  R_{cam}$</span>. Where <span class="math-container">$T_{pivot}$</span> is a translation matrix derived from the position of the pivot in world space, <span class="math-container">$R_{pivot}$</span> is the rotation matrix of the pivot (what you want), <span class="math-container">$T_{cam}$</span> is a translation matrix derived from the position of the camera in the pivot's coord system, similarly for <span class="math-container">$R_{cam}$</span>.</p>

CC BY-SA 4.0
2019-09-12T14:03:57.770Z
How does this solution compare to a quaternion-based implementation? What are the differences in performance/limitations between the two?
Daniel Marques
CC BY-SA 4.0
2019-09-12T14:10:35.980Z
@DanielMarques They are interchangeable - you can transform between the two representations depending on your needs. As far as performance goes, here is a detailed break-down: https://www.geometrictools.com/Documentation/RotationIssues.pdf
lightxbulb
CC BY-SA 4.0
2019-09-12T13:40:43.507Z
2019-09-12T13:40:43.507Z
lightxbulb
<p>I've developed a simple (Third-Person) Perspective Camera with Orbit controls.</p>

<p>However, my implementation doesn't handle the case where if the pitch goes over 90º it flips upside down. From what I've learned, the reason behind it has to do with using the Look-At function.</p>

<p>My questions are:  </p>

<ul>
<li>Is the problem really the Look-at function? How would I fix this, without restricting the camera's pitch?</li>
<li>I want to develop a camera class that is control agnostic (can be fly, orbit, ...) and therefore not take into account that it's first/third-person. Are there methods on how to develop such a camera abstraction?  </li>
<li>I also want it to be able to roll, can the advised solution for the last question support it? </li>
<li>I haven't delved into quaternions yet, is the solution based on them? If so, are there also limitations to an implementation like that?</li>
</ul>

<p>I'm basically looking for theory on developing proper robust cameras (with as few limitations as possible), so if you could point me into the right direction it would be nice, but a full answer is also very much appreciated. Code is not essential, but it would be also very helpful to see a proper (yet simple) implementation.</p>

<p>My camera implementation (in Vulkan):<br>
Camera.hpp:  </p>

<pre class="lang-cpp prettyprint-override"><code>#pragma once

#define GLM_FORCE_RADIANS
#define GLM_FORCE_DEPTH_ZERO_TO_ONE
#include &lt;glm/glm.hpp&gt;
#include &lt;glm/gtc/quaternion.hpp&gt;
#include &lt;glm/gtc/matrix_transform.hpp&gt;

#include &lt;GLFW/glfw3.h&gt;


class CameraControl;

class Camera 
{
private:
    glm::vec3 position = glm::vec3(0.0f, 0.0f, 1.0f);
    glm::vec3 target = glm::vec3(0.0f, 0.0f, 0.0f);

    glm::vec3 up = glm::vec3(0.0f, 1.0f, 0.0f);

    float fov = glm::radians(45.0f);
    float aspectRatio = 1.0f;
    float near = 0.1f;
    float far = 1000.0f;

    glm::mat4 view;
    glm::mat4 projection;

private:
    void updateViewMatrix();
    void updateProjectionMatrix();

public:
    CameraControl *control;

public:
    Camera();
    ~Camera();
    void setPosition(glm::vec3 position);
    void setTarget(glm::vec3 target);
    void setPerspective(float fov, float aspectRatio, float near, float far);

    glm::mat4 inline getView() { return view; };
    glm::mat4 inline getProjection() { return projection; };
    glm::vec3 inline getPosition() { return position; };
    glm::vec3 inline getTarget() { return target; };
};


class CameraControl {
public:
    float orbitSensitivity = 0.2f;
    float zoomSensitivity = 0.0025f;

private:
    Camera *camera;

    bool firstMouse;
    float lastMouseX;
    float lastMouseY;

    float distance;
    float pitch;
    float yaw;

public:
    CameraControl(Camera *camera);
    void handleInput(GLFWwindow *window);
};
</code></pre>

<p>Camera.cpp:  </p>

<pre class="lang-cpp prettyprint-override"><code>#include "Camera.hpp"

Camera::Camera() {
    control = new CameraControl(this);
    updateViewMatrix();
    updateProjectionMatrix();
}

Camera::~Camera() {
    delete control;
}

void Camera::setPosition(glm::vec3 position) {
    this-&gt;position = position;
    updateViewMatrix();
}

void Camera::setTarget(glm::vec3 target) {
    this-&gt;target = target;
    updateViewMatrix();
}

void Camera::setPerspective(float fov, float aspectRatio, float near, float far) {
    this-&gt;fov = fov;
    this-&gt;aspectRatio = aspectRatio;
    this-&gt;near = near;
    this-&gt;far = far;
    updateProjectionMatrix();
}

void Camera::updateViewMatrix() {
    view = glm::lookAt(position, target, up);
}

void Camera::updateProjectionMatrix() {
    projection = glm::perspective(fov, aspectRatio, near, far);
    projection[1][1] *= -1; // Vulkan coordinate system fix
}


CameraControl::CameraControl(Camera* camera) {
    this-&gt;camera = camera;
    firstMouse = true;
    distance = 1.0f;
    pitch = 0.0f;
    yaw = 180.0f;
}

void CameraControl::handleInput(GLFWwindow *window) {
    double xpos, ypos;
    glfwGetCursorPos(window, &amp;xpos, &amp;ypos);

    if (firstMouse) {
        lastMouseX = xpos;
        lastMouseY = ypos;
        firstMouse = false;
    }

    float xoffset = xpos - lastMouseX;
    float yoffset = lastMouseY - ypos;

    lastMouseX = xpos;
    lastMouseY = ypos;

    if(glfwGetMouseButton(window, GLFW_MOUSE_BUTTON_LEFT)) {
        pitch -= yoffset * orbitSensitivity;
        yaw -= xoffset * orbitSensitivity;
    }

    if(glfwGetMouseButton(window, GLFW_MOUSE_BUTTON_RIGHT)) {
        distance += yoffset * zoomSensitivity;
        distance = glm::max(distance, 0.0f);
    }

    float verticalDistance = distance * glm::sin(glm::radians(pitch));
    float horizontalDistance = distance * glm::cos(glm::radians(pitch));

    glm::vec3 position;
    position.x = camera-&gt;getTarget().x - horizontalDistance * glm::sin(glm::radians(yaw));
    position.y = camera-&gt;getTarget().y + verticalDistance;
    position.z = camera-&gt;getTarget().z - horizontalDistance * glm::cos(glm::radians(yaw));
    camera-&gt;setPosition(position);
}

</code></pre>

<p>Thank you for your attention! Cheers!</p>

CC BY-SA 4.0
2020-10-08T09:36:13.223Z
Do you want to have a camera which can be controlled like an airplane? What I mean is: It can roll pitch yaw? That is quite simple: your camera has a projection and a view-matrix. In this case we need to change the view-matrix. By calculating viewMatrix = viewMatrix * rotationMatrix where rotationMatrix can be X-Rotation (pitch), Y-Rotation (yaw) and Z-Rotation (roll). With this solution you only need the lookat method to initialize the camera. At the rest of the time you only need to multiply a rotation onto it.
Thomas
CC BY-SA 4.0
2021-10-01T22:20:56.803Z
Changing the up axis when the camera passes a preset angle (like 89 degrees)  sides steps the issues all together.  Changing the axis at 89 degrees means the new axis is only 1 degree off axis....when it gets to 89 degrees do it again. such as y->x then x->y. This is easy to do with a camera that has roll and pitch ( or any combination of two angles of rotation) but for a camera that has roll, pitch and yaw with 360 degrees of freedom in all directions a quaternion camera is just easier to implement and maintain (but is the hardest to understand)
pmw1234
CC BY-SA 4.0
2021-10-01T22:26:57.813Z
I will say that once you warp your mind around a quaternion based camera, everything quaternion is suddenly easier to understand. The sandwich product is your friend.
pmw1234
<mathematics><camera>
Camera Implementation without Limitations
Daniel Marques
# Camera Implementation without Limitations

(Asked by: username_0 on 2019-09-12)

I've developed a simple (Third-Person) Perspective Camera with Orbit controls.

However, my implementation doesn't handle the case where if the pitch goes over 90º it flips upside down. From what I've learned, the reason behind it has to do with using the Look-At function.

My questions are:

* Is the problem really the Look-at function? How would I fix this, without restricting the camera's pitch?
* I want to develop a camera class that is control agnostic (can be fly, orbit, ...) and therefore not take into account that it's first/third-person. Are there methods on how to develop such a camera abstraction?
* I also want it to be able to roll, can the advised solution for the last question support it?
* I haven't delved into quaternions yet, is the solution based on them? If so, are there also limitations to an implementation like that?

I'm basically looking for theory on developing proper robust cameras (with as few limitations as possible), so if you could point me into the right direction it would be nice, but a full answer is also very much appreciated. Code is not essential, but it would be also very helpful to see a proper (yet simple) implementation.

My camera implementation (in Vulkan):  
Camera.hpp:

```cpp
#pragma once

#define GLM_FORCE_RADIANS
#define GLM_FORCE_DEPTH_ZERO_TO_ONE
#include <glm/glm.hpp>
#include <glm/gtc/quaternion.hpp>
#include <glm/gtc/matrix_transform.hpp>

#include <GLFW/glfw3.h>

class CameraControl;

class Camera 
{
private:
    glm::vec3 position = glm::vec3(0.0f, 0.0f, 1.0f);
    glm::vec3 target = glm::vec3(0.0f, 0.0f, 0.0f);

    glm::vec3 up = glm::vec3(0.0f, 1.0f, 0.0f);

    float fov = glm::radians(45.0f);
    float aspectRatio = 1.0f;
    float near = 0.1f;
    float far = 1000.0f;

    glm::mat4 view;
    glm::mat4 projection;

private:
    void updateViewMatrix();
    void updateProjectionMatrix();

public:
    CameraControl *control;

public:
    Camera();
    ~Camera();
    void setPosition(glm::vec3 position);
    void setTarget(glm::vec3 target);
    void setPerspective(float fov, float aspectRatio, float near, float far);

    glm::mat4 inline getView() { return view; };
    glm::mat4 inline getProjection() { return projection; };
    glm::vec3 inline getPosition() { return position; };
    glm::vec3 inline getTarget() { return target; };
};

class CameraControl {
public:
    float orbitSensitivity = 0.2f;
    float zoomSensitivity = 0.0025f;

private:
    Camera *camera;

    bool firstMouse;
    float lastMouseX;
    float lastMouseY;

    float distance;
    float pitch;
    float yaw;

public:
    CameraControl(Camera *camera);
    void handleInput(GLFWwindow *window);
};

```

Camera.cpp:

```cpp
#include "Camera.hpp"

Camera::Camera() {
    control = new CameraControl(this);
    updateViewMatrix();
    updateProjectionMatrix();
}

Camera::~Camera() {
    delete control;
}

void Camera::setPosition(glm::vec3 position) {
    this->position = position;
    updateViewMatrix();
}

void Camera::setTarget(glm::vec3 target) {
    this->target = target;
    updateViewMatrix();
}

void Camera::setPerspective(float fov, float aspectRatio, float near, float far) {
    this->fov = fov;
    this->aspectRatio = aspectRatio;
    this->near = near;
    this->far = far;
    updateProjectionMatrix();
}

void Camera::updateViewMatrix() {
    view = glm::lookAt(position, target, up);
}

void Camera::updateProjectionMatrix() {
    projection = glm::perspective(fov, aspectRatio, near, far);
    projection[1][1] *= -1; // Vulkan coordinate system fix
}

CameraControl::CameraControl(Camera* camera) {
    this->camera = camera;
    firstMouse = true;
    distance = 1.0f;
    pitch = 0.0f;
    yaw = 180.0f;
}

void CameraControl::handleInput(GLFWwindow *window) {
    double xpos, ypos;
    glfwGetCursorPos(window, &xpos, &ypos);

    if (firstMouse) {
        lastMouseX = xpos;
        lastMouseY = ypos;
        firstMouse = false;
    }

    float xoffset = xpos - lastMouseX;
    float yoffset = lastMouseY - ypos;

    lastMouseX = xpos;
    lastMouseY = ypos;

    if(glfwGetMouseButton(window, GLFW_MOUSE_BUTTON_LEFT)) {
        pitch -= yoffset * orbitSensitivity;
        yaw -= xoffset * orbitSensitivity;
    }

    if(glfwGetMouseButton(window, GLFW_MOUSE_BUTTON_RIGHT)) {
        distance += yoffset * zoomSensitivity;
        distance = glm::max(distance, 0.0f);
    }

    float verticalDistance = distance * glm::sin(glm::radians(pitch));
    float horizontalDistance = distance * glm::cos(glm::radians(pitch));

    glm::vec3 position;
    position.x = camera->getTarget().x - horizontalDistance * glm::sin(glm::radians(yaw));
    position.y = camera->getTarget().y + verticalDistance;
    position.z = camera->getTarget().z - horizontalDistance * glm::cos(glm::radians(yaw));
    camera->setPosition(position);
}

```

Thank you for your attention! Cheers!

**Tags:** mathematics, camera

> **Comments:**
> **username_1**: Do you want to have a camera which can be controlled like an airplane? What I mean is: It can roll pitch yaw? That is quite simple: your camera has a projection and a view-matrix. In this case we need to change the view-matrix. By calculating viewMatrix = viewMatrix * rotationMatrix where rotationMatrix can be X-Rotation (pitch), Y-Rotation (yaw) and Z-Rotation (roll). With this solution you only need the lookat method to initialize the camera. At the rest of the time you only need to multiply a rotation onto it.  
> **username_2**: Changing the up axis when the camera passes a preset angle (like 89 degrees)  sides steps the issues all together.  Changing the axis at 89 degrees means the new axis is only 1 degree off axis....when it gets to 89 degrees do it again. such as y->x then x->y. This is easy to do with a camera that has roll and pitch ( or any combination of two angles of rotation) but for a camera that has roll, pitch and yaw with 360 degrees of freedom in all directions a quaternion camera is just easier to implement and maintain (but is the hardest to understand)  
> **username_2**: I will say that once you warp your mind around a quaternion based camera, everything quaternion is suddenly easier to understand. The sandwich product is your friend.  

---

## 1 answers

### Answer by username_3 on 2019-09-12. Score: 1

Attach your camera to some pivot (in a transformation hierarchical sense). Rotate the pivot rather than the camera (do not use look at). It boils down to using $T\_{pivot} R\_{pivot} T\_{cam} R\_{cam}$. Where $T\_{pivot}$ is a translation matrix derived from the position of the pivot in world space, $R\_{pivot}$ is the rotation matrix of the pivot (what you want), $T\_{cam}$ is a translation matrix derived from the position of the camera in the pivot's coord system, similarly for $R\_{cam}$.

> **Comments:**
> **username_0**: How does this solution compare to a quaternion-based implementation? What are the differences in performance/limitations between the two?  
> **username_3**: @username_0 They are interchangeable - you can transform between the two representations depending on your needs. As far as performance goes, here is a detailed break-down: https://www.geometrictools.com/Documentation/RotationIssues.pdf  

---

