Introduction
This article shows how to build and display stereoscopic images in Windows Presentation Foundation (WPF). The WPF already has the ability to render a 3D image – we will just add a little depth.
Background
3D in the WPF
The WPF has the capability to display 3D models in perspective – the way as humans or cameras see it. A 3D model can be described in the WPF by geometries (often presented as a mesh of triangles) and light sources. The model can be included in WPF windows/documents via a Viewport3D
control. The XAML document normally will contain the following code:
<Viewport3D>
<Viewport3D.Camera>
<PerspectiveCamera ... />
</Viewport3D.Camera>
<ModelVisual3D>
...
</ModelVisual3D>
</Viewport3D>
The Viewport3D
control requires camera description to render the referred 3D model. We will use the PerspectiveCamera
to describe how the model will be viewed.
Figure: Example of camera and model items
Stereoscopic Image
Human can perceive depth using two eyes; the brain makes a judgment about how far objects are based on images received from both eyes. Showing the same image to both of the two eyes or using only one of the eyes forces the brain to guess the objects' depth.
To make a stereoscopic image, you have to have two images: one for each eye. The image taken for the left eye has to be shown to the left eye and the image taken for the right eye has to be shown to the right eye. Two cameras are used to create a stereoscopic pair of images. Usually, the cameras are positioned side-by-side horizontally; the distance between the cameras' lenses has to be the same as the distance between the eyes of a human.
Figure: Cameras positioned for taking stereoscopic image
There are several ways to display two different images for the different eyes. We will be interested only in three of them: parallel viewing, cross viewing and anaglyph. The first two are simple to create, but not easy to view. In parallel viewing, two images, for left and right eyes, are printed side-by-side, and human eyes are trying to focus “behind” the image to let the eyes see their own images. One disadvantage is that the images have to be small – the width of the image has to be not more than the distance between the eyes. In cross viewing, two images are swapped and printed side-by-side: first for the right eye then for the left eye; and human eyes are trying to focus “in front of” the image to let the eyes see their own images.
Figure: Cross and parallel viewing of a stereoscopic pair of images
To view a typical anaglyph, you need to have special glasses: the one side is made of red glass, the other side is made of cyan (blue) glass. The red glass helps an eye to see only red (channel) colors, and the cyan glass helps to see all other colors except red. An anaglyph can be built by replacing the red channel colors of the first image (taken for the right eye) by the red channel colors from the second image (taken for the left eye).
Pixel Shader
The .NET Framework 3.5 SP1 introduced custom bitmap effects that can be based on Pixel Shader technology. The Pixel Shader lets developer create code using High Level Shading Language (HLSL) that can be executed by a graphics processing unit (GPU) during image rendering to add some special effect.
The ShaderEffect
class of the WPF library helps to encapsulate interface with low level Pixel Shader object and code. The SharedEffect
inherited class has to create PixelShader
object and provide compiled effect code that performs actual calculations. The Pixel Shader code will accept one or more bitmap images and/or additional parameters to produce the output.
Using the Code
The code contains the AnaglyphEffects
library and a sample 3D WPF application. The AnaglyphEffects
library contains RedCyanEffect
class that is inherited from ShaderEffect
class. The source code for Pixel Shader looks like this:
sampler2D input : register(s0);
sampler2D otherEye : register(s1);
float4 main(float2 uv : TEXCOORD) : COLOR
{
float4 clr1;
clr1= tex2D(otherEye, uv.xy);
float4 Color;
Color= tex2D(input, uv.xy);
Color.r=clr1.r;
return Color;
}
The code accepts two images as inputs. During its execution, it replaces the red color in the input image with red layer of the other (tex1
) image. It allows to build an anaglyph image from two stereoscopic pair of images.
Figure: How anaglyph is built by the RedCyanEffect
The sample 3D WPF Application contains a 3D model of a house, that is viewed from the perspective of two cameras: left and right. The output from the two cameras is merged using custom anaglyph bitmap effect (RedCyanEffect
class) from the AnaglyphEffects
library.
<Rectangle Name="anaglyph">
<Rectangle.Fill>
<VisualBrush Visual="{Binding ElementName=rightViewport}" Stretch="Uniform" />
</Rectangle.Fill>
<Rectangle.Effect>
<ae:RedCyanEffect>
<ae:RedCyanEffect.OtherEye>
<VisualBrush Visual="{Binding ElementName=leftViewport}"
Stretch="Uniform" />
</ae:RedCyanEffect.OtherEye>
</ae:RedCyanEffect>
</Rectangle.Effect>
</Rectangle>
Figure: Sample application screenshot
Points of Interest
Amazingly, the WPF lets you manipulate with the output at any level: before the control is drawn by styling it, after the control is drawn by applying the bitmap effect, or just retrieve the control image to reuse it in other parts of an application. VisualBrush
element will let you get control image, transform it, and use the images as a Brush
, e.g. it can be used to create reflection.
Creation of the custom bitmap effects is simplified and does not require special knowledge of how to optimize it to run it on different processors – the framework will take care of it. The Pixel Shader code has to be complied in an intermediate code using fxc
command line tool (the part of the DirectX SDK) and be attached as embedded resource to the assembly. The compiled code is used as parameter during ShaderEffect
initialization.
The implemented RedCyanEffect
can be applied to any stereoscopic pair of images, e.g. build an anaglyph viewer for JPEG (JPS) stereo images.
References
- "A Series on GPU-based Effects for WPF", Greg Schechter
- "http://en.wikipedia.org/wiki/Stereoscopy", Wikipedia
History
- 15th April, 2009: Initial post