我在OpenGL中写了一个小体素,这是我第一次使用framebuffers。
正如您能够看到的那样,水面和水面地形正在水下地形之上。这是一种不受欢迎的影响,我想知道是否有办法摆脱它。
使用以下代码初始化帧缓冲区。
bool Framebuffer::Create(int width, int height) {
// set width and height
m_width = width;
m_height = height;
// create a frame buffer object
glGenFramebuffers(1, &m_fbo);
// generate a texture id
glGenTextures(1, &m_tid);
// bind both the fbo and to
glBindFramebuffer(GL_FRAMEBUFFER, m_fbo);
glBindTexture(GL_TEXTURE_2D, m_tid);
// generate a texture for the framebuffer
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA8, width, height, 0, GL_RGBA, GL_UNSIGNED_BYTE, 0);
// set the texture filtering to nearest
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
// create a depth renderbuffer
glGenRenderbuffers(1, &m_dbr);
glBindRenderbuffer(GL_RENDERBUFFER, m_dbr);
glRenderbufferStorage(GL_RENDERBUFFER, GL_DEPTH24_STENCIL8, width, height);
glBindRenderbuffer(GL_RENDERBUFFER, 0);
// set the color output for the texture to be the render texture
glFramebufferTexture(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, m_tid, 0);
glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_DEPTH_STENCIL_ATTACHMENT, GL_RENDERBUFFER, m_dbr);
// set the amount of draw buffers for the framebuffer
GLenum buffers[1] = { GL_COLOR_ATTACHMENT0 };
glDrawBuffers(1, buffers);
int fboStatus = glCheckFramebufferStatus(GL_FRAMEBUFFER);
if (fboStatus != GL_FRAMEBUFFER_COMPLETE) {
std::cout << "[ERROR]: Framebuffer failed to create!" << std::endl;
std::cout << ((fboStatus == GL_FRAMEBUFFER_UNSUPPORTED) ? "Framebuffer Unsupported!\n" : "");
return false;
}
glBindFramebuffer(GL_FRAMEBUFFER, 0);
glBindTexture(GL_TEXTURE_2D, 0);
// the stride for the data
// stride is the byte amount for each element
// stride is equal to seven because three coordinates per vertex
// and four coordinates per color
int stride = 5 * sizeof(GLfloat);
// the offset for the vertex data
// this is zero because the vertex data starts immediately
void* vertOff = reinterpret_cast<void*>(0);
// the offset for the color data
// this is three because it starts right after the vertex data
// multiplied by the byte size of a float
void* texOff = reinterpret_cast<void*>(3 * sizeof(GLfloat));
// create the quad for rendering this framebuffer
glGenBuffers(1, &m_vbo);
glGenVertexArrays(1, &m_vao);
// bind the vao and vbo
glBindVertexArray(m_vao);
glBindBuffer(GL_ARRAY_BUFFER, m_vbo);
// the vertex and uv data for the quad
const GLfloat quad_data[] = {
// First Triangle
1.0f, 1.0f, 0.0f, 1.0f, 1.0f,
-1.0f, 1.0f, 0.0f, 0.0f, 1.0f,
-1.0f, -1.0f, 0.0f, 0.0f, 0.0f,
// Second Triangle
-1.0f, -1.0f, 0.0f, 0.0f, 0.0f,
1.0f, -1.0f, 0.0f, 1.0f, 0.0f,
1.0f, 1.0f, 0.0f, 1.0f, 1.0f
};
// set the buffer data to the quad data
glBufferData(GL_ARRAY_BUFFER, sizeof(quad_data), quad_data, GL_STATIC_DRAW);
// set the first attribute to the vertex data
glVertexAttribPointer(0, 3, GL_FLOAT, false, stride, vertOff);
// set the second attribute to the uv data
glVertexAttribPointer(1, 2, GL_FLOAT, false, stride, texOff);
// unbind the vao and vbo
glBindBuffer(GL_ARRAY_BUFFER, 0);
glBindVertexArray(0);
m_initialized = true;
return true;
}
我使用下面的代码渲染整个世界。
(代码中的Draw *()函数只是在vbo上调用draw,没有其他的东西)
void World::Draw() {
// the shaders needed to draw
Shader* framebuffer = vxl::GetShaderManager()->GetShader("framebuffer");
Shader* drawFramebuffer = vxl::GetShaderManager()->GetShader("drawFramebuffer");
Shader* drawBlurFramebuffer = vxl::GetShaderManager()->GetShader("drawBlurFramebuffer");
Shader* water = vxl::GetShaderManager()->GetShader("water");
Shader* normal = vxl::GetShaderManager()->GetShader("normal");
// the camera position to chunk coordinates
int chunkX = static_cast<int>(floor(vxl::GetCameraPosition().x) / 16.0f / 2.0f) >> 4;
int chunkZ = static_cast<int>(floor(-vxl::GetCameraPosition().z) / 16.0f / 2.0f) >> 4;
// the amount of chunks to render, plus the center
const int chunkRenderDistance = 3;
for (int z = chunkZ - chunkRenderDistance; z < chunkZ + chunkRenderDistance; z++) {
for (int x = chunkX - chunkRenderDistance; x < chunkX + chunkRenderDistance; x++) {
m_renderChunks.push_back(m_chunks[Position(x * m_chunkSize, 0, z * m_chunkSize)]);
}
}
// draw each opaque object into the opaque framebuffer
// this just draws all of the geometry with a black transparent clear color
// allowing for blending with other objects in the scene
// this isn't going to be flipped, so the flipped arg should be false
_DrawOpaqueFBO(framebuffer, false);
// draw the opaque objects with the framebuffer quad
_DrawFBOToQuad(&m_opaqueFbo, drawFramebuffer, false);
// redraw the fbo but upside down
// this allows for reflections to actually work correctly
// this is going to be flipped, so the flipped arg should be true
_DrawOpaqueFBO(framebuffer, true);
// draw the water in this scene
_DrawWater(water);
// draw the water in this scene
// draw the objects underwater
// bind the normal shader
normal->Bind();
// draw each underwater part of a chunk
for (size_t i = 0; i < m_renderChunks.size(); i++) {
if (m_renderChunks[i] != nullptr) {
if (!m_renderChunks[i]->GetUnderwaterRenderer()->IsAllocationQueued() && m_renderChunks[i]->GetUnderwaterRenderer()->NeedsAllocation()) {
m_pool.AssignTask(&StaticRenderer::AllocVoxels, m_renderChunks[i]->GetUnderwaterRenderer());
m_renderChunks[i]->GetUnderwaterRenderer()->SetAllocationQueued(true);
}
if (!m_renderChunks[i]->GetUnderwaterRenderer()->IsAllocationQueued() && !m_renderChunks[i]->GetUnderwaterRenderer()->NeedsAllocation())
m_renderChunks[i]->DrawUnderwater(normal);
}
}
normal->Unbind();
// draw the water
_DrawFBOToQuad(&m_waterFbo, drawBlurFramebuffer, true);
// clear all of the render chunks for next frame
m_renderChunks.clear();
}
void World::_DrawFBOToQuad(Framebuffer* fbo, Shader* shader, bool drawBlurred) {
// bind the shader for rendering to a texture
shader->Bind();
// set the active texture to zero
shader->Uniform1f("tex", 0);
if (drawBlurred) {
// the viewport for the quad
shader->Uniform2f("viewport", static_cast<float>(vxl::GetWidth()), static_cast<float>(vxl::GetHeight()));
// the direction for blurring
shader->Uniform2f("blurDir", 0.0f, 1.0f);
// the radius to blur
shader->Uniform1f("blurRadius", 1.0f);
}
// bind the framebuffer's texture id
glBindTexture(GL_TEXTURE_2D, fbo->GetTextureID());
// draw the actual framebuffer quad
fbo->Draw();
// unbind the texture
glBindTexture(GL_TEXTURE_2D, 0);
// unbind the shader for other shaders
shader->Unbind();
}
Framebuffer::Create
方法只生成一个带有颜色纹理和深度渲染缓冲区的帧缓冲对象。我觉得深度渲染缓冲区没有被正确写入,就像我运行调试器一样,深度只是纯白色。
World::Draw
方法只能在渲染距离内获得一些块,以便它可以实际绘制世界。
World::_Draw*
方法将特定部分渲染到其各自的FBO中。例如,World::_DrawOpaqueFBO
只是将不透明的水上地形绘制到不透明的帧缓冲区。
World::_DrawFBOToQuad
只是使用Framebuffer::Create
构建的四边形将渲染纹理绘制到屏幕上。
我不知道如何让深度实际上适用于每个帧缓冲四元组。深度测试已启用,我还清除了深度和颜色缓冲区位。
提前感谢您的任何帮助:)