WP7-XNA 3D开发 显示3D模型(BasicEffect、DualTexturesEffect)

开发3D游戏是个很HIGH的事情。

本文介绍如何在XNA框架下使用BasicEffect和DualTexturesEffect显示模型。

参考文章(E文好的可以不同在这里看我啰嗦 ╮(╯▽╰)╭):

http://www.codeproject.com/KB/windows-phone-7/3DGraphicsWP7.aspx

本文包括了简单的模型制作步骤(因为这是我的薄弱环节,个人再摸索这两个效果时候耗时最多,所以对建模说明的也比较详细),对代码的分析没有太深入(因为XNA框架个人认为代码结构比较容易理解,按照流程走下来,多想想 因该很容易上手~)

 

Tips:

1、XNA中默认使用“厘米”为单位。再建模时最好把模型单位设置为厘米。

以3DsMAX为例,设置单位再“自定义”菜单的“单位设置”选项。

image

将“显示单位比例”设置为 “公制-厘米”

image

2、输出模型的贴图注意一定不要使用sRGB颜色空间,并且保证模型的贴图宽度都是2的次幂形式 (比如512*512,128*128这样)。

当时我在MAX里导出png格式的UV模板然后再画图工具里直接修改导出的这个UV模板的颜色。结果在XNA里显示出来的颜色就像褪了色一样,研究好长时间 后来发现居然是颜色空间的问题。(MAX2012的UV贴图貌似使用的是sRGB空间,用photoshop可以看到)

image

image

当然。。如果是这样,别怕。。改成adobe RGB就可以了,或者上色的时候干脆新建一个图片,或者不要使用png格式。。

3、XNA程序默认横屏。

4、为了效率和节能WindowsphoneSDK中XNA并不支持shader。做光影效果使用DualTexturesEffect

BasicEffect

BasicEffect,很明显就是基本效果的意思。。也就是最简单的模型显示方式。

先在3DsMAX中创建一个模型。并加个贴图。

Like this:

这里创建了一个单位为5cm的茶壶模型。。

image

UV展开的贴图和上色后的贴图如下(上色的时候新建一个图片然后将贴图作为图层后上色注意颜色空间别是sRGB就OK)

贴图不一定要再建模的时候就添加到模型上。

imageimage

(贴图简单上色,有点二。。但是,别怕)

将模型导出为FBX文件

image

差不多就这些。。。够了。准备工作完成,然后打开VS。

新建一个项目。选择Visual C# / XNA Game Studio 4.0 / WindowsPhoneGame(4.0) 起名就叫BasicEffectTest

image

VS会自动创建好一个XNA项目的模板。

image

Game1.cs就是主函数,别的可以暂时不用管。。

XNA框架简单说就是个死循环啊死循环。(此图应该多处可见,不做赘述)

image

开始正题。首先进行简单的设置

XNA中模型对象类型为Model,贴图对象类型为Texture2D。

首先要做的就是声明这两个变量(类属性?),分别用来保存模型和贴图。

   1: Texture2D texture;
   2: Model model;

然后将模型和贴图添加到Content中

image

image

添加的结果是这样的

image

然后可以将资源加入到代码中。在主类(Game1.cs)中载入贴图,添加载入资源代码的LodContent方法如下。

   1: protected override void LoadContent()
   2: {
   3:     // Create a new SpriteBatch, which can be used to draw textures.
   4:     spriteBatch = new SpriteBatch(GraphicsDevice);
   5:  
   6:     texture = Content.Load<Texture2D>(@"teapotTexture"); //载入贴图
   7:     model = Content.Load<Model>(@"teapot");//载入模型
   8:  
   9:     // TODO: use this.Content to load your game content here
  10: }

好了 然后就是最激动人心的时刻了。。

显示模型,首先要定义3个矩阵。(关于3D世界中矩阵的知识,请自行抱佛脚)

1、世界矩阵:让模型知道他应该显示在那里,也就是他在虚拟世界中的位置,最基本的一个矩阵,用来描述模型在整个场景的基准位置。

Matrix world = Matrix.Identity; //定义一个单位矩阵,就是模型的基准是世界的原点

2、视图矩阵:可以理解为一个摄像机,用来描述你在世界里用怎样的视角来观察这个该死的模型。

Matrix view = Matrix.CreateLookAt(new Vector3(0,0,50), Vector3.Zero, Vector3.Up); //从(0,0,50)这个坐标向(0,0,0)这个角度看去,默认向上和世界坐标的上是一个方向

3、投影矩阵:可以理解为一个大幕布,整个世界模型摆好了,摄像机也摆好了,这个投影矩阵用来把场景中的模型投影在幕布上,这样从摄像机看去的影像就是最后拍成电影呈献给观众的画面。

Matrix projection = Matrix.CreatePerspectiveFieldOfView(
                                                       MathHelper.ToRadians(45),            //视角
                                                       GraphicsDevice.Viewport.AspectRatio,  //屏幕长宽比
                                                       0.1f, //最近拍摄范围
                                                       100f  //最远拍摄范围
                                                       );

好了 矩阵设置好了。然后就是最简单的 让模型显示就好了。。最基本的显示模型的代码很简单。。。。 - -

model.Draw(world, view, projection); 

OK,That's all ,吧这些统统添加到主类(Game1.cs)的Draw方法中,添加后的Draw方法如下。

   1: protected override void Draw(GameTime gameTime)
   2: {
   3:     GraphicsDevice.Clear(Color.CornflowerBlue);
   4:  
   5:     // TODO: Add your drawing code here
   6:     Matrix world = Matrix.Identity; //定义一个单位矩阵,就是模型的基准是世界的原点
   7:     Matrix view = Matrix.CreateLookAt(new Vector3(0,0,50), Vector3.Zero, Vector3.Up); //从(0,0,50)这个坐标向(0,0,0)这个角度看去,默认向上和世界坐标的上是一个方向
   8:     Matrix projection = Matrix.CreatePerspectiveFieldOfView(
   9:                                                MathHelper.ToRadians(45),            //视角
  10:                                                GraphicsDevice.Viewport.AspectRatio,  //屏幕长宽比
  11:                                                0.1f, //最近拍摄范围
  12:                                                100f  //最远拍摄范围
  13:                                                );
  14:     model.Draw(world, view, projection); //模型显示代码
  15:  
  16:     base.Draw(gameTime);
  17: }

然后,run下,看看效果。。。(因为我建模的时候没有直接给模型加上贴图,所以看起来就是这样。。)

image

好,模型显示出来了。现在要做的就是添加贴图。显然。要添加贴图那种基本的显示方法明显不够用了啊。所以这里要使用比较笨得方法显示(为了让模型显示更加可控,再实际项目中都会用这种方式)

   1: Matrix[] transforms = new Matrix[model.Bones.Count];
   2: model.CopyAbsoluteBoneTransformsTo(transforms);
   3:  
   4: foreach (ModelMesh mesh in model.Meshes)
   5: {
   6:     foreach (BasicEffect effect in mesh.Effects)
   7:     {
   8:         effect.View = view;
   9:         effect.Projection = projection;
  10:         effect.World = transforms[mesh.ParentBone.Index] * world;
  11:     }
  12:     mesh.Draw();
  13: }

上面这一堆等同于之前的一句“model.Draw(world, view, projection); ”,仔细分析这个代码其实就是将FBX中模型的每个面(Mesh)都分别进行BasicEffect进行渲染。

现在要添加贴图了。

其实很简单,设置下BasicEffect的属性就OK

添加设置后的代码如下:

   1: protected override void Draw(GameTime gameTime)
   2: {
   3:     GraphicsDevice.Clear(Color.CornflowerBlue);
   4:  
   5:     // TODO: Add your drawing code here
   6:     Matrix world = Matrix.Identity; //定义一个单位矩阵,就是模型的基准是世界的原点
   7:     Matrix view = Matrix.CreateLookAt(new Vector3(0,0,50), Vector3.Zero, Vector3.Up); //从(0,0,50)这个坐标向(0,0,0)这个角度看去,默认向上和世界坐标的上是一个方向
   8:     Matrix projection = Matrix.CreatePerspectiveFieldOfView(
   9:                                                MathHelper.ToRadians(45),            //视角
  10:                                                GraphicsDevice.Viewport.AspectRatio,  //屏幕长宽比
  11:                                                0.1f, //最近拍摄范围
  12:                                                100f  //最远拍摄范围
  13:                                                );
  14:     Matrix[] transforms = new Matrix[model.Bones.Count];
  15:     model.CopyAbsoluteBoneTransformsTo(transforms);
  16:  
  17:     foreach (ModelMesh mesh in model.Meshes)
  18:     {
  19:         foreach (BasicEffect effect in mesh.Effects)
  20:         {
  21:             effect.TextureEnabled = true; // 设置为使用贴图属性指向的资源作为当前模型的贴图
  22:             effect.Texture = texture;//设置贴图属性为之前导入的资源
  23:             effect.View = view;
  24:             effect.Projection = projection;
  25:             effect.World = transforms[mesh.ParentBone.Index] * world;
  26:         }
  27:         mesh.Draw();
  28:     }
  29:  
  30:  
  31:     base.Draw(gameTime);
  32: }

再次运行结果如下:(为了更好的观察模型 将视图矩阵的位置设置的为30,想象下把摄像头往前放点给模型来个特写╮(╯▽╰)╭)

imageimage

坑爹啊,看起来和贴图颜色不一样有木有!

别怕 这个是因为BasicEffect还有一个灯光属性没有设置,想象下舞台上的超级亮的探照灯。木有灯当然暗了。

imageimage

这样是不是就看起来和贴图一致了~~。

好了 ,BasicEffect就介绍到这里,看了文章开头的E文资料的童鞋会发现,那个外国大牛介绍了更多BasicEffect的特性,比如BasicEffect可以添加3个灯光等等 ,本文不作赘述,感兴趣的去啃E文吧。因为目前这样在我当前的项目里够用了。。。所以我没有深究。。

 

DualTexturesEffect

看完BasicEffect的使用,和示例是不是感觉总是少了点什么。对了,阴影效果。DaulTexturesEffect就是XNA中解决这个问题的。

玩多大型游戏的童鞋都应该有体会,实时阴影效果一开,游戏对机器的要求马上就加大了,但是又希望再XNA中做出光照投影的效果,WindowsPhone作为手机平台,是无法实时处理那么多的光影效果的。

这个时候就可以使用DaulTexturesEffect作为一个折中的办法。

DaulTexturesEffect原理就是一共两个贴图,一个管颜色一个管阴影效果(切换光影效果可以直接替换投影那个贴图就OK了,着色贴图就不用换了~~)

Color = Texture1.rgb * Texture2.rgb * 2  (Texture1 就是色彩贴图   Texture2是光影贴图, 光阴贴图在没有任何光阴效果的情况下默认是颜色为(128,128,128),原理自己想。。。。)

内啥 。。 因为茶壶模型再烘焙后出现的LightMap贴图会有点诡异(不知道原因。。因为我3DMAX是菜鸟 )。估计是因为茶壶模型的面比较复杂吧,所以这里不再使用茶壶模型进行演示了。重新建一个方块的模型。类似于游戏中的建筑模型吧。

模型如下,很简单 一个平面然后放上3个长方体(当是建筑物吧。。为了贴图方便 我再摆放完后把他们合成了一个模型)

将模型导出,就叫dualModel.FBX吧

image

然后展开UV贴图 (注意保存贴图坐标)

image

然后新建一个贴图通道,导入刚刚展开的贴图坐标(就是通道1和通道2展开的贴图坐标一致(实际项目中可以按需调整,这里使用一致的贴图坐标是为了方便。。))

image

然后就是制作贴图了,这里要制作两个贴图,一个是漫反射贴图,也就是着色用的贴图。

为了体现色彩,所以就直接搞成彩色的好了,当然 注意 千万不要用sRGB颜色空间,用adobe RGB就好了

buildingColor

然后,是渲染阴影贴图

3步走

1、制作一个颜色为(128,128,128)的底色材质并付给模型

image

2、给模型来点光照,并渲染下看看效果

image

3、渲染一个lightingMap贴图出来

imagebuildingLighting

资源准备完成,大概包括如下这些

image

好了,这些准备好 然后再VS中新建一个项目,叫DualTexturesEffectTest吧

先将资源导入,程序中需要用到的就是一个模型两个贴图。

image

这里要注意一点,要让模型使用DualTexturesEffect需要修改模型的默认效果属性

image

这一切搞定后,可以打开Game1.cs修改代码了

还是3步走

1、添加类属性用来保管资源

   1: Model model;
   2: Texture2D colorTexture;
   3: Texture2D lightingTexture;

2、在LoadContent方法中将资源载入

   1: protected override void LoadContent()
   2: {
   3:     // Create a new SpriteBatch, which can be used to draw textures.
   4:     spriteBatch = new SpriteBatch(GraphicsDevice);
   5:  
   6:     // TODO: use this.Content to load your game content here
   7:     model = Content.Load<Model>(@"dualModel");
   8:     lightingTexture = Content.Load<Texture2D>(@"buildingLighting");
   9:     colorTexture = Content.Load<Texture2D>(@"buildingColor");
  10: }

3、在draw方法中加入模型显示代码,使用DaulTexturesEffect展示模型(这里显示方法参考前面BasicEffect代码稍作修改就可以)

需要说明的是DaulTexturesEffect效果有两个texture属性 一个是effect.Texture另一个是effect.Texture2 前者用来设置着色贴图后者用来设置光照贴图(有且只有两个。。这个一定要明确)。另外DaulTexturesEffect没有设置灯光的属性。

   1: protected override void Draw(GameTime gameTime)
   2: {
   3:     GraphicsDevice.Clear(Color.CornflowerBlue);
   4:  
   5:     // TODO: Add your drawing code here
   6:     Matrix world = Matrix.Identity; 
   7:     Matrix view = Matrix.CreateLookAt(new Vector3(0, 200, 300), Vector3.Zero, Vector3.Up); //为了更好地观察模型 摄像机位置坐标进行了调整
   8:     Matrix projection = Matrix.CreatePerspectiveFieldOfView(
   9:                                                MathHelper.ToRadians(45),
  10:                                                GraphicsDevice.Viewport.AspectRatio, 
  11:                                                0.1f, 
  12:                                                1000f   //为了能看到模型 投影范围也扩大鸟
  13:                                                );
  14:     Matrix[] transforms = new Matrix[model.Bones.Count];
  15:     model.CopyAbsoluteBoneTransformsTo(transforms);
  16:  
  17:     foreach (ModelMesh mesh in model.Meshes)
  18:     {
  19:         foreach (DualTextureEffect effect in mesh.Effects)   //使用DualTextureEffect效果
  20:         {
  21:             effect.Texture = colorTexture;//设置Texture贴图属性为之前导入的着色贴图资源
  22:             effect.Texture2 = lightingTexture;//设置Texture2贴图属性为之前导入的光照贴图资源
  23:  
  24:             effect.View = view;
  25:             effect.Projection = projection;
  26:             effect.World = transforms[mesh.ParentBone.Index] * world;
  27:         }
  28:         mesh.Draw();
  29:     }
  30:  
  31:     base.Draw(gameTime);
  32: }
好了 然后就是显示效果。(额。。可能我色彩高的太艳了 光照又太强了。。效果不是很明显 但是能看出来 这个是又阴影效果的 对吧 ╮(╯▽╰)╭)

image

搞定收工。。~~!╮(╯▽╰)╭!~~

 

代码下载:http://vdisk.weibo.com/s/1a7y5

posted @ 2011-11-16 19:34  LightingCui  阅读(2128)  评论(1编辑  收藏  举报