Compare commits

..

30 Commits

Author SHA1 Message Date
Benjamin Doherty
44125926d1 Disable configuration-cache 2021-11-08 17:05:34 -08:00
Benjamin Doherty
fbfd5ec0ec Merge branch 'rc/1.13.0' into release 2021-11-08 11:50:19 -08:00
Ben Doherty
a74a95cc65 Call VirtualMachineEnv::JNI_OnLoad for non-Android Java builds (better fix) (#4779) 2021-11-04 13:28:17 -07:00
Benjamin Doherty
bc0ea16ff0 Update RELEASE_NOTES for 1.13.0 2021-11-02 13:15:53 -07:00
Mathias Agopian
b2dc8aa84c Fix typo that broke the directional shadowmap 2021-11-02 13:11:44 -07:00
Benjamin Doherty
9987e8b6ab Bump version to 1.13.0 2021-11-01 14:59:11 -07:00
Benjamin Doherty
0d9bdcc008 Merge branch 'rc/1.12.11' into release 2021-11-01 14:55:58 -07:00
Benjamin Doherty
81fa33abeb Release Filament 1.12.11 2021-11-01 14:55:54 -07:00
Mathias Agopian
af7f87e19b make sure the near plane for spot shadows is not negative 2021-11-01 14:19:13 -07:00
Mathias Agopian
3073b03d56 Improve computation of spotlight near/far plane
Near/far plane is now computed dynamically, this significantly
improves the shadowmap depth resolution.

Quite a bit of refactoring was necessary to get all the right data 
in the right place, in particular SceneInfo is now passed around for
both directional and spot shadows.
2021-10-29 18:57:26 -07:00
Mathias Agopian
23dab0a013 Fix very narrow spotlight lighting and other minor issues
The froxel code was failing when a spotlight cone was too narrow. This
is fixed here by never using a cone smaller than 0.5 degrees during
forxelization. 

Additionally we now silently clamp the cone angles to 0.5 degrees at
the API level because, the falloff was also failing due to floating
point precision in the shader.

Finally, we clamp the inner cone upper value to the outer cone's
instead of the other way around. i.e. the outer cone defines the 
spotlight while the inner cone just controls the falloff.
2021-10-29 15:08:05 -07:00
Mathias Agopian
fd68144ba2 fix depth precision with VSM
When calculating the linear depth for VSM, we were using the whole
range between 0 and the far plane, the near plane wasn't taken into
account.

This can be a problem is the light is very far, but it's near plane is
closer to the camera/scene, in this case the depth precision wasn't
used optimally.

Note that we don't hit this problem currently, because the directional
light is constructed such that its origin is at the near plane, and the
spotlights have a fixed near plane (which is a problem and will be 
fixed at a later time).
2021-10-29 12:03:41 -07:00
Mathias Agopian
51e92cd142 Fix spotlight direction and falloff
Direction and falloff were recently changed to fp16 int the shader,
which is not enough (far from it) when a spotlight is over 100m away.
2021-10-28 21:59:01 -07:00
Mathias Agopian
50f33f7196 fix some warnings and typos 2021-10-28 21:58:45 -07:00
Benjamin Doherty
b5c634045e Update RELEASE_NOTES for 1.12.11 2021-10-28 16:13:50 -07:00
Ben Doherty
1f05531d53 Call VirtualMachineEnv::JNI_OnLoad for non-Android Java builds (better fix) (#4779) 2021-10-28 16:02:23 -07:00
Benjamin Doherty
88f382f0e3 Revert "refactor colorgrading materials"
This reverts commit fb86a77cf8.
2021-10-28 15:56:50 -07:00
Benjamin Doherty
3e59925900 Remove problematic configuration-cache setting for release build 2021-10-28 15:56:26 -07:00
Mathias Agopian
1182d30fb9 MSAA VSMs seem to now work on Qualcomm h/w
The bug that prevented them to work has been worked around, but we 
don't know when or how. The original bug still exists as demonstrated
by our standalone test apk.

For now, we reenable MSAA VSM on Adreno.
2021-10-26 16:06:20 -07:00
Romain Guy
095179eeb3 Enabled Gradle configuration caching and rename properties (#4769)
* Fix configurationg caching problems

* Enable Gradle configuration caching

* Update BUILDING.md

* Update RELEASE_NOTES.md

Co-authored-by: Mathias Agopian <mathias@google.com>

Co-authored-by: Benjamin Doherty <bendoherty@google.com>
2021-10-26 12:47:35 -07:00
Romain Guy
69b48eaadb Fix erroenous warning 2021-10-26 12:05:49 -07:00
Timo Röhling
4031c6f0d3 Simplify devendoring (#4765)
* Perform sanity check when combining static libraries

This is a small modification that simplifies Debian packaging when one
or more vendored dependencies are replaced by system shared libraries
and no longer need to be combined with a Filament library.

* Verify vendoring before including license texts

This is another small modification to simplify Debian packaging when one
or more vendored dependencies are removed and replaced by system
libraries, so they no longer need to be considered when compiling the
license texts.
2021-10-26 11:31:35 -07:00
Mathias Agopian
7a22f7dfc5 move some VSM computations into the vertex shader 2021-10-26 11:26:21 -07:00
Mathias Agopian
728ebf5023 cleanup shadowing shader code
- remove PCF MEDIUM and HIGH quality -- these were never used in
practice, and "quality" really meant "blurriness", now we have VSM
for doing that with much more control and more efficiently.

PCF_HARD is now the default. PCF_LOW is still available at compile time
for now. It shouldn't be needed though because VSM can achieve the same.

Moved the divide-by-w of light space into the shadow() method -- which
known if it should be done or not.
2021-10-26 11:26:21 -07:00
Timo Röhling
62476d2f06 Fix compatibility with libstdc++ (#4763)
The codebase needs only a few additional standard #includes to compile
against libstdc++ on Linux; presumably those headers are implicitly
included with MSVC's and Clang's standard C++ library, but even if
libstdc++ compatibility is not a goal, it is advisable to have them
included directly.
2021-10-26 11:07:39 -07:00
Benjamin Doherty
602a550d93 Bump version to 1.12.11 2021-10-25 12:30:37 -07:00
Benjamin Doherty
5fea428243 Release Filament 1.12.10 2021-10-25 11:06:12 -07:00
clayly
9a3c9ccbf3 android-samples-gradle-plugin-id (#4740) 2021-10-24 11:12:15 -07:00
Ben Doherty
ef4dfcecd6 Metal: support framebuffer fetch on M1 devices (#4735) 2021-10-21 11:27:05 -07:00
Ben Doherty
5943382d23 iOS: avoid precision issues with CACurrentMediaTime (#4753) 2021-10-21 11:13:51 -07:00
62 changed files with 687 additions and 658 deletions

View File

@@ -291,11 +291,11 @@ Alternatively you can build the AAR from the command line by executing the follo
`android/` directory:
```
$ ./gradlew -Pfilament_dist_dir=../../out/android-release/filament assembleRelease
$ ./gradlew -Pcom.google.android.filament.dist-dir=../../out/android-release/filament assembleRelease
```
The `-Pfilament_dist_dir` can be used to specify a different installation directory (it must match
the CMake install prefix used in the previous steps).
The `-Pcom.google.android.filament.dist-dir` can be used to specify a different installation
directory (it must match the CMake install prefix used in the previous steps).
#### Using Filament's AAR

View File

@@ -502,11 +502,15 @@ function(list_licenses OUTPUT MODULES)
foreach(module ${_MODULES})
set(license_path "../../third_party/${module}/LICENSE")
get_filename_component(fullname "${license_path}" ABSOLUTE)
string(APPEND CONTENT "${STR_OPENER}License and copyrights for ${module}:\n${STR_CLOSER},\n")
file(READ ${license_path} license_long)
string(REPLACE "\n" "${STR_CLOSER},\n${STR_OPENER}" license ${license_long})
string(APPEND CONTENT ${STR_OPENER}${license}\n${STR_CLOSER},)
string(APPEND CONTENT "\n\n")
if(EXISTS ${fullname})
string(APPEND CONTENT "${STR_OPENER}License and copyrights for ${module}:\n${STR_CLOSER},\n")
file(READ ${license_path} license_long)
string(REPLACE "\n" "${STR_CLOSER},\n${STR_OPENER}" license ${license_long})
string(APPEND CONTENT ${STR_OPENER}${license}\n${STR_CLOSER},)
string(APPEND CONTENT "\n\n")
else()
message(AUTHOR_WARNING "${license_path} not found. You can ignore this warning if you have devendored ${module}.")
endif()
endforeach()
configure_file(${FILAMENT}/build/licenses.inc.in ${OUTPUT})
endfunction(list_licenses)
@@ -522,7 +526,12 @@ function(combine_static_libs TARGET OUTPUT DEPS)
# Loop through the dependent libraries and query their location on disk.
set(DEPS_FILES )
foreach(DEPENDENCY ${DEPS})
list(APPEND DEPS_FILES "$<TARGET_FILE:${DEPENDENCY}>")
if(TARGET ${DEPENDENCY})
get_property(dep_type TARGET ${DEPENDENCY} PROPERTY TYPE)
if(dep_type STREQUAL "STATIC_LIBRARY")
list(APPEND DEPS_FILES "$<TARGET_FILE:${DEPENDENCY}>")
endif()
endif()
endforeach()
add_custom_command(

View File

@@ -31,7 +31,7 @@ repositories {
}
dependencies {
implementation 'com.google.android.filament:filament-android:1.12.10'
implementation 'com.google.android.filament:filament-android:1.13.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.12.10'
pod 'Filament', '~> 1.13.0'
```
### Snapshots

View File

@@ -3,7 +3,23 @@
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.12.11 (currently main branch)
## v1.13.1 (currently main branch)
## v1.13.0
- Android: Gradle configuration caching is now enabled.
- Android: Filament's Gradle properties have all been renamed to `com.google.android.filament.xxx`
where `xxx` is the property name. See `android/build.gradle` for a complete list [⚠️]
- Android: The Gradle property `filament_tools_dir` (now called
`com.google.android.filament.tools-dir`) does not have a default value anymore. Please specify one
in your `gradle.properties` if you reuse the Gradle plugin in your projects [⚠️]
- engine: Fix spotlights direction and falloff [⚠️ **Material breakage**].
- engine: Improvements to VSM and spotlight shadows.
## v1.12.11
- Metal: Color grading performance improvement on M1 devices.
- samples: Fix glitchy animation seen in gltf-viewer iOS sample.
## v1.12.10

View File

@@ -122,20 +122,20 @@ The Gradle project used to generate the AAR is located at `<filament>\android`.
```
cd android
gradlew -Pfilament_dist_dir=..\out\android-release\filament assembleRelease
gradlew -Pcom.google.android.filament.dist-dir=..\out\android-release\filament assembleRelease
copy filament-android\build\outputs\aar\filament-android-release.aar ..\..\out\
```
If you're only interested in building for a single ABI, you'll need to pass a `filament_abis` parameter:
If you're only interested in building for a single ABI, you'll need to pass a `com.google.android.filament.abis` parameter:
```
gradlew -Pfilament_dist_dir=..\out\android-release\filament assembleRelease -Pfilament_abis=x86
gradlew -Pcom.google.android.filament.dist-dir=..\out\android-release\filament assembleRelease -Pcom.google.android.filament.abis=x86
```
If you're only interested in building SDK, you may skip samples build by passing a `filament_skip_samples` flag:
If you're only interested in building SDK, you may skip samples build by passing a `com.google.android.filament.skip-samples` flag:
```
gradlew -Pfilament_dist_dir=..\out\android-release\filament assembleRelease -Pfilament_skip_samples
gradlew -Pcom.google.android.filament.dist-dir=..\out\android-release\filament assembleRelease -Pfilament_skip_samples
```

View File

@@ -1,26 +1,28 @@
// This script accepts the following parameters:
//
// filament_dist_dir
// com.google.android.filament.dist-dir
// Path to the Filament distribution/install directory for Android
// (produced by make/ninja install). This directory must contain lib/arm64-v8a/ etc.
//
// filament_tools_dir
// com.google.android.filament.tools-dir
// Path to the Filament distribution/install directory for desktop.
// This directory must contain bin/matc.
//
// filament_exclude_vulkan
// com.google.android.filament.exclude-vulkan
// When set, support for Vulkan will be excluded.
//
// filament_skip_samples
// com.google.android.filament.skip-samples
// Exclude samples from the project. Useful to speed up compilation.
//
// filament_abis
// com.google.android.filament.abis
// List of supported ABIs to build as a comma separated list. Available options are:
// arm64-v8a, armeabi-v7a, x86_64, x86, all
// Defaults to all.
//
// Example:
// ./gradlew -Pfilament_dist_dir=../dist-android-release assembleRelease -Pfilament_abis=x86
// ./gradlew -Pcom.google.android.filament.dist-dir=../dist-android-release \
// -Pcom.google.android.filament.abis=x86 \
// assembleRelease
// Publishing to Maven Central:
// - Build and upload artifacts with ./gradlew publish
@@ -41,19 +43,19 @@
buildscript {
def filamentPath = file("../out/android-release/filament").absolutePath
if (project.hasProperty("filament_dist_dir")) {
filamentPath = file(project.property("filament_dist_dir")).absolutePath
if (project.hasProperty("com.google.android.filament.dist-dir")) {
filamentPath = file(project.property("com.google.android.filament.dist-dir")).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("filament_exclude_vulkan")
def excludeVulkan = project.hasProperty("com.google.android.filament.exclude-vulkan")
def abis = ["arm64-v8a", "armeabi-v7a", "x86_64", "x86"]
if (project.hasProperty("filament_abis")) {
def newAbis = project.property("filament_abis").split(',')
if (project.hasProperty("com.google.android.filament.abis")) {
def newAbis = project.property("com.google.android.filament.abis").split(',')
if (!newAbis.contains("all")) {
abis = newAbis
}
@@ -187,7 +189,8 @@ subprojects {
gradle.taskGraph.whenReady {
gradle.taskGraph.allTasks.each {
it.onlyIf {
!it.project.ext.has('isSample') || !project.hasProperty('filament_skip_samples')
!it.project.ext.has('isSample') ||
!project.hasProperty('com.google.android.filament.skip-samples')
}
}
}

View File

@@ -1,3 +1,17 @@
plugins {
id 'groovy-gradle-plugin'
}
gradlePlugin {
plugins {
create("filament-tools-plugin") {
id = "filament-tools-plugin"
implementationClass = "FilamentToolsPlugin"
}
}
}
repositories {
mavenCentral()
}

View File

@@ -1,20 +1,22 @@
// This plugin accepts the following parameters:
//
// filament_tools_dir
// com.google.android.filament.tools-dir
// Path to the Filament distribution/install directory for desktop.
// This directory must contain bin/matc.
//
// filament_exclude_vulkan
// com.google.android.filament.exclude-vulkan
// When set, support for Vulkan will be excluded.
//
// Example:
// ./gradlew -Pfilament_tools_dir=../../dist-release assembleDebug
// ./gradlew -Pcom.google.android.filament.tools-dir=../../dist-release assembleDebug
import org.gradle.api.DefaultTask
import org.gradle.api.GradleException
import org.gradle.api.Plugin
import org.gradle.api.Project
import org.gradle.api.provider.ProviderFactory
import org.gradle.api.file.DirectoryProperty
import org.gradle.api.file.FileSystemOperations
import org.gradle.api.file.FileType
import org.gradle.api.file.RegularFileProperty
import org.gradle.api.logging.LogLevel
@@ -27,14 +29,18 @@ import org.gradle.api.tasks.Optional
import org.gradle.api.tasks.OutputDirectory
import org.gradle.api.tasks.TaskAction
import org.gradle.api.tasks.incremental.InputFileDetails
import org.gradle.api.model.ObjectFactory
import org.gradle.internal.os.OperatingSystem
import org.gradle.process.ExecOperations
import org.gradle.work.ChangeType
import org.gradle.work.Incremental
import org.gradle.work.InputChanges
import java.nio.file.Paths
class TaskWithBinary extends DefaultTask {
import javax.inject.Inject
abstract class TaskWithBinary extends DefaultTask {
private final String binaryName
private Property<String> binaryPath = null
@@ -42,15 +48,23 @@ class TaskWithBinary extends DefaultTask {
binaryName = name
}
@Inject abstract ObjectFactory getObjects()
@Inject abstract ProviderFactory getProviders()
@Input
Property<String> getBinary() {
if (binaryPath == null) {
def tool = ["/bin/${binaryName}.exe", "/bin/${binaryName}"]
def fullPath = tool.collect { path ->
Paths.get(project.ext.filamentToolsPath.absolutePath, path).toFile()
def filamentToolsPath = providers
.gradleProperty("com.google.android.filament.tools-dir")
.forUseAtConfigurationTime().get()
def directory = objects.fileProperty()
.fileValue(new File(filamentToolsPath)).getAsFile().get()
Paths.get(directory.absolutePath, path).toFile()
}
binaryPath = project.objects.property(String.class)
binaryPath = objects.property(String.class)
binaryPath.set(
(OperatingSystem.current().isWindows() ? fullPath[0] : fullPath[1]).toString())
}
@@ -84,6 +98,11 @@ abstract class MaterialCompiler extends TaskWithBinary {
@OutputDirectory
abstract DirectoryProperty getOutputDir()
@Inject abstract FileSystemOperations getFs()
@Inject abstract ExecOperations getExec()
@Inject abstract ObjectFactory getObjects()
@Inject abstract ProviderFactory getProviders()
MaterialCompiler() {
super("matc")
}
@@ -91,7 +110,9 @@ abstract class MaterialCompiler extends TaskWithBinary {
@TaskAction
void execute(InputChanges inputs) {
if (!inputs.incremental) {
project.delete(project.fileTree(outputDir.asFile.get()).matching { include '*.filamat' })
fs.delete({
delete(objects.fileTree().from(outputDir).matching { include '*.filamat' })
})
}
inputs.getFileChanges(inputDir).each { InputFileDetails change ->
@@ -115,12 +136,15 @@ abstract class MaterialCompiler extends TaskWithBinary {
}
def matcArgs = []
if (!project.hasProperty("filament_exclude_vulkan")) {
def exclude_vulkan = providers
.gradleProperty("com.google.android.filament.exclude-vulkan")
.forUseAtConfigurationTime().present
if (!exclude_vulkan) {
matcArgs += ['-a', 'vulkan']
}
matcArgs += ['-a', 'opengl', '-p', 'mobile', '-o', getOutputFile(file), file]
project.exec {
exec.exec {
standardOutput out
errorOutput err
executable "${binary.get()}"
@@ -149,6 +173,10 @@ abstract class IblGenerator extends TaskWithBinary {
@OutputDirectory
abstract DirectoryProperty getOutputDir()
@Inject abstract FileSystemOperations getFs()
@Inject abstract ExecOperations getExec()
@Inject abstract ObjectFactory getObjects()
IblGenerator() {
super("cmgen")
}
@@ -156,7 +184,9 @@ abstract class IblGenerator extends TaskWithBinary {
@TaskAction
void execute(InputChanges inputs) {
if (!inputs.incremental) {
project.delete(project.fileTree(outputDir.asFile.get()).matching { include '*' })
fs.delete({
delete(objects.fileTree().from(outputDir).matching { include '*' })
})
}
inputs.getFileChanges(inputFile).each { InputFileDetails change ->
@@ -188,7 +218,7 @@ abstract class IblGenerator extends TaskWithBinary {
}
commandArgs = commandArgs + " " + file
project.exec {
exec.exec {
standardOutput out
errorOutput err
executable "${binary.get()}"
@@ -213,6 +243,9 @@ abstract class MeshCompiler extends TaskWithBinary {
@OutputDirectory
abstract DirectoryProperty getOutputDir()
@Inject abstract FileSystemOperations getFs()
@Inject abstract ExecOperations getExec()
MeshCompiler() {
super("filamesh")
}
@@ -220,7 +253,9 @@ abstract class MeshCompiler extends TaskWithBinary {
@TaskAction
void execute(InputChanges inputs) {
if (!inputs.incremental) {
project.delete(project.fileTree(outputDir.asFile.get()).matching { include '*.filamesh' })
fs.delete({
delete(objects.fileTree().from(outputDir).matching { include '*.filamesh' })
})
}
inputs.getFileChanges(inputFile).each { InputFileDetails change ->
@@ -243,7 +278,7 @@ abstract class MeshCompiler extends TaskWithBinary {
" Ensure Filament has been built/installed before building this app.")
}
project.exec {
exec.exec {
standardOutput out
errorOutput err
executable "${binary.get()}"
@@ -280,11 +315,6 @@ class FilamentToolsPlugin implements Plugin<Project> {
extension.meshInputFile = project.objects.fileProperty()
extension.meshOutputDir = project.objects.directoryProperty()
project.ext.filamentToolsPath = project.file("../../../out/release/filament")
if (project.hasProperty("filament_tools_dir")) {
project.ext.filamentToolsPath = project.file(project.property("filament_tools_dir"))
}
project.tasks.register("filamentCompileMaterials", MaterialCompiler) {
enabled =
extension.materialInputDir.isPresent() &&

View File

@@ -18,21 +18,15 @@
#include "private/backend/VirtualMachineEnv.h"
namespace filament {
extern jint JNI_OnLoad(JavaVM* vm, void* reserved);
};
JNIEXPORT jint JNI_OnLoad(JavaVM* vm, void* reserved) {
JNIEnv* env;
if (vm->GetEnv(reinterpret_cast<void**>(&env), JNI_VERSION_1_6) != JNI_OK) {
return -1;
}
#if ANDROID
::filament::JNI_OnLoad(vm, reserved);
#else
// This must be called when the library is loaded. We need this to get a reference to the
// global VM
::filament::VirtualMachineEnv::JNI_OnLoad(vm);
#endif
return JNI_VERSION_1_6;
}

View File

@@ -1,5 +1,5 @@
GROUP=com.google.android.filament
VERSION_NAME=1.12.10
VERSION_NAME=1.13.0
POM_DESCRIPTION=Real-time physically based rendering engine for Android.
@@ -18,3 +18,7 @@ POM_DEVELOPER_NAME=Filament Team
org.gradle.jvmargs=-Xmx1536m
android.useAndroidX=true
org.gradle.unsafe.configuration-cache=false
com.google.android.filament.tools-dir=../../../out/release/filament

View File

@@ -1,6 +1,8 @@
apply plugin: 'com.android.application'
apply plugin: 'kotlin-android'
apply plugin: FilamentToolsPlugin
plugins {
id 'com.android.application'
id 'kotlin-android'
id 'filament-tools-plugin'
}
project.ext.isSample = true

View File

@@ -1,6 +1,8 @@
apply plugin: 'com.android.application'
apply plugin: 'kotlin-android'
apply plugin: FilamentToolsPlugin
plugins {
id 'com.android.application'
id 'kotlin-android'
id 'filament-tools-plugin'
}
project.ext.isSample = true

View File

@@ -1,6 +1,8 @@
apply plugin: 'com.android.application'
apply plugin: 'kotlin-android'
apply plugin: FilamentToolsPlugin
plugins {
id 'com.android.application'
id 'kotlin-android'
id 'filament-tools-plugin'
}
project.ext.isSample = true

View File

@@ -1,6 +1,8 @@
apply plugin: 'com.android.application'
apply plugin: 'kotlin-android'
apply plugin: FilamentToolsPlugin
plugins {
id 'com.android.application'
id 'kotlin-android'
id 'filament-tools-plugin'
}
project.ext.isSample = true

View File

@@ -1,6 +1,8 @@
apply plugin: 'com.android.application'
apply plugin: 'kotlin-android'
apply plugin: FilamentToolsPlugin
plugins {
id 'com.android.application'
id 'kotlin-android'
id 'filament-tools-plugin'
}
project.ext.isSample = true

View File

@@ -1,6 +1,8 @@
apply plugin: 'com.android.application'
apply plugin: 'kotlin-android'
apply plugin: FilamentToolsPlugin
plugins {
id 'com.android.application'
id 'kotlin-android'
id 'filament-tools-plugin'
}
project.ext.isSample = true

View File

@@ -1,6 +1,8 @@
apply plugin: 'com.android.application'
apply plugin: 'kotlin-android'
apply plugin: FilamentToolsPlugin
plugins {
id 'com.android.application'
id 'kotlin-android'
id 'filament-tools-plugin'
}
project.ext.isSample = true

View File

@@ -1,5 +1,8 @@
apply plugin: 'com.android.application'
apply plugin: FilamentToolsPlugin
plugins {
id 'com.android.application'
id 'kotlin-android'
id 'filament-tools-plugin'
}
project.ext.isSample = true

View File

@@ -70,11 +70,11 @@ public class MainActivity extends Activity
private Page mPage;
private PageMaterials mPageMaterials;
private Scene mScene;
private Texture[] mTextures = new Texture[2];
private final Texture[] mTextures = new Texture[2];
private @Entity int mLight;
private IndirectLight mIndirectLight;
private float[] mTouchDownPoint = new float[2];
private final float[] mTouchDownPoint = new float[2];
private float mTouchDownValue = 0;
private float mPageAnimationRadians = 0;
private float mPageAnimationValue = 0;

View File

@@ -1,6 +1,8 @@
apply plugin: 'com.android.application'
apply plugin: 'kotlin-android'
apply plugin: FilamentToolsPlugin
plugins {
id 'com.android.application'
id 'kotlin-android'
id 'filament-tools-plugin'
}
project.ext.isSample = true

View File

@@ -1,6 +1,8 @@
apply plugin: 'com.android.application'
apply plugin: 'kotlin-android'
apply plugin: FilamentToolsPlugin
plugins {
id 'com.android.application'
id 'kotlin-android'
id 'filament-tools-plugin'
}
project.ext.isSample = true

View File

@@ -1,6 +1,8 @@
apply plugin: 'com.android.application'
apply plugin: 'kotlin-android'
apply plugin: FilamentToolsPlugin
plugins {
id 'com.android.application'
id 'kotlin-android'
id 'filament-tools-plugin'
}
project.ext.isSample = true

View File

@@ -1,6 +1,8 @@
apply plugin: 'com.android.application'
apply plugin: 'kotlin-android'
apply plugin: FilamentToolsPlugin
plugins {
id 'com.android.application'
id 'kotlin-android'
id 'filament-tools-plugin'
}
project.ext.isSample = true

View File

@@ -453,23 +453,23 @@ function build_android {
if [[ "${ISSUE_DEBUG_BUILD}" == "true" ]]; then
./gradlew \
-Pfilament_dist_dir=../out/android-debug/filament \
-Pfilament_abis=${ABI_GRADLE_OPTION} \
-Pcom.google.android.filament.dist-dir=../out/android-debug/filament \
-Pcom.google.android.filament.abis=${ABI_GRADLE_OPTION} \
${VULKAN_ANDROID_GRADLE_OPTION} \
:filament-android:assembleDebug \
:gltfio-android:assembleDebug \
:filament-utils-android:assembleDebug
./gradlew \
-Pfilament_dist_dir=../out/android-debug/filament \
-Pfilament_abis=${ABI_GRADLE_OPTION} \
-Pcom.google.android.filament.dist-dir=../out/android-debug/filament \
-Pcom.google.android.filament.abis=${ABI_GRADLE_OPTION} \
:filamat-android:assembleDebug
if [[ "${BUILD_ANDROID_SAMPLES}" == "true" ]]; then
for sample in ${ANDROID_SAMPLES}; do
./gradlew \
-Pfilament_dist_dir=../out/android-debug/filament \
-Pfilament_abis=${ABI_GRADLE_OPTION} \
-Pcom.google.android.filament.dist-dir=../out/android-debug/filament \
-Pcom.google.android.filament.abis=${ABI_GRADLE_OPTION} \
:samples:${sample}:assembleDebug
done
fi
@@ -502,23 +502,23 @@ function build_android {
if [[ "${ISSUE_RELEASE_BUILD}" == "true" ]]; then
./gradlew \
-Pfilament_dist_dir=../out/android-release/filament \
-Pfilament_abis=${ABI_GRADLE_OPTION} \
-Pcom.google.android.filament.dist-dir=../out/android-release/filament \
-Pcom.google.android.filament.abis=${ABI_GRADLE_OPTION} \
${VULKAN_ANDROID_GRADLE_OPTION} \
:filament-android:assembleRelease \
:gltfio-android:assembleRelease \
:filament-utils-android:assembleRelease
./gradlew \
-Pfilament_dist_dir=../out/android-release/filament \
-Pfilament_abis=${ABI_GRADLE_OPTION} \
-Pcom.google.android.filament.dist-dir=../out/android-release/filament \
-Pcom.google.android.filament.abis=${ABI_GRADLE_OPTION} \
:filamat-android:assembleRelease
if [[ "${BUILD_ANDROID_SAMPLES}" == "true" ]]; then
for sample in ${ANDROID_SAMPLES}; do
./gradlew \
-Pfilament_dist_dir=../out/android-release/filament \
-Pfilament_abis=${ABI_GRADLE_OPTION} \
-Pcom.google.android.filament.dist-dir=../out/android-release/filament \
-Pcom.google.android.filament.abis=${ABI_GRADLE_OPTION} \
:samples:${sample}:assembleRelease
done
fi
@@ -831,7 +831,7 @@ while getopts ":hacCfijmp:q:uvslwtdk:" opt; do
;;
v)
VULKAN_ANDROID_OPTION="-DFILAMENT_SUPPORTS_VULKAN=OFF"
VULKAN_ANDROID_GRADLE_OPTION="-Pfilament_exclude_vulkan"
VULKAN_ANDROID_GRADLE_OPTION="-Pcom.google.android.filament.exclude-vulkan"
echo "Disabling support for Vulkan in the core Filament library."
echo "Consider using -c after changing this option to clear the Gradle cache."
;;

View File

@@ -275,6 +275,7 @@ add_custom_command(
OUTPUT "${MATERIAL_DIR}/colorGrading.filamat"
DEPENDS ../shaders/src/dithering.fs
DEPENDS ../shaders/src/vignette.fs
DEPENDS src/materials/colorGrading/colorGrading.fs
APPEND
)
@@ -282,6 +283,7 @@ add_custom_command(
OUTPUT "${MATERIAL_DIR}/colorGradingAsSubpass.filamat"
DEPENDS ../shaders/src/dithering.fs
DEPENDS ../shaders/src/vignette.fs
DEPENDS src/materials/colorGrading/colorGrading.fs
APPEND
)

View File

@@ -21,6 +21,8 @@
#include <utils/Log.h>
#include <utils/debug.h>
#include <limits>
namespace filament {
namespace backend {

View File

@@ -64,16 +64,19 @@ MetalDriver::MetalDriver(backend::MetalPlatform* platform) noexcept
initializeSupportedGpuFamilies(mContext);
utils::slog.d << "Supported GPU families: " << utils::io::endl;
utils::slog.v << "Supported GPU families: " << utils::io::endl;
if (mContext->highestSupportedGpuFamily.common > 0) {
utils::slog.d << " MTLGPUFamilyCommon" << (int) mContext->highestSupportedGpuFamily.common << utils::io::endl;
utils::slog.v << " MTLGPUFamilyCommon" << (int) mContext->highestSupportedGpuFamily.common << utils::io::endl;
}
if (mContext->highestSupportedGpuFamily.apple > 0) {
utils::slog.d << " MTLGPUFamilyApple" << (int) mContext->highestSupportedGpuFamily.apple << utils::io::endl;
utils::slog.v << " MTLGPUFamilyApple" << (int) mContext->highestSupportedGpuFamily.apple << utils::io::endl;
}
if (mContext->highestSupportedGpuFamily.mac > 0) {
utils::slog.d << " MTLGPUFamilyMac" << (int) mContext->highestSupportedGpuFamily.mac << utils::io::endl;
utils::slog.v << " MTLGPUFamilyMac" << (int) mContext->highestSupportedGpuFamily.mac << utils::io::endl;
}
utils::slog.v << "Features:" << utils::io::endl;
utils::slog.v << " readWriteTextureSupport: " <<
(bool) mContext->device.readWriteTextureSupport << utils::io::endl;
// In order to support texture swizzling, the GPU needs to support it and the system be running
// iOS 13+.
@@ -650,11 +653,10 @@ bool MetalDriver::isRenderTargetFormatSupported(TextureFormat format) {
}
bool MetalDriver::isFrameBufferFetchSupported() {
#if defined(IOS) && !defined(FILAMENT_IOS_SIMULATOR)
return true;
#else
return false;
#endif
// FrameBuffer fetch is achievable via "programmable blending" in Metal, and only supported on
// Apple GPUs with readWriteTextureSupport.
return mContext->highestSupportedGpuFamily.apple >= 1 &&
mContext->device.readWriteTextureSupport;
}
bool MetalDriver::isFrameBufferFetchMultiSampleSupported() {

View File

@@ -352,8 +352,7 @@ MetalProgram::MetalProgram(id<MTLDevice> device, const Program& program) noexcep
length:source.size()
encoding:NSUTF8StringEncoding];
NSError* error = nil;
MTLCompileOptions* options = [MTLCompileOptions new];
options.languageVersion = MTLLanguageVersion1_1;
// When options is nil, Metal uses the most recent language version available.
id<MTLLibrary> library = [device newLibraryWithSource:objcSource
options:nil
error:&error];

View File

@@ -87,8 +87,14 @@ OpenGLContext::OpenGLContext() noexcept {
}
// On Adreno (As of 3/20) timer query seem to return the CPU time, not the GPU time.
bugs.dont_use_timer_query = true;
// Blits to texture arrays are failing
bugs.disable_sidecar_blit_into_texture_array = true;
// This bug doesn't happen anymore, but we don't know why. The standalone sample
// app that was written to show this problem still does. We have tested this on
// several V@0490.0 on several devices and the problem appears to have gone away.
// The working hypthesis is that some other state affects this behavior.
bugs.disable_sidecar_blit_into_texture_array = false;
// early exit condition is flattened in EASU code
bugs.split_easu = true;
bugs.invalidate_end_only_if_invalidate_start = true;

View File

@@ -245,11 +245,6 @@ AcquiredImage PlatformEGLAndroid::transformAcquiredImage(AcquiredImage source) n
return { eglImage, patchedCallback, closure, source.handler };
}
// This must be called when the library is loaded. We need this to get a reference to the global VM
void JNI_OnLoad(JavaVM* vm, void* reserved) {
::filament::VirtualMachineEnv::JNI_OnLoad(vm);
}
} // namespace filament
// ---------------------------------------------------------------------------------------------

View File

@@ -558,10 +558,11 @@ public:
* and are defined by the angle from the center axis to where the falloff begins (i.e.
* cones are defined by their half-angle).
*
* @param inner inner cone angle in *radians* between 0 and @f$ \pi/2 @f$
*
* @param outer outer cone angle in *radians* between \p inner and @f$ \pi/2 @f$
* Both inner and outer are silently clamped to a minimum value of 0.5 degrees
* (~0.00873 radians) to avoid floating-point precision issues during rendering.
*
* @param inner inner cone angle in *radians* between 0.00873 and \p outer
* @param outer outer cone angle in *radians* between 0.00873 inner and @f$ \pi/2 @f$
* @return This Builder, for chaining calls.
*
* @note
@@ -812,8 +813,8 @@ public:
* Dynamically updates a spot light's cone as angles
*
* @param i Instance of the component obtained from getInstance().
* @param inner inner cone angle in *radians* between 0 and pi/2
* @param outer outer cone angle in *radians* between inner and pi/2
* @param inner inner cone angle in *radians* between 0.00873 and outer
* @param outer outer cone angle in *radians* between 0.00873 and pi/2
*
* @see Builder.spotLightCone()
*/

View File

@@ -560,14 +560,19 @@ void Froxelizer::froxelizeLoop(FEngine& engine,
const mat4f& projection = mProjection;
const mat3f& vn = camera.view.upperLeft();
// We use minimum cone angle of 0.5 degrees because too small angles cause issues in the
// sphere/cone intersection test, due to floating-point precision.
constexpr float maxInvSin = 114.59301f; // 1 / sin(0.5 degrees)
constexpr float maxCosSquared = 0.99992385f; // cos(0.5 degrees)^2
for (size_t i = offset; i < count; i += stride) {
const size_t j = i + FScene::DIRECTIONAL_LIGHTS_COUNT;
FLightManager::Instance li = instances[j];
LightParams light = {
.position = (camera.view * float4{ spheres[j].xyz, 1 }).xyz, // to view-space
.cosSqr = lcm.getCosOuterSquared(li), // spot only
.axis = vn * directions[j], // spot only
.invSin = lcm.getSinInverse(li), // spot only
.cosSqr = std::min(maxCosSquared, lcm.getCosOuterSquared(li)), // spot only
.axis = vn * directions[j], // spot only
.invSin = std::min(maxInvSin, lcm.getSinInverse(li)), // spot only
.radius = spheres[j].w,
};

View File

@@ -84,6 +84,8 @@ void PerViewUniforms::prepareCamera(const CameraInfo& camera) noexcept {
s.cameraPosition = float3{ camera.getPosition() };
s.worldOffset = camera.worldOffset;
s.cameraFar = camera.zf;
s.oneOverFarMinusNear = 1.0f / (camera.zf - camera.zn);
s.nearOverFarMinusNear = camera.zn / (camera.zf - camera.zn);
s.clipControl = mClipControl;
}

View File

@@ -316,9 +316,11 @@ void FScene::prepareDynamicLights(const CameraInfo& camera, ArenaScope& rootAren
const size_t gpuIndex = i - DIRECTIONAL_LIGHTS_COUNT;
auto li = instances[i];
lp[gpuIndex].positionFalloff = { spheres[i].xyz, lcm.getSquaredFalloffInv(li) };
lp[gpuIndex].color = { lcm.getColor(li), 0.0f };
lp[gpuIndex].directionIES = { directions[i], 0.0f };
lp[gpuIndex].direction = directions[i];
lp[gpuIndex].reserved1 = {};
lp[gpuIndex].colorIES = { lcm.getColor(li), 0.0f };
lp[gpuIndex].spotScaleOffset = lcm.getSpotParams(li).scaleOffset;
lp[gpuIndex].reserved3 = {};
lp[gpuIndex].intensity = lcm.getIntensity(li);
lp[gpuIndex].typeShadow = LightsUib::packTypeShadow(
lcm.isPointLight(li) ? 0u : 1u,
@@ -326,7 +328,6 @@ void FScene::prepareDynamicLights(const CameraInfo& camera, ArenaScope& rootAren
shadowInfo[i].index,
shadowInfo[i].layer);
lp[gpuIndex].channels = LightsUib::packChannels(lcm.getLightChannels(li), shadowInfo[i].castsShadows);
lp[gpuIndex].reserved = {};
}
driver.updateBufferObject(lightUbh, { lp, positionalLightCount * sizeof(LightsUib) }, 0);

View File

@@ -52,7 +52,7 @@ ShadowMap::ShadowMap(FEngine& engine) noexcept :
FDebugRegistry& debugRegistry = engine.getDebugRegistry();
debugRegistry.registerProperty("d.shadowmap.focus_shadowcasters", &engine.debug.shadowmap.focus_shadowcasters);
debugRegistry.registerProperty("d.shadowmap.far_uses_shadowcasters", &engine.debug.shadowmap.far_uses_shadowcasters);
if (ENABLE_LISPSM) {
if constexpr (ENABLE_LISPSM) {
debugRegistry.registerProperty("d.shadowmap.lispsm", &engine.debug.shadowmap.lispsm);
debugRegistry.registerProperty("d.shadowmap.dzn", &engine.debug.shadowmap.dzn);
debugRegistry.registerProperty("d.shadowmap.dzf", &engine.debug.shadowmap.dzf);
@@ -79,49 +79,15 @@ void ShadowMap::render(FScene const& scene, utils::Range<uint32_t> range,
pass->sortCommands();
}
mat4f ShadowMap::getLightViewMatrix(float3 position, float3 direction) noexcept {
mat4f ShadowMap::getDirectionalLightViewMatrix(float3 direction, float3 position) noexcept {
const mat4f M = mat4f::lookAt(position, position + direction, float3{ 0, 1, 0 });
return FCamera::rigidTransformInverse(M);
}
void ShadowMap::computeSceneInfo(float3 dir,
FScene const& scene, filament::CameraInfo const& camera, uint8_t visibleLayers,
SceneInfo& sceneInfo) {
// We assume the light is at the origin to compute the SceneInfo. This is consumed later by
// computeShadowCameraDirectional() which takes this into account.
const mat4f Mv = getLightViewMatrix({}, dir);
const mat4f V = camera.view;
// Compute scene bounds in world space, as well as the light-space and view-space near/far planes
sceneInfo.lsNearFar = { std::numeric_limits<float>::lowest(), std::numeric_limits<float>::max() };
sceneInfo.vsNearFar = { std::numeric_limits<float>::lowest(), std::numeric_limits<float>::max() };
sceneInfo.wsShadowCastersVolume = {};
sceneInfo.wsShadowReceiversVolume = {};
visitScene(scene, visibleLayers,
[&](Aabb caster) {
sceneInfo.wsShadowCastersVolume.min =
min(sceneInfo.wsShadowCastersVolume.min, caster.min);
sceneInfo.wsShadowCastersVolume.max =
max(sceneInfo.wsShadowCastersVolume.max, caster.max);
float2 nf = 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) {
sceneInfo.wsShadowReceiversVolume.min =
min(sceneInfo.wsShadowReceiversVolume.min, receiver.min);
sceneInfo.wsShadowReceiversVolume.max =
max(sceneInfo.wsShadowReceiversVolume.max, receiver.max);
float2 nf = computeNearFar(V, receiver);
sceneInfo.vsNearFar.x = std::max(sceneInfo.vsNearFar.x, nf.x);
sceneInfo.vsNearFar.y = std::min(sceneInfo.vsNearFar.y, nf.y);
}
);
}
void ShadowMap::update(const FScene::LightSoa& lightData, size_t index,
filament::CameraInfo const& camera, const ShadowMapInfo& shadowMapInfo,
const SceneInfo& cascadeParams) noexcept {
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();
@@ -148,7 +114,7 @@ void ShadowMap::update(const FScene::LightSoa& lightData, size_t index,
cullingProjection[2].z = (f + n) / (n - f);
cullingProjection[3].z = (2 * f * n) / (n - f);
} else {
// ortho projection
// orthographic projection
cullingProjection[2].z = 2.0f / (n - f);
cullingProjection[3].z = (f + n) / (n - f);
}
@@ -178,14 +144,15 @@ void ShadowMap::update(const FScene::LightSoa& lightData, size_t index,
case LightType::DIRECTIONAL:
computeShadowCameraDirectional(
lightData.elementAt<FScene::DIRECTION>(index),
cameraInfo, params, cascadeParams);
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);
lightData.elementAt<FScene::POSITION_RADIUS>(index).w,
cameraInfo, params, scene, sceneInfo);
break;
case LightType::POINT:
break;
@@ -195,19 +162,22 @@ void ShadowMap::update(const FScene::LightSoa& lightData, size_t index,
void ShadowMap::computeShadowCameraDirectional(
float3 const& dir, ShadowCameraInfo const& camera,
FLightManager::ShadowParams const& params,
SceneInfo cascadeParams) noexcept {
FScene const& scene, SceneInfo& sceneInfo) noexcept {
/*
* Compute the light's model matrix
*/
// 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 mater for a directional light.
// 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 = getLightViewMatrix({}, dir);
const mat4f MvAtOrigin = getDirectionalLightViewMatrix(dir);
const Aabb wsShadowCastersVolume = cascadeParams.wsShadowCastersVolume;
const Aabb wsShadowReceiversVolume = cascadeParams.wsShadowReceiversVolume;
// Compute scene-dependent values shared across all cascades
ShadowMap::updateSceneInfo(MvAtOrigin, scene, sceneInfo);
const Aabb wsShadowCastersVolume = sceneInfo.wsShadowCastersVolume;
const Aabb wsShadowReceiversVolume = sceneInfo.wsShadowReceiversVolume;
if (wsShadowCastersVolume.isEmpty() || wsShadowReceiversVolume.isEmpty()) {
mHasVisibleShadows = false;
return;
@@ -217,13 +187,13 @@ void ShadowMap::computeShadowCameraDirectional(
float3 wsViewFrustumVertices[8];
computeFrustumCorners(wsViewFrustumVertices,
camera.model * FCamera::inverseProjection(camera.projection),
cascadeParams.csNearFar);
sceneInfo.csNearFar);
// we use aligned_storage<> here to avoid the default initialization of std::array<>
std::aligned_storage<sizeof(FrustumBoxIntersection)>::type localStorage;
std::aligned_storage<sizeof(FrustumBoxIntersection)>::type localStorage; // NOLINT(cppcoreguidelines-pro-type-member-init)
FrustumBoxIntersection& wsClippedShadowReceiverVolume{ reinterpret_cast<FrustumBoxIntersection&>(localStorage) };
// compute the intersection of the shadow receivers volume with the view volume
// compute the intersection of the shadow receivers' volume with the view volume
// in world space. This returns a set of points on the convex-hull of the intersection.
size_t vertexCount = intersectFrustumWithBox(wsClippedShadowReceiverVolume,
wsViewFrustumVertices, wsShadowReceiversVolume);
@@ -243,27 +213,27 @@ void ShadowMap::computeShadowCameraDirectional(
*/
Aabb lsLightFrustumBounds;
if (!USE_DEPTH_CLAMP) {
if constexpr (!USE_DEPTH_CLAMP) {
// near plane from shadow caster volume
lsLightFrustumBounds.max.z = cascadeParams.lsNearFar[0];
lsLightFrustumBounds.max.z = sceneInfo.lsNearFar[0];
}
for (size_t i = 0; i < vertexCount; ++i) {
// far: figure out farthest shadow receivers
// far: figure out the farthest shadow receivers
float3 v = mat4f::project(MvAtOrigin, wsClippedShadowReceiverVolume[i]);
lsLightFrustumBounds.min.z = std::min(lsLightFrustumBounds.min.z, v.z);
if (USE_DEPTH_CLAMP) {
// further tighten to the shadow receiver volume
if constexpr (USE_DEPTH_CLAMP) {
// tighten the shadow receiver volume further
lsLightFrustumBounds.max.z = std::max(lsLightFrustumBounds.max.z, v.z);
}
}
if (mEngine.debug.shadowmap.far_uses_shadowcasters) {
// far: closest of the farthest shadow casters and receivers
lsLightFrustumBounds.min.z = std::max(lsLightFrustumBounds.min.z, cascadeParams.lsNearFar[1]);
lsLightFrustumBounds.min.z = std::max(lsLightFrustumBounds.min.z, sceneInfo.lsNearFar[1]);
}
// 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 = getLightViewMatrix(dir * -lsLightFrustumBounds.max.z, dir);
const mat4f Mv = getDirectionalLightViewMatrix(dir, dir * -lsLightFrustumBounds.max.z);
// near / far planes are specified relative to the direction the eye is looking at
// i.e. the -z axis (see: ortho)
@@ -279,7 +249,7 @@ void ShadowMap::computeShadowCameraDirectional(
float4 viewVolumeBoundingSphere = {};
if (params.options.stable) {
// In stable mode, the light frustum size must be fixed, so we can choose either the
// whole view frustum, or the whole scene bounding volume. We simply pick whichever is
// whole view frustum, or the whole scene bounding volume. We simply pick whichever
// is smaller.
// in stable mode we simply take the shadow receivers volume
@@ -411,7 +381,7 @@ void ShadowMap::computeShadowCameraDirectional(
const mat4f S = F * WLMpMv;
// Computes St the transform to use in the shader to access the shadow map texture
// i.e. it transform a world-space vertex to a texture coordinate in the shadow-map
// i.e. it transforms a world-space vertex to a texture coordinate in the shadowmap
const mat4 MbMt = getTextureCoordsMapping();
const mat4f St = mat4f(MbMt * S);
@@ -426,7 +396,7 @@ void ShadowMap::computeShadowCameraDirectional(
if (!mShadowMapInfo.vsm) {
mLightSpace = St;
} else {
mLightSpace = computeVsmLightSpaceMatrix(St, Mv, zfar);
mLightSpace = computeVsmLightSpaceMatrix(St, Mv, znear, zfar);
}
// We apply the constant bias in world space (as opposed to light-space) to account
@@ -451,39 +421,47 @@ void ShadowMap::computeShadowCameraDirectional(
}
}
void ShadowMap::computeShadowCameraSpot(math::float3 const& position, math::float3 const& dir,
void ShadowMap::computeShadowCameraSpot(float3 const& position, float3 const& dir,
float outerConeAngle, float radius, ShadowCameraInfo const& camera,
FLightManager::ShadowParams const& params) noexcept {
FLightManager::ShadowParams const& params, FScene const& scene,
SceneInfo& sceneInfo) noexcept {
// TODO: correctly compute if this spot light has any visible shadows.
mHasVisibleShadows = true;
/*
* Compute the light models matrix.
* Compute the light model matrix.
*/
// Choose a reasonable value for the near plane.
const float nearPlane = radius * (1.0f / 1024.0f);
const float farPlane = radius;
const mat4f Mv = getLightViewMatrix(position, dir);
const mat4f Mv = getDirectionalLightViewMatrix(dir, 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)
float nearPlane = std::max(0.01f, -sceneInfo.lsNearFar.x);
float farPlane = std::min(radius, -sceneInfo.lsNearFar.y);
float outerConeAngleDegrees = outerConeAngle * f::RAD_TO_DEG;
const mat4f Mp = mat4f::perspective(outerConeAngleDegrees * 2, 1.0f, nearPlane, farPlane,
mat4f::Fov::HORIZONTAL);
const mat4f MpMv(Mp * Mv);
const mat4f Mp = mat4f::perspective(outerConeAngleDegrees * 2.0f, 1.0f, nearPlane, farPlane);
const mat4f MpMv(math::highPrecisionMultiply(Mp, Mv));
// Final shadow transform
const mat4f S = MpMv;
const mat4 MbMt = getTextureCoordsMapping();
const mat4f St = mat4f(MbMt * S);
mTexelSizeWs = texelSizeWorldSpace(Mp, mat4f(MbMt));
// TODO: focus projection
// FIXME: texelSizeWorldSpace doesn't work for spotlights
mTexelSizeWs = 0; //texelSizeWorldSpace(Mp, mat4f(MbMt));
if (!mShadowMapInfo.vsm) {
mLightSpace = St;
} else {
mLightSpace = computeVsmLightSpaceMatrix(St, Mv, farPlane);
mLightSpace = computeVsmLightSpaceMatrix(St, Mv, nearPlane, farPlane);
}
const float constantBias = mShadowMapInfo.vsm ? 0.0f : params.options.constantBias;
@@ -505,7 +483,7 @@ void ShadowMap::computeShadowCameraSpot(math::float3 const& position, math::floa
mDebugCamera->setCustomProjection(mat4(Sb * camera.worldOrigin), nearPlane, radius);
}
mat4f ShadowMap::applyLISPSM(math::mat4f& Wp,
mat4f ShadowMap::applyLISPSM(mat4f& Wp,
ShadowCameraInfo const& camera, FLightManager::ShadowParams const& params,
mat4f const& LMpMv,
FrustumBoxIntersection const& wsShadowReceiversVolume, size_t vertexCount,
@@ -514,7 +492,7 @@ mat4f ShadowMap::applyLISPSM(math::mat4f& Wp,
const float LoV = dot(camera.getForwardVector(), dir);
const float sinLV = std::sqrt(std::max(0.0f, 1.0f - LoV * LoV));
// Virtual near plane -- the default is 1m, can be changed by the user.
// Virtual near plane -- the default is 1 m, can be changed by the user.
// The virtual near plane prevents too much resolution to be wasted in the area near the eye
// where shadows might not be visible (e.g. a character standing won't see shadows at her feet).
const float dzn = std::max(0.0f, params.options.shadowNearHint - camera.zn);
@@ -528,8 +506,8 @@ mat4f ShadowMap::applyLISPSM(math::mat4f& Wp,
// compute n and f, the near and far planes coordinates of Wp (warp space).
// It's found by looking down the Y axis in light space (i.e. -Z axis of Wp,
// i.e. the axis orthogonal to the light direction) and taking the min/max
// of the shadow receivers volume.
// Note: znear/zfar encoded in Mp has no influence here (b/c we're interested only by the y axis)
// of the shadow receivers' volume.
// Note: znear/zfar encoded in Mp has no influence here (b/c we're interested only by the y-axis)
const float2 nf = computeNearFarOfWarpSpace(LMpMv, wsShadowReceiversVolume.data(), vertexCount);
const float n = nf[0]; // near plane coordinate of Mp (light space)
const float f = nf[1]; // far plane coordinate of Mp (light space)
@@ -563,10 +541,10 @@ mat4f ShadowMap::applyLISPSM(math::mat4f& Wp,
const float3 lsCameraPosition = mat4f::project(LMpMv, camera.getPosition());
const float3 p = {
// Another option here is to use lsShadowReceiversCenter.x, which skews less the
// x axis. Doesn't seem to make a big difference in the end.
// x-axis. Doesn't seem to make a big difference in the end.
lsCameraPosition.x,
n - nopt,
// note: various papers suggest to use the shadow receiver's center z coordinate in light
// note: various papers suggest using the shadow receiver's center z coordinate in light
// space, i.e. to center "vertically" on the shadow receiver volume.
// e.g. (LMpMv * wsShadowReceiversVolume.center()).z
// However, simply using 0, guarantees to be centered on the light frustum, which itself
@@ -629,22 +607,22 @@ mat4 ShadowMap::getTextureCoordsMapping() const noexcept {
return mat4(Mf * Mb * Mv * Mt);
}
math::mat4f ShadowMap::computeVsmLightSpaceMatrix(const math::mat4f& lightSpacePcf,
const math::mat4f& Mv, float zfar) noexcept {
mat4f ShadowMap::computeVsmLightSpaceMatrix(const mat4f& lightSpacePcf,
const mat4f& Mv, float znear, float zfar) noexcept {
// The lightSpacePcf matrix transforms coordinates from world space into (u, v, z) coordinates,
// where (u, v) are used to access the shadow map, and z is the (non linear) PCF comparison
// where (u, v) are used to access the shadow map, and z is the (non-linear) PCF comparison
// value [0, 1].
//
// For VSM, we want to leave the z coordinate in linear light space, normalized between [0, 1]
// (the normalization factor is therefore -1/zfar).
//
// For VSM, we want to leave the z coordinate in linear light-space, normalized between [0, 1],
// i.e. remapping [near, far] to [0, 1].
// When sampling a VSM shadow map, the shader follows suit, and doesn't divide by w for the z
// coordinate. See getters.fs.
math::mat4f lightSpaceVsm{ lightSpacePcf };
lightSpaceVsm[0].z = Mv[0].z * (-1.0f / zfar);
lightSpaceVsm[1].z = Mv[1].z * (-1.0f / zfar);
lightSpaceVsm[2].z = Mv[2].z * (-1.0f / zfar);
lightSpaceVsm[3].z = Mv[3].z * (-1.0f / zfar);
// coordinate. See shadowing.fs.
// compute z' = -(Mv * position).z / (far - near) - (near / (far - near))
const float scale = 1.0f / (zfar - znear);
mat4f lightSpaceVsm{ lightSpacePcf };
lightSpaceVsm[0].z = -Mv[0].z * scale;
lightSpaceVsm[1].z = -Mv[1].z * scale;
lightSpaceVsm[2].z = -Mv[2].z * scale;
lightSpaceVsm[3].z = (-Mv[3].z - znear) * scale;
return lightSpaceVsm;
}
@@ -663,7 +641,7 @@ mat4f ShadowMap::warpFrustum(float n, float f) noexcept {
return Wp;
}
math::mat4f ShadowMap::directionalLightFrustum(float near, float far) noexcept {
mat4f ShadowMap::directionalLightFrustum(float near, float far) noexcept {
const float d = far - near;
mat4f m;
m[2][2] = -2 / d;
@@ -694,7 +672,7 @@ float2 ShadowMap::computeNearFarOfWarpSpace(mat4f const& lightView,
float2 nearFar = { std::numeric_limits<float>::max(), std::numeric_limits<float>::lowest() };
#pragma nounroll
for (size_t i = 0; i < count; i++) {
// we're on the y axis in light space (looking down to +y)
// we're on the y-axis in light space (looking down to +y)
float c = mat4f::project(lightView, wsVertices[i]).y;
nearFar.x = std::min(nearFar.x, c);
nearFar.y = std::max(nearFar.y, c);
@@ -805,7 +783,7 @@ void ShadowMap::snapLightFrustum(float2& s, float2& o,
const float2 r = 2.0f * shadowMapResolution;
o -= fmod(o, r);
// This offsets the texture coordinates so it has a fixed offset w.r.t the world
// This offsets the texture coordinates, so it has a fixed offset w.r.t the world
const float2 lsOrigin = mat4f::project(Mv, worldOrigin).xy * s;
o -= fmod(lsOrigin, r);
}
@@ -854,7 +832,7 @@ size_t ShadowMap::intersectFrustumWithBox(
// b) add the scene's vertices that are known to be inside the view frustum
//
// We need to handle the case where a corner of the box lies exactly on a plane of
// the frustum. This actually happens often due to fitting light-space
// the frustum. This actually often happens due to fitting light-space
// We fudge the distance to the plane by a small amount.
#pragma nounroll
for (float3 p : wsSceneReceiversCorners) {
@@ -988,7 +966,7 @@ float ShadowMap::texelSizeWorldSpace(const mat3f& worldToShadowTexture) const no
// The Jacobian of the transformation from texture-to-world is the matrix itself for
// orthographic projections. We just need to inverse worldToShadowTexture,
// which is guaranteed to be orthographic.
// The two first columns give us the how a texel maps in world-space.
// The two first columns give us how a texel maps in world-space.
const float ures = 1.0f / mShadowMapInfo.shadowDimension;
const float vres = 1.0f / mShadowMapInfo.shadowDimension;
const mat3f shadowTextureToWorld(inverse(worldToShadowTexture));
@@ -1001,7 +979,7 @@ float ShadowMap::texelSizeWorldSpace(const mat3f& worldToShadowTexture) const no
float ShadowMap::texelSizeWorldSpace(const mat4f& Wp, const mat4f& MbMtF) const noexcept {
// Here we compute the Jacobian of inverse(MbMtF * Wp).
// The expression below has been computed with Mathematica. However, it's not very hard,
// albeit error prone, to do it by hand because MbMtF is a linear transform.
// albeit error-prone, to do it by hand because MbMtF is a linear transform.
// So we really only need to calculate the Jacobian of inverse(Wp) at inverse(MbMtF).
//
// Because we're only interested in the length of the columns of the Jacobian, we can use
@@ -1018,7 +996,7 @@ float ShadowMap::texelSizeWorldSpace(const mat4f& Wp, const mat4f& MbMtF) const
const float dres = mShadowMapInfo.zResolution;
constexpr bool JACOBIAN_ESTIMATE = false;
if (JACOBIAN_ESTIMATE) {
if constexpr (JACOBIAN_ESTIMATE) {
// this estimates the Jacobian -- this is a lot heavier. This is mostly for reference
// and testing.
const mat4f Si(inverse(MbMtF * Wp));
@@ -1063,7 +1041,6 @@ float ShadowMap::texelSizeWorldSpace(const mat4f& Wp, const mat4f& MbMtF) const
return s;
}
template<typename Casters, typename Receivers>
void ShadowMap::visitScene(const FScene& scene, uint32_t visibleLayers,
Casters casters, Receivers receivers) noexcept {
@@ -1090,4 +1067,48 @@ void ShadowMap::visitScene(const FScene& scene, uint32_t visibleLayers,
}
}
void ShadowMap::initSceneInfo(FScene const& scene, filament::CameraInfo const& camera,
ShadowMap::SceneInfo& sceneInfo) {
sceneInfo.vsNearFar = { std::numeric_limits<float>::lowest(), std::numeric_limits<float>::max() };
// We assume the light is at the origin to compute the SceneInfo. This is consumed later by
// computeShadowCameraDirectional() which takes this into account.
const mat4f V = camera.view;
// Compute scene bounds in world space, as well as the light-space and view-space near/far planes
sceneInfo.wsShadowCastersVolume = {};
sceneInfo.wsShadowReceiversVolume = {};
visitScene(scene, sceneInfo.visibleLayers,
[&](Aabb caster) {
sceneInfo.wsShadowCastersVolume.min =
min(sceneInfo.wsShadowCastersVolume.min, caster.min);
sceneInfo.wsShadowCastersVolume.max =
max(sceneInfo.wsShadowCastersVolume.max, caster.max);
},
[&](Aabb receiver) {
sceneInfo.wsShadowReceiversVolume.min =
min(sceneInfo.wsShadowReceiversVolume.min, receiver.min);
sceneInfo.wsShadowReceiversVolume.max =
max(sceneInfo.wsShadowReceiversVolume.max, receiver.max);
float2 nf = ShadowMap::computeNearFar(V, receiver);
sceneInfo.vsNearFar.x = std::max(sceneInfo.vsNearFar.x, nf.x);
sceneInfo.vsNearFar.y = std::min(sceneInfo.vsNearFar.y, nf.y);
}
);
}
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) {
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) {
}
);
}
} // namespace filament

View File

@@ -64,6 +64,8 @@ public:
};
struct SceneInfo {
explicit SceneInfo(uint8_t visibleLayers) noexcept : visibleLayers(visibleLayers) { }
// The near and far planes, in clip space, to use for this shadow map
math::float2 csNearFar = { -1.0f, 1.0f };
@@ -81,22 +83,18 @@ public:
// World-space shadow-receivers volume
Aabb wsShadowReceiversVolume;
uint8_t visibleLayers;
};
static math::mat4f getLightViewMatrix(
math::float3 position, math::float3 direction) noexcept;
// Call once per frame to populate the CascadeParameters struct, then pass to update().
// This computes values constant across all cascades.
static void computeSceneInfo(math::float3 dir,
FScene const& scene, filament::CameraInfo const& camera, uint8_t visibleLayers,
SceneInfo& sceneInfo);
static math::mat4f getDirectionalLightViewMatrix(
math::float3 direction, math::float3 position = {}) noexcept;
// 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,
const ShadowMapInfo& shadowMapInfo, const SceneInfo& cascadeParams) noexcept;
void update(const FScene::LightSoa& lightData, size_t index, filament::CameraInfo const& camera,
const ShadowMapInfo& shadowMapInfo, FScene const& scene,
SceneInfo& sceneInfo) noexcept;
void render(FScene const& scene, utils::Range<uint32_t> range,
FScene::VisibleMaskType visibilityMask, filament::CameraInfo const& cameraInfo,
@@ -120,6 +118,15 @@ public:
backend::PolygonOffset getPolygonOffset() const noexcept { return mPolygonOffset; }
// Call once per frame to populate the SceneInfo struct, then pass to update().
// This computes values constant across all shadow maps.
static void initSceneInfo(FScene const& scene, filament::CameraInfo const& camera,
ShadowMap::SceneInfo& sceneInfo);
// Update SceneInfo struct for a given light
static void updateSceneInfo(const math::mat4f& Mv, FScene const& scene,
ShadowMap::SceneInfo& sceneInfo);
private:
struct ShadowCameraInfo {
math::mat4f projection;
@@ -145,13 +152,14 @@ 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& direction,
ShadowCameraInfo const& camera, FLightManager::ShadowParams const& params,
SceneInfo cascadeParams) noexcept;
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) noexcept;
FLightManager::ShadowParams const& params, FScene const& scene,
SceneInfo& sceneInfo) noexcept;
static math::mat4f applyLISPSM(math::mat4f& Wp,
ShadowCameraInfo const& camera, FLightManager::ShadowParams const& params,
@@ -213,8 +221,8 @@ private:
math::mat4 getTextureCoordsMapping() const noexcept;
static math::mat4f computeVsmLightSpaceMatrix(const math::mat4f& lightSpace,
const math::mat4f& Mv, float zfar) noexcept;
static math::mat4f computeVsmLightSpaceMatrix(const math::mat4f& lightSpacePcf,
const math::mat4f& Mv, float znear, float zfar) noexcept;
float texelSizeWorldSpace(const math::mat3f& worldToShadowTexture) const noexcept;
float texelSizeWorldSpace(const math::mat4f& W, const math::mat4f& MbMtF) const noexcept;

View File

@@ -34,7 +34,7 @@ namespace filament {
using namespace backend;
using namespace math;
ShadowMapManager::ShadowMapManager(FEngine& engine) {
ShadowMapManager::ShadowMapManager(FEngine& engine) { // NOLINT(cppcoreguidelines-pro-type-member-init)
// initialize our ShadowMap array in-place
for (auto& entry : mShadowMapCache) {
new (&entry) ShadowMap(engine);
@@ -59,8 +59,20 @@ ShadowMapManager::ShadowTechnique ShadowMapManager::update(
FScene::LightSoa& lightData) noexcept {
calculateTextureRequirements(engine, view, lightData);
ShadowTechnique shadowTechnique = {};
shadowTechnique |= updateCascadeShadowMaps(engine, view, renderableData, lightData);
shadowTechnique |= updateSpotShadowMaps(engine, view, shadowUb, renderableData, lightData);
ShadowMap::SceneInfo sceneInfo(view.getVisibleLayers());
// Compute scene-dependent values shared across all shadow maps
ShadowMap::initSceneInfo(
*view.getScene(), view.getCameraInfo(),
sceneInfo);
shadowTechnique |= updateCascadeShadowMaps(
engine, view, renderableData, lightData, sceneInfo);
shadowTechnique |= updateSpotShadowMaps(
engine, view, renderableData, lightData, sceneInfo, shadowUb);
return shadowTechnique;
}
@@ -163,9 +175,9 @@ void ShadowMapManager::render(FrameGraph& fg, FEngine& engine, backend::DriverAp
const float4 vsmClearColor{ vsmMoment1, vsmMoment2, 0.0f, 0.0f };
struct ShadowPassData {
FrameGraphId<FrameGraphTexture> tempBlurSrc; // temporary shadowmap when blurring
uint32_t blurRt;
uint32_t shadowRt;
FrameGraphId<FrameGraphTexture> tempBlurSrc{}; // temporary shadowmap when blurring
uint32_t blurRt{};
uint32_t shadowRt{};
};
auto shadows = prepareShadowPass.getData().shadows;
@@ -240,7 +252,7 @@ void ShadowMapManager::render(FrameGraph& fg, FEngine& engine, backend::DriverAp
renderTargetDesc.clearFlags = TargetBufferFlags::DEPTH;
}
// finally create the shadowmap render target -- one per layer.
// finally, create the shadowmap render target -- one per layer.
data.shadowRt = builder.declareRenderPass("Shadow RT", renderTargetDesc);
},
[=, &engine, &view](FrameGraphResources const& resources,
@@ -261,7 +273,7 @@ void ShadowMapManager::render(FrameGraph& fg, FEngine& engine, backend::DriverAp
view.prepareCamera(cameraInfo);
// We set a viewport with a 1-texel border for when we index outside of the
// We set a viewport with a 1-texel border for when we index outside the
// texture.
// DON'T CHANGE this unless ShadowMap::getTextureCoordsMapping() is updated too.
// see: ShadowMap::getTextureCoordsMapping()
@@ -276,7 +288,7 @@ void ShadowMapManager::render(FrameGraph& fg, FEngine& engine, backend::DriverAp
view.prepareViewport(viewport);
// set uniforms needed to render this ShadowMap
// Currently these uniforms are owned by View and are global, but eventully
// Currently these uniforms are owned by View and are global, but eventually
// this will set a separate per shadowmap UBO
view.prepareShadowMap();
@@ -298,7 +310,7 @@ void ShadowMapManager::render(FrameGraph& fg, FEngine& engine, backend::DriverAp
const float sigma = (blurWidth + 1.0f) / 6.0f;
size_t kernelWidth = std::ceil((blurWidth - 5.0f) / 4.0f);
kernelWidth = kernelWidth * 4 + 5;
const float ratio = (kernelWidth + 1.0f) / sigma;
const float ratio = float(kernelWidth + 1) / sigma;
ppm.gaussianBlurPass(fg,
shadowPass->tempBlurSrc, 0,
shadows, 0, layer,
@@ -321,26 +333,18 @@ void ShadowMapManager::render(FrameGraph& fg, FEngine& engine, backend::DriverAp
fg.getBlackboard().put("shadows", shadows);
}
ShadowMapManager::ShadowTechnique ShadowMapManager::updateCascadeShadowMaps(
FEngine& engine, FView& view, FScene::RenderableSoa& renderableData,
FScene::LightSoa& lightData) noexcept {
ShadowMapManager::ShadowTechnique ShadowMapManager::updateCascadeShadowMaps(FEngine& engine,
FView& view, FScene::RenderableSoa& renderableData, FScene::LightSoa& lightData,
ShadowMap::SceneInfo& sceneInfo) noexcept {
FScene* scene = view.getScene();
const CameraInfo& viewingCameraInfo = view.getCameraInfo();
uint8_t visibleLayers = view.getVisibleLayers();
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);
ShadowMap::SceneInfo sceneInfo;
if (!mCascadeShadowMaps.empty()) {
// Compute scene-dependent values shared across all cascades
const float3 dir = lightData.elementAt<FScene::DIRECTION>(0);
ShadowMap::computeSceneInfo(dir,
*scene, viewingCameraInfo, visibleLayers, sceneInfo);
// 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];
@@ -354,14 +358,14 @@ ShadowMapManager::ShadowTechnique ShadowMapManager::updateCascadeShadowMaps(
.vsm = view.hasVsm()
};
map.update(lightData, 0, viewingCameraInfo, shadowMapInfo, sceneInfo);
map.update(lightData, 0, viewingCameraInfo, shadowMapInfo, *scene, sceneInfo);
Frustum const& frustum = map.getCamera().getCullingFrustum();
FView::cullRenderables(engine.getJobSystem(), renderableData, frustum,
VISIBLE_DIR_SHADOW_RENDERABLE_BIT);
// note: normalBias is ignored for VSM
const float normalBias = lcm.getShadowNormalBias(0);
// 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();
mShadowMappingUniforms.shadowBias = float3{ 0, normalBias * texelSizeWorldSpace, 0 };
@@ -414,7 +418,6 @@ ShadowMapManager::ShadowTechnique ShadowMapManager::updateCascadeShadowMaps(
ShadowTechnique shadowTechnique{};
uint32_t directionalShadowsMask = 0;
uint32_t cascadeHasVisibleShadows = 0;
float screenSpaceShadowDistance = 0.0f;
for (size_t i = 0, c = mCascadeShadowMaps.size(); i < c; i++) {
auto& entry = mCascadeShadowMaps[i];
@@ -431,7 +434,11 @@ ShadowMapManager::ShadowTechnique ShadowMapManager::updateCascadeShadowMaps(
.vsm = view.hasVsm()
};
sceneInfo.csNearFar = { csSplitPosition[i], csSplitPosition[i + 1] };
shadowMap.update(lightData, 0, viewingCameraInfo, shadowMapInfo, sceneInfo);
shadowMap.update(lightData, 0,
viewingCameraInfo, shadowMapInfo,
*scene,sceneInfo);
if (shadowMap.hasVisibleShadows()) {
mShadowMappingUniforms.lightFromWorldMatrix[i] = shadowMap.getLightSpaceMatrix();
shadowTechnique |= ShadowTechnique::SHADOW_MAP;
@@ -440,7 +447,7 @@ ShadowMapManager::ShadowTechnique ShadowMapManager::updateCascadeShadowMaps(
}
// screen-space contact shadows for the directional light
screenSpaceShadowDistance = options.maxShadowDistance;
float screenSpaceShadowDistance = options.maxShadowDistance;
if (options.screenSpaceContactShadows) {
shadowTechnique |= ShadowTechnique::SCREEN_SPACE;
}
@@ -467,15 +474,15 @@ ShadowMapManager::ShadowTechnique ShadowMapManager::updateCascadeShadowMaps(
return shadowTechnique;
}
ShadowMapManager::ShadowTechnique ShadowMapManager::updateSpotShadowMaps(
FEngine& engine, FView& view, TypedUniformBuffer<ShadowUib>& shadowUb,
FScene::RenderableSoa& renderableData, FScene::LightSoa& lightData) noexcept {
ShadowMapManager::ShadowTechnique ShadowMapManager::updateSpotShadowMaps(FEngine& engine,
FView& view, FScene::RenderableSoa& renderableData, FScene::LightSoa& lightData,
ShadowMap::SceneInfo& sceneInfo, TypedUniformBuffer <ShadowUib>& shadowUb) noexcept {
ShadowTechnique shadowTechnique{};
const CameraInfo& viewingCameraInfo = view.getCameraInfo();
const uint16_t textureSize = mTextureRequirements.size;
// shadow-map shadows for point/spot lights
// shadow-map shadows for point/spotlights
auto& lcm = engine.getLightManager();
FScene::ShadowInfo* const shadowInfo = lightData.data<FScene::SHADOW_INFO>();
for (size_t i = 0, c = mSpotShadowMaps.size(); i < c; i++) {
@@ -486,40 +493,43 @@ ShadowMapManager::ShadowTechnique ShadowMapManager::updateSpotShadowMaps(
size_t l = entry.getLightIndex();
const size_t textureDimension = entry.getShadowOptions()->mapSize;
const ShadowMap::ShadowMapInfo layout{
const ShadowMap::ShadowMapInfo shadowMapInfo{
.zResolution = mTextureZResolution,
.atlasDimension = textureSize,
.textureDimension = (uint16_t)textureDimension,
.shadowDimension = (uint16_t)(textureDimension - 2),
.vsm = view.hasVsm()
};
shadowMap.update(lightData, l, viewingCameraInfo, layout, {});
shadowMap.update(lightData, l,
viewingCameraInfo, shadowMapInfo,
*view.getScene(), sceneInfo);
FLightManager::Instance light = lightData.elementAt<FScene::LIGHT_INSTANCE>(l);
if (shadowMap.hasVisibleShadows()) {
// Cull shadow casters
auto& s = shadowUb.edit();
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();
// note: normalBias is ignored for VSM
// note: normalBias is set to zero for VSM
const float3 dir = lightData.elementAt<FScene::DIRECTION>(l);
const float texelSizeWorldSpace = shadowMap.getTexelSizeWorldSpace();
const float normalBias = lcm.getShadowNormalBias(light);
const float normalBias = shadowMapInfo.vsm ? 0.0f : lcm.getShadowNormalBias(light);
s.directionShadowBias[i] = float4{ dir, normalBias * texelSizeWorldSpace };
shadowTechnique |= ShadowTechnique::SHADOW_MAP;
}
}
// screen-space contact shadows for point/spot lights
// screen-space contact shadows for point/spotlights
auto *pInstance = lightData.data<FScene::LIGHT_INSTANCE>();
for (size_t i = 0, c = lightData.size(); i < c; i++) {
// screen-space contact shadows
@@ -539,7 +549,7 @@ void ShadowMapManager::calculateTextureRequirements(FEngine& engine, FView& view
// Lay out the shadow maps. For now, we take the largest requested dimension and allocate a
// texture of that size. Each cascade / shadow map gets its own layer in the array texture.
// The directional shadow cascades start on layer 0, followed by spot lights.
// The directional shadow cascades start on layer 0, followed by spotlights.
uint8_t layer = 0;
uint32_t maxDimension = 0;
for (auto& entry : mCascadeShadowMaps) {

View File

@@ -120,12 +120,13 @@ private:
uint8_t levels = 0;
} mTextureRequirements;
ShadowTechnique updateCascadeShadowMaps(FEngine& engine, FView& view,
FScene::RenderableSoa& renderableData, FScene::LightSoa& lightData) noexcept;
ShadowTechnique updateCascadeShadowMaps(FEngine& engine,
FView& view, FScene::RenderableSoa& renderableData, FScene::LightSoa& lightData,
ShadowMap::SceneInfo& sceneInfo) noexcept;
ShadowTechnique updateSpotShadowMaps(FEngine& engine, FView& view,
TypedUniformBuffer<ShadowUib>& shadowUb,
FScene::RenderableSoa& renderableData, FScene::LightSoa& lightData) noexcept;
ShadowTechnique updateSpotShadowMaps(FEngine& engine,
FView& view, FScene::RenderableSoa& renderableData, FScene::LightSoa& lightData,
ShadowMap::SceneInfo& sceneInfo, TypedUniformBuffer<ShadowUib>& shadowUb) noexcept;
void calculateTextureRequirements(FEngine& engine, FView& view, FScene::LightSoa& lightData) noexcept;

View File

@@ -352,23 +352,23 @@ void FLightManager::setFalloff(Instance i, float falloff) noexcept {
void FLightManager::setSpotLightCone(Instance i, float inner, float outer) noexcept {
auto& manager = mManager;
if (i && isSpotLight(i)) {
// clamp the inner/outer angles to pi
float innerClamped = std::min(std::abs(inner), f::PI_2);
float outerClamped = std::min(std::abs(outer), f::PI_2);
// clamp the inner/outer angles to [0.5 degrees, 90 degrees]
float innerClamped = std::clamp(std::abs(inner), 0.5f * f::DEG_TO_RAD, f::PI_2);
float outerClamped = std::clamp(std::abs(outer), 0.5f * f::DEG_TO_RAD, f::PI_2);
// outer must always be bigger than inner
outerClamped = std::max(innerClamped, outerClamped);
// inner must always be smaller than outer
innerClamped = std::min(innerClamped, outerClamped);
float cosOuter = fast::cos(outerClamped);
float cosInner = fast::cos(innerClamped);
float cosOuterSquared = cosOuter * cosOuter;
float scale = 1 / std::max(1.0f / 1024.0f, cosInner - cosOuter);
float scale = 1.0f / std::max(1.0f / 1024.0f, cosInner - cosOuter);
float offset = -cosOuter * scale;
SpotParams& spotParams = manager[i].spotParams;
spotParams.outerClamped = outerClamped;
spotParams.cosOuterSquared = cosOuterSquared;
spotParams.sinInverse = 1 / std::sqrt(1 - cosOuterSquared);
spotParams.sinInverse = 1.0f / std::sin(outerClamped);
spotParams.scaleOffset = { scale, offset };
// we need to recompute the luminous intensity

View File

@@ -0,0 +1,13 @@
vec3 colorGrade(mediump sampler3D lut, const vec3 x) {
// Alexa LogC EI 1000
const float a = 5.555556;
const float b = 0.047996;
const float c = 0.244161 / log2(10.0);
const float d = 0.386036;
vec3 logc = c * log2(a * x + b) + d;
// Remap to sample pixel centers
logc = materialParams.lutSize.x + logc * materialParams.lutSize.y;
return textureLod(lut, logc, 0.0).rgb;
}

View File

@@ -79,8 +79,9 @@ fragment {
#include "../../../../shaders/src/dithering.fs"
#include "../../../../shaders/src/vignette.fs"
#include "colorGrading.fs"
void dummy(){ }
void dummy(){}
float starburst(const vec2 uv) {
// get an offset that continuously moves with the camera
@@ -123,68 +124,52 @@ vec3 bloom(const vec3 color) {
return result;
}
vec3 colorGrade(mediump sampler3D lut, const vec3 x) {
// Alexa LogC EI 1000
const float a = 5.555556;
const float b = 0.047996;
const float c = 0.244161 / log2(10.0);
const float d = 0.386036;
vec3 logc = c * log2(a * x + b) + d;
// Remap to sample pixel centers
logc = materialParams.lutSize.x + logc * materialParams.lutSize.y;
return textureLod(lut, logc, 0.0).rgb;
}
vec3 resolveFragment(const ivec2 uv) {
return texelFetch(materialParams_colorBuffer, uv, 0).rgb;
}
vec4 resolveAlphaFragment(const ivec2 uv) {
return texelFetch(materialParams_colorBuffer, uv, 0);
}
vec4 resolve() {
vec4 resolveFragment(const ivec2 uv) {
#if POST_PROCESS_OPAQUE
vec4 color = vec4(resolveFragment(ivec2(getUV())), 1.0);
if (materialParams.bloom.x > 0.0) {
color.rgb = bloom(color.rgb);
}
if (materialParams.vignette.x < MEDIUMP_FLT_MAX) {
highp vec2 uv = getUV() * frameUniforms.resolution.zw;
color.rgb = vignette(color.rgb, uv, materialParams.vignette, materialParams.vignetteColor);
}
color.rgb = colorGrade(materialParams_lut, color.rgb);
if (materialParams.fxaa > 0) {
color.a = luminance(color.rgb);
}
return vec4(texelFetch(materialParams_colorBuffer, uv, 0).rgb, 1.0);
#else
vec4 color = resolveAlphaFragment(ivec2(getUV()));
color.rgb /= color.a + FLT_EPS;
if (materialParams.bloom.x > 0.0) {
color.rgb = bloom(color.rgb);
}
if (materialParams.vignette.x < MEDIUMP_FLT_MAX) {
highp vec2 uv = getUV() * frameUniforms.resolution.zw;
color.rgb = vignette(color.rgb, uv, materialParams.vignette, materialParams.vignetteColor);
}
color.rgb = colorGrade(materialParams_lut, color.rgb);
color.rgb *= color.a + FLT_EPS;
#endif
vec4 color = texelFetch(materialParams_colorBuffer, uv, 0);
color.rgb *= 1.0 / (color.a + FLT_EPS);
return color;
#endif
}
void postProcess(inout PostProcessInputs postProcess) {
postProcess.color = resolve();
if (materialParams.dithering > 0) {
vec4 dithered = dither(postProcess.color, materialParams.temporalNoise);
#if POST_PROCESS_OPAQUE
postProcess.color.rgb = dithered.rgb;
#else
postProcess.color = dithered;
#endif
vec4 color = resolveFragment(ivec2(getUV()));
// Bloom
if (materialParams.bloom.x > 0.0) {
color.rgb = bloom(color.rgb);
}
// Vignette
if (materialParams.vignette.x < MEDIUMP_FLT_MAX) {
highp vec2 uv = getUV() * frameUniforms.resolution.zw;
color.rgb = vignette(color.rgb, uv, materialParams.vignette, materialParams.vignetteColor);
}
// Color grading
color.rgb = colorGrade(materialParams_lut, color.rgb);
// output in premultiplied alpha
#if !POST_PROCESS_OPAQUE
color.rgb *= color.a + FLT_EPS;
#endif
// dithering
if (materialParams.dithering > 0) {
color = dither(color, materialParams.temporalNoise);
}
// kill alpha computations when opaque / fxaa luminance
#if POST_PROCESS_OPAQUE
color.a = 1.0;
if (materialParams.fxaa > 0) {
color.a = luminance(color.rgb);
}
#endif
postProcess.color = color;
}
}

View File

@@ -71,64 +71,51 @@ fragment {
#include "../../../../shaders/src/dithering.fs"
#include "../../../../shaders/src/vignette.fs"
#include "colorGrading.fs"
vec3 colorGrade(mediump sampler3D lut, const vec3 x) {
// Alexa LogC EI 1000
const float a = 5.555556;
const float b = 0.047996;
const float c = 0.244161 / log2(10.0);
const float d = 0.386036;
vec3 logc = c * log2(a * x + b) + d;
void dummy(){}
// Remap to sample pixel centers
logc = materialParams.lutSize.x + logc * materialParams.lutSize.y;
return textureLod(lut, logc, 0.0).rgb;
}
vec3 resolveFragment(const ivec2 uv) {
return subpassLoad(materialParams_colorBuffer).rgb;
}
vec4 resolveAlphaFragment(const ivec2 uv) {
return subpassLoad(materialParams_colorBuffer);
}
vec4 resolve() {
vec4 resolveFragment() {
#if POST_PROCESS_OPAQUE
vec4 color = vec4(resolveFragment(ivec2(getUV())), 1.0);
if (materialParams.vignette.x < MEDIUMP_FLT_MAX) {
highp vec2 uv = getUV() * frameUniforms.resolution.zw;
color.rgb = vignette(color.rgb, uv, materialParams.vignette, materialParams.vignetteColor);
}
color.rgb = colorGrade(materialParams_lut, color.rgb);
if (materialParams.fxaa > 0) {
color.a = luminance(color.rgb);
}
return vec4(subpassLoad(materialParams_colorBuffer).rgb, 1.0);
#else
vec4 color = resolveAlphaFragment(ivec2(getUV()));
color.rgb /= color.a + FLT_EPS;
if (materialParams.vignette.x < MEDIUMP_FLT_MAX) {
highp vec2 uv = getUV() * frameUniforms.resolution.zw;
color.rgb = vignette(color.rgb, uv, materialParams.vignette, materialParams.vignetteColor);
}
color.rgb = colorGrade(materialParams_lut, color.rgb);
color.rgb *= color.a + FLT_EPS;
vec4 color = subpassLoad(materialParams_colorBuffer);
color.rgb *= 1.0 / (color.a + FLT_EPS);
return color;
#endif
return color;
}
void postProcess(inout PostProcessInputs postProcess) {
vec4 color = resolveFragment();
// Vignette
if (materialParams.vignette.x < MEDIUMP_FLT_MAX) {
highp vec2 uv = getUV() * frameUniforms.resolution.zw;
color.rgb = vignette(color.rgb, uv, materialParams.vignette, materialParams.vignetteColor);
}
void postProcess(inout PostProcessInputs postProcess) {
vec4 color = resolve();
if (materialParams.dithering > 0) {
vec4 dithered = dither(color, materialParams.temporalNoise);
#if POST_PROCESS_OPAQUE
color.rgb = dithered.rgb;
#else
color = dithered;
// Color grading
color.rgb = colorGrade(materialParams_lut, color.rgb);
// output in premultiplied alpha
#if !POST_PROCESS_OPAQUE
color.rgb *= color.a + FLT_EPS;
#endif
}
postProcess.tonemappedOutput = color;
// dithering
if (materialParams.dithering > 0) {
color = dither(color, materialParams.temporalNoise);
}
// kill alpha computations when opaque / fxaa luminance
#if POST_PROCESS_OPAQUE
color.a = 1.0;
if (materialParams.fxaa > 0) {
color.a = luminance(color.rgb);
}
#endif
postProcess.tonemappedOutput = color;
}
}

View File

@@ -1,12 +1,12 @@
Pod::Spec.new do |spec|
spec.name = "Filament"
spec.version = "1.12.10"
spec.version = "1.13.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.12.10/filament-v1.12.10-ios.tgz" }
spec.source = { :http => "https://github.com/google/filament/releases/download/v1.13.0/filament-v1.13.0-ios.tgz" }
# Fix linking error with Xcode 12; we do not yet support the simulator on Apple silicon.
spec.pod_target_xcconfig = {

View File

@@ -45,6 +45,7 @@ using namespace utils;
@implementation FILViewController {
CADisplayLink* _displayLink;
CFTimeInterval _startTime;
viewer::RemoteServer* _server;
viewer::AutomationEngine* _automation;
@@ -102,6 +103,7 @@ using namespace utils;
[self stopDisplayLink];
// Call our render method 60 times a second.
_startTime = CACurrentMediaTime();
_displayLink = [CADisplayLink displayLinkWithTarget:self selector:@selector(render)];
_displayLink.preferredFramesPerSecond = 60;
[_displayLink addToRunLoop:NSRunLoop.currentRunLoop forMode:NSDefaultRunLoopMode];
@@ -230,7 +232,8 @@ using namespace utils;
auto* animator = self.modelView.animator;
if (animator) {
if (animator->getAnimationCount() > 0) {
animator->applyAnimation(0, CACurrentMediaTime());
CFTimeInterval elapsedTime = CACurrentMediaTime() - _startTime;
animator->applyAnimation(0, static_cast<float>(elapsedTime));
}
animator->updateBoneMatrices();
}

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 = 12;
static constexpr size_t MATERIAL_VERSION = 13;
/**
* Supported shading models

View File

@@ -88,7 +88,7 @@ struct PerViewUib { // NOLINT(cppcoreguidelines-pro-type-member-init)
math::float4 userTime; // time(s), (double)time - (float)time, 0, 0
float iblRoughnessOneLevel; // level for roughness == 1
float cameraFar; // camera *culling* far-plane distance (projection far is at +inf)
float cameraFar; // camera *culling* far-plane distance, always positive (projection far is at +inf)
float refractionLodOffset;
// bit 0: directional (sun) shadow enabled
@@ -129,8 +129,8 @@ struct PerViewUib { // NOLINT(cppcoreguidelines-pro-type-member-init)
float vsmReserved0;
float lodBias;
float reserved1;
float reserved2;
float oneOverFarMinusNear; // 1 / (f-n), always positive
float nearOverFarMinusNear; // n / (f-n), always positive
float reserved3;
// bring PerViewUib to 2 KiB
@@ -164,13 +164,14 @@ static_assert(sizeof(PerRenderableUib) % 256 == 0, "sizeof(Transform) should be
struct LightsUib {
static constexpr utils::StaticString _name{ "LightsUniforms" };
math::float4 positionFalloff; // { float3(pos), 1/falloff^2 }
math::half4 color; // { half3(col), 0 }
math::half4 directionIES; // { half3(dir), IES index }
math::half2 spotScaleOffset; // { scale, offset }
float intensity; // float
uint32_t typeShadow; // 0x00.ll.ii.ct (t: 0=point, 1=spot, c:contact, ii: index, ll: layer)
uint32_t channels; // 0x000c00ll (ll: light channels, c: caster)
math::float4 reserved; // 0
math::float3 direction; // dir
float reserved1; // 0
math::half4 colorIES; // { half3(col), IES index }
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 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);

View File

@@ -53,7 +53,7 @@ namespace filament {
//
// Standard variants:
// +-----+-----+-----+-----+-----+-----+-----+-----+
// | 0 | VSM | FOG | 0 | SKN | SRE | DYN | DIR | 64 (-24)
// | 0 | VSM | FOG | 0 | SKN | SRE | DYN | DIR | 40 (-24)
// +-----+-----+-----+-----+-----+-----+-----+-----+
// Vertex shader 0 0 0 X X X X
// Fragment shader X X 0 0 X X X
@@ -62,14 +62,18 @@ namespace filament {
//
// Depth variants:
// +-----+-----+-----+-----+-----+-----+-----+-----+
// | 0 | VSM | PCK | 1 | SKN | 0 | 0 | 0 | 8 (-58)
// | 0 | VSM | PCK | 1 | SKN | 0 | 0 | 0 | 6 (-58)
// +-----+-----+-----+-----+-----+-----+-----+-----+
// Vertex depth X 0 1 X 0 0 0
// Fragment depth X X 1 0 0 0 0
// Reserved 1 1 1 X 0 0 0
// Reserved X X 1 X X X 1
// Reserved X X 1 X X 1 X
// Reserved X X 1 X 1 X X
// Reserved X X 1 X 0 0 1
// Reserved X X 1 X 0 1 0
// Reserved X X 1 X 0 1 1
// Reserved X X 1 X 1 0 0
// Reserved X X 1 X 1 0 1
// Reserved X X 1 X 1 1 0
// Reserved X X 1 X 1 1 1
//
// 46 variants used, 82 reserved
//
@@ -87,14 +91,16 @@ namespace filament {
static constexpr uint8_t PICKING = 0x20; // picking (depth)
static constexpr uint8_t VSM = 0x40; // variance shadow maps
static constexpr uint8_t DEPTH_MASK = DIRECTIONAL_LIGHTING |
DYNAMIC_LIGHTING |
SHADOW_RECEIVER |
DEPTH;
static constexpr uint8_t STANDARD_VARIANT = 0u;
static constexpr uint8_t STANDARD_MASK = DEPTH;
// the depth variant deactivates all variants that make no sense when writing the depth
// only -- essentially, all fragment-only variants.
static constexpr uint8_t DEPTH_VARIANT = DEPTH;
static constexpr uint8_t DEPTH_MASK = DIRECTIONAL_LIGHTING |
DYNAMIC_LIGHTING |
SHADOW_RECEIVER |
DEPTH;
// this mask filters out the lighting variants
static constexpr uint8_t UNLIT_MASK = SKINNING_OR_MORPHING | FOG;
@@ -118,22 +124,31 @@ namespace filament {
inline void setVsm(bool v) noexcept { set(v, VSM); }
inline static constexpr bool isValidDepthVariant(uint8_t variantKey) noexcept {
// VSM and PICKING are mutually exclusive for DEPTH variants
// Can't have VSM and PICKING together with DEPTH variants
constexpr uint8_t RESERVED_MASK = VSM | PICKING | DEPTH;
constexpr uint8_t RESERVED_VALUE = VSM | PICKING | DEPTH;
return (variantKey & DEPTH_MASK) == DEPTH_VARIANT &&
variantKey != 0b1110000u &&
variantKey != 0b1111000u;
((variantKey & RESERVED_MASK) != RESERVED_VALUE);
}
inline static constexpr bool isValidStandardVariant(uint8_t variantKey) noexcept {
// can't have shadow receiver if we don't have any lighting
constexpr uint8_t RESERVED0_MASK = SHADOW_RECEIVER | DYNAMIC_LIGHTING | DIRECTIONAL_LIGHTING;
constexpr uint8_t RESERVED0_VALUE = SHADOW_RECEIVER;
// can't have VSM without shadow receiver
constexpr uint8_t RESERVED1_MASK = VSM | SHADOW_RECEIVER;
constexpr uint8_t RESERVED1_VALUE = VSM;
return (variantKey & STANDARD_MASK) == STANDARD_VARIANT &&
(variantKey & RESERVED0_MASK) != RESERVED0_VALUE &&
(variantKey & RESERVED1_MASK) != RESERVED1_VALUE;
}
static constexpr bool isReserved(uint8_t variantKey) noexcept {
// reserved variants that should just be skipped
// 1. If the DEPTH bit is set, then it must be a valid depth variant. Otherwise, the
// variant is reserved.
// 2. If SRE is set, either DYN or DIR must also be set (it makes no sense to have
// shadows without lights).
// 3. If VSM is set, then SRE must be set.
return ((variantKey & DEPTH) && !isValidDepthVariant(variantKey)) ||
(variantKey & 0b0010111u) == 0b0000100u ||
(variantKey & 0b1010100u) == 0b1000000u;
if (variantKey & DEPTH) {
return !isValidDepthVariant(variantKey);
}
return !isValidStandardVariant(variantKey);
}
static constexpr uint8_t filterVariantVertex(uint8_t variantKey) noexcept {

View File

@@ -121,10 +121,16 @@ void GLSLPostProcessor::spirvToToMsl(const SpirvBlob *spirv, std::string *outMsl
CompilerMSL::Options mslOptions = {};
mslOptions.platform = platform,
mslOptions.msl_version = CompilerMSL::Options::make_msl_version(1, 1);
mslOptions.msl_version = config.shaderModel == filament::backend::ShaderModel::GL_ES_30 ?
CompilerMSL::Options::make_msl_version(2, 0) : CompilerMSL::Options::make_msl_version(2, 2);
if (config.shaderModel == filament::backend::ShaderModel::GL_ES_30) {
if (config.hasFramebufferFetch) {
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 == filament::backend::ShaderModel::GL_CORE_41) {
mslOptions.msl_version = CompilerMSL::Options::make_msl_version(2, 3);
}
}
mslCompiler.set_msl_options(mslOptions);

View File

@@ -110,8 +110,8 @@ UniformInterfaceBlock const& UibGenerator::getPerViewUib() noexcept {
.add("vsmReserved0", 1, UniformInterfaceBlock::Type::FLOAT)
.add("lodBias", 1, UniformInterfaceBlock::Type::FLOAT)
.add("reserved1", 1, UniformInterfaceBlock::Type::FLOAT)
.add("reserved2", 1, UniformInterfaceBlock::Type::FLOAT)
.add("oneOverFarMinusNear", 1, UniformInterfaceBlock::Type::FLOAT, Precision::HIGH)
.add("nearOverFarMinusNear", 1, UniformInterfaceBlock::Type::FLOAT, Precision::HIGH)
.add("reserved3", 1, UniformInterfaceBlock::Type::FLOAT)
// bring PerViewUib to 2 KiB

View File

@@ -22,6 +22,7 @@
#include <utils/Log.h>
#include <cstring>
#include <limits>
#include <memory>
#include <sstream>

View File

@@ -21,6 +21,7 @@
#include <utils/Panic.h>
#include <algorithm>
#include <limits>
#include <memory>
#include <type_traits>
#include <utility>

View File

@@ -20,6 +20,7 @@
#include <utils/Log.h>
#include <cstring>
#include <vector>
using namespace utils;

View File

@@ -37,18 +37,8 @@ void main() {
#endif
#if defined(HAS_VSM)
// For VSM, we use the linear light space Z coordinate as the depth metric, which works for both
// directional and spot lights.
// The value is guaranteed to be between [0, -zfar] by construction of viewFromWorldMatrix,
// (see ShadowMap.cpp).
highp float z = (frameUniforms.viewFromWorldMatrix * vec4(vertex_worldPosition, 1.0)).z;
// rescale the depth between [0, 1]
highp float depth = -z / abs(frameUniforms.cameraFar);
// We use positive only EVSM which helps a lot with light bleeding.
depth = depth * 2.0 - 1.0;
depth = exp(frameUniforms.vsmExponent * depth);
// interpolated depth is stored in vertex_worldPosition.w (see depth_main.vs / main.vs)
highp float depth = exp(vertex_worldPosition.w);
// computes the moments
// See GPU Gems 3

View File

@@ -25,7 +25,21 @@ void main() {
#endif
#if defined(HAS_VSM)
vertex_worldPosition = material.worldPosition.xyz;
// For VSM, we use the linear light-space Z coordinate as the depth metric, which works for both
// directional and spot lights and can be safely interpolated.
// The value is guaranteed to be between [-znear, -zfar] by construction of viewFromWorldMatrix,
// (see ShadowMap.cpp).
// Use vertex_worldPosition.w which is otherwise not used to store the interpolated
// light-space depth.
highp float z = (frameUniforms.viewFromWorldMatrix * vec4(material.worldPosition.xyz, 1.0)).z;
// rescale [near, far] to [0, 1]
highp float depth = -z * frameUniforms.oneOverFarMinusNear - frameUniforms.nearOverFarMinusNear;
// EVSM pre-mapping
depth = frameUniforms.vsmExponent * (depth * 2.0 - 1.0);
vertex_worldPosition.w = depth;
#endif
// this must happen before we compensate for vulkan below

View File

@@ -81,19 +81,6 @@ highp vec2 uvToRenderTargetUV(highp vec2 uv) {
return uv;
}
#if defined(HAS_SHADOWING) && defined(HAS_DIRECTIONAL_LIGHTING)
highp vec3 getLightSpacePosition() {
#if defined(HAS_VSM)
// For VSM, do not project the Z coordinate. It remains as linear Z in light space.
// See the computeVsmLightSpaceMatrix comments in ShadowMap.cpp.
return vec3(vertex_lightSpacePosition.xy * (1.0 / vertex_lightSpacePosition.w),
vertex_lightSpacePosition.z);
#else
return vertex_lightSpacePosition.xyz * (1.0 / vertex_lightSpacePosition.w);
#endif
}
#endif
/**
* Returns the normalized [0, 1] viewport coordinates with the origin at the viewport's bottom-left.
* Z coordinate is in the [0, 1] range as well.
@@ -112,19 +99,11 @@ highp vec3 getNormalizedViewportCoord2() {
}
#if defined(HAS_SHADOWING) && defined(HAS_DYNAMIC_LIGHTING)
highp vec3 getSpotLightSpacePosition(uint index) {
highp vec4 getSpotLightSpacePosition(uint index) {
vec3 dir = shadowUniforms.directionShadowBias[index].xyz;
float bias = shadowUniforms.directionShadowBias[index].w;
highp vec4 position = computeLightSpacePosition(vertex_worldPosition,
return computeLightSpacePosition(vertex_worldPosition.xyz,
vertex_worldNormal, dir, bias, shadowUniforms.spotLightFromWorldMatrix[index]);
#if defined(HAS_VSM)
// For VSM, do not project the Z coordinate. It remains as linear Z in light space.
// See the computeVsmLightSpaceMatrix comments in ShadowMap.cpp.
return vec3(position.xy * (1.0 / position.w), position.z);
#else
return position.xyz * (1.0 / position.w);
#endif
}
#endif
@@ -146,25 +125,18 @@ uint getShadowCascade() {
#if defined(HAS_SHADOWING) && defined(HAS_DIRECTIONAL_LIGHTING)
highp vec3 getCascadeLightSpacePosition(uint cascade) {
highp vec4 getCascadeLightSpacePosition(uint cascade) {
// For the first cascade, return the interpolated light space position.
// This branch will be coherent (mostly) for neighboring fragments, and it's worth avoiding
// the matrix multiply inside computeLightSpacePosition.
if (cascade == 0u) {
// Note: this branch may cause issues with derivatives
return getLightSpacePosition();
return vertex_lightSpacePosition;
}
highp vec4 pos = computeLightSpacePosition(getWorldPosition(), getWorldNormalVector(),
return computeLightSpacePosition(getWorldPosition(), getWorldNormalVector(),
frameUniforms.lightDirection, frameUniforms.shadowBias.y,
frameUniforms.lightFromWorldMatrix[cascade]);
#if defined(HAS_VSM)
// For VSM, do not project the Z coordinate. It remains as linear Z in light space.
// See the computeVsmLightSpaceMatrix comments in ShadowMap.cpp.
return vec3(pos.xy * (1.0 / pos.w), pos.z);
#else
return pos.xyz * (1.0 / pos.w);
#endif
}
#endif

View File

@@ -2,7 +2,7 @@
// Attributes and uniforms
//------------------------------------------------------------------------------
LAYOUT_LOCATION(4) in highp vec3 vertex_worldPosition;
LAYOUT_LOCATION(4) in highp vec4 vertex_worldPosition;
#if defined(HAS_ATTRIBUTE_TANGENTS)
LAYOUT_LOCATION(5) SHADING_INTERPOLATION in mediump vec3 vertex_worldNormal;

View File

@@ -56,7 +56,8 @@ layout(location = LOCATION_CUSTOM6) in vec4 mesh_custom6;
layout(location = LOCATION_CUSTOM7) in vec4 mesh_custom7;
#endif
LAYOUT_LOCATION(4) out highp vec3 vertex_worldPosition;
LAYOUT_LOCATION(4) out highp vec4 vertex_worldPosition;
#if defined(HAS_ATTRIBUTE_TANGENTS)
LAYOUT_LOCATION(5) SHADING_INTERPOLATION out mediump vec3 vertex_worldNormal;
#if defined(MATERIAL_NEEDS_TBN)

View File

@@ -110,7 +110,7 @@ float getDistanceAttenuation(const highp vec3 posToLight, float falloff) {
return attenuation * 1.0 / max(distanceSquare, 1e-4);
}
float getAngleAttenuation(const vec3 lightDir, const vec3 l, const vec2 scaleOffset) {
float getAngleAttenuation(const highp vec3 lightDir, const highp vec3 l, const highp vec2 scaleOffset) {
float cd = dot(lightDir, l);
float attenuation = saturate(cd * scaleOffset.x + scaleOffset.y);
return attenuation * attenuation;
@@ -132,26 +132,23 @@ Light getLight(const uint index) {
highp mat4 data = lightsUniforms.lights[lightIndex];
highp vec4 positionFalloff = data[0];
vec4 color = vec4(
unpackHalf2x16(floatBitsToUint(data[1][0])),
unpackHalf2x16(floatBitsToUint(data[1][1]))
highp vec3 direction = data[1].xyz;
vec4 colorIES = vec4(
unpackHalf2x16(floatBitsToUint(data[2][0])),
unpackHalf2x16(floatBitsToUint(data[2][1]))
);
vec4 directionIES = vec4(
unpackHalf2x16(floatBitsToUint(data[1][2])),
unpackHalf2x16(floatBitsToUint(data[1][3]))
);
vec2 scaleOffset = unpackHalf2x16(floatBitsToUint(data[2][0]));
highp float intensity = data[2][1];
highp uint typeShadow = floatBitsToUint(data[2][2]);
highp uint channels = floatBitsToUint(data[2][3]);
highp vec2 scaleOffset = data[2].zw;
highp float intensity = data[3][1];
highp uint typeShadow = floatBitsToUint(data[3][2]);
highp uint channels = floatBitsToUint(data[3][3]);
// poition-to-light vector
highp vec3 worldPosition = vertex_worldPosition;
highp vec3 worldPosition = vertex_worldPosition.xyz;
highp vec3 posToLight = positionFalloff.xyz - worldPosition;
// and populate the Light structure
Light light;
light.colorIntensity.rgb = color.rgb;
light.colorIntensity.rgb = colorIES.rgb;
light.colorIntensity.w = computePreExposedIntensity(intensity, frameUniforms.exposure);
light.l = normalize(posToLight);
light.attenuation = getDistanceAttenuation(posToLight, positionFalloff.w);
@@ -165,7 +162,7 @@ Light getLight(const uint index) {
uint type = typeShadow & 0x1u;
if (type == LIGHT_TYPE_SPOT) {
light.attenuation *= getAngleAttenuation(-directionIES.xyz, light.l, scaleOffset);
light.attenuation *= getAngleAttenuation(-direction, light.l, scaleOffset);
light.contactShadows = bool(typeShadow & 0x10u);
light.shadowIndex = (typeShadow >> 8u) & 0xFFu;
light.shadowLayer = (typeShadow >> 16u) & 0xFFu;

View File

@@ -82,13 +82,14 @@ void main() {
#endif
// The world position can be changed by the user in materialVertex()
vertex_worldPosition = material.worldPosition.xyz;
vertex_worldPosition.xyz = material.worldPosition.xyz;
#ifdef HAS_ATTRIBUTE_TANGENTS
vertex_worldNormal = material.worldNormal;
#endif
#if defined(HAS_SHADOWING) && defined(HAS_DIRECTIONAL_LIGHTING)
vertex_lightSpacePosition = computeLightSpacePosition(vertex_worldPosition, vertex_worldNormal,
vertex_lightSpacePosition = computeLightSpacePosition(vertex_worldPosition.xyz, vertex_worldNormal,
frameUniforms.lightDirection, frameUniforms.shadowBias.y, getLightFromWorldMatrix());
#endif
@@ -108,6 +109,24 @@ void main() {
gl_Position.z = gl_Position.z * -0.5 + 0.5;
#endif
#if defined(HAS_VSM)
// For VSM, we use the linear light-space Z coordinate as the depth metric, which works for both
// directional and spot lights and can be safely interpolated.
// The value is guaranteed to be between [-znear, -zfar] by construction of viewFromWorldMatrix,
// (see ShadowMap.cpp).
// Use vertex_worldPosition.w which is otherwise not used to store the interpolated
// light-space depth.
highp float z = (frameUniforms.viewFromWorldMatrix * vec4(material.worldPosition.xyz, 1.0)).z;
// rescale [near, far] to [0, 1]
highp float depth = -z * frameUniforms.oneOverFarMinusNear - frameUniforms.nearOverFarMinusNear;
// EVSM pre-mapping
depth = frameUniforms.vsmExponent * (depth * 2.0 - 1.0);
vertex_worldPosition.w = depth;
#endif
// this must happen before we compensate for vulkan below
vertex_position = gl_Position;

View File

@@ -32,7 +32,7 @@ void computeShadingParams() {
#endif
#endif
shading_position = vertex_worldPosition;
shading_position = vertex_worldPosition.xyz;
shading_view = normalize(frameUniforms.cameraPosition - shading_position);
// we do this so we avoid doing (matrix multiply), but we burn 4 varyings:

View File

@@ -2,31 +2,19 @@
// Shadowing configuration
//------------------------------------------------------------------------------
#define SHADOW_SAMPLING_PCF_HARD 0
#define SHADOW_SAMPLING_PCF_LOW 1
#define SHADOW_SAMPLING_PCF_MEDIUM 2
#define SHADOW_SAMPLING_PCF_HIGH 3
#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_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_RECEIVER_PLANE_DEPTH_BIAS_MIN_SAMPLING_METHOD SHADOW_SAMPLING_PCF_MEDIUM
#define SHADOW_SAMPLING_METHOD SHADOW_SAMPLING_PCF_LOW
#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
#if SHADOW_SAMPLING_ERROR == SHADOW_SAMPLING_ERROR_ENABLED
#undef SHADOW_RECEIVER_PLANE_DEPTH_BIAS
#define SHADOW_RECEIVER_PLANE_DEPTH_BIAS SHADOW_RECEIVER_PLANE_DEPTH_BIAS_ENABLED
#elif SHADOW_SAMPLING_METHOD < SHADOW_RECEIVER_PLANE_DEPTH_BIAS_MIN_SAMPLING_METHOD
#undef SHADOW_RECEIVER_PLANE_DEPTH_BIAS
#define SHADOW_RECEIVER_PLANE_DEPTH_BIAS SHADOW_RECEIVER_PLANE_DEPTH_BIAS_DISABLED
#endif
//------------------------------------------------------------------------------
// Shadow sampling methods
//------------------------------------------------------------------------------
@@ -61,9 +49,7 @@ float samplingBias(float depth, const vec2 rpdb, const highp vec2 texelSize) {
float sampleDepth(const mediump sampler2DArrayShadow map, const uint layer,
const highp vec2 base, const highp vec2 dudv, float depth, vec2 rpdb) {
#if SHADOW_RECEIVER_PLANE_DEPTH_BIAS == SHADOW_RECEIVER_PLANE_DEPTH_BIAS_ENABLED
#if SHADOW_SAMPLING_METHOD >= SHADOW_RECEIVER_PLANE_DEPTH_BIAS_MIN_SAMPLING_METHOD
depth += dot(dudv, rpdb);
#endif
#endif
// depth must be clamped to support floating-point depth formats. This is to avoid comparing a
// value from the depth texture (which is never greater than 1.0) with a greater-than-one
@@ -77,7 +63,7 @@ float ShadowSample_Hard(const mediump sampler2DArrayShadow map, const uint layer
highp vec2 texelSize = vec2(1.0) / size;
vec2 rpdb = computeReceiverPlaneDepthBias(position);
float depth = samplingBias(position.z, rpdb, texelSize);
return texture(map, vec4(position.xy, layer, saturate(depth)));
return sampleDepth(map,layer, position.xy, vec2(0.0f), depth, rpdb);
}
#endif
@@ -86,6 +72,8 @@ float ShadowSample_PCF_Low(const mediump sampler2DArrayShadow map, const uint la
const highp vec2 size, highp vec3 position) {
// Castaño, 2013, "Shadow Mapping Summary Part 1"
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));
@@ -104,9 +92,6 @@ float ShadowSample_PCF_Low(const mediump sampler2DArrayShadow map, const uint la
u *= texelSize.x;
v *= texelSize.y;
vec2 rpdb = computeReceiverPlaneDepthBias(position);
float depth = samplingBias(position.z, rpdb, texelSize);
float sum = 0.0;
sum += uw.x * vw.x * sampleDepth(map, layer, base, vec2(u.x, v.x), depth, rpdb);
@@ -119,118 +104,6 @@ float ShadowSample_PCF_Low(const mediump sampler2DArrayShadow map, const uint la
}
#endif
#if SHADOW_SAMPLING_METHOD == SHADOW_SAMPLING_PCF_MEDIUM
float ShadowSample_PCF_Medium(const mediump sampler2DArrayShadow map, const uint layer,
const highp vec2 size, highp vec3 position) {
// Castaño, 2013, "Shadow Mapping Summary Part 1"
highp vec2 texelSize = vec2(1.0) / size;
// 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);
vec3 uw = vec3(4.0 - 3.0 * st.x, 7.0, 1.0 + 3.0 * st.x);
vec3 vw = vec3(4.0 - 3.0 * st.y, 7.0, 1.0 + 3.0 * st.y);
highp vec3 u = vec3((3.0 - 2.0 * st.x) / uw.x - 2.0, (3.0 + st.x) / uw.y, st.x / uw.z + 2.0);
highp vec3 v = vec3((3.0 - 2.0 * st.y) / vw.x - 2.0, (3.0 + st.y) / vw.y, st.y / vw.z + 2.0);
u *= texelSize.x;
v *= texelSize.y;
vec2 rpdb = computeReceiverPlaneDepthBias(position);
float depth = samplingBias(position.z, rpdb, texelSize);
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.z * vw.x * sampleDepth(map, layer, base, vec2(u.z, 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);
sum += uw.z * vw.y * sampleDepth(map, layer, base, vec2(u.z, v.y), depth, rpdb);
sum += uw.x * vw.z * sampleDepth(map, layer, base, vec2(u.x, v.z), depth, rpdb);
sum += uw.y * vw.z * sampleDepth(map, layer, base, vec2(u.y, v.z), depth, rpdb);
sum += uw.z * vw.z * sampleDepth(map, layer, base, vec2(u.z, v.z), depth, rpdb);
return sum * (1.0 / 144.0);
}
#endif
#if SHADOW_SAMPLING_METHOD == SHADOW_SAMPLING_PCF_HIGH
float ShadowSample_PCF_High(const mediump sampler2DArrayShadow map, const uint layer,
const highp vec2 size, highp vec3 position) {
// Castaño, 2013, "Shadow Mapping Summary Part 1"
highp vec2 texelSize = vec2(1.0) / size;
// 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);
vec4 uw = vec4(
5.0 * st.x - 6.0,
11.0 * st.x - 28.0,
-(11.0 * st.x + 17.0),
-(5.0 * st.x + 1.0));
vec4 vw = vec4(
5.0 * st.y - 6.0,
11.0 * st.y - 28.0,
-(11.0 * st.y + 17.0),
-(5.0 * st.y + 1.0));
vec4 u = vec4(
(4.0 * st.x - 5.0) / uw.x - 3.0,
(4.0 * st.x - 16.0) / uw.y - 1.0,
-(7.0 * st.x + 5.0) / uw.z + 1.0,
-st.x / uw.w + 3.0);
vec4 v = vec4(
(4.0 * st.y - 5.0) / vw.x - 3.0,
(4.0 * st.y - 16.0) / vw.y - 1.0,
-(7.0 * st.y + 5.0) / vw.z + 1.0,
-st.y / vw.w + 3.0);
u *= texelSize.x;
v *= texelSize.y;
vec2 rpdb = computeReceiverPlaneDepthBias(position);
float depth = samplingBias(position.z, rpdb, texelSize);
highp 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.z * vw.x * sampleDepth(map, layer, base, vec2(u.z, v.x), depth, rpdb);
sum += uw.w * vw.x * sampleDepth(map, layer, base, vec2(u.w, 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);
sum += uw.z * vw.y * sampleDepth(map, layer, base, vec2(u.z, v.y), depth, rpdb);
sum += uw.w * vw.y * sampleDepth(map, layer, base, vec2(u.w, v.y), depth, rpdb);
sum += uw.x * vw.z * sampleDepth(map, layer, base, vec2(u.x, v.z), depth, rpdb);
sum += uw.y * vw.z * sampleDepth(map, layer, base, vec2(u.y, v.z), depth, rpdb);
sum += uw.z * vw.z * sampleDepth(map, layer, base, vec2(u.z, v.z), depth, rpdb);
sum += uw.w * vw.z * sampleDepth(map, layer, base, vec2(u.w, v.z), depth, rpdb);
sum += uw.x * vw.w * sampleDepth(map, layer, base, vec2(u.x, v.w), depth, rpdb);
sum += uw.y * vw.w * sampleDepth(map, layer, base, vec2(u.y, v.w), depth, rpdb);
sum += uw.z * vw.w * sampleDepth(map, layer, base, vec2(u.z, v.w), depth, rpdb);
sum += uw.w * vw.w * sampleDepth(map, layer, base, vec2(u.w, v.w), depth, rpdb);
return sum * (1.0 / 2704.0);
}
#endif
//------------------------------------------------------------------------------
// Screen-space Contact Shadows
//------------------------------------------------------------------------------
@@ -347,30 +220,30 @@ float chebyshevUpperBound(const highp vec2 moments, const highp float mean,
// PCF sampling
float shadow(const mediump sampler2DArrayShadow shadowMap,
const uint layer, const highp vec3 shadowPosition) {
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, shadowPosition);
return ShadowSample_Hard(shadowMap, layer, size, position);
#elif SHADOW_SAMPLING_METHOD == SHADOW_SAMPLING_PCF_LOW
return ShadowSample_PCF_Low(shadowMap, layer, size, shadowPosition);
#elif SHADOW_SAMPLING_METHOD == SHADOW_SAMPLING_PCF_MEDIUM
return ShadowSample_PCF_Medium(shadowMap, layer, size, shadowPosition);
#elif SHADOW_SAMPLING_METHOD == SHADOW_SAMPLING_PCF_HIGH
return ShadowSample_PCF_High(shadowMap, layer, size, shadowPosition);
return ShadowSample_PCF_Low(shadowMap, layer, size, position);
#endif
}
// VSM sampling
// VSM or DPCF sampling
float shadow(const mediump sampler2DArray shadowMap,
const uint layer, const highp vec3 shadowPosition) {
const uint layer, const highp vec4 shadowPosition) {
// 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(shadowPosition.xy, layer)).xy;
highp float depth = shadowPosition.z;
highp vec2 moments = texture(shadowMap, vec3(position.xy, layer)).xy;
highp float depth = position.z;
// EVSM depth warping
depth = depth * 2.0 - 1.0;

View File

@@ -1,6 +1,6 @@
{
"name": "filament",
"version": "1.12.10",
"version": "1.13.0",
"description": "Real-time physically based rendering engine",
"main": "filament.js",
"module": "filament.js",