您的位置:首页 > 移动开发 > Objective-C

Direct3D 世界的Hello:高洛德渲染的三角形(转)

2009-05-09 20:43 323 查看
 
  Direct3D的所有操作都是在Direct3D设备上进行的。创建设备要使用Device类的构造函数。
  
  public Device ( System.Int32 adapter , Microsoft.DirectX.Direct3D.DeviceType deviceType ,
  
  System.Windows.Forms.Control renderWindow , Microsoft.DirectX.Direct3D.CreateFlags
  
  behaviorFlags, Microsoft.DirectX.Direct3D.PresentParameters presentationParameters )
  
  Direct3D设备有3种类型,Hardware, Software和Reference类型。其中Reference类型只有在DirectX SDK中的runtime才有。我们通常使用的DirectX Runtime没有这种类型的设备。
  
  创建设备的时候我们还要指定在那个显卡上进行操作,一般情况下,我们指定0,表示使用缺省的显卡。
  
  我们还要把该设备与一个窗口关联起来。
  
  CreateFlags中我们经常使用的参数,就是指定要使用硬件/软件定点处理。
  
  当我们缩放窗口的时候,拉大窗口肯定会导致窗口被重绘,但是缩小窗口并不能导致窗口被绘制。这也是合情合理的,因为缩小窗口只是导致窗口可见面积的减小,
  
  没有必要重新绘制。当然这是一般情况,在我们的例子,我们要求三角形总是在窗口中央,所以这样的表现不被接受。我们需要自己来强制重绘,所以,我们在Form1_Paint()事件中加入了this.Invalidate()函数。
  
  但是这样又引入了另外的问题,运行修改后的程序后,我们发现我们绘制的内容不见了。这是因为在Windows Form中不是所有的绘制都发生在Paint中,因为Windows XP支持透明窗口,这个透明处理是由操作系统自动完成的,这就说明在Paint事件之外会发生一些绘制操作,Windows会用缺省的信息来重绘窗口,这导致我们绘制的内容被覆盖掉。这就是为什么我们要在我们的Form类的构造函数种使用SetStyle函数了。
  
  (??为什么加入了Invalidate()后会出现这种现象?
  
  我的看法,不对的地方还请高手指教了。嘿嘿。
  
  我们是先Render,然后才调用Invalidate()的。Invalidate()触发Windows的Piant外绘制,我们的绘制被覆盖。移动的时候,反复这样清除,导致闪烁,所以
  
  就有了我们看到的现象。
  
  )
  
  this.SetStyle(ControlStyles.AllPaintingInWmPaint | ControlStyles.Opaque, true);
  
  这一语句告诉Windows 我们的窗口不是透明的,并要求所有的绘制都要发生在Paint事件中。否则,我们会发现我们绘制的内容根本看不到,当移动窗口的时候,会隐隐看到我们绘制的三角形在闪烁。大家可以试试看,在我的机器上,不是很明显。
  
  在绘制三角形的时候,我们使用了TransfomredColored的顶点格式。这种格式就如他的名字一样,是已经转换过的,对于这种格式的顶点,Direct3D不再做Lighting, View Transform,Projection Transform.我们在数据中指定的坐标值就是这些顶点要显示的地方。
  
  而且我们可以看到,绘制出来的三角形内部已经被填充了,可是我们并没有做任何类似的操作。这其实是一种渲染模式(Shading Mode, 不知道这么翻译对不对)。
  
  高洛德(Gouraud)渲染,还有Flat Shading Mode和Phong Shading Mode.
  
  (Gouraud渲染很有用的,尤其是在用多个三角形描述一个曲面/对象的时候,面对几千个上万个三角形,你不可能自己去计算每个三角形的光照吧。Gouraud可以根据每个三角形面和光源的角度自动计算这些三角形顶点的具体颜色和亮度,然后用差值平滑处理三角形内部的点,这样就可以形成真实的物体了。具体来说是这样,
  
  每个三角形是是一个平面,有自己的法向矢量,光线也有自己的矢量,根据这个三角形面和光线的角度可以计算出这个三角形的顶点的具体光照情况,然后根据材料特性就可以计算出这些顶点的具体颜色、亮度等。然后对于三角形内部的其他点,可以用差值平滑处理,计算出这些点的颜色和亮度信息。具体怎么算,我也不知道,呵呵,
  
  我不是搞算法的,不关心这个。
  
  如果你把顶点数据的格式改成 PositonColored,你会发现,又看不到我们绘制的东西了,呵呵,我们下面会知道为什么。
  
  好了,下面是这节的例子。
  
  #region Using directives
  
  using System;
  
  using System.Collections.Generic;
  
  using System.ComponentModel;
  
  using System.Data;
  
  using System.Drawing;
  
  using System.Windows.Forms;
  
  using Microsoft.DirectX;
  
  using Microsoft.DirectX.Direct3D;
  
  #endregion
  
  namespace Ch01
  
  {
  
  partial class Form1 : Form
  
  {
  
  private Device device = null;
  
  public Form1()
  
  {
  
  InitializeComponent();
  
  this.SetStyle( ControlStyles.AllPaintingInWmPaint | ControlStyles.Opaque, true );
  
  }
  
  public void InitializeGraphics()
  
  {
  
  PresentParameters para = new PresentParameters();
  
  para.Windowed = true;
  
  para.SwapEffect = SwapEffect.Discard;
  
  device = new Device( 0, DeviceType.Hardware, this, CreateFlags.HardwareVertexProcessing, para );
  
  }
  
  private void Form1_Paint(object sender, PaintEventArgs e)
  
  {
  
  device.Clear( ClearFlags.Target, Color.Blue, 1.0f, 0 );
  
  CustomVertex.TransformedColored[] verts = new CustomVertex.TransformedColored[3];
  
  verts[0].Position = new Vector4( this.Width / 2.0f, 50.0f, 0.5f, 1.0f );
  
  verts[0].Color = Color.Aqua.ToArgb();
  
  verts[1].Position = new Vector4( this.Width - this.Width / 5.0f, this.Height - ( this.Height / 5.0f ), 0.5f, 1.0f );
  
  verts[1].Color = Color.Black.ToArgb();
  
  verts[2].Position = new Vector4( this.Width / 5.0f, this.Height - ( this.Height / 5.0f ), 0.5f, 1.0f );
  
  verts[2].Color = Color.Purple.ToArgb();
  
  device.BeginScene();
  
  device.VertexFormat = CustomVertex .TransformedColored .Format;
  
  device.DrawUserPrimitives( PrimitiveType.TriangleList, 1, verts );
  
  device.EndScene();
  
  device.Present();
  
 
4000
 this.Invalidate();
  
  }
  
  static void Main()
  
  {
  
  using ( Form1 frm = new Form1() )
  
  {
  
  // Show our form and initialize our graphics engine
  
  frm.Show();
  
  frm.InitializeGraphics();
  
  Application.Run( frm );
  
  }
  
  }
  
  }
  
  }
  
  BeginScene()和EndScene()是配对的。必需调用Present()函数后,我们绘制的东西才能显示出来。
  
  DrawUserPrimitives()是具体的绘制函数,还有个类似的函数DrawPrimitives(),这个函数用起来就比较麻烦了,得创建顶点缓冲,然后通过Device的SetStreamSource()函数设置到设备上去,并且要把顶点数据,写到这个顶点缓冲中去,然后才能够调用DrawPrimitives()绘制。
  
  在使用DrawUserPrimitives()的时候,我们使用了PrimitiveType.TrangleList,这里涉及了一个基本概念,3D Primitive.在Direct3D中支持多种类型。
  
  其中比较重要的是,Triangle List, Triangle Strip, Triangle Fan.具体概念,可以参考其他的资料。并且这些Primitive在不同的Shading Mode中的含义也是不同的,起码在Gouraud shading mode中是这样,TriangleList的每个三角形被单独平滑处理,TriangleStrip中的三角形被作为一个整体来平滑,TriangleFan呢?
  
  也应该和TriangleStrip一样吧,没有试过,有心人给确认一下吧。
  
  而且组织顶点数据的时候还涉及了Backface Culling的概念,也就是说顶点的顺序也是有讲究的,不能乱来。否则,打死你也看不到你画的东西,除非你在device.RenderState.CullMode中关掉了Backface Culling.
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息