• 处理模型——使用自定义Effects和纹理绘制模型


    问题

    当你使用教材4-1中的代码将一个模型加载到XNA项目时,你使用的是一个BasicEffect实例。在简单的情况下BasicEffect可以很好地绘制模型,当我们常常想使用一个不同的,自定义的effect绘制一个模型。

    解决方案

    通过将模型的effect包含在BasicEffect对象中,你可以获取effect的所有信息,诸如纹理和材质等信息。当你将这些属性复制到某个安全的地方后,就可以用你选择的effect覆盖这个effect。

    注意:你还可以使用自定义内容处理器更加清晰地完成这个任务,可见教材4-12。

    工作原理

    首先你需要加载一个模型和自定义effect,两者都需要使用内容管道进行加载:

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

    本例中的自定义effect会使用灰度值绘制模型(译者注:本例中的灰度实现使用最简单的三个颜色通道求平均值的方法,而不是更好的给绿色更多的权重)。

    因为effect是存储在模型的ModelMesh中的,所以你可以简单地使用自定义effect覆盖它们。但是,所有原始effect信息都会丢失!这些信息包括了纹理和材质颜色信息。

    在覆盖模型的默认effect前,你想存储这些信息,因为自定义effect需要知道某些信息,例如使用哪个纹理。

    你将创建一个方法处理模型和自定义effect,这个方法会使用自定义effect的副本替换模型中的所有effect。最后,存储初始数据,返回初始effect中所有纹理的数组,这样你就可以将它们传递到自定义effect。本例中,所有的effect都会被自定义effect复写,但a small adaptation suffices to override only the effect of a particular ModelMeshPart(译者:?):

    private Texture2D[] ChangeEffect(ref Model model, Effect newEffect) 
    {
    } 

    因为你想永久地改变模型,所以model参数要使用引用(在第一个变量前加上ref)。否则会使用一个本地副本,而对于这个副本所有改变都不会被调用代码获知。

    首先,你要创建一个被原始effect使用的所用纹理的副本:

    List<Texture2D> textureList = new List<Texture2D>(); 
    foreach (ModelMesh mesh in model.Meshes) 
        foreach (ModelMeshPart modmeshpart in mesh.MeshParts) 
        {
            BasicEffect oldEffect = (BasicEffect)modmeshpart.Effect; 
            textureList.Add(oldEffect.Texture); 
        }
        Texture2D[] textures = textureList.ToArray(); 
        return textures; 

    你创建了一个集合存储纹理,然后遍历每个ModelMesh的不同ModelMeshParts,每个ModelMeshPart包含指向effect的链接。将这个effect传递到一个BasicEffect对象,这样你就可以访问到它的属性,例如纹理。对于模型中的每个effect都附加上一个纹理集合中的纹理。

    收集完所有的纹理后,你将集合转换为一个数组并将这个数组返回。

    现在你所做的操作会使用自定义effect的副本覆盖原始effect,所以在第二个循环中添加以下代码:

     modmeshpart.Effect = newEffect.Clone(device); 

    注意:你使用了Clone方法创建了一个effect的副本,你想使用这个副本,否则所有的ModelMeshPart都会共享相同的effect。通过给每个ModelMeshPart施加各自effect的副本,每个ModelMeshPart都能使用不同的纹理。

    在LoadContent方法中加载了模型和自定义effect之后调用这个方法:

    modelTextures = ChangeEffect(ref myModel, customEffect); 

    当绘制模型时,你就可以单独定义自定义effect的每个参数了:

    int i = 0; 
    Matrix worldMatrix = Matrix.CreateScale(0.01f); 
    myModel.CopyAbsoluteBoneTransformsTo(modelTransforms); 
    foreach (ModelMesh mesh in myModel.Meshes) 
    {
        foreach (Effect currentEffect in mesh.Effects) 
        { 
            currentEffect.Parameters["xWorld"]. 
            SetValue(modelTransforms[mesh.ParentBone.Index]*worldMatrix); 
            currentEffect.Parameters["xView"].SetValue(fpsCam.ViewMatrix); 
            currentEffect.Parameters["xProjection"].SetValue(fpsCam.ProjectionMatrix); 
            currentEffect.Parameters["xTexture"].SetValue(modelTextures[i++]); 
        } 
        mesh.Draw(); 
    } 

    请注意你是如何将原始effect信息传递到自定义的effect中的,例如本例中的纹理。

    遍历ModelMeshPart而不是Effect

    前面的循环中遍历了所有effect,但这样做当你想设置一个特定ModelMeshPart的effect时会出现问题,所以你遍历ModelMesh的ModelMeshPart而不是它的effect:

    int i = 0; 
    Matrix worldMatrix = Matrix.CreateScale(0.01f); 
    myModel.CopyAbsoluteBoneTransformsTo(modelTransforms); 
    foreach (ModelMesh mesh in myModel.Meshes) 
    {
        foreach (ModelMeshPart part in mesh.MeshParts) 
        {
            Effect currentEffect = part.Effect; 
            currentEffect.Parameters["xWorld"]. SetValue(modelTransforms[mesh.ParentBone.Index] * worldMatrix);
            currentEffect.Parameters["xView"].SetValue(fpsCam.ViewMatrix); 
            currentEffect.Parameters["xProjection"].SetValue(fpsCam.ProjectionMatrix); 
            currentEffect.Parameters["xTexture"].SetValue(modelTextures[i++]); 
        }
        mesh.Draw(); 
    } 
    存储所有原始信息

    如果你还想存储的纹理之外的信息,只需简单地将原始effect添加到集合替代纹理:

    BasicEffect[] originalEffects; 

    在ChangeEffect方法中,将原始effect添加到集合中去:

    BasicEffect oldEffect = (BasicEffect)modmeshpart.Effect; 
    effectList.Add(oldEffect); 

    当设置自定义effect的参数时,你可以很容易地访问到初始信息:

    currentEffect.Parameters["xTexture"].SetValue(originalEffects[i++].Texture); 
    代码

    在LoadContent方法中,加载模型和自定义effect:

    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]; 
        customEffect = Content.Load<Effect>("bwcolor"); 
        originalEffects = ChangeEffect(ref myModel, customEffect); 
    } 

    最后一行代码使用自定义effect的副本替换了模型中的effect,是在下列方法中实现的:

    private BasicEffect[] ChangeEffect(ref Model model, Effect newEffect)
    {
        List<BasicEffect> effectList = new List<BasicEffect>(); 
        foreach (ModelMesh mesh in model.Meshes) 
            foreach (ModelMeshPart modmeshpart in mesh.MeshParts) 
            {
                BasicEffect oldEffect = (BasicEffect)modmeshpart.Effect; 
                effectList.Add(oldEffect); 
                modmeshpart.Effect = newEffect.Clone(device); 
            }
        BasicEffect[] effects = effectList.ToArray(); 
        return effects; 
    } 

    当绘制模型时,你可以像这样访问到存储的信息:

    int i = 0; 
    Matrix worldMatrix = Matrix.CreateScale(0.01f); 
    myModel.CopyAbsoluteBoneTransformsTo(modelTransforms); 
    foreach (ModelMesh mesh in myModel.Meshes) 
    {
        foreach (ModelMeshPart part in mesh.MeshParts) 
        {
            Effect currentEffect = part.Effect; 
            currentEffect.Parameters["xWorld"]. SetValue(modelTransforms[mesh.ParentBone.Index] * worldMatrix);
            currentEffect.Parameters["xView"].SetValue(fpsCam.ViewMatrix); 
            currentEffect.Parameters["xProjection"].SetValue(fpsCam.ProjectionMatrix); 
            currentEffect.Parameters["xTexture"].SetValue(originalEffects[i++].Texture); 
        } 
        mesh.Draw(); 
    } 

    1

  • 相关阅读:
    为sublime text2 添加SASS语法高亮
    下拉框点链接js
    [JavaScript] 初中级Javascript程序员必修学习目录
    判断页数及切换
    切换加上延迟加载js代码
    jquery 简单弹出层(转)
    左右滑动删除ListView条目Item--第三方开源--SwipeToDismiss
    使用自定义的item、Adapter和AsyncTask、第三方开源框架PullToRefresh联合使用实现自定义的下拉列表(从网络加载图片显示在item中的ImageView)
    从一个URL下载原始数据,基于byte字节,得到byte数组
    动画气泡指示当前滑动值--第三方开源--DiscreteSeekbar
  • 原文地址:https://www.cnblogs.com/AlexCheng/p/2120138.html
Copyright © 2020-2023  润新知