Compare commits
68 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
bbe7dbfa92 | ||
|
|
5697922a65 | ||
|
|
4da83df2b9 | ||
|
|
8f156d6588 | ||
|
|
cec0871c11 | ||
|
|
41a809368b | ||
|
|
f690015a88 | ||
|
|
342deb5a64 | ||
|
|
fdc480ff6e | ||
|
|
378b7cd4dd | ||
|
|
f779fc7a0e | ||
|
|
fe11f495d6 | ||
|
|
265876c849 | ||
|
|
c9c52518c0 | ||
|
|
96c9ac6f8b | ||
|
|
bdeb30a847 | ||
|
|
ea53eb9290 | ||
|
|
05875057c9 | ||
|
|
d67210155b | ||
|
|
ae92c50cee | ||
|
|
43429dc0ec | ||
|
|
04cf92a27f | ||
|
|
57a57f50f6 | ||
|
|
5e5a75e6c5 | ||
|
|
109c86aaf7 | ||
|
|
10e7e7727a | ||
|
|
a39775050a | ||
|
|
44125926d1 | ||
|
|
60734349de | ||
|
|
fbfd5ec0ec | ||
|
|
157c0264af | ||
|
|
9bacfdb390 | ||
|
|
f9aaf5c42e | ||
|
|
a74a95cc65 | ||
|
|
21db695e79 | ||
|
|
cf917f1093 | ||
|
|
a8d3a61c25 | ||
|
|
80f13a8149 | ||
|
|
e172f3a67f | ||
|
|
c382c0a9cc | ||
|
|
bc0ea16ff0 | ||
|
|
b2dc8aa84c | ||
|
|
78b29fe967 | ||
|
|
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 |
29
.github/workflows/release.yml
vendored
29
.github/workflows/release.yml
vendored
@@ -1,14 +1,25 @@
|
||||
name: Release
|
||||
|
||||
# This Workflow can be triggered two ways:
|
||||
# 1. A GitHub release is created (using the GitHub web UI). This triggers all of the platforms to build and upload assets.
|
||||
# 2. A repository_dispatch API event is sent. This triggers a build for only the platform specified in the dispatch event.
|
||||
# 1. A GitHub release is created (using the GitHub web UI). This triggers all of the platforms to
|
||||
# build and upload assets.
|
||||
# 2. A workflow_dispatch event is triggered from the GitHub web UI. This triggers a build for only
|
||||
# the platform specified in the dispatch event.
|
||||
|
||||
env:
|
||||
RELEASE_TAG: ${{ github.event.client_payload.release_tag }}
|
||||
RELEASE_TAG: ${{ github.event.inputs.release_tag }}
|
||||
|
||||
on:
|
||||
repository_dispatch:
|
||||
workflow_dispatch:
|
||||
inputs:
|
||||
platform:
|
||||
description: 'Platform to build (desktop, web, android, ios, windows)'
|
||||
required: true
|
||||
default: 'desktop'
|
||||
release_tag:
|
||||
description: 'Release tag to build (e.g., v1.13.0)'
|
||||
required: true
|
||||
default: 'v1.13.0'
|
||||
release:
|
||||
types: [created]
|
||||
|
||||
@@ -16,7 +27,7 @@ jobs:
|
||||
build-desktop:
|
||||
name: build-desktop
|
||||
runs-on: ${{ matrix.os }}
|
||||
if: github.event_name == 'release' || github.event.client_payload.platform == 'desktop'
|
||||
if: github.event_name == 'release' || github.event.inputs.platform == 'desktop'
|
||||
|
||||
strategy:
|
||||
matrix:
|
||||
@@ -51,7 +62,7 @@ jobs:
|
||||
build-web:
|
||||
name: build-web
|
||||
runs-on: macos-latest
|
||||
if: github.event_name == 'release' || github.event.client_payload.platform == 'web'
|
||||
if: github.event_name == 'release' || github.event.inputs.platform == 'web'
|
||||
|
||||
steps:
|
||||
- name: Decide Git ref
|
||||
@@ -80,7 +91,7 @@ jobs:
|
||||
build-android:
|
||||
name: build-android
|
||||
runs-on: macos-latest
|
||||
if: github.event_name == 'release' || github.event.client_payload.platform == 'android'
|
||||
if: github.event_name == 'release' || github.event.inputs.platform == 'android'
|
||||
|
||||
steps:
|
||||
- name: Decide Git ref
|
||||
@@ -129,7 +140,7 @@ jobs:
|
||||
build-ios:
|
||||
name: build-ios
|
||||
runs-on: macos-latest
|
||||
if: github.event_name == 'release' || github.event.client_payload.platform == 'ios'
|
||||
if: github.event_name == 'release' || github.event.inputs.platform == 'ios'
|
||||
|
||||
steps:
|
||||
- name: Decide Git ref
|
||||
@@ -158,7 +169,7 @@ jobs:
|
||||
build-windows:
|
||||
name: build-windows
|
||||
runs-on: windows-latest
|
||||
if: github.event_name == 'release' || github.event.client_payload.platform == 'windows'
|
||||
if: github.event_name == 'release' || github.event.inputs.platform == 'windows'
|
||||
|
||||
steps:
|
||||
- name: Decide Git ref
|
||||
|
||||
@@ -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.14.1'
|
||||
}
|
||||
```
|
||||
|
||||
@@ -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.14.1'
|
||||
```
|
||||
|
||||
### Snapshots
|
||||
|
||||
@@ -3,7 +3,35 @@
|
||||
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.14.2 (currently main branch)
|
||||
|
||||
## v1.14.1
|
||||
|
||||
- engine: Improvements to shadowing.
|
||||
|
||||
## v1.14.0
|
||||
|
||||
- engine: Internal materials can use structures as parameters [⚠️ **Material breakage**].
|
||||
- engine: `readPixels` on a `SwapChain` must be called within `beginFrame` / `endFrame` [⚠️ **API
|
||||
Change**].
|
||||
- engine: Fix normal bias and improve spotlight quality.
|
||||
- Java: Fix shadow biases.
|
||||
|
||||
## v1.13.0
|
||||
|
||||
- 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,30 @@
|
||||
import java.nio.file.Paths
|
||||
|
||||
// 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
|
||||
@@ -40,23 +44,31 @@
|
||||
//
|
||||
|
||||
buildscript {
|
||||
def filamentPath = file("../out/android-release/filament").absolutePath
|
||||
if (project.hasProperty("filament_dist_dir")) {
|
||||
filamentPath = file(project.property("filament_dist_dir")).absolutePath
|
||||
}
|
||||
def path = providers
|
||||
.gradleProperty("com.google.android.filament.dist-dir")
|
||||
.forUseAtConfigurationTime().get()
|
||||
|
||||
def directory = objects.fileProperty().fileValue(new File(path)).getAsFile().get()
|
||||
def filamentPath = directory.absolutePath
|
||||
|
||||
// Our CMake scripts require a forward-slash path for the FILAMENT_DIST_DIR
|
||||
// variable, so here we convert the native path to a forward-slash path.
|
||||
filamentPath = filamentPath.replace(File.separator, '/')
|
||||
|
||||
// Warning: changing this property does not work well with incremental builds.
|
||||
def excludeVulkan = project.hasProperty("filament_exclude_vulkan")
|
||||
def excludeVulkan = providers
|
||||
.gradleProperty("com.google.android.filament.exclude-vulkan")
|
||||
.forUseAtConfigurationTime()
|
||||
.isPresent()
|
||||
|
||||
def abis = ["arm64-v8a", "armeabi-v7a", "x86_64", "x86"]
|
||||
if (project.hasProperty("filament_abis")) {
|
||||
def newAbis = project.property("filament_abis").split(',')
|
||||
if (!newAbis.contains("all")) {
|
||||
abis = newAbis
|
||||
}
|
||||
def newAbis = providers
|
||||
.gradleProperty("com.google.android.filament.abis")
|
||||
.forUseAtConfigurationTime()
|
||||
.get()
|
||||
.split(',')
|
||||
if (!newAbis.contains("all")) {
|
||||
abis = newAbis
|
||||
}
|
||||
|
||||
ext.versions = [
|
||||
@@ -70,7 +82,7 @@ buildscript {
|
||||
|
||||
ext.deps = [
|
||||
'androidx': [
|
||||
'annotations': "androidx.annotation:annotation:1.1.0",
|
||||
'annotations': "androidx.annotation:annotation:1.3.0",
|
||||
'core': "androidx.core:core:1.3.0",
|
||||
],
|
||||
'kotlin': "org.jetbrains.kotlin:kotlin-stdlib-jdk8:${versions.kotlin}"
|
||||
@@ -118,7 +130,7 @@ buildscript {
|
||||
}
|
||||
|
||||
plugins {
|
||||
id 'io.codearte.nexus-staging' version '0.22.0'
|
||||
id 'io.codearte.nexus-staging' version '0.30.0'
|
||||
}
|
||||
|
||||
// Nexus Staging configuration
|
||||
@@ -187,7 +199,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;
|
||||
}
|
||||
|
||||
@@ -76,7 +76,9 @@ extern "C" JNIEXPORT void JNICALL
|
||||
Java_com_google_android_filament_LightManager_nBuilderShadowOptions(JNIEnv* env, jclass,
|
||||
jlong nativeBuilder, jint mapSize, jint cascades, jfloatArray splitPositions,
|
||||
jfloat constantBias, jfloat normalBias, jfloat shadowFar, jfloat shadowNearHint,
|
||||
jfloat shadowFarHint, jboolean stable, jboolean screenSpaceContactShadows, jint stepCount,
|
||||
jfloat shadowFarHint, jboolean stable,
|
||||
jfloat polygonOffsetConstant, jfloat polygonOffsetSlope,
|
||||
jboolean screenSpaceContactShadows, jint stepCount,
|
||||
jfloat maxShadowDistance, jint vsmMsaaSamples, jfloat blurWidth) {
|
||||
LightManager::Builder *builder = (LightManager::Builder *) nativeBuilder;
|
||||
LightManager::ShadowOptions shadowOptions {
|
||||
@@ -88,6 +90,8 @@ Java_com_google_android_filament_LightManager_nBuilderShadowOptions(JNIEnv* env,
|
||||
.shadowNearHint = shadowNearHint,
|
||||
.shadowFarHint = shadowFarHint,
|
||||
.stable = (bool)stable,
|
||||
.polygonOffsetConstant = polygonOffsetConstant,
|
||||
.polygonOffsetSlope = polygonOffsetConstant,
|
||||
.screenSpaceContactShadows = (bool)screenSpaceContactShadows,
|
||||
.stepCount = uint8_t(stepCount),
|
||||
.maxShadowDistance = maxShadowDistance,
|
||||
|
||||
@@ -244,13 +244,13 @@ public class LightManager {
|
||||
* light. 1mm by default.
|
||||
* This is ignored when the View's ShadowType is set to VSM.
|
||||
*/
|
||||
public float constantBias = 0.05f;
|
||||
public float constantBias = 0.001f;
|
||||
|
||||
/** Amount by which the maximum sampling error is scaled. The resulting value is used
|
||||
* to move the shadow away from the fragment normal. Should be 1.0.
|
||||
* This is ignored when the View's ShadowType is set to VSM.
|
||||
*/
|
||||
public float normalBias = 0.4f;
|
||||
public float normalBias = 1.0f;
|
||||
|
||||
/** Distance from the camera after which shadows are clipped. This is used to clip
|
||||
* shadows that are too far and wouldn't contribute to the scene much, improving
|
||||
@@ -279,7 +279,24 @@ public class LightManager {
|
||||
* When set to true, all resolution enhancing features that can affect stability are
|
||||
* disabling, resulting in significantly lower resolution shadows, albeit stable ones.
|
||||
*/
|
||||
public boolean stable = true;
|
||||
public boolean stable = false;
|
||||
|
||||
/**
|
||||
* Constant bias in depth-resolution units by which shadows are moved away from the
|
||||
* light. The default value of 0.5 is used to round depth values up.
|
||||
* Generally this value shouldn't be changed or at least be small and positive.
|
||||
* This is ignored when the View's ShadowType is set to VSM.
|
||||
*/
|
||||
float polygonOffsetConstant = 0.5f;
|
||||
|
||||
/**
|
||||
* Bias based on the change in depth in depth-resolution units by which shadows are moved
|
||||
* away from the light. The default value of 2.0 works well with SHADOW_SAMPLING_PCF_LOW.
|
||||
* Generally this value is between 0.5 and the size in texel of the PCF filter.
|
||||
* Setting this value correctly is essential for LISPSM shadow-maps.
|
||||
* This is ignored when the View's ShadowType is set to VSM.
|
||||
*/
|
||||
float polygonOffsetSlope = 2.0f;
|
||||
|
||||
/**
|
||||
* Whether screen-space contact shadows are used. This applies regardless of whether a
|
||||
@@ -471,7 +488,9 @@ public class LightManager {
|
||||
nBuilderShadowOptions(mNativeBuilder,
|
||||
options.mapSize, options.shadowCascades, options.cascadeSplitPositions,
|
||||
options.constantBias, options.normalBias, options.shadowFar, options.shadowNearHint,
|
||||
options.shadowFarHint, options.stable, options.screenSpaceContactShadows,
|
||||
options.shadowFarHint, options.stable,
|
||||
options.polygonOffsetConstant, options.polygonOffsetSlope,
|
||||
options.screenSpaceContactShadows,
|
||||
options.stepCount, options.maxShadowDistance, options.vsmMsaaSamples,
|
||||
options.blurWidth);
|
||||
return this;
|
||||
@@ -1131,7 +1150,7 @@ public class LightManager {
|
||||
private static native void nDestroyBuilder(long nativeBuilder);
|
||||
private static native boolean nBuilderBuild(long nativeBuilder, long nativeEngine, int entity);
|
||||
private static native void nBuilderCastShadows(long nativeBuilder, boolean enable);
|
||||
private static native void nBuilderShadowOptions(long nativeBuilder, int mapSize, int cascades, float[] splitPositions, float constantBias, float normalBias, float shadowFar, float shadowNearHint, float shadowFarhint, boolean stable, boolean screenSpaceContactShadows, int stepCount, float maxShadowDistance, int vsmMsaaSamples, float blurWidth);
|
||||
private static native void nBuilderShadowOptions(long nativeBuilder, int mapSize, int cascades, float[] splitPositions, float constantBias, float normalBias, float shadowFar, float shadowNearHint, float shadowFarhint, boolean stable, float polygonOffsetConstant, float polygonOffsetSlope, boolean screenSpaceContactShadows, int stepCount, float maxShadowDistance, int vsmMsaaSamples, float blurWidth);
|
||||
private static native void nBuilderCastLight(long nativeBuilder, boolean enabled);
|
||||
private static native void nBuilderPosition(long nativeBuilder, float x, float y, float z);
|
||||
private static native void nBuilderDirection(long nativeBuilder, float x, float y, float z);
|
||||
|
||||
@@ -437,8 +437,9 @@ public class Renderer {
|
||||
*</pre>
|
||||
*
|
||||
*
|
||||
* <p>Typically <code>readPixels</code> will be called after {@link #render} and before
|
||||
* {@link #endFrame}.</p>
|
||||
* <p><code>readPixels</code> must be called within a frame, meaning after {@link #beginFrame}
|
||||
* and before {@link #endFrame}. Typically, <code>readPixels</code> will be called after
|
||||
* {@link #render}.</p>
|
||||
* <br>
|
||||
* <p>After calling this method, the callback associated with <code>buffer</code>
|
||||
* will be invoked on the main thread, indicating that the read-back has completed.
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
GROUP=com.google.android.filament
|
||||
VERSION_NAME=1.12.10
|
||||
VERSION_NAME=1.14.1
|
||||
|
||||
POM_DESCRIPTION=Real-time physically based rendering engine for Android.
|
||||
|
||||
@@ -18,3 +18,9 @@ POM_DEVELOPER_NAME=Filament Team
|
||||
org.gradle.jvmargs=-Xmx1536m
|
||||
|
||||
android.useAndroidX=true
|
||||
|
||||
org.gradle.unsafe.configuration-cache=true
|
||||
|
||||
com.google.android.filament.tools-dir=../../../out/release/filament
|
||||
com.google.android.filament.dist-dir=../out/android-release/filament
|
||||
com.google.android.filament.abis=all
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -30,6 +32,11 @@ android {
|
||||
targetSdkVersion versions.targetSdk
|
||||
missingDimensionStrategy 'functionality', 'full'
|
||||
}
|
||||
// NOTE: This is a workaround required because the AGP task collectReleaseDependencies
|
||||
// is not configuration-cache friendly yet; this is only useful for Play publication
|
||||
dependenciesInfo {
|
||||
includeInApk = false
|
||||
}
|
||||
}
|
||||
|
||||
dependencies {
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -21,6 +23,12 @@ android {
|
||||
targetSdkVersion versions.targetSdk
|
||||
}
|
||||
|
||||
// NOTE: This is a workaround required because the AGP task collectReleaseDependencies
|
||||
// is not configuration-cache friendly yet; this is only useful for Play publication
|
||||
dependenciesInfo {
|
||||
includeInApk = false
|
||||
}
|
||||
|
||||
// We use the .filamat extension for materials compiled with matc
|
||||
// Telling aapt to not compress them allows to load them efficiently
|
||||
aaptOptions {
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -21,6 +23,12 @@ android {
|
||||
targetSdkVersion versions.targetSdk
|
||||
}
|
||||
|
||||
// NOTE: This is a workaround required because the AGP task collectReleaseDependencies
|
||||
// is not configuration-cache friendly yet; this is only useful for Play publication
|
||||
dependenciesInfo {
|
||||
includeInApk = false
|
||||
}
|
||||
|
||||
// We use the .filamat extension for materials compiled with matc
|
||||
// Telling aapt to not compress them allows to load them efficiently
|
||||
aaptOptions {
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -27,6 +29,12 @@ android {
|
||||
targetSdkVersion versions.targetSdk
|
||||
}
|
||||
|
||||
// NOTE: This is a workaround required because the AGP task collectReleaseDependencies
|
||||
// is not configuration-cache friendly yet; this is only useful for Play publication
|
||||
dependenciesInfo {
|
||||
includeInApk = false
|
||||
}
|
||||
|
||||
// We use the .filamat extension for materials compiled with matc
|
||||
// Telling aapt to not compress them allows to load them efficiently
|
||||
aaptOptions {
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -20,6 +22,12 @@ android {
|
||||
targetSdkVersion versions.targetSdk
|
||||
}
|
||||
|
||||
// NOTE: This is a workaround required because the AGP task collectReleaseDependencies
|
||||
// is not configuration-cache friendly yet; this is only useful for Play publication
|
||||
dependenciesInfo {
|
||||
includeInApk = false
|
||||
}
|
||||
|
||||
// We use the .filamat extension for materials compiled with matc
|
||||
// Telling aapt to not compress them allows to load them efficiently
|
||||
aaptOptions {
|
||||
|
||||
@@ -10,6 +10,12 @@ android {
|
||||
minSdkVersion versions.minSdk
|
||||
targetSdkVersion versions.targetSdk
|
||||
}
|
||||
|
||||
// NOTE: This is a workaround required because the AGP task collectReleaseDependencies
|
||||
// is not configuration-cache friendly yet; this is only useful for Play publication
|
||||
dependenciesInfo {
|
||||
includeInApk = false
|
||||
}
|
||||
}
|
||||
|
||||
dependencies {
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -31,6 +33,12 @@ android {
|
||||
missingDimensionStrategy 'functionality', 'full'
|
||||
}
|
||||
|
||||
// NOTE: This is a workaround required because the AGP task collectReleaseDependencies
|
||||
// is not configuration-cache friendly yet; this is only useful for Play publication
|
||||
dependenciesInfo {
|
||||
includeInApk = false
|
||||
}
|
||||
|
||||
// We use the .filamat extension for materials compiled with matc
|
||||
// Telling aapt to not compress them allows to load them efficiently
|
||||
aaptOptions {
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -20,6 +22,12 @@ android {
|
||||
targetSdkVersion versions.targetSdk
|
||||
}
|
||||
|
||||
// NOTE: This is a workaround required because the AGP task collectReleaseDependencies
|
||||
// is not configuration-cache friendly yet; this is only useful for Play publication
|
||||
dependenciesInfo {
|
||||
includeInApk = false
|
||||
}
|
||||
|
||||
// We use the .filamat extension for materials compiled with matc
|
||||
// Telling aapt to not compress them allows to load them efficiently
|
||||
aaptOptions {
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -22,6 +25,12 @@ android {
|
||||
targetSdkVersion versions.targetSdk
|
||||
}
|
||||
|
||||
// NOTE: This is a workaround required because the AGP task collectReleaseDependencies
|
||||
// is not configuration-cache friendly yet; this is only useful for Play publication
|
||||
dependenciesInfo {
|
||||
includeInApk = false
|
||||
}
|
||||
|
||||
// We use the .filamat extension for materials compiled with matc
|
||||
// Telling aapt to not compress them allows to load them efficiently
|
||||
aaptOptions {
|
||||
@@ -31,5 +40,5 @@ android {
|
||||
|
||||
dependencies {
|
||||
implementation project(':filament-android')
|
||||
implementation 'androidx.annotation:annotation:1.1.0'
|
||||
implementation deps.androidx.annotations
|
||||
}
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -21,6 +23,12 @@ android {
|
||||
targetSdkVersion versions.targetSdk
|
||||
}
|
||||
|
||||
// NOTE: This is a workaround required because the AGP task collectReleaseDependencies
|
||||
// is not configuration-cache friendly yet; this is only useful for Play publication
|
||||
dependenciesInfo {
|
||||
includeInApk = false
|
||||
}
|
||||
|
||||
// We use the .filamat extension for materials compiled with matc
|
||||
// Telling aapt to not compress them allows to load them efficiently
|
||||
aaptOptions {
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -21,6 +23,12 @@ android {
|
||||
targetSdkVersion versions.targetSdk
|
||||
}
|
||||
|
||||
// NOTE: This is a workaround required because the AGP task collectReleaseDependencies
|
||||
// is not configuration-cache friendly yet; this is only useful for Play publication
|
||||
dependenciesInfo {
|
||||
includeInApk = false
|
||||
}
|
||||
|
||||
// We use the .filamat extension for materials compiled with matc
|
||||
// Telling aapt to not compress them allows to load them efficiently
|
||||
aaptOptions {
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -28,6 +30,12 @@ android {
|
||||
missingDimensionStrategy 'functionality', 'full'
|
||||
}
|
||||
|
||||
// NOTE: This is a workaround required because the AGP task collectReleaseDependencies
|
||||
// is not configuration-cache friendly yet; this is only useful for Play publication
|
||||
dependenciesInfo {
|
||||
includeInApk = false
|
||||
}
|
||||
|
||||
// We use the .filamat extension for materials compiled with matc
|
||||
// Telling aapt to not compress them allows to load them efficiently
|
||||
aaptOptions {
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -21,6 +23,12 @@ android {
|
||||
targetSdkVersion versions.targetSdk
|
||||
}
|
||||
|
||||
// NOTE: This is a workaround required because the AGP task collectReleaseDependencies
|
||||
// is not configuration-cache friendly yet; this is only useful for Play publication
|
||||
dependenciesInfo {
|
||||
includeInApk = false
|
||||
}
|
||||
|
||||
// We use the .filamat extension for materials compiled with matc
|
||||
// Telling aapt to not compress them allows to load them efficiently
|
||||
aaptOptions {
|
||||
|
||||
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."
|
||||
;;
|
||||
|
||||
@@ -198,7 +198,10 @@ set(MATERIAL_SRCS
|
||||
src/materials/skybox.mat
|
||||
src/materials/ssao/sao.mat
|
||||
src/materials/ssao/saoBentNormals.mat
|
||||
src/materials/separableGaussianBlur.mat
|
||||
src/materials/separableGaussianBlur1.mat
|
||||
src/materials/separableGaussianBlur2.mat
|
||||
src/materials/separableGaussianBlur3.mat
|
||||
src/materials/separableGaussianBlur4.mat
|
||||
src/materials/antiAliasing/fxaa.mat
|
||||
src/materials/antiAliasing/taa.mat
|
||||
src/materials/vsmMipmap.mat
|
||||
@@ -275,6 +278,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 +286,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
|
||||
)
|
||||
|
||||
@@ -391,6 +396,34 @@ add_custom_command(
|
||||
APPEND
|
||||
)
|
||||
|
||||
add_custom_command(
|
||||
OUTPUT "${MATERIAL_DIR}/separableGaussianBlur1.filamat"
|
||||
DEPENDS src/materials/separableGaussianBlur.vs
|
||||
DEPENDS src/materials/separableGaussianBlur.fs
|
||||
APPEND
|
||||
)
|
||||
|
||||
add_custom_command(
|
||||
OUTPUT "${MATERIAL_DIR}/separableGaussianBlur2.filamat"
|
||||
DEPENDS src/materials/separableGaussianBlur.vs
|
||||
DEPENDS src/materials/separableGaussianBlur.fs
|
||||
APPEND
|
||||
)
|
||||
|
||||
add_custom_command(
|
||||
OUTPUT "${MATERIAL_DIR}/separableGaussianBlur3.filamat"
|
||||
DEPENDS src/materials/separableGaussianBlur.vs
|
||||
DEPENDS src/materials/separableGaussianBlur.fs
|
||||
APPEND
|
||||
)
|
||||
|
||||
add_custom_command(
|
||||
OUTPUT "${MATERIAL_DIR}/separableGaussianBlur4.filamat"
|
||||
DEPENDS src/materials/separableGaussianBlur.vs
|
||||
DEPENDS src/materials/separableGaussianBlur.fs
|
||||
APPEND
|
||||
)
|
||||
|
||||
add_custom_command(
|
||||
OUTPUT ${RESGEN_OUTPUTS}
|
||||
COMMAND resgen ${RESGEN_FLAGS} ${MATERIAL_BINS}
|
||||
|
||||
@@ -220,7 +220,8 @@ enum class UniformType : uint8_t {
|
||||
UINT3,
|
||||
UINT4,
|
||||
MAT3, //!< a 3x3 float matrix
|
||||
MAT4 //!< a 4x4 float matrix
|
||||
MAT4, //!< a 4x4 float matrix
|
||||
STRUCT
|
||||
};
|
||||
|
||||
enum class Precision : uint8_t {
|
||||
|
||||
@@ -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,13 @@ 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
|
||||
// This bug continues to reproduce, though at times we've seen it appear to "go away". The
|
||||
// standalone sample app that was written to show this problem still reproduces.
|
||||
// The working hypthesis is that some other state affects this behavior.
|
||||
bugs.disable_sidecar_blit_into_texture_array = true;
|
||||
|
||||
// early exit condition is flattened in EASU code
|
||||
bugs.split_easu = true;
|
||||
bugs.invalidate_end_only_if_invalidate_start = true;
|
||||
|
||||
@@ -33,6 +33,13 @@ using namespace filament::math;
|
||||
using namespace utils;
|
||||
using namespace backend;
|
||||
|
||||
static void logCompilationError(utils::io::ostream& out,
|
||||
backend::Program::Shader shaderType, const char* name,
|
||||
GLuint shaderId, std::string_view source) noexcept;
|
||||
|
||||
static void logProgramLinkError(utils::io::ostream& out,
|
||||
const char* name, GLuint program) noexcept;
|
||||
|
||||
OpenGLProgram::OpenGLProgram(OpenGLDriver* gl, const Program& programBuilder) noexcept
|
||||
: HwProgram(programBuilder.getName()), mIsValid(false) {
|
||||
|
||||
@@ -57,7 +64,7 @@ OpenGLProgram::OpenGLProgram(OpenGLDriver* gl, const Program& programBuilder) no
|
||||
|
||||
if (!shadersSource[i].empty()) {
|
||||
GLint status;
|
||||
auto shader = shadersSource[i];
|
||||
Program::ShaderBlob shader = shadersSource[i];
|
||||
std::string temp;
|
||||
std::string_view shaderView((const char*)shader.data(), shader.size());
|
||||
|
||||
@@ -123,17 +130,18 @@ highp uint packHalf2x16(vec2 v) {
|
||||
shaderView = temp;
|
||||
}
|
||||
|
||||
const char * const source = shaderView.data();
|
||||
GLint length = (GLint)shaderView.length();
|
||||
|
||||
GLuint shaderId = glCreateShader(glShaderType);
|
||||
glShaderSource(shaderId, 1, &source, &length);
|
||||
glCompileShader(shaderId);
|
||||
{ // scope for source/length (we don't want them to leak out)
|
||||
const char* const source = shaderView.data();
|
||||
const GLint length = (GLint)shaderView.length();
|
||||
glShaderSource(shaderId, 1, &source, &length);
|
||||
glCompileShader(shaderId);
|
||||
}
|
||||
|
||||
glGetShaderiv(shaderId, GL_COMPILE_STATUS, &status);
|
||||
if (UTILS_UNLIKELY(status != GL_TRUE)) {
|
||||
logCompilationError(slog.e, type,
|
||||
programBuilder.getName().c_str_safe(), shaderId, source);
|
||||
programBuilder.getName().c_str_safe(), shaderId, shaderView);
|
||||
glDeleteShader(shaderId);
|
||||
return;
|
||||
}
|
||||
@@ -315,8 +323,8 @@ void OpenGLProgram::updateSamplers(OpenGLDriver* gld) noexcept {
|
||||
}
|
||||
|
||||
UTILS_NOINLINE
|
||||
void OpenGLProgram::logCompilationError(io::ostream& out, Program::Shader shaderType,
|
||||
const char* name, GLuint shaderId, char const* source) noexcept {
|
||||
void logCompilationError(io::ostream& out, Program::Shader shaderType,
|
||||
const char* name, GLuint shaderId, std::string_view shader) noexcept {
|
||||
|
||||
auto to_string = [](Program::Shader type) -> const char* {
|
||||
switch (type) {
|
||||
@@ -333,23 +341,26 @@ void OpenGLProgram::logCompilationError(io::ostream& out, Program::Shader shader
|
||||
<< io::endl;
|
||||
|
||||
size_t lc = 1;
|
||||
char* shader = strdup(source);
|
||||
char* start = shader;
|
||||
char* endl = strchr(start, '\n');
|
||||
|
||||
while (endl != nullptr) {
|
||||
*endl = '\0';
|
||||
out << lc++ << ": ";
|
||||
out << start << io::endl;
|
||||
start = endl + 1;
|
||||
endl = strchr(start, '\n');
|
||||
size_t start = 0;
|
||||
std::string line;
|
||||
while (true) {
|
||||
size_t end = shader.find('\n', start);
|
||||
if (end == std::string::npos) {
|
||||
line = shader.substr(start);
|
||||
} else {
|
||||
line = shader.substr(start, end - start);
|
||||
}
|
||||
out << lc++ << ": "<< line.c_str() << '\n';
|
||||
if (end == std::string::npos) {
|
||||
break;
|
||||
}
|
||||
start = end + 1;
|
||||
}
|
||||
|
||||
free(shader);
|
||||
out << io::endl;
|
||||
}
|
||||
|
||||
UTILS_NOINLINE
|
||||
void OpenGLProgram::logProgramLinkError(io::ostream& out, char const* name, GLuint program) noexcept {
|
||||
void logProgramLinkError(io::ostream& out, char const* name, GLuint program) noexcept {
|
||||
char error[1024];
|
||||
glGetProgramInfoLog(program, sizeof(error), nullptr, error);
|
||||
|
||||
|
||||
@@ -66,13 +66,6 @@ public:
|
||||
GLuint program;
|
||||
} gl; // 12 bytes
|
||||
|
||||
static void logCompilationError(utils::io::ostream& out,
|
||||
backend::Program::Shader shaderType, const char* name,
|
||||
GLuint shaderId, char const* source) noexcept;
|
||||
|
||||
static void logProgramLinkError(utils::io::ostream& out,
|
||||
const char* name, GLuint program) noexcept;
|
||||
|
||||
private:
|
||||
static constexpr uint8_t TEXTURE_UNIT_COUNT = OpenGLContext::MAX_TEXTURE_UNIT_COUNT;
|
||||
static constexpr uint8_t VERTEX_SHADER_BIT = uint8_t(1) << size_t(backend::Program::Shader::VERTEX);
|
||||
|
||||
@@ -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
|
||||
|
||||
// ---------------------------------------------------------------------------------------------
|
||||
|
||||
@@ -273,6 +273,7 @@ public:
|
||||
* Constant bias in depth-resolution units by which shadows are moved away from the
|
||||
* light. The default value of 0.5 is used to round depth values up.
|
||||
* Generally this value shouldn't be changed or at least be small and positive.
|
||||
* This is ignored when the View's ShadowType is set to VSM.
|
||||
*/
|
||||
float polygonOffsetConstant = 0.5f;
|
||||
|
||||
@@ -281,6 +282,7 @@ public:
|
||||
* away from the light. The default value of 2.0 works well with SHADOW_SAMPLING_PCF_LOW.
|
||||
* Generally this value is between 0.5 and the size in texel of the PCF filter.
|
||||
* Setting this value correctly is essential for LISPSM shadow-maps.
|
||||
* This is ignored when the View's ShadowType is set to VSM.
|
||||
*/
|
||||
float polygonOffsetSlope = 2.0f;
|
||||
|
||||
@@ -558,10 +560,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 +815,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()
|
||||
*/
|
||||
|
||||
@@ -341,7 +341,7 @@ public:
|
||||
*
|
||||
* Framebuffer as seen on User buffer (PixelBufferDescriptor&)
|
||||
* screen
|
||||
*
|
||||
*
|
||||
* +--------------------+
|
||||
* | | .stride .alignment
|
||||
* | | ----------------------->-->
|
||||
@@ -359,7 +359,8 @@ public:
|
||||
* O------------+-------+
|
||||
*
|
||||
*
|
||||
* Typically readPixels() will be called after render() and before endFrame().
|
||||
* readPixels() must be called within a frame, meaning after beginFrame() and before endFrame().
|
||||
* Typically, readPixels() will be called after render().
|
||||
*
|
||||
* After issuing this method, the callback associated with `buffer` will be invoked on the
|
||||
* main thread, indicating that the read-back has completed. Typically, this will happen
|
||||
|
||||
@@ -560,16 +560,25 @@ 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 = lcm.getSinInverse(li), // spot only
|
||||
.radius = spheres[j].w,
|
||||
};
|
||||
// infinity means "pointlight"
|
||||
if (light.invSin != std::numeric_limits<float>::infinity()) {
|
||||
light.invSin = std::min(maxInvSin, light.invSin);
|
||||
}
|
||||
|
||||
const size_t group = i % GROUP_COUNT;
|
||||
const size_t bit = i / GROUP_COUNT;
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
|
||||
@@ -224,7 +224,10 @@ static const MaterialInfo sMaterialList[] = {
|
||||
{ "mipmapDepth", MATERIAL(MIPMAPDEPTH) },
|
||||
{ "sao", MATERIAL(SAO) },
|
||||
{ "saoBentNormals", MATERIAL(SAOBENTNORMALS) },
|
||||
{ "separableGaussianBlur", MATERIAL(SEPARABLEGAUSSIANBLUR) },
|
||||
{ "separableGaussianBlur1", MATERIAL(SEPARABLEGAUSSIANBLUR1) },
|
||||
{ "separableGaussianBlur2", MATERIAL(SEPARABLEGAUSSIANBLUR2) },
|
||||
{ "separableGaussianBlur3", MATERIAL(SEPARABLEGAUSSIANBLUR3) },
|
||||
{ "separableGaussianBlur4", MATERIAL(SEPARABLEGAUSSIANBLUR4) },
|
||||
{ "taa", MATERIAL(TAA) },
|
||||
{ "vsmMipmap", MATERIAL(VSMMIPMAP) },
|
||||
{ "fsr_easu", MATERIAL(FSR_EASU) },
|
||||
@@ -844,7 +847,23 @@ FrameGraphId<FrameGraphTexture> PostProcessManager::gaussianBlurPass(FrameGraph&
|
||||
[=](FrameGraphResources const& resources,
|
||||
auto const& data, DriverApi& driver) {
|
||||
|
||||
auto const& separableGaussianBlur = getPostProcessMaterial("separableGaussianBlur");
|
||||
auto hwTempRT = resources.getRenderPassInfo(0);
|
||||
auto hwOutRT = resources.getRenderPassInfo(1);
|
||||
auto hwTemp = resources.getTexture(data.temp);
|
||||
auto hwIn = resources.getTexture(data.in);
|
||||
auto const& inDesc = resources.getDescriptor(data.in);
|
||||
auto const& outDesc = resources.getDescriptor(data.out);
|
||||
auto const& tempDesc = resources.getDescriptor(data.temp);
|
||||
|
||||
utils::StaticString materialName;
|
||||
switch (backend::getFormatSize(outDesc.format)) {
|
||||
case 1: materialName = "separableGaussianBlur1"; break;
|
||||
case 2: materialName = "separableGaussianBlur2"; break;
|
||||
case 3: materialName = "separableGaussianBlur3"; break;
|
||||
default: materialName = "separableGaussianBlur4"; break;
|
||||
}
|
||||
|
||||
auto const& separableGaussianBlur = getPostProcessMaterial(materialName);
|
||||
FMaterialInstance* const mi = separableGaussianBlur.getMaterialInstance();
|
||||
const size_t kernelStorageSize = mi->getMaterial()->reflect("kernel")->size;
|
||||
|
||||
@@ -853,13 +872,6 @@ FrameGraphId<FrameGraphTexture> PostProcessManager::gaussianBlurPass(FrameGraph&
|
||||
std::min(sizeof(kernel) / sizeof(*kernel), kernelStorageSize));
|
||||
|
||||
// horizontal pass
|
||||
auto hwTempRT = resources.getRenderPassInfo(0);
|
||||
auto hwOutRT = resources.getRenderPassInfo(1);
|
||||
auto hwTemp = resources.getTexture(data.temp);
|
||||
auto hwIn = resources.getTexture(data.in);
|
||||
auto const& inDesc = resources.getDescriptor(data.in);
|
||||
auto const& outDesc = resources.getDescriptor(data.out);
|
||||
auto const& tempDesc = resources.getDescriptor(data.temp);
|
||||
|
||||
mi->setParameter("source", hwIn, {
|
||||
.filterMag = SamplerMagFilter::LINEAR,
|
||||
|
||||
@@ -1171,6 +1171,11 @@ void FRenderer::endFrame() {
|
||||
|
||||
void FRenderer::readPixels(uint32_t xoffset, uint32_t yoffset, uint32_t width, uint32_t height,
|
||||
PixelBufferDescriptor&& buffer) {
|
||||
#ifndef NDEBUG
|
||||
const bool withinFrame = mSwapChain != nullptr;
|
||||
ASSERT_PRECONDITION(withinFrame, "readPixels() on a SwapChain must be called after"
|
||||
" beginFrame() and before endFrame().");
|
||||
#endif
|
||||
readPixels(mRenderTarget, xoffset, yoffset, width, height, std::move(buffer));
|
||||
}
|
||||
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -22,6 +22,7 @@
|
||||
|
||||
#include "details/Engine.h"
|
||||
#include "details/Scene.h"
|
||||
#include "details/View.h"
|
||||
|
||||
#include <backend/DriverEnums.h>
|
||||
|
||||
@@ -52,7 +53,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);
|
||||
@@ -74,140 +75,66 @@ void ShadowMap::render(FScene const& scene, utils::Range<uint32_t> range,
|
||||
pass->setCamera(cameraInfo);
|
||||
pass->setVisibilityMask(visibilityMask);
|
||||
pass->setGeometry(scene.getRenderableData(), range, scene.getRenderableUBO());
|
||||
pass->overridePolygonOffset(&mPolygonOffset);
|
||||
pass->overridePolygonOffset(&mShadowMapInfo.polygonOffset);
|
||||
pass->appendCommands(RenderPass::SHADOW);
|
||||
pass->sortCommands();
|
||||
}
|
||||
|
||||
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;
|
||||
void ShadowMap::updateDirectional(const FScene::LightSoa& lightData, size_t index,
|
||||
filament::CameraInfo const& camera,
|
||||
const ShadowMapInfo& shadowMapInfo, FScene const& scene,
|
||||
SceneInfo& sceneInfo) noexcept {
|
||||
|
||||
// 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 {
|
||||
// this is the hard part here, find a good frustum for our camera
|
||||
|
||||
auto& lcm = mEngine.getLightManager();
|
||||
|
||||
FLightManager::Instance li = lightData.elementAt<FScene::LIGHT_INSTANCE>(index);
|
||||
mShadowMapInfo = shadowMapInfo;
|
||||
|
||||
FLightManager::ShadowParams params = lcm.getShadowParams(li);
|
||||
mPolygonOffset = {
|
||||
// handle reversed Z
|
||||
.slope = -params.options.polygonOffsetSlope,
|
||||
.constant = -params.options.polygonOffsetConstant
|
||||
};
|
||||
|
||||
// Note: we keep the polygon offset even with VSM as it seems to help.
|
||||
auto& lcm = mEngine.getLightManager();
|
||||
FLightManager::Instance li = lightData.elementAt<FScene::LIGHT_INSTANCE>(index);
|
||||
FLightManager::ShadowParams params = lcm.getShadowParams(li);
|
||||
|
||||
// Adjust the camera's projection for the light's shadowFar
|
||||
mat4f cullingProjection(camera.cullingProjection);
|
||||
if (params.options.shadowFar > 0.0f) {
|
||||
float n = camera.zn;
|
||||
float f = params.options.shadowFar;
|
||||
if (std::abs(cullingProjection[2].w) > std::numeric_limits<float>::epsilon()) {
|
||||
// perspective projection
|
||||
cullingProjection[2].z = (f + n) / (n - f);
|
||||
cullingProjection[3].z = (2 * f * n) / (n - f);
|
||||
} else {
|
||||
// ortho projection
|
||||
cullingProjection[2].z = 2.0f / (n - f);
|
||||
cullingProjection[3].z = (f + n) / (n - f);
|
||||
}
|
||||
}
|
||||
|
||||
const ShadowCameraInfo cameraInfo = {
|
||||
.projection = cullingProjection,
|
||||
.model = camera.model,
|
||||
.view = camera.view,
|
||||
.worldOrigin = camera.worldOrigin,
|
||||
.zn = camera.zn,
|
||||
.zf = camera.zf
|
||||
};
|
||||
|
||||
// debugging...
|
||||
const float dz = cameraInfo.zf - cameraInfo.zn;
|
||||
#ifndef NDEBUG
|
||||
// LISPSM debugging for directional light (works because we only have one)
|
||||
const float dz = camera.zf - camera.zn;
|
||||
float& dzn = mEngine.debug.shadowmap.dzn;
|
||||
float& dzf = mEngine.debug.shadowmap.dzf;
|
||||
if (dzn < 0) dzn = std::max(0.0f, params.options.shadowNearHint - camera.zn) / dz;
|
||||
else params.options.shadowNearHint = dzn * dz - camera.zn;
|
||||
if (dzf > 0) dzf =-std::max(0.0f, camera.zf - params.options.shadowFarHint) / dz;
|
||||
else params.options.shadowFarHint = dzf * dz + camera.zf;
|
||||
#endif
|
||||
|
||||
using LightType = FLightManager::Type;
|
||||
switch (lcm.getType(li)) {
|
||||
case LightType::SUN:
|
||||
case LightType::DIRECTIONAL:
|
||||
computeShadowCameraDirectional(
|
||||
lightData.elementAt<FScene::DIRECTION>(index),
|
||||
cameraInfo, params, cascadeParams);
|
||||
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);
|
||||
break;
|
||||
case LightType::POINT:
|
||||
break;
|
||||
// Adjust the camera's projection for the light's shadowFar
|
||||
mat4f cullingProjection(camera.cullingProjection);
|
||||
if (params.options.shadowFar > 0.0f) {
|
||||
float n = camera.zn;
|
||||
float f = params.options.shadowFar;
|
||||
// orthographic projection
|
||||
assert_invariant(std::abs(cullingProjection[2].w) <= std::numeric_limits<float>::epsilon());
|
||||
cullingProjection[2].z = 2.0f / (n - f);
|
||||
cullingProjection[3].z = (f + n) / (n - f);
|
||||
}
|
||||
}
|
||||
|
||||
void ShadowMap::computeShadowCameraDirectional(
|
||||
float3 const& dir, ShadowCameraInfo const& camera,
|
||||
FLightManager::ShadowParams const& params,
|
||||
SceneInfo cascadeParams) noexcept {
|
||||
auto direction = lightData.elementAt<FScene::DIRECTION>(index);
|
||||
|
||||
/*
|
||||
* 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(direction);
|
||||
|
||||
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;
|
||||
@@ -216,14 +143,14 @@ void ShadowMap::computeShadowCameraDirectional(
|
||||
// view frustum vertices in world-space
|
||||
float3 wsViewFrustumVertices[8];
|
||||
computeFrustumCorners(wsViewFrustumVertices,
|
||||
camera.model * FCamera::inverseProjection(camera.projection),
|
||||
cascadeParams.csNearFar);
|
||||
camera.model * FCamera::inverseProjection(cullingProjection),
|
||||
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 +170,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(direction, direction * -lsLightFrustumBounds.max.z);
|
||||
|
||||
// near / far planes are specified relative to the direction the eye is looking at
|
||||
// i.e. the -z axis (see: ortho)
|
||||
@@ -279,7 +206,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
|
||||
@@ -340,7 +267,7 @@ void ShadowMap::computeShadowCameraDirectional(
|
||||
LMpMv = L * MpMv;
|
||||
|
||||
W = applyLISPSM(Wp, camera, params, LMpMv,
|
||||
wsClippedShadowReceiverVolume, vertexCount, dir);
|
||||
wsClippedShadowReceiverVolume, vertexCount, direction);
|
||||
}
|
||||
|
||||
/*
|
||||
@@ -393,7 +320,8 @@ void ShadowMap::computeShadowCameraDirectional(
|
||||
|
||||
if (params.options.stable) {
|
||||
// Use the world origin as reference point, fixed w.r.t. the camera
|
||||
snapLightFrustum(s, o, Mv, camera.worldOrigin[3].xyz, 1.0f / mShadowMapInfo.shadowDimension);
|
||||
snapLightFrustum(s, o, Mv, camera.worldOrigin[3].xyz,
|
||||
1.0f / mShadowMapInfo.shadowDimension);
|
||||
}
|
||||
|
||||
const mat4f F(mat4f::row_major_init {
|
||||
@@ -411,29 +339,29 @@ 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);
|
||||
|
||||
// note: in texelSizeWorldSpace() below, we could use Mb * Mt * F * W because
|
||||
// L * Mp * Mv is a rigid transform (for directional lights)
|
||||
if (USE_LISPSM) {
|
||||
mTexelSizeWs = texelSizeWorldSpace(Wp, mat4f(MbMt * F));
|
||||
mTexelSizeAtOneMeterWs = texelSizeWorldSpace(Wp, mat4f(MbMt * F));
|
||||
} else {
|
||||
// We know we're using an ortho projection
|
||||
mTexelSizeWs = texelSizeWorldSpace(St.upperLeft());
|
||||
mTexelSizeAtOneMeterWs = texelSizeWorldSpace(St.upperLeft());
|
||||
}
|
||||
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
|
||||
// for perspective and lispsm shadow maps. This also allows us to do this at zero-cost
|
||||
// by baking it in the shadow-map itself.
|
||||
const float constantBias = mShadowMapInfo.vsm ? 0.0f : params.options.constantBias;
|
||||
const mat4f b = mat4f::translation(dir * constantBias);
|
||||
const mat4f b = mat4f::translation(direction * constantBias);
|
||||
|
||||
// It's important to set the light camera's model matrix separately from its projection, so
|
||||
// that the cameraPosition uniform gets set correctly.
|
||||
@@ -451,43 +379,67 @@ void ShadowMap::computeShadowCameraDirectional(
|
||||
}
|
||||
}
|
||||
|
||||
void ShadowMap::computeShadowCameraSpot(math::float3 const& position, math::float3 const& dir,
|
||||
float outerConeAngle, float radius, ShadowCameraInfo const& camera,
|
||||
FLightManager::ShadowParams const& params) noexcept {
|
||||
void ShadowMap::updateSpot(const FScene::LightSoa& lightData, size_t index,
|
||||
filament::CameraInfo const& camera,
|
||||
const ShadowMapInfo& shadowMapInfo,
|
||||
FScene const& scene, SceneInfo& sceneInfo) noexcept {
|
||||
|
||||
mShadowMapInfo = shadowMapInfo;
|
||||
|
||||
auto& lcm = mEngine.getLightManager();
|
||||
auto li = lightData.elementAt<FScene::LIGHT_INSTANCE>(index);
|
||||
auto position = lightData.elementAt<FScene::POSITION_RADIUS>(index).xyz;
|
||||
auto direction = lightData.elementAt<FScene::DIRECTION>(index);
|
||||
auto radius = lightData.elementAt<FScene::POSITION_RADIUS>(index).w;
|
||||
auto outerConeAngle = lcm.getSpotLightOuterCone(li);
|
||||
const FLightManager::ShadowParams& params = lcm.getShadowParams(li);
|
||||
|
||||
// TODO: correctly compute if this spot light has any visible shadows.
|
||||
mHasVisibleShadows = true;
|
||||
|
||||
/*
|
||||
* 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(direction, position);
|
||||
|
||||
// find decent near/far
|
||||
ShadowMap::updateSceneInfo(Mv, scene, sceneInfo, mShadowMapInfo.spotIndex);
|
||||
// FIXME: we need a configuration for minimum near plane (for now hardcoded to 1cm)
|
||||
float nearPlane = std::max(0.01f, -sceneInfo.lsNearFar.x);
|
||||
float farPlane = std::min(radius, -sceneInfo.lsNearFar.y);
|
||||
|
||||
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
|
||||
// 1) focus on the casters
|
||||
// 2) additionally focus that on intersection of view & receivers
|
||||
// Alternatively,
|
||||
// Project receivers, casters and view onto near plane,
|
||||
// compute intersection of that which gives the l,r,t,b planes
|
||||
|
||||
// For spotlights, we store the texel size at 1 world unit
|
||||
// The size of a texel in world unit is given by: (near/dimension) / lightspace.z,
|
||||
// Note: this would not work with LISPSM, which warps the texture space.
|
||||
mTexelSizeAtOneMeterWs = nearPlane / float(mShadowMapInfo.shadowDimension);
|
||||
|
||||
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;
|
||||
const mat4f b = mat4f::translation(dir * constantBias);
|
||||
const mat4f b = mat4f::translation(direction * constantBias);
|
||||
const mat4f Sb = S * b;
|
||||
|
||||
// It's important to set the light camera's model matrix separately from its projection, so that
|
||||
@@ -505,8 +457,8 @@ void ShadowMap::computeShadowCameraSpot(math::float3 const& position, math::floa
|
||||
mDebugCamera->setCustomProjection(mat4(Sb * camera.worldOrigin), nearPlane, radius);
|
||||
}
|
||||
|
||||
mat4f ShadowMap::applyLISPSM(math::mat4f& Wp,
|
||||
ShadowCameraInfo const& camera, FLightManager::ShadowParams const& params,
|
||||
mat4f ShadowMap::applyLISPSM(mat4f& Wp,
|
||||
filament::CameraInfo const& camera, FLightManager::ShadowParams const& params,
|
||||
mat4f const& LMpMv,
|
||||
FrustumBoxIntersection const& wsShadowReceiversVolume, size_t vertexCount,
|
||||
float3 const& dir) {
|
||||
@@ -514,7 +466,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 +480,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 +515,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 +581,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 +615,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 +646,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 +757,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 +806,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 +940,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 +953,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
|
||||
@@ -1015,10 +967,10 @@ float ShadowMap::texelSizeWorldSpace(const mat4f& Wp, const mat4f& MbMtF) const
|
||||
|
||||
const float ures = 1.0f / mShadowMapInfo.shadowDimension;
|
||||
const float vres = 1.0f / mShadowMapInfo.shadowDimension;
|
||||
const float dres = mShadowMapInfo.zResolution;
|
||||
const float dres = 1.0f / 65536.0f;
|
||||
|
||||
constexpr bool JACOBIAN_ESTIMATE = false;
|
||||
if (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 +1015,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 {
|
||||
@@ -1075,19 +1026,81 @@ void ShadowMap::visitScene(const FScene& scene, uint32_t visibleLayers,
|
||||
float3 const* const UTILS_RESTRICT worldAABBExtent = soa.data<FScene::WORLD_AABB_EXTENT>();
|
||||
uint8_t const* const UTILS_RESTRICT layers = soa.data<FScene::LAYERS>();
|
||||
State const* const UTILS_RESTRICT visibility = soa.data<FScene::VISIBILITY_STATE>();
|
||||
auto const* const UTILS_RESTRICT visibleMasks = soa.data<FScene::VISIBLE_MASK>();
|
||||
size_t c = soa.size();
|
||||
for (size_t i = 0; i < c; i++) {
|
||||
if (layers[i] & visibleLayers) {
|
||||
const Aabb aabb{ worldAABBCenter[i] - worldAABBExtent[i],
|
||||
worldAABBCenter[i] + worldAABBExtent[i] };
|
||||
if (visibility[i].castShadows) {
|
||||
casters(aabb);
|
||||
casters(aabb, visibleMasks[i]);
|
||||
}
|
||||
if (visibility[i].receiveShadows) {
|
||||
receivers(aabb);
|
||||
receivers(aabb, visibleMasks[i]);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
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, Culler::result_type) {
|
||||
sceneInfo.wsShadowCastersVolume.min =
|
||||
min(sceneInfo.wsShadowCastersVolume.min, caster.min);
|
||||
sceneInfo.wsShadowCastersVolume.max =
|
||||
max(sceneInfo.wsShadowCastersVolume.max, caster.max);
|
||||
},
|
||||
[&](Aabb receiver, Culler::result_type) {
|
||||
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, Culler::result_type) {
|
||||
float2 nf = ShadowMap::computeNearFar(Mv, caster);
|
||||
sceneInfo.lsNearFar.x = std::max(sceneInfo.lsNearFar.x, nf.x); // near
|
||||
sceneInfo.lsNearFar.y = std::min(sceneInfo.lsNearFar.y, nf.y); // far
|
||||
},
|
||||
[&](Aabb receiver, Culler::result_type) {
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
void ShadowMap::updateSceneInfo(const mat4f& Mv, FScene const& scene,
|
||||
ShadowMap::SceneInfo& sceneInfo, uint16_t index) {
|
||||
sceneInfo.lsNearFar = { std::numeric_limits<float>::lowest(), std::numeric_limits<float>::max() };
|
||||
sceneInfo.vsNearFar = { std::numeric_limits<float>::lowest(), std::numeric_limits<float>::max() };
|
||||
visitScene(scene, sceneInfo.visibleLayers,
|
||||
[&](Aabb caster, Culler::result_type mask) {
|
||||
if (mask & VISIBLE_SPOT_SHADOW_RENDERABLE_N(index)) {
|
||||
float2 nf = ShadowMap::computeNearFar(Mv, caster);
|
||||
sceneInfo.lsNearFar.x = std::max(sceneInfo.lsNearFar.x, nf.x); // near
|
||||
sceneInfo.lsNearFar.y = std::min(sceneInfo.lsNearFar.y, nf.y); // far
|
||||
}
|
||||
},
|
||||
[&](Aabb receiver, Culler::result_type) {
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
} // namespace filament
|
||||
|
||||
@@ -44,10 +44,6 @@ public:
|
||||
~ShadowMap();
|
||||
|
||||
struct ShadowMapInfo {
|
||||
// the smallest increment in depth precision
|
||||
// e.g., for 16 bit depth textures, is this 1 / (2^16)
|
||||
float zResolution = 0.0f;
|
||||
|
||||
// the dimension of the encompassing texture atlas
|
||||
uint16_t atlasDimension = 0;
|
||||
|
||||
@@ -59,11 +55,19 @@ public:
|
||||
// e.g., for a texture dimension of 512, shadowDimension would be 510
|
||||
uint16_t shadowDimension = 0;
|
||||
|
||||
// This spot shadowmap index.
|
||||
uint16_t spotIndex = 0;
|
||||
|
||||
// whether we're using vsm
|
||||
bool vsm = false;
|
||||
|
||||
// polygon offset
|
||||
backend::PolygonOffset polygonOffset{};
|
||||
};
|
||||
|
||||
struct SceneInfo {
|
||||
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 };
|
||||
|
||||
@@ -71,32 +75,34 @@ public:
|
||||
|
||||
// light's near/far expressed in light-space, calculated from the scene's content
|
||||
// assuming the light is at the origin.
|
||||
math::float2 lsNearFar;
|
||||
math::float2 lsNearFar{};
|
||||
|
||||
// Viewing camera's near/far expressed in view-space, calculated from the scene's content
|
||||
math::float2 vsNearFar;
|
||||
math::float2 vsNearFar{};
|
||||
|
||||
// World-space shadow-casters volume
|
||||
Aabb wsShadowCastersVolume;
|
||||
|
||||
// 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,
|
||||
void updateDirectional(const FScene::LightSoa& lightData, size_t index,
|
||||
filament::CameraInfo const& camera,
|
||||
const ShadowMapInfo& shadowMapInfo, const SceneInfo& cascadeParams) noexcept;
|
||||
const ShadowMapInfo& shadowMapInfo, FScene const& scene,
|
||||
SceneInfo& sceneInfo) noexcept;
|
||||
|
||||
void updateSpot(const FScene::LightSoa& lightData, size_t index,
|
||||
filament::CameraInfo const& camera,
|
||||
const ShadowMapInfo& shadowMapInfo, FScene const& scene,
|
||||
SceneInfo& sceneInfo) noexcept;
|
||||
|
||||
void render(FScene const& scene, utils::Range<uint32_t> range,
|
||||
FScene::VisibleMaskType visibilityMask, filament::CameraInfo const& cameraInfo,
|
||||
@@ -110,7 +116,7 @@ public:
|
||||
math::mat4f const& getLightSpaceMatrix() const noexcept { return mLightSpace; }
|
||||
|
||||
// return the size of a texel in world space (pre-warping)
|
||||
float getTexelSizeWorldSpace() const noexcept { return mTexelSizeWs; }
|
||||
float getTexelSizAtOneMeterWs() const noexcept { return mTexelSizeAtOneMeterWs; }
|
||||
|
||||
// Returns the light's projection. Valid after calling update().
|
||||
FCamera const& getCamera() const noexcept { return *mCamera; }
|
||||
@@ -118,22 +124,21 @@ public:
|
||||
// use only for debugging
|
||||
FCamera const& getDebugCamera() const noexcept { return *mDebugCamera; }
|
||||
|
||||
backend::PolygonOffset getPolygonOffset() const noexcept { return mPolygonOffset; }
|
||||
backend::PolygonOffset getPolygonOffset() const noexcept { return mShadowMapInfo.polygonOffset; }
|
||||
|
||||
// Call once per frame to populate the SceneInfo struct, then pass to update().
|
||||
// This computes values constant across all shadow maps.
|
||||
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);
|
||||
|
||||
static void updateSceneInfo(const math::mat4f& Mv, FScene const& scene,
|
||||
ShadowMap::SceneInfo& sceneInfo, uint16_t index);
|
||||
|
||||
private:
|
||||
struct ShadowCameraInfo {
|
||||
math::mat4f projection;
|
||||
math::mat4f model;
|
||||
math::mat4f view;
|
||||
math::mat4f worldOrigin;
|
||||
float zn = 0;
|
||||
float zf = 0;
|
||||
math::float3 const& getPosition() const noexcept { return model[3].xyz; }
|
||||
math::float3 getForwardVector() const noexcept {
|
||||
return -normalize(model[2].xyz); // the camera looks towards -z
|
||||
}
|
||||
};
|
||||
|
||||
struct Segment {
|
||||
uint8_t v0, v1;
|
||||
};
|
||||
@@ -145,16 +150,8 @@ private:
|
||||
// 8 corners, 12 segments w/ 2 intersection max -- all of this twice (8 + 12 * 2) * 2 (768 bytes)
|
||||
using FrustumBoxIntersection = std::array<math::float3, 64>;
|
||||
|
||||
void computeShadowCameraDirectional(
|
||||
math::float3 const& direction,
|
||||
ShadowCameraInfo const& camera, FLightManager::ShadowParams const& params,
|
||||
SceneInfo cascadeParams) noexcept;
|
||||
void computeShadowCameraSpot(math::float3 const& position, math::float3 const& dir,
|
||||
float outerConeAngle, float radius, ShadowCameraInfo const& camera,
|
||||
FLightManager::ShadowParams const& params) noexcept;
|
||||
|
||||
static math::mat4f applyLISPSM(math::mat4f& Wp,
|
||||
ShadowCameraInfo const& camera, FLightManager::ShadowParams const& params,
|
||||
filament::CameraInfo const& camera, FLightManager::ShadowParams const& params,
|
||||
const math::mat4f& LMpMv,
|
||||
FrustumBoxIntersection const& wsShadowReceiverVolume, size_t vertexCount,
|
||||
const math::float3& dir);
|
||||
@@ -213,8 +210,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;
|
||||
@@ -236,12 +233,11 @@ private:
|
||||
FCamera* mCamera = nullptr; // 8
|
||||
FCamera* mDebugCamera = nullptr; // 8
|
||||
math::mat4f mLightSpace; // 64
|
||||
float mTexelSizeWs = 0.0f; // 4
|
||||
float mTexelSizeAtOneMeterWs = 0.0f; // 4
|
||||
|
||||
// set-up in update()
|
||||
ShadowMapInfo mShadowMapInfo; // 12
|
||||
ShadowMapInfo mShadowMapInfo; // 20
|
||||
bool mHasVisibleShadows = false; // 1
|
||||
backend::PolygonOffset mPolygonOffset{}; // 8
|
||||
|
||||
FEngine& mEngine; // 8
|
||||
const bool mClipSpaceFlipped; // 1
|
||||
|
||||
@@ -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);
|
||||
@@ -57,10 +57,23 @@ ShadowMapManager::ShadowTechnique ShadowMapManager::update(
|
||||
FEngine& engine, FView& view,
|
||||
TypedUniformBuffer<ShadowUib>& shadowUb, FScene::RenderableSoa& renderableData,
|
||||
FScene::LightSoa& lightData) noexcept {
|
||||
calculateTextureRequirements(engine, view, lightData);
|
||||
ShadowTechnique shadowTechnique = {};
|
||||
shadowTechnique |= updateCascadeShadowMaps(engine, view, renderableData, lightData);
|
||||
shadowTechnique |= updateSpotShadowMaps(engine, view, shadowUb, renderableData, lightData);
|
||||
|
||||
calculateTextureRequirements(engine, view, 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;
|
||||
}
|
||||
|
||||
@@ -95,7 +108,7 @@ void ShadowMapManager::render(FrameGraph& fg, FEngine& engine, backend::DriverAp
|
||||
const TextureFormat vsmTextureFormat = TextureFormat::RG16F;
|
||||
|
||||
// make a copy here, because it's a very small structure
|
||||
const TextureRequirements textureRequirements = mTextureRequirements;
|
||||
const TextureAtlasRequirements textureRequirements = mTextureAtlasRequirements;
|
||||
assert_invariant(textureRequirements.layers <= MAX_SHADOW_LAYERS);
|
||||
|
||||
struct ShadowPass {
|
||||
@@ -163,9 +176,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 +253,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 +274,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 +289,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 +311,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,50 +334,48 @@ 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);
|
||||
FLightManager::ShadowOptions const& options = lcm.getShadowOptions(directionalLight);
|
||||
FLightManager::ShadowParams const& params = lcm.getShadowParams(directionalLight);
|
||||
|
||||
ShadowMap::SceneInfo sceneInfo;
|
||||
const ShadowMap::ShadowMapInfo shadowMapInfo{
|
||||
.atlasDimension = mTextureAtlasRequirements.size,
|
||||
.textureDimension = uint16_t(options.mapSize),
|
||||
.shadowDimension = uint16_t(options.mapSize - 2u),
|
||||
.vsm = view.hasVsm(),
|
||||
.polygonOffset = { // handle reversed Z
|
||||
.slope = view.hasVsm() ? 0.0f : -params.options.polygonOffsetSlope,
|
||||
.constant = view.hasVsm() ? 0.0f : -params.options.polygonOffsetConstant
|
||||
}
|
||||
};
|
||||
|
||||
if (!mCascadeShadowMaps.empty()) {
|
||||
// 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];
|
||||
ShadowMap& map = entry.getShadowMap();
|
||||
const size_t textureDimension = entry.getShadowOptions()->mapSize;
|
||||
const ShadowMap::ShadowMapInfo shadowMapInfo {
|
||||
.zResolution = mTextureZResolution,
|
||||
.atlasDimension = textureSize,
|
||||
.textureDimension = (uint16_t)textureDimension,
|
||||
.shadowDimension = (uint16_t)(textureDimension - 2),
|
||||
.vsm = view.hasVsm()
|
||||
};
|
||||
ShadowMap& shadowMap = entry.getShadowMap();
|
||||
|
||||
map.update(lightData, 0, viewingCameraInfo, shadowMapInfo, sceneInfo);
|
||||
shadowMap.updateDirectional(lightData, 0, viewingCameraInfo, shadowMapInfo, *scene, sceneInfo);
|
||||
|
||||
Frustum const& frustum = map.getCamera().getCullingFrustum();
|
||||
Frustum const& frustum = shadowMap.getCamera().getCullingFrustum();
|
||||
FView::cullRenderables(engine.getJobSystem(), renderableData, frustum,
|
||||
VISIBLE_DIR_SHADOW_RENDERABLE_BIT);
|
||||
|
||||
// note: normalBias is ignored for VSM
|
||||
const float normalBias = lcm.getShadowNormalBias(0);
|
||||
// Set shadowBias, using the first directional cascade.
|
||||
const float texelSizeWorldSpace = map.getTexelSizeWorldSpace();
|
||||
mShadowMappingUniforms.shadowBias = float3{ 0, normalBias * texelSizeWorldSpace, 0 };
|
||||
// when computing the required bias we need a half-texel size, so we multiply by 0.5 here.
|
||||
// note: normalBias is set to zero for VSM
|
||||
const float normalBias = shadowMapInfo.vsm ? 0.0f : 0.5f * lcm.getShadowNormalBias(0);
|
||||
// Texel size is constant for directional light (although that's not true when LISPSM
|
||||
// is used, but in that case we're pretending it is).
|
||||
const float wsTexelSize = shadowMap.getTexelSizAtOneMeterWs();
|
||||
mShadowMappingUniforms.shadowBias = float3{ 0, normalBias * wsTexelSize, 0 };
|
||||
}
|
||||
|
||||
// Adjust the near and far planes to tightly bound the scene.
|
||||
@@ -414,7 +425,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];
|
||||
|
||||
@@ -422,16 +432,12 @@ ShadowMapManager::ShadowTechnique ShadowMapManager::updateCascadeShadowMaps(
|
||||
ShadowMap& shadowMap = entry.getShadowMap();
|
||||
assert_invariant(entry.getLightIndex() == 0);
|
||||
|
||||
const size_t textureDimension = entry.getShadowOptions()->mapSize;
|
||||
const ShadowMap::ShadowMapInfo shadowMapInfo{
|
||||
.zResolution = mTextureZResolution,
|
||||
.atlasDimension = textureSize,
|
||||
.textureDimension = (uint16_t)textureDimension,
|
||||
.shadowDimension = (uint16_t)(textureDimension - 2),
|
||||
.vsm = view.hasVsm()
|
||||
};
|
||||
sceneInfo.csNearFar = { csSplitPosition[i], csSplitPosition[i + 1] };
|
||||
shadowMap.update(lightData, 0, viewingCameraInfo, shadowMapInfo, sceneInfo);
|
||||
|
||||
shadowMap.updateDirectional(lightData, 0,
|
||||
viewingCameraInfo, shadowMapInfo,
|
||||
*scene, sceneInfo);
|
||||
|
||||
if (shadowMap.hasVisibleShadows()) {
|
||||
mShadowMappingUniforms.lightFromWorldMatrix[i] = shadowMap.getLightSpaceMatrix();
|
||||
shadowTechnique |= ShadowTechnique::SHADOW_MAP;
|
||||
@@ -440,7 +446,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,63 +473,85 @@ 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
|
||||
auto& lcm = engine.getLightManager();
|
||||
const CameraInfo& viewingCameraInfo = view.getCameraInfo();
|
||||
|
||||
// shadow-map shadows for point/spotlights
|
||||
ShadowTechnique shadowTechnique{};
|
||||
FScene::ShadowInfo* const shadowInfo = lightData.data<FScene::SHADOW_INFO>();
|
||||
for (size_t i = 0, c = mSpotShadowMaps.size(); i < c; i++) {
|
||||
auto& entry = mSpotShadowMaps[i];
|
||||
|
||||
// compute the frustum for this light
|
||||
ShadowMap& shadowMap = entry.getShadowMap();
|
||||
size_t l = entry.getLightIndex();
|
||||
const size_t lightIndex = entry.getLightIndex();
|
||||
const FLightManager::Instance li = lightData.elementAt<FScene::LIGHT_INSTANCE>(lightIndex);
|
||||
FLightManager::ShadowParams params = lcm.getShadowParams(li);
|
||||
|
||||
const size_t textureDimension = entry.getShadowOptions()->mapSize;
|
||||
const ShadowMap::ShadowMapInfo layout{
|
||||
.zResolution = mTextureZResolution,
|
||||
.atlasDimension = textureSize,
|
||||
.textureDimension = (uint16_t)textureDimension,
|
||||
.shadowDimension = (uint16_t)(textureDimension - 2),
|
||||
.vsm = view.hasVsm()
|
||||
FLightManager::ShadowOptions const* const options = entry.getShadowOptions();
|
||||
const ShadowMap::ShadowMapInfo shadowMapInfo{
|
||||
.atlasDimension = mTextureAtlasRequirements.size,
|
||||
.textureDimension = uint16_t(options->mapSize),
|
||||
.shadowDimension = uint16_t(options->mapSize - 2u),
|
||||
.spotIndex = uint16_t(i),
|
||||
.vsm = view.hasVsm(),
|
||||
.polygonOffset = { // handle reversed Z
|
||||
.slope = view.hasVsm() ? 0.0f : -params.options.polygonOffsetSlope,
|
||||
.constant = view.hasVsm() ? 0.0f : -params.options.polygonOffsetConstant
|
||||
}
|
||||
};
|
||||
shadowMap.update(lightData, l, viewingCameraInfo, layout, {});
|
||||
|
||||
FLightManager::Instance light = lightData.elementAt<FScene::LIGHT_INSTANCE>(l);
|
||||
// for spotlights, we cull shadow casters first because we already know the frustum,
|
||||
// this will help us find better near/far plane later
|
||||
|
||||
const auto position = lightData.elementAt<FScene::POSITION_RADIUS>(lightIndex).xyz;
|
||||
const auto direction = lightData.elementAt<FScene::DIRECTION>(lightIndex);
|
||||
const auto radius = lightData.elementAt<FScene::POSITION_RADIUS>(lightIndex).w;
|
||||
const auto outerConeAngle = lcm.getSpotLightOuterCone(li);
|
||||
|
||||
const mat4f Mv = ShadowMap::getDirectionalLightViewMatrix(direction, position);
|
||||
const mat4f Mp = mat4f::perspective(outerConeAngle * f::RAD_TO_DEG * 2.0f,
|
||||
1.0f, 0.01f, radius);
|
||||
const mat4f MpMv(math::highPrecisionMultiply(Mp, Mv));
|
||||
const Frustum frustum(MpMv);
|
||||
|
||||
// Cull shadow casters
|
||||
FView::cullRenderables(engine.getJobSystem(), renderableData, frustum,
|
||||
VISIBLE_SPOT_SHADOW_RENDERABLE_N_BIT(i));
|
||||
|
||||
shadowMap.updateSpot(lightData, lightIndex,
|
||||
viewingCameraInfo, shadowMapInfo,
|
||||
*view.getScene(), sceneInfo);
|
||||
|
||||
if (shadowMap.hasVisibleShadows()) {
|
||||
// Cull shadow casters
|
||||
shadowInfo[lightIndex].castsShadows = true;
|
||||
shadowInfo[lightIndex].index = i;
|
||||
shadowInfo[lightIndex].layer = entry.getLayer();
|
||||
|
||||
const float wsTexelSizeAtOneMeter = shadowMap.getTexelSizAtOneMeterWs();
|
||||
// when computing the required bias we need a half-texel size, so we multiply by 0.5 here.
|
||||
// note: normalBias is set to zero for VSM
|
||||
const float normalBias = shadowMapInfo.vsm ? 0.0f : 0.5f * options->normalBias;
|
||||
|
||||
auto& s = shadowUb.edit();
|
||||
Frustum const& frustum = shadowMap.getCamera().getCullingFrustum();
|
||||
FView::cullRenderables(engine.getJobSystem(), renderableData, frustum,
|
||||
VISIBLE_SPOT_SHADOW_RENDERABLE_N_BIT(i));
|
||||
|
||||
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
|
||||
const float3 dir = lightData.elementAt<FScene::DIRECTION>(l);
|
||||
const float texelSizeWorldSpace = shadowMap.getTexelSizeWorldSpace();
|
||||
const float normalBias = lcm.getShadowNormalBias(light);
|
||||
s.directionShadowBias[i] = float4{ dir, normalBias * texelSizeWorldSpace };
|
||||
s.shadows[i].direction = direction;
|
||||
s.shadows[i].normalBias = normalBias * wsTexelSizeAtOneMeter;
|
||||
s.shadows[i].texelSizeAtOneMeter = wsTexelSizeAtOneMeter;
|
||||
s.shadows[i].lightFromWorldMatrix = shadowMap.getLightSpaceMatrix();
|
||||
|
||||
shadowTechnique |= ShadowTechnique::SHADOW_MAP;
|
||||
}
|
||||
}
|
||||
|
||||
// screen-space contact shadows for point/spot lights
|
||||
auto *pInstance = lightData.data<FScene::LIGHT_INSTANCE>();
|
||||
// screen-space contact shadows for point/spotlights
|
||||
auto *pLightInstances = lightData.data<FScene::LIGHT_INSTANCE>();
|
||||
for (size_t i = 0, c = lightData.size(); i < c; i++) {
|
||||
// screen-space contact shadows
|
||||
LightManager::ShadowOptions const& shadowOptions = lcm.getShadowOptions(pInstance[i]);
|
||||
LightManager::ShadowOptions const& shadowOptions = lcm.getShadowOptions(pLightInstances[i]);
|
||||
if (shadowOptions.screenSpaceContactShadows) {
|
||||
shadowTechnique |= ShadowTechnique::SCREEN_SPACE;
|
||||
shadowInfo[i].contactShadows = true;
|
||||
@@ -539,7 +567,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) {
|
||||
@@ -569,7 +597,7 @@ void ShadowMapManager::calculateTextureRequirements(FEngine& engine, FView& view
|
||||
mipLevels = std::max(1, FTexture::maxLevelCount(maxDimension) - lowMipmapLevel);
|
||||
}
|
||||
|
||||
mTextureRequirements = {
|
||||
mTextureAtlasRequirements = {
|
||||
(uint16_t)maxDimension,
|
||||
layersNeeded,
|
||||
mipLevels
|
||||
|
||||
@@ -114,18 +114,21 @@ public:
|
||||
|
||||
private:
|
||||
|
||||
struct TextureRequirements {
|
||||
// Atlas requirements, updated in ShadowMapManager::update(),
|
||||
// consumed in ShadowMapManager::render()
|
||||
struct TextureAtlasRequirements {
|
||||
uint16_t size = 0;
|
||||
uint8_t layers = 0;
|
||||
uint8_t levels = 0;
|
||||
} mTextureRequirements;
|
||||
} mTextureAtlasRequirements;
|
||||
|
||||
ShadowTechnique updateCascadeShadowMaps(FEngine& engine, FView& view,
|
||||
FScene::RenderableSoa& renderableData, FScene::LightSoa& lightData) 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;
|
||||
|
||||
@@ -199,7 +202,6 @@ private:
|
||||
// TODO: make it an option.
|
||||
// TODO: iOS does not support the DEPTH16 texture format.
|
||||
backend::TextureFormat mTextureFormat = backend::TextureFormat::DEPTH16;
|
||||
float mTextureZResolution = 1.0f / (1u << 16u);
|
||||
|
||||
ShadowMappingUniforms mShadowMappingUniforms;
|
||||
|
||||
|
||||
@@ -316,21 +316,21 @@ void FView::prepareShadowing(FEngine& engine, DriverApi& driver,
|
||||
// when we get here all the lights should be visible
|
||||
assert_invariant(lightData.elementAt<FScene::VISIBILITY>(l));
|
||||
|
||||
FLightManager::Instance light = lightData.elementAt<FScene::LIGHT_INSTANCE>(l);
|
||||
FLightManager::Instance li = lightData.elementAt<FScene::LIGHT_INSTANCE>(l);
|
||||
|
||||
if (UTILS_LIKELY(!light)) {
|
||||
if (UTILS_LIKELY(!li)) {
|
||||
continue; // invalid instance
|
||||
}
|
||||
|
||||
if (UTILS_LIKELY(!lcm.isShadowCaster(light))) {
|
||||
if (UTILS_LIKELY(!lcm.isShadowCaster(li))) {
|
||||
continue; // doesn't cast shadows
|
||||
}
|
||||
|
||||
if (UTILS_LIKELY(!lcm.isSpotLight(light))) {
|
||||
continue; // is not a spot-light (we're not supporting point-lights yet)
|
||||
if (UTILS_LIKELY(!lcm.isSpotLight(li))) {
|
||||
continue; // is not a spot-li (we're not supporting point-lights yet)
|
||||
}
|
||||
|
||||
const auto& shadowOptions = lcm.getShadowOptions(light);
|
||||
const auto& shadowOptions = lcm.getShadowOptions(li);
|
||||
mShadowMapManager.addSpotShadowMap(l, &shadowOptions);
|
||||
++shadowCastingSpotCount;
|
||||
if (shadowCastingSpotCount > CONFIG_MAX_SHADOW_CASTING_SPOTS - 1) {
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -106,7 +106,7 @@ public:
|
||||
VISIBILITY_STATE, // 1 | visibility data of the component
|
||||
SKINNING_BUFFER, // 8 | bones uniform buffer handle, count, offset
|
||||
WORLD_AABB_CENTER, // 12 | world-space bounding box center of the renderable
|
||||
VISIBLE_MASK, // 1 | each bit represents a visibility in a pass
|
||||
VISIBLE_MASK, // 2 | each bit represents a visibility in a pass
|
||||
MORPH_WEIGHTS, // 4 | floats for morphing
|
||||
CHANNELS, // 1 | currently light channels only
|
||||
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
49
filament/src/materials/separableGaussianBlur.fs
Normal file
49
filament/src/materials/separableGaussianBlur.fs
Normal file
@@ -0,0 +1,49 @@
|
||||
// BLUR_TYPE and BLUR_SWIZZLE must be defined
|
||||
// BLUR_TYPE vec2, vec3, vec4
|
||||
// BLUR_SWIZZLE r, rg, rgb, rgba
|
||||
|
||||
float vmax(const float v) {
|
||||
return v;
|
||||
}
|
||||
|
||||
void tap(inout highp BLUR_TYPE sum, const float weight, const highp vec2 position) {
|
||||
vec4 s = textureLod(materialParams_source, position, materialParams.level);
|
||||
sum.BLUR_SWIZZLE += s.BLUR_SWIZZLE * weight;
|
||||
}
|
||||
|
||||
void tapReinhard(inout highp BLUR_TYPE sum, inout float totalWeight, const float weight, const highp vec2 position) {
|
||||
vec4 s = textureLod(materialParams_source, position, materialParams.level);
|
||||
float w = weight / (1.0 + vmax(s.BLUR_SWIZZLE));
|
||||
totalWeight += w;
|
||||
sum.BLUR_SWIZZLE += s.BLUR_SWIZZLE * w;
|
||||
}
|
||||
|
||||
void postProcess(inout PostProcessInputs postProcess) {
|
||||
highp vec2 uv = variable_vertex.xy;
|
||||
|
||||
// we handle the center pixel separately
|
||||
highp BLUR_TYPE sum = BLUR_TYPE(0.0);
|
||||
|
||||
if (materialParams.reinhard != 0) {
|
||||
float totalWeight = 0.0;
|
||||
tapReinhard(sum, totalWeight, materialParams.kernel[0].x, uv);
|
||||
vec2 offset = materialParams.axis;
|
||||
for (int i = 1; i < materialParams.count; i++, offset += materialParams.axis * 2.0) {
|
||||
float k = materialParams.kernel[i].x;
|
||||
vec2 o = offset + materialParams.axis * materialParams.kernel[i].y;
|
||||
tapReinhard(sum, totalWeight, k, uv + o);
|
||||
tapReinhard(sum, totalWeight, k, uv - o);
|
||||
}
|
||||
sum *= 1.0 / totalWeight;
|
||||
} else {
|
||||
tap(sum, materialParams.kernel[0].x, uv);
|
||||
vec2 offset = materialParams.axis;
|
||||
for (int i = 1; i < materialParams.count; i++, offset += materialParams.axis * 2.0) {
|
||||
float k = materialParams.kernel[i].x;
|
||||
vec2 o = offset + materialParams.axis * materialParams.kernel[i].y;
|
||||
tap(sum, k, uv + o);
|
||||
tap(sum, k, uv - o);
|
||||
}
|
||||
}
|
||||
postProcess.color.BLUR_SWIZZLE = sum.BLUR_SWIZZLE;
|
||||
}
|
||||
@@ -1,95 +0,0 @@
|
||||
material {
|
||||
name : separableGaussianBlur,
|
||||
parameters : [
|
||||
{
|
||||
type : sampler2d,
|
||||
name : source,
|
||||
precision: medium
|
||||
},
|
||||
{
|
||||
type : float4,
|
||||
name : resolution,
|
||||
precision: high
|
||||
},
|
||||
{
|
||||
type : float2,
|
||||
name : axis
|
||||
},
|
||||
{
|
||||
type : float,
|
||||
name : level
|
||||
},
|
||||
{
|
||||
type : int,
|
||||
name : count
|
||||
},
|
||||
{
|
||||
type : int,
|
||||
name : reinhard
|
||||
},
|
||||
{
|
||||
type : float2[32],
|
||||
name : kernel
|
||||
}
|
||||
],
|
||||
variables : [
|
||||
vertex
|
||||
],
|
||||
domain : postprocess,
|
||||
depthWrite : false,
|
||||
depthCulling : false
|
||||
}
|
||||
|
||||
vertex {
|
||||
void postProcessVertex(inout PostProcessVertexInputs postProcess) {
|
||||
// in the fragment shader, this is interpolated to pixel centers, but since we use
|
||||
// texel-fetch, it's not what we want. Convert from screen uv to texture uv.
|
||||
vec2 size = vec2(textureSize(materialParams_source, int(materialParams.level)));
|
||||
postProcess.vertex.xy = (postProcess.normalizedUV - 0.5 * materialParams.resolution.zw) + 0.5 / size;
|
||||
}
|
||||
}
|
||||
|
||||
fragment {
|
||||
void tap(inout highp vec3 sum, const float weight, const highp vec2 position) {
|
||||
vec3 s = textureLod(materialParams_source, position, materialParams.level).rgb;
|
||||
sum += s * weight;
|
||||
}
|
||||
|
||||
void tapReinhard(inout highp vec3 sum, inout float totalWeight, const float weight, const highp vec2 position) {
|
||||
vec3 s = textureLod(materialParams_source, position, materialParams.level).rgb;
|
||||
float w = weight / (1.0 + max3(s));
|
||||
totalWeight += w;
|
||||
sum += s * w;
|
||||
}
|
||||
|
||||
void postProcess(inout PostProcessInputs postProcess) {
|
||||
highp vec2 uv = variable_vertex.xy;
|
||||
|
||||
// we handle the center pixel separately
|
||||
highp vec3 sum = vec3(0);
|
||||
|
||||
if (materialParams.reinhard != 0) {
|
||||
float totalWeight = 0.0;
|
||||
tapReinhard(sum, totalWeight, materialParams.kernel[0].x, uv);
|
||||
vec2 offset = materialParams.axis;
|
||||
for (int i = 1; i < materialParams.count; i++, offset += materialParams.axis * 2.0) {
|
||||
float k = materialParams.kernel[i].x;
|
||||
vec2 o = offset + materialParams.axis * materialParams.kernel[i].y;
|
||||
tapReinhard(sum, totalWeight, k, uv + o);
|
||||
tapReinhard(sum, totalWeight, k, uv - o);
|
||||
}
|
||||
sum *= 1.0 / totalWeight;
|
||||
} else {
|
||||
tap(sum, materialParams.kernel[0].x, uv);
|
||||
vec2 offset = materialParams.axis;
|
||||
for (int i = 1; i < materialParams.count; i++, offset += materialParams.axis * 2.0) {
|
||||
float k = materialParams.kernel[i].x;
|
||||
vec2 o = offset + materialParams.axis * materialParams.kernel[i].y;
|
||||
tap(sum, k, uv + o);
|
||||
tap(sum, k, uv - o);
|
||||
}
|
||||
}
|
||||
|
||||
postProcess.color.rgb = sum;
|
||||
}
|
||||
}
|
||||
6
filament/src/materials/separableGaussianBlur.vs
Normal file
6
filament/src/materials/separableGaussianBlur.vs
Normal file
@@ -0,0 +1,6 @@
|
||||
void postProcessVertex(inout PostProcessVertexInputs postProcess) {
|
||||
// in the fragment shader, this is interpolated to pixel centers, but since we use
|
||||
// texel-fetch, it's not what we want. Convert from screen uv to texture uv.
|
||||
vec2 size = vec2(textureSize(materialParams_source, int(materialParams.level)));
|
||||
postProcess.vertex.xy = (postProcess.normalizedUV - 0.5 * materialParams.resolution.zw) + 0.5 / size;
|
||||
}
|
||||
54
filament/src/materials/separableGaussianBlur1.mat
Normal file
54
filament/src/materials/separableGaussianBlur1.mat
Normal file
@@ -0,0 +1,54 @@
|
||||
material {
|
||||
name : separableGaussianBlur1,
|
||||
parameters : [
|
||||
{
|
||||
type : sampler2d,
|
||||
name : source,
|
||||
precision: medium
|
||||
},
|
||||
{
|
||||
type : float4,
|
||||
name : resolution,
|
||||
precision: high
|
||||
},
|
||||
{
|
||||
type : float2,
|
||||
name : axis
|
||||
},
|
||||
{
|
||||
type : float,
|
||||
name : level
|
||||
},
|
||||
{
|
||||
type : int,
|
||||
name : count
|
||||
},
|
||||
{
|
||||
type : int,
|
||||
name : reinhard
|
||||
},
|
||||
{
|
||||
type : float2[32],
|
||||
name : kernel
|
||||
}
|
||||
],
|
||||
variables : [
|
||||
vertex
|
||||
],
|
||||
domain : postprocess,
|
||||
depthWrite : false,
|
||||
depthCulling : false
|
||||
}
|
||||
|
||||
vertex {
|
||||
#include "separableGaussianBlur.vs"
|
||||
}
|
||||
|
||||
fragment {
|
||||
|
||||
#define BLUR_TYPE vec2
|
||||
#define BLUR_SWIZZLE r
|
||||
|
||||
#include "separableGaussianBlur.fs"
|
||||
|
||||
}
|
||||
54
filament/src/materials/separableGaussianBlur2.mat
Normal file
54
filament/src/materials/separableGaussianBlur2.mat
Normal file
@@ -0,0 +1,54 @@
|
||||
material {
|
||||
name : separableGaussianBlur2,
|
||||
parameters : [
|
||||
{
|
||||
type : sampler2d,
|
||||
name : source,
|
||||
precision: medium
|
||||
},
|
||||
{
|
||||
type : float4,
|
||||
name : resolution,
|
||||
precision: high
|
||||
},
|
||||
{
|
||||
type : float2,
|
||||
name : axis
|
||||
},
|
||||
{
|
||||
type : float,
|
||||
name : level
|
||||
},
|
||||
{
|
||||
type : int,
|
||||
name : count
|
||||
},
|
||||
{
|
||||
type : int,
|
||||
name : reinhard
|
||||
},
|
||||
{
|
||||
type : float2[32],
|
||||
name : kernel
|
||||
}
|
||||
],
|
||||
variables : [
|
||||
vertex
|
||||
],
|
||||
domain : postprocess,
|
||||
depthWrite : false,
|
||||
depthCulling : false
|
||||
}
|
||||
|
||||
vertex {
|
||||
#include "separableGaussianBlur.vs"
|
||||
}
|
||||
|
||||
fragment {
|
||||
|
||||
#define BLUR_TYPE vec2
|
||||
#define BLUR_SWIZZLE rg
|
||||
|
||||
#include "separableGaussianBlur.fs"
|
||||
|
||||
}
|
||||
54
filament/src/materials/separableGaussianBlur3.mat
Normal file
54
filament/src/materials/separableGaussianBlur3.mat
Normal file
@@ -0,0 +1,54 @@
|
||||
material {
|
||||
name : separableGaussianBlur3,
|
||||
parameters : [
|
||||
{
|
||||
type : sampler2d,
|
||||
name : source,
|
||||
precision: medium
|
||||
},
|
||||
{
|
||||
type : float4,
|
||||
name : resolution,
|
||||
precision: high
|
||||
},
|
||||
{
|
||||
type : float2,
|
||||
name : axis
|
||||
},
|
||||
{
|
||||
type : float,
|
||||
name : level
|
||||
},
|
||||
{
|
||||
type : int,
|
||||
name : count
|
||||
},
|
||||
{
|
||||
type : int,
|
||||
name : reinhard
|
||||
},
|
||||
{
|
||||
type : float2[32],
|
||||
name : kernel
|
||||
}
|
||||
],
|
||||
variables : [
|
||||
vertex
|
||||
],
|
||||
domain : postprocess,
|
||||
depthWrite : false,
|
||||
depthCulling : false
|
||||
}
|
||||
|
||||
vertex {
|
||||
#include "separableGaussianBlur.vs"
|
||||
}
|
||||
|
||||
fragment {
|
||||
|
||||
#define BLUR_TYPE vec3
|
||||
#define BLUR_SWIZZLE rgb
|
||||
|
||||
#include "separableGaussianBlur.fs"
|
||||
|
||||
}
|
||||
54
filament/src/materials/separableGaussianBlur4.mat
Normal file
54
filament/src/materials/separableGaussianBlur4.mat
Normal file
@@ -0,0 +1,54 @@
|
||||
material {
|
||||
name : separableGaussianBlur4,
|
||||
parameters : [
|
||||
{
|
||||
type : sampler2d,
|
||||
name : source,
|
||||
precision: medium
|
||||
},
|
||||
{
|
||||
type : float4,
|
||||
name : resolution,
|
||||
precision: high
|
||||
},
|
||||
{
|
||||
type : float2,
|
||||
name : axis
|
||||
},
|
||||
{
|
||||
type : float,
|
||||
name : level
|
||||
},
|
||||
{
|
||||
type : int,
|
||||
name : count
|
||||
},
|
||||
{
|
||||
type : int,
|
||||
name : reinhard
|
||||
},
|
||||
{
|
||||
type : float2[32],
|
||||
name : kernel
|
||||
}
|
||||
],
|
||||
variables : [
|
||||
vertex
|
||||
],
|
||||
domain : postprocess,
|
||||
depthWrite : false,
|
||||
depthCulling : false
|
||||
}
|
||||
|
||||
vertex {
|
||||
#include "separableGaussianBlur.vs"
|
||||
}
|
||||
|
||||
fragment {
|
||||
|
||||
#define BLUR_TYPE vec4
|
||||
#define BLUR_SWIZZLE rgba
|
||||
|
||||
#include "separableGaussianBlur.fs"
|
||||
|
||||
}
|
||||
@@ -1,12 +1,12 @@
|
||||
Pod::Spec.new do |spec|
|
||||
spec.name = "Filament"
|
||||
spec.version = "1.12.10"
|
||||
spec.version = "1.14.1"
|
||||
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.14.1/filament-v1.14.1-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 = 14;
|
||||
|
||||
/**
|
||||
* 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);
|
||||
@@ -184,9 +185,15 @@ static_assert(sizeof(LightsUib) == 64, "the actual UBO is an array of 256 mat4")
|
||||
// UBO for punctual (spot light) shadows.
|
||||
struct ShadowUib {
|
||||
static constexpr utils::StaticString _name{ "ShadowUniforms" };
|
||||
math::mat4f spotLightFromWorldMatrix[CONFIG_MAX_SHADOW_CASTING_SPOTS];
|
||||
math::float4 directionShadowBias[CONFIG_MAX_SHADOW_CASTING_SPOTS]; // light direction, normal bias
|
||||
struct alignas(16) ShadowData {
|
||||
math::mat4f lightFromWorldMatrix;
|
||||
math::float3 direction;
|
||||
float normalBias;
|
||||
float texelSizeAtOneMeter;
|
||||
};
|
||||
ShadowData shadows[CONFIG_MAX_SHADOW_CASTING_SPOTS];
|
||||
};
|
||||
static_assert(sizeof(ShadowUib) <= 16384, "ShadowUib exceed max UBO size");
|
||||
|
||||
// UBO froxel record buffer.
|
||||
struct FroxelRecordUib {
|
||||
@@ -202,9 +209,8 @@ struct PerRenderableUibBone {
|
||||
math::float4 s = { 1, 1, 1, 0 };
|
||||
math::float4 ns = { 1, 1, 1, 0 };
|
||||
};
|
||||
|
||||
static_assert(CONFIG_MAX_BONE_COUNT * sizeof(PerRenderableUibBone) <= 16384,
|
||||
"Bones exceed max UBO size");
|
||||
"PerRenderableUibBone exceed max UBO size");
|
||||
|
||||
} // namespace filament
|
||||
|
||||
|
||||
@@ -63,23 +63,38 @@ public:
|
||||
Builder& add(utils::StaticString const& uniformName, size_t size,
|
||||
Type type, Precision precision = Precision::DEFAULT);
|
||||
|
||||
// Add a known struct
|
||||
Builder& add(utils::CString const& uniformName, size_t size,
|
||||
utils::CString const& structName, size_t stride);
|
||||
|
||||
template<size_t N>
|
||||
Builder& add(utils::StringLiteral<N> const& uniformName, size_t size,
|
||||
Type type, Precision precision = Precision::DEFAULT) {
|
||||
return add(utils::StaticString{ uniformName }, size, type, precision);
|
||||
}
|
||||
|
||||
template<size_t N0, size_t N1>
|
||||
Builder& add(utils::StringLiteral<N0> const& uniformName, size_t size,
|
||||
utils::StringLiteral<N1> const& structName, size_t stride) {
|
||||
return add(utils::StaticString{ uniformName }, size,
|
||||
utils::StaticString{ structName }, stride);
|
||||
}
|
||||
|
||||
// build and return the UniformInterfaceBlock
|
||||
UniformInterfaceBlock build();
|
||||
private:
|
||||
friend class UniformInterfaceBlock;
|
||||
struct Entry {
|
||||
Entry(utils::CString name, uint32_t size, Type type, Precision precision) noexcept
|
||||
: name(std::move(name)), size(size), type(type), precision(precision) { }
|
||||
: name(std::move(name)), size(size), type(type), precision(precision), stride(strideForType(type, 0)) { }
|
||||
Entry(utils::CString name, uint32_t size, utils::CString structName, size_t stride) noexcept
|
||||
: name(std::move(name)), size(size), type(Type::STRUCT), structName(std::move(structName)), stride(stride) { }
|
||||
utils::CString name;
|
||||
uint32_t size;
|
||||
Type type;
|
||||
Precision precision;
|
||||
Precision precision{};
|
||||
utils::CString structName{};
|
||||
uint32_t stride;
|
||||
};
|
||||
utils::CString mName;
|
||||
std::vector<Entry> mEntries;
|
||||
@@ -92,6 +107,7 @@ public:
|
||||
Type type; // type of this uniform
|
||||
uint32_t size; // size of the array in elements, or 1 if not an array
|
||||
Precision precision;// precision of this uniform
|
||||
utils::CString structName;// name of this uniform structure if type is STRUCT
|
||||
// returns offset in bytes of this uniform (at index if an array)
|
||||
inline size_t getBufferOffset(size_t index = 0) const {
|
||||
assert(index < size);
|
||||
@@ -125,7 +141,7 @@ private:
|
||||
explicit UniformInterfaceBlock(Builder const& builder) noexcept;
|
||||
|
||||
static uint8_t baseAlignmentForType(Type type) noexcept;
|
||||
static uint8_t strideForType(Type type) noexcept;
|
||||
static uint8_t strideForType(Type type, uint32_t stride) noexcept;
|
||||
|
||||
utils::CString mName;
|
||||
std::vector<UniformInfo> mUniformsInfoList;
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -18,8 +18,6 @@
|
||||
|
||||
#include <utils/Panic.h>
|
||||
#include <utils/compiler.h>
|
||||
#include <private/filament/UniformInterfaceBlock.h>
|
||||
|
||||
|
||||
using namespace utils;
|
||||
|
||||
@@ -64,6 +62,14 @@ UniformInterfaceBlock::Builder& UniformInterfaceBlock::Builder::add(
|
||||
return *this;
|
||||
}
|
||||
|
||||
UniformInterfaceBlock::Builder& UniformInterfaceBlock::Builder::add(
|
||||
utils::CString const& uniformName, size_t size,
|
||||
utils::CString const& structName, size_t stride) {
|
||||
mEntries.emplace_back(uniformName, (uint32_t)size, structName, stride);
|
||||
return *this;
|
||||
}
|
||||
|
||||
|
||||
UniformInterfaceBlock UniformInterfaceBlock::Builder::build() {
|
||||
return UniformInterfaceBlock(*this);
|
||||
}
|
||||
@@ -89,7 +95,7 @@ UniformInterfaceBlock::UniformInterfaceBlock(Builder const& builder) noexcept
|
||||
uint16_t offset = 0;
|
||||
for (auto const& e : builder.mEntries) {
|
||||
size_t alignment = baseAlignmentForType(e.type);
|
||||
uint8_t stride = strideForType(e.type);
|
||||
uint8_t stride = strideForType(e.type, e.stride);
|
||||
if (e.size > 1) { // this is an array
|
||||
// round the alignment up to that of a float4
|
||||
alignment = (alignment + 3) & ~3;
|
||||
@@ -101,7 +107,7 @@ UniformInterfaceBlock::UniformInterfaceBlock(Builder const& builder) noexcept
|
||||
offset += padding;
|
||||
|
||||
UniformInfo& info = uniformsInfoList[i];
|
||||
info = { e.name, offset, stride, e.type, e.size, e.precision };
|
||||
info = { e.name, offset, stride, e.type, e.size, e.precision, e.structName };
|
||||
|
||||
// record this uniform info
|
||||
infoMap[info.name.c_str()] = i;
|
||||
@@ -154,11 +160,12 @@ uint8_t UTILS_NOINLINE UniformInterfaceBlock::baseAlignmentForType(UniformInterf
|
||||
case Type::UINT4:
|
||||
case Type::MAT3:
|
||||
case Type::MAT4:
|
||||
case Type::STRUCT:
|
||||
return 4;
|
||||
}
|
||||
}
|
||||
|
||||
uint8_t UTILS_NOINLINE UniformInterfaceBlock::strideForType(UniformInterfaceBlock::Type type) noexcept {
|
||||
uint8_t UTILS_NOINLINE UniformInterfaceBlock::strideForType(UniformInterfaceBlock::Type type, uint32_t stride) noexcept {
|
||||
switch (type) {
|
||||
case Type::BOOL:
|
||||
case Type::INT:
|
||||
@@ -184,6 +191,8 @@ uint8_t UTILS_NOINLINE UniformInterfaceBlock::strideForType(UniformInterfaceBloc
|
||||
return 12;
|
||||
case Type::MAT4:
|
||||
return 16;
|
||||
case Type::STRUCT:
|
||||
return stride;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -190,6 +190,7 @@ public:
|
||||
using MaterialDomain = filament::MaterialDomain;
|
||||
using RefractionMode = filament::RefractionMode;
|
||||
using RefractionType = filament::RefractionType;
|
||||
using VertexAttribute = filament::VertexAttribute;
|
||||
|
||||
using ShaderQuality = filament::ShaderQuality;
|
||||
using BlendingMode = filament::BlendingMode;
|
||||
@@ -285,10 +286,10 @@ public:
|
||||
*
|
||||
* position is always required and normal depends on the shading model.
|
||||
*/
|
||||
MaterialBuilder& require(filament::VertexAttribute attribute) noexcept;
|
||||
MaterialBuilder& require(VertexAttribute attribute) noexcept;
|
||||
|
||||
//! Specify the domain that this material will operate in.
|
||||
MaterialBuilder& materialDomain(MaterialDomain materialDomain) noexcept;
|
||||
MaterialBuilder& materialDomain(filament::MaterialDomain materialDomain) noexcept;
|
||||
|
||||
/**
|
||||
* Set the code content of this material.
|
||||
@@ -608,7 +609,7 @@ public:
|
||||
|
||||
// Preview the first shader generated by the given CodeGenParams.
|
||||
// This is used to run Static Code Analysis before generating a package.
|
||||
const std::string peek(filament::backend::ShaderType type,
|
||||
std::string peek(filament::backend::ShaderType type,
|
||||
const CodeGenParams& params, const PropertyList& properties) noexcept;
|
||||
|
||||
// Returns true if any of the parameter samplers is of type samplerExternal
|
||||
@@ -651,6 +652,9 @@ private:
|
||||
const std::vector<Variant>& variants, ChunkContainer& container,
|
||||
const MaterialInfo& info) const noexcept;
|
||||
|
||||
bool hasCustomVaryings() const noexcept;
|
||||
bool needsStandardDepthProgram() const noexcept;
|
||||
|
||||
bool isLit() const noexcept { return mShading != filament::Shading::UNLIT; }
|
||||
|
||||
utils::CString mMaterialName;
|
||||
@@ -680,7 +684,7 @@ private:
|
||||
bool mIncludesResolved = false;
|
||||
};
|
||||
|
||||
ShaderCode mMaterialCode;
|
||||
ShaderCode mMaterialFragmentCode;
|
||||
ShaderCode mMaterialVertexCode;
|
||||
|
||||
IncludeCallback mIncludeCallback = nullptr;
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -17,6 +17,7 @@
|
||||
#include "filamat/MaterialBuilder.h"
|
||||
|
||||
#include <atomic>
|
||||
#include <utility>
|
||||
#include <vector>
|
||||
|
||||
#include <utils/JobSystem.h>
|
||||
@@ -141,13 +142,13 @@ MaterialBuilder& MaterialBuilder::fileName(const char* fileName) noexcept {
|
||||
}
|
||||
|
||||
MaterialBuilder& MaterialBuilder::material(const char* code, size_t line) noexcept {
|
||||
mMaterialCode.setUnresolved(CString(code));
|
||||
mMaterialCode.setLineOffset(line);
|
||||
mMaterialFragmentCode.setUnresolved(CString(code));
|
||||
mMaterialFragmentCode.setLineOffset(line);
|
||||
return *this;
|
||||
}
|
||||
|
||||
MaterialBuilder& MaterialBuilder::includeCallback(IncludeCallback callback) noexcept {
|
||||
mIncludeCallback = callback;
|
||||
mIncludeCallback = std::move(callback);
|
||||
return *this;
|
||||
}
|
||||
|
||||
@@ -244,12 +245,12 @@ MaterialBuilder& MaterialBuilder::parameter(SubpassType subpassType, const char*
|
||||
return parameter(subpassType, SamplerFormat::FLOAT, ParameterPrecision::DEFAULT, name);
|
||||
}
|
||||
|
||||
MaterialBuilder& MaterialBuilder::require(filament::VertexAttribute attribute) noexcept {
|
||||
MaterialBuilder& MaterialBuilder::require(VertexAttribute attribute) noexcept {
|
||||
mRequiredAttributes.set(attribute);
|
||||
return *this;
|
||||
}
|
||||
|
||||
MaterialBuilder& MaterialBuilder::materialDomain(MaterialDomain materialDomain) noexcept {
|
||||
MaterialBuilder& MaterialBuilder::materialDomain(filament::MaterialDomain materialDomain) noexcept {
|
||||
mMaterialDomain = materialDomain;
|
||||
return *this;
|
||||
}
|
||||
@@ -520,7 +521,7 @@ bool MaterialBuilder::findAllProperties() noexcept {
|
||||
return true;
|
||||
#else
|
||||
GLSLToolsLite glslTools;
|
||||
if (glslTools.findProperties(ShaderType::FRAGMENT, mMaterialCode.getResolved(), mProperties)) {
|
||||
if (glslTools.findProperties(ShaderType::FRAGMENT, mMaterialFragmentCode.getResolved(), mProperties)) {
|
||||
return glslTools.findProperties(
|
||||
ShaderType::VERTEX, mMaterialVertexCode.getResolved(), mProperties);
|
||||
}
|
||||
@@ -587,7 +588,7 @@ bool MaterialBuilder::ShaderCode::resolveIncludes(IncludeCallback callback,
|
||||
.lineNumberOffset = getLineOffset(),
|
||||
.name = utils::CString("")
|
||||
};
|
||||
if (!::filamat::resolveIncludes(source, callback, options)) {
|
||||
if (!::filamat::resolveIncludes(source, std::move(callback), options)) {
|
||||
return false;
|
||||
}
|
||||
mCode = source.text;
|
||||
@@ -635,17 +636,11 @@ bool MaterialBuilder::generateShaders(JobSystem& jobSystem, const std::vector<Va
|
||||
// End: must be protected by lock
|
||||
|
||||
ShaderGenerator sg(
|
||||
mProperties, mVariables, mOutputs, mDefines, mMaterialCode.getResolved(),
|
||||
mMaterialCode.getLineOffset(), mMaterialVertexCode.getResolved(),
|
||||
mProperties, mVariables, mOutputs, mDefines, mMaterialFragmentCode.getResolved(),
|
||||
mMaterialFragmentCode.getLineOffset(), mMaterialVertexCode.getResolved(),
|
||||
mMaterialVertexCode.getLineOffset(), mMaterialDomain);
|
||||
|
||||
bool emptyVertexCode = mMaterialVertexCode.getResolved().empty();
|
||||
bool customDepth = sg.hasCustomDepthShader() ||
|
||||
mBlendingMode == BlendingMode::MASKED ||
|
||||
((mBlendingMode == BlendingMode::TRANSPARENT ||mBlendingMode == BlendingMode::FADE) &&
|
||||
mTransparentShadow) ||
|
||||
!emptyVertexCode;
|
||||
container.addSimpleChild<bool>(ChunkType::MaterialHasCustomDepthShader, customDepth);
|
||||
container.addSimpleChild<bool>(ChunkType::MaterialHasCustomDepthShader, needsStandardDepthProgram());
|
||||
|
||||
std::atomic_bool cancelJobs(false);
|
||||
bool firstJob = true;
|
||||
@@ -748,7 +743,7 @@ bool MaterialBuilder::generateShaders(JobSystem& jobSystem, const std::vector<Va
|
||||
|
||||
if (targetApi == TargetApi::OPENGL) {
|
||||
if (targetLanguage == TargetLanguage::SPIRV) {
|
||||
sg.fixupExternalSamplers(shaderModel, shader, info);
|
||||
ShaderGenerator::fixupExternalSamplers(shaderModel, shader, info);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -915,7 +910,7 @@ Package MaterialBuilder::build(JobSystem& jobSystem) noexcept {
|
||||
}
|
||||
|
||||
// Resolve all the #include directives within user code.
|
||||
if (!mMaterialCode.resolveIncludes(mIncludeCallback, mFileName) ||
|
||||
if (!mMaterialFragmentCode.resolveIncludes(mIncludeCallback, mFileName) ||
|
||||
!mMaterialVertexCode.resolveIncludes(mIncludeCallback, mFileName)) {
|
||||
return Package::invalidPackage();
|
||||
}
|
||||
@@ -967,10 +962,30 @@ Package MaterialBuilder::build(JobSystem& jobSystem) noexcept {
|
||||
return package;
|
||||
}
|
||||
|
||||
const std::string MaterialBuilder::peek(filament::backend::ShaderType type,
|
||||
bool MaterialBuilder::hasCustomVaryings() const noexcept {
|
||||
for (const auto& variable : mVariables) {
|
||||
if (!variable.empty()) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool MaterialBuilder::needsStandardDepthProgram() const noexcept {
|
||||
const bool hasEmptyVertexCode = mMaterialVertexCode.getResolved().empty();
|
||||
return !hasEmptyVertexCode ||
|
||||
hasCustomVaryings() ||
|
||||
mBlendingMode == BlendingMode::MASKED ||
|
||||
(mTransparentShadow &&
|
||||
(mBlendingMode == BlendingMode::TRANSPARENT ||
|
||||
mBlendingMode == BlendingMode::FADE));
|
||||
}
|
||||
|
||||
std::string MaterialBuilder::peek(filament::backend::ShaderType type,
|
||||
const CodeGenParams& params, const PropertyList& properties) noexcept {
|
||||
ShaderGenerator sg(properties, mVariables, mOutputs, mDefines, mMaterialCode.getResolved(),
|
||||
mMaterialCode.getLineOffset(), mMaterialVertexCode.getResolved(),
|
||||
|
||||
ShaderGenerator sg(properties, mVariables, mOutputs, mDefines, mMaterialFragmentCode.getResolved(),
|
||||
mMaterialFragmentCode.getLineOffset(), mMaterialVertexCode.getResolved(),
|
||||
mMaterialVertexCode.getLineOffset(), mMaterialDomain);
|
||||
|
||||
MaterialInfo info;
|
||||
@@ -987,8 +1002,6 @@ const std::string MaterialBuilder::peek(filament::backend::ShaderType type,
|
||||
return sg.createFragmentProgram(ShaderModel(params.shaderModel), params.targetApi,
|
||||
params.targetLanguage, info, 0, mInterpolation);
|
||||
}
|
||||
|
||||
return std::string("");
|
||||
}
|
||||
|
||||
void MaterialBuilder::writeCommonChunks(ChunkContainer& container, MaterialInfo& info) const noexcept {
|
||||
|
||||
@@ -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
|
||||
@@ -145,8 +145,7 @@ UniformInterfaceBlock const& UibGenerator::getLightsUib() noexcept {
|
||||
UniformInterfaceBlock const& UibGenerator::getShadowUib() noexcept {
|
||||
static UniformInterfaceBlock uib = UniformInterfaceBlock::Builder()
|
||||
.name(ShadowUib::_name)
|
||||
.add("spotLightFromWorldMatrix", CONFIG_MAX_SHADOW_CASTING_SPOTS, UniformInterfaceBlock::Type::MAT4, Precision::HIGH)
|
||||
.add("directionShadowBias", CONFIG_MAX_SHADOW_CASTING_SPOTS, UniformInterfaceBlock::Type::FLOAT4, Precision::HIGH)
|
||||
.add("shadows", CONFIG_MAX_SHADOW_CASTING_SPOTS, "ShadowData", sizeof(ShadowUib::ShadowData))
|
||||
.build();
|
||||
return uib;
|
||||
}
|
||||
|
||||
@@ -32,7 +32,7 @@ using namespace filament;
|
||||
using namespace backend;
|
||||
using namespace utils;
|
||||
|
||||
io::sstream& CodeGenerator::generateSeparator(io::sstream& out) const {
|
||||
io::sstream& CodeGenerator::generateSeparator(io::sstream& out) {
|
||||
out << '\n';
|
||||
return out;
|
||||
}
|
||||
@@ -137,12 +137,12 @@ Precision CodeGenerator::getDefaultUniformPrecision() const {
|
||||
}
|
||||
}
|
||||
|
||||
io::sstream& CodeGenerator::generateEpilog(io::sstream& out) const {
|
||||
io::sstream& CodeGenerator::generateEpilog(io::sstream& out) {
|
||||
out << "\n"; // For line compression all shaders finish with a newline character.
|
||||
return out;
|
||||
}
|
||||
|
||||
io::sstream& CodeGenerator::generateShaderMain(io::sstream& out, ShaderType type) const {
|
||||
io::sstream& CodeGenerator::generateShaderMain(io::sstream& out, ShaderType type) {
|
||||
if (type == ShaderType::VERTEX) {
|
||||
out << SHADERS_MAIN_VS_DATA;
|
||||
} else if (type == ShaderType::FRAGMENT) {
|
||||
@@ -151,7 +151,7 @@ io::sstream& CodeGenerator::generateShaderMain(io::sstream& out, ShaderType type
|
||||
return out;
|
||||
}
|
||||
|
||||
io::sstream& CodeGenerator::generatePostProcessMain(io::sstream& out, ShaderType type) const {
|
||||
io::sstream& CodeGenerator::generatePostProcessMain(io::sstream& out, ShaderType type) {
|
||||
if (type == ShaderType::VERTEX) {
|
||||
out << SHADERS_POST_PROCESS_VS_DATA;
|
||||
} else if (type == ShaderType::FRAGMENT) {
|
||||
@@ -161,7 +161,7 @@ io::sstream& CodeGenerator::generatePostProcessMain(io::sstream& out, ShaderType
|
||||
}
|
||||
|
||||
io::sstream& CodeGenerator::generateVariable(io::sstream& out, ShaderType type,
|
||||
const CString& name, size_t index) const {
|
||||
const CString& name, size_t index) {
|
||||
|
||||
if (!name.empty()) {
|
||||
if (type == ShaderType::VERTEX) {
|
||||
@@ -176,7 +176,7 @@ io::sstream& CodeGenerator::generateVariable(io::sstream& out, ShaderType type,
|
||||
}
|
||||
|
||||
io::sstream& CodeGenerator::generateShaderInputs(io::sstream& out, ShaderType type,
|
||||
const AttributeBitset& attributes, Interpolation interpolation) const {
|
||||
const AttributeBitset& attributes, Interpolation interpolation) {
|
||||
|
||||
const char* shading = getInterpolationQualifier(interpolation);
|
||||
out << "#define SHADING_INTERPOLATION " << shading << "\n";
|
||||
@@ -242,8 +242,8 @@ io::sstream& CodeGenerator::generateShaderInputs(io::sstream& out, ShaderType ty
|
||||
return out;
|
||||
}
|
||||
|
||||
utils::io::sstream& CodeGenerator::generateOutput(utils::io::sstream& out, ShaderType type,
|
||||
const utils::CString& name, size_t index,
|
||||
io::sstream& CodeGenerator::generateOutput(io::sstream& out, ShaderType type,
|
||||
const CString& name, size_t index,
|
||||
MaterialBuilder::VariableQualifier qualifier,
|
||||
MaterialBuilder::OutputType outputType) const {
|
||||
if (name.empty() || type == ShaderType::VERTEX) {
|
||||
@@ -285,17 +285,16 @@ utils::io::sstream& CodeGenerator::generateOutput(utils::io::sstream& out, Shade
|
||||
}
|
||||
|
||||
|
||||
io::sstream& CodeGenerator::generateDepthShaderMain(io::sstream& out, ShaderType type) const {
|
||||
if (type == ShaderType::VERTEX) {
|
||||
out << SHADERS_DEPTH_MAIN_VS_DATA;
|
||||
} else if (type == ShaderType::FRAGMENT) {
|
||||
io::sstream& CodeGenerator::generateDepthShaderMain(io::sstream& out, ShaderType type) {
|
||||
assert(type != ShaderType::VERTEX);
|
||||
if (type == ShaderType::FRAGMENT) {
|
||||
out << SHADERS_DEPTH_MAIN_FS_DATA;
|
||||
}
|
||||
return out;
|
||||
}
|
||||
|
||||
const char* CodeGenerator::getUniformPrecisionQualifier(UniformType type, Precision precision,
|
||||
Precision uniformPrecision, Precision defaultPrecision) const noexcept {
|
||||
Precision uniformPrecision, Precision defaultPrecision) noexcept {
|
||||
if (!hasPrecision(type)) {
|
||||
return "";
|
||||
}
|
||||
@@ -326,7 +325,7 @@ io::sstream& CodeGenerator::generateUniforms(io::sstream& out, ShaderType shader
|
||||
}
|
||||
out << "std140) uniform " << blockName.c_str() << " {\n";
|
||||
for (auto const& info : infos) {
|
||||
char const* const type = getUniformTypeName(info.type);
|
||||
char const* const type = getUniformTypeName(info);
|
||||
char const* const precision = getUniformPrecisionQualifier(info.type, info.precision,
|
||||
uniformPrecision, defaultPrecision);
|
||||
out << " " << precision;
|
||||
@@ -385,8 +384,8 @@ io::sstream& CodeGenerator::generateSamplers(
|
||||
return out;
|
||||
}
|
||||
|
||||
utils::io::sstream& CodeGenerator::generateSubpass(utils::io::sstream& out,
|
||||
SubpassInfo subpass) const {
|
||||
io::sstream& CodeGenerator::generateSubpass(io::sstream& out,
|
||||
SubpassInfo subpass) {
|
||||
if (!subpass.isValid) {
|
||||
return out;
|
||||
}
|
||||
@@ -454,31 +453,31 @@ void CodeGenerator::fixupExternalSamplers(
|
||||
}
|
||||
|
||||
|
||||
io::sstream& CodeGenerator::generateDefine(io::sstream& out, const char* name, bool value) const {
|
||||
io::sstream& CodeGenerator::generateDefine(io::sstream& out, const char* name, bool value) {
|
||||
if (value) {
|
||||
out << "#define " << name << "\n";
|
||||
}
|
||||
return out;
|
||||
}
|
||||
|
||||
io::sstream& CodeGenerator::generateDefine(io::sstream& out, const char* name, uint32_t value) const {
|
||||
io::sstream& CodeGenerator::generateDefine(io::sstream& out, const char* name, uint32_t value) {
|
||||
out << "#define " << name << " " << value << "\n";
|
||||
return out;
|
||||
}
|
||||
|
||||
io::sstream& CodeGenerator::generateDefine(io::sstream& out, const char* name, const char* string) const {
|
||||
io::sstream& CodeGenerator::generateDefine(io::sstream& out, const char* name, const char* string) {
|
||||
out << "#define " << name << " " << string << "\n";
|
||||
return out;
|
||||
}
|
||||
|
||||
io::sstream& CodeGenerator::generateIndexedDefine(io::sstream& out, const char* name,
|
||||
uint32_t index, uint32_t value) const {
|
||||
uint32_t index, uint32_t value) {
|
||||
out << "#define " << name << index << " " << value << "\n";
|
||||
return out;
|
||||
}
|
||||
|
||||
io::sstream& CodeGenerator::generateMaterialProperty(io::sstream& out,
|
||||
MaterialBuilder::Property property, bool isSet) const {
|
||||
MaterialBuilder::Property property, bool isSet) {
|
||||
if (isSet) {
|
||||
out << "#define " << "MATERIAL_HAS_" << getConstantName(property) << "\n";
|
||||
}
|
||||
@@ -515,7 +514,7 @@ io::sstream& CodeGenerator::generateQualityDefine(io::sstream& out, ShaderQualit
|
||||
return out;
|
||||
}
|
||||
|
||||
io::sstream& CodeGenerator::generateCommon(io::sstream& out, ShaderType type) const {
|
||||
io::sstream& CodeGenerator::generateCommon(io::sstream& out, ShaderType type) {
|
||||
out << SHADERS_COMMON_MATH_FS_DATA;
|
||||
out << SHADERS_COMMON_SHADOWING_FS_DATA;
|
||||
if (type == ShaderType::VERTEX) {
|
||||
@@ -527,7 +526,7 @@ io::sstream& CodeGenerator::generateCommon(io::sstream& out, ShaderType type) co
|
||||
return out;
|
||||
}
|
||||
|
||||
io::sstream& CodeGenerator::generateFog(io::sstream& out, ShaderType type) const {
|
||||
io::sstream& CodeGenerator::generateFog(io::sstream& out, ShaderType type) {
|
||||
if (type == ShaderType::VERTEX) {
|
||||
} else if (type == ShaderType::FRAGMENT) {
|
||||
out << SHADERS_FOG_FS_DATA;
|
||||
@@ -535,7 +534,7 @@ io::sstream& CodeGenerator::generateFog(io::sstream& out, ShaderType type) const
|
||||
return out;
|
||||
}
|
||||
|
||||
io::sstream& CodeGenerator::generateCommonMaterial(io::sstream& out, ShaderType type) const {
|
||||
io::sstream& CodeGenerator::generateCommonMaterial(io::sstream& out, ShaderType type) {
|
||||
if (type == ShaderType::VERTEX) {
|
||||
out << SHADERS_MATERIAL_INPUTS_VS_DATA;
|
||||
} else if (type == ShaderType::FRAGMENT) {
|
||||
@@ -544,7 +543,7 @@ io::sstream& CodeGenerator::generateCommonMaterial(io::sstream& out, ShaderType
|
||||
return out;
|
||||
}
|
||||
|
||||
io::sstream& CodeGenerator::generatePostProcessInputs(io::sstream& out, ShaderType type) const {
|
||||
io::sstream& CodeGenerator::generatePostProcessInputs(io::sstream& out, ShaderType type) {
|
||||
if (type == ShaderType::VERTEX) {
|
||||
out << SHADERS_POST_PROCESS_INPUTS_VS_DATA;
|
||||
} else if (type == ShaderType::FRAGMENT) {
|
||||
@@ -553,8 +552,8 @@ io::sstream& CodeGenerator::generatePostProcessInputs(io::sstream& out, ShaderTy
|
||||
return out;
|
||||
}
|
||||
|
||||
utils::io::sstream& CodeGenerator::generatePostProcessGetters(utils::io::sstream& out,
|
||||
ShaderType type) const {
|
||||
io::sstream& CodeGenerator::generatePostProcessGetters(io::sstream& out,
|
||||
ShaderType type) {
|
||||
out << SHADERS_COMMON_GETTERS_FS_DATA;
|
||||
if (type == ShaderType::VERTEX) {
|
||||
out << SHADERS_POST_PROCESS_GETTERS_VS_DATA;
|
||||
@@ -562,7 +561,7 @@ utils::io::sstream& CodeGenerator::generatePostProcessGetters(utils::io::sstream
|
||||
return out;
|
||||
}
|
||||
|
||||
io::sstream& CodeGenerator::generateGetters(io::sstream& out, ShaderType type) const {
|
||||
io::sstream& CodeGenerator::generateGetters(io::sstream& out, ShaderType type) {
|
||||
out << SHADERS_COMMON_GETTERS_FS_DATA;
|
||||
if (type == ShaderType::VERTEX) {
|
||||
out << SHADERS_GETTERS_VS_DATA;
|
||||
@@ -572,7 +571,7 @@ io::sstream& CodeGenerator::generateGetters(io::sstream& out, ShaderType type) c
|
||||
return out;
|
||||
}
|
||||
|
||||
io::sstream& CodeGenerator::generateParameters(io::sstream& out, ShaderType type) const {
|
||||
io::sstream& CodeGenerator::generateParameters(io::sstream& out, ShaderType type) {
|
||||
if (type == ShaderType::VERTEX) {
|
||||
} else if (type == ShaderType::FRAGMENT) {
|
||||
out << SHADERS_SHADING_PARAMETERS_FS_DATA;
|
||||
@@ -581,7 +580,7 @@ io::sstream& CodeGenerator::generateParameters(io::sstream& out, ShaderType type
|
||||
}
|
||||
|
||||
io::sstream& CodeGenerator::generateShaderLit(io::sstream& out, ShaderType type,
|
||||
filament::Variant variant, filament::Shading shading, bool customSurfaceShading) const {
|
||||
filament::Variant variant, Shading shading, bool customSurfaceShading) {
|
||||
if (type == ShaderType::VERTEX) {
|
||||
} else if (type == ShaderType::FRAGMENT) {
|
||||
out << SHADERS_COMMON_LIGHTING_FS_DATA;
|
||||
@@ -627,7 +626,7 @@ io::sstream& CodeGenerator::generateShaderLit(io::sstream& out, ShaderType type,
|
||||
}
|
||||
|
||||
io::sstream& CodeGenerator::generateShaderUnlit(io::sstream& out, ShaderType type,
|
||||
filament::Variant variant, bool hasShadowMultiplier) const {
|
||||
filament::Variant variant, bool hasShadowMultiplier) {
|
||||
if (type == ShaderType::VERTEX) {
|
||||
} else if (type == ShaderType::FRAGMENT) {
|
||||
if (hasShadowMultiplier) {
|
||||
@@ -673,9 +672,9 @@ char const* CodeGenerator::getConstantName(MaterialBuilder::Property property) n
|
||||
}
|
||||
}
|
||||
|
||||
char const* CodeGenerator::getUniformTypeName(UniformInterfaceBlock::Type type) noexcept {
|
||||
char const* CodeGenerator::getUniformTypeName(UniformInterfaceBlock::UniformInfo const& info) noexcept {
|
||||
using Type = UniformInterfaceBlock::Type;
|
||||
switch (type) {
|
||||
switch (info.type) {
|
||||
case Type::BOOL: return "bool";
|
||||
case Type::BOOL2: return "bvec2";
|
||||
case Type::BOOL3: return "bvec3";
|
||||
@@ -694,6 +693,7 @@ char const* CodeGenerator::getUniformTypeName(UniformInterfaceBlock::Type type)
|
||||
case Type::UINT4: return "uvec4";
|
||||
case Type::MAT3: return "mat3";
|
||||
case Type::MAT4: return "mat4";
|
||||
case Type::STRUCT: return info.structName.c_str();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -777,6 +777,7 @@ bool CodeGenerator::hasPrecision(UniformInterfaceBlock::Type type) noexcept {
|
||||
case UniformType::BOOL2:
|
||||
case UniformType::BOOL3:
|
||||
case UniformType::BOOL4:
|
||||
case UniformType::STRUCT:
|
||||
return false;
|
||||
default:
|
||||
return true;
|
||||
|
||||
@@ -56,39 +56,39 @@ public:
|
||||
filament::backend::ShaderModel getShaderModel() const noexcept { return mShaderModel; }
|
||||
|
||||
// insert a separator (can be a new line)
|
||||
utils::io::sstream& generateSeparator(utils::io::sstream& out) const;
|
||||
static utils::io::sstream& generateSeparator(utils::io::sstream& out) ;
|
||||
|
||||
// generate prolog for the given shader
|
||||
utils::io::sstream& generateProlog(utils::io::sstream& out, ShaderType type, bool hasExternalSamplers) const;
|
||||
|
||||
utils::io::sstream& generateEpilog(utils::io::sstream& out) const;
|
||||
static utils::io::sstream& generateEpilog(utils::io::sstream& out) ;
|
||||
|
||||
// generate common functions for the given shader
|
||||
utils::io::sstream& generateCommon(utils::io::sstream& out, ShaderType type) const;
|
||||
utils::io::sstream& generateCommonMaterial(utils::io::sstream& out, ShaderType type) const;
|
||||
static utils::io::sstream& generateCommon(utils::io::sstream& out, ShaderType type) ;
|
||||
static utils::io::sstream& generateCommonMaterial(utils::io::sstream& out, ShaderType type) ;
|
||||
|
||||
utils::io::sstream& generateFog(utils::io::sstream& out, ShaderType type) const;
|
||||
static utils::io::sstream& generateFog(utils::io::sstream& out, ShaderType type) ;
|
||||
|
||||
// generate the shader's main()
|
||||
utils::io::sstream& generateShaderMain(utils::io::sstream& out, ShaderType type) const;
|
||||
utils::io::sstream& generatePostProcessMain(utils::io::sstream& out, ShaderType type) const;
|
||||
static utils::io::sstream& generateShaderMain(utils::io::sstream& out, ShaderType type) ;
|
||||
static utils::io::sstream& generatePostProcessMain(utils::io::sstream& out, ShaderType type) ;
|
||||
|
||||
// generate the shader's code for the lit shading model
|
||||
utils::io::sstream& generateShaderLit(utils::io::sstream& out, ShaderType type,
|
||||
filament::Variant variant, filament::Shading shading, bool customSurfaceShading) const;
|
||||
static utils::io::sstream& generateShaderLit(utils::io::sstream& out, ShaderType type,
|
||||
filament::Variant variant, filament::Shading shading, bool customSurfaceShading) ;
|
||||
|
||||
// generate the shader's code for the unlit shading model
|
||||
utils::io::sstream& generateShaderUnlit(utils::io::sstream& out, ShaderType type,
|
||||
filament::Variant variant, bool hasShadowMultiplier) const;
|
||||
static utils::io::sstream& generateShaderUnlit(utils::io::sstream& out, ShaderType type,
|
||||
filament::Variant variant, bool hasShadowMultiplier) ;
|
||||
|
||||
// generate declarations for custom interpolants
|
||||
utils::io::sstream& generateVariable(utils::io::sstream& out, ShaderType type,
|
||||
const utils::CString& name, size_t index) const;
|
||||
static utils::io::sstream& generateVariable(utils::io::sstream& out, ShaderType type,
|
||||
const utils::CString& name, size_t index) ;
|
||||
|
||||
// generate declarations for non-custom "in" variables
|
||||
utils::io::sstream& generateShaderInputs(utils::io::sstream& out, ShaderType type,
|
||||
const filament::AttributeBitset& attributes, filament::Interpolation interpolation) const;
|
||||
utils::io::sstream& generatePostProcessInputs(utils::io::sstream& out, ShaderType type) const;
|
||||
static utils::io::sstream& generateShaderInputs(utils::io::sstream& out, ShaderType type,
|
||||
const filament::AttributeBitset& attributes, filament::Interpolation interpolation) ;
|
||||
static utils::io::sstream& generatePostProcessInputs(utils::io::sstream& out, ShaderType type) ;
|
||||
|
||||
// generate declarations for custom output variables
|
||||
utils::io::sstream& generateOutput(utils::io::sstream& out, ShaderType type,
|
||||
@@ -97,7 +97,7 @@ public:
|
||||
MaterialBuilder::OutputType outputType) const;
|
||||
|
||||
// generate no-op shader for depth prepass
|
||||
utils::io::sstream& generateDepthShaderMain(utils::io::sstream& out, ShaderType type) const;
|
||||
static utils::io::sstream& generateDepthShaderMain(utils::io::sstream& out, ShaderType type) ;
|
||||
|
||||
// generate uniforms
|
||||
utils::io::sstream& generateUniforms(utils::io::sstream& out, ShaderType type, uint8_t binding,
|
||||
@@ -108,24 +108,24 @@ public:
|
||||
utils::io::sstream& out, uint8_t firstBinding, const filament::SamplerInterfaceBlock& sib) const;
|
||||
|
||||
// generate subpass
|
||||
utils::io::sstream& generateSubpass(utils::io::sstream& out,
|
||||
filament::SubpassInfo subpass) const;
|
||||
static utils::io::sstream& generateSubpass(utils::io::sstream& out,
|
||||
filament::SubpassInfo subpass) ;
|
||||
|
||||
// generate material properties getters
|
||||
utils::io::sstream& generateMaterialProperty(utils::io::sstream& out,
|
||||
MaterialBuilder::Property property, bool isSet) const;
|
||||
static utils::io::sstream& generateMaterialProperty(utils::io::sstream& out,
|
||||
MaterialBuilder::Property property, bool isSet) ;
|
||||
|
||||
utils::io::sstream& generateQualityDefine(utils::io::sstream& out, ShaderQuality quality) const;
|
||||
|
||||
utils::io::sstream& generateDefine(utils::io::sstream& out, const char* name, bool value) const;
|
||||
utils::io::sstream& generateDefine(utils::io::sstream& out, const char* name, uint32_t value) const;
|
||||
utils::io::sstream& generateDefine(utils::io::sstream& out, const char* name, const char* string) const;
|
||||
utils::io::sstream& generateIndexedDefine(utils::io::sstream& out, const char* name,
|
||||
uint32_t index, uint32_t value) const;
|
||||
static utils::io::sstream& generateDefine(utils::io::sstream& out, const char* name, bool value) ;
|
||||
static utils::io::sstream& generateDefine(utils::io::sstream& out, const char* name, uint32_t value) ;
|
||||
static utils::io::sstream& generateDefine(utils::io::sstream& out, const char* name, const char* string) ;
|
||||
static utils::io::sstream& generateIndexedDefine(utils::io::sstream& out, const char* name,
|
||||
uint32_t index, uint32_t value) ;
|
||||
|
||||
utils::io::sstream& generatePostProcessGetters(utils::io::sstream& out, ShaderType type) const;
|
||||
utils::io::sstream& generateGetters(utils::io::sstream& out, ShaderType type) const;
|
||||
utils::io::sstream& generateParameters(utils::io::sstream& out, ShaderType type) const;
|
||||
static utils::io::sstream& generatePostProcessGetters(utils::io::sstream& out, ShaderType type) ;
|
||||
static utils::io::sstream& generateGetters(utils::io::sstream& out, ShaderType type) ;
|
||||
static utils::io::sstream& generateParameters(utils::io::sstream& out, ShaderType type) ;
|
||||
|
||||
static void fixupExternalSamplers(
|
||||
std::string& shader, filament::SamplerInterfaceBlock const& sib) noexcept;
|
||||
@@ -134,10 +134,10 @@ private:
|
||||
filament::backend::Precision getDefaultPrecision(ShaderType type) const;
|
||||
filament::backend::Precision getDefaultUniformPrecision() const;
|
||||
|
||||
const char* getUniformPrecisionQualifier(filament::backend::UniformType type,
|
||||
static const char* getUniformPrecisionQualifier(filament::backend::UniformType type,
|
||||
filament::backend::Precision precision,
|
||||
filament::backend::Precision uniformPrecision,
|
||||
filament::backend::Precision defaultPrecision) const noexcept;
|
||||
filament::backend::Precision defaultPrecision) noexcept;
|
||||
|
||||
// return type name of sampler (e.g.: "sampler2D")
|
||||
char const* getSamplerTypeName(filament::backend::SamplerType type,
|
||||
@@ -154,7 +154,7 @@ private:
|
||||
TargetLanguage mTargetLanguage;
|
||||
|
||||
// return type name of uniform (e.g.: "vec3", "vec4", "float")
|
||||
static char const* getUniformTypeName(filament::UniformInterfaceBlock::Type uniformType) noexcept;
|
||||
static char const* getUniformTypeName(filament::UniformInterfaceBlock::UniformInfo const& info) noexcept;
|
||||
|
||||
// return type name of output (e.g.: "vec3", "vec4", "float")
|
||||
static char const* getOutputTypeName(MaterialBuilder::OutputType type) noexcept;
|
||||
|
||||
@@ -28,26 +28,26 @@
|
||||
#include "CodeGenerator.h"
|
||||
#include "../UibGenerator.h"
|
||||
|
||||
using namespace filament;
|
||||
using namespace filament::backend;
|
||||
|
||||
namespace filamat {
|
||||
|
||||
static const char* getShadingDefine(filament::Shading shading) noexcept {
|
||||
using namespace filament;
|
||||
using namespace filament::backend;
|
||||
using namespace utils;
|
||||
|
||||
static const char* getShadingDefine(Shading shading) noexcept {
|
||||
switch (shading) {
|
||||
case filament::Shading::LIT: return "SHADING_MODEL_LIT";
|
||||
case filament::Shading::UNLIT: return "SHADING_MODEL_UNLIT";
|
||||
case filament::Shading::SUBSURFACE: return "SHADING_MODEL_SUBSURFACE";
|
||||
case filament::Shading::CLOTH: return "SHADING_MODEL_CLOTH";
|
||||
case filament::Shading::SPECULAR_GLOSSINESS: return "SHADING_MODEL_SPECULAR_GLOSSINESS";
|
||||
case Shading::LIT: return "SHADING_MODEL_LIT";
|
||||
case Shading::UNLIT: return "SHADING_MODEL_UNLIT";
|
||||
case Shading::SUBSURFACE: return "SHADING_MODEL_SUBSURFACE";
|
||||
case Shading::CLOTH: return "SHADING_MODEL_CLOTH";
|
||||
case Shading::SPECULAR_GLOSSINESS: return "SHADING_MODEL_SPECULAR_GLOSSINESS";
|
||||
}
|
||||
}
|
||||
|
||||
static void generateMaterialDefines(utils::io::sstream& os, const CodeGenerator& cg,
|
||||
MaterialBuilder::PropertyList const properties,
|
||||
static void generateMaterialDefines(io::sstream& os, MaterialBuilder::PropertyList const properties,
|
||||
const MaterialBuilder::PreprocessorDefineList& defines) noexcept {
|
||||
for (size_t i = 0; i < MaterialBuilder::MATERIAL_PROPERTIES_COUNT; i++) {
|
||||
cg.generateMaterialProperty(os, static_cast<MaterialBuilder::Property>(i), properties[i]);
|
||||
CodeGenerator::generateMaterialProperty(os, static_cast<MaterialBuilder::Property>(i), properties[i]);
|
||||
}
|
||||
// synthetic defines
|
||||
bool hasTBN =
|
||||
@@ -55,40 +55,39 @@ static void generateMaterialDefines(utils::io::sstream& os, const CodeGenerator&
|
||||
properties[static_cast<int>(MaterialBuilder::Property::NORMAL)] ||
|
||||
properties[static_cast<int>(MaterialBuilder::Property::BENT_NORMAL)] ||
|
||||
properties[static_cast<int>(MaterialBuilder::Property::CLEAR_COAT_NORMAL)];
|
||||
cg.generateDefine(os, "MATERIAL_NEEDS_TBN", hasTBN);
|
||||
CodeGenerator::generateDefine(os, "MATERIAL_NEEDS_TBN", hasTBN);
|
||||
|
||||
// Additional, user-provided defines.
|
||||
for (const auto& define : defines) {
|
||||
cg.generateDefine(os, define.name.c_str(), define.value.c_str());
|
||||
CodeGenerator::generateDefine(os, define.name.c_str(), define.value.c_str());
|
||||
}
|
||||
}
|
||||
|
||||
static void generateVertexDomain(const CodeGenerator& cg, utils::io::sstream& vs,
|
||||
filament::VertexDomain domain) noexcept {
|
||||
static void generateVertexDomain(io::sstream& vs, VertexDomain domain) noexcept {
|
||||
switch (domain) {
|
||||
case VertexDomain::OBJECT:
|
||||
cg.generateDefine(vs, "VERTEX_DOMAIN_OBJECT", true);
|
||||
CodeGenerator::generateDefine(vs, "VERTEX_DOMAIN_OBJECT", true);
|
||||
break;
|
||||
case VertexDomain::WORLD:
|
||||
cg.generateDefine(vs, "VERTEX_DOMAIN_WORLD", true);
|
||||
CodeGenerator::generateDefine(vs, "VERTEX_DOMAIN_WORLD", true);
|
||||
break;
|
||||
case VertexDomain::VIEW:
|
||||
cg.generateDefine(vs, "VERTEX_DOMAIN_VIEW", true);
|
||||
CodeGenerator::generateDefine(vs, "VERTEX_DOMAIN_VIEW", true);
|
||||
break;
|
||||
case VertexDomain::DEVICE:
|
||||
cg.generateDefine(vs, "VERTEX_DOMAIN_DEVICE", true);
|
||||
CodeGenerator::generateDefine(vs, "VERTEX_DOMAIN_DEVICE", true);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
static void generatePostProcessMaterialVariantDefines(const CodeGenerator& cg,
|
||||
utils::io::sstream& shader, PostProcessVariant variant) noexcept {
|
||||
static void generatePostProcessMaterialVariantDefines(io::sstream& shader,
|
||||
PostProcessVariant variant) noexcept {
|
||||
switch (variant) {
|
||||
case PostProcessVariant::OPAQUE:
|
||||
cg.generateDefine(shader, "POST_PROCESS_OPAQUE", 1u);
|
||||
CodeGenerator::generateDefine(shader, "POST_PROCESS_OPAQUE", 1u);
|
||||
break;
|
||||
case PostProcessVariant::TRANSLUCENT:
|
||||
cg.generateDefine(shader, "POST_PROCESS_OPAQUE", 0u);
|
||||
CodeGenerator::generateDefine(shader, "POST_PROCESS_OPAQUE", 0u);
|
||||
break;
|
||||
}
|
||||
}
|
||||
@@ -102,7 +101,7 @@ static size_t countLines(const char* s) noexcept {
|
||||
return lines;
|
||||
}
|
||||
|
||||
static size_t countLines(const utils::CString& s) noexcept {
|
||||
static size_t countLines(const CString& s) noexcept {
|
||||
size_t lines = 0;
|
||||
for (char i : s) {
|
||||
if (i == '\n') lines++;
|
||||
@@ -110,8 +109,8 @@ static size_t countLines(const utils::CString& s) noexcept {
|
||||
return lines;
|
||||
}
|
||||
|
||||
static void appendShader(utils::io::sstream& ss,
|
||||
const utils::CString& shader, size_t lineOffset) noexcept {
|
||||
static void appendShader(io::sstream& ss,
|
||||
const CString& shader, size_t lineOffset) noexcept {
|
||||
if (!shader.empty()) {
|
||||
size_t lines = countLines(ss.c_str());
|
||||
ss << "#line " << lineOffset + 1 << '\n';
|
||||
@@ -130,50 +129,52 @@ ShaderGenerator::ShaderGenerator(
|
||||
MaterialBuilder::VariableList const& variables,
|
||||
MaterialBuilder::OutputList const& outputs,
|
||||
MaterialBuilder::PreprocessorDefineList const& defines,
|
||||
utils::CString const& materialCode, size_t lineOffset,
|
||||
utils::CString const& materialVertexCode, size_t vertexLineOffset,
|
||||
CString const& materialCode, size_t lineOffset,
|
||||
CString const& materialVertexCode, size_t vertexLineOffset,
|
||||
MaterialBuilder::MaterialDomain materialDomain) noexcept {
|
||||
|
||||
std::copy(std::begin(properties), std::end(properties), std::begin(mProperties));
|
||||
std::copy(std::begin(variables), std::end(variables), std::begin(mVariables));
|
||||
std::copy(std::begin(outputs), std::end(outputs), std::back_inserter(mOutputs));
|
||||
|
||||
mMaterialCode = materialCode;
|
||||
mMaterialFragmentCode = materialCode;
|
||||
mMaterialVertexCode = materialVertexCode;
|
||||
mIsMaterialVertexShaderEmpty = materialVertexCode.empty();
|
||||
mMaterialLineOffset = lineOffset;
|
||||
mMaterialVertexLineOffset = vertexLineOffset;
|
||||
mMaterialDomain = materialDomain;
|
||||
mDefines = defines;
|
||||
|
||||
if (mMaterialCode.empty()) {
|
||||
if (mMaterialFragmentCode.empty()) {
|
||||
if (mMaterialDomain == MaterialBuilder::MaterialDomain::SURFACE) {
|
||||
mMaterialCode =
|
||||
utils::CString("void material(inout MaterialInputs m) {\n prepareMaterial(m);\n}\n");
|
||||
mMaterialFragmentCode =
|
||||
CString("void material(inout MaterialInputs m) {\n prepareMaterial(m);\n}\n");
|
||||
} else if (mMaterialDomain == MaterialBuilder::MaterialDomain::POST_PROCESS) {
|
||||
mMaterialCode =
|
||||
utils::CString("void postProcess(inout PostProcessInputs p) {\n}\n");
|
||||
mMaterialFragmentCode =
|
||||
CString("void postProcess(inout PostProcessInputs p) {\n}\n");
|
||||
}
|
||||
}
|
||||
if (mMaterialVertexCode.empty()) {
|
||||
if (mMaterialDomain == MaterialBuilder::MaterialDomain::SURFACE) {
|
||||
mMaterialVertexCode =
|
||||
utils::CString("void materialVertex(inout MaterialVertexInputs m) {\n}\n");
|
||||
CString("void materialVertex(inout MaterialVertexInputs m) {\n}\n");
|
||||
} else if (mMaterialDomain == MaterialBuilder::MaterialDomain::POST_PROCESS) {
|
||||
mMaterialVertexCode =
|
||||
utils::CString("void postProcessVertex(inout PostProcessVertexInputs m) {\n}\n");
|
||||
CString("void postProcessVertex(inout PostProcessVertexInputs m) {\n}\n");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
std::string ShaderGenerator::createVertexProgram(filament::backend::ShaderModel shaderModel,
|
||||
std::string ShaderGenerator::createVertexProgram(ShaderModel shaderModel,
|
||||
MaterialBuilder::TargetApi targetApi, MaterialBuilder::TargetLanguage targetLanguage,
|
||||
MaterialInfo const& material, uint8_t variantKey, filament::Interpolation interpolation,
|
||||
filament::VertexDomain vertexDomain) const noexcept {
|
||||
MaterialInfo const& material, uint8_t variantKey, Interpolation interpolation,
|
||||
VertexDomain vertexDomain) const noexcept {
|
||||
if (mMaterialDomain == MaterialBuilder::MaterialDomain::POST_PROCESS) {
|
||||
return createPostProcessVertexProgram(shaderModel, targetApi,
|
||||
targetLanguage, material, variantKey, material.samplerBindings);
|
||||
}
|
||||
|
||||
utils::io::sstream vs;
|
||||
io::sstream vs;
|
||||
|
||||
const CodeGenerator cg(shaderModel, targetApi, targetLanguage);
|
||||
const bool lit = material.isLit;
|
||||
@@ -183,19 +184,35 @@ std::string ShaderGenerator::createVertexProgram(filament::backend::ShaderModel
|
||||
|
||||
cg.generateQualityDefine(vs, material.quality);
|
||||
|
||||
cg.generateDefine(vs, "MAX_SHADOW_CASTING_SPOTS", uint32_t(CONFIG_MAX_SHADOW_CASTING_SPOTS));
|
||||
CodeGenerator::generateDefine(vs, "MAX_SHADOW_CASTING_SPOTS", uint32_t(CONFIG_MAX_SHADOW_CASTING_SPOTS));
|
||||
|
||||
cg.generateDefine(vs, "FLIP_UV_ATTRIBUTE", material.flipUV);
|
||||
CodeGenerator::generateDefine(vs, "FLIP_UV_ATTRIBUTE", material.flipUV);
|
||||
|
||||
bool litVariants = lit || material.hasShadowMultiplier;
|
||||
cg.generateDefine(vs, "HAS_DIRECTIONAL_LIGHTING", litVariants && variant.hasDirectionalLighting());
|
||||
cg.generateDefine(vs, "HAS_DYNAMIC_LIGHTING", litVariants && variant.hasDynamicLighting());
|
||||
cg.generateDefine(vs, "HAS_SHADOWING", litVariants && variant.hasShadowReceiver());
|
||||
cg.generateDefine(vs, "HAS_SHADOW_MULTIPLIER", material.hasShadowMultiplier);
|
||||
cg.generateDefine(vs, "HAS_SKINNING_OR_MORPHING", variant.hasSkinningOrMorphing());
|
||||
cg.generateDefine(vs, "HAS_VSM", variant.hasVsm());
|
||||
cg.generateDefine(vs, getShadingDefine(material.shading), true);
|
||||
generateMaterialDefines(vs, cg, mProperties, mDefines);
|
||||
const bool litVariants = lit || material.hasShadowMultiplier;
|
||||
|
||||
// note: even if the user vertex shader is empty, we can't use the "optimized" version if
|
||||
// we're in masked mode because fragment shader needs the color varyings
|
||||
const bool useOptimizedDepthVertexShader =
|
||||
// must be a depth variant
|
||||
filament::Variant::isValidDepthVariant(variantKey) &&
|
||||
// must have an empty vertex shader
|
||||
mIsMaterialVertexShaderEmpty &&
|
||||
// but must not be MASKED mode
|
||||
material.blendingMode != BlendingMode::MASKED &&
|
||||
// and must not have transparent shadows
|
||||
!(material.hasTransparentShadow &&
|
||||
(material.blendingMode == BlendingMode::TRANSPARENT ||
|
||||
material.blendingMode == BlendingMode::FADE));
|
||||
|
||||
CodeGenerator::generateDefine(vs, "USE_OPTIMIZED_DEPTH_VERTEX_SHADER", useOptimizedDepthVertexShader);
|
||||
CodeGenerator::generateDefine(vs, "HAS_DIRECTIONAL_LIGHTING", litVariants && variant.hasDirectionalLighting());
|
||||
CodeGenerator::generateDefine(vs, "HAS_DYNAMIC_LIGHTING", litVariants && variant.hasDynamicLighting());
|
||||
CodeGenerator::generateDefine(vs, "HAS_SHADOWING", litVariants && variant.hasShadowReceiver());
|
||||
CodeGenerator::generateDefine(vs, "HAS_SHADOW_MULTIPLIER", material.hasShadowMultiplier);
|
||||
CodeGenerator::generateDefine(vs, "HAS_SKINNING_OR_MORPHING", variant.hasSkinningOrMorphing());
|
||||
CodeGenerator::generateDefine(vs, "HAS_VSM", variant.hasVsm());
|
||||
CodeGenerator::generateDefine(vs, getShadingDefine(material.shading), true);
|
||||
generateMaterialDefines(vs, mProperties, mDefines);
|
||||
|
||||
AttributeBitset attributes = material.requiredAttributes;
|
||||
if (variant.hasSkinningOrMorphing()) {
|
||||
@@ -210,16 +227,16 @@ std::string ShaderGenerator::createVertexProgram(filament::backend::ShaderModel
|
||||
attributes.set(VertexAttribute::MORPH_TANGENTS_2);
|
||||
attributes.set(VertexAttribute::MORPH_TANGENTS_3);
|
||||
}
|
||||
cg.generateShaderInputs(vs, ShaderType::VERTEX, attributes, interpolation);
|
||||
CodeGenerator::generateShaderInputs(vs, ShaderType::VERTEX, attributes, interpolation);
|
||||
|
||||
// custom material variables
|
||||
size_t variableIndex = 0;
|
||||
for (const auto& variable : mVariables) {
|
||||
cg.generateVariable(vs, ShaderType::VERTEX, variable, variableIndex++);
|
||||
CodeGenerator::generateVariable(vs, ShaderType::VERTEX, variable, variableIndex++);
|
||||
}
|
||||
|
||||
// materials defines
|
||||
generateVertexDomain(cg, vs, vertexDomain);
|
||||
generateVertexDomain(vs, vertexDomain);
|
||||
|
||||
// uniforms
|
||||
cg.generateUniforms(vs, ShaderType::VERTEX,
|
||||
@@ -233,45 +250,27 @@ std::string ShaderGenerator::createVertexProgram(filament::backend::ShaderModel
|
||||
}
|
||||
cg.generateUniforms(vs, ShaderType::VERTEX,
|
||||
BindingPoints::PER_MATERIAL_INSTANCE, material.uib);
|
||||
cg.generateSeparator(vs);
|
||||
CodeGenerator::generateSeparator(vs);
|
||||
// TODO: should we generate per-view SIB in the vertex shader?
|
||||
cg.generateSamplers(vs,
|
||||
material.samplerBindings.getBlockOffset(BindingPoints::PER_MATERIAL_INSTANCE),
|
||||
material.sib);
|
||||
|
||||
// shader code
|
||||
cg.generateCommon(vs, ShaderType::VERTEX);
|
||||
cg.generateGetters(vs, ShaderType::VERTEX);
|
||||
cg.generateCommonMaterial(vs, ShaderType::VERTEX);
|
||||
CodeGenerator::generateCommon(vs, ShaderType::VERTEX);
|
||||
CodeGenerator::generateGetters(vs, ShaderType::VERTEX);
|
||||
CodeGenerator::generateCommonMaterial(vs, ShaderType::VERTEX);
|
||||
|
||||
if (filament::Variant::isValidDepthVariant(variantKey) &&
|
||||
material.blendingMode != BlendingMode::MASKED &&
|
||||
!material.hasTransparentShadow &&
|
||||
!hasCustomDepthShader()) {
|
||||
// these variants are special and are treated as DEPTH variants. Filament will never
|
||||
// request that variant for the color pass.
|
||||
cg.generateDepthShaderMain(vs, ShaderType::VERTEX);
|
||||
} else {
|
||||
// main entry point
|
||||
appendShader(vs, mMaterialVertexCode, mMaterialVertexLineOffset);
|
||||
cg.generateShaderMain(vs, ShaderType::VERTEX);
|
||||
}
|
||||
// main entry point
|
||||
appendShader(vs, mMaterialVertexCode, mMaterialVertexLineOffset);
|
||||
CodeGenerator::generateShaderMain(vs, ShaderType::VERTEX);
|
||||
|
||||
cg.generateEpilog(vs);
|
||||
CodeGenerator::generateEpilog(vs);
|
||||
|
||||
return vs.c_str();
|
||||
}
|
||||
|
||||
bool ShaderGenerator::hasCustomDepthShader() const noexcept {
|
||||
for (const auto& variable : mVariables) {
|
||||
if (!variable.empty()) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
static bool isMobileTarget(filament::backend::ShaderModel model) {
|
||||
static bool isMobileTarget(ShaderModel model) {
|
||||
switch (model) {
|
||||
case ShaderModel::UNKNOWN:
|
||||
return false;
|
||||
@@ -282,10 +281,10 @@ static bool isMobileTarget(filament::backend::ShaderModel model) {
|
||||
}
|
||||
}
|
||||
|
||||
std::string ShaderGenerator::createFragmentProgram(filament::backend::ShaderModel shaderModel,
|
||||
std::string ShaderGenerator::createFragmentProgram(ShaderModel shaderModel,
|
||||
MaterialBuilder::TargetApi targetApi, MaterialBuilder::TargetLanguage targetLanguage,
|
||||
MaterialInfo const& material, uint8_t variantKey,
|
||||
filament::Interpolation interpolation) const noexcept {
|
||||
Interpolation interpolation) const noexcept {
|
||||
if (mMaterialDomain == MaterialBuilder::MaterialDomain::POST_PROCESS) {
|
||||
return createPostProcessFragmentProgram(shaderModel, targetApi, targetLanguage, material,
|
||||
variantKey, material.samplerBindings);
|
||||
@@ -295,121 +294,121 @@ std::string ShaderGenerator::createFragmentProgram(filament::backend::ShaderMode
|
||||
const bool lit = material.isLit;
|
||||
const filament::Variant variant(variantKey);
|
||||
|
||||
utils::io::sstream fs;
|
||||
io::sstream fs;
|
||||
cg.generateProlog(fs, ShaderType::FRAGMENT, material.hasExternalSamplers);
|
||||
|
||||
cg.generateQualityDefine(fs, material.quality);
|
||||
|
||||
cg.generateDefine(fs, "GEOMETRIC_SPECULAR_AA", material.specularAntiAliasing && lit);
|
||||
CodeGenerator::generateDefine(fs, "GEOMETRIC_SPECULAR_AA", material.specularAntiAliasing && lit);
|
||||
|
||||
cg.generateDefine(fs, "CLEAR_COAT_IOR_CHANGE", material.clearCoatIorChange);
|
||||
CodeGenerator::generateDefine(fs, "CLEAR_COAT_IOR_CHANGE", material.clearCoatIorChange);
|
||||
|
||||
cg.generateDefine(fs, "MAX_SHADOW_CASTING_SPOTS", uint32_t(CONFIG_MAX_SHADOW_CASTING_SPOTS));
|
||||
CodeGenerator::generateDefine(fs, "MAX_SHADOW_CASTING_SPOTS", uint32_t(CONFIG_MAX_SHADOW_CASTING_SPOTS));
|
||||
|
||||
auto defaultSpecularAO = isMobileTarget(shaderModel) ?
|
||||
SpecularAmbientOcclusion::NONE : SpecularAmbientOcclusion::SIMPLE;
|
||||
auto specularAO = material.specularAOSet ? material.specularAO : defaultSpecularAO;
|
||||
cg.generateDefine(fs, "SPECULAR_AMBIENT_OCCLUSION", uint32_t(specularAO));
|
||||
CodeGenerator::generateDefine(fs, "SPECULAR_AMBIENT_OCCLUSION", uint32_t(specularAO));
|
||||
|
||||
cg.generateDefine(fs, "HAS_REFRACTION", material.refractionMode != RefractionMode::NONE);
|
||||
CodeGenerator::generateDefine(fs, "HAS_REFRACTION", material.refractionMode != RefractionMode::NONE);
|
||||
if (material.refractionMode != RefractionMode::NONE) {
|
||||
cg.generateDefine(fs, "REFRACTION_MODE_CUBEMAP", uint32_t(RefractionMode::CUBEMAP));
|
||||
cg.generateDefine(fs, "REFRACTION_MODE_SCREEN_SPACE", uint32_t(RefractionMode::SCREEN_SPACE));
|
||||
CodeGenerator::generateDefine(fs, "REFRACTION_MODE_CUBEMAP", uint32_t(RefractionMode::CUBEMAP));
|
||||
CodeGenerator::generateDefine(fs, "REFRACTION_MODE_SCREEN_SPACE", uint32_t(RefractionMode::SCREEN_SPACE));
|
||||
switch (material.refractionMode) {
|
||||
case RefractionMode::NONE:
|
||||
// can't be here
|
||||
break;
|
||||
case RefractionMode::CUBEMAP:
|
||||
cg.generateDefine(fs, "REFRACTION_MODE", "REFRACTION_MODE_CUBEMAP");
|
||||
CodeGenerator::generateDefine(fs, "REFRACTION_MODE", "REFRACTION_MODE_CUBEMAP");
|
||||
break;
|
||||
case RefractionMode::SCREEN_SPACE:
|
||||
cg.generateDefine(fs, "REFRACTION_MODE", "REFRACTION_MODE_SCREEN_SPACE");
|
||||
CodeGenerator::generateDefine(fs, "REFRACTION_MODE", "REFRACTION_MODE_SCREEN_SPACE");
|
||||
break;
|
||||
}
|
||||
cg.generateDefine(fs, "REFRACTION_TYPE_SOLID", uint32_t(RefractionType::SOLID));
|
||||
cg.generateDefine(fs, "REFRACTION_TYPE_THIN", uint32_t(RefractionType::THIN));
|
||||
CodeGenerator::generateDefine(fs, "REFRACTION_TYPE_SOLID", uint32_t(RefractionType::SOLID));
|
||||
CodeGenerator::generateDefine(fs, "REFRACTION_TYPE_THIN", uint32_t(RefractionType::THIN));
|
||||
switch (material.refractionType) {
|
||||
case RefractionType::SOLID:
|
||||
cg.generateDefine(fs, "REFRACTION_TYPE", "REFRACTION_TYPE_SOLID");
|
||||
CodeGenerator::generateDefine(fs, "REFRACTION_TYPE", "REFRACTION_TYPE_SOLID");
|
||||
break;
|
||||
case RefractionType::THIN:
|
||||
cg.generateDefine(fs, "REFRACTION_TYPE", "REFRACTION_TYPE_THIN");
|
||||
CodeGenerator::generateDefine(fs, "REFRACTION_TYPE", "REFRACTION_TYPE_THIN");
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
bool multiBounceAO = material.multiBounceAOSet ?
|
||||
material.multiBounceAO : !isMobileTarget(shaderModel);
|
||||
cg.generateDefine(fs, "MULTI_BOUNCE_AMBIENT_OCCLUSION", multiBounceAO ? 1u : 0u);
|
||||
CodeGenerator::generateDefine(fs, "MULTI_BOUNCE_AMBIENT_OCCLUSION", multiBounceAO ? 1u : 0u);
|
||||
|
||||
// lighting variants
|
||||
bool litVariants = lit || material.hasShadowMultiplier;
|
||||
cg.generateDefine(fs, "HAS_DIRECTIONAL_LIGHTING", litVariants && variant.hasDirectionalLighting());
|
||||
cg.generateDefine(fs, "HAS_DYNAMIC_LIGHTING", litVariants && variant.hasDynamicLighting());
|
||||
cg.generateDefine(fs, "HAS_SHADOWING", litVariants && variant.hasShadowReceiver());
|
||||
cg.generateDefine(fs, "HAS_FOG", variant.hasFog() && !variant.hasDepth());
|
||||
cg.generateDefine(fs, "HAS_PICKING", variant.hasPicking() && variant.hasDepth());
|
||||
cg.generateDefine(fs, "HAS_VSM", variant.hasVsm());
|
||||
cg.generateDefine(fs, "HAS_SHADOW_MULTIPLIER", material.hasShadowMultiplier);
|
||||
cg.generateDefine(fs, "HAS_TRANSPARENT_SHADOW", material.hasTransparentShadow);
|
||||
CodeGenerator::generateDefine(fs, "HAS_DIRECTIONAL_LIGHTING", litVariants && variant.hasDirectionalLighting());
|
||||
CodeGenerator::generateDefine(fs, "HAS_DYNAMIC_LIGHTING", litVariants && variant.hasDynamicLighting());
|
||||
CodeGenerator::generateDefine(fs, "HAS_SHADOWING", litVariants && variant.hasShadowReceiver());
|
||||
CodeGenerator::generateDefine(fs, "HAS_FOG", variant.hasFog() && !variant.hasDepth());
|
||||
CodeGenerator::generateDefine(fs, "HAS_PICKING", variant.hasPicking() && variant.hasDepth());
|
||||
CodeGenerator::generateDefine(fs, "HAS_VSM", variant.hasVsm());
|
||||
CodeGenerator::generateDefine(fs, "HAS_SHADOW_MULTIPLIER", material.hasShadowMultiplier);
|
||||
CodeGenerator::generateDefine(fs, "HAS_TRANSPARENT_SHADOW", material.hasTransparentShadow);
|
||||
|
||||
// material defines
|
||||
cg.generateDefine(fs, "MATERIAL_HAS_DOUBLE_SIDED_CAPABILITY", material.hasDoubleSidedCapability);
|
||||
CodeGenerator::generateDefine(fs, "MATERIAL_HAS_DOUBLE_SIDED_CAPABILITY", material.hasDoubleSidedCapability);
|
||||
switch (material.blendingMode) {
|
||||
case BlendingMode::OPAQUE:
|
||||
cg.generateDefine(fs, "BLEND_MODE_OPAQUE", true);
|
||||
CodeGenerator::generateDefine(fs, "BLEND_MODE_OPAQUE", true);
|
||||
break;
|
||||
case BlendingMode::TRANSPARENT:
|
||||
cg.generateDefine(fs, "BLEND_MODE_TRANSPARENT", true);
|
||||
CodeGenerator::generateDefine(fs, "BLEND_MODE_TRANSPARENT", true);
|
||||
break;
|
||||
case BlendingMode::ADD:
|
||||
cg.generateDefine(fs, "BLEND_MODE_ADD", true);
|
||||
CodeGenerator::generateDefine(fs, "BLEND_MODE_ADD", true);
|
||||
break;
|
||||
case BlendingMode::MASKED:
|
||||
cg.generateDefine(fs, "BLEND_MODE_MASKED", true);
|
||||
CodeGenerator::generateDefine(fs, "BLEND_MODE_MASKED", true);
|
||||
break;
|
||||
case BlendingMode::FADE:
|
||||
// Fade is a special case of transparent
|
||||
cg.generateDefine(fs, "BLEND_MODE_TRANSPARENT", true);
|
||||
cg.generateDefine(fs, "BLEND_MODE_FADE", true);
|
||||
CodeGenerator::generateDefine(fs, "BLEND_MODE_TRANSPARENT", true);
|
||||
CodeGenerator::generateDefine(fs, "BLEND_MODE_FADE", true);
|
||||
break;
|
||||
case BlendingMode::MULTIPLY:
|
||||
cg.generateDefine(fs, "BLEND_MODE_MULTIPLY", true);
|
||||
CodeGenerator::generateDefine(fs, "BLEND_MODE_MULTIPLY", true);
|
||||
break;
|
||||
case BlendingMode::SCREEN:
|
||||
cg.generateDefine(fs, "BLEND_MODE_SCREEN", true);
|
||||
CodeGenerator::generateDefine(fs, "BLEND_MODE_SCREEN", true);
|
||||
break;
|
||||
}
|
||||
switch (material.postLightingBlendingMode) {
|
||||
case BlendingMode::OPAQUE:
|
||||
cg.generateDefine(fs, "POST_LIGHTING_BLEND_MODE_OPAQUE", true);
|
||||
CodeGenerator::generateDefine(fs, "POST_LIGHTING_BLEND_MODE_OPAQUE", true);
|
||||
break;
|
||||
case BlendingMode::TRANSPARENT:
|
||||
cg.generateDefine(fs, "POST_LIGHTING_BLEND_MODE_TRANSPARENT", true);
|
||||
CodeGenerator::generateDefine(fs, "POST_LIGHTING_BLEND_MODE_TRANSPARENT", true);
|
||||
break;
|
||||
case BlendingMode::ADD:
|
||||
cg.generateDefine(fs, "POST_LIGHTING_BLEND_MODE_ADD", true);
|
||||
CodeGenerator::generateDefine(fs, "POST_LIGHTING_BLEND_MODE_ADD", true);
|
||||
break;
|
||||
case BlendingMode::MULTIPLY:
|
||||
cg.generateDefine(fs, "POST_LIGHTING_BLEND_MODE_MULTIPLY", true);
|
||||
CodeGenerator::generateDefine(fs, "POST_LIGHTING_BLEND_MODE_MULTIPLY", true);
|
||||
break;
|
||||
case BlendingMode::SCREEN:
|
||||
cg.generateDefine(fs, "POST_LIGHTING_BLEND_MODE_SCREEN", true);
|
||||
CodeGenerator::generateDefine(fs, "POST_LIGHTING_BLEND_MODE_SCREEN", true);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
cg.generateDefine(fs, getShadingDefine(material.shading), true);
|
||||
generateMaterialDefines(fs, cg, mProperties, mDefines);
|
||||
CodeGenerator::generateDefine(fs, getShadingDefine(material.shading), true);
|
||||
generateMaterialDefines(fs, mProperties, mDefines);
|
||||
|
||||
cg.generateDefine(fs, "MATERIAL_HAS_CUSTOM_SURFACE_SHADING", material.hasCustomSurfaceShading);
|
||||
CodeGenerator::generateDefine(fs, "MATERIAL_HAS_CUSTOM_SURFACE_SHADING", material.hasCustomSurfaceShading);
|
||||
|
||||
cg.generateShaderInputs(fs, ShaderType::FRAGMENT, material.requiredAttributes, interpolation);
|
||||
CodeGenerator::generateShaderInputs(fs, ShaderType::FRAGMENT, material.requiredAttributes, interpolation);
|
||||
|
||||
// custom material variables
|
||||
size_t variableIndex = 0;
|
||||
for (const auto& variable : mVariables) {
|
||||
cg.generateVariable(fs, ShaderType::FRAGMENT, variable, variableIndex++);
|
||||
CodeGenerator::generateVariable(fs, ShaderType::FRAGMENT, variable, variableIndex++);
|
||||
}
|
||||
|
||||
// uniforms and samplers
|
||||
@@ -427,7 +426,7 @@ std::string ShaderGenerator::createFragmentProgram(filament::backend::ShaderMode
|
||||
BindingPoints::FROXEL_RECORDS, UibGenerator::getFroxelRecordUib());
|
||||
cg.generateUniforms(fs, ShaderType::FRAGMENT,
|
||||
BindingPoints::PER_MATERIAL_INSTANCE, material.uib);
|
||||
cg.generateSeparator(fs);
|
||||
CodeGenerator::generateSeparator(fs);
|
||||
cg.generateSamplers(fs,
|
||||
material.samplerBindings.getBlockOffset(BindingPoints::PER_VIEW),
|
||||
SibGenerator::getPerViewSib(variantKey));
|
||||
@@ -438,39 +437,41 @@ std::string ShaderGenerator::createFragmentProgram(filament::backend::ShaderMode
|
||||
fs << "float filament_lodBias;\n";
|
||||
|
||||
// shading code
|
||||
cg.generateCommon(fs, ShaderType::FRAGMENT);
|
||||
cg.generateGetters(fs, ShaderType::FRAGMENT);
|
||||
cg.generateCommonMaterial(fs, ShaderType::FRAGMENT);
|
||||
cg.generateParameters(fs, ShaderType::FRAGMENT);
|
||||
cg.generateFog(fs, ShaderType::FRAGMENT);
|
||||
CodeGenerator::generateCommon(fs, ShaderType::FRAGMENT);
|
||||
CodeGenerator::generateGetters(fs, ShaderType::FRAGMENT);
|
||||
CodeGenerator::generateCommonMaterial(fs, ShaderType::FRAGMENT);
|
||||
CodeGenerator::generateParameters(fs, ShaderType::FRAGMENT);
|
||||
CodeGenerator::generateFog(fs, ShaderType::FRAGMENT);
|
||||
|
||||
// shading model
|
||||
if (filament::Variant::isValidDepthVariant(variantKey)) {
|
||||
// In MASKED mode or with transparent shadows, we need the alpha channel computed by
|
||||
// the material (user code), so we append it here.
|
||||
if (material.blendingMode == BlendingMode::MASKED || material.hasTransparentShadow) {
|
||||
appendShader(fs, mMaterialCode, mMaterialLineOffset);
|
||||
appendShader(fs, mMaterialFragmentCode, mMaterialLineOffset);
|
||||
}
|
||||
// these variants are special and are treated as DEPTH variants. Filament will never
|
||||
// request that variant for the color pass.
|
||||
cg.generateDepthShaderMain(fs, ShaderType::FRAGMENT);
|
||||
CodeGenerator::generateDepthShaderMain(fs, ShaderType::FRAGMENT);
|
||||
} else {
|
||||
appendShader(fs, mMaterialCode, mMaterialLineOffset);
|
||||
appendShader(fs, mMaterialFragmentCode, mMaterialLineOffset);
|
||||
if (material.isLit) {
|
||||
cg.generateShaderLit(fs, ShaderType::FRAGMENT, variant, material.shading,
|
||||
CodeGenerator::generateShaderLit(fs, ShaderType::FRAGMENT, variant, material.shading,
|
||||
material.hasCustomSurfaceShading);
|
||||
} else {
|
||||
cg.generateShaderUnlit(fs, ShaderType::FRAGMENT, variant, material.hasShadowMultiplier);
|
||||
CodeGenerator::generateShaderUnlit(fs, ShaderType::FRAGMENT, variant, material.hasShadowMultiplier);
|
||||
}
|
||||
// entry point
|
||||
cg.generateShaderMain(fs, ShaderType::FRAGMENT);
|
||||
CodeGenerator::generateShaderMain(fs, ShaderType::FRAGMENT);
|
||||
}
|
||||
|
||||
cg.generateEpilog(fs);
|
||||
CodeGenerator::generateEpilog(fs);
|
||||
|
||||
return fs.c_str();
|
||||
}
|
||||
|
||||
void ShaderGenerator::fixupExternalSamplers(filament::backend::ShaderModel sm,
|
||||
std::string& shader, MaterialInfo const& material) const noexcept {
|
||||
void ShaderGenerator::fixupExternalSamplers(ShaderModel sm,
|
||||
std::string& shader, MaterialInfo const& material) noexcept {
|
||||
// External samplers are only supported on GL ES at the moment, we must
|
||||
// skip the fixup on desktop targets
|
||||
if (material.hasExternalSamplers && sm == ShaderModel::GL_ES_30) {
|
||||
@@ -479,28 +480,28 @@ void ShaderGenerator::fixupExternalSamplers(filament::backend::ShaderModel sm,
|
||||
}
|
||||
|
||||
std::string ShaderGenerator::createPostProcessVertexProgram(
|
||||
filament::backend::ShaderModel sm, MaterialBuilder::TargetApi targetApi,
|
||||
ShaderModel sm, MaterialBuilder::TargetApi targetApi,
|
||||
MaterialBuilder::TargetLanguage targetLanguage, MaterialInfo const& material,
|
||||
uint8_t variant, const filament::SamplerBindingMap& samplerBindingMap) const noexcept {
|
||||
uint8_t variant, const SamplerBindingMap& samplerBindingMap) const noexcept {
|
||||
const CodeGenerator cg(sm, targetApi, targetLanguage);
|
||||
utils::io::sstream vs;
|
||||
io::sstream vs;
|
||||
cg.generateProlog(vs, ShaderType::VERTEX, false);
|
||||
|
||||
cg.generateQualityDefine(vs, material.quality);
|
||||
|
||||
cg.generateDefine(vs, "LOCATION_POSITION", uint32_t(VertexAttribute::POSITION));
|
||||
CodeGenerator::generateDefine(vs, "LOCATION_POSITION", uint32_t(VertexAttribute::POSITION));
|
||||
|
||||
// The UVs are at the location immediately following the custom variables.
|
||||
cg.generateDefine(vs, "LOCATION_UVS", uint32_t(MaterialBuilder::MATERIAL_VARIABLES_COUNT));
|
||||
CodeGenerator::generateDefine(vs, "LOCATION_UVS", uint32_t(MaterialBuilder::MATERIAL_VARIABLES_COUNT));
|
||||
|
||||
// custom material variables
|
||||
size_t variableIndex = 0;
|
||||
for (const auto& variable : mVariables) {
|
||||
cg.generateVariable(vs, ShaderType::VERTEX, variable, variableIndex++);
|
||||
CodeGenerator::generateVariable(vs, ShaderType::VERTEX, variable, variableIndex++);
|
||||
}
|
||||
|
||||
cg.generatePostProcessInputs(vs, ShaderType::VERTEX);
|
||||
generatePostProcessMaterialVariantDefines(cg, vs, PostProcessVariant(variant));
|
||||
CodeGenerator::generatePostProcessInputs(vs, ShaderType::VERTEX);
|
||||
generatePostProcessMaterialVariantDefines(vs, PostProcessVariant(variant));
|
||||
|
||||
cg.generateUniforms(vs, ShaderType::VERTEX,
|
||||
BindingPoints::PER_VIEW, UibGenerator::getPerViewUib());
|
||||
@@ -511,36 +512,36 @@ std::string ShaderGenerator::createPostProcessVertexProgram(
|
||||
material.samplerBindings.getBlockOffset(BindingPoints::PER_MATERIAL_INSTANCE),
|
||||
material.sib);
|
||||
|
||||
cg.generateCommon(vs, ShaderType::VERTEX);
|
||||
cg.generatePostProcessGetters(vs, ShaderType::VERTEX);
|
||||
CodeGenerator::generateCommon(vs, ShaderType::VERTEX);
|
||||
CodeGenerator::generatePostProcessGetters(vs, ShaderType::VERTEX);
|
||||
|
||||
appendShader(vs, mMaterialVertexCode, mMaterialVertexLineOffset);
|
||||
|
||||
cg.generatePostProcessMain(vs, ShaderType::VERTEX);
|
||||
CodeGenerator::generatePostProcessMain(vs, ShaderType::VERTEX);
|
||||
|
||||
cg.generateEpilog(vs);
|
||||
CodeGenerator::generateEpilog(vs);
|
||||
return vs.c_str();
|
||||
}
|
||||
|
||||
std::string ShaderGenerator::createPostProcessFragmentProgram(
|
||||
filament::backend::ShaderModel sm, MaterialBuilder::TargetApi targetApi,
|
||||
ShaderModel sm, MaterialBuilder::TargetApi targetApi,
|
||||
MaterialBuilder::TargetLanguage targetLanguage, MaterialInfo const& material,
|
||||
uint8_t variant, const filament::SamplerBindingMap& samplerBindingMap) const noexcept {
|
||||
uint8_t variant, const SamplerBindingMap& samplerBindingMap) const noexcept {
|
||||
const CodeGenerator cg(sm, targetApi, targetLanguage);
|
||||
utils::io::sstream fs;
|
||||
io::sstream fs;
|
||||
cg.generateProlog(fs, ShaderType::FRAGMENT, false);
|
||||
|
||||
cg.generateQualityDefine(fs, material.quality);
|
||||
|
||||
// The UVs are at the location immediately following the custom variables.
|
||||
cg.generateDefine(fs, "LOCATION_UVS", uint32_t(MaterialBuilder::MATERIAL_VARIABLES_COUNT));
|
||||
CodeGenerator::generateDefine(fs, "LOCATION_UVS", uint32_t(MaterialBuilder::MATERIAL_VARIABLES_COUNT));
|
||||
|
||||
generatePostProcessMaterialVariantDefines(cg, fs, PostProcessVariant(variant));
|
||||
generatePostProcessMaterialVariantDefines(fs, PostProcessVariant(variant));
|
||||
|
||||
// custom material variables
|
||||
size_t variableIndex = 0;
|
||||
for (const auto& variable : mVariables) {
|
||||
cg.generateVariable(fs, ShaderType::FRAGMENT, variable, variableIndex++);
|
||||
CodeGenerator::generateVariable(fs, ShaderType::FRAGMENT, variable, variableIndex++);
|
||||
}
|
||||
|
||||
cg.generateUniforms(fs, ShaderType::FRAGMENT,
|
||||
@@ -553,10 +554,10 @@ std::string ShaderGenerator::createPostProcessFragmentProgram(
|
||||
material.sib);
|
||||
|
||||
// subpass
|
||||
cg.generateSubpass(fs, material.subpass);
|
||||
CodeGenerator::generateSubpass(fs, material.subpass);
|
||||
|
||||
cg.generateCommon(fs, ShaderType::FRAGMENT);
|
||||
cg.generatePostProcessGetters(fs, ShaderType::FRAGMENT);
|
||||
CodeGenerator::generateCommon(fs, ShaderType::FRAGMENT);
|
||||
CodeGenerator::generatePostProcessGetters(fs, ShaderType::FRAGMENT);
|
||||
|
||||
// Generate post-process outputs.
|
||||
for (const auto& output : mOutputs) {
|
||||
@@ -565,16 +566,16 @@ std::string ShaderGenerator::createPostProcessFragmentProgram(
|
||||
output.qualifier, output.type);
|
||||
}
|
||||
if (output.target == MaterialBuilder::OutputTarget::DEPTH) {
|
||||
cg.generateDefine(fs, "FRAG_OUTPUT_DEPTH", 1u);
|
||||
CodeGenerator::generateDefine(fs, "FRAG_OUTPUT_DEPTH", 1u);
|
||||
}
|
||||
}
|
||||
|
||||
cg.generatePostProcessInputs(fs, ShaderType::FRAGMENT);
|
||||
CodeGenerator::generatePostProcessInputs(fs, ShaderType::FRAGMENT);
|
||||
|
||||
appendShader(fs, mMaterialCode, mMaterialLineOffset);
|
||||
appendShader(fs, mMaterialFragmentCode, mMaterialLineOffset);
|
||||
|
||||
cg.generatePostProcessMain(fs, ShaderType::FRAGMENT);
|
||||
cg.generateEpilog(fs);
|
||||
CodeGenerator::generatePostProcessMain(fs, ShaderType::FRAGMENT);
|
||||
CodeGenerator::generateEpilog(fs);
|
||||
return fs.c_str();
|
||||
}
|
||||
|
||||
|
||||
@@ -45,18 +45,17 @@ public:
|
||||
size_t vertexLineOffset,
|
||||
MaterialBuilder::MaterialDomain materialDomain) noexcept;
|
||||
|
||||
std::string createVertexProgram(filament::backend::ShaderModel sm,
|
||||
std::string createVertexProgram(filament::backend::ShaderModel shaderModel,
|
||||
MaterialBuilder::TargetApi targetApi, MaterialBuilder::TargetLanguage targetLanguage,
|
||||
MaterialInfo const& material, uint8_t variantKey,
|
||||
filament::Interpolation interpolation,
|
||||
filament::VertexDomain vertexDomain) const noexcept;
|
||||
|
||||
std::string createFragmentProgram(filament::backend::ShaderModel sm,
|
||||
MaterialBuilder::TargetApi targetApi, MaterialBuilder::TargetLanguage targetLanguage,
|
||||
MaterialInfo const& material, uint8_t variantKey,
|
||||
filament::Interpolation interpolation) const noexcept;
|
||||
|
||||
bool hasCustomDepthShader() const noexcept;
|
||||
|
||||
/**
|
||||
* When a GLSL shader is optimized we run it through an intermediate SPIR-V
|
||||
* representation. Unfortunately external samplers cannot be used with SPIR-V
|
||||
@@ -64,8 +63,8 @@ public:
|
||||
* fixup step can be used to turn the samplers back into external samplers after
|
||||
* the optimizations have been applied.
|
||||
*/
|
||||
void fixupExternalSamplers(filament::backend::ShaderModel sm, std::string& shader,
|
||||
MaterialInfo const& material) const noexcept;
|
||||
static void fixupExternalSamplers(filament::backend::ShaderModel sm, std::string& shader,
|
||||
MaterialInfo const& material) noexcept;
|
||||
|
||||
private:
|
||||
|
||||
@@ -84,10 +83,11 @@ private:
|
||||
MaterialBuilder::OutputList mOutputs;
|
||||
MaterialBuilder::MaterialDomain mMaterialDomain;
|
||||
MaterialBuilder::PreprocessorDefineList mDefines;
|
||||
utils::CString mMaterialCode;
|
||||
utils::CString mMaterialFragmentCode;
|
||||
utils::CString mMaterialVertexCode;
|
||||
size_t mMaterialLineOffset;
|
||||
size_t mMaterialVertexLineOffset;
|
||||
bool mIsMaterialVertexShaderEmpty;
|
||||
};
|
||||
|
||||
} // namespace filament
|
||||
|
||||
@@ -280,7 +280,7 @@ static float UTILS_UNUSED VisibilityAshikhmin(float NoV, float NoL, float /*a*/)
|
||||
* N h
|
||||
*
|
||||
* N 4
|
||||
* Er() = ------------- --- ∑ V(v) <n•l>
|
||||
* Er() = ------------- --- ∑ L(v) <n•l>
|
||||
* 4 ∑ <n•l> N
|
||||
*
|
||||
*
|
||||
@@ -541,7 +541,7 @@ void CubemapIBL::roughnessFilter(
|
||||
* N l n•l
|
||||
*
|
||||
*
|
||||
* To avoid to multiply by 1/PI in the shader, we do it here, which simplifies to:
|
||||
* To avoid multiplying by 1/PI in the shader, we do it here, which simplifies to:
|
||||
*
|
||||
* +----------------------+
|
||||
* | 1 |
|
||||
|
||||
@@ -22,6 +22,7 @@
|
||||
|
||||
#include <utils/Log.h>
|
||||
|
||||
#include <cstring>
|
||||
#include <limits>
|
||||
#include <memory>
|
||||
#include <sstream>
|
||||
|
||||
@@ -182,6 +182,7 @@ const char* toString(backend::UniformType type) noexcept {
|
||||
case backend::UniformType::UINT4: return "uint4";
|
||||
case backend::UniformType::MAT3: return "float3x3";
|
||||
case backend::UniformType::MAT4: return "float4x4";
|
||||
case backend::UniformType::STRUCT: return "struct";
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -17,7 +17,7 @@
|
||||
#ifndef TNT_UTILS_CONDITION_H
|
||||
#define TNT_UTILS_CONDITION_H
|
||||
|
||||
#if defined(__linux__) && !defined(__SANITIZE_THREAD__)
|
||||
#if defined(__linux__)
|
||||
#include <utils/linux/Condition.h>
|
||||
#else
|
||||
#include <utils/generic/Condition.h>
|
||||
|
||||
@@ -21,6 +21,7 @@
|
||||
#include <utils/Panic.h>
|
||||
|
||||
#include <algorithm>
|
||||
#include <limits>
|
||||
#include <memory>
|
||||
#include <type_traits>
|
||||
#include <utility>
|
||||
|
||||
@@ -17,7 +17,7 @@
|
||||
#ifndef TNT_UTILS_MUTEX_H
|
||||
#define TNT_UTILS_MUTEX_H
|
||||
|
||||
#if defined(__linux__) && !defined(__SANITIZE_THREAD__)
|
||||
#if defined(__linux__)
|
||||
#include <utils/linux/Mutex.h>
|
||||
#else
|
||||
#include <utils/generic/Mutex.h>
|
||||
|
||||
@@ -75,8 +75,8 @@ private:
|
||||
};
|
||||
} // namespace details
|
||||
|
||||
#if defined(__SANITIZE_THREAD__)
|
||||
// Unfortunately TSAN doesn't support homegrown synchronization primitives
|
||||
#if UTILS_HAS_SANITIZE_THREAD
|
||||
// Active spins with atomics slow down execution too much under ThreadSanitizer.
|
||||
using SpinLock = Mutex;
|
||||
#elif defined(__ARM_ARCH_7A__)
|
||||
// We've had problems with "wfe" on some ARM-V7 devices, causing spurious SIGILL
|
||||
|
||||
@@ -65,19 +65,21 @@
|
||||
#endif
|
||||
|
||||
#define UTILS_NO_SANITIZE_THREAD
|
||||
#if defined(__has_feature)
|
||||
# if __has_feature(thread_sanitizer)
|
||||
# undef UTILS_NO_SANITIZE_THREAD
|
||||
# define UTILS_NO_SANITIZE_THREAD __attribute__((no_sanitize("thread")))
|
||||
# endif
|
||||
#if __has_feature(thread_sanitizer)
|
||||
#undef UTILS_NO_SANITIZE_THREAD
|
||||
#define UTILS_NO_SANITIZE_THREAD __attribute__((no_sanitize("thread")))
|
||||
#endif
|
||||
|
||||
#define UTILS_HAS_SANITIZE_THREAD 0
|
||||
#if __has_feature(thread_sanitizer) || defined(__SANITIZE_THREAD__)
|
||||
#undef UTILS_HAS_SANITIZE_THREAD
|
||||
#define UTILS_HAS_SANITIZE_THREAD 1
|
||||
#endif
|
||||
|
||||
#define UTILS_HAS_SANITIZE_MEMORY 0
|
||||
#if defined(__has_feature)
|
||||
# if __has_feature(memory_sanitizer)
|
||||
# undef UTILS_HAS_SANITIZE_MEMORY
|
||||
# define UTILS_HAS_SANITIZE_MEMORY 1
|
||||
# endif
|
||||
#if __has_feature(memory_sanitizer)
|
||||
#undef UTILS_HAS_SANITIZE_MEMORY
|
||||
#define UTILS_HAS_SANITIZE_MEMORY 1
|
||||
#endif
|
||||
|
||||
/*
|
||||
|
||||
@@ -20,6 +20,7 @@
|
||||
|
||||
#include <utils/Log.h>
|
||||
|
||||
#include <cstring>
|
||||
#include <vector>
|
||||
|
||||
using namespace utils;
|
||||
|
||||
@@ -22,7 +22,6 @@ set(SHADERS
|
||||
src/common_shadowing.fs
|
||||
src/common_types.fs
|
||||
src/depth_main.fs
|
||||
src/depth_main.vs
|
||||
src/fog.fs
|
||||
src/getters.fs
|
||||
src/getters.vs
|
||||
|
||||
@@ -57,6 +57,18 @@ float max3(const vec3 v) {
|
||||
return max(v.x, max(v.y, v.z));
|
||||
}
|
||||
|
||||
float vmax(const vec2 v) {
|
||||
return max(v.x, v.y);
|
||||
}
|
||||
|
||||
float vmax(const vec3 v) {
|
||||
return max(v.x, max(v.y, v.z));
|
||||
}
|
||||
|
||||
float vmax(const vec4 v) {
|
||||
return max(max(v.x, v.y), max(v.y, v.z));
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the minimum component of the specified vector.
|
||||
*
|
||||
@@ -66,6 +78,18 @@ float min3(const vec3 v) {
|
||||
return min(v.x, min(v.y, v.z));
|
||||
}
|
||||
|
||||
float vmin(const vec2 v) {
|
||||
return min(v.x, v.y);
|
||||
}
|
||||
|
||||
float vmin(const vec3 v) {
|
||||
return min(v.x, min(v.y, v.z));
|
||||
}
|
||||
|
||||
float vmin(const vec4 v) {
|
||||
return min(min(v.x, v.y), min(v.y, v.z));
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
// Trigonometry
|
||||
//------------------------------------------------------------------------------
|
||||
@@ -102,7 +126,7 @@ float acosFastPositive(float x) {
|
||||
*
|
||||
* @public-api
|
||||
*/
|
||||
vec4 mulMat4x4Float3(const highp mat4 m, const highp vec3 v) {
|
||||
highp vec4 mulMat4x4Float3(const highp mat4 m, const highp vec3 v) {
|
||||
return v.x * m[0] + (v.y * m[1] + (v.z * m[2] + m[3]));
|
||||
}
|
||||
|
||||
@@ -112,7 +136,7 @@ vec4 mulMat4x4Float3(const highp mat4 m, const highp vec3 v) {
|
||||
*
|
||||
* @public-api
|
||||
*/
|
||||
vec3 mulMat3x3Float3(const highp mat4 m, const highp vec3 v) {
|
||||
highp vec3 mulMat3x3Float3(const highp mat4 m, const highp vec3 v) {
|
||||
return v.x * m[0].xyz + (v.y * m[1].xyz + (v.z * m[2].xyz));
|
||||
}
|
||||
|
||||
|
||||
@@ -8,18 +8,19 @@
|
||||
* The returned point may contain a bias to attempt to eliminate common
|
||||
* shadowing artifacts such as "acne". To achieve this, the world space
|
||||
* normal at the point must also be passed to this function.
|
||||
* Normal bias is not used for VSM.
|
||||
*/
|
||||
highp vec4 computeLightSpacePosition(const highp vec3 p, const highp vec3 n, const highp vec3 l,
|
||||
const float b, const highp mat4 lightFromWorldMatrix) {
|
||||
#if defined(HAS_VSM)
|
||||
// VSM don't apply the shadow bias
|
||||
highp vec4 lightSpacePosition = (lightFromWorldMatrix * vec4(p, 1.0));
|
||||
#else
|
||||
float NoL = saturate(dot(n, l));
|
||||
float sinTheta = sqrt(1.0 - NoL * NoL);
|
||||
highp vec3 offsetPosition = p + n * (sinTheta * b);
|
||||
highp vec4 lightSpacePosition = (lightFromWorldMatrix * vec4(offsetPosition, 1.0));
|
||||
|
||||
highp vec4 computeLightSpacePosition(highp vec3 p, const highp vec3 n,
|
||||
const highp vec3 l, const float b, const highp mat4 lightFromWorldMatrix) {
|
||||
|
||||
#if !defined(HAS_VSM)
|
||||
highp float NoL = saturate(dot(n, l));
|
||||
highp float sinTheta = sqrt(1.0 - NoL * NoL);
|
||||
p += n * (sinTheta * b);
|
||||
#endif
|
||||
return lightSpacePosition;
|
||||
|
||||
return mulMat4x4Float3(lightFromWorldMatrix, p);
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif // HAS_SHADOWING
|
||||
|
||||
@@ -22,3 +22,12 @@
|
||||
|
||||
#define float3x3 mat3
|
||||
#define float4x4 mat4
|
||||
|
||||
// Adreno drivers seem to ignore precision qualifiers in structs, unless they're used in
|
||||
// UBOs, which is is the case here.
|
||||
struct ShadowData {
|
||||
highp mat4 lightFromWorldMatrix;
|
||||
highp vec3 direction;
|
||||
float normalBias;
|
||||
float texelSizeAtOneMeter;
|
||||
};
|
||||
|
||||
@@ -12,10 +12,12 @@ layout(location = 0) out highp uint2 outPicking;
|
||||
// note: HAS_VSM and HAS_PICKING are mutually exclusive
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
highp vec2 computeDepthMomentsVSM(const highp float depth);
|
||||
|
||||
void main() {
|
||||
filament_lodBias = frameUniforms.lodBias;
|
||||
|
||||
#if defined(BLEND_MODE_MASKED) || (defined(BLEND_MODE_TRANSPARENT) && defined(HAS_TRANSPARENT_SHADOW))
|
||||
#if defined(BLEND_MODE_MASKED) || ((defined(BLEND_MODE_TRANSPARENT) || defined(BLEND_MODE_FADE)) && defined(HAS_TRANSPARENT_SHADOW))
|
||||
MaterialInputs inputs;
|
||||
initMaterial(inputs);
|
||||
material(inputs);
|
||||
@@ -37,19 +39,22 @@ 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 main.vs)
|
||||
highp float depth = vertex_worldPosition.w;
|
||||
depth = exp(depth);
|
||||
fragColor.xy = computeDepthMomentsVSM(depth);
|
||||
fragColor.zw = vec2(0.0);
|
||||
// enable for full EVSM (needed for large blurs). RGBA16F needed.
|
||||
//fragColor.zw = computeDepthMomentsVSM(-1.0/depth);
|
||||
#elif defined(HAS_PICKING)
|
||||
outPicking.x = objectUniforms.objectId;
|
||||
outPicking.y = floatBitsToUint(vertex_position.z / vertex_position.w);
|
||||
#else
|
||||
// that's it
|
||||
#endif
|
||||
}
|
||||
|
||||
highp vec2 computeDepthMomentsVSM(const highp float depth) {
|
||||
// computes the moments
|
||||
// See GPU Gems 3
|
||||
// https://developer.nvidia.com/gpugems/gpugems3/part-ii-light-and-shadows/chapter-8-summed-area-variance-shadow-maps
|
||||
@@ -63,11 +68,5 @@ void main() {
|
||||
highp float dy = dFdy(depth);
|
||||
moments.y = depth * depth + 0.25 * (dx * dx + dy * dy);
|
||||
|
||||
fragColor = vec4(moments, 0.0, 0.0);
|
||||
#elif defined(HAS_PICKING)
|
||||
outPicking.x = objectUniforms.objectId;
|
||||
outPicking.y = floatBitsToUint(vertex_position.z / vertex_position.w);
|
||||
#else
|
||||
// that's it
|
||||
#endif
|
||||
return moments;
|
||||
}
|
||||
|
||||
@@ -1,43 +0,0 @@
|
||||
// The sole purpose of this no-op function is to improve parity between the depth vertex shader
|
||||
// and color vertex shader, thus working around a variance issue seen with NVIDIA drivers.
|
||||
void materialVertex(inout MaterialVertexInputs m) { }
|
||||
|
||||
// NOTE: This shader is only used when the user's material does not have custom vertex code.
|
||||
// There is no need to check anything related to material inputs in this file.
|
||||
void main() {
|
||||
|
||||
// World position is used to compute gl_Position, except for vertices already in the device domain.
|
||||
// Regardless of vertex domain, if VSM is turned on, then we need to compute world position to pass
|
||||
// to the fragment shader.
|
||||
#if !defined(VERTEX_DOMAIN_DEVICE) || defined(HAS_VSM)
|
||||
// Run initMaterialVertex to compute material.worldPosition.
|
||||
MaterialVertexInputs material;
|
||||
initMaterialVertex(material);
|
||||
materialVertex(material);
|
||||
#endif
|
||||
|
||||
#if defined(VERTEX_DOMAIN_DEVICE)
|
||||
gl_Position = getPosition();
|
||||
// GL convention to inverted DX convention
|
||||
gl_Position.z = gl_Position.z * -0.5 + 0.5;
|
||||
#else
|
||||
gl_Position = getClipFromWorldMatrix() * getWorldPosition(material);
|
||||
#endif
|
||||
|
||||
#if defined(HAS_VSM)
|
||||
vertex_worldPosition = material.worldPosition.xyz;
|
||||
#endif
|
||||
|
||||
// this must happen before we compensate for vulkan below
|
||||
vertex_position = gl_Position;
|
||||
|
||||
#if defined(TARGET_VULKAN_ENVIRONMENT)
|
||||
// In Vulkan, clip space is Y-down. In OpenGL and Metal, clip space is Y-up.
|
||||
gl_Position.y = -gl_Position.y;
|
||||
#endif
|
||||
|
||||
#if !defined(TARGET_VULKAN_ENVIRONMENT) && !defined(TARGET_METAL_ENVIRONMENT)
|
||||
// This is not needed in Vulkan or Metal because clipControl is always (1, 0)
|
||||
gl_Position.z = dot(gl_Position.zw, frameUniforms.clipControl);
|
||||
#endif
|
||||
}
|
||||
@@ -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,17 @@ highp vec3 getNormalizedViewportCoord2() {
|
||||
}
|
||||
|
||||
#if defined(HAS_SHADOWING) && defined(HAS_DYNAMIC_LIGHTING)
|
||||
highp vec3 getSpotLightSpacePosition(uint index) {
|
||||
vec3 dir = shadowUniforms.directionShadowBias[index].xyz;
|
||||
float bias = shadowUniforms.directionShadowBias[index].w;
|
||||
highp vec4 position = computeLightSpacePosition(vertex_worldPosition,
|
||||
vertex_worldNormal, dir, bias, shadowUniforms.spotLightFromWorldMatrix[index]);
|
||||
highp vec4 getSpotLightSpacePosition(uint index) {
|
||||
highp mat4 lightFromWorldMatrix = shadowUniforms.shadows[index].lightFromWorldMatrix;
|
||||
highp vec3 dir = shadowUniforms.shadows[index].direction;
|
||||
|
||||
#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
|
||||
// for spotlights, the bias depends on z
|
||||
float bias = shadowUniforms.shadows[index].normalBias;
|
||||
highp vec4 positionLs = mulMat4x4Float3(lightFromWorldMatrix, vertex_worldPosition.xyz);
|
||||
highp float oneOverZ = positionLs.w / positionLs.z;
|
||||
|
||||
return computeLightSpacePosition(vertex_worldPosition.xyz,
|
||||
vertex_worldNormal, dir, oneOverZ * bias, lightFromWorldMatrix);
|
||||
}
|
||||
#endif
|
||||
|
||||
@@ -146,25 +131,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)
|
||||
|
||||
@@ -57,7 +57,7 @@ void evaluateDirectionalLight(const MaterialInputs material,
|
||||
bool hasDirectionalShadows = bool(frameUniforms.directionalShadows & 1u);
|
||||
if (hasDirectionalShadows && cascadeHasVisibleShadows) {
|
||||
uint layer = cascade;
|
||||
visibility = shadow(light_shadowMap, layer, getCascadeLightSpacePosition(cascade));
|
||||
visibility = shadow(true, light_shadowMap, layer, 0u, cascade);
|
||||
}
|
||||
if ((frameUniforms.directionalShadows & 0x2u) != 0u && visibility > 0.0) {
|
||||
if ((objectUniforms.flags & FILAMENT_OBJECT_CONTACT_SHADOWS_BIT) != 0u) {
|
||||
|
||||
@@ -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;
|
||||
@@ -214,8 +211,7 @@ void evaluatePunctualLights(const MaterialInputs material,
|
||||
#if defined(HAS_SHADOWING)
|
||||
if (light.NoL > 0.0) {
|
||||
if (light.castsShadows) {
|
||||
visibility = shadow(light_shadowMap, light.shadowLayer,
|
||||
getSpotLightSpacePosition(light.shadowIndex));
|
||||
visibility = shadow(false, light_shadowMap, light.shadowLayer, light.shadowIndex, 0u);
|
||||
}
|
||||
if (light.contactShadows && visibility > 0.0) {
|
||||
if ((objectUniforms.flags & FILAMENT_OBJECT_CONTACT_SHADOWS_BIT) != 0u) {
|
||||
|
||||
@@ -1,5 +1,28 @@
|
||||
/*
|
||||
* This is the main vertex shader of surface materials. It can be invoked with
|
||||
* USE_OPTIMIZED_DEPTH_VERTEX_SHADER defined, and in this case we are guaranteed that the
|
||||
* DEPTH variant is active *AND* there is no custom vertex shader (i.e.: materialVertex() is
|
||||
* empty).
|
||||
* We can use this to remove all code that doesn't participate in the depth computation.
|
||||
*/
|
||||
|
||||
void main() {
|
||||
// Initialize the inputs to sensible default values, see material_inputs.vs
|
||||
#if defined(USE_OPTIMIZED_DEPTH_VERTEX_SHADER)
|
||||
|
||||
// In USE_OPTIMIZED_DEPTH_VERTEX_SHADER mode, we can even skip this if we're already in
|
||||
// VERTEX_DOMAIN_DEVICE and we don't have VSM.
|
||||
#if !defined(VERTEX_DOMAIN_DEVICE) || defined(HAS_VSM)
|
||||
// Run initMaterialVertex to compute material.worldPosition.
|
||||
MaterialVertexInputs material;
|
||||
initMaterialVertex(material);
|
||||
// materialVertex() is guaranteed to be empty here, but we keep it to workaround some problem
|
||||
// in NVIDA drivers related to depth invariance.
|
||||
materialVertex(material);
|
||||
#endif
|
||||
|
||||
#else // defined(USE_OPTIMIZED_DEPTH_VERTEX_SHADER)
|
||||
|
||||
MaterialVertexInputs material;
|
||||
initMaterialVertex(material);
|
||||
|
||||
@@ -82,16 +105,21 @@ 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
|
||||
|
||||
#endif // !defined(USE_OPTIMIZED_DEPTH_VERTEX_SHADER)
|
||||
|
||||
|
||||
#if defined(VERTEX_DOMAIN_DEVICE)
|
||||
// The other vertex domains are handled in initMaterialVertex()->computeWorldPosition()
|
||||
gl_Position = getPosition();
|
||||
@@ -99,15 +127,35 @@ void main() {
|
||||
gl_Position = getClipFromWorldMatrix() * getWorldPosition(material);
|
||||
#endif
|
||||
|
||||
#if !defined(USE_OPTIMIZED_DEPTH_VERTEX_SHADER)
|
||||
#if defined(MATERIAL_HAS_CLIP_SPACE_TRANSFORM)
|
||||
gl_Position = getClipSpaceTransform(material) * gl_Position;
|
||||
#endif
|
||||
#endif // !USE_OPTIMIZED_DEPTH_VERTEX_SHADER
|
||||
|
||||
#if defined(VERTEX_DOMAIN_DEVICE)
|
||||
// GL convention to inverted DX convention (must happen after clipSpaceTransform)
|
||||
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:
|
||||
|
||||
@@ -45,7 +45,7 @@ vec4 evaluateMaterial(const MaterialInputs material) {
|
||||
bool hasDirectionalShadows = bool(frameUniforms.directionalShadows & 1u);
|
||||
if (hasDirectionalShadows && cascadeHasVisibleShadows) {
|
||||
uint layer = cascade;
|
||||
visibility = shadow(light_shadowMap, layer, getCascadeLightSpacePosition(cascade));
|
||||
visibility = shadow(true, light_shadowMap, layer, 0u, cascade);
|
||||
}
|
||||
if ((frameUniforms.directionalShadows & 0x2u) != 0u && visibility > 0.0) {
|
||||
if ((objectUniforms.flags & FILAMENT_OBJECT_CONTACT_SHADOWS_BIT) != 0u) {
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user