From 73f1ddfebf058fe3be3e54850122f41de71f0d9f Mon Sep 17 00:00:00 2001 From: Pixelflinger Date: Sun, 20 Sep 2020 01:05:16 -0700 Subject: [PATCH] ssao: reduce creases caused by geometry tessellation We borrow a page from the HBAO book here and allow to limit the angle with the horizon of SSAO samples. This can help reducing the effects of low tessellation, which tend to create unwanted creases with SSAO. --- android/filament-android/src/main/cpp/View.cpp | 5 +++-- .../java/com/google/android/filament/View.java | 11 +++++++++-- filament/include/filament/View.h | 1 + filament/src/PostProcessManager.cpp | 1 + filament/src/details/View.h | 1 + filament/src/materials/ssao/sao.mat | 18 ++++++++++++++++-- libs/gltfio/include/gltfio/SimpleViewer.h | 1 + samples/material_sandbox.cpp | 1 + 8 files changed, 33 insertions(+), 6 deletions(-) diff --git a/android/filament-android/src/main/cpp/View.cpp b/android/filament-android/src/main/cpp/View.cpp index 4548e1c293..4c773d7d5b 100644 --- a/android/filament-android/src/main/cpp/View.cpp +++ b/android/filament-android/src/main/cpp/View.cpp @@ -196,7 +196,7 @@ Java_com_google_android_filament_View_nGetAmbientOcclusion(JNIEnv*, jclass, jlon extern "C" JNIEXPORT void JNICALL Java_com_google_android_filament_View_nSetAmbientOcclusionOptions(JNIEnv*, jclass, jlong nativeView, jfloat radius, jfloat bias, jfloat power, jfloat resolution, jfloat intensity, - jint quality, jint upsampling, jboolean enabled) { + jint quality, jint upsampling, jboolean enabled, jfloat minHorizonAngleRad) { View* view = (View*) nativeView; View::AmbientOcclusionOptions options = { .radius = radius, @@ -206,7 +206,8 @@ Java_com_google_android_filament_View_nSetAmbientOcclusionOptions(JNIEnv*, jclas .intensity = intensity, .quality = (View::QualityLevel)quality, .upsampling = (View::QualityLevel)upsampling, - .enabled = (bool)enabled + .enabled = (bool)enabled, + .minHorizonAngleRad = minHorizonAngleRad }; view->setAmbientOcclusionOptions(options); } diff --git a/android/filament-android/src/main/java/com/google/android/filament/View.java b/android/filament-android/src/main/java/com/google/android/filament/View.java index f2d0e15154..c8cecfe897 100644 --- a/android/filament-android/src/main/java/com/google/android/filament/View.java +++ b/android/filament-android/src/main/java/com/google/android/filament/View.java @@ -188,6 +188,13 @@ public class View { * enable or disable screen space ambient occlusion */ public boolean enabled = false; + + /** + * Minimal angle to consider in radian. This is used to reduce the creases that can + * appear due to insufficiently tessellated geometry. + * For e.g. a good values to try could be around 0.2. + */ + public float minHorizonAngleRad = 0.0f; } /** @@ -1070,7 +1077,7 @@ public class View { mAmbientOcclusionOptions = options; nSetAmbientOcclusionOptions(getNativeObject(), options.radius, options.bias, options.power, options.resolution, options.intensity, options.quality.ordinal(), options.upsampling.ordinal(), - options.enabled); + options.enabled, options.minHorizonAngleRad); } /** @@ -1234,7 +1241,7 @@ public class View { private static native boolean nIsFrontFaceWindingInverted(long nativeView); private static native void nSetAmbientOcclusion(long nativeView, int ordinal); private static native int nGetAmbientOcclusion(long nativeView); - private static native void nSetAmbientOcclusionOptions(long nativeView, float radius, float bias, float power, float resolution, float intensity, int quality, int upsampling, boolean enabled); + private static native void nSetAmbientOcclusionOptions(long nativeView, float radius, float bias, float power, float resolution, float intensity, int quality, int upsampling, boolean enabled, float minHorizonAngleRad); private static native void nSetBloomOptions(long nativeView, long dirtNativeObject, float dirtStrength, float strength, int resolution, float anamorphism, int levels, int blendMode, boolean threshold, boolean enabled, float highlight); private static native void nSetFogOptions(long nativeView, float distance, float maximumOpacity, float height, float heightFalloff, float v, float v1, float v2, float density, float inScatteringStart, float inScatteringSize, boolean fogColorFromIbl, boolean enabled); private static native void nSetBlendMode(long nativeView, int blendMode); diff --git a/filament/include/filament/View.h b/filament/include/filament/View.h index 1b60ad9400..9e70e40616 100644 --- a/filament/include/filament/View.h +++ b/filament/include/filament/View.h @@ -223,6 +223,7 @@ public: QualityLevel quality = QualityLevel::LOW; //!< affects # of samples used for AO. QualityLevel upsampling = QualityLevel::LOW; //!< affects AO buffer upsampling quality. bool enabled = false; //!< enables or disables screen-space ambient occlusion + float minHorizonAngleRad = 0.0f; //!< min angle in radian to consider }; /** diff --git a/filament/src/PostProcessManager.cpp b/filament/src/PostProcessManager.cpp index 339c72e289..f98fcd86b3 100644 --- a/filament/src/PostProcessManager.cpp +++ b/filament/src/PostProcessManager.cpp @@ -482,6 +482,7 @@ FrameGraphId PostProcessManager::screenSpaceAmbientOcclusion( mi->setParameter("resolution", float4{ desc.width, desc.height, 1.0f / desc.width, 1.0f / desc.height }); mi->setParameter("invRadiusSquared", 1.0f / (options.radius * options.radius)); + mi->setParameter("minHorizonAngleSineSquared", std::pow(std::sin(options.minHorizonAngleRad), 2.0f)); mi->setParameter("projectionScaleRadius", projectionScale * options.radius); mi->setParameter("depthParams", cameraInfo.projection[3][2] * 0.5f); diff --git a/filament/src/details/View.h b/filament/src/details/View.h index edb517f516..15f8ff2afe 100644 --- a/filament/src/details/View.h +++ b/filament/src/details/View.h @@ -288,6 +288,7 @@ public: options.resolution = std::floor( math::clamp(1.0f, 2.0f, options.resolution * 2.0f) + 0.5f) * 0.5f; options.intensity = std::max(0.0f, options.intensity); + options.minHorizonAngleRad = math::clamp(0.0f, math::f::PI_2, options.minHorizonAngleRad); mAmbientOcclusionOptions = options; } diff --git a/filament/src/materials/ssao/sao.mat b/filament/src/materials/ssao/sao.mat index dfa1b9c460..4e8c8c81b6 100644 --- a/filament/src/materials/ssao/sao.mat +++ b/filament/src/materials/ssao/sao.mat @@ -25,6 +25,10 @@ material { type : float, name : invRadiusSquared }, + { + type : float, + name : minHorizonAngleSineSquared + }, { type : float, name : peak2 @@ -191,8 +195,18 @@ fragment { float vv = dot(v, v); // squared distance float vn = dot(v, normal); // distance * cos(v, normal) - float w = max(0.0, 1.0 - vv * materialParams.invRadiusSquared); - occlusion += (w * w) * max(0.0, vn + origin.z * materialParams.bias) / (vv + materialParams.peak2); + // discard samples that are outside of the radius, preventing distant geometry to + // cast shadows -- there are many functions that work and choosing one is an artistic + // decision. + float w = sq(max(0.0, 1.0 - vv * materialParams.invRadiusSquared)); + + // discard samples that are too close to the horizon to reduce shadows cast by geometry + // not sufficently tessellated. The goal is to discard samples that form an angle 'beta' + // smaller than 'epsilon' with the horizon. We already have dot(v,n) which is equal to the + // sin(beta) * |v|. So the test simplifies to vn^2 < vv * sin(epsilon)^2. + w *= step(vv * materialParams.minHorizonAngleSineSquared, vn * vn); + + occlusion += w * max(0.0, vn + origin.z * materialParams.bias) / (vv + materialParams.peak2); } void postProcess(inout PostProcessInputs postProcess) { diff --git a/libs/gltfio/include/gltfio/SimpleViewer.h b/libs/gltfio/include/gltfio/SimpleViewer.h index 1ea2b97f92..ae512177f9 100644 --- a/libs/gltfio/include/gltfio/SimpleViewer.h +++ b/libs/gltfio/include/gltfio/SimpleViewer.h @@ -478,6 +478,7 @@ void SimpleViewer::updateUserInterface() { bool upsampling = mSSAOOptions.upsampling != View::QualityLevel::LOW; ImGui::SliderInt("Quality", &quality, 0, 3); ImGui::Checkbox("High quality upsampling", &upsampling); + ImGui::SliderFloat("Min Horizon angle", &mSSAOOptions.minHorizonAngleRad, 0.0f, (float)M_PI_4); mSSAOOptions.upsampling = upsampling ? View::QualityLevel::HIGH : View::QualityLevel::LOW; mSSAOOptions.quality = (View::QualityLevel) quality; } diff --git a/samples/material_sandbox.cpp b/samples/material_sandbox.cpp index 1a96209dbb..c8a622edc7 100644 --- a/samples/material_sandbox.cpp +++ b/samples/material_sandbox.cpp @@ -608,6 +608,7 @@ static void gui(filament::Engine* engine, filament::View*) { ImGui::Checkbox("Enabled##ssao", ¶ms.ssaoOptions.enabled); ImGui::SliderFloat("Radius", ¶ms.ssaoOptions.radius, 0.05f, 5.0f); ImGui::SliderFloat("Bias", ¶ms.ssaoOptions.bias, 0.0f, 0.01f, "%.6f"); + ImGui::SliderFloat("Min Horizon angle", ¶ms.ssaoOptions.minHorizonAngleRad, 0.0f, (float)M_PI_4, "%.6f"); ImGui::SliderFloat("Intensity", ¶ms.ssaoOptions.intensity, 0.0f, 4.0f); ImGui::SliderFloat("Power", ¶ms.ssaoOptions.power, 0.0f, 4.0f); ImGui::SliderInt("Quality", &quality, 0, 3);