我刚刚开始研究使用 OpenGL 和 GLFW C++ 库创建 2D 游戏,目前我有游戏循环使用 freetype 在屏幕上渲染一些文本。但我注意到,如果我在视图的某个位置渲染文本,并且如果窗口被调整大小,它会移动,这是有道理的。
所以现在我试图创建一个特定大小/维度的屏幕外纹理,游戏中的所有内容都将被渲染到该屏幕外纹理,然后该屏幕外纹理将被渲染到窗口并根据需要向上/向下缩放。
现在我已经编写了我认为大部分代码的内容,但似乎无法使其正常工作。
glCheckFramebufferStatus 通过这意味着正确设置了帧缓冲区。
glGenFramebuffers(1, &m_frame_buffer);
glBindFramebuffer(GL_FRAMEBUFFER, m_frame_buffer);
glGenTextures(1, &m_texture);
glBindTexture(GL_TEXTURE_2D, m_texture);
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, m_width, m_height, 0, GL_RGB, GL_UNSIGNED_BYTE, 0);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
// The depth buffer
GLuint depthrenderbuffer;
glGenRenderbuffers(1, &depthrenderbuffer);
glBindRenderbuffer(GL_RENDERBUFFER, depthrenderbuffer);
glRenderbufferStorage(GL_RENDERBUFFER, GL_DEPTH_COMPONENT, m_width, m_height);
glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_RENDERBUFFER, depthrenderbuffer);
glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, m_texture, 0);
// Set the list of draw buffers.
GLenum DrawBuffers[1] = { GL_COLOR_ATTACHMENT0 };
glDrawBuffers(1, DrawBuffers); // "1" is the size of DrawBuffers
if (glCheckFramebufferStatus(GL_FRAMEBUFFER) != GL_FRAMEBUFFER_COMPLETE)
{
std::cout << "FrameBuffer is busted" << std::endl;
}
然后在渲染循环期间,我首先切换我刚刚使用此代码创建的帧缓冲区。
glBindFramebuffer(GL_FRAMEBUFFER, m_virtual_render_target.getFrameBuffer());
glViewport(0, 0, m_virtual_render_target.getWidth(), m_virtual_render_target.getHeight());
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
然后我照常做我的渲染代码,这只是一些使用 FreeType 的文本渲染,如下所示。
在游戏循环开始初始化 Front 以呈现文本之前调用一次加载字体。
void TextManager::loadFont()
{
// Configure VAO / VBO for textures
glGenVertexArrays(1, &m_VAO);
glGenBuffers(1, &m_VBO);
glBindVertexArray(m_VAO);
glBindBuffer(GL_ARRAY_BUFFER, m_VBO);
glBufferData(GL_ARRAY_BUFFER, sizeof(float) * 6 * 4, NULL, GL_DYNAMIC_DRAW);
glEnableVertexAttribArray(0);
glVertexAttribPointer(0, 4, GL_FLOAT, GL_FALSE, 4 * sizeof(float), 0);
glBindBuffer(GL_ARRAY_BUFFER, 0);
glBindVertexArray(0);
// If we can load the font face
if (FT_New_Face(m_font_lib, "fonts/arial.ttf", 0, &m_font_face))
{
// Failed to load
std::cout << "ERROR::FREETYPE: Failed to load font" << std::endl;
return;
}
// set size to load glyphs as
FT_Set_Pixel_Sizes(m_font_face, 0, 48);
// disable byte-alignment restriction
glPixelStorei(GL_UNPACK_ALIGNMENT, 1);
// load first 128 characters of ASCII set
for (unsigned char c = 0; c < 128; c++)
{
// Load character glyph
if (FT_Load_Char(m_font_face, c, FT_LOAD_RENDER))
{
std::cout << "ERROR::FREETYTPE: Failed to load Glyph" << std::endl;
continue;
}
// generate texture
unsigned int texture;
glGenTextures(1, &texture);
glBindTexture(GL_TEXTURE_2D, texture);
glTexImage2D(
GL_TEXTURE_2D,
0,
GL_RED,
m_font_face->glyph->bitmap.width,
m_font_face->glyph->bitmap.rows,
0,
GL_RED,
GL_UNSIGNED_BYTE,
m_font_face->glyph->bitmap.buffer
);
// set texture options
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
// now store character for later use
TextCharacter character = TextCharacter(
texture,
glm::ivec2(m_font_face->glyph->bitmap.width, m_font_face->glyph->bitmap.rows),
glm::ivec2(m_font_face->glyph->bitmap_left, m_font_face->glyph->bitmap_top),
static_cast<unsigned int>(m_font_face->glyph->advance.x)
);
m_characters.insert(std::pair<char, TextCharacter>(c, character));
}
glBindTexture(GL_TEXTURE_2D, 0);
// destroy FreeType once we're finished
FT_Done_Face(m_font_face);
FT_Done_FreeType(m_font_lib);
}
然后在每个循环中,这是实际渲染文本的代码
void TextManager::renderText(Shader* shader, std::string text, float x, float y, float scale, glm::vec3 color)
{
// activate corresponding render state
shader->use();
glUniform3f(glGetUniformLocation(shader->getProgramID() , "textColor"), color.x, color.y, color.z);
glActiveTexture(GL_TEXTURE0);
glBindVertexArray(m_VAO);
// iterate through all characters
std::string::const_iterator c;
for (c = text.begin(); c != text.end(); c++)
{
TextCharacter ch = m_characters[*c];
float xpos = x + ch.getBearing().x * scale;
float ypos = y - (ch.getSize().y - ch.getBearing().y) * scale;
float w = ch.getSize().x * scale;
float h = ch.getSize().y * scale;
// update VBO for each character
float vertices[6][4] = {
{ xpos, ypos + h, 0.0f, 0.0f },
{ xpos, ypos, 0.0f, 1.0f },
{ xpos + w, ypos, 1.0f, 1.0f },
{ xpos, ypos + h, 0.0f, 0.0f },
{ xpos + w, ypos, 1.0f, 1.0f },
{ xpos + w, ypos + h, 1.0f, 0.0f }
};
// render glyph texture over quad
glBindTexture(GL_TEXTURE_2D, ch.getTextureID());
// update content of VBO memory
glBindBuffer(GL_ARRAY_BUFFER, m_VBO);
glBufferSubData(GL_ARRAY_BUFFER, 0, sizeof(vertices), vertices); // be sure to use glBufferSubData and not glBufferData
glBindBuffer(GL_ARRAY_BUFFER, 0);
// render quad
glDrawArrays(GL_TRIANGLES, 0, 6);
// now advance cursors for next glyph (note that advance is number of 1/64 pixels)
x += (ch.getAdvance() >> 6) * scale; // bitshift by 6 to get value in pixels (2^6 = 64 (divide amount of 1/64th pixels by 64 to get amount of pixels))
}
glBindVertexArray(0);
glBindTexture(GL_TEXTURE_2D, 0);
}
然后我使用此代码将所有内容渲染回 GLFW 窗口。
// Switch back to default frame buffer
glBindFramebuffer(GL_FRAMEBUFFER, 0);
// 4. RENDER FULLSCREEN QUAD
glViewport(0, 0, m_viewport_width, m_viewport_height);
glClearColor(0.0f, 0.0f, 0.0f, 1.0f);
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
auto windowShader = m_shader_manager->getShader("WindowShader");
GLuint texID = windowShader->getUniformLocation("renderedTexture");
GLuint timeID = windowShader->getUniformLocation("time");
glUseProgram(windowShader->getProgramID());
//glActiveTexture(GL_TEXTURE0);
//glBindTexture(GL_TEXTURE_2D, m_virtual_render_target.getTexture());
glUniform1i(texID, 0);
//glUniform1f(timeID, glfwGetTime() * 10.0f);
glEnableVertexAttribArray(0);
glBindBuffer(GL_ARRAY_BUFFER, m_quad_model);
glVertexAttribPointer(
0, // attribute 0. No particular reason for 0, but must match the layout in the shader.
3, // size
GL_FLOAT, // type
GL_FALSE, // normalized?
0, // stride
(void*)0 // array buffer offset
);
// Draw the triangles !
glDrawArrays(GL_TRIANGLES, 0, 6); // 2*3 indices starting at 0 -> 2 triangles
glDisableVertexAttribArray(0);
这是四边形模型的顶点数据。
// The fullscreen quad's FBO
static const GLfloat quadVertexBufferData[] = {
-1.0f, -1.0f, 0.0f,
1.0f, -1.0f, 0.0f,
-1.0f, 1.0f, 0.0f,
-1.0f, 1.0f, 0.0f,
1.0f, -1.0f, 0.0f,
1.0f, 1.0f, 0.0f,
};
GLuint quadVertexBuffer;
glGenBuffers(1, &quadVertexBuffer);
glBindBuffer(GL_ARRAY_BUFFER, quadVertexBuffer);
glBufferData(GL_ARRAY_BUFFER, sizeof(quadVertexBufferData), quadVertexBufferData, GL_STATIC_DRAW);
return quadVertexBuffer;
在所有这些之后,GLFW 只是空白/与之前使用的 clear 命令相同的颜色。为什么会发生这种情况?我知道这是一篇很长的文章,但 OpenGL 代码似乎总是很长。
旁注,如果我取出切换帧缓冲区的代码并只运行渲染代码,视图仍然是空白的,但是如果我删除生成帧缓冲区和纹理生成的代码,我的文本会再次渲染,所以也许问题就在这里。
感谢您的帮助
谢谢