Wskaźniki na nowoczesne mapowanie cieni OpenGL?
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 porozumieniaOto 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.
KwestiaTeraz 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?)
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.