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

compositor_morphological_distance_feather.glsl « compositor « shaders « gpu « blender « source - git.blender.org/blender.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
blob: acdd8a40342bbdec4b0d2d810a9b6cf5561fadc7 (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
#pragma BLENDER_REQUIRE(gpu_shader_compositor_texture_utilities.glsl)

/* The Morphological Distance Feather operation is a linear combination between the result of two
 * operations. The first operation is a Gaussian blur with a radius equivalent to the dilate/erode
 * distance, which is straightforward and implemented as a separable filter similar to the blur
 * operation.
 *
 * The second operation is an approximation of a morphological inverse distance operation evaluated
 * at a distance falloff function. The result of a morphological inverse distance operation is a
 * narrow band distance field that starts at its maximum value at boundaries where a difference in
 * values took place and linearly deceases until it reaches zero in the span of a number of pixels
 * equivalent to the erode/dilate distance. Additionally, instead of linearly decreasing, the user
 * may choose a different falloff which is evaluated at the computed distance. For dilation, the
 * distance field decreases outwards, and for erosion, the distance field decreased inwards.
 *
 * The reason why the result of a Gaussian blur is mixed in with the distance field is because the
 * distance field is merely approximated and not accurately computed, the defects of which is more
 * apparent away from boundaries and especially at corners where the distance field should take a
 * circular shape. That's why the Gaussian blur is mostly mixed only further from boundaries.
 *
 * The morphological inverse distance operation is approximated using a separable implementation
 * and intertwined with the Gaussian blur implementation as follows. A search window of a radius
 * equivalent to the dilate/erode distance is applied on the image to find either the minimum or
 * maximum pixel value multiplied by its corresponding falloff value in the window. For dilation,
 * we try to find the maximum, and for erosion, we try to find the minimum. Additionally, we also
 * save the falloff value where the minimum or maximum was found. The found value will be that of
 * the narrow band distance field and the saved falloff value will be used as the mixing factor
 * with the Gaussian blur.
 *
 * To make sense of the aforementioned algorithm, assume we are dilating a binary image by 5 pixels
 * whose half has a value of 1 and the other half has a value of zero. Consider the following:
 *
 * - A pixel of value 1 already has the maximum possible value, so its value will remain unchanged
 *   regardless of its position.
 * - A pixel of value 0 that is right at the boundary of the 1's region will have a maximum value
 *   of around 0.8 depending on the falloff. That's because the search window intersects the 1's
 *   region, which when multiplied by the falloff gives the first value of the falloff, which is
 *   larger than the initially zero value computed at the center of the search window.
 * - A pixel of value 0 that is 3 pixels away from the boundary will have a maximum value of around
 *   0.4 depending on the falloff. That's because the search window intersects the 1's region,
 *   which when multiplied by the falloff gives the third value of the falloff, which is larger
 *   than the initially zero value computed at the center of the search window.
 * - Finally, a pixel of value 0 that is 6 pixels away from the boundary will have a maximum value
 *   of 0, because the search window doesn't intersects the 1's region and only spans zero values.
 *
 * The previous example demonstrates how the distance field naturally arises, and the same goes for
 * the erode case, except the minimum value is computed instead.
 */
void main()
{
  ivec2 texel = ivec2(gl_GlobalInvocationID.xy);

  /* A value for accumulating the blur result. */
  float accumulated_value = 0.0;

  /* Compute the contribution of the center pixel to the blur result. */
  float center_value = texture_load(input_tx, texel).x;
  accumulated_value += center_value * texture_load(weights_tx, 0).x;

  /* Start with the center value as the maximum/minimum distance and reassign to the true maximum
   * or minimum in the search loop below. Additionally, the center falloff is always 1.0, so start
   * with that. */
  float limit_distance = center_value;
  float limit_distance_falloff = 1.0;

  /* Compute the contributions of the pixels to the right and left, noting that the weights and
   * falloffs textures only store the weights and falloffs for the positive half, but since the
   * they are both symmetric, the same weights and falloffs are used for the negative half and we
   * compute both of their contributions. */
  for (int i = 1; i < texture_size(weights_tx); i++) {
    float weight = texture_load(weights_tx, i).x;
    float falloff = texture_load(falloffs_tx, i).x;

    /* Loop for two iterations, where s takes the value of -1 and 1, which is used as the sign
     * needed to evaluated the positive and negative sides as explain above. */
    for (int s = -1; s < 2; s += 2) {
      /* Compute the contribution of the pixel to the blur result. */
      float value = texture_load(input_tx, texel + ivec2(s * i, 0)).x;
      accumulated_value += value * weight;

      /* The distance is computed such that its highest value is the pixel value itself, so
       * multiply the distance falloff by the pixel value. */
      float falloff_distance = value * falloff;

      /* Find either the maximum or the minimum for the dilate and erode cases respectively. */
      if (COMPARE(falloff_distance, limit_distance)) {
        limit_distance = falloff_distance;
        limit_distance_falloff = falloff;
      }
    }
  }

  /* Mix between the limit distance and the blurred accumulated value such that the limit distance
   * is used for pixels closer to the boundary and the blurred value is used for pixels away from
   * the boundary. */
  float value = mix(accumulated_value, limit_distance, limit_distance_falloff);

  /* Write the value using the transposed texel. See the execute_distance_feather_horizontal_pass
   * method for more information on the rational behind this. */
  imageStore(output_img, texel.yx, vec4(value));
}