19. Material类
Material意为材质,定义了物体当被光照射时表面的颜色,同样的纹理若材质不同在屏幕上的表现也不同。在DirectX中,有专门的材质结构D3DMATERIAL9:
typedef struct D3DMATERIAL9 { D3DCOLORVALUE Diffuse; D3DCOLORVALUE Ambient; D3DCOLORVALUE Specular; D3DCOLORVALUE Emissive; float Power; } D3DMATERIAL9, *LPD3DMATERIAL9;
这个结构包括漫反射颜色,环境光颜色,镜面反射颜色,自发光颜色,镜面反射强度。在MDX中也有Material(http://msdn.microsoft.com/en-us/library/bb322396(VS.85).aspx)结构,包含Ambient、AmbientColor、Diffuse、DiffuseColor、Emissive、EmissiveColor、Specular、SpecularColor、SpecularSharpness属性。在MDX中还有一个ExtendedMaterial(http://msdn.microsoft.com/en-us/library/bb322539(VS.85).aspx)结构,额外包含了TextureFilename属性,即把纹理信息也包括在材质之中。
不知为何在XNA中并没有处理材质的类,大概是因为为了统一管理,我们通常都要扩展这个类,使它还包含纹理、雾化和Effect等,因此材质类都是自己定义的。在我的引擎中的Material类就包含了这些属性。
参考了一些自己曾初步研究过的示例,其中《Beginning XNA 3.0 Game Programming From Novice to Professional》一书中的做法是先定义一个LightMaterial类,只包含光照属性,再定义一个TextureMaterial类只包含纹理属性,地形的材质类TerrainMaterial是一个LightMaterial类和六个TextureMaterial类的组合,然后再定义一个TerrainEffect类管理材质的属性,详见10.3 地形Effect。
而在InnovationEngine引擎中,则是先创建一个Material基类,而将Effect绑定到这个材质,其他材质从这个基类继承,详见XNA Game Engine教程系列9:ComponentPredicate和Materials类。
而我的材质实现主要参考了xna官网上的Shader系列4:材质和光源,主要思路还是将Effect包含在材质中,这个做法与InnovationEngine引擎是类似的,所以请先看懂Shader系列4:材质和光源,然后才能理解下面的几个教程。
首先,DirectX中的材质数据放在结构中,而我放在了类中。对于小数据类型,应该是结构更快点,具体解释可参见C#中结构与类的区别(http://www.cnblogs.com/fhbcn/archive/2009/01/08/1371790.html),但是基于两个理由我使用了类:1.主要是我要使用继承,而结构类型无法继承。2.结构是值引用,要改变结构中的属性,需要引用它,代码写起来有点啰嗦。
其次,浏览一下我要实现的对象有:天空球(只要一张纹理,不使用光照),天空盒(一张立方贴图,不使用光照),水面(一张水面凹凸贴图,需要光照),海面(一张水面凹凸贴图,一张立方贴图实现反射,需要光照),其他如平面、立方体,球体,简单地形,Billboard都使用一张漫反射纹理,一张细节纹理,需要光照,最后高级地形需要四张纹理并实现光照。因此材质的层次结构如下:
BaseMaterial.cs
首先是材质基类BaseMaterial.cs,它主要定义了IMaterial接口,并由传入的参数创建effect实例,它还在每一帧设置节点的世界矩阵和视矩阵。代码如下:
namespace StunEngine.Material { ////// 材质基类,定义了IMaterial接口,并由传入的参数创建effect实例,它还在每一帧设置节点的世界矩阵和视矩阵。 /// public abstract class BaseMaterial:IMaterial { ////// 用来绘制节点的Effect实例。 /// protected Effect effectInstance; ////// 对引擎的引用 /// protected StunXnaGE engine; // 缓存Effect的参数 ////// 世界矩阵。 /// protected EffectParameter gWorld; ////// 视矩阵。 /// protected EffectParameter gView; ////// 创建一个BaseMaterial对象 /// ///引擎 /// 此材质使用的Effect public BaseMaterial(StunXnaGE engine, Effect setEffect) { this.engine = engine; if (setEffect == null) { throw new ArgumentNullException("setEffect"); } //克隆传入的setEffect创建此材质的effect实例 this.effectInstance = setEffect.Clone(engine .GraphicsDevice ); //默认使用effect的第一个technique this.effectInstance.CurrentTechnique = this.effectInstance.Techniques[0]; //------------------------------------------------- // 初始化Effect参数缓存 //------------------------------------------------- gWorld = effectInstance.Parameters["gWorld"]; gView = effectInstance.Parameters["gView"]; } #region 属性 ////// 获取绘制节点的Effect实例 /// public Effect EffectInstance { get { return effectInstance; } } ////// 获取或设置effect使用的technique名称。 /// public string CurrentTechniqueName { get { return effectInstance.CurrentTechnique .Name ; } set { effectInstance.CurrentTechnique = effectInstance.Techniques[value];} } #endregion ////// 设置世界矩阵和视矩阵 /// /// 世界矩阵 /// 此节点是否使用反射绘制 public virtual void SetEffectParameters(ref Matrix world,bool useReflection) { gWorld.SetValue(world); if (!useReflection) gView.SetValue(engine.SceneManager.ActiveScene.sceneCamera.ViewMatrix); else gView.SetValue(engine.SceneManager.ActiveScene.sceneCamera.ReflectionViewMatrix); } } }
IMaterial
其中接口IMaterial的代码如下:
using Microsoft.Xna.Framework.Graphics; namespace StunEngine.Material { public interface IMaterial { ////// 材质使用的Effect实例 /// Effect EffectInstance { get; } ////// Effect当前使用的technique名称 /// string CurrentTechniqueName { get; set; } ////// 设置世界矩阵和视矩阵 /// ///世界矩阵 /// 此节点是否使用反射绘制 void SetEffectParameters(ref Matrix world,bool useReflection); } }
创建这个接口的目的是为了减少冗余代码。原来引擎中从SceneNode派生了RenderableSceneNode类,其他绘制2D的UISceneNode和3D的CubeSceneNode……等从RenderableSceneNode继承,现在的变化是UISceneNode从SceneNode直接派生处理2D对象,而将RenderableSceneNode类重命名为Renderable3DsceneNode负责处理3D对象,CubeSceneNode……从Renderable3DsceneNode类继承,但是从Renderable3DsceneNode类继承的类具有不同的材质,为了将Draw代码放置在Renderable3DsceneNode类中而不是放在各自的继承类中减少代码冗余,需要在Renderable3DsceneNode类中添加这个接口:
protected IMaterial Imaterial;
而这个变量的设置是在继承类中实现的。现在Renderable3DsceneNode类的Draw代码如下:
public virtual int Draw(GameTime gameTime,bool useReflection) { int totalPrimitives = 0; //设置节点的顶点声明和顶点缓冲,如果包含索引数据,还设置顶点缓冲 mesh.PrepareRender(Engine.GraphicsDevice); //设置节点的世界矩阵和视矩阵 Imaterial.SetEffectParameters(ref pose.WorldMatrix, useReflection); Imaterial.EffectInstance.CurrentTechnique = Imaterial.EffectInstance.Techniques[Imaterial.CurrentTechniqueName]; //-------------------------------------------------- // 开始绘制 //-------------------------------------------------- Imaterial.EffectInstance.Begin(); for (int i = 0; i < this.Imaterial.EffectInstance.CurrentTechnique.Passes.Count; i++) { EffectPass pass = this.Imaterial.EffectInstance.CurrentTechnique.Passes[i]; pass.Begin(); mesh.Render(Engine.GraphicsDevice); pass.End(); totalPrimitives += mesh.primitiveCount; } Imaterial.EffectInstance.End(); //返回此节点的图元数量 return totalPrimitives; }
SingleTextureMaterial
接着从BaseMaterial.cs派生出SingleTextureMaterial,用于天空球和水面,在BaseMaterial基础上只是多了一个纹理属性,代码如下:
namespace StunEngine.Material { ////// 水面和天空球使用的材质,从BaseMaterial继承。 /// public class SingleTextureMaterial:BaseMaterial { ////// 纹理 /// private Texture2D texture1 = null; ////// 纹理文件名称,需要包含路径 /// protected string texture1Name = null; ////// 创建一个只使用一张纹理的材质 /// ///引擎引用 /// 所使用的effect public SingleTextureMaterial(StunXnaGE engine, Effect setEffect) : base(engine, setEffect) { } ////// 获取或设置纹理文件名称,需要包含路径 /// public string Texture1Name { get { return texture1Name; } set { texture1Name = value; if (string.IsNullOrEmpty (texture1Name)) { texture1 = null; effectInstance.Parameters["gTexture1"].SetValue((Texture2D)null); } //如果texture1Name存在,则加载纹理并设置effect的gTexture1参数 else { texture1 = Utility.LoadTexture(engine, texture1Name); effectInstance.Parameters["gTexture1"].SetValue(texture1); } } } } }
OceanMaterial
海面使用的纹理从SingleTextureMaterial,多了一张立方贴图,代码如下:
namespace StunEngine.Material { ////// 海面使用的材质。 /// public class OceanMaterial:SingleTextureMaterial { ////// 立方贴图纹理 /// private TextureCube cubeTexture = null; ////// 立方贴图纹理文件名称,需要包含路径 /// private string cubeTextureName = null; ////// 创建海面使用的材质 /// ///引擎引用 /// 使用的effect public OceanMaterial(StunXnaGE engine, Effect setEffect) : base(engine, setEffect) { } ////// 获取或设置立方贴图纹理文件名称,需要包含路径 /// public string CubeTextureName { get { return cubeTextureName; } set { cubeTextureName = value; if (string.IsNullOrEmpty(cubeTextureName)) { cubeTexture = null; effectInstance.Parameters["gCubeTexture"].SetValue((TextureCube)null); } else { cubeTexture = engine.Content.Load(cubeTextureName); effectInstance.Parameters["gCubeTexture"].SetValue(cubeTexture); } } } } }
SkyBoxMaterial
天空盒使用的材质从BaseMaterial继承,使用了一张立方贴图,代码如下:
namespace StunEngine.Material { ////// 天空盒材质,从BaseMaterial继承。 /// public class SkyBoxMaterial:BaseMaterial { ////// 立方贴图纹理 /// private TextureCube cubeTexture = null; ////// 立方贴图纹理文件名称,需要包含路径 /// private string cubeTextureName = null; ////// 创建一个天空盒使用的材质 /// ///引擎引用 /// 使用的effect public SkyBoxMaterial(StunXnaGE engine,Effect setEffect):base(engine,setEffect) { } ////// 获取或设置立方贴图纹理文件名称,需要包含路径 /// public string CubeTextureName { get { return cubeTextureName; } set { cubeTextureName = value; if (cubeTextureName != null) { cubeTexture = engine.Content.Load(cubeTextureName); effectInstance.Parameters["gCubeTexture"].SetValue(cubeTexture); } else { cubeTexture = null; effectInstance.Parameters["gCubeTexture"].SetValue((TextureCube)null); } } } } }
GenericMaterial
最常用的GenericMaterial类从BaseMaterial类继承,需要一张漫反射纹理和一张细节纹理以及光照属性,代码如下:
namespace StunEngine.Material { ////// 通用材质数据,用于平面、立方体、球体、BillBoard,简单地形。 /// public class GenericMaterial:BaseMaterial { ////// 漫反射颜色。如果材质的漫反射纹理不存在则为顶点颜色,默认为白色。 /// protected Vector3 diffuseColor = Vector3.One; ////// 物体的自发光颜色。如果设置,就算物体没有被照亮也可见,拥有自发光颜色,默认为黑色。 /// protected Vector3 emissiveColor = Vector3.Zero; ////// 物体的镜面高光颜色,默认为黑色。 /// protected Vector3 specularColor = Vector3.Zero; ////// 镜面高光强度,默认为0.01。 /// protected float specularPower = 0.01f; ////// 漫反射纹理 /// private Texture2D diffuseTexture = null; ////// 漫反射纹理文件名称,需要包含路径 /// protected string diffuseTextureName = null; ////// 漫反射纹理UV方向平铺次数 /// protected Vector2 diffuseUVTile = Vector2 .One ; ////// 细节纹理 /// private Texture2D detailTexture = null; ////// 细节纹理文件名称,需要包含路径 /// protected string detailTextureName = null; ////// 细节纹理的平铺次数 /// protected Vector2 detailUVTile = Vector2.Zero ; ////// 是否进行雾化,默认为false。 /// protected bool fogEnabled = false; public GenericMaterial(StunXnaGE engine,Effect setEffect):base( engine,setEffect ) { //------------------------------------------------- // 设置Effect参数的默认值 //------------------------------------------------- effectInstance.Parameters["gDiffuseEnabled"].SetValue (true); effectInstance.Parameters["gDiffuseUVTile"].SetValue(diffuseUVTile); effectInstance.Parameters["gDetailUVTile"].SetValue (detailUVTile ); effectInstance.Parameters["gMaterialDiffuse"].SetValue (diffuseColor); effectInstance.Parameters["gMaterialEmissive"].SetValue (emissiveColor ); effectInstance.Parameters["gMaterialSpecular"].SetValue (specularColor ); effectInstance.Parameters["gMaterialSpecPower"].SetValue (specularPower ); effectInstance.Parameters["gFogEnabled"].SetValue(fogEnabled ); } #region 属性 ////// 获取或设置漫反射颜色。如果材质的漫反射纹理不存在则为顶点颜色,默认为白色。 /// public Vector3 DiffuseColor { get { return diffuseColor; } set { diffuseColor = value; effectInstance.Parameters["gMaterialDiffuse"].SetValue(diffuseColor); } } ////// 获取或设置物体的自发光颜色。如果设置,就算物体没有被照亮也可见,拥有自发光颜色,默认为黑色。 /// public Vector3 EmissiveColor { get { return emissiveColor; } set { emissiveColor = value; effectInstance.Parameters["gMaterialEmissive"].SetValue(emissiveColor); } } ////// 获取或设置物体的镜面高光颜色,默认为黑色。 /// public Vector3 SpecularColor { get { return specularColor; } set { specularColor = value; effectInstance.Parameters["gMaterialSpecular"].SetValue(specularColor); } } ////// 获取或设置镜面高光强度,默认为0.01。 /// public float SpecularPower { get { return specularPower; } set { specularPower = value; effectInstance.Parameters["gMaterialSpecPower"].SetValue(specularPower); } } ////// 获取或设置漫反射纹理文件名称,需要包含路径 /// public string DiffuseTextureName { get { return diffuseTextureName; } set { diffuseTextureName = value; if (diffuseTextureName == null) { diffuseTexture = null; effectInstance.Parameters["gDiffuseTexture"].SetValue((Texture)null); effectInstance.Parameters["gDiffuseEnabled"].SetValue(false ); } else { diffuseTexture = Utility.LoadTexture (engine,diffuseTextureName); effectInstance.Parameters["gDiffuseTexture"].SetValue(diffuseTexture); effectInstance.Parameters["gDiffuseEnabled"].SetValue(true); } } } ////// 获取或设置漫反射纹理UV方向平铺次数 /// public Vector2 DiffuseUVTile { get { return diffuseUVTile; } set { diffuseUVTile = value; effectInstance.Parameters["gDiffuseUVTile"].SetValue(diffuseUVTile); } } ////// 获取或设置细节纹理文件名称,需要包含路径 /// public string DetailTextureName { get { return detailTextureName; } set { detailTextureName = value; if (detailTextureName == null) { detailTexture = null; effectInstance.Parameters["gDetailTexture"].SetValue((Texture)null); } else { detailTexture = Utility.LoadTexture(engine, detailTextureName); effectInstance.Parameters["gDetailTexture"].SetValue(detailTexture); } } } ////// 获取或设置细节纹理的平铺次数 /// public Vector2 DetailUVTile { get { return detailUVTile; } set { detailUVTile = value; effectInstance.Parameters["gDetailUVTile"].SetValue(detailUVTile); } } ////// 获取或设置是否进行雾化,默认为false。 /// public bool FogEnabled { get { return fogEnabled; } set { fogEnabled = value; //只有在场景的FogEnabled开启并且节点本身的fogEnabled开启的情况下才进行雾化计算 effectInstance.Parameters["gFogEnabled"].SetValue(engine.SceneManager.ActiveScene.FogEnabled && fogEnabled); } } #endregion ////// 被ModelSceneNode调用,因为不想对外暴露diffuseTexture,所以通过这个方法设置diffuseTexture /// /// public void SetDiffuseTexture(Texture2D setTexture) { diffuseTexture = setTexture; effectInstance.Parameters["gDiffuseTexture"].SetValue(diffuseTexture); } } }
AdvancedTerrainMaterial
最后是高级地形使用的材质,从GenericMaterial类继承,需要使用四张纹理,代码如下:
namespace StunEngine.Material { ///文件下载(已下载 1189 次)/// 高级地形使用的材质,从GenericMaterial继承。 /// public class AdvancedTerrainMaterial:GenericMaterial { ////// 第三张纹理 /// private Texture2D texture3 = null; ////// 第三张纹理文件名称,需要包含路径 /// protected string texture3Name = null; ////// 第四张纹理 /// private Texture2D texture4 = null; ////// 第四张纹理文件名称,需要包含路径 /// protected string texture4Name = null; ////// 是否开启光照运算,默认为false /// protected bool enableLighting = false; public AdvancedTerrainMaterial(StunXnaGE engine, Effect setEffect) : base(engine, setEffect) { effectInstance.Parameters["gEnableLighting"].SetValue(enableLighting); } #region 属性 ////// 获取或设置第三张纹理文件名称,需要包含路径 /// public string Texture3Name { get { return texture3Name; } set { texture3Name = value; if (texture3Name == null) { texture3 = null; effectInstance.Parameters["gTexture3"].SetValue((Texture2D)null); } else { texture3 = Utility.LoadTexture(engine, texture3Name); effectInstance.Parameters["gTexture3"].SetValue(texture3); } } } ////// 获取或设置第四张纹理文件名称,需要包含路径 /// public string Texture4Name { get { return texture4Name; } set { texture4Name = value; if (texture4Name == null) { texture4 = null; effectInstance.Parameters["gTexture4"].SetValue((Texture2D)null); } else { texture4 = Utility.LoadTexture(engine, texture4Name); effectInstance.Parameters["gTexture4"].SetValue(texture4); } } } ////// 获取或设置是否开启光照运算 /// public bool EnableLighting { get { return enableLighting; } set { enableLighting = value; effectInstance.Parameters["gEnableLighting"].SetValue(enableLighting); } } #endregion } }
发布时间:2010/4/18 下午1:50:45 阅读次数:7578