]> git.sesse.net Git - nageru/blob - sor.frag
Compute diffusivity instead of smoothness, which saves a flow-size texture; shaves...
[nageru] / sor.frag
1 #version 450 core
2
3 in vec2 tc, tc_left, tc_down;
4 in float element_sum_idx;
5 out vec2 diff_flow;
6
7 uniform sampler2D diff_flow_tex, diffusivity_tex;
8 uniform usampler2D equation_tex;
9 uniform int phase;
10
11 uniform bool zero_diff_flow;
12
13 // See pack_floats_shared() in equations.frag.
14 vec2 unpack_floats_shared(uint c)
15 {
16         // Recover the exponent, and multiply it in. Add one because
17         // we have denormalized mantissas, then another one because we
18         // already reduced the exponent by one. Then subtract 20, because
19         // we are going to shift up the number by 20 below to recover the sign bits.
20         float normalizer = uintBitsToFloat(((c >> 1) & 0x7f800000u) - (18 << 23));
21         normalizer *= (1.0 / 2047.0);
22
23         // Shift the values up so that we recover the sign bit, then normalize.
24         float a = int(uint(c & 0x000fffu) << 20) * normalizer;
25         float b = int(uint(c & 0xfff000u) << 8) * normalizer;
26
27         return vec2(a, b);
28 }
29
30 float zero_if_outside_border(vec4 val)
31 {
32         if (val.w < 1.0f) {
33                 // We hit the border (or more like half-way to it), so zero smoothness.
34                 return 0.0f;
35         } else {
36                 return val.x;
37         }
38 }
39
40 void main()
41 {
42         // Red-black SOR: Every other pass, we update every other element in a
43         // checkerboard pattern. This is rather suboptimal for the GPU, as it
44         // just immediately throws away half of the warp, but it helps convergence
45         // a _lot_ (rough testing indicates that five iterations of SOR is as good
46         // as ~50 iterations of Jacobi). We could probably do better by reorganizing
47         // the data into two-values-per-pixel, so-called “twinning buffering”,
48         // but it makes for rather annoying code in the rest of the pipeline.
49         int color = int(round(element_sum_idx)) & 1;
50         if (color != phase) discard;
51
52         uvec4 equation = texture(equation_tex, tc);
53         float inv_A11 = uintBitsToFloat(equation.x);
54         float A12 = uintBitsToFloat(equation.y);
55         float inv_A22 = uintBitsToFloat(equation.z);
56         vec2 b = unpack_floats_shared(equation.w);
57
58         if (zero_diff_flow) {
59                 diff_flow = vec2(0.0f);
60         } else {
61                 // Subtract the missing terms from the right-hand side
62                 // (it couldn't be done earlier, because we didn't know
63                 // the values of the neighboring pixels; they change for
64                 // each SOR iteration).
65                 float smooth_l = zero_if_outside_border(texture(diffusivity_tex, tc_left));
66                 float smooth_r = zero_if_outside_border(textureOffset(diffusivity_tex, tc_left, ivec2(1, 0)));
67                 float smooth_d = zero_if_outside_border(texture(diffusivity_tex, tc_down));
68                 float smooth_u = zero_if_outside_border(textureOffset(diffusivity_tex, tc_down, ivec2(0, 1)));
69                 b += smooth_l * textureOffset(diff_flow_tex, tc, ivec2(-1,  0)).xy;
70                 b += smooth_r * textureOffset(diff_flow_tex, tc, ivec2( 1,  0)).xy;
71                 b += smooth_d * textureOffset(diff_flow_tex, tc, ivec2( 0, -1)).xy;
72                 b += smooth_u * textureOffset(diff_flow_tex, tc, ivec2( 0,  1)).xy;
73                 diff_flow = texture(diff_flow_tex, tc).xy;
74         }
75
76         const float omega = 1.8;  // Marginally better than 1.6, it seems.
77
78         // From https://en.wikipedia.org/wiki/Successive_over-relaxation.
79         float sigma_u = A12 * diff_flow.y;
80         diff_flow.x += omega * ((b.x - sigma_u) * inv_A11 - diff_flow.x);
81         float sigma_v = A12 * diff_flow.x;
82         diff_flow.y += omega * ((b.y - sigma_v) * inv_A22 - diff_flow.y);
83 }