Advertisement
Jiggel

Temporal Anti-Aliasing GLSL Shader

Feb 21st, 2019
499
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
  1. #version 420 core
  2. //Hehe 420
  3.  
  4. /*
  5. MIT License
  6.  
  7. Copyright (c) [2019] [Feodor Volguine]
  8.  
  9. Permission is hereby granted, free of charge, to any person obtaining a copy
  10. of this software and associated documentation files (the "Software"), to deal
  11. in the Software without restriction, including without limitation the rights
  12. to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
  13. copies of the Software, and to permit persons to whom the Software is
  14. furnished to do so, subject to the following conditions:
  15.  
  16. The above copyright notice and this permission notice shall be included in all
  17. copies or substantial portions of the Software.
  18.  
  19. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
  20. IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
  21. FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
  22. AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
  23. LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
  24. OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
  25. SOFTWARE.
  26. */
  27.  
  28. #define PI 3.14159265359f
  29. #define EPSILON 0.000001f
  30. #define LUMA_COEFFICIENTS vec3(0.2126f, 0.7152f, 0.0722f)
  31. //Feel free to play around with these values. My jitter sequence has 16 elements so I use 1 - (1 / 16) = 0.9375 for the max weight
  32. #define WEIGHT_MINIMUM 0.8f
  33. #define WEIGHT_MAXIMUM 0.9375f
  34.  
  35. layout(location = 0) out vec4 FragColor;
  36.  
  37. in vec2 texCoordsFrag;
  38.  
  39. //Camera near and far plane used to linearize depth
  40. uniform float nearPlane;
  41. uniform float farPlane;
  42. //Input texture from current frame (it's called low-res because I am using this algorithm for supersampling)
  43. uniform sampler2D lowResTexture;
  44. //Result from previous frame
  45. uniform sampler2D previousLowResTexture;
  46. //Non-linear depth
  47. uniform sampler2D depthTexture;
  48. //Encoded normals and velocity
  49. uniform sampler2D normalVelocity;
  50.  
  51. float LinearizeDepth(float inputDepth) { return (2.0f * nearPlane) / (farPlane + nearPlane - inputDepth * (farPlane - nearPlane)); }
  52. float ComputeLuminance(vec3 rgb) { return dot(LUMA_COEFFICIENTS, rgb); }
  53. //https://software.intel.com/en-us/node/503873
  54. vec3  RGBtoYCOCG(vec3 c)
  55. { return vec3(c.x / 4.0f + c.y / 2.0f + c.z / 4.0f, c.x / 2.0f - c.z / 2.0f, -c.x / 4.0f + c.y / 2.0f - c.z / 4.0f); }
  56. vec3  YCOCGtoRGB(vec3 c)
  57. { return clamp(vec3(c.x + c.y - c.z, c.x + c.z, c.x - c.y - c.z), 0.0f, 1.0f); }
  58. //https://gpuopen.com/optimized-reversible-tonemapper-for-resolve/
  59. vec3  Tonemap(vec3 c) { return c / (max(c.r, max(c.g, c.b)) + 1.0f); }
  60. vec3  InverseTonemap(vec3 c) { return c / (1.0f - max(c.r, max(c.g, c.b))); }
  61. float BlackmanHarris(float x);
  62. vec2  FetchClosestInverseVelocity(vec2 texCoords);
  63. vec4  Reproject(sampler2D previousInputTexture, vec2 previousTexCoords);
  64. void  VarianceAABB(inout vec3 aabbMin, inout vec3 aabbMax, vec3 point, vec3 mu, vec3 mu2);
  65. void  TightenAABBchroma(inout vec3 mu, inout vec3 mu2, inout vec3 aabbMin, inout vec3 aabbMax, vec3 currentPixelValue);
  66. vec4  ClipAABB(vec3 neighborhoodMin, vec3 neighborhoodMax, vec4 currentValue, vec4 previousValue);
  67. vec4  FetchSampleValue(sampler2D inputTexture, vec2 texCoords);
  68. float UnbiasedLuminanceWeight(float currentLuminance, float previousLuminance, vec3 aabbMax);
  69. vec4  TemporalResolve(sampler2D inputTexture, sampler2D previousInputTexture);
  70.  
  71. vec2 resolutionInput  = textureSize(lowResTexture,  0);
  72. vec2 resolutionOutput = textureSize(normalVelocity, 0);
  73. //Replace these with uniforms because division is slow
  74. vec2 texelSizeInput  = 1.0f / resolutionInput;
  75. vec2 texelSizeOutput = 1.0f / resolutionOutput;
  76.  
  77. void main()
  78. {
  79.     vec4 result = TemporalResolve(lowResTexture, previousLowResTexture);
  80.     //result.a = texture(lowResTexture, texCoordsFrag).a;
  81.    
  82.     FragColor = result;
  83. }
  84.  
  85. float BlackmanHarris(float x)
  86. {
  87.     x = 1.0f - x;
  88.  
  89.     float a0 = 0.35875f;
  90.     float a1 = 0.48829f;
  91.     float a2 = 0.14128f;
  92.     float a3 = 0.01168f;
  93.     return clamp(a0 - a1 * cos(PI * x) + a2 * cos(2 * PI * x) - a3 * cos(3 * PI * x), 0.0f, 1.0f);
  94. }
  95.  
  96. vec4 FetchSampleValue(sampler2D inputTexture, vec2 texCoords)
  97. {
  98.     vec4 value = texture(inputTexture, texCoords);
  99.     value.xyz  = Tonemap(value.xyz);
  100.     value.xyz  = RGBtoYCOCG(value.xyz);
  101.     return value;
  102. }
  103.  
  104. float UnbiasedLuminanceWeight(float currentLuminance, float previousLuminance, vec3 aabbMax)
  105. {
  106.     float unbiasedLumaDifference = abs(currentLuminance - previousLuminance) / (max(currentLuminance, max(previousLuminance, ComputeLuminance(aabbMax))) + EPSILON);
  107.    
  108.     float weight = 1.0f - unbiasedLumaDifference;
  109.     weight       = weight * weight;
  110.     weight       = mix(WEIGHT_MINIMUM, WEIGHT_MAXIMUM, weight);
  111.     return weight;
  112. }
  113.  
  114. //https://developer.download.nvidia.com/gameworks/events/GDC2016/msalvi_temporal_supersampling.pdf
  115. void VarianceAABB(inout vec3 aabbMin, inout vec3 aabbMax, vec3 point, vec3 mu, vec3 mu2)
  116. {
  117.     //Standard deviation sigma
  118.     vec3 sigma = sqrt(abs(mu2 - mu * mu));
  119.     //Larger gamma is more stable but has more ghosting (values between 0.75 and 1.25 work well)
  120.     const float gamma = 1.0f;
  121.  
  122.     //Create AABB centered around mu
  123.     vec3 newAABBmin = mu - gamma * sigma;
  124.     vec3 newAABBmax = mu + gamma * sigma;
  125.     //Clamp against old AABB
  126.     aabbMin = max(newAABBmin, aabbMin);
  127.     aabbMax = min(newAABBmax, aabbMax);
  128. }
  129.  
  130. //https://github.com/playdeadgames/temporal/blob/master/Assets/Shaders/TemporalReprojection.shader
  131. void TightenAABBchroma(inout vec3 mu, inout vec3 mu2, inout vec3 aabbMin, inout vec3 aabbMax, vec3 currentPixelValue)
  132. {
  133.     vec2 chromaExtent  = vec2(0.25f * 0.5f * (aabbMax.x - aabbMin.x));
  134.     vec2 chromaCenter  = currentPixelValue.yz;
  135.  
  136.     mu.yz      = chromaCenter;
  137.     //Not sure how to shrink mu2 (squaring center chroma doesnt seem to be correct)
  138.     //mu2.yz     = chromaCenter * chromaCenter;
  139.     aabbMin.yz = chromaCenter - chromaExtent;
  140.     aabbMax.yz = chromaCenter + chromaExtent;
  141. }
  142.  
  143. //https://github.com/playdeadgames/temporal/blob/master/Assets/Shaders/TemporalReprojection.shader
  144. vec3 ClipAABB(vec3 aabbMin, vec3 aabbMax, vec3 point)
  145. {
  146.     //Clips towards AABB center for better perfomance
  147.     vec3 center   = 0.5f * (aabbMax + aabbMin);
  148.     vec3 halfSize = 0.5f * (aabbMax - aabbMin) + EPSILON;
  149.     //Relative position from the center
  150.     vec3 clip     = point - center;
  151.     //Normalize relative position
  152.     vec3 unit     = clip / halfSize;
  153.     vec3 absUnit  = abs(unit);
  154.     float maxUnit = max(absUnit.x, max(absUnit.y, absUnit.z));
  155.     if(maxUnit > 1.0f)
  156.         return center + clip / maxUnit;
  157.     else
  158.         return point;
  159. }
  160.  
  161. //Obtains closest inverse velocity in 3x3 neighborhood
  162. //NOTE: Using longest velocity instead of closest could be better
  163. vec2 FetchClosestInverseVelocity(vec2 texCoords, vec2 texelSize)
  164. {
  165.     float closestDepth     = 1.0f;
  166.     vec2  closestTexCoords = vec2(0.0f);
  167.  
  168.     //Search 3x3 neighborhood
  169.     for(int i = -1; i <= 1; ++i)
  170.     {
  171.         for(int j = -1; j <= 1; ++j)
  172.         {
  173.             vec2  currentTexCoords = texCoords + (vec2(i, j) * texelSize);
  174.             float currentDepth     = LinearizeDepth(texture(depthTexture, currentTexCoords).x);
  175.             if(currentDepth < closestDepth)
  176.             {
  177.                 closestDepth     = currentDepth;
  178.                 closestTexCoords = currentTexCoords;
  179.             }
  180.         }
  181.     }
  182.  
  183.     return -texture(normalVelocity, closestTexCoords).zw;
  184. }
  185.  
  186. vec4 Reproject(sampler2D previousInputTexture, vec2 previousTexCoords)
  187. {
  188.     vec4 previousPixelValue = FetchSampleValue(previousInputTexture, previousTexCoords);
  189.     if(true)
  190.         return previousPixelValue;
  191.     //This doesn't work yet.
  192.     else
  193.     {
  194.         vec4  valueTotal  = vec4(0.0f);
  195.         float weightTotal = 0.0f;
  196.  
  197.         for(int i = -1; i <= 1; i++)
  198.         {
  199.             for(int j = -1; j <= 1; j++)
  200.             {
  201.                 vec2  sampleOffset   = vec2(j, i);
  202.                 vec2  sampleCoords   = previousTexCoords + (sampleOffset * texelSizeOutput);
  203.                 vec4  sampleValue    = FetchSampleValue(previousInputTexture, sampleCoords);
  204.                 vec2  sampleDistance = abs(sampleOffset);
  205.                 float weightSample   = BlackmanHarris(sampleDistance.x) * BlackmanHarris(sampleDistance.y);
  206.  
  207.                 valueTotal  += sampleValue * weightSample;
  208.                 weightTotal += weightSample;
  209.             }
  210.         }
  211.  
  212.         return max(valueTotal / weightTotal, 0.0f);
  213.     }
  214. }
  215.  
  216. vec4 TemporalResolve(sampler2D inputTexture, sampler2D previousInputTexture)
  217. {
  218.     //Value of current pixel
  219.     vec4 currentPixelValue = FetchSampleValue(lowResTexture, texCoordsFrag);
  220.     //Screen space velocity
  221.     vec2 velocity = FetchClosestInverseVelocity(texCoordsFrag, texelSizeOutput);
  222.  
  223.     //Texture coordinate reprojection
  224.     vec2 previousTexCoords  = texCoordsFrag + velocity;
  225.     vec4 previousPixelValue = Reproject(previousInputTexture, previousTexCoords);
  226.  
  227.     vec2 texelSizeInputX = vec2(texelSizeInput.x, 0.0f);
  228.     vec2 texelSizeInputY = vec2(0.0f, texelSizeInput.y);
  229.  
  230.     //Sample 3x3 neighborhood around current pixel
  231.     vec4 nbrSamples[9];
  232.     nbrSamples[0] = FetchSampleValue(inputTexture, texCoordsFrag - texelSizeInput);
  233.     nbrSamples[1] = FetchSampleValue(inputTexture, texCoordsFrag - texelSizeInputY);
  234.     nbrSamples[2] = FetchSampleValue(inputTexture, texCoordsFrag + texelSizeInputX - texelSizeInputY);
  235.     nbrSamples[3] = FetchSampleValue(inputTexture, texCoordsFrag - texelSizeInputX);
  236.     nbrSamples[4] = currentPixelValue;
  237.     nbrSamples[5] = FetchSampleValue(inputTexture, texCoordsFrag + texelSizeInputX);
  238.     nbrSamples[6] = FetchSampleValue(inputTexture, texCoordsFrag - texelSizeInputX + texelSizeInputY);
  239.     nbrSamples[7] = FetchSampleValue(inputTexture, texCoordsFrag + texelSizeInputY);
  240.     nbrSamples[8] = FetchSampleValue(inputTexture, texCoordsFrag + texelSizeInput);
  241.  
  242.     vec4 neighborhoodMoment  = vec4(0.0f);
  243.     vec4 neighborhoodMoment2 = vec4(0.0f);
  244.     for(uint i = 0; i < 9; i++)
  245.     {
  246.         neighborhoodMoment  += nbrSamples[i];
  247.         neighborhoodMoment2 += nbrSamples[i] * nbrSamples[i];
  248.     }
  249.  
  250.     vec4 neighborhoodMu  = neighborhoodMoment  / 9.0f;
  251.     vec4 neighborhoodMu2 = neighborhoodMoment2 / 9.0f;
  252.     vec4 neighborhoodMin = nbrSamples[0];
  253.     vec4 neighborhoodMax = nbrSamples[0];
  254.     for(uint i = 1; i < 9; i++)
  255.     {
  256.         neighborhoodMin = min(neighborhoodMin, nbrSamples[i]);
  257.         neighborhoodMax = max(neighborhoodMax, nbrSamples[i]);
  258.     }
  259.    
  260.     //Cross (+) shape neighborhood values
  261.     vec4 neighborhoodCrossMoment  = nbrSamples[1] + nbrSamples[3] + nbrSamples[4] + nbrSamples[5] + nbrSamples[7];
  262.     vec4 neighborhoodCrossMoment2 = nbrSamples[1] * nbrSamples[1] + nbrSamples[3] * nbrSamples[3] + nbrSamples[4] * nbrSamples[4] + nbrSamples[5] * nbrSamples[5] + nbrSamples[7] * nbrSamples[7];
  263.     vec4 neighborhoodCrossMu      = neighborhoodCrossMoment  / 5.0f;
  264.     vec4 neighborhoodCrossMu2     = neighborhoodCrossMoment2 / 5.0f;
  265.     vec4 neighborhoodCrossMin     = min(nbrSamples[1], min(nbrSamples[3], min(nbrSamples[4], min(nbrSamples[5], nbrSamples[7]))));
  266.     vec4 neighborhoodCrossMax     = max(nbrSamples[1], max(nbrSamples[3], max(nbrSamples[4], max(nbrSamples[5], nbrSamples[7]))));
  267.  
  268.     //https://de45xmedrsdbp.cloudfront.net/Resources/files/TemporalAA_small-59732822.pdf Slide 32
  269.     //Blend cross neighborhood values and 3x3 neighborhood values to get soft corners
  270.     //NOTE: Taking a simple average of mu's may not be the mathematically correct way to blend them
  271.     neighborhoodMu  = (neighborhoodMu  + neighborhoodCrossMu)  * 0.5f;
  272.     neighborhoodMu2 = (neighborhoodMu2 + neighborhoodCrossMu2) * 0.5f;
  273.     neighborhoodMin = (neighborhoodMin + neighborhoodCrossMin) * 0.5f;
  274.     neighborhoodMax = (neighborhoodMax + neighborhoodCrossMax) * 0.5f;
  275.  
  276.     //Clip previous pixel value to current local neighborhood's color space AABB
  277.     //Chrominance tightening disabled until a solution for shrinking mu2 is found. Though maybe it's not necessary and tightening only the min/max is better?
  278.     #if(false)
  279.     TightenAABBchroma(neighborhoodMu.xyz, neighborhoodMu2.xyz, neighborhoodMin.xyz, neighborhoodMax.xyz, currentPixelValue.xyz);
  280.     #endif
  281.     //NOTE: Filtering the color moments (eg. with separable guassian, similar to exponential shadow maps) and using them to generate AABB could be better
  282.     VarianceAABB(neighborhoodMin.xyz, neighborhoodMax.xyz, previousPixelValue.xyz, neighborhoodMu.xyz, neighborhoodMu2.xyz);
  283.     previousPixelValue.xyz = ClipAABB(neighborhoodMin.xyz, neighborhoodMax.xyz, previousPixelValue.xyz);
  284.  
  285.     float modulationWeight = UnbiasedLuminanceWeight(currentPixelValue.x, previousPixelValue.x, neighborhoodMax.xyz);
  286.  
  287.     //TODO: Detect disocclusion: minimum depth in a 3x3 pixel neighborhood of the current frame is compared with the maximum depth in a corresponding
  288.     //2x2 pixel region in the previous frame. A disocclusion event is detected when the current depth exceeds the previous depth
  289.  
  290.     //Fallback to current image if reprojected coordinates are outside screen
  291.     if(any(greaterThan(previousTexCoords, vec2(1.0f)) || lessThan(previousTexCoords, vec2(0.0f))))
  292.         modulationWeight = 0.0f;
  293.  
  294.     vec4 resultValue = mix(currentPixelValue, previousPixelValue, modulationWeight);
  295.  
  296.     resultValue.rgb = YCOCGtoRGB(resultValue.xyz);
  297.     resultValue.rgb = InverseTonemap(resultValue.rgb);
  298.     return resultValue;
  299. }
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement