domingo, 27 de junio de 2010

hacking depth-buffer

Me preguntaron como se podía acceder al depth-buffer directamente desde un shader. La verdad que siempre que necesite el valor de z, por ejemplo para un shadow map, lo tenia guardado en una textura, previamente generada.
Sin embargo, porque no poder acceder directamente al depth-buffer? aunque sea solo para lectura, si de alguna manera esta disponible.

Buscando, encontre este truco en el manual de programación de la placa. Desafortunadamente es especifico de la placa, en este caso de la nvidia serie 7, y no es algo del directx. El truco es asi, para poder hacerlo, el driver de la placa expone una textura especial que contiene los datos del depth-buffer, engañando asi al directX. Para poder hacerlo, el formato de la textura se crea con una macro FOURCC y el siguiente codigo "no documentado" :


pd3dDevice->CreateTexture(ancho, alto, 1,D3DUSAGE_DEPTHSTENCIL,
(D3DFORMAT)MAKEFOURCC('R','A','W','Z'),
D3DPOOL_DEFAULT, &m_DepthBufTexture, NULL);

El codigo RAWZ funciona para la GeForce serie 6 y 7. Una vez creada esta textura se comporta como cualquier otra textura. Por ejemplo desde un shader se puede acceder al valor del z asi:

(1)
float z = dot(tex2D(DepthBufSampler, Tex).arg,
float3(0.99609375,0.0038909912109375,0.000015199184417724609375));

De donde sale semejante formula?
El tema es asi, el rawz significa que el driver expone la memoria tal como esta, sin convertila a ningun valor, el depth buffer tiene que tener el formato de 24bits para el z-buffer y 8 para el stencil. El z-buffer se almacen en los primeros 3 bytes en el A,R y G, y el stencil se almacena en el B.
A,R y G van de 0 a 1, el A es el de mayor peso, lo que nos lleva a esta formula:
z = A*255/256 + R*255/256/256 + G*255/256/256/256
que es justamente la formula (1). (Ya se imaginan los problemas de redondeo que debe tener esta fórmula no?)

Y esto anda? Bueno me anduvo barbaro hasta que quise probar otra forma de hacerlo, que con el código mas nuevo: INTZ, que dice que expone directamente el valor de Z, en cualquier de los 3 canales, con lo cual no habría que hacer ninguna conversión, y chau problemas de redondeo. Tambien dice que requiere el nuevo driver de nvidia, asi que me lo baje.
A partir de eso no me anda ni el RAWT que si andaba y se me cuelga el panel de control de la nvidia (entre otras cosas....) (tendria que haber guardado el driver original no? )
Mas alla de eso, es interesante como usan una textura con un código mágico para exponer al directx una característica no standard. Buscando un poco por la red encontré que esto es comun en casi todas las placas y hay otras características que se pueden acceder de esta manera.

No hay comentarios:

Publicar un comentario