Compare commits
10 Commits
v1.69.5
...
pf/renderd
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
e4fa86fb01 | ||
|
|
7da2a08df6 | ||
|
|
82246d934d | ||
|
|
c60969ef67 | ||
|
|
ce37f216bc | ||
|
|
81c71fbbb9 | ||
|
|
07a7c6003a | ||
|
|
804a74c205 | ||
|
|
dde49a410a | ||
|
|
770ce7f8ec |
@@ -6,3 +6,6 @@
|
||||
appropriate header in [RELEASE_NOTES.md](./RELEASE_NOTES.md).
|
||||
|
||||
## Release notes for next branch cut
|
||||
|
||||
- engine: fix crash when using variance shadow maps
|
||||
- materials: better shadow normal-bias calculations [⚠️ **New Material Version**]
|
||||
|
||||
@@ -31,7 +31,7 @@ repositories {
|
||||
}
|
||||
|
||||
dependencies {
|
||||
implementation 'com.google.android.filament:filament-android:1.69.5'
|
||||
implementation 'com.google.android.filament:filament-android:1.69.4'
|
||||
}
|
||||
```
|
||||
|
||||
@@ -50,7 +50,7 @@ Here are all the libraries available in the group `com.google.android.filament`:
|
||||
iOS projects can use CocoaPods to install the latest release:
|
||||
|
||||
```shell
|
||||
pod 'Filament', '~> 1.69.5'
|
||||
pod 'Filament', '~> 1.69.4'
|
||||
```
|
||||
|
||||
## Documentation
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
GROUP=com.google.android.filament
|
||||
VERSION_NAME=1.69.5
|
||||
VERSION_NAME=1.69.4
|
||||
|
||||
POM_DESCRIPTION=Real-time physically based rendering engine for Android.
|
||||
|
||||
|
||||
@@ -181,7 +181,7 @@ important for <code>matc</code> (material compiler).</p>
|
||||
}
|
||||
|
||||
dependencies {
|
||||
implementation 'com.google.android.filament:filament-android:1.69.3'
|
||||
implementation 'com.google.android.filament:filament-android:1.69.4'
|
||||
}
|
||||
</code></pre>
|
||||
<p>Here are all the libraries available in the group <code>com.google.android.filament</code>:</p>
|
||||
@@ -195,7 +195,7 @@ dependencies {
|
||||
</div>
|
||||
<h3 id="ios"><a class="header" href="#ios">iOS</a></h3>
|
||||
<p>iOS projects can use CocoaPods to install the latest release:</p>
|
||||
<pre><code class="language-shell">pod 'Filament', '~> 1.69.3'
|
||||
<pre><code class="language-shell">pod 'Filament', '~> 1.69.4'
|
||||
</code></pre>
|
||||
<h2 id="documentation"><a class="header" href="#documentation">Documentation</a></h2>
|
||||
<ul>
|
||||
|
||||
@@ -228,6 +228,7 @@ branch called <code>my-pr-branch</code>, to <code>filament</code>. This PR requi
|
||||
it in the following fashion</p>
|
||||
<h3 id="using-a-script-to-update-the-golden-repo"><a class="header" href="#using-a-script-to-update-the-golden-repo">Using a script to update the golden repo</a></h3>
|
||||
<ul>
|
||||
<li>Make sure you've completed the steps in 'Setting up python'</li>
|
||||
<li>Run interactive mode in the <code>update_golden.py</code> script.
|
||||
<pre><code>python3 test/renderdiff/src/update_golden.py
|
||||
</code></pre>
|
||||
|
||||
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
@@ -91,7 +91,7 @@ public:
|
||||
GLenum getIndicesType() const noexcept {
|
||||
return indicesType;
|
||||
}
|
||||
} gl;
|
||||
};
|
||||
|
||||
static bool queryOpenGLVersion(GLint* major, GLint* minor) noexcept;
|
||||
|
||||
|
||||
@@ -271,8 +271,8 @@ std::unique_ptr<MaterialDefinition> MaterialDefinition::create(FEngine& engine,
|
||||
|
||||
void MaterialDefinition::terminate(FEngine& engine) {
|
||||
DriverApi& driver = engine.getDriverApi();
|
||||
perViewDescriptorSetLayout.terminate(engine.getDescriptorSetLayoutFactory(), driver);
|
||||
perViewDescriptorSetLayoutVsm.terminate(engine.getDescriptorSetLayoutFactory(), driver);
|
||||
perViewDescriptorSetLayoutPcf.terminate(engine.getDescriptorSetLayoutFactory(), driver);
|
||||
perViewDescriptorSetLayoutS2d.terminate(engine.getDescriptorSetLayoutFactory(), driver);
|
||||
descriptorSetLayout.terminate(engine.getDescriptorSetLayoutFactory(), driver);
|
||||
}
|
||||
|
||||
@@ -607,23 +607,23 @@ void MaterialDefinition::processDescriptorSets(FEngine& engine) {
|
||||
refractionMode == RefractionMode::SCREEN_SPACE;
|
||||
bool const hasFog = !(variantFilterMask & UserVariantFilterMask(UserVariantFilterBit::FOG));
|
||||
|
||||
this->perViewDescriptorSetLayoutDescription = descriptor_sets::getPerViewDescriptorSetLayout(
|
||||
this->perViewDescriptorSetLayoutPcfDescription = descriptor_sets::getPerViewDescriptorSetLayout(
|
||||
materialDomain, isLit, isSSR, hasFog, false);
|
||||
|
||||
this->perViewDescriptorSetLayoutVsmDescription = descriptor_sets::getPerViewDescriptorSetLayout(
|
||||
this->perViewDescriptorSetLayoutS2dDescription = descriptor_sets::getPerViewDescriptorSetLayout(
|
||||
materialDomain, isLit, isSSR, hasFog, true);
|
||||
|
||||
// set the labels
|
||||
this->descriptorSetLayoutDescription.label = CString{ name }.append("_perMat");
|
||||
this->perViewDescriptorSetLayoutDescription.label = CString{ name }.append("_perView");
|
||||
this->perViewDescriptorSetLayoutVsmDescription.label = CString{ name }.append("_perViewVsm");
|
||||
this->perViewDescriptorSetLayoutPcfDescription.label = CString{ name }.append("_perView");
|
||||
this->perViewDescriptorSetLayoutS2dDescription.label = CString{ name }.append("_perViewVsm");
|
||||
|
||||
// get the PER_RENDERABLE and PER_VIEW descriptor binding info
|
||||
for (auto&& [bindingPoint, dsl] : {
|
||||
std::pair{ DescriptorSetBindingPoints::PER_RENDERABLE,
|
||||
descriptor_sets::getPerRenderableLayout() },
|
||||
std::pair{ DescriptorSetBindingPoints::PER_VIEW,
|
||||
this->perViewDescriptorSetLayoutDescription }}) {
|
||||
this->perViewDescriptorSetLayoutPcfDescription }}) {
|
||||
Program::DescriptorBindingsInfo& descriptors = programDescriptorBindings[+bindingPoint];
|
||||
descriptors.reserve(dsl.descriptors.size());
|
||||
for (auto const& entry: dsl.descriptors) {
|
||||
@@ -636,17 +636,17 @@ void MaterialDefinition::processDescriptorSets(FEngine& engine) {
|
||||
descriptorSetLayoutFactory, driver,
|
||||
this->descriptorSetLayoutDescription };
|
||||
|
||||
this->perViewDescriptorSetLayout = {
|
||||
this->perViewDescriptorSetLayoutPcf = {
|
||||
descriptorSetLayoutFactory, driver,
|
||||
this->perViewDescriptorSetLayoutDescription };
|
||||
this->perViewDescriptorSetLayoutPcfDescription };
|
||||
|
||||
this->perViewDescriptorSetLayoutVsm = {
|
||||
this->perViewDescriptorSetLayoutS2d = {
|
||||
descriptorSetLayoutFactory, driver,
|
||||
this->perViewDescriptorSetLayoutVsmDescription };
|
||||
this->perViewDescriptorSetLayoutS2dDescription };
|
||||
}
|
||||
|
||||
backend::DescriptorSetLayout const& MaterialDefinition::getPerViewDescriptorSetLayoutDescription(
|
||||
Variant const variant, bool const useVsmDescriptorSetLayout) const noexcept {
|
||||
Variant const variant, bool const useS2dDescriptorSetLayout) const noexcept {
|
||||
if (materialDomain == MaterialDomain::SURFACE) {
|
||||
if (Variant::isValidDepthVariant(variant)) {
|
||||
// Use the layout description used to create the per view depth variant layout.
|
||||
@@ -657,10 +657,10 @@ backend::DescriptorSetLayout const& MaterialDefinition::getPerViewDescriptorSetL
|
||||
return descriptor_sets::getSsrVariantLayout();
|
||||
}
|
||||
}
|
||||
if (useVsmDescriptorSetLayout) {
|
||||
return perViewDescriptorSetLayoutVsmDescription;
|
||||
if (useS2dDescriptorSetLayout) {
|
||||
return perViewDescriptorSetLayoutS2dDescription;
|
||||
}
|
||||
return perViewDescriptorSetLayoutDescription;
|
||||
return perViewDescriptorSetLayoutPcfDescription;
|
||||
}
|
||||
|
||||
Handle<HwProgram> MaterialDefinition::compileProgram(
|
||||
@@ -689,7 +689,7 @@ Handle<HwProgram> MaterialDefinition::compileProgram(
|
||||
pb.descriptorLayout(+DescriptorSetBindingPoints::PER_VIEW,
|
||||
getPerViewDescriptorSetLayoutDescription(
|
||||
specialization.variant,
|
||||
Variant::isVSMVariant(specialization.variant)));
|
||||
Variant::isShadowSampler2DVariant(specialization.variant)));
|
||||
pb.descriptorLayout(+DescriptorSetBindingPoints::PER_RENDERABLE,
|
||||
descriptor_sets::getPerRenderableLayout());
|
||||
pb.descriptorLayout(
|
||||
|
||||
@@ -94,17 +94,17 @@ struct MaterialDefinition {
|
||||
backend::ShaderModel const sm, bool isStereoSupported) const noexcept;
|
||||
|
||||
backend::DescriptorSetLayout const& getPerViewDescriptorSetLayoutDescription(
|
||||
Variant const variant, bool useVsmDescriptorSetLayout) const noexcept;
|
||||
Variant const variant, bool useS2dDescriptorSetLayout) const noexcept;
|
||||
|
||||
// Keep track of the definitions of the descriptor set layouts, as these
|
||||
// may be used by some backends in parallel compilation of programs.
|
||||
backend::DescriptorSetLayout perViewDescriptorSetLayoutDescription;
|
||||
backend::DescriptorSetLayout perViewDescriptorSetLayoutVsmDescription;
|
||||
backend::DescriptorSetLayout perViewDescriptorSetLayoutPcfDescription;
|
||||
backend::DescriptorSetLayout perViewDescriptorSetLayoutS2dDescription;
|
||||
backend::DescriptorSetLayout descriptorSetLayoutDescription;
|
||||
|
||||
// try to order by frequency of use
|
||||
filament::DescriptorSetLayout perViewDescriptorSetLayout;
|
||||
filament::DescriptorSetLayout perViewDescriptorSetLayoutVsm;
|
||||
filament::DescriptorSetLayout perViewDescriptorSetLayoutPcf;
|
||||
filament::DescriptorSetLayout perViewDescriptorSetLayoutS2d;
|
||||
filament::DescriptorSetLayout descriptorSetLayout;
|
||||
backend::Program::DescriptorSetInfo programDescriptorBindings;
|
||||
|
||||
|
||||
@@ -802,7 +802,7 @@ FrameGraphId<FrameGraphTexture> PostProcessManager::ssr(FrameGraph& fg,
|
||||
|
||||
// use our special SSR variant, it can only be applied to object that have
|
||||
// the SCREEN_SPACE ReflectionMode.
|
||||
passBuilder.variant(Variant{ Variant::SPECIAL_SSR });
|
||||
passBuilder.variant(Variant{ Variant::SPECIAL_SSR_VARIANT });
|
||||
|
||||
// generate all our drawing commands, except blended objects.
|
||||
passBuilder.commandTypeFlags(RenderPass::CommandTypeFlags::SCREEN_SPACE_REFLECTIONS);
|
||||
|
||||
@@ -568,7 +568,7 @@ RenderPass::Command* RenderPass::generateCommandsImpl(CommandTypeFlags extraFlag
|
||||
if constexpr (isDepthPass) {
|
||||
cmd.info.materialVariant = variant;
|
||||
cmd.info.rasterState = {};
|
||||
cmd.info.rasterState.colorWrite = Variant::isPickingVariant(variant) || Variant::isVSMVariant(variant);
|
||||
cmd.info.rasterState.colorWrite = Variant::isPickingVariant(variant) || Variant::isDepthMomentsVariant(variant);
|
||||
cmd.info.rasterState.depthWrite = true;
|
||||
cmd.info.rasterState.depthFunc = RasterState::DepthFunc::GE;
|
||||
cmd.info.rasterState.alphaToCoverage = false;
|
||||
@@ -616,7 +616,7 @@ RenderPass::Command* RenderPass::generateCommandsImpl(CommandTypeFlags extraFlag
|
||||
bool const hasSkinningOrMorphing = hasSkinning || hasMorphing;
|
||||
|
||||
// if we are already an SSR variant, the SRE bit is already set
|
||||
static_assert(Variant::SPECIAL_SSR & Variant::SRE);
|
||||
static_assert(Variant::SPECIAL_SSR_VARIANT & Variant::SRE);
|
||||
Variant renderableVariant{ variant };
|
||||
|
||||
// we can't have SSR and shadowing together by construction
|
||||
|
||||
@@ -213,12 +213,14 @@ ShadowMap::ShaderParameters ShadowMap::updateDirectional(FEngine& engine,
|
||||
* Final shadow map transform
|
||||
*/
|
||||
|
||||
// Final shadow transform (focused warped light-space)
|
||||
// Final shadow transform (focused warped light-space), world space to clip space
|
||||
const mat4f S = F * (W * LMpMv);
|
||||
|
||||
// Computes St the transform to use in the shader to access the shadow map texture
|
||||
// i.e. it transforms a world-space vertex to a texture coordinate in the shadowmap
|
||||
const auto [Mt, Mn] = getTextureCoordsMapping(shadowMapInfo, getViewport());
|
||||
|
||||
// world space to texture atlas space
|
||||
const mat4f St = highPrecisionMultiply(Mt, S);
|
||||
|
||||
ShaderParameters shaderParameters;
|
||||
@@ -226,14 +228,12 @@ ShadowMap::ShaderParameters ShadowMap::updateDirectional(FEngine& engine,
|
||||
|
||||
// note: in texelSizeWorldSpace() below, we can use Mb * Mt * F * W because
|
||||
// L * Mp * Mv is a rigid transform for directional lights, and doesn't matter.
|
||||
// if Wp[3][1] is 0, then LISPSM was cancelled.
|
||||
// if Wp[3][1] is 0, then LiSPSM was canceled.
|
||||
if (useLispsm && Wp[3][1] != 0.0f) {
|
||||
shaderParameters.texelSizeAtOneMeterWs =
|
||||
texelSizeWorldSpace(Wp, mat4f(Mt * F), shadowMapInfo.shadowDimension);
|
||||
shaderParameters.texelSizeAtOneMeterWs = texelSizeWorldSpace(S, shadowMapInfo.shadowDimension);
|
||||
} else {
|
||||
// We know we're using an ortho projection
|
||||
shaderParameters.texelSizeAtOneMeterWs =
|
||||
texelSizeWorldSpace(St.upperLeft(), shadowMapInfo.shadowDimension);
|
||||
shaderParameters.texelSizeAtOneMeterWs = texelSizeWorldSpace(S.upperLeft(), shadowMapInfo.shadowDimension);
|
||||
}
|
||||
if (!shadowMapInfo.vsm) {
|
||||
shaderParameters.lightSpace = St;
|
||||
@@ -1117,84 +1117,67 @@ bool ShadowMap::intersectSegmentWithPlanarQuad(float3& UTILS_RESTRICT p,
|
||||
return hit;
|
||||
}
|
||||
|
||||
float ShadowMap::texelSizeWorldSpace(const mat3f& worldToShadowTexture,
|
||||
uint16_t shadowDimension) noexcept {
|
||||
float2 ShadowMap::texelSizeWorldSpace(const mat3f& clipFromWorld, uint16_t shadowDimension) noexcept {
|
||||
// The Jacobian of the transformation from texture-to-world is the matrix itself for
|
||||
// orthographic projections. We just need to inverse worldToShadowTexture,
|
||||
// orthographic projections. We just need to inverse shadowMapFromWorld,
|
||||
// which is guaranteed to be orthographic.
|
||||
// The two first columns give us how a texel maps in world-space.
|
||||
const float ures = 1.0f / float(shadowDimension);
|
||||
const float vres = 1.0f / float(shadowDimension);
|
||||
const mat3f shadowTextureToWorld(inverse(worldToShadowTexture));
|
||||
const float3 Jx = shadowTextureToWorld[0];
|
||||
const float3 Jy = shadowTextureToWorld[1];
|
||||
const float s = std::max(length(Jx) * ures, length(Jy) * vres);
|
||||
float const oneTexel = 2.0f / float(shadowDimension);
|
||||
mat3f const worldFromClip(inverse(clipFromWorld));
|
||||
float3 const Jx = worldFromClip[0];
|
||||
float3 const Jy = worldFromClip[1];
|
||||
float2 const s = float2{ length(Jx), length(Jy) } * oneTexel;
|
||||
return s;
|
||||
}
|
||||
|
||||
float ShadowMap::texelSizeWorldSpace(const mat4f& Wp, const mat4f& MbMtF,
|
||||
uint16_t shadowDimension) noexcept {
|
||||
// Here we compute the Jacobian of inverse(MbMtF * Wp).
|
||||
// The expression below has been computed with Mathematica. However, it's not very hard,
|
||||
// albeit error-prone, to do it by hand because MbMtF is a linear transform.
|
||||
// So we really only need to calculate the Jacobian of inverse(Wp) at inverse(MbMtF).
|
||||
//
|
||||
// Because we're only interested in the length of the columns of the Jacobian, we can use
|
||||
// Mb * Mt * F * Wp instead of the full expression Mb * Mt * F * Wp * Wv * L * Mp * Mv,
|
||||
// because Wv * L * Mp * Mv is a rigid transform, which doesn't affect the length of
|
||||
// the Jacobian's column vectors.
|
||||
/**
|
||||
* Calculates the Jacobian matrix J = ∂(u,v,d)/∂(x,y,z) for a 4x4 perspective projection.
|
||||
*
|
||||
* Given a view-space point P = [x, y, z, 1]^T and a projection matrix M,
|
||||
* the projected homogeneous coordinates are [X, Y, Z, W]^T = M * P.
|
||||
* The resulting NDC coordinates are u = X/W, v = Y/W, and depth d = Z/W.
|
||||
*
|
||||
* To find the Jacobian on the CPU, we apply the quotient rule to each component:
|
||||
* ∂(X/W) / ∂xi = ( (∂X/∂xi) * W - X * (∂W/∂xi) ) / W^2
|
||||
*
|
||||
* In matrix form, this can be expressed as:
|
||||
* J = (1/W) * [ M_sub - (1/W) * (T ⊗ w_grad) ]
|
||||
*
|
||||
* Where:
|
||||
* - W: The homogeneous w-component after projection (usually -z for standard mats).
|
||||
* - M_sub: The top-left 3x3 submatrix of M.
|
||||
* - T: The column vector [X, Y, Z]^T (pre-perspective divide).
|
||||
* - w_grad: The row vector [m30, m31, m32] (the first three elements of M's last row).
|
||||
* - ⊗: The outer product, resulting in a 3x3 matrix.
|
||||
*
|
||||
* This Jacobian describes the local "stretch" of the projection. For LiSPSM or
|
||||
* shadow mapping, the inverse Jacobian J^-1 provides the world-space footprint
|
||||
* of a shadow texel, which is essential for calculating an accurate,
|
||||
* non-constant depth bias to eliminate shadow acne.
|
||||
*
|
||||
* @param M The 4x4 projection matrix.
|
||||
* @param p The 3D point in view-space where the Jacobian is evaluated.
|
||||
* @return A 3x3 matrix representing the partial derivatives of (u,v,d) w.r.t (x,y,z).
|
||||
*/
|
||||
static mat3f jacobian(mat4f const& M, float3 const& p) noexcept {
|
||||
float4 const T = M * p;
|
||||
mat3f const M_sub = M.upperLeft();
|
||||
float3 const w_grad = { M[0].w, M[1].w, M[2].w };
|
||||
mat3f const t_cross_w{
|
||||
w_grad.x * T.xyz,
|
||||
w_grad.y * T.xyz,
|
||||
w_grad.z * T.xyz
|
||||
};
|
||||
return (M_sub - t_cross_w / T.w) / T.w;
|
||||
}
|
||||
|
||||
float2 ShadowMap::texelSizeWorldSpace(mat4f const& S, uint16_t const shadowDimension) noexcept {
|
||||
// The Jacobian is not constant, so we evaluate it in the center of the shadow-map texture.
|
||||
// It might be better to do this computation in the vertex shader.
|
||||
float3 const p = { 0.5f, 0.5f, 0.0f };
|
||||
|
||||
const float ures = 1.0f / float(shadowDimension);
|
||||
const float vres = 1.0f / float(shadowDimension);
|
||||
const float dres = 1.0f / 65536.0f;
|
||||
|
||||
constexpr bool JACOBIAN_ESTIMATE = false;
|
||||
if constexpr (JACOBIAN_ESTIMATE) {
|
||||
// This estimates the Jacobian -- this is a lot heavier. This is mostly for reference
|
||||
// and testing.
|
||||
const mat4f Si(inverse(MbMtF * Wp));
|
||||
const float3 p0 = mat4f::project(Si, p);
|
||||
const float3 p1 = mat4f::project(Si, p + float3{ 1, 0, 0 } * ures);
|
||||
const float3 p2 = mat4f::project(Si, p + float3{ 0, 1, 0 } * vres);
|
||||
const float3 p3 = mat4f::project(Si, p + float3{ 0, 0, 1 } * dres);
|
||||
const float3 Jx = p1 - p0;
|
||||
const float3 Jy = p2 - p0;
|
||||
const float3 UTILS_UNUSED Jz = p3 - p0;
|
||||
const float s = std::max(length(Jx), length(Jy));
|
||||
return s;
|
||||
}
|
||||
|
||||
const float n = Wp[0][0];
|
||||
const float A = Wp[1][1];
|
||||
const float B = Wp[3][1];
|
||||
const float sx = MbMtF[0][0];
|
||||
const float sy = MbMtF[1][1];
|
||||
const float sz = MbMtF[2][2];
|
||||
const float ox = MbMtF[3][0];
|
||||
const float oy = MbMtF[3][1];
|
||||
const float oz = MbMtF[3][2];
|
||||
|
||||
const float X = p.x - ox;
|
||||
const float Y = p.y - oy;
|
||||
const float Z = p.z - oz;
|
||||
|
||||
const float dz = A * sy - Y;
|
||||
const float nsxsz = n * sx * sz;
|
||||
const float j = -(B * sy) / (nsxsz * dz * dz);
|
||||
const mat3f J(mat3f::row_major_init{
|
||||
j * dz * sz, j * X * sz, 0.0f,
|
||||
0.0f, j * nsxsz, 0.0f,
|
||||
0.0f, j * Z * sx, j * dz * sx
|
||||
});
|
||||
|
||||
float3 const Jx = J[0] * ures;
|
||||
float3 const Jy = J[1] * vres;
|
||||
UTILS_UNUSED float3 const Jz = J[2] * dres;
|
||||
const float s = std::max(length(Jx), length(Jy));
|
||||
float3 const p = { 0.0f, 0.0f, 0.0f }; // clip-space
|
||||
float const oneTexel = 2.0f / float(shadowDimension);
|
||||
mat3f const J = jacobian(inverse(S), p);
|
||||
float2 const s = float2{ length(J[0]), length(J[1]) } * oneTexel;
|
||||
return s;
|
||||
}
|
||||
|
||||
|
||||
@@ -135,7 +135,7 @@ public:
|
||||
math::mat4f lightSpace{};
|
||||
math::float4 lightFromWorldZ{};
|
||||
math::float4 scissorNormalized{};
|
||||
float texelSizeAtOneMeterWs{};
|
||||
math::float2 texelSizeAtOneMeterWs{};
|
||||
};
|
||||
|
||||
// Call once per frame if the light, scene (or visible layers) or camera changes.
|
||||
@@ -319,11 +319,8 @@ private:
|
||||
|
||||
math::float4 getClampToEdgeCoords(ShadowMapInfo const& shadowMapInfo) const noexcept;
|
||||
|
||||
static float texelSizeWorldSpace(const math::mat3f& worldToShadowTexture,
|
||||
uint16_t shadowDimension) noexcept;
|
||||
|
||||
static float texelSizeWorldSpace(const math::mat4f& W, const math::mat4f& MbMtF,
|
||||
uint16_t shadowDimension) noexcept;
|
||||
static math::float2 texelSizeWorldSpace(const math::mat3f& clipFromWorld, uint16_t shadowDimension) noexcept;
|
||||
static math::float2 texelSizeWorldSpace(const math::mat4f& clipFromWorld, uint16_t shadowDimension) noexcept;
|
||||
|
||||
static constexpr Segment sBoxSegments[12] = {
|
||||
{ 0, 1 }, { 1, 3 }, { 3, 2 }, { 2, 0 },
|
||||
|
||||
@@ -742,17 +742,16 @@ ShadowMapManager::ShadowTechnique ShadowMapManager::updateCascadeShadowMaps(FEng
|
||||
|
||||
// Texel size is constant for directional light (although that's not true when LISPSM
|
||||
// is used, but in that case we're pretending it is).
|
||||
const float wsTexelSize = shaderParameters.texelSizeAtOneMeterWs;
|
||||
float2 const wsTexelSize = shaderParameters.texelSizeAtOneMeterWs;
|
||||
|
||||
auto& s = mShadowUb.edit();
|
||||
s.shadows[shadowIndex].layer = shadowMap.getLayer();
|
||||
s.shadows[shadowIndex].lightFromWorldMatrix = shaderParameters.lightSpace;
|
||||
s.shadows[shadowIndex].scissorNormalized = shaderParameters.scissorNormalized;
|
||||
s.shadows[shadowIndex].normalBias = normalBias * wsTexelSize;
|
||||
s.shadows[shadowIndex].texelSizeAtOneMeter = wsTexelSize;
|
||||
s.shadows[shadowIndex].normalBias = wsTexelSize * normalBias;
|
||||
s.shadows[shadowIndex].elvsm = options.vsm.elvsm;
|
||||
s.shadows[shadowIndex].bulbRadiusLs =
|
||||
mSoftShadowOptions.penumbraScale * options.shadowBulbRadius / wsTexelSize;
|
||||
mSoftShadowOptions.penumbraScale * options.shadowBulbRadius / length(wsTexelSize);
|
||||
|
||||
shadowTechnique |= ShadowTechnique::SHADOW_MAP;
|
||||
cascadeHasVisibleShadows |= 0x1u << i;
|
||||
@@ -833,7 +832,7 @@ void ShadowMapManager::prepareSpotShadowMap(ShadowMap& shadowMap, FEngine& engin
|
||||
// and if we need to generate it, update all the UBO data
|
||||
if (shadowMap.hasVisibleShadows()) {
|
||||
const size_t shadowIndex = shadowMap.getShadowIndex();
|
||||
const float wsTexelSizeAtOneMeter = shaderParameters.texelSizeAtOneMeterWs;
|
||||
const float2 wsTexelSizeAtOneMeter = shaderParameters.texelSizeAtOneMeterWs;
|
||||
// note: normalBias is set to zero for VSM
|
||||
const float normalBias = shadowMapInfo.vsm ? 0.0f : options->normalBias;
|
||||
|
||||
@@ -845,12 +844,11 @@ void ShadowMapManager::prepareSpotShadowMap(ShadowMap& shadowMap, FEngine& engin
|
||||
s.shadows[shadowIndex].scissorNormalized = shaderParameters.scissorNormalized;
|
||||
s.shadows[shadowIndex].normalBias = normalBias * wsTexelSizeAtOneMeter;
|
||||
s.shadows[shadowIndex].lightFromWorldZ = shaderParameters.lightFromWorldZ;
|
||||
s.shadows[shadowIndex].texelSizeAtOneMeter = wsTexelSizeAtOneMeter;
|
||||
s.shadows[shadowIndex].nearOverFarMinusNear = float(n / (f - n));
|
||||
s.shadows[shadowIndex].elvsm = options->vsm.elvsm;
|
||||
s.shadows[shadowIndex].bulbRadiusLs =
|
||||
mSoftShadowOptions.penumbraScale * options->shadowBulbRadius
|
||||
/ wsTexelSizeAtOneMeter;
|
||||
/ length(wsTexelSizeAtOneMeter);
|
||||
|
||||
}
|
||||
}
|
||||
@@ -926,7 +924,7 @@ void ShadowMapManager::preparePointShadowMap(ShadowMap& shadowMap,
|
||||
// and if we need to generate it, update all the UBO data
|
||||
if (shadowMap.hasVisibleShadows()) {
|
||||
const size_t shadowIndex = shadowMap.getShadowIndex();
|
||||
const float wsTexelSizeAtOneMeter = shaderParameters.texelSizeAtOneMeterWs;
|
||||
const float2 wsTexelSizeAtOneMeter = shaderParameters.texelSizeAtOneMeterWs;
|
||||
// note: normalBias is set to zero for VSM
|
||||
const float normalBias = shadowMapInfo.vsm ? 0.0f : options->normalBias;
|
||||
|
||||
@@ -938,12 +936,11 @@ void ShadowMapManager::preparePointShadowMap(ShadowMap& shadowMap,
|
||||
s.shadows[shadowIndex].scissorNormalized = shaderParameters.scissorNormalized;
|
||||
s.shadows[shadowIndex].normalBias = normalBias * wsTexelSizeAtOneMeter;
|
||||
s.shadows[shadowIndex].lightFromWorldZ = shaderParameters.lightFromWorldZ;
|
||||
s.shadows[shadowIndex].texelSizeAtOneMeter = wsTexelSizeAtOneMeter;
|
||||
s.shadows[shadowIndex].nearOverFarMinusNear = float(n / (f - n));
|
||||
s.shadows[shadowIndex].elvsm = options->vsm.elvsm;
|
||||
s.shadows[shadowIndex].bulbRadiusLs =
|
||||
mSoftShadowOptions.penumbraScale * options->shadowBulbRadius
|
||||
/ wsTexelSizeAtOneMeter;
|
||||
/ length(wsTexelSizeAtOneMeter);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -257,9 +257,9 @@ filament::DescriptorSetLayout const& FMaterial::getPerViewDescriptorSetLayout(
|
||||
}
|
||||
// mDefinition.perViewDescriptorSetLayout{Vsm} is already resolved for MaterialDomain
|
||||
if (useVsmDescriptorSetLayout) {
|
||||
return mDefinition.perViewDescriptorSetLayoutVsm;
|
||||
return mDefinition.perViewDescriptorSetLayoutS2d;
|
||||
}
|
||||
return mDefinition.perViewDescriptorSetLayout;
|
||||
return mDefinition.perViewDescriptorSetLayoutPcf;
|
||||
}
|
||||
|
||||
void FMaterial::compile(CompilerPriorityQueue const priority,
|
||||
|
||||
@@ -139,7 +139,7 @@ public:
|
||||
// This is mostly intended to be used for post-process materials; but it's also useful for
|
||||
// Surface material that behave like post-process material (i.e. that don't really have variants or for
|
||||
// which VSM is nonsensical, like for unlit materials)
|
||||
return mDefinition.perViewDescriptorSetLayout;
|
||||
return mDefinition.perViewDescriptorSetLayoutPcf;
|
||||
}
|
||||
|
||||
DescriptorSetLayout const& getPerViewDescriptorSetLayout(
|
||||
@@ -378,11 +378,11 @@ private:
|
||||
Variant variant = {}) const noexcept;
|
||||
|
||||
bool isSharedVariant(Variant const variant) const {
|
||||
// HACK: The default material "should" have VSM | DEP, but then we'd have to compile it as a
|
||||
// HACK: The default material "should" have MNT | DEP, but then we'd have to compile it as a
|
||||
// lit material, which would increase binary size. Perhaps we could specially compile it
|
||||
// with this variant, but with the shader program cache in active development, the days of
|
||||
// the default material are numbered anyway.
|
||||
constexpr Variant::type_t vsmAndDep = Variant::VSM | Variant::DEP;
|
||||
constexpr Variant::type_t vsmAndDep = Variant::MNT | Variant::DEP;
|
||||
return mDefinition.materialDomain == MaterialDomain::SURFACE && !mIsDefaultMaterial &&
|
||||
!mDefinition.hasCustomDepthShader && Variant::isValidDepthVariant(variant) &&
|
||||
(variant.key & vsmAndDep) != vsmAndDep;
|
||||
|
||||
@@ -969,10 +969,7 @@ void FRenderer::renderJob(DriverApi& driver, RootArenaScope& rootArenaScope, FVi
|
||||
variant.setDirectionalLighting(view.hasDirectionalLighting());
|
||||
variant.setDynamicLighting(view.hasDynamicLighting());
|
||||
variant.setFog(view.hasFog());
|
||||
// The VSM bit has a different meaning for STANDARD_VARIANT (as opposed to DEPTH_VARIANT),
|
||||
// In the STANDARD_VARIANT case, we are *using* the shadow-map, and the VSM only decides which
|
||||
// type of sampler is used (samplerShadow or sampler2D).
|
||||
variant.setVsm(view.hasShadowing() && view.getShadowType() != ShadowType::PCF);
|
||||
variant.setShadowSampler2D(view.hasShadowing() && view.getShadowType() != ShadowType::PCF);
|
||||
variant.setStereo(view.hasStereo());
|
||||
|
||||
/*
|
||||
@@ -981,10 +978,7 @@ void FRenderer::renderJob(DriverApi& driver, RootArenaScope& rootArenaScope, FVi
|
||||
|
||||
if (view.needsShadowMap()) {
|
||||
Variant shadowVariant(Variant::DEPTH_VARIANT);
|
||||
// The VSM bit has a different meaning for DEPTH_VARIANT (as opposed to STANDARD_VARIANT),
|
||||
// In the DEPTH_VARIANT case, we are *generating* the shadow-map, and some computations
|
||||
// are handled differently. In addition, the color buffer is used.
|
||||
shadowVariant.setVsm(view.getShadowType() == ShadowType::VSM);
|
||||
shadowVariant.setDepthMoments(view.getShadowType() == ShadowType::VSM);
|
||||
|
||||
auto shadows = view.renderShadowMaps(engine, fg, cameraInfo, mShaderUserTime,
|
||||
RenderPassBuilder{ commandArena }
|
||||
|
||||
@@ -143,7 +143,7 @@ FView::FView(FEngine& engine)
|
||||
#ifndef NDEBUG
|
||||
// This can fail if another view has already registered this data source
|
||||
mDebugState->owner = debugRegistry.registerDataSource("d.view.frame_info",
|
||||
[weak = std::weak_ptr(mDebugState)]() -> DebugRegistry::DataSource {
|
||||
[weak = std::weak_ptr<DebugState>(mDebugState)]() -> DebugRegistry::DataSource {
|
||||
// the View could have been destroyed by the time we do this
|
||||
auto const state = weak.lock();
|
||||
if (!state) {
|
||||
|
||||
@@ -1,12 +1,12 @@
|
||||
Pod::Spec.new do |spec|
|
||||
spec.name = "Filament"
|
||||
spec.version = "1.69.5"
|
||||
spec.version = "1.69.4"
|
||||
spec.license = { :type => "Apache 2.0", :file => "LICENSE" }
|
||||
spec.homepage = "https://google.github.io/filament"
|
||||
spec.authors = "Google LLC."
|
||||
spec.summary = "Filament is a real-time physically based rendering engine for Android, iOS, Windows, Linux, macOS, and WASM/WebGL."
|
||||
spec.platform = :ios, "11.0"
|
||||
spec.source = { :http => "https://github.com/google/filament/releases/download/v1.69.5/filament-v1.69.5-ios.tgz" }
|
||||
spec.source = { :http => "https://github.com/google/filament/releases/download/v1.69.4/filament-v1.69.4-ios.tgz" }
|
||||
|
||||
spec.libraries = 'c++'
|
||||
|
||||
|
||||
@@ -36,7 +36,7 @@ backend::DescriptorSetLayout const& getPerRenderableLayout() noexcept;
|
||||
backend::DescriptorSetLayout getPerViewDescriptorSetLayout(
|
||||
MaterialDomain domain,
|
||||
bool isLit, bool isSSR, bool hasFog,
|
||||
bool isVSM) noexcept;
|
||||
bool isShadowSampler2D) noexcept;
|
||||
|
||||
backend::DescriptorSetLayout getPerViewDescriptorSetLayoutWithVariant(
|
||||
Variant variant,
|
||||
|
||||
@@ -299,10 +299,9 @@ struct ShadowUib { // NOLINT(cppcoreguidelines-pro-type-member-init)
|
||||
math::mat4f lightFromWorldMatrix; // 64
|
||||
math::float4 lightFromWorldZ; // 16
|
||||
math::float4 scissorNormalized; // 16
|
||||
float texelSizeAtOneMeter; // 4
|
||||
float bulbRadiusLs; // 4
|
||||
float nearOverFarMinusNear; // 4
|
||||
float normalBias; // 4
|
||||
math::float2 normalBias; // 4
|
||||
bool elvsm; // 4
|
||||
uint32_t layer; // 4
|
||||
uint32_t reserved1; // 4
|
||||
|
||||
@@ -48,20 +48,21 @@ struct Variant {
|
||||
// SRE: Shadow Receiver
|
||||
// SKN: Skinning
|
||||
// DEP: Depth only
|
||||
// FOG: Fog
|
||||
// PCK: Picking (depth variant only)
|
||||
// VSM: Variance shadow maps (depth) / sampler type (standard)
|
||||
// FOG: Fog (standard)
|
||||
// PCK: Picking (depth)
|
||||
// MNT: Output depth moments (depth)
|
||||
// S2D: Sampler type for shadows (0: samplerShadowArray, 1: sampler2DArray) (standard)
|
||||
// STE: Instanced stereo rendering
|
||||
//
|
||||
// X: either 1 or 0
|
||||
// +-----+-----+-----+-----+-----+-----+-----+-----+
|
||||
// Variant | STE | VSM | FOG | DEP | SKN | SRE | DYN | DIR | 256
|
||||
// Variant | STE | S2D | FOG | DEP | SKN | SRE | DYN | DIR | 256
|
||||
// +-----+-----+-----+-----+-----+-----+-----+-----+
|
||||
// PCK
|
||||
// MNT PCK
|
||||
//
|
||||
// Standard variants:
|
||||
// +-----+-----+-----+-----+-----+-----+-----+-----+
|
||||
// | STE | VSM | FOG | 0 | SKN | SRE | DYN | DIR | 128 - 44 = 84
|
||||
// | STE | S2D | FOG | 0 | SKN | SRE | DYN | DIR | 128 - 44 = 84
|
||||
// +-----+-----+-----+-----+-----+-----+-----+-----+
|
||||
// Vertex shader X 0 0 0 X X X X
|
||||
// Fragment shader 0 X X 0 0 X X X
|
||||
@@ -72,7 +73,7 @@ struct Variant {
|
||||
//
|
||||
// Depth variants:
|
||||
// +-----+-----+-----+-----+-----+-----+-----+-----+
|
||||
// | STE | VSM | PCK | 1 | SKN | 0 | 0 | 0 | 16 - 4 = 12
|
||||
// | STE | MNT | PCK | 1 | SKN | 0 | 0 | 0 | 16 - 4 = 12
|
||||
// +-----+-----+-----+-----+-----+-----+-----+-----+
|
||||
// Vertex depth X X 0 1 X 0 0 0
|
||||
// Fragment depth 0 0 X 1 0 0 0 0
|
||||
@@ -96,13 +97,15 @@ struct Variant {
|
||||
static constexpr type_t DEP = 0x10; // depth only variants
|
||||
static constexpr type_t FOG = 0x20; // fog (standard)
|
||||
static constexpr type_t PCK = 0x20; // picking (depth)
|
||||
static constexpr type_t VSM = 0x40; // variance shadow maps / sampler type
|
||||
static constexpr type_t S2D = 0x40; // sampler type
|
||||
static constexpr type_t MNT = 0x40; // variance shadow maps
|
||||
static constexpr type_t STE = 0x80; // instanced stereo
|
||||
|
||||
static constexpr type_t NO_VARIANT = 0u;
|
||||
|
||||
// special variants (variants that use the reserved space)
|
||||
static constexpr type_t SPECIAL_SSR = VSM | SRE; // screen-space reflections variant
|
||||
static constexpr type_t SPECIAL_SSR_VARIANT= S2D | SRE ;
|
||||
static constexpr type_t SPECIAL_SSR_MASK = STE | S2D | DEP | SRE | DYN | DIR;
|
||||
|
||||
static constexpr type_t STANDARD_MASK = DEP;
|
||||
static constexpr type_t STANDARD_VARIANT = 0u;
|
||||
@@ -127,29 +130,30 @@ struct Variant {
|
||||
void setSkinning(bool v) noexcept { set(v, SKN); }
|
||||
void setFog(bool v) noexcept { set(v, FOG); }
|
||||
void setPicking(bool v) noexcept { set(v, PCK); }
|
||||
void setVsm(bool v) noexcept { set(v, VSM); }
|
||||
void setShadowSampler2D(bool v) noexcept { set(v, S2D); }
|
||||
void setDepthMoments(bool v) noexcept { set(v, MNT); }
|
||||
void setStereo(bool v) noexcept { set(v, STE); }
|
||||
|
||||
static constexpr bool isValidDepthVariant(Variant variant) noexcept {
|
||||
// Can't have VSM and PICKING together with DEPTH variants
|
||||
constexpr type_t RESERVED_MASK = VSM | PCK | DEP | SRE | DYN | DIR;
|
||||
constexpr type_t RESERVED_VALUE = VSM | PCK | DEP;
|
||||
constexpr type_t RESERVED_MASK = MNT | PCK | DEP | SRE | DYN | DIR;
|
||||
constexpr type_t RESERVED_VALUE = MNT | PCK | DEP;
|
||||
return ((variant.key & DEPTH_MASK) == DEPTH_VARIANT) &&
|
||||
((variant.key & RESERVED_MASK) != RESERVED_VALUE);
|
||||
}
|
||||
|
||||
static constexpr bool isValidStandardVariant(Variant variant) noexcept {
|
||||
// can't have shadow receiver if we don't have any lighting
|
||||
constexpr type_t RESERVED0_MASK = VSM | FOG | SRE | DYN | DIR;
|
||||
constexpr type_t RESERVED0_VALUE = VSM | FOG | SRE;
|
||||
constexpr type_t RESERVED0_MASK = S2D | FOG | SRE | DYN | DIR;
|
||||
constexpr type_t RESERVED0_VALUE = S2D | FOG | SRE;
|
||||
|
||||
// can't have shadow receiver if we don't have any lighting
|
||||
constexpr type_t RESERVED1_MASK = VSM | SRE | DYN | DIR;
|
||||
constexpr type_t RESERVED1_MASK = S2D | SRE | DYN | DIR;
|
||||
constexpr type_t RESERVED1_VALUE = SRE;
|
||||
|
||||
// can't have VSM without shadow receiver
|
||||
constexpr type_t RESERVED2_MASK = VSM | SRE;
|
||||
constexpr type_t RESERVED2_VALUE = VSM;
|
||||
constexpr type_t RESERVED2_MASK = S2D | SRE;
|
||||
constexpr type_t RESERVED2_VALUE = S2D;
|
||||
|
||||
return ((variant.key & STANDARD_MASK) == STANDARD_VARIANT) &&
|
||||
((variant.key & RESERVED0_MASK) != RESERVED0_VALUE) &&
|
||||
@@ -174,11 +178,15 @@ struct Variant {
|
||||
}
|
||||
|
||||
static constexpr bool isSSRVariant(Variant variant) noexcept {
|
||||
return (variant.key & (STE | VSM | DEP | SRE | DYN | DIR)) == (VSM | SRE);
|
||||
return (variant.key & SPECIAL_SSR_MASK) == SPECIAL_SSR_VARIANT;
|
||||
}
|
||||
|
||||
static constexpr bool isVSMVariant(Variant variant) noexcept {
|
||||
return !isSSRVariant(variant) && ((variant.key & VSM) == VSM);
|
||||
static constexpr bool isShadowSampler2DVariant(Variant variant) noexcept {
|
||||
return !isSSRVariant(variant) && ((variant.key & (S2D | DEP)) == S2D);
|
||||
}
|
||||
|
||||
static constexpr bool isDepthMomentsVariant(Variant variant) noexcept {
|
||||
return !isSSRVariant(variant) && ((variant.key & (MNT | DEP)) == (MNT | DEP));
|
||||
}
|
||||
|
||||
static constexpr bool isShadowReceiverVariant(Variant variant) noexcept {
|
||||
@@ -202,13 +210,13 @@ struct Variant {
|
||||
// vertex shader.
|
||||
if ((variant.key & STANDARD_MASK) == STANDARD_VARIANT) {
|
||||
if (isSSRVariant(variant)) {
|
||||
variant.key &= ~(VSM | SRE);
|
||||
variant.key &= ~SPECIAL_SSR_VARIANT;
|
||||
}
|
||||
return variant & (STE | SKN | SRE | DYN | DIR);
|
||||
}
|
||||
if ((variant.key & DEPTH_MASK) == DEPTH_VARIANT) {
|
||||
// Only VSM, skinning, and stereo affect the vertex shader's DEPTH variant
|
||||
return variant & (STE | VSM | SKN | DEP);
|
||||
// Only MNT, skinning, and stereo affect the vertex shader's DEPTH variant
|
||||
return variant & (STE | MNT | SKN | DEP);
|
||||
}
|
||||
return {};
|
||||
}
|
||||
@@ -217,11 +225,11 @@ struct Variant {
|
||||
// filter out fragment variants that are not needed. For e.g. skinning doesn't
|
||||
// affect the fragment shader.
|
||||
if ((variant.key & STANDARD_MASK) == STANDARD_VARIANT) {
|
||||
return variant & (VSM | FOG | SRE | DYN | DIR);
|
||||
return variant & (S2D | FOG | SRE | DYN | DIR);
|
||||
}
|
||||
if ((variant.key & DEPTH_MASK) == DEPTH_VARIANT) {
|
||||
// Only VSM & PICKING affects the fragment shader's DEPTH variant
|
||||
return variant & (VSM | PCK | DEP);
|
||||
return variant & (MNT | PCK | DEP);
|
||||
}
|
||||
return {};
|
||||
}
|
||||
@@ -230,8 +238,8 @@ struct Variant {
|
||||
// special case for depth variant
|
||||
if (isValidDepthVariant(variant)) {
|
||||
if (!isLit) {
|
||||
// if we're unlit, we never need the VSM variant
|
||||
return variant & ~VSM;
|
||||
// if we're unlit, we never need the MNT variant
|
||||
return variant & ~MNT;
|
||||
}
|
||||
return variant;
|
||||
}
|
||||
@@ -242,9 +250,9 @@ struct Variant {
|
||||
// when the shading mode is unlit, remove all the lighting variants
|
||||
return variant & UNLIT_MASK;
|
||||
}
|
||||
// if shadow receiver is disabled, turn off VSM
|
||||
// if shadow receiver is disabled, we pick the shadow sampler
|
||||
if (!(variant.key & SRE)) {
|
||||
return variant & ~VSM;
|
||||
return variant & ~S2D;
|
||||
}
|
||||
return variant;
|
||||
}
|
||||
|
||||
@@ -216,7 +216,7 @@ utils::CString getDescriptorName(DescriptorSetBindingPoints const set,
|
||||
DescriptorSetLayout getPerViewDescriptorSetLayout(
|
||||
MaterialDomain const domain,
|
||||
bool const isLit, bool const isSSR, bool const hasFog,
|
||||
bool const isVSM) noexcept {
|
||||
bool const isShadowSampler2D) noexcept {
|
||||
|
||||
switch (domain) {
|
||||
case MaterialDomain::SURFACE: {
|
||||
@@ -255,7 +255,7 @@ DescriptorSetLayout getPerViewDescriptorSetLayout(
|
||||
}
|
||||
|
||||
// change the SHADOW_MAP descriptor type for VSM
|
||||
if (isVSM) {
|
||||
if (isShadowSampler2D) {
|
||||
auto const pos = std::find_if(layout.descriptors.begin(), layout.descriptors.end(),
|
||||
[](auto const& v) {
|
||||
return v.binding == PerViewBindingPoints::SHADOW_MAP;
|
||||
@@ -285,8 +285,7 @@ DescriptorSetLayout getPerViewDescriptorSetLayoutWithVariant(
|
||||
return ssrVariantDescriptorSetLayout;
|
||||
}
|
||||
// We need to filter out all the descriptors not included in the "resolved" layout below
|
||||
return getPerViewDescriptorSetLayout(domain, isLit, isSSR, hasFog,
|
||||
Variant::isVSMVariant(variant));
|
||||
return getPerViewDescriptorSetLayout(domain, isLit, isSSR, hasFog, Variant::isShadowSampler2DVariant(variant));
|
||||
}
|
||||
|
||||
DescriptorType getDescriptorType(SamplerType const type, SamplerFormat const format) {
|
||||
|
||||
@@ -32,41 +32,46 @@ namespace filament {
|
||||
Variant Variant::filterUserVariant(
|
||||
Variant variant, UserVariantFilterMask filterMask) noexcept {
|
||||
// these are easy to filter by just removing the corresponding bit
|
||||
if (filterMask & (uint32_t)UserVariantFilterBit::DIRECTIONAL_LIGHTING) {
|
||||
if (filterMask & uint32_t(UserVariantFilterBit::DIRECTIONAL_LIGHTING)) {
|
||||
variant.key &= ~DIR;
|
||||
}
|
||||
if (filterMask & (uint32_t)UserVariantFilterBit::DYNAMIC_LIGHTING) {
|
||||
|
||||
if (filterMask & uint32_t(UserVariantFilterBit::DYNAMIC_LIGHTING)) {
|
||||
variant.key &= ~DYN;
|
||||
}
|
||||
if (filterMask & (uint32_t)UserVariantFilterBit::SKINNING) {
|
||||
|
||||
if (filterMask & uint32_t(UserVariantFilterBit::SKINNING)) {
|
||||
variant.key &= ~SKN;
|
||||
}
|
||||
if (filterMask & (uint32_t)UserVariantFilterBit::STE) {
|
||||
|
||||
if (filterMask & uint32_t(UserVariantFilterBit::STE)) {
|
||||
variant.key &= ~(filterMask & STE);
|
||||
}
|
||||
if (!isValidDepthVariant(variant)) {
|
||||
// we can't remove FOG from depth variants, this would, in fact, remove picking
|
||||
if (filterMask & (uint32_t)UserVariantFilterBit::FOG) {
|
||||
variant.key &= ~FOG;
|
||||
|
||||
if (isValidDepthVariant(variant)) {
|
||||
// depth variants can have their MNT bit filtered
|
||||
if (filterMask & uint32_t(UserVariantFilterBit::VSM)) {
|
||||
variant.key &= ~MNT;
|
||||
}
|
||||
} else {
|
||||
// depth variants can have their VSM bit filtered
|
||||
if (filterMask & (uint32_t)UserVariantFilterBit::VSM) {
|
||||
variant.key &= ~VSM;
|
||||
// we can't remove FOG from depth variants, this would, in fact, remove picking
|
||||
if (filterMask & uint32_t(UserVariantFilterBit::FOG)) {
|
||||
variant.key &= ~FOG;
|
||||
}
|
||||
}
|
||||
|
||||
if (!isSSRVariant(variant)) {
|
||||
// SSR variant needs to be handled separately
|
||||
if (filterMask & (uint32_t)UserVariantFilterBit::SHADOW_RECEIVER) {
|
||||
if (filterMask & uint32_t(UserVariantFilterBit::SHADOW_RECEIVER)) {
|
||||
variant.key &= ~SRE;
|
||||
}
|
||||
if (filterMask & (uint32_t)UserVariantFilterBit::VSM) {
|
||||
variant.key &= ~VSM;
|
||||
if (filterMask & uint32_t(UserVariantFilterBit::VSM)) {
|
||||
variant.key &= ~S2D;
|
||||
}
|
||||
} else {
|
||||
// see if we need to filter out the SSR variants
|
||||
if (filterMask & (uint32_t)UserVariantFilterBit::SSR) {
|
||||
variant.key &= ~SPECIAL_SSR;
|
||||
if (filterMask & uint32_t(UserVariantFilterBit::SSR)) {
|
||||
variant.key &= ~SPECIAL_SSR_VARIANT;
|
||||
}
|
||||
}
|
||||
return variant;
|
||||
|
||||
@@ -60,7 +60,8 @@ void ShaderGenerator::generateSurfaceMaterialVariantDefines(io::sstream& out,
|
||||
CodeGenerator::generateDefine(out, "VARIANT_HAS_SHADOWING",
|
||||
litVariants && filament::Variant::isShadowReceiverVariant(variant));
|
||||
CodeGenerator::generateDefine(out, "VARIANT_HAS_VSM",
|
||||
filament::Variant::isVSMVariant(variant));
|
||||
filament::Variant::isShadowSampler2DVariant(variant) ||
|
||||
filament::Variant::isDepthMomentsVariant(variant));
|
||||
CodeGenerator::generateDefine(out, "VARIANT_HAS_STEREO",
|
||||
hasStereo(variant, featureLevel));
|
||||
CodeGenerator::generateDefine(out, "VARIANT_DEPTH",
|
||||
|
||||
@@ -49,7 +49,7 @@ SamplerInterfaceBlock const& SibGenerator::getPerViewSib(Variant variant) noexce
|
||||
// reason we name them "unused*" to ensure we're not using them by mistake (type/format don't
|
||||
// matter).
|
||||
|
||||
static SamplerInterfaceBlock const sibPcf{ SamplerInterfaceBlock::Builder()
|
||||
static SamplerInterfaceBlock const sibShadowSamplerPcf{ SamplerInterfaceBlock::Builder()
|
||||
.name("sampler0")
|
||||
.stageFlags(backend::ShaderStageFlags::FRAGMENT)
|
||||
.add( {{ "shadowMap", +PerViewBindingPoints::SHADOW_MAP, Type::SAMPLER_2D_ARRAY, Format::SHADOW, Precision::MEDIUM, FILTERABLE, !MULTISAMPLE, ALL_STAGES },
|
||||
@@ -62,7 +62,7 @@ SamplerInterfaceBlock const& SibGenerator::getPerViewSib(Variant variant) noexce
|
||||
)
|
||||
.build() };
|
||||
|
||||
static SamplerInterfaceBlock const sibVsm{ SamplerInterfaceBlock::Builder()
|
||||
static SamplerInterfaceBlock const sibShadowSampler2D{ SamplerInterfaceBlock::Builder()
|
||||
.name("sampler0")
|
||||
.stageFlags(backend::ShaderStageFlags::FRAGMENT)
|
||||
.add( {{ "shadowMap", +PerViewBindingPoints::SHADOW_MAP, Type::SAMPLER_2D_ARRAY, Format::FLOAT, Precision::HIGH, FILTERABLE, !MULTISAMPLE, ALL_STAGES },
|
||||
@@ -85,10 +85,10 @@ SamplerInterfaceBlock const& SibGenerator::getPerViewSib(Variant variant) noexce
|
||||
|
||||
if (Variant::isSSRVariant(variant)) {
|
||||
return sibSsr;
|
||||
} else if (Variant::isVSMVariant(variant)) {
|
||||
return sibVsm;
|
||||
} else if (Variant::isShadowSampler2DVariant(variant)) {
|
||||
return sibShadowSampler2D;
|
||||
} else {
|
||||
return sibPcf;
|
||||
return sibShadowSamplerPcf;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -48,10 +48,11 @@ std::string formatVariantString(Variant variant, MaterialDomain domain) noexcept
|
||||
if (variant.key & Variant::DEP) variantString += "DEP|";
|
||||
if (variant.key & Variant::DEP) {
|
||||
if (variant.key & Variant::PCK) variantString += "PCK|";
|
||||
if (variant.key & Variant::MNT) variantString += "MNT|";
|
||||
} else {
|
||||
if (variant.key & Variant::FOG) variantString += "FOG|";
|
||||
if (variant.key & Variant::S2D) variantString += "S2D|";
|
||||
}
|
||||
if (variant.key & Variant::VSM) variantString += "VSM|";
|
||||
if (variant.key & Variant::STE) variantString += "STE|";
|
||||
variantString = variantString.substr(0, variantString.length() - 1);
|
||||
}
|
||||
|
||||
@@ -111,7 +111,7 @@ highp vec4 getSpotLightSpacePosition(int index, highp vec3 dir, highp float zLig
|
||||
highp mat4 lightFromWorldMatrix = shadowUniforms.shadows[index].lightFromWorldMatrix;
|
||||
|
||||
// for spotlights, the bias depends on z
|
||||
float bias = shadowUniforms.shadows[index].normalBias * zLight;
|
||||
highp vec2 bias = shadowUniforms.shadows[index].normalBias * zLight;
|
||||
|
||||
return computeLightSpacePosition(getWorldPosition(), getWorldGeometricNormalVector(),
|
||||
dir, bias, lightFromWorldMatrix);
|
||||
|
||||
@@ -12,12 +12,51 @@
|
||||
*/
|
||||
|
||||
highp vec4 computeLightSpacePosition(highp vec3 p, const highp vec3 n,
|
||||
const highp vec3 dir, const float b, highp_mat4 lightFromWorldMatrix) {
|
||||
const highp vec3 dir, const highp vec2 b, highp_mat4 lightFromWorldMatrix) {
|
||||
|
||||
#if !defined(VARIANT_HAS_VSM)
|
||||
highp float cosTheta = saturate(dot(n, dir));
|
||||
highp float sinTheta = sqrt(1.0 - cosTheta * cosTheta);
|
||||
p += n * (sinTheta * b);
|
||||
// --------------------------------------------------------------------------------------
|
||||
// Anisotropic Normal Bias for Shadow Mapping
|
||||
// --------------------------------------------------------------------------------------
|
||||
// To prevent shadow acne, we must push the geometry along its normal to clear the
|
||||
// quantization steps of the shadow map's discrete depth grid. The exact physical depth
|
||||
// error we must clear is proportional to the shadow texel's world-space dimensions.
|
||||
//
|
||||
// This implementation computes the exact geometric projection of the rectangular
|
||||
// shadow map texel onto the surface normal.
|
||||
//
|
||||
// 1. Coordinate Space Transition:
|
||||
// We project the world-space normal onto the light's X and Y basis vectors (L_right,
|
||||
// L_up). This gives us the lateral components of the normal in Light Space (n_Lx, n_Ly).
|
||||
//
|
||||
// 2. The Implicit sin(theta) Slope Scale:
|
||||
// Because the normal is a unit vector, the magnitude of its lateral components in
|
||||
// light space inherently equals sin(theta), where theta is the angle of incidence.
|
||||
// This perfectly and automatically scales the bias from 0.0 (top-down, flat surface)
|
||||
// to maximum (grazing angle).
|
||||
//
|
||||
// 3. Exact Anisotropic Footprint (The L1 Norm):
|
||||
// Shadow texels are rarely perfectly square due to Cascaded Shadow Maps (CSM) or
|
||||
// Light Space Perspective Shadow Maps (LiSPSM). Jx and Jy are the physical world-space
|
||||
// dimensions of the texel.
|
||||
// By evaluating `abs(n_Lx * Jx) + abs(n_Ly * Jy)`, we compute the exact scalar
|
||||
// projection of the rectangular texel footprint.
|
||||
// - It is superior to `max(Jx, Jy)` which assumes a massive square and causes Peter Panning.
|
||||
// - It is superior to `length()` which assumes an ellipse and under-biases the corners.
|
||||
// --------------------------------------------------------------------------------------
|
||||
|
||||
// Extract the first row (Light's Right vector in World Space)
|
||||
highp vec3 L_right = vec3(lightFromWorldMatrix[0][0], lightFromWorldMatrix[1][0], lightFromWorldMatrix[2][0]);
|
||||
|
||||
// Extract the second row (Light's Up vector in World Space)
|
||||
highp vec3 L_up = vec3(lightFromWorldMatrix[0][1], lightFromWorldMatrix[1][1], lightFromWorldMatrix[2][1]);
|
||||
|
||||
// Project the world normal onto the shadow map's 2D grid
|
||||
highp float n_Lx = dot(n, L_right);
|
||||
highp float n_Ly = dot(n, L_up);
|
||||
|
||||
// Apply the anisotropic normal bias
|
||||
p += n * (abs(n_Lx * b.x) + abs(n_Ly * b.y));
|
||||
#endif
|
||||
|
||||
return mulMat4x4Float3(lightFromWorldMatrix, p);
|
||||
|
||||
@@ -5,10 +5,9 @@ struct ShadowData {
|
||||
highp mat4 lightFromWorldMatrix;
|
||||
highp vec4 lightFromWorldZ;
|
||||
highp vec4 scissorNormalized;
|
||||
mediump float texelSizeAtOneMeter;
|
||||
mediump float bulbRadiusLs;
|
||||
mediump float nearOverFarMinusNear;
|
||||
mediump float normalBias;
|
||||
highp vec2 normalBias;
|
||||
bool elvsm;
|
||||
mediump uint layer;
|
||||
mediump uint reserved1;
|
||||
|
||||
@@ -76,10 +76,11 @@ into **branch** of `filament-assets`. This branch is paired with a PR or commit
|
||||
|
||||
As an example, imagine I am working on a PR, and I've uploaded my change, which is in a
|
||||
branch called `my-pr-branch`, to `filament`. This PR requires updating the golden. We would do
|
||||
it in the following fashion
|
||||
it in the following fashion on a macOS machine:
|
||||
|
||||
### Using a script to update the golden repo
|
||||
|
||||
- Make sure you've completed the steps in 'Setting up python'
|
||||
- Run interactive mode in the `update_golden.py` script.
|
||||
```
|
||||
python3 test/renderdiff/src/update_golden.py
|
||||
|
||||
@@ -16,6 +16,7 @@ import os
|
||||
import shutil
|
||||
import re
|
||||
import sys
|
||||
import tempfile
|
||||
|
||||
from utils import execute, ArgParseImpl, mkdir_p
|
||||
|
||||
@@ -99,10 +100,11 @@ class GoldenManager:
|
||||
code, old_commit = execute(f'git log --format=%B -n 1', cwd=assets_dir)
|
||||
if tag and len(tag) > 0:
|
||||
old_commit += f'\nFILAMENT={tag}'
|
||||
COMMIT_FILE = '/tmp/golden_commit.txt'
|
||||
with open(COMMIT_FILE, 'w') as f:
|
||||
with tempfile.NamedTemporaryFile('w', delete=False) as f:
|
||||
f.write(old_commit)
|
||||
self._git_exec(f'commit --amend -F {COMMIT_FILE}')
|
||||
commit_file = f.name
|
||||
self._git_exec(f'commit --amend -F {commit_file}')
|
||||
os.remove(commit_file)
|
||||
|
||||
# Do the actual merge
|
||||
self._git_exec(f'checkout main')
|
||||
@@ -139,11 +141,11 @@ class GoldenManager:
|
||||
os.path.join(rdiff_dir, f))
|
||||
self._git_exec(f'add {os.path.join(GOLDENS_DIR, f)}')
|
||||
|
||||
TMP_GOLDEN_COMMIT_FILE = '/tmp/golden_commit.txt'
|
||||
|
||||
with open(TMP_GOLDEN_COMMIT_FILE, 'w') as f:
|
||||
with tempfile.NamedTemporaryFile('w', delete=False) as f:
|
||||
f.write(commit_msg)
|
||||
self._git_exec(f'commit -a -F {TMP_GOLDEN_COMMIT_FILE}')
|
||||
tmp_golden_commit_file = f.name
|
||||
self._git_exec(f'commit -a -F {tmp_golden_commit_file}')
|
||||
os.remove(tmp_golden_commit_file)
|
||||
if push_to_remote and \
|
||||
(self.access_token_ or self.access_type_ == ACCESS_TYPE_SSH):
|
||||
self._git_exec(f'push -f origin {branch}')
|
||||
|
||||
@@ -64,19 +64,19 @@ def _do_update(golden_manager, config):
|
||||
|
||||
def _same_image_diffimg(diffimg_path, img1, img2):
|
||||
cmd = [diffimg_path, img1, img2]
|
||||
try:
|
||||
result = subprocess.run(cmd, capture_output=True, text=True)
|
||||
output = result.stdout.strip()
|
||||
if not output:
|
||||
return False
|
||||
result = subprocess.run(cmd, capture_output=True, text=True)
|
||||
if result.returncode != 0:
|
||||
raise RuntimeError(f"diffimg failed with return code {result.returncode}:\n{result.stderr}")
|
||||
|
||||
try:
|
||||
res_json = json.loads(output)
|
||||
return res_json.get('passed', False)
|
||||
except json.JSONDecodeError:
|
||||
return False
|
||||
except Exception:
|
||||
return False
|
||||
output = result.stdout.strip()
|
||||
if not output:
|
||||
raise RuntimeError("diffimg produced no output")
|
||||
|
||||
try:
|
||||
res_json = json.loads(output)
|
||||
return res_json.get('passed', False)
|
||||
except json.JSONDecodeError as e:
|
||||
raise RuntimeError(f"Failed to parse diffimg output: {e}\nOutput: {output}")
|
||||
|
||||
def _get_deletes_updates(update_dir, golden_dir, diffimg_path):
|
||||
ret_delete = []
|
||||
@@ -93,13 +93,13 @@ def _get_deletes_updates(update_dir, golden_dir, diffimg_path):
|
||||
# However, update_golden typically only updates/adds based on the new render set.
|
||||
# But strict sync might imply deleting missing ones.
|
||||
# The original logic was: delete = list(base - new).
|
||||
delete = list(base - new)
|
||||
delete_files = list(base_files - new_files)
|
||||
|
||||
# Files in new but not in base are definitely updates (additions)
|
||||
update = list(new - base)
|
||||
update_files = list(new_files - base_files)
|
||||
|
||||
# Files in both need comparison
|
||||
for fpath in base.intersection(new_files):
|
||||
for fpath in base_files.intersection(new_files):
|
||||
base_fpath = os.path.join(golden_dir, fpath)
|
||||
new_fpath = os.path.join(update_dir, fpath)
|
||||
|
||||
@@ -110,10 +110,10 @@ def _get_deletes_updates(update_dir, golden_dir, diffimg_path):
|
||||
is_different = _file_as_str(new_fpath) != _file_as_str(base_fpath)
|
||||
|
||||
if is_different:
|
||||
update.append(fpath)
|
||||
ret_update.append(fpath)
|
||||
|
||||
ret_update += update
|
||||
ret_delete += delete
|
||||
ret_update += update_files
|
||||
ret_delete += delete_files
|
||||
|
||||
return ret_delete, ret_update
|
||||
|
||||
@@ -179,7 +179,7 @@ if __name__ == "__main__":
|
||||
parser = ArgParseImpl()
|
||||
parser.add_argument('--branch', help='Branch of the golden repo to write to')
|
||||
parser.add_argument('--golden-repo-token', help='Access token for the golden repo')
|
||||
parser.add_argument('--push-to-remote', action="store_true", help='Access token for the golden repo')
|
||||
parser.add_argument('--push-to-remote', action="store_true", help='Push the golden repo changes to remote')
|
||||
parser.add_argument('--diffimg', help='Path to the diffimg tool',
|
||||
default='./out/cmake-release/tools/diffimg/diffimg')
|
||||
|
||||
|
||||
@@ -18,6 +18,7 @@ import shutil
|
||||
import argparse
|
||||
import sys
|
||||
import pathlib
|
||||
import shlex
|
||||
|
||||
def execute(cmd,
|
||||
cwd=None,
|
||||
@@ -25,7 +26,7 @@ def execute(cmd,
|
||||
stdin=None,
|
||||
env=None,
|
||||
raise_errors=False):
|
||||
in_env = os.environ
|
||||
in_env = os.environ.copy()
|
||||
in_env.update(env if env else {})
|
||||
home = os.environ['HOME']
|
||||
if f'{home}/bin' not in in_env['PATH']:
|
||||
@@ -45,11 +46,11 @@ def execute(cmd,
|
||||
'universal_newlines': True
|
||||
}
|
||||
if capture_output:
|
||||
process = subprocess.Popen(cmd.split(' '), **kwargs)
|
||||
process = subprocess.Popen(shlex.split(cmd), **kwargs)
|
||||
output, err_output = process.communicate()
|
||||
return_code = process.returncode
|
||||
else:
|
||||
return_code = subprocess.call(cmd.split(' '), **kwargs)
|
||||
return_code = subprocess.call(shlex.split(cmd), **kwargs)
|
||||
|
||||
if return_code:
|
||||
# Error
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "filament",
|
||||
"version": "1.69.5",
|
||||
"version": "1.69.4",
|
||||
"description": "Real-time physically based rendering engine",
|
||||
"main": "filament.js",
|
||||
"module": "filament.js",
|
||||
|
||||
Reference in New Issue
Block a user