📄 9-7.txt
字号:
渲染茶壶虽然很有意思,但游戏里不可能只需要渲染茶壶。大量的mesh是通过艺术家使用专业的建模软件来创造的。如果你的建模软件可以导出.X文件那么恭喜你,你很幸运(Direct SDK里包含了常用建模软件的导出转换器)。
可以通过加载x文件里储存的几种数据类型来创建mesh。当然顶点和索引数据是渲染物体的最基本要求。mesh的每个子集都会关联到一种材质。每一个材质组也同样能包含纹理信息。还可以同时使用x文件和High Level Shader Language(HLSL)文件来创建mesh。HLSL是一门高级技术,我们会在后边的内容里深入讨论。
和创建“简单”图原类型的静态方法一样,Mesh类还有两个主要的静态方法可以加载外部模型。这两个方法分别是Mesh.FormFile和Mesh.FromStream。两个方法本质上来说都是一样的,stream方法有更多的重载以适应不同大小的流。最常用的重载方法如下:
public static Mesh FromFile(string filename,MeshFlags options,Device device,out GraphicsStream adjacency,out ExtendedMaterial materials,out EffectInstance effects);
public static Mesh FromStream(Stream stream,int readBytes,MeshFlags options,Device device,out GraphicsStream adjacency,out ExtendedMaterial materials, out EffectInstance effects);
第一个参数是加载为mesh的数据源。对于FromFile方法来说,他是所要加载的文件名;对于FromStream方法来说,它是所使用的流以及要读取的数据字节数。如果使用整个流的话,只要使用没有readBytes参数的重载就可以了。MeshFlags参数控制着去哪里以及如何加载数据。这个参数的值可以通过以下值组合而来:
Mesh Flags Enumeration values
PARAMETER value
MeshFlags.DoNotClip Use the Usage.DoNotClip flag for vertex and index buffers.
MeshFlags.Dynamic Equivalent to using both IbDynamic and VbDynamic.
MeshFlags.IbDynamicUse Usage.Dynamic for index buffers.
MeshFlags.IbManaged Use the Pool.Managed memory store for index buffers.
MeshFlags.IbSoftware ProcessingUse the Usage.SoftwareProcessing flag for index buffers.
MeshFlags.IbSystemMem Use the Pool.SystemMemory memory pool for index buffers.
MeshFlags.IbWriteOnly Use the Usage.WriteOnly flag for index buffers.
MeshFlags.VbDynamic Use Usage.Dynamic for vertex buffers.
MeshFlags.VbManaged Use the Pool.Managed memory store for vertex buffers.
MeshFlags.VbSoftwareProcessing Use the Usage.SoftwareProcessing flag for vertex buffers.
MeshFlags.VbSystemMem Use the Pool.SystemMemory memory pool for vertex buffers.
MeshFlags.VbWriteOnly Use the Usage.WriteOnly flag for vertex buffers.
MeshFlags.Managed Equivalent to using both IbManaged and VbManaged.
MeshFlags.Npatches Use the Usage.NPatches flag for both index and vertex buffers. This is required if the mesh will be rendered using N-Patch enhancement.
MeshFlags.Points Use the Usage.Points flag for both index and vertex buffers.
MeshFlags.RtPatches Use the Usage.RtPatches flag for both index and vertex buffers.
MeshFlags.SoftwareProcessing Equivalent to using both IbSoftwareProcessing and VbSoftwareProcessing.
MeshFlags.SystemMemory Equivalent to using both IbSystemMem and VbSystemMem.
MeshFlags.Use32Bit Use 32-bit indices for the index buffer. While possible, normally not recommended.
MeshFlags.UseHardwareOnly Use hardware processing only.
下一个参数是渲染mesh的device。应为资源必须关联到一个device,这是个必选的参数。adjacency参数是一个“out”参数,着表示在这个方法结束后adjacency会被分配并且传递出去,它将返回阾接信息。ExtendedMaterial类保存了普通的Direct3D材质和一个加载为纹理的字符串。这个字符串通常是使用的纹理或资源文件名,因为加载纹理是由程序来进行的,它也可以是任何用户提供的字符串。组后,EffectInstance参数描述了用于mesh的HLSL材质文件和值。可以根据需要选择具有不同参数的方法重载。
这里讨论了大量关于加载和渲染mesh的细节,但实际上并没有那么复杂。一开始你可能会有些担心,但看到实际代码之后,确实很简单。现在就来试试吧。首先,要确保有可以用来为不同的子集储存材质和纹理的变量成员。在声明了mesh之后添加如下代码:
private Material[] meshMaterials;
private Texture[] MeshTextures;
因为mesh中可能有许多不同的子集,所以需要分别创建一个材质和纹理的数组以满足每一个子集的需要。好了现在来添加一些真正加载mesh的方法吧,创建一个名为“LoadMesh”的函数,代码如下:
private void LoadMesh(String file)
{
``````(此处代码较多,详见源码)
}
好啦,虽然看起来比我们之前所作的简单工作吓人一点,但实际上却不是这样。首先,我们我们声明了用于保存mesh子集信息的ExtendenMaterial数组。然后,调用FromFile方法加载mesh。我们现在并不关心adjacency或HLSL参数,所以选用了不含这两个参数的重载。
加载mesh之后,需要为大量的子集储存材质和纹理信息。确定了是否有不同的子集之后,我们最终使用子集的大小为材质和纹理成员分配大小。接下来,使用循环把ExtenedMaterial中的数据拷贝到meshMaterials中。如果子集中还包含纹理信息的话,使用TextureLoader.FromFile方法来创建纹理。这个方法接受两个参数,device以及作为纹理的文件名,这个方法可要比以前使用的System.Drawing.Bitmap快许多。
为了绘制mesh,还需要添加如下方法:
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);
}
}
你可能已经注意到,这个方法保留了DrawBox方法的签名部分。接下来为了绘制mesh,迭代所有材质,并且执行一下步骤:
1, 把保存的材质赋予device;
2, 把纹理赋予device。这里,在没有纹理的情况下,即使值为null也不会出错。
3, 根据子集的ID调用DrawSubset方法
perfect,现在我们已经完成了加载和渲染mesh的工作了。我已经制作了一个名为tiny.x的模型。添加如下代码来加载这个模型吧:
this.LoadMesh(@"..\..\tiny.x");
还需要调整一下摄像机的位置,应为只是模型看起来像除了tiny之外的任何东西。由于模型非常的大,摄像机需要退后一点,修改以下方法:
device.Transform.Projection = Matrix.PerspectiveFovLH((float)Math.PI / 4, this.Width / this.Height, 1.0f, 1000.0f);
device.Transform.View = Matrix.LookAtLH(new Vector3(0,0, 580.0f), new Vector3(), new Vector3(0,1,0));
我们重修调增加了到后裁剪平面的距离,平且把摄像机移动的相当靠后,好了最后的任务:在渲染部分调用DrawMesh方法:
this.DrawMesh(angle / (float)Math.PI,angle / (float)Math.PI*2.0f,angle/(float)Math.PI/4.0f,0.0f,0.0f,0.0f);
最后,你还可以调整一下灯光的颜色试试。
我们又向前迈进了一大步,这可比总看着立方体旋转要有趣多了。
(第了六部分完)
下一章我们将使用Managed DirectX来写一个真正的游戏了,最然它可能看起来很简单,但毕竟是我们的第一个三维游戏^_^,第六章内容比较,大概3、4次才能翻译完。
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -