OpenGL / GLSL: ¿Renderizar a mapa de cubo?

13

Estoy tratando de descubrir cómo representar mi escena en un mapa de cubos. Estuve atascado en esto por un momento y pensé que les pediría ayuda. Soy nuevo en OpenGL y esta es la primera vez que uso un FBO.

Actualmente tengo un ejemplo práctico de uso de un archivo bmp cubemap, y el tipo de muestra samplerCube en el sombreador de fragmentos está adjunto a GL_TEXTURE1. No estoy cambiando el código del sombreador en absoluto. Solo estoy cambiando el hecho de que no llamaré a la función que estaba cargando el archivo bmp de cubemap e intento usar el código siguiente para representar un mapa de cubos.

Puede ver a continuación que también adjunto la textura nuevamente a GL_TEXTURE1. Esto es así cuando configuro el uniforme:

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

Puede acceder a él en mi fragment shader a través de uniform samplerCube Cubemap.

Llamo a la siguiente función así:

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

Ahora, me doy cuenta en el siguiente ciclo de dibujo que no estoy cambiando la dirección de la vista para mirar hacia abajo el eje + x, -x, + y, -y, + z, -z. Realmente solo quería ver que algo funcionara antes de implementarlo. Pensé que al menos debería ver algo en mi objeto como está ahora el código.

No veo nada, solo negro. He hecho mi fondo blanco aún, el objeto es negro. Eliminé la iluminación y el color para probar solo la textura del mapa de cubos y aún negro.

Estoy pensando que el problema podría ser el tipo de formato al configurar mi textura, que es GL_RGB8, GL_RGBA, pero también he intentado:

GL_RGBA, GL_RGBA GL_RGB, GL_RGB

Pensé que esto sería estándar ya que estamos renderizando a una textura adjunta a un framebuffer, pero he visto diferentes ejemplos que usan diferentes valores de enumeración.

También he intentado vincular la textura del mapa de cubos en cada llamada de dibujo que quiero usar el mapa de cubos:

glBindTexture(GL_TEXTURE_CUBE_MAP, cubeMapTexture);

Además, no estoy creando un búfer de profundidad para el FBO que vi en la mayoría de los ejemplos, porque solo quiero el búfer de color para mi mapa de cubos. De hecho, agregué uno para ver si ese era el problema y aún obtuve los mismos resultados. Podría haberlo falsificado cuando lo intenté.

Cualquier ayuda que pueda orientarme en la dirección correcta sería 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
fuente
¿Has probado para ver que tu drawSpheresfunción realmente dibuja algo visible? ¿La función realmente dibuja algo? ¿Qué sucede si cambias drawSpherespara borrar el framebuffer?
Nicol Bolas
Si. Estoy haciendo dos pases. Uno en el código anterior, en realidad 6 llamadas anteriores. Luego llamo a drawSpheres cuando renderizo a framebuffer 0 y aparece.
Joey Green el
Además, he configurado mi fondo en blanco. ¿No se vería al menos el color blanco en la textura?
Joey Green
¿Su código funciona bien para un FBO normal? La manera en que yo entiendo, un mapa de cubo debe ser sólo seis texturas, y que tendría que dar a cada uno por separado ..
Jari Komppa

Respuestas:

10

Bueno, no puedo garantizar que esto te ayude a descubrir qué está pasando. Simplemente no ha publicado suficiente información sobre lo que está haciendo para localizar cualquier error en particular. Aunque puedo corregir una cosa tuya muy rápido:

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

Esto solo llamará drawSpheres cinco veces. Supongo que querías llamarlo 6 veces.

Pero puedo publicar una respuesta de trabajo. Tenga en cuenta que este código está diseñado para ejecutarse junto con mi serie de tutoriales , por lo que hace referencia al código que no está presente. Pero esto es principalmente cosas como crear mallas y demás; Nada realmente importante.

Aquí están los puntos más destacados. Los sombreadores para el objeto de esfera principal.

Sombreador de vértices:

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

Sombreador de fragmentos:

#version 330

in vec3 modelSpaceNormal;

uniform samplerCube cubeTexture;

out vec4 outputColor;

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

La creación de la textura del mapa de cubos que se utilizará como destino de representación:

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

De hecho, relleno la textura con datos (en lugar de pasar NULL a glTexImage2D) como una ayuda de depuración. Asegura que todo estaba funcionando antes de comenzar a usar la textura como un objetivo de renderizado.

Además, tenga en cuenta que proporciono BASE_LEVEL y MAX_LEVEL. Yo siempre hago con mis texturas inmediatamente después de la creación. Es solo un buen hábito, ya que OpenGL puede ser exigente a veces sobre la integridad de la textura y la pirámide mipmap. En lugar de recordar las reglas, simplemente las configuré religiosamente con los valores correctos.

Aquí está la función principal de dibujo:

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

Esto hace referencia a DrawFace, que dibuja la cara dada del mapa de cubos. Eso se implementa de la siguiente manera:

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

Esta función hace referencia a un conjunto de tablas globales que utilizo para dar a cada cara un color de fondo distinto, un color de esfera distinto y para colocar la esfera (en el espacio de la cámara) correctamente para esa cara.

Los puntos más destacados DrawFaceson estos.

Como regla general, a menos que tenga cierto conocimiento de que el estado está establecido, lo establezco. Establezco la ventana gráfica cada vez que llamo DrawFace. Establezco la matriz de proyección cada vez. Esos son superfluos; Podría haberlos configurado nuevamente displayantes del ciclo que llama DrawFace, tal como lo hago con el FBO actual y el buffer de renderizado de profundidad.

Pero también elimino los buffers, que son diferentes para cada cara (ya que cada cara tiene un color diferente).

Nicol Bolas
fuente