如何使渲染纹理使用正确的深度?

时间:2016-02-04 17:44:56

标签: c++ opengl framebuffer

我在OpenGL中写了一个小体素,这是我第一次使用framebuffers。

the problem i am facing

正如您能够看到的那样,水面和水面地形正在水下地形之上。这是一种不受欢迎的影响,我想知道是否有办法摆脱它。

使用以下代码初始化帧缓冲区。

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构建的四边形将渲染纹理绘制到屏幕上。

我不知道如何让深度实际上适用于每个帧缓冲四元组。深度测试已启用,我还清除了深度和颜色缓冲区位。

提前感谢您的任何帮助:)

0 个答案:

没有答案