diff --git a/RELEASE_NOTES.md b/RELEASE_NOTES.md
index d7eafe573b..9a862312a8 100644
--- a/RELEASE_NOTES.md
+++ b/RELEASE_NOTES.md
@@ -24,6 +24,7 @@ A new header is inserted each time a *tag* is created.
- WebGL: Simplified callback API for glTF. (⚠ API Change)
- gltfio: Removed deprecated "Bindings" API. (⚠ API Change)
- Add support for screen-space contact shadows
+- Add support for fog
## v1.4.5
diff --git a/android/filament-android/src/main/cpp/View.cpp b/android/filament-android/src/main/cpp/View.cpp
index 223e8d79a8..d88e41489c 100644
--- a/android/filament-android/src/main/cpp/View.cpp
+++ b/android/filament-android/src/main/cpp/View.cpp
@@ -270,3 +270,23 @@ Java_com_google_android_filament_View_nSetBloomOptions(JNIEnv*, jclass,
};
view->setBloomOptions(options);
}
+
+extern "C" JNIEXPORT void JNICALL
+Java_com_google_android_filament_View_nSetFogOptions(JNIEnv *, jclass , jlong nativeView,
+ jfloat distance, jfloat maximumOpacity, jfloat height, jfloat heightFalloff, jfloat r,
+ jfloat g, jfloat b, jfloat density, jfloat inScatteringStart,
+ jfloat inScatteringSize, jboolean enabled) {
+ View* view = (View*) nativeView;
+ View::FogOptions options = {
+ .distance = distance,
+ .maximumOpacity = maximumOpacity,
+ .height = height,
+ .heightFalloff = heightFalloff,
+ .color = math::float3{r, g, b},
+ .density = density,
+ .inScatteringStart = inScatteringStart,
+ .inScatteringSize = inScatteringSize,
+ .enabled = (bool)enabled
+ };
+ view->setFogOptions(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 2a02d73b4a..65e7153f16 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
@@ -64,6 +64,7 @@ public class View {
private RenderQuality mRenderQuality;
private AmbientOcclusionOptions mAmbientOcclusionOptions;
private BloomOptions mBloomOptions;
+ private FogOptions mFogOptions;
private RenderTarget mRenderTarget;
/**
@@ -263,6 +264,59 @@ public class View {
public boolean enabled = false;
}
+ /**
+ * Options to control fog in the scene
+ *
+ * @see setFogOptions
+ */
+ public static class FogOptions {
+ /**
+ * distance in world units from the camera where the fog starts ( >= 0.0 )
+ */
+ public float distance = 1.0f;
+
+ /**
+ * fog's maximum opacity between 0 and 1
+ */
+ public float maximumOpacity = 1.0f;
+
+ /**
+ * fog's floor in world units
+ */
+ public float height = 0.0f;
+
+ /**
+ * how fast fog dissipates with altitude
+ */
+ public float heightFalloff = 1.0f;
+
+ /**
+ * fog's color (linear)
+ */
+ @NonNull
+ public float[] color = { 0.5f, 0.5f, 0.5f };
+
+ /**
+ * fog's density at altitude given by 'height'
+ */
+ public float density = 0.1f;
+
+ /**
+ * distance in world units from the camera where in-scattering starts
+ */
+ public float inScatteringStart = 0.0f;
+
+ /**
+ * size of in-scattering (>0 to activate)
+ */
+ public float inScatteringSize = 0.0f;
+
+ /**
+ * enable or disable fog
+ */
+ public boolean enabled = false;
+ }
+
/**
* Structure used to set the color precision for the rendering of a View.
*
@@ -842,6 +896,19 @@ public class View {
options.threshold, options.enabled);
}
+ /**
+ * Sets fog options.
+ *
+ * @param options Options for fog.
+ */
+ public void setFogOptions(@NonNull FogOptions options) {
+ mFogOptions = options;
+ nSetFogOptions(getNativeObject(), options.distance, options.maximumOpacity, options.height,
+ options.heightFalloff, options.color[0], options.color[1], options.color[2],
+ options.density, options.inScatteringStart, options.inScatteringSize,
+ options.enabled);
+ }
+
/**
* Gets the bloom options
*
@@ -898,4 +965,6 @@ public class View {
private static native int nGetAmbientOcclusion(long nativeView);
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);
+ private static native void nSetFogOptions(long nativeView, float distance, float maximumOpacity, float height, float heightFalloff, float v, float v1, float v2, float density, float inScatteringStart, float inScatteringSize, boolean enabled);
+
}
diff --git a/filament/include/filament/View.h b/filament/include/filament/View.h
index 15dfd8f552..a81b31fa01 100644
--- a/filament/include/filament/View.h
+++ b/filament/include/filament/View.h
@@ -154,17 +154,32 @@ public:
ADD, //!< Bloom is modulated by the strength parameter and added to the scene
INTERPOLATE //!< Bloom is interpolated with the scene using the strength parameter
};
- Texture* dirt = nullptr; //!< User provided dirt texture
+ Texture* dirt = nullptr; //!< user provided dirt texture
float dirtStrength = 0.2f; //!< strength of the dirt texture
- float strength = 0.10f; //!< Between 0.0 and 1.0
- uint32_t resolution = 360; //!< Resolution of minor axis (2^levels to 4096)
- float anamorphism = 1.0f; //!< Bloom x/y aspect-ratio (1/32 to 32)
+ float strength = 0.10f; //!< bloom's strength between 0.0 and 1.0
+ uint32_t resolution = 360; //!< resolution of minor axis (2^levels to 4096)
+ float anamorphism = 1.0f; //!< bloom x/y aspect-ratio (1/32 to 32)
uint8_t levels = 6; //!< number of blur levels (3 to 12)
- BlendMode blendMode = BlendMode::ADD; //!< How the bloom effect is applied
- bool threshold = true; //!< Whether to threshold the source
+ BlendMode blendMode = BlendMode::ADD; //!< how the bloom effect is applied
+ bool threshold = true; //!< whether to threshold the source
bool enabled = false; //!< enable or disable bloom
};
+ /**
+ * Options to control fog in the scene
+ */
+ struct FogOptions {
+ float distance = 1.0f; //!< distance in world units from the camera where the fog starts ( >= 0.0 )
+ float maximumOpacity = 1.0f; //!< fog's maximum opacity between 0 and 1
+ float height = 0.0f; //!< fog's floor in world units
+ float heightFalloff = 1.0f; //!< how fast fog dissipates with altitude
+ math::float3 color{ 0.5f }; //!< fog's color (linear)
+ float density = 0.1f; //!< fog's density at altitude given by 'height'
+ float inScatteringStart = 0.0f; //!< distance in world units from the camera where in-scattering starts
+ float inScatteringSize = 0.0f; //!< size of in-scattering (>0 to activate)
+ bool enabled = false; //!< enable or disable fog
+ };
+
/**
* Structure used to set the precision of the color buffer and related quality settings.
*
@@ -497,10 +512,17 @@ public:
/**
* Enables or disables bloom in the post-processing stage. Disabled by default.
*
- * @param bloom options
+ * @param options options
*/
void setBloomOptions(BloomOptions options) noexcept;
+ /**
+ * Enables or disables fog. Disabled by default.
+ *
+ * @param options options
+ */
+ void setFogOptions(FogOptions options) noexcept;
+
/**
* Queries the bloom options.
*
diff --git a/filament/src/RenderPass.cpp b/filament/src/RenderPass.cpp
index 24cf09c5d3..13eae9da5a 100644
--- a/filament/src/RenderPass.cpp
+++ b/filament/src/RenderPass.cpp
@@ -375,6 +375,7 @@ void RenderPass::generateCommandsImpl(uint32_t extraFlags,
Variant materialVariant;
materialVariant.setDirectionalLighting(renderFlags & HAS_DIRECTIONAL_LIGHT);
materialVariant.setDynamicLighting(renderFlags & HAS_DYNAMIC_LIGHTING);
+ materialVariant.setFog(renderFlags & HAS_FOG);
materialVariant.setShadowReceiver(false); // this is set per Renderable
Command cmdColor;
diff --git a/filament/src/RenderPass.h b/filament/src/RenderPass.h
index beccfc1a6e..011d22180d 100644
--- a/filament/src/RenderPass.h
+++ b/filament/src/RenderPass.h
@@ -50,14 +50,14 @@ public:
static constexpr uint64_t BLEND_TWO_PASS_MASK = 0x1llu;
static constexpr unsigned BLEND_TWO_PASS_SHIFT = 0;
- static constexpr uint64_t MATERIAL_INSTANCE_ID_MASK = 0x0000FFFFllu;
+ static constexpr uint64_t MATERIAL_INSTANCE_ID_MASK = 0x00000FFFllu;
static constexpr unsigned MATERIAL_INSTANCE_ID_SHIFT = 0;
- static constexpr uint64_t MATERIAL_VARIANT_KEY_MASK = 0x001F0000llu;
- static constexpr unsigned MATERIAL_VARIANT_KEY_SHIFT = 16;
+ static constexpr uint64_t MATERIAL_VARIANT_KEY_MASK = 0x000FF000llu;
+ static constexpr unsigned MATERIAL_VARIANT_KEY_SHIFT = 12;
- static constexpr uint64_t MATERIAL_ID_MASK = 0xFFE00000llu;
- static constexpr unsigned MATERIAL_ID_SHIFT = 21;
+ static constexpr uint64_t MATERIAL_ID_MASK = 0xFFF00000llu;
+ static constexpr unsigned MATERIAL_ID_SHIFT = 20;
static constexpr uint64_t BLEND_DISTANCE_MASK = 0xFFFFFFFF0000llu;
static constexpr unsigned BLEND_DISTANCE_SHIFT = 16;
@@ -180,10 +180,10 @@ public:
// The sorting material key is 32 bits and encoded as:
//
- // | 11 | 5 | 16 |
- // +-----------+-----+----------------+
- // | material | var | instance |
- // +-----------+-----+----------------+
+ // | 12 | 8 | 12 |
+ // +------------+--------+------------+
+ // | material |variant | instance |
+ // +------------+--------+------------+
//
// The variant is inserted while building the commands, because we don't know it before that
//
@@ -238,6 +238,7 @@ public:
static constexpr RenderFlags HAS_DIRECTIONAL_LIGHT = 0x02;
static constexpr RenderFlags HAS_DYNAMIC_LIGHTING = 0x04;
static constexpr RenderFlags HAS_INVERSE_FRONT_FACES = 0x08;
+ static constexpr RenderFlags HAS_FOG = 0x10;
RenderPass(FEngine& engine, utils::GrowingSlice commands) noexcept;
diff --git a/filament/src/Renderer.cpp b/filament/src/Renderer.cpp
index 0c4071b859..22d3e4544a 100644
--- a/filament/src/Renderer.cpp
+++ b/filament/src/Renderer.cpp
@@ -239,6 +239,7 @@ void FRenderer::renderJob(ArenaScope& arena, FView& view) {
if (view.hasShadowing()) renderFlags |= RenderPass::HAS_SHADOWING;
if (view.hasDirectionalLight()) renderFlags |= RenderPass::HAS_DIRECTIONAL_LIGHT;
if (view.hasDynamicLighting()) renderFlags |= RenderPass::HAS_DYNAMIC_LIGHTING;
+ if (view.hasFog()) renderFlags |= RenderPass::HAS_FOG;
if (view.isFrontFaceWindingInverted()) renderFlags |= RenderPass::HAS_INVERSE_FRONT_FACES;
pass.setRenderFlags(renderFlags);
diff --git a/filament/src/View.cpp b/filament/src/View.cpp
index 7c9b94e692..fd67bce03a 100644
--- a/filament/src/View.cpp
+++ b/filament/src/View.cpp
@@ -602,10 +602,22 @@ void FView::prepare(FEngine& engine, backend::DriverApi& driver, ArenaScope& are
* Update driver state
*/
+ auto& u = mPerViewUb;
+
const uint64_t oneSecondRemainder = engine.getEngineTime().count() % 1000000000;
const float fraction = float(double(oneSecondRemainder) / 1000000000.0);
- mPerViewUb.setUniform(offsetof(PerViewUib, time), fraction);
- mPerViewUb.setUniform(offsetof(PerViewUib, userTime), userTime);
+ u.setUniform(offsetof(PerViewUib, time), fraction);
+ u.setUniform(offsetof(PerViewUib, userTime), userTime);
+
+ auto const& fogOptions = mFogOptions;
+ u.setUniform(offsetof(PerViewUib, fogStart), fogOptions.distance);
+ u.setUniform(offsetof(PerViewUib, fogMaxOpacity), fogOptions.maximumOpacity);
+ u.setUniform(offsetof(PerViewUib, fogHeight), fogOptions.height);
+ u.setUniform(offsetof(PerViewUib, fogHeightFalloff), fogOptions.heightFalloff);
+ u.setUniform(offsetof(PerViewUib, fogColor), fogOptions.color);
+ u.setUniform(offsetof(PerViewUib, fogDensity), fogOptions.density);
+ u.setUniform(offsetof(PerViewUib, fogInscatteringStart), fogOptions.inScatteringStart);
+ u.setUniform(offsetof(PerViewUib, fogInscatteringSize), fogOptions.inScatteringSize);
// upload the renderables's dirty UBOs
engine.getRenderableManager().prepare(driver,
@@ -693,6 +705,8 @@ void FView::prepareCamera(const CameraInfo& camera, const filament::Viewport& vi
u.setUniform(offsetof(PerViewUib, cameraPosition), float3{camera.getPosition()});
u.setUniform(offsetof(PerViewUib, worldOffset), camera.worldOffset);
+ u.setUniform(offsetof(PerViewUib, cameraNear), camera.zn);
+ u.setUniform(offsetof(PerViewUib, cameraFar), camera.zf);
}
void FView::prepareSSAO(Handle ssao) const noexcept {
@@ -1035,6 +1049,10 @@ void View::setBloomOptions(View::BloomOptions options) noexcept {
upcast(this)->setBloomOptions(options);
}
+void View::setFogOptions(View::FogOptions options) noexcept {
+ upcast(this)->setFogOptions(options);
+}
+
View::BloomOptions View::getBloomOptions() const noexcept {
return upcast(this)->getBloomOptions();
}
diff --git a/filament/src/details/View.h b/filament/src/details/View.h
index bd7c26d153..a275556e60 100644
--- a/filament/src/details/View.h
+++ b/filament/src/details/View.h
@@ -173,6 +173,7 @@ public:
bool hasDirectionalLight() const noexcept { return mHasDirectionalLight; }
bool hasDynamicLighting() const noexcept { return mHasDynamicLighting; }
bool hasShadowing() const noexcept { return mHasShadowing; }
+ bool hasFog() const noexcept { return mFogOptions.enabled && mFogOptions.density > 0.0f; }
void renderShadowMaps(FEngine& engine, FEngine::DriverApi& driver, RenderPass& pass) noexcept;
@@ -286,6 +287,16 @@ public:
mBloomOptions = options;
}
+ void setFogOptions(FogOptions options) noexcept {
+ options.distance = std::max(0.0f, options.distance);
+ options.maximumOpacity = math::clamp(options.maximumOpacity, 0.0f, 1.0f);
+ options.density = std::max(0.0f, options.density);
+ options.heightFalloff = std::max(0.0f, options.heightFalloff);
+ options.inScatteringSize = std::max(0.0f, options.inScatteringSize);
+ options.inScatteringStart = std::max(0.0f, options.inScatteringStart);
+ mFogOptions = options;
+ }
+
BloomOptions getBloomOptions() const noexcept {
return mBloomOptions;
}
@@ -390,6 +401,7 @@ private:
AmbientOcclusion mAmbientOcclusion = AmbientOcclusion::NONE;
AmbientOcclusionOptions mAmbientOcclusionOptions{};
BloomOptions mBloomOptions;
+ FogOptions mFogOptions;
using duration = std::chrono::duration;
DynamicResolutionOptions mDynamicResolution;
diff --git a/libs/filabridge/include/private/filament/UibGenerator.h b/libs/filabridge/include/private/filament/UibGenerator.h
index be42b1c380..b68d81ca6e 100644
--- a/libs/filabridge/include/private/filament/UibGenerator.h
+++ b/libs/filabridge/include/private/filament/UibGenerator.h
@@ -55,7 +55,10 @@ struct PerViewUib { // NOLINT(cppcoreguidelines-pro-type-member-init)
filament::math::float4 resolution; // viewport width, height, 1/width, 1/height
- filament::math::float3 cameraPosition; // this is (0,0,0) when camera_at_origin is enabled
+ // camera position in view space (when camera_at_origin is enabled), i.e. it's (0,0,0).
+ // Always add worldOffset in the shader to get the true world-space position of the camera.
+ filament::math::float3 cameraPosition;
+
float time; // time in seconds, with a 1 second period
filament::math::float4 lightColorIntensity; // directional light
@@ -92,8 +95,21 @@ struct PerViewUib { // NOLINT(cppcoreguidelines-pro-type-member-init)
filament::math::float3 worldOffset; // this is (0,0,0) when camera_at_origin is disabled
float ssContactShadowDistance;
+ // fog
+ float fogStart;
+ float fogMaxOpacity;
+ float fogHeight;
+ float fogHeightFalloff;
+ math::float3 fogColor;
+ float fogDensity;
+ float fogInscatteringStart;
+ float fogInscatteringSize;
+
+ float cameraNear; // camera near-plane distance (positive value)
+ float cameraFar; // camera *culling* far-plane distance (projection far is at +inf)
+
// bring PerViewUib to 1 KiB
- filament::math::float4 padding2[15];
+ filament::math::float4 padding2[12];
};
diff --git a/libs/filabridge/include/private/filament/Variant.h b/libs/filabridge/include/private/filament/Variant.h
index eefc39ea0b..0d09af1a02 100644
--- a/libs/filabridge/include/private/filament/Variant.h
+++ b/libs/filabridge/include/private/filament/Variant.h
@@ -21,7 +21,7 @@
#include
namespace filament {
- static constexpr size_t VARIANT_COUNT = 32;
+ static constexpr size_t VARIANT_COUNT = 64;
// IMPORTANT: update filterVariant() when adding more variants
struct Variant {
@@ -34,21 +34,22 @@ namespace filament {
// SRE: Shadow Receiver
// SKN: Skinning
// DEP: Depth only
+ // FOG: Fog
//
// X: either 1 or 0
//
- // ...-----+-----+-----+-----+-----+-----+
- // Variant 0 | DEP | SKN | SRE | DYN | DIR |
- // ...-----+-----+-----+-----+-----+-----+
+ // ...-----+-----+-----+-----+-----+-----+-----+
+ // Variant 0 | FOG | DEP | SKN | SRE | DYN | DIR |
+ // ...-----+-----+-----+-----+-----+-----+-----+
// Reserved variants:
- // Vertex depth 1 X 0 0 0
- // Fragment depth 1 0 0 0 0
- // Reserved 1 X X X X
- // Reserved 0 X 1 0 0
+ // Vertex depth 0 1 X 0 0 0
+ // Fragment depth 0 1 0 0 0 0
+ // Reserved X 1 X X X X
+ // Reserved X 0 X 1 0 0
//
// Standard variants:
- // Vertex shader 0 X X 0 X
- // Fragment shader 0 0 X X X
+ // Vertex shader 0 0 X X 0 X
+ // Fragment shader X 0 0 X X X
uint8_t key = 0;
@@ -59,6 +60,7 @@ namespace filament {
static constexpr uint8_t SHADOW_RECEIVER = 0x04; // receives shadows, per renderable
static constexpr uint8_t SKINNING_OR_MORPHING = 0x08; // GPU skinning and/or morphing
static constexpr uint8_t DEPTH = 0x10; // depth only variants
+ static constexpr uint8_t FOG = 0x20; // fog
static constexpr uint8_t VERTEX_MASK = DIRECTIONAL_LIGHTING |
SHADOW_RECEIVER |
@@ -68,6 +70,7 @@ namespace filament {
static constexpr uint8_t FRAGMENT_MASK = DIRECTIONAL_LIGHTING |
DYNAMIC_LIGHTING |
SHADOW_RECEIVER |
+ FOG |
DEPTH;
static constexpr uint8_t DEPTH_MASK = DIRECTIONAL_LIGHTING |
@@ -80,17 +83,19 @@ namespace filament {
static constexpr uint8_t DEPTH_VARIANT = DEPTH;
// this mask filters out the lighting variants
- static constexpr uint8_t UNLIT_MASK = SKINNING_OR_MORPHING;
+ static constexpr uint8_t UNLIT_MASK = SKINNING_OR_MORPHING | FOG;
inline bool hasSkinningOrMorphing() const noexcept { return key & SKINNING_OR_MORPHING; }
inline bool hasDirectionalLighting() const noexcept { return key & DIRECTIONAL_LIGHTING; }
inline bool hasDynamicLighting() const noexcept { return key & DYNAMIC_LIGHTING; }
inline bool hasShadowReceiver() const noexcept { return key & SHADOW_RECEIVER; }
+ inline bool hasFog() const noexcept { return key & FOG; }
inline void setSkinning(bool v) noexcept { set(v, SKINNING_OR_MORPHING); }
inline void setDirectionalLighting(bool v) noexcept { set(v, DIRECTIONAL_LIGHTING); }
inline void setDynamicLighting(bool v) noexcept { set(v, DYNAMIC_LIGHTING); }
inline void setShadowReceiver(bool v) noexcept { set(v, SHADOW_RECEIVER); }
+ inline void setFog(bool v) noexcept { set(v, FOG); }
inline constexpr bool isDepthPass() const noexcept {
return (key & DEPTH_MASK) == DEPTH_VARIANT;
@@ -98,9 +103,7 @@ namespace filament {
static constexpr bool isReserved(uint8_t variantKey) noexcept {
// reserved variants that should just be skipped
- return (variantKey & DEPTH_MASK) > DEPTH ||
- variantKey == 0b00100 ||
- variantKey == 0b01100;
+ return (variantKey & DEPTH_MASK) > DEPTH || (variantKey & 0b010111u) == 0b000100u;
}
static constexpr uint8_t filterVariantVertex(uint8_t variantKey) noexcept {
diff --git a/libs/filabridge/src/UibGenerator.cpp b/libs/filabridge/src/UibGenerator.cpp
index 1e747c12af..c852c5f28c 100644
--- a/libs/filabridge/src/UibGenerator.cpp
+++ b/libs/filabridge/src/UibGenerator.cpp
@@ -80,8 +80,21 @@ UniformInterfaceBlock const& UibGenerator::getPerViewUib() noexcept {
// view
.add("worldOffset", 1, UniformInterfaceBlock::Type::FLOAT3)
.add("ssContactShadowDistance", 1, UniformInterfaceBlock::Type::FLOAT)
+ // fog
+ .add("fogStart", 1, UniformInterfaceBlock::Type::FLOAT)
+ .add("fogMaxOpacity", 1, UniformInterfaceBlock::Type::FLOAT)
+ .add("fogHeight", 1, UniformInterfaceBlock::Type::FLOAT)
+ .add("fogHeightFalloff", 1, UniformInterfaceBlock::Type::FLOAT)
+ .add("fogColor", 1, UniformInterfaceBlock::Type::FLOAT3)
+ .add("fogDensity", 1, UniformInterfaceBlock::Type::FLOAT)
+ .add("fogInscatteringStart", 1, UniformInterfaceBlock::Type::FLOAT)
+ .add("fogInscatteringSize", 1, UniformInterfaceBlock::Type::FLOAT)
+ // more camera stuff
+ .add("cameraNear", 1, UniformInterfaceBlock::Type::FLOAT)
+ .add("cameraFar", 1, UniformInterfaceBlock::Type::FLOAT)
+
// bring size to 1 KiB
- .add("padding2", 15, UniformInterfaceBlock::Type::FLOAT4)
+ .add("padding2", 12, UniformInterfaceBlock::Type::FLOAT4)
.build();
return uib;
}
diff --git a/libs/filamat/src/shaders/CodeGenerator.cpp b/libs/filamat/src/shaders/CodeGenerator.cpp
index 3119f49390..48f3b05eaf 100644
--- a/libs/filamat/src/shaders/CodeGenerator.cpp
+++ b/libs/filamat/src/shaders/CodeGenerator.cpp
@@ -411,6 +411,7 @@ io::sstream& CodeGenerator::generateCommon(io::sstream& out, ShaderType type) co
out << SHADERS_COMMON_SHADING_FS_DATA;
out << SHADERS_COMMON_GRAPHICS_FS_DATA;
out << SHADERS_COMMON_MATERIAL_FS_DATA;
+ out << SHADERS_FOG_FS_DATA;
}
return out;
}
diff --git a/libs/filamat/src/shaders/ShaderGenerator.cpp b/libs/filamat/src/shaders/ShaderGenerator.cpp
index 2eabcf4dd2..6933263737 100644
--- a/libs/filamat/src/shaders/ShaderGenerator.cpp
+++ b/libs/filamat/src/shaders/ShaderGenerator.cpp
@@ -150,7 +150,7 @@ ShaderGenerator::ShaderGenerator(
}
}
-const std::string ShaderGenerator::createVertexProgram(filament::backend::ShaderModel shaderModel,
+std::string ShaderGenerator::createVertexProgram(filament::backend::ShaderModel shaderModel,
MaterialBuilder::TargetApi targetApi, MaterialBuilder::TargetLanguage targetLanguage,
MaterialInfo const& material, uint8_t variantKey, filament::Interpolation interpolation,
filament::VertexDomain vertexDomain) const noexcept {
@@ -267,7 +267,7 @@ static bool isMobileTarget(filament::backend::ShaderModel model) {
}
}
-const std::string ShaderGenerator::createFragmentProgram(filament::backend::ShaderModel shaderModel,
+std::string ShaderGenerator::createFragmentProgram(filament::backend::ShaderModel shaderModel,
MaterialBuilder::TargetApi targetApi, MaterialBuilder::TargetLanguage targetLanguage,
MaterialInfo const& material, uint8_t variantKey,
filament::Interpolation interpolation) const noexcept {
@@ -330,6 +330,7 @@ const std::string ShaderGenerator::createFragmentProgram(filament::backend::Shad
cg.generateDefine(fs, "HAS_DYNAMIC_LIGHTING", litVariants && variant.hasDynamicLighting());
cg.generateDefine(fs, "HAS_SHADOWING", litVariants && variant.hasShadowReceiver());
cg.generateDefine(fs, "HAS_SHADOW_MULTIPLIER", material.hasShadowMultiplier);
+ cg.generateDefine(fs, "HAS_FOG", variant.hasFog());
// material defines
cg.generateDefine(fs, "MATERIAL_HAS_DOUBLE_SIDED_CAPABILITY", material.hasDoubleSidedCapability);
@@ -444,7 +445,7 @@ void ShaderGenerator::fixupExternalSamplers(filament::backend::ShaderModel sm,
}
}
-const std::string ShaderGenerator::createPostProcessVertexProgram(
+std::string ShaderGenerator::createPostProcessVertexProgram(
filament::backend::ShaderModel sm, MaterialBuilder::TargetApi targetApi,
MaterialBuilder::TargetLanguage targetLanguage, MaterialInfo const& material,
uint8_t variant, const filament::SamplerBindingMap& samplerBindingMap) const noexcept {
@@ -485,7 +486,7 @@ const std::string ShaderGenerator::createPostProcessVertexProgram(
return vs.c_str();
}
-const std::string ShaderGenerator::createPostProcessFragmentProgram(
+std::string ShaderGenerator::createPostProcessFragmentProgram(
filament::backend::ShaderModel sm, MaterialBuilder::TargetApi targetApi,
MaterialBuilder::TargetLanguage targetLanguage, MaterialInfo const& material,
uint8_t variant, const filament::SamplerBindingMap& samplerBindingMap) const noexcept {
diff --git a/libs/filamat/src/shaders/ShaderGenerator.h b/libs/filamat/src/shaders/ShaderGenerator.h
index 3306611ba7..73996885d3 100644
--- a/libs/filamat/src/shaders/ShaderGenerator.h
+++ b/libs/filamat/src/shaders/ShaderGenerator.h
@@ -43,12 +43,12 @@ public:
size_t vertexLineOffset,
MaterialBuilder::MaterialDomain materialDomain) noexcept;
- const std::string createVertexProgram(filament::backend::ShaderModel sm,
+ std::string createVertexProgram(filament::backend::ShaderModel sm,
MaterialBuilder::TargetApi targetApi, MaterialBuilder::TargetLanguage targetLanguage,
MaterialInfo const& material, uint8_t variantKey,
filament::Interpolation interpolation,
filament::VertexDomain vertexDomain) const noexcept;
- const std::string createFragmentProgram(filament::backend::ShaderModel sm,
+ std::string createFragmentProgram(filament::backend::ShaderModel sm,
MaterialBuilder::TargetApi targetApi, MaterialBuilder::TargetLanguage targetLanguage,
MaterialInfo const& material, uint8_t variantKey,
filament::Interpolation interpolation) const noexcept;
@@ -67,12 +67,12 @@ public:
private:
- const std::string createPostProcessVertexProgram(filament::backend::ShaderModel sm,
+ std::string createPostProcessVertexProgram(filament::backend::ShaderModel sm,
MaterialBuilder::TargetApi targetApi, MaterialBuilder::TargetLanguage targetLanguage,
MaterialInfo const& material, uint8_t variant,
const filament::SamplerBindingMap& samplerBindingMap) const noexcept;
- const std::string createPostProcessFragmentProgram(filament::backend::ShaderModel sm,
+ std::string createPostProcessFragmentProgram(filament::backend::ShaderModel sm,
MaterialBuilder::TargetApi targetApi, MaterialBuilder::TargetLanguage targetLanguage,
MaterialInfo const& material, uint8_t variant,
const filament::SamplerBindingMap& samplerBindingMap) const noexcept;
diff --git a/libs/gltfio/include/gltfio/SimpleViewer.h b/libs/gltfio/include/gltfio/SimpleViewer.h
index 619fd11656..c95c7aca8a 100644
--- a/libs/gltfio/include/gltfio/SimpleViewer.h
+++ b/libs/gltfio/include/gltfio/SimpleViewer.h
@@ -201,6 +201,7 @@ private:
bool mEnableMsaa = true;
bool mEnableSsao = true;
filament::View::BloomOptions mBloomOptions = { .enabled = true };
+ filament::View::FogOptions mFogOptions = {};
int mSidebarWidth;
uint32_t mFlags;
};
@@ -444,13 +445,6 @@ void SimpleViewer::updateUserInterface() {
ImGui::Checkbox("Bloom", &mBloomOptions.enabled);
}
- mView->setDithering(mEnableDithering ? View::Dithering::TEMPORAL : View::Dithering::NONE);
- mView->setAntiAliasing(mEnableFxaa ? View::AntiAliasing::FXAA : View::AntiAliasing::NONE);
- mView->setSampleCount(mEnableMsaa ? 4 : 1);
- mView->setAmbientOcclusion(
- mEnableSsao ? View::AmbientOcclusion::SSAO : View::AmbientOcclusion::NONE);
- mView->setBloomOptions(mBloomOptions);
-
if (ImGui::CollapsingHeader("Light", headerFlags)) {
ImGui::SliderFloat("IBL intensity", &mIblIntensity, 0.0f, 100000.0f);
ImGui::SliderAngle("IBL rotation", &mIblRotation);
@@ -461,6 +455,25 @@ void SimpleViewer::updateUserInterface() {
ImGui::Checkbox("Enable contact shadows", &mEnableContactShadows);
}
+ if (ImGui::CollapsingHeader("Fog")) {
+ ImGui::Checkbox("Enable Fog", &mFogOptions.enabled);
+ ImGui::SliderFloat("Start", &mFogOptions.distance, 0.0f, 100.0f);
+ ImGui::SliderFloat("Density", &mFogOptions.density, 0.0f, 1.0f);
+ ImGui::SliderFloat("Height", &mFogOptions.height, 0.0f, 100.0f);
+ ImGui::SliderFloat("Height Falloff", &mFogOptions.heightFalloff, 0.0f, 10.0f);
+ ImGui::SliderFloat("Scattering Start", &mFogOptions.inScatteringStart, 0.0f, 100.0f);
+ ImGui::SliderFloat("Scattering Size", &mFogOptions.inScatteringSize, 0.0f, 1.0f);
+ ImGui::ColorPicker3("Color", mFogOptions.color.v);
+ }
+
+ mView->setDithering(mEnableDithering ? View::Dithering::TEMPORAL : View::Dithering::NONE);
+ mView->setAntiAliasing(mEnableFxaa ? View::AntiAliasing::FXAA : View::AntiAliasing::NONE);
+ mView->setSampleCount(mEnableMsaa ? 4 : 1);
+ mView->setAmbientOcclusion(
+ mEnableSsao ? View::AmbientOcclusion::SSAO : View::AmbientOcclusion::NONE);
+ mView->setBloomOptions(mBloomOptions);
+ mView->setFogOptions(mFogOptions);
+
if (mEnableSunlight) {
mScene->addEntity(mSunlight);
auto sun = lm.getInstance(mSunlight);
diff --git a/samples/app/FilamentApp.cpp b/samples/app/FilamentApp.cpp
index 3d1bb252b7..2957adbabc 100644
--- a/samples/app/FilamentApp.cpp
+++ b/samples/app/FilamentApp.cpp
@@ -696,7 +696,7 @@ void FilamentApp::Window::configureCamerasForWindow() {
const uint32_t mainWidth = std::max(1, (int) width - sidebar);
double near = 0.1;
- double far = 50;
+ double far = 100;
mMainCamera->setLensProjection(mFilamentApp->mCameraFocalLength, double(mainWidth) / height, near, far);
mDebugCamera->setProjection(45.0, double(width) / height, 0.0625, 4096, Camera::Fov::VERTICAL);
mOrthoCamera->setProjection(Camera::Projection::ORTHO, -3, 3, -3 * ratio, 3 * ratio, near, far);
diff --git a/samples/material_sandbox.cpp b/samples/material_sandbox.cpp
index c0d73592ca..27f85387ad 100644
--- a/samples/material_sandbox.cpp
+++ b/samples/material_sandbox.cpp
@@ -494,6 +494,17 @@ static void gui(filament::Engine* engine, filament::View*) {
ImGui::Unindent();
}
+ if (ImGui::CollapsingHeader("Fog")) {
+ ImGui::Checkbox("Enable Fog", ¶ms.fogOptions.enabled);
+ ImGui::SliderFloat("Start", ¶ms.fogOptions.distance, 0.0f, 100.0f);
+ ImGui::SliderFloat("Density", ¶ms.fogOptions.density, 0.0f, 1.0f);
+ ImGui::SliderFloat("Height", ¶ms.fogOptions.height, 0.0f, 100.0f);
+ ImGui::SliderFloat("Height Falloff", ¶ms.fogOptions.heightFalloff, 0.0f, 10.0f);
+ ImGui::SliderFloat("Scattering Start", ¶ms.fogOptions.inScatteringStart, 0.0f, 100.0f);
+ ImGui::SliderFloat("Scattering Size", ¶ms.fogOptions.inScatteringSize, 0.0f, 1.0f);
+ ImGui::ColorPicker3("Color", params.fogOptions.color.v);
+ }
+
if (ImGui::CollapsingHeader("Post-processing")) {
ImGui::Checkbox("msaa 4x", ¶ms.msaa);
ImGui::Checkbox("tone-mapping", ¶ms.tonemapping);
@@ -596,6 +607,7 @@ static void preRender(filament::Engine*, filament::View* view, filament::Scene*,
view->setToneMapping(g_params.tonemapping ? View::ToneMapping::ACES : View::ToneMapping::LINEAR);
view->setDithering(g_params.dithering ? View::Dithering::TEMPORAL : View::Dithering::NONE);
view->setBloomOptions(g_params.bloomOptions);
+ view->setFogOptions(g_params.fogOptions);
view->setSampleCount((uint8_t) (g_params.msaa ? 4 : 1));
view->setAmbientOcclusion(
g_params.ssao ? View::AmbientOcclusion::SSAO : View::AmbientOcclusion::NONE);
diff --git a/samples/material_sandbox.h b/samples/material_sandbox.h
index f41aa23ffb..11820e804b 100644
--- a/samples/material_sandbox.h
+++ b/samples/material_sandbox.h
@@ -109,6 +109,7 @@ struct SandboxParameters {
bool ssao = false;
filament::View::AmbientOcclusionOptions ssaoOptions;
filament::View::BloomOptions bloomOptions;
+ filament::View::FogOptions fogOptions;
bool screenSpaceContactShadows = false;
int stepCount = 8;
float maxShadowDistance = 0.3;
diff --git a/shaders/CMakeLists.txt b/shaders/CMakeLists.txt
index ce28ad32fa..7b54491e2e 100644
--- a/shaders/CMakeLists.txt
+++ b/shaders/CMakeLists.txt
@@ -22,6 +22,7 @@ set(SHADERS
src/common_types.fs
src/depth_main.fs
src/depth_main.vs
+ src/fog.fs
src/getters.fs
src/getters.vs
src/inputs.fs
diff --git a/shaders/src/fog.fs b/shaders/src/fog.fs
new file mode 100644
index 0000000000..f7e0614006
--- /dev/null
+++ b/shaders/src/fog.fs
@@ -0,0 +1,44 @@
+//------------------------------------------------------------------------------
+// Fog
+//------------------------------------------------------------------------------
+
+vec3 fog(vec3 color, vec3 view, vec3 wsCameraPosition, vec3 up) {
+ if (frameUniforms.fogDensity > 0.0) {
+ float wsCameraHeight = dot(up, wsCameraPosition);
+
+ // v can have infinites (usually due to the skybox)
+ float cameraToWorldDistance = min(MEDIUMP_FLT_MAX, length(view));
+
+ // clamp to camera far plane so the skybox participates to the fog
+ float cameraToWorldDistanceClamped = min(frameUniforms.cameraFar, cameraToWorldDistance);
+
+ // fog density with respect to camera's altitude
+ float density = frameUniforms.fogDensity * exp2(-frameUniforms.fogHeightFalloff * (wsCameraHeight - frameUniforms.fogHeight));
+
+ // falloff due to height of the shaded fragment w.r.t. the camera
+ float h = dot(view, up);
+ h = abs(h) > 0.01 ? h : 0.01;
+ float falloff = max(frameUniforms.fogHeightFalloff * h, -127.0);
+
+ // fog density at shaded fragment w.r.t. camera's altitude
+ float densityFalloff = density * (1.0 - exp2(-falloff)) / falloff;
+
+ // take fog start distance into account
+ float lineIntegral = densityFalloff * max(cameraToWorldDistanceClamped - frameUniforms.fogStart, 0.0);
+ float fogFactor = max(clamp(exp2(-lineIntegral), 0.0, 1.0), 1.0 - frameUniforms.fogMaxOpacity);
+
+ vec3 foggedColor = frameUniforms.fogColor * (1.0 - fogFactor);
+ if (frameUniforms.fogInscatteringSize > 0.0) {
+ vec3 lightDirectionNorm = -normalize(frameUniforms.lightDirection);
+ vec3 cameraToWorldUnit = view * (1.0 / cameraToWorldDistance);
+ float lightInscatteringFactor = max(dot(cameraToWorldUnit, lightDirectionNorm), 0.0);
+ vec3 lightInscattering = frameUniforms.lightColorIntensity.rgb * frameUniforms.lightColorIntensity.w * pow(lightInscatteringFactor, frameUniforms.fogInscatteringSize);
+ float lightInscatteringIntegral = densityFalloff * max(cameraToWorldDistanceClamped - frameUniforms.fogInscatteringStart, 0.0);
+ float inscatteringFactor = clamp(exp2(-lightInscatteringIntegral), 0.0, 1.0);
+ foggedColor += lightInscattering * (1.0 - inscatteringFactor);
+ }
+
+ return color * fogFactor + foggedColor;
+ }
+ return color;
+}
diff --git a/shaders/src/main.fs b/shaders/src/main.fs
index 7381bae553..9fa9ca149f 100644
--- a/shaders/src/main.fs
+++ b/shaders/src/main.fs
@@ -28,6 +28,13 @@ void main() {
fragColor = evaluateMaterial(inputs);
+#if defined(HAS_FOG)
+ // we need the real world position, which is obtained by adding the world-offset
+ vec3 wsCameraPostion = getWorldCameraPosition() + getWorldOffset();
+ vec3 view = getWorldPosition() - getWorldCameraPosition();
+ fragColor.rgb = fog(fragColor.rgb, view, wsCameraPostion, vec3(0, 1, 0));
+#endif
+
#if defined(MATERIAL_HAS_POST_LIGHTING_COLOR)
blendPostLightingColor(inputs, fragColor);
#endif