@@ -27,6 +27,7 @@ struct VertexOutput3D {
2727 @location (0 ) uv : vec2 <f32 >,
2828 @location (1 ) energy : f32 ,
2929 @location (2 ) depth : f32 ,
30+ @location (3 ) @interpolate (linear ) ndc : vec2 <f32 >,
3031}
3132
3233@vertex
@@ -41,6 +42,7 @@ fn vs_main_3d(in: VertexInput, @builtin(instance_index) inst_idx: u32) -> Vertex
4142 out . uv = vec2 <f32 >(0 .0 );
4243 out . energy = 0 .0 ;
4344 out . depth = 0 .0 ;
45+ out . ndc = vec2 <f32 >(0 .0 );
4446 return out ;
4547 }
4648
@@ -58,20 +60,44 @@ fn vs_main_3d(in: VertexInput, @builtin(instance_index) inst_idx: u32) -> Vertex
5860 out . uv = vec2 <f32 >(0 .0 );
5961 out . energy = 0 .0 ;
6062 out . depth = 0 .0 ;
63+ out . ndc = vec2 <f32 >(0 .0 );
6164 return out ;
6265 }
6366
64- out . clip_position = camera . proj_matrix * view_pos ;
67+ var clip_pos = camera . proj_matrix * view_pos ;
68+
69+ // Apply barrel distortion in clip space (NDC)
70+ let ndc = clamp (clip_pos . xy / clip_pos . w , vec2 <f32 >(- 2 .0 ), vec2 <f32 >(2 .0 ));
71+ let r2 = min (2 .0 , dot (ndc , ndc ));
72+ let distorted_ndc = ndc * (1 .0 + r2 * 0 .055 );
73+ clip_pos = vec4 <f32 >(distorted_ndc * clip_pos . w , clip_pos . z , clip_pos . w );
74+
75+ out . clip_position = clip_pos ;
6576 out . uv = in . tex_coords ;
6677 out . energy = p . vel . w ;
6778 out . depth = depth ;
79+ out . ndc = distorted_ndc ;
6880
6981 return out ;
7082}
7183
84+ fn hash21 (p : vec2 <f32 >) -> f32 {
85+ var p3 = fract (vec3 <f32 >(p . xyx ) * 0 .1031 );
86+ p3 = p3 + dot (p3 , p3 . yzx + 33 .33 );
87+ return fract ((p3 . x + p3 . y ) * p3 . z );
88+ }
89+
7290@fragment
7391fn fs_main (in : VertexOutput3D ) -> @location (0 ) vec4 <f32 > {
74- // Circle clip on each face using UV coordinate distance from center
92+ // 1. Bezel boundary check
93+ if (abs (in . ndc . x ) > 1 .0 || abs (in . ndc . y ) > 1 .0 ) {
94+ discard ;
95+ }
96+
97+ let border_dist = min (1 .0 - abs (in . ndc . x ), 1 .0 - abs (in . ndc . y ));
98+ let bezel_mask = smoothstep (0 .0 , 0 .03 , border_dist );
99+
100+ // 2. Circle clip on each face using UV coordinate distance from center
75101 let dist = length (in . uv - vec2 <f32 >(0 .5 , 0 .5 ));
76102 if (dist > 0 .5 ) {
77103 discard ;
@@ -96,7 +122,23 @@ fn fs_main(in: VertexOutput3D) -> @location(0) vec4<f32> {
96122
97123 // Add distance fog to match the deep ocean depth feel
98124 let fog_factor = smoothstep (5 .0 , 35 .0 , in . depth );
99- let final_color = mix (color * intensity , vec3 <f32 >(0 .001 , 0 .003 , 0 .008 ), fog_factor );
125+ var final_color = mix (color * intensity , vec3 <f32 >(0 .001 , 0 .003 , 0 .008 ), fog_factor );
126+
127+ // Apply bezel vignette mask
128+ final_color = final_color * bezel_mask ;
129+
130+ // CRT Filter: Scanlines
131+ let scanline = 0 .86 + 0 .14 * cos (in . clip_position . y * 3 .14159 );
132+ final_color = final_color * scanline ;
133+
134+ // CRT Filter: Flicker
135+ let flicker = 0 .98 + 0 .02 * sin (audio . time * 115 .0 );
136+ final_color = final_color * flicker ;
137+
138+ // CRT Filter: Analog static noise
139+ let noise_val = hash21 (in . clip_position . xy + fract (audio . smooth_time ) * 149 .0 );
140+ let static_noise = noise_val * 0 .022 * bezel_mask ;
141+ final_color = final_color + vec3 <f32 >(static_noise );
100142
101143 return vec4 <f32 >(final_color , 1 .0 );
102144}
0 commit comments