【问题标题】:Can't read a FORMAT_R8 uniform sampler2D texture correctly from within a shader in Godot无法从 Godot 的着色器中正确读取 FORMAT_R8 统一的 sampler2D 纹理
【发布时间】:2021-08-01 09:32:27
【问题描述】:

我需要将整数数组作为统一传递给着色器。由于尚不支持统一数组,因此我使用FORMAT_R8 texture/isampler2D 来实现此目的。

如果我从纹理中正确读取字节,它们应该在 [0..7] 范围内,然后通过规范化该值,我应该得到不同的红色阴影,但是这不会发生。

在屏幕截图中,您可以看到当值被读取为 0 时,它正确显示为黑色,但当它不是 0 时,它是全红色,没有阴影。我尝试比较读取的值,它总是大于我可以尝试的任何值,最大为 32 位有符号整数的最大值,所以不知道我从纹理中读回的值是什么。

有什么想法吗?

完整的项目在这里:https://github.com/Drean64/clash-of-the-colors/tree/simple

GDScript:

extends ViewportContainer

func _ready():
    var attr_ink = PoolByteArray([])
    attr_ink.resize(32*24)
    for i in range(0, 32*24):
        attr_ink[i] = i % 8 # fill array with [0..7]
    
    var image = Image.new()
    image.create_from_data(32, 24, false, Image.FORMAT_R8, attr_ink)

    var ink = ImageTexture.new()
    ink.create_from_image(image, 0)
    
    (self.material as ShaderMaterial).set_shader_param("ink", ink)

着色器:

shader_type canvas_item;

uniform isampler2D ink;

void fragment() {
    COLOR = vec4(0., 0., 0., 1.); // Black
    ivec2 cell = ivec2(UV / TEXTURE_PIXEL_SIZE) / 8; // 8x8 pixel cell coordinate
    int ink_color = texelFetch(ink, cell, 0).r; // Should be [0..7]
    COLOR.r = float(ink_color) / 7.; // Should normalize the red value to [0..1]
    // Line above is a kind of debug to get a hint at what the ink_color value is
}

【问题讨论】:

    标签: shader fragment-shader godot gdscript godot-shader-language


    【解决方案1】:

    这对我有用:

    1. 使用sampler2D 而不是isampler2D(这似乎是罪魁祸首)。
    2. 乘以32.0(即256.0 / 8.0)进行归一化。

    即:

    COLOR.r = texelFetch(ink, cell, 0).r * 32.0;
    

    更新:显然使用isampler2D时值存在,但编码错误。

    我首先确认纹理生成正确。问题是在为着色器绑定纹理时。

    我查看了 Godot 的源代码,但没有具体说明为 isampler2D 提供纹理。这让我觉得它的处理方式与sampler2D 相同(或者我不太擅长看,无论哪种情况,我都应该尝试找出与着色器代码混淆的事情)。

    首先要尝试找出值的大小。他们来到了1000000000。所以,我们似乎得到了 32 位的值。在尝试了每一个之后,这非常接近一个解决方案:

    int x = texelFetch(ink, cell, 0).r;
    COLOR.r = mod(float(x / (256 * 256)), 256.0) / 256.0;
    

    这几乎行得通。由于我不清楚的原因,它应该有第二个红色阴影,它是黑色的。这让我觉得也许这些值被编码为浮点数。

    根据这个想法,我决定将这些值除以 2097152 (2²¹ = 256 * 256 * 32)。这将去掉大部分小数部分。

    float                    binary                                integer
    5.877471754111438e-39 -> 0 00000000 01000000000000000000000 -> 2097152
                        1 -> 0 01111111 00000000000000000000000 -> 1065353216
                        2 -> 0 10000000 00000000000000000000000 -> 1073741824
                        3 -> 0 10000000 10000000000000000000000 -> 1077936128
                        4 -> 0 10000001 00000000000000000000000 -> 1082130432
                        5 -> 0 10000001 01000000000000000000000 -> 1084227584
                        6 -> 0 10000001 10000000000000000000000 -> 1086324736
                        7 -> 0 10000001 11000000000000000000000 -> 1088421888
                                      X XX
    

    我使用https://baseconvert.com/ieee-754-floating-point得到了浮点值的这些二进制表示

    我的想法是采用我在上面标记 X 的部分。除以2097152 后,它们将移至三个最低有效位。这给我们留下了这段代码:

    int x = texelFetch(ink, cell, 0).r;
    COLOR.r = mod(float(x / 2097152), 8.0) / 8.0;
    

    这行得通吗?再次,几乎。这与之前的结果相同。除了这个通过上表对结果进行了(部分)解释,因为值 2 在我上面标记为 X 的位上为 0。事实上,0 和 1 的值也需要修补。

    现在,我只能修补这些值,不知何故,我们会有工作代码,对吧?我只需要弄清楚这些值最终是什么。

    如果我尝试将 2 的值(1073741824)除以2097152,我得到512。哪个......没有用(我会责怪baseconvert.com)。但作为搜索实际值的良好起点。

    我以下面的代码结束,这将给出isampler2D 的预期结果:

    int x = texelFetch(ink, cell, 0).r;
    int y = x / 2097152;
    COLOR.r = mod(float(y), 8.0) / 8.0;
    if (y == 476)
    {
       COLOR.r = 1.0 / 8.0;
    }
    if (y == 480)
    {
       COLOR.r = 2.0 / 8.0;
    }
    if (y == 482)
    {
        COLOR.r = 3.0 / 8.0;
    }
    

    所以,我的解释是,它将值作为浮点数传递,但将它们作为整数读取。

    【讨论】:

    • 我会尝试找出 isampler2D 不起作用的原因。
    • 谢谢!有效。我也想知道为什么 isampler2D 不起作用。
    • @Petruza 已更新。我想鼓励在github.com/godotengine/godot/issues 上打开一个问题
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2017-08-25
    • 2011-12-29
    • 1970-01-01
    • 2019-06-06
    • 2019-01-23
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多