AppleSeeker's Tech Blog
Welcome to AppleSeeker's space

为了尽量让使用天空体容易,我们将在游戏框架中整合一个特殊的天空体对象,允许我们来确保它是在场景中的任何内容的之前渲染。

天空体创建一个新的游戏框架类:MatrixSkyboxObject。该类继承于MatrixObjectBase,它所对应的继承关系如图所示。

image

在类中,为了天空体能包含立方体的4个面(包括顶部和底部面),代码创建一个顶点缓冲,所有面都朝向内部。它们使用纹理坐标来配置,在盒子内部围绕所提供的纹理。

在天空体类的Update方法中,相机坐标通过读取它的Transformation.Translation属性来取得,并设置为天空体得自己的坐标。这样做确保天空体总是在相机中出现相同距离,无论在场景中相机是放置在何处。从相机的转换矩阵中读取坐标来确保无论相机如何放置都能取得精确读取(相机的Position属性不可能反映它的最终位置,依靠相机如何转换)。下面将描述Update函数代码。

public override void  Update(GameTime  gameTime)
{
    base.Update(gameTime);

    // Calculate  the  transformation matrix
    SetIdentity();

    // Observe the  camera's position if one is active 
    if (Game.Camera  !=  null)
    {
        // Read  the  camera's calculated position
        ApplyTransformation( Matrix.CreateTranslation(Game.Camera.Transformation.Translation));
    }

    // Now  apply  the  standard  transformations
    ApplyStandardTransformations();
}

当绘制天空体时,首先它关闭任何可能激活和禁止深度缓冲的光照。光照是非常重要的,因为我们不想光照计算应用于天空体—这不是真正天空是如何工作的。禁用深度缓冲确保随后渲染的对象将不会出现在天空后面,这种效果同样看起来是不切实际的。

随着渲染配置,绘制天空体。一旦绘制完成,光照和深度缓冲将设置回前一个状态。

下面代码关于Draw函数。

public override void  Draw(Microsoft.Xna.Framework.GameTime  gameTime, Effect effect)
{
    // Prepare the  effect for drawing
    PrepareEffect(effect);

    // Disable  lighting but  remember  whether it was switched  on... 
    bool lightingEnabled = ((BasicEffect)effect).LightingEnabled; 
    
    ((BasicEffect)effect).LightingEnabled = false;
    // Disable  the  depth buffer
    DepthStencilState depthState  = effect.GraphicsDevice.DepthStencilState;
    effect.GraphicsDevice.DepthStencilState = DepthStencilState.None;

    // Set the  active vertex  buffer 
    effect.GraphicsDevice.SetVertexBuffer(_vertexBuffer);

    // Draw  the  object
    foreach  (EffectPass pass in effect.CurrentTechnique.Passes)
    {
        // Apply the  pass pass.Apply();
        // Draw  the  sky box 
        effect.GraphicsDevice.DrawPrimitives(PrimitiveType.TriangleList, 0, _vertices.Length / 3);
    }

    // Re-enable lighting and the  depth buffer if required
    if (lightingEnabled) 
        ((BasicEffect)effect).LightingEnabled = true;
    
    effect.GraphicsDevice.DepthStencilState = depthState;
}

为了正确绘制天空体,第一件需要做的事情是在场景中去绘制。因此我们可以确保这种情况下,在GameHost类,我们将天空体中作为特殊的游戏。用同样的方式,我们添加一个Camera属性来允许相机可以在适当的点进行处理,所以我们也将添加一个Skybox属性。这个新属性可以设置到MatrixSkyboxObject类的实例中,或任何从它派生出的类。

在相机后,GameHost.UpdateAll函数直接更新天空体对象,以便相机位置可以通过天空体中读取。在相机后,GameHost.DrawObjects函数绘制天空体,但在任何更远的对象渲染之前。为了确保渲染天空体仅出现一次绘制(即使DrawObjects被调用多次),一个内部类变量是用来追踪绘制操作,放置重复绘制操作发生。

总体来说,实现提供一个简单而有效的天空体,对于很多游戏来说将足够了,特别是那些保持相机以便它是水平方向中主要的。如果需要的话,类可以很容易扩展来提供完全立方体几何学。

posted on 2012-07-05 13:45  AppleSeeker(冯峰)  阅读(725)  评论(0编辑  收藏  举报