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

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).

Korzystając z naszej strony potwierdzasz, że przeczytałeś(-aś) i rozumiesz nasze zasady używania plików cookie i zasady ochrony prywatności.
Licensed under cc by-sa 3.0 with attribution required.