Hazel游戏引擎(033)正交摄像机实现

文中若有代码、术语等错误,欢迎指正

摄像机实现原理BLOG:https://blog.csdn.net/qq_34060370/article/details/129391683

前言

  • 此两节目的

    为了实现一个简单的正交摄像机,能观看场景内的物体

  • 摄像机相关概念

    • 摄像机定义

      为我们提供了一种观察世界的方式,摄像机是从特定角度观察场景的所有物体,不仅是有特定角度,还可以调整视野大小观察场景。

    • 摄像机概念

      摄像机并不存在,只是计算抽象摄像机的投影与观察矩阵坐标空间,将场景内的物体先变换到观察矩阵,最后变换到投影矩阵坐标空间。

    • 摄像机属性

      fov视野大小、自己的位置、宽高比、近远平面范围等

    • 摄像机的观察矩阵推导

      • 介绍工作方式

        摄像机往移动,其实是场景内的物体往移动

        摄像机往旋转,其实是场景内的物体往旋转

      • 摄像机观察矩阵如何计算

        摄像机的位置、旋转角度组成的变换矩阵transform取逆后的矩阵view就是观察矩阵,用此观察矩阵乘以场景内的物体变换到这个坐标空间

        取逆是一种计算观察矩阵方法,第二种是用欧拉角+LookAt计算观察矩阵方法(文章开头链接Blog是这种)

    • 公式

      • OpenGL

        • 写代码顺序: project * view * world * verpos

          摄像机移动后,最好将最新的摄像机的proj*view矩阵在CPU上计算后再传入到GLSL

          因为若将proj与view传到GPU上,每个物体都要做一遍proj*view这个矩阵乘法,可以放到CPU上这样只需做一次+上传即可。

        • 读顺序:从右往左

      • Directx

        • 写代码顺序:verpos * world * view * project
  • API设计

    Renderer::BeginScene(camera);
    Renderer::Submit(m_Shader1, VertexArray1);// 给场景提交要渲染的物体
    Renderer::Submit(m_Shader2, VertexArray2);// 给场景提交要渲染的物体
    Renderer::EndScene();
    Renderer::Flush();
    void BeingScene(Camera& camera){
          
          
        // 在cpp中计算Project * view矩阵,并存储起来,
        this->ViewProjectionMatrix =  camera.getProjectionMatrix() * camera.getViewMatrix();
    }
    void Submit(Shader& shader, VertexArray& v){
          
          
        shader->Bind();						// 着色器绑定
        glUniformvec4("viewprojection", this->ViewProjectionMatrix);// 上传给Uniform
        v->bind();	// 绑定顶点数组
        RenderCommand::DrawIndexed(vertexArray);// drawcall
    }
    
  • 此节完成类图

    请添加图片描述

记录思考点

关键代码

// 投影矩阵计算
OrthographicCamera::OrthographicCamera(float left, float right, float bottom, float top)
    : m_ProjectionMatrix(glm::ortho(left, right, bottom, top, -1.0f, 1.0f)), m_ViewMatrix(1.0f){
    
    
        m_ViewProjectionMatrix = m_ProjectionMatrix * m_ViewMatrix;
    }
// 投影观察矩阵
void OrthographicCamera::RecalculateViewMatrix(){
    
    
    glm::mat4 transform = glm::translate(glm::mat4(1.0f), m_Position) *
    glm::rotate(glm::mat4(1.0f), glm::radians(m_Rotation), glm::vec3(0, 0, 1)); // 绕z轴旋转
	
    m_ViewMatrix = glm::inverse(transform);
    // 投影观察矩阵
    m_ViewProjectionMatrix = m_ProjectionMatrix * m_ViewMatrix;
}
// 物体上传场景
void Renderer::Submit(const std::shared_ptr<Shader>& shader, const std::shared_ptr<VertexArray>& vertexArray){
    
    
    shader->Bind();// 着色器邦迪
    shader->UploadUniformMat4("u_ViewProjection", m_SceneData->ViewProjectionMatrix);// 上传到Uniform

    vertexArray->Bind();// 顶点数组绑定
    RenderCommand::DrawIndexed(vertexArray);// drawcall
}
void Shader::UploadUniformMat4(const std::string& name, const glm::mat4& matrix){
    
    
    GLint location = glGetUniformLocation(m_RendererID, name.c_str());// 获取uniform名称的位置
    glUniformMatrix4fv(location, 1, GL_FALSE, glm::value_ptr(matrix));// 根据位置上传
}
// glsl
uniform mat4 u_ViewProjection;// 声明uniform
void main(){
    
    
    v_Position = a_Position;
    v_Color = a_Color;
    gl_Position = vec4(a_Position, 1.0);	
    // 将顶点变换到投影矩阵坐标空间(裁剪空间)下:projection * view * world * vpos
    gl_Position = u_ViewProjection * vec4(a_Position, 1.0);	
}

关于窗口比例影响图形显示

OrthographicCamera::OrthographicCamera(float left, float right, float bottom, float top)
    : m_ProjectionMatrix(glm::ortho(left, right, bottom, top, -1.0f, 1.0f)), m_ViewMatrix(1.0f){
    
    
    m_ViewProjectionMatrix = m_ProjectionMatrix * m_ViewMatrix;
}

前置要点

  • 窗口上下左右都是1与变为2的对比

    可见窗口若从1到2,三角形0.5围成的范围缩小,所以图形会变小。

    glm::ortho(-2,2,-2,2,-1.0f,1.0f)得出矩阵值为

    0.5 0	0	0
    0 	0.5 0	0
    0	0	-1	0
    0	0 	0	1
    
  • 缩小后的结果

    请添加图片描述

具体问题

  • 正方形变成长方形

    窗口是1280 * 720,当glm::ortho(-1.0f,1.0f, -1.0f, 1.0f, -1.0f, 1.0f);时候,本来正方形的蓝色quad变为长方形

    请添加图片描述

  • 修复变回正方形

    在1280*720下,left right需传入1280/720=1.7左右,将宽放大,从而左右视角变大,物体围成的宽范围缩小,从而变回正方形。

    /*
    由于窗口的大小是1280 :720,是16 / 9 = 1.77777
    那么设置m_Camera的宽设置 1.6范围,高设为0.9就可以解决。或者 1.7与1也行
    */
    Application::Application()
        :m_Camera(-1.6f, 1.6f, -0.9f, 0.9f){
          
          }
    

    请添加图片描述

GLM库函数相关

  • glm::ortho

    left = -1.0f;right = 1.0f;bottom = -1.0f;top = 1.0f
    glm::ortho(left,right, bottom, top, -1.0f, 1.0f);

    得到的矩阵是

    1 0  0 0
    0 1  0 0
    0 0 -1 0
    0 0  0 1
    
  • glm::translate(glm::mat4(1.0f), m_Position);

    m_Position= {0.5f, 0.5f, 0.5f};

    glm::mat4(1.0f),是4x4的单位矩阵

    1 0 0 0
    0 1 0 0
    0 0 1 0
    0 0 0 1
    

    glm::translate(glm::mat4(1.0f), m_Position);

    /*
        glm::translate函数中
        mat<4, 4, T, Q> Result(m); 
        Result[3] = m[0] * v[0] + m[1] * v[1] + m[2] * v[2] + m[3];
        Result[3]是第4行,m[0]是第1行,m[1]是第2行,m[2]是第3行。。。
        第四行  = (1 * 0.5,0,0,0) + (0, 1 * 0.5, 0, 0) + (0, 0, 1 * 0.5, 0) + (0, 0, 0, 1)
    */
    // 最后的结果是// 有可能反了:第四行应该与第四列交换
    1 	0 	0 	0
    0 	1 	0 	0
    0 	0 	1 	0
    0.5 0.5 0.5 1
    

全部代码

  • OrthographicCamera

    class OrthographicCamera{
          
          
    public:
        OrthographicCamera(float left, float right, float bottom, float top);
        const glm::vec3& GetPosition() const {
          
           return m_Position; }
        void SetPosition(const glm::vec3& position) {
          
           m_Position = position; RecalculateViewMatrix(); }
        float GetRotation() const {
          
           return m_Rotation; }
        void SetRotation(float rotation) {
          
           m_Rotation = rotation; RecalculateViewMatrix(); }
        const glm::mat4& GetProjectionMatrix() const {
          
           return m_ProjectionMatrix; }
        const glm::mat4& GetViewMatrix() const {
          
           return m_ViewMatrix; }
        const glm::mat4& GetViewProjectionMatrix() const {
          
           return m_ViewProjectionMatrix; }
    private:
        void RecalculateViewMatrix();
     private:
        glm::mat4 m_ProjectionMatrix;
        glm::mat4 m_ViewMatrix;
        glm::mat4 m_ViewProjectionMatrix;
        glm::vec3 m_Position = {
          
           0.0f, 0.0f, 0.0f };// 位置
        float m_Rotation = 0.0f;					// 绕z轴的旋转角度
    };
    
    // 初始化用glm计算正交投影矩阵
    OrthographicCamera::OrthographicCamera(float left, float right, float bottom, float top)
        : m_ProjectionMatrix(glm::ortho(left, right, bottom, top, -1.0f, 1.0f)), m_ViewMatrix(1.0f)
        {
          
          
            m_ViewProjectionMatrix = m_ProjectionMatrix * m_ViewMatrix;
        }
    // 投影观察矩阵计算
    void OrthographicCamera::RecalculateViewMatrix()
    {
          
          
        // 观察矩阵
        glm::mat4 transform = glm::translate(glm::mat4(1.0f), m_Position) *
            glm::rotate(glm::mat4(1.0f), glm::radians(m_Rotation), glm::vec3(0, 0, 1));
    
        m_ViewMatrix = glm::inverse(transform);
        m_ViewProjectionMatrix = m_ProjectionMatrix * m_ViewMatrix;
    }
    
  • Renderer

    class Renderer{
          
          
    public:
        static void BeginScene(OrthographicCamera& camera);	// 开始场景
        static void EndScene();		// 结束场景
        static void Submit(const std::shared_ptr<Shader>& shader, const std::shared_ptr<VertexArray>& vertexArray);// 提交物体的顶点数组
        inline static RendererAPI::API GetAPI() {
          
           return RendererAPI::GetAPI(); }
    private:
        struct SceneData {
          
          
            glm::mat4 ViewProjectionMatrix;
        };
        static SceneData* m_SceneData;
    };
    
    Renderer::SceneData* Renderer::m_SceneData = new Renderer::SceneData;
    void Renderer::BeginScene(OrthographicCamera& camera){
          
          
        m_SceneData->ViewProjectionMatrix = camera.GetViewProjectionMatrix(); // 保存计算的Projection * view矩阵
    }
    void Renderer::EndScene(){
          
          }
    void Renderer::Submit(const std::shared_ptr<Shader>& shader, const std::shared_ptr<VertexArray>& vertexArray){
          
          
        shader->Bind();			// 着色器绑定
        shader->UploadUniformMat4("u_ViewProjection", m_SceneData->ViewProjectionMatrix);// 上传投影观察矩阵
    
        vertexArray->Bind();// 顶点数组绑定
        RenderCommand::DrawIndexed(vertexArray);// drawcall
    }
    
  • Shader

    void UploadUniformMat4(const std::string& name, const glm::mat4& matrix);
    void Shader::UploadUniformMat4(const std::string& name, const glm::mat4& matrix){
          
          
        GLint location = glGetUniformLocation(m_RendererID, name.c_str());
        glUniformMatrix4fv(location, 1, GL_FALSE, glm::value_ptr(matrix));
    }
    
  • Application

    	OrthographicCamera m_Camera;
    private:
    	static Application* s_Instance;
    
    Application::Application() : m_Camera(-1.6f, 1.6f, -0.9f, 0.9f){
          
          
        // 着色器代码
        std::string vertexSrc = R"(
    			#version 330 core
    
    			layout(location = 0) in vec3 a_Position;
    			layout(location = 1) in vec4 a_Color;
    			uniform mat4 u_ViewProjection;
    			out vec3 v_Position;
    			out vec4 v_Color;
    			void main()
    			{
    				v_Position = a_Position;
    				v_Color = a_Color;
    				gl_Position = u_ViewProjection * vec4(a_Position, 1.0);	
    			}
    		)";
    void Application::Run(){
          
          
        while (m_Running){
          
          
            RenderCommand::SetClearColor({
          
           0.1f, 0.1f, 0.1f, 1 });
            RenderCommand::Clear();
    		/*
    			5和6指明近和远平面范围
    			glm::ortho(left, right, bottom, top, -1.0f, 1.0f)
    			摄像机位置的z轴位置只要-1~1之间就行
    		*/
            m_Camera.SetPosition({
          
           0.5f, 0.5f, 0.0f });
            m_Camera.SetRotation(45.0f);
    
            Renderer::BeginScene(m_Camera);
            // 绘制四边形
            Renderer::Submit(m_BlueShader, m_SquareVA);
    
            // 绘制三角形
            Renderer::Submit(m_Shader, m_VertexArray);
    
            Renderer::EndScene();
    

猜你喜欢

转载自blog.csdn.net/qq_34060370/article/details/131496185