Representación perfecta en píxeles para un objetivo de rende con un quad de pantalla completa

9

Tengo algunos problemas para representar un montón de valores en un rendertarget. Los valores nunca terminan en el rango exacto que quiero. Básicamente, utilizo un quad de pantalla completa y un sombreador de píxeles para representar mi textura de renderizado y luego intento usar las coordenadas de textura como base para algunos cálculos en el sombreador. Las coordenadas de textura del rango cuádruple van desde (0,0) en la esquina superior izquierda hasta (1,1) en la esquina inferior derecha ... el problema es que, después de la interpolación, estos valores no llegan al sombreador de píxeles como tal .

Un ejemplo: renderizo a una textura 4x4 y el sombreador en este caso simplemente muestra las coordenadas de textura (u, v) en los canales rojo y verde:

return float4(texCoord.rg, 0, 1);

Lo que quiero obtener es una textura donde el píxel en la parte superior izquierda es RGB (0,0,0) y, por lo tanto, negro, el píxel en la parte superior derecha es RGB (255,0,0) y, por lo tanto, un brillo rojo y el píxel en la parte inferior izquierda es RGB (0,255,0), un verde brillante.

Sin embargo, en cambio obtengo esto aquí a la derecha:

(representación cuádruple recta, sin corrección)
renderizado directo de quad, sin corrección aplicada

El píxel superior izquierdo es negro, pero solo obtengo un rojo relativamente oscuro y un verde oscuro en las otras esquinas. Sus valores RGB son (191,0,0) y (0,191,0). Sospecho fuertemente que tiene que ver con las ubicaciones de muestreo del quad: el píxel superior izquierdo muestrea correctamente la esquina superior izquierda del quad y obtiene (0,0) como coordenadas UV, pero los otros píxeles de la esquina no toman muestras Las otras esquinas del patio. He ilustrado esto en la imagen izquierda con el cuadro azul que representa el quad y los puntos blancos las coordenadas de muestreo superiores.

Ahora sé sobre el desplazamiento de medio píxel que debe aplicar a sus coordenadas al representar quads alineados con la pantalla en Direct3D9. Veamos qué tipo de resultado obtengo de esto:

(quad renderizado con el desplazamiento de medio píxel de DX9)
quad renderizado con desplazamiento de medio píxel

El rojo y el verde se han vuelto más brillantes pero aún no son correctos: 223 es el máximo que obtengo en el canal de color rojo o verde. Pero ahora, ya ni siquiera tengo negro puro, ¡sino un gris oscuro amarillento con RGB (32,32,0)!

Lo que realmente necesito sería este tipo de renderizado:

(renderizado objetivo, tamaño cuádruple reducido)
negros puros, verdes y rojos con interpolación correcta

Parece que tengo que mover el borde derecho e inferior de mi quad exactamente un píxel hacia arriba y hacia la izquierda, en comparación con la primera figura. Entonces, la columna derecha y la fila inferior de píxeles deberían obtener correctamente las coordenadas UV directamente desde el borde del quad:

VertexPositionTexture[] quad = new VertexPositionTexture[]
{
    new VertexPositionTexture(new Vector3(-1.0f,  1.0f, 1f), new Vector2(0,0)),
    new VertexPositionTexture(new Vector3(1.0f - pixelSize.X,  1.0f, 1f), new Vector2(1,0)),
    new VertexPositionTexture(new Vector3(-1.0f, -1.0f + pixelSize.Y, 1f), new Vector2(0,1)),
    new VertexPositionTexture(new Vector3(1.0f - pixelSize.X, -1.0f + pixelSize.Y, 1f), new Vector2(1,1)),
};

Sin embargo, esto no funcionó del todo y causó que los píxeles inferiores y derechos no se representaran en absoluto. Supongo que los centros de estos píxeles ya no están cubiertos por el quad y, por lo tanto, el sombreador no los procesará. Si cambio el cálculo de pixelSize en una pequeña cantidad para hacer que el quad sea un poco más grande, funciona un poco ... al menos en una textura 4x4. No funciona en texturas más pequeñas y me temo que distorsiona sutilmente la distribución uniforme de los valores uv en texturas más grandes también:

Vector2 pixelSize = new Vector2((2f / textureWidth) - 0.001f, (2f / textureHeight) - 0.001f);

(Modifiqué el cálculo de pixelSize en 0.001f - para texturas más pequeñas, por ejemplo, tablas de búsqueda 1D, esto no funciona y tengo que aumentarlo a 0.01f o algo más grande)

Por supuesto, este es un ejemplo trivial y podría hacer este cálculo mucho más fácilmente en la CPU sin tener que preocuparme por mapear los rayos UV a los centros de píxeles ... aún así, tiene que haber una manera de representar un completo, completo [0, 1] rango a píxeles en un rendertarget?

Mario
fuente
No es que sea de ayuda, pero estoy teniendo exactamente el mismo problema.
IUsedToBeAPygmy
1
No olvide deshabilitar también el muestreo lineal.
r2d2rigo
No parece que el sistema de coordenadas que está utilizando coincida con los píxeles de la pantalla; si lo fuera, parece que solo está dibujando 2 × 2 píxeles aquí. El primer problema que resolvería es escalar las matrices del proyecto y la vista del modelo de modo que +1 en el espacio de coordenadas sea un desplazamiento de 1 píxel en la pantalla.
Slipp D. Thompson

Respuestas:

4

Su problema es que los UV están diseñados para ser coordenadas de textura. Una coordenada de 0,0 es la esquina superior izquierda del píxel superior izquierdo en la textura, que no es donde normalmente desea leer la textura. Para 2D, desea leer la textura en el medio de ese píxel.

Si mis cálculos son correctos, lo que debe hacer para trabajar es:

return float4((texCoord.rg - (0.5f/textureSize)) * (textureSize/(textureSize-1)), 0, 1);

Es decir, resta el desplazamiento apropiado para obtener el píxel superior izquierdo a 0,0 y luego aplica una escala para obtener la parte inferior derecha correcta.

Lo mismo también podría lograrse expandiendo las coordenadas UV para la geometría fuera del rango 0-1.

Adán
fuente
Para profundizar en la última oración: podría aplicar esta transformación a los UV en el búfer de vértices para las cuatro esquinas del quad, luego en el sombreador de píxeles simplemente haga return float4(texCoord.rg, 0, 1);.
Nathan Reed
Probé la fórmula sugerida arriba y no funcionó: en mi muestra de 4x4 rendertarget desde arriba obtengo 0, 42, 127 y 212 en los canales R y G, de izquierda a derecha o de arriba a abajo. Sin embargo, me gustaría tener valores de 0 a 255 en pasos uniformemente espaciados (para una textura 4x4 que sería 0, 85, 170 y 255). También intenté alterar las coordenadas UV, pero aún no encontré el desplazamiento correcto.
Mario
0

Parece que tengo que mover el borde derecho e inferior de mi quad exactamente un píxel hacia arriba y hacia la izquierda, en comparación con la primera figura.

Casi, pero solo la mitad del píxel. Simplemente resta 0.5 de tus vértices cuádruples. Por ejemplo, si quieres un quad de 256x256 con la misma textura, aquí están tus puntos:

p1 = (-0.5, -0.5)
p2 = (-0.5, 255-0.5)
p3 = (255-0.5, 255-0.5)
p4 = (255-0.5, -0.5)

O bien, puede agregar la mitad del texel en el sombreador de píxeles (texCoord.rg + 0.5 * (1.0 / texSize))

La compensación de medio píxel es un hecho conocido en DX9. Esto y estos artículos también pueden ayudarlo.

alariq
fuente
0

Ciertamente, esto se debe al muestreo lineal. Si miras los texels a la derecha y debajo del píxel superior izquierdo completamente negro, verás que tienen 63 en los canales R y / o G, y 2 de ellos tienen 2 en B. Ahora mira el se vuelve amarillo oscuro y fangoso; es 31 en R y G y 1 en B. Eso definitivamente es el resultado de promediar texels sobre un grupo de 2x2, por lo que la solución es configurar su filtro de textura para que apunte.

Maximus Minimus
fuente
-2

Eso es simplemente porque está utilizando los texCoords de la salida de vértice que es interpolada por el disco duro después de procesar el sombreador de vértices.

Jason Huang
fuente