1

所以我希望在我的级联阴影贴图分割之间实现抖动作为混合模式。

我不知道它们是什么,所以我观看了这个视频来尝试理解它。
据我了解,这是一种将图像颜色映射到有限托盘的方法,同时试图在不同颜色的像素之间保持令人信服的渐变。

现在,从这个视频中,我了解了如何根据抖动模式的权重计算我的眼睛会看到什么颜色。我不明白的是我们如何拍摄具有 4 字节像素数据的图像,例如尝试将其映射到 1 字节像素数据。如果我们基本上受到限制,我们如何将原始图像中的每个像素颜色映射到其加权平均看起来就像是原始颜色的抖动模式?假设我们仅限于 5 种颜色,我猜测并非使用这 5 种托盘颜色的抖动模式的所有可能加权平均组合都可以产生原始像素颜色,那么如何实现呢?是否还为每个像素计算抖动模式以实现抖动图像?

除了这些关于图像抖动的一般问题之外,我仍然很难理解这种技术如何帮助我们在级联分割之间进行混合,就在代码中实际实现它而言,我已经看到了一个使用片段空间坐标的示例并计算一个抖动(不确定它实际上在计算什么,因为它不返回一个矩阵它返回一个浮点数):

float GetDither2(ivec2 p)
{
    float d = 0.0;

    if((p.x & 1) != (p.y & 1))
        d += 2.0;
    if((p.y & 1) == 1)
        d += 1.0;

    d *= 0.25;

    return d;
}

float GetDither4(ivec2 p)
{
    float d = GetDither2(p);
    d = d * 0.25 + GetDither2(p >> 1);
    return d;
}

float threshold = GetDither4(ivec2(gl_FragCoord.xy));

if(factor <= threshold)
{
    // sample current cascade
}
else
{
    // sample next cascade
}

然后它根据返回的浮点数对任一级联图进行采样。所以我的大脑无法将我了解到的你可以有一个抖动模式来模拟大颜色模式的知识转化为这个使用返回的浮点数作为阈值因子并将其与某个混合因子进行比较的示例,以从任一阴影贴图中进行采样。所以这让我更加困惑。

希望对此有一个很好的解释

编辑:

好的,我看到我提供的算法与维基百科关于有序抖动的文章之间的相关性,据我所知,这是首选的抖动算法,因为根据文章:

此外,由于抖动模式的位置相对于显示帧始终保持不变,因此与误差扩散方法相比,它更不容易出现抖动,因此适用于动画。

现在我看到代码试图为给定的空间坐标获取此阈值,尽管在我看来它有点错误,因为以下阈值计算如下: Mpre(i,j) = (Mint(i,j)+1) / n^2

它需要设置:float d = 1.0而不是float d = 0.0如果我没记错的话。其次,我不确定如何左移ivec2空间坐标(我什至不确定 glsl 中向量的按位移位的行为是什么......)但我假设它只是组件按位运算,我尝试了插件(头部计算) 对于给定的空间坐标(2,1)(根据我对按位运算的假设)并得到不同的阈值结果,该阈值应该是 4x4 拜耳矩阵中该位置的阈值。

所以我怀疑这段代码实现有序抖动算法的效果如何。

其次,我仍然不确定这个阈值与在阴影贴图 1 或 2 之间进行选择有什么关系,而不仅仅是减少给定像素的颜色托盘,这个逻辑还没有在我的脑海中解决,因为我不明白使用给定空间坐标的抖动阈值来选择要从中采样的正确地图。

最后会不会选择空间坐标会导致抖动?给定世界位置的片段,(x,y,z)谁被阴影。给定帧的片段空间坐标是(i,j)。如果相机移动,这个片段空间坐标是否会改变,从而使为这个片段计算的抖动阈值随着每次移动而改变,从而导致抖动模式的抖动?

EDIT2: 尝试将地图混合如下,尽管结果看起来不太好,有什么想法吗?

const int indexMatrix8x8[64] = int[](
    0, 32, 8, 40, 2, 34, 10, 42,
    48, 16, 56, 24, 50, 18, 58, 26,
    12, 44, 4, 36, 14, 46, 6, 38,
    60, 28, 52, 20, 62, 30, 54, 22,
    3, 35, 11, 43, 1, 33, 9, 41,
    51, 19, 59, 27, 49, 17, 57, 25,
    15, 47, 7, 39, 13, 45, 5, 37,
    63, 31, 55, 23, 61, 29, 53, 21
);

for (int i = 0; i < NR_LIGHT_SPACE; i++) {
        if (fs_in.v_FragPosClipSpaceZ <= u_CascadeEndClipSpace[i]) {
            shadow = isInShadow(fs_in.v_FragPosLightSpace[i], normal, lightDirection, i) * u_ShadowStrength;
                int x = int(mod(gl_FragCoord.x, 8));
                int y = int(mod(gl_FragCoord.y, 8));
                float threshold = (indexMatrix8x8[(x + y * 8)] + 1) / 64.0;
                if (u_CascadeBlend >= threshold)
                {
                    shadow = isInShadow(fs_in.v_FragPosLightSpace[i + 1], normal, lightDirection, i + 1) * u_ShadowStrength;
                }
            }
            break;
        }
    }

基本上,如果我理解我正在做的是从矩阵中获取阴影像素的每个空间坐标的阈值,并且它(使用概率)高于混合因子,而不是我对第二张地图进行采样。

结果如下: 在此处输入图像描述 较大的红色框是地图之间发生分割的地方。
较小的红色框表示存在一些抖动模式,但图像并没有像我认为的那样混合。

4

2 回答 2

1

首先我对CSM一无所知,所以我专注于抖动和混合。首先看这些:

他们基本上回答了你关于如何计算抖动模式/像素的问题。

拥有良好的抖动调色板也很重要,可以将 24/32 bpp 降低到 8 bpp(或更少)。有2个基本方法

  1. 减少颜色(颜色量化)

    所以计算原始图像的直方图并从中选择或多或少覆盖整个图像信息的重要颜色。有关更多信息,请参阅:

  2. 抖动调色板

    抖动使用像素的平均值来生成所需的颜色,因此我们需要能够生成我们想要的所有可能颜色的颜色。因此,每种基色(R,G,B,C,M,Y)和一些(> = 4)灰色阴影的阴影很少(2..4)很好。从这些你可以组合你想要的任何颜色和强度(如果你有足够的像素)

#1是最好的,但它与每个图像相关,因此您需要为每个图像计算调色板。这可能是个问题,因为计算是令人讨厌的 CPU 消耗量。同样在旧的 256 色模式下,您无法同时显示 2 个不同的调色板(真彩色不再是问题),因此抖动通常是更好的选择。

您甚至可以将两者结合起来以获得令人印象深刻的结果。

使用的调色板越好,结果的颗粒感就越少......

标准 VGA 16 和 256 调色板专为抖动而设计,因此使用它们是个好主意...

标准 VGA 16 调色板: VGA 16 色

标准 VGA 256 调色板: VGA 256 色

这里还有 256 种颜色的 C++ 代码:

//---------------------------------------------------------------------------
//--- EGA VGA pallete -------------------------------------------------------
//---------------------------------------------------------------------------
#ifndef _vgapal_h
#define _vgapal_h
//---------------------------------------------------------------------------
unsigned int vgapal[256]=
    {
    0x00000000,0x00220000,0x00002200,0x00222200,
    0x00000022,0x00220022,0x00001522,0x00222222,
    0x00151515,0x00371515,0x00153715,0x00373715,
    0x00151537,0x00371537,0x00153737,0x00373737,
    0x00000000,0x00050505,0x00000000,0x00030303,
    0x00060606,0x00111111,0x00141414,0x00101010,
    0x00141414,0x00202020,0x00242424,0x00202020,
    0x00252525,0x00323232,0x00303030,0x00373737,
    0x00370000,0x00370010,0x00370017,0x00370027,
    0x00370037,0x00270037,0x00170037,0x00100037,
    0x00000037,0x00001037,0x00001737,0x00002737,
    0x00003737,0x00003727,0x00003717,0x00003710,
    0x00003700,0x00103700,0x00173700,0x00273700,
    0x00373700,0x00372700,0x00371700,0x00371000,
    0x00371717,0x00371727,0x00371727,0x00371737,
    0x00371737,0x00371737,0x00271737,0x00271737,
    0x00171737,0x00172737,0x00172737,0x00173737,
    0x00173737,0x00173737,0x00173727,0x00173727,
    0x00173717,0x00273717,0x00273717,0x00373717,
    0x00373717,0x00373717,0x00372717,0x00372717,
    0x00372525,0x00372531,0x00372536,0x00372532,
    0x00372537,0x00322537,0x00362537,0x00312537,
    0x00252537,0x00253137,0x00253637,0x00253237,
    0x00253737,0x00253732,0x00253736,0x00253731,
    0x00253725,0x00313725,0x00363725,0x00323725,
    0x00373725,0x00373225,0x00373625,0x00373125,
    0x00140000,0x00140007,0x00140006,0x00140015,
    0x00140014,0x00150014,0x00060014,0x00070014,
    0x00000014,0x00000714,0x00000614,0x00001514,
    0x00001414,0x00001415,0x00001406,0x00001407,
    0x00001400,0x00071400,0x00061400,0x00151400,
    0x00141400,0x00141500,0x00140600,0x00140700,
    0x00140606,0x00140611,0x00140615,0x00140610,
    0x00140614,0x00100614,0x00150614,0x00110614,
    0x00060614,0x00061114,0x00061514,0x00061014,
    0x00061414,0x00061410,0x00061415,0x00061411,
    0x00061406,0x00111406,0x00151406,0x00101406,
    0x00141406,0x00141006,0x00141506,0x00141106,
    0x00141414,0x00141416,0x00141410,0x00141412,
    0x00141414,0x00121414,0x00101414,0x00161414,
    0x00141414,0x00141614,0x00141014,0x00141214,
    0x00141414,0x00141412,0x00141410,0x00141416,
    0x00141414,0x00161414,0x00101414,0x00121414,
    0x00141414,0x00141214,0x00141014,0x00141614,
    0x00100000,0x00100004,0x00100000,0x00100004,
    0x00100010,0x00040010,0x00000010,0x00040010,
    0x00000010,0x00000410,0x00000010,0x00000410,
    0x00001010,0x00001004,0x00001000,0x00001004,
    0x00001000,0x00041000,0x00001000,0x00041000,
    0x00101000,0x00100400,0x00100000,0x00100400,
    0x00100000,0x00100002,0x00100004,0x00100006,
    0x00100010,0x00060010,0x00040010,0x00020010,
    0x00000010,0x00000210,0x00000410,0x00000610,
    0x00001010,0x00001006,0x00001004,0x00001002,
    0x00001000,0x00021000,0x00041000,0x00061000,
    0x00101000,0x00100600,0x00100400,0x00100200,
    0x00100303,0x00100304,0x00100305,0x00100307,
    0x00100310,0x00070310,0x00050310,0x00040310,
    0x00030310,0x00030410,0x00030510,0x00030710,
    0x00031010,0x00031007,0x00031005,0x00031004,
    0x00031003,0x00041003,0x00051003,0x00071003,
    0x00101003,0x00100703,0x00100503,0x00100403,
    0x00000000,0x00000000,0x00000000,0x00000000,
    0x00000000,0x00000000,0x00000000,0x00000000,
    };
//---------------------------------------------------------------------------
class _vgapal_init_class
        {
public: _vgapal_init_class();
        } vgapal_init_class;
//---------------------------------------------------------------------------
_vgapal_init_class::_vgapal_init_class()
        {
        int i;
        BYTE a;
        union { unsigned int dd; BYTE db[4]; } c;
        for (i=0;i<256;i++)
            {
            c.dd=vgapal[i];
            c.dd=c.dd<<2;
            a      =c.db[0];
            c.db[0]=c.db[2];
            c.db[2]=      a;
            vgapal[i]=c.dd;
            }
        }
//---------------------------------------------------------------------------
#endif
//---------------------------------------------------------------------------
//--- end. ------------------------------------------------------------------
//---------------------------------------------------------------------------

现在回到你关于通过抖动混合的问题

混合是将 2 个相同分辨率的图像按一定数量(权重)合并在一起。所以每个像素颜色的计算如下:

color = w0*color0 + w1*color1;

其中color?是源图像中的像素,w?是所有权重总和为 1 的权重:

w0 + w1 = 1;

这里的例子:

和预览(这些点从我的 GIF 编码器抖动):

混合

但是通过抖动进行混合的方式不同。我们不使用混合颜色,而是使用来自一张图像的一定百分比的像素和来自第二张图像的其他像素。所以:

if (Random()<w0) color = color0;
 else            color = color1;

WhereRandom()返回范围内的伪随机数<0,1>。正如您所看到的,您只需从复制像素的图像中选择没有完成颜色组合...这里预览:

通过抖动混合

现在这些点是由抖动混合引起的,因为图像的强度彼此相距很远,所以看起来不太好,但如果你抖动相对相似的图像(如你的阴影贴图图层),结果应该足够好(几乎没有性能损失)。

为了加快速度,通常会预先计算Random()某个框(8x8、16x16 ......)的输出并将其用于整个图像(它有点块状,但这是一种有趣的效果......)。这样它也可以无分支地完成(如果您存储指向源图像的指针而不是随机值)。如果权重是整数,例如,它也可以完全在整数上完成(没有固定的精度)<0..255>......

现在要进行级联/转换image0image1或者只是简单地做这样的事情:

for (w0=1.0;w0>=0.0;w0-=0.05)
 {
 w1=1.0-w0;
 render blended images;
 Sleep(100);
 }
render image1;
于 2020-04-16T06:22:20.313 回答
0

我让抖动混合在我的代码中工作,如下所示:

for (int i = 0; i < NR_LIGHT_SPACE; i++) {
        if (fs_in.v_FragPosClipSpaceZ <= u_CascadeEndClipSpace[i])
        {
            float fade = fadedShadowStrength(fs_in.v_FragPosClipSpaceZ, 1.0 / u_CascadeEndClipSpace[i], 1.0 / u_CascadeBlend);
            if (fade < 1.0) {
                int x = int(mod(gl_FragCoord.x, 8));
                int y = int(mod(gl_FragCoord.y, 8));
                float threshold = (indexMatrix8x8[(x + y * 8)] + 1) / 64.0;
                if (fade < threshold)
                {
                    shadow = isInShadow(fs_in.v_FragPosLightSpace[i + 1], normal, lightDirection, i + 1) * u_ShadowStrength;
                }
                else
                {
                    shadow = isInShadow(fs_in.v_FragPosLightSpace[i], normal, lightDirection, i) * u_ShadowStrength;
                }
            }
            else
            {
                shadow = isInShadow(fs_in.v_FragPosLightSpace[i], normal, lightDirection, i) * u_ShadowStrength;
            }
            break;
        }
}

首先,考虑到片段位置剪辑空间和级联剪辑空间的结尾,检查我们是否接近通过衰落因子分割的级联fadedShadowStrength(我使用此函数在级联之间进行正常混合,以了解何时开始混合,基本上例如,如果混合因子u_CascadeBlend设置为0.1,那么当我们至少 90% 进入当前级联(z 剪辑空间明智)时,我们会进行混合。

然后,如果我们需要淡入淡出(if (fade <1.0)),我只需将淡入淡出因子与矩阵中的阈值进行比较,并相应地选择阴影贴图。结果: 在此处输入图像描述

于 2020-04-16T15:54:05.857 回答