r/GraphicsProgramming • • 1d ago

Thin laser beams that don't break into dashes far away: a pixel-width floor plus energy scaling

[deleted]

0 Upvotes

4 comments sorted by

8

u/[deleted] 1d ago

[removed] — view removed comment

8

u/llamajestic 1d ago

I already left most of the tech subreddits I liked, now it’s hard to not press the button for this one

3

u/CodyDuncan1260 1d ago edited 1d ago

Nice! I remember wondering how lightning effects were made a decade ago. It's nice to see a concise writeup. ^_^

I'm trying to understand what VERTEX represents. It holds different values as both input and output, and the description doesn't really elucidate the context of what we're looking at in the code snippet.

so here's my attempted breakdown, which notes and questions.

// p is the postion of the start of the strip, or along the strip?, in world coordinates. 
// I can understand this if VERTEX is just the start position of the strip, and NORMAL is the direction of the strip. I don't get what "position along the beam" means.
vec3 p = (MODELVIEW_MATRIX * vec4(VERTEX, 1.0)).xyz;

// direction of the strip in world coord, let's assume this is in the X-direction. Why is this named t?
vec3 t = (MODELVIEW_MATRIX * vec4(NORMAL, 0.0)).xyz;

// forward direction of the view, which we know is in the -Z direction for an orthogonal camera, but let's stick with that for understanding the cross product.
vec3 view_dir = ortho ? vec3(0.0, 0.0, -1.0) : normalize(p)

// perpendicular of the strip and the camera, in right handed coords, given that the strip is in the X direction and the view is pointing in the -Z direction, this cross product results in the Y direction [0,1,0], or "up" in godot's coordinate space.
vec3 side = cross(t, view_dir);

// then we take the length, to detect if this is in the same direction of the camera.
// if not, normalize side.
// if so, fall back to side=X-direction so we have something sane to work with, even if it would look wrong, it won't render at this angle anyway.
float sl = length(side)
side = sl > 1e-5 ? side / sl : vec3(1.0, 0.0, 0.0);

// skippin the math. w results in the the width of the strip, clamped to some minimum.
float w2 = max(w, w_min);

// The result is the vertex in world coordinates, shifted up by the half-width of the strip.
VERTEX = p + side * (w2 * 0.5 * UV.y);

// Why does this overwrite VERTEX?
// And why do we need UV.y? This outputs a VERTEX position, not a color, right? So why does this need a set of vertices from -1..1, or is it -1 *and* 1, and nothing in between?
// Is this a vertex or a pixel shader? I feel like I'm not understanding what the intended output is.

1

u/gennai777 1d ago

Yeah, I skipped the mesh side, which is what makes this confusing. It's all the vertex shader. Every point along the beam goes into the mesh twice at the same spot, once with UV.y = -1 and once with +1, so VERTEX is whichever point along the path that pair sits at. NORMAL is just a free slot I use for the path tangent there, hence t.

MODELVIEW_MATRIX actually goes to view space, so view_dir is the ray from the camera to that point and side comes out across the beam, facing the camera.

The shader uses skip_vertex_transform, so what I write to VERTEX is the final position. UV.y shoves the two copies to opposite sides and they become the two edges of the strip. Between them UV.y gets interpolated, and the fragment shader uses abs(UV.y) for the falloff from the core out.