diff --git a/android/filament-android/src/main/cpp/View.cpp b/android/filament-android/src/main/cpp/View.cpp index d88e41489c..e7638988ce 100644 --- a/android/filament-android/src/main/cpp/View.cpp +++ b/android/filament-android/src/main/cpp/View.cpp @@ -275,7 +275,7 @@ 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) { + jfloat inScatteringSize, jboolean fogColorFromIbl, jboolean enabled) { View* view = (View*) nativeView; View::FogOptions options = { .distance = distance, @@ -286,6 +286,7 @@ Java_com_google_android_filament_View_nSetFogOptions(JNIEnv *, jclass , jlong na .density = density, .inScatteringStart = inScatteringStart, .inScatteringSize = inScatteringSize, + .fogColorFromIbl = (bool)fogColorFromIbl, .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 65e7153f16..d09ae9dc22 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 @@ -307,10 +307,15 @@ public class View { public float inScatteringStart = 0.0f; /** - * size of in-scattering (>0 to activate) + * size of in-scattering (>=0 to activate). Good values are >> 1 (e.g. ~10 - 100) */ public float inScatteringSize = 0.0f; + /** + * fog color will be modulated by the IBL color in the view direction + */ + public boolean fogColorFromIbl = false; + /** * enable or disable fog */ @@ -906,6 +911,7 @@ public class View { 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.fogColorFromIbl, options.enabled); } @@ -965,6 +971,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); + 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 fogColorFromIbl, boolean enabled); } diff --git a/filament/include/filament/View.h b/filament/include/filament/View.h index a81b31fa01..2016f2a8d0 100644 --- a/filament/include/filament/View.h +++ b/filament/include/filament/View.h @@ -173,10 +173,11 @@ public: 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) + math::float3 color{ 0.5f }; //!< fog's color (linear), see fogColorFromIbl 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) + float inScatteringSize = -1.0f; //!< size of in-scattering (>=0 to activate). Good values are >> 1 (e.g. ~10 - 100). + bool fogColorFromIbl = false; //!< Fog color will be modulated by the IBL color in the view direction. bool enabled = false; //!< enable or disable fog }; diff --git a/filament/src/View.cpp b/filament/src/View.cpp index b427676f37..3f71dbeda5 100644 --- a/filament/src/View.cpp +++ b/filament/src/View.cpp @@ -616,14 +616,23 @@ void FView::prepare(FEngine& engine, backend::DriverApi& driver, ArenaScope& are u.setUniform(offsetof(PerViewUib, userTime), userTime); auto const& fogOptions = mFogOptions; + + // shader uses exp2() instead of exp() so we correct that here + float heightFalloffExp2 = fogOptions.heightFalloff * 1.44269f; + + // precalculate the constant part of density integral + float density = (fogOptions.density / fogOptions.heightFalloff) * + std::exp(-fogOptions.heightFalloff * (camera->getPosition().y - fogOptions.height)); + 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, fogHeightFalloff), heightFalloffExp2); u.setUniform(offsetof(PerViewUib, fogColor), fogOptions.color); - u.setUniform(offsetof(PerViewUib, fogDensity), fogOptions.density); + u.setUniform(offsetof(PerViewUib, fogDensity), density); u.setUniform(offsetof(PerViewUib, fogInscatteringStart), fogOptions.inScatteringStart); u.setUniform(offsetof(PerViewUib, fogInscatteringSize), fogOptions.inScatteringSize); + u.setUniform(offsetof(PerViewUib, fogColorFromIbl), fogOptions.fogColorFromIbl ? 1.0f : 0.0f); // upload the renderables's dirty UBOs engine.getRenderableManager().prepare(driver, @@ -711,7 +720,6 @@ 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); } diff --git a/filament/src/details/View.h b/filament/src/details/View.h index a275556e60..97b8592734 100644 --- a/filament/src/details/View.h +++ b/filament/src/details/View.h @@ -292,7 +292,7 @@ public: 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.inScatteringSize = options.inScatteringSize; options.inScatteringStart = std::max(0.0f, options.inScatteringStart); mFogOptions = options; } diff --git a/filament/src/materials/sao.mat b/filament/src/materials/sao.mat index f150212066..a5e2a109f5 100644 --- a/filament/src/materials/sao.mat +++ b/filament/src/materials/sao.mat @@ -96,7 +96,7 @@ fragment { // random number between 0 and 1, using interleaved gradient noise float random(vec2 w) { const vec3 m = vec3(0.06711056, 0.00583715, 52.9829189); - return fract(m.z * fract(dot(w.xy, m.xy))); + return fract(m.z * fract(dot(w, m.xy))); } highp float linearizeDepth(highp float depth) { diff --git a/libs/filabridge/include/private/filament/UibGenerator.h b/libs/filabridge/include/private/filament/UibGenerator.h index dc71dcf881..ceea90d834 100644 --- a/libs/filabridge/include/private/filament/UibGenerator.h +++ b/libs/filabridge/include/private/filament/UibGenerator.h @@ -86,7 +86,7 @@ struct PerViewUib { // NOLINT(cppcoreguidelines-pro-type-member-init) filament::math::float4 userTime; // time(s), (double)time - (float)time, 0, 0 float iblRoughnessOneLevel; // level for roughness == 1 - float padding0; + float cameraFar; // camera *culling* far-plane distance (projection far is at +inf) float refractionLodOffset; // bit 0: directional (sun) shadow enabled @@ -100,14 +100,13 @@ struct PerViewUib { // NOLINT(cppcoreguidelines-pro-type-member-init) float fogStart; float fogMaxOpacity; float fogHeight; - float fogHeightFalloff; + float fogHeightFalloff; // falloff * 1.44269 math::float3 fogColor; - float fogDensity; + float fogDensity; // (density/falloff)*exp(-falloff*(camera.y - fogHeight)) float fogInscatteringStart; float fogInscatteringSize; - - float cameraNear; // camera near-plane distance (positive value) - float cameraFar; // camera *culling* far-plane distance (projection far is at +inf) + float fogColorFromIbl; + float padding1; // bring PerViewUib to 1 KiB filament::math::float4 padding2[12]; diff --git a/libs/filabridge/src/UibGenerator.cpp b/libs/filabridge/src/UibGenerator.cpp index 934d0bf4e8..cd4d5bd799 100644 --- a/libs/filabridge/src/UibGenerator.cpp +++ b/libs/filabridge/src/UibGenerator.cpp @@ -75,7 +75,7 @@ UniformInterfaceBlock const& UibGenerator::getPerViewUib() noexcept { .add("userTime", 1, UniformInterfaceBlock::Type::FLOAT4) // ibl max mip level .add("iblRoughnessOneLevel", 1, UniformInterfaceBlock::Type::FLOAT) - .add("padding0", 1, UniformInterfaceBlock::Type::FLOAT) + .add("cameraFar", 1, UniformInterfaceBlock::Type::FLOAT) .add("refractionLodOffset", 1, UniformInterfaceBlock::Type::FLOAT) .add("directionalShadows", 1, UniformInterfaceBlock::Type::UINT) // view @@ -91,8 +91,8 @@ UniformInterfaceBlock const& UibGenerator::getPerViewUib() noexcept { .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) + .add("fogColorFromIbl", 1, UniformInterfaceBlock::Type::FLOAT) + .add("padding1", 1, UniformInterfaceBlock::Type::FLOAT) // bring size to 1 KiB .add("padding2", 12, UniformInterfaceBlock::Type::FLOAT4) diff --git a/libs/gltfio/include/gltfio/SimpleViewer.h b/libs/gltfio/include/gltfio/SimpleViewer.h index b800c752fd..515df5731a 100644 --- a/libs/gltfio/include/gltfio/SimpleViewer.h +++ b/libs/gltfio/include/gltfio/SimpleViewer.h @@ -312,6 +312,7 @@ void SimpleViewer::removeAsset() { void SimpleViewer::setIndirectLight(filament::IndirectLight* ibl, filament::math::float3 const* sh3) { using namespace filament::math; + mFogOptions.color = sh3[0]; mIndirectLight = ibl; if (ibl) { float3 d = filament::IndirectLight::getDirectionEstimate(sh3); @@ -462,7 +463,8 @@ void SimpleViewer::updateUserInterface() { 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::SliderFloat("Scattering Size", &mFogOptions.inScatteringSize, 0.0f, 10.0f); + ImGui::Checkbox("Color from IBL", &mFogOptions.fogColorFromIbl); ImGui::ColorPicker3("Color", mFogOptions.color.v); } diff --git a/samples/material_sandbox.cpp b/samples/material_sandbox.cpp index 27f85387ad..8c38ab8c2f 100644 --- a/samples/material_sandbox.cpp +++ b/samples/material_sandbox.cpp @@ -502,6 +502,7 @@ static void gui(filament::Engine* engine, filament::View*) { 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::Checkbox("Color from IBL", ¶ms.fogOptions.fogColorFromIbl); ImGui::ColorPicker3("Color", params.fogOptions.color.v); } diff --git a/shaders/src/fog.fs b/shaders/src/fog.fs index f7e0614006..8f8b8a61d3 100644 --- a/shaders/src/fog.fs +++ b/shaders/src/fog.fs @@ -2,43 +2,49 @@ // Fog //------------------------------------------------------------------------------ -vec3 fog(vec3 color, vec3 view, vec3 wsCameraPosition, vec3 up) { +vec3 fog(vec3 color, vec3 view) { if (frameUniforms.fogDensity > 0.0) { - float wsCameraHeight = dot(up, wsCameraPosition); + float A = frameUniforms.fogDensity; + float B = frameUniforms.fogHeightFalloff; - // v can have infinites (usually due to the skybox) - float cameraToWorldDistance = min(MEDIUMP_FLT_MAX, length(view)); + float d = length(view); + float h = abs(view.y) < FLT_EPS ? FLT_EPS : view.y; // avoid divide-by-zero - // clamp to camera far plane so the skybox participates to the fog - float cameraToWorldDistanceClamped = min(frameUniforms.cameraFar, cameraToWorldDistance); + float fogIntegralFunctionOfDistance = A * (1.0 - exp2(-B * h)) / h; + float fogIntegral = fogIntegralFunctionOfDistance * max(d - frameUniforms.fogStart, 0.0); - // fog density with respect to camera's altitude - float density = frameUniforms.fogDensity * exp2(-frameUniforms.fogHeightFalloff * (wsCameraHeight - frameUniforms.fogHeight)); + // remap fogIntegral to an opacity between 0 and 1. saturate(fogIntegral) is too harsh. + float fogOpacity = max(1.0 - exp2(-fogIntegral), 0.0); + //float fogOpacity = clamp(fogIntegral, 0.0, 1.0); - // 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); + // don't go above requested max opacity + fogOpacity = min(fogOpacity, frameUniforms.fogMaxOpacity); - // fog density at shaded fragment w.r.t. camera's altitude - float densityFalloff = density * (1.0 - exp2(-falloff)) / falloff; + // compute fog color + vec3 fogColor = frameUniforms.fogColor; - // 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); + if (frameUniforms.fogColorFromIbl > 0.0) { + // get fog color from envmap + float lod = frameUniforms.iblRoughnessOneLevel; + fogColor *= textureLod(light_iblSpecular, view, lod).rgb * frameUniforms.iblLuminance; } - return color * fogFactor + foggedColor; + fogColor *= fogOpacity; + if (frameUniforms.fogInscatteringSize >= 0.0) { + // compute a new line-integral for a different start distance + float inscatteringIntegral = fogIntegralFunctionOfDistance * max(d - frameUniforms.fogInscatteringStart, 0.0); + float inscatteringOpacity = max(1.0 - exp2(-inscatteringIntegral), 0.0); + //float inscatteringOpacity = clamp(inscatteringIntegral, 0.0, 1.0); + + // Add sun colored fog when looking towards the sun + vec3 sunColor = frameUniforms.lightColorIntensity.rgb * frameUniforms.lightColorIntensity.w; + float sunAmount = max(dot(view, frameUniforms.lightDirection) / d, 0.0); // between 0 and 1 + float sunInscattering = pow(sunAmount, frameUniforms.fogInscatteringSize); + + fogColor += sunColor * (sunInscattering * inscatteringOpacity); + } + + return color * (1.0 - fogOpacity) + fogColor; } return color; } diff --git a/shaders/src/getters.vs b/shaders/src/getters.vs index 0c2806104f..dee641559c 100644 --- a/shaders/src/getters.vs +++ b/shaders/src/getters.vs @@ -142,6 +142,9 @@ vec4 computeWorldPosition() { #else mat4 transform = getWorldFromClipMatrix(); vec4 position = transform * getPosition(); + if (abs(position.w) < MEDIUMP_FLT_MIN) { + position.w = position.w < 0.0 ? -MEDIUMP_FLT_MIN : MEDIUMP_FLT_MIN; + } return position * (1.0 / position.w); #endif } diff --git a/shaders/src/main.fs b/shaders/src/main.fs index 9fa9ca149f..d9e505421c 100644 --- a/shaders/src/main.fs +++ b/shaders/src/main.fs @@ -29,10 +29,8 @@ 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)); + fragColor.rgb = fog(fragColor.rgb, view); #endif #if defined(MATERIAL_HAS_POST_LIGHTING_COLOR)