Extra2D/Extra2D/src/graphics/backends/opengl/gl_renderer.cpp

1087 lines
31 KiB
C++
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

#include <cmath>
#include <cstring>
#include <extra2d/core/service_locator.h>
#include <extra2d/graphics/backends/opengl/gl_context.h>
#include <extra2d/graphics/backends/opengl/gl_font_atlas.h>
#include <extra2d/graphics/backends/opengl/gl_framebuffer.h>
#include <extra2d/graphics/backends/opengl/gl_renderer.h>
#include <extra2d/graphics/backends/opengl/gl_texture.h>
#include <extra2d/graphics/batch/sprite_batch.h>
#include <extra2d/graphics/memory/gpu_context.h>
#include <extra2d/graphics/memory/vram_manager.h>
#include <extra2d/graphics/shader/shader_manager.h>
#include <extra2d/platform/iwindow.h>
#include <extra2d/services/logger_service.h>
#include <vector>
namespace extra2d {
// VBO 初始大小(用于 VRAM 跟踪)
static constexpr size_t SHAPE_VBO_SIZE = 1024 * sizeof(float);
/**
* @brief 构造函数初始化OpenGL渲染器成员变量
*/
GLRenderer::GLRenderer()
: window_(nullptr), shapeVao_(0), lineVao_(0), vsync_(true),
shapeVertexCount_(0), currentShapeMode_(GL_TRIANGLES),
lineVertexCount_(0), currentLineWidth_(1.0f) {
resetStats();
for (auto &v : shapeVertexCache_) {
v = {0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f};
}
for (auto &v : lineVertexCache_) {
v = {0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f};
}
}
/**
* @brief 析构函数调用shutdown释放资源
*/
GLRenderer::~GLRenderer() { shutdown(); }
/**
* @brief 初始化OpenGL渲染器
* @param window 窗口指针
* @return 初始化成功返回true失败返回false
*/
bool GLRenderer::init(IWindow *window) {
window_ = window;
// 初始化 OpenGL 上下文Switch 平台已通过 SDL2 + EGL 初始化GLContext
// 会处理兼容性)
if (!GLContext::get().init()) {
E2D_LOG_ERROR("初始化 OpenGL 上下文失败");
return false;
}
// 初始化精灵批渲染器
if (!spriteBatch_.init()) {
E2D_LOG_ERROR("初始化精灵批处理失败");
return false;
}
// 初始化形状渲染
initShapeRendering();
// 初始化管线状态管理
PipelineDesc pipelineDesc;
pipelineDesc.blendMode = BlendMode::Alpha;
pipelineDesc.depthTest = false;
pipelineDesc.depthWrite = false;
if (!pipeline_.init(pipelineDesc)) {
E2D_LOG_ERROR("初始化 GLPipeline 失败");
return false;
}
// 应用初始管线状态
pipeline_.applyAllStates();
// 标记 GPU 上下文为有效
GPUContext::get().markValid();
E2D_LOG_INFO("OpenGL 渲染器初始化成功");
E2D_LOG_INFO("OpenGL 版本: {}", GLContext::get().getVersionString());
return true;
}
/**
* @brief 关闭渲染器释放所有GPU资源
*/
void GLRenderer::shutdown() {
// 标记 GPU 上下文为无效
// 这会在销毁 OpenGL 上下文之前通知所有 GPU 资源
GPUContext::get().markInvalid();
spriteBatch_.shutdown();
// 关闭 GLBuffer自动释放 VBO
lineBuffer_.shutdown();
shapeBuffer_.shutdown();
// 删除 VAOVAO 仍然手动管理)
if (lineVao_ != 0) {
glDeleteVertexArrays(1, &lineVao_);
lineVao_ = 0;
}
if (shapeVao_ != 0) {
glDeleteVertexArrays(1, &shapeVao_);
shapeVao_ = 0;
}
// 关闭 OpenGL 上下文
GLContext::get().shutdown();
}
/**
* @brief 开始新帧,清除颜色缓冲区并重置统计信息
* @param clearColor 清屏颜色
*/
void GLRenderer::beginFrame(const Color &clearColor) {
// 应用管线状态
pipeline_.applyAllStates();
glClearColor(clearColor.r, clearColor.g, clearColor.b, clearColor.a);
glClear(GL_COLOR_BUFFER_BIT);
resetStats();
}
/**
* @brief 结束当前帧,刷新所有待处理的渲染批次
*/
void GLRenderer::endFrame() {
// 刷新所有待处理的精灵批次(自动批处理)
if (autoBatchEnabled_ && batchActive_) {
flush();
}
// 刷新所有待处理的形状批次
flushShapeBatch();
// 刷新所有待处理的线条批次
flushLineBatch();
}
/**
* @brief 设置视口区域
* @param x 视口左下角X坐标
* @param y 视口左下角Y坐标
* @param width 视口宽度
* @param height 视口高度
*/
void GLRenderer::setViewport(int x, int y, int width, int height) {
// 使用 GLPipeline 管理视口状态
pipeline_.setViewport(x, y, width, height);
}
/**
* @brief 设置垂直同步
* @param enabled true启用垂直同步false禁用
*/
void GLRenderer::setVSync(bool enabled) {
vsync_ = enabled;
// 通过窗口接口设置垂直同步
if (window_) {
window_->setVSync(enabled);
}
}
/**
* @brief 设置混合模式
* @param mode 混合模式枚举值
*/
void GLRenderer::setBlendMode(BlendMode mode) {
// 使用 GLPipeline 管理混合状态
pipeline_.setBlendMode(mode);
}
/**
* @brief 设置视图投影矩阵
* @param matrix 4x4视图投影矩阵
*/
void GLRenderer::setViewProjection(const glm::mat4 &matrix) {
viewProjection_ = matrix;
}
/**
* @brief 压入变换矩阵到变换栈
* @param transform 变换矩阵
*/
void GLRenderer::pushTransform(const glm::mat4 &transform) {
if (transformStack_.empty()) {
transformStack_.push_back(transform);
} else {
transformStack_.push_back(transformStack_.back() * transform);
}
}
/**
* @brief 从变换栈弹出顶部变换矩阵
*/
void GLRenderer::popTransform() {
if (!transformStack_.empty()) {
transformStack_.pop_back();
}
}
/**
* @brief 获取当前累积的变换矩阵
* @return 当前变换矩阵,如果栈为空则返回单位矩阵
*/
glm::mat4 GLRenderer::getCurrentTransform() const {
if (transformStack_.empty()) {
return glm::mat4(1.0f);
}
return transformStack_.back();
}
/**
* @brief 创建纹理对象
* @param width 纹理宽度
* @param height 纹理高度
* @param pixels 像素数据指针
* @param channels 颜色通道数
* @return 创建的纹理智能指针
*/
Ptr<Texture> GLRenderer::createTexture(int width, int height,
const uint8_t *pixels, int channels) {
return makePtr<GLTexture>(width, height, pixels, channels);
}
/**
* @brief 从文件加载纹理
* @param filepath 纹理文件路径
* @return 加载的纹理智能指针
*/
Ptr<Texture> GLRenderer::loadTexture(const std::string &filepath) {
return makePtr<GLTexture>(filepath);
}
/**
* @brief 确保批处理已激活(自动批处理内部使用)
*/
void GLRenderer::ensureBatchActive() {
if (!batchActive_) {
spriteBatch_.begin(viewProjection_);
batchActive_ = true;
currentBatchTexture_ = nullptr;
pendingSprites_.clear();
}
}
/**
* @brief 提交待处理的精灵(自动批处理内部使用)
*/
void GLRenderer::submitPendingSprites() {
if (pendingSprites_.empty()) {
return;
}
// 提交所有待处理的精灵
spriteBatch_.drawBatch(*currentBatchTexture_, pendingSprites_);
pendingSprites_.clear();
currentBatchTexture_ = nullptr;
}
/**
* @brief 开始手动精灵批处理(高级用法)
* @note 一般情况下不需要调用drawSprite/drawText 会自动管理批处理
*/
void GLRenderer::beginSpriteBatch() {
// 如果自动批处理已激活,先提交
if (autoBatchEnabled_ && batchActive_) {
flush();
}
// 禁用自动批处理,进入手动模式
autoBatchEnabled_ = false;
spriteBatch_.begin(viewProjection_);
batchActive_ = true;
}
/**
* @brief 绘制精灵(带完整参数)
* @param texture 纹理引用
* @param destRect 目标矩形(屏幕坐标)
* @param srcRect 源矩形(纹理坐标)
* @param tint 着色颜色
* @param rotation 旋转角度(度)
* @param anchor 锚点位置0-1范围
*/
void GLRenderer::drawSprite(const Texture &texture, const Rect &destRect,
const Rect &srcRect, const Color &tint,
float rotation, const Vec2 &anchor) {
// 自动批处理模式
if (autoBatchEnabled_) {
ensureBatchActive();
// 如果纹理变化或缓冲区满,先提交当前批次
if (currentBatchTexture_ != &texture ||
pendingSprites_.size() >= MAX_BATCH_SPRITES) {
submitPendingSprites();
currentBatchTexture_ = &texture;
}
// 创建精灵数据
SpriteData data;
data.position = Vec2(destRect.origin.x, destRect.origin.y);
data.size = Vec2(destRect.size.width, destRect.size.height);
Texture *tex = const_cast<Texture *>(&texture);
float texW = static_cast<float>(tex->getWidth());
float texH = static_cast<float>(tex->getHeight());
// 纹理坐标计算
float u1 = srcRect.origin.x / texW;
float u2 = (srcRect.origin.x + srcRect.size.width) / texW;
float v1 = srcRect.origin.y / texH;
float v2 = (srcRect.origin.y + srcRect.size.height) / texH;
data.uvRect = Rect(Vec2(glm::min(u1, u2), glm::min(v1, v2)),
Size(glm::abs(u2 - u1), glm::abs(v2 - v1)));
data.color = tint;
data.rotation = rotation * 3.14159f / 180.0f;
data.pivot = Vec2(anchor.x, anchor.y);
// 添加到待处理列表
pendingSprites_.push_back(data);
} else {
// 手动批处理模式
SpriteData data;
data.position = Vec2(destRect.origin.x, destRect.origin.y);
data.size = Vec2(destRect.size.width, destRect.size.height);
Texture *tex = const_cast<Texture *>(&texture);
float texW = static_cast<float>(tex->getWidth());
float texH = static_cast<float>(tex->getHeight());
float u1 = srcRect.origin.x / texW;
float u2 = (srcRect.origin.x + srcRect.size.width) / texW;
float v1 = srcRect.origin.y / texH;
float v2 = (srcRect.origin.y + srcRect.size.height) / texH;
data.uvRect = Rect(Vec2(glm::min(u1, u2), glm::min(v1, v2)),
Size(glm::abs(u2 - u1), glm::abs(v2 - v1)));
data.color = tint;
data.rotation = rotation * 3.14159f / 180.0f;
data.pivot = Vec2(anchor.x, anchor.y);
spriteBatch_.draw(texture, data);
}
}
/**
* @brief 绘制精灵(简化版本)
* @param texture 纹理引用
* @param position 绘制位置
* @param tint 着色颜色
*/
void GLRenderer::drawSprite(const Texture &texture, const Vec2 &position,
const Color &tint) {
Rect destRect(position.x, position.y, static_cast<float>(texture.getWidth()),
static_cast<float>(texture.getHeight()));
Rect srcRect(0, 0, static_cast<float>(texture.getWidth()),
static_cast<float>(texture.getHeight()));
drawSprite(texture, destRect, srcRect, tint, 0.0f, Vec2(0, 0));
}
/**
* @brief 结束手动精灵批处理并提交绘制
* @note 一般情况下不需要调用
*/
void GLRenderer::endSpriteBatch() {
if (autoBatchEnabled_) {
// 自动模式下,只是标记批处理结束
flush();
} else {
// 手动模式下,提交批处理并恢复自动模式
spriteBatch_.end();
stats_.drawCalls += spriteBatch_.getDrawCallCount();
batchActive_ = false;
autoBatchEnabled_ = true;
}
}
/**
* @brief 立即提交当前批处理
* @note 手动控制批处理提交时机,一般情况下不需要调用
*/
void GLRenderer::flush() {
if (autoBatchEnabled_ && batchActive_) {
submitPendingSprites();
spriteBatch_.end();
stats_.drawCalls += spriteBatch_.getDrawCallCount();
batchActive_ = false;
currentBatchTexture_ = nullptr;
}
}
/**
* @brief 绘制线段
* @param start 起点坐标
* @param end 终点坐标
* @param color 线条颜色
* @param width 线条宽度
*/
void GLRenderer::drawLine(const Vec2 &start, const Vec2 &end,
const Color &color, float width) {
// 如果线宽改变,需要先刷新线条批次
if (width != currentLineWidth_) {
flushLineBatch();
currentLineWidth_ = width;
}
// 添加两个顶点到线条缓冲区
addLineVertex(start.x, start.y, color);
addLineVertex(end.x, end.y, color);
}
void GLRenderer::drawRect(const Rect &rect, const Color &color, float width) {
// 如果线宽改变,需要先刷新线条批次
if (width != currentLineWidth_) {
flushLineBatch();
currentLineWidth_ = width;
}
float x1 = rect.origin.x;
float y1 = rect.origin.y;
float x2 = rect.origin.x + rect.size.width;
float y2 = rect.origin.y + rect.size.height;
// 4条线段 = 8个顶点
// 上边
addLineVertex(x1, y1, color);
addLineVertex(x2, y1, color);
// 右边
addLineVertex(x2, y1, color);
addLineVertex(x2, y2, color);
// 下边
addLineVertex(x2, y2, color);
addLineVertex(x1, y2, color);
// 左边
addLineVertex(x1, y2, color);
addLineVertex(x1, y1, color);
}
/**
* @brief 填充矩形
* @param rect 矩形区域
* @param color 填充颜色
*/
void GLRenderer::fillRect(const Rect &rect, const Color &color) {
// 提交当前批次(如果模式不同)
submitShapeBatch(GL_TRIANGLES);
// 添加两个三角形组成矩形6个顶点
float x1 = rect.origin.x;
float y1 = rect.origin.y;
float x2 = rect.origin.x + rect.size.width;
float y2 = rect.origin.y + rect.size.height;
// 三角形1: (x1,y1), (x2,y1), (x2,y2)
addShapeVertex(x1, y1, color);
addShapeVertex(x2, y1, color);
addShapeVertex(x2, y2, color);
// 三角形2: (x1,y1), (x2,y2), (x1,y2)
addShapeVertex(x1, y1, color);
addShapeVertex(x2, y2, color);
addShapeVertex(x1, y2, color);
}
/**
* @brief 绘制圆形边框
* @param center 圆心坐标
* @param radius 半径
* @param color 边框颜色
* @param segments 分段数
* @param width 线条宽度
*/
void GLRenderer::drawCircle(const Vec2 &center, float radius,
const Color &color, int segments, float width) {
// 限制段数不超过缓存大小
if (segments > static_cast<int>(MAX_CIRCLE_SEGMENTS)) {
segments = static_cast<int>(MAX_CIRCLE_SEGMENTS);
}
// 如果线宽改变,需要先刷新线条批次
if (width != currentLineWidth_) {
flushLineBatch();
currentLineWidth_ = width;
}
// 使用线条批处理绘制圆形
for (int i = 0; i < segments; ++i) {
float angle1 =
2.0f * 3.14159f * static_cast<float>(i) / static_cast<float>(segments);
float angle2 = 2.0f * 3.14159f * static_cast<float>(i + 1) /
static_cast<float>(segments);
addLineVertex(center.x + radius * cosf(angle1),
center.y + radius * sinf(angle1), color);
addLineVertex(center.x + radius * cosf(angle2),
center.y + radius * sinf(angle2), color);
}
}
/**
* @brief 填充圆形
* @param center 圆心坐标
* @param radius 半径
* @param color 填充颜色
* @param segments 分段数
*/
void GLRenderer::fillCircle(const Vec2 &center, float radius,
const Color &color, int segments) {
// 限制段数不超过缓存大小
if (segments > static_cast<int>(MAX_CIRCLE_SEGMENTS)) {
segments = static_cast<int>(MAX_CIRCLE_SEGMENTS);
}
// 提交当前批次(如果模式不同)
submitShapeBatch(GL_TRIANGLES);
// 使用三角形扇形填充圆
// 中心点 + 边缘点
for (int i = 0; i < segments; ++i) {
float angle1 =
2.0f * 3.14159f * static_cast<float>(i) / static_cast<float>(segments);
float angle2 = 2.0f * 3.14159f * static_cast<float>(i + 1) /
static_cast<float>(segments);
// 每个三角形:中心 -> 边缘点1 -> 边缘点2
addShapeVertex(center.x, center.y, color);
addShapeVertex(center.x + radius * cosf(angle1),
center.y + radius * sinf(angle1), color);
addShapeVertex(center.x + radius * cosf(angle2),
center.y + radius * sinf(angle2), color);
}
}
/**
* @brief 绘制三角形边框
* @param p1 第一个顶点
* @param p2 第二个顶点
* @param p3 第三个顶点
* @param color 边框颜色
* @param width 线条宽度
*/
void GLRenderer::drawTriangle(const Vec2 &p1, const Vec2 &p2, const Vec2 &p3,
const Color &color, float width) {
drawLine(p1, p2, color, width);
drawLine(p2, p3, color, width);
drawLine(p3, p1, color, width);
}
/**
* @brief 填充三角形
* @param p1 第一个顶点
* @param p2 第二个顶点
* @param p3 第三个顶点
* @param color 填充颜色
*/
void GLRenderer::fillTriangle(const Vec2 &p1, const Vec2 &p2, const Vec2 &p3,
const Color &color) {
submitShapeBatch(GL_TRIANGLES);
addShapeVertex(p1.x, p1.y, color);
addShapeVertex(p2.x, p2.y, color);
addShapeVertex(p3.x, p3.y, color);
}
/**
* @brief 绘制多边形边框
* @param points 顶点数组
* @param color 边框颜色
* @param width 线条宽度
*/
void GLRenderer::drawPolygon(const std::vector<Vec2> &points,
const Color &color, float width) {
if (points.size() < 2)
return;
// 如果线宽改变,需要先刷新线条批次
if (width != currentLineWidth_) {
flushLineBatch();
currentLineWidth_ = width;
}
// 绘制所有边
for (size_t i = 0; i < points.size(); ++i) {
const Vec2 &p1 = points[i];
const Vec2 &p2 = points[(i + 1) % points.size()];
addLineVertex(p1.x, p1.y, color);
addLineVertex(p2.x, p2.y, color);
}
}
/**
* @brief 填充多边形
* @param points 顶点数组
* @param color 填充颜色
*/
void GLRenderer::fillPolygon(const std::vector<Vec2> &points,
const Color &color) {
if (points.size() < 3)
return;
submitShapeBatch(GL_TRIANGLES);
// 使用三角形扇形填充
// 从第一个点开始,每两个相邻点组成一个三角形
for (size_t i = 1; i < points.size() - 1; ++i) {
addShapeVertex(points[0].x, points[0].y, color);
addShapeVertex(points[i].x, points[i].y, color);
addShapeVertex(points[i + 1].x, points[i + 1].y, color);
}
}
/**
* @brief 创建字体图集
* @param filepath 字体文件路径
* @param fontSize 字体大小
* @param useSDF 是否使用SDF渲染
* @return 创建的字体图集智能指针
*/
Ptr<FontAtlas> GLRenderer::createFontAtlas(const std::string &filepath,
int fontSize, bool useSDF) {
return makePtr<GLFontAtlas>(filepath, fontSize, useSDF);
}
/**
* @brief 绘制文本使用Vec2位置
* @param font 字体图集引用
* @param text 文本内容
* @param position 绘制位置
* @param color 文本颜色
*/
void GLRenderer::drawText(const FontAtlas &font, const std::string &text,
const Vec2 &position, const Color &color) {
drawText(font, text, position.x, position.y, color);
}
/**
* @brief 绘制文本(使用浮点坐标)
* @param font 字体图集引用
* @param text 文本内容
* @param x X坐标
* @param y Y坐标
* @param color 文本颜色
*/
void GLRenderer::drawText(const FontAtlas &font, const std::string &text,
float x, float y, const Color &color) {
float cursorX = x;
float cursorY = y;
float baselineY = cursorY + font.getAscent();
// 检查是否为SDF字体
bool isSDF = font.isSDF();
// 如果是SDF字体切换到SDF着色器
if (isSDF && sdfFontShader_) {
// 先提交当前普通批处理
if (autoBatchEnabled_ && !pendingSprites_.empty()) {
submitPendingSprites();
}
// 使用SDF着色器开始新的批处理
// 设置需要动态计算的uniform值
UniformValueMap sdfUniformValues;
sdfUniformValues["u_viewProjection"] = viewProjection_;
if (font.getTexture()) {
sdfUniformValues["u_textureSize"] = ShaderUniformValue(
glm::vec2(static_cast<float>(font.getTexture()->getWidth()),
static_cast<float>(font.getTexture()->getHeight())));
}
// 设置额外的uniform值让submitBatch使用
spriteBatch_.setExtraUniforms(sdfUniformValues);
spriteBatch_.begin(viewProjection_, sdfFontShader_);
} else {
// 确保批处理已激活(自动批处理)
if (autoBatchEnabled_) {
ensureBatchActive();
}
// 检查纹理变化,如果纹理不同则先提交当前批次
if (autoBatchEnabled_ && currentBatchTexture_ != nullptr &&
currentBatchTexture_ != font.getTexture()) {
submitPendingSprites();
}
if (autoBatchEnabled_) {
currentBatchTexture_ = font.getTexture();
}
}
// 收集所有字符数据用于批处理
std::vector<SpriteData> sprites;
sprites.reserve(text.size()); // 预分配空间
for (char c : text) {
char32_t codepoint = static_cast<char32_t>(static_cast<unsigned char>(c));
if (codepoint == '\n') {
// 换行时,将当前行添加到待处理列表
if (!sprites.empty()) {
if (isSDF && sdfFontShader_) {
// SDF模式直接提交
spriteBatch_.drawBatch(*font.getTexture(), sprites);
} else if (autoBatchEnabled_) {
pendingSprites_.insert(pendingSprites_.end(), sprites.begin(),
sprites.end());
} else {
// 手动模式直接提交
spriteBatch_.drawBatch(*font.getTexture(), sprites);
}
sprites.clear();
}
cursorX = x;
cursorY += font.getLineHeight();
baselineY = cursorY + font.getAscent();
continue;
}
const Glyph *glyph = font.getGlyph(codepoint);
if (glyph) {
float penX = cursorX;
cursorX += glyph->advance;
// 使用 epsilon 比较浮点数,避免精度问题
constexpr float EPSILON = 0.001f;
if (glyph->width < EPSILON || glyph->height < EPSILON) {
continue;
}
// 计算字形位置
// bearingX: 水平偏移(从左边缘到字形左边缘)
// bearingY: 垂直偏移(从基线到字形顶部,通常为负值)
float xPos = penX + glyph->bearingX;
float yPos = baselineY + glyph->bearingY;
SpriteData data;
// 设置精灵中心位置(精灵批处理使用中心点)
data.position =
Vec2(xPos + glyph->width * 0.5f, yPos + glyph->height * 0.5f);
data.size = Vec2(glyph->width, glyph->height);
data.uvRect = Rect(Vec2(glyph->u0, glyph->v0),
Size(glyph->u1 - glyph->u0, glyph->v1 - glyph->v0));
data.color = color;
data.rotation = 0.0f;
// pivot (0.5, 0.5) 表示中心点,这样 position 就是精灵中心
data.pivot = Vec2(0.5f, 0.5f);
sprites.push_back(data);
// 自动批处理:如果缓冲区满,先提交当前批次
if (!isSDF && autoBatchEnabled_ && sprites.size() >= MAX_BATCH_SPRITES) {
pendingSprites_.insert(pendingSprites_.end(), sprites.begin(),
sprites.end());
sprites.clear();
}
}
}
// 提交剩余的字符
if (!sprites.empty()) {
if (isSDF && sdfFontShader_) {
// SDF模式直接提交
spriteBatch_.drawBatch(*font.getTexture(), sprites);
} else if (autoBatchEnabled_) {
pendingSprites_.insert(pendingSprites_.end(), sprites.begin(),
sprites.end());
} else {
// 手动模式下直接提交
spriteBatch_.drawBatch(*font.getTexture(), sprites);
}
}
// 如果是SDF字体结束批处理并恢复普通着色器
if (isSDF && sdfFontShader_) {
spriteBatch_.end();
// 清除额外的uniform值
spriteBatch_.clearExtraUniforms();
// 恢复默认的sprite着色器
auto defaultShader = ShaderManager::getInstance().getBuiltin("sprite");
if (defaultShader) {
spriteBatch_.setShader(defaultShader);
}
}
}
/**
* @brief 重置渲染统计信息
*/
void GLRenderer::resetStats() { stats_ = Stats{}; }
/**
* @brief 初始化形状渲染所需的OpenGL资源VAO、VBO、着色器
*/
void GLRenderer::initShapeRendering() {
// 从ShaderManager获取形状着色器
shapeShader_ = ShaderManager::getInstance().getBuiltin("shape");
if (!shapeShader_) {
E2D_LOG_WARN("获取内置形状着色器失败,尝试从管理器加载");
if (!ShaderManager::getInstance().isInitialized()) {
E2D_LOG_ERROR("ShaderManager 未初始化,形状渲染可能失败");
}
}
// 加载SDF字体着色器
sdfFontShader_ = ShaderManager::getInstance().getBuiltin("sdf_font");
if (!sdfFontShader_) {
E2D_LOG_WARN("获取SDF字体着色器失败SDF字体将使用普通渲染");
} else {
E2D_LOG_INFO("SDF字体着色器加载成功");
}
// 初始化形状 GLBuffer使用 Dynamic 使用模式,因为每帧都会更新)
BufferDesc shapeBufferDesc;
shapeBufferDesc.type = BufferType::Vertex;
shapeBufferDesc.usage = BufferUsage::Dynamic;
shapeBufferDesc.size = MAX_SHAPE_VERTICES * sizeof(ShapeVertex);
if (!shapeBuffer_.init(shapeBufferDesc)) {
E2D_LOG_ERROR("初始化形状缓冲区失败");
}
// 创建形状 VAO手动管理用于顶点属性配置
glGenVertexArrays(1, &shapeVao_);
glBindVertexArray(shapeVao_);
shapeBuffer_.bind();
// 位置属性 (location = 0)
glEnableVertexAttribArray(0);
glVertexAttribPointer(0, 2, GL_FLOAT, GL_FALSE, sizeof(ShapeVertex),
reinterpret_cast<void *>(offsetof(ShapeVertex, x)));
// 颜色属性 (location = 1)
glEnableVertexAttribArray(1);
glVertexAttribPointer(1, 4, GL_FLOAT, GL_FALSE, sizeof(ShapeVertex),
reinterpret_cast<void *>(offsetof(ShapeVertex, r)));
glBindVertexArray(0);
// 初始化线条 GLBuffer使用 Dynamic 使用模式,因为每帧都会更新)
BufferDesc lineBufferDesc;
lineBufferDesc.type = BufferType::Vertex;
lineBufferDesc.usage = BufferUsage::Dynamic;
lineBufferDesc.size = MAX_LINE_VERTICES * sizeof(ShapeVertex);
if (!lineBuffer_.init(lineBufferDesc)) {
E2D_LOG_ERROR("初始化线条缓冲区失败");
}
// 创建线条 VAO手动管理用于顶点属性配置
glGenVertexArrays(1, &lineVao_);
glBindVertexArray(lineVao_);
lineBuffer_.bind();
// 位置属性 (location = 0)
glEnableVertexAttribArray(0);
glVertexAttribPointer(0, 2, GL_FLOAT, GL_FALSE, sizeof(ShapeVertex),
reinterpret_cast<void *>(offsetof(ShapeVertex, x)));
// 颜色属性 (location = 1)
glEnableVertexAttribArray(1);
glVertexAttribPointer(1, 4, GL_FLOAT, GL_FALSE, sizeof(ShapeVertex),
reinterpret_cast<void *>(offsetof(ShapeVertex, r)));
glBindVertexArray(0);
}
/**
* @brief 添加形状顶点到缓存
* @param x X坐标
* @param y Y坐标
* @param color 顶点颜色
*/
void GLRenderer::addShapeVertex(float x, float y, const Color &color) {
if (shapeVertexCount_ >= MAX_SHAPE_VERTICES) {
flushShapeBatch();
}
glm::vec4 pos(x, y, 0.0f, 1.0f);
if (!transformStack_.empty()) {
pos = transformStack_.back() * pos;
}
ShapeVertex &v = shapeVertexCache_[shapeVertexCount_++];
v.x = pos.x;
v.y = pos.y;
v.r = color.r;
v.g = color.g;
v.b = color.b;
v.a = color.a;
}
/**
* @brief 添加线条顶点到缓存
* @param x X坐标
* @param y Y坐标
* @param color 顶点颜色
*/
void GLRenderer::addLineVertex(float x, float y, const Color &color) {
if (lineVertexCount_ >= MAX_LINE_VERTICES) {
flushLineBatch();
}
glm::vec4 pos(x, y, 0.0f, 1.0f);
if (!transformStack_.empty()) {
pos = transformStack_.back() * pos;
}
ShapeVertex &v = lineVertexCache_[lineVertexCount_++];
v.x = pos.x;
v.y = pos.y;
v.r = color.r;
v.g = color.g;
v.b = color.b;
v.a = color.a;
}
/**
* @brief 提交形状批次(如果需要切换绘制模式)
* @param mode OpenGL绘制模式
*/
void GLRenderer::submitShapeBatch(GLenum mode) {
if (shapeVertexCount_ == 0)
return;
// 如果模式改变,先刷新
if (currentShapeMode_ != mode && shapeVertexCount_ > 0) {
flushShapeBatch();
}
currentShapeMode_ = mode;
}
/**
* @brief 刷新形状批次执行实际的OpenGL绘制调用
*/
void GLRenderer::flushShapeBatch() {
if (shapeVertexCount_ == 0)
return;
if (shapeShader_) {
shapeShader_->bind();
// 只提供需要动态计算的值其他值使用JSON中定义的默认值
UniformValueMap uniformValues;
uniformValues["u_viewProjection"] = viewProjection_;
// 使用ShaderManager自动应用uniform值未提供的值使用JSON中的默认值
// 使用着色器自己的名称从JSON中解析的name字段
ShaderManager::getInstance().applyUniforms(
shapeShader_, shapeShader_->getName(), uniformValues);
}
// 使用 GLBuffer::updateData() 更新缓冲区数据
shapeBuffer_.updateData(shapeVertexCache_.data(), 0,
shapeVertexCount_ * sizeof(ShapeVertex));
glBindVertexArray(shapeVao_);
glDrawArrays(currentShapeMode_, 0, static_cast<GLsizei>(shapeVertexCount_));
stats_.drawCalls++;
stats_.triangleCount += static_cast<uint32_t>(shapeVertexCount_ / 3);
shapeVertexCount_ = 0;
}
/**
* @brief 刷新线条批次执行实际的OpenGL绘制调用
*/
void GLRenderer::flushLineBatch() {
if (lineVertexCount_ == 0)
return;
// 先刷新形状批次
flushShapeBatch();
glLineWidth(currentLineWidth_);
if (shapeShader_) {
shapeShader_->bind();
// 只提供需要动态计算的值其他值使用JSON中定义的默认值
UniformValueMap uniformValues;
uniformValues["u_viewProjection"] = viewProjection_;
// 使用ShaderManager自动应用uniform值未提供的值使用JSON中的默认值
// 使用着色器自己的名称从JSON中解析的name字段
ShaderManager::getInstance().applyUniforms(
shapeShader_, shapeShader_->getName(), uniformValues);
}
// 使用 GLBuffer::updateData() 更新缓冲区数据
lineBuffer_.updateData(lineVertexCache_.data(), 0,
lineVertexCount_ * sizeof(ShapeVertex));
glBindVertexArray(lineVao_);
glDrawArrays(GL_LINES, 0, static_cast<GLsizei>(lineVertexCount_));
stats_.drawCalls++;
lineVertexCount_ = 0;
}
/**
* @brief 创建帧缓冲对象
* @param desc 帧缓冲描述
* @return 创建的帧缓冲智能指针
*/
Ptr<GLFramebuffer> GLRenderer::createFramebuffer(const FramebufferDesc &desc) {
auto framebuffer = makePtr<GLFramebuffer>();
if (!framebuffer->init(desc)) {
E2D_LOG_ERROR("创建帧缓冲区失败");
return nullptr;
}
return framebuffer;
}
/**
* @brief 绑定帧缓冲(作为渲染目标)
* @param framebuffer 帧缓冲对象指针,传入 nullptr 则绑定默认帧缓冲
*/
void GLRenderer::bindFramebuffer(GLFramebuffer *framebuffer) {
// 先刷新所有待处理的渲染批次
flush();
flushShapeBatch();
flushLineBatch();
if (framebuffer == nullptr) {
// 绑定默认帧缓冲ID 为 0
glBindFramebuffer(GL_FRAMEBUFFER, 0);
currentFramebuffer_ = nullptr;
E2D_LOG_TRACE("绑定默认帧缓冲区 (0)");
} else {
// 绑定自定义帧缓冲
framebuffer->bind();
currentFramebuffer_ = framebuffer;
E2D_LOG_TRACE("绑定自定义帧缓冲区 (ID: {})", framebuffer->getFboID());
}
}
/**
* @brief 解绑帧缓冲(恢复到默认帧缓冲)
*/
void GLRenderer::unbindFramebuffer() { bindFramebuffer(nullptr); }
/**
* @brief 获取默认帧缓冲
* @return 默认帧缓冲智能指针
*/
Ptr<GLFramebuffer> GLRenderer::getDefaultFramebuffer() const {
if (!defaultFramebuffer_) {
// 延迟创建默认帧缓冲对象代表系统默认帧缓冲ID 为 0
defaultFramebuffer_ = makePtr<GLFramebuffer>();
// 注意:默认帧缓冲不需要显式初始化,它的 FBO ID 为 0
}
return defaultFramebuffer_;
}
/**
* @brief 清除当前绑定的帧缓冲
* @param color 清除颜色
* @param clearColor 是否清除颜色缓冲
* @param clearDepth 是否清除深度缓冲
* @param clearStencil 是否清除模板缓冲
*/
void GLRenderer::clearFramebuffer(const Color &color, bool clearColor,
bool clearDepth, bool clearStencil) {
GLbitfield mask = 0;
if (clearColor) {
glClearColor(color.r, color.g, color.b, color.a);
mask |= GL_COLOR_BUFFER_BIT;
}
if (clearDepth) {
mask |= GL_DEPTH_BUFFER_BIT;
}
if (clearStencil) {
mask |= GL_STENCIL_BUFFER_BIT;
}
if (mask != 0) {
glClear(mask);
}
}
} // namespace extra2d