现代OpenGL阴影立方体映射的指针? 背景 我正在使用C++和现代OpenGL(3.3)进行3D游戏。我现在正在进行照明和阴影渲染,并且已经成功地实现了方向阴影贴图。在阅读了游戏的要求后,我决定需要点光源阴影贴图。在做了一些研究之后,我发现要做全向阴影贴图,我会做一些类似于方向阴影贴图的事情,但会使用立方体贴图

现代OpenGL阴影立方体映射的指针? 背景 我正在使用C++和现代OpenGL(3.3)进行3D游戏。我现在正在进行照明和阴影渲染,并且已经成功地实现了方向阴影贴图。在阅读了游戏的要求后,我决定需要点光源阴影贴图。在做了一些研究之后,我发现要做全向阴影贴图,我会做一些类似于方向阴影贴图的事情,但会使用立方体贴图,c++,opengl,3d,shader,shadow-mapping,C++,Opengl,3d,Shader,Shadow Mapping,我以前不知道立方体贴图,但我对它们的理解是立方体贴图是六种纹理,无缝连接。 我环顾了一下四周,但不幸的是,我很难找到关于现代OpenGL主题的权威“教程”。我首先寻找从开始到结束解释它的教程,因为我很难从源代码片段或概念中学习,但我尝试了 当前的理解 这是我对这个想法的总体理解,不包括技术细节。请纠正我 对于每个点光源,将设置帧缓冲区,如方向阴影贴图 然后生成一个立方体贴图纹理,并与glBindTexture(GL\u texture\u CUBE\u MAP,shadowmap)绑定 立方体

我以前不知道立方体贴图,但我对它们的理解是立方体贴图是六种纹理,无缝连接。 我环顾了一下四周,但不幸的是,我很难找到关于现代OpenGL主题的权威“教程”。我首先寻找从开始到结束解释它的教程,因为我很难从源代码片段或概念中学习,但我尝试了

当前的理解 这是我对这个想法的总体理解,不包括技术细节。请纠正我

  • 对于每个点光源,将设置帧缓冲区,如方向阴影贴图
  • 然后生成一个立方体贴图纹理,并与
    glBindTexture(GL\u texture\u CUBE\u 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);
    
    // 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);
        }
    }
    
  • 要渲染场景时,将分两次渲染场景,就像方向阴影贴图一样

  • 首先,绑定阴影帧缓冲区,将视口调整为阴影贴图的大小(本例中为1024 x 1024)
  • 使用
    glCullFace(GL\u front)
  • 活动着色器程序切换到顶点和片段阴影着色器,我将提供进一步向下的源
  • 将计算所有六个视图的灯光视图矩阵。我通过创建glm::mat4的向量和
    push_back()
    矩阵来实现,如下所示:

     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);
    
    // 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);
        }
    }
    
    阴影片段着色器: 标准顶点着色器: 标准片段着色器: 我记不起ComputerShadowFactor和VectorToDepthValue函数代码来自何处,因为我当时正在笔记本电脑上研究它,但现在还无法找到,但这是这些着色器的结果:

    它是一个由阴影空间包围的无阴影空间的小正方形


    很明显,我在这里犯了很多错误,可能是以我的着色器为中心的,这是因为我缺乏这方面的知识,因为我发现除了教程之外很难从任何东西中学习,对此我感到非常抱歉。我不知所措,如果有人能清楚地解释我做错了什么,为什么做错了,我如何修复它,甚至一些代码,这将是一件非常美妙的事情。我认为问题可能是因为我在错误的空间工作。

    我希望回答您的一些问题,但首先需要一些定义:

    什么是立方体地图?

    它是从一个方向向量到一对[face,该面上的2d坐标]的映射,通过将方向向量投影到一个假设的立方体上获得

    什么是OpenGL立方体贴图纹理?

    这是一套六幅“图像”

    什么是GLSL立方体贴图取样器?

    它是一个采样器原语,从中可以进行立方体贴图采样。这意味着使用方向向量而不是通常的纹理坐标对其进行采样。然后,硬件将方向向量投影到一个假设的立方体上,并使用生成的[face,2d texture coordinate]对在右侧2d位置对右侧“图像”进行采样

    什么是GLSL阴影采样器?

    它是一个取样器原语,绑定到包含NDC空间深度值的纹理,当使用特定于阴影的采样函数进行采样时,返回NDC空间深度(显然在阴影贴图的同一空间中)与存储在绑定纹理内的NDC空间深度之间的“比较”。调用采样函数时,要比较的深度指定为纹理坐标中的附加元素。请注意,提供阴影采样器是为了便于使用和提高速度,但始终可以在着色器中“手动”进行比较


    现在,请回答您的问题:

    OpenGL只是将[…]渲染到立方体贴图,对吗?

    否,OpenGL渲染到当前有界帧缓冲区中的一组目标

    对于立方体贴图,通常的渲染方式是:

    • 创建它们并将它们的六个“图像”中的每一个附加到同一个 帧缓冲区(显然在不同的附着点)
    • 一次仅启用一个目标(因此,可以分别在每个立方体贴图面中渲染)
    • 在立方体贴图面中渲染所需内容(可能使用面特定的“视图”和“投影”矩阵)
    点光源阴影贴图

    除了所有关于立方体贴图的内容外,在使用立方体贴图实现点光源阴影贴图时还存在许多问题,因此很少使用硬件深度比较

    相反,常见的做法是:

    • 不写NDC空间深度,而是写距离 点光源
    • 查询阴影贴图时(请参见底部的示例代码):
      • 不要使用硬件深度比较(使用samplerCube而不是samplerCubeShadow)
      • 在“立方体空间”(完全不包括投影)中变换要测试的点
      • 使用“立方体空间”向量作为查找方向对立方体贴图进行采样
      • 将从立方体贴图中采样的径向距离与测试点的径向距离进行比较
    示例代码

    // sample radial distance from the cubemap
    float radial_dist = texture(my_cubemap, cube_space_vector).x;
    
    // compare against test point radial distance
    bool shadowed = length(cube_space_vector) > radial_dist;
    
    #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);
    }
    
    // sample radial distance from the cubemap
    float radial_dist = texture(my_cubemap, cube_space_vector).x;
    
    // compare against test point radial distance
    bool shadowed = length(cube_space_vector) > radial_dist;