Esta documentación está archivada y no tiene mantenimiento.

Información general sobre gráficos 3D

Actualización: noviembre 2007

En este tema se proporciona información general sobre la funcionalidad 3D del sistema de gráficos de Windows Presentation Foundation (WPF). La implementación de 3D en WPF permite a los programadores dibujar, transformar y animar gráficos 3D en el marcado y en el código de procedimientos, utilizando las mismas funciones permitidas por la plataforma a los gráficos 2D. Los programadores pueden combinar gráficos 2D y 3D para crear controles enriquecidos, proporcionar ilustraciones complejas de datos o mejorar la experiencia del usuario con la interfaz de una aplicación. La compatibilidad con 3D de WPF no está diseñada para proporcionar una plataforma con todas las funciones para la programación de juegos.

Este tema contiene las secciones siguientes.

El contenido de los gráficos 3D de WPF se encapsula en un elemento, Viewport3D, que puede participar en la estructura de elementos bidimensionales. El sistema de gráficos trata Viewport3D como un elemento visual bidimensional como tantos en WPF. Viewport3D actúa como una ventana, un área de visualización, a una escena tridimensional. Expresado de modo más preciso, es la superficie sobre la que se proyecta una escena 3D.

En una aplicación 2D convencional, utilice Viewport3D como cualquier otro elemento contenedor, como Grid o Canvas. Aunque puede utilizar Viewport3D con otros objetos de dibujo 2D en el mismo gráfico de escena, no puede interpenetrar objetos 2D y 3D dentro de Viewport3D. Este tema se centrará en cómo dibujar gráficos 3D dentro de Viewport3D.

El sistema de coordenadas de WPF para gráficos 2D localiza el origen en la parte superior izquierda del área de representación (que suele ser la pantalla). En el sistema 2D, los valores positivos del eje X se extienden hacia la derecha, y los valores positivos del eje Y extienden hacia abajo. En el sistema de coordenadas 3D, sin embargo, el origen se encuentra en el centro del área de representación, los valores positivos del eje X se extienden hacia la derecha, los valores positivos del eje Y se extienden hacia arriba (no hacia abajo) y los valores positivos del eje Z se extienden hacia el exterior partiendo del origen, es decir, hacia el espectador.

Representaciones convencionales de los sistemas de coordenadas 2D y 3D
Sistemas de coordenadas

El espacio definido por estos ejes es el marco estático de referencia para los objetos 3D en WPF. Cuando se generan modelos en este espacio y se crean luces y cámaras para verlos, resulta útil distinguir este marco estático de referencia, o "espacio universal", del marco de referencia local creado para cada modelo al aplicarle transformaciones. Recuerde también que los objetos del espacio universal podrían parecer completamente diferentes, o incluso no estar visibles en absoluto, dependiendo de la configuración de las luces y la cámara, pero que la posición de la cámara no cambia la ubicación de los objetos en el espacio universal.

Los programadores que trabajan en 2D están acostumbrado a colocar los elementos de dibujo primitivos en una pantalla bidimensional. Al crear una escena 3D, es importante recordar que, en realidad, se está creando una representación 2D de los objetos 3D. Dado que una escena 3D tiene un aspecto diferente dependiendo del punto de vista del espectador, debe especificar ese punto de vista. La clase Camera permite especificar este punto de vista para una escena 3D.

Otra manera de entender cómo se representa una escena 3D en una superficie 2D consiste en describir dicha escena como una proyección sobre la superficie de visualización. ProjectionCamera permite especificar proyecciones diferentes y sus propiedades para cambiar la manera en que el espectador ve los modelos 3D. PerspectiveCamera especifica una proyección en escorzo de la escena. En otras palabras, PerspectiveCamera proporciona una perspectiva de punto de fuga. Puede especificar la posición de la cámara en el espacio de coordenadas de la escena, la dirección y el campo de visión de la cámara, y un vector que define la dirección "hacia arriba" en la escena. En el diagrama siguiente se muestra la proyección de PerspectiveCamera.

Las propiedades NearPlaneDistance y FarPlaneDistance de ProjectionCamera limitan el intervalo de proyección de la cámara. Dado que las cámaras se pueden ubicar en cualquier parte de la escena, es posible situarlas dentro de un modelo o muy cerca de él, con lo que resultaría difícil distinguir correctamente los objetos. NearPlaneDistance permite especificar una distancia mínima a la cámara a partir de la cual no se dibujarán objetos. A la inversa, FarPlaneDistance permite especificar una distancia máxima a la cámara, más allá de la que no se dibujarán objetos, lo que garantice que no se incluyan en la escena aquellos objetos que estén demasiado lejos para ser reconocibles.

Posición de la cámara
Instalación de cámara

OrthographicCamera especifica una proyección ortogonal de un modelo 3D sobre una superficie visual 2D. Al igual que otras cámaras, especifica una posición, dirección de visualización y dirección "hacia arriba". Sin embargo, a diferencia de PerspectiveCamera, OrthographicCamera describe una proyección que no incluye la perspectiva en escorzo. Es decir, OrthographicCamera describe un cuadro de vista cuyos lados son paralelos, en lugar de uno cuyos lados convergen en un punto de la cámara. La imagen siguiente muestra el mismo modelo visto con PerspectiveCamera y con OrthographicCamera.

Perspectiva y proyecciones ortográficas
Proyección en perspectiva y ortográfica

En el código siguiente se muestran algunos valores de cámara típicos.

			<!-- Add a camera. -->
			<Viewport3D.Camera>
				<PerspectiveCamera FarPlaneDistance="20" LookDirection="5,-2,-3" UpDirection="0,1,0" NearPlaneDistance="1" Position="-5,2,3" FieldOfView="45" />
			</Viewport3D.Camera>


// Defines the camera used to view the 3D object. In order to view the 3D object,
// the camera must be positioned and pointed such that the object is within view 
// of the camera.
PerspectiveCamera myPCamera = new PerspectiveCamera();

// Specify where in the 3D scene the camera is.
myPCamera.Position = new Point3D(0, 0, 2);

// Specify the direction that the camera is pointing.
myPCamera.LookDirection = new Vector3D(0, 0, -1);

// Define camera's horizontal field of view in degrees.
myPCamera.FieldOfView = 60;

// Asign the camera to the viewport
myViewport3D.Camera = myPCamera;


// Defines the camera used to view the 3D object. In order to view the 3D object,
// the camera must be positioned and pointed such that the object is within view 
// of the camera.
PerspectiveCamera myPCamera = new PerspectiveCamera();

// Specify where in the 3D scene the camera is.
myPCamera.Position = new Point3D(0, 0, 2);

// Specify the direction that the camera is pointing.
myPCamera.LookDirection = new Vector3D(0, 0, -1);

// Define camera's horizontal field of view in degrees.
myPCamera.FieldOfView = 60;

// Asign the camera to the viewport
myViewport3D.Camera = myPCamera;


Model3D es la clase base abstracta que representa un objeto 3D genérico. Para generar una escena 3D, necesita algunos objetos que ver; los objetos que componen el gráfico de escena se derivan de la clase Model3D. En la actualidad, WPF permite geometrías de modelado con GeometryModel3D. La propiedad Geometry de este modelo acepta un elemento primitivo de malla.

Para crear un modelo, comience por crear un elemento primitivo, o malla. Un elemento 3D primitivo es una colección de vértices que constituyen una entidad 3D única. La mayoría de los sistemas 3D proporcionan elementos primitivos modelados a partir de la figura cerrada más simple: un triángulo definido por tres vértices. Dado que los tres puntos de un triángulo son coplanares, puede seguir agregando triángulos para modelar formas más complejas, denominadas mallas.

El sistema 3D de WPF proporciona actualmente la clase MeshGeometry3D, que permite especificar cualquier geometría; en este momento, no admite elementos 3D primitivos predefinidos, tales como esferas o formas cúbicas. Empiece por crear MeshGeometry3D especificando una lista de vértices de triángulos como su propiedad Positions. Cada vértice se especifica como un Point3D. (En Lenguaje de marcado de aplicaciones extensible (XAML), especifique esta propiedad como una lista de números agrupados en ternas, que representan las coordenadas de cada vértice.) Según cuál sea la geometría, la malla puede estar compuesta de muchos triángulos, algunos de los cuales compartirán las mismas esquinas (vértices). WPF necesita información sobre qué triángulos comparten qué vértices para dibujar la malla correctamente. Esta información se proporciona especificando una lista de índices de triángulos con la propiedad TriangleIndices. Esta lista especifica el orden en el que los puntos especificados en la lista Positions determinarán un triángulo.

<GeometryModel3D>
  <GeometryModel3D.Geometry>
          <MeshGeometry3D 
              Positions="-1 -1 0  1 -1 0  -1 1 0  1 1 0"
              Normals="0 0 1  0 0 1  0 0 1  0 0 1"
              TextureCoordinates="0 1  1 1  0 0  1 0   "
              TriangleIndices="0 1 2  1 3 2" />
      </GeometryModel3D.Geometry>
      <GeometryModel3D.Material>
          <DiffuseMaterial>
              <DiffuseMaterial.Brush>
                  <SolidColorBrush Color="Cyan" Opacity="0.3"/>
              </DiffuseMaterial.Brush>
          </DiffuseMaterial>
      </GeometryModel3D.Material>
  <!-- Translate the plane. -->
      <GeometryModel3D.Transform>
          <TranslateTransform3D
            OffsetX="2" OffsetY="0" OffsetZ="-1"   >
          </TranslateTransform3D>
      </GeometryModel3D.Transform>
  </GeometryModel3D>


En el ejemplo anterior, la lista Positions especifica ocho vértices para definir una malla en forma de cubo. La propiedad TriangleIndices especifica una lista de doce grupos de tres índices. Cada número de la lista hace referencia a un desplazamiento en la lista Positions. Por ejemplo, los tres primeros vértices especificados por la lista Positions son (1,1,0), (0,1,0) y (0,0,0). Los tres primeros índices especificados por la lista TriangleIndices son 0, 2 y 1, que corresponden a los puntos primero, tercero y segundo de la lista Positions. Como resultado, el primer triángulo que constituye el modelo del cubo se creará de (1,1,0) a (0,1,0) y a (0,0,0), y los once triángulos restantes se determinarán de igual forma.

Puede seguir definiendo el modelo especificando valores para las propiedades Normals y TextureCoordinates. Para representar la superficie del modelo, el sistema de gráficos necesita información sobre en qué dirección mira la superficie de cualquier triángulo dado. Utiliza esta información para realizar los cálculos de iluminación del modelo: las superficies que miran directamente hacia una fuente de luz parecen más luminosas que las que tienen un ángulo que las oculta de la luz. Aunque WPF puede determinar los vectores normales predeterminados utilizando las coordenadas de posición, también es posible especificar vectores normales diferentes para crear un aspecto más aproximado de las superficies curvas.

La propiedad TextureCoordinates especifica una colección de puntos (Point) que indica al sistema de gráficos cómo asignar las coordenadas que determinan cómo trazar una textura en los vértices de la malla. Las TextureCoordinates se especifican como un valor comprendido entre cero y 1, incluidos. Como sucede con la propiedad Normals, el sistema de gráficos puede calcular las coordenadas de textura predeterminadas, pero si lo desea puede establecer coordenadas de textura diferentes a fin de controlar la asignación de una textura que incluya parte de un patrón repetitivo, por ejemplo. Encontrará más información sobre coordenadas de textura en los temas siguientes o en Managed Direct3D SDK.

En el ejemplo siguiente se muestra cómo crear una cara del modelo del cubo en código de procedimiento. Observe que puede dibujar el cubo completo como un solo objeto GeometryModel3D; en este ejemplo se dibuja la cara del cubo como un modelo distinto a fin de aplicar después texturas independientes a cada cara.

MeshGeometry3D side1Plane = new MeshGeometry3D();


side1Plane.Positions.Add(new Point3D(-0.5, -0.5, -0.5));
side1Plane.Positions.Add(new Point3D(-0.5, 0.5, -0.5));
side1Plane.Positions.Add(new Point3D(0.5, 0.5, -0.5));
side1Plane.Positions.Add(new Point3D(0.5, 0.5, -0.5));
side1Plane.Positions.Add(new Point3D(0.5, -0.5, -0.5));
side1Plane.Positions.Add(new Point3D(-0.5, -0.5, -0.5));

side1Plane.TriangleIndices.Add(0);
side1Plane.TriangleIndices.Add(1);
side1Plane.TriangleIndices.Add(2);
side1Plane.TriangleIndices.Add(3);
side1Plane.TriangleIndices.Add(4);
side1Plane.TriangleIndices.Add(5);

side1Plane.Normals.Add(new Vector3D(0, 0, -1));
side1Plane.Normals.Add(new Vector3D(0, 0, -1));
side1Plane.Normals.Add(new Vector3D(0, 0, -1));
side1Plane.Normals.Add(new Vector3D(0, 0, -1));
side1Plane.Normals.Add(new Vector3D(0, 0, -1));
side1Plane.Normals.Add(new Vector3D(0, 0, -1));

side1Plane.TextureCoordinates.Add(new Point(1, 0));
side1Plane.TextureCoordinates.Add(new Point(1, 1));
side1Plane.TextureCoordinates.Add(new Point(0, 1));
side1Plane.TextureCoordinates.Add(new Point(0, 1));
side1Plane.TextureCoordinates.Add(new Point(0, 0));
side1Plane.TextureCoordinates.Add(new Point(1, 0));


Para que una malla parezca un objeto tridimensional, debe tener una textura aplicada que cubra la superficie definida por sus vértices y triángulos, de manera que se pueda iluminar y proyectar por la cámara. En 2D, se utiliza la clase Brush para aplicar colores, patrones, degradados y otro contenido visual a las áreas de la pantalla. El aspecto de los objetos 3D, sin embargo, depende del modelo de iluminación, no sólo del color o del patrón que se les aplica. Los objetos reales reflejan la luz de manera distinta según la calidad de su superficie: las superficies satinadas y brillantes no tienen el mismo aspecto que las superficies ásperas o mates, y algunos objetos parecen absorber la luz, mientras que otros la emiten. Puede aplicar a los objetos 3D los mismos pinceles que a los objetos 2D, pero no directamente.

Para definir las características de la superficie de un modelo, WPF utiliza la clase abstracta Material. Las subclases concretas de Material determinan algunas de las características del aspecto de la superficie del modelo y, además, cada una de ellas proporciona una propiedad Brush a la que puede pasar SolidColorBrush, TileBrush o VisualBrush.

  • DiffuseMaterial especifica que el pincel se aplicará al modelo como si estuviera iluminado con una luz difusa. Utilizar DiffuseMaterial es lo que más se parece al uso directo de pinceles en los modelos 2D; las superficies del modelo no reflejan la luz como si brillasen.

  • SpecularMaterial especifica que el pincel se aplicará al modelo como si la superficie del modelo fuese dura o brillante, capaz de reflejar la iluminación. Puede establecer el grado en que la textura sugerirá esta cualidad de reflexión, o "brillo", especificando un valor para la propiedad SpecularPower.

  • EmissiveMaterial permite especificar que la textura se aplicará como si el modelo estuviera emitiendo luz del mismo color que el pincel. Esto no convierte el modelo en una luz; sin embargo, participará de manera diferente en el sombreado que si se aplica textura con DiffuseMaterial o SpecularMaterial.

Para mejorar rendimiento, las caras ocultas de GeometryModel3D (aquéllas que están fuera de la vista porque se encuentran en el lado del modelo opuesto a la cámara) se seleccionan de la escena. Para especificar el Material que se aplicará a la cara oculta de un modelo, como un plano, establezca la propiedad BackMaterial del modelo.

Para lograr algunas cualidades de la superficie, como el brillo o los efectos de reflejo, puede ser conveniente aplicar sucesivamente varios pinceles diferentes a un modelo. Puede aplicar y reutilizar varios materiales mediante la clase MaterialGroup. Los elementos secundarios de MaterialGroup se aplican del primero al último en varias pasadas de representación.

En los ejemplos de código siguientes se muestra cómo aplicar un color sólido y un dibujo como pinceles a los modelos 3D.

<GeometryModel3D.Material>
    <DiffuseMaterial>
        <DiffuseMaterial.Brush>
            <SolidColorBrush Color="Cyan" Opacity="0.3"/>
        </DiffuseMaterial.Brush>
    </DiffuseMaterial>
</GeometryModel3D.Material>


<DrawingBrush x:Key="patternBrush" Viewport="0,0,0.1,0.1" TileMode="Tile">
  <DrawingBrush.Drawing>
    <DrawingGroup>
      <DrawingGroup.Children>
        <GeometryDrawing Geometry="M0,0.1 L0.1,0 1,0.9, 0.9,1z"
          Brush="Gray" />
        <GeometryDrawing Geometry="M0.9,0 L1,0.1 0.1,1 0,0.9z"
          Brush="Gray" />
        <GeometryDrawing Geometry="M0.25,0.25 L0.5,0.125 0.75,0.25 0.5,0.5z"
          Brush="#FFFF00" />
        <GeometryDrawing Geometry="M0.25,0.75 L0.5,0.875 0.75,0.75 0.5,0.5z"
          Brush="Black" />
        <GeometryDrawing Geometry="M0.25,0.75 L0.125,0.5 0.25,0.25 0.5,0.5z"
          Brush="#FF0000" />
        <GeometryDrawing Geometry="M0.75,0.25 L0.875,0.5 0.75,0.75 0.5,0.5z"
          Brush="MediumBlue" />
      </DrawingGroup.Children>
    </DrawingGroup>
  </DrawingBrush.Drawing>
</DrawingBrush>


<DrawingBrush x:Key="patternBrush" Viewport="0,0,0.1,0.1" TileMode="Tile">
  <DrawingBrush.Drawing>
    <DrawingGroup>
      <DrawingGroup.Children>
        <GeometryDrawing Geometry="M0,0.1 L0.1,0 1,0.9, 0.9,1z"
          Brush="Gray" />
        <GeometryDrawing Geometry="M0.9,0 L1,0.1 0.1,1 0,0.9z"
          Brush="Gray" />
        <GeometryDrawing Geometry="M0.25,0.25 L0.5,0.125 0.75,0.25 0.5,0.5z"
          Brush="#FFFF00" />
        <GeometryDrawing Geometry="M0.25,0.75 L0.5,0.875 0.75,0.75 0.5,0.5z"
          Brush="Black" />
        <GeometryDrawing Geometry="M0.25,0.75 L0.125,0.5 0.25,0.25 0.5,0.5z"
          Brush="#FF0000" />
        <GeometryDrawing Geometry="M0.75,0.25 L0.875,0.5 0.75,0.75 0.5,0.5z"
          Brush="MediumBlue" />
      </DrawingGroup.Children>
    </DrawingGroup>
  </DrawingBrush.Drawing>
</DrawingBrush>


DiffuseMaterial side5Material = new DiffuseMaterial((Brush)Application.Current.Resources["patternBrush"]);


Las luces de los gráficos 3D hacen lo mismo que las luces en el mundo real: permiten ver las superficies. Más concretamente, las luces determinan qué parte de una escena se incluye en la proyección. Los objetos de luz en WPF crean gran variedad de efectos de luz y sombra y siguen el modelo de comportamiento de diversas luces del mundo real. Debe incluir por lo menos una luz en la escena, pues de lo contrario no habrá ningún modelo visible.

Las luces siguientes se derivan de la clase base Light:

  • AmbientLight: proporciona iluminación de ambiente que ilumina uniformemente todos los objetos sin tener en cuenta su ubicación u orientación.

  • DirectionalLight: ilumina como una fuente de luz distante. Las luces direccionales tienen Direction, que se especifica como Vector3D, pero ninguna ubicación concreta.

  • PointLight: ilumina como una fuente de luz cercana. Las luces puntuales tienen posición y emiten la luz desde esa posición. Los objetos de la escena se iluminan dependiendo de su posición y distancia con respecto a la luz. PointLightBase expone una propiedad Range, que determina una distancia más allá de la cual la luz no iluminará los modelos. PointLight también expone propiedades de atenuación que determinan cómo disminuye la intensidad de la luz con la distancia. Puede especificar interpolaciones constantes, lineales o cuadráticas para la atenuación de la luz.

  • SpotLight: hereda de PointLight. Los focos de luz iluminan como las luces puntuales, y tienen posición y dirección. Proyectan la luz en una área cónica establecida por las propiedades InnerConeAngle y OuterConeAngle, especificadas en grados.

Las luces son objetos Model3D, por lo que puede transformar y animar las propiedades de la luz, incluidas su posición, posición, color, dirección y alcance.

<ModelVisual3D.Content>
    <AmbientLight Color="#333333" />
</ModelVisual3D.Content>


DirectionalLight myDirLight = new DirectionalLight();


myDirLight.Color = Colors.White;
myDirLight.Direction = new Vector3D(-3, -4, -5);


modelGroup.Children.Add(myDirLight);


Al crear modelos, éstos tienen una ubicación determinada en la escena. Para mover esos modelos por la escena, girarlos o cambiar su tamaño, no es práctico para cambiar los vértices que definen los propios modelos. En lugar de ello, al igual que en 2D, se aplican transformaciones a los modelos.

Cada objeto de modelo tiene una propiedad Transform con la que puede mover, reorientar o cambiar el tamaño del modelo. Al aplicar una transformación, en realidad lo que se hace es desplazar todos los puntos del modelo según un vector o valor especificado por la transformación. En otras palabras, se transforma el espacio de coordenadas en el que se ha definido el modelo ("espacio del modelo"), pero no se cambian los valores que constituyen la geometría del modelo en el sistema de coordenadas de la escena completa ("espacio universal").

Para obtener más información acerca de la transformación de modelos, consulte Información general sobre transformaciones de modelos 3D.

La implementación de 3D en WPF utiliza el mismo sistema de control de tiempo y animación que los gráficos 2D. En otras palabras, para animar una escena 3D, se animan las propiedades de sus modelos. Es posible de animar directamente las propiedades de los elementos primitivos, pero suele ser más fácil de animar las transformaciones que cambian la posición o el aspecto de los modelos. Dado que las transformaciones se pueden aplicar a los objetos Model3DGroup así como a los modelos individuales, es posible de aplicar un conjunto de animaciones a un elemento secundario de Model3DGroup y otro conjunto de animaciones a un grupo de objetos secundarios. También puede lograr gran variedad de efectos visuales animando las propiedades de iluminación de la escena. Finalmente, si lo desea puede animar la propia proyección, animando la posición de la cámara o el campo de visión. Para obtener información general sobre el sistema de control de tiempo y animación de WPF, consulte los temas Información general sobre animaciones, Información general sobre objetos Storyboard y Información general sobre objetos Freezable.

Para animar un objeto en WPF, se crea una escala de tiempo, se define una animación (que, en realidad, es un cambio de algún valor de propiedad a lo largo del tiempo) y se especifica la propiedad a la que aplicar la animación. Dado que todos los objetos de una escena 3D son elementos secundarios de Viewport3D, las propiedades de destino de cualquier animación que desea aplicar a la escena son propiedades de propiedades de Viewport3D.

Supongamos que desea hacer que un modelo parezca tambalearse en su lugar. Podría aplicar RotateTransform3D al modelo y animar el eje de giro de un vector a otro. En el ejemplo de código siguiente se muestra cómo aplicar Vector3DAnimation a la propiedad Axis de la propiedad Rotation3D de la transformación, suponiendo que RotateTransform3D es una de las diversas transformaciones aplicadas al modelo con TransformGroup.

//Define a rotation
RotateTransform3D myRotateTransform = new RotateTransform3D(new AxisAngleRotation3D(new Vector3D(0, 1, 0), 1));


Vector3DAnimation myVectorAnimation = new Vector3DAnimation(new Vector3D(-1, -1, -1), new Duration(TimeSpan.FromMilliseconds(5000)));
myVectorAnimation.RepeatBehavior = RepeatBehavior.Forever;


myRotateTransform.Rotation.BeginAnimation(AxisAngleRotation3D.AxisProperty, myVectorAnimation);


//Add transformation to the model
cube1TransformGroup.Children.Add(myRotateTransform);


Para representar la escena, agregue modelos y luces a Model3DGroup, a continuación, establezca Model3DGroup en el Content de ModelVisual3D. Agregue ModelVisual3D a la colección Children de Viewport3D. Agregue cámaras a Viewport3D, estableciendo su propiedad Camera.

Finalmente, agregue Viewport3D a la ventana. Cuando se incluye Viewport3D como contenido de un elemento de diseño, como Canvas, debe especificar el tamaño de Viewport3D estableciendo las propiedades Height y Width (heredadas de FrameworkElement).

Mostrar: