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
{
///
/// 高级地形使用的材质,从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
}
}
文件下载(已下载 1189 次)
发布时间:2010/4/18 下午1:50:45 阅读次数:8166
