Wskaźniki na nowoczesne mapowanie cieni OpenGL?

tło

Pracuję nad grą 3D przy użyciu C ++ i nowoczesnego OpenGL (3.3). Pracuję teraz nad oświetleniem i renderowaniem cieni, i z powodzeniem wdrożyłem kierunkowe mapowanie cieni. Po przeczytaniu wymagań gry zdecydowałem, że potrzebuję mapowania cieni w świetle punktowym. Po przeprowadzeniu pewnych badań odkryłem, że aby wykonać wielokierunkowe mapowanie cieni, zrobię coś podobnego do kierunkowego mapowania cieni, ale zamiast tego zastosuję mapowanie kubemów.

Nie mam żadnej wcześniejszej wiedzy o cubemapach, ale ich zrozumienie jest takie, że cubemap to sześć tekstur, bezproblemowo dołączonych. Zrobiłem trochę rozglądając się, ale niestety usiłowałem znaleźć definitywny „tutorial” na temat współczesnego OpenGL. Najpierw szukam samouczków, które wyjaśniają to od początku do końca, ponieważ poważnie zmagałem się z fragmentami kodu źródłowego lub po prostu pojęciami, ale próbowałem.

Aktualne porozumienia

Oto moje ogólne zrozumienie pomysłu, pomijając szczegóły techniczne. Proszę mnie poprawić.

Dla każdego światła punktowego tworzony jest bufor ramki, taki jak kierunkowe mapowanie cieniPojedyncza tekstura cubemap jest następnie generowana i wiązana zglBindTexture(GL_TEXTURE_CUBE_MAP, shadowmap).

Cubemap ma następujące atrybuty:

glTexParameteri(GL_TEXTURE_CUBE_MAP_ARB, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
glTexParameteri(GL_TEXTURE_CUBE_MAP_ARB, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
glTexParameteri(GL_TEXTURE_CUBE_MAP_ARB, GL_TEXTURE_WRAP_R, GL_CLAMP_TO_EDGE);
glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_MAG_FILTER, GL_LINEAR);

(jest to również podobne do kierunkowego mapowania cieni)

TerazglTexImage2D() jest powtarzany sześć razy, raz dla każdej twarzy. Robię to w ten sposób:

 for (int face = 0; face < 6; face++) // Fill each face of the shadow cubemap
     glTexImage2D(GL_TEXTURE_CUBE_MAP_POSITIVE_X + face, 0, GL_DEPTH_COMPONENT32F , 1024, 1024, 0, GL_DEPTH_COMPONENT, GL_FLOAT, NULL);

Tekstura jest dołączana do bufora ramki za pomocą wywołania

glFramebufferTexture(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, shadowmap, 0);

Gdy scena ma być renderowana, jest renderowana w dwóch przejściach, jak kierunkowe mapowanie cieni.

Po pierwsze, bufor ramki cienia jest związany, rzutnia jest dostosowana do rozmiaru mapy cieni (w tym przypadku 1024 na 1024).Culling jest ustawiony na przednich ścianachglCullFace(GL_FRONT)Aktywny program cieniujący jest przełączany na cieniowanie cieni wierzchołków i fragmentów, które zapewnię źródła w dół

Obliczane są macierze widoku światła dla wszystkich sześciu widoków. Robię to, tworząc wektor glm :: mat4's ipush_back() macierze, takie jak ta:

// Create the six view matrices for all six sides
for (int i = 0; i < renderedObjects.size(); i++) // Iterate through all rendered objects
{
    renderedObjects[i]->bindBuffers(); // Bind buffers for rendering with it

    glm::mat4 depthModelMatrix = renderedObjects[i]->getModelMatrix(); // Set up model matrix

    for (int i = 0; i < 6; i++) // Draw for each side of the light
    {
        glFramebufferTexture2D(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_TEXTURE_CUBE_MAP_POSITIVE_X + i, shadowmap, 0);
        glClear(GL_DEPTH_BUFFER_BIT); // Clear depth buffer

        // Send MVP for shadow map
        glm::mat4 depthMVP = depthProjectionMatrix * depthViewMatrices[i] * depthModelMatrix;
        glUniformMatrix4fv(glGetUniformLocation(shadowMappingProgram, "depthMVP"), 1, GL_FALSE, glm::value_ptr(depthMVP));

        glUniformMatrix4fv(glGetUniformLocation(shadowMappingProgram, "lightViewMatrix"), 1, GL_FALSE, glm::value_ptr(depthViewMatrices[i]));
        glUniformMatrix4fv(glGetUniformLocation(shadowMappingProgram, "lightProjectionMatrix"), 1, GL_FALSE, glm::value_ptr(depthProjectionMatrix));
        glDrawElements(renderedObjects[i]->getDrawType(), renderedObjects[i]->getElementSize(), GL_UNSIGNED_INT, 0);
    }
}

Domyślny bufor ramki jest powiązany, a scena jest rysowana normalnie.

Kwestia

Teraz do shaderów. To jest miejsce, gdzie moje zrozumienie wysycha. Jestem całkowicie niepewny, co powinienem zrobić, moje badania wydają się być ze sobą w konflikcie, ponieważ dotyczy różnych wersji. Skończyło się na tym, że kopiowałem i wklejałem kod z losowych źródeł, mając nadzieję, że osiągnie coś innego niż czarny ekran. Wiem, że to straszne, ale wydaje się, że nie ma jasnych definicji, co robić. W jakich miejscach pracuję? Czy potrzebuję nawet osobnego modułu cieniującego, jak w oświetleniu kierunkowym? Czego, do diabła, używam jako typ mapy cienia? samplerCube? samplerCubeShadow? Jak prawidłowo pobrać próbkę tej kostki? Mam nadzieję, że ktoś może mi to wyjaśnić i dostarczyć miłego wyjaśnienia. Moje obecne rozumienie części shadera to: - Gdy scena jest renderowana w kubemapie, moduł cieniowania wierzchołków pobiera po prostu obliczoną w moim kodzie C ++ jednolitość depthMVP i przekształca przez nie wierzchołki wejściowe. - Moduł cieniujący fragmentu mapy kostki po prostu przypisuje pojedynczą wartość dogl_FragCoord.z. (Ta część nie zmieniła się od momentu, w którym zaimplementowałem kierunkowe mapowanie cieni. Zakładałem, że będzie to samo dla kubingu, ponieważ shadery nawet nie wchodzą w interakcję z kubemapą - OpenGL po prostu renderuje dane wyjściowe z nich na kubapę, prawda? bufor ramki?)

Moduł cieniujący wierzchołka dla normalnego renderowania pozostaje niezmieniony.W cieniu fragmentu do normalnego renderowania pozycja wierzchołka jest przekształcana w przestrzeń światła za pomocą rzutowania światła i macierzy widoku.Jest to w jakiś sposób używane w wyszukiwaniu tekstur cubemap. ??Gdy głębokość zostanie odzyskana za pomocą środków magicznych, jest porównywana z odległością światła od wierzchołka, podobnie jak kierunkowe mapowanie cieni. Jeśli jest mniej, punkt ten musi być zacieniony i odwrotnie.

To nie ma większego zrozumienia. Odchodzę od tego, jak wierzchołki są przekształcane i używane do wyszukiwania kubemap, więc zamierzam wkleić źródło moich shaderów, w nadziei, że ludzie będą w stanie to wyjaśnić. Proszę zauważyć, że wiele z tego kodu jest ślepym kopiowaniem i wklejaniem, nie zmieniłem niczego, by nie zagrażać jakiemukolwiek zrozumieniu.

Shader wierzchołka cienia:
#version 150

in vec3 position;

uniform mat4 depthMVP;

void main()
{
    gl_Position = depthMVP * vec4(position, 1);
}
Shader fragmentu cienia:
#version 150

out float fragmentDepth;

void main()
{
    fragmentDepth = gl_FragCoord.z;
}
Standardowy moduł cieniujący wierzchołków:
#version 150

in vec3 position;
in vec3 normal;
in vec2 texcoord;

uniform mat3 modelInverseTranspose;
uniform mat4 modelMatrix;
uniform mat4 viewMatrix;
uniform mat4 projectionMatrix;

out vec3 fragnormal;
out vec3 fragnormaldirection;
out vec2 fragtexcoord;
out vec4 fragposition;
out vec4 fragshadowcoord;

void main()
{
    fragposition = modelMatrix * vec4(position, 1.0);
    fragtexcoord = texcoord;
    fragnormaldirection = normalize(modelInverseTranspose * normal);
    fragnormal = normalize(normal);
    fragshadowcoord = projectionMatrix * viewMatrix * modelMatrix * vec4(position, 1.0);


    gl_Position = projectionMatrix * viewMatrix * modelMatrix * vec4(position, 1.0);
}
Moduł cieniujący standardowy fragment:
#version 150

out vec4 outColour;

in vec3 fragnormaldirection;
in vec2 fragtexcoord;
in vec3 fragnormal;
in vec4 fragposition;
in vec4 fragshadowcoord;

uniform mat4 modelMatrix;
uniform mat4 viewMatrix;
uniform mat4 projectionMatrix;
uniform mat4 viewMatrixInversed;

uniform mat4 lightViewMatrix;
uniform mat4 lightProjectionMatrix;

uniform sampler2D tex;
uniform samplerCubeShadow shadowmap;

float VectorToDepthValue(vec3 Vec)
{
    vec3 AbsVec = abs(Vec);
    float LocalZcomp = max(AbsVec.x, max(AbsVec.y, AbsVec.z));

    const float f = 2048.0;
    const float n = 1.0;
    float NormZComp = (f+n) / (f-n) - (2*f*n)/(f-n)/LocalZcomp;
    return (NormZComp + 1.0) * 0.5;
}

float ComputeShadowFactor(samplerCubeShadow ShadowCubeMap, vec3 VertToLightWS)
{   
    float ShadowVec = texture(ShadowCubeMap, vec4(VertToLightWS, 1.0));
    if (ShadowVec + 0.0001 > VectorToDepthValue(VertToLightWS)) // To avoid self shadowing, I guess
        return 1.0;

    return 0.7;
}

void main()
{
    vec3 light_position = vec3(0.0, 0.0, 0.0);
    vec3 VertToLightWS = light_position - fragposition.xyz;
    outColour = texture(tex, fragtexcoord) * ComputeShadowFactor(shadowmap, VertToLightWS);
}

Nie pamiętam, skąd pochodzi kod funkcji ComputerShadowFactor i VectorToDepthValue, ponieważ badałem go na moim laptopie, do którego nie mogę się teraz dostać, ale jest to wynik tych shaderów:

Jest to mały kwadrat nieosłoniętej przestrzeni otoczony cieniem.

Najwyraźniej robię tu wiele złego, prawdopodobnie koncentrując się na moich shaderach, z powodu braku wiedzy na ten temat, ponieważ trudno mi się uczyć z niczego poza samouczkami i bardzo mi przykro. Jestem w strachu, że byłoby wspaniale, gdyby ktoś mógł rzucić światło na to z jasnym wyjaśnieniem, co robię źle, dlaczego jest źle, jak mogę to naprawić, a może nawet jakiś kod. Myślę, że problem może wynikać z tego, że pracuję w niewłaściwych miejscach.

questionAnswers(1)

yourAnswerToTheQuestion