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