You are on page 1of 185

XNA Game Studio 3.

1
Carlos Osnaya Medrano
Manual de programacin bsica sobre computacin grfica 3D en XNA Game Studio 3.1

1 Contenido
Parte I............................................................................................................................................................ 4 1 Diseo del manual de XNA en espaol.................................................................................................. 5 1.1 1.2 1.3 1.3.1 1.3.2 1 Estructura ..................................................................................................................................... 5 A quin va dirigido el manual de programacin en XNA .............................................................. 5 Requisitos ..................................................................................................................................... 6 Software ................................................................................................................................... 6 Hardware .................................................................................................................................. 7

Parte II Manual ............................................................................................................................................. 8 Vertex Buffer ......................................................................................................................................... 9 1.1 1.2 2 2.1 2.2 2.3 2.4 2.5 2.6 2.7 3 3.1 3.2 3.3 3.4 3.5 3.6 4 4.1 4.2 4.3 4.4 4.5 5 5.1 VertexBuffer ................................................................................................................................. 9 DynamicVertexBuffer ................................................................................................................. 19 PointList ...................................................................................................................................... 22 LineList ........................................................................................................................................ 23 LineStrip ...................................................................................................................................... 23 Modos de Culling ........................................................................................................................ 24 TriangleList.................................................................................................................................. 24 TriangleStrip ............................................................................................................................... 26 TriangleFan ................................................................................................................................. 27 Posicionando cmara.................................................................................................................. 31 Teclado ....................................................................................................................................... 32 Control 360 ................................................................................................................................. 32 Traslacin.................................................................................................................................... 34 Escala .......................................................................................................................................... 35 Rotacin ...................................................................................................................................... 37 El txel ........................................................................................................................................ 40 Filtros .......................................................................................................................................... 41 Mipmaps ..................................................................................................................................... 42 Plano con textura........................................................................................................................ 42 Modos de direccionamiento ....................................................................................................... 48 SpriteFont ................................................................................................................................... 52

Primitivas ............................................................................................................................................. 21

Creando objeto 3D .............................................................................................................................. 28

Textura ................................................................................................................................................ 40

Texto .................................................................................................................................................... 52

5.2 6 6.1 6.2 6.3 6.4 6.4.1 7 7.1 7.2 7.2.1 7.2.2 7.2.3 7.2.4 7.3 7.3.1 7.3.2 7.3.3 7.4 7.4.1 7.5 7.5.1 7.5.2 8 8.1 8.1.1 8.1.2 8.2 8.3 9 9.1 9.2 9.3 9.3.1 9.3.2 9.3.3

Sprite Font Texture ..................................................................................................................... 57 Formatos de archivos ................................................................................................................. 61 La clase Model ............................................................................................................................ 61 Ejemplo 01 .................................................................................................................................. 63 Ejemplo 02 .................................................................................................................................. 66 Blending ................................................................................................................................. 70 Shader ......................................................................................................................................... 74 Iluminacin ambiental ................................................................................................................ 75 HLSL. Modelo de iluminacin ambiental ................................................................................ 75 Inciando FX Composer............................................................................................................ 81 HLSL. Modelo de iluminacin ambiental con textura ............................................................ 86 Aadiendo nuevo efecto en FX Composer ............................................................................. 90 Iluminacin difusa ...................................................................................................................... 91 Fuente de iluminacin direccional ......................................................................................... 93 Fuente de iluminacin puntual .............................................................................................. 97 Fuente de iluminacin spot .................................................................................................. 103 Iluminacin especular ............................................................................................................... 110 Reflexin especular. Actualizando cdigos de fuentes de iluminacin ............................... 112 Mltiples fuentes de iluminacin ............................................................................................. 114 Multi-pass rending ............................................................................................................... 115 Ejemplo Multiluces ............................................................................................................... 115 Efecto ambiental....................................................................................................................... 123 Clonacin de efecto.............................................................................................................. 123 Interfaz para el shader ......................................................................................................... 126 Luz direccional .......................................................................................................................... 132 Efecto multiluces ...................................................................................................................... 139 Bounding Sphere vs. Bounding Sphere ..................................................................................... 151 Axis Aligned Bounding Box vs. Axis Aligned Bounding Box....................................................... 160 Ray vs. Boundign Sphere .......................................................................................................... 167 Cmara libre ......................................................................................................................... 167 La clase Mira ......................................................................................................................... 173 La clase Proyectil .................................................................................................................. 174 2

Cmo cargar modelos 3D desde un archivo X y FBX ........................................................................... 61

Iluminacin .......................................................................................................................................... 74

Cmo agregar un efecto en XNA ....................................................................................................... 123

Colisin .............................................................................................................................................. 150

9.3.4 9.3.5 10 11

La clase ModeloEstatico ....................................................................................................... 176 Implementacin ................................................................................................................... 178

Bibliografa .................................................................................................................................... 182 Glosario ......................................................................................................................................... 184

Parte I

1 Diseo del manual de XNA en espaol


1.1 Estructura
El manual est constituido por nueve captulos, los primeros seis muestran el uso de las clases principales de dibujado en 3D en XNA, y no demuestran una gran complicacin para el lector. Los ltimos tres captulos hacen uso de las clases explicadas en los seis primeros, y aumenta la complejidad del cdigo, pues abarca conceptos matemticos computacin grfica y un nuevo lenguaje de programacin. Sin embargo, se ha tratado de explicar de la mejor manera para su comprensin. En cada captulo se muestra una serie de ejemplos con su cdigo explicado lnea a lnea, al finalizar cada uno se muestran imgenes que ayudan a demostrar el resultado esperado. En ocasiones se deja al lector un ejercicio para que verifique los diferentes valores que pueden tomar algunos mtodos, o se deja que complemente algunos ejemplos vistos con anterioridad. En seguida se muestra un resumen de los captulos del manual de programacin en XNA. Vertex buffer. En este captulo se explican las clases VertexBuffer y DynamicVertexBuffer, clases que representan el bfer de vrtices del dispositivo grfico. Para mostrar las diferencias entre cada clase, se utiliza un arreglo de vrtices para dibujar un tringulo. Primitivas. Aqu se muestra cada una de las primitivas que XNA ofrece para dibujar. En cada una de ellas se utiliza un mismo arreglo de vrtices para mostrar las diferencias entre cada una de ellas; a excepcin de la ltima, en donde se le asigna al bfer de vrtices otro arreglo. Creando objeto 3D. En este captulo se muestra cmo posicionar la cmara en el mundo tridimensional, a utilizar la entrada de datos por medio del teclado y el gamepad del Xbox 360, para trasladar, rotar y/o escalar un cubo. Este cubo es creado a partir de un arreglo de vrtices y uno de ndices. Textura. Se presenta un plano, creado a partir de un arreglo de vrtices, en donde texturiza una imagen. A la textura se le aplican diferentes filtros predefinidos en XNA, tambin se utilizan todos los modos de direccionamiento que presenta XNA. Texto. XNA tiene dos formas de presentar texto en pantalla, una es a partir de un XML con todas las propiedades de la fuente y la otra es a partir de una imagen con todos los caracteres a mostrar. Iluminacin. Se deja a un lado XNA y se manejan los shaders, para explicar modelos de iluminacin bsicos con diferentes tipos de fuente de iluminacin. Y se introduce al lector al lenguaje de programacin sobre hardware, High Level Shader Languaje (HLSL). Cmo agregar un efecto en XNA. Se integran los efectos, vistos en el captulo anterior, en una solucin de XNA. Se hacen pequeos cambios a los shaders, pero solo como cuestin de ilustracin; se le deja al lector tratar de entender los cambios y el crear la aplicacin que totalice los efectos de iluminacin en XNA. Colisin. El ltimo captulo revisa las clases BoundingSphere, BoundingBox y Ray. Para cada una de ellas se presenta un caso de colisin entre ellas mismas, a excepcin de la clase Ray, ya que no es una envolvente. Se crean las clases Camara, Mira, Proyectil, ModeloEstatico y ModeloDinamico; que complementan los tres ejemplos de colisin.

1.2 A quin va dirigido el manual de programacin en XNA


El manual de programacin naci con la idea de apoyar a los estudiantes que estn cursando, o hayan cursado, la materia de Computacin grfica, en la carrera de Ingeniera en computacin, de la Facultad de Ingeniera en la Universidad Nacional Autnoma de Mxico. Sin embargo, en la Internet se encontr mucho inters por parte de personas apasionadas con los grficos, y sobre todo, con la nueva tecnologa que representa XNA Game Studio. El manual est enfocado en los grficos 3D, por lo tanto, se recomienda que el pblico lector de esta obra tenga los siguientes conocimientos, solo para una mejor comprensin. 5

Lenguaje de programacin C#. Paradigma de programacin orientado a objetos. Lenguaje de programacin C. Geometra Analtica. Algebra Lineal.

Aunque la anterior lista representa un obstculo, se ha tratado de explicar de la mejor manera los ejemplos, para aquellas que estn por involucrarse en esta materia.

1.3 Requisitos
El manual de programacin estar acompaado por los programas fuentes, listos para ser ejecutados desde un inicio. Tambin se incluirn videos demostrativos de algunos ejemplos que requieran ms que una imagen. Adems se incluir una copia de Visual Studio 2008 Express, XNA Game Studio 3.1 y FX Composer. Todo este software se puede descargar gratuitamente de las siguientes direcciones Web. 1.3.1 http://www.microsoft.com/express/download/ http://www.microsoft.com/downloads/details.aspx?FamilyID=80782277-d584-42d2-8024893fcd9d3e82&displaylang=en http://developer.nvidia.com/object/fx_composer_home.html Software

XNA Game Studio funciona sobre plataformas Windows, en la Tabla 1-1 se muestran las versiones de los sistemas operativos admitidos para la instalacin de XNA. Se recomienda la actualizacin del sistema operativo
Tabla 1-1
1

Sistema Operativo Windows XP

Versiones admitidas Home Edition Professional Media Center Edition Tablet PC Edition Home Basic Home Premium Business Enterprise Ultimate Home Basic Home Premium Professional Enterprise Ultimate

Windows Vista

Window 7

En el caso del Xbox 360, se debe contar con XNA Game Studio Connect. Este software se descarga desde el bazar del Xbox 360.
1

Tabla de requisitos tomada de la siguiente direccin: http://msdn.microsoft.com/es-mx/library/bb203925.aspx

XNA Game Studio 3.1 necesita del entorno de desarrollo Visual Studio 2008, en cualquiera de sus versiones Express, Standard, Professional o Team System. Las especificaciones de software y hardware de cada una de ellas varan. 1.3.2 Hardware

El requisito adicional en hardware, sobre la PC, es una tarjeta grfica que admita Shader Model 3.0 y DirectX 9.0c. Este requisito es indispensable para poder ejecutar los ejemplos mostrados en el captulo de Iluminacin. Se recomienda la actualizacin de los controladores de la tarjeta grfica. Para probar los ejemplos en el Xbox 360, se debe contar con un disco duro para su almacenamiento.

Parte II Manual

1 Vertex Buffer
1.1 VertexBuffer
Los modelos tridimensionales que se pueden ver en los videojuegos estn conformados por un conjunto de planos triangulares en el espacio tridimensional. Cada plano est constituido por una terna de puntos y una triada de aristas. Los puntos, alejndonos del trmino matemtico, almacenan informacin sobre su posicin, color, textura, normal, tangente y binormal. Los puntos en el espacio, por lo menos deben contener un conjunto de tres coordenadas (x, y, z). Dado la posible informacin que representan estos puntos, se les ha denominado vrtices. El vertex buffer es una regin de memoria, en la tarjeta grfica, para almacenar vrtices. Para establecer dichos datos en el bfer de vrtices, XNA ofrece las clases VertexBuffer y DynamicVertexBuffer, esta ltima 2 hereda de la primera y VertexBuffer no se recomienda para el uso sobre el Xbox360 . En el ejemplo siguiente, se muestra el uso del VertexBuffer para dibujar un tringulo. Comience por abrir Visual Studio y cree un nuevo proyecto; para ello, vaya al men principal y seleccione Archivo\Nuevo\Proyecto. Enseguida se abrir una ventana de dilogo para seleccionar el tipo de proyecto y plantilla instalados. En tipos de proyecto, seleccione XNA Game Studio 3.1 y en la parte de plantillas tome Windows Game (3.1), vase Ilustracin 1-1.

Ilustracin 1-1 Ventana de dilogo: Nuevo proyecto

Al crear su nueva aplicacin, podr visualizar que automticamente se crea todo lo necesario para comenzar, es ms, puede oprimir F5 para ejecutarla y ver una ventana azul, vase Ilustracin 1-2.

Para mayor informacin acerca del vertex buffer consulte: http://msdn.microsoft.com/en-us/library/bb198836.aspx

Ilustracin 1-2 Programa predefinido

Claro est, que esto hace ms rpido el desarrollo de la aplicacin, sin embargo, hay que explicar unas cosas antes de continuar, y slo sern las necesarias para ver el tringulo dibujado en la ventana.
using System; using Microsoft.Xna.Framework; using Microsoft.Xna.Framework.Graphics;

Estas son las directivas que se necesitan para crear el tringulo y que tiene que colocar al inicio del archivo Game1.cs creado por Visual Studio. System es el espacio de nombre que contiene las clases fundamentales que definen los datos, los 3 eventos, las interfaces, etctera. Microsoft.XNA.Framework es el espacio de nombre que contiene las clases necesarias para crear juegos. Microsoft.XNA.Framework.Graphics es el espacio de nombre que contiene los mtodos de la API de bajo nivel.

namespace TutorialX01 { public class Game1 : Microsoft.Xna.Framework.Game { GraphicsDeviceManager graphics; } }

Visual Studio crea el namespace, cuyo nombre corresponde al del proyecto; la clase Game1, que hereda de la clase Game y la variable de instancia GraphicsDeviceManager que maneja la configuracin y administracin del dispositivo grfico, que es la tarjeta de video de la computadora. La clase Game provee de la inicializacin bsica del dispositivo grfico, de la lgica del juego y el cdigo del render. Para el ejemplo se aaden las siguientes lneas, despus de la declaracin del dispositivo grfico GraphicsDeviceManager.
VertexDeclaration vertexDeclaration; BasicEffect effect;
3

Para mayor informacin acerca de System visite la siguiente direccin: http://msdn.microsoft.com/es-es/library/system.aspx

10

VertexBuffer vertexBuffer;

VertexDeclaration es la clase que representa la declaracin de un vrtice, debido a que existen diferentes tipos de vrtices definidos en XNA, estos pueden ser: VertexPositionColor. Esctructura personalizada que contiene posicin y color. VertexPositionColorTexture. Estructura personalizada que contiene posicin, color y textura. VertexPositionNormalTexture. Estructura personalizada que contiene posicin, normal y textura. VertexPositionTexture. Estructura personalizada que contiene posicin y textura.

En este ejemplo se utiliza VertexPositionColor, ms adelante se mostraran los dems tipos de vrtices. En DirectX se defina la estructura del tipo de vrtice, sin embargo, XNA da estos cuatro tipos de estructuras como parte sta, sin ms que llamarlas como cualquier tipo de dato. BasicEffect representa un shader versin 1.1, dando soporte para el color de los vrtices, textura e iluminacin. En XNA es necesario utilizar un tipo de shader para mostrar en pantalla el dibujo, a diferencia de DirectX u OpenGL. VertexBuffer es la clase que representa el buffer de vrtices para manipular los recursos. Ahora se instancia los vrtices que crearan el tringulo, por lo que se usa un arreglo de VertexPositionColor. El constructor sera el siguiente: public VertexPositionColor (Vector3 position, Color color)
Tabla 1-1

Propiedad position color

Descripcin Es un Vector3, por lo que representa la posicin del vrtice en coordeanas x, y ,z. Es el color del vrtice representado por los colores R,G,B.

private { new new new };

readonly VertexPositionColor[] vertices = VertexPositionColor(new Vector3(0.0F, 1.0F, 0.0F), Color.White), VertexPositionColor(new Vector3(1.0F, -1.0F, 0.0F), Color. White), VertexPositionColor(new Vector3(-1.0F, -1.0F, 0.0F), Color. White)

En el constructor, de la clase Game1, se inicializa el dispositivo grfico y algunas propiedades de la ventana en donde se mostrar todo el mundo que se cre, en la Tabla 1-2 se muestran algunas de estas 4 propiedades .
public Game1() { graphics = new GraphicsDeviceManager(this); this.IsMouseVisible = true; this.Window.Title = "doxmo Tutorial 00"; this.Window.AllowUserResizing = true; }

Para mayor informacin de los miebros de la clase Game consulte la siguiente direccin: http://msdn.microsoft.com/enus/library/microsoft.xna.framework.game_members.aspx

11

Tabla 1-2

Propiedad IsMouseVisible Window.Title Window.AllowUserResizing

Descripcin Muestra u oculta el puntero del mouse sobre la venta, por default est oculto. Es el ttulo de la ventana, por default el ttulo es el nombre que se le di a la solucin en el momento de crearla. Permite mximizar o redimensionar el tamao de la venta.

En el mtodo Initialize es donde se inicializa toda lgica o cualquier recurso no grfico. Por default dentro del mtodo se tiene la inicializacin base de la clase Game, as que hay que agregar unas cuantas lneas para crear la declaracin del vrtice, el efecto as como el bfer de vrtices.
protected override void Initialize() { base.Initialize(); }

La declaracin de VertexDeclaration necesita de dos parmetros; el primero es el dispositivo grfico en el que se asocian los vrtices, el segundo es un arreglo de elementos de vrtices, que ya contiene la estructura de VertexPositionColor.
protected override void Initialize() { vertexDeclaration = new VertexDeclaration(GraphicsDevice, VertexPositionColor.VertexElements); vertexBuffer = new VertexBuffer(GraphicsDevice, 3 * VertexPositionColor.SizeInBytes, BufferUsage.WriteOnly); effect = new BasicEffect(GraphicsDevice, null); effect.VertexColorEnabled = true; vertexBuffer.SetData<VertexPositionColor>(vertices, 0, vertices.Length); base.Initialize(); }

El constructor del VertexBuffer est sobrecargado, el que se tom en cuenta es el que especfica el tamao y su uso. VertexBuffer (GraphicsDevice, Int32, BufferUsage)
Tabla 1-3

Parmetros graphicsDevice sizeInBytes Usage

Descripcin Es el dispositivo grfico asociado con el vertex buffer. Es el nmero de bytes alojados en el vertex buffer. Es la opcin que identifica el comportamiento del vertex buffer.

Para inicializar el efecto bsico que proporciona XNA se utiliza: 12

public BasicEffect (GraphicsDevice device, EffectPool effectPool)


Tabla 1-4

Parmetros device effectPool

Descripcin Es el dispositivo grfico que crear el efecto. Especifica un conjunto de recursos para compartir entre los efectos.

BasicEffect.VertexColorEnabled es una propiedad que habilita el uso de vertices con colores. public void SetData<T> (T[] data, int startIndex, int elementCount) El mtodo SetData de VertexBuffer adquiere los datos a copiar en el bfer de vrtices, donde T es un tipo de dato en el bfer.
Tabla 1-5

Parmetros data starIndex elementCount

Descripcin Es un arreglo que ser copiado al vertex buffer. Indica el ndice a partir del cual se copiarn los datos. Es el nmero de elementos mximos a copiar.

Siguiendo con el cdigo generado automticamente, por Visual Studio, tenemos los siguientes mtodos:
protected override void LoadContent() { } protected override void UnloadContent() { } protected override void Update(GameTime gameTime) { base.Update(gameTime); }

LoadContent es llamado cuando los recursos grficos necesitan ser cargados. UnloadContent es llamado cuando los recursos grficos necesitan ser liberados. Update actualiza el estado de la sesin de multiplayer, como actualizar o supervisar el estado de los dispositivos de entrada, como el gamepad.

Como parte final del archivo Game1.cs se tiene el mtodo que dibujar las geometras, y otras cosas ms en pantalla, que por el momento ser un tringulo.
protected override void Draw(GameTime gameTime) { GraphicsDevice.Clear(Color.Black); Single apecto = GraphicsDevice.Viewport.AspectRatio; effect.World = Matrix.Identity; effect.View = Matrix.CreateLookAt(new Vector3(0, 0, 5), Vector3.Zero, Vector3.Up);

13

effect.Projection = Matrix.CreatePerspectiveFieldOfView(1, apecto, 1, 10); GraphicsDevice.RenderState.FillMode = FillMode.WireFrame; GraphicsDevice.VertexDeclaration = vertexDeclaration; GraphicsDevice.Vertices[0].SetSource(vertexBuffer, 0, VertexPositionColor.SizeInBytes); effect.Begin(); effect.CurrentTechnique.Passes[0].Begin(); GraphicsDevice.DrawPrimitives(PrimitiveType.TriangleList, 0, 1); effect.CurrentTechnique.Passes[0].End(); effect.End(); base.Draw(gameTime); }

La primera lnea limpia el viewport especificando un color, en este caso negro. Se asignan valores a la matriz de mundo del efecto, en este caso con la matriz identidad, esta matriz sirve para hacer cabios de posicin del modelo. As tambin se le asigna un valor a la matriz de vista, que sirve para cambiar la posicin y direccin de la cmara, por medio del mtodo esttico CreateLookAt. Por el momento no se explicaran a fondo estos trminos, pues se ver en los siguientes ejemplos, por ahora slo escrbalos. Projection: es la matriz para cambiar la imagen 3D a 2D que ser dibujada en la pantalla de la computadora; hay diferentes formas de proyecciones que se vern posteriormente. Fillmode: es un numerador que especifica cmo se rellena el tringulo; existen tres modos de relleno de las geometras, los cuales pueden ser punto, malla o slido. En el modo de relleno Point se dibujan los vrtices de la geometra sin conectarlos entre s, vase Ilustracin 1-3.

Ilustracin 1-3 Fillmode.Point

En el modo de relleno WireFrame, slo se dibujan los lados que conectan los vrtices de la geometra, vase Ilustracin 1-4.

14

Ilustracin 1-4 Fillmode.WireFrame

En el modo de relleno Solid, se dibujan los lados que conectan los vrtices y los rellena, como en la Ilustracin 1-5.

Ilustracin 1-5 Fillmode.Solid

A GraphicsDevice.VertexDeclaration se le asigna la declaracin del vrtice al dispositivo grfico. A GraphicsDevice.Vertices[0].SetSource se le asigna el contenido del bfer de vrtices. public void SetSource (VertexBuffer vb, int offsetInBytes,int vertexStride)
Tabla 1-6

Parmetro

Descripcin

15

vb offsetInBytes vertexStride

El verttex buffer de donde se tomaran los datos. Es el byte a partir del cual sern copiados los datos. Es el tamao en bytes de los elementos en el vertex buffer.

Para dibujar la geometra se utilizan los mtodos del efecto, as que se envuelve entre un Begin, mtodo que comienza el efecto; y un End, mtodo que finaliza el efecto, al mtodo GraphicsDevice.DrawPrimitives. Tambin se debe envolver entre las tcnicas que contiene el efecto y sus pasadas, en este caso solo tienen una y tambin comienza con un Begin y termina con un End. public void DrawPrimitives(PrimitiveType primitiveType, int startVertex, int primitiveCount) DrawPrimitives dibuja una secuencia no indexada de la geometra, especificando el tipo de primitiva. En el siguiente captulo se vern los distintos tipos de primitivas que ofrece XNA.
Tabla 1-7

Parmetro primitiveType startVertex primitiveCount

Descripcin Describe el tipo de primitiva a dibujar. Indica el primer vrtice a partir del cual comenzar a dibujar. Es el nmero de primitivas a dibujar.

Para concluir este captulo, en el archivo Program.cs, que crea Visual Studio, se encuentra el mtodo Main, el cual corresponde al punto de inicio del programa.
static void Main(string[] args) { using (Game1 game = new Game1()) { game.Run(); } }

El mtodo Run de la clase Game es para inicializar el juego, para mantener en un bucle el dibujo y para comenzar el procesamiento de eventos de la aplicacin. Genere el proyecto en el men Generar y corrija cualquier excepcin de compilacin que haya ocurrido. Si todo sali bien corra la aplicacin con F5 o vaya al men Depurar para iniciar el programa, y ver un tringulo como el que se muestra en la Ilustracin 1-6.

16

Ilustracin 1-6 Tringulo

En el Cdigo 1-1 se muestra el enlistado completo que debera tener Game1.cs.


Cdigo 1-1
1. 2. 3. 4. 5. 6. 7. 8. 9. 10. 11. 12. 13. 14. 15. 16. 17. 18. 19. 20. 21. 22. 23. 24. 25. 26. 27. 28. 29. 30. 31. 32. 33. 34. 35. 36. 37. 38. 39. 40. 41. 42. using System; using Microsoft.Xna.Framework; using Microsoft.Xna.Framework.Graphics; namespace TutorialX01 { public class Game1 : Microsoft.Xna.Framework.Game { GraphicsDeviceManager graphics; VertexDeclaration vertexDeclaration; BasicEffect effect; VertexBuffer vertexBuffer; private { new new new }; readonly VertexPositionColor[] vertices = VertexPositionColor(new Vector3(0.0F, 1.0F, 0.0F), Color.White), VertexPositionColor(new Vector3(1.0F, -1.0F, 0.0F), Color.White), VertexPositionColor(new Vector3(-1.0F, -1.0F, 0.0F), Color.White)

public Game1() { graphics = new GraphicsDeviceManager(this); this.IsMouseVisible = true; this.Window.Title = "doxmo Tutorial 00"; this.Window.AllowUserResizing = true; Content.RootDirectory = "Content"; } protected override void Initialize() { vertexDeclaration = new VertexDeclaration(GraphicsDevice, VertexPositionColor.VertexElements); vertexBuffer = new VertexBuffer(GraphicsDevice, 3 * VertexPositionColor.SizeInBytes, BufferUsage.WriteOnly); effect = new BasicEffect(GraphicsDevice, null); effect.VertexColorEnabled = true; vertexBuffer.SetData<VertexPositionColor>(vertices, 0, vertices.Length);

17

43. 44. 45. 46. 47. 48. 49. 50. 51. 52. 53. 54. 55. 56. 57. 58. 59. 60. 61. 62. 63. 64. 65. 66. 67. 68. 69. 70. 71. 72. 73. 74. 75. 76. 77. 78. 79. 80. 81. 82. 83. 84. 85.

base.Initialize(); } protected override void LoadContent() { } protected override void UnloadContent() { } protected override void Update(GameTime gameTime) { base.Update(gameTime); } protected override void Draw(GameTime gameTime) { GraphicsDevice.Clear(Color.Black); Single apecto = GraphicsDevice.Viewport.AspectRatio; effect.World = Matrix.Identity; effect.View = Matrix.CreateLookAt(new Vector3(0, 0, 5), Vector3.Zero, Vector3.Up); effect.Projection = Matrix.CreatePerspectiveFieldOfView(1, apecto, 1, 10); GraphicsDevice.RenderState.FillMode = FillMode.WireFrame; GraphicsDevice.VertexDeclaration = vertexDeclaration; GraphicsDevice.Vertices[0].SetSource(vertexBuffer, 0, VertexPositionColor.SizeInBytes); effect.Begin(); effect.CurrentTechnique.Passes[0].Begin(); GraphicsDevice.DrawPrimitives(PrimitiveType.TriangleList, 0, 1); effect.CurrentTechnique.Passes[0].End(); effect.End(); base.Draw(gameTime); } } }

18

1.2 DynamicVertexBuffer
Al igual que la clase VertexBuffer, la clase DynamicVertexBuffer sirve para almacenar una lista de vrtices, sin embargo, esta clase funciona para un arreglo dinmico de vrtices, mientras que el otro es para un arreglo no dinmico. As mismo lo recomiendan para el uso de aplicaciones sobre el Xbox360, en vez del VetertexBuffer; porque se puede sobre pasar el tamao de la memoria de 10MB del EDRAM de la consola. VertexBuffer.SetData no ser necesario para escribir los datos en el bfer de vrtices. Para lo anterior se cuentan con otros mtodos de entrada de datos, pero que por ahora slo se mostrara el adecuado para el tringulo. public void DrawUserPrimitives<T> (PrimitiveType primitiveType, T[] vertexData, int vertexOffset, int primitiveCount)
Tabla 1-8

Parmetro primitiveType vertexData vertexOffset primitiveCount

Descripcin Describe el tipo de primitiva a dibujar Es el arreglo de vrtices Es el ndice del vrtice a partir del cual se copiaran los datos al vertex buffer. Es el nmero de primitivas mximo que se dibujaran.

En la lnea 65 del Cdigo 1-2 se muestra el fcil uso de DrawUserPrimitive. El resto del cdigo es el mismo que en el ejemplo anterior.
Cdigo 1-2
1. 2. 3. 4. 5. 6. 7. 8. 9. 10. 11. 12. 13. 14. 15. 16. 17. 18. 19. 20. 21. 22. 23. 24. 25. 26. 27. 28. 29. 30. public class Game1 : Microsoft.Xna.Framework.Game { GraphicsDeviceManager graphics; VertexDeclaration vertexDeclaration; BasicEffect effect; private readonly VertexPositionColor[] vertices = { new VertexPositionColor(new Vector3(0.0F, 1.0F, 0.0F), Color.Blue), new VertexPositionColor(new Vector3(1.0F, -1.0F, 0.0F), Color.Green), new VertexPositionColor(new Vector3(-1.0F, -1.0F, 0.0F), Color.Red) }; public Game1() { graphics = new GraphicsDeviceManager(this); Content.RootDirectory = "Content"; this.IsMouseVisible = true; this.Window.Title = "doxmo Tutorial 01"; this.Window.AllowUserResizing = true; } protected override void Initialize() { vertexDeclaration = new VertexDeclaration(GraphicsDevice, VertexPositionColor.VertexElements); effect = new BasicEffect(GraphicsDevice, null); effect.VertexColorEnabled = true;

19

31. 32. 33. 34. 35. 36. 37. 38. 39. 40. 41. 42. 43. 44. 45. 46. 47. 48. 49. 50. 51. 52. 53. 54. 55. 56. 57. 58. 59. 60. 61. 62. 63. 64. 65. 66. 67. 68. 69. 70. 71.

base.Initialize(); } protected override void LoadContent() { } protected override void UnloadContent() { } protected override void Update(GameTime gameTime) { base.Update(gameTime); } protected override void Draw(GameTime gameTime) { GraphicsDevice.Clear(Color.Black); Single apecto = GraphicsDevice.Viewport.AspectRatio; effect.World = Matrix.Identity; effect.View = Matrix.CreateLookAt(new Vector3(0, 0, 5), Vector3.Zero, Vector3.Up); effect.Projection = Matrix.CreatePerspectiveFieldOfView(1, apecto, 1, 10); GraphicsDevice.RenderState.FillMode = FillMode.WireFrame; GraphicsDevice.VertexDeclaration = vertexDeclaration; effect.Begin(); effect.CurrentTechnique.Passes[0].Begin(); GraphicsDevice.DrawUserPrimitives<VertexPositionColor>( PrimitiveType.TriangleList, vertices, 0, 1); effect.CurrentTechnique.Passes[0].End(); effect.End(); base.Draw(gameTime); } }

20

2 Primitivas
Las primitivas son elementos bsicos para dibujar cualquier geometra en el espacio tridimensional. XNA ofrece un conjunto de stas, y cada una se explicar a continuacin. As como en el captulo anterior, se seguir usando el VertexBuffer y el DynamicBuffer para los ejemplos. Abra Visual Studio y cree un nuevo proyecto de XNA Game Studio 3.1, seleccionando la plantilla Windows Game (3.1). Escriba la declaracin del bfer de vrtices y las variables de instancia de los vrtices, en el archivo que Game1.cs; recuerde que se est trabajando en los archivos que se crean por default.
VertexDeclaration vertexDeclaration; BasicEffect effect; VertexBuffer vertexBuffer; VertexPositionColor[] vertices ={ new VertexPositionColor(new Vector3(-2.0F, 0.0F, 2.0F), Color.White), new VertexPositionColor(new Vector3(-1.0F, 2.0F, 2.0F), Color.White), new VertexPositionColor(new Vector3(0.0F, 0.0F, 2.0F), Color.White), new VertexPositionColor(new Vector3(1.0F, 1.5F, 2.0F), Color.White), new VertexPositionColor(new Vector3(1.5F, 0.0F, 2.0F), Color.White), new VertexPositionColor(new Vector3(2.2F, 1.0F, 2.0F), Color.White), new VertexPositionColor(new Vector3(2.5F, 0.0F, 2.0F), Color.White)}; VertexPositionColor[] abanico = { new VertexPositionColor(new Vector3(0.0F, 1.0F, 2.0F), Color.White), new VertexPositionColor(new Vector3(1.0F, 1.0F, 2.0F), Color.White), new VertexPositionColor(new Vector3(1.0F, 0.0F, 2.0F), Color.White), new VertexPositionColor(new Vector3(0.5F, -0.5F, 2.0F), Color.White), new VertexPositionColor(new Vector3(-0.5F, -1.0F, 2.0F), Color.White)};

En este caso se declararon dos arreglos de vrtices, el arreglo vertice es para mostrar cinco de las seis primitivas de XNA, y el arreglo abanico es para mostrar la primitiva de abanico. Dentro del constructor escriba las siguientes lneas para modificar algunas propiedades de la ventana en que se mostraran las geometras, son las mismas propiedades que se explicaron en el captulo anterior.
this.IsMouseVisible = true; this.Window.AllowUserResizing = true; this.Window.Title = "doxmo Tutorial 02a";

En el mtodo Initialize se crean las nuevas instancias del bfer de vrtices y el efecto.
vertexDeclaration = new VertexDeclaration(GraphicsDevice, VertexPositionColor.VertexElements); vertexBuffer = new VertexBuffer(GraphicsDevice, 7 * VertexPositionColor.SizeInBytes, BufferUsage.WriteOnly); vertexBuffer.SetData<VertexPositionColor>(vertices, 0, vertices.Length); effect = new BasicEffect(GraphicsDevice, null); effect.VertexColorEnabled = true;

Lo nico que cambia en esta declaracin es el nmero de elementos que contendr el bfer de vrtices, as que se multiplica el nmero de elementos del arreglo por el tamao en bytes de la estructura VertexPositionColor, es decir:
vertexBuffer = new VertexBuffer(GraphicsDevice, vertices.Length * VertexPositionColor.SizeInBytes, BufferUsage.WriteOnly);

En el llenado del bfer de vrtices se utiliza el primer arreglo para mostrar las siguientes primitivas: 21

PointList LineList LineStrip TriangleList TriangleStrip

2.1 PointList
La primitiva PoinList toma una lista de vrtices y los muestra como puntos en el espacio, en el orden en que se encuentran en el bfer de vrtices.
Cdigo 2-1
1. 2. 3. 4. 5. 6. 7. 8. 9. 10. 11. 12. 13. 14. 15. 16. 17. 18. GraphicsDevice.Clear(Color.Black); Single aspecto = GraphicsDevice.Viewport.AspectRatio; effect.World = Matrix.Identity; effect.View = Matrix.CreateLookAt(new Vector3(0.0F, 0.0F, 6.0F), Vector3.Zero, Vector3.Up); effect.Projection = Matrix.CreatePerspectiveFieldOfView(1.0F, aspecto, 1.0F, 10.0F); GraphicsDevice.RenderState.FillMode = FillMode.WireFrame; GraphicsDevice.VertexDeclaration = vertexDeclaration; GraphicsDevice.Vertices[0].SetSource(vertexBuffer, 0, VertexPositionColor.SizeInBytes); effect.Begin(); effect.CurrentTechnique.Passes[0].Begin(); GraphicsDevice.DrawPrimitives(PrimitiveType.PointList, 0, 7); effect.CurrentTechnique.Passes[0].End(); effect.End();

El Cdigo 2-1 se deber escribir dentro del mtodo Draw, note que la mayora del cdigo es el mismo que en el captulo anterior, excepto la lnea 16 en donde el tipo de primitiva ser PointList y el nmero mximo de elementos que se dibujarn sern 7; por lo que podr cambiarlo por la propiedad Lenght del arreglo vertices. Inicie la aplicacin y ver siete puntos como en la Ilustracin 2-1.

Ilustracin 2-1 PointList

22

2.2 LineList
LineList es una primitiva que dibuja lneas rectas a partir de un par de vrtices del bfer de vrtices. En el ejemplo se tienen siete vrtices con lo que se dibujarn tres lneas, el ltimo no se ocupa. Modifique el mtodo DrawPimitives del Cdigo 2-1, lnea 16, cambiando el parmetro PrimitiveType.PointList por PrimitiveType.LineList y el nmero de elementos a tres.
GraphicsDevice.DrawPrimitives(PrimitiveType.LineList, 0, 3);

Oprima F5 y ver algo similar a la Ilustracin 2-2. Como se puede ver, las lneas van del vertice 0 al 1, del 2 al 3 y del 4 al 5.

Ilustracin 2-2 LineList

2.3 LineStrip
LinStrip es la primitiva que une dos puntos para crear una lnea recta, tomando como punto de inicio el segundo punto de la lnea anterior, excepto la primera lnea que no le antecede otra. El nmero de rectas que se pueden dibujar dado un nmero de vrtices ser igual a: = 1 Modifique el tipo de primitiva del Cdigo 2-1, lnea 16, por PrimitiveType.LineStrip y el nmero de elementos a dibujar por seis, o por el nmero de elementos del arreglo vrtice menos uno.
GraphicsDevice.DrawPrimitives(PrimitiveType.LineStrip, 0, 6);

Oprima F5 y vera una imagen similar a la Ilustracin 2-3.

23

Ilustracin 2-3LineStrip

2.4 Modos de Culling


El culling es un algoritmo que determina la visibildad de las caras, sirve para dibujar slo aquella geometra que se alcanza a ver, por ejemplo, en un cubo a lo ms que podemos ver son tres de sus caras. La manera de conocer qu caras se dibujarn es por medio de la normal de sta. La normal es un vector perpendicular al plano y la direccin de ste depender del sentido en que se vayan dibujando las primitivas. Por default XNA tiene el modo CullCounterClockwiseFace activado, es decir, oculta aquellas caras que por orden en el bfer de vrtices se asemejan al sentido contrario a las manecillas del reloj, ms adelante se ver un ejemplo. El modo CullClockwiseFace, oculta aquellas caras que en el bfer de vrtices tengan el orden semejante al sentido de las manecillas del reloj. El modo None hace caso omiso del modo de culling, para dibujar todas las caras. Escriba dentro del mtodo Draw en el Cdigo 2-1, lnea 13, el modo de culling None.
GraphicsDevice.RenderState.CullMode = CullMode.None;

Una manera sencilla de conocer qu caras no son dibujadas, es por medio de la regla de la mano derecha para el caso de CullCounterClockwiseFace y la regla de la mano izquierda para el caso CullClockwiseFace.

2.5 TriangleList
TriangleList es la primitiva que toma triadas de vrtices para dibujar un tringulo. La manera de saber qu nmero de elementos se pueden dibujar dado un nmero de vrtices es la siguiente: = 3

El nmero de elementos ser igual a la parte entera del resultado de la divisin, adems de que debe ser mayor a cero. Modifique el mtodo Draw en el Cdigo 2-1, lnea 16; el primer parmetro cmbielo por Primitive.TriangleList y el tercer parmetro por 2.

24

GraphicsDevice.DrawPrimitives(PrimitiveType.TriangleList, 0, 2);

Oprima F5 para iniciar la aplicacin y ver una imagen similar a la Ilustracin 2-4.

Ilustracin 2-4 CullMode.None

Como puede ver, la primitiva toma los tres primeros vrtices para crear el tringulo de la derecha, posteriormente toma los siguientes tres vrtices para dibujar el tringulo de la izquierda, por lo que no se toma el ltimo vrtice. Ahora modifique la lnea 13 del Cdigo 2-1 por CullClockwiseFace, para que oculte el tringulo cuya primitiva se dibuje en el sentido de la manecillas del reloj.
GraphicsDevice.RenderState.CullMode = CullMode.CullClockwiseFace;

Ilustracin 2-5 CullMode.CullClockwiseFace

Despus cambie el modo del culling a CullCounterClockwiseFace para ocultar el tringulo cuya primitiva se dibuje en el sentido contrario a las manecillas del reloj. 25

Ilustracin 2-6 CullMode.CullCounterClockwiseFace

2.6 TriangleStrip
TriangleStrip es la primitiva que permite dibujar con eficiencia tringulos, pues toma dos de los ltimos vrtices del tringulo anterior ms un nuevo vrtice para crearlo, excepto la primera triada de vertices del bfer de vrtices. Es decir, el primer tringulo toma los vrtices 1, 2 y 3; el segundo toma los vrtices 2, 3 y 4; el tercer tringulo toma los vrtices 3, 4 y 5.hasta concluir con la lista contenida en el bfer de vrtices. Cambie el mtodo DrawPrimitives en el Cdigo 2-1, lnea 16, en el primer y ltimo parmetro.
GraphicsDevice.DrawPrimitives(PrimitiveType.TriangleStrip, 0, 5);

Inicie la aplicacin con la tecla F5, y ver algo similar a la Ilustracin 2-7.

Ilustracin 2-7 TriangleStrip

Para conocer la cantidad mxima de TriangleStrip que se pueden dibujar a partir de un nmero dado de vrtices se resta el nmero de vrtices menos dos. = 2

26

2.7 TriangleFan
Esta ltima primitiva utiliza el primer vrtice del bfer de vrtices, el ltimo vrtice del tringulo anterior y el vrtice siguiente. Es decir, el primer tringulo est formado por los vrtices 1, 2 y 3; el segundo tringulo por los vrtices 1, 3 y 4; el tercer tringulo est constituido por los vrtices 1, 4 y 5. De nueva cuenta, cambie los parmetros uno y tres del mtodo DrawPimitives, del Cdigo 2-1, lnea 16.
GraphicsDevice.DrawPrimitives(PrimitiveType.TriangleFan, 0, 3);

Ilustracin 2-8 TriangleFan

El nmero de TriangleFan mximos que se pueden extraer de una lista de vrtices, se calcula al restar dos, al nmero de vrtices del bfer, con lo cual se obtiene la siguiente frmula: = 2

27

3 Creando objeto 3D
En este captulo se dibujar un cubo a partir de un arreglo de vrtices y un ndice que indica que nmero de vrtice debe conformar el tringulo. Un cubo se conforma de seis caras, ocho vrtices y doce tringulos, sin embargo, no se declararan treinta y seis vrtices, pues habra redundancia en los datos hacindolo ineficaz para este ejemplo. As que el ndice llevar el orden en que se deben dibujar los tringulos. Comience por abrir Visual Studio y cree una nueva solucin para XNA. Declare en el archivo Game1.cs como variables de instancia los vrtices, el efecto, el vertexbuffer, el indexbuffer y un arreglo de enteros que servir como ndice.
Cdigo 3-1
1. 2. 3. 4. 5. 6. 7. 8. 9. 10. 11. 12. 13. 14. 15. 16. 17. 18. 19. 20. 21. 22. 23. 24. 25. 26. 27. 28. 29. VertexDeclaration vertexDeclaration; BasicEffect effect; VertexBuffer vertexBuffer; IndexBuffer indexBuffer; Color color = new Color(new Vector3(0.05859375F, 0.12890625F, 0.1484375F)); VertexPositionColor[] vertices ={ new VertexPositionColor(new Vector3(-1.0F, -1.0F, 1.0F), Color.White), new VertexPositionColor(new Vector3(-1.0F, 1.0F, 1.0F), Color.White), new VertexPositionColor(new Vector3(1.0F, 1.0F, 1.0F), Color.White), new VertexPositionColor(new Vector3(1.0F, -1.0F, 1.0F), Color.White), new VertexPositionColor(new Vector3(-1.0F, -1.0F, -1.0F), Color.White), new VertexPositionColor(new Vector3(-1.0F, 1.0F, -1.0F), Color.White), new VertexPositionColor(new Vector3(1.0F, 1.0F, -1.0F), Color.White), new VertexPositionColor(new Vector3(1.0F, -1.0F, -1.0F), Color.White) }; Int32[] indices ={0, 1, 2, 0, 2, 3, 4, 6, 5, // Posterior 4, 7, 6, 4, 5, 1, // Izquierda 4, 1, 0, 3, 2, 6, // Derecha 3, 6, 7, 1, 5, 6, // Tapa 1, 6, 2, 4, 0, 3, // Base 4, 3, 7}; // Frente

Las lnea 4 del Cdigo 3-1 representa: el bfer de ndices, que describe el orden de dibujo de los vrtices en el bfer de vrtices. Se pueden generar nuevos colores con el constructor Color que tiene ocho sobrecargas, lnea 5, en este caso se utiliz la sobrecarga que recibe como parmetro una estructura Vector3, cuyas coordenadas (x, y, z) representan el rojo, el verde y el azul respectivamente. El rango que puede contener cada coordenada (x, y, z) es de 0.0F a 1.0F. En el mtodo Initialize de la clase Game1, inicialice el bfer de vrtices, el bfer de ndices y el efecto.
Cdigo 3-2
1. 2. 3. 4. 5. 6. 7. 8. 9. 10. protected override void Initialize() { vertexDeclaration = new VertexDeclaration(GraphicsDevice, VertexPositionColor.VertexElements); vertexBuffer = new VertexBuffer(GraphicsDevice, vertices.Length * VertexPositionColor.SizeInBytes, BufferUsage.WriteOnly); // inicializacin del index buffer indexBuffer = new IndexBuffer(GraphicsDevice, typeof(Int32), indices.Length, BufferUsage.WriteOnly);

28

11. 12. 13. 14. 15. 16. 17.

effect = new BasicEffect(GraphicsDevice, null); effect.VertexColorEnabled = true; vertexBuffer.SetData<VertexPositionColor>(vertices, 0, vertices.Length); indexBuffer.SetData<Int32>(indices, 0, indices.Length); base.Initialize(); }

El constructor de IndexBuffer que se ocup tiene la siguiente forma: public IndexBuffer(GraphicsDevice graphicsDevice, Type indexType, int elementCount, BufferUsage usage) En la Tabla 3-1 se muestra el significado de cada parmetro que toma el constructor.
Tabla 3-1

Parmetro graphicsDevice indexType elementCount Usage

Descripcin Es el dispositivo grfico asociado con el bfer de ndice. Es el tipo usado por los valores del ndice. Es el nmero de valores en el bfer. Es un conjunto de opciones que identifican el comportamiento de los recursos del bfer de ndice.

En la lnea 14 del Cdigo 3-2, la asignacin de los datos de un arreglo al bfer de ndice se utiliz el siguiente mtodo. public void SetData<T>(T[] data, int startIndex, int elementCount) Donde T es el tipo de dato de los elementos del arreglo a copiar en el bfer.
Tabla 3-2

Parmetro data startIndex elementCount

Descripcin Es el arreglo de datos a copiar. Es el ndice a partir del cual comenzar a copiar. Es el nmero de elementos a copiar.

Ya slo falta mandar a dibujar el cubo, as que en el mtodo Draw escriba las siguientes lneas para poder ver el cubo en modo WireFrame.
Cdigo 3-3
1. 2. 3. 4. 5. 6. 7. 8. 9. protected override void Draw(GameTime gameTime) { GraphicsDevice.Clear(color); Single aspecto = GraphicsDevice.Viewport.AspectRatio; effect.World = Matrix.Identity; effect.View = Matrix.CreateLookAt(new Vector3(0.0F, 0.0F, -4.0F), Vector3.Zero, Vector3.Up);

29

10. 11. 12. 13. 14. 15. 16. 17. 18. 19. 20. 21. 22. 23. 24. 25.

effect.Projection = Matrix.CreatePerspectiveFieldOfView(1, aspecto, 1, 1000); GraphicsDevice.RenderState.FillMode = FillMode.WireFrame; GraphicsDevice.VertexDeclaration = vertexDeclaration; GraphicsDevice.Vertices[0].SetSource(vertexBuffer, 0, VertexPositionColor.SizeInBytes); GraphicsDevice.Indices = indexBuffer; effect.Begin(); effect.CurrentTechnique.Passes[0].Begin(); GraphicsDevice.DrawIndexedPrimitives(PrimitiveType.TriangleList, 0, 0, indices.Length, 0, 12); effect.CurrentTechnique.Passes[0].End(); effect.End(); base.Draw(gameTime); }

GraphicsDevice.Indices es una propiedad del dispositivo grfico para establecer el bfer de ndices al dispositivo grfico. El mtodo de dibujo, ahora toma un arreglo de enteros que indica el orden en que sern dibujados los tringulos. public void DrawIndexedPrimitives(PrimitiveType primitiveType, int baseVertex, int minVertexIndex, int numVertices, int startIndex, int primitiveCount) En la Tabla 3-3 se muestra el significado de cada uno de los parmetros.
Tabla 3-3

Parmetro primitiveType baseVertex minVertexIndex numVertices startIndex primitiveCount

Descripcin Es el tipo de primitiva a dibujar. Es el ndice a partir del cual se tomar en cuenta. Es el ndice del vrtice usado durante la llamada. Es el nmero de vrtices usados durante la llamada. ndice a partir del cual se dibujar. Es el nmero de primitivas a dibujar.

Inicie el programa con F5 y podr ver algo parecido a la Ilustracin 3-1; si tuvo problemas de compilacin resulvalas y vuelva a intentarlo.

30

Ilustracin 3-1 Cubo

Aunque a primera vista lo que aparece en el viewport no se parezca a un cubo, no es as, esto se debe a la posicin de la cmara que se encentra frente a un lado del cubo. As que ahora modificaremos la posicin de la cmara, slo para darnos una idea de que en realidad se ha dibujado una figura tridimensional.

3.1 Posicionando cmara


Para posicionar la cmara haremos uso del mtodo CreateLookAt que tiene tres vectores cmo parmetros que indican: la posicin de la cmara, el punto de observacin y un vector unitario que indica dnde es hacia arriba, vase Ilustracin 3-2.

Ilustracin 3-2 Parmetros del mtodo CreateLookAt

En este ejemplo slo se cambiar la posicin de la cmara sin cambiar los otros dos vectores. As que comience por declarar los vectores de la cmara como variables de instancia de la clase Game1.
Vector3 posicion = new Vector3(0.0F, 0.0F, -4.0F); Vector3 vista = Vector3.Zero; Vector3 arriba = Vector3.Up;

31

Ahora asgnelas en los parmetros de CreateLookAt, lneas 7 9, Cdigo 3-3.


effect.View = Matrix.CreateLookAt(posicion, vista, arriba);

Para cambiar la posicin de la cmara se utilizar el teclado, y sabiendo que XNA se cre para que la creacin de videojuegos fuera sencilla, pues s, tambin se utilizar el control 360 para cambiar la cmara.

3.2 Teclado
Comencemos con el teclado pues es el dispositivo de entrada de datos ms antiguo. Pero primero definamos con qu teclas se harn los cambios. Tecla Flecha Izquierda Flecha Derecha Flecha Arriba Flecha Abajo Avanza pgina Retrocede pgina Movimiento Disminuye la coordenada X Incrementa la coordenada X Incrementa la coordenada Y Disminuye coordenada Y Incrementa coordenada Z Disminuye coordenada Z

Ahora hay que escribir las siguientes lneas de cdigo dentro del mtodo Update de la clase Game1.
if (Keyboard.GetState(PlayerIndex.One).IsKeyDown(Keys.Left)) posicion.X -= 0.1F; if (Keyboard.GetState(PlayerIndex.One).IsKeyDown(Keys.Right)) posicion.X += 0.1F; if (Keyboard.GetState(PlayerIndex.One).IsKeyDown(Keys.Up)) posicion.Y += 0.1F; if (Keyboard.GetState(PlayerIndex.One).IsKeyDown(Keys.Down)) posicion.Y -= 0.1F; if (Keyboard.GetState(PlayerIndex.One).IsKeyDown(Keys.PageUp)) posicion.Z += 0.1F; if (Keyboard.GetState(PlayerIndex.One).IsKeyDown(Keys.PageDown)) posicion.Z -= 0.1F;

La clase Keyboard del namespace Microsoft.Xna.Framework.Input, es la encargada de verificar qu tecla se oprimi o dej de oprimirse. El mtodo static GetState tiene dos sobrecargas, la primera no obtiene ningn parmetro, y la segunda toma el jugador asociado al control. IsKeyDown regresa un valor booleano si se ha presionado la tecla que se pas como parmetro; el parmetro debe ser un elemento de la numeracin Keys.

3.3 Control 360


Antes de continuar con el manejo del control 360, es importante que se verifique que este dispositivo se encuentre funcionando correctamente; por default Windows Vista y Windows 7 tiene el controlador instalado, sin embargo, en versiones anteriores se tiene que descargar el controlador del control, en la siguiente liga pueden descargarlo. http://www.microsoft.com/hardware/download/download.aspx?category=Gaming 32

En la Ilustracin 3-3 se muestra el control del Xbox 360 con los nombres de los componentes de la clase GamePad.

Ilustracin 3-3 Control 360

Ahora s, coloque las siguientes lneas de cdigo dentro del mtodo Update de la clase Game1, y por debajo de las lneas que manejan el teclado.
if (GamePad.GetState(PlayerIndex.One).ThumbSticks.Right.X > 0) posicion.X += 0.1F * GamePad.GetState(PlayerIndex.One).ThumbSticks.Right.X; if (GamePad.GetState(PlayerIndex.One).ThumbSticks.Right.X < 0) posicion.X -= 0.1F * -GamePad.GetState(PlayerIndex.One).ThumbSticks.Right.X; if (GamePad.GetState(PlayerIndex.One).ThumbSticks.Right.Y > 0) posicion.Y += 0.1F * GamePad.GetState(PlayerIndex.One).ThumbSticks.Right.Y; if (GamePad.GetState(PlayerIndex.One).ThumbSticks.Right.Y < 0) posicion.Y -= 0.1F * -GamePad.GetState(PlayerIndex.One).ThumbSticks.Right.Y; if (GamePad.GetState(PlayerIndex.One).Triggers.Right > 0) posicion.Z += 0.1F * GamePad.GetState(PlayerIndex.One).Triggers.Right; if (GamePad.GetState(PlayerIndex.One).Triggers.Left > 0) posicion.Z -= 0.1F * GamePad.GetState(PlayerIndex.One).Triggers.Left;

Al igual que con el teclado, la clase GamePad tiene mtodos estticos que muestran el estado de cada uno de los componentes del control 360. GetState tiene dos sobrecargas, en la primera recibe el nmero del jugador asociado al control; en la segunda sobrecarga recibe el nmero del jugador (PlayerIndex) y el valor de la numeracin que especifica el uso de la zona muerta (GamePadDeadZone). Los valores que puede tomar GamePadDeadZone es: Circular, IndependentAxes y None. El valor de Circular combina los posiciones x y y de cada uno de los sticks y es comparado con la zona muerta; esto es una mejor prctica, que utilizar los ejes independientes. IndependentAxes compara a cada eje con la zona muerta independientemente, y es el valor que se da por default en el mtodo GetState con un parmetro. None regresa los valores sin procesar de cada stick como un arreglo; esto es cuando intenta implementar su propia zona muerta. Los sticks regresan un Vector2 con valores de punto flotante entre -1.0F y 1.0F. Los triggers regresan un valor de punto flotante entre 0.0F y 1.0F. stos ltimos se utilizaran para cambiar el valor de la posicin de la cmara. Ya puede oprimir F5 para ejecutar la aplicacin y verificar que puede mover la cmara de posicin con el teclado y el control del Xbox 360; en caso de tener un error de compilacin corrjalo y vulvalo a intentar. 33

Ilustracin 3-4 Cambio de posicin

3.4 Traslacin
Para comenzar con los tipos de transformaciones que puede realizarle a un modelo grfico, agregue las siguientes variables de instancia a la clase Game1.
Vector3 traslacion = Vector3.Zero; Single escala = 1.0F; Single rotacionX = 0.0F; Single rotacionY = 0.0F; Single rotacionZ = 0.0F;

La traslacin consiste en mover los vrtices del modelo a una nueva posicin y esto se logra por medio de la siguiente declaracin:
effect.World = Matrix.CreateTranslation(traslacion);

Recuerde que la matriz World sirve para realizar las transformaciones sobre la geometra, as que la asignacin se le hace a la propiedad del efecto, en el mtodo Draw de la clase Game1. El mtodo static CreateTranslation crea una matriz de translacin, la cual tiene cuatro sobrecargas, pero todas comparten la idea de aceptar coordenadas x, y y z, de una manera u otra, en este caso fue un Vector3D. Sus coordenadas se tomaran como la translacin respecto al eje que representan. Es decir, si el vector tiene como coordenadas (1.0, -2.0, 0.0) los vrtices tendrn que trasladarse una unidad sobre el eje x, menos dos unidades sobre el eje y, y cero unidades sobre el eje z. Al igual que para mover la posicin de la cmara, la siguiente tabla muestra qu teclas debern oprimirse para trasladar el modelo 3D.
Tabla 3-4

Tecla D A

Movimiento Disminuye la coordenada X Incrementa la coordenada X

34

W S Z X

Incrementa la coordenada Y Disminuye la coordenada Y Incrementa la coordenada Z Disminuye la coordenada Z

Agregue las siguientes lneas de cdigo para trasladar el cubo, recuerde que deben escribirse dentro del mtodo Update de la clase Game1.
if (Keyboard.GetState(PlayerIndex.One).IsKeyDown(Keys.D)) traslacion.X -= 0.1F; if (Keyboard.GetState(PlayerIndex.One).IsKeyDown(Keys.A)) traslacion.X += 0.1F; if (Keyboard.GetState(PlayerIndex.One).IsKeyDown(Keys.W)) traslacion.Y += 0.1F; if (Keyboard.GetState(PlayerIndex.One).IsKeyDown(Keys.S)) traslacion.Y -= 0.1F; if (Keyboard.GetState(PlayerIndex.One).IsKeyDown(Keys.Z)) traslacion.Z += 0.1F; if (Keyboard.GetState(PlayerIndex.One).IsKeyDown(Keys.X)) traslacion.Z -= 0.1F;

Inicie la solucin oprimiendo F5, si hay algn error que compilacin resulvalo y vuelva a intentar.

Ilustracin 3-5 Traslacin

3.5 Escala
Para cambiar el tamao de los modelos se crea una matriz de escala, la cual tiene seis sobrecargas para aceptar valores de tipo flotante o estructuras de tipo Vector3. El valor que se le asigne al mtodo esttico CreateScale, se usar para cambiar el tamao del modelo. Algunas de las sobrecargas son: Matrix.CreateScale (Single) Matrix.CreateScale (Single, Single, Single) Matrix.CreateScale (Vector3)

35

La primera sobrecarga escala las coordenadas de los vrtices por el valor del parmetro. En la segunda sobrecarga cada parmetro representa la escala sobre el eje x, y y z, respectivamente. Y por ltimo, el parmetro que toma un Vector3 toma los valores de sus coordenadas para cambiar el tamao del modelo, muy parecido a la sobrecarga anterior. En este caso se us un solo valor para cambiar en x, y y z el tamao del cubo. Las siguientes teclas son para cambiar el tamao de la geometra. Tecla E R Accin Incrementa el tamao. Disminuye el tamao

Escriba el cdigo siguiente en el mtodo Update de la clase Game1, despus del que traslada la geometra.
if (Keyboard.GetState(PlayerIndex.One).IsKeyDown(Keys.E)) escala += 0.1F; if (Keyboard.GetState(PlayerIndex.One).IsKeyDown(Keys.R)) { escala -= 0.1F; if (escala < 0.1F) escala = 0.1F; }

Se coloca un condicional if en la disminucin de la escala, pues si llega a ser cero el modelo desaparecera, y si llegar a ser menor que cero se llegara a cambiar el culling del modelo, lo que hara sera mostrarnos las paredes internas del cubo. Ahora hay que generar la matriz y multiplicarla por los valores anteriores, en el mtodo Draw de la clase Game1.
effect.World = Matrix.Identity * Matrix.CreateTranslation(traslacion) * Matrix.CreateScale(escala);

Oprima F5 y pruebe los cambios que puede hacer con el teclado; si hay un problema de compilacin resulvalo y trate de nuevo.

36

Ilustracin 3-6 Escala

3.6 Rotacin
Como en cualquier otro juego en tercera persona, en el que se observa al personaje; el modelo 3D se traslada sobre un mundo y tambin rota para poder cambiar de direccin mientras corren por sus vidas u otra cosa que les pida cambiar de rumbo. La rotacin es la ltima transformacin que se har al cubito con el que se ha estado trabajando, y que stas, las transformaciones, no son exclusivas de los modelos 3D, tambin puede aplicarse a la cmara. La rotacin se hace alrededor de un eje, y para eso se crea una matriz por medio de los mtodos Matrix.CreateRotationX, Matrix.CreateRotationY y Matrix.CreateRotationZ. Esta terna de mtodos recibe un parmetro en punto flotante que representa el ngulo en radianes que ser rotado alrededor de algn eje coordenado. En la Tabla 3-5 se muestra las teclas con las que manejar el cambio de orientacin del cubo.
Tabla 3-5

Tecla J L I K U O

Accin Disminuye el ngulo de rotacin en Y. Incrementa el ngulo de rotacin en Y. Incrementa el ngulo de rotacin en X. Disminuye el ngulo de rotacin en X. Incrementa el ngulo de rotacin en Z. Disminuye el ngulo de rotacin en Z.

Escriba las lneas cdigo dentro del mtodo Update y despus de las lneas que manipulan el tamao del cubo.
if (Keyboard.GetState(PlayerIndex.One).IsKeyDown(Keys.J)) rotacionY -= 0.1F;

37

if (Keyboard.GetState(PlayerIndex.One).IsKeyDown(Keys.L)) rotacionY += 0.1F; if (Keyboard.GetState(PlayerIndex.One).IsKeyDown(Keys.I)) rotacionX += 0.1F; if (Keyboard.GetState(PlayerIndex.One).IsKeyDown(Keys.K)) rotacionX -= 0.1F; if (Keyboard.GetState(PlayerIndex.One).IsKeyDown(Keys.U)) rotacionZ += 0.1F; if (Keyboard.GetState(PlayerIndex.One).IsKeyDown(Keys.O)) rotacionZ -= 0.1F;

Ahora hay que multiplicar la matriz generada por la matriz de mundo del efecto; en este punto hay que tener cuidado al colocar el ordene de las matrices, y que queda fuera del alcance de este documento, pues la multiplicacin de las matrices no es conmutativa, as que en este ejemplo se coloca primero las matrices de rotacin y despus la de traslacin.
effect.World = Matrix.Identity * Matrix.CreateScale(escala) * Matrix.CreateRotationX(rotacionX) * Matrix.CreateRotationY(rotacionY) * Matrix.CreateRotationZ(rotacionZ) * Matrix.CreateTranslation(traslacion);

As que la siguiente ilustracin se muestra lo que pasa cuando se coloca primero las matrices de rotacin y luego la de traslacin.

Ilustracin 3-7 Rotar y trasladar

Se oprimi una de las teclas que rota el cubo y luego se oprimi una que traslada al cubo; es como no si hubiera movido los ejes coordenados, es decir, se rota alrededor del eje y y luego se traslada sobre el eje x, conservando la misma distancia con el viewport. Cambiando el orden de las matrices, primero la matriz de traslacin y luego las de rotacin, se aprecia un cambio importante en la ilustracin siguiente.

38

Ilustracin 3-8 Rotar y trasladar

Se oprimieron las mismas teclas que en el ejemplo anterior para apreciar el cambio; en este caso es como si se hubiera cambiado la orientacin de los ejes coordenados, es decir, primero se rota el cubo alrededor del eje y (es como se hubieran girado tambin los ejes coordenados), y luego se traslad el cubo sobre el eje x, lo que lo aleja del viewport.

39

4 Textura
Las texturas sirven como papel tapiz que se pegan sobre los modelos 3D para darle un aspecto ms real, sin embargo, existen tcnicas ms avanzadas para generar la sensacin de realismo y que se basan en el shader pero que no reemplazan a las texturas, las mejoran. En este captulo se mostrar un ejemplo sencillo, un plano formado por dos tringulos y texturizado. Las texturas son simplemente imgenes digitalizadas que deben tener ciertas caractersticas para que la tarjeta grfica pueda soportarla. Anteriormente el tamao de las imgenes no pasaba de los 512x512 pixeles, empero la evolucin de las GPUs (Graphics Processing Unit) di cabida para experimentar con mejores imgenes. As que hasta ahora las imgenes debern medir con potencias de 2, es decir, imgenes que tengan las siguientes medidas sern aceptadas: 512x512, 512x1024, 256x512, 2048x1024, 1024zx1024, etctera. Los formatos de imgenes que podrn agregar en la solucin de un proyecto de XNA se muestran en la 5 Tabla 4-1.
Tabla 4-1

Formato Bmp Dds Dib Hdr Jpg Pfm Png Ppm Tga

Significado Microsoft Windows bitmap. DirectDrawSurface. Microsoft Windows bitmap. High dynamic-range. Joint Photographic Experts Group (JPEG) compressed Portable float map. Portable Network Graphics. Portable pixmap. Truevision Targa image.

Cada formato de imagen tiene sus caractersticas particulares que las hacen importantes para cada fin; mientras unos son prcticamente una matriz de datos, lo que los hacen mucho ms grandes en bits y ms completos; otros son menos grandes en bits pero con menor informacin y adems agregan sus cdigos para ser decodificados; otros tantos agregan funciones como el canal alfa para crear sprites. As que en el momento de seleccionar el formato de las imgenes hay que tener en cuenta la calidad, el tamao en bits y el tiempo que tarda en cargarse. Este texto no abarca este tipo de cuestiones, pues est fuera de su alcance.

4.1 El txel
De la misma manera que las imgenes tienen al pixel como unidad de medida, para altura y anchura, las texturas tienen al txel como su unidad, y en muchas de las veces es igual a uno. Adems, pueden ser ledos
5

Para mayor informacin sobre los formatos de imgenes visite la siguiente pgina web: http://msdn.microsoft.com/enus/library/microsoft.xna.framework.graphics.imagefileformat.aspx

40

o escritos desde una GPU. Especficamente, un txel puede ser cualquiera de los formatos de textura 6 disponible representados en la numeracin SurfaceFormat. Para conocer qu parte de la textura le corresponde a un vrtice es necesario asignarle una coordenada de textura; las coordenadas de texturas se llaman u y v. El origen de estas coordenadas se sita en la esquina superior izquierda de la imagen; la coordenada u aumenta hacia la derecha y la coordenada v aumenta hacia abajo. La Ilustracin 4-1 muestra en cada esquina las coordenadas que le corresponderan, en el caso que la misma figura fuera la unidad. La esquina superior izquierda el valor de las coordenadas son las mismas, cero; en la esquina superior derecha el valor de la coordenada u es uno; en la esquina inferior izquierda la coordenada v es uno y la coordenada u es cero; y por ltimo, en la esquina inferior derecha las coordenadas tienen el mismo valor, uno.

Ilustracin 4-1 Textura

4.2 Filtros
Las geometras no siempre coincidirn con el tamao de las texturas, es decir, la distancia entre dos vrtices puede medir ms o medir menos, comparada con una unidad de textura, por lo que puede haber un mayor o menor nmero de pixeles asociados al txel. Por lo tanto existen dos modos de sampleo o de muestreo, el de magnificacin y el de minificacin, que permiten seleccionar el color del pixel final. Los filtros se encargan de calcular ese color del pixel, y XNA ofrece varios de stos, que estn enumerados en 7 TextureFilter , vase la Tabla 4-2. Tratar de explicar cmo funciona cada filtro est fuera del alcance de este texto; si quiere conocer ms, acerca de los tipos de filtros, busque en libros de procesamiento digital de imgenes.
Tabla 4-2

Miembro

Descripcin Filtro de textura anisotrpico que se utiliza como filtro de ampliacin o reduccin de la textura. Este tipo de filtro compensa la distorsin producida por la diferencia de ngulo entre el polgono de la textura y el plano de la 8 pantalla. Es el filtro Gaussiano con mscara de 4 x 4 para la magnificacin y la

Anisotropic

GaussianQuad
6

Para mayor informacin visite la siguiente pgina web: http://msdn.microsoft.com/enus/library/microsoft.xna.framework.graphics.surfaceformat.aspx 7 Para mayor informacin visite la siguiente pgina web: http://msdn.microsoft.com/enus/library/microsoft.xna.framework.graphics.texturefilter.aspx 8 Texto tomado de: http://msdn.microsoft.com/es-es/library/microsoft.windowsmobile.directx.direct3d.texturefilter(VS.85).aspx

41

minificacin. Filtro de interpolacin bilineal utilizado como un filtro de ampliacin o reduccin de la textura. Se utiliza un promedio ponderado de un rea 2x2 de txels (elementos de textura de un pxel) alrededor del pxel deseado. El filtro de la textura utilizado entre los niveles de mipmap es una interpolacin de mipmaps trilineal en la que la impresora de trama realiza la interpolacin lineal del color del pxel, utilizando los txels de las dos texturas de mipmap ms cercanas. Los mipmaps estn deshabilitados. En su lugar, la impresora de trama utiliza el filtro de ampliacin. Filtro de punto utilizado como un filtro de ampliacin o reduccin de la textura. Se utiliza el txel con las coordenadas ms prximas al valor de pxel deseado. El filtro de textura utilizado entre los niveles de mipmap se basa en el punto ms cercano; es decir, la impresora de trama utiliza el color del 9 txel de la textura de mipmap ms cercana. Usa un filtro paso banda con mscara de 4x4 para la magnificacin y minificacin de la textura.

Linear

None

Point

PyramidalQuad

4.3 Mipmaps
Los mipmaps son una secuencia de texturas que parten de una original, cada textura es la mitad de su tamao de su antecesora, excepto la primera. Esto ayuda a mejorar la imagen final, quitando ese parpadeo cuando hay cambios bruscos, u otros problemas visuales.

4.4 Plano con textura


Ahora que ya se ha explicado un poco sobre las texturas en el mundo de la computacin grfica, es momento de revisar un ejemplo que maneje una textura sobre una geometra simple. Dos tringulos adyacentes formaran un cuadrado con una textura. Comience por abrir Visual Studio 2008 y cree un nuevo proyecto para XNA, seleccione la plantilla Windows Game (3.1). Tomando parte del cdigo del ejemplo anterior, solo se explicaran las nuevas lneas de cdigo. Ahora agregue las siguientes variables de instancia, dentro de la definicin de la clase pero fuera de todo mtodo, en la clase Game1 (a menos que le haya cambiado el nombre)que hereda de la clase Game.
Cdigo 4-1
1. 2. 3. 4. 5. 6. 7. 8. 9. 10. 11. 12. VertexDeclaration vertexDeclaration; VertexPositionNormalTexture[] vertices ={ new VertexPositionNormalTexture(new Vector3(-1.0F, -1.0F, -1.0F), new Vector3(0.0F,0.0F,-1.0F), new Vector2(0.0F,1.0F)), new VertexPositionNormalTexture(new Vector3(-1.0F,1.0F,-1.0F), new Vector3(0.0F,0.0F,-1.0F), new Vector2(0.0F,0.0F)), new VertexPositionNormalTexture(new Vector3(1.0F,1.0F,-1.0F), new Vector3(0.0F,0.0F,-1.0F), new Vector2(1.0F,0.0F)), new VertexPositionNormalTexture(new Vector3(1.0F,-1.0F,-1.0F), new Vector3(0.0F,0.0F,-1.0F), new Vector2(1.0F,1.0F)) }; Int32[] indices = { 0, 1, 2, 0, 2, 3 };

Texto tomado de: http://msdn.microsoft.com/es-es/library/microsoft.windowsmobile.directx.direct3d.texturefilter(VS.85).aspx

42

13. 14. 15. 16. 17. 18. 19. 20. 21.

Texture2D textura; BasicEffect efecto; Vector3 posicion = new Vector3(0.0F, 0.0F, 2.0F); Vector3 vista = Vector3.Zero; Vector3 traslacion = Vector3.Zero; Single escala = 1.0F; Single rotacionX = 0.0F; Single rotacionY = 0.0F; Single rotacionZ = 0.0F;

El tipo de vrtice en esta ocasin es VertexPositionNormalTexture, lnea 2, debe contener dos Vector3 que contendrn las coordenadas espaciales y la normal respectivamente; y un Vector2 para las coordenadas de textura. El objeto textura, lnea 13, representa una malla 2D de txeles y cada txel es direccionado por un vector con coordenadas u y v. En el mtodo Initialize de la clase Game1, agregue las siguientes lneas de cdigo para inicializar el efecto y el dispositivo grfico.
vertexDeclaration = new VertexDeclaration(GraphicsDevice, VertexPositionNormalTexture.VertexElements); efecto = new BasicEffect(GraphicsDevice, null); efecto.TextureEnabled = true; // se habilita la textura del efecto bsico // modos de sampleo GraphicsDevice.SamplerStates[0].MagFilter = TextureFilter.Linear; GraphicsDevice.SamplerStates[0].MinFilter = TextureFilter.Linear; GraphicsDevice.SamplerStates[0].MipFilter = TextureFilter.Linear;

En este caso necesitamos habilitar la propiedad Textura del EffectBasic que nos proporciona XNA; como puede ver no se ha habilitado el color de los vrtices, pues no tiene sentido al carecer stos de dicha informacin, pero haga la prueba para que vea que es lo que pasa. Se inicializa el modo de sampleo del dispositivo grfico con SamplerStates, que recupera una coleccin de objetos SamplerState del GraphicsDevice. Y se le asignan a cada una de las propiedades MagFilter, Minfilter y MipFilter; el filtro de tipo Linear. Antes de continuar con el cdigo es necesario aadir la imagen en el proyecto, as que para agregar un nuevo elemento haga clic secundario sobre Content, en el Explorador de Soluciones de Visual Studio y seleccione Agregar, vase Ilustracin 4-2.

43

Ilustracin 4-2 Agregar\ Elemento

En este caso se agreg una nueva carpeta, llamada Texturas, para almacenar las imgenes, y es que es mejor tener las cosas ordenadas. Luego haga lo mismo con la carpeta creada en Content, o sea la nueva carpeta creada con el nombre Texturas, y agregue Elemento existente, vase Ilustracin 4-2. Siguiendo con el cdigo, dentro del mtodo LoadContent escriba las siguientes lneas para cargar la textura y generar los MipMaps.
// Lectura de la textura textura = Content.Load<Texture2D>(@"Texturas\Xin"); textura.GenerateMipMaps(TextureFilter.Linear); // creacin de los MipMaps

Hay dos maneras de cargar una textura desde un archivo, una es por medio el mtodo genrico y 10 esttico ContentManager.Load . ste carga un recurso para ser procesado por el Content Pipeline. Y el otro es por medio del mtodo Texture2D.TextureFromFile. Lo recomendable usar es el Content.Load, el cual recibe un String del Asset Name. El Asset Name es el nombre que se usar como referencia en tiempo de ejecucin, por lo que no es necesario colocar la extensin del archivo. Para saber el Asset Name, seleccione el archivo en el Explorador de soluciones de Visual Studio, y ver en la ventana Propiedades el String del Asset Name, vase Ilustracin 4-3.

10

Para mayor informacin revise la siguiente pgina web: http://msdn.microsoft.com/en-us/library/bb197848.aspx

44

Ilustracin 4-3 Asset Name

El string que debe recibir el mtodo LoadContent, debe incluir los nombres de las carpetas anidadas en directorio asociado en el ContentManager, en donde se deposita el archivo creado por un Digital Content 11 Creation (DCC) . El ContentManager es una clase que carga el contenido del Content Pipeline en tiempo de 12 ejecucin . Enseguida se generan los mipmaps de la textura con la constante TextureFilter.Linear con el que se filtra cada nivel del mipmap, por medio del mtodo Texture.GenerateMipMaps. public void GenerateMipMaps(TextureFilter filterType) En el mtodo UnloadContent se aade la siguiente lnea para liberar el recurso ocupado, que en este caso es la textura.
protected override void UnloadContent() { textura.Dispose(); }

Para poder apreciar que en realidad se ha colocado la textura sobre una geometra se toman las mismas lneas de transformaciones del ejemplo anterior y se escriben dentro del mtodo Update.
if (GamePad.GetState(PlayerIndex.One).Buttons.Back == ButtonState.Pressed) this.Exit(); if (Keyboard.GetState(PlayerIndex.One).IsKeyDown(Keys.Left)) posicion.X -= 0.1F; if (Keyboard.GetState(PlayerIndex.One).IsKeyDown(Keys.Right)) posicion.X += 0.1F; if (Keyboard.GetState(PlayerIndex.One).IsKeyDown(Keys.Up)) posicion.Y += 0.1F; if (Keyboard.GetState(PlayerIndex.One).IsKeyDown(Keys.Down)) posicion.Y -= 0.1F; if (Keyboard.GetState(PlayerIndex.One).IsKeyDown(Keys.PageUp)) posicion.Z += 0.1F; if (Keyboard.GetState(PlayerIndex.One).IsKeyDown(Keys.PageDown)) posicion.Z -= 0.1F;
11 12

La DCC es una herramienta de creacin de contenido digital. Para mayor informacin revise la siguientes pginas web: http://msdn.microsoft.com/enus/library/microsoft.xna.framework.content.contentmanager.aspx http://msdn.microsoft.com/es-es/library/bb447756.aspx

45

if (Keyboard.GetState(PlayerIndex.One).IsKeyDown(Keys.D)) traslacion.X -= 0.1F; if (Keyboard.GetState(PlayerIndex.One).IsKeyDown(Keys.A)) traslacion.X += 0.1F; if (Keyboard.GetState(PlayerIndex.One).IsKeyDown(Keys.W)) traslacion.Y += 0.1F; if (Keyboard.GetState(PlayerIndex.One).IsKeyDown(Keys.S)) traslacion.Y -= 0.1F; if (Keyboard.GetState(PlayerIndex.One).IsKeyDown(Keys.Z)) traslacion.Z += 0.1F; if (Keyboard.GetState(PlayerIndex.One).IsKeyDown(Keys.X)) traslacion.Z -= 0.1F; if (Keyboard.GetState(PlayerIndex.One).IsKeyDown(Keys.E)) escala += 0.1F; if (Keyboard.GetState(PlayerIndex.One).IsKeyDown(Keys.R)) { escala -= 0.1F; if (escala < 0.1F) escala = 0.1F; } if (Keyboard.GetState(PlayerIndex.One).IsKeyDown(Keys.J)) rotacionY -= 0.1F; if (Keyboard.GetState(PlayerIndex.One).IsKeyDown(Keys.L)) rotacionY += 0.1F; if (Keyboard.GetState(PlayerIndex.One).IsKeyDown(Keys.I)) rotacionX += 0.1F; if (Keyboard.GetState(PlayerIndex.One).IsKeyDown(Keys.K)) rotacionX -= 0.1F; if (Keyboard.GetState(PlayerIndex.One).IsKeyDown(Keys.U)) rotacionZ += 0.1F; if (Keyboard.GetState(PlayerIndex.One).IsKeyDown(Keys.O)) rotacionZ -= 0.1F; if (Keyboard.GetState(PlayerIndex.One).IsKeyDown(Keys.Escape)) this.Exit();

La asignacin de la textura a la propiedad Texture de la instancia efecto, lnea 13, Cdigo 4-2, es la textura que ser aplicada hacia la geometra. El tipo de dato es un Texture2D, como lo indica la sintaxis siguiente. public Texture2D Texture { get; set; } El mtodo EnableDefaultLighting, lnea 14, habilita la iluminacin por omisin de este efecto. La propiedad PreferPerPixelLighting, lnea 15, obtiene o establece que la iluminacin por pxel puede ser soportada por el dispositivo grfico, o sea la GPU. As que si no tiene una GPU con soporte mnimo para Pixel Shader 2.0 no escriba esta lnea de cdigo.
Cdigo 4-2
1. 2. 3. 4. 5. 6. 7. 8. 9. 10. 11. 12. 13. 14. 15. 16. 17. 18. 19. protected override void Draw(GameTime gameTime) { GraphicsDevice.Clear(color); Single aspecto = GraphicsDevice.Viewport.AspectRatio; efecto.World = Matrix.Identity * Matrix.CreateScale(escala) * Matrix.CreateRotationX(rotacionX) * Matrix.CreateRotationY(rotacionY) * Matrix.CreateRotationZ(rotacionZ) * Matrix.CreateTranslation(traslacion); efecto.View = Matrix.CreateLookAt(posicion, vista, arriba); efecto.Projection = Matrix.CreatePerspectiveFieldOfView(1, aspecto, 1, 1000); efecto.Texture = textura; efecto.EnableDefaultLighting(); // Iluminacin por default del efecto bsico efecto.PreferPerPixelLighting = true; // iluminacin por pixel GraphicsDevice.RenderState.FillMode = FillMode.Solid; GraphicsDevice.VertexDeclaration = vertexDeclaration; GraphicsDevice.RenderState.CullMode = CullMode.None;

46

20. 21. 22. 23. 24. 25. 26. 27. 28. 29. 30. 31.

// comienza el trazado de la geometra efecto.Begin(); efecto.CurrentTechnique.Passes[0].Begin(); GraphicsDevice.DrawUserIndexedPrimitives<VertexPositionNormalTexture>( PrimitiveType.TriangleList, vertices, 0, vertices.Length, indices, 0, 2); efecto.CurrentTechnique.Passes[0].End(); efecto.End(); base.Draw(gameTime); }

Ahora inicie la depuracin oprimiendo F5 para poder ver el plano con la textura pegada a sta. Si todo sali bien, podr ver una imagen similar a la Ilustracin 4-4, si tiene algn error de compilacin corrija y vuelva a intentar.

Ilustracin 4-4 Plano con textura

Sobre todo mueva el modelo con el teclado para que pueda apreciar los cambios, como se muestra en la Ilustracin 4-5.

Ilustracin 4-5 Transformaciones sobre el plano texturizado

47

4.5 Modos de direccionamiento


Hasta ahora las coordenadas de textura asignadas tienen valores de cero o uno, para cubrir toda la geometra con la imagen, pero qu pasara si estos valores fueran mayores a uno? Bueno la manera en que la textura ahora envolver a la geometra depender del tipo de direccionamiento que se le asigne a las coordenadas u y v. Para poder ver los modos de direccionamiento se cambiaran las coordenadas de textura por las siguientes.
VertexPositionNormalTexture[] vertices ={ new VertexPositionNormalTexture(new Vector3(-1.0F, -1.0F, -1.0F), new Vector3(0.0F,0.0F,-1.0F), new Vector2(0.0F,5.0F)), new VertexPositionNormalTexture(new Vector3(-1.0F,1.0F,-1.0F), new Vector3(0.0F,0.0F,-1.0F), new Vector2(0.0F,0.0F)), new VertexPositionNormalTexture(new Vector3(1.0F,1.0F,-1.0F), new Vector3(0.0F,0.0F,-1.0F), new Vector2(5.0F,0.0F)), new VertexPositionNormalTexture(new Vector3(1.0F,-1.0F,-1.0F), new Vector3(0.0F,0.0F,-1.0F), new Vector2(5.0F,5.0F)) };

El modo de direccionamiento Wrap ser el primero en revisar, ste sirve para repetir la textura sobre la geometra de manera que cubra toda. En el mtodo Initialize agregue las siguientes lneas de cdigo, despus de la asignacin del filtro lineal.
GraphicsDevice.SamplerStates[0].AddressU = TextureAddressMode.Wrap; GraphicsDevice.SamplerStates[0].AddressV = TextureAddressMode.Wrap;

En este caso se deja el mismo modo de direccionamiento para las coordenadas u y v, pero no significa que as sea. AddressU y AddressV son propiedades que obtienen o establecen el direccionamiento sobre las coordenadas u y v, respectivamente. TextureAddressMode es una numeracin que define el modo de direccionamiento de la textura. Ejecute el programa y corrija cualquier error de compilacin que sucediera, si tiene xito lograr algo similar a la Ilustracin 4-6.

48

Ilustracin 4-6 Wrap

El siguiente modo de direccionamiento corresponde a Clamp, las coordenadas de textura fuera del rango [0.0, 1.0] se definen con el color de la ltima columna y rengln de la textura para rellenar. Cambie la numeracin de TextureAddressMode.Wrap a TextureAddressMode.Clamp.
GraphicsDevice.SamplerStates[0].AddressU = TextureAddressMode.Clamp; GraphicsDevice.SamplerStates[0].AddressV = TextureAddressMode.Clamp;

Inicie el programa y corrija cualquier error de compilacin que suceda, si tiene xito podr ver algo similar a la Ilustracin 4-7.

Ilustracin 4-7 Clamp

El direccionamiento Mirror es similar al Wrap, sin embargo, en el momento de cubrir la geometra la siguiente imagen estar invertida, como si se estuviera viendo en un espejo; esto sucede hasta que se termine de cubrir la geometra. Cambie la numeracin TextureAddressMode.Clamp por TextureAddressMode.Mirror en las propiedades AddresU y AddresV.

49

GraphicsDevice.SamplerStates[0].AddressU = TextureAddressMode.Mirror; GraphicsDevice.SamplerStates[0].AddressV = TextureAddressMode.Mirror;

Oprima F5 para comenzar con la depuracin y as correr el programa, si ocurre cualquier error de compilacin o de tiempo de ejecucin solucinelo y vuelva a intentar. Si lo ha conseguido ver una imagen como la que se muestra en la Ilustracin 4-8.

Ilustracin 4-8 Mirror

Modo de direccionamiento Border, aqu se coloca una vez la textura sobre la geometra y el resto, si es que tiene, ser rellenado por un color. Cambie la numeracin del modo de direccionamiento de las coordenadas u y v por Border, y agregue el color al borde. BorderColor es una propiedad que obtiene o establece el color del borde.
GraphicsDevice.SamplerStates[0].AddressU = TextureAddressMode.Border; GraphicsDevice.SamplerStates[0].AddressV = TextureAddressMode.Border; GraphicsDevice.SamplerStates[0].BorderColor = Color.LightBlue;

Inicie con la depuracin del programa con F5, y corrija cualquier error para poder ver algo similar a la Ilustracin 4-9.

50

Ilustracin 4-9 Border

Regularmente los modos de direccionamiento son iguales para u y v, sin embargo, pueden crearse combinaciones entre las distintas propiedades.

51

5 Texto
En esta parte del tutorial no se ver nada del mundo 3D, que ms tiene en inters al autor, y es que hasta este momento XNA 3.1 no tiene una librera del texto en 3D, como si lo tiene DirectX u OpenGL, pero que de todas formas se tomar en cuenta en este escrito. El tipo de fuente que se utiliza en XNA es prcticamente toda aquella que se tenga instalada en el Sistema Operativo o que se pueda agregar como una textura de una fuente. Las primeras se agregarn al proyecto como una descripcin del Sprit Font, as que debe tener instalado la fuente. La segunda toma una imagen con los caracteres de la fuente, por lo que no necesita la fuente instalada en la computadora.

Ilustracin 5-1 Sprite Font Texture

Para comenzar este ejemplo, necesitar descargar las imgenes de las fuentes que se agregaron en la misma carpeta en que descargo este tutorial. Pero si quieren crear las suyas pueden descargar la 13 herramienta Bitmap Font Maker de XNA CREATORS CLUB ONLINE . Estas texturas sern procesadas por el Content Pipeline de XNA como Sprite Font Texture, que es uno de los procesos estndar que proporciona XNA. Lo que hace el proceso Sprite Font Texture es tomar una entrada, que en este caso es de tipo Texture2DContent que representa una textura regular de dos dimensiones; y la transforma en una fuente dando como salida a un SpriteFontContent. Esto lo hace al cambiar los pixeles de Color.Magenta a Color.TransparentBlack, y claro estas texturas tienen un canal alfa para hacer transparentes las regiones oscuras.

5.1 SpriteFont
En este ejemplo slo se mostrar un conjunto de enunciados que muestran el uso de texto en XNA. As que comenzaremos por crear un proyecto nuevo de XNA Game Studio 3.1, utilizando la plantilla Windows Game (3.1). Ahora hay que agregar el SpritFont, esto se hace en el explorador de soluciones del proyecto. Como ya se haba visto anteriormente, coloque el cursor sobre el icono de Content y oprima el botn derecho del ratn para agregar un nuevo elemento al proyecto, como se muestra en la Ilustracin 5-2.

13

http://creators.xna.com/es-ES/utilities/bitmapfontmaker

52

Ilustracin 5-2 Nuevo elemento

Inmediatamente se abre una ventana dilogo, Agregar nuevo elemento Content, como se muestra en la Ilustracin 5-3. Seleccione la plantilla Sprite Font, es la que muestra una letra A, cambie el nombre por el de la fuente que quiere colocar en la aplicacin y d clic en el botn Agregar; no es necesario que el nombre del SpriteFont sea el mismo que el de la fuente, es slo por orden. Recuerde que para poder utilizar el SpritFont debe tener instalada la fuente en el Sistema Operativo.

Ilustracin 5-3 Agregar un Sprite Font

Visual Studio abrir de inmediato el archivo *.spritefont que agregamos al proyecto anteriormente. Como podr ver es un archivo XML que contiene la descripcin de una fuente y que ser ledo por el Content Pipeline de XNA; no es necesario que aprenda sobre XML para poder agregar texto en XNA, sin embargo, sera una buena idea hacerlo porque usted mismo podra crear sus propia definicin de documentos o esquema para instanciar los modelos grficos dentro de un mundo 3D, dndoles su pocin, orientacin y nombre a cada uno de ellos. Adems que .NET tiene su librera para manipular XML. En fin, el XML contiene diez etiquetas que corresponden a las propiedades que comnmente tienen las 14 fuentes, las cuales se presenta en la Tabla 5-1 .
Tabla 5-1

Etiqueta

Tipo de dato

Descripcin

14

Para ms informacin revise la siguiente direccin: http://msdn.microsoft.com/en-us/library/bb447759.aspx

53

<FontName>

String

Es el nombre de la fuente que se tiene instalada en el Sistema Operativo, y que prcticamente toma cualquiera que est, excepto las de tipo *.fon. Es el tamao en punto flotante de la fuente. Es el nmero de pxeles a aadir entre cada carcter cuando la cadena se dibuja. Especifica si ser usado el ajuste de espacio cuando se dibuje la fuente. Su valor por default es true. Es el estilo de la fuente a ser importada. Es el carcter Unicode que substituir al carcter que no se encuentra en la fuente importada. La especificacin de esta etiqueta es opcional. Uno o ms rangos numricos indicando que subconjunto de caracteres Unicode ser importado. Es el inicio y el final de una regin de caracteres Unicode. Es el primer carcter Unicode a incluir en un <CharacterRegion> Es el ltimo carcter Unicode a incluir en un <CharcaterRegion>

<Size>

Single

<Spacing>

Single

<UseKerning>

Boolean

<Style>

"Regular," "Bold," "Italic," o "Bold, Italic"

<DefaultCharacter>

Char

<CharacterRegions>

Uno o ms etiquetas <CharacterRegion>

<CharacterRegion> <Start> <End>

Una etiqueta <Start> y una <End> Char Char

En la lnea 4 del Cdigo 5-1 el nombre de la fuente se ha cambiado por Kids, en la lnea 6 se ha puesto el valor de 20 para que se pueda apreciar la cadena en la ilustracin que enseguida aparecer; el valor del espaciado ser de 2, lnea 8, pues por default es 0 y a veces por el tipo de fuente queda muy amontonado como le ocurre a Kids. Tambin se ha des comentado la etiqueta DefaultCharacter, por si hubiera alguna letra de la cadena que no pueda ser encontrada en el intervalo de caracteres de cdigo ASCII imprimible que se le ha asignado, lneas 18 y 19.
Cdigo 5-1
1. 2. 3. <?xml version="1.0" encoding="utf-8"?> <XnaContent xmlns:Graphics="Microsoft.Xna.Framework.Content.Pipeline.Graphics"> <Asset Type="Graphics:FontDescription">

54

4. 5. 6. 7. 8. 9. 10. 11. 12. 13. 14. 15. 16. 17. 18. 19. 20. 21. 22. 23.

<FontName>Kids</FontName> <Size>20</Size> <Spacing>2</Spacing> <UseKerning>true</UseKerning> <Style>Regular</Style> <DefaultCharacter>*</DefaultCharacter> <CharacterRegions> <CharacterRegion> <Start>&#32;</Start> <End>&#126;</End> </CharacterRegion> </CharacterRegions> </Asset> </XnaContent>

Dejando a un lado el XML, es momento de regresar a C#, y comenzaremos declarando una instancia de SpritFont en las variables de instancia de la clase Game1 que gener Visual Studio en el archivo Game1.cs.
Cdigo 5-2
1. 2. 3. 4. 5. 6. 7. 8. 9. 10. 11. 12. 13. 14. 15. 16. 17. 18. 19. 20. 21. 22. 23. 24. 25. 26. 27. 28. 29. 30. 31. 32. 33. 34. 35. 36. 37. 38. 39. 40. 41. 42. 43. 44. 45. public class Game1 : Microsoft.Xna.Framework.Game { GraphicsDeviceManager graphics; SpriteBatch spriteBatch; SpriteFont kids; public Game1() { graphics = new GraphicsDeviceManager(this); Content.RootDirectory = "Content"; this.Window.Title = "TutorialX07"; this.Window.AllowUserResizing = true; this.IsMouseVisible = true; } protected override void Initialize() { base.Initialize(); } protected override void LoadContent() { spriteBatch = new SpriteBatch(GraphicsDevice); kids = Content.Load<SpriteFont>("Kids"); } protected override void UnloadContent() { } protected override void Update(GameTime gameTime) { if (GamePad.GetState(PlayerIndex.One).Buttons.Back == ButtonState.Pressed) this.Exit(); base.Update(gameTime); } protected override void Draw(GameTime gameTime) { GraphicsDevice.Clear(Color.White); spriteBatch.Begin(); spriteBatch.DrawString(kids, "Hola mundo!", new Vector2(0.0F, 0.0F),

55

46. 47. 48. 49. 50. 51.

Color.Black); spriteBatch.End(); base.Draw(gameTime); } }

En el mtodo LoadContet, lneas 21 26 Cdigo 5-2, se carga el *.spritfont que se aadi en el Content. El tipo de dato que se usa es de tipo SpritFont en el mtodo genrico Content.Load. Hay que recordar que slo se debe tomar el Asset Name con el que ContentManaged lo identifica, no se necesita la extensin del archivo. Como ltimo paso, hay que mandar a dibujar el SpriteFont en el mtodo Draw, lneas 40 - 50. Para comenzar hay que preparar el dispositivo grfico para dibujar los sprites con SpriteBatch.Begin. Enseguida se dibuja la cadena con el mtodo SpritBatch.DrawString que tiene seis sobrecargas, en este caso se seleccion el primero que nos da el IntelliSense. public void DrawString(SpriteFont spriteFont, string text, Vector2 position, Color color) Cuyos parmetros se muestran en la Tabla 5-2.
Tabla 5-2

Parmetro spriteFont text position color

Descripcin El sprit de la fuente. La cadena a dibujar. La localidad, en coordenadas de la pantalla, donde ser dibujado el texto. Es el color para el texto.

Inicie el programa con la tecla F5, para poder ver algo similar a la Ilustracin 5-4, si surge cualquier error de compilacin encuentre el problema para resolverlo y trate de nuevo.

Ilustracin 5-4 Carcter desconocido

Como se puede ver, cuando se agregan caracteres con acentos, diresis o la letra , no podrn mostrarse, a cambio se dibuja el signo que se le dio a la etiqueta DefaultCharacter del Spritfont; esto es 15 porque dichos caracteres no se en encuentran dentro del intervalo que se le dio al XML . Para resolver esto, hay que agregar otro intervalo de caracteres ASCII o aumentar el nmero en que termina el intervalo.
<CharacterRegions> <CharacterRegion> <Start>&#32;</Start> <End>&#255;</End> </CharacterRegion>

Vase la siguiente direccin para revisar en que intervalo del cdigo ASCII se encuentran ciertos caracteres http://msdn.microsoft.com/en-us/library/4z4t9ed1(VS.71).aspx

15

56

En este caso se aument el intervalo, para mostrar los signos de puntuacin que se ocupan en el espaol. Vuelva a correr el programa y ver que el mensaje escrito ser el que quera mostrar.

Ilustracin 5-5 Aumenta el rango de caracteres reconocibles

Las dems sobrecargas del mtodo SpriteBatch.DrawString para dibujar texto, se dejan como ejercicio para el lector.

5.2 Sprite Font Texture


En caso que el tipo de fuente no se encuentre instalado en el Sistema Opertativo, o se quiera enriquecer ms el estilo de la fuente se podra exportar al programa una imagen con las letras necesarias que se ocupan en el proyecto, para ello el Sprite Font Texture es una buena solucin. Comience por agregar un nuevo elemento al Content; esta vez ser un archivo de imagen como se muestra en la Ilustracin 5-6. A estas alturas ya sabr como agregar nuevos elementos en el Content, as que omitiremos esos pasos. La imagen debe tener ciertas propiedades para que el Content Pipeline pueda procesarla, y sta son que tenga un color magenta y este habilitado el canal Alfa.

Ilustracin 5-6 Adler

Sin embargo, hay que hacer unas modificaciones en el Content Processor, que es el que se encargara en procesar la imagen de forma adecuada para crear un Sprite Font Texture. As que diriga el icono de la imagen que acaba de agregar en el Content, lo anterior se realiza en el explorador de soluciones, posteriormente haga clic para poder ver las propiedades del archivo. Por default, el valor que tiene la propiedad Content Processor de la lista XNA Framework Content Pipeline, es Texture XNA Framework, vase Ilustracin 5-7.

57

Ilustracin 5-7 Propiedades de una imagen

Cambie la manera en que el Content Pipeline procesar la imagen por Sprite Font Texture XNA Framework, como se ve en la Ilustracin 5-8.

Ilustracin 5-8 Cambiando el procesador de contenido

Cabe aclarar que la textura que ha tomado el Content Pipeline debe tener todos los caracteres necesarios para poder dibujar la cadena deseada, porque si no es as, tambin abra que dejar un carcter por default en caso que no exista. Ahora hay que agregar el Sprite Font Texture al cdigo, comience por declarar una variable de instancia en la clase Game1, lnea5 Cdigo 5-3. En el mtodo LoadContent, lneas 22 -29, al igual que en el ejemplo anterior, se carga el Sprit Font Texture con el nombre del Asset. Y es aqu donde se modifican las propiedades del SpriteFont, el ms importante es el SpriteFont.DefaultCharacter, lnea 26, pues no falta el smbolo que no se pueda representar y d un error en tiempo de ejecucin, hay que asegurarse tambin que el carcter que se tome por default est en el Sprite Font Texture, porque si as no fuera se caera en el mismo problema y esto tambin lo es en el XML del ejemplo anterior. 58

Cdigo 5-3
1. 2. 3. 4. 5. 6. 7. 8. 9. 10. 11. 12. 13. 14. 15. 16. 17. 18. 19. 20. 21. 22. 23. 24. 25. 26. 27. 28. 29. 30. 31. 32. 33. 34. 35. 36. 37. 38. 39. 40. 41. 42. 43. 44. 45. 46. 47. 48. 49. 50. 51. 52. 53. 54. public class Game1 : Microsoft.Xna.Framework.Game { GraphicsDeviceManager graphics; SpriteBatch spriteBatch; SpriteFont adler; public Game1() { graphics = new GraphicsDeviceManager(this); Content.RootDirectory = "Content"; this.Window.Title = "TutorialX07"; this.Window.AllowUserResizing = true; this.IsMouseVisible = true; } protected override void Initialize() { base.Initialize(); } protected override void LoadContent() { spriteBatch = new SpriteBatch(GraphicsDevice); adler = Content.Load<SpriteFont>("Adler0"); adler.DefaultCharacter = '*'; adler.LineSpacing = 0; adler.Spacing = 0; } protected override void UnloadContent() { } protected override void Update(GameTime gameTime) { if (GamePad.GetState(PlayerIndex.One).Buttons.Back == ButtonState.Pressed) this.Exit(); base.Update(gameTime); } protected override void Draw(GameTime gameTime) { GraphicsDevice.Clear(Color.Black); spriteBatch.Begin(); spriteBatch.DrawString(adler, DateTime.Now.TimeOfDay.ToString(), new Vector2(GraphicsDevice.Viewport.Width / 4.5F, GraphicsDevice.Viewport.Height / 2), Color.White); spriteBatch.End(); base.Draw(gameTime); } }

En el mtodo Draw, lneas 42 - 53, como en el ejemplo anterior de dibuja la cadena con el mismo mtodo sobrecargado SpriteBatch.Drawing, lnea 47. Sin embargo, esta vez se mostrar el reloj del sistema como la cadena; para ello se obtiene la propiedad DateTime.Now.TimeOfDay en forma de String. Y la posicin de la cadena ser tomando en cuenta el ancho y alto del Viewport. Para que no se vea alterado el color de la cadena, pues se pretende que el Sprite Font Texture tiene los colores y efectos que no nos puede dar el XML, se deja en Color.White. Por fin, esto es todo con respecto a texto en XNA, o por lo menos lo ms bsico para colocar informacin valiosa en el Viewport, pues a veces no sabemos qu valores toma los vrtices de nuestros modelos 3D o de colisin en tiempo real.

59

Ilustracin 5-9 Reloj

Inicie el programa con la tecla F5 y corrija cualquier error de compilacin si as lo necesitar; ver un lindo reloj ms o menos como la Ilustracin 5-9.

60

6 Cmo cargar modelos 3D desde un archivo X y FBX


Mquinas que crean mquinas, software que crea software, as es cmo se hacen las cosas, pero todo tuvo un comienzo, es por eso que se explic lo que era el bfer de vrtices y el bfer de ndices. Sin embargo, para crear aplicaciones ms complejas es necesario apoyarse de herramientas ya construidas para no volver a reinventar la rueda. Por eso, en esta parte, se har mencin a la carga de modelos 3D, que al fin de cuentas fueron hechos en herramientas de modelacin, como pueden ser 3D MAX, XSI, MAYA, AutoCAD, etc.

6.1 Formatos de archivos


Los modelos 3D se guardan en memoria secundaria, por lo que se tiene un archivo con toda la informacin necesaria para representar en XNA lo que se tena en el modelador. Estos archivos tienen su propia estructura para almacenar los vrtices, las coordenadas de textura, las normales, los ndices de los vrtices, los ndices de las coordenadas de textura, los materiales, etctera. Algunos de los formatos de estos archivos son *.3ds, *.x, *.obj, *.fbx, *.md5, *.dwg, *.max, *.exp, etctera. Sin embargo, no se entrar en detalle qu informacin guarda cada uno de ellos, pues estara lejos del alcance de este escrito, eso y porque algunos estn protegidos. XNA no puede abrir todos los diferentes archivos que generan los modeladores 3D, esto le correspondera al programador. Para nuestra fortuna hay dos tipos de archivos que puede abrirse y cargarse, sin complicarnos la vida, estos son los *.x y los *.fbx. El formato *.x fue introducido en el DirectX 2.0 y que ahora puede estar codificado en forma binaria o en 16 texto plano . El formato *.fbx es una de las tecnologas de Autodesk, cuyo objetivo es la portabilidad de los datos 3D a travs de las diferentes soluciones que existen en el mercado, tambin puede presentarse en forma binaria 17 o en texto plano . La forma de codificar los archivos, ya sea binaria o en texto, depender en qu es lo que le conviene mejor para la aplicacin, o para el desarrollador. Cuando el texto es plano, es fcil revisar, por si llegar a haber algn problema a la hora cargar el modelo 3D en la solucin, puesto que es texto que nosotros como humanos podemos interpretar de forma rpida, otra de las caractersticas importantes es que el archivo ocupa menos memoria en comparacin con el binario. Por otra parte, los archivos que se guardan en forma binaria son muy fciles de interpretar por la computadora, en comparacin con el texto plano.

6.2 La clase Model


Para manipular de una manera ms sencilla la informacin obtenida de un modelo 3D, mucho ms elaborado, XNA ofrece una clase llamada Model y la ayuda de su ContentManaged para crgalo. Las propiedades en las que se conserva la informacin de los modelos, de la clase Model, son Meshes y Bones. Meshes: es una coleccin de objetos ModelMesh que componen el modelo, y cada ModelMesh puede moverse independientemente de otro y ser compuesto por mltiples materiales identificados como objetos ModelMeshPart. Bones: es una coleccin de objetos ModelBone que describen cmo cada uno de los mesh en la coleccin Meshes de este modelo se relaciona con su mesh padre. Pero qu es el mesh?, el mesh como tal es donde se guarda la informacin de la geometra, por lo que tiene un vertex buffer y un index buffer, adems de otros datos. La clase ModelMeshPart es un lote de informacin de la geometra a enviar al dispositivo grfico durante el rendering. Cada ModelMeshpart es una subdivisin de un objeto ModelMesh, as que la clase
16 17

Para mayor informacin revise la siguiente liga http://msdn.microsoft.com/en-us/library/bb174837(VS.85).aspx Para mayor informacin revise la siguiente liga http://usa.autodesk.com/adsk/servlet/pc/index?siteID=123112&id=6837478

61

ModelMesh est formada por varios objetos ModelMeshpart, y que tpicamente se basan en la informacin del material. Por ejemplo, si en una escena se tiene una esfera y un cubo, y cada uno de ellos tiene diferentes materiales, stos se representaran como objetos ModelMeshpart y la escena se representar como un ModelMesh. En la Ilustracin 6-1, se muestra un ModelMesh que contiene tres ModelMeshpart.

Ilustracin 6-1 Tres ModelMeshpart diferentes

En fin, creo que ha sido momento de tener el ejemplo de cmo cargar un modelo 3D en una solucin de XNA. El primer ejemplo ser muy simple para comprender un poco ms el cmo aprovechar las propiedades de la clase Model. (Cmo leer el bfer de vrtices de un ModelMeshPart para crear nuestra propia 18 envolvente de colisin) As que no se desesperen si quieren una solucin rpida para sus necesidades, es mejor ir por las piedritas.

18

Esto debido a la pregunta que surga en los foros, cmo leo el vertex buffer de un mesh?

62

6.3 Ejemplo 01
El siguiente ejemplo muestra cmo cargar un modelo 3D, un elefante, as que si quieren probar ste ejemplo con un archivo que contenga ms de un modelo 3D, solo se ver uno. Tampoco ser la mejor manera de hacerlo, pero creo menester para explicarlo. Pero no se preocupen, en los siguientes ejemplos de este captulo se har mejor la codificacin. Abra Visual Studio para crear una nueva solucin de XNA, llegado hasta este punto no creo que sea necesario explicar cmo crear una nueva solucin, as que enseguida aada un nuevo archivo *.x o *.fbx. XNA tiene predefinido tomar estos dos tipos de archivos para procesarlos. Para este ejemplo se utiliz la figura de un elefante, Elefante.fbx, que pueden descargar del mismo sitio en donde descargaron este texto. Es de esperarse que el ContentManaged sea el encargado de tomar estos tipos de archivos, as que en el momento de agregar el archivo, no hay que olvidar que es en el Content en donde hay que agregar el archivo, a menos que cambie la carpeta raz del Content. En el archivo Game1.cs escriba las siguientes lneas de cdigo fuera del constructor pero dentro de la clase Game1:
Model modelo; BasicEffect basicEffect;

La primera lnea es un objeto de la clase Model, que no creo sea necesario explicar para qu sirve, el segundo objeto es el efecto bsico que nos ofrece XNA, para mandar a dibujarlo. En el mtodo LoadContent de la clase Game1 hay que leer el contenido del archivo Elefante, y se har con ayuda del mtodo genrico Load del Content.
protected override void LoadContent() { spriteBatch = new SpriteBatch(GraphicsDevice); modelo = Content.Load<Model>("Elefante"); }

Hay que recordar que el nombre que se le pasa como parmetro al mtodo debe ser el que est en el Asset name. Hasta el momento todo esto ha sido conocido, eso espero, as que hay que pasar al mtodo Draw. Y es en esta parte que a muchos les parecer mal, pero esa no es la intencin, es nada ms para que pueda explicarse mejor el ejemplo, as que la optimizacin se deja como ejercicio para el lector.
Cdigo 6-1
1. 2. 3. 4. 5. 6. 7. 8. 9. 10. 11. 12. 13. 14. 15. 16. 17. 18. 19. protected override void Draw(GameTime gameTime) { GraphicsDevice.Clear(Color.Black); Matrix[] transformaciones = new Matrix[modelo.Bones.Count]; modelo.CopyAbsoluteBoneTransformsTo(transformaciones); // preparando el efecto basicEffect = (BasicEffect)modelo.Meshes[0].Effects[0]; basicEffect.World = transformaciones[modelo.Meshes[0].ParentBone.Index]; basicEffect.Projection = Matrix.CreatePerspectiveFieldOfView(1.0F, GraphicsDevice.Viewport.AspectRatio, 1.0F, 1000.0F); basicEffect.View = Matrix.CreateLookAt(new Vector3(150.0F, 25.0F, 250.0F), new Vector3(-50.0F, 0.0F, 0.0F), Vector3.Up); basicEffect.EnableDefaultLighting(); ModelMesh modelMesh = modelo.Meshes[0]; ModelMeshPart meshPart = modelMesh.MeshParts[0];

63

20. 21. 22. 23. 24. 25. 26. 27. 28. 29. 30. 31. 32. 33. 34. 35.

// preparando el dispositivo GraphicsDevice.Vertices[0].SetSource( modelMesh.VertexBuffer, meshPart.StreamOffset, meshPart.VertexStride); GraphicsDevice.VertexDeclaration = meshPart.VertexDeclaration; GraphicsDevice.Indices = modelMesh.IndexBuffer; basicEffect.Begin(SaveStateMode.None); basicEffect.CurrentTechnique.Passes[0].Begin(); GraphicsDevice.DrawIndexedPrimitives(PrimitiveType.TriangleList, meshPart.BaseVertex, 0, meshPart.NumVertices, meshPart.StartIndex, meshPart.PrimitiveCount); basicEffect.CurrentTechnique.Passes[0].End(); basicEffect.End(); base.Draw(gameTime); }// fin del mtodo Draw

En la lnea 5, Cdigo 6-1, se crea un arreglo de matrices con dimensin igual al nmero de objetos ModelBone que contenga el modelo. Esto servir para dejar el modelo 3D, que tenamos en el modelador, en la misma posicin en la que estaba. En la lnea 6 se utiliza el mtodo CopyAbsoluteBoneTransformsTo para copiar las transformaciones de cada uno de los huesos en la matriz que sea acaba de crear. Ahora hay que inicializar el efecto y sus propiedades para poder visualizar el modelo 3D; en la lnea 8 se hace una conversin explicita del tipo de dato, esto es para inicializar el efecto bsico que nos proporciona XNA. Sin embargo, no inicializa todas las propiedades que quisiramos, como son las luces y las matrices. Vase que de antemano tomamos el primer ModelMesh y su primer ModelEffect de ste; esto es porque sabemos que el archivo contiene un slo modelo, as que slo tiene un ModelMesh y un slo ModelEffect. No es necesario que sepamos cuntos meshes contiene el archivo; porque al fin de cuentas es un arreglo, y si conocen el uso de arreglos en C#, vern que el propio arreglo conoce cuntos elementos tiene, y es porque es un objeto. As que enseguida le damos valores a las matrices World, Projection y View, lneas 9, 10 y 13 respectivamente. En la matriz World le asignamos la matriz de transformaciones, pero utilizando como indce el del ParentBone. El ParentBone es una propiedad del ModelMesh que obtiene el ModelBone padre de ese mesh. El ModelBone de un mesh contiene una matriz de transformacin que indica cmo es colocado con referencia al mesh padre del modelo. Trate de colocar el valor de cero y ver que no es lo igual, a menos que el modelo estuviera en el centro de referencia del sistema coordenado y orientado a los ejes.

Ilustracin 6-2 Posicin Correcta e incorrecta

En la Ilustracin 6-2 se muestra el resultado de haber respetado el ndice proporcionado por ParentBone, y cuando no se toma en cuenta. Para poder ver las caractersticas de los materiales del modelo, se habilita la luz que por default tiene el efecto, vase lnea 15. De antemano se conoce el nmero de meshes que contiene el archivo, as que se declara un ModelMesh, lnea 17. Y tambin se conoce cuntos MeshPart tiene el ModelMesh, por lo que se declara en 64

la lnea 18. Esto en s, es malo hacerlo en esta parte del cdigo, pero se hizo para poder explicarlo de la mejor manera. Ahora hay que preparar el dispositivo grfico con los datos a dibujar, el primero es el contenido del bfer de vrtices, con el mtodo SetSource, lneas 21 22. Lo interesante aqu es de dnde se obtiene la informacin, del ModelMesh y del ModelMeshPart; ambos pertenecen a la clase Model. Si recuerda el primer parmetro del mtodo SetSource, es el mismo bfer de vrtices, el cual es parte del ModelMesh. Despus necesita el byte a partir del cual sern copiados los datos, lo cual es proporcionado por el ModelMeshPart. Y por ltimo est necesita el tamao en bytes de los elementos del bfer de vrtices, el cual tambin es proporcionado por el ModelMeshPart. Despus de lo anterior, ahora se necesita declarar el tipo de vrtices que ocupar el dispositivo, para eso regresamos a usar el mtodo VertexDeclaration y la propiedad con el mismo nombre del ModelMeshPart, lnea 23. Ya como ltimo preparativo del dispositivo, se asigna el bfer de indices del ModelMesh al Indexbuffer de GraphicsDevice, lnea 24. En este ejemplo se conoce el nmero tcnicas de rendereo, as que el ndice en los arreglos Passes ser cero, lneas 27 y 31. Otra vez, se recurre al mtodo DrawIndexPrimitives del dispositivo grfico para mandar a dibujar la geometra, y obtenemos los datos de las propiedades BaseVertex, NumVertices, StartIndex y PrimitiveCount de la ModelMeshPart. No hace falta decir para qu son estas propiedades, as que si hay alguna duda revsese el captulo 001. Eso es todo para poder ver el elefante en el viewport, Ilustracin 6-3; si hay algn error de compilacin corrija y vuelva a intentarlo otra vez.

Ilustracin 6-3 Un mesh

Como un agregado cambie la lnea de la propiedad World del efecto por el siguiente enlistado.
tiempo = (Single)gameTime.TotalGameTime.TotalSeconds; basicEffect.World = transformaciones[modelo.Meshes[0].ParentBone.Index] * Matrix.CreateRotationX(tiempo) * Matrix.CreateRotationY(tiempo) * Matrix.CreateRotationZ(tiempo);

En donde tiempo es una variable de instancia de la clase Game1 de tipo float, y que se inicializa dentro del mtodo Draw de la clase Game1, y antes de la asignacin de datos a la propiedad World. A ste se le 65

asigna el valor total en segundos, del tiempo transcurrido desde el inicio de la aplicacin. Y como se encuentra dentro del mtodo Draw se actualizar cada vez que se mande a rasterizar. En la asignacin de valores a la propiedad World del efecto, se obtienen primero las matrices de rotacin alrededor de los ejes coordenados x, y y z. Estas matrices se obtienen con ayuda de los mtodos estticos CreateRotationX, CreateRotationY y CreateRotationZ de la clase Matrix. La variable tiempo, de tipo float, representa el ngulo expresado en radianes. Inicie una vez ms la aplicacin y ver rotar el modelo extrado de un archivo *.fbx o *.x.

6.4 Ejemplo 02
En un archivo se pueden encontrar varios y diferentes modelos, cada uno con sus respectivos materiales, texturas y/o efectos. As que este ejemplo muestra cmo dibujar dichos modelos contenidos en un archivo. Cree un nuevo proyecto para XNA Game Studio 3.1 y seleccione la plantilla Windows Game (3.1). En Content, en Explorador de soluciones, agregue dos nuevas carpetas con los nombres Modelos y Texturas respectivamente. Uno de los problemas al cargar archivos que contenan texturas, ya sea en formato *.x o *.fbx, es el nombre relativo del archivo de la textura. Este nombre relativo se crea cuando se exporta el archivo desde el modelador 3D. Este nombre contiene la ruta de donde se lea la textura o simplemente el nombre de la textura. As que en el momento en que se trata de generar la solucin, oprimiendo F6, aparece el error de Missing asset, esto es porque de forma automtica XNA hace referencia al nombre del archivo de la textura que viene dentro de las propiedades del archivo *.x o *.fbx, y al no encontrar dicho archivo no permite continuar con la ejecucin, an si no se hace mencin en los archivos *.cs de la solucin de XNA. Una solucin es dejar el archivo *.x o *.fbx en el mismo lugar en donde se encuentran las texturas, pero esto dejara todo revuelto, as que otra de las soluciones es modificar el nombre del archivo de textura que se hace mencin en el arhivo *.x o *.fbx. En los achivos *.x lo encuentran con el metadato TextureFilename y en *.fbx como RelativeFilename. Pero esto tambin sera algo mal hecho, as que otra de las soluciones es crear carpetas que contengan por separado a las texturas, los shaders, los modelos, etctera, en la solucin que se est creando. Y a partir de ellas es toman los recursos necesarios para modelar y no estar modificando a mano dichos nombres de archivos de textura. Los nombres de las carpetas son necesarios para este ejemplo, pues el archivo que contiene la geometra, hace referencia a los archivos de textura: "..\..\Content\Texturas\Piso.bmp" "..\..\Content\Texturas\Tablero.bmp" "..\..\Content\Texturas\Checker.bmp" Ahora agregue el archivo, en este caso se utiliz el archivo con nombre TeterasF03.FBX, en la carpeta Modelos, y los archivos de textura, en este caso fueron: Piso.bmp, Tablero.bmp y Checker.bmp, en la carpeta Texturas, y ver algo similar a la Ilustracin 6-4.

66

Ilustracin 6-4 Agregando texturas en el Content

Todos los modelos los haremos rotar alrededor de los ejes locales de cada uno de ellos, por medio del teclado. As que comenzaremos por agregar las variables de instancia en el archivo Game1.cs que cre Visual Studio.
Model modelo; Single rotacionX, rotacionY, rotacionZ; const Single angulo = 2.0F * (Single)Math.PI / 180.0F; Single escala = 1.0F; Matrix[] transformaciones;

Las variables rotacionX, rotacionY y rotacionZ son los ngulos a rotar alrededor de los ejes x, y y z respectivamente. Recordando que los ngulos se deben pasar en radianes, la constante angulo ser el ngulo de dos grados por el que se incrementar las variables rotacionX, rotacionY y rotacionZ cada vez que oprima una de las siguientes teclas.
Tabla 6-1

Tecla Left Right Up Down PageDown PageUp Z C

Accin Incrementa la variable rotacionY. Decrementa la variable rotacionY. Incrementa la variable rotacionX. Decrementa la variable rotacionX. Incrementa la variable rotacionZ. Decrementa la variable rotacionZ. Incrementa la variable escala. Decrementa la varibale escala.

67

La variable escala es el valor en punto flotante que se usar para aumentar o disminuir el tamao de todas las figuras geomtricas. El arreglo Matrix guardar todos los BoneTransforms del modelo. En el constructor de la clase Game1 se cambian las ya dichosas propiedades siguientes:
public Game1() { graphics = new GraphicsDeviceManager(this); Content.RootDirectory = "Content"; this.Window.Title = "TutorialX08c"; this.Window.AllowUserResizing = true; this.IsMouseVisible = true; }

En el mtodo LoadContent de la clase Game1 se comienza por cargar el contenido del archivo del modelo tridimensional, al objeto modelo. Inmediatamente se copian las matrices de transformacin de cada figura geomtrica del objeto modelo
protected override void LoadContent() { spriteBatch = new SpriteBatch(GraphicsDevice); modelo = Content.Load<Model>(Content.RootDirectory + @"\Modelos\TeterasF03"); transformaciones = new Matrix[modelo.Bones.Count]; modelo.CopyAbsoluteBoneTransformsTo(transformaciones); }

Hasta este punto no cambia nada al ejemplo anterior, pero ntese que hasta el momento no existe ninguna instancia de la clase BasicEffect. En el mtodo Update, de la clase Game1, hay que agregar todas las acciones descritas en la tabla 6-1.
protected override void Update(GameTime gameTime) { if (GamePad.GetState(PlayerIndex.One).Buttons.Back == ButtonState.Pressed) this.Exit(); if (Keyboard.GetState().IsKeyDown(Keys.Left)) rotacionY += angulo; if (Keyboard.GetState().IsKeyDown(Keys.Right)) rotacionY -= angulo; if (Keyboard.GetState().IsKeyDown(Keys.Up)) rotacionX += angulo; if (Keyboard.GetState().IsKeyDown(Keys.Down)) rotacionX -= angulo; if(Keyboard.GetState().IsKeyDown(Keys.PageDown)) rotacionZ +=angulo; if(Keyboard.GetState().IsKeyDown(Keys.PageUp)) rotacionZ -=angulo; if (Keyboard.GetState().IsKeyDown(Keys.Z)) escala += 0.1F; if (Keyboard.GetState().IsKeyDown(Keys.C)) { if((escala-=0.1F)<1.0F) escala = 1.0F; } base.Update(gameTime); }

68

Cada variable se incrementa o decrementa con un valor constante, cada vez que se registra una tecla oprimida. En el caso de la variable escala, hay que verificar que no tenga un menor a uno. En realidad puede tener un valor menor a uno, lo que hara es encoger los modelos geomtricos; sin embargo, debe ser mayor. Para el mtodo Draw se crean dos instrucciones foreach, una de ellas est anidada; la primera de ellas va a mapear los ModelMesh que contiene el arreglo Meshes del objeto modelo. El segundo bucle va a mapear los efectos de cada mesh que contiene el arreglo Effects del ModelMesh obtenido del foreach anterior.
protected override void Draw(GameTime gameTime) { GraphicsDevice.Clear(Color.Black); foreach (ModelMesh mesh in modelo.Meshes) { foreach (BasicEffect efecto in mesh.Effects) { efecto.World = Matrix.CreateRotationX(rotacionX) * Matrix.CreateRotationY(rotacionY) * Matrix.CreateRotationZ(rotacionZ) * Matrix.CreateScale(escala) * transformaciones[mesh.ParentBone.Index]; efecto.View = Matrix.CreateLookAt(new Vector3(-50.0F, 70.0F, 75.0F), new Vector3(0.0F, 0.0F, 0.0F), Vector3.Up); efecto.Projection = Matrix.CreatePerspectiveFieldOfView(1, GraphicsDevice.Viewport.AspectRatio, 1.0F, 10000.0F); efecto.EnableDefaultLighting(); efecto.PreferPerPixelLighting = true; }// fin de foreach mesh.Draw(); }// fin de foreach base.Draw(gameTime); }

Vase que en el foreach anidado se utiliza la clase BasicEffect para cargar en el GraphicsDevice todas las propiedades, como es el material, la iluminacin, texturas, etctera. En el cuerpo del foreach anidado se le pasan los valores a las matrices World, View y Projection. Aqu lo importante es la matriz World de BasicEffect, pues es la que har que las figuras roten o escalen. Tom en cuenta el orden en que estn dispuestas las operaciones, recuerde que es una multiplicacin de matrices, y por ende su propiedad no es conmutativa. As que como tarea vea que lo que pasa al alterar el orden de las matrices de transformacin. La propiedad PreferPerPixelLighting de BasicEffect, indica que se habilita la iluminacin por pxel, siempre y cuando su Unidad de Procesamiento Grfico o GPU (Graphics Processing Unit) soporte Pixel Shader Model 2.0. Si no es as, comente la lnea o ponga el valor a false. Terminando el foreach anidado, se manda a llamar al mtodo Draw de la clase ModelMesh, sta manda a dibujar todos los ModelMeshPart del mesh, usando el efecto actual. Corra el ejemplo con F5, y vera algo similar a la Ilustracin 6-5.

69

Ilustracin 6-5 Multiples meshes

Oprima las teclas indicadas para las acciones mencionadas en la Tabla 6-1, y ver cmo se mueven todas la teteras al unsono. Note que todas las teteras tienen diferentes propiedades de materiales, textura y sus combinaciones. Sin embargo, algunas de ellas son trasparentes, como lo muestra la Ilustracin 6-6.

Ilustracin 6-6 Render tomado desde un modelador 3D.

6.4.1

Blending

En realidad el efecto de las teteras menos opacas o ms transparentes es una unin de pxeles que da la sensacin de material translcido. Este proceso se le llama blend y la frmula que se utiliza en el ejemplo se le llama ecuacin de blend. PixelSalida: es valor de pxel, resultado de la suma de las multiplicaciones de fuente y destino. PixelFuente: es el color del pxel que se pretende sea el translcido. FactorBlendFuente: es un factor por el que se multiplica a PixelFuente. PixelDestino: es el color del pxel que se desea ver a travs del PixelFuente. 70 = ( ) + ( )

FactorBlendDestino: factor por el que se multiplica a PixelDestino. Los factores indican cunto y cmo contribuye cada pxel al clculo final del color. Los factores blend se componen por Red, Green, Blue y Alpha (r, g, b, a). Y cada uno de ellos se multiplica por su contraparte en el pxel destino y/o fuente. La siguiente tabla muestra los factores de la numeracin 19 Blend que proporciona XNA.
Tabla 6-2

Nombre del miembro Zero One SourceColor

Descripcin Cada componente del pxel es multiplicado por (0, 0, 0, 0), por lo que elimina. Cada componente del pxel es multiplicado por (1, 1, 1, 1), por lo que no se ve afectado. Cada componente del pxel es multiplicado por el color en el pxel fuente. ste puede ser representado como ( , , , ).

InverseSourceColor

SourceAlpha

Cada componente del pxel es multiplicado por la resta de 1 menos el valor del componente del pxel fuente. ste puede ser representado como (1 , 1 , 1 , 1 ). Cada componente del pxel es multiplicado por el valor alfa del pxel fuente. ste puede ser representado como ( , , , ).

InverseSourceAlpha

DestinationAlpha

Cada componente del pxel es multiplicado por el valor obtenido de la resta de 1 menos el valor que contiene el pxel fuente en el campo del canal alfa. ste puede ser representado como (1 , 1 , 1 , 1 )

Cada componente del pxel es multiplicado por el valor alfa del pxel destino. ste puede ser representado como ( , , , ). Cada componente del pxel es multiplicado por el valor obtenido de la resta de 1 menos el valor que contiene el pxel destino en el campo del canal alfa. ste puede ser representado como (1 , 1 , 1 , 1 ).

InverseDestinationAlpha

SourceAlphaSaturation

Cada componente del pxel fuente o destino se multiplica por el valor ms pequeo entre alfa del fuente o uno menos alfa del destino. El componente alfa no sufre cambios. ste puede ser representado como ( , , , 1), donde = ( , 1 ). Aplica slo a la plataforma Win32. Cada componente del pxel fuente es multiplicado por el resultado de la resta 1 menos el valor que contiene el pxel fuente en el campo del canal alfa, y cada componente del pxel destino es multiplicado por el valor alfa del pxel fuente. stos pueden ser representados como (1 , 1 , 1 , 1 ) , y para el blend destino ( , , , ); el blend destino se invlida. De este modo slo es soportado por el SourceBlend de RenderState.

BothInverseSourceAlpha

Para mayor informacin visite la siguiente pgina: http://msdn.microsoft.com/en-us/library/microsoft.xna.framework.graphics.blend.aspx

19

71

BlendFactor

Cada componente del pxel 20 RenderState.BlendFactor .

es

multiplicado

por

la

propiedad

InverseBlendFactor

Cada componente del pxel es multiplicado por el valor obtenido de la resta de 1 menos la propiedad BlendFactor. ste mtodo es soportado solo s SupportsBlendFactor es verdadero en las propiedades SourceBlendCapablities o DestinationBlendCapabilities.

Estos factores se pueden ocupar tanto para el blend fuente como en el destino, a menos que se diga lo contrario. En este caso el canal alfa se involucra en las operaciones, y es quien nos indica la opacidad, por lo que un valor grande es un material ms opaco. Los valores que puede tomar el canal alfa es entre 0 y 1. Este valor de alfa puede provenir del material de la figura geomtrica o de la textura, pero esto ya lo tiene registrado los elementos del arreglo Effects del mesh, por lo que no hay necesidad de agregarlo directamente en el cdigo. Para habilitar el efecto de transparencia, se le debe indicar al dispositivo grfico el estado de procesamiento, en ste caso es el blending. Tomando el ejemplo anterior, escriba las siguientes lneas de cdigo, entre la llave de apertura del foreach anidado y la asignacin de la matriz de mundo de efecto.
Cdigo 6-2
1. 2. 3. 4. 5. 6. 7. 8. 9. if (efecto.Alpha != 1.0F) { efecto.GraphicsDevice.RenderState.AlphaBlendEnable = true; efecto.GraphicsDevice.RenderState.SourceBlend = Blend.One; efecto.GraphicsDevice.RenderState.DestinationBlend = Blend.One; efecto.GraphicsDevice.RenderState.BlendFunction = BlendFunction.Add; } else efecto.GraphicsDevice.RenderState.AlphaBlendEnable = false;

No todos los mesh del archivo contienen el mismo efecto de transparencia, as que hay que tener cuidado de habilitar y deshabilitar el blending, pues si no se hace, el efecto afectara a todos los dems meshes siguientes del primero que lo activo. Como se haba indicado anteriormente, el canal alfa es la clave del blending, pues nos indica que tan opaco es el material o la textura. En la lnea 1 del enlistado anterior, ver que se verifica que la propiedad Alpha del objeto efecto sea diferente de 1.0F para activar el efecto de blending. Para activar el blend se establece en verdadero a la propiedad AlphaBlendEnable del RenderState. Para las lneas 4 y 5 se establece el factor de blend en One de la enumeracin Blend, a SourceBlend y DestinationBlend. En la lnea 6 se aplica la combinacin de colores por medio de la ecuacin de blend, que nos ofrece la enumeracin BlendFunction, como Add. En el cuerpo de else se deshabilita el efecto con slo poner en falso la propiedad AlphaBlendEnable del RenderState. Corra el ejemplo y ver algo similar a la Ilustracin 6-7. Cada tetera tiene asignado diferentes materiales y texturas, adems se habilito el blend que contiene contiene cada una de ellas.

BlendFactor es una propiedad que obtiene o establece el color usado por un factor constante-blend durante el blending. El valor por default es Color.White. Para mayor informacin visite la pgina siguiente: http://msdn.microsoft.com/en-us/library/microsoft.xna.framework.graphics.renderstate.blendfactor.aspx

20

72

Ilustracin 6-7 Blending

Mueva con las teclas vistas en la tabla 6.-1 las teteras, para que vea el resultado final y esperado del archivo. Juegue con los valores de los factores de blend y los diferentes valores de la propiedad BlendFunction , para que corrobore los distintos efectos que logra con sus combinaciones.
21

Para mayor informacin visite la siguiente pgina: http://msdn.microsoft.com/en-us/library/microsoft.xna.framework.graphics.blendfunction.aspx

21

73

7 Iluminacin
En este captulo se explicarn algunos modelos de iluminacin, as como diferentes tipos de fuentes importantes en computacin grfica. El ojo humano es ms sensible a los cambios de brillo que a los de color, por lo que una imagen con 22 efectos de iluminacin transmite ms informacin al espectador de forma ms eficaz. La iluminacin es uno de los elementos que le da ms vida a un videojuego, y es que solo hay que recordar algunos ttulos para distinguir la importancia que tuvieron. Por ejemplo, las aventuras de TomRaider, HalfLife, Gears of War, Final Fantasy o WarCraft. Son videojuegos que con el paso del tiempo, experiencia y tecnologa aumentaron la sensacin de realismo, exigiendo ms por parte de los usuarios que de los mismos creadores. Y aunque mucho tiene que ver el comportamiento de la luz en el mundo real, tambin es muy cierto que en este mundo de la computacin grfica no todo se maneja como en la naturaleza, y esto debido a que las mquinas en que se desarrollan y corren estn limitadas. Y es que para pruebas basta el botn del error del punto flotante o errores de aproximacin. En fin, en lo que se refiere a la parte de videojuegos y entretenimiento lo que se ve bien, est bien. Claro que en el caso de un CAD (Computer Aided Design) sera un desastre natural. Los clculos matemticos para obtener los efectos de iluminacin se explicarn de la mejor manera, para que se puedan implementar en un shader, ocupando Hight Level Shader Languaje (HLSL). Lo cual har un poco difcil de explicar la codificacin, pues este lenguaje de programacin es diferente a C#. Adems Visual Studio no proporciona las mismas herramientas para escribir este tipo de programas com sucede con sus lenguajes nativos. Es decir, no se tiene el apoyo del IntelliSense, o el realce de los tipos de datos y de mtodos; etctera. Por lo tanto, solo queda un editor de texto plano y llano. La recomendacin para este captulo es descargar un IDE (Integrated Development Environment) para shaders. Pueden descargar el que ms les agrade, sin embargo, los ejemplos que se vern a travs de este escrito fueron desarrollados en FX Composer 2.5.

7.1 Shader
Shader: procedimiento de sombreado e iluminacin personalizado que permite al artista o programador 23 especificar el renderizado de un vrtice o un pxel. La palabra shader proviene directamente del software RenderMan de Pixar.
24

Estara de sobra escribir algo de la historia del hardware programable, sin embargo, es necesario hacer una diferencia entre lo que anteriormente se utilizaba para crear un renderizado en tiempo real y el hardware programable. La programacin fija, llamada fixed function, est sujeta a las APIs de renderizado, la cual haca las mismas funciones de transformacin, desde que se tenan los datos de los vrtices hasta la representacin visual en la pantalla, a este proceso de transformacin se le conoce como pipiline. Esto produca efectos pobres y no tena le flexibilidad que necesitaban los artistas. Con el paso del tiempo, y a causa de los buenos efectos obtenidos a partir del renderizado off-line, en las pelculas y anuncios de televisin. Los ingenieros de hardware comenzaron a ver la posibilidad de hacer ms flexible el pipeline. El vertex shader fue una de las mejoras que vinieron a eliminar el procesador de vrtices de la tarjeta grfica y los sustitua por un microprocesador programable, sin embargo, an no se poda lograr efectos sorprendentes en tiempo real como los vistos en filmes.
22 23

http://www.nvidia.es/object/IO_20020107_6675.html http://www.srg.es/files/apendice_tuberia_programable.pdf 24 dem

74

Luego vino la verdadera revolucin en el renderizado en tiempo real con la llegada de los fragment shaders, pues ahora se podan implementar muchos ms modelos de iluminacin que anteriormente se limitaba a Flat y Gouraud. Algunos de los modelos de iluminacin que ahora se implementan son Phong, Blinn, Cell, Mapping, Toon, etctera. Anteriormente se hacia la programacin de shaders por medio del lenguaje ensamblador, sin embargo, y como era de suponerse, pasar a un lenguaje de alto nivel era la va ms apropiada, si es que se quera avanzar ms rpido. Por lo que ahora se tienen en el mercado, tres lenguajes importantes para shader: HLSL (High Level Shading Languaje) de Microsoft, GLSL (OpenGL Shading Languaje) de OpenGl, y Cg(C for graphics) creado por Nvidia. El primero corre sobre DirectX y XNA, el segundo lenguaje es para ocuparla con OpenGL; y por ltimo Cg correr tanto en OpenGL como en DirectX, lo cual lo convierte en uno de los lenguajes de programacin de hardware ms verstil, sin embargo, en este texto se usa HLSL, porque XNA con ayuda de su ContentManger har ms sencillo cargar el programa de sombreado. Como este texto no trata con profundidad el lenguaje HLSL y el texto est dirigido para programadores, no para artistas, es recomendable que las matemticas explicadas ms adelante y el cdigo se traten de entender lo mejor posible, y esto se tratar de lograr con ejemplos de iluminacin bsica, por lo que se espera que en un futuro el lector pueda entender un poco ms, cualquier shader en HLSL, y porque no, en GLSL o en Cg. En los siguientes subtemas se explicaran a groso modo el significado de cada modelo de iluminacin, acompaado de algunos clculos matemticos y al final de cada uno de ellos, un ejemplo ocupando HLSL en FX Composer, para visualizar en tiempo real los cambios en las variables del shading. Cabe aclarar, si es que no lo he mencionado anteriormente, no se profundizar en el lenguaje de shading, ni en el uso del IDE FX Composer y mucho menos, en la forma artstica en que puede manejarse esta herramienta. Pues el fin de este tema es introducir al lector al hardware programable con ejemplos sencillos.

7.2 Iluminacin ambiental


El modelo de iluminacin ambiental es la intensidad en que se refleja la propiedad del material en todas direcciones, de una manera difusa, por lo que no se debe considerar como auto-iluminante, aunque as lo aparenta. Este modelo muestra un objeto de color monocromtico, a menos que una de sus partes poligonales tenga otra propiedad, o sea, otro color. Y aunque no es de mucho inters manejar la iluminacin ambiental por separado, es importante para complementar los siguientes modelos. La ecuacin de iluminacin es la siguiente: Donde es la intensidad resultante, es la intensidad de la luz ambiental, y que puede considerarse constante para todos los objetos. El coeficiente de reflexin ambiental, es una propiedad del material que refleja la cantidad de luz ambiental; su valores varan entre 0 y 1. =

Tmese en cuenta que el coeficiente de reflexin ambiental, como en los dems modelos de iluminacin, es una conveniencia emprica y no corresponde a ninguna propiedad fsica de los materiales reales. HLSL. Modelo de iluminacin ambiental

7.2.1

El ejemplo siguiente aplica el modelo de iluminacin ambiental, ocupando HLSL para darle al lector desde el inicio una introduccin a la sintaxis. Pero antes hay que redefinir la ecuacin de iluminacin ambiental para el shader. Los trminos de intensidad de luz ambiental, y el coeficiente de reflexin ambiental, quedaran como un nico elemento, el color del material ambiental, esto es para fines prcticos lo que no significa que no se puedan aadir en el cdigo. Por lo tanto la ecuacin sera la siguiente: 75

Este primer ejemplo toma el color de entrada desde la aplicacin XNA para colorear la geometra de un modelo 3D. Esta interaccin entre la aplicacin XNA y el shader necesita de variables uniform, que no son ms que variables globales al estilo del lenguaje de C, pero su caracterstica principal es que son de slo lectura. La forma de definir una variable en HLSL es la siguiente: [Store_Class][Type_Modifier] Type Name [Index][:Semantic][Annotations][=Initial_Value][:Packsoffset][:Register]; Store_Class: son modificadores opcionales que le sugieren al compilador el alcance y tiempo de vida de 25 las variables; este modificador puede ser especificado en cualquier orden . Type_Modifier: es opcional el modificador de tipo de variable. Type: es cualquier tipo enlistado en la Tabla 7-1.
Tabla 7-1

Tipos intrnsecos Bufer Scalar Vector, Matrix Sampler, Shader, Texture Struct, Use Defined

Descripcin Bufer, que contiene uno o ms escalares. Un componente escalar. Componente mltiple vector o matriz. Sampler, shader u objeto textura Estructura personalizad o tipo definido
26

Name[Index]: es una cadena ASCII que identifica nicamente a una variable shader. Semantic: es una informacin opcional como parmetro, usado por el compilador para ligar las entradas y salidas de los shaders, o sea el VertexShader y PixelShader. Hay varias semnticas predefinidas para el vertex y pixel shaders, adems de que no se pueden crear otras. El compilador ignorar la semntica a menos que se declaren como variables globales o como parmetro dentro de un shader. Annotanios: es un metadato opcional, en la forma de un string, conectado a una variable global. Una anotacin es usada por el framework del efecto e ignorada por HLSL. Initial_Value: es el valor inicial y es opcional; el nmero de valores depender del nmero de componentes en Type. Cada una de las variables marcadas como extern debern ser inicializadas con un valor literal; cada variable marcada como static deber ser inicializada como contante. Las variables globales no se deben marcar como extern o static, y si se inicializan no sern compiladas dentro del shader y podrn ser optimizadas. Packoffset: es una palabra reservada para empaquetar manualmente una constante. Register: es una palabra reservada para asignarle a una variable un registro en particular. El listado siguiente muestra el programa que utiliza el modelo de iluminacin ambiental, para la geometra.
25 26

Para mayor informacin consulte la siguiente pgina: http://msdn.microsoft.com/en-us/library/bb509706(v=VS.85).aspx Para mayor informacin consulte la siguiente pgina: http://msdn.microsoft.com/en-us/library/bb509587(v=VS.85).aspx

76

Las lneas 1 5, del Cdigo 7-1, es un comentario multi lnea enmarcado entre /* y */, al estilo de C; tambin pueden hacerse comentarios de una sola lnea empleando //. Enseguida del comentario se crean las variables globales, lneas 6 14, stas se consideran variables uniform, as que no es necesario colocar el Storage_Class uniform. Las variables world, view y projection son matrices de 4 renglones por 4 columnas de tipo float, vase que le precede dos puntos y el nombre de la variable, pero con la primera letra mayscula, esto es una anotacin que ayudar a visualizar el resultado de la compilacin en FX Composer, y no es necesario que tenga el mismo nombre que la variable. Por lo tanto, las semnticas para las siguientes variables, view y projection, tienen el mismo fin que World. HLSL no tomar en cuenta estas semnticas, pero se han incluido en este programa, simplemente para poder visualizar el resultado final en el IDE.
Cdigo 7-1
1. 2. 3. 4. 5. 6. 7. 8. 9. 10. 11. 12. 13. 14. 15. 16. 17. 18. 19. 20. 21. 22. 23. 24. 25. 26. 27. 28. 29. 30. 31. 32. 33. 34. 35. /* Modelo de iluminacin ambiental.

*/ float4x4 world : World; float4x4 view : View; float4x4 projection : Projection; float4 colorMaterialAmbiental < string UIName = "Color Ambiental"; string UIWidget = "Color"; > = {0.05F, 0.05F, 0.05F, 1.0F}; float4 mainVS(float3 posicion : POSITION) : POSITION { float4x4 wvp = mul(world, mul(view, projection)); return mul(float4(posicion.xyz, 1.0F), wvp); } float4 mainPS() : COLOR { return colorMaterialAmbiental; } technique ModeloAmbiental { pass p0 { VertexShader = compile vs_3_0 mainVS(); PixelShader = compile ps_3_0 mainPS(); } }

La variable colorMaterialAmbiental es un vector de cuatro componentes escalares de tipo float, y cada uno de ellos representa los componentes RGBA del color. Enseguida de la declaracin de esta variable se puede ver la inclusin de un metadato encerrado entre < y >, y la inicializacin de la variable; esto con el fin de manipular con la mayor sencillez el color en FX Composer. HLSL descartar este metadato y no se tomar en cuenta en la aplicacin XNA, pero que repito, es para poder manejar las variables de una manera ms sencilla en FX Composer. El metadato UIName, de colorMaterialAmbiental le asigna un alias de tipo string al nombre, pero que ser ignorado por HLSL, empero que se reflejar en la interfaz grfica del usuario (GUI, por su soglas en ingls) de FX Composer. Se utiliza tambin, el string UIWidget para abrir una ventana llamada Color Picker en FX Composer, la cual ayuda a seleccionar un color, vase Ilustracin 7-1.

77

La inicializacin de la variable es como cualquier asignacin a un arreglo de flotantes en C; entre llaves y separado por comas se asignan los valores correspondientes, no olvide colocar el punto y coma al final de la asignacin.

Ilustracin 7-1 Color Picker

Las lneas 16 19 es la subrutina que servir para hacer los clculos correspondientes a cada vrtice que sea ledo por el shader, regularmente llamado vertex shader. Esta subrutina necesita datos de entrada, adems de las variables globales, que se obtienen a partir de los parmetros de sta, lnea 16. Ntese que la declaracin es parecida a las funciones del lenguaje de C, pero con la diferencia que sta debe recibir por lo menos una semntica y arrojar otra. Las semnticas son regularmente estructuras, cuyos componentes son semnticas predefinidas de HLSL. Por ejemplo:
struct VertexShaderEntrada { float4 Posicion : POSITION; float2 CoordenadaTextura : TEXCOORD0; }; struct VertexShaderSalida { float4 Posicion : POSITION; float2 CoordenadaTextura : TEXCOORD0; };

Algunas de las semnticas establecidas para las entradas son: POSITIONn TEXCOORDn NORMAL COLORn POSITION COLORn 27 TEXCOORDn

Y para datos de salida:

En donde n es nmero entero.


27

Para mayor informacin acerca de las semnticas, consulte la siguiente direccin: http://msdn.microsoft.com/enus/library/bb509647(v=VS.85).aspx

78

Tome en cuenta la integridad de los datos, pues a veces puede haber truncamiento de datos, provocando errores en tiempo de ejecucin. Tambin tome en cuenta que las semnticas deben ser las mismas en ambas estructuras de entrada como de salida. En este ejemplo no se manejan las semnticas como estructuras de entrada y de salida, por lo que en los parmetros de la funcin mainVS, lnea 16, se deben incluir despus del nombre de la variable y despus de los parmetros de la funcin, pues esto indica al compilador que el resultado arrojado por la subrutina ser de tipo POSITION. Por lo menos el vertex shader debe arrojar la posicin del vrtice, para que el pixel shader lo tome como una semntica de interpoladores como se muestra en la subrutina mainPS, lneas 22 25, tambin llamada pixel shader. Este vertex shacer toma como entrada la posicin del vrtice, y tambin puede agregarse otras, como las coordenadas de textura, la normal y la tangente del vrtice. En este ejemplo solo se necesita el valor de la posicin del vrtice para implementar el modelo de iluminacin ambiental. El valor arrojado por el vertex shader es de tipo float y que gracias a la semntica POSITION, es posible que el pixel shader pueda interpolarlo para su uso. Los interpoladores o variables Varying sirven para la comunicacin entre los vertex shader y pixel shaders. Estos interpoladores son para mantener la congruencia de la informacin, por ejemplo, en la Ilustracin 7-2 se muestra una superficie formada por tres vrtices v1, v2 y v3, y cada uno de ellos con sus respectivas normales nv1, nv2 y nv3. Estos datos slo son tiles cuando se est trabajando a nivel de vrtice, y que se recomienda que a este nivel se haga la mayora de los clculos, pues el nmero de veces que se ejecuta es menor que si se trabaja a nivel pxel. Para que el pxel shader, tambin llamado fragment shader, pueda hacerse de los datos correspondientes, debe interpolar la informacin recibida del vertex shader antes de comenzar cualquier clculo. La normal de color verde, nf, es la normal correspondiente a ese pxel y es el resultado de la interpolacin que se hace al momento de pasar los datos del vertex shader al pxel shader. Es por eso que se debe tener una buena integracin de datos en el momento de declarar las variables, y el orden de las semnticas correspondientes a las de tipo uniform.

Ilustracin 7-2 Interpolacin de vertex shader a pixel shader

En el vertex shader, mainVS, se harn las transformaciones que regularmente hace el pipeline. Cambiar del espacio de coordenadas locales a las coordenadas de mundo, luego a las coordenadas de vista y por ltimo se transforma a las coordenadas de proyeccin. Esta transformacin se hace sobre cada uno de los vrtices; para la transformacin se ocupan las matrices de mundo, vista y proyeccin; estas matrices las proporciona la API de XNA y DirectX. 79

En la lnea 18 se declara una matriz de 4 renglones por 4 columnas, que se inicializa con la multiplicacin 28 de las variables world, view y projection. La funcin mul es una funcin intrnseca de HLSL; multiplica dos matrices A y B siempre y cuando el nmero de columnas de la matriz A sea igual al nmero de renglones de la matriz B. As que est funcin trabaja de la misma manera que se hara una multiplicacin de matrices en algebra lineal. El resultado es una matriz cuya dimensin es igual al nmero de renglones de la matriz A por el nmero de columnas de la matriz B. Tmese en cuenta que la multiplicacin de matrices no es conmutativa, as que hay que cuidar el orden en que se le dan las matrices a la funcin intrnseca mul. Luego de haber obtenido la matriz wvp, se multiplica por la posicin del vrtice para hacer la transformacin de las coordenadas locales a las de proyeccin, lnea 19. Pero antes de hacer dicha operacin se debe homogenizar el vector de posicin del vrtice, esto es para poder realizar la multiplicacin. En este caso se crea un nuevo vector de cuatro elementos de tipo flotante, donde los primeros tres son iguales a las coordenadas x, y y z de la posicin del vrtice, y el ltimo es uno. Como valor de retorno del vertex shader es la posicin del vrtice transformado, utilizando la palabra reservada return. El pixel shader, mainPS, lneas 22 25, no tiene una semntica de entrada explcita, pues en este caso se toma como default la posicin del vrtice. Pero si tiene una explcita de salida que indica que regresar el color del pxel, este valor de retorno es el color que se toma a partir de la entrada de la aplicacin XNA, y en este caso de FX Composer, lnea 24. Aqu es importante aclarar que este color de retorno iluminara o coloreara de igual forma la geometra, esto representa el material ambiental del objeto y la ecuacin del modelo ambiental. Por ltimo, se tiene la tcnica con la cual se har pasar la informacin de la geometra, lneas 27 34, para su transformacin directa en el hardware programable. Se debe comenzar por escribir la palabra technique y enseguida el nombre, lnea 27. El nombre de la tcnica como el de las subrutinas puede contener caracteres alfanumricos y guin bajo. En el cuerpo de la la tcnica, encerrada entre { y }, se incluyen las pasadas por las que se harn las transformaciones de la informacin de la geometra, y estas deben comenzar con la palabra reservada pass, luego del nombre. Esto regularmente se utiliza para efectos ms elaborados, por lo que no se incluye en la mayora de los ejemplos de este texto, y que por el momento no se ver. La declaracin de las variables VertexShader y PixelShader se deben de hacer dentro de pass, lneas 31 y 32, cuya sintaxis debe cumplir con lo siguiente: PixelShader = compile ShaderTarget ShaderFunction(); VertexShader = compile ShaderTarget ShaderFunction();
Tabla 7-2

Parmetros XXXShader ShaderTarget ShaderFunction()

Descripcin Una variable shader, que representa el shader compilado. Puede PixelShader o VertexShader. Es el model shader contra el que se compila. Es una cadena ASCII que contiene el nombre de la funcin de entrada del shader; esta es la funcin que comienza la ejecucin cuando el shader es invocado. El () representa los argumentos del shader.

28

Para conocer todas las funciones intrnsecas que ofrece HLSL, consulte la siguiente pgina web: http://msdn.microsoft.com/enus/library/ff471376(v=VS.85).aspx

80

Los model shader, dependern del API con la cual se har su ejecucin, la GPU y el sistema operativo. Sin embargo, XNA no tiene soporte para todas las versiones de model shader, por lo que trabajara al igual que Direct3D 9, cuyos modelos de shaders se limita a las versiones 1, 2 y 3. En la tabla siguiente se enlista los 30 model shader que pueden ocuparse .
Tabla 7-3

29

Model Shader Shader Model 1 Shader Model 2 Shader Model 3

Shader Profile vs_1_1 ps_2_0, ps_2_x, vs_2_0,.vs_2_x ps_3_0, vs_3_0

Las funciones mainVS y mainPS que se ocupan para la declaracin de las variables shaders, no deben contener como argumentos las semnticas que ocupa la API como entradas, pero si debe especificarse aquellas que se declaren como uniform y que puedan servir como entradas extras para la activacin de cierto clculo. En este ejemplo las funciones de shaders contienen nicamente las semnticas que la API necesita como entrada, por lo que en las lneas 32 y 33 se deja vaco los argumentos. 7.2.2 Inciando FX Composer

FX Composer 2.5 es un IDE para el desarrollo de shaders orientado para artistas y programadores, ste ltimo es en el que se enfocar este texto, pues permite escribir directamente en su editor de texto, visualizar en tiempo real el resultado del shader y una interfaz sencilla para manipular las variables uniform que pueda tener el shader. En la Ilustracin 7-3 se muestra la interfaz grfica de FX Composer 2.5.

Antes de ejecutar el shader se debe saber el modelo de shader que puede ejecutar la GPU, por lo que es menester revisar las caractersticas del dispositivo grfico. 30 Para mayor informacin acerca de los model shader visite la pgina siguiente: http://msdn.microsoft.com/enus/library/bb509626(v=VS.85).aspx

29

81

Ilustracin 7-3 GUI de Fx Composer 2.5

Una vez iniciado FX Composer 2.5 cree un nuevo proyecto oprimiendo las teclas Ctrl+Mays+N, o en File\New Project como se ve en la Ilustracin 7-4, para abrir un cuadro de dilogo.

Ilustracin 7-4 Nuevo proyecto

En el cuadro de dilogo, Ilustracin 7-5, se tiene el TextBox Name y el ListBox Location, para darle nombre al proyecto y localidad en el dispositivo de almacenamiento. Es recomendable dejar habilitada la creacin del directorio del proyecto, pues esto permite una mejor organizacin. Para cambiar de localidad el proyecto el botn abrir otro cuadro de dilogo para buscar en dnde quisiera que se alojara.

Ilustracin 7-5 Ventana de dilogo

82

Una vez que se tiene el nombre del proyecto y la localidad, oprima el botn OK. En seguida se activarn iconos del ToolBar, y en el TabPage Render se activar el viewport que mostrar la geometra. La insercin de un nuevo Effect consiste en crear un nuevo shader en los diferentes lenguajes de shaders disponibles en FX Composer 2.5. En la barra de tareas oprima en la etiqueta Create\Add Effect, como se ve en la Ilustracin 7-6.

Ilustracin 7-6 Aadir nuevo efecto

Enseguida se abre una ventana de dilogo Effect Wizard, Ilustracin 7-7, para agregar un nuevo efecto al proyecto. En este caso slo se trabajar con HLSL FX por lo que se habilita la casilla son dicho nombre en el cuadro de dilogo.

Ilustracin 7-7 Ventana de dilogo para aadir nuevo efecto

Ya seleccionado, el perfil a crear, se oprime el botn Next para seguir con la seleccin de la plantilla Empty que ofrece FX Composer de una lista predeterminada que tiene, vase Ilustracin 7-8.

83

Ilustracin 7-8 Plantillas para HLSL

En este punto se debe dar un nombre al archivo fx que se crear y su localizacin. Despus de haber nombrado al archivo, oprima el botn Next. Este ltimo apartado es para darle nombre al efecto, se recomienda dejar el establecido por FX Composer, pues corresponder al asignado en el archivo *.fx, de la misma manera que el nombre del material, tambin se recomienda dejar activada la casilla Create a material for this effect. Oprima el botn Finish, vase Ilustracin 7-9.

Ilustracin 7-9 Ventana de dilogo para el nombre del efecto

En la parte de materiales de la GUI podr ver que se ha creado un nuevo material llamado Ambiental_Material, el cual solo corresponde a una muestra del efecto en una esfera. Como se ha seleccionado la plantilla Empty, la esfera es ms un crculo blanco. Haga doble clic sobre el material Ambiental_Material para poder editarlo, vase Ilustracin 7-10.

84

Ilustracin 7-10 Editor de texto de FX Composer

El editor de texto ofrece el realce de palabras reservadas, el nombre de las variables de HLSL y los comentarios. Borre todo el contenido del editor de texto y escriba el Cdigo 7-1 de este texto. Compile la aplicacin con la tecla F6 y vera el cambio inmediato en el material Ambiental_Material. Si tiene un error en tiempo de compilacin busque el error en las lneas transcritas en el editor de texto, pues este ejemplo ha sido probado perfectamente. Para ver el resultado en el viewport que se encuentra en la TabPage Render, primero obtenga el foco oprimiendo en la pestaa. Luego seleccione uno de los modelos predefinidos de FX Composer en la barra de tareas, o importe un modelo en el men File\Import En el viewport se visualiza una elefante con el material DefaultMaterial, sta no se puede editar pero si se manipular sus propiedades. Ahora hay que aadir el material Ambiental_Material al modelo, para esto arrastre el material de la seccin Materials hacia el elefante, y en automtico ver el cambio en el viewport, vase Ilustracin 7-11.

85

Ilustracin 7-11 Elefante con iluminacin ambiental

Para cambiar el Color Ambiental, seleccione de la parte Properties el parmetro con dicho nombre y se abrir el Color Picker para seleccionar el color con el ratn. Al seleccionar el color dentro del crculo y la intensidad en la barra, se ver el cambio inmediato en el viewport y en el material. 7.2.3 HLSL. Modelo de iluminacin ambiental con textura

La textura es uno de los parmetros que permiten dar al renderizado una mejor vista de los que queremos que aparente tal modelo. Es por eso que en este ejemplo slo se aadir una textura al shader anterior, y que en los siguientes modelos de iluminacin no sea necesario volver a explicar. Tambin se cambiar la manera en que se escriben las semnticas de entrada y salida para los shaders vertex y pixel. La declaracin de la variable texturaModelo de tipo Texture2D en el Cdigo 7-2, lnea 16, es la variable que contendr la textura provista por la API como entrada, la declaracin de sta es la misma que para cualquier variable uniform. Type Name
Tabla 7-4

Parmetro Type Name

Descripcin Es uno de los siguientes tipos: Texture1D, Texture1DArray, Texture2D, Texture2DArray, Texture3D, TextureCube. Cadena ASCII que identifica el nombre de la variable.

Despus de haber declarado la variable es recomendable escribir de inmediato el tipo de sampleo de la 31 textura. La siguiente sintaxis declara el estado del sampleo para Direct3D 9 .

Para mayor informacin acerca del tipo de muestreo de la textura en Direct3D 9 y Direct3D 10 visite la siguiente direccin web: http://msdn.microsoft.com/en-us/library/bb509644(VS.85).aspx

31

86

sampler Name = SamplerType{Texture = <texture_variable>; [state_name = state_value;]}


Tabla 7-5

Parmetro sampler Name SamplerType

Descripcin Palabra reservada que se requiere. Cadena ASCII nica que identifica el nombre de la variable sampler. [entrada] Tipo de muestreo que es uno de los siguientes: sampler, sampler1D, sampler 2D, sampler3D, samplerCUBE, sampler_state, SamplerState. Una variable de textura. La palabra reservada Texture es requerida para establecer la regla de la mano izquierda o derecha. Los corchetes de ngulo son para establecer la regla de la mano izquierda y si se omiten es para establecer la regla de la mano derecha. [entrada] Asignacin de estados opcional. Todas las asignaciones de estado pueden aparecer fuera del bloque encerrado por { y }. Cada asignacin ser separada con un punto y coma. La siguiente lista muestra los posibles nombres de estado: AddressU AddressV AddressW BorderColor Filter MaxAnisotropy MaxLOD MinLOD MipLODBias Los valores de los estados sern igual a los ofrecidos por Direct3D o 32 XNA

Texture = <texture_variable>

state_name = name_value

Las lneas 21 29 muestran el tipo de sampleo que se utiliza para la textura, y si se recuerda el Textura no ser necesario explicar lo estados y valores que puede tomar el tipo de sampleo. La declaracin de estructuras que le siguen al tipo de sampleo es opcional, pero es ms sencillo de entender, de escribir y de mantener. Estas estructuras representan la semntica de entrada y salida para el vertex shader, lneas 48 56, y como semntica de entrada para el pixel shader, lneas 58 68.

Para mayor informacin acerca de los valores que ofrece Direct3D consulte la siguiente direccin web: http://msdn.microsoft.com/en-us/library/bb173347(v=VS.85).aspx

32

87

Cdigo 7-2
1. 2. 3. 4. 5. 6. 7. 8. 9. 10. 11. 12. 13. 14. 15. 16. 17. 18. 19. 20. 21. 22. 23. 24. 25. 26. 27. 28. 29. 30. 31. 32. 33. 34. 35. 36. 37. 38. 39. 40. 41. 42. 43. 44. 45. 46. 47. 48. 49. 50. 51. 52. 53. 54. 55. 56. 57. 58. 59. 60. 61. 62. 63. 64. 65. 66. 67. 68. 69. /* Modelo de iluminacin ambiental. Carlos Osnaya Medrano */ float4x4 world : World; float4x4 view : View; float4x4 projection : Projection; float4 colorMaterialAmbiental < string UIName = "Color Ambiental"; string UIWidget = "Color"; > = {0.05F, 0.05F, 0.05F, 1.0F}; texture2D texturaModelo < string UIName = "Textura"; >; sampler modeloTexturaMuestra = sampler_state { Texture = <texturaModelo>; MinFilter = Linear; MagFilter = Linear; MipFilter = Linear; AddressU = Wrap; AddressV = Wrap; }; struct VertexShaderEntrada { float4 Posicion : POSITION; float2 CoordenadaTextura : TEXCOORD0; }; struct VertexShaderSalida { float4 Posicion : POSITION; float2 CoordenadaTextura : TEXCOORD0; }; struct PixelShaderEntrada { float2 CoordenadaTextura : TEXCOORD0; }; VertexShaderSalida mainVS(VertexShaderEntrada entrada) { VertexShaderSalida salida; float4x4 wvp = mul(world, mul(view, projection)); salida.Posicion = mul(entrada.Posicion, wvp); salida.CoordenadaTextura = entrada.CoordenadaTextura; return salida; }// fin del VertexShader mainVS float4 mainPS(PixelShaderEntrada entrada, uniform bool habilitarTextura) : COLOR { if(habilitarTextura) { float4 texturaColor = tex2D(modeloTexturaMuestra, entrada.CoordenadaTextura); colorMaterialAmbiental *= texturaColor; } return colorMaterialAmbiental; }// fin del PixelShader mainPS

88

70. 71. 72. 73. 74. 75. 76. 77. 78. 79. 80. 81. 82. 83. 84. 85. 86.

technique Texturizado { pass p0 { VertexShader = compile vs_3_0 mainVS(); PixelShader = compile ps_3_0 mainPS(true); } }// fin de la tcnica Texturizado technique Material { pass p0 { VertexShader = compile vs_3_0 mainVS(); PixelShader = compile ps_3_0 mainPS(false); } }// fin de la tcnica Material

Para que las estructuras puedan servir como semnticas, es necesario que todos sus campos sean semnticos. La estructura VertexShaderEntrada y VertexShaderSalida tienen los mismos campos, se ha dejado de esta manera para una mejor comprensin del cdigo, por lo que no es necesario escribir ambos. Estas estructuras albergan el campo de tipo float4 que representa la posicin del vrtice, y cuya semntica es POSITION. El segundo campo es de tipo float2, representa la coordenada de textura que contiene el vrtice, por lo tanto su semntica es TEXCOORD0. La estructura PixelShaderEntrada, lneas 43 46, contiene un slo campo de tipo float2 que representa la coordenada de textura del pxel; la semntica debe ser la misma que arroja como resultado el vertex shader, en este caso es TEXCOORD0. La subrutina mainVS toma como entrada la estructura VertexShaderEntrada y da como resultado una estructura VertexShaderSalida, esta sintaxis es igual que en el lenguaje C y C#, por lo que no se profundizara. Hay que recordar que estas subrutinas que se compilan como shaders deben tener como entradas y salidas semnticas de HLSL. En la lnea 50 se declara la variable salida de tipo VertexShaderSalida, que fungir como dato de salida del vertex shader. Enseguida se inicializan los campos de dicha variable, lneas 52 y 53. La coordenada de textura del vrtice de entrada se le asigna sin ninguna modificacin al campo CoordenadaTextura de la variable salida. Y por ltimo, se regresa como dato de salida la variable salida. El pixel shader mainPS, lneas 58 68, sigue ofreciendo como salida, el tipo de dato float4 que representa el color final del pxel, gracias a la semntica COLOR. A diferencia al ejemplo anterior, ste toma como parmetro de entrada una estructura y una variable de tipo uniform, que no tiene semntica; lo anterior servir para habilitar la textura, en el resultado final del pxel. Las variables que se den como argumentos en las subrutinas que se compilen como vertex o pixel shaders deben tener semntica, en dado caso de no hacerlo se debe incluir la palabra reservada uniform, antes del tipo de dato de la variable. El clculo necesario para tener como resultado final la combinacin de cualquier modelo de iluminacin, y la textura, es una multiplicacin uno a uno, es decir elemento por elemento, entre el arreglo de cuatro elementos que representa el color obtenido a partir de las operaciones del modelo de iluminacin, por el resultado de la funcin intrnseca tex2D de HLSL, lnea 63 65. tex2D(s, t) 33
Tabla 7-6

Parmetro
33

Descripcin

Para mayor informacin acerca de las funciones intrnsecas para textura, revise la siguiente pgina web: http://msdn.microsoft.com/en-us/library/ff471376(v=VS.85).aspx

89

s t

[entrada]Estado de muestreo de la textura. [entrada]Coordenada de textura.

Con el condicional if y la variable habilitarTextura de tipo bool, lnea 62, se procesan los datos correspondientes para texturizar el pxel, en caso contrario slo se pinta con el color ambiental. En este ejemplo se utilizan dos tcnicas Texturizado, lneas 70 77, y Material, lneas 79 86. Tambin se pudo haber utilizado una variable global para habilitar la textura, pero se ha dejado as para fines educativos. La compilacin del pixel shader mainPS, lneas 75 y 84, pasan como parmetro de entrada el valor true o false directamente. 7.2.4 Aadiendo nuevo efecto en FX Composer

Retomando el proyecto ya hecho en el dubtema 7.2.2, aada un nuevo efecto tomando la plantilla Empty. Luego borre todo el contenido del nuevo efecto y escriba el listado anterior, Ambiental con Textura. Compile el efecto y quite cualquier error de compilacin si as sucediese. Cambie el material del modelo en el viewport, arrastrando el material Ambiental con Textura al modelo tridimensional, ver que la geometra es de un slo color, esto es porque no tiene una textura como entrada. Haga clic en el parmetro Textura en la parte Properties de FX Composer 2.5, Ilustracin 7-12, seleccione la opcin Image para abrir un cuadro de dilogo llamado Textures, Ilustracin 7-13.

Ilustracin 7-12 Campo Textura

Luego oprima el icono Add Image, aquel con forma de cruz o el smbolo ms, en seguida se abrir otra ventana de dilogo para seleccionar las imgenes tomadas de memoria secundaria. Sin embargo, no todas podrn agregarse al shader, pero s estarn enlistadas en el cuadro de dilogo Textures, como lo muestra la Ilustracin 7-13. Una vez seleccionada la imagen oprima el botn OK y de inmediato ver en el viewport del TabPage Render que la textura est envolviendo al modelo.

90

Ilustracin 7-13 Texturas

FX Composer no ejecuta todas las tcnicas que el archivo fx contiene, pero s las compila. As que para ver el efecto que no se est ejecutando coment la tcnica que est ms arriba del archivo. Por ltimo, cambie los valores al color ambiental para ver el resultado final sobre la textura.

Ilustracin 7-14 Ambiental con textura

7.3 Iluminacin difusa


La iluminacin ambiental no es de gran inters, pues los colores son planos y no da una sensacin de volumen de los objetos. La iluminacin difusa junto con la ambiental obtiene ese sentido de volumen, gracias a las diferentes intensidades en cada vrtice o pxel, esta cantidad de luz reflejada depende del ngulo formado entre la normal del vrtice o pxel y el haz de luz incidente que proviene de una fuente de iluminacin, vase Ilustracin 7-15.

91

Ilustracin 7-15 ngulo formado entre el haz de luz y la normal de la superficie

La reflexin difusa o tambin llamada reflexin Lambertiana, no toma en cuenta la posicin del observador, por lo tanto es independiente de ste y, slo es proporcional al coseno del ngulo de incidencia de luz y la normal. La ecuacin de iluminacin difusa es: Donde es la intensidad de la fuente luminosa; es el coeficiente de reflexin difusa del material que vara entre cero y uno. Regularmente el rango del ngulo se encuentra entre 0 a 90, esto es con el fin de hacer autoocluyente la reflexin difusa, vase Ilustracin 7-16. = cos

Ilustracin 7-16 Autoocluyente

Para conocer el ngulo entre dos vectores, se utiliza la siguiente expresin: = cos | |

son dos vectores cualesquiera. Donde y

Despejamos el coseno del ngulo y obtenemos la siguiente expresin: 92

Ahora se toman los valores de los vectores y , donde el vector del haz de luz es unitario y el coseno el ngulo debe ser positivo, para ser autoocluyente la expresin quedara como: Donde max selecciona el valor mximo entre el producto punto y cero. La ecuacin de iluminacin difusa quedara escrita como: , 0 = cos = , 0

cos =

| |

Para obtener una iluminacin ms realista se agrega el modelo de iluminacin ambiental, al modelo difuso, esto se logra sumando ambos modelos de iluminacin. El vector del haz de luz depender del tipo de fuente de iluminacin. En este texto se manejaran tres tipos bsicos de fuentes, mismas que ofrece DirectX en su pipeline, Direccional, Puntual y Spot. Pero antes se reescribe la ecuacin anterior para adaptarlo al shader de los siguientes ejemplos. La intensidad de iluminacin y coeficiente de reflexin ambiental se sustituye por el color ambiental del material; la intensidad de iluminacin y coeficiente de reflexin difusa se sustituyen por el color difuso del material. = + , 0.0 , 0 = +

7.3.1

Fuente de iluminacin direccional

La fuente de iluminacin direccional es semejante a tener una fuente en el infinito con una cantidad constante de energa, por lo que no importa la distancia del objeto a la fuente. Tambin se puede considerar que el vector del haz de iluminacin direccional es paralelo para todos los vrtices o pixeles del modelo tridimensional. En la Ilustracin 7-17 se muestra que el vector de iluminacin es paralelo para todos los vrtices o pxeles del cubo.

Ilustracin 7-17 Iluminacin direccional

Por lo tanto, la nica informacin que se requiere de este tipo de fuente es su direccin; el vector unitario es el adecuado para este tipo de luz. Tambin se puede considerar el color de la fuente, pero en este ejemplo no se tomar en cuenta. Retomando la ecuacin de iluminacin, hasta ahora descrita, se reescribir para el shader de manera que sea prctico usar.

93

En los siguientes ejemplos los clculos necesarios para obtener el color final sobre el material y textura del modelo tridimensional se har por pxel, esto no significa que no se puedan hacer por vrtice, es ms, las operaciones que se hagan por pxel son muy similares a los que se haran por vrtice. As que si el lector quiere probar estos ejemplos sobre cada vrtice, se espera no le sea difcil de lograrlo. Tambin se ha tomado la decisin de dejar las operaciones por pxel, pues el resultado final es mejor, visualmente, pero requiere ms poder de GPU en comparacin si se hiciese por vrtice. En el siguiente enlistado similar al visto con anterioridad, se explicarn las cosas nuevas que se han agregado para el modelo de iluminacin difusa, reutilizando el ejemplo del modelo de iluminacin ambiental con textura.
Cdigo 7-3
1. 2. 3. 4. 5. 6. 7. 8. 9. 10. 11. 12. 13. 14. 15. 16. 17. 18. 19. 20. 21. 22. 23. 24. 25. 26. 27. 28. 29. 30. 31. 32. 33. 34. 35. 36. 37. 38. 39. 40. 41. 42. 43. 44. 45. 46. 47. 48. 49. 50. 51. 52. 53. 54. 55. /*

Modelo de iluminacin: Especular o Difusa. Tipo de fuente: Direccional. Tcnica empleada: PixelShader. Material clsico. */ float4x4 world : World; float4x4 view : View; float4x4 projection : Projection; float3 direccionLuz < string UIName = "Direccin de Luz"; >; float4 colorMaterialAmbiental < string UIName = "Material ambiental"; string UIWidget = "Color"; > = {0.05F, 0.05F, 0.05F, 1.0F}; float4 colorMaterialDifuso < string UIName = "Material difuso"; string UIWidget = "Color"; > = {0.24F, 0.34F, 0.39F, 1.0F}; texture2D texturaModelo < string UIName = "Textura"; >; sampler modeloTexturaMuestra = sampler_state { Texture = <texturaModelo>; MinFilter = Linear; MagFilter = Linear; MipFilter = Linear; AddressU = Wrap; AddressV = Wrap; }; struct VertexShaderEntrada { float4 Posicion : POSITION; float3 Normal : NORMAL; float2 CoordenadaTextura : TEXCOORD0; }; struct VertexShaderSalida { float4 Posicion : POSITION;

94

56. 57. 58. 59. 60. 61. 62. 63. 64. 65. 66. 67. 68. 69. 70. 71. 72. 73. 74. 75. 76. 77. 78. 79. 80. 81. 82. 83. 84. 85. 86. 87. 88. 89. 90. 91. 92. 93. 94. 95. 96. 97. 98. 99. 100. 101. 102. 103. 104. 105. 106. 107. 108. 109. 110. 111. 112. 113. 114. 115. 116. 117. 118. 119. 120. 121. 122. 123. 124. 125.

float2 CoordenadaTextura : TEXCOORD0; float3 WorldNormal : TEXCOORD1; float3 WorldPosition : TEXCOORD2; }; struct PixelShaderEntrada { float2 CoordenadaTextura : TEXCOORD0; float3 WorldNormal : TEXCOORD1; float3 WorldPosition : TEXCOORD2; }; VertexShaderSalida MainVS(VertexShaderEntrada entrada) { VertexShaderSalida salida; float4x4 wvp = mul(world, mul(view, projection)); salida.Posicion = mul(entrada.Posicion, wvp); salida.CoordenadaTextura = entrada.CoordenadaTextura; salida.WorldNormal = mul(entrada.Normal, world); salida.WorldPosition = mul(entrada.Posicion, world); return salida; }// fin del vertex shader MainVS float4 MainPS(PixelShaderEntrada entrada, uniform bool habilitarTextura) : COLOR { // modelo de iluminacin difusa // iluminacin difusa direccionLuz = normalize(direccionLuz); float reflexionDifusa = max(dot(-direccionLuz, entrada.WorldNormal), 0.0F); float4 difuso = colorMaterialDifuso * reflexionDifusa; float4 color; // color final color = colorMaterialAmbiental + difuso; // modelo de iluminacin especular

// texturizado if(habilitarTextura) { float4 colorTextura = tex2D(modeloTexturaMuestra, entrada.CoordenadaTextura); color *= colorTextura; } // color final color.a = 1.0F; return color; }// fin pixel shader MainPS technique Texturizado { pass P0 { VertexShader = compile vs_3_0 MainVS(); PixelShader = compile ps_3_0 MainPS(true); } } technique Material { pass P0 { VertexShader = compile vs_3_0 MainVS(); PixelShader = compile ps_3_0 MainPS(false); } }

95

En la declaracin de variables globales se han agregado la direccin de la luz, lneas 15 18, Cdigo 7-3, y el color del material difuso, lneas 26 30. En la estructura de entrada para la subrutina vertex shader, se ha agregado el campo Normal de tipo float3 y semntica NORMAL, lnea 49, que recibir la normal asociado al vrtice. Para la estructura VertexShaderSalida se ha aadido el campo WorldNormal de tipo float3 con semntica TEXCOORD1, lnea 57; y el campo WorldPosition de tipo float3 con semntica TEXCOORD2, lnea 58. Estos dos ltimos campos son para hacerle pasar al pixelshader de manera coherente los datos de la normal y posicin utilizando la semntica TEXCOORD. En la estructura PixelShaderEntrada se agregan los mismos campos WorldNormal y WorldPosition que los que contiene VertexShaderSalida, vase que tienen la misma semntica. Dentro de la subrutina MainVS, lneas 68 78, se inicializan los campos de la variable inmediata salida. Los nuevos campos de la estructura VertexShaderSalida deben contener la normal y posicin del vrtice en el espacio de coordenadas de mundo, para eso hay que multiplicar la normal y posicin del vrtice por la matriz de mundo world, lneas 74 y 75. La direccin de la luz debe ser normalizada, es decir, debe convertirse el vector a un vector unitario, pues lo nico que interesa de este tipo de fuente es la direccin de sta. La funcin intrnseca normalize regresa el vector de cualquier tamao normalizado. En la lnea 85 se le asigna a la misma variable direccionLuz, el resultado de la normalizacin. Adems con esto se asegura que el vector dado por la API sea unitario, tambin se pudo haber normalizado desde sta y evitarle el clculo a la GPU. La intensidad difusa, como se haba explicado con anterioridad, depende del coseno del ngulo formado entre la normal del vrtice o pxel y el vector de direccin de la fuente de iluminacin. Por lo que el valor mximo de la intensidad difusa ser cuando la direccin de luz sea paralela a la normal y el menor ser cuando sean ortogonales. Para hacer autoocluyente, como se indica en la ecuacin del modelo de iluminacin difusa, se utiliza la funcin intrinseca max, lnea 86, que devuelve el valor mximo entre los dos parmetros que recibe. El primer parmetro es el resultado de la funcin intrnseca dot que obtiene el producto punto entre dos vectores, y el primero de dot es direccinluz multiplicado por menos uno; el cambio de direccin de direccionLuz, es para darle coherencia entre la entrada del dato por parte del usuario y el clculo para la intensidad, pues lo que se pregunta es hacia a dnde apunta la luz, no de dnde proviene la luz. El segundo parmetro de la funcin max es cero, pues los valores que debe normalmente tomar la intensidad difusa es entre cero y uno. La asignacin del resultado de la funcin max a la variable reflexionDifusa de tipo float, lnea 86, es para una mejor comprensin del ejemplo, por lo que no es necesario declararla. Luego de haber obtenido la intensidad de iluminacin difusa, se debe de multiplicar por el color del material difuso para luego sumarlo al modelo de iluminacin ambiental. Primero se declara la variable difuso y se inicializa con la multiplicacin entre el escalar intesidadDifusa por el vector colorMaterialDifuso, lnea 87. La variable color, lnea 88, servir para almacenar el color final, a partir de la suma de los modelos de iluminacin. El comentario de la lnea 92 indica que a partir de ah se harn los operaciones para el modelo de iluminacin especular. Hasta este punto es todo lo nuevo en este cdigo, todo el resto es el mismo que en el ejemplo anterior. Cree un proyecto nuevo en FX Composer para probar el shader, si no conoce cmo crear uno, lea el subtema 7.2.2, si encuentra un error de compilacin, corrija y pruebe de nuevo.

96

Ilustracin 7-18 Difuso con textura

Como se puede ver en la Ilustracin 7-18 el modelo tiene un aspecto mucho mejor que en el visto en la Ilustracin 7-14. Pruebe con la tcnica Material, comentando la tcnica Texturizado, lneas 108 115, para poder apreciar mejor la sensacin de volumen del objeto, como se muestra en la Ilustracin 7-19.

Ilustracin 7-19 Difuso sin textura

7.3.2

Fuente de iluminacin puntual

La fuente de iluminacin puntual es similar a tener un foco incandescente en un cuarto totalmente oscuro. Esto hace que la direccin de la fuente de iluminacin al vrtice vare dependiendo de la posicin en la cual se encuentre la fuente y/o el vrtice. En la Ilustracin 7-20 se muestra el vector de posicin de la fuente y los vectores de cada vrtice o pxel. 97

Ilustracin 7-20 Fuente puntual

Otra caracterstica importante de este tipo de fuente, es la atenuacin. La atenuacin como en el mundo real, depender de la distancia entre el foco y el objeto; entre ms cerca se encuentre de la fuente mayor intensidad de luz reflejada se tiene; entre ms alejado del foco menor intensidad de luz se podr reflejar. Una tentativa para obtener esta atenuacin es la inversa del cuadrado de la distancia. = 1 2

Sin embargo, como lo indican los pioneros en graficacin por computadora, esto no siempre funciona bien. As que una forma til, permitiendo una mayor gama de efectos es: = 1 , 1 1 + 2 + 3 2

Donde 1 , 2 y 3 son constante definidas por el usuario. La primera constante evita que el denominador sea demasiado pequeo cuando la luz est cerca. La funcin min limita a un valor a uno, para asegurar que siempre se atene. Una manera de conocer qu valores deben tener las constantes de atenuacin es graficar la ecuacin anterior, como se muestre en la Ilustracin 7-21.

98

1.2 1 Factor de atenuacin 0.8 0.6 0.4 0.2 0 0 50 100 150 200 250 300 350 Distancia
Ilustracin 7-21 Factor de atenuacin de iluminacin. c1= 1, c2 = 0, c3 = 0.001

Regularmente los valores del factor de atenuacin deben estar entre cero y uno, este valor mximo puede manipularse a partir de la primera constante, las dems constantes es para disminuir el factor de atenuacin conforme aumenta la distancia. Tome en cuenta que estos valores, por lo menos para las constantes 2 y 3 deben ser menores que uno y mayores a cero. , 0 = +

Reescribiendo la ecuacin de iluminacin con el factor de atenuacin para el modelo de iluminacin difuso.

Luego se sustituye el miembro derecho del factor de atenuacin y tenemos la nueva ecuacin de iluminacin para la fuente tipo puntual y spot. = + 1 , 1 , 0 1 + 2 + 3 2

Muchas de las lneas del cdigo siguiente son iguales a las vistas en la fuente de iluminacin direccional, por lo tanto slo describirn aquellas que sean relevantes. En la declaracin de variables globales se tiene la posicin de la fuente de iluminacin como posicionLuz, una variable de tipo float3, lneas 15 18, Cdigo 7-4. Las constantes de atenuacin 1 , 2 y 3 y rango son variables de tipo float, lneas 32 y 33. La variable rango sirve para evaluar la atenuacin a la distancia establecida por rango, es decir, aquel modelo que se encuentre dentro de la distancia establecida por rango, ser afectada por la atenuacin, en dado caso que estuviera fuera de esa distancia la atenuacin sera cero, por lo que slo la iluminacin ambiental afectara el color final del material y textura. La variable atenuado de tipo bool, lneas 46 49, sirve para activar los clculos de atenuacin dentro de un condicional if. Las estructuras de entrada y salida para los shaders vertex y pixel, siguen siendo las mismas que en el ejemplo anterior. En el pixel shader, lneas 87 132, el nico cambio importante es el clculo de la direccin de la luz a partir de la posicin de dicha fuente, y los clculo de atenuacin. El primer paso es calcular la direccin de la luz para cada pxel, esto se logra restando el vector de posicin del pxel (o vrtice) menos el vector de posicin de la luz. Luego se normaliza dicho resultado y se le asigna a una nueva variable local, direccionLuz de tipo float3, lnea 91.

99

Vase que el clculo para la intensidad difusa, lnea 92, no se multiplica la direccin de luz por menos uno, esto es porque se busca de dnde proviene el haz y no hacia donde apunta. La variable color de tipo float4 se utiliza para almacenar el color final que resulte de las operaciones para el color difuso, especular y atenuacin. Cada vez que se termine con el clculo de cualquier modelo de iluminacin se le asignar el resultado a color como la suma entre el contenido de color y el resultado de la operacin del modelo, excepto con el primero, lnea 95. Para habilitar la atenuacin se hace uso del condicional if, lneas 102 120, y la variable uniform atenuado. Lo primero que hay que obtener es la distancia entre la fuente de iluminacin y el pxel, para luego compararlo con el rango ofrecido por el usuario. La funcin intrnseca distance obtiene la distancia entre dos vectores, siempre y cuando sean de la misma dimensin, es decir, que tengan el mismo nmero de elementos. Tambin se pudo haber obtenido el mdulo de la resta entre posicin de la fuente y el pxel, pero se ha dejado de esta manera para que el lector puedo comprender mejor el cdigo. El resultado arrojado por distance se guarda en la variable local distancia, lnea 105. En seguida se declara la variable de tipo float, atenuacin, lnea 107, que almacenar el factor de atenuacin. Ahora se compara la distancia entre la fuente al pxel y el rango establecido por el usuario, en caso que la distancia sea menor o igual al rango, se calcula el factor de atenuacin, lnea 114 y 115; en caso contrario el factor de atenuacin es cero. Luego se multiplica el factor por el modelo de iluminacin difuso, lnea 119. Despus del condicional que habilita la atenuacin, se suma el color del material ambiental al color final que se ha obtenido y luego se le asigna a la variable color, lnea 122.
Cdigo 7-4
1. 2. 3. 4. 5. 6. 7. 8. 9. 10. 11. 12. 13. 14. 15. 16. 17. 18. 19. 20. 21. 22. 23. 24. 25. 26. 27. 28. 29. 30. 31. 32. 33. 34. 35. 36. 37. 38. 39. 40. /*

Modelo de iluminacin: Especular o Difusa. Tipo de fuente: Puntual. Tcnica empleada: PixelShader. Material clsico. */ float4x4 world : World; float4x4 view : View; float4x4 projection : Projection; float3 posicionLuz < string UIName = "Posicin de Luz"; >; float4 colorMaterialAmbiental < string UIName = "Color ambiental"; string UIWidget = "Color"; > = {0.05F, 0.05F, 0.05F, 1.0F}; float4 colorMaterialDifuso < string UIName = "Color difuso"; string UIWidget = "Color"; > = {0.24F, 0.34F, 0.39F, 1.0F}; float c1, c2, c3; // constante de atenuacin float rango; // rango a evaluar la atenuacin texture2D texturaModelo; sampler modeloTexturaMuestra = sampler_state { Texture = <texturaModelo>; MinFilter = Linear; MagFilter = Linear;

100

41. 42. 43. 44. 45. 46. 47. 48. 49. 50. 51. 52. 53. 54. 55. 56. 57. 58. 59. 60. 61. 62. 63. 64. 65. 66. 67. 68. 69. 70. 71. 72. 73. 74. 75. 76. 77. 78. 79. 80. 81. 82. 83. 84. 85. 86. 87. 88. 89. 90. 91. 92. 93. 94. 95. 96. 97. 98. 99. 100. 101. 102. 103. 104. 105. 106. 107. 108. 109. 110. 111.

MipFilter = Linear; AddressU = Wrap; AddressV = Wrap; }; bool atenuado < string UIName = "Atenuado"; > = false; struct VertexShaderEntrada { float4 Posicion : POSITION; float3 Normal : NORMAL; float2 CoordenadaTextura : TEXCOORD0; }; struct VertexShaderSalida { float4 Posicion : POSITION; float2 CoordenadaTextura : TEXCOORD0; float3 WorldNormal : TEXCOORD1; float3 WorldPosition : TEXCOORD2; }; struct PixelShaderEntrada { float2 CoordenadaTextura : TEXCOORD0; float3 WorldNormal : TEXCOORD1; float3 WorldPosition : TEXCOORD2; };

VertexShaderSalida MainVS(VertexShaderEntrada entrada) { VertexShaderSalida salida; float4x4 wvp = mul(world, mul(view, projection)); salida.Posicion = mul(entrada.Posicion, wvp); salida.CoordenadaTextura = entrada.CoordenadaTextura; salida.WorldNormal = mul(entrada.Normal, world); salida.WorldPosition = mul(entrada.Posicion, world); return salida; }// fin del vertex shader MainVS float4 MainPS(PixelShaderEntrada entrada, uniform bool texturizado) : COLOR { float4 color; // modelo de iluminacin difusa float3 direccionLuz = normalize(posicionLuz - entrada.WorldPosition); float reflexionDifusa = max(dot(direccionLuz, entrada.WorldNormal), 0.0F); float4 difuso = reflexionDifusa * colorMaterialDifuso; // color final color = difuso; // modelo de iluminacin especular

// atenuacin if(atenuado) { // atenuacin float distancia = distance(entrada.WorldPosition, posicionLuz); // distancia de la fuente al vrtice float atenuacion; // Si la distancia entre la fuente y el vrtice se // encuentra entre el rango // ste se ver afectado por la atenuacin, en caso // contrario la atenuacin

101

112. 113. 114. 115. 116. 117. 118. 119. 120. 121. 122. 123. 124. 125. 126. 127. 128. 129. 130. 131. 132. 133. 134. 135. 136. 137. 138. 139. 140. 141. 142. 143. 144. 145. 146. 147. 148. 149. 150. 151. 152. 153.

// ser de cero. if(distancia <= rango) atenuacion = min(1 / (c1 + (c2 * distancia) + (c3 * pow(distancia, 2.0F))), 1.0F); else atenuacion = 0; // color final color *= atenuacion; }// fin del if // color final color += colorMaterialAmbiental; // texturizado if(texturizado) { float4 texturaColor = tex2D(modeloTexturaMuestra, entrada.CoordenadaTextura); color *= texturaColor; } // color final color.a = 1.0F; return color; }// fin pixel shader MainPs technique Texturizado { pass P0 { VertexShader = compile vs_3_0 MainVS(); PixelShader = compile ps_3_0 MainPS(true); } } technique Material { pass P0 { VertexShader = compile vs_3_0 MainVS(); PixelShader = compile ps_3_0 MainPS(false); } }

Cree un nuevo proyecto en FX Composer 2.5 para probar el Cdigo 7-4 y ver algo similar a la Ilustracin 7-22; si no conoce cmo generar un proyecto en FX Composer 2.5 lea Inciando FX Composer.

102

Ilustracin 7-22 Fuente puntual

Para saber que realmente se ha generado un tipo de fuente diferente, cambie de posicin del modelo. Esto se logra seleccionando el modelo en el viewport luego oprima la letra W y aparecer un gizmo con tres colores, azul, verde y rojo. Al acercar el cursor del ratn a alguna de la flechas del gizmo ver que estas cambian a un color blanco y el puntero cambia de figura. Cuando esto pasa haga clic y arrastre el puntero del mouse para mover el modelo en la direccin. Al cambiar de posicin el modelo, los vectores de direccin de iluminacin cambian y as tambin el color en cada pxel. Juegue con los valores de entrada del shader en la parte Parameters de FX Composer, sobre todo con las constantes, recomiendo tener cerca la grfica del factor de atenuacin para tener una mejor referencia de lo que se hace. 7.3.3 Fuente de iluminacin spot

La fuente de iluminacin spot es similar a tener una lmpara sorda en un cuarto oscuro, este tipo de fuente ilumina una regin en forma cnica, que gradualmente se atena dependiendo de la distancia del objeto a la posicin de la fuente y del ngulo de abertura de la lmpara. Las caractersticas de una fuente tipo spot estn representadas en la Ilustracin 7-23, donde la posicin de la fuente est representado por el vector de posicin , el objetivo de la luz o lugar hacia dnde apunta . la fuente, es el vector de posicin Adems de incluir la atenuacin de la distancia entre la fuente y el objeto, en la fuente spot existe la atenuacin respecto al ngulo de apertura interno y externo . Esto indica que el objeto que se encuentre dentro del ngulo interno no sufrir ninguna atenuacin por parte del cono. Si el objeto se encuentra entre el ngulo interno y el externo ser afectado por la atenuacin del cono. Y si el objeto est fuera del ngulo externo el nico modelo de iluminacin que puede afectarlo es el ambiental.

103

Ilustracin 7-23 Fuente de iluminacin spot

La manera de saber qu vrtice o pxel debe ser afectado por la fuente, es por medio del ngulo que existe entre la normal y el vector de direccin de la fuente hacia el vrtice o pxel. En la Ilustracin 7-24 ste ngulo es , el cual es menor cuando el vrtice o pxel se encuentran dentro del cono de iluminacin y, mayor cuando est fuera. Para comparar ngulos entre vectores se echara mano de la funcin trigonomtrica coseno y la relacin que existe entre sta y el producto punto. Las condiciones para conocer qu parte del objeto deben ser iluminados, atenuados o ignorados es la 34 siguiente : 0.0 cos cos = cos cos 1.0 cos cos

cos < cos < cos cos cos

34

http://wiki.gamedev.net/index.php/D3DBook:%28Lighting%29_Direct_Light_Sources

104

Ilustracin 7-24 Fuente spot

Y la ecuacin del modelo de iluminacin quedara expresada como: = + 1 , 1 , 0 1 + 2 + 3 2

Tmese en cuenta que la funcin coseno tiene su valor mximo cuando el ngulo es cero y menor cuando es radianes, esto por considerar que tambin es una fuente autoocluyente. As que la anterior 2 condicin acerca de la atenuacin es correcta. El exponente falloff es un valor tipo flotante que regularmente debe tomar valores positivos, en la Ilustracin 7-25 se tiene una grfica de la atenuacin angular, un nombre que de ahora en adelante le he propuesto para diferenciarla de la atenuacin de distancia.

105

1.2 1 Factor de atenuacion 0.8 0.6 0.4 0.2 0 0 10 20 30 ngulo


Ilustracin 7-25 Factor de atenuacin angular. falloff = 3, = 10, = 15

40

50

60

El Cdigo 7-5 tiene muchas cosas ya vistas en el primer ejemplo, Fuente de iluminacin direccional, se ha suprimido la variable uniform direccionLuz, y se ha modificado la subrutina MainPS. Las variables globales nuevas son los ngulos interno y externo, lneas 15 y 28 respectivamente, estas son de tipo float, y es importante que los valores que se den a estas variables sea en radianes, la API que es la encargada de ofrecer dichas entradas extras de informacin, ofrecer los datos ntegros para el shader. La posicin y el blanco de la luz spot, lneas 19 y 33, tambin son variables uniform que complementan los datos de entrada para el shader; ambos son de tipo float3. El exponente de atenuacin angular falloff, lnea 24, las constantes de atenuacin de distancia, lnea 49, y el rango lnea 50, son variables de tipo flotante que corresponden a las ecuaciones de atenuacin, todas ellas son las nuevas entradas de informacin por parte del usuario. En la subrutina MainPS, lneas 103 162, se hacen las operaciones correspondientes para cada pxel. Lo primero que se hace es declarar la variable color, lnea 105, para almacenar el resultado obtenido de cada uno de los modelos de iluminacin. , indica la direccin de la fuente spot, por lo tanto es la resultante de la resta entre el El vector unitario vector de posicin de luz y el vector de posicin del blanco de sta. En la lnea 106 se asigna dicha operacin a la variable direccionSpot. La direccin de la luz respecto al pxel es el vector unitario de la resta del vector de posicin de luz menos la posicin del pxel, lnea 108, que se almacena en la variable direccionLuz. Esta variable sirve para calcular la intensidad de la iluminacin difusa, lneas 109 112. Los cosenos de los ngulos internos y externos bien pudieron haberse calculado por parte de la API. Pero aqu se calculan con la funcin intrnseca cos, a cada ngulo, lneas 118 y 119, y se almacenan en nuevas variables tipo float, este exceso de declaraciones de variables slo es para fines didcticos, por lo que la optimizacin del cdigo se deja al lector como ejercicio. El coseno del ngulo es el producto punto entre direccionSpot y direccionLuz, recordando hacer autoocluyente se selecciona el valor mximo entre cero y el producto escalar, lnea 117. El condicional if, lneas 122 126, hacen la segunda evaluacin del condicional de atenuacin ngular, es decir, se pregunta si el ngulo se encuentra entre el ngulo interno y el externo, en dado caso que as sea se ejecuta la operacin de atenuacin angular y se multiplica el resultado por la variable color para luego volverla a asigna a la misma, lneas 124 y 125. 106

Si el ngulo no se encuentra entre y , se pregunta si es mayor que , en caso que as sea el factor de atenuacin es cero y se le multiplica la variable color para luego volver a asignarle la resultante al mismo color, lnea 129.
Cdigo 7-5
1. 2. 3. 4. 5. 6. 7. 8. 9. 10. 11. 12. 13. 14. 15. 16. 17. 18. 19. 20. 21. 22. 23. 24. 25. 26. 27. 28. 29. 30. 31. 32. 33. 34. 35. 36. 37. 38. 39. 40. 41. 42. 43. 44. 45. 46. 47. 48. 49. 50. 51. 52. 53. 54. 55. 56. 57. 58. 59. 60. 61. 62. 63. 64. /*

Modelo de iluminacin: Especular o Difusa. Tipo de fuente: Spot. Tcnica empleada: PixelShader. Material clsico. */ float4x4 world: World; float4x4 view : View; float4x4 projection : Projection; float anguloInterno < string UIName = "ngulo interno"; >;// ngulo interno en radianes float3 blancoLuz < string UIName = "Blanco de la luz"; >;// hacia dnde ilumina la luz float falloff < string UIName = "Fallof"; >;// exponente de atenuacin float anguloExterno < string UIName = "ngulo externo"; >;// ngulo externo, que se toma de referencia para atenuar la luz float3 posicionLuz < string UIName = "Posicin de la fuente"; >; float4 colorMaterialAmbiental // color de la luz ambiental < string UIName = "Luz Ambiental"; string UIWidget = "Color"; > = {0.07F, 0.07F, 0.07F, 1.0F}; float4 colorMaterialDifuso // color de la luz Difusa < string UIName = "Luz difusa"; string UIWidget = "Color"; > = {0.24F ,0.34F, 0.39F, 1.0F}; float c1, c2, c3; float rango; // constantes de atenuacin // rango a evaluar la atenuacin

texture2D texturaModelo; sampler modeloTexturaMuestra = sampler_state { Texture = <texturaModelo>; MinFilter = Linear; MagFilter = Linear; MipFilter = Linear; AddressU = Wrap; AddressV = Wrap; }; bool atenuado <

107

65. 66. 67. 68. 69. 70. 71. 72. 73. 74. 75. 76. 77. 78. 79. 80. 81. 82. 83. 84. 85. 86. 87. 88. 89. 90. 91. 92. 93. 94. 95. 96. 97. 98. 99. 100. 101. 102. 103. 104. 105. 106. 107. 108. 109. 110. 111. 112. 113. 114. 115. 116. 117. 118. 119. 120. 121. 122. 123. 124. 125. 126. 127. 128. 129. 130. 131. 132. 133. 134. 135.

string UIName = "Atenuado"; > = false; struct VertexShaderEntrada { float4 Posicion : POSITION; float3 Normal : NORMAL; float2 CoordenadaTextura : TEXCOORD0; }; struct VertexShaderSalida { float4 Posicion : POSITION; float2 CoordenadaTextura : TEXCOORD0; float3 WorldNormal : TEXCOORD1; float3 WorldPosition : TEXCOORD2; }; struct PixelShaderEntrada { float2 CoordenadaTextura : TEXCOORD0; float3 WorldNormal : TEXCOORD1; float3 WorldPosition : TEXCOORD2; }; VertexShaderSalida MainVS(VertexShaderEntrada entrada) { VertexShaderSalida salida; float4x4 mvp = mul(world, mul(view, projection)); salida.Posicion = mul(entrada.Posicion, mvp); salida.CoordenadaTextura = entrada.CoordenadaTextura; salida.WorldNormal = mul(entrada.Normal, world); salida.WorldPosition = mul(entrada.Posicion, world); return salida; }// fin del Vertex Shader MainVS // Pixel Shader float4 MainPS(PixelShaderEntrada entrada, uniform bool texturizado) : COLOR { float4 color; float3 direccionSpot = normalize(posicionLuz - blancoLuz); float3 direccionLuz = normalize(posicionLuz - entrada.WorldPosition); float reflexionDifusa = max((dot(direccionLuz, entrada.WorldNormal)), 0.0F); float4 difusa = colorMaterialDifuso * reflexionDifusa; // color final color = difusa; // modelo de iluminacin especular

// datos para float cosAlfa float cosTeta float cosFi =

la atenuacin = max(dot(direccionSpot, direccionLuz), 0.0F); = cos(anguloInterno); cos(anguloExterno);

// el ngulo alfa es mayor al ngulo interno y menor al ngulo externo if(cosAlfa < cosTeta && cosAlfa > cosFi) { float facAteAng = pow((cosAlfa - cosFi) / (cosTeta - cosFi), falloff); color *= facAteAng; } else if(cosAlfa <= cosFi) // el ngulo alfa es mayor al ngulo externo { color *= 0.0F; }// fin del else // atenuacin respecto a la distancia del foco if(atenuado) { // atenuacin difusa

108

136. 137. 138. 139. 140. 141. 142. 143. 144. 145. 146. 147. 148. 149. 150. 151. 152. 153. 154. 155. 156. 157. 158. 159. 160. 161. 162. 163. 164. 165. 166. 167. 168. 169. 170. 171. 172. 173. 174. 175. 176. 177. 178. 179. 180.

float distancia = distance(entrada.WorldPosition, posicionLuz); // distancia de la fuente al vrtice float facAteDist; // Si la distancia entre la fuente y el vrtice se encuentra entre el rango // ste se ver afectado por la atenuacin, en caso contrario la // atenuacin ser de cero. if(distancia <= rango) facAteDist = min(1 / (c1 + (c2 * distancia) + (c3 * pow(distancia, 2.0F))), 1.0F); else facAteDist = 0; // color final color *= facAteDist; }// fin del if // color final color += colorMaterialAmbiental; if(texturizado) { float4 colorTextura = tex2D(modeloTexturaMuestra, entrada.CoordenadaTextura); color *= colorTextura; } color.a = 1.0F; return color; }// fin del Pixel Shader MainPS technique Texturizado { pass P0 { VertexShader = compile vs_3_0 MainVS(); PixelShader = compile ps_3_0 MainPS(true); } } technique Material { pass P0 { VertexShader = compile vs_3_0 MainVS(); PixelShader = compile ps_3_0 MainPS(false); } }

Estas son las nuevas parte en el pxel shader MainPS, en comparacin con los anteriores ejemplos de fuentes de iluminacin. Cabe aclarar que algunas restricciones a los datos de entrada para el shader, se pueden hacer desde la API, dejando lo ms importante en el shader. Cree un nuevo proyecto en FX Composer 2.5, para saber cmo, lea Inciando FX Composer de este texto. Transcriba el Cdigo 7-5 y corrija cualquier error de compilacin si as sucediese. Pruebe con los siguientes valores en los parmetros del shader: ngulo interno: 0.01 Blanco de la luz: 0 0 0 Falloff: 0.8 ngulo externo: 0.1 Posicin de la fuente: 0 10 0 Luz ambiental: (al gusto) Luz difusa: (al gusto) c1 : 1 c2: 0 c3: 0.001 109

Rango: 20 texturaModelo: (al gusto) Atenuado: true

Pruebe con el plano ofrecido por FX Composer en la barra de tareas, para poder observar que se crea un crculo en donde se ilumina por el spot y despus solo es por la luz ambiental. Si con la textura no alcanza a distinguir muy bien est definicin solo cambie el valor de los colores de los materiales ambiental o difuso, de preferencia tome un color ms oscuro para el ambiental. Otra manera es ejecutar la tcnica Material del shader, as que comente la tcnica Texturizado y podr ver algo similar a la Ilustracin 7-26.

Ilustracin 7-26 Fuente Spot

Pruebe con otros modelos y muvalos de lugar para ver que en realidad se trata de una fuente spot.

7.4 Iluminacin especular


La iluminacin especular se puede apreciar en toda superficie brillante, como los plsticos o metales. La mxima iluminacin se encuentra en un punto llamado highlight generado por la reflexin especular, este punto de iluminacin regularmente es del color de la fuente. Otra caracterstica de la iluminacin especular es que el highlight se mueve cuando el observador cambia de posicin, esto es como si estuviera siguiendo al observador.

Ilustracin 7-27 Reflexin especular

Adems, la luz slo se refleja en la direccin que es la inversa del haz de luz respecto a la normal . La intensidad de la reflexin expecular depende del ngulo entre el vector de reflexin y el vector de direccin del observador . Esto indica que el valor mximo de reflectancia se tiene cuando es cero y decrece cuando aumenta. La cada es dada aproximadamente por: Donde n es el exponente de reflexin especular del material. El rango de valores que toma n regularmente es de uno a varios cientos, dependiendo el material. 110 cos

Este trmino manejado por Warnock, supona que la reflexin estaba en la misma posicin que el punto del observador, por lo que no tena un resultado apropiado, y no fue sino hasta que Phong Bui Tuong considero al observador y las luces en diferentes posiciones. La caracterstica que aadi Phong es que la luz reflejada especularmente depende del ngulo de incidencia . La fraccin de luz reflejada suele asignarse como la constante , coeficiente de reflexin especular del material, que regularmente vara entre 0 y 1. As el modelo de iluminacin queda completo con la reflexin especular: , 0 + cos = +

El clculo del vector de reflexin , puede obtenerse a partir de la Ilustracin 7-28, como la suma del , donde es un vector auxiliar ms el vector , conocido como componente vectorial de sobre . escalar y es el vector unitario de = + (1)

Ilustracin 7-28 Vector auxiliar

Dado que el vector es el resultado de la resta entre menos el vector de direccin del haz de luz . El escalar es el componente escalar de sobre , que est dado por la siguiente expresin: Sustituyendo la ecuacin (3) en (2) se tiene:

(2)

= | |

(3)

Una vez sustituyendo las ecuaciones (3) y (4) en (1), el vector de reflexin estar dado por la siguiente expresin: = + | | | | | | | | | | | |

= | | | |

(4)

Como el mdulo de la normal es igual a uno, el vector estar dado por: Una vez ms reescribiendo la ecuacin del modelo de iluminacin se tiene en trminos de la normal y el haz de luz. , 0.0 , 0.0 + max 2 = +

= 2

= 2( )

111

La manera de seleccionar el valor adecuado para el exponente de reflexin especular y el coeficiente de reflexin especular es graficar cos , como se muestra en la Ilustracin 7-29. El exponente hace que la reflexin sea suave o puntual; el coeficiente especular slo aumenta o disminuye la intensidad de la reflexin. 1.2 1 Reflexin especular 0.8 0.6 0.4 0.2 0 -2 -1.5 -1 -0.5 0 ngulo
Ilustracin 7-29 Reflexin especular n = 3

0.5

1.5

Como hasta ahora, se reescribir la ecuacin del modelo de iluminacin para la implementacin del shader. La intensidad de iluminacin y el coeficiente de reflexin ambiental se sustituyen por el color ambiental del material; la intensidad de iluminacin de la fuente y el coeficiente de reflexin difusa del material se sustituye por el color difuso del material; y se aade el color especular del material, dando como resultado la siguiente expresin: = + , 0.0

Como la reflexin especular es el ltimo modelo de iluminacin que se suma, se reutilizaran los ejemplos de las fuentes de iluminacin de la reflexin difusa anteriores. 7.4.1 Reflexin especular. Actualizando cdigos de fuentes de iluminacin

, 0.0 + 2

En los enlistados de los ejemplos Fuente de Iluminacin Direccional, Cdigo 7-3, Puntal, Cdigo 7-4, y Spot, Cdigo 7-5, vistos anteriormente se debe agregar las siguientes variables globales para la reflexin especular.
Cdigo 7-6
1. 2. 3. 4. 5. 6. 7. 8. 9. 10. 11. 12. 13. float ks < string UIWidget = "slider"; float UIMin = 0.0F; float UIMax = 10.0F; float UIStep = 0.05F; string UIName = "Coeficiente de reflexin especular"; > = {0.05F}; float n < string UIWidget = "slider"; float UIMin = 0.0F;

112

14. 15. 16. 17. 18. 19. 20. 21. 22. 23. 24. 25. 26. 27. 28. 29. 30.

float UIMax = 128.0F; float UIStep = 1.0F; string UIName = "Exponente de reflexin especular"; > = 5.0F; float4 colorMaterialEspecular < string UIName = "Material especular"; string UIWidget = "Color"; > = {1.0F, 1.0F, 1.0F, 1.0F}; float3 posicionCamara < string UIName = "Posicin cmara"; >; bool modIluEsp < string UIName = "Modelo Iluminacin Especular"; > = false;

En la declaracin del coeficiente y exponente de reflexin especular, lneas 1 8 y 10 17 respectivamente, se hace uso de una nueva anotacin. sta sirve para hacer aparecer un control TrackBar horizontal, o Slider, en la GUI de FX Composer, vase ilustracin 7 31.

Ilustracin 7-30 Slider de FX Composer

La declaracin de este control dentro de la anotacin es de tipo string con nombre UIWidget e inicializado como slider. Estas anotaciones deben escribirse como estn definidas en el Standard 35 Annotations and Semantics (SAS) . Las siguientes anotaciones despus de la declaracin del control son los parmetros de ste, como es el valor mnimo, mximo y el paso al cual cambiar el slider. Aunque no es necesario escribir dichas anotaciones a las variables globales, ha sido menester hacerlo en FX Composer para controlar los valores de una manera ms sencilla. La variable modIluEsp de tipo bool, lnea 27, es para activar la reflexin especular en el condicional if, por default estar inhabilitada para disminuir el clculo por pxel. Algunos recomiendan habilitar e inhabilitar este modelo de iluminacin cundo sea necesario. En cada uno de los ejemplos Fuentes de Iluminacin se dej un comentario que dice // modelo de iluminacin especular, enseguida debe escribir el siguiente cdigo para las operaciones de reflexin especular.
Cdigo 7-7
1. 2. 3. 4. 5. 6. 7. 8. 9. 10. 11. if(modIluEsp) { // iluminacin especular float3 reflexion = normalize(2 * entrada.WorldNormal * max(dot(direccionLuz, entrada.WorldNormal), 0.0F) - direccionLuz); float3 direccionCamara = normalize(posicionCamara - entrada.WorldPosition.xyz); float intensidadEspecular = pow(max(dot(reflexion, direccionCamara), 0.0F), n); float4 especular = ks * intensidadEspecular * colorMaterialEspecular; color += especular * reflexionDifusa; }// fin del if

En la lnea 4, del Cdigo 7-7, se calcula el vector de reflexin especular, slo hay que recordar que para la fuente direccional se debe multiplicar la direccin de la luz por menos uno.
float3 reflexion = normalize(2 * entrada.WorldNormal *

Para mayor informacin acerca de las anotaciones usando SAS consulte la siguiente direccin electrnica: http://developer.nvidia.com/object/using_sas.html

35

113

max(dot(-direccionLuz, entrada.WorldNormal), 0.0F) + direccionLuz);

La direccin del observador, representado en las ecuaciones como el vector , es el vector unitario de la resta entre el vector de posicin de la cmara menos el vector de posicin del pxel, lnea 6.

Por ltimo, se hace uso de la reflexin especular para obtener el modelo de iluminacin completo, lneas 7 10. La multiplicacin de la reflexin difusa por la reflexin especular, lnea 8, limita el brillo a la parte iluminada por la fuente en cuestin, si se omite dicha operacin el objeto seguira brillando en las partes no iluminadas.

Vuelva a compilar los shaders en FX Composer y juegue con los valores de la reflexin especular para obtener un material parecido al mostrado en la ilustracin 7 32.

Ilustracin 7-31 Reflexin especular

7.5 Mltiples fuentes de iluminacin


Tomando como ejemplo el mundo real, la luz proviene de diferentes fuentes de iluminacin como el sol, el foco, la lmpara de escritorio, la luz del monitor, etctera. La suma de todas esas fuentes provoca diferentes tipos de resultados sobre los objetos que tienen su propio color. Estos colores propios son colores de material Ambiental Difuso Especular

Hasta el momento el color de la fuente no afectaba el modelo de iluminacin, y se limitaba a tomar el color del material para multiplicarlo por la reflexin ambiental, difusa o especular, se puede decir que el color de la fuente siempre fue blanco y reflejaba el color real del material. Tambin se consider una sola fuente para iluminar el modelo tridimensional, lo que en la prctica resultara no comn. En el siguiente ejemplo se aplican los tres tipos de fuentes y de cada una de ellas existe ms de una. Es decir, existirn n luces direccionales, n puntual y n tipo spot. El nmero de luces de cada tipo ser el mismo para todas. El modelo de iluminacin ambiental se aplicar una vez a la geometra, por lo que no se tomar en cuenta para el clculo de cada luz.

114

7.5.1

Multi-pass rending
36

Multi pass rendering es el proceso de renderizar diferentes atributos de nuestra escena por separado.

A veces los efectos ms complejos necesitan de varias pasadas para obtener el resultado deseado. Al referirse como pasada, indica que se dibuja ms de una vez, y cada una de ellas contribuye a generar un efecto ms elaborado y enriquecido. En este ejemplo que sigue, se crean dos PixelShader. El primero obtiene la iluminacin ambiental y el segundo hace los clculos necesarios para sumar todas las fuentes de iluminacin al render anterior.

Ilustracin 7-32 Suma de imgenes

En la Ilustracin 7-32 se logra ver que la tcnica de multi pass rendering es la suma de cada uno de los renderizados, o tambin se puede ver como una sobre posicin de cada uno de ellos. En la segunda pasada se habilita el Alpha blending para combinar el modelo de iluminacin ambiental con la suma de las fuentes de iluminacin. Recurdese que para el blending se debe cambiar el modo de renderizado de la tarjeta grfica, y esto se logra desde XNA o si lo prefieren desde el shader. En este caso se le dejar a XNA hacer los cambios necesarios para cambiar las propiedades del dibujado. 7.5.2 Ejemplo Multiluces

En este ltimo ejemplo de shaders, se reutiliza parte del cdigo de los anterios programas en HLSL, y se crea por fin estructuras que representan las fuentes de iluminacin direccional, puntual y de spot. Estas estructuras permitirn generar varias fuentes de iluminacin, por lo que lo hace un shader de iluminacin bsico lo ms parecido a lo que el fixed pipeline de DirectX u OpenGL ofrecen. La estructura de la fuente direccional LuzDireccional, lneas 87 -92, Cdigo 7-8, tiene como campos la direccin de la fuente, el color y la opcin de encender o apagarla. La estructutra de la fuente puntual LuzPuntual, lneas 94 102, tiene los campos de posicin de la fuente, el color, el rango de alcance, las constantes de atenuacin, la opcin de habilitar o inhabilitar la atenuacin y la posibilidad de encender o apagar la luz. La fuente de tipo spot representada por la estructura LuzSpot, lneas 104 116, tiene como campos la posicin de la fuente, el blanco de hacia dnde apunta, el color, el ngulo interno y externo de la fuente; el exponente de atenuacin de abertura de la fuente, el rango de alcance, las constantes de atenuacin, la opcin de habilitar o inhabilitar la atenuacin por distancia de la fuente, y la posibilidad de encender o apagar la luz. Ntese que no se ocup el tipo de dato booleano en los campos Encender y Atenuar en las estructuras de las fuentes. Esto debido a que el compilador de shaders de Microsoft, limita a este tipo de dato a un determinado nmero; lo que vara entre FX Composer y XNA; por lo tanto se han dejado como tipos enteros. El lmite de operaciones por fuente se lmita por la variable numLuces, lnea 118, que sirve como variable de escape en los ciclos for. Los arreglos de estructuras de las fuentes, son representadas por las variables:
36

Traduccin hecha a partir de http://www.3drender.com/light/compositing/index.html

115

direccionales[], lnea 259 puntuales[], lnea 260 spots[], lnea 261

La reservacin de memoria para cada una debe hacerse en tiempo de compilacin, y como se haba mencionado anteriormente depender de la versin del compilador, por lo que el nmero mximo permitido podra variar. El listado presenta un vertex shader, MainVS ,lneas 121 132, que ser suficiente para el multi pass rendering. Es aqu en donde se harn las transformaciones que hace el fixed pipeline, cosa que en ejemplos anteriores ya se explic. Vase que no existe una estructura como parmetro en el vertex shader, por lo que cada variable de entrada desde XNA, se especifica su semntica. La caraterstica particular de un multi pass rendering, es que se tienen diferentes pixel shaders, para cada pasada del shader, y rara vez diferentes vertex shaders. En este ejemplo se tienen dos pixel shaders, MainPSAmbiental, lneas 135 144, y MainPS, lneas 265 - 296. El primero representa el modelo de iluminacin ambiental y el segundo los modelos de iluminacin difuso o especular con la suma de cada una de las fuentes. En el pixel shader MainPSAmbiental, se multiplica el color del material ambiental por la textura si se ha habilitado la texturizacin, en caso contrario se pasa solo el color. En el pixel shader MainPS se multiplica el color difuso por la textura, si sta se ha habilitado. Luego se pasa por un bucle for para calcular cada una de las fuentes que estn encendidas. Esto se logra con tres condicionales if, cada uno verifica si el campo Encender es diferente de cero, en cada una de las distintas fuentes. Dependiendo el tipo de fuente se realizan las operaciones correspondientes con ayuda de de tres funciones: FuenteDireccional, FuentePuntual y FuenteSpot. Cada una de ellas devuelve un color que ser sumado en una variable local en el MainPS y ese ser el color final. La funcin FuenteDireccional, lneas 192 199, toma como parmetros la estructura LuzDireccional, la posicin del vrtice, la normal y el color del material, ste ltimo es el color de la suma del color difuso multiplicado por el de textura. Como todas las funciones hacen uso de los clculos del modelo de reflexin Phong, se ha escrito una funcin que devuelve el color de dicho modelo. La funcin, con el mismo nombre del modelo, lneas 176 189, toma como entradas la direccin de la luz, la posicin del vrtice, la normal, el color de luz y el color del material. Las operaciones son las necesarias para obtener la reflexin difusa y la reflexin especular, siempre y cuando est habilitado. Tomando el principio de divide y venceras, se ha construido una funcin llamada ReflexionEspecular, para hacer legible el programa. La funcin devuelve el color especular y toma como parmetros la direccin de luz, la posicin del vrtice, la normal y el color de luz. Las operaciones que realizan las funciones Phong y ReflexinEspecular, ya se explicaron en los ejemplos anteriores, por lo que si el lector tiene dudas por favor de regrese a consultarlos los apartados: Iluminacin difusa e Iluminacin especular. La funcin FuentePuntual, lneas 201 217, toma como parmetros la estructura LuzPuntual, la posicin del vrtice, la normal y el color del material. La nica entrada que cambia cuando se llama a la funcin Phong, es la direccin de luz, vase lnea 205. Adems esta fuente de iluminacin puede atenuarse dependiendo la distancia que se encuentre el vrtice de la fuente y del campo que habilita dicha operacin, lneas 210 214. Como la fuente spot tambin puede ser atenuada de esta forma, se ha escrito una funcin que devuelve un escalar que indica la atenuacin de la luz. En la lneas 147 161, la funcin AtenuarFuente realiza los clculos necesarios, y ya vistos en ejemplos anteriores, que permiten disminuir la intensidad de luminancia de la fuente. Las entradas de esta funcin son la posicin del vrtice, la posicin de la luz, el rango en que se permite la atenucin y las constantes de atenuacin. La funcin FuenteSpot, lneas 219 258, toma prcticamente el cdigo del shader descrito en el tema Fuente de iluminacin spot, con la diferencia que separ los clculos necesarios para limitar la iluminacin, y los clculos del modelo iluminacin Phong. 116

Las tcnicas, lneas 298 -325, Texturizado y Material, ambas tiene dos pasadas llamadas Ambiental y MultiLuces. La primera llama el vertex shader MainVS y el pixel shader MainPSAmbiental. Esta primera pasada slo colorea el modelo tridemensional con el color ambiental, y dependiendo de la tcnica habilita o inhabilita la textura. En la segunda pasada vuelve a llamar al vertex shader MainVS, pues es necesario realizar el renderizado una vez ms; y como pixel shader se llama MainPS que permite la suma de varias fuentes de luz en el mismo pxel.
Cdigo 7-8
1. 2. 3. 4. 5. 6. 7. 8. 9. 10. 11. 12. 13. 14. 15. 16. 17. 18. 19. 20. 21. 22. 23. 24. 25. 26. 27. 28. 29. 30. 31. 32. 33. 34. 35. 36. 37. 38. 39. 40. 41. 42. 43. 44. 45. 46. 47. 48. 49. 50. 51. 52. 53. 54. 55. 56. 57. 58. /*

Multiples luces. Modelo de iluminacin: Especular o Difusa. Tipo de fuente: Direccional, Puntual y Spot. Tcnica empleada: PixelShader. Material clsico. */ float4x4 world: World; float4x4 view : View; float4x4 projection : Projection; float3 posicionCamara < string UIName = "Posicin cmara"; >; float ks < string UIWidget = "slider"; float UIMin = 0.0F; float UIMax = 10.0F; float UIStep = 0.05F; string UIName = "Coeficiente de reflexin especular"; > = {0.05F}; float n < string UIWidget = "slider"; float UIMin = 0.0F; float UIMax = 128.0F; float UIStep = 1.0F; string UIName = "Exponente de reflexin especular"; > = 5.0F; float4 colorMaterialAmbiental < string UIName = "Material ambiental"; string UIWidget = "Color"; > = {0.07F, 0.07F, 0.07F, 1.0F}; float4 colorMaterialDifuso < string UIName = "Color Material"; string UIWidget = "Color"; > = {0.24F ,0.34F, 0.39F, 1.0F}; float4 colorMaterialEspecular < string UIName = "Material especular"; string UIWidget = "Color"; > = {1.0F, 1.0F, 1.0F, 1.0F}; bool habiEspec < string UIName = "Modelo Iluminacin Especular";

117

59. 60. 61. 62. 63. 64. 65. 66. 67. 68. 69. 70. 71. 72. 73. 74. 75. 76. 77. 78. 79. 80. 81. 82. 83. 84. 85. 86. 87. 88. 89. 90. 91. 92. 93. 94. 95. 96. 97. 98. 99. 100. 101. 102. 103. 104. 105. 106. 107. 108. 109. 110. 111. 112. 113. 114. 115. 116. 117. 118. 119. 120. 121. 122. 123. 124. 125. 126. 127. 128. 129.

> = false; texture2D texturaModelo; sampler modeloTexturaMuestra = sampler_state { Texture = <texturaModelo>; MinFilter = Linear; MagFilter = Linear; MipFilter = Linear; AddressU = Wrap; AddressV = Wrap; }; struct VertexShaderSalida { float4 Posicion : POSITION; float2 CoordenadaTextura : TEXCOORD0; float3 WorldNormal : TEXCOORD1; float3 WorldPosition : TEXCOORD2; }; struct PixelShaderEntrada { float2 CoordenadaTextura : TEXCOORD0; float3 WorldNormal : TEXCOORD1; float3 WorldPosition : TEXCOORD2; }; struct LuzDireccional { float3 Direccion; float4 Color; int Encender; }; struct LuzPuntual { float3 Posicion; float4 Color; float Rango; float C1, C2, C3; int Atenuar; int Encender; }; struct LuzSpot { float3 Posicion; float3 Blanco; float4 Color; float AnguloInterno; float AnguloExterno; float Falloff; float Rango; float C1, C2, C3; int Atenuar; int Encender; }; int numLuces = 4; // Vertex Shader VertexShaderSalida MainVS(float3 posicion : POSITION, float3 normal : NORMAL, float2 coordenadaTextura : TEXCOORD0) { VertexShaderSalida salida; float4x4 mvp = mul(world, mul(view, projection)); salida.Posicion = mul(float4(posicion, 1.0F), mvp); salida.WorldNormal = mul(normal, world); salida.WorldPosition = mul(float4(posicion, 1.0F), world); salida.CoordenadaTextura = coordenadaTextura;

118

130. 131. 132. 133. 134. 135. 136. 137. 138. 139. 140. 141. 142. 143. 144. 145. 146. 147. 148. 149. 150. 151. 152. 153. 154. 155. 156. 157. 158. 159. 160. 161. 162. 163. 164. 165. 166. 167. 168. 169. 170. 171. 172. 173. 174. 175. 176. 177. 178. 179. 180. 181. 182. 183. 184. 185. 186. 187. 188. 189. 190. 191. 192. 193. 194. 195. 196. 197. 198. 199. 200.

return salida; }// fin del vertexshader MainVS // Pixel Shader que obtiene la iluminacin ambiental, con o sin textura float4 MainPSAmbiental(PixelShaderEntrada entrada, uniform bool habiTextura) : COLOR { float4 color = colorMaterialAmbiental; if(habiTextura) { color *= tex2D(modeloTexturaMuestra, entrada.CoordenadaTextura); } return color; }// fin del pixelshader MainPSAmbiental // Funcin que calcula la atenuacin de la distancia de la fuente al objeto float AtenuarFuente(float3 worldPosicion, float3 posicionLuz, float rango, float c1, float c2, float c3) { float distancia = distance(worldPosicion, posicionLuz); float atenuacionD = 0; // si la distancia est dentro del rango se hace el clculo de la // atenuacin. if(distancia <= rango) { atenuacionD = min(1 / (c1 + (c2 * distancia) + (c3 * pow(distancia, 2.0F))), 1.0F); }// fin del if return atenuacionD; }// fin de la funcin AtenuarFuente // Funcin que calcula la reflexin especular. float4 ReflexionEspecular(float3 direccionLuz, float3 worldPosicion, float3 worldNormal, float4 colorLuz) { float3 reflexion = normalize(2 * worldNormal * max(dot(direccionLuz, worldNormal), 0.0F) - direccionLuz); float3 direccionCamara = normalize(posicionCamara - worldPosicion); float reflexionEspecular = pow(max(dot(reflexion, direccionCamara), 0.0F), n); return (ks * reflexionEspecular) * (colorLuz * colorMaterialEspecular); }// fin de la funcin ReflexionEspecular float4 Phong(float3 direccionLuz, float3 worldPosicion, float3 worldNormal, float4 colorLuz, float4 colorMater) { float reflexionDifusa = max(dot(direccionLuz, worldNormal), 0.0F); float4 phong = reflexionDifusa * (colorLuz * colorMater); // activacin de la reflexin especular if(habiEspec) { phong += ReflexionEspecular(direccionLuz, worldPosicion, worldNormal, colorLuz) * colorMater * reflexionDifusa; }// fin del if return phong; }// fin de la funcin Phong // Funcin que calcula la luz tipo direccional float4 FuenteDireccional(LuzDireccional luz, float3 worldPosicion, float3 worldNormal, float4 colorMater) { return Phong(-normalize(luz.Direccion), // direccin de luz worldPosicion, worldNormal, luz.Color, colorMater); }// fin de la funcin FuenteDireccional

119

201. 202. 203. 204. 205. 206. 207. 208. 209. 210. 211. 212. 213. 214. 215. 216. 217. 218. 219. 220. 221. 222. 223. 224. 225. 226. 227. 228. 229. 230. 231. 232. 233. 234. 235. 236. 237. 238. 239. 240. 241. 242. 243. 244. 245. 246. 247. 248. 249. 250. 251. 252. 253. 254. 255. 256. 257. 258. 259. 260. 261. 262. 263. 264. 265. 266. 267. 268. 269. 270. 271.

float4 FuentePuntual(LuzPuntual luz, float3 worldPosicion, float3 worldNormal, float4 colorMater) { float4 color = Phong( normalize(luz.Posicion - worldPosicion),// direccin de luz worldPosicion, worldNormal, luz.Color, colorMater); // atenuar luz if(luz.Atenuar != 0) { color *= AtenuarFuente(worldPosicion, luz.Posicion, luz.Rango, luz.C1, luz.C2, luz.C3); }// fin del if return color; }// fin de la funcin FuentePuntual float4 FuenteSpot(LuzSpot luz, float3 worldPosicion, float3 worldNormal, float4 colorMater) { float4 color = 0; float3 direccionLuz = normalize(luz.Posicion - worldPosicion); float3 direccionSpot = normalize(luz.Posicion - luz.Blanco); // calculando cosenos de alfa, teta y fi float cosAlfa = max(dot(direccionSpot, direccionLuz), 0.0F); float cosTeta = cos(luz.AnguloInterno); float cosFi = cos(luz.AnguloExterno); // atenuacin angular entre 0 y 1 if(cosAlfa < cosTeta && cosAlfa > cosFi) { float atenuacionAngular = pow((cosAlfa - cosFi) / (cosTeta - cosFi), luz.Falloff); color = atenuacionAngular * Phong(direccionLuz, worldPosicion, worldNormal, luz.Color, colorMater); // atenuar fuente if(luz.Atenuar != 0) { color *= AtenuarFuente(worldPosicion, luz.Posicion, luz.Rango, luz.C1, luz.C2, luz.C3); }// fin del if }// fin del if else if(cosAlfa >= cosFi) // atenuacin angular igual a uno { color = Phong(direccionLuz, worldPosicion, worldNormal, luz.Color, colorMater); // atenuar fuente if(luz.Atenuar) { color *= AtenuarFuente(worldPosicion, luz.Posicion, luz.Rango, luz.C1, luz.C2, luz.C3); } }// fin del if return color; }// fin de la funcin FuenteSpot LuzDireccional direccionales[2]; LuzPuntual puntuales[2]; LuzSpot spots[2];

float4 MainPS(PixelShaderEntrada entrada, uniform bool texturizado) : COLOR { float4 color = 0; float4 colorDifuso = colorMaterialDifuso; if(texturizado) {

120

272. 273. 274. 275. 276. 277. 278. 279. 280. 281. 282. 283. 284. 285. 286. 287. 288. 289. 290. 291. 292. 293. 294. 295. 296. 297. 298. 299. 300. 301. 302. 303. 304. 305. 306. 307. 308. 309. 310. 311. 312. 313. 314. 315. 316. 317. 318. 319. 320. 321. 322. 323. 324. 325.

colorDifuso *= tex2D(modeloTexturaMuestra, entrada.CoordenadaTextura); }// fin del if for(int i = 0; i < numLuces; i++) { if(direccionales[i].Encender != 0) { color += FuenteDireccional(direccionales[i], entrada.WorldPosition, entrada.WorldNormal, colorDifuso); } if(puntuales[i].Encender != 0) { color += FuentePuntual(puntuales[i], entrada.WorldPosition, entrada.WorldNormal, colorDifuso); } if(spots[i].Encender != 0) { color += FuenteSpot(spots[i], entrada.WorldPosition, entrada.WorldNormal, colorDifuso); } }// fin del for color.a = 1.0F; return color; }// fin del pixelShader mainPS technique Texturizado { pass Ambiental { VertexShader = compile vs_3_0 MainVS(); PixelShader = compile ps_3_0 MainPSAmbiental(true); } pass MultiLuces { VertexShader = compile vs_3_0 MainVS(); PixelShader = compile ps_3_0 MainPS(true); } }// fin de la tcnica Texturizado technique Material { pass Ambiental { VertexShader = compile vs_3_0 MainVS(); PixelShader = compile ps_3_0 MainPSAmbiental(false); } pass MultiLuces { VertexShader = compile vs_3_0 MainVS(); PixelShader = compile ps_3_0 MainPS(false); } }// fin de la tcnica Texturizado

Cre un nuevo proyecto en FX Composer y transcriba el enlistado anterior para crear un efecto en HLSL. Compile y aada el nuevo material a un modelo que FX Composer ofrece, de preferencia la tetera o esfera. En la parte de Parameters: HLSL Profile, se muestran todas las propiedades de entrada del shader. Los campos de las estructuras de las fuentes de iluminacin no pueden tener anotaciones que habiliten la paleta de colores o los sliders, por lo tanto hay que escribir el valor numrico con el teclado.

121

Ilustracin 7-33 Multi pass rendering

Desafortunadamente FX Composer no puede hacer correr mltiples pasadas, o por lo menos en algunas tarjetas grficas, as que toma la ltima que se encuentra en la tcnica. En la Ilustracin 7-33 se muestra el resultado sin aplicar la pasada Ambiental, con textura y sin textura. En el siguiente captulo se explicar cmo agregar los shaders creados en este apartado en XNA. Y se deja al lector estudiar acerca de este lenguaje, pues est fuera del alcance de este texto.

122

8 Cmo agregar un efecto en XNA


En este captulo se muestra cmo cargar un efecto proveniente de un archivo fx, en especfico sern algunos de los ejemplos vistos anteriormente sobre el lenguaje HLSL, y se espera que el lector pueda cargar cualquier otro shader. El ContentManager de XNA realiza la mayor parte del trabajo, sin embargo, la manera en cmo se le pasarn los datos de XNA a HLSL depender del programador, pues si en el shader no se restringieron sus variables extern uniform, en C# se pueden limitar; adems se debe tener cuidado en la asignacin de datos. La pregunta es cmo reconocer los tipos de datos correspondientes del .Net Framework Class Library(CL) y del Framework de XNA a HLSL? En s, la mayora de los datos comunes o no compuestos como los enteros, flotantes y boleanos tienen nombres similares entre CL y HLSL. La manera de reconocer los tipos de datos compuestos o estructuras como float4x4, float3 es haciendo una comparacin manual entre sus campos y los que constituyen aquellos que pueden parecerse en el Framework de XNA, por lo tanto hay que conocer los tipos de datos de ambos, lo cual se deja al lector como estudio, y que en ocasiones ser intuitivo esta asignacin de tipos de datos.

8.1 Efecto ambiental


En este aparatado se muestran dos formas de cargar un efecto proveniente de un archivo fx. El primero muestra cmo cambiar las instancias de la clase Effect del objeto Model por la instancia Effect asociada al efecto ambiental; esto permite mandar a llamar el mtodo Draw de la clase ModelMesh, permitiendo una programacin ms sencilla, pero a costa de cambiar todas las propiedades de material de la instancia Model. La segunda forma no cambia las propiedades del material asociadas a la instancia Model. Para poder dibujar la geometra con el efecto proveniente de un archivo fx, se utiliza el mtodo DrawIndexedPrimitives de la clase GraphicsDevice. 8.1.1 Clonacin de efecto

En este primer ejemplo se utiliza el mtodo Effect.Clone para copiar los valores de un objeto existente a otro, por lo que este mtodo arroja una excepcin cuando se tratan de copiar los valores de un objeto nulo, as que no se recomienda el uso del signo de asignacin, =. Aclarado el uso del mtodo Clone y el signo de asignacin, cre un nuevo proyecto de XNA en Visual Studio. En la clase Game1 escriba las siguientes variables de instancia:
Cdigo 8-1
1. 2. 3. 4. private Model modelo; private Matrix[] transformaciones; private Texture2D textura; Single tiempo;

La declaracin de la variable modelo es para el modelo tridimensional, lnea 1, Cdigo 8-1; que en este caso fue ElefanteC.fbx y que puede ser cualquiera que tengan a la mano. En seguida se declara un arreglo de matrices que almacenaran las transformaciones del modelo, lnea 2. El efecto Ambiental.fx tiene dos tipos de tcnicas, Material y Texturizado, para este ltimo se ha declarado a textura como tipo Texture2D, lnea 3. La variable tiempo servir como ngulo de rotacin alrededor del eje y, lnea 4. Antes de continuar con el cdigo, cre tres carpetas en el Content en el Explorador de soluciones de Visual Studio, los nombres que se utilizaron para las carpetas fueron: Modelos, Shaders y Texturas. En cada una de ellas se aadieron el modelo ElefanteC.fbx, el shader Ambiental.fx y la imagen Omision.png, respectivamente.

123

Una vez que el ContentManager les ha asignado el Asset a cada elemento, en el mtodo LoadContent de la clase Game1 escriba las siguientes lneas.
Cdigo 8-2
1. 2. 3. 4. 5. 6. 7. 8. 9. 10. 11. 12. 13. 14. protected override void LoadContent() { spriteBatch = new SpriteBatch(GraphicsDevice); modelo = Content.Load<Model>(@"Modelos\ElefanteC"); Effect efecto = Content.Load<Effect>(@"Shaders\Ambiental"); textura = Content.Load<Texture2D>(@"Texturas\Omision"); transformaciones = new Matrix[modelo.Bones.Count]; modelo.CopyAbsoluteBoneTransformsTo(transformaciones); foreach (ModelMesh mesh in modelo.Meshes) foreach (ModelMeshPart meshPart in mesh.MeshParts) meshPart.Effect = efecto.Clone(GraphicsDevice); }

De la lnea 5 a la 7, Cdigo 8-2, el mtodo Load genrico de la clase ContentManager crea las instancias respectivas a su tipo de dato de entrada, y se los asigna a modelo, efecto y textura. Luego se copian las matrices de transfomacin del modelo al arreglo transformaciones. En las lneas 11 a 13, se copian los datos de efecto a la propiedad Effect del ModelMeshpart de cada ModelMesh del modelo. Para rotar el modelo alrededor del eje y se ha declarado la variable tiempo, que servir como ngulo. En el mtodo Update de la clase Game1, se actualizar tiempo cada segundo con el total de tiempo que ha transcurrido la apliacin desde que inici. En la lnea 6, Cdigo 8-3, se sebe convertir el tipo double a float.
Cdigo 8-3
1. 2. 3. 4. 5. 6. 7. 8. 9. protected override void Update(GameTime gameTime) { if (GamePad.GetState(PlayerIndex.One).Buttons.Back == ButtonState.Pressed) this.Exit(); tiempo = (Single)gameTime.TotalGameTime.TotalSeconds; base.Update(gameTime); }

Por ltimo, en el mtodo Draw se dibuja la geometra como se vio en el Cmo cargar modelos 3D desde un archivo X y FBX, con la diferencia en que el foreach anidado, lneas 7 a 22, Cdigo 8-4, tiene como elemento de interacin una instancia Effect. En la lnea 9 del mtodo Draw, se establece la tcnica del shader con la propiedad CurrentTechnique de la clase Effect y se obtiene a partir de la coleccin Techniques del objeto effect. Se puede utilizar como ndice de seleccin un entero, sin embargo, el string con el nombre de la tcnica es ms til para el mantenimiento del programa. Despus de seleccionar la tcnica del shader, se establecen los valores de las variables uniform extern del shader, con la ayuda del mtodo SetValue. La instancia de la clase Effect tiene una coleccin de parmetros que representan estas variables del shader. La forma de seleccin es por medio de un tipo entero o un string. Este ltimo se utiliza como preferente, por legibilidad del programa. El mtodo SetValue est sobrecargado 18 veces para aceptar diferentes tipos de datos, sin embargo, no verifica en tiempo de compilacin la integridad de stos.
37

37

Para consultar todas las sobrecargas del mtodo consulte la siguiente pgina: http://msdn.microsoft.com/query/dev10.query?appId=Dev10IDEF1&l=ENUS&k=k(MICROSOFT.XNA.FRAMEWORK.GRAPHICS.EFFECTPARAMETER.SETVALUE);k(DevLang-CSHARP)&rd=true

124

La lnea 10 del mtodo Draw, establece la matriz de mundo a partir del elemento del arreglo transformaciones y la matriz de rotacin alrededor del eje y. En la lnea 13 se establece la matriz de vista a partir del mtodo CreateLookAt. Siguiendo con la asignacin del color del material ambiental, lnea 19 20, se debe convertir el tipo de dato Color por un Vector4, que contiene cuatro elementos de tipo flotante. Por ltimo, se asigna la textura al shader en la lnea 21.
Cdigo 8-4
1. 2. 3. 4. 5. 6. 7. 8. 9. 10. 11. 12. 13. 14. 15. 16. 17. 18. 19. 20. 21. 22. 23. 24. 25. 26. 27. protected override void Draw(GameTime gameTime) { GraphicsDevice.Clear(Color.White); foreach (ModelMesh mesh in modelo.Meshes) { foreach (Effect effect in mesh.Effects) { effect.CurrentTechnique = effect.Techniques["Material"]; effect.Parameters["world"].SetValue( transformaciones[mesh.ParentBone.Index] * Matrix.CreateRotationY(tiempo)); effect.Parameters["view"].SetValue(Matrix.CreateLookAt( new Vector3(0, 0, 300), Vector3.Zero, Vector3.Up)); effect.Parameters["projection"].SetValue( Matrix.CreatePerspectiveFieldOfView( MathHelper.PiOver4, GraphicsDevice.Viewport.AspectRatio, 1.0F, 1000.0F)); effect.Parameters["colorMaterialAmbiental"].SetValue( Color.Gray.ToVector4()); effect.Parameters["texturaModelo"].SetValue(textura); }// fin del foreach mesh.Draw(); }// fin del foreach base.Draw(gameTime); }

Hasta ahora no ha sido tan complicada la asingacin de valores al shader, sin embargo, esta manera de utilizar el efecto no es la ms conveniente, porque si quisiramos pintar este mismo objeto Model con otros efectos de iluminacin se tendra que clonar una vez ms la instancia Effect del ModelMesh, ocupando cuatro bucles foreach. En los siguientes ejemplos de este captulo se utiliza una manera ms eficaz pero no tan sencilla o por lo menos no tan corta. Corra el programa para ver un elefante, o el modelo tridimensional de su preferencia, pintado de un slo color, vase Ilustracin 8-1. Cambie la tcnica Material por Texturizacion, en la lnea 9 del mtodo Draw.

Ilustracin 8-1 Integracin de shader en XNA

125

8.1.2

Interfaz para el shader

El subttulo de este apartado no debe confundirse con la palabra reservada interface de C#, es algo ms general. Es la manera de comunicacin entre XNA y el shader. Y eso es lo que se programara para dibujar la geometra con el efecto deseado, sin necesidad de cambiar sus propiedades de material. A partir de este ejemplo y lo queda de este captulo se reutilizarn los enlistados, con el fin de aprovechar las ventajas de la programacin orientada a objetos. Comience por crear un nuevo proyecto en Visual Studio de XNA Game. Vuelva a elaborar las tres carpetas Modelos, Shaders y Texturas en el Content, en la parte del explorador de soluciones. En la carpeta Modelos, aada cualquier modelo tridimensional que pueda manejar el ContentManager. En la carpeta Shader vuelva a aadir el efecto Ambiental.fx que se vio en el captulo previo a ste. Asegrese que la textura que agregue en la carpeta Texturas, sea compatible con los formatos que el ContentManager controla. Con un diagrama de clases se puede visualizar mejor el software que se contruir, para las interfaces de los efectos. En la ilustracin 8 2 se muestran las interfaces IFx, IDifuso e IEspecular. Cada una de ellas servir para el polimorfismo y herencia de las clases que interacten con el shader.

Ilustracin 8-2 Diagrama de clases. Interfaces

Para crear una interfaz en Visual Studio seleccione el nombre de la solucin en el explorador de soluciones. Haga clic derecho y seleccione Aadir nuevo elemento. Inmediatemente se abre una ventana de dilogo para seleccionar una plantilla de cualquiera de las categoras. Seleccione la plantilla Interface de la categora Cdigo. El nombre que le asigne a cualquier interfaz debe comenzar con la letra i mayscula, seguida del nombre que la describe. Una vez creado el archivo de la interfaz IFx escriba el mtodo DrawModelMeshPart, dentro de las llaves de la interfaz IFx.
public interface IFx { /// <summary> /// Mtodo que dibuja un ModelMeshPart. /// </summary> void DrawModelMeshPart(Microsoft.Xna.Framework.Graphics.ModelMeshPart modelMeshPart, Microsoft.Xna.Framework.Graphics.GraphicsDevice graphicsDevice); }

Este mtodo dibujar el ModelMeshPart que compone al ModelMesh de la clase Model. Cabe aclarar que cada ModelMeshPart se distingue de otro por sus diferentes materiales.

126

La interfaz IDifuso, hereda de la interfaz IFx, y aade una propiedad que se llama ColorDifuso. Cre una nueva interfaz con el mismo nombre Idifuso y escriba las siguientes lneas.
public interface IDifuso : IFx { /// <summary> /// Propiedad que obtiene o establece el color difuso. /// </summary> Color ColorDifuso { get; set; } }

La creacin de esta interfaz es para separar los modelos de iluminacin difusa del especular. Por lo que la siguiente interfaz es para el especular. Agregue una interfaz al proyecto con el nombre IEspecular y escriba en ella el siguiente enlistado.
{ /// <summary> /// Propiedad que obtiene o establece el coeficiente especular. /// </summary> Single Ks { get; set; } /// <summary> /// Propiedad que obtiene o establece la potencia especular. /// </summary> Single N { get; set; } /// <summary> /// Propiedad que obtiene o establece el color especular. /// </summary> Color ColorEspecular { get; set; } /// <summary> /// Propiedad que habilita o inhabilita el modelo especular. /// </summary> Boolean HabilitarEspecular { get; set; } /// <sumary> /// Propiedad que obtiene o establece la posicin de la cmara. /// </sumary> Vector3 PosicionCamara { get; set;} }

Esta interfaz IEspecular tiene las propiedades del coeficiente especular, la potencia especular, el color especular, la posicin de cmara y la opcin de habilitar o inhabilitar el modelo de iluminacin especular. 8.1.2.1 Clase FxAmbiental. La clase FxAmbiental, que a continuacin se describe, hereda de la interfaz IFx, y define el mtodo DrawModelMeshPart. Adems se definen los campos y propiedades que ayudan a la comunicacin entre XNA y el efecto Ambiental.fx. Cre una nueva clase en la solucin, con el nombre FxAmbiental, y en la parte de directivas escriba las siguientes.
using System; using Microsoft.Xna.Framework.Graphics; using Microsoft.Xna.Framework;

Dentro del cuerpo de la clase escriba las variables instancia, lneas 3 9, Cdigo 8-5. La nica variable cuyo modificador de acceso es diferente a private es effect, lnea 9. Esto es porque de esta clase se heredan 127

las siguientes clases para el shader Direccional.fx y MulDirec.fx, este ltimo es una versin reducida del shader MultiIliminacin visto en el captulo anterior. Vase que cada una de estas variables, excepto habilitarTextura y effect, corresponden a las variables uniform del shader Ambiental.fx. En el constructor, lneas 11 16, se obtiene una instancia Effect como parmetro y el cuerpo inicializa el color ambiental e inhabilita la tcnica de Texturizacion. Cada una de las propiedades obtiene o establece el valor correspondiente a la variable de instancia de la clase, y para ser legible las propiedades tienen el mismo nombre que la variables, pero diferencindolas por tener la primera letra en maysculas. El descriptor de acceso set de cada propiedad establece el valor a la variable de instancia de la clase, antes de pasar el dato al mtodo SetValue. En casi todas las propiedades la asignacin de los datos hacia el efecto es igual al ejemplo anterior, exceptuando en HabilitarTextura, lnea35 44. El valor de sta es de tipo boleano, sirve como selector entre las tcnicas. Para ello se utiliza el operador ternario ? :. Si el valor de la variable habilitarTextura es verdadero, se selecciona el string Texturizacion, en caso contrario ser Material. El mtodo DrawModelMeshPart utiliza el modificador virtual para que otras clases derivadas de FxAmbiental puedan cambiar el cuerpo, pero no as la firma. La manera en que se manda a dibujar la geometra es las misma que se vio en el Vertex Buffer, en donde se envuelve el mtodo DrawIndexedPrimitives entre los mtodos Begin y End del objeto effect, lneas 103 110. En este caso el shader Ambiental contiene una pasada y se toma el primer elemento de la coleccin Passes. Todos los parmetros que necesita este mtodo se obtienen del ModelMeshPart.
Cdigo 8-5
1. 2. 3. 4. 5. 6. 7. 8. 9. 10. 11. 12. 13. 14. 15. 16. 17. 18. 19. 20. 21. 22. 23. 24. 25. 26. 27. 28. 29. 30. 31. 32. 33. 34. 35. 36. 37. 38. public class FxAmbiental : IFx { private Matrix view; private Matrix projection; private Matrix world; private Color colorAmbiental; private Texture2D textura2D; private Boolean habilitarTextura; protected Effect effect; public FxAmbiental(Effect effect) { this.effect = effect; ColorAmbiental = Color.Black; HabilitarTextura = false; } /// <summary> /// Propiedad que obtiene o establece el color ambiental del material. /// </summary> public virtual Color ColorAmbiental { get { return colorAmbiental; } set { colorAmbiental = value; effect.Parameters["colorMaterialAmbiental"].SetValue( colorAmbiental.ToVector4()); } }// fin de la propiedad ColorAmbiental /// <summary> /// Propiedad que habilita o inhabilita la textura. /// </summary> public virtual Boolean HabilitarTextura { get { return habilitarTextura; } set

128

39. 40. 41. 42. 43. 44. 45. 46. 47. 48. 49. 50. 51. 52. 53. 54. 55. 56. 57. 58. 59. 60. 61. 62. 63. 64. 65. 66. 67. 68. 69. 70. 71. 72. 73. 74. 75. 76. 77. 78. 79. 80. 81. 82. 83. 84. 85. 86. 87. 88. 89. 90. 91. 92. 93. 94. 95. 96. 97. 98. 99. 100. 101. 102. 103. 104. 105. 106. 107. 108. 109.

{ habilitarTextura = value; effect.CurrentTechnique = effect.Techniques[ (value) ? "Texturizado" : "Material"]; } }// fin de la propiedad HabilitarTextura /// <summary> /// Propiedad que obtiene o establece la matriz projection. /// </summary> public virtual Matrix Projection { get { return projection; } set { projection = value; effect.Parameters["projection"].SetValue(projection); } }// fin de la propiedad Projection /// <summary> /// Propiedad que obtiene o establece la textura. /// </summary> public virtual Texture2D Textura2D { get { return textura2D; } set { textura2D = value; effect.Parameters["texturaModelo"].SetValue(textura2D); } }// fin de la propiedad Textura2D

/// <summary> /// Propiedad que obtiene o establece la matriz view. /// </summary> public virtual Matrix View { get { return view; } set { view = value; effect.Parameters["view"].SetValue(view); } }// fin de la propiedad View /// <summary> /// Propiedad que obtiene o establece la matriz world. /// </summary> public virtual Matrix World { get { return world; } set { world = value; effect.Parameters["world"].SetValue(world); } }// fin de la propiedad World #region IFx Members public virtual void DrawModelMeshPart(ModelMeshPart modelMeshPart, GraphicsDevice graphicsDevice) { effect.Begin(); effect.CurrentTechnique.Passes[0].Begin(); graphicsDevice.DrawIndexedPrimitives(PrimitiveType.TriangleList, modelMeshPart.BaseVertex, 0, modelMeshPart.NumVertices, modelMeshPart.StartIndex, modelMeshPart.PrimitiveCount); effect.CurrentTechnique.Passes[0].End();

129

110. 111. 112. 113. 114.

effect.End(); }// fin del mtodo DrawModelMeshPart #endregion }// fin de la clase FxAmbiental

Queda claro que la nueva forma de asignar un efecto a la geometra es ms complicada, o ms grande que la anterior, sin embargo, se obtiene un mejor control de los valores de ste. La implementacin del modelo de iluminacin ambiental por shader, se har en la clase Game1. Como primer paso escriba las siguientes variables de instancia de la clase Game1.
GraphicsDeviceManager graphics; Model elefante; Matrix[] transformaciones; FxAmbiental fxAmbiental; Texture2D textura; Matrix view, projection;

Luego hay que cambiar el tamao del back buffer, esto con la finalidad de seguir algunas de las recomendaciones que se hace en la documentacin acerca de tamaos de pantalla para ser representados en cualquier dispositivo visual en el que se conecte el Xbox 360. El tamao recomendado es de 1280 para el ancho y 720 para el alto. En el constructor de la clase Game1, despus de la asignacin de la ruta del Content, escriba las siguientes lneas.
public Game1() { graphics = new GraphicsDeviceManager(this); Content.RootDirectory = "Content"; graphics.PreferredBackBufferHeight = 720; graphics.PreferredBackBufferWidth = 1280; }

La propiedad PreferredBackBufferHeight obtiene o establece PreferredBackBufferWidth hace lo mismo, pero para el alto del bfer.

el

alto

del

bfer,

En el mtodo Initialize, inicalice las matrices view y projection, como se muestra en el siguiente enlistado, y que se deja como opcin los valores propuestos aqu, pues es para visualizar toda la geometra que se dibujar en este ejemplo y los dos ms que le siguen.
protected override void Initialize() { view = Matrix.CreateLookAt(new Vector3(120.0F, 96.0F, 473.0F), new Vector3(75.0F, 104.0F, 7.0F), Vector3.Up); projection = Matrix.CreatePerspectiveFieldOfView(MathHelper.PiOver4, GraphicsDevice.Viewport.AspectRatio, 1.0F, 1000.0F); base.Initialize(); }

En el mtodo LoadContent de la clase Game1, se cargan los datos de textura, el modelo trdimensional y el shader, los cuales no deben ser desconcidos para el lector, pues en anteriores ejemplos se utilizaron. Luego de leer los datos del archivo Ambiental.fx y asignarlos al objeto fxAmbiental, se modifican las propiedades ColorAmbiental y Textura2D.
protected override void LoadContent() {

130

elefante = Content.Load<Model>(@"Modelos\Elefante"); transformaciones = new Matrix[elefante.Bones.Count]; elefante.CopyAbsoluteBoneTransformsTo(transformaciones); fxAmbiental = new FxAmbiental( Content.Load<Effect>(@"Shaders\Ambiental")); fxAmbiental.ColorAmbiental = Color.Brown; fxAmbiental.Textura2D = textura; textura = Content.Load<Texture2D>(@"Texturas\Omision"); }

Para concluir con este ejemplo, el mtodo Draw de la clase Game1 retoma parte del primer ejemplo del Cmo cargar modelos 3D desde un archivo X y FBX, en dnde se dibuja un ModelMeshPart de la coleccin ModelMesh de la instancia de la clase Model. Sin embargo, en esta ocasin se mandarn a dibujar todos los ModelMeshPart. En las lneas 5 y 6, Cdigo 8-6, se establecen las matrices de vista y proyeccin del objeto fxAmbiental. Sin duda, estas propiedades pueden no estar aqu, pero se ha dejado as porque son algunas de las variables que pueden cambiar en una aplicacin, por no decir las comunes. El primer foreach, lneas 8 22, mapea cada elemento de la coleccin Meshes del objeto elefante. Enseguida se establece el valor de la propiedad World del objeto fxAmbiental con la matriz de transformacin obtenida del objeto elefante. Se debe establecer el bfer de ndices en el dispositivo grfico, lnea 12, a partir de la instancia mesh que el primer foreach utiliza como elemento de mapeo sobre la coleccin Meshes del objeto elefante. La propiedad IndexBuffer de la clase ModelMesh solo obtiene dicho bfer. En el foreach anidado, lneas 14 21, se utiliza el objeto meshPart para hacer referencia a cada elemento de la coleccin MeshPart del objeto mesh que utiliza el primer foreach. ste es menester utilizarlo para establecer el bfer de vrtices y la declaracin de estos, lneas 16 18, las propiedades VertexBuffer, StreamOffset, VertexStride y VertexDeclaration son la clave para dicho xito. Antes de la llave que cierra el foreach anidado se invoca el mtodo DrawModelMeshPart del objeto fxAmbiental, y cuyos parmetros son el mehsPart y el GraphicsDevice, lnea 20.
Cdigo 8-6
1. 2. 3. 4. 5. 6. 7. 8. 9. 10. 11. 12. 13. 14. 15. 16. 17. 18. 19. 20. 21. 22. 23. 24. 25. protected override void Draw(GameTime gameTime) { GraphicsDevice.Clear(Color.White); fxAmbiental.View = view; fxAmbiental.Projection = projection; foreach (ModelMesh mesh in elefante.Meshes) { fxAmbiental.World = transformaciones[mesh.ParentBone.Index]; GraphicsDevice.Indices = mesh.IndexBuffer; foreach (ModelMeshPart meshPart in mesh.MeshParts) { GraphicsDevice.Vertices[0].SetSource(mesh.VertexBuffer, meshPart.StreamOffset, meshPart.VertexStride); GraphicsDevice.VertexDeclaration = meshPart.VertexDeclaration; fxAmbiental.DrawModelMeshPart(meshPart, GraphicsDevice); } } base.Draw(gameTime); }

131

Ejecute el programa con la tecla F5 y ver algo parecido a la ilustracin 8 -3; corriga cualquier error de compilacin si es el caso y vuleva a intentar.

Ilustracin 8-3 Integracin de shader en XNA

8.2 Luz direccional


Para este ejemplo se usa el shader que implementa una luz de tipo direccional que se estudi en el captulo anterior, empero se le agreg todas las variables uniform necesarias para el modelo de iluminacin especular, como no es el punto de este captulo explicar el siguiente enlistado, se deja como ejercicio entender su funcionamiento. Escriba el siguiente cdigo en Fx Composer y luego agrguelo en la carpeta Shaders de la solucin anterior, en la que se cre la clase FxAmbiental.
Cdigo 8-7
1. 2. 3. 4. 5. 6. 7. 8. 9. 10. 11. 12. 13. 14. 15. 16. 17. 18. 19. 20. 21. 22. 23. 24. 25. 26. 27. 28. 29. /*

Modelo de iluminacin: Especular o Difusa. Tipo de fuente: Direccional. Tcnica empleada: PixelShader. Material clsico. */ float4x4 world : World; float4x4 view : View; float4x4 projection : Projection; float3 direccionLuz < string UIName = "Direccin de Luz"; >; float3 posicionCamara < string UIName = "Posicin cmara"; >; float ks < string UIWidget = "slider"; float UIMin = 0.0F; float UIMax = 1.0F; float UIStep = 0.05F; string UIName = "Specular";

132

30. 31. 32. 33. 34. 35. 36. 37. 38. 39. 40. 41. 42. 43. 44. 45. 46. 47. 48. 49. 50. 51. 52. 53. 54. 55. 56. 57. 58. 59. 60. 61. 62. 63. 64. 65. 66. 67. 68. 69. 70. 71. 72. 73. 74. 75. 76. 77. 78. 79. 80. 81. 82. 83. 84. 85. 86. 87. 88. 89. 90. 91. 92. 93. 94. 95. 96. 97. 98. 99. 100.

> = {0.05F}; float n < string UIWidget = "slider"; float UIMin = 0.0F; float UIMax = 128.0F; float UIStep = 1.0F; string UIName = "Specular pow"; > = 5.0F; float4 colorMaterialAmbiental < string UIName = "Material ambiental"; string UIWidget = "Color"; > = {0.05F, 0.05F, 0.05F, 1.0F}; float4 colorMaterialDifuso < string UIName = "Material difuso"; string UIWidget = "Color"; > = {0.24F, 0.34F, 0.39F, 1.0F}; float4 colorMaterialEspecular < string UIName = "Material especular"; string UIWidget = "Color"; > = {1.0F, 1.0F, 1.0F, 1.0F}; texture texturaModelo < string UIName = "Textura"; >; sampler modeloTexturaMuestra = sampler_state { Texture = <texturaModelo>; MinFilter = Linear; MagFilter = Linear; MipFilter = Linear; AddressU = Wrap; AddressV = Wrap; }; struct VertexShaderEntrada { float4 Posicion : POSITION; float3 Normal : NORMAL; float2 CoordenadaTextura : TEXCOORD0; }; struct VertexShaderSalida { float4 Posicion : POSITION; float2 CoordenadaTextura : TEXCOORD0; float3 WorldNormal : TEXCOORD1; float3 WorldPosition : TEXCOORD2; }; struct PixelShaderEntrada { float2 CoordenadaTextura : TEXCOORD0; float3 WorldNormal : TEXCOORD1; float3 WorldPosition : TEXCOORD2; }; bool modIluEsp < string UIName = "Modelo Iluminacin Especular"; > = false; VertexShaderSalida MainVS(VertexShaderEntrada entrada) { VertexShaderSalida salida;

133

101. 102. 103. 104. 105. 106. 107. 108. 109. 110. 111. 112. 113. 114. 115. 116. 117. 118. 119. 120. 121. 122. 123. 124. 125. 126. 127. 128. 129. 130. 131. 132. 133. 134. 135. 136. 137. 138. 139. 140. 141. 142. 143. 144. 145. 146. 147. 148. 149. 150. 151. 152. 153. 154. 155. 156. 157. 158. 159. 160. 161. 162. 163. 164.

float4x4 wvp = mul(world, mul(view, projection)); salida.Posicion = mul(entrada.Posicion, wvp); salida.WorldNormal = mul(entrada.Normal, world); float4 worldPosition = mul(entrada.Posicion, world); salida.WorldPosition = worldPosition / worldPosition.w; salida.CoordenadaTextura = entrada.CoordenadaTextura; return salida; }// fin del vertex shader MainVS float4 MainPS(PixelShaderEntrada entrada, uniform bool habilitarTextura) : COLOR { // modelo de iluminacin difusa float3 dirLuz = normalize(direccionLuz); float intensidadDifusa = max(dot(-dirLuz, entrada.WorldNormal), 0.0F); float4 difuso = colorMaterialDifuso * intensidadDifusa; float4 color; // color final color = colorMaterialAmbiental + difuso; // modelo de iluminacin especular if(modIluEsp) { // iluminacin especular float3 reflexion = normalize(2 * entrada.WorldNormal * max(dot(-dirLuz, entrada.WorldNormal), 0.0F) + dirLuz); float3 direccionCamara = normalize(posicionCamara entrada.WorldPosition.xyz); float intensidadEspecular = pow(max(dot(reflexion, direccionCamara), 0.0F), n); float4 especular = ks * intensidadEspecular * colorMaterialEspecular; color += especular * intensidadDifusa; }// fin del if if(habilitarTextura) { float4 colorTextura = tex2D(modeloTexturaMuestra, entrada.CoordenadaTextura); color *= colorTextura; } // color final color.a = 1.0F; return color; }// fin pixel shader MainPS technique Texturizado { pass P0 { VertexShader = compile vs_3_0 MainVS(); PixelShader = compile ps_3_0 MainPS(true); } } technique Material { pass P0 { VertexShader = compile vs_3_0 MainVS(); PixelShader = compile ps_3_0 MainPS(false); } }

La clase FxDireccional es la clase que servir como interfaz entre el shader anterior y XNA. Esta clase necesita las propiedades de las interfaces IDifuso, IEspecular y las hereda de la clase FxAmbiental; en la Ilustracin 8-4 se muestra el diagrama de clase. Cre una nueva clase en la solucin anterior, llamada FxDireccional y escriba el Cdigo 8-8.

134

Ilustracin 8-4 Diagrama de clases. FxDireccional.

Entre las lneas 3 9, se encuentran las variables de instancia que sirven de intermediarios entre los datos que recibe del usuario de la clase FxDireccional y el shader. Vea la equivalencia entre estas variables y aquellas a las que se quiere asociar al shader, excepto en aquellas que se han declarado como estructuras Color, lneas 5 y 6. En el constructor de la clase, lneas 11 19, se inicializan las variables de instancia a partir de sus propiedades, esto con la finalidad de darle los valores a cada elemento de la coleccin de Parameters del efecto. Cada propiedad se encargar de establecer dicho valor a cada variable uniform extern. Para la propiedad Ks, lneas 26 34, coeficiente especular, no se tiene ninguna restriccin en el momento de establecer el valor de la variable de instancia ks, que vara entre cero y uno. Esto se ha dejado as para que se pueda visualizar qu pasa con valores mayores a uno y para valores negativos. Recuerde que primero se debe asignar el dato a la variable de instancia, y luego sta se le establece al elemento de la coleccin correspondiente. La propiedad potencia especular N, lneas 39 47, tampoco restringe el valor que generalmente es entre 1 y 128. El objetivo, al igual que la propiedad Ks, es que se pueda visualizar con valores fuera de lo comn. Las propiedades ColorEspecular, lneas 52 60, y ColorDifuso, lneas 96 105, tienen que convertir la estructura Color a una estructura Vector4 con el fin de garantizar la integracin de los datos. HabilitarEspecular, lneas 66 74, y PosicionCamara, lneas 79 87, solo pasan el valor correspondiente al elemento de la coleccin Parameters. La propiedad DireccionLuz, lneas 112 121, evala el valor antes de asignar el dato a la variable direccionLuz, lnea 117; si el valor es diferente de Vector3.Zero se le asigna a la variable direccionLuz, en caso contrario se le da un valor por default. Esto garantiza que siempre tendr una direccin. Como esta clase hereda de la clase FxAmbiental, las propiedades ColorAmbiental, HabilitarTextura, Projection, Textura2D, View y World deberan de sobrescribirse, debido a que el ndice que se utiliza para seleccionar el elemento de la coleccin Parameters del objeto Effect podra cambiar. Esto depende del nombre de las variables uniform extern de los shaders, en caso que no coincida, el error no se detectar en tiempo de compilacin sino en ejecucin. Sin embargo, en todos los ejemplos de shaders se les dio un mismo nombre a cada una de las variables para permitir una programacin ms sencilla. La mejor prctica es agregar el modificador virtual a las propiedades base para poder cambiarlas en las clases que las hereden.
Cdigo 8-8
1. 2. 3. 4. 5. 6. public class FxDireccional : FxAmbiental, IEspecular, IDifuso { private Single ks; private Single n; private Color colorDifuso; private Color colorEspecular;

135

7. 8. 9. 10. 11. 12. 13. 14. 15. 16. 17. 18. 19. 20. 21. 22. 23. 24. 25. 26. 27. 28. 29. 30. 31. 32. 33. 34. 35. 36. 37. 38. 39. 40. 41. 42. 43. 44. 45. 46. 47. 48. 49. 50. 51. 52. 53. 54. 55. 56. 57. 58. 59. 60. 61. 62. 63. 64. 65. 66. 67. 68. 69. 70. 71. 72. 73. 74. 75. 76. 77.

private Vector3 direccionLuz; private Boolean habilitarEspecular; private Vector3 posicionCamara; public FxDireccional(Effect effect) : base(effect) { ColorDifuso = Color.WhiteSmoke; ColorEspecular = Color.WhiteSmoke; DireccionLuz = new Vector3(-1.0F, -1.0F, -1.0F); Ks = 20.0F; N = 128.0F; }// fin del constructor #region IEspecular Members /// <summary> /// Propiedad que obtiene o establece el coeficiente especular. /// </summary> public float Ks { get { return ks; } set { ks = value; base.effect.Parameters["ks"].SetValue(ks); } }// fin de la propiedad Ks /// <summary> /// Propiedad que obtiene o establece la potencia /// </summary> public float N { get { return n; } set { n = value; base.effect.Parameters["n"].SetValue(n); } }// fin de la propiedad N /// <summary> /// Propiedad que obtiene o establece el color especular del material. /// </summary> public Color ColorEspecular { get { return colorEspecular; } set { colorEspecular = value; base.effect.Parameters["colorMaterialEspecular"].SetValue( colorEspecular.ToVector4()); } }// fin de la propiedad ColorEspecular /// <summary> /// Propiedad que habilita o inhabilita el modelo iliminacin especular. /// </summary> public Boolean HabilitarEspecular { get { return habilitarEspecular; } set { habilitarEspecular = value; base.effect.Parameters["modIluEsp"].SetValue(habilitarEspecular); } }// fin de la propiedad DireccionLuz /// <summary> /// Propiedad que obtiene o establece la posicin de la cmara.

136

78. 79. 80. 81. 82. 83. 84. 85. 86. 87. 88. 89. 90. 91. 92. 93. 94. 95. 96. 97. 98. 99. 100. 101. 102. 103. 104. 105. 106. 107. 108. 109. 110. 111. 112. 113. 114. 115. 116. 117. 118. 119. 120. 121. 122.

/// </summary> public Vector3 PosicionCamara { get { return posicionCamara; } set { posicionCamara = value; effect.Parameters["posicionCamara"].SetValue(posicionCamara); } }// fin de la propiedad PosicionCamara #endregion #region IDifuso Members /// <summary> /// Propiedad que obtiene o establece el color difuso del material. /// </summary> public Color ColorDifuso { get { return colorDifuso; } set { colorDifuso = value; base.effect.Parameters["colorMaterialDifuso"].SetValue( colorDifuso.ToVector4()); } }// fin de la propiedad ColorDifuso #endregion /// <summary> /// Propiedad que obtiene o establece la direccin de luz. /// </summary> public Vector3 DireccionLuz { get { return direccionLuz; } set { direccionLuz = (value != Vector3.Zero) ? value : new Vector3(-1.0F, -1.0F, -1.0F); base.effect.Parameters["direccionLuz"].SetValue(direccionLuz); } }// fin de la propiedad DireccionLuz }// fin de la clase FxDireccional

La implementacin de la clase FxAmbiental se realiza en la clase Game1. Abra el archivo Game1.cs y escriba dos variables de instancia nuevas.
FxDireccional fxDireccional; Vector3 posicion;

La variable posicion sirve para establecer el lugar de la cmara en la propiedad PosicionCamara del objeto fxDireccional. En el mtodo Initialize se inicializa la estructura posicion para luego drsela al mtodo CreateLookAt como uno de sus parmetros.
protected override void Initialize() { posicion = new Vector3(-120.0F, 96.0F, 473.0F); view = Matrix.CreateLookAt(posicion, new Vector3(75.0F, 104.0F, 7.0F), Vector3.Up); projection = Matrix.CreatePerspectiveFieldOfView(MathHelper.PiOver4, GraphicsDevice.Viewport.AspectRatio, 1.0F, 1000.0F);

137

base.Initialize(); }

En el mtodo LoadContent se crea la instancia fxAmbiental y se inicializan algunas de sus propiedades, vase el siguiente cdigo.
protected override void LoadContent() { elefante = Content.Load<Model>(@"Modelos\Elefante"); transformaciones = new Matrix[elefante.Bones.Count]; elefante.CopyAbsoluteBoneTransformsTo(transformaciones); fxAmbiental = new FxAmbiental( Content.Load<Effect>(@"Shaders\Ambiental")); fxAmbiental.ColorAmbiental = Color.Brown; fxAmbiental.Textura2D = textura; textura = Content.Load<Texture2D>(@"Texturas\Omision"); fxDireccional = new FxDireccional(Content.Load<Effect>(@"Shaders\Direccional")); fxDireccional.Textura2D = textura; fxDireccional.ColorAmbiental = Color.Black; fxDireccional.ColorDifuso = new Color(5, 2, 2); fxDireccional.ColorEspecular = Color.Brown; fxDireccional.DireccionLuz = new Vector3(0, -1, -1); fxDireccional.HabilitarEspecular = true; fxDireccional.HabilitarTextura = true; fxDireccional.Ks = 10; fxDireccional.N = 120; fxDireccional.PosicionCamara = posicion; }

En el mtodo Draw,Cdigo 8-9, se actualizan las propiedades View, lnea 8; Projection, lnea 9; y World, lneas 13 y 24; del objeto fxDireccional. Antes de llamar al mtodo DrawModelMeshPart de la instancia FxDireccional, lnea 25, se traslada la geometra menos cien unidades sobre el eje x y menos doscientas unidades sobre el eje z. Corra el ejemplo con la tecla F5 y ver algo similar a la Ilustracin 8-5.
Cdigo 8-9
1. 2. 3. 4. 5. 6. 7. 8. 9. 10. 11. 12. 13. 14. 15. 16. 17. 18. 19. 20. 21. 22. 23. 24. 25. protected override void Draw(GameTime gameTime) { GraphicsDevice.Clear(Color.White); fxAmbiental.View = view; fxAmbiental.Projection = projection; fxDireccional.View = fxAmbiental.View; fxDireccional.Projection = fxAmbiental.Projection; foreach (ModelMesh mesh in elefante.Meshes) { fxAmbiental.World = transformaciones[mesh.ParentBone.Index]; fxDireccional.World = fxAmbiental.World; GraphicsDevice.Indices = mesh.IndexBuffer; foreach (ModelMeshPart meshPart in mesh.MeshParts) { GraphicsDevice.Vertices[0].SetSource(mesh.VertexBuffer, meshPart.StreamOffset, meshPart.VertexStride); GraphicsDevice.VertexDeclaration = meshPart.VertexDeclaration; fxAmbiental.DrawModelMeshPart(meshPart, GraphicsDevice); fxDireccional.World *= Matrix.CreateTranslation(-100, 0, -200); fxDireccional.DrawModelMeshPart(meshPart, GraphicsDevice);

138

26. 27. 28. 29. 30.

} } base.Draw(gameTime); }

Si ocurre un error de compilacin o de ejecucin revis de nueva cuenta el cdigo, y vuelva intentar.

Ilustracin 8-5 Integracin de los shaders Ambiental y Direccional en XNA

8.3 Efecto multiluces


Para este ltimo ejemplo se utiliza una versin reducida del shader MultiLuces que se vio en el captulo anterior. Este nuevo shader solo contiene un tipo de fuente: la direccional. Esto es con el fin que se entienda cmo se crea la clase que comunica los datos de XNA hacia el efecto, y se deja como ejercicio al lector crear la clase correspondiente para la versin completa de MultiLuces. En el listado siguiente se muestra la versin reducida de MultiLuces, y se espera que el lector pueda interpretar con facilidad cada lnea, pues la explicacin ya fue comentada en el captulo anterior. Abra Fx Composer y cre un nuevo proyecto para escribir las siguientes lneas en un archivo fx.
Cdigo 8-10
1. 2. 3. 4. 5. 6. 7. 8. 9. 10. 11. 12. 13. 14. 15. 16. 17. 18. 19. 20. 21. 22. /*

Multiples luces. Modelo de iluminacin: Especular o Difusa. Tipo de fuente: Direccional. Tcnica empleada: PixelShader. Material clsico. */ float4x4 world: World; float4x4 view : View; float4x4 projection : Projection; float3 posicionCamara < string UIName = "Posicin cmara"; >; float ks < string UIWidget = "slider";

139

23. 24. 25. 26. 27. 28. 29. 30. 31. 32. 33. 34. 35. 36. 37. 38. 39. 40. 41. 42. 43. 44. 45. 46. 47. 48. 49. 50. 51. 52. 53. 54. 55. 56. 57. 58. 59. 60. 61. 62. 63. 64. 65. 66. 67. 68. 69. 70. 71. 72. 73. 74. 75. 76. 77. 78. 79. 80. 81. 82. 83. 84. 85. 86. 87. 88. 89. 90. 91. 92. 93.

float UIMin = 0.0F; float UIMax = 10.0F; float UIStep = 0.05F; string UIName = "Coeficiente de reflexin especular"; > = {0.05F}; float n < string UIWidget = "slider"; float UIMin = 0.0F; float UIMax = 128.0F; float UIStep = 1.0F; string UIName = "Exponente de reflexin especular"; > = 5.0F; float4 colorMaterialAmbiental < string UIName = "Material ambiental"; string UIWidget = "Color"; > = {0.07F, 0.07F, 0.07F, 1.0F}; float4 colorMaterialDifuso < string UIName = "Color Material"; string UIWidget = "Color"; > = {0.24F ,0.34F, 0.39F, 1.0F}; float4 colorMaterialEspecular < string UIName = "Material especular"; string UIWidget = "Color"; > = {1.0F, 1.0F, 1.0F, 1.0F}; bool habiEspec < string UIName = "Modelo Iluminacin Especular"; > = false; texture2D texturaModelo; sampler modeloTexturaMuestra = sampler_state { Texture = <texturaModelo>; MinFilter = Linear; MagFilter = Linear; MipFilter = Linear; AddressU = Wrap; AddressV = Wrap; }; struct VertexShaderSalida { float4 Posicion : POSITION; float2 CoordenadaTextura : TEXCOORD0; float3 WorldNormal : TEXCOORD1; float3 WorldPosition : TEXCOORD2; }; struct PixelShaderEntrada { float2 CoordenadaTextura : TEXCOORD0; float3 WorldNormal : TEXCOORD1; float3 WorldPosition : TEXCOORD2; }; struct LuzDireccional { float3 Direccion; float4 Color; int Encender; };

140

94. 95. 96. 97. 98. 99. 100. 101. 102. 103. 104. 105. 106. 107. 108. 109. 110. 111. 112. 113. 114. 115. 116. 117. 118. 119. 120. 121. 122. 123. 124. 125. 126. 127. 128. 129. 130. 131. 132. 133. 134. 135. 136. 137. 138. 139. 140. 141. 142. 143. 144. 145. 146. 147. 148. 149. 150. 151. 152. 153. 154. 155. 156. 157. 158. 159. 160. 161. 162. 163. 164.

int numLuces = 4; VertexShaderSalida MainVS(float3 posicion : POSITION, float3 normal : NORMAL, float2 coordenadaTextura : TEXCOORD0) { VertexShaderSalida salida; float4x4 mvp = mul(world, mul(view, projection)); salida.Posicion = mul(float4(posicion, 1.0F), mvp); salida.WorldNormal = mul(normal, world); salida.WorldPosition = mul(float4(posicion, 1.0F), world); salida.CoordenadaTextura = coordenadaTextura; return salida; }// fin del vertexshader MainVS float4 MainPSAmbiental(PixelShaderEntrada entrada, uniform bool habiTextura) : COLOR { float4 color = colorMaterialAmbiental; if(habiTextura) { color *= tex2D(modeloTexturaMuestra, entrada.CoordenadaTextura); } return color; }// fin del pixelshader MainPSAmbiental // Funcin que calcula la reflexin especular. float4 ReflexionEspecular(float3 direccionLuz, float3 worldPosicion, float3 worldNormal, float4 colorLuz) { float3 reflexion = normalize(2 * worldNormal * max(dot(direccionLuz, worldNormal), 0.0F) - direccionLuz); float3 direccionCamara = normalize(posicionCamara - worldPosicion); float reflexionEspecular = pow(max(dot(reflexion, direccionCamara), 0.0F), n); return (ks * reflexionEspecular) * (colorLuz * colorMaterialEspecular); }// fin de la funcin ReflexionEspecular float4 Phong(float3 direccionLuz, float3 worldPosicion, float3 worldNormal, float4 colorLuz, float4 colorMater) { float reflexionDifusa = max(dot(direccionLuz, worldNormal), 0.0F); float4 phong = reflexionDifusa * colorLuz * colorMater; // activacin de la reflexin especular if(habiEspec) { phong += ReflexionEspecular(direccionLuz, worldPosicion, worldNormal, colorLuz) * colorMater * reflexionDifusa; }// fin del if return phong; }// fin de la funcin Phong // Funcin que calcula la luz tipo direccional float4 FuenteDireccional(LuzDireccional luz, float3 worldPosicion, float3 worldNormal, float4 colorMater) { return Phong(-normalize(luz.Direccion), // direccin de luz worldPosicion, worldNormal, luz.Color, colorMater); }// fin de la funcin FuenteDireccional LuzDireccional direccionales[20]; float4 MainPS(PixelShaderEntrada entrada, uniform bool texturizado) : COLOR { float4 color = 0; float4 colorDifuso = colorMaterialDifuso;

141

165. 166. 167. 168. 169. 170. 171. 172. 173. 174. 175. 176. 177. 178. 179. 180. 181. 182. 183. 184. 185. 186. 187. 188. 189. 190. 191. 192. 193. 194. 195. 196. 197. 198. 199. 200. 201. 202. 203. 204. 205. 206. 207. 208. 209.

if(texturizado) { colorDifuso *= tex2D(modeloTexturaMuestra, entrada.CoordenadaTextura); }// fin del if for(int i = 0; i < numLuces; i++) { if(direccionales[i].Encender != 0) { color += FuenteDireccional(direccionales[i], entrada.WorldPosition, entrada.WorldNormal, colorDifuso); } }// fin del for color.a = 1.0F; return color; }// fin del pixelShader mainPS technique Texturizado { pass Ambiental { VertexShader = compile vs_3_0 MainVS(); PixelShader = compile ps_3_0 MainPSAmbiental(true); } pass MultiLuces { VertexShader = compile vs_3_0 MainVS(); PixelShader = compile ps_3_0 MainPS(true); } }// fin de la tcnica Texturizado technique Material { pass Ambiental { VertexShader = compile vs_3_0 MainVS(); PixelShader = compile ps_3_0 MainPSAmbiental(false); } pass MultiLuces { VertexShader = compile vs_3_0 MainVS(); PixelShader = compile ps_3_0 MainPS(false); } }// fin de la tcnica Texturizado

Continuando con el proyecto de Visual Studio se deben agregar dos clases, llamadas FxMultiluces y LuzDireccional. La primera representa la clase que comunicar los datos desde XNA hacia el shader, y la segunda representar los campos de la estructura LuzDireccional del shader. En la Ilustracin 8-6, se muestra el diagrama de clase para FxMultiluces. sta hereda de la clase FxAmbiental y de las interfaces IDifuso e IEspecular. Lo que hace pensar que dicha herencia de clase pudo haberse dado de FxDireccional hacia FxMultiluces. Pero la propiedad DireccionLuz de FxDireccional no se encuentra como miembro de la clase FxMultiluces, y por eso se consideran como clases diferentes.

142

Ilustracin 8-6 Diagrama de clases. FxMultiluces

Antes de escribir la clase FxMultiluces hay que definir los campos de la clase LuzDireccional que utiliza FxMultiluces como dependencia. En las lneas 3 a 6, Cdigo 8-11, se declaran las variables de instancia color, direccin, encender y estrucutraLuz. Las tres primeras corresponden a la estructura que LuzDireccional del shader Multluces.fx. Pero si uno observa el campo Encender de dicha estructura en el shader no es del tipo booleano, sino entera. Para conservar la integridad de los datos, en las propiedades, la asignacin de dichos valores se hace adecuadamente y dependiendo de la informacin. La variable estructuraLuz, lnea 6, representa un elemento del parmetro direccionales del shader, ste es un arreglo de veinte estrucuturas LuzDireccional, as que cada una tiene tres elementos a los cuales se les puede asignar un valor. El constructor de la clase LuzDireccional, lneas 8 14, establece la direccin de la luz, el color e inhabilita la luz. Por default todas las luces estarn apagadas. Para la propiedad Color, lneas 19 28, se transforma la estructura Color por Vector4. En la propiedad DireccionLuz se evita que se establezca un valor tipo Vector3.Zero. En el descriptor de acceso set, lneas 50 55 de la propiedad Encender, se establece el valor a uno si el valor de la variable encender es true y en caso contrario sera cero.
Cdigo 8-11
1. 2. 3. 4. 5. 6. 7. 8. 9. 10. 11. 12. 13. 14. 15. 16. 17. 18. 19. 20. 21. 22. 23. 24. public class LuzDireccional { private Color color; private Vector3 direccion; private Boolean encender; private EffectParameter estructuraLuz; public LuzDireccional(EffectParameter effectParameter, Vector3 direccion) { estructuraLuz = effectParameter; Direccion = direccion; Color = Color.WhiteSmoke; Encender = false; } /// <summary> /// Propiedad que obtiene o establece el color de la luz. /// </summary> public Color Color { get { return color; } set { color = value;

143

25. 26. 27. 28. 29. 30. 31. 32. 33. 34. 35. 36. 37. 38. 39. 40. 41. 42. 43. 44. 45. 46. 47. 48. 49. 50. 51. 52. 53. 54. 55. 56. 57.

estructuraLuz.StructureMembers["Color"].SetValue( color.ToVector4()); } }// fin de la propiedad Color /// <summary> /// Propiedad que obtiene o establece el vector de direccin de la luz. /// </summary> public Vector3 Direccion { get { return direccion; } set { direccion = (value != Vector3.Zero) ? value : new Vector3(-1.0F, -1.0F, -1.0F); estructuraLuz.StructureMembers["Direccion"].SetValue(direccion); } }// fin de la propiedad Direccion /// <summary> /// Propiedad que enciende o apaga la luz. /// </summary> public Boolean Encender { get { return encender; } set { encender = value; estructuraLuz.StructureMembers["Encender"].SetValue( (encender) ? 1 : 0); } }// fin de la propiedad Encender }// fin de la clase LuzDireccional

Agregue una nueva clase en la solucin en Visual Studio cuyo nombre sea FxMultiluces, y escriba el Cdigo 8-12. Cada una de las variables del efecto tienen su lter ego en las variables de instancia, lneas 3 10. Como se ha manejado anteriormente no todas las variables corresponden al mismo tipo de dato, sin embargo la integridad se conserva en las propiedades. En el constructor se inicializan los colores del material, el coeficiente especular y su potencia; se inhabilita la textura y se le asigna el parmetro correspondiente al arreglo que tiene las estructuras de la luz direccional, lnea 21. La definicin de las propiedades, en su mayora, son las mismas que en la clase FxDireccional, as que solo se explicara el mtodo DrawModelMeshPart, lneas 131 168 Este shader contiene dos pasadas por tcnica y deben ser llamadas una a una en el mtodo DrawModelMeshPart de la clase FxAmbiental, para sumar los efectos de ambas pasadas. Para lograr dicho efecto se debe inhabilitar la escritura sobre el DepthBuffer y habilitar el canal alfa; luego se deben regresar a un estado anterior, para no afectar a las dems geometras. El cambio debe hacerse solo para la clase FxMultiLuces, as que se deber reescribir el mtodo DrawModelMeshPart que se hereda de la clase FxAmbiental, lneas 131 168. En el mtodo DrawModelMeshPart se hace llamar dos veces el mtodo DrawIndexedPrimitives de la instancia graphicsDevice, lneas 138 -141, y lneas 158 161, cada una de ellas se encuentra entre los mtodos Begin y End de cada elemento EffectPass que corresponde a Ambiental y Multiluces del shader. Despes de llamar por primera vez al mtodo DrawIndexedPrimitives, se habilita la transparencia, lnea 145, y junto con ella todas las propiedades que van de la mano del blending . Enseguida se llama el mtodo DrawIndexedPrimitives, no sin antes propagar el efecto anterior al dispositivo grfico antes del renderizado. Para lograr dicho fin, se llama el mtodo CommitChanges, lnea 153. 144

El mtodo CommitChanges, debe llamarse dentro del par de mtodos Begin y End de la pasada actual, y antes de varios mtodos DrawPrimitives, que sirven como propagadores de los cambios hacia el dispositivo grfico. Antes de llamar el mtodo End, lnea 164, se inhabilita el blending para no afectar las goemetrias posteriores al llamado del mtodo DrawModelMeshPart.
Cdigo 8-12
1. 2. 3. 4. 5. 6. 7. 8. 9. 10. 11. 12. 13. 14. 15. 16. 17. 18. 19. 20. 21. 22. 23. 24. 25. 26. 27. 28. 29. 30. 31. 32. 33. 34. 35. 36. 37. 38. 39. 40. 41. 42. 43. 44. 45. 46. 47. 48. 49. 50. 51. 52. 53. 54. 55. 56. 57. 58. 59. 60. 61. public class FxMultiluces : FxAmbiental, IEspecular, IDifuso { private Color colorDifuso; private Color colorEspecular; private Boolean habilitarEspecular; private Single ks; private Single n; private Int16 numeroLuces; private EffectParameter direccionales; private Vector3 posicionCamara; public FxMultiluces(Effect effect) : base(effect) { ColorDifuso = Color.Black; ColorDifuso = Color.WhiteSmoke; ColorEspecular = Color.WhiteSmoke; Ks = 10.0F; N = 20.0F; HabilitarTextura = false; direccionales = effect.Parameters["direccionales"]; } #region IEspecular Members /// <summary> /// Propiedad que obtiene o establece el coeficiente especular. /// </summary> public float Ks { get { return ks; } set { ks = value; base.effect.Parameters["ks"].SetValue(ks); } }// fin de la propiedad Ks /// <summary> /// Propiedad que obtiene o establece la potencia especular. /// </summary> public float N { get { return n; } set { n = value; base.effect.Parameters["n"].SetValue(n); } }// fin de la propiedad N /// <summary> /// Propiedad que obtiene o establece el color especular. /// </summary> public Color ColorEspecular { get { return colorEspecular; } set { colorEspecular = value; base.effect.Parameters["colorMaterialEspecular"].SetValue(

145

62. 63. 64. 65. 66. 67. 68. 69. 70. 71. 72. 73. 74. 75. 76. 77. 78. 79. 80. 81. 82. 83. 84. 85. 86. 87. 88. 89. 90. 91. 92. 93. 94. 95. 96. 97. 98. 99. 100. 101. 102. 103. 104. 105. 106. 107. 108. 109. 110. 111. 112. 113. 114. 115. 116. 117. 118. 119. 120. 121. 122. 123. 124. 125. 126. 127. 128. 129. 130. 131. 132.

colorEspecular.ToVector4()); } }// fin de la propiedad ColorEspecular /// <summary> /// Propiedad que habilita o inhabilita el modelo de iluminacin especular. /// </summary> public bool HabilitarEspecular { get { return habilitarEspecular; } set { habilitarEspecular = value; effect.Parameters["habiEspec"].SetValue(habilitarEspecular); } }// fin de la propiedad HabilitarEspecular /// <summary> /// Propiedad que obtiene o establece la posicion de la cmara. /// </summary> public Vector3 PosicionCamara { get { return posicionCamara; } set { posicionCamara = value; effect.Parameters["posicionCamara"].SetValue(posicionCamara); } }// fin de la propiedad PosicionCamara #endregion #region IDifuso Members /// <summary> /// Propiedad que obtiene o establece el color difuso. /// </summary> public Color ColorDifuso { get { return colorDifuso; } set { colorDifuso = value; base.effect.Parameters["colorMaterialDifuso"].SetValue( colorDifuso.ToVector4()); } }// fin de la propiedad ColorDifuso #endregion /// <summary> /// Propiedad que obtiene o establece el nmero de luces. /// </summary> public Int16 NumeroLuces { get { return numeroLuces; } set { numeroLuces = value; base.effect.Parameters["numLuces"].SetValue(numeroLuces); } }// fin de la propiedad NumeroLuces /// <summary> /// Propiedad que obtiene el parmetro de la estructura /// que corresponde a las luces direccionales. /// </summary> public EffectParameter Direccionales { get { return direccionales; } } public override void DrawModelMeshPart(ModelMeshPart modelMeshPart, GraphicsDevice graphicsDevice)

146

133. 134. 135. 136. 137. 138. 139. 140. 141. 142. 143. 144. 145. 146. 147. 148. 149. 150. 151. 152. 153. 154. 155. 156. 157. 158. 159. 160. 161. 162. 163. 164. 165. 166.

{ effect.Begin(); #region Effect.Begin effect.CurrentTechnique.Passes["Ambiental"].Begin(); graphicsDevice.DrawIndexedPrimitives(PrimitiveType.TriangleList, modelMeshPart.BaseVertex, 0, modelMeshPart.NumVertices, modelMeshPart.StartIndex, modelMeshPart.PrimitiveCount); effect.CurrentTechnique.Passes["Ambiental"].End(); // se habilita el blending graphicsDevice.RenderState.AlphaBlendEnable = true; graphicsDevice.RenderState.BlendFunction = BlendFunction.Add; graphicsDevice.RenderState.DestinationBlend = Blend.One; graphicsDevice.RenderState.SourceBlend = Blend.One; effect.CurrentTechnique.Passes["MultiLuces"].Begin(); effect.CommitChanges(); graphicsDevice.DrawIndexedPrimitives(PrimitiveType.TriangleList, modelMeshPart.BaseVertex, 0, modelMeshPart.NumVertices, modelMeshPart.StartIndex, modelMeshPart.PrimitiveCount); effect.CurrentTechnique.Passes["MultiLuces"].End(); // valor por default de XNA graphicsDevice.RenderState.AlphaBlendEnable = false; #endregion effect.End(); }// fin del mtodo DrawModelMeshPart }// fin de la clase FxMultiluces

Para implementar esta ltima clase, se debe crear el objeto de la clase Multiluces y las luces de tipo direccional. En la clase Game1 de la solucin, escriba las siguientes variables de instancia que indican dichos objetos.
FxMultiluces fxMultiluces; LuzDireccional[] lucesDireccionales;

Y en el mtodo LoadContent, de la clase Game1, se carga el shader en el constructor de la clase FxMultiluces, lnea 1, Cdigo 8-13. Luego se inicializan las propiedades del objeto fxMultilices, lneas 2 10. La creacin de las luces direccionales se hace dentro del mismo mtodo LoadContent, pues su constructor necesita la direccin de memoria de cada elemento que indica una luz del shader, lneas 12 24. Luego se inicializan las propiedades de cada fuente de iluminacin, lneas 26 37.
Cdigo 8-13
1. 2. 3. 4. 5. 6. 7. 8. 9. 10. 11. 12. 13. 14. 15. 16. fxMultiluces = new FxMultiluces(Content.Load<Effect>(@"Shaders\MulDirec")); fxMultiluces.NumeroLuces = 6; fxMultiluces.Textura2D = textura; fxMultiluces.HabilitarEspecular = true; fxMultiluces.HabilitarTextura = true; fxMultiluces.ColorDifuso = new Color(3, 3, 3); fxMultiluces.ColorAmbiental = Color.Black; fxMultiluces.Ks = 10; fxMultiluces.N = 20; fxMultiluces.PosicionCamara = posicion; lucesDireccionales = new LuzDireccional[fxMultiluces.NumeroLuces]; lucesDireccionales[0] = new LuzDireccional( fxMultiluces.Direccionales.Elements[0], new Vector3(0, -1, 0)); lucesDireccionales[1] = new LuzDireccional( fxMultiluces.Direccionales.Elements[1], new Vector3(0, 1, 0));

147

17. 18. 19. 20. 21. 22. 23. 24. 25. 26. 27. 28. 29. 30. 31. 32. 33. 34. 35. 36. 37.

lucesDireccionales[2] = new LuzDireccional( fxMultiluces.Direccionales.Elements[2], new lucesDireccionales[3] = new LuzDireccional( fxMultiluces.Direccionales.Elements[3], new lucesDireccionales[4] = new LuzDireccional( fxMultiluces.Direccionales.Elements[4], new lucesDireccionales[5] = new LuzDireccional( fxMultiluces.Direccionales.Elements[5], new

Vector3(0, 0, 1)); Vector3(0, 0, -1)); Vector3(1, 0, 0)); Vector3(-1, 0, 0));

lucesDireccionales[0].Encender = true; lucesDireccionales[0].Color = Color.Green; lucesDireccionales[1].Encender = true; lucesDireccionales[1].Color = Color.LawnGreen; lucesDireccionales[2].Encender = true; lucesDireccionales[2].Color = Color.Indigo; lucesDireccionales[3].Encender = true; lucesDireccionales[3].Color = Color.Brown; lucesDireccionales[4].Encender = true; lucesDireccionales[4].Color = Color.Chocolate; lucesDireccionales[5].Encender = true; lucesDireccionales[5].Color = Color.LemonChiffon;

Para concluir con este ejemplo, en el mtodo Draw de la clase Game1, Cdigo 8-14, se establecen las matrices de mundo, lnea 17; vista, lnea 10; y proyeccin, lnea 11; del objeto fxMultiluces. Y se llama al mtodo DrawModelMeshPart de la instancia fxMultiluces, lnea 32. Ejecute el programa, y corrija cualquier error de compilacin que surgiese y vuelva a intentarlo, ver algo parecido a la ilustracin 8 7.
Cdigo 8-14
1. 2. 3. 4. 5. 6. 7. 8. 9. 10. 11. 12. 13. 14. 15. 16. 17. 18. 19. 20. 21. 22. 23. 24. 25. 26. 27. 28. 29. 30. 31. 32. 33. 34. 35. 36. 37. protected override void Draw(GameTime gameTime) { GraphicsDevice.Clear(Color.White); fxAmbiental.View = view; fxAmbiental.Projection = projection; fxDireccional.View = fxAmbiental.View; fxDireccional.Projection = fxAmbiental.Projection; fxMultiluces.View = fxAmbiental.View; fxMultiluces.Projection = fxAmbiental.Projection; foreach (ModelMesh mesh in elefante.Meshes) { fxAmbiental.World = transformaciones[mesh.ParentBone.Index]; fxDireccional.World = fxAmbiental.World; fxMultiluces.World = fxAmbiental.World;

GraphicsDevice.Indices = mesh.IndexBuffer; foreach (ModelMeshPart meshPart in mesh.MeshParts) { GraphicsDevice.Vertices[0].SetSource(mesh.VertexBuffer, meshPart.StreamOffset, meshPart.VertexStride); GraphicsDevice.VertexDeclaration = meshPart.VertexDeclaration; fxAmbiental.DrawModelMeshPart(meshPart, GraphicsDevice); fxDireccional.World *= Matrix.CreateTranslation(-100, 0, -200); fxDireccional.DrawModelMeshPart(meshPart, GraphicsDevice); fxMultiluces.World *= Matrix.CreateTranslation(100, 0, 200); fxMultiluces.DrawModelMeshPart(meshPart, GraphicsDevice); } } base.Draw(gameTime); }

148

Este ltimo ejemplo demuestra que tan complicado se puede hacer la comunicacin entre los datos de XNA hacia el shader, adems representa un gran nmero de lneas de cdigo en comparacin con el primer ejemplo que se vi en este captulo, pero que sin duda es mejor.

Ilustracin 8-7 Integracin del shader Multiluces en XNA

Como ejercicio para el lector se deja crear la clase que comunique los datos desde XNA hacia el shader MultiLuces que se vio en el apartado Iluminacin, en donde se tienen diferentes fuentes de iluminacin.

149

9 Colisin
Una colisin es una configuracin en la que dos objetos ocupan parte de una misma porcin del espacio 38 al mismo tiempo. La anterior definicin sobre lo que es colisin en computacin grfica rompe la propiedad de la impenetrabilidad de la fsica, y es este mismo que se toma en cuenta en los siguientes ejemplos. Los cuales muestran la impenetrabilidad con diferentes tipos de envolventes de colisin. Un volumen envolvente es una primitiva simple que encierra a una forma ms compleja y al que se le 39 puede hacer una prueba de interseccin ms rpido en trminos computacionales. Es decir, toda geometra compleja puede ser sustituida por una geometra ms sencilla que envuelva a la primera, permitiendo clculos ms sencillos a un costo de la impenetrabilidad. Existen varias envolventes de colisin como son: Esfera envolvente (Bounding Sphere, BS) Caja envolvente alineada con los ejes (Axis Aligned Bounding Box, AABB) Caja envolvente orientada (Oriented Boundig Box, OBB) Politopo de Orientacin Discreta (Discrete Orientation Polytope, k-DOP)

En la Ilustracin 9-1 se muestra la figura de Hebe envuelta en una esfera y en una caja. La primera cubre un mayor volumen que la estatua, lo que no permitira a otra acercarse lo suficiente. La segunda envolvente esta ms pegada a Hebe, lo que permite a otra acercarse lo suficiente como para no atravesarla.

Ilustracin 9-1 Envolvente de colisin

La seleccin de qu tipo de envolvente es la ms adecuada, es aquella que sea lo ms cercana a las dimensiones de la figura, tomando en cuenta el costo computacional, pues las pruebas de interseccin que se hacen con cada envolvente se hace cada vez que se actualiza el dibujo o se atiende una interrupcin. La manipulacin de la colisin se puede ver en tres pasos: Deteccin de colisin Determinacin de colisin Repuesta a la colisin

La deteccin de colisin es un valor lgico que indica si dos o ms objetos han colisionado. Para conocer si existe o no se determina la colisin con los clculos de las intersecciones. Y por ltimo se toman acciones en respuesta a la colisin.
38 39

Deteccin de Colisiones mediante Recubrimientos Simpliciales. D. Juan Jos Jimenz Delgado. 2006 Universidad de Granada. p. 11. dem p. 20

150

Este texto no mostrar todas las envolventes de colisin, ni tampoco todas las combinaciones que puedan existir entre cada una de ellas para determinar una, esto sale del alcance del texto. XNA ofrece algunos mtodos que devuelven un valor lgico cuando sucede una colisin. Sin embargo, no ofrece tipos de acciones que se deben llevar a cabo una vez detectada, esto depende del problema. Es por eso, que en los siguientes cuatro ejemplos se muestra el uso de dichos mtodos de deteccin, de determinacin y respuesta; estos dos ltimos son implementaciones que el autor determin, pero no indican que sean las mejores. En cada ejemplo se manejan modelos tridimensionales con un solo ModelMesh; quedara como ejercicio para el lector, crear el programa que pueda manejar ms de un ModelMesh para las colisiones.

9.1 Bounding Sphere vs. Bounding Sphere


La envolvente de colisin Bounding Sphere (BS) es una esfera que encierra a la geometra principal. Esta envolvente ofrece dos propiedades para los clculos: radio y centro. XNA ya ofrece mtodos que calculan la envolvente a partir de los vectores de posicin de los vrtices y a partir de otras envolventes. Por otra parte ofrece mtodos que indican el valor lgico si encuentra una colisin con otra envolvente. A partir de los ejemplos se har mencin de cada uno de estos mtodos. Para este primer ejemplo, se hace mover una figura por medio del teclado o el gamepad, para hacerlo colisionar contra otra figura. Cada una estar envuelta en una esfera. Una vez determinada la colisin, se realizan clculos para hacer retroceder la envolvente y aparentar un choque, es decir, solo se detendr en la direccin del movimiento cuando exista colisin. Para mostrar en tiempo real los cambios numricos que sufren las propiedades de las envolventes de colisin se dibujar con un SpriteFont en pantalla, las propiedades de cada envolvente. Comience por crear un nuevo proyecto en Visual Studio y seleccione la plantilla de Windows Game (3.1). Agregue una nueva clase llamada Texto, Cdigo 9-1, La clase Texto ayudar a dibujar los valores numricos de cada envolvente. Las tres variables de instancia, lneas 9 11, sirven para dibujar el string en pantalla, cada una de ellas se explic en el Texto. La parte importante de la clase es el mtodo Draw, lneas 20 26, el cual recibe un string llamado mensaje y el color para la fuente, lnea 20. Despus de mandar a dibujar el texto, se habilita el DephBuffer, lnea 25, porque en la llamada al mtodo DrawString, lnea 23, se inhabilita el bfer.
Cdigo 9-1
1. 2. 3. 4. 5. 6. 7. 8. 9. 10. 11. 12. 13. 14. 15. 16. 17. 18. 19. 20. 21. 22. 23. 24. using System; using Microsoft.Xna.Framework.Graphics; using Microsoft.Xna.Framework; namespace TutorialX12 { public class Texto { GraphicsDevice graphicsDevice; SpriteBatch spriteBatch; SpriteFont spriteFont; public Texto(SpriteFont spriteFont, GraphicsDevice graphicsDevice) { this.graphicsDevice = graphicsDevice; spriteBatch = new SpriteBatch(graphicsDevice); this.spriteFont = spriteFont; }// fin del constructor public void Draw(String mensaje, Color color) { spriteBatch.Begin(); spriteBatch.DrawString(spriteFont, mensaje, Vector2.Zero, color); spriteBatch.End();

151

25. 26. 27. 28.

graphicsDevice.RenderState.DepthBufferEnable = true; }// fin del mtodo Draw }// fin de la clase Texto }// fin del namespace

La Ilustracin 9-2 es un diagrama UML que muestra dos clases: ModeloEstatico y ModeloDinamico, este ltimo hereda del primero. Para los ejemplos siguientes se seguir utilizando este diagrama de clases. La primera clase obtiene la envolvente de colisin de esfera, pinta el modelo tridimensional del archivo fbx o x y regresa el nombre de la figura, el centro y el radio de la esfera. La clase ModeloEstatico, Cdigo 9-2, tiene cuatro variables de instancia: modelo, lnea 9, que representa el modelo obtenido a partir de un archivo fbx o x; el arreglo transformaciones, lnea 10, guarda todas las matrices de transformacin del modelo; boundingSphere, lnea 11, representa la envolvente de colisin esfera; y nombre es un string que guarda el nombre de la figura 3D.

Ilustracin 9-2 Diagrama de clase. Modelos esttico y dinmicos.

El constructor, lneas 14 26, toma como parmetro un objeto Model e inicializa el arreglo de transformaciones, lneas 17 18. Cada objeto ModelMesh del Model contiene su propia envolvente de esfera; adems se sabe que el modelo del archivo contiene un solo ModelMesh, as que solo se le asigna a la variable boundingSphere, lnea 19. La esfera de colisin debe ser transformada por medio de una de matriz del modelo que indica la posicin correcta y tamao del radio de la envolvente. El objeto boundingSphere tiene un mtodo llamado Transform que sirve para trasladar o escalar el BS, lneas 21 23. El mtodo Transform tiene dos sobrecargas, la primera toma una matriz como parmetro y devuelve un BS; la segunda sobrecarga toma dos parmetros de entrada, el primero es la matriz de transformacin como referencia, y la segunda es el BS como salida. public void Transform (ref Matrix matrix, out BoundingSphere result) Para extraer el nombre de la figura, se asigna la propiedad Name, del primer ModelMesh del modelo a la variable nombre, lnea 25. La propiedad EnvolventeEsfera, lnea 31, obtiene la BoundingSphere para hacer pruebas de colisin. El mtodo Draw, lneas 33 49, dibuja el Model extrado del archivo del modelo; este mismo mtodo se vio en el Cmo cargar modelos 3D desde un archivo X y FBX. Por ltimo, el mtodo ToString, lneas 55 61, devuelve un string con el nombre del primer ModelMesh del modelo 3D, el centro y radio de la esfera de colisin.
Cdigo 9-2
1. 2. 3. 4. 5. 6. 7. 8. 9. using System; using Microsoft.Xna.Framework; using Microsoft.Xna.Framework.Graphics; namespace TutorialX12 { public class ModeloEstatico { private Model modelo;

152

10. 11. 12. 13. 14. 15. 16. 17. 18. 19. 20. 21. 22. 23. 24. 25. 26. 27. 28. 29. 30. 31. 32. 33. 34. 35. 36. 37. 38. 39. 40. 41. 42. 43. 44. 45. 46. 47. 48. 49. 50. 51. 52. 53. 54. 55. 56. 57. 58. 59. 60. 61. 62. 63.

private Matrix[] transformaciones; protected BoundingSphere boundingSphere; string nombre; public ModeloEstatico(Model modelo) { this.modelo = modelo; transformaciones = new Matrix[modelo.Bones.Count]; modelo.CopyAbsoluteBoneTransformsTo(transformaciones); boundingSphere = modelo.Meshes[0].BoundingSphere; // traslacin del centro de la envolvente boundingSphere.Transform( ref transformaciones[modelo.Meshes[0].ParentBone.Index], out boundingSphere); nombre = modelo.Meshes[0].Name; }// fin del constructor /// <summary> /// Propiedad que obtiene la envolvente esfera. /// </summary> public BoundingSphere EnvolventeEsfera { get { return boundingSphere; } } public virtual void Draw(ref Matrix world, ref Matrix view, ref Matrix projection) { foreach (ModelMesh mesh in modelo.Meshes) { foreach (BasicEffect basicEffect in mesh.Effects) { basicEffect.World = transformaciones[mesh.ParentBone.Index] * world; basicEffect.View = view; basicEffect.Projection = projection; basicEffect.EnableDefaultLighting(); basicEffect.PreferPerPixelLighting = true; }// fin del foreach mesh.Draw(); }// fin del foreach }// fin del mtodo Draw /// <summary> /// Propiedad que devuelve informacin de la envolvente esfera. /// </summary> /// <returns></returns> public override string ToString() { return string.Format("{0}\nBoundingSphere.Center {1}\nRadio {2}", nombre, boundingSphere.Center.ToString(), boundingSphere.Radius); }// fin del mtodo ToString }// fin de la clase ModeloEstatico }// fin del namespace

La clase ModeloDinamico, Error! No se encuentra el origen de la referencia., traslada la geometra con ayuda de las teclas de navegacin o el gamepad. En caso de encontrar una colisin en contra de una envolvente de esfera, el movimiento del objeto se detendr en esa direccin de desplazamiento, evitando que se traslapen las figuras. En esta clase se utilizan dos matrices de traslacin, lneas 11 12, la primera sirve para trasladar todo el modelo tridimensional y la segunda matriz es para trasladar el centro de la esfera. La distancia mxima que puede desplazarse la figura ser dada por la constante step, lnea 10. La Tabla 9-1 enlista las teclas, ejes del stick derecho, y los triggers que se usan para desplazar el objeto tridimensional.

153

Tabla 9-1

Tecla Up Down Right Left PageUp PageDown

GamePad

Movimiento Desplazamiento negativo sobre el eje Z. Desplazamiento positivo sobre el eje Z. Desplazamiento positivo sobre el eje X. Desplazamiento negativo sobre el eje X. Desplazamiento positivo sobre el eje Y. Desplazamiento negativo sobre el eje Y.

ThumbSticks.Right.Y

ThumbSticks.Right.X

Triggers.Right Triggers.Left

El cuerpo del constructor no contiene ninguna inicializacin, lneas 14 17, tan solo se pasa el parmetro modelo al constructor de la clase base. La actualizacin de la informacin del desplazamiento se genera en el mtodo Update, lneas 19 81. La primera parte corresponde al teclado, lneas 21 52, en donde, en cada cuerpo del condicional if se crea la matriz de traslacin para cambiar el centro de la envolvente de esfera. Enseguida se llama al mtodo DetenerMovimiento que actualiza la posicin de la figura y el de la envolvente. La segunda parte corresponde al gamepad, lneas 54 80; en la creacin de cada matriz de traslacin se multiplica la constante step por el cambio en el eje del stick o por el cambio en los triggers, luego se llama al mtodo DetenerMovimiento. El mtodo DetenerMovimiento, lneas 87 117, determina si el movimiento de la envolvente del objeto ModeloDinamico continua o se debe detener. El mtodo recibe una envolvente de esfera de colisin, para buscar la colisin con su envolvente. En la lnea 90, se traslada la envolvente del objeto ModeloDinamico con el mtodo Transform. La instancia del BoundingSphere contiene el mtodo Intersects, lnea 92, que comprueba la colisin entre la envolvente actual y alguna en especfico, en este caso la del objeto esttico. El mtodo Intersects 40 est sobrecargado para las diferentes envolventes que ofrece XNA. public bool Intersects (BoundingSphere sphere) Tambin existe el mtodo Contains, que comprueba si el BoundingSphere actual contiene a la envolvente dada, y al igual que Intersects, sta tambin se encuetra sobrecargada. public ContainmentType Contains (BoundingSphere sphere) Una vez que se ha detectado la interseccin entre las envolventes, se recorre el centro del BundingSphere del modelo dinmico, lneas 96 -98, a una posicin anterior a la colisin; esto se logra

40

Las diferentes sobrecargas se pueden revisarse en la siguente pgina de Internet: http://msdn.microsoft.com/query/dev10.query?appId=Dev10IDEF1&l=ESES&k=k(MICROSOFT.XNA.FRAMEWORK.BOUNDINGSPHERE.INTERSECTS)&rd=true

154

multiplicando la matriz de traslacin de la envolvente actual por menos uno y despus multiplandola por el centro de la esfera actual. En la Ilustracin 9-3 se muestran dos esferas A y B, la primera representa el modelo dinmico y la segunda el esttico. El vector representa el vector unitario paralelo a la resta entre los centros y . =|
|

(1)

Ilustracin 9-3 Distancia entre dos esferas

El valor de es: la diferencia entre la distancia de los vectores de posicin de los centros de las esferas y la suma de los radios de stas. El vector de traslacin para la esfera A estar dada por: | ( + ) = | (2)

| ( + ) = | | = ( ) 1 |
+

(3) (4)

En las lneas 101 105, se calcula el valor de la distancia que falta para que las esferas estn tan cerca como sea posible, aplicando la ecuacin (4). Tmese en cuenta que la distancia entre dos vectores cualesquiera, es el mdulo entre la diferencia entre ellos. Por eso se utiliza el mtodo esttico Distance, de la escructura Vector3. Se vuelve a crear la matriz de traslacin para el centro de la esfera del modelo dinmico, lnea 106, y se cambia la posicin de la esfera con el mtodo Transform, lneas 109 110. Al final del condicional if se multiplica la matriz de transformacin de la esfera por la del modelo geomtrico, para volvrselo a asignar a este ltimo. En el mtodo Draw, lneas 126 131, se multiplica la matriz de traslacin de la geometra por la matriz de mundo, lnea 129, por si hay alguna modificacin desde el exterior sobre la figura. Luego se hace llamar el 155

mtodo Draw de la clase base con la nueva matriz mtxTraslacion, como su primer parmetro; los dems pasan sin cambio desde la clase hija.
Cdigo 9-3
1. 2. 3. 4. 5. 6. 7. 8. 9. 10. 11. 12. 13. 14. 15. 16. 17. 18. 19. 20. 21. 22. 23. 24. 25. 26. 27. 28. 29. 30. 31. 32. 33. 34. 35. 36. 37. 38. 39. 40. 41. 42. 43. 44. 45. 46. 47. 48. 49. 50. 51. 52. 53. 54. 55. 56. 57. 58. 59. 60. 61. 62. 63. 64. 65. using using using using System; Microsoft.Xna.Framework; Microsoft.Xna.Framework.Input; Microsoft.Xna.Framework.Graphics;

namespace TutorialX12 { public class ModeloDinamico : ModeloEstatico { private const float step = 1.0F; private Matrix mtxTraslacion = Matrix.Identity; private Matrix mtxTraslacionCenter; public ModeloDinamico(Model modelo) : base(modelo) { }// fin del constructor public void Update(BoundingSphere boudingSphereB) { #region Keyboard if (Keyboard.GetState().IsKeyDown(Keys.Up)) { mtxTraslacionCenter = Matrix.CreateTranslation(0, 0, -step); DetenerMovimiento(boudingSphereB); } if (Keyboard.GetState().IsKeyDown(Keys.Down)) { mtxTraslacionCenter = Matrix.CreateTranslation(0, 0, step); DetenerMovimiento(boudingSphereB); } if (Keyboard.GetState().IsKeyDown(Keys.Right)) { mtxTraslacionCenter = Matrix.CreateTranslation(step, 0, 0); DetenerMovimiento(boudingSphereB); } if (Keyboard.GetState().IsKeyDown(Keys.Left)) { mtxTraslacionCenter = Matrix.CreateTranslation(-step, 0, 0); DetenerMovimiento(boudingSphereB); } if (Keyboard.GetState().IsKeyDown(Keys.PageUp)) { mtxTraslacionCenter = Matrix.CreateTranslation(0, step, 0); DetenerMovimiento(boudingSphereB); } if (Keyboard.GetState().IsKeyDown(Keys.PageDown)) { mtxTraslacionCenter = Matrix.CreateTranslation(0, -step, 0); DetenerMovimiento(boudingSphereB); } #endregion #region Gamepad if (GamePad.GetState(PlayerIndex.One).ThumbSticks.Right.Y != 0.0F) { mtxTraslacionCenter = Matrix.CreateTranslation(0, 0, -step * GamePad.GetState(PlayerIndex.One).ThumbSticks.Right.Y); DetenerMovimiento(boudingSphereB); } if (GamePad.GetState(PlayerIndex.One).ThumbSticks.Right.X != 0.0F) { mtxTraslacionCenter = Matrix.CreateTranslation(step * GamePad.GetState(PlayerIndex.One).ThumbSticks.Right.X , 0, 0);

156

66. 67. 68. 69. 70. 71. 72. 73. 74. 75. 76. 77. 78. 79. 80. 81. 82. 83. 84. 85. 86. 87. 88. 89. 90. 91. 92. 93. 94. 95. 96. 97. 98. 99. 100. 101. 102. 103. 104. 105. 106. 107. 108. 109. 110. 111. 112. 113. 114. 115. 116. 117. 118. 119. 120. 121. 122. 123. 124. 125. 126. 127. 128. 129. 130. 131. 132. 133.

DetenerMovimiento(boudingSphereB); } if (GamePad.GetState(PlayerIndex.One).Triggers.Right != 0.0F) { mtxTraslacionCenter = Matrix.CreateTranslation(0, step * GamePad.GetState(PlayerIndex.One).Triggers.Right, 0); DetenerMovimiento(boudingSphereB); } if (GamePad.GetState(PlayerIndex.One).Triggers.Left != 0.0F) { mtxTraslacionCenter = Matrix.CreateTranslation(0, -step * GamePad.GetState(PlayerIndex.One).Triggers.Left, 0); DetenerMovimiento(boudingSphereB); } #endregion }// fin del mtodo Update /// <summary> /// Mtodo que detiene el movimiento de la BS, si sta intersecta con otra. /// </summary> /// <param name="boudingSphereB"></param> private void DetenerMovimiento(BoundingSphere boundingSphereB) { // se traslada el centro de la envolvente del modelo dinmico boundingSphere.Transform(ref mtxTraslacionCenter, out boundingSphere); if (boundingSphere.Intersects(boundingSphereB)) { // se invierte el vector de traslacin para regresar la posicin // del centro de la esfera, antes de intersecar con boundigSphereB mtxTraslacionCenter.Translation = -mtxTraslacionCenter.Translation; boundingSphere.Transform(ref mtxTraslacionCenter, out boundingSphere); // se clcula la distancia a recorrer la envolvente de esfera Vector3 distancia = (boundingSphereB.Center boundingSphere.Center) * (1 - (boundingSphere.Radius + boundingSphereB.Radius) / Vector3.Distance(boundingSphereB.Center, boundingSphere.Center)); mtxTraslacionCenter = Matrix.CreateTranslation(distancia); // se actualiza el centro de la esfera boundingSphere.Transform(ref mtxTraslacionCenter, out boundingSphere); }// fin del if // se multiplica la matriz de traslacin del centro // de la envolvente de la esfera por la matriz de // traslacin de la figura mtxTraslacion *= mtxTraslacionCenter; }// fin del mtodo DetenerMovimiento /// <summary> /// Mtodo que dibuja la geometra. /// </summary> /// <param name="world">Matriz de mundo.</param> /// <param name="view">Matriz de vista.</param> /// <param name="projection">Matriz de proyeccin.</param> public override void Draw(ref Matrix world, ref Matrix view, ref Matrix projection) { mtxTraslacion *= world; base.Draw(ref mtxTraslacion, ref view, ref projection); }// fin del mtodo } }

Para la clase Game1, Cdigo 9-4, se necesitan dos modelos que contengan un ModelMesh cada uno, y un SpriteFont. En este ejemplo se utiliz una esfera y una tetera para representar a los objetos 157

ModeloEstatico y ModeloDinamico, respectivamente, vase Ilustracin 9-4. Cada una de estas figuras se representar por los objetos modeloEstatico, lnea 20, y modeloDinamico, lnea 21. La variable texto, lnea 23, es para mostrar la informacin de las envolventes de esfera de cada objeto. En el mtodo Initialize, lneas 33 46, se inicializan las matrices de mundo, vista y proyeccin. En el mtodo LoadContent, lneas 48 58, se crean las instancias de ModeloEstatico, ModeloDinamico y Texto. Para actualizar el estado de modeloDinamico se hace llamar su mtodo Update en el mismo de la clase Game1, lnea 71. El parmetro que toma es la envolvente del modeloEstatico.

Ilustracin 9-4 BS vs BS

Por ltimo, se presenta el mtodo Draw, lneas 76 86, que hace llamar a los mtodos de dibujo de cada objeto, modeloEstatico, modeloDinamico y texto. El mtodo Draw de texto, lneas 82, toma como primer parmetro el string devuelto por el mtodo ToString de modeloEstatico, y en la lnea 83 toma el string de modeloDinamico; para diferenciar a cada uno se le da diferentes colores al mtodo Draw de texto. Ejecute el programa y pruebe con el gamepad o el teclado el mover el modeloDinamico, ver que la informacin de sta cambia y al momento de acercarse tanto a la envolvente de modeloEstatico, se detendr en seco. Esta es una manera de responder al momento de saber que existe una colisin, sin embargo, no es la nica. Por ejemplo, se pudieron hacer este tipo de acciones al conocer que existe dicha situacin: Regresar al punto de inicio el modeloDinamico. Desaparecer el modeloDinamico. Rebotar el modeloDinamico. El modeloDinamico rodea al modeloEstatico. Etctera.

Cada una de esas acciones le corresponde un modelo matemtico diferente, o bien se puede crear una fsica de cuerpos rgidos completa. Lo que por desgracia est fuera del alcance de este texto.

158

Cdigo 9-4
1. 2. 3. 4. 5. 6. 7. 8. 9. 10. 11. 12. 13. 14. 15. 16. 17. 18. 19. 20. 21. 22. 23. 24. 25. 26. 27. 28. 29. 30. 31. 32. 33. 34. 35. 36. 37. 38. 39. 40. 41. 42. 43. 44. 45. 46. 47. 48. 49. 50. 51. 52. 53. 54. 55. 56. 57. 58. 59. 60. 61. 62. 63. 64. 65. 66. 67. 68. 69. using using using using using using using using using using using using System; System.Collections.Generic; System.Linq; Microsoft.Xna.Framework; Microsoft.Xna.Framework.Audio; Microsoft.Xna.Framework.Content; Microsoft.Xna.Framework.GamerServices; Microsoft.Xna.Framework.Graphics; Microsoft.Xna.Framework.Input; Microsoft.Xna.Framework.Media; Microsoft.Xna.Framework.Net; Microsoft.Xna.Framework.Storage;

namespace TutorialX12 { public class Game1 : Microsoft.Xna.Framework.Game { GraphicsDeviceManager graphics; SpriteBatch spriteBatch; ModeloEstatico modeloEstatico; ModeloDinamico modeloDinamico; Matrix world, view, projection; Texto texto; public Game1() { graphics = new GraphicsDeviceManager(this); Content.RootDirectory = "Content"; graphics.PreferredBackBufferHeight = 720; graphics.PreferredBackBufferWidth = 1280; } protected override void Initialize() { world = Matrix.Identity; view = Matrix.CreateLookAt( new Vector3(0, 100, 350), Vector3.Zero, Vector3.Up); projection = Matrix.CreatePerspectiveFieldOfView( MathHelper.PiOver4, GraphicsDevice.Viewport.AspectRatio, 1.0F, 10000.0F); base.Initialize(); } protected override void LoadContent() { spriteBatch = new SpriteBatch(GraphicsDevice); modeloEstatico = new ModeloEstatico( Content.Load<Model>("SphereA")); modeloDinamico = new ModeloDinamico( Content.Load<Model>("SphereB")); texto = new Texto(Content.Load<SpriteFont>("Arial"), GraphicsDevice); } protected override void UnloadContent() { // TODO: Unload any non ContentManager content here } protected override void Update(GameTime gameTime) { if (GamePad.GetState(PlayerIndex.One).Buttons.Back == ButtonState.Pressed) this.Exit();

159

70. 71. 72. 73. 74. 75. 76. 77. 78. 79. 80. 81. 82. 83. 84. 85. 86. 87. 88.

modeloDinamico.Update(modeloEstatico.EnvolventeEsfera); base.Update(gameTime); } protected override void Draw(GameTime gameTime) { GraphicsDevice.Clear(Color.White); modeloEstatico.Draw(ref world, ref view, ref projection); modeloDinamico.Draw(ref world, ref view, ref projection); texto.Draw(modeloEstatico.ToString(), Color.Brown); texto.Draw("\n\n\n" + modeloDinamico.ToString(), Color.Black); base.Draw(gameTime); } } }

9.2 Axis Aligned Bounding Box vs. Axis Aligned Bounding Box
La AABB por sus siglas en ingls, o Caja Envolvente Alineada con los Ejes, cubre la geometra con una caja alineada a los ejes del mundo. Esto puede causar un inconveniente en el momento de girar la geometra, porque puede cambiar las dimensiones de la envolvente, vase Ilustracin 9-5.

Ilustracin 9-5 AABB

La AABB se basa en dos puntos en el espacio llamados mximo y mnimo; cada uno representa en sus coordenadas el valor mximo o mnimo de los vectores de posicin de los vrtices de la geometra. La obtencin de dichos puntos se puede hacer buscando el menor y mayor valor entre las coordenadas de cada vrtice de la figura tridimensional. XNA proporciona mtodos para la obtencin de la envolvente a partir de las posiciones de los vrtices, de una envolvente de esfera y de dos instancias de BoundingBox especificadas. Al igual que el ejemplo de Bounding Sphere vs. Bounding Sphere, se har mover la instancia de ModeloDinamico para mostrar el choque entre dos AABB. La clase ModeloEstatico, Cdigo 9-5, es muy similar al Cdigo 9-2, a excepcin de la envolvente BoundingBox, lnea 11, y el mtodo ObtenerAABB, lneas 35 -53. La primera representa un volumen 3D en forma de caja alineado a los ejes, y el segundo es un mtodo privado que se describe ms adelante. En la lnea 20 se obtiene la AABB a partir de la informacin del bfer de vrtices del ModelMesh. Cabe aclarar que cada ModelMesh de un Model tiene su propio bfer, por lo tanto, en este ejemplo slo se ocupa el primer ModelMesh. 160

El mtodo ObtenerAABB regresa un BoundingBox a partir de un VertexBuffer, que recibe como parmetro. En las lneas 37 39, se declara un arreglo de VertexPositionNormalTexture para almacenar una copia de los vrtices del bfer. El tamao de dicho arreglo se obtiene a partir de la divisin entre el tamao en bytes del bfer y el tamao en bytes del tipo de dato, el resultado es el nmero de vrtices en el bfer. El tipo de dato debe soportar toda la informacin almacenada en el bfer, de no ser as, una excepcin en tiempo de ejecucin resultar. La informacin almacenada en el bfer de vrtices no se puede modificar, pero si se puede leer; para ello se hace una copia de los datos, con el mtodo VertexBuffer.GetData, lneas 40 41. public void GetData<T> (T[] data) Este mtodo es genrico y est sobrecargado tres veces. Las estructuras definidas por XNA, para los vrtices son lo ms apropiados para su uso. El arreglo debe ser lo suficientemente grande para hacer la copia y no provocar una excepcin en tiempo de ejecucin. Una vez que se tiene la copia de los vrtices se debe extraer los vectores de posicin, el arreglo puntos, lnea 43, es de tipo Vector3 y es de la misma dimensin que el arreglo de vrtices. En el ciclo for, lneas 44 49, se itera a travs del arreglo de vrtices, vertexPositionNormalTexture, para extraer la posicin y multiplicarla por la matriz de transformaciones que se obtiene del modelo, lneas 46 48; y almacenarla en el elemento correspondiente al arreglo puntos. El mtodo esttico BoundingBox.CreateFromPoints, lnea 52, crea el BoundingBox a partir de un grupo 41 de puntos. Este mtodo tambin acepta un List como entrada, pues ocupa una interfaz IEnumerable. public static BoundingBox CreateFromPoints (IEnumerable<Vector3> points)
Cdigo 9-5
1. 2. 3. 4. 5. 6. 7. 8. 9. 10. 11. 12. 13. 14. 15. 16. 17. 18. 19. 20. 21. 22. 23. 24. 25. 26. 27. 28. 29. 30. 31. 32. 33. 34.
41

using System; using Microsoft.Xna.Framework.Graphics; using Microsoft.Xna.Framework; namespace TutorialX13 { public class ModeloEstatico { private Model modelo; private Matrix[] transformaciones; protected BoundingBox boundingBox; string nombre; public ModeloEstatico(Model modelo) { this.modelo = modelo; transformaciones = new Matrix[modelo.Bones.Count]; modelo.CopyAbsoluteBoneTransformsTo(transformaciones); boundingBox = ObtenerAABB(modelo.Meshes[0].VertexBuffer); nombre = modelo.Meshes[0].Name; }// fin del constructor /// <summary> /// Propiedad que obtiene la AABB. /// </summary> public BoundingBox EnvolventeCaja { get { return boundingBox; } } /// /// /// /// /// <summary> Mtodo que obtiene la AABB. </summary> <param name="vertexBuffer">Bfer de vrtices.</param> <returns></returns>

Representa una lista de objetos.

161

35. 36. 37. 38. 39. 40. 41. 42. 43. 44. 45. 46. 47. 48. 49. 50. 51. 52. 53. 54. 55. 56. 57. 58. 59. 60. 61. 62. 63. 64. 65. 66. 67. 68. 69. 70. 71. 72. 73. 74. 75. 76. 77. 78. 79. 80.

private BoundingBox ObtenerAABB(VertexBuffer vertexBuffer) { VertexPositionNormalTexture[] vertexPositionNormalTexture = new VertexPositionNormalTexture[vertexBuffer.SizeInBytes / VertexPositionNormalTexture.SizeInBytes]; vertexBuffer.GetData<VertexPositionNormalTexture>( vertexPositionNormalTexture); // se copian las posiciones de los puntos en un arreglo de Vector3 Vector3[] puntos = new Vector3[vertexPositionNormalTexture.Length]; for (int i = 0; i < vertexPositionNormalTexture.Length; i++) { puntos[i] = Vector3.Transform( vertexPositionNormalTexture[i].Position, transformaciones[modelo.Meshes[0].ParentBone.Index]); }// fin del for // se crea el BoundinBox a partir de un arreglo de puntos return BoundingBox.CreateFromPoints(puntos); }// fin del mtodo ObtenerAABB public virtual void Draw(ref Matrix world, ref Matrix view, ref Matrix projection) { foreach (ModelMesh mesh in modelo.Meshes) { foreach (BasicEffect basicEffect in mesh.Effects) { basicEffect.World = transformaciones[mesh.ParentBone.Index] * world; basicEffect.View = view; basicEffect.Projection = projection; basicEffect.EnableDefaultLighting(); basicEffect.PreferPerPixelLighting = true; }// fin del foreach mesh.Draw(); }// fin del foreach }// fin del mtodo Draw public override string ToString() { return string.Format("{0}\nBoundingBox.Min {1}\nBoundingBox.Max {2}", nombre, boundingBox.Min, boundingBox.Max); }// fin del mtodo ToString }// fin de la clase ModeloEstatico }// fin del namespace TutorialX13

Al igual que en el ejemplo de Bounding Sphere vs. Bounding Sphere, se tiene una clase ModeloDinamico que cambia la posicin del modelo tridimensional, con el teclado o el gamepad. La asignacin de acciones para el teclado y los elementos del control del Xbox son los mismos que en el subtema anterior. En el Cdigo 9-6 la variable de instancia mtxTraslacionPuntos, lnea 11, sirve como una matriz de transformacin para los puntos mximo y minmo del AABB. La variable direccion, lnea12, es un numerador que presenta las seis posibles traslaciones, la definicin de dicha numeracin se presenta en el El mtodo Draw, lneas 171 176, no sufre ningn cambio con relacin al visto en el Cdigo 9-3. Cdigo 9-7. El mtodo Update, lneas 19 98, en cada condicional if se establece la direccin de hacia donde se mueve la figura, se crea la matrix de traslacin y se hace llamar al mtodo DetenerMovimiento. Como en el subtema anterior, se hace mover primero los puntos de la AABB a travs de la matriz mtxTraslacionPuntos, concebida en el mtodo Update, para conocer si existe colisin entre cajas. En caso de que exista, se hace retroceder la caja a una posicin anterior y se calcula una distancia lo suficientemente cercana entre los lmites de las cajas. Luego se hace mover los puntos, esa distancia, y por ltimo se multiplica la matriz de traslacin de puntos por la matriz de traslacin de la figura y el resultado es asignado a esta ltima. Si no existiera dicha colisin, solo se hace este ltimo paso. 162

El mtodo DetenerMovimiento, lneas 104 163, calcula las matrices de traslacin para la figura tridimensional y para los puntos de la AABB, que en realidad siempre est empujando la envolvente de caja a una posicin prudente para que no exista interseccin. En la primera parte de este mtodo se multiplican los puntos mximo y mnimo, de la AABB del objeto ModeloDinamico, por la matriz mtxTraslacionPuntos, lneas 117 120. Enseguida se busca la existencia de una interseccin entre las envolventes del modelo esttico, boundingBoxB, y el dinmico, boundingBox, lnea 112. Si el mtodo Intersects devuelve un valor verdadero, se comienza por retroceder los puntos del boundingBox a un estado anterior, esto se logra multiplicando la matriz de mtxTraslacionPuntos por menos uno, lnea 115, para luego aplicarlo sobre los puntos Min y Max de boundingBox, lneas 117 120. Se crea una variable de tipo flotante llamada dist, lnea 124, que sirve como margen de error para evitar que la distancia entre los lmites de las AABB sea cero, permitiendo que se acerque tanto como sea posible para mover el boundingBox en otra direccin. En la instruccin de control switch, lneas 125 154, se selecciona la direccin en la que se pretende mover el modelo dinmico. Esta seleccin sirve para realizar la operacin correcta entre los puntos mximo y mnimo de las AABB de cada objeto. Por ejemplo, en la Ilustracin 9-6, se tienen dos cajas. A representa el modelo dinmico y B es el modelo esttico. Los puntos naranjas indican los puntos mximos, y los verdes son los puntos mnimos de cada envolvente. La caja A se mueve en direccin de z positivo, lo que indica que su direccin es hacia atrs, y la distancia a recorrer A hacia B es el valor absoluto de la resta entre el componente z del punto mnimo de B menos el componente z del punto mximo de A. Este mismo razonamiento se aplica en cada posible movimiento de la envolvente A. = | | (1)

Ilustracin 9-6 Distancia entre AABBs

Sin embargo, al hacer la distancia d cero, el mtodo Intersects del BoundingBox seguir arrogando un valor verdadero, an si el movimiento siguiente no provoque una colisin. Es por eso que se agrega una constante de error a la ecuacin 1. Esta constante debe ser muy pequea, para no ser percibida en el dibujo final. Continuando con el enlistado, dentro del primer case, lnea 127, se clcua la distancia con la ecuacin 2, lnea 128, enseguida se crea la matriz de traslacin de los puntos, lnea 129. En cada caso se debe utilizar una ecuacin diferente para obtener la distancia correcta a recorrer la figura y la envolvente. Al final del swtich se calculan las nuevas posiciones de los puntos del boundingBox, lneas 156 159. = | | + (2)

163

Independientemente, si el cuerpo del condicional if es alcanzado o no, se multiplica la matriz de traslacin de los puntos, mtxTraslacionPuntos, por la matriz de traslacin de la figura, mtxTraslacion y el resultado es guardado en esta ltima, lnea 162.
Cdigo 9-6
1. 2. 3. 4. 5. 6. 7. 8. 9. 10. 11. 12. 13. 14. 15. 16. 17. 18. 19. 20. 21. 22. 23. 24. 25. 26. 27. 28. 29. 30. 31. 32. 33. 34. 35. 36. 37. 38. 39. 40. 41. 42. 43. 44. 45. 46. 47. 48. 49. 50. 51. 52. 53. 54. 55. 56. 57. 58. 59. 60. 61. 62. 63. 64. using System; using Microsoft.Xna.Framework; using Microsoft.Xna.Framework.Input; namespace TutorialX13 { public class ModeloDinamico : ModeloEstatico { private const float step = 1.0F; private Matrix mtxTraslacion = Matrix.Identity; private Matrix mtxTraslacionPuntos; private Direccion direccion; public ModeloDinamico(Microsoft.Xna.Framework.Graphics.Model modelo) : base(modelo) { }// fin del constructor public void Update(BoundingBox boundingBoxB) { #region Keyboard if (Keyboard.GetState().IsKeyDown(Keys.Up)) { direccion = Direccion.Adelante; mtxTraslacionPuntos = Matrix.CreateTranslation(0, 0, -step); DetenerMovimiento(boundingBoxB); } if (Keyboard.GetState().IsKeyDown(Keys.Down)) { direccion = Direccion.Atras; mtxTraslacionPuntos = Matrix.CreateTranslation(0, 0, step); DetenerMovimiento(boundingBoxB); } if (Keyboard.GetState().IsKeyDown(Keys.Right)) { direccion = Direccion.Derecha; mtxTraslacionPuntos = Matrix.CreateTranslation(step, 0, 0); DetenerMovimiento(boundingBoxB); } if (Keyboard.GetState().IsKeyDown(Keys.Left)) { direccion = Direccion.Izquierda; mtxTraslacionPuntos = Matrix.CreateTranslation(-step, 0, 0); DetenerMovimiento(boundingBoxB); } if (Keyboard.GetState().IsKeyDown(Keys.PageUp)) { direccion = Direccion.Arriba; mtxTraslacionPuntos = Matrix.CreateTranslation(0, step, 0); DetenerMovimiento(boundingBoxB); } if (Keyboard.GetState().IsKeyDown(Keys.PageDown)) { direccion = Direccion.Abajo; mtxTraslacionPuntos = Matrix.CreateTranslation(0, -step, 0); DetenerMovimiento(boundingBoxB); } #endregion #region Gamepad if (GamePad.GetState(PlayerIndex.One).ThumbSticks.Right.Y < 0) { direccion = Direccion.Atras; mtxTraslacionPuntos = Matrix.CreateTranslation(0, 0, step);

164

65. 66. 67. 68. 69. 70. 71. 72. 73. 74. 75. 76. 77. 78. 79. 80. 81. 82. 83. 84. 85. 86. 87. 88. 89. 90. 91. 92. 93. 94. 95. 96. 97. 98. 99. 100. 101. 102. 103. 104. 105. 106. 107. 108. 109. 110. 111. 112. 113. 114. 115. 116. 117. 118. 119. 120. 121. 122. 123. 124. 125. 126. 127. 128. 129. 130. 131. 132. 133. 134. 135.

DetenerMovimiento(boundingBoxB); } if (GamePad.GetState(PlayerIndex.One).ThumbSticks.Right.Y > 0) { direccion = Direccion.Adelante; mtxTraslacionPuntos = Matrix.CreateTranslation(0, 0, -step); DetenerMovimiento(boundingBoxB); } if (GamePad.GetState(PlayerIndex.One).ThumbSticks.Right.X < 0) { direccion = Direccion.Izquierda; mtxTraslacionPuntos = Matrix.CreateTranslation(-step, 0, 0); DetenerMovimiento(boundingBoxB); } if (GamePad.GetState(PlayerIndex.One).ThumbSticks.Right.X > 0) { direccion = Direccion.Derecha; mtxTraslacionPuntos = Matrix.CreateTranslation(step, 0, 0); DetenerMovimiento(boundingBoxB); } if (GamePad.GetState(PlayerIndex.One).Triggers.Right != 0) { direccion = Direccion.Arriba; mtxTraslacionPuntos = Matrix.CreateTranslation(0, step, 0); DetenerMovimiento(boundingBoxB); } if (GamePad.GetState(PlayerIndex.One).Triggers.Left != 0) { direccion = Direccion.Abajo; mtxTraslacionPuntos = Matrix.CreateTranslation(0, -step, 0); DetenerMovimiento(boundingBoxB); } #endregion }// fin del mtodo Update /// <summary> /// Mtodo que detiene el movimiento de la BS, si sta intersecta con otra. /// </summary> /// <param name="boudingSphereB"></param> private void DetenerMovimiento(BoundingBox boundingBoxB) { // se trasladan los puntos Min y Max de BoundingBox Vector3.Transform(ref boundingBox.Min, ref mtxTraslacionPuntos, out boundingBox.Min); Vector3.Transform(ref boundingBox.Max, ref mtxTraslacionPuntos, out boundingBox.Max); if (boundingBox.Intersects(boundingBoxB)) { // se regresan los puntos a una posicin anterior mtxTraslacionPuntos.Translation = -mtxTraslacionPuntos.Translation; Vector3.Transform(ref boundingBox.Min, ref mtxTraslacionPuntos, out boundingBox.Min); Vector3.Transform(ref boundingBox.Max, ref mtxTraslacionPuntos, out boundingBox.Max); // se calcula la distancia que falta para que // las envolventes estn tan cerca // como para no intersecarse float dist = -0.01F; switch (direccion) { case Direccion.Atras: dist += Math.Abs(boundingBoxB.Min.Z - boundingBox.Max.Z); mtxTraslacionPuntos = Matrix.CreateTranslation(0, 0, dist); break; case Direccion.Adelante: dist += Math.Abs(boundingBox.Min.Z - boundingBoxB.Max.Z); mtxTraslacionPuntos = Matrix.CreateTranslation( 0, 0, -dist); break;

165

136. 137. 138. 139. 140. 141. 142. 143. 144. 145. 146. 147. 148. 149. 150. 151. 152. 153. 154. 155. 156. 157. 158. 159. 160. 161. 162. 163. 164. 165. 166. 167. 168. 169. 170. 171. 172. 173. 174. 175. 176. 177. 178.

case Direccion.Derecha: dist += Math.Abs(boundingBoxB.Min.X - boundingBox.Max.X); mtxTraslacionPuntos = Matrix.CreateTranslation(dist, 0, 0); break; case Direccion.Izquierda: dist += Math.Abs(boundingBox.Min.X - boundingBoxB.Max.X); mtxTraslacionPuntos = Matrix.CreateTranslation( -dist, 0, 0); break; case Direccion.Abajo: dist += Math.Abs(boundingBox.Min.Y - boundingBoxB.Max.Y); mtxTraslacionPuntos = Matrix.CreateTranslation( 0, -dist, 0); break; case Direccion.Arriba: dist += Math.Abs(boundingBoxB.Min.Y - boundingBox.Max.Y); mtxTraslacionPuntos = Matrix.CreateTranslation(0, dist, 0); break; }; // se trasladan los puntos a la nueva posicin Vector3.Transform(ref boundingBox.Min, ref mtxTraslacionPuntos, out boundingBox.Min); Vector3.Transform(ref boundingBox.Max, ref mtxTraslacionPuntos, out boundingBox.Max); }// fin del if mtxTraslacion *= mtxTraslacionPuntos; }// fin del mtodo DetenerMovimiento /// <summary> /// Mtodo que dibuja la geometra. /// </summary> /// <param name="world">Matriz de mundo.</param> /// <param name="view">Matriz de vista.</param> /// <param name="projection">Matriz de proyeccin.</param> public override void Draw(ref Matrix world, ref Matrix view, ref Matrix projection) { mtxTraslacion *= world; base.Draw(ref mtxTraslacion, ref view, ref projection); }// fin del mtodo }// fin de la clase }// fin del namespace

El mtodo Draw, lneas 171 176, no sufre ningn cambio con relacin al visto en el Cdigo 9-3.
Cdigo 9-7
1. 2. 3. 4. 5. 6. 7. 8. 9. 10. 11. 12. 13. 14. using System; namespace TutorialX13 { public enum Direccion { Adelante, Atras, Abajo, Arriba, Derecha, Izquierda, } }

Para probar la colisin entre las AABBs se reutiliza el Cdigo 9-4, con un ligero cambio en la llamada del mtodo Update, lnea 71; pues esta vez se pasa como parmetro una BoundingBox.
modeloDinamico.Update(modeloEstatico.EnvolventeCaja);

166

Corra la aplicacin y corrija cualquier error en tiempo de ejecucin que pudiera suceder. En la Ilustracin 9-7 se muestran dos figuras de elefante colisionando.

Ilustracin 9-7 AABB vs. AABB

9.3 Ray vs. Boundign Sphere


A pesar que las envolventes de colisin ayudan en gran medida a conocer si dos o ms figuras impactan, reduciendo los clculos y tiempos para la computadora; a veces existen cosas pequeas que bien podran ser abstradas como partcula, permitiendo un mejor desempeo computacional que si se hubieran manejado como esfera u otra envolvente. Por ejemplo: La deteccin de una colisin de un proyectil en contra de un objeto se puede calcular omitiendo el tamao, la friccin, el peso, temperatura y dems factores que podran alterar su trayectoria, antes de chocar. Lo que nos queda como estudio es la posicin a partir de la cual se lanza y la direccin que toma. El rayo es un ente que parte de un punto en el espacio e indica una trayectoria a partir de un vector de direccin. Ray es la estructura que XNA ofrece para el rayo. Rayo no es considerado como un volumen de envolvente, pues no existe el conjunto de vrtices al cual cubrir. Sin embargo, se pueden tener pruebas de interseccin entre el rayo y las envolventes. En el siguiente ejemplo se muestra un sprite como mira, para orientar la vista y poder seleccionar algn elemento rodeado por una BS. Cuando la mira est apuntando a alguna envolvente de esfera, cambiar su color original a rojo, indicando que puede ser seleccionada. La figura elegida cambiar su color ambiental negro por rojo, despus de un segundo, retornar a su color original. El movimiento de la cmara se har a travs del gamepad. 9.3.1 Cmara libre

Los movimientos de la cmara libre no tienen alguna restriccin, permitiendo cualquier observacin en cualquier punto, en cualquier modo de orientacin. La cmara puede trasladarse sobre sus propios ejes y rotar alrededor de ellos. Los ejes de la cmara que se manejan en el ejemplo son: Up, Binormal y Direccin, vase Ilustracin 9-8. El primero permite tener una nocin de donde es arriba y abajo; el segundo es perpendicular a Up y Direccin. El ltimo, es resultado de la resta entre el vector de observacin y la posicin de la cmara.

167

Ilustracin 9-8 Vectores de la cmara

En la clase CamaraLibre, Cdigo 9-8, las variables de instancia posicion, objetivo y up, lnea 9, son las necesarias para obtener la matriz de vista mtxVista, lnea 10, con el mtodo CreateLookAt. La variable playerIndex, lnea 11, es para seleccionar al jugador que controla la cmara, por medio del gamepad. Los ejes binormal, lnea 12, y direccin, lnea 13, estn representados por las variables de su mismo nombre, y que ayudaran a calcular las transformaciones sobre la matriz de vista. La traslacin y rotacin de la cmara estarn dadas por las matrices mtxTraslacion, lnea 14, y mtxRotacion, lnea 15. La sensibilidad, lnea 16, denota que tan rpido puede girar la cmara al cambio sobre el stick o triggers del gamepad. La variable pasoMaximo, lnea 17, le da un valor mximo a los cambios sobre el stick, para trasladar la cmara. La ltima variable de instancia, invertirVista, lnea 18, permite cambiar el sentido en que gira la cmara sobre el eje binormal. El constructor debe recibir los vectores que definen la matriz de vista, el valor de la variable de slo lectura, pasoMaximo; y el nmero del jugador. En el cuerpo del constructor se inicializan algunas variables de instancia, lneas 32 38, y se hace llamar al mtodo Inicializar, lneas 72 80. En este ltimo, se calculan los ejes de la cmara y la matriz de vista. El clculo del vector direccin est dada por: = | | (1)

Donde es el vector de observacin y es el vector de posicin de la cmara. El operador est sobrecargado para las estructuras que definen un vector, lnea 75, y matrices. Adems estas estructuras contienen mtodos que completan las operaciones sobre los vectores o matrices; el mtodo Normalize, lnea 76, crea un vector unitario. El vector unitario binormal es el resultado de la normalizacin del producto cruz entre los vectores direccin y objetivo, tmese en cuenta que el producto cruz no cumple con la propiedad de conmutacin. Donde es el vector direccin y es el vector de observacin. El mtodo esttico Cross, lnea 77, obtiene el producto cruz. Estos mtodos sobre los vectores o matrices, tienen sobrecargas que permiten 42 de valores a sus parmetros y es cuestin del desarrollador elegir seleccionar el tipo de paso adecuadamente cul es el mejor. = (2)

Existen dos formas de pasar los valores a un mtodo: por valor o por referencia. El paso por valor hace una copia de la informacin evitando alterar la fuente.

42

168

Al final del mtodo Inicializar se crea la matriz de vista, lnea 79, unque no es necesario normalizar el vector up para el mtodo CreateLookAt, si lo ser para los clculos de rotacin. La propiedad MtxVista, lnea 45, obtiene el valor de la matriz de vista, parte importante para el render; la propiedad Direccion, lnea 50, obtiene el vector de direccin de la cmara, ste sirve para definir el rayo; la propiedad Posicion, lnea 55, obtiene el vector de posicin de la cmara, tambin importante para crear un rayo. La nica propiedad que obtiene o establece valores es Sensibilidad, lneas 61 70, permitiendo cambiar fuera de la instancia la rapidez con que puede cambiar el giro de la cmara sobre sus ejes. La Sensibilidad es el ngulo, expresado en radianes, restringido entre uno a diez grados, en caso que el valor a establecer se encuentre fuera de este rango, se le asigna un grado. El mtodo AdelanteAtras, lneas 82 89, hace avanzar la cmara sobre su eje direccin positivamente o negativamente, dependiendo del signo del parmetro de entrada. En el cuerpo de ste, se crea la matriz de traslacin, lnea 84, a partir del vector de direccin y los escalares pasoMaximo y paso. Enseguida se multiplica la matriz por los vectores posicin y objetivo, lneas 86 87, para luego crear la matriz de vista, lnea 88. Para hacer avanzar la cmara en direccin positivo o negativo sobre el eje binormal, el mtodo DerechaIzquierda, lneas 91 97, utiliza la mismas operaciones que el mtodo AdelanteAtras, pero utilizando el vector binormal como eje de traslacin, lnea 93. Los giros de la cmara se hacen alrededor de sus ejes, y a cada uno le corresponde un mtodo cuyo nombre corresponde a las distintas superficies de control de un avin, vase Ilustracin 9-9.

Ilustracin 9-9 Superficies de control del avin

El mtodo Guiada, lneas 99 108, hace rotar la cmara alrededor del vector Up, cambiando los ejes direccin y up; el parmetro de entrada, as como en los otros dos mtodos de giro, es una variable de tipo flotante que representa el ngulo de cambio. El ngulo es multiplicado por la propiedad Sensibilidad, y el valor obtenido se almacena en la variable angulo, lnea 101. Luego se crea la matriz de rotacin mtxRotacion con el mtodo Matrix.CreateFromAxesAngle, lnea 102. Este mtodo crea una matriz que gira alrededor de un vector arbitrario, por lo tanto su parmetros son el vector y el escalar como ngulo. Una vez obtenido la matriz de rotacin, se les multiplica a los vectores de direccin y up para girarlos, lneas 103 104. Ahora que se han actualizado los ejes de la cmara, es momento de cambiar el vector objetivo de la matriz de vista, lnea 105; a partir de la ecuacin (1) se obtiene el valor de dicho vector. Ntese que el vector objetivo no es un vector unitario, por eso se ha omitido la normalizacin. = + (3)

El paso por referencia no hace una copia del valor, sino apunta a la direccin sobre la cual se encuentra la informacin, evitando redundancia, lo que permite cambiar el valor de la fuente. Los mtodos cuyos parmetros son estructuras, por default pasan por valor, a menos que se indique lo contrario por medio de las palabras clave out o ref.

169

Al final, se actualiza la matriz de vista, creando una nueva con Matriz.CreateLookAt, lnea 106. El siguiente mtodo, Inclinacin, gira la cmara alrededor del vector binormal, afectando los ejes direccin y up. Las operaciones son las mismas que en el mtodo Guiada, se crea la matriz de rotacin alrededor del eje binormal, lnea112, se multiplica la matriz por los vectores de direccin y up, lneas 113 114; se actualiza el vector objetivo, lnea 115; y se crea una nueva matriz de vista, lnea 116. El mtodo Balanceo gira la cmara alrededor del eje direccin, afectando los vectores binormal y up. As que primero se crea la matriz de rotacin alrededor del vector direccin, lnea 122, se multiplica esta matriz por los vectores binormal y up, lneas 123 124; y se actualiza la matriz de vista, lnea 125. En la Tabla 9-2 se muestra la asignacin de movimientos en el gamepad para describir el mtodo Update, lneas 128 158.
Tabla 9-2

Gamepad ThumbSticks.Right.X ThumbSticks.Right.Y ThumbSticks.Left.X ThumbSticks.Left.Y Triggers.Right Triggers.Left

Momiviento Guiada Inclinacin DerechaIzquierda ArribaAbajo Balanceo Balanceo

El mtodo Update no se describir a detalle, pues una vez descrita la tabla anterior, es muy fcil implementarla; a excepcin del uso del ThumbSticks.Richt.Y, donde el uso de un condicional if anidado, lneas 136 140, verifica en qu sentido se debe hacer el cambio de la cmara para mirar hacia arriba o hacia abajo. Si el valor de la propiedad InvertirVista es verdadero, el cambio sobre el ThumbSticks.Right.Y se multiplicar por menos uno y se le pasar como parmetro al mtodo privado Inclinacin; en caso contrario el valor del ThumbSitck no se alterar.
Cdigo 9-8
1. 2. 3. 4. 5. 6. 7. 8. 9. 10. 11. 12. 13. 14. 15. 16. 17. 18. 19. 20. 21. using System; using Microsoft.Xna.Framework; using Microsoft.Xna.Framework.Input; namespace TutorialX15 { public class CamaraLibre { private Vector3 posicion, objetivo, up; private Matrix mtxVista; private PlayerIndex playerIndex; private Vector3 binormal; private Vector3 direccion; private Matrix mtxTraslacion; private Matrix mtxRotacion; private Single sensibilidad; private readonly Single pasoMaximo; public Boolean InvertirVista; /// <summary> /// Constructor

170

22. 23. 24. 25. 26. 27. 28. 29. 30. 31. 32. 33. 34. 35. 36. 37. 38. 39. 40. 41. 42. 43. 44. 45. 46. 47. 48. 49. 50. 51. 52. 53. 54. 55. 56. 57. 58. 59. 60. 61. 62. 63. 64. 65. 66. 67. 68. 69. 70. 71. 72. 73. 74. 75. 76. 77. 78. 79. 80. 81. 82. 83. 84. 85. 86. 87. 88. 89. 90. 91. 92.

/// </summary> /// <param name="position">Posicin de la cmara.</param> /// <param name="objetivo">Objetivo de la cmara.</param> /// <param name="up">Vector Up de la cmara.</param> /// <param name="pasoMaximo">Paso mximo al que se /// movera la cmara.</param> /// <param name="playerIndex">Nmero de jugador.</param> public CamaraLibre(Vector3 posicion, Vector3 objetivo, Vector3 up, Single pasoMaximo, PlayerIndex playerIndex) { this.posicion = posicion; this.objetivo = objetivo; this.up = up; this.playerIndex = playerIndex; this.pasoMaximo = pasoMaximo; Sensibilidad = 1.0F; InvertirVista = true; Inicializar(); }// fin del constructor /// <summary> /// Propiedad que obtiene la matriz de vista. /// </summary> public Matrix MtxVista { get { return mtxVista; } } /// <summary> /// Propiedad que obtiene la direccin de la cmara. /// </summary> public Vector3 Direccion { get { return direccion; } } /// <summary> /// Propiedad que obtiene la posicin de la cmara. /// </summary> public Vector3 Posicion { get { return posicion; } } /// <summary> /// Propiedad que obtiene o establece la sensibilidad /// de giro de la cmara. /// </summary> public Single Sensibilidad { get { return sensibilidad; } set { sensibilidad = MathHelper.ToRadians( (value >= 1 && value <= 10) ? value : 1.0F); } }// fin de la propiedad Sensibilidad private void Inicializar() { up.Normalize(); direccion = objetivo - posicion; direccion.Normalize(); Vector3.Cross(ref direccion, ref up, out binormal); binormal.Normalize(); mtxVista = Matrix.CreateLookAt(posicion, objetivo, up); }// fin del mtodo Inicializar private void AdelanteAtras(Single paso) { mtxTraslacion = Matrix.CreateTranslation(pasoMaximo * paso * direccion); Vector3.Transform(ref posicion, ref mtxTraslacion, out posicion); Vector3.Transform(ref objetivo, ref mtxTraslacion, out objetivo); mtxVista = Matrix.CreateLookAt(posicion, objetivo, up); }// fin del mtodo AdelanteAtras private void DerechaIzquierda(Single paso) {

171

93. 94. 95. 96. 97. 98. 99. 100. 101. 102. 103. 104. 105. 106. 107. 108. 109. 110. 111. 112. 113. 114. 115. 116. 117. 118. 119. 120. 121. 122. 123. 124. 125. 126. 127. 128. 129. 130. 131. 132. 133. 134. 135. 136. 137. 138. 139. 140. 141. 142. 143. 144. 145. 146. 147. 148. 149. 150. 151. 152. 153. 154. 155. 156. 157. 158. 159. 160.

mtxTraslacion = Matrix.CreateTranslation(pasoMaximo * paso * binormal); Vector3.Transform(ref posicion, ref mtxTraslacion, out posicion); Vector3.Transform(ref objetivo, ref mtxTraslacion, out objetivo); mtxVista = Matrix.CreateLookAt(posicion, objetivo, up); }// fin del mtodo DerechaIzquierda private void Guiada(Single angulo) { angulo *= Sensibilidad; mtxRotacion = Matrix.CreateFromAxisAngle(up, angulo); Vector3.Transform(ref direccion, ref mtxRotacion, out direccion); Vector3.Transform(ref binormal, ref mtxRotacion, out binormal); objetivo = posicion + direccion; mtxVista = Matrix.CreateLookAt(posicion, objetivo, up); }// fin del mtodo private void Inclinacion(Single angulo) { angulo *= Sensibilidad; mtxRotacion = Matrix.CreateFromAxisAngle(binormal, angulo); Vector3.Transform(ref direccion, ref mtxRotacion, out direccion); Vector3.Transform(ref up, ref mtxRotacion, out up); objetivo = posicion + direccion; mtxVista = Matrix.CreateLookAt(posicion, objetivo, up); }// fin del mtodo private void Balanceo(Single angulo) { angulo *= Sensibilidad; mtxRotacion = Matrix.CreateFromAxisAngle(direccion, angulo); Vector3.Transform(ref binormal, ref mtxRotacion, out binormal); Vector3.Transform(ref up, ref mtxRotacion, out up); mtxVista = Matrix.CreateLookAt(posicion, objetivo, up); }// fin del mtodo public void Update() { if (GamePad.GetState(playerIndex).ThumbSticks.Right.X != 0) { Guiada(-GamePad.GetState(playerIndex).ThumbSticks.Right.X); } if (GamePad.GetState(playerIndex).ThumbSticks.Right.Y != 0) { if(InvertirVista) Inclinacion( -GamePad.GetState(playerIndex).ThumbSticks.Right.Y); else Inclinacion(GamePad.GetState(playerIndex).ThumbSticks.Right.Y); } if (GamePad.GetState(playerIndex).ThumbSticks.Left.X != 0) { DerechaIzquierda(GamePad.GetState(playerIndex).ThumbSticks.Left.X); } if (GamePad.GetState(playerIndex).ThumbSticks.Left.Y != 0) { AdelanteAtras(GamePad.GetState(playerIndex).ThumbSticks.Left.Y); } if (GamePad.GetState(playerIndex).Triggers.Right != 0) { Balanceo(GamePad.GetState(playerIndex).Triggers.Right); } if (GamePad.GetState(playerIndex).Triggers.Left != 0) { Balanceo(-GamePad.GetState(playerIndex).Triggers.Left); } }// fin del mtodo Update }// fin de la clase }// fin del namespace

172

9.3.2

La clase Mira

La clase Mira representa un sprite que es dibujado en medio del viewport, Cdigo 9-9, y cambia de textura cada vez que ha encontrado un BS con el que colisiona un rayo. El graphicsDevice, lnea 9, sirve para obtener las dimensiones del viewport y poder crear el spriteBatch, lnea 10; la texturas A y B, lnea 11, sern los dos posibles sprites a dibujar; la variable rectangulo, lnea 12, constituye las dimensiones del sprite; y la variable preparado, lnea 13, selecciona qu textura se dibujar. El constructor, lneas 15 26, tiene como parmetros las dos texturas, el ancho y alto para definir el rectngulo; y el graphicsDevice. Dentro del cuerpo se incializan y se crean las variables de instancia. El constructor Rectangle, lneas 23 26, pide el valor en entero de la coordenada x de la posicin de la esquina superior izquierda del rectngulo, en el viewport; el valor en entero de la coordenada y de la misma esquina, el ancho y alto del rectngulo. As que para centrar el rectngulo en el viewport se le resta al ancho del viewport el ancho del rectngulo y se le divide entre dos, y lo mismo sucede para el alto; recurdese que los valores deben ser enteros, y la conversin explicita de float a int, lneas 24 25, es obligatoria. El mtodo Update actualiza el valor de la variable preparado, a partir de la colisin entre un rayo y una envolvente de esfera. Sus parmetros son el rayo y un arreglo de objetos ModeloEstatico de donde se obtienen las BS. Para seleccionar cada BS se hace pasar el arreglo de ModeloEstatico por un bucle foreach, lneas 30 39, y a cada uno se le hace la prueba de interseccin, Ray vs. BS, lnea 32; la prueba regresa un 43 Nullable <float>, esto indica que el elemento puede ser del tipo de dato T, en este caso float, o puede ser null. En caso que la prueba haya encontrado una interseccin, el mtodo regresa la distancia del punto de posicin del rayo a la envolvente, en caso contrario regresa un null. El mtodo Ray.Intersects est sobrecargado para aceptar otras envolventes. public Nullable<float> Intersects (BoundingSphere sphere) El condicional if, lnea 32, selecciona cualquier valor diferente de null, para cambiar el estado del booleano, preparado, a verdadero y salir del ciclo foreach con la instruccin break; en caso contrario se le asigna el valor falso a la variable preparado. El mtodo Update se hace llamar desde fuera de la clase, y no est sujeto a alguna interrupcin de una entrada estndar o el gamepad, as que siempre que se llame a este mtodo se har recorrer el arreglo de ModeloEstatico para buscar la condicin para cambiar la textura. La llamada a este mtodo, pudo hacerse dentro del mtodo Draw de esta misma clase, pero se ha dejado as para seguir con la separacin de tareas entre los mtodos Draw y Update de la clase Game. Con el mtodo Draw, lneas 42 51, se dibuja el sprite seleccionado por el booleano, lneas 46 49, al final del mtodo spriteBath.End se cambia la propiedad DepthBufferEnable a verdadero, lnea 50, ya que el mtodo spriteBatch.Draw lo cambia.
Cdigo 9-9
1. 2. 3. 4. 5. 6. 7. 8. 9. 10. 11. 12. 13. 14.
43

using System; using Microsoft.Xna.Framework.Graphics; using Microsoft.Xna.Framework; namespace TutorialX15 { public class Mira { GraphicsDevice graphicsDevice; SpriteBatch spriteBatch; Texture2D texturaA, texturaB; Rectangle rectangulo; Boolean preparado;

http://msdn.microsoft.com/es-es/library/b3h38hb0.aspx

173

15. 16. 17. 18. 19. 20. 21. 22. 23. 24. 25. 26. 27. 28. 29. 30. 31. 32. 33. 34. 35. 36. 37. 38. 39. 40. 41. 42. 43. 44. 45. 46. 47. 48. 49. 50. 51. 52. 53.

public Mira(Texture2D texturaA, Texture2D texturaB, Int32 ancho, Int32 alto, GraphicsDevice graphicsDevice) { this.graphicsDevice = graphicsDevice; spriteBatch = new SpriteBatch(graphicsDevice); this.texturaA = texturaA; this.texturaB = texturaB; // se crea el rectangulo y se posiciona al centro de la pantalla rectangulo = new Rectangle( (int)(graphicsDevice.Viewport.Width - ancho) / 2, (int)(graphicsDevice.Viewport.Height - alto) / 2, ancho, alto); }// fin del constructor public void Update(Ray rayo, ModeloEstatico[] modelosEstaticos) { foreach (ModeloEstatico modelo in modelosEstaticos) { if (rayo.Intersects(modelo.EnvolventeEsfera) != null) { preparado = true; break; } else preparado = false; }// fin del foreach }// fin del mtodo Update public void Draw() { spriteBatch.Begin(); if(preparado) spriteBatch.Draw(texturaB, rectangulo, Color.White); else spriteBatch.Draw(texturaA, rectangulo, Color.White); spriteBatch.End(); graphicsDevice.RenderState.DepthBufferEnable = true; }// fin del mtodo Draw }// fin de la clase }// fin del namespace

9.3.3

La clase Proyectil

La clase Proyectil, Cdigo 9-10, busca impactar un rayo con alguna envolvente de esfera, cada vez que se presiona el botn A del gamepad. PlayterIndex, lnea 9, es para conocer qu jugador ha disparado el proyectil, y distancia, lnea 10, es para conocer el blanco con el que se ha impactado primero. Al constructor solo se le hace pasar el playerIndex para inicializar la variable de instancia del mismo nombre. Update, lneas 17 22, es el mtodo en donde se busca impactar el proyectil con alguna BS que se encuentre en su paso. Para eso necesita datos del exterior, como la posicin de dnde se hizo el disparo, la direccin del mvil y un arreglo de objetos ModeloEstatico, para hacer la bsqueda. Cada vez que se presiona el botn A del Gamepad se llama al mtodo Impactar, lneas 30 35, el cual ya recibe un rayo y el arreglo de modelos como parmetros. La variable de tipo Nullable<Int32>, lneas 32, es para conocer el ndice del arreglo en donde se ha impactado el proyectil. El mtodo privado Indice regresa dicho valor, a partir del rayo y el arreglo. Una vez encontrado el ndice, se cambia la propiedad Seleccionado del modelo, lnea 34, a verdadero. Un proyectil no puede impactar a ms de un modelo, por eso la distancia mnima entre la posicin del disparo y la envolvente debe ser la mnima entre aquellos que puede intersecar. La variable distMIn, lnea 46, representa dicha situacin; unaVez es para conocer que al menos se ha encontrado una distancia de la prueba Intersects e indice indica el valor encontrado del arreglo. En el bucle for, lneas 50 59, se hace la prueba Intersects del objeto ray a cada una de las envolventes, lnea 52, el valor arrojado por el mtodo se alojar en distancia. El primer if, lnea 54, selecciona cualquier 174

valor diferente de null, o sea cualquier colisin hallada. En el primer if anidado, lnea 56, se le asigna a distMin del valor en flotate de la prueba de interseccin, es importante que el valor de la variable distancia no sea null, porque arrojara un error en tiempo de ejecucin; luego se cambia la variable unaVez a falso, lnea 59, y a indice se le asigna el nmero del elemento con el que encontr colisin, lnea 60. Para evitar la prueba del segundo if anidado, se hace uso de la instruccin continue, lnea 61, para seguir con la siguiente iteracin del bucle for. En el segundo if anidado, lneas 63 67, se hace la comparacin entre la distancia mnima y la distancia actual encontrada, lnea 63; en caso que sta ltima sea menor, se cambia el valor de la distancia mnima por la actual, lnea 65, y el nmero del ndice se actualiza por el del elemento con el que se colision, lnea 66. Al final del ciclo for, se regresa el nmero del ndice del arreglo con el que se tiene una distancia mnima, en comparacin con otra con la que se haya encontrado.
Cdigo 9-10
1. 2. 3. 4. 5. 6. 7. 8. 9. 10. 11. 12. 13. 14. 15. 16. 17. 18. 19. 20. 21. 22. 23. 24. 25. 26. 27. 28. 29. 30. 31. 32. 33. 34. 35. 36. 37. 38. 39. 40. 41. 42. 43. 44. 45. 46. 47. 48. 49. 50. 51. using System; using Microsoft.Xna.Framework; using Microsoft.Xna.Framework.Input; namespace TutorialX15 { public class Proyectil { PlayerIndex playerIndex; Nullable<Single> distancia; public Proyectil(PlayerIndex playerIndex) { this.playerIndex = playerIndex; }// fin del constructor public void Update(Vector3 posicion, Vector3 direccion, ModeloEstatico[] modeloEstatico) { if (GamePad.GetState(playerIndex).Buttons.A == ButtonState.Pressed) Impactar(new Ray(posicion, direccion), modeloEstatico); }// fin del mtodo Update /// <summary> /// Mtodo que selecciona el modelo esttico /// sobre el que se ha impactado la bala. /// </summary> /// <param name="rayo"></param> /// <param name="modeloEstatico"></param> private void Impactar(Ray rayo, ModeloEstatico[] modeloEstatico) { Int32? i = Indice(rayo, modeloEstatico); if (i != null) modeloEstatico[i.Value].Seleccionado = true; }// fin del mtodo /// <summary> /// Mtodo que devuelve el ndice del modelo esttico /// con el que se ha impactado la bala. /// </summary> /// <param name="rayo"></param> /// <param name="modelos"></param> /// <returns></returns> private Nullable<Int32> Indice(Ray rayo, ModeloEstatico[] modelos) { Single distMin = 0.0F; bool unaVez = true; Int32? indice = null; for (Int32 i = 0; i < modelos.Length; i++) {

175

52. 53. 54. 55. 56. 57. 58. 59. 60. 61. 62. 63. 64. 65. 66. 67. 68. 69. 70. 71. 72. 73. 74. 75. 76. 77. 78. 79.

distancia = rayo.Intersects(modelos[i].EnvolventeEsfera); // signifa que existe una interseccin if (distancia != null) { if (unaVez) { distMin = distancia.Value; unaVez = false; indice = i; continue; } if (distMin > distancia.Value) { distMin = distancia.Value; indice = i; } }// fin del if }// fin del for return indice; }// fin del mtodo Indice public override string ToString() { return string.Format("Distancia: {0}", distancia); }// fin del mtodo ToString }// fin de la clase }// fin del namespace

9.3.4

La clase ModeloEstatico

La clase ModeloEstatico, Cdigo 9-11, se ha aumentado el nmero de lneas de cdigo, al visto en Bounding Sphere vs. Bounding Sphere, Cdigo 9-2. Por lo tanto solo aquellas nuevas lneas se explicaran. ColorAmbiental es una variable de instancia pblica, lnea 13, que ayudar a cambiar el color ambiental del shader predeterminado de XNA. El booleano Seleccionado, lnea 14, es para indicar que la instancia del ModeloEstatico ha sido seleccionado para cambiar el color ambiental. Al ser seleccionado la instancia, el cambio de color ambiental dura un tiempo determinado por la variable de instancia Tiempo, lnea 15. El nico cambio en el constructor, es en la inicializacin de la variable ColorAmbiental al color negro, lnea 29. En el mtodo Draw, lneas 37 54, se agrego el cambio del color ambiental, lnea 50, para hacerlo actualizar cada vez que se ha seleccionado la instancia; la propiedad AmbientLightColor es de tipo Vector3 por lo que el ColorAmbiental debe convertirse explisitamente a este tipo de dato con el mtodo ToVector3. Se ha agregado el mtodo Update con el parmero gameTime para regresar el color ambiental de rojo a negro, cada vez que ha pasado un segundo despus de ser seleccionado. En el primer if, lneas 58 62, la variable de instancia Seleccionado permite cambiar el color ambiental de negro a rojo e iniciando la suma de tiempo transcurrido a partir de la primera vez que el programa entra en el cuerpo de este condicional. Si el tiempo es mayor a un segundo, en el segundo if, lneas 63 68, se cambia el color ambiental a negro, la variable tiempo se inicia en cero y la variable de instancia Seleccionado toma el valor de falso, para no continuar con la suma del tiempo.
Cdigo 9-11
1. 2. 3. 4. 5. 6. 7. 8. 9. 10. using System; using Microsoft.Xna.Framework.Graphics; using Microsoft.Xna.Framework; namespace TutorialX15 { public class ModeloEstatico { private Model modelo; private Matrix[] transformaciones;

176

11. 12. 13. 14. 15. 16. 17. 18. 19. 20. 21. 22. 23. 24. 25. 26. 27. 28. 29. 30. 31. 32. 33. 34. 35. 36. 37. 38. 39. 40. 41. 42. 43. 44. 45. 46. 47. 48. 49. 50. 51. 52. 53. 54. 55. 56. 57. 58. 59. 60. 61. 62. 63. 64. 65. 66. 67. 68. 69. 70. 71. 72. 73. 74. 75. 76. 77. 78. 79. 80.

protected BoundingSphere boundingSphere; private String nombre; public Color ColorAmbiental; public bool Seleccionado; private Single tiempo; public ModeloEstatico(Model modelo) { this.modelo = modelo; transformaciones = new Matrix[modelo.Bones.Count]; modelo.CopyAbsoluteBoneTransformsTo(transformaciones); boundingSphere = modelo.Meshes[0].BoundingSphere; // traslacin del centro de la envolvente boundingSphere.Transform( ref transformaciones[modelo.Meshes[0].ParentBone.Index], out boundingSphere); nombre = modelo.Meshes[0].Name; ColorAmbiental = Color.Black; }// fin del constructor /// <summary> /// Propiedad que obtiene la envolvente de esfera. /// </summary> public BoundingSphere EnvolventeEsfera { get { return boundingSphere; } } public virtual void Draw(ref Matrix world, ref Matrix view, ref Matrix projection) { foreach (ModelMesh mesh in modelo.Meshes) { foreach (BasicEffect basicEffect in mesh.Effects) { basicEffect.World = transformaciones[mesh.ParentBone.Index] * world; basicEffect.View = view; basicEffect.Projection = projection; basicEffect.EnableDefaultLighting(); basicEffect.PreferPerPixelLighting = true; basicEffect.AmbientLightColor = ColorAmbiental.ToVector3(); }// fin del foreach mesh.Draw(); }// fin del foreach }// fin del mtodo Draw public void Update(GameTime gameTime) { if (Seleccionado) { ColorAmbiental = Color.Red; tiempo += (Single)gameTime.ElapsedGameTime.TotalSeconds; } if ((tiempo) > 1.0) { ColorAmbiental = Color.Black; tiempo = 0; Seleccionado = false; } }// fin del mtodo Update public override string ToString() { return string.Format("{0}\nBoundingSphere.Center {1}\nRadio {2}", nombre, boundingSphere.Center.ToString(), boundingSphere.Radius); }// fin del mtodo ToString }// fin de la clase }// fin del namespace

177

9.3.5

Implementacin

Ya para concluir este captulo, en la clase Game1, Cdigo 9-12, se implementan las clases descritas anteriormente. En las variables de instancia, se ha agregado la cmara libre, lneas 15; la mira, lnea 16; y el proyectil, lnea 17. Dentro del mtodo Initialize, lneas 24 41, se crea la cmara, lnea 34, y se inicializa la sensibilidad de rotacin a tres unidades, lnea 37. A la matriz view, lnea 38, se le asigna el valor de la propiedad MtxVista de la cmara. El proyectil tambin se crea en la este mtodo, Initialize, lnea 39. Los modelos estticos y la mira deben crearse en el mtodo LoadContent, lneas 43 56, por tener parmetros que el ContenManager maneja. En el mtodo Update se hacen llamar a los mtodos de actualizacin de: la cmara, de cada elemento del arreglo de modelos estticos, del proyectil y de la mira. Vase que la bala y la mira, dependen de la cmara, por lo tanto, deben ir despus sta. En el mtodo Draw, lneas 77 87, se actualiza la matriz view con la propiedad MtxVista de la cmara, y luego se llaman los mtodos de dibujado de cada modelo esttico y de la mira.
Cdigo 9-12
1. 2. 3. 4. 5. 6. 7. 8. 9. 10. 11. 12. 13. 14. 15. 16. 17. 18. 19. 20. 21. 22. 23. 24. 25. 26. 27. 28. 29. 30. 31. 32. 33. 34. 35. 36. 37. 38. 39. 40. 41. 42. 43. 44. using using using using using System; Microsoft.Xna.Framework; Microsoft.Xna.Framework.Content; Microsoft.Xna.Framework.Graphics; Microsoft.Xna.Framework.Input;

namespace TutorialX15 { public class Game1 : Microsoft.Xna.Framework.Game { GraphicsDeviceManager graphics; SpriteBatch spriteBatch; ModeloEstatico[] modelosEsticos; Matrix world, view, projection; CamaraLibre camara; Mira mira; Proyectil bala; public Game1() { graphics = new GraphicsDeviceManager(this); Content.RootDirectory = "Content"; graphics.PreferredBackBufferHeight = 720; graphics.PreferredBackBufferWidth = 1280; } protected override void Initialize() { world = Matrix.Identity; projection = Matrix.CreatePerspectiveFieldOfView( MathHelper.PiOver4, GraphicsDevice.Viewport.AspectRatio, 1.0F, 10000.0F); camara = new CamaraLibre(new Vector3(0, 10, 350), new Vector3(0, 10, 0), Vector3.Up, 4.0F, PlayerIndex.One); camara.Sensibilidad = 3.0F; view = camara.MtxVista; bala = new Proyectil(PlayerIndex.One); base.Initialize(); } protected override void LoadContent() {

178

45. 46. 47. 48. 49. 50. 51. 52. 53. 54. 55. 56. 57. 58. 59. 60. 61. 62. 63. 64. 65. 66. 67. 68. 69. 70. 71. 72. 73. 74. 75. 76. 77. 78. 79. 80. 81. 82. 83. 84. 85. 86. 87. 88. 89.

spriteBatch = new SpriteBatch(GraphicsDevice); modelosEsticos = new ModeloEstatico[2]; modelosEsticos[0] = new ModeloEstatico(Content.Load<Model>("EsferaD")); modelosEsticos[1] = new ModeloEstatico( Content.Load<Model>("ElefanteA")); mira = new Mira( Content.Load<Texture2D>("Mira"), Content.Load<Texture2D>("Mira2"), 10, 10, GraphicsDevice); } protected override void UnloadContent() { } protected override void Update(GameTime gameTime) { if (GamePad.GetState(PlayerIndex.One).Buttons.Back == ButtonState.Pressed) this.Exit(); camara.Update(); modelosEsticos[0].Update(gameTime); modelosEsticos[1].Update(gameTime); bala.Update(camara.Posicion, camara.Direccion, modelosEsticos); mira.Update(new Ray(camara.Posicion, camara.Direccion), modelosEsticos); base.Update(gameTime); } protected override void Draw(GameTime gameTime) { GraphicsDevice.Clear(Color.White); view = camara.MtxVista; modelosEsticos[0].Draw(ref world, ref view, ref projection); modelosEsticos[1].Draw(ref world, ref view, ref projection); mira.Draw(); base.Draw(gameTime); } } }

Ejecute el programa y corrija cualquier error que pudiera encontrarse, e intente de nuevo. En la Ilustracin 9-10 se muestra la figura del elefante antes y despus de ser cambiado su color ambiental.

Ilustracin 9-10 Cambio de color ambiental

En la Ilustracin 9-11 se trato de mostrar que el cambio de color solo afecta a la figura que se encuentre ms cerca de la posicin de la cmara, de donde se ha disparado el proyectil.

179

Ilustracin 9-11 La colisin existe para el elemento ms cercano a la cmara

180

181

10 Bibliografa
1. St-Lauret, Sebastien. The complete effect and HLSL guide. Estados Unidos de Amrica : Paradoxal, 2005. ISBN 0-9766132-1-2. 2. Solis Ubaldo, Rodolfo. Geometra Analtica. Mxico : Limusa, 1992. ISBN 968-837-162-9. 3. Solar Gonzlez, Eduardo. lgebra Lineal. Mxico : Limusa, 2003. ISBN 968-18-5365-2. 4. Landa Cosio, Niclas Arrioja. DirectX programacin de grficoas 3D. Buenos Aires, Argentina : Users.code, 2006. ISBN 987-1347-04-9. 5. Jimnez Delgado, Juan Jos. Deteccin de Colisiones mediante Recubrimientos Simpliciales. Granada, Espaa : Universidad de Granada, 2006. 6. Deitel M., Harvey y Deitel J., Paul. Cmo programar C#. Mxico : Pearson Educacin de Mxico, 2007. ISBN 978-970-26-1056-4. 7. The Competitive Intelligence Unit. [En lnea] [Citado el: 16 de enero de 2011.] http://www.theciu.net/ciu_0k/pdf/CIU-Mercado%20de%20Videojuegos%20Mexico%20v01.pdf. 8. El Economista. El Economista. [En lnea] http://eleconomista.com.mx/tecnociencia/2010/10/22/sergamer-cuesta-6818-mexico. 9. Universidad de Oviedo. srg. [En lnea] http://www.srg.es/files/apendice_tuberia_programable.pdf. 10. NVIDIA. nVidia. [En lnea] nVidia Corporation, 2011. http://www.nvidia.es/object/IO_20020107_6675.html. 11. MSDN. msdn. [En lnea] Microsoft Corporation, 2011. http://msdn.microsoft.com/esmx/library/bb203925.aspx. 12. . msdn. [En lnea] Microsoft Corporation, 2011. http://msdn.microsoft.com/eses/library/bb447756.aspx. 13. . msdn. [En lnea] Microsoft Corporation, 2011. http://msdn.microsoft.com/eses/library/b3h38hb0.aspx . 14. . msdn. [En lnea] Microsoft Corporation, 2011. http://msdn.microsoft.com/query/dev10.query?appId=Dev10IDEF1&l=ESES&k=k(MICROSOFT.XNA.FRAMEWORK.BOUNDINGSPHERE.INTERSECTS)&rd=true. 15. . msdn. [En lnea] Microsoft Corporation, 2011. http://msdn.microsoft.com/query/dev10.query?appId=Dev10IDEF1&l=ENUS&k=k(MICROSOFT.XNA.FRAMEWORK.GRAPHICS.EFFECTPARAMETER.SETVALUE);k(DevLangCSHARP)&rd=true. 16. . msdn. [En lnea] Microsoft Corporation, 2011. http://msdn.microsoft.com/enus/library/ff471376(v=VS.85).aspx. 17. . msdn. [En lnea] Microsoft Coporation, 2011. ttp://msdn.microsoft.com/enus/library/bb173347(v=VS.85).aspx. 18. . msdn. [En lnea] Microsoft Corporation, 2011. http://msdn.microsoft.com/enus/library/bb509626(v=VS.85).aspx. 19. . msdn. [En lnea] Microsoft Corporation, 2011. http://msdn.microsoft.com/enus/library/bb509644(VS.85).aspx. 20. . msdn. [En lnea] Microsoft Corporation, 2011. http://msdn.microsoft.com/enus/library/ff471376(v=VS.85).aspx.

182

21. . msdn. [En lnea] Microsoft Corporation, 2011. http://msdn.microsoft.com/enus/library/bb509647(v=VS.85).aspx. 22. . msdn. [En lnea] Microsoft Corporation, 2011. http://msdn.microsoft.com/enus/library/bb509587(v=VS.85).aspx. 23. . msdn. [En lnea] Microsoft Corporation, 2011. http://msdn.microsoft.com/enus/library/bb509706(v=VS.85).aspx. 24. . msdn. [En lnea] Microsoft Corporation, 2011. http://msdn.microsoft.com/enus/library/microsoft.xna.framework.graphics.blendfunction.aspx. 25. . msdn. [En lnea] Microsoft Corporation, 2011. http://msdn.microsoft.com/enus/library/microsoft.xna.framework.graphics.renderstate.blendfactor.aspx . 26. . msdn. [En lnea] Microsoft Corporation, 2011. http://msdn.microsoft.com/enus/library/microsoft.xna.framework.graphics.blend.aspx. 27. . msdn. [En lnea] Microsoft Corporation, 2011. ttp://msdn.microsoft.com/enus/library/bb174837(VS.85).aspx. 28. . msdn. [En lnea] Microsoft Corporation, 2011. http://msdn.microsoft.com/enus/library/4z4t9ed1(VS.71).aspx. 29. . msdn. [En lnea] Microsoft Corporation, 2011. http://msdn.microsoft.com/enus/library/bb447759.aspx. 30. . msdn. [En lnea] Microsoft Corporation, 2011. http://msdn.microsoft.com/enus/library/microsoft.xna.framework.content.contentmanager.aspx. 31. . msdn. [En lnea] Microsoft Corporation, 2011. http://msdn.microsoft.com/enus/library/bb197848.aspx. 32. . msdn. [En lnea] Microsoft Corporation, 2011. http://msdn.microsoft.com/eses/library/microsoft.windowsmobile.directx.direct3d.texturefilter(VS.85).aspx. 33. . msdn. [En lnea] Microsoft Corporation, 2011. http://msdn.microsoft.com/eses/library/microsoft.windowsmobile.directx.direct3d.texturefilter(VS.85).aspx. 34. . msdn. [En lnea] Microsoft Corporation, 2011. http://msdn.microsoft.com/enus/library/microsoft.xna.framework.graphics.texturefilter.aspx. 35. . msdn. [En lnea] Microsoft Corporation, 2011. http://msdn.microsoft.com/enus/library/microsoft.xna.framework.graphics.surfaceformat.aspx. 36. . msdn. [En lnea] Microsoft Corporation, 2011. http://msdn.microsoft.com/enus/library/microsoft.xna.framework.game_members.aspx. 37. . msdn. [En lnea] Microsoft Corporation, 2011. http://msdn.microsoft.com/enus/library/bb198836.aspx. 38. . msdn. [En lnea] Microsoft Corporation, 2011. http://msdn.microsoft.com/eses/library/system.aspx. 39. Nvidia. Developer zone. [En lnea] nVidia, 2008. http://developer.nvidia.com/object/using_sas.html. 40. 3dRender. 3dRender.com. [En lnea] 2006. http://www.3drender.com/light/compositing/index.html. 41. The Game Development Wiki. [En lnea] http://wiki.gamedev.net/index.php/D3DBook:%28Lighting%29_Direct_Light_Sources.

183

11 Glosario
Depth Buffer: es un bfer que es de la misma anchura y altura como su render target. Este bfer registra la profundidad de cada pxel que es renderizado. Cuando un pxel es renderizado una segunda vez, como cuando un objeto es renderizado detrs de otro, el Depth Buffer toma cualquier valor anterior de profundidad, o lo reemplaza por el valor de profundidad del segundo pxel. Qu profundidad es preservada y qu profundidad es descartada depender de la funcin de profundidad seleccionada. Por ejemplo, si CompareFunction.LessEqual es la funcin actual, los valores de profundidad son menores o iguales al valor actual son preservados. Cualquier valor mayor al valor de profundidad actual es descartado. Esto se le llama depth test. El depth test se produce cada vez que el pxel es renderizado. Cuando un pxel pasa el depth test, ese color es escrito en el render target y esa profundidad es escrita en el depth buffer. La profundidad de un pxel se determina basndose en el tamao de las matrices de vista y proyeccin seleccionadas para renderizar. Un pxel que toca el plano near de la proyeccin tiene una profundidad 0. Un pxel que toca el plano far de la proyeccin tiene una profundidad 1. Como cada uno de los objetos en la escena es renderizado, normalmente los pxeles que estn cerca de la cmara se mantendrn, ya que esos objetos bloquean la visin de los objetos detrs de ellos. El depth buffer puede contener bits de stencil, por esta razn se le llama a menudo depth-stencil buffer. El formato de profundidad describe la composicin del depth buffer. El depth buffer siempre es de 32 bits, pero esos bits pueden ser arreglados en diferentes maneras, similar a cmo pueden variar los formatos de textura. Un comn formato de profundidad es Depth32, donde todos los 32 bits son reservados para la informacin de profundidad. Otro formato comn es DepthFormat.Depth24Stencil8, donde los 24 bits son reservados para los clculos de profundidad y 8 bits son usados para el stencil buffer. DepthFormat.Depth24Stencil8Single es un formato inusual donde los 24 bits del depth buffer son dispuestos 44 como un valor de punto flotante. Renderizado: es la representacin final de la imagen, a la que han eliminado las partes ocultas y se le han aplicado modelos de iluminacin. Render Target: Es un bfer donde la tarjeta de video dibuja los pxeles de una escena siendo ste 45 renderizado por un Effect Class. Shader: procedimiento de sombreado e iluminacin personalizado que permite al artista o programador 46 especificar el renderizado de un vrtice o un pxel. Sprite: Es un bitmap 2D que es dibujado directamente sin usar las transformaciones del pipeline, luces o 47 efectos. Stencil Buffer: Es similar al depth buffer. De hecho, ste ocupa parte del depth buffer. El stencil buffer permite a los programadores establecer una funcin de stencil que probar el valor de referencia stencil, un valor global, contra el valor en el stencil buffer cada momento que el pxel es renderizado. El resultado del test stencil determina si el valor del color del pxel es escrito en la target render, y si el 48 valor de profundidad de ese pxel es escrito en el depth buffer. Viewport: Es un rectngulo que define cmo se representa una escena 3D en una venta 2D.
49

44 45

Traduccin hecha a partir de la siguiente direccin: http://msdn.microsoft.com/en-us/library/bb976071.aspx Traduccin hecha a partir de la siguiente direccin: http://msdn.microsoft.com/en-us/library/bb976073.aspx 46 http://www.srg.es/files/apendice_tuberia_programable.pdf 47 http://msdn.microsoft.com/en-us/library/bb203919.aspx 48 Traduccin hecha a partir de la siguiente direccin: http://msdn.microsoft.com/en-us/library/bb976074.aspx 49 http://msdn.microsoft.com/es-es/library/microsoft.windowsmobile.directx.direct3d.viewport(VS.90).aspx

184

You might also like