zoukankan      html  css  js  c++  java
  • 处理模型——扩展模型内容导入器加载自定义Effects

    问题

    你想使用一个自定义内容处理器改变模型的effect而不是实时处理。通过这种方式,模型可以正确地加载到XNA项目中,而无需在实时保存原始effect中的所有纹理和其他信息。

    注意:如果你想学习如何扩展默认模型处理器,最好先看下一个教程。

    解决方案

    扩展默认模型处理器并重写ConvertMaterial方法,这个方法被模型中的每个MaterialContent调用,把这个方法重写将所有的MaterialContent信息传递到自定义材质处理器。

    在这个自定义材质处理器中,你将创建一个空的材质对象,然后将自定义的effect链接到这个对象。你也可以将原始MaterialContent的纹理复制到这个新对象,这个就可以设置effect的所有HLSL变量了。

    工作原理

    因为这个教程需要扩展默认内容处理器,所以请确保已经读过了教程3-9中内容管道的知识。首先建立一个新的内容管道项目,具体解释可见教程3-9中的“扩展一个已存在的内容处理器”一节。

    本教程中,你将扩展默认模型处理器,如图4-16所示。当默认模型导入器从磁盘读取一个模型并创建一个NodeContent对象时,它保存了MaterialContent对象中的effect。你的模型处理器只改变这些MaterialContent对象并存储在NodeContent中,NodeContent对象的其他部分保持不变。

    image

    图4-16 内容管道中的自定义模型处理器

    注意:如果你比较一下图4-15和图3-6,你会发现你也是扩展了处理器。但是,因为这个处理器处理的是模型而不是纹理,所以它的输入和输出是不同的。

    通过扩展Process方法,你可以在模型对象加载到XNA项目之前完全控制它的内容。在本教程中,因为effect是存储在材质信息中的,所以你想改变处理模型中材质的方式。在自定义模型处理器类中,你无需重写Process方法,但要重写ConvertMaterial方法,每次当模型加载材质时默认处理器都会调用这个方法。

    所以用以下代码替换Process方法:

    [ContentProcessor] 
    public class ModelCustomEffectProcessor : ModelProcessor
    {
        protected override MaterialContent ConvertMaterial(MaterialContent material, ContentProcessorContext context)
        { 
            return context.Convert<MaterialContent, MaterialContent>(material, "MaterialCustomEffectProcessor"); 
        }
    } 

    以上代码创建一个ModelCustomEffectProcessor类。因为你只重写了ConvertMaterial方法,所以这个处理器会使用与默认模型处理器同样的方式处理几何数据。在ConvertMaterial 方法中,你指定材质需要被MaterialCustomEffectProcessor类处理。

    在ModelCustomEffectProcessor类之后,添加这个MaterialCustomEffectProcessor类:

    [ContentProcessor] 
    public class MaterialCustomEffectProcessor : MaterialProcessor
    {
        public override MaterialContent Process(MaterialContent input, ContentProcessorContext context)
        {
            return base.Process(input, context); 
        }
    } 

    从参数可以看出这个处理器处理的是材质。通过从材质处理器继承,你以一个MaterialContent作为输入,然后进行处理,返回更新过的MaterialContent对象。这次,因为你想改变处理MaterialContent对象的方式,所以需要重写MaterialCustomEffectProcessor的Process方法。

    注意:在ModelCustomEffectProcessor类中,你重写了NodeObject 中的所有MaterialContent对象的处理方式。与之类似,因为纹理和effect也是存储在MaterialContent 对象中的,你也可以通过重写MaterialCustomEffectProcessor 类中的BuildEffect或BuildTexture方法改变EffectContent和TextureContent对象的处理方式。

    现在Process方法只是调用了它的基类MaterialProcessor。当然,你需要改变这个方法让你可以改变MaterialContent的处理过程。

    现在,将Process方法的内容改为以下代码:

    EffectMaterialContent myMaterial = new EffectMaterialContent(); 
    string map 	= Path.GetDirectoryName(input.Identity.SourceFilename); 
    string effectFile = Path.Combine(map, "colorchannels.fx"); 
    myMaterial.Effect = new ExternalReference<EffectContent>(effectFile); 

    上述代码会创建一个空的EffectMaterialContent对象并存储一个指向自定义effect文件的链接。首先找到模型存储的文件夹并附加上effect文件的名称,当编译器运行到这行代码时,会将这个effect添加到素材列表中,这样可以自动加载默认EffectProcessor使用的effect,但你也确保effect文件存储在与模型文件相同的目录中。

    大多数模型还包含纹理,你需要将原始MaterialContent对象转换成新的。本例中使用的effect从一个叫做xTexture的纹理中采样颜色,让你可以单独设置红绿篮颜色通道的强度。大多数模型每个effect只有一张纹理,但有些有多张纹理(例如,一个凹凸映射,可见教程5-15)。因为没有和原始effect的纹理序号联系,所以这个代码只能获取xTexture 变量的最后一个纹理,但代码也展示了如何变量多个纹理的方法。

    你可以访问原始effect的纹理集合并将这些纹理变量绑定到自定义effect的HLSL纹理变量上。

    if (input.Textures != null) 
        foreach (string key in input.Textures.Keys) 
    myMaterial.Textures.Add("xTexture", input.Textures[key]); 

    如果你想在加载模型时调整某些HLSL参数,下面是方法:

    myMaterial.OpaqueData.Add("xRedIntensity", 1.1f); 
    myMaterial.OpaqueData.Add("xGreenIntensity", 0.8f); 
    myMaterial.OpaqueData.Add("xBlueIntensity", 0.8f); 

    注意:在HLSL effect文件中,每个像素的红、绿、篮颜色通道需要乘以这个变量。上面的代码让红色比其他颜色强度更大,模拟出太阳照射的效果。这在简单的post-processing effect也是很有用的!

    最后需要返回新建的MaterialContent。你可以直接将它返回,但最好将这个对象传递到默认材质处理器,这样做可以保证错误(例如你没有指定需要的域)会被修正。你可以通过调用base. Process类或使用下列代码做到这点,两者效果一样。

    return context.Convert<MaterialContent, MaterialContent>(myMaterial, "MaterialProcessor"); 

    这行代码搜索叫做MaterialProcessor的处理器并将一个MaterialContent对象作为输入参数并生成另一个MaterialContent对象,这个新创建的Material对象被返回并存储在模型中。

    注意:比较一下这行代码和ConvertMaterial方法中的最后一行代码(译者注:即return context.Convert<MaterialContent, MaterialContent>(material, "MaterialCustomEffectProcessor"))。

    使用自定义Effect 将模型导入到XNA项目中,然后选择新的ModelCustomEffectProcessor,如图4-17所示 (不要选择MaterialCustomEffectProcessor,因为它也在列表中)。

    image

    图4-17 选择自定义模型处理器

    在LoadContent method方法中加载这个模型:

    myModel = content.Load<Model>("tank"); 
    modelTransforms = new Matrix[myModel.Bones.Count]; 

    现在你的模型已经包含了指定的effect!当绘制模型时,你可以使用清晰得多的代码设置参数:

    myModel.CopyAbsoluteBoneTransformsTo(modelTransforms); 
    foreach (ModelMesh mesh in myModel.Meshes) 
    {
        foreach (Effect effect in mesh.Effects) 
        { 
            effect.Parameters["xWorld"]. SetValue(modelTransforms[mesh.ParentBone.Index]); 
            effect.Parameters["xView"].SetValue(fpsCam.GetViewMatrix()); 
            effect.Parameters["xProjection"].SetValue(fpsCam.GetProjectionMatrix()); 
            effect.Parameters["xRedIntensity"].SetValue(1.2f); 
        }
        mesh.Draw();
    } 

    这个方法比教程4-7更加清晰。

    代码

    首先你要让MaterialCustomEffectProcessor处理模型中的每个MaterialContent对象,当然,你还要定义这个自定义材质处理器。

    namespace ModelCustomEffectPipeline
    {
        [ContentProcessor] 
        public class ModelCustomEffectProcessor : ModelProcessor
        {
            protected override MaterialContent ConvertMaterial(MaterialContent material, ContentProcessorContext context) 
            { 
                return context.Convert<MaterialContent, MaterialContent>(material, "MaterialCustomEffectProcessor"); 
            }
        }
        
        [ContentProcessor] 
        public class MaterialCustomEffectProcessor : MaterialProcessor 
        {
            public override MaterialContent Process(MaterialContent input, ContentProcessorContext context) 
            {
                EffectMaterialContent myMaterial = new EffectMaterialContent(); 
                string map = Path.GetDirectoryName(input.Identity.SourceFilename); 
                string effectFile = Path.Combine(map, "colorchannels.fx"); 
                myMaterial.Effect = new ExternalReference<EffectContent>(effectFile); 
                if (input.Textures != null) 
                    foreach (string key in input.Textures.Keys) 
                myMaterial.Textures.Add("xTexture", input.Textures[key]); 
                myMaterial.OpaqueData.Add("xRedIntensity", 1.1f); 
                myMaterial.OpaqueData.Add("xGreenIntensity", 0.8f); 
                myMaterial.OpaqueData.Add("xBlueIntensity", 0.8f); 
                
                return context.Convert<MaterialContent, MaterialContent>(myMaterial,"MaterialProcessor"); 
            }
        }
    } 

    接下来,将模型导入到XNA项目中,选择自定义模型处理器处理这个模型并加载:

    protected override void LoadContent() 
    {
        device = graphics.GraphicsDevice; 
        basicEffect = new BasicEffect(device, null); 
        cCross = new CoordCross(device); 
        myModel = Content.Load<Model>("tank"); 
        modelTransforms = new Matrix[myModel.Bones.Count]; 
    }

    当执行这一步时,模型会加载自定义effect,让你可以在绘制前设置参数:

    protected override void Draw(GameTime gameTime) 
    {
        device.Clear(ClearOptions.Target | ClearOptions.DepthBuffer, Color.CornflowerBlue, 1, 0); 
        cCross.Draw(fpsCam.ViewMatrix, 
        fpsCam.ProjectionMatrix); 
        
        //draw model 
        Matrix worldMatrix = Matrix.CreateScale(0.01f, 0.01f, 0.01f); 
        myModel.CopyAbsoluteBoneTransformsTo(modelTransforms); 
        foreach (ModelMesh mesh in myModel.Meshes) 
        {
            foreach (Effect effect in mesh.Effects) 
            { 
                effect.Parameters["xWorld"]. SetValue(modelTransforms[mesh.ParentBone.Index] * worldMatrix); 
                effect.Parameters["xView"].SetValue(fpsCam.ViewMatrix); 
                effect.Parameters["xProjection"].SetValue(fpsCam.ProjectionMatrix); 
                effect.Parameters["xRedIntensity"].SetValue(1.2f); 
            }
            mesh.Draw(); 
        }
        base.Draw(gameTime); 
    } 

    image

  • 相关阅读:
    hdu4612 无向图中随意加入一条边后使桥的数量最少 / 无向图缩点+求树的直径
    Python 之 安装模块的多种方法
    开源项目Universal Image Loader for Android 说明文档 (1) 简单介绍
    IDEA下使用Jetty进行Debug模式调试
    离线安装Cloudera Manager5.3.4与CDH5.3.4(一)
    让你提前认识软件开发(38):完毕第一个新需求
    Windows App开发之经常使用控件与应用栏
    【剑指Offer学习】【面试题58:二叉树的下一个结点】
    【Win】编写简单的bat文件
    【Linux】MySQL解压版安装及允许远程访问
  • 原文地址:https://www.cnblogs.com/AlexCheng/p/2120131.html
Copyright © 2011-2022 走看看