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)
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)
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)
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?
Respuestas:
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.
fuente
return float4(texCoord.rg, 0, 1);
.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:
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.
fuente
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.
fuente
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.
fuente