如果一个3DMAX场景中有多个模型,但是只想控制其中一个模型的颜色,并对其进行模型进行变色(替换贴图)。
补充一下:导入的一个model中,3DMAX建模时的每个小的模型作为这个model的mesh。(囧 大概就这么回事,不好描述啊,多理解理解吧)
这个情况再实际项目中可以用来实现一个播放中的电视机模型(知识设想,回头可以试着实现一个玩一玩),可以将屏幕单独做成一个面,然后只轮换修改其贴图,做出电视播放的效果。
好了 大概就是这么个东西。然后谈谈如何实现。
还是,新建个项目,叫TextureControl吧(由于要对模型的内部结构进行控制,所以显示的时候不能简单的用model.Draw方法,要用之前那个一堆foreach循环那个)
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,100,20), 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 (BasicEffect effect in mesh.Effects)
20: {
21: //光照设置
22: effect.LightingEnabled = true;
23: effect.AmbientLightColor = new Vector3(0.9f, 0.9f, 0.9f);
24:
25: effect.View = view;
26: effect.Projection = projection;
27: effect.World = transforms[mesh.ParentBone.Index] * world;
28: }
29: mesh.Draw();
30: }
31: base.Draw(gameTime);
32: }
然后构造一个纯色的贴图
新建一个类变量用来保存贴图
1: Texture2D texture;
然后在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>(@"boxes");
8:
9: texture = new Texture2D(GraphicsDevice, 1, 1);//创建一个2D图片对象
10: texture.SetData(new Color[] { new Color(255, 0, 0, 255) });//改变图像属性(上色)
11: }
贴上试试
好了 接下来就是重头戏了。。
观察显示模型的foreach循环之前的准备动作那两句
1: Matrix[] transforms = new Matrix[model.Bones.Count];
2: model.CopyAbsoluteBoneTransformsTo(transforms);
这两句的作用就是将模型中每个Mesh相对于模型基准坐标的平移变量缓存出来
然后观察缓存出来的那个矩阵数据再循环中的应用,也可以证明,这个矩阵数据即使用来分别设置每个Mesh的位置并分别进行显示
1: foreach (ModelMesh mesh in model.Meshes)//循环模型的每个Mesh
2: {
3: foreach (BasicEffect effect in mesh.Effects)//对Mesh的每个Effect进行设置
4: {
5: //光照设置
6: effect.LightingEnabled = true;
7: effect.AmbientLightColor = new Vector3(0.9f, 0.9f, 0.9f);
8:
9: //设置贴图
10: effect.TextureEnabled = true;
11: effect.Texture = texture;
12:
13: //设置观察矩阵
14: effect.View = view;
15: //设置投影矩阵
16: effect.Projection = projection;
17: //设置位置(相对于基准点)矩阵
18: effect.World = transforms[mesh.ParentBone.Index] * world;
19: }
20: mesh.Draw();//显示Mesh
21: }
重点观察 设置为是矩阵那一句
1: effect.World = transforms[mesh.ParentBone.Index] * world;
这里用到了之前缓存回来的矩阵数组。所以数组的游标可以用来控制前正在处理的Mesh。
然后debug观察下mesh.ParentBone.Index值的变化发现这个值再1--9之间变化,结合我之前再Max中给场景创建了9个方块,可知游标从1开始。
所以改变单独一个Mesh的贴图就是在循环中控制只有在需要改变时采取设置贴图,代码如下
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,100,20), 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 (BasicEffect effect in mesh.Effects)
20: {
21: effect.LightingEnabled = true;
22: effect.AmbientLightColor = new Vector3(0.9f, 0.9f, 0.9f);
23:
24: if (mesh.ParentBone.Index == 1) //如果当前Mesh游标为1 则修改其贴图
25: {
26: //设置贴图
27: effect.TextureEnabled = true;
28: effect.Texture = texture;
29: }
30: else
31: {
32: effect.TextureEnabled = false;
33: }
34:
35:
36: effect.View = view;
37: effect.Projection = projection;
38: //设置位置(相对于基准点)矩阵
39: effect.World = transforms[mesh.ParentBone.Index] * world;
40: }
41: mesh.Draw();//显示Mesh
42: }
43: base.Draw(gameTime);
44: }
显示效果如下
补充一点,经过测试,对于每个Mash的游标编号和Mash 对应的模型再3DMAX中创建顺序相关,第几个创建的编号就是几。