【翻译】Managed DirectX(第十章)

** 第十章 使用助手类( ** ** Using the Helper Classes ** ** ) **


** 翻译:clayman
[email protected]
仅供个人学习之用,勿用于任何商业用途,转载请注明作者^_^ **


**绘制直线

**

在第四章里我们就讨论过关于绘制直线的问题:使用基本图元里的 line list 或 line strip 绘制直线。但是这两种直线都不能改变宽度,也没有抗锯齿功能(除非整个场景都使用了抗锯齿)。

对于不同类型的应用程序来说,绘制直线可能是最普通常见的操作,也可能根本不需要绘制他们。无论如何,有一个方便的 Line 类能在任何时候满足我们的需要。为了展现绘制线条是多么方便,我们将快速写一个程序来随即绘制一些线条。

创建一个新工程,为编写 Direct3D 程序做好准备。不需要再次重复这些简单的操作了吧。

public void InitializeGraphics() (略)

protected override void OnPaint(PaintEventArgs e)

{

device.Clear(ClearFlags.Target,Color.Black, 1.0f ,0);

device.BeginScene();

//Draw some lines

DrawRandomLines();

device.EndScene();

device.Present();

System.Threading.Thread.Sleep(500);

this.Invalidate();

}

这里没有什么新内容。只是在最后我们让线程休眠一小段时间,这样可以看清我们所绘制的线,接下来再次开始循环。显然,还没有定义 DrawRandomLines 方法,添加代码:

private void DrawRandomLines()

{

Random r = new Random();

int numberLines = r.Next(50);

using(Line l = new Line(device))

{

for(int i=0; i

  1<numberlines; 100);="" 2)="" <="" c="Color.FromArgb(r.Next(byte.MaxValue),r.Next(byte.MaxValue),r.Next(byte.MaxValue));" color="" for(int="" i++)="" inner="" inner++)="" int="" l.antialias="r.Next(50)" l.width="width;" numvectors="r.Next(4);" vecs="new" vecs.length;="" vecs[inner]="new" vector2(r.next(this.width),r.next(this.height));="" vector2[]="" vector2[numvectors];="" while(numvectors="" while(width="0)" width="r.Next(this.Width" {=""> 25 ? true : false; 
  2
  3l.Begin(); 
  4
  5l.Draw(vecs,c); 
  6
  7l.End(); 
  8
  9} 
 10
 11} 
 12
 13} 
 14
 15每次调用这个方法的时候,都先创建一个随机数作为所要绘制  线条  的数量。创建一个  line  对象来分别绘制每一根  线条  。可以为每一条线都创建一个  line  对象,可一个创建一个“全局”的  line  对象,当然前者让代码更容易看懂。 
 16
 17接下来,随机选择这条  线条  中的点。必须保证最少有  2  个点。在决定了线条中将有几个点之后,根据当先窗口的高度和宽度产生随机数,作为线条中线段的终点和起点。我们还随机选择了线条的宽度以及颜色。当然,线条的宽度也是基于窗口宽度生成的。同样,是否抗锯齿也是随机选择的。可以看到没有抗锯齿的线条(特别是很宽的那种)呈明显锯齿状。最后,绘制直线。  Draw  方法前后的  begin  和  end  方法让  Direct3D  知道所绘制的是直线。 
 18
 19![](http://dev.csdn.net/images/blog_csdn_net/soilwork/line.jpg)
 20
 21虽然这里没有提到,但还有一些其他属性可以用来控制如何绘制直线。一个名为  GlLines  的布尔变量可以用来选择时候绘制  OpenGl  风格的线条(默认值为  false  )。还可以使用  DrawTransform  方法在三维空间里绘制。 
 22
 23**绘制文本
 24
 25**
 26
 27同样,绘制文本也是前面讨论过的内容。但只学了一点点而已,这次我们将会讨论的深入一些。在前面几章里,我们知道  Microsoft.DirectX.Direct3D  名称空间和  System.Drawing  名称空间下都有一个  Font  类。使用如下的语句来帮助区别他们: 
 28
 29using Direct3D = Microsoft.DirectX.Direct3D; 
 30
 31这样可以把整个名称空间缩写为  Direct3D  。创建新工程,添加如下变量: 
 32
 33这里我们声明了将要在屏幕表面绘制的字体,以及一个  mesh  和相应的材质对象。  Mesh  将作为一个拉伸的三维文本模型。  Angle  参数用于控制  3  维文本的旋转。现在初始化图形: 
 34
 35public void InitializeGraphics() 
 36
 37{ 
 38
 39PresentParameters presentParams = new PresentParameters (); 
 40
 41presentParams.Windowed = true; 
 42
 43presentParams.SwapEffect = SwapEffect.Discard; 
 44
 45presentParams.AutoDepthStencilFormat = DepthFormat.D16; 
 46
 47presentParams.EnableAutoDepthStencil = true; 
 48
 49device = new Device(0,DeviceType.Hardware,this,CreateFlags.HardwareVertexProcessing,presentParams); 
 50
 51device.DeviceReset +=new EventHandler(this.OnDeviceReset); 
 52
 53OnDeviceReset(device,null); 
 54
 55System.Drawing.Font localFont = new System.Drawing.Font("Arial",  14.0f  ,FontStyle.Italic); 
 56
 57mesh = Mesh.TextFromFont(device,localFont,"Managed DirectX",  0.001f  ,  0.4f  ); 
 58
 59meshMaterial = new Material(); 
 60
 61meshMaterial.Diffuse = Color.Peru; 
 62
 63font = new Microsoft.DirectX.Direct3D.Font(device,localFont); 
 64
 65} 
 66
 67我们创建了一个拥有深度缓冲的设备,并为他订阅了  DeviceReset  事件。因为每次重置设备时,只需要设置灯光和摄像机,所以我们把它放到单独的事件处理程序中。最后,创建了  System.Drawing.Font  对象作为  2  维和  3  维文本的基础。我们选择了  14  个像素大小的  Arial  字体。首先使用字体对象拉伸出了三维字体的  mesh  。我们使用了字符串“  Managed DirectX  ”来拉伸。当然你可以使用其它任何喜欢的字符串。接下来,设置了材质的颜色,创建  2  维字体。 
 68
 69在  OnDeviceReset  方法中设置摄像机以及灯光: 
 70
 71private void OnDeviceReset(object sender, EventArgs e) 
 72
 73{ 
 74
 75Device dev = (Device)sender; 
 76
 77dev.Transform.Projection = Matrix.PerspectiveFovLH((float)Math.PI/4, this.Width/this.Height,  1.0f  ,  100.0f  ); 
 78
 79dev.Transform.View = Matrix.LookAtLH(new Vector3(0,0,  9.0f  ),new Vector3(),new Vector3(0,1,0)); 
 80
 81dev.Lights[0].Type = LightType.Directional; 
 82
 83dev.Lights[0].Diffuse = Color.White; 
 84
 85dev.Lights[0].Direction = new Vector3(0,0,1); 
 86
 87dev.Lights[0].Update(); 
 88
 89dev.Lights[0].Enabled = true; 
 90
 91} 
 92
 93摄像机和灯光都是为了拉伸的三维字体才创建的。二维的字体已经经过变换而起是照亮了的。但是,拉伸的三维字体是真实的模型,所以需要设置灯光和摄像机。添加绘制三维字体的方法: 
 94
 95private void Draw3DText(Vector3 axis, Vector3 location) 
 96
 97{ 
 98
 99device.Transform.World = Matrix.RotationAxis(axis,angle) * Matrix.Translation(location); 
100
101device.Material = meshMaterial; 
102
103mesh.DrawSubset(0); 
104
105angle +=  0.01f  ; 
106
107} 
108
109如你所见,我们传入  mesh  在世界坐标中的位置,以及旋转轴。这个方法和之前的  DrawMeshff  是很相似的:设置材质,绘制第一个子集。我们还增加了旋转角度,这样做的结果是动画将基于帧速率。接下来添加绘制  2  为字体的代码: 
110
111private void Draw2DText(string text,int x,int y,Color c) 
112
113{ 
114
115font.DrawText(null,text,new Rectangle(x,y,this.Width,this.Heightk),DrawTextFormat.NoClip | DrawTextFormat.ExpandTabs| DrawTextFormat.WordBreak, c); 
116
117} 
118
119这里的代码也很简单吧。你可能注意到了我们把使用窗口宽度和高度创建的矩形作为参数。这样做的原因是使用了  WordBreak  标志,可以在文本超出了绑定的矩形范围之后自动换行。我们也希望文本中的制表符被正确的拉伸,同时,字体不会被裁减了。 
120
121有了这两个主要的绘制字体的方法,在  OnPaint  中添加代码: 
122
123protected override void OnPaint(PaintEventArgs e) 
124
125{ 
126
127device.Clear(ClearFlags.Target,Color.Black,  1.0f  ,0); 
128
129device.BeginScene(); 
130
131Draw2DText("Here's some text",10,10,Color.WhiteSmoke); 
132
133Draw2DText("Here's some text\t\nwith\r\nhard\r\nline breaks",100,80,Color.Violet); 
134
135Draw2DText("This\tis\tsome\ttext\twith\ttabs.",this.Width/2,this.Height - 80,Color.RoyalBlue); 
136
137Draw2DText("If you type enough words in a single sentecne you may notice that tha text begins to warp."+ 
138
139"Try resizing the window to notice how the text changes as you size it.",this.Width/2+this.Width/4,this.Height/4,Color.Yellow); 
140
141Draw3DText(new Vector3(  1.0f  ,  1.0f  ,  0.0f  ), new Vector3(  -3.0f  ,  0.0f  ,  0.0f  )); 
142
143Draw3DText(new Vector3(  0.0f  ,  1.0f  ,  1.0f  ), new Vector3(  0.0f  ,  -1.0f  ,  1.0f  )); 
144
145device.EndScene(); 
146
147device.Present(); 
148
149this.Invalidate(); 
150
151} 
152
153这里我们绘制了几种不同的字符串:包含换函符和回车符的,包含制表符的,以及长句。对于长句,我们希望他会正确的换行。(注:调试程序的时候,长句的换行总是不正确,包括作者的源码显示也不正确,书上的结图却是正确的,郁闷了  -_-#  ) 
154
155![](http://dev.csdn.net/images/blog_csdn_net/soilwork/text.jpg)
156
157特别提示:提高字体性能 
158
159Font  用于绘制文本的字体是基于纹理的。把这些字体绘制为纹理是通过  GDI  来完成的,相当缓慢。最好在开始时使用  font  类的预载方法,保证不会再运行时遇到几次这样的加载。可以调用  PreloadCharacters  方法来加载指定的字体,或者使用  PreloadText  方法加载指定的字符串。 
160
161** Rendering to Surfaces 
162
163**
164
165你是否玩过那种可以打开一个倒视镜的赛车游戏?或者可以在屏幕表面显示当前赛道的赛车游戏。这些效果都是通过把同一个场景(通常使用不同的摄像机)渲染为一个纹理来实现的。事实上,这虽然听起来很复杂,却相当容易实现。再从第五章的例子开始。 
166
167首先自然先声明将用来渲染的纹理,添加代码: 
168
169private Texture renderTexture = null; 
170
171private Surface renderSurface = null; 
172
173private RenderToSurface rts = null; 
174
175private const int RenderSurfaceSize = 128; 
176
177这里声明了用于渲染的纹理,实际所要渲染的表面,以及用于绘制表面的助手对象。我们同时还声明了将要创建的纹理大小。在  InitializeGraphics  方法中,订阅  device reste  事件来创建纹理以及表面。添加代码: 
178
179device = new Device(0, DeviceType.Hardware, this, CreateFlags.SoftwareVertexProcessing, presentParams); 
180
181device.DeviceReset +=new EventHandler(OnDeviceReset); 
182
183this.OnDeviceReset();</numberlines;>
Published At
Categories with Web编程
Tagged with
comments powered by Disqus