mightymandel v16

GPU-based Mandelbrot set explorer

fp32_colour_frag.glsl
Go to the documentation of this file.
1 // mightymandel -- GPU-based Mandelbrot Set explorer
2 // Copyright (C) 2012,2013,2014,2015 Claude Heiland-Allen
3 // License GPL3+ http://www.gnu.org/licenses/gpl.html
4 
21 uniform sampler2D ida0;
22 
26 uniform usampler2D slice_number;
27 
31 uniform usampler2D slice_coords;
32 
38 uniform bool update;
39 
46 uniform bool update2;
47 
55 uniform bool errs;
56 
62 uniform float thickness;
63 
70 uniform ivec2 slicing;
71 
75 smooth in vec2 t;
76 
80 out layout(location = 0, index = 0) vec4 c;
81 
88 ivec2 local_coords(ivec2 it) {
89  int mask = (1 << slicing.y) - 1;
90  int i = it.x & mask;
91  int j = it.y & mask;
92  return ivec2(i, j);
93 }
94 
101 ivec2 global_coords(ivec2 it) {
102  int i = (it.x >> slicing.y) << slicing.y;
103  int j = (it.y >> slicing.y) << slicing.y;
104  return ivec2(i, j);
105 }
106 
113 ivec2 block_coords(int block) {
114  int i = block >> slicing.y;
115  int j = block - (i << slicing.y);
116  return ivec2(texelFetch(slice_coords, ivec2(i, j).yx, SLICE_MAX - slicing.y).xy);
117 }
118 
126 int block_number_raw(ivec2 it, int level) {
127  return int(texelFetch(slice_number, it.yx, level).x);
128 }
129 
136 int block_number(ivec2 it) {
137  // check if our block is already computed
138  int block = block_number_raw(it, SLICE_MAX - slicing.y);
139  if (block <= slicing.x) { return block; }
140  // slicing.y > 0 because if it were 0, we would have returned already
141  int i = it.x;
142  int j = it.y;
143  // search through mipmap levels
144  for (int level = SLICE_MAX - slicing.y; level < SLICE_MAX; ++level) {
145  // look up in 2x2 neighbourhood
146  int i0 = (i >> 1) << 1;
147  int i1 = i0 + 1;
148  int j0 = (j >> 1) << 1;
149  int j1 = j0 + 1;
150  // find highest block number that isn't too big
151  block = -1;
152  int block00 = block_number_raw(ivec2(i0, j0), level);
153  if (block < block00 && block00 <= slicing.x) { block = block00; }
154  int block01 = block_number_raw(ivec2(i0, j1), level);
155  if (block < block01 && block01 <= slicing.x) { block = block01; }
156  int block10 = block_number_raw(ivec2(i1, j0), level);
157  if (block < block10 && block10 <= slicing.x) { block = block10; }
158  int block11 = block_number_raw(ivec2(i1, j1), level);
159  if (block < block11 && block11 <= slicing.x) { block = block11; }
160  // if we found one, return it
161  if (0 <= block) { return block; }
162  // didn't find one, going up a level with a smaller size
163  i = i >> 1;
164  j = j >> 1;
165  }
166  // didn't find one, return the block that always exists
167  return 0;
168 }
169 
175 int delta() {
176  if (update2) { return 1; }
177  int m = 0;
178  for (int k = slicing.y; k > 0; --k) {
179  int n = 1 << (k << 1);
180  if (slicing.x >= n) {
181  m = slicing.y - k;
182  break;
183  }
184  }
185  return 1 << m;
186 }
187 
194 ivec2 quantize_coords(ivec2 it) {
195  if (update2) { return it; }
196  ivec2 global_it = global_coords(it);
197  ivec2 local_it = local_coords(it);
198  int block = block_number(local_it);
199  ivec2 block_it = block_coords(block);
200  return global_it + block_it;
201 }
202 
221 vec4 colour(ivec2 it0, ivec2 dims) {
222  ivec2 it = quantize_coords(it0);
223  vec4 ida = texelFetch(ida0, it, 0);
224  bool escaped = ida.x > 0.0;
225  bool glitched = ida.y != 0.0;
226  vec3 rgb = vec3(1.0);
227  float de = 1.0;
228  float alpha = 1.0;
229 
230  if (! escaped) {
231  rgb = vec3(1.0, 0.7, 0.0);
232  alpha = 1.0;
233  } else {
234 
235 #ifdef DE
236 
237  de = tanh(clamp(ida.z * thickness / 2.0, 0.0, 4.0));
238 
239 #else
240 
241  int neighbour_count = 0;
242  float neighbour_z = thickness * 3.0;
243  float neighbour_sum2 = neighbour_z * neighbour_z;
244  int d = delta();
245  for (int i = -d; i <= d; i += d) {
246  for (int j = -d; j <= d; j += d) {
247  // want 8-point neighbourhood
248  if (i == 0 && j == 0) continue;
249  // quantize neighbour's coordinates
250  ivec2 it20 = it + ivec2(i, j);
251  ivec2 it2 = quantize_coords(it20);
252  // check that it's really a different pixel
253  if (it2.x == it.x && it2.y == it.y) continue;
254  // check image bounds
255  if (it2.x < 0 || it2.y < 0) continue;
256  if (it2.x >= dims.x || it2.y >= dims.y) continue;
257  // compute fake de
258  float neighbour = texelFetch(ida0, it2, 0).x;
259  // check exterior
260  if (neighbour > 0.0) {
261  neighbour_count += 1;
262  float distance = length(vec2(it2 - it));
263  float diff = (ida.x - neighbour) / distance;
264  neighbour_sum2 += diff * diff;
265  }
266  }
267  }
268  if (neighbour_count > 0) {
269  de = neighbour_z / sqrt(neighbour_sum2);
270  } else {
271  alpha = 0.0;
272  }
273 
274 #endif
275 
276  }
277 
278  if (errs && glitched) {
279  rgb = mix(rgb, vec3(1.0, 0.0, 0.0), tanh(clamp(abs(ida.y) / 16.0, 0.0, 3.0) + 1.0));
280  }
281 
282  if (escaped) {
283  rgb = mix(vec3(0.0), rgb, errs && glitched ? de * 0.5 + 0.5 : de);
284  }
285 
286  return vec4(rgb * alpha, alpha);
287 }
288 
297 void main() {
298  ivec2 dims = textureSize(ida0, 0);
299  ivec2 it0 = ivec2(floor(t * vec2(dims)));
300  ivec2 it = quantize_coords(it0);
301  vec4 ida = texelFetch(ida0, it, 0);
302  bool glitched = ida.y != 0.0;
303  vec4 rgba = vec4(0.0);
304  if (false && ! update && ida.x <= 0.0) {
305  discard;
306  } else {
307  if (! glitched || errs) {
308  // show good pixels normally (! glitched)
309  // show errors normally (errs)
310  rgba = colour(it0, dims);
311  } else {
312  // mask small glitches by blending neighbouring texels
313  int d = delta();
314  for (int i = -d; i <= d; i += d) {
315  for (int j = -d; j <= d; j += d) {
316  // want 8-point neighbourhood
317  if (i == 0 && j == 0) continue;
318  // quantize neighbour's coordinates
319  ivec2 it20 = it + ivec2(i, j);
320  ivec2 it2 = quantize_coords(it20);
321  // check that it's really a different pixel
322  if (it2.x == it.x && it2.y == it.y) continue;
323  // check image bounds
324  if (it2.x < 0 || it2.y < 0) continue;
325  if (it2.x >= dims.x || it2.y >= dims.y) continue;
326  // if it's unglitched, accumulate colour weighted by distance
327  float glitch2 = texelFetch(ida0, it2, 0).y;
328  if (glitch2 == 0.0) {
329  float distance = length(vec2(it2 - it));
330  rgba += distance * colour(it2, dims);
331  }
332  }
333  }
334  if (rgba.a > 0.0) {
335  rgba /= rgba.a;
336  } else {
337  // no neighbouring unglitched pixels, last resort...
338  rgba = vec4(vec3(0.0), 1.0);
339  }
340  }
341  if (rgba.a > 0.0) {
342  c = vec4(rgba.rgb, 1.0);
343  } else {
344  discard;
345  }
346  }
347 }