Compare commits

..

68 Commits

Author SHA1 Message Date
Benjamin Doherty
bbe7dbfa92 Merge branch 'rc/1.14.1' into release 2021-11-22 10:11:06 -08:00
Benjamin Doherty
5697922a65 Update RELEASE_NOTES for 1.14.1 2021-11-17 12:04:26 -08:00
Ben Doherty
4da83df2b9 Fix material compilation error with device vertex domain (#4865)
A recent refactor was causing the following error when the vertex domain
was set to `device`:
```
ERROR: main.vs:23: 'material' : undeclared identifier
ERROR: main.vs:23: 'materialVertex' : no matching overloaded function found
```
2021-11-17 12:03:55 -08:00
Ben Doherty
8f156d6588 Android: re-enable VSM cascade fix (#4863) 2021-11-17 10:19:09 -08:00
Benjamin Doherty
cec0871c11 Bump version to 1.14.1 2021-11-15 10:09:45 -08:00
Benjamin Doherty
41a809368b Merge branch 'rc/1.14.0' into release 2021-11-15 10:07:56 -08:00
Benjamin Doherty
f690015a88 Release Filament 1.14.0 2021-11-15 10:06:45 -08:00
Ben Doherty
342deb5a64 speed up under ThreadSanitizer (#4842) 2021-11-12 09:52:58 -08:00
Mathias Agopian
fdc480ff6e Gaussian blur is now applied only on the needed channels
The gaussian blur code was always processing 3 channels no matter the
source or destination. We now have 4 versions for all possible cases.
2021-11-11 23:35:10 -08:00
Mathias Agopian
378b7cd4dd rearrange EVSM code to make it easy to test full EVSM
We're currently only using the positive layer of EVSM which works
work small blurs and anti-aliasing. This just rearranges the code to
make it easy to test the full formulation.
2021-11-11 23:35:10 -08:00
Mathias Agopian
f779fc7a0e move the shadowmap position computation inside shadow()
This is possible because, in fact, we know if we're dealing with a
spot or directional light when we invoke shadow(). So a conditional
inside it is resolved at compile time.

This will allow more flexibility in the future. Also now the shadow()
function only needs the shadow index and cascade -- which is more
future proof (e.g. if we want to handle more than one directional
shadow or spot lights with cascades).
2021-11-11 23:35:10 -08:00
Mathias Agopian
fe11f495d6 simplify how we compute the shadow position
this effectively undoes a recent change, where we had a method for
spot lights and one for the directional light. instead, we can
calculate the bias at the correct Z on the caller side -- which is
needed only for spotlights.
2021-11-11 23:35:10 -08:00
Mathias Agopian
265876c849 store the texel size in world-space in the spot shadow structure 2021-11-11 23:35:10 -08:00
Mathias Agopian
c9c52518c0 repair transparent shadows 2021-11-10 19:48:25 -08:00
Mathias Agopian
96c9ac6f8b fix point-lights which broke recently.
The faulty commit was:

commit 23dab0a013
Author: Mathias Agopian <mathias@google.com>
Date:   Fri Oct 29 12:03:14 2021 -0700

    Fix very narrow spotlight lighting and other minor issues
2021-11-10 15:22:38 -08:00
Ben Doherty
bdeb30a847 Use workflow_dispatch instead of repository_dispatch (#4834) 2021-11-10 13:38:58 -08:00
Romain Guy
ea53eb9290 Skip task incompatible with configuration caching (#4831) 2021-11-09 15:56:00 -08:00
Benjamin Doherty
05875057c9 Update RELEASE_NOTES for 1.14.0 2021-11-09 15:51:13 -08:00
Romain Guy
d67210155b Skip task incompatible with configuration caching (#4831) 2021-11-09 15:01:02 -08:00
Mathias Agopian
ae92c50cee more robust GL compiler error parsing 2021-11-09 14:59:50 -08:00
Mathias Agopian
43429dc0ec remove depth_main.vs from source tree 2021-11-09 12:34:19 -08:00
Mathias Agopian
04cf92a27f Remove all "Shadow Receiver Plane Depth Bias" related code
Shadow Receiver Plane Depth Bias is only needed when using large PCF
filters, which we are no longer doing. Large filter kernels are now 
supported through VSM sampling.
2021-11-09 12:31:43 -08:00
Mathias Agopian
57a57f50f6 most methods can be static in CodeGenerator 2021-11-09 12:31:28 -08:00
Mathias Agopian
5e5a75e6c5 fix bug with MASKED blending mode introduced in previous PR 2021-11-09 10:27:52 -08:00
Mathias Agopian
109c86aaf7 get rid of depth_main.vs
It's functionality is now handled in main.vs which, hopefully will be
less error prone.
2021-11-09 09:54:50 -08:00
Mathias Agopian
10e7e7727a filamat cleanup
This changes how we select the "optimized depth vertex shader", but
this shouldn't change the current behavior.

We now select the "optimized depth vertex shader" if and only if the
user code is empty. In that case, we can safely assume we can remove
all the code that's not necessary for the depth, since it is all
controlled by filament.
2021-11-09 09:54:50 -08:00
Mathias Agopian
a39775050a workaround a IDE (CLion) code parsing bug
It looks like some parts of CLion don't like MaterialDomain because
it exists as an enum value
2021-11-09 09:54:50 -08:00
Benjamin Doherty
44125926d1 Disable configuration-cache 2021-11-08 17:05:34 -08:00
Benjamin Doherty
60734349de Bump version to 1.14.0 2021-11-08 11:52:50 -08:00
Benjamin Doherty
fbfd5ec0ec Merge branch 'rc/1.13.0' into release 2021-11-08 11:50:19 -08:00
Benjamin Doherty
157c0264af Release Filament 1.13.0 2021-11-08 11:49:39 -08:00
Mathias Agopian
9bacfdb390 simplify shadowing code
- remove PCF "low" quality, we only use "HARD" now, when using PCF.
  Higher quality levels are achieved by using VSM.

- added a version of PCF that doesn't use a shadowSampler for future
  use.
2021-11-04 23:01:08 -07:00
Mathias Agopian
f9aaf5c42e Fix normal bias for spotlights.
The normal bias is now computed correctly, this requires to compute
the z in lightspace in the shader.
Note that this would not work as well if we used LISPSM, but we'll
cross that bridge when we get there.
2021-11-04 14:56:00 -07:00
Ben Doherty
a74a95cc65 Call VirtualMachineEnv::JNI_OnLoad for non-Android Java builds (better fix) (#4779) 2021-11-04 13:28:17 -07:00
Ben Doherty
21db695e79 Call VirtualMachineEnv::JNI_OnLoad for non-Android Java builds (better fix) (#4779) 2021-11-04 13:28:08 -07:00
Mathias Agopian
cf917f1093 Add a (crude) way to have structs in our UBOs
The struct must be declared in common_type.fs, so custom
structures are not supported.
2021-11-04 12:43:50 -07:00
Ben Doherty
a8d3a61c25 Enforce readPixels is called within frame (#4802) 2021-11-03 10:59:25 -07:00
Mathias Agopian
80f13a8149 fix typo in comments 2021-11-03 10:54:05 -07:00
Mathias Agopian
e172f3a67f Fix java shadow biases + minor cleanups
- constant bias and normal bias default values in java didn't match
  C++ or the documentation

- stable shadows were enabled by default in java

- polygon offset biases were missing from the java API

- document and don't apply polygon offset to VSM

- remove unused code
2021-11-03 00:20:48 -07:00
Mathias Agopian
c382c0a9cc Tighten spotlights near/far further
We now cull the shadow casters before computing the near/far plane
for spotlights -- we can do that because we know the light's frustum.
So only these casters that contribute to the shadow are accounted for
when calculating the near/far plane.

This PR also include more cleanup and simplifications.
2021-11-02 14:05:09 -07:00
Benjamin Doherty
bc0ea16ff0 Update RELEASE_NOTES for 1.13.0 2021-11-02 13:15:53 -07:00
Mathias Agopian
b2dc8aa84c Fix typo that broke the directional shadowmap 2021-11-02 13:11:44 -07:00
Mathias Agopian
78b29fe967 Fix typo that broke the directional shadowmap 2021-11-01 16:41:30 -07:00
Benjamin Doherty
9987e8b6ab Bump version to 1.13.0 2021-11-01 14:59:11 -07:00
Benjamin Doherty
0d9bdcc008 Merge branch 'rc/1.12.11' into release 2021-11-01 14:55:58 -07:00
Benjamin Doherty
81fa33abeb Release Filament 1.12.11 2021-11-01 14:55:54 -07:00
Mathias Agopian
af7f87e19b make sure the near plane for spot shadows is not negative 2021-11-01 14:19:13 -07:00
Mathias Agopian
3073b03d56 Improve computation of spotlight near/far plane
Near/far plane is now computed dynamically, this significantly
improves the shadowmap depth resolution.

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

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

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

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

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

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

* Enable Gradle configuration caching

* Update BUILDING.md

* Update RELEASE_NOTES.md

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

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

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

* Verify vendoring before including license texts

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

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

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

View File

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

View File

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

View File

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

View File

@@ -31,7 +31,7 @@ repositories {
}
dependencies {
implementation 'com.google.android.filament:filament-android:1.12.10'
implementation 'com.google.android.filament:filament-android:1.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

View File

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

View File

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

View File

@@ -1,26 +1,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')
}
}
}

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@@ -87,8 +87,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;

View File

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

View File

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

View File

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

View File

@@ -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()
*/

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@@ -34,7 +34,7 @@ namespace filament {
using namespace backend;
using namespace math;
ShadowMapManager::ShadowMapManager(FEngine& engine) {
ShadowMapManager::ShadowMapManager(FEngine& engine) { // NOLINT(cppcoreguidelines-pro-type-member-init)
// initialize our ShadowMap array in-place
for (auto& entry : mShadowMapCache) {
new (&entry) ShadowMap(engine);
@@ -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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

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

View 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"
}

View 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"
}

View 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"
}

View 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"
}

View File

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

View File

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

View File

@@ -27,7 +27,7 @@
namespace filament {
// update this when a new version of filament wouldn't work with older materials
static constexpr size_t MATERIAL_VERSION = 12;
static constexpr size_t MATERIAL_VERSION = 14;
/**
* Supported shading models

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@@ -121,10 +121,16 @@ void GLSLPostProcessor::spirvToToMsl(const SpirvBlob *spirv, std::string *outMsl
CompilerMSL::Options mslOptions = {};
mslOptions.platform = platform,
mslOptions.msl_version = CompilerMSL::Options::make_msl_version(1, 1);
mslOptions.msl_version = config.shaderModel == filament::backend::ShaderModel::GL_ES_30 ?
CompilerMSL::Options::make_msl_version(2, 0) : CompilerMSL::Options::make_msl_version(2, 2);
if (config.shaderModel == filament::backend::ShaderModel::GL_ES_30) {
if (config.hasFramebufferFetch) {
mslOptions.use_framebuffer_fetch_subpasses = true;
// On macOS, framebuffer fetch is only available starting with MSL 2.3. Filament will only
// use framebuffer fetch materials on devices that support it.
if (config.shaderModel == filament::backend::ShaderModel::GL_CORE_41) {
mslOptions.msl_version = CompilerMSL::Options::make_msl_version(2, 3);
}
}
mslCompiler.set_msl_options(mslOptions);

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@@ -81,19 +81,6 @@ highp vec2 uvToRenderTargetUV(highp vec2 uv) {
return uv;
}
#if defined(HAS_SHADOWING) && defined(HAS_DIRECTIONAL_LIGHTING)
highp vec3 getLightSpacePosition() {
#if defined(HAS_VSM)
// For VSM, do not project the Z coordinate. It remains as linear Z in light space.
// See the computeVsmLightSpaceMatrix comments in ShadowMap.cpp.
return vec3(vertex_lightSpacePosition.xy * (1.0 / vertex_lightSpacePosition.w),
vertex_lightSpacePosition.z);
#else
return vertex_lightSpacePosition.xyz * (1.0 / vertex_lightSpacePosition.w);
#endif
}
#endif
/**
* Returns the normalized [0, 1] viewport coordinates with the origin at the viewport's bottom-left.
* Z coordinate is in the [0, 1] range as well.
@@ -112,19 +99,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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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