Functions.glsl 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388
  1. // Copyright (C) 2009-2018, Panagiotis Christopoulos Charitos and contributors.
  2. // All rights reserved.
  3. // Code licensed under the BSD License.
  4. // http://www.anki3d.org/LICENSE
  5. #pragma once
  6. #include <shaders/Common.glsl>
  7. #if defined(ANKI_FRAGMENT_SHADER)
  8. Vec3 dither(in Vec3 col, in F32 C)
  9. {
  10. Vec3 vDither = Vec3(dot(Vec2(171.0, 231.0), gl_FragCoord.xy));
  11. vDither.rgb = fract(vDither.rgb / Vec3(103.0, 71.0, 97.0));
  12. col = col * (255.0 / C) + vDither.rgb;
  13. col = floor(col) / 255.0;
  14. col *= C;
  15. return col;
  16. }
  17. F32 dither(in F32 col, in F32 C)
  18. {
  19. F32 vDither = dot(Vec2(171.0, 231.0), gl_FragCoord.xy);
  20. vDither = fract(vDither / 103.0);
  21. col = col * (255.0 / C) + vDither;
  22. col = floor(col) / 255.0;
  23. col *= C;
  24. return col;
  25. }
  26. #endif
  27. // Convert to linear depth
  28. F32 linearizeDepth(in F32 depth, in F32 zNear, in F32 zFar)
  29. {
  30. return zNear / ((zNear - zFar) + zFar / depth);
  31. }
  32. // This is the optimal linearizeDepth where a=(n-f)/n and b=f/n
  33. F32 linearizeDepthOptimal(in F32 depth, in F32 a, in F32 b)
  34. {
  35. return 1.0 / (a + b / depth);
  36. }
  37. // This is the optimal linearizeDepth where a=(n-f)/n and b=f/n
  38. Vec4 linearizeDepthOptimal(in Vec4 depths, in F32 a, in F32 b)
  39. {
  40. return 1.0 / (a + b / depths);
  41. }
  42. // Project a vector by knowing only the non zero values of a perspective matrix
  43. Vec4 projectPerspective(in Vec4 vec, in F32 m00, in F32 m11, in F32 m22, in F32 m23)
  44. {
  45. Vec4 o;
  46. o.x = vec.x * m00;
  47. o.y = vec.y * m11;
  48. o.z = vec.z * m22 + vec.w * m23;
  49. o.w = -vec.z;
  50. return o;
  51. }
  52. // Stolen from shadertoy.com/view/4tyGDD
  53. Vec4 textureCatmullRom4Samples(sampler2D tex, Vec2 uv, Vec2 texSize)
  54. {
  55. Vec2 halff = 2.0 * fract(0.5 * uv * texSize - 0.25) - 1.0;
  56. Vec2 f = fract(halff);
  57. Vec2 sum0 = (2.0 * f - 3.5) * f + 0.5;
  58. Vec2 sum1 = (2.0 * f - 2.5) * f - 0.5;
  59. Vec4 w = Vec4(f * sum0 + 1.0, f * sum1);
  60. Vec4 pos = Vec4((((-2.0 * f + 3.0) * f + 0.5) * f - 1.5) * f / (w.xy * texSize) + uv,
  61. (((-2.0 * f + 5.0) * f - 2.5) * f - 0.5) / (sum1 * texSize) + uv);
  62. w.xz *= halff.x * halff.y > 0.0 ? 1.0 : -1.0;
  63. return (texture(tex, pos.xy) * w.x + texture(tex, pos.zy) * w.z) * w.y
  64. + (texture(tex, pos.xw) * w.x + texture(tex, pos.zw) * w.z) * w.w;
  65. }
  66. F32 rand(Vec2 n)
  67. {
  68. return 0.5 + 0.5 * fract(sin(dot(n, Vec2(12.9898, 78.233))) * 43758.5453);
  69. }
  70. Vec4 nearestDepthUpscale(
  71. Vec2 uv, sampler2D depthFull, sampler2D depthHalf, sampler2D colorTex, Vec2 linearDepthCf, F32 depthThreshold)
  72. {
  73. F32 fullDepth = texture(depthFull, uv).r;
  74. fullDepth = linearizeDepthOptimal(fullDepth, linearDepthCf.x, linearDepthCf.y);
  75. Vec4 halfDepths = textureGather(depthHalf, uv, 0);
  76. halfDepths = linearizeDepthOptimal(halfDepths, linearDepthCf.x, linearDepthCf.y);
  77. Vec4 diffs = abs(Vec4(fullDepth) - halfDepths);
  78. Vec4 color;
  79. if(all(lessThan(diffs, Vec4(depthThreshold))))
  80. {
  81. // No major discontinuites, sample with bilinear
  82. color = texture(colorTex, uv);
  83. }
  84. else
  85. {
  86. // Some discontinuites, need to use the newUv
  87. Vec4 r = textureGather(colorTex, uv, 0);
  88. Vec4 g = textureGather(colorTex, uv, 1);
  89. Vec4 b = textureGather(colorTex, uv, 2);
  90. Vec4 a = textureGather(colorTex, uv, 3);
  91. F32 minDiff = diffs.x;
  92. U32 comp = 0;
  93. if(diffs.y < minDiff)
  94. {
  95. comp = 1;
  96. minDiff = diffs.y;
  97. }
  98. if(diffs.z < minDiff)
  99. {
  100. comp = 2;
  101. minDiff = diffs.z;
  102. }
  103. if(diffs.w < minDiff)
  104. {
  105. comp = 3;
  106. }
  107. color = Vec4(r[comp], g[comp], b[comp], a[comp]);
  108. }
  109. return color;
  110. }
  111. F32 _calcDepthWeight(sampler2D depthLow, Vec2 uv, F32 ref, Vec2 linearDepthCf)
  112. {
  113. F32 d = texture(depthLow, uv).r;
  114. F32 linearD = linearizeDepthOptimal(d, linearDepthCf.x, linearDepthCf.y);
  115. return 1.0 / (EPSILON + abs(ref - linearD));
  116. }
  117. Vec4 _sampleAndWeight(sampler2D depthLow,
  118. sampler2D colorLow,
  119. Vec2 lowInvSize,
  120. Vec2 uv,
  121. Vec2 offset,
  122. F32 ref,
  123. F32 weight,
  124. Vec2 linearDepthCf,
  125. inout F32 normalize)
  126. {
  127. uv += offset * lowInvSize;
  128. F32 dw = _calcDepthWeight(depthLow, uv, ref, linearDepthCf);
  129. Vec4 v = texture(colorLow, uv);
  130. normalize += weight * dw;
  131. return v * dw * weight;
  132. }
  133. Vec4 bilateralUpsample(
  134. sampler2D depthHigh, sampler2D depthLow, sampler2D colorLow, Vec2 lowInvSize, Vec2 uv, Vec2 linearDepthCf)
  135. {
  136. const Vec3 WEIGHTS = Vec3(0.25, 0.125, 0.0625);
  137. F32 depthRef = linearizeDepthOptimal(texture(depthHigh, uv).r, linearDepthCf.x, linearDepthCf.y);
  138. F32 normalize = 0.0;
  139. Vec4 sum = _sampleAndWeight(
  140. depthLow, colorLow, lowInvSize, uv, Vec2(0.0, 0.0), depthRef, WEIGHTS.x, linearDepthCf, normalize);
  141. sum += _sampleAndWeight(
  142. depthLow, colorLow, lowInvSize, uv, Vec2(-1.0, 0.0), depthRef, WEIGHTS.y, linearDepthCf, normalize);
  143. sum += _sampleAndWeight(
  144. depthLow, colorLow, lowInvSize, uv, Vec2(0.0, -1.0), depthRef, WEIGHTS.y, linearDepthCf, normalize);
  145. sum += _sampleAndWeight(
  146. depthLow, colorLow, lowInvSize, uv, Vec2(1.0, 0.0), depthRef, WEIGHTS.y, linearDepthCf, normalize);
  147. sum += _sampleAndWeight(
  148. depthLow, colorLow, lowInvSize, uv, Vec2(0.0, 1.0), depthRef, WEIGHTS.y, linearDepthCf, normalize);
  149. sum += _sampleAndWeight(
  150. depthLow, colorLow, lowInvSize, uv, Vec2(1.0, 1.0), depthRef, WEIGHTS.z, linearDepthCf, normalize);
  151. sum += _sampleAndWeight(
  152. depthLow, colorLow, lowInvSize, uv, Vec2(1.0, -1.0), depthRef, WEIGHTS.z, linearDepthCf, normalize);
  153. sum += _sampleAndWeight(
  154. depthLow, colorLow, lowInvSize, uv, Vec2(-1.0, 1.0), depthRef, WEIGHTS.z, linearDepthCf, normalize);
  155. sum += _sampleAndWeight(
  156. depthLow, colorLow, lowInvSize, uv, Vec2(-1.0, -1.0), depthRef, WEIGHTS.z, linearDepthCf, normalize);
  157. return sum / normalize;
  158. }
  159. Vec3 getCubemapDirection(Vec2 norm, U32 faceIdx)
  160. {
  161. Vec3 zDir = Vec3((faceIdx <= 1u) ? 1 : 0, (faceIdx & 2u) >> 1u, (faceIdx & 4u) >> 2u);
  162. zDir *= (((faceIdx & 1u) == 1u) ? -1.0 : 1.0);
  163. Vec3 yDir = (faceIdx == 2u) ? Vec3(0.0, 0.0, 1.0) : (faceIdx == 3u) ? Vec3(0.0, 0.0, -1.0) : Vec3(0.0, -1.0, 0.0);
  164. Vec3 xDir = cross(zDir, yDir);
  165. return normalize(norm.x * xDir + norm.y * yDir + zDir);
  166. }
  167. // Convert 3D cubemap coordinates to 2D plus face index. v doesn't need to be normalized.
  168. Vec2 convertCubeUvs(Vec3 v, out F32 faceIndex)
  169. {
  170. Vec3 absV = abs(v);
  171. F32 mag;
  172. Vec2 uv;
  173. if(all(greaterThanEqual(absV.zz, absV.xy)))
  174. {
  175. faceIndex = (v.z < 0.0) ? 5.0 : 4.0;
  176. uv = Vec2((v.z < 0.0) ? -v.x : v.x, -v.y);
  177. mag = absV.z;
  178. }
  179. else if(absV.y >= absV.x)
  180. {
  181. faceIndex = (v.y < 0.0) ? 3.0 : 2.0;
  182. uv = Vec2(v.x, (v.y < 0.0) ? -v.z : v.z);
  183. mag = absV.y;
  184. }
  185. else
  186. {
  187. faceIndex = (v.x < 0.0) ? 1.0 : 0.0;
  188. uv = Vec2((v.x < 0.0) ? v.z : -v.z, -v.y);
  189. mag = absV.x;
  190. }
  191. return 0.5 / mag * uv + 0.5;
  192. }
  193. // Same as convertCubeUvs but it returns the faceIndex as unsigned I32.
  194. Vec2 convertCubeUvsu(Vec3 v, out U32 faceIndex)
  195. {
  196. Vec3 absV = abs(v);
  197. F32 mag;
  198. Vec2 uv;
  199. if(all(greaterThanEqual(absV.zz, absV.xy)))
  200. {
  201. faceIndex = (v.z < 0.0) ? 5u : 4u;
  202. uv = Vec2((v.z < 0.0) ? -v.x : v.x, -v.y);
  203. mag = absV.z;
  204. }
  205. else if(absV.y >= absV.x)
  206. {
  207. faceIndex = (v.y < 0.0) ? 3u : 2u;
  208. uv = Vec2(v.x, (v.y < 0.0) ? -v.z : v.z);
  209. mag = absV.y;
  210. }
  211. else
  212. {
  213. faceIndex = (v.x < 0.0) ? 1u : 0u;
  214. uv = Vec2((v.x < 0.0) ? v.z : -v.z, -v.y);
  215. mag = absV.x;
  216. }
  217. return 0.5 / mag * uv + 0.5;
  218. }
  219. Vec3 grayScale(Vec3 col)
  220. {
  221. F32 grey = (col.r + col.g + col.b) * (1.0 / 3.0);
  222. return Vec3(grey);
  223. }
  224. Vec3 saturateColor(Vec3 col, F32 factor)
  225. {
  226. const Vec3 LUM_COEFF = Vec3(0.2125, 0.7154, 0.0721);
  227. Vec3 intensity = Vec3(dot(col, LUM_COEFF));
  228. return mix(intensity, col, factor);
  229. }
  230. Vec3 gammaCorrection(Vec3 gamma, Vec3 col)
  231. {
  232. return pow(col, 1.0 / gamma);
  233. }
  234. // Can use 0.15 for sharpenFactor
  235. Vec3 readSharpen(sampler2D tex, Vec2 uv, F32 sharpenFactor, Bool detailed)
  236. {
  237. Vec3 col = textureLod(tex, uv, 0.0).rgb;
  238. Vec3 col2 = textureLodOffset(tex, uv, 0.0, IVec2(1, 1)).rgb;
  239. col2 += textureLodOffset(tex, uv, 0.0, IVec2(-1, -1)).rgb;
  240. col2 += textureLodOffset(tex, uv, 0.0, IVec2(1, -1)).rgb;
  241. col2 += textureLodOffset(tex, uv, 0.0, IVec2(-1, 1)).rgb;
  242. F32 f = 4.0;
  243. if(detailed)
  244. {
  245. col2 += textureLodOffset(tex, uv, 0.0, IVec2(0, 1)).rgb;
  246. col2 += textureLodOffset(tex, uv, 0.0, IVec2(1, 0)).rgb;
  247. col2 += textureLodOffset(tex, uv, 0.0, IVec2(-1, 0)).rgb;
  248. col2 += textureLodOffset(tex, uv, 0.0, IVec2(0, -1)).rgb;
  249. f = 8.0;
  250. }
  251. col = col * (f * sharpenFactor + 1.0) - sharpenFactor * col2;
  252. return max(Vec3(0.0), col);
  253. }
  254. Vec3 readErosion(sampler2D tex, Vec2 uv)
  255. {
  256. Vec3 minValue = textureLod(tex, uv, 0.0).rgb;
  257. #define ANKI_EROSION(x, y) \
  258. col2 = textureLodOffset(tex, uv, 0.0, IVec2(x, y)).rgb; \
  259. minValue = min(col2, minValue);
  260. Vec3 col2;
  261. ANKI_EROSION(1, 1);
  262. ANKI_EROSION(-1, -1);
  263. ANKI_EROSION(1, -1);
  264. ANKI_EROSION(-1, 1);
  265. ANKI_EROSION(0, 1);
  266. ANKI_EROSION(1, 0);
  267. ANKI_EROSION(-1, 0);
  268. ANKI_EROSION(0, -1);
  269. #undef ANKI_EROSION
  270. return minValue;
  271. }
  272. // 5 color heatmap from a factor.
  273. Vec3 heatmap(F32 factor)
  274. {
  275. F32 intPart;
  276. F32 fractional = modf(factor * 4.0, intPart);
  277. if(intPart < 1.0)
  278. {
  279. return mix(Vec3(0.0, 0.0, 0.0), Vec3(0.0, 0.0, 1.0), fractional);
  280. }
  281. else if(intPart < 2.0)
  282. {
  283. return mix(Vec3(0.0, 0.0, 1.0), Vec3(0.0, 1.0, 0.0), fractional);
  284. }
  285. else if(intPart < 3.0)
  286. {
  287. return mix(Vec3(0.0, 1.0, 0.0), Vec3(1.0, 1.0, 0.0), fractional);
  288. }
  289. else
  290. {
  291. return mix(Vec3(1.0, 1.0, 0.0), Vec3(1.0, 0.0, 0.0), fractional);
  292. }
  293. }
  294. Bool incorrectColor(Vec3 c)
  295. {
  296. return isnan(c.x) || isnan(c.y) || isnan(c.z) || isinf(c.x) || isinf(c.y) || isinf(c.z);
  297. }
  298. F32 areaElement(F32 x, F32 y)
  299. {
  300. return atan(x * y, sqrt(x * x + y * y + 1.0));
  301. }
  302. // Compute the solid angle of a cube. Solid angle is the area of a sphere when projected into a cubemap. It's also the
  303. // delta omega (dω) in the irradiance integral and other integrals that operate in a sphere.
  304. // http://www.rorydriscoll.com/2012/01/15/cubemap-texel-solid-angle/
  305. F32 cubeCoordSolidAngle(Vec2 norm, F32 cubeFaceSize)
  306. {
  307. Vec2 invSize = Vec2(1.0 / cubeFaceSize);
  308. Vec2 v0 = norm - invSize;
  309. Vec2 v1 = norm + invSize;
  310. return areaElement(v0.x, v0.y) - areaElement(v0.x, v1.y) - areaElement(v1.x, v0.y) + areaElement(v1.x, v1.y);
  311. }
  312. // Intersect a ray against an AABB. The ray is inside the AABB. The function returns the distance 'a' where the
  313. // intersection point is rayOrigin + rayDir * a
  314. // https://community.arm.com/graphics/b/blog/posts/reflections-based-on-local-cubemaps-in-unity
  315. F32 rayAabbIntersectionInside(Vec3 rayOrigin, Vec3 rayDir, Vec3 aabbMin, Vec3 aabbMax)
  316. {
  317. Vec3 intersectMaxPointPlanes = (aabbMax - rayOrigin) / rayDir;
  318. Vec3 intersectMinPointPlanes = (aabbMin - rayOrigin) / rayDir;
  319. Vec3 largestParams = max(intersectMaxPointPlanes, intersectMinPointPlanes);
  320. F32 distToIntersect = min(min(largestParams.x, largestParams.y), largestParams.z);
  321. return distToIntersect;
  322. }
  323. // A convenience macro to skip out of bounds invocations on post-process compute shaders.
  324. #define SKIP_OUT_OF_BOUNDS_INVOCATIONS() \
  325. if((FB_SIZE.x % WORKGROUP_SIZE.x) != 0u || (FB_SIZE.y % WORKGROUP_SIZE.y) != 0u) \
  326. { \
  327. if(gl_GlobalInvocationID.x >= FB_SIZE.x || gl_GlobalInvocationID.y >= FB_SIZE.y) \
  328. { \
  329. return; \
  330. } \
  331. }