Отложенный рендеринг Skybox OpenGL

Я только что реализовал отложенный рендеринг, и у меня возникли проблемы с работой моего скайбокса. Я пытаюсь визуализировать свой скайбокс в самом конце цикла рендеринга, и все, что я получаю, это черный экран. Вот цикл рендеринга:

    //binds the fbo
    gBuffer.Bind();

    //the shader that writes info to gbuffer
    geometryPass.Bind();

    glDepthMask(GL_TRUE);
    glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
    glEnable(GL_DEPTH_TEST);
    glDisable(GL_BLEND);

    //draw geometry
    geometryPass.SetUniform("model", transform.GetModel());
    geometryPass.SetUniform("mvp", camera.GetViewProjection() * transform.GetModel());

    mesh3.Draw();

    geometryPass.SetUniform("model", transform2.GetModel());
    geometryPass.SetUniform("mvp", camera.GetViewProjection() * transform2.GetModel());
    sphere.Draw();

    glDepthMask(GL_FALSE);
    glDisable(GL_DEPTH_TEST);

    glEnable(GL_BLEND);
    glBlendEquation(GL_FUNC_ADD);
    glBlendFunc(GL_ONE, GL_ONE);

    glBindFramebuffer(GL_FRAMEBUFFER, 0);
    glClear(GL_COLOR_BUFFER_BIT);

    //shader that calculates lighting
    pointLightPass.Bind();
    pointLightPass.SetUniform("cameraPos", camera.GetTransform().GetPosition());

    for (int i = 0; i < 2; i++)
    {
        pointLightPass.SetUniformPointLight("light", pointLights[i]);
        pointLightPass.SetUniform("mvp", glm::mat4(1.0f));
        //skybox.GetCubeMap()->Bind(9);
        quad.Draw();
    }

    //draw skybox
    glEnable(GL_DEPTH_TEST);
    skybox.Render(camera);

    window.Update();
    window.SwapBuffers();

Ниже приведена функция рендеринга скайбокса.

glCullFace(GL_FRONT);
glDepthFunc(GL_LEQUAL);

m_transform.SetPosition(camera.GetTransform().GetPosition());
m_shader->Bind();

m_shader->SetUniform("mvp", camera.GetViewProjection() * m_transform.GetModel());
m_shader->SetUniform("cubeMap", 0);

m_cubeMap->Bind(0);
m_cubeMesh->Draw();

glDepthFunc(GL_LESS);
glCullFace(GL_BACK);

А вот и вершинный шейдер скайбокса:

layout (location = 0) in vec3 position;

out vec3 TexCoord;

uniform mat4 mvp;

void main()
{
    vec4 pos = mvp * vec4(position, 1.0);
    gl_Position = pos.xyww;
    TexCoord = position;
}

Фрагментный шейдер скайбокса просто устанавливает выходной цвет на texture(cubeMap, TexCoord). Как вы можете видеть из вершинного шейдера, я устанавливаю компонент позиции z равным w, чтобы он всегда имел глубину 1. Я также устанавливаю функцию глубины GL_LEQUAL, чтобы она не прошла тест глубины. Должно ли это не только рисовать скайбокс в местах, где другие объекты еще не были нарисованы? Почему это приводит к черному экрану?

Я знаю, что правильно настроил скайбокс, потому что если я просто нарисую скайбокс сам по себе, он будет отображаться нормально.


person theonewhoknocks    schedule 28.03.2015    source источник
comment
Я думаю, что ваше последнее предложение говорит само за себя. Он работает без рисования материала ранее. Либо предыдущие вызовы отрисовки изменяют состояние GL, например, включение GL_DEPTH_TEST для прохода освещения, либо отрисовка вашей геометрии изменяет значения глубины повсюду. Я не заметил ничего очевидного. Я бы установил gl_FragDepth, а не gl_Position.z, потому что я бы предположил, что это испортит отсечение, но если это работает, то работает.   -  person jozxyqk    schedule 29.03.2015
comment
Я не знаю о геометрии, меняющей значения глубины везде, но проход освещения или любой другой проход после прохода геометрии не меняется GL_DEPTH_TEST. Я также пытался установить gl_FragDepth, но это все равно приводит к черному экрану. Я действительно не знаю, почему это не работает. Если я не включу GL_DEPTH_TEST перед рендерингом скайбокса, он появится, но никакая другая геометрия не будет отображаться, потому что скайбокс находится поверх всего.   -  person theonewhoknocks    schedule 30.03.2015
comment
О верно. Дело в черном экране довольно странное (я предположил, что вы имели в виду, что фон был черным). Если вы рисуете что-то, что заменяет предыдущие значения черным цветом, значит, что-то действительно рисуется. Если вы видите небо без проверки глубины, я могу только предположить, что небо перезаписывает черную геометрию. Хотя TexCoord должен гарантировать, что вы не станете черным, предполагая, что ваша текстура не привязана и не привязана, если только координата не равна нулю (что в любом случае поместит геометрию за камеру). Я бы попытался сузить круг причин, вызывающих черный цвет. Может быть, начать с установки постоянного цвета во фрагментном шейдере.   -  person jozxyqk    schedule 30.03.2015
comment
Даже если я установлю постоянный цвет во фрагментном шейдере, это все равно приведет к черному экрану. Ни одна из моих геометрических фигур не черная, а скайбокс имеет голубоватую текстуру.   -  person theonewhoknocks    schedule 31.03.2015
comment
Это довольно странно. Я все еще не уверен насчет pos.xyww, но если не происходят какие-то странные операции смешивания, фрагмент должен либо устанавливать, либо не устанавливать пиксель. Не знаю, как он становится черным. Не могли бы вы добавить полную матрицу обзора в скайбокс, чтобы разместить его как объект в мире и посмотреть, как он будет отображаться?   -  person jozxyqk    schedule 31.03.2015
comment
Если я не установлю камеру в центре скайбокса и не использую матрицу проекции вида модели, то это все равно будет черный экран. Если я не включу GL_DEPTH_TEST перед рисованием скайбокса, то я увижу только скайбокс и никакую другую геометрию. Но я думаю, что GL_DEPTH_TEST следует включить перед рисованием скайбокса. Я также должен отметить, что если я не включу тест глубины при рисовании скайбокса, я могу на долю секунды увидеть геометрию, которая должна быть отрисована до того, как скайбокс будет отрисован поверх всего.   -  person theonewhoknocks    schedule 01.04.2015
comment
Ну, боюсь, у меня закончились идеи. Посмотрите, поможет ли мой ответ, я немного объяснил. Если вы найдете проблему, мне было бы интересно узнать, что могло вызвать ваши проблемы!   -  person jozxyqk    schedule 01.04.2015


Ответы (1)


На долю секунды я могу кратко увидеть геометрию, которая должна быть нарисована до того, как скайбокс будет нарисован поверх всего.

Поскольку вы используете двойную буферизацию, видение разных вещей должно быть связано с отрисовкой другого кадра. Буфер глубины в кадровом буфере по умолчанию не очищается, что, как мне кажется, является причиной временной нестабильности.

В вашем случае вы хотите, чтобы буфер глубины по умолчанию был таким же, как GBuffer, когда вы рисуете скайбокс. Быстрый способ добиться этого — использовать glBlitFramebuffer, также избегая необходимость очистить его:

glBindFramebuffer(GL_READ_FRAMEBUFFER, gbuffer);
glBindFramebuffer(GL_DRAW_FRAMEBUFFER, 0);
glBlitFramebuffer(..., GL_DEPTH_BUFFER_BIT, ...);

Теперь, чтобы объяснить черный экран, когда скайбокс заполняет экран. Без теста глубины конечно скайбокс просто рисует. С тестом глубины скайбокс по-прежнему рисует первый кадр, но вскоре после второго кадра очищается только цветовой буфер. Буфер глубины все еще содержит устаревшие значения скайбокса, поэтому он не перерисовывается для этого кадра, и у вас остается черный цвет...

Однако ваш проход геометрии отрисовывается без включенного тестирования глубины, поэтому это все равно должно быть видно, даже если скайбокс не виден. Также это произойдет только с GL_LESS, а у вас GL_LEQUAL. И у вас есть glDepthMask false, что означает, что ничего не должно записываться в буфер глубины по умолчанию в вашем коде. Это указывает на буфер глубины, содержащий другие значения, возможно, неинициализированные, но, по моему опыту, изначально он равен нулю. Также это все еще происходит, когда скайбокс не заполняет экран, нарисованный в виде куба вдали от камеры, что сводит на нет этот аргумент. Теперь, возможно, если бы геометрия не отрисовывалась во втором кадре, это бы объяснило это. В этом отношении явные ошибки драйверов тоже будут, но я не вижу никаких проблем в данном коде.

TLDR: много необъяснимых вещей, поэтому **я попробовал сам и не могу воспроизвести вашу проблему...

Вот быстрый пример, основанный на вашем коде, и он отлично работает для меня...

(зеленая сфера — геометрия, красный куб — ​​скайбокс)

gl_Position = pos:
введите здесь описание изображения

Обратите внимание на желтый цвет от аддитивного смешивания, даже если скайбокс нарисован поверх. Я думал, ты тоже это увидишь.

gl_Position = pos.xyww:
введите здесь описание изображения

Теперь по коду...

//I haven't enabled back face culling, but that shouldn't affect anything

//binds the fbo
fbo.bind();

//the shader that writes info to gbuffer
//geometryPass.Bind(); //fixed pipeline for now

glDepthMask(GL_TRUE);
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
glEnable(GL_DEPTH_TEST);
glDisable(GL_BLEND);

glColor3f(0,1,0);
fly.uploadCamera(); //glLoadMatrixf
sphere.draw();

glDepthMask(GL_FALSE);
glDisable(GL_DEPTH_TEST);

glEnable(GL_BLEND);
glBlendEquation(GL_FUNC_ADD);
glBlendFunc(GL_ONE, GL_ONE);

fbo.unbind(); //glBindFramebuffer(GL_FRAMEBUFFER, 0);
glClear(GL_COLOR_BUFFER_BIT);

//shader that calculates lighting
drawtex.use();
//pointLightPass.SetUniform("cameraPos", camera.GetTransform().GetPosition());
drawtex.set("tex", *(Texture2D*)fbo.colour[0]);

for (int i = 0; i < 2; i++)
{
    //pointLightPass.SetUniformPointLight("light", pointLights[i]);
    //pointLightPass.SetUniform("mvp", glm::mat4(1.0f));
    //skybox.GetCubeMap()->Bind(9);
    drawtex.set("modelviewMat", mat44::identity());
    quad.draw();
}

drawtex.unuse();

//draw skybox
glEnable(GL_DEPTH_TEST);

glBindFramebuffer(GL_READ_FRAMEBUFFER, fbo);
glBindFramebuffer(GL_DRAW_FRAMEBUFFER, 0);
glBlitFramebuffer(0, 0, fbo.size.x, fbo.size.y, 0, 0, fbo.size.x, fbo.size.y, GL_DEPTH_BUFFER_BIT, GL_NEAREST);
glBindFramebuffer(GL_FRAMEBUFFER, 0);

//glCullFace(GL_FRONT);
glDepthFunc(GL_LEQUAL);

//m_transform.SetPosition(camera.GetTransform().GetPosition());
skybox.use();

skybox.set("mvp", fly.camera.getProjection() * fly.camera.getInverse() * mat44::translate(1,0,0));
//m_shader->SetUniform("mvp", camera.GetViewProjection() * m_transform.GetModel());
//m_shader->SetUniform("cubeMap", 0);

//m_cubeMap->Bind(0);
cube.draw();
skybox.unuse();

glDepthFunc(GL_LESS);
//glCullFace(GL_BACK);

//window.Update();
//window.SwapBuffers();
person jozxyqk    schedule 01.04.2015
comment
Большое спасибо за помощь! Я очень ценю это. Использование glBlitFramebuffer для копирования буфера глубины действительно решило мою проблему. Мне интересно, есть ли более подходящее место для рисования скайбокса, чтобы мне не приходилось копировать буфер глубины из gbuffer в буфер кадра по умолчанию. Пока я буду использовать это исправление. Спасибо еще раз за помощь. - person theonewhoknocks; 02.04.2015
comment
@theonewhoknocks Не беспокойтесь, рад, что это сработало. Я думаю, что блефовать - это нормально. Чтобы избежать копирования, вы хотите использовать текстуру в качестве буфера глубины по умолчанию, что невозможно. Попытка сначала отрендерить скайбокс или в GBuffer не будет чистой. Если позже вы выполните некоторую постобработку и рендеринг в другой FBO, вы можете использовать текстуру глубины GBuffer прямо там, но блит настолько быстр по сравнению с другими вещами, которые вы будете делать, я не думаю, что вы бы заметить любую разницу. - person jozxyqk; 02.04.2015