DirectX9 3D 快速上手 5 - 中国WEB开发者网络 (http://www.webasp.net) -- 技术教程 (http://www.webasp.net/article/) --- DirectX9 3D 快速上手 5 (http://www.webasp.net/article/24/23793.htm) |
| -- 作者:未知 -- 发布日期: 2005-05-12 |
DirectX9 3D 快速上手 5 By sssa2000 这一章的内容相对很简单,控制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); } } public: void RotateX( float angle ); public function RotateX( angle : float ); 这里我们处理小车前后移动的时候,没有直接使用世界变换,而是对摄影机进行了操作,这也很好理解,为了达到这个目的,我把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(@"..\.. r.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(@"..\.. r.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); } } } } 好幸库,结束。 |
| webasp.net |