Perspective : easy 3D programming with Silverlight 5

Perspective : easy 3D programming with Silverlight 5

Version 5 of Silverlight allows building 3D scenes that use hardware acceleration (ie, that delegate display calculations to the graphics processor or GPU). This is actually an integration of XNA within the Silverlight runtime. As Don Burnett points out in his article XNA for Web Browsers or Silverlight 5 3D from Mix11 , the focus is on code portability and not on XNA compatibility with WPF high-level 3D API. XNA is a low-level API, designed for programmers, and a priori unsuitable for a declarative language like XAML and for associated tools. So, what about the designer/developer collaboration ?

With my experience of encapsulation of the WPF 3D API, I created in the Perspective library a set of classes to bring 3D programming with Silverlight 5 to a level close to WPF and Perspective for WPF .

This set of classes is provided in the assembly Perspective.Wpf3DX.dll for Silverlight 5, with its source code.

Download

Introduction to 3D

3D is a technique used to represent a scene with depth, relief and perspective.

The content of the scene is defined by 3D models, whose faces are composed of triangles with a texture (color, pattern) and a material (which defines the response to light). Indeed, The smallest surface that can be represented is a triangle.

The position of the point of view and the looking direction determine the appearance of a 3D scene. These are managed by a PerspectiveCamera object, which represents a movie camera.

A scene must contain a light to show its content. Specific objects are used to define the nature of light : multidirectional ambient lightning, directional lightning (similar to sunlight), spot lighting, etc..

The principle of displaying a scene consists in providing models data to the GPU and then calling a display function. These operations are repeated at each screen refresh (usually 30 to 60 times per second). Drawing informations are provided through the triangles vertices array (vertex buffer), projection matrices, lighting information, vertices and pixels management algorithms (specific programs called shaders), etc. 3D development is inherently low-level.

3D Scene

The Silverlight 3D coordinate system has the following features, from an observation point whose coordinates are positive on all three axes :

  • Abscissa X, positive values to the right.
  • Ordinate Y, positive values upward.
  • Depth Z, positive values forward.

Silverlight 3D coordinate system

This is, like in WPF, a "right hand" system.

Indeed, the axes of a 3D coordinate system can be represented by the fingers of one hand: thumb is the X axis, index is the y-axis and middle finger is the Z axis. The X axis is usually directed to the right and the Y axis upward. The direction of the Z axis is specific to each of both systems: the "right hand" one and the "left hand" one.

The physical size of the unit and the position on the screen of the origin point (0, 0, 0) depend on the parameters of the screen projection.

In XNA (and thus in Silverlight), all the 3D coordinates are defined with the float type (as opposed to WPF which uses the double type).

The XNA Vector3 structure enables to store the coordinates of a point, exposed in its X, Y and Z properties.

Workshop3DX : a workshop for 3D exploration

Workshop3DX is a 2D Silverlight control from the Perspective library which makes it easy to display a 3D scene and to move it with the keyboard. It encapsulates a DrawingSurface element (Silverlight 5 basic element to display a 3D scene) and a Perspective Scene object, which is used to configure camera, lighting and models. A camera and lighting are provided by default.

A Perspective 3D scene can be defined in XAML (present article) or in .NET code (C#, Visual Basic, etc.) .

The following XAML example displays a cube using Workshop3DX, Scene and Box classes of the Perspective library :

<UserControl 
  x:Class="PerspectiveDemo.Wpf3DX.DemoPage"
...
  xmlns:p="http://www.codeplex.com/perspective">
  <Grid x:Name="LayoutRoot" Background="#333333">
    <p:Workshop3DX 
      Name="workshop3DX">
      <p:Scene>
        <p:Box />
      </p:Scene>
    </p:Workshop3DX>
  </Grid>
</UserControl>

Perspective 3D scene

The different types of Perspective predefined 3D models are presented in this article .

Interactivity

When the Workshop3DX is focused, the keyboard can be used to control the camera's zoom factor, position and direction :

  • [Plus] and [Minus] keys act on the zoom factor (filed of view).
  • Numeric keypad arrow key act on the camera position (on a xz-plane).
  • When the [Ctrl] key is pressed, they move the orientation of the camera according to a vertical plane ([Up] and [Down] arrows) or horizontal plane ([Left] and [Right] arrows). When the [Shift] key is pressed, they turn the camera around the origin according to a vertical plane ([Up] and [Down] arrows) or horizontal plane ([Left] and [Right] arrows).
  • The key [5] or [Ctrl][Plus] of the numeric keypad elevates the position of the camera. Keys [Ctrl][5] or [Ctrl][Minus] reduces the height of the camera position.

Keyboard is now the only interactive interface available for Workshop3DX.

Materialization of the coordinate system

The Perspective XyzAxis element materializes the coordinate system on the origin point, which facilitates the development of models. The Length property indicates the size of its axes (in 3D units).

<p:Workshop3DX 
  Name="workshop3DX">
  <p:Scene>
    <p:XyzAxis Length="3"/>
  </p:Scene>
</p:Workshop3DX>

3 units system

The Signed property (false by default) indicates whether the axes should also be drawn on negative values.

Position and size of models

If we describe a scene using a XyzAxis model and a Box model, we find that the cube is positioned at the origin, and that the size of each side is 1 unit.

<p:Scene>
  <p:XyzAxis Length="3"/>
  <p:Box />
</p:Scene>

Position and size

All 3D models of the Perspective library are by default positioned on the origin, usually with dimensions of 1 unit. They do not expose property to change position and size.

Indeed, as far as possible, positioning and sizing models are entrusted to the Perspective 3D transformations objects that use the GPU.

These 3D transformations are used to move, reduce, enlarge or rotate a model, or even apply a combination of these effects. These are specified in the Transform property of the model, mainly through Translation, Rotation and Scaling classes. ModelTransformGroup class applies several transformations.

<p:Box>
  <p:Box.Transform>
    <p:ModelTransformGroup>
      <p:Translation OffsetX="-1" OffsetY="0.5" />
      <p:Scaling ScaleZ="2" />

      <p:Rotation Angle="10" Direction="Clockwise" Axis="Z"/>

      <p:Rotation Angle="15" Direction="CounterClockwise"  Axis="Y"/>
    </p:ModelTransformGroup>
  </p:Box.Transform>
</p:Box>

Transformations

Texture

The color or pattern of Perspective models are defined in their Texture property through ColorTexture or BitmapTexture objects. Internally, these objects use XNA textures. The following example changes the color of a model (yellow by default):

<p:Box>
  <p:Box.Texture>
    <p:ColorTexture R="0.55" G="0.15" B="0.87" A="1"/>
  </p:Box.Texture>
</p:Box>

Color

The management of textures is different from WPF, that uses brushes.

Class BitmapTexture allows to use an image as a texture. The image is specified by a previously initialized BitmapSource object. In the current version of Perspective, it is necessary to do this initialization by .NET code.

Moreover, in comparison with WPF, Silverlight scalability is limited. Thus, it is not possible (to my knowledge) to retrieve in the .NET code a Perspective model reference declared with the XAML x:Name attribute. A reference must be created manually in .NET code so that the object can be handled globally in the different events on a page. This reference may be initialized in the Initialized event of the model.

In the following example, when the Initialized event fires, an image is loaded from the assembly's embedded resources and then used as a texture via a BitmapImage object (inherited from BitmapSource).

<p:Box Initialized="Box_Initialized" />
// Code-behind :
private void Box_Initialized(object sender, EventArgs e)
{
  Uri uri = new Uri("/Perspective.Demo3D;component/BoxTexture.png", UriKind.Relative);
  StreamResourceInfo sri = Application.GetResourceStream(uri);
  BitmapImage bitmapImage = new BitmapImage();
  bitmapImage.SetSource(sri.Stream);
  var box = (Box)sender;
  box.Texture = new BitmapTexture(bitmapImage);
  box.InvalidateTexture();
}

The InvalidateTexture method notifies the Perspective display engine of a texture change.

Image texture

Material

The material of a model defines its reaction to light. It is defined in the Material property of the model using a ModelMaterial object which has the following properties (whose values must be between 0 and 1) :

  • Ambientness : ambient light response factor.
  • Diffuseness : directional light response factor.
  • Specularness : specularness factor (the intensity of the reflection being adjustable by means of Shininess property).
  • Emissiveness : light emission factor (with it, a model doesn't need lightning).

The following example shows how to set a bright sphere, using the Spherical Perspective model, which contains reflections of lighting:

<p:Spherical
  ParallelCount="80">
  <p:Spherical.Texture>
    <p:ColorTexture R="0.55" G="0.15" B="0.87" A="1"/>
  </p:Spherical.Texture>
  <p:Spherical.Material>
    <p:ModelMaterial 
      Diffuseness="1.0"
      Specularness="1" 
      Shininess="0.8" />
  </p:Spherical.Material>
</p:Spherical>

Material

Animations

Although the Perspective 3D scenes can be defined with XAML, the properties of 3D Perspective classes are not Dependency Properties and can not be animated using the traditional animation mechanism of Silverlight. This is due to the underlying use of XNA, which requires an animation mechanism based on .NET code.

Typically, the animation will be applied to a property of a transformation during the Render event (fired during the display of each frame).

The following example animates the rotation of a sphere around the origin. A primary translation shifts the sphere of 4 units. A rotation is added and the Angle property is incremented at each frame during the Render event. Previously, the model is referenced in the Initialized event.

<p:Spherical
  Initialized="Spherical_Initialized"
  Render="Spherical_Render"
  ParallelCount="80">
  ...
  <p:Spherical.Transform>
    <p:ModelTransformGroup>
      <p:Translation OffsetX="4"/>
    </p:ModelTransformGroup>
  </p:Spherical.Transform>
</p:Spherical>
// Code-behind :
private Spherical _spherical;
private Rotation _sphericalRotation = new Rotation();
private void Spherical_Initialized(object sender, EventArgs e)
{
  _spherical = (Spherical)sender;
  _sphericalRotation.Axis = AxisDirection.Y;
  (_spherical.Transform as ModelTransformGroup).Children.Add(_sphericalRotation);
}
private void Spherical_Render(object sender, Wpf3DX.RenderEventArgs e)
{
  if (_spherical != null)
  {
    _sphericalRotation.Angle++;
    _spherical.InvalidateTransform();
  }
}

The InvalidateTransform method notifies the rendering engine of change in the transformation.

About this article

Author : Olivier Dewit.

History :

  • October 27, 2012 : texture code fix, antialiasing, link to dynamic scenes .
  • February 6, 2012 : animation code fix.
  • December 28, 2011 : validation for Silverlight 5 RTM.
  • August 15, 2011 : translation in English.
  • June 29, 2011 : 1st publication for Silverlight 5 beta, in French.