OpenGL / GLSL: Czy renderować mapę do kostki?

13

Próbuję wymyślić, jak renderować moją scenę do mapy kostki. Utknąłem na tym trochę i pomyślałem, że poproszę was o pomoc. Jestem nowy w OpenGL i po raz pierwszy korzystam z FBO.

Obecnie mam działający przykład użycia pliku bmp z mapą Cubemap, a typ próbki samplerCube w module cieniującym fragmenty jest dołączony do GL_TEXTURE1. W ogóle nie zmieniam kodu modułu cieniującego. Zmieniam tylko fakt, że nie będę wywoływał funkcji, która ładowała plik bmp Cubemap i próbowała użyć poniższego kodu do renderowania mapy Cubemap.

Poniżej możesz zobaczyć, że ponownie dołączam teksturę do GL_TEXTURE1. Dzieje się tak, gdy ustawiam mundur:

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

może uzyskać do niego dostęp za pośrednictwem mojego modułu cieniującego fragmenty uniform samplerCube Cubemap.

Nazywam poniższą funkcję tak:

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

Teraz zdaję sobie sprawę w poniższej pętli rysowania, że ​​nie zmieniam kierunku widoku, aby spojrzeć w dół osi + x, -x, + y, -y, + z, -z. Naprawdę chciałem tylko zobaczyć, jak coś działa, zanim to zaimplementuję. Uznałem, że powinienem przynajmniej zobaczyć coś na swoim obiekcie takim, jak jest teraz w kodzie.

Nic nie widzę, tylko prosta czerń. Moje tło jest białe, a obiekt jest czarny. Usunąłem oświetlenie i kolory, aby po prostu próbkować teksturę mapy kubka i nadal czarny.

Myślę, że problemem mogą być typy formatów podczas ustawiania mojej tekstury, którą jest GL_RGB8, GL_RGBA, ale próbowałem też:

GL_RGBA, GL_RGBA GL_RGB, GL_RGB

Myślałem, że będzie to standard, ponieważ renderujemy teksturę dołączoną do bufora ramki, ale widziałem różne przykłady, które używają różnych wartości wyliczania.

Próbowałem również powiązać teksturę mapy kostki w każdym wywołaniu losowania, którego chcę użyć mapy kostki:

glBindTexture(GL_TEXTURE_CUBE_MAP, cubeMapTexture);

Ponadto nie tworzę bufora głębokości dla FBO, co widziałem w większości przykładów, ponieważ chcę tylko bufor kolorów dla mojej mapy kostki. Dodałem jeden, aby sprawdzić, czy to jest problem i nadal mam takie same wyniki. Mógłbym to zrobić, gdy próbowałem.

Doceniam każdą pomoc, która może skierować mnie w dobrym kierunku.

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
źródło
Czy sprawdziłeś, czy twoja drawSpheresfunkcja rzeczywiście rysuje coś widocznego? Czy funkcja rzeczywiście coś rysuje? Co się stanie, jeśli zmienisz się, drawSpheresaby po prostu wyczyścić bufor ramki?
Nicol Bolas,
Tak. Robię dwa przejścia. Jeden z powyższego kodu, właściwie 6 wywołań powyżej. Następnie wywołuję drawSpheres podczas renderowania do bufora ramki 0 i to się pojawia.
Joey Green,
Poza tym ustawiłem moje tło na białe. Czy biały kolor nie pojawiłby się przynajmniej na fakturze?
Joey Green,
Czy Twój kod działa poprawnie dla normalnego FBO? W moim rozumieniu mapa kostki powinna składać się z sześciu tekstur, a każda z nich
musiałaby

Odpowiedzi:

10

Nie mogę zagwarantować, że to pomoże ci dowiedzieć się, co się dzieje. Po prostu nie opublikowałeś wystarczającej ilości informacji o tym, co robisz, aby wyśledzić jakieś konkretne błędy. Chociaż mogę bardzo szybko poprawić jedną z twoich rzeczy:

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

To zadzwoni tylko drawSpheres pięć razy. Zgaduję, że chciałeś to nazwać 6 razy.

Ale mogę opublikować działającą odpowiedź. Zauważ, że ten kod jest przeznaczony do uruchamiania razem z moją serią samouczków , więc zawiera odniesienie do kodu, który nie jest obecny. Ale chodzi przede wszystkim o tworzenie siatek i tak dalej; nic naprawdę ważnego.

Oto najważniejsze punkty. Shadery dla głównego obiektu kuli.

Moduł cieniujący wierzchołek:

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

Moduł cieniujący fragmenty:

#version 330

in vec3 modelSpaceNormal;

uniform samplerCube cubeTexture;

out vec4 outputColor;

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

Tworzenie tekstury mapy kubaturowej, która będzie używana jako cel renderowania:

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

Faktycznie wypełniam teksturę danymi (zamiast przekazywać NULL do glTexImage2D) jako pomoc w debugowaniu. Zapewnia to, że wszystko działało przed użyciem tekstury jako celu renderowania.

Zauważ też, że dostarczam BASE_LEVEL i MAX_LEVEL. I zawsze zrobić z moich tekstur bezpośrednio po stworzeniu. To po prostu dobry nawyk, ponieważ OpenGL bywa czasem wybredny pod względem kompletności tekstur i piramidy mipmap. Zamiast zapamiętywać reguły, po prostu ustawiłem je zgodnie z religijnymi wartościami.

Oto główna funkcja rysowania:

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

Odnosi się to do DrawFace, który rysuje dane oblicze mapy cubemap. Jest to realizowane w następujący sposób:

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

Ta funkcja odnosi się do zestawu globalnych tabel, których używam, aby nadać każdej twarzy odrębny kolor tła, odrębny kolor kuli i odpowiednio ustawić kulę (w przestrzeni kamery) dla tej twarzy.

Najważniejsze z DrawFacenich to:

Zasadniczo, o ile nie mam pewności, że stan jest ustawiony, ustawiam ten stan. Ustawiam rzutnię za każdym razem, gdy dzwonię DrawFace. Za każdym razem ustawiam macierz projekcji. Te są zbyteczne; Mógłbym ustawić je z powrotem displayprzed pętlą, która wywołuje DrawFace, tak jak robię to z obecnym buforem renderowania głębokości i FBO.

Ale usuwam również bufory, które są różne dla każdej twarzy (ponieważ każda twarz ma inny kolor).

Nicol Bolas
źródło