Kamerawackeln als Post Processing Effect

26.05.2009 17:57

Jeder aktuelle Spieletitel setzt Post Processing Effects ein, um dem Spiel eine gewisse Note zu verleihen oder besonders spektakuläre Spezialeffekte umzusetzen. Allerdings muss ein derartiger Effekt nicht immer sehr aufwändig sein, um dem Spiel zu einem besseren "Feeling" zu verhelfen. Ein Kamerawackler, der zum Beispiel eingesetzt werden kann, wenn der eigene Panzer getroffen wurde unterstützt die Action eines Spiels. Ein Kamerawackeln lässt sich sehr leicht als Post Processing Effect realisieren.

Zur Realisierung des Effekts muss die Szene in einem Render Target vorliegen, dass wir als Textur an einen Shader geben können. Der Pixel Shader macht nichts anderes, als die Texturkoordinaten zu verzerren, so dass die komplette Szene auf einer Ellipse bewegt wird. Die Translation muss nur schnell genug stattfinden, damit die Verzerrung als Kamerawackeln erscheint. Wie folgt sieht der Pixel Shader aus:
float fTime;

uniform extern texture ScreenTexture;
sampler ScreenS = sampler_state
{
Texture = <ScreenTexture>;
};

float4 PixelShaderFunction(float2 oTexCoord : TEXCOORD0) : COLOR0
{
  return tex2D(ScreenS,
    oTexCoord + float2(cos(fTime * 1000.0f) * 0.01f,
    sin(1.0f + (1.5f - fTime) * 1000.0f) * 0.0005f) * fTime);
}
Der Pixel Shader addiert zu den Texturkoordinaten einen zweiten Vektor. Jener Vektor errechnet sich aus der Kosinus- und Sinusfunktion, um eine Kreisbewegung zu erzeugen. Die Variable fTime wird von der Anwendung hereingegeben und geht gegen 0. Somit nimmt das Kamerawackeln gegen Ende stetig ab. Da die Zeit in Sekunden angegeben wird, skaliert der Pixel Shader die Zeit mit einem Faktor von 1000. Die Ellipse entsteht dadurch, dass die Kosinus- und Sinusfunktion unterschiedlich skaliert wird.
Die Technique sieht folgendermaßen aus:
technique
{
  pass P0
  {
    PixelShader = compile ps_2_0 PixelShaderFunction);
  }
}
Die Szene wird mit einem BasicEffect-Objekt gerendert:
oSpriteBatch.Begin(SpriteBlendMode.None, SpriteSortMode.Immediate, SaveStateMode.None);
oEffect.Parameters["fTime"].SetValue(m_fTime);

oEffect.Begin();
oEffect.CurrentTechnique.Passes[0].Begin();
oSprite.Draw(oTexture, new Rectangle(0, 0,
  this.GraphicsDevice.Viewport.Width,
  this.GraphicsDevice.Viewport.Height), Color.White);
oSprite.End();

oEffect.CurrentTechnique.Passes[0].End();
oEffect.End();
Im nächsten Blogeintrag werden wir uns dann einer kleinen Druckwelle für zweidimensionale Spiele widmen. Diese kann ebenfalls mit einer Sinusfunktion realisiert werden und erweckt dennoch Eindruck beim Benutzer.  
© 2009 Jens Konerow