Eevee: Change Bent normal calculation
[blender.git] / source / blender / draw / engines / eevee / shaders / ambient_occlusion_lib.glsl
1
2 /* Based on Practical Realtime Strategies for Accurate Indirect Occlusion
3  * http://blog.selfshadow.com/publications/s2016-shading-course/activision/s2016_pbs_activision_occlusion.pdf
4  * http://blog.selfshadow.com/publications/s2016-shading-course/activision/s2016_pbs_activision_occlusion.pptx */
5
6 #if defined(MESH_SHADER)
7 # if !defined(USE_ALPHA_HASH)
8 #  if !defined(USE_ALPHA_CLIP)
9 #   if !defined(SHADOW_SHADER)
10 #    if !defined(USE_MULTIPLY)
11 #     if !defined(USE_ALPHA_BLEND)
12 #      define ENABLE_DEFERED_AO
13 #     endif
14 #    endif
15 #   endif
16 #  endif
17 # endif
18 #endif
19
20 #ifndef ENABLE_DEFERED_AO
21 # if defined(STEP_RESOLVE)
22 #  define ENABLE_DEFERED_AO
23 # endif
24 #endif
25
26 #define MAX_PHI_STEP 32
27 #define MAX_SEARCH_ITER 32
28 #define MAX_LOD 6.0
29
30 #ifndef UTIL_TEX
31 #define UTIL_TEX
32 uniform sampler2DArray utilTex;
33 #define texelfetch_noise_tex(coord) texelFetch(utilTex, ivec3(ivec2(coord) % LUT_SIZE, 2.0), 0)
34 #endif /* UTIL_TEX */
35
36 uniform sampler2D horizonBuffer;
37
38 /* aoSettings flags */
39 #define USE_AO            1
40 #define USE_BENT_NORMAL   2
41 #define USE_DENOISE       4
42
43 vec4 pack_horizons(vec4 v) { return v * 0.5 + 0.5; }
44 vec4 unpack_horizons(vec4 v) { return v * 2.0 - 1.0; }
45
46 /* Returns maximum screen distance an AO ray can travel for a given view depth */
47 vec2 get_max_dir(float view_depth)
48 {
49         float homcco = ProjectionMatrix[2][3] * view_depth + ProjectionMatrix[3][3];
50         float max_dist = aoDistance / homcco;
51         return vec2(ProjectionMatrix[0][0], ProjectionMatrix[1][1]) * max_dist;
52 }
53
54 vec2 get_ao_dir(float jitter)
55 {
56         /* Only half a turn because we integrate in slices. */
57         jitter *= M_PI;
58         return vec2(cos(jitter), sin(jitter));
59 }
60
61 void get_max_horizon_grouped(vec4 co1, vec4 co2, vec3 x, float lod, inout float h)
62 {
63         int mip = int(lod) + hizMipOffset;
64         co1 *= mipRatio[mip].xyxy;
65         co2 *= mipRatio[mip].xyxy;
66
67         float depth1 = textureLod(maxzBuffer, co1.xy, floor(lod)).r;
68         float depth2 = textureLod(maxzBuffer, co1.zw, floor(lod)).r;
69         float depth3 = textureLod(maxzBuffer, co2.xy, floor(lod)).r;
70         float depth4 = textureLod(maxzBuffer, co2.zw, floor(lod)).r;
71
72         vec4 len, s_h;
73
74         vec3 s1 = get_view_space_from_depth(co1.xy, depth1); /* s View coordinate */
75         vec3 omega_s1 = s1 - x;
76         len.x = length(omega_s1);
77         s_h.x = omega_s1.z / len.x;
78
79         vec3 s2 = get_view_space_from_depth(co1.zw, depth2); /* s View coordinate */
80         vec3 omega_s2 = s2 - x;
81         len.y = length(omega_s2);
82         s_h.y = omega_s2.z / len.y;
83
84         vec3 s3 = get_view_space_from_depth(co2.xy, depth3); /* s View coordinate */
85         vec3 omega_s3 = s3 - x;
86         len.z = length(omega_s3);
87         s_h.z = omega_s3.z / len.z;
88
89         vec3 s4 = get_view_space_from_depth(co2.zw, depth4); /* s View coordinate */
90         vec3 omega_s4 = s4 - x;
91         len.w = length(omega_s4);
92         s_h.w = omega_s4.z / len.w;
93
94         /* Blend weight after half the aoDistance to fade artifacts */
95         vec4 blend = saturate((1.0 - len / aoDistance) * 2.0);
96
97         h = mix(h, max(h, s_h.x), blend.x);
98         h = mix(h, max(h, s_h.y), blend.y);
99         h = mix(h, max(h, s_h.z), blend.z);
100         h = mix(h, max(h, s_h.w), blend.w);
101 }
102
103 vec2 search_horizon_sweep(vec2 t_phi, vec3 pos, vec2 uvs, float jitter, vec2 max_dir)
104 {
105         max_dir *= max_v2(abs(t_phi));
106
107         /* Convert to pixel space. */
108         t_phi /= vec2(textureSize(maxzBuffer, 0));
109
110         /* Avoid division by 0 */
111         t_phi += vec2(1e-5);
112
113         jitter *= 0.25;
114
115         /* Compute end points */
116         vec2 corner1 = min(vec2(1.0) - uvs,  max_dir); /* Top right */
117         vec2 corner2 = max(vec2(0.0) - uvs, -max_dir); /* Bottom left */
118         vec2 iter1 = corner1 / t_phi;
119         vec2 iter2 = corner2 / t_phi;
120
121         vec2 min_iter = max(-iter1, -iter2);
122         vec2 max_iter = max( iter1,  iter2);
123
124         vec2 times = vec2(-min_v2(min_iter), min_v2(max_iter));
125
126         vec2 h = vec2(-1.0); /* init at cos(pi) */
127
128         /* This is freaking sexy optimized. */
129         for (float i = 0.0, ofs = 4.0, time = -1.0;
130                  i < MAX_SEARCH_ITER && time > times.x;
131                  i++, time -= ofs, ofs = min(exp2(MAX_LOD) * 4.0, ofs + ofs * aoQuality))
132         {
133                 vec4 t = max(times.xxxx, vec4(time) - (vec4(0.25, 0.5, 0.75, 1.0) - jitter) * ofs);
134                 vec4 cos1 = uvs.xyxy + t_phi.xyxy * t.xxyy;
135                 vec4 cos2 = uvs.xyxy + t_phi.xyxy * t.zzww;
136                 float lod = min(MAX_LOD, max(i - jitter * 4.0, 0.0) * aoQuality);
137                 get_max_horizon_grouped(cos1, cos2, pos, lod, h.y);
138         }
139
140         for (float i = 0.0, ofs = 4.0, time = 1.0;
141                  i < MAX_SEARCH_ITER && time < times.y;
142                  i++, time += ofs, ofs = min(exp2(MAX_LOD) * 4.0, ofs + ofs * aoQuality))
143         {
144                 vec4 t = min(times.yyyy, vec4(time) + (vec4(0.25, 0.5, 0.75, 1.0) - jitter) * ofs);
145                 vec4 cos1 = uvs.xyxy + t_phi.xyxy * t.xxyy;
146                 vec4 cos2 = uvs.xyxy + t_phi.xyxy * t.zzww;
147                 float lod = min(MAX_LOD, max(i - jitter * 4.0, 0.0) * aoQuality);
148                 get_max_horizon_grouped(cos1, cos2, pos, lod, h.x);
149         }
150
151         return h;
152 }
153
154 void integrate_slice(vec3 normal, vec2 t_phi, vec2 horizons, inout float visibility, inout vec3 bent_normal)
155 {
156         /* Projecting Normal to Plane P defined by t_phi and omega_o */
157         vec3 np = vec3(t_phi.y, -t_phi.x, 0.0); /* Normal vector to Integration plane */
158         vec3 t = vec3(-t_phi, 0.0);
159         vec3 n_proj = normal - np * dot(np, normal);
160         float n_proj_len = max(1e-16, length(n_proj));
161
162         float cos_n = clamp(n_proj.z / n_proj_len, -1.0, 1.0);
163         float n = sign(dot(n_proj, t)) * fast_acos(cos_n); /* Angle between view vec and normal */
164
165         /* (Slide 54) */
166         vec2 h = fast_acos(horizons);
167         h.x = -h.x;
168
169         /* Clamping thetas (slide 58) */
170         h.x = n + max(h.x - n, -M_PI_2);
171         h.y = n + min(h.y - n, M_PI_2);
172
173         /* Solving inner integral */
174         vec2 h_2 = 2.0 * h;
175         vec2 vd = -cos(h_2 - n) + cos_n + h_2 * sin(n);
176         float vis = (vd.x + vd.y) * 0.25 * n_proj_len;
177
178         visibility += vis;
179
180         /* O. Klehm, T. Ritschel, E. Eisemann, H.-P. Seidel
181          * Bent Normals and Cones in Screen-space
182          * Sec. 3.1 : Bent normals */
183         float b_angle = (h.x + h.y) * 0.5;
184         bent_normal += vec3(sin(b_angle) * -t_phi, cos(b_angle)) * vis;
185 }
186
187 void gtao_deferred(
188         vec3 normal, vec4 noise, float frag_depth, out float visibility, out vec3 bent_normal)
189 {
190         /* Fetch early, hide latency! */
191         vec4 horizons = texelFetch(horizonBuffer, ivec2(gl_FragCoord.xy), 0);
192
193         vec4 dirs;
194         dirs.xy = get_ao_dir(noise.x * 0.5);
195         dirs.zw = get_ao_dir(noise.x * 0.5 + 0.5);
196
197         bent_normal = vec3(0.0);
198         visibility = 0.0;
199
200         horizons = unpack_horizons(horizons);
201
202         integrate_slice(normal, dirs.xy, horizons.xy, visibility, bent_normal);
203         integrate_slice(normal, dirs.zw, horizons.zw, visibility, bent_normal);
204
205         bent_normal = normalize(bent_normal / visibility);
206
207         visibility *= 0.5; /* We integrated 2 slices. */
208 }
209
210 void gtao(vec3 normal, vec3 position, vec4 noise, out float visibility, out vec3 bent_normal)
211 {
212         vec2 uvs = get_uvs_from_view(position);
213         vec2 max_dir = get_max_dir(position.z);
214         vec2 dir = get_ao_dir(noise.x);
215
216         bent_normal = vec3(0.0);
217         visibility = 0.0;
218
219         /* Only trace in 2 directions. May lead to a darker result but since it's mostly for
220          * alpha blended objects that will have overdraw, we limit the performance impact. */
221         vec2 horizons = search_horizon_sweep(dir, position, uvs, noise.y, max_dir);
222         integrate_slice(normal, dir, horizons, visibility, bent_normal);
223
224         bent_normal = normalize(bent_normal / visibility);
225 }
226
227 /* Multibounce approximation base on surface albedo.
228  * Page 78 in the .pdf version. */
229 float gtao_multibounce(float visibility, vec3 albedo)
230 {
231         if (aoBounceFac == 0.0) return visibility;
232
233         /* Median luminance. Because Colored multibounce looks bad. */
234         float lum = dot(albedo, vec3(0.3333));
235
236         float a =  2.0404 * lum - 0.3324;
237         float b = -4.7951 * lum + 0.6417;
238         float c =  2.7552 * lum + 0.6903;
239
240         float x = visibility;
241         return max(x, ((x * a + b) * x + c) * x);
242 }
243
244 /* Use the right occlusion  */
245 float occlusion_compute(vec3 N, vec3 vpos, float user_occlusion, vec4 rand, out vec3 bent_normal)
246 {
247 #ifndef USE_REFRACTION
248         if ((int(aoSettings) & USE_AO) != 0) {
249                 float visibility;
250                 vec3 vnor = mat3(ViewMatrix) * N;
251
252 #ifdef ENABLE_DEFERED_AO
253                 gtao_deferred(vnor, rand, gl_FragCoord.z, visibility, bent_normal);
254 #else
255                 gtao(vnor, vpos, rand, visibility, bent_normal);
256 #endif
257
258                 /* Prevent some problems down the road. */
259                 visibility = max(1e-3, visibility);
260
261                 if ((int(aoSettings) & USE_BENT_NORMAL) != 0) {
262                         /* The bent normal will show the facet look of the mesh. Try to minimize this. */
263                         float mix_fac = visibility * visibility * visibility;
264                         bent_normal = normalize(mix(bent_normal, vnor, mix_fac));
265
266                         bent_normal = transform_direction(ViewMatrixInverse, bent_normal);
267                 }
268                 else {
269                         bent_normal = N;
270                 }
271
272                 /* Scale by user factor */
273                 visibility = pow(visibility, aoFactor);
274
275                 return min(visibility, user_occlusion);
276         }
277 #endif
278
279         bent_normal = N;
280         return user_occlusion;
281 }