1+ //
2+ // Port of a "Fireball" shader.
3+ // Original source: https://www.shadertoy.com/view/XtGGRt by boywonder
4+ //
5+
6+ #import bevy_sprite :: mesh2d_view_bindings :: globals
7+ #import bevy_sprite :: mesh2d_vertex_output :: VertexOutput
8+ #import bevy_render :: view :: View
9+ #import shadplay :: shader_utils :: common :: {PI , TAU , sdBox , rotate2D_clockwise , fbm_fireball }
10+
11+ @group (0 ) @binding (0 ) var <uniform > view : View ;
12+ // TODO: Mouse input is not yet fully handled in this port.
13+ // To enable mouse rotation, you would need to:
14+ // 1. Ensure the necessary mouse data (position and button state) is sent to the shader.
15+ // 2. Uncomment the mouse uniform binding below.
16+ // 3. Uncomment the mouse rotation logic in the fragment shader's main loop.
17+ // #import shadplay::shader_utils::common::YourShader2D
18+ // @group(3) @binding(0) var<uniform> mouse: YourShader2D;
19+
20+ const EPSILON : f32 = 1e-6 ;
21+
22+ // A private global variable to accumulate color during raymarching.
23+ // This is a common pattern in Shadertoy ports where functions have side effects.
24+ var <private > col : vec3 <f32 >;
25+
26+ // --- Helper Functions ---
27+
28+ /// Macro replacement for `(sin(v)*.5+.5)` from the original shader.
29+ fn s1 (v : vec3 <f32 >) -> vec3 <f32 > {
30+ return sin (v ) * 0 .5 + 0 .5 ;
31+ }
32+
33+ /// The main Signed Distance Function for the fireball scene.
34+ fn sdFireBall (p : vec3 <f32 >) -> f32 {
35+ var p_mut = p ;
36+ p_mut . y -= - 1 .0 ;
37+ var q = p_mut ;
38+
39+ let T = globals . time ;
40+ let h = 5 .0 ;
41+ let range = smoothstep (- h , h , p_mut . y );
42+ let w = range * 4 .0 + 1 .0 ;
43+ let thick = range * 4 .0 + 1 .0 ;
44+
45+ let new_xz = rotate2D_clockwise (q . y * 1 .0 - T * 2 .0 ) * q . xz ;
46+ q . x = new_xz . x ;
47+ q . z = new_xz . y ;
48+
49+ var d = sdBox (q , vec3 <f32 >(w , h , thick ));
50+ let d1 = sdBox (q - vec3 <f32 >(0 .0 , 1 .0 , 0 .0 ), vec3 <f32 >(w , h , thick ) * vec3 <f32 >(0 .7 , 2 .0 , 0 .7 ));
51+ d = max (d , - d1 );
52+
53+ d += fbm_fireball (p_mut * 3 .0 , T ) * 0 .5 ;
54+
55+ d = abs (d ) * 0 .1 + 0 .01 ;
56+
57+ let c = s1 (vec3 <f32 >(3 .0 , 2 .0 , 1 .0 ) + (p_mut . y + p_mut . z ) * 0 .5 - T * 2 .0 );
58+ // Accumulate color as a side effect.
59+ col += pow (1 .3 / d , 2 .0 ) * c ;
60+
61+ return d ;
62+ }
63+
64+
65+ // --- Main Fragment Shader ---
66+
67+ @fragment
68+ fn fragment (in : VertexOutput ) -> @location (0 ) vec4 <f32 > {
69+ let R = view . viewport . zw ;
70+ // Normalize UVs to -1.0 to 1.0, accounting for aspect ratio.
71+ var uv = (in . uv * R * 2 .0 - R ) / R . y ;
72+ uv . y *= - 1 .0 ; // Invert Y-axis to correct orientation.
73+ // let m = (mouse.mouse_pos * 2.0 - R) / R * PI * 2.0;
74+
75+ // Reset the global color accumulator for this fragment.
76+ col = vec3 <f32 >(0 .0 );
77+ var O : vec4 <f32 >;
78+
79+ // Setup camera ray origin and direction.
80+ var ro = vec3 <f32 >(0 .0 , 0 .0 , - 10 .0 );
81+ let rd = normalize (vec3 <f32 >(uv , 1 .0 ));
82+
83+ // Raymarch the scene.
84+ let zMax = 20 .0 ;
85+ var z = 0 .1 ;
86+
87+ for (var i = 0 .0 ; i < 100 .0 ; i = i + 1 .0 ) {
88+ var p = ro + rd * z ;
89+
90+ // Mouse interaction is disabled for now.
91+ // if (mouse.button_pressed > 0.0) {
92+ // let m_x_rot = rotate2D_clockwise(m.x);
93+ // let new_xz = m_x_rot * p.xz;
94+ // p.x = new_xz.x;
95+ // p.z = new_xz.y;
96+
97+ // let m_y_rot = rotate2D_clockwise(m.y);
98+ // let new_yz = m_y_rot * p.yz;
99+ // p.y = new_yz.x;
100+ // p.z = new_yz.y;
101+ // }
102+
103+ let d = sdFireBall (p );
104+
105+ if (d < EPSILON || z > zMax ) {
106+ break ;
107+ }
108+ z += d ;
109+ }
110+
111+ // Final color processing (tonemapping).
112+ col = tanh (col / 9e4 );
113+ O = vec4 <f32 >(col , 1 .0 );
114+ return O ;
115+ }
0 commit comments