本节是将之前在sandbox中控制相机运动的这部分代码抽象到 CameraController 类当中,
创建 CameraController.h CameraController.cpp
在头文件中进行如下声明:
#pragma once
#include "Hazel/Renderer/OrthographicCamera.h"
#include "Hazel/Core/Timestep.h"
#include "Hazel/Events/ApplicationEvent.h"
#include "Hazel/Events/MouseEvent.h"
namespace Hazel {
class OrthographicCameraController
{
public:
OrthographicCameraController(float aspectRatio, bool rotation = false);
void OnUpdate(Timestep ts);
void OnEvent(Event& e);
OrthographicCamera& GetCamera() { return m_Camera; }
const OrthographicCamera& GetCamera() const { return m_Camera; }
private:
bool OnMouseScrolled(MouseScrolledEvent& e);
bool OnWindowResized(WindowResizeEvent& e);
private:
float m_AspectRatio;
float m_ZoomLevel = 1.0f;
OrthographicCamera m_Camera;
bool m_Rotation;
glm::vec3 m_CameraPosition = { 0.0f, 0.0f, 0.0f };
float m_CameraRotation = 0.0f;
float m_CameraTranslationSpeed = 5.0f, m_CameraRotationSpeed = 180.0f;
};
}
可以看到,Cameracontroller 实现相机运动的方式和之前有所区别,之前是通过 inputpolling 实现的,然后这次是通过 event 来实现,在成员变量中,创建了用于控制视角远近的 ZoomLevel ,持有一台正交相机,以及用于控制旋转,平移的参数。成员函数方面,有两个用于 Event 的回调函数 OnMouseScrolled OnWindowResized,然后就是 upDate 和 OnEvent 函数,此外还分别创建了获取相机的 const 和 非 const 函数。
然后在评论区看到有人提出了问题:
For some reason, putting these private variables in a different order, breaks the app. You only see something when you either scroll, resize or move the camera
就是说以一个不同的顺序排列这些私有成员变量会出问题,导致只有同时 移动放缩相机的时候,才能看到物体,某位大佬是这么回答的:
The order they are defined in is also used for the order they are initialized in. If they are in the wrong order, the zoom level might be set to 0.0f. This is then incorrectly passed into the constructor of the OrthographicCamera:
大致意思就是,变量定义的顺序和初始化的顺序是一样的,如果不先定义 zoom ,那么初始的时候 Zoom很可能被设置为 0,然后传到正交相机的构造函数中,然后就什么也看不到了,所以要保证初始化相机的时候,Zoom已经有值了。
cpp函数中进行如下定义:
#include "hzpch.h"
#include "OrthographicCameraController.h"
#include "Hazel/Input.h"
#include "Hazel/KeyCodes.h"
namespace Hazel {
OrthographicCameraController::OrthographicCameraController(float aspectRatio, bool rotation)
: m_AspectRatio(aspectRatio), m_Camera(-m_AspectRatio * m_ZoomLevel, m_AspectRatio * m_ZoomLevel, -m_ZoomLevel, m_ZoomLevel), m_Rotation(rotation)
{
}
void OrthographicCameraController::OnUpdate(Timestep ts)
{
if (Input::IsKeyPressed(HZ_KEY_A))
m_CameraPosition.x -= m_CameraTranslationSpeed * ts;
else if (Input::IsKeyPressed(HZ_KEY_D))
m_CameraPosition.x += m_CameraTranslationSpeed * ts;
if (Input::IsKeyPressed(HZ_KEY_W))
m_CameraPosition.y += m_CameraTranslationSpeed * ts;
else if (Input::IsKeyPressed(HZ_KEY_S))
m_CameraPosition.y -= m_CameraTranslationSpeed * ts;
if (m_Rotation)
{
if (Input::IsKeyPressed(HZ_KEY_Q))
m_CameraRotation += m_CameraRotationSpeed * ts;
if (Input::IsKeyPressed(HZ_KEY_E))
m_CameraRotation -= m_CameraRotationSpeed * ts;
m_Camera.SetRotation(m_CameraRotation);
}
m_Camera.SetPosition(m_CameraPosition);
m_CameraTranslationSpeed = m_ZoomLevel;
}
void OrthographicCameraController::OnEvent(Event& e)
{
EventDispatcher dispatcher(e);
dispatcher.Dispatch<MouseScrolledEvent>(HZ_BIND_EVENT_FN(OrthographicCameraController::OnMouseScrolled));
dispatcher.Dispatch<WindowResizeEvent>(HZ_BIND_EVENT_FN(OrthographicCameraController::OnWindowResized));
}
bool OrthographicCameraController::OnMouseScrolled(MouseScrolledEvent& e)
{
m_ZoomLevel -= e.GetYOffset() * 0.25f;
m_ZoomLevel = std::max(m_ZoomLevel, 0.25f);
m_Camera.SetProjection(-m_AspectRatio * m_ZoomLevel, m_AspectRatio * m_ZoomLevel, -m_ZoomLevel, m_ZoomLevel);
return false;
}
bool OrthographicCameraController::OnWindowResized(WindowResizeEvent& e)
{
m_AspectRatio = (float)e.GetWidth() / (float)e.GetHeight();
m_Camera.SetProjection(-m_AspectRatio * m_ZoomLevel, m_AspectRatio * m_ZoomLevel, -m_ZoomLevel, m_ZoomLevel);
return false;
}
}
主要看一下相机拉近和远离这部分实现,是通过相机的 SetProjection 这一函数实现的,先来看一下这个函数的实现:
void OrthographicCamera::SetProjection(float left, float right, float bottom, float top)
{
m_ProjectionMatrix = glm::ortho(left, right, bottom, top, -1.0f, 1.0f);
m_ViewProjectionMatrix = m_ProjectionMatrix * m_ViewMatrix;
}
在这里利用 ZoomLevel和 aspectRatio 来进行投影平面的控制,试想,假如说我们原本的物体是正交投影到 [ -16 , 16 ] ,[ -9 , 9 ] ,这么一个平面上面的,当我们拉近相机的时候,物体会变大,也就是相当于投影平面减小,所以使用 ZoomLevel 来放缩投影平面达到相机拉近和远离的效果。但是在本节我们还没有对 opengl 回调函数进行设置,所以resize window 之后,窗口的大小还是那么大,这个问题会在之后解决。
之后删除所有 SandBox 中的关于操控 Camera 的代码,并在 Hazel.h 中添加 cameraController 的头文件加以应用。