Compare commits
14 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
41a809368b | ||
|
|
ea53eb9290 | ||
|
|
05875057c9 | ||
|
|
60734349de | ||
|
|
157c0264af | ||
|
|
9bacfdb390 | ||
|
|
f9aaf5c42e | ||
|
|
21db695e79 | ||
|
|
cf917f1093 | ||
|
|
a8d3a61c25 | ||
|
|
80f13a8149 | ||
|
|
e172f3a67f | ||
|
|
c382c0a9cc | ||
|
|
78b29fe967 |
@@ -31,7 +31,7 @@ repositories {
|
||||
}
|
||||
|
||||
dependencies {
|
||||
implementation 'com.google.android.filament:filament-android:1.13.0'
|
||||
implementation 'com.google.android.filament:filament-android:1.14.0'
|
||||
}
|
||||
```
|
||||
|
||||
@@ -52,7 +52,7 @@ Here are all the libraries available in the group `com.google.android.filament`:
|
||||
iOS projects can use CocoaPods to install the latest release:
|
||||
|
||||
```
|
||||
pod 'Filament', '~> 1.13.0'
|
||||
pod 'Filament', '~> 1.14.0'
|
||||
```
|
||||
|
||||
### Snapshots
|
||||
|
||||
@@ -3,7 +3,15 @@
|
||||
This file contains one line summaries of commits that are worthy of mentioning in release notes.
|
||||
A new header is inserted each time a *tag* is created.
|
||||
|
||||
## v1.13.1 (currently main branch)
|
||||
## v1.14.1 (currently main branch)
|
||||
|
||||
## v1.14.0
|
||||
|
||||
- engine: Internal materials can use structures as parameters [⚠️ **Material breakage**].
|
||||
- engine: `readPixels` on a `SwapChain` must be called within `beginFrame` / `endFrame` [⚠️ **API
|
||||
Change**].
|
||||
- engine: Fix normal bias and improve spotlight quality.
|
||||
- Java: Fix shadow biases.
|
||||
|
||||
## v1.13.0
|
||||
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
import java.nio.file.Paths
|
||||
|
||||
// This script accepts the following parameters:
|
||||
//
|
||||
// com.google.android.filament.dist-dir
|
||||
@@ -42,23 +44,31 @@
|
||||
//
|
||||
|
||||
buildscript {
|
||||
def filamentPath = file("../out/android-release/filament").absolutePath
|
||||
if (project.hasProperty("com.google.android.filament.dist-dir")) {
|
||||
filamentPath = file(project.property("com.google.android.filament.dist-dir")).absolutePath
|
||||
}
|
||||
def path = providers
|
||||
.gradleProperty("com.google.android.filament.dist-dir")
|
||||
.forUseAtConfigurationTime().get()
|
||||
|
||||
def directory = objects.fileProperty().fileValue(new File(path)).getAsFile().get()
|
||||
def filamentPath = directory.absolutePath
|
||||
|
||||
// Our CMake scripts require a forward-slash path for the FILAMENT_DIST_DIR
|
||||
// variable, so here we convert the native path to a forward-slash path.
|
||||
filamentPath = filamentPath.replace(File.separator, '/')
|
||||
|
||||
// Warning: changing this property does not work well with incremental builds.
|
||||
def excludeVulkan = project.hasProperty("com.google.android.filament.exclude-vulkan")
|
||||
def excludeVulkan = providers
|
||||
.gradleProperty("com.google.android.filament.exclude-vulkan")
|
||||
.forUseAtConfigurationTime()
|
||||
.isPresent()
|
||||
|
||||
def abis = ["arm64-v8a", "armeabi-v7a", "x86_64", "x86"]
|
||||
if (project.hasProperty("com.google.android.filament.abis")) {
|
||||
def newAbis = project.property("com.google.android.filament.abis").split(',')
|
||||
if (!newAbis.contains("all")) {
|
||||
abis = newAbis
|
||||
}
|
||||
def newAbis = providers
|
||||
.gradleProperty("com.google.android.filament.abis")
|
||||
.forUseAtConfigurationTime()
|
||||
.get()
|
||||
.split(',')
|
||||
if (!newAbis.contains("all")) {
|
||||
abis = newAbis
|
||||
}
|
||||
|
||||
ext.versions = [
|
||||
@@ -72,7 +82,7 @@ buildscript {
|
||||
|
||||
ext.deps = [
|
||||
'androidx': [
|
||||
'annotations': "androidx.annotation:annotation:1.1.0",
|
||||
'annotations': "androidx.annotation:annotation:1.3.0",
|
||||
'core': "androidx.core:core:1.3.0",
|
||||
],
|
||||
'kotlin': "org.jetbrains.kotlin:kotlin-stdlib-jdk8:${versions.kotlin}"
|
||||
@@ -120,7 +130,7 @@ buildscript {
|
||||
}
|
||||
|
||||
plugins {
|
||||
id 'io.codearte.nexus-staging' version '0.22.0'
|
||||
id 'io.codearte.nexus-staging' version '0.30.0'
|
||||
}
|
||||
|
||||
// Nexus Staging configuration
|
||||
|
||||
@@ -76,7 +76,9 @@ extern "C" JNIEXPORT void JNICALL
|
||||
Java_com_google_android_filament_LightManager_nBuilderShadowOptions(JNIEnv* env, jclass,
|
||||
jlong nativeBuilder, jint mapSize, jint cascades, jfloatArray splitPositions,
|
||||
jfloat constantBias, jfloat normalBias, jfloat shadowFar, jfloat shadowNearHint,
|
||||
jfloat shadowFarHint, jboolean stable, jboolean screenSpaceContactShadows, jint stepCount,
|
||||
jfloat shadowFarHint, jboolean stable,
|
||||
jfloat polygonOffsetConstant, jfloat polygonOffsetSlope,
|
||||
jboolean screenSpaceContactShadows, jint stepCount,
|
||||
jfloat maxShadowDistance, jint vsmMsaaSamples, jfloat blurWidth) {
|
||||
LightManager::Builder *builder = (LightManager::Builder *) nativeBuilder;
|
||||
LightManager::ShadowOptions shadowOptions {
|
||||
@@ -88,6 +90,8 @@ Java_com_google_android_filament_LightManager_nBuilderShadowOptions(JNIEnv* env,
|
||||
.shadowNearHint = shadowNearHint,
|
||||
.shadowFarHint = shadowFarHint,
|
||||
.stable = (bool)stable,
|
||||
.polygonOffsetConstant = polygonOffsetConstant,
|
||||
.polygonOffsetSlope = polygonOffsetConstant,
|
||||
.screenSpaceContactShadows = (bool)screenSpaceContactShadows,
|
||||
.stepCount = uint8_t(stepCount),
|
||||
.maxShadowDistance = maxShadowDistance,
|
||||
|
||||
@@ -244,13 +244,13 @@ public class LightManager {
|
||||
* light. 1mm by default.
|
||||
* This is ignored when the View's ShadowType is set to VSM.
|
||||
*/
|
||||
public float constantBias = 0.05f;
|
||||
public float constantBias = 0.001f;
|
||||
|
||||
/** Amount by which the maximum sampling error is scaled. The resulting value is used
|
||||
* to move the shadow away from the fragment normal. Should be 1.0.
|
||||
* This is ignored when the View's ShadowType is set to VSM.
|
||||
*/
|
||||
public float normalBias = 0.4f;
|
||||
public float normalBias = 1.0f;
|
||||
|
||||
/** Distance from the camera after which shadows are clipped. This is used to clip
|
||||
* shadows that are too far and wouldn't contribute to the scene much, improving
|
||||
@@ -279,7 +279,24 @@ public class LightManager {
|
||||
* When set to true, all resolution enhancing features that can affect stability are
|
||||
* disabling, resulting in significantly lower resolution shadows, albeit stable ones.
|
||||
*/
|
||||
public boolean stable = true;
|
||||
public boolean stable = false;
|
||||
|
||||
/**
|
||||
* Constant bias in depth-resolution units by which shadows are moved away from the
|
||||
* light. The default value of 0.5 is used to round depth values up.
|
||||
* Generally this value shouldn't be changed or at least be small and positive.
|
||||
* This is ignored when the View's ShadowType is set to VSM.
|
||||
*/
|
||||
float polygonOffsetConstant = 0.5f;
|
||||
|
||||
/**
|
||||
* Bias based on the change in depth in depth-resolution units by which shadows are moved
|
||||
* away from the light. The default value of 2.0 works well with SHADOW_SAMPLING_PCF_LOW.
|
||||
* Generally this value is between 0.5 and the size in texel of the PCF filter.
|
||||
* Setting this value correctly is essential for LISPSM shadow-maps.
|
||||
* This is ignored when the View's ShadowType is set to VSM.
|
||||
*/
|
||||
float polygonOffsetSlope = 2.0f;
|
||||
|
||||
/**
|
||||
* Whether screen-space contact shadows are used. This applies regardless of whether a
|
||||
@@ -471,7 +488,9 @@ public class LightManager {
|
||||
nBuilderShadowOptions(mNativeBuilder,
|
||||
options.mapSize, options.shadowCascades, options.cascadeSplitPositions,
|
||||
options.constantBias, options.normalBias, options.shadowFar, options.shadowNearHint,
|
||||
options.shadowFarHint, options.stable, options.screenSpaceContactShadows,
|
||||
options.shadowFarHint, options.stable,
|
||||
options.polygonOffsetConstant, options.polygonOffsetSlope,
|
||||
options.screenSpaceContactShadows,
|
||||
options.stepCount, options.maxShadowDistance, options.vsmMsaaSamples,
|
||||
options.blurWidth);
|
||||
return this;
|
||||
@@ -1131,7 +1150,7 @@ public class LightManager {
|
||||
private static native void nDestroyBuilder(long nativeBuilder);
|
||||
private static native boolean nBuilderBuild(long nativeBuilder, long nativeEngine, int entity);
|
||||
private static native void nBuilderCastShadows(long nativeBuilder, boolean enable);
|
||||
private static native void nBuilderShadowOptions(long nativeBuilder, int mapSize, int cascades, float[] splitPositions, float constantBias, float normalBias, float shadowFar, float shadowNearHint, float shadowFarhint, boolean stable, boolean screenSpaceContactShadows, int stepCount, float maxShadowDistance, int vsmMsaaSamples, float blurWidth);
|
||||
private static native void nBuilderShadowOptions(long nativeBuilder, int mapSize, int cascades, float[] splitPositions, float constantBias, float normalBias, float shadowFar, float shadowNearHint, float shadowFarhint, boolean stable, float polygonOffsetConstant, float polygonOffsetSlope, boolean screenSpaceContactShadows, int stepCount, float maxShadowDistance, int vsmMsaaSamples, float blurWidth);
|
||||
private static native void nBuilderCastLight(long nativeBuilder, boolean enabled);
|
||||
private static native void nBuilderPosition(long nativeBuilder, float x, float y, float z);
|
||||
private static native void nBuilderDirection(long nativeBuilder, float x, float y, float z);
|
||||
|
||||
@@ -437,8 +437,9 @@ public class Renderer {
|
||||
*</pre>
|
||||
*
|
||||
*
|
||||
* <p>Typically <code>readPixels</code> will be called after {@link #render} and before
|
||||
* {@link #endFrame}.</p>
|
||||
* <p><code>readPixels</code> must be called within a frame, meaning after {@link #beginFrame}
|
||||
* and before {@link #endFrame}. Typically, <code>readPixels</code> will be called after
|
||||
* {@link #render}.</p>
|
||||
* <br>
|
||||
* <p>After calling this method, the callback associated with <code>buffer</code>
|
||||
* will be invoked on the main thread, indicating that the read-back has completed.
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
GROUP=com.google.android.filament
|
||||
VERSION_NAME=1.13.0
|
||||
VERSION_NAME=1.14.0
|
||||
|
||||
POM_DESCRIPTION=Real-time physically based rendering engine for Android.
|
||||
|
||||
@@ -19,6 +19,8 @@ org.gradle.jvmargs=-Xmx1536m
|
||||
|
||||
android.useAndroidX=true
|
||||
|
||||
org.gradle.unsafe.configuration-cache=false
|
||||
org.gradle.unsafe.configuration-cache=true
|
||||
|
||||
com.google.android.filament.tools-dir=../../../out/release/filament
|
||||
com.google.android.filament.dist-dir=../out/android-release/filament
|
||||
com.google.android.filament.abis=all
|
||||
|
||||
@@ -32,6 +32,11 @@ android {
|
||||
targetSdkVersion versions.targetSdk
|
||||
missingDimensionStrategy 'functionality', 'full'
|
||||
}
|
||||
// NOTE: This is a workaround required because the AGP task collectReleaseDependencies
|
||||
// is not configuration-cache friendly yet; this is only useful for Play publication
|
||||
dependenciesInfo {
|
||||
includeInApk = false
|
||||
}
|
||||
}
|
||||
|
||||
dependencies {
|
||||
|
||||
@@ -23,6 +23,12 @@ android {
|
||||
targetSdkVersion versions.targetSdk
|
||||
}
|
||||
|
||||
// NOTE: This is a workaround required because the AGP task collectReleaseDependencies
|
||||
// is not configuration-cache friendly yet; this is only useful for Play publication
|
||||
dependenciesInfo {
|
||||
includeInApk = false
|
||||
}
|
||||
|
||||
// We use the .filamat extension for materials compiled with matc
|
||||
// Telling aapt to not compress them allows to load them efficiently
|
||||
aaptOptions {
|
||||
|
||||
@@ -23,6 +23,12 @@ android {
|
||||
targetSdkVersion versions.targetSdk
|
||||
}
|
||||
|
||||
// NOTE: This is a workaround required because the AGP task collectReleaseDependencies
|
||||
// is not configuration-cache friendly yet; this is only useful for Play publication
|
||||
dependenciesInfo {
|
||||
includeInApk = false
|
||||
}
|
||||
|
||||
// We use the .filamat extension for materials compiled with matc
|
||||
// Telling aapt to not compress them allows to load them efficiently
|
||||
aaptOptions {
|
||||
|
||||
@@ -29,6 +29,12 @@ android {
|
||||
targetSdkVersion versions.targetSdk
|
||||
}
|
||||
|
||||
// NOTE: This is a workaround required because the AGP task collectReleaseDependencies
|
||||
// is not configuration-cache friendly yet; this is only useful for Play publication
|
||||
dependenciesInfo {
|
||||
includeInApk = false
|
||||
}
|
||||
|
||||
// We use the .filamat extension for materials compiled with matc
|
||||
// Telling aapt to not compress them allows to load them efficiently
|
||||
aaptOptions {
|
||||
|
||||
@@ -22,6 +22,12 @@ android {
|
||||
targetSdkVersion versions.targetSdk
|
||||
}
|
||||
|
||||
// NOTE: This is a workaround required because the AGP task collectReleaseDependencies
|
||||
// is not configuration-cache friendly yet; this is only useful for Play publication
|
||||
dependenciesInfo {
|
||||
includeInApk = false
|
||||
}
|
||||
|
||||
// We use the .filamat extension for materials compiled with matc
|
||||
// Telling aapt to not compress them allows to load them efficiently
|
||||
aaptOptions {
|
||||
|
||||
@@ -10,6 +10,12 @@ android {
|
||||
minSdkVersion versions.minSdk
|
||||
targetSdkVersion versions.targetSdk
|
||||
}
|
||||
|
||||
// NOTE: This is a workaround required because the AGP task collectReleaseDependencies
|
||||
// is not configuration-cache friendly yet; this is only useful for Play publication
|
||||
dependenciesInfo {
|
||||
includeInApk = false
|
||||
}
|
||||
}
|
||||
|
||||
dependencies {
|
||||
|
||||
@@ -33,6 +33,12 @@ android {
|
||||
missingDimensionStrategy 'functionality', 'full'
|
||||
}
|
||||
|
||||
// NOTE: This is a workaround required because the AGP task collectReleaseDependencies
|
||||
// is not configuration-cache friendly yet; this is only useful for Play publication
|
||||
dependenciesInfo {
|
||||
includeInApk = false
|
||||
}
|
||||
|
||||
// We use the .filamat extension for materials compiled with matc
|
||||
// Telling aapt to not compress them allows to load them efficiently
|
||||
aaptOptions {
|
||||
|
||||
@@ -22,6 +22,12 @@ android {
|
||||
targetSdkVersion versions.targetSdk
|
||||
}
|
||||
|
||||
// NOTE: This is a workaround required because the AGP task collectReleaseDependencies
|
||||
// is not configuration-cache friendly yet; this is only useful for Play publication
|
||||
dependenciesInfo {
|
||||
includeInApk = false
|
||||
}
|
||||
|
||||
// We use the .filamat extension for materials compiled with matc
|
||||
// Telling aapt to not compress them allows to load them efficiently
|
||||
aaptOptions {
|
||||
|
||||
@@ -25,6 +25,12 @@ android {
|
||||
targetSdkVersion versions.targetSdk
|
||||
}
|
||||
|
||||
// NOTE: This is a workaround required because the AGP task collectReleaseDependencies
|
||||
// is not configuration-cache friendly yet; this is only useful for Play publication
|
||||
dependenciesInfo {
|
||||
includeInApk = false
|
||||
}
|
||||
|
||||
// We use the .filamat extension for materials compiled with matc
|
||||
// Telling aapt to not compress them allows to load them efficiently
|
||||
aaptOptions {
|
||||
@@ -34,5 +40,5 @@ android {
|
||||
|
||||
dependencies {
|
||||
implementation project(':filament-android')
|
||||
implementation 'androidx.annotation:annotation:1.1.0'
|
||||
implementation deps.androidx.annotations
|
||||
}
|
||||
|
||||
@@ -23,6 +23,12 @@ android {
|
||||
targetSdkVersion versions.targetSdk
|
||||
}
|
||||
|
||||
// NOTE: This is a workaround required because the AGP task collectReleaseDependencies
|
||||
// is not configuration-cache friendly yet; this is only useful for Play publication
|
||||
dependenciesInfo {
|
||||
includeInApk = false
|
||||
}
|
||||
|
||||
// We use the .filamat extension for materials compiled with matc
|
||||
// Telling aapt to not compress them allows to load them efficiently
|
||||
aaptOptions {
|
||||
|
||||
@@ -23,6 +23,12 @@ android {
|
||||
targetSdkVersion versions.targetSdk
|
||||
}
|
||||
|
||||
// NOTE: This is a workaround required because the AGP task collectReleaseDependencies
|
||||
// is not configuration-cache friendly yet; this is only useful for Play publication
|
||||
dependenciesInfo {
|
||||
includeInApk = false
|
||||
}
|
||||
|
||||
// We use the .filamat extension for materials compiled with matc
|
||||
// Telling aapt to not compress them allows to load them efficiently
|
||||
aaptOptions {
|
||||
|
||||
@@ -30,6 +30,12 @@ android {
|
||||
missingDimensionStrategy 'functionality', 'full'
|
||||
}
|
||||
|
||||
// NOTE: This is a workaround required because the AGP task collectReleaseDependencies
|
||||
// is not configuration-cache friendly yet; this is only useful for Play publication
|
||||
dependenciesInfo {
|
||||
includeInApk = false
|
||||
}
|
||||
|
||||
// We use the .filamat extension for materials compiled with matc
|
||||
// Telling aapt to not compress them allows to load them efficiently
|
||||
aaptOptions {
|
||||
|
||||
@@ -23,6 +23,12 @@ android {
|
||||
targetSdkVersion versions.targetSdk
|
||||
}
|
||||
|
||||
// NOTE: This is a workaround required because the AGP task collectReleaseDependencies
|
||||
// is not configuration-cache friendly yet; this is only useful for Play publication
|
||||
dependenciesInfo {
|
||||
includeInApk = false
|
||||
}
|
||||
|
||||
// We use the .filamat extension for materials compiled with matc
|
||||
// Telling aapt to not compress them allows to load them efficiently
|
||||
aaptOptions {
|
||||
|
||||
@@ -220,7 +220,8 @@ enum class UniformType : uint8_t {
|
||||
UINT3,
|
||||
UINT4,
|
||||
MAT3, //!< a 3x3 float matrix
|
||||
MAT4 //!< a 4x4 float matrix
|
||||
MAT4, //!< a 4x4 float matrix
|
||||
STRUCT
|
||||
};
|
||||
|
||||
enum class Precision : uint8_t {
|
||||
|
||||
@@ -273,6 +273,7 @@ public:
|
||||
* Constant bias in depth-resolution units by which shadows are moved away from the
|
||||
* light. The default value of 0.5 is used to round depth values up.
|
||||
* Generally this value shouldn't be changed or at least be small and positive.
|
||||
* This is ignored when the View's ShadowType is set to VSM.
|
||||
*/
|
||||
float polygonOffsetConstant = 0.5f;
|
||||
|
||||
@@ -281,6 +282,7 @@ public:
|
||||
* away from the light. The default value of 2.0 works well with SHADOW_SAMPLING_PCF_LOW.
|
||||
* Generally this value is between 0.5 and the size in texel of the PCF filter.
|
||||
* Setting this value correctly is essential for LISPSM shadow-maps.
|
||||
* This is ignored when the View's ShadowType is set to VSM.
|
||||
*/
|
||||
float polygonOffsetSlope = 2.0f;
|
||||
|
||||
|
||||
@@ -341,7 +341,7 @@ public:
|
||||
*
|
||||
* Framebuffer as seen on User buffer (PixelBufferDescriptor&)
|
||||
* screen
|
||||
*
|
||||
*
|
||||
* +--------------------+
|
||||
* | | .stride .alignment
|
||||
* | | ----------------------->-->
|
||||
@@ -359,7 +359,8 @@ public:
|
||||
* O------------+-------+
|
||||
*
|
||||
*
|
||||
* Typically readPixels() will be called after render() and before endFrame().
|
||||
* readPixels() must be called within a frame, meaning after beginFrame() and before endFrame().
|
||||
* Typically, readPixels() will be called after render().
|
||||
*
|
||||
* After issuing this method, the callback associated with `buffer` will be invoked on the
|
||||
* main thread, indicating that the read-back has completed. Typically, this will happen
|
||||
|
||||
@@ -1171,6 +1171,11 @@ void FRenderer::endFrame() {
|
||||
|
||||
void FRenderer::readPixels(uint32_t xoffset, uint32_t yoffset, uint32_t width, uint32_t height,
|
||||
PixelBufferDescriptor&& buffer) {
|
||||
#ifndef NDEBUG
|
||||
const bool withinFrame = mSwapChain != nullptr;
|
||||
ASSERT_PRECONDITION(withinFrame, "readPixels() on a SwapChain must be called after"
|
||||
" beginFrame() and before endFrame().");
|
||||
#endif
|
||||
readPixels(mRenderTarget, xoffset, yoffset, width, height, std::move(buffer));
|
||||
}
|
||||
|
||||
|
||||
@@ -22,6 +22,7 @@
|
||||
|
||||
#include "details/Engine.h"
|
||||
#include "details/Scene.h"
|
||||
#include "details/View.h"
|
||||
|
||||
#include <backend/DriverEnums.h>
|
||||
|
||||
@@ -74,7 +75,7 @@ void ShadowMap::render(FScene const& scene, utils::Range<uint32_t> range,
|
||||
pass->setCamera(cameraInfo);
|
||||
pass->setVisibilityMask(visibilityMask);
|
||||
pass->setGeometry(scene.getRenderableData(), range, scene.getRenderableUBO());
|
||||
pass->overridePolygonOffset(&mPolygonOffset);
|
||||
pass->overridePolygonOffset(&mShadowMapInfo.polygonOffset);
|
||||
pass->appendCommands(RenderPass::SHADOW);
|
||||
pass->sortCommands();
|
||||
}
|
||||
@@ -84,85 +85,41 @@ mat4f ShadowMap::getDirectionalLightViewMatrix(float3 direction, float3 position
|
||||
return FCamera::rigidTransformInverse(M);
|
||||
}
|
||||
|
||||
void ShadowMap::update(const FScene::LightSoa& lightData, size_t index,
|
||||
void ShadowMap::updateDirectional(const FScene::LightSoa& lightData, size_t index,
|
||||
filament::CameraInfo const& camera,
|
||||
const ShadowMapInfo& shadowMapInfo, FScene const& scene,
|
||||
SceneInfo& sceneInfo) noexcept {
|
||||
// this is the hard part here, find a good frustum for our camera
|
||||
|
||||
auto& lcm = mEngine.getLightManager();
|
||||
|
||||
FLightManager::Instance li = lightData.elementAt<FScene::LIGHT_INSTANCE>(index);
|
||||
mShadowMapInfo = shadowMapInfo;
|
||||
|
||||
FLightManager::ShadowParams params = lcm.getShadowParams(li);
|
||||
mPolygonOffset = {
|
||||
// handle reversed Z
|
||||
.slope = -params.options.polygonOffsetSlope,
|
||||
.constant = -params.options.polygonOffsetConstant
|
||||
};
|
||||
|
||||
// Note: we keep the polygon offset even with VSM as it seems to help.
|
||||
auto& lcm = mEngine.getLightManager();
|
||||
FLightManager::Instance li = lightData.elementAt<FScene::LIGHT_INSTANCE>(index);
|
||||
FLightManager::ShadowParams params = lcm.getShadowParams(li);
|
||||
|
||||
// Adjust the camera's projection for the light's shadowFar
|
||||
mat4f cullingProjection(camera.cullingProjection);
|
||||
if (params.options.shadowFar > 0.0f) {
|
||||
float n = camera.zn;
|
||||
float f = params.options.shadowFar;
|
||||
if (std::abs(cullingProjection[2].w) > std::numeric_limits<float>::epsilon()) {
|
||||
// perspective projection
|
||||
cullingProjection[2].z = (f + n) / (n - f);
|
||||
cullingProjection[3].z = (2 * f * n) / (n - f);
|
||||
} else {
|
||||
// orthographic projection
|
||||
cullingProjection[2].z = 2.0f / (n - f);
|
||||
cullingProjection[3].z = (f + n) / (n - f);
|
||||
}
|
||||
}
|
||||
|
||||
const ShadowCameraInfo cameraInfo = {
|
||||
.projection = cullingProjection,
|
||||
.model = camera.model,
|
||||
.view = camera.view,
|
||||
.worldOrigin = camera.worldOrigin,
|
||||
.zn = camera.zn,
|
||||
.zf = camera.zf
|
||||
};
|
||||
|
||||
// debugging...
|
||||
const float dz = cameraInfo.zf - cameraInfo.zn;
|
||||
#ifndef NDEBUG
|
||||
// LISPSM debugging for directional light (works because we only have one)
|
||||
const float dz = camera.zf - camera.zn;
|
||||
float& dzn = mEngine.debug.shadowmap.dzn;
|
||||
float& dzf = mEngine.debug.shadowmap.dzf;
|
||||
if (dzn < 0) dzn = std::max(0.0f, params.options.shadowNearHint - camera.zn) / dz;
|
||||
else params.options.shadowNearHint = dzn * dz - camera.zn;
|
||||
if (dzf > 0) dzf =-std::max(0.0f, camera.zf - params.options.shadowFarHint) / dz;
|
||||
else params.options.shadowFarHint = dzf * dz + camera.zf;
|
||||
#endif
|
||||
|
||||
using LightType = FLightManager::Type;
|
||||
switch (lcm.getType(li)) {
|
||||
case LightType::SUN:
|
||||
case LightType::DIRECTIONAL:
|
||||
computeShadowCameraDirectional(
|
||||
lightData.elementAt<FScene::DIRECTION>(index),
|
||||
cameraInfo, params, scene, sceneInfo);
|
||||
break;
|
||||
case LightType::FOCUSED_SPOT:
|
||||
case LightType::SPOT:
|
||||
computeShadowCameraSpot(
|
||||
lightData.elementAt<FScene::POSITION_RADIUS>(index).xyz,
|
||||
lightData.elementAt<FScene::DIRECTION>(index), lcm.getSpotLightOuterCone(li),
|
||||
lightData.elementAt<FScene::POSITION_RADIUS>(index).w,
|
||||
cameraInfo, params, scene, sceneInfo);
|
||||
break;
|
||||
case LightType::POINT:
|
||||
break;
|
||||
// Adjust the camera's projection for the light's shadowFar
|
||||
mat4f cullingProjection(camera.cullingProjection);
|
||||
if (params.options.shadowFar > 0.0f) {
|
||||
float n = camera.zn;
|
||||
float f = params.options.shadowFar;
|
||||
// orthographic projection
|
||||
assert_invariant(std::abs(cullingProjection[2].w) <= std::numeric_limits<float>::epsilon());
|
||||
cullingProjection[2].z = 2.0f / (n - f);
|
||||
cullingProjection[3].z = (f + n) / (n - f);
|
||||
}
|
||||
}
|
||||
|
||||
void ShadowMap::computeShadowCameraDirectional(
|
||||
float3 const& dir, ShadowCameraInfo const& camera,
|
||||
FLightManager::ShadowParams const& params,
|
||||
FScene const& scene, SceneInfo& sceneInfo) noexcept {
|
||||
auto direction = lightData.elementAt<FScene::DIRECTION>(index);
|
||||
|
||||
/*
|
||||
* Compute the light's model matrix
|
||||
@@ -171,7 +128,7 @@ void ShadowMap::computeShadowCameraDirectional(
|
||||
// We compute the directional light's model matrix using the origin's as the light position.
|
||||
// The choice of the light's origin initially doesn't matter for a directional light.
|
||||
// This will be adjusted later because of how we compute the depth metric for VSM.
|
||||
const mat4f MvAtOrigin = getDirectionalLightViewMatrix(dir);
|
||||
const mat4f MvAtOrigin = getDirectionalLightViewMatrix(direction);
|
||||
|
||||
// Compute scene-dependent values shared across all cascades
|
||||
ShadowMap::updateSceneInfo(MvAtOrigin, scene, sceneInfo);
|
||||
@@ -186,7 +143,7 @@ void ShadowMap::computeShadowCameraDirectional(
|
||||
// view frustum vertices in world-space
|
||||
float3 wsViewFrustumVertices[8];
|
||||
computeFrustumCorners(wsViewFrustumVertices,
|
||||
camera.model * FCamera::inverseProjection(camera.projection),
|
||||
camera.model * FCamera::inverseProjection(cullingProjection),
|
||||
sceneInfo.csNearFar);
|
||||
|
||||
// we use aligned_storage<> here to avoid the default initialization of std::array<>
|
||||
@@ -233,7 +190,7 @@ void ShadowMap::computeShadowCameraDirectional(
|
||||
|
||||
// Now that we know the znear (-lsLightFrustumBounds.max.z), adjust the light's position such
|
||||
// that znear = 0, this is only need for VSM, but doesn't hurt PCF.
|
||||
const mat4f Mv = getDirectionalLightViewMatrix(dir, dir * -lsLightFrustumBounds.max.z);
|
||||
const mat4f Mv = getDirectionalLightViewMatrix(direction, direction * -lsLightFrustumBounds.max.z);
|
||||
|
||||
// near / far planes are specified relative to the direction the eye is looking at
|
||||
// i.e. the -z axis (see: ortho)
|
||||
@@ -310,7 +267,7 @@ void ShadowMap::computeShadowCameraDirectional(
|
||||
LMpMv = L * MpMv;
|
||||
|
||||
W = applyLISPSM(Wp, camera, params, LMpMv,
|
||||
wsClippedShadowReceiverVolume, vertexCount, dir);
|
||||
wsClippedShadowReceiverVolume, vertexCount, direction);
|
||||
}
|
||||
|
||||
/*
|
||||
@@ -363,7 +320,8 @@ void ShadowMap::computeShadowCameraDirectional(
|
||||
|
||||
if (params.options.stable) {
|
||||
// Use the world origin as reference point, fixed w.r.t. the camera
|
||||
snapLightFrustum(s, o, Mv, camera.worldOrigin[3].xyz, 1.0f / mShadowMapInfo.shadowDimension);
|
||||
snapLightFrustum(s, o, Mv, camera.worldOrigin[3].xyz,
|
||||
1.0f / mShadowMapInfo.shadowDimension);
|
||||
}
|
||||
|
||||
const mat4f F(mat4f::row_major_init {
|
||||
@@ -403,7 +361,7 @@ void ShadowMap::computeShadowCameraDirectional(
|
||||
// for perspective and lispsm shadow maps. This also allows us to do this at zero-cost
|
||||
// by baking it in the shadow-map itself.
|
||||
const float constantBias = mShadowMapInfo.vsm ? 0.0f : params.options.constantBias;
|
||||
const mat4f b = mat4f::translation(dir * constantBias);
|
||||
const mat4f b = mat4f::translation(direction * constantBias);
|
||||
|
||||
// It's important to set the light camera's model matrix separately from its projection, so
|
||||
// that the cameraPosition uniform gets set correctly.
|
||||
@@ -421,10 +379,20 @@ void ShadowMap::computeShadowCameraDirectional(
|
||||
}
|
||||
}
|
||||
|
||||
void ShadowMap::computeShadowCameraSpot(float3 const& position, float3 const& dir,
|
||||
float outerConeAngle, float radius, ShadowCameraInfo const& camera,
|
||||
FLightManager::ShadowParams const& params, FScene const& scene,
|
||||
SceneInfo& sceneInfo) noexcept {
|
||||
void ShadowMap::updateSpot(const FScene::LightSoa& lightData, size_t index,
|
||||
filament::CameraInfo const& camera,
|
||||
const ShadowMapInfo& shadowMapInfo,
|
||||
FScene const& scene, SceneInfo& sceneInfo) noexcept {
|
||||
|
||||
mShadowMapInfo = shadowMapInfo;
|
||||
|
||||
auto& lcm = mEngine.getLightManager();
|
||||
auto li = lightData.elementAt<FScene::LIGHT_INSTANCE>(index);
|
||||
auto position = lightData.elementAt<FScene::POSITION_RADIUS>(index).xyz;
|
||||
auto direction = lightData.elementAt<FScene::DIRECTION>(index);
|
||||
auto radius = lightData.elementAt<FScene::POSITION_RADIUS>(index).w;
|
||||
auto outerConeAngle = lcm.getSpotLightOuterCone(li);
|
||||
const FLightManager::ShadowParams& params = lcm.getShadowParams(li);
|
||||
|
||||
// TODO: correctly compute if this spot light has any visible shadows.
|
||||
mHasVisibleShadows = true;
|
||||
@@ -434,12 +402,11 @@ void ShadowMap::computeShadowCameraSpot(float3 const& position, float3 const& di
|
||||
*/
|
||||
|
||||
// Choose a reasonable value for the near plane.
|
||||
const mat4f Mv = getDirectionalLightViewMatrix(dir, position);
|
||||
const mat4f Mv = getDirectionalLightViewMatrix(direction, position);
|
||||
|
||||
// find decent near/far
|
||||
// TODO: we can do much better by rejecting objects that don't intersect our frustum
|
||||
ShadowMap::updateSceneInfo(Mv, scene, sceneInfo);
|
||||
// FIXME: we need a configuration minimum near plane (for now hardcoded to 1cm)
|
||||
ShadowMap::updateSceneInfo(Mv, scene, sceneInfo, mShadowMapInfo.spotIndex);
|
||||
// FIXME: we need a configuration for minimum near plane (for now hardcoded to 1cm)
|
||||
float nearPlane = std::max(0.01f, -sceneInfo.lsNearFar.x);
|
||||
float farPlane = std::min(radius, -sceneInfo.lsNearFar.y);
|
||||
|
||||
@@ -454,9 +421,17 @@ void ShadowMap::computeShadowCameraSpot(float3 const& position, float3 const& di
|
||||
const mat4f St = mat4f(MbMt * S);
|
||||
|
||||
// TODO: focus projection
|
||||
// 1) focus on the casters
|
||||
// 2) additionally focus that on intersection of view & receivers
|
||||
// Alternatively,
|
||||
// Project receivers, casters and view onto near plane,
|
||||
// compute intersection of that which gives the l,r,t,b planes
|
||||
|
||||
// FIXME: texelSizeWorldSpace doesn't work for spotlights
|
||||
mTexelSizeWs = 0; //texelSizeWorldSpace(Mp, mat4f(MbMt));
|
||||
// For spotlights, we store the texel size at 1 world unit
|
||||
// The size of a texel in world unit is given by: (near/dimension) / lightspace.z,
|
||||
// when computing the required bias we need a half-texel size, so we multiply by 0.5 here.
|
||||
// Note: this would not work with LISPSM, which warps the texture space.
|
||||
mTexelSizeWs = 0.5f * nearPlane / float(mShadowMapInfo.shadowDimension);
|
||||
|
||||
if (!mShadowMapInfo.vsm) {
|
||||
mLightSpace = St;
|
||||
@@ -465,7 +440,7 @@ void ShadowMap::computeShadowCameraSpot(float3 const& position, float3 const& di
|
||||
}
|
||||
|
||||
const float constantBias = mShadowMapInfo.vsm ? 0.0f : params.options.constantBias;
|
||||
const mat4f b = mat4f::translation(dir * constantBias);
|
||||
const mat4f b = mat4f::translation(direction * constantBias);
|
||||
const mat4f Sb = S * b;
|
||||
|
||||
// It's important to set the light camera's model matrix separately from its projection, so that
|
||||
@@ -484,7 +459,7 @@ void ShadowMap::computeShadowCameraSpot(float3 const& position, float3 const& di
|
||||
}
|
||||
|
||||
mat4f ShadowMap::applyLISPSM(mat4f& Wp,
|
||||
ShadowCameraInfo const& camera, FLightManager::ShadowParams const& params,
|
||||
filament::CameraInfo const& camera, FLightManager::ShadowParams const& params,
|
||||
mat4f const& LMpMv,
|
||||
FrustumBoxIntersection const& wsShadowReceiversVolume, size_t vertexCount,
|
||||
float3 const& dir) {
|
||||
@@ -993,7 +968,7 @@ float ShadowMap::texelSizeWorldSpace(const mat4f& Wp, const mat4f& MbMtF) const
|
||||
|
||||
const float ures = 1.0f / mShadowMapInfo.shadowDimension;
|
||||
const float vres = 1.0f / mShadowMapInfo.shadowDimension;
|
||||
const float dres = mShadowMapInfo.zResolution;
|
||||
const float dres = 1.0f / 65536.0f;
|
||||
|
||||
constexpr bool JACOBIAN_ESTIMATE = false;
|
||||
if constexpr (JACOBIAN_ESTIMATE) {
|
||||
@@ -1052,16 +1027,17 @@ void ShadowMap::visitScene(const FScene& scene, uint32_t visibleLayers,
|
||||
float3 const* const UTILS_RESTRICT worldAABBExtent = soa.data<FScene::WORLD_AABB_EXTENT>();
|
||||
uint8_t const* const UTILS_RESTRICT layers = soa.data<FScene::LAYERS>();
|
||||
State const* const UTILS_RESTRICT visibility = soa.data<FScene::VISIBILITY_STATE>();
|
||||
auto const* const UTILS_RESTRICT visibleMasks = soa.data<FScene::VISIBLE_MASK>();
|
||||
size_t c = soa.size();
|
||||
for (size_t i = 0; i < c; i++) {
|
||||
if (layers[i] & visibleLayers) {
|
||||
const Aabb aabb{ worldAABBCenter[i] - worldAABBExtent[i],
|
||||
worldAABBCenter[i] + worldAABBExtent[i] };
|
||||
if (visibility[i].castShadows) {
|
||||
casters(aabb);
|
||||
casters(aabb, visibleMasks[i]);
|
||||
}
|
||||
if (visibility[i].receiveShadows) {
|
||||
receivers(aabb);
|
||||
receivers(aabb, visibleMasks[i]);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1079,13 +1055,13 @@ void ShadowMap::initSceneInfo(FScene const& scene, filament::CameraInfo const& c
|
||||
sceneInfo.wsShadowCastersVolume = {};
|
||||
sceneInfo.wsShadowReceiversVolume = {};
|
||||
visitScene(scene, sceneInfo.visibleLayers,
|
||||
[&](Aabb caster) {
|
||||
[&](Aabb caster, Culler::result_type) {
|
||||
sceneInfo.wsShadowCastersVolume.min =
|
||||
min(sceneInfo.wsShadowCastersVolume.min, caster.min);
|
||||
sceneInfo.wsShadowCastersVolume.max =
|
||||
max(sceneInfo.wsShadowCastersVolume.max, caster.max);
|
||||
},
|
||||
[&](Aabb receiver) {
|
||||
[&](Aabb receiver, Culler::result_type) {
|
||||
sceneInfo.wsShadowReceiversVolume.min =
|
||||
min(sceneInfo.wsShadowReceiversVolume.min, receiver.min);
|
||||
sceneInfo.wsShadowReceiversVolume.max =
|
||||
@@ -1101,12 +1077,29 @@ void ShadowMap::updateSceneInfo(const mat4f& Mv, FScene const& scene,
|
||||
ShadowMap::SceneInfo& sceneInfo) {
|
||||
sceneInfo.lsNearFar = { std::numeric_limits<float>::lowest(), std::numeric_limits<float>::max() };
|
||||
visitScene(scene, sceneInfo.visibleLayers,
|
||||
[&](Aabb caster) {
|
||||
[&](Aabb caster, Culler::result_type) {
|
||||
float2 nf = ShadowMap::computeNearFar(Mv, caster);
|
||||
sceneInfo.lsNearFar.x = std::max(sceneInfo.lsNearFar.x, nf.x); // near
|
||||
sceneInfo.lsNearFar.y = std::min(sceneInfo.lsNearFar.y, nf.y); // far
|
||||
},
|
||||
[&](Aabb receiver) {
|
||||
[&](Aabb receiver, Culler::result_type) {
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
void ShadowMap::updateSceneInfo(const mat4f& Mv, FScene const& scene,
|
||||
ShadowMap::SceneInfo& sceneInfo, uint16_t index) {
|
||||
sceneInfo.lsNearFar = { std::numeric_limits<float>::lowest(), std::numeric_limits<float>::max() };
|
||||
sceneInfo.vsNearFar = { std::numeric_limits<float>::lowest(), std::numeric_limits<float>::max() };
|
||||
visitScene(scene, sceneInfo.visibleLayers,
|
||||
[&](Aabb caster, Culler::result_type mask) {
|
||||
if (mask & VISIBLE_SPOT_SHADOW_RENDERABLE_N(index)) {
|
||||
float2 nf = ShadowMap::computeNearFar(Mv, caster);
|
||||
sceneInfo.lsNearFar.x = std::max(sceneInfo.lsNearFar.x, nf.x); // near
|
||||
sceneInfo.lsNearFar.y = std::min(sceneInfo.lsNearFar.y, nf.y); // far
|
||||
}
|
||||
},
|
||||
[&](Aabb receiver, Culler::result_type) {
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
@@ -44,10 +44,6 @@ public:
|
||||
~ShadowMap();
|
||||
|
||||
struct ShadowMapInfo {
|
||||
// the smallest increment in depth precision
|
||||
// e.g., for 16 bit depth textures, is this 1 / (2^16)
|
||||
float zResolution = 0.0f;
|
||||
|
||||
// the dimension of the encompassing texture atlas
|
||||
uint16_t atlasDimension = 0;
|
||||
|
||||
@@ -59,8 +55,14 @@ public:
|
||||
// e.g., for a texture dimension of 512, shadowDimension would be 510
|
||||
uint16_t shadowDimension = 0;
|
||||
|
||||
// This spot shadowmap index.
|
||||
uint16_t spotIndex = 0;
|
||||
|
||||
// whether we're using vsm
|
||||
bool vsm = false;
|
||||
|
||||
// polygon offset
|
||||
backend::PolygonOffset polygonOffset{};
|
||||
};
|
||||
|
||||
struct SceneInfo {
|
||||
@@ -73,10 +75,10 @@ public:
|
||||
|
||||
// light's near/far expressed in light-space, calculated from the scene's content
|
||||
// assuming the light is at the origin.
|
||||
math::float2 lsNearFar;
|
||||
math::float2 lsNearFar{};
|
||||
|
||||
// Viewing camera's near/far expressed in view-space, calculated from the scene's content
|
||||
math::float2 vsNearFar;
|
||||
math::float2 vsNearFar{};
|
||||
|
||||
// World-space shadow-casters volume
|
||||
Aabb wsShadowCastersVolume;
|
||||
@@ -92,7 +94,13 @@ public:
|
||||
|
||||
// Call once per frame if the light, scene (or visible layers) or camera changes.
|
||||
// This computes the light's camera.
|
||||
void update(const FScene::LightSoa& lightData, size_t index, filament::CameraInfo const& camera,
|
||||
void updateDirectional(const FScene::LightSoa& lightData, size_t index,
|
||||
filament::CameraInfo const& camera,
|
||||
const ShadowMapInfo& shadowMapInfo, FScene const& scene,
|
||||
SceneInfo& sceneInfo) noexcept;
|
||||
|
||||
void updateSpot(const FScene::LightSoa& lightData, size_t index,
|
||||
filament::CameraInfo const& camera,
|
||||
const ShadowMapInfo& shadowMapInfo, FScene const& scene,
|
||||
SceneInfo& sceneInfo) noexcept;
|
||||
|
||||
@@ -116,7 +124,7 @@ public:
|
||||
// use only for debugging
|
||||
FCamera const& getDebugCamera() const noexcept { return *mDebugCamera; }
|
||||
|
||||
backend::PolygonOffset getPolygonOffset() const noexcept { return mPolygonOffset; }
|
||||
backend::PolygonOffset getPolygonOffset() const noexcept { return mShadowMapInfo.polygonOffset; }
|
||||
|
||||
// Call once per frame to populate the SceneInfo struct, then pass to update().
|
||||
// This computes values constant across all shadow maps.
|
||||
@@ -127,20 +135,10 @@ public:
|
||||
static void updateSceneInfo(const math::mat4f& Mv, FScene const& scene,
|
||||
ShadowMap::SceneInfo& sceneInfo);
|
||||
|
||||
private:
|
||||
struct ShadowCameraInfo {
|
||||
math::mat4f projection;
|
||||
math::mat4f model;
|
||||
math::mat4f view;
|
||||
math::mat4f worldOrigin;
|
||||
float zn = 0;
|
||||
float zf = 0;
|
||||
math::float3 const& getPosition() const noexcept { return model[3].xyz; }
|
||||
math::float3 getForwardVector() const noexcept {
|
||||
return -normalize(model[2].xyz); // the camera looks towards -z
|
||||
}
|
||||
};
|
||||
static void updateSceneInfo(const math::mat4f& Mv, FScene const& scene,
|
||||
ShadowMap::SceneInfo& sceneInfo, uint16_t index);
|
||||
|
||||
private:
|
||||
struct Segment {
|
||||
uint8_t v0, v1;
|
||||
};
|
||||
@@ -152,17 +150,8 @@ private:
|
||||
// 8 corners, 12 segments w/ 2 intersection max -- all of this twice (8 + 12 * 2) * 2 (768 bytes)
|
||||
using FrustumBoxIntersection = std::array<math::float3, 64>;
|
||||
|
||||
void computeShadowCameraDirectional(math::float3 const& dir, ShadowCameraInfo const& camera,
|
||||
FLightManager::ShadowParams const& params, FScene const& scene,
|
||||
SceneInfo& sceneInfo) noexcept;
|
||||
|
||||
void computeShadowCameraSpot(math::float3 const& position, math::float3 const& dir,
|
||||
float outerConeAngle, float radius, ShadowCameraInfo const& camera,
|
||||
FLightManager::ShadowParams const& params, FScene const& scene,
|
||||
SceneInfo& sceneInfo) noexcept;
|
||||
|
||||
static math::mat4f applyLISPSM(math::mat4f& Wp,
|
||||
ShadowCameraInfo const& camera, FLightManager::ShadowParams const& params,
|
||||
filament::CameraInfo const& camera, FLightManager::ShadowParams const& params,
|
||||
const math::mat4f& LMpMv,
|
||||
FrustumBoxIntersection const& wsShadowReceiverVolume, size_t vertexCount,
|
||||
const math::float3& dir);
|
||||
@@ -247,9 +236,8 @@ private:
|
||||
float mTexelSizeWs = 0.0f; // 4
|
||||
|
||||
// set-up in update()
|
||||
ShadowMapInfo mShadowMapInfo; // 12
|
||||
ShadowMapInfo mShadowMapInfo; // 20
|
||||
bool mHasVisibleShadows = false; // 1
|
||||
backend::PolygonOffset mPolygonOffset{}; // 8
|
||||
|
||||
FEngine& mEngine; // 8
|
||||
const bool mClipSpaceFlipped; // 1
|
||||
|
||||
@@ -57,9 +57,10 @@ ShadowMapManager::ShadowTechnique ShadowMapManager::update(
|
||||
FEngine& engine, FView& view,
|
||||
TypedUniformBuffer<ShadowUib>& shadowUb, FScene::RenderableSoa& renderableData,
|
||||
FScene::LightSoa& lightData) noexcept {
|
||||
calculateTextureRequirements(engine, view, lightData);
|
||||
ShadowTechnique shadowTechnique = {};
|
||||
|
||||
calculateTextureRequirements(engine, view, lightData);
|
||||
|
||||
ShadowMap::SceneInfo sceneInfo(view.getVisibleLayers());
|
||||
|
||||
// Compute scene-dependent values shared across all shadow maps
|
||||
@@ -107,7 +108,7 @@ void ShadowMapManager::render(FrameGraph& fg, FEngine& engine, backend::DriverAp
|
||||
const TextureFormat vsmTextureFormat = TextureFormat::RG16F;
|
||||
|
||||
// make a copy here, because it's a very small structure
|
||||
const TextureRequirements textureRequirements = mTextureRequirements;
|
||||
const TextureAtlasRequirements textureRequirements = mTextureAtlasRequirements;
|
||||
assert_invariant(textureRequirements.layers <= MAX_SHADOW_LAYERS);
|
||||
|
||||
struct ShadowPass {
|
||||
@@ -338,36 +339,39 @@ ShadowMapManager::ShadowTechnique ShadowMapManager::updateCascadeShadowMaps(FEng
|
||||
ShadowMap::SceneInfo& sceneInfo) noexcept {
|
||||
FScene* scene = view.getScene();
|
||||
const CameraInfo& viewingCameraInfo = view.getCameraInfo();
|
||||
const uint16_t textureSize = mTextureRequirements.size;
|
||||
auto& lcm = engine.getLightManager();
|
||||
|
||||
FLightManager::Instance directionalLight = lightData.elementAt<FScene::LIGHT_INSTANCE>(0);
|
||||
LightManager::ShadowOptions const& options = lcm.getShadowOptions(directionalLight);
|
||||
FLightManager::ShadowOptions const& options = lcm.getShadowOptions(directionalLight);
|
||||
FLightManager::ShadowParams const& params = lcm.getShadowParams(directionalLight);
|
||||
|
||||
const ShadowMap::ShadowMapInfo shadowMapInfo{
|
||||
.atlasDimension = mTextureAtlasRequirements.size,
|
||||
.textureDimension = uint16_t(options.mapSize),
|
||||
.shadowDimension = uint16_t(options.mapSize - 2u),
|
||||
.vsm = view.hasVsm(),
|
||||
.polygonOffset = { // handle reversed Z
|
||||
.slope = view.hasVsm() ? 0.0f : -params.options.polygonOffsetSlope,
|
||||
.constant = view.hasVsm() ? 0.0f : -params.options.polygonOffsetConstant
|
||||
}
|
||||
};
|
||||
|
||||
if (!mCascadeShadowMaps.empty()) {
|
||||
// Even if we have more than one cascade, we cull directional shadow casters against the
|
||||
// entire camera frustum, as if we only had a single cascade.
|
||||
ShadowMapEntry& entry = mCascadeShadowMaps[0];
|
||||
ShadowMap& map = entry.getShadowMap();
|
||||
const size_t textureDimension = entry.getShadowOptions()->mapSize;
|
||||
const ShadowMap::ShadowMapInfo shadowMapInfo {
|
||||
.zResolution = mTextureZResolution,
|
||||
.atlasDimension = textureSize,
|
||||
.textureDimension = (uint16_t)textureDimension,
|
||||
.shadowDimension = (uint16_t)(textureDimension - 2),
|
||||
.vsm = view.hasVsm()
|
||||
};
|
||||
ShadowMap& shadowMap = entry.getShadowMap();
|
||||
|
||||
map.update(lightData, 0, viewingCameraInfo, shadowMapInfo, *scene, sceneInfo);
|
||||
shadowMap.updateDirectional(lightData, 0, viewingCameraInfo, shadowMapInfo, *scene, sceneInfo);
|
||||
|
||||
Frustum const& frustum = map.getCamera().getCullingFrustum();
|
||||
Frustum const& frustum = shadowMap.getCamera().getCullingFrustum();
|
||||
FView::cullRenderables(engine.getJobSystem(), renderableData, frustum,
|
||||
VISIBLE_DIR_SHADOW_RENDERABLE_BIT);
|
||||
|
||||
// note: normalBias is set to zero for VSM
|
||||
const float normalBias = shadowMapInfo.vsm ? 0.0f : lcm.getShadowNormalBias(0);
|
||||
// Set shadowBias, using the first directional cascade.
|
||||
const float texelSizeWorldSpace = map.getTexelSizeWorldSpace();
|
||||
const float texelSizeWorldSpace = shadowMap.getTexelSizeWorldSpace();
|
||||
mShadowMappingUniforms.shadowBias = float3{ 0, normalBias * texelSizeWorldSpace, 0 };
|
||||
}
|
||||
|
||||
@@ -425,19 +429,11 @@ ShadowMapManager::ShadowTechnique ShadowMapManager::updateCascadeShadowMaps(FEng
|
||||
ShadowMap& shadowMap = entry.getShadowMap();
|
||||
assert_invariant(entry.getLightIndex() == 0);
|
||||
|
||||
const size_t textureDimension = entry.getShadowOptions()->mapSize;
|
||||
const ShadowMap::ShadowMapInfo shadowMapInfo{
|
||||
.zResolution = mTextureZResolution,
|
||||
.atlasDimension = textureSize,
|
||||
.textureDimension = (uint16_t)textureDimension,
|
||||
.shadowDimension = (uint16_t)(textureDimension - 2),
|
||||
.vsm = view.hasVsm()
|
||||
};
|
||||
sceneInfo.csNearFar = { csSplitPosition[i], csSplitPosition[i + 1] };
|
||||
|
||||
shadowMap.update(lightData, 0,
|
||||
shadowMap.updateDirectional(lightData, 0,
|
||||
viewingCameraInfo, shadowMapInfo,
|
||||
*scene,sceneInfo);
|
||||
*scene, sceneInfo);
|
||||
|
||||
if (shadowMap.hasVisibleShadows()) {
|
||||
mShadowMappingUniforms.lightFromWorldMatrix[i] = shadowMap.getLightSpaceMatrix();
|
||||
@@ -476,64 +472,81 @@ ShadowMapManager::ShadowTechnique ShadowMapManager::updateCascadeShadowMaps(FEng
|
||||
|
||||
ShadowMapManager::ShadowTechnique ShadowMapManager::updateSpotShadowMaps(FEngine& engine,
|
||||
FView& view, FScene::RenderableSoa& renderableData, FScene::LightSoa& lightData,
|
||||
ShadowMap::SceneInfo& sceneInfo, TypedUniformBuffer <ShadowUib>& shadowUb) noexcept {
|
||||
ShadowMap::SceneInfo& sceneInfo, TypedUniformBuffer<ShadowUib>& shadowUb) noexcept {
|
||||
|
||||
ShadowTechnique shadowTechnique{};
|
||||
auto& lcm = engine.getLightManager();
|
||||
const CameraInfo& viewingCameraInfo = view.getCameraInfo();
|
||||
const uint16_t textureSize = mTextureRequirements.size;
|
||||
|
||||
// shadow-map shadows for point/spotlights
|
||||
auto& lcm = engine.getLightManager();
|
||||
ShadowTechnique shadowTechnique{};
|
||||
FScene::ShadowInfo* const shadowInfo = lightData.data<FScene::SHADOW_INFO>();
|
||||
for (size_t i = 0, c = mSpotShadowMaps.size(); i < c; i++) {
|
||||
auto& entry = mSpotShadowMaps[i];
|
||||
|
||||
// compute the frustum for this light
|
||||
ShadowMap& shadowMap = entry.getShadowMap();
|
||||
size_t l = entry.getLightIndex();
|
||||
const size_t lightIndex = entry.getLightIndex();
|
||||
const FLightManager::Instance li = lightData.elementAt<FScene::LIGHT_INSTANCE>(lightIndex);
|
||||
FLightManager::ShadowParams params = lcm.getShadowParams(li);
|
||||
|
||||
const size_t textureDimension = entry.getShadowOptions()->mapSize;
|
||||
FLightManager::ShadowOptions const* const options = entry.getShadowOptions();
|
||||
const ShadowMap::ShadowMapInfo shadowMapInfo{
|
||||
.zResolution = mTextureZResolution,
|
||||
.atlasDimension = textureSize,
|
||||
.textureDimension = (uint16_t)textureDimension,
|
||||
.shadowDimension = (uint16_t)(textureDimension - 2),
|
||||
.vsm = view.hasVsm()
|
||||
.atlasDimension = mTextureAtlasRequirements.size,
|
||||
.textureDimension = uint16_t(options->mapSize),
|
||||
.shadowDimension = uint16_t(options->mapSize - 2u),
|
||||
.spotIndex = uint16_t(i),
|
||||
.vsm = view.hasVsm(),
|
||||
.polygonOffset = { // handle reversed Z
|
||||
.slope = view.hasVsm() ? 0.0f : -params.options.polygonOffsetSlope,
|
||||
.constant = view.hasVsm() ? 0.0f : -params.options.polygonOffsetConstant
|
||||
}
|
||||
};
|
||||
|
||||
shadowMap.update(lightData, l,
|
||||
// for spotlights, we cull shadow casters first because we already know the frustum,
|
||||
// this will help us find better near/far plane later
|
||||
|
||||
const auto position = lightData.elementAt<FScene::POSITION_RADIUS>(lightIndex).xyz;
|
||||
const auto direction = lightData.elementAt<FScene::DIRECTION>(lightIndex);
|
||||
const auto radius = lightData.elementAt<FScene::POSITION_RADIUS>(lightIndex).w;
|
||||
const auto outerConeAngle = lcm.getSpotLightOuterCone(li);
|
||||
|
||||
const mat4f Mv = ShadowMap::getDirectionalLightViewMatrix(direction, position);
|
||||
const mat4f Mp = mat4f::perspective(outerConeAngle * f::RAD_TO_DEG * 2.0f,
|
||||
1.0f, 0.01f, radius);
|
||||
const mat4f MpMv(math::highPrecisionMultiply(Mp, Mv));
|
||||
const Frustum frustum(MpMv);
|
||||
|
||||
// Cull shadow casters
|
||||
FView::cullRenderables(engine.getJobSystem(), renderableData, frustum,
|
||||
VISIBLE_SPOT_SHADOW_RENDERABLE_N_BIT(i));
|
||||
|
||||
shadowMap.updateSpot(lightData, lightIndex,
|
||||
viewingCameraInfo, shadowMapInfo,
|
||||
*view.getScene(), sceneInfo);
|
||||
|
||||
FLightManager::Instance light = lightData.elementAt<FScene::LIGHT_INSTANCE>(l);
|
||||
if (shadowMap.hasVisibleShadows()) {
|
||||
// Cull shadow casters
|
||||
Frustum const& frustum = shadowMap.getCamera().getCullingFrustum();
|
||||
FView::cullRenderables(engine.getJobSystem(), renderableData, frustum,
|
||||
VISIBLE_SPOT_SHADOW_RENDERABLE_N_BIT(i));
|
||||
|
||||
auto& s = shadowUb.edit();
|
||||
s.spotLightFromWorldMatrix[i] = shadowMap.getLightSpaceMatrix();
|
||||
|
||||
shadowInfo[l].castsShadows = true;
|
||||
shadowInfo[l].index = i;
|
||||
shadowInfo[l].layer = mSpotShadowMaps[i].getLayer();
|
||||
shadowInfo[lightIndex].castsShadows = true;
|
||||
shadowInfo[lightIndex].index = i;
|
||||
shadowInfo[lightIndex].layer = entry.getLayer();
|
||||
|
||||
// note: normalBias is set to zero for VSM
|
||||
const float3 dir = lightData.elementAt<FScene::DIRECTION>(l);
|
||||
const float texelSizeWorldSpace = shadowMap.getTexelSizeWorldSpace();
|
||||
const float normalBias = shadowMapInfo.vsm ? 0.0f : lcm.getShadowNormalBias(light);
|
||||
s.directionShadowBias[i] = float4{ dir, normalBias * texelSizeWorldSpace };
|
||||
const float normalBias = shadowMapInfo.vsm ? 0.0f : options->normalBias;
|
||||
|
||||
auto& s = shadowUb.edit();
|
||||
s.shadows[i].direction = direction;
|
||||
s.shadows[i].normalBias = normalBias * texelSizeWorldSpace;
|
||||
s.shadows[i].lightFromWorldMatrix = shadowMap.getLightSpaceMatrix();
|
||||
|
||||
shadowTechnique |= ShadowTechnique::SHADOW_MAP;
|
||||
}
|
||||
}
|
||||
|
||||
// screen-space contact shadows for point/spotlights
|
||||
auto *pInstance = lightData.data<FScene::LIGHT_INSTANCE>();
|
||||
auto *pLightInstances = lightData.data<FScene::LIGHT_INSTANCE>();
|
||||
for (size_t i = 0, c = lightData.size(); i < c; i++) {
|
||||
// screen-space contact shadows
|
||||
LightManager::ShadowOptions const& shadowOptions = lcm.getShadowOptions(pInstance[i]);
|
||||
LightManager::ShadowOptions const& shadowOptions = lcm.getShadowOptions(pLightInstances[i]);
|
||||
if (shadowOptions.screenSpaceContactShadows) {
|
||||
shadowTechnique |= ShadowTechnique::SCREEN_SPACE;
|
||||
shadowInfo[i].contactShadows = true;
|
||||
@@ -579,7 +592,7 @@ void ShadowMapManager::calculateTextureRequirements(FEngine& engine, FView& view
|
||||
mipLevels = std::max(1, FTexture::maxLevelCount(maxDimension) - lowMipmapLevel);
|
||||
}
|
||||
|
||||
mTextureRequirements = {
|
||||
mTextureAtlasRequirements = {
|
||||
(uint16_t)maxDimension,
|
||||
layersNeeded,
|
||||
mipLevels
|
||||
|
||||
@@ -114,11 +114,13 @@ public:
|
||||
|
||||
private:
|
||||
|
||||
struct TextureRequirements {
|
||||
// Atlas requirements, updated in ShadowMapManager::update(),
|
||||
// consumed in ShadowMapManager::render()
|
||||
struct TextureAtlasRequirements {
|
||||
uint16_t size = 0;
|
||||
uint8_t layers = 0;
|
||||
uint8_t levels = 0;
|
||||
} mTextureRequirements;
|
||||
} mTextureAtlasRequirements;
|
||||
|
||||
ShadowTechnique updateCascadeShadowMaps(FEngine& engine,
|
||||
FView& view, FScene::RenderableSoa& renderableData, FScene::LightSoa& lightData,
|
||||
@@ -200,7 +202,6 @@ private:
|
||||
// TODO: make it an option.
|
||||
// TODO: iOS does not support the DEPTH16 texture format.
|
||||
backend::TextureFormat mTextureFormat = backend::TextureFormat::DEPTH16;
|
||||
float mTextureZResolution = 1.0f / (1u << 16u);
|
||||
|
||||
ShadowMappingUniforms mShadowMappingUniforms;
|
||||
|
||||
|
||||
@@ -316,21 +316,21 @@ void FView::prepareShadowing(FEngine& engine, DriverApi& driver,
|
||||
// when we get here all the lights should be visible
|
||||
assert_invariant(lightData.elementAt<FScene::VISIBILITY>(l));
|
||||
|
||||
FLightManager::Instance light = lightData.elementAt<FScene::LIGHT_INSTANCE>(l);
|
||||
FLightManager::Instance li = lightData.elementAt<FScene::LIGHT_INSTANCE>(l);
|
||||
|
||||
if (UTILS_LIKELY(!light)) {
|
||||
if (UTILS_LIKELY(!li)) {
|
||||
continue; // invalid instance
|
||||
}
|
||||
|
||||
if (UTILS_LIKELY(!lcm.isShadowCaster(light))) {
|
||||
if (UTILS_LIKELY(!lcm.isShadowCaster(li))) {
|
||||
continue; // doesn't cast shadows
|
||||
}
|
||||
|
||||
if (UTILS_LIKELY(!lcm.isSpotLight(light))) {
|
||||
continue; // is not a spot-light (we're not supporting point-lights yet)
|
||||
if (UTILS_LIKELY(!lcm.isSpotLight(li))) {
|
||||
continue; // is not a spot-li (we're not supporting point-lights yet)
|
||||
}
|
||||
|
||||
const auto& shadowOptions = lcm.getShadowOptions(light);
|
||||
const auto& shadowOptions = lcm.getShadowOptions(li);
|
||||
mShadowMapManager.addSpotShadowMap(l, &shadowOptions);
|
||||
++shadowCastingSpotCount;
|
||||
if (shadowCastingSpotCount > CONFIG_MAX_SHADOW_CASTING_SPOTS - 1) {
|
||||
|
||||
@@ -106,7 +106,7 @@ public:
|
||||
VISIBILITY_STATE, // 1 | visibility data of the component
|
||||
SKINNING_BUFFER, // 8 | bones uniform buffer handle, count, offset
|
||||
WORLD_AABB_CENTER, // 12 | world-space bounding box center of the renderable
|
||||
VISIBLE_MASK, // 1 | each bit represents a visibility in a pass
|
||||
VISIBLE_MASK, // 2 | each bit represents a visibility in a pass
|
||||
MORPH_WEIGHTS, // 4 | floats for morphing
|
||||
CHANNELS, // 1 | currently light channels only
|
||||
|
||||
|
||||
@@ -1,12 +1,12 @@
|
||||
Pod::Spec.new do |spec|
|
||||
spec.name = "Filament"
|
||||
spec.version = "1.13.0"
|
||||
spec.version = "1.14.0"
|
||||
spec.license = { :type => "Apache 2.0", :file => "LICENSE" }
|
||||
spec.homepage = "https://google.github.io/filament"
|
||||
spec.authors = "Google LLC."
|
||||
spec.summary = "Filament is a real-time physically based rendering engine for Android, iOS, Windows, Linux, macOS, and WASM/WebGL."
|
||||
spec.platform = :ios, "11.0"
|
||||
spec.source = { :http => "https://github.com/google/filament/releases/download/v1.13.0/filament-v1.13.0-ios.tgz" }
|
||||
spec.source = { :http => "https://github.com/google/filament/releases/download/v1.14.0/filament-v1.14.0-ios.tgz" }
|
||||
|
||||
# Fix linking error with Xcode 12; we do not yet support the simulator on Apple silicon.
|
||||
spec.pod_target_xcconfig = {
|
||||
|
||||
@@ -27,7 +27,7 @@
|
||||
namespace filament {
|
||||
|
||||
// update this when a new version of filament wouldn't work with older materials
|
||||
static constexpr size_t MATERIAL_VERSION = 13;
|
||||
static constexpr size_t MATERIAL_VERSION = 14;
|
||||
|
||||
/**
|
||||
* Supported shading models
|
||||
|
||||
@@ -185,9 +185,14 @@ static_assert(sizeof(LightsUib) == 64, "the actual UBO is an array of 256 mat4")
|
||||
// UBO for punctual (spot light) shadows.
|
||||
struct ShadowUib {
|
||||
static constexpr utils::StaticString _name{ "ShadowUniforms" };
|
||||
math::mat4f spotLightFromWorldMatrix[CONFIG_MAX_SHADOW_CASTING_SPOTS];
|
||||
math::float4 directionShadowBias[CONFIG_MAX_SHADOW_CASTING_SPOTS]; // light direction, normal bias
|
||||
struct alignas(16) ShadowData {
|
||||
math::mat4f lightFromWorldMatrix;
|
||||
math::float3 direction;
|
||||
float normalBias;
|
||||
};
|
||||
ShadowData shadows[CONFIG_MAX_SHADOW_CASTING_SPOTS];
|
||||
};
|
||||
static_assert(sizeof(ShadowUib) <= 16384, "ShadowUib exceed max UBO size");
|
||||
|
||||
// UBO froxel record buffer.
|
||||
struct FroxelRecordUib {
|
||||
@@ -203,9 +208,8 @@ struct PerRenderableUibBone {
|
||||
math::float4 s = { 1, 1, 1, 0 };
|
||||
math::float4 ns = { 1, 1, 1, 0 };
|
||||
};
|
||||
|
||||
static_assert(CONFIG_MAX_BONE_COUNT * sizeof(PerRenderableUibBone) <= 16384,
|
||||
"Bones exceed max UBO size");
|
||||
"PerRenderableUibBone exceed max UBO size");
|
||||
|
||||
} // namespace filament
|
||||
|
||||
|
||||
@@ -63,23 +63,38 @@ public:
|
||||
Builder& add(utils::StaticString const& uniformName, size_t size,
|
||||
Type type, Precision precision = Precision::DEFAULT);
|
||||
|
||||
// Add a known struct
|
||||
Builder& add(utils::CString const& uniformName, size_t size,
|
||||
utils::CString const& structName, size_t stride);
|
||||
|
||||
template<size_t N>
|
||||
Builder& add(utils::StringLiteral<N> const& uniformName, size_t size,
|
||||
Type type, Precision precision = Precision::DEFAULT) {
|
||||
return add(utils::StaticString{ uniformName }, size, type, precision);
|
||||
}
|
||||
|
||||
template<size_t N0, size_t N1>
|
||||
Builder& add(utils::StringLiteral<N0> const& uniformName, size_t size,
|
||||
utils::StringLiteral<N1> const& structName, size_t stride) {
|
||||
return add(utils::StaticString{ uniformName }, size,
|
||||
utils::StaticString{ structName }, stride);
|
||||
}
|
||||
|
||||
// build and return the UniformInterfaceBlock
|
||||
UniformInterfaceBlock build();
|
||||
private:
|
||||
friend class UniformInterfaceBlock;
|
||||
struct Entry {
|
||||
Entry(utils::CString name, uint32_t size, Type type, Precision precision) noexcept
|
||||
: name(std::move(name)), size(size), type(type), precision(precision) { }
|
||||
: name(std::move(name)), size(size), type(type), precision(precision), stride(strideForType(type, 0)) { }
|
||||
Entry(utils::CString name, uint32_t size, utils::CString structName, size_t stride) noexcept
|
||||
: name(std::move(name)), size(size), type(Type::STRUCT), structName(std::move(structName)), stride(stride) { }
|
||||
utils::CString name;
|
||||
uint32_t size;
|
||||
Type type;
|
||||
Precision precision;
|
||||
Precision precision{};
|
||||
utils::CString structName{};
|
||||
uint32_t stride;
|
||||
};
|
||||
utils::CString mName;
|
||||
std::vector<Entry> mEntries;
|
||||
@@ -92,6 +107,7 @@ public:
|
||||
Type type; // type of this uniform
|
||||
uint32_t size; // size of the array in elements, or 1 if not an array
|
||||
Precision precision;// precision of this uniform
|
||||
utils::CString structName;// name of this uniform structure if type is STRUCT
|
||||
// returns offset in bytes of this uniform (at index if an array)
|
||||
inline size_t getBufferOffset(size_t index = 0) const {
|
||||
assert(index < size);
|
||||
@@ -125,7 +141,7 @@ private:
|
||||
explicit UniformInterfaceBlock(Builder const& builder) noexcept;
|
||||
|
||||
static uint8_t baseAlignmentForType(Type type) noexcept;
|
||||
static uint8_t strideForType(Type type) noexcept;
|
||||
static uint8_t strideForType(Type type, uint32_t stride) noexcept;
|
||||
|
||||
utils::CString mName;
|
||||
std::vector<UniformInfo> mUniformsInfoList;
|
||||
|
||||
@@ -18,8 +18,6 @@
|
||||
|
||||
#include <utils/Panic.h>
|
||||
#include <utils/compiler.h>
|
||||
#include <private/filament/UniformInterfaceBlock.h>
|
||||
|
||||
|
||||
using namespace utils;
|
||||
|
||||
@@ -64,6 +62,14 @@ UniformInterfaceBlock::Builder& UniformInterfaceBlock::Builder::add(
|
||||
return *this;
|
||||
}
|
||||
|
||||
UniformInterfaceBlock::Builder& UniformInterfaceBlock::Builder::add(
|
||||
utils::CString const& uniformName, size_t size,
|
||||
utils::CString const& structName, size_t stride) {
|
||||
mEntries.emplace_back(uniformName, (uint32_t)size, structName, stride);
|
||||
return *this;
|
||||
}
|
||||
|
||||
|
||||
UniformInterfaceBlock UniformInterfaceBlock::Builder::build() {
|
||||
return UniformInterfaceBlock(*this);
|
||||
}
|
||||
@@ -89,7 +95,7 @@ UniformInterfaceBlock::UniformInterfaceBlock(Builder const& builder) noexcept
|
||||
uint16_t offset = 0;
|
||||
for (auto const& e : builder.mEntries) {
|
||||
size_t alignment = baseAlignmentForType(e.type);
|
||||
uint8_t stride = strideForType(e.type);
|
||||
uint8_t stride = strideForType(e.type, e.stride);
|
||||
if (e.size > 1) { // this is an array
|
||||
// round the alignment up to that of a float4
|
||||
alignment = (alignment + 3) & ~3;
|
||||
@@ -101,7 +107,7 @@ UniformInterfaceBlock::UniformInterfaceBlock(Builder const& builder) noexcept
|
||||
offset += padding;
|
||||
|
||||
UniformInfo& info = uniformsInfoList[i];
|
||||
info = { e.name, offset, stride, e.type, e.size, e.precision };
|
||||
info = { e.name, offset, stride, e.type, e.size, e.precision, e.structName };
|
||||
|
||||
// record this uniform info
|
||||
infoMap[info.name.c_str()] = i;
|
||||
@@ -154,11 +160,12 @@ uint8_t UTILS_NOINLINE UniformInterfaceBlock::baseAlignmentForType(UniformInterf
|
||||
case Type::UINT4:
|
||||
case Type::MAT3:
|
||||
case Type::MAT4:
|
||||
case Type::STRUCT:
|
||||
return 4;
|
||||
}
|
||||
}
|
||||
|
||||
uint8_t UTILS_NOINLINE UniformInterfaceBlock::strideForType(UniformInterfaceBlock::Type type) noexcept {
|
||||
uint8_t UTILS_NOINLINE UniformInterfaceBlock::strideForType(UniformInterfaceBlock::Type type, uint32_t stride) noexcept {
|
||||
switch (type) {
|
||||
case Type::BOOL:
|
||||
case Type::INT:
|
||||
@@ -184,6 +191,8 @@ uint8_t UTILS_NOINLINE UniformInterfaceBlock::strideForType(UniformInterfaceBloc
|
||||
return 12;
|
||||
case Type::MAT4:
|
||||
return 16;
|
||||
case Type::STRUCT:
|
||||
return stride;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -145,8 +145,7 @@ UniformInterfaceBlock const& UibGenerator::getLightsUib() noexcept {
|
||||
UniformInterfaceBlock const& UibGenerator::getShadowUib() noexcept {
|
||||
static UniformInterfaceBlock uib = UniformInterfaceBlock::Builder()
|
||||
.name(ShadowUib::_name)
|
||||
.add("spotLightFromWorldMatrix", CONFIG_MAX_SHADOW_CASTING_SPOTS, UniformInterfaceBlock::Type::MAT4, Precision::HIGH)
|
||||
.add("directionShadowBias", CONFIG_MAX_SHADOW_CASTING_SPOTS, UniformInterfaceBlock::Type::FLOAT4, Precision::HIGH)
|
||||
.add("shadows", CONFIG_MAX_SHADOW_CASTING_SPOTS, "ShadowData", sizeof(ShadowUib::ShadowData))
|
||||
.build();
|
||||
return uib;
|
||||
}
|
||||
|
||||
@@ -326,7 +326,7 @@ io::sstream& CodeGenerator::generateUniforms(io::sstream& out, ShaderType shader
|
||||
}
|
||||
out << "std140) uniform " << blockName.c_str() << " {\n";
|
||||
for (auto const& info : infos) {
|
||||
char const* const type = getUniformTypeName(info.type);
|
||||
char const* const type = getUniformTypeName(info);
|
||||
char const* const precision = getUniformPrecisionQualifier(info.type, info.precision,
|
||||
uniformPrecision, defaultPrecision);
|
||||
out << " " << precision;
|
||||
@@ -673,9 +673,9 @@ char const* CodeGenerator::getConstantName(MaterialBuilder::Property property) n
|
||||
}
|
||||
}
|
||||
|
||||
char const* CodeGenerator::getUniformTypeName(UniformInterfaceBlock::Type type) noexcept {
|
||||
char const* CodeGenerator::getUniformTypeName(UniformInterfaceBlock::UniformInfo const& info) noexcept {
|
||||
using Type = UniformInterfaceBlock::Type;
|
||||
switch (type) {
|
||||
switch (info.type) {
|
||||
case Type::BOOL: return "bool";
|
||||
case Type::BOOL2: return "bvec2";
|
||||
case Type::BOOL3: return "bvec3";
|
||||
@@ -694,6 +694,7 @@ char const* CodeGenerator::getUniformTypeName(UniformInterfaceBlock::Type type)
|
||||
case Type::UINT4: return "uvec4";
|
||||
case Type::MAT3: return "mat3";
|
||||
case Type::MAT4: return "mat4";
|
||||
case Type::STRUCT: return info.structName.c_str();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -777,6 +778,7 @@ bool CodeGenerator::hasPrecision(UniformInterfaceBlock::Type type) noexcept {
|
||||
case UniformType::BOOL2:
|
||||
case UniformType::BOOL3:
|
||||
case UniformType::BOOL4:
|
||||
case UniformType::STRUCT:
|
||||
return false;
|
||||
default:
|
||||
return true;
|
||||
|
||||
@@ -154,7 +154,7 @@ private:
|
||||
TargetLanguage mTargetLanguage;
|
||||
|
||||
// return type name of uniform (e.g.: "vec3", "vec4", "float")
|
||||
static char const* getUniformTypeName(filament::UniformInterfaceBlock::Type uniformType) noexcept;
|
||||
static char const* getUniformTypeName(filament::UniformInterfaceBlock::UniformInfo const& info) noexcept;
|
||||
|
||||
// return type name of output (e.g.: "vec3", "vec4", "float")
|
||||
static char const* getOutputTypeName(MaterialBuilder::OutputType type) noexcept;
|
||||
|
||||
@@ -280,7 +280,7 @@ static float UTILS_UNUSED VisibilityAshikhmin(float NoV, float NoL, float /*a*/)
|
||||
* N h
|
||||
*
|
||||
* N 4
|
||||
* Er() = ------------- --- ∑ V(v) <n•l>
|
||||
* Er() = ------------- --- ∑ L(v) <n•l>
|
||||
* 4 ∑ <n•l> N
|
||||
*
|
||||
*
|
||||
@@ -541,7 +541,7 @@ void CubemapIBL::roughnessFilter(
|
||||
* N l n•l
|
||||
*
|
||||
*
|
||||
* To avoid to multiply by 1/PI in the shader, we do it here, which simplifies to:
|
||||
* To avoid multiplying by 1/PI in the shader, we do it here, which simplifies to:
|
||||
*
|
||||
* +----------------------+
|
||||
* | 1 |
|
||||
|
||||
@@ -182,6 +182,7 @@ const char* toString(backend::UniformType type) noexcept {
|
||||
case backend::UniformType::UINT4: return "uint4";
|
||||
case backend::UniformType::MAT3: return "float3x3";
|
||||
case backend::UniformType::MAT4: return "float4x4";
|
||||
case backend::UniformType::STRUCT: return "struct";
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -102,7 +102,7 @@ float acosFastPositive(float x) {
|
||||
*
|
||||
* @public-api
|
||||
*/
|
||||
vec4 mulMat4x4Float3(const highp mat4 m, const highp vec3 v) {
|
||||
highp vec4 mulMat4x4Float3(const highp mat4 m, const highp vec3 v) {
|
||||
return v.x * m[0] + (v.y * m[1] + (v.z * m[2] + m[3]));
|
||||
}
|
||||
|
||||
@@ -112,7 +112,7 @@ vec4 mulMat4x4Float3(const highp mat4 m, const highp vec3 v) {
|
||||
*
|
||||
* @public-api
|
||||
*/
|
||||
vec3 mulMat3x3Float3(const highp mat4 m, const highp vec3 v) {
|
||||
highp vec3 mulMat3x3Float3(const highp mat4 m, const highp vec3 v) {
|
||||
return v.x * m[0].xyz + (v.y * m[1].xyz + (v.z * m[2].xyz));
|
||||
}
|
||||
|
||||
|
||||
@@ -8,18 +8,37 @@
|
||||
* The returned point may contain a bias to attempt to eliminate common
|
||||
* shadowing artifacts such as "acne". To achieve this, the world space
|
||||
* normal at the point must also be passed to this function.
|
||||
* Normal bias is not used for VSM.
|
||||
*/
|
||||
highp vec4 computeLightSpacePosition(const highp vec3 p, const highp vec3 n, const highp vec3 l,
|
||||
const float b, const highp mat4 lightFromWorldMatrix) {
|
||||
|
||||
#if defined(HAS_VSM)
|
||||
// VSM don't apply the shadow bias
|
||||
highp vec4 lightSpacePosition = (lightFromWorldMatrix * vec4(p, 1.0));
|
||||
#else
|
||||
float NoL = saturate(dot(n, l));
|
||||
float sinTheta = sqrt(1.0 - NoL * NoL);
|
||||
highp vec3 offsetPosition = p + n * (sinTheta * b);
|
||||
highp vec4 lightSpacePosition = (lightFromWorldMatrix * vec4(offsetPosition, 1.0));
|
||||
#endif
|
||||
return lightSpacePosition;
|
||||
#if defined(HAS_DIRECTIONAL_LIGHTING)
|
||||
highp vec4 computeLightSpacePositionDirectional(const highp vec3 p, const highp vec3 n,
|
||||
const highp vec3 l, const float b, const highp mat4 lightFromWorldMatrix) {
|
||||
return mulMat4x4Float3(lightFromWorldMatrix, p);
|
||||
}
|
||||
#endif
|
||||
#endif // HAS_DIRECTIONAL_LIGHTING
|
||||
#if defined(HAS_DYNAMIC_LIGHTING)
|
||||
highp vec4 computeLightSpacePositionSpot(const highp vec3 p, const highp vec3 n,
|
||||
const highp vec3 l, const float b, const highp mat4 lightFromWorldMatrix) {
|
||||
return mulMat4x4Float3(lightFromWorldMatrix, p);
|
||||
}
|
||||
#endif // HAS_DYNAMIC_LIGHTING
|
||||
#else // HAS_VSM
|
||||
highp vec4 computeLightSpacePositionDirectional(const highp vec3 p, const highp vec3 n,
|
||||
const highp vec3 l, const float b, const highp mat4 lightFromWorldMatrix) {
|
||||
float NoL = saturate(dot(n, l));
|
||||
highp float sinTheta = sqrt(1.0 - NoL * NoL);
|
||||
highp vec3 offsetPosition = p + n * (sinTheta * b);
|
||||
return mulMat4x4Float3(lightFromWorldMatrix, offsetPosition);
|
||||
}
|
||||
#if defined(HAS_DYNAMIC_LIGHTING)
|
||||
highp vec4 computeLightSpacePositionSpot(const highp vec3 p, const highp vec3 n,
|
||||
const highp vec3 l, const float b, const highp mat4 lightFromWorldMatrix) {
|
||||
highp vec4 positionLs = mulMat4x4Float3(lightFromWorldMatrix, p);
|
||||
highp float oneOverZ = positionLs.w / positionLs.z;
|
||||
return computeLightSpacePositionDirectional(p, n, l, b * oneOverZ, lightFromWorldMatrix);
|
||||
}
|
||||
#endif // HAS_DIRECTIONAL_LIGHTING
|
||||
#endif // HAS_VSM
|
||||
#endif // HAS_SHADOWING
|
||||
|
||||
@@ -22,3 +22,11 @@
|
||||
|
||||
#define float3x3 mat3
|
||||
#define float4x4 mat4
|
||||
|
||||
// Adreno drivers seem to ignore precision qualifiers in structs, unless they're used in
|
||||
// UBOs, which is is the case here.
|
||||
struct ShadowData {
|
||||
highp mat4 lightFromWorldMatrix;
|
||||
highp vec3 direction;
|
||||
float normalBias;
|
||||
};
|
||||
|
||||
@@ -100,10 +100,10 @@ highp vec3 getNormalizedViewportCoord2() {
|
||||
|
||||
#if defined(HAS_SHADOWING) && defined(HAS_DYNAMIC_LIGHTING)
|
||||
highp vec4 getSpotLightSpacePosition(uint index) {
|
||||
vec3 dir = shadowUniforms.directionShadowBias[index].xyz;
|
||||
float bias = shadowUniforms.directionShadowBias[index].w;
|
||||
return computeLightSpacePosition(vertex_worldPosition.xyz,
|
||||
vertex_worldNormal, dir, bias, shadowUniforms.spotLightFromWorldMatrix[index]);
|
||||
highp vec3 dir = shadowUniforms.shadows[index].direction;
|
||||
float bias = shadowUniforms.shadows[index].normalBias;
|
||||
return computeLightSpacePositionSpot(vertex_worldPosition.xyz,
|
||||
vertex_worldNormal, dir, bias, shadowUniforms.shadows[index].lightFromWorldMatrix);
|
||||
}
|
||||
#endif
|
||||
|
||||
@@ -128,13 +128,13 @@ uint getShadowCascade() {
|
||||
highp vec4 getCascadeLightSpacePosition(uint cascade) {
|
||||
// For the first cascade, return the interpolated light space position.
|
||||
// This branch will be coherent (mostly) for neighboring fragments, and it's worth avoiding
|
||||
// the matrix multiply inside computeLightSpacePosition.
|
||||
// the matrix multiply inside computeLightSpacePositionDirectional.
|
||||
if (cascade == 0u) {
|
||||
// Note: this branch may cause issues with derivatives
|
||||
return vertex_lightSpacePosition;
|
||||
}
|
||||
|
||||
return computeLightSpacePosition(getWorldPosition(), getWorldNormalVector(),
|
||||
return computeLightSpacePositionDirectional(getWorldPosition(), getWorldNormalVector(),
|
||||
frameUniforms.lightDirection, frameUniforms.shadowBias.y,
|
||||
frameUniforms.lightFromWorldMatrix[cascade]);
|
||||
}
|
||||
|
||||
@@ -89,7 +89,8 @@ void main() {
|
||||
#endif
|
||||
|
||||
#if defined(HAS_SHADOWING) && defined(HAS_DIRECTIONAL_LIGHTING)
|
||||
vertex_lightSpacePosition = computeLightSpacePosition(vertex_worldPosition.xyz, vertex_worldNormal,
|
||||
vertex_lightSpacePosition = computeLightSpacePositionDirectional(
|
||||
vertex_worldPosition.xyz, vertex_worldNormal,
|
||||
frameUniforms.lightDirection, frameUniforms.shadowBias.y, getLightFromWorldMatrix());
|
||||
#endif
|
||||
|
||||
|
||||
@@ -2,16 +2,12 @@
|
||||
// Shadowing configuration
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
#define SHADOW_SAMPLING_PCF_HARD 0
|
||||
#define SHADOW_SAMPLING_PCF_LOW 1
|
||||
|
||||
#define SHADOW_SAMPLING_ERROR_DISABLED 0
|
||||
#define SHADOW_SAMPLING_ERROR_ENABLED 1
|
||||
|
||||
#define SHADOW_RECEIVER_PLANE_DEPTH_BIAS_DISABLED 0
|
||||
#define SHADOW_RECEIVER_PLANE_DEPTH_BIAS_ENABLED 1
|
||||
|
||||
#define SHADOW_SAMPLING_METHOD SHADOW_SAMPLING_PCF_HARD
|
||||
#define SHADOW_SAMPLING_ERROR SHADOW_SAMPLING_ERROR_DISABLED
|
||||
#define SHADOW_RECEIVER_PLANE_DEPTH_BIAS SHADOW_RECEIVER_PLANE_DEPTH_BIAS_DISABLED
|
||||
|
||||
@@ -57,52 +53,38 @@ float sampleDepth(const mediump sampler2DArrayShadow map, const uint layer,
|
||||
return texture(map, vec4(base + dudv, layer, saturate(depth)));
|
||||
}
|
||||
|
||||
#if SHADOW_SAMPLING_METHOD == SHADOW_SAMPLING_PCF_HARD
|
||||
float ShadowSample_Hard(const mediump sampler2DArrayShadow map, const uint layer,
|
||||
const highp vec2 size, const highp vec3 position) {
|
||||
float ShadowSample_PCF(const mediump sampler2DArrayShadow map,
|
||||
const uint layer, const highp vec3 position) {
|
||||
highp vec2 size = vec2(textureSize(map, 0));
|
||||
highp vec2 texelSize = vec2(1.0) / size;
|
||||
vec2 rpdb = computeReceiverPlaneDepthBias(position);
|
||||
float depth = samplingBias(position.z, rpdb, texelSize);
|
||||
// use hardware assisted PCF
|
||||
return sampleDepth(map,layer, position.xy, vec2(0.0f), depth, rpdb);
|
||||
}
|
||||
#endif
|
||||
|
||||
#if SHADOW_SAMPLING_METHOD == SHADOW_SAMPLING_PCF_LOW
|
||||
float ShadowSample_PCF_Low(const mediump sampler2DArrayShadow map, const uint layer,
|
||||
const highp vec2 size, highp vec3 position) {
|
||||
// Castaño, 2013, "Shadow Mapping Summary Part 1"
|
||||
float ShadowSample_PCF(const mediump sampler2DArray shadowMap,
|
||||
const uint layer, const highp vec3 position) {
|
||||
highp vec2 size = vec2(textureSize(shadowMap, 0));
|
||||
highp vec2 texelSize = vec2(1.0) / size;
|
||||
vec2 rpdb = computeReceiverPlaneDepthBias(position);
|
||||
float depth = samplingBias(position.z, rpdb, texelSize);
|
||||
|
||||
// clamp position to avoid overflows below, which cause some GPUs to abort
|
||||
position.xy = clamp(position.xy, vec2(-1.0), vec2(2.0));
|
||||
|
||||
vec2 offset = vec2(0.5);
|
||||
highp vec2 uv = (position.xy * size) + offset;
|
||||
highp vec2 base = (floor(uv) - offset) * texelSize;
|
||||
highp vec2 st = fract(uv);
|
||||
|
||||
vec2 uw = vec2(3.0 - 2.0 * st.x, 1.0 + 2.0 * st.x);
|
||||
vec2 vw = vec2(3.0 - 2.0 * st.y, 1.0 + 2.0 * st.y);
|
||||
|
||||
highp vec2 u = vec2((2.0 - st.x) / uw.x - 1.0, st.x / uw.y + 1.0);
|
||||
highp vec2 v = vec2((2.0 - st.y) / vw.x - 1.0, st.y / vw.y + 1.0);
|
||||
|
||||
u *= texelSize.x;
|
||||
v *= texelSize.y;
|
||||
|
||||
float sum = 0.0;
|
||||
|
||||
sum += uw.x * vw.x * sampleDepth(map, layer, base, vec2(u.x, v.x), depth, rpdb);
|
||||
sum += uw.y * vw.x * sampleDepth(map, layer, base, vec2(u.y, v.x), depth, rpdb);
|
||||
|
||||
sum += uw.x * vw.y * sampleDepth(map, layer, base, vec2(u.x, v.y), depth, rpdb);
|
||||
sum += uw.y * vw.y * sampleDepth(map, layer, base, vec2(u.y, v.y), depth, rpdb);
|
||||
|
||||
return sum * (1.0 / 16.0);
|
||||
}
|
||||
// use manual PCF
|
||||
highp vec2 st = position.xy * size - 0.5;
|
||||
vec4 d;
|
||||
#if defined(FILAMENT_HAS_FEATURE_TEXTURE_GATHER)
|
||||
d = textureGather(shadowMap, vec3(position.xy, layer), 0); // 01, 11, 10, 00
|
||||
#else
|
||||
highp ivec3 tc = ivec3(st, layer);
|
||||
d[0] = texelFetchOffset(shadowMap, tc, 0, ivec2(0, 1)).r;
|
||||
d[1] = texelFetchOffset(shadowMap, tc, 0, ivec2(1, 1)).r;
|
||||
d[2] = texelFetchOffset(shadowMap, tc, 0, ivec2(1, 0)).r;
|
||||
d[3] = texelFetchOffset(shadowMap, tc, 0, ivec2(0, 0)).r;
|
||||
#endif
|
||||
vec4 pcf = step(0.0, d - vec4(depth));
|
||||
highp vec2 grad = fract(st);
|
||||
return mix(mix(pcf.w, pcf.z, grad.x), mix(pcf.x, pcf.y, grad.x), grad.y);
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
// Screen-space Contact Shadows
|
||||
@@ -208,6 +190,22 @@ float chebyshevUpperBound(const highp vec2 moments, const highp float mean,
|
||||
return mean <= moments.x ? 1.0 : pMax;
|
||||
}
|
||||
|
||||
float ShadowSample_VSM(const mediump sampler2DArray shadowMap,
|
||||
const uint layer, const highp vec3 position) {
|
||||
// Read the shadow map with all available filtering
|
||||
highp vec2 moments = texture(shadowMap, vec3(position.xy, layer)).xy;
|
||||
highp float depth = position.z;
|
||||
|
||||
// EVSM depth warping
|
||||
depth = depth * 2.0 - 1.0;
|
||||
depth = exp(frameUniforms.vsmExponent * depth);
|
||||
|
||||
highp float depthScale = frameUniforms.vsmDepthScale * depth;
|
||||
highp float minVariance = depthScale * depthScale;
|
||||
float lightBleedReduction = frameUniforms.vsmLightBleedReduction;
|
||||
return chebyshevUpperBound(moments, depth, minVariance, lightBleedReduction);
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
// Shadow sampling dispatch
|
||||
//------------------------------------------------------------------------------
|
||||
@@ -222,35 +220,16 @@ float chebyshevUpperBound(const highp vec2 moments, const highp float mean,
|
||||
float shadow(const mediump sampler2DArrayShadow shadowMap,
|
||||
const uint layer, const highp vec4 shadowPosition) {
|
||||
highp vec3 position = shadowPosition.xyz * (1.0 / shadowPosition.w);
|
||||
highp vec2 size = vec2(textureSize(shadowMap, 0));
|
||||
// note: shadowPosition.z is in the [1, 0] range (reversed Z)
|
||||
#if SHADOW_SAMPLING_METHOD == SHADOW_SAMPLING_PCF_HARD
|
||||
return ShadowSample_Hard(shadowMap, layer, size, position);
|
||||
#elif SHADOW_SAMPLING_METHOD == SHADOW_SAMPLING_PCF_LOW
|
||||
return ShadowSample_PCF_Low(shadowMap, layer, size, position);
|
||||
#endif
|
||||
return ShadowSample_PCF(shadowMap, layer, position);
|
||||
}
|
||||
|
||||
// VSM or DPCF sampling
|
||||
// VSM sampling
|
||||
float shadow(const mediump sampler2DArray shadowMap,
|
||||
const uint layer, const highp vec4 shadowPosition) {
|
||||
|
||||
// note: shadowPosition.z is in linear light space normalized to [0, 1]
|
||||
// note: shadowPosition.z is in linear light-space normalized to [0, 1]
|
||||
// see: ShadowMap::computeVsmLightSpaceMatrix() in ShadowMap.cpp
|
||||
// see: computeLightSpacePosition() in common_shadowing.fs
|
||||
|
||||
highp vec3 position = vec3(shadowPosition.xy * (1.0 / shadowPosition.w), shadowPosition.z);
|
||||
|
||||
// Read the shadow map with all available filtering
|
||||
highp vec2 moments = texture(shadowMap, vec3(position.xy, layer)).xy;
|
||||
highp float depth = position.z;
|
||||
|
||||
// EVSM depth warping
|
||||
depth = depth * 2.0 - 1.0;
|
||||
depth = exp(frameUniforms.vsmExponent * depth);
|
||||
|
||||
highp float depthScale = frameUniforms.vsmDepthScale * depth;
|
||||
highp float minVariance = depthScale * depthScale;
|
||||
float lightBleedReduction = frameUniforms.vsmLightBleedReduction;
|
||||
return chebyshevUpperBound(moments, depth, minVariance, lightBleedReduction);
|
||||
return ShadowSample_VSM(shadowMap, layer, position);
|
||||
}
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "filament",
|
||||
"version": "1.13.0",
|
||||
"version": "1.14.0",
|
||||
"description": "Real-time physically based rendering engine",
|
||||
"main": "filament.js",
|
||||
"module": "filament.js",
|
||||
|
||||
Reference in New Issue
Block a user