Reduces specular aliasing and preserves the shape of specular highlights as an object moves
away from the camera. This anti-aliasing solution is particularly effective on glossy materials
- (low roughness) but increases the cost of the material. The strengthf of the anti-aliasing
+ (low roughness) but increases the cost of the material. The strength of the anti-aliasing
effect can be controlled using two other properties: specularAntiAliasingVariance and
specularAntiAliasingThreshold.
diff --git a/docs/Materials.md.html b/docs/Materials.md.html
index 36f40622db..7c4a4f736b 100644
--- a/docs/Materials.md.html
+++ b/docs/Materials.md.html
@@ -408,7 +408,7 @@ and with (right)](images/screenshot_normal_mapping.jpg)
### Bent normal
The `bentNormal` property defines the average unoccluded direction at a point on the surface. It is
-used to improve the accuracy of indirect lighting. Bent normals also improve the quality of
+used to improve the accuracy of indirect lighting. Bent normals can also improve the quality of
specular ambient occlusion (see section [Lighting: specularAmbientOcclusion] about
`specularAmbientOcclusion`).
@@ -419,10 +419,6 @@ instance.
![Figure [bentNormalMapped]: Example of a model rendered with and without a bent normal map. Both
versions use the same ambient occlusion map.](images/material_bent_normal.gif)
-!!! Warning
- Using a bent normal map increases the runtime cost of the material, particularly when
- `specularAmbientOcclusion` is turned on.
-
### Clear coat normal
The `clearCoatNormal` property defines the normal of the clear coat layer at a given point. It
@@ -1537,20 +1533,27 @@ occclusion enabled and disabled.](images/screenshot_multi_bounce_ao.gif)
### Lighting: specularAmbientOcclusion
Type
-: `boolean`
+: `string`
Value
-: `true` or `false`. Defaults to `false` on mobile, `true` on desktop.
+: `none`, `simple` or `bentNormals`. Defaults to `none` on mobile, `simple` on desktop. For
+ compatibility reasons, `true` and `false` are also accepted and map respectively to `simple`
+ and `none`.
Description
: Static ambient occlusion maps and dynamic ambient occlusion (SSAO, etc.) apply to diffuse
- indirect lighting. When setting this property to true, a new ambient occlusion term is
- derived from the surface roughness and applied to specular indirec lighting. This effect
- helps remove unwanted specular reflections as shown in figure [specularAO].
+ indirect lighting. When setting this property to other than `none`, a new ambient occlusion
+ term is derived from the surface roughness and applied to specular indirect lighting.
+ This effect helps remove unwanted specular reflections as shown in figure [specularAO].
+ When this value is set to `simple`, Filament uses a cheap but approximate method of computing
+ the specular ambient occlusion term. If this value is set to `bentNormals`, Filament will use
+ a much more accurate but much more expensive method. `bentNormals` only works if the material
+ sets the `bentNormal` property inside the fragment shader. If that property isn't set,
+ `bentNormals` will behave like `simple`.
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ JSON
material {
- specularAmbientOcclusion : true
+ specularAmbientOcclusion : simple
}
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
@@ -1568,7 +1571,7 @@ Value
Description
: Reduces specular aliasing and preserves the shape of specular highlights as an object moves
away from the camera. This anti-aliasing solution is particularly effective on glossy materials
- (low roughness) but increases the cost of the material. The strengthf of the anti-aliasing
+ (low roughness) but increases the cost of the material. The strength of the anti-aliasing
effect can be controlled using two other properties: `specularAntiAliasingVariance` and
`specularAntiAliasingThreshold`.
diff --git a/filament/include/filament/Scene.h b/filament/include/filament/Scene.h
index 6a15463dd8..6dca2229fc 100644
--- a/filament/include/filament/Scene.h
+++ b/filament/include/filament/Scene.h
@@ -84,7 +84,6 @@ public:
*/
void setIndirectLight(IndirectLight const* ibl) noexcept;
-
/**
* Adds an Entity to the Scene.
*
diff --git a/filament/src/Renderer.cpp b/filament/src/Renderer.cpp
index 60d8e58379..232b5faeea 100644
--- a/filament/src/Renderer.cpp
+++ b/filament/src/Renderer.cpp
@@ -766,7 +766,7 @@ bool FRenderer::beginFrame(FSwapChain* swapChain, uint64_t vsyncSteadyClockTimeN
#endif
// latch the frame time
- std::chrono::duration time(userVsync - mUserEpoch);
+ std::chrono::duration time(appVsync - mUserEpoch);
float h = float(time.count());
float l = float(time.count() - h);
mShaderUserTime = { h, l, 0, 0 };
diff --git a/libs/filabridge/include/filament/MaterialEnums.h b/libs/filabridge/include/filament/MaterialEnums.h
index 7944b7195f..c9cc70b990 100644
--- a/libs/filabridge/include/filament/MaterialEnums.h
+++ b/libs/filabridge/include/filament/MaterialEnums.h
@@ -145,15 +145,24 @@ static constexpr size_t MAX_CUSTOM_ATTRIBUTES = 8;
/**
* Material domains
*/
-enum MaterialDomain : uint8_t {
+enum class MaterialDomain : uint8_t {
SURFACE = 0, //!< shaders applied to renderables
POST_PROCESS = 1, //!< shaders applied to rendered buffers
};
+/**
+ * Specular occlusion
+ */
+enum class SpecularAmbientOcclusion : uint8_t {
+ NONE = 0, //!< no specular occlusion
+ SIMPLE = 1, //!< simple specular occlusion
+ BENT_NORMALS = 2, //!< more accurate specular occlusion, requires bent normals
+};
+
/**
* Refraction
*/
-enum RefractionMode : uint8_t {
+enum class RefractionMode : uint8_t {
NONE = 0, //!< no refraction
CUBEMAP = 1, //!< refracted rays go to the ibl cubemap
SCREEN_SPACE = 2, //!< refracted rays go to screen space
@@ -162,7 +171,7 @@ enum RefractionMode : uint8_t {
/**
* Refraction type
*/
-enum RefractionType : uint8_t {
+enum class RefractionType : uint8_t {
SOLID = 0, //!< refraction through solid objects (e.g. a sphere)
THIN = 1, //!< refraction through thin objects (e.g. window)
};
diff --git a/libs/filamat/include/filamat/MaterialBuilder.h b/libs/filamat/include/filamat/MaterialBuilder.h
index 126fe84ccd..ce3974976e 100644
--- a/libs/filamat/include/filamat/MaterialBuilder.h
+++ b/libs/filamat/include/filamat/MaterialBuilder.h
@@ -184,6 +184,7 @@ public:
using Interpolation = filament::Interpolation;
using VertexDomain = filament::VertexDomain;
using TransparencyMode = filament::TransparencyMode;
+ using SpecularAmbientOcclusion = filament::SpecularAmbientOcclusion;
using UniformType = filament::backend::UniformType;
using SamplerType = filament::backend::SamplerType;
@@ -235,12 +236,6 @@ public:
//! Specify the domain that this material will operate in.
MaterialBuilder& materialDomain(MaterialDomain materialDomain) noexcept;
- //! Specify the refraction
- MaterialBuilder& materialRefraction(RefractionMode refraction) noexcept;
-
- //! Specify the refraction type
- MaterialBuilder& materialRefractionType(RefractionType refractionType) noexcept;
-
/**
* Set the code content of this material.
*
@@ -388,8 +383,14 @@ public:
//! Enable / disable multi-bounce ambient occlusion, disabled by default on mobile.
MaterialBuilder& multiBounceAmbientOcclusion(bool multiBounceAO) noexcept;
- //! Enable / disable specular ambient occlusion, disabled by default on mobile.
- MaterialBuilder& specularAmbientOcclusion(bool specularAO) noexcept;
+ //! Set the specular ambient occlusion technique. Disabled by default on mobile.
+ MaterialBuilder& specularAmbientOcclusion(SpecularAmbientOcclusion specularAO) noexcept;
+
+ //! Specify the refraction
+ MaterialBuilder& refractionMode(RefractionMode refraction) noexcept;
+
+ //! Specify the refraction type
+ MaterialBuilder& refractionType(RefractionType refractionType) noexcept;
//! Specifies how transparent objects should be rendered (default is DEFAULT).
MaterialBuilder& transparencyMode(TransparencyMode mode) noexcept;
@@ -573,7 +574,8 @@ private:
bool mMultiBounceAO = false;
bool mMultiBounceAOSet = false;
- bool mSpecularAO = false;
+
+ SpecularAmbientOcclusion mSpecularAO = SpecularAmbientOcclusion::NONE;
bool mSpecularAOSet = false;
};
diff --git a/libs/filamat/src/MaterialBuilder.cpp b/libs/filamat/src/MaterialBuilder.cpp
index 3d9a6a9aab..749c0403a9 100644
--- a/libs/filamat/src/MaterialBuilder.cpp
+++ b/libs/filamat/src/MaterialBuilder.cpp
@@ -210,12 +210,12 @@ MaterialBuilder& MaterialBuilder::materialDomain(MaterialDomain materialDomain)
return *this;
}
-MaterialBuilder& MaterialBuilder::materialRefraction(RefractionMode refraction) noexcept {
+MaterialBuilder& MaterialBuilder::refractionMode(RefractionMode refraction) noexcept {
mRefractionMode = refraction;
return *this;
}
-MaterialBuilder& MaterialBuilder::materialRefractionType(RefractionType refractionType) noexcept {
+MaterialBuilder& MaterialBuilder::refractionType(RefractionType refractionType) noexcept {
mRefractionType = refractionType;
return *this;
}
@@ -303,7 +303,7 @@ MaterialBuilder& MaterialBuilder::multiBounceAmbientOcclusion(bool multiBounceAO
return *this;
}
-MaterialBuilder& MaterialBuilder::specularAmbientOcclusion(bool specularAO) noexcept {
+MaterialBuilder& MaterialBuilder::specularAmbientOcclusion(SpecularAmbientOcclusion specularAO) noexcept {
mSpecularAO = specularAO;
mSpecularAOSet = true;
return *this;
diff --git a/libs/filamat/src/shaders/MaterialInfo.h b/libs/filamat/src/shaders/MaterialInfo.h
index c5fa3a80a2..f1066bd95c 100644
--- a/libs/filamat/src/shaders/MaterialInfo.h
+++ b/libs/filamat/src/shaders/MaterialInfo.h
@@ -41,8 +41,8 @@ struct UTILS_PUBLIC MaterialInfo {
bool flipUV;
bool multiBounceAO;
bool multiBounceAOSet;
- bool specularAO;
bool specularAOSet;
+ filament::SpecularAmbientOcclusion specularAO;
filament::RefractionMode refractionMode;
filament::RefractionType refractionType;
filament::AttributeBitset requiredAttributes;
diff --git a/libs/filamat/src/shaders/ShaderGenerator.cpp b/libs/filamat/src/shaders/ShaderGenerator.cpp
index b2886e149e..6881859539 100644
--- a/libs/filamat/src/shaders/ShaderGenerator.cpp
+++ b/libs/filamat/src/shaders/ShaderGenerator.cpp
@@ -296,32 +296,33 @@ std::string ShaderGenerator::createFragmentProgram(filament::backend::ShaderMode
cg.generateDefine(fs, "MAX_SHADOW_CASTING_SPOTS", uint32_t(CONFIG_MAX_SHADOW_CASTING_SPOTS));
- bool specularAO = material.specularAOSet ?
- material.specularAO : !isMobileTarget(shaderModel);
- cg.generateDefine(fs, "SPECULAR_AMBIENT_OCCLUSION", specularAO ? 1u : 0u);
+ auto defaultSpecularAO = isMobileTarget(shaderModel) ?
+ SpecularAmbientOcclusion::NONE : SpecularAmbientOcclusion::SIMPLE;
+ auto specularAO = material.specularAOSet ? material.specularAO : defaultSpecularAO;
+ cg.generateDefine(fs, "SPECULAR_AMBIENT_OCCLUSION", uint32_t(specularAO));
cg.generateDefine(fs, "HAS_REFRACTION", material.refractionMode != RefractionMode::NONE);
if (material.refractionMode != RefractionMode::NONE) {
cg.generateDefine(fs, "REFRACTION_MODE_CUBEMAP", uint32_t(RefractionMode::CUBEMAP));
cg.generateDefine(fs, "REFRACTION_MODE_SCREEN_SPACE", uint32_t(RefractionMode::SCREEN_SPACE));
switch (material.refractionMode) {
- case NONE:
+ case RefractionMode::NONE:
// can't be here
break;
- case CUBEMAP:
+ case RefractionMode::CUBEMAP:
cg.generateDefine(fs, "REFRACTION_MODE", "REFRACTION_MODE_CUBEMAP");
break;
- case SCREEN_SPACE:
+ case RefractionMode::SCREEN_SPACE:
cg.generateDefine(fs, "REFRACTION_MODE", "REFRACTION_MODE_SCREEN_SPACE");
break;
}
cg.generateDefine(fs, "REFRACTION_TYPE_SOLID", uint32_t(RefractionType::SOLID));
cg.generateDefine(fs, "REFRACTION_TYPE_THIN", uint32_t(RefractionType::THIN));
switch (material.refractionType) {
- case SOLID:
+ case RefractionType::SOLID:
cg.generateDefine(fs, "REFRACTION_TYPE", "REFRACTION_TYPE_SOLID");
break;
- case THIN:
+ case RefractionType::THIN:
cg.generateDefine(fs, "REFRACTION_TYPE", "REFRACTION_TYPE_THIN");
break;
}
diff --git a/libs/filamat/tests/test_filamat.cpp b/libs/filamat/tests/test_filamat.cpp
index 1821f8d231..e3882c590d 100644
--- a/libs/filamat/tests/test_filamat.cpp
+++ b/libs/filamat/tests/test_filamat.cpp
@@ -54,7 +54,7 @@ std::string shaderWithAllProperties(ShaderType type,
builder.optimization(filamat::MaterialBuilder::Optimization::NONE);
builder.shading(shadingModel);
builder.includeCallback(includer);
- builder.materialRefraction(refractionMode);
+ builder.refractionMode(refractionMode);
MaterialBuilder::PropertyList allProperties;
std::fill_n(allProperties, MaterialBuilder::MATERIAL_PROPERTIES_COUNT, true);
diff --git a/libs/gltfio/materials/ubershader.mat.in b/libs/gltfio/materials/ubershader.mat.in
index f341a31213..1c141b80fe 100644
--- a/libs/gltfio/materials/ubershader.mat.in
+++ b/libs/gltfio/materials/ubershader.mat.in
@@ -6,7 +6,7 @@ material {
depthWrite : true,
doubleSided : ${DOUBLESIDED},
flipUV : false,
- specularAmbientOcclusion: true,
+ specularAmbientOcclusion: simple,
parameters : [
{ type : float3, name : specularFactor },
diff --git a/libs/gltfio/src/MaterialGenerator.cpp b/libs/gltfio/src/MaterialGenerator.cpp
index 2943240f58..7ab78fb07b 100644
--- a/libs/gltfio/src/MaterialGenerator.cpp
+++ b/libs/gltfio/src/MaterialGenerator.cpp
@@ -242,7 +242,7 @@ Material* createMaterial(Engine* engine, const MaterialKey& config, const UvMap&
MaterialBuilder builder = MaterialBuilder()
.name(name)
.flipUV(false)
- .specularAmbientOcclusion(true)
+ .specularAmbientOcclusion(MaterialBuilder::SpecularAmbientOcclusion::SIMPLE)
.material(shader.c_str())
.doubleSided(config.doubleSided)
.targetApi(filamat::targetApiFromBackend(engine->getBackend()));
diff --git a/samples/sample_full_pbr.cpp b/samples/sample_full_pbr.cpp
index 29b6e6853f..2629dbee59 100644
--- a/samples/sample_full_pbr.cpp
+++ b/samples/sample_full_pbr.cpp
@@ -362,7 +362,7 @@ static void setup(Engine* engine, View* view, Scene* scene) {
.name("DefaultMaterial")
.material(shader.c_str())
.multiBounceAmbientOcclusion(true)
- .specularAmbientOcclusion(true)
+ .specularAmbientOcclusion(MaterialBuilder::SpecularAmbientOcclusion::BENT_NORMALS)
.shading(Shading::LIT);
if (hasUV) {
diff --git a/shaders/src/ambient_occlusion.fs b/shaders/src/ambient_occlusion.fs
index c723fa0589..8b7acacfa9 100644
--- a/shaders/src/ambient_occlusion.fs
+++ b/shaders/src/ambient_occlusion.fs
@@ -1,3 +1,12 @@
+//------------------------------------------------------------------------------
+// Ambient occlusion configuration
+//------------------------------------------------------------------------------
+
+// Diffuse BRDFs
+#define SPECULAR_AO_OFF 0
+#define SPECULAR_AO_SIMPLE 1
+#define SPECULAR_AO_BENT_NORMALS 2
+
//------------------------------------------------------------------------------
// Ambient occlusion helpers
//------------------------------------------------------------------------------
@@ -8,7 +17,6 @@ float evaluateSSAO() {
return textureLod(light_ssao, uv, 0.0).r;
}
-#if SPECULAR_AMBIENT_OCCLUSION == 1
float SpecularAO_Lagarde(float NoV, float visibility, float roughness) {
// Lagarde and de Rousiers 2014, "Moving Frostbite to PBR"
return saturate(pow(NoV + visibility, exp2(-16.0 * roughness - 1.0)) - 1.0 + visibility);
@@ -39,9 +47,11 @@ float sphericalCapsIntersection(float cosCap1, float cosCap2, float cosDistance)
float area = sq(x) * (-2.0 * x + 3.0);
return area * (1.0 - max(cosCap1, cosCap2));
}
+#endif
// This function could (should?) be implemented as a 3D LUT instead, but we need to save samplers
-float SpecularAO_Cones(float visibility, float roughness) {
+float SpecularAO_Cones(float NoV, float visibility, float roughness) {
+#if defined(MATERIAL_HAS_BENT_NORMAL)
// Jimenez et al. 2016, "Practical Realtime Strategies for Accurate Indirect Occlusion"
// aperture from ambient occlusion
@@ -56,22 +66,23 @@ float SpecularAO_Cones(float visibility, float roughness) {
float ao = sphericalCapsIntersection(cosAv, cosAs, 0.5 * cosB + 0.5) / (1.0 - cosAs);
// Smoothly kill specular AO when entering the perceptual roughness range [0.1..0.3]
// Without this, specular AO can remove all reflections, which looks bad on metals
+ if (fract(getUserTime().x) < 0.5)
+ return SpecularAO_Lagarde(NoV, visibility, roughness);
+ else
return mix(1.0, ao, smoothstep(0.01, 0.09, roughness));
+#else
+ return SpecularAO_Lagarde(NoV, visibility, roughness);
+#endif
}
-#endif
-
-#endif
/**
* Computes a specular occlusion term from the ambient occlusion term.
*/
float computeSpecularAO(float NoV, float visibility, float roughness) {
-#if SPECULAR_AMBIENT_OCCLUSION == 1
-#if defined(MATERIAL_HAS_BENT_NORMAL)
- return SpecularAO_Cones(visibility, roughness);
-#else
+#if SPECULAR_AMBIENT_OCCLUSION == SPECULAR_AO_SIMPLE
return SpecularAO_Lagarde(NoV, visibility, roughness);
-#endif
+#elif SPECULAR_AMBIENT_OCCLUSION == SPECULAR_AO_BENT_NORMALS
+ return SpecularAO_Cones(NoV, visibility, roughness);
#else
return 1.0;
#endif
@@ -100,7 +111,7 @@ void multiBounceAO(float visibility, const vec3 albedo, inout vec3 color) {
}
void multiBounceSpecularAO(float visibility, const vec3 albedo, inout vec3 color) {
-#if MULTI_BOUNCE_AMBIENT_OCCLUSION == 1 && SPECULAR_AMBIENT_OCCLUSION == 1
+#if MULTI_BOUNCE_AMBIENT_OCCLUSION == 1 && SPECULAR_AMBIENT_OCCLUSION != SPECULAR_AO_OFF
color *= gtaoMultiBounce(visibility, albedo);
#endif
}
diff --git a/tools/matc/src/matc/ParametersProcessor.cpp b/tools/matc/src/matc/ParametersProcessor.cpp
index b28352479a..247f2ca11e 100644
--- a/tools/matc/src/matc/ParametersProcessor.cpp
+++ b/tools/matc/src/matc/ParametersProcessor.cpp
@@ -429,7 +429,20 @@ static bool processMultiBounceAO(MaterialBuilder& builder, const JsonishValue& v
}
static bool processSpecularAmbientOcclusion(MaterialBuilder& builder, const JsonishValue& value) {
- builder.specularAmbientOcclusion(value.toJsonBool()->getBool());
+ static const std::unordered_map strToEnum {
+ { "none", MaterialBuilder::SpecularAmbientOcclusion::NONE },
+ { "simple", MaterialBuilder::SpecularAmbientOcclusion::SIMPLE },
+ { "bentNormals", MaterialBuilder::SpecularAmbientOcclusion::BENT_NORMALS },
+ // Backward compatibility
+ { "false", MaterialBuilder::SpecularAmbientOcclusion::NONE },
+ { "true", MaterialBuilder::SpecularAmbientOcclusion::SIMPLE },
+ };
+ auto jsonString = value.toJsonString();
+ if (!isStringValidEnum(strToEnum, jsonString->getString())) {
+ return logEnumIssue("specularAO", *jsonString, strToEnum);
+ }
+
+ builder.specularAmbientOcclusion(stringToEnum(strToEnum, jsonString->getString()));
return true;
}
@@ -475,7 +488,7 @@ static bool processRefractionMode(MaterialBuilder& builder, const JsonishValue&
return logEnumIssue("refraction", *jsonString, strToEnum);
}
- builder.materialRefraction(stringToEnum(strToEnum, jsonString->getString()));
+ builder.refractionMode(stringToEnum(strToEnum, jsonString->getString()));
return true;
}
@@ -489,7 +502,7 @@ static bool processRefractionType(MaterialBuilder& builder, const JsonishValue&
return logEnumIssue("refractionType", *jsonString, strToEnum);
}
- builder.materialRefractionType(stringToEnum(strToEnum, jsonString->getString()));
+ builder.refractionType(stringToEnum(strToEnum, jsonString->getString()));
return true;
}
@@ -556,7 +569,7 @@ ParametersProcessor::ParametersProcessor() {
mParameters["clearCoatIorChange"] = { &processClearCoatIorChange, Type::BOOL };
mParameters["flipUV"] = { &processFlipUV, Type::BOOL };
mParameters["multiBounceAmbientOcclusion"] = { &processMultiBounceAO, Type::BOOL };
- mParameters["specularAmbientOcclusion"] = { &processSpecularAmbientOcclusion, Type::BOOL };
+ mParameters["specularAmbientOcclusion"] = { &processSpecularAmbientOcclusion, Type::STRING };
mParameters["domain"] = { &processDomain, Type::STRING };
mParameters["refractionMode"] = { &processRefractionMode, Type::STRING };
mParameters["refractionType"] = { &processRefractionType, Type::STRING };