Hi guys
Ive been experimenting with particle effects and using the particle3D sample from the xna website(link below) as a platform
for experimentation. the effect Im trying to implement in my project is an explosion ring which means laying the billboard parallel
to the terrain ,and even though Ive tried different approaches Im still not quite sure what portion of the code needs to be modified
visuals of what Im working towards
[attachment=18981:fig15.png]
below is what I have so far
a texture that expands and fades out but how do I make it parallel to the ground
[attachment=18982:11 28 2013 5 25 59 PM.jpeg]
code I have so far
main game class
#region Using Statements
using System;
using System.Collections.Generic;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Content;
using Microsoft.Xna.Framework.Graphics;
using Microsoft.Xna.Framework.Input;
#endregion
namespace Particle3DSample
{
/// <summary>
/// Sample showing how to implement a particle system entirely
/// on the GPU, using the vertex shader to animate point sprites.
/// </summary>
public class Particle3DSampleGame : Microsoft.Xna.Framework.Game
{
#region Fields
GraphicsDeviceManager graphics;
SpriteBatch spriteBatch;
SpriteFont font;
Model grid;
// This sample uses five different particle systems.
ParticleSystem explosionParticles;
ParticleSystem explosionSmokeParticles;
ParticleSystem projectileTrailParticles;
ParticleSystem smokePlumeParticles;
ParticleSystem fireParticles;
// The sample can switch between three different visual effects.
enum ParticleState
{
Explosions,
SmokePlume,
RingOfFire,
};
ParticleState currentState = ParticleState.Explosions;
// The explosions effect works by firing projectiles up into the
// air, so we need to keep track of all the active projectiles.
List<Projectile> projectiles = new List<Projectile>();
TimeSpan timeToNextProjectile = TimeSpan.Zero;
// Random number generator for the fire effect.
Random random = new Random();
// Input state.
KeyboardState currentKeyboardState;
GamePadState currentGamePadState;
KeyboardState lastKeyboardState;
GamePadState lastGamePadState;
// Camera state.
float cameraArc = -22;
float cameraRotation = 20;
float cameraDistance = 200;
#endregion
#region Initialization
/// <summary>
/// Constructor.
/// </summary>
public Particle3DSampleGame()
{
graphics = new GraphicsDeviceManager(this);
Content.RootDirectory = "Content";
graphics.PreferredBackBufferWidth = 853;
graphics.PreferredBackBufferHeight = 480;
graphics.MinimumPixelShaderProfile = ShaderProfile.PS_2_0;
// Construct our particle system components.
explosionParticles = new ExplosionParticleSystem(this, Content);
explosionSmokeParticles = new ExplosionSmokeParticleSystem(this, Content);
projectileTrailParticles = new ProjectileTrailParticleSystem(this, Content);
smokePlumeParticles = new SmokePlumeParticleSystem(this, Content);
fireParticles = new FireParticleSystem(this, Content);
// Set the draw order so the explosions and fire
// will appear over the top of the smoke.
smokePlumeParticles.DrawOrder = 100;
explosionSmokeParticles.DrawOrder = 200;
projectileTrailParticles.DrawOrder = 300;
explosionParticles.DrawOrder = 400;
fireParticles.DrawOrder = 500;
// Register the particle system components.
Components.Add(explosionParticles);
Components.Add(explosionSmokeParticles);
Components.Add(projectileTrailParticles);
Components.Add(smokePlumeParticles);
Components.Add(fireParticles);
}
/// <summary>
/// Load your graphics content.
/// </summary>
protected override void LoadContent()
{
spriteBatch = new SpriteBatch(graphics.GraphicsDevice);
font = Content.Load<SpriteFont>("font");
grid = Content.Load<Model>("grid");
}
#endregion
#region Update and Draw
/// <summary>
/// Allows the game to run logic.
/// </summary>
protected override void Update(GameTime gameTime)
{
HandleInput();
UpdateCamera(gameTime);
switch (currentState)
{
case ParticleState.Explosions:
UpdateExplosions(gameTime);
break;
case ParticleState.SmokePlume:
UpdateSmokePlume();
break;
case ParticleState.RingOfFire:
UpdateFire();
break;
}
UpdateProjectiles(gameTime);
base.Update(gameTime);
}
/// <summary>
/// Helper for updating the explosions effect.
/// </summary>
void UpdateExplosions(GameTime gameTime)
{
timeToNextProjectile -= gameTime.ElapsedGameTime;
if (timeToNextProjectile <= TimeSpan.Zero)
{
// Create a new projectile once per second. The real work of moving
// and creating particles is handled inside the Projectile class.
projectiles.Add(new Projectile(explosionParticles,
explosionSmokeParticles,
projectileTrailParticles));
timeToNextProjectile += TimeSpan.FromSeconds(1);
}
}
/// <summary>
/// Helper for updating the list of active projectiles.
/// </summary>
void UpdateProjectiles(GameTime gameTime)
{
int i = 0;
while (i < projectiles.Count)
{
if (!projectiles[i].Update(gameTime))
{
// Remove projectiles at the end of their life.
projectiles.RemoveAt(i);
}
else
{
// Advance to the next projectile.
i++;
}
}
}
/// <summary>
/// Helper for updating the smoke plume effect.
/// </summary>
void UpdateSmokePlume()
{
// This is trivial: we just create one new smoke particle per frame.
smokePlumeParticles.AddParticle(Vector3.Zero, Vector3.Zero);
}
/// <summary>
/// Helper for updating the fire effect.
/// </summary>
void UpdateFire()
{
const int fireParticlesPerFrame = 20;
// Create a number of fire particles, randomly positioned around a circle.
for (int i = 0; i < fireParticlesPerFrame; i++)
{
fireParticles.AddParticle(RandomPointOnCircle(), Vector3.Zero);
}
// Create one smoke particle per frmae, too.
smokePlumeParticles.AddParticle(RandomPointOnCircle(), Vector3.Zero);
}
/// <summary>
/// Helper used by the UpdateFire method. Chooses a random location
/// around a circle, at which a fire particle will be created.
/// </summary>
Vector3 RandomPointOnCircle()
{
const float radius = 30;
const float height = 40;
double angle = random.NextDouble() * Math.PI * 2;
float x = (float)Math.Cos(angle);
float y = (float)Math.Sin(angle);
return new Vector3(x * radius, y * radius + height, 0);
}
/// <summary>
/// This is called when the game should draw itself.
/// </summary>
protected override void Draw(GameTime gameTime)
{
GraphicsDevice device = graphics.GraphicsDevice;
device.Clear(Color.CornflowerBlue);
// Compute camera matrices.
float aspectRatio = (float)device.Viewport.Width /
(float)device.Viewport.Height;
Matrix view = Matrix.CreateTranslation(0, -25, 0) *
Matrix.CreateRotationY(MathHelper.ToRadians(cameraRotation)) *
Matrix.CreateRotationX(MathHelper.ToRadians(cameraArc)) *
Matrix.CreateLookAt(new Vector3(0, 0, -cameraDistance),
new Vector3(0, 0, 0), Vector3.Up);
Matrix projection = Matrix.CreatePerspectiveFieldOfView(MathHelper.PiOver4,
aspectRatio,
1, 10000);
// Pass camera matrices through to the particle system components.
//explosionParticles.SetCamera(view, projection);
//explosionSmokeParticles.SetCamera(view, projection);
projectileTrailParticles.SetCamera( view, projection );
// smokePlumeParticles.SetCamera(view, projection);
//fireParticles.SetCamera(view, projection);
// Draw our background grid and message text.
DrawGrid(view, projection);
DrawMessage();
// This will draw the particle system components.
base.Draw(gameTime);
}
/// <summary>
/// Helper for drawing the background grid model.
/// </summary>
void DrawGrid(Matrix view, Matrix projection)
{
GraphicsDevice device = graphics.GraphicsDevice;
device.RenderState.AlphaBlendEnable = false;
device.RenderState.AlphaTestEnable = false;
device.RenderState.DepthBufferEnable = true;
device.SamplerStates[0].AddressU = TextureAddressMode.Wrap;
device.SamplerStates[0].AddressV = TextureAddressMode.Wrap;
foreach (ModelMesh mesh in grid.Meshes)
{
foreach (BasicEffect effect in mesh.Effects)
{
effect.View = view;
effect.Projection = projection;
}
mesh.Draw();
}
}
/// <summary>
/// Helper for drawing our message text.
/// </summary>
void DrawMessage()
{
string message = string.Format("Current effect: {0}!!!\n" +
"Hit the A button or space bar to switch.",
currentState);
spriteBatch.Begin();
spriteBatch.DrawString(font, message, new Vector2(50, 50), Color.White);
spriteBatch.End();
}
#endregion
#region Handle Input
/// <summary>
/// Handles input for quitting the game and cycling
/// through the different particle effects.
/// </summary>
void HandleInput()
{
lastKeyboardState = currentKeyboardState;
lastGamePadState = currentGamePadState;
currentKeyboardState = Keyboard.GetState();
currentGamePadState = GamePad.GetState(PlayerIndex.One);
// Check for exit.
if (currentKeyboardState.IsKeyDown(Keys.Escape) ||
currentGamePadState.Buttons.Back == ButtonState.Pressed)
{
Exit();
}
// Check for changing the active particle effect.
if (((currentKeyboardState.IsKeyDown(Keys.Space) &&
(lastKeyboardState.IsKeyUp(Keys.Space))) ||
((currentGamePadState.Buttons.A == ButtonState.Pressed)) &&
(lastGamePadState.Buttons.A == ButtonState.Released)))
{
currentState++;
if (currentState > ParticleState.RingOfFire)
currentState = 0;
}
}
/// <summary>
/// Handles input for moving the camera.
/// </summary>
void UpdateCamera(GameTime gameTime)
{
float time = (float)gameTime.ElapsedGameTime.TotalMilliseconds;
// Check for input to rotate the camera up and down around the model.
if (currentKeyboardState.IsKeyDown(Keys.Up) ||
currentKeyboardState.IsKeyDown(Keys.W))
{
cameraArc += time * 0.025f;
}
if (currentKeyboardState.IsKeyDown(Keys.Down) ||
currentKeyboardState.IsKeyDown(Keys.S))
{
cameraArc -= time * 0.025f;
}
cameraArc += currentGamePadState.ThumbSticks.Right.Y * time * 0.05f;
// Limit the arc movement.
if (cameraArc > 90.0f)
cameraArc = 90.0f;
else if (cameraArc < -90.0f)
cameraArc = -90.0f;
// Check for input to rotate the camera around the model.
if (currentKeyboardState.IsKeyDown(Keys.Right) ||
currentKeyboardState.IsKeyDown(Keys.D))
{
cameraRotation += time * 0.05f;
}
if (currentKeyboardState.IsKeyDown(Keys.Left) ||
currentKeyboardState.IsKeyDown(Keys.A))
{
cameraRotation -= time * 0.05f;
}
cameraRotation += currentGamePadState.ThumbSticks.Right.X * time * 0.1f;
// Check for input to zoom camera in and out.
if (currentKeyboardState.IsKeyDown(Keys.Z))
cameraDistance += time * 0.25f;
if (currentKeyboardState.IsKeyDown(Keys.X))
cameraDistance -= time * 0.25f;
cameraDistance += currentGamePadState.Triggers.Left * time * 0.5f;
cameraDistance -= currentGamePadState.Triggers.Right * time * 0.5f;
// Limit the camera distance.
if (cameraDistance > 500)
cameraDistance = 500;
else if (cameraDistance < 10)
cameraDistance = 10;
if (currentGamePadState.Buttons.RightStick == ButtonState.Pressed ||
currentKeyboardState.IsKeyDown(Keys.R))
{
cameraArc = -5;
cameraRotation = 0;
cameraDistance = 200;
}
}
#endregion
}
hlsl code
// Camera parameters.
float4x4 View;
float4x4 Projection;
float ViewportHeight;
// The current time, in seconds.
float CurrentTime;
// Parameters describing how the particles animate.
float Duration;
float DurationRandomness;
float3 Gravity;
float EndVelocity;
float4 MinColor;
float4 MaxColor;
// These float2 parameters describe the min and max of a range.
// The actual value is chosen differently for each particle,
// interpolating between x and y by some random amount.
float2 RotateSpeed;
float2 StartSize;
float2 EndSize;
// Particle texture and sampler.
texture Texture;
sampler Sampler = sampler_state
{
Texture = (Texture);
MinFilter = Linear;
MagFilter = Linear;
MipFilter = Point;
AddressU = Clamp;
AddressV = Clamp;
};
// Vertex shader input structure describes the start position and
// velocity of the particle, and the time at which it was created,
// along with some random values that affect its size and rotation.
struct VertexShaderInput
{
float3 Position : POSITION0;
float3 Velocity : NORMAL0;
float4 Random : COLOR0;
float Time : TEXCOORD0;
};
// Vertex shader output structure specifies the position, size, and
// color of the particle, plus a 2x2 rotation matrix (packed into
// a float4 value because we don't have enough color interpolators
// to send this directly as a float2x2).
struct VertexShaderOutput
{
float4 Position : POSITION0;
float Size : PSIZE0;
float4 Color : COLOR0;
float4 Rotation : COLOR1;
};
// Vertex shader helper for computing the position of a particle.
float4 ComputeParticlePosition(float3 position, float3 velocity,
float age, float normalizedAge)
{
float startVelocity = length(velocity);
// Work out how fast the particle should be moving at the end of its life,
// by applying a constant scaling factor to its starting velocity.
float endVelocity = startVelocity * EndVelocity;
// Our particles have constant acceleration, so given a starting velocity
// S and ending velocity E, at time T their velocity should be S + (E-S)*T.
// The particle position is the sum of this velocity over the range 0 to T.
// To compute the position directly, we must integrate the velocity
// equation. Integrating S + (E-S)*T for T produces S*T + (E-S)*T*T/2.
float velocityIntegral = startVelocity * normalizedAge +
(endVelocity - startVelocity) * normalizedAge *
normalizedAge / 2;
position += normalize(velocity) * velocityIntegral * Duration;
// Apply the gravitational force.
position += Gravity * age * normalizedAge;
// Apply the camera view and projection transforms.
return mul(mul(float4(position, 1), View), Projection);
}
// Vertex shader helper for computing the size of a particle.
float ComputeParticleSize(float4 projectedPosition,
float randomValue, float normalizedAge)
{
// Apply a random factor to make each particle a slightly different size.
float startSize = lerp(StartSize.x, StartSize.y, randomValue);
float endSize = lerp(EndSize.x, EndSize.y, randomValue);
// Compute the actual size based on the age of the particle.
float size = lerp(startSize, endSize, normalizedAge);
// Project the size into screen coordinates.
return size * Projection._m11 / projectedPosition.w * ViewportHeight / 2;
}
// Vertex shader helper for computing the color of a particle.
float4 ComputeParticleColor(float4 projectedPosition,
float randomValue, float normalizedAge)
{
// Apply a random factor to make each particle a slightly different color.
float4 color = lerp(MinColor, MaxColor, randomValue);
// Fade the alpha based on the age of the particle. This curve is hard coded
// to make the particle fade in fairly quickly, then fade out more slowly:
// plot x*(1-x)*(1-x) for x=0:1 in a graphing program if you want to see what
// this looks like. The 6.7 scaling factor normalizes the curve so the alpha
// will reach all the way up to fully solid.
color.a *= normalizedAge * (1-normalizedAge) * (1-normalizedAge) * 6.7;
return color;
}
// Vertex shader helper for computing the rotation of a particle.
float4 ComputeParticleRotation(float randomValue, float age)
{
// Apply a random factor to make each particle rotate at a different speed.
float rotateSpeed = lerp(RotateSpeed.x, RotateSpeed.y, randomValue);
float rotation = rotateSpeed * age;
// Compute a 2x2 rotation matrix.
float c = cos(rotation);
float s = sin(rotation);
float4 rotationMatrix = float4(c, -s, s, c);
// Normally we would output this matrix using a texture coordinate interpolator,
// but texture coordinates are generated directly by the hardware when drawing
// point sprites. So we have to use a color interpolator instead. Only trouble
// is, color interpolators are clamped to the range 0 to 1. Our rotation values
// range from -1 to 1, so we have to scale them to avoid unwanted clamping.
rotationMatrix *= 0.5;
rotationMatrix += 0.5;
return rotationMatrix;
}
// Custom vertex shader animates particles entirely on the GPU.
VertexShaderOutput VertexShader(VertexShaderInput input)
{
VertexShaderOutput output;
// Compute the age of the particle.
float age = CurrentTime - input.Time;
// Apply a random factor to make different particles age at different rates.
age *= 1 + input.Random.x * DurationRandomness;
// Normalize the age into the range zero to one.
float normalizedAge = saturate(age / Duration);
// Compute the particle position, size, color, and rotation.
output.Position = ComputeParticlePosition(input.Position, input.Velocity,
age, normalizedAge);
output.Size = ComputeParticleSize(output.Position, input.Random.y, normalizedAge);
output.Color = ComputeParticleColor(output.Position, input.Random.z, normalizedAge);
output.Rotation = ComputeParticleRotation(input.Random.w, age);
return output;
}
// Pixel shader input structure for particles that do not rotate.
struct NonRotatingPixelShaderInput
{
float4 Color : COLOR0;
#ifdef XBOX
float2 TextureCoordinate : SPRITETEXCOORD;
#else
float2 TextureCoordinate : TEXCOORD0;
#endif
};
// Pixel shader for drawing particles that do not rotate.
float4 NonRotatingPixelShader(NonRotatingPixelShaderInput input) : COLOR0
{
return tex2D(Sampler, input.TextureCoordinate) * input.Color;
}
// Pixel shader input structure for particles that can rotate.
struct RotatingPixelShaderInput
{
float4 Color : COLOR0;
float4 Rotation : COLOR1;
#ifdef XBOX
float2 TextureCoordinate : SPRITETEXCOORD;
#else
float2 TextureCoordinate : TEXCOORD0;
#endif
};
// Pixel shader for drawing particles that can rotate. It is not actually
// possible to rotate a point sprite, so instead we rotate our texture
// coordinates. Leaving the sprite the regular way up but rotating the
// texture has the exact same effect as if we were able to rotate the
// point sprite itself.
float4 RotatingPixelShader(RotatingPixelShaderInput input) : COLOR0
{
float2 textureCoordinate = input.TextureCoordinate;
// We want to rotate around the middle of the particle, not the origin,
// so we offset the texture coordinate accordingly.
textureCoordinate -= 0.5;
// Apply the rotation matrix, after rescaling it back from the packed
// color interpolator format into a full -1 to 1 range.
float4 rotation = input.Rotation * 2 - 1;
textureCoordinate = mul(textureCoordinate, float2x2(rotation));
// Point sprites are squares. So are textures. When we rotate one square
// inside another square, the corners of the texture will go past the
// edge of the point sprite and get clipped. To avoid this, we scale
// our texture coordinates to make sure the entire square can be rotated
// inside the point sprite without any clipping.
textureCoordinate *= sqrt(2);
// Undo the offset used to control the rotation origin.
textureCoordinate += 0.5;
return tex2D(Sampler, textureCoordinate) * input.Color;
}
// Effect technique for drawing particles that do not rotate. Works with shader 1.1.
technique NonRotatingParticles
{
pass P0
{
VertexShader = compile vs_1_1 VertexShader();
PixelShader = compile ps_1_1 NonRotatingPixelShader();
}
}
// Effect technique for drawing particles that can rotate. Requires shader 2.0.
technique RotatingParticles
{
pass P0
{
VertexShader = compile vs_1_1 VertexShader();
PixelShader = compile ps_2_0 RotatingPixelShader();
}
}
link to original xna project
http://xbox.create.msdn.com/en-US/education/catalog/sample/particle_3d
Thanks Guys