C# Direct3D9开发实例一:月球绕着地球转

原创 2005年10月21日 14:39:00

源程序下载

一、建立空窗体

新建一个工程,添加引用,并导入名称空间。
加入一个设备对象变量:
private Microsoft.DirectX.Direct3D.Device device = null;
添加初始化图形函数,并在这里面对设备对象进行实例化:
public void InitializeGraphics()
         {
              PresentParameters presentParams = new PresentParameters();
              presentParams.Windowed = true;
              presentParams.SwapEffect = SwapEffect.Flip;
              presentParams.AutoDepthStencilFormat = DepthFormat.D16;
              presentParams.EnableAutoDepthStencil = true;
              device = new Microsoft.DirectX.Direct3D.Device(0, Microsoft.DirectX.Direct3D.DeviceType.Hardware, this,      CreateFlags.HardwareVertexProcessing, presentParams);
     }
当程序执行时,需要绘制场景,代码在这个函数里:
         public void Render()
         {
              // 清空设备,并准备显示下一帧。
              device.Clear(ClearFlags.Target | ClearFlags.ZBuffer, Color.Black , 1.0f, 0);
              // 设置照相机的位置
              SetupCamera();
              //开始场景
              device.BeginScene();
              if(meshLoaded)
              {
                   mesh.Render(meshLoc);
              }
              device.EndScene();
         //显示设备内容。
              device.Present();
         }
设置照相机的位置:
private void SetupCamera()
     {
              device.Transform.Projection = Matrix.PerspectiveFovLH((float)Math.PI / 4, this.Width / this.Height, 1.0f, 1000.00f);
              device.Transform.View = Matrix.LookAtLH(new Vector3(0.0f ,0.0f, 20.0f), new Vector3(0.0f,0.0f, 0.0f), new Vector3(0,1,0));
     }
现在改变主函数,调用我们写的初始化函数,并显示场景:
[STAThread]
         static void Main()
         {
         using (Form1 EarthForm = new Form1())
              {
                   EarthForm.InitializeGraphics();
                   EarthForm.Show();
                  
                   while(EarthForm.Created)
                   {
              EarthForm.Render();
                   Application.DoEvents();
                   }
                   EarthForm.Dispose();
              }
运行程序,会显示一个空的窗体。

二、加入地球:

在这一步里需创建一个3D网格对象,来作为要显示的地球,为此,在工程中新加入一个类Earth,此类可以包含所创建的网格对象的信息。
加入一些相关变量,含义见注释:
public class Earth : BaseEarth
     {
         private Material[] mMaterials; //保存材质
         private Texture[] mTextures; //保存纹理
         private Matrix locationOffset; //用来保存网格对象的相对位置
         private Mesh mMesh = null; //三角形网格对象
         private Device meshDevice; //需要显示在哪个设备上。
}
在构造函数中,把Device设备拷贝到私有成员变量,这样就可以在这个类的其它方法内使用它,另外就是把位置变量进行赋值:
     public Earth(ref Device device, Matrix location): base(ref device)
         {
              meshDevice = device;
              locationOffset = location;
         }
下面这个函数是装入.X文件。
public bool LoadMesh(string meshfile)
         {
              ExtendedMaterial[] mtrl;
              try
              {
                   // 装载文件
                   mMesh = Mesh.FromFile(meshfile, MeshFlags.Managed, meshDevice, out mtrl);
                   // 如果有材质的话,装入它们
                   if ((mtrl != null) && (mtrl.Length > 0))
                   {
                       mMaterials = new Material[mtrl.Length];
                       mTextures = new Texture[mtrl.Length];
                       // 得到材质和纹理
                       for (int i = 0; i < mtrl.Length; i++)
                       {
                            mMaterials[i] = mtrl[i].Material3D;
                            if ((mtrl[i].TextureFilename != null) && (mtrl[i].TextureFilename != string.Empty))
                            {
                                 //前面得到的纹理的路径是相对路径,需要保存的是绝对路径,通过应用程序路径可以获得
                                 mTextures[i] = TextureLoader.FromFile(meshDevice, @"../../" +        mtrl[i].TextureFilename);
                            }
                       }
                   }
                   return true;
              }
              catch
              {
                   return false;
              }
         }
在这个方法内,使用Mesh.FromFile()这个方法,从给定的文件名中找到.X文件,并装入相关数据,一旦数据格式设置完成,可以从此文件中找到材质和贴图信息,并把它存放在数组中,并通过文件路径,得到纹理文件文件的路径,最后返回真值,如果整个过程出现错误,返回假值。
下面这个Render()方法,是把此对象,即地球显示在设备对象上,此方法较简单,通过变形操作来得到网格对象的X,Y,Z坐标,接着设置网格对象的材质和纹理,最后,将每个材质和纹理应用到每个网格。
public void Render(Matrix worldTransform)
         {
              /把位置变为世界坐标
              meshDevice.Transform.World = Matrix.Multiply(locationOffset, worldTransform);
              //绘制网格
              for (int i = 0; i < mMaterials.Length; i++)
              {
                   meshDevice.Material = mMaterials[i];
                   meshDevice.SetTexture(0, mTextures[i]);
                   mMesh.DrawSubset(i);
              }
         }
 
现在回到窗体代码中,添加引用网格对象的相关变量:
private Earth mesh = null;
         private Matrix meshLoc;
         private bool meshLoaded = false;
 
在图形初始化函数中,需要对网格对象进行初始化。加入下面的代码:
              meshLoc = Matrix.Identity;
              meshLoc.M41 = 2.0f;
              mesh = new Earth(ref device, meshLoc);
              if (mesh.LoadMesh(@"../../earth.x"))
              {
                   meshLoaded = true;
              }
代码第一句把网格对象的位置定为原点,接着偏移X轴2个单位,接下来从文件中得到此.X文件。如果成功设置,meshLoaded置为真。注意,这里有一个.X文件,在源代码中有此文件。
 
在设置相机的函数中,加入一盏灯光:
device.Lights[0].Type = LightType.Directional;
              device.Lights[0].Diffuse = Color.White;
              device.Lights[0].Direction = new Vector3(0, -1, -1);
              device.Lights[0].Update();
              device.Lights[0].Enabled = true;
此灯光较简单,仅为一个直射型白光灯。
最后,在Render()方法中,调用网格对象的Render()方法,以显示地球。

三、使地球旋转

前面用一个网格对象来建立地球,但此类没有平移,旋转及缩放等方法,下面就加入这些方法,因为这些方法具有通用性,因此可以新建一个类,把这些方法写在这些类中,使地球对象成为它的派生类。
在工程中新添加一个类:BaseEarth;
加入进行平移、旋转、缩放的变量:
private float xloc = 0.0f;
         private float yloc = 0.0f;
         private float zloc = 0.0f;
         private float xrot = 0.0f;
         private float yrot = 0.0f;
         private float zrot = 0.0f;
         private float xscale = 1.0f;
         private float yscale = 1.0f;
         private float zscale = 1.0f;
加入相应的属性代码:
public float XLoc
         {
              get
              {
                   return xloc;
              }
              set
              {
                   xloc = value;
              }
         }
…………
在Render()虚函数中,应用平移、旋转及缩放。
public virtual void Render()
         {            
              objdevice.MultiplyTransform(TransformType.World,Matrix.Translation(xloc, yloc, zloc));
              objdevice.MultiplyTransform(TransformType.World,Matrix.RotationAxis(new Vector3(1.0f, 0.0f, 0.0f), xrot));
              objdevice.MultiplyTransform(TransformType.World,Matrix.RotationAxis(new Vector3(0.0f, 1.0f, 0.0f), yrot));
              objdevice.MultiplyTransform(TransformType.World,Matrix.RotationAxis(new Vector3(0.0f, 0.0f, 1.0f), zrot));
              objdevice.MultiplyTransform(TransformType.World,Matrix.Scaling(xscale, yscale, zscale));
              return; 
         }
现在回到地球类,需要将其改为新类的派生类,同时更改构造函数,另外,在Render()方法中,应先调用基类的Render()方法:
public override void Render()
 
         {
              base.Render();
              //把位置变为世界坐标
         //   meshDevice.Transform.World = Matrix.Multiply(locationOffset, worldTransform);
              //绘制网格
              。。。。。。
 
         }
现在,由于在基类中可以设置对象位置,因此,可以把与locationOffset相关,即与设置位置的变量及语句注释掉。

四、加入月球

在这一步加入月球,实际上是再创建一个网格对象新实例,只是把纹理进行更改即可,为了代码模块性更好,把两个对象放在一个新类CModel中,在工程中新添加一个类CModel,并声明对象实例。
 
     public class cModel
     {
         private cMeshObject mesh1 = null;
         private cMeshObject mesh2 = null;
         private bool modelloaded;
}
把窗口代码中的Load()事件,放在CModel中,这次不仅生成了地球,而且生成了月球。
public void Load(ref Device device)
         {
              mesh1 = new Earth(ref device);
              mesh2 = new Earth(ref device);
              if (mesh1.LoadMesh(@"../../earth2.x"))
              {
                   modelloaded = true;
              }
              else
              {
                   modelloaded = false;
              }
              if (mesh2.LoadMesh(@"../../moon.x"))
              {
                   mesh2.XLoc += 20.0f;
                   modelloaded = true;
              }
              else
              {
                   modelloaded = false;
              }
         }
下面的Update()方法中,参数dir 用来判断是顺时针旋转还是逆时针旋转,另外,地球和月球绕Y轴增加的角度大小不同,也就决定了二者旋转的速度不同。
public void Update(int dir)
         {
              if(dir > 0)
              {
                   mesh1.YRot += 0.02f;
                   mesh2.YRot += 0.05f;
              }
              else if(dir < 0)
              {
                   mesh1.YRot -= 0.02f;
                   mesh2.YRot -= 0.05f;
              }
         }
在下面的render()方法中,生成显示月球和地球:
public void Render(ref Device device)
         {
              device.Transform.World = Matrix.Identity;
              if(modelloaded)
              {
              mesh1.Render();
              mesh2.Render();
              }
         }
把窗口代码中的加入灯光的方法,也放在此类中:
public void LoadLights(ref Device device)
         {
              device.Lights[0].Type = LightType.Directional;
              device.Lights[0].Diffuse = Color.White;
              device.Lights[0].Position = new Vector3(0.0f, 0.0f, 25.0f);
              device.Lights[0].Direction = new Vector3(0, 0, -1);         
         }
public void Light(ref Device device)
         {
              device.Lights[0].Update();
              device.Lights[0].Enabled = true;
          }

五、与鼠标交互操作

为了实现与键盘、鼠标交互,新添加一个类:CMouse,添加引用Microsoft.DirectX.DirectInput,并添加命名空间。加入相关变量:
         private Microsoft.DirectX.DirectInput.Device mouse = null;
         public System.Threading.AutoResetEvent MouseUpdated;
         private float x, y, z = 0.0f;
         private byte[] buttons;
在下面的构造函数代码中,首先创建鼠标设备,并初始化回调事件:
         public CMouse(System.Windows.Forms.Control control)
         {
              mouse = new Microsoft.DirectX.DirectInput.Device(SystemGuid.Mouse);
              mouse.SetCooperativeLevel(control, CooperativeLevelFlags.Background | CooperativeLevelFlags.NonExclusive);
                       mouse.Properties.AxisModeAbsolute = false;
              MouseUpdated = new System.Threading.AutoResetEvent(false);
              mouse.SetEventNotification(MouseUpdated);
              mouse.Acquire();
              Update();
         }
下面的Update()方法中获得鼠标的坐标值,并赋给私有成员变量:
public void Update()
         {
              MouseState state = mouse.CurrentMouseState;       
                            x = state.X;
              y = state.Y;
              z = state.Z;
              buttons = state.GetMouseButtons();
         }
还需要有一个函数来检测鼠标左键是否按下:
public bool LeftButtonDown
         {
              get
              {
                   bool a;
                   return a = (buttons[0] != 0);
              }
         }

六、大结局

现在已经做完了准备工作,返回到窗口代码中,需要对这里的代码重新进行一些调整:
在图形初始化函数中创建一个CModel类及CMouse类:
private CModel model = null;
private CMouse mouse = null;
private bool leftbuttondown = false;
private float mousexloc;
添加对鼠标初始化的方法:
public void InitializeInput()
{
     mouse = new CMouse(this);
}
 
添加UpdateInputState()方法,当按下鼠标左键时,将leftbuttondown值设置为真,当鼠标抬起时,将mousexloc置0:
private void UpdateInputState()
         {
              mouse.Update();
              if (mouse.LeftButtonDown)
              {
                   if(leftbuttondown == false)
                   {
                       mousexloc = 0.0f;
                       leftbuttondown = true;
                   }
                   else
                   {
                       mousexloc =  -mouse.X;
                   }
              }
              else
              {
                   leftbuttondown = false;
                   mousexloc = 0.0f;
              }
         }
在此程序中,只对X值进行了操作,即只能左右转。
Render()方法更新如下:
         public void Render()
         {
              UpdateInputState();
              device.Clear(ClearFlags.Target | ClearFlags.ZBuffer, Color.DarkGray, 1.0f, 0);
              SetupCamera();
              device.BeginScene();
              model.Update((int)mousexloc);
              model.Light(ref device);
              model.Render(ref device);
              device.EndScene();
              device.Present();
          }
最后更改Main()主函数:
static void Main()
         {
         using (Form1 EarthForm = new Form1())
              {
                   EarthForm.InitializeGraphics();
                   EarthForm.InitializeInput();
              EarthForm.Show();
                   while(EarthForm.Created)
                   {
              EarthForm.Render();
                   Application.DoEvents();
                   }
                   EarthForm.Dispose();
              }
运行程序,按下鼠标左键拖动,即可旋转月球与地球。
版权声明:本文为博主原创文章,未经博主允许不得转载。

相关文章推荐

最简单的视音频播放示例3:Direct3D播放YUV,RGB(通过Surface)

上一篇文章记录了GDI播放视频的技术。打算接下来写两篇文章记录Direct3D(简称D3D)播放视频的技术。Direct3D应该Windows下最常用的播放视频的技术。实际上视频播放只是Direct3...

C#控件中使用DirectX

添加Reference:   using Microsoft.DirectX; using Microsoft.DirectX.Direct3D;     从标准控件派生 public ...

Direct2d CPU占用过高的问题。

使用传统的渲染循环发现direct2d占用满CPU,而且是启用垂直同步的情况下。 我就纳闷了,不可能微软是骗人的吧。还“硬件加速”呢,全效渲染,帧率居然比directx9低。 代码使用simple...
  • glunoy
  • glunoy
  • 2012-04-06 10:39
  • 2640

Visual C#使用DirectX实现视频播放

本文转自 博客园 浩然秋发 原文地址:http://www.cnblogs.com/qiufa/archive/2007/05/09/739977.html   很多人第一次接触到DirectX...
  • kenkao
  • kenkao
  • 2013-07-28 11:48
  • 1217

c#开发环境下用Directx载入3D模型

Direcx一般都是用VC++开发环境,而c#相关的资料比较少,最近做的一个项目中需要用到一个3D模型,而开发环境是visual c#,没办法,就四处搜集资料,查找相关的资料,然后找到了几篇相关的博文...

c#/winform Directx编程入门之实现立方体

c# directx编程入门之实现立方体 一、Directx简介      Directx是微软公司开发的一套功能丰富的底层API, 二、开始绘制 1、设备初始化,创建设备 ...

C#+Direct3D9.0开发实例之月亮绕着地球转

一、建立空窗体   新建一个工程,添加引用,并导入名称空间。  加入一个设备对象变量:private Microsoft.DirectX.Direct3D.Device device = null; ...

关于vs2012中使用C#和DirectX的配置

1.安装好vs2012和directx; 2.新建一个c#项目,然后添加引用Microsoft.DirectX.dll、Microsoft.DirectX.Direct3D.dll、Microsoft...
内容举报
返回顶部
收藏助手
不良信息举报
您举报文章:深度学习:神经网络中的前向传播和反向传播算法推导
举报原因:
原因补充:

(最多只允许输入30个字)