OpenGL / GLSL: renderizar em mapa de cubo?

13

Estou tentando descobrir como renderizar minha cena em um mapa de cubo. Fiquei um tempo preso nisso e pensei em pedir ajuda a vocês. Eu sou novo no OpenGL e esta é a primeira vez que estou usando um FBO.

Atualmente, tenho um exemplo de uso de um arquivo cmp de mapa de cubos e o tipo de amostra samplerCube no shader de fragmento está anexado a GL_TEXTURE1. Não estou mudando o código do sombreador. Estou apenas mudando o fato de que não chamarei a função que estava carregando o arquivo bmp do cubemap e tentando usar o código abaixo para renderizar em um mapa do cubo.

Você pode ver abaixo que também estou anexando a textura novamente ao GL_TEXTURE1. É assim que eu coloco o uniforme:

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

ele pode acessá-lo no meu fragment shader via uniform samplerCube Cubemap.

Estou chamando a função abaixo da seguinte maneira:

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

Agora, percebo no loop de desenho abaixo que não estou mudando a direção da vista para olhar para o eixo + x, -x, + y, -y, + z, -z. Eu realmente só queria ver algo funcionando primeiro antes de implementar isso. Imaginei que deveria pelo menos ver algo no meu objeto da maneira que o código é agora.

Não estou vendo nada, apenas preto. Eu fiz meu fundo branco ainda o objeto é preto. Eu removi a iluminação e a coloração para provar apenas a textura do cubemap e ainda preta.

Eu estou pensando que o problema pode ser os tipos de formato ao definir minha textura que é GL_RGB8, GL_RGBA, mas também tentei:

GL_RGBA, GL_RGBA GL_RGB, GL_RGB

Eu pensei que isso seria padrão, pois estamos renderizando uma textura anexada a um framebuffer, mas vi exemplos diferentes que usam valores de enumeração diferentes.

Eu também tentei vincular a textura do mapa do cubo em todas as chamadas de desenho que eu estou querendo usar o mapa do cubo:

glBindTexture(GL_TEXTURE_CUBE_MAP, cubeMapTexture);

Além disso, não estou criando um buffer de profundidade para o FBO que vi na maioria dos exemplos, porque estou apenas querendo o buffer de cores para o meu mapa de cubos. Na verdade, adicionei um para ver se esse era o problema e ainda obtive os mesmos resultados. Eu poderia ter estragado tudo quando tentei.

Qualquer ajuda que possa me apontar na direção certa seria apreciada.

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;
    }
Joey Green
fonte
Você já testou para ver se sua drawSpheresfunção realmente desenha algo visível? A função realmente desenha alguma coisa? O que acontece se você mudar drawSpherespara limpar o buffer de quadros?
Nicol Bolas
Sim. Estou fazendo dois passes. Um no código acima, na verdade 6 chamadas acima. Então, eu estou chamando drawSpheres ao renderizar no framebuffer 0 e ele aparece.
Joey Green
Além disso, eu configurei meu plano de fundo para branco. A cor branca não apareceria pelo menos na textura?
Joey Green
Seu código funciona bem para um FBO normal? A maneira que eu entendo, um mapa de cubo deve ser apenas seis texturas, e você teria que retribuirá a cada um separadamente ..
Jari Komppa

Respostas:

10

Bem, não posso garantir que isso ajude você a descobrir o que está acontecendo. Você simplesmente não postou informações suficientes sobre o que está fazendo para rastrear erros específicos. Embora eu possa corrigir uma coisa sua muito rapidamente:

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);
}

Isso ligará apenas drawSpheres cinco vezes. Acho que você queria chamá-lo 6 vezes.

Mas posso postar uma resposta funcional. Observe que esse código foi projetado para ser executado ao lado da minha série de tutoriais , portanto, faz referência ao código que não está presente. Mas isso é principalmente coisas como criar malhas e assim por diante; nada realmente importante.

Aqui estão os pontos salientes. Os shaders para o objeto principal da esfera.

Vertex shader:

#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;
}

Shader de fragmento:

#version 330

in vec3 modelSpaceNormal;

uniform samplerCube cubeTexture;

out vec4 outputColor;

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

A criação da textura do mapa do cubo que será usada como destino de renderização:

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);
}

Na verdade, preencho a textura com dados (em vez de passar NULL para glTexImage2D) como um auxiliar de depuração. Ele garante que tudo estava funcionando antes de começar a usar a textura como um destino de renderização.

Além disso, observe que eu forneço um BASE_LEVEL e MAX_LEVEL. Eu sempre faço isso com minhas texturas imediatamente após a criação. É apenas um bom hábito, já que o OpenGL pode às vezes ser exigente quanto à textura e à pirâmide mipmap. Em vez de lembrar das regras, apenas as defino religiosamente.

Aqui está a principal função de desenho:

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();
}

Isso faz referência a DrawFace, que desenha a face especificada do mapa do cubo. Isso é implementado da seguinte maneira:

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);
    }
}

Essa função faz referência a um conjunto de tabelas globais que eu uso para dar a cada face uma cor de fundo distinta, uma cor de esfera distinta e posicionar a esfera (no espaço da câmera) adequadamente para essa face.

Os pontos salientes para DrawFacesão estes.

Como regra geral, a menos que eu tenha certo conhecimento de que estado está definido, eu o defino. Eu defino a janela de visualização cada vez que ligo DrawFace. Eu defino a matriz de projeção a cada vez. Aqueles são supérfluos; Eu poderia colocá-los de volta displayantes do loop que chama DrawFace, assim como faço com o atual FBO e o renderbuffer de profundidade.

Mas também limpo os buffers, que são diferentes para cada rosto (já que cada rosto tem uma cor diferente).

Nicol Bolas
fonte