1#pragma BLENDER_REQUIRE(common_view_lib.glsl)
2#pragma BLENDER_REQUIRE(common_math_lib.glsl)
3
4/**
5 * Separable Hexagonal Bokeh Blur by Colin Barré-Brisebois
6 * https://colinbarrebrisebois.com/2017/04/18/hexagonal-bokeh-blur-revisited-part-1-basic-3-pass-version/
7 * Converted and adapted from HLSL to GLSL by Clément Foucault
8 */
9
10uniform vec2 invertedViewportSize;
11uniform vec2 nearFar;
12uniform vec3 dofParams;
13uniform float noiseOffset;
14uniform sampler2D inputCocTex;
15uniform sampler2D maxCocTilesTex;
16uniform sampler2D sceneColorTex;
17uniform sampler2D sceneDepthTex;
18uniform sampler2D backgroundTex;
19uniform sampler2D halfResColorTex;
20uniform sampler2D blurTex;
21uniform sampler2D noiseTex;
22
23#define dof_aperturesize dofParams.x
24#define dof_distance dofParams.y
25#define dof_invsensorsize dofParams.z
26
27#define weighted_sum(a, b, c, d, e, e_sum) \
28  ((a)*e.x + (b)*e.y + (c)*e.z + (d)*e.w) / max(1e-6, e_sum);
29
30/* divide by sensor size to get the normalized size */
31#define calculate_coc(zdepth) \
32  (dof_aperturesize * (dof_distance / zdepth - 1.0) * dof_invsensorsize)
33
34#define linear_depth(z) \
35  ((ProjectionMatrix[3][3] == 0.0) ? \
36       (nearFar.x * nearFar.y) / (z * (nearFar.x - nearFar.y) + nearFar.y) : \
37       (z * 2.0 - 1.0) * nearFar.y)
38
39const float MAX_COC_SIZE = 100.0;
40vec2 encode_coc(float near, float far)
41{
42  return vec2(near, far) / MAX_COC_SIZE;
43}
44float decode_coc(vec2 cocs)
45{
46  return max(cocs.x, cocs.y) * MAX_COC_SIZE;
47}
48float decode_signed_coc(vec2 cocs)
49{
50  return ((cocs.x > cocs.y) ? cocs.x : -cocs.y) * MAX_COC_SIZE;
51}
52
53/**
54 * ----------------- STEP 0 ------------------
55 * Custom Coc aware downsampling. Half res pass.
56 */
57#ifdef PREPARE
58
59layout(location = 0) out vec4 halfResColor;
60layout(location = 1) out vec2 normalizedCoc;
61
62void main()
63{
64  ivec4 texel = ivec4(gl_FragCoord.xyxy) * 2 + ivec4(0, 0, 1, 1);
65
66  vec4 color1 = texelFetch(sceneColorTex, texel.xy, 0);
67  vec4 color2 = texelFetch(sceneColorTex, texel.zw, 0);
68  vec4 color3 = texelFetch(sceneColorTex, texel.zy, 0);
69  vec4 color4 = texelFetch(sceneColorTex, texel.xw, 0);
70
71  vec4 depths;
72  depths.x = texelFetch(sceneDepthTex, texel.xy, 0).x;
73  depths.y = texelFetch(sceneDepthTex, texel.zw, 0).x;
74  depths.z = texelFetch(sceneDepthTex, texel.zy, 0).x;
75  depths.w = texelFetch(sceneDepthTex, texel.xw, 0).x;
76
77  vec4 zdepths = linear_depth(depths);
78  vec4 cocs_near = calculate_coc(zdepths);
79  vec4 cocs_far = -cocs_near;
80
81  float coc_near = max(max_v4(cocs_near), 0.0);
82  float coc_far = max(max_v4(cocs_far), 0.0);
83
84  /* now we need to write the near-far fields premultiplied by the coc
85   * also use bilateral weighting by each coc values to avoid bleeding. */
86  vec4 near_weights = step(0.0, cocs_near) * clamp(1.0 - abs(coc_near - cocs_near), 0.0, 1.0);
87  vec4 far_weights = step(0.0, cocs_far) * clamp(1.0 - abs(coc_far - cocs_far), 0.0, 1.0);
88
89  /* now write output to weighted buffers. */
90  /* Take far plane pixels in priority. */
91  vec4 w = any(notEqual(far_weights, vec4(0.0))) ? far_weights : near_weights;
92  float tot_weight = dot(w, vec4(1.0));
93  halfResColor = weighted_sum(color1, color2, color3, color4, w, tot_weight);
94  halfResColor = clamp(halfResColor, 0.0, 3.0);
95
96  normalizedCoc = encode_coc(coc_near, coc_far);
97}
98#endif
99
100/**
101 * ----------------- STEP 0.5 ------------------
102 * Custom Coc aware downsampling. Quater res pass.
103 */
104#ifdef DOWNSAMPLE
105
106layout(location = 0) out vec4 outColor;
107layout(location = 1) out vec2 outCocs;
108
109void main()
110{
111  vec4 texel = vec4(gl_FragCoord.xyxy) * 2.0 + vec4(0.0, 0.0, 1.0, 1.0);
112  texel = (texel - 0.5) / vec4(textureSize(sceneColorTex, 0).xyxy);
113
114  /* Using texelFetch can bypass the mip range setting on some platform.
115   * Using texture Lod fix this issue. Note that we need to disable filtering to get the right
116   * texel values. */
117  vec4 color1 = textureLod(sceneColorTex, texel.xy, 0.0);
118  vec4 color2 = textureLod(sceneColorTex, texel.zw, 0.0);
119  vec4 color3 = textureLod(sceneColorTex, texel.zy, 0.0);
120  vec4 color4 = textureLod(sceneColorTex, texel.xw, 0.0);
121
122  vec4 depths;
123  vec2 cocs1 = textureLod(inputCocTex, texel.xy, 0.0).rg;
124  vec2 cocs2 = textureLod(inputCocTex, texel.zw, 0.0).rg;
125  vec2 cocs3 = textureLod(inputCocTex, texel.zy, 0.0).rg;
126  vec2 cocs4 = textureLod(inputCocTex, texel.xw, 0.0).rg;
127
128  vec4 cocs_near = vec4(cocs1.r, cocs2.r, cocs3.r, cocs4.r) * MAX_COC_SIZE;
129  vec4 cocs_far = vec4(cocs1.g, cocs2.g, cocs3.g, cocs4.g) * MAX_COC_SIZE;
130
131  float coc_near = max_v4(cocs_near);
132  float coc_far = max_v4(cocs_far);
133
134  /* now we need to write the near-far fields premultiplied by the coc
135   * also use bilateral weighting by each coc values to avoid bleeding. */
136  vec4 near_weights = step(0.0, cocs_near) * clamp(1.0 - abs(coc_near - cocs_near), 0.0, 1.0);
137  vec4 far_weights = step(0.0, cocs_far) * clamp(1.0 - abs(coc_far - cocs_far), 0.0, 1.0);
138
139  /* now write output to weighted buffers. */
140  vec4 w = any(notEqual(far_weights, vec4(0.0))) ? far_weights : near_weights;
141  float tot_weight = dot(w, vec4(1.0));
142  outColor = weighted_sum(color1, color2, color3, color4, w, tot_weight);
143
144  outCocs = encode_coc(coc_near, coc_far);
145}
146#endif
147
148/**
149 * ----------------- STEP 1 ------------------
150 * Flatten COC buffer using max filter.
151 */
152#if defined(FLATTEN_VERTICAL) || defined(FLATTEN_HORIZONTAL)
153
154layout(location = 0) out vec2 flattenedCoc;
155
156void main()
157{
158#  ifdef FLATTEN_HORIZONTAL
159  ivec2 texel = ivec2(gl_FragCoord.xy) * ivec2(8, 1);
160  vec2 cocs1 = texelFetchOffset(inputCocTex, texel, 0, ivec2(0, 0)).rg;
161  vec2 cocs2 = texelFetchOffset(inputCocTex, texel, 0, ivec2(1, 0)).rg;
162  vec2 cocs3 = texelFetchOffset(inputCocTex, texel, 0, ivec2(2, 0)).rg;
163  vec2 cocs4 = texelFetchOffset(inputCocTex, texel, 0, ivec2(3, 0)).rg;
164  vec2 cocs5 = texelFetchOffset(inputCocTex, texel, 0, ivec2(4, 0)).rg;
165  vec2 cocs6 = texelFetchOffset(inputCocTex, texel, 0, ivec2(5, 0)).rg;
166  vec2 cocs7 = texelFetchOffset(inputCocTex, texel, 0, ivec2(6, 0)).rg;
167  vec2 cocs8 = texelFetchOffset(inputCocTex, texel, 0, ivec2(7, 0)).rg;
168#  else /* FLATTEN_VERTICAL */
169  ivec2 texel = ivec2(gl_FragCoord.xy) * ivec2(1, 8);
170  vec2 cocs1 = texelFetchOffset(inputCocTex, texel, 0, ivec2(0, 0)).rg;
171  vec2 cocs2 = texelFetchOffset(inputCocTex, texel, 0, ivec2(0, 1)).rg;
172  vec2 cocs3 = texelFetchOffset(inputCocTex, texel, 0, ivec2(0, 2)).rg;
173  vec2 cocs4 = texelFetchOffset(inputCocTex, texel, 0, ivec2(0, 3)).rg;
174  vec2 cocs5 = texelFetchOffset(inputCocTex, texel, 0, ivec2(0, 4)).rg;
175  vec2 cocs6 = texelFetchOffset(inputCocTex, texel, 0, ivec2(0, 5)).rg;
176  vec2 cocs7 = texelFetchOffset(inputCocTex, texel, 0, ivec2(0, 6)).rg;
177  vec2 cocs8 = texelFetchOffset(inputCocTex, texel, 0, ivec2(0, 7)).rg;
178#  endif
179  flattenedCoc = max(max(max(cocs1, cocs2), max(cocs3, cocs4)),
180                     max(max(cocs5, cocs6), max(cocs7, cocs8)));
181}
182#endif
183
184/**
185 * ----------------- STEP 1.ax------------------
186 * Dilate COC buffer using min filter.
187 */
188#if defined(DILATE_VERTICAL) || defined(DILATE_HORIZONTAL)
189
190layout(location = 0) out vec2 dilatedCoc;
191
192void main()
193{
194  vec2 texel_size = 1.0 / vec2(textureSize(inputCocTex, 0));
195  vec2 uv = gl_FragCoord.xy * texel_size;
196#  ifdef DILATE_VERTICAL
197  vec2 cocs1 = texture(inputCocTex, uv + texel_size * vec2(-3, 0)).rg;
198  vec2 cocs2 = texture(inputCocTex, uv + texel_size * vec2(-2, 0)).rg;
199  vec2 cocs3 = texture(inputCocTex, uv + texel_size * vec2(-1, 0)).rg;
200  vec2 cocs4 = texture(inputCocTex, uv + texel_size * vec2(0, 0)).rg;
201  vec2 cocs5 = texture(inputCocTex, uv + texel_size * vec2(1, 0)).rg;
202  vec2 cocs6 = texture(inputCocTex, uv + texel_size * vec2(2, 0)).rg;
203  vec2 cocs7 = texture(inputCocTex, uv + texel_size * vec2(3, 0)).rg;
204#  else /* DILATE_HORIZONTAL */
205  vec2 cocs1 = texture(inputCocTex, uv + texel_size * vec2(0, -3)).rg;
206  vec2 cocs2 = texture(inputCocTex, uv + texel_size * vec2(0, -2)).rg;
207  vec2 cocs3 = texture(inputCocTex, uv + texel_size * vec2(0, -1)).rg;
208  vec2 cocs4 = texture(inputCocTex, uv + texel_size * vec2(0, 0)).rg;
209  vec2 cocs5 = texture(inputCocTex, uv + texel_size * vec2(0, 1)).rg;
210  vec2 cocs6 = texture(inputCocTex, uv + texel_size * vec2(0, 2)).rg;
211  vec2 cocs7 = texture(inputCocTex, uv + texel_size * vec2(0, 3)).rg;
212#  endif
213  // dilatedCoc = max(max(cocs3, cocs4), max(max(cocs5, cocs6), cocs2));
214  dilatedCoc = max(max(max(cocs1, cocs2), max(cocs3, cocs4)), max(max(cocs5, cocs6), cocs7));
215}
216#endif
217
218/**
219 * ----------------- STEP 2 ------------------
220 * Blur vertically and diagonally.
221 * Outputs vertical blur and combined blur in MRT
222 */
223#ifdef BLUR1
224layout(location = 0) out vec4 blurColor;
225
226#  define NUM_SAMPLES 49
227
228layout(std140) uniform dofSamplesBlock
229{
230  vec4 samples[NUM_SAMPLES];
231};
232
233vec2 get_random_vector(float offset)
234{
235  /* Interlieved gradient noise by Jorge Jimenez
236   * http://www.iryoku.com/next-generation-post-processing-in-call-of-duty-advanced-warfare */
237  float ign = fract(offset +
238                    52.9829189 * fract(0.06711056 * gl_FragCoord.x + 0.00583715 * gl_FragCoord.y));
239  float bn = texelFetch(noiseTex, ivec2(gl_FragCoord.xy) % 64, 0).a;
240  float ang = M_PI * 2.0 * fract(bn + offset);
241  return vec2(cos(ang), sin(ang)) * sqrt(ign);
242  // return noise.rg * sqrt(ign);
243}
244
245void main()
246{
247  vec2 uv = gl_FragCoord.xy * invertedViewportSize * 2.0;
248
249  vec2 size = vec2(textureSize(halfResColorTex, 0).xy);
250  ivec2 texel = ivec2(uv * size);
251
252  vec4 color = vec4(0.0);
253  float tot = 0.0;
254
255  float coc = decode_coc(texelFetch(inputCocTex, texel, 0).rg);
256  float max_radius = coc;
257  vec2 noise = get_random_vector(noiseOffset) * 0.2 * clamp(max_radius * 0.2 - 4.0, 0.0, 1.0);
258  for (int i = 0; i < NUM_SAMPLES; i++) {
259    vec2 tc = uv + (noise + samples[i].xy) * invertedViewportSize * max_radius;
260
261    /* decode_signed_coc return biggest coc. */
262    coc = abs(decode_signed_coc(texture(inputCocTex, tc).rg));
263
264    float lod = log2(clamp((coc + min(coc, max_radius)) * 0.5 - 21.0, 0.0, 16.0) * 0.25);
265    vec4 samp = textureLod(halfResColorTex, tc, lod);
266
267    float radius = samples[i].z * max_radius;
268    float weight = abs(coc) * smoothstep(radius - 0.5, radius + 0.5, abs(coc));
269
270    color += samp * weight;
271    tot += weight;
272  }
273
274  if (tot > 0.0) {
275    blurColor = color / tot;
276  }
277  else {
278    blurColor = textureLod(halfResColorTex, uv, 0.0);
279  }
280}
281#endif
282
283/**
284 * ----------------- STEP 3 ------------------
285 * 3x3 Median Filter
286 * Morgan McGuire and Kyle Whitson
287 * http://graphics.cs.williams.edu
288 *
289 *
290 * Copyright (c) Morgan McGuire and Williams College, 2006
291 * All rights reserved.
292 * Redistribution and use in source and binary forms, with or without
293 * modification, are permitted provided that the following conditions are
294 * met:
295 *
296 * Redistributions of source code must retain the above copyright notice,
297 * this list of conditions and the following disclaimer.
298 *
299 * Redistributions in binary form must reproduce the above copyright
300 * notice, this list of conditions and the following disclaimer in the
301 * documentation and/or other materials provided with the distribution.
302 *
303 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
304 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
305 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
306 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
307 * HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
308 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
309 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
310 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
311 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
312 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
313 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
314 */
315#ifdef BLUR2
316out vec4 finalColor;
317
318void main()
319{
320  /* Half Res pass */
321  vec2 pixel_size = 1.0 / vec2(textureSize(blurTex, 0).xy);
322  vec2 uv = gl_FragCoord.xy * pixel_size.xy;
323  float coc = decode_coc(texture(inputCocTex, uv).rg);
324  /* Only use this filter if coc is > 9.0
325   * since this filter is not weighted by CoC
326   * and can bleed a bit. */
327  float rad = clamp(coc - 9.0, 0.0, 1.0);
328
329#  define vec vec4
330#  define toVec(x) x.rgba
331
332#  define s2(a, b) \
333    temp = a; \
334    a = min(a, b); \
335    b = max(temp, b);
336#  define mn3(a, b, c) \
337    s2(a, b); \
338    s2(a, c);
339#  define mx3(a, b, c) \
340    s2(b, c); \
341    s2(a, c);
342
343#  define mnmx3(a, b, c) \
344    mx3(a, b, c); \
345    s2(a, b); /* 3 exchanges */
346#  define mnmx4(a, b, c, d) \
347    s2(a, b); \
348    s2(c, d); \
349    s2(a, c); \
350    s2(b, d); /* 4 exchanges */
351#  define mnmx5(a, b, c, d, e) \
352    s2(a, b); \
353    s2(c, d); \
354    mn3(a, c, e); \
355    mx3(b, d, e); /* 6 exchanges */
356#  define mnmx6(a, b, c, d, e, f) \
357    s2(a, d); \
358    s2(b, e); \
359    s2(c, f); \
360    mn3(a, b, c); \
361    mx3(d, e, f); /* 7 exchanges */
362
363  vec v[9];
364
365  /* Add the pixels which make up our window to the pixel array. */
366  for (int dX = -1; dX <= 1; dX++) {
367    for (int dY = -1; dY <= 1; dY++) {
368      vec2 offset = vec2(float(dX), float(dY));
369      /* If a pixel in the window is located at (x+dX, y+dY), put it at index (dX + R)(2R + 1) +
370       * (dY + R) of the pixel array. This will fill the pixel array, with the top left pixel of
371       * the window at pixel[0] and the bottom right pixel of the window at pixel[N-1]. */
372      v[(dX + 1) * 3 + (dY + 1)] = toVec(texture(blurTex, uv + offset * pixel_size * rad));
373    }
374  }
375
376  vec temp;
377
378  /* Starting with a subset of size 6, remove the min and max each time */
379  mnmx6(v[0], v[1], v[2], v[3], v[4], v[5]);
380  mnmx5(v[1], v[2], v[3], v[4], v[6]);
381  mnmx4(v[2], v[3], v[4], v[7]);
382  mnmx3(v[3], v[4], v[8]);
383  toVec(finalColor) = v[4];
384}
385
386#endif
387
388/**
389 * ----------------- STEP 4 ------------------
390 */
391#ifdef RESOLVE
392
393layout(location = 0, index = 0) out vec4 finalColorAdd;
394layout(location = 0, index = 1) out vec4 finalColorMul;
395
396void main()
397{
398  /* Fullscreen pass */
399  vec2 pixel_size = 0.5 / vec2(textureSize(halfResColorTex, 0).xy);
400  vec2 uv = gl_FragCoord.xy * pixel_size;
401
402  /* TODO MAKE SURE TO ALIGN SAMPLE POSITION TO AVOID OFFSET IN THE BOKEH */
403  float depth = texelFetch(sceneDepthTex, ivec2(gl_FragCoord.xy), 0).r;
404  float zdepth = linear_depth(depth);
405  float coc = calculate_coc(zdepth);
406
407  float blend = smoothstep(1.0, 3.0, abs(coc));
408  finalColorAdd = texture(halfResColorTex, uv) * blend;
409  finalColorMul = vec4(1.0 - blend);
410}
411#endif
412