From 675012a2eb91c160c7fe1c74443b6904b2a9da20 Mon Sep 17 00:00:00 2001 From: Pixelflinger Date: Wed, 19 Feb 2020 23:33:08 -0800 Subject: [PATCH] Added a quality level parameter for SSAO Currently it only selects how many samples are used. Low,medium,high and ultra respectively map to 7,11,16 and 32 samples. The default is "low", which is sufficient for most mobile applications. --- .../filament-android/src/main/cpp/View.cpp | 6 ++- .../com/google/android/filament/View.java | 49 +++++++++++-------- filament/include/filament/View.h | 1 + filament/src/PostProcessManager.cpp | 25 +++++++++- filament/src/materials/sao.mat | 18 ++++--- 5 files changed, 70 insertions(+), 29 deletions(-) diff --git a/android/filament-android/src/main/cpp/View.cpp b/android/filament-android/src/main/cpp/View.cpp index cfab8fabe6..622366686f 100644 --- a/android/filament-android/src/main/cpp/View.cpp +++ b/android/filament-android/src/main/cpp/View.cpp @@ -235,14 +235,16 @@ 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) { + jlong nativeView, jfloat radius, jfloat bias, jfloat power, jfloat resolution, jfloat intensity, + jint quality) { View* view = (View*) nativeView; View::AmbientOcclusionOptions options = { .radius = radius, .power = power, .bias = bias, .resolution = resolution, - .intensity = intensity + .intensity = intensity, + .quality = (View::QualityLevel)quality }; 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 83e3e7c407..67b8a35757 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 @@ -66,6 +66,16 @@ public class View { private BloomOptions mBloomOptions; private RenderTarget mRenderTarget; + /** + * Generic Quality Level + */ + public enum QualityLevel { + LOW, + MEDIUM, + HIGH, + ULTRA + } + /** * Dynamic resolution can be used to either reach a desired target frame rate by lowering the * resolution of a View, or to increase the quality when the rendering is faster @@ -155,6 +165,14 @@ public class View { * Strength of the Ambient Occlusion effect. Must be positive. */ public float intensity = 1.0f; + + /** + * The quality setting controls the number of samples used for evaluating Ambient + * occlusion. The default is QualityLevel.LOW which is sufficient for most mobile + * applications. + */ + @NonNull + public QualityLevel quality = QualityLevel.LOW; } /** @@ -237,24 +255,6 @@ public class View { public boolean enabled = false; } - /** - * Sets the quality of the HDR color buffer. - * - *

- * A quality of HIGH or ULTRA means using an RGB16F or RGBA16F color - * buffer. This means colors in the LDR range (0..1) have 10 bit precision. A quality of - * LOW or MEDIUM means using an R11G11B10F opaque color buffer or an - * RGBA16F transparent color buffer. With R11G11B10F colors in the LDR range have a precision of - * either 6 bits (red and green channels) or 5 bits (blue channel). - *

- */ - public enum QualityLevel { - LOW, - MEDIUM, - HIGH, - ULTRA - } - /** * Structure used to set the color precision for the rendering of a View. * @@ -267,6 +267,15 @@ public class View { * @see #getRenderQuality */ public static class RenderQuality { + /** + *

+ * A quality of HIGH or ULTRA means using an RGB16F or RGBA16F color + * buffer. This means colors in the LDR range (0..1) have 10 bit precision. A quality of + * LOW or MEDIUM means using an R11G11B10F opaque color buffer or an + * RGBA16F transparent color buffer. With R11G11B10F colors in the LDR range have a precision of + * either 6 bits (red and green channels) or 5 bits (blue channel). + *

+ */ public QualityLevel hdrColorBuffer = QualityLevel.HIGH; } @@ -795,7 +804,7 @@ public class View { public void setAmbientOcclusionOptions(@NonNull AmbientOcclusionOptions options) { mAmbientOcclusionOptions = options; nSetAmbientOcclusionOptions(getNativeObject(), options.radius, options.bias, options.power, - options.resolution, options.intensity); + options.resolution, options.intensity, options.quality.ordinal()); } /** @@ -878,6 +887,6 @@ 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); + private static native void nSetAmbientOcclusionOptions(long nativeView, float radius, float bias, float power, float resolution, float intensity, int quality); 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); } diff --git a/filament/include/filament/View.h b/filament/include/filament/View.h index 7f151eaff8..503ba5ea20 100644 --- a/filament/include/filament/View.h +++ b/filament/include/filament/View.h @@ -190,6 +190,7 @@ public: float bias = 0.0005f; //!< Self-occlusion bias in meters. Use to avoid self-occlusion. Between 0 and a few mm. float resolution = 0.5; //!< How each dimension of the AO buffer is scaled. Must be positive and <= 1. float intensity = 1.0; //!< Strength of the Ambient Occlusion effect. + QualityLevel quality = QualityLevel::LOW; //!< affects # of samples used for AO. }; /** diff --git a/filament/src/PostProcessManager.cpp b/filament/src/PostProcessManager.cpp index 771d3eac21..d669b345f0 100644 --- a/filament/src/PostProcessManager.cpp +++ b/filament/src/PostProcessManager.cpp @@ -498,7 +498,7 @@ FrameGraphId PostProcessManager::ssao(FrameGraph& fg, RenderP }, TargetBufferFlags::COLOR); }, [=](FrameGraphPassResources const& resources, - auto const& data, DriverApi& driver) { + auto const& data, DriverApi& driver) { auto depth = resources.getTexture(data.depth); auto ssao = resources.getRenderTarget(data.rt); auto const& desc = resources.getDescriptor(data.ssao); @@ -516,6 +516,27 @@ FrameGraphId PostProcessManager::ssao(FrameGraph& fg, RenderP // always square AO result, as it looks much better const float power = data.options.power * 2.0f; + float sampleCount = 7.0f; + float spiralTurns = 5.0f; + switch (data.options.quality) { + case View::QualityLevel::LOW: + sampleCount = 7.0f; + spiralTurns = 5.0f; + break; + case View::QualityLevel::MEDIUM: + sampleCount = 11.0f; + spiralTurns = 9.0f; + break; + case View::QualityLevel::HIGH: + sampleCount = 16.0f; + spiralTurns = 10.0f; + break; + case View::QualityLevel::ULTRA: + sampleCount = 32.0f; + spiralTurns = 14.0f; + break; + } + FMaterialInstance* const mi = mSSAO.getMaterialInstance(); mi->setParameter("depth", depth, { .filterMin = SamplerMinFilter::NEAREST_MIPMAP_NEAREST @@ -529,6 +550,8 @@ FrameGraphId PostProcessManager::ssao(FrameGraph& fg, RenderP mi->setParameter("power", power); mi->setParameter("intensity", intensity); mi->setParameter("maxLevel", uint32_t(levelCount - 1)); + mi->setParameter("sampleCount", float2{ sampleCount, 1.0 / sampleCount }); + mi->setParameter("spiralTurns", spiralTurns); mi->commit(driver); mi->use(driver); diff --git a/filament/src/materials/sao.mat b/filament/src/materials/sao.mat index 0ef45de89f..048de089e1 100644 --- a/filament/src/materials/sao.mat +++ b/filament/src/materials/sao.mat @@ -34,6 +34,14 @@ material { type : float, name : intensity }, + { + type : float2, + name : sampleCount + }, + { + type : float, + name : spiralTurns + }, { type : int, name : maxLevel @@ -54,8 +62,6 @@ vertex { } fragment { - const float kSpiralSampleCount = 7.0; - const float kSpiralTurns = 5.0; const float kLog2LodRate = 3.0; vec2 sq(const vec2 a) { @@ -130,8 +136,8 @@ fragment { // note: with this formulation we could precompute the samples in an array // and combine the noise, which would allow is to call sin/cos only // once per pixel. - float radius = (i + 0.5) * (1.0 / kSpiralSampleCount); - float angle = (radius * kSpiralTurns + noise) * (2.0 * PI); + float radius = (i + 0.5) * materialParams.sampleCount.y; + float angle = (radius * materialParams.spiralTurns + noise) * (2.0 * PI); return vec3(fast_cossin(angle), radius); } @@ -171,11 +177,11 @@ fragment { float ssDiskRadius = -(materialParams.projectionScaleRadius / origin.z); float occlusion = 0.0; - for (float i = 0.0; i < kSpiralSampleCount; i += 1.0) { + for (float i = 0.0; i < materialParams.sampleCount.x; i += 1.0) { computeAmbientOcclusionSAO(occlusion, i, ssDiskRadius, uv, origin, normal, noise); } - float ao = max(0.0, 1.0 - occlusion * (materialParams.intensity * (1.0 / kSpiralSampleCount))); + float ao = max(0.0, 1.0 - occlusion * (materialParams.intensity * materialParams.sampleCount.y)); ao = pow(ao, materialParams.power); // Apply a 2x2 bilateral box filter, taking advantage of quad shading. If we were not