WPF 中的浮雕着色器效果
本文介绍如何在 WPF 中使用 ShaderEffect 实现红蓝眼镜(用于红/青眼镜)的 Anaglyph 混合效果。该效果可用于 2D 和 3D 元素。

引言
Anaglyph 图像在通过红/青眼镜观看时提供 3D 立体效果。本文展示了如何使用 WPF 着色器效果来混合左右图像,以产生 Anaglyph 的错觉。此着色器可用于任何 WPF UIElement。
Using the Code
要在您自己的项目中使用的代码,您应该添加 AnaglyphEffect
类和像素着色器 AnaglyphEffect.ps(已编译的 HLSL)。
着色器效果
Anaglyph 效果结合了两个图像源,结果是左图像源的红色通道和右图像源的绿色和蓝色通道。alpha 值取自两个图像源的最大值。它使用以下 HLSL 代码实现:
sampler2D input1 : register(S0); // right image input
sampler2D input2 : register(S1); // left image input
float4 main(float2 uv : TEXCOORD) : COLOR
{
float4 Color1;
Color1 = tex2D( input1 , uv.xy);
float4 Color2;
Color2 = tex2D( input2 , uv.xy);
Color1.r = Color2.r;
Color1.g = Color1.g;
Color1.b = Color1.b;
Color1.a = max(Color1.a,Color2.a);
return Color1;
}
请记住,使用 Codepage 1252 保存效果文件,否则 fxc 将无法编译着色器代码。在 Visual Studio 中,使用“文件 | 高级保存选项...”并选择“西欧(Windows)Codepage 1252”。
编译着色器效果
安装 Microsoft DirectX SDK,并将 fxc 编译器添加到路径 C:/Program Files/Microsoft DirectX SDK (June 2008)/Utilities/bin/x86 或类似路径。
可以使用以下命令编译着色器效果:
fxc /T ps_2_0 /E main /Fo AnaglyphEffect.ps AnaglyphEffect.fx
然后,将编译后的像素着色器(.ps)作为资源添加到项目中。
也可以使用着色器效果构建任务将着色器效果添加到您的构建脚本中,请参阅 WPF Futures 了解如何执行此操作。
着色器效果类
AnaglyphEffect
类包含 LeftInput
和 RightInput
的依赖属性。这些 Brush
输入用于定义 Anaglyph 混合着色器的输入源。
由于在此所有演示中,该效果都应用于右图像源元素,因此我没有使用 RightInput
属性。
效果的首次测试
首先,我们用一些简单的线条和文本视觉效果来测试着色器。
如果您戴上 Anaglyph 眼镜并闭上左眼,您应该只能看到“RIGHT”和一个对角线。右眼反之亦然。仅使用左眼时看到“RIGHT”文本的微弱“鬼影”是正常的。

源代码可在 TestWindow.xaml 中找到。
<Grid>
<Canvas Name="LeftCanvas" Background="White">
<Polyline Points="20,20 320,320" Stroke="Black" StrokeThickness="16"/>
<TextBlock Text="Left" Foreground="Black" Canvas.Left="20" Canvas.Top="140"
FontSize="40" FontWeight="Bold"/>
</Canvas>
<Canvas Name="RightCanvas" Background="White">
<Canvas.Effect>
<cc:AnaglyphEffect x:Name="Effect2">
<cc:AnaglyphEffect.LeftInput>
<VisualBrush Visual="{Binding ElementName=LeftCanvas}"/>
</cc:AnaglyphEffect.LeftInput>
</cc:AnaglyphEffect>
</Canvas.Effect>
<Polyline Points="20,320 320,20" Stroke="Black" StrokeThickness="16"/>
<TextBlock Text="Right" Foreground="Black" Canvas.Left="220" Canvas.Top="140"
FontSize="40" FontWeight="Bold"/>
</Canvas>
</Grid>
Anaglyph 立体图像
下一个测试是将两个图像混合以产生 Anaglyph 图像。这非常简单,我们只需将第一个示例中的 Polyline
/TextBlock
元素替换为 Image
元素。我还添加了鼠标捕获功能,以便能够移动顶层图像。结果如下所示:

这些图像是使用三脚架和 滑轨 拍摄的。相机在两张图像之间移动的距离称为立体基线。它取决于最近物体的距离和焦距。有关计算立体基线的信息,请参阅下面的部分。对于移动的拍摄对象,有必要将两个相机安装在滑轨上并精确同步快门。
XAML 源代码如下:
<Grid>
<Canvas Name="LeftCanvas" Background="White" Width="1000" Height="500">
<Image Source="images/left.jpg" Width="800"
Height="450" Canvas.Left="20" Canvas.Top="20" />
</Canvas>
<Canvas Background="White" Width="1000" Height="500">
<Image Name="RightImage" Width="800" Height="450" Canvas.Left="20" Canvas.Top="20"
Source="images/right.jpg"
MouseDown="Image_MouseDown" MouseMove="Image_MouseMove" MouseUp="Image_MouseUp"/>
<Canvas.Effect>
<me:AnaglyphEffect x:Name="Effect1">
<me:AnaglyphEffect.LeftInput>
<VisualBrush Visual="{Binding ElementName=LeftCanvas}"/>
</me:AnaglyphEffect.LeftInput>
</me:AnaglyphEffect>
</Canvas.Effect>
</Canvas>
</Grid>
计算立体基线
立体基线可以使用 Bercovitz 公式计算:
B = P * (L*N/(L-N)) * (1/F - (L+N) / (2*L*N))
其中
B = stereo base (distance between the camera optical axes)
P = parallax aimed for, in mm on the film
L = largest distance from the camera lens
N = nearest distance from the camera lens
F = focal length of the lens
如果您使用的是 35mm 格式,您应该追求大约 1.2mm 的视差,这是一个约 1/30 的比例。
当最近距离为 2m,最远距离为 10m,焦距为 50mm 时,立体基线应为 57mm。
Anaglyph 3D 渲染
现在我们将把该效果应用于 Viewport3D
元素:

此窗口包含两个 Viewport3D
元素,一个用于左相机,一个用于右相机。
我给立方体添加了一个纹理,这提高了深度感知。
左视图的 Camera
位于 [-0.15 5 4],指向原点。
<Grid Name="LeftView" Background="White">
<Viewport3D >
<ModelVisual3D>
...
</ModelVisual3D>
<Viewport3D.Camera>
<PerspectiveCamera x:Name="LeftCamera"
Position="-0.15 5 4"
LookDirection="0 -5 -4"
UpDirection="0 0 1"
FieldOfView="75"
NearPlaneDistance="0.15"/>
</Viewport3D.Camera>
</Viewport3D>
</Grid>
右视图的 Camera
位于 [0.15 5 4]。Viewport3D
包裹在一个 Grid
元素中,我们将 Anaglyph 效果添加到 Grid.Effect
属性。另请注意,为了使其正常工作,我将背景色设置为白色。
<Grid Background="White">
<Grid.Effect>
<me:AnaglyphEffect x:Name="Effect1">
<me:AnaglyphEffect.LeftInput>
<VisualBrush Visual="{Binding ElementName=LeftView}"/>
</me:AnaglyphEffect.LeftInput>
</me:AnaglyphEffect>
</Grid.Effect>
<Viewport3D Name="RightView">
<ModelVisual3D>
...
</ModelVisual3D>
<Viewport3D.Camera>
<PerspectiveCamera x:Name="RightCamera"
Position="0.15 5 4"
LookDirection="0 -5 -4"
UpDirection="0 0 1"
FieldOfView="75"
NearPlaneDistance="0.15"/>
</Viewport3D.Camera>
</Viewport3D>
</Grid>
Anaglyph 3D 渲染
该解决方案还包含一个名为 TransparentCube3D
的项目,该项目可在透明窗口上以 Anaglyph 模式渲染立方体。
幻影图
幻影图(Phantograms)从 2D 图像中产生光学 3D 错觉。当观察者位于正确的位置时,图像看起来会从表面弹出。这些图像应以 45 度角观看以获得最佳效果。

可以使用 Anaglyph 着色器效果和以下 XAML 代码创建此视图:
<Grid>
<Image Name="LeftImage" Source="phantogram/iitala_left.jpg" />
<Image Name="RightImage" Source="phantogram/iitala_right.jpg">
<Image.Effect>
<cc:AnaglyphEffect x:Name="Effect2">
<cc:AnaglyphEffect.LeftInput>
<VisualBrush Visual="{Binding ElementName=LeftImage}"/>
</cc:AnaglyphEffect.LeftInput>
</cc:AnaglyphEffect>
</Image.Effect>
</Image>
</Grid>
(花瓶由 Alvar Aalto 设计。)
如何制作幻影图
- 将您想要的对象放在一张纸上,标记纸的角落。
- 将相机放在三脚架上,向下指向纸张 45 度。
- 拍摄一张间隔几厘米(“立体基线”)的左右照片(如果有滑轨请使用)。
- 裁剪并校正纸张边缘的透视(在 Photoshop 中,使用“透视”选项勾选的裁剪工具)。
- 以未约束的比例调整大小至纸张的原始尺寸。
- 如果以不同的比例显示幻影图,应相应调整“立体基线”。
Anaglyph 网络摄像头
为了显示来自网络摄像头的视频,我使用了出色的 WpfCap WPF 网络摄像头控件。使用两个 CapPlayer
控件,并在最后一个上添加 Anaglyph 效果即可获得混合的 Anaglyph 视频。
<webcam:CapPlayer Grid.Row="2" Grid.ColumnSpan="2"
x:Name="webcamPlayer1" Height="480"
Device="{Binding RelativeSource= {RelativeSource AncestorType=
{x:Type local:MainWindow}}, Path=SelectedWebcam1}">
</webcam:CapPlayer>
<webcam:CapPlayer Grid.Row="2" Grid.ColumnSpan="2" x:Name="webcamPlayer2" Height="480"
Device="{Binding RelativeSource={RelativeSource AncestorType=
{x:Type local:MainWindow}}, Path=SelectedWebcam2}">
<webcam:CapPlayer.Effect>
<me:AnaglyphEffect x:Name="Effect1">
<me:AnaglyphEffect.LeftInput>
<VisualBrush Visual="{Binding ElementName=webcamPlayer1}"/>
</me:AnaglyphEffect.LeftInput>
</me:AnaglyphEffect>
</webcam:CapPlayer.Effect>
<webcam:CapPlayer.LayoutTransform>
<ScaleTransform ScaleX="1" ScaleY="-1"/>
</webcam:CapPlayer.LayoutTransform>
</webcam:CapPlayer>
出于某种原因,网络摄像头控件会垂直翻转视频。我在 LayoutTransform
中添加了一个 ScaleTransform
来修复此问题,但我希望看到更好的解决方案。
混合后的网络摄像头视频如下所示:

我使用了两个廉价的网络摄像头放在屏幕顶部,相机的对齐还可以改进。

参考/灵感
- Charles Petzold 的 空间站
- Barcinski 和 Jean-Jean 的 使用 Papervision3D 制作 Anaglyphs
- Greg Schecter 的 多输入效果
- 维基百科 Anaglyph 图像
- 维基百科 幻影图
- John Wattie 的 Bercovitz 公式
历史
- 2009 年 1 月 18 日 - 首次发布
- 2009 年 3 月 19 日 - 添加了 Anaglyph 网络摄像头代码
- 2009 年 11 月 8 日 - 向着色器效果添加了 alpha,给 3D 立方体添加了纹理,增加了关于立体基线的部分,演示了透明窗口