Necesito pasar una matriz de enteros a un sombreador como uniforme. Como las matrices uniformes aún no son compatibles, estoy usando una textura FORMAT_R8 / isampler2D para lograr esto.

Si estoy leyendo correctamente los bytes de la textura, deberían estar en el rango [0..7] y luego, al normalizar ese valor, debería obtener diferentes tonos de rojo, pero eso no está sucediendo .

En la captura de pantalla, puede ver que cuando el valor se lee como 0, se muestra correctamente en negro, pero cuando es diferente de 0, es completamente rojo, sin sombras. Intenté comparar el valor leído y siempre es mayor que cualquier valor que pueda probar, hasta el valor máximo de un entero de 32 bits con signo, así que no tengo idea de qué valor estoy leyendo de la textura.

¿Alguna idea?

El proyecto completo está aquí: https://github.com/Drean64/ choque-de-colores / árbol / 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:

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
}
2
Petruza 11 may. 2021 a las 04:54

1 respuesta

La mejor respuesta

Esto es lo que funcionó para mí:

  1. Utilice sampler2D en lugar de isampler2D (este parece ser el culpable).
  2. Normalice multiplicando por 32.0 (es decir 256.0 / 8.0).

Es decir:

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

Actualización : aparentemente, los valores están ahí cuando se usa isampler2D, pero están codificados de manera incorrecta.

Comencé confirmando que la textura se estaba generando correctamente. El problema es al enlazar la textura para el sombreador.

Eché un vistazo a la fuente de Godot, pero no encontré nada específico sobre la textura de isampler2D. Lo que me hizo pensar que se manejó de la misma manera que sampler2D (o no soy muy bueno para mirar, en cualquier caso, debería intentar imaginar que las cosas se metan con el código de sombreado).

Lo primero que debe intentar fue averiguar qué tan grandes eran los valores. Y dieron la vuelta 1000000000. Entonces, parece que estamos obteniendo valores de 32 bits. Después de probar cada uno, esto se acerca bastante a una solución:

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

Eso casi funciona. Por razones que no me quedan claras, donde debería tener el segundo tono de rojo, es negro. Eso me hizo pensar que quizás los valores estén codificados como flotante.

Bajo esa idea, decidí dividir los valores por 2097152 (2²¹ = 256 * 256 * 32). Esto eliminaría la mayor parte de la parte fraccionaria.

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

Obtuve esta representación binaria de valores de punto flotante usando https://baseconvert.com/ieee -754-punto-flotante

Mi idea era tomar los bits que marqué con una X arriba. Después de dividir por 2097152, se habrían movido a los tres bits menos significativos. Eso nos deja este código:

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

¿Eso funciona? De nuevo, casi. Es el mismo resultado que antes. Excepto que este viene con una explicación (parcial) del resultado en virtud de la tabla anterior, ya que el valor 2 es 0 en los bits que marqué con una X arriba. De hecho, los valores de 0 y 1 también necesitarían parche.

Ahora, solo podría parchear los valores, de alguna manera, tendríamos código de trabajo, ¿verdad? Solo necesito averiguar cuáles terminaron siendo los valores.

Si intento dividir el valor de 2, que es 1073741824 por 2097152, obtengo 512. Lo cual ... no funcionó (y culparé a baseconvert.com). Pero sirvió como un buen punto de partida para buscar los valores reales.

Terminé con el siguiente código, que dará el resultado esperado con 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;
}

Entonces, mi explicación es que está pasando los valores como números de punto flotante, pero leyéndolos como números enteros.

2
Theraot 11 may. 2021 a las 05:39