将 2D 和 3D 阴影贴图发送到着色器

Sending 2D and 3D shadowmaps to shaders

我正在尝试为我的简单引擎实现阴影贴图,我发现我应该将全向阴影贴图(用于点光源的立方体贴图)与 2d 贴图(用于平行光和聚光灯)结合起来。

我的制服块是这样的:

#define MAX_LIGHTS 128
//...
struct Light
{
   //light data...
};
//...
layout (std140) uniform Lights
{
    int lightCount; //how many lights were passed into the shader (from 0 to MAX_LIGHTS)
    Light lights[MAX_LIGHTS];
};

我有两个问题要问你。

  1. sampler 对象昂贵吗?以下代码是否适用于多灯?

    sampler2D shadowMaps2D[MAX_LIGHTS];
    samplerCube shadowCubemaps[MAX_LIGHTS];
    //...
    if (lights[index].type == POINT_LIGHT)
        CalculateShadow(shadowCubemaps[lights[index].shadowMapNr]);
    else
        CalculateShadow(shadowMaps2D[lights[index].shadowMapNr]);
    

    只有 lightCount 个对象会实际填充纹理。我们遇到了很多未定义的 采样器 ,我认为这可能会导致一些问题。

  2. 如果我没理解错的话,我不能在uniform块中声明sampler。那么每次阴影贴图更新时,我真的被迫循环遍历所有着色器并更新采样器吗?这是浪费时间!

Are sampler objects costly?

这个问题有点误导,因为 GLSL 中的 sampler 数据类型只是 不透明句柄 ,它引用 纹理单元 .代价高昂的是实际的采样操作。此外,特定着色阶段的纹理单元数量是有限的。该规范仅保证 16。由于您不能为不同的采样器类型重复使用相同的单元,因此这会将您的 MAX_LIGHTS 限制为 8.

然而,人们很少需要采样器阵列。相反,您可以使用 array textures,这将允许您将所有阴影贴图(每个纹理类型)存储在一个纹理对象中,并且您只需要一个采样器。

说了这么多我还是觉得你的光数是完全不现实的。即使在目前最快的 GPU 上,实时应用 128 个阴影贴图也行不通...

If I understand correctly, I mustn't declare sampler in uniform blocks.

正确。

So am I really forced to cycle through all of my shaders and update samplers each time the shadow maps get updated? It's a waste of time!

没有。如果要从 采样的纹理单元的 索引发生变化(理想情况下永远不会发生变化),则采样器统一只需要更新。绑定不同的纹理时不会,某些纹理内容发生变化时不会。