本节主要是完成一个正交相机的实现:
创建OrthographicCamera.h
OrthographicCamera.cpp
,然后添加相关函数:
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; RecalculateViewMatirx(); }
float GetRotation() const { return m_Rotation; }
void SetRotation(float rotation) { m_Rotation = rotation; RecalculateViewMatirx(); }
const glm::mat4 GetProjectionMatrix() { return m_ProjectionMatrix; }
const glm::mat4 GetViewMatrix() { return m_ViewMatrix; }
const glm::mat4 GetViewProjectionMatrix() { return m_ViewProjectionMatrix; }
private:
//use for update
void RecalculateViewMatirx();
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;
};
}
在正交相机中,我们拥有相机的 View 矩阵和 Projection 矩阵,这两个矩阵用于场景中所有物体的视口投影变换,最终结果是将所有物体变换到一个 (-1, 1) 的立方体中,此外,为了控制相机的移动和旋转,我们还拥有相机的位置和旋转角这两个参数,在函数的设置中,除了获取和设置相机的 Pos、 Rotation,还能够取得相机的 VP矩阵。
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::RecalculateViewMatirx()
{
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);
//注意矩阵相乘的顺序,先乘 view 矩阵 之后再乘 proj 矩阵
m_ViewProjectionMatrix = m_ProjectionMatrix * m_ViewMatrix;
}
在构造函数中,利用传入的四个视口的参数初始化了用于正交投影的投影矩阵,这里目前没有添加远近平面,而是简单的设置为了 -1 和 1 , View 矩阵也仅仅是用单位矩阵进行了初始化,然后计算得到 VP 矩阵,然后由于我们每次设置完相机的位置和旋转之后,需要 update VP 矩阵,所以设置了RecalculateViewMatirx
这个函数,这个函数是根据修改后的相机位置和旋转角度,重新计算 VP 矩阵,具体做法是,相机进行 transform 之后,View 矩阵便是其逆矩阵,因为 View 矩阵的作用是假定相机在原点不动,将所有物体沿着相机移动的相反方向移动,以实现相机的移动。
然后在 BeginScene
函数中我们要获取渲染的一些基本数据,例如 Camera 的属性参数,环境光照的各种信息,这里我们仅仅只是先添加了相机的参数,在 renderer 中,创建了一个 SceneData 的结构体,用于保存相机的 VP 矩阵。
struct SceneData
{
glm::mat4 ViewProjectionMatrix;
};
static SceneData* s_SceneData;
然后在 BeginScene 中进行该 data 的初始化:
void Renderer::BeginScene(OrthographicCamera& camera)
{
s_SceneData->ViewProjectionMatrix = camera.GetViewProjectionMatrix();
}
然后我们在 Submit 函数中希望进行 Shader 的 Bind 以及 unform 变量的设置,对于 uniform ,实际上就是把我们经常会用到的数据放在一块区域,然后放在显存中的某个区域,这样就不用每次从内存读取数据。
Uniform变量 -- 即统一变量,简单理解就是一个 GLSL shader 中的全局常量,可以随意在任意shader(vertex shader, geometry shader, or fragment shader)访问,不同的shader中uniform是一起链接的,初始化之后,不能修改其值,否则会引起编译错误。
所以在 Shader 中需要完成 uniform 变量的设置:
void Shader::UpLoadUniformMat4(const std::string& name, const glm::mat4 matrix)
{
//返回一个 int 值,这个值会验证我们传入的值是否有效
GLint location = glGetUniformLocation(m_RendererID, name.c_str());
glUniformMatrix4fv(location, 1, GL_FALSE, glm::value_ptr(matrix));
}
之后 Submit 函数:
void Renderer::Submit(const std::shared_ptr<Shader>& shader, const std::shared_ptr<VertexArray>& vertexArray)
{
//在OpenGL 中 Bind 顺序不分先后
shader->Bind();
shader->UpLoadUniformMat4("u_ViewProjection", s_SceneData->ViewProjectionMatrix);
vertexArray->Bind();
RenderCommand::DrawIndexed(vertexArray);
}
在 Application 中添加成员变量 m_Camera 然后在构造函数中进行初始化,之后在 Shader 中设置 uniform 变量然后进行测试:
Application::Application()
:m_Camera(-1.6f, 1.6f, -0.9f, 0.9f)
//VertexShader
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);
}
//RunLoop
RenderCommand::SetClearColor({ 0.1f, 0.1f, 0.1f, 1 });
RenderCommand::Clear();
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();