OpenGL / GLSL: рендерить на карту куба?

13

Я пытаюсь выяснить, как визуализировать мою сцену на карте куба. Я застрял на этом немного и решил, что попросил бы вас, ребята, о помощи. Я новичок в OpenGL и впервые использую FBO.

В настоящее время у меня есть рабочий пример использования bmp-файла cubemap, а тип образца samplerCube в фрагментном шейдере прикреплен к GL_TEXTURE1. Я не изменяю код шейдера вообще. Я просто изменяю тот факт, что я не буду вызывать функцию, которая загружает bmp-файл cubemap, и пытаюсь использовать приведенный ниже код для рендеринга в cubemap.

Ниже вы можете видеть, что я также снова прикрепляю текстуру к GL_TEXTURE1. Это так, когда я устанавливаю форму:

glUniform1i(getUniLoc(myProg, "Cubemap"), 1);

он может получить к нему доступ через мой фрагментный шейдер через uniform samplerCube Cubemap.

Я вызываю функцию ниже так:

cubeMapTexture = renderToCubeMap(150, GL_RGBA8, GL_RGBA, GL_UNSIGNED_BYTE);

Теперь я понимаю, что в цикле рисования ниже я не изменяю направление просмотра, чтобы смотреть вниз по оси + x, -x, + y, -y, + z, -z. Я действительно просто хотел увидеть что-то работающее, прежде чем реализовать это. Я подумал, что должен хотя бы увидеть что-то на своем объекте, как сейчас код.

Я ничего не вижу, просто прямо черный. Я сделал свой фон белым, но объект черный. Я удалил освещение и раскраску, чтобы просто попробовать текстуру куба и все еще черный.

Я думаю, что проблема может заключаться в типах форматов при настройке моей текстуры, GL_RGB8, GL_RGBA, но я также пытался:

GL_RGBA, GL_RGBA GL_RGB, GL_RGB

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

Я также попытался связать текстуру карты куба при каждом вызове отрисовки, который я хочу использовать карту куба:

glBindTexture(GL_TEXTURE_CUBE_MAP, cubeMapTexture);

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

Буду признателен за любую помощь, которая может направить меня в правильном направлении.

GLuint renderToCubeMap(int size, GLenum InternalFormat, GLenum Format, GLenum Type)
    {

    // color cube map
    GLuint textureObject;
    int face;
    GLenum status;

    //glEnable(GL_TEXTURE_2D);
    glActiveTexture(GL_TEXTURE1);
    glGenTextures(1, &textureObject);
    glBindTexture(GL_TEXTURE_CUBE_MAP, textureObject);
    glTexParameterf(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
    glTexParameterf(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
    glTexParameterf(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
    glTexParameterf(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
    glTexParameterf(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_WRAP_R, GL_CLAMP_TO_EDGE);

    for (face = 0; face < 6; face++) {
        glTexImage2D(GL_TEXTURE_CUBE_MAP_POSITIVE_X + face, 0, InternalFormat, size, size, 0, Format, Type, NULL);
    }

    // framebuffer object
    glGenFramebuffers(1, &fbo);
    glBindFramebuffer(GL_FRAMEBUFFER, fbo);

    glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_CUBE_MAP_POSITIVE_X, textureObject, 0);

    status = glCheckFramebufferStatus(GL_FRAMEBUFFER);
    printf("%d\"\n", status);
        printf("%d\n", GL_FRAMEBUFFER_COMPLETE);

    glViewport(0,0,size, size);

    for (face = 1; face < 6; face++) {

        drawSpheres();
        glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0,GL_TEXTURE_CUBE_MAP_POSITIVE_X + face, textureObject, 0);

    }

     //Bind 0, which means render to back buffer, as a result, fb is unbound
       glBindFramebuffer(GL_FRAMEBUFFER, 0);

       return textureObject;
    }
Джои Грин
источник
Вы проверяли, что ваша drawSpheresфункция на самом деле рисует что-то видимое? Функция на самом деле что-то рисует? Что произойдет, если вы измените drawSpheresпросто очистить фреймбуфер?
Никол Болас
Да. Я делаю два прохода. Один в приведенном выше коде, на самом деле 6 звонков выше. Затем я вызываю drawSpheres при рендеринге в кадровый буфер 0, и он действительно появляется.
Джои Грин
Кроме того, я установил свой фон на белый. Не будет ли белый цвет хотя бы отображаться в текстуре?
Джои Грин
Ваш код работает нормально для нормального FBO? Насколько я понимаю, на кубической карте должно быть всего шесть текстур, и вам придется рендерить каждую по отдельности ..
Яри ​​Комппа

Ответы:

10

Ну, я не могу гарантировать, что это поможет вам понять, что происходит. Вы просто не опубликовали достаточно информации о том, что вы делаете, чтобы отследить какие-либо конкретные ошибки. Хотя я могу исправить одну твою вещь очень быстро:

glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_CUBE_MAP_POSITIVE_X, textureObject, 0);

...

for (face = 1; face < 6; face++) {
    drawSpheres();
    glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_CUBE_MAP_POSITIVE_X + face, textureObject, 0);
}

Это будет звонить только drawSpheres пять раз. Я предполагаю, что вы хотели назвать это 6 раз.

Но я могу опубликовать рабочий ответ. Обратите внимание, что этот код предназначен для запуска вместе с моей учебной серией , поэтому он ссылается на код, которого нет. Но это в основном такие вещи, как создание сеток и т. Д .; ничего действительно важного.

Вот основные моменты. Шейдеры для основного объекта сферы.

Вершинный шейдер:

#version 330

layout(std140) uniform;

layout(location = 0) in vec4 position;
layout(location = 2) in vec3 normal;

out vec3 modelSpaceNormal;

uniform Projection
{
    mat4 cameraToClipMatrix;
};

uniform mat4 modelToCameraMatrix;

void main()
{
    gl_Position = cameraToClipMatrix * (modelToCameraMatrix * position);
    modelSpaceNormal = normal;
}

Фрагмент шейдера:

#version 330

in vec3 modelSpaceNormal;

uniform samplerCube cubeTexture;

out vec4 outputColor;

void main()
{
    outputColor = texture(cubeTexture, modelSpaceNormal);
//  outputColor = vec4(normalize(modelSpaceNormal), 1.0);
}

Создание текстуры кубической карты, которая будет использоваться в качестве цели рендеринга:

void CreateCubeTexture()
{
    glGenTextures(1, &g_cubeTexture);
    glBindTexture(GL_TEXTURE_CUBE_MAP, g_cubeTexture);

    glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_BASE_LEVEL, 0);
    glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_MAX_LEVEL, 0);
    glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
    glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_MIN_FILTER, GL_LINEAR);

    std::vector<GLubyte> testData(CUBE_TEXTURE_SIZE * CUBE_TEXTURE_SIZE * 256, 128);
    std::vector<GLubyte> xData(CUBE_TEXTURE_SIZE * CUBE_TEXTURE_SIZE * 256, 255);

    for(int loop = 0; loop < 6; ++loop)
    {
        if(loop)
        {
            glTexImage2D(GL_TEXTURE_CUBE_MAP_POSITIVE_X + loop, 0, GL_RGBA8,
                CUBE_TEXTURE_SIZE, CUBE_TEXTURE_SIZE, 0, GL_RGBA, GL_UNSIGNED_BYTE, &testData[0]);
        }
        else
        {
            glTexImage2D(GL_TEXTURE_CUBE_MAP_POSITIVE_X + loop, 0, GL_RGBA8,
                CUBE_TEXTURE_SIZE, CUBE_TEXTURE_SIZE, 0, GL_RGBA, GL_UNSIGNED_BYTE, &xData[0]);
        }
    }

    glBindTexture(GL_TEXTURE_CUBE_MAP, 0);
}

Я фактически заполняю текстуру данными (вместо того, чтобы передавать NULL в glTexImage2D) в качестве средства отладки. Это гарантирует, что все работало до начала использования текстуры в качестве цели рендеринга.

Также обратите внимание, что я предоставляю BASE_LEVEL и MAX_LEVEL. Я всегда делаю это с моими текстурами сразу после создания. Это просто хорошая привычка, так как OpenGL иногда требователен к полноте текстуры и пирамиде mipmap. Вместо того, чтобы помнить правила, я просто установил для них правильные значения неукоснительно.

Вот основная функция рисования:

void display()
{
    //Draw the cubemap.
    glBindFramebuffer(GL_DRAW_FRAMEBUFFER, g_framebuffer);
    glFramebufferRenderbuffer(GL_DRAW_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_RENDERBUFFER, g_depthbuffer);

    for(int loop = 0; loop < 6; ++loop)
        DrawFace(loop);

    glBindFramebuffer(GL_DRAW_FRAMEBUFFER, 0);

    //Draw the main scene.
    //The projection matrix is in a uniform buffer.
    ProjectionBlock projData;
    projData.cameraToClipMatrix = glm::perspective(90.0f,
        (g_viewportSize.x / (float)g_viewportSize.y), g_fzNear, g_fzFar);

    glBindBuffer(GL_UNIFORM_BUFFER, g_projectionUniformBuffer);
    glBufferSubData(GL_UNIFORM_BUFFER, 0, sizeof(ProjectionBlock), &projData);
    glBindBuffer(GL_UNIFORM_BUFFER, 0);

    glViewport(0, 0, (GLsizei)g_viewportSize.x, (GLsizei)g_viewportSize.y);

    glClearColor(0.75f, 0.75f, 1.0f, 1.0f);
    glClearDepth(1.0f);
    glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);

    glutil::MatrixStack modelMatrix;
    modelMatrix.ApplyMatrix(g_viewPole.CalcMatrix());

    if(g_pSphere)
    {
        glutil::PushStack push(modelMatrix);

        glUseProgram(g_progMain.theProgram);
        glUniformMatrix4fv(g_progMain.modelToCameraMatrixUnif, 1, GL_FALSE,
            glm::value_ptr(modelMatrix.Top()));

        glActiveTexture(GL_TEXTURE0 + g_cubeTexUnit);
        glBindTexture(GL_TEXTURE_CUBE_MAP, g_cubeTexture);

        g_pSphere->Render("lit");

        glBindTexture(GL_TEXTURE_CUBE_MAP, 0);

        glUseProgram(0);
    }

    glutPostRedisplay();
    glutSwapBuffers();
}

Это указывает на то DrawFace, что рисует заданную грань кубической карты. Это реализовано следующим образом:

void DrawFace(int iFace)
{
    glFramebufferTexture2D(GL_DRAW_FRAMEBUFFER, GL_COLOR_ATTACHMENT0,
        GL_TEXTURE_CUBE_MAP_POSITIVE_X + iFace, g_cubeTexture, 0);

    GLenum status = glCheckFramebufferStatus(GL_DRAW_FRAMEBUFFER);
    if(status != GL_FRAMEBUFFER_COMPLETE)
        printf("Status error: %08x\n", status);

    //The projection matrix is in a uniform buffer.
    ProjectionBlock projData;
    projData.cameraToClipMatrix = glm::perspective(90.0f, 1.0f, g_fzNear, g_fzFar);

    glBindBuffer(GL_UNIFORM_BUFFER, g_projectionUniformBuffer);
    glBufferSubData(GL_UNIFORM_BUFFER, 0, sizeof(ProjectionBlock), &projData);
    glBindBuffer(GL_UNIFORM_BUFFER, 0);

    glViewport(0, 0, (GLsizei)CUBE_TEXTURE_SIZE, (GLsizei)CUBE_TEXTURE_SIZE);

    const glm::vec4 &faceColor = g_faceColors[iFace];
    glClearColor(faceColor.x, faceColor.y, faceColor.z, faceColor.w);
    glClearDepth(1.0f);
    glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);

    if(g_pSphere)
    {
        glutil::MatrixStack modelMatrix;
        modelMatrix.Translate(g_faceSphereLocs[iFace]);

        glUseProgram(g_progUnlit.theProgram);
        glUniformMatrix4fv(g_progUnlit.modelToCameraMatrixUnif, 1, GL_FALSE,
            glm::value_ptr(modelMatrix.Top()));

        const glm::vec4 &sphereColor = g_faceSphereColors[iFace];
        glUniform4fv(g_progUnlit.objectColorUnif, 1, glm::value_ptr(sphereColor));

        glActiveTexture(GL_TEXTURE0 + g_cubeTexUnit);
        glBindTexture(GL_TEXTURE_CUBE_MAP, g_cubeTexture);

        g_pSphere->Render("flat");

        glBindTexture(GL_TEXTURE_CUBE_MAP, 0);
        glUseProgram(0);
    }
}

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

Существенные моменты для DrawFaceэтого.

Как правило, если у меня нет определенных знаний о том, что состояние установлено, я устанавливаю это состояние. Я устанавливаю окно просмотра каждый раз, когда я звоню DrawFace. Я устанавливаю матрицу проекции каждый раз. Это лишнее; Я мог бы установить их обратно displayдо цикла, который вызывает DrawFace, так же, как я делаю с текущим FBO и рендерингом глубины.

Но я также очищаю буферы, которые различны для каждого лица (так как каждое лицо имеет свой цвет).

Николь Болас
источник