Tải bản đầy đủ - 0trang
1 3D—a natural way of interacting with information
3D—a natural way of interacting with information
The customer really wanted 3D (they actually asked for it), so they could quickly zip
around the model of the car, drawing cracks and chips. But the technology available to
us at the time and, quite frankly, our lack of knowledge, kept us from being able to
deliver on that. Instead, we opted for a flattened 2D version they could draw on. It was
nice, but it lacked that extra bit that could have put this application over the top.
The WPF 3D Closet Configurator. Shortly after I joined Microsoft in fall 2009, I
attended the Professional Developers Conference (PDC). While there, I talked to a
developer from Europe and a local designer who had together created a cool sit-down
in-store closet configurator application in WPF.2 European homes tend to be smaller
than American homes, so storage space is taken seriously. Configuring a closet for the
maximum use of that space can be a somewhat lengthy process.
The application they built was used in-store (like an upscale European version of a
big-box home store) and run by sales clerks with customers sitting next to them. The
clerk positioned 3D items on the screen: drawers, shelves, hangers, etc. to optimize the
use of space. While that was happening, the application built a list of all the pieces
needed. When complete, customers had 2D and 3D drawings of the final closet, a complete bill of materials, and an exact price for everything they needed. This approach
was both more enjoyable and more efficient than looking in catalogs or typing codes
into a computer.
It may not have had 300 text boxes on a battleship-gray forum, but there’s no
doubt in my mind that that was a business application, and a well-executed one at
that. There are many more examples of enhancing the experience using 3D for visualization or input. Once you have the skills and knowledge, you’ll find many other
opportunities where 3D can make your application that much more usable or help it
stand out in the crowd.
Now, to get back to the Amiga. The Amiga didn’t start it all, but it certainly popularized 3D. It was also one of the first consumer machines used in the production of
3D graphics on television (such as Babylon 5) and film. So, in tribute to the little Amiga
my friend had, and to the Amiga 1000 that started it all,3 and to show off what Silverlight is capable of, I decided to take inspiration from the Boing ball demo for the sample you’ll start in this chapter and complete in the next. In these two chapters, you’ll
build a simple animated bouncing ball demo, minus the physics simulation, to showcase the 3D capabilities of Silverlight.
This chapter isn’t intended to be a full primer on XNA and 3D, but more a practical one on how to use those technologies inside Silverlight. If you want to learn more
about these technologies, most of the things you’ll learn about XNA 4 on the web
apply to the version that’s in Silverlight. Plus, in general, 3D concepts such as matrices
and vertices are universal.
You can see my video interview showing the application here: http://bit.ly/WPF3dCloset.
Want to see some mid-1980s hardware demos that blew PCs and the Mac out of the water? See these two videos
on YouTube. First is the Boing ball that’s the inspiration for the 3D in this chapter: http://bit.ly/AmigaBoing.
Second is an iPhone-like shape/shadow real-time 3D demo: http://bit.ly/AmigaRT3D.
Introduction to 3D
26.2 The Silverlight/XNA 3D API
3D in Silverlight is based on a subset of the XNA Game Studio 4.0 3D core libraries,
which themselves are a .NET layer over the Windows-only DirectX technology. DirectX
is a C/C++ API for very low-level graphics programming, a driver specification, and also
a ton of helper functionality for creating graphics applications and games. It was
designed primarily to make games a reality on Windows but has since ended up just
about everywhere, including as the underlying technology for Windows Vista and Windows 7/8 desktop rendering.
NOTE XNA is not a high-level 3D engine with lighting and physics and what-
not, but rather the building blocks you or others can use to build a 3D rendering engine for specific application needs. A great example of a 3D engine with
all of that in place is the Babylon 3D Toolkit for Silverlight. This is freely available on CodePlex, so be sure to take a look at it there.
Silverlight supports what’s known as the XNA “Reach” profile. That means that it
works on Windows XP and above on Shader Model 2.0 or above DirectX 9–compatible
video card. The video card is a hard requirement; there’s no software fallback rendering. Not to worry, though, most PCs and laptops created in the past few years support
these levels. You’ll only run into problems if you try to repurpose that old Windows 3.1
26.2.1 Rendering pipeline
Silverlight supports Shader Model 2, not 3. This is important if you code your own
custom shaders or try to use shaders created by other people. The difference between
the two models has to do with what support they require on the video card as well
as differences in the shader programming language, number of available registers,
Unlike 2D shaders, 3D shaders are code that run directly on the GPU, outside the
Silverlight sandbox. Because of that hardware-level access, they can potentially be
used as a way to gain access to your computer for malicious purposes. Windows XP systems are especially vulnerable to this because the old driver model is nowhere nearly
as protected as the newer model. Silverlight 5 addresses this security issue by requiring
either elevated trust or end-user confirmation when you want to use the 3D system.
In addition, you must have the Silverlight EnableGPUAcceleration setting turned
on in order to use 3D rendering. Unlike WPF, Silverlight has no fallback to software
rendering when the hardware isn’t up to snuff.
In a nutshell, the rendering architecture of 3D rendering in Silverlight looks like
The rendering system takes vertices and primitives, which I’ll cover shortly, processes them, then moves them to the pixel-processing step. Pixel processing takes textures (images applied to the surface of 3D geometry) and combines them with the
geometry to produce the final on-screen pixels.
The Silverlight/XNA 3D API
The Silverlight 3D rendering pipeline. Source: MSDN Silverlight 3D Overview.
The XNA libraries are split across the Silverlight runtime, the Silverlight SDK, and the
Silverlight toolkit. The team split the assemblies this way to minimize the runtime
footprint of applications that don’t use 3D—the majority of Silverlight applications
out there. Conveniently, the 3D project templates take care of adding all the appropriate references for you.
26.2.2 Project templates
The Silverlight toolkit comes with several 3D project templates. You can get pretty far
without using the templates, but once you try to do something like load content,
you’ll wish you started with them. It’s also difficult to add a content project to an existing solution, so it’s best to start with the full project template.
The first step is to download and install the Silverlight 5 toolkit from http://
silverlight.codeplex.com. The Silverlight 5 version of the toolkit (I’m using the September 2011 build to code against, but a newer version should be out by the time you
read this) includes the Silverlight 3D Project templates as well as a number of helper
Next, you need to install XNA Studio 4 in order to use the 3D project templates
included with the toolkit. This free download is also linked to from the toolkit site.
Finally, if you plan to modify or compile any custom shaders in your project (the
default template, as of this writing, uses one named CustomEffect.slfx), you’ll need to
also install the latest DirectX SDK for Windows. It’s a big download, so budget some
time and bandwidth to get it.
Once you have the toolkit and other bits installed, create a new Silverlight 3D
Application project. I creatively named mine Silverlight3DExample. Figure 26.2 shows
the correct project template selected in the New Project dialog.
When the solution loads, you’ll see four different projects. The Silverlight project
and web project are ones you should be familiar with by now. The two content projects
are for loading assets like models, sounds, and images. They have a special relationship
Introduction to 3D
The New Project dialog showing the selected Silverlight 3D Application template
with the Silverlight application which (as of this writing) isn’t represented in the solution. If you crack open the Silverlight application’s project file, though, and search for
the word “content,” you’ll see how the two are linked.
The Silverlight application includes a basic Scene class as well as a custom vertex
type and a cube object. I’ll talk about vertices later. The cube is some prebuild geometry. That’s no fun, though! Ignore it. Delete it. Do whatever you need to do to pretend
it doesn’t exist. You’ll implement your own geometry so you’ll need to delete it later
Open the project properties for the Silverlight 3D application and make it an outof-browser application. Don’t make it elevated permissions for now; I’ll get to that
shortly. You want it left as is so you can do a little sleuthing in the next section.
26.3 Detecting capabilities with the GraphicsDeviceManager
Before you can use 3D, you should check to see if it’s available on the current system.
The user may have declined to permit it, the graphics card may not support it, or
something else may be wrong. Rather than simply show blank UI, it’s better to tell the
user what’s going on.
The GraphicsDeviceManager will be the primary mechanism for detecting the capabilities. But you’ll need to do a little work in XAML to make sure you have something
Detecting capabilities with the GraphicsDeviceManager
meaningful to show in either case. One thing I like to do for this and other similar
situations in Silverlight is to have the UI split into two different grids. The first grid is
shown when the feature can’t be used. The second grid is shown when the feature usage
Listing 26.1 shows an example of this using two grids: Overlay3DNotAvailable
Listing 26.1 The XAML UI with information overlay
3D goes here
When you pasted in this code, you got rid of the “myDrawingSurface” from XAML.
That’s okay—you’ll replace it later. I’m not a fan of the “my” naming convention in
any case. But to be able to compile, you’ll need to comment out the offending lines
that reference it in the code-behind. I’ll address that shortly so don’t worry about it
for the moment.
When the application first starts up, both grids are collapsed and therefore invisible. It will be up to your code to decide which grid should be displayed.
Next you need to add a little code to make use of the two different parts of the UI.
For this you’ll use the GraphicsDeviceManager class. This is a singleton class that
exposes an instance of itself through the Current property. The instance exposes two
Introduction to 3D
properties and an event that are useful when checking for the availability of 3D graphics. Table 26.1 lists them.
Table 26.1 GraphicsDeviceManager members used when checking for the availability of 3D graphics
Gets the status of 3D rendering capabilities. In Silverlight, because
there’s no software rendering, it’s either Unavailable or Hardware.
An enumeration of possible reasons for the RenderMode value.
Using this, you can determine if the cause is a failure to set GPU
acceleration, blocked by security, you have an incompatible card, or
3D is temporarily unavailable for some other system reason, such as
a Windows XP lock screen.
This event is raised whenever the RenderMode property changes.
The passed-in event args include all the information you’d need about
the current and previous render modes.
By using the GraphicsDeviceManager’s properties, you can quickly and easily tell if
you’ll be able to use 3D features in Silverlight. In addition, by ensuring you pay attention to changes in the render mode by using the RenderModeChanged event, you can
build a more robust application capable of reacting properly to system changes.
Listing 26.2 shows how to use the GraphicsDeviceManager to enable the correct
portions of the UI and inform the user of any changes to the render capabilities.
Listing 26.2 Checking for 3D availability using the GraphicsDeviceManager class
public partial class MainPage : UserControl
Loaded += new RoutedEventHandler(MainPage_Loaded);
void MainPage_Loaded(object sender, RoutedEventArgs e)
if (GraphicsDeviceManager.Current.RenderMode ==
void OnRenderModeChanged(object sender, RenderModeChangedEventArgs e)
Detecting capabilities with the GraphicsDeviceManager
if (e.RenderMode == RenderMode.Unavailable)
private void Disable3dUI(RenderModeReason reason)
string message = "3d Not Available. Reason: " + reason.ToString();
Overlay3DNotAvailable.Visibility = Visibility.Visible;
MainContent.Visibility = Visibility.Collapsed;
private void Enable3dUI()
Overlay3DNotAvailable.Visibility = Visibility.Collapsed;
MainContent.Visibility = Visibility.Visible;
Now you can run the application. You should see a MessageBox informing you that 3D
isn’t available. You’ll also see the “3D is not available” overlay UI once you dismiss the
Now, enable GPU acceleration. On a web page, you do so by using the object tag
For this project, though, I’d like to simply make it an elevated trust out-of-browser
application. This isn’t a requirement, but for the reasons mentioned in the overview
of this chapter, it makes security easier when you go to run the application outside of
the development environment.
If you haven’t already, open the project properties and select the option to enable
running out-of-browser, as covered in chapter 5. Next, click the out-of-browser settings
button, and check both Use GPU Acceleration and “Require elevated trust when running outside the browser.”
Now, run the application again. You shouldn’t see the MessageBox this time
around but instead should see the placeholder for 3D content. If you see the placeholder, you’re ready to proceed to the rest of the chapter. If you see a message about
not having 3D capabilities on your system, you may want to first update your graphics
driver from the vendor’s website.4 If you still see that message, you may be on a very
old or unsupported piece of hardware. There aren’t a lot of those out there, especially
All the cool kids know that you don’t get your graphics drivers from Windows Update if you’re using a discrete
graphics card. Instead, go to the nVidia or AMD site and download the correct drivers directly.
Introduction to 3D
in developer hands, but it happens. In that case, I can recommend only that you beg,
borrow, and steal until you have a more modern machine with 3D capabilities.
So far, you’ve used the RenderModeChanged event simply to enable or disable some
UI. In a real application, this is the place you’ll want to refresh your data. For example, if the render mode changes from unavailable to available, you’ll have to rebuild
the objects you need to support 3D rendering in your application. Without 3D graphics available, you wouldn’t want to bother loading all that data in memory.
The GraphicsDeviceManager and GraphicsDevice together have a lot of additional features, which you’ll explore in many of the remaining examples in this chapter. Before you explore the types that make up shapes to render, let’s wire up the
drawing surface to provide you with somewhere to render to.
26.4 Using the DrawingSurface
The regular Silverlight graphics system is a retained mode system. That means that you
define the structures for rendering (like a rectangle or some text), and Silverlight
remembers it from frame to frame. Another type of rendering, often used in games and
in 3D,5 is called immediate mode rendering. In this type of rendering, you’re responsible for drawing the entire frame of graphics on-screen each time it’s rendered.
Because of this different approach in rendering, Silverlight requires a rectangle on
the screen where the 3D content can be placed, rather than just loosely intermingling
it like any other UI element or requiring an actual new Windows-recognized operating system window like many other desktop technologies do. This rectangle, defined
by the DrawingSurface element, is the virtual window in your application that will be
owned by XNA/DirectX.
Because the DrawingSurface is a FrameworkElement, you can use it in XAML just
like any other element, but much like the MediaElement, it’s a window into another
rendering system. Update the MainPage.xaml file MainContent grid so it looks like
the next listing. You can leave the original TextBlock if you wish, or comment it out as
Listing 26.3 The DrawingSurface element on a page
And good old GDI/GDI+ if you ever had the pain or pleasure of overriding WM_PAINT to do your drawing.
Project structure: the scene and objects
Listing 26.3 shows how to put the drawing surface on a page in a Silverlight application. I mentioned that the DrawingSurface is a FrameworkElement. Because of that, it
can participate in composition and properly work with z-ordering and other XAML
features. You can overlay XAML to partially obscure the DrawingSurface, for example.
This is something that business application designers will simply expect, but for game
designers, that’s the holy grail of 2D and 3D composition efficiency.
Antialiasing is the generic name for a number of algorithms that help remove the
staircase effect on lines. If we had a higher density of pixels on the screen than our
eyes could detect, this wouldn’t be necessary. The reality is, displays haven’t yet caught
up. If you want to support antialiasing for your own project, the PreferredMultiSampleCount property is what you need to use. I broke out the CompositionMode
property because this is something you can set only in XAML.6 The higher the number, the slower the rendering, so be careful of how much smoothness you ask for.
In the markup, I wired up the Draw event exposed by the DrawingSurface. The
Draw event is raised whenever the drawing surface is invalidated—meaning whenever
it needs to be drawn. If you follow the usual approach of drawing items inside the
event handler and then calling InvalidateSurface, it’ll be called for each frame to
be rendered to screen.
TIP If you’re looking for a place to put code to handle the screen resizing—
for example, to recalculate what you put in 3D space—the DrawingSurface
SizeChanged event is the recommended location.
If you run the application now (if you pasted the code rather than typing it by hand,
make sure you have the Draw event handler MainDrawingSurface_Draw:stubbed out),
you’ll get a blank 3D rendering space, most likely purple.7
You now have an application that’s capable of rendering 3D content. But you don’t
yet have anything in place to actually render. You’d be forgiven for assuming it’d be a
simple matter to throw a few shapes out there and they’d easily render. 3D rendering
is much more complex than that. For that reason, you’ll need to add structure and a
fair bit of code to the application before you’ll see results.
26.5 Project structure: the scene and objects
3D programming doesn’t start to make sense until you see an actual shape on-screen.
If there’s any one single chapter where I’d recommend you type in the code and
experiment a bit to see what happens, this would be that chapter.
This was true in the prerelease builds and is expected to remain true in the released product. Don’t just take
my word for it, though; try it out both in markup and in code.
As a reviewer from the Baltimore area pointed out, it’s a nice Ravens purple. Go Ravens!
Introduction to 3D
A high-level look at the
architecture of the
The problem is, 3D rendering systems show you absolutely nothing until you have all
the major pieces in place. If I added bits to the code as they were explained, you
wouldn’t see anything rendered on-screen until the last few sections in this chapter. I
don’t know about you, but when talking about something like 3D, I want to see pixels!
In this section, you’ll structure the project to make it easier to plug additional
pieces in later. There will be a number of things, like the camera, projections, shaders,
and more, that will be included in here in the barest possible way simply to make sure
you can render on-screen.
You can avoid these problems if you use a 3D toolkit like Babylon 3D. Although I’m
a big fan of that toolkit, I’d rather show you how Silverlight is working and let you
make toolkit decisions yourself with the underlying knowledge in place.
But in acknowledgement of the popularity and, quite frankly, necessity of toolkits,
I’ve structured this example application following patterns and APIs that are as close
to the Babylon approach without adding extra work. When you finish the example,
you’ll have a basic 3D application architecture that will help you understand 3D as well
as enable you to, relatively easily, port to a 3D toolkit later if you wish. Figure 26.3
shows the architecture.
In this section, you’ll build the skeleton of the application architecture. There will
be a fair bit of code in place you don’t yet understand, but by the end of the next
chapter, all will be revealed. The code you create here will provide the minimum
amount of structure necessary to avoid a constant refactoring, as well as provide the
support objects required to render 3D content.
26.5.1 The scene
When rendering in 3D, you typically need some sort of master object that defines all the
parameters for what you want to render. It includes the angles through which the user
will see the content, a list of the content itself, and other things like lights and effects.
The object responsible for managing all of that information is typically called a
scene or stage, taking inspiration from movie and theater, respectively. Scene is generally the more common name, and more appropriate for having multiple instances, so
you’ll use that here.
A 3D system could have multiple scenes. Consider for a moment the old 2D video
games where you had a “walking around in the wildness” mode, and then a special