Skip to content

Commit

Permalink
New API to apply a transform to the large-scale fog.
Browse files Browse the repository at this point in the history
This works by making the fog an entity which can be used to create
a TransformManager component an participate to the transform hierarchy.

This feature can be used as more advanced way to set the fog's floor,
which now can have an orientation (essentially be a plane).
This is useful for coordinate systems that are not y-up.
  • Loading branch information
pixelflinger committed May 16, 2023
1 parent 8aa1435 commit f6c8ce1
Show file tree
Hide file tree
Showing 14 changed files with 118 additions and 48 deletions.
1 change: 1 addition & 0 deletions NEW_RELEASE_NOTES.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,3 +8,4 @@ appropriate header in [RELEASE_NOTES.md](./RELEASE_NOTES.md).

## Release notes for next branch cut

- engine: a new feature to set a transform on the global-scale fog [⚠️ **Recompile materials**]
8 changes: 8 additions & 0 deletions android/filament-android/src/main/cpp/View.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -505,3 +505,11 @@ Java_com_google_android_filament_View_nGetMaterialGlobal(JNIEnv *env, jclass cla
std::copy_n(result.v, 4, out);
env->ReleaseFloatArrayElements(out_, out, 0);
}

extern "C"
JNIEXPORT int JNICALL
Java_com_google_android_filament_View_nGetFogEntity(JNIEnv *env, jclass clazz,
jlong nativeView) {
View *view = (View *) nativeView;
return (jint)view->getFogEntity().getId();
}
Original file line number Diff line number Diff line change
Expand Up @@ -1148,6 +1148,20 @@ public float[] getMaterialGlobal(int index, @Nullable @Size(min = 4) float[] out
return out;
}

/**
* Get an Entity representing the large scale fog object.
* This entity is always inherited by the View's Scene.
*
* It is for example possible to create a TransformManager component with this
* Entity and apply a transformation globally on the fog.
*
* @return an Entity representing the large scale fog object.
*/
@Entity
public int getFogEntity() {
return nGetFogEntity(getNativeObject());
}

public long getNativeObject() {
if (mNativeObject == 0) {
throw new IllegalStateException("Calling method on destroyed View");
Expand Down Expand Up @@ -1206,6 +1220,7 @@ private static native void nSetDepthOfFieldOptions(long nativeView, float cocSca
private static native boolean nIsStencilBufferEnabled(long nativeView);
private static native void nSetMaterialGlobal(long nativeView, int index, float x, float y, float z, float w);
private static native void nGetMaterialGlobal(long nativeView, int index, float[] out);
private static native int nGetFogEntity(long nativeView);


/**
Expand Down
11 changes: 11 additions & 0 deletions filament/include/filament/View.h
Original file line number Diff line number Diff line change
Expand Up @@ -827,6 +827,17 @@ class UTILS_PUBLIC View : public FilamentAPI {
*/
math::float4 getMaterialGlobal(uint32_t index) const;

/**
* Get an Entity representing the large scale fog object.
* This entity is always inherited by the View's Scene.
*
* It is for example possible to create a TransformManager component with this
* Entity and apply a transformation globally on the fog.
*
* @return an Entity representing the large scale fog object.
*/
utils::Entity getFogEntity() const noexcept;

/**
* List of available ambient occlusion techniques
* @deprecated use AmbientOcclusionOptions::enabled instead
Expand Down
22 changes: 20 additions & 2 deletions filament/src/PerViewUniforms.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -128,11 +128,28 @@ void PerViewUniforms::prepareTemporalNoise(FEngine& engine,
s.temporalNoise = options.enabled ? temporalNoise : 0.0f;
}

void PerViewUniforms::prepareFog(float3 const& cameraPosition, FogOptions const& options) noexcept {
void PerViewUniforms::prepareFog(const CameraInfo& cameraInfo,
mat4 const& userWorldFromFog, FogOptions const& options) noexcept {
// Fog should be calculated in the "user's world coordinates" so that it's not
// affected by the IBL rotation.
// fogFromWorldMatrix below is only used to transform the view vector in the shader, which is
// why we store the cofactor matrix.

mat4f const viewFromWorld = cameraInfo.view;
mat4 const worldFromUserWorld = cameraInfo.worldOrigin;
mat4 const worldFromFog = worldFromUserWorld * userWorldFromFog;
mat4 const viewFromFog = viewFromWorld * worldFromFog;

mat4 const fogFromView = inverse(viewFromFog);
mat3 const fogFromWorld = inverse(worldFromFog.upperLeft());

// camera position relative to the fog's origin
auto const userCameraPosition = fogFromView[3].xyz;

const float heightFalloff = std::max(0.0f, options.heightFalloff);

// precalculate the constant part of density integral
const float density = -heightFalloff * (cameraPosition.y - options.height);
const float density = -float(heightFalloff * (userCameraPosition.y - options.height));

auto& s = mUniforms.edit();
s.fogStart = options.distance;
Expand All @@ -144,6 +161,7 @@ void PerViewUniforms::prepareFog(float3 const& cameraPosition, FogOptions const&
s.fogInscatteringStart = options.inScatteringStart;
s.fogInscatteringSize = options.inScatteringSize;
s.fogColorFromIbl = options.fogColorFromIbl ? 1.0f : 0.0f;
s.fogFromWorldMatrix = mat3f{ cof(fogFromWorld) };
}

void PerViewUniforms::prepareSSAO(Handle<HwTexture> ssao,
Expand Down
3 changes: 2 additions & 1 deletion filament/src/PerViewUniforms.h
Original file line number Diff line number Diff line change
Expand Up @@ -85,7 +85,8 @@ class PerViewUniforms {
void prepareTime(FEngine& engine, math::float4 const& userTime) noexcept;
void prepareTemporalNoise(FEngine& engine, TemporalAntiAliasingOptions const& options) noexcept;
void prepareExposure(float ev100) noexcept;
void prepareFog(math::float3 const& cameraPosition, FogOptions const& options) noexcept;
void prepareFog(const CameraInfo& cameraInfo,
math::mat4 const& fogTransform, FogOptions const& options) noexcept;
void prepareStructure(TextureHandle structure) noexcept;
void prepareSSAO(TextureHandle ssao, AmbientOcclusionOptions const& options) noexcept;
void prepareBlending(bool needsAlphaChannel) noexcept;
Expand Down
4 changes: 4 additions & 0 deletions filament/src/View.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -296,4 +296,8 @@ math::float4 View::getMaterialGlobal(uint32_t index) const {
return downcast(this)->getMaterialGlobal(index);
}

utils::Entity View::getFogEntity() const noexcept {
return downcast(this)->getFogEntity();
}

} // namespace filament
22 changes: 13 additions & 9 deletions filament/src/details/View.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -56,20 +56,21 @@ static constexpr float PID_CONTROLLER_Ki = 0.002f;
static constexpr float PID_CONTROLLER_Kd = 0.0f;

FView::FView(FEngine& engine)
: mFroxelizer(engine),
mPerViewUniforms(engine),
mShadowMapManager(engine) {
: mFroxelizer(engine),
mFogEntity(engine.getEntityManager().create()),
mPerViewUniforms(engine),
mShadowMapManager(engine) {
DriverApi& driver = engine.getDriverApi();

FDebugRegistry& debugRegistry = engine.getDebugRegistry();

debugRegistry.registerProperty("d.view.camera_at_origin",
&engine.debug.view.camera_at_origin);

// Integral term is used to fight back the dead-band below, we limit how much it can act.
// The integral term is used to fight back the dead-band below, we limit how much it can act.
mPidController.setIntegralLimits(-100.0f, 100.0f);

// dead-band, 1% for scaling down, 5% for scaling up. This stabilizes all the jitters.
// Dead-band, 1% for scaling down, 5% for scaling up. This stabilizes all the jitters.
mPidController.setOutputDeadBand(-0.01f, 0.05f);

#ifndef NDEBUG
Expand All @@ -86,7 +87,7 @@ FView::FView(FEngine& engine)
engine.debug.view.pid.kp, engine.debug.view.pid.ki, engine.debug.view.pid.kd);
#endif

// allocate ubos
// allocate UBOs
mLightUbh = driver.createBufferObject(CONFIG_MAX_LIGHT_COUNT * sizeof(LightsUib),
BufferObjectBinding::UNIFORM, BufferUsage::DYNAMIC);

Expand Down Expand Up @@ -114,6 +115,8 @@ void FView::terminate(FEngine& engine) {
mShadowMapManager.terminate(engine);
mPerViewUniforms.terminate(driver);
mFroxelizer.terminate(driver);

engine.getEntityManager().destroy(mFogEntity);
}

void FView::setViewport(filament::Viewport const& viewport) noexcept {
Expand Down Expand Up @@ -639,10 +642,11 @@ void FView::prepare(FEngine& engine, DriverApi& driver, ArenaScope& arena,
* Update driver state
*/

auto const userModelMatrix = inverse(cameraInfo.getUserViewMatrix());
auto const userCameraPosition = userModelMatrix[3].xyz;
auto const& tcm = engine.getTransformManager();
auto const fogTransform = tcm.getWorldTransformAccurate(tcm.getInstance(mFogEntity));

mPerViewUniforms.prepareTime(engine, userTime);
mPerViewUniforms.prepareFog(userCameraPosition, mFogOptions);
mPerViewUniforms.prepareFog(cameraInfo, fogTransform, mFogOptions);
mPerViewUniforms.prepareTemporalNoise(engine, mTemporalAntiAliasingOptions);
mPerViewUniforms.prepareBlending(needsAlphaChannel);
mPerViewUniforms.prepareMaterialGlobals(mMaterialGlobals);
Expand Down
5 changes: 5 additions & 0 deletions filament/src/details/View.h
Original file line number Diff line number Diff line change
Expand Up @@ -415,6 +415,10 @@ class FView : public View {

math::float4 getMaterialGlobal(uint32_t index) const;

utils::Entity getFogEntity() const noexcept {
return mFogEntity;
}

private:

struct FPickingQuery : public PickingQuery {
Expand Down Expand Up @@ -511,6 +515,7 @@ class FView : public View {
BlendMode mBlendMode = BlendMode::OPAQUE;
const FColorGrading* mColorGrading = nullptr;
const FColorGrading* mDefaultColorGrading = nullptr;
utils::Entity mFogEntity{};

PIDController mPidController;
DynamicResolutionOptions mDynamicResolution;
Expand Down
61 changes: 33 additions & 28 deletions libs/filabridge/include/private/filament/UibStructs.h
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,35 @@

namespace filament {

namespace std140 {

struct alignas(16) vec3 : public std::array<float, 3> {};
struct alignas(16) vec4 : public std::array<float, 4> {};

struct mat33 : public std::array<vec3, 3> {
mat33& operator=(math::mat3f const& rhs) noexcept {
for (int i = 0; i < 3; i++) {
(*this)[i][0] = rhs[i][0];
(*this)[i][1] = rhs[i][1];
(*this)[i][2] = rhs[i][2];
}
return *this;
}
};

struct mat44 : public std::array<vec4, 4> {
mat44& operator=(math::mat4f const& rhs) noexcept {
for (int i = 0; i < 4; i++) {
(*this)[i][0] = rhs[i][0];
(*this)[i][1] = rhs[i][1];
(*this)[i][2] = rhs[i][2];
(*this)[i][3] = rhs[i][3];
}
return *this;
}
};

} // std140
/*
* IMPORTANT NOTE: Respect std140 layout, don't update without updating UibGenerator::get{*}Uib()
*/
Expand Down Expand Up @@ -147,6 +176,7 @@ struct PerViewUib { // NOLINT(cppcoreguidelines-pro-type-member-init)
float fogInscatteringSize;
float fogReserved1;
float fogReserved2;
std140::mat33 fogFromWorldMatrix;

// --------------------------------------------------------------------------------------------
// Screen-space reflections [variant: SSR (i.e.: VSM | SRE)]
Expand All @@ -172,7 +202,7 @@ struct PerViewUib { // NOLINT(cppcoreguidelines-pro-type-member-init)
float es2Reserved2;

// bring PerViewUib to 2 KiB
math::float4 reserved[55];
math::float4 reserved[52];
};

// 2 KiB == 128 float4s
Expand All @@ -183,33 +213,8 @@ static_assert(sizeof(PerViewUib) == sizeof(math::float4) * 128,
// MARK: -

struct PerRenderableData {

struct alignas(16) vec3_std140 : public std::array<float, 3> { };
struct alignas(16) vec4_std140 : public std::array<float, 4> { };
struct mat33_std140 : public std::array<vec3_std140, 3> {
mat33_std140& operator=(math::mat3f const& rhs) noexcept {
for (int i = 0; i < 3; i++) {
(*this)[i][0] = rhs[i][0];
(*this)[i][1] = rhs[i][1];
(*this)[i][2] = rhs[i][2];
}
return *this;
}
};
struct mat44_std140 : public std::array<vec4_std140, 4> {
mat44_std140& operator=(math::mat4f const& rhs) noexcept {
for (int i = 0; i < 4; i++) {
(*this)[i][0] = rhs[i][0];
(*this)[i][1] = rhs[i][1];
(*this)[i][2] = rhs[i][2];
(*this)[i][3] = rhs[i][3];
}
return *this;
}
};

mat44_std140 worldFromModelMatrix;
mat33_std140 worldFromModelNormalMatrix;
std140::mat44 worldFromModelMatrix;
std140::mat33 worldFromModelNormalMatrix;
int32_t morphTargetCount;
int32_t flagsChannels; // see packFlags() below (0x00000fll)
int32_t objectId; // used for picking
Expand Down
1 change: 1 addition & 0 deletions libs/filamat/src/shaders/UibGenerator.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -129,6 +129,7 @@ BufferInterfaceBlock const& UibGenerator::getPerViewUib() noexcept {
{ "fogInscatteringSize", 0, Type::FLOAT, Precision::DEFAULT, FeatureLevel::FEATURE_LEVEL_0 },
{ "fogReserved1", 0, Type::FLOAT },
{ "fogReserved2", 0, Type::FLOAT },
{ "fogFromWorldMatrix", 0, Type::MAT3, Precision::HIGH, FeatureLevel::FEATURE_LEVEL_0 },

// ------------------------------------------------------------------------------------
// Screen-space reflections [variant: SSR (i.e.: VSM | SRE)]
Expand Down
2 changes: 2 additions & 0 deletions samples/gltf_viewer.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -586,6 +586,7 @@ int main(int argc, char** argv) {
auto& tcm = engine->getTransformManager();
app.rootTransformEntity = engine->getEntityManager().create();
tcm.create(app.rootTransformEntity);
tcm.create(view->getFogEntity());

const bool batchMode = !app.batchFile.empty();

Expand Down Expand Up @@ -920,6 +921,7 @@ int main(int argc, char** argv) {
TransformManager::Instance const& root = tcm.getInstance(app.rootTransformEntity);
tcm.setParent(tcm.getInstance(camera.getEntity()), root);
tcm.setParent(tcm.getInstance(app.asset->getRoot()), root);
tcm.setParent(tcm.getInstance(view->getFogEntity()), root);
tcm.setTransform(root, mat4f::translation(float3{ app.originIsFarAway ? 1e6f : 0.0f }));

// Check if color grading has changed.
Expand Down
8 changes: 1 addition & 7 deletions shaders/src/main.fs
Original file line number Diff line number Diff line change
Expand Up @@ -44,13 +44,7 @@ void main() {

#if defined(VARIANT_HAS_FOG)
highp vec3 view = getWorldPosition() - getWorldCameraPosition();

// fog should be calculated in the "user's world coordinates" so that it's not
// affected by the IBL rotation. We're transofrming a vector, so we should use the
// cofactor (or inverse-transpose), but since we know this matrix is a rigid transform,
// we can use it as is.
view = mulMat3x3Float3(frameUniforms.userWorldFromWorldMatrix, view);

view = frameUniforms.fogFromWorldMatrix * view;
fragColor = fog(fragColor, view);
#endif

Expand Down
3 changes: 2 additions & 1 deletion web/filament-js/jsbindings.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -617,7 +617,8 @@ class_<View>("View")
.function("setStencilBufferEnabled", &View::setStencilBufferEnabled)
.function("isStencilBufferEnabled", &View::isStencilBufferEnabled)
.function("setMaterialGlobal", &View::setMaterialGlobal)
.function("getMaterialGlobal", &View::getMaterialGlobal);
.function("getMaterialGlobal", &View::getMaterialGlobal)
.function("getFogEntity", &View::getFogEntity);

/// Scene ::core class:: Flat container of renderables and lights.
/// See also the [Engine] methods `createScene` and `destroyScene`.
Expand Down

0 comments on commit f6c8ce1

Please sign in to comment.