From 0c6d96c407e9d3df4e44764036a39a98d25161e1 Mon Sep 17 00:00:00 2001 From: Mathias Agopian Date: Thu, 19 Mar 2020 18:01:07 -0700 Subject: [PATCH] Add support for basic fog (#2256) * Add support for basic fog * address reviewers comments --- RELEASE_NOTES.md | 1 + .../filament-android/src/main/cpp/View.cpp | 20 ++++++ .../com/google/android/filament/View.java | 69 +++++++++++++++++++ filament/include/filament/View.h | 36 ++++++++-- filament/src/RenderPass.cpp | 1 + filament/src/RenderPass.h | 19 ++--- filament/src/Renderer.cpp | 1 + filament/src/View.cpp | 22 +++++- filament/src/details/View.h | 12 ++++ .../include/private/filament/UibGenerator.h | 20 +++++- .../include/private/filament/Variant.h | 31 +++++---- libs/filabridge/src/UibGenerator.cpp | 15 +++- libs/filamat/src/shaders/CodeGenerator.cpp | 1 + libs/filamat/src/shaders/ShaderGenerator.cpp | 9 +-- libs/filamat/src/shaders/ShaderGenerator.h | 8 +-- libs/gltfio/include/gltfio/SimpleViewer.h | 27 ++++++-- samples/app/FilamentApp.cpp | 2 +- samples/material_sandbox.cpp | 12 ++++ samples/material_sandbox.h | 1 + shaders/CMakeLists.txt | 1 + shaders/src/fog.fs | 44 ++++++++++++ shaders/src/main.fs | 7 ++ 22 files changed, 308 insertions(+), 51 deletions(-) create mode 100644 shaders/src/fog.fs 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