From fe474a9d7e21a9990dc13ea1da27e551e02ef0ff Mon Sep 17 00:00:00 2001 From: Doris Wu Date: Fri, 16 May 2025 08:13:12 +0800 Subject: [PATCH] Add GTAO shader support (#8688) * Add GTAO options into the struct * Add gtao mat and shader * Set type specific parameters in ppm * Fix inconsistent name * Fix incorrect param type * Add bent normal calculation * Fix the gui * Adjust precision * Use mix * Add some comments * Refactoring * Update comments * Add comments for aoOptions * Update * Update * Address the comments * Split .mat into bent and non-bent * Update * Update * Use sqrt on mobile * Omit default case * Commit beamsplitter generated changes * Use built-in acosFast * Remove unused function * Fix the mismatched parameter type * Some optimizations * Commit beamsplitter results * Update the comment * Update default value * Commit beamsplitter changes * Extract common parameters out --- .../com/google/android/filament/View.java | 38 +++- filament/CMakeLists.txt | 24 +++ filament/include/filament/Options.h | 30 ++- filament/src/PostProcessManager.cpp | 154 ++++++++------- filament/src/materials/ssao/gtao.mat | 160 ++++++++++++++++ .../src/materials/ssao/gtaoBentNormals.mat | 176 ++++++++++++++++++ filament/src/materials/ssao/gtaoImpl.fs | 153 +++++++++++++++ libs/viewer/src/Settings_generated.cpp | 59 +++++- libs/viewer/src/Settings_generated.h | 6 + libs/viewer/src/ViewerGui.cpp | 29 ++- web/filament-js/extensions_generated.js | 11 ++ web/filament-js/filament.d.ts | 37 +++- web/filament-js/jsbindings_generated.cpp | 8 + web/filament-js/jsenums_generated.cpp | 5 + 14 files changed, 811 insertions(+), 79 deletions(-) create mode 100644 filament/src/materials/ssao/gtao.mat create mode 100644 filament/src/materials/ssao/gtaoBentNormals.mat create mode 100644 filament/src/materials/ssao/gtaoImpl.fs 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 b9304ada0a..4ce293d64a 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 @@ -1801,6 +1801,22 @@ public class View { * @see setAmbientOcclusionOptions() */ public static class AmbientOcclusionOptions { + public enum AmbientOcclusionType { + /** + * use Scalable Ambient Occlusion + */ + SAO, + /** + * use Ground Truth-Based Ambient Occlusion + */ + GTAO, + } + + /** + * Type of ambient occlusion algorithm. + */ + @NonNull + public AmbientOcclusionOptions.AmbientOcclusionType aoType = AmbientOcclusionOptions.AmbientOcclusionType.SAO; /** * Ambient Occlusion radius in meters, between 0 and ~10. */ @@ -1810,7 +1826,8 @@ public class View { */ public float power = 1.0f; /** - * Self-occlusion bias in meters. Use to avoid self-occlusion. Between 0 and a few mm. + * Self-occlusion bias in meters. Use to avoid self-occlusion. + * Between 0 and a few mm. No effect when aoType set to GTAO */ public float bias = 0.0005f; /** @@ -1826,12 +1843,12 @@ public class View { */ public float bilateralThreshold = 0.05f; /** - * affects # of samples used for AO. + * affects # of samples used for AO and params for filtering */ @NonNull public QualityLevel quality = QualityLevel.LOW; /** - * affects AO smoothness + * affects AO smoothness. Recommend setting to HIGH when aoType set to GTAO. */ @NonNull public QualityLevel lowPassFilter = QualityLevel.MEDIUM; @@ -1849,7 +1866,7 @@ public class View { */ public boolean bentNormals = false; /** - * min angle in radian to consider + * min angle in radian to consider. No effect when aoType set to GTAO. */ public float minHorizonAngleRad = 0.0f; /** @@ -1904,6 +1921,19 @@ public class View { */ public boolean ssctEnabled = false; + /** + * Ground Truth-base Ambient Occlusion (GTAO) options + */ + public int gtaoSampleSliceCount = 4; + /** + * Ground Truth-base Ambient Occlusion (GTAO) options + */ + public int gtaoSampleStepsPerSlice = 3; + /** + * Ground Truth-base Ambient Occlusion (GTAO) options + */ + public float gtaoThicknessHeuristic = 0.004f; + } /** diff --git a/filament/CMakeLists.txt b/filament/CMakeLists.txt index 1ecf8dc550..3821404d22 100644 --- a/filament/CMakeLists.txt +++ b/filament/CMakeLists.txt @@ -262,6 +262,8 @@ set(MATERIAL_SRCS src/materials/ssao/mipmapDepth.mat src/materials/ssao/sao.mat src/materials/ssao/saoBentNormals.mat + src/materials/ssao/gtao.mat + src/materials/ssao/gtaoBentNormals.mat src/materials/vsmMipmap.mat ) @@ -461,6 +463,28 @@ add_custom_command( APPEND ) +add_custom_command( + OUTPUT "${MATERIAL_DIR}/gtao.filamat" + DEPENDS src/materials/ssao/ssaoUtils.fs + DEPENDS src/materials/ssao/ssct.fs + DEPENDS src/materials/utils/depthUtils.fs + DEPENDS src/materials/utils/geometry.fs + DEPENDS src/materials/ssao/gtaoImpl.fs + DEPENDS src/materials/ssao/ssctImpl.fs + APPEND +) + +add_custom_command( + OUTPUT "${MATERIAL_DIR}/gtaoBentNormals.filamat" + DEPENDS src/materials/ssao/ssaoUtils.fs + DEPENDS src/materials/ssao/ssct.fs + DEPENDS src/materials/utils/depthUtils.fs + DEPENDS src/materials/utils/geometry.fs + DEPENDS src/materials/ssao/gtaoImpl.fs + DEPENDS src/materials/ssao/ssctImpl.fs + APPEND +) + add_custom_command( OUTPUT "${MATERIAL_DIR}/bilateralBlur.filamat" DEPENDS src/materials/ssao/ssaoUtils.fs diff --git a/filament/include/filament/Options.h b/filament/include/filament/Options.h index dc06143dd4..101607c6f1 100644 --- a/filament/include/filament/Options.h +++ b/filament/include/filament/Options.h @@ -373,18 +373,30 @@ struct RenderQuality { * @see setAmbientOcclusionOptions() */ struct AmbientOcclusionOptions { + enum class AmbientOcclusionType : uint8_t { + SAO, //!< use Scalable Ambient Occlusion + GTAO, //!< use Ground Truth-Based Ambient Occlusion + }; + + AmbientOcclusionType aoType = AmbientOcclusionType::SAO;//!< Type of ambient occlusion algorithm. float radius = 0.3f; //!< Ambient Occlusion radius in meters, between 0 and ~10. float power = 1.0f; //!< Controls ambient occlusion's contrast. Must be positive. - float bias = 0.0005f; //!< Self-occlusion bias in meters. Use to avoid self-occlusion. Between 0 and a few mm. + + /** + * Self-occlusion bias in meters. Use to avoid self-occlusion. + * Between 0 and a few mm. No effect when aoType set to GTAO + */ + float bias = 0.0005f; + float resolution = 0.5f;//!< How each dimension of the AO buffer is scaled. Must be either 0.5 or 1.0. float intensity = 1.0f; //!< Strength of the Ambient Occlusion effect. float bilateralThreshold = 0.05f; //!< depth distance that constitute an edge for filtering - QualityLevel quality = QualityLevel::LOW; //!< affects # of samples used for AO. - QualityLevel lowPassFilter = QualityLevel::MEDIUM; //!< affects AO smoothness + QualityLevel quality = QualityLevel::LOW; //!< affects # of samples used for AO and params for filtering + QualityLevel lowPassFilter = QualityLevel::MEDIUM; //!< affects AO smoothness. Recommend setting to HIGH when aoType set to GTAO. QualityLevel upsampling = QualityLevel::LOW; //!< affects AO buffer upsampling quality bool enabled = false; //!< enables or disables screen-space ambient occlusion bool bentNormals = false; //!< enables bent normals computation from AO, and specular AO - float minHorizonAngleRad = 0.0f; //!< min angle in radian to consider + float minHorizonAngleRad = 0.0f; //!< min angle in radian to consider. No effect when aoType set to GTAO. /** * Screen Space Cone Tracing (SSCT) options * Ambient shadows from dominant light @@ -402,6 +414,16 @@ struct AmbientOcclusionOptions { bool enabled = false; //!< enables or disables SSCT }; Ssct ssct; // %codegen_skip_javascript% %codegen_java_flatten% + + /** + * Ground Truth-base Ambient Occlusion (GTAO) options + */ + struct Gtao { + uint8_t sampleSliceCount = 4; //!< # of slices. Higher value makes less noise. + uint8_t sampleStepsPerSlice = 3; //!< # of steps the radius is divided into for integration. Higher value makes less bias. + float thicknessHeuristic = 0.004f; //!< thickness heuristic, should be closed to 0 + }; + Gtao gtao; // %codegen_skip_javascript% %codegen_java_flatten% }; /** diff --git a/filament/src/PostProcessManager.cpp b/filament/src/PostProcessManager.cpp index 4d008cb6f2..63f6aacbdc 100644 --- a/filament/src/PostProcessManager.cpp +++ b/filament/src/PostProcessManager.cpp @@ -100,7 +100,9 @@ using namespace backend; static constexpr uint8_t kMaxBloomLevels = 12u; static_assert(kMaxBloomLevels >= 3, "We require at least 3 bloom levels"); -constexpr static float halton(unsigned int i, unsigned int const b) noexcept { +namespace { + +constexpr float halton(unsigned int i, unsigned int const b) noexcept { // skipping a bunch of entries makes the average of the sequence closer to 0.5 i += 409; float f = 1.0f; @@ -113,6 +115,19 @@ constexpr static float halton(unsigned int i, unsigned int const b) noexcept { return r; } +template +void setConstantParameter(FMaterial* const material, std::string_view const name, + ValueType value, bool& dirty) noexcept { + auto id = material->getSpecializationConstantId(name); + if (id.has_value()) { + if (material->setConstant(id.value(), value)) { + dirty = true; + } + } +} + +} // anonymous + // ------------------------------------------------------------------------------------------------ PostProcessManager::PostProcessMaterial::PostProcessMaterial(StaticMaterialInfo const& info) noexcept @@ -289,6 +304,8 @@ static const PostProcessManager::StaticMaterialInfo sMaterialList[] = { { "mipmapDepth", MATERIAL(MIPMAPDEPTH) }, { "sao", MATERIAL(SAO) }, { "saoBentNormals", MATERIAL(SAOBENTNORMALS) }, + { "gtao", MATERIAL(GTAO) }, + { "gtaoBentNormals", MATERIAL(GTAOBENTNORMALS) }, { "separableGaussianBlur1", MATERIAL(SEPARABLEGAUSSIANBLUR), { {"arraySampler", false}, {"componentCount", 1} } }, { "separableGaussianBlur1L", MATERIAL(SEPARABLEGAUSSIANBLUR), @@ -846,12 +863,6 @@ FrameGraphId PostProcessManager::screenSpaceAmbientOcclusion( 0.5f * cameraInfo.projection[0].x * desc.width, 0.5f * cameraInfo.projection[1].y * desc.height); - // Where the falloff function peaks - const float peak = 0.1f * options.radius; - const float intensity = (f::TAU * peak) * options.intensity; - // always square AO result, as it looks much better - const float power = options.power * 2.0f; - const auto invProjection = inverse(cameraInfo.projection); const float inc = (1.0f / (sampleCount - 0.5f)) * spiralTurns * f::TAU; @@ -862,40 +873,75 @@ FrameGraphId PostProcessManager::screenSpaceAmbientOcclusion( 0.0, 0.0, 0.0, 1.0 }}; - auto& material = computeBentNormals ? - getPostProcessMaterial("saoBentNormals") : - getPostProcessMaterial("sao"); - FMaterial const* const ma = material.getMaterial(mEngine); + std::string_view materialName; + if (options.aoType == + AmbientOcclusionOptions::AmbientOcclusionType::GTAO) { + materialName = computeBentNormals ? "gtaoBentNormals" : "gtao"; + } else { + materialName = computeBentNormals ? "saoBentNormals" : "sao"; + } + + auto& material = getPostProcessMaterial(materialName); + + FMaterial const * const ma = material.getMaterial(mEngine); FMaterialInstance* const mi = PostProcessMaterial::getMaterialInstance(ma); - mi->setParameter("depth", depth, { - .filterMin = SamplerMinFilter::NEAREST_MIPMAP_NEAREST }); + + // Set AO type specific material parameters + switch (options.aoType) { + case AmbientOcclusionOptions::AmbientOcclusionType::SAO: { + // Where the falloff function peaks + const float peak = 0.1f * options.radius; + const float intensity = (f::TAU * peak) * options.intensity; + + // always square AO result, as it looks much better + const float power = options.power * 2.0f; + + mi->setParameter("minHorizonAngleSineSquared", + std::pow(std::sin(options.minHorizonAngleRad), 2.0f)); + mi->setParameter("intensity", intensity / sampleCount); + mi->setParameter("power", power); + mi->setParameter("peak2", peak * peak); + mi->setParameter("bias", options.bias); + mi->setParameter("sampleCount", + float2{ sampleCount, 1.0f / (sampleCount - 0.5f) }); + mi->setParameter("spiralTurns", spiralTurns); + mi->setParameter("angleIncCosSin", float2{ std::cos(inc), std::sin(inc) }); + break; + } + case AmbientOcclusionOptions::AmbientOcclusionType::GTAO: { + const auto sliceCount = static_cast(options.gtao.sampleSliceCount); + mi->setParameter("stepsPerSlice", + static_cast(options.gtao.sampleStepsPerSlice)); + mi->setParameter("sliceCount", + float2{ sliceCount, 1.0f / sliceCount }); + mi->setParameter("power", options.power); + mi->setParameter("radius", options.radius); + mi->setParameter("intensity", options.intensity); + mi->setParameter("thicknessHeuristic", options.gtao.thicknessHeuristic); + + break; + } + } + + // Set common material parameters + mi->setParameter("invRadiusSquared", 1.0f / (options.radius * options.radius)); + mi->setParameter("depth", depth, + { .filterMin = SamplerMinFilter::NEAREST_MIPMAP_NEAREST }); mi->setParameter("screenFromViewMatrix", mat4f(screenFromClipMatrix * cameraInfo.projection)); 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("projectionScale", - projectionScale); - mi->setParameter("projectionScaleRadius", - projectionScale * options.radius); - mi->setParameter("positionParams", float2{ - invProjection[0][0], invProjection[1][1] } * 2.0f); - mi->setParameter("peak2", peak * peak); - mi->setParameter("bias", options.bias); - mi->setParameter("power", power); - mi->setParameter("intensity", intensity / sampleCount); + mi->setParameter("projectionScale", projectionScale); + mi->setParameter("projectionScaleRadius", projectionScale * options.radius); + mi->setParameter("positionParams", + float2{ invProjection[0][0], invProjection[1][1] } * 2.0f); mi->setParameter("maxLevel", uint32_t(levelCount - 1)); - mi->setParameter("sampleCount", float2{ sampleCount, 1.0f / (sampleCount - 0.5f) }); - mi->setParameter("spiralTurns", spiralTurns); - mi->setParameter("angleIncCosSin", float2{ std::cos(inc), std::sin(inc) }); mi->setParameter("invFarPlane", 1.0f / -cameraInfo.zf); - mi->setParameter("ssctShadowDistance", options.ssct.shadowDistance); - mi->setParameter("ssctConeAngleTangeant", std::tan(options.ssct.lightConeRad * 0.5f)); - mi->setParameter("ssctContactDistanceMaxInv", 1.0f / options.ssct.contactDistanceMax); + mi->setParameter("ssctConeAngleTangeant", + std::tan(options.ssct.lightConeRad * 0.5f)); + mi->setParameter("ssctContactDistanceMaxInv", + 1.0f / options.ssct.contactDistanceMax); // light direction in view space const mat4f view{ cameraInfo.getUserViewMatrix() }; const float3 l = normalize( @@ -2632,25 +2678,15 @@ void PostProcessManager::configureTemporalAntiAliasingMaterial( FMaterial* const ma = getPostProcessMaterial("taa").getMaterial(mEngine); bool dirty = false; - auto setConstantParameter = - [&dirty](FMaterial* const material, std::string_view const name, auto value) noexcept { - auto id = material->getSpecializationConstantId(name); - if (id.has_value()) { - if (material->setConstant(id.value(), value)) { - dirty = true; - } - } - }; - - setConstantParameter(ma, "upscaling", taaOptions.upscaling); - setConstantParameter(ma, "historyReprojection", taaOptions.historyReprojection); - setConstantParameter(ma, "filterHistory", taaOptions.filterHistory); - setConstantParameter(ma, "filterInput", taaOptions.filterInput); - setConstantParameter(ma, "useYCoCg", taaOptions.useYCoCg); - setConstantParameter(ma, "preventFlickering", taaOptions.preventFlickering); - setConstantParameter(ma, "boxType", int32_t(taaOptions.boxType)); - setConstantParameter(ma, "boxClipping", int32_t(taaOptions.boxClipping)); - setConstantParameter(ma, "varianceGamma", taaOptions.varianceGamma); + setConstantParameter(ma, "upscaling", taaOptions.upscaling, dirty); + setConstantParameter(ma, "historyReprojection", taaOptions.historyReprojection, dirty); + setConstantParameter(ma, "filterHistory", taaOptions.filterHistory, dirty); + setConstantParameter(ma, "filterInput", taaOptions.filterInput, dirty); + setConstantParameter(ma, "useYCoCg", taaOptions.useYCoCg, dirty); + setConstantParameter(ma, "preventFlickering", taaOptions.preventFlickering, dirty); + setConstantParameter(ma, "boxType", int32_t(taaOptions.boxType), dirty); + setConstantParameter(ma, "boxClipping", int32_t(taaOptions.boxClipping), dirty); + setConstantParameter(ma, "varianceGamma", taaOptions.varianceGamma, dirty); if (dirty) { ma->invalidate(); // TODO: call Material::compile(), we can't do that now because it works only @@ -2665,18 +2701,8 @@ FMaterialInstance* PostProcessManager::configureColorGradingMaterial( FMaterial* const ma = material.getMaterial(mEngine); bool dirty = false; - auto setConstantParameter = - [&dirty](FMaterial* const material, std::string_view const name, auto value) noexcept { - auto id = material->getSpecializationConstantId(name); - if (id.has_value()) { - if (material->setConstant(id.value(), value)) { - dirty = true; - } - } - }; - - setConstantParameter(ma, "isOneDimensional", colorGrading->isOneDimensional()); - setConstantParameter(ma, "isLDR", colorGrading->isLDR()); + setConstantParameter(ma, "isOneDimensional", colorGrading->isOneDimensional(), dirty); + setConstantParameter(ma, "isLDR", colorGrading->isLDR(), dirty); if (dirty) { ma->invalidate(); diff --git a/filament/src/materials/ssao/gtao.mat b/filament/src/materials/ssao/gtao.mat new file mode 100644 index 0000000000..bd0c71c678 --- /dev/null +++ b/filament/src/materials/ssao/gtao.mat @@ -0,0 +1,160 @@ +material { + name : gtao, + parameters : [ + { + type : sampler2d, + name : depth, + precision: high + }, + { + type : mat4, + name : screenFromViewMatrix + }, + { + type : float4, + name : resolution, + precision: high + }, + { + type : float2, + name : positionParams, + precision: high + }, + { + type : float, + name : invFarPlane + }, + { + type : int, + name : maxLevel + }, + { + type : float, + name : projectionScale + }, + { + type : float, + name : intensity + }, + { + type : float2, + name : sliceCount + }, + { + type : float, + name : stepsPerSlice + }, + { + type : float, + name : radius + }, + { + type : float, + name : invRadiusSquared + }, + { + type : float, + name : projectionScaleRadius + }, + { + type : float, + name : power + }, + { + type : float, + name : thicknessHeuristic + }, + { + type : float, + name : ssctShadowDistance + }, + { + type : float, + name : ssctConeAngleTangeant + }, + { + type : float, + name : ssctContactDistanceMaxInv + }, + { + type : float3, + name : ssctVsLightDirection + }, + { + type : float, + name : ssctIntensity + }, + { + type : float2, + name : ssctDepthBias + }, + { + type : float2, + name : ssctRayCount + }, + { + type : uint, + name : ssctSampleCount + } + ], + variables : [ + vertex + ], + domain : postprocess, + depthWrite : false, + depthCulling : true +} + +vertex { + void postProcessVertex(inout PostProcessVertexInputs postProcess) { + // we don't use use uvToRenderTargetUV() here to compensate for metal/vulkan texture + // coordinates differences because this is done in sampleDepth() + postProcess.vertex.xy = postProcess.normalizedUV; + } +} + +fragment { + +#define COMPUTE_BENT_NORMAL 0 + + #include "gtaoImpl.fs" + #include "ssctImpl.fs" + #include "../utils/geometry.fs" + #include "ssaoUtils.fs" + + void dummy(){} + + void postProcess(inout PostProcessInputs postProcess) { + highp vec2 uv = variable_vertex.xy; // interpolated to pixel center + + highp float depth = sampleDepth(materialParams_depth, uv, 0.0); + highp float z = linearizeDepth(depth); + highp vec3 origin = computeViewSpacePositionFromDepth(uv, z, materialParams.positionParams); + + vec3 normal = computeViewSpaceNormal(materialParams_depth, uv, depth, origin, + materialParams.resolution.zw, + materialParams.positionParams); + + float occlusion = 0.0; + vec3 bentNormal; // will be discarded + + if (materialParams.intensity > 0.0) { + groundTruthAmbientOcclusion(occlusion, bentNormal, uv, origin, normal); + } + + if (materialParams.ssctIntensity > 0.0) { + occlusion = max(occlusion, dominantLightShadowing(uv, origin, normal)); + } + + // occlusion to visibility + float aoVisibility = pow(saturate(1.0 - occlusion), materialParams.power); + aoVisibility = mix(1.0, aoVisibility, materialParams.intensity); + +#if defined(TARGET_MOBILE) + // this line is needed to workaround what seems to be a bug on qualcomm hardware + aoVisibility += gl_FragCoord.x * MEDIUMP_FLT_MIN; +#endif + + postProcess.color.rgb = vec3(aoVisibility, pack(origin.z * materialParams.invFarPlane)); + } +} \ No newline at end of file diff --git a/filament/src/materials/ssao/gtaoBentNormals.mat b/filament/src/materials/ssao/gtaoBentNormals.mat new file mode 100644 index 0000000000..f385830700 --- /dev/null +++ b/filament/src/materials/ssao/gtaoBentNormals.mat @@ -0,0 +1,176 @@ +material { + name : gtao, + parameters : [ + { + type : sampler2d, + name : depth, + precision: high + }, + { + type : mat4, + name : screenFromViewMatrix + }, + { + type : float4, + name : resolution, + precision: high + }, + { + type : float2, + name : positionParams, + precision: high + }, + { + type : float, + name : invFarPlane + }, + { + type : int, + name : maxLevel + }, + { + type : float, + name : projectionScale + }, + { + type : float, + name : intensity + }, + { + type : float2, + name : sliceCount + }, + { + type : float, + name : stepsPerSlice + }, + { + type : float, + name : radius + }, + { + type : float, + name : invRadiusSquared + }, + { + type : float, + name : projectionScaleRadius + }, + { + type : float, + name : power + }, + { + type : float, + name : thicknessHeuristic + }, + { + type : float, + name : ssctShadowDistance + }, + { + type : float, + name : ssctConeAngleTangeant + }, + { + type : float, + name : ssctContactDistanceMaxInv + }, + { + type : float3, + name : ssctVsLightDirection + }, + { + type : float, + name : ssctIntensity + }, + { + type : float2, + name : ssctDepthBias + }, + { + type : float2, + name : ssctRayCount + }, + { + type : uint, + name : ssctSampleCount + } + ], + outputs : [ + { + name : aoData, + target : color, + type : float3 + }, + { + name : bnData, + target : color, + type : float3 + } + ], + variables : [ + vertex + ], + domain : postprocess, + depthWrite : false, + depthCulling : true +} + +vertex { + void postProcessVertex(inout PostProcessVertexInputs postProcess) { + // we don't use use uvToRenderTargetUV() here to compensate for metal/vulkan texture + // coordinates differences because this is done in sampleDepth() + postProcess.vertex.xy = postProcess.normalizedUV; + } +} + +fragment { + +#define COMPUTE_BENT_NORMAL 1 + + #include "gtaoImpl.fs" + #include "ssctImpl.fs" + #include "../utils/geometry.fs" + #include "ssaoUtils.fs" + + void dummy(){} + + void postProcess(inout PostProcessInputs postProcess) { + highp vec2 uv = variable_vertex.xy; // interpolated to pixel center + + highp float depth = sampleDepth(materialParams_depth, uv, 0.0); + highp float z = linearizeDepth(depth); + highp vec3 origin = computeViewSpacePositionFromDepth(uv, z, materialParams.positionParams); + + vec3 normal = computeViewSpaceNormal(materialParams_depth, uv, depth, origin, + materialParams.resolution.zw, + materialParams.positionParams); + + float occlusion = 0.0; + vec3 bentNormal = normal; + + if (materialParams.intensity > 0.0) { + groundTruthAmbientOcclusion(occlusion, bentNormal, uv, origin, normal); + } + + if (materialParams.ssctIntensity > 0.0) { + occlusion = max(occlusion, dominantLightShadowing(uv, origin, normal)); + } + + // occlusion to visibility + float aoVisibility = pow(saturate(1.0 - occlusion), materialParams.power); + aoVisibility = mix(1.0, aoVisibility, materialParams.intensity); + +#if defined(TARGET_MOBILE) + // this line is needed to workaround what seems to be a bug on qualcomm hardware + aoVisibility += gl_FragCoord.x * MEDIUMP_FLT_MIN; +#endif + + // transform to world space (we're guaranteed the view matrix is a rigid body transform) + vec3 bn = mat3(getWorldFromViewMatrix()) * bentNormal; + + postProcess.bnData = packBentNormal(bn); + postProcess.aoData = vec3(aoVisibility, pack(origin.z * materialParams.invFarPlane)); + } +} \ No newline at end of file diff --git a/filament/src/materials/ssao/gtaoImpl.fs b/filament/src/materials/ssao/gtaoImpl.fs new file mode 100644 index 0000000000..12b195844d --- /dev/null +++ b/filament/src/materials/ssao/gtaoImpl.fs @@ -0,0 +1,153 @@ +/* + * Copyright (C) 2025 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/* + * This is our implementation of GTAO -- it's not standalone because it uses materialParams + * directly. Therefore it must be included in *.mat file that has all these parameters. + * The main reason for using a separate file is to be able to have several version of the + * code with only minor changes. + */ + +#include "../utils/geometry.fs" + +#define rsqrt inversesqrt + +#ifndef COMPUTE_BENT_NORMAL +#error COMPUTE_BENT_NORMAL must be set +#endif + +const float kLog2LodRate = 3.0; + +// Ambient Occlusion, largely inspired from: +// "Practical Real-Time Strategies for Accurate Indirect Occlusion" by Jimenez et al. +// https://github.com/GameTechDev/XeGTAO +// https://github.com/MaxwellGengYF/Unity-Ground-Truth-Ambient-Occlusion + +highp vec3 getViewSpacePosition(vec2 uv, float level) { + highp float depth = sampleDepthLinear(materialParams_depth, uv, level); + return computeViewSpacePositionFromDepth(uv, depth, materialParams.positionParams); +} + +float integrateArcCosWeight(float h, float n) { + float arc = -cos(2.0 * h - n) + cos(n) + 2.0 * h * sin(n); + return 0.25 * arc; +} + +// https://blog.selfshadow.com/publications/s2016-shading-course/activision/s2016_pbs_activision_occlusion.pdf slide 93 +float spatialDirectionNoise(float2 uv) { + int2 position = int2(uv * materialParams.resolution.xy); + return (1.0/16.0) * (float(((position.x + position.y) & 3) << 2) + float(position.x & 3)); +} + +// https://blog.selfshadow.com/publications/s2016-shading-course/activision/s2016_pbs_activision_occlusion.pdf slide 93 +float spatialOffsetsNoise(float2 uv) { + int2 position = int2(uv * materialParams.resolution.xy); + return 0.25 * float((position.y - position.x) & 3); +} + +void groundTruthAmbientOcclusion(out float obscurance, out vec3 bentNormal, + highp vec2 uv, highp vec3 origin, vec3 normal) { + vec2 uvSamplePos = uv; + highp vec3 viewDir = normalize(-origin); + highp float ssRadius = -(materialParams.projectionScaleRadius / origin.z); + + float noiseOffset = spatialOffsetsNoise(uv); + float noiseDirection = spatialDirectionNoise(uv); + + float initialRayStep = fract(noiseOffset); + + // The distance we want to move forward for each step + float stepRadius = ssRadius / (materialParams.stepsPerSlice + 1.0); + + float visibility = 0.0; + for (float i = 0.0; i < materialParams.sliceCount.x; i += 1.0) { + float slice = (i + noiseDirection) * materialParams.sliceCount.y; + float phi = slice * PI; + float cosPhi = cos(phi); + float sinPhi = sin(phi); + vec2 omega = vec2(cosPhi, sinPhi); + + // Calculate the direction of the current slice + vec3 direction = vec3(cosPhi, sinPhi, 0.0); + // Project direction onto the plane orthogonal to viewDir. + vec3 orthoDirection = normalize(direction - (dot(direction, viewDir)*viewDir)); + // axis is orthogonal to direction and viewDir (basically the normal of the slice plane) + // Used to define projectedNormal + vec3 axis = cross(orthoDirection, viewDir); + // Project the normal onto the slice plane + vec3 projNormal = normal - axis * dot(normal, axis); + + float signNorm = sign(dot(orthoDirection, projNormal)); + float projNormalLength = length(projNormal); + float cosNorm = saturate(dot(projNormal, viewDir) / projNormalLength); + + float n = signNorm * acosFast(cosNorm); + + float horizonCos0 = -1.0; + float horizonCos1 = -1.0; + for (float j = 0.0; j < materialParams.stepsPerSlice; j += 1.0) { + // At least move 1 pixel forward in the screen-space + vec2 sampleOffset = max((j + initialRayStep)*stepRadius, 1.0 + j) * omega; + float sampleOffsetLength = length(sampleOffset); + + float level = clamp(floor(log2(sampleOffsetLength)) - kLog2LodRate, 0.0, float(materialParams.maxLevel)); + + vec2 uvSampleOffset = sampleOffset * materialParams.resolution.zw; + + vec2 sampleScreenPos0 = uv + uvSampleOffset; + vec2 sampleScreenPos1 = uv - uvSampleOffset; + + // Sample the depth and use it to reconstruct the view space position + highp vec3 samplePos0 = getViewSpacePosition(sampleScreenPos0, level); + highp vec3 samplePos1 = getViewSpacePosition(sampleScreenPos1, level); + + highp vec3 sampleDelta0 = (samplePos0 - origin); + highp vec3 sampleDelta1 = (samplePos1 - origin); + vec2 sqSampleDist = vec2(dot(sampleDelta0, sampleDelta0), dot(sampleDelta1, sampleDelta1)); + vec2 invSampleDist = rsqrt(sqSampleDist); + + // Use the view space radius to calculate the fallOff + vec2 fallOff = saturate(sqSampleDist.xy * materialParams.invRadiusSquared * 2.0); + + // sample horizon cos + float shc0 = dot(sampleDelta0, viewDir) * invSampleDist.x; + float shc1 = dot(sampleDelta1, viewDir) * invSampleDist.y; + + // If the new sample value is greater then the current one, update the value with some fallOff. + // Otherwise, apply thicknessHeuristic. + horizonCos0 = shc0 > horizonCos0 ? mix(shc0, horizonCos0, fallOff.x) : mix(horizonCos0, shc0, materialParams.thicknessHeuristic); + horizonCos1 = shc1 > horizonCos1 ? mix(shc1, horizonCos1, fallOff.y) : mix(horizonCos1, shc1, materialParams.thicknessHeuristic); + } + + float h0 = -acosFast(horizonCos1); + float h1 = acosFast(horizonCos0); + h0 = n + clamp(h0 - n, -HALF_PI, HALF_PI); + h1 = n + clamp(h1 - n, -HALF_PI, HALF_PI); + +#if COMPUTE_BENT_NORMAL + float angle = 0.5 * (h0 + h1); + bentNormal += viewDir * cos(angle) - orthoDirection * sin(angle); +#endif + + visibility += projNormalLength * (integrateArcCosWeight(h0, n) + integrateArcCosWeight(h1, n)); + } + + obscurance = 1.0 - saturate(visibility * materialParams.sliceCount.y); + +#if COMPUTE_BENT_NORMAL + bentNormal = normalize(bentNormal); +#endif +} \ No newline at end of file diff --git a/libs/viewer/src/Settings_generated.cpp b/libs/viewer/src/Settings_generated.cpp index ba7705e299..1b2a4fa406 100644 --- a/libs/viewer/src/Settings_generated.cpp +++ b/libs/viewer/src/Settings_generated.cpp @@ -504,6 +504,23 @@ std::ostream& operator<<(std::ostream& out, const RenderQuality& in) { << "}"; } +int parse(jsmntok_t const* tokens, int i, const char* jsonChunk, AmbientOcclusionOptions::AmbientOcclusionType* out) { + if (0 == compare(tokens[i], jsonChunk, "SAO")) { *out = AmbientOcclusionOptions::AmbientOcclusionType::SAO; } + else if (0 == compare(tokens[i], jsonChunk, "GTAO")) { *out = AmbientOcclusionOptions::AmbientOcclusionType::GTAO; } + else { + slog.w << "Invalid AmbientOcclusionOptions::AmbientOcclusionType: '" << STR(tokens[i], jsonChunk) << "'" << io::endl; + } + return i + 1; +} + +std::ostream& operator<<(std::ostream& out, AmbientOcclusionOptions::AmbientOcclusionType in) { + switch (in) { + case AmbientOcclusionOptions::AmbientOcclusionType::SAO: return out << "\"SAO\""; + case AmbientOcclusionOptions::AmbientOcclusionType::GTAO: return out << "\"GTAO\""; + } + return out << "\"INVALID\""; +} + int parse(jsmntok_t const* tokens, int i, const char* jsonChunk, AmbientOcclusionOptions::Ssct* out) { CHECK_TOKTYPE(tokens[i], JSMN_OBJECT); int size = tokens[i++].size; @@ -557,13 +574,47 @@ std::ostream& operator<<(std::ostream& out, const AmbientOcclusionOptions::Ssct& << "}"; } +int parse(jsmntok_t const* tokens, int i, const char* jsonChunk, AmbientOcclusionOptions::Gtao* out) { + CHECK_TOKTYPE(tokens[i], JSMN_OBJECT); + int size = tokens[i++].size; + for (int j = 0; j < size; ++j) { + const jsmntok_t tok = tokens[i]; + CHECK_KEY(tok); + if (compare(tok, jsonChunk, "sampleSliceCount") == 0) { + i = parse(tokens, i + 1, jsonChunk, &out->sampleSliceCount); + } else if (compare(tok, jsonChunk, "sampleStepsPerSlice") == 0) { + i = parse(tokens, i + 1, jsonChunk, &out->sampleStepsPerSlice); + } else if (compare(tok, jsonChunk, "thicknessHeuristic") == 0) { + i = parse(tokens, i + 1, jsonChunk, &out->thicknessHeuristic); + } else { + slog.w << "Invalid Gtao key: '" << STR(tok, jsonChunk) << "'" << io::endl; + i = parse(tokens, i + 1); + } + if (i < 0) { + slog.e << "Invalid Gtao value: '" << STR(tok, jsonChunk) << "'" << io::endl; + return i; + } + } + return i; +} + +std::ostream& operator<<(std::ostream& out, const AmbientOcclusionOptions::Gtao& in) { + return out << "{\n" + << "\"sampleSliceCount\": " << int(in.sampleSliceCount) << ",\n" + << "\"sampleStepsPerSlice\": " << int(in.sampleStepsPerSlice) << ",\n" + << "\"thicknessHeuristic\": " << (in.thicknessHeuristic) << "\n" + << "}"; +} + int parse(jsmntok_t const* tokens, int i, const char* jsonChunk, AmbientOcclusionOptions* out) { CHECK_TOKTYPE(tokens[i], JSMN_OBJECT); int size = tokens[i++].size; for (int j = 0; j < size; ++j) { const jsmntok_t tok = tokens[i]; CHECK_KEY(tok); - if (compare(tok, jsonChunk, "radius") == 0) { + if (compare(tok, jsonChunk, "aoType") == 0) { + i = parse(tokens, i + 1, jsonChunk, &out->aoType); + } else if (compare(tok, jsonChunk, "radius") == 0) { i = parse(tokens, i + 1, jsonChunk, &out->radius); } else if (compare(tok, jsonChunk, "power") == 0) { i = parse(tokens, i + 1, jsonChunk, &out->power); @@ -589,6 +640,8 @@ int parse(jsmntok_t const* tokens, int i, const char* jsonChunk, AmbientOcclusio i = parse(tokens, i + 1, jsonChunk, &out->minHorizonAngleRad); } else if (compare(tok, jsonChunk, "ssct") == 0) { i = parse(tokens, i + 1, jsonChunk, &out->ssct); + } else if (compare(tok, jsonChunk, "gtao") == 0) { + i = parse(tokens, i + 1, jsonChunk, &out->gtao); } else { slog.w << "Invalid AmbientOcclusionOptions key: '" << STR(tok, jsonChunk) << "'" << io::endl; i = parse(tokens, i + 1); @@ -603,6 +656,7 @@ int parse(jsmntok_t const* tokens, int i, const char* jsonChunk, AmbientOcclusio std::ostream& operator<<(std::ostream& out, const AmbientOcclusionOptions& in) { return out << "{\n" + << "\"aoType\": " << (in.aoType) << ",\n" << "\"radius\": " << (in.radius) << ",\n" << "\"power\": " << (in.power) << ",\n" << "\"bias\": " << (in.bias) << ",\n" @@ -615,7 +669,8 @@ std::ostream& operator<<(std::ostream& out, const AmbientOcclusionOptions& in) { << "\"enabled\": " << to_string(in.enabled) << ",\n" << "\"bentNormals\": " << to_string(in.bentNormals) << ",\n" << "\"minHorizonAngleRad\": " << (in.minHorizonAngleRad) << ",\n" - << "\"ssct\": " << (in.ssct) << "\n" + << "\"ssct\": " << (in.ssct) << ",\n" + << "\"gtao\": " << (in.gtao) << "\n" << "}"; } diff --git a/libs/viewer/src/Settings_generated.h b/libs/viewer/src/Settings_generated.h index 1215a147ee..370c691887 100644 --- a/libs/viewer/src/Settings_generated.h +++ b/libs/viewer/src/Settings_generated.h @@ -60,9 +60,15 @@ std::ostream& operator<<(std::ostream& out, const VignetteOptions& in); int parse(jsmntok_t const* tokens, int i, const char* jsonChunk, RenderQuality* out); std::ostream& operator<<(std::ostream& out, const RenderQuality& in); +int parse(jsmntok_t const* tokens, int i, const char* jsonChunk, AmbientOcclusionOptions::AmbientOcclusionType* out); +std::ostream& operator<<(std::ostream& out, AmbientOcclusionOptions::AmbientOcclusionType in); + int parse(jsmntok_t const* tokens, int i, const char* jsonChunk, AmbientOcclusionOptions::Ssct* out); std::ostream& operator<<(std::ostream& out, const AmbientOcclusionOptions::Ssct& in); +int parse(jsmntok_t const* tokens, int i, const char* jsonChunk, AmbientOcclusionOptions::Gtao* out); +std::ostream& operator<<(std::ostream& out, const AmbientOcclusionOptions::Gtao& in); + int parse(jsmntok_t const* tokens, int i, const char* jsonChunk, AmbientOcclusionOptions* out); std::ostream& operator<<(std::ostream& out, const AmbientOcclusionOptions& in); diff --git a/libs/viewer/src/ViewerGui.cpp b/libs/viewer/src/ViewerGui.cpp index 826d18c944..d1d5165a25 100644 --- a/libs/viewer/src/ViewerGui.cpp +++ b/libs/viewer/src/ViewerGui.cpp @@ -824,6 +824,12 @@ void ViewerGui::updateUserInterface() { if (ImGui::CollapsingHeader("SSAO Options")) { auto& ssao = mSettings.view.ssao; + ImGui::Checkbox("Enabled", &ssao.enabled); + + int ambienOcclusionType = (int)ssao.aoType; + ImGui::Combo("AO Type", &ambienOcclusionType, "SAO\0GTAO\0\0"); + ssao.aoType = (decltype(ssao.aoType))ambienOcclusionType; + int quality = (int) ssao.quality; int lowpass = (int) ssao.lowPassFilter; bool upsampling = ssao.upsampling != View::QualityLevel::LOW; @@ -833,7 +839,28 @@ void ViewerGui::updateUserInterface() { ImGui::SliderInt("Low Pass", &lowpass, 0, 2); ImGui::Checkbox("Bent Normals", &ssao.bentNormals); ImGui::Checkbox("High quality upsampling", &upsampling); - ImGui::SliderFloat("Min Horizon angle", &ssao.minHorizonAngleRad, 0.0f, (float)M_PI_4); + ImGui::SliderFloat("Radius", &ssao.radius, 0.1f, 10.0f); + ImGui::SliderFloat("Power", &ssao.power, 1.0f, 8.0f); + + switch (ssao.aoType) { + case AmbientOcclusionOptions::AmbientOcclusionType::SAO: { + ImGui::SliderFloat("Min Horizon angle", &ssao.minHorizonAngleRad, 0.0f, + (float) M_PI_4); + break; + } + case AmbientOcclusionOptions::AmbientOcclusionType::GTAO: { + int sliceCount = ssao.gtao.sampleSliceCount; + int stepsPerSlice = ssao.gtao.sampleStepsPerSlice; + + ImGui::SliderInt("Slice Count", &sliceCount, 1, 10); + ImGui::SliderInt("Steps Per Slice", &stepsPerSlice, 1, 4); + + ssao.gtao.sampleSliceCount = static_cast(sliceCount); + ssao.gtao.sampleStepsPerSlice = static_cast(stepsPerSlice); + break; + } + } + ImGui::SliderFloat("Bilateral Threshold", &ssao.bilateralThreshold, 0.0f, 0.1f); ImGui::Checkbox("Half resolution", &halfRes); ssao.resolution = halfRes ? 0.5f : 1.0f; diff --git a/web/filament-js/extensions_generated.js b/web/filament-js/extensions_generated.js index a0d69778c4..c6fc55f7f0 100644 --- a/web/filament-js/extensions_generated.js +++ b/web/filament-js/extensions_generated.js @@ -108,8 +108,18 @@ Filament.loadGeneratedExtensions = function() { return Object.assign(options, overrides); }; + Filament.View.prototype.setGtaoDefaults = function(overrides) { + const options = { + sampleSliceCount: 4, + sampleStepsPerSlice: 3, + thicknessHeuristic: 0.004, + }; + return Object.assign(options, overrides); + }; + Filament.View.prototype.setAmbientOcclusionOptionsDefaults = function(overrides) { const options = { + aoType: Filament.View$AmbientOcclusionOptions$AmbientOcclusionType.SAO, radius: 0.3, power: 1.0, bias: 0.0005, @@ -123,6 +133,7 @@ Filament.loadGeneratedExtensions = function() { bentNormals: false, minHorizonAngleRad: 0.0, // JavaScript binding for ssct is not yet supported, must use default value. + // JavaScript binding for gtao is not yet supported, must use default value. }; return Object.assign(options, overrides); }; diff --git a/web/filament-js/filament.d.ts b/web/filament-js/filament.d.ts index 4f9e5ddaa6..cf52555ce6 100644 --- a/web/filament-js/filament.d.ts +++ b/web/filament-js/filament.d.ts @@ -1516,6 +1516,11 @@ export interface View$RenderQuality { hdrColorBuffer?: View$QualityLevel; } +export enum View$AmbientOcclusionOptions$AmbientOcclusionType { + SAO, // use Scalable Ambient Occlusion + GTAO, // use Ground Truth-Based Ambient Occlusion +} + /** * Screen Space Cone Tracing (SSCT) options * Ambient shadows from dominant light @@ -1563,11 +1568,33 @@ export interface View$AmbientOcclusionOptions$Ssct { enabled?: boolean; } +/** + * Ground Truth-base Ambient Occlusion (GTAO) options + */ +export interface View$AmbientOcclusionOptions$Gtao { + /** + * # of slices. Higher value makes less noise. + */ + sampleSliceCount?: number; + /** + * # of steps the radius is divided into for integration. Higher value makes less bias. + */ + sampleStepsPerSlice?: number; + /** + * thickness heuristic, should be closed to 0 + */ + thicknessHeuristic?: number; +} + /** * Options for screen space Ambient Occlusion (SSAO) and Screen Space Cone Tracing (SSCT) * @see setAmbientOcclusionOptions() */ export interface View$AmbientOcclusionOptions { + /** + * Type of ambient occlusion algorithm. + */ + aoType?: View$AmbientOcclusionOptions$AmbientOcclusionType; /** * Ambient Occlusion radius in meters, between 0 and ~10. */ @@ -1577,7 +1604,8 @@ export interface View$AmbientOcclusionOptions { */ power?: number; /** - * Self-occlusion bias in meters. Use to avoid self-occlusion. Between 0 and a few mm. + * Self-occlusion bias in meters. Use to avoid self-occlusion. + * Between 0 and a few mm. No effect when aoType set to GTAO */ bias?: number; /** @@ -1593,11 +1621,11 @@ export interface View$AmbientOcclusionOptions { */ bilateralThreshold?: number; /** - * affects # of samples used for AO. + * affects # of samples used for AO and params for filtering */ quality?: View$QualityLevel; /** - * affects AO smoothness + * affects AO smoothness. Recommend setting to HIGH when aoType set to GTAO. */ lowPassFilter?: View$QualityLevel; /** @@ -1613,10 +1641,11 @@ export interface View$AmbientOcclusionOptions { */ bentNormals?: boolean; /** - * min angle in radian to consider + * min angle in radian to consider. No effect when aoType set to GTAO. */ minHorizonAngleRad?: number; // JavaScript binding for ssct is not yet supported, must use default value. + // JavaScript binding for gtao is not yet supported, must use default value. } /** diff --git a/web/filament-js/jsbindings_generated.cpp b/web/filament-js/jsbindings_generated.cpp index 07fd54e786..ca7760e308 100644 --- a/web/filament-js/jsbindings_generated.cpp +++ b/web/filament-js/jsbindings_generated.cpp @@ -95,7 +95,14 @@ value_object("View$AmbientOcclusionOptions$ .field("enabled", &View::AmbientOcclusionOptions::Ssct::enabled) ; +value_object("View$AmbientOcclusionOptions$Gtao") + .field("sampleSliceCount", &View::AmbientOcclusionOptions::Gtao::sampleSliceCount) + .field("sampleStepsPerSlice", &View::AmbientOcclusionOptions::Gtao::sampleStepsPerSlice) + .field("thicknessHeuristic", &View::AmbientOcclusionOptions::Gtao::thicknessHeuristic) + ; + value_object("View$AmbientOcclusionOptions") + .field("aoType", &View::AmbientOcclusionOptions::aoType) .field("radius", &View::AmbientOcclusionOptions::radius) .field("power", &View::AmbientOcclusionOptions::power) .field("bias", &View::AmbientOcclusionOptions::bias) @@ -109,6 +116,7 @@ value_object("View$AmbientOcclusionOptions") .field("bentNormals", &View::AmbientOcclusionOptions::bentNormals) .field("minHorizonAngleRad", &View::AmbientOcclusionOptions::minHorizonAngleRad) // JavaScript binding for ssct is not yet supported, must use default value. + // JavaScript binding for gtao is not yet supported, must use default value. ; value_object("View$MultiSampleAntiAliasingOptions") diff --git a/web/filament-js/jsenums_generated.cpp b/web/filament-js/jsenums_generated.cpp index d729b59c3c..638ac38ea3 100644 --- a/web/filament-js/jsenums_generated.cpp +++ b/web/filament-js/jsenums_generated.cpp @@ -33,6 +33,11 @@ enum_("View$DepthOfFieldOptions$Filter") .value("MEDIAN", View::DepthOfFieldOptions::Filter::MEDIAN) ; +enum_("View$AmbientOcclusionOptions$AmbientOcclusionType") + .value("SAO", View::AmbientOcclusionOptions::AmbientOcclusionType::SAO) + .value("GTAO", View::AmbientOcclusionOptions::AmbientOcclusionType::GTAO) + ; + enum_("View$TemporalAntiAliasingOptions$BoxType") .value("AABB", View::TemporalAntiAliasingOptions::BoxType::AABB) .value("VARIANCE", View::TemporalAntiAliasingOptions::BoxType::VARIANCE)