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:
解决这个问题并不困难,我希望仍然使用传统的老方法来设置状态。首先,自然是定义一系列常量,表示所有渲染状态,这里使用了一个静态类,当然替换为枚举也是一样的。
{
//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的行为:
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有更好的性能:
{
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)
}