* Computes the alpha transparency for a flying ember effect.
* This function determines whether a pixel is inside a moving ember
* and applies a soft fade effect at the edges.
* @param uv - (float2) The UV coordinates of the fragment in range [0,1].
* @param velocity - (float) Speed at which the ember moves horizontally.
* @param width - (float) Radius of the ember.
* @param feather - (float) Controls how soft the ember fades at the edges.
* @param alpha - (out float) Output alpha transparency for this fragment.
void FlyingEmbers_float(float2 uv, float velocity, float width, float feather, out float alpha)
// Convert UV from [0,1] range to [-1,1] range
// Scale X by (velocity + width) and Y by width
uv.x *= velocity + width;
// Define the movement vector (direction of ember motion)
float2 v = float2(2 * velocity, 0);
// Compute the position of the ember relative to the moving direction
float2 p = uv - float2(velocity, 0);
// Quadratic equation coefficients for ray-circle intersection
float a = dot(v, v); // v•v (squared magnitude of velocity vector)
float b = dot(v, p); // v•p (projection of position onto velocity)
float c = dot(p, p) - r*r; // (distance squared - radius squared)
float d = b * b - a * c; // Discriminant of quadratic equation
// **Edge fading effect**
// Compute the shortest distance from the ember centerline
float h = saturate(-b / a);
float dist = length(p + h * v);
// Use smoothstep to fade alpha at the ember's edges
alpha = smoothstep(0, feather * width, width - dist);
// **Handle ray-circle intersection**
if (d > 0.0) // If discriminant is positive, there's an intersection
d = sqrt(d); // Compute square root of the discriminant
// Solve for t0 and t1 (the intersection points along the ray)
float t0 = max(0, (-b - d) / a);
float t1 = min(1, (-b + d) / a);
// Modify alpha based on the segment of the ray inside the ember
alpha *= saturate(2 * (t1 - t0));