【问题标题】:How do I use a cubemap array in OpenGL to render multiple point lights with shadowmaps?如何在 OpenGL 中使用立方体贴图数组来渲染带有阴影贴图的多个点光源?
【发布时间】:2021-01-24 21:24:31
【问题描述】:

我阅读并实现了以下教程:https://learnopengl.com/Advanced-Lighting/Shadows/Point-Shadows

现在我想将它概括为在我的场景中渲染多个点光源,我该怎么做?我听说立方体贴图数组可以用于这样的事情,它们是如何工作的?谁能给我举个例子?

(我是为自己和子孙后代回答这个问题,因为目前还没有关于如何使用立方体贴图数组的简单谷歌教程。)

【问题讨论】:

    标签: c++ opengl glsl


    【解决方案1】:

    如果您想将多个立方体贴图阴影贴图传递给着色器,并且您事先不知道具体有多少,那么立方体贴图数组是一个很好的解决方案。它们像 3D 纹理一样工作,其中每一层都是立方体贴图。在片段着色器中,您可以像这样为它们创建一个采样器:

    uniform samplerCubeArray cubeMapArray;
    

    您可以通过以下方式将数组纹理绑定到它:

    glBindTexture(GL_TEXTURE_CUBE_MAP_ARRAY, m_PointLightDepthMaps.m_DepthTexture);
    

    您可以像从立方体贴图中一样从 samplerCubeArray 进行采样,但在这种情况下,您的向量会获得第四个元素,即要从中采样的层。例如:

    texture(cubeMapArray, vec4(dir, 3)).r
    

    将从数组中的第四个立方体贴图采样。要为自己创建一个用作深度纹理的立方体贴图数组,您可以使用以下模板:

    void Renderer::initCubemapDepthMap(CubemapDepthMap& map)
    {
        glGenTextures(1, &map.m_DepthTexture);
        glBindTexture(GL_TEXTURE_CUBE_MAP_ARRAY, map.m_DepthTexture);
        glTexParameteri(GL_TEXTURE_CUBE_MAP_ARRAY, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
        glTexParameteri(GL_TEXTURE_CUBE_MAP_ARRAY, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
        glTexParameteri(GL_TEXTURE_CUBE_MAP_ARRAY, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
        glTexParameteri(GL_TEXTURE_CUBE_MAP_ARRAY, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
        glTexParameteri(GL_TEXTURE_CUBE_MAP_ARRAY, GL_TEXTURE_WRAP_R, GL_CLAMP_TO_EDGE);
        glTexImage3D(
            GL_TEXTURE_CUBE_MAP_ARRAY, 0, GL_DEPTH_COMPONENT, m_ShadowCubeMapResolution, m_ShadowCubeMapResolution, 6 * m_MaxPointLights + 6 * m_MaxSpotLights, 0,
            GL_DEPTH_COMPONENT, GL_FLOAT, nullptr);
        glBindTexture(GL_TEXTURE_CUBE_MAP_ARRAY, 0);
    
        glGenFramebuffers(1, &map.m_FrameBuffer);
        glBindFramebuffer(GL_FRAMEBUFFER, map.m_FrameBuffer);
        glFramebufferTexture(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, map.m_DepthTexture, 0);
        glDrawBuffer(GL_NONE);
        glReadBuffer(GL_NONE);
    
        const int status = glCheckFramebufferStatus(GL_FRAMEBUFFER);
        if (status != GL_FRAMEBUFFER_COMPLETE)
        {
            std::cout << "ERROR::FRAMEBUFFER:: Framebuffer is not complete!\n";
            throw 0;
        }
    
        glBindFramebuffer(GL_FRAMEBUFFER, 0);
    }
    

    请注意,您需要使用 GL_TEXTURE_CUBE_MAP_ARRAY 作为纹理类型,并且需要使用 glTexImage3D。调用 glTexImage3D 时,层数是您需要的立方体贴图数量乘以 6,因为每个立方体贴图面都像单独的层一样分配。在片段着色器中采样时无需考虑这一点!

    在渲染阴影贴图时,您可以像绑定任何其他缓冲区一样绑定缓冲区。当您将深度纹理传递给着色器时,您会传递整个立方体贴图数组,无需与单独的层混淆。

    【讨论】:

    • 我今天刚实现,“6 * m_MaxPointLights + 6 * m_MaxSpotLights”不正确。它应该是“6 * m_MaxSpotLights”(即能被 6 整除)。并且“m_FrameBuffer”应该是一个包含 6 个元素的数组,因为 cubemapArrays 只能通过它们在 CPU 上的面来访问,而不是通过它们的“层”(就像在片段着色器中一样)。那么“glGenFramebuffers”将是:“glGenFramebuffers(1, &mFbo[i]);”在计数为 6 的“for”循环中。根据我的经验,几何着色器方法运行得更好,因为每个点光源只有一个深度通道,而不是 6 个(每个面一个)。
    • “应该是“6 * m_MaxSpotLights”(即能被 6 整除)”我不太明白。我给出的数字也能被六整除。另外,为什么您需要六个帧缓冲区?
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2014-11-22
    • 1970-01-01
    • 2012-03-28
    • 2012-12-09
    • 1970-01-01
    • 2018-06-08
    相关资源
    最近更新 更多