分享
 
 
 

C#+Direct3D9.0开发实例之月亮绕着地球转

王朝c#·作者佚名  2008-05-19
窄屏简体版  字體: |||超大  

一、建立空窗体

新建一个工程,添加引用,并导入名称空间。

加入一个设备对象变量:

private Microsoft.DirectX.Direct3D.Device device = null;

添加初始化图形函数,并在这里面对设备对象进行实例化:

public void InitializeGraphics()

{

PresentParameters presentParams = new PresentParameters();

presentParams.Windowed = true;

presentParams.SwapEffect = SwapEffect.Flip;

presentParams.AutoDepthStencilFormat = DepthFormat.D16;

presentParams.EnableAutoDepthStencil = true;

device = new Microsoft.DirectX.Direct3D.Device(0, Microsoft.DirectX.Direct3D.DeviceType.Hardware, this,

CreateFlags.HardwareVertexProcessing, presentParams);

}

当程序执行时,需要绘制场景,代码在这个函数里:

public void Render()

{

// 清空设备,并准备显示下一帧。

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

// 设置照相机的位置

SetupCamera();

//开始场景

device.BeginScene();

if(meshLoaded)

{

mesh.Render(meshLoc);

}

device.EndScene();

//显示设备内容。

device.Present();

}

设置照相机的位置:

private void SetupCamera()

{

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

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

}

现在改变主函数,调用我们写的初始化函数,并显示场景:

[STAThread]

static void Main()

{

using (Form1 EarthForm = new Form1())

{

EarthForm.InitializeGraphics();

EarthForm.Show();

while(EarthForm.Created)

{

EarthForm.Render();

Application.DoEvents();

}

EarthForm.Dispose();

}

运行程序,会显示一个空的窗体。

二、加入地球:

在这一步里需创建一个3D网格对象,来作为要显示的地球,为此,在工程中新加入一个类Earth,此类可以包含所创建的网格对象的信息。

加入一些相关变量,含义见注释:

public class Earth : BaseEarth

{

private Material[] mMaterials; //保存材质

private Texture[] mTextures; //保存纹理

private Matrix locationOffset; //用来保存网格对象的相对位置

private Mesh mMesh = null; //三角形网格对象

private Device meshDevice; //需要显示在哪个设备上。

}

在构造函数中,把Device设备拷贝到私有成员变量,这样就可以在这个类的其它方法内使用它,另外就是把位置变量进行赋值:

public Earth(ref Device device, Matrix location): base(ref device)

{

meshDevice = device;

locationOffset = location;

}

下面这个函数是装入.X文件。

public bool LoadMesh(string meshfile)

{

ExtendedMaterial[] mtrl;

try

{

// 装载文件

mMesh = Mesh.FromFile(meshfile, MeshFlags.Managed, meshDevice, out mtrl);

// 如果有材质的话,装入它们

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

{

mMaterials = new Material[mtrl.Length];

mTextures = new Texture[mtrl.Length];

// 得到材质和纹理

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

{

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

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

{

//前面得到的纹理的路径是相对路径,需要保存的是绝对路径,通过应用程序路径可以获得

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

}

}

}

return true;

}

catch

{

return false;

}

}

在这个方法内,使用Mesh.FromFile()这个方法,从给定的文件名中找到.X文件,并装入相关数据,一旦数据格式设置完成,可以从此文件中找到材质和贴图信息,并把它存放在数组中,并通过文件路径,得到纹理文件文件的路径,最后返回真值,如果整个过程出现错误,返回假值。

下面这个Render()方法,是把此对象,即地球显示在设备对象上,此方法较简单,通过变形操作来得到网格对象的X,Y,Z坐标,接着设置网格对象的材质和纹理,最后,将每个材质和纹理应用到每个网格。

public void Render(Matrix worldTransform)

{

/把位置变为世界坐标

meshDevice.Transform.World = Matrix.Multiply(locationOffset, worldTransform);

//绘制网格

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

{

meshDevice.Material = mMaterials[i];

meshDevice.SetTexture(0, mTextures[i]);

mMesh.DrawSubset(i);

}

}

现在回到窗体代码中,添加引用网格对象的相关变量:

private Earth mesh = null;

private Matrix meshLoc;

private bool meshLoaded = false;

在图形初始化函数中,需要对网格对象进行初始化。加入下面的代码:

meshLoc = Matrix.Identity;

meshLoc.M41 = 2.0f;

mesh = new Earth(ref device, meshLoc);

if (mesh.LoadMesh(@"..\..\earth.x"))

{

meshLoaded = true;

}

代码第一句把网格对象的位置定为原点,接着偏移X轴2个单位,接下来从文件中得到此.X文件。如果成功设置,meshLoaded置为真。注意,这里有一个.X文件,在源代码中有此文件。

在设置相机的函数中,加入一盏灯光:

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;

此灯光较简单,仅为一个直射型白光灯。

最后,在Render()方法中,调用网格对象的Render()方法,以显示地球。

三、使地球旋转

前面用一个网格对象来建立地球,但此类没有平移,旋转及缩放等方法,下面就加入这些方法,因为这些方法具有通用性,因此可以新建一个类,把这些方法写在这些类中,使地球对象成为它的派生类。

在工程中新添加一个类:BaseEarth;

加入进行平移、旋转、缩放的变量:

private float xloc = 0.0f;

private float yloc = 0.0f;

private float zloc = 0.0f;

private float xrot = 0.0f;

private float yrot = 0.0f;

private float zrot = 0.0f;

private float xscale = 1.0f;

private float yscale = 1.0f;

private float zscale = 1.0f;

加入相应的属性代码:

public float XLoc

{

get

{

return xloc;

}

set

{

xloc = value;

}

}

…………

在Render()虚函数中,应用平移、旋转及缩放。

public virtual void Render()

{

objdevice.MultiplyTransform(TransformType.World,Matrix.Translation(xloc, yloc, zloc));

objdevice.MultiplyTransform(TransformType.World,Matrix.RotationAxis(new Vector3(1.0f, 0.0f, 0.0f), xrot));

objdevice.MultiplyTransform(TransformType.World,Matrix.RotationAxis(new Vector3(0.0f, 1.0f, 0.0f), yrot));

objdevice.MultiplyTransform(TransformType.World,Matrix.RotationAxis(new Vector3(0.0f, 0.0f, 1.0f), zrot));

objdevice.MultiplyTransform(TransformType.World,Matrix.Scaling(xscale, yscale, zscale));

return;

}

现在回到地球类,需要将其改为新类的派生类,同时更改构造函数,另外,在Render()方法中,应先调用基类的Render()方法:

public override void Render()

{

base.Render();

//把位置变为世界坐标

// meshDevice.Transform.World = Matrix.Multiply(locationOffset, worldTransform);

//绘制网格

。。。。。。

}

现在,由于在基类中可以设置对象位置,因此,可以把与locationOffset相关,即与设置位置的变量及语句注释掉。

四、加入月球

在这一步加入月球,实际上是再创建一个网格对象新实例,只是把纹理进行更改即可,为了代码模块性更好,把两个对象放在一个新类CModel中,在工程中新添加一个类CModel,并声明对象实例。

public class cModel

{

private cMeshObject mesh1 = null;

private cMeshObject mesh2 = null;

private bool modelloaded;

}

把窗口代码中的Load()事件,放在CModel中,这次不仅生成了地球,而且生成了月球。

public void Load(ref Device device)

{

mesh1 = new Earth(ref device);

mesh2 = new Earth(ref device);

if (mesh1.LoadMesh(@"..\..\earth2.x"))

{

modelloaded = true;

}

else

{

modelloaded = false;

}

if (mesh2.LoadMesh(@"..\..\moon.x"))

{

mesh2.XLoc += 20.0f;

modelloaded = true;

}

else

{

modelloaded = false;

}

}

下面的Update()方法中,参数dir 用来判断是顺时针旋转还是逆时针旋转,另外,地球和月球绕Y轴增加的角度大小不同,也就决定了二者旋转的速度不同。

public void Update(int dir)

{

if(dir > 0)

{

mesh1.YRot += 0.02f;

mesh2.YRot += 0.05f;

}

else if(dir < 0)

{

mesh1.YRot -= 0.02f;

mesh2.YRot -= 0.05f;

}

}

在下面的render()方法中,生成显示月球和地球:

public void Render(ref Device device)

{

device.Transform.World = Matrix.Identity;

if(modelloaded)

{

mesh1.Render();

mesh2.Render();

}

}

把窗口代码中的加入灯光的方法,也放在此类中:

public void LoadLights(ref Device device)

{

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

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

device.Lights[0].Position = new Vector3(0.0f, 0.0f, 25.0f);

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

}

public void Light(ref Device device)

{

device.Lights[0].Update();

device.Lights[0].Enabled = true;

}

五、与鼠标交互操作

为了实现与键盘、鼠标交互,新添加一个类:CMouse,添加引用Microsoft.DirectX.DirectInput,并添加命名空间。加入相关变量:

private Microsoft.DirectX.DirectInput.Device mouse = null;

public System.Threading.AutoResetEvent MouseUpdated;

private float x, y, z = 0.0f;

private byte[] buttons;

在下面的构造函数代码中,首先创建鼠标设备,并初始化回调事件:

public CMouse(System.Windows.Forms.Control control)

{

mouse = new Microsoft.DirectX.DirectInput.Device(SystemGuid.Mouse);

mouse.SetCooperativeLevel(control, CooperativeLevelFlags.Background | CooperativeLevelFlags.NonExclusive);

mouse.Properties.AxisModeAbsolute = false;

MouseUpdated = new System.Threading.AutoResetEvent(false);

mouse.SetEventNotification(MouseUpdated);

mouse.Acquire();

Update();

}

下面的Update()方法中获得鼠标的坐标值,并赋给私有成员变量:

public void Update()

{

MouseState state = mouse.CurrentMouseState;

x = state.X;

y = state.Y;

z = state.Z;

buttons = state.GetMouseButtons();

}

还需要有一个函数来检测鼠标左键是否按下:

public bool LeftButtonDown

{

get

{

bool a;

return a = (buttons[0] != 0);

}

}

六、大结局

现在已经做完了准备工作,返回到窗口代码中,需要对这里的代码重新进行一些调整:

在图形初始化函数中创建一个CModel类及CMouse类:

private CModel model = null;

private CMouse mouse = null;

private bool leftbuttondown = false;

private float mousexloc;

添加对鼠标初始化的方法:

public void InitializeInput()

{

mouse = new CMouse(this);

}

添加UpdateInputState()方法,当按下鼠标左键时,将leftbuttondown值设置为真,当鼠标抬起时,将mousexloc置0:

private void UpdateInputState()

{

mouse.Update();

if (mouse.LeftButtonDown)

{

if(leftbuttondown == false)

{

mousexloc = 0.0f;

leftbuttondown = true;

}

else

{

mousexloc = -mouse.X;

}

}

else

{

leftbuttondown = false;

mousexloc = 0.0f;

}

}

在此程序中,只对X值进行了操作,即只能左右转。

Render()方法更新如下:

public void Render()

{

UpdateInputState();

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

SetupCamera();

device.BeginScene();

model.Update((int)mousexloc);

model.Light(ref device);

model.Render(ref device);

device.EndScene();

device.Present();

}

最后更改Main()主函数:

static void Main()

{

using (Form1 EarthForm = new Form1())

{

EarthForm.InitializeGraphics();

EarthForm.InitializeInput();

EarthForm.Show();

while(EarthForm.Created)

{

EarthForm.Render();

Application.DoEvents();

}

EarthForm.Dispose();

}

运行程序,按下鼠标左键拖动,即可旋转月球与地球。

 
 
 
免责声明:本文为网络用户发布,其观点仅代表作者个人观点,与本站无关,本站仅提供信息存储服务。文中陈述内容未经本站证实,其真实性、完整性、及时性本站不作任何保证或承诺,请读者仅作参考,并请自行核实相关内容。
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- 王朝網路 版權所有