Compare commits

..

23 Commits

Author SHA1 Message Date
Benjamin Doherty
18dab53920 All changes 2022-11-14 22:20:59 -08:00
Benjamin Doherty
b754b81e6f Almost all the changes 2022-11-14 22:16:58 -08:00
Benjamin Doherty
65bb66f90c Even more shader changes 2022-11-14 22:00:10 -08:00
Benjamin Doherty
a0af84d034 Some more shader changes 2022-11-14 21:56:50 -08:00
Benjamin Doherty
3cdaf3feeb ShadowMapManager.h changes 2022-11-14 17:18:00 -08:00
Benjamin Doherty
da7d1b6fa9 Update Scene.cpp 2022-11-14 17:09:55 -08:00
Benjamin Doherty
3129226839 Fix literal 0 2022-11-14 17:07:55 -08:00
Benjamin Doherty
6935acafff Changes to ShadowMapManager 2022-11-14 17:07:25 -08:00
Benjamin Doherty
d8103f4fd6 Update ShaderGenerator.cpp 2022-11-14 16:55:50 -08:00
Benjamin Doherty
09a13f4015 Some shadowing changes 2022-11-14 15:25:08 -08:00
Benjamin Doherty
e888023102 Some shader changes 2022-11-14 15:00:09 -08:00
Benjamin Doherty
f62f4736f0 Adjust LightsUib 2022-11-14 13:39:08 -08:00
Benjamin Doherty
a378272d56 Remove some more uniforms 2022-11-14 13:34:26 -08:00
Benjamin Doherty
3d61938fcf Remove lightFromWorldMatrix from PerViewUniforms 2022-11-14 13:30:54 -08:00
Benjamin Doherty
a9f3937da6 Trivial changes 2022-11-14 13:05:52 -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
41 changed files with 325 additions and 305 deletions

View File

@@ -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

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

View File

@@ -405,6 +405,7 @@ if (APPLE)
backend
getopt
gtest
filamat
SPIRV
spirv-cross-glsl)

View File

@@ -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.

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.

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()) };

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;

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]));

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]));

View File

@@ -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));

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);

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

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,

View File

@@ -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);
}

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 {

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;
@@ -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();
}
}
}

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

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);

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;

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);

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;

View File

@@ -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;

View File

@@ -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.

View File

@@ -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");

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

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;

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();

View File

@@ -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,

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);
}

View File

@@ -9,7 +9,6 @@ struct Light {
bool contactShadows;
uint type;
uint shadowIndex;
uint shadowLayer;
uint channels;
};

View File

@@ -9,6 +9,10 @@ struct ShadowData {
float bulbRadiusLs;
float nearOverFarMinusNear;
bool elvsm;
uint layer;
uint reserved0;
uint reserved1;
uint reserved2;
};
struct BoneData {

View File

@@ -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

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.

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) {

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,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) {

View File

@@ -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)

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) {

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;
@@ -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);
}

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,

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);

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)