游戏开发论坛

 找回密码
 立即注册
搜索
查看: 3504|回复: 3

DirectX9 3D 快速上手 5

[复制链接]

44

主题

248

帖子

274

积分

中级会员

Rank: 3Rank: 3

积分
274
发表于 2005-4-18 23:07:00 | 显示全部楼层 |阅读模式
                                                 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;
                device.SetTexture(0, meshTextures);
                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//远裁减平面
);
和非托管的相比,仅仅少了一个参数。定义宽高比的作用是,如果用户改变了窗口的大小,那么仍然就可以正确的显示物体的大小。最后两个参数是 近远裁减平面,必须为这两个平面指定2个Z值,一般来说,假设物体将在Z轴的1.0f和1000f之间运动时,就分别把这两个参数设为1.0f,1000.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个参数指向跟随物体的方向,把相机位置设置为这个物体的周围。前面我们提过,这类相机不能绕自身旋转。
好,让我们来简单实现这个功能,我们想让汽车前进后退的时候能让摄像机来跟随它,首先,我们必须得到读入的Mesh的x,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 = mtrl.Material3D;
                    if ((mtrl.TextureFilename != null) && (mtrl.TextureFilename != string.Empty))
                    {
                        // We have a texture, try to load it
                        meshTextures = TextureLoader.FromFile(device, @"..\..\" + mtrl.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;
                device.SetTexture(0, meshTextures);
                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
4/18/2005

sf_20054182370.doc

106 KB, 下载次数:

2

主题

21

帖子

21

积分

注册会员

Rank: 2

积分
21
发表于 2005-4-19 14:18:00 | 显示全部楼层

Re:DirectX9 3D 快速上手 5

这么好地帖子怎么没人顶哩!!

0

主题

6

帖子

6

积分

新手上路

Rank: 1

积分
6
发表于 2005-7-18 11:24:00 | 显示全部楼层

Re:DirectX9 3D 快速上手 5

我顶了!!!!!!!!!!!!!!!!!!!!!!!

3

主题

27

帖子

27

积分

注册会员

Rank: 2

积分
27
发表于 2006-3-10 19:26:00 | 显示全部楼层

Re:DirectX9 3D 快速上手 5

我顶一个
您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

作品发布|文章投稿|广告合作|关于本站|游戏开发论坛 ( 闽ICP备17032699号-3 )

GMT+8, 2026-1-23 21:13

Powered by Discuz! X3.4

Copyright © 2001-2021, Tencent Cloud.

快速回复 返回顶部 返回列表