Stretched Billboard Projected Particles

Started by
4 comments, last by Hodgman 6 years ago

Hello,

I am trying to recreate a feature that exists in Unity which is called Stretched Billboarding. However I am having a hard time figuring out how to use a particle velocity to rotate and stretch the particle-quad accordingly.

Here's a screenie of unity's example:

image.png.67dba716a590c8f21c0ad9e1244f2ff1.png

 

Depending on the velocity of the particle, the quad rotates and stretches, but it is still always facing the camera.
In my current solution I have normal billboarding and velocities and particle-local rotations are working fine.
I generate my quads in a geometry-shader, if that makes any difference.
So does anyone have any thoughts of how to achieve this?

Best regards

Hampus

Game-programmer, mostly specialized in graphics programming.

Visit my portfolio to learn more: https://www.hampussiversson.com

Advertisement

Hi, for this effect you should first calculate the view space particle velocity from the world space velocity:


float4x4 g_xCamera_View; // camera view matrix
// ...

float3 velocity = mul(particle.velocity, (float3x3)g_xCamera_View);

Then after the quad is already expanded/created (and rotated), for each vertex of the quad do this (before the quad vertices are projected):


float xParticleMotionBlurAmount; // particle system setting: how much motion blur affects the particles...
// ...

quadPos += dot(quadPos, velocity) * velocity * xParticleMotionBlurAmount;

This means that vertices which are aligned with the view space motion vector get extruded by the motion vector. This solution doesn't rotate according to the velocity, but I found that only extruding already gives the results what I needed. And this way I can still add rotational animation from the particle system settings.

Also, you most likely can avoid using the geometry shader and you should consider doing so. The quad expansion can be done entirely in the vertex shader. If you are interested, you can take a look at my particle quad vertex shader: https://github.com/turanszkij/WickedEngine/blob/master/WickedEngine/emittedparticleVS.hlsl

Good luck! :)

To define an orientation for each quad, you need three perpendicular unit vectors, for the local x, y, z axes. 

You've set a few constraints: (1) one axis should be facing the camera, (2) one axis should be facing the direction of motion. 

That leaves one free axis (sideways), which you can compute with the cross product of the above two vectors. 

e.g. z = to_camera; y = normalize(velocity); x = cross(z, y);

At this point though, x is perpendicular to both y and z, but there's no guarantee that y and z are perpendicular to each other, which would result in skewing. Depending on which is more important (facing the camera, or stretching in the direction of motion), you can recompute the less important one (e.g. z = cross(x, y)). 

x, y, z are then a 3x3 rotation matrix defining the orientation of your quad. You can stretch it in the y axis according to velocity if you want fast particles to be longer than slow ones. 

2 hours ago, turanszkij said:

Hi, for this effect you should first calculate the view space particle velocity from the world space velocity:



float4x4 g_xCamera_View; // camera view matrix
// ...

float3 velocity = mul(particle.velocity, (float3x3)g_xCamera_View);

Then after the quad is already expanded/created (and rotated), for each vertex of the quad do this (before the quad vertices are projected):



float xParticleMotionBlurAmount; // particle system setting: how much motion blur affects the particles...
// ...

quadPos += dot(quadPos, velocity) * velocity * xParticleMotionBlurAmount;

This means that vertices which are aligned with the view space motion vector get extruded by the motion vector. This solution doesn't rotate according to the velocity, but I found that only extruding already gives the results what I needed. And this way I can still add rotational animation from the particle system settings.

Also, you most likely can avoid using the geometry shader and you should consider doing so. The quad expansion can be done entirely in the vertex shader. If you are interested, you can take a look at my particle quad vertex shader: https://github.com/turanszkij/WickedEngine/blob/master/WickedEngine/emittedparticleVS.hlsl

Good luck! :)

Thank you for the answer, this solution gave me kind of the stretching that I was looking for, but as you said it doesn't account for the rotation, which is one of the major things that I need for this effect.

2 hours ago, Hodgman said:

To define an orientation for each quad, you need three perpendicular unit vectors, for the local x, y, z axes. 

You've set a few constraints: (1) one axis should be facing the camera, (2) one axis should be facing the direction of motion. 

That leaves one free axis (sideways), which you can compute with the cross product of the above two vectors. 

e.g. z = to_camera; y = normalize(velocity); x = cross(z, y);

At this point though, x is perpendicular to both y and z, but there's no guarantee that y and z are perpendicular to each other, which would result in skewing. Depending on which is more important (facing the camera, or stretching in the direction of motion), you can recompute the less important one (e.g. z = cross(x, y)). 

 x, y, z are then a 3x3 rotation matrix defining the orientation of your quad. You can stretch it in the y axis according to velocity if you want fast particles to be longer than slow ones. 

I am a bit confused of the spaces that you would do these operations. Are you suggesting this in view-space?

When I tried this, it feels like I'm in the wrong space, and I've tried to convert both my velocity, positioning and to_camera vectors to both world & view space, but something is still wrong.

 

This is how I do the operations at the moment.


float3 z = normalize(mul(viewOnlyRot, cameraPosition - input[0].myWPosition.xyz));
float3 y = normalize(input[0].myVelocity);
float3 x = normalize(cross(z,y));

float3x3 rotMat = 
{
    x.x, x.y, x.z,
    y.x, y.y, y.z,
    z.x, z.y, z.z
};

vertex.myPosition.xyz = input[0].myPosition.xyz;
vertex.myPosition.xyz = mul(rotMat, vertex.myPosition.xyz);

 

Game-programmer, mostly specialized in graphics programming.

Visit my portfolio to learn more: https://www.hampussiversson.com

On 5/3/2018 at 10:15 PM, Hampus Siversson said:

I am a bit confused of the spaces that you would do these operations. Are you suggesting this in view-space?

The coordinate space shouldn't matter as long as you're consistent. Any will work. 

From the looks of it, you're passing the data into the float3x3 constructor in tow-major order but then using a column-vector matrix multiplication order later on (which would produce the inverse rotation). 

You can also try writing it out by hand instead of using a matrix:

New = old.x * x + old.y * y + old.z * z;

This topic is closed to new replies.

Advertisement