DirectX desembarca en Linux

Publicado el Jueves 7 octubre 2010 en DirectX,Miniposts,Noticias por Thund

Parece ser que el grupo de desarrollo freedesktop.org (los mismos que están detrás de VMWare) ha conseguido hacer andar las APIs de DirectX 10 y 11 en sistemas Linux, utilizando una capa intermedia, a la que llaman Gallium, situada entre las interfaces de las librerías y los drivers de las tarjetas gráficas. No se trata de una emulación como venía haciendo Wine, transformando DirectX en OpenGL, sino que han implementado la API de DirectX al completo para integrarla con la arquitectura de Gallium, evitando así sensibles pérdidas de rendimiento. Aún faltará mucho, presumo, para que los juegos de hoy en día funcionen al 100% sobre esta plataforma, pero se ha abierto la primera brecha en un muro que parecía demasiado sólido como para que mereciera la pena atravesarlo. ¿Se ejecutarán los juegos mucho más rápido en Linux? ¿Acabará la industria de los videojuegos provocando la emigración del usuario medio hacia tierras más frías, donde habiten los pingüinos? ¿Conquistarán el mercado de OpenGL? ¿Cómo habrá sentado la noticia a los de Redmond? El tiempo dirá…

Enlaces de interés

Artículo en Phoronix (del cual se ha hecho eco todo Dios :D )

Etiquetas: , ,

Quimera Engine

Publicado el Lunes 27 septiembre 2010 en DirectX,Noticias,Videojuegos por Thund

Si alguno de aquí leyó el primer post de este blog, sabrá de mi propensión al fracaso en cuanto a proyectos en solitario se refiere, por distintos motivos. Así que un día me pregunté: ¿y si cambio el punto de partida? ¿Y si dejo de avanzar solo? Decidido a probar suerte, sin mucha esperanza, la verdad, repartí anuncios por distintas webs y foros relacionadas con C++ y videojuegos, buscando profesionales con tiempo libre para dedicarlo a un proyecto de una envergadura considerable, aunque no inviable: Un motor genérico de videojuegos.

Al tiempo (meses), fueron contestando algunas personas interesadas en participar, sin ánimo de lucro, en un proyecto del que aún no estaban diseñados ni los cimientos. Fueron pocos, aunque repartidos por toda España. Algunos aportaban especialización en las tecnologías necesarias, otros ofrecían una gran experiencia laboral en ámbitos relacionados. Era consciente de que encontrar gente que tuviera de antemano los conocimientos deseados era casi imposible, y menos aún en este país; sin embargo, estas personas venían con ganas, y eso me pareció suficiente para que fueran capaces de autoformarse sobre la marcha (de hecho, así me lo han demostrado).

Cuando conseguí reclutar a un número suficiente como para que, aun descolgándose alguien, pudiéramos avanzar a ritmo satisfactorio (esto es, una velocidad de producción que mantuviera la moral de todos), tuvimos la primera reunión, nos presentamos y sentamos las bases de lo que haríamos en adelante. Tras dedicar un tiempo prudencial a la autoformación del equipo, repartimos las primeras tareas. Durante el proceso, efectivamente, algunos integrantes abandonaron la formación por motivos personales, así que ahora cuento con sólo unos pocos kamikazes valientes para materializar esta dura obra. Será difícil no caer en la inmensa cantidad de trampas que toda empresa lleva consigo, pero lo haré, lo haremos, lo mejor que sepamos.

Como no me gusta escribir lo mismo dos veces, aquí os copio la descripción del proyecto, al que llamamos Quimera Engine:

Quimera Engine es un ambicioso proyecto iniciado en 2010 por un reducido grupo de desarrolladores, autodenominados Kinesis Team, decididos a desarrollar el primer motor genérico de videojuegos de origen español, totalmente gratuito.

No se trata de un videojuego, un motor para un tipo de videojuego, un motor gráfico ni una herramienta visual de diseño de videojuegos (por ahora), sino de un conjunto de librerías escritas en C++ que proveen de la infraestructura necesaria para que un programador de videojuegos no tenga que preocuparse del hardware, el sistema operativo o las lilbrerías de bajo nivel. Se pretende que el diseño posibilite la máxima modularización y facilidad de uso y ampliación, siempre que no suponga un escollo para el rendimiento.

El motor abarca todos los ámbitos que conforman un videojuego: gráficos 2D y 3D, sonido, comunicaciones, dispositivos de entrada / salida, carga de contenidos, detección de colisiones, etc. La implementación de cada componente es personalizable, aunque originalmente se ofrecen las más comunes (por ejemplo, para el módulo de gráficos, puede elegirse de entrada si se quiere utilizar OpenGL 4 o DirectX 11, o puede ser implementado por el usario utilizando Ogre3D).

Quimera Engine es portable para Windows, Linux y Mac OS X, y por tanto para las plataformas PC y Mac. Las consolas y los dispositivos móviles no están, por el momento, al alcance, aunque sí cabe la posibilidad a medio-largo plazo de un port para NaCl.

Quimera Engine es Open Source y totalmente gratuito, tanto para uso personal como comercial, bajo licencia LGPL, por lo que es ideal para estudios sin recursos o desarrolladores indie.

Aunque el proyecto sea impulsado por españoles, no se pretende que la comunidad de usuarios se circunscriba únicamente a España. Todo lo contrario, Kinesis Team ofrece soporte a nivel internacional en inglés, como cualquier otro grupo de desarrollo, pero contando además con la ventaja de poder comunicarse de una manera más directa y eficaz con usuarios hispanohablantes.

Por último, hacemos un llamamiento a programadores españoles para que colaboren con nosotros de forma activa, programando y diseñando, o de forma pasiva, a modo de consultores. Podéis contactar con nosotros aquí.

Kinesis

Kinesis es el nombre que elegimos para el grupo de desarrollo. A mí me gusta, denota movimiento. Tal y como reza en el párrafo anterior, seguimos buscando profesionales interesados en colaborar en algo que creo que nos enriquecerá a todos. De hecho, y hablo al menos por mí, ya lo está haciendo.

Cualquier información que preciséis no tenéis más que preguntarla, bien en los comentarios del post o por correo, suelo responder rápido.

Web de desarrollo de Quimera Engine

Un vértice, dos vértices, tres vértices ¡coño un triángulo!

Publicado el Sábado 25 septiembre 2010 en DirectX por Thund

Ha pasado más tiempo del que me gustaría desde que escribí el anterior post sobre la inicialización de Direct3D, pero es que hacía mucha calor para ponerme a redactar… espero me comprendan, ejem. De hecho, el código expuesto aquí lleva escrito desde hace un mes. Pero vamos al lío, que no hay tiempo que perder. En éste post mostraré el tortuoso camino hacia la visualización de elementos geométricos, desde donde lo dejamos.

Hasta ahora teníamos 4 interfaces de objetos correctamente inicializados: ID3D11Device, IDXGISwapChain, ID3D11DeviceContext e ID3D11RenderTarget. Esto era suficiente para poder arrancar la maquinaria de Direct3D y rellenar el área de cliente de la ventana con un color para probarlo; sin embargo, en el paso 4, que era el último, sería lógico incluir algo que necesitaremos en adelante, que es la asociación del render target al device context:

pImmediateContext->OMSetRenderTargets(1, &pRenderTargetView, NULL);

Paso 5: El ojo de buey

Es necesario definir sobre qué porción de nuestra ventana será proyectado el front buffer. Antes se utilizaba la estructura D3DVIEWPORT9 para empaquetar los parámetros del viewport, ahora utilizamos D3D11_VIEWPORT, que ni quita ni pone nada nuevo.

D3D11_VIEWPORT viewport;
viewport.Height   = 600.0f;
viewport.Width    = 800.0f;
viewport.TopLeftX = 0.0f;
viewport.TopLeftY = 0.0f;
viewport.MaxDepth = 1.0f;
viewport.MinDepth = 0.0f;

pImmediateContext->RSSetViewports(1, &viewport);

Paso 6: El átomo

Como bien sabe el lector, el universo tridimensional que vemos por pantalla está formado en su inmensa mayoría por vértices que, ordenados de cierta manera, definen superficies triangulares que, a su vez, pueden formar mallas. Toda la parafernalia geométrica puede encontrarse fácilmente en Internet y no es el objeto de éste blog por el momento, así que me lo salto. El hecho es que, tal como ocurre con los átomos tradicionales, no todos los vértices son iguales; podemos definir un vértice al cual afecte la luz, que pueda ser envuelto por una textura o que sea representado por un color determinado. O podemos diseñar otro para el cual las anteriores propiedades no tengan sentido.

struct MyVertexType
{
    XMFLOAT4 position;
    XMFLOAT4 color;
};

D3D11_INPUT_ELEMENT_DESC arElements[] =
{
    { "POSITION", 0, DXGI_FORMAT_R32G32B32A32_FLOAT, 0, D3D11_APPEND_ALIGNED_ELEMENT, D3D11_INPUT_PER_VERTEX_DATA, 0 },
    { "COLOR", 0, DXGI_FORMAT_R32G32B32A32_FLOAT, 0, D3D11_APPEND_ALIGNED_ELEMENT, D3D11_INPUT_PER_VERTEX_DATA, 0 },
};

Lo único que hacemos aquí es declarar nuestro tipo de vértice, como hacíamos con DirectX 9. No, no es así de simple, hay un montón de cosas que explicar. Empezaré por la primera estructura, en la que aparece un nuevo "tipo básico" de DirectX llamado XMFLOAT4. Se trata de una de las muchas nuevas estructuras matemáticas añadidas a la API mediante la librería XNAMath. ¿Y de dónde sale XNA si lo que vamos a usar es DirectX11 a pelo? Pues se trata  de un intento por unificar parte de las APIs de ambas plataformas, aumentando la portabilidad entre PC y XBox; aunque aún no han marcado como deprecated los antiguos tipos, creo que es preferible ir migrando desde ya en lugar de hacer adaptaciones posteriores. Para añadir la librería a nuestro código hay que incluir el archivo "xnamath.h". En éste caso, XMFLOAT4 vendría a sustituir a D3DXVECTOR4.

La siguiente estructura es también muy similar a lo que estábamos acostumbrados, usamos el tipo D3D11_INPUT_ELEMENT_DESC en lugar de D3DVERTEXELEMENT9. Nótese cómo Microsoft ha optado por un nombrado más legible. El contenido de este nuevo tipo ha cambiado bastante, y para verlo mejor, observemos las diferencias:

typedef struct D3DVERTEXELEMENT9 {
  WORD Stream;
  WORD Offset;
  BYTE Type;
  BYTE Method;
  BYTE Usage;
  BYTE UsageIndex;
} D3DVERTEXELEMENT9, *LPD3DVERTEXELEMENT9;
typedef struct D3D11_INPUT_ELEMENT_DESC {
  LPCSTR                     SemanticName;
  UINT                       SemanticIndex;
  DXGI_FORMAT                Format;
  UINT                       InputSlot;
  UINT                       AlignedByteOffset;
  D3D11_INPUT_CLASSIFICATION InputSlotClass;
  UINT                       InstanceDataStepRate;
} D3D11_INPUT_ELEMENT_DESC;

Antes especificábamos el índice del stream que iba a contener vértices con tal elemento, el desfase de bytes respecto al inicio de la estructura, el tipo del elemento (vector de 3 floats, un entero, etc.), la interpretación del tesselator, la semántica del elemento en los shaders (elegido de un enumerado) y el índice del elemento para esa semántica. Ahora nos olvidamos de los streams y nos centramos únicamente en lo que respecta al formato de entrada a los shaders: determinamos la semántica del elemento en los shaders mediante una cadena, y su índice, por si hay más de un elemento con la misma; a continuación pasamos el formato del elemento, tal y como queremos que sea interpretado por los shaders (dense cuenta de que es el mismo enumerado de la API de DXGI que usamos anteriormente). El hueco de entrada (input slot) es un índice que determina por qué "puerto" del Input Assembler entrará la información de los vértices, de manera que, si ponemos los datos de posición (vectores de 3 componentes, por ejemplo) en un buffer de vértices y los datos de color (un entero) en otro buffer, podamos introducirlos por distintos slots y obtener como entrada al vertex shader una estructura con ambos datos ensamblados; ya veremos un ejemplo de esto en el futuro. Lo siguiente es especificar cuántos bytes de desfase hay entre el elemento actual, según su formato, respecto al precedente; lo normal aquí es usar la constante D3D11_APPEND_ALIGNED_ELEMENT que, como puede deducirse, pone el elemento justo después del anterior. Quisiera apuntar que el valor de este dato se calcula respecto al input slot al que pertenece el elemento, pudiendo tener 2 elementos con desfase cero si son los primeros de 2 slots distintos. Los 2 datos restantes está relacionados con el concepto de instanciación, disponible desde DirectX 10, al que dedicaré un post en su momento.

Alguien puede pensar que la declaración del tipo de vértice está incompleta, ¿dónde está la interfaz IDirect3DVertexDeclaration9? Ha dejado de existir, ahora lo que se crean son una especie de objetos intermediarios (layouts) que relacionan el array de la declaración con la firma de un shader determinado, como veremos más adelante. A partir de DirectX 10 ya no tenemos la fixed function pipeline, así que no es posible especificar el formato de un vértice mediante constantes D3DFVF, hay que seguir forzosamente ésta metodología. Otra cosa que falta a la declaración es el uso de la macro D3DDECL_END() como último elemento, que ya no es necesaria.

Paso 7: El cristal con que miramos

Ahora empieza la parte terrorífica de la historia, es hora de hablar de los… ¡shaders! Pero que nadie se achante, en realidad son como los dobermanns, acojonan al principio pero cuando los conoces pueden ser tus mejores amigos (vaya comparación de mierda :D ). El primer bicho con el que trataremos es la interfaz ID3DBlob, que encapsula un trozo de memoria de tamaño determinado, sin estructura definida. El uso que le daremos en esta ocasión será como recipiente de un shader compilado y como salida de los mensajes de error de la compilación. No voy a utilizar el Effects Framework todavía sino las funciones globales de que nos provee DirectX 11. Por tanto, para compilar un shader escrito en HLSL en un archivo de texto, llamaré a D3DXCompileShaderFromFile… ah no, que ahora se llama D3DX11CompileFromFile. Las diferencias entre una y otra función son ínfimas, quizá lo más destacable sea la posibilidad de realizar la compilación en un thread distinto mediante el uso de la interfaz ID3DX11ThreadPump, que no explicaré aquí, pero que según tengo entendido consume más de lo que ahorra. Para tener acceso a tal función hay que incluir la cabecera "D3DX11async.h".

UINT shaderFlags = D3D10_SHADER_DEBUG | D3D10_SHADER_SKIP_OPTIMIZATION | D3D10_SHADER_ENABLE_STRICTNESS;

ID3DBlob* pBlobVS = NULL;
ID3DBlob* pErrorBlobVS = NULL;
D3DX11CompileFromFile( L"MyEffect.fx", NULL, NULL, "vs_main", "vs_4_0", shaderFlags, NULL, NULL, &pBlobVS, &pErrorBlobVS, NULL );

ID3DBlob* pBlobPS = NULL;
ID3DBlob* pErrorBlobPS = NULL;
D3DX11CompileFromFile( L"MyEffect.fx", NULL, NULL, "ps_main", "ps_4_0", shaderFlags, NULL, NULL, &pBlobPS, &pErrorBlobPS, NULL );

Al igual que con las versiones anteriores, especificamos la ruta (relativa o absoluta) del archivo de texto, las macros del shader (que aquí no usamos), los trozos de shader a incluir (que tampoco necesitamos), el nombre de la función principal del shader y la versión del Shader Model para la cual se compilará. A continuación, se especifican los flags de compilación, valores constantes heredados de DirectX 10 (con prefijo D3D10_SHADER_) que se pueden combinar para modificar el comportamiento del compilador de shaders; aquí hemos usado, por ejemplo, D3D10_SHADER_DEBUG para generar información de depuración que sirva a un depurador, como el de PIX, para mostrar el código HLSL directamente, en lugar del código ensamblador; D3D10_SHADER_SKIP_OPTIMIZATION evita que el compilador realice optimizaciones, lo cual provocaría que el código HLSL visto en el depurador no concordara con el generado; finalmente, el flag D3D10_SHADER_ENABLE_STRICTNESS nos obligará a utilizar la sintaxis de la versión 4.0 y posteriores del Shader Model. Lo siguiente que se nos pide es una combinación de flags para efectos (no ponemos nada), un puntero a la interfaz del thread antes mencionada y variables de salida, de las que sólo cabe destacar la última, pHResult, que debe apuntar a una zona válida de memoria para almacenar el resultado de la operación llevada a cabo en el otro thread, siempre y cuando se haya especificado la susodicha interfaz. Otro NULL al bote.

Para poder insertar nuestros shaders en la pipeline, primero tenemos que encapsularlos en sendas interfaces ID3D11VertexShader y ID3D11PixelShader (antes llamadas IDirect3DVertexShader9 y IDirect3DPixelShader9, respectivamente). La única información que hay que pasar al dispositivo para crear los objetos es el punto de inicio del buffer que contiene el resultado de la compilación y su longitud. A continuación se asocian al contexto encargado de dibujar nuestro polígono.

ID3D11VertexShader* pVS = NULL;
pDevice->CreateVertexShader(pBlobVS->GetBufferPointer(), pBlobVS->GetBufferSize(), NULL, &pVS);
pImmediateContext->VSSetShader(pVS, NULL, 0);

ID3D11PixelShader* pPS = NULL;
pDevice->CreatePixelShader(pBlobPS->GetBufferPointer(), pBlobPS->GetBufferSize(), NULL, &pPS);
pImmediateContext->PSSetShader(pPS, NULL, 0);

Ya tenemos nuestros shaders cargados en memoria, compilados y preparados para ser ejecutados por el mecanismo de DirectX para representar nuestra geometría con el aspecto que más nos guste. Alto, ¿cómo sabe DirectX qué tipo de datos va a enviar al Input Assembler para que los shaders los procesen? ¿Cómo deben ser transformados aquéllos para que coincidan con los esperados?

Paso 8: Las gallinas que entran por las que van saliendo, o no

Como mencioné anteriormente, en DirectX 11 se usan unos intermediarios que actúan como adaptadores entre los shaders, el Input Assembler (por cierto, aunque ya lo expliraré, el Input Assembler es una de las fases (stage) de la pipeline, concretamente la de entrada) y los datos de entrada, que llegan en forma de paquetes de vértices. Estos adaptadores van encapsulados en interfaces ID3D11InputLayout y están sujetos a la firma de un shader y al formato de los datos de entrada, información que se transfiere al Input Assembler.

ID3D11InputLayout* pMyInputLayout = NULL;
pDevice->CreateInputLayout(arElements, sizeof(arElements) / sizeof(arElements[0]), pBlobVS->GetBufferPointer(), pBlobVS->GetBufferSize(), &pMyInputLayout);
pImmediateContext->IASetInputLayout(pMyInputLayout);

Tal y como puede verse claramente, se utilizan la declaración del tipo de datos de nuestro vértice y el resultado de la compilación del vertex shader (especificando sus puntos de inicio y su tamaño en memoria). Acto seguido se asocia el nuevo layout con el Input Assembler por medio del método IASetInputLayout (es obvio de dónde le viene el prefijo, veremos que han usado tal convención de nombres para todos los métodos relacionados directamente con ciertas fases de la pipeline).

Paso 9: Un pino, dos pinos, tres pinos… ¡coño un pinar!

Sólo resta meter datos en la tubería para que sean procesados por la maquinaria que acabamos de ajustar. Primero creamos una lista de 3 vértices cuyas coordenadas en el espacio proyectado sobre el viewport forman un triángulo (no vamos a aplicar transformaciones aún). Tal información irá empaquetada en un buffer cuya estructura debemos describir usando el tipo D3D11_BUFFER_DESC.

MyVertexType vertices[3];
vertices[0].position = XMFLOAT4(0.0f, 0.5f, 0.0f, 1.0f);
vertices[1].position = XMFLOAT4(0.5f, -0.5f, 0.0f, 1.0f);
vertices[2].position = XMFLOAT4(-0.5f, -0.5f, 0.0f, 1.0f);
vertices[0].color = vertices[1].color = vertices[2].color = XMFLOAT4(1.0f, 1.0f, 1.0f, 1.0f);

D3D11_BUFFER_DESC bufferDesc;
bufferDesc.Usage = D3D11_USAGE_DEFAULT;
bufferDesc.ByteWidth = sizeof( vertices );
bufferDesc.BindFlags = D3D11_BIND_VERTEX_BUFFER;
bufferDesc.CPUAccessFlags = 0;
bufferDesc.MiscFlags = 0;

Antes de continuar, quisiera remarcar que el buffer que estamos definiendo es tratado por DirectX 11 como un recurso, al igual que las texturas (sus interfaces heredan de ID3D11Resource), por lo que la manera de crearlos y parametrizarlos es muy similar, basta observar cómo se repiten determinados campos en las estructuras D3D11_BUFFER_DESC, D3D11_TEXTURE1D_DESC, D3D11_TEXTURE2D_DESC y D3D11_TEXTURE3D_DESC. En DirectX 9 ocurría igual, aunque ahora se ha simplificado o unificado las opciones. Un ejemplo de esto era el parámetro de tipo D3DPOOL en el que especificábamos dónde alojar el recurso (memoria de sistema, de la tarjeta gráfica o administrado por DirectX), o la cantidad de tipos de uso, que ahora se ve reducida de 16 opciones a 4. También hay que mencionar la pérdida de las interfaces IDirect3DVertexBuffer9 e IDirect3DIndexBuffer9, cuyas responsabilidades son ahora cubiertas por ID3D11Buffer, según el flag de enlazado (bind flag).

Prosigamos. Rellenar la estructura es sencillo, como puede verse. Primero especificamos el uso (acceso de lectura / escritura por parte de la CPU y la GPU); luego el tamaño de nuestra lista de vértices; seguidamente el flag de enlazado, que en este caso indica que nuestro buffer actuará como un "vertex buffer"; no necesitamos acceder al recurso desde la CPU en esta ocasión, por lo que pasamos un cero, igual que para los flags especiales.

D3D11_SUBRESOURCE_DATA initialData;
initialData.pSysMem = vertices;
initialData.SysMemPitch = 0;
initialData.SysMemSlicePitch = 0;

ID3D11Buffer* pBuffer = NULL;
pDevice->CreateBuffer( &bufferDesc, &initialData, &pBuffer );

Para crear el buffer tenemos que "empaquetar" los datos en oootra esctructura (nótese mi jartura, con jota), D3D11_SUBRESOURCE_DATA, a la que pasamos un puntero a nuestra lista de vértices. Los otros 2 campos no son necesarios dado que no estamos tratando con una textura. De este modo, tenemos por un lado los datos y por otro su forma. Los usamos en el método CreateBuffer y hala, ya tenemos un dichoso buffer.

Como colofón, pasamos nuestro recién armado vertex buffer como entrada del Input Assembler:

UINT stride = sizeof( MyVertexType );
UINT offset = 0;
pImmediateContext->IASetVertexBuffers( 0, 1, &pBuffer, &stride, &offset );
pImmediateContext->IASetPrimitiveTopology( D3D11_PRIMITIVE_TOPOLOGY_TRIANGLELIST );

El método IASetVertexBuffers reemplaza al antiguo SetStreamSource, con el añadido de que se le pueden pasar varios vertex buffers en una sola llamada. La última sentencia especifica qué tipo de primitivas forman los vértices suministrados, es decir, si se trata de una lista de líneas (cogiendo los vértices de 2 en 2), o si es una tira (que es distinto de uns lista) de triángulos, etc. Aquí se envía una lista en la que hay un único triángulo.

El resultado de todo este trabajo lo podemos ver si llamamos al método Draw del immediate context. Existen varias versiones de este método según el proceso que queramos seguir a la hora de dibujar. Por ejemplo, si estuviéramos usando índices llamaríamos a DrawIndexed, y en caso de usar instanciación invocaríamos a DrawInstanced. Hablaré de todos ellos en los próximos posts. Como es obvio, tales métodos son evoluciones de sus casi homónimos de la interfaz del dispositivo de DirectX 9.

pImmediateContext->Draw( 3, 0 );

Con esto expresamos que queremos dibujar 3 vértices, empezando por el número cero. Y éste es el resultado:

Sumario de Portabilidad

DirectX 9 DirectX 11 Comentario
D3DVIEWPORT9 D3D11_VIEWPORT  
D3DXFLOAT4 XMFLOAT4  
D3DVERTEXELEMENT9 D3D11_INPUT_ELEMENT_DESC  
IDirect3DVertexDeclaration9   No hay análogo.
Constantes D3DFVF   No hay análogo.
D3DDECL_END()   No hay análogo.
D3DXCompileShaderFromFile D3DX11CompileFromFile  
IDirect3DVertexBuffer9 ID3D11Buffer Bind Flag = D3D11_BIND_VERTEX_BUFFER
IDirect3DIndexBuffer9 ID3D11Buffer Bind Flag = D3D11_BIND_INDEX_BUFFER
IDirect3DDevice9::CreateVertexBuffer ID3D11Device::CreateBuffer  
IDirect3DDevice9::CreateIndexBuffer ID3D11Device::CreateBuffer  
IDirect3DVertexShader9 ID3D11VertexShader  
IDirect3DPixelShader9 ID3D11PixelShader  
IDirect3DDevice9::SetStreamSource ID3D11DeviceContext::IASetVertexBuffers  
IDirect3DDevice9::DrawPrimitive ID3D11DeviceContext::Draw  

 

Descargas

Podéis descargar el código de ejemplo desde aquí:

EndlessGameTriangle.zip (23 Kb)
 

Etiquetas: , ,

Lectura recomendada

Publicado el Domingo 15 agosto 2010 en DirectX por Thund

Aquí os dejo una lista de libros relacionados con DirectX o con la programación gráfica en general que he leído o que conozco. Son todos en inglés, obviamente, y los podéis encontrar tanto en la Amazonia como pidiéndoselos a una mula, que cada cual elija su locura.

Introduction to 3D Game Programming With DirectX 9.0
 

Éste fue el primer libro sobre DirectX que leí. Se me hizo un poco cuesta arriba debido a que, por aquel entonces, mi conocimiento acerca del funcionamiento de una API gráfica era inexistente y mi dominio tanto del inglés como de C++ algo mediocre. Aun así le eché un par de voluntades y me fui enterando de toda la teoría que hay delante y detrás de la programación de una aplicación basada en DirectX. Como el título indica, se trata de una introducción, es decir, toca los palos principales y sin entrar en mucho detalle. Esto no quiere decir que explique las cosas de pasada y te deje más perdío que Marco el día de la madre; se centra en los aspectos más básicos, desde cero, siguiendo una curva poco pronunciada, fácil de seguir. Si te apoyas en la referencia del SDK de DirectX a la par que avanzas por los capítulos, el aprendizaje se verá mucho más reforzado. Está claro que es un libro obsoleto, no sirve a alguien que vaya a emprender su andadura por DirectX 11, pero ésto es una verdad a medias; dado que es un libro introductorio, no hace hincapié en el código y en las particularidades de la API, por lo que hay muchas técnicas y bases descritas que son comunes a cualquier librería del estilo, y que pueden ser de gran utilidad para comprender la arquitectura y la forma de trabajar de cualquier versión. Unas 394 páginas, poca cosa.

Aviso de que quizás, tras haber terminado el libro, no seáis capaces de hacer un dragón rojo echando fuego.

Advanced 3D Game Programming with DirectX 9.0
 

Una extensión del anterior, lo que no quiere decir que empiece donde aquél lo dejó, sino que entra en mucha más profundidad en los temas ya tratados y añade muchos nuevos. Si la introducción abarcaba mayormante el campo de los gráficos, éste tomo expande las fronteras hacia el resto de componentes de DirectX, como son DirectSound y DirectInput, y mete la cabeza en la inteligencia articial y las telecomunicaciones. Digno de mención son el capítulo acerca de la gestión de la escena y los detalles sobre mallas progresivas. No me gustó demasiado la maquetación con respecto a su antecesor. De las 514 páginas puedes saltarte las 60 primeras y luego ir dando saltos, el orden no es importante.

Sí, la portada es fea de cojones.

DirectX 9 Graphics The Definitive Guide to Direct3D
 
Este libro es un intento por cubrir todas las áreas habidas y por haber en torno a los gráficos, aunque se deja un apartado sin cubrir de gran importancia, como son los shaders. Empieza desde lo básico, da una base matemática y en pocas páginas nos encontramos iluminando la escena. Como puntos fuertes tiene la descripción de los archivos .X y su utilización; explica la animación por esqueletos y pieles y mediante keyframes; y es de los pocos que tratan temas de animación de texturas y el uso de DirectShow. En total 351 páginas, recomendable como libro de consulta.
Advanced Animation with DirectX
 
Se trata de un libro más especializado, concretamente en el mundo de la animación mediante código y la interpretación de formatos de animación estándar, como la animación por esqueleto / piel (incluyendo el efecto rag doll), morphing, movimiento de partículas, animación de texturas y efectos de animación de objetos blandos como la ropa. A destacar la explicación del uso de archivos .X de DirectX. Unas 346 páginas en las que no se llega a profundizar demasiado en las técnicas aunque se dan buenas bases. Lo bueno de éste libro es que vale para cualquier API gráfica, aunque en el título aparezca DirectX.
Real-time Rendering Tricks and Techniques in DirectX
 
Éste no lo he leído, más bien lo he usado como libro de consulta. Básicamente hace un breve recorrido por los elementos gráficos de Direct3D hasta que empieza a hablar de shaders. Contiene un montón de ejemplos tanto de pixel shaders como de vertex shaders; lo malo para los programadores que usen DirectX es que tales ejemplos no contienen código HLSL sino ensamblador, lo cual es bueno para los que usen otras APIs como OpenGL (si saben traducirlo a GLSL claro). Al final del libro describe técnicas como el motion blur y el picking, y dedica apartados al stencil buffer y a DirectShow. Unas 827 páginas muy recomendables.
The Complete Effect And HLSL Guide
 
Libro dedicado exclusivamente a shaders, tanto en ensamblador como en HLSL, y al framework de efectos de DirectX 9. Éste sí me lo leí de cabo a rabo porque necesitaba coger una buena base, que fuera especializada, acerca de los shaders. Tiene cosas muy malas y otras muy buenas. Una de las malas es que hay muchas partes que son copy&paste de la MSDN; si quisiera leer la MSDN me iría a la maldita MSDN. En cambio, una de las características más destacables es lo detallado de la información acerca de las equivalencias entre el lenguaje HLSL y el ensamblador, las capacidades de los distintos shader models, desde el 1.0 al 3.0, y una precisa descripción de cada instrucción de la GPU. Sabemos que, a día de hoy, contamos con el shader model 5.0 pero, si nos fijamos bien, nos damos cuenta de que desde la versión 3.0 no ha cambiado demasiado el abanico de funciones ni su comportamiento, aunque sí lo hayan hecho algunos elementos de la semántica y la capacidad del hardware (número de instrucciones permitidas, número de constantes…), casos para los que disponemos de la referencia del SDK o la MSDN. El otro aspecto a remarcar de éste libro es que es de los pocos sitios donde se habla del Effects Framework, que parece siempre como si lo guardaran en secreto por la poca información que se puede encontrar por ahí. A mí me aclaró muchas cosas, por eso lo recomiendo, o si ha salido alguna nueva versión del libro tanto mejor. Se hace cortito, 307 páginas de las que la mitad son tablas.
3D Math Primer for Graphics and Game Development
 
Bases matemáticas para muchas de las cosas que normalmente utilizamos y que damos por sentado. No me lo he leído, pero lo pongo aquí para que se sepa de su existencia. Son 435 páginas.
Geometric Tools for Computer Graphics  
El libro gordo de Petete, 1055 páginas para explicarnos una barbaridad de conceptos y algoritmos matemáticos. Los amantes del álgebra, los espacios vectoriales y la geometría se harán pajas con él. Echando un vistazo al índice (pues no me lo he leído aún) puede verse claramente cómo cubre un montón de aspectos útiles, como la formación de elementos geométricos, intersecciones, partición espacial binaria, triangulación, cálculos de distancias, etc. Por ahí se dice que tiene un montón de erratas, pero en su página web vienen todas aquellas que se han detectado (que oye, son unas cuantas). Recomendabilísimo.
Game Programming Gems  
He decidido poner toda la saga junta porque su descripción es similar y no quiero morirme escribiendo. Muchos ya la conoceréis, se trata de una serie de publicaciones que contienen perlas algorítmicas, técnicas geniales y muy extendidas en la programación de juegos en el ámbito profesional, escritas por veteranos de la industria en muchas ocasiones. Los que no sabíais de su existencia, quedaréis gratamente sorprendidos. Eso sí, yo miraría antes el índice de cada uno antes de comprarlo, ya que cada cual contiene métodos muy dispares, a todos los niveles, no sólo relacionados con los gráficos. Imprescindibles.
Introduction to 3D Game Programming with Direct3D 10
 
Homólogo del anteriormente descrito pero para DirectX 10 (además, escrito por el mismo autor). No lo he leído pero se ve que es lo mismo adaptado a la siguiente versión. Unas 500 páginas.
Advanced 3D Game Programming with DirectX 10
 
La temática es la misma que la de su homólogo de DirectX 9 (y otra vez, coincide el autor). Amplía el contenido acerca de la IA y habla de las curvas beizer y los quads. Este lo he leído un poco por encima. Un total de 527 páginas.
C++ for Game Programmers
 
Este post no es para libros de C++, pero el que he puesto está precisamente orientado a la programación de juegos, o aplicaciones gráficas. Muy recomensable, expone una gran cantidad de trucos, recomendaciones y optimizaciones que todo programador agradece, y siempre hay alguna cosa que se aprende por muy ducho que seas en la materia. Son 434 muy amenas para los amantes de C++.

Hay muchos más por ahí, pero esto es un post no la Biblioteca de Alejandría.

Etiquetas: , , ,

Página siguiente »