Compare commits
30 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
44125926d1 | ||
|
|
fbfd5ec0ec | ||
|
|
a74a95cc65 | ||
|
|
bc0ea16ff0 | ||
|
|
b2dc8aa84c | ||
|
|
9987e8b6ab | ||
|
|
0d9bdcc008 | ||
|
|
81fa33abeb | ||
|
|
af7f87e19b | ||
|
|
3073b03d56 | ||
|
|
23dab0a013 | ||
|
|
fd68144ba2 | ||
|
|
51e92cd142 | ||
|
|
50f33f7196 | ||
|
|
b5c634045e | ||
|
|
1f05531d53 | ||
|
|
88f382f0e3 | ||
|
|
3e59925900 | ||
|
|
1182d30fb9 | ||
|
|
095179eeb3 | ||
|
|
69b48eaadb | ||
|
|
4031c6f0d3 | ||
|
|
7a22f7dfc5 | ||
|
|
728ebf5023 | ||
|
|
62476d2f06 | ||
|
|
602a550d93 | ||
|
|
5fea428243 | ||
|
|
9a3c9ccbf3 | ||
|
|
ef4dfcecd6 | ||
|
|
5943382d23 |
@@ -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
|
||||
|
||||
|
||||
@@ -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(
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
|
||||
@@ -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
|
||||
```
|
||||
|
||||
|
||||
|
||||
@@ -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')
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,3 +1,17 @@
|
||||
|
||||
plugins {
|
||||
id 'groovy-gradle-plugin'
|
||||
}
|
||||
|
||||
gradlePlugin {
|
||||
plugins {
|
||||
create("filament-tools-plugin") {
|
||||
id = "filament-tools-plugin"
|
||||
implementationClass = "FilamentToolsPlugin"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
repositories {
|
||||
mavenCentral()
|
||||
}
|
||||
|
||||
@@ -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() &&
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
|
||||
26
build.sh
26
build.sh
@@ -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."
|
||||
;;
|
||||
|
||||
@@ -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
|
||||
)
|
||||
|
||||
|
||||
@@ -21,6 +21,8 @@
|
||||
#include <utils/Log.h>
|
||||
#include <utils/debug.h>
|
||||
|
||||
#include <limits>
|
||||
|
||||
namespace filament {
|
||||
namespace backend {
|
||||
|
||||
|
||||
@@ -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() {
|
||||
|
||||
@@ -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];
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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
|
||||
|
||||
// ---------------------------------------------------------------------------------------------
|
||||
|
||||
@@ -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()
|
||||
*/
|
||||
|
||||
@@ -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,
|
||||
};
|
||||
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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) {
|
||||
|
||||
@@ -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;
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
13
filament/src/materials/colorGrading/colorGrading.fs
Normal file
13
filament/src/materials/colorGrading/colorGrading.fs
Normal 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;
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -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 = {
|
||||
|
||||
@@ -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();
|
||||
}
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -22,6 +22,7 @@
|
||||
|
||||
#include <utils/Log.h>
|
||||
|
||||
#include <cstring>
|
||||
#include <limits>
|
||||
#include <memory>
|
||||
#include <sstream>
|
||||
|
||||
@@ -21,6 +21,7 @@
|
||||
#include <utils/Panic.h>
|
||||
|
||||
#include <algorithm>
|
||||
#include <limits>
|
||||
#include <memory>
|
||||
#include <type_traits>
|
||||
#include <utility>
|
||||
|
||||
@@ -20,6 +20,7 @@
|
||||
|
||||
#include <utils/Log.h>
|
||||
|
||||
#include <cstring>
|
||||
#include <vector>
|
||||
|
||||
using namespace utils;
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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;
|
||||
|
||||
|
||||
@@ -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:
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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",
|
||||
|
||||
Reference in New Issue
Block a user