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