Compare commits

..
Author SHA1 Message Date
Benjamin Doherty b236bd4f50 Fix cancelled vertices on iOS OpenGL ES simulator 2022-11-15 20:24:21 -08:00
Benjamin Doherty b7b7afb62a Bump version to 1.29.0 2022-11-09 16:43:08 -08:00
Benjamin Doherty 957380b258 Release Filament 1.28.3 (ignore prev commit) 2022-11-09 16:41:13 -08:00
Benjamin Doherty fe3f16924d Release Filament 1.29.0 2022-11-09 16:37:39 -08:00
Ben Doherty ffc3128377 Skip rendering renderables with missing geometry (#6281) 2022-11-09 16:35:02 -08:00
Mathias Agopian c05e2ec47d improve variant filter
- variant filter didn't filter the VSM variant of depth variants

Fix #6274
2022-11-08 16:48:54 -08:00
Mathias Agopian 9768f49714 unify point and spotlight shadows
point light shadows are not sampled just like spotlight shadows, the
only difference is that we're calculating the face first to access the
corresponding shadowmap data (including the light transform).
2022-11-08 16:35:54 -08:00
Mathias Agopian 6c54cfe88a Shadowmaping code improvements
- the layer is now stored in the shadow ubo
- we now have one shadow ubo per face
- ShadowMap holds UBO index even for cascades
- move some shadow "view" uniforms into the shadow UBO
2022-11-08 15:25:33 -08:00
Mathias Agopian 2dc8d6bcc0 minor cleanups, comment fixes
- update comments
- remove unused or unneeded methods
2022-11-08 15:25:33 -08:00
Balazs Vegh 08dc398452 Fix skipped depth bias set on Metal (#6276) 2022-11-07 11:26:53 -08:00
Benjamin Doherty cd180c9a44 Update Python requirements.txt to fix CI 2022-11-04 11:35:10 -07:00
daemyung jang 0a7fe52124 Trim a whitespace (#6272) 2022-11-04 08:36:36 -07:00
Mathias Agopian b71faf47cc fix shadow cascades
breakage was due to an uninitialized variable at the beginning of the
frame, caused by a recent change.

shadows would disappear when cascades were used with 'stable' shadows.
2022-11-03 13:48:09 -07:00
Ben Doherty 67babceb73 Fix backend_test to work with Metal arg buffers (#6135) 2022-11-03 11:04:29 -07:00
Ben Doherty 8e13fc86d5 Expose missing ColorGrading::Builder methods to JS (#6262) 2022-11-03 11:04:06 -07:00
Mathias Agopian 351c8e0972 fix spotlight attenuation
Spotlight attenuation was computed only when shadowing was enabled.
2022-11-03 10:34:39 -07:00
50 changed files with 420 additions and 445 deletions
+2 -2
View File
@@ -31,7 +31,7 @@ repositories {
}
dependencies {
implementation 'com.google.android.filament:filament-android:1.28.2'
implementation 'com.google.android.filament:filament-android:1.29.0'
}
```
@@ -51,7 +51,7 @@ Here are all the libraries available in the group `com.google.android.filament`:
iOS projects can use CocoaPods to install the latest release:
```
pod 'Filament', '~> 1.28.2'
pod 'Filament', '~> 1.29.0'
```
### Snapshots
+4
View File
@@ -5,9 +5,13 @@ A new header is inserted each time a *tag* is created.
## main branch
## v1.29.0
- gltfio: calculate primitive's AABB correctly.
- gltfio: recompute bounding boxes with morph targets
- engine: add missing getters on `MaterialInstance`
- WebGL: add missing `ColorGrading` JS bindings
- engine: improvements/cleanup of Shadow mapping code [⚠️ **Recompile Materials**]
## v1.28.3
+1 -1
View File
@@ -1,5 +1,5 @@
GROUP=com.google.android.filament
VERSION_NAME=1.28.2
VERSION_NAME=1.29.0
POM_DESCRIPTION=Real-time physically based rendering engine for Android.
+10 -10
View File
@@ -1,14 +1,14 @@
setuptools==40.6.2
setuptools==58.0.4
wheel==0.37.1
certifi==2021.10.8
cffi==1.15.0
charset-normalizer==2.0.12
certifi==2022.9.24
cffi==1.15.1
charset-normalizer==2.1.1
Deprecated==1.2.13
idna==3.3
idna==3.4
pycparser==2.21
PyGithub==1.55
PyJWT==2.4.0
PyGithub==1.56
PyJWT==2.6.0
PyNaCl==1.5.0
requests==2.27.1
urllib3==1.26.9
wrapt==1.14.0
requests==2.28.1
urllib3==1.26.12
wrapt==1.14.1
+1
View File
@@ -405,6 +405,7 @@ if (APPLE)
backend
getopt
gtest
filamat
SPIRV
spirv-cross-glsl)
@@ -96,6 +96,8 @@ struct MetalContext {
SamplerStateCache samplerStateCache;
ArgumentEncoderCache argumentEncoderCache;
PolygonOffset currentPolygonOffset = {0.0f, 0.0f};
MetalSamplerGroup* samplerBindings[Program::SAMPLER_BINDING_COUNT] = {};
// Keeps track of sampler groups we've finalized for the current render pass.
+8 -1
View File
@@ -1005,6 +1005,7 @@ void MetalDriver::beginRenderPass(Handle<HwRenderTarget> rth,
mContext->depthStencilState.invalidate();
mContext->cullModeState.invalidate();
mContext->windingState.invalidate();
mContext->currentPolygonOffset = {0.0f, 0.0f};
mContext->finalizedSamplerGroups.clear();
}
@@ -1183,8 +1184,12 @@ void MetalDriver::startCapture(int) {
if (@available(iOS 13, *)) {
MTLCaptureDescriptor* descriptor = [MTLCaptureDescriptor new];
descriptor.captureObject = mContext->device;
#if defined(IOS)
descriptor.destination = MTLCaptureDestinationDeveloperTools;
#else
descriptor.destination = MTLCaptureDestinationGPUTraceDocument;
descriptor.outputURL = [[NSURL alloc] initFileURLWithPath:@"filament.gputrace"];
#endif
NSError* error = nil;
[[MTLCaptureManager sharedCaptureManager] startCaptureWithDescriptor:descriptor
error:&error];
@@ -1586,10 +1591,12 @@ void MetalDriver::draw(PipelineState ps, Handle<HwRenderPrimitive> rph, uint32_t
[mContext->currentRenderPassEncoder setDepthStencilState:state];
}
if (ps.polygonOffset.constant != 0.0 || ps.polygonOffset.slope != 0.0) {
if (ps.polygonOffset.constant != mContext->currentPolygonOffset.constant ||
ps.polygonOffset.slope != mContext->currentPolygonOffset.slope) {
[mContext->currentRenderPassEncoder setDepthBias:ps.polygonOffset.constant
slopeScale:ps.polygonOffset.slope
clamp:0.0];
mContext->currentPolygonOffset = ps.polygonOffset;
}
// Set scissor-rectangle.
+13 -37
View File
@@ -22,8 +22,12 @@
#include <spirv_glsl.hpp>
#include <spirv_msl.hpp>
#include "../src/GLSLPostProcessor.h"
#include "builtinResource.h"
#include <utils/FixedCapacityVector.h>
#include <iostream>
#include <utility>
#include <vector>
@@ -59,38 +63,6 @@ void SpvToGlsl(const SpirvBlob* spirv, std::string* outGlsl) {
*outGlsl = glslCompiler.compile();
}
void SpvToMsl(const SpirvBlob* spirv, std::string* outMsl) {
CompilerMSL mslCompiler(*spirv);
mslCompiler.set_msl_options(CompilerMSL::Options {
.msl_version = CompilerMSL::Options::make_msl_version(1, 1)
});
auto executionModel = mslCompiler.get_execution_model();
auto duplicateResourceBinding = [executionModel, &mslCompiler](const auto& resource) {
auto set = mslCompiler.get_decoration(resource.id, spv::DecorationDescriptorSet);
auto binding = mslCompiler.get_decoration(resource.id, spv::DecorationBinding);
MSLResourceBinding newBinding;
newBinding.stage = executionModel;
newBinding.desc_set = set;
newBinding.binding = binding;
newBinding.msl_texture = binding;
newBinding.msl_sampler = binding;
newBinding.msl_buffer = binding;
mslCompiler.add_msl_resource_binding(newBinding);
};
auto resources = mslCompiler.get_shader_resources();
for (const auto& resource : resources.sampled_images) {
duplicateResourceBinding(resource);
}
for (const auto& resource : resources.uniform_buffers) {
duplicateResourceBinding(resource);
}
*outMsl = mslCompiler.compile();
}
} // anonymous namespace
void ShaderGenerator::init() {
@@ -102,15 +74,16 @@ void ShaderGenerator::shutdown() {
}
ShaderGenerator::ShaderGenerator(std::string vertex, std::string fragment,
Backend backend, bool isMobile) noexcept
Backend backend, bool isMobile, const filament::SamplerInterfaceBlock* sib) noexcept
: mBackend(backend),
mVertexBlob(transpileShader(ShaderStage::VERTEX, std::move(vertex), backend, isMobile)),
mVertexBlob(transpileShader(ShaderStage::VERTEX, std::move(vertex), backend, isMobile, sib)),
mFragmentBlob(transpileShader(ShaderStage::FRAGMENT, std::move(fragment), backend,
isMobile)) {
isMobile, sib)) {
}
ShaderGenerator::Blob ShaderGenerator::transpileShader(
ShaderStage stage, std::string shader, Backend backend, bool isMobile) noexcept {
ShaderStage stage, std::string shader, Backend backend, bool isMobile,
const filament::SamplerInterfaceBlock* sib) noexcept {
TProgram program;
const EShLanguage language = stage == ShaderStage::VERTEX ? EShLangVertex : EShLangFragment;
TShader tShader(language);
@@ -173,7 +146,10 @@ ShaderGenerator::Blob ShaderGenerator::transpileShader(
}
return { result.c_str(), result.c_str() + result.length() + 1 };
} else if (backend == Backend::METAL) {
SpvToMsl(&spirv, &result);
const auto sm = isMobile ? ShaderModel::MOBILE : ShaderModel::DESKTOP;
filamat::SibVector sibs = filamat::SibVector::with_capacity(1);
if (sib) { sibs.emplace_back(0, sib); }
filamat::GLSLPostProcessor::spirvToMsl(&spirv, &result, sm, false, sibs, nullptr);
return { result.c_str(), result.c_str() + result.length() + 1 };
} else if (backend == Backend::VULKAN) {
return { (uint8_t*)spirv.data(), (uint8_t*)(spirv.data() + spirv.size()) };
+4 -2
View File
@@ -19,6 +19,7 @@
#include "PlatformRunner.h"
#include "private/filament/SamplerInterfaceBlock.h"
#include "private/backend/DriverApi.h"
#include "backend/Program.h"
@@ -38,7 +39,8 @@ public:
* @param vertex The vertex shader, written in GLSL 450 core.
* @param fragment The fragment shader, written in GLSL 450 core.
*/
ShaderGenerator(std::string vertex, std::string fragment, Backend backend, bool isMobile) noexcept;
ShaderGenerator(std::string vertex, std::string fragment, Backend backend, bool isMobile,
const filament::SamplerInterfaceBlock* sib = nullptr) noexcept;
ShaderGenerator(const ShaderGenerator& rhs) = delete;
ShaderGenerator& operator=(const ShaderGenerator& rhs) = delete;
@@ -50,7 +52,7 @@ private:
using Blob = std::vector<char>;
static Blob transpileShader(ShaderStage stage, std::string shader, Backend backend,
bool isMobile) noexcept;
bool isMobile, const filament::SamplerInterfaceBlock* sib = nullptr) noexcept;
Backend mBackend;
+6 -1
View File
@@ -133,7 +133,12 @@ TEST_F(BackendTest, FeedbackLoops) {
// Create a program.
ProgramHandle program;
{
ShaderGenerator shaderGen(fullscreenVs, fullscreenFs, sBackend, sIsMobilePlatform);
SamplerInterfaceBlock sib = filament::SamplerInterfaceBlock::Builder()
.name("backend_test_sib")
.stageFlags(backend::ShaderStageFlags::ALL_SHADER_STAGE_FLAGS)
.add( {{"tex", SamplerType::SAMPLER_2D, SamplerFormat::FLOAT, Precision::HIGH }} )
.build();
ShaderGenerator shaderGen(fullscreenVs, fullscreenFs, sBackend, sIsMobilePlatform, &sib);
Program prog = shaderGen.getProgram(api);
Program::Sampler psamplers[] = { utils::CString("tex"), 0 };
prog.setSamplerGroup(0, ShaderStageFlags::ALL_SHADER_STAGE_FLAGS, psamplers, sizeof(psamplers) / sizeof(psamplers[0]));
+31 -12
View File
@@ -19,6 +19,7 @@
#include "ShaderGenerator.h"
#include "TrianglePrimitive.h"
#include "private/filament/SamplerInterfaceBlock.h"
#include "private/backend/SamplerGroup.h"
#include <math/half.h>
@@ -54,7 +55,7 @@ layout(location = 0) out vec4 fragColor;
// Filament's Vulkan backend requires a descriptor set index of 1 for all samplers.
// This parameter is ignored for other backends.
layout(location = 0, set = 1) uniform {samplerType} tex;
layout(location = 0, set = 1) uniform {samplerType} test_tex;
void main() {
vec2 fbsize = vec2(512);
@@ -62,7 +63,7 @@ void main() {
#if defined(TARGET_METAL_ENVIRONMENT) || defined(TARGET_VULKAN_ENVIRONMENT)
uv.y = 1.0 - uv.y;
#endif
fragColor = vec4(texture(tex, uv).rgb, 1.0f);
fragColor = vec4(texture(test_tex, uv).rgb, 1.0f);
}
)");
@@ -72,8 +73,7 @@ std::string fragmentUpdateImage3DTemplate (R"(#version 450 core
layout(location = 0) out vec4 fragColor;
// Filament's Vulkan backend requires a descriptor set index of 1 for all samplers.
// This parameter is ignored for other backends.
layout(location = 0, set = 1) uniform {samplerType} tex;
layout(location = 0, set = 1) uniform {samplerType} test_tex;
float getLayer(in sampler3D s) { return 2.5f / 4.0f; }
float getLayer(in sampler2DArray s) { return 2.0f; }
@@ -84,7 +84,7 @@ void main() {
#if defined(TARGET_METAL_ENVIRONMENT) || defined(TARGET_VULKAN_ENVIRONMENT)
uv.y = 1.0 - uv.y;
#endif
fragColor = vec4(texture(tex, vec3(uv, getLayer(tex))).rgb, 1.0f);
fragColor = vec4(texture(test_tex, vec3(uv, getLayer(test_tex))).rgb, 1.0f);
}
)");
@@ -94,8 +94,7 @@ std::string fragmentUpdateImageMip (R"(#version 450 core
layout(location = 0) out vec4 fragColor;
// Filament's Vulkan backend requires a descriptor set index of 1 for all samplers.
// This parameter is ignored for other backends.
layout(location = 0, set = 1) uniform sampler2D tex;
layout(location = 0, set = 1) uniform sampler2D test_tex;
void main() {
vec2 fbsize = vec2(512);
@@ -103,7 +102,7 @@ void main() {
#if defined(TARGET_METAL_ENVIRONMENT) || defined(TARGET_VULKAN_ENVIRONMENT)
uv.y = 1.0 - uv.y;
#endif
fragColor = vec4(textureLod(tex, uv, 1.0f).rgb, 1.0f);
fragColor = vec4(textureLod(test_tex, uv, 1.0f).rgb, 1.0f);
}
)");
@@ -345,10 +344,15 @@ TEST_F(BackendTest, UpdateImage2D) {
auto defaultRenderTarget = api.createDefaultRenderTarget(0);
// Create a program.
SamplerInterfaceBlock sib = filament::SamplerInterfaceBlock::Builder()
.name("Test")
.stageFlags(backend::ShaderStageFlags::ALL_SHADER_STAGE_FLAGS)
.add( {{"tex", SamplerType::SAMPLER_2D, SamplerFormat::FLOAT, Precision::HIGH }} )
.build();
ProgramHandle program;
std::string fragment = stringReplace("{samplerType}",
getSamplerTypeName(t.textureFormat), fragmentTemplate);
ShaderGenerator shaderGen(vertex, fragment, sBackend, sIsMobilePlatform);
ShaderGenerator shaderGen(vertex, fragment, sBackend, sIsMobilePlatform, &sib);
Program prog = shaderGen.getProgram(api);
Program::Sampler psamplers[] = { utils::CString("tex"), 0 };
prog.setSamplerGroup(0, ShaderStageFlags::ALL_SHADER_STAGE_FLAGS, psamplers, sizeof(psamplers) / sizeof(psamplers[0]));
@@ -425,9 +429,14 @@ TEST_F(BackendTest, UpdateImageSRGB) {
auto defaultRenderTarget = api.createDefaultRenderTarget(0);
// Create a program.
SamplerInterfaceBlock sib = filament::SamplerInterfaceBlock::Builder()
.name("Test")
.stageFlags(backend::ShaderStageFlags::ALL_SHADER_STAGE_FLAGS)
.add( {{"tex", SamplerType::SAMPLER_2D, SamplerFormat::FLOAT, Precision::HIGH }} )
.build();
std::string fragment = stringReplace("{samplerType}",
getSamplerTypeName(textureFormat), fragmentTemplate);
ShaderGenerator shaderGen(vertex, fragment, sBackend, sIsMobilePlatform);
ShaderGenerator shaderGen(vertex, fragment, sBackend, sIsMobilePlatform, &sib);
Program prog = shaderGen.getProgram(api);
Program::Sampler psamplers[] = { utils::CString("tex"), 0 };
prog.setSamplerGroup(0, ShaderStageFlags::ALL_SHADER_STAGE_FLAGS, psamplers, sizeof(psamplers) / sizeof(psamplers[0]));
@@ -511,9 +520,14 @@ TEST_F(BackendTest, UpdateImageMipLevel) {
auto defaultRenderTarget = api.createDefaultRenderTarget(0);
// Create a program.
SamplerInterfaceBlock sib = filament::SamplerInterfaceBlock::Builder()
.name("Test")
.stageFlags(backend::ShaderStageFlags::ALL_SHADER_STAGE_FLAGS)
.add( {{"tex", SamplerType::SAMPLER_3D, SamplerFormat::FLOAT, Precision::HIGH }} )
.build();
std::string fragment = stringReplace("{samplerType}",
getSamplerTypeName(textureFormat), fragmentUpdateImageMip);
ShaderGenerator shaderGen(vertex, fragment, sBackend, sIsMobilePlatform);
ShaderGenerator shaderGen(vertex, fragment, sBackend, sIsMobilePlatform, &sib);
Program prog = shaderGen.getProgram(api);
Program::Sampler psamplers[] = { utils::CString("tex"), 0 };
prog.setSamplerGroup(0, ShaderStageFlags::ALL_SHADER_STAGE_FLAGS, psamplers, sizeof(psamplers) / sizeof(psamplers[0]));
@@ -583,9 +597,14 @@ TEST_F(BackendTest, UpdateImage3D) {
auto defaultRenderTarget = api.createDefaultRenderTarget(0);
// Create a program.
SamplerInterfaceBlock sib = filament::SamplerInterfaceBlock::Builder()
.name("Test")
.stageFlags(backend::ShaderStageFlags::ALL_SHADER_STAGE_FLAGS)
.add( {{"tex", SamplerType::SAMPLER_3D, SamplerFormat::FLOAT, Precision::HIGH }} )
.build();
std::string fragment = stringReplace("{samplerType}",
getSamplerTypeName(samplerType), fragmentUpdateImage3DTemplate);
ShaderGenerator shaderGen(vertex, fragment, sBackend, sIsMobilePlatform);
ShaderGenerator shaderGen(vertex, fragment, sBackend, sIsMobilePlatform, &sib);
Program prog = shaderGen.getProgram(api);
Program::Sampler psamplers[] = { utils::CString("tex"), 0 };
prog.setSamplerGroup(0, ShaderStageFlags::ALL_SHADER_STAGE_FLAGS, psamplers, sizeof(psamplers) / sizeof(psamplers[0]));
@@ -45,7 +45,7 @@ std::string fragment (R"(#version 450 core
layout(location = 0) out vec4 fragColor;
layout(location = 0) in vec2 uv;
layout(set = 1, binding = 6) uniform sampler2D tex;
layout(location = 0, set = 1) uniform sampler2D tex;
void main() {
fragColor = texture(tex, uv);
@@ -65,11 +65,16 @@ TEST_F(BackendTest, RenderExternalImageWithoutSet) {
auto swapChain = createSwapChain();
ShaderGenerator shaderGen(vertex, fragment, sBackend, sIsMobilePlatform);
SamplerInterfaceBlock sib = filament::SamplerInterfaceBlock::Builder()
.name("backend_test_sib")
.stageFlags(backend::ShaderStageFlags::ALL_SHADER_STAGE_FLAGS)
.add( {{"tex", SamplerType::SAMPLER_EXTERNAL, SamplerFormat::FLOAT, Precision::HIGH }} )
.build();
ShaderGenerator shaderGen(vertex, fragment, sBackend, sIsMobilePlatform, &sib);
// Create a program that samples a texture.
Program p = shaderGen.getProgram(getDriverApi());
Program::Sampler sampler { utils::CString("tex"), 6 };
Program::Sampler sampler { utils::CString("tex"), 0 };
p.setSamplerGroup(0, ShaderStageFlags::ALL_SHADER_STAGE_FLAGS, &sampler, 1);
backend::Handle<HwProgram> program = getDriverApi().createProgram(std::move(p));
@@ -139,11 +144,16 @@ TEST_F(BackendTest, RenderExternalImage) {
auto swapChain = createSwapChain();
ShaderGenerator shaderGen(vertex, fragment, sBackend, sIsMobilePlatform);
SamplerInterfaceBlock sib = filament::SamplerInterfaceBlock::Builder()
.name("backend_test_sib")
.stageFlags(backend::ShaderStageFlags::ALL_SHADER_STAGE_FLAGS)
.add( {{"tex", SamplerType::SAMPLER_EXTERNAL, SamplerFormat::FLOAT, Precision::HIGH }} )
.build();
ShaderGenerator shaderGen(vertex, fragment, sBackend, sIsMobilePlatform, &sib);
// Create a program that samples a texture.
Program p = shaderGen.getProgram(getDriverApi());
Program::Sampler sampler { utils::CString("tex"), 6 };
Program::Sampler sampler { utils::CString("tex"), 0 };
p.setSamplerGroup(0, ShaderStageFlags::ALL_SHADER_STAGE_FLAGS, &sampler, 1);
auto program = getDriverApi().createProgram(std::move(p));
-39
View File
@@ -98,45 +98,6 @@ void PerShadowMapUniforms::prepareTime(Transaction const& transaction,
s.userTime = userTime;
}
void PerShadowMapUniforms::prepareDirectionalLight(Transaction const& transaction,
FEngine& engine,
float exposure,
float3 const& sceneSpaceDirection,
PerShadowMapUniforms::LightManagerInstance directionalLight) noexcept {
FLightManager& lcm = engine.getLightManager();
auto& s = edit(transaction);
const float3 l = -sceneSpaceDirection; // guaranteed normalized
if (directionalLight.isValid()) {
const float4 colorIntensity = {
lcm.getColor(directionalLight), lcm.getIntensity(directionalLight) * exposure };
s.lightDirection = l;
s.lightColorIntensity = colorIntensity;
s.lightChannels = lcm.getLightChannels(directionalLight);
const bool isSun = lcm.isSunLight(directionalLight);
// The last parameter must be < 0.0f for regular directional lights
float4 sun{ 0.0f, 0.0f, 0.0f, -1.0f };
if (UTILS_UNLIKELY(isSun && colorIntensity.w > 0.0f)) {
// currently we have only a single directional light, so it's probably likely that it's
// also the Sun. However, conceptually, most directional lights won't be sun lights.
float radius = lcm.getSunAngularRadius(directionalLight);
float haloSize = lcm.getSunHaloSize(directionalLight);
float haloFalloff = lcm.getSunHaloFalloff(directionalLight);
sun.x = std::cos(radius);
sun.y = std::sin(radius);
sun.z = 1.0f / (std::cos(radius * haloSize) - sun.x);
sun.w = haloFalloff;
}
s.sun = sun;
} else {
// Disable the sun if there's no directional light
s.sun = float4{ 0.0f, 0.0f, 0.0f, -1.0f };
}
}
void PerShadowMapUniforms::prepareShadowMapping(Transaction const& transaction,
bool highPrecision) noexcept {
auto& s = edit(transaction);
-4
View File
@@ -73,10 +73,6 @@ public:
static void prepareShadowMapping(Transaction const& transaction,
bool highPrecision) noexcept;
static void prepareDirectionalLight(Transaction const& transaction,
FEngine& engine, float exposure,
math::float3 const& sceneSpaceDirection, LightManagerInstance instance) noexcept;
static Transaction open(backend::DriverApi& driver) noexcept;
// update local data into GPU UBO
-4
View File
@@ -290,14 +290,10 @@ void PerViewUniforms::prepareShadowMapping(bool highPrecision) noexcept {
void PerViewUniforms::prepareShadowSampling(PerViewUib& uniforms,
ShadowMappingUniforms const& shadowMappingUniforms) noexcept {
uniforms.lightFromWorldMatrix = shadowMappingUniforms.lightFromWorldMatrix;
uniforms.cascadeSplits = shadowMappingUniforms.cascadeSplits;
uniforms.shadowBulbRadiusLs = shadowMappingUniforms.shadowBulbRadiusLs;
uniforms.shadowBias = shadowMappingUniforms.shadowBias;
uniforms.ssContactShadowDistance = shadowMappingUniforms.ssContactShadowDistance;
uniforms.directionalShadows = shadowMappingUniforms.directionalShadows;
uniforms.cascades = shadowMappingUniforms.cascades;
uniforms.cascades |= uint32_t(shadowMappingUniforms.elvsm) << 31u;
}
void PerViewUniforms::prepareShadowVSM(Handle<HwTexture> texture,
+5
View File
@@ -750,6 +750,11 @@ void RenderPass::Executor::execute(backend::DriverApi& driver,
continue;
}
// primitiveHandle may be invalid if no geometry was set on the renderable.
if (UTILS_UNLIKELY(!first->primitive.primitiveHandle)) {
continue;
}
// per-renderable uniform
const PrimitiveInfo info = first->primitive;
pipeline.rasterState = info.rasterState;
+44 -90
View File
@@ -413,41 +413,10 @@ ShadowMap::ShaderParameters ShadowMap::updateDirectional(FEngine& engine,
return shaderParameters;
}
ShadowMap::ShaderParameters ShadowMap::updateSpot(FEngine& engine,
const FScene::LightSoa& lightData, size_t index,
filament::CameraInfo const& camera,
const ShadowMapInfo& shadowMapInfo,
FScene const& scene, SceneInfo sceneInfo) noexcept {
ShaderParameters shaderParameters;
auto& lcm = engine.getLightManager();
auto li = lightData.elementAt<FScene::LIGHT_INSTANCE>(index);
auto position = lightData.elementAt<FScene::POSITION_RADIUS>(index).xyz;
auto direction = lightData.elementAt<FScene::DIRECTION>(index);
auto radius = lightData.elementAt<FScene::POSITION_RADIUS>(index).w;
auto outerConeAngle = lcm.getSpotLightOuterCone(li);
const FLightManager::ShadowParams& params = lcm.getShadowParams(li);
/*
* Compute the light model matrix.
*/
// Choose a reasonable value for the near plane.
const mat4f Mv = getDirectionalLightViewMatrix(direction, position);
// find decent near/far
ShadowMap::updateSceneInfoSpot(Mv, scene, sceneInfo);
// if the scene was empty, near > far
mHasVisibleShadows = -sceneInfo.lsNearFar[0] < -sceneInfo.lsNearFar[1];
// FIXME: we need a configuration for minimum near plane (for now hardcoded to 1cm)
float nearPlane = std::max(0.01f, -sceneInfo.lsNearFar[0]);
float farPlane = std::min(radius, -sceneInfo.lsNearFar[1]);
float outerConeAngleDegrees = outerConeAngle * f::RAD_TO_DEG;
const mat4f Mp = mat4f::perspective(outerConeAngleDegrees * 2.0f, 1.0f, nearPlane, farPlane);
ShadowMap::ShaderParameters ShadowMap::updateSpotOrPoint(
mat4f const& Mv, float outerConeAngle, float nearPlane, float farPlane,
const ShadowMapInfo& shadowMapInfo, const FLightManager::ShadowParams& params) noexcept {
const mat4f Mp = mat4f::perspective(outerConeAngle * f::RAD_TO_DEG * 2.0f, 1.0f, nearPlane, farPlane);
const mat4f MpMv(math::highPrecisionMultiply(Mp, Mv));
// Final shadow transform
@@ -469,7 +438,9 @@ ShadowMap::ShaderParameters ShadowMap::updateSpot(FEngine& engine,
// = zInLightSpace * texelSizeAtOneMeter
// = zInLightSpace * (2*tan(halfConeAngle)/dimension)
// Note: this would not work with LISPSM, which warps the texture space.
shaderParameters.texelSizeAtOneMeterWs = (2.0f * std::tan(outerConeAngle) / float(shadowMapInfo.shadowDimension));
ShaderParameters shaderParameters;
shaderParameters.texelSizeAtOneMeterWs =
(2.0f * std::tan(outerConeAngle) / float(shadowMapInfo.shadowDimension));
shaderParameters.lightFromWorldZ = -transpose(Mv)[2]; // negate because camera looks in -Z
if (!shadowMapInfo.vsm) {
@@ -478,9 +449,9 @@ ShadowMap::ShaderParameters ShadowMap::updateSpot(FEngine& engine,
shaderParameters.lightSpace = computeVsmLightSpaceMatrix(St, Mv, nearPlane, farPlane);
}
const float3 direction = -transpose(Mv)[2].xyz;
const float constantBias = shadowMapInfo.vsm ? 0.0f : params.options.constantBias;
const mat4f b = mat4f::translation(direction * constantBias);
const mat4f Sb = S * b;
// It's important to set the light camera's model matrix separately from its projection, so that
// the cameraPosition uniform gets set correctly.
@@ -493,19 +464,44 @@ ShadowMap::ShaderParameters ShadowMap::updateSpot(FEngine& engine,
mCamera->setModelMatrix(mat4{ FCamera::rigidTransformInverse(Mv * b) });
mCamera->setCustomProjection(mat4(Mp), nearPlane, farPlane);
// for the debug camera, we need to undo the world origin
mDebugCamera->setCustomProjection(mat4(Sb * camera.worldOrigin), nearPlane, radius);
return shaderParameters;
}
ShadowMap::ShaderParameters ShadowMap::updateSpot(FEngine& engine,
const FScene::LightSoa& lightData, size_t index,
filament::CameraInfo const& camera,
const ShadowMapInfo& shadowMapInfo,
FScene const& scene, SceneInfo sceneInfo) noexcept {
auto& lcm = engine.getLightManager();
auto position = lightData.elementAt<FScene::POSITION_RADIUS>(index).xyz;
auto direction = lightData.elementAt<FScene::DIRECTION>(index);
auto radius = lightData.elementAt<FScene::POSITION_RADIUS>(index).w;
auto li = lightData.elementAt<FScene::LIGHT_INSTANCE>(index);
const FLightManager::ShadowParams& params = lcm.getShadowParams(li);
const mat4f Mv = getDirectionalLightViewMatrix(direction, position);
// find decent near/far
ShadowMap::updateSceneInfoSpot(Mv, scene, sceneInfo);
// if the scene was empty, near > far
mHasVisibleShadows = -sceneInfo.lsNearFar[0] < -sceneInfo.lsNearFar[1];
if (!mHasVisibleShadows) {
return {};
}
// FIXME: we need a configuration for minimum near plane (for now hardcoded to 1cm)
float nearPlane = std::max(0.01f, -sceneInfo.lsNearFar[0]);
float farPlane = std::min(radius, -sceneInfo.lsNearFar[1]);
auto outerConeAngle = lcm.getSpotLightOuterCone(li);
return updateSpotOrPoint(Mv, outerConeAngle, nearPlane, farPlane, shadowMapInfo, params);
}
ShadowMap::ShaderParameters ShadowMap::updatePoint(FEngine& engine,
const FScene::LightSoa& lightData, size_t index,
filament::CameraInfo const& camera, const ShadowMapInfo& shadowMapInfo, FScene const& scene,
SceneInfo, uint8_t face) noexcept {
ShaderParameters shaderParameters;
// check if this shadow map has anything to render
mHasVisibleShadows = false;
FScene::RenderableSoa const& UTILS_RESTRICT soa = scene.getRenderableData();
@@ -518,53 +514,16 @@ ShadowMap::ShaderParameters ShadowMap::updatePoint(FEngine& engine,
}
}
if (!mHasVisibleShadows) {
return shaderParameters;
return {};
}
auto& lcm = engine.getLightManager();
auto li = lightData.elementAt<FScene::LIGHT_INSTANCE>(index);
auto position = lightData.elementAt<FScene::POSITION_RADIUS>(index).xyz;
auto radius = lightData.elementAt<FScene::POSITION_RADIUS>(index).w;
auto li = lightData.elementAt<FScene::LIGHT_INSTANCE>(index);
const FLightManager::ShadowParams& params = lcm.getShadowParams(li);
/*
* Compute the light model matrix.
*/
const mat4f Mv = getPointLightViewMatrix(TextureCubemapFace(face), position);
const float3 direction = -transpose(Mv)[2].xyz;
// TODO: don't hardcode near plane
// Choose a reasonable value for the near plane.
float nearPlane = 0.01f;
float farPlane = radius;
const mat4f Mp = mat4f::perspective(90.0f, 1.0f, nearPlane, farPlane);
// For calculating the point light normal bias, we need the texel size in world space at the
// sample location. Using Thales's theorem, we find:
// texelSize(zInLightSpace) = zInLightSpace * texelSizeOnTheNearPlane / near
// = zInLightSpace * texelSizeAtOneMeter
// = zInLightSpace * (2*tan(halfConeAngle)/dimension)
// Note: this would not work with LISPSM, which warps the texture space.
shaderParameters.texelSizeAtOneMeterWs =
(2.0f * std::tan(f::PI_4) / float(shadowMapInfo.shadowDimension));
const float constantBias = shadowMapInfo.vsm ? 0.0f : params.options.constantBias;
const mat4f b = mat4f::translation(direction * constantBias);
// It's important to set the light camera's model matrix separately from its projection, so that
// the cameraPosition uniform gets set correctly.
// mLightSpace is used in the shader to access the shadow map texture, and has the model matrix
// baked in.
// The model matrix below is in fact inverted to get the view matrix and passed to the
// shader as 'viewFromWorldMatrix', and is used in the VSM case to compute the depth metric.
// (see depth_main.fs). Note that in the case of VSM, 'b' below is identity.
mCamera->setModelMatrix(mat4{ FCamera::rigidTransformInverse(Mv * b) });
mCamera->setCustomProjection(mat4(Mp), nearPlane, farPlane);
return shaderParameters;
return updateSpotOrPoint(Mv, 45.0f * f::DEG_TO_RAD, 0.01f, radius, shadowMapInfo, params);
}
mat4f ShadowMap::applyLISPSM(mat4f& Wp,
@@ -1184,6 +1143,8 @@ void ShadowMap::visitScene(const FScene& scene, uint32_t visibleLayers,
void ShadowMap::initSceneInfo(ShadowMap::SceneInfo& sceneInfo,
uint8_t visibleLayers, FScene const& scene, mat4f const& viewMatrix) {
sceneInfo.csNearFar = { -1.0f, 1.0f };
sceneInfo.lsNearFar = {};
sceneInfo.visibleLayers = visibleLayers;
sceneInfo.vsNearFar = { std::numeric_limits<float>::lowest(), std::numeric_limits<float>::max() };
@@ -1281,13 +1242,6 @@ void ShadowMap::prepareTime(Transaction const& transaction,
PerShadowMapUniforms::prepareTime(transaction, engine, userTime);
}
void ShadowMap::prepareDirectionalLight(Transaction const& transaction,
FEngine& engine, math::float3 const& sceneSpaceDirection,
LightManager::Instance instance) noexcept {
PerShadowMapUniforms::prepareDirectionalLight(transaction,
engine, 1.0f, sceneSpaceDirection, instance);
}
void ShadowMap::prepareShadowMapping(Transaction const& transaction,
bool highPrecision) noexcept {
PerShadowMapUniforms::prepareShadowMapping(transaction, highPrecision);
@@ -1302,7 +1256,7 @@ void ShadowMap::commit(Transaction& transaction,
mPerShadowMapUniforms.commit(transaction, driver);
}
void ShadowMap::bindPerViewUniformsAndSamplers(backend::DriverApi& driver) const noexcept {
void ShadowMap::bind(backend::DriverApi& driver) const noexcept {
mPerShadowMapUniforms.bind(driver);
}
+6 -4
View File
@@ -195,15 +195,12 @@ public:
const filament::Viewport& viewport) noexcept;
static void prepareTime(Transaction const& transaction,
FEngine& engine, math::float4 const& userTime) noexcept;
static void prepareDirectionalLight(Transaction const& transaction,
FEngine& engine, math::float3 const& sceneSpaceDirection,
LightManager::Instance instance) noexcept;
static void prepareShadowMapping(Transaction const& transaction,
bool highPrecision) noexcept;
static PerShadowMapUniforms::Transaction open(backend::DriverApi& driver) noexcept;
void commit(Transaction& transaction,
backend::DriverApi& driver) const noexcept;
void bindPerViewUniformsAndSamplers(backend::DriverApi& driver) const noexcept;
void bind(backend::DriverApi& driver) const noexcept;
private:
struct Segment {
@@ -217,6 +214,11 @@ private:
// 8 corners, 12 segments w/ 2 intersection max -- all of this twice (8 + 12 * 2) * 2 (768 bytes)
using FrustumBoxIntersection = std::array<math::float3, 64>;
ShaderParameters updateSpotOrPoint(
math::mat4f const& Mv, float outerConeAngle, float nearPlane, float farPlane,
const ShadowMapInfo& shadowMapInfo,
const FLightManager::ShadowParams& params) noexcept;
static math::mat4f applyLISPSM(math::mat4f& Wp,
filament::CameraInfo const& camera, FLightManager::ShadowParams const& params,
const math::mat4f& LMpMv,
+58 -62
View File
@@ -97,8 +97,10 @@ void ShadowMapManager::setDirectionalShadowMap(size_t lightIndex,
LightManager::ShadowOptions const* options) noexcept {
assert_invariant(options->shadowCascades <= CONFIG_MAX_SHADOW_CASCADES);
for (size_t c = 0; c < options->shadowCascades; c++) {
auto* pShadowMap = getCascadeShadowMap(c);
pShadowMap->initialize(lightIndex, ShadowType::DIRECTIONAL, c, 0, options);
const size_t i = c;
assert_invariant(i < CONFIG_MAX_SHADOW_CASCADES);
auto* pShadowMap = getCascadeShadowMap(i);
pShadowMap->initialize(lightIndex, ShadowType::DIRECTIONAL, i, 0, options);
mCascadeShadowMaps.push_back(pShadowMap);
}
}
@@ -107,17 +109,19 @@ void ShadowMapManager::addShadowMap(size_t lightIndex, bool spotlight,
LightManager::ShadowOptions const* options) noexcept {
if (spotlight) {
const size_t c = mSpotShadowMaps.size();
assert_invariant(c < CONFIG_MAX_SHADOWMAP_PUNCTUAL);
auto* pShadowMap = getPointOrSpotShadowMap(c);
pShadowMap->initialize(lightIndex, ShadowType::SPOT, c, 0, options);
const size_t i = c + CONFIG_MAX_SHADOW_CASCADES;
assert_invariant(i < CONFIG_MAX_SHADOWMAPS);
auto* pShadowMap = getPointOrSpotShadowMap(i);
pShadowMap->initialize(lightIndex, ShadowType::SPOT, i, 0, options);
mSpotShadowMaps.push_back(pShadowMap);
} else {
// point-light
// point-light, generate 6 independent shadowmaps
for (size_t face = 0; face < 6; face++) {
const size_t c = mSpotShadowMaps.size();
assert_invariant(c < CONFIG_MAX_SHADOWMAP_PUNCTUAL);
auto* pShadowMap = getPointOrSpotShadowMap(c);
pShadowMap->initialize(lightIndex, ShadowType::POINT, c, face, options);
const size_t i = c + CONFIG_MAX_SHADOW_CASCADES;
assert_invariant(i < CONFIG_MAX_SHADOWMAPS);
auto* pShadowMap = getPointOrSpotShadowMap(i);
pShadowMap->initialize(lightIndex, ShadowType::POINT, i, face, options);
mSpotShadowMaps.push_back(pShadowMap);
}
}
@@ -157,7 +161,7 @@ FrameGraphId<FrameGraphTexture> ShadowMapManager::render(FEngine& engine, FrameG
auto& prepareShadowPass = fg.addPass<PrepareShadowPassData>("Prepare Shadow Pass",
[&](FrameGraph::Builder& builder, auto& data) {
data.passList.reserve(CONFIG_MAX_SHADOW_LAYERS);
data.passList.reserve(CONFIG_MAX_SHADOWMAPS);
data.shadows = builder.createTexture("Shadowmap", {
.width = textureRequirements.size, .height = textureRequirements.size,
.depth = textureRequirements.layers,
@@ -229,15 +233,15 @@ FrameGraphId<FrameGraphTexture> ShadowMapManager::render(FEngine& engine, FrameG
}
if (shadowMap.hasVisibleShadows()) {
// TODO: this loop can generate a lot of commands. investigate if
// we could compact or reduce the number of commands.
// - we could move this into the execute of each pass, but
// then we would have to update the UBO each time.
// - we could also always use the same commands for all
// shadow maps, but then we'd generate a lot of
// unneeded draw call.
// Note: this loop can generate a lot of commands that come out of the
// "per frame command arena". The allocation persists until the
// end of the frame.
// One way to possibly mitigate this, would be to always use the
// same command buffer for all shadow map, but then we'd generate
// a lot of unneeded draw calls.
// To do this efficiently, we'd need a way to cull draw calls already
// recorded in the command buffer, per shadow map.
// generate and sort the commands for rendering the shadow map
// cameraInfo only valid after calling update
const CameraInfo cameraInfo{ shadowMap.getCamera() };
@@ -248,17 +252,13 @@ FrameGraphId<FrameGraphTexture> ShadowMapManager::render(FEngine& engine, FrameG
ShadowMap::prepareTime(transaction, engine, userTime);
ShadowMap::prepareShadowMapping(transaction,
view.getVsmShadowOptions().highPrecision);
if (shadowMap.getShadowType() == ShadowType::DIRECTIONAL) {
ShadowMap::prepareDirectionalLight(transaction, engine,
scene->getLightData().elementAt<FScene::DIRECTION>(0),
scene->getLightData().elementAt<FScene::LIGHT_INSTANCE>(0));
}
shadowMap.commit(transaction, driver);
// updatePrimitivesLod must be run before RenderPass::appendCommands.
view.updatePrimitivesLod(engine,
cameraInfo, scene->getRenderableData(), entry.range);
// generate and sort the commands for rendering the shadow map
RenderPass pass(passTemplate);
pass.setCamera(cameraInfo);
pass.setVisibilityMask(entry.visibilityMask);
@@ -270,14 +270,10 @@ FrameGraphId<FrameGraphTexture> ShadowMapManager::render(FEngine& engine, FrameG
entry.executor = pass.getExecutor();
if (!view.hasVSM()) {
auto li = scene->getLightData().elementAt<FScene::LIGHT_INSTANCE>(
entry.shadowMap->getLightIndex());
auto const& params = engine.getLightManager().getShadowParams(li);
auto const* options = shadowMap.getShadowOptions();
const PolygonOffset polygonOffset = { // handle reversed Z
.slope = -params.options.polygonOffsetSlope,
.constant = -params.options.polygonOffsetConstant
.slope = -options->polygonOffsetSlope,
.constant = -options->polygonOffsetConstant
};
entry.executor.overridePolygonOffset(&polygonOffset);
}
@@ -393,16 +389,15 @@ FrameGraphId<FrameGraphTexture> ShadowMapManager::render(FEngine& engine, FrameG
// `entry` lives in `PrepareShadowPassData` which is guaranteed to still
// be alive when we execute here (all passes stay alive until the FrameGraph
// is destroyed).
// It wouldn't work to capture by copy because entry.pass wouldn't be
// It wouldn't work to capture by copy because entry.executor wouldn't be
// initialized, as this happens in an `execute` block.
entry.shadowMap->bindPerViewUniformsAndSamplers(driver);
auto rt = resources.getRenderPassInfo(data.rt);
rt.params.viewport = entry.shadowMap->getViewport();
engine.flush();
driver.beginRenderPass(rt.target, rt.params);
entry.shadowMap->bind(driver);
entry.executor.execute(engine, "Shadow Pass");
driver.endRenderPass();
});
@@ -464,7 +459,7 @@ ShadowMapManager::ShadowTechnique ShadowMapManager::updateCascadeShadowMaps(FEng
// entire camera frustum, as if we only had a single cascade.
ShadowMap& shadowMap = *mCascadeShadowMaps[0];
auto shaderParameters = shadowMap.updateDirectional(mEngine,
shadowMap.updateDirectional(mEngine,
lightData, 0, cameraInfo, shadowMapInfo, *scene, sceneInfo);
hasVisibleShadows = shadowMap.hasVisibleShadows();
@@ -473,18 +468,6 @@ ShadowMapManager::ShadowTechnique ShadowMapManager::updateCascadeShadowMaps(FEng
Frustum const& frustum = shadowMap.getCamera().getCullingFrustum();
FView::cullRenderables(engine.getJobSystem(), renderableData, frustum,
VISIBLE_DIR_SHADOW_RENDERABLE_BIT);
// Set shadowBias, using the first directional cascade.
// when computing the required bias we need a half-texel size, so we multiply by 0.5 here.
// note: normalBias is set to zero for VSM
const float normalBias = shadowMapInfo.vsm ? 0.0f : 0.5f * lcm.getShadowNormalBias(0);
// 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;
mShadowMappingUniforms.shadowBias = normalBias * wsTexelSize;
mShadowMappingUniforms.shadowBulbRadiusLs =
mSoftShadowOptions.penumbraScale * options.shadowBulbRadius / wsTexelSize;
mShadowMappingUniforms.elvsm = options.vsm.elvsm;
}
}
@@ -537,6 +520,10 @@ ShadowMapManager::ShadowTechnique ShadowMapManager::updateCascadeShadowMaps(FEng
mShadowMappingUniforms.cascadeSplits = wsSplitPositionUniform;
// when computing the required bias we need a half-texel size, so we multiply by 0.5 here.
// note: normalBias is set to zero for VSM
const float normalBias = shadowMapInfo.vsm ? 0.0f : 0.5f * lcm.getShadowNormalBias(0);
for (size_t i = 0, c = mCascadeShadowMaps.size(); i < c; i++) {
assert_invariant(mCascadeShadowMaps[i]);
@@ -550,7 +537,22 @@ ShadowMapManager::ShadowTechnique ShadowMapManager::updateCascadeShadowMaps(FEng
lightData, 0, cameraInfo, shadowMapInfo, *scene, sceneInfo);
if (shadowMap.hasVisibleShadows()) {
mShadowMappingUniforms.lightFromWorldMatrix[i] = shaderParameters.lightSpace;
const size_t shadowIndex = shadowMap.getShadowIndex();
assert_invariant(shadowIndex == i);
// 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;
auto& s = mShadowUb.edit();
s.shadows[shadowIndex].layer = shadowMap.getLayer();
s.shadows[shadowIndex].lightFromWorldMatrix = shaderParameters.lightSpace;
s.shadows[shadowIndex].normalBias = normalBias * wsTexelSize;
s.shadows[shadowIndex].texelSizeAtOneMeter = wsTexelSize;
s.shadows[shadowIndex].elvsm = options.vsm.elvsm;
s.shadows[shadowIndex].bulbRadiusLs =
mSoftShadowOptions.penumbraScale * options.shadowBulbRadius / wsTexelSize;
shadowTechnique |= ShadowTechnique::SHADOW_MAP;
cascadeHasVisibleShadows |= 0x1u << i;
}
@@ -678,6 +680,7 @@ void ShadowMapManager::prepareSpotShadowMap(ShadowMap& shadowMap,
auto& s = mShadowUb.edit();
const double n = shadowMap.getCamera().getNear();
const double f = shadowMap.getCamera().getCullingFar();
s.shadows[shadowIndex].layer = shadowMap.getLayer();
s.shadows[shadowIndex].lightFromWorldMatrix = shaderParameters.lightSpace;
s.shadows[shadowIndex].direction = direction;
s.shadows[shadowIndex].normalBias = normalBias * wsTexelSizeAtOneMeter;
@@ -687,7 +690,8 @@ void ShadowMapManager::prepareSpotShadowMap(ShadowMap& shadowMap,
s.shadows[shadowIndex].elvsm = options->vsm.elvsm;
s.shadows[shadowIndex].bulbRadiusLs =
mSoftShadowOptions.penumbraScale * options->shadowBulbRadius
/ wsTexelSizeAtOneMeter;
/ wsTexelSizeAtOneMeter;
}
}
@@ -749,7 +753,6 @@ void ShadowMapManager::preparePointShadowMap(ShadowMap& shadowMap,
// and if we need to generate it, update all the UBO data
// Note: this below is done for all six faces even if it sets identical values each time
if (shadowMap.hasVisibleShadows()) {
const size_t shadowIndex = shadowMap.getShadowIndex();
const float wsTexelSizeAtOneMeter = shaderParameters.texelSizeAtOneMeterWs;
@@ -759,16 +762,11 @@ void ShadowMapManager::preparePointShadowMap(ShadowMap& shadowMap,
auto& s = mShadowUb.edit();
const double n = shadowMap.getCamera().getNear();
const double f = shadowMap.getCamera().getCullingFar();
s.shadows[shadowIndex].lightFromWorldMatrix = {}; // no texture matrix for point lights
s.shadows[shadowIndex].layer = shadowMap.getLayer();
s.shadows[shadowIndex].lightFromWorldMatrix = shaderParameters.lightSpace;
s.shadows[shadowIndex].direction = {}; // no direction of point lights
s.shadows[shadowIndex].normalBias = normalBias * wsTexelSizeAtOneMeter;
s.shadows[shadowIndex].lightFromWorldZ = {
-((n + f) / (f - n)) * 0.5f + 0.5f,
(f * n) / (f - n),
-n / (f - n),
1.0f / (f - n),
};
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;
@@ -788,14 +786,12 @@ ShadowMapManager::ShadowTechnique ShadowMapManager::updateSpotShadowMaps(FEngine
shadowTechnique |= ShadowTechnique::SHADOW_MAP;
for (auto const* pShadowMap : mSpotShadowMaps) {
const size_t lightIndex = pShadowMap->getLightIndex();
// FIXME: currently we have one slot per shadowmap in the UBO, but we now have up to
// 6 shadowmap per light. So for now, we only write the data of the face 0,
// and the shader will figure out where to find the other face (layer+face)
// gather the per-light (not per shadow map) information. For point lights we will
// "see" 6 shadowmaps (one per face), we must use the first face one, the shader
// knows how to find the entry for other faces (they're guaranteed to be sequential).
if (pShadowMap->getFace() == 0) {
shadowInfo[lightIndex].castsShadows = true; // FIXME: is that set correctly?
shadowInfo[lightIndex].index = pShadowMap->getShadowIndex();
shadowInfo[lightIndex].layer = pShadowMap->getLayer();
}
}
}
+4 -8
View File
@@ -45,14 +45,10 @@ class FrameGraph;
class RenderPass;
struct ShadowMappingUniforms {
std::array<math::mat4f, CONFIG_MAX_SHADOW_CASCADES> lightFromWorldMatrix;
math::float4 cascadeSplits;
float shadowBulbRadiusLs;
float shadowBias;
float ssContactShadowDistance;
uint32_t directionalShadows;
uint32_t cascades;
bool elvsm;
};
class ShadowMapManager {
@@ -103,9 +99,9 @@ public:
}
ShadowMap* getPointOrSpotShadowMap(size_t index) noexcept {
assert_invariant(index < CONFIG_MAX_SHADOWMAP_PUNCTUAL);
assert_invariant(index < CONFIG_MAX_SHADOWMAPS);
return std::launder(reinterpret_cast<ShadowMap*>(
&mShadowMapCache[CONFIG_MAX_SHADOW_CASCADES + index]));
&mShadowMapCache[index]));
}
ShadowMap const* getPointOrSpotShadowMap(size_t spot) const noexcept {
@@ -215,13 +211,13 @@ private:
utils::FixedCapacityVector<ShadowMap*> mSpotShadowMaps{
utils::FixedCapacityVector<ShadowMap*>::with_capacity(
CONFIG_MAX_SHADOWMAP_PUNCTUAL) };
CONFIG_MAX_SHADOWMAPS - CONFIG_MAX_SHADOW_CASCADES) };
// inline storage for all our ShadowMap objects, we can't easily use a std::array<> directly.
// because ShadowMap doesn't have a default ctor, and we avoid out-of-line allocations.
// Each ShadowMap is currently 40 bytes (total of 2.5KB for 64 shadow maps)
using ShadowMapStorage = std::aligned_storage<sizeof(ShadowMap), alignof(ShadowMap)>::type;
std::array<ShadowMapStorage, CONFIG_MAX_SHADOW_LAYERS> mShadowMapCache;
std::array<ShadowMapStorage, CONFIG_MAX_SHADOWMAPS> mShadowMapCache;
};
} // namespace filament
+7 -1
View File
@@ -442,9 +442,15 @@ Program FMaterial::getProgramBuilderWithVariants(
}
}
int platformId = 0;
#if defined(IOS)
platformId = 1;
#endif
program.specializationConstants({
{ 0, (int)mEngine.getSupportedFeatureLevel() },
{ 1, (int)CONFIG_MAX_INSTANCES }
{ 1, (int)CONFIG_MAX_INSTANCES },
{ 2, platformId }
});
return program;
+4 -3
View File
@@ -343,9 +343,10 @@ void FScene::prepareDynamicLights(const CameraInfo& camera, ArenaScope& rootAren
lp[gpuIndex].typeShadow = LightsUib::packTypeShadow(
lcm.isPointLight(li) ? 0u : 1u,
shadowInfo[i].contactShadows,
shadowInfo[i].index,
shadowInfo[i].layer);
lp[gpuIndex].channels = LightsUib::packChannels(lcm.getLightChannels(li), shadowInfo[i].castsShadows);
shadowInfo[i].index);
lp[gpuIndex].channels = LightsUib::packChannels(
lcm.getLightChannels(li),
shadowInfo[i].castsShadows);
}
driver.updateBufferObject(lightUbh, { lp, positionalLightCount * sizeof(LightsUib) }, 0);
+2 -2
View File
@@ -155,7 +155,6 @@ public:
bool castsShadows = false; // whether this light casts shadows
bool contactShadows = false; // whether this light casts contact shadows
uint8_t index = 0; // an index into the arrays in the Shadows uniform buffer
uint8_t layer = 0; // which layer of the shadow texture array to sample from
};
enum {
@@ -179,7 +178,8 @@ public:
LightSoa const& getLightData() const noexcept { return mLightData; }
LightSoa& getLightData() noexcept { return mLightData; }
void updateUBOs(utils::Range<uint32_t> visibleRenderables, backend::Handle<backend::HwBufferObject> renderableUbh) noexcept;
void updateUBOs(utils::Range<uint32_t> visibleRenderables,
backend::Handle<backend::HwBufferObject> renderableUbh) noexcept;
bool hasContactShadows() const noexcept;
+6 -12
View File
@@ -294,10 +294,11 @@ void FView::prepareShadowing(FEngine& engine, DriverApi& driver,
}
// Find all shadow-casting spotlights.
size_t shadowLayerCount = 0;
size_t shadowMapCount = 0;
// We allow a max of CONFIG_MAX_SHADOW_CASTING_SPOTS spotlight shadows. Any additional
// We allow a max of CONFIG_MAX_SHADOWMAPS point/spotlight shadows. Any additional
// shadow-casting spotlights are ignored.
// Note that pointlight shadows cost 6 shadowmaps, reducing the total count.
for (size_t l = FScene::DIRECTIONAL_LIGHTS_COUNT; l < lightData.size(); l++) {
// when we get here all the lights should be visible
@@ -317,12 +318,12 @@ void FView::prepareShadowing(FEngine& engine, DriverApi& driver,
mShadowMapManager.addShadowMap(l, lcm.isSpotLight(li), &shadowOptions);
if (lcm.isSpotLight(li)) {
shadowLayerCount += 6;
shadowMapCount += 6;
} else {
shadowLayerCount += 1;
shadowMapCount += 1;
}
if (shadowLayerCount > CONFIG_MAX_SHADOWMAP_PUNCTUAL - 1) {
if (shadowMapCount > CONFIG_MAX_SHADOWMAPS - 1) {
break; // we ran out of spotlight shadow casting
}
}
@@ -693,13 +694,6 @@ void FView::prepareSSR(Handle<HwTexture> ssr, float refractionLodOffset,
mPerViewUniforms.prepareSSR(ssr, refractionLodOffset, ssrOptions);
}
void FView::prepareHistorySSR(Handle<HwTexture> ssr,
mat4f const& historyProjection, mat4f const& uvFromViewMatrix,
ScreenSpaceReflectionsOptions const& ssrOptions) const noexcept {
mPerViewUniforms.prepareHistorySSR(ssr,
historyProjection, uvFromViewMatrix, ssrOptions);
}
void FView::prepareStructure(Handle<HwTexture> structure) const noexcept {
// sampler must be NEAREST
mPerViewUniforms.prepareStructure(structure);
-3
View File
@@ -145,9 +145,6 @@ public:
void prepareSSAO(backend::Handle<backend::HwTexture> ssao) const noexcept;
void prepareSSR(backend::Handle<backend::HwTexture> ssr, float refractionLodOffset,
ScreenSpaceReflectionsOptions const& ssrOptions) const noexcept;
void prepareHistorySSR(backend::Handle<backend::HwTexture> ssr,
math::mat4f const& historyProjection, math::mat4f const& uvFromViewMatrix,
ScreenSpaceReflectionsOptions const& ssrOptions) const noexcept;
void prepareStructure(backend::Handle<backend::HwTexture> structure) const noexcept;
void prepareShadow(backend::Handle<backend::HwTexture> structure) const noexcept;
void prepareShadowMapping(bool highPrecision) const noexcept;
+2 -2
View File
@@ -1,12 +1,12 @@
Pod::Spec.new do |spec|
spec.name = "Filament"
spec.version = "1.28.2"
spec.version = "1.29.0"
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.28.2/filament-v1.28.2-ios.tgz" }
spec.source = { :http => "https://github.com/google/filament/releases/download/v1.29.0/filament-v1.29.0-ios.tgz" }
# Fix linking error with Xcode 12; we do not yet support the simulator on Apple silicon.
spec.pod_target_xcconfig = {
@@ -27,7 +27,7 @@
namespace filament {
// update this when a new version of filament wouldn't work with older materials
static constexpr size_t MATERIAL_VERSION = 28;
static constexpr size_t MATERIAL_VERSION = 29;
/**
* Supported shading models
@@ -232,15 +232,14 @@ enum class Property : uint8_t {
// when adding new Properties, make sure to update MATERIAL_PROPERTIES_COUNT
};
// These flags should match the equivalents in Variant.h.
enum class UserVariantFilterBit : uint32_t {
DIRECTIONAL_LIGHTING = 0x01,
DYNAMIC_LIGHTING = 0x02,
SHADOW_RECEIVER = 0x04,
SKINNING = 0x08,
FOG = 0x20,
VSM = 0x40,
SSR = VSM | SHADOW_RECEIVER,
FOG = 0x10,
VSM = 0x20,
SSR = 0x40,
};
using UserVariantFilterMask = uint32_t;
@@ -60,29 +60,23 @@ enum class SamplerBindingPoints : uint8_t {
constexpr size_t CONFIG_MAX_LIGHT_COUNT = 256;
constexpr size_t CONFIG_MAX_LIGHT_INDEX = CONFIG_MAX_LIGHT_COUNT - 1;
// The maximum number of shadowmaps.
// There is currently a maximum limit of 128 shadowmaps.
// Factors contributing to this limit:
// - minspec for UBOs is 16KiB, which currently can hold a maximum of 128 entries
constexpr size_t CONFIG_MAX_SHADOWMAPS = 64;
// The maximum number of shadow layers.
// There is currently a limit to 146 layers.
// There is currently a maximum limit of 255 layers.
// Several factors are contributing to this limit:
// - minspec for 2d texture arrays layer is 256
// - we're using uint8_t to store the number of layers (255 max)
// - minspec for UBOs is 16KiB, which currently can hold a maximum of 146 entries
// - we need to reserve 4 entries for the cascaded shadow map
//
// CONFIG_MAX_SHADOW_LAYERS determines how many shadows can be used in the
// scene. Currently, a spotlight uses 1 layer, a cascaded directional light uses up to 4
// and a point light uses 6.
//
constexpr size_t CONFIG_MAX_SHADOW_LAYERS = 64;
// - nonsensical to be larger than the number of shadowmaps
constexpr size_t CONFIG_MAX_SHADOW_LAYERS = CONFIG_MAX_SHADOWMAPS;
// The maximum number of shadow cascades that can be used for directional lights.
constexpr size_t CONFIG_MAX_SHADOW_CASCADES = 4;
// Maximum number of shadowmap for point and spotlights
// spotlights use 1, point lights use 6
constexpr size_t CONFIG_MAX_SHADOWMAP_PUNCTUAL =
CONFIG_MAX_SHADOW_LAYERS - CONFIG_MAX_SHADOW_CASCADES;
// The maximum UBO size, in bytes. This value is set to 16 KiB due to the ES3.0 spec.
// Note that this value constrains the maximum number of skinning bones, morph targets,
// instances, and shadow casting spotlights.
@@ -124,12 +124,10 @@ struct PerViewUib { // NOLINT(cppcoreguidelines-pro-type-member-init)
// bit 0-3: cascade count
// bit 4: visualize cascades
// bit 8-11: cascade has visible shadows
// bit 31: elvsm
uint32_t cascades;
float shadowBulbRadiusLs; // light radius in light-space
float shadowBias; // normal bias
float reserved0;
float reserved1; // normal bias
float shadowPenumbraRatioScale; // For DPCF or PCSS, scale penumbra ratio for artistic use
std::array<math::mat4f, CONFIG_MAX_SHADOW_CASCADES> lightFromWorldMatrix;
// --------------------------------------------------------------------------------------------
// VSM shadows [variant: VSM]
@@ -164,7 +162,7 @@ struct PerViewUib { // NOLINT(cppcoreguidelines-pro-type-member-init)
float ssrStride; // ssr texel stride, >= 1.0
// bring PerViewUib to 2 KiB
math::float4 reserved[47];
math::float4 reserved[63];
};
// 2 KiB == 128 float4s
@@ -229,11 +227,11 @@ struct LightsUib { // NOLINT(cppcoreguidelines-pro-type-member-init)
math::float2 spotScaleOffset; // { scale, offset }
float reserved3; // 0
float intensity; // float
uint32_t typeShadow; // 0x00.ll.ii.ct (t: 0=point, 1=spot, c:contact, ii: index, ll: layer)
uint32_t typeShadow; // 0x00.00.ii.ct (t: 0=point, 1=spot, c:contact, ii: index)
uint32_t channels; // 0x000c00ll (ll: light channels, c: caster)
static uint32_t packTypeShadow(uint8_t type, bool contactShadow, uint8_t index, uint8_t layer) noexcept {
return (type & 0xF) | (contactShadow ? 0x10 : 0x00) | (index << 8) | (layer << 16);
static uint32_t packTypeShadow(uint8_t type, bool contactShadow, uint8_t index) noexcept {
return (type & 0xF) | (contactShadow ? 0x10 : 0x00) | (index << 8);
}
static uint32_t packChannels(uint8_t lightChannels, bool castShadows) noexcept {
return lightChannels | (castShadows ? 0x10000 : 0);
@@ -249,17 +247,22 @@ static_assert(sizeof(LightsUib) == 64,
struct ShadowUib { // NOLINT(cppcoreguidelines-pro-type-member-init)
static constexpr std::string_view _name{ "ShadowUniforms" };
struct alignas(16) ShadowData {
math::mat4f lightFromWorldMatrix; // 64 - unused for point lights
math::float3 direction; // 12 - unused for point lights
float normalBias; // 4 - unused for point lights
math::float4 lightFromWorldZ; // 16 - point lights { depth reconstruction values }
math::mat4f lightFromWorldMatrix; // 64
math::float3 direction; // 12
float normalBias; // 4
math::float4 lightFromWorldZ; // 16
float texelSizeAtOneMeter; // 4
float bulbRadiusLs; // 4
float nearOverFarMinusNear; // 4
bool elvsm; // 4
uint32_t layer; // 4
uint32_t reserved0; // 4
uint32_t reserved1; // 4
uint32_t reserved2; // 4
};
ShadowData shadows[CONFIG_MAX_SHADOWMAP_PUNCTUAL];
ShadowData shadows[CONFIG_MAX_SHADOWMAPS];
};
static_assert(sizeof(ShadowUib) <= CONFIG_MINSPEC_UBO_SIZE,
"ShadowUib exceeds max UBO size");
+5 -2
View File
@@ -35,6 +35,11 @@ Variant Variant::filterUserVariant(
if (filterMask & (uint32_t)UserVariantFilterBit::FOG) {
variant.key &= ~(filterMask & FOG);
}
} else {
// depth variants can have their VSM bit filtered
if (filterMask & (uint32_t)UserVariantFilterBit::VSM) {
variant.key &= ~(filterMask & VSM);
}
}
if (!isSSRVariant(variant)) {
// SSR variant needs to be handled separately
@@ -53,8 +58,6 @@ Variant Variant::filterUserVariant(
return variant;
}
namespace details {
// compile time sanity-check tests
+29 -21
View File
@@ -54,9 +54,7 @@ namespace msl { // this is only used for MSL
using BindingIndexMap = std::unordered_map<std::string, uint16_t>;
template<typename F>
static void forEachSib(const GLSLPostProcessor::Config& config, F fn) {
const auto& samplerBindings = config.materialInfo->samplerBindings;
static void collectSibs(const GLSLPostProcessor::Config& config, SibVector& sibs) {
switch (config.domain) {
case MaterialDomain::SURFACE:
UTILS_NOUNROLL
@@ -67,14 +65,15 @@ static void forEachSib(const GLSLPostProcessor::Config& config, F fn) {
auto const* sib =
SibGenerator::getSib((SamplerBindingPoints)blockIndex, config.variant);
if (sib && hasShaderType(sib->getStageFlags(), config.shaderType)) {
fn(blockIndex, sib);
sibs.emplace_back(blockIndex, sib);
}
}
case MaterialDomain::POST_PROCESS:
case MaterialDomain::COMPUTE:
break;
}
fn((uint8_t) SamplerBindingPoints::PER_MATERIAL_INSTANCE, &config.materialInfo->sib);
sibs.emplace_back((uint8_t) SamplerBindingPoints::PER_MATERIAL_INSTANCE,
&config.materialInfo->sib);
}
}; // namespace msl
@@ -138,8 +137,9 @@ static std::string stringifySpvOptimizerMessage(spv_message_level_t level, const
return oss.str();
}
void GLSLPostProcessor::spirvToToMsl(const SpirvBlob *spirv, std::string *outMsl,
const Config &config, ShaderMinifier& minifier) {
void GLSLPostProcessor::spirvToMsl(const SpirvBlob *spirv, std::string *outMsl,
filament::backend::ShaderModel shaderModel, bool useFramebufferFetch, const SibVector& sibs,
const ShaderMinifier* minifier) {
using namespace msl;
@@ -148,19 +148,19 @@ void GLSLPostProcessor::spirvToToMsl(const SpirvBlob *spirv, std::string *outMsl
mslCompiler.set_common_options(options);
const CompilerMSL::Options::Platform platform =
config.shaderModel == ShaderModel::MOBILE ?
shaderModel == ShaderModel::MOBILE ?
CompilerMSL::Options::Platform::iOS : CompilerMSL::Options::Platform::macOS;
CompilerMSL::Options mslOptions = {};
mslOptions.platform = platform,
mslOptions.msl_version = config.shaderModel == ShaderModel::MOBILE ?
mslOptions.msl_version = shaderModel == ShaderModel::MOBILE ?
CompilerMSL::Options::make_msl_version(2, 0) : CompilerMSL::Options::make_msl_version(2, 2);
if (config.hasFramebufferFetch) {
if (useFramebufferFetch) {
mslOptions.use_framebuffer_fetch_subpasses = true;
// On macOS, framebuffer fetch is only available starting with MSL 2.3. Filament will only
// use framebuffer fetch materials on devices that support it.
if (config.shaderModel == ShaderModel::DESKTOP) {
if (shaderModel == ShaderModel::DESKTOP) {
mslOptions.msl_version = CompilerMSL::Options::make_msl_version(2, 3);
}
}
@@ -221,9 +221,7 @@ void GLSLPostProcessor::spirvToToMsl(const SpirvBlob *spirv, std::string *outMsl
//
// Which is then bound to the vertex/fragment functions:
// constant spvDescriptorSetBuffer1& spvDescriptorSet1 [[buffer(27)]]
forEachSib(config,
[&executionModel, &mslCompiler, &argumentBuffers](
uint8_t bindingPoint, const SamplerInterfaceBlock* sib) {
for (auto [bindingPoint, sib] : sibs) {
const auto& infoList = sib->getSamplerInfoList();
// bindingPoint + 1, because the first descriptor set is for uniforms
@@ -251,7 +249,7 @@ void GLSLPostProcessor::spirvToToMsl(const SpirvBlob *spirv, std::string *outMsl
argBufferBinding.msl_buffer =
CodeGenerator::METAL_SAMPLER_GROUP_BINDING_START + bindingPoint;
mslCompiler.add_msl_resource_binding(argBufferBinding);
});
}
auto updateResourceBindingDefault = [executionModel, &mslCompiler]
(const auto& resource, const BindingIndexMap* map = nullptr) {
@@ -283,7 +281,9 @@ void GLSLPostProcessor::spirvToToMsl(const SpirvBlob *spirv, std::string *outMsl
mslCompiler.add_discrete_descriptor_set(0);
*outMsl = mslCompiler.compile();
*outMsl = minifier.removeWhitespace(*outMsl);
if (minifier) {
*outMsl = minifier->removeWhitespace(*outMsl);
}
// Replace spirv-cross' generated argument buffers with our own.
for (auto* argBuffer : argumentBuffers) {
@@ -374,8 +374,11 @@ bool GLSLPostProcessor::process(const std::string& inputShader, Config const& co
GlslangToSpv(*program.getIntermediate(internalConfig.shLang),
*internalConfig.spirvOutput, &options);
if (internalConfig.mslOutput) {
spirvToToMsl(internalConfig.spirvOutput, internalConfig.mslOutput, config,
internalConfig.minifier);
auto sibs = SibVector::with_capacity(CONFIG_SAMPLER_BINDING_COUNT);
msl::collectSibs(config, sibs);
spirvToMsl(internalConfig.spirvOutput, internalConfig.mslOutput,
config.shaderModel, config.hasFramebufferFetch, sibs,
&internalConfig.minifier);
}
} else {
slog.e << "GLSL post-processor invoked with optimization level NONE"
@@ -453,8 +456,10 @@ void GLSLPostProcessor::preprocessOptimization(glslang::TShader& tShader,
}
if (internalConfig.mslOutput) {
spirvToToMsl(internalConfig.spirvOutput, internalConfig.mslOutput, config,
internalConfig.minifier);
auto sibs = SibVector::with_capacity(CONFIG_SAMPLER_BINDING_COUNT);
msl::collectSibs(config, sibs);
spirvToMsl(internalConfig.spirvOutput, internalConfig.mslOutput, config.shaderModel,
config.hasFramebufferFetch, sibs, &internalConfig.minifier);
}
if (internalConfig.glslOutput) {
@@ -480,7 +485,10 @@ void GLSLPostProcessor::fullOptimization(const TShader& tShader,
}
if (internalConfig.mslOutput) {
spirvToToMsl(&spirv, internalConfig.mslOutput, config, internalConfig.minifier);
auto sibs = SibVector::with_capacity(CONFIG_SAMPLER_BINDING_COUNT);
msl::collectSibs(config, sibs);
spirvToMsl(&spirv, internalConfig.mslOutput, config.shaderModel, config.hasFramebufferFetch,
sibs, &internalConfig.minifier);
}
// Transpile back to GLSL
+13 -2
View File
@@ -26,6 +26,8 @@
#include "filamat/MaterialBuilder.h" // for MaterialBuilder:: enums
#include <utils/FixedCapacityVector.h>
#include "ShaderMinifier.h"
#include <ShaderLang.h>
@@ -34,9 +36,15 @@
#include <memory>
namespace filament {
class SamplerInterfaceBlock;
};
namespace filamat {
using SpirvBlob = std::vector<uint32_t>;
using BindingPointAndSib = std::pair<uint8_t, const filament::SamplerInterfaceBlock*>;
using SibVector = utils::FixedCapacityVector<BindingPointAndSib>;
class GLSLPostProcessor {
public:
@@ -69,6 +77,11 @@ public:
SpirvBlob* outputSpirv,
std::string* outputMsl);
// public so backend_test can also use it
static void spirvToMsl(const SpirvBlob* spirv, std::string* outMsl,
filament::backend::ShaderModel shaderModel, bool useFramebufferFetch,
const SibVector& sibs, const ShaderMinifier* minifier);
private:
struct InternalConfig {
std::string* glslOutput = nullptr;
@@ -97,8 +110,6 @@ private:
static void registerPerformancePasses(spvtools::Optimizer& optimizer, Config const& config);
void optimizeSpirv(OptimizerPtr optimizer, SpirvBlob& spirv) const;
static void spirvToToMsl(const SpirvBlob* spirv, std::string* outMsl, const Config& config,
ShaderMinifier& minifier);
const MaterialBuilder::Optimization mOptimization;
const bool mPrintShaders;
+3 -4
View File
@@ -105,10 +105,9 @@ BufferInterfaceBlock const& UibGenerator::getPerViewUib() noexcept {
{ "cascadeSplits", 0, Type::FLOAT4, Precision::HIGH },
{ "cascades", 0, Type::UINT },
{ "shadowBulbRadiusLs", 0, Type::FLOAT },
{ "shadowBias", 0, Type::FLOAT },
{ "reserved0", 0, Type::FLOAT },
{ "reserved1", 0, Type::FLOAT },
{ "shadowPenumbraRatioScale", 0, Type::FLOAT },
{ "lightFromWorldMatrix", 4, Type::MAT4, Precision::HIGH },
// ------------------------------------------------------------------------------------
// VSM shadows [variant: VSM]
@@ -171,7 +170,7 @@ BufferInterfaceBlock const& UibGenerator::getLightsUib() noexcept {
BufferInterfaceBlock const& UibGenerator::getShadowUib() noexcept {
static BufferInterfaceBlock uib = BufferInterfaceBlock::Builder()
.name(ShadowUib::_name)
.add({{ "shadows", CONFIG_MAX_SHADOWMAP_PUNCTUAL,
.add({{ "shadows", CONFIG_MAX_SHADOWMAPS,
BufferInterfaceBlock::Type::STRUCT, {},
"ShadowData", sizeof(ShadowUib::ShadowData) }})
.build();
@@ -123,6 +123,7 @@ utils::io::sstream& CodeGenerator::generateProlog(utils::io::sstream& out, Shade
out << '\n';
generateSpecificationConstant(out, "BACKEND_FEATURE_LEVEL", 0, 1);
generateSpecificationConstant(out, "CONFIG_MAX_INSTANCES", 1, (int)CONFIG_MAX_INSTANCES);
generateSpecificationConstant(out, "TARGET_PLATFORM", 2, 0);
out << '\n';
out << SHADERS_COMMON_DEFINES_GLSL_DATA;
@@ -287,6 +287,10 @@ std::string ShaderGenerator::createVertexProgram(ShaderModel shaderModel,
UniformBindingPoints::PER_VIEW, UibGenerator::getPerViewUib());
cg.generateUniforms(vs, ShaderStage::VERTEX,
UniformBindingPoints::PER_RENDERABLE, UibGenerator::getPerRenderableUib());
if (litVariants && filament::Variant::isShadowReceiverVariant(variant)) {
cg.generateUniforms(vs, ShaderStage::FRAGMENT,
UniformBindingPoints::SHADOW, UibGenerator::getShadowUib());
}
if (variant.hasSkinningOrMorphing()) {
cg.generateUniforms(vs, ShaderStage::VERTEX,
UniformBindingPoints::PER_RENDERABLE_BONES,
+1 -1
View File
@@ -603,7 +603,7 @@ void FAssetLoader::recurseEntities(const cgltf_data* srcAsset, const cgltf_node*
}
}
if (node->light ) {
if (node->light) {
createLight(node->light, entity);
}
+8
View File
@@ -22,3 +22,11 @@
#define float3x3 mat3
#define float4x4 mat4
#define TARGET_PLATFORM_UNKNOWN 0
#define TARGET_PLATFORM_IOS 1
#if defined(TARGET_GLES_ENVIRONMENT)
const bool openGlesIos = TARGET_PLATFORM == TARGET_PLATFORM_IOS;
#else
const bool openGlesIos = false;
#endif
-1
View File
@@ -9,7 +9,6 @@ struct Light {
bool contactShadows;
uint type;
uint shadowIndex;
uint shadowLayer;
uint channels;
};
+4
View File
@@ -9,6 +9,10 @@ struct ShadowData {
float bulbRadiusLs;
float nearOverFarMinusNear;
bool elvsm;
uint layer;
uint reserved0;
uint reserved1;
uint reserved2;
};
struct BoneData {
+4 -3
View File
@@ -128,14 +128,15 @@ highp vec4 getCascadeLightSpacePosition(uint cascade) {
// For the first cascade, return the interpolated light space position.
// This branch will be coherent (mostly) for neighboring fragments, and it's worth avoiding
// the matrix multiply inside computeLightSpacePosition.
if (cascade == 0u) {
if (cascade == 0u && !openGlesIos) {
// Note: this branch may cause issues with derivatives
return vertex_lightSpacePosition;
}
return computeLightSpacePosition(getWorldPosition(), getWorldNormalVector(),
frameUniforms.lightDirection, frameUniforms.shadowBias,
frameUniforms.lightFromWorldMatrix[cascade]);
frameUniforms.lightDirection,
shadowUniforms.shadows[cascade].normalBias,
shadowUniforms.shadows[cascade].lightFromWorldMatrix);
}
#endif
-4
View File
@@ -13,10 +13,6 @@ int getInstanceIndex() {
// Uniforms access
//------------------------------------------------------------------------------
mat4 getLightFromWorldMatrix() {
return frameUniforms.lightFromWorldMatrix[0];
}
PerRenderableData getObjectUniforms() {
#if defined(MATERIAL_HAS_INSTANCES)
// the material manages instancing, all instances share the same uniform block.
+1 -2
View File
@@ -56,9 +56,8 @@ void evaluateDirectionalLight(const MaterialInputs material,
bool cascadeHasVisibleShadows = bool(frameUniforms.cascades & ((1u << cascade) << 8u));
bool hasDirectionalShadows = bool(frameUniforms.directionalShadows & 1u);
if (hasDirectionalShadows && cascadeHasVisibleShadows) {
uint layer = cascade;
highp vec4 shadowPosition = getShadowPosition(true, 0u, cascade, 0.0f);
visibility = shadow(true, light_shadowMap, layer, 0u, shadowPosition, 0.0f);
visibility = shadow(true, light_shadowMap, cascade, shadowPosition, 0.0f);
}
if ((frameUniforms.directionalShadows & 0x2u) != 0u && visibility > 0.0) {
if ((getObjectUniforms().flagsChannels & FILAMENT_OBJECT_CONTACT_SHADOWS_BIT) != 0u) {
+13 -14
View File
@@ -154,17 +154,18 @@ Light getLight(const uint lightIndex) {
light.worldPosition = positionFalloff.xyz;
light.channels = channels;
light.contactShadows = bool(typeShadow & 0x10u);
#if defined(VARIANT_HAS_SHADOWING)
#if defined(VARIANT_HAS_DYNAMIC_LIGHTING)
light.shadowIndex = (typeShadow >> 8u) & 0xFFu;
light.shadowLayer = (typeShadow >> 16u) & 0xFFu;
light.castsShadows = bool(channels & 0x10000u);
light.type = (typeShadow & 0x1u);
#if defined(VARIANT_HAS_SHADOWING)
light.shadowIndex = (typeShadow >> 8u) & 0xFFu;
light.castsShadows = bool(channels & 0x10000u);
if (light.type == LIGHT_TYPE_SPOT) {
light.attenuation *= getAngleAttenuation(-direction, light.l, scaleOffset);
light.zLight = dot(shadowUniforms.shadows[light.shadowIndex].lightFromWorldZ, vec4(worldPosition, 1.0));
}
#endif
if (light.type == LIGHT_TYPE_SPOT) {
light.attenuation *= getAngleAttenuation(-direction, light.l, scaleOffset);
}
#endif
return light;
}
@@ -209,19 +210,17 @@ void evaluatePunctualLights(const MaterialInputs material,
#if defined(VARIANT_HAS_SHADOWING)
if (light.NoL > 0.0) {
if (light.castsShadows) {
uint layer = light.shadowLayer;
highp vec4 shadowPosition;
uint shadowIndex = light.shadowIndex;
if (light.type == LIGHT_TYPE_POINT) {
// point-light shadows are sampled from a direction
highp vec3 r = getWorldPosition() - light.worldPosition;
highp vec4 nf = shadowUniforms.shadows[light.shadowIndex].lightFromWorldZ;
// getShadowPosition returns zLight which is needed for PCSS/DPCF
shadowPosition = getShadowPosition(r, nf, layer, light.zLight);
} else {
// getShadowPosition needs zLight for applying the normal bias
shadowPosition = getShadowPosition(false, light.shadowIndex, 0u, light.zLight);
uint face = getPointLightFace(r);
shadowIndex += face;
light.zLight = dot(shadowUniforms.shadows[shadowIndex].lightFromWorldZ,
vec4(getWorldPosition(), 1.0));
}
visibility = shadow(false, light_shadowMap, layer, light.shadowIndex,
highp vec4 shadowPosition = getShadowPosition(false, shadowIndex, 0u, light.zLight);
visibility = shadow(false, light_shadowMap, shadowIndex,
shadowPosition, light.zLight);
}
if (light.contactShadows && visibility > 0.0) {
+10 -3
View File
@@ -141,9 +141,16 @@ void main() {
#endif
#if defined(VARIANT_HAS_SHADOWING) && defined(VARIANT_HAS_DIRECTIONAL_LIGHTING)
vertex_lightSpacePosition = computeLightSpacePosition(
vertex_worldPosition.xyz, vertex_worldNormal,
frameUniforms.lightDirection, frameUniforms.shadowBias, getLightFromWorldMatrix());
if (openGlesIos) {
// Hack for OpenGL ES on iOS.
vertex_lightSpacePosition = vec4(1.0);
} else {
vertex_lightSpacePosition = computeLightSpacePosition(
vertex_worldPosition.xyz, vertex_worldNormal,
frameUniforms.lightDirection,
shadowUniforms.shadows[0].normalBias,
shadowUniforms.shadows[0].lightFromWorldMatrix);
}
#endif
#endif // !defined(USE_OPTIMIZED_DEPTH_VERTEX_SHADER)
+1 -2
View File
@@ -50,9 +50,8 @@ vec4 evaluateMaterial(const MaterialInputs material) {
bool cascadeHasVisibleShadows = bool(frameUniforms.cascades & ((1u << cascade) << 8u));
bool hasDirectionalShadows = bool(frameUniforms.directionalShadows & 1u);
if (hasDirectionalShadows && cascadeHasVisibleShadows) {
uint layer = cascade;
highp vec4 shadowPosition = getShadowPosition(true, 0u, cascade, 0.0f);
visibility = shadow(true, light_shadowMap, layer, 0u, shadowPosition, 0.0f);
visibility = shadow(true, light_shadowMap, cascade, shadowPosition, 0.0f);
}
if ((frameUniforms.directionalShadows & 0x2u) != 0u && visibility > 0.0) {
if ((getObjectUniforms().flagsChannels & FILAMENT_OBJECT_CONTACT_SHADOWS_BIT) != 0u) {
+12 -45
View File
@@ -161,7 +161,7 @@ float getPenumbraLs(const bool DIRECTIONAL, const uint index, const highp float
float penumbra;
// This conditional is resolved at compile time
if (DIRECTIONAL) {
penumbra = frameUniforms.shadowBulbRadiusLs;
penumbra = shadowUniforms.shadows[index].bulbRadiusLs;
} else {
// the penumbra radius depends on the light-space z for spotlights
penumbra = shadowUniforms.shadows[index].bulbRadiusLs / zLight;
@@ -494,55 +494,27 @@ highp vec4 getShadowPosition(const bool DIRECTIONAL,
return p;
}
// get {texture coordinate, layer} for point shadow maps
highp vec4 getShadowPosition(const highp vec3 r, const highp vec4 nf,
inout uint layer, out highp float d) {
uint getPointLightFace(const highp vec3 r) {
highp vec4 tc;
highp float rx = abs(r.x);
highp float ry = abs(r.y);
highp float rz = abs(r.z);
d = max(rx, max(ry, rz));
highp float ma = 1.0 / d;
highp float d = max(rx, max(ry, rz));
if (d == rx) {
tc.x = r.x >= 0.0 ? r.z : -r.z;
tc.y = r.y;
layer += (r.x >= 0.0 ? 0u : 1u);
return (r.x >= 0.0 ? 0u : 1u);
} else if (d == ry) {
tc.x = r.y >= 0.0 ? r.x : -r.x;
tc.y = r.z;
layer += (r.y >= 0.0 ? 2u : 3u);
return (r.y >= 0.0 ? 2u : 3u);
} else {
tc.x = r.z >= 0.0 ? -r.x : r.x;
tc.y = r.y;
layer += (r.z >= 0.0 ? 4u : 5u);
return (r.z >= 0.0 ? 4u : 5u);
}
// ma is guaranteed to be >= sc and tc
tc.xy = (tc.xy * ma + vec2(1.0)) * 0.5;
// z coordinate of the normalized fragment position in light-space
// i.e.: remap [near, far] to [0,1] : d = (d - n) / (f - n)
d = nf[2] + nf[3] * d;
if (frameUniforms.shadowSamplingType == SHADOW_SAMPLING_RUNTIME_EVSM) {
// for VSM, the depth metric is linear normalized in light-space
tc.z = d;
} else {
// for other types of shadows it's clip-space depth. Below is an optimized version of
// (lightProjection * position).z
tc.z = nf[0] + nf[1] * ma;
}
// FIXME: the normal bias is not applied
tc.w = 1.0;
return tc;
}
// PCF sampling
float shadow(const bool DIRECTIONAL,
const mediump sampler2DArrayShadow shadowMap,
const uint layer, const uint index, highp vec4 shadowPosition, highp float zLight) {
const uint index, highp vec4 shadowPosition, highp float zLight) {
uint layer = shadowUniforms.shadows[index].layer;
#if SHADOW_SAMPLING_METHOD == SHADOW_SAMPLING_PCF_HARD
return ShadowSample_PCF_Hard(shadowMap, layer, shadowPosition);
#elif SHADOW_SAMPLING_METHOD == SHADOW_SAMPLING_PCF_LOW
@@ -553,16 +525,11 @@ float shadow(const bool DIRECTIONAL,
// Shadow requiring a sampler2D sampler (VSM, DPCF and PCSS)
float shadow(const bool DIRECTIONAL,
const mediump sampler2DArray shadowMap,
const uint layer, const uint index, highp vec4 shadowPosition, highp float zLight) {
const uint index, highp vec4 shadowPosition, highp float zLight) {
uint layer = shadowUniforms.shadows[index].layer;
// This conditional is resolved at compile time
if (frameUniforms.shadowSamplingType == SHADOW_SAMPLING_RUNTIME_EVSM) {
bool elvsm = false;
if (DIRECTIONAL) {
elvsm = bool((frameUniforms.cascades >> 31u) & 1u);
} else {
elvsm = shadowUniforms.shadows[index].elvsm;
}
bool elvsm = shadowUniforms.shadows[index].elvsm;
return ShadowSample_VSM(elvsm, shadowMap, layer, shadowPosition);
}
+13 -2
View File
@@ -417,7 +417,13 @@ export class Camera {
export class ColorGrading$Builder {
public quality(qualityLevel: ColorGrading$QualityLevel): ColorGrading$Builder;
public format(format: ColorGrading$LutFormat): ColorGrading$Builder;
public dimensions(dim: number): ColorGrading$Builder;
public toneMapping(toneMapping: ColorGrading$ToneMapping): ColorGrading$Builder;
public luminanceScaling(luminanceScaling: boolean): ColorGrading$Builder;
public gamutMapping(gamutMapping: boolean): ColorGrading$Builder;
public exposure(exposure: number): ColorGrading$Builder;
public nightAdaptation(adaptation: boolean): ColorGrading$Builder;
public whiteBalance(temperature: number, tint: number): ColorGrading$Builder;
public channelMixer(outRed: float3, outGreen: float3, outBlue: float3): ColorGrading$Builder;
public shadowsMidtonesHighlights(shadows: float4, midtones: float4, highlights: float4,
@@ -671,12 +677,17 @@ export enum Camera$Projection {
ORTHO,
}
export enum ColorGrading$QualityLevel {
export enum ColorGrading$QualityLevel {
LOW,
MEDIUM,
HIGH,
ULTRA,
}
}
export enum ColorGrading$LutFormat {
INTEGER,
FLOAT,
}
export enum ColorGrading$ToneMapping {
LINEAR,
+29
View File
@@ -747,11 +747,40 @@ class_<ColorBuilder>("ColorGrading$Builder")
return &builder->quality(ql);
})
.BUILDER_FUNCTION("format", ColorBuilder, (ColorBuilder* builder,
ColorGrading::LutFormat format), {
return &builder->format(format);
})
.BUILDER_FUNCTION("dimensions", ColorBuilder, (ColorBuilder* builder, uint8_t dim), {
return &builder->dimensions(dim);
})
.BUILDER_FUNCTION("toneMapping", ColorBuilder, (ColorBuilder* builder,
ColorGrading::ToneMapping tm), {
return &builder->toneMapping(tm);
})
.BUILDER_FUNCTION("luminanceScaling", ColorBuilder, (ColorBuilder* builder,
bool luminanceScaling), {
return &builder->luminanceScaling(luminanceScaling);
})
.BUILDER_FUNCTION("gamutMapping", ColorBuilder, (ColorBuilder* builder,
bool gamutMapping), {
return &builder->gamutMapping(gamutMapping);
})
.BUILDER_FUNCTION("exposure", ColorBuilder, (ColorBuilder* builder,
float exposure), {
return &builder->exposure(exposure);
})
.BUILDER_FUNCTION("nightAdaptation", ColorBuilder, (ColorBuilder* builder,
float adaptation), {
return &builder->nightAdaptation(adaptation);
})
.BUILDER_FUNCTION("whiteBalance", ColorBuilder, (ColorBuilder* builder, float temp,
float tint), {
return &builder->whiteBalance(temp, tint);
+4
View File
@@ -147,6 +147,10 @@ enum_<ColorGrading::ToneMapping>("ColorGrading$ToneMapping")
.value("FILMIC", ColorGrading::ToneMapping::FILMIC)
.value("DISPLAY_RANGE", ColorGrading::ToneMapping::DISPLAY_RANGE);
enum_<ColorGrading::LutFormat>("ColorGrading$LutFormat")
.value("INTEGER", ColorGrading::LutFormat::INTEGER)
.value("FLOAT", ColorGrading::LutFormat::FLOAT);
enum_<Frustum::Plane>("Frustum$Plane")
.value("LEFT", Frustum::Plane::LEFT)
.value("RIGHT", Frustum::Plane::RIGHT)
+1 -1
View File
@@ -1,6 +1,6 @@
{
"name": "filament",
"version": "1.28.2",
"version": "1.29.0",
"description": "Real-time physically based rendering engine",
"main": "filament.js",
"module": "filament.js",