[XNA] Introducción al trazado de rayos con XNA

Los rayos son usados en desarrollo de videojuegos para trazar líneas imaginarias entre un punto y otro en el espacio tridimensional, y valorar si hay intersecciones entre el vector que une ambos. Puede servir para detectar si un disparo intersecciona con su objetivo o no, y para calcular la distancia entre este y quien ha disparado… o también puede ser útil para pintar un texto encima de un modelo en el espacio 3D… o para hacer un Drag & Drop de modelos en un espacio tridimensional. De hecho, el trazado de rayos puede ser utilizado incluso para renderizar escenas… pero no se usa en videojuegos a tiempo real porque consume muchos recursos.

Con un poco de álgebra se puede calcular la intersección entre un rayo y una esfera, entre un rayo y un plano, un cubo, o incluso un triángulo… las matemáticas para hacer estos cálculos pueden llegar a ser más o menos complicadas… pero si programas en XNA, se te va a caer la lagrimita de lo fácil que es utilizar técnicas de trazado de rayos. A pesar de esta ventaja, hay que decir que la documentación en MSDN acerca de la estructura Ray es escueta… por decirlo de forma amable. Así que voy a compartir con vosotros algunos experimentos que he hecho con ella y os puede interesar conocer.

En este ejemplo lo que hacemos es crear un rayo a partir de la posición del puntero del ratón sobre la pantalla, y calcular el destino del rayo a partir de la vista y proyección de la camara -pero creas que es gran cosa, porqué aquí XNA vuelve a socorrernos-.

Así pues, en el método update, “refrescaremos” el rayo llamando al método siguiente:

   1: private Ray posicionarRayo() 

   2:         {

   3:             Vector2 posicionMouse = new Vector2(Mouse.GetState().X, Mouse.GetState().Y);

   4:  

   5:             // Calculamos a qué posiciones del World corresponden las del mouse con Unproject

   6:             Vector3 puntoCercano = GraphicsDevice.Viewport.Unproject(new Vector3(posicionMouse, 0f),

   7:                 camara.projection, camara.view, Matrix.Identity);

   8:  

   9:             Vector3 puntoLejano = GraphicsDevice.Viewport.Unproject(new Vector3(posicionMouse, 1f),

  10:                 camara.projection, camara.view, Matrix.Identity);

  11:  

  12:  

  13:             Vector3 direccion = puntoLejano - puntoCercano;

  14:  

  15:             direccion.Normalize();

  16:  

  17:             return new Ray(puntoCercano, direccion);

  18:         }

El método realmente interesante de este código es Unproject. Lo que hace es proyectar el un vector a partir de la posición del mouse, en una posición de la escena en 3D.

Después, para saber si hay intersección entre el rayo y el BoundingSphere, sería algo tan sencillo como utilizar un método de la propia estructura Ray:

   1: rayo.Intersects(ref esfera, out distancia);

Donde esfera es un boundingSphere y la distancia es la longitud a la que existe la intersección entre el rayo y el boundingsphere.

Finalmente, para pintar el texto encima del modelo 3D (en este caso las primitivas de un BoundingSphere), tenemos que traducir la posición en el world del modelo (en este caso hemos puesto Matrix.Identity) a lo que es la pantalla:

   1: string mensaje = "Rayo intersecciona con la esfera!";

   2:                 

   3: // Calculamos la posición en pantalla del boundingSphere gracias al método Project del Viewport

   4: Vector3 posicionenPantalla = graphics.GraphicsDevice.Viewport.Project(

   5: Vector3.Zero, camara.projection, camara.view, Matrix.Identity);

   6:  

   7: // Obtenemos el centro del texto

   8: Vector2 centroTexto = this.fuente.MeasureString(mensaje) / 2;

   9:  

  10: Vector2 posicionTexto = new Vector2(posicionenPantalla.X, posicionenPantalla.Y - (esfera.Radius + 50));

  11:  

  12: spriteBatch.DrawString(fuente, mensaje, posicionTexto - centroTexto, Color.White);

El resultado de este ejemplo sencillo sería el siguiente, donde estamos pintando un boundingsphere (aparece rojo si ponemos el mouse “sobre él”, y blanco si el mouse queda “fuera” de él, además aparece el texto en blanco por encima):

Además, en XNA Creators Club Online hay un par de ejemplos interesantes relacionados con el trazado de rayos donde podrás ampliar conocimientos:

Rayos con modelos
http://creators.xna.com/en-US/sample/picking

Rayos a nivel de triangulos
http://creators.xna.com/en-US/sample/pickingtriangle

2 comentarios en “[XNA] Introducción al trazado de rayos con XNA”

Deja un comentario

Tu dirección de correo electrónico no será publicada. Los campos obligatorios están marcados con *