OpenGL / GLSL: rendre la carte du cube?

13

J'essaie de comprendre comment rendre ma scène sur une carte de cube. Je suis coincé là-dessus depuis un moment et j'ai pensé que je vous demanderais de l'aide. Je suis nouveau sur OpenGL et c'est la première fois que j'utilise un FBO.

J'ai actuellement un exemple de travail d'utilisation d'un fichier bmp cubemap, et le type d'échantillon samplerCube dans le fragment shader est attaché à GL_TEXTURE1. Je ne change pas du tout le code du shader. Je change juste le fait que je n'appellerai pas la fonction qui chargeait le fichier bmp de cubemap et essayait d'utiliser le code ci-dessous pour effectuer un rendu sur un cubemap.

Vous pouvez voir ci-dessous que je rattache également la texture à GL_TEXTURE1. C'est ainsi lorsque je mets l'uniforme:

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

il peut y accéder via mon fragment shader via uniform samplerCube Cubemap.

J'appelle la fonction ci-dessous comme ceci:

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

Maintenant, je réalise dans la boucle de dessin ci-dessous que je ne change pas la direction de la vue pour regarder vers le bas sur l'axe + x, -x, + y, -y, + z, -z. Je voulais vraiment voir quelque chose fonctionner avant de l'implémenter. J'ai pensé que je devrais au moins voir quelque chose sur mon objet comme le code est maintenant.

Je ne vois rien, juste noir droit. J'ai rendu mon arrière-plan blanc, mais l'objet est noir. J'ai supprimé l'éclairage et la coloration pour simplement échantillonner la texture cubemap et toujours noir.

Je pense que le problème pourrait être les types de format lors de la définition de ma texture qui est GL_RGB8, GL_RGBA mais j'ai également essayé:

GL_RGBA, GL_RGBA GL_RGB, GL_RGB

Je pensais que ce serait standard car nous rendons une texture attachée à un framebuffer, mais j'ai vu différents exemples qui utilisent différentes valeurs d'énumération.

J'ai également essayé de lier la texture de la carte du cube dans chaque appel de dessin que je souhaite utiliser la carte du cube:

glBindTexture(GL_TEXTURE_CUBE_MAP, cubeMapTexture);

De plus, je ne crée pas de tampon de profondeur pour le FBO que j'ai vu dans la plupart des exemples, car je ne veux que le tampon de couleur pour ma carte de cube. J'ai en fait ajouté un pour voir si c'était le problème et j'ai toujours obtenu les mêmes résultats. J'aurais pu tromper cela quand j'ai essayé.

Toute aide pouvant me diriger dans la bonne direction serait appréciée.

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
la source
Avez-vous testé pour voir que votre drawSpheresfonction dessine réellement quelque chose de visible? La fonction dessine-t-elle réellement quelque chose? Que se passe-t-il si vous changez drawSpherespour simplement effacer le tampon d'image?
Nicol Bolas
Oui. Je fais deux passes. Un dans le code ci-dessus, en fait 6 appels ci-dessus. Ensuite, j'appelle drawSpheres lors du rendu sur framebuffer 0 et il apparaît.
Joey Green
De plus, j'ai défini mon arrière-plan sur blanc. La couleur blanche n'apparaîtrait-elle pas au moins dans la texture?
Joey Green
Votre code fonctionne-t-il bien pour un FBO normal? D'après ce que je comprends, une carte de cube ne devrait comporter que six textures, et il faudrait les rendre séparément pour chacune.
Jari Komppa

Réponses:

10

Eh bien, je ne peux pas garantir que cela vous aidera à comprendre ce qui se passe. Vous n'avez tout simplement pas publié suffisamment d'informations sur ce que vous faites pour rechercher des erreurs particulières. Bien que je puisse corriger une chose très rapidement:

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

Cela n'appellera que drawSpheres cinq fois. Je suppose que tu voulais l'appeler 6 fois.

Mais je peux poster une réponse de travail. Notez que ce code est conçu pour être exécuté avec ma série de didacticiels , il fait donc référence à du code qui n'est pas présent. Mais c'est principalement des choses comme la création de maillages et ainsi de suite; rien de vraiment important.

Voici les points saillants. Les shaders de l'objet sphère principal.

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 fragment:

#version 330

in vec3 modelSpaceNormal;

uniform samplerCube cubeTexture;

out vec4 outputColor;

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

La création de la texture cubemap qui sera utilisée comme cible de rendu:

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

En fait, je remplis la texture de données (plutôt que de passer NULL à glTexImage2D) comme aide au débogage. Il garantit que tout fonctionnait avant de commencer à utiliser la texture comme cible de rendu.

Notez également que je fournis un BASE_LEVEL et un MAX_LEVEL. Je le fais toujours avec mes textures immédiatement après la création. C'est juste une bonne habitude, car OpenGL peut parfois être difficile sur la complétude de la texture et la pyramide mipmap. Plutôt que de me souvenir des règles, je les mets juste aux bonnes valeurs religieusement.

Voici la principale fonction de dessin:

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

Cela fait référence à DrawFace, qui dessine la face donnée du cubemap. Cela est mis en œuvre comme suit:

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

Cette fonction fait référence à un ensemble de tableaux globaux que j'utilise pour donner à chaque visage une couleur d'arrière-plan distincte, une couleur de sphère distincte et pour positionner correctement la sphère (dans l'espace de la caméra) pour ce visage.

Les points saillants DrawFacesont les suivants.

En règle générale, à moins que je sois certain que cet état est défini, je règle cet état. Je règle la fenêtre chaque fois que j'appelle DrawFace. J'ai réglé la matrice de projection à chaque fois. Ce sont superflus; J'aurais pu les replacer displayavant la boucle qui appelle DrawFace, tout comme je le fais avec le FBO actuel et le rendu de tampon de profondeur.

Mais j'efface également les tampons, qui sont différents pour chaque face (puisque chaque face a une couleur différente).

Nicol Bolas
la source