TRABAJO DE INFORMÁTICA GRÁFICAS 2006/2007 UNIVERSIDAD DE SALAMANCA
INTRODUCCIÓN A DIRECT3D
VÍCTOR ROLDÁN BETANCORT RAÚL SÁNCHEZ RUIZ
ÍNDICE
1. HISTORIA ........................................................................................................................1 2. HAL & COM ....................................................................................................................4 2.1. HARDWARE ABSTRACTION LAYER (HAL)................................................................... 4 2.2. COMPONENT OBJECT MODEL (COM) ...................................................................... 6 3. PRINCIPIOS DE LA PROGRAMACIÓN DE GRÁFICOS EN DIRECT3D ...................................8 4. ARQUITECTURA: LA TUBERÍA DE DIRECT3D (DIRECT3D-PIPELINE) ...........................10 5. TUBERÍA DE TRANSFORMACIONES ................................................................................12 6. TRANSFORMACIONES DEL MUNDO ...............................................................................14 7. TRANSFORMACIONES DE VISTA ...................................................................................17 8. TRANSFORMACIONES DE LA PROYECCIÓN ...................................................................18 9. PUERTO DE VISTA (VIEWPORTS) ..................................................................................20 10. DISPOSITIVOS DE DIRECT3D ......................................................................................22 11. BUFFERS DE VERTICES (VERTEX BUFFERS)................................................................26 12. RENDERIZAR UNA ESCENA A PARTIR DE UN BUFFER DE VÉRTICES ...............................29 13. DIBUJAR A PARTIR DE UN BUFFER DE ÍNDICES (INDEX BUFFER)...............................32 14. CONCLUSIÓN FINAL Y COMPARATIVA ENTRE DIRECT3D Y OPENGL .........................34 15. BIBLIOGRAFÍA ............................................................................................................38
INTRODUCCIÓN A DIRECT3D INFORMÁTICA GRÁFICA 2006/2007
VÍCTOR ROLDÁN BETANCORT RAÚL SÁNCHEZ RUIZ
1. HISTORIA Direct3D es el API (Application Programming Interface) de programación de gráficos 3D perteneciente a la familia DirectX. Su historia se remonta mediados de la década de los 90 con la aparición del sistema operativo Microsoft Windows 95. Durante esa época, la mayoría de los juegos se desarrollaban bajo DOS y a Microsoft le interesaba enormemente “enganchar” a ese amplio sector de la informática que son los videojuegos a su nuevo proyecto, y así hacer mucho más famoso su sistema operativo. Sin embargo, Windows 95 ofrecía demasiadas complicaciones para los programadores de juegos: existía un gran número de capas de abstracción al hardware de audio y video que limitaban enormemente su uso, además de ralentizarlo. El acceso directo que DOS garantizaba también tenia sus propias complicaciones, pero a pesar de ello, dejaban a su alcance acceso total al hardware de audio y video, lo cual llevó a los desarrolladores a escribir complejos códigos para lograr una buena consistencia para todas las configuraciones de PC, y a continuar trabajando bajo DOS. En vista de esta situación, Microsoft se planteó la posibilidad de desarrollar un interfaz que permitiese a los desarrolladores de juegos un mejor acceso al hardware para así permitir que los juegos se ejecutasen a una velocidad aceptable, y proporcionase una abstracción del hardware subyacente que tanto complicaba el desarrollo de los juegos. Pero en lugar de desarrollar su propia API desde cero, Microsoft hizo un astuto movimiento, y es que si Bill Gates es bien conocido es por su gran capacidad como empresario, no siendo la primera vez en la historia de la empresa en que se lleva a cabo algo similar: Microsoft adquirió un motor 3D en desarrollo y lo integró en DirectX. Durante la década de los 90, una importante parte de los motores 3D para ordenador se desarrollaban en Gran Bretaña. Cabe destacar empresas como RenderWare, Argonaut y su conocidísimo motor “BRender” (el cual fue portado en 1994 al sistema operativo OS/2) u otras como la pequeña empresa RenderMorphics. Esta última fue fundada en 1993 por Servan Keondjian, Kate Seekings y Doug Rabson, y fueron los responsables del desarrollo de un producto denominado “Reality Lab”. Fue Keondjian quien desarrollo el motor durante el día mientras tocaba el piano en una banda durante la noche. Mientras tanto, Seekings obtuvo un master en gráficos por computador en la universidad de Middlesex, presentando como proyecto la librería 3D desarrollada por el equipo, y que curiosamente suspendió por no haberse ceñido a las especificaciones del mismo. RenderMorphics había integrado Reality Lab en su “Game SDK”, y confiaban en que tendría gran aceptación entre los desarrolladores de juegos, y no se equivocaron. Durante la presentación de su SDK en 1994 en “SIGGRAPH94”, Microsoft presenció el evento, finalmente adquiriendo la empresa en Febrero de 1995. Tras la adquisición de RenderMorphics, Microsoft integró Reality Lab en su familia de APIs DirectX. Una determinada parte de los componentes de Reality Lab fueron transformados al estándar de API 3D de Windows en ese momento, el “3-D-DDI”, que fue desarrollado por Michael Abrash, que posteriormente ayudo a John Carmack a desarrollar el revolucionario motor de juego “Quake” de ID Software.
INGENIERÍA INFORMÁTICA, UNIVERSIDAD DE SALAMANCA -1-
INTRODUCCIÓN A DIRECT3D INFORMÁTICA GRÁFICA 2006/2007
VÍCTOR ROLDÁN BETANCORT RAÚL SÁNCHEZ RUIZ
DirectX 1.0 no fue nada aceptado, era muy lento, mal estructurado, muy complejo e incluso con muchos fallos de programación. La siguiente versión no apareció hasta 1996, la versión 2.0 de DirectX, que no logró integrase en la comunidad de programadores de juegos. Obviamente, Microsoft no pensaba en abandonar, y trabajo mucho para mejorarlo. La primera versión que fue realmente viable fue la 3.0. Algunos años después surgió la 5.0 (saltándose la 4.0), que fue la primera versión útil de verdad. Grandes mejoras se introdujeron en Direct3D en la versión 6.0 de DirectX, como el soporte de transformaciones e iluminaciones por aceleración de hardware, reorganización de las luces, materiales, objetos de puertos de vista (que entonces pasó a controlarse mediante “dispositivos”, conocidos como “devices”), además de un cambio completo en el control de las texturas, que pasó a se mucho mas sencillo. La versión 7.0 fue la primera que fue altamente aceptada por los desarrolladores de juegos: funcionaba bien, hacía la programación de juegos razonablemente fácil, y a mucha gente le empezó a gustar el interfaz. La versión 8.0 introdujo las mayores mejoras de la historia de Direct3D. Prácticamente se modifico la arquitectura, fusionando en una misma interfaz la parte 2D del API, “DirectDraw”, y la 3D en una misma interfaz denominada “DirectX Graphics”, que llevo a un menor uso de memoria y un modelo de programación más sencillo. Gracias a esos cambios, DirectX fue por primera vez mas sencillo de usar y mas consistente que hasta el entonces dominante OpenGL. Esta versión introdujo técnicas como “Point Sprites” (Conjunto de píxeles con apoyo por hardware en el sistema de partículas para generar efectos de lluvia, explosiones, nieve…), “3D Volumetric Textures” (texturas con 3 dimensiones, permitiéndose iluminaciones por píxel, efectos atmosféricos…), una enorme mejora en la librería “Direct3DX”, introducida en la versión 7.0 y que proporcionan rutinas útiles y altamente eficientes para, por ejemplo, enumerar configuraciones de dispositivos, configurarlos, ejecutar en pantalla completa o en ventana de forma uniforme, cambios de tamaño, cálculos sobre matrices, etc., así como las técnicas de “N-Patches”, o los famosos “Vertex & Píxel Shaders”, que produjo un cambio radical en la forma de programar los juegos modernos y su apariencia. Entre la versión 8.0 y la 9.0 no se introdujeron demasiados cambios. La 9.0 mejora enormemente los estándares de “Píxel & Vertex Shader”. Ya que programar los “shaders” es complejo, se introdujo el HLSL (High-Level Shader Languaje), que permitía escribir “shaders” de una forma rápida y fácil, además de eficiente, en un lenguaje similar al C, en lugar de la antigua sintaxis similar al ensamblador. Además de esto se incluyeron en la versión 9.0 el antialiasing de líneas, G-Buffer, o el esperado soporte para 24-bits de precisión de color. La última que está por llegar en breve (10), incorpora importantes cambios estructurales que soluciona diversos problemas que presentaba la versión 9 y que limitaba a los creadores de videojuegos, como por ejemplo el “object overhead” (sobrecarga de objetos), derivado del cuello de botella introducido por el acceso de la CPU a la API de DirectX. Otra importante limitación era el uso de un pipeline fijo, lo cual muchas veces podía desembocar en la subutilización de recursos (por ejemplo, situaciones en las que el Píxel Shader estaba usado al 100% mientras que el Vertex Shader estaba completamente inutilizado). INGENIERÍA INFORMÁTICA, UNIVERSIDAD DE SALAMANCA -2-
INTRODUCCIÓN A DIRECT3D INFORMÁTICA GRÁFICA 2006/2007
VÍCTOR ROLDÁN BETANCORT RAÚL SÁNCHEZ RUIZ
Para solucionar estas deficiencias, la versión 10 introduce nuevos shaders de geometría, el “Stream Out” y sobre todo, una nueva arquitectura unificada (Vertex, Píxel y Geometry Shaders están unificados, componiéndose como un único elemento de funcionalidad dinámica). Todo esto desemboca un altísimo nivel de detalle que roza el foto-realismo, mejorado procesamiento de sombras, escenas y ambientes más complejos. Durante mucho tiempo, el API Direct3D se consideraba muy malo comparado con OpenGL. Sin embargo, el paso del tiempo y la continua evolución han convertido a Direct3D en una interfaz de programación de gráficos muy potente y estable, y en muchos casos va de la mano en lo que se refiere a innovación en el campo de los gráficos 3D, ya que Microsoft trabaja muy de cerca con las empresas de hardware gráfico llegando, en casos recientes, a introducir nuevas técnicas antes de que el hardware lo haga. INGENIERÍA INFORMÁTICA, UNIVERSIDAD DE SALAMANCA -3-
INTRODUCCIÓN A DIRECT3D INFORMÁTICA GRÁFICA 2006/2007
VÍCTOR ROLDÁN BETANCORT RAÚL SÁNCHEZ RUIZ
2. HAL & COM Si quisiéramos hacer una descripción general de la estructura de Direct3D, habría que destacar la existencia del HAL (Hardware Abstraction Layer) y el uso del modelo COM (Component Object Model) para encapsular su código. El uso de estas dos características surge partir de la necesidad de tener un API robusto, que proporcione una enorme compatibilidad hardware con mecanismo de ejecución alternativo en caso de que este no soporte alguna característica, una interfaz versátil y que no exija a los programadores aprender nuevas especificaciones con cada versión, así como una compatibilidad total con las anteriores versiones de DirectX.
2.1. HARDWARE ABSTRACTION LAYER (HAL) Una de las primeras ventajas que proporciona Direct3D es lo que se conoce como capa de abstracción del hardware o HAL. A parte del controlador del dispositivo (driver) que se proporciona con el hardware de video, Direct3D proporciona una capa de software en torno a la tarjeta gráfica, a la que pertenece el HAL. Dicho HAL esta en comunicación con el driver de la tarjeta gráfica, y ofrece al programador una capa que abstrae las diferencias de cada hardware, haciendo ver a Direct3D la tarjeta como una entidad funcional con unas determinadas características: cuando la tarjeta es capaz de realizar una determinada técnica o cálculo, la muestra como activada, cuando no, la muestra como desactivada. Así, por ejemplo, cuando un programa solicita operaciones al API de Direct3D, en función de si una característica es presentada por el HAL, decidirá hacer uso de ella por hardware o en caso de que no la presenta, usar alguna técnica de emulación alternativa, que siempre se traduce en una degradación de la calidad de la imagen.
FIGURA 1: RELACIÓN DEL HAL Y GDI CON EL DRIVER
INGENIERÍA INFORMÁTICA, UNIVERSIDAD DE SALAMANCA -4-
INTRODUCCIÓN A DIRECT3D INFORMÁTICA GRÁFICA 2006/2007
VÍCTOR ROLDÁN BETANCORT RAÚL SÁNCHEZ RUIZ
Por tanto, Direct3D usa el HAL para acceder a la tarjeta gráfica a través del driver, y se muestra como un dispositivo. Si la tarjeta gráfica soporta Direct3D, entonces existirá HAL en el computador, y para ello el driver debe ofrecer una interfaz compatible con DirectX, ciñéndose a unas determinadas características. Cabe destacar además que si la tarjeta no ofrece ningún tipo de aceleración por hardware, no se podrá crear el dispositivo HAL. Los dispositivos HAL tienen cuatro modos de procesamiento de vértices: -
Procesamiento de vértices por Software Procesamiento de vértices por Hardware Procesamiento de vértices Mixto Procesamiento de vértices puro
Cuando la tarjeta no es capaz de realizar determinadas operaciones, se puede escribir el código que permita emular dicha característica e integrarlo en la ejecución de un programa junto con Direct3D: es lo que se denomina “Pluggable Software Device” (anteriormente conocido como HEL o Hardware Emulation Layer). Hoy en día, aquel programador que desarrollar una aplicación debe escribir su capa de emulación del hardware si desea que la aplicación se ejecute con hardware que no es capaz de ejecutar todas las operaciones que el programa exige o incluso si no hubiese aceleración por hardware, tratando de proporcionar una experiencia visual lo mas parecida posible sin degradar el rendimiento enormemente. A día de hoy son muy pocos los que se enfrentan a este reto y prefieren denegar la ejecución del programa si el hardware no está capacitado, ya que dichas aplicaciones son muy dependientes de lo que sea capaz de hacer la tarjeta gráfica. Gracias a esta característica, Microsoft nos garantiza un API muy consistente capaz de realizar operaciones alternativas cuando el computador no presente hardware especializado. Esto se traduce en una enorme versatilidad y aislamiento del hardware.
INGENIERÍA INFORMÁTICA, UNIVERSIDAD DE SALAMANCA -5-
INTRODUCCIÓN A DIRECT3D INFORMÁTICA GRÁFICA 2006/2007
VÍCTOR ROLDÁN BETANCORT RAÚL SÁNCHEZ RUIZ
2.2. COMPONENT OBJECT MODEL (COM) El COM es un modelo de programación orientada a objetos muy utilizada por Microsoft y la mayoría de aplicaciones en su plataforma Win32. ¿En qué consiste? COM es simplemente una porción de código que ofrece un determinado servicio a través de una interfaz, dicha interfaz se compone de métodos. Un componente COM es normalmente una librería de enlace dinámico (.DLL, Dynamic Link Library) a la que se acceder de una forma determinada. Esto proporciona unas importantes ventajas al programador: 1) Las interfaces de los objetos COM nunca cambian. Es lo que se conoce como estrategia de versiones: tu objeto puede evolucionar y ofrecer nuevas interfaces, pero nunca dejará de ofrecer las antiguas, permanecerán para proporcionar una compatibilidad hacia atrás, garantizando que las aplicaciones siempre funcionarán a medida que evoluciona. 2) Los objetos COM son independientes del lenguaje usado: sea cual sea el lenguaje que se use, se podrá hacer uso de los objetos COM, ya que no son más que porciones de código binario al cual se accede de una forma siempre constate. Esto se logra, sobre todo, gracias a que los métodos no se llaman directamente, sino a través de una doble indirección mediante una tabla de punteros a métodos denominada V-Table. Por ejemplo, una vez declarada una variable de tipo Interfaz Direct3D 9, se inicializa mediante una llamada a un método (Direct3DCreate9) que devuelve el puntero a la interfaz de Direct3D a través de la V-Table. Luego, accediendo a través del puntero, se pueden invocar los diversos métodos de la interfaz. 3) Son muy robustos: no se puede acceder a la información almacenada en los objetos, solo a sus métodos. ¿En qué repercute el uso de COM sobre DirectX? Pues se podría decir que en compatibilidad, sobre todo: si se desarrolló una aplicación en DirectX 3.0 en un ya obsoleto computador, dicha aplicación funcionará todavía en un computador de última generación con una tarjeta de video moderna y con la última versión de DirectX, y no solo con la última, sino con las futuras versiones. Esto se debe a que el interfaz que se ofrece al programador Direct3D para trabajar no ha desaparecido. En cada nueva versión lo que se ofrece es una nueva interfaz, pero no se elimina la anterior. Por ejemplo, la versión 9.0 de DirectX ofrece las siguientes interfaces de Direct3D: -
IDirect3D IDirect3D2 IDirect3D3 IDirect3D7 IDirect3D8 IDirect3D9
Así pues, gracias al modelo COM, se garantiza que toda aplicación que trabaje con DirectX funcionará en versiones futuras, además de ayudar a encapsular la información y a proporcionar interfaces consistentes frente a los diversos lenguajes de programación. INGENIERÍA INFORMÁTICA, UNIVERSIDAD DE SALAMANCA -6-
INTRODUCCIÓN A DIRECT3D INFORMÁTICA GRÁFICA 2006/2007
VÍCTOR ROLDÁN BETANCORT RAÚL SÁNCHEZ RUIZ
Veamos a continuación un ejemplo que corrobore todo lo explicado: para poner una luz en la escena, llamaremos a través de la interfaz de direct3D al método que se encarga de ello: hr = m_pd3dDevice -> lpVtbl -> SetLight( m_pd3dDevice, 0, &light); En C, se accederá a una interfaz (m_pd3dDevice) a través de un puntero a la VTable, que a su vez tendrá el puntero al método que queremos usar (SetLight), al cual, además, habrá que pasarle como primer parámetro un puntero this. Con C++ los objetos COM y los objetos C++ son compatibles a nivel binario, de modo que el compilador maneja interfaces COM y clases abstractas de C++ del mismo modo, por lo que no habrá que acceder a la V-Table, pues ya se hace de forma implícita, al igual que el paso del puntero this. hr = m_pd3dDevice -> SetLight( 0, &light); Por lo general, podemos decir que en general, la mayoría de las llamadas a DirectX en C++ son de la forma NOMBRE_INTERFAZ NOMBRE_METODO. Para poder hacer uso de los objetos COM de Direct3D, a la hora de programar tenemos que enlazar las librerías que se van a usar, que son las siguientes: -
d3dx9dt.lib d3dxof.lib d3d9.lib winmm.lib dxguid.lib comctl32.lib
También se deberá incluir los ficheros de cabecera, con los prototipos de los métodos, definiciones de tipos, etc. Todas estas librerías se proporcionan en el SDK de DirectX creado por Microsoft, que se puede encontrar en www.microsoft.com.
INGENIERÍA INFORMÁTICA, UNIVERSIDAD DE SALAMANCA -7-
INTRODUCCIÓN A DIRECT3D INFORMÁTICA GRÁFICA 2006/2007
VÍCTOR ROLDÁN BETANCORT RAÚL SÁNCHEZ RUIZ
3. PRINCIPIOS FUNDAMENTALES DE LA PROGRAMACIÓN DE GRÁFICOS EN DIRECT3D Antes de entrar en detalles de programación, veamos algunos fundamentos 3D con los que trabaja el API de Microsoft. Por defecto, el sistema de coordenadas de Direct3D es levógiro (o como encontrará en los documentos, left-handed), con la Z positiva entrando en la pantalla:
FIGURA 2: SISTEMA DE COORDENADAS DE DIRECT3D OpenGL, sin embargo, usa un sistema de coordenadas dextrógiro (right-handed), con la Z positiva saliendo de la pantalla. Esto es importante pues en muchos casos interesa transformar de un sistema de coordenadas a otro. Los vértices son la base de los gráficos 3D, y se usan para definir nuestros objetos en el espacio. Se definen como vectores libres cuyo origen es el origen del sistema de coordenadas, y el final es el propio vértice. Los vectores no solo se usan para definir vértices, también se usan para las iluminaciones o para indicar una dirección. En Direct3D un vector se define como un tipo de dato denominado D3DVECTOR, y consta de las componentes, x, y, z. En el caso de los vértices, estos se definen como algo más que un vector que indica su posición. La estructura típica del tipo de dato Vertex en Direct3D es similar a la siguiente: Struct Vertex { D3DVECTOR vPosition; DWORD dwDiffuse; D3DVECTOR vNormal; FLOAT u, v; } El primer atributo indica el vector posición, el segundo un valor que indica el color del píxel, el tercer valor es el vector normal (necesario para las iluminaciones, como por ejemplo, la técnica del sombreado gouraud) y el cuarto valor especifica las coordenadas de una textura para lo que se conoce como mapeado de texturas (texture-mapping).
INGENIERÍA INFORMÁTICA, UNIVERSIDAD DE SALAMANCA -8-
INTRODUCCIÓN A DIRECT3D INFORMÁTICA GRÁFICA 2006/2007
VÍCTOR ROLDÁN BETANCORT RAÚL SÁNCHEZ RUIZ
Direct3D trabaja con vértices de esta forma, y los aloja en un buffer de vértices (vertex-buffer) al que podrá acceder nuestro programa de aplicación, cuando normalmente solo podía acceder el driver de la tarjeta gráfica. El usuario podrá definir qué estructura tendrá el tipo de dato vértice, en función de la información que nos interese alojar: si no vamos a usar iluminaciones, no nos interesará las normales, y tampoco incorporaremos las coordenadas de las texturas si no van a ser usadas. Así logramos optimizar el uso de memoria, que es crítico, pero en cada función es necesario especificar que estructura tiene el vértice. Esto es una práctica habitual que se realiza a través de banderas y macros ya definidas en las cabeceras de DirectX. Cuando posicionamos un objeto en el espacio 3D, necesitamos especificar su orientación, esto se consigue usando tres vectores. Estos vectores se usan para indicar: - Hacia dónde está mirando el objeto - Dónde es arriba para el objeto - En qué posición se encuentra Todo objeto 3D se compone de caras, y ya que Direct3D solo es capaz de renderizar 3 tipos de primitivas (puntos, líneas y triángulos), se encargará de descomponer el objeto en triángulos. Para obtener un rendimiento óptimo, todo objeto que definamos deberá poder descomponerse en triángulos, por lo que lo ideal es generar los modelos a partir de triángulos, así sabremos que Direct3D no introducirá elementos indeseados que ralenticen la renderización (esto se debe, sobre todo, a que hoy en día las tarjetas gráficas hacen cálculos muy rápidamente a través de triángulos). Cabe destacar que Direct3D hace uso de una técnica conocida como “BackFace Culling” de forma automática, que consiste en no renderizar aquellos triángulos que no se ven en la escena, como los que están fuera de la visión de la cámara o aquellos que no puede ver el espectador porque están detrás de otro elemento. Esto se calcula obteniendo el vector normal que define el plano de dos lados comunes de dos triángulos y determinando si el vector está orientado “saliendo de la pantalla” (se renderizará) o “entrando en la pantalla” (no se renderizará). Así se acelera enormemente la renderización de una escena.
INGENIERÍA INFORMÁTICA, UNIVERSIDAD DE SALAMANCA -9-
INTRODUCCIÓN A DIRECT3D INFORMÁTICA GRÁFICA 2006/2007
VÍCTOR ROLDÁN BETANCORT RAÚL SÁNCHEZ RUIZ
4. ARQUITECTURA: LA TUBERÍA DE DIRECT3D (DIRECT3D-PIPELINE) Una visión de alto nivel de cómo trabaja Direct3D podría ser la siguiente:
FIGURA 3: TUBERÍA DE DIRECT3D Esta “tubería” se podría asemejar a una cadena de producción: al comienzo tenemos la materia prima, a partir de la cual, a lo largo de una serie de etapas, se van procesando para dar lugar a la imagen que deseamos. Es el planteamiento de esta tubería lo que convierte a Direct3D en un API de gráficos 3D potente y eficiente. Veamos un breve comentario acerca de estas etapas: -
Datos de vértices y primitivas: no es una etapa, sino la información que obtiene como entrada la tubería a partir de la cual generará la escena. Los vértices son un conjunto de estos sin transformar en buffers de memoria. Las primitivas son figuras geométricas básicas (puntos, líneas, triángulos, polígonos…) que hacen referencia al buffer de vértices mediante un buffer índice, a partir de los cuales se podrá renderizar el objeto.
-
Tessellation: Esta etapa consiste en convertir o descomponer aquellas formas o primitivas de alto nivel en vértices sin transformar.
-
Vertex Processing: Durante esta etapa se efectúan transformaciones básicas, como las transformaciones de vista y modelo, mundo, proyección, cálculo de luces, (de ahí que esta etapa se conozca también como “Transform & Lighting”) o incluso el uso de los “Vertex Shaders”, que nos permite inyectar código propio para procesar los vértices.
-
Geometry Processing: Aquí se aplican una amplia diversidad de técnicas sobre los vértices ya transformados, como por ejemplo el “Back-Culling”, evaluación de los atributos de los vértices, clipping (relacionado con el buffer de profundidad), rasterización (proceso por el cual transforma los vértices a píxeles con coordenadas de dispositivo, solventando los problemas de no concordancia entre las coordenadas del vértice y del píxel), etc.
INGENIERÍA INFORMÁTICA, UNIVERSIDAD DE SALAMANCA - 10 -
INTRODUCCIÓN A DIRECT3D INFORMÁTICA GRÁFICA 2006/2007
VÍCTOR ROLDÁN BETANCORT RAÚL SÁNCHEZ RUIZ
-
Píxel Processing: Procesamiento de texturas y uso del famoso “píxel-shader”, técnica relativamente nueva que al igual que los “vertex-shaders” permite inyectar código propio en el procesamiento de los píxeles. Este proceso desemboca en un conjunto de píxeles con un color calculado a partir de todos los procesos que en esta etapa se efectúan.
-
Píxel Rendering: Se aplican las últimas transformaciones sobre los píxeles, como por ejemplo, aplicar transformaciones con la información de profundidad (z-buffer), niebla, alfa, stencil buffer…y un gran conjunto de técnicas que producen finalmente los píxeles para mostrar en pantalla.
Por tanto, podemos descomponer la tubería en dos grandes etapas: el procesamiento de vértices y el procesamiento de píxeles.
INGENIERÍA INFORMÁTICA, UNIVERSIDAD DE SALAMANCA - 11 -
INTRODUCCIÓN A DIRECT3D INFORMÁTICA GRÁFICA 2006/2007
VÍCTOR ROLDÁN BETANCORT RAÚL SÁNCHEZ RUIZ
5. TUBERÍA DE TRANSFORMACIONES En Direct3D se pueden llevar a cabo una serie de transformaciones para modificar la escena: las transformaciones del mundo, transformaciones de vista, y transformaciones de proyección, además de las transformaciones del modelo. Toda escena 3D al final se convertirá en un plano 2D y todas estas transformaciones determinan que es lo que se verá a través del puerto de vista. La tubería de transformaciones toma los vértices como entrada, y aplica transformaciones de mundo, vista y proyección para transformar los vértices. comienzo de la entrada, los vértices del modelo hacen referencia a un sistema coordenadas locales (los vértices en este estado se conocen como espacio coordenadas locales):
las Al de de
-
Lo primero que se hará es transformar de las coordenadas locales a un sistema de coordenadas mundiales, común a todos los objetos definidos en la escena. El proceso de reorientar los vértices a las nuevas coordenadas se conoce como transformaciones del mundo, obteniéndose el espacio de coordenadas mundiales.
-
La siguiente etapa consiste en reorientar los vértices del mundo 3D respecto a la cámara, es decir, la aplicación toma una posición para la cámara y Direct3D reorienta todos los puntos en coordenadas mundiales en función de lo que el observador debería ver, proceso que se conoce como transformaciones de vista. El conjunto de vértices transformados se conocen como espacio de vista.
-
A continuación se efectúan las transformaciones de proyección. Durante este proceso, los objetos son escalados para dar la sensación de profundidad, a causa de que se pretende ofrecer una escena 2D de algo que ocurre en 3D, aunque no todas las proyecciones (como la ortogonal) escalan los objetos de la escena. También define el volumen de vista. El conjunto de vértices resultantes se conoce como espacio de proyección.
-
En la última etapa de la tubería, se eliminan los vértices que no se van a ver en la escena, así el rasterizador no tiene que calcular colores ni sombreados para píxeles que no se ven, proceso que se conoce como “clipping” o recortado. A continuación, el conjunto de vértices resultantes se transforman acordes a los parámetros del puerto de vista y se convierten en coordenadas de pantalla o dispositivo. El conjunto de vértices resultantes, que se ven en pantalla tras la rasterización (transformación a píxeles) y se conoce como espacio de pantalla o de despliegue.
Se debe comentar que también se efectúan las transformaciones de modelo durante la tubería de transformaciones, pero estas no reubican los vértices, sino que calculan, mayormente, las componentes de iluminación difusa o especular de cada vértice. El orden en el que se efectúe esta transformación durante la tubería es irrelevante, sin embargo, las anteriores etapas han de realizarse en el orden descrito. INGENIERÍA INFORMÁTICA, UNIVERSIDAD DE SALAMANCA - 12 -
INTRODUCCIÓN A DIRECT3D INFORMÁTICA GRÁFICA 2006/2007
VÍCTOR ROLDÁN BETANCORT RAÚL SÁNCHEZ RUIZ
Todas estas transformaciones se realizan a través de producto de matrices, y para definir nuestra escena necesitaremos la matriz del mundo, la matriz de vista y la matriz de proyección. Antes de continuar, se debe comentar un mínimo acerca de la librería Direct3DX, pues se verá a continuación que la mayoría de funciones y definiciones provienen de ésta. Direct3D Extended, que es como se conoce a esta librería, posee funciones básicas para la realización de operaciones esenciales, pues lo que nos ofrece Direct3D es de muy bajo nivel. Por ejemplo, si no fuera por estas librerías, tendríamos que aplicar las transformaciones componiendo nosotros nuestra propia matriz de transformación y luego multiplicando. Gracias a Direct3DX, tendremos funciones que realicen dichas operaciones de forma eficaz y segura. Estas funciones han sido estudiadas en profundidad por los desarrolladores de Microsoft para que resultasen extremadamente eficientes. Las matrices en Direct3D se definen mediante D3DMATRIX, pero por conveniencia usaremos la definición de Direct3DX, similar a la anterior, para la cual, por ejemplo, se han sobrecargado los operadores, entre otras cosas. typedef struct _D3DMATRIX { union { struct { float _11, float _21, float _31, float _41,
_12, _22, _32, _42,
_13, _23, _33, _43,
_14; _24; _34; _44;
}; float m[4][4]; }; } D3DMATRIX; Se observa que se podrá acceder a un elemento mediante _XY, donde X es la fila e Y la columna, o bien mediante [X][Y].
INGENIERÍA INFORMÁTICA, UNIVERSIDAD DE SALAMANCA - 13 -
INTRODUCCIÓN A DIRECT3D INFORMÁTICA GRÁFICA 2006/2007
VÍCTOR ROLDÁN BETANCORT RAÚL SÁNCHEZ RUIZ
6. TRANSFORMACIONES DEL MUNDO Las transformaciones básicas que se pueden realizar son rotaciones, translaciones y escalados. A partir de estas operaciones, las transformaciones del mundo se efectúan concatenando diversas matrices de transformación para finalmente establecerla como matriz del mundo, siendo luego Direct3D el encargado de reubicar los objetos en funcion de esta matriz. Crear matriz de traslación: D3DXMATRIX *WINAPI D3DXMatrixTranslation( D3DXMATRIX *pOut, FLOAT x, FLOAT y, FLOAT z ); Los ángulos de las rotaciones se especifican en radianes Crear matriz de rotación en torno al eje X: D3DXMATRIX *WINAPI D3DXMatrixRotationX( D3DXMATRIX *pOut, FLOAT Angle ); Crear matriz de rotación en torno al eje Y: D3DXMATRIX *WINAPI D3DXMatrixRotationY( D3DXMATRIX *pOut, FLOAT Angle );
Crear matriz de rotación en torno al eje Z: D3DXMATRIX *WINAPI D3DXMatrixRotationZ( D3DXMATRIX *pOut, FLOAT Angle ); Crear matriz de rotación en torno a un vector determinado: D3DXMATRIX *WINAPI D3DXMatrixRotationAxis( D3DXMATRIX *pOut, CONST D3DXVECTOR3 *pV, FLOAT Angle ); INGENIERÍA INFORMÁTICA, UNIVERSIDAD DE SALAMANCA - 14 -
INTRODUCCIÓN A DIRECT3D INFORMÁTICA GRÁFICA 2006/2007
VÍCTOR ROLDÁN BETANCORT RAÚL SÁNCHEZ RUIZ
Crear matriz de rotación en torno a ejes Z (Roll), X (Pitch) e Y (Yaw) consecutivamente: D3DXMATRIX *WINAPI D3DXMatrixRotationYawPitchRoll( D3DXMATRIX *pOut, FLOAT Yaw, FLOAT Pitch, FLOAT Roll ); Crear matriz de escalado: D3DXMATRIX *WINAPI D3DXMatrixScaling( D3DXMATRIX *pOut, FLOAT sx, FLOAT sy, FLOAT sz ); Para obtener el efecto de varias transformaciones a la vez podemos multiplicar las matrices entre sí, proceso conocido como concatenación de matrices. El orden en que se efectúan las multiplicaciones es crucial, ya que a diferencia de los valores escalares, el producto de matrices no es conmutativo. Los efectos visuales de una determinada concatenación de matrices se efectúan de izquierda a derecha. Por ejemplo, si deseamos rotar y luego trasladar, se efectuará la siguiente operación: M = R * T, siendo R la matriz de rotación y T la matriz de traslación. Una vez obtenida y establecida la matriz de modelo y vista, las transformaciones se observarán en pantalla en el orden de izquierda a derecha, es decir, en el orden que se efectúan las multiplicaciones. Esto es de vital importancia, pues por ejemplo, en OpenGL las transformaciones ocurren en el orden contrario en que se invocan las multiplicaciones: las transformaciones ocurren de derecha a izquierda. Para multiplicar matrices podemos usar la función D3DXMatrixMultiply(). Veamos un ejemplo para aclarar las transformaciones: En primer lugar se define la matriz del mundo. A continuación, se calcula la matriz de traslación partiendo del origen. Dado que D3DXMatrixTranslation genera una matriz de traslación, al multiplicar la identidad por la matriz de traslación resulta la misma matriz de traslación, por eso es almacenada en la variable matWorld. A continuación creamos las matrices de rotación en torno al eje Y girando 90 grados (1.57 radianes). Finalmente se multiplican las matrices en el orden que se desee efectuar las operaciones (rotación y luego traslación), para luego establecer dicha matriz como la matriz del mundo. La constante D3DTS_WORLD especifica que se va a establecer la matriz del mundo.
INGENIERÍA INFORMÁTICA, UNIVERSIDAD DE SALAMANCA - 15 -
INTRODUCCIÓN A DIRECT3D INFORMÁTICA GRÁFICA 2006/2007
VÍCTOR ROLDÁN BETANCORT RAÚL SÁNCHEZ RUIZ
D3DXMATRIX matWorld; D3DXMatrixTranslation(&matWorld, 0,1,0); D3DXMATRIX matRotateX; D3DXMatrixRotationY(&matRotateY, 1.57); D3DXMatrixMultiply(&matWorld, & matRotateY, & matWorld); ... // en la funcion encargada del Render m_pd3dDevice -> SetTransform(D3DTS_WORLD, matWorld); ...
INGENIERÍA INFORMÁTICA, UNIVERSIDAD DE SALAMANCA - 16 -
INTRODUCCIÓN A DIRECT3D INFORMÁTICA GRÁFICA 2006/2007
VÍCTOR ROLDÁN BETANCORT RAÚL SÁNCHEZ RUIZ
7. TRANSFORMACIONES DE VISTA La transformación de vista localiza al espectador en el espacio del mundo, transformando los vértices al espacio de vista. En dicho espacio, el espectador está al origen, mirando en sentido positivo de la dirección Z de un nuevo sistema de coordenadas de referencia. La matriz de vista reubicará los objetos de nuestra escena en torno a la posición y orientación de la cámara (origen del espacio de vista). Es decir, se definirá un nuevo sistema de coordenadas, las coordenadas de vista, que establecen como está la cámara en la escena, y en función de las cuales se reubicarán los vértices. Hay diversas formas de crear una matriz de vista, pero en todos los casos, la cámara tendrá una posición y orientación dentro del espacio del mundo que tomamos como referencia inicial para su creación. Una primera forma es combinar matrices de traslación y rotación para la orientación de cada una de las tres direcciones de nuestro nuevo sistema de referencia. Para facilitar esta complicada labor, las librerías D3X proporcionan unas funciones para la creación de dichas matrices. Construir una matriz de vista con un sistema de referencia levógiro. D3DXMATRIX *WINAPI D3DXMatrixLookAtLH( D3DXMATRIX *pOut, CONST D3DXVECTOR3 *pEye, CONST D3DXVECTOR3 *pAt, CONST D3DXVECTOR3 *pUp ); Donde pOut es un puntero a la matriz resultante, pEye es un puntero a un vector de 3 componentes que indican las coordenadas dentro del espacio del mundo donde se encuentra el observador, pAt es otro vector de las mismas características que el anterior y que define la dirección en la que está mirando el espectador, y pUp define para la cámara cual será la dirección que tomará como “arriba”. También se proporciona una función equivalente para los sistemas de coordenadas dextrógiros: D3DXMatrixLookAtRH Ejemplo: D3DXMATRIX out; D3DXVECTOR3 eye(2,3,3); D3DXVECTOR3 at(0,0,0); D3DXVECTOR3 up(0,1,0); D3DXMatrixLookAtLH(&out, &eye, &at, &up); m_pd3dDevice -> SetTransform(D3DTS_VIEW, &out); Para establecer una matriz de vista, accedemos al método SetTransform a través del dispositivo asignado por el interfaz de Direct3D, donde la constante D3DTS_VIEW establece que el cambio que se efectuará es el de la matriz de vista.
INGENIERÍA INFORMÁTICA, UNIVERSIDAD DE SALAMANCA - 17 -
INTRODUCCIÓN A DIRECT3D INFORMÁTICA GRÁFICA 2006/2007
VÍCTOR ROLDÁN BETANCORT RAÚL SÁNCHEZ RUIZ
8. TRANSFORMACIONES DE LA PROYECCIÓN Esta es la transformación más complicada de la tubería, y podría entenderse como una analogía con el tipo de lente que se usa en la cámara: cómo se plasmará la escena 3D que se observa en el plano 2D al que se proyecta y de qué forma se proyectará los elementos dentro del volumen de vista. La “instantánea” obtenida dependerá del tipo de proyección que se use. Existen varios tipos, y las librerías D3X nos proporcionan funciones que crean matrices de proyección tanto en perspectiva como en proyección ortogonal. Toda proyección definirá un volumen fuera del cual los objetos no son vistos por el espectador. Este espacio podría entenderse como el recinto existente entre dos planos: uno cercano y otro lejano. Estableciendo la forma y tamaño de estos planos podemos lograr cambiar la relación de aspecto de la escena a proyectar y el ángulo de visión que tenemos de la misma escena. Las funciones proporcionadas para crear la matriz de proyección son las siguientes: Crea matriz de proyección levógiro: D3DXMATRIX *WINAPI D3DXMatrixPerspectiveLH ( D3DXMATRIX *pOut, FLOAT w, FLOAT h, FLOAT zn, FLOAT zf ); Donde pOut estable el puntero a la matriz de salida, w es el ancho del plano más cercano que define el recinto, y h su ancho. Zn definirá la profundidad a la que se encuentra el plano más cercano, y zf la del plano más lejano. Para esta función existe su variante dextrógiro, D3DXMatrixPerspectiveRH. Efectúa una proyección ortogonal y es equivalente a la función D3DXMatrixPerspectiveOffCenterLH. Construir matriz de proyección levógiro basándose en un campo de visión D3DXMATRIX *WINAPI D3DXMatrixPerspectiveFovLH( D3DXMATRIX *pOut, FLOAT fovy, FLOAT Aspect, FLOAT zn, FLOAT zf );
INGENIERÍA INFORMÁTICA, UNIVERSIDAD DE SALAMANCA - 18 -
INTRODUCCIÓN A DIRECT3D INFORMÁTICA GRÁFICA 2006/2007
VÍCTOR ROLDÁN BETANCORT RAÚL SÁNCHEZ RUIZ
En este caso, el volumen de vista es nuevamente definido por un plano cercano y otro lejano, pero ahora los dos planos serán diferentes, en base a un ángulo de visión en radianes en la dirección de Y (fovy), y una relación de aspecto del volumen de vista aspect (ancho entre alto). Esto produce que tengamos dos planos proporcionales pero no iguales, resultando en una proyección en perspectiva. Junto a ella, las librerías proporcionan también su variante dextrógiro D3DXMatrixPerspectiveFovRH. Construir matriz de proyección levógiro personalizada D3DXMATRIX *WINAPI D3DXMatrixPerspectiveOffCenterLH( D3DXMATRIX *pOut, FLOAT l, FLOAT r, FLOAT b, FLOAT t, FLOAT zn, FLOAT zf ); En esta última función, definiremos el volumen de proyección a través de un recinto cúbico mediante sus coordenadas X mínima (l) y máxima (r), Y mínima (b) y máxima (t), y Z mínimo (zn) y máximo (zf). Como el resto de funciones de esta librería, posee también su variante dextrógiro D3DXMatrixPerspectiveOffCenterRH y efectúa una proyección ortogonal. Ejemplo: D3DXMATRIX matProj; D3DXMatrixPerspectiveFovLH( &matProj, D3DX_PI/4, 1.0f, 1.0f, 100.0f ); g_pd3dDevice->SetTransform( D3DTS_PROJECTION, &matProj ); Este ejemplo genera una matriz con un ángulo de visión de PI cuartos, una relación de aspecto de 1 (uno de alto por uno de ancho), un volumen definido entre Z = 1 y Z = 100, y luego la establece como matriz de proyección mediante la constante D3DTS_PROJECTION.
INGENIERÍA INFORMÁTICA, UNIVERSIDAD DE SALAMANCA - 19 -
INTRODUCCIÓN A DIRECT3D INFORMÁTICA GRÁFICA 2006/2007
VÍCTOR ROLDÁN BETANCORT RAÚL SÁNCHEZ RUIZ
9. PUERTO DE VISTA (VIEWPORTS) El puerto de vista es, conceptualmente, el plano 2D sobre el que se proyecta la escena 3D que hemos definido. En Direct3D, las coordenadas de este plano son definidas sobre el dispositivo en que se renderiza la escena. La transformación de proyección convierte los vértices en el sistema de coordenadas del puerto de vista. En Direct3D, el rectángulo que define el puerto de vista se especifica a través de la estructura D3DVIEWPORT9, la cual es usada en los métodos de manipulación proporcionados por el dispositivo de Direct3D (IDirect3DDevice9) GetViewport() y SetViewport(). La estructura en cuestión es como sigue: typedef struct _D3DVIEWPORT9 { DWORD X; DWORD Y; DWORD Width; DWORD Height; float MinZ; float MaxZ; } D3DVIEWPORT9; X e Y definen las coordenadas del píxel de la esquina superior izquierda del puerto de vista sobre el dispositivo de renderización. Width especifica su ancho y Height su alto, y los parámetros MinZ y MaxZ referencian a los valores mínimos y máximos a usar en lo referente a profundidad. Todos estos parámetros describen el rectángulo del puerto de vista.
FIGURA 4: PUERTO DE VISTA Una vez creada la matriz del puerto de vista, se le pasa como argumento al método SetViewport() del dispositivo, y tomará efecto la próxima vez que se renderice la escena. Si no se especifica un puerto de vista, Direct3D tomará por defecto todo el dispositivo de renderización que se le haya asignado. Mediante GetViewport() podemos recuperar los parámetros del puerto de vista actual y, a través del método Clear() del dispositivo, se puede limpiar el puerto de vista. Es común su uso para limpiar la imagen y establecer valores de inicialización del z-buffer antes de presentar una nueva escena.
INGENIERÍA INFORMÁTICA, UNIVERSIDAD DE SALAMANCA - 20 -
INTRODUCCIÓN A DIRECT3D INFORMÁTICA GRÁFICA 2006/2007
VÍCTOR ROLDÁN BETANCORT RAÚL SÁNCHEZ RUIZ
HRESULT Clear( DWORD Count, const D3DRECT *pRects, DWORD Flags, D3DCOLOR Color, float Z, DWORD Stencil ); Como se pueden definir varios rectángulos, count especifica cuantos contendrá la estructura pRects, que un puntero a un array de rectángulos. Para indicar que se limpiará todo el puerto de vista y no hay rectángulos definidos en este, se pone count a cero y pRects a NULL. Flags indica que es lo que se va a limpiar, y puede ser ZBUFFER, RENDER TARGET (en referencia al dispositivo) o STENCIL BUFFER. Mediante Color concretamos el color de borrado, Z es el valor por defecto del Z buffer al borrarlo y Stencil es el valor por defecto al borrar del Stencil Buffer. Una llamada habitual a este método podría ser el siguiente, en el cual se limpia todo el puerto de vista en negro, y el buffer de profundidad: d3dDevice -> Clear( 0, NULL, D3DCLEAR_TARGET | D3DCLEAR_ZBUFFER, 0x00000000, 1.0f, 0L );
INGENIERÍA INFORMÁTICA, UNIVERSIDAD DE SALAMANCA - 21 -
INTRODUCCIÓN A DIRECT3D INFORMÁTICA GRÁFICA 2006/2007
VÍCTOR ROLDÁN BETANCORT RAÚL SÁNCHEZ RUIZ
10. DISPOSITIVOS DE DIRECT3D Un dispositivo es un componente de Direct3D que se encarga de la renderización, encapsulando y almacenando todo su estado. Además, el dispositivo se encarga de las transformaciones y de las luces, así como del proceso de rasterizado de la escena en una superficie en pantalla. En lo referente arquitectura, podemos distinguir tres grandes módulos: el módulo de transformaciones, el módulo de iluminaciones y el módulo de rasterizado.
FIGURA 5: ARQUITECTURA DE LOS DISPOSITIVOS DE DIRECT3D Actualmente, Direct3D da soporte a dos tipos de dispositivos: el HAL y el dispositivo de referencia. Ambos pueden verse como dos drivers independientes: el HAL representa un driver hardware, el dispositivo de referencia podría ser un driver software. Así, el HAL efectuaría trabajos a través de aceleración por hardware, y el dispositivo de referencia podría emular características que aún no está disponible en el hardware actual. El dispositivo Direct3D que una aplicación cree debe estar en correspondencia adecuada con las capacidades que presenta el hardware que usa la propia aplicación. Así pues, Direct3D presentará unas capacidades, que o bien podrán ser fruto del trabajo mediante hardware o bien puedes ser emulado por software. Los dispositivos acelerados por hardware proporcionan un rendimiento muy superior a los que se logra con emulación por software. El dispositivo HAL estará disponible para todos aquellos computadores con tarjetas gráficas que soporten Direct3D. En la mayoría de los casos, las aplicaciones se diseñan para computadores con aceleración por hardware, y se apoyan en la emulación por software en aquellos computadores de baja gama, sin embargo, lo cierto es que no siempre los dispositivos por software soportan las mismas características que los dispositivos hardware.
INGENIERÍA INFORMÁTICA, UNIVERSIDAD DE SALAMANCA - 22 -
INTRODUCCIÓN A DIRECT3D INFORMÁTICA GRÁFICA 2006/2007
VÍCTOR ROLDÁN BETANCORT RAÚL SÁNCHEZ RUIZ
Para crear un dispositivo HAL, llamaremos al método IDirect3D9::CreateDevice usando D3DDEVTYPE_HAL como parámetro. Si en cambio lo que deseamos es usar un dispositivo de referencia, le pasaremos como parámetro D3DDEVTYPE_REF. El dispositivo de referencia se usa muy poco en aplicaciones comerciales, y se limita a propósitos de demostración o testeo. Para crear un dispositivo, primero se ha de crear un objeto Direct3D, el encargado de ponernos en comunicación con el API. Todos los dispositivos creados por un objeto Direct3D comparten los mismo recursos físicos, lo cual significará que si se crea varios dispositivos sobre un objeto Direct3D, el rendimiento se deteriorará enormemente a causa de compartir el mismo hardware. En primer lugar, se debe inicializar un conjunto de parámetros del dispositivo a través de una estructura concreta. Esta estructura, D3DPRESENT_PARAMETERS tiene los siguientes parámetros: typedef struct _D3DPRESENT_PARAMETERS_ { UINT BackBufferWidth, BackBufferHeight; D3DFORMAT BackBufferFormat; UINT BackBufferCount; D3DMULTISAMPLE_TYPE MultiSampleType; DWORD MultiSampleQuality; D3DSWAPEFFECT SwapEffect; HWND hDeviceWindow; BOOL Windowed; BOOL EnableAutoDepthStencil; D3DFORMAT AutoDepthStencilFormat; DWORD Flags; UINT FullScreen_RefreshRateInHz; UINT PresentationInterval; } D3DPRESENT_PARAMETERS; -
Direct3D se apoya en un conjunto de buffers (conocidos como back buffers) que usa para almacenar la imagen, trabajando de manera encadenada de forma similar a la técnica del “double buffering”. BackBufferWidth y BackBufferHeight indican el tamaño de los dichos buffers en píxeles.
-
BackBufferFormat es como almacenará la información de la imagen en los buffers. Direct3D soporta los estándares A2R10G10B10, A8R8G8B8, X8R8G8B8, A1R5G5B5, X1R5G5B5 y R5G6B5.
-
BackBufferCount indicará el número de back buffers de que dispondrá la aplicación para el proceso de intercambio. Un valor de cero es interpretado como uno
-
MultiSampleType y MultiSampleQuality configuran la técnica de “multisampling” aplicada cuando no hay intercambio de back buffers.
INGENIERÍA INFORMÁTICA, UNIVERSIDAD DE SALAMANCA - 23 -
INTRODUCCIÓN A DIRECT3D INFORMÁTICA GRÁFICA 2006/2007
VÍCTOR ROLDÁN BETANCORT RAÚL SÁNCHEZ RUIZ
-
SwapEffect nos servirá para indicar que tipo de comportamiento tendrá el intercambio de back buffers.
-
hDeviceWindow es un manejador del dispositivo de pantalla, que es el encargado de determinar la localización y el tamaño de los back buffers en pantalla.
-
Mediante windowed especificaremos si la aplicación corre bajo un sistema de ventanas o a pantalla completa.
-
EnableAutoDepthStencil hace que Direct3D maneje automáticamente los buffers de profundidad Z y Stencil. Cuando el dispositivo es creado, estos buffers son creados automáticamente, de la misma forma que son eliminados o regenerados cuando el dispositivo es reseteado.
-
AutoDepthStencilFormat establecerá el formato de los buffers automático de profundidad.
-
Flags contiene un conjunto adicional de restricciones como la desactivación de los buffers de profundidad, bloqueo de back buffers, apoyo para video…
-
FullScreen_RefreshRateInHz concreta la frecuencia de refresco de la imagen en pantalla en hercios. Depende del modo en que se esté ejecutando la aplicación (ventana o pantalla completa)
-
PresentationInterval contiene la frecuencia máxima a la que se irán mostrando los sucesivos back buffers especificados.
Por lo general, se pondrán toda la estructura a cero, para establecer las propiedades menos usadas a su valor por defecto, y luego se establece aquellas características cuya elección si es crítica. El siguiente paso es crear el dispositivo a través del método CreateDevice(): HRESULT CreateDevice( UINT Adapter, D3DDEVTYPE DeviceType, HWND hFocusWindow, DWORD BehaviorFlags, D3DPRESENT_PARAMETERS *pPresentationParameters, IDirect3DDevice9 **ppReturnedDeviceInterface ); -
Adapter indica cual de los dispositivos de vídeo (varias tarjeta gráficas) usar. Normalmente se pondrá a D3DADAPTER_DEFAULT, que siempre es el primer dispositivo.
-
DeviceType hace referencia al tipo de dispositivo usado, HAL o de referencia INGENIERÍA INFORMÁTICA, UNIVERSIDAD DE SALAMANCA - 24 -
INTRODUCCIÓN A DIRECT3D INFORMÁTICA GRÁFICA 2006/2007
VÍCTOR ROLDÁN BETANCORT RAÚL SÁNCHEZ RUIZ
-
hFocusWindow hace referencia al foco de la ventana de nuestra de aplicación. Por ejemplo, en pantalla completa, nuestra ventana debe ser la que esté más al frente, en cambio, en modo ventana de daremos el valor NULL.
-
BehaviorFlags permitirá combinar una o más opciones que controlan el proceso de creación del dispositivo. Lo normal es establecer el comportamiento del procesamiento de vértices que tendrá el dispositivo.
-
pPresentationParameters es el puntero a la estructura con los parámetros de la creación de nuestro dispositivo.
-
ppReturnedDeviceInterface se trata del puntero de salida que ubicará la dirección de nuestro interfaz con el dispositivo creado.
El siguiente ejemplo crea un dispositivo Direct3D tomando el adaptador de video por defecto, que será de tipo HAL mediante procesamiento de vértices por software. g_pD3D -> CreateDevice(D3DADAPTER_DEFAULT, D3DDEVTYPE_HAL, hWnd, D3DCREATE_SOFTWARE_VERTEXPROCESSING, &d3dpp, &d3dDevice) Una vez creado el dispositivo, se procedería a establecer su estado (activación de luces, del culling…). Direct3D proporciona algunas funciones para facilitar el proceso de detección de las capacidades del hardware presente en la máquina. La mas usada, CheckDeviceType(), permite determinar si el dispositivo está capacitado para llevar a cabo cálculos con aceleración por hardware, o si permite crear varios back buffers para la presentación de la imagen, o su capacidad para renderizar el formato de los buffers especificado. CheckDepthStencilMatch() ayuda a conocer si el dispositivo está capacitado para representar el formato de los buffers de profundidad especificado, entre otras cosas.
INGENIERÍA INFORMÁTICA, UNIVERSIDAD DE SALAMANCA - 25 -
INTRODUCCIÓN A DIRECT3D INFORMÁTICA GRÁFICA 2006/2007
VÍCTOR ROLDÁN BETANCORT RAÚL SÁNCHEZ RUIZ
11. BUFFERS DE VERTICES (VERTEX BUFFERS) Los buffers de vértices son porciones de memoria que alojan vértices (en forma de arrays que representan un vértice). Estos vértices pueden estar transformados o no, y se podrán procesar para realizar una transformación, iluminaciones, etc. Estos buffers se suelen alojar en la memoria de video, desde donde se pueden efectuar los cálculos 3D mucho más rápido. Por ejemplo, podríamos almacenar un determinado modelo, efectuar todas las transformaciones necesarias sobre el y luego representar la escena tantas veces como se desee sin tener que volver a efectuar las transformaciones. Para crear un buffer de vértices, usaremos el método de la interfaz del dispositivo IDirect3DDevice9::CreateVertexBuffer. Una primera característica importante que se comentó acerca de los conceptos 3D básicos de Direct3D es que la estructura de los vértices no es constante, sino que podemos variarla en función de la información sobre ellos que queramos alojar. Los parámetros que podemos almacenar en una estructura de vértice se especifican a través de una mascara (esta característica es conocida como Flexible Vertex Format, o FVF). Los principales parámetro son, y debiendo ser especificados en orden, los siguientes: 1. Posición del vértice: float x, float y, float z D3DFVF_XYZ 2. RJW (reciprocal of homogeneus w coordinate) solo para vértices ya transformados D3DFVF_XYZRHW 3. Normal del vertice: float x, float y, float z D3DFVF_NORMAL 4. Tamaño de punto del vértice D3DFVF_PSIZE 5. Color difuso (RGBA) D3DFVF_DIFFUSE 6. Color Specular (RGBA) D3DFVF_SPECULAR 7. Coordenada de la textura de 1 a 8 (coordenadas u y v): pueden ser especificadas 2 coordenadas (texturas 2D) mediante D3DFVF_TEXCOORDSIZE2(n) o 3 coordenadas (texturas 3D) con D3DFVF_TEXCOORDSIZE3(n). El argumento n representa el número de la coordenada. El número de coordenadas lo especificaremos con D3DFVF_TEXn, siendo n el número de estas coordenadas Estos son los principales. Un ejemplo de máscara típica sería: #define D3DFVF_CUSTOMVERTEX (D3DFVF_XYZ|D3DFVF_DIFFUSE|D3DFVF_TEX1)
INGENIERÍA INFORMÁTICA, UNIVERSIDAD DE SALAMANCA - 26 -
INTRODUCCIÓN A DIRECT3D INFORMÁTICA GRÁFICA 2006/2007
VÍCTOR ROLDÁN BETANCORT RAÚL SÁNCHEZ RUIZ
Que definiría la siguiente estructura de vértice, que también es necesario definir: typedef struct { D3DVALUE x,y,z; D3DVALUE diffuse; D3DVALUE u,v; } VERTICE;
Esta definición de vértice deberá se pasada como parámetro a la función CreateVertexBuffer para que conozca el formato de vértice usado. Este método tiene los siguientes parámetros: HRESULT CreateVertexBuffer( UINT Length, DWORD Usage, DWORD FVF, D3DPOOL Pool, IDirect3DVertexBuffer9** ppVertexBuffer, HANDLE* pSharedHandle ); -
-
Lenght: Primero se especifica el tamaño del buffer. Un ejemplo podría ser 10 * sizeof(VERTICE). Usage: Conjunto de flags que especifican como serán usados los recursos almacenados en el vértice. Por lo general, se pone a cero. FVF: Máscara con el formato del vértice. Pool: Tipo de clase de memoria donde disponer los datos. Se suele usar D3DPOOL_DEFAULT, que referencia a la memoria de vídeo o incluso dejar que Direct3D decida con D3DPOOL_MANAGED, más seguro aún. ppVertexBuffer: Aquí retorna el puntero al buffer de vértices generado. pSharedHandle: Es un parámetro reservado, poner a NULL.
Ejemplo: d3dDevice->CreateVertexBuffer(3*sizeof(VERTICE),0, D3DFVF_CUSTOMVERTEX, D3DPOOL_DEFAULT, &g_pVB, NULL) Por lo general, el uso de las llamadas a funciones en Direct3D devuelve un código de retorno que indica si ha sido válido o no u otras situaciones. Para facilitar la evaluación de dichos parámetros, en las cabeceras se ha definido unas macros para ello. Estas macros son SUCEEDED() para llamadas que resultaron satisfactorias y FAILED() para llamadas que generaron error.
INGENIERÍA INFORMÁTICA, UNIVERSIDAD DE SALAMANCA - 27 -
INTRODUCCIÓN A DIRECT3D INFORMÁTICA GRÁFICA 2006/2007
VÍCTOR ROLDÁN BETANCORT RAÚL SÁNCHEZ RUIZ
Para poder acceder al buffer, primero hay que bloquearlo. Para ello se usa la función IDirect3DVertexBuffer9::Lock, para luego rellenar el buffer con los vértices (mediante un simple memcpy en el caso de trabajar en C) o bien leer su contenido. Esta función acepta cuatro parámetros: HRESULT Lock( UINT OffsetToLock, UINT SizeToLock, VOID **ppbData, DWORD Flags ); -
OffsetToLock: contiene la dirección de comienzo a partir de la cual vamos a bloquear. Si vamos a bloquear todo el buffer, se deberá poner a cero. SizeToLock: Tamaño a bloquear a partir del offset. ppbData: Puntero al vértice (de tipo VOID) flags: Describe como se realizará el bloqueo de la memoria, mediante flags. D3DLOCK_DISCARD se usa para poner el modo de escritura y D3DLOCK_READONLY para ponerlo en modo lectura.
Pongamos por ejemplo, que se va a bloquear como escritura y a copiar todo el contenido de un array de vértices en memoria en el buffer: VOID* pVertices; if (FAILED(m_pVB->Lock(0, m_dwSizeofVertices, (BYTE**)&pVertices,0))) return E_FAIL; memcpy( pVertices, cvVertices, m_dwSizeofVertices); m_pVB->Unlock(); Como era de esperar, tras finalizar el uso de un buffer de vértices, habrá que desbloquearlo a través de IDirect3DVertexBuffer9::Unlock(). No tiene ningún parámetro.
INGENIERÍA INFORMÁTICA, UNIVERSIDAD DE SALAMANCA - 28 -
INTRODUCCIÓN A DIRECT3D INFORMÁTICA GRÁFICA 2006/2007
VÍCTOR ROLDÁN BETANCORT RAÚL SÁNCHEZ RUIZ
12. RENDERIZAR UNA ESCENA A PARTIR DE UN BUFFER DE VÉRTICES Para dibujar una escena a partir de un conjunto de vértice, en primer lugar se debe especificar la fuente de los mismos, para lo cual se usa la función IDirect3DDevice9::SetStreamSource(). HRESULT SetStreamSource( UINT StreamNumber, IDirect3DVertexBuffer9 *pStreamData, UINT OffsetInBytes, UINT Stride ); El primer parámetro, StreamNumber, indicará el número de la fuente de información, que se pone a cero casi siempre. El segundo parámetro, el puntero pStreamData, es el puntero al buffer, obtenido previo bloqueo del mismo. El tercero indica el tamaño del buffer, que no se suele indicar en la llamada, y el cuarto es el tamaño de cada componente (de cada vértice). Por ejemplo: d3dDevice->SetStreamSource(0, g_pVB, sizeof(VERTICE)); Indica que el puntero al buffer g_pVB contiene elementos del tamaño de los vértices que definimos como VERTICE. El siguientes pasos para el dibujo de una escena sería indicar el “vertex shader” a usar. Habrá que indicarle el formato del vértice con el que estamos tratando mediante el método SetVertexShader(): d3dDevice -> SetVertexShader(D3DFVF_CUSTOMVERTEX); Ya estamos en disposición de dibujar la escena. Esto se realiza a través de la función IDirect3DDevice9::DrawPrimitive, que dibuja a partir de los vértices del buffer una de las primitivas que Direct3D es capaz de renderizar. El conjunto de primitivas que Direct3D puede dibujar es el siguiente: -
D3DPT_POINTLIST: Renderiza un conjunto de puntos aislados. No puede trabajar con índices.
-
D3DPT_LINELIST: Renderiza los vertices como un conjunto de líneas aisladas, definidas mediante 2 vértices, es decir, tomará dos vértices y dibujara una línea, y a continuación los siguientes dos vértices para dibujar otra línea.
-
D3DPT_LINESTRIP: Renderiza los vertices como un conjunto de líneas continuas, es decir, los dos primeros vértices definen una línea, el siguiente, otra línea que parte del vértice anterior, y así sucesivamente.
INGENIERÍA INFORMÁTICA, UNIVERSIDAD DE SALAMANCA - 29 -
INTRODUCCIÓN A DIRECT3D INFORMÁTICA GRÁFICA 2006/2007
VÍCTOR ROLDÁN BETANCORT RAÚL SÁNCHEZ RUIZ
-
D3DPT_TRIANGLELIST: Efectuar la renderización de los vértices como un conjunto de triángulos aislados. Cada tres vértices define un triángulo.
-
D3DPT_TRIANGLESTRIP: Se renderizará un conjunto de triángulos como una tira.
-
D3DPT_TRIANGLEFAN: Renderiza los vertices como un abanico (cada vértice sucesivo definido define un triángulo con los dos vértices anteriores)
d3dDevice->DrawPrimitive(D3DPT_TRIANGLELIST, 0, 1); El primer parámetro será el tipo de primitiva, el segundo parámetro el índice del primer vértice a usar, y el último parámetro, el número de primitivas a dibujar. Lo más eficiente es definir las escenas mediante “Triangle Strips” o “Triangle Fans” porque se duplican menos vértices.
FIGURA 6: TRIANGLEFAN
INGENIERÍA INFORMÁTICA, UNIVERSIDAD DE SALAMANCA - 30 -
INTRODUCCIÓN A DIRECT3D INFORMÁTICA GRÁFICA 2006/2007
VÍCTOR ROLDÁN BETANCORT RAÚL SÁNCHEZ RUIZ
FIGURA 7: TRIANGLESTRIP
Todo el proceso de renderización se debe llevar a cabo entre la llamadas a las funciones BeginScene() y EndScene() del dispositivo, que indican a Direct3D que se va a renderizar la escena, y éste comprobará si el sistema está listo para ello también. Ejemplo: if( SUCCEEDED(d3dDevice->BeginScene())) { d3dDevice->SetStreamSource(0, m_pVB, sizeof(VERTICE)); d3dDevice->SetVertexShader(D3DFVF_CUSTOMVERTEX ); d3dDevice->DrawPrimitive(D3DPT_TRIANGLEFAN, 0, 2); d3dDevice->EndScene(); }
INGENIERÍA INFORMÁTICA, UNIVERSIDAD DE SALAMANCA - 31 -
INTRODUCCIÓN A DIRECT3D INFORMÁTICA GRÁFICA 2006/2007
VÍCTOR ROLDÁN BETANCORT RAÚL SÁNCHEZ RUIZ
13. DIBUJAR A PARTIR DE UN BUFFER DE ÍNDICES (INDEX BUFFER) En determinadas situaciones, dibujar a partir de un buffer de vértices limita mucho la renderización, pues nos vemos obligados siempre a disponer los vértices en el orden que se van a dibujar las primitivas, y eso puede complicar mucho el diseño de un modelo, y también produce una redundancia de vértices significativa. Veamos un ejemplo:
FIGURA 8: LISTA DE TRIÁNGULOS
Si dibujamos estos dos triángulos de forma independiente, definiéndola como una lista de triángulo sin índice, tendremos que manejar 6 vértices, cuando se ve claramente que estos triángulos comparten dos vértices. En cambio, si los encadenamos solo necesitaremos cuatro vértices, y el apoyo de un buffer de índices. De una forma intuitiva, el índice debería indicar “construye el primer triángulo a partir de los vértices V1, V2 y V3, y el segundo triángulo mediante V2, V3 y V4”. Mediante lista de triángulos sin índice, el buffer contendrá información duplicada, y esto produce un desaprovechamiento enorme de la memoria. El uso del buffer de vértices se puede reducir usando un buffer de índices, y un buffer de vértices más pequeño reduce el número de vértices que deben ser enviados al adaptador de vídeo, y lo que es mas importante, lograremos que se almacenen en la caché de vértices del adaptador datos que volverán a utilizar de nuevo, ya que las siguientes primitivas poseen vértices recientemente usados, por lo que el vértice se puede obtener de la caché en lugar de leerla del buffer de memoria de nuevo. Todo ello resulta en una gran mejoría del rendimiento. Sin el uso de índices, además, este tipo de diseño es mucho mas complicado, sobre todo cuando tratamos con un número considerable de triángulos. HRESULT CreateIndexBuffer( UINT Length, DWORD Usage, D3DFORMAT Format, D3DPOOL Pool, IDirect3DIndexBuffer9** ppIndexBuffer, HANDLE* pSharedHandle ); La creación de un buffer de índices exige de una serie de parámetros que defina sus características. INGENIERÍA INFORMÁTICA, UNIVERSIDAD DE SALAMANCA - 32 -
INTRODUCCIÓN A DIRECT3D INFORMÁTICA GRÁFICA 2006/2007
VÍCTOR ROLDÁN BETANCORT RAÚL SÁNCHEZ RUIZ
Length indica el tamaño que tendrá el buffer, en bytes. Usage concreta el uso que se le dará al buffer, concretando determinadas compatibilidades, por ejemplo, que el buffer sea usado por software en lugar de hardware (D3DUSAGE_SOFTWAREPROCESSING). Format indica el tipo de dato que será el índice. Al igual que con el buffer de vértices, habrá que especificar donde y como se alojará la información de los índices, a través de Pool, para lo cual se recomienda relegar esa decisión a Direct3D con D3DPOOL_MANAGED. ppIndexBuffer será nuestro puntero al buffer, y el último parámetro es nuevamente reservado para el sistema. Una vez creado, el acceso al buffer exigirá de nuevo el uso de los métodos Lock() y Unlock() para bloquearlo y poder acceder a el, de forma similar a como hacíamos con el buffer de vértices. Veamos un ejemplo, en el cual se crear un índice, posteriormente se bloquea y se copia sobre el los valores de los índices, y luego se desbloquea: LPDIRECT3DINDEXBUFFER9 m_pIB; WORD dwIndices[] = {0, 1, 2, 0, 2, 3}; m_dwSizeofIndices = sizeof(dwIndices); if(FAILED(d3dDevice->CreateIndexBuffer(m_dwSizeofIndices, 0, D3DFMT_INDEX16, D3DPOOL_MANAGED, &m_pIB ) ) ) return E_FAIL; VOID* pIndices; if(FAILED(m_pIB->Lock( 0, m_dwSizeofIndices, (BYTE**)&pIndices, 0))) return E_FAIL; memcpy( pIndices, dwIndices, sizeof(dwIndices) ); m_pIB->Unlock(); Ahora, para dibujar las primitivas, habrá que establecer en primer lugar el índice que estamos usando mediante SetIndices(), y en segundo lugar sustituir el uso de la función DrawPrimitive() por DrawIndexedPrimitive(). En lo referente a presentar una escena, en determinadas ocasiones necesitaremos forzar inmediatamente a presentar la imagen. Para ello usaremos el método Present() del dispositivo de Direct3D, que forzará a intercambiar con el siguiente buffer dentro de la secuencia de back buffers definida. La llamada habitual, a no ser que hayamos configurado características especiales en el buffer, será: g_pd3dDevice->Present(NULL, NULL, NULL, NULL);
INGENIERÍA INFORMÁTICA, UNIVERSIDAD DE SALAMANCA - 33 -
INTRODUCCIÓN A DIRECT3D INFORMÁTICA GRÁFICA 2006/2007
VÍCTOR ROLDÁN BETANCORT RAÚL SÁNCHEZ RUIZ
14. CONCLUSIÓN FINAL Y COMPARATIVA ENTRE DIRECT3D Y OPENGL Este documento ha pretendido servir para entender las principales características de Direct3D, así como sus principios básicos. Mucha teoría existe en torno a los gráficos 3D, pero se ha tratado abarcar lo fundamental en la medida de lo posible: conocer como funciona este API sirve como punto de partida al enorme y siempre en crecimiento mundo de los gráficos 3D. Hoy en día Direct3D resulta un pilar básico en la programación de gráficos 3D en aplicaciones y juegos. Antiguamente, se consideraba muy malo comparado con el API dominante, OpenGL. Sin embargo, las continuas evoluciones sufridas han hecho de Direct3D una API muy fuerte y estable. Existe una idea generalizada de que Direct3D es el estándar de gráficos para plataformas Windows. Esto no es cierto, ya que gran cantidad de aplicaciones han sido desarrolladas en APIs como OpenGL o Glide. Sin embargo, si se puede decir que Microsoft trabaja muy cerca de los fabricantes de tarjetas gráficas, a causa de su posición dominante en el mundo de los computadores. Esto no proporciona la garantía de que toda nueva característica que surjan en la evolución del hardware gráfico será siempre soportado por Direct3D, de hecho, muchas veces Direct3D dispone de nuevas características antes de que las tarjetas gráficas lo hagan. Todo ello hace de Direct3D un API ideal para aquellos que deseen escoger un sistema fuerte y con continuo soporte ante la rápida evolución del hardware gráfico, con la garantía de que funcionará con un sistema operativo muy comprometido con las aplicaciones y juegos 3D como es Microsoft Windows. Como características principales de Direct3D, hay que recalcar la estructura basada en objetos COM y la integración de la capa HAL. El HAL, como ya se ha comentado, nos permite abstraernos de las peculiaridades de cada fabricante de hardware gráfico y centrarnos en lo que interesa al programador: las capacidades de aceleración hardware que posee el adaptador. Esto supuso un paso importante en la programación de gráficos 3D. Gracias al uso del COM, Microsoft ha garantizado un sistema que a pesar de estar en continua evolución, garantizará el funcionamiento de aplicaciones desarrolladas con versiones antiguas del API. Sin embargo, su uso puede asustar al comienzo, ya que las cabeceras COM no son precisamente sencillas de entender, aunque el paso del tiempo han permitido que los desarrolladores tengan a su alcance una estructura y sintaxis de programación mas intuitiva y sencilla. Además, aquellos desarrolladores acostumbrados a la programación orientada a objetos se sentirán cómodos con Direct3D. Otra ventaja que proporciona Direct3D, es que gracias al modelo COM, es independiente del lenguaje, pues los objetos COM son simples librerías de código binario: simplemente se necesita unas cabeceras de definición y un conjunto de punteros a métodos. Sin embargo, en contradicción con esta ventaja, Direct3D no funcionará en otras plataformas que no sea Microsoft Windows.
INGENIERÍA INFORMÁTICA, UNIVERSIDAD DE SALAMANCA - 34 -
INTRODUCCIÓN A DIRECT3D INFORMÁTICA GRÁFICA 2006/2007
VÍCTOR ROLDÁN BETANCORT RAÚL SÁNCHEZ RUIZ
Todo esto conduce a que la programación ideal de gráficos 3D en Windows debería hacerse mediante Direct3D, y no se trata de una idea descabellada, pues es obvio que un API desarrollado por Microsoft funcione mejor en su plataforma que otros. Y lo cierto es que Direct3D hace las cosas muy bien, y a causa de estar diseñado para Windows, lo hace ideal para computadores muy variados, desde sistemas de pocos recursos hasta computadores con lo último de hardware gráfico. Los profesionales de los gráficos 3D preferirán una plataforma UNIX o de Sillicon Graphics. Comentar también que no hay nada que Direct3D haga y OpenGL no pueda hacer, pero tienen una cierta ventaja a causa de que las últimas características del hardware gráfico necesitarán código personalizado para cada adaptador en OpenGL, mientras que Direct3D ya estará implementado. No obstante, Direct3D resultará más complicado a la hora de programar, porque no oculta totalmente los elementos de bajo nivel como hace OpenGL, pero esto también desemboca en una pérdida de flexibilidad a la hora de programar. Será el programador el que deba decidir entre flexibilidad o simplicidad. En lo referente a flexibilidad, cabe destacar las dos mayores fuerzas de Direct3D en lo que a programación se refiere: “Programable Píxel & Vertex Shaders”, porciones de código propio similar al ensamblador que se inyectan en la tubería de renderización para procesar los vértices y píxeles finales que se mostrarán en pantalla. Las desventajas principales podrían ser: - DirectX se actualiza cada año o más, lo cual es relativamente lento frente a la rápida evolución del hardware gráfico. - Direct3D requiere mucho más código para inicializarse. En versiones anteriores a la 8, era necesario de en torno a 800 líneas de código. Hoy en día, esto se ha reducido a 200, que aun sigue siendo considerablemente grande. - Direct3D requerirá un buen conocimiento de cómo funciona, por lo que es mucho más complicado de aprender a usar que OpenGL. - No es portable - No es de estándar abierto, por lo que Microsoft es el único que puede implementarlo en sus sistema - Programarlo en C puede resultar muy complicado debido a que es necesario acceder a los objetos COM a través de las V-Tables. Frente a esto se encuentra OpenGL, relativamente sencillo de aprender, altamente portable, muy eficiente y fuerte también en cualquier plataforma, de estándar abierto (cualquiera puede comprar una licencia e implementar OpenGL en su plataforma), y su desarrollo representa un amplio rango de intereses proveniente de muchas empresas, en contra de la política de Microsoft, lo cual le aportará una mayor flexibilidad. Además, es reconocido como el estándar de la industria de los gráficos, como en el campo del modelado 3D profesional, uso militar, CAD, etc, salvo en el campo de los juegos y aplicaciones, donde Direct3D ofrece una fuerte competencia. INGENIERÍA INFORMÁTICA, UNIVERSIDAD DE SALAMANCA - 35 -
INTRODUCCIÓN A DIRECT3D INFORMÁTICA GRÁFICA 2006/2007
VÍCTOR ROLDÁN BETANCORT RAÚL SÁNCHEZ RUIZ
Una de las principales dificultades que presenta son las extensiones, que son muy dependientes de hardware especifico, o también su elevado número de nombres diferentes para una misma función (Direct3D aprovecha la sobrecarga de nombres de función propias de la programación orientada a objetos). Tampoco tiene mucho soporte para todos los lenguajes de programación. La programación de gráficos 3D en sí es muy complicada, y comúnmente las trabas que se encuentran a lo largo del aprendizaje se atribuyen al API escogido, cuando lo cierto es q a la larga ambos interfaces son igual de complicados. Si se desea aprender a programar gráficos 3D, cualquiera de los dos APIs es una buena elección, y en cualquier momento se podrá aprender a usar el otro, ya que las bases son las mismas. Quizás superior complejidad de Direct3D sea a causa de su inicialización: a partir de superar esa barrera, el funcionamiento será igual al de OpenGL. Feature
OpenGL 1.2 Core
Direct3D 7
Direct3D 8
System Mechanics
Operating System Support
Windows (9x, NT, Windows (9x, 2000, 2000), MacOS, BeOS, CE) *nix, others
Windows (9x, 2000)
API Definition Control OpenGL ARB
Microsoft
Microsoft
API Specification
OpenGL Specification
SDK/DDK Documentation and DDK Reference
SDK Documentation
API Mechanism
includes and libraries
COM
COM
Software Emulation of Yes Unaccelerated Features
No
No
Extension Mechanism
No
Yes
Yes
No
Fixed-Function Vertex No Blending
Yes
Yes
Programmable Vertex Blending
No
No
Yes
Parametric Curve Primitives
Yes
No
Yes
Parametric Surface Primitives
Yes
No
Yes
Hierarchical Display Lists
Yes
No
No
Yes
Source Implementation Yes Available Modeling
Rendering
Two-sided Lighting
Yes
No
No
Point Size Rendering Attributes
Yes
No
Yes
INGENIERÍA INFORMÁTICA, UNIVERSIDAD DE SALAMANCA - 36 -
INTRODUCCIÓN A DIRECT3D INFORMÁTICA GRÁFICA 2006/2007
VÍCTOR ROLDÁN BETANCORT RAÚL SÁNCHEZ RUIZ
Line Width Rendering Attributes
Yes
No
No
Programmable Pixel Shading
No
No
Yes
Triadic Texture Blending Operations
No
No
Yes
Cube Environment Mapping
No
Yes
Yes
Volume Textures
Yes
No
Yes
Multitexture Cascade
No
Yes
Yes
Texture Temporary Result Register
No
No
Yes
Mirror Texture Addressing
No
Yes
Yes
Texture "Wrapping"
No
Yes
Yes
Range-Based Fog
No
Yes
Yes
Bump Mapping
No
Yes
Yes
Modulate 2X Texture Blend
No
Yes
Yes
Modulate 4X Texture Blend
No
Yes
Yes
Add Signed Texture Blend
No
Yes
Yes
Frame Buffer
Hardware Independent Yes Z Buffer Access
No
No
Full-Screen Antialiasing
Yes
Yes
Yes
Motion Blur
Yes
No
Yes
Depth of Field
Yes
No
Yes
Accumulation Buffers
Yes
No
No
Miscellaneous
Picking Support
Yes
No
Yes
Yes
Yes
No
No
Multiple Monitor Support No Stereo Rendering Yes
FIGURA 9: “DIRECT3D VS. OPENGL: A COMPARISON”, EXTRAIDO DE HTTP://WWW.XMISSION.COM/~LEGALIZE/D3D-VS-OPENGL.HTML INGENIERÍA INFORMÁTICA, UNIVERSIDAD DE SALAMANCA - 37 -
INTRODUCCIÓN A DIRECT3D INFORMÁTICA GRÁFICA 2006/2007
VÍCTOR ROLDÁN BETANCORT RAÚL SÁNCHEZ RUIZ
15. BIBLIOGRAFÍA -
Microsoft DirectX 9.0 SDK Update (April 2005) Documentation
-
Artículo: Direct3D vs. OpenGL: Which API to Use When, Where, and Why by Promit Roy, GameDev.net
-
Direct3D vs. OpenGL: A Comparison, http://www.xmission.com/~legalize/d3d-vsopengl.html
-
Beginning Direct3D By Vahid Kazemi,www.codeproject.com/directx/d3d_tut1.asp
-
Cutting Edge Direct 3D Programming (Publisher: The Coriolis Group) Author(s): Stan Trujillo ISBN: 1576100502 Publication Date: 11/01/96
-
Beginning Direct3D Game Programming, Second Edition 2003, Wolfgang F. Engel, Premier Press. ISBN: 193184139x
-
Tutoriales de Direct3D by Corday, 24 Octubre del 2001, basado en los textos de Wolfgang F. Engel, http://www.geocities.com/cordayuk/Direct3D/
-
http://www.programmersheaven.com/
-
http://www.gamedev.net/
-
http://www.chilehardware.com/guias_guia067-20061024.html
-
Apuntes de Informática Gráfica de la Ingeniería Informática de la Universidad de Salamanca creados por Dr. Juan Manuel Corchado.
INGENIERÍA INFORMÁTICA, UNIVERSIDAD DE SALAMANCA - 38 -