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);