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  阅读次数:7578

2006 - 2024,推荐分辨率 1024*768 以上,推荐浏览器 Chrome、Edge 等现代浏览器,截止 2021 年 12 月 5 日的访问次数:1872 万 9823 站长邮箱

沪 ICP 备 18037240 号-1

沪公网安备 31011002002865 号