Switched to sin based HBAO

This commit is contained in:
Olivier Prat 2018-10-01 19:22:54 +02:00
parent abc415c5ad
commit 7f6c9a6cc1
6 changed files with 135 additions and 52 deletions

View file

@ -206,10 +206,10 @@ gpu::TexturePointer AmbientOcclusionFramebuffer::getNormalTexture() {
AmbientOcclusionEffectConfig::AmbientOcclusionEffectConfig() :
render::GPUJobConfig::Persistent(QStringList() << "Render" << "Engine" << "Ambient Occlusion", false),
radius{ 0.3f },
radius{ 0.5f },
perspectiveScale{ 1.0f },
obscuranceLevel{ 0.5f },
falloffAngle{ 0.45f },
obscuranceLevel{ 0.25f },
falloffAngle{ 0.7f },
edgeSharpness{ 1.0f },
blurDeviation{ 2.5f },
numSpiralTurns{ 7.0f },
@ -227,6 +227,7 @@ AmbientOcclusionEffect::AOParameters::AOParameters() {
_radiusInfo = { 0.5f, 0.5f * 0.5f, 1.0f / (0.25f * 0.25f * 0.25f), 1.0f };
_ditheringInfo = { 0.0f, 0.0f, 0.01f, 1.0f };
_sampleInfo = { 11.0f, 1.0f / 11.0f, 7.0f, 1.0f };
_falloffInfo = { 0.5f, 2.0f, 0.866f, 1.1547f };
}
AmbientOcclusionEffect::BlurParameters::BlurParameters() {
@ -263,10 +264,13 @@ void AmbientOcclusionEffect::configure(const Config& config) {
current.w = config.obscuranceLevel;
}
if (config.falloffAngle != _aoParametersBuffer->getFalloffAngle()) {
auto& current = _aoParametersBuffer.edit()._ditheringInfo;
current.z = config.falloffAngle;
current.y = 1.0f / (1.0f - config.falloffAngle);
if (config.falloffAngle != _aoParametersBuffer->getFalloffCosAngle()) {
auto& current = _aoParametersBuffer.edit()._falloffInfo;
current.x = config.falloffAngle;
current.y = 1.0f / (1.0f - current.x);
// Compute sin from cos
current.z = sqrtf(1.0f - config.falloffAngle * config.falloffAngle);
current.w = 1.0f / current.z;
}
// Update bilateral blur
@ -535,7 +539,7 @@ void AmbientOcclusionEffect::run(const render::RenderContextPointer& renderConte
auto& sample = _aoFrameParametersBuffer[splitId].edit();
sample._angleInfo.x = _randomSamples[splitId + SSAO_RANDOM_SAMPLE_COUNT * _frameId];
}
_frameId = (_frameId + 1) % SSAO_RANDOM_SAMPLE_COUNT;
//_frameId = (_frameId + 1) % SSAO_RANDOM_SAMPLE_COUNT;
gpu::doInBatch("AmbientOcclusionEffect::run", args->_context, [=](gpu::Batch& batch) {
PROFILE_RANGE_BATCH(batch, "SSAO");

View file

@ -150,7 +150,7 @@ public:
float getRadius() const { return _radiusInfo.x; }
float getPerspectiveScale() const { return _resolutionInfo.z; }
float getObscuranceLevel() const { return _radiusInfo.w; }
float getFalloffAngle() const { return (float)_ditheringInfo.z; }
float getFalloffCosAngle() const { return (float)_falloffInfo.x; }
float getNumSpiralTurns() const { return _sampleInfo.z; }
int getNumSamples() const { return (int)_sampleInfo.x; }

View file

@ -115,11 +115,18 @@ float isBorderingEnabled() {
return params._ditheringInfo.w;
}
float getFalloffAngle() {
return params._ditheringInfo.z;
float getFalloffCosAngle() {
return params._falloffInfo.x;
}
float getFalloffAngleScale() {
return params._ditheringInfo.y;
float getFalloffCosAngleScale() {
return params._falloffInfo.y;
}
float getFalloffSinAngle() {
return params._falloffInfo.z;
}
float getFalloffSinAngleScale() {
return params._falloffInfo.w;
}
float getNumSamples() {
@ -330,10 +337,27 @@ vec3 buildNormal(ivec4 side, vec2 fragUVPos, vec3 fragPosition, vec2 deltaDepthU
vec3 fragPositionDyPos = buildPosition(side, fragUVPos, ivec2(0,1), deltaDepthUV);
vec3 fragPositionDyNeg = buildPosition(side, fragUVPos, ivec2(0,-1), deltaDepthUV);
vec3 fragPositionDx = getMinDelta(fragPosition, fragPositionDxPos, fragPositionDxNeg);
vec3 fragPositionDy = getMinDelta(fragPosition, fragPositionDyPos, fragPositionDyNeg);
vec3 fragDeltaDx = getMinDelta(fragPosition, fragPositionDxPos, fragPositionDxNeg);
vec3 fragDeltaDy = getMinDelta(fragPosition, fragPositionDyPos, fragPositionDyNeg);
return normalize( cross(fragPositionDx, fragPositionDy) );
return normalize( cross(fragDeltaDx, fragDeltaDy) );
}
void buildTangentBinormal(ivec4 side, vec2 fragUVPos, vec3 fragPosition, vec3 fragNormal, vec2 deltaDepthUV,
out vec3 fragTangent, out vec3 fragBinormal) {
vec3 fragPositionDxPos = buildPosition(side, fragUVPos, ivec2(1,0), deltaDepthUV);
vec3 fragPositionDxNeg = buildPosition(side, fragUVPos, ivec2(-1,0), deltaDepthUV);
vec3 fragPositionDyPos = buildPosition(side, fragUVPos, ivec2(0,1), deltaDepthUV);
vec3 fragPositionDyNeg = buildPosition(side, fragUVPos, ivec2(0,-1), deltaDepthUV);
vec3 fragDeltaDx = getMinDelta(fragPosition, fragPositionDxPos, fragPositionDxNeg);
vec3 fragDeltaDy = getMinDelta(fragPosition, fragPositionDyPos, fragPositionDyNeg);
//fragTangent = normalize( cross(fragDeltaDy, fragNormal) );
//fragBinormal = normalize( cross(fragNormal, fragDeltaDx) );
fragTangent = fragDeltaDx;
fragBinormal = fragDeltaDy;
}
<@endfunc@>
@ -341,6 +365,12 @@ vec3 buildNormal(ivec4 side, vec2 fragUVPos, vec3 fragPosition, vec2 deltaDepthU
<@func declareEvalObscurance()@>
struct TBNFrame {
vec3 tangent;
vec3 binormal;
vec3 normal;
};
vec3 fastAcos(vec3 x) {
// [Eberly2014] GPGPU Programming for Games and Science
vec3 absX = abs(x);
@ -357,29 +387,25 @@ float evalVisibilitySSAO(in vec3 centerPosition, in vec3 centerNormal, in vec3 t
// Falloff function as recommended in SSAO paper
const float epsilon = 0.01;
float f = max(getRadius2() - vv, 0.0);
return f * f * f * max((vn - getFalloffAngle()) / (epsilon + vv), 0.0);
return f * f * f * max((vn - getFalloffCosAngle()) / (epsilon + vv), 0.0);
}
vec2 computeHorizonFromTap(vec3 tapPositionES, vec3 fragPositionES, vec3 fragNormalES) {
const float epsilon = 0.001;
#define HBAO_USE_COS_ANGLE 0
vec3 deltaVec = tapPositionES - fragPositionES;
float distance = length(deltaVec);
float cosHorizonAngle = dot(deltaVec, fragNormalES) / (distance + epsilon);
float computeWeightedHorizon(float horizonLimit, float distanceSquared) {
float radiusFalloff = distanceSquared / getRadius2();
return vec2(cosHorizonAngle, distance);
}
radiusFalloff = max(0.0, 1.0 - radiusFalloff);
float computeWeightedHorizonFromTap(vec3 tapPositionES, vec3 fragPositionES, vec3 fragNormalES) {
vec2 rawHorizon = computeHorizonFromTap(tapPositionES, fragPositionES, fragNormalES);
float distance = rawHorizon.y;
float cosHorizonAngle = rawHorizon.x;
float radiusFalloff = max(0.0, 1.0 - (distance*distance / getRadius2()));
#if !HBAO_USE_COS_ANGLE
horizonLimit = getFalloffSinAngle() - horizonLimit;
#endif
horizonLimit *= radiusFalloff;
#if !HBAO_USE_COS_ANGLE
horizonLimit = getFalloffSinAngle() - horizonLimit;
#endif
cosHorizonAngle = max(0.0, (cosHorizonAngle - getFalloffAngle()) * getFalloffAngleScale());
cosHorizonAngle *= radiusFalloff;
return cosHorizonAngle;
return horizonLimit;
}
<@func computeHorizon()@>
@ -389,24 +415,46 @@ float computeWeightedHorizonFromTap(vec3 tapPositionES, vec3 fragPositionES, vec
}
vec2 tapMipZ = fetchTap(side, fragUVPos, radius);
vec3 tapPositionES = evalEyePositionFromZeye(side.x, tapMipZ.y, fragUVPos);
float tapCosHorizonAngle = computeWeightedHorizonFromTap(tapPositionES, fragPositionES, fragNormalES);
vec3 deltaVec = tapPositionES - fragPositionES;
float distanceSquared = dot(deltaVec, deltaVec);
float distance = sqrt(distanceSquared);
float deltaDotNormal = dot(deltaVec, fragFrameES.normal);
#if HBAO_USE_COS_ANGLE
float tapHorizonLimit = deltaDotNormal;
#else
float tapHorizonLimit = dot(deltaVec, fragFrameES.tangent);
#endif
float epsilon = 0.0001;
tapHorizonLimit /= (distance + epsilon);
cosHorizonAngle = max(cosHorizonAngle, tapCosHorizonAngle);
if (distanceSquared < getRadius2() && deltaDotNormal>0.0 &&
#if HBAO_USE_COS_ANGLE
tapHorizonLimit > horizonLimit
#else
tapHorizonLimit < horizonLimit
#endif
) {
tapHorizonLimit = computeWeightedHorizon(tapHorizonLimit, distanceSquared);
#if HBAO_USE_COS_ANGLE
horizonLimit = max(horizonLimit, tapHorizonLimit);
#else
horizonLimit = min(horizonLimit, tapHorizonLimit);
#endif
}
<@endfunc@>
#define HBAO_HORIZON_SEARCH_CONSTANT_STEP 0
float computeHorizon(ivec4 side, vec2 fragUVPos, vec3 fragPositionES, vec3 fragNormalES, vec2 searchVec, vec2 pixelSearchVec,
float searchRadius) {
float computeHorizon(ivec4 side, vec2 fragUVPos, vec3 fragPositionES, TBNFrame fragFrameES, vec2 searchVec, float searchRadius, int stepCount) {
vec2 absSearchVec = abs(searchVec);
pixelSearchVec = abs(pixelSearchVec);
int stepCount = int(ceil(max(pixelSearchVec.x, pixelSearchVec.y)));
float cosHorizonAngle = 0.0;
#if HBAO_USE_COS_ANGLE
float horizonLimit = getFalloffCosAngle();
#else
float horizonLimit = getFalloffSinAngle();
#endif
if (stepCount>0) {
vec2 deltaTapUV = searchVec / float(stepCount);
float deltaRadius = searchRadius / float(stepCount);
#if HBAO_HORIZON_SEARCH_CONSTANT_STEP
@ -439,22 +487,33 @@ float computeHorizon(ivec4 side, vec2 fragUVPos, vec3 fragPositionES, vec3 fragN
#endif
}
return cosHorizonAngle;
return horizonLimit;
}
float evalVisibilityHBAO(ivec4 side, vec2 fragUVPos, vec2 invSideImageSize, vec2 deltaTap, float diskPixelRadius,
vec3 fragPositionES, vec3 fragNormalES) {
vec3 fragPositionES, TBNFrame fragFrameES) {
vec2 pixelSearchVec = deltaTap * diskPixelRadius;
vec2 searchVec = pixelSearchVec * invSideImageSize;
float obscurance = 0.0;
float obscuranceH1 = 0.0;
float obscuranceH2 = 0.0;
#if !HBAO_USE_COS_ANGLE
fragFrameES.tangent = normalize(fragFrameES.tangent * deltaTap.x + fragFrameES.binormal * deltaTap.y);
#endif
pixelSearchVec = abs(pixelSearchVec);
int stepCount = int(ceil(max(pixelSearchVec.x, pixelSearchVec.y)));
// Forward search for h1
obscurance = computeHorizon(side, fragUVPos, fragPositionES, fragNormalES, searchVec, pixelSearchVec, diskPixelRadius);
obscuranceH1 = computeHorizon(side, fragUVPos, fragPositionES, fragFrameES, searchVec, diskPixelRadius, stepCount);
// Backward search for h2
obscurance += computeHorizon(side, fragUVPos, fragPositionES, fragNormalES, -searchVec, pixelSearchVec, diskPixelRadius);
#if !HBAO_USE_COS_ANGLE
fragFrameES.tangent = -fragFrameES.tangent;
#endif
obscuranceH2 = computeHorizon(side, fragUVPos, fragPositionES, fragFrameES, -searchVec, diskPixelRadius, stepCount);
return obscurance;
return obscuranceH1 + obscuranceH2;
}
<@endfunc@>

View file

@ -55,6 +55,15 @@ void main(void) {
diskPixelRadius = min(diskPixelRadius, SSAO_HBAO_MAX_RADIUS);
}
TBNFrame fragFrameES;
fragFrameES.tangent = vec3(0.0);
fragFrameES.binormal = vec3(0.0);
fragFrameES.normal = fragNormalES;
#if !HBAO_USE_COS_ANGLE
buildTangentBinormal(side, fragUVPos, fragPositionES, fragNormalES, deltaDepthUV, fragFrameES.tangent, fragFrameES.binormal);
#endif
// Let's make noise
float randomPatternRotationAngle = getAngleDithering(fragPixelPos);
@ -66,9 +75,18 @@ void main(void) {
if (isHorizonBased()) {
for (int i = 0; i < numSamples; ++i) {
vec3 deltaTap = getUnitTapLocation(i, 1.0, randomPatternRotationAngle, PI);
obscuranceSum += evalVisibilityHBAO(side, fragUVPos, deltaDepthUV, deltaTap.xy, diskPixelRadius, fragPositionES, fragNormalES);
obscuranceSum += evalVisibilityHBAO(side, fragUVPos, deltaDepthUV, deltaTap.xy, diskPixelRadius, fragPositionES, fragFrameES);
}
obscuranceSum *= invNumSamples;
#if HBAO_USE_COS_ANGLE
obscuranceSum *= 0.5 / PI;
obscuranceSum = 1.0 - obscuranceSum * obscuranceSum * getObscuranceScaling();
#else
obscuranceSum *= 0.5;
obscuranceSum += 1.0 - getFalloffSinAngle();
obscuranceSum = mix(1.0, obscuranceSum, getObscuranceScaling());
#endif
} else {
// Steps are in the depth texture resolution
vec2 depthTexFragPixelPos = fragUVPos * sideDepthSize;
@ -80,10 +98,11 @@ void main(void) {
vec3 tapPositionES = evalEyePositionFromZeye(side.x, tapMipZ.y, tapUV);
obscuranceSum += float(tap.z > 0.0) * evalVisibilitySSAO(fragPositionES, fragNormalES, tapPositionES);
}
obscuranceSum *= invNumSamples;
obscuranceSum = 1.0 - obscuranceSum * obscuranceSum * getObscuranceScaling();
}
obscuranceSum *= getObscuranceScaling() * invNumSamples;
float occlusion = clamp(1.0 - obscuranceSum * obscuranceSum, 0.0, 1.0);
float occlusion = clamp(obscuranceSum, 0.0, 1.0);
outFragColor = packOcclusionOutput(occlusion, fragPositionES.z, fragNormalES);
}

View file

@ -40,6 +40,7 @@ struct AmbientOcclusionParams {
SSAO_VEC4 _radiusInfo;
SSAO_VEC4 _ditheringInfo;
SSAO_VEC4 _sampleInfo;
SSAO_VEC4 _falloffInfo;
SSAO_VEC4 _sideSizes[2];
};

View file

@ -37,7 +37,7 @@ Rectangle {
"Level:obscuranceLevel:1.0:false",
"Num Taps:numSamples:16:true",
"Taps Spiral:numSpiralTurns:10.0:false",
"Falloff Angle:falloffAngle:0.5:false",
"Falloff Angle:falloffAngle:1.0:false",
"Blur Edge Sharpness:edgeSharpness:1.0:false",
"Blur Radius:blurRadius:15.0:true",
"Resolution Downscale:resolutionLevel:2:true",