@@ -37,46 +37,55 @@ fn project_3d(p3: vec3<f32>, ro: vec3<f32>, u: vec3<f32>, v_cam: vec3<f32>, w: v
3737 return vec3 <f32 >(proj_x , proj_y , dist_w );
3838}
3939
40- fn draw_3d_segment (p : vec2 <f32 >, ro : vec3 <f32 >, u : vec3 <f32 >, v_cam : vec3 <f32 >, w : vec3 <f32 >, start_pos : vec3 <f32 >, end_pos : vec3 <f32 >, thickness_base : f32 , intensity : f32 , is_branch : bool ) -> f32 {
41- var pA = project_3d (start_pos , ro , u , v_cam , w );
42- var pB = project_3d (end_pos , ro , u , v_cam , w );
40+ fn draw_projected_segment (p : vec2 <f32 >, pA : vec3 <f32 >, pB : vec3 <f32 >, thickness_base : f32 , intensity : f32 , is_branch : bool ) -> f32 {
41+ if (pA . z <= 0 .1 || pB . z <= 0 .1 ) {
42+ return 0 .0 ;
43+ }
4344
44- if (pA . z <= 0 .0 && pB . z <= 0 .0 ) { return 0 .0 ; }
45+ let margin = select (0 .08 , 0 .04 , is_branch );
46+ let min_x = min (pA . x , pB . x ) - margin ;
47+ let max_x = max (pA . x , pB . x ) + margin ;
48+ let min_y = min (pA . y , pB . y ) - margin ;
49+ let max_y = max (pA . y , pB . y ) + margin ;
4550
46- if (pA . z <= 0 .0 && pB . z > 0 .0 ) {
47- let t = clamp (- pA . z / (pB . z - pA . z ), 0 .0 , 1 .0 );
48- let front_pos = start_pos + (end_pos - start_pos ) * t ;
49- pA = project_3d (front_pos , ro , u , v_cam , w );
50- } else if (pB . z <= 0 .0 && pA . z > 0 .0 ) {
51- let t = clamp (- pA . z / (pB . z - pA . z ), 0 .0 , 1 .0 );
52- let front_pos = start_pos + (end_pos - start_pos ) * t ;
53- pB = project_3d (front_pos , ro , u , v_cam , w );
51+ if (p . x < min_x || p . x > max_x || p . y < min_y || p . y > max_y ) {
52+ return 0 .0 ;
5453 }
5554
56- if (pA . z > 0 .0 || pB . z > 0 .0 ) {
57- let d = sd_segment (p , pA . xy , pB . xy );
58- let depth = max (0 .4 , max (pA . z , pB . z ));
59- let thickness = thickness_base / depth ;
60- let core = smoothstep (thickness * 0 .5 , 0 .0 , d );
61- let glow_radius = select (60 .0 , 120 .0 , is_branch ); // Branches have a tighter, less intense glow
62- let glow_mult = select (0 .7 , 0 .3 , is_branch );
63- let glow = exp (- d * glow_radius ) * glow_mult ;
64- return (core + glow ) * intensity ;
65- }
66- return 0 .0 ;
55+ let d = sd_segment (p , pA . xy , pB . xy );
56+ let depth = max (0 .4 , max (pA . z , pB . z ));
57+ let thickness = thickness_base / depth ;
58+ let core = smoothstep (thickness * 0 .5 , 0 .0 , d );
59+ let glow_radius = select (60 .0 , 120 .0 , is_branch ); // Branches have a tighter, less intense glow
60+ let glow_mult = select (0 .7 , 0 .3 , is_branch );
61+ let glow = exp (- d * glow_radius ) * glow_mult ;
62+ return (core + glow ) * intensity ;
6763}
6864
6965fn draw_3d_lightning_bolt (ro : vec3 <f32 >, u : vec3 <f32 >, v_cam : vec3 <f32 >, w : vec3 <f32 >, p : vec2 <f32 >, seed : f32 , intensity : f32 , origin : vec3 <f32 >, plane_z : f32 ) -> f32 {
7066 if (intensity < 0 .01 ) { return 0 .0 ; }
7167
72- var bolt = 0 .0 ;
73- var current_pos = origin ;
7468 let segments = 15 ;
7569 let frustum_height = plane_z - ro . z ;
7670 let bolt_height = frustum_height * 2 .4 ; // Ensure it reaches ground
7771 let seg_h = bolt_height / f32 (segments );
7872 let j_scale = plane_z * 0 .15 ; // Scale jitter with distance
7973
74+ // Bounding box check in screen space (Fast Pruning)
75+ let max_dev_3d = 6 .5 * j_scale ;
76+ let p_top = project_3d (origin , ro , u , v_cam , w );
77+ let p_bot = project_3d (origin - vec3 <f32 >(0 .0 , bolt_height , 0 .0 ), ro , u , v_cam , w );
78+
79+ let depth_min = max (0 .1 , plane_z - max_dev_3d - ro . z );
80+ let margin = max_dev_3d / depth_min + 0 .15 ;
81+
82+ let d_line = sd_segment (p , p_top . xy , p_bot . xy );
83+ if (d_line > margin ) { return 0 .0 ; }
84+
85+ var bolt = 0 .0 ;
86+ var current_pos = origin ;
87+ var p_curr_proj = p_top ;
88+
8089 // Simulate dart leaders and return stroke pulsing via high-frequency flicker
8190 let flicker = 0 .6 + 0 .4 * sin (audio . smooth_time * 60 .0 + seed );
8291 let main_intensity = intensity * flicker ;
@@ -87,43 +96,51 @@ fn draw_3d_lightning_bolt(ro: vec3<f32>, u: vec3<f32>, v_cam: vec3<f32>, w: vec3
8796 let x_jitter = (hash11 (seed + f_i * 7 .13 ) - 0 .5 ) * j_scale * 2 .5 ;
8897 let z_jitter = (hash11 (seed + f_i * 13 .7 ) - 0 .5 ) * j_scale * 2 .5 ;
8998 let next_pos = current_pos + vec3 <f32 >(x_jitter , - seg_h , z_jitter );
99+ let p_next_proj = project_3d (next_pos , ro , u , v_cam , w );
90100
91101 // 1. Main Return Stroke Channel
92102 let thickness = 0 .012 + main_intensity * 0 .015 ;
93- bolt += draw_3d_segment (p , ro , u , v_cam , w , current_pos , next_pos , thickness , main_intensity , false );
103+ bolt += draw_projected_segment (p , p_curr_proj , p_next_proj , thickness , main_intensity , false );
94104
95105 // 2. Stepped Leaders (Primary Branches)
96- // Spawn branches from most nodes to simulate the searching fractal nature
97106 if (hash11 (seed + f_i * 3 .1 ) > 0 .25 ) {
98107 let bx = (hash11 (seed + f_i * 8 .2 ) - 0 .5 ) * j_scale * 4 .0 ;
99108 let bz = (hash11 (seed + f_i * 9 .3 ) - 0 .5 ) * j_scale * 4 .0 ;
100109 let branch_pos = current_pos + vec3 <f32 >(bx , - seg_h * 1 .5 , bz );
110+ let p_branch_proj = project_3d (branch_pos , ro , u , v_cam , w );
101111
102- bolt += draw_3d_segment (p , ro , u , v_cam , w , current_pos , branch_pos , 0 .005 , branch_intensity , true );
112+ bolt += draw_projected_segment (p , p_curr_proj , p_branch_proj , 0 .005 , branch_intensity , true );
103113
104114 // 3. Sub-branches (Secondary splits from the stepped leaders)
105115 if (hash11 (seed + f_i * 2 .4 ) > 0 .35 ) {
106116 let bx2 = (hash11 (seed + f_i * 1 .2 ) - 0 .5 ) * j_scale * 3 .0 ;
107117 let bz2 = (hash11 (seed + f_i * 4 .3 ) - 0 .5 ) * j_scale * 3 .0 ;
108118 let branch_pos2 = branch_pos + vec3 <f32 >(bx2 , - seg_h * 1 .2 , bz2 );
109- bolt += draw_3d_segment (p , ro , u , v_cam , w , branch_pos , branch_pos2 , 0 .003 , branch_intensity * 0 .6 , true );
119+ let p_branch2_proj = project_3d (branch_pos2 , ro , u , v_cam , w );
120+
121+ bolt += draw_projected_segment (p , p_branch_proj , p_branch2_proj , 0 .003 , branch_intensity * 0 .6 , true );
110122 }
111123 }
112124
113125 // 4. Major splits (connecting leaders that travel alongside the main channel)
114126 if (i == 3 || i == 8 ) {
115127 var split_pos = current_pos ;
128+ var p_split_proj = p_curr_proj ;
116129 for (var j = 0 ; j < 4 ; j = j + 1 ) {
117130 let f_j = f32 (j );
118131 let sx = (hash11 (seed + f_i * 5 .0 + f_j ) - 0 .5 ) * j_scale * 3 .0 ;
119132 let sz = (hash11 (seed + f_i * 6 .0 + f_j ) - 0 .5 ) * j_scale * 3 .0 ;
120133 let next_split = split_pos + vec3 <f32 >(sx , - seg_h * 1 .1 , sz );
121- bolt += draw_3d_segment (p , ro , u , v_cam , w , split_pos , next_split , 0 .007 , branch_intensity * 1 .5 , true );
134+ let p_next_split_proj = project_3d (next_split , ro , u , v_cam , w );
135+
136+ bolt += draw_projected_segment (p , p_split_proj , p_next_split_proj , 0 .007 , branch_intensity * 1 .5 , true );
122137 split_pos = next_split ;
138+ p_split_proj = p_next_split_proj ;
123139 }
124140 }
125141
126142 current_pos = next_pos ;
143+ p_curr_proj = p_next_proj ;
127144 }
128145
129146 return bolt ;
0 commit comments