ssao.glsl 24 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479
  1. ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  2. // Copyright (c) 2016, Intel Corporation
  3. // Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated
  4. // documentation files (the "Software"), to deal in the Software without restriction, including without limitation
  5. // the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to
  6. // permit persons to whom the Software is furnished to do so, subject to the following conditions:
  7. // The above copyright notice and this permission notice shall be included in all copies or substantial portions of
  8. // the Software.
  9. // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO
  10. // THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
  11. // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
  12. // TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
  13. // SOFTWARE.
  14. ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  15. // File changes (yyyy-mm-dd)
  16. // 2016-09-07: filip.strugar@intel.com: first commit
  17. // 2020-12-05: clayjohn: convert to Vulkan and Godot
  18. ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  19. #[compute]
  20. #version 450
  21. #VERSION_DEFINES
  22. #define INTELSSAO_MAIN_DISK_SAMPLE_COUNT (32)
  23. const vec4 sample_pattern[INTELSSAO_MAIN_DISK_SAMPLE_COUNT] = {
  24. vec4(0.78488064, 0.56661671, 1.500000, -0.126083), vec4(0.26022232, -0.29575172, 1.500000, -1.064030), vec4(0.10459357, 0.08372527, 1.110000, -2.730563), vec4(-0.68286800, 0.04963045, 1.090000, -0.498827),
  25. vec4(-0.13570161, -0.64190155, 1.250000, -0.532765), vec4(-0.26193795, -0.08205118, 0.670000, -1.783245), vec4(-0.61177456, 0.66664219, 0.710000, -0.044234), vec4(0.43675563, 0.25119025, 0.610000, -1.167283),
  26. vec4(0.07884444, 0.86618668, 0.640000, -0.459002), vec4(-0.12790935, -0.29869005, 0.600000, -1.729424), vec4(-0.04031125, 0.02413622, 0.600000, -4.792042), vec4(0.16201244, -0.52851415, 0.790000, -1.067055),
  27. vec4(-0.70991218, 0.47301072, 0.640000, -0.335236), vec4(0.03277707, -0.22349690, 0.600000, -1.982384), vec4(0.68921727, 0.36800742, 0.630000, -0.266718), vec4(0.29251814, 0.37775412, 0.610000, -1.422520),
  28. vec4(-0.12224089, 0.96582592, 0.600000, -0.426142), vec4(0.11071457, -0.16131058, 0.600000, -2.165947), vec4(0.46562141, -0.59747696, 0.600000, -0.189760), vec4(-0.51548797, 0.11804193, 0.600000, -1.246800),
  29. vec4(0.89141309, -0.42090443, 0.600000, 0.028192), vec4(-0.32402530, -0.01591529, 0.600000, -1.543018), vec4(0.60771245, 0.41635221, 0.600000, -0.605411), vec4(0.02379565, -0.08239821, 0.600000, -3.809046),
  30. vec4(0.48951152, -0.23657045, 0.600000, -1.189011), vec4(-0.17611565, -0.81696892, 0.600000, -0.513724), vec4(-0.33930185, -0.20732205, 0.600000, -1.698047), vec4(-0.91974425, 0.05403209, 0.600000, 0.062246),
  31. vec4(-0.15064627, -0.14949332, 0.600000, -1.896062), vec4(0.53180975, -0.35210401, 0.600000, -0.758838), vec4(0.41487166, 0.81442589, 0.600000, -0.505648), vec4(-0.24106961, -0.32721516, 0.600000, -1.665244)
  32. };
  33. // these values can be changed (up to SSAO_MAX_TAPS) with no changes required elsewhere; values for 4th and 5th preset are ignored but array needed to avoid compilation errors
  34. // the actual number of texture samples is two times this value (each "tap" has two symmetrical depth texture samples)
  35. const int num_taps[5] = { 3, 5, 12, 0, 0 };
  36. #define SSAO_TILT_SAMPLES_ENABLE_AT_QUALITY_PRESET (99) // to disable simply set to 99 or similar
  37. #define SSAO_TILT_SAMPLES_AMOUNT (0.4)
  38. //
  39. #define SSAO_HALOING_REDUCTION_ENABLE_AT_QUALITY_PRESET (1) // to disable simply set to 99 or similar
  40. #define SSAO_HALOING_REDUCTION_AMOUNT (0.6) // values from 0.0 - 1.0, 1.0 means max weighting (will cause artifacts, 0.8 is more reasonable)
  41. //
  42. #define SSAO_NORMAL_BASED_EDGES_ENABLE_AT_QUALITY_PRESET (2) // to disable simply set to 99 or similar
  43. #define SSAO_NORMAL_BASED_EDGES_DOT_THRESHOLD (0.5) // use 0-0.1 for super-sharp normal-based edges
  44. //
  45. #define SSAO_DETAIL_AO_ENABLE_AT_QUALITY_PRESET (1) // whether to use detail; to disable simply set to 99 or similar
  46. //
  47. #define SSAO_DEPTH_MIPS_ENABLE_AT_QUALITY_PRESET (2) // !!warning!! the MIP generation on the C++ side will be enabled on quality preset 2 regardless of this value, so if changing here, change the C++ side too
  48. #define SSAO_DEPTH_MIPS_GLOBAL_OFFSET (-4.3) // best noise/quality/performance tradeoff, found empirically
  49. //
  50. // !!warning!! the edge handling is hard-coded to 'disabled' on quality level 0, and enabled above, on the C++ side; while toggling it here will work for
  51. // testing purposes, it will not yield performance gains (or correct results)
  52. #define SSAO_DEPTH_BASED_EDGES_ENABLE_AT_QUALITY_PRESET (1)
  53. //
  54. #define SSAO_REDUCE_RADIUS_NEAR_SCREEN_BORDER_ENABLE_AT_QUALITY_PRESET (1)
  55. #define SSAO_MAX_TAPS 32
  56. #define SSAO_ADAPTIVE_TAP_BASE_COUNT 5
  57. #define SSAO_ADAPTIVE_TAP_FLEXIBLE_COUNT (SSAO_MAX_TAPS - SSAO_ADAPTIVE_TAP_BASE_COUNT)
  58. #define SSAO_DEPTH_MIP_LEVELS 4
  59. layout(local_size_x = 8, local_size_y = 8, local_size_z = 1) in;
  60. layout(set = 0, binding = 0) uniform sampler2DArray source_depth_mipmaps;
  61. layout(rgba8, set = 0, binding = 1) uniform restrict readonly image2D source_normal;
  62. layout(set = 0, binding = 2) uniform Constants { //get into a lower set
  63. vec4 rotation_matrices[20];
  64. }
  65. constants;
  66. #ifdef ADAPTIVE
  67. layout(rg8, set = 1, binding = 0) uniform restrict readonly image2DArray source_ssao;
  68. layout(set = 1, binding = 1) uniform sampler2D source_importance;
  69. layout(set = 1, binding = 2, std430) buffer Counter {
  70. uint sum;
  71. }
  72. counter;
  73. #endif
  74. layout(rg8, set = 2, binding = 0) uniform restrict writeonly image2D dest_image;
  75. // This push_constant is full - 128 bytes - if you need to add more data, consider adding to the uniform buffer instead
  76. layout(push_constant, std430) uniform Params {
  77. ivec2 screen_size;
  78. int pass;
  79. int quality;
  80. vec2 half_screen_pixel_size;
  81. int size_multiplier;
  82. float detail_intensity;
  83. vec2 NDC_to_view_mul;
  84. vec2 NDC_to_view_add;
  85. vec2 pad2;
  86. vec2 half_screen_pixel_size_x025;
  87. float radius;
  88. float intensity;
  89. float shadow_power;
  90. float shadow_clamp;
  91. float fade_out_mul;
  92. float fade_out_add;
  93. float horizon_angle_threshold;
  94. float inv_radius_near_limit;
  95. bool is_orthogonal;
  96. float neg_inv_radius;
  97. float load_counter_avg_div;
  98. float adaptive_sample_limit;
  99. ivec2 pass_coord_offset;
  100. vec2 pass_uv_offset;
  101. }
  102. params;
  103. // packing/unpacking for edges; 2 bits per edge mean 4 gradient values (0, 0.33, 0.66, 1) for smoother transitions!
  104. float pack_edges(vec4 p_edgesLRTB) {
  105. p_edgesLRTB = round(clamp(p_edgesLRTB, 0.0, 1.0) * 3.05);
  106. return dot(p_edgesLRTB, vec4(64.0 / 255.0, 16.0 / 255.0, 4.0 / 255.0, 1.0 / 255.0));
  107. }
  108. vec3 NDC_to_view_space(vec2 p_pos, float p_viewspace_depth) {
  109. if (params.is_orthogonal) {
  110. return vec3((params.NDC_to_view_mul * p_pos.xy + params.NDC_to_view_add), p_viewspace_depth);
  111. } else {
  112. return vec3((params.NDC_to_view_mul * p_pos.xy + params.NDC_to_view_add) * p_viewspace_depth, p_viewspace_depth);
  113. }
  114. }
  115. // calculate effect radius and fit our screen sampling pattern inside it
  116. void calculate_radius_parameters(const float p_pix_center_length, const vec2 p_pixel_size_at_center, out float r_lookup_radius, out float r_radius, out float r_fallof_sq) {
  117. r_radius = params.radius;
  118. // when too close, on-screen sampling disk will grow beyond screen size; limit this to avoid closeup temporal artifacts
  119. const float too_close_limit = clamp(p_pix_center_length * params.inv_radius_near_limit, 0.0, 1.0) * 0.8 + 0.2;
  120. r_radius *= too_close_limit;
  121. // 0.85 is to reduce the radius to allow for more samples on a slope to still stay within influence
  122. r_lookup_radius = (0.85 * r_radius) / p_pixel_size_at_center.x;
  123. // used to calculate falloff (both for AO samples and per-sample weights)
  124. r_fallof_sq = -1.0 / (r_radius * r_radius);
  125. }
  126. vec4 calculate_edges(const float p_center_z, const float p_left_z, const float p_right_z, const float p_top_z, const float p_bottom_z) {
  127. // slope-sensitive depth-based edge detection
  128. vec4 edgesLRTB = vec4(p_left_z, p_right_z, p_top_z, p_bottom_z) - p_center_z;
  129. vec4 edgesLRTB_slope_adjusted = edgesLRTB + edgesLRTB.yxwz;
  130. edgesLRTB = min(abs(edgesLRTB), abs(edgesLRTB_slope_adjusted));
  131. return clamp((1.3 - edgesLRTB / (p_center_z * 0.040)), 0.0, 1.0);
  132. }
  133. vec3 load_normal(ivec2 p_pos) {
  134. vec3 encoded_normal = normalize(imageLoad(source_normal, p_pos).xyz * 2.0 - 1.0);
  135. encoded_normal.z = -encoded_normal.z;
  136. return encoded_normal;
  137. }
  138. vec3 load_normal(ivec2 p_pos, ivec2 p_offset) {
  139. vec3 encoded_normal = normalize(imageLoad(source_normal, p_pos + p_offset).xyz * 2.0 - 1.0);
  140. encoded_normal.z = -encoded_normal.z;
  141. return encoded_normal;
  142. }
  143. // all vectors in viewspace
  144. float calculate_pixel_obscurance(vec3 p_pixel_normal, vec3 p_hit_delta, float p_fallof_sq) {
  145. float length_sq = dot(p_hit_delta, p_hit_delta);
  146. float NdotD = dot(p_pixel_normal, p_hit_delta) / sqrt(length_sq);
  147. float falloff_mult = max(0.0, length_sq * p_fallof_sq + 1.0);
  148. return max(0, NdotD - params.horizon_angle_threshold) * falloff_mult;
  149. }
  150. void SSAO_tap_inner(const int p_quality_level, inout float r_obscurance_sum, inout float r_weight_sum, const vec2 p_sampling_uv, const float p_mip_level, const vec3 p_pix_center_pos, vec3 p_pixel_normal, const float p_fallof_sq, const float p_weight_mod) {
  151. // get depth at sample
  152. float viewspace_sample_z = textureLod(source_depth_mipmaps, vec3(p_sampling_uv, params.pass), p_mip_level).x;
  153. // convert to viewspace
  154. vec3 hit_pos = NDC_to_view_space(p_sampling_uv.xy, viewspace_sample_z).xyz;
  155. vec3 hit_delta = hit_pos - p_pix_center_pos;
  156. float obscurance = calculate_pixel_obscurance(p_pixel_normal, hit_delta, p_fallof_sq);
  157. float weight = 1.0;
  158. if (p_quality_level >= SSAO_HALOING_REDUCTION_ENABLE_AT_QUALITY_PRESET) {
  159. float reduct = max(0, -hit_delta.z);
  160. reduct = clamp(reduct * params.neg_inv_radius + 2.0, 0.0, 1.0);
  161. weight = SSAO_HALOING_REDUCTION_AMOUNT * reduct + (1.0 - SSAO_HALOING_REDUCTION_AMOUNT);
  162. }
  163. weight *= p_weight_mod;
  164. r_obscurance_sum += obscurance * weight;
  165. r_weight_sum += weight;
  166. }
  167. void SSAOTap(const int p_quality_level, inout float r_obscurance_sum, inout float r_weight_sum, const int p_tap_index, const mat2 p_rot_scale, const vec3 p_pix_center_pos, vec3 p_pixel_normal, const vec2 p_normalized_screen_pos, const float p_mip_offset, const float p_fallof_sq, float p_weight_mod, vec2 p_norm_xy, float p_norm_xy_length) {
  168. vec2 sample_offset;
  169. float sample_pow_2_len;
  170. // patterns
  171. {
  172. vec4 new_sample = sample_pattern[p_tap_index];
  173. sample_offset = new_sample.xy * p_rot_scale;
  174. sample_pow_2_len = new_sample.w; // precalculated, same as: sample_pow_2_len = log2( length( new_sample.xy ) );
  175. p_weight_mod *= new_sample.z;
  176. }
  177. // snap to pixel center (more correct obscurance math, avoids artifacts)
  178. sample_offset = round(sample_offset);
  179. // calculate MIP based on the sample distance from the center, similar to as described
  180. // in http://graphics.cs.williams.edu/papers/SAOHPG12/.
  181. float mip_level = (p_quality_level < SSAO_DEPTH_MIPS_ENABLE_AT_QUALITY_PRESET) ? (0) : (sample_pow_2_len + p_mip_offset);
  182. vec2 sampling_uv = sample_offset * params.half_screen_pixel_size + p_normalized_screen_pos;
  183. SSAO_tap_inner(p_quality_level, r_obscurance_sum, r_weight_sum, sampling_uv, mip_level, p_pix_center_pos, p_pixel_normal, p_fallof_sq, p_weight_mod);
  184. // for the second tap, just use the mirrored offset
  185. vec2 sample_offset_mirrored_uv = -sample_offset;
  186. // tilt the second set of samples so that the disk is effectively rotated by the normal
  187. // effective at removing one set of artifacts, but too expensive for lower quality settings
  188. if (p_quality_level >= SSAO_TILT_SAMPLES_ENABLE_AT_QUALITY_PRESET) {
  189. float dot_norm = dot(sample_offset_mirrored_uv, p_norm_xy);
  190. sample_offset_mirrored_uv -= dot_norm * p_norm_xy_length * p_norm_xy;
  191. sample_offset_mirrored_uv = round(sample_offset_mirrored_uv);
  192. }
  193. // snap to pixel center (more correct obscurance math, avoids artifacts)
  194. vec2 sampling_mirrored_uv = sample_offset_mirrored_uv * params.half_screen_pixel_size + p_normalized_screen_pos;
  195. SSAO_tap_inner(p_quality_level, r_obscurance_sum, r_weight_sum, sampling_mirrored_uv, mip_level, p_pix_center_pos, p_pixel_normal, p_fallof_sq, p_weight_mod);
  196. }
  197. void generate_SSAO_shadows_internal(out float r_shadow_term, out vec4 r_edges, out float r_weight, const vec2 p_pos, int p_quality_level, bool p_adaptive_base) {
  198. vec2 pos_rounded = trunc(p_pos);
  199. uvec2 upos = uvec2(pos_rounded);
  200. const int number_of_taps = (p_adaptive_base) ? (SSAO_ADAPTIVE_TAP_BASE_COUNT) : (num_taps[p_quality_level]);
  201. float pix_z, pix_left_z, pix_top_z, pix_right_z, pix_bottom_z;
  202. vec4 valuesUL = textureGather(source_depth_mipmaps, vec3(pos_rounded * params.half_screen_pixel_size, params.pass));
  203. vec4 valuesBR = textureGather(source_depth_mipmaps, vec3((pos_rounded + vec2(1.0)) * params.half_screen_pixel_size, params.pass));
  204. // get this pixel's viewspace depth
  205. pix_z = valuesUL.y;
  206. // get left right top bottom neighboring pixels for edge detection (gets compiled out on quality_level == 0)
  207. pix_left_z = valuesUL.x;
  208. pix_top_z = valuesUL.z;
  209. pix_right_z = valuesBR.z;
  210. pix_bottom_z = valuesBR.x;
  211. vec2 normalized_screen_pos = pos_rounded * params.half_screen_pixel_size + params.half_screen_pixel_size_x025;
  212. vec3 pix_center_pos = NDC_to_view_space(normalized_screen_pos, pix_z);
  213. // Load this pixel's viewspace normal
  214. uvec2 full_res_coord = upos * 2 * params.size_multiplier + params.pass_coord_offset.xy;
  215. vec3 pixel_normal = load_normal(ivec2(full_res_coord));
  216. const vec2 pixel_size_at_center = NDC_to_view_space(normalized_screen_pos.xy + params.half_screen_pixel_size, pix_center_pos.z).xy - pix_center_pos.xy;
  217. float pixel_lookup_radius;
  218. float fallof_sq;
  219. // calculate effect radius and fit our screen sampling pattern inside it
  220. float viewspace_radius;
  221. calculate_radius_parameters(length(pix_center_pos), pixel_size_at_center, pixel_lookup_radius, viewspace_radius, fallof_sq);
  222. // calculate samples rotation/scaling
  223. mat2 rot_scale_matrix;
  224. uint pseudo_random_index;
  225. {
  226. vec4 rotation_scale;
  227. // reduce effect radius near the screen edges slightly; ideally, one would render a larger depth buffer (5% on each side) instead
  228. if (!p_adaptive_base && (p_quality_level >= SSAO_REDUCE_RADIUS_NEAR_SCREEN_BORDER_ENABLE_AT_QUALITY_PRESET)) {
  229. float near_screen_border = min(min(normalized_screen_pos.x, 1.0 - normalized_screen_pos.x), min(normalized_screen_pos.y, 1.0 - normalized_screen_pos.y));
  230. near_screen_border = clamp(10.0 * near_screen_border + 0.6, 0.0, 1.0);
  231. pixel_lookup_radius *= near_screen_border;
  232. }
  233. // load & update pseudo-random rotation matrix
  234. pseudo_random_index = uint(pos_rounded.y * 2 + pos_rounded.x) % 5;
  235. rotation_scale = constants.rotation_matrices[params.pass * 5 + pseudo_random_index];
  236. rot_scale_matrix = mat2(rotation_scale.x * pixel_lookup_radius, rotation_scale.y * pixel_lookup_radius, rotation_scale.z * pixel_lookup_radius, rotation_scale.w * pixel_lookup_radius);
  237. }
  238. // the main obscurance & sample weight storage
  239. float obscurance_sum = 0.0;
  240. float weight_sum = 0.0;
  241. // edge mask for between this and left/right/top/bottom neighbor pixels - not used in quality level 0 so initialize to "no edge" (1 is no edge, 0 is edge)
  242. vec4 edgesLRTB = vec4(1.0, 1.0, 1.0, 1.0);
  243. // Move center pixel slightly towards camera to avoid imprecision artifacts due to using of 16bit depth buffer.
  244. pix_center_pos *= 0.99;
  245. if (!p_adaptive_base && (p_quality_level >= SSAO_DEPTH_BASED_EDGES_ENABLE_AT_QUALITY_PRESET)) {
  246. edgesLRTB = calculate_edges(pix_z, pix_left_z, pix_right_z, pix_top_z, pix_bottom_z);
  247. }
  248. // adds a more high definition sharp effect, which gets blurred out (reuses left/right/top/bottom samples that we used for edge detection)
  249. if (!p_adaptive_base && (p_quality_level >= SSAO_DETAIL_AO_ENABLE_AT_QUALITY_PRESET)) {
  250. // disable in case of quality level 4 (reference)
  251. if (p_quality_level != 4) {
  252. //approximate neighboring pixels positions (actually just deltas or "positions - pix_center_pos" )
  253. vec3 normalized_viewspace_dir = vec3(pix_center_pos.xy / pix_center_pos.zz, 1.0);
  254. vec3 pixel_left_delta = vec3(-pixel_size_at_center.x, 0.0, 0.0) + normalized_viewspace_dir * (pix_left_z - pix_center_pos.z);
  255. vec3 pixel_right_delta = vec3(+pixel_size_at_center.x, 0.0, 0.0) + normalized_viewspace_dir * (pix_right_z - pix_center_pos.z);
  256. vec3 pixel_top_delta = vec3(0.0, -pixel_size_at_center.y, 0.0) + normalized_viewspace_dir * (pix_top_z - pix_center_pos.z);
  257. vec3 pixel_bottom_delta = vec3(0.0, +pixel_size_at_center.y, 0.0) + normalized_viewspace_dir * (pix_bottom_z - pix_center_pos.z);
  258. const float range_reduction = 4.0f; // this is to avoid various artifacts
  259. const float modified_fallof_sq = range_reduction * fallof_sq;
  260. vec4 additional_obscurance;
  261. additional_obscurance.x = calculate_pixel_obscurance(pixel_normal, pixel_left_delta, modified_fallof_sq);
  262. additional_obscurance.y = calculate_pixel_obscurance(pixel_normal, pixel_right_delta, modified_fallof_sq);
  263. additional_obscurance.z = calculate_pixel_obscurance(pixel_normal, pixel_top_delta, modified_fallof_sq);
  264. additional_obscurance.w = calculate_pixel_obscurance(pixel_normal, pixel_bottom_delta, modified_fallof_sq);
  265. obscurance_sum += params.detail_intensity * dot(additional_obscurance, edgesLRTB);
  266. }
  267. }
  268. // Sharp normals also create edges - but this adds to the cost as well
  269. if (!p_adaptive_base && (p_quality_level >= SSAO_NORMAL_BASED_EDGES_ENABLE_AT_QUALITY_PRESET)) {
  270. vec3 neighbour_normal_left = load_normal(ivec2(full_res_coord), ivec2(-2, 0));
  271. vec3 neighbour_normal_right = load_normal(ivec2(full_res_coord), ivec2(2, 0));
  272. vec3 neighbour_normal_top = load_normal(ivec2(full_res_coord), ivec2(0, -2));
  273. vec3 neighbour_normal_bottom = load_normal(ivec2(full_res_coord), ivec2(0, 2));
  274. const float dot_threshold = SSAO_NORMAL_BASED_EDGES_DOT_THRESHOLD;
  275. vec4 normal_edgesLRTB;
  276. normal_edgesLRTB.x = clamp((dot(pixel_normal, neighbour_normal_left) + dot_threshold), 0.0, 1.0);
  277. normal_edgesLRTB.y = clamp((dot(pixel_normal, neighbour_normal_right) + dot_threshold), 0.0, 1.0);
  278. normal_edgesLRTB.z = clamp((dot(pixel_normal, neighbour_normal_top) + dot_threshold), 0.0, 1.0);
  279. normal_edgesLRTB.w = clamp((dot(pixel_normal, neighbour_normal_bottom) + dot_threshold), 0.0, 1.0);
  280. edgesLRTB *= normal_edgesLRTB;
  281. }
  282. const float global_mip_offset = SSAO_DEPTH_MIPS_GLOBAL_OFFSET;
  283. float mip_offset = (p_quality_level < SSAO_DEPTH_MIPS_ENABLE_AT_QUALITY_PRESET) ? (0) : (log2(pixel_lookup_radius) + global_mip_offset);
  284. // Used to tilt the second set of samples so that the disk is effectively rotated by the normal
  285. // effective at removing one set of artifacts, but too expensive for lower quality settings
  286. vec2 norm_xy = vec2(pixel_normal.x, pixel_normal.y);
  287. float norm_xy_length = length(norm_xy);
  288. norm_xy /= vec2(norm_xy_length, -norm_xy_length);
  289. norm_xy_length *= SSAO_TILT_SAMPLES_AMOUNT;
  290. // standard, non-adaptive approach
  291. if ((p_quality_level != 3) || p_adaptive_base) {
  292. for (int i = 0; i < number_of_taps; i++) {
  293. SSAOTap(p_quality_level, obscurance_sum, weight_sum, i, rot_scale_matrix, pix_center_pos, pixel_normal, normalized_screen_pos, mip_offset, fallof_sq, 1.0, norm_xy, norm_xy_length);
  294. }
  295. }
  296. #ifdef ADAPTIVE
  297. else {
  298. // add new ones if needed
  299. vec2 full_res_uv = normalized_screen_pos + params.pass_uv_offset.xy;
  300. float importance = textureLod(source_importance, full_res_uv, 0.0).x;
  301. // this is to normalize SSAO_DETAIL_AO_AMOUNT across all pixel regardless of importance
  302. obscurance_sum *= (SSAO_ADAPTIVE_TAP_BASE_COUNT / float(SSAO_MAX_TAPS)) + (importance * SSAO_ADAPTIVE_TAP_FLEXIBLE_COUNT / float(SSAO_MAX_TAPS));
  303. // load existing base values
  304. vec2 base_values = imageLoad(source_ssao, ivec3(upos, params.pass)).xy;
  305. weight_sum += base_values.y * float(SSAO_ADAPTIVE_TAP_BASE_COUNT * 4.0);
  306. obscurance_sum += (base_values.x) * weight_sum;
  307. // increase importance around edges
  308. float edge_count = dot(1.0 - edgesLRTB, vec4(1.0, 1.0, 1.0, 1.0));
  309. float avg_total_importance = float(counter.sum) * params.load_counter_avg_div;
  310. float importance_limiter = clamp(params.adaptive_sample_limit / avg_total_importance, 0.0, 1.0);
  311. importance *= importance_limiter;
  312. float additional_sample_count = SSAO_ADAPTIVE_TAP_FLEXIBLE_COUNT * importance;
  313. const float blend_range = 3.0;
  314. const float blend_range_inv = 1.0 / blend_range;
  315. additional_sample_count += 0.5;
  316. uint additional_samples = uint(additional_sample_count);
  317. uint additional_samples_to = min(SSAO_MAX_TAPS, additional_samples + SSAO_ADAPTIVE_TAP_BASE_COUNT);
  318. for (uint i = SSAO_ADAPTIVE_TAP_BASE_COUNT; i < additional_samples_to; i++) {
  319. additional_sample_count -= 1.0f;
  320. float weight_mod = clamp(additional_sample_count * blend_range_inv, 0.0, 1.0);
  321. SSAOTap(p_quality_level, obscurance_sum, weight_sum, int(i), rot_scale_matrix, pix_center_pos, pixel_normal, normalized_screen_pos, mip_offset, fallof_sq, weight_mod, norm_xy, norm_xy_length);
  322. }
  323. }
  324. #endif
  325. // early out for adaptive base - just output weight (used for the next pass)
  326. if (p_adaptive_base) {
  327. float obscurance = obscurance_sum / weight_sum;
  328. r_shadow_term = obscurance;
  329. r_edges = vec4(0.0);
  330. r_weight = weight_sum;
  331. return;
  332. }
  333. // calculate weighted average
  334. float obscurance = obscurance_sum / weight_sum;
  335. // calculate fadeout (1 close, gradient, 0 far)
  336. float fade_out = clamp(pix_center_pos.z * params.fade_out_mul + params.fade_out_add, 0.0, 1.0);
  337. // Reduce the SSAO shadowing if we're on the edge to remove artifacts on edges (we don't care for the lower quality one)
  338. if (!p_adaptive_base && (p_quality_level >= SSAO_DEPTH_BASED_EDGES_ENABLE_AT_QUALITY_PRESET)) {
  339. // when there's more than 2 opposite edges, start fading out the occlusion to reduce aliasing artifacts
  340. float edge_fadeout_factor = clamp((1.0 - edgesLRTB.x - edgesLRTB.y) * 0.35, 0.0, 1.0) + clamp((1.0 - edgesLRTB.z - edgesLRTB.w) * 0.35, 0.0, 1.0);
  341. fade_out *= clamp(1.0 - edge_fadeout_factor, 0.0, 1.0);
  342. }
  343. // strength
  344. obscurance = params.intensity * obscurance;
  345. // clamp
  346. obscurance = min(obscurance, params.shadow_clamp);
  347. // fadeout
  348. obscurance *= fade_out;
  349. // conceptually switch to occlusion with the meaning being visibility (grows with visibility, occlusion == 1 implies full visibility),
  350. // to be in line with what is more commonly used.
  351. float occlusion = 1.0 - obscurance;
  352. // modify the gradient
  353. // note: this cannot be moved to a later pass because of loss of precision after storing in the render target
  354. occlusion = pow(clamp(occlusion, 0.0, 1.0), params.shadow_power);
  355. // outputs!
  356. r_shadow_term = occlusion; // Our final 'occlusion' term (0 means fully occluded, 1 means fully lit)
  357. r_edges = edgesLRTB; // These are used to prevent blurring across edges, 1 means no edge, 0 means edge, 0.5 means half way there, etc.
  358. r_weight = weight_sum;
  359. }
  360. void main() {
  361. float out_shadow_term;
  362. float out_weight;
  363. vec4 out_edges;
  364. ivec2 ssC = ivec2(gl_GlobalInvocationID.xy);
  365. if (any(greaterThanEqual(ssC, params.screen_size))) { //too large, do nothing
  366. return;
  367. }
  368. vec2 uv = vec2(gl_GlobalInvocationID) + vec2(0.5);
  369. #ifdef SSAO_BASE
  370. generate_SSAO_shadows_internal(out_shadow_term, out_edges, out_weight, uv, params.quality, true);
  371. imageStore(dest_image, ivec2(gl_GlobalInvocationID.xy), vec4(out_shadow_term, out_weight / (float(SSAO_ADAPTIVE_TAP_BASE_COUNT) * 4.0), 0.0, 0.0));
  372. #else
  373. generate_SSAO_shadows_internal(out_shadow_term, out_edges, out_weight, uv, params.quality, false); // pass in quality levels
  374. if (params.quality == 0) {
  375. out_edges = vec4(1.0);
  376. }
  377. imageStore(dest_image, ivec2(gl_GlobalInvocationID.xy), vec4(out_shadow_term, pack_edges(out_edges), 0.0, 0.0));
  378. #endif
  379. }