Hey gangâ€¦ New MonoGame dev here. Iâ€™ve managed to migrate my game over to MonoGame in no time. The only holdover is cleaning up the Particle System. Iâ€™ve read over several solutions with porting from XNA to MonoGame and implemented those solutions but Iâ€™m still running into issues. Iâ€™m changed my shorts to Vector2s and reordered my vertex declaration and everything compiles.

NOW here is one crazy partâ€¦ if I swap NORMAL0 NORMAL1 assignments, I get some results but itâ€™s off (expected but odd that I get results swapping at all). The image below show the triangles rendering at an angle. They are not shifting to face the viewport as they did in XNAâ€¦

Iâ€™m sure itâ€™s something stupid Iâ€™m missingâ€¦ Thoughts?

My vertex declaration:

` struct ParticleVertex {

// Stores the starting position of the particle.

public Vector3 Position;

```
// Stores which corner of the particle quad this vertex represents.
public Vector2 Corner;
// Stores the starting velocity of the particle.
public Vector3 Velocity;
// Four random values, used to make each particle look slightly different.
public Color Random;
// The time (in seconds) at which this particle was created.
public float Time;
public static readonly VertexDeclaration VertexDeclaration = new VertexDeclaration
(
new VertexElement(0, VertexElementFormat.Vector3,
VertexElementUsage.Position, 0),
new VertexElement(12, VertexElementFormat.Vector2,
VertexElementUsage.Normal, 0),
new VertexElement(20, VertexElementFormat.Vector3,
VertexElementUsage.Normal, 1),
new VertexElement(32, VertexElementFormat.Color,
VertexElementUsage.Color, 0),
new VertexElement(36, VertexElementFormat.Single,
VertexElementUsage.TextureCoordinate, 0)
);
public const int SizeInBytes = 40;
}`
```

My shader code is here:

`

// Camera parameters.

float4x4 View;

float4x4 Projection;

float2 ViewportScale;

```
// 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 : SV_POSITION;
float2 Corner : NORMAL0;
float3 Velocity : NORMAL1;
float4 Random : COLOR0;
float Time : TEXCOORD0;
};
// Vertex shader output structure specifies the position and color of the particle.
struct VertexShaderOutput
{
float4 Position : SV_POSITION;
float4 Color : COLOR0;
float2 TextureCoordinate : 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(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;
}
// 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.
float2x2 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;
float rotation = 0;
// Compute a 2x2 rotation matrix.
float c = cos(rotation);
float s = sin(rotation);
return float2x2(c, -s, s, c);
}
// Custom vertex shader animates particles entirely on the GPU.
VertexShaderOutput ParticleVertexShader(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);
float size = ComputeParticleSize(input.Random.y, normalizedAge);
float2x2 rotation = ComputeParticleRotation(input.Random.w, age);
output.Position.xy += mul(input.Corner, rotation) * size * ViewportScale;
output.Color = ComputeParticleColor(output.Position, input.Random.z, normalizedAge);
output.TextureCoordinate = (input.Corner + 1) / 2;
return output;
}
// Pixel shader for drawing particles.
float4 ParticlePixelShader(VertexShaderOutput input) : COLOR0
{
return tex2D(Sampler, input.TextureCoordinate) * input.Color;
}
// Effect technique for drawing particles.
technique Particles
{
pass P0
{
VertexShader = compile vs_4_0_level_9_1 ParticleVertexShader();
PixelShader = compile ps_4_0_level_9_1 ParticlePixelShader();
}
}
```

`