文中若有代码、术语等错误,欢迎指正
前言
-
上一节的bug
将Entity的m_EntityHandle初始化为null, 而不是0
entt::entity m_EntityHandle{ entt::null }; operator bool() const { return m_EntityHandle != entt::null; }
-
此节内容
-
设计一个Camera组件
即一个实体可以用有Camera组件
-
这个Camera组件需拥有Camera类指针
Camera类拥有的属性与行为
-
设置为透视投影projection矩阵还是正交投影projection矩阵(未做)
-
是否主相机
-
获取projection投影矩阵
注意区分:Camera组件与Camera类,实体添加Camera组件、Camera组件拥有Camera类、所以实体能通过组件获得Camera
-
-
Camera类与Camera组件设计
-
新添加Camera.h
#pragma once #include <glm/glm.hpp> namespace Hazel { class Camera { public: Camera(const glm::mat4& projection) : m_Projection(projection){ } const glm::mat4& GetProjection() const { return m_Projection; } // TOOD:做透视投影 private: glm::mat4 m_Projection; }; }
-
Components.h
struct CameraComponent { Camera camera; bool primary = true; CameraComponent() = default; CameraComponent(const CameraComponent&) = default; CameraComponent(const glm::mat4 & projection) : camera(projection) { } };
场景切换到主摄像机视角代码流程
-
EditorLayer层添加一个实体
EditorLayer.h Entity m_CameraEntity; // 摄像机实体
-
实体添加摄像机组件,并初始化正交投影矩阵
EditorLayer.cpp // 初始化摄像机实体 m_CameraEntity = m_ActiveScene->CreateEntity("Camera Entity"); m_CameraEntity.AddComponent<CameraComponent>(glm::ortho(-16.0f, 16.0f, -9.0f, 9.0f, -1.0f, 1.0f));
-
在scene onupdate方法中寻找主摄像机,以及获取主摄像机的transform用来计算视图矩阵
Scene.cpp void Scene::OnUpdate(Timestep ts) { // 获取到主摄像机,并且获取到摄像机的位置,用来计算投影矩阵projection Camera* mainCamera = nullptr; glm::mat4* cameraTransform = nullptr; { auto group = m_Registry.view<TransformComponent, CameraComponent>(); for (auto entity : group){ auto &[transform, camera] = group.get<TransformComponent, CameraComponent>(entity); if (camera.primary) { mainCamera = &camera.camera; cameraTransform = &transform.Transform; } } } /// // 注意这,BeginScene中传入主摄像机的投影矩阵与主摄像机的transform矩阵 if (mainCamera) { Renderer2D::BeginScene(mainCamera->GetProjection(), *cameraTransform); auto group = m_Registry.group<TransformComponent>(entt::get<SpriteRendererComponent>); for (auto entity : group) { auto& [transform, sprite] = group.get<TransformComponent, SpriteRendererComponent>(entity); Renderer2D::DrawQuad(transform, sprite.Color); } Renderer2D::EndScene(); } }
-
在渲染器的beginscene方法中投影矩阵*视图矩阵,并且上传到opengl中
Renderer2D.cpp void Renderer2D::BeginScene(const Camera& camera, const glm::mat4& transform) { // 投影矩阵projection * 视图矩阵 glm::mat4 viewProj = camera.GetProjection() * glm::inverse(transform); s_Data.TextureShader->Bind(); // 绑定shader s_Data.TextureShader->SetMat4("u_ViewProjection", viewProj);
两个实体,每个实体有Camera组件的效果
-
实体(摄像机)1当作是主摄像机
-
实体(摄像机)2当作是主摄像机
问题
-
问题详情
为什么修改transform矩阵的第四列就能改变场景的摄像机的位置
/* 一开始以为:Transform[3]是第四行 后面查资料才发现Transform[3]应该是第四列 */ ImGui::DragFloat3("Camera Transform", glm::value_ptr(m_CameraEntity.GetComponent<TransformComponent>().Transform[3]));
transform矩阵是平移矩阵*旋转矩阵*缩放矩阵的结合,由于没有设置旋转、缩放矩阵,所以
可以这里可以认为transform矩阵就是translate平移矩阵
-
translate平移矩阵图参考资料
-
其中第四列就是摄像机的位置
-
transform矩阵就是translate平移矩阵
-
第四列用Transform[3]表示,所以改变Transform[3]就能改变摄像机的位置
-
-
另外讲些原理参考资料
-
由034小节,摄像机左移界面其实显示物体右移
-
所以这里改变摄像机的transform的第四列其实并未改变摄像机的位置
-
而是在Renderer2D::BeginScene中进行计算摄像机的视图矩阵,与投影矩阵相乘后上传给OpenGL
-
OpenGL的着色器中用投影矩阵*视图矩阵*物体的顶点位置
-
将物体的顶点位置经过视图矩阵变换到观察空间
(物体的顶点位置根据transform矩阵变换到具体位置,此节是顶点根据translate矩阵平移到指定位置)
所以此节修改transform[3]的值会在这里生效
-
再经过投影矩阵变换到裁剪空间、到标准化设备坐标、到屏幕坐标(显示在屏幕上)
-
-
表面上是摄像机在移动,其实是组成物体的所有顶点进行的移动。
void Renderer2D::BeginScene(const Camera& camera, const glm::mat4& transform){ // 投影矩阵projection * 视图矩阵 glm::mat4 viewProj = camera.GetProjection() * glm::inverse(transform); s_Data.TextureShader->Bind(); // 绑定shader s_Data.TextureShader->SetMat4("u_ViewProjection", viewProj);
-