馃悕馃悕馃悕
1
2struct Uniforms /* buffer 0 0 */ {
3 width: u32,
4 height: u32,
5 center: vec2f,
6 p: vec2f, // -3 to 3 = 0,0
7 q: f32, // 0 to 1 = 0
8 zoom: f32,
9 iterations: u32, // hard 0 to 500 = 100
10 escape_distance: f32 // 0 to 10 = 2
11}
12
13@group(0) @binding(0) var<uniform> uniforms: Uniforms;
14@group(0) @binding(1) var output_texture: texture_storage_2d<rgba8unorm, write>;
15
16fn add12(ab: vec2<f32>) -> vec2<f32> {
17 let s = ab.x + ab.y;
18 let v = s - ab.x;
19 let r = (ab.x - (s - v)) + (ab.y - v);
20 return vec2<f32>(s, r);
21}
22
23fn split(a: f32) -> vec2<f32> {
24 let c = 4097 * a;
25 let a_b = c - a;
26 let a_h = c - a_b;
27 let a_l = a - a_h;
28 return vec2<f32>(a_h, a_l);
29}
30
31fn mul12(ab: vec2<f32>) -> vec2<f32> {
32 let x = ab.x * ab.y;
33 let a_split = split(ab.x);
34 let b_split = split(ab.y);
35 let err1 = x - (a_split.x * b_split.x); // high * high
36 let err2 = err1 - (a_split.y * b_split.x); // low * high
37 let err3 = err2 - (a_split.x * b_split.y); // high * low
38 let y = (a_split.y * b_split.y) - err3; // low * low
39 return vec2<f32>(x, y);
40}
41
42fn complex_mag_sq(z: vec2<f32>) -> f32 {
43 return z.x * z.x + z.y * z.y;
44}
45
46fn complex_mag(z: vec2<f32>) -> f32 {
47 return sqrt(complex_mag_sq(z));
48}
49
50fn complex_angle(z: vec2<f32>) -> f32 {
51 return atan2(z.y, z.x);
52}
53
54fn complex_mul(za: vec2<f32>, zb: vec2<f32>) -> vec2<f32> {
55 return vec2<f32>(
56 za.x * zb.x - za.y * zb.y,
57 za.x * zb.y + za.y * zb.x
58 );
59}
60
61fn pixel_to_complex(px: u32, py: u32) -> vec2<f32> {
62 let aspect = f32(uniforms.width) / f32(uniforms.height);
63 let scale = 1.0 / uniforms.zoom;
64 let half_width = f32(uniforms.width) * 0.5;
65 let half_height = f32(uniforms.height) * 0.5;
66 let x = (f32(px) - half_width) * scale * aspect / f32(uniforms.width) + uniforms.center.x;
67 let y = (f32(py) - half_height) * scale / f32(uniforms.height) + uniforms.center.y;
68 return vec2<f32>(x, y);
69}
70
71fn pixel_to_complex_alt(px: u32, py: u32) -> vec2<f32> {
72 let aspect = f32(uniforms.width) / f32(uniforms.height);
73 let scale = 4.0 / uniforms.zoom;
74 let half_width = f32(uniforms.width) * 0.5;
75 let half_height = f32(uniforms.height) * 0.5;
76
77 // map y-axis to magnitude, x-axis to angle
78 let angle = (f32(px) - half_width) * scale * aspect / f32(uniforms.width) + uniforms.center.x;
79 let mag = (f32(py) - half_height) * scale / (2.0 * f32(uniforms.height)) + uniforms.center.y;
80
81 return vec2<f32>(mag * cos(angle), mag * sin(angle));
82}
83
84fn pixel_to_complex_inverted(px: u32, py: u32) -> vec2<f32> {
85 let aspect = f32(uniforms.width) / f32(uniforms.height);
86 let scale = 4.0 / uniforms.zoom;
87 let half_width = f32(uniforms.width) * 0.5;
88 let half_height = f32(uniforms.height) * 0.5;
89
90 let x = (f32(px) - half_width) * scale * aspect / f32(uniforms.width) + uniforms.center.x;
91 let y = (f32(py) - half_height) * scale / f32(uniforms.height) + uniforms.center.y;
92
93 let z = vec2<f32>(x, y);
94 let mag_sq = x * x + y * y;
95
96 // handle singularity at origin
97 if (mag_sq < 1e-10) {
98 return vec2<f32>(1e10, 0.0); // or whatever you want infinity to map to
99 }
100
101 return vec2<f32>(x / mag_sq, -y / mag_sq);
102}
103
104fn pixel_to_complex_inverted_d(px: u32, py: u32) -> vec2<f32> {
105 let aspect = f32(uniforms.width) / f32(uniforms.height);
106 let scale = 16.0 / uniforms.zoom;
107 let half_width = f32(uniforms.width) * 0.5;
108 let half_height = f32(uniforms.height) * 0.5;
109
110 // get position relative to center
111 let x = (f32(px) - half_width) * scale * aspect / f32(uniforms.width);
112 let y = (f32(py) - half_height) * scale / f32(uniforms.height);
113
114 let mag_sq = x * x + y * y;
115 if (mag_sq < 1e-10) {
116 return vec2<f32>(uniforms.center.x + 1e10, uniforms.center.y);
117 }
118
119 // invert then add center back
120 return vec2<f32>(
121 uniforms.center.x + x / mag_sq,
122 uniforms.center.y - y / mag_sq
123 );
124}
125
126fn c_avg(prev: f32, val: f32, n: f32) -> f32 {
127 return prev + (val - prev) / n;
128}
129
130fn hash(seed: u32) -> u32 {
131 var x = seed;
132 x = ((x >> 16u) ^ x) * 0x45d9f3bu;
133 x = ((x >> 16u) ^ x) * 0x45d9f3bu;
134 x = (x >> 16u) ^ x;
135 return x;
136}
137
138fn pcg_hash(x: u32) -> u32 {
139 let state = x * 747796405u + 2891336453u;
140 let word = ((state >> ((state >> 28u) + 4u)) ^ state) * 277803737u;
141 return (word >> 22u) ^ word;
142}
143
144fn random_float(seed: u32) -> f32 {
145 return f32(hash(seed)) / 4294967296.0; // 2^32
146}
147
148@compute @workgroup_size(16, 16)
149fn main(@builtin(global_invocation_id) id: vec3<u32>) {
150 let px = id.x;
151 let py = id.y;
152
153 if (px >= uniforms.width || py >= uniforms.height) {
154 return;
155 }
156
157 var nq = 1.0 - uniforms.q;
158 var c = ${pixel_mapping}(px, py) * nq + uniforms.p * uniforms.q;
159 var z = uniforms.p * nq + ${pixel_mapping}(px, py) * uniforms.q;
160 let orig_z = z;
161 let n_perturbations = 1u;
162 let escape_threshold = uniforms.escape_distance * uniforms.escape_distance;
163
164 var escaped = false;
165 var iter = 0u;
166 var cavg = f32(0.0);
167 let epsilon = 1.19e-6;
168
169 let transient_skip = u32(f32(uniforms.iterations) * f32(0.95));
170
171 for (iter = 0u; iter < uniforms.iterations; iter = iter + 1u) {
172 let mag_sq = complex_mag_sq(z);
173 if (mag_sq > escape_threshold) {
174 escaped = true;
175 break;
176 }
177
178 z = complex_mul(z, z) + c;
179 //z = avg_abs_df_z_p;
180 }
181
182 var color: vec4<f32>;
183 if (escaped) {
184 let escape_speed = 1.0 - pow(f32(iter) / f32(uniforms.iterations), 2.0);
185 color = vec4<f32>(escape_speed, escape_speed, escape_speed, 1.0);
186 } else {
187 let final_mag = complex_mag(z);
188 let final_angle = complex_angle(z);
189 //let scaled_mag = log(final_mag);
190 //let final_x = scaled_mag * (z.x / final_mag);
191 //let final_y = scaled_mag * (z.y / final_mag);
192 //color = vec4<f32>(0.5 + 0.5 * final_x, 0.0, 0.5 + 0.5 * final_y, 1.0);
193 let scaled_mag = pow(final_mag / escape_threshold, 0.5);
194 let scaled_angle = (final_angle + 3.1415926535) / 6.283185307179586;
195 let r = max(0, 2.0 * scaled_angle - 1.0);
196 let b = max(0, 2.0 * (1.0 - scaled_angle) - 1.0);
197 color = vec4<f32>(r, scaled_mag, b, 1.0);
198 }
199
200 //color = vec4<f32>(1.0 - cavg / 10.0, 1.0 - (-cavg / 5.0), 1.0 - (-cavg / 5.0), 1.0);
201
202 textureStore(output_texture, vec2<i32>(i32(px), i32(py)), color);
203}
204
205