原文:WPF: Creation of Text Labels for 3D Scene

转载:http://www.codeproject.com/KB/WPF/WPF_Text3D.aspx

Download demo - 26.46 KBDownload source - 37.19 KB

Introduction

Some time ago, I started working on my application that takes the advantages of 3D graphics to display some results. I am .NET developer so I have chosen Windows Presentation Foundation (WPF) as my framework to create 3D graphics. I have to admit that this
is powerful library that allows to create 3D graphics quite easy. It is not hard to understand how to create a 3D scene using sets of triangles. What is more, many articles describe how to do it. I encountered some problems when I started adding some text
to my 3D scene. I think that this article may help other developers to choose the best way of writing text on a 3D scene.

Background

Let's start, our goal is to add some text labels to the 3D scene. We have at least two approaches:

  • 3D label creation
  • 2D label creation and overlay it on the scene

The first approach is based on the creation of a 3D object in the scene (typically it is a rectangle) and writing text on it (it can be achieved using the brush made from text). The sample result is showed in the picture below:

The second approach is based on layers. The bottom layer is Viweport3D and the top layer isCanvas. The top layer is transparent and overlays the
Viewport3D. This approach usesTexBlocks that are members of the
Canvas but the 2D location depends on the location in 3D space. The picture below presents that approach:

Of course, both approaches have some advantages and disadvantages. The 3D labels based on the first approach are more useful when the text is a part of the scene. The labels based on the second approach are more readable (but the solution is more complex).
The picture below shows both approaches:

Using the Code

The sample code draws the coordinate system and some labels on it. The scene could be rotated using the sliders.Checkboxes from the left pane allow to switch on / switch off the visibility of elements of the scene (especially labels 2D and 3D).
I do not want to go into detail as to how this application is created. I want to focus on the two approaches mentioned previously.

3D Labels

To illustrate how to create a label in 3D, I am going to describe how to create the function:CreateTextLabel3D. This function is going to be responsible for returning theModelVisual3D object that represents the label in 3D space.
Such a function has the following declaration:

Collapse |Copy
Code
/// <summary>
/// Creates a ModelVisual3D containing a text label.
/// </summary>
/// <param name="text">The string to be drawn</param>
/// <param name="textColor">The color of the text.</param>
/// <param name="isDoubleSided">Visible from both sides?</param>
/// <param name="height">Height of the characters</param>
/// <param name="basePoint">The base point of the label</param>
/// <param name="isBasePointCenterPoint">if set to <c>true</c> the base point
/// is center point of the label.</param>
/// <param name="vectorOver">Horizontal direction of the label</param>
/// <param name="vectorUp">Vertical direction of the label</param>
/// <returns>Suitable for adding to your Viewport3D</returns>
/// <remarks>Two vectors: vectorOver and vectorUp are creating the surface
/// on which we are drawing the text. Both those vectors are used for
/// calculation of label size, so it is reasonable that each co-ordinate
/// should be 0 or 1. e.g. [1,1,0] or [1,0,1], etc...</remarks>
public static ModelVisual3D CreateTextLabel3D( string text, Brush textColor,
bool isDoubleSided, double
height, Point3D basePoint, bool isBasePointCenterPoint, Vector3D vectorOver,
Vector3D vectorUp);

How It Works?

  1. First we need to create a TextBlock that allows us to create a label:

    Collapse |Copy
    Code
    TextBlock textblock = new TextBlock(new Run(text));
    textblock.Foreground = textColor; // setting the text color
    textblock.FontFamily = new FontFamily("Arial"); // setting the font to be used
  2. Now we have to create a brush and material that contains the TextBlock created in the previous step:
    Collapse |Copy
    Code
    DiffuseMaterial mataterialWithLabel = new DiffuseMaterial();
    // Allows the application of a 2-D brush,
    // like a SolidColorBrush or TileBrush, to a diffusely-lit 3-D model.
    // we are creating the brush from the TextBlock
    mataterialWithLabel.Brush = new VisualBrush(textblock);
  3. Now it is time to create the object in the 3D space that will be covered by the text:
    Collapse |Copy
    Code
    //calculation of text width (assuming that characters are square):
    double width = text.Length * height;
    // we need to find the four corners
    // p0: the lower left corner; p1: the upper left
    // p2: the lower right; p3: the upper right
    Point3D p0 = basePoint;
    // when the base point is the center point we have to set it up in different way
    if(isBasePointCenterPoint)
    p0 = basePoint - width / 2 * vectorOver - height / 2 * vectorUp;
    Point3D p1 = p0 + vectorUp * 1 * height;
    Point3D p2 = p0 + vectorOver * width;
    Point3D p3 = p0 + vectorUp * 1 * height + vectorOver * width;
    // we are going to create object in 3D now:
    // this object will be painted using the (text) brush created before
    // the object is rectangle made of two triangles (on each side).
    MeshGeometry3D mg_RestangleIn3D = new MeshGeometry3D();
    mg_RestangleIn3D.Positions = new Point3DCollection();
    mg_RestangleIn3D.Positions.Add(p0); // 0
    mg_RestangleIn3D.Positions.Add(p1); // 1
    mg_RestangleIn3D.Positions.Add(p2); // 2
    mg_RestangleIn3D.Positions.Add(p3); // 3
    // when we want to see the text on both sides:
    if (isDoubleSided)
    {
    mg_RestangleIn3D.Positions.Add(p0); // 4
    mg_RestangleIn3D.Positions.Add(p1); // 5
    mg_RestangleIn3D.Positions.Add(p2); // 6
    mg_RestangleIn3D.Positions.Add(p3); // 7
    }
    mg_RestangleIn3D.TriangleIndices.Add(0);
    mg_RestangleIn3D.TriangleIndices.Add(3);
    mg_RestangleIn3D.TriangleIndices.Add(1);
    mg_RestangleIn3D.TriangleIndices.Add(0);
    mg_RestangleIn3D.TriangleIndices.Add(2);
    mg_RestangleIn3D.TriangleIndices.Add(3);
    // when we want to see the text on both sides:
    if (isDoubleSided)
    {
    mg_RestangleIn3D.TriangleIndices.Add(4);
    mg_RestangleIn3D.TriangleIndices.Add(5);
    mg_RestangleIn3D.TriangleIndices.Add(7);
    mg_RestangleIn3D.TriangleIndices.Add(4);
    mg_RestangleIn3D.TriangleIndices.Add(7);
    mg_RestangleIn3D.TriangleIndices.Add(6);
    }
  4. We are covering the object created in the previous step using the material created at the beginning:
    Collapse |Copy
    Code
    // texture coordinates must be set to
    // stretch the TextBox brush to cover
    // the full side of the 3D label.
    mg_RestangleIn3D.TextureCoordinates.Add(new Point(0, 1));
    mg_RestangleIn3D.TextureCoordinates.Add(new Point(0, 0));
    mg_RestangleIn3D.TextureCoordinates.Add(new Point(1, 1));
    mg_RestangleIn3D.TextureCoordinates.Add(new Point(1, 0));
    // when the label is double sided:
    if (isDoubleSided)
    {
    mg_RestangleIn3D.TextureCoordinates.Add(new Point(1, 1));
    mg_RestangleIn3D.TextureCoordinates.Add(new Point(1, 0));
    mg_RestangleIn3D.TextureCoordinates.Add(new Point(0, 1));
    mg_RestangleIn3D.TextureCoordinates.Add(new Point(0, 0));
    }
  5. Now it is time to create ModelVisual3D that we want to return:
    Collapse |Copy
    Code
    ModelVisual3D result = new ModelVisual3D();
    // we are setting the content:
    // our 3D rectangle object covered with material that is made of label
    // (TextBox with text)
    result.Content = new GeometryModel3D(mg_RestangleIn3D, mataterialWithLabel); ;
    return result;

2D Labels

This solution is much more complicated and it is hard to prepare one function that deals with all tasks.

First of all, we have to create two layers:

  • Viewport3D as the bottom layer
  • Canvas as the top layer

The XAML code below presents a sample of two overlaid layers:

Collapse |Copy
Code
<Grid ClipToBounds="True">
<Viewport3D Name="mainViewport" ClipToBounds="True" Grid.Column="0" Grid.Row="0">
<Viewport3D.Camera>
<PerspectiveCamera FarPlaneDistance="100" LookDirection="-11,-10,-9" UpDirection="0,1,0"
NearPlaneDistance="1" Position="11,10,9" FieldOfView="70" />
</Viewport3D.Camera> <ModelVisual3D> <ModelVisual3D.Content>
<DirectionalLight Color="White" Direction="-2,-3,-1" />
</ModelVisual3D.Content>
</ModelVisual3D>
</Viewport3D> <Canvas Name="mainViewportCanvas" ClipToBounds="True"
Grid.Column="0" Grid.Row="0">
</Canvas>
</Grid>

The second task is localization of the point from 3D space on our overlaid canvas. To transform the location, we can use the following function:

Collapse |Copy
Code
public static Point Get2DPoint(Point3D p3d, Viewport3D vp)
{
bool TransformationResultOK;
Viewport3DVisual vp3Dv = VisualTreeHelper.GetParent(
vp.Children[0]) as Viewport3DVisual;
Matrix3D m = MathUtils.TryWorldToViewportTransform
(vp3Dv, out TransformationResultOK);
if (!TransformationResultOK) return new Point(0, 0);
Point3D pb = m.Transform(p3d);
Point p2d = new Point(pb.X, pb.Y);
return p2d;
}

Note that this function uses function TryWorldToViewportTransform from 3D Tools package (availablehere):
_3DTools.MathUtils.TryWorldToViewportTransform.

The next task is creation and localization of the TextBlock:

Collapse |Copy
Code
UIElement IModelVisual3D.GetUIElement
(ModelVisual3DFilter FilterSettings, Viewport3D DestinationViewport3D)
{
if (FilterSettings.Texts2D)
{
TextBlock tb = new TextBlock();
tb.Text = Description;
Point p2d = Panel3DMath.Get2DPoint(this.Point3D, DestinationViewport3D);
Canvas.SetTop(tb, p2d.Y);
Canvas.SetLeft(tb, p2d.X);
return tb;
}
else
return new UIElement();
}

The last thing is adding the TextBlock (created before) to our canvas.

Collapse |Copy
Code
this.mainViewportCanvas.Children.Add(element.GetUIElement(filter,mainViewport)); 

Summary

I hope that my two approaches that could be used during creation of text labels for 3D scenes might be interesting for the reader. The Polish version of this article will be available soon onmy blog.

 

WPF: Creation of Text Labels for 3D Scene的更多相关文章

  1. WPF换肤之八:创建3D浏览效果

    原文:WPF换肤之八:创建3D浏览效果 上节中,我们展示了WPF中的异步以及界面线程交互的方式,使得应用程序的显示更加的流畅.这节我们主要讲解如何设计一个具有3D浏览效果的天气信息浏览器. 效果显示 ...

  2. 在WPF中使用PlaneProjection模拟动态3D效果

    原文:在WPF中使用PlaneProjection模拟动态3D效果 虽然在WPF中也集成了3D呈现的功能,在简单的3D应用中,有时候并不需要真实光影的3D场景.毕竟使用3D引擎会消耗很多资源,有时候使 ...

  3. 泡泡一分钟:Project AutoVision - Localization and 3D Scene Perception for an Autonomous Vehicle with a Multi-Camera System

    Project AutoVision - Localization and 3D Scene Perception for an Autonomous Vehicle with a Multi-Cam ...

  4. Chapter 1:Create You First 3D Scene With Three.js

    1,各浏览器对WebGL的支持 手机浏览器对WebGL的支持: 书的源码:https://github.com/josdirksen/learning-threejs 第一次用浏览器打开代码可能无法正 ...

  5. cocos2d-x-3.1 Text Labels(官方正式译文)

    介绍 cocos2d支持(true type字体)标签,和纹理地图集标签. LabelTTF 标签的优缺点: 全部 TTF 字体的长处: 随意大小,支持调整字距. 易于使用. 不须要外部编辑器. 创建 ...

  6. 优化WPF 3D性能

    Maximize WPF 3D Performance .NET Framework 4.5   As you use the Windows Presentation Foundation (WPF ...

  7. Unity3D Keynote

    [Unity3D Keynote] 1.场景文件扩展名为.unity. 2.up为Y正方向,down为Y负方向,right为X正方向,left为X负方向,forward为Z正方向,back为z负方向. ...

  8. WPF中的3D Wireframe

    原文:WPF中的3D Wireframe WPF不支持画三维线,但开发人员提供了ScreenSpaceLines3D 类用于实现这个功能.我已经在程序中实现并成功显示3D Wireframe,并能够进 ...

  9. WPF 3D 知识点大全以及实例

    引言 现在物联网概念这么火,如果监控的信息能够实时在手机的客服端中以3D形式展示给我们,那种体验大家可以发挥自己的想象. 那生活中我们还有很多地方用到这些,如上图所示的Kinect 在医疗上的应用,当 ...

随机推荐

  1. HIVE快速入门 分类: B4_HIVE 2015-06-06 11:27 59人阅读 评论(0) 收藏

    (一)简单入门 1.创建一个表 create table if not exists ljh_emp( name string, salary float, gender string) commen ...

  2. IOS8刷机之后

    用的4s,明显感觉卡- =BUG也非常多,点击设置都闪退..有些应用打不开. 不建议非开发人员尝试. 眼下发现的bug有:同一时候关闭多个应用会造成应用无法关闭. 常常重新启动.耗电没留意.

  3. C++基础学习教程(七)----类编写及类的两个特性解析---&gt;多态&amp;继承

    类引入 到眼下为止我们所写的自己定义类型都是keywordstruct,从如今起我们将採用class方式定义类,这样的方式对于学习过其它高级语言包含脚本(Such as Python)的人来说再熟悉只 ...

  4. swift学习第十五天:闭包

    闭包 闭包的介绍 闭包和OC中的block非常相似 OC中的block是匿名的函数 Swift中的闭包是一个特殊的函数 block和闭包都经常用于回调 注意:闭包和block一样,第一次使用时可能不习 ...

  5. android SQLite 使用实例

    Android作为眼下主流的移动操作系统,全然符合SQLite占用资源少的优势,故在Android平台上,集成了一个嵌入式关系型数据库-SQLite.假设想要开发 Android 应用程序,须要在 A ...

  6. pandas 学习(五)—— datetime(日期)

    date range pd.date_range('2014-11-19', '2014-11-21', freq='D') # 起始时间,终止时间,时间间隔,也即步长,D ⇒ Day,5H:以 5 ...

  7. VS2008的C++TR1库已经支持正则表达式

    作者:朱金灿 来源:http://blog.csdn.net/clever101 发现VS2008的C++ TR1库已经支持正则表达式了(注意装了VS 2008sp1采用TR1库的).下面简单做个测试 ...

  8. Windows 程序启动性能优化(先载入EXE,后载入DLL,只取有限的代码载入内存,将CPU的IP指向程序的入口点)

    一.重定位链接时重定位:目标文件一般由多个节组成,编译器在编译每个目标文件时一般都是从0地址开始生成代码.当多个代码节合成一个代码段时,需要根据其在最终代码段中的位置做出调整.同时,链接器需要对已经解 ...

  9. 小强的HTML5移动开发之路(46)——汇率计算器【2】

    在上一篇中我们完成了汇率计算页面,下面来完成汇率设置页面的显示. <div class="setRates"> <div class="header&q ...

  10. 《图说VR》——HTC Vive控制器按键事件解耦使用

    本文章由cartzhang编写,转载请注明出处. 全部权利保留. 文章链接:http://blog.csdn.net/cartzhang/article/details/53915229 作者:car ...