Depth Buffering
>Ya estamos otra vez con los nombre raros!!
Pues si, no es mi culpa, que quieres que le haga?? yo no invente todo esto!!! asi que no te quejes y a seguir leyendo...
Esto del "Depth Buffering" es simplemente de que manera nos las arreglamos para dibujar solo aquellas cosas que se ven. Parece una perogrullada, pero no es tan facil. La cosa que parece tan facil en realidad es muy simple de entender, pero algo mas complicado de llevar a la practica. Suponiendo que tenemos un monton de cajas desparramadas por el suelo enfrente nuestro, desde nuestra posicion veriamos un par o tres caras de la caja que estuviese mas cerca de nosotros, algunas porciones de algunas de las caras de la caja que estuviese detras de esta, y asi con el resto de cajas, incluso habra alguna de las cajas que ni siquiera veriamos.
>Pues tio, es muy sencillo, dibujo todas las caras por orden... comenzando por la que esta mas lejos, y acabando con la que esta mas cerca.
muy bien, veo que tambien piensas!! este metodo que tu dices funcionaria, ya que redibujariamos una caja sobre otra hasta obtener el resultado final.
El problema que tiene este metodo es que cuando tenemos miles de caras que dibujar, nos puede costar muchisimo tiempo redibujar todas una por una, y encima varias veces por segundo. Para que vamos a dibujar lo que no vamos a ver?
Debemos calcular que superficies son visibles desde nuestro punto antes de dibujarlas (este proceso se llama VSD, Visible Surface Determination)
Segun la cantidad de veces que redibujemos sobre un mismo pixel, mayor sera la "complejidad de profundidad" (Depth Complexity) Por ejemplo, una complejidad de valor 3 significa que un pixel ha tenido que ser renderizado 3 veces hasta que vemos su color final en la pantalla. Tambien significa que estamos renderizando 3 veces mas lento que si solo tuviesemos que dibujar una vez. Segun la complejidad de la escena podemos variar de una complejidad 1 (la ideal) hasta 10 o mas, lo mas normal es que redibujemos 2 o 3 veces un pixel.
Para calcular que caras son visibles y cuales no utilizamos lo que se llama "Depth Buffers" (buffer de profundidad). La idea es sencilla, poligonos cerca de la camara ocultan poligonos mas lejanos. La tecnica que mas se usa hoy en dia se llama BSD (Binary Space Partition trees) y es la que se emplea en juegos como Quake. Otra tecnica posible se llama "Octrees" que se usan en juegos donde haya que generar terrenos y paisajes, por ejemplo en juegos de carreras de coches o motos.
Direct3D soporta almacenar la profundidad de una superficie en la propia estructura de la superficie. De este modo a la hora de renderizar Direct3D sabe si un poligono esta situado antes o despues que otro.
El buffer de profundidad suele tener el mismo tamaño que el buffer de color, este es donde se guarda la escena antes de ser dibujada. Esto viene a ser entre 16 y 32 bits por cada pixel. Los valores de la distancia en el espacio-mundo cuando estas mirandolo en perspectiva no son proporcionales a los almacenados en el buffer de profundidad (llamados z-values) Esto significa que la distancia entre objetos que esten juntos no guarda la misma relacion en el buffer de profundidad, en este buffer los objetos mas cercanos tienen mayor precision de profundidad que los que esten lejos.
Esto es asi porque si no tuviesemos esta precision en el buffer de profundidad habria la posibilidad de que se generasen muchos errores a la hora de renderizar.
Como la precision del buffer de profundidad depende de la precision del buffer de color, al utilizar resoluciones de 16 bits podriamos tener estos errores de los que hablaba antes. Para solucionarlos, Direct3D nos ofrece dos tipos de Depth Buffers: z-buffer y w-buffer. El w-buffer tiene mas resolucion que el z-buffer y no es soportado todavia por casi ninguna tarjeta 3D (en cambio si z-buffer).
>Joer tio, y como hago para usar todo esto en mi programa???
lo primero de todo:
m_bUseDepthBuffer = TRUE;
en el constructor de nuestra clase CMyD3DApplication() (recuerdas el Common Files Framework??)
Con esto Direct3D habilita internamente el Z-Buffer. Si queremos usar W-Buffer debemos primero comprobar que nuestra tarjeta lo soporta:
D3DPRIMCAPS* pdpc = &m_pDeviceInfo -> ddDeviceDesc.dpcTriCaps;
if ( 0L == ( pdpc -> dwRasterCaps & D3DPRASTERCAPS_WBUFFER ) )
m_pd3dDevice -> SetRenderState( D3DRS_ZENABLE, D3DZB_USEW ); // Z-Buffer
else
m_pd3dDevice -> SetRenderState( D3DRS_ZENABLE,TRUE ); // W-Buffer
Cuando uses DepthBuffer debes vaciarlo cada vez que redibujas el frame, asi estara listo para el siguiente frame. Asi pues, en el metodo Render() escribiriamos:
m_pd3dDevice -> Clear( 0, NULL, D3DCLEAR_TARGET | D3DCLEAR_ZBUFFER, 0x00000000, 1.0f, 0L );
Esto borra el Z-Buffer (o el W-Buffer) y el Viewport pintandolo de negro.
Tambien es posible borrar solo algunas partes del viewport (por si no quieres borrar todo). No tiene sentido en juegos tipo Quake, pero si en aventuras graficas donde hay zonas de la pantalla que no cambian entre frame y frame. Para ello debes conoces como funciona la funcion que acabamos de emplear, Clear()
HRESULT Clear(
DWORD Count,
CONST D3DRECT* pRects;
DWORD Flags,
D3DCOLOR Color,
float Z,
DWORD Stencil
};
Esta funcion acepta uno o mas rectangulos en pRects. El numero de rectangulos que le pases debes pasarselo a Count. La variable Flags indica que tipo de memoria se va a borrar. Esto es Z-BUFFER, RENDER TARGET, y STENCIL BUFFER. La variable Z almacena el valor Z, y la variable Stencil el valor Stencil (quiero decir, los que se guardan en el z-buffer y en el stencil buffer).
De todas formas, no vamos a usar nada de esto en nuestro proximo programa, asi que no te preocupes.