无法从 Godot 的着色器中正确读取 FORMAT_R8 uniform sampler2D 纹理
Can't read a FORMAT_R8 uniform sampler2D texture correctly from within a shader in Godot
我需要将整数数组作为统一传递给着色器。由于尚不支持统一数组,我使用 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
}
这对我有用:
- 使用
sampler2D
代替isampler2D
(这似乎是罪魁祸首)。
- 通过乘以
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;
这有用吗?再次,几乎。这与之前的结果相同。除了这个根据上面的 table 附带对结果的(部分)解释,因为值 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;
}
所以,我的解释是,它将值作为浮点数传递,但将它们作为整数读取。
我需要将整数数组作为统一传递给着色器。由于尚不支持统一数组,我使用 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
}
这对我有用:
- 使用
sampler2D
代替isampler2D
(这似乎是罪魁祸首)。 - 通过乘以
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;
这有用吗?再次,几乎。这与之前的结果相同。除了这个根据上面的 table 附带对结果的(部分)解释,因为值 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;
}
所以,我的解释是,它将值作为浮点数传递,但将它们作为整数读取。