Compare commits
23 Commits
bjd/fix-va
...
bjd/debugg
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
18dab53920 | ||
|
|
b754b81e6f | ||
|
|
65bb66f90c | ||
|
|
a0af84d034 | ||
|
|
3cdaf3feeb | ||
|
|
da7d1b6fa9 | ||
|
|
3129226839 | ||
|
|
6935acafff | ||
|
|
d8103f4fd6 | ||
|
|
09a13f4015 | ||
|
|
e888023102 | ||
|
|
f62f4736f0 | ||
|
|
a378272d56 | ||
|
|
3d61938fcf | ||
|
|
a9f3937da6 | ||
|
|
2dc8d6bcc0 | ||
|
|
08dc398452 | ||
|
|
cd180c9a44 | ||
|
|
0a7fe52124 | ||
|
|
b71faf47cc | ||
|
|
67babceb73 | ||
|
|
8e13fc86d5 | ||
|
|
351c8e0972 |
@@ -8,6 +8,8 @@ A new header is inserted each time a *tag* is created.
|
||||
- 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,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
|
||||
|
||||
@@ -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.
|
||||
|
||||
@@ -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.
|
||||
|
||||
@@ -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()) };
|
||||
|
||||
@@ -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;
|
||||
|
||||
|
||||
@@ -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]));
|
||||
|
||||
@@ -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));
|
||||
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -1184,6 +1184,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 +1283,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 +1297,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);
|
||||
}
|
||||
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -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;
|
||||
@@ -688,6 +691,7 @@ void ShadowMapManager::prepareSpotShadowMap(ShadowMap& shadowMap,
|
||||
s.shadows[shadowIndex].bulbRadiusLs =
|
||||
mSoftShadowOptions.penumbraScale * options->shadowBulbRadius
|
||||
/ 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,7 +762,7 @@ void ShadowMapManager::preparePointShadowMap(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 = {}; // no texture matrix for point lights
|
||||
s.shadows[shadowIndex].direction = {}; // no direction of point lights
|
||||
s.shadows[shadowIndex].normalBias = normalBias * wsTexelSizeAtOneMeter;
|
||||
@@ -788,14 +791,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();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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;
|
||||
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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);
|
||||
@@ -258,8 +256,13 @@ struct ShadowUib { // NOLINT(cppcoreguidelines-pro-type-member-init)
|
||||
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");
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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();
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -603,7 +603,7 @@ void FAssetLoader::recurseEntities(const cgltf_data* srcAsset, const cgltf_node*
|
||||
}
|
||||
}
|
||||
|
||||
if (node->light ) {
|
||||
if (node->light) {
|
||||
createLight(node->light, entity);
|
||||
}
|
||||
|
||||
|
||||
@@ -9,7 +9,6 @@ struct Light {
|
||||
bool contactShadows;
|
||||
uint type;
|
||||
uint shadowIndex;
|
||||
uint shadowLayer;
|
||||
uint channels;
|
||||
};
|
||||
|
||||
|
||||
@@ -9,6 +9,10 @@ struct ShadowData {
|
||||
float bulbRadiusLs;
|
||||
float nearOverFarMinusNear;
|
||||
bool elvsm;
|
||||
uint layer;
|
||||
uint reserved0;
|
||||
uint reserved1;
|
||||
uint reserved2;
|
||||
};
|
||||
|
||||
struct BoneData {
|
||||
|
||||
@@ -134,8 +134,9 @@ highp vec4 getCascadeLightSpacePosition(uint cascade) {
|
||||
}
|
||||
|
||||
return computeLightSpacePosition(getWorldPosition(), getWorldNormalVector(),
|
||||
frameUniforms.lightDirection, frameUniforms.shadowBias,
|
||||
frameUniforms.lightFromWorldMatrix[cascade]);
|
||||
frameUniforms.lightDirection,
|
||||
shadowUniforms.shadows[cascade].normalBias,
|
||||
shadowUniforms.shadows[cascade].lightFromWorldMatrix);
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
@@ -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.
|
||||
|
||||
@@ -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) {
|
||||
|
||||
@@ -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,20 @@ void evaluatePunctualLights(const MaterialInputs material,
|
||||
#if defined(VARIANT_HAS_SHADOWING)
|
||||
if (light.NoL > 0.0) {
|
||||
if (light.castsShadows) {
|
||||
uint layer = light.shadowLayer;
|
||||
uint shadowIndex = light.shadowIndex;
|
||||
highp vec4 shadowPosition;
|
||||
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;
|
||||
highp uint face = 0u;
|
||||
// getShadowPosition returns zLight which is needed for PCSS/DPCF
|
||||
shadowPosition = getShadowPosition(r, nf, layer, light.zLight);
|
||||
shadowPosition = getShadowPosition(r, shadowIndex, light.zLight, face);
|
||||
shadowIndex += face;
|
||||
} else {
|
||||
// getShadowPosition needs zLight for applying the normal bias
|
||||
shadowPosition = getShadowPosition(false, light.shadowIndex, 0u, light.zLight);
|
||||
shadowPosition = getShadowPosition(false, shadowIndex, 0u, light.zLight);
|
||||
}
|
||||
visibility = shadow(false, light_shadowMap, layer, light.shadowIndex,
|
||||
visibility = shadow(false, light_shadowMap, shadowIndex,
|
||||
shadowPosition, light.zLight);
|
||||
}
|
||||
if (light.contactShadows && visibility > 0.0) {
|
||||
|
||||
@@ -143,7 +143,9 @@ void main() {
|
||||
#if defined(VARIANT_HAS_SHADOWING) && defined(VARIANT_HAS_DIRECTIONAL_LIGHTING)
|
||||
vertex_lightSpacePosition = computeLightSpacePosition(
|
||||
vertex_worldPosition.xyz, vertex_worldNormal,
|
||||
frameUniforms.lightDirection, frameUniforms.shadowBias, getLightFromWorldMatrix());
|
||||
frameUniforms.lightDirection,
|
||||
shadowUniforms.shadows[0].normalBias,
|
||||
shadowUniforms.shadows[0].lightFromWorldMatrix);
|
||||
#endif
|
||||
|
||||
#endif // !defined(USE_OPTIMIZED_DEPTH_VERTEX_SHADER)
|
||||
|
||||
@@ -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) {
|
||||
|
||||
@@ -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;
|
||||
@@ -495,8 +495,8 @@ highp vec4 getShadowPosition(const bool DIRECTIONAL,
|
||||
}
|
||||
|
||||
// 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) {
|
||||
highp vec4 getShadowPosition(const highp vec3 r, const highp uint shadowIndex,
|
||||
out highp float d, out highp uint face) {
|
||||
highp vec4 tc;
|
||||
highp float rx = abs(r.x);
|
||||
highp float ry = abs(r.y);
|
||||
@@ -506,20 +506,22 @@ highp vec4 getShadowPosition(const highp vec3 r, const highp vec4 nf,
|
||||
if (d == rx) {
|
||||
tc.x = r.x >= 0.0 ? r.z : -r.z;
|
||||
tc.y = r.y;
|
||||
layer += (r.x >= 0.0 ? 0u : 1u);
|
||||
face = (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);
|
||||
face = (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);
|
||||
face = (r.z >= 0.0 ? 4u : 5u);
|
||||
}
|
||||
|
||||
// ma is guaranteed to be >= sc and tc
|
||||
tc.xy = (tc.xy * ma + vec2(1.0)) * 0.5;
|
||||
|
||||
highp vec4 nf = shadowUniforms.shadows[shadowIndex + face].lightFromWorldZ;
|
||||
|
||||
// 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;
|
||||
@@ -542,7 +544,8 @@ highp vec4 getShadowPosition(const highp vec3 r, const highp vec4 nf,
|
||||
// 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 +556,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);
|
||||
}
|
||||
|
||||
|
||||
15
web/filament-js/filament.d.ts
vendored
15
web/filament-js/filament.d.ts
vendored
@@ -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,
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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)
|
||||
|
||||
Reference in New Issue
Block a user