Compare commits
2 Commits
ry/wgpuFix
...
rc/1.68.4
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
c03276f38a | ||
|
|
f98b76f99f |
25
.github/workflows/android-continuous.yml
vendored
Normal file
25
.github/workflows/android-continuous.yml
vendored
Normal file
@@ -0,0 +1,25 @@
|
||||
name: Android
|
||||
|
||||
on:
|
||||
push:
|
||||
branches:
|
||||
- main
|
||||
- release
|
||||
- rc/**
|
||||
|
||||
jobs:
|
||||
build-android:
|
||||
name: build-android
|
||||
# We intentially use a larger runner here to enable larger disk space
|
||||
# (standard linux runner will fail on disk space and faster build time).
|
||||
runs-on: 'ubuntu-24.04-16core'
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v4.1.6
|
||||
with:
|
||||
fetch-depth: 0
|
||||
- uses: ./.github/actions/linux-prereq
|
||||
- name: Run Android Continuous
|
||||
uses: ./.github/actions/android-continuous
|
||||
with:
|
||||
build-abi: armeabi-v7a,arm64-v8a,x86_64
|
||||
29
.github/workflows/ios-continuous.yml
vendored
Normal file
29
.github/workflows/ios-continuous.yml
vendored
Normal file
@@ -0,0 +1,29 @@
|
||||
name: iOS
|
||||
|
||||
on:
|
||||
push:
|
||||
branches:
|
||||
- main
|
||||
- release
|
||||
- rc/**
|
||||
|
||||
jobs:
|
||||
build-ios:
|
||||
name: build-ios
|
||||
runs-on: macos-14-xlarge
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v4.1.6
|
||||
with:
|
||||
fetch-depth: 0
|
||||
- uses: ./.github/actions/mac-prereq
|
||||
- name: Run build script
|
||||
run: |
|
||||
cd build/ios && printf "y" | ./build.sh continuous
|
||||
- uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: filament-ios
|
||||
path: out/filament-release-ios.tgz
|
||||
- name: Build iOS samples
|
||||
run: |
|
||||
cd build/ios && ./build-samples.sh continuous
|
||||
26
.github/workflows/linux-continuous.yml
vendored
Normal file
26
.github/workflows/linux-continuous.yml
vendored
Normal file
@@ -0,0 +1,26 @@
|
||||
name: Linux
|
||||
|
||||
on:
|
||||
push:
|
||||
branches:
|
||||
- main
|
||||
- release
|
||||
- rc/**
|
||||
|
||||
jobs:
|
||||
build-linux:
|
||||
name: build-linux
|
||||
runs-on: 'ubuntu-24.04-16core'
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v4.1.6
|
||||
with:
|
||||
fetch-depth: 0
|
||||
- uses: ./.github/actions/linux-prereq
|
||||
- name: Run build script
|
||||
run: |
|
||||
cd build/linux && printf "y" | ./build.sh continuous
|
||||
- uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: filament-linux
|
||||
path: out/filament-release-linux.tgz
|
||||
29
.github/workflows/mac-continuous.yml
vendored
Normal file
29
.github/workflows/mac-continuous.yml
vendored
Normal file
@@ -0,0 +1,29 @@
|
||||
name: macOS
|
||||
|
||||
on:
|
||||
push:
|
||||
branches:
|
||||
- main
|
||||
- release
|
||||
- rc/**
|
||||
|
||||
jobs:
|
||||
build-mac:
|
||||
name: build-mac
|
||||
runs-on: macos-14-xlarge
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v4.1.6
|
||||
with:
|
||||
fetch-depth: 0
|
||||
- uses: ./.github/actions/mac-prereq
|
||||
- name: Run build script
|
||||
run: |
|
||||
cd build/mac && printf "y" | ./build.sh continuous
|
||||
- uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: filament-mac
|
||||
path: out/filament-release-darwin.tgz
|
||||
- name: Check public headers
|
||||
run: |
|
||||
test/check-headers/test.sh out/release/filament/include
|
||||
59
.github/workflows/postsubmit-main.yml
vendored
59
.github/workflows/postsubmit-main.yml
vendored
@@ -1,59 +0,0 @@
|
||||
name: 'Postsubmit Tasks'
|
||||
|
||||
on:
|
||||
push:
|
||||
branches:
|
||||
- main
|
||||
|
||||
jobs:
|
||||
update-renderdiff-goldens:
|
||||
name: update-renderdiff-goldens
|
||||
runs-on: 'ubuntu-24.04-4core'
|
||||
steps:
|
||||
- uses: actions/checkout@v4.1.6
|
||||
with:
|
||||
fetch-depth: 0
|
||||
- uses: ./.github/actions/linux-prereq
|
||||
- id: get_commit_msg
|
||||
uses: ./.github/actions/get-commit-msg
|
||||
- name: Prerequisites
|
||||
run: pip install tifffile numpy
|
||||
- name: Run update script
|
||||
env:
|
||||
GH_TOKEN: ${{ secrets.FILAMENTBOT_TOKEN }}
|
||||
COMMIT_MESSAGE: ${{ steps.get_commit_msg.outputs.msg }}
|
||||
run: |
|
||||
GOLDEN_BRANCH=$(echo "${COMMIT_MESSAGE}" | python3 test/renderdiff/src/commit_msg.py)
|
||||
COMMIT_HASH="${{ steps.get_commit_msg.outputs.hash }}"
|
||||
if [[ "${GOLDEN_BRANCH}" != "main" ]]; then
|
||||
git config --global user.email "filament.bot@gmail.com"
|
||||
git config --global user.name "Filament Bot"
|
||||
git config --global credential.helper cache
|
||||
echo "branch==${GOLDEN_BRANCH}"
|
||||
echo "hash==${COMMIT_HASH}"
|
||||
python3 test/renderdiff/src/update_golden.py --branch=${GOLDEN_BRANCH} \
|
||||
--merge-to-main --filament-tag=${COMMIT_HASH} --golden-repo-token=${GH_TOKEN}
|
||||
fi
|
||||
|
||||
update-docs:
|
||||
name: update-docs
|
||||
runs-on: 'ubuntu-24.04-4core'
|
||||
steps:
|
||||
- uses: actions/checkout@v4.1.6
|
||||
with:
|
||||
fetch-depth: 0
|
||||
- uses: ./.github/actions/linux-prereq
|
||||
- id: get_commit_msg
|
||||
uses: ./.github/actions/get-commit-msg
|
||||
- name: Prerequisites
|
||||
run: pip install selenium
|
||||
- name: Run update script
|
||||
env:
|
||||
GH_TOKEN: ${{ secrets.FILAMENTBOT_TOKEN }}
|
||||
run: |
|
||||
bash docs_src/build/install_mdbook.sh && source ~/.bashrc
|
||||
COMMIT_HASH="${{ steps.get_commit_msg.outputs.hash }}"
|
||||
git config --global user.email "filament.bot@gmail.com"
|
||||
git config --global user.name "Filament Bot"
|
||||
git config --global credential.helper cache
|
||||
bash docs_src/build/postsubmit.sh ${COMMIT_HASH} ${GH_TOKEN}
|
||||
134
.github/workflows/postsubmit.yml
vendored
134
.github/workflows/postsubmit.yml
vendored
@@ -1,115 +1,59 @@
|
||||
name: 'Postsubmit CI'
|
||||
name: 'Post-submit tasks'
|
||||
|
||||
on:
|
||||
push:
|
||||
branches:
|
||||
- main
|
||||
- release
|
||||
- rc/**
|
||||
|
||||
jobs:
|
||||
build-android:
|
||||
name: build-android
|
||||
# We intentially use a larger runner here to enable larger disk space
|
||||
# (standard linux runner will fail on disk space and faster build time).
|
||||
runs-on: 'ubuntu-24.04-16core'
|
||||
|
||||
update-renderdiff-goldens:
|
||||
name: update-renderdiff-goldens
|
||||
runs-on: 'ubuntu-24.04-4core'
|
||||
steps:
|
||||
- uses: actions/checkout@v4.1.6
|
||||
with:
|
||||
fetch-depth: 0
|
||||
- uses: ./.github/actions/linux-prereq
|
||||
- name: Run Android Continuous
|
||||
uses: ./.github/actions/android-continuous
|
||||
with:
|
||||
build-abi: armeabi-v7a,arm64-v8a,x86_64
|
||||
|
||||
build-ios:
|
||||
name: build-ios
|
||||
runs-on: macos-14-xlarge
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v4.1.6
|
||||
with:
|
||||
fetch-depth: 0
|
||||
- uses: ./.github/actions/mac-prereq
|
||||
- name: Run build script
|
||||
- id: get_commit_msg
|
||||
uses: ./.github/actions/get-commit-msg
|
||||
- name: Prerequisites
|
||||
run: pip install tifffile numpy
|
||||
- name: Run update script
|
||||
env:
|
||||
GH_TOKEN: ${{ secrets.FILAMENTBOT_TOKEN }}
|
||||
COMMIT_MESSAGE: ${{ steps.get_commit_msg.outputs.msg }}
|
||||
run: |
|
||||
cd build/ios && printf "y" | ./build.sh continuous
|
||||
- uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: filament-ios
|
||||
path: out/filament-release-ios.tgz
|
||||
- name: Build iOS samples
|
||||
run: |
|
||||
cd build/ios && ./build-samples.sh continuous
|
||||
|
||||
build-linux:
|
||||
name: build-linux
|
||||
runs-on: 'ubuntu-24.04-16core'
|
||||
GOLDEN_BRANCH=$(echo "${COMMIT_MESSAGE}" | python3 test/renderdiff/src/commit_msg.py)
|
||||
COMMIT_HASH="${{ steps.get_commit_msg.outputs.hash }}"
|
||||
if [[ "${GOLDEN_BRANCH}" != "main" ]]; then
|
||||
git config --global user.email "filament.bot@gmail.com"
|
||||
git config --global user.name "Filament Bot"
|
||||
git config --global credential.helper cache
|
||||
echo "branch==${GOLDEN_BRANCH}"
|
||||
echo "hash==${COMMIT_HASH}"
|
||||
python3 test/renderdiff/src/update_golden.py --branch=${GOLDEN_BRANCH} \
|
||||
--merge-to-main --filament-tag=${COMMIT_HASH} --golden-repo-token=${GH_TOKEN}
|
||||
fi
|
||||
|
||||
update-docs:
|
||||
name: update-docs
|
||||
runs-on: 'ubuntu-24.04-4core'
|
||||
steps:
|
||||
- uses: actions/checkout@v4.1.6
|
||||
with:
|
||||
fetch-depth: 0
|
||||
- uses: ./.github/actions/linux-prereq
|
||||
- name: Run build script
|
||||
- id: get_commit_msg
|
||||
uses: ./.github/actions/get-commit-msg
|
||||
- name: Prerequisites
|
||||
run: pip install selenium
|
||||
- name: Run update script
|
||||
env:
|
||||
GH_TOKEN: ${{ secrets.FILAMENTBOT_TOKEN }}
|
||||
run: |
|
||||
cd build/linux && printf "y" | ./build.sh continuous
|
||||
- uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: filament-linux
|
||||
path: out/filament-release-linux.tgz
|
||||
|
||||
build-mac:
|
||||
name: build-mac
|
||||
runs-on: macos-14-xlarge
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v4.1.6
|
||||
with:
|
||||
fetch-depth: 0
|
||||
- uses: ./.github/actions/mac-prereq
|
||||
- name: Run build script
|
||||
run: |
|
||||
cd build/mac && printf "y" | ./build.sh continuous
|
||||
- uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: filament-mac
|
||||
path: out/filament-release-darwin.tgz
|
||||
- name: Check public headers
|
||||
run: |
|
||||
test/check-headers/test.sh out/release/filament/include
|
||||
|
||||
build-web:
|
||||
name: build-web
|
||||
runs-on: 'ubuntu-24.04-16core'
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v4.1.6
|
||||
with:
|
||||
fetch-depth: 0
|
||||
- uses: ./.github/actions/linux-prereq
|
||||
- uses: ./.github/actions/web-prereq
|
||||
- name: Run build script
|
||||
run: |
|
||||
cd build/web && printf "y" | ./build.sh continuous
|
||||
- uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: filament-web
|
||||
path: out/filament-release-web.tgz
|
||||
|
||||
build-windows:
|
||||
name: build-windows
|
||||
runs-on: windows-2022-32core
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v4.1.6
|
||||
- name: Run build script
|
||||
run: |
|
||||
build\windows\build-github.bat continuous
|
||||
shell: cmd
|
||||
- uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: filament-windows
|
||||
path: out/filament-windows.tgz
|
||||
bash docs_src/build/install_mdbook.sh && source ~/.bashrc
|
||||
COMMIT_HASH="${{ steps.get_commit_msg.outputs.hash }}"
|
||||
git config --global user.email "filament.bot@gmail.com"
|
||||
git config --global user.name "Filament Bot"
|
||||
git config --global credential.helper cache
|
||||
bash docs_src/build/postsubmit.sh ${COMMIT_HASH} ${GH_TOKEN}
|
||||
|
||||
4
.github/workflows/presubmit.yml
vendored
4
.github/workflows/presubmit.yml
vendored
@@ -1,4 +1,4 @@
|
||||
name: 'Presubmit CI'
|
||||
name: Presubmit
|
||||
|
||||
on:
|
||||
push:
|
||||
@@ -19,7 +19,7 @@ jobs:
|
||||
- uses: ./.github/actions/mac-prereq
|
||||
- name: Run build script
|
||||
run: |
|
||||
cd build/mac && printf "y" | ./build.sh presubmit-with-test
|
||||
cd build/mac && printf "y" | ./build.sh presubmit
|
||||
- name: Test material parser
|
||||
run: |
|
||||
out/cmake-release/filament/test/test_material_parser
|
||||
|
||||
27
.github/workflows/web-continuous.yml
vendored
Normal file
27
.github/workflows/web-continuous.yml
vendored
Normal file
@@ -0,0 +1,27 @@
|
||||
name: Web
|
||||
|
||||
on:
|
||||
push:
|
||||
branches:
|
||||
- main
|
||||
- release
|
||||
- rc/**
|
||||
|
||||
jobs:
|
||||
build-web:
|
||||
name: build-web
|
||||
runs-on: 'ubuntu-24.04-16core'
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v4.1.6
|
||||
with:
|
||||
fetch-depth: 0
|
||||
- uses: ./.github/actions/linux-prereq
|
||||
- uses: ./.github/actions/web-prereq
|
||||
- name: Run build script
|
||||
run: |
|
||||
cd build/web && printf "y" | ./build.sh continuous
|
||||
- uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: filament-web
|
||||
path: out/filament-release-web.tgz
|
||||
24
.github/workflows/windows-continuous.yml
vendored
Normal file
24
.github/workflows/windows-continuous.yml
vendored
Normal file
@@ -0,0 +1,24 @@
|
||||
name: Windows
|
||||
|
||||
on:
|
||||
push:
|
||||
branches:
|
||||
- main
|
||||
- release
|
||||
- rc/**
|
||||
|
||||
jobs:
|
||||
build-windows:
|
||||
name: build-windows
|
||||
runs-on: windows-2022-32core
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v4.1.6
|
||||
- name: Run build script
|
||||
run: |
|
||||
build\windows\build-github.bat continuous
|
||||
shell: cmd
|
||||
- uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: filament-windows
|
||||
path: out/filament-windows.tgz
|
||||
36
BUILDING.md
36
BUILDING.md
@@ -1,6 +1,6 @@
|
||||
# Building Filament
|
||||
## Building Filament
|
||||
|
||||
## Prerequisites
|
||||
### Prerequisites
|
||||
|
||||
To build Filament, you must first install the following tools:
|
||||
|
||||
@@ -18,7 +18,7 @@ To build Filament for Android you must also install the following:
|
||||
- Android NDK 25.1 or higher
|
||||
- Java 17
|
||||
|
||||
## Environment variables
|
||||
### Environment variables
|
||||
|
||||
To build Filament for Android, make sure the environment variable `ANDROID_HOME` points to the
|
||||
location of your Android SDK.
|
||||
@@ -30,7 +30,7 @@ When building for WebGL, you'll also need to set `EMSDK`. See [WebAssembly](#web
|
||||
We recommend using CLion to develop for Filament. Simply open the root directory's CMakeLists.txt
|
||||
in CLion to obtain a usable project.
|
||||
|
||||
## Easy build
|
||||
### Easy build
|
||||
|
||||
Once the required OS specific dependencies listed below are installed, you can use the script
|
||||
located in `build.sh` to build Filament easily on macOS and Linux.
|
||||
@@ -67,7 +67,7 @@ For more specialized options, please also consider the following pages:
|
||||
- `-t`: [`fgviewer`](https://google.github.io/filament/dup/fgviewer.html)
|
||||
- `-b` and `-y`: [ASAN/UBSAN builds](https://google.github.io/filament/notes/asan_ubsan.html)
|
||||
|
||||
## Filament-specific CMake Options
|
||||
### Filament-specific CMake Options
|
||||
|
||||
The following CMake options are boolean options specific to Filament:
|
||||
|
||||
@@ -89,7 +89,7 @@ cmake . -DOPTION=ON # Replace OPTION with the option name, set to ON / OFF
|
||||
|
||||
Options can also be set with the CMake GUI.
|
||||
|
||||
## Linux
|
||||
### Linux
|
||||
|
||||
Make sure you've installed the following dependencies:
|
||||
|
||||
@@ -148,7 +148,7 @@ ninja
|
||||
|
||||
This will build Filament, its tests and samples, and various host tools.
|
||||
|
||||
## macOS
|
||||
### macOS
|
||||
|
||||
To compile Filament you must have the most recent version of Xcode installed and you need to
|
||||
make sure the command line tools are setup by running:
|
||||
@@ -169,7 +169,7 @@ cmake -G Ninja -DCMAKE_BUILD_TYPE=Release -DCMAKE_INSTALL_PREFIX=../release/fila
|
||||
ninja
|
||||
```
|
||||
|
||||
## iOS
|
||||
### iOS
|
||||
|
||||
The easiest way to build Filament for iOS is to use `build.sh` and the
|
||||
`-p ios` flag. For instance to build the debug target:
|
||||
@@ -180,9 +180,9 @@ The easiest way to build Filament for iOS is to use `build.sh` and the
|
||||
|
||||
See [ios/samples/README.md](./ios/samples/README.md) for more information.
|
||||
|
||||
## Windows
|
||||
### Windows
|
||||
|
||||
### Building on Windows with Visual Studio 2019 or later
|
||||
#### Building on Windows with Visual Studio 2019 or later
|
||||
|
||||
Install the following components:
|
||||
|
||||
@@ -225,7 +225,7 @@ You can also use CMake to invoke the build without opening Visual Studio. For ex
|
||||
cmake --build . --target gltf_viewer --config Release
|
||||
```
|
||||
|
||||
## Android
|
||||
### Android
|
||||
|
||||
Before building Filament for Android, make sure to build Filament for your host. Some of the
|
||||
host tools are required to successfully build for Android.
|
||||
@@ -242,13 +242,13 @@ foremost for `arm64-v8a`.
|
||||
|
||||
To build Android on Windows machines, see [android/Windows.md](android/Windows.md).
|
||||
|
||||
### Important: SDK location
|
||||
#### Important: SDK location
|
||||
|
||||
Either ensure your `ANDROID_HOME` environment variable is set or make sure the root project
|
||||
contains a `local.properties` file with the `sdk.dir` property pointing to your installation of
|
||||
the Android SDK.
|
||||
|
||||
### Easy Android build
|
||||
#### Easy Android build
|
||||
|
||||
The easiest way to build Filament for Android is to use `build.sh` and the
|
||||
`-p android` flag. For instance to build the release target:
|
||||
@@ -266,7 +266,7 @@ The output APK can be found in `android/samples/sample-hello-triangle/build/outp
|
||||
|
||||
Run `build.sh -h` for more information.
|
||||
|
||||
### Android Studio
|
||||
#### Android Studio
|
||||
|
||||
You must use the latest stable release of Android Studio.
|
||||
|
||||
@@ -296,7 +296,7 @@ device's architecture. So if you are targeting a new Pixel phone, make sure that
|
||||
an emulator on a Linux machine with an x86 64-bit chipset, you would indicate (`-q x86_64`) in the above step.
|
||||
|
||||
|
||||
### Manual builds
|
||||
#### Manual builds
|
||||
|
||||
Invoke CMake in a build directory of your choice, inside of filament's directory. The commands
|
||||
below show how to build Filament for ARM 64-bit (`aarch64`).
|
||||
@@ -324,7 +324,7 @@ This will generate Filament's Android binaries in `out/android-release`. This lo
|
||||
to build the Android Studio projects located in `filament/android`. After install, the library
|
||||
binaries should be found in `out/android-release/filament/lib/arm64-v8a`.
|
||||
|
||||
### AAR
|
||||
#### AAR
|
||||
|
||||
Before you attempt to build the AAR, make sure you've compiled and installed the native libraries
|
||||
as explained in the sections above. You must have the following ABIs built in
|
||||
@@ -356,7 +356,7 @@ Alternatively you can build the AAR from the command line by executing the follo
|
||||
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
|
||||
#### Using Filament's AAR
|
||||
|
||||
Create a new module in your project and select _Import .JAR or .AAR Package_ when prompted. Make
|
||||
sure to add the newly created module as a dependency to your application.
|
||||
@@ -397,7 +397,7 @@ productFlavors {
|
||||
}
|
||||
```
|
||||
|
||||
## WebAssembly
|
||||
### WebAssembly
|
||||
|
||||
The core Filament library can be cross-compiled to WebAssembly from either macOS or Linux. To get
|
||||
started, follow the instructions for building Filament on your platform ([macOS](#macos) or
|
||||
|
||||
@@ -10,7 +10,6 @@ cmake_minimum_required(VERSION 3.22.1)
|
||||
# ==================================================================================================
|
||||
# Toolchain configuration
|
||||
# ==================================================================================================
|
||||
# On iOS, the deployment target is set inside third_party/clang/ios.cmake
|
||||
if (APPLE AND NOT IOS)
|
||||
# This must be set before project() is called
|
||||
set(CMAKE_OSX_DEPLOYMENT_TARGET 10.15 CACHE STRING "")
|
||||
@@ -368,22 +367,14 @@ endif()
|
||||
|
||||
if (LINUX)
|
||||
option(USE_STATIC_LIBCXX "Link against the static runtime libraries." ON)
|
||||
|
||||
# Add this step to support both glibc-based Linux distributions (e.g., Ubuntu, Debian)
|
||||
# and musl-based distributions (e.g., Alpine).
|
||||
include(CheckSymbolExists)
|
||||
check_symbol_exists(__GLIBC__ "features.h" CLANG_WITH_GLIBC)
|
||||
|
||||
if (${USE_STATIC_LIBCXX})
|
||||
if (FILAMENT_USING_GCC)
|
||||
link_libraries("-static-libgcc -static-libstdc++")
|
||||
elseif (CLANG_WITH_GLIBC)
|
||||
else ()
|
||||
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -stdlib=libc++")
|
||||
link_libraries("-static-libgcc -static-libstdc++")
|
||||
link_libraries(libc++.a)
|
||||
link_libraries(libc++abi.a)
|
||||
else()
|
||||
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -static")
|
||||
endif()
|
||||
endif()
|
||||
|
||||
@@ -587,7 +578,7 @@ if (FILAMENT_SUPPORTS_METAL)
|
||||
endif()
|
||||
|
||||
# Building filamat increases build times and isn't required for web, so turn it off by default.
|
||||
if (NOT WEBGL)
|
||||
if (NOT WEBGL AND NOT IOS)
|
||||
option(FILAMENT_BUILD_FILAMAT "Build filamat and JNI buildings" ON)
|
||||
else()
|
||||
option(FILAMENT_BUILD_FILAMAT "Build filamat and JNI buildings" OFF)
|
||||
|
||||
@@ -31,7 +31,7 @@ repositories {
|
||||
}
|
||||
|
||||
dependencies {
|
||||
implementation 'com.google.android.filament:filament-android:1.68.5'
|
||||
implementation 'com.google.android.filament:filament-android:1.68.4'
|
||||
}
|
||||
```
|
||||
|
||||
@@ -51,7 +51,7 @@ Here are all the libraries available in the group `com.google.android.filament`:
|
||||
iOS projects can use CocoaPods to install the latest release:
|
||||
|
||||
```shell
|
||||
pod 'Filament', '~> 1.68.5'
|
||||
pod 'Filament', '~> 1.68.4'
|
||||
```
|
||||
|
||||
## Documentation
|
||||
|
||||
@@ -7,15 +7,6 @@ A new header is inserted each time a *tag* is created.
|
||||
Instead, if you are authoring a PR for the main branch, add your release note to
|
||||
[NEW_RELEASE_NOTES.md](./NEW_RELEASE_NOTES.md).
|
||||
|
||||
## v1.69.0
|
||||
|
||||
- engine: Support custom attributes morphing, and allow for omitting position and/or normal data. [⚠️ **Recompile Materials**]
|
||||
|
||||
## v1.68.5
|
||||
|
||||
- engine: "native" Streams are officially deprecated. Use "acquired" streams instead.
|
||||
- engine: add "engine.skip_frame_when_cpu_ahead_of_display" feature [b/474599530]
|
||||
|
||||
## v1.68.4
|
||||
|
||||
- gltfio: Add optional support for webp textures (EXT_texture_webp), controlled via FILAMENT_SUPPORTS_WEBP_TEXTURES cmake option
|
||||
|
||||
@@ -194,7 +194,7 @@ subprojects {
|
||||
google()
|
||||
}
|
||||
|
||||
if (!name.startsWith("sample") && name != "filament-tools") {
|
||||
if (!name.startsWith("sample")) {
|
||||
apply plugin: 'com.android.library'
|
||||
|
||||
android {
|
||||
|
||||
@@ -18,7 +18,6 @@
|
||||
|
||||
#include <filament/Camera.h>
|
||||
#include <filament/Engine.h>
|
||||
#include <filament/MorphTargetBuffer.h>
|
||||
|
||||
#include <utils/Entity.h>
|
||||
#include <utils/EntityManager.h>
|
||||
@@ -208,14 +207,6 @@ Java_com_google_android_filament_Engine_nDestroySkinningBuffer(JNIEnv*, jclass,
|
||||
return engine->destroy(skinningBuffer);
|
||||
}
|
||||
|
||||
extern "C" JNIEXPORT jboolean JNICALL
|
||||
Java_com_google_android_filament_Engine_nDestroyMorphTargetBuffer(JNIEnv*, jclass,
|
||||
jlong nativeEngine, jlong nativeMorphTargetBuffer) {
|
||||
Engine* engine = (Engine*) nativeEngine;
|
||||
MorphTargetBuffer* mtb = (MorphTargetBuffer*) nativeMorphTargetBuffer;
|
||||
return engine->destroy(mtb);
|
||||
}
|
||||
|
||||
extern "C" JNIEXPORT jboolean JNICALL
|
||||
Java_com_google_android_filament_Engine_nDestroyIndirectLight(JNIEnv*, jclass,
|
||||
jlong nativeEngine, jlong nativeIndirectLight) {
|
||||
@@ -337,13 +328,6 @@ Java_com_google_android_filament_Engine_nIsValidSkinningBuffer(JNIEnv*, jclass,
|
||||
return (jboolean)engine->isValid((SkinningBuffer*)nativeSkinningBuffer);
|
||||
}
|
||||
|
||||
extern "C" JNIEXPORT jboolean JNICALL
|
||||
Java_com_google_android_filament_Engine_nIsValidMorphTargetBuffer(JNIEnv*, jclass,
|
||||
jlong nativeEngine, jlong nativeMorphTargetBuffer) {
|
||||
Engine* engine = (Engine*) nativeEngine;
|
||||
return (jboolean) engine->isValid((MorphTargetBuffer*) nativeMorphTargetBuffer);
|
||||
}
|
||||
|
||||
extern "C" JNIEXPORT jboolean JNICALL
|
||||
Java_com_google_android_filament_Engine_nIsValidIndirectLight(JNIEnv*, jclass,
|
||||
jlong nativeEngine, jlong nativeIndirectLight) {
|
||||
|
||||
@@ -58,27 +58,6 @@ Java_com_google_android_filament_MorphTargetBuffer_nBuilderCount(JNIEnv*, jclass
|
||||
builder->count((size_t) count);
|
||||
}
|
||||
|
||||
extern "C" JNIEXPORT void JNICALL
|
||||
Java_com_google_android_filament_MorphTargetBuffer_nBuilderWithPositions(JNIEnv*, jclass,
|
||||
jlong nativeBuilder, jboolean enabled) {
|
||||
MorphTargetBuffer::Builder* builder = (MorphTargetBuffer::Builder*) nativeBuilder;
|
||||
builder->withPositions(enabled);
|
||||
}
|
||||
|
||||
extern "C" JNIEXPORT void JNICALL
|
||||
Java_com_google_android_filament_MorphTargetBuffer_nBuilderWithTangents(JNIEnv*, jclass,
|
||||
jlong nativeBuilder, jboolean enabled) {
|
||||
MorphTargetBuffer::Builder* builder = (MorphTargetBuffer::Builder*) nativeBuilder;
|
||||
builder->withTangents(enabled);
|
||||
}
|
||||
|
||||
extern "C" JNIEXPORT void JNICALL
|
||||
Java_com_google_android_filament_MorphTargetBuffer_nBuilderEnableCustomMorphing(JNIEnv*,
|
||||
jclass, jlong nativeBuilder, jboolean enabled) {
|
||||
MorphTargetBuffer::Builder* builder = (MorphTargetBuffer::Builder*) nativeBuilder;
|
||||
builder->enableCustomMorphing(enabled);
|
||||
}
|
||||
|
||||
extern "C"
|
||||
JNIEXPORT jlong JNICALL
|
||||
Java_com_google_android_filament_MorphTargetBuffer_nBuilderBuild(JNIEnv*, jclass,
|
||||
@@ -133,24 +112,3 @@ Java_com_google_android_filament_MorphTargetBuffer_nGetCount(JNIEnv*, jclass,
|
||||
MorphTargetBuffer *morphTargetBuffer = (MorphTargetBuffer *) nativeObject;
|
||||
return (jint)morphTargetBuffer->getCount();
|
||||
}
|
||||
|
||||
extern "C" JNIEXPORT jboolean JNICALL
|
||||
Java_com_google_android_filament_MorphTargetBuffer_nHasPositions(JNIEnv*, jclass,
|
||||
jlong nativeObject) {
|
||||
MorphTargetBuffer* morphTargetBuffer = (MorphTargetBuffer*) nativeObject;
|
||||
return (jboolean) morphTargetBuffer->hasPositions();
|
||||
}
|
||||
|
||||
extern "C" JNIEXPORT jboolean JNICALL
|
||||
Java_com_google_android_filament_MorphTargetBuffer_nHasTangents(JNIEnv*, jclass,
|
||||
jlong nativeObject) {
|
||||
MorphTargetBuffer* morphTargetBuffer = (MorphTargetBuffer*) nativeObject;
|
||||
return (jboolean) morphTargetBuffer->hasTangents();
|
||||
}
|
||||
|
||||
extern "C" JNIEXPORT jboolean JNICALL
|
||||
Java_com_google_android_filament_MorphTargetBuffer_nIsCustomMorphingEnabled(JNIEnv*, jclass,
|
||||
jlong nativeObject) {
|
||||
MorphTargetBuffer* morphTargetBuffer = (MorphTargetBuffer*) nativeObject;
|
||||
return (jboolean) morphTargetBuffer->isCustomMorphingEnabled();
|
||||
}
|
||||
|
||||
@@ -52,11 +52,6 @@ Java_com_google_android_filament_ToneMapper_nCreatePBRNeutralToneMapper(JNIEnv*,
|
||||
return (jlong) new PBRNeutralToneMapper();
|
||||
}
|
||||
|
||||
extern "C" JNIEXPORT jlong JNICALL
|
||||
Java_com_google_android_filament_ToneMapper_nCreateGT7ToneMapper(JNIEnv*, jclass) {
|
||||
return (jlong) new GT7ToneMapper();
|
||||
}
|
||||
|
||||
extern "C" JNIEXPORT jlong JNICALL
|
||||
Java_com_google_android_filament_ToneMapper_nCreateAgxToneMapper(JNIEnv*, jclass, jint look) {
|
||||
return (jlong) new AgxToneMapper(AgxToneMapper::AgxLook(look));
|
||||
|
||||
@@ -939,15 +939,6 @@ public class Engine {
|
||||
return nIsValidSkinningBuffer(getNativeObject(), object.getNativeObject());
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns whether the object is valid.
|
||||
* @param object Object to check for validity
|
||||
* @return returns true if the specified object is valid.
|
||||
*/
|
||||
public boolean isValidMorphTargetBuffer(@NonNull MorphTargetBuffer object) {
|
||||
return nIsValidMorphTargetBuffer(getNativeObject(), object.getNativeObject());
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns whether the object is valid.
|
||||
* @param object Object to check for validity
|
||||
@@ -1201,15 +1192,6 @@ public class Engine {
|
||||
skinningBuffer.clearNativeObject();
|
||||
}
|
||||
|
||||
/**
|
||||
* Destroys a {@link MorphTargetBuffer} and frees all its associated resources.
|
||||
* @param morphTargetBuffer the {@link MorphTargetBuffer} to destroy
|
||||
*/
|
||||
public void destroyMorphTargetBuffer(@NonNull MorphTargetBuffer morphTargetBuffer) {
|
||||
assertDestroy(nDestroyMorphTargetBuffer(getNativeObject(), morphTargetBuffer.getNativeObject()));
|
||||
morphTargetBuffer.clearNativeObject();
|
||||
}
|
||||
|
||||
/**
|
||||
* Destroys a {@link IndirectLight} and frees all its associated resources.
|
||||
* @param ibl the {@link IndirectLight} to destroy
|
||||
@@ -1501,7 +1483,6 @@ public class Engine {
|
||||
private static native boolean nDestroyIndexBuffer(long nativeEngine, long nativeIndexBuffer);
|
||||
private static native boolean nDestroyVertexBuffer(long nativeEngine, long nativeVertexBuffer);
|
||||
private static native boolean nDestroySkinningBuffer(long nativeEngine, long nativeSkinningBuffer);
|
||||
private static native boolean nDestroyMorphTargetBuffer(long nativeEngine, long nativeMorphTargetBuffer);
|
||||
private static native boolean nDestroyIndirectLight(long nativeEngine, long nativeIndirectLight);
|
||||
private static native boolean nDestroyMaterial(long nativeEngine, long nativeMaterial);
|
||||
private static native boolean nDestroyMaterialInstance(long nativeEngine, long nativeMaterialInstance);
|
||||
@@ -1518,7 +1499,6 @@ public class Engine {
|
||||
private static native boolean nIsValidIndexBuffer(long nativeEngine, long nativeIndexBuffer);
|
||||
private static native boolean nIsValidVertexBuffer(long nativeEngine, long nativeVertexBuffer);
|
||||
private static native boolean nIsValidSkinningBuffer(long nativeEngine, long nativeSkinningBuffer);
|
||||
private static native boolean nIsValidMorphTargetBuffer(long nativeEngine, long nativeMorphTargetBuffer);
|
||||
private static native boolean nIsValidIndirectLight(long nativeEngine, long nativeIndirectLight);
|
||||
private static native boolean nIsValidMaterial(long nativeEngine, long nativeMaterial);
|
||||
private static native boolean nIsValidMaterialInstance(long nativeEngine, long nativeMaterial, long nativeMaterialInstance);
|
||||
|
||||
@@ -64,45 +64,6 @@ public class MorphTargetBuffer {
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Use this method to enable or disable the built-in position morphing buffer.
|
||||
* Default is true.
|
||||
*
|
||||
* @param enabled true to enable, false to disable
|
||||
* @return this <code>Builder</code> object for chaining calls
|
||||
*/
|
||||
@NonNull
|
||||
public Builder withPositions(boolean enabled) {
|
||||
nBuilderWithPositions(mNativeBuilder, enabled);
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Use this method to enable or disable the built-in tangent morphing buffer.
|
||||
* Default is true.
|
||||
*
|
||||
* @param enabled true to enable, false to disable
|
||||
* @return this <code>Builder</code> object for chaining calls
|
||||
*/
|
||||
@NonNull
|
||||
public Builder withTangents(boolean enabled) {
|
||||
nBuilderWithTangents(mNativeBuilder, enabled);
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Use this method to enable or disable custom morphing.
|
||||
* Default is false.
|
||||
*
|
||||
* @param enabled true to enable, false to disable
|
||||
* @return this <code>Builder</code> object for chaining calls
|
||||
*/
|
||||
@NonNull
|
||||
public Builder enableCustomMorphing(boolean enabled) {
|
||||
nBuilderEnableCustomMorphing(mNativeBuilder, enabled);
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates and returns the <code>MorphTargetBuffer</code> object.
|
||||
*
|
||||
@@ -195,27 +156,6 @@ public class MorphTargetBuffer {
|
||||
return nGetCount(mNativeObject);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return true if this MorphTargetBuffer has a position buffer.
|
||||
*/
|
||||
public boolean hasPositions() {
|
||||
return nHasPositions(mNativeObject);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return true if this MorphTargetBuffer has a tangent buffer.
|
||||
*/
|
||||
public boolean hasTangents() {
|
||||
return nHasTangents(mNativeObject);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return true if custom morphing is enabled.
|
||||
*/
|
||||
public boolean isCustomMorphingEnabled() {
|
||||
return nIsCustomMorphingEnabled(mNativeObject);
|
||||
}
|
||||
|
||||
public long getNativeObject() {
|
||||
if (mNativeObject == 0) {
|
||||
throw new IllegalStateException("Calling method on destroyed MorphTargetBuffer");
|
||||
@@ -231,16 +171,10 @@ public class MorphTargetBuffer {
|
||||
private static native void nDestroyBuilder(long nativeBuilder);
|
||||
private static native void nBuilderVertexCount(long nativeBuilder, int vertexCount);
|
||||
private static native void nBuilderCount(long nativeBuilder, int count);
|
||||
private static native void nBuilderWithPositions(long nativeBuilder, boolean enabled);
|
||||
private static native void nBuilderWithTangents(long nativeBuilder, boolean enabled);
|
||||
private static native void nBuilderEnableCustomMorphing(long nativeBuilder, boolean enabled);
|
||||
private static native long nBuilderBuild(long nativeBuilder, long nativeEngine);
|
||||
|
||||
private static native int nSetPositionsAt(long nativeObject, long nativeEngine, int targetIndex, float[] positions, int count);
|
||||
private static native int nSetTangentsAt(long nativeObject, long nativeEngine, int targetIndex, short[] tangents, int count);
|
||||
private static native int nGetVertexCount(long nativeObject);
|
||||
private static native int nGetCount(long nativeObject);
|
||||
private static native boolean nHasPositions(long nativeObject);
|
||||
private static native boolean nHasTangents(long nativeObject);
|
||||
private static native boolean nIsCustomMorphingEnabled(long nativeObject);
|
||||
}
|
||||
|
||||
@@ -24,7 +24,6 @@ package com.google.android.filament;
|
||||
* <li>ACESLegacyToneMapper</li>
|
||||
* <li>FilmicToneMapper</li>
|
||||
* <li>PBRNeutralToneMapper</li>
|
||||
* <li>GT7ToneMapper</li>
|
||||
* </ul>
|
||||
* <li>Debug/validation tone mapping operators</li>
|
||||
* <ul>
|
||||
@@ -112,19 +111,6 @@ public class ToneMapper {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Gran Turismo 7 tone mapping operator. This tone mapper was designed
|
||||
* to preserve the appearance of materials across lighting conditions while
|
||||
* avoiding artifacts in the highlights in high dynamic range conditions.
|
||||
* This tone mapper targets an SDR paper white value of 250 nits, with a
|
||||
* reference luminance of 100 cd/m^2 (a value of 1.0 in the HDR framebuffer).
|
||||
*/
|
||||
public static class GT7ToneMapper extends ToneMapper {
|
||||
public GT7ToneMapper() {
|
||||
super(nCreateGT7ToneMapper());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* AgX tone mapping operator.
|
||||
*/
|
||||
@@ -258,7 +244,6 @@ public class ToneMapper {
|
||||
private static native long nCreateACESLegacyToneMapper();
|
||||
private static native long nCreateFilmicToneMapper();
|
||||
private static native long nCreatePBRNeutralToneMapper();
|
||||
private static native long nCreateGT7ToneMapper();
|
||||
private static native long nCreateAgxToneMapper(int look);
|
||||
private static native long nCreateGenericToneMapper(
|
||||
float contrast, float midGrayIn, float midGrayOut, float hdrMax);
|
||||
|
||||
12
android/filament-tools/.gitignore
vendored
12
android/filament-tools/.gitignore
vendored
@@ -1,12 +0,0 @@
|
||||
*.iml
|
||||
.gradle
|
||||
/local.properties
|
||||
/.idea/workspace.xml
|
||||
/.idea/libraries
|
||||
/.idea/caches
|
||||
/.idea/gradle.xml
|
||||
.DS_Store
|
||||
/build
|
||||
/captures
|
||||
.externalNativeBuild
|
||||
/.cxx
|
||||
@@ -1,64 +0,0 @@
|
||||
plugins {
|
||||
id "de.undercouch.download" version "5.6.0"
|
||||
}
|
||||
|
||||
apply from: rootProject.file('gradle/gradle-mvn-push.gradle')
|
||||
|
||||
def tools = ['matc', 'cmgen']
|
||||
|
||||
def platforms = [
|
||||
'mac': [classifier: 'osx-aarch64', archive: "filament-v${VERSION_NAME}-mac.tgz", path: { t -> "filament/bin/${t}" }],
|
||||
'linux': [classifier: 'linux-x86_64', archive: "filament-v${VERSION_NAME}-linux.tgz", path: { t -> "filament/bin/${t}" }],
|
||||
'windows': [classifier: 'windows-x86_64', archive: "filament-v${VERSION_NAME}-windows.tgz", path: { t -> "bin/${t}.exe" }]
|
||||
]
|
||||
|
||||
platforms.each { platform, config ->
|
||||
def platformName = platform.capitalize()
|
||||
def remoteUrl = "https://github.com/google/filament/releases/download/v${VERSION_NAME}/${config.archive}"
|
||||
def downloadFile = file("${buildDir}/downloads/${config.archive}")
|
||||
def extractDir = file("${buildDir}/extracted/filament-v${VERSION_NAME}-${platform}")
|
||||
|
||||
task "downloadRelease${platformName}"(type: Download) {
|
||||
src remoteUrl
|
||||
dest downloadFile
|
||||
overwrite false
|
||||
}
|
||||
|
||||
def extractionTask = task "extractTools${platformName}"(dependsOn: "downloadRelease${platformName}", type: Copy) {
|
||||
group = "setup"
|
||||
from tarTree(resources.gzip(downloadFile))
|
||||
|
||||
// Include specific tools based on platform pattern
|
||||
include tools.collect { tool -> config.path(tool) }
|
||||
|
||||
// Flatten the path so it lands directly in 'into'
|
||||
eachFile { fcd ->
|
||||
fcd.relativePath = new RelativePath(true, fcd.name)
|
||||
}
|
||||
|
||||
into extractDir
|
||||
includeEmptyDirs = false
|
||||
}
|
||||
}
|
||||
|
||||
publishing {
|
||||
publications {
|
||||
tools.each { toolName ->
|
||||
create(toolName, MavenPublication) {
|
||||
artifactId = toolName
|
||||
|
||||
platforms.each { platform, config ->
|
||||
def extractDir = file("${buildDir}/extracted/filament-v${VERSION_NAME}-${platform}")
|
||||
def archivePath = config.path(toolName)
|
||||
def exeName = new File(archivePath).name
|
||||
|
||||
artifact(new File(extractDir, exeName)) {
|
||||
classifier = config.classifier
|
||||
extension = "exe"
|
||||
builtBy tasks.named("extractTools${platform.capitalize()}")
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,2 +0,0 @@
|
||||
POM_NAME=Filament
|
||||
POM_PACKAGING=exe
|
||||
@@ -1,5 +1,5 @@
|
||||
GROUP=com.google.android.filament
|
||||
VERSION_NAME=1.68.5
|
||||
VERSION_NAME=1.68.4
|
||||
|
||||
POM_DESCRIPTION=Real-time physically based rendering engine for Android.
|
||||
|
||||
|
||||
@@ -3,7 +3,6 @@ include ':filament-android'
|
||||
include ':filamat-android'
|
||||
include ':gltfio-android'
|
||||
include ':filament-utils-android'
|
||||
include ':filament-tools'
|
||||
|
||||
// Samples
|
||||
include ':samples:sample-gltf-viewer'
|
||||
|
||||
@@ -34,11 +34,6 @@ if [[ "$TARGET" == "presubmit" ]]; then
|
||||
BUILD_RELEASE=release
|
||||
fi
|
||||
|
||||
if [[ "$TARGET" == "presubmit-with-test" ]]; then
|
||||
BUILD_RELEASE=release
|
||||
RUN_TESTS=-u
|
||||
fi
|
||||
|
||||
if [[ "$TARGET" == "debug" ]]; then
|
||||
BUILD_DEBUG=debug
|
||||
GENERATE_ARCHIVES=-a
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
<head>
|
||||
<!-- Book generated using mdBook -->
|
||||
<meta charset="UTF-8">
|
||||
<title>Maven - Filament</title>
|
||||
<title>Maven Release - Filament</title>
|
||||
|
||||
|
||||
<!-- Custom HTML head -->
|
||||
@@ -218,11 +218,11 @@ the <strong>Publish</strong> button to publish the artifacts. It typically takes
|
||||
|
||||
<nav class="nav-wrapper" aria-label="Page navigation">
|
||||
<!-- Mobile navigation buttons -->
|
||||
<a rel="prev" href="../release/cocoapods.html" class="mobile-nav-chapters previous" title="Previous chapter" aria-label="Previous chapter" aria-keyshortcuts="Left">
|
||||
<a rel="prev" href="../build/windows_android.html" class="mobile-nav-chapters previous" title="Previous chapter" aria-label="Previous chapter" aria-keyshortcuts="Left">
|
||||
<i class="fa fa-angle-left"></i>
|
||||
</a>
|
||||
|
||||
<a rel="next prefetch" href="../release/guide.html" class="mobile-nav-chapters next" title="Next chapter" aria-label="Next chapter" aria-keyshortcuts="Right">
|
||||
<a rel="next prefetch" href="../dup/contributing.html" class="mobile-nav-chapters next" title="Next chapter" aria-label="Next chapter" aria-keyshortcuts="Right">
|
||||
<i class="fa fa-angle-right"></i>
|
||||
</a>
|
||||
|
||||
@@ -232,11 +232,11 @@ the <strong>Publish</strong> button to publish the artifacts. It typically takes
|
||||
</div>
|
||||
|
||||
<nav class="nav-wide-wrapper" aria-label="Page navigation">
|
||||
<a rel="prev" href="../release/cocoapods.html" class="nav-chapters previous" title="Previous chapter" aria-label="Previous chapter" aria-keyshortcuts="Left">
|
||||
<a rel="prev" href="../build/windows_android.html" class="nav-chapters previous" title="Previous chapter" aria-label="Previous chapter" aria-keyshortcuts="Left">
|
||||
<i class="fa fa-angle-left"></i>
|
||||
</a>
|
||||
|
||||
<a rel="next prefetch" href="../release/guide.html" class="nav-chapters next" title="Next chapter" aria-label="Next chapter" aria-keyshortcuts="Right">
|
||||
<a rel="next prefetch" href="../dup/contributing.html" class="nav-chapters next" title="Next chapter" aria-label="Next chapter" aria-keyshortcuts="Right">
|
||||
<i class="fa fa-angle-right"></i>
|
||||
</a>
|
||||
</nav>
|
||||
6
docs/build/windows_android.html
vendored
6
docs/build/windows_android.html
vendored
@@ -3,7 +3,7 @@
|
||||
<head>
|
||||
<!-- Book generated using mdBook -->
|
||||
<meta charset="UTF-8">
|
||||
<title>Android on Windows - Filament</title>
|
||||
<title>Build for Android on Windows - Filament</title>
|
||||
|
||||
|
||||
<!-- Custom HTML head -->
|
||||
@@ -273,7 +273,7 @@ copy filament-android\build\outputs\aar\filament-android-release.aar ..\..\out\
|
||||
<i class="fa fa-angle-left"></i>
|
||||
</a>
|
||||
|
||||
<a rel="next prefetch" href="../dup/contributing.html" class="mobile-nav-chapters next" title="Next chapter" aria-label="Next chapter" aria-keyshortcuts="Right">
|
||||
<a rel="next prefetch" href="../build/maven_release.html" class="mobile-nav-chapters next" title="Next chapter" aria-label="Next chapter" aria-keyshortcuts="Right">
|
||||
<i class="fa fa-angle-right"></i>
|
||||
</a>
|
||||
|
||||
@@ -287,7 +287,7 @@ copy filament-android\build\outputs\aar\filament-android-release.aar ..\..\out\
|
||||
<i class="fa fa-angle-left"></i>
|
||||
</a>
|
||||
|
||||
<a rel="next prefetch" href="../dup/contributing.html" class="nav-chapters next" title="Next chapter" aria-label="Next chapter" aria-keyshortcuts="Right">
|
||||
<a rel="next prefetch" href="../build/maven_release.html" class="nav-chapters next" title="Next chapter" aria-label="Next chapter" aria-keyshortcuts="Right">
|
||||
<i class="fa fa-angle-right"></i>
|
||||
</a>
|
||||
</nav>
|
||||
|
||||
@@ -1,255 +0,0 @@
|
||||
<!DOCTYPE HTML>
|
||||
<html lang="en" class="light sidebar-visible" dir="ltr">
|
||||
<head>
|
||||
<!-- Book generated using mdBook -->
|
||||
<meta charset="UTF-8">
|
||||
<title>backend - Filament</title>
|
||||
|
||||
|
||||
<!-- Custom HTML head -->
|
||||
|
||||
<meta name="description" content="">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||
<meta name="theme-color" content="#ffffff">
|
||||
|
||||
<link rel="shortcut icon" href="../favicon.png">
|
||||
<link rel="stylesheet" href="../css/variables.css">
|
||||
<link rel="stylesheet" href="../css/general.css">
|
||||
<link rel="stylesheet" href="../css/chrome.css">
|
||||
|
||||
<!-- Fonts -->
|
||||
<link rel="stylesheet" href="../FontAwesome/css/font-awesome.css">
|
||||
<link rel="stylesheet" href="../fonts/fonts.css">
|
||||
|
||||
<!-- Highlight.js Stylesheets -->
|
||||
<link rel="stylesheet" href="../highlight.css">
|
||||
<link rel="stylesheet" href="../tomorrow-night.css">
|
||||
<link rel="stylesheet" href="../ayu-highlight.css">
|
||||
|
||||
<!-- Custom theme stylesheets -->
|
||||
|
||||
<!-- MathJax -->
|
||||
<script async src="https://cdnjs.cloudflare.com/ajax/libs/mathjax/2.7.1/MathJax.js?config=TeX-AMS-MML_HTMLorMML"></script>
|
||||
|
||||
<!-- Provide site root to javascript -->
|
||||
<script>
|
||||
var path_to_root = "../";
|
||||
var default_theme = window.matchMedia("(prefers-color-scheme: dark)").matches ? "light" : "light";
|
||||
</script>
|
||||
<!-- Start loading toc.js asap -->
|
||||
<script src="../toc.js"></script>
|
||||
</head>
|
||||
<body>
|
||||
<div id="body-container">
|
||||
<!-- Work around some values being stored in localStorage wrapped in quotes -->
|
||||
<script>
|
||||
try {
|
||||
var theme = localStorage.getItem('mdbook-theme');
|
||||
var sidebar = localStorage.getItem('mdbook-sidebar');
|
||||
|
||||
if (theme.startsWith('"') && theme.endsWith('"')) {
|
||||
localStorage.setItem('mdbook-theme', theme.slice(1, theme.length - 1));
|
||||
}
|
||||
|
||||
if (sidebar.startsWith('"') && sidebar.endsWith('"')) {
|
||||
localStorage.setItem('mdbook-sidebar', sidebar.slice(1, sidebar.length - 1));
|
||||
}
|
||||
} catch (e) { }
|
||||
</script>
|
||||
|
||||
<!-- Set the theme before any content is loaded, prevents flash -->
|
||||
<script>
|
||||
var theme;
|
||||
try { theme = localStorage.getItem('mdbook-theme'); } catch(e) { }
|
||||
if (theme === null || theme === undefined) { theme = default_theme; }
|
||||
const html = document.documentElement;
|
||||
html.classList.remove('light')
|
||||
html.classList.add(theme);
|
||||
html.classList.add("js");
|
||||
</script>
|
||||
|
||||
<input type="checkbox" id="sidebar-toggle-anchor" class="hidden">
|
||||
|
||||
<!-- Hide / unhide sidebar before it is displayed -->
|
||||
<script>
|
||||
var sidebar = null;
|
||||
var sidebar_toggle = document.getElementById("sidebar-toggle-anchor");
|
||||
if (document.body.clientWidth >= 1080) {
|
||||
try { sidebar = localStorage.getItem('mdbook-sidebar'); } catch(e) { }
|
||||
sidebar = sidebar || 'visible';
|
||||
} else {
|
||||
sidebar = 'hidden';
|
||||
}
|
||||
sidebar_toggle.checked = sidebar === 'visible';
|
||||
html.classList.remove('sidebar-visible');
|
||||
html.classList.add("sidebar-" + sidebar);
|
||||
</script>
|
||||
|
||||
<nav id="sidebar" class="sidebar" aria-label="Table of contents">
|
||||
<div style="display:flex;align-items:center;justify-content:center">
|
||||
<img class="flogo" src="../images/filament_logo_small.png"></img>
|
||||
</div>
|
||||
<!-- populated by js -->
|
||||
<mdbook-sidebar-scrollbox class="sidebar-scrollbox"></mdbook-sidebar-scrollbox>
|
||||
<noscript>
|
||||
<iframe class="sidebar-iframe-outer" src="../toc.html"></iframe>
|
||||
</noscript>
|
||||
<div id="sidebar-resize-handle" class="sidebar-resize-handle">
|
||||
<div class="sidebar-resize-indicator"></div>
|
||||
</div>
|
||||
</nav>
|
||||
|
||||
<div id="page-wrapper" class="page-wrapper">
|
||||
|
||||
<div class="page">
|
||||
<div id="menu-bar-hover-placeholder"></div>
|
||||
<div id="menu-bar" class="menu-bar sticky">
|
||||
<div class="left-buttons">
|
||||
<label id="sidebar-toggle" class="icon-button" for="sidebar-toggle-anchor" title="Toggle Table of Contents" aria-label="Toggle Table of Contents" aria-controls="sidebar">
|
||||
<i class="fa fa-bars"></i>
|
||||
</label>
|
||||
<!-- Filament: disable themes because the markdeep part does not look good for dark themes -->
|
||||
<!--
|
||||
<button id="theme-toggle" class="icon-button" type="button" title="Change theme" aria-label="Change theme" aria-haspopup="true" aria-expanded="false" aria-controls="theme-list">
|
||||
<i class="fa fa-paint-brush"></i>
|
||||
</button>
|
||||
<ul id="theme-list" class="theme-popup" aria-label="Themes" role="menu">
|
||||
<li role="none"><button role="menuitem" class="theme" id="light">Light</button></li>
|
||||
<li role="none"><button role="menuitem" class="theme" id="rust">Rust</button></li>
|
||||
<li role="none"><button role="menuitem" class="theme" id="coal">Coal</button></li>
|
||||
<li role="none"><button role="menuitem" class="theme" id="navy">Navy</button></li>
|
||||
<li role="none"><button role="menuitem" class="theme" id="ayu">Ayu</button></li>
|
||||
</ul>
|
||||
-->
|
||||
<button id="search-toggle" class="icon-button" type="button" title="Search. (Shortkey: s)" aria-label="Toggle Searchbar" aria-expanded="false" aria-keyshortcuts="S" aria-controls="searchbar">
|
||||
<i class="fa fa-search"></i>
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<h1 class="menu-title">Filament</h1>
|
||||
|
||||
<div class="right-buttons">
|
||||
<a href="https://github.com/google/filament" title="Git repository" aria-label="Git repository">
|
||||
<i id="git-repository-button" class="fa fa-github"></i>
|
||||
</a>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div id="search-wrapper" class="hidden">
|
||||
<form id="searchbar-outer" class="searchbar-outer">
|
||||
<input type="search" id="searchbar" name="searchbar" placeholder="Search this book ..." aria-controls="searchresults-outer" aria-describedby="searchresults-header">
|
||||
</form>
|
||||
<div id="searchresults-outer" class="searchresults-outer hidden">
|
||||
<div id="searchresults-header" class="searchresults-header"></div>
|
||||
<ul id="searchresults">
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Apply ARIA attributes after the sidebar and the sidebar toggle button are added to the DOM -->
|
||||
<script>
|
||||
document.getElementById('sidebar-toggle').setAttribute('aria-expanded', sidebar === 'visible');
|
||||
document.getElementById('sidebar').setAttribute('aria-hidden', sidebar !== 'visible');
|
||||
Array.from(document.querySelectorAll('#sidebar a')).forEach(function(link) {
|
||||
link.setAttribute('tabIndex', sidebar === 'visible' ? 0 : -1);
|
||||
});
|
||||
</script>
|
||||
|
||||
<div id="content" class="content">
|
||||
<main>
|
||||
<h1 id="backend-unit-tests"><a class="header" href="#backend-unit-tests">Backend Unit Tests</a></h1>
|
||||
<p>These are tests that ensure the Filament backend is working properly on various operating systems
|
||||
and graphics backends.</p>
|
||||
<p>The majority of these tests generate images that are then compared against a known golden image.</p>
|
||||
<h2 id="running-with-a-specific-graphics-library"><a class="header" href="#running-with-a-specific-graphics-library">Running with a specific graphics library</a></h2>
|
||||
<p>Run with <code>-a<backend></code> to run with a specific backend, such as <code>-avulkan</code> for vulkan or <code>-ametal</code>
|
||||
for metal.</p>
|
||||
<h2 id="image-comparisons"><a class="header" href="#image-comparisons">Image comparisons</a></h2>
|
||||
<p>The expected images are stored as PNG files in the <code>expected_images</code> subdirectory of the test source
|
||||
code. When cmake is run it will copy this directory to the build output creating
|
||||
<code>images/expected_images</code> inside the same directory as the unit test binary.</p>
|
||||
<p>The unit tests will then write their resulting images into <code>images/actual_images</code> in order to make
|
||||
the tests easier to debug.</p>
|
||||
<p>If an image comparison test fails, it writes a diff image to <code>images/diff_images</code> where each pixel of the diff is white (255,255,255) if the comparison for the corresponding pixel succeeds and black (0,0,0) of the comparison for the corresponding pixel fails.</p>
|
||||
<h3 id="python-utility-for-updating-golden-images-and-comparing-results"><a class="header" href="#python-utility-for-updating-golden-images-and-comparing-results">Python utility for updating golden images and comparing results</a></h3>
|
||||
<p>Inside the unit test source code directory there is a python script called
|
||||
<code>move_actual_images_to_expected.py</code>.
|
||||
It will display the actual and expected images side-by-side and copy the actual image into the
|
||||
source tree's <code>expected_images</code> directory if the user approves the change.</p>
|
||||
<h4 id="directories"><a class="header" href="#directories">Directories</a></h4>
|
||||
<p>The <code>-r</code> flag is required and should be the directory where the test binary is.</p>
|
||||
<p>If not running the script from the directory it's in, <code>-s</code> should be the source code's
|
||||
<code>expected_images</code> directory.</p>
|
||||
<h4 id="configuring-comparemove-behavior"><a class="header" href="#configuring-comparemove-behavior">Configuring compare/move behavior</a></h4>
|
||||
<p>The <code>-c</code> flag has the tool display the actual and expected images side-by-side. By default it
|
||||
does this by opening both with the OS's default behavior. <code>-p</code> can be used to specify a different
|
||||
terminal program.</p>
|
||||
<p>The <code>-m</code> flag has the tool move the actual image to overwrite the expected image in the source
|
||||
tree. If the images are also being compared then the move will only happen if the user approves the
|
||||
change.</p>
|
||||
<p>After updating the expected images, cmake will need to be run again to copy them to the binary's
|
||||
directory. Also, currently some platforms can't load images to compare and so have the hash
|
||||
hardcoded into the test source code, so for now those need to be updated by running the test and
|
||||
copying the value manually.</p>
|
||||
<h4 id="picking-which-tests-to-compare"><a class="header" href="#picking-which-tests-to-compare">Picking which tests to compare</a></h4>
|
||||
<p>The <code>-x</code> flag causes the tool to check the <code>test_detail.xml</code> file and only process the images who
|
||||
failed a comparison. <code>--gtest_output=xml</code> needs to be passed to the test binary to generate this
|
||||
file.</p>
|
||||
<p>The <code>-t</code> argument takes a list of images to compare, provided as the file name without the <code>.png</code>
|
||||
suffix.</p>
|
||||
<h2 id="unsupported-tests"><a class="header" href="#unsupported-tests">Unsupported tests</a></h2>
|
||||
<p>If a test depends on a feature that is unsupported by the current environment, it will start with
|
||||
the <code>SKIP_IF</code> macro. This will cause the test to not be run.</p>
|
||||
|
||||
</main>
|
||||
|
||||
<nav class="nav-wrapper" aria-label="Page navigation">
|
||||
<!-- Mobile navigation buttons -->
|
||||
<a rel="prev" href="../notes/tests.html" class="mobile-nav-chapters previous" title="Previous chapter" aria-label="Previous chapter" aria-keyshortcuts="Left">
|
||||
<i class="fa fa-angle-left"></i>
|
||||
</a>
|
||||
|
||||
<a rel="next prefetch" href="../dup/test_ci_backend.html" class="mobile-nav-chapters next" title="Next chapter" aria-label="Next chapter" aria-keyshortcuts="Right">
|
||||
<i class="fa fa-angle-right"></i>
|
||||
</a>
|
||||
|
||||
<div style="clear: both"></div>
|
||||
</nav>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<nav class="nav-wide-wrapper" aria-label="Page navigation">
|
||||
<a rel="prev" href="../notes/tests.html" class="nav-chapters previous" title="Previous chapter" aria-label="Previous chapter" aria-keyshortcuts="Left">
|
||||
<i class="fa fa-angle-left"></i>
|
||||
</a>
|
||||
|
||||
<a rel="next prefetch" href="../dup/test_ci_backend.html" class="nav-chapters next" title="Next chapter" aria-label="Next chapter" aria-keyshortcuts="Right">
|
||||
<i class="fa fa-angle-right"></i>
|
||||
</a>
|
||||
</nav>
|
||||
|
||||
</div>
|
||||
|
||||
|
||||
|
||||
|
||||
<script>
|
||||
window.playground_copyable = true;
|
||||
</script>
|
||||
|
||||
|
||||
<script src="../elasticlunr.min.js"></script>
|
||||
<script src="../mark.min.js"></script>
|
||||
<script src="../searcher.js"></script>
|
||||
|
||||
<script src="../clipboard.min.js"></script>
|
||||
<script src="../highlight.js"></script>
|
||||
<script src="../book.js"></script>
|
||||
|
||||
<!-- Custom JS scripts -->
|
||||
|
||||
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
||||
@@ -158,8 +158,7 @@
|
||||
|
||||
<div id="content" class="content">
|
||||
<main>
|
||||
<h1 id="bluevk"><a class="header" href="#bluevk">bluevk</a></h1>
|
||||
<h2 id="updating-vulkan-headers"><a class="header" href="#updating-vulkan-headers">Updating Vulkan headers</a></h2>
|
||||
<h2 id="updating-vulkan-headers"><a class="header" href="#updating-vulkan-headers">Updating Vulkan headers</a></h2>
|
||||
<p>To update the Vulkan headers, perform the following steps.</p>
|
||||
<p>First, find the latest version of the Vulkan headers here:
|
||||
https://github.com/KhronosGroup/Vulkan-Headers/tags</p>
|
||||
|
||||
@@ -158,8 +158,8 @@
|
||||
|
||||
<div id="content" class="content">
|
||||
<main>
|
||||
<h1 id="building-filament"><a class="header" href="#building-filament">Building Filament</a></h1>
|
||||
<h2 id="prerequisites"><a class="header" href="#prerequisites">Prerequisites</a></h2>
|
||||
<h2 id="building-filament"><a class="header" href="#building-filament">Building Filament</a></h2>
|
||||
<h3 id="prerequisites"><a class="header" href="#prerequisites">Prerequisites</a></h3>
|
||||
<p>To build Filament, you must first install the following tools:</p>
|
||||
<ul>
|
||||
<li>CMake 3.22.1 (or more recent)</li>
|
||||
@@ -175,14 +175,14 @@ section below.</p>
|
||||
<li>Android NDK 25.1 or higher</li>
|
||||
<li>Java 17</li>
|
||||
</ul>
|
||||
<h2 id="environment-variables"><a class="header" href="#environment-variables">Environment variables</a></h2>
|
||||
<h3 id="environment-variables"><a class="header" href="#environment-variables">Environment variables</a></h3>
|
||||
<p>To build Filament for Android, make sure the environment variable <code>ANDROID_HOME</code> points to the
|
||||
location of your Android SDK.</p>
|
||||
<p>When building for WebGL, you'll also need to set <code>EMSDK</code>. See <a href="#webassembly">WebAssembly</a>.</p>
|
||||
<h3 id="ide"><a class="header" href="#ide">IDE</a></h3>
|
||||
<p>We recommend using CLion to develop for Filament. Simply open the root directory's CMakeLists.txt
|
||||
in CLion to obtain a usable project.</p>
|
||||
<h2 id="easy-build"><a class="header" href="#easy-build">Easy build</a></h2>
|
||||
<h3 id="easy-build"><a class="header" href="#easy-build">Easy build</a></h3>
|
||||
<p>Once the required OS specific dependencies listed below are installed, you can use the script
|
||||
located in <code>build.sh</code> to build Filament easily on macOS and Linux.</p>
|
||||
<p>This script can be invoked from anywhere and will produce build artifacts in the <code>out/</code> directory
|
||||
@@ -206,7 +206,7 @@ The script offers more features described by executing <code>build.sh -h</code>.
|
||||
<li><code>-t</code>: <a href="https://google.github.io/filament/dup/fgviewer.html"><code>fgviewer</code></a></li>
|
||||
<li><code>-b</code> and <code>-y</code>: <a href="https://google.github.io/filament/notes/asan_ubsan.html">ASAN/UBSAN builds</a></li>
|
||||
</ul>
|
||||
<h2 id="filament-specific-cmake-options"><a class="header" href="#filament-specific-cmake-options">Filament-specific CMake Options</a></h2>
|
||||
<h3 id="filament-specific-cmake-options"><a class="header" href="#filament-specific-cmake-options">Filament-specific CMake Options</a></h3>
|
||||
<p>The following CMake options are boolean options specific to Filament:</p>
|
||||
<ul>
|
||||
<li><code>FILAMENT_ENABLE_LTO</code>: Enable link-time optimizations if supported by the compiler</li>
|
||||
@@ -223,7 +223,7 @@ The script offers more features described by executing <code>build.sh -h</code>.
|
||||
cmake . -DOPTION=ON # Replace OPTION with the option name, set to ON / OFF
|
||||
</code></pre>
|
||||
<p>Options can also be set with the CMake GUI.</p>
|
||||
<h2 id="linux"><a class="header" href="#linux">Linux</a></h2>
|
||||
<h3 id="linux"><a class="header" href="#linux">Linux</a></h3>
|
||||
<p>Make sure you've installed the following dependencies:</p>
|
||||
<ul>
|
||||
<li><code>clang-16</code> or higher</li>
|
||||
@@ -265,7 +265,7 @@ update-alternatives --install /usr/bin/c++ c++ /usr/bin/clang++ 100
|
||||
<pre><code class="language-shell">ninja
|
||||
</code></pre>
|
||||
<p>This will build Filament, its tests and samples, and various host tools.</p>
|
||||
<h2 id="macos"><a class="header" href="#macos">macOS</a></h2>
|
||||
<h3 id="macos"><a class="header" href="#macos">macOS</a></h3>
|
||||
<p>To compile Filament you must have the most recent version of Xcode installed and you need to
|
||||
make sure the command line tools are setup by running:</p>
|
||||
<pre><code class="language-shell">xcode-select --install
|
||||
@@ -278,14 +278,14 @@ cd out/cmake-release
|
||||
cmake -G Ninja -DCMAKE_BUILD_TYPE=Release -DCMAKE_INSTALL_PREFIX=../release/filament ../..
|
||||
ninja
|
||||
</code></pre>
|
||||
<h2 id="ios"><a class="header" href="#ios">iOS</a></h2>
|
||||
<h3 id="ios"><a class="header" href="#ios">iOS</a></h3>
|
||||
<p>The easiest way to build Filament for iOS is to use <code>build.sh</code> and the
|
||||
<code>-p ios</code> flag. For instance to build the debug target:</p>
|
||||
<pre><code class="language-shell">./build.sh -p ios debug
|
||||
</code></pre>
|
||||
<p>See <a href="./ios/samples/README.html">ios/samples/README.md</a> for more information.</p>
|
||||
<h2 id="windows"><a class="header" href="#windows">Windows</a></h2>
|
||||
<h3 id="building-on-windows-with-visual-studio-2019-or-later"><a class="header" href="#building-on-windows-with-visual-studio-2019-or-later">Building on Windows with Visual Studio 2019 or later</a></h3>
|
||||
<h3 id="windows"><a class="header" href="#windows">Windows</a></h3>
|
||||
<h4 id="building-on-windows-with-visual-studio-2019-or-later"><a class="header" href="#building-on-windows-with-visual-studio-2019-or-later">Building on Windows with Visual Studio 2019 or later</a></h4>
|
||||
<p>Install the following components:</p>
|
||||
<ul>
|
||||
<li><a href="https://www.visualstudio.com/downloads">Visual Studio 2019 or later</a></li>
|
||||
@@ -314,7 +314,7 @@ target in the <em>Solution Explorer</em> and choose <em>Build</em> to build a sp
|
||||
<code>out</code> folder run the following command.</p>
|
||||
<pre><code class="language-bat">cmake --build . --target gltf_viewer --config Release
|
||||
</code></pre>
|
||||
<h2 id="android"><a class="header" href="#android">Android</a></h2>
|
||||
<h3 id="android"><a class="header" href="#android">Android</a></h3>
|
||||
<p>Before building Filament for Android, make sure to build Filament for your host. Some of the
|
||||
host tools are required to successfully build for Android.</p>
|
||||
<p>Filament can be built for the following architectures:</p>
|
||||
@@ -327,11 +327,11 @@ host tools are required to successfully build for Android.</p>
|
||||
<p>Note that the main target is the ARM 64-bit target. Our implementation is optimized first and
|
||||
foremost for <code>arm64-v8a</code>.</p>
|
||||
<p>To build Android on Windows machines, see <a href="android/Windows.html">android/Windows.md</a>.</p>
|
||||
<h3 id="important-sdk-location"><a class="header" href="#important-sdk-location">Important: SDK location</a></h3>
|
||||
<h4 id="important-sdk-location"><a class="header" href="#important-sdk-location">Important: SDK location</a></h4>
|
||||
<p>Either ensure your <code>ANDROID_HOME</code> environment variable is set or make sure the root project
|
||||
contains a <code>local.properties</code> file with the <code>sdk.dir</code> property pointing to your installation of
|
||||
the Android SDK.</p>
|
||||
<h3 id="easy-android-build"><a class="header" href="#easy-android-build">Easy Android build</a></h3>
|
||||
<h4 id="easy-android-build"><a class="header" href="#easy-android-build">Easy Android build</a></h4>
|
||||
<p>The easiest way to build Filament for Android is to use <code>build.sh</code> and the
|
||||
<code>-p android</code> flag. For instance to build the release target:</p>
|
||||
<pre><code class="language-shell">./build.sh -p android release
|
||||
@@ -341,7 +341,7 @@ the Android SDK.</p>
|
||||
</code></pre>
|
||||
<p>The output APK can be found in <code>android/samples/sample-hello-triangle/build/outputs/apk/release/sample-hello-triangle-release-unsigned.apk</code></p>
|
||||
<p>Run <code>build.sh -h</code> for more information.</p>
|
||||
<h3 id="android-studio"><a class="header" href="#android-studio">Android Studio</a></h3>
|
||||
<h4 id="android-studio"><a class="header" href="#android-studio">Android Studio</a></h4>
|
||||
<p>You must use the latest stable release of Android Studio.</p>
|
||||
<p>The Android build of filament is separated into java/kotlin client APIs, a layer of jni bindings
|
||||
that bridges java/kotlin with native code, and Filament and other component code that have been compiled
|
||||
@@ -362,7 +362,7 @@ By doing so, Android Studio will automatically try to compile the app only for t
|
||||
device's architecture. So if you are targeting a new Pixel phone, make sure that the step above
|
||||
(compiling the library) is targeting ARM 64-bit (<code>-q arm64-v8a</code> ), and if you are running the app on
|
||||
an emulator on a Linux machine with an x86 64-bit chipset, you would indicate (<code>-q x86_64</code>) in the above step.</p>
|
||||
<h3 id="manual-builds"><a class="header" href="#manual-builds">Manual builds</a></h3>
|
||||
<h4 id="manual-builds"><a class="header" href="#manual-builds">Manual builds</a></h4>
|
||||
<p>Invoke CMake in a build directory of your choice, inside of filament's directory. The commands
|
||||
below show how to build Filament for ARM 64-bit (<code>aarch64</code>).</p>
|
||||
<pre><code class="language-shell">mkdir out/android-build-release-aarch64
|
||||
@@ -379,7 +379,7 @@ cmake -G Ninja -DCMAKE_TOOLCHAIN_FILE=../../build/toolchain-aarch64-linux-androi
|
||||
<p>This will generate Filament's Android binaries in <code>out/android-release</code>. This location is important
|
||||
to build the Android Studio projects located in <code>filament/android</code>. After install, the library
|
||||
binaries should be found in <code>out/android-release/filament/lib/arm64-v8a</code>.</p>
|
||||
<h3 id="aar"><a class="header" href="#aar">AAR</a></h3>
|
||||
<h4 id="aar"><a class="header" href="#aar">AAR</a></h4>
|
||||
<p>Before you attempt to build the AAR, make sure you've compiled and installed the native libraries
|
||||
as explained in the sections above. You must have the following ABIs built in
|
||||
<code>out/android-release/filament/lib/</code>:</p>
|
||||
@@ -405,7 +405,7 @@ AAR.</p>
|
||||
</code></pre>
|
||||
<p>The <code>-Pcom.google.android.filament.dist-dir</code> can be used to specify a different installation
|
||||
directory (it must match the CMake install prefix used in the previous steps).</p>
|
||||
<h3 id="using-filaments-aar"><a class="header" href="#using-filaments-aar">Using Filament's AAR</a></h3>
|
||||
<h4 id="using-filaments-aar"><a class="header" href="#using-filaments-aar">Using Filament's AAR</a></h4>
|
||||
<p>Create a new module in your project and select <em>Import .JAR or .AAR Package</em> when prompted. Make
|
||||
sure to add the newly created module as a dependency to your application.</p>
|
||||
<p>If you do not wish to include all supported ABIs, make sure to create the appropriate flavors in
|
||||
@@ -441,7 +441,7 @@ productFlavors {
|
||||
}
|
||||
}
|
||||
</code></pre>
|
||||
<h2 id="webassembly"><a class="header" href="#webassembly">WebAssembly</a></h2>
|
||||
<h3 id="webassembly"><a class="header" href="#webassembly">WebAssembly</a></h3>
|
||||
<p>The core Filament library can be cross-compiled to WebAssembly from either macOS or Linux. To get
|
||||
started, follow the instructions for building Filament on your platform (<a href="#macos">macOS</a> or
|
||||
<a href="#linux">linux</a>), which will ensure you have the proper dependencies installed.</p>
|
||||
|
||||
@@ -209,7 +209,7 @@ as possible. The current external dependencies of the runtime library include:</
|
||||
|
||||
<nav class="nav-wrapper" aria-label="Page navigation">
|
||||
<!-- Mobile navigation buttons -->
|
||||
<a rel="prev" href="../build/windows_android.html" class="mobile-nav-chapters previous" title="Previous chapter" aria-label="Previous chapter" aria-keyshortcuts="Left">
|
||||
<a rel="prev" href="../build/maven_release.html" class="mobile-nav-chapters previous" title="Previous chapter" aria-label="Previous chapter" aria-keyshortcuts="Left">
|
||||
<i class="fa fa-angle-left"></i>
|
||||
</a>
|
||||
|
||||
@@ -223,7 +223,7 @@ as possible. The current external dependencies of the runtime library include:</
|
||||
</div>
|
||||
|
||||
<nav class="nav-wide-wrapper" aria-label="Page navigation">
|
||||
<a rel="prev" href="../build/windows_android.html" class="nav-chapters previous" title="Previous chapter" aria-label="Previous chapter" aria-keyshortcuts="Left">
|
||||
<a rel="prev" href="../build/maven_release.html" class="nav-chapters previous" title="Previous chapter" aria-label="Previous chapter" aria-keyshortcuts="Left">
|
||||
<i class="fa fa-angle-left"></i>
|
||||
</a>
|
||||
|
||||
|
||||
@@ -253,7 +253,7 @@ add a link in <code>SUMMARY.md</code>, and perform the steps outlined in
|
||||
|
||||
<nav class="nav-wrapper" aria-label="Page navigation">
|
||||
<!-- Mobile navigation buttons -->
|
||||
<a rel="prev" href="../release/branching.html" class="mobile-nav-chapters previous" title="Previous chapter" aria-label="Previous chapter" aria-keyshortcuts="Left">
|
||||
<a rel="prev" href="../notes/release_guide.html" class="mobile-nav-chapters previous" title="Previous chapter" aria-label="Previous chapter" aria-keyshortcuts="Left">
|
||||
<i class="fa fa-angle-left"></i>
|
||||
</a>
|
||||
|
||||
@@ -267,7 +267,7 @@ add a link in <code>SUMMARY.md</code>, and perform the steps outlined in
|
||||
</div>
|
||||
|
||||
<nav class="nav-wide-wrapper" aria-label="Page navigation">
|
||||
<a rel="prev" href="../release/branching.html" class="nav-chapters previous" title="Previous chapter" aria-label="Previous chapter" aria-keyshortcuts="Left">
|
||||
<a rel="prev" href="../notes/release_guide.html" class="nav-chapters previous" title="Previous chapter" aria-label="Previous chapter" aria-keyshortcuts="Left">
|
||||
<i class="fa fa-angle-left"></i>
|
||||
</a>
|
||||
|
||||
|
||||
@@ -158,7 +158,7 @@
|
||||
|
||||
<div id="content" class="content">
|
||||
<main>
|
||||
<h1 id="filamesh"><a class="header" href="#filamesh">filamesh</a></h1>
|
||||
<h1 id="filamesh"><a class="header" href="#filamesh">Filamesh</a></h1>
|
||||
<p><code>filamesh</code> converts any mesh file supported by <code>assimp</code> (as configured in this source tree) into a
|
||||
custom binary file format. The goal of this binary file format is to allow test applications to
|
||||
easily and quickly load meshes.</p>
|
||||
@@ -419,7 +419,7 @@ Mesh loadMeshFromFile(filament::Engine* engine, const utils::Path& path,
|
||||
<i class="fa fa-angle-left"></i>
|
||||
</a>
|
||||
|
||||
<a rel="next prefetch" href="../dup/matinfo.html" class="mobile-nav-chapters next" title="Next chapter" aria-label="Next chapter" aria-keyshortcuts="Right">
|
||||
<a rel="next prefetch" href="../dup/normal_blending.html" class="mobile-nav-chapters next" title="Next chapter" aria-label="Next chapter" aria-keyshortcuts="Right">
|
||||
<i class="fa fa-angle-right"></i>
|
||||
</a>
|
||||
|
||||
@@ -433,7 +433,7 @@ Mesh loadMeshFromFile(filament::Engine* engine, const utils::Path& path,
|
||||
<i class="fa fa-angle-left"></i>
|
||||
</a>
|
||||
|
||||
<a rel="next prefetch" href="../dup/matinfo.html" class="nav-chapters next" title="Next chapter" aria-label="Next chapter" aria-keyshortcuts="Right">
|
||||
<a rel="next prefetch" href="../dup/normal_blending.html" class="nav-chapters next" title="Next chapter" aria-label="Next chapter" aria-keyshortcuts="Right">
|
||||
<i class="fa fa-angle-right"></i>
|
||||
</a>
|
||||
</nav>
|
||||
|
||||
@@ -158,7 +158,7 @@
|
||||
|
||||
<div id="content" class="content">
|
||||
<main>
|
||||
<h1 id="gltfio"><a class="header" href="#gltfio">gltfio</a></h1>
|
||||
<h1 id="description"><a class="header" href="#description">Description</a></h1>
|
||||
<p><code>gltfio</code> is a loader library that consumes <code>gltf</code> or <code>glb</code> content and produces Filament
|
||||
objects. For usage details, see the docstring for <code>AssetLoader</code>.</p>
|
||||
<p>gltfio has two plug-in interfaces, <code>TextureProvider</code> and <code>MaterialProvider</code>. Filament ships with
|
||||
|
||||
@@ -181,7 +181,7 @@ important for <code>matc</code> (material compiler).</p>
|
||||
}
|
||||
|
||||
dependencies {
|
||||
implementation 'com.google.android.filament:filament-android:1.68.5'
|
||||
implementation 'com.google.android.filament:filament-android:1.68.2'
|
||||
}
|
||||
</code></pre>
|
||||
<p>Here are all the libraries available in the group <code>com.google.android.filament</code>:</p>
|
||||
@@ -196,7 +196,7 @@ dependencies {
|
||||
</div>
|
||||
<h3 id="ios"><a class="header" href="#ios">iOS</a></h3>
|
||||
<p>iOS projects can use CocoaPods to install the latest release:</p>
|
||||
<pre><code class="language-shell">pod 'Filament', '~> 1.68.5'
|
||||
<pre><code class="language-shell">pod 'Filament', '~> 1.68.2'
|
||||
</code></pre>
|
||||
<h2 id="documentation"><a class="header" href="#documentation">Documentation</a></h2>
|
||||
<ul>
|
||||
|
||||
@@ -158,7 +158,7 @@
|
||||
|
||||
<div id="content" class="content">
|
||||
<main>
|
||||
<h1 id="matinfo"><a class="header" href="#matinfo">matinfo</a></h1>
|
||||
<h1 id="matinfo"><a class="header" href="#matinfo">Matinfo</a></h1>
|
||||
<p><code>matinfo</code> lists the content of a compiled material as output by <code>matc</code>. This tool is meant to be
|
||||
used for debug purpose only.</p>
|
||||
<h2 id="usage"><a class="header" href="#usage">Usage</a></h2>
|
||||
@@ -169,11 +169,11 @@ used for debug purpose only.</p>
|
||||
|
||||
<nav class="nav-wrapper" aria-label="Page navigation">
|
||||
<!-- Mobile navigation buttons -->
|
||||
<a rel="prev" href="../dup/filamesh.html" class="mobile-nav-chapters previous" title="Previous chapter" aria-label="Previous chapter" aria-keyshortcuts="Left">
|
||||
<a rel="prev" href="../dup/mipgen.html" class="mobile-nav-chapters previous" title="Previous chapter" aria-label="Previous chapter" aria-keyshortcuts="Left">
|
||||
<i class="fa fa-angle-left"></i>
|
||||
</a>
|
||||
|
||||
<a rel="next prefetch" href="../dup/mipgen.html" class="mobile-nav-chapters next" title="Next chapter" aria-label="Next chapter" aria-keyshortcuts="Right">
|
||||
<a rel="next prefetch" href="../dup/roughness_prefilter.html" class="mobile-nav-chapters next" title="Next chapter" aria-label="Next chapter" aria-keyshortcuts="Right">
|
||||
<i class="fa fa-angle-right"></i>
|
||||
</a>
|
||||
|
||||
@@ -183,11 +183,11 @@ used for debug purpose only.</p>
|
||||
</div>
|
||||
|
||||
<nav class="nav-wide-wrapper" aria-label="Page navigation">
|
||||
<a rel="prev" href="../dup/filamesh.html" class="nav-chapters previous" title="Previous chapter" aria-label="Previous chapter" aria-keyshortcuts="Left">
|
||||
<a rel="prev" href="../dup/mipgen.html" class="nav-chapters previous" title="Previous chapter" aria-label="Previous chapter" aria-keyshortcuts="Left">
|
||||
<i class="fa fa-angle-left"></i>
|
||||
</a>
|
||||
|
||||
<a rel="next prefetch" href="../dup/mipgen.html" class="nav-chapters next" title="Next chapter" aria-label="Next chapter" aria-keyshortcuts="Right">
|
||||
<a rel="next prefetch" href="../dup/roughness_prefilter.html" class="nav-chapters next" title="Next chapter" aria-label="Next chapter" aria-keyshortcuts="Right">
|
||||
<i class="fa fa-angle-right"></i>
|
||||
</a>
|
||||
</nav>
|
||||
|
||||
@@ -169,11 +169,11 @@
|
||||
|
||||
<nav class="nav-wrapper" aria-label="Page navigation">
|
||||
<!-- Mobile navigation buttons -->
|
||||
<a rel="prev" href="../dup/matinfo.html" class="mobile-nav-chapters previous" title="Previous chapter" aria-label="Previous chapter" aria-keyshortcuts="Left">
|
||||
<a rel="prev" href="../dup/normal_blending.html" class="mobile-nav-chapters previous" title="Previous chapter" aria-label="Previous chapter" aria-keyshortcuts="Left">
|
||||
<i class="fa fa-angle-left"></i>
|
||||
</a>
|
||||
|
||||
<a rel="next prefetch" href="../dup/normal_blending.html" class="mobile-nav-chapters next" title="Next chapter" aria-label="Next chapter" aria-keyshortcuts="Right">
|
||||
<a rel="next prefetch" href="../dup/matinfo.html" class="mobile-nav-chapters next" title="Next chapter" aria-label="Next chapter" aria-keyshortcuts="Right">
|
||||
<i class="fa fa-angle-right"></i>
|
||||
</a>
|
||||
|
||||
@@ -183,11 +183,11 @@
|
||||
</div>
|
||||
|
||||
<nav class="nav-wide-wrapper" aria-label="Page navigation">
|
||||
<a rel="prev" href="../dup/matinfo.html" class="nav-chapters previous" title="Previous chapter" aria-label="Previous chapter" aria-keyshortcuts="Left">
|
||||
<a rel="prev" href="../dup/normal_blending.html" class="nav-chapters previous" title="Previous chapter" aria-label="Previous chapter" aria-keyshortcuts="Left">
|
||||
<i class="fa fa-angle-left"></i>
|
||||
</a>
|
||||
|
||||
<a rel="next prefetch" href="../dup/normal_blending.html" class="nav-chapters next" title="Next chapter" aria-label="Next chapter" aria-keyshortcuts="Right">
|
||||
<a rel="next prefetch" href="../dup/matinfo.html" class="nav-chapters next" title="Next chapter" aria-label="Next chapter" aria-keyshortcuts="Right">
|
||||
<i class="fa fa-angle-right"></i>
|
||||
</a>
|
||||
</nav>
|
||||
|
||||
@@ -167,11 +167,11 @@ correct results (as opposed to common techniques such as linear or overlay blend
|
||||
|
||||
<nav class="nav-wrapper" aria-label="Page navigation">
|
||||
<!-- Mobile navigation buttons -->
|
||||
<a rel="prev" href="../dup/mipgen.html" class="mobile-nav-chapters previous" title="Previous chapter" aria-label="Previous chapter" aria-keyshortcuts="Left">
|
||||
<a rel="prev" href="../dup/filamesh.html" class="mobile-nav-chapters previous" title="Previous chapter" aria-label="Previous chapter" aria-keyshortcuts="Left">
|
||||
<i class="fa fa-angle-left"></i>
|
||||
</a>
|
||||
|
||||
<a rel="next prefetch" href="../dup/roughness_prefilter.html" class="mobile-nav-chapters next" title="Next chapter" aria-label="Next chapter" aria-keyshortcuts="Right">
|
||||
<a rel="next prefetch" href="../dup/mipgen.html" class="mobile-nav-chapters next" title="Next chapter" aria-label="Next chapter" aria-keyshortcuts="Right">
|
||||
<i class="fa fa-angle-right"></i>
|
||||
</a>
|
||||
|
||||
@@ -181,11 +181,11 @@ correct results (as opposed to common techniques such as linear or overlay blend
|
||||
</div>
|
||||
|
||||
<nav class="nav-wide-wrapper" aria-label="Page navigation">
|
||||
<a rel="prev" href="../dup/mipgen.html" class="nav-chapters previous" title="Previous chapter" aria-label="Previous chapter" aria-keyshortcuts="Left">
|
||||
<a rel="prev" href="../dup/filamesh.html" class="nav-chapters previous" title="Previous chapter" aria-label="Previous chapter" aria-keyshortcuts="Left">
|
||||
<i class="fa fa-angle-left"></i>
|
||||
</a>
|
||||
|
||||
<a rel="next prefetch" href="../dup/roughness_prefilter.html" class="nav-chapters next" title="Next chapter" aria-label="Next chapter" aria-keyshortcuts="Right">
|
||||
<a rel="next prefetch" href="../dup/mipgen.html" class="nav-chapters next" title="Next chapter" aria-label="Next chapter" aria-keyshortcuts="Right">
|
||||
<i class="fa fa-angle-right"></i>
|
||||
</a>
|
||||
</nav>
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
<head>
|
||||
<!-- Book generated using mdBook -->
|
||||
<meta charset="UTF-8">
|
||||
<title>CI: renderdiff - Filament</title>
|
||||
<title>renderdiff - Filament</title>
|
||||
|
||||
|
||||
<!-- Custom HTML head -->
|
||||
@@ -159,8 +159,7 @@
|
||||
<div id="content" class="content">
|
||||
<main>
|
||||
<h1 id="rendering-difference-test"><a class="header" href="#rendering-difference-test">Rendering Difference Test</a></h1>
|
||||
<p>This tool (<code>/test/renderdiff</code>) is a collections of scripts to run <code>gltf_viewer</code> and produce headless
|
||||
renderings.</p>
|
||||
<p>This tool is a collections of scripts to run <code>gltf_viewer</code> and produce headless renderings.</p>
|
||||
<p>This is mainly useful for continuous integration where GPUs are generally not available on cloud
|
||||
machines. To perform software rasterization, these scripts are centered around <a href="https://docs.mesa3d.org">Mesa</a>'s
|
||||
software rasterizers, but nothing bars us from using another rasterizer like <a href="https://github.com/google/swiftshader">SwiftShader</a>.
|
||||
@@ -252,7 +251,7 @@ git switch -c my-pr-branch-golden
|
||||
</code></pre>
|
||||
</li>
|
||||
<li>In the commit message of your working branch on <code>filament</code>, add the following line
|
||||
<pre><code>RDIFF_BRANCH=my-pr-branch-golden
|
||||
<pre><code>RDIFF_BBRANCH=my-pr-branch-golden
|
||||
</code></pre>
|
||||
</li>
|
||||
</ul>
|
||||
@@ -293,7 +292,7 @@ the run number is <code>18023632663</code>.</p>
|
||||
|
||||
<nav class="nav-wrapper" aria-label="Page navigation">
|
||||
<!-- Mobile navigation buttons -->
|
||||
<a rel="prev" href="../dup/test_ci_backend.html" class="mobile-nav-chapters previous" title="Previous chapter" aria-label="Previous chapter" aria-keyshortcuts="Left">
|
||||
<a rel="prev" href="../notes/tests.html" class="mobile-nav-chapters previous" title="Previous chapter" aria-label="Previous chapter" aria-keyshortcuts="Left">
|
||||
<i class="fa fa-angle-left"></i>
|
||||
</a>
|
||||
|
||||
@@ -307,7 +306,7 @@ the run number is <code>18023632663</code>.</p>
|
||||
</div>
|
||||
|
||||
<nav class="nav-wide-wrapper" aria-label="Page navigation">
|
||||
<a rel="prev" href="../dup/test_ci_backend.html" class="nav-chapters previous" title="Previous chapter" aria-label="Previous chapter" aria-keyshortcuts="Left">
|
||||
<a rel="prev" href="../notes/tests.html" class="nav-chapters previous" title="Previous chapter" aria-label="Previous chapter" aria-keyshortcuts="Left">
|
||||
<i class="fa fa-angle-left"></i>
|
||||
</a>
|
||||
|
||||
@@ -167,11 +167,11 @@ be used to reduce shading aliasing.</p>
|
||||
|
||||
<nav class="nav-wrapper" aria-label="Page navigation">
|
||||
<!-- Mobile navigation buttons -->
|
||||
<a rel="prev" href="../dup/normal_blending.html" class="mobile-nav-chapters previous" title="Previous chapter" aria-label="Previous chapter" aria-keyshortcuts="Left">
|
||||
<a rel="prev" href="../dup/matinfo.html" class="mobile-nav-chapters previous" title="Previous chapter" aria-label="Previous chapter" aria-keyshortcuts="Left">
|
||||
<i class="fa fa-angle-left"></i>
|
||||
</a>
|
||||
|
||||
<a rel="next prefetch" href="../dup/specgen.html" class="mobile-nav-chapters next" title="Next chapter" aria-label="Next chapter" aria-keyshortcuts="Right">
|
||||
<a rel="next prefetch" href="../dup/specular_color.html" class="mobile-nav-chapters next" title="Next chapter" aria-label="Next chapter" aria-keyshortcuts="Right">
|
||||
<i class="fa fa-angle-right"></i>
|
||||
</a>
|
||||
|
||||
@@ -181,11 +181,11 @@ be used to reduce shading aliasing.</p>
|
||||
</div>
|
||||
|
||||
<nav class="nav-wide-wrapper" aria-label="Page navigation">
|
||||
<a rel="prev" href="../dup/normal_blending.html" class="nav-chapters previous" title="Previous chapter" aria-label="Previous chapter" aria-keyshortcuts="Left">
|
||||
<a rel="prev" href="../dup/matinfo.html" class="nav-chapters previous" title="Previous chapter" aria-label="Previous chapter" aria-keyshortcuts="Left">
|
||||
<i class="fa fa-angle-left"></i>
|
||||
</a>
|
||||
|
||||
<a rel="next prefetch" href="../dup/specgen.html" class="nav-chapters next" title="Next chapter" aria-label="Next chapter" aria-keyshortcuts="Right">
|
||||
<a rel="next prefetch" href="../dup/specular_color.html" class="nav-chapters next" title="Next chapter" aria-label="Next chapter" aria-keyshortcuts="Right">
|
||||
<i class="fa fa-angle-right"></i>
|
||||
</a>
|
||||
</nav>
|
||||
|
||||
@@ -1,265 +0,0 @@
|
||||
<!DOCTYPE HTML>
|
||||
<html lang="en" class="light sidebar-visible" dir="ltr">
|
||||
<head>
|
||||
<!-- Book generated using mdBook -->
|
||||
<meta charset="UTF-8">
|
||||
<title>specgen - Filament</title>
|
||||
|
||||
|
||||
<!-- Custom HTML head -->
|
||||
|
||||
<meta name="description" content="">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||
<meta name="theme-color" content="#ffffff">
|
||||
|
||||
<link rel="shortcut icon" href="../favicon.png">
|
||||
<link rel="stylesheet" href="../css/variables.css">
|
||||
<link rel="stylesheet" href="../css/general.css">
|
||||
<link rel="stylesheet" href="../css/chrome.css">
|
||||
|
||||
<!-- Fonts -->
|
||||
<link rel="stylesheet" href="../FontAwesome/css/font-awesome.css">
|
||||
<link rel="stylesheet" href="../fonts/fonts.css">
|
||||
|
||||
<!-- Highlight.js Stylesheets -->
|
||||
<link rel="stylesheet" href="../highlight.css">
|
||||
<link rel="stylesheet" href="../tomorrow-night.css">
|
||||
<link rel="stylesheet" href="../ayu-highlight.css">
|
||||
|
||||
<!-- Custom theme stylesheets -->
|
||||
|
||||
<!-- MathJax -->
|
||||
<script async src="https://cdnjs.cloudflare.com/ajax/libs/mathjax/2.7.1/MathJax.js?config=TeX-AMS-MML_HTMLorMML"></script>
|
||||
|
||||
<!-- Provide site root to javascript -->
|
||||
<script>
|
||||
var path_to_root = "../";
|
||||
var default_theme = window.matchMedia("(prefers-color-scheme: dark)").matches ? "light" : "light";
|
||||
</script>
|
||||
<!-- Start loading toc.js asap -->
|
||||
<script src="../toc.js"></script>
|
||||
</head>
|
||||
<body>
|
||||
<div id="body-container">
|
||||
<!-- Work around some values being stored in localStorage wrapped in quotes -->
|
||||
<script>
|
||||
try {
|
||||
var theme = localStorage.getItem('mdbook-theme');
|
||||
var sidebar = localStorage.getItem('mdbook-sidebar');
|
||||
|
||||
if (theme.startsWith('"') && theme.endsWith('"')) {
|
||||
localStorage.setItem('mdbook-theme', theme.slice(1, theme.length - 1));
|
||||
}
|
||||
|
||||
if (sidebar.startsWith('"') && sidebar.endsWith('"')) {
|
||||
localStorage.setItem('mdbook-sidebar', sidebar.slice(1, sidebar.length - 1));
|
||||
}
|
||||
} catch (e) { }
|
||||
</script>
|
||||
|
||||
<!-- Set the theme before any content is loaded, prevents flash -->
|
||||
<script>
|
||||
var theme;
|
||||
try { theme = localStorage.getItem('mdbook-theme'); } catch(e) { }
|
||||
if (theme === null || theme === undefined) { theme = default_theme; }
|
||||
const html = document.documentElement;
|
||||
html.classList.remove('light')
|
||||
html.classList.add(theme);
|
||||
html.classList.add("js");
|
||||
</script>
|
||||
|
||||
<input type="checkbox" id="sidebar-toggle-anchor" class="hidden">
|
||||
|
||||
<!-- Hide / unhide sidebar before it is displayed -->
|
||||
<script>
|
||||
var sidebar = null;
|
||||
var sidebar_toggle = document.getElementById("sidebar-toggle-anchor");
|
||||
if (document.body.clientWidth >= 1080) {
|
||||
try { sidebar = localStorage.getItem('mdbook-sidebar'); } catch(e) { }
|
||||
sidebar = sidebar || 'visible';
|
||||
} else {
|
||||
sidebar = 'hidden';
|
||||
}
|
||||
sidebar_toggle.checked = sidebar === 'visible';
|
||||
html.classList.remove('sidebar-visible');
|
||||
html.classList.add("sidebar-" + sidebar);
|
||||
</script>
|
||||
|
||||
<nav id="sidebar" class="sidebar" aria-label="Table of contents">
|
||||
<div style="display:flex;align-items:center;justify-content:center">
|
||||
<img class="flogo" src="../images/filament_logo_small.png"></img>
|
||||
</div>
|
||||
<!-- populated by js -->
|
||||
<mdbook-sidebar-scrollbox class="sidebar-scrollbox"></mdbook-sidebar-scrollbox>
|
||||
<noscript>
|
||||
<iframe class="sidebar-iframe-outer" src="../toc.html"></iframe>
|
||||
</noscript>
|
||||
<div id="sidebar-resize-handle" class="sidebar-resize-handle">
|
||||
<div class="sidebar-resize-indicator"></div>
|
||||
</div>
|
||||
</nav>
|
||||
|
||||
<div id="page-wrapper" class="page-wrapper">
|
||||
|
||||
<div class="page">
|
||||
<div id="menu-bar-hover-placeholder"></div>
|
||||
<div id="menu-bar" class="menu-bar sticky">
|
||||
<div class="left-buttons">
|
||||
<label id="sidebar-toggle" class="icon-button" for="sidebar-toggle-anchor" title="Toggle Table of Contents" aria-label="Toggle Table of Contents" aria-controls="sidebar">
|
||||
<i class="fa fa-bars"></i>
|
||||
</label>
|
||||
<!-- Filament: disable themes because the markdeep part does not look good for dark themes -->
|
||||
<!--
|
||||
<button id="theme-toggle" class="icon-button" type="button" title="Change theme" aria-label="Change theme" aria-haspopup="true" aria-expanded="false" aria-controls="theme-list">
|
||||
<i class="fa fa-paint-brush"></i>
|
||||
</button>
|
||||
<ul id="theme-list" class="theme-popup" aria-label="Themes" role="menu">
|
||||
<li role="none"><button role="menuitem" class="theme" id="light">Light</button></li>
|
||||
<li role="none"><button role="menuitem" class="theme" id="rust">Rust</button></li>
|
||||
<li role="none"><button role="menuitem" class="theme" id="coal">Coal</button></li>
|
||||
<li role="none"><button role="menuitem" class="theme" id="navy">Navy</button></li>
|
||||
<li role="none"><button role="menuitem" class="theme" id="ayu">Ayu</button></li>
|
||||
</ul>
|
||||
-->
|
||||
<button id="search-toggle" class="icon-button" type="button" title="Search. (Shortkey: s)" aria-label="Toggle Searchbar" aria-expanded="false" aria-keyshortcuts="S" aria-controls="searchbar">
|
||||
<i class="fa fa-search"></i>
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<h1 class="menu-title">Filament</h1>
|
||||
|
||||
<div class="right-buttons">
|
||||
<a href="https://github.com/google/filament" title="Git repository" aria-label="Git repository">
|
||||
<i id="git-repository-button" class="fa fa-github"></i>
|
||||
</a>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div id="search-wrapper" class="hidden">
|
||||
<form id="searchbar-outer" class="searchbar-outer">
|
||||
<input type="search" id="searchbar" name="searchbar" placeholder="Search this book ..." aria-controls="searchresults-outer" aria-describedby="searchresults-header">
|
||||
</form>
|
||||
<div id="searchresults-outer" class="searchresults-outer hidden">
|
||||
<div id="searchresults-header" class="searchresults-header"></div>
|
||||
<ul id="searchresults">
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Apply ARIA attributes after the sidebar and the sidebar toggle button are added to the DOM -->
|
||||
<script>
|
||||
document.getElementById('sidebar-toggle').setAttribute('aria-expanded', sidebar === 'visible');
|
||||
document.getElementById('sidebar').setAttribute('aria-hidden', sidebar !== 'visible');
|
||||
Array.from(document.querySelectorAll('#sidebar a')).forEach(function(link) {
|
||||
link.setAttribute('tabIndex', sidebar === 'visible' ? 0 : -1);
|
||||
});
|
||||
</script>
|
||||
|
||||
<div id="content" class="content">
|
||||
<main>
|
||||
<h1 id="specgen-spectral-integration-matrix-generator-for-real-time-dispersion"><a class="header" href="#specgen-spectral-integration-matrix-generator-for-real-time-dispersion">SPECGEN: Spectral Integration Matrix Generator for Real-Time Dispersion</a></h1>
|
||||
<h2 id="theoretical-background"><a class="header" href="#theoretical-background">Theoretical Background</a></h2>
|
||||
<h3 id="1-spectral-rendering-in-rgb"><a class="header" href="#1-spectral-rendering-in-rgb">1. Spectral Rendering in RGB</a></h3>
|
||||
<p>Real-time rendering typically operates in a tristimulus color space (like sRGB). However, physical phenomena like dispersion (refraction splitting light by wavelength) are inherently spectral. To simulate this in an RGB pipeline, we approximate the continuous spectral integration using a finite sum of weighted samples.</p>
|
||||
<p>The fundamental equation for perceived color \(C\) is:</p>
|
||||
<p>$$ C = \int L(\lambda) \cdot r(\lambda) d\lambda $$</p>
|
||||
<p>where:</p>
|
||||
<ul>
|
||||
<li>\(L(\lambda)\) is the spectral radiance reaching the eye.</li>
|
||||
<li>\(r(\lambda)\) is the sensor response function (e.g., CIE 1931 \(\bar{x}, \bar{y}, \bar{z}\) matching functions).</li>
|
||||
</ul>
|
||||
<h3 id="2-basis-transformation-strategy"><a class="header" href="#2-basis-transformation-strategy">2. Basis Transformation Strategy</a></h3>
|
||||
<p>We assume the input light is defined in linear sRGB. To process it spectrally:</p>
|
||||
<ol>
|
||||
<li>Convert sRGB to CIE XYZ (D65 white point).</li>
|
||||
<li>Assume the spectral distribution of the light is a sum of impulses or narrow bands weighted by the XYZ components (or simplified directly from sRGB).</li>
|
||||
<li>Apply spectral effects (like wavelength-dependent refraction).</li>
|
||||
<li>Integrate back to XYZ using the CIE Color Matching Functions (CMFs).</li>
|
||||
<li>Convert final XYZ back to sRGB.</li>
|
||||
</ol>
|
||||
<p>This tool pre-calculates a set of matrices (\(K_n\)) that combine these steps. For a set of \(N\) sample wavelengths \({ \lambda_0, \dots, \lambda_{N-1} }\), the final color is:</p>
|
||||
<p>$$ C_{final} = \sum_{n=0}^{N-1} (K_n \cdot C_{input}) $$</p>
|
||||
<p>Each matrix \(K_n\) represents the contribution of the \(n\)-th spectral sample to the final image, accounting for the conversion to/from XYZ and the spectral weight of that sample.</p>
|
||||
<p><strong>Derivation of \(K_n\):</strong></p>
|
||||
<p>$$ K_n = M_{XYZ \to sRGB} \cdot \text{Diag}(W_n) \cdot M_{sRGB \to XYZ} $$</p>
|
||||
<p>where \(W_n\) is the "spectral weight" vector \((x, y, z)\) for wavelength \(\lambda_n\):</p>
|
||||
<p>$$ W_n = \text{CMF}(\lambda_n) \cdot \text{weight}_n $$</p>
|
||||
<h3 id="3-normalization-energy-conservation"><a class="header" href="#3-normalization-energy-conservation">3. Normalization (Energy Conservation)</a></h3>
|
||||
<p>To ensure that a white input \((1, 1, 1)\) results in a white output \((1, 1, 1)\) when no dispersion occurs (i.e., all samples land on the same pixel), we must normalize the weights.</p>
|
||||
<p>We require: \(\sum K_n = I\)</p>
|
||||
<p>This implies:</p>
|
||||
<p>$$ \sum ( M_{XYZ \to sRGB} \cdot \text{Diag}(W_n) \cdot M_{sRGB \to XYZ} ) = I $$
|
||||
$$ M_{XYZ \to sRGB} \cdot \sum \text{Diag}(W_n) \cdot M_{sRGB \to XYZ} = I $$
|
||||
$$ \sum \text{Diag}(W_n) = M_{sRGB \to XYZ} \cdot I \cdot M_{XYZ \to sRGB} $$
|
||||
$$ \sum \text{Diag}(W_n) = I $$
|
||||
<em>(since \(M \cdot M^{-1} = I\))</em></p>
|
||||
<p>Therefore, we normalize \(W_n\) such that:</p>
|
||||
<p>$$ \sum W_{n,x} = 1.0, \quad \sum W_{n,y} = 1.0, \quad \sum W_{n,z} = 1.0 $$</p>
|
||||
<p><strong>Note:</strong> We do <strong>NOT</strong> normalize to the D65 white point \((0.95047, 1.0, 1.08883)\). The \(M_{sRGB \to XYZ}\) matrix already handles the conversion from linear sRGB \((1, 1, 1)\) to D65 XYZ. If we normalized \(W_n\) to D65, we would effectively be applying the white point twice, resulting in a tinted image. By normalizing \(\sum W_n\) to \((1, 1, 1)\), we ensure that the energy is conserved through the spectral transformation pipeline.</p>
|
||||
<p>In practice, we calculate the raw sum of \(W_n\) from the quadrature weights and CMFs, then compute a correction factor:</p>
|
||||
<p>$$ \text{Correction} = \frac{1.0}{\sum W_{n,raw}} $$
|
||||
$$ W_{n,final} = W_{n,raw} \cdot \text{Correction} $$</p>
|
||||
<h3 id="4-dispersion-and-ior-offsets"><a class="header" href="#4-dispersion-and-ior-offsets">4. Dispersion and IOR Offsets</a></h3>
|
||||
<p>The Index of Refraction (IOR) varies with wavelength. We use the Abbe number (\(V_d\)) to parameterize this variation relative to a base IOR (\(n_D\)) at 589.3nm.</p>
|
||||
<p><strong>Cauchy Dispersion Model:</strong></p>
|
||||
<p>$$ n(\lambda) = A + \frac{B}{\lambda^2} $$</p>
|
||||
<p>Using the definition of Abbe number \(V_d = \frac{n_D - 1}{n_F - n_C}\), we can derive:</p>
|
||||
<p>$$ n(\lambda) = n_D + \frac{n_D - 1}{V_d} \cdot \text{Offset}(\lambda) $$</p>
|
||||
<p>Where \(\text{Offset}(\lambda)\) is pre-calculated by this tool:</p>
|
||||
<p>$$ \text{Offset}(\lambda) = \frac{ \frac{1}{\lambda^2} - \frac{1}{\lambda_D^2} }{ \frac{1}{\lambda_F^2} - \frac{1}{\lambda_C^2} } $$</p>
|
||||
<p>This allows the shader to compute the specific IOR for each sample efficiently:</p>
|
||||
<pre><code class="language-glsl">float ior_n = baseIOR + dispersionFactor * offsets[n];
|
||||
</code></pre>
|
||||
|
||||
</main>
|
||||
|
||||
<nav class="nav-wrapper" aria-label="Page navigation">
|
||||
<!-- Mobile navigation buttons -->
|
||||
<a rel="prev" href="../dup/roughness_prefilter.html" class="mobile-nav-chapters previous" title="Previous chapter" aria-label="Previous chapter" aria-keyshortcuts="Left">
|
||||
<i class="fa fa-angle-left"></i>
|
||||
</a>
|
||||
|
||||
<a rel="next prefetch" href="../dup/specular_color.html" class="mobile-nav-chapters next" title="Next chapter" aria-label="Next chapter" aria-keyshortcuts="Right">
|
||||
<i class="fa fa-angle-right"></i>
|
||||
</a>
|
||||
|
||||
<div style="clear: both"></div>
|
||||
</nav>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<nav class="nav-wide-wrapper" aria-label="Page navigation">
|
||||
<a rel="prev" href="../dup/roughness_prefilter.html" class="nav-chapters previous" title="Previous chapter" aria-label="Previous chapter" aria-keyshortcuts="Left">
|
||||
<i class="fa fa-angle-left"></i>
|
||||
</a>
|
||||
|
||||
<a rel="next prefetch" href="../dup/specular_color.html" class="nav-chapters next" title="Next chapter" aria-label="Next chapter" aria-keyshortcuts="Right">
|
||||
<i class="fa fa-angle-right"></i>
|
||||
</a>
|
||||
</nav>
|
||||
|
||||
</div>
|
||||
|
||||
|
||||
|
||||
|
||||
<script>
|
||||
window.playground_copyable = true;
|
||||
</script>
|
||||
|
||||
|
||||
<script src="../elasticlunr.min.js"></script>
|
||||
<script src="../mark.min.js"></script>
|
||||
<script src="../searcher.js"></script>
|
||||
|
||||
<script src="../clipboard.min.js"></script>
|
||||
<script src="../highlight.js"></script>
|
||||
<script src="../book.js"></script>
|
||||
|
||||
<!-- Custom JS scripts -->
|
||||
|
||||
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
||||
@@ -182,7 +182,7 @@ grazing angles. See Hoffman 2019, "Fresnel Equations Considered Harmful".</p>
|
||||
|
||||
<nav class="nav-wrapper" aria-label="Page navigation">
|
||||
<!-- Mobile navigation buttons -->
|
||||
<a rel="prev" href="../dup/specgen.html" class="mobile-nav-chapters previous" title="Previous chapter" aria-label="Previous chapter" aria-keyshortcuts="Left">
|
||||
<a rel="prev" href="../dup/roughness_prefilter.html" class="mobile-nav-chapters previous" title="Previous chapter" aria-label="Previous chapter" aria-keyshortcuts="Left">
|
||||
<i class="fa fa-angle-left"></i>
|
||||
</a>
|
||||
|
||||
@@ -196,7 +196,7 @@ grazing angles. See Hoffman 2019, "Fresnel Equations Considered Harmful".</p>
|
||||
</div>
|
||||
|
||||
<nav class="nav-wide-wrapper" aria-label="Page navigation">
|
||||
<a rel="prev" href="../dup/specgen.html" class="nav-chapters previous" title="Previous chapter" aria-label="Previous chapter" aria-keyshortcuts="Left">
|
||||
<a rel="prev" href="../dup/roughness_prefilter.html" class="nav-chapters previous" title="Previous chapter" aria-label="Previous chapter" aria-keyshortcuts="Left">
|
||||
<i class="fa fa-angle-left"></i>
|
||||
</a>
|
||||
|
||||
|
||||
@@ -1,230 +0,0 @@
|
||||
<!DOCTYPE HTML>
|
||||
<html lang="en" class="light sidebar-visible" dir="ltr">
|
||||
<head>
|
||||
<!-- Book generated using mdBook -->
|
||||
<meta charset="UTF-8">
|
||||
<title>CI: backend - Filament</title>
|
||||
|
||||
|
||||
<!-- Custom HTML head -->
|
||||
|
||||
<meta name="description" content="">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||
<meta name="theme-color" content="#ffffff">
|
||||
|
||||
<link rel="shortcut icon" href="../favicon.png">
|
||||
<link rel="stylesheet" href="../css/variables.css">
|
||||
<link rel="stylesheet" href="../css/general.css">
|
||||
<link rel="stylesheet" href="../css/chrome.css">
|
||||
|
||||
<!-- Fonts -->
|
||||
<link rel="stylesheet" href="../FontAwesome/css/font-awesome.css">
|
||||
<link rel="stylesheet" href="../fonts/fonts.css">
|
||||
|
||||
<!-- Highlight.js Stylesheets -->
|
||||
<link rel="stylesheet" href="../highlight.css">
|
||||
<link rel="stylesheet" href="../tomorrow-night.css">
|
||||
<link rel="stylesheet" href="../ayu-highlight.css">
|
||||
|
||||
<!-- Custom theme stylesheets -->
|
||||
|
||||
<!-- MathJax -->
|
||||
<script async src="https://cdnjs.cloudflare.com/ajax/libs/mathjax/2.7.1/MathJax.js?config=TeX-AMS-MML_HTMLorMML"></script>
|
||||
|
||||
<!-- Provide site root to javascript -->
|
||||
<script>
|
||||
var path_to_root = "../";
|
||||
var default_theme = window.matchMedia("(prefers-color-scheme: dark)").matches ? "light" : "light";
|
||||
</script>
|
||||
<!-- Start loading toc.js asap -->
|
||||
<script src="../toc.js"></script>
|
||||
</head>
|
||||
<body>
|
||||
<div id="body-container">
|
||||
<!-- Work around some values being stored in localStorage wrapped in quotes -->
|
||||
<script>
|
||||
try {
|
||||
var theme = localStorage.getItem('mdbook-theme');
|
||||
var sidebar = localStorage.getItem('mdbook-sidebar');
|
||||
|
||||
if (theme.startsWith('"') && theme.endsWith('"')) {
|
||||
localStorage.setItem('mdbook-theme', theme.slice(1, theme.length - 1));
|
||||
}
|
||||
|
||||
if (sidebar.startsWith('"') && sidebar.endsWith('"')) {
|
||||
localStorage.setItem('mdbook-sidebar', sidebar.slice(1, sidebar.length - 1));
|
||||
}
|
||||
} catch (e) { }
|
||||
</script>
|
||||
|
||||
<!-- Set the theme before any content is loaded, prevents flash -->
|
||||
<script>
|
||||
var theme;
|
||||
try { theme = localStorage.getItem('mdbook-theme'); } catch(e) { }
|
||||
if (theme === null || theme === undefined) { theme = default_theme; }
|
||||
const html = document.documentElement;
|
||||
html.classList.remove('light')
|
||||
html.classList.add(theme);
|
||||
html.classList.add("js");
|
||||
</script>
|
||||
|
||||
<input type="checkbox" id="sidebar-toggle-anchor" class="hidden">
|
||||
|
||||
<!-- Hide / unhide sidebar before it is displayed -->
|
||||
<script>
|
||||
var sidebar = null;
|
||||
var sidebar_toggle = document.getElementById("sidebar-toggle-anchor");
|
||||
if (document.body.clientWidth >= 1080) {
|
||||
try { sidebar = localStorage.getItem('mdbook-sidebar'); } catch(e) { }
|
||||
sidebar = sidebar || 'visible';
|
||||
} else {
|
||||
sidebar = 'hidden';
|
||||
}
|
||||
sidebar_toggle.checked = sidebar === 'visible';
|
||||
html.classList.remove('sidebar-visible');
|
||||
html.classList.add("sidebar-" + sidebar);
|
||||
</script>
|
||||
|
||||
<nav id="sidebar" class="sidebar" aria-label="Table of contents">
|
||||
<div style="display:flex;align-items:center;justify-content:center">
|
||||
<img class="flogo" src="../images/filament_logo_small.png"></img>
|
||||
</div>
|
||||
<!-- populated by js -->
|
||||
<mdbook-sidebar-scrollbox class="sidebar-scrollbox"></mdbook-sidebar-scrollbox>
|
||||
<noscript>
|
||||
<iframe class="sidebar-iframe-outer" src="../toc.html"></iframe>
|
||||
</noscript>
|
||||
<div id="sidebar-resize-handle" class="sidebar-resize-handle">
|
||||
<div class="sidebar-resize-indicator"></div>
|
||||
</div>
|
||||
</nav>
|
||||
|
||||
<div id="page-wrapper" class="page-wrapper">
|
||||
|
||||
<div class="page">
|
||||
<div id="menu-bar-hover-placeholder"></div>
|
||||
<div id="menu-bar" class="menu-bar sticky">
|
||||
<div class="left-buttons">
|
||||
<label id="sidebar-toggle" class="icon-button" for="sidebar-toggle-anchor" title="Toggle Table of Contents" aria-label="Toggle Table of Contents" aria-controls="sidebar">
|
||||
<i class="fa fa-bars"></i>
|
||||
</label>
|
||||
<!-- Filament: disable themes because the markdeep part does not look good for dark themes -->
|
||||
<!--
|
||||
<button id="theme-toggle" class="icon-button" type="button" title="Change theme" aria-label="Change theme" aria-haspopup="true" aria-expanded="false" aria-controls="theme-list">
|
||||
<i class="fa fa-paint-brush"></i>
|
||||
</button>
|
||||
<ul id="theme-list" class="theme-popup" aria-label="Themes" role="menu">
|
||||
<li role="none"><button role="menuitem" class="theme" id="light">Light</button></li>
|
||||
<li role="none"><button role="menuitem" class="theme" id="rust">Rust</button></li>
|
||||
<li role="none"><button role="menuitem" class="theme" id="coal">Coal</button></li>
|
||||
<li role="none"><button role="menuitem" class="theme" id="navy">Navy</button></li>
|
||||
<li role="none"><button role="menuitem" class="theme" id="ayu">Ayu</button></li>
|
||||
</ul>
|
||||
-->
|
||||
<button id="search-toggle" class="icon-button" type="button" title="Search. (Shortkey: s)" aria-label="Toggle Searchbar" aria-expanded="false" aria-keyshortcuts="S" aria-controls="searchbar">
|
||||
<i class="fa fa-search"></i>
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<h1 class="menu-title">Filament</h1>
|
||||
|
||||
<div class="right-buttons">
|
||||
<a href="https://github.com/google/filament" title="Git repository" aria-label="Git repository">
|
||||
<i id="git-repository-button" class="fa fa-github"></i>
|
||||
</a>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div id="search-wrapper" class="hidden">
|
||||
<form id="searchbar-outer" class="searchbar-outer">
|
||||
<input type="search" id="searchbar" name="searchbar" placeholder="Search this book ..." aria-controls="searchresults-outer" aria-describedby="searchresults-header">
|
||||
</form>
|
||||
<div id="searchresults-outer" class="searchresults-outer hidden">
|
||||
<div id="searchresults-header" class="searchresults-header"></div>
|
||||
<ul id="searchresults">
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Apply ARIA attributes after the sidebar and the sidebar toggle button are added to the DOM -->
|
||||
<script>
|
||||
document.getElementById('sidebar-toggle').setAttribute('aria-expanded', sidebar === 'visible');
|
||||
document.getElementById('sidebar').setAttribute('aria-hidden', sidebar !== 'visible');
|
||||
Array.from(document.querySelectorAll('#sidebar a')).forEach(function(link) {
|
||||
link.setAttribute('tabIndex', sidebar === 'visible' ? 0 : -1);
|
||||
});
|
||||
</script>
|
||||
|
||||
<div id="content" class="content">
|
||||
<main>
|
||||
<h1 id="backend-tests"><a class="header" href="#backend-tests">Backend Tests</a></h1>
|
||||
<p>This directory (<code>/test/backend</code>) contains scripts to run Filament's backend tests using a software
|
||||
rasterizer (Mesa). This is useful for running tests in a continuous integration environment where a
|
||||
GPU might not be available.</p>
|
||||
<p>The <code>test.sh</code> script will:</p>
|
||||
<ol>
|
||||
<li>Build Mesa if needed.</li>
|
||||
<li>Build the backend tests.</li>
|
||||
<li>Run the tests using the OpenGL and Vulkan backends with Mesa.</li>
|
||||
</ol>
|
||||
<h2 id="flags"><a class="header" href="#flags">Flags</a></h2>
|
||||
<p>The <code>test.sh</code> script accepts the following flags:</p>
|
||||
<ul>
|
||||
<li><code>--gtest_filter</code>: Filters the tests that are run. This is passed directly to the underlying
|
||||
gtest executable.</li>
|
||||
<li><code>--backend</code>: Specifies which backend to test. Can be <code>opengl</code>, <code>vulkan</code>, or a comma-separated
|
||||
list (e.g., <code>opengl,vulkan</code>). Defaults to both.</li>
|
||||
</ul>
|
||||
|
||||
</main>
|
||||
|
||||
<nav class="nav-wrapper" aria-label="Page navigation">
|
||||
<!-- Mobile navigation buttons -->
|
||||
<a rel="prev" href="../dup/backend_test.html" class="mobile-nav-chapters previous" title="Previous chapter" aria-label="Previous chapter" aria-keyshortcuts="Left">
|
||||
<i class="fa fa-angle-left"></i>
|
||||
</a>
|
||||
|
||||
<a rel="next prefetch" href="../dup/test_ci_renderdiff.html" class="mobile-nav-chapters next" title="Next chapter" aria-label="Next chapter" aria-keyshortcuts="Right">
|
||||
<i class="fa fa-angle-right"></i>
|
||||
</a>
|
||||
|
||||
<div style="clear: both"></div>
|
||||
</nav>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<nav class="nav-wide-wrapper" aria-label="Page navigation">
|
||||
<a rel="prev" href="../dup/backend_test.html" class="nav-chapters previous" title="Previous chapter" aria-label="Previous chapter" aria-keyshortcuts="Left">
|
||||
<i class="fa fa-angle-left"></i>
|
||||
</a>
|
||||
|
||||
<a rel="next prefetch" href="../dup/test_ci_renderdiff.html" class="nav-chapters next" title="Next chapter" aria-label="Next chapter" aria-keyshortcuts="Right">
|
||||
<i class="fa fa-angle-right"></i>
|
||||
</a>
|
||||
</nav>
|
||||
|
||||
</div>
|
||||
|
||||
|
||||
|
||||
|
||||
<script>
|
||||
window.playground_copyable = true;
|
||||
</script>
|
||||
|
||||
|
||||
<script src="../elasticlunr.min.js"></script>
|
||||
<script src="../mark.min.js"></script>
|
||||
<script src="../searcher.js"></script>
|
||||
|
||||
<script src="../clipboard.min.js"></script>
|
||||
<script src="../highlight.js"></script>
|
||||
<script src="../book.js"></script>
|
||||
|
||||
<!-- Custom JS scripts -->
|
||||
|
||||
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
||||
@@ -158,7 +158,11 @@
|
||||
|
||||
<div id="content" class="content">
|
||||
<main>
|
||||
<h1 id="uberz"><a class="header" href="#uberz">uberz</a></h1>
|
||||
<ul>
|
||||
<li><a href="#ubershader-archive-files">Ubershader Archive Files</a></li>
|
||||
<li><a href="#ubershader-spec-files">Ubershader Spec Files</a></li>
|
||||
</ul>
|
||||
<h1 id="ubershader-archive-files"><a class="header" href="#ubershader-archive-files">Ubershader Archive Files</a></h1>
|
||||
<p>An ubershader archive provides a way to bundle up a set of <code>filamat</code> files along with some metadata
|
||||
that conveys which glTF features each material can handle. It is a file that has been compressed
|
||||
with <code>zstd</code> and has an <code>.uberz</code> file extension. In uncompressed form, it has the following layout
|
||||
|
||||
@@ -504,9 +504,9 @@ D_{GGX}(h,\alpha) = \frac{\aa}{\pi ( (\NoH)^2 (\aa - 1) + 1)^2}
|
||||
\end{equation}$$
|
||||
</p><p>
|
||||
The GLSL implementation of the NDF, shown in <a href="#listing_speculard">listing 1</a>, is simple and efficient.
|
||||
</p><pre class="listing tilde"><code><span class="line"><span class="hljs-function"><span class="hljs-type">float</span> <span class="hljs-title">D_GGX</span><span class="hljs-params">(<span class="hljs-type">float</span> NoH, <span class="hljs-type">float</span> roughness)</span> </span>{</span>
|
||||
<span class="line"> <span class="hljs-type">float</span> a = NoH * roughness;</span>
|
||||
<span class="line"> <span class="hljs-type">float</span> k = roughness / (<span class="hljs-number">1.0</span> - NoH * NoH + a * a);</span>
|
||||
</p><pre class="listing tilde"><code><span class="line"><span class="hljs-function"><span class="hljs-built_in">float</span> <span class="hljs-title">D_GGX</span>(<span class="hljs-params"><span class="hljs-built_in">float</span> NoH, <span class="hljs-built_in">float</span> roughness</span>)</span> {</span>
|
||||
<span class="line"> <span class="hljs-built_in">float</span> a = NoH * roughness;</span>
|
||||
<span class="line"> <span class="hljs-built_in">float</span> k = roughness / (<span class="hljs-number">1.0</span> - NoH * NoH + a * a);</span>
|
||||
<span class="line"> <span class="hljs-keyword">return</span> k * k * (<span class="hljs-number">1.0</span> / PI);</span>
|
||||
<span class="line">}</span></code></pre><center><div class="listingcaption tilde"><a class="target" name="listing_speculard"> </a><b style="font-style:normal;">Listing 1:</b> Implementation of the specular D term in GLSL</div></center>
|
||||
<p>
|
||||
@@ -590,10 +590,10 @@ V(v,l,\alpha) = \frac{0.5}{\NoL \sqrt{(\NoV)^2 (1 - \aa) + \aa} + \NoV \sqrt{(\N
|
||||
\end{equation}$$
|
||||
</p><p>
|
||||
The GLSL implementation of the visibility term, shown in <a href="#listing_specularv">listing 3</a>, is a bit more expensive than we would like since it requires two <code>sqrt</code> operations.
|
||||
</p><pre class="listing tilde"><code><span class="line"><span class="hljs-function"><span class="hljs-type">float</span> <span class="hljs-title">V_SmithGGXCorrelated</span><span class="hljs-params">(<span class="hljs-type">float</span> NoV, <span class="hljs-type">float</span> NoL, <span class="hljs-type">float</span> roughness)</span> </span>{</span>
|
||||
<span class="line"> <span class="hljs-type">float</span> a2 = roughness * roughness;</span>
|
||||
<span class="line"> <span class="hljs-type">float</span> GGXV = NoL * <span class="hljs-built_in">sqrt</span>(NoV * NoV * (<span class="hljs-number">1.0</span> - a2) + a2);</span>
|
||||
<span class="line"> <span class="hljs-type">float</span> GGXL = NoV * <span class="hljs-built_in">sqrt</span>(NoL * NoL * (<span class="hljs-number">1.0</span> - a2) + a2);</span>
|
||||
</p><pre class="listing tilde"><code><span class="line"><span class="hljs-function"><span class="hljs-keyword">float</span> <span class="hljs-title">V_SmithGGXCorrelated</span><span class="hljs-params">(<span class="hljs-keyword">float</span> NoV, <span class="hljs-keyword">float</span> NoL, <span class="hljs-keyword">float</span> roughness)</span> </span>{</span>
|
||||
<span class="line"> <span class="hljs-keyword">float</span> a2 = roughness * roughness;</span>
|
||||
<span class="line"> <span class="hljs-keyword">float</span> GGXV = NoL * <span class="hljs-built_in">sqrt</span>(NoV * NoV * (<span class="hljs-number">1.0</span> - a2) + a2);</span>
|
||||
<span class="line"> <span class="hljs-keyword">float</span> GGXL = NoV * <span class="hljs-built_in">sqrt</span>(NoL * NoL * (<span class="hljs-number">1.0</span> - a2) + a2);</span>
|
||||
<span class="line"> <span class="hljs-keyword">return</span> <span class="hljs-number">0.5</span> / (GGXV + GGXL);</span>
|
||||
<span class="line">}</span></code></pre><center><div class="listingcaption tilde"><a class="target" name="listing_specularv"> </a><b style="font-style:normal;">Listing 3:</b> Implementation of the specular V term in GLSL</div></center>
|
||||
<p>
|
||||
@@ -604,10 +604,10 @@ V(v,l,\alpha) = \frac{0.5}{\NoL (\NoV (1 - \alpha) + \alpha) + \NoV (\NoL (1 - \
|
||||
\end{equation}$$
|
||||
</p><p>
|
||||
This approximation is mathematically wrong but saves two square root operations and is good enough for real-time mobile applications, as shown in <a href="#listing_approximatedspecularv">listing 4</a>.
|
||||
</p><pre class="listing tilde"><code><span class="line"><span class="hljs-function"><span class="hljs-type">float</span> <span class="hljs-title">V_SmithGGXCorrelatedFast</span><span class="hljs-params">(<span class="hljs-type">float</span> NoV, <span class="hljs-type">float</span> NoL, <span class="hljs-type">float</span> roughness)</span> </span>{</span>
|
||||
<span class="line"> <span class="hljs-type">float</span> a = roughness;</span>
|
||||
<span class="line"> <span class="hljs-type">float</span> GGXV = NoL * (NoV * (<span class="hljs-number">1.0</span> - a) + a);</span>
|
||||
<span class="line"> <span class="hljs-type">float</span> GGXL = NoV * (NoL * (<span class="hljs-number">1.0</span> - a) + a);</span>
|
||||
</p><pre class="listing tilde"><code><span class="line"><span class="hljs-function"><span class="hljs-built_in">float</span> <span class="hljs-title">V_SmithGGXCorrelatedFast</span>(<span class="hljs-params"><span class="hljs-built_in">float</span> NoV, <span class="hljs-built_in">float</span> NoL, <span class="hljs-built_in">float</span> roughness</span>)</span> {</span>
|
||||
<span class="line"> <span class="hljs-built_in">float</span> a = roughness;</span>
|
||||
<span class="line"> <span class="hljs-built_in">float</span> GGXV = NoL * (NoV * (<span class="hljs-number">1.0</span> - a) + a);</span>
|
||||
<span class="line"> <span class="hljs-built_in">float</span> GGXL = NoV * (NoL * (<span class="hljs-number">1.0</span> - a) + a);</span>
|
||||
<span class="line"> <span class="hljs-keyword">return</span> <span class="hljs-number">0.5</span> / (GGXV + GGXL);</span>
|
||||
<span class="line">}</span></code></pre><center><div class="listingcaption tilde"><a class="target" name="listing_approximatedspecularv"> </a><b style="font-style:normal;">Listing 4:</b> Implementation of the approximated specular V term in GLSL</div></center>
|
||||
<p>
|
||||
@@ -659,7 +659,7 @@ $$\begin{equation}
|
||||
\end{equation}$$
|
||||
</p><p>
|
||||
In practice, the diffuse reflectance \(\sigma\) is multiplied later, as shown in <a href="#listing_diffusebrdf">listing 8</a>.
|
||||
</p><pre class="listing tilde"><code><span class="line"><span class="hljs-function"><span class="hljs-built_in">float</span> <span class="hljs-title">Fd_Lambert</span>()</span> {</span>
|
||||
</p><pre class="listing tilde"><code><span class="line"><span class="hljs-function"><span class="hljs-built_in">float</span> <span class="hljs-title">Fd_Lambert</span>(<span class="hljs-params"></span>)</span> {</span>
|
||||
<span class="line"> <span class="hljs-keyword">return</span> <span class="hljs-number">1.0</span> / PI;</span>
|
||||
<span class="line">}</span>
|
||||
<span class="line"></span>
|
||||
@@ -680,14 +680,14 @@ Where:
|
||||
$$\begin{equation}
|
||||
\fGrazing=0.5 + 2 \cdot \alpha cos^2(\theta_d)
|
||||
\end{equation}$$
|
||||
</p><pre class="listing tilde"><code><span class="line"><span class="hljs-type">float</span> <span class="hljs-title function_">F_Schlick</span><span class="hljs-params">(<span class="hljs-type">float</span> u, <span class="hljs-type">float</span> f0, <span class="hljs-type">float</span> f90)</span> {</span>
|
||||
<span class="line"> <span class="hljs-keyword">return</span> f0 + (f90 - f0) * pow(<span class="hljs-number">1.0</span> - u, <span class="hljs-number">5.0</span>);</span>
|
||||
</p><pre class="listing tilde"><code><span class="line"><span class="hljs-function"><span class="hljs-keyword">float</span> <span class="hljs-title">F_Schlick</span><span class="hljs-params">(<span class="hljs-keyword">float</span> u, <span class="hljs-keyword">float</span> f0, <span class="hljs-keyword">float</span> f90)</span> </span>{</span>
|
||||
<span class="line"> <span class="hljs-keyword">return</span> f0 + (f90 - f0) * <span class="hljs-built_in">pow</span>(<span class="hljs-number">1.0</span> - u, <span class="hljs-number">5.0</span>);</span>
|
||||
<span class="line">}</span>
|
||||
<span class="line"></span>
|
||||
<span class="line"><span class="hljs-type">float</span> <span class="hljs-title function_">Fd_Burley</span><span class="hljs-params">(<span class="hljs-type">float</span> NoV, <span class="hljs-type">float</span> NoL, <span class="hljs-type">float</span> LoH, <span class="hljs-type">float</span> roughness)</span> {</span>
|
||||
<span class="line"> <span class="hljs-type">float</span> <span class="hljs-variable">f90</span> <span class="hljs-operator">=</span> <span class="hljs-number">0.5</span> + <span class="hljs-number">2.0</span> * roughness * LoH * LoH;</span>
|
||||
<span class="line"> <span class="hljs-type">float</span> <span class="hljs-variable">lightScatter</span> <span class="hljs-operator">=</span> F_Schlick(NoL, <span class="hljs-number">1.0</span>, f90);</span>
|
||||
<span class="line"> <span class="hljs-type">float</span> <span class="hljs-variable">viewScatter</span> <span class="hljs-operator">=</span> F_Schlick(NoV, <span class="hljs-number">1.0</span>, f90);</span>
|
||||
<span class="line"><span class="hljs-function"><span class="hljs-keyword">float</span> <span class="hljs-title">Fd_Burley</span><span class="hljs-params">(<span class="hljs-keyword">float</span> NoV, <span class="hljs-keyword">float</span> NoL, <span class="hljs-keyword">float</span> LoH, <span class="hljs-keyword">float</span> roughness)</span> </span>{</span>
|
||||
<span class="line"> <span class="hljs-keyword">float</span> f90 = <span class="hljs-number">0.5</span> + <span class="hljs-number">2.0</span> * roughness * LoH * LoH;</span>
|
||||
<span class="line"> <span class="hljs-keyword">float</span> lightScatter = F_Schlick(NoL, <span class="hljs-number">1.0</span>, f90);</span>
|
||||
<span class="line"> <span class="hljs-keyword">float</span> viewScatter = F_Schlick(NoV, <span class="hljs-number">1.0</span>, f90);</span>
|
||||
<span class="line"> <span class="hljs-keyword">return</span> lightScatter * viewScatter * (<span class="hljs-number">1.0</span> / PI);</span>
|
||||
<span class="line">}</span></code></pre><center><div class="listingcaption tilde"><a class="target" name="listing_diffusebrdf"> </a><b style="font-style:normal;">Listing 8:</b> Implementation of the diffuse Disney BRDF in GLSL</div></center>
|
||||
<p>
|
||||
@@ -704,47 +704,47 @@ We could allow artists/developers to choose the Disney diffuse BRDF depending on
|
||||
<strong class="asterisk">Diffuse term</strong>: a Lambertian diffuse model.
|
||||
</p><p>
|
||||
The full GLSL implementation of the standard model is shown in <a href="#listing_glslbrdf">listing 9</a>.
|
||||
</p><pre class="listing tilde"><code><span class="line"><span class="hljs-type">float</span> <span class="hljs-title function_">D_GGX</span><span class="hljs-params">(<span class="hljs-type">float</span> NoH, <span class="hljs-type">float</span> a)</span> {</span>
|
||||
<span class="line"> <span class="hljs-type">float</span> <span class="hljs-variable">a2</span> <span class="hljs-operator">=</span> a * a;</span>
|
||||
<span class="line"> <span class="hljs-type">float</span> <span class="hljs-variable">f</span> <span class="hljs-operator">=</span> (NoH * a2 - NoH) * NoH + <span class="hljs-number">1.0</span>;</span>
|
||||
</p><pre class="listing tilde"><code><span class="line"><span class="hljs-type">float</span> D_GGX(<span class="hljs-type">float</span> NoH, <span class="hljs-type">float</span> a) {</span>
|
||||
<span class="line"> <span class="hljs-type">float</span> a2 = a * a;</span>
|
||||
<span class="line"> <span class="hljs-type">float</span> f = (NoH * a2 - NoH) * NoH + <span class="hljs-number">1.0</span>;</span>
|
||||
<span class="line"> <span class="hljs-keyword">return</span> a2 / (PI * f * f);</span>
|
||||
<span class="line">}</span>
|
||||
<span class="line"></span>
|
||||
<span class="line">vec3 <span class="hljs-title function_">F_Schlick</span><span class="hljs-params">(<span class="hljs-type">float</span> u, vec3 f0)</span> {</span>
|
||||
<span class="line"> <span class="hljs-keyword">return</span> f0 + (vec3(<span class="hljs-number">1.0</span>) - f0) * pow(<span class="hljs-number">1.0</span> - u, <span class="hljs-number">5.0</span>);</span>
|
||||
<span class="line"><span class="hljs-type">vec3</span> F_Schlick(<span class="hljs-type">float</span> u, <span class="hljs-type">vec3</span> f0) {</span>
|
||||
<span class="line"> <span class="hljs-keyword">return</span> f0 + (<span class="hljs-type">vec3</span>(<span class="hljs-number">1.0</span>) - f0) * <span class="hljs-built_in">pow</span>(<span class="hljs-number">1.0</span> - u, <span class="hljs-number">5.0</span>);</span>
|
||||
<span class="line">}</span>
|
||||
<span class="line"></span>
|
||||
<span class="line"><span class="hljs-type">float</span> <span class="hljs-title function_">V_SmithGGXCorrelated</span><span class="hljs-params">(<span class="hljs-type">float</span> NoV, <span class="hljs-type">float</span> NoL, <span class="hljs-type">float</span> a)</span> {</span>
|
||||
<span class="line"> <span class="hljs-type">float</span> <span class="hljs-variable">a2</span> <span class="hljs-operator">=</span> a * a;</span>
|
||||
<span class="line"> <span class="hljs-type">float</span> <span class="hljs-variable">GGXL</span> <span class="hljs-operator">=</span> NoV * sqrt((-NoL * a2 + NoL) * NoL + a2);</span>
|
||||
<span class="line"> <span class="hljs-type">float</span> <span class="hljs-variable">GGXV</span> <span class="hljs-operator">=</span> NoL * sqrt((-NoV * a2 + NoV) * NoV + a2);</span>
|
||||
<span class="line"><span class="hljs-type">float</span> V_SmithGGXCorrelated(<span class="hljs-type">float</span> NoV, <span class="hljs-type">float</span> NoL, <span class="hljs-type">float</span> a) {</span>
|
||||
<span class="line"> <span class="hljs-type">float</span> a2 = a * a;</span>
|
||||
<span class="line"> <span class="hljs-type">float</span> GGXL = NoV * <span class="hljs-built_in">sqrt</span>((-NoL * a2 + NoL) * NoL + a2);</span>
|
||||
<span class="line"> <span class="hljs-type">float</span> GGXV = NoL * <span class="hljs-built_in">sqrt</span>((-NoV * a2 + NoV) * NoV + a2);</span>
|
||||
<span class="line"> <span class="hljs-keyword">return</span> <span class="hljs-number">0.5</span> / (GGXV + GGXL);</span>
|
||||
<span class="line">}</span>
|
||||
<span class="line"></span>
|
||||
<span class="line"><span class="hljs-type">float</span> <span class="hljs-title function_">Fd_Lambert</span><span class="hljs-params">()</span> {</span>
|
||||
<span class="line"><span class="hljs-type">float</span> Fd_Lambert() {</span>
|
||||
<span class="line"> <span class="hljs-keyword">return</span> <span class="hljs-number">1.0</span> / PI;</span>
|
||||
<span class="line">}</span>
|
||||
<span class="line"></span>
|
||||
<span class="line"><span class="hljs-keyword">void</span> <span class="hljs-title function_">BRDF</span><span class="hljs-params">(...)</span> {</span>
|
||||
<span class="line"> <span class="hljs-type">vec3</span> <span class="hljs-variable">h</span> <span class="hljs-operator">=</span> normalize(v + l);</span>
|
||||
<span class="line"><span class="hljs-type">void</span> BRDF(...) {</span>
|
||||
<span class="line"> <span class="hljs-type">vec3</span> h = <span class="hljs-built_in">normalize</span>(v + l);</span>
|
||||
<span class="line"></span>
|
||||
<span class="line"> <span class="hljs-type">float</span> <span class="hljs-variable">NoV</span> <span class="hljs-operator">=</span> abs(dot(n, v)) + <span class="hljs-number">1e-5</span>;</span>
|
||||
<span class="line"> <span class="hljs-type">float</span> <span class="hljs-variable">NoL</span> <span class="hljs-operator">=</span> clamp(dot(n, l), <span class="hljs-number">0.0</span>, <span class="hljs-number">1.0</span>);</span>
|
||||
<span class="line"> <span class="hljs-type">float</span> <span class="hljs-variable">NoH</span> <span class="hljs-operator">=</span> clamp(dot(n, h), <span class="hljs-number">0.0</span>, <span class="hljs-number">1.0</span>);</span>
|
||||
<span class="line"> <span class="hljs-type">float</span> <span class="hljs-variable">LoH</span> <span class="hljs-operator">=</span> clamp(dot(l, h), <span class="hljs-number">0.0</span>, <span class="hljs-number">1.0</span>);</span>
|
||||
<span class="line"> <span class="hljs-type">float</span> NoV = <span class="hljs-built_in">abs</span>(<span class="hljs-built_in">dot</span>(n, v)) + <span class="hljs-number">1e-5</span>;</span>
|
||||
<span class="line"> <span class="hljs-type">float</span> NoL = <span class="hljs-built_in">clamp</span>(<span class="hljs-built_in">dot</span>(n, l), <span class="hljs-number">0.0</span>, <span class="hljs-number">1.0</span>);</span>
|
||||
<span class="line"> <span class="hljs-type">float</span> NoH = <span class="hljs-built_in">clamp</span>(<span class="hljs-built_in">dot</span>(n, h), <span class="hljs-number">0.0</span>, <span class="hljs-number">1.0</span>);</span>
|
||||
<span class="line"> <span class="hljs-type">float</span> LoH = <span class="hljs-built_in">clamp</span>(<span class="hljs-built_in">dot</span>(l, h), <span class="hljs-number">0.0</span>, <span class="hljs-number">1.0</span>);</span>
|
||||
<span class="line"></span>
|
||||
<span class="line"> <span class="hljs-comment">// perceptually linear roughness to roughness (see parameterization)</span></span>
|
||||
<span class="line"> <span class="hljs-type">float</span> <span class="hljs-variable">roughness</span> <span class="hljs-operator">=</span> perceptualRoughness * perceptualRoughness;</span>
|
||||
<span class="line"> <span class="hljs-type">float</span> roughness = perceptualRoughness * perceptualRoughness;</span>
|
||||
<span class="line"></span>
|
||||
<span class="line"> <span class="hljs-type">float</span> <span class="hljs-variable">D</span> <span class="hljs-operator">=</span> D_GGX(NoH, roughness);</span>
|
||||
<span class="line"> <span class="hljs-type">vec3</span> <span class="hljs-variable">F</span> <span class="hljs-operator">=</span> F_Schlick(LoH, f0);</span>
|
||||
<span class="line"> <span class="hljs-type">float</span> <span class="hljs-variable">V</span> <span class="hljs-operator">=</span> V_SmithGGXCorrelated(NoV, NoL, roughness);</span>
|
||||
<span class="line"> <span class="hljs-type">float</span> D = D_GGX(NoH, roughness);</span>
|
||||
<span class="line"> <span class="hljs-type">vec3</span> F = F_Schlick(LoH, f0);</span>
|
||||
<span class="line"> <span class="hljs-type">float</span> V = V_SmithGGXCorrelated(NoV, NoL, roughness);</span>
|
||||
<span class="line"></span>
|
||||
<span class="line"> <span class="hljs-comment">// specular BRDF</span></span>
|
||||
<span class="line"> <span class="hljs-type">vec3</span> <span class="hljs-variable">Fr</span> <span class="hljs-operator">=</span> (D * V) * F;</span>
|
||||
<span class="line"> <span class="hljs-type">vec3</span> Fr = (D * V) * F;</span>
|
||||
<span class="line"></span>
|
||||
<span class="line"> <span class="hljs-comment">// diffuse BRDF</span></span>
|
||||
<span class="line"> <span class="hljs-type">vec3</span> <span class="hljs-variable">Fd</span> <span class="hljs-operator">=</span> diffuseColor * Fd_Lambert();</span>
|
||||
<span class="line"> <span class="hljs-type">vec3</span> Fd = diffuseColor * Fd_Lambert();</span>
|
||||
<span class="line"></span>
|
||||
<span class="line"> <span class="hljs-comment">// apply lighting...</span></span>
|
||||
<span class="line">}</span></code></pre><center><div class="listingcaption tilde"><a class="target" name="listing_glslbrdf"> </a><b style="font-style:normal;">Listing 9:</b> Evaluation of the BRDF in GLSL</div></center>
|
||||
@@ -965,7 +965,7 @@ $$\begin{equation}
|
||||
\end{equation}$$
|
||||
</p><p>
|
||||
<a href="#listing_fnormal">Listing 12</a> shows how \(\fNormal\) is computed for both dielectric and metallic materials. It shows that the color of the specular reflectance is derived from the base color in the metallic case.
|
||||
</p><pre class="listing tilde"><code><span class="line"><span class="hljs-symbol">vec3</span> <span class="hljs-built_in">f0</span> = <span class="hljs-number">0</span>.<span class="hljs-number">16</span> * reflectance * reflectance * (<span class="hljs-number">1</span>.<span class="hljs-number">0</span> - metallic) + baseColor * metallic<span class="hljs-comment">;</span></span></code></pre><center><div class="listingcaption tilde"><a class="target" name="listing_fnormal"> </a><b style="font-style:normal;">Listing 12:</b> Computing \(\fNormal\) for dielectric and metallic materials in GLSL</div></center>
|
||||
</p><pre class="listing tilde"><code><span class="line">vec3 f0 = 0.16 <span class="hljs-emphasis">* reflectance *</span> reflectance <span class="hljs-emphasis">* (1.0 - metallic) + baseColor *</span> metallic;</span></code></pre><center><div class="listingcaption tilde"><a class="target" name="listing_fnormal"> </a><b style="font-style:normal;">Listing 12:</b> Computing \(\fNormal\) for dielectric and metallic materials in GLSL</div></center>
|
||||
<a class="target" name="roughnessremappingandclamping"> </a><a class="target" name="materialsystem/parameterization/remapping/roughnessremappingandclamping"> </a><a class="target" name="toc4.8.3.3"> </a><h4 id="roughness-remapping-and-clamping"><a class="header" href="#roughness-remapping-and-clamping">Roughness remapping and clamping</a></h4>
|
||||
<p>
|
||||
<p>The roughness set by the user, called <code>perceptualRoughness</code> here, is remapped to a perceptually linear range using the following formulation:</p>
|
||||
@@ -1054,7 +1054,7 @@ V(l,h) = \frac{1}{4(\LoH)^2}
|
||||
This masking-shadowing function is not physically based, as shown in [<a href="#citation-heitz14">Heitz14</a>], but its simplicity makes it desirable for real-time rendering.
|
||||
</p><p>
|
||||
In summary, our clear coat BRDF is a Cook-Torrance specular microfacet model, with a GGX normal distribution function, a Kelemen visibility function, and a Schlick Fresnel function. <a href="#listing_kelemen">Listing 13</a> shows how trivial the GLSL implementation is.
|
||||
</p><pre class="listing tilde"><code><span class="line"><span class="hljs-function"><span class="hljs-type">float</span> <span class="hljs-title">V_Kelemen</span><span class="hljs-params">(<span class="hljs-type">float</span> LoH)</span> </span>{</span>
|
||||
</p><pre class="listing tilde"><code><span class="line"><span class="hljs-function"><span class="hljs-built_in">float</span> <span class="hljs-title">V_Kelemen</span>(<span class="hljs-params"><span class="hljs-built_in">float</span> LoH</span>)</span> {</span>
|
||||
<span class="line"> <span class="hljs-keyword">return</span> <span class="hljs-number">0.25</span> / (LoH * LoH);</span>
|
||||
<span class="line">}</span></code></pre><center><div class="listingcaption tilde"><a class="target" name="listing_kelemen"> </a><b style="font-style:normal;">Listing 13:</b> Implementation of the Kelemen visibility term in GLSL</div></center>
|
||||
<p>
|
||||
@@ -1097,18 +1097,18 @@ The clear coat roughness parameter is remapped and clamped in a similar way to t
|
||||
<center><div class="image" style=""><a href="../images/material_clear_coat2.png" target="_blank"><img class="markdeep" src="../images/material_clear_coat2.png" /></a><center><span class="imagecaption"><a class="target" name="figure_clearcoatroughness"> </a><b style="font-style:normal;">Figure 26:</b> Clear coat roughness varying from 0.0 (left) to 1.0 (right) with metallic set to 1.0, roughness to 0.8 and clear coat to 1.0</span></center></div></center>
|
||||
</p><p>
|
||||
<a href="#listing_clearcoatbrdf">Listing 14</a> shows the GLSL implementation of the clear coat material model after remapping, parameterization and integration in the standard surface response.
|
||||
</p><pre class="listing tilde"><code><span class="line"><span class="hljs-function"><span class="hljs-type">void</span> <span class="hljs-title">BRDF</span><span class="hljs-params">(...)</span> </span>{</span>
|
||||
</p><pre class="listing tilde"><code><span class="line"><span class="hljs-function"><span class="hljs-keyword">void</span> <span class="hljs-title">BRDF</span>(<span class="hljs-params">...</span>)</span> {</span>
|
||||
<span class="line"> <span class="hljs-comment">// compute Fd and Fr from standard model</span></span>
|
||||
<span class="line"></span>
|
||||
<span class="line"> <span class="hljs-comment">// remapping and linearization of clear coat roughness</span></span>
|
||||
<span class="line"> clearCoatPerceptualRoughness = <span class="hljs-built_in">clamp</span>(clearCoatPerceptualRoughness, <span class="hljs-number">0.089</span>, <span class="hljs-number">1.0</span>);</span>
|
||||
<span class="line"> clearCoatPerceptualRoughness = clamp(clearCoatPerceptualRoughness, <span class="hljs-number">0.089</span>, <span class="hljs-number">1.0</span>);</span>
|
||||
<span class="line"> clearCoatRoughness = clearCoatPerceptualRoughness * clearCoatPerceptualRoughness;</span>
|
||||
<span class="line"></span>
|
||||
<span class="line"> <span class="hljs-comment">// clear coat BRDF</span></span>
|
||||
<span class="line"> <span class="hljs-type">float</span> Dc = <span class="hljs-built_in">D_GGX</span>(clearCoatRoughness, NoH);</span>
|
||||
<span class="line"> <span class="hljs-type">float</span> Vc = <span class="hljs-built_in">V_Kelemen</span>(clearCoatRoughness, LoH);</span>
|
||||
<span class="line"> <span class="hljs-type">float</span> Fc = <span class="hljs-built_in">F_Schlick</span>(<span class="hljs-number">0.04</span>, LoH) * clearCoat; <span class="hljs-comment">// clear coat strength</span></span>
|
||||
<span class="line"> <span class="hljs-type">float</span> Frc = (Dc * Vc) * Fc;</span>
|
||||
<span class="line"> <span class="hljs-built_in">float</span> Dc = D_GGX(clearCoatRoughness, NoH);</span>
|
||||
<span class="line"> <span class="hljs-built_in">float</span> Vc = V_Kelemen(clearCoatRoughness, LoH);</span>
|
||||
<span class="line"> <span class="hljs-built_in">float</span> Fc = F_Schlick(<span class="hljs-number">0.04</span>, LoH) * clearCoat; <span class="hljs-comment">// clear coat strength</span></span>
|
||||
<span class="line"> <span class="hljs-built_in">float</span> Frc = (Dc * Vc) * Fc;</span>
|
||||
<span class="line"></span>
|
||||
<span class="line"> <span class="hljs-comment">// account for energy loss in the base layer</span></span>
|
||||
<span class="line"> <span class="hljs-keyword">return</span> color * ((Fd + Fr * (<span class="hljs-number">1.0</span> - Fc)) * (<span class="hljs-number">1.0</span> - Fc) + Frc);</span>
|
||||
@@ -1294,14 +1294,14 @@ f_{r}(v,h,\alpha) = \frac{D_{velvet}(v,h,\alpha)}{4(\NoL + \NoV - (\NoL)(\NoV))}
|
||||
\end{equation}$$
|
||||
</p><p>
|
||||
The implementation of the velvet NDF is presented in <a href="#listing_clothbrdf">listing 17</a>, optimized to properly fit in half float formats and to avoid computing a costly cotangent, relying instead on trigonometric identities. Note that we removed the Fresnel component from this BRDF.
|
||||
</p><pre class="listing tilde"><code><span class="line"><span class="hljs-function"><span class="hljs-type">float</span> <span class="hljs-title">D_Ashikhmin</span><span class="hljs-params">(<span class="hljs-type">float</span> roughness, <span class="hljs-type">float</span> NoH)</span> </span>{</span>
|
||||
</p><pre class="listing tilde"><code><span class="line">float D_Ashikhmin(float roughness, float NoH) {</span>
|
||||
<span class="line"> <span class="hljs-comment">// Ashikhmin 2007, "Distribution-based BRDFs"</span></span>
|
||||
<span class="line"> <span class="hljs-type">float</span> a2 = roughness * roughness;</span>
|
||||
<span class="line"> <span class="hljs-type">float</span> cos2h = NoH * NoH;</span>
|
||||
<span class="line"> <span class="hljs-type">float</span> sin2h = <span class="hljs-built_in">max</span>(<span class="hljs-number">1.0</span> - cos2h, <span class="hljs-number">0.0078125</span>); <span class="hljs-comment">// 2^(-14/2), so sin2h^2 > 0 in fp16</span></span>
|
||||
<span class="line"> <span class="hljs-type">float</span> sin4h = sin2h * sin2h;</span>
|
||||
<span class="line"> <span class="hljs-type">float</span> cot2 = -cos2h / (a2 * sin2h);</span>
|
||||
<span class="line"> <span class="hljs-keyword">return</span> <span class="hljs-number">1.0</span> / (PI * (<span class="hljs-number">4.0</span> * a2 + <span class="hljs-number">1.0</span>) * sin4h) * (<span class="hljs-number">4.0</span> * <span class="hljs-built_in">exp</span>(cot2) + sin4h);</span>
|
||||
<span class="line"> float a<span class="hljs-number">2</span> = roughness * roughness;</span>
|
||||
<span class="line"> float <span class="hljs-keyword">cos</span><span class="hljs-number">2</span>h = NoH * NoH;</span>
|
||||
<span class="line"> float <span class="hljs-keyword">sin</span><span class="hljs-number">2</span>h = <span class="hljs-keyword">max</span>(<span class="hljs-number">1.0</span> - <span class="hljs-keyword">cos</span><span class="hljs-number">2</span>h, <span class="hljs-number">0.0078125</span>); <span class="hljs-comment">// 2^(-14/2), so sin2h^2 > 0 in fp16</span></span>
|
||||
<span class="line"> float <span class="hljs-keyword">sin</span><span class="hljs-number">4</span>h = <span class="hljs-keyword">sin</span><span class="hljs-number">2</span>h * <span class="hljs-keyword">sin</span><span class="hljs-number">2</span>h;</span>
|
||||
<span class="line"> float cot<span class="hljs-number">2</span> = -<span class="hljs-keyword">cos</span><span class="hljs-number">2</span>h / (a<span class="hljs-number">2</span> * <span class="hljs-keyword">sin</span><span class="hljs-number">2</span>h);</span>
|
||||
<span class="line"> <span class="hljs-keyword">return</span> <span class="hljs-number">1.0</span> / (PI * (<span class="hljs-number">4.0</span> * a<span class="hljs-number">2</span> + <span class="hljs-number">1.0</span>) * <span class="hljs-keyword">sin</span><span class="hljs-number">4</span>h) * (<span class="hljs-number">4.0</span> * exp(cot<span class="hljs-number">2</span>) + <span class="hljs-keyword">sin</span><span class="hljs-number">4</span>h);</span>
|
||||
<span class="line">}</span></code></pre><center><div class="listingcaption tilde"><a class="target" name="listing_clothbrdf"> </a><b style="font-style:normal;">Listing 17:</b> Implementation of Ashikhmin's velvet NDF in GLSL</div></center>
|
||||
<p>
|
||||
<p>In [<a href="#citation-estevez17">Estevez17</a>] Estevez and Kulla propose a different NDF (called the “Charlie” sheen) that is based on an exponentiated sinusoidal instead of an inverted Gaussian. This NDF is appealing for several reasons: its parameterization feels more natural and intuitive, it provides a softer appearance and, as shown in equation (\ref{charlieNDF}), its implementation is simpler:</p>
|
||||
@@ -1312,11 +1312,11 @@ D(m) = \frac{(2 + \frac{1}{\alpha}) sin(\theta)^{\frac{1}{\alpha}}}{2 \pi}
|
||||
</p><p>
|
||||
[<a href="#citation-estevez17">Estevez17</a>] also presents a new shadowing term that we omit here because of its cost. We instead rely on the visibility term from [<a href="#citation-neubelt13">Neubelt13</a>] (shown in equation \(\ref{clothSpecularBRDF}\) above).
|
||||
The implementation of this NDF is presented in <a href="#listing_clothcharliebrdf">listing 18</a>, optimized to properly fit in half float formats.
|
||||
</p><pre class="listing tilde"><code><span class="line"><span class="hljs-function"><span class="hljs-type">float</span> <span class="hljs-title">D_Charlie</span><span class="hljs-params">(<span class="hljs-type">float</span> roughness, <span class="hljs-type">float</span> NoH)</span> </span>{</span>
|
||||
</p><pre class="listing tilde"><code><span class="line"><span class="hljs-function"><span class="hljs-keyword">float</span> <span class="hljs-title">D_Charlie</span><span class="hljs-params">(<span class="hljs-keyword">float</span> roughness, <span class="hljs-keyword">float</span> NoH)</span> </span>{</span>
|
||||
<span class="line"> <span class="hljs-comment">// Estevez and Kulla 2017, "Production Friendly Microfacet Sheen BRDF"</span></span>
|
||||
<span class="line"> <span class="hljs-type">float</span> invAlpha = <span class="hljs-number">1.0</span> / roughness;</span>
|
||||
<span class="line"> <span class="hljs-type">float</span> cos2h = NoH * NoH;</span>
|
||||
<span class="line"> <span class="hljs-type">float</span> sin2h = <span class="hljs-built_in">max</span>(<span class="hljs-number">1.0</span> - cos2h, <span class="hljs-number">0.0078125</span>); <span class="hljs-comment">// 2^(-14/2), so sin2h^2 > 0 in fp16</span></span>
|
||||
<span class="line"> <span class="hljs-keyword">float</span> invAlpha = <span class="hljs-number">1.0</span> / roughness;</span>
|
||||
<span class="line"> <span class="hljs-keyword">float</span> cos2h = NoH * NoH;</span>
|
||||
<span class="line"> <span class="hljs-keyword">float</span> sin2h = max(<span class="hljs-number">1.0</span> - cos2h, <span class="hljs-number">0.0078125</span>); <span class="hljs-comment">// 2^(-14/2), so sin2h^2 > 0 in fp16</span></span>
|
||||
<span class="line"> <span class="hljs-keyword">return</span> (<span class="hljs-number">2.0</span> + invAlpha) * <span class="hljs-built_in">pow</span>(sin2h, invAlpha * <span class="hljs-number">0.5</span>) / (<span class="hljs-number">2.0</span> * PI);</span>
|
||||
<span class="line">}</span></code></pre><center><div class="listingcaption tilde"><a class="target" name="listing_clothcharliebrdf"> </a><b style="font-style:normal;">Listing 18:</b> Implementation of the “Charlie” NDF in GLSL</div></center>
|
||||
<a class="target" name="sheencolor"> </a><a class="target" name="materialsystem/clothmodel/clothspecularbrdf/sheencolor"> </a><a class="target" name="toc4.12.1.1"> </a><h4 id="sheen-color"><a class="header" href="#sheen-color">Sheen color</a></h4>
|
||||
@@ -1745,21 +1745,21 @@ The photometric attenuation function can be easily implemented in GLSL by adding
|
||||
<span class="line">}</span></code></pre><center><div class="listingcaption tilde"><a class="target" name="listing_glslphotometricpunctuallight"> </a><b style="font-style:normal;">Listing 22:</b> Implementation of attenuation from photometric profiles in GLSL</div></center>
|
||||
<p>
|
||||
<p>The light intensity is computed CPU-side (<a href="#listing_photometriclightintensity">listing 23</a>) and depends on whether the photometric profile is used as a mask.</p>
|
||||
</p><pre class="listing tilde"><code><span class="line"><span class="hljs-type">float</span> multiplier;</span>
|
||||
</p><pre class="listing tilde"><code><span class="line"><span class="hljs-keyword">float</span> multiplier;</span>
|
||||
<span class="line"><span class="hljs-comment">// Photometric profile used as a mask</span></span>
|
||||
<span class="line"><span class="hljs-keyword">if</span> (photometricLight.<span class="hljs-built_in">isMasked</span>()) {</span>
|
||||
<span class="line"><span class="hljs-keyword">if</span> (photometricLight.isMasked()) {</span>
|
||||
<span class="line"> <span class="hljs-comment">// The desired intensity is set by the artist</span></span>
|
||||
<span class="line"> <span class="hljs-comment">// The integrated intensity comes from a Monte-Carlo</span></span>
|
||||
<span class="line"> <span class="hljs-comment">// integration over the unit sphere around the luminaire</span></span>
|
||||
<span class="line"> multiplier = photometricLight.<span class="hljs-built_in">getDesiredIntensity</span>() /</span>
|
||||
<span class="line"> photometricLight.<span class="hljs-built_in">getIntegratedIntensity</span>();</span>
|
||||
<span class="line"> multiplier = photometricLight.getDesiredIntensity() /</span>
|
||||
<span class="line"> photometricLight.getIntegratedIntensity();</span>
|
||||
<span class="line">} <span class="hljs-keyword">else</span> {</span>
|
||||
<span class="line"> <span class="hljs-comment">// Multiplier provided for convenience, set to 1.0 by default</span></span>
|
||||
<span class="line"> multiplier = photometricLight.<span class="hljs-built_in">getMultiplier</span>();</span>
|
||||
<span class="line"> multiplier = photometricLight.getMultiplier();</span>
|
||||
<span class="line">}</span>
|
||||
<span class="line"></span>
|
||||
<span class="line"><span class="hljs-comment">// The max intensity in cd comes from the IES profile</span></span>
|
||||
<span class="line"><span class="hljs-type">float</span> lightIntensity = photometricLight.<span class="hljs-built_in">getMaxIntensity</span>() * multiplier;</span></code></pre><center><div class="listingcaption tilde"><a class="target" name="listing_photometriclightintensity"> </a><b style="font-style:normal;">Listing 23:</b> Computing the intensity of a photometric light on the CPU</div></center>
|
||||
<span class="line"><span class="hljs-keyword">float</span> lightIntensity = photometricLight.getMaxIntensity() * multiplier;</span></code></pre><center><div class="listingcaption tilde"><a class="target" name="listing_photometriclightintensity"> </a><b style="font-style:normal;">Listing 23:</b> Computing the intensity of a photometric light on the CPU</div></center>
|
||||
<p>
|
||||
<div class="endnote"><a class="target" name="endnote-xarrowintensity"> </a><sup>4</sup> The XArrow profile declares a luminous intensity of 1,750 lm but a Monte-Carlo integration shows an intensity of only 350 lm.
|
||||
</div>
|
||||
@@ -2058,19 +2058,19 @@ In practice only 4 or 9 coefficients (i.e.: 2 or 3 bands) are enough for \(\cosT
|
||||
<center><div class="image" style=""><a href="../images/ibl/ibl_irradiance_sh2.png" target="_blank"><img class="markdeep" src="../images/ibl/ibl_irradiance_sh2.png" style="max-width:100%;" /></a><center><span class="imagecaption"><a class="target" name="figure_iblsh2"> </a><b style="font-style:normal;">Figure 52:</b> 2 bands (4 coefficients)</span></center></div></center>
|
||||
</p><p>
|
||||
In practice we pre-convolve \(\Lt\) with \(\cosTheta\) and pre-scale these coefficients by the basis scaling factors \(K_l^m\) so that the reconstruction code is as simple as possible in the shader:
|
||||
</p><pre class="listing tilde"><code><span class="line">vec3 irradianceSH(vec3 n) {</span>
|
||||
<span class="line"> // uniform vec3 sphericalHarmonics<span class="hljs-selector-attr">[9]</span></span>
|
||||
<span class="line"> // We can <span class="hljs-selector-tag">use</span> only the first <span class="hljs-number">2</span> bands for better performance</span>
|
||||
<span class="line"> return</span>
|
||||
<span class="line"> sphericalHarmonics<span class="hljs-selector-attr">[0]</span></span>
|
||||
<span class="line"> + sphericalHarmonics<span class="hljs-selector-attr">[1]</span> * (n<span class="hljs-selector-class">.y</span>)</span>
|
||||
<span class="line"> + sphericalHarmonics<span class="hljs-selector-attr">[2]</span> * (n<span class="hljs-selector-class">.z</span>)</span>
|
||||
<span class="line"> + sphericalHarmonics<span class="hljs-selector-attr">[3]</span> * (n<span class="hljs-selector-class">.x</span>)</span>
|
||||
<span class="line"> + sphericalHarmonics<span class="hljs-selector-attr">[4]</span> * (n<span class="hljs-selector-class">.y</span> * n<span class="hljs-selector-class">.x</span>)</span>
|
||||
<span class="line"> + sphericalHarmonics<span class="hljs-selector-attr">[5]</span> * (n<span class="hljs-selector-class">.y</span> * n<span class="hljs-selector-class">.z</span>)</span>
|
||||
<span class="line"> + sphericalHarmonics<span class="hljs-selector-attr">[6]</span> * (<span class="hljs-number">3.0</span> * n<span class="hljs-selector-class">.z</span> * n<span class="hljs-selector-class">.z</span> - <span class="hljs-number">1.0</span>)</span>
|
||||
<span class="line"> + sphericalHarmonics<span class="hljs-selector-attr">[7]</span> * (n<span class="hljs-selector-class">.z</span> * n<span class="hljs-selector-class">.x</span>)</span>
|
||||
<span class="line"> + sphericalHarmonics<span class="hljs-selector-attr">[8]</span> * (n<span class="hljs-selector-class">.x</span> * n<span class="hljs-selector-class">.x</span> - n<span class="hljs-selector-class">.y</span> * n<span class="hljs-selector-class">.y</span>);</span>
|
||||
</p><pre class="listing tilde"><code><span class="line">vec3 <span class="hljs-function"><span class="hljs-title">irradianceSH</span>(<span class="hljs-params">vec3 n</span>)</span> {</span>
|
||||
<span class="line"> <span class="hljs-comment">// uniform vec3 sphericalHarmonics[9]</span></span>
|
||||
<span class="line"> <span class="hljs-comment">// We can use only the first 2 bands for better performance</span></span>
|
||||
<span class="line"> <span class="hljs-keyword">return</span></span>
|
||||
<span class="line"> sphericalHarmonics[<span class="hljs-number">0</span>]</span>
|
||||
<span class="line"> + sphericalHarmonics[<span class="hljs-number">1</span>] * (n.y)</span>
|
||||
<span class="line"> + sphericalHarmonics[<span class="hljs-number">2</span>] * (n.z)</span>
|
||||
<span class="line"> + sphericalHarmonics[<span class="hljs-number">3</span>] * (n.x)</span>
|
||||
<span class="line"> + sphericalHarmonics[<span class="hljs-number">4</span>] * (n.y * n.x)</span>
|
||||
<span class="line"> + sphericalHarmonics[<span class="hljs-number">5</span>] * (n.y * n.z)</span>
|
||||
<span class="line"> + sphericalHarmonics[<span class="hljs-number">6</span>] * (<span class="hljs-number">3.0</span> * n.z * n.z - <span class="hljs-number">1.0</span>)</span>
|
||||
<span class="line"> + sphericalHarmonics[<span class="hljs-number">7</span>] * (n.z * n.x)</span>
|
||||
<span class="line"> + sphericalHarmonics[<span class="hljs-number">8</span>] * (n.x * n.x - n.y * n.y);</span>
|
||||
<span class="line">}</span></code></pre><center><div class="listingcaption tilde"><a class="target" name="listing_irradiancesh"> </a><b style="font-style:normal;">Listing 26:</b> GLSL code to reconstruct the irradiance from the pre-scaled SH</div></center>
|
||||
<p>
|
||||
<p>Note that with 2 bands, the computation above becomes a single (4 \times 4) matrix-by-vector multiply.</p>
|
||||
@@ -2443,7 +2443,7 @@ LD(n, \alpha) &= \frac{\sum_i^N V(l_i, n,
|
||||
$$
|
||||
</p><p>
|
||||
These two new \(DFG\) terms simply need to replace the ones used in the implementation shown in section <a href="#toc9.5">9.5</a>:
|
||||
</p><pre class="listing tilde"><code><span class="line"><span class="hljs-type">float</span> Fc = <span class="hljs-built_in">pow</span>(<span class="hljs-number">1</span> - VoH, <span class="hljs-number">5.0f</span>);</span>
|
||||
</p><pre class="listing tilde"><code><span class="line"><span class="hljs-keyword">float</span> Fc = <span class="hljs-built_in">pow</span>(<span class="hljs-number">1</span> - VoH, <span class="hljs-number">5.0f</span>);</span>
|
||||
<span class="line">r.x += Gv * Fc;</span>
|
||||
<span class="line">r.y += Gv;</span></code></pre><center><div class="listingcaption tilde"><a class="target" name="listing_multiscatteriblpreintegration"> </a><b style="font-style:normal;">Listing 29:</b> C++ implementation of the \(L_{DFG}\) term for multiscattering</div></center>
|
||||
<p>
|
||||
@@ -2507,11 +2507,11 @@ using an environment made of colored vertical stripes (skybox hidden).</span></c
|
||||
<p>
|
||||
<p>When sampling the IBL, the clear coat layer is calculated as a second specular lobe. This specular lobe is oriented along the view direction since we cannot reasonably integrate over the hemisphere. <a href="#listing_clearcoatibl">Listing 31</a> demonstrates this approximation in practice. It also shows the energy conservation step. It is important to note that this second specular lobe is computed exactly the same way as the main specular lobe, using the same DFG approximation.</p>
|
||||
</p><pre class="listing tilde"><code><span class="line"><span class="hljs-comment">// clearCoat_NoV == shading_NoV if the clear coat layer doesn't have its own normal map</span></span>
|
||||
<span class="line"><span class="hljs-type">float</span> Fc = <span class="hljs-built_in">F_Schlick</span>(<span class="hljs-number">0.04</span>, <span class="hljs-number">1.0</span>, clearCoat_NoV) * clearCoat;</span>
|
||||
<span class="line"><span class="hljs-built_in">float</span> Fc = F_Schlick(<span class="hljs-number">0.04</span>, <span class="hljs-number">1.0</span>, clearCoat_NoV) * clearCoat;</span>
|
||||
<span class="line"><span class="hljs-comment">// base layer attenuation for energy compensation</span></span>
|
||||
<span class="line">iblDiffuse *= <span class="hljs-number">1.0</span> - Fc;</span>
|
||||
<span class="line">iblSpecular *= <span class="hljs-built_in">sq</span>(<span class="hljs-number">1.0</span> - Fc);</span>
|
||||
<span class="line">iblSpecular += <span class="hljs-built_in">specularIBL</span>(r, clearCoatPerceptualRoughness) * Fc;</span></code></pre><center><div class="listingcaption tilde"><a class="target" name="listing_clearcoatibl"> </a><b style="font-style:normal;">Listing 31:</b> GLSL implementation of the clear coat specular lobe for image-based lighting</div></center>
|
||||
<span class="line">iblSpecular *= sq(<span class="hljs-number">1.0</span> - Fc);</span>
|
||||
<span class="line">iblSpecular += specularIBL(r, clearCoatPerceptualRoughness) * Fc;</span></code></pre><center><div class="listingcaption tilde"><a class="target" name="listing_clearcoatibl"> </a><b style="font-style:normal;">Listing 31:</b> GLSL implementation of the clear coat specular lobe for image-based lighting</div></center>
|
||||
<a class="target" name="anisotropy"> </a><a class="target" name="lighting/imagebasedlights/anisotropy"> </a><a class="target" name="toc5.3.6"> </a><h3 id="anisotropy"><a class="header" href="#anisotropy">Anisotropy </a></h3>
|
||||
<p>
|
||||
<p>[<a href="#citation-mcauley15">McAuley15</a>] describes a technique called “bent reflection vector”, based [<a href="#citation-revie12">Revie12</a>]. The bent reflection vector is a rough approximation of anisotropic lighting but the alternative is to use importance sampling. This approximation is sufficiently cheap to compute and provides good results, as shown in <a href="#figure_anisotropicibl1">figure 59</a> and <a href="#figure_anisotropicibl2">figure 60</a>.</p>
|
||||
@@ -2550,17 +2550,17 @@ The DG term is generated using uniform sampling as recommended in [<a href="#cit
|
||||
<center><div class="image" style=""><a href="../images/ibl/dfg_cloth.png" target="_blank"><img class="markdeep" src="../images/ibl/dfg_cloth.png" /></a><center><span class="imagecaption"><a class="target" name="figure_dfgclothlut"> </a><b style="font-style:normal;">Figure 62:</b> DFG LUT with a 3rd channel encoding the DG term of the cloth BRDF</span></center></div></center>
|
||||
</p><p>
|
||||
The remainder of the image-based lighting implementation follows the same steps as the implementation of regular lights, including the optional subsurface scattering term and its wrap diffuse component. Just as with the clear coat IBL implementation, we cannot integrate over the hemisphere and use the view direction as the dominant light direction to compute the wrap diffuse component.
|
||||
</p><pre class="listing tilde"><code><span class="line"><span class="hljs-type">float</span> diffuse = <span class="hljs-built_in">Fd_Lambert</span>() * ambientOcclusion;</span>
|
||||
<span class="line"><span class="hljs-meta">#<span class="hljs-keyword">if</span> defined(SHADING_MODEL_CLOTH)</span></span>
|
||||
<span class="line"><span class="hljs-meta">#<span class="hljs-keyword">if</span> defined(MATERIAL_HAS_SUBSURFACE_COLOR)</span></span>
|
||||
<span class="line">diffuse *= <span class="hljs-built_in">saturate</span>((NoV + <span class="hljs-number">0.5</span>) / <span class="hljs-number">2.25</span>);</span>
|
||||
<span class="line"><span class="hljs-meta">#<span class="hljs-keyword">endif</span></span></span>
|
||||
<span class="line"><span class="hljs-meta">#<span class="hljs-keyword">endif</span></span></span>
|
||||
</p><pre class="listing tilde"><code><span class="line"><span class="hljs-built_in">float</span> diffuse = Fd_Lambert() * ambientOcclusion;</span>
|
||||
<span class="line"><span class="hljs-meta">#<span class="hljs-meta-keyword">if</span> defined(SHADING_MODEL_CLOTH)</span></span>
|
||||
<span class="line"><span class="hljs-meta">#<span class="hljs-meta-keyword">if</span> defined(MATERIAL_HAS_SUBSURFACE_COLOR)</span></span>
|
||||
<span class="line">diffuse *= saturate((NoV + <span class="hljs-number">0.5</span>) / <span class="hljs-number">2.25</span>);</span>
|
||||
<span class="line"><span class="hljs-meta">#<span class="hljs-meta-keyword">endif</span></span></span>
|
||||
<span class="line"><span class="hljs-meta">#<span class="hljs-meta-keyword">endif</span></span></span>
|
||||
<span class="line"></span>
|
||||
<span class="line">vec3 indirectDiffuse = <span class="hljs-built_in">irradianceIBL</span>(n) * diffuse;</span>
|
||||
<span class="line"><span class="hljs-meta">#<span class="hljs-keyword">if</span> defined(SHADING_MODEL_CLOTH) && defined(MATERIAL_HAS_SUBSURFACE_COLOR)</span></span>
|
||||
<span class="line">indirectDiffuse *= <span class="hljs-built_in">saturate</span>(subsurfaceColor + NoV);</span>
|
||||
<span class="line"><span class="hljs-meta">#<span class="hljs-keyword">endif</span></span></span>
|
||||
<span class="line">vec3 indirectDiffuse = irradianceIBL(n) * diffuse;</span>
|
||||
<span class="line"><span class="hljs-meta">#<span class="hljs-meta-keyword">if</span> defined(SHADING_MODEL_CLOTH) && defined(MATERIAL_HAS_SUBSURFACE_COLOR)</span></span>
|
||||
<span class="line">indirectDiffuse *= saturate(subsurfaceColor + NoV);</span>
|
||||
<span class="line"><span class="hljs-meta">#<span class="hljs-meta-keyword">endif</span></span></span>
|
||||
<span class="line"></span>
|
||||
<span class="line">vec3 ibl = diffuseColor * indirectDiffuse + indirectSpecular * specularColor;</span></code></pre><center><div class="listingcaption tilde"><a class="target" name="listing_clothapprox"> </a><b style="font-style:normal;">Listing 34:</b> GLSL implementation of the DFG approximation for the cloth NDF</div></center>
|
||||
<p>
|
||||
@@ -2982,20 +2982,20 @@ L_{max} &= 2^{EV_{100}} \times 1.2
|
||||
<span class="line"><span class="hljs-comment">// aperture in f-stops</span></span>
|
||||
<span class="line"><span class="hljs-comment">// shutterSpeed in seconds</span></span>
|
||||
<span class="line"><span class="hljs-comment">// sensitivity in ISO</span></span>
|
||||
<span class="line"><span class="hljs-type">float</span> <span class="hljs-title function_">exposureSettings</span><span class="hljs-params">(<span class="hljs-type">float</span> aperture, <span class="hljs-type">float</span> shutterSpeed, <span class="hljs-type">float</span> sensitivity)</span> {</span>
|
||||
<span class="line"><span class="hljs-function"><span class="hljs-keyword">float</span> <span class="hljs-title">exposureSettings</span><span class="hljs-params">(<span class="hljs-keyword">float</span> aperture, <span class="hljs-keyword">float</span> shutterSpeed, <span class="hljs-keyword">float</span> sensitivity)</span> </span>{</span>
|
||||
<span class="line"> <span class="hljs-keyword">return</span> log2((aperture * aperture) / shutterSpeed * <span class="hljs-number">100.0</span> / sensitivity);</span>
|
||||
<span class="line">}</span>
|
||||
<span class="line"></span>
|
||||
<span class="line"><span class="hljs-comment">// Computes the exposure normalization factor from</span></span>
|
||||
<span class="line"><span class="hljs-comment">// the camera's EV100</span></span>
|
||||
<span class="line"><span class="hljs-type">float</span> <span class="hljs-title function_">exposure</span><span class="hljs-params">(<span class="hljs-type">float</span> ev100)</span> {</span>
|
||||
<span class="line"> <span class="hljs-keyword">return</span> <span class="hljs-number">1.0</span> / (pow(<span class="hljs-number">2.0</span>, ev100) * <span class="hljs-number">1.2</span>);</span>
|
||||
<span class="line"><span class="hljs-function"><span class="hljs-keyword">float</span> <span class="hljs-title">exposure</span><span class="hljs-params">(<span class="hljs-keyword">float</span> ev100)</span> </span>{</span>
|
||||
<span class="line"> <span class="hljs-keyword">return</span> <span class="hljs-number">1.0</span> / (<span class="hljs-built_in">pow</span>(<span class="hljs-number">2.0</span>, ev100) * <span class="hljs-number">1.2</span>);</span>
|
||||
<span class="line">}</span>
|
||||
<span class="line"></span>
|
||||
<span class="line"><span class="hljs-type">float</span> <span class="hljs-variable">ev100</span> <span class="hljs-operator">=</span> exposureSettings(aperture, shutterSpeed, sensitivity);</span>
|
||||
<span class="line"><span class="hljs-type">float</span> <span class="hljs-variable">exposure</span> <span class="hljs-operator">=</span> exposure(ev100);</span>
|
||||
<span class="line"><span class="hljs-keyword">float</span> ev100 = exposureSettings(aperture, shutterSpeed, sensitivity);</span>
|
||||
<span class="line"><span class="hljs-keyword">float</span> exposure = exposure(ev100);</span>
|
||||
<span class="line"></span>
|
||||
<span class="line"><span class="hljs-type">vec4</span> <span class="hljs-variable">color</span> <span class="hljs-operator">=</span> evaluateLighting();</span>
|
||||
<span class="line">vec4 color = evaluateLighting();</span>
|
||||
<span class="line">color.rgb *= exposure;</span></code></pre><center><div class="listingcaption tilde"><a class="target" name="listing_fragmentexposure"> </a><b style="font-style:normal;">Listing 42:</b> Implementation of exposure in GLSL</div></center>
|
||||
<p>
|
||||
<p>In practice the exposure factor can be pre-computed on the CPU to save shader instructions.</p>
|
||||
@@ -3466,9 +3466,9 @@ Our implementation is presented in <a href="#listing_specularcolorimpl">listing&
|
||||
<span class="line"><span class="hljs-comment">// Data source:</span></span>
|
||||
<span class="line"><span class="hljs-comment">// http://cvrl.ioo.ucl.ac.uk/cmfs.htm</span></span>
|
||||
<span class="line"><span class="hljs-comment">// http://cvrl.ioo.ucl.ac.uk/database/text/cmfs/ciexyz31.htm</span></span>
|
||||
<span class="line"><span class="hljs-type">const</span> <span class="hljs-type">size_t</span> CIE_XYZ_START = <span class="hljs-number">360</span>;</span>
|
||||
<span class="line"><span class="hljs-type">const</span> <span class="hljs-type">size_t</span> CIE_XYZ_COUNT = <span class="hljs-number">471</span>;</span>
|
||||
<span class="line"><span class="hljs-type">const</span> float3 CIE_XYZ[CIE_XYZ_COUNT] = { ... };</span>
|
||||
<span class="line"><span class="hljs-keyword">const</span> <span class="hljs-keyword">size_t</span> CIE_XYZ_START = <span class="hljs-number">360</span>;</span>
|
||||
<span class="line"><span class="hljs-keyword">const</span> <span class="hljs-keyword">size_t</span> CIE_XYZ_COUNT = <span class="hljs-number">471</span>;</span>
|
||||
<span class="line"><span class="hljs-keyword">const</span> float3 CIE_XYZ[CIE_XYZ_COUNT] = { ... };</span>
|
||||
<span class="line"></span>
|
||||
<span class="line"><span class="hljs-comment">// CIE Standard Illuminant D65 relative spectral power distribution,</span></span>
|
||||
<span class="line"><span class="hljs-comment">// from 300nm to 830, at 5nm intervals</span></span>
|
||||
@@ -3476,51 +3476,51 @@ Our implementation is presented in <a href="#listing_specularcolorimpl">listing&
|
||||
<span class="line"><span class="hljs-comment">// Data source:</span></span>
|
||||
<span class="line"><span class="hljs-comment">// https://en.wikipedia.org/wiki/Illuminant_D65</span></span>
|
||||
<span class="line"><span class="hljs-comment">// https://cielab.xyz/pdf/CIE_sel_colorimetric_tables.xls</span></span>
|
||||
<span class="line"><span class="hljs-type">const</span> <span class="hljs-type">size_t</span> CIE_D65_INTERVAL = <span class="hljs-number">5</span>;</span>
|
||||
<span class="line"><span class="hljs-type">const</span> <span class="hljs-type">size_t</span> CIE_D65_START = <span class="hljs-number">300</span>;</span>
|
||||
<span class="line"><span class="hljs-type">const</span> <span class="hljs-type">size_t</span> CIE_D65_END = <span class="hljs-number">830</span>;</span>
|
||||
<span class="line"><span class="hljs-type">const</span> <span class="hljs-type">size_t</span> CIE_D65_COUNT = <span class="hljs-number">107</span>;</span>
|
||||
<span class="line"><span class="hljs-type">const</span> <span class="hljs-type">float</span> CIE_D65[CIE_D65_COUNT] = { ... };</span>
|
||||
<span class="line"><span class="hljs-keyword">const</span> <span class="hljs-keyword">size_t</span> CIE_D65_INTERVAL = <span class="hljs-number">5</span>;</span>
|
||||
<span class="line"><span class="hljs-keyword">const</span> <span class="hljs-keyword">size_t</span> CIE_D65_START = <span class="hljs-number">300</span>;</span>
|
||||
<span class="line"><span class="hljs-keyword">const</span> <span class="hljs-keyword">size_t</span> CIE_D65_END = <span class="hljs-number">830</span>;</span>
|
||||
<span class="line"><span class="hljs-keyword">const</span> <span class="hljs-keyword">size_t</span> CIE_D65_COUNT = <span class="hljs-number">107</span>;</span>
|
||||
<span class="line"><span class="hljs-keyword">const</span> <span class="hljs-keyword">float</span> CIE_D65[CIE_D65_COUNT] = { ... };</span>
|
||||
<span class="line"></span>
|
||||
<span class="line"><span class="hljs-keyword">struct</span> <span class="hljs-title class_">Sample</span> {</span>
|
||||
<span class="line"> <span class="hljs-type">float</span> w = <span class="hljs-number">0.0f</span>; <span class="hljs-comment">// wavelength</span></span>
|
||||
<span class="line"> std::complex<<span class="hljs-type">float</span>> ior; <span class="hljs-comment">// complex IOR, n + ik</span></span>
|
||||
<span class="line"><span class="hljs-class"><span class="hljs-keyword">struct</span> <span class="hljs-title">Sample</span> {</span></span>
|
||||
<span class="line"> <span class="hljs-keyword">float</span> w = <span class="hljs-number">0.0f</span>; <span class="hljs-comment">// wavelength</span></span>
|
||||
<span class="line"> <span class="hljs-built_in">std</span>::<span class="hljs-built_in">complex</span><<span class="hljs-keyword">float</span>> ior; <span class="hljs-comment">// complex IOR, n + ik</span></span>
|
||||
<span class="line">};</span>
|
||||
<span class="line"></span>
|
||||
<span class="line"><span class="hljs-function"><span class="hljs-type">static</span> <span class="hljs-type">float</span> <span class="hljs-title">illuminantD65</span><span class="hljs-params">(<span class="hljs-type">float</span> w)</span> </span>{</span>
|
||||
<span class="line"> <span class="hljs-keyword">auto</span> i0 = <span class="hljs-built_in">size_t</span>((w - CIE_D65_START) / CIE_D65_INTERVAL);</span>
|
||||
<span class="line"> uint2 indexBounds{i0, std::<span class="hljs-built_in">min</span>(i0 + <span class="hljs-number">1</span>, CIE_D65_END)};</span>
|
||||
<span class="line"><span class="hljs-function"><span class="hljs-keyword">static</span> <span class="hljs-keyword">float</span> <span class="hljs-title">illuminantD65</span><span class="hljs-params">(<span class="hljs-keyword">float</span> w)</span> </span>{</span>
|
||||
<span class="line"> <span class="hljs-keyword">auto</span> i0 = <span class="hljs-keyword">size_t</span>((w - CIE_D65_START) / CIE_D65_INTERVAL);</span>
|
||||
<span class="line"> uint2 indexBounds{i0, <span class="hljs-built_in">std</span>::min(i0 + <span class="hljs-number">1</span>, CIE_D65_END)};</span>
|
||||
<span class="line"></span>
|
||||
<span class="line"> float2 wavelengthBounds = CIE_D65_START + float2{indexBounds} * CIE_D65_INTERVAL;</span>
|
||||
<span class="line"> <span class="hljs-type">float</span> t = (w - wavelengthBounds.x) / (wavelengthBounds.y - wavelengthBounds.x);</span>
|
||||
<span class="line"> <span class="hljs-keyword">return</span> <span class="hljs-built_in">lerp</span>(CIE_D65[indexBounds.x], CIE_D65[indexBounds.y], t);</span>
|
||||
<span class="line"> <span class="hljs-keyword">float</span> t = (w - wavelengthBounds.x) / (wavelengthBounds.y - wavelengthBounds.x);</span>
|
||||
<span class="line"> <span class="hljs-keyword">return</span> lerp(CIE_D65[indexBounds.x], CIE_D65[indexBounds.y], t);</span>
|
||||
<span class="line">}</span>
|
||||
<span class="line"></span>
|
||||
<span class="line"><span class="hljs-comment">// For std::lower_bound</span></span>
|
||||
<span class="line"><span class="hljs-type">bool</span> <span class="hljs-keyword">operator</span><(<span class="hljs-type">const</span> Sample& lhs, <span class="hljs-type">const</span> Sample& rhs) {</span>
|
||||
<span class="line"><span class="hljs-keyword">bool</span> <span class="hljs-keyword">operator</span><(<span class="hljs-keyword">const</span> Sample& lhs, <span class="hljs-keyword">const</span> Sample& rhs) {</span>
|
||||
<span class="line"> <span class="hljs-keyword">return</span> lhs.w < rhs.w;</span>
|
||||
<span class="line">}</span>
|
||||
<span class="line"></span>
|
||||
<span class="line"><span class="hljs-comment">// The wavelength w must be between 360nm and 830nm</span></span>
|
||||
<span class="line"><span class="hljs-function"><span class="hljs-type">static</span> std::complex<<span class="hljs-type">float</span>> <span class="hljs-title">findSample</span><span class="hljs-params">(<span class="hljs-type">const</span> std::vector<sample>& samples, <span class="hljs-type">float</span> w)</span> </span>{</span>
|
||||
<span class="line"> <span class="hljs-keyword">auto</span> i1 = std::<span class="hljs-built_in">lower_bound</span>(</span>
|
||||
<span class="line"> samples.<span class="hljs-built_in">begin</span>(), samples.<span class="hljs-built_in">end</span>(), Sample{w, <span class="hljs-number">0.0f</span> + <span class="hljs-number">0.0</span><span class="hljs-keyword">if</span>});</span>
|
||||
<span class="line"><span class="hljs-function"><span class="hljs-keyword">static</span> <span class="hljs-built_in">std</span>::<span class="hljs-built_in">complex</span><<span class="hljs-keyword">float</span>> <span class="hljs-title">findSample</span><span class="hljs-params">(<span class="hljs-keyword">const</span> <span class="hljs-built_in">std</span>::<span class="hljs-built_in">vector</span><sample>& samples, <span class="hljs-keyword">float</span> w)</span> </span>{</span>
|
||||
<span class="line"> <span class="hljs-keyword">auto</span> i1 = <span class="hljs-built_in">std</span>::lower_bound(</span>
|
||||
<span class="line"> samples.begin(), samples.end(), Sample{w, <span class="hljs-number">0.0f</span> + <span class="hljs-number">0.0</span><span class="hljs-keyword">if</span>});</span>
|
||||
<span class="line"> <span class="hljs-keyword">auto</span> i0 = i1 - <span class="hljs-number">1</span>;</span>
|
||||
<span class="line"></span>
|
||||
<span class="line"> <span class="hljs-comment">// Interpolate the complex IORs</span></span>
|
||||
<span class="line"> <span class="hljs-type">float</span> t = (w - i0->w) / (i1->w - i0->w);</span>
|
||||
<span class="line"> <span class="hljs-type">float</span> n = <span class="hljs-built_in">lerp</span>(i0->ior.<span class="hljs-built_in">real</span>(), i1->ior.<span class="hljs-built_in">real</span>(), t);</span>
|
||||
<span class="line"> <span class="hljs-type">float</span> k = <span class="hljs-built_in">lerp</span>(i0->ior.<span class="hljs-built_in">imag</span>(), i1->ior.<span class="hljs-built_in">imag</span>(), t);</span>
|
||||
<span class="line"> <span class="hljs-keyword">float</span> t = (w - i0->w) / (i1->w - i0->w);</span>
|
||||
<span class="line"> <span class="hljs-keyword">float</span> n = lerp(i0->ior.real(), i1->ior.real(), t);</span>
|
||||
<span class="line"> <span class="hljs-keyword">float</span> k = lerp(i0->ior.imag(), i1->ior.imag(), t);</span>
|
||||
<span class="line"> <span class="hljs-keyword">return</span> { n, k };</span>
|
||||
<span class="line">}</span>
|
||||
<span class="line"></span>
|
||||
<span class="line"><span class="hljs-function"><span class="hljs-type">static</span> <span class="hljs-type">float</span> <span class="hljs-title">fresnel</span><span class="hljs-params">(<span class="hljs-type">const</span> std::complex<<span class="hljs-type">float</span>>& sample)</span> </span>{</span>
|
||||
<span class="line"> <span class="hljs-keyword">return</span> (((sample - (<span class="hljs-number">1.0f</span> + <span class="hljs-number">0</span><span class="hljs-keyword">if</span>)) * (std::<span class="hljs-built_in">conj</span>(sample) - (<span class="hljs-number">1.0f</span> + <span class="hljs-number">0</span><span class="hljs-keyword">if</span>))) /</span>
|
||||
<span class="line"> ((sample + (<span class="hljs-number">1.0f</span> + <span class="hljs-number">0</span><span class="hljs-keyword">if</span>)) * (std::<span class="hljs-built_in">conj</span>(sample) + (<span class="hljs-number">1.0f</span> + <span class="hljs-number">0</span><span class="hljs-keyword">if</span>)))).<span class="hljs-built_in">real</span>();</span>
|
||||
<span class="line"><span class="hljs-function"><span class="hljs-keyword">static</span> <span class="hljs-keyword">float</span> <span class="hljs-title">fresnel</span><span class="hljs-params">(<span class="hljs-keyword">const</span> <span class="hljs-built_in">std</span>::<span class="hljs-built_in">complex</span><<span class="hljs-keyword">float</span>>& sample)</span> </span>{</span>
|
||||
<span class="line"> <span class="hljs-keyword">return</span> (((sample - (<span class="hljs-number">1.0f</span> + <span class="hljs-number">0</span><span class="hljs-keyword">if</span>)) * (<span class="hljs-built_in">std</span>::conj(sample) - (<span class="hljs-number">1.0f</span> + <span class="hljs-number">0</span><span class="hljs-keyword">if</span>))) /</span>
|
||||
<span class="line"> ((sample + (<span class="hljs-number">1.0f</span> + <span class="hljs-number">0</span><span class="hljs-keyword">if</span>)) * (<span class="hljs-built_in">std</span>::conj(sample) + (<span class="hljs-number">1.0f</span> + <span class="hljs-number">0</span><span class="hljs-keyword">if</span>)))).real();</span>
|
||||
<span class="line">}</span>
|
||||
<span class="line"></span>
|
||||
<span class="line"><span class="hljs-function"><span class="hljs-type">static</span> float3 <span class="hljs-title">XYZ_to_sRGB</span><span class="hljs-params">(<span class="hljs-type">const</span> float3& v)</span> </span>{</span>
|
||||
<span class="line"> <span class="hljs-type">const</span> mat3f XYZ_sRGB{</span>
|
||||
<span class="line"><span class="hljs-function"><span class="hljs-keyword">static</span> float3 <span class="hljs-title">XYZ_to_sRGB</span><span class="hljs-params">(<span class="hljs-keyword">const</span> float3& v)</span> </span>{</span>
|
||||
<span class="line"> <span class="hljs-keyword">const</span> mat3f XYZ_sRGB{</span>
|
||||
<span class="line"> <span class="hljs-number">3.2404542f</span>, <span class="hljs-number">-0.9692660f</span>, <span class="hljs-number">0.0556434f</span>,</span>
|
||||
<span class="line"> <span class="hljs-number">-1.5371385f</span>, <span class="hljs-number">1.8760108f</span>, <span class="hljs-number">-0.2040259f</span>,</span>
|
||||
<span class="line"> <span class="hljs-number">-0.4985314f</span>, <span class="hljs-number">0.0415560f</span>, <span class="hljs-number">1.0572252f</span></span>
|
||||
@@ -3529,21 +3529,21 @@ Our implementation is presented in <a href="#listing_specularcolorimpl">listing&
|
||||
<span class="line">}</span>
|
||||
<span class="line"></span>
|
||||
<span class="line"><span class="hljs-comment">// Outputs a linear sRGB color</span></span>
|
||||
<span class="line"><span class="hljs-function"><span class="hljs-type">static</span> float3 <span class="hljs-title">computeColor</span><span class="hljs-params">(<span class="hljs-type">const</span> std::vector<sample>& samples)</span> </span>{</span>
|
||||
<span class="line"><span class="hljs-function"><span class="hljs-keyword">static</span> float3 <span class="hljs-title">computeColor</span><span class="hljs-params">(<span class="hljs-keyword">const</span> <span class="hljs-built_in">std</span>::<span class="hljs-built_in">vector</span><sample>& samples)</span> </span>{</span>
|
||||
<span class="line"> float3 xyz{<span class="hljs-number">0.0f</span>};</span>
|
||||
<span class="line"> <span class="hljs-type">float</span> y = <span class="hljs-number">0.0f</span>;</span>
|
||||
<span class="line"> <span class="hljs-keyword">float</span> y = <span class="hljs-number">0.0f</span>;</span>
|
||||
<span class="line"></span>
|
||||
<span class="line"> <span class="hljs-keyword">for</span> (<span class="hljs-type">size_t</span> i = <span class="hljs-number">0</span>; i < CIE_XYZ_COUNT; i++) {</span>
|
||||
<span class="line"> <span class="hljs-keyword">for</span> (<span class="hljs-keyword">size_t</span> i = <span class="hljs-number">0</span>; i < CIE_XYZ_COUNT; i++) {</span>
|
||||
<span class="line"> <span class="hljs-comment">// Current wavelength</span></span>
|
||||
<span class="line"> <span class="hljs-type">float</span> w = CIE_XYZ_START + i;</span>
|
||||
<span class="line"> <span class="hljs-keyword">float</span> w = CIE_XYZ_START + i;</span>
|
||||
<span class="line"></span>
|
||||
<span class="line"> <span class="hljs-comment">// Find most appropriate CIE XYZ sample for the wavelength</span></span>
|
||||
<span class="line"> <span class="hljs-keyword">auto</span> sample = <span class="hljs-built_in">findSample</span>(samples, w);</span>
|
||||
<span class="line"> <span class="hljs-keyword">auto</span> sample = findSample(samples, w);</span>
|
||||
<span class="line"> <span class="hljs-comment">// Compute Fresnel reflectance at normal incidence</span></span>
|
||||
<span class="line"> <span class="hljs-type">float</span> f0 = <span class="hljs-built_in">fresnel</span>(sample);</span>
|
||||
<span class="line"> <span class="hljs-keyword">float</span> f0 = fresnel(sample);</span>
|
||||
<span class="line"></span>
|
||||
<span class="line"> <span class="hljs-comment">// We need to multiply by the spectral power distribution of the illuminant</span></span>
|
||||
<span class="line"> <span class="hljs-type">float</span> d65 = <span class="hljs-built_in">illuminantD65</span>(w);</span>
|
||||
<span class="line"> <span class="hljs-keyword">float</span> d65 = illuminantD65(w);</span>
|
||||
<span class="line"></span>
|
||||
<span class="line"> xyz += f0 * CIE_XYZ[i] * d65;</span>
|
||||
<span class="line"> y += CIE_XYZ[i].y * d65;</span>
|
||||
@@ -3552,10 +3552,10 @@ Our implementation is presented in <a href="#listing_specularcolorimpl">listing&
|
||||
<span class="line"> <span class="hljs-comment">// Normalize so that 100% reflectance at every wavelength yields Y=1</span></span>
|
||||
<span class="line"> xyz /= y;</span>
|
||||
<span class="line"></span>
|
||||
<span class="line"> float3 linear = <span class="hljs-built_in">XYZ_to_sRGB</span>(xyz);</span>
|
||||
<span class="line"> float3 linear = XYZ_to_sRGB(xyz);</span>
|
||||
<span class="line"></span>
|
||||
<span class="line"> <span class="hljs-comment">// Normalize out-of-gamut values</span></span>
|
||||
<span class="line"> <span class="hljs-keyword">if</span> (<span class="hljs-built_in">any</span>(<span class="hljs-built_in">greaterThan</span>(linear, float3{<span class="hljs-number">1.0f</span>}))) linear *= <span class="hljs-number">1.0f</span> / <span class="hljs-built_in">max</span>(linear);</span>
|
||||
<span class="line"> <span class="hljs-keyword">if</span> (any(greaterThan(linear, float3{<span class="hljs-number">1.0f</span>}))) linear *= <span class="hljs-number">1.0f</span> / max(linear);</span>
|
||||
<span class="line"></span>
|
||||
<span class="line"> <span class="hljs-keyword">return</span> linear;</span>
|
||||
<span class="line">}</span></code></pre><center><div class="listingcaption tilde"><a class="target" name="listing_specularcolorimpl"> </a><b style="font-style:normal;">Listing 46:</b> C++ implementation to compute the base color of a metallic surface from spectral data</div></center>
|
||||
@@ -3735,47 +3735,47 @@ l &= \{ cos\phi sin\theta,sin\phi sin\theta,cos\theta \}
|
||||
<pre class="listing tilde"><code><span class="line">vec2f hammersley(uint i, <span class="hljs-built_in">float</span> numSamples) {</span>
|
||||
<span class="line"> uint bits = i;</span>
|
||||
<span class="line"> bits = (bits << <span class="hljs-string">16) | (bits >> 16</span>);</span>
|
||||
<span class="line"> bits = ((bits & <span class="hljs-number">0</span>x55555555) << <span class="hljs-number">1</span>) | ((bits & <span class="hljs-number">0</span>xAAAAAAAA) >> <span class="hljs-number">1</span>);</span>
|
||||
<span class="line"> bits = ((bits & <span class="hljs-number">0</span>x33333333) << <span class="hljs-number">2</span>) | ((bits & <span class="hljs-number">0</span>xCCCCCCCC) >> <span class="hljs-number">2</span>);</span>
|
||||
<span class="line"> bits = ((bits & <span class="hljs-number">0</span>x0F0F0F0F) << <span class="hljs-number">4</span>) | ((bits & <span class="hljs-number">0</span>xF0F0F0F0) >> <span class="hljs-number">4</span>);</span>
|
||||
<span class="line"> bits = ((bits & <span class="hljs-number">0</span>x00FF00FF) << <span class="hljs-number">8</span>) | ((bits & <span class="hljs-number">0</span>xFF00FF00) >> <span class="hljs-number">8</span>);</span>
|
||||
<span class="line"> return vec2f(i / numSamples, bits / exp2(<span class="hljs-number">32</span>));</span>
|
||||
<span class="line"> bits = ((bits & 0x55555555) << <span class="hljs-string">1) | ((bits & 0xAAAAAAAA) >> 1</span>);</span>
|
||||
<span class="line"> bits = ((bits & 0x33333333) << <span class="hljs-string">2) | ((bits & 0xCCCCCCCC) >> 2</span>);</span>
|
||||
<span class="line"> bits = ((bits & 0x0F0F0F0F) << <span class="hljs-string">4) | ((bits & 0xF0F0F0F0) >> 4</span>);</span>
|
||||
<span class="line"> bits = ((bits & 0x00FF00FF) << <span class="hljs-string">8) | ((bits & 0xFF00FF00) >> 8</span>);</span>
|
||||
<span class="line"> <span class="hljs-built_in">return</span> vec2f(i / numSamples, bits / exp2(32));</span>
|
||||
<span class="line">}</span></code></pre><center><div class="listingcaption tilde">C++ implementation of a Hammersley sequence generator</div></center>
|
||||
<a class="target" name="precomputinglforimage-basedlighting"> </a><a class="target" name="annex/precomputinglforimage-basedlighting"> </a><a class="target" name="toc9.5"> </a><h2 id="precomputing-l-for-image-based-lighting"><a class="header" href="#precomputing-l-for-image-based-lighting">Precomputing L for image-based lighting</a></h2>
|
||||
<p>
|
||||
<p>The term ( L_{DFG} ) is only dependent on ( \NoV ). Below, the normal is arbitrarily set to ( n=\left[0, 0, 1\right] ) and (v) is chosen to satisfy ( \NoV ). The vector ( h_i ) is the ( D_{GGX}(\alpha) ) important direction sample (i).</p>
|
||||
</p><pre class="listing tilde"><code><span class="line"><span class="hljs-type">float</span> <span class="hljs-title function_">GDFG</span><span class="hljs-params">(<span class="hljs-type">float</span> NoV, <span class="hljs-type">float</span> NoL, <span class="hljs-type">float</span> a)</span> {</span>
|
||||
<span class="line"> <span class="hljs-type">float</span> <span class="hljs-variable">a2</span> <span class="hljs-operator">=</span> a * a;</span>
|
||||
<span class="line"> <span class="hljs-type">float</span> <span class="hljs-variable">GGXL</span> <span class="hljs-operator">=</span> NoV * sqrt((-NoL * a2 + NoL) * NoL + a2);</span>
|
||||
<span class="line"> <span class="hljs-type">float</span> <span class="hljs-variable">GGXV</span> <span class="hljs-operator">=</span> NoL * sqrt((-NoV * a2 + NoV) * NoV + a2);</span>
|
||||
</p><pre class="listing tilde"><code><span class="line"><span class="hljs-type">float</span> GDFG(<span class="hljs-type">float</span> NoV, <span class="hljs-type">float</span> NoL, <span class="hljs-type">float</span> a) {</span>
|
||||
<span class="line"> <span class="hljs-type">float</span> a2 = a * a;</span>
|
||||
<span class="line"> <span class="hljs-type">float</span> GGXL = NoV * <span class="hljs-built_in">sqrt</span>((-NoL * a2 + NoL) * NoL + a2);</span>
|
||||
<span class="line"> <span class="hljs-type">float</span> GGXV = NoL * <span class="hljs-built_in">sqrt</span>((-NoV * a2 + NoV) * NoV + a2);</span>
|
||||
<span class="line"> <span class="hljs-keyword">return</span> (<span class="hljs-number">2</span> * NoL) / (GGXV + GGXL);</span>
|
||||
<span class="line">}</span>
|
||||
<span class="line"></span>
|
||||
<span class="line">float2 <span class="hljs-title function_">DFG</span><span class="hljs-params">(<span class="hljs-type">float</span> NoV, <span class="hljs-type">float</span> a)</span> {</span>
|
||||
<span class="line">float2 DFG(<span class="hljs-type">float</span> NoV, <span class="hljs-type">float</span> a) {</span>
|
||||
<span class="line"> float3 V;</span>
|
||||
<span class="line"> V.x = sqrt(<span class="hljs-number">1.0f</span> - NoV*NoV);</span>
|
||||
<span class="line"> V.y = <span class="hljs-number">0.0f</span>;</span>
|
||||
<span class="line"> V.x = <span class="hljs-built_in">sqrt</span>(<span class="hljs-number">1.0</span>f - NoV*NoV);</span>
|
||||
<span class="line"> V.y = <span class="hljs-number">0.0</span>f;</span>
|
||||
<span class="line"> V.z = NoV;</span>
|
||||
<span class="line"></span>
|
||||
<span class="line"> <span class="hljs-type">float2</span> <span class="hljs-variable">r</span> <span class="hljs-operator">=</span> <span class="hljs-number">0.0f</span>;</span>
|
||||
<span class="line"> <span class="hljs-keyword">for</span> (<span class="hljs-type">uint</span> <span class="hljs-variable">i</span> <span class="hljs-operator">=</span> <span class="hljs-number">0</span>; i < sampleCount; i++) {</span>
|
||||
<span class="line"> <span class="hljs-type">float2</span> <span class="hljs-variable">Xi</span> <span class="hljs-operator">=</span> hammersley(i, sampleCount);</span>
|
||||
<span class="line"> <span class="hljs-type">float3</span> <span class="hljs-variable">H</span> <span class="hljs-operator">=</span> importanceSampleGGX(Xi, a, N);</span>
|
||||
<span class="line"> <span class="hljs-type">float3</span> <span class="hljs-variable">L</span> <span class="hljs-operator">=</span> <span class="hljs-number">2.0f</span> * dot(V, H) * H - V;</span>
|
||||
<span class="line"> float2 r = <span class="hljs-number">0.0</span>f;</span>
|
||||
<span class="line"> <span class="hljs-keyword">for</span> (<span class="hljs-type">uint</span> i = <span class="hljs-number">0</span>; i < sampleCount; i++) {</span>
|
||||
<span class="line"> float2 Xi = hammersley(i, sampleCount);</span>
|
||||
<span class="line"> float3 H = importanceSampleGGX(Xi, a, N);</span>
|
||||
<span class="line"> float3 L = <span class="hljs-number">2.0</span>f * <span class="hljs-built_in">dot</span>(V, H) * H - V;</span>
|
||||
<span class="line"></span>
|
||||
<span class="line"> <span class="hljs-type">float</span> <span class="hljs-variable">VoH</span> <span class="hljs-operator">=</span> saturate(dot(V, H));</span>
|
||||
<span class="line"> <span class="hljs-type">float</span> <span class="hljs-variable">NoL</span> <span class="hljs-operator">=</span> saturate(L.z);</span>
|
||||
<span class="line"> <span class="hljs-type">float</span> <span class="hljs-variable">NoH</span> <span class="hljs-operator">=</span> saturate(H.z);</span>
|
||||
<span class="line"> <span class="hljs-type">float</span> VoH = saturate(<span class="hljs-built_in">dot</span>(V, H));</span>
|
||||
<span class="line"> <span class="hljs-type">float</span> NoL = saturate(L.z);</span>
|
||||
<span class="line"> <span class="hljs-type">float</span> NoH = saturate(H.z);</span>
|
||||
<span class="line"></span>
|
||||
<span class="line"> <span class="hljs-keyword">if</span> (NoL > <span class="hljs-number">0.0f</span>) {</span>
|
||||
<span class="line"> <span class="hljs-type">float</span> <span class="hljs-variable">G</span> <span class="hljs-operator">=</span> GDFG(NoV, NoL, a);</span>
|
||||
<span class="line"> <span class="hljs-type">float</span> <span class="hljs-variable">Gv</span> <span class="hljs-operator">=</span> G * VoH / NoH;</span>
|
||||
<span class="line"> <span class="hljs-type">float</span> <span class="hljs-variable">Fc</span> <span class="hljs-operator">=</span> pow(<span class="hljs-number">1</span> - VoH, <span class="hljs-number">5.0f</span>);</span>
|
||||
<span class="line"> <span class="hljs-keyword">if</span> (NoL > <span class="hljs-number">0.0</span>f) {</span>
|
||||
<span class="line"> <span class="hljs-type">float</span> G = GDFG(NoV, NoL, a);</span>
|
||||
<span class="line"> <span class="hljs-type">float</span> Gv = G * VoH / NoH;</span>
|
||||
<span class="line"> <span class="hljs-type">float</span> Fc = <span class="hljs-built_in">pow</span>(<span class="hljs-number">1</span> - VoH, <span class="hljs-number">5.0</span>f);</span>
|
||||
<span class="line"> r.x += Gv * (<span class="hljs-number">1</span> - Fc);</span>
|
||||
<span class="line"> r.y += Gv * Fc;</span>
|
||||
<span class="line"> }</span>
|
||||
<span class="line"> }</span>
|
||||
<span class="line"> <span class="hljs-keyword">return</span> r * (<span class="hljs-number">1.0f</span> / sampleCount);</span>
|
||||
<span class="line"> <span class="hljs-keyword">return</span> r * (<span class="hljs-number">1.0</span>f / sampleCount);</span>
|
||||
<span class="line">}</span></code></pre><center><div class="listingcaption tilde">C++ implementation of the \( L_{DFG} \) term</div></center>
|
||||
<a class="target" name="sphericalharmonics"> </a><a class="target" name="annex/sphericalharmonics"> </a><a class="target" name="toc9.6"> </a><h2 id="spherical-harmonics"><a class="header" href="#spherical-harmonics">Spherical Harmonics</a></h2>
|
||||
<p>
|
||||
@@ -3851,56 +3851,56 @@ sin(m \phi + \phi) &= sin(m \phi) cos(\phi) + cos(m \phi) sin(\phi) \Leftrightar
|
||||
\end{align*}$$
|
||||
</p><p>
|
||||
<a href="#listing_nonnormalizedshbasis">Listing 47</a> shows the C++ code to compute the non-normalized SH basis \(\frac{y^m_l(s)}{\sqrt{2} K^m_l}\):
|
||||
</p><pre class="listing tilde"><code><span class="line"><span class="hljs-function"><span class="hljs-type">static</span> <span class="hljs-keyword">inline</span> <span class="hljs-type">size_t</span> <span class="hljs-title">SHindex</span><span class="hljs-params">(<span class="hljs-type">ssize_t</span> m, <span class="hljs-type">size_t</span> l)</span> </span>{</span>
|
||||
</p><pre class="listing tilde"><code><span class="line"><span class="hljs-function"><span class="hljs-keyword">static</span> <span class="hljs-keyword">inline</span> <span class="hljs-keyword">size_t</span> <span class="hljs-title">SHindex</span><span class="hljs-params">(<span class="hljs-keyword">ssize_t</span> m, <span class="hljs-keyword">size_t</span> l)</span> </span>{</span>
|
||||
<span class="line"> <span class="hljs-keyword">return</span> l * (l + <span class="hljs-number">1</span>) + m;</span>
|
||||
<span class="line">}</span>
|
||||
<span class="line"></span>
|
||||
<span class="line"><span class="hljs-function"><span class="hljs-type">void</span> <span class="hljs-title">computeShBasis</span><span class="hljs-params">(</span>
|
||||
<span class="line"> <span class="hljs-type">double</span>* <span class="hljs-type">const</span> SHb,</span>
|
||||
<span class="line"> <span class="hljs-type">size_t</span> numBands,</span>
|
||||
<span class="line"> <span class="hljs-type">const</span> vec3& s)</span></span>
|
||||
<span class="line"><span class="hljs-function"><span class="hljs-keyword">void</span> <span class="hljs-title">computeShBasis</span><span class="hljs-params">(</span>
|
||||
<span class="line"> <span class="hljs-keyword">double</span>* <span class="hljs-keyword">const</span> SHb,</span>
|
||||
<span class="line"> <span class="hljs-keyword">size_t</span> numBands,</span>
|
||||
<span class="line"> <span class="hljs-keyword">const</span> vec3& s)</span></span>
|
||||
<span class="line"></span>{</span>
|
||||
<span class="line"> <span class="hljs-comment">// handle m=0 separately, since it produces only one coefficient</span></span>
|
||||
<span class="line"> <span class="hljs-type">double</span> Pml_2 = <span class="hljs-number">0</span>;</span>
|
||||
<span class="line"> <span class="hljs-type">double</span> Pml_1 = <span class="hljs-number">1</span>;</span>
|
||||
<span class="line"> <span class="hljs-keyword">double</span> Pml_2 = <span class="hljs-number">0</span>;</span>
|
||||
<span class="line"> <span class="hljs-keyword">double</span> Pml_1 = <span class="hljs-number">1</span>;</span>
|
||||
<span class="line"> SHb[<span class="hljs-number">0</span>] = Pml_1;</span>
|
||||
<span class="line"> <span class="hljs-keyword">for</span> (<span class="hljs-type">ssize_t</span> l = <span class="hljs-number">1</span>; l < numBands; l++) {</span>
|
||||
<span class="line"> <span class="hljs-type">double</span> Pml = ((<span class="hljs-number">2</span> * l - <span class="hljs-number">1</span>) * Pml_1 * s.z - (l - <span class="hljs-number">1</span>) * Pml_2) / l;</span>
|
||||
<span class="line"> <span class="hljs-keyword">for</span> (<span class="hljs-keyword">ssize_t</span> l = <span class="hljs-number">1</span>; l < numBands; l++) {</span>
|
||||
<span class="line"> <span class="hljs-keyword">double</span> Pml = ((<span class="hljs-number">2</span> * l - <span class="hljs-number">1</span>) * Pml_1 * s.z - (l - <span class="hljs-number">1</span>) * Pml_2) / l;</span>
|
||||
<span class="line"> Pml_2 = Pml_1;</span>
|
||||
<span class="line"> Pml_1 = Pml;</span>
|
||||
<span class="line"> SHb[<span class="hljs-built_in">SHindex</span>(<span class="hljs-number">0</span>, l)] = Pml;</span>
|
||||
<span class="line"> SHb[SHindex(<span class="hljs-number">0</span>, l)] = Pml;</span>
|
||||
<span class="line"> }</span>
|
||||
<span class="line"> <span class="hljs-type">double</span> Pmm = <span class="hljs-number">1</span>;</span>
|
||||
<span class="line"> <span class="hljs-keyword">for</span> (<span class="hljs-type">ssize_t</span> m = <span class="hljs-number">1</span>; m < numBands ; m++) {</span>
|
||||
<span class="line"> <span class="hljs-keyword">double</span> Pmm = <span class="hljs-number">1</span>;</span>
|
||||
<span class="line"> <span class="hljs-keyword">for</span> (<span class="hljs-keyword">ssize_t</span> m = <span class="hljs-number">1</span>; m < numBands ; m++) {</span>
|
||||
<span class="line"> Pmm = (<span class="hljs-number">1</span> - <span class="hljs-number">2</span> * m) * Pmm;</span>
|
||||
<span class="line"> <span class="hljs-type">double</span> Pml_2 = Pmm;</span>
|
||||
<span class="line"> <span class="hljs-type">double</span> Pml_1 = (<span class="hljs-number">2</span> * m + <span class="hljs-number">1</span>)*Pmm*s.z;</span>
|
||||
<span class="line"> <span class="hljs-keyword">double</span> Pml_2 = Pmm;</span>
|
||||
<span class="line"> <span class="hljs-keyword">double</span> Pml_1 = (<span class="hljs-number">2</span> * m + <span class="hljs-number">1</span>)*Pmm*s.z;</span>
|
||||
<span class="line"> <span class="hljs-comment">// l == m</span></span>
|
||||
<span class="line"> SHb[<span class="hljs-built_in">SHindex</span>(-m, m)] = Pml_2;</span>
|
||||
<span class="line"> SHb[<span class="hljs-built_in">SHindex</span>( m, m)] = Pml_2;</span>
|
||||
<span class="line"> SHb[SHindex(-m, m)] = Pml_2;</span>
|
||||
<span class="line"> SHb[SHindex( m, m)] = Pml_2;</span>
|
||||
<span class="line"> <span class="hljs-keyword">if</span> (m + <span class="hljs-number">1</span> < numBands) {</span>
|
||||
<span class="line"> <span class="hljs-comment">// l == m+1</span></span>
|
||||
<span class="line"> SHb[<span class="hljs-built_in">SHindex</span>(-m, m + <span class="hljs-number">1</span>)] = Pml_1;</span>
|
||||
<span class="line"> SHb[<span class="hljs-built_in">SHindex</span>( m, m + <span class="hljs-number">1</span>)] = Pml_1;</span>
|
||||
<span class="line"> <span class="hljs-keyword">for</span> (<span class="hljs-type">ssize_t</span> l = m + <span class="hljs-number">2</span>; l < numBands; l++) {</span>
|
||||
<span class="line"> <span class="hljs-type">double</span> Pml = ((<span class="hljs-number">2</span> * l - <span class="hljs-number">1</span>) * Pml_1 * s.z - (l + m - <span class="hljs-number">1</span>) * Pml_2)</span>
|
||||
<span class="line"> SHb[SHindex(-m, m + <span class="hljs-number">1</span>)] = Pml_1;</span>
|
||||
<span class="line"> SHb[SHindex( m, m + <span class="hljs-number">1</span>)] = Pml_1;</span>
|
||||
<span class="line"> <span class="hljs-keyword">for</span> (<span class="hljs-keyword">ssize_t</span> l = m + <span class="hljs-number">2</span>; l < numBands; l++) {</span>
|
||||
<span class="line"> <span class="hljs-keyword">double</span> Pml = ((<span class="hljs-number">2</span> * l - <span class="hljs-number">1</span>) * Pml_1 * s.z - (l + m - <span class="hljs-number">1</span>) * Pml_2)</span>
|
||||
<span class="line"> / (l - m);</span>
|
||||
<span class="line"> Pml_2 = Pml_1;</span>
|
||||
<span class="line"> Pml_1 = Pml;</span>
|
||||
<span class="line"> SHb[<span class="hljs-built_in">SHindex</span>(-m, l)] = Pml;</span>
|
||||
<span class="line"> SHb[<span class="hljs-built_in">SHindex</span>( m, l)] = Pml;</span>
|
||||
<span class="line"> SHb[SHindex(-m, l)] = Pml;</span>
|
||||
<span class="line"> SHb[SHindex( m, l)] = Pml;</span>
|
||||
<span class="line"> }</span>
|
||||
<span class="line"> }</span>
|
||||
<span class="line"> }</span>
|
||||
<span class="line"> <span class="hljs-type">double</span> Cm = s.x;</span>
|
||||
<span class="line"> <span class="hljs-type">double</span> Sm = s.y;</span>
|
||||
<span class="line"> <span class="hljs-keyword">for</span> (<span class="hljs-type">ssize_t</span> m = <span class="hljs-number">1</span>; m <= numBands ; m++) {</span>
|
||||
<span class="line"> <span class="hljs-keyword">for</span> (<span class="hljs-type">ssize_t</span> l = m; l < numBands ; l++) {</span>
|
||||
<span class="line"> SHb[<span class="hljs-built_in">SHindex</span>(-m, l)] *= Sm;</span>
|
||||
<span class="line"> SHb[<span class="hljs-built_in">SHindex</span>( m, l)] *= Cm;</span>
|
||||
<span class="line"> <span class="hljs-keyword">double</span> Cm = s.x;</span>
|
||||
<span class="line"> <span class="hljs-keyword">double</span> Sm = s.y;</span>
|
||||
<span class="line"> <span class="hljs-keyword">for</span> (<span class="hljs-keyword">ssize_t</span> m = <span class="hljs-number">1</span>; m <= numBands ; m++) {</span>
|
||||
<span class="line"> <span class="hljs-keyword">for</span> (<span class="hljs-keyword">ssize_t</span> l = m; l < numBands ; l++) {</span>
|
||||
<span class="line"> SHb[SHindex(-m, l)] *= Sm;</span>
|
||||
<span class="line"> SHb[SHindex( m, l)] *= Cm;</span>
|
||||
<span class="line"> }</span>
|
||||
<span class="line"> <span class="hljs-type">double</span> Cm1 = Cm * s.x - Sm * s.y;</span>
|
||||
<span class="line"> <span class="hljs-type">double</span> Sm1 = Sm * s.x + Cm * s.y;</span>
|
||||
<span class="line"> <span class="hljs-keyword">double</span> Cm1 = Cm * s.x - Sm * s.y;</span>
|
||||
<span class="line"> <span class="hljs-keyword">double</span> Sm1 = Sm * s.x + Cm * s.y;</span>
|
||||
<span class="line"> Cm = Cm1;</span>
|
||||
<span class="line"> Sm = Sm1;</span>
|
||||
<span class="line"> }</span>
|
||||
@@ -3979,10 +3979,10 @@ $$\begin{equation}
|
||||
\end{equation}$$
|
||||
</p><p>
|
||||
Here is the C++ code to compute \(\hat{C}_l\):
|
||||
</p><pre class="listing tilde"><code><span class="line"><span class="hljs-function"><span class="hljs-type">static</span> <span class="hljs-type">double</span> <span class="hljs-title">factorial</span><span class="hljs-params">(<span class="hljs-type">size_t</span> n, <span class="hljs-type">size_t</span> d = <span class="hljs-number">1</span>)</span></span>;</span>
|
||||
</p><pre class="listing tilde"><code><span class="line"><span class="hljs-function"><span class="hljs-keyword">static</span> <span class="hljs-keyword">double</span> <span class="hljs-title">factorial</span><span class="hljs-params">(<span class="hljs-keyword">size_t</span> n, <span class="hljs-keyword">size_t</span> d = <span class="hljs-number">1</span>)</span></span>;</span>
|
||||
<span class="line"></span>
|
||||
<span class="line"><span class="hljs-comment">// < cos(theta) > SH coefficients pre-multiplied by 1 / K(0,l)</span></span>
|
||||
<span class="line"><span class="hljs-function"><span class="hljs-type">double</span> <span class="hljs-title">computeTruncatedCosSh</span><span class="hljs-params">(<span class="hljs-type">size_t</span> l)</span> </span>{</span>
|
||||
<span class="line"><span class="hljs-function"><span class="hljs-keyword">double</span> <span class="hljs-title">computeTruncatedCosSh</span><span class="hljs-params">(<span class="hljs-keyword">size_t</span> l)</span> </span>{</span>
|
||||
<span class="line"> <span class="hljs-keyword">if</span> (l == <span class="hljs-number">0</span>) {</span>
|
||||
<span class="line"> <span class="hljs-keyword">return</span> M_PI;</span>
|
||||
<span class="line"> } <span class="hljs-keyword">else</span> <span class="hljs-keyword">if</span> (l == <span class="hljs-number">1</span>) {</span>
|
||||
@@ -3990,17 +3990,17 @@ Here is the C++ code to compute \(\hat{C}_l\):
|
||||
<span class="line"> } <span class="hljs-keyword">else</span> <span class="hljs-keyword">if</span> (l & <span class="hljs-number">1</span>) {</span>
|
||||
<span class="line"> <span class="hljs-keyword">return</span> <span class="hljs-number">0</span>;</span>
|
||||
<span class="line"> }</span>
|
||||
<span class="line"> <span class="hljs-type">const</span> <span class="hljs-type">size_t</span> l_2 = l / <span class="hljs-number">2</span>;</span>
|
||||
<span class="line"> <span class="hljs-type">double</span> A0 = ((l_2 & <span class="hljs-number">1</span>) ? <span class="hljs-number">1.0</span> : <span class="hljs-number">-1.0</span>) / ((l + <span class="hljs-number">2</span>) * (l - <span class="hljs-number">1</span>));</span>
|
||||
<span class="line"> <span class="hljs-type">double</span> A1 = <span class="hljs-built_in">factorial</span>(l, l_2) / (<span class="hljs-built_in">factorial</span>(l_2) * (<span class="hljs-number">1</span> << l));</span>
|
||||
<span class="line"> <span class="hljs-keyword">const</span> <span class="hljs-keyword">size_t</span> l_2 = l / <span class="hljs-number">2</span>;</span>
|
||||
<span class="line"> <span class="hljs-keyword">double</span> A0 = ((l_2 & <span class="hljs-number">1</span>) ? <span class="hljs-number">1.0</span> : <span class="hljs-number">-1.0</span>) / ((l + <span class="hljs-number">2</span>) * (l - <span class="hljs-number">1</span>));</span>
|
||||
<span class="line"> <span class="hljs-keyword">double</span> A1 = factorial(l, l_2) / (factorial(l_2) * (<span class="hljs-number">1</span> << l));</span>
|
||||
<span class="line"> <span class="hljs-keyword">return</span> <span class="hljs-number">2</span> * M_PI * A0 * A1;</span>
|
||||
<span class="line">}</span>
|
||||
<span class="line"></span>
|
||||
<span class="line"><span class="hljs-comment">// returns n! / d!</span></span>
|
||||
<span class="line"><span class="hljs-function"><span class="hljs-type">double</span> <span class="hljs-title">factorial</span><span class="hljs-params">(<span class="hljs-type">size_t</span> n, <span class="hljs-type">size_t</span> d )</span> </span>{</span>
|
||||
<span class="line"> d = std::<span class="hljs-built_in">max</span>(<span class="hljs-built_in">size_t</span>(<span class="hljs-number">1</span>), d);</span>
|
||||
<span class="line"> n = std::<span class="hljs-built_in">max</span>(<span class="hljs-built_in">size_t</span>(<span class="hljs-number">1</span>), n);</span>
|
||||
<span class="line"> <span class="hljs-type">double</span> r = <span class="hljs-number">1.0</span>;</span>
|
||||
<span class="line"><span class="hljs-function"><span class="hljs-keyword">double</span> <span class="hljs-title">factorial</span><span class="hljs-params">(<span class="hljs-keyword">size_t</span> n, <span class="hljs-keyword">size_t</span> d )</span> </span>{</span>
|
||||
<span class="line"> d = <span class="hljs-built_in">std</span>::max(<span class="hljs-keyword">size_t</span>(<span class="hljs-number">1</span>), d);</span>
|
||||
<span class="line"> n = <span class="hljs-built_in">std</span>::max(<span class="hljs-keyword">size_t</span>(<span class="hljs-number">1</span>), n);</span>
|
||||
<span class="line"> <span class="hljs-keyword">double</span> r = <span class="hljs-number">1.0</span>;</span>
|
||||
<span class="line"> <span class="hljs-keyword">if</span> (n == d) {</span>
|
||||
<span class="line"> <span class="hljs-comment">// intentionally left blank</span></span>
|
||||
<span class="line"> } <span class="hljs-keyword">else</span> <span class="hljs-keyword">if</span> (n > d) {</span>
|
||||
|
||||
@@ -185,11 +185,11 @@ the <em>correct</em> fix in both the next release candidate branch <em>and</em>
|
||||
|
||||
<nav class="nav-wrapper" aria-label="Page navigation">
|
||||
<!-- Mobile navigation buttons -->
|
||||
<a rel="prev" href="../release/versioning.html" class="mobile-nav-chapters previous" title="Previous chapter" aria-label="Previous chapter" aria-keyshortcuts="Left">
|
||||
<a rel="prev" href="../notes/versioning.html" class="mobile-nav-chapters previous" title="Previous chapter" aria-label="Previous chapter" aria-keyshortcuts="Left">
|
||||
<i class="fa fa-angle-left"></i>
|
||||
</a>
|
||||
|
||||
<a rel="next prefetch" href="../dup/docs.html" class="mobile-nav-chapters next" title="Next chapter" aria-label="Next chapter" aria-keyshortcuts="Right">
|
||||
<a rel="next prefetch" href="../notes/release_guide.html" class="mobile-nav-chapters next" title="Next chapter" aria-label="Next chapter" aria-keyshortcuts="Right">
|
||||
<i class="fa fa-angle-right"></i>
|
||||
</a>
|
||||
|
||||
@@ -199,11 +199,11 @@ the <em>correct</em> fix in both the next release candidate branch <em>and</em>
|
||||
</div>
|
||||
|
||||
<nav class="nav-wide-wrapper" aria-label="Page navigation">
|
||||
<a rel="prev" href="../release/versioning.html" class="nav-chapters previous" title="Previous chapter" aria-label="Previous chapter" aria-keyshortcuts="Left">
|
||||
<a rel="prev" href="../notes/versioning.html" class="nav-chapters previous" title="Previous chapter" aria-label="Previous chapter" aria-keyshortcuts="Left">
|
||||
<i class="fa fa-angle-left"></i>
|
||||
</a>
|
||||
|
||||
<a rel="next prefetch" href="../dup/docs.html" class="nav-chapters next" title="Next chapter" aria-label="Next chapter" aria-keyshortcuts="Right">
|
||||
<a rel="next prefetch" href="../notes/release_guide.html" class="nav-chapters next" title="Next chapter" aria-label="Next chapter" aria-keyshortcuts="Right">
|
||||
<i class="fa fa-angle-right"></i>
|
||||
</a>
|
||||
</nav>
|
||||
@@ -165,7 +165,7 @@
|
||||
|
||||
<nav class="nav-wrapper" aria-label="Page navigation">
|
||||
<!-- Mobile navigation buttons -->
|
||||
<a rel="prev" href="../dup/test_ci_renderdiff.html" class="mobile-nav-chapters previous" title="Previous chapter" aria-label="Previous chapter" aria-keyshortcuts="Left">
|
||||
<a rel="prev" href="../dup/renderdiff.html" class="mobile-nav-chapters previous" title="Previous chapter" aria-label="Previous chapter" aria-keyshortcuts="Left">
|
||||
<i class="fa fa-angle-left"></i>
|
||||
</a>
|
||||
|
||||
@@ -179,7 +179,7 @@
|
||||
</div>
|
||||
|
||||
<nav class="nav-wide-wrapper" aria-label="Page navigation">
|
||||
<a rel="prev" href="../dup/test_ci_renderdiff.html" class="nav-chapters previous" title="Previous chapter" aria-label="Previous chapter" aria-keyshortcuts="Left">
|
||||
<a rel="prev" href="../dup/renderdiff.html" class="nav-chapters previous" title="Previous chapter" aria-label="Previous chapter" aria-keyshortcuts="Left">
|
||||
<i class="fa fa-angle-left"></i>
|
||||
</a>
|
||||
|
||||
|
||||
@@ -668,7 +668,7 @@ Values can be <strong>negative</strong> to change the orientation of the specula
|
||||
<i class="fa fa-angle-left"></i>
|
||||
</a>
|
||||
|
||||
<a rel="next prefetch" href="../release/index.html" class="mobile-nav-chapters next" title="Next chapter" aria-label="Next chapter" aria-keyshortcuts="Right">
|
||||
<a rel="next prefetch" href="../notes/versioning.html" class="mobile-nav-chapters next" title="Next chapter" aria-label="Next chapter" aria-keyshortcuts="Right">
|
||||
<i class="fa fa-angle-right"></i>
|
||||
</a>
|
||||
|
||||
@@ -682,7 +682,7 @@ Values can be <strong>negative</strong> to change the orientation of the specula
|
||||
<i class="fa fa-angle-left"></i>
|
||||
</a>
|
||||
|
||||
<a rel="next prefetch" href="../release/index.html" class="nav-chapters next" title="Next chapter" aria-label="Next chapter" aria-keyshortcuts="Right">
|
||||
<a rel="next prefetch" href="../notes/versioning.html" class="nav-chapters next" title="Next chapter" aria-label="Next chapter" aria-keyshortcuts="Right">
|
||||
<i class="fa fa-angle-right"></i>
|
||||
</a>
|
||||
</nav>
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
<head>
|
||||
<!-- Book generated using mdBook -->
|
||||
<meta charset="UTF-8">
|
||||
<title>Github - Filament</title>
|
||||
<title>Release Guide - Filament</title>
|
||||
|
||||
|
||||
<!-- Custom HTML head -->
|
||||
@@ -250,11 +250,11 @@ release (for example, v1.42.2).</p>
|
||||
|
||||
<nav class="nav-wrapper" aria-label="Page navigation">
|
||||
<!-- Mobile navigation buttons -->
|
||||
<a rel="prev" href="../release/maven.html" class="mobile-nav-chapters previous" title="Previous chapter" aria-label="Previous chapter" aria-keyshortcuts="Left">
|
||||
<a rel="prev" href="../notes/branching.html" class="mobile-nav-chapters previous" title="Previous chapter" aria-label="Previous chapter" aria-keyshortcuts="Left">
|
||||
<i class="fa fa-angle-left"></i>
|
||||
</a>
|
||||
|
||||
<a rel="next prefetch" href="../release/versioning.html" class="mobile-nav-chapters next" title="Next chapter" aria-label="Next chapter" aria-keyshortcuts="Right">
|
||||
<a rel="next prefetch" href="../dup/docs.html" class="mobile-nav-chapters next" title="Next chapter" aria-label="Next chapter" aria-keyshortcuts="Right">
|
||||
<i class="fa fa-angle-right"></i>
|
||||
</a>
|
||||
|
||||
@@ -264,11 +264,11 @@ release (for example, v1.42.2).</p>
|
||||
</div>
|
||||
|
||||
<nav class="nav-wide-wrapper" aria-label="Page navigation">
|
||||
<a rel="prev" href="../release/maven.html" class="nav-chapters previous" title="Previous chapter" aria-label="Previous chapter" aria-keyshortcuts="Left">
|
||||
<a rel="prev" href="../notes/branching.html" class="nav-chapters previous" title="Previous chapter" aria-label="Previous chapter" aria-keyshortcuts="Left">
|
||||
<i class="fa fa-angle-left"></i>
|
||||
</a>
|
||||
|
||||
<a rel="next prefetch" href="../release/versioning.html" class="nav-chapters next" title="Next chapter" aria-label="Next chapter" aria-keyshortcuts="Right">
|
||||
<a rel="next prefetch" href="../dup/docs.html" class="nav-chapters next" title="Next chapter" aria-label="Next chapter" aria-keyshortcuts="Right">
|
||||
<i class="fa fa-angle-right"></i>
|
||||
</a>
|
||||
</nav>
|
||||
@@ -170,7 +170,7 @@ for in our Continuation Integration flow <a href="https://https://github.com/goo
|
||||
<i class="fa fa-angle-left"></i>
|
||||
</a>
|
||||
|
||||
<a rel="next prefetch" href="../dup/backend_test.html" class="mobile-nav-chapters next" title="Next chapter" aria-label="Next chapter" aria-keyshortcuts="Right">
|
||||
<a rel="next prefetch" href="../dup/renderdiff.html" class="mobile-nav-chapters next" title="Next chapter" aria-label="Next chapter" aria-keyshortcuts="Right">
|
||||
<i class="fa fa-angle-right"></i>
|
||||
</a>
|
||||
|
||||
@@ -184,7 +184,7 @@ for in our Continuation Integration flow <a href="https://https://github.com/goo
|
||||
<i class="fa fa-angle-left"></i>
|
||||
</a>
|
||||
|
||||
<a rel="next prefetch" href="../dup/backend_test.html" class="nav-chapters next" title="Next chapter" aria-label="Next chapter" aria-keyshortcuts="Right">
|
||||
<a rel="next prefetch" href="../dup/renderdiff.html" class="nav-chapters next" title="Next chapter" aria-label="Next chapter" aria-keyshortcuts="Right">
|
||||
<i class="fa fa-angle-right"></i>
|
||||
</a>
|
||||
</nav>
|
||||
|
||||
@@ -186,11 +186,11 @@ post-process materials. However for now these two numbers must be set to the sam
|
||||
|
||||
<nav class="nav-wrapper" aria-label="Page navigation">
|
||||
<!-- Mobile navigation buttons -->
|
||||
<a rel="prev" href="../release/guide.html" class="mobile-nav-chapters previous" title="Previous chapter" aria-label="Previous chapter" aria-keyshortcuts="Left">
|
||||
<a rel="prev" href="../notes/material_properties.html" class="mobile-nav-chapters previous" title="Previous chapter" aria-label="Previous chapter" aria-keyshortcuts="Left">
|
||||
<i class="fa fa-angle-left"></i>
|
||||
</a>
|
||||
|
||||
<a rel="next prefetch" href="../release/branching.html" class="mobile-nav-chapters next" title="Next chapter" aria-label="Next chapter" aria-keyshortcuts="Right">
|
||||
<a rel="next prefetch" href="../notes/branching.html" class="mobile-nav-chapters next" title="Next chapter" aria-label="Next chapter" aria-keyshortcuts="Right">
|
||||
<i class="fa fa-angle-right"></i>
|
||||
</a>
|
||||
|
||||
@@ -200,11 +200,11 @@ post-process materials. However for now these two numbers must be set to the sam
|
||||
</div>
|
||||
|
||||
<nav class="nav-wide-wrapper" aria-label="Page navigation">
|
||||
<a rel="prev" href="../release/guide.html" class="nav-chapters previous" title="Previous chapter" aria-label="Previous chapter" aria-keyshortcuts="Left">
|
||||
<a rel="prev" href="../notes/material_properties.html" class="nav-chapters previous" title="Previous chapter" aria-label="Previous chapter" aria-keyshortcuts="Left">
|
||||
<i class="fa fa-angle-left"></i>
|
||||
</a>
|
||||
|
||||
<a rel="next prefetch" href="../release/branching.html" class="nav-chapters next" title="Next chapter" aria-label="Next chapter" aria-keyshortcuts="Right">
|
||||
<a rel="next prefetch" href="../notes/branching.html" class="nav-chapters next" title="Next chapter" aria-label="Next chapter" aria-keyshortcuts="Right">
|
||||
<i class="fa fa-angle-right"></i>
|
||||
</a>
|
||||
</nav>
|
||||
@@ -1,249 +0,0 @@
|
||||
<!DOCTYPE HTML>
|
||||
<html lang="en" class="light sidebar-visible" dir="ltr">
|
||||
<head>
|
||||
<!-- Book generated using mdBook -->
|
||||
<meta charset="UTF-8">
|
||||
<title>CocoaPods - Filament</title>
|
||||
|
||||
|
||||
<!-- Custom HTML head -->
|
||||
|
||||
<meta name="description" content="">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||
<meta name="theme-color" content="#ffffff">
|
||||
|
||||
<link rel="shortcut icon" href="../favicon.png">
|
||||
<link rel="stylesheet" href="../css/variables.css">
|
||||
<link rel="stylesheet" href="../css/general.css">
|
||||
<link rel="stylesheet" href="../css/chrome.css">
|
||||
|
||||
<!-- Fonts -->
|
||||
<link rel="stylesheet" href="../FontAwesome/css/font-awesome.css">
|
||||
<link rel="stylesheet" href="../fonts/fonts.css">
|
||||
|
||||
<!-- Highlight.js Stylesheets -->
|
||||
<link rel="stylesheet" href="../highlight.css">
|
||||
<link rel="stylesheet" href="../tomorrow-night.css">
|
||||
<link rel="stylesheet" href="../ayu-highlight.css">
|
||||
|
||||
<!-- Custom theme stylesheets -->
|
||||
|
||||
<!-- MathJax -->
|
||||
<script async src="https://cdnjs.cloudflare.com/ajax/libs/mathjax/2.7.1/MathJax.js?config=TeX-AMS-MML_HTMLorMML"></script>
|
||||
|
||||
<!-- Provide site root to javascript -->
|
||||
<script>
|
||||
var path_to_root = "../";
|
||||
var default_theme = window.matchMedia("(prefers-color-scheme: dark)").matches ? "light" : "light";
|
||||
</script>
|
||||
<!-- Start loading toc.js asap -->
|
||||
<script src="../toc.js"></script>
|
||||
</head>
|
||||
<body>
|
||||
<div id="body-container">
|
||||
<!-- Work around some values being stored in localStorage wrapped in quotes -->
|
||||
<script>
|
||||
try {
|
||||
var theme = localStorage.getItem('mdbook-theme');
|
||||
var sidebar = localStorage.getItem('mdbook-sidebar');
|
||||
|
||||
if (theme.startsWith('"') && theme.endsWith('"')) {
|
||||
localStorage.setItem('mdbook-theme', theme.slice(1, theme.length - 1));
|
||||
}
|
||||
|
||||
if (sidebar.startsWith('"') && sidebar.endsWith('"')) {
|
||||
localStorage.setItem('mdbook-sidebar', sidebar.slice(1, sidebar.length - 1));
|
||||
}
|
||||
} catch (e) { }
|
||||
</script>
|
||||
|
||||
<!-- Set the theme before any content is loaded, prevents flash -->
|
||||
<script>
|
||||
var theme;
|
||||
try { theme = localStorage.getItem('mdbook-theme'); } catch(e) { }
|
||||
if (theme === null || theme === undefined) { theme = default_theme; }
|
||||
const html = document.documentElement;
|
||||
html.classList.remove('light')
|
||||
html.classList.add(theme);
|
||||
html.classList.add("js");
|
||||
</script>
|
||||
|
||||
<input type="checkbox" id="sidebar-toggle-anchor" class="hidden">
|
||||
|
||||
<!-- Hide / unhide sidebar before it is displayed -->
|
||||
<script>
|
||||
var sidebar = null;
|
||||
var sidebar_toggle = document.getElementById("sidebar-toggle-anchor");
|
||||
if (document.body.clientWidth >= 1080) {
|
||||
try { sidebar = localStorage.getItem('mdbook-sidebar'); } catch(e) { }
|
||||
sidebar = sidebar || 'visible';
|
||||
} else {
|
||||
sidebar = 'hidden';
|
||||
}
|
||||
sidebar_toggle.checked = sidebar === 'visible';
|
||||
html.classList.remove('sidebar-visible');
|
||||
html.classList.add("sidebar-" + sidebar);
|
||||
</script>
|
||||
|
||||
<nav id="sidebar" class="sidebar" aria-label="Table of contents">
|
||||
<div style="display:flex;align-items:center;justify-content:center">
|
||||
<img class="flogo" src="../images/filament_logo_small.png"></img>
|
||||
</div>
|
||||
<!-- populated by js -->
|
||||
<mdbook-sidebar-scrollbox class="sidebar-scrollbox"></mdbook-sidebar-scrollbox>
|
||||
<noscript>
|
||||
<iframe class="sidebar-iframe-outer" src="../toc.html"></iframe>
|
||||
</noscript>
|
||||
<div id="sidebar-resize-handle" class="sidebar-resize-handle">
|
||||
<div class="sidebar-resize-indicator"></div>
|
||||
</div>
|
||||
</nav>
|
||||
|
||||
<div id="page-wrapper" class="page-wrapper">
|
||||
|
||||
<div class="page">
|
||||
<div id="menu-bar-hover-placeholder"></div>
|
||||
<div id="menu-bar" class="menu-bar sticky">
|
||||
<div class="left-buttons">
|
||||
<label id="sidebar-toggle" class="icon-button" for="sidebar-toggle-anchor" title="Toggle Table of Contents" aria-label="Toggle Table of Contents" aria-controls="sidebar">
|
||||
<i class="fa fa-bars"></i>
|
||||
</label>
|
||||
<!-- Filament: disable themes because the markdeep part does not look good for dark themes -->
|
||||
<!--
|
||||
<button id="theme-toggle" class="icon-button" type="button" title="Change theme" aria-label="Change theme" aria-haspopup="true" aria-expanded="false" aria-controls="theme-list">
|
||||
<i class="fa fa-paint-brush"></i>
|
||||
</button>
|
||||
<ul id="theme-list" class="theme-popup" aria-label="Themes" role="menu">
|
||||
<li role="none"><button role="menuitem" class="theme" id="light">Light</button></li>
|
||||
<li role="none"><button role="menuitem" class="theme" id="rust">Rust</button></li>
|
||||
<li role="none"><button role="menuitem" class="theme" id="coal">Coal</button></li>
|
||||
<li role="none"><button role="menuitem" class="theme" id="navy">Navy</button></li>
|
||||
<li role="none"><button role="menuitem" class="theme" id="ayu">Ayu</button></li>
|
||||
</ul>
|
||||
-->
|
||||
<button id="search-toggle" class="icon-button" type="button" title="Search. (Shortkey: s)" aria-label="Toggle Searchbar" aria-expanded="false" aria-keyshortcuts="S" aria-controls="searchbar">
|
||||
<i class="fa fa-search"></i>
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<h1 class="menu-title">Filament</h1>
|
||||
|
||||
<div class="right-buttons">
|
||||
<a href="https://github.com/google/filament" title="Git repository" aria-label="Git repository">
|
||||
<i id="git-repository-button" class="fa fa-github"></i>
|
||||
</a>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div id="search-wrapper" class="hidden">
|
||||
<form id="searchbar-outer" class="searchbar-outer">
|
||||
<input type="search" id="searchbar" name="searchbar" placeholder="Search this book ..." aria-controls="searchresults-outer" aria-describedby="searchresults-header">
|
||||
</form>
|
||||
<div id="searchresults-outer" class="searchresults-outer hidden">
|
||||
<div id="searchresults-header" class="searchresults-header"></div>
|
||||
<ul id="searchresults">
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Apply ARIA attributes after the sidebar and the sidebar toggle button are added to the DOM -->
|
||||
<script>
|
||||
document.getElementById('sidebar-toggle').setAttribute('aria-expanded', sidebar === 'visible');
|
||||
document.getElementById('sidebar').setAttribute('aria-hidden', sidebar !== 'visible');
|
||||
Array.from(document.querySelectorAll('#sidebar a')).forEach(function(link) {
|
||||
link.setAttribute('tabIndex', sidebar === 'visible' ? 0 : -1);
|
||||
});
|
||||
</script>
|
||||
|
||||
<div id="content" class="content">
|
||||
<main>
|
||||
<h1 id="filament-cocoapods"><a class="header" href="#filament-cocoapods">Filament CocoaPods</a></h1>
|
||||
<p><a href="https://cocoapods.org/">CocoaPods</a> is the dependency manager Filament uses for Apple platforms
|
||||
(macOS and iOS).</p>
|
||||
<h2 id="installation"><a class="header" href="#installation">Installation</a></h2>
|
||||
<p>On macOS, CocoaPods requires a modern Ruby environment. Avoid using the system Ruby; instead,
|
||||
install the latest Ruby version using <code>brew</code>, <code>port,</code> or use a manager like <code>rbenv</code> or <code>rvm</code>. Once
|
||||
Ruby is configured:</p>
|
||||
<pre><code class="language-bash">gem install cocoapods
|
||||
</code></pre>
|
||||
<h2 id="linting-the-podspec"><a class="header" href="#linting-the-podspec">Linting the Podspec</a></h2>
|
||||
<p>The Filament podspec is configured to fetch releases directly from GitHub. Before deployment,
|
||||
CocoaPods must <strong>lint</strong> the spec to validate that the library compiles and links correctly across
|
||||
all supported architectures.</p>
|
||||
<p>To run the linter locally:</p>
|
||||
<pre><code class="language-bash">cd ios/CocoaPods/
|
||||
pod spec lint
|
||||
</code></pre>
|
||||
<h3 id="debugging-failures"><a class="header" href="#debugging-failures">Debugging Failures</a></h3>
|
||||
<p>If the lint fails, use the <code>--verbose</code> flag to inspect the underlying <code>xcodebuild</code> output:</p>
|
||||
<pre><code class="language-bash">pod spec lint --verbose
|
||||
</code></pre>
|
||||
<h2 id="isolating-subspecs"><a class="header" href="#isolating-subspecs">Isolating Subspecs</a></h2>
|
||||
<p>Filament is partitioned into multiple <strong>subspecs</strong>. CocoaPods lints these individually. To
|
||||
accelerate debugging or isolate a specific build failure (e.g., <code>Filament/filament</code>), use the
|
||||
following command:</p>
|
||||
<pre><code class="language-bash">pod spec lint --no-clean --verbose --subspec=Filament/filament 2>&1 | tee lint_output.log
|
||||
</code></pre>
|
||||
<ul>
|
||||
<li><code>--no-clean</code>: Preserves the temporary Xcode workspace and build artifacts in <code>/var/folders/...</code>
|
||||
for inspection.</li>
|
||||
<li><code>--verbose</code>: Prints the full compiler and linker invocations.</li>
|
||||
<li><code>--subspec</code>: Limits the scope of the lint to a specific component.</li>
|
||||
</ul>
|
||||
<blockquote>
|
||||
<p><strong>Tip:</strong> To view all defined subspecs, grep the podspec file:
|
||||
<code>grep "spec.subspec" ios/CocoaPods/Filament.podspec</code></p>
|
||||
</blockquote>
|
||||
|
||||
</main>
|
||||
|
||||
<nav class="nav-wrapper" aria-label="Page navigation">
|
||||
<!-- Mobile navigation buttons -->
|
||||
<a rel="prev" href="../release/index.html" class="mobile-nav-chapters previous" title="Previous chapter" aria-label="Previous chapter" aria-keyshortcuts="Left">
|
||||
<i class="fa fa-angle-left"></i>
|
||||
</a>
|
||||
|
||||
<a rel="next prefetch" href="../release/maven.html" class="mobile-nav-chapters next" title="Next chapter" aria-label="Next chapter" aria-keyshortcuts="Right">
|
||||
<i class="fa fa-angle-right"></i>
|
||||
</a>
|
||||
|
||||
<div style="clear: both"></div>
|
||||
</nav>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<nav class="nav-wide-wrapper" aria-label="Page navigation">
|
||||
<a rel="prev" href="../release/index.html" class="nav-chapters previous" title="Previous chapter" aria-label="Previous chapter" aria-keyshortcuts="Left">
|
||||
<i class="fa fa-angle-left"></i>
|
||||
</a>
|
||||
|
||||
<a rel="next prefetch" href="../release/maven.html" class="nav-chapters next" title="Next chapter" aria-label="Next chapter" aria-keyshortcuts="Right">
|
||||
<i class="fa fa-angle-right"></i>
|
||||
</a>
|
||||
</nav>
|
||||
|
||||
</div>
|
||||
|
||||
|
||||
|
||||
|
||||
<script>
|
||||
window.playground_copyable = true;
|
||||
</script>
|
||||
|
||||
|
||||
<script src="../elasticlunr.min.js"></script>
|
||||
<script src="../mark.min.js"></script>
|
||||
<script src="../searcher.js"></script>
|
||||
|
||||
<script src="../clipboard.min.js"></script>
|
||||
<script src="../highlight.js"></script>
|
||||
<script src="../book.js"></script>
|
||||
|
||||
<!-- Custom JS scripts -->
|
||||
|
||||
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
||||
@@ -1,215 +0,0 @@
|
||||
<!DOCTYPE HTML>
|
||||
<html lang="en" class="light sidebar-visible" dir="ltr">
|
||||
<head>
|
||||
<!-- Book generated using mdBook -->
|
||||
<meta charset="UTF-8">
|
||||
<title>Release - Filament</title>
|
||||
|
||||
|
||||
<!-- Custom HTML head -->
|
||||
|
||||
<meta name="description" content="">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||
<meta name="theme-color" content="#ffffff">
|
||||
|
||||
<link rel="shortcut icon" href="../favicon.png">
|
||||
<link rel="stylesheet" href="../css/variables.css">
|
||||
<link rel="stylesheet" href="../css/general.css">
|
||||
<link rel="stylesheet" href="../css/chrome.css">
|
||||
|
||||
<!-- Fonts -->
|
||||
<link rel="stylesheet" href="../FontAwesome/css/font-awesome.css">
|
||||
<link rel="stylesheet" href="../fonts/fonts.css">
|
||||
|
||||
<!-- Highlight.js Stylesheets -->
|
||||
<link rel="stylesheet" href="../highlight.css">
|
||||
<link rel="stylesheet" href="../tomorrow-night.css">
|
||||
<link rel="stylesheet" href="../ayu-highlight.css">
|
||||
|
||||
<!-- Custom theme stylesheets -->
|
||||
|
||||
<!-- MathJax -->
|
||||
<script async src="https://cdnjs.cloudflare.com/ajax/libs/mathjax/2.7.1/MathJax.js?config=TeX-AMS-MML_HTMLorMML"></script>
|
||||
|
||||
<!-- Provide site root to javascript -->
|
||||
<script>
|
||||
var path_to_root = "../";
|
||||
var default_theme = window.matchMedia("(prefers-color-scheme: dark)").matches ? "light" : "light";
|
||||
</script>
|
||||
<!-- Start loading toc.js asap -->
|
||||
<script src="../toc.js"></script>
|
||||
</head>
|
||||
<body>
|
||||
<div id="body-container">
|
||||
<!-- Work around some values being stored in localStorage wrapped in quotes -->
|
||||
<script>
|
||||
try {
|
||||
var theme = localStorage.getItem('mdbook-theme');
|
||||
var sidebar = localStorage.getItem('mdbook-sidebar');
|
||||
|
||||
if (theme.startsWith('"') && theme.endsWith('"')) {
|
||||
localStorage.setItem('mdbook-theme', theme.slice(1, theme.length - 1));
|
||||
}
|
||||
|
||||
if (sidebar.startsWith('"') && sidebar.endsWith('"')) {
|
||||
localStorage.setItem('mdbook-sidebar', sidebar.slice(1, sidebar.length - 1));
|
||||
}
|
||||
} catch (e) { }
|
||||
</script>
|
||||
|
||||
<!-- Set the theme before any content is loaded, prevents flash -->
|
||||
<script>
|
||||
var theme;
|
||||
try { theme = localStorage.getItem('mdbook-theme'); } catch(e) { }
|
||||
if (theme === null || theme === undefined) { theme = default_theme; }
|
||||
const html = document.documentElement;
|
||||
html.classList.remove('light')
|
||||
html.classList.add(theme);
|
||||
html.classList.add("js");
|
||||
</script>
|
||||
|
||||
<input type="checkbox" id="sidebar-toggle-anchor" class="hidden">
|
||||
|
||||
<!-- Hide / unhide sidebar before it is displayed -->
|
||||
<script>
|
||||
var sidebar = null;
|
||||
var sidebar_toggle = document.getElementById("sidebar-toggle-anchor");
|
||||
if (document.body.clientWidth >= 1080) {
|
||||
try { sidebar = localStorage.getItem('mdbook-sidebar'); } catch(e) { }
|
||||
sidebar = sidebar || 'visible';
|
||||
} else {
|
||||
sidebar = 'hidden';
|
||||
}
|
||||
sidebar_toggle.checked = sidebar === 'visible';
|
||||
html.classList.remove('sidebar-visible');
|
||||
html.classList.add("sidebar-" + sidebar);
|
||||
</script>
|
||||
|
||||
<nav id="sidebar" class="sidebar" aria-label="Table of contents">
|
||||
<div style="display:flex;align-items:center;justify-content:center">
|
||||
<img class="flogo" src="../images/filament_logo_small.png"></img>
|
||||
</div>
|
||||
<!-- populated by js -->
|
||||
<mdbook-sidebar-scrollbox class="sidebar-scrollbox"></mdbook-sidebar-scrollbox>
|
||||
<noscript>
|
||||
<iframe class="sidebar-iframe-outer" src="../toc.html"></iframe>
|
||||
</noscript>
|
||||
<div id="sidebar-resize-handle" class="sidebar-resize-handle">
|
||||
<div class="sidebar-resize-indicator"></div>
|
||||
</div>
|
||||
</nav>
|
||||
|
||||
<div id="page-wrapper" class="page-wrapper">
|
||||
|
||||
<div class="page">
|
||||
<div id="menu-bar-hover-placeholder"></div>
|
||||
<div id="menu-bar" class="menu-bar sticky">
|
||||
<div class="left-buttons">
|
||||
<label id="sidebar-toggle" class="icon-button" for="sidebar-toggle-anchor" title="Toggle Table of Contents" aria-label="Toggle Table of Contents" aria-controls="sidebar">
|
||||
<i class="fa fa-bars"></i>
|
||||
</label>
|
||||
<!-- Filament: disable themes because the markdeep part does not look good for dark themes -->
|
||||
<!--
|
||||
<button id="theme-toggle" class="icon-button" type="button" title="Change theme" aria-label="Change theme" aria-haspopup="true" aria-expanded="false" aria-controls="theme-list">
|
||||
<i class="fa fa-paint-brush"></i>
|
||||
</button>
|
||||
<ul id="theme-list" class="theme-popup" aria-label="Themes" role="menu">
|
||||
<li role="none"><button role="menuitem" class="theme" id="light">Light</button></li>
|
||||
<li role="none"><button role="menuitem" class="theme" id="rust">Rust</button></li>
|
||||
<li role="none"><button role="menuitem" class="theme" id="coal">Coal</button></li>
|
||||
<li role="none"><button role="menuitem" class="theme" id="navy">Navy</button></li>
|
||||
<li role="none"><button role="menuitem" class="theme" id="ayu">Ayu</button></li>
|
||||
</ul>
|
||||
-->
|
||||
<button id="search-toggle" class="icon-button" type="button" title="Search. (Shortkey: s)" aria-label="Toggle Searchbar" aria-expanded="false" aria-keyshortcuts="S" aria-controls="searchbar">
|
||||
<i class="fa fa-search"></i>
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<h1 class="menu-title">Filament</h1>
|
||||
|
||||
<div class="right-buttons">
|
||||
<a href="https://github.com/google/filament" title="Git repository" aria-label="Git repository">
|
||||
<i id="git-repository-button" class="fa fa-github"></i>
|
||||
</a>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div id="search-wrapper" class="hidden">
|
||||
<form id="searchbar-outer" class="searchbar-outer">
|
||||
<input type="search" id="searchbar" name="searchbar" placeholder="Search this book ..." aria-controls="searchresults-outer" aria-describedby="searchresults-header">
|
||||
</form>
|
||||
<div id="searchresults-outer" class="searchresults-outer hidden">
|
||||
<div id="searchresults-header" class="searchresults-header"></div>
|
||||
<ul id="searchresults">
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Apply ARIA attributes after the sidebar and the sidebar toggle button are added to the DOM -->
|
||||
<script>
|
||||
document.getElementById('sidebar-toggle').setAttribute('aria-expanded', sidebar === 'visible');
|
||||
document.getElementById('sidebar').setAttribute('aria-hidden', sidebar !== 'visible');
|
||||
Array.from(document.querySelectorAll('#sidebar a')).forEach(function(link) {
|
||||
link.setAttribute('tabIndex', sidebar === 'visible' ? 0 : -1);
|
||||
});
|
||||
</script>
|
||||
|
||||
<div id="content" class="content">
|
||||
<main>
|
||||
<h1 id="release"><a class="header" href="#release">Release</a></h1>
|
||||
<p>This is a collection of guides for releasing Filament on different distribution platforms, including
|
||||
Github, Maven, and CocoaPods.</p>
|
||||
|
||||
</main>
|
||||
|
||||
<nav class="nav-wrapper" aria-label="Page navigation">
|
||||
<!-- Mobile navigation buttons -->
|
||||
<a rel="prev" href="../notes/material_properties.html" class="mobile-nav-chapters previous" title="Previous chapter" aria-label="Previous chapter" aria-keyshortcuts="Left">
|
||||
<i class="fa fa-angle-left"></i>
|
||||
</a>
|
||||
|
||||
<a rel="next prefetch" href="../release/cocoapods.html" class="mobile-nav-chapters next" title="Next chapter" aria-label="Next chapter" aria-keyshortcuts="Right">
|
||||
<i class="fa fa-angle-right"></i>
|
||||
</a>
|
||||
|
||||
<div style="clear: both"></div>
|
||||
</nav>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<nav class="nav-wide-wrapper" aria-label="Page navigation">
|
||||
<a rel="prev" href="../notes/material_properties.html" class="nav-chapters previous" title="Previous chapter" aria-label="Previous chapter" aria-keyshortcuts="Left">
|
||||
<i class="fa fa-angle-left"></i>
|
||||
</a>
|
||||
|
||||
<a rel="next prefetch" href="../release/cocoapods.html" class="nav-chapters next" title="Next chapter" aria-label="Next chapter" aria-keyshortcuts="Right">
|
||||
<i class="fa fa-angle-right"></i>
|
||||
</a>
|
||||
</nav>
|
||||
|
||||
</div>
|
||||
|
||||
|
||||
|
||||
|
||||
<script>
|
||||
window.playground_copyable = true;
|
||||
</script>
|
||||
|
||||
|
||||
<script src="../elasticlunr.min.js"></script>
|
||||
<script src="../mark.min.js"></script>
|
||||
<script src="../searcher.js"></script>
|
||||
|
||||
<script src="../clipboard.min.js"></script>
|
||||
<script src="../highlight.js"></script>
|
||||
<script src="../book.js"></script>
|
||||
|
||||
<!-- Custom JS scripts -->
|
||||
|
||||
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
||||
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
@@ -1,311 +0,0 @@
|
||||
|
||||
// SimulatedSkybox.js
|
||||
// Ported from samples/utils/SimulatedSkybox.cpp
|
||||
|
||||
class SimulatedSkybox {
|
||||
constructor(engine) {
|
||||
this.engine = engine;
|
||||
|
||||
// Default Parameters
|
||||
this.sunDirection = [0, 1, 0];
|
||||
this.sunIntensity = 100000.0;
|
||||
this.turbidity = 2.0;
|
||||
this.rayleigh = 1.0;
|
||||
this.mieCoefficient = 1.0;
|
||||
this.mieG = 0.8;
|
||||
this.ozone = 0.0;
|
||||
this.msFactors = [0.1, 0.5, 0.0];
|
||||
this.contrast = 1.0;
|
||||
this.nightColor = [0.0, 0.0003, 0.00075];
|
||||
this.shimmerControl = [0.0, 20.0, 0.1];
|
||||
this.cloudControl = [0.0, 0.1, 8000.0, 0.0];
|
||||
this.cloudControl2 = [0.0, 0.0, 0.0, 0.0];
|
||||
this.waterControl = [50.0, 1.0, 1.0, 4.0]; // x=Strength, y=Speed, z=DerivativeTrick, w=Octaves
|
||||
this.starControl = [1.0, 1.0]; // x=Density (0-1), y=Enabled (0-1)
|
||||
this.planetRadius = 6360.0;
|
||||
|
||||
// Sun Halo
|
||||
// x=cos(rad), y=limbDarkening, z=intensity, w=enabled
|
||||
this.sunHalo = [Math.cos(0.5 * Math.PI / 180.0), 0.5, 1.0, 1.0];
|
||||
|
||||
this.initEntity();
|
||||
}
|
||||
|
||||
async loadMaterial(url) {
|
||||
console.log("Loading material from:", url);
|
||||
const response = await fetch(url);
|
||||
const buffer = await response.arrayBuffer();
|
||||
this.material = this.engine.createMaterial(new Uint8Array(buffer));
|
||||
this.materialInstance = this.material.createInstance();
|
||||
|
||||
// Re-bind the entity with the loaded material
|
||||
const rcm = this.engine.getRenderableManager();
|
||||
const instance = rcm.getInstance(this.entity);
|
||||
rcm.setMaterialInstanceAt(instance, 0, this.materialInstance);
|
||||
|
||||
console.log("Material loaded and bound.");
|
||||
this.updateCoefficients();
|
||||
}
|
||||
|
||||
initEntity() {
|
||||
const EntityManager = Filament.EntityManager;
|
||||
const RenderableManager = Filament.RenderableManager;
|
||||
const VertexBuffer = Filament.VertexBuffer;
|
||||
const IndexBuffer = Filament.IndexBuffer;
|
||||
const AttributeType = Filament.VertexBuffer$AttributeType;
|
||||
const VertexAttribute = Filament.VertexAttribute;
|
||||
const PrimitiveType = Filament.RenderableManager$PrimitiveType;
|
||||
const IndexType = Filament.IndexBuffer$IndexType;
|
||||
|
||||
this.entity = EntityManager.get().create();
|
||||
|
||||
// 3 vertices for full screen triangle
|
||||
// coords: -1,-1 to 3,-1 to -1,3
|
||||
const TRIANGLE_VERTICES = new Float32Array([
|
||||
-1.0, -1.0,
|
||||
3.0, -1.0,
|
||||
-1.0, 3.0
|
||||
]);
|
||||
|
||||
const TRIANGLE_INDICES = new Uint16Array([0, 1, 2]);
|
||||
|
||||
this.vb = VertexBuffer.Builder()
|
||||
.vertexCount(3)
|
||||
.bufferCount(1)
|
||||
.attribute(VertexAttribute.POSITION, 0, AttributeType.FLOAT2, 0, 8)
|
||||
.build(this.engine);
|
||||
|
||||
this.vb.setBufferAt(this.engine, 0, TRIANGLE_VERTICES);
|
||||
|
||||
this.ib = IndexBuffer.Builder()
|
||||
.indexCount(3)
|
||||
.bufferType(IndexType.USHORT)
|
||||
.build(this.engine);
|
||||
|
||||
this.ib.setBuffer(this.engine, TRIANGLE_INDICES);
|
||||
|
||||
// We create a dummy material first or wait?
|
||||
// In JS we usually can't block. We'll rely on loadMaterial being called.
|
||||
// For now, we build the Renderable without material, then set it later.
|
||||
|
||||
RenderableManager.Builder(1)
|
||||
.geometry(0, PrimitiveType.TRIANGLES, this.vb, this.ib)
|
||||
.culling(false)
|
||||
.castShadows(false)
|
||||
.receiveShadows(false)
|
||||
.priority(7) // Render behind translucent objects? 7 is skybox priority typically.
|
||||
.build(this.engine, this.entity);
|
||||
}
|
||||
|
||||
setSunPosition(direction) {
|
||||
// normalize
|
||||
const len = Math.hypot(direction[0], direction[1], direction[2]);
|
||||
if (len > 0) {
|
||||
this.sunDirection = [direction[0] / len, direction[1] / len, direction[2] / len];
|
||||
} else {
|
||||
this.sunDirection = [0, 1, 0];
|
||||
}
|
||||
this.updateCoefficients();
|
||||
}
|
||||
|
||||
setSunIntensity(intensity) {
|
||||
this.sunIntensity = Math.max(0.0, intensity);
|
||||
this.updateCoefficients();
|
||||
}
|
||||
|
||||
setTurbidity(turbidity) {
|
||||
this.turbidity = Math.max(0.0, turbidity);
|
||||
this.updateCoefficients();
|
||||
}
|
||||
|
||||
setRayleigh(rayleigh) {
|
||||
this.rayleigh = Math.max(0.0, rayleigh);
|
||||
this.updateCoefficients();
|
||||
}
|
||||
|
||||
setMieCoefficient(mie) {
|
||||
this.mieCoefficient = Math.max(0.0, mie);
|
||||
this.updateCoefficients();
|
||||
}
|
||||
|
||||
setMieG(g) {
|
||||
this.mieG = Math.max(0.0, g);
|
||||
this.updateCoefficients();
|
||||
}
|
||||
|
||||
setOzone(strength) {
|
||||
this.ozone = Math.max(0.0, strength);
|
||||
this.updateCoefficients();
|
||||
}
|
||||
|
||||
setMultiScattering(r, m) {
|
||||
this.msFactors[0] = Math.max(0.0, Math.min(2.0, r));
|
||||
this.msFactors[1] = Math.max(0.0, Math.min(2.0, m));
|
||||
this.updateCoefficients();
|
||||
}
|
||||
|
||||
setHorizonGlow(strength) {
|
||||
this.msFactors[2] = Math.max(0.0, Math.min(1.0, strength));
|
||||
this.updateCoefficients();
|
||||
}
|
||||
|
||||
setContrast(contrast) {
|
||||
this.contrast = contrast;
|
||||
this.updateCoefficients();
|
||||
}
|
||||
|
||||
setNightColor(color) {
|
||||
this.nightColor = color;
|
||||
this.updateCoefficients();
|
||||
}
|
||||
|
||||
setSunRadius(degrees) {
|
||||
const rad = degrees * (Math.PI / 180.0);
|
||||
this.sunHalo[0] = Math.cos(rad);
|
||||
this.updateCoefficients();
|
||||
}
|
||||
|
||||
setSunDiskIntensity(intensity) {
|
||||
this.sunHalo[2] = Math.max(0.0, intensity);
|
||||
this.updateCoefficients();
|
||||
}
|
||||
|
||||
setSunLimbDarkening(strength) {
|
||||
this.sunHalo[1] = Math.max(0.0, strength);
|
||||
this.updateCoefficients();
|
||||
}
|
||||
|
||||
setSunDiskEnabled(enabled) {
|
||||
this.sunHalo[3] = enabled ? 1.0 : 0.0;
|
||||
this.updateCoefficients();
|
||||
}
|
||||
|
||||
setShimmerControl(strength, frequency, maskHeight) {
|
||||
this.shimmerControl[0] = Math.max(0.0, strength);
|
||||
this.shimmerControl[1] = Math.max(0.0, frequency);
|
||||
this.shimmerControl[2] = Math.max(0.001, maskHeight);
|
||||
this.updateCoefficients();
|
||||
}
|
||||
|
||||
setCloudControl(coverage, density, height, speed) {
|
||||
this.cloudControl[0] = Math.max(0.0, Math.min(1.0, coverage));
|
||||
this.cloudControl[1] = Math.max(0.0, density);
|
||||
this.cloudControl[2] = Math.max(1000.0, height);
|
||||
// JS speed adjustment logic matches C++: speed * (0.05 / 72.0)
|
||||
this.cloudControl[3] = speed * (0.05 / 72.0);
|
||||
this.updateCoefficients();
|
||||
}
|
||||
|
||||
setCloudShapeEvolution(speed) {
|
||||
this.cloudControl2[0] = speed;
|
||||
this.updateCoefficients();
|
||||
}
|
||||
|
||||
setCloudVolumetricLighting(enabled) {
|
||||
this.cloudControl2[1] = enabled ? 1.0 : 0.0;
|
||||
this.updateCoefficients();
|
||||
}
|
||||
|
||||
setWaterControl(strength, speed, derivativeTrick, octaves) {
|
||||
this.waterControl[0] = Math.max(0.0, strength);
|
||||
this.waterControl[1] = Math.max(0.0, speed);
|
||||
this.waterControl[2] = derivativeTrick;
|
||||
this.waterControl[3] = Math.max(1.0, Math.min(8.0, octaves));
|
||||
this.updateCoefficients();
|
||||
}
|
||||
|
||||
setStarControl(density, enabled) {
|
||||
this.starControl[0] = Math.max(0.0, Math.min(1.0, density));
|
||||
this.starControl[1] = enabled ? 1.0 : 0.0;
|
||||
this.updateCoefficients();
|
||||
}
|
||||
|
||||
updateCoefficients() {
|
||||
if (!this.materialInstance) {
|
||||
console.warn("updateCoefficients called before material loaded");
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
// 1. Rayleigh Coefficients
|
||||
const F_PI = Math.PI;
|
||||
const lambda = [680e-9, 550e-9, 440e-9];
|
||||
const n = 1.0003;
|
||||
const N = 2.545e25;
|
||||
const term = (8.0 * Math.pow(F_PI, 3.0) * Math.pow(n * n - 1.0, 2.0)) / (3.0 * N);
|
||||
|
||||
const depthR = [
|
||||
term / Math.pow(lambda[0], 4.0),
|
||||
term / Math.pow(lambda[1], 4.0),
|
||||
term / Math.pow(lambda[2], 4.0)
|
||||
].map(v => v * 8000.0 * this.rayleigh);
|
||||
|
||||
// 2. Mie Coefficients
|
||||
const mieAlpha = 1.3;
|
||||
const mieBase = 2.0e-5 * this.turbidity;
|
||||
const depthM = [
|
||||
mieBase * Math.pow(550e-9 / lambda[0], mieAlpha),
|
||||
mieBase * Math.pow(550e-9 / lambda[1], mieAlpha),
|
||||
mieBase * Math.pow(550e-9 / lambda[2], mieAlpha)
|
||||
].map(v => v * 1200.0 * this.mieCoefficient);
|
||||
|
||||
// Fake Ozone
|
||||
const ozone = [0.0, this.ozone * 0.1, 0.0];
|
||||
|
||||
// Sun Fade (Horizon)
|
||||
const cutoffAngle = 96.0 * (F_PI / 180.0);
|
||||
const steepness = 1.5;
|
||||
const zenithFade = 1.0 - Math.exp(-(cutoffAngle / steepness));
|
||||
const zenithAngle = Math.acos(Math.max(-1.0, Math.min(1.0, this.sunDirection[1])));
|
||||
const sunFade = Math.max(0.0, 1.0 - Math.exp(-((cutoffAngle - zenithAngle) / steepness))) / zenithFade;
|
||||
|
||||
const physicalSunIntensity = this.sunIntensity * sunFade;
|
||||
|
||||
// Radiance Conversion for Sun Halo
|
||||
// Solid Angle = 2 * PI * (1 - cos(angularRadius))
|
||||
const solidAngle = 2.0 * F_PI * (1.0 - this.sunHalo[0]);
|
||||
const radianceConversion = 1.0 / Math.max(1e-9, solidAngle);
|
||||
const sunHaloUpload = [...this.sunHalo];
|
||||
sunHaloUpload[2] *= radianceConversion;
|
||||
|
||||
// Cloud Intersection
|
||||
const r = this.planetRadius;
|
||||
const h = this.cloudControl[2] * 0.001; // m -> km
|
||||
const intersectC = r * r - (r + h) * (r + h);
|
||||
const cloudUniform = [...this.cloudControl];
|
||||
cloudUniform[2] = intersectC;
|
||||
|
||||
// Shimmer Uniform
|
||||
const shimmerUniform = [...this.shimmerControl, r];
|
||||
|
||||
// Multi-Scattering Vector
|
||||
const isotropicPhase = 0.25;
|
||||
const msVector = depthR.map((v, i) => (v * this.msFactors[0] + depthM[i] * this.msFactors[1]) * isotropicPhase);
|
||||
|
||||
// Upload
|
||||
this.materialInstance.setFloat3Parameter('sunDirection', new Float32Array(this.sunDirection));
|
||||
this.materialInstance.setFloat3Parameter('depthR', new Float32Array(depthR));
|
||||
this.materialInstance.setFloat3Parameter('depthM', new Float32Array(depthM));
|
||||
this.materialInstance.setFloat3Parameter('ozone', new Float32Array(ozone));
|
||||
this.materialInstance.setFloat4Parameter('sunHalo', new Float32Array(sunHaloUpload));
|
||||
|
||||
this.materialInstance.setFloat4Parameter('multiScatParams', new Float32Array([...msVector, this.msFactors[2]]));
|
||||
|
||||
// Mie Phase
|
||||
const g2 = this.mieG * this.mieG;
|
||||
this.materialInstance.setFloat2Parameter('miePhaseParams', new Float32Array([1.0 + g2, -2.0 * this.mieG]));
|
||||
|
||||
this.materialInstance.setFloatParameter('contrast', this.contrast);
|
||||
|
||||
const nightColorScaled = this.nightColor.map(v => v * this.sunIntensity);
|
||||
this.materialInstance.setFloat3Parameter('nightColor', new Float32Array(nightColorScaled));
|
||||
|
||||
this.materialInstance.setFloat4Parameter('shimmerControl', new Float32Array(shimmerUniform));
|
||||
this.materialInstance.setFloat4Parameter('cloudControl', new Float32Array(cloudUniform));
|
||||
this.materialInstance.setFloat4Parameter('cloudControl2', new Float32Array(this.cloudControl2));
|
||||
this.materialInstance.setFloat4Parameter('waterControl', new Float32Array(this.waterControl));
|
||||
this.materialInstance.setFloat2Parameter('starControl', new Float32Array(this.starControl));
|
||||
|
||||
this.materialInstance.setFloatParameter('sunIntensity', physicalSunIntensity);
|
||||
}
|
||||
}
|
||||
Binary file not shown.
@@ -1,10 +0,0 @@
|
||||
# Result: /Users/mathias/sources/git/filament/out/cmake-release/tools/matc/matc
|
||||
MATC="../../../../out/cmake-release/tools/matc/matc"
|
||||
|
||||
# Navigate to script directory to ensure relative paths work
|
||||
cd "$(dirname "$0")"
|
||||
|
||||
set -e
|
||||
|
||||
$MATC -a opengl -p mobile -o assets/simulated_skybox.filamat simulated_skybox.mat
|
||||
echo "Material recompiled to assets/simulated_skybox.filamat"
|
||||
File diff suppressed because one or more lines are too long
Binary file not shown.
28
docs/wip/sky/gl-matrix-min.js
vendored
28
docs/wip/sky/gl-matrix-min.js
vendored
File diff suppressed because one or more lines are too long
@@ -1,39 +0,0 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
|
||||
<head>
|
||||
<title>Analytic Skybox</title>
|
||||
<meta charset="utf-8">
|
||||
<meta name="viewport" content="width=device-width,user-scalable=no,initial-scale=1">
|
||||
<link href="styles.css" rel="stylesheet">
|
||||
<style>
|
||||
body {
|
||||
margin: 0;
|
||||
overflow: hidden;
|
||||
background: #000;
|
||||
}
|
||||
|
||||
canvas {
|
||||
touch-action: none;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
outline: none;
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
|
||||
<body>
|
||||
<canvas></canvas>
|
||||
<!-- Filament -->
|
||||
<script src="filament.js"></script>
|
||||
<script src="gl-matrix-min.js"></script>
|
||||
|
||||
<!-- UI -->
|
||||
<script src="lil-gui.js"></script>
|
||||
|
||||
<!-- App -->
|
||||
<script src="SimulatedSkybox.js?v=26"></script>
|
||||
<script src="main.js?v=26"></script>
|
||||
</body>
|
||||
|
||||
</html>
|
||||
File diff suppressed because one or more lines are too long
@@ -1,345 +0,0 @@
|
||||
|
||||
// main.js
|
||||
|
||||
Filament.init(['assets/simulated_skybox.filamat'], () => {
|
||||
window.app = new App(document.getElementsByTagName('canvas')[0]);
|
||||
});
|
||||
|
||||
class App {
|
||||
constructor(canvas) {
|
||||
this.canvas = canvas;
|
||||
const engine = this.engine = Filament.Engine.create(this.canvas);
|
||||
this.scene = engine.createScene();
|
||||
|
||||
this.skybox = new SimulatedSkybox(engine);
|
||||
this.skybox.entity = this.skybox.entity; // Ensuring access if needed
|
||||
this.scene.addEntity(this.skybox.entity);
|
||||
|
||||
// Load the material explicitly since we passed it to init but SimulatedSkybox needs to bind it
|
||||
// Actually SimulatedSkybox.loadMaterial fetches it.
|
||||
// Since we already loaded it in Filament.init, we can arguably just use it if we had a way to access the asset.
|
||||
// But Filament.init assets are for internal or easy access via assets object if configured?
|
||||
// Let's just let SimulatedSkybox fetch it again or use a blob if we wanted.
|
||||
// Simpler: Just let SimulatedSkybox fetch it.
|
||||
this.skybox.loadMaterial('assets/simulated_skybox.filamat').then(() => {
|
||||
this.initGUI();
|
||||
});
|
||||
|
||||
this.swapChain = engine.createSwapChain();
|
||||
this.renderer = engine.createRenderer();
|
||||
this.camera = engine.createCamera(Filament.EntityManager.get().create());
|
||||
|
||||
this.view = engine.createView();
|
||||
this.view.setCamera(this.camera);
|
||||
this.view.setScene(this.scene);
|
||||
// Color Grading
|
||||
const ColorGrading = Filament.ColorGrading;
|
||||
const ToneMapping = Filament.ColorGrading$ToneMapping;
|
||||
this.colorGrading = ColorGrading.Builder()
|
||||
.toneMapping(ToneMapping.ACES_LEGACY)
|
||||
.build(engine);
|
||||
this.view.setColorGrading(this.colorGrading);
|
||||
this.view.setPostProcessingEnabled(true); // Essential for tone mapping
|
||||
|
||||
// Bloom
|
||||
this.view.setBloomOptions({
|
||||
enabled: false,
|
||||
lenseFlare: false
|
||||
});
|
||||
|
||||
// Clear color is not really visible behind skybox, but black is standard
|
||||
this.renderer.setClearOptions({ clearColor: [0.0, 0.0, 0.0, 1.0], clear: true });
|
||||
|
||||
// Camera handling (Exposure)
|
||||
this.params = {
|
||||
aperture: 16.0,
|
||||
shutterSpeed: 125.0,
|
||||
iso: 100.0,
|
||||
sunTheta: Math.acos(0.0), // Default Height 0.0 (Horizon)
|
||||
sunPhi: 0.0,
|
||||
focalLength: 24.0, // mm
|
||||
sunIntensity: 100000.0 // Base intensity
|
||||
};
|
||||
|
||||
this.camState = {
|
||||
theta: Math.PI / 2, // Look at +X (Sun Position at Phi=0)
|
||||
phi: 0.0,
|
||||
dragging: false,
|
||||
lastX: 0,
|
||||
lastY: 0
|
||||
};
|
||||
|
||||
this.initControls(); // Initialize controls immediately
|
||||
|
||||
this.resize();
|
||||
window.addEventListener('resize', this.resize.bind(this));
|
||||
|
||||
this.render = this.render.bind(this);
|
||||
window.requestAnimationFrame(this.render);
|
||||
}
|
||||
|
||||
getExposure() {
|
||||
// Formula: 1.0 / ( 1.2 * (N^2 / t) * (S / 100) )
|
||||
// t = 1/shutterSpeed
|
||||
const N = this.params.aperture;
|
||||
const t = 1.0 / this.params.shutterSpeed;
|
||||
const S = this.params.iso;
|
||||
const ev100_linear = (N * N) / t * (100.0 / S);
|
||||
const exposure = 1.0 / (1.2 * ev100_linear);
|
||||
return exposure;
|
||||
}
|
||||
|
||||
updateCameraExposure() {
|
||||
this.camera.setExposure(this.params.aperture, 1.0 / this.params.shutterSpeed, this.params.iso);
|
||||
// Also update Sun Intensity because it needs to be pre-exposed
|
||||
this.updateSunIntensity();
|
||||
}
|
||||
|
||||
updateSunIntensity() {
|
||||
const exposure = this.getExposure();
|
||||
const preExposedIntensity = this.params.sunIntensity * exposure;
|
||||
this.skybox.setSunIntensity(preExposedIntensity);
|
||||
}
|
||||
|
||||
updateCameraProjection() {
|
||||
const width = this.canvas.width;
|
||||
const height = this.canvas.height;
|
||||
const aspect = width / height;
|
||||
this.camera.setLensProjection(this.params.focalLength, aspect, 0.1, 5000.0);
|
||||
}
|
||||
|
||||
initGUI() {
|
||||
const gui = new lil.GUI({ title: "Analytic Skybox" });
|
||||
const self = this;
|
||||
const sky = this.skybox;
|
||||
|
||||
// Initialize local params from skybox defaults
|
||||
// Initialize local params from skybox defaults
|
||||
// REMOVED: Do not overwrite this.params from sky.sunDirection (Zenith)
|
||||
// const currentDir = sky.sunDirection;
|
||||
// this.params.sunTheta = ...
|
||||
|
||||
const updateSun = () => {
|
||||
const theta = this.params.sunTheta;
|
||||
const phi = this.params.sunPhi;
|
||||
const x = Math.sin(theta) * Math.cos(phi);
|
||||
const y = Math.cos(theta);
|
||||
const z = Math.sin(theta) * Math.sin(phi);
|
||||
sky.setSunPosition([x, y, z]);
|
||||
};
|
||||
|
||||
const sunFolder = gui.addFolder('Sun');
|
||||
// Helper for "Sun Height" cosine slider like C++
|
||||
const sunHeightParam = { height: Math.cos(this.params.sunTheta) };
|
||||
sunFolder.add(sunHeightParam, 'height', -0.2, 1.0).name('Height (Cos)').onChange(v => {
|
||||
this.params.sunTheta = Math.acos(v);
|
||||
updateSun();
|
||||
});
|
||||
sunFolder.add(this.params, 'sunPhi', 0.0, Math.PI * 2).name('Azimuth').onChange(updateSun);
|
||||
// Updated: Controls params.sunIntensity and triggers updateSunIntensity
|
||||
sunFolder.add(this.params, 'sunIntensity', 0.0, 500000.0).onChange(v => this.updateSunIntensity());
|
||||
|
||||
const sunDisk = sunFolder.addFolder('Disk');
|
||||
// We need local proxy for sunRadius due to conversion
|
||||
const diskParams = {
|
||||
radius: 1.2,
|
||||
enabled: true // Enable sun disk
|
||||
};
|
||||
sky.setSunDiskEnabled(true);
|
||||
sky.setSunRadius(1.2);
|
||||
sunDisk.add(diskParams, 'enabled').onChange(v => sky.setSunDiskEnabled(v));
|
||||
sunDisk.add(diskParams, 'radius', 0.0, 5.0).onChange(v => sky.setSunRadius(v));
|
||||
sunDisk.add(sky.sunHalo, 1, 0.0, 2.0).name('Limb Darkening').onChange(v => sky.setSunLimbDarkening(v));
|
||||
sunDisk.add(sky.sunHalo, 2, 0.0, 100.0).name('Intensity Boost').onChange(v => sky.setSunDiskIntensity(v));
|
||||
|
||||
const atmFolder = gui.addFolder('Atmosphere');
|
||||
atmFolder.add(sky, 'turbidity', 1.0, 10.0).onChange(v => sky.setTurbidity(v));
|
||||
atmFolder.add(sky, 'rayleigh', 0.0, 10.0).onChange(v => sky.setRayleigh(v));
|
||||
atmFolder.add(sky, 'mieCoefficient', 0.0, 10.0).onChange(v => sky.setMieCoefficient(v));
|
||||
// Set Ozone default to 0.25
|
||||
sky.setOzone(0.25);
|
||||
atmFolder.add(sky, 'ozone', 0.0, 1.0).onChange(v => sky.setOzone(v));
|
||||
atmFolder.add(sky, 'mieG', 0.0, 0.999).onChange(v => sky.setMieG(v));
|
||||
|
||||
const artFolder = gui.addFolder('Artistic');
|
||||
// Set Horizon Glow default to 1.0
|
||||
sky.setHorizonGlow(1.0);
|
||||
sky.msFactors[2] = 1.0;
|
||||
// Set Contrast default to 0.85
|
||||
sky.setContrast(0.85);
|
||||
|
||||
artFolder.add(sky.msFactors, 0, 0.0, 2.0).name('MS Rayleigh').onChange(v => sky.setMultiScattering(v, sky.msFactors[1]));
|
||||
artFolder.add(sky.msFactors, 1, 0.0, 2.0).name('MS Mie').onChange(v => sky.setMultiScattering(sky.msFactors[0], v));
|
||||
artFolder.add(sky.msFactors, 2, 0.0, 1.0).name('Horizon Glow').onChange(v => sky.setHorizonGlow(v));
|
||||
artFolder.add(sky, 'contrast', 0.1, 2.0).onChange(v => sky.setContrast(v));
|
||||
|
||||
artFolder.addColor(sky, 'nightColor').onChange(v => sky.setNightColor(v));
|
||||
|
||||
const shmFolder = artFolder.addFolder('Shimmer');
|
||||
// Set Shimmer Strength default to 0.0
|
||||
sky.setShimmerControl(0.0, sky.shimmerControl[1], sky.shimmerControl[2]);
|
||||
|
||||
shmFolder.add(sky.shimmerControl, 0, 0.0, 0.1).name('Strength').onChange(v => sky.setShimmerControl(v, sky.shimmerControl[1], sky.shimmerControl[2]));
|
||||
shmFolder.add(sky.shimmerControl, 1, 1.0, 100.0).name('Frequency').onChange(v => sky.setShimmerControl(sky.shimmerControl[0], v, sky.shimmerControl[2]));
|
||||
shmFolder.add(sky.shimmerControl, 2, 0.01, 0.5).name('Mask Height').onChange(v => sky.setShimmerControl(sky.shimmerControl[0], sky.shimmerControl[1], v));
|
||||
|
||||
const cloudFolder = gui.addFolder('Clouds');
|
||||
const cParams = {
|
||||
volumetrics: sky.cloudControl2[1] > 0.5,
|
||||
coverage: 0.4,
|
||||
density: 0.02,
|
||||
height: sky.cloudControl[2],
|
||||
speed: 50.0,
|
||||
evolution: 0.02
|
||||
};
|
||||
// Apply Cloud Defaults
|
||||
sky.setCloudControl(0.4, 0.02, cParams.height, 50.0);
|
||||
sky.setCloudShapeEvolution(0.02);
|
||||
|
||||
cloudFolder.add(cParams, 'volumetrics').onChange(v => sky.setCloudVolumetricLighting(v));
|
||||
cloudFolder.add(cParams, 'coverage', 0.0, 1.0).onChange(v => sky.setCloudControl(v, cParams.density, cParams.height, cParams.speed));
|
||||
cloudFolder.add(cParams, 'density', 0.0, 1.0).onChange(v => sky.setCloudControl(cParams.coverage, v, cParams.height, cParams.speed));
|
||||
cloudFolder.add(cParams, 'height', 2000.0, 20000.0).onChange(v => sky.setCloudControl(cParams.coverage, cParams.density, v, cParams.speed));
|
||||
// Reverse speed calc: w = speed * (0.05 / 72.0)
|
||||
cloudFolder.add(cParams, 'speed', 0.0, 200.0).onChange(v => sky.setCloudControl(cParams.coverage, cParams.density, cParams.height, v));
|
||||
cloudFolder.add(cParams, 'evolution', 0.0, 2.0).onChange(v => sky.setCloudShapeEvolution(v));
|
||||
|
||||
const waterFolder = gui.addFolder('Water');
|
||||
const wParams = {
|
||||
derivativeTrick: true,
|
||||
strength: 50.0,
|
||||
speed: 1.0,
|
||||
octaves: 4.0
|
||||
};
|
||||
// Initialize defaults
|
||||
sky.setWaterControl(50.0, 1.0, 1.0, 4.0); // 1.0 = Derivative Trick On, 4 octaves
|
||||
|
||||
const updateWater = () => {
|
||||
sky.setWaterControl(wParams.strength, wParams.speed, wParams.derivativeTrick ? 1.0 : 0.0, wParams.octaves);
|
||||
};
|
||||
|
||||
waterFolder.add(wParams, 'derivativeTrick').name('Derivative Trick').onChange(updateWater);
|
||||
waterFolder.add(wParams, 'strength', 10.0, 100.0).onChange(updateWater);
|
||||
waterFolder.add(wParams, 'speed', 0.0, 5.0).onChange(updateWater);
|
||||
waterFolder.add(wParams, 'octaves', 1, 8, 1).name('Octaves').onChange(updateWater);
|
||||
waterFolder.close();
|
||||
|
||||
const starFolder = gui.addFolder('Stars');
|
||||
const sParams = {
|
||||
enabled: true,
|
||||
density: 1.0
|
||||
};
|
||||
// Initialize defaults (Density 1.0, Enabled True)
|
||||
sky.setStarControl(1.0, true);
|
||||
|
||||
const updateStars = () => {
|
||||
sky.setStarControl(sParams.density, sParams.enabled);
|
||||
};
|
||||
|
||||
starFolder.add(sParams, 'enabled').name('Enabled').onChange(updateStars);
|
||||
starFolder.add(sParams, 'density', 0.0, 1.0).name('Density').onChange(updateStars);
|
||||
starFolder.close();
|
||||
|
||||
const camFolder = gui.addFolder('Camera');
|
||||
camFolder.add(this.params, 'focalLength', 8.0, 300.0).name('Focal Length').onChange(() => this.updateCameraProjection());
|
||||
camFolder.add(this.params, 'aperture', 1.4, 32.0).onChange(() => this.updateCameraExposure());
|
||||
camFolder.add(this.params, 'shutterSpeed', 1.0, 1000.0).onChange(() => this.updateCameraExposure());
|
||||
camFolder.add(this.params, 'iso', 50.0, 3200.0).onChange(() => this.updateCameraExposure());
|
||||
|
||||
const bloomFolder = camFolder.addFolder('Bloom');
|
||||
const bParams = {
|
||||
enabled: false,
|
||||
lensFlare: false
|
||||
};
|
||||
|
||||
const updateBloom = () => {
|
||||
this.view.setBloomOptions({
|
||||
enabled: bParams.enabled,
|
||||
lensFlare: bParams.lensFlare
|
||||
});
|
||||
};
|
||||
|
||||
bloomFolder.add(bParams, 'enabled').onChange(updateBloom);
|
||||
bloomFolder.add(bParams, 'lensFlare').onChange(updateBloom);
|
||||
bloomFolder.close();
|
||||
|
||||
// Collapse folders by default
|
||||
sunDisk.close();
|
||||
atmFolder.close();
|
||||
artFolder.close();
|
||||
// shmFolder is inside artFolder, so it's hidden, but we can close it too if we want
|
||||
shmFolder.close();
|
||||
cloudFolder.close();
|
||||
// camFolder left open? User didn't specify, but "Artistic, shimmer and clouds" + "Disk, Atmosphere" were requested.
|
||||
// So Camera might stay open or close. Let's keep Camera open for now as it wasn't listed.
|
||||
|
||||
// Initial sync
|
||||
updateSun();
|
||||
this.updateCameraExposure(); // This will trigger updateSunIntensity too
|
||||
}
|
||||
|
||||
initControls() {
|
||||
|
||||
// listeners only
|
||||
this.canvas.addEventListener('mousedown', e => {
|
||||
this.camState.dragging = true;
|
||||
this.camState.lastX = e.clientX;
|
||||
this.camState.lastY = e.clientY;
|
||||
});
|
||||
|
||||
window.addEventListener('mouseup', () => {
|
||||
this.camState.dragging = false;
|
||||
});
|
||||
|
||||
window.addEventListener('mousemove', e => {
|
||||
if (!this.camState.dragging) return;
|
||||
const dx = e.clientX - this.camState.lastX;
|
||||
const dy = e.clientY - this.camState.lastY;
|
||||
this.camState.lastX = e.clientX;
|
||||
this.camState.lastY = e.clientY;
|
||||
|
||||
const sensitivity = 0.005;
|
||||
this.camState.theta -= dx * sensitivity;
|
||||
this.camState.phi += dy * sensitivity;
|
||||
// Clamp pitch to avoid flip [ -PI/2, PI/2 ]
|
||||
this.camState.phi = Math.max(-Math.PI / 2 + 0.01, Math.min(Math.PI / 2 - 0.01, this.camState.phi));
|
||||
});
|
||||
|
||||
|
||||
}
|
||||
|
||||
render() {
|
||||
// Update Camera LookAt
|
||||
const r = 1.0;
|
||||
const theta = this.camState.theta;
|
||||
const phi = this.camState.phi;
|
||||
|
||||
// Convert spherical to cartesian
|
||||
// Y is UP. Z is Forward.
|
||||
// At phi=0, y=0. forward vector should correspond to theta.
|
||||
// Let's say theta=0 is -Z.
|
||||
const y = Math.sin(phi);
|
||||
const h = Math.cos(phi);
|
||||
const x = h * Math.sin(theta);
|
||||
const z = -h * Math.cos(theta);
|
||||
|
||||
const eye = [0, 0, 0];
|
||||
const center = [x, y, z];
|
||||
const up = [0, 1, 0];
|
||||
this.camera.lookAt(eye, center, up);
|
||||
|
||||
this.renderer.render(this.swapChain, this.view);
|
||||
window.requestAnimationFrame(this.render);
|
||||
}
|
||||
|
||||
resize() {
|
||||
const dpr = window.devicePixelRatio;
|
||||
const width = this.canvas.width = window.innerWidth * dpr;
|
||||
const height = this.canvas.height = window.innerHeight * dpr;
|
||||
this.view.setViewport([0, 0, width, height]);
|
||||
|
||||
const aspect = width / height;
|
||||
// near=0.1, far=5000.0
|
||||
this.camera.setLensProjection(this.params.focalLength, aspect, 0.1, 5000.0);
|
||||
}
|
||||
}
|
||||
@@ -1,909 +0,0 @@
|
||||
material {
|
||||
name : SimulatedSkybox,
|
||||
parameters : [
|
||||
{
|
||||
type : float3,
|
||||
name : sunDirection
|
||||
},
|
||||
{
|
||||
type : float3,
|
||||
name : sunDirection2
|
||||
},
|
||||
{
|
||||
type : float3,
|
||||
name : depthR,
|
||||
precision : high
|
||||
},
|
||||
{
|
||||
type : float3,
|
||||
name : depthM,
|
||||
precision : high
|
||||
},
|
||||
{
|
||||
type : float2,
|
||||
name : miePhaseParams, // x=(1+g^2), y=(-2*g)
|
||||
precision : high
|
||||
},
|
||||
{
|
||||
type : float,
|
||||
name : sunIntensity,
|
||||
precision : high
|
||||
},
|
||||
{
|
||||
type : float,
|
||||
name : contrast,
|
||||
precision : high
|
||||
},
|
||||
{
|
||||
type : float3,
|
||||
name : nightColor,
|
||||
precision : high
|
||||
},
|
||||
{
|
||||
type : float3,
|
||||
name : ozone,
|
||||
precision : high
|
||||
},
|
||||
{
|
||||
type : float4,
|
||||
name : multiScatParams, // xyz=MultiScatteringColor, w=HorizonGlow
|
||||
precision : high
|
||||
},
|
||||
{
|
||||
type : float4,
|
||||
name : sunHalo, // x=Size, y=Limb, z=Intensity, w=Enabled
|
||||
precision : high
|
||||
},
|
||||
{
|
||||
type : float4,
|
||||
name : shimmerControl, // x=Strength, y=Frequency, z=MaskHeight, w=PlanetRadius
|
||||
precision : high
|
||||
},
|
||||
{
|
||||
type : float4,
|
||||
name : cloudControl, // x=Coverage, y=Density, z=QuadraticConst, w=WindSpeed
|
||||
precision : high
|
||||
},
|
||||
{
|
||||
type : float4,
|
||||
name : cloudControl2, // x=EvolutionSpeed
|
||||
precision : high
|
||||
},
|
||||
{
|
||||
type : float,
|
||||
name : sunIntensity2,
|
||||
precision : high
|
||||
},
|
||||
{
|
||||
type : float4,
|
||||
name : sunHalo2, // x=Size, y=Limb, z=Intensity, w=Enabled
|
||||
precision : high
|
||||
},
|
||||
{
|
||||
type : float4,
|
||||
name : waterControl, // x=Strength, y=Speed, z=DerivativeTrick, w=Octaves
|
||||
precision : high
|
||||
},
|
||||
{
|
||||
type : float2,
|
||||
name : starControl, // x=Density, y=Enabled
|
||||
precision : high
|
||||
}
|
||||
],
|
||||
variables : [
|
||||
eyeDirection
|
||||
],
|
||||
vertexDomain : device,
|
||||
depthWrite : false,
|
||||
shadingModel : unlit,
|
||||
culling: none
|
||||
}
|
||||
|
||||
vertex {
|
||||
void materialVertex(inout MaterialVertexInputs material) {
|
||||
// This code is taken from computeWorldPosition and assumes the vertex domain is 'device'.
|
||||
highp vec4 p = getPosition();
|
||||
// GL convention to inverted DX convention
|
||||
p.z = p.z * -0.5 + 0.5;
|
||||
highp vec4 worldPosition = getWorldFromClipMatrix() * p;
|
||||
// Getting the true world position would require dividing by w, but since this is a skybox
|
||||
// at inifinity, this results in very large numbers for material.eyeDirection.
|
||||
// Since the eyeDirection is only used as a direction vector in the fragment shader, we can
|
||||
// skip that step to improve precision.
|
||||
material.eyeDirection.xyz = worldPosition.xyz;
|
||||
}
|
||||
}
|
||||
|
||||
fragment {
|
||||
// ------------------------------------------------------------------------
|
||||
// Analytic Rayleigh and Mie Scattering (Physics Based)
|
||||
// Derived from:
|
||||
// - Hoffman & Preetham (2002): "Real-time Light-Atmosphere Interactions"
|
||||
// - Henyey & Greenstein (1941): "Diffuse radiation in the galaxy" (Mie Phase)
|
||||
// - Kasten & Young (1989): "Revised optical air mass tables" (Air Mass)
|
||||
// - "Simulated Sky" / Three.js (Sky.js): Empirical adjustments for aesthetics
|
||||
// ------------------------------------------------------------------------
|
||||
|
||||
#define PI 3.14159265359
|
||||
|
||||
void dummy() {} // squash editor syntax highlighting bugs
|
||||
|
||||
// Rayleigh Phase Function: Scattering distribution for small particles (air molecules)
|
||||
// Lord Rayleigh (1871)
|
||||
// Normalized to integrate to 4*PI (Boosting brightness by factor PI vs standard 1-normalization)
|
||||
highp float rayleighPhase(highp float cosTheta) {
|
||||
const highp float THREE_SIXTEENTH = (3.0 / 16.0);
|
||||
return THREE_SIXTEENTH * (1.0 + cosTheta * cosTheta);
|
||||
}
|
||||
|
||||
// Henyey-Greenstein Phase Function (Mie)
|
||||
// Henyey & Greenstein (1941)
|
||||
// Controls the forward scattering peak (sun halo) via anisotropy parameter 'g'
|
||||
// Optimized: params.x = (1 + g^2), params.y = (-2 * g)
|
||||
highp float hgPhase(highp float cosTheta, highp vec2 params) {
|
||||
const highp float ONE_FOURTH = (1.0 / 4.0);
|
||||
// Recover (1 - g^2) => 2.0 - (1 + g^2)
|
||||
highp float oneMinusG2 = 2.0 - params.x;
|
||||
highp float inverse = 1.0 / pow(params.x + params.y * cosTheta, 1.5);
|
||||
return ONE_FOURTH * (oneMinusG2 * inverse);
|
||||
}
|
||||
|
||||
// --- Noise Functions for Clouds ---
|
||||
highp float hash13(highp vec3 p3) {
|
||||
p3 = fract(p3 * .1031);
|
||||
p3 += dot(p3, p3.yzx + 33.33);
|
||||
return fract((p3.x + p3.y) * p3.z);
|
||||
}
|
||||
|
||||
highp float noise(highp vec3 p) {
|
||||
highp vec3 i = floor(p);
|
||||
highp vec3 f = fract(p);
|
||||
// Cubic Hermite Interpolation
|
||||
highp vec3 u = f*f*(3.0-2.0*f);
|
||||
return mix(mix(mix(hash13(i + vec3(0,0,0)), hash13(i + vec3(1,0,0)), u.x),
|
||||
mix(hash13(i + vec3(0,1,0)), hash13(i + vec3(1,1,0)), u.x), u.y),
|
||||
mix(mix(hash13(i + vec3(0,0,1)), hash13(i + vec3(1,0,1)), u.x),
|
||||
mix(hash13(i + vec3(0,1,1)), hash13(i + vec3(1,1,1)), u.x), u.y), u.z);
|
||||
}
|
||||
|
||||
// Fractal Brownian Motion (4 Octaves)
|
||||
highp float fbm(highp vec3 p) {
|
||||
highp float total = 0.0;
|
||||
highp float amplitude = 0.5;
|
||||
for (int i = 0; i < 4; i++) {
|
||||
total += noise(p) * amplitude;
|
||||
p *= 2.02; // Lacunarity
|
||||
p += 100.0; // Shift to avoid artifacts
|
||||
amplitude *= 0.5; // Gain
|
||||
}
|
||||
return total;
|
||||
}
|
||||
|
||||
highp float fbm(highp vec3 p, int octaves) {
|
||||
highp float total = 0.0;
|
||||
highp float amplitude = 0.5;
|
||||
for (int i = 0; i < 8; i++) {
|
||||
if (i >= octaves) break;
|
||||
total += noise(p) * amplitude;
|
||||
p *= 2.02;
|
||||
p += 100.0;
|
||||
amplitude *= 0.5;
|
||||
}
|
||||
return total;
|
||||
}
|
||||
|
||||
// Ray-Sphere Intersection
|
||||
// Returns distance to intersection or -1.0 if none.
|
||||
// Re = Planet Radius.
|
||||
// C = Re^2 - (Re + height)^2 (Precalculated on CPU for precision).
|
||||
highp float raySphereIntersect(highp vec3 rd, highp float Re, highp float C) {
|
||||
// Ray Origin is (0, Re, 0) relative to Planet Center (0, 0, 0)
|
||||
// We solve |(0, Re, 0) + t*rd|^2 = Rm^2
|
||||
// |O + tD|^2 = R^2
|
||||
// t^2 + 2t(O.D) + (O^2 - R^2) = 0
|
||||
// a=1, b=2(O.D), c = O^2 - R^2 = C
|
||||
// Reduced quadratic: t = -b' +/- sqrt(b'^2 - c) where b' = O.D
|
||||
|
||||
highp float b = Re * rd.y; // dot(vec3(0, Re, 0), rd)
|
||||
highp float disc = b*b - C;
|
||||
|
||||
if (disc < 0.0) return -1.0;
|
||||
|
||||
// t = -b + sqrt(disc)
|
||||
return -b + sqrt(disc);
|
||||
}
|
||||
|
||||
// ------------------------------------------------------------------------
|
||||
// Atmospheric Heat Shimmer (Mirage)
|
||||
// ------------------------------------------------------------------------
|
||||
// Simulates heat convection turbulence near the horizon (e.g., hot desert road effect).
|
||||
//
|
||||
// PHYSICS:
|
||||
// Heat rising from the ground creates pockets of varying air density (refractive index).
|
||||
// This bends light rays, causing a visual "shimmer" or displacement.
|
||||
//
|
||||
// IMPLEMENTATION:
|
||||
// - Perturbs the view vector `V.y` using interleaved sine waves.
|
||||
// - Uses World Space `V` so the noise is stable under camera rotation.
|
||||
// - Masked to only affect the horizon line.
|
||||
//
|
||||
// PARAMETERS:
|
||||
// @param V Normalized World View Vector (modified in place).
|
||||
// @param strength Max vertical displacement amplitude. (e.g. 0.002).
|
||||
// @param freq Ripple frequency/density. (e.g. 20.0).
|
||||
// @param maskHeight Horizon mask height (0.0 to 1.0). (e.g. 0.1).
|
||||
// ------------------------------------------------------------------------
|
||||
float applyHeatShimmer(inout highp vec3 V, highp float strength, highp float freq, highp float maskHeight) {
|
||||
if (strength <= 0.0) return 0.0;
|
||||
|
||||
// Mask: Strongest at horizon (0.0), fades out by maskHeight.
|
||||
highp float mask = 1.0 - smoothstep(0.0, maskHeight, abs(V.y));
|
||||
|
||||
if (mask > 0.0) {
|
||||
// Use FBM for organic turbulence (rising heat waves)
|
||||
highp float time = getUserTime().x;
|
||||
// Animate upward (y) and slightly drift (x)
|
||||
highp vec3 p = vec3(V.x * freq, V.y * freq + time * 2.0, time * 0.5);
|
||||
|
||||
// We use a cheap noise or FBM. Since we have FBM:
|
||||
// Use fewer octaves for performance if possible, but 4 is fine.
|
||||
highp float distortion = fbm(p);
|
||||
|
||||
// Remap noise from [0, 1] to [-1, 1] for perturbation
|
||||
distortion = distortion * 2.0 - 1.0;
|
||||
|
||||
// Apply vertical perturbation
|
||||
V.y += distortion * strength * mask * 0.1;
|
||||
V = normalize(V);
|
||||
}
|
||||
return mask;
|
||||
}
|
||||
|
||||
// ------------------------------------------------------------------------
|
||||
// Analytic Sky Model (Rayleigh + Mie + Ozone)
|
||||
// ------------------------------------------------------------------------
|
||||
// Computes the scattering and transmittance of the atmosphere along the view ray.
|
||||
//
|
||||
// PHYSICS:
|
||||
// - Rayleigh: Scattering by air molecules (Blue sky). High frequency (lambda^-4).
|
||||
// - Mie: Scattering by aerosols/dust (White haze). Low frequency (lambda^-1.3).
|
||||
// - Ozone: Absorption layer (Pink sunset). Absorbs green light.
|
||||
// - Optical Mass: Approximation of path length through spherical atmosphere.
|
||||
//
|
||||
// OUTPUTS:
|
||||
// @param V Normalized View Vector.
|
||||
// @param L Normalized Sun Vector.
|
||||
// @param sunIntensity Sun Illuminance (Lux).
|
||||
// @param depthR Rayleigh Optical Depth (Precalculated).
|
||||
// @param depthM Mie Optical Depth (Precalculated).
|
||||
// @param ozone Ozone Absorption (Precalculated).
|
||||
// @param msFactors Multi-Scattering factors (Rayleigh, Mie, Glow).
|
||||
// @param mieG Mie Phase Anisotropy.
|
||||
// @param outTransmittance Output: Atmospheric Transmittance (0..1) along V.
|
||||
// @return Output: In-Scattered Radiance (The sky color).
|
||||
// ------------------------------------------------------------------------
|
||||
highp vec3 getAtmosphere(highp vec3 V, highp vec3 L, highp float sunIntensity,
|
||||
highp vec3 depthR, highp vec3 depthM, highp vec3 ozone,
|
||||
highp vec4 multiScatParams, highp vec2 mieParams,
|
||||
out highp vec3 outTransmittance) {
|
||||
|
||||
highp float cosTheta = dot(V, L);
|
||||
|
||||
// 1. Phase Functions
|
||||
// "Golden Hour" Hack (Three.js Sky.js):
|
||||
// Remapping cosTheta from [-1, 1] to [0, 1] breaks the symmetry of Rayleigh scattering.
|
||||
highp float rPhase = rayleighPhase(cosTheta * 0.5 + 0.5);
|
||||
highp float mPhase = hgPhase(cosTheta, mieParams);
|
||||
|
||||
// 2. Optical Depth (Air Mass)
|
||||
// Kasten and Young (1989) - Relative Air Mass Model
|
||||
highp float zenithCos = clamp(V.y, 0.0, 1.0);
|
||||
highp float zenithAngle = acos(zenithCos);
|
||||
highp float zenithAngleDeg = zenithAngle * (180.0 / PI);
|
||||
highp float opticalMass = 1.0 / (zenithCos + 0.15 * pow(93.885 - zenithAngleDeg, -1.253));
|
||||
|
||||
// 3. Extinction & Transmittance
|
||||
highp vec3 totalExtinction = depthR + depthM + ozone;
|
||||
highp vec3 extinction = totalExtinction * opticalMass;
|
||||
outTransmittance = exp(-extinction);
|
||||
|
||||
// 4. In-Scattering
|
||||
// Approximate Multi-Scattering (Isotropic Fill) precomputed in C++.
|
||||
highp vec3 multiScattering = multiScatParams.xyz;
|
||||
|
||||
highp vec3 scatteringTerm = (depthR * rPhase) + (depthM * mPhase) + multiScattering;
|
||||
highp vec3 extinctionTerm = max(vec3(1e-6), totalExtinction);
|
||||
|
||||
// Equilibrium Radiance (Source Function)
|
||||
highp vec3 inScattering = sunIntensity * (scatteringTerm / extinctionTerm);
|
||||
|
||||
// Single-Scattering Integral: L = L_inf * (1 - exp(-opticalDepth))
|
||||
highp vec3 sunLight = inScattering * (1.0 - outTransmittance);
|
||||
|
||||
// 5. Horizon "Glow" Mix (Artistic Hack)
|
||||
// multiScatParams.w contains the Horizon Glow Strength
|
||||
// Uses Sun Elevation (L.y) to only activate during golden hour/twilight.
|
||||
mediump float horizonMix = saturate(pow(1.0 - L.y, 5.0)) * multiScatParams.w;
|
||||
highp vec3 horizonGlow = sqrt(inScattering * outTransmittance);
|
||||
sunLight *= mix(vec3(1.0), horizonGlow, horizonMix);
|
||||
|
||||
return sunLight;
|
||||
}
|
||||
|
||||
// ------------------------------------------------------------------------
|
||||
// Secondary Sun Scattering (Simplified)
|
||||
// ------------------------------------------------------------------------
|
||||
// Computes in-scattering for a second light source, reusing precomputed optical depths.
|
||||
// Skips multi-scattering (ambient) for performance, providing only direct beams/glow.
|
||||
//
|
||||
// @param V Normalized View Vector.
|
||||
// @param L Normalized Sun Vector.
|
||||
// @param sunIntensity Sun Illuminance.
|
||||
// @param depthR Rayleigh Optical Depth.
|
||||
// @param depthM Mie Optical Depth.
|
||||
// @param ozone Ozone Absorption.
|
||||
// @param mieParams Mie Phase Params.
|
||||
// @param transmittance Precomputed Atmospheric Transmittance.
|
||||
// @return In-Scattered Radiance.
|
||||
// ------------------------------------------------------------------------
|
||||
highp vec3 getSecondarySunScattering(highp vec3 V, highp vec3 L, highp float sunIntensity,
|
||||
highp vec3 depthR, highp vec3 depthM, highp vec3 ozone,
|
||||
highp vec2 miePhaseParams, highp vec3 transmittance) {
|
||||
highp float cosTheta = dot(V, L);
|
||||
|
||||
// Phase Functions
|
||||
highp float rPhase = rayleighPhase(cosTheta * 0.5 + 0.5);
|
||||
highp float mPhase = hgPhase(cosTheta, miePhaseParams);
|
||||
|
||||
// Scattering
|
||||
highp vec3 scatteringTerm = (depthR * rPhase) + (depthM * mPhase);
|
||||
highp vec3 totalExtinction = depthR + depthM + ozone;
|
||||
highp vec3 extinctionTerm = max(vec3(1e-6), totalExtinction);
|
||||
|
||||
highp vec3 inScattering = sunIntensity * (scatteringTerm / extinctionTerm);
|
||||
return inScattering * (1.0 - transmittance);
|
||||
}
|
||||
|
||||
// ------------------------------------------------------------------------
|
||||
// Physical Sun Disk
|
||||
// ------------------------------------------------------------------------
|
||||
// Renders the Solar Photosphere with limb darkening.
|
||||
//
|
||||
// PHYSICS:
|
||||
// - The sun is not a point light; it has an angular size (~0.53 deg).
|
||||
// - Limb Darkening: The sun is darker at the edges (limbs) because we see cooler outer layers.
|
||||
// - Drawn "Behind" the atmosphere, so it is attenuated by Transmittance.
|
||||
//
|
||||
// PARAMETERS:
|
||||
// @param V Normalized View Vector.
|
||||
// @param L Normalized Sun Vector.
|
||||
// @param sunParams x=CosRadius, y=LimbDarkening, z=IntensityBoost, w=Enabled.
|
||||
// @param sunIntensity Peak Sun Illuminance (Lux).
|
||||
// @param transmittance Atmospheric Transmittance (0..1).
|
||||
// @return Radiance of the sun disk (if visible and enabled).
|
||||
// ------------------------------------------------------------------------
|
||||
highp vec3 getSunDisk(highp vec3 V, highp vec3 L, highp vec4 sunParams,
|
||||
highp float sunIntensity, highp vec3 transmittance) {
|
||||
|
||||
highp float sunCosRadius = sunParams.x;
|
||||
highp float limbDarkening = sunParams.y;
|
||||
highp float sunDiskIntensity = sunParams.z;
|
||||
bool sunEnabled = sunParams.w > 0.5;
|
||||
|
||||
highp float cosTheta = dot(V, L);
|
||||
|
||||
// Robust edge detection for small angles using (1 - cos)
|
||||
highp float dist = 1.0 - cosTheta;
|
||||
highp float diskRadius = max(1e-6, 1.0 - sunCosRadius);
|
||||
|
||||
// AA Edge: smoothstep from radius to radius+epsilon
|
||||
// We invert it because we want 1.0 inside (dist < radius)
|
||||
highp float sunDiskProfile = 1.0 - smoothstep(diskRadius, diskRadius + 0.00002, dist);
|
||||
|
||||
if (sunEnabled && sunDiskProfile > 0.0) {
|
||||
// Limb Darkening approximation: mu = sqrt(1 - (r/R)^2)
|
||||
// dist/diskRadius is approx (r/R)^2 for small angles
|
||||
highp float relativeDist = min(1.0, dist / diskRadius);
|
||||
highp float mu = sqrt(1.0 - relativeDist);
|
||||
|
||||
// Avoid pow(0, 0) which causes NaNs
|
||||
highp float limbFactor = (limbDarkening < 1e-4) ? 1.0 : pow(mu, limbDarkening);
|
||||
|
||||
// Direct Sun Light (Radiance)
|
||||
// SunIntensity * Transmittance -> Physical Sun Color
|
||||
// SunDiskIntensity -> Artistic Boost to punch through Mie halo
|
||||
return sunIntensity * transmittance * limbFactor * sunDiskIntensity * sunDiskProfile;
|
||||
}
|
||||
|
||||
return vec3(0.0);
|
||||
}
|
||||
|
||||
// ------------------------------------------------------------------------
|
||||
// Procedural Cirrus Clouds
|
||||
// ------------------------------------------------------------------------
|
||||
// Renders a thin layer of high-altitude clouds (Cirrus) using 3D Noise.
|
||||
//
|
||||
// IMPLEMENTATION:
|
||||
// - Modeled as a spherical shell at a specific altitude.
|
||||
// - Ray-Sphere intersection determines UV layout and distance.
|
||||
// - Animated using 3D FBM (Fractal Brownian Motion) for shape evolution + Wind drift.
|
||||
// - Lighting includes Silver Lining (HG Phase) and Atmospheric Extinction.
|
||||
//
|
||||
// PARAMETERS:
|
||||
// @param background Current Sky Color (to be blended with).
|
||||
// @param V Normalized View Vector.
|
||||
// @param L Normalized Sun Vector.
|
||||
// @param control x=Coverage, y=Density, z=QuadraticConst(C), w=WindSpeed.
|
||||
// @param control2 x=EvolutionSpeed.
|
||||
// @param geometry w=PlanetRadius (Re).
|
||||
// @param sunIntensity Sun Illuminance.
|
||||
// @param transmittance Atmospheric Transmittance (Cloud Color Tint).
|
||||
// @return Sky color composed with clouds.
|
||||
// ------------------------------------------------------------------------
|
||||
// ------------------------------------------------------------------------
|
||||
// Procedural Cirrus Clouds
|
||||
// ------------------------------------------------------------------------
|
||||
// Renders a thin layer of high-altitude clouds (Cirrus) using 3D Noise.
|
||||
//
|
||||
// PARAMETERS:
|
||||
// @param V .
|
||||
// @param L .
|
||||
// @param control .
|
||||
// @param control2 .
|
||||
// @param geometry .
|
||||
// @param sunIntensity .
|
||||
// @param transmittance.
|
||||
// @param outDensity Output: Cloud Density (0..1).
|
||||
// @return Cloud Lit Color (pre-multiplied by density? No, just lit color).
|
||||
// ------------------------------------------------------------------------
|
||||
highp vec3 getCloudLayer(highp vec3 V, highp vec3 L,
|
||||
highp vec4 control, highp vec4 control2, highp vec4 geometry,
|
||||
highp float sunIntensity, highp vec3 transmittance,
|
||||
out highp float outDensity) {
|
||||
|
||||
outDensity = 0.0;
|
||||
highp float cloudCoverage = control.x;
|
||||
|
||||
// Clip clouds below the horizon (Earth occlusion)
|
||||
// Simple check V.y > 0.0 is sufficient for skybox provided camera is near ground.
|
||||
if (cloudCoverage > 0.0 && V.y > 0.0) {
|
||||
highp float Re = geometry.w;
|
||||
highp float intersectC = control.z;
|
||||
highp float distToCloud = raySphereIntersect(V, Re, intersectC);
|
||||
|
||||
if (distToCloud > 0.0) {
|
||||
highp vec3 p = V * distToCloud;
|
||||
highp float speed = control.w;
|
||||
highp float morphSpeed = control2.x;
|
||||
highp float time = getUserTime().x;
|
||||
|
||||
// UV Mapping (Planar projected onto sphere cap is sufficient for skybox)
|
||||
// Scale factor 0.05 km^-1
|
||||
highp vec2 uv = (p.xz * 0.05) + vec2(time * speed * 2.0, 0.0);
|
||||
|
||||
// 3D Noise for Morphing
|
||||
highp float noiseVal = fbm(vec3(uv, time * morphSpeed));
|
||||
|
||||
// Remap noise based on coverage.
|
||||
// Coverage 0.5 -> threshold 0.5. Coverage 1.0 -> threshold 0.0.
|
||||
highp float threshold = 1.0 - cloudCoverage;
|
||||
highp float cloudDensity = smoothstep(threshold, threshold + 0.3, noiseVal);
|
||||
|
||||
if (cloudDensity > 0.0) {
|
||||
cloudDensity *= control.y; // Global Density Scalar
|
||||
cloudDensity = clamp(cloudDensity, 0.0, 1.0);
|
||||
outDensity = cloudDensity;
|
||||
|
||||
// Cloud Lighting
|
||||
// Silver Lining: Strong forward scattering (Fixed g=0.9 for clouds)
|
||||
highp float cosTheta = dot(V, L);
|
||||
// We need separate params for cloud silver lining (g=0.9).
|
||||
// 1 + 0.9^2 = 1.81. -2*0.9 = -1.8.
|
||||
|
||||
// Attenuation (Beer's Law)
|
||||
// Thick clouds block light.
|
||||
// 20.0 is an artistic extinction coefficient.
|
||||
highp float extinction = exp(-cloudDensity * 20.0);
|
||||
|
||||
highp float silver = hgPhase(cosTheta, vec2(1.81, -1.8)) * 40.0 * extinction;
|
||||
|
||||
// Ambient/Diffuse term.
|
||||
// We allow some ambient light to pass through even thick clouds (0.05 min)
|
||||
// so they don't look like black holes.
|
||||
highp float ambient = 0.1 + 0.4 * extinction;
|
||||
|
||||
// Diffuse term (Sun Color) + Silver Lining
|
||||
highp vec3 cloudLight = sunIntensity * transmittance * (ambient + silver);
|
||||
|
||||
// Mix based on density
|
||||
highp float volumetric = control2.y;
|
||||
highp float shading = 1.0;
|
||||
|
||||
if (volumetric > 0.5) {
|
||||
// Gradient Lighting (Fake Volumetric Bump)
|
||||
highp float gradX = dFdx(cloudDensity);
|
||||
highp float gradY = dFdy(cloudDensity);
|
||||
// Smaller Z = Steeper Bumps.
|
||||
// dFdx(density) is typically small (e.g. 0.001).
|
||||
// We want Normal to have significant X/Y component.
|
||||
highp vec3 N = normalize(vec3(-gradX, -gradY, 0.001));
|
||||
|
||||
// Screen Space Sun Direction
|
||||
highp vec3 sRight = normalize(dFdx(V));
|
||||
highp vec3 sUp = normalize(dFdy(V));
|
||||
highp vec3 L_screen = vec3(dot(L, sRight), dot(L, sUp), 0.5);
|
||||
L_screen = normalize(L_screen);
|
||||
|
||||
shading = dot(N, L_screen);
|
||||
// Increase contrast: Darker shadows
|
||||
// dot is [-1, 1]. Map to [0.3, 1.0]
|
||||
shading = mix(0.3, 1.0, shading * 0.5 + 0.5);
|
||||
|
||||
// Darken thick parts (Beer's Law approximation)
|
||||
// Aggressively darken center of clouds
|
||||
shading *= (1.0 - cloudDensity * 0.7);
|
||||
}
|
||||
|
||||
return cloudLight * shading;
|
||||
}
|
||||
}
|
||||
}
|
||||
return vec3(0.0);
|
||||
}
|
||||
|
||||
|
||||
// ------------------------------------------------------------------------
|
||||
// Dynamic Tone Mapping
|
||||
// ------------------------------------------------------------------------
|
||||
// Applies a contrast curve that varies with sun elevation.
|
||||
//
|
||||
// PROBLEM:
|
||||
// Default linear/gamma tone mapping can make sunsets look washing out.
|
||||
// Real eyes accept much higher dynamic range at twilight.
|
||||
//
|
||||
// SOLUTION:
|
||||
// - Zenith (Noon): Linear gamma (Exponent 1.0). Physically accurate.
|
||||
// - Horizon (Sunset): High contrast (Exponent > 1.0). Crushes shadows, boosts color.
|
||||
//
|
||||
// @param color Input HDR color.
|
||||
// @param L Normalized Sun Vector.
|
||||
// @param contrast Maximum contrast exponent (at horizon). e.g. 1.5.
|
||||
// @return Tone mapped color.
|
||||
// ------------------------------------------------------------------------
|
||||
highp vec3 applyDynamicToneMapping(highp vec3 color, highp vec3 L, highp float contrast) {
|
||||
float c = saturate(L.y);
|
||||
// Exponent blends from 'contrast' (at L.y=0) to 1.0 (at L.y=1)
|
||||
float exponent = mix(contrast, 1.0, sqrt(c));
|
||||
return pow(max(vec3(0.0), color), vec3(exponent));
|
||||
}
|
||||
|
||||
// ------------------------------------------------------------------------
|
||||
// Procedural Water Surface
|
||||
// ------------------------------------------------------------------------
|
||||
// Simulates an infinite ocean plane at y=0 using screen-space derivatives for normals.
|
||||
//
|
||||
// FEATURES:
|
||||
// - Projected grid for infinite surface.
|
||||
// - Screen-space wave normal reconstruction (no geometry required).
|
||||
// - Fresnel reflection of Atmosphere, Sun, and Clouds.
|
||||
// - Specular highlights (Blinn-Phong).
|
||||
//
|
||||
// PARAMETERS:
|
||||
// @param V Normalized View Vector.
|
||||
// @param L Normalized Sun Vector.
|
||||
// @param sunIntensity Sun Illuminance.
|
||||
// @param depthR Rayleigh Optical Depth.
|
||||
// @param depthM Mie Optical Depth.
|
||||
// @param ozone Ozone Absorption.
|
||||
// @param multiScatParams Multi-Scattering Params.
|
||||
// @param miePhaseParams Mie Phase Params.
|
||||
// @param sunHalo Sun Halo Params.
|
||||
// @param cloudControl Cloud Control Params.
|
||||
// @param cloudControl2 Cloud Evolution Params.
|
||||
// @param shimmerControl Shimmer Control (w component used as PlanetRadius for clouds).
|
||||
// @param waterControl Water Control (x=Strength, y=Speed, z=DerivativeTrick).
|
||||
// @return Water surface color.
|
||||
// ------------------------------------------------------------------------
|
||||
// 3D Noise for Stars
|
||||
highp float hash31(highp vec3 p) {
|
||||
p = fract(p * 0.1031);
|
||||
p += dot(p, p.yzx + 33.33);
|
||||
return fract((p.x + p.y) * p.z);
|
||||
}
|
||||
|
||||
highp float getStars(highp vec3 V, highp float density) {
|
||||
// Simple procedural stars
|
||||
// We use view vector direction to tile the sky
|
||||
// Higher frequency = smaller stars
|
||||
highp float frequency = 300.0;
|
||||
highp vec3 p = floor(V * frequency);
|
||||
|
||||
highp float h = hash31(p);
|
||||
|
||||
// Threshold for stars (very sparse)
|
||||
// param density: 0.0 (none) to 1.0 (max)
|
||||
// Default threshold was 0.995 (0.5% stars)
|
||||
// We map density 0.0 -> 1.0 threshold (no stars)
|
||||
// density 1.0 -> 0.990 threshold (1.0% stars)
|
||||
highp float threshold = 1.0 - (0.001 + density * 0.009);
|
||||
|
||||
highp float star = 0.0;
|
||||
if (h > threshold) {
|
||||
// Random brightness
|
||||
highp float brightness = (h - threshold) / (1.0 - threshold);
|
||||
star = brightness * 15.0; // Reduced from 50.0 to 15.0
|
||||
}
|
||||
return star;
|
||||
}
|
||||
|
||||
// New helper to handle Star Compositing (Fade, Rotation, Occlusion)
|
||||
highp vec3 getStarLayer(highp vec3 V, highp vec3 L, highp float cloudDensity, highp vec3 transmittance, highp vec2 starControl) {
|
||||
// starControl.x = Density, .y = Enabled
|
||||
if (starControl.y < 0.5) return vec3(0.0);
|
||||
|
||||
// 1. Fade by Sun Elevation
|
||||
// Start appearing sooner (when sun is still slightly up), but stay dim.
|
||||
// 0.10 (5.7 deg up) -> 0.0
|
||||
// -0.20 (11.5 deg down) -> 1.0
|
||||
highp float starFade = 1.0 - smoothstep(-0.20, 0.10, L.y);
|
||||
starFade *= starFade;
|
||||
|
||||
if (starFade <= 0.0) return vec3(0.0);
|
||||
|
||||
// 2. Rotate to break grid alignment
|
||||
highp vec3 rotV = vec3(
|
||||
dot(V, vec3(0.6, 0.8, 0.0)),
|
||||
dot(V, vec3(-0.8, 0.6, 0.0)),
|
||||
V.z
|
||||
);
|
||||
|
||||
highp float starVal = getStars(rotV, starControl.x);
|
||||
if (starVal <= 0.0) return vec3(0.0);
|
||||
|
||||
// 3. Cloud Occlusion (Aggressive)
|
||||
highp float cloudOcclusion = 1.0 - smoothstep(0.0, 1.0, pow(cloudDensity, 0.1));
|
||||
|
||||
return vec3(starVal) * transmittance * starFade * cloudOcclusion * 0.1;
|
||||
}
|
||||
|
||||
// ------------------------------------------------------------------------
|
||||
// Procedural Water Surface
|
||||
// ------------------------------------------------------------------------
|
||||
// Simulates an infinite ocean plane at y=0 using screen-space derivatives for normals.
|
||||
//
|
||||
// FEATURES:
|
||||
// - Projected grid for infinite surface.
|
||||
// - Screen-space wave normal reconstruction (no geometry required).
|
||||
// - Fresnel reflection of Atmosphere, Sun, and Clouds.
|
||||
// - Specular highlights (Blinn-Phong).
|
||||
//
|
||||
// PARAMETERS:
|
||||
// @param V Normalized View Vector.
|
||||
// @param L Normalized Sun Vector.
|
||||
// @param sunIntensity Sun Illuminance.
|
||||
// @param depthR Rayleigh Optical Depth.
|
||||
// @param depthM Mie Optical Depth.
|
||||
// @param ozone Ozone Absorption.
|
||||
// @param multiScatParams Multi-Scattering Params.
|
||||
// @param miePhaseParams Mie Phase Params.
|
||||
// @param sunHalo Sun Halo Params.
|
||||
// @param cloudControl Cloud Control Params.
|
||||
// @param cloudControl2 Cloud Evolution Params.
|
||||
// @param shimmerControl Shimmer Control (w component used as PlanetRadius for clouds).
|
||||
// @param waterControl Water Control (x=Strength, y=Speed, z=DerivativeTrick).
|
||||
// @return Water surface color.
|
||||
// ------------------------------------------------------------------------
|
||||
highp vec3 getWaterColor(highp vec3 V, highp vec3 L,
|
||||
highp float sunIntensity,
|
||||
highp vec3 depthR, highp vec3 depthM, highp vec3 ozone,
|
||||
highp vec4 multiScatParams, highp vec2 miePhaseParams,
|
||||
highp vec4 sunHalo,
|
||||
highp vec4 cloudControl, highp vec4 cloudControl2,
|
||||
highp vec4 shimmerControl, highp vec4 waterControl) {
|
||||
|
||||
// Project to plane y=0
|
||||
highp float t = -10.0 / min(V.y, -0.0002); // Reduced clamp to minimize "wall" artifact
|
||||
highp vec2 uv = V.xz * t * 0.05;
|
||||
|
||||
highp float time = getUserTime().x;
|
||||
highp float speed = waterControl.y;
|
||||
uv += vec2(time * 0.5 * speed, time * 0.2 * speed);
|
||||
|
||||
// Wave Normal
|
||||
// Use screen-space derivatives to compute world-space normal perturbation
|
||||
// Wave Normal
|
||||
// Use screen-space derivatives to compute world-space normal perturbation
|
||||
int octaves = int(max(1.0, waterControl.w));
|
||||
highp float h = fbm(vec3(uv, time * 0.1 * speed), octaves);
|
||||
|
||||
// Reconstruct screen-space basis in world space
|
||||
// highp vec3 sRight = normalize(dFdx(V)); // Moved inside block
|
||||
// highp vec3 sUp = normalize(dFdy(V)); // Moved inside block
|
||||
|
||||
// Perturb normal based on height gradient
|
||||
// If h increases in screen-X direction, normal tilts against sRight.
|
||||
// Fade out perturbation near horizon (V.y -> 0) to reduce aliasing
|
||||
highp float horizonFade = smoothstep(0.0, 0.5, abs(V.y));
|
||||
highp float strength = waterControl.x;
|
||||
|
||||
highp vec3 N_perturb;
|
||||
|
||||
// Derivative Trick Toggle
|
||||
if (waterControl.z > 0.5) {
|
||||
// Screen-Space Derivatives (Fast, 1 tap)
|
||||
// Reconstruct screen-space basis in world space
|
||||
// If h increases in screen-X direction, normal tilts against sRight.
|
||||
highp vec3 sRight = normalize(dFdx(V));
|
||||
highp vec3 sUp = normalize(dFdy(V));
|
||||
N_perturb = (sRight * dFdx(h) + sUp * dFdy(h)) * strength * horizonFade;
|
||||
} else {
|
||||
// Finite Difference (Standard, 3 taps)
|
||||
// More expensive but analytically correct in world space (independent of view resolution/derivatives)
|
||||
float eps = 0.02; // Epsilon for gradient
|
||||
vec3 p = vec3(uv, time * 0.1 * speed);
|
||||
float hx = fbm(p + vec3(eps, 0.0, 0.0), octaves);
|
||||
float hy = fbm(p + vec3(0.0, eps, 0.0), octaves);
|
||||
|
||||
// Gradient
|
||||
float dx = (hx - h) / eps;
|
||||
float dy = (hy - h) / eps;
|
||||
|
||||
// Construct World Space Perturbation
|
||||
// Gradient (dx, dy) acts on XZ plane.
|
||||
// Normal = normalize(-dx, 1, -dy).
|
||||
// We want N_perturb to SUBTRACT from (0,1,0).
|
||||
// N_water = normalize(Up - Perturb).
|
||||
// So Perturb = (dx, 0, dy).
|
||||
// Note: Strength needs to be calibrated to match derivative trick roughly, or just raw.
|
||||
// Derivative trick Strength was ~50.0.
|
||||
// Here dx/dy are raw noise slopes.
|
||||
// Reduced to 0.002 to match visual range of derivative trick and prevent black artifacts.
|
||||
N_perturb = vec3(dx, 0.0, dy) * (strength * 0.002) * horizonFade;
|
||||
}
|
||||
|
||||
highp vec3 N_water = normalize(vec3(0.0, 1.0, 0.0) - N_perturb);
|
||||
|
||||
// Reflection
|
||||
highp vec3 R = reflect(V, N_water);
|
||||
|
||||
highp vec3 transRefl;
|
||||
highp vec3 reflection = getAtmosphere(R, L, sunIntensity,
|
||||
depthR, depthM,
|
||||
ozone, multiScatParams,
|
||||
miePhaseParams,
|
||||
transRefl);
|
||||
|
||||
// Clouds in reflection
|
||||
highp float reflCloudDensity;
|
||||
highp vec3 reflCloudLayer = getCloudLayer(R, L, materialParams.cloudControl, materialParams.cloudControl2,
|
||||
materialParams.shimmerControl, materialParams.sunIntensity, transRefl,
|
||||
reflCloudDensity);
|
||||
|
||||
// Add Stars to Reflection
|
||||
// Use helper with Reflection Vector and Reflection Cloud Density
|
||||
// Horizon Mask: Fade out star reflections that are deep in the water (high R.y)
|
||||
// Restricted to very close to horizon (0.0 to 0.1) as requested.
|
||||
highp float rHorizonMask = 1.0 - smoothstep(0.0, 0.1, R.y);
|
||||
|
||||
if (rHorizonMask > 0.0) {
|
||||
reflection += getStarLayer(R, L, reflCloudDensity, transRefl, materialParams.starControl) * rHorizonMask;
|
||||
}
|
||||
|
||||
// Add Sun Disk to reflection (Occluded)
|
||||
highp float reflSunAccess = 1.0 - smoothstep(0.0, 0.7, reflCloudDensity * 1.5);
|
||||
reflection += getSunDisk(R, L, sunHalo, sunIntensity, transRefl) * reflSunAccess;
|
||||
|
||||
// Apply clouds to reflection
|
||||
reflection = mix(reflection, reflCloudLayer, reflCloudDensity);
|
||||
|
||||
// Fresnel
|
||||
highp float F0 = 0.02; // Water
|
||||
highp float cosTheta = clamp(dot(-V, N_water), 0.0, 1.0);
|
||||
highp float F = F0 + (1.0 - F0) * pow(1.0 - cosTheta, 5.0);
|
||||
|
||||
highp vec3 deepColor = vec3(0.0, 0.005, 0.02); // Deep blue/black
|
||||
|
||||
highp vec3 waterColor = mix(deepColor, reflection, F);
|
||||
|
||||
return waterColor;
|
||||
}
|
||||
|
||||
|
||||
|
||||
void material(inout MaterialInputs material) {
|
||||
prepareMaterial(material);
|
||||
|
||||
highp vec3 V = normalize(variable_eyeDirection.xyz);
|
||||
highp vec3 L = normalize(materialParams.sunDirection);
|
||||
|
||||
// 1. Heat Shimmer
|
||||
// Fade out as sun rises (Strongest at horizon, zero at 30 degrees up)
|
||||
highp float sunFade = 1.0 - smoothstep(0.0, 0.5, abs(L.y));
|
||||
highp float shimmerIntensity = applyHeatShimmer(V, materialParams.shimmerControl.x * sunFade,
|
||||
materialParams.shimmerControl.y,
|
||||
materialParams.shimmerControl.z);
|
||||
|
||||
// 2. Atmospheric Scattering
|
||||
highp vec3 transmittance;
|
||||
highp vec3 inScatter1 = getAtmosphere(V, L, materialParams.sunIntensity,
|
||||
materialParams.depthR, materialParams.depthM,
|
||||
materialParams.ozone, materialParams.multiScatParams,
|
||||
materialParams.miePhaseParams,
|
||||
transmittance);
|
||||
|
||||
|
||||
// Sun 2 (Optional)
|
||||
// We reuse the same Transmittance (view dependent) and Phase params.
|
||||
// We do NOT add extra Multi-Scattering (Ambient) for the second sun to save cost/complexity.
|
||||
// It contributes Direct In-Scattering (Beams/Glow) only.
|
||||
highp vec3 inScatter2 = vec3(0.0);
|
||||
if (materialParams.sunHalo2.w > 0.5) {
|
||||
highp vec3 L2 = normalize(materialParams.sunDirection2);
|
||||
inScatter2 = getSecondarySunScattering(V, L2,
|
||||
materialParams.sunIntensity2,
|
||||
materialParams.depthR,
|
||||
materialParams.depthM,
|
||||
materialParams.ozone,
|
||||
materialParams.miePhaseParams,
|
||||
transmittance);
|
||||
}
|
||||
|
||||
highp vec3 finalColor = inScatter1 + inScatter2;
|
||||
|
||||
// 5. Procedural Clouds
|
||||
highp float cloudDensity;
|
||||
highp vec3 cloudLayer = getCloudLayer(V, L,
|
||||
materialParams.cloudControl,
|
||||
materialParams.cloudControl2,
|
||||
materialParams.shimmerControl, // reusing w=PlanetRadius
|
||||
materialParams.sunIntensity,
|
||||
transmittance,
|
||||
cloudDensity);
|
||||
|
||||
// Add Stars
|
||||
// Stars are at infinity.
|
||||
// Use helper function.
|
||||
finalColor += getStarLayer(V, L, cloudDensity, transmittance, materialParams.starControl);
|
||||
|
||||
// 3. Sun Disks - Occluded by clouds
|
||||
// Sun Access is (1.0 - cloudDensity) but arguably non-linear for sharp disk
|
||||
highp float sunAccess = 1.0 - smoothstep(0.0, 0.7, cloudDensity * 1.5);
|
||||
|
||||
finalColor += getSunDisk(V, L, materialParams.sunHalo,
|
||||
materialParams.sunIntensity, transmittance) * sunAccess;
|
||||
|
||||
if (materialParams.sunHalo2.w > 0.5) {
|
||||
highp vec3 L2 = normalize(materialParams.sunDirection2);
|
||||
// Note: Ideally we should compute cloud density for L2 direction if clouds are 3D...
|
||||
// But here we use V direction clouds (view-based).
|
||||
// Since clouds are in front of everything, this is correct for view-based occlusion.
|
||||
finalColor += getSunDisk(V, L2, materialParams.sunHalo2,
|
||||
materialParams.sunIntensity2, transmittance) * sunAccess;
|
||||
}
|
||||
|
||||
// 4. Night Sky Offset
|
||||
finalColor += materialParams.nightColor;
|
||||
|
||||
// 5. Apply Clouds
|
||||
finalColor = mix(finalColor, cloudLayer, cloudDensity);
|
||||
|
||||
// 6. Dynamic Tone Mapping
|
||||
finalColor = applyDynamicToneMapping(finalColor, L, materialParams.contrast);
|
||||
|
||||
if (V.y < 0.0) {
|
||||
finalColor = getWaterColor(V, L, materialParams.sunIntensity,
|
||||
materialParams.depthR, materialParams.depthM,
|
||||
materialParams.ozone, materialParams.multiScatParams,
|
||||
materialParams.miePhaseParams,
|
||||
materialParams.sunHalo,
|
||||
materialParams.cloudControl, materialParams.cloudControl2,
|
||||
materialParams.shimmerControl,
|
||||
materialParams.waterControl);
|
||||
finalColor = applyDynamicToneMapping(finalColor, L, materialParams.contrast);
|
||||
}
|
||||
|
||||
material.baseColor = vec4(finalColor, 1.0);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -1,9 +0,0 @@
|
||||
body {
|
||||
margin: 0;
|
||||
overflow: hidden;
|
||||
}
|
||||
canvas {
|
||||
touch-action: none;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
}
|
||||
@@ -49,9 +49,6 @@
|
||||
"libs/fgviewer/README.md": {
|
||||
"dest": "dup/fgviewer.md"
|
||||
},
|
||||
"ios/CocoaPods/README.md": {
|
||||
"dest": "release/cocoapods.md"
|
||||
},
|
||||
"tools/normal-blending/README.md": {
|
||||
"dest": "dup/normal_blending.md"
|
||||
},
|
||||
@@ -82,22 +79,13 @@
|
||||
"tools/specular-color/README.md": {
|
||||
"dest": "dup/specular_color.md"
|
||||
},
|
||||
"tools/specgen/README.md": {
|
||||
"dest": "dup/specgen.md"
|
||||
},
|
||||
"docs_src/README.md": {
|
||||
"dest": "dup/docs.md"
|
||||
},
|
||||
"test/renderdiff/README.md": {
|
||||
"dest": "dup/test_ci_renderdiff.md",
|
||||
"dest": "dup/renderdiff.md",
|
||||
"link_transforms": {
|
||||
"docs/images/": "../images/"
|
||||
}
|
||||
},
|
||||
"test/backend/README.md": {
|
||||
"dest": "dup/test_ci_backend.md"
|
||||
},
|
||||
"filament/backend/test/README.md": {
|
||||
"dest": "dup/backend_test.md"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -20,6 +20,3 @@ enable = false
|
||||
|
||||
[output.html.fold]
|
||||
enable = false
|
||||
|
||||
[output.html.mathjax]
|
||||
enable = true
|
||||
@@ -2,7 +2,8 @@
|
||||
|
||||
- [Introduction](./dup/intro.md)
|
||||
- [Build](./dup/building.md)
|
||||
- [Android on Windows](./build/windows_android.md)
|
||||
- [Build for Android on Windows](./build/windows_android.md)
|
||||
- [Maven Release](./build/maven_release.md)
|
||||
- [Contribute](./dup/contributing.md)
|
||||
- [Coding Style](./dup/code_style.md)
|
||||
- [Core Concepts](./main/README.md)
|
||||
@@ -13,12 +14,9 @@
|
||||
- [Web Tutorial](./samples/web.md)
|
||||
- [Technical Notes](./notes/README.md)
|
||||
- [Material Properties](./notes/material_properties.md)
|
||||
- [Release](./release/README.md)
|
||||
- [CocoaPods](./release/cocoapods.md)
|
||||
- [Maven](./release/maven.md)
|
||||
- [Github](./release/guide.md)
|
||||
- [Versioning](./release/versioning.md)
|
||||
- [Branching](./release/branching.md)
|
||||
- [Versioning](./notes/versioning.md)
|
||||
- [Branching](./notes/branching.md)
|
||||
- [Release Guide](./notes/release_guide.md)
|
||||
- [Documentation](./dup/docs.md)
|
||||
- [Debugging](./notes/debugging.md)
|
||||
- [Metal](./notes/metal_debugging.md)
|
||||
@@ -30,9 +28,7 @@
|
||||
- [Performance analysis](./notes/performance_analysis.md)
|
||||
- [Framegraph](./notes/framegraph.md)
|
||||
- [Tests](./notes/tests.md)
|
||||
- [backend](./dup/backend_test.md)
|
||||
- [CI: backend](./dup/test_ci_backend.md)
|
||||
- [CI: renderdiff](./dup/test_ci_renderdiff.md)
|
||||
- [renderdiff](./dup/renderdiff.md)
|
||||
- [Libraries](./notes/libs.md)
|
||||
- [bluegl](./dup/bluegl.md)
|
||||
- [bluevk](./dup/bluevk.md)
|
||||
@@ -47,10 +43,9 @@
|
||||
- [cmgen](./dup/cmgen.md)
|
||||
- [cso-lut](./dup/cso_lut.md)
|
||||
- [filamesh](./dup/filamesh.md)
|
||||
- [matinfo](./dup/matinfo.md)
|
||||
- [mipgen](./dup/mipgen.md)
|
||||
- [normal-blending](./dup/normal_blending.md)
|
||||
- [mipgen](./dup/mipgen.md)
|
||||
- [matinfo](./dup/matinfo.md)
|
||||
- [roughness-prefilter](./dup/roughness_prefilter.md)
|
||||
- [specgen](./dup/specgen.md)
|
||||
- [specular-color](./dup/specular_color.md)
|
||||
- [zbloat](./dup/zbloat.md)
|
||||
|
||||
@@ -1,4 +0,0 @@
|
||||
# Release
|
||||
|
||||
This is a collection of guides for releasing Filament on different distribution platforms, including
|
||||
Github, Maven, and CocoaPods.
|
||||
@@ -1,311 +0,0 @@
|
||||
|
||||
// SimulatedSkybox.js
|
||||
// Ported from samples/utils/SimulatedSkybox.cpp
|
||||
|
||||
class SimulatedSkybox {
|
||||
constructor(engine) {
|
||||
this.engine = engine;
|
||||
|
||||
// Default Parameters
|
||||
this.sunDirection = [0, 1, 0];
|
||||
this.sunIntensity = 100000.0;
|
||||
this.turbidity = 2.0;
|
||||
this.rayleigh = 1.0;
|
||||
this.mieCoefficient = 1.0;
|
||||
this.mieG = 0.8;
|
||||
this.ozone = 0.0;
|
||||
this.msFactors = [0.1, 0.5, 0.0];
|
||||
this.contrast = 1.0;
|
||||
this.nightColor = [0.0, 0.0003, 0.00075];
|
||||
this.shimmerControl = [0.0, 20.0, 0.1];
|
||||
this.cloudControl = [0.0, 0.1, 8000.0, 0.0];
|
||||
this.cloudControl2 = [0.0, 0.0, 0.0, 0.0];
|
||||
this.waterControl = [50.0, 1.0, 1.0, 4.0]; // x=Strength, y=Speed, z=DerivativeTrick, w=Octaves
|
||||
this.starControl = [1.0, 1.0]; // x=Density (0-1), y=Enabled (0-1)
|
||||
this.planetRadius = 6360.0;
|
||||
|
||||
// Sun Halo
|
||||
// x=cos(rad), y=limbDarkening, z=intensity, w=enabled
|
||||
this.sunHalo = [Math.cos(0.5 * Math.PI / 180.0), 0.5, 1.0, 1.0];
|
||||
|
||||
this.initEntity();
|
||||
}
|
||||
|
||||
async loadMaterial(url) {
|
||||
console.log("Loading material from:", url);
|
||||
const response = await fetch(url);
|
||||
const buffer = await response.arrayBuffer();
|
||||
this.material = this.engine.createMaterial(new Uint8Array(buffer));
|
||||
this.materialInstance = this.material.createInstance();
|
||||
|
||||
// Re-bind the entity with the loaded material
|
||||
const rcm = this.engine.getRenderableManager();
|
||||
const instance = rcm.getInstance(this.entity);
|
||||
rcm.setMaterialInstanceAt(instance, 0, this.materialInstance);
|
||||
|
||||
console.log("Material loaded and bound.");
|
||||
this.updateCoefficients();
|
||||
}
|
||||
|
||||
initEntity() {
|
||||
const EntityManager = Filament.EntityManager;
|
||||
const RenderableManager = Filament.RenderableManager;
|
||||
const VertexBuffer = Filament.VertexBuffer;
|
||||
const IndexBuffer = Filament.IndexBuffer;
|
||||
const AttributeType = Filament.VertexBuffer$AttributeType;
|
||||
const VertexAttribute = Filament.VertexAttribute;
|
||||
const PrimitiveType = Filament.RenderableManager$PrimitiveType;
|
||||
const IndexType = Filament.IndexBuffer$IndexType;
|
||||
|
||||
this.entity = EntityManager.get().create();
|
||||
|
||||
// 3 vertices for full screen triangle
|
||||
// coords: -1,-1 to 3,-1 to -1,3
|
||||
const TRIANGLE_VERTICES = new Float32Array([
|
||||
-1.0, -1.0,
|
||||
3.0, -1.0,
|
||||
-1.0, 3.0
|
||||
]);
|
||||
|
||||
const TRIANGLE_INDICES = new Uint16Array([0, 1, 2]);
|
||||
|
||||
this.vb = VertexBuffer.Builder()
|
||||
.vertexCount(3)
|
||||
.bufferCount(1)
|
||||
.attribute(VertexAttribute.POSITION, 0, AttributeType.FLOAT2, 0, 8)
|
||||
.build(this.engine);
|
||||
|
||||
this.vb.setBufferAt(this.engine, 0, TRIANGLE_VERTICES);
|
||||
|
||||
this.ib = IndexBuffer.Builder()
|
||||
.indexCount(3)
|
||||
.bufferType(IndexType.USHORT)
|
||||
.build(this.engine);
|
||||
|
||||
this.ib.setBuffer(this.engine, TRIANGLE_INDICES);
|
||||
|
||||
// We create a dummy material first or wait?
|
||||
// In JS we usually can't block. We'll rely on loadMaterial being called.
|
||||
// For now, we build the Renderable without material, then set it later.
|
||||
|
||||
RenderableManager.Builder(1)
|
||||
.geometry(0, PrimitiveType.TRIANGLES, this.vb, this.ib)
|
||||
.culling(false)
|
||||
.castShadows(false)
|
||||
.receiveShadows(false)
|
||||
.priority(7) // Render behind translucent objects? 7 is skybox priority typically.
|
||||
.build(this.engine, this.entity);
|
||||
}
|
||||
|
||||
setSunPosition(direction) {
|
||||
// normalize
|
||||
const len = Math.hypot(direction[0], direction[1], direction[2]);
|
||||
if (len > 0) {
|
||||
this.sunDirection = [direction[0] / len, direction[1] / len, direction[2] / len];
|
||||
} else {
|
||||
this.sunDirection = [0, 1, 0];
|
||||
}
|
||||
this.updateCoefficients();
|
||||
}
|
||||
|
||||
setSunIntensity(intensity) {
|
||||
this.sunIntensity = Math.max(0.0, intensity);
|
||||
this.updateCoefficients();
|
||||
}
|
||||
|
||||
setTurbidity(turbidity) {
|
||||
this.turbidity = Math.max(0.0, turbidity);
|
||||
this.updateCoefficients();
|
||||
}
|
||||
|
||||
setRayleigh(rayleigh) {
|
||||
this.rayleigh = Math.max(0.0, rayleigh);
|
||||
this.updateCoefficients();
|
||||
}
|
||||
|
||||
setMieCoefficient(mie) {
|
||||
this.mieCoefficient = Math.max(0.0, mie);
|
||||
this.updateCoefficients();
|
||||
}
|
||||
|
||||
setMieG(g) {
|
||||
this.mieG = Math.max(0.0, g);
|
||||
this.updateCoefficients();
|
||||
}
|
||||
|
||||
setOzone(strength) {
|
||||
this.ozone = Math.max(0.0, strength);
|
||||
this.updateCoefficients();
|
||||
}
|
||||
|
||||
setMultiScattering(r, m) {
|
||||
this.msFactors[0] = Math.max(0.0, Math.min(2.0, r));
|
||||
this.msFactors[1] = Math.max(0.0, Math.min(2.0, m));
|
||||
this.updateCoefficients();
|
||||
}
|
||||
|
||||
setHorizonGlow(strength) {
|
||||
this.msFactors[2] = Math.max(0.0, Math.min(1.0, strength));
|
||||
this.updateCoefficients();
|
||||
}
|
||||
|
||||
setContrast(contrast) {
|
||||
this.contrast = contrast;
|
||||
this.updateCoefficients();
|
||||
}
|
||||
|
||||
setNightColor(color) {
|
||||
this.nightColor = color;
|
||||
this.updateCoefficients();
|
||||
}
|
||||
|
||||
setSunRadius(degrees) {
|
||||
const rad = degrees * (Math.PI / 180.0);
|
||||
this.sunHalo[0] = Math.cos(rad);
|
||||
this.updateCoefficients();
|
||||
}
|
||||
|
||||
setSunDiskIntensity(intensity) {
|
||||
this.sunHalo[2] = Math.max(0.0, intensity);
|
||||
this.updateCoefficients();
|
||||
}
|
||||
|
||||
setSunLimbDarkening(strength) {
|
||||
this.sunHalo[1] = Math.max(0.0, strength);
|
||||
this.updateCoefficients();
|
||||
}
|
||||
|
||||
setSunDiskEnabled(enabled) {
|
||||
this.sunHalo[3] = enabled ? 1.0 : 0.0;
|
||||
this.updateCoefficients();
|
||||
}
|
||||
|
||||
setShimmerControl(strength, frequency, maskHeight) {
|
||||
this.shimmerControl[0] = Math.max(0.0, strength);
|
||||
this.shimmerControl[1] = Math.max(0.0, frequency);
|
||||
this.shimmerControl[2] = Math.max(0.001, maskHeight);
|
||||
this.updateCoefficients();
|
||||
}
|
||||
|
||||
setCloudControl(coverage, density, height, speed) {
|
||||
this.cloudControl[0] = Math.max(0.0, Math.min(1.0, coverage));
|
||||
this.cloudControl[1] = Math.max(0.0, density);
|
||||
this.cloudControl[2] = Math.max(1000.0, height);
|
||||
// JS speed adjustment logic matches C++: speed * (0.05 / 72.0)
|
||||
this.cloudControl[3] = speed * (0.05 / 72.0);
|
||||
this.updateCoefficients();
|
||||
}
|
||||
|
||||
setCloudShapeEvolution(speed) {
|
||||
this.cloudControl2[0] = speed;
|
||||
this.updateCoefficients();
|
||||
}
|
||||
|
||||
setCloudVolumetricLighting(enabled) {
|
||||
this.cloudControl2[1] = enabled ? 1.0 : 0.0;
|
||||
this.updateCoefficients();
|
||||
}
|
||||
|
||||
setWaterControl(strength, speed, derivativeTrick, octaves) {
|
||||
this.waterControl[0] = Math.max(0.0, strength);
|
||||
this.waterControl[1] = Math.max(0.0, speed);
|
||||
this.waterControl[2] = derivativeTrick;
|
||||
this.waterControl[3] = Math.max(1.0, Math.min(8.0, octaves));
|
||||
this.updateCoefficients();
|
||||
}
|
||||
|
||||
setStarControl(density, enabled) {
|
||||
this.starControl[0] = Math.max(0.0, Math.min(1.0, density));
|
||||
this.starControl[1] = enabled ? 1.0 : 0.0;
|
||||
this.updateCoefficients();
|
||||
}
|
||||
|
||||
updateCoefficients() {
|
||||
if (!this.materialInstance) {
|
||||
console.warn("updateCoefficients called before material loaded");
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
// 1. Rayleigh Coefficients
|
||||
const F_PI = Math.PI;
|
||||
const lambda = [680e-9, 550e-9, 440e-9];
|
||||
const n = 1.0003;
|
||||
const N = 2.545e25;
|
||||
const term = (8.0 * Math.pow(F_PI, 3.0) * Math.pow(n * n - 1.0, 2.0)) / (3.0 * N);
|
||||
|
||||
const depthR = [
|
||||
term / Math.pow(lambda[0], 4.0),
|
||||
term / Math.pow(lambda[1], 4.0),
|
||||
term / Math.pow(lambda[2], 4.0)
|
||||
].map(v => v * 8000.0 * this.rayleigh);
|
||||
|
||||
// 2. Mie Coefficients
|
||||
const mieAlpha = 1.3;
|
||||
const mieBase = 2.0e-5 * this.turbidity;
|
||||
const depthM = [
|
||||
mieBase * Math.pow(550e-9 / lambda[0], mieAlpha),
|
||||
mieBase * Math.pow(550e-9 / lambda[1], mieAlpha),
|
||||
mieBase * Math.pow(550e-9 / lambda[2], mieAlpha)
|
||||
].map(v => v * 1200.0 * this.mieCoefficient);
|
||||
|
||||
// Fake Ozone
|
||||
const ozone = [0.0, this.ozone * 0.1, 0.0];
|
||||
|
||||
// Sun Fade (Horizon)
|
||||
const cutoffAngle = 96.0 * (F_PI / 180.0);
|
||||
const steepness = 1.5;
|
||||
const zenithFade = 1.0 - Math.exp(-(cutoffAngle / steepness));
|
||||
const zenithAngle = Math.acos(Math.max(-1.0, Math.min(1.0, this.sunDirection[1])));
|
||||
const sunFade = Math.max(0.0, 1.0 - Math.exp(-((cutoffAngle - zenithAngle) / steepness))) / zenithFade;
|
||||
|
||||
const physicalSunIntensity = this.sunIntensity * sunFade;
|
||||
|
||||
// Radiance Conversion for Sun Halo
|
||||
// Solid Angle = 2 * PI * (1 - cos(angularRadius))
|
||||
const solidAngle = 2.0 * F_PI * (1.0 - this.sunHalo[0]);
|
||||
const radianceConversion = 1.0 / Math.max(1e-9, solidAngle);
|
||||
const sunHaloUpload = [...this.sunHalo];
|
||||
sunHaloUpload[2] *= radianceConversion;
|
||||
|
||||
// Cloud Intersection
|
||||
const r = this.planetRadius;
|
||||
const h = this.cloudControl[2] * 0.001; // m -> km
|
||||
const intersectC = r * r - (r + h) * (r + h);
|
||||
const cloudUniform = [...this.cloudControl];
|
||||
cloudUniform[2] = intersectC;
|
||||
|
||||
// Shimmer Uniform
|
||||
const shimmerUniform = [...this.shimmerControl, r];
|
||||
|
||||
// Multi-Scattering Vector
|
||||
const isotropicPhase = 0.25;
|
||||
const msVector = depthR.map((v, i) => (v * this.msFactors[0] + depthM[i] * this.msFactors[1]) * isotropicPhase);
|
||||
|
||||
// Upload
|
||||
this.materialInstance.setFloat3Parameter('sunDirection', new Float32Array(this.sunDirection));
|
||||
this.materialInstance.setFloat3Parameter('depthR', new Float32Array(depthR));
|
||||
this.materialInstance.setFloat3Parameter('depthM', new Float32Array(depthM));
|
||||
this.materialInstance.setFloat3Parameter('ozone', new Float32Array(ozone));
|
||||
this.materialInstance.setFloat4Parameter('sunHalo', new Float32Array(sunHaloUpload));
|
||||
|
||||
this.materialInstance.setFloat4Parameter('multiScatParams', new Float32Array([...msVector, this.msFactors[2]]));
|
||||
|
||||
// Mie Phase
|
||||
const g2 = this.mieG * this.mieG;
|
||||
this.materialInstance.setFloat2Parameter('miePhaseParams', new Float32Array([1.0 + g2, -2.0 * this.mieG]));
|
||||
|
||||
this.materialInstance.setFloatParameter('contrast', this.contrast);
|
||||
|
||||
const nightColorScaled = this.nightColor.map(v => v * this.sunIntensity);
|
||||
this.materialInstance.setFloat3Parameter('nightColor', new Float32Array(nightColorScaled));
|
||||
|
||||
this.materialInstance.setFloat4Parameter('shimmerControl', new Float32Array(shimmerUniform));
|
||||
this.materialInstance.setFloat4Parameter('cloudControl', new Float32Array(cloudUniform));
|
||||
this.materialInstance.setFloat4Parameter('cloudControl2', new Float32Array(this.cloudControl2));
|
||||
this.materialInstance.setFloat4Parameter('waterControl', new Float32Array(this.waterControl));
|
||||
this.materialInstance.setFloat2Parameter('starControl', new Float32Array(this.starControl));
|
||||
|
||||
this.materialInstance.setFloatParameter('sunIntensity', physicalSunIntensity);
|
||||
}
|
||||
}
|
||||
Binary file not shown.
@@ -1,10 +0,0 @@
|
||||
# Result: /Users/mathias/sources/git/filament/out/cmake-release/tools/matc/matc
|
||||
MATC="../../../../out/cmake-release/tools/matc/matc"
|
||||
|
||||
# Navigate to script directory to ensure relative paths work
|
||||
cd "$(dirname "$0")"
|
||||
|
||||
set -e
|
||||
|
||||
$MATC -a opengl -p mobile -o assets/simulated_skybox.filamat simulated_skybox.mat
|
||||
echo "Material recompiled to assets/simulated_skybox.filamat"
|
||||
File diff suppressed because one or more lines are too long
Binary file not shown.
28
docs_src/src_raw/wip/sky/gl-matrix-min.js
vendored
28
docs_src/src_raw/wip/sky/gl-matrix-min.js
vendored
File diff suppressed because one or more lines are too long
@@ -1,39 +0,0 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
|
||||
<head>
|
||||
<title>Analytic Skybox</title>
|
||||
<meta charset="utf-8">
|
||||
<meta name="viewport" content="width=device-width,user-scalable=no,initial-scale=1">
|
||||
<link href="styles.css" rel="stylesheet">
|
||||
<style>
|
||||
body {
|
||||
margin: 0;
|
||||
overflow: hidden;
|
||||
background: #000;
|
||||
}
|
||||
|
||||
canvas {
|
||||
touch-action: none;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
outline: none;
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
|
||||
<body>
|
||||
<canvas></canvas>
|
||||
<!-- Filament -->
|
||||
<script src="filament.js"></script>
|
||||
<script src="gl-matrix-min.js"></script>
|
||||
|
||||
<!-- UI -->
|
||||
<script src="lil-gui.js"></script>
|
||||
|
||||
<!-- App -->
|
||||
<script src="SimulatedSkybox.js?v=26"></script>
|
||||
<script src="main.js?v=26"></script>
|
||||
</body>
|
||||
|
||||
</html>
|
||||
File diff suppressed because one or more lines are too long
@@ -1,345 +0,0 @@
|
||||
|
||||
// main.js
|
||||
|
||||
Filament.init(['assets/simulated_skybox.filamat'], () => {
|
||||
window.app = new App(document.getElementsByTagName('canvas')[0]);
|
||||
});
|
||||
|
||||
class App {
|
||||
constructor(canvas) {
|
||||
this.canvas = canvas;
|
||||
const engine = this.engine = Filament.Engine.create(this.canvas);
|
||||
this.scene = engine.createScene();
|
||||
|
||||
this.skybox = new SimulatedSkybox(engine);
|
||||
this.skybox.entity = this.skybox.entity; // Ensuring access if needed
|
||||
this.scene.addEntity(this.skybox.entity);
|
||||
|
||||
// Load the material explicitly since we passed it to init but SimulatedSkybox needs to bind it
|
||||
// Actually SimulatedSkybox.loadMaterial fetches it.
|
||||
// Since we already loaded it in Filament.init, we can arguably just use it if we had a way to access the asset.
|
||||
// But Filament.init assets are for internal or easy access via assets object if configured?
|
||||
// Let's just let SimulatedSkybox fetch it again or use a blob if we wanted.
|
||||
// Simpler: Just let SimulatedSkybox fetch it.
|
||||
this.skybox.loadMaterial('assets/simulated_skybox.filamat').then(() => {
|
||||
this.initGUI();
|
||||
});
|
||||
|
||||
this.swapChain = engine.createSwapChain();
|
||||
this.renderer = engine.createRenderer();
|
||||
this.camera = engine.createCamera(Filament.EntityManager.get().create());
|
||||
|
||||
this.view = engine.createView();
|
||||
this.view.setCamera(this.camera);
|
||||
this.view.setScene(this.scene);
|
||||
// Color Grading
|
||||
const ColorGrading = Filament.ColorGrading;
|
||||
const ToneMapping = Filament.ColorGrading$ToneMapping;
|
||||
this.colorGrading = ColorGrading.Builder()
|
||||
.toneMapping(ToneMapping.ACES_LEGACY)
|
||||
.build(engine);
|
||||
this.view.setColorGrading(this.colorGrading);
|
||||
this.view.setPostProcessingEnabled(true); // Essential for tone mapping
|
||||
|
||||
// Bloom
|
||||
this.view.setBloomOptions({
|
||||
enabled: false,
|
||||
lenseFlare: false
|
||||
});
|
||||
|
||||
// Clear color is not really visible behind skybox, but black is standard
|
||||
this.renderer.setClearOptions({ clearColor: [0.0, 0.0, 0.0, 1.0], clear: true });
|
||||
|
||||
// Camera handling (Exposure)
|
||||
this.params = {
|
||||
aperture: 16.0,
|
||||
shutterSpeed: 125.0,
|
||||
iso: 100.0,
|
||||
sunTheta: Math.acos(0.0), // Default Height 0.0 (Horizon)
|
||||
sunPhi: 0.0,
|
||||
focalLength: 24.0, // mm
|
||||
sunIntensity: 100000.0 // Base intensity
|
||||
};
|
||||
|
||||
this.camState = {
|
||||
theta: Math.PI / 2, // Look at +X (Sun Position at Phi=0)
|
||||
phi: 0.0,
|
||||
dragging: false,
|
||||
lastX: 0,
|
||||
lastY: 0
|
||||
};
|
||||
|
||||
this.initControls(); // Initialize controls immediately
|
||||
|
||||
this.resize();
|
||||
window.addEventListener('resize', this.resize.bind(this));
|
||||
|
||||
this.render = this.render.bind(this);
|
||||
window.requestAnimationFrame(this.render);
|
||||
}
|
||||
|
||||
getExposure() {
|
||||
// Formula: 1.0 / ( 1.2 * (N^2 / t) * (S / 100) )
|
||||
// t = 1/shutterSpeed
|
||||
const N = this.params.aperture;
|
||||
const t = 1.0 / this.params.shutterSpeed;
|
||||
const S = this.params.iso;
|
||||
const ev100_linear = (N * N) / t * (100.0 / S);
|
||||
const exposure = 1.0 / (1.2 * ev100_linear);
|
||||
return exposure;
|
||||
}
|
||||
|
||||
updateCameraExposure() {
|
||||
this.camera.setExposure(this.params.aperture, 1.0 / this.params.shutterSpeed, this.params.iso);
|
||||
// Also update Sun Intensity because it needs to be pre-exposed
|
||||
this.updateSunIntensity();
|
||||
}
|
||||
|
||||
updateSunIntensity() {
|
||||
const exposure = this.getExposure();
|
||||
const preExposedIntensity = this.params.sunIntensity * exposure;
|
||||
this.skybox.setSunIntensity(preExposedIntensity);
|
||||
}
|
||||
|
||||
updateCameraProjection() {
|
||||
const width = this.canvas.width;
|
||||
const height = this.canvas.height;
|
||||
const aspect = width / height;
|
||||
this.camera.setLensProjection(this.params.focalLength, aspect, 0.1, 5000.0);
|
||||
}
|
||||
|
||||
initGUI() {
|
||||
const gui = new lil.GUI({ title: "Analytic Skybox" });
|
||||
const self = this;
|
||||
const sky = this.skybox;
|
||||
|
||||
// Initialize local params from skybox defaults
|
||||
// Initialize local params from skybox defaults
|
||||
// REMOVED: Do not overwrite this.params from sky.sunDirection (Zenith)
|
||||
// const currentDir = sky.sunDirection;
|
||||
// this.params.sunTheta = ...
|
||||
|
||||
const updateSun = () => {
|
||||
const theta = this.params.sunTheta;
|
||||
const phi = this.params.sunPhi;
|
||||
const x = Math.sin(theta) * Math.cos(phi);
|
||||
const y = Math.cos(theta);
|
||||
const z = Math.sin(theta) * Math.sin(phi);
|
||||
sky.setSunPosition([x, y, z]);
|
||||
};
|
||||
|
||||
const sunFolder = gui.addFolder('Sun');
|
||||
// Helper for "Sun Height" cosine slider like C++
|
||||
const sunHeightParam = { height: Math.cos(this.params.sunTheta) };
|
||||
sunFolder.add(sunHeightParam, 'height', -0.2, 1.0).name('Height (Cos)').onChange(v => {
|
||||
this.params.sunTheta = Math.acos(v);
|
||||
updateSun();
|
||||
});
|
||||
sunFolder.add(this.params, 'sunPhi', 0.0, Math.PI * 2).name('Azimuth').onChange(updateSun);
|
||||
// Updated: Controls params.sunIntensity and triggers updateSunIntensity
|
||||
sunFolder.add(this.params, 'sunIntensity', 0.0, 500000.0).onChange(v => this.updateSunIntensity());
|
||||
|
||||
const sunDisk = sunFolder.addFolder('Disk');
|
||||
// We need local proxy for sunRadius due to conversion
|
||||
const diskParams = {
|
||||
radius: 1.2,
|
||||
enabled: true // Enable sun disk
|
||||
};
|
||||
sky.setSunDiskEnabled(true);
|
||||
sky.setSunRadius(1.2);
|
||||
sunDisk.add(diskParams, 'enabled').onChange(v => sky.setSunDiskEnabled(v));
|
||||
sunDisk.add(diskParams, 'radius', 0.0, 5.0).onChange(v => sky.setSunRadius(v));
|
||||
sunDisk.add(sky.sunHalo, 1, 0.0, 2.0).name('Limb Darkening').onChange(v => sky.setSunLimbDarkening(v));
|
||||
sunDisk.add(sky.sunHalo, 2, 0.0, 100.0).name('Intensity Boost').onChange(v => sky.setSunDiskIntensity(v));
|
||||
|
||||
const atmFolder = gui.addFolder('Atmosphere');
|
||||
atmFolder.add(sky, 'turbidity', 1.0, 10.0).onChange(v => sky.setTurbidity(v));
|
||||
atmFolder.add(sky, 'rayleigh', 0.0, 10.0).onChange(v => sky.setRayleigh(v));
|
||||
atmFolder.add(sky, 'mieCoefficient', 0.0, 10.0).onChange(v => sky.setMieCoefficient(v));
|
||||
// Set Ozone default to 0.25
|
||||
sky.setOzone(0.25);
|
||||
atmFolder.add(sky, 'ozone', 0.0, 1.0).onChange(v => sky.setOzone(v));
|
||||
atmFolder.add(sky, 'mieG', 0.0, 0.999).onChange(v => sky.setMieG(v));
|
||||
|
||||
const artFolder = gui.addFolder('Artistic');
|
||||
// Set Horizon Glow default to 1.0
|
||||
sky.setHorizonGlow(1.0);
|
||||
sky.msFactors[2] = 1.0;
|
||||
// Set Contrast default to 0.85
|
||||
sky.setContrast(0.85);
|
||||
|
||||
artFolder.add(sky.msFactors, 0, 0.0, 2.0).name('MS Rayleigh').onChange(v => sky.setMultiScattering(v, sky.msFactors[1]));
|
||||
artFolder.add(sky.msFactors, 1, 0.0, 2.0).name('MS Mie').onChange(v => sky.setMultiScattering(sky.msFactors[0], v));
|
||||
artFolder.add(sky.msFactors, 2, 0.0, 1.0).name('Horizon Glow').onChange(v => sky.setHorizonGlow(v));
|
||||
artFolder.add(sky, 'contrast', 0.1, 2.0).onChange(v => sky.setContrast(v));
|
||||
|
||||
artFolder.addColor(sky, 'nightColor').onChange(v => sky.setNightColor(v));
|
||||
|
||||
const shmFolder = artFolder.addFolder('Shimmer');
|
||||
// Set Shimmer Strength default to 0.0
|
||||
sky.setShimmerControl(0.0, sky.shimmerControl[1], sky.shimmerControl[2]);
|
||||
|
||||
shmFolder.add(sky.shimmerControl, 0, 0.0, 0.1).name('Strength').onChange(v => sky.setShimmerControl(v, sky.shimmerControl[1], sky.shimmerControl[2]));
|
||||
shmFolder.add(sky.shimmerControl, 1, 1.0, 100.0).name('Frequency').onChange(v => sky.setShimmerControl(sky.shimmerControl[0], v, sky.shimmerControl[2]));
|
||||
shmFolder.add(sky.shimmerControl, 2, 0.01, 0.5).name('Mask Height').onChange(v => sky.setShimmerControl(sky.shimmerControl[0], sky.shimmerControl[1], v));
|
||||
|
||||
const cloudFolder = gui.addFolder('Clouds');
|
||||
const cParams = {
|
||||
volumetrics: sky.cloudControl2[1] > 0.5,
|
||||
coverage: 0.4,
|
||||
density: 0.02,
|
||||
height: sky.cloudControl[2],
|
||||
speed: 50.0,
|
||||
evolution: 0.02
|
||||
};
|
||||
// Apply Cloud Defaults
|
||||
sky.setCloudControl(0.4, 0.02, cParams.height, 50.0);
|
||||
sky.setCloudShapeEvolution(0.02);
|
||||
|
||||
cloudFolder.add(cParams, 'volumetrics').onChange(v => sky.setCloudVolumetricLighting(v));
|
||||
cloudFolder.add(cParams, 'coverage', 0.0, 1.0).onChange(v => sky.setCloudControl(v, cParams.density, cParams.height, cParams.speed));
|
||||
cloudFolder.add(cParams, 'density', 0.0, 1.0).onChange(v => sky.setCloudControl(cParams.coverage, v, cParams.height, cParams.speed));
|
||||
cloudFolder.add(cParams, 'height', 2000.0, 20000.0).onChange(v => sky.setCloudControl(cParams.coverage, cParams.density, v, cParams.speed));
|
||||
// Reverse speed calc: w = speed * (0.05 / 72.0)
|
||||
cloudFolder.add(cParams, 'speed', 0.0, 200.0).onChange(v => sky.setCloudControl(cParams.coverage, cParams.density, cParams.height, v));
|
||||
cloudFolder.add(cParams, 'evolution', 0.0, 2.0).onChange(v => sky.setCloudShapeEvolution(v));
|
||||
|
||||
const waterFolder = gui.addFolder('Water');
|
||||
const wParams = {
|
||||
derivativeTrick: true,
|
||||
strength: 50.0,
|
||||
speed: 1.0,
|
||||
octaves: 4.0
|
||||
};
|
||||
// Initialize defaults
|
||||
sky.setWaterControl(50.0, 1.0, 1.0, 4.0); // 1.0 = Derivative Trick On, 4 octaves
|
||||
|
||||
const updateWater = () => {
|
||||
sky.setWaterControl(wParams.strength, wParams.speed, wParams.derivativeTrick ? 1.0 : 0.0, wParams.octaves);
|
||||
};
|
||||
|
||||
waterFolder.add(wParams, 'derivativeTrick').name('Derivative Trick').onChange(updateWater);
|
||||
waterFolder.add(wParams, 'strength', 10.0, 100.0).onChange(updateWater);
|
||||
waterFolder.add(wParams, 'speed', 0.0, 5.0).onChange(updateWater);
|
||||
waterFolder.add(wParams, 'octaves', 1, 8, 1).name('Octaves').onChange(updateWater);
|
||||
waterFolder.close();
|
||||
|
||||
const starFolder = gui.addFolder('Stars');
|
||||
const sParams = {
|
||||
enabled: true,
|
||||
density: 1.0
|
||||
};
|
||||
// Initialize defaults (Density 1.0, Enabled True)
|
||||
sky.setStarControl(1.0, true);
|
||||
|
||||
const updateStars = () => {
|
||||
sky.setStarControl(sParams.density, sParams.enabled);
|
||||
};
|
||||
|
||||
starFolder.add(sParams, 'enabled').name('Enabled').onChange(updateStars);
|
||||
starFolder.add(sParams, 'density', 0.0, 1.0).name('Density').onChange(updateStars);
|
||||
starFolder.close();
|
||||
|
||||
const camFolder = gui.addFolder('Camera');
|
||||
camFolder.add(this.params, 'focalLength', 8.0, 300.0).name('Focal Length').onChange(() => this.updateCameraProjection());
|
||||
camFolder.add(this.params, 'aperture', 1.4, 32.0).onChange(() => this.updateCameraExposure());
|
||||
camFolder.add(this.params, 'shutterSpeed', 1.0, 1000.0).onChange(() => this.updateCameraExposure());
|
||||
camFolder.add(this.params, 'iso', 50.0, 3200.0).onChange(() => this.updateCameraExposure());
|
||||
|
||||
const bloomFolder = camFolder.addFolder('Bloom');
|
||||
const bParams = {
|
||||
enabled: false,
|
||||
lensFlare: false
|
||||
};
|
||||
|
||||
const updateBloom = () => {
|
||||
this.view.setBloomOptions({
|
||||
enabled: bParams.enabled,
|
||||
lensFlare: bParams.lensFlare
|
||||
});
|
||||
};
|
||||
|
||||
bloomFolder.add(bParams, 'enabled').onChange(updateBloom);
|
||||
bloomFolder.add(bParams, 'lensFlare').onChange(updateBloom);
|
||||
bloomFolder.close();
|
||||
|
||||
// Collapse folders by default
|
||||
sunDisk.close();
|
||||
atmFolder.close();
|
||||
artFolder.close();
|
||||
// shmFolder is inside artFolder, so it's hidden, but we can close it too if we want
|
||||
shmFolder.close();
|
||||
cloudFolder.close();
|
||||
// camFolder left open? User didn't specify, but "Artistic, shimmer and clouds" + "Disk, Atmosphere" were requested.
|
||||
// So Camera might stay open or close. Let's keep Camera open for now as it wasn't listed.
|
||||
|
||||
// Initial sync
|
||||
updateSun();
|
||||
this.updateCameraExposure(); // This will trigger updateSunIntensity too
|
||||
}
|
||||
|
||||
initControls() {
|
||||
|
||||
// listeners only
|
||||
this.canvas.addEventListener('mousedown', e => {
|
||||
this.camState.dragging = true;
|
||||
this.camState.lastX = e.clientX;
|
||||
this.camState.lastY = e.clientY;
|
||||
});
|
||||
|
||||
window.addEventListener('mouseup', () => {
|
||||
this.camState.dragging = false;
|
||||
});
|
||||
|
||||
window.addEventListener('mousemove', e => {
|
||||
if (!this.camState.dragging) return;
|
||||
const dx = e.clientX - this.camState.lastX;
|
||||
const dy = e.clientY - this.camState.lastY;
|
||||
this.camState.lastX = e.clientX;
|
||||
this.camState.lastY = e.clientY;
|
||||
|
||||
const sensitivity = 0.005;
|
||||
this.camState.theta -= dx * sensitivity;
|
||||
this.camState.phi += dy * sensitivity;
|
||||
// Clamp pitch to avoid flip [ -PI/2, PI/2 ]
|
||||
this.camState.phi = Math.max(-Math.PI / 2 + 0.01, Math.min(Math.PI / 2 - 0.01, this.camState.phi));
|
||||
});
|
||||
|
||||
|
||||
}
|
||||
|
||||
render() {
|
||||
// Update Camera LookAt
|
||||
const r = 1.0;
|
||||
const theta = this.camState.theta;
|
||||
const phi = this.camState.phi;
|
||||
|
||||
// Convert spherical to cartesian
|
||||
// Y is UP. Z is Forward.
|
||||
// At phi=0, y=0. forward vector should correspond to theta.
|
||||
// Let's say theta=0 is -Z.
|
||||
const y = Math.sin(phi);
|
||||
const h = Math.cos(phi);
|
||||
const x = h * Math.sin(theta);
|
||||
const z = -h * Math.cos(theta);
|
||||
|
||||
const eye = [0, 0, 0];
|
||||
const center = [x, y, z];
|
||||
const up = [0, 1, 0];
|
||||
this.camera.lookAt(eye, center, up);
|
||||
|
||||
this.renderer.render(this.swapChain, this.view);
|
||||
window.requestAnimationFrame(this.render);
|
||||
}
|
||||
|
||||
resize() {
|
||||
const dpr = window.devicePixelRatio;
|
||||
const width = this.canvas.width = window.innerWidth * dpr;
|
||||
const height = this.canvas.height = window.innerHeight * dpr;
|
||||
this.view.setViewport([0, 0, width, height]);
|
||||
|
||||
const aspect = width / height;
|
||||
// near=0.1, far=5000.0
|
||||
this.camera.setLensProjection(this.params.focalLength, aspect, 0.1, 5000.0);
|
||||
}
|
||||
}
|
||||
@@ -1,909 +0,0 @@
|
||||
material {
|
||||
name : SimulatedSkybox,
|
||||
parameters : [
|
||||
{
|
||||
type : float3,
|
||||
name : sunDirection
|
||||
},
|
||||
{
|
||||
type : float3,
|
||||
name : sunDirection2
|
||||
},
|
||||
{
|
||||
type : float3,
|
||||
name : depthR,
|
||||
precision : high
|
||||
},
|
||||
{
|
||||
type : float3,
|
||||
name : depthM,
|
||||
precision : high
|
||||
},
|
||||
{
|
||||
type : float2,
|
||||
name : miePhaseParams, // x=(1+g^2), y=(-2*g)
|
||||
precision : high
|
||||
},
|
||||
{
|
||||
type : float,
|
||||
name : sunIntensity,
|
||||
precision : high
|
||||
},
|
||||
{
|
||||
type : float,
|
||||
name : contrast,
|
||||
precision : high
|
||||
},
|
||||
{
|
||||
type : float3,
|
||||
name : nightColor,
|
||||
precision : high
|
||||
},
|
||||
{
|
||||
type : float3,
|
||||
name : ozone,
|
||||
precision : high
|
||||
},
|
||||
{
|
||||
type : float4,
|
||||
name : multiScatParams, // xyz=MultiScatteringColor, w=HorizonGlow
|
||||
precision : high
|
||||
},
|
||||
{
|
||||
type : float4,
|
||||
name : sunHalo, // x=Size, y=Limb, z=Intensity, w=Enabled
|
||||
precision : high
|
||||
},
|
||||
{
|
||||
type : float4,
|
||||
name : shimmerControl, // x=Strength, y=Frequency, z=MaskHeight, w=PlanetRadius
|
||||
precision : high
|
||||
},
|
||||
{
|
||||
type : float4,
|
||||
name : cloudControl, // x=Coverage, y=Density, z=QuadraticConst, w=WindSpeed
|
||||
precision : high
|
||||
},
|
||||
{
|
||||
type : float4,
|
||||
name : cloudControl2, // x=EvolutionSpeed
|
||||
precision : high
|
||||
},
|
||||
{
|
||||
type : float,
|
||||
name : sunIntensity2,
|
||||
precision : high
|
||||
},
|
||||
{
|
||||
type : float4,
|
||||
name : sunHalo2, // x=Size, y=Limb, z=Intensity, w=Enabled
|
||||
precision : high
|
||||
},
|
||||
{
|
||||
type : float4,
|
||||
name : waterControl, // x=Strength, y=Speed, z=DerivativeTrick, w=Octaves
|
||||
precision : high
|
||||
},
|
||||
{
|
||||
type : float2,
|
||||
name : starControl, // x=Density, y=Enabled
|
||||
precision : high
|
||||
}
|
||||
],
|
||||
variables : [
|
||||
eyeDirection
|
||||
],
|
||||
vertexDomain : device,
|
||||
depthWrite : false,
|
||||
shadingModel : unlit,
|
||||
culling: none
|
||||
}
|
||||
|
||||
vertex {
|
||||
void materialVertex(inout MaterialVertexInputs material) {
|
||||
// This code is taken from computeWorldPosition and assumes the vertex domain is 'device'.
|
||||
highp vec4 p = getPosition();
|
||||
// GL convention to inverted DX convention
|
||||
p.z = p.z * -0.5 + 0.5;
|
||||
highp vec4 worldPosition = getWorldFromClipMatrix() * p;
|
||||
// Getting the true world position would require dividing by w, but since this is a skybox
|
||||
// at inifinity, this results in very large numbers for material.eyeDirection.
|
||||
// Since the eyeDirection is only used as a direction vector in the fragment shader, we can
|
||||
// skip that step to improve precision.
|
||||
material.eyeDirection.xyz = worldPosition.xyz;
|
||||
}
|
||||
}
|
||||
|
||||
fragment {
|
||||
// ------------------------------------------------------------------------
|
||||
// Analytic Rayleigh and Mie Scattering (Physics Based)
|
||||
// Derived from:
|
||||
// - Hoffman & Preetham (2002): "Real-time Light-Atmosphere Interactions"
|
||||
// - Henyey & Greenstein (1941): "Diffuse radiation in the galaxy" (Mie Phase)
|
||||
// - Kasten & Young (1989): "Revised optical air mass tables" (Air Mass)
|
||||
// - "Simulated Sky" / Three.js (Sky.js): Empirical adjustments for aesthetics
|
||||
// ------------------------------------------------------------------------
|
||||
|
||||
#define PI 3.14159265359
|
||||
|
||||
void dummy() {} // squash editor syntax highlighting bugs
|
||||
|
||||
// Rayleigh Phase Function: Scattering distribution for small particles (air molecules)
|
||||
// Lord Rayleigh (1871)
|
||||
// Normalized to integrate to 4*PI (Boosting brightness by factor PI vs standard 1-normalization)
|
||||
highp float rayleighPhase(highp float cosTheta) {
|
||||
const highp float THREE_SIXTEENTH = (3.0 / 16.0);
|
||||
return THREE_SIXTEENTH * (1.0 + cosTheta * cosTheta);
|
||||
}
|
||||
|
||||
// Henyey-Greenstein Phase Function (Mie)
|
||||
// Henyey & Greenstein (1941)
|
||||
// Controls the forward scattering peak (sun halo) via anisotropy parameter 'g'
|
||||
// Optimized: params.x = (1 + g^2), params.y = (-2 * g)
|
||||
highp float hgPhase(highp float cosTheta, highp vec2 params) {
|
||||
const highp float ONE_FOURTH = (1.0 / 4.0);
|
||||
// Recover (1 - g^2) => 2.0 - (1 + g^2)
|
||||
highp float oneMinusG2 = 2.0 - params.x;
|
||||
highp float inverse = 1.0 / pow(params.x + params.y * cosTheta, 1.5);
|
||||
return ONE_FOURTH * (oneMinusG2 * inverse);
|
||||
}
|
||||
|
||||
// --- Noise Functions for Clouds ---
|
||||
highp float hash13(highp vec3 p3) {
|
||||
p3 = fract(p3 * .1031);
|
||||
p3 += dot(p3, p3.yzx + 33.33);
|
||||
return fract((p3.x + p3.y) * p3.z);
|
||||
}
|
||||
|
||||
highp float noise(highp vec3 p) {
|
||||
highp vec3 i = floor(p);
|
||||
highp vec3 f = fract(p);
|
||||
// Cubic Hermite Interpolation
|
||||
highp vec3 u = f*f*(3.0-2.0*f);
|
||||
return mix(mix(mix(hash13(i + vec3(0,0,0)), hash13(i + vec3(1,0,0)), u.x),
|
||||
mix(hash13(i + vec3(0,1,0)), hash13(i + vec3(1,1,0)), u.x), u.y),
|
||||
mix(mix(hash13(i + vec3(0,0,1)), hash13(i + vec3(1,0,1)), u.x),
|
||||
mix(hash13(i + vec3(0,1,1)), hash13(i + vec3(1,1,1)), u.x), u.y), u.z);
|
||||
}
|
||||
|
||||
// Fractal Brownian Motion (4 Octaves)
|
||||
highp float fbm(highp vec3 p) {
|
||||
highp float total = 0.0;
|
||||
highp float amplitude = 0.5;
|
||||
for (int i = 0; i < 4; i++) {
|
||||
total += noise(p) * amplitude;
|
||||
p *= 2.02; // Lacunarity
|
||||
p += 100.0; // Shift to avoid artifacts
|
||||
amplitude *= 0.5; // Gain
|
||||
}
|
||||
return total;
|
||||
}
|
||||
|
||||
highp float fbm(highp vec3 p, int octaves) {
|
||||
highp float total = 0.0;
|
||||
highp float amplitude = 0.5;
|
||||
for (int i = 0; i < 8; i++) {
|
||||
if (i >= octaves) break;
|
||||
total += noise(p) * amplitude;
|
||||
p *= 2.02;
|
||||
p += 100.0;
|
||||
amplitude *= 0.5;
|
||||
}
|
||||
return total;
|
||||
}
|
||||
|
||||
// Ray-Sphere Intersection
|
||||
// Returns distance to intersection or -1.0 if none.
|
||||
// Re = Planet Radius.
|
||||
// C = Re^2 - (Re + height)^2 (Precalculated on CPU for precision).
|
||||
highp float raySphereIntersect(highp vec3 rd, highp float Re, highp float C) {
|
||||
// Ray Origin is (0, Re, 0) relative to Planet Center (0, 0, 0)
|
||||
// We solve |(0, Re, 0) + t*rd|^2 = Rm^2
|
||||
// |O + tD|^2 = R^2
|
||||
// t^2 + 2t(O.D) + (O^2 - R^2) = 0
|
||||
// a=1, b=2(O.D), c = O^2 - R^2 = C
|
||||
// Reduced quadratic: t = -b' +/- sqrt(b'^2 - c) where b' = O.D
|
||||
|
||||
highp float b = Re * rd.y; // dot(vec3(0, Re, 0), rd)
|
||||
highp float disc = b*b - C;
|
||||
|
||||
if (disc < 0.0) return -1.0;
|
||||
|
||||
// t = -b + sqrt(disc)
|
||||
return -b + sqrt(disc);
|
||||
}
|
||||
|
||||
// ------------------------------------------------------------------------
|
||||
// Atmospheric Heat Shimmer (Mirage)
|
||||
// ------------------------------------------------------------------------
|
||||
// Simulates heat convection turbulence near the horizon (e.g., hot desert road effect).
|
||||
//
|
||||
// PHYSICS:
|
||||
// Heat rising from the ground creates pockets of varying air density (refractive index).
|
||||
// This bends light rays, causing a visual "shimmer" or displacement.
|
||||
//
|
||||
// IMPLEMENTATION:
|
||||
// - Perturbs the view vector `V.y` using interleaved sine waves.
|
||||
// - Uses World Space `V` so the noise is stable under camera rotation.
|
||||
// - Masked to only affect the horizon line.
|
||||
//
|
||||
// PARAMETERS:
|
||||
// @param V Normalized World View Vector (modified in place).
|
||||
// @param strength Max vertical displacement amplitude. (e.g. 0.002).
|
||||
// @param freq Ripple frequency/density. (e.g. 20.0).
|
||||
// @param maskHeight Horizon mask height (0.0 to 1.0). (e.g. 0.1).
|
||||
// ------------------------------------------------------------------------
|
||||
float applyHeatShimmer(inout highp vec3 V, highp float strength, highp float freq, highp float maskHeight) {
|
||||
if (strength <= 0.0) return 0.0;
|
||||
|
||||
// Mask: Strongest at horizon (0.0), fades out by maskHeight.
|
||||
highp float mask = 1.0 - smoothstep(0.0, maskHeight, abs(V.y));
|
||||
|
||||
if (mask > 0.0) {
|
||||
// Use FBM for organic turbulence (rising heat waves)
|
||||
highp float time = getUserTime().x;
|
||||
// Animate upward (y) and slightly drift (x)
|
||||
highp vec3 p = vec3(V.x * freq, V.y * freq + time * 2.0, time * 0.5);
|
||||
|
||||
// We use a cheap noise or FBM. Since we have FBM:
|
||||
// Use fewer octaves for performance if possible, but 4 is fine.
|
||||
highp float distortion = fbm(p);
|
||||
|
||||
// Remap noise from [0, 1] to [-1, 1] for perturbation
|
||||
distortion = distortion * 2.0 - 1.0;
|
||||
|
||||
// Apply vertical perturbation
|
||||
V.y += distortion * strength * mask * 0.1;
|
||||
V = normalize(V);
|
||||
}
|
||||
return mask;
|
||||
}
|
||||
|
||||
// ------------------------------------------------------------------------
|
||||
// Analytic Sky Model (Rayleigh + Mie + Ozone)
|
||||
// ------------------------------------------------------------------------
|
||||
// Computes the scattering and transmittance of the atmosphere along the view ray.
|
||||
//
|
||||
// PHYSICS:
|
||||
// - Rayleigh: Scattering by air molecules (Blue sky). High frequency (lambda^-4).
|
||||
// - Mie: Scattering by aerosols/dust (White haze). Low frequency (lambda^-1.3).
|
||||
// - Ozone: Absorption layer (Pink sunset). Absorbs green light.
|
||||
// - Optical Mass: Approximation of path length through spherical atmosphere.
|
||||
//
|
||||
// OUTPUTS:
|
||||
// @param V Normalized View Vector.
|
||||
// @param L Normalized Sun Vector.
|
||||
// @param sunIntensity Sun Illuminance (Lux).
|
||||
// @param depthR Rayleigh Optical Depth (Precalculated).
|
||||
// @param depthM Mie Optical Depth (Precalculated).
|
||||
// @param ozone Ozone Absorption (Precalculated).
|
||||
// @param msFactors Multi-Scattering factors (Rayleigh, Mie, Glow).
|
||||
// @param mieG Mie Phase Anisotropy.
|
||||
// @param outTransmittance Output: Atmospheric Transmittance (0..1) along V.
|
||||
// @return Output: In-Scattered Radiance (The sky color).
|
||||
// ------------------------------------------------------------------------
|
||||
highp vec3 getAtmosphere(highp vec3 V, highp vec3 L, highp float sunIntensity,
|
||||
highp vec3 depthR, highp vec3 depthM, highp vec3 ozone,
|
||||
highp vec4 multiScatParams, highp vec2 mieParams,
|
||||
out highp vec3 outTransmittance) {
|
||||
|
||||
highp float cosTheta = dot(V, L);
|
||||
|
||||
// 1. Phase Functions
|
||||
// "Golden Hour" Hack (Three.js Sky.js):
|
||||
// Remapping cosTheta from [-1, 1] to [0, 1] breaks the symmetry of Rayleigh scattering.
|
||||
highp float rPhase = rayleighPhase(cosTheta * 0.5 + 0.5);
|
||||
highp float mPhase = hgPhase(cosTheta, mieParams);
|
||||
|
||||
// 2. Optical Depth (Air Mass)
|
||||
// Kasten and Young (1989) - Relative Air Mass Model
|
||||
highp float zenithCos = clamp(V.y, 0.0, 1.0);
|
||||
highp float zenithAngle = acos(zenithCos);
|
||||
highp float zenithAngleDeg = zenithAngle * (180.0 / PI);
|
||||
highp float opticalMass = 1.0 / (zenithCos + 0.15 * pow(93.885 - zenithAngleDeg, -1.253));
|
||||
|
||||
// 3. Extinction & Transmittance
|
||||
highp vec3 totalExtinction = depthR + depthM + ozone;
|
||||
highp vec3 extinction = totalExtinction * opticalMass;
|
||||
outTransmittance = exp(-extinction);
|
||||
|
||||
// 4. In-Scattering
|
||||
// Approximate Multi-Scattering (Isotropic Fill) precomputed in C++.
|
||||
highp vec3 multiScattering = multiScatParams.xyz;
|
||||
|
||||
highp vec3 scatteringTerm = (depthR * rPhase) + (depthM * mPhase) + multiScattering;
|
||||
highp vec3 extinctionTerm = max(vec3(1e-6), totalExtinction);
|
||||
|
||||
// Equilibrium Radiance (Source Function)
|
||||
highp vec3 inScattering = sunIntensity * (scatteringTerm / extinctionTerm);
|
||||
|
||||
// Single-Scattering Integral: L = L_inf * (1 - exp(-opticalDepth))
|
||||
highp vec3 sunLight = inScattering * (1.0 - outTransmittance);
|
||||
|
||||
// 5. Horizon "Glow" Mix (Artistic Hack)
|
||||
// multiScatParams.w contains the Horizon Glow Strength
|
||||
// Uses Sun Elevation (L.y) to only activate during golden hour/twilight.
|
||||
mediump float horizonMix = saturate(pow(1.0 - L.y, 5.0)) * multiScatParams.w;
|
||||
highp vec3 horizonGlow = sqrt(inScattering * outTransmittance);
|
||||
sunLight *= mix(vec3(1.0), horizonGlow, horizonMix);
|
||||
|
||||
return sunLight;
|
||||
}
|
||||
|
||||
// ------------------------------------------------------------------------
|
||||
// Secondary Sun Scattering (Simplified)
|
||||
// ------------------------------------------------------------------------
|
||||
// Computes in-scattering for a second light source, reusing precomputed optical depths.
|
||||
// Skips multi-scattering (ambient) for performance, providing only direct beams/glow.
|
||||
//
|
||||
// @param V Normalized View Vector.
|
||||
// @param L Normalized Sun Vector.
|
||||
// @param sunIntensity Sun Illuminance.
|
||||
// @param depthR Rayleigh Optical Depth.
|
||||
// @param depthM Mie Optical Depth.
|
||||
// @param ozone Ozone Absorption.
|
||||
// @param mieParams Mie Phase Params.
|
||||
// @param transmittance Precomputed Atmospheric Transmittance.
|
||||
// @return In-Scattered Radiance.
|
||||
// ------------------------------------------------------------------------
|
||||
highp vec3 getSecondarySunScattering(highp vec3 V, highp vec3 L, highp float sunIntensity,
|
||||
highp vec3 depthR, highp vec3 depthM, highp vec3 ozone,
|
||||
highp vec2 miePhaseParams, highp vec3 transmittance) {
|
||||
highp float cosTheta = dot(V, L);
|
||||
|
||||
// Phase Functions
|
||||
highp float rPhase = rayleighPhase(cosTheta * 0.5 + 0.5);
|
||||
highp float mPhase = hgPhase(cosTheta, miePhaseParams);
|
||||
|
||||
// Scattering
|
||||
highp vec3 scatteringTerm = (depthR * rPhase) + (depthM * mPhase);
|
||||
highp vec3 totalExtinction = depthR + depthM + ozone;
|
||||
highp vec3 extinctionTerm = max(vec3(1e-6), totalExtinction);
|
||||
|
||||
highp vec3 inScattering = sunIntensity * (scatteringTerm / extinctionTerm);
|
||||
return inScattering * (1.0 - transmittance);
|
||||
}
|
||||
|
||||
// ------------------------------------------------------------------------
|
||||
// Physical Sun Disk
|
||||
// ------------------------------------------------------------------------
|
||||
// Renders the Solar Photosphere with limb darkening.
|
||||
//
|
||||
// PHYSICS:
|
||||
// - The sun is not a point light; it has an angular size (~0.53 deg).
|
||||
// - Limb Darkening: The sun is darker at the edges (limbs) because we see cooler outer layers.
|
||||
// - Drawn "Behind" the atmosphere, so it is attenuated by Transmittance.
|
||||
//
|
||||
// PARAMETERS:
|
||||
// @param V Normalized View Vector.
|
||||
// @param L Normalized Sun Vector.
|
||||
// @param sunParams x=CosRadius, y=LimbDarkening, z=IntensityBoost, w=Enabled.
|
||||
// @param sunIntensity Peak Sun Illuminance (Lux).
|
||||
// @param transmittance Atmospheric Transmittance (0..1).
|
||||
// @return Radiance of the sun disk (if visible and enabled).
|
||||
// ------------------------------------------------------------------------
|
||||
highp vec3 getSunDisk(highp vec3 V, highp vec3 L, highp vec4 sunParams,
|
||||
highp float sunIntensity, highp vec3 transmittance) {
|
||||
|
||||
highp float sunCosRadius = sunParams.x;
|
||||
highp float limbDarkening = sunParams.y;
|
||||
highp float sunDiskIntensity = sunParams.z;
|
||||
bool sunEnabled = sunParams.w > 0.5;
|
||||
|
||||
highp float cosTheta = dot(V, L);
|
||||
|
||||
// Robust edge detection for small angles using (1 - cos)
|
||||
highp float dist = 1.0 - cosTheta;
|
||||
highp float diskRadius = max(1e-6, 1.0 - sunCosRadius);
|
||||
|
||||
// AA Edge: smoothstep from radius to radius+epsilon
|
||||
// We invert it because we want 1.0 inside (dist < radius)
|
||||
highp float sunDiskProfile = 1.0 - smoothstep(diskRadius, diskRadius + 0.00002, dist);
|
||||
|
||||
if (sunEnabled && sunDiskProfile > 0.0) {
|
||||
// Limb Darkening approximation: mu = sqrt(1 - (r/R)^2)
|
||||
// dist/diskRadius is approx (r/R)^2 for small angles
|
||||
highp float relativeDist = min(1.0, dist / diskRadius);
|
||||
highp float mu = sqrt(1.0 - relativeDist);
|
||||
|
||||
// Avoid pow(0, 0) which causes NaNs
|
||||
highp float limbFactor = (limbDarkening < 1e-4) ? 1.0 : pow(mu, limbDarkening);
|
||||
|
||||
// Direct Sun Light (Radiance)
|
||||
// SunIntensity * Transmittance -> Physical Sun Color
|
||||
// SunDiskIntensity -> Artistic Boost to punch through Mie halo
|
||||
return sunIntensity * transmittance * limbFactor * sunDiskIntensity * sunDiskProfile;
|
||||
}
|
||||
|
||||
return vec3(0.0);
|
||||
}
|
||||
|
||||
// ------------------------------------------------------------------------
|
||||
// Procedural Cirrus Clouds
|
||||
// ------------------------------------------------------------------------
|
||||
// Renders a thin layer of high-altitude clouds (Cirrus) using 3D Noise.
|
||||
//
|
||||
// IMPLEMENTATION:
|
||||
// - Modeled as a spherical shell at a specific altitude.
|
||||
// - Ray-Sphere intersection determines UV layout and distance.
|
||||
// - Animated using 3D FBM (Fractal Brownian Motion) for shape evolution + Wind drift.
|
||||
// - Lighting includes Silver Lining (HG Phase) and Atmospheric Extinction.
|
||||
//
|
||||
// PARAMETERS:
|
||||
// @param background Current Sky Color (to be blended with).
|
||||
// @param V Normalized View Vector.
|
||||
// @param L Normalized Sun Vector.
|
||||
// @param control x=Coverage, y=Density, z=QuadraticConst(C), w=WindSpeed.
|
||||
// @param control2 x=EvolutionSpeed.
|
||||
// @param geometry w=PlanetRadius (Re).
|
||||
// @param sunIntensity Sun Illuminance.
|
||||
// @param transmittance Atmospheric Transmittance (Cloud Color Tint).
|
||||
// @return Sky color composed with clouds.
|
||||
// ------------------------------------------------------------------------
|
||||
// ------------------------------------------------------------------------
|
||||
// Procedural Cirrus Clouds
|
||||
// ------------------------------------------------------------------------
|
||||
// Renders a thin layer of high-altitude clouds (Cirrus) using 3D Noise.
|
||||
//
|
||||
// PARAMETERS:
|
||||
// @param V .
|
||||
// @param L .
|
||||
// @param control .
|
||||
// @param control2 .
|
||||
// @param geometry .
|
||||
// @param sunIntensity .
|
||||
// @param transmittance.
|
||||
// @param outDensity Output: Cloud Density (0..1).
|
||||
// @return Cloud Lit Color (pre-multiplied by density? No, just lit color).
|
||||
// ------------------------------------------------------------------------
|
||||
highp vec3 getCloudLayer(highp vec3 V, highp vec3 L,
|
||||
highp vec4 control, highp vec4 control2, highp vec4 geometry,
|
||||
highp float sunIntensity, highp vec3 transmittance,
|
||||
out highp float outDensity) {
|
||||
|
||||
outDensity = 0.0;
|
||||
highp float cloudCoverage = control.x;
|
||||
|
||||
// Clip clouds below the horizon (Earth occlusion)
|
||||
// Simple check V.y > 0.0 is sufficient for skybox provided camera is near ground.
|
||||
if (cloudCoverage > 0.0 && V.y > 0.0) {
|
||||
highp float Re = geometry.w;
|
||||
highp float intersectC = control.z;
|
||||
highp float distToCloud = raySphereIntersect(V, Re, intersectC);
|
||||
|
||||
if (distToCloud > 0.0) {
|
||||
highp vec3 p = V * distToCloud;
|
||||
highp float speed = control.w;
|
||||
highp float morphSpeed = control2.x;
|
||||
highp float time = getUserTime().x;
|
||||
|
||||
// UV Mapping (Planar projected onto sphere cap is sufficient for skybox)
|
||||
// Scale factor 0.05 km^-1
|
||||
highp vec2 uv = (p.xz * 0.05) + vec2(time * speed * 2.0, 0.0);
|
||||
|
||||
// 3D Noise for Morphing
|
||||
highp float noiseVal = fbm(vec3(uv, time * morphSpeed));
|
||||
|
||||
// Remap noise based on coverage.
|
||||
// Coverage 0.5 -> threshold 0.5. Coverage 1.0 -> threshold 0.0.
|
||||
highp float threshold = 1.0 - cloudCoverage;
|
||||
highp float cloudDensity = smoothstep(threshold, threshold + 0.3, noiseVal);
|
||||
|
||||
if (cloudDensity > 0.0) {
|
||||
cloudDensity *= control.y; // Global Density Scalar
|
||||
cloudDensity = clamp(cloudDensity, 0.0, 1.0);
|
||||
outDensity = cloudDensity;
|
||||
|
||||
// Cloud Lighting
|
||||
// Silver Lining: Strong forward scattering (Fixed g=0.9 for clouds)
|
||||
highp float cosTheta = dot(V, L);
|
||||
// We need separate params for cloud silver lining (g=0.9).
|
||||
// 1 + 0.9^2 = 1.81. -2*0.9 = -1.8.
|
||||
|
||||
// Attenuation (Beer's Law)
|
||||
// Thick clouds block light.
|
||||
// 20.0 is an artistic extinction coefficient.
|
||||
highp float extinction = exp(-cloudDensity * 20.0);
|
||||
|
||||
highp float silver = hgPhase(cosTheta, vec2(1.81, -1.8)) * 40.0 * extinction;
|
||||
|
||||
// Ambient/Diffuse term.
|
||||
// We allow some ambient light to pass through even thick clouds (0.05 min)
|
||||
// so they don't look like black holes.
|
||||
highp float ambient = 0.1 + 0.4 * extinction;
|
||||
|
||||
// Diffuse term (Sun Color) + Silver Lining
|
||||
highp vec3 cloudLight = sunIntensity * transmittance * (ambient + silver);
|
||||
|
||||
// Mix based on density
|
||||
highp float volumetric = control2.y;
|
||||
highp float shading = 1.0;
|
||||
|
||||
if (volumetric > 0.5) {
|
||||
// Gradient Lighting (Fake Volumetric Bump)
|
||||
highp float gradX = dFdx(cloudDensity);
|
||||
highp float gradY = dFdy(cloudDensity);
|
||||
// Smaller Z = Steeper Bumps.
|
||||
// dFdx(density) is typically small (e.g. 0.001).
|
||||
// We want Normal to have significant X/Y component.
|
||||
highp vec3 N = normalize(vec3(-gradX, -gradY, 0.001));
|
||||
|
||||
// Screen Space Sun Direction
|
||||
highp vec3 sRight = normalize(dFdx(V));
|
||||
highp vec3 sUp = normalize(dFdy(V));
|
||||
highp vec3 L_screen = vec3(dot(L, sRight), dot(L, sUp), 0.5);
|
||||
L_screen = normalize(L_screen);
|
||||
|
||||
shading = dot(N, L_screen);
|
||||
// Increase contrast: Darker shadows
|
||||
// dot is [-1, 1]. Map to [0.3, 1.0]
|
||||
shading = mix(0.3, 1.0, shading * 0.5 + 0.5);
|
||||
|
||||
// Darken thick parts (Beer's Law approximation)
|
||||
// Aggressively darken center of clouds
|
||||
shading *= (1.0 - cloudDensity * 0.7);
|
||||
}
|
||||
|
||||
return cloudLight * shading;
|
||||
}
|
||||
}
|
||||
}
|
||||
return vec3(0.0);
|
||||
}
|
||||
|
||||
|
||||
// ------------------------------------------------------------------------
|
||||
// Dynamic Tone Mapping
|
||||
// ------------------------------------------------------------------------
|
||||
// Applies a contrast curve that varies with sun elevation.
|
||||
//
|
||||
// PROBLEM:
|
||||
// Default linear/gamma tone mapping can make sunsets look washing out.
|
||||
// Real eyes accept much higher dynamic range at twilight.
|
||||
//
|
||||
// SOLUTION:
|
||||
// - Zenith (Noon): Linear gamma (Exponent 1.0). Physically accurate.
|
||||
// - Horizon (Sunset): High contrast (Exponent > 1.0). Crushes shadows, boosts color.
|
||||
//
|
||||
// @param color Input HDR color.
|
||||
// @param L Normalized Sun Vector.
|
||||
// @param contrast Maximum contrast exponent (at horizon). e.g. 1.5.
|
||||
// @return Tone mapped color.
|
||||
// ------------------------------------------------------------------------
|
||||
highp vec3 applyDynamicToneMapping(highp vec3 color, highp vec3 L, highp float contrast) {
|
||||
float c = saturate(L.y);
|
||||
// Exponent blends from 'contrast' (at L.y=0) to 1.0 (at L.y=1)
|
||||
float exponent = mix(contrast, 1.0, sqrt(c));
|
||||
return pow(max(vec3(0.0), color), vec3(exponent));
|
||||
}
|
||||
|
||||
// ------------------------------------------------------------------------
|
||||
// Procedural Water Surface
|
||||
// ------------------------------------------------------------------------
|
||||
// Simulates an infinite ocean plane at y=0 using screen-space derivatives for normals.
|
||||
//
|
||||
// FEATURES:
|
||||
// - Projected grid for infinite surface.
|
||||
// - Screen-space wave normal reconstruction (no geometry required).
|
||||
// - Fresnel reflection of Atmosphere, Sun, and Clouds.
|
||||
// - Specular highlights (Blinn-Phong).
|
||||
//
|
||||
// PARAMETERS:
|
||||
// @param V Normalized View Vector.
|
||||
// @param L Normalized Sun Vector.
|
||||
// @param sunIntensity Sun Illuminance.
|
||||
// @param depthR Rayleigh Optical Depth.
|
||||
// @param depthM Mie Optical Depth.
|
||||
// @param ozone Ozone Absorption.
|
||||
// @param multiScatParams Multi-Scattering Params.
|
||||
// @param miePhaseParams Mie Phase Params.
|
||||
// @param sunHalo Sun Halo Params.
|
||||
// @param cloudControl Cloud Control Params.
|
||||
// @param cloudControl2 Cloud Evolution Params.
|
||||
// @param shimmerControl Shimmer Control (w component used as PlanetRadius for clouds).
|
||||
// @param waterControl Water Control (x=Strength, y=Speed, z=DerivativeTrick).
|
||||
// @return Water surface color.
|
||||
// ------------------------------------------------------------------------
|
||||
// 3D Noise for Stars
|
||||
highp float hash31(highp vec3 p) {
|
||||
p = fract(p * 0.1031);
|
||||
p += dot(p, p.yzx + 33.33);
|
||||
return fract((p.x + p.y) * p.z);
|
||||
}
|
||||
|
||||
highp float getStars(highp vec3 V, highp float density) {
|
||||
// Simple procedural stars
|
||||
// We use view vector direction to tile the sky
|
||||
// Higher frequency = smaller stars
|
||||
highp float frequency = 300.0;
|
||||
highp vec3 p = floor(V * frequency);
|
||||
|
||||
highp float h = hash31(p);
|
||||
|
||||
// Threshold for stars (very sparse)
|
||||
// param density: 0.0 (none) to 1.0 (max)
|
||||
// Default threshold was 0.995 (0.5% stars)
|
||||
// We map density 0.0 -> 1.0 threshold (no stars)
|
||||
// density 1.0 -> 0.990 threshold (1.0% stars)
|
||||
highp float threshold = 1.0 - (0.001 + density * 0.009);
|
||||
|
||||
highp float star = 0.0;
|
||||
if (h > threshold) {
|
||||
// Random brightness
|
||||
highp float brightness = (h - threshold) / (1.0 - threshold);
|
||||
star = brightness * 15.0; // Reduced from 50.0 to 15.0
|
||||
}
|
||||
return star;
|
||||
}
|
||||
|
||||
// New helper to handle Star Compositing (Fade, Rotation, Occlusion)
|
||||
highp vec3 getStarLayer(highp vec3 V, highp vec3 L, highp float cloudDensity, highp vec3 transmittance, highp vec2 starControl) {
|
||||
// starControl.x = Density, .y = Enabled
|
||||
if (starControl.y < 0.5) return vec3(0.0);
|
||||
|
||||
// 1. Fade by Sun Elevation
|
||||
// Start appearing sooner (when sun is still slightly up), but stay dim.
|
||||
// 0.10 (5.7 deg up) -> 0.0
|
||||
// -0.20 (11.5 deg down) -> 1.0
|
||||
highp float starFade = 1.0 - smoothstep(-0.20, 0.10, L.y);
|
||||
starFade *= starFade;
|
||||
|
||||
if (starFade <= 0.0) return vec3(0.0);
|
||||
|
||||
// 2. Rotate to break grid alignment
|
||||
highp vec3 rotV = vec3(
|
||||
dot(V, vec3(0.6, 0.8, 0.0)),
|
||||
dot(V, vec3(-0.8, 0.6, 0.0)),
|
||||
V.z
|
||||
);
|
||||
|
||||
highp float starVal = getStars(rotV, starControl.x);
|
||||
if (starVal <= 0.0) return vec3(0.0);
|
||||
|
||||
// 3. Cloud Occlusion (Aggressive)
|
||||
highp float cloudOcclusion = 1.0 - smoothstep(0.0, 1.0, pow(cloudDensity, 0.1));
|
||||
|
||||
return vec3(starVal) * transmittance * starFade * cloudOcclusion * 0.1;
|
||||
}
|
||||
|
||||
// ------------------------------------------------------------------------
|
||||
// Procedural Water Surface
|
||||
// ------------------------------------------------------------------------
|
||||
// Simulates an infinite ocean plane at y=0 using screen-space derivatives for normals.
|
||||
//
|
||||
// FEATURES:
|
||||
// - Projected grid for infinite surface.
|
||||
// - Screen-space wave normal reconstruction (no geometry required).
|
||||
// - Fresnel reflection of Atmosphere, Sun, and Clouds.
|
||||
// - Specular highlights (Blinn-Phong).
|
||||
//
|
||||
// PARAMETERS:
|
||||
// @param V Normalized View Vector.
|
||||
// @param L Normalized Sun Vector.
|
||||
// @param sunIntensity Sun Illuminance.
|
||||
// @param depthR Rayleigh Optical Depth.
|
||||
// @param depthM Mie Optical Depth.
|
||||
// @param ozone Ozone Absorption.
|
||||
// @param multiScatParams Multi-Scattering Params.
|
||||
// @param miePhaseParams Mie Phase Params.
|
||||
// @param sunHalo Sun Halo Params.
|
||||
// @param cloudControl Cloud Control Params.
|
||||
// @param cloudControl2 Cloud Evolution Params.
|
||||
// @param shimmerControl Shimmer Control (w component used as PlanetRadius for clouds).
|
||||
// @param waterControl Water Control (x=Strength, y=Speed, z=DerivativeTrick).
|
||||
// @return Water surface color.
|
||||
// ------------------------------------------------------------------------
|
||||
highp vec3 getWaterColor(highp vec3 V, highp vec3 L,
|
||||
highp float sunIntensity,
|
||||
highp vec3 depthR, highp vec3 depthM, highp vec3 ozone,
|
||||
highp vec4 multiScatParams, highp vec2 miePhaseParams,
|
||||
highp vec4 sunHalo,
|
||||
highp vec4 cloudControl, highp vec4 cloudControl2,
|
||||
highp vec4 shimmerControl, highp vec4 waterControl) {
|
||||
|
||||
// Project to plane y=0
|
||||
highp float t = -10.0 / min(V.y, -0.0002); // Reduced clamp to minimize "wall" artifact
|
||||
highp vec2 uv = V.xz * t * 0.05;
|
||||
|
||||
highp float time = getUserTime().x;
|
||||
highp float speed = waterControl.y;
|
||||
uv += vec2(time * 0.5 * speed, time * 0.2 * speed);
|
||||
|
||||
// Wave Normal
|
||||
// Use screen-space derivatives to compute world-space normal perturbation
|
||||
// Wave Normal
|
||||
// Use screen-space derivatives to compute world-space normal perturbation
|
||||
int octaves = int(max(1.0, waterControl.w));
|
||||
highp float h = fbm(vec3(uv, time * 0.1 * speed), octaves);
|
||||
|
||||
// Reconstruct screen-space basis in world space
|
||||
// highp vec3 sRight = normalize(dFdx(V)); // Moved inside block
|
||||
// highp vec3 sUp = normalize(dFdy(V)); // Moved inside block
|
||||
|
||||
// Perturb normal based on height gradient
|
||||
// If h increases in screen-X direction, normal tilts against sRight.
|
||||
// Fade out perturbation near horizon (V.y -> 0) to reduce aliasing
|
||||
highp float horizonFade = smoothstep(0.0, 0.5, abs(V.y));
|
||||
highp float strength = waterControl.x;
|
||||
|
||||
highp vec3 N_perturb;
|
||||
|
||||
// Derivative Trick Toggle
|
||||
if (waterControl.z > 0.5) {
|
||||
// Screen-Space Derivatives (Fast, 1 tap)
|
||||
// Reconstruct screen-space basis in world space
|
||||
// If h increases in screen-X direction, normal tilts against sRight.
|
||||
highp vec3 sRight = normalize(dFdx(V));
|
||||
highp vec3 sUp = normalize(dFdy(V));
|
||||
N_perturb = (sRight * dFdx(h) + sUp * dFdy(h)) * strength * horizonFade;
|
||||
} else {
|
||||
// Finite Difference (Standard, 3 taps)
|
||||
// More expensive but analytically correct in world space (independent of view resolution/derivatives)
|
||||
float eps = 0.02; // Epsilon for gradient
|
||||
vec3 p = vec3(uv, time * 0.1 * speed);
|
||||
float hx = fbm(p + vec3(eps, 0.0, 0.0), octaves);
|
||||
float hy = fbm(p + vec3(0.0, eps, 0.0), octaves);
|
||||
|
||||
// Gradient
|
||||
float dx = (hx - h) / eps;
|
||||
float dy = (hy - h) / eps;
|
||||
|
||||
// Construct World Space Perturbation
|
||||
// Gradient (dx, dy) acts on XZ plane.
|
||||
// Normal = normalize(-dx, 1, -dy).
|
||||
// We want N_perturb to SUBTRACT from (0,1,0).
|
||||
// N_water = normalize(Up - Perturb).
|
||||
// So Perturb = (dx, 0, dy).
|
||||
// Note: Strength needs to be calibrated to match derivative trick roughly, or just raw.
|
||||
// Derivative trick Strength was ~50.0.
|
||||
// Here dx/dy are raw noise slopes.
|
||||
// Reduced to 0.002 to match visual range of derivative trick and prevent black artifacts.
|
||||
N_perturb = vec3(dx, 0.0, dy) * (strength * 0.002) * horizonFade;
|
||||
}
|
||||
|
||||
highp vec3 N_water = normalize(vec3(0.0, 1.0, 0.0) - N_perturb);
|
||||
|
||||
// Reflection
|
||||
highp vec3 R = reflect(V, N_water);
|
||||
|
||||
highp vec3 transRefl;
|
||||
highp vec3 reflection = getAtmosphere(R, L, sunIntensity,
|
||||
depthR, depthM,
|
||||
ozone, multiScatParams,
|
||||
miePhaseParams,
|
||||
transRefl);
|
||||
|
||||
// Clouds in reflection
|
||||
highp float reflCloudDensity;
|
||||
highp vec3 reflCloudLayer = getCloudLayer(R, L, materialParams.cloudControl, materialParams.cloudControl2,
|
||||
materialParams.shimmerControl, materialParams.sunIntensity, transRefl,
|
||||
reflCloudDensity);
|
||||
|
||||
// Add Stars to Reflection
|
||||
// Use helper with Reflection Vector and Reflection Cloud Density
|
||||
// Horizon Mask: Fade out star reflections that are deep in the water (high R.y)
|
||||
// Restricted to very close to horizon (0.0 to 0.1) as requested.
|
||||
highp float rHorizonMask = 1.0 - smoothstep(0.0, 0.1, R.y);
|
||||
|
||||
if (rHorizonMask > 0.0) {
|
||||
reflection += getStarLayer(R, L, reflCloudDensity, transRefl, materialParams.starControl) * rHorizonMask;
|
||||
}
|
||||
|
||||
// Add Sun Disk to reflection (Occluded)
|
||||
highp float reflSunAccess = 1.0 - smoothstep(0.0, 0.7, reflCloudDensity * 1.5);
|
||||
reflection += getSunDisk(R, L, sunHalo, sunIntensity, transRefl) * reflSunAccess;
|
||||
|
||||
// Apply clouds to reflection
|
||||
reflection = mix(reflection, reflCloudLayer, reflCloudDensity);
|
||||
|
||||
// Fresnel
|
||||
highp float F0 = 0.02; // Water
|
||||
highp float cosTheta = clamp(dot(-V, N_water), 0.0, 1.0);
|
||||
highp float F = F0 + (1.0 - F0) * pow(1.0 - cosTheta, 5.0);
|
||||
|
||||
highp vec3 deepColor = vec3(0.0, 0.005, 0.02); // Deep blue/black
|
||||
|
||||
highp vec3 waterColor = mix(deepColor, reflection, F);
|
||||
|
||||
return waterColor;
|
||||
}
|
||||
|
||||
|
||||
|
||||
void material(inout MaterialInputs material) {
|
||||
prepareMaterial(material);
|
||||
|
||||
highp vec3 V = normalize(variable_eyeDirection.xyz);
|
||||
highp vec3 L = normalize(materialParams.sunDirection);
|
||||
|
||||
// 1. Heat Shimmer
|
||||
// Fade out as sun rises (Strongest at horizon, zero at 30 degrees up)
|
||||
highp float sunFade = 1.0 - smoothstep(0.0, 0.5, abs(L.y));
|
||||
highp float shimmerIntensity = applyHeatShimmer(V, materialParams.shimmerControl.x * sunFade,
|
||||
materialParams.shimmerControl.y,
|
||||
materialParams.shimmerControl.z);
|
||||
|
||||
// 2. Atmospheric Scattering
|
||||
highp vec3 transmittance;
|
||||
highp vec3 inScatter1 = getAtmosphere(V, L, materialParams.sunIntensity,
|
||||
materialParams.depthR, materialParams.depthM,
|
||||
materialParams.ozone, materialParams.multiScatParams,
|
||||
materialParams.miePhaseParams,
|
||||
transmittance);
|
||||
|
||||
|
||||
// Sun 2 (Optional)
|
||||
// We reuse the same Transmittance (view dependent) and Phase params.
|
||||
// We do NOT add extra Multi-Scattering (Ambient) for the second sun to save cost/complexity.
|
||||
// It contributes Direct In-Scattering (Beams/Glow) only.
|
||||
highp vec3 inScatter2 = vec3(0.0);
|
||||
if (materialParams.sunHalo2.w > 0.5) {
|
||||
highp vec3 L2 = normalize(materialParams.sunDirection2);
|
||||
inScatter2 = getSecondarySunScattering(V, L2,
|
||||
materialParams.sunIntensity2,
|
||||
materialParams.depthR,
|
||||
materialParams.depthM,
|
||||
materialParams.ozone,
|
||||
materialParams.miePhaseParams,
|
||||
transmittance);
|
||||
}
|
||||
|
||||
highp vec3 finalColor = inScatter1 + inScatter2;
|
||||
|
||||
// 5. Procedural Clouds
|
||||
highp float cloudDensity;
|
||||
highp vec3 cloudLayer = getCloudLayer(V, L,
|
||||
materialParams.cloudControl,
|
||||
materialParams.cloudControl2,
|
||||
materialParams.shimmerControl, // reusing w=PlanetRadius
|
||||
materialParams.sunIntensity,
|
||||
transmittance,
|
||||
cloudDensity);
|
||||
|
||||
// Add Stars
|
||||
// Stars are at infinity.
|
||||
// Use helper function.
|
||||
finalColor += getStarLayer(V, L, cloudDensity, transmittance, materialParams.starControl);
|
||||
|
||||
// 3. Sun Disks - Occluded by clouds
|
||||
// Sun Access is (1.0 - cloudDensity) but arguably non-linear for sharp disk
|
||||
highp float sunAccess = 1.0 - smoothstep(0.0, 0.7, cloudDensity * 1.5);
|
||||
|
||||
finalColor += getSunDisk(V, L, materialParams.sunHalo,
|
||||
materialParams.sunIntensity, transmittance) * sunAccess;
|
||||
|
||||
if (materialParams.sunHalo2.w > 0.5) {
|
||||
highp vec3 L2 = normalize(materialParams.sunDirection2);
|
||||
// Note: Ideally we should compute cloud density for L2 direction if clouds are 3D...
|
||||
// But here we use V direction clouds (view-based).
|
||||
// Since clouds are in front of everything, this is correct for view-based occlusion.
|
||||
finalColor += getSunDisk(V, L2, materialParams.sunHalo2,
|
||||
materialParams.sunIntensity2, transmittance) * sunAccess;
|
||||
}
|
||||
|
||||
// 4. Night Sky Offset
|
||||
finalColor += materialParams.nightColor;
|
||||
|
||||
// 5. Apply Clouds
|
||||
finalColor = mix(finalColor, cloudLayer, cloudDensity);
|
||||
|
||||
// 6. Dynamic Tone Mapping
|
||||
finalColor = applyDynamicToneMapping(finalColor, L, materialParams.contrast);
|
||||
|
||||
if (V.y < 0.0) {
|
||||
finalColor = getWaterColor(V, L, materialParams.sunIntensity,
|
||||
materialParams.depthR, materialParams.depthM,
|
||||
materialParams.ozone, materialParams.multiScatParams,
|
||||
materialParams.miePhaseParams,
|
||||
materialParams.sunHalo,
|
||||
materialParams.cloudControl, materialParams.cloudControl2,
|
||||
materialParams.shimmerControl,
|
||||
materialParams.waterControl);
|
||||
finalColor = applyDynamicToneMapping(finalColor, L, materialParams.contrast);
|
||||
}
|
||||
|
||||
material.baseColor = vec4(finalColor, 1.0);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -1,9 +0,0 @@
|
||||
body {
|
||||
margin: 0;
|
||||
overflow: hidden;
|
||||
}
|
||||
canvas {
|
||||
touch-action: none;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
}
|
||||
@@ -28,6 +28,7 @@ set(SRCS
|
||||
src/BackendUtils.cpp
|
||||
src/BlobCacheKey.cpp
|
||||
src/Callable.cpp
|
||||
src/CallbackHandler.cpp
|
||||
src/CallbackManager.cpp
|
||||
src/CircularBuffer.cpp
|
||||
src/CommandBufferQueue.cpp
|
||||
|
||||
@@ -17,14 +17,6 @@
|
||||
#ifndef TNT_FILAMENT_BACKEND_CALLBACKHANDLER_H
|
||||
#define TNT_FILAMENT_BACKEND_CALLBACKHANDLER_H
|
||||
|
||||
#ifdef __clang__
|
||||
#pragma clang diagnostic push
|
||||
// Disable the weak-vtables warning because we need the destructor to be inlined in the headers. It
|
||||
// is not optimal (for build), but we have clients that compile their libraries (filament) and app
|
||||
// in different rtti settings.
|
||||
#pragma clang diagnostic ignored "-Wweak-vtables"
|
||||
#endif
|
||||
|
||||
namespace filament::backend {
|
||||
|
||||
/**
|
||||
@@ -72,13 +64,9 @@ public:
|
||||
virtual void post(void* user, Callback callback) = 0;
|
||||
|
||||
protected:
|
||||
virtual ~CallbackHandler() = default;
|
||||
virtual ~CallbackHandler();
|
||||
};
|
||||
|
||||
} // namespace filament::backend
|
||||
|
||||
#ifdef __clang__
|
||||
#pragma clang diagnostic pop
|
||||
#endif
|
||||
|
||||
#endif // TNT_FILAMENT_BACKEND_CALLBACKHANDLER_H
|
||||
|
||||
@@ -1009,99 +1009,98 @@ enum class CompressedPixelDataType : uint16_t {
|
||||
*
|
||||
* @see Texture
|
||||
*/
|
||||
// The [index] comments indicate the corresponding uint16_t values.
|
||||
enum class TextureFormat : uint16_t {
|
||||
// 8-bits per element
|
||||
R8, R8_SNORM, R8UI, R8I, STENCIL8, // [0 - 4]
|
||||
R8, R8_SNORM, R8UI, R8I, STENCIL8,
|
||||
|
||||
// 16-bits per element
|
||||
R16F, R16UI, R16I, // [5 - 7]
|
||||
RG8, RG8_SNORM, RG8UI, RG8I, // [8 - 11]
|
||||
RGB565, // [12]
|
||||
RGB9_E5, // 9995 is actually 32 bpp but it's here for historical reasons. [13]
|
||||
RGB5_A1, // [14]
|
||||
RGBA4, // [15]
|
||||
DEPTH16, // [16]
|
||||
R16F, R16UI, R16I,
|
||||
RG8, RG8_SNORM, RG8UI, RG8I,
|
||||
RGB565,
|
||||
RGB9_E5, // 9995 is actually 32 bpp but it's here for historical reasons.
|
||||
RGB5_A1,
|
||||
RGBA4,
|
||||
DEPTH16,
|
||||
|
||||
// 24-bits per element
|
||||
RGB8, SRGB8, RGB8_SNORM, RGB8UI, RGB8I, // [17 - 21]
|
||||
DEPTH24, // [22]
|
||||
RGB8, SRGB8, RGB8_SNORM, RGB8UI, RGB8I,
|
||||
DEPTH24,
|
||||
|
||||
// 32-bits per element
|
||||
R32F, R32UI, R32I, // [23 - 25]
|
||||
RG16F, RG16UI, RG16I, // [26 - 28]
|
||||
R11F_G11F_B10F, // [29]
|
||||
RGBA8, SRGB8_A8,RGBA8_SNORM, // [30 - 32]
|
||||
UNUSED, // used to be rgbm [33]
|
||||
RGB10_A2, RGBA8UI, RGBA8I, // [34 - 36]
|
||||
DEPTH32F, DEPTH24_STENCIL8, DEPTH32F_STENCIL8, // [37 - 39]
|
||||
R32F, R32UI, R32I,
|
||||
RG16F, RG16UI, RG16I,
|
||||
R11F_G11F_B10F,
|
||||
RGBA8, SRGB8_A8,RGBA8_SNORM,
|
||||
UNUSED, // used to be rgbm
|
||||
RGB10_A2, RGBA8UI, RGBA8I,
|
||||
DEPTH32F, DEPTH24_STENCIL8, DEPTH32F_STENCIL8,
|
||||
|
||||
// 48-bits per element
|
||||
RGB16F, RGB16UI, RGB16I, // [40 - 42]
|
||||
RGB16F, RGB16UI, RGB16I,
|
||||
|
||||
// 64-bits per element
|
||||
RG32F, RG32UI, RG32I, // [43 - 45]
|
||||
RGBA16F, RGBA16UI, RGBA16I, // [46 - 48]
|
||||
RG32F, RG32UI, RG32I,
|
||||
RGBA16F, RGBA16UI, RGBA16I,
|
||||
|
||||
// 96-bits per element
|
||||
RGB32F, RGB32UI, RGB32I, // [49 - 51]
|
||||
RGB32F, RGB32UI, RGB32I,
|
||||
|
||||
// 128-bits per element
|
||||
RGBA32F, RGBA32UI, RGBA32I, // [52 - 54]
|
||||
RGBA32F, RGBA32UI, RGBA32I,
|
||||
|
||||
// compressed formats
|
||||
|
||||
// Mandatory in GLES 3.0 and GL 4.3
|
||||
EAC_R11, EAC_R11_SIGNED, EAC_RG11, EAC_RG11_SIGNED, // [55 - 58]
|
||||
ETC2_RGB8, ETC2_SRGB8, // [59 - 60]
|
||||
ETC2_RGB8_A1, ETC2_SRGB8_A1, // [61 - 62]
|
||||
ETC2_EAC_RGBA8, ETC2_EAC_SRGBA8, // [63 - 64]
|
||||
EAC_R11, EAC_R11_SIGNED, EAC_RG11, EAC_RG11_SIGNED,
|
||||
ETC2_RGB8, ETC2_SRGB8,
|
||||
ETC2_RGB8_A1, ETC2_SRGB8_A1,
|
||||
ETC2_EAC_RGBA8, ETC2_EAC_SRGBA8,
|
||||
|
||||
// Available everywhere except Android/iOS
|
||||
DXT1_RGB, DXT1_RGBA, DXT3_RGBA, DXT5_RGBA, // [65 - 68]
|
||||
DXT1_SRGB, DXT1_SRGBA, DXT3_SRGBA, DXT5_SRGBA, // [69 - 72]
|
||||
DXT1_RGB, DXT1_RGBA, DXT3_RGBA, DXT5_RGBA,
|
||||
DXT1_SRGB, DXT1_SRGBA, DXT3_SRGBA, DXT5_SRGBA,
|
||||
|
||||
// ASTC formats are available with a GLES extension
|
||||
RGBA_ASTC_4x4, // [73]
|
||||
RGBA_ASTC_5x4, // [74]
|
||||
RGBA_ASTC_5x5, // [75]
|
||||
RGBA_ASTC_6x5, // [76]
|
||||
RGBA_ASTC_6x6, // [77]
|
||||
RGBA_ASTC_8x5, // [78]
|
||||
RGBA_ASTC_8x6, // [79]
|
||||
RGBA_ASTC_8x8, // [80]
|
||||
RGBA_ASTC_10x5, // [81]
|
||||
RGBA_ASTC_10x6, // [82]
|
||||
RGBA_ASTC_10x8, // [83]
|
||||
RGBA_ASTC_10x10, // [84]
|
||||
RGBA_ASTC_12x10, // [85]
|
||||
RGBA_ASTC_12x12, // [86]
|
||||
SRGB8_ALPHA8_ASTC_4x4, // [87]
|
||||
SRGB8_ALPHA8_ASTC_5x4, // [88]
|
||||
SRGB8_ALPHA8_ASTC_5x5, // [89]
|
||||
SRGB8_ALPHA8_ASTC_6x5, // [90]
|
||||
SRGB8_ALPHA8_ASTC_6x6, // [91]
|
||||
SRGB8_ALPHA8_ASTC_8x5, // [92]
|
||||
SRGB8_ALPHA8_ASTC_8x6, // [93]
|
||||
SRGB8_ALPHA8_ASTC_8x8, // [94]
|
||||
SRGB8_ALPHA8_ASTC_10x5, // [95]
|
||||
SRGB8_ALPHA8_ASTC_10x6, // [96]
|
||||
SRGB8_ALPHA8_ASTC_10x8, // [97]
|
||||
SRGB8_ALPHA8_ASTC_10x10, // [98]
|
||||
SRGB8_ALPHA8_ASTC_12x10, // [99]
|
||||
SRGB8_ALPHA8_ASTC_12x12, // [100]
|
||||
RGBA_ASTC_4x4,
|
||||
RGBA_ASTC_5x4,
|
||||
RGBA_ASTC_5x5,
|
||||
RGBA_ASTC_6x5,
|
||||
RGBA_ASTC_6x6,
|
||||
RGBA_ASTC_8x5,
|
||||
RGBA_ASTC_8x6,
|
||||
RGBA_ASTC_8x8,
|
||||
RGBA_ASTC_10x5,
|
||||
RGBA_ASTC_10x6,
|
||||
RGBA_ASTC_10x8,
|
||||
RGBA_ASTC_10x10,
|
||||
RGBA_ASTC_12x10,
|
||||
RGBA_ASTC_12x12,
|
||||
SRGB8_ALPHA8_ASTC_4x4,
|
||||
SRGB8_ALPHA8_ASTC_5x4,
|
||||
SRGB8_ALPHA8_ASTC_5x5,
|
||||
SRGB8_ALPHA8_ASTC_6x5,
|
||||
SRGB8_ALPHA8_ASTC_6x6,
|
||||
SRGB8_ALPHA8_ASTC_8x5,
|
||||
SRGB8_ALPHA8_ASTC_8x6,
|
||||
SRGB8_ALPHA8_ASTC_8x8,
|
||||
SRGB8_ALPHA8_ASTC_10x5,
|
||||
SRGB8_ALPHA8_ASTC_10x6,
|
||||
SRGB8_ALPHA8_ASTC_10x8,
|
||||
SRGB8_ALPHA8_ASTC_10x10,
|
||||
SRGB8_ALPHA8_ASTC_12x10,
|
||||
SRGB8_ALPHA8_ASTC_12x12,
|
||||
|
||||
// RGTC formats available with a GLES extension
|
||||
RED_RGTC1, // BC4 unsigned [101]
|
||||
SIGNED_RED_RGTC1, // BC4 signed [102]
|
||||
RED_GREEN_RGTC2, // BC5 unsigned [103]
|
||||
SIGNED_RED_GREEN_RGTC2, // BC5 signed [104]
|
||||
RED_RGTC1, // BC4 unsigned
|
||||
SIGNED_RED_RGTC1, // BC4 signed
|
||||
RED_GREEN_RGTC2, // BC5 unsigned
|
||||
SIGNED_RED_GREEN_RGTC2, // BC5 signed
|
||||
|
||||
// BPTC formats available with a GLES extension
|
||||
RGB_BPTC_SIGNED_FLOAT, // BC6H signed [105]
|
||||
RGB_BPTC_UNSIGNED_FLOAT,// BC6H unsigned [106]
|
||||
RGBA_BPTC_UNORM, // BC7 [107]
|
||||
SRGB_ALPHA_BPTC_UNORM, // BC7 sRGB [108]
|
||||
RGB_BPTC_SIGNED_FLOAT, // BC6H signed
|
||||
RGB_BPTC_UNSIGNED_FLOAT,// BC6H unsigned
|
||||
RGBA_BPTC_UNORM, // BC7
|
||||
SRGB_ALPHA_BPTC_UNORM, // BC7 sRGB
|
||||
};
|
||||
|
||||
TextureType getTextureType(TextureFormat format) noexcept;
|
||||
|
||||
@@ -31,10 +31,6 @@
|
||||
#include <memory>
|
||||
#include <mutex>
|
||||
|
||||
namespace utils {
|
||||
class FeatureFlagManager;
|
||||
}
|
||||
|
||||
namespace filament::backend {
|
||||
|
||||
class CallbackHandler;
|
||||
@@ -108,11 +104,6 @@ public:
|
||||
/** duration in nanosecond on the std::steady_clock */
|
||||
using duration_ns = int64_t;
|
||||
static constexpr time_point_ns INVALID = -1; //!< value not supported
|
||||
/**
|
||||
* The time delta [ns] between subsequent composition events.
|
||||
*/
|
||||
duration_ns compositeInterval;
|
||||
|
||||
/**
|
||||
* The timestamp [ns] since epoch of the next time the compositor will begin composition.
|
||||
* This is effectively the deadline for when the compositor must receive a newly queued
|
||||
@@ -120,6 +111,11 @@ public:
|
||||
*/
|
||||
time_point_ns compositeDeadline;
|
||||
|
||||
/**
|
||||
* The time delta [ns] between subsequent composition events.
|
||||
*/
|
||||
duration_ns compositeInterval;
|
||||
|
||||
/**
|
||||
* The time delta [ns] between the start of composition and the expected present time of
|
||||
* that composition. This can be used to estimate the latency of the actual present time.
|
||||
@@ -127,9 +123,22 @@ public:
|
||||
duration_ns compositeToPresentLatency;
|
||||
|
||||
/**
|
||||
* Expected latency [ns] of frame presentation relative to vsync.
|
||||
* The timestamp [ns] since epoch of the system's expected presentation time.
|
||||
* INVALID if not supported.
|
||||
*/
|
||||
duration_ns expectedPresentLatency;
|
||||
time_point_ns expectedPresentTime;
|
||||
|
||||
/**
|
||||
* The timestamp [ns] since epoch of the current frame's start (i.e. vsync)
|
||||
* INVALID if not supported.
|
||||
*/
|
||||
time_point_ns frameTime;
|
||||
|
||||
/**
|
||||
* The timestamp [ns] since epoch of the current frame's deadline
|
||||
* INVALID if not supported.
|
||||
*/
|
||||
time_point_ns frameTimelineDeadline;
|
||||
};
|
||||
|
||||
struct FrameTimestamps {
|
||||
@@ -269,11 +278,6 @@ public:
|
||||
};
|
||||
|
||||
struct DriverConfig {
|
||||
/**
|
||||
* Reference to the system's FeatureFlagManager. Can be nullptr.
|
||||
*/
|
||||
utils::FeatureFlagManager const * UTILS_NULLABLE featureFlagManager = nullptr;
|
||||
|
||||
/**
|
||||
* Size of handle arena in bytes. Setting to 0 indicates default value is to be used.
|
||||
* Driver clamps to valid values.
|
||||
|
||||
@@ -180,6 +180,10 @@ private:
|
||||
|
||||
int32_t setProducerThrottlingEnabled(EGLNativeWindowType nativeWindow, bool enabled) const;
|
||||
|
||||
struct InitializeJvmForPerformanceManagerIfNeeded {
|
||||
InitializeJvmForPerformanceManagerIfNeeded();
|
||||
};
|
||||
|
||||
struct ExternalTextureAndroid : public ExternalTexture {
|
||||
EGLImageKHR eglImage = EGL_NO_IMAGE;
|
||||
};
|
||||
@@ -187,6 +191,7 @@ private:
|
||||
int mOSVersion;
|
||||
ExternalStreamManagerAndroid& mExternalStreamManager;
|
||||
AndroidDetails& mAndroidDetails;
|
||||
InitializeJvmForPerformanceManagerIfNeeded const mInitializeJvmForPerformanceManagerIfNeeded;
|
||||
utils::PerformanceHintManager mPerformanceHintManager;
|
||||
utils::PerformanceHintManager::Session mPerformanceHintSession;
|
||||
using clock = std::chrono::high_resolution_clock;
|
||||
|
||||
@@ -540,11 +540,6 @@ private:
|
||||
* to be created from a 3d VkImage.
|
||||
*/
|
||||
bool imageView2Don3DImage;
|
||||
|
||||
/**
|
||||
* Desired global priority value for all VkQueue at a system level.
|
||||
*/
|
||||
Platform::GpuContextPriority gpuContextPriority = Platform::GpuContextPriority::DEFAULT;
|
||||
};
|
||||
|
||||
void createInstance(ExtensionSet const& requiredExts) noexcept;
|
||||
|
||||
26
filament/backend/src/CallbackHandler.cpp
Normal file
26
filament/backend/src/CallbackHandler.cpp
Normal file
@@ -0,0 +1,26 @@
|
||||
/*
|
||||
* Copyright (C) 2025 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#include <backend/CallbackHandler.h>
|
||||
|
||||
namespace filament::backend {
|
||||
|
||||
// Define the virtual destructor here to avoid a compiler warning that we treat as an error.
|
||||
// "'CallbackHandler' has no out-of-line virtual method definitions; its vtable will be emitted in every translation unit"
|
||||
// The warning occurs when a class inherits from CallbackHandler.
|
||||
CallbackHandler::~CallbackHandler() = default;
|
||||
|
||||
} // namespace filament::backend
|
||||
@@ -81,8 +81,14 @@ DriverBase::DriverBase(const Platform::DriverConfig& driverConfig) noexcept
|
||||
|
||||
DriverBase::~DriverBase() noexcept {
|
||||
assert_invariant(mCallbacks.empty());
|
||||
assert_invariant(mServiceThreadCallbackQueue.empty());
|
||||
if constexpr (UTILS_HAS_THREADING) {
|
||||
stopServiceThread();
|
||||
// quit our service thread
|
||||
std::unique_lock<std::mutex> lock(mServiceThreadLock);
|
||||
mExitRequested = true;
|
||||
mServiceThreadCondition.notify_one();
|
||||
lock.unlock();
|
||||
mServiceThread.join();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -179,22 +185,6 @@ void DriverBase::debugCommandEnd(CommandStream* cmds, bool synchronous,
|
||||
}
|
||||
}
|
||||
|
||||
#if UTILS_HAS_THREADING
|
||||
void DriverBase::stopServiceThread() noexcept {
|
||||
if (!mServiceThread.joinable()) {
|
||||
return;
|
||||
}
|
||||
|
||||
{
|
||||
std::lock_guard<std::mutex> lock(mServiceThreadLock);
|
||||
mExitRequested = true;
|
||||
}
|
||||
mServiceThreadCondition.notify_one();
|
||||
mServiceThread.join();
|
||||
assert_invariant(mServiceThreadCallbackQueue.empty());
|
||||
}
|
||||
#endif
|
||||
|
||||
size_t Driver::getElementTypeSize(ElementType type) noexcept {
|
||||
switch (type) {
|
||||
case ElementType::BYTE: return sizeof(int8_t);
|
||||
|
||||
@@ -173,8 +173,6 @@ struct HwTimerQuery : public HwBase {
|
||||
|
||||
/*
|
||||
* Base class of all Driver implementations
|
||||
* If multithreading is supported, this class creates a `ServiceThread` responsible for executing
|
||||
* user handlers.
|
||||
*/
|
||||
|
||||
class DriverBase : public Driver {
|
||||
@@ -238,10 +236,6 @@ protected:
|
||||
void debugCommandBegin(CommandStream* cmds, bool synchronous, const char* methodName) noexcept override;
|
||||
void debugCommandEnd(CommandStream* cmds, bool synchronous, const char* methodName) noexcept override;
|
||||
|
||||
// Stops the `ServiceThread`. This method is called during destruction but may be called
|
||||
// explicitly if earlier shutdown is needed. This method is idempotent.
|
||||
void stopServiceThread() noexcept;
|
||||
|
||||
private:
|
||||
const Platform::DriverConfig mDriverConfig;
|
||||
|
||||
|
||||
@@ -382,18 +382,9 @@ void OpenGLDriver::terminate() {
|
||||
delete mCurrentPushConstants;
|
||||
mCurrentPushConstants = nullptr;
|
||||
|
||||
// Flush all pending asynchronous tasks. Some tasks may end up posting follow-up operations to
|
||||
// the `ServiceThread` (e.g., via CountdownCallbackHandler or any user-provided handlers). So we
|
||||
// early stop the ServiceThread to ensure these are processed as well. Tasks posted to the main
|
||||
// thread (due to no user handler) during this process are handled later by `Driver::purge`
|
||||
// within `FEngine::shutdown`.
|
||||
if (getJobWorker()) {
|
||||
getJobWorker()->terminate();
|
||||
}
|
||||
if constexpr (UTILS_HAS_THREADING) {
|
||||
stopServiceThread();
|
||||
}
|
||||
|
||||
mContext.terminate();
|
||||
mPlatform.terminate();
|
||||
}
|
||||
@@ -446,8 +437,7 @@ void OpenGLDriver::setPushConstant(ShaderStage const stage, uint8_t const index,
|
||||
if (std::holds_alternative<bool>(value)) {
|
||||
assert_invariant(type == ConstantType::BOOL);
|
||||
bool const bval = std::get<bool>(value);
|
||||
// This must be the 'ui' version of glUniform1 due to a crash on M-series macbooks.
|
||||
glUniform1ui(location, bval ? 1 : 0);
|
||||
glUniform1i(location, bval ? 1 : 0);
|
||||
} else if (std::holds_alternative<float>(value)) {
|
||||
assert_invariant(type == ConstantType::FLOAT);
|
||||
float const fval = std::get<float>(value);
|
||||
|
||||
@@ -254,14 +254,8 @@ void OpenGLProgram::initializeProgramState(OpenGLContext& context, GLuint progra
|
||||
mPushConstants.reserve(totalConstantCount);
|
||||
mPushConstantFragmentStageOffset = vertexConstants.size();
|
||||
auto const transformAndAdd = [&](Program::PushConstant const& constant) {
|
||||
if (!constant.name.empty()) {
|
||||
GLint const loc = glGetUniformLocation(program, constant.name.c_str());
|
||||
mPushConstants.push_back({loc, constant.type});
|
||||
} else {
|
||||
// If the constant is not named, then we assume it will not be referenced in this
|
||||
// program.
|
||||
mPushConstants.push_back({ -1, constant.type });
|
||||
}
|
||||
GLint const loc = glGetUniformLocation(program, constant.name.c_str());
|
||||
mPushConstants.push_back({loc, constant.type});
|
||||
};
|
||||
std::for_each(vertexConstants.cbegin(), vertexConstants.cend(), transformAndAdd);
|
||||
std::for_each(fragmentConstants.cbegin(), fragmentConstants.cend(), transformAndAdd);
|
||||
|
||||
@@ -79,7 +79,6 @@ static void process_GOOGLE_cpp_style_line_directive(OpenGLContext const& context
|
||||
static void process_OVR_multiview2(OpenGLContext const& context, int32_t eyeCount, char* source,
|
||||
size_t len) noexcept;
|
||||
static std::string_view process_ARB_shading_language_packing(OpenGLContext& context) noexcept;
|
||||
static std::string_view process_countBits(OpenGLContext& context) noexcept;
|
||||
static std::array<std::string_view, 3> splitShaderSource(std::string_view source);
|
||||
|
||||
// ------------------------------------------------------------------------------------------------
|
||||
@@ -780,7 +779,6 @@ void ShaderCompilerService::cancelPendingSynchronousProgram(program_token_t cons
|
||||
|
||||
// add support for ARB_shading_language_packing if needed
|
||||
auto const packingFunctions = process_ARB_shading_language_packing(context);
|
||||
auto const countBitsFunctions = process_countBits(context);
|
||||
|
||||
// split shader source, so we can insert the specialization constants and the packing
|
||||
// functions
|
||||
@@ -791,10 +789,10 @@ void ShaderCompilerService::cancelPendingSynchronousProgram(program_token_t cons
|
||||
version = "#version 310 es\n";
|
||||
}
|
||||
|
||||
std::array<std::string_view, 6> sources = {
|
||||
std::array<std::string_view, 5> sources = {
|
||||
version, prolog,
|
||||
{ specializationConstantString.data(), specializationConstantString.size() },
|
||||
packingFunctions, countBitsFunctions,
|
||||
packingFunctions,
|
||||
{ body.data(), body.size() - 1 } // null-terminated
|
||||
};
|
||||
|
||||
@@ -805,8 +803,8 @@ void ShaderCompilerService::cancelPendingSynchronousProgram(program_token_t cons
|
||||
[](std::string_view s) { return !s.empty(); });
|
||||
size_t const count = std::distance(sources.begin(), partitionPoint);
|
||||
|
||||
std::array<const char*, 6> shaderStrings;
|
||||
std::array<GLint, 6> lengths;
|
||||
std::array<const char*, 5> shaderStrings;
|
||||
std::array<GLint, 5> lengths;
|
||||
for (size_t j = 0; j < count; j++) {
|
||||
shaderStrings[j] = sources[j].data();
|
||||
lengths[j] = GLint(sources[j].size());
|
||||
@@ -1087,29 +1085,6 @@ UTILS_NOINLINE
|
||||
}
|
||||
}
|
||||
|
||||
/* static */ std::string_view process_countBits(OpenGLContext& context) noexcept {
|
||||
using namespace std::literals;
|
||||
// bitCount is available in GL 4.0 and GLES 3.1.
|
||||
if (context.isAtLeastGL<4, 0>() || context.isAtLeastGLES<3, 1>()) {
|
||||
return ""sv;
|
||||
}
|
||||
|
||||
// GLES 2.0 does not support bitwise operations or unsigned integers.
|
||||
if (context.isES2()) {
|
||||
return ""sv;
|
||||
}
|
||||
|
||||
return R"(
|
||||
// https://graphics.stanford.edu/%7Eseander/bithacks.html
|
||||
int bitCount(highp uint value) {
|
||||
value = value - ((value >> 1u) & 0x55555555u);
|
||||
value = (value & 0x33333333u) + ((value >> 2u) & 0x33333333u);
|
||||
return int(((value + (value >> 4u) & 0xF0F0F0Fu) * 0x1010101u) >> 24u);
|
||||
}
|
||||
)"sv;
|
||||
}
|
||||
|
||||
|
||||
// Tragically, OpenGL 4.1 doesn't support unpackHalf2x16 (appeared in 4.2) and
|
||||
// macOS doesn't support GL_ARB_shading_language_packing
|
||||
// Also GLES3.0 didn't have the full set of packing/unpacking functions
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user