overte-HifiExperiments/libraries/render-utils/src/taa.slh
2018-08-03 14:58:11 -07:00

523 lines
17 KiB
Text

// Generated on <$_SCRIBE_DATE$>
//
// TAA.slh
// Common component needed by TemporalAntialiasing fragment shader
//
// Created by Sam Gateau on 8/17/2017
// Copyright 2017 High Fidelity, Inc.
//
// Distributed under the Apache License, Version 2.0.
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
//
<@include DeferredTransform.slh@>
<$declareDeferredFrameTransform()$>
<@include render-utils/ShaderConstants.h@>
<@include gpu/Color.slh@>
layout(binding=RENDER_UTILS_TEXTURE_TAA_HISTORY) uniform sampler2D historyMap;
layout(binding=RENDER_UTILS_TEXTURE_TAA_SOURCE) uniform sampler2D sourceMap;
layout(binding=RENDER_UTILS_TEXTURE_TAA_VELOCITY) uniform sampler2D velocityMap;
layout(binding=RENDER_UTILS_TEXTURE_TAA_DEPTH) uniform sampler2D depthMap;
layout(binding=RENDER_UTILS_TEXTURE_TAA_NEXT) uniform sampler2D nextMap;
struct TAAParams
{
float none;
float blend;
float covarianceGamma;
float debugShowVelocityThreshold;
ivec4 flags;
vec4 pixelInfo_orbZoom;
vec4 regionInfo;
};
layout(std140, binding=RENDER_UTILS_BUFFER_TAA_PARAMS) uniform taaParamsBuffer {
TAAParams params;
};
#define GET_BIT(bitfield, bitIndex) bool((bitfield) & (1 << (bitIndex)))
bool taa_isDebugEnabled() {
return GET_BIT(params.flags.x, 0);
}
bool taa_showDebugCursor() {
return GET_BIT(params.flags.x, 1);
}
bool taa_showClosestFragment() {
return GET_BIT(params.flags.x, 3);
}
bool taa_constrainColor() {
return GET_BIT(params.flags.y, 1);
}
bool taa_feedbackColor() {
return GET_BIT(params.flags.y, 4);
}
vec2 taa_getDebugCursorTexcoord() {
return params.pixelInfo_orbZoom.xy;
}
float taa_getDebugOrbZoom() {
return params.pixelInfo_orbZoom.z;
}
vec2 taa_getRegionDebug() {
return params.regionInfo.xy;
}
vec2 taa_getRegionFXAA() {
return params.regionInfo.zw;
}
#define USE_YCOCG 1
vec4 taa_fetchColor(sampler2D map, vec2 uv) {
vec4 c = texture(map, uv);
// Apply rapid pseudo tonemapping as TAA is applied to a tonemapped image, using luminance as weight, as proposed in
// https://de45xmedrsdbp.cloudfront.net/Resources/files/TemporalAA_small-59732822.pdf
float lum = dot(vec3(0.3,0.5,0.2),c.rgb);
c.rgb = c.rgb / (1.0+lum);
#if USE_YCOCG
return vec4(color_LinearToYCoCg(c.rgb), c.a);
#else
return c;
#endif
}
vec3 taa_resolveColor(vec3 color) {
#if USE_YCOCG
color = max(vec3(0), color_YCoCgToUnclampedLinear(color));
#endif
// Apply rapid inverse tonemapping, using luminance as weight, as proposed in
// https://de45xmedrsdbp.cloudfront.net/Resources/files/TemporalAA_small-59732822.pdf
float lum = dot(vec3(0.3,0.5,0.2),color.rgb);
color = color / (1.0-lum);
return color;
}
vec4 taa_fetchSourceMap(vec2 uv) {
return taa_fetchColor(sourceMap, uv);
}
vec4 taa_fetchHistoryMap(vec2 uv) {
return taa_fetchColor(historyMap, uv);
}
vec4 taa_fetchNextMap(vec2 uv) {
return taa_fetchColor(nextMap, uv);
}
vec2 taa_fetchVelocityMap(vec2 uv) {
return texture(velocityMap, uv).xy;
}
float taa_fetchDepth(vec2 uv) {
return -texture(depthMap, vec2(uv), 0).x;
}
#define ZCMP_GT(a, b) (a > b)
vec2 taa_getImageSize() {
vec2 imageSize = getWidthHeight(0);
if (isStereo()) {
imageSize.x *= 2.0;
}
return imageSize;
}
vec2 taa_getTexelSize() {
vec2 texelSize = getInvWidthHeight();
if (isStereo()) {
texelSize.x *= 0.5;
}
return texelSize;
}
vec3 taa_findClosestFragment3x3(vec2 uv)
{
vec2 dd = abs(taa_getTexelSize());
vec2 du = vec2(dd.x, 0.0);
vec2 dv = vec2(0.0, dd.y);
vec3 dtl = vec3(-1, -1, taa_fetchDepth(uv - dv - du));
vec3 dtc = vec3( 0, -1, taa_fetchDepth(uv - dv));
vec3 dtr = vec3( 1, -1, taa_fetchDepth(uv - dv + du));
vec3 dml = vec3(-1, 0, taa_fetchDepth(uv - du));
vec3 dmc = vec3( 0, 0, taa_fetchDepth(uv));
vec3 dmr = vec3( 1, 0, taa_fetchDepth(uv + du));
vec3 dbl = vec3(-1, 1, taa_fetchDepth(uv + dv - du));
vec3 dbc = vec3( 0, 1, taa_fetchDepth(uv + dv));
vec3 dbr = vec3( 1, 1, taa_fetchDepth(uv + dv + du));
vec3 dmin = dtl;
if (ZCMP_GT(dmin.z, dtc.z)) dmin = dtc;
if (ZCMP_GT(dmin.z, dtr.z)) dmin = dtr;
if (ZCMP_GT(dmin.z, dml.z)) dmin = dml;
if (ZCMP_GT(dmin.z, dmc.z)) dmin = dmc;
if (ZCMP_GT(dmin.z, dmr.z)) dmin = dmr;
if (ZCMP_GT(dmin.z, dbl.z)) dmin = dbl;
if (ZCMP_GT(dmin.z, dbc.z)) dmin = dbc;
if (ZCMP_GT(dmin.z, dbr.z)) dmin = dbr;
return vec3(uv + dd.xy * dmin.xy, dmin.z);
}
vec2 taa_fetchVelocityMapBest(vec2 uv) {
vec2 dd = abs(taa_getTexelSize());
vec2 du = vec2(dd.x, 0.0);
vec2 dv = vec2(0.0, dd.y);
vec2 dtl = taa_fetchVelocityMap(uv - dv - du);
vec2 dtc = taa_fetchVelocityMap(uv - dv);
vec2 dtr = taa_fetchVelocityMap(uv - dv + du);
vec2 dml = taa_fetchVelocityMap(uv - du);
vec2 dmc = taa_fetchVelocityMap(uv);
vec2 dmr = taa_fetchVelocityMap(uv + du);
vec2 dbl = taa_fetchVelocityMap(uv + dv - du);
vec2 dbc = taa_fetchVelocityMap(uv + dv);
vec2 dbr = taa_fetchVelocityMap(uv + dv + du);
vec3 best = vec3(dtl, dot(dtl,dtl));
float testSpeed = dot(dtc,dtc);
if (testSpeed > best.z) { best = vec3(dtc, testSpeed); }
testSpeed = dot(dtr,dtr);
if (testSpeed > best.z) { best = vec3(dtr, testSpeed); }
testSpeed = dot(dml,dml);
if (testSpeed > best.z) { best = vec3(dml, testSpeed); }
testSpeed = dot(dmc,dmc);
if (testSpeed > best.z) { best = vec3(dmc, testSpeed); }
testSpeed = dot(dmr,dmr);
if (testSpeed > best.z) { best = vec3(dmr, testSpeed); }
testSpeed = dot(dbl,dbl);
if (testSpeed > best.z) { best = vec3(dbl, testSpeed); }
testSpeed = dot(dbc,dbc);
if (testSpeed > best.z) { best = vec3(dbc, testSpeed); }
testSpeed = dot(dbr,dbr);
if (testSpeed > best.z) { best = vec3(dbr, testSpeed); }
return best.xy;
}
vec2 taa_fromFragUVToEyeUVAndSide(vec2 fragUV, out int stereoSide) {
vec2 eyeUV = fragUV;
stereoSide = 0;
if (isStereo()) {
if (eyeUV.x > 0.5) {
eyeUV.x -= 0.5;
stereoSide = 1;
}
eyeUV.x *= 2.0;
}
return eyeUV;
}
vec2 taa_fromEyeUVToFragUV(vec2 eyeUV, int stereoSide) {
vec2 fragUV = eyeUV;
if (isStereo()) {
fragUV.x *= 0.5;
fragUV.x += stereoSide*0.5;
}
return fragUV;
}
vec2 taa_computePrevFragAndEyeUV(vec2 fragUV, vec2 fragVelocity, out vec2 prevEyeUV) {
int stereoSide = 0;
vec2 eyeUV = taa_fromFragUVToEyeUVAndSide(fragUV, stereoSide);
prevEyeUV = eyeUV - fragVelocity;
return taa_fromEyeUVToFragUV(prevEyeUV, stereoSide);
}
vec2 taa_fetchSourceAndHistory(vec2 fragUV, vec2 fragVelocity, out vec3 sourceColor, out vec3 historyColor) {
vec2 prevEyeUV;
vec2 prevFragUV = taa_computePrevFragAndEyeUV(fragUV, fragVelocity, prevEyeUV);
sourceColor = taa_fetchSourceMap(fragUV).xyz;
historyColor = sourceColor;
if (!(any(lessThan(prevEyeUV, vec2(0.0))) || any(greaterThan(prevEyeUV, vec2(1.0))))) {
historyColor = taa_fetchHistoryMap(prevFragUV).xyz;
}
return prevFragUV;
}
float Luminance(vec3 rgb) {
return rgb.x/4.0 + rgb.y/2.0 + rgb.z/4.0;
}
#define MINMAX_3X3_ROUNDED 1
mat3 taa_evalNeighbourColorVariance(vec3 sourceColor, vec2 fragUV, vec2 fragVelocity) {
vec2 texelSize = taa_getTexelSize();
vec2 du = vec2(texelSize.x, 0.0);
vec2 dv = vec2(0.0, texelSize.y);
vec3 sampleColor = taa_fetchSourceMap(fragUV - dv - du).rgb;
vec3 sumSamples = sampleColor;
vec3 sumSamples2 = sampleColor * sampleColor;
sampleColor = taa_fetchSourceMap(fragUV - dv).rgb;
sumSamples += sampleColor;
sumSamples2 += sampleColor * sampleColor;
sampleColor = taa_fetchSourceMap(fragUV - dv + du).rgb;
sumSamples += sampleColor;
sumSamples2 += sampleColor * sampleColor;
sampleColor = taa_fetchSourceMap(fragUV - du).rgb;
sumSamples += sampleColor;
sumSamples2 += sampleColor * sampleColor;
sampleColor = sourceColor; //taa_fetchSourceMap(fragUV).rgb; // could resuse the same osurce sampleColor isn't it ?
sumSamples += sampleColor;
sumSamples2 += sampleColor * sampleColor;
sampleColor = taa_fetchSourceMap(fragUV + du).rgb;
sumSamples += sampleColor;
sumSamples2 += sampleColor * sampleColor;
sampleColor = taa_fetchSourceMap(fragUV + dv - du).rgb;
sumSamples += sampleColor;
sumSamples2 += sampleColor * sampleColor;
sampleColor = taa_fetchSourceMap(fragUV + dv).rgb;
sumSamples += sampleColor;
sumSamples2 += sampleColor * sampleColor;
sampleColor = taa_fetchSourceMap(fragUV + dv + du).rgb;
sumSamples += sampleColor;
sumSamples2 += sampleColor * sampleColor;
vec3 mu = sumSamples / vec3(9.0);
vec3 sigma = sqrt(max(sumSamples2 / vec3(9.0) - mu * mu, vec3(0.0)));
float gamma = params.covarianceGamma;
vec3 cmin = mu - gamma * sigma;
vec3 cmax = mu + gamma * sigma;
return mat3(cmin, cmax, mu);
}
mat3 taa_evalNeighbourColorRegion(vec3 sourceColor, vec2 fragUV, vec2 fragVelocity, float fragZe) {
vec2 imageSize = taa_getImageSize();
vec2 texelSize = taa_getTexelSize();
vec3 cmin, cmax, cavg;
#if MINMAX_3X3_ROUNDED
vec2 du = vec2(texelSize.x, 0.0);
vec2 dv = vec2(0.0, texelSize.y);
vec3 ctl = taa_fetchSourceMap(fragUV - dv - du).rgb;
vec3 ctc = taa_fetchSourceMap(fragUV - dv).rgb;
vec3 ctr = taa_fetchSourceMap(fragUV - dv + du).rgb;
vec3 cml = taa_fetchSourceMap(fragUV - du).rgb;
vec3 cmc = sourceColor; //taa_fetchSourceMap(fragUV).rgb; // could resuse the same osurce sample isn't it ?
vec3 cmr = taa_fetchSourceMap(fragUV + du).rgb;
vec3 cbl = taa_fetchSourceMap(fragUV + dv - du).rgb;
vec3 cbc = taa_fetchSourceMap(fragUV + dv).rgb;
vec3 cbr = taa_fetchSourceMap(fragUV + dv + du).rgb;
cmin = min(ctl, min(ctc, min(ctr, min(cml, min(cmc, min(cmr, min(cbl, min(cbc, cbr))))))));
cmax = max(ctl, max(ctc, max(ctr, max(cml, max(cmc, max(cmr, max(cbl, max(cbc, cbr))))))));
#if MINMAX_3X3_ROUNDED || USE_YCOCG || USE_CLIPPING
cavg = (ctl + ctc + ctr + cml + cmc + cmr + cbl + cbc + cbr) / 9.0;
#elif
cavg = (cmin + cmax ) * 0.5;
#endif
#if MINMAX_3X3_ROUNDED
vec3 cmin5 = min(ctc, min(cml, min(cmc, min(cmr, cbc))));
vec3 cmax5 = max(ctc, max(cml, max(cmc, max(cmr, cbc))));
vec3 cavg5 = (ctc + cml + cmc + cmr + cbc) / 5.0;
cmin = 0.5 * (cmin + cmin5);
cmax = 0.5 * (cmax + cmax5);
cavg = 0.5 * (cavg + cavg5);
#endif
#else
const float _SubpixelThreshold = 0.5;
const float _GatherBase = 0.5;
const float _GatherSubpixelMotion = 0.1666;
vec2 texel_vel = fragVelocity * imageSize;
float texel_vel_mag = length(texel_vel) * -fragZe;
float k_subpixel_motion = clamp(_SubpixelThreshold / (0.0001 + texel_vel_mag), 0.0, 1.0);
float k_min_max_support = _GatherBase + _GatherSubpixelMotion * k_subpixel_motion;
vec2 ss_offset01 = k_min_max_support * vec2(-texelSize.x, texelSize.y);
vec2 ss_offset11 = k_min_max_support * vec2(texelSize.x, texelSize.y);
vec3 c00 = taa_fetchSourceMap(fragUV - ss_offset11).rgb;
vec3 c10 = taa_fetchSourceMap(fragUV - ss_offset01).rgb;
vec3 c01 = taa_fetchSourceMap(fragUV + ss_offset01).rgb;
vec3 c11 = taa_fetchSourceMap(fragUV + ss_offset11).rgb;
cmin = min(c00, min(c10, min(c01, c11)));
cmax = max(c00, max(c10, max(c01, c11)));
cavg = (cmin + cmax ) * 0.5;
#if USE_YCOCG || USE_CLIPPING
cavg = (c00 + c10 + c01 + c11) / 4.0;
#elif
cavg = (cmin + cmax ) * 0.5;
#endif
#endif
// shrink chroma min-max
#if USE_YCOCG
vec2 chroma_extent = vec2(0.25 * 0.5 * (cmax.r - cmin.r));
vec2 chroma_center = sourceColor.gb;
cmin.yz = chroma_center - chroma_extent;
cmax.yz = chroma_center + chroma_extent;
cavg.yz = chroma_center;
#endif
return mat3(cmin, cmax, cavg);
}
//#define USE_OPTIMIZATIONS 0
vec3 taa_clampColor(vec3 colorMin, vec3 colorMax, vec3 colorSource, vec3 color) {
const float eps = 0.00001;
vec3 p = colorSource;
vec3 q = color;
// note: only clips towards aabb center (but fast!)
vec3 p_clip = 0.5 * (colorMax + colorMin);
vec3 e_clip = 0.5 * (colorMax - colorMin) + vec3(eps);
vec3 v_clip = q - p_clip;
vec3 v_unit = v_clip.xyz / e_clip;
vec3 a_unit = abs(v_unit);
float ma_unit = max(a_unit.x, max(a_unit.y, a_unit.z));
if (ma_unit > 1.0)
return p_clip + v_clip / ma_unit;
else
return q;// point inside aabb
}
vec3 taa_evalConstrainColor(vec3 sourceColor, vec2 sourceUV, vec2 sourceVel, vec3 candidateColor) {
mat3 colorMinMaxAvg;
colorMinMaxAvg = taa_evalNeighbourColorVariance(sourceColor, sourceUV, sourceVel);
// clamp history to neighbourhood of current sample
return taa_clampColor(colorMinMaxAvg[0], colorMinMaxAvg[1], sourceColor, candidateColor);
}
vec3 taa_evalFeedbackColor(vec3 sourceColor, vec3 historyColor, float blendFactor) {
const float _FeedbackMin = 0.1;
const float _FeedbackMax = 0.9;
// feedback weight from unbiased luminance diff (t.lottes)
#if USE_YCOCG
float lum0 = sourceColor.r;
float lum1 = historyColor.r;
#else
float lum0 = Luminance(sourceColor.rgb);
float lum1 = Luminance(historyColor.rgb);
#endif
float unbiased_diff = abs(lum0 - lum1) / max(lum0, max(lum1, 0.2));
float unbiased_weight = 1.0 - unbiased_diff;
float unbiased_weight_sqr = unbiased_weight * unbiased_weight;
float k_feedback = mix(_FeedbackMin, _FeedbackMax, unbiased_weight_sqr);
vec3 nextColor = mix(historyColor, sourceColor, k_feedback * blendFactor).xyz;
return nextColor;
}
<$declareColorWheel()$>
vec3 taa_getVelocityColorRelative(float velocityPixLength) {
return colorRamp(velocityPixLength/params.debugShowVelocityThreshold);
}
vec3 taa_getVelocityColorAboveThreshold(float velocityPixLength) {
return colorRamp((velocityPixLength - params.debugShowVelocityThreshold)/params.debugShowVelocityThreshold);
}
vec3 taa_evalFXAA(vec2 fragUV) {
// vec2 texelSize = getInvWidthHeight();
vec2 texelSize = taa_getTexelSize();
// filter width limit for dependent "two-tap" texture samples
float FXAA_SPAN_MAX = 8.0;
// local contrast multiplier for performing AA
// higher = sharper, but setting this value too high will cause near-vertical and near-horizontal edges to fail
// see "fxaaQualityEdgeThreshold"
float FXAA_REDUCE_MUL = 1.0 / 8.0;
// luminance threshold for processing dark colors
// see "fxaaQualityEdgeThresholdMin"
float FXAA_REDUCE_MIN = 1.0 / 128.0;
// fetch raw RGB values for nearby locations
// sampling pattern is "five on a die" (each diagonal direction and the center)
// computing the coordinates for these texture reads could be moved to the vertex shader for speed if needed
vec3 rgbNW = texture(sourceMap, fragUV + (vec2(-1.0, -1.0) * texelSize)).xyz;
vec3 rgbNE = texture(sourceMap, fragUV + (vec2(+1.0, -1.0) * texelSize)).xyz;
vec3 rgbSW = texture(sourceMap, fragUV + (vec2(-1.0, +1.0) * texelSize)).xyz;
vec3 rgbSE = texture(sourceMap, fragUV + (vec2(+1.0, +1.0) * texelSize)).xyz;
vec3 rgbM = texture(sourceMap, fragUV).xyz;
// convert RGB values to luminance
vec3 luma = vec3(0.299, 0.587, 0.114);
float lumaNW = dot(rgbNW, luma);
float lumaNE = dot(rgbNE, luma);
float lumaSW = dot(rgbSW, luma);
float lumaSE = dot(rgbSE, luma);
float lumaM = dot( rgbM, luma);
// luma range of local neighborhood
float lumaMin = min(lumaM, min(min(lumaNW, lumaNE), min(lumaSW, lumaSE)));
float lumaMax = max(lumaM, max(max(lumaNW, lumaNE), max(lumaSW, lumaSE)));
// direction perpendicular to local luma gradient
vec2 dir;
dir.x = -((lumaNW + lumaNE) - (lumaSW + lumaSE));
dir.y = ((lumaNW + lumaSW) - (lumaNE + lumaSE));
// compute clamped direction offset for additional "two-tap" samples
// longer vector = blurry, shorter vector = sharp
float dirReduce = max((lumaNW + lumaNE + lumaSW + lumaSE) * (0.25 * FXAA_REDUCE_MUL), FXAA_REDUCE_MIN);
float rcpDirMin = 1.0 / (min(abs(dir.x), abs(dir.y)) + dirReduce);
dir = min(vec2(FXAA_SPAN_MAX, FXAA_SPAN_MAX),
max(vec2(-FXAA_SPAN_MAX, -FXAA_SPAN_MAX), dir * rcpDirMin)) * texelSize;
// perform additional texture sampling perpendicular to gradient
vec3 rgbA = (1.0 / 2.0) * (
texture(sourceMap, fragUV + dir * (1.0 / 3.0 - 0.5)).xyz +
texture(sourceMap, fragUV + dir * (2.0 / 3.0 - 0.5)).xyz);
vec3 rgbB = rgbA * (1.0 / 2.0) + (1.0 / 4.0) * (
texture(sourceMap, fragUV + dir * (0.0 / 3.0 - 0.5)).xyz +
texture(sourceMap, fragUV + dir * (3.0 / 3.0 - 0.5)).xyz);
float lumaB = dot(rgbB, luma);
// compare luma of new samples to the luma range of the original neighborhood
// if the new samples exceed this range, just use the first two samples instead of all four
if (lumaB < lumaMin || lumaB > lumaMax) {
return rgbA;
} else {
return rgbB;
}
}