DirectX9 3D 快速上手 8

80酷酷网    80kuku.com

  

 
上一次中途结束了本来应该讲到的控制Mesh的细节程度的方法的,这一次补上。
我们这里使用的是简单的方法,并没有涉及到场景剔出等等复杂的方法,我这里主要还是用DX9提供给我们的类库,progressive meshe。
progressive meshes主要的优点就是允许我们控制顶点和面的数目,这样我们就可以灵活的控制mesh细节的显示程度。
和Mesh一样,progressive meshe也都是继承自BaseMesh这个类。比较重要的属性主要有2个NumberFaces和NumberVertices。从字面我们就可以看出两个属性的含义。
有了前面的基础在使用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("..\.. printRacer.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 = "..\.. phere.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 = "..\.. phere.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);
            }
        }
     }
}

分享到
  • 微信分享
  • 新浪微博
  • QQ好友
  • QQ空间
点击: