perf(渲染): 优化文本渲染和精灵批处理性能
- 在 GLSpriteBatch 中添加 viewProjection 缓存以减少重复设置 - 增大字体图集尺寸以支持更多字符 - 预分配字形缓冲区避免动态内存分配 - 统一使用 RGBA 格式处理 SDF 和普通字形 - 优化文本渲染使用批处理绘制 - 使用编译期生成的静态索引缓冲区 - 优化顶点变换计算减少重复运算 - 硬编码 SDF 常量到着色器中避免运行时传参
This commit is contained in:
parent
f86f9b3966
commit
4f641a2854
|
|
@ -34,9 +34,9 @@ public:
|
||||||
bool isSDF() const override { return useSDF_; }
|
bool isSDF() const override { return useSDF_; }
|
||||||
|
|
||||||
private:
|
private:
|
||||||
// 图集配置
|
// 图集配置 - 增大尺寸以支持更多字符
|
||||||
static constexpr int ATLAS_WIDTH = 512;
|
static constexpr int ATLAS_WIDTH = 1024;
|
||||||
static constexpr int ATLAS_HEIGHT = 512;
|
static constexpr int ATLAS_HEIGHT = 1024;
|
||||||
static constexpr int PADDING = 2; // 字形之间的间距
|
static constexpr int PADDING = 2; // 字形之间的间距
|
||||||
|
|
||||||
int fontSize_;
|
int fontSize_;
|
||||||
|
|
@ -56,6 +56,10 @@ private:
|
||||||
float descent_;
|
float descent_;
|
||||||
float lineGap_;
|
float lineGap_;
|
||||||
|
|
||||||
|
// 预分配字形位图缓冲区,避免每次动态分配
|
||||||
|
mutable std::vector<uint8_t> glyphBitmapCache_;
|
||||||
|
mutable std::vector<uint8_t> glyphRgbaCache_;
|
||||||
|
|
||||||
void createAtlas();
|
void createAtlas();
|
||||||
void cacheGlyph(char32_t codepoint) const;
|
void cacheGlyph(char32_t codepoint) const;
|
||||||
};
|
};
|
||||||
|
|
|
||||||
|
|
@ -79,6 +79,10 @@ private:
|
||||||
bool currentIsSDF_;
|
bool currentIsSDF_;
|
||||||
glm::mat4 viewProjection_;
|
glm::mat4 viewProjection_;
|
||||||
|
|
||||||
|
// 缓存上一帧的 viewProjection,避免重复设置
|
||||||
|
glm::mat4 cachedViewProjection_;
|
||||||
|
bool viewProjectionDirty_ = true;
|
||||||
|
|
||||||
uint32_t drawCallCount_;
|
uint32_t drawCallCount_;
|
||||||
uint32_t spriteCount_;
|
uint32_t spriteCount_;
|
||||||
uint32_t batchCount_;
|
uint32_t batchCount_;
|
||||||
|
|
|
||||||
|
|
@ -97,7 +97,8 @@ Vec2 GLFontAtlas::measureText(const std::string &text) {
|
||||||
// 创建图集纹理 - 初始化空白纹理和矩形打包上下文
|
// 创建图集纹理 - 初始化空白纹理和矩形打包上下文
|
||||||
// ============================================================================
|
// ============================================================================
|
||||||
void GLFontAtlas::createAtlas() {
|
void GLFontAtlas::createAtlas() {
|
||||||
int channels = useSDF_ ? 1 : 4;
|
// 统一使用 4 通道格式
|
||||||
|
int channels = 4;
|
||||||
std::vector<uint8_t> emptyData(ATLAS_WIDTH * ATLAS_HEIGHT * channels, 0);
|
std::vector<uint8_t> emptyData(ATLAS_WIDTH * ATLAS_HEIGHT * channels, 0);
|
||||||
texture_ = std::make_unique<GLTexture>(ATLAS_WIDTH, ATLAS_HEIGHT,
|
texture_ = std::make_unique<GLTexture>(ATLAS_WIDTH, ATLAS_HEIGHT,
|
||||||
emptyData.data(), channels);
|
emptyData.data(), channels);
|
||||||
|
|
@ -107,6 +108,12 @@ void GLFontAtlas::createAtlas() {
|
||||||
packNodes_.resize(ATLAS_WIDTH);
|
packNodes_.resize(ATLAS_WIDTH);
|
||||||
stbrp_init_target(&packContext_, ATLAS_WIDTH, ATLAS_HEIGHT, packNodes_.data(),
|
stbrp_init_target(&packContext_, ATLAS_WIDTH, ATLAS_HEIGHT, packNodes_.data(),
|
||||||
ATLAS_WIDTH);
|
ATLAS_WIDTH);
|
||||||
|
|
||||||
|
// 预分配字形缓冲区
|
||||||
|
// 假设最大字形尺寸为 fontSize * fontSize * 4 (RGBA)
|
||||||
|
size_t maxGlyphSize = static_cast<size_t>(fontSize_ * fontSize_ * 4 * 4);
|
||||||
|
glyphBitmapCache_.reserve(maxGlyphSize);
|
||||||
|
glyphRgbaCache_.reserve(maxGlyphSize);
|
||||||
}
|
}
|
||||||
|
|
||||||
// ============================================================================
|
// ============================================================================
|
||||||
|
|
@ -171,14 +178,23 @@ void GLFontAtlas::cacheGlyph(char32_t codepoint) const {
|
||||||
|
|
||||||
glyphs_[codepoint] = glyph;
|
glyphs_[codepoint] = glyph;
|
||||||
|
|
||||||
|
// 将 SDF 单通道数据转换为 RGBA 格式(统一格式)
|
||||||
|
size_t pixelCount = static_cast<size_t>(w) * static_cast<size_t>(h);
|
||||||
|
glyphRgbaCache_.resize(pixelCount * 4);
|
||||||
|
for (size_t i = 0; i < pixelCount; ++i) {
|
||||||
|
uint8_t alpha = sdf[i];
|
||||||
|
glyphRgbaCache_[i * 4 + 0] = 255; // R
|
||||||
|
glyphRgbaCache_[i * 4 + 1] = 255; // G
|
||||||
|
glyphRgbaCache_[i * 4 + 2] = 255; // B
|
||||||
|
glyphRgbaCache_[i * 4 + 3] = alpha; // A - SDF 值存储在 Alpha 通道
|
||||||
|
}
|
||||||
|
|
||||||
|
// 直接设置像素对齐为 4,无需查询当前状态
|
||||||
glBindTexture(GL_TEXTURE_2D, texture_->getTextureID());
|
glBindTexture(GL_TEXTURE_2D, texture_->getTextureID());
|
||||||
GLint prevUnpackAlignment = 4;
|
glPixelStorei(GL_UNPACK_ALIGNMENT, 4);
|
||||||
glGetIntegerv(GL_UNPACK_ALIGNMENT, &prevUnpackAlignment);
|
|
||||||
glPixelStorei(GL_UNPACK_ALIGNMENT, 1);
|
|
||||||
// OpenGL纹理坐标原点在左下角,需要将Y坐标翻转
|
// OpenGL纹理坐标原点在左下角,需要将Y坐标翻转
|
||||||
glTexSubImage2D(GL_TEXTURE_2D, 0, atlasX, ATLAS_HEIGHT - atlasY - h, w, h,
|
glTexSubImage2D(GL_TEXTURE_2D, 0, atlasX, ATLAS_HEIGHT - atlasY - h, w, h,
|
||||||
GL_RED, GL_UNSIGNED_BYTE, sdf);
|
GL_RGBA, GL_UNSIGNED_BYTE, glyphRgbaCache_.data());
|
||||||
glPixelStorei(GL_UNPACK_ALIGNMENT, prevUnpackAlignment);
|
|
||||||
|
|
||||||
stbtt_FreeSDF(sdf, nullptr);
|
stbtt_FreeSDF(sdf, nullptr);
|
||||||
return;
|
return;
|
||||||
|
|
@ -199,9 +215,10 @@ void GLFontAtlas::cacheGlyph(char32_t codepoint) const {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
std::vector<unsigned char> bitmap(
|
// 使用预分配缓冲区
|
||||||
static_cast<size_t>(w) * static_cast<size_t>(h), 0);
|
size_t pixelCount = static_cast<size_t>(w) * static_cast<size_t>(h);
|
||||||
stbtt_MakeCodepointBitmap(&fontInfo_, bitmap.data(), w, h, w, scale_, scale_,
|
glyphBitmapCache_.resize(pixelCount);
|
||||||
|
stbtt_MakeCodepointBitmap(&fontInfo_, glyphBitmapCache_.data(), w, h, w, scale_, scale_,
|
||||||
static_cast<int>(codepoint));
|
static_cast<int>(codepoint));
|
||||||
|
|
||||||
// 使用 stb_rect_pack 打包矩形
|
// 使用 stb_rect_pack 打包矩形
|
||||||
|
|
@ -244,20 +261,22 @@ void GLFontAtlas::cacheGlyph(char32_t codepoint) const {
|
||||||
glyphs_[codepoint] = glyph;
|
glyphs_[codepoint] = glyph;
|
||||||
|
|
||||||
// 将单通道字形数据转换为 RGBA 格式(白色字形,Alpha 通道存储灰度)
|
// 将单通道字形数据转换为 RGBA 格式(白色字形,Alpha 通道存储灰度)
|
||||||
std::vector<uint8_t> rgbaData(w * h * 4);
|
glyphRgbaCache_.resize(pixelCount * 4);
|
||||||
for (int i = 0; i < w * h; ++i) {
|
for (size_t i = 0; i < pixelCount; ++i) {
|
||||||
uint8_t alpha = bitmap[static_cast<size_t>(i)];
|
uint8_t alpha = glyphBitmapCache_[i];
|
||||||
rgbaData[i * 4 + 0] = 255; // R
|
glyphRgbaCache_[i * 4 + 0] = 255; // R
|
||||||
rgbaData[i * 4 + 1] = 255; // G
|
glyphRgbaCache_[i * 4 + 1] = 255; // G
|
||||||
rgbaData[i * 4 + 2] = 255; // B
|
glyphRgbaCache_[i * 4 + 2] = 255; // B
|
||||||
rgbaData[i * 4 + 3] = alpha; // A
|
glyphRgbaCache_[i * 4 + 3] = alpha; // A
|
||||||
}
|
}
|
||||||
|
|
||||||
// 更新纹理 - 将字形数据上传到图集的指定位置
|
// 更新纹理 - 将字形数据上传到图集的指定位置
|
||||||
// OpenGL纹理坐标原点在左下角,需要将Y坐标翻转
|
// 直接设置像素对齐为 4,无需查询当前状态
|
||||||
glBindTexture(GL_TEXTURE_2D, texture_->getTextureID());
|
glBindTexture(GL_TEXTURE_2D, texture_->getTextureID());
|
||||||
|
glPixelStorei(GL_UNPACK_ALIGNMENT, 4);
|
||||||
|
// OpenGL纹理坐标原点在左下角,需要将Y坐标翻转
|
||||||
glTexSubImage2D(GL_TEXTURE_2D, 0, atlasX, ATLAS_HEIGHT - atlasY - h, w, h,
|
glTexSubImage2D(GL_TEXTURE_2D, 0, atlasX, ATLAS_HEIGHT - atlasY - h, w, h,
|
||||||
GL_RGBA, GL_UNSIGNED_BYTE, rgbaData.data());
|
GL_RGBA, GL_UNSIGNED_BYTE, glyphRgbaCache_.data());
|
||||||
}
|
}
|
||||||
|
|
||||||
} // namespace extra2d
|
} // namespace extra2d
|
||||||
|
|
|
||||||
|
|
@ -444,6 +444,10 @@ void GLRenderer::drawText(const FontAtlas &font, const std::string &text,
|
||||||
float cursorY = y;
|
float cursorY = y;
|
||||||
float baselineY = cursorY + font.getAscent();
|
float baselineY = cursorY + font.getAscent();
|
||||||
|
|
||||||
|
// 收集所有字符数据用于批处理
|
||||||
|
std::vector<GLSpriteBatch::SpriteData> sprites;
|
||||||
|
sprites.reserve(text.size()); // 预分配空间
|
||||||
|
|
||||||
for (char32_t codepoint : utf8ToUtf32(text)) {
|
for (char32_t codepoint : utf8ToUtf32(text)) {
|
||||||
if (codepoint == '\n') {
|
if (codepoint == '\n') {
|
||||||
cursorX = x;
|
cursorX = x;
|
||||||
|
|
@ -464,20 +468,24 @@ void GLRenderer::drawText(const FontAtlas &font, const std::string &text,
|
||||||
float xPos = penX + glyph->bearingX;
|
float xPos = penX + glyph->bearingX;
|
||||||
float yPos = baselineY + glyph->bearingY;
|
float yPos = baselineY + glyph->bearingY;
|
||||||
|
|
||||||
Rect destRect(xPos, yPos, glyph->width, glyph->height);
|
|
||||||
|
|
||||||
GLSpriteBatch::SpriteData data;
|
GLSpriteBatch::SpriteData data;
|
||||||
data.position = glm::vec2(destRect.origin.x, destRect.origin.y);
|
data.position = glm::vec2(xPos, yPos);
|
||||||
data.size = glm::vec2(destRect.size.width, destRect.size.height);
|
data.size = glm::vec2(glyph->width, glyph->height);
|
||||||
data.texCoordMin = glm::vec2(glyph->u0, glyph->v0);
|
data.texCoordMin = glm::vec2(glyph->u0, glyph->v0);
|
||||||
data.texCoordMax = glm::vec2(glyph->u1, glyph->v1);
|
data.texCoordMax = glm::vec2(glyph->u1, glyph->v1);
|
||||||
data.color = glm::vec4(color.r, color.g, color.b, color.a);
|
data.color = glm::vec4(color.r, color.g, color.b, color.a);
|
||||||
data.rotation = 0.0f;
|
data.rotation = 0.0f;
|
||||||
data.anchor = glm::vec2(0.0f, 0.0f);
|
data.anchor = glm::vec2(0.0f, 0.0f);
|
||||||
data.isSDF = font.isSDF();
|
data.isSDF = font.isSDF();
|
||||||
spriteBatch_.draw(*font.getTexture(), data);
|
|
||||||
|
sprites.push_back(data);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 使用批处理绘制所有字符
|
||||||
|
if (!sprites.empty()) {
|
||||||
|
spriteBatch_.drawBatch(*font.getTexture(), sprites);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void GLRenderer::resetStats() { stats_ = Stats{}; }
|
void GLRenderer::resetStats() { stats_ = Stats{}; }
|
||||||
|
|
|
||||||
|
|
@ -49,6 +49,24 @@ private:
|
||||||
|
|
||||||
const TrigLookup::Tables TrigLookup::table_;
|
const TrigLookup::Tables TrigLookup::table_;
|
||||||
|
|
||||||
|
// 静态索引生成函数
|
||||||
|
static const std::array<GLuint, GLSpriteBatch::MAX_INDICES>& getIndices() {
|
||||||
|
static std::array<GLuint, GLSpriteBatch::MAX_INDICES> indices = []() {
|
||||||
|
std::array<GLuint, GLSpriteBatch::MAX_INDICES> arr{};
|
||||||
|
for (size_t i = 0; i < GLSpriteBatch::MAX_SPRITES; ++i) {
|
||||||
|
GLuint base = static_cast<GLuint>(i * GLSpriteBatch::VERTICES_PER_SPRITE);
|
||||||
|
arr[i * GLSpriteBatch::INDICES_PER_SPRITE + 0] = base + 0;
|
||||||
|
arr[i * GLSpriteBatch::INDICES_PER_SPRITE + 1] = base + 1;
|
||||||
|
arr[i * GLSpriteBatch::INDICES_PER_SPRITE + 2] = base + 2;
|
||||||
|
arr[i * GLSpriteBatch::INDICES_PER_SPRITE + 3] = base + 0;
|
||||||
|
arr[i * GLSpriteBatch::INDICES_PER_SPRITE + 4] = base + 2;
|
||||||
|
arr[i * GLSpriteBatch::INDICES_PER_SPRITE + 5] = base + 3;
|
||||||
|
}
|
||||||
|
return arr;
|
||||||
|
}();
|
||||||
|
return indices;
|
||||||
|
}
|
||||||
|
|
||||||
// 顶点着色器 (GLES 3.2)
|
// 顶点着色器 (GLES 3.2)
|
||||||
static const char *SPRITE_VERTEX_SHADER = R"(
|
static const char *SPRITE_VERTEX_SHADER = R"(
|
||||||
#version 300 es
|
#version 300 es
|
||||||
|
|
@ -70,6 +88,8 @@ void main() {
|
||||||
)";
|
)";
|
||||||
|
|
||||||
// 片段着色器 (GLES 3.2)
|
// 片段着色器 (GLES 3.2)
|
||||||
|
// SDF 常量硬编码:ONEDGE_VALUE=128/255=0.502, PIXEL_DIST_SCALE=255/64=3.98
|
||||||
|
// SDF 值存储在 Alpha 通道
|
||||||
static const char *SPRITE_FRAGMENT_SHADER = R"(
|
static const char *SPRITE_FRAGMENT_SHADER = R"(
|
||||||
#version 300 es
|
#version 300 es
|
||||||
precision highp float;
|
precision highp float;
|
||||||
|
|
@ -78,15 +98,13 @@ in vec4 vColor;
|
||||||
|
|
||||||
uniform sampler2D uTexture;
|
uniform sampler2D uTexture;
|
||||||
uniform int uUseSDF;
|
uniform int uUseSDF;
|
||||||
uniform float uSdfOnEdge;
|
|
||||||
uniform float uSdfScale;
|
|
||||||
|
|
||||||
out vec4 fragColor;
|
out vec4 fragColor;
|
||||||
|
|
||||||
void main() {
|
void main() {
|
||||||
if (uUseSDF == 1) {
|
if (uUseSDF == 1) {
|
||||||
float dist = texture(uTexture, vTexCoord).r;
|
float dist = texture(uTexture, vTexCoord).a;
|
||||||
float sd = (dist - uSdfOnEdge) * uSdfScale;
|
float sd = (dist - 0.502) * 3.98;
|
||||||
float w = fwidth(sd);
|
float w = fwidth(sd);
|
||||||
float alpha = smoothstep(-w, w, sd);
|
float alpha = smoothstep(-w, w, sd);
|
||||||
fragColor = vec4(vColor.rgb, vColor.a * alpha);
|
fragColor = vec4(vColor.rgb, vColor.a * alpha);
|
||||||
|
|
@ -136,19 +154,8 @@ bool GLSpriteBatch::init() {
|
||||||
glVertexAttribPointer(2, 4, GL_FLOAT, GL_FALSE, sizeof(Vertex),
|
glVertexAttribPointer(2, 4, GL_FLOAT, GL_FALSE, sizeof(Vertex),
|
||||||
(void *)offsetof(Vertex, color));
|
(void *)offsetof(Vertex, color));
|
||||||
|
|
||||||
// 生成索引缓冲区 - 静态,只需创建一次
|
// 使用编译期生成的静态索引缓冲区
|
||||||
std::vector<GLuint> indices;
|
const auto& indices = getIndices();
|
||||||
indices.reserve(MAX_INDICES);
|
|
||||||
for (size_t i = 0; i < MAX_SPRITES; ++i) {
|
|
||||||
GLuint base = static_cast<GLuint>(i * VERTICES_PER_SPRITE);
|
|
||||||
indices.push_back(base + 0);
|
|
||||||
indices.push_back(base + 1);
|
|
||||||
indices.push_back(base + 2);
|
|
||||||
indices.push_back(base + 0);
|
|
||||||
indices.push_back(base + 2);
|
|
||||||
indices.push_back(base + 3);
|
|
||||||
}
|
|
||||||
|
|
||||||
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, ibo_);
|
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, ibo_);
|
||||||
glBufferData(GL_ELEMENT_ARRAY_BUFFER, indices.size() * sizeof(GLuint),
|
glBufferData(GL_ELEMENT_ARRAY_BUFFER, indices.size() * sizeof(GLuint),
|
||||||
indices.data(), GL_STATIC_DRAW);
|
indices.data(), GL_STATIC_DRAW);
|
||||||
|
|
@ -196,40 +203,55 @@ bool GLSpriteBatch::needsFlush(const Texture &texture, bool isSDF) const {
|
||||||
}
|
}
|
||||||
|
|
||||||
void GLSpriteBatch::addVertices(const SpriteData &data) {
|
void GLSpriteBatch::addVertices(const SpriteData &data) {
|
||||||
// 计算变换后的顶点位置
|
// 计算锚点偏移
|
||||||
glm::vec2 anchorOffset(data.size.x * data.anchor.x,
|
float anchorOffsetX = data.size.x * data.anchor.x;
|
||||||
data.size.y * data.anchor.y);
|
float anchorOffsetY = data.size.y * data.anchor.y;
|
||||||
|
|
||||||
// 使用三角函数查表替代 cosf/sinf
|
// 使用三角函数查表替代 cosf/sinf
|
||||||
float cosR = TrigLookup::cosRad(data.rotation);
|
float cosR = TrigLookup::cosRad(data.rotation);
|
||||||
float sinR = TrigLookup::sinRad(data.rotation);
|
float sinR = TrigLookup::sinRad(data.rotation);
|
||||||
|
|
||||||
auto transform = [&](float x, float y) -> glm::vec2 {
|
|
||||||
float rx = x - anchorOffset.x;
|
|
||||||
float ry = y - anchorOffset.y;
|
|
||||||
return glm::vec2(data.position.x + rx * cosR - ry * sinR,
|
|
||||||
data.position.y + rx * sinR + ry * cosR);
|
|
||||||
};
|
|
||||||
|
|
||||||
glm::vec4 color(data.color.r, data.color.g, data.color.b, data.color.a);
|
glm::vec4 color(data.color.r, data.color.g, data.color.b, data.color.a);
|
||||||
|
|
||||||
// 添加四个顶点(图片已在加载时翻转,纹理坐标直接使用)
|
// 直接计算变换后的位置
|
||||||
// v0(左上) -- v1(右上)
|
float rx0 = -anchorOffsetX;
|
||||||
// | |
|
float ry0 = -anchorOffsetY;
|
||||||
// v3(左下) -- v2(右下)
|
float rx1 = data.size.x - anchorOffsetX;
|
||||||
Vertex v0{transform(0, 0), glm::vec2(data.texCoordMin.x, data.texCoordMin.y),
|
float ry1 = data.size.y - anchorOffsetY;
|
||||||
color};
|
|
||||||
Vertex v1{transform(data.size.x, 0),
|
|
||||||
glm::vec2(data.texCoordMax.x, data.texCoordMin.y), color};
|
|
||||||
Vertex v2{transform(data.size.x, data.size.y),
|
|
||||||
glm::vec2(data.texCoordMax.x, data.texCoordMax.y), color};
|
|
||||||
Vertex v3{transform(0, data.size.y),
|
|
||||||
glm::vec2(data.texCoordMin.x, data.texCoordMax.y), color};
|
|
||||||
|
|
||||||
vertexBuffer_[vertexCount_++] = v0;
|
// 预计算旋转后的偏移
|
||||||
vertexBuffer_[vertexCount_++] = v1;
|
float cosRx0 = rx0 * cosR, sinRx0 = rx0 * sinR;
|
||||||
vertexBuffer_[vertexCount_++] = v2;
|
float cosRx1 = rx1 * cosR, sinRx1 = rx1 * sinR;
|
||||||
vertexBuffer_[vertexCount_++] = v3;
|
float cosRy0 = ry0 * cosR, sinRy0 = ry0 * sinR;
|
||||||
|
float cosRy1 = ry1 * cosR, sinRy1 = ry1 * sinR;
|
||||||
|
|
||||||
|
// v0: (0, 0) -> (rx0, ry0)
|
||||||
|
vertexBuffer_[vertexCount_++] = {
|
||||||
|
glm::vec2(data.position.x + cosRx0 - sinRy0, data.position.y + sinRx0 + cosRy0),
|
||||||
|
glm::vec2(data.texCoordMin.x, data.texCoordMin.y),
|
||||||
|
color
|
||||||
|
};
|
||||||
|
|
||||||
|
// v1: (size.x, 0) -> (rx1, ry0)
|
||||||
|
vertexBuffer_[vertexCount_++] = {
|
||||||
|
glm::vec2(data.position.x + cosRx1 - sinRy0, data.position.y + sinRx1 + cosRy0),
|
||||||
|
glm::vec2(data.texCoordMax.x, data.texCoordMin.y),
|
||||||
|
color
|
||||||
|
};
|
||||||
|
|
||||||
|
// v2: (size.x, size.y) -> (rx1, ry1)
|
||||||
|
vertexBuffer_[vertexCount_++] = {
|
||||||
|
glm::vec2(data.position.x + cosRx1 - sinRy1, data.position.y + sinRx1 + cosRy1),
|
||||||
|
glm::vec2(data.texCoordMax.x, data.texCoordMax.y),
|
||||||
|
color
|
||||||
|
};
|
||||||
|
|
||||||
|
// v3: (0, size.y) -> (rx0, ry1)
|
||||||
|
vertexBuffer_[vertexCount_++] = {
|
||||||
|
glm::vec2(data.position.x + cosRx0 - sinRy1, data.position.y + sinRx0 + cosRy1),
|
||||||
|
glm::vec2(data.texCoordMin.x, data.texCoordMax.y),
|
||||||
|
color
|
||||||
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
void GLSpriteBatch::draw(const Texture &texture, const SpriteData &data) {
|
void GLSpriteBatch::draw(const Texture &texture, const SpriteData &data) {
|
||||||
|
|
@ -316,8 +338,7 @@ void GLSpriteBatch::flush() {
|
||||||
shader_.setMat4("uViewProjection", viewProjection_);
|
shader_.setMat4("uViewProjection", viewProjection_);
|
||||||
shader_.setInt("uTexture", 0);
|
shader_.setInt("uTexture", 0);
|
||||||
shader_.setInt("uUseSDF", currentIsSDF_ ? 1 : 0);
|
shader_.setInt("uUseSDF", currentIsSDF_ ? 1 : 0);
|
||||||
shader_.setFloat("uSdfOnEdge", 128.0f / 255.0f);
|
// SDF 常量已硬编码到着色器中
|
||||||
shader_.setFloat("uSdfScale", 255.0f / 64.0f);
|
|
||||||
|
|
||||||
// 更新 VBO 数据 - 只更新实际使用的部分
|
// 更新 VBO 数据 - 只更新实际使用的部分
|
||||||
glBindBuffer(GL_ARRAY_BUFFER, vbo_);
|
glBindBuffer(GL_ARRAY_BUFFER, vbo_);
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue