DirectX9 3D 快速上手 (续)(转)

DirectX9 3D 快速上手  4
原文:http://dev.gameres.com/Program/Visual/DirectX/DX93D_4.mht
出自:http://www.gameres.com

By  sssa2000

4/17/2005

接下来我们要使用Mesh读入.X文件,关于.X文件,其实可以说很多,我们可以用外部的工具例如3DS MAX来建立.3ds文件,然后利用微软提供给我们的工具转换成.X文件,如果你同时会用3DS你也许会问,那材质怎么办? 你不用担心,.X文件能自动的包含材质,和动画,所以利用Mesh我们可以方便的八我们在3DS MAX的做品用在游戏中。

.X文件允许自定义的扩展,当然这个我们在以后的内容也会讲到,我们先看怎么使用,顺便提一句,还有一种用得比较多的文件格式.Md3格式的文件,也就是Quake3使用的文件,如果有机会我也会讲到。

关于3DS文件和.X转换可以参看GameRes网站的文章http://www.gameres.com/Articles/Program/Visual/3D/3DinApp.htm

 

接下来的过程简单的让你无法相信,请看:

        private Mesh mesh = null; //建立Mesh对象

        private Material[] meshMaterials;  //用于保存材质

        private Texture[] meshTextures;   //用于保存纹理

        private void LoadMesh(string file)

        {

            ExtendedMaterial[] mtrl; //保存Mesh子集信息,保存Material信息

            // Load our mesh

            mesh = Mesh.FromFile(file, MeshFlags.Managed, device, out mtrl);

            // If we have any materials, store them

            if ((mtrl != null) && (mtrl.Length > 0))

            {

                meshMaterials = new Material[mtrl.Length];

                meshTextures = new Texture[mtrl.Length];

                // Store each material and texture

                for (int i = 0; i < mtrl.Length; i++)

                {

                    meshMaterials[i] = mtrl[i].Material3D;

                    if ((mtrl[i].TextureFilename != null) && (mtrl[i].TextureFilename != string.Empty))

                    {

                        // We have a texture, try to load it

                        meshTextures[i] = TextureLoader.FromFile(device, @"..\..\" + mtrl[i].TextureFilename);

                    }

                }

            }

        }

如果你觉得这里代码比较混乱,没有关系,我们来理一理,首先要说的是Mesh类还有两个主要的静态方法可以加载外部模型。这两个方法分别是Mesh.FormFileMesh.FromStream。两个方法本质上来说都是一样的,stream方法有更多的重载以适应不同大小的流。Mesh.FormFile最简单,我们就先只介绍这个,这个方法有很多重载的版本,我们用的是参数最多的一个,这样其他的也都好理解了:

public static Mesh FromFile(
    string filename, //
文件名
    MeshFlags options,//
控制着去哪里以及如何加载数据
    Device device,//
渲染meshdevice
    out GraphicsStream adjacency,//
保存每个面相邻的3个面
    out ExtendedMaterial materials,//
保存了普通的Direct3D材质和一个加载为纹理的字符串,常是使用的纹理或资源文件名
    out EffectInstance effects //描述了用于meshHLSL材质文件和值。
);

我们使用的是一个有4参数的方法:

public static Mesh FromFile(
    string filename,
    MeshFlags options,
    Device device,
    out ExtendedMaterial materials
);

接下来,如果读入的这个.X文件有材质或者纹理,就根据材质和纹理的大小建立数组以保存这些纹理和材质。因为mesh中可能有许多不同的子集,所以需要分别创建一个材质和纹理的数组以满足每一个子集的需要。Mesh会自动地把每一个子集和这些子集的材质纹理一一对座。

这样我们就完成了一个LoadMesh函数,这个函数我们可以用在以后任何需要读入.X文件的地方,十分的方便。

下面我们来看看我们的成果,当然我们还需要在绘图的地方加几句:

            for (int i = 0; i < meshMaterials.Length; i++)

            {

                device.Material = meshMaterials[i];// 把保存的材质赋予device

                device.SetTexture(0, meshTextures[i]);// 把纹理赋予device,如果没有就为Null

                mesh.DrawSubset(i);// 根据子集的ID调用DrawSubset方法

            }

好了,现在我们可以在以往的框架的基础上编译一下了,我们可以看到已经读入了一个.X文件,不管怎么样 都是值得庆贺的。我们再看看SDK Tutorial6的例子后,发现这个程序的结果可以旋转,其实也很好实现,我们只需要设置一下旋转矩阵就可以了,就像上一张我们讲的那样,在上面那段for前面加上一句就可以了:

device.Transform.World = Matrix.RotationYawPitchRoll(yaw, pitch, roll) * Matrix.Translation(x, y, z);

好了这样我们也就达到目的了,你也可以随自己的意思设置变换矩阵,很COOL吧?

好了,下面是完整的代码,如果大家有什么意见,发到我的blog上吧。大家一起进步。

 

using System;

using System.Drawing;

using System.Collections;

using System.ComponentModel;

using System.Windows.Forms;

using System.Data;

using Microsoft.DirectX;

using Microsoft.DirectX.Direct3D;

 

namespace Chapter5Code

{

     /// <summary>

     /// Summary description for Form1.

     /// </summary>

     public class Form1 : System.Windows.Forms.Form

     {

        private Device device = null;

        private Mesh mesh = null;

        private Material[] meshMaterials;

        private Texture[] meshTextures;

 

         /// <summary>

         /// Required designer variable.

         /// </summary>

         private System.ComponentModel.Container components = null;

        private float angle = 0.0f;

 

         public Form1()

         {

              //

              // Required for Windows Form Designer support

              //

              InitializeComponent();

 

            this.SetStyle(ControlStyles.AllPaintingInWmPaint | ControlStyles.Opaque, true);

         }

 

        /// <summary>

        /// We will initialize our graphics device here

        /// </summary>

        public void InitializeGraphics()

        {

            // Set our presentation parameters

            PresentParameters presentParams = new PresentParameters();

 

            presentParams.Windowed = true;

            presentParams.SwapEffect = SwapEffect.Discard;

            presentParams.AutoDepthStencilFormat = DepthFormat.D16;

            presentParams.EnableAutoDepthStencil = true;

 

            // Create our device

            device = new Device(0, DeviceType.Hardware, this, CreateFlags.SoftwareVertexProcessing, presentParams);

 

            // Load our mesh

            LoadMesh(@"..\..\tiny.x");

        }

 

        private void LoadMesh(string file)

        {

            ExtendedMaterial[] mtrl;

 

            // Load our mesh

            mesh = Mesh.FromFile(file, MeshFlags.Managed, device, out mtrl);

 

            // If we have any materials, store them

            if ((mtrl != null) && (mtrl.Length > 0))

            {

                meshMaterials = new Material[mtrl.Length];

                meshTextures = new Texture[mtrl.Length];

 

                // Store each material and texture

                for (int i = 0; i < mtrl.Length; i++)

                {

                    meshMaterials[i] = mtrl[i].Material3D;

                    if ((mtrl[i].TextureFilename != null) && (mtrl[i].TextureFilename != string.Empty))

                    {

                        // We have a texture, try to load it

                        meshTextures[i] = TextureLoader.FromFile(device, @"..\..\" + mtrl[i].TextureFilename);

                    }

                }

            }

        }

 

        private void SetupCamera()

        {

            device.Transform.Projection = Matrix.PerspectiveFovLH((float)Math.PI / 4, this.Width / this.Height, 1.0f, 10000.0f);

            device.Transform.View = Matrix.LookAtLH(new Vector3(0,0, 580.0f), new Vector3(), new Vector3(0,1,0));

            //device.RenderState.Ambient = Color.DarkBlue;

            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;

 

        }

 

        protected override void OnPaint(System.Windows.Forms.PaintEventArgs e)

        {

            device.Clear(ClearFlags.Target | ClearFlags.ZBuffer, Color.CornflowerBlue, 1.0f, 0);

 

            SetupCamera();

 

            device.BeginScene();

 

            // Draw our Mesh

            DrawMesh(angle / (float)Math.PI, angle / (float)Math.PI * 2.0f, angle / (float)Math.PI / 4.0f, 0.0f, 0.0f, 0.0f);

 

            device.EndScene();

 

            device.Present();

 

            this.Invalidate();

        }

 

        private void DrawMesh(float yaw, float pitch, float roll, float x, float y, float z)

        {

            angle += 0.01f;

 

            device.Transform.World = Matrix.RotationYawPitchRoll(yaw, pitch, roll) * Matrix.Translation(x, y, z);

            for (int i = 0; i < meshMaterials.Length; i++)

            {

                device.Material = meshMaterials[i];

                device.SetTexture(0, meshTextures[i]);

                mesh.DrawSubset(i);

            }

        }

 

        /// <summary>

         /// Clean up any resources being used.

         /// </summary>

         protected override void Dispose( bool disposing )

         {

              if( disposing )

              {

                   if (components != null)

                   {

                       components.Dispose();

                   }

              }

              base.Dispose( disposing );

         }

 

         #region Windows Form Designer generated code

         /// <summary>

         /// Required method for Designer support - do not modify

         /// the contents of this method with the code editor.

         /// </summary>

         private void InitializeComponent()

         {

              this.components = new System.ComponentModel.Container();

              this.Size = new Size(800,600);

              this.Text = "Form1";

         }

         #endregion

 

         /// <summary>

         /// The main entry point for the application.

         /// </summary>

        static void Main()

        {

            using (Form1 frm = new Form1())

            {

                // Show our form and initialize our graphics engine

                frm.Show();

                frm.InitializeGraphics();

                Application.Run(frm);

            }

        }

     }

}

其实我们都已经发现,到目前为止我们的程序都是遵循一个固定的框架,有时候你发现程序很长,其实从框架角度来看,就没那么长了,怕看长代码的人可以注意一下,同时很多代码都是通用的。下次学习怎么操控Mesh

By sssa2000

DirectX9 3D 快速上手  5

By sssa2000

4/18/2005

    这一章的内容相对很简单,控制Mesh的移动,旋转等等,其实这一切都是在对矩阵进行操作。在 DX中,用到的变换有3种,一种是基于Word坐标系的,一种是基于View坐标系的,还有一种是基于投影的变换。而这些变换都是通过矩阵的运算来实现的,在.Net的托管环境下,实现这些操作相对于非托管来说简单一写,不用对矩阵的每个值运算。

关于矩阵的运算和变换的关系,很多文章都有分析,GameRes也有很多这样的好文章,例如:http://dev.gameres.com/Program/Visual/3D/3DGame.mht 这里就有很详细的介绍。

我们这篇文章不研究细节,因为毕竟是快速开发,我们的目标就是用键盘控制读入的Mesh的运动。

其实在前面一篇文章中我们就有提到几个函数,只是没有介绍。我们来看看我们这一篇中要用到的几个简单的函数,从字面我们就可以判断出这些函数有什么用:

 

Matrix.RotateX方法:public void RotateX(    float angle);

Matrix. RotateY方法:public void RotateY(    float angle);

Matrix.RotateX方法:public void RotateZ(    float angle);

Matrix.Translation方法:public static Matrix Translation(float offsetx, float offsety, float offsetz);

 

好,我们现在重新打开上一次的读入Mesh的那个工程,我们现在要加入新的部分,既然是用键盘控制那么就要首先对键盘的状态进行分析:

         String status=null;//用于保存状态

protected override void OnKeyDown(System.Windows.Forms.KeyEventArgs e)

         {

              // Handle the escape key for quiting

              if (e.KeyCode == Keys.Escape)

              {

                   // Close the form and return

                   this.Close();

                   return;

              }

              // Handle left and right keys

              else if ((e.KeyCode == Keys.Left) || (e.KeyCode == Keys.NumPad4))

              {

                   status="left";  //向左旋转

              }

              else if ((e.KeyCode == Keys.Right) || (e.KeyCode == Keys.NumPad6))

              {

                   status="right";//向右旋转

              }

              else if((e.KeyCode ==Keys.Up )||(e.KeyCode ==Keys.NumPad8 ))

              {

                   status="up";//向上旋转

              }

              else if((e.KeyCode ==Keys.Down  )||(e.KeyCode ==Keys.NumPad2 ))

              {

                   status="down";//向下旋转

              }

              else if(e.KeyCode ==Keys.Enter)

              {

                   status="stop";//停止旋转

              }

              else if(e.KeyCode ==Keys.W  )

              {

                   status="goahead";//向前

 

              }

              else if(e.KeyCode ==Keys.S )

              {

                   status="goback";//向后

              }

              else if(e.KeyCode ==Keys.A)

              {

                   status="goleft";//向左

              }

              else if(e.KeyCode ==Keys.D )

              {

                   status="goright";//向右

              }

             

         }

很简单,以至于没什么可说的,下面就要对Mesh进行移动,由于我们观察的运动都是相对运动,所以无论你是对摄像机进行操作还是进行世界变换都是可以的,随你所好。我们对DrawMesh函数进行修改,主要是对状态的判断,然后对相应的矩阵进行变换:

        private void DrawMesh(string stat)

              if(stat=="left")

              {

                   angle=angle+0.1f;

                   device.Transform.World = Matrix.RotationY (angle);

              }

              else if(stat=="right")

              {

                   angle=angle-0.1f;

                   device.Transform.World = Matrix.RotationY (angle);

              }

              else if(stat=="up")

              {

                   angle=angle+0.1f;

                   device.Transform.World = Matrix.RotationX (angle);

 

 

              }

              else if(stat=="down")

              {

                   angle=angle-0.1f;

                   device.Transform.World = Matrix.RotationX (angle);

 

              }

              else if(stat=="stop")

              {

                   angle=0.0f;

                   //device.Reset ()

              }

              else if(stat=="goahead")

              {

                   v=v+0.1f;

              }

              else if(stat=="goback")

              {

                   v=v-0.1f;

              }

              else if(stat=="goleft")

              {

                   offsetx=offsetx+0.01f;

 

                   device.Transform.World = Matrix.Translation(offsetx,0,0);

              }

              else if(stat=="goright")

              {

                   offsetx=offsetx-0.01f;

                   device.Transform.World = Matrix.Translation(offsetx,0,0);

 

              }

 

            for (int i = 0; i < meshMaterials.Length; i++)

            {

                device.Material = meshMaterials[i];

                device.SetTexture(0, meshTextures[i]);

                mesh.DrawSubset(i);

            }

        }

这里我们处理小车前后移动的时候,没有直接使用世界变换,而是对摄影机进行了操作,这也很好理解,为了达到这个目的,我把SetupCamera函数小小的修改了一下:

  private void SetupCamera(float deep )

        {

            device.Transform.Projection = Matrix.PerspectiveFovLH((float)Math.PI / 4, this.Width / this.Height, 1.0f, 10000.0f);

            device.Transform.View = Matrix.LookAtLH(new Vector3(0,0, 10.0f+deep), new Vector3(), new Vector3(0,1,0));

            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;

        }

我给它加了一个参数,这个参数就是摄像机在Z轴的位置,我们只需要修改摄像机在Z轴的位置就可以实现透视效果,用这个方法实现我们这个简单的需求是很恰当的

这里再来对这里面的2个函数分析一下:

device.Transform.Projection用来获取或者设置投影变换矩阵,它本身也是一个Matrix型的变量。Matrix.PerspectiveFovLH方法:

public static Matrix PerspectiveFovLH(
    float fieldOfViewY, //Y
方向的视觉范围,弧度表示,即Y方向的视域角
    float aspectRatio, //
视觉范围的长宽比
    float znearPlane, //
*裁减*面      
    float zfarPlane//
远裁减*面
);

和非托管的相比,仅仅少了一个参数。定义宽高比的作用是,如果用户改变了窗口的大小,那么仍然就可以正确的显示物体的大小。最后两个参数是 *远裁减*面,必须为这两个*面指定2Z值,一般来说,假设物体将在Z轴的1.0f1000f之间运动时,就分别把这两个参数设为1.0f1000.0f

再来看看另外方法:Matrix.LookAtLH方法。 在使用观察矩阵来建立一个立体的照相机模型后,允许玩家通过一系列不同的观察点来看世界,使用Matrix.LookAtLH方法建立的照相机模型,是一种十分高效的照相机模型,但是注意,这一种模型不能绕所有的轴做三维转动。

public static Matrix LookAtLH(
    Vector3 cameraPosition,//
相机位置
    Vector3 cameraTarget,   //
相机的方向
    Vector3 cameraUpVector  //
相机的Up向量
);

其中第二个参数为相机的面对方向,假设相机面对的是Z轴的方向,那么就设置为(0,0,1)。好,现在也许有人要问,那如果面对的是Z轴的负方向呢?这也就是第3个参数的目的,第三个参数定一个Up向量,所以如果相机是正着放的,那么第3个参数就应该是(0,1,0),如果是倒着放的就是(0,-1,0)。所以如果面对的是Z的负方向,第三个参数就是(0,-1,0)

利用这个方法我们可以轻易的建立一个跟随式的相机(follow-up camera),例如实现第一人称视角,只需要把第2个参数指向跟随物体的方向,把相机位置设置为这个物体的周围。前面我们提过,这类相机不能绕自身旋转。

好,让我们来简单实现这个功能,我们想让汽车前进后退的时候能让摄像机来跟随它,首先,我们必须得到读入的Meshx,y,z这样才能给摄像机提供第一个参数,其次我们要确定朝向,在这里,很好确定,因为汽车就是朝Z方向开的,好了,都确定下来我们就可以写代码了。为了得到mesh的位置,就要使用Mesh内置的定点缓冲。

……………………………………..

             LoadMesh(@"..\..\sr.x");

             vb=mesh.VertexBuffer; //取得读入mesh的中心

              try

              {

                   Vector3 max;

                   Vector3 min;

                   GraphicsStream gs = vb.Lock(0, 0, LockFlags.None);

                   Geometry.ComputeBoundingBox(gs, mesh.NumberVertices, mesh.VertexFormat, out min, out max); //取得mesh的边框

                   float tx = (max.X - min.X)/2;

                   float ty =( max.Y - min.Y)/2;

                   float tz =( max.Z - min.Z);

                   pos.X =tx-0.9f;  //试出来的,这样才能使摄像机*似在车子中心

                   pos.Y =ty+0.6f;

                   pos.Z =tz-1.8f;

                   //float cameraHeadingH=

                   lookat = new Vector3 (0,0,1);

              }

              finally

              {

                   vb.Unlock();

                   vb.Dispose();

              }

修改KeyDown并且修改SetupCamera函数判断摄像机的选择。

private void SetupCamera(float deep )

        {

            device.Transform.Projection = Matrix.PerspectiveFovLH((float)Math.PI / 4, this.Width / this.Height, 1.0f, 10000.0f);

              if(camera%2!=0)

                device.Transform.View = Matrix.LookAtLH(new Vector3(0,0, 10.0f+deep), new Vector3(), new Vector3(0,1,0));

              else

                   device.Transform .View =Matrix.LookAtLH (pos,new Vector3 (0,1,0),new Vector3 (0,1,0));

 

            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;

        }

 

这样就完成了这个功能,不过这里只是简单模拟一下,只有在前进后退的时候才有效果,不过由于没有参照物,在前进后退的时候看不出画面的变化。下面贴出完整的代码:

using System;

using System.Drawing;

using System.Collections;

using System.ComponentModel;

using System.Windows.Forms;

using System.Data;

using Microsoft.DirectX;

using Microsoft.DirectX.Direct3D;

 

namespace Chapter5Code

{

     /// <summary>

     /// Summary description for Form1.

     /// </summary>

     public class Form1 : System.Windows.Forms.Form

     {

        private Device device = null;

         PresentParameters presentParams =null;

        private Mesh mesh = null;

        private Material[] meshMaterials;

        private Texture[] meshTextures;

 

         private Mesh mesh2=null;

         Material[] meshMaterials2;

         Texture[] meshTextures2;

 

        string status=null; //小车状态,向左转动还是向右转动,或者停止

         private VertexBuffer vb = null;

         Vector3 pos;

         Vector3 lookat;

 

         int camera=1;

         float angle=0.0f;

         float v=0.1f;

         float offsetx=0.1f;

 

         /// <summary>

         /// Required designer variable.

         /// </summary>

         private System.ComponentModel.Container components = null;

 

         public Form1()

         {

              //

              // Required for Windows Form Designer support

              //

              InitializeComponent();

 

            this.SetStyle(ControlStyles.AllPaintingInWmPaint | ControlStyles.Opaque, true);

         }

 

        /// <summary>

        /// We will initialize our graphics device here

        /// </summary>

        public void InitializeGraphics()

        {

            // Set our presentation parameters

            PresentParameters presentParams = new PresentParameters();

 

            presentParams.Windowed = true;

            presentParams.SwapEffect = SwapEffect.Discard;

            presentParams.AutoDepthStencilFormat = DepthFormat.D16;

            presentParams.EnableAutoDepthStencil = true;

 

            // Create our device

            device = new Device(0, DeviceType.Hardware, this, CreateFlags.SoftwareVertexProcessing, presentParams);

            // Load our mesh

             LoadMesh(@"..\..\sr.x");

 

              vb=mesh.VertexBuffer; //取得读入mesh的中心

              try

              {

                   Vector3 max;

                   Vector3 min;

                   GraphicsStream gs = vb.Lock(0, 0, LockFlags.None);

                   Geometry.ComputeBoundingBox(gs, mesh.NumberVertices, mesh.VertexFormat, out min, out max);

                   float tx = (max.X - min.X)/2;

                   float ty =( max.Y - min.Y)/2;

                   float tz =( max.Z - min.Z);

                   pos.X =tx-0.9f;

                   pos.Y =ty+0.6f;

                   pos.Z =tz-1.8f;

                   //float cameraHeadingH=

                   lookat = new Vector3 (0,0,1);

 

              }

              finally

              {

                   vb.Unlock();

                   vb.Dispose();

              }

        }

 

        private void LoadMesh(string file)

        {

            ExtendedMaterial[] mtrl;

 

            // Load our mesh

            mesh = Mesh.FromFile(file, MeshFlags.Managed, device, out mtrl);

             // If we have any materials, store them

            if ((mtrl != null) && (mtrl.Length > 0))

            {

                meshMaterials = new Material[mtrl.Length];

                meshTextures = new Texture[mtrl.Length];

 

                // Store each material and texture

                for (int i = 0; i < mtrl.Length; i++)

                {

                    meshMaterials[i] = mtrl[i].Material3D;

                    if ((mtrl[i].TextureFilename != null) && (mtrl[i].TextureFilename != string.Empty))

                    {

                        // We have a texture, try to load it

                        meshTextures[i] = TextureLoader.FromFile(device, @"..\..\" + mtrl[i].TextureFilename);

                    }

                }

            }

        }

 

 

 

         protected override void OnKeyDown(System.Windows.Forms.KeyEventArgs e)

         {

              // Handle the escape key for quiting

              if (e.KeyCode == Keys.Escape)

              {

                   // Close the form and return

                   this.Close();

                   return;

              }

              // Handle left and right keys

              else if ((e.KeyCode == Keys.Left) || (e.KeyCode == Keys.NumPad4))

              {

                   status="left";

              }

              else if ((e.KeyCode == Keys.Right) || (e.KeyCode == Keys.NumPad6))

              {

                   status="right";

              }

              else if((e.KeyCode ==Keys.Up )||(e.KeyCode ==Keys.NumPad8 ))

              {

                   status="up";

              }

              else if((e.KeyCode ==Keys.Down  )||(e.KeyCode ==Keys.NumPad2 ))

              {

                   status="down";

              }

              else if(e.KeyCode ==Keys.Enter)

              {

                   status="stop";

              }

              else if(e.KeyCode ==Keys.W  )

              {

                   status="goahead";

 

              }

              else if(e.KeyCode ==Keys.S )

              {

                   status="goback";

              }

              else if(e.KeyCode ==Keys.A)

              {

                   status="goleft";

              }

              else if(e.KeyCode ==Keys.D )

              {

                   status="goright";

              }

              else if(e.KeyCode ==Keys.C)

              {

                   camera++;

              }

 

             

 

         }

 

        private void SetupCamera(float deep )

        {

            device.Transform.Projection = Matrix.PerspectiveFovLH((float)Math.PI / 4, this.Width / this.Height, 1.0f, 10000.0f);

              if(camera%2!=0)

                device.Transform.View = Matrix.LookAtLH(new Vector3(0,0, 10.0f+deep), new Vector3(), new Vector3(0,1,0));

              else

                   device.Transform .View =Matrix.LookAtLH (pos,new Vector3 (0,1,0),new Vector3 (0,1,0));

 

            //device.RenderState.Ambient = Color.DarkBlue;

            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;

 

        }

 

        protected override void OnPaint(System.Windows.Forms.PaintEventArgs e)

        {

            device.Clear(ClearFlags.Target | ClearFlags.ZBuffer, Color.CornflowerBlue, 1.0f, 0);

 

            SetupCamera(v );

 

            device.BeginScene();

 

            // Draw our Mesh

            DrawMesh(status);

 

            device.EndScene();

 

            device.Present();

 

            this.Invalidate();

        }

 

        private void DrawMesh(string stat)

        {

         //   angle += 0.01f;

 

        //    device.Transform.World = Matrix.RotationYawPitchRoll(yaw, pitch, roll) * Matrix.Translation(x, y, z);

              if(stat=="left")

              {

                   angle=angle+0.1f;

                   device.Transform.World = Matrix.RotationY (angle);

              }

              else if(stat=="right")

              {

                   angle=angle-0.1f;

                   device.Transform.World = Matrix.RotationY (angle);

              }

              else if(stat=="up")

              {

                   angle=angle+0.1f;

                   device.Transform.World = Matrix.RotationX (angle);

 

 

              }

              else if(stat=="down")

              {

                   angle=angle-0.1f;

                   device.Transform.World = Matrix.RotationX (angle);

 

              }

              else if(stat=="stop")

              {

                   angle=0.0f;

                   //device.Reset ()

              }

              else if(stat=="goahead")

              {

                   v=v+0.1f;

              }

              else if(stat=="goback")

              {

                   v=v-0.1f;

              }

              else if(stat=="goleft")

              {

                   offsetx=offsetx+0.001f;

 

                   device.Transform.World = Matrix.Translation(offsetx,0,0);

                   //device.Transform .View =Matrix.Translation(offsetx,0,0);

              //   pos.X -=offsetx;

              //   device.Transform .View =Matrix.LookAtLH(pos,new Vector3 (0,0,1),new Vector3 (0,1,0));

              }

              else if(stat=="goright")

              {

                   offsetx=offsetx-0.001f;

                   device.Transform.World = Matrix.Translation(offsetx,0,0);

 

 

              }

 

 

            for (int i = 0; i < meshMaterials.Length; i++)

            {

                device.Material = meshMaterials[i];

                device.SetTexture(0, meshTextures[i]);

                mesh.DrawSubset(i);

            }

        }

 

        /// <summary>

         /// Clean up any resources being used.

         /// </summary>

         protected override void Dispose( bool disposing )

         {

              if( disposing )

              {

                   if (components != null)

                   {

                       components.Dispose();

                  }

              }

              base.Dispose( disposing );

         }

 

         #region Windows Form Designer generated code

         /// <summary>

         /// Required method for Designer support - do not modify

         /// the contents of this method with the code editor.

         /// </summary>

         private void InitializeComponent()

         {

              this.components = new System.ComponentModel.Container();

              this.Size = new Size(800,600);

              this.Text = "Form1";

         }

         #endregion

 

         /// <summary>

         /// The main entry point for the application.

         /// </summary>

        static void Main()

        {

            using (Form1 frm = new Form1())

            {

                // Show our form and initialize our graphics engine

                frm.Show();

                frm.InitializeGraphics();

                Application.Run(frm);

            }

        }

     }

}

 

好幸库,结束。

By sssa2000

DirectX9 3D 快速上手 6

By sssa2000

4/25/2005

讲了很多,基础的部分就剩下纹理没有讲到了。TextureDirectx里面非常重要的一部分。为了简便起见,我们还是以SDKTutorial5为例子。

纹理就像一张墙纸,用来贴在物体的表面,当然,如果足够大,贴一次就能覆盖整个物体的表面,也可以用适当的方法让纹理排列成你要的效果。

来看看纹理的比较重要的函数:Device.SetTexture

public void SetTexture(
    int stage, //
纹理混合阶段序号,从0开始
    BaseTexture texture //
要设置的纹理对象
);

 

public void SetTextureStageState(
    int stage, //
纹理混合阶段序号
    TextureStageStates state, // TextureStageStates enumeration
的成员
    int value //
对应阶段状态的值
);

SetTextureStageState函数对处理不同的纹理坐标,颜色操作,Alpha操作,和凹凸映射/环境映射比较适用,但是这些操作只对DX9的固定功能的多纹理单元有效,不能将他们与像素shader连用。

public void SetSamplerState(
    int stage, //
纹理混合阶段序号
    SamplerStageStates state, // SamplerStageStates enumeration
的成员
  int value  //
对应采样器状态的值
);

知道了这些下面读懂这些代码就很容易了,我们需要建立Vertex,这里我们需要有一点点地改变,在以前我们接触到的Vertex里面都不涉及到纹理,所以我们选择了CustomVertex里面不包括纹理的类型,现在我们要用CustomVertex.PositionNormalTextured,从名字就可以看出来,这个类型包括了法线还包括了位置的X,Y,Z,以及纹理坐标的TuTv

当然如果使用CustomVertex.PositionTextured 也是可以的,它不包括法线信息。

接下来我们需要为每个Vertex指定信息,我们先打断一下讲讲纹理坐标,为了通过指定纹理坐标来访问纹理中的每个图素,DX采用了一个一般化的编址方案,纹理地址由[0.0,1.0]区间内的坐标组成,这样我们就不用关心纹理的实际尺寸,例如可以使用(0.0f,0.0f) ,(1.0f,0.0f),(1.0f,1.0f),(0.0f,1.0f)把一个纹理贴到一个矩形上,同样如果(0.0f,0.0f) ,(05f,0.0f),(0.5,1.0f),(0.0f,1.0f)就是纹理的左半边。

我们可以通过TextureLoader.FromFile方法来读入图片作为纹理。

这里代码很简单里面有详细的注释,我就不多讲了,

//-----------------------------------------------------------------------------

// File: texture.cs

//

// Desc: Better than just lights and materials, 3D objects look much more

//       convincing when texture-mapped. Textures can be thought of as a sort

//       of wallpaper, that is shrinkwrapped to fit a texture. Textures are

//       typically loaded from image files, and D3DX provides a utility to

//       function to do this for us. Like a vertex buffer, textures have

//       Lock() and Unlock() functions to access (read or write) the image

//       data. Textures have a width, height, miplevel, and pixel format. The

//       miplevel is for "mipmapped" textures, an advanced performance-

//       enhancing feature which uses lower resolutions of the texture for

//       objects in the distance where detail is less noticeable. The pixel

//       format determines how the colors are stored in a texel. The most

//       common formats are the 16-bit R5G6B5 format (5 bits of red, 6-bits of

//       green and 5 bits of blue) and the 32-bit A8R8G8B8 format (8 bits each

//       of alpha, red, green, and blue).

//

//       Textures are associated with geometry through texture coordinates.

//       Each vertex has one or more sets of texture coordinates, which are

//       named tu and tv and range from 0.0 to 1.0. Texture coordinates can be

//       supplied by the geometry, or can be automatically generated using

//       Direct3D texture coordinate generation (which is an advanced feature).

//

// Copyright (c) Microsoft Corporation. All rights reserved.

//-----------------------------------------------------------------------------

using System;

using System.Drawing;

using System.Windows.Forms;

using Microsoft.DirectX;

using Microsoft.DirectX.Direct3D;

using Direct3D=Microsoft.DirectX.Direct3D;

 

namespace TextureTutorial

{

     public class Textures : Form

     {

         // Our global variables for this project

         Device device = null; // Our rendering device

         VertexBuffer vertexBuffer = null;

         Texture texture = null;

         PresentParameters presentParams = new PresentParameters();

         bool pause = false;

 

 

         public Textures()

         {

              // Set the initial size of our form

              this.ClientSize = new System.Drawing.Size(400,300);

              // And its caption

              this.Text = "Direct3D Tutorial 5 - Textures";

         }

 

         public bool InitializeGraphics()

         {

              try

              {

                   presentParams.Windowed=true; // We don't want to run fullscreen

                   presentParams.SwapEffect = SwapEffect.Discard; // Discard the frames

                   presentParams.EnableAutoDepthStencil = true; // Turn on a Depth stencil

                   presentParams.AutoDepthStencilFormat = DepthFormat.D16; // And the stencil format

                   device = new Device(0, DeviceType.Hardware, this, CreateFlags.SoftwareVertexProcessing, presentParams); //Create a device

                   device.DeviceReset += new System.EventHandler(this.OnResetDevice);

                   this.OnCreateDevice(device, null);

                   this.OnResetDevice(device, null);

                   pause = false;

 

                   return true;

              }

              catch (DirectXException)

              {

                   // Catch any errors and return a failure

                   return false;

              }

         }

         public void OnCreateDevice(object sender, EventArgs e)

         {

              Device dev = (Device)sender;

              // Now Create the VB

              vertexBuffer = new VertexBuffer(typeof(CustomVertex.PositionNormalTextured), 100, dev, Usage.WriteOnly, CustomVertex.PositionNormalTextured.Format, Pool.Default);

              vertexBuffer.Created += new System.EventHandler(this.OnCreateVertexBuffer);

              this.OnCreateVertexBuffer(vertexBuffer, null);

         }

         public void OnResetDevice(object sender, EventArgs e)

         {

              Device dev = (Device)sender;

              // Turn off culling, so we see the front and back of the triangle

              dev.RenderState.CullMode = Cull.None;

              // Turn off D3D lighting

              dev.RenderState.Lighting = false;

              // Turn on the ZBuffer

              dev.RenderState.ZBufferEnable = true;

              // Now create our texture

              texture = TextureLoader.FromFile(dev, Application.StartupPath + @"\..\..\banana.bmp");

         }

         public void OnCreateVertexBuffer(object sender, EventArgs e)

         {

              VertexBuffer vb = (VertexBuffer)sender;

              // Create a vertex buffer (100 customervertex)

              CustomVertex.PositionNormalTextured[] verts = (CustomVertex.PositionNormalTextured[])vb.Lock(0,0); // Lock the buffer (which will return our structs)

              for (int i = 0; i < 50; i++)

              {

                   // Fill up our structs

                   float theta = (float)(2 * Math.PI * i) / 49;

                   verts[2 * i].Position = new Vector3((float)Math.Sin(theta), -1, (float)Math.Cos(theta));

                   verts[2 * i].Normal = new Vector3((float)Math.Sin(theta), 0, (float)Math.Cos(theta));

                   verts[2 * i].Tu = ((float)i)/(50-1);

                   verts[2 * i].Tv = 1.0f;

                   verts[2 * i + 1].Position = new Vector3((float)Math.Sin(theta), 1, (float)Math.Cos(theta));

                   verts[2 * i + 1].Normal = new Vector3((float)Math.Sin(theta), 0, (float)Math.Cos(theta));

                   verts[2 * i + 1].Tu = ((float)i)/(50-1);

                   verts[2 * i + 1].Tv = 0.0f;

              }

              // Unlock (and copy) the data

              vb.Unlock();

         }

         private void SetupMatrices()

         {

              // For our world matrix, we will just rotate the object about the y-axis.

              device.Transform.World = Matrix.RotationAxis(new Vector3((float)Math.Cos(Environment.TickCount / 250.0f),1,(float)Math.Sin(Environment.TickCount / 250.0f)), Environment.TickCount / 1000.0f );

 

              // Set up our view matrix. A view matrix can be defined given an eye point,

              // a point to lookat, and a direction for which way is up. Here, we set the

              // eye five units back along the z-axis and up three units, look at the

              // origin, and define "up" to be in the y-direction.

              device.Transform.View = Matrix.LookAtLH( new Vector3( 0.0f, 3.0f,-5.0f ), new Vector3( 0.0f, 0.0f, 0.0f ), new Vector3( 0.0f, 1.0f, 0.0f ) );

 

              // For the projection matrix, we set up a perspective transform (which

              // transforms geometry from 3D view space to 2D viewport space, with

              // a perspective divide making objects smaller in the distance). To build

              // a perpsective transform, we need the field of view (1/4 pi is common),

              // the aspect ratio, and the near and far clipping planes (which define at

              // what distances geometry should be no longer be rendered).

              device.Transform.Projection = Matrix.PerspectiveFovLH( (float)Math.PI / 4.0f, 1.0f, 1.0f, 100.0f );

         }

 

         private void Render()

         {

              if (pause)

                   return;

 

              //Clear the backbuffer to a blue color

              device.Clear(ClearFlags.Target | ClearFlags.ZBuffer, System.Drawing.Color.Blue, 1.0f, 0);

              //Begin the scene

              device.BeginScene();

              // Setup the world, view, and projection matrices

              SetupMatrices();

              // Setup our texture. Using textures introduces the texture stage states,

              // which govern how textures get blended together (in the case of multiple

              // textures) and lighting information. In this case, we are modulating

              // (blending) our texture with the diffuse color of the vertices.

              device.SetTexture(0,texture);

              device.TextureState[0].ColorOperation = TextureOperation.Modulate;

              device.TextureState[0].ColorArgument1 = TextureArgument.TextureColor;

              device.TextureState[0].ColorArgument2 = TextureArgument.Diffuse;

              device.TextureState[0].AlphaOperation = TextureOperation.Disable;

    

              device.SetStreamSource(0, vertexBuffer, 0);

              device.VertexFormat = CustomVertex.PositionNormalTextured.Format;

              device.DrawPrimitives(PrimitiveType.TriangleStrip, 0, (4*25)-2);

              //End the scene

              device.EndScene();

              // Update the screen

              device.Present();

         }

 

         protected override void OnPaint(System.Windows.Forms.PaintEventArgs e)

         {

              this.Render(); // Render on painting

         }

         protected override void OnKeyPress(System.Windows.Forms.KeyPressEventArgs e)

         {

              if ((int)(byte)e.KeyChar == (int)System.Windows.Forms.Keys.Escape)

                   this.Dispose(); // Esc was pressed

         }

        protected override void OnResize(System.EventArgs e)

        {

            pause = ((this.WindowState == FormWindowState.Minimized) || !this.Visible);

        }

 

       

       

        /// <summary>

         /// The main entry point for the application.

         /// </summary>

         static void Main()

         {

 

            using (Textures frm = new Textures())

            {

                if (!frm.InitializeGraphics()) // Initialize Direct3D

                {

                    MessageBox.Show("Could not initialize Direct3D.  This tutorial will exit.");

                    return;

                }

                frm.Show();

 

                // While the form is still valid, render and process messages

                while(frm.Created)

                {

                    frm.Render();

                    Application.DoEvents();

                }

            }

         }

 

     }

}

这里还有一个简单的方法处理纹理,其实也差不多,看上去简单一些而已:

tex = new Texture(device, new Bitmap(this.GetType(), "puck.bmp"), Usage.Dynamic, Pool.Default);

然后在画图的时候用一句

device.SetTexture(0, tex);

就可以把纹理设置到物体上了,不过如果要进行稍微复杂的纹理操作,这个方法就不管用了。

关于纹理的东西还有很多很多,比如纹理的寻址模式,纹理包装,纹理过滤抗锯齿以及alpha混合 和多重纹理等等,这里介绍的只是九牛一毛,不过这些在后面都会慢慢介绍到。

 

例程:http://dev.gameres.com/Program/Visual/DirectX/DX93D_6.rar

DirectX9 3D 快速上手 7

By sssa2000

4/28/2005

这里我想继续写点和Mesh有关的东西,毕竟我们可能还需要对它有很多别的要求。在3D游戏的实际运用中,一般来说都是运用低多边形模型,简称低模。这样才能有更加好的速度来运行游戏,恰好DX中有提供给我们这样的函数让我们来控制读入的Mesh的复杂程度。

public void WeldVertices (
 Microsoft.DirectX.Direct3D.WeldEpsilonsFlags flags ,//标志
    Microsoft.DirectX.Direct3D.WeldEpsilons epsilons , 
    Microsoft.DirectX.Direct3D.GraphicsStream adjacencyIn ,
    Microsoft.DirectX.Direct3D.GraphicsStream adjacencyOut ,
    out int[] faceRemap ,
    Microsoft.DirectX.Direct3D.GraphicsStream vertexRemap )

这个方法能实现简化模型的目的,前2个参数用来确定怎么简化模型,

第一个标志一共包括以下几个:

Member

Value

Description

DoNotSplit

8

Instructs the weld to allow vertices to be modified only, not removed. This flag is valid only if WeldPartialMatches is set. It is useful to modify vertices so that they are equal, but not to allow vertices to be removed.

只有当WeldPartialMatches参数指定时才能生效,不允许分离定点

DoNotRemoveVertices

4

Instructs the weld to allow vertices to be modified only, not removed. This flag is valid only if WeldPartialMatches is set. It is useful to modify vertices to be equal, but not to allow vertices to be removed.

只有当WeldPartialMatches参数指定时才能生效,不能移除定点,只能修改

WeldPartialMatches

2

If a given vertex component is within epsilon, instructs the weld to modify partially matched vertices so that both components are equal. If all components are equal, one of the vertices is removed.

修改符合在WeldEpsilons结构中给定的顶点的条件

WeldAll

1

Welds all vertices that are marked by adjacency as being overlapping.

焊接所有的adjacency指定的定点

例如我们可以这样简单的调用这个方法

mesh.WeldVertices(WeldEpsilonsFlags.WeldAll, new WeldEpsilons(), null, null);

当然 前提是你必须在这之前对变量mesh进行付值。

到程序里面看看,效果还是不错的,已经简化了很多顶点。

 

或许你还觉得不够简化,没关系这里我们还可以把复杂的模型拆成小块,用下面这个函数:

public static Microsoft.DirectX.Direct3D.Mesh[] Split (
Mesh mesh , //要拆分的mesh
int[] adjacencyIn , 
System.Int32 maxSize ,// 拆分后新的Mesh的最大顶点数量
MeshFlags options , //标志
    out GraphicsStream adjacencyArrayOut , //这三个out 参数返回新mesh的一些信息
    out GraphicsStream faceRemapArrayOut ,
    out GraphicsStream vertRemapArrayOut )

 

我们也可以使用简单的重载版本的函数。

在拆分前,我们需要建立保存拆分后的各个mesh的容器,数组不错。

我们这么写:

Mesh  splitmeshes[]=meshes = Mesh.Split(mesh, null, 500, mesh.Options.Value);

 

这样我们就把mesh根据顶点的树木分成了很多个部分

在我们这个事例程序里面,通过控制对数组的遍历来实现对所有拆分出来的mesh的遍历。

完整代码如下:

using System;

using System.Drawing;

using System.Collections;

using System.ComponentModel;

using System.Windows.Forms;

using System.Data;

using Microsoft.DirectX;

using Microsoft.DirectX.Direct3D;

 

namespace Chapter7Code

{

     /// <summary>

     /// Summary description for Form1.

     /// </summary>

     public class Form1 : System.Windows.Forms.Form

     {

        private Device device = null;

        private Mesh mesh = null;

        private Material[] meshMaterials;

        private Texture[] meshTextures;

 

        // Split information

        private Mesh[] meshes = null;

        private bool drawSplit = false;

        private bool drawAllSplit = false;

        private int index = 0;

        private int lastIndexTick = System.Environment.TickCount;

 

        /// <summary>

         /// Required designer variable.

         /// </summary>

         private System.ComponentModel.Container components = null;

        private float angle = 0.0f;

 

         public Form1()

         {

              //

              // Required for Windows Form Designer support

              //

              InitializeComponent();

 

            this.SetStyle(ControlStyles.AllPaintingInWmPaint | ControlStyles.Opaque, true);

         }

 

        /// <summary>

        /// We will initialize our graphics device here

        /// </summary>

        public void InitializeGraphics()

        {

            // Set our presentation parameters

            PresentParameters presentParams = new PresentParameters();

 

            presentParams.Windowed = true;

            presentParams.SwapEffect = SwapEffect.Discard;

            presentParams.AutoDepthStencilFormat = DepthFormat.D16;

            presentParams.EnableAutoDepthStencil = true;

 

            // Create our device

            device = new Device(0, DeviceType.Hardware, this, CreateFlags.SoftwareVertexProcessing, presentParams);

 

            // Load our mesh

            LoadMesh(@"..\..\tiny.x");

 

            meshes = Mesh.Split(mesh, null, 10, mesh.Options.Value);

        }

 

        private void LoadMesh(string file)

        {

            ExtendedMaterial[] mtrl;

 

            // Load our mesh

            mesh = Mesh.FromFile(file, MeshFlags.Managed, device, out mtrl);

 

            // If we have any materials, store them

            if ((mtrl != null) && (mtrl.Length > 0))

            {

                meshMaterials = new Material[mtrl.Length];

                meshTextures = new Texture[mtrl.Length];

 

                // Store each material and texture

                for (int i = 0; i < mtrl.Length; i++)

                {

                    meshMaterials[i] = mtrl[i].Material3D;

                    if ((mtrl[i].TextureFilename != null) && (mtrl[i].TextureFilename != string.Empty))

                    {

                        // We have a texture, try to load it

                        meshTextures[i] = TextureLoader.FromFile(device, @"..\..\" + mtrl[i].TextureFilename);

                    }

                }

            }

        }

 

        private void SetupCamera()

        {

            device.Transform.Projection = Matrix.PerspectiveFovLH((float)Math.PI / 4, this.Width / this.Height, 1.0f, 10000.0f);

            device.Transform.View = Matrix.LookAtLH(new Vector3(0,0, 580.0f), new Vector3(), new Vector3(0,1,0));

            //device.RenderState.FillMode = FillMode.WireFrame;

            device.RenderState.Ambient = Color.DarkBlue;

            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;

 

        }

 

        protected override void OnPaint(System.Windows.Forms.PaintEventArgs e)

        {

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

 

            SetupCamera();

 

            device.BeginScene();

 

            // Draw our Mesh

            DrawMesh(angle / (float)Math.PI, angle / (float)Math.PI * 2.0f, angle / (float)Math.PI / 4.0f, 0.0f, 0.0f, 0.0f);

 

            device.EndScene();

 

            device.Present();

 

            this.Invalidate();

        }

 

        private void DrawMesh(float yaw, float pitch, float roll, float x, float y, float z)

        {

            angle += 0.01f;

 

        if ((System.Environment.TickCount - lastIndexTick) > 500)

        {

            index++;

            if (index >= meshes.Length)

                index = 0;

 

            lastIndexTick = System.Environment.TickCount;

        }

        device.Transform.World = Matrix.RotationYawPitchRoll(yaw, pitch, roll) * Matrix.Translation(x, y, z);

        for (int i = 0; i < meshMaterials.Length; i++)

        {

            device.Material = meshMaterials[i];

            device.SetTexture(0, meshTextures[i]);

            if (drawSplit)

            {

                if (drawAllSplit)

                {

                    foreach(Mesh m in meshes)

                        m.DrawSubset(i);

                }

                else

                {

                    meshes[index].DrawSubset(i);

                }

            }

            else

            {

                mesh.DrawSubset(i);

            }

        }

        }

        protected override void OnKeyPress(KeyPressEventArgs e)

        {

            if (e.KeyChar == ' ')

            {

                drawSplit = !drawSplit;

            }

            else if (e.KeyChar == 'm')

            {

                drawAllSplit = !drawAllSplit;

            }

        }

 

        /// <summary>

         /// Clean up any resources being used.

         /// </summary>

         protected override void Dispose( bool disposing )

         {

              if( disposing )

              {

                   if (components != null)

                   {

                        components.Dispose();

                   }

              }

              base.Dispose( disposing );

         }

 

         #region Windows Form Designer generated code

         /// <summary>

         /// Required method for Designer support - do not modify

         /// the contents of this method with the code editor.

         /// </summary>

         private void InitializeComponent()

         {

              this.components = new System.ComponentModel.Container();

              this.Size = new Size(800,600);

              this.Text = "Form1";

         }

         #endregion

 

         /// <summary>

         /// The main entry point for the application.

         /// </summary>

        static void Main()

        {

            using (Form1 frm = new Form1())

            {

                // Show our form and initialize our graphics engine

                frm.Show();

                frm.InitializeGraphics();

                Application.Run(frm);

            }

        }

     }

}

当然我们这里并不能任意的控制模型的细节程度,明天要期中答辩,突然发现自己的周记没写完,所以今天先草草结束,细节控制的下一次我们再讲吧。

By sssa2000

4/28/2005

 

DirectX9 3D 快速上手 8

By sssa2000

5/4/2005

 

上一次中途结束了本来应该讲到的控制Mesh的细节程度的方法的,这一次补上。

我们这里使用的是简单的方法,并没有涉及到场景剔出等等复杂的方法,我这里主要还是用DX9提供给我们的类库,progressive meshe

progressive meshes主要的优点就是允许我们控制顶点和面的数目,这样我们就可以灵活的控制mesh细节的显示程度。

Mesh一样,progressive meshe也都是继承自BaseMesh这个类。比较重要的属性主要有2NumberFacesNumberVertices。从字面我们就可以看出两个属性的含义。

有了前面的基础在使用progressive mesh上也变得十分的简单,首先我们需要声明一个progressive mesh的变量,然后我们看看progressive mesh的构造函数:

public ProgressiveMesh(
    Mesh mesh,
    GraphicsStream adjacency,
    GraphicsStream vertexWeights, //
顶点的权值,越高越不容易被剔出,如果设为null就全部被设为1
    int minValue, //设定被简化的最小值
    MeshFlags options
);

以下就是核心的代码:

private void LoadMesh(string file)
{
    ExtendedMaterial[] mtrl;
    GraphicsStream adj;
 
    // Load our mesh
    using(Mesh mesh = Mesh.FromFile(file, MeshFlags.Managed, device,
                out adj, out mtrl))
    {
 
        // If we have any materials, store them
        if ((mtrl != null) && (mtrl.Length > 0))
        {
            meshMaterials = new Material[mtrl.Length];
            meshTextures = new Texture[mtrl.Length];
 
            // Store each material and texture
            for (int i = 0; i < mtrl.Length; i++)
            {
                meshMaterials[i] = mtrl[i].Material3D;
                if ((mtrl[i].TextureFilename != null) &&
                    (mtrl[i].TextureFilename != string.Empty))
                {
                    // We have a texture, try to load it
                    meshTextures[i] = TextureLoader.FromFile(device,
                        @"..\..\" + mtrl[i].TextureFilename);
                }
            }
        }
 
        // Clean our main mesh
        using(Mesh tempMesh = Mesh.Clean(mesh, adj, adj))
        {
            // Create our progressive mesh
            progressiveMesh = new ProgressiveMesh(tempMesh, adj,
                null, 1, MeshFlags.SimplifyVertex);
 
            // Set the initial mesh to the max
            progressiveMesh.NumberFaces = progressiveMesh.MaxFaces;
            progressiveMesh.NumberVertices = progressiveMesh.MaxVertices;
        }
    }
}

其实这里大部分代码和前面的关于Mesh那一节的代码差不多,关于progressiveMesh,微软的SDK也有一个例子,不过那个例子太过于庞大,大部分都是在处理UI方面,真正涉及到progressiveMesh的部分还是很少的。

以下是代码的事例图和完整代码:

这个例子里可以切换线框渲染模式和实体作色模式。

using System;

using System.Drawing;

using System.Collections;

using System.ComponentModel;

using System.Windows.Forms;

using System.Data;

using Microsoft.DirectX;

using Microsoft.DirectX.Direct3D;

 

namespace progressiveMesh

{

     /// <summary>

     /// Summary description for Form1.

     /// </summary>

     public class Form1 : System.Windows.Forms.Form

     {

        private Device device = null;

        private ProgressiveMesh progressiveMesh = null;

        private Material[] meshMaterials;

        private Texture[] meshTextures;

 

        private Microsoft.DirectX.Direct3D.Font font = null;

        private float cameraPos = 8.0f;

        private const int MoveAmount = 2;

 

        /// <summary>

         /// Required designer variable.

         /// </summary>

         private System.ComponentModel.Container components = null;

        private float angle = 0.0f;

 

         public Form1()

         {

              //

              // Required for Windows Form Designer support

              //

              InitializeComponent();

 

            this.SetStyle(ControlStyles.AllPaintingInWmPaint | ControlStyles.Opaque, true);

         }

 

        /// <summary>

        /// We will initialize our graphics device here

        /// </summary>

        public void InitializeGraphics()

        {

            // Set our presentation parameters

            PresentParameters presentParams = new PresentParameters();

 

            presentParams.Windowed = true;

            presentParams.SwapEffect = SwapEffect.Discard;

            presentParams.AutoDepthStencilFormat = DepthFormat.D16;

            presentParams.EnableAutoDepthStencil = true;

 

            // Create our device

            device = new Device(0, DeviceType.Hardware, this, CreateFlags.SoftwareVertexProcessing, presentParams);

 

            // Load our mesh

            LoadMesh(@"..\..\SprintRacer.x");

 

            // Create our font

            font = new Microsoft.DirectX.Direct3D.Font(device, new System.Drawing.Font

                ("Arial", 14.0f, FontStyle.Bold | FontStyle.Italic));

        }

 

        private void LoadMesh(string file)

        {

            ExtendedMaterial[] mtrl;

            GraphicsStream adj;

 

            // Load our mesh

            using(Mesh mesh = Mesh.FromFile(file, MeshFlags.Managed, device,

                        out adj, out mtrl))

            {

 

                // If we have any materials, store them

                if ((mtrl != null) && (mtrl.Length > 0))

                {

                    meshMaterials = new Material[mtrl.Length];

                    meshTextures = new Texture[mtrl.Length];

 

                    // Store each material and texture

                    for (int i = 0; i < mtrl.Length; i++)

                    {

                        meshMaterials[i] = mtrl[i].Material3D;

                        if ((mtrl[i].TextureFilename != null) &&

                            (mtrl[i].TextureFilename != string.Empty))

                        {

                            // We have a texture, try to load it

                            meshTextures[i] = TextureLoader.FromFile(device,

                                @"..\..\" + mtrl[i].TextureFilename);

                        }

                    }

                }

 

                // Clean our main mesh

                using(Mesh tempMesh = Mesh.Clean(CleanType.Simplification ,mesh, adj, adj))

                {

                    // Create our progressive mesh

                    progressiveMesh = new ProgressiveMesh(tempMesh, adj,

                        null, 1, MeshFlags.SimplifyVertex);

 

                    // Set the initial mesh to the max

                    progressiveMesh.NumberFaces = progressiveMesh.MaxFaces;

                    progressiveMesh.NumberVertices = progressiveMesh.MaxVertices;

                }

            }

        }

 

        private void SetupCamera()

        {

            device.Transform.Projection = Matrix.PerspectiveFovLH((float)Math.PI / 4, this.Width / this.Height, 1.0f, 10000.0f);

            device.Transform.View = Matrix.LookAtLH(new Vector3(0,0, cameraPos),

                new Vector3(), new Vector3(0,1,0));

 

            device.RenderState.Ambient = Color.Blue;

            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;

 

        }

 

        protected override void OnPaint(System.Windows.Forms.PaintEventArgs e)

        {

            device.Clear(ClearFlags.Target | ClearFlags.ZBuffer, Color.Blue , 1.0f, 0);

 

            SetupCamera();

 

            device.BeginScene();

 

            // Draw our Mesh

            DrawMesh(angle / (float)Math.PI, angle / (float)Math.PI * 2.0f, angle / (float)Math.PI / 4.0f, 0.0f, 0.0f, 0.0f);

 

            font.DrawText(null, string.Format("Number vertices in mesh: {0}",

                ((BaseMesh)progressiveMesh).NumberVertices), new Rectangle(10, 10, 0, 0),

                DrawTextFormat.NoClip, Color.BlanchedAlmond);

 

            font.DrawText(null, string.Format("Number faces in mesh: {0}",

                ((BaseMesh)progressiveMesh).NumberFaces), new Rectangle(10, 30, 0, 0),

                DrawTextFormat.NoClip, Color.BlanchedAlmond);

 

            device.EndScene();

 

            device.Present();

 

            this.Invalidate();

        }

 

        private void DrawMesh(float yaw, float pitch, float roll, float x, float y, float z)

        {

            angle += 0.11f;

 

            device.Transform.World = Matrix.RotationYawPitchRoll(yaw, pitch, roll) * Matrix.Translation(x, y, z);

            for (int i = 0; i < meshMaterials.Length; i++)

            {

                device.Material = meshMaterials[i];

                device.SetTexture(0, meshTextures[i]);

                progressiveMesh.DrawSubset(i);

            }

        }

        protected override void OnKeyPress(KeyPressEventArgs e)

        {

            if (e.KeyChar == '+')

            {

                cameraPos += (MoveAmount * 2);

                progressiveMesh.NumberVertices =

                    ((BaseMesh)progressiveMesh).NumberVertices - MoveAmount;

 

                progressiveMesh.NumberFaces =

                    ((BaseMesh)progressiveMesh).NumberFaces - MoveAmount;

            }

            if (e.KeyChar == '-')

            {

                cameraPos -= (MoveAmount * 2);

                progressiveMesh.NumberVertices =

                    ((BaseMesh)progressiveMesh).NumberVertices + MoveAmount;

 

                progressiveMesh.NumberFaces =

                    ((BaseMesh)progressiveMesh).NumberFaces + MoveAmount;

            }

            if (e.KeyChar == 'w')

                device.RenderState.FillMode = FillMode.WireFrame;

 

            if (e.KeyChar == 's')

                device.RenderState.FillMode = FillMode.Solid;

        }

 

        /// <summary>

         /// Clean up any resources being used.

         /// </summary>

         protected override void Dispose( bool disposing )

         {

              if( disposing )

              {

                   if (components != null)

                   {

                       components.Dispose();

                   }

              }

              base.Dispose( disposing );

         }

 

         #region Windows Form Designer generated code

         /// <summary>

         /// Required method for Designer support - do not modify

         /// the contents of this method with the code editor.

         /// </summary>

         private void InitializeComponent()

         {

              this.components = new System.ComponentModel.Container();

              this.Size = new Size(800,600);

              this.Text = "Form1";

         }

         #endregion

 

         /// <summary>

         /// The main entry point for the application.

         /// </summary>

        static void Main()

        {

            using (Form1 frm = new Form1())

            {

                // Show our form and initialize our graphics engine

                frm.Show();

                frm.InitializeGraphics();

                Application.Run(frm);

            }

        }

     }

}

 

这里我们已经实现了能自由控制显示的细节问题,通过progressiveMesh 我们可以很方便的完成这一点,接下来,顺着这个话题我们来讨论一下如何增加细节的显示程度,也就是说我们将要把读入的Mesh的细节程度增多。这里我们部探讨原理,因为我数学也是很差,我们可以借助Patch Meshes 来实现。

Patch Meshes通过增加读入的Mesh的顶点来对细节程度进行增加。

public PatchMesh(ID3DXPatchMesh); 这就是PathMesh的构造函数,只要传入一个读入的Mesh就可以了。接下来我们将用一个例子展示怎么增加显示的细节程度

private void CreatePatchMesh(string file, float tessLevel)
{
    if (tessLevel < 1.0f) //如果小于默认的值,不再增加,直接返回
        return;
 
    if (mesh != null)
        mesh.Dispose();
 
    using (Mesh tempmesh = LoadMesh(file))
    {
        using (PatchMesh patch = PatchMesh.CreateNPatchMesh(tempmesh))
        {
            // Calculate the new number of faces/vertices
            int numberFaces = (int)(tempmesh.NumberFaces
                * Math.Pow(tessLevel, 3));
            int numberVerts = (int)(tempmesh.NumberVertices
                * Math.Pow(tessLevel, 3));
 
            mesh = new Mesh(numberFaces, numberVerts, MeshFlags.Managed
                | MeshFlags.Use32Bit, tempmesh.VertexFormat, device);
 
            // Tessellate the patched mesh
            patch.Tessellate(tessLevel, mesh); //tessLevel的基础上把mesh分成小方格
        }
    }
}
 
private Mesh LoadMesh(string file)
{
……………………….
if ((mesh.VertexFormat & VertexFormats.Normal) != VertexFormats.Normal)
//如果没有法线信息,就要计算法线,发现信息会在上面的Tessellate方法中被用到。
    {
        // We must have normals for our patch meshes
        Mesh tempMesh = mesh.Clone(mesh.Options.Value,
            mesh.VertexFormat | VertexFormats.Normal, device);
 
        tempMesh.ComputeNormals();
 
        mesh.Dispose();
        mesh = tempMesh;
    }
    return mesh;
}
 

以下就是运行的图例

可以明显地看出,我们的程序还是有明显的效果的,不过对于增加的细节,程序将变得十分的缓慢。

以下是全部的代码:

using System;

using System.Drawing;

using System.Collections;

using System.ComponentModel;

using System.Windows.Forms;

using System.Data;

using Microsoft.DirectX;

using Microsoft.DirectX.Direct3D;

 

namespace PathMesh

{

     /// <summary>

     /// Summary description for Form1.

     /// </summary>

     public class Form1 : System.Windows.Forms.Form

     {

        private Device device = null;

        private Mesh mesh = null;

        private Material[] meshMaterials;

        private Texture[] meshTextures;

 

        private float tessLevel = 1.0f;

        private const float tessIncrement = 1.0f;

        private string filename = @"..\..\sphere.x";

 

        private Microsoft.DirectX.Direct3D.Font font = null;

 

        /// <summary>

         /// Required designer variable.

         /// </summary>

         private System.ComponentModel.Container components = null;

        private float angle = 0.0f;

 

         public Form1()

         {

              //

              // Required for Windows Form Designer support

              //

              InitializeComponent();

 

            this.SetStyle(ControlStyles.AllPaintingInWmPaint | ControlStyles.Opaque, true);

         }

 

        /// <summary>

        /// We will initialize our graphics device here

        /// </summary>

        public void InitializeGraphics()

        {

            // Set our presentation parameters

            PresentParameters presentParams = new PresentParameters();

 

            presentParams.Windowed = true;

            presentParams.SwapEffect = SwapEffect.Discard;

            presentParams.AutoDepthStencilFormat = DepthFormat.D16;

            presentParams.EnableAutoDepthStencil = true;

 

            // Create our device

            device = new Device(0, DeviceType.Hardware, this, CreateFlags.SoftwareVertexProcessing, presentParams);

 

            // Create our patch mesh

            CreatePatchMesh(filename, tessLevel);

 

            // Create our font

            font = new Microsoft.DirectX.Direct3D.Font(device, new System.Drawing.Font

                ("Arial", 14.0f, FontStyle.Bold | FontStyle.Italic));

 

            // Default to wireframe mode first

            device.RenderState.FillMode = FillMode.WireFrame;

        }

 

        /// <summary>

        /// Creates a temporary mesh object, and a patch mesh from it

        /// </summary>

        /// <param name="file">The original mesh to use</param>

        /// <param name="tessLevel">The tesselation level</param>

        private void CreatePatchMesh(string file, float tessLevel)

        {

            if (tessLevel < 1.0f) // Nothing to do

                return;

 

            if (mesh != null)

                mesh.Dispose();

 

            using (Mesh tempmesh = LoadMesh(file))

            {

                using (PatchMesh patch = PatchMesh.CreateNPatchMesh(tempmesh))

                {

                    // Calculate the new number of faces/vertices

                    int numberFaces = (int)(tempmesh.NumberFaces

                        * Math.Pow(tessLevel, 3));

                    int numberVerts = (int)(tempmesh.NumberVertices

                        * Math.Pow(tessLevel, 3));

 

                    mesh = new Mesh(numberFaces, numberVerts, MeshFlags.Managed

                        | MeshFlags.Use32Bit, tempmesh.VertexFormat, device);

 

                    // Tessellate the patched mesh

                    patch.Tessellate(tessLevel, mesh);

                }

            }

        }

 

        /// <summary>

        /// Load a mesh from a file and return it

        /// </summary>

        /// <param name="file">The file to load</param>

        /// <returns>The created mesh</returns>

        private Mesh LoadMesh(string file)

        {

            ExtendedMaterial[] mtrl;

 

            // Load our mesh

            Mesh mesh = Mesh.FromFile(file, MeshFlags.Managed, device,

                        out mtrl);

 

            // If we have any materials, store them

            if ((mtrl != null) && (mtrl.Length > 0))

            {

                meshMaterials = new Material[mtrl.Length];

                meshTextures = new Texture[mtrl.Length];

 

                // Store each material and texture

                for (int i = 0; i < mtrl.Length; i++)

                {

                    meshMaterials[i] = mtrl[i].Material3D;

                    if ((mtrl[i].TextureFilename != null) &&

                        (mtrl[i].TextureFilename != string.Empty))

                    {

                        // We have a texture, try to load it

                        meshTextures[i] = TextureLoader.FromFile(device,

                            @"..\..\" + mtrl[i].TextureFilename);

                    }

                }

            }

 

            if ((mesh.VertexFormat & VertexFormats.Normal) != VertexFormats.Normal)

            {

                // We must have normals for our patch meshes

                Mesh tempMesh = mesh.Clone(mesh.Options.Value,

                    mesh.VertexFormat | VertexFormats.Normal, device);

 

                tempMesh.ComputeNormals();

 

                mesh.Dispose();

                mesh = tempMesh;

            }

            return mesh;

        }

 

        private void SetupCamera()

        {

            device.Transform.Projection = Matrix.PerspectiveFovLH(

                (float)Math.PI / 4, this.Width / this.Height, 1.0f, 100.0f);

 

            device.Transform.View = Matrix.LookAtLH(new Vector3(0,0, 5.0f),

                new Vector3(), new Vector3(0,1,0));

 

            device.Lights[0].Type = LightType.Directional;

            device.Lights[0].Diffuse = Color.DarkKhaki;

            device.Lights[0].Direction = new Vector3(0, 0, -1);

            device.Lights[0].Update();

            device.Lights[0].Enabled = true;

 

        }

 

        protected override void OnPaint(System.Windows.Forms.PaintEventArgs e)

        {

            device.Clear(ClearFlags.Target | ClearFlags.ZBuffer, Color.CornflowerBlue, 1.0f, 0);

 

            SetupCamera();

 

            device.BeginScene();

 

            // Draw our Mesh

            DrawMesh(angle / (float)Math.PI, angle / (float)Math.PI * 2.0f, angle / (float)Math.PI / 4.0f, 0.0f, 0.0f, 0.0f);

 

            font.DrawText(null, string.Format

                ("Number Vertices: {0}\r\nNumber Faces: {1}",

                mesh.NumberVertices, mesh.NumberFaces),

                new Rectangle(10,10,0,0),

                DrawTextFormat.NoClip, Color.Black);

 

            device.EndScene();

 

            device.Present();

 

            this.Invalidate();

        }

 

        private void DrawMesh(float yaw, float pitch, float roll, float x, float y, float z)

        {

            angle += 0.01f;

 

            device.Transform.World = Matrix.RotationYawPitchRoll(yaw, pitch, roll) * Matrix.Translation(x, y, z);

            for (int i = 0; i < meshMaterials.Length; i++)

            {

                device.Material = meshMaterials[i];

                device.SetTexture(0, meshTextures[i]);

                mesh.DrawSubset(i);

            }

        }

        protected override void OnKeyPress(KeyPressEventArgs e)

        {

            if (e.KeyChar == '+')

            {

                tessLevel += tessIncrement;

                CreatePatchMesh(filename, tessLevel);

            }

            if (e.KeyChar == '-')

            {

                tessLevel -= tessIncrement;

                CreatePatchMesh(filename, tessLevel);

            }

            if (e.KeyChar == 'c')

            {

                filename = @"..\..\cube.x";

                tessLevel = 1.0f;

                CreatePatchMesh(filename, tessLevel);

            }

            if (e.KeyChar == 'o')

            {

                filename = @"..\..\sphere.x";

                tessLevel = 1.0f;

                CreatePatchMesh(filename, tessLevel);

            }

            if (e.KeyChar == 't')

            {

                filename = @"..\..\tiger.x";

                tessLevel = 1.0f;

                CreatePatchMesh(filename, tessLevel);

            }

            if (e.KeyChar == 'w')

                device.RenderState.FillMode = FillMode.WireFrame;

 

            if (e.KeyChar == 's')

                device.RenderState.FillMode = FillMode.Solid;

 

        }

 

        /// <summary>

         /// Clean up any resources being used.

         /// </summary>

         protected override void Dispose( bool disposing )

         {

              if( disposing )

              {

                   if (components != null)

                   {

                       components.Dispose();

                   }

              }

              base.Dispose( disposing );

         }

 

         #region Windows Form Designer generated code

         /// <summary>

         /// Required method for Designer support - do not modify

         /// the contents of this method with the code editor.

         /// </summary>

         private void InitializeComponent()

         {

              this.components = new System.ComponentModel.Container();

              this.Size = new Size(800,600);

              this.Text = "Form1";

         }

         #endregion

 

         /// <summary>

         /// The main entry point for the application.

         /// </summary>

        static void Main()

        {

            using (Form1 frm = new Form1())

            {

                // Show our form and initialize our graphics engine

                frm.Show();

                frm.InitializeGraphics();

                Application.Run(frm);

            }

        }

     }

}

 

http://dev.gameres.com/Program/Visual/DirectX/DX93D_8.rar

By sssa2000

 

posted @ 2005-05-06 09:08  沉默天蝎的学习汇集  阅读(1091)  评论(0编辑  收藏  举报