viernes, 19 de noviembre de 2010

GPU-RT : Mipmapping

La imagen anterior tenia un problema, en los bordes de la esfera se ve un color incorrecto, que parece un problema de aliasing:




Sin embargo el defecto no tiene nada que ver con el aliasing. De hecho, si se reemplaza el color de las esferas por un color fijo y el piso tambien, se puede observar que no hay ningun problema en el contorno, y la precision es la del pixel.




El problema es mucho mas complicado y esta relacionado con el mipmapping de las texturas. Cuando se carga una textura el directX puede generar una serie de texturas reduciendo el tamaño por la mitad en el ancho y en el alto, y generando una cadena de texturas, que facilitan el "texture filtering". Asi para una textura de 64x64, se generan otras de 32x32, 16x16...hasta una de un pixel.

El pipeline automaticamente selecciona la textura correspondiente haciendo uso de lo que se llama la derivada parcial de la coordenada.

Ejemplo: del manual del direct:
ddx - HLSL
Returns the partial derivative of x with respect to the screen-space x coordinate.

The x is supposed to be an interpolated parameter, for example a texture coordinate. The ddx function indeed returns the rate of change of it's parameter across the current pixel and the adjacent one. This works, because hardware that supports the derivative operations will always rasterize at least a 2x2 grid of pixels at the same time.

El problema que tenemos es que en verdad estamos dibujando 2 triangulos! Y cuando se llega al borde con otro objeto la grilla interna del hardware va a tener pixels que corresponden a coordenadas de textura completamente diferentes (inclusive de diferentes texturas). La ddx le da cualquier cosa, y usa un mimmap incorrecto, generando ese problema en el borde. Usualmente le da un valor enorme, y utiliza el ultimo mimap, que corresponde al promedido de todos los puntos de la textura. (Este hecho es el que me ayudo a darme cuenta lo que pasaba), porque en el borde me ponia el promedio de la textura!

Desafortundamente, resolverlo no es tan facil. Para hacerlo bien habria que calcular 2 rayos adicionales, moviendose un pixel en x y en y respectivamente, de tal forma de determinar a que punto de la textura van a parar, y ahi determinar cuantos texels se requieren promediar, para buscar el bitmap mas adecuado.

Esto quiza sugiere modificar el algoritmo para que trabaje con 4 rayos al mismo tiempo, pero por el momento, una forma facil de resolverlo es usar la funcion
tex2Dlod en lugar de tex2. Esta funcion recibe como parametro tambien el LOD de la textura. El mismo se puede aproximar usando la distancia desde la camara al punto de interseccion, basicamente cuanto mas lejos este de la camara menos nivel de detalle utiliza, o sea mas grande el parametro que se le pasa a tex2Dlod.
Otra vez, no hay una forma de calcularlo rapido, y hay que tocarlo a "dedo".

Ejemplo:

// interpolo la textura en el triangulo                
float2 tex = txA*(1-bc_b-bc_g) + txB*bc_b + txC*bc_g;
//float3 color_obj = tex2D( g_samDef, tex);
float3 color_obj = tex2Dlod( g_samDef, float4(tex.x,tex.y,0,R/35));


R es la distancia desde la camara hasta el pto de inteseccion, pero el 35 surge de la distancia maxima visible y la el hecho que la textura tenia 8 mipmapings, o sea esta a "ojo". En general una funcion lineal como R*K resuelve el problema en casi toda la imagen, menos en lo que esta muy lejos, como se puede ver en la siguiente imagen, que los contornos de la esfera salen bien, pero la parte de atras del piso, no tiene mucha resolucion.

No hay comentarios:

Publicar un comentario