微信公众号搜"智元新知"关注
微信扫一扫可直接关注哦!

无法从 Godot 的着色器中正确读取 FORMAT_R8 统一的 sampler2D 纹理

如何解决无法从 Godot 的着色器中正确读取 FORMAT_R8 统一的 sampler2D 纹理

我需要将整数数组作为统一传递给着色器。由于尚不支持统一数组,我使用 FORMAT_R8 纹理/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.,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
}

解决方法

这对我有用:

  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,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,0).r;
COLOR.r = mod(float(x / 2097152),8.0) / 8.0;

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

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

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

我以以下代码结束,这将给出带有 512 的预期结果:

isampler2D

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

版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。