Clayman's Graphics Corner

DirectX,Shader & Game Engine Programming

  博客园 :: 首页 :: 博问 :: 闪存 :: 新随笔 :: 联系 :: 订阅 订阅 :: 管理 ::

XNA中的Render State管理

仅供个人学习使用,请勿转载,勿用于任何商业用途。

The Problem:
       XNA中一个设计的非常不好的地方,就是把各种render state定义为RenderState类的成员,而不是枚举。在DX/MDX中,如果有一系列render state需要设置,只需要
foreach state in renderStates
    gfxDevice.SetRenderState( stateName,stateValue);

    简单,明了。
    而同样的操作在XNA中,则要麻烦很多,你不得不”显式” 访问每一个render state成员来设置相应的值:
gfxDevice.RenderState.XXXX = value;
gfxDevice.RenderState.XXXX = value;
……

      这样的代码非糟糕,简直是在”hard code”。假如事先不确定有多少render state需要设置,如何编码呢?

The Solution:
      解决这个问题并不困难,我希望仍然使用传统的老方法来设置状态。首先,自然是定义一系列常量,表示所有渲染状态,这里使用了一个静态类,当然替换为枚举也是一样的。

public static class HaRenderStateId
{
    
//alpha state
    public const byte AlphaBlendEnable = 0;
    
public const byte AlphaBlendOperation = 1;
    
public const byte SourceBlend = 2;
    
public const byte DestinationBlend = 3;
    
public const byte AlphaDestinationBlend = 4;
    
public const byte AlphaSourceBlend = 5;
    
public const byte AlphaFunction = 6;
    
public const byte AlphaTestEnable = 7;
    
public const byte BlendFactor = 8;
    
public const byte BlendFunction = 9;
    
public const byte SaparateAlphaBlendEnable = 10;
    
public const byte ReferenceAlpha = 11;

    
//depth state
    public const byte DepthWriteEnable = 12;
    .
}

 
      接下来,需要统一所有render state的值。这样,才能把每种render state作为相同的key-value对,放到同一个容器中。观察一下所有的render state,他们的值不外乎以下几种:枚举,int,float,bool,color. 非常幸运,他们都能用一个32位的值来表示。但是具体如何编码呢?这种情况下,C++程序员首先想到的一定是使用union。C#中虽然没有内置的union类型,但这并不妨碍我们用struct模拟union的行为:

[StructLayout(LayoutKind.Explicit)]
public struct HaRenderStateValue
{
    [FieldOffset(
0)]
    
public int IntegerValue;

    [FieldOffset(
0)]
    
public float FloatValue;

    [FieldOffset(
0)]
    
public Color Color;

    [FieldOffset(
0)]
    
public bool BooleanValue;

    
public void SetValue(int value)
    
public void SetValue(Color value)
    
public void SetValue(float value)
    
public void SetValue(bool value)
}

public struct HaRenderState
{
    
public readonly byte Id;
    
public HaRenderStateValue Value;
}

 
    最后则是实际设置渲染状态的函数,这一步并没有什么技巧可言,只是用了一个非常长的switch把所有需要hard code的部分集中到这里。这是目前为止我能想到的最简单有效的方法,使用Dictionary也许能减少代码长度,但实际测试表明,switch有更好的性能:

 public static void SetRenderState(GraphicsDevice graphics, HaRenderState renderState)
 {
     
switch (renderState.Id)
     {
         
case HaRenderStateId.AlphaBlendEnable:
             graphics.RenderState.AlphaBlendEnable 
= renderState.Value.BooleanValue;
             
break;

         
case HaRenderStateId.AlphaBlendOperation:
             graphics.RenderState.AlphaBlendOperation 
= (BlendFunction)renderState.Value.IntegerValue;
             
break;

         
case HaRenderStateId.SourceBlend:
             graphics.RenderState.SourceBlend 
= (Blend)renderState.Value.IntegerValue;
             
break;
        
      }
}


        扩展结束,现在可以像文章开头介绍的那样,使用循环设置所有render state,更重要的是可以随时添加或者删除集合中的render state,这样给为渲染器设计带来了非常大的便利和灵活性:

material.AddRenderState(RenderState)
material.RemoveRenderState(RenderState)
......
material.ApplyRenderState()

{
      foreach renderState in this.renderStates
           SetRenderState(gfxDevice,renderState)
}

 

posted on 2009-06-23 19:20  clayman  阅读(1416)  评论(3编辑  收藏  举报