批渲染中的旋转处理
这章我们继续完善批渲染,支持旋转处理。
回顾下通用的顶点着色器的代码:
c++
void main()
{
v_Color = a_Color;
v_TexCoord = a_TexCoord;
v_TexIndex = a_TexIndex;
gl_Position = u_ViewProjection * u_Transform * vec4(a_Position, 1.0);
}
一个顶点的坐标计算依赖"投影矩阵(u_ViewProjection)"、"移动变换(u_Transform)"、"原始坐标(a_Position)",其中u_Transform是由程序中传进来的通用变量,一个完整的网格有一个u_Transform,多个网格就对应多个u_Transform,
如果要实现多个网格批渲染,就需要类似color一样,将u_Transform作为顶点属性传进来,以实现每个顶点的u_Transform都不一样。
另一个方法是,在shader外面将transform提前乘到position上,即传到shader中的顶点已经计算过旋转、缩放、位移了。shader修改为:
c++
void main()
{
v_Color = a_Color;
v_TexCoord = a_TexCoord;
v_TexIndex = a_TexIndex;
v_TilingFactor = a_TilingFactor;
// gl_Position = u_ViewProjection * u_Transform * vec4(a_Position, 1.0);
gl_Position = u_ViewProjection * vec4(a_Position, 1.0);
}
最终的实现效果如下,demo中增加旋转的矩形:
代码实现
整体思路是,在draw函数中,将旋转乘到position上
Sandbox/Hazel/src/Hazel/Renderer/Renderer2D.cpp
c++
// 1. 增加单位矩阵的顶点
struct Renderer2DData{
....
std::array<Ref<Texture2D>, MaxTexturesSlots> TextureSlots;
uint32_t TextureSlotIndex = 1; // 0 = white texture
glm::vec4 QuadVertexPositions[4]; // 矩形的四个顶点
}
// 2. 初始化单位矩阵的顶点
void Renderer2D::Init() {
....
// Set all texture slots to 0
// 安全起见,将数组中的每个值都初始化成一个默认的纹理,即单像素纹理
for (int i = 0; i < s_Data->MaxTexturesSlots; i++) {
s_Data->TextureSlots[i] = s_Data->WhiteTexture;
}
// 初始化单位矩阵的顶点坐标
s_Data->QuadVertexPositions[0] = {-0.5f, -0.5f, 0.0f, 1.0f};
s_Data->QuadVertexPositions[1] = {0.5f, -0.5f, 0.0f, 1.0f};
s_Data->QuadVertexPositions[2] = {0.5f, 0.5f, 0.0f, 1.0f};
s_Data->QuadVertexPositions[3] = {-0.5f, 0.5f, 0.0f, 1.0f};
}
// 3. DrawQuad函数中增加旋转处理
void Renderer2D::DrawQuad(const glm::vec3 &position, const glm::vec2 &size, float rotation,
const glm::vec4 &color) {
HZ_PROFILE_FUNCTION();
const float texIndex = 0.0f; // white Texture
const float tilingFactor = 1.0f;
glm::mat4 transform = glm::translate(glm::mat4(1.0f), position)
* glm::rotate(glm::mat4(1.0f), glm::radians(rotation), {0.0f, 0.0f, 1.0f})
* glm::scale(glm::mat4(1.0f), {size.x, size.y, 1.0f});
s_Data->TextureShader->SetFloat4("u_Color", color);
s_Data->TextureShader->SetFloat("u_TilingFactor", 1.0f);
s_Data->WhiteTexture->Bind();
s_Data->QuadVertexBufferPtr->Position = transform * s_Data->QuadVertexPositions[0];
...
s_Data->QuadVertexBufferPtr->Position = transform * s_Data->QuadVertexPositions[1];
...
s_Data->QuadVertexBufferPtr->Position = transform * s_Data->QuadVertexPositions[2];
...
s_Data->QuadVertexBufferPtr->Position = transform * s_Data->QuadVertexPositions[3];
}
...
一共有4个重载的Renderer2D::DrawQuad函数,都需要改进,将transform提前计算好,乘到position上。此处仅说明实现思路,完整代码参考文末github地址。
demo中增加旋转的矩阵,此处我们根据update的时间,动态的增加旋转角度
Sandbox/src/Sandbox2D.cpp
c++
{
HZ_PROFILE_SCOPE("Renderer Draw");
static float rotation = 0.f;
rotation += ts * 50.0f;
Hazel::Renderer2D::BeginScene(m_CameraController.GetCamera());
// 旋转45°
Hazel::Renderer2D::DrawQuad({1.0f, 0.0f}, {0.8f, 0.8f}, -45, {0.8f, 0.2f, 0.3f, 1.0f});
// 静态矩形
Hazel::Renderer2D::DrawQuad({-1.0f, 0.0f}, {0.8f, 0.8f}, {0.8f, 0.2f, 0.3f, 1.0f});
Hazel::Renderer2D::DrawQuad({0.5f, -0.5f}, {0.5f, 0.75f}, {0.2f, 0.3f, 0.8f, 1.0f});
Hazel::Renderer2D::DrawQuad({0.0f, 0.0f, -0.1f}, {10.0f, 10.0f}, m_CheckerboardTexture, 10.0f);
// 持续旋转45°
Hazel::Renderer2D::DrawQuad({1.f, 0.5f, 0.1f}, {0.8f, 0.8f}, rotation, m_CoverTexture, 1.0f);
Hazel::Renderer2D::EndScene();
}
完整代码 & 总结
完整代码
总结
批渲染的核心,就是将所有要绘制的数据尽量统一起来,去异求同,将不同的地方提前处理好或者在shader中按参数动态计算,以达到使用同一个shader,不切换状态,一次绘制上屏。
最近工作有点累,加上流感盛行,娃老生病,有点干不动了。加油💪🏻