Welcome to mirror list, hosted at ThFree Co, Russian Federation.

eevee_depth_of_field_filter_comp.glsl « shaders « eevee_next « engines « draw « blender « source - git.blender.org/blender.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
blob: c5c0e210109677aee7a08e69e6026dc57c570acc (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163

/**
 * Gather Filter pass: Filter the gather pass result to reduce noise.
 *
 * This is a simple 3x3 median filter to avoid dilating highlights with a 3x3 max filter even if
 * cheaper.
 */

struct FilterSample {
  vec4 color;
  float weight;
};

/* -------------------------------------------------------------------- */
/** \name Pixel cache.
 * \{ */

const uint cache_size = gl_WorkGroupSize.x + 2;
shared vec4 color_cache[cache_size][cache_size];
shared float weight_cache[cache_size][cache_size];

void cache_init()
{
  /**
   * Load enough values into LDS to perform the filter.
   *
   * ┌──────────────────────────────┐
   * │                              │  < Border texels that needs to be loaded.
   * │    x  x  x  x  x  x  x  x    │  ─┐
   * │    x  x  x  x  x  x  x  x    │   │
   * │    x  x  x  x  x  x  x  x    │   │
   * │    x  x  x  x  x  x  x  x    │   │ Thread Group Size 8x8.
   * │ L  L  L  L  L  x  x  x  x    │   │
   * │ L  L  L  L  L  x  x  x  x    │   │
   * │ L  L  L  L  L  x  x  x  x    │   │
   * │ L  L  L  L  L  x  x  x  x    │  ─┘
   * │ L  L  L  L  L                │  < Border texels that needs to be loaded.
   * └──────────────────────────────┘
   *   └───────────┘
   *    Load using 5x5 threads.
   */

  ivec2 texel = ivec2(gl_GlobalInvocationID.xy) - 1;
  if (all(lessThan(gl_LocalInvocationID.xy, uvec2(cache_size / 2u)))) {
    for (int y = 0; y < 2; y++) {
      for (int x = 0; x < 2; x++) {
        ivec2 offset = ivec2(x, y) * ivec2(cache_size / 2u);
        ivec2 cache_texel = ivec2(gl_LocalInvocationID.xy) + offset;
        ivec2 load_texel = clamp(texel + offset, ivec2(0), textureSize(color_tx, 0) - 1);

        color_cache[cache_texel.y][cache_texel.x] = texelFetch(color_tx, load_texel, 0);
        weight_cache[cache_texel.y][cache_texel.x] = texelFetch(weight_tx, load_texel, 0).r;
      }
    }
  }
  barrier();
}

FilterSample cache_sample(int x, int y)
{
  return FilterSample(color_cache[y][x], weight_cache[y][x]);
}

/** \} */

/* -------------------------------------------------------------------- */
/** \name Median filter
 * From:
 * Implementing Median Filters in XC4000E FPGAs
 * JOHN L. SMITH, Univision Technologies Inc., Billerica, MA
 * http://users.utcluj.ro/~baruch/resources/Image/xl23_16.pdf
 * Figure 1
 * \{ */

FilterSample filter_min(FilterSample a, FilterSample b)
{
  return FilterSample(min(a.color, b.color), min(a.weight, b.weight));
}

FilterSample filter_max(FilterSample a, FilterSample b)
{
  return FilterSample(max(a.color, b.color), max(a.weight, b.weight));
}

FilterSample filter_min(FilterSample a, FilterSample b, FilterSample c)
{
  return FilterSample(min(a.color, min(c.color, b.color)), min(a.weight, min(c.weight, b.weight)));
}

FilterSample filter_max(FilterSample a, FilterSample b, FilterSample c)
{
  return FilterSample(max(a.color, max(c.color, b.color)), max(a.weight, max(c.weight, b.weight)));
}

FilterSample filter_median(FilterSample s1, FilterSample s2, FilterSample s3)
{
  /* From diagram, with nodes numbered from top to bottom. */
  FilterSample l1 = filter_min(s2, s3);
  FilterSample h1 = filter_max(s2, s3);
  FilterSample h2 = filter_max(s1, l1);
  FilterSample l3 = filter_min(h2, h1);
  return l3;
}

struct FilterLmhResult {
  FilterSample low;
  FilterSample median;
  FilterSample high;
};

FilterLmhResult filter_lmh(FilterSample s1, FilterSample s2, FilterSample s3)
{
  /* From diagram, with nodes numbered from top to bottom. */
  FilterSample h1 = filter_max(s2, s3);
  FilterSample l1 = filter_min(s2, s3);

  FilterSample h2 = filter_max(s1, l1);
  FilterSample l2 = filter_min(s1, l1);

  FilterSample h3 = filter_max(h2, h1);
  FilterSample l3 = filter_min(h2, h1);

  FilterLmhResult result;
  result.low = l2;
  result.median = l3;
  result.high = h3;

  return result;
}

/** \} */

void main()
{
  /**
   * NOTE: We can **NOT** optimize by discarding some tiles as the result is sampled using bilinear
   * filtering in the resolve pass. Not outputing to a tile means that border texels have undefined
   * value and tile border will be noticeable in the final image.
   */

  cache_init();

  ivec2 texel = ivec2(gl_LocalInvocationID.xy);

  FilterLmhResult rows[3];
  for (int y = 0; y < 3; y++) {
    rows[y] = filter_lmh(cache_sample(texel.x + 0, texel.y + y),
                         cache_sample(texel.x + 1, texel.y + y),
                         cache_sample(texel.x + 2, texel.y + y));
  }
  /* Left nodes. */
  FilterSample high = filter_max(rows[0].low, rows[1].low, rows[2].low);
  /* Right nodes. */
  FilterSample low = filter_min(rows[0].high, rows[1].high, rows[2].high);
  /* Center nodes. */
  FilterSample median = filter_median(rows[0].median, rows[1].median, rows[2].median);
  /* Last bottom nodes. */
  median = filter_median(low, median, high);

  ivec2 out_texel = ivec2(gl_GlobalInvocationID.xy);
  imageStore(out_color_img, out_texel, median.color);
  imageStore(out_weight_img, out_texel, vec4(median.weight));
}