Указатели на современные OpenGL теневые кубы?

Фон

Я работаю над 3D-игрой, использующей C ++ и современный OpenGL (3.3). Сейчас я работаю над освещением и рендерингом теней, и я успешно реализовал направленное отображение теней. После прочтения требований к игре, я решил, что мне понадобится точечный свет для отображения теней. Проведя некоторое исследование, я обнаружил, что для всенаправленного отображения теней я сделаю нечто похожее на направленное отображение теней, но вместо этого с кубической картой.

У меня нет предыдущих знаний о кубических картах, но я понимаю, что кубическая карта - это шесть текстур, которые можно легко прикрепить. Я немного осмотрелся, но, к сожалению, я изо всех сил пытался найти окончательное «учебное пособие» по теме для современного OpenGL. Сначала я ищу учебные пособия, которые объясняют это от начала до конца, потому что я изо всех сил пытался извлечь уроки из фрагментов исходного кода или просто концепций, но я попробовал.

Текущие договоренности

Вот мое общее понимание идеи, за вычетом технических деталей. Пожалуйста, поправьте меня.

Для каждого точечного источника света устанавливается кадровый буфер, например, направленное отображение тенейЗатем генерируется одна текстура кубической карты, которая связывается сglBindTexture(GL_TEXTURE_CUBE_MAP, shadowmap).

Карта куба настроена со следующими атрибутами:

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

(это также похоже на направленное отображение теней)

В настоящее времяglTexImage2D() повторяется шесть раз, по одному разу для каждого лица. Я делаю это так:

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

Текстура прикреплена к фреймбуферу с вызовом

glFramebufferTexture(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, shadowmap, 0);

Когда сцена должна быть визуализирована, она визуализируется в два прохода, как направленное отображение теней.

Прежде всего, теневой кадровый буфер связан, область просмотра настраивается на размер карты теней (в данном случае 1024 на 1024).Отбраковка устанавливается на передние грани сglCullFace(GL_FRONT)Активная шейдерная программа переключается на вершинные и фрагментные теневые шейдеры, которые я предоставлю в дальнейшем источниках

Матрицы освещения для всех шести видов рассчитываются. Я делаю это, создавая вектор glm :: mat4's иpush_back() матрицы, как это:

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

Фреймбуфер по умолчанию связан, и сцена рисуется нормально.

вопрос

Теперь к шейдерам. Это где мое понимание иссякает. Я совершенно не уверен в том, что мне следует делать, мое исследование противоречит друг другу, потому что оно для разных версий. В конце концов я копировал и вставлял код из случайных источников и надеялся, что он достигнет чего-то другого, кроме черного экрана. Я знаю, что это ужасно, но нет четких определений того, что делать. В каких местах я работаю? Мне даже нужен отдельный теневой шейдер, как я использовал при точечном освещении? Какого черта я использую в качестве типа для теневой карты куба? samplerCube? samplerCubeShadow? Как правильно сэмплировать указанную кубическую карту? Я надеюсь, что кто-то может прояснить это для меня и дать хорошее объяснение. Мое текущее понимание части шейдера таково: - Когда сцена визуализируется в кубическую карту, вершинный шейдер просто берет унифицированную глубину MVP, которую я рассчитал в своем коде C ++, и преобразует им входные вершины. - Фрагментный шейдер прохода кубической карты просто назначает единственное значение дляgl_FragCoord.z, (Эта часть не изменилась с момента, когда я реализовал направленное отображение теней. Я предполагал, что это будет то же самое для отображения кубов, потому что шейдеры даже не взаимодействуют с картой куба - OpenGL просто выводит выходные данные из них в карту куба, верно? Потому что это кадровый буфер?)

Вершинный шейдер для обычного рендеринга не изменяется.В фрагментном шейдере для нормального рендеринга положение вершины преобразуется в пространство источника света с помощью матрицы проекции и вида источника света.Это как-то используется в поиске текстуры кубической карты. ???Как только глубина была найдена с помощью магических средств, она сравнивается с расстоянием света до вершины, очень похоже на направленное отображение теней. Если оно меньше, эта точка должна быть затенена, и наоборот.

Это не так много понимания. Я не обращаю внимания на то, как вершины преобразуются и используются для поиска кубической карты, поэтому я собираюсь вставить исходный код для своих шейдеров, в надежде, что люди смогут уточнить это. Обратите внимание, что большая часть этого кода является слепым копированием и вставкой, я ничего не изменил, чтобы не подвергать опасности любое понимание.

Вершинный шейдер теней:
#version 150

in vec3 position;

uniform mat4 depthMVP;

void main()
{
    gl_Position = depthMVP * vec4(position, 1);
}
Шейдер фрагмента тени:
#version 150

out float fragmentDepth;

void main()
{
    fragmentDepth = gl_FragCoord.z;
}
Стандартный вершинный шейдер:
#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);
}
Стандартный фрагментный шейдер:
#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);
}

Я не могу вспомнить, откуда появился код функции ComputerShadowFactor и VectorToDepthValue, потому что я исследовал его на своем ноутбуке, к которому сейчас не могу добраться, но это результат этих шейдеров:

Это небольшой квадрат не затененного пространства, окруженный затененным пространством.

Я, очевидно, делаю здесь много неправильно, возможно, из-за недостатка знаний по этому вопросу, потому что мне трудно учиться чему-либо, кроме учебников, и я очень сожалею об этом. Я в недоумении, было бы замечательно, если бы кто-то мог пролить свет на это с четким объяснением того, что я делаю неправильно, почему это неправильно, как я могу это исправить и, возможно, даже некоторый код. Я думаю, что проблема может быть в том, что я работаю не в том пространстве.

Ответы на вопрос(1)

Ваш ответ на вопрос