分享
 
 
 

DirectX9 3D 快速上手 5

王朝other·作者佚名  2006-01-31
窄屏简体版  字體: |||超大  

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);

}

}

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(@"..\..\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

4/18/2005

 
 
 
免责声明:本文为网络用户发布,其观点仅代表作者个人观点,与本站无关,本站仅提供信息存储服务。文中陈述内容未经本站证实,其真实性、完整性、及时性本站不作任何保证或承诺,请读者仅作参考,并请自行核实相关内容。
2023年上半年GDP全球前十五强
 百态   2023-10-24
美众议院议长启动对拜登的弹劾调查
 百态   2023-09-13
上海、济南、武汉等多地出现不明坠落物
 探索   2023-09-06
印度或要将国名改为“巴拉特”
 百态   2023-09-06
男子为女友送行,买票不登机被捕
 百态   2023-08-20
手机地震预警功能怎么开?
 干货   2023-08-06
女子4年卖2套房花700多万做美容:不但没变美脸,面部还出现变形
 百态   2023-08-04
住户一楼被水淹 还冲来8头猪
 百态   2023-07-31
女子体内爬出大量瓜子状活虫
 百态   2023-07-25
地球连续35年收到神秘规律性信号,网友:不要回答!
 探索   2023-07-21
全球镓价格本周大涨27%
 探索   2023-07-09
钱都流向了那些不缺钱的人,苦都留给了能吃苦的人
 探索   2023-07-02
倩女手游刀客魅者强控制(强混乱强眩晕强睡眠)和对应控制抗性的关系
 百态   2020-08-20
美国5月9日最新疫情:美国确诊人数突破131万
 百态   2020-05-09
荷兰政府宣布将集体辞职
 干货   2020-04-30
倩女幽魂手游师徒任务情义春秋猜成语答案逍遥观:鹏程万里
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案神机营:射石饮羽
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案昆仑山:拔刀相助
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案天工阁:鬼斧神工
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案丝路古道:单枪匹马
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案镇郊荒野:与虎谋皮
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案镇郊荒野:李代桃僵
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案镇郊荒野:指鹿为马
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案金陵:小鸟依人
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案金陵:千金买邻
 干货   2019-11-12
 
推荐阅读
 
 
 
>>返回首頁<<
 
靜靜地坐在廢墟上,四周的荒凉一望無際,忽然覺得,淒涼也很美
© 2005- 王朝網路 版權所有