Compare commits

...

18 Commits

Author SHA1 Message Date
Powei Feng
48592d7d22 Bump version to 1.69.5 2026-02-24 19:37:59 -08:00
Powei Feng
11714d3adc Release Filament 1.69.4 2026-02-24 19:37:50 -08:00
Filament Bot
6aac9071b3 [automated] Updating /docs due to commit da9173e
Full commit hash is da9173e9dc

DOCS_ALLOW_DIRECT_EDITS
2026-02-25 03:15:24 +00:00
Powei Feng
da9173e9dc ci: automate renderdiff golden image updates (#9740)
- Introduces the `RDIFF_ACCEPT_NEW_GOLDENS` commit message tag.
- Conditionally skip the `test-renderdiff` presubmit comparison
  if this tag is present.
- Extracts renderdiff generation into a reusable
  `.github/actions/renderdiff-generate` composite action.
- Modifies `postsubmit-main.yml` to automatically generate new
  goldens and push them to a temporary
  `accept-goldens-<short-hash>` branch before merging them into
  `main` when the tag is found.
2026-02-25 03:12:50 +00:00
Anish Goyal
cd64d50408 Fixes FBO destroys for inflight command buffers (#9744)
If a framebuffer is destroyed while a command buffer is in flight (e.g.
during a window resize), it's possible for a command buffer to try to
use a framebuffer that no longer exists. This encapsulates framebuffers
and render passes in resource_ptr, to ensure they are never reclaimed
prematurely.
2026-02-25 01:13:51 +00:00
dependabot[bot]
a3145cb96f Bump minimatch (#9747)
Bumps the npm_and_yarn group with 1 update in the /build/common/upload-release-assets directory: [minimatch](https://github.com/isaacs/minimatch).


Updates `minimatch` from 3.1.2 to 3.1.3
- [Changelog](https://github.com/isaacs/minimatch/blob/main/changelog.md)
- [Commits](https://github.com/isaacs/minimatch/compare/v3.1.2...v3.1.3)

---
updated-dependencies:
- dependency-name: minimatch
  dependency-version: 3.1.3
  dependency-type: indirect
  dependency-group: npm_and_yarn
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2026-02-24 16:52:35 -08:00
Nick Fisher
cdfb92e14a gltfio: Allow compile-time override of GLTFIO_USE_FILESYSTEM (#9733) 2026-02-24 17:47:49 +00:00
Doris Wu
55c16e6e7a call execute() under single threaded mode (#9738) 2026-02-23 22:20:03 +00:00
Powei Feng
65e3c3bfb9 backend: disable autoresolve test for gl+vk on CI (#9742)
BUGS=486954356
2026-02-23 21:57:12 +00:00
Ben Doherty
902f869721 Metal: recreate sidecar texture if sample count changes (#9430) 2026-02-23 09:54:21 -08:00
Eliza
ad1bc6f360 engine: fix VSM (#9737) 2026-02-20 15:08:59 -08:00
Sungun Park
73c343635e Turn off UBO batching (#9736)
BUGS=[486200381]
2026-02-20 20:04:05 +00:00
Mathias Agopian
432e672022 Revert "Swap logic of how the EGL display is initialized. (#9634)" (#9729)
This reverts commit c35ae6571f.

BUGS=[481534922, 478925865]
2026-02-20 08:34:51 -08:00
Doris Wu
b56b04c5f8 Fix translucent objects are pickable when skybox is disabled (#9688) 2026-02-20 11:36:58 +08:00
Filament Bot
99816d67c2 [automated] Updating /docs due to commit d6d4f92
Full commit hash is d6d4f92922

DOCS_ALLOW_DIRECT_EDITS
2026-02-19 20:03:46 +00:00
Mathias Agopian
d6d4f92922 fix intensities (#9728)
DOCS_FORCE
2026-02-19 11:59:34 -08:00
Powei Feng
6a59a68622 gl: update record when detaching stream (#9712)
FIXES=483744050
2026-02-19 18:33:04 +00:00
Filament Bot
4580f57987 [automated] Updating /docs due to commit 38f7e57
Full commit hash is 38f7e579f1

DOCS_ALLOW_DIRECT_EDITS
2026-02-19 17:33:34 +00:00
47 changed files with 815 additions and 343 deletions

View File

@@ -0,0 +1,26 @@
name: 'Renderdiff Generate'
description: 'Sets up prerequisites and runs the generate script for renderdiff'
runs:
using: "composite"
steps:
- uses: ./.github/actions/mac-prereq
- uses: ./.github/actions/get-gltf-assets
- uses: ./.github/actions/get-mesa
- uses: ./.github/actions/get-vulkan-sdk
- name: Prerequisites
run: |
# Must have at least clang-16 for a webgpu/dawn build.
sudo xcode-select -s /Applications/Xcode_16.2.app/Contents/Developer
shell: bash
- name: Generate images
run: |
TEST_DIR=test/renderdiff
source ${TEST_DIR}/src/preamble.sh
set -eux
bash ${TEST_DIR}/generate.sh
set +eux
shell: bash
- name: Build diffimg tool
run: |
./build.sh release diffimg
shell: bash

View File

@@ -10,16 +10,27 @@ jobs:
# a branch on filament-assets.
update-renderdiff-goldens:
name: update-renderdiff-goldens
runs-on: 'ubuntu-24.04-4core'
runs-on: 'macos-14-xlarge'
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: Build diffimg
run: ./build.sh release diffimg
- name: Check if accepting new goldens
id: check_accept
env:
COMMIT_MESSAGE: ${{ steps.get_commit_msg.outputs.msg }}
run: |
if echo "${COMMIT_MESSAGE}" | python3 test/renderdiff/src/commit_msg.py --mode=accept_new_goldens; then
echo "accept=true" >> "$GITHUB_OUTPUT"
else
echo "accept=false" >> "$GITHUB_OUTPUT"
fi
shell: bash
- name: Renderdiff Generate for new goldens
if: steps.check_accept.outputs.accept == 'true'
uses: ./.github/actions/renderdiff-generate
- name: Run update script
env:
GH_TOKEN: ${{ secrets.FILAMENTBOT_TOKEN }}
@@ -27,10 +38,24 @@ jobs:
run: |
GOLDEN_BRANCH=$(echo "${COMMIT_MESSAGE}" | python3 test/renderdiff/src/commit_msg.py)
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
if [[ "${{ steps.check_accept.outputs.accept }}" == "true" ]]; then
SHORT_HASH="${COMMIT_HASH:0:8}"
GOLDEN_BRANCH="accept-goldens-${SHORT_HASH}"
echo "Generating new goldens for branch ${GOLDEN_BRANCH}"
python3 test/renderdiff/src/update_golden.py \
--branch=${GOLDEN_BRANCH} \
--source=$(pwd)/out/renderdiff/renders \
--commit-msg="Auto-update goldens from ${COMMIT_HASH}" \
--push-to-remote \
--golden-repo-token=${GH_TOKEN}
fi
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} \

View File

@@ -126,18 +126,24 @@ jobs:
- uses: actions/checkout@v4.1.6
with:
fetch-depth: 0
- uses: ./.github/actions/mac-prereq
- uses: ./.github/actions/get-gltf-assets
- uses: ./.github/actions/get-mesa
- uses: ./.github/actions/get-vulkan-sdk
- id: get_commit_msg
uses: ./.github/actions/get-commit-msg
- name: Prerequisites
- name: Check if accepting new goldens
id: check_accept
env:
COMMIT_MESSAGE: ${{ steps.get_commit_msg.outputs.msg }}
run: |
# Must have at least clang-16 for a webgpu/dawn build.
sudo xcode-select -s /Applications/Xcode_16.2.app/Contents/Developer
if echo "${COMMIT_MESSAGE}" | python3 test/renderdiff/src/commit_msg.py --mode=accept_new_goldens; then
echo "accept=true" >> "$GITHUB_OUTPUT"
else
echo "accept=false" >> "$GITHUB_OUTPUT"
fi
shell: bash
- name: Renderdiff Generate
if: steps.check_accept.outputs.accept != 'true'
uses: ./.github/actions/renderdiff-generate
- name: Render and compare
if: steps.check_accept.outputs.accept != 'true'
id: render_compare
env:
COMMIT_MESSAGE: ${{ steps.get_commit_msg.outputs.msg }}
@@ -146,9 +152,6 @@ jobs:
source ${TEST_DIR}/src/preamble.sh
set -eux
GOLDEN_BRANCH=$(echo "${COMMIT_MESSAGE}" | python3 ${TEST_DIR}/src/commit_msg.py)
bash ${TEST_DIR}/generate.sh
# Build diffimg tool
./build.sh release diffimg
python3 ${TEST_DIR}/src/golden_manager.py \
--branch=${GOLDEN_BRANCH} \
@@ -172,10 +175,12 @@ jobs:
fi
shell: bash
- uses: actions/upload-artifact@v4
if: steps.check_accept.outputs.accept != 'true'
with:
name: presubmit-renderdiff-result
path: ./out/renderdiff
- name: Compare result
if: steps.check_accept.outputs.accept != 'true'
run: |
ERROR_STR="${{ steps.render_compare.outputs.err }}"
if [ -n "${ERROR_STR}" ]; then

View File

@@ -31,7 +31,7 @@ repositories {
}
dependencies {
implementation 'com.google.android.filament:filament-android:1.69.3'
implementation 'com.google.android.filament:filament-android:1.69.5'
}
```
@@ -50,7 +50,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.69.3'
pod 'Filament', '~> 1.69.5'
```
## Documentation

View File

@@ -7,6 +7,10 @@ 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.5
- engine: fix crash when using variance shadow maps
## v1.69.4

View File

@@ -1,5 +1,5 @@
GROUP=com.google.android.filament
VERSION_NAME=1.69.3
VERSION_NAME=1.69.5
POM_DESCRIPTION=Real-time physically based rendering engine for Android.

View File

@@ -235,9 +235,9 @@
"license": "MIT"
},
"node_modules/minimatch": {
"version": "3.1.2",
"resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz",
"integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==",
"version": "3.1.3",
"resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.3.tgz",
"integrity": "sha512-M2GCs7Vk83NxkUyQV1bkABc4yxgz9kILhHImZiBPAZ9ybuvCb0/H7lEl5XvIg3g+9d4eNotkZA5IWwYl0tibaA==",
"dependencies": {
"brace-expansion": "^1.1.7"
},
@@ -429,9 +429,9 @@
"integrity": "sha512-nGqtvLrj5w0naR6tDPfB4cUmYCqouzyQiz6C5y/LtcDllJdrcc6WaWW6iXyIIOErTa/XRybj28aasdn4LkVk6Q=="
},
"minimatch": {
"version": "3.1.2",
"resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz",
"integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==",
"version": "3.1.3",
"resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.3.tgz",
"integrity": "sha512-M2GCs7Vk83NxkUyQV1bkABc4yxgz9kILhHImZiBPAZ9ybuvCb0/H7lEl5XvIg3g+9d4eNotkZA5IWwYl0tibaA==",
"requires": {
"brace-expansion": "^1.1.7"
}

View File

@@ -181,7 +181,7 @@ important for <code>matc</code> (material compiler).</p>
}
dependencies {
implementation 'com.google.android.filament:filament-android:1.69.2'
implementation 'com.google.android.filament:filament-android:1.69.3'
}
</code></pre>
<p>Here are all the libraries available in the group <code>com.google.android.filament</code>:</p>
@@ -195,7 +195,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', '~&gt; 1.69.2'
<pre><code class="language-shell">pod 'Filament', '~&gt; 1.69.3'
</code></pre>
<h2 id="documentation"><a class="header" href="#documentation">Documentation</a></h2>
<ul>

View File

@@ -263,6 +263,27 @@ branch of the golden repo (i.e. <code>my-pr-branch-golden</code>).</li>
<li>If the PR is merged, then there is another workflow that will merge <code>my-pr-branch-golden</code>
to the <code>main</code> branch of the golden repo.</li>
</ul>
<h3 id="automated-update-via-commit-message"><a class="header" href="#automated-update-via-commit-message">Automated update via commit message</a></h3>
<p>Alternatively, if you are confident in your changes and want the CI to handle the update
for you, you can use the following tag in your commit message:</p>
<ul>
<li>In the commit message of your working branch on <code>filament</code>, add the following line:
<pre><code>RDIFF_ACCEPT_NEW_GOLDENS
</code></pre>
</li>
</ul>
<p>This has the following effects:</p>
<ul>
<li>The presubmit test <code>test-renderdiff</code> will be bypassed (it will not perform rendering or
comparison).</li>
<li>When the PR is merged, the postsubmit CI will automatically:
<ol>
<li>Build Filament and generate the new images.</li>
<li>Upload them to a temporary branch in <code>filament-assets</code>.</li>
<li>Merge that branch into <code>main</code>.</li>
</ol>
</li>
</ul>
<h2 id="viewing-test-results"><a class="header" href="#viewing-test-results">Viewing test results</a></h2>
<p>We provide a viewer for looking at the result of a test run. The viewer is a webapp that can
be used by pointing your browser to a localhost port. If you input the viewer with a PR or a

View File

@@ -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&nbsp;1</a>, is simple and efficient.
</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>
</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>
<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">&nbsp;</a><b style="font-style:normal;">Listing&nbsp;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&nbsp;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-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>
</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>
<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">&nbsp;</a><b style="font-style:normal;">Listing&nbsp;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&nbsp;4</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">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>
</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>
<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">&nbsp;</a><b style="font-style:normal;">Listing&nbsp;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&nbsp;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 class="hljs-params"></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> {</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-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>
</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>
<span class="line">}</span>
<span class="line"></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-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-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">&nbsp;</a><b style="font-style:normal;">Listing&nbsp;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&nbsp;9</a>.
</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>
</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>
<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"><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">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>
<span class="line"></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-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-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> Fd_Lambert() {</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-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-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 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>
<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 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>
<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> roughness = perceptualRoughness * perceptualRoughness;</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>
<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 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>
<span class="line"> <span class="hljs-comment">// specular BRDF</span></span>
<span class="line"> <span class="hljs-type">vec3</span> Fr = (D * V) * F;</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>
<span class="line"> <span class="hljs-comment">// diffuse BRDF</span></span>
<span class="line"> <span class="hljs-type">vec3</span> Fd = diffuseColor * Fd_Lambert();</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>
<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">&nbsp;</a><b style="font-style:normal;">Listing&nbsp;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&nbsp;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">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">&nbsp;</a><b style="font-style:normal;">Listing&nbsp;12:</b> Computing \(\fNormal\) for dielectric and metallic materials in GLSL</div></center>
</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">&nbsp;</a><b style="font-style:normal;">Listing&nbsp;12:</b> Computing \(\fNormal\) for dielectric and metallic materials in GLSL</div></center>
<a class="target" name="roughnessremappingandclamping">&nbsp;</a><a class="target" name="materialsystem/parameterization/remapping/roughnessremappingandclamping">&nbsp;</a><a class="target" name="toc4.8.3.3">&nbsp;</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&nbsp;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-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>
</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>
<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">&nbsp;</a><b style="font-style:normal;">Listing&nbsp;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">&nbsp;</a><b style="font-style:normal;">Figure&nbsp;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&nbsp;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-keyword">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-type">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 = clamp(clearCoatPerceptualRoughness, <span class="hljs-number">0.089</span>, <span class="hljs-number">1.0</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"> 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-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 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>
<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&nbsp;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">float D_Ashikhmin(float roughness, float NoH) {</span>
</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>
<span class="line"> <span class="hljs-comment">// Ashikhmin 2007, &quot;Distribution-based BRDFs&quot;</span></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 &gt; 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 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 &gt; 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">}</span></code></pre><center><div class="listingcaption tilde"><a class="target" name="listing_clothbrdf">&nbsp;</a><b style="font-style:normal;">Listing&nbsp;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&nbsp;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-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>
</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>
<span class="line"> <span class="hljs-comment">// Estevez and Kulla 2017, &quot;Production Friendly Microfacet Sheen BRDF&quot;</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 &gt; 0 in fp16</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 &gt; 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">&nbsp;</a><b style="font-style:normal;">Listing&nbsp;18:</b> Implementation of the &ldquo;Charlie&rdquo; NDF in GLSL</div></center>
<a class="target" name="sheencolor">&nbsp;</a><a class="target" name="materialsystem/clothmodel/clothspecularbrdf/sheencolor">&nbsp;</a><a class="target" name="toc4.12.1.1">&nbsp;</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">&nbsp;</a><b style="font-style:normal;">Listing&nbsp;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-keyword">float</span> multiplier;</span>
</p><pre class="listing tilde"><code><span class="line"><span class="hljs-type">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.isMasked()) {</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-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.getDesiredIntensity() /</span>
<span class="line"> photometricLight.getIntegratedIntensity();</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">} <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.getMultiplier();</span>
<span class="line"> multiplier = photometricLight.<span class="hljs-built_in">getMultiplier</span>();</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-keyword">float</span> lightIntensity = photometricLight.getMaxIntensity() * multiplier;</span></code></pre><center><div class="listingcaption tilde"><a class="target" name="listing_photometriclightintensity">&nbsp;</a><b style="font-style:normal;">Listing&nbsp;23:</b> Computing the intensity of a photometric light on the CPU</div></center>
<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">&nbsp;</a><b style="font-style:normal;">Listing&nbsp;23:</b> Computing the intensity of a photometric light on the CPU</div></center>
<p>
<div class="endnote"><a class="target" name="endnote-xarrowintensity">&nbsp;</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">&nbsp;</a><b style="font-style:normal;">Figure&nbsp;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 <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>
</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>
<span class="line">}</span></code></pre><center><div class="listingcaption tilde"><a class="target" name="listing_irradiancesh">&nbsp;</a><b style="font-style:normal;">Listing&nbsp;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-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>
</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>
<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">&nbsp;</a><b style="font-style:normal;">Listing&nbsp;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&#x27;t have its own normal map</span></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-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-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 *= 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">&nbsp;</a><b style="font-style:normal;">Listing&nbsp;31:</b> GLSL implementation of the clear coat specular lobe for image-based lighting</div></center>
<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">&nbsp;</a><b style="font-style:normal;">Listing&nbsp;31:</b> GLSL implementation of the clear coat specular lobe for image-based lighting</div></center>
<a class="target" name="anisotropy">&nbsp;</a><a class="target" name="lighting/imagebasedlights/anisotropy">&nbsp;</a><a class="target" name="toc5.3.6">&nbsp;</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">&nbsp;</a><b style="font-style:normal;">Figure&nbsp;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-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>
</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>
<span class="line"></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) &amp;&amp; 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">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) &amp;&amp; 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"></span>
<span class="line">vec3 ibl = diffuseColor * indirectDiffuse + indirectSpecular * specularColor;</span></code></pre><center><div class="listingcaption tilde"><a class="target" name="listing_clothapprox">&nbsp;</a><b style="font-style:normal;">Listing&nbsp;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-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-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-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&#x27;s EV100</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 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>
<span class="line"></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 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>
<span class="line">vec4 color = evaluateLighting();</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">color.rgb *= exposure;</span></code></pre><center><div class="listingcaption tilde"><a class="target" name="listing_fragmentexposure">&nbsp;</a><b style="font-style:normal;">Listing&nbsp;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-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 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>
<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-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 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>
<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>&lt;<span class="hljs-keyword">float</span>&gt; ior; <span class="hljs-comment">// complex IOR, n + ik</span></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&lt;<span class="hljs-type">float</span>&gt; 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-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 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>
<span class="line"> float2 wavelengthBounds = CIE_D65_START + float2{indexBounds} * CIE_D65_INTERVAL;</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 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>
<span class="line"></span>
<span class="line"><span class="hljs-comment">// For std::lower_bound</span></span>
<span class="line"><span class="hljs-keyword">bool</span> <span class="hljs-keyword">operator</span>&lt;(<span class="hljs-keyword">const</span> Sample&amp; lhs, <span class="hljs-keyword">const</span> Sample&amp; rhs) {</span>
<span class="line"><span class="hljs-type">bool</span> <span class="hljs-keyword">operator</span>&lt;(<span class="hljs-type">const</span> Sample&amp; lhs, <span class="hljs-type">const</span> Sample&amp; rhs) {</span>
<span class="line"> <span class="hljs-keyword">return</span> lhs.w &lt; 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-keyword">static</span> <span class="hljs-built_in">std</span>::<span class="hljs-built_in">complex</span>&lt;<span class="hljs-keyword">float</span>&gt; <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>&lt;sample&gt;&amp; 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-function"><span class="hljs-type">static</span> std::complex&lt;<span class="hljs-type">float</span>&gt; <span class="hljs-title">findSample</span><span class="hljs-params">(<span class="hljs-type">const</span> std::vector&lt;sample&gt;&amp; 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-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-keyword">float</span> t = (w - i0-&gt;w) / (i1-&gt;w - i0-&gt;w);</span>
<span class="line"> <span class="hljs-keyword">float</span> n = lerp(i0-&gt;ior.real(), i1-&gt;ior.real(), t);</span>
<span class="line"> <span class="hljs-keyword">float</span> k = lerp(i0-&gt;ior.imag(), i1-&gt;ior.imag(), t);</span>
<span class="line"> <span class="hljs-type">float</span> t = (w - i0-&gt;w) / (i1-&gt;w - i0-&gt;w);</span>
<span class="line"> <span class="hljs-type">float</span> n = <span class="hljs-built_in">lerp</span>(i0-&gt;ior.<span class="hljs-built_in">real</span>(), i1-&gt;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-&gt;ior.<span class="hljs-built_in">imag</span>(), i1-&gt;ior.<span class="hljs-built_in">imag</span>(), 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-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>&lt;<span class="hljs-keyword">float</span>&gt;&amp; 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 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&lt;<span class="hljs-type">float</span>&gt;&amp; 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>
<span class="line"></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&amp; v)</span> </span>{</span>
<span class="line"> <span class="hljs-keyword">const</span> mat3f XYZ_sRGB{</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&amp; v)</span> </span>{</span>
<span class="line"> <span class="hljs-type">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-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>&lt;sample&gt;&amp; samples)</span> </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&lt;sample&gt;&amp; samples)</span> </span>{</span>
<span class="line"> float3 xyz{<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 class="hljs-type">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-keyword">size_t</span> i = <span class="hljs-number">0</span>; i &lt; CIE_XYZ_COUNT; i++) {</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 &lt; CIE_XYZ_COUNT; i++) {</span>
<span class="line"> <span class="hljs-comment">// Current wavelength</span></span>
<span class="line"> <span class="hljs-keyword">float</span> w = CIE_XYZ_START + i;</span>
<span class="line"> <span class="hljs-type">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 = findSample(samples, w);</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-comment">// Compute Fresnel reflectance at normal incidence</span></span>
<span class="line"> <span class="hljs-keyword">float</span> f0 = fresnel(sample);</span>
<span class="line"> <span class="hljs-type">float</span> f0 = <span class="hljs-built_in">fresnel</span>(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-keyword">float</span> d65 = illuminantD65(w);</span>
<span class="line"> <span class="hljs-type">float</span> d65 = <span class="hljs-built_in">illuminantD65</span>(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 = XYZ_to_sRGB(xyz);</span>
<span class="line"> float3 linear = <span class="hljs-built_in">XYZ_to_sRGB</span>(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> (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 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>
<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">&nbsp;</a><b style="font-style:normal;">Listing&nbsp;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 &lt;&lt; <span class="hljs-string">16) | (bits &gt;&gt; 16</span>);</span>
<span class="line"> bits = ((bits &amp; 0x55555555) &lt;&lt; <span class="hljs-string">1) | ((bits &amp; 0xAAAAAAAA) &gt;&gt; 1</span>);</span>
<span class="line"> bits = ((bits &amp; 0x33333333) &lt;&lt; <span class="hljs-string">2) | ((bits &amp; 0xCCCCCCCC) &gt;&gt; 2</span>);</span>
<span class="line"> bits = ((bits &amp; 0x0F0F0F0F) &lt;&lt; <span class="hljs-string">4) | ((bits &amp; 0xF0F0F0F0) &gt;&gt; 4</span>);</span>
<span class="line"> bits = ((bits &amp; 0x00FF00FF) &lt;&lt; <span class="hljs-string">8) | ((bits &amp; 0xFF00FF00) &gt;&gt; 8</span>);</span>
<span class="line"> <span class="hljs-built_in">return</span> vec2f(i / numSamples, bits / exp2(32));</span>
<span class="line"> bits = ((bits &amp; <span class="hljs-number">0</span>x55555555) &lt;&lt; <span class="hljs-number">1</span>) | ((bits &amp; <span class="hljs-number">0</span>xAAAAAAAA) &gt;&gt; <span class="hljs-number">1</span>);</span>
<span class="line"> bits = ((bits &amp; <span class="hljs-number">0</span>x33333333) &lt;&lt; <span class="hljs-number">2</span>) | ((bits &amp; <span class="hljs-number">0</span>xCCCCCCCC) &gt;&gt; <span class="hljs-number">2</span>);</span>
<span class="line"> bits = ((bits &amp; <span class="hljs-number">0</span>x0F0F0F0F) &lt;&lt; <span class="hljs-number">4</span>) | ((bits &amp; <span class="hljs-number">0</span>xF0F0F0F0) &gt;&gt; <span class="hljs-number">4</span>);</span>
<span class="line"> bits = ((bits &amp; <span class="hljs-number">0</span>x00FF00FF) &lt;&lt; <span class="hljs-number">8</span>) | ((bits &amp; <span class="hljs-number">0</span>xFF00FF00) &gt;&gt; <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">}</span></code></pre><center><div class="listingcaption tilde">C++ implementation of a Hammersley sequence generator</div></center>
<a class="target" name="precomputinglforimage-basedlighting">&nbsp;</a><a class="target" name="annex/precomputinglforimage-basedlighting">&nbsp;</a><a class="target" name="toc9.5">&nbsp;</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> 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>
</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>
<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 DFG(<span class="hljs-type">float</span> NoV, <span class="hljs-type">float</span> a) {</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"> float3 V;</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.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.z = NoV;</span>
<span class="line"></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 &lt; 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 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 &lt; 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"></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 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>
<span class="line"> <span class="hljs-keyword">if</span> (NoL &gt; <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"> <span class="hljs-keyword">if</span> (NoL &gt; <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"> 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.0</span>f / sampleCount);</span>
<span class="line"> <span class="hljs-keyword">return</span> r * (<span class="hljs-number">1.0f</span> / 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">&nbsp;</a><a class="target" name="annex/sphericalharmonics">&nbsp;</a><a class="target" name="toc9.6">&nbsp;</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&nbsp;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-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>
</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>
<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-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&amp; s)</span></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&amp; 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-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"> <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"> SHb[<span class="hljs-number">0</span>] = Pml_1;</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 &lt; 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"> <span class="hljs-keyword">for</span> (<span class="hljs-type">ssize_t</span> l = <span class="hljs-number">1</span>; l &lt; 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"> Pml_2 = Pml_1;</span>
<span class="line"> Pml_1 = Pml;</span>
<span class="line"> SHb[SHindex(<span class="hljs-number">0</span>, l)] = 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"> }</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 &lt; numBands ; m++) {</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 &lt; 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-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-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-comment">// l == m</span></span>
<span class="line"> SHb[SHindex(-m, m)] = Pml_2;</span>
<span class="line"> SHb[SHindex( 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[<span class="hljs-built_in">SHindex</span>( m, m)] = Pml_2;</span>
<span class="line"> <span class="hljs-keyword">if</span> (m + <span class="hljs-number">1</span> &lt; numBands) {</span>
<span class="line"> <span class="hljs-comment">// l == m+1</span></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 &lt; 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"> 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 &lt; 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"> / (l - m);</span>
<span class="line"> Pml_2 = Pml_1;</span>
<span class="line"> Pml_1 = Pml;</span>
<span class="line"> SHb[SHindex(-m, l)] = Pml;</span>
<span class="line"> SHb[SHindex( m, l)] = 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"> }</span>
<span class="line"> }</span>
<span class="line"> }</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 &lt;= numBands ; m++) {</span>
<span class="line"> <span class="hljs-keyword">for</span> (<span class="hljs-keyword">ssize_t</span> l = m; l &lt; 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 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 &lt;= numBands ; m++) {</span>
<span class="line"> <span class="hljs-keyword">for</span> (<span class="hljs-type">ssize_t</span> l = m; l &lt; 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>
<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"> <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"> 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-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>
</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>
<span class="line"></span>
<span class="line"><span class="hljs-comment">// &lt; cos(theta) &gt; SH coefficients pre-multiplied by 1 / K(0,l)</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-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-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 &amp; <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-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 &amp; <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> &lt;&lt; l));</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 &amp; <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> &lt;&lt; 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-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-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-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 &gt; d) {</span>

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View File

@@ -16,12 +16,13 @@ class SimulatedSkybox {
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.nightColor = [0.0, 3.0e-9, 7.5e-9];
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 = [0.001, 1.0, 350.0, 0.01]; // x=Density, y=Enabled, z=Frequency, w=PixelScale
this.starIntensity = 1.0;
this.focalLength = 24.0;
this.height = 1000.0;
this.planetRadius = 6360.0;
@@ -43,7 +44,7 @@ class SimulatedSkybox {
// Milky Way Parameters
// x=Intensity, y=Saturation, z=Unused
this.milkyWayControl = [1.0, 1.0, 0.07];
this.milkyWayControl = [1.0, 1.2, 0.05];
this.milkyWayEnabled = true;
this.milkyWayRotation = [1, 0, 0, 0, 1, 0, 0, 0, 1]; // Identity by default
this.initEntity();
@@ -453,6 +454,11 @@ class SimulatedSkybox {
this.updateCoefficients();
}
setStarIntensity(intensity) {
this.starIntensity = Math.max(0.0, intensity);
this.updateCoefficients();
}
setMoonPosition(direction) {
// normalize
const len = Math.hypot(direction[0], direction[1], direction[2]);
@@ -500,6 +506,13 @@ class SimulatedSkybox {
}
}
setExposure(exposure) {
this.exposure = exposure;
this.updateCoefficients();
}
updateCoefficients() {
if (!this.materialInstance) {
console.warn("updateCoefficients called before material loaded");
@@ -585,6 +598,7 @@ class SimulatedSkybox {
this.materialInstance.setFloat4Parameter('cloudControl2', new Float32Array(this.cloudControl2));
this.materialInstance.setFloat4Parameter('waterControl', new Float32Array(this.waterControl));
this.materialInstance.setFloat4Parameter('starControl', new Float32Array(this.starControl));
this.materialInstance.setFloatParameter('starIntensity', this.starIntensity);
this.materialInstance.setFloatParameter('sunIntensity', physicalSunIntensity);
@@ -615,12 +629,13 @@ class SimulatedSkybox {
this.materialInstance.setFloatParameter('sunIntensity2', finalMoonIntensity);
// Scale Milky Way by Sun Intensity (Pre-exposed) to match dynamic range
// Calibration: 1.0 User Intensity ~ 0.025 Lux Relative (approx 2.5% of Sun Pixel Value at Day)
// At Sunny 16 (Sun ~ 2.6), this gives ~0.065 pixel brightness, which is visible but dim.
// Scale Milky Way by Sun Intensity
// Calibration: 1.0 User Intensity = 1.5e-3 cd/m^2 (Nits) approx.
// Target: 1.5e-3 Nits. SunIntensity = 100,000.
// Scale = 1.5e-3 / 1.0e5 = 1.5e-8.
const mwIntensity = this.milkyWayEnabled ? this.milkyWayControl[0] : 0.0;
const mwUniform = [
mwIntensity * this.sunIntensity * 0.025,
mwIntensity * this.sunIntensity * 1.5e-8,
this.milkyWayControl[1],
this.milkyWayControl[2]
];
@@ -634,6 +649,7 @@ class SimulatedSkybox {
const moonHaloUpload = [...this.moonHalo];
moonHaloUpload[2] *= moonRadConv;
this.materialInstance.setFloat4Parameter('sunHalo2', new Float32Array(moonHaloUpload));
this.materialInstance.setFloatParameter('exposure', this.exposure !== undefined ? this.exposure : 1.0);
// Solar Eclipse (CPU Calculation)
const sunRadius = Math.acos(this.sunHalo[0]);

View File

@@ -218,6 +218,7 @@ class App {
const exposure = this.getExposure();
const preExposedIntensity = this.params.sunIntensity * exposure;
this.skybox.setSunIntensity(preExposedIntensity);
this.skybox.setExposure(exposure); // Update Skybox Exposure Uniform
// Moon Exposure
if (this.mParams) {
@@ -389,7 +390,7 @@ class App {
};
mwFolder.add(this.mwParams, 'enabled').name('Enabled').onChange(updateMW);
mwFolder.add(this.mwParams, 'intensity', 0.0, 5.0).onChange(updateMW);
mwFolder.add(this.mwParams, 'intensity', 0.0, 100.0).onChange(updateMW);
mwFolder.add(this.mwParams, 'saturation', 0.0, 2.0).onChange(updateMW);
mwFolder.add(this.mwParams, 'blackPoint', 0.0, 0.5).name('Black Point').onChange(updateMW);
mwFolder.add(this.mwParams, 'siderealTime', 0.0, 24.0).name('Sidereal Time').listen().onChange(updateMW);
@@ -464,18 +465,23 @@ class App {
const starFolder = gui.addFolder('Stars');
this.sParams = {
enabled: true,
density: 0.001
density: 0.001,
intensity: 0.0 // 2^0 = 1.0
};
// Initialize defaults (Density 0.001, Enabled True)
sky.setStarControl(0.001, true);
sky.setStarIntensity(Math.pow(2.0, 0.0));
const updateStars = () => {
sky.setStarControl(this.sParams.density, this.sParams.enabled);
sky.setStarIntensity(Math.pow(2.0, this.sParams.intensity));
};
starFolder.add(this.sParams, 'enabled').name('Enabled').onChange(updateStars);
starFolder.add(this.sParams, 'density', 0.0, 0.01, 0.0001).name('Density').onChange(updateStars);
starFolder.add(this.sParams, 'intensity', 0.0, 24.0).name('Intensity (Exp)').onChange(updateStars);
starFolder.close();
this.updateStars = updateStars;
const artFolder = gui.addFolder('Artistic');
// Set Horizon Glow default to 0.0
@@ -489,7 +495,7 @@ class App {
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
@@ -502,7 +508,7 @@ class App {
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, 'shutterSpeed', 0.05, 1000.0).onChange(() => this.updateCameraExposure());
camFolder.add(this.params, 'iso', 50.0, 3200.0).onChange(() => this.updateCameraExposure());
const bloomFolder = camFolder.addFolder('Bloom');
@@ -692,13 +698,15 @@ class App {
const s = this.sParams;
const b = this.bParams;
const m = this.mParams;
const mw = this.mwParams;
const sk = this.skybox;
return {
p: { a: p.aperture, ss: p.shutterSpeed, i: p.iso, st: p.sunTheta, sp: p.sunPhi, fl: p.focalLength, si: p.sunIntensity },
c: { v: c.volumetrics, co: c.coverage, d: c.density, h: c.height, s: c.speed, e: c.evolution },
w: { dt: w.derivativeTrick, st: w.strength, s: w.speed, o: w.octaves },
s: { e: s.enabled, d: s.density },
s: { e: s.enabled, d: s.density, i: s.intensity },
mw: { e: mw.enabled, i: mw.intensity, s: mw.saturation, bp: mw.blackPoint, st: mw.siderealTime, l: mw.latitude },
b: { e: b.enabled, lf: b.lensFlare },
m: { e: m.enabled, az: m.azimuth, h: m.height, r: m.radius, i: m.intensity },
cm: { t: this.camState.theta, p: this.camState.phi },
@@ -723,6 +731,7 @@ class App {
const c = state.c;
const w = state.w;
const s = state.s;
const mw = state.mw;
const b = state.b;
const m = state.m;
const k = state.k;
@@ -757,6 +766,18 @@ class App {
if (s) {
if (s.e !== undefined) this.sParams.enabled = s.e;
if (s.d !== undefined) this.sParams.density = s.d;
if (s.i !== undefined) this.sParams.intensity = s.i;
if (this.updateStars) this.updateStars();
}
if (mw) {
if (mw.e !== undefined) this.mwParams.enabled = mw.e;
if (mw.i !== undefined) this.mwParams.intensity = mw.i;
if (mw.s !== undefined) this.mwParams.saturation = mw.s;
if (mw.bp !== undefined) this.mwParams.blackPoint = mw.bp;
if (mw.st !== undefined) this.mwParams.siderealTime = mw.st;
if (mw.l !== undefined) this.mwParams.latitude = mw.l;
if (this.updateMW) this.updateMW();
}
if (b) {

View File

@@ -96,10 +96,20 @@ material {
name : starControl, // x=Density, y=Enabled, z=Frequency, w=PixelScale
precision : high
},
{
type : float,
name : starIntensity,
precision : high
},
{
type : sampler2d,
name : moonTexture
},
{
type : float,
name : exposure,
precision : high
},
{
type : sampler2d,
name : moonNormal
@@ -162,9 +172,9 @@ fragment {
// --- CONFIGURATION ---
// Stars
#define STAR_GLOBAL_INTENSITY 150.0 // Master brightness multiplier [0.0 - 500.0]
#define STAR_BRIGHTNESS_BASE 0.5 // Minimum random brightness [0.0 - 1.0]
#define STAR_BRIGHTNESS_VAR 4.0 // Random brightness variance range [0.0 - 10.0]
#define STAR_GLOBAL_INTENSITY 100.0 // Master brightness multiplier [0.0 - 500.0]
#define STAR_BRIGHTNESS_BASE 0.01 // Minimum random brightness [0.0 - 1.0]
#define STAR_BRIGHTNESS_VAR 15.0 // Random brightness variance range [0.0 - 10.0]
#define STAR_FADE_SUN_ELV_HIGH 0.10 // Sun elevation (sin) where stars are fully hidden [0.0 - 0.5]
#define STAR_FADE_SUN_ELV_LOW -0.20 // Sun elevation (sin) where stars are fully visible [-0.5 - 0.0]
#define STAR_CLOUD_OCCLUSION 0.1 // Visibility when covered by clouds [0.0 - 1.0]
@@ -961,7 +971,11 @@ fragment {
highp vec4 sunHalo, highp vec4 cloudControl, highp vec4 cloudControl2,
highp vec4 shimmerControl, highp vec4 waterControl,
highp vec3 L2, highp float sunIntensity2, highp vec4 sunHalo2,
sampler2D moonTex, sampler2D moonNormal) {
sampler2D moonTex, sampler2D moonNormal,
highp vec3 nightColor,
highp mat3 milkyWayRotation,
highp vec3 milkyWayControl,
sampler2D milkyWayTexture) {
// Project to plane y=0
highp float t = WATER_PLANE_HEIGHT / min(V.y, -0.0002); // Reduced clamp to minimize "wall" artifact
@@ -1057,7 +1071,7 @@ fragment {
highp float rHorizonMask = 1.0 - smoothstep(0.0, REFLECTION_HORIZON_FADE, R.y);
if (rHorizonMask > 0.0) {
reflection += getStarLayer(R, L, reflCloudDensity, outTransmittance, materialParams.starControl) * rHorizonMask;
reflection += getStarLayer(R, L, reflCloudDensity, outTransmittance, materialParams.starControl) * rHorizonMask * materialParams.exposure;
}
// Sun Disk Reflection
@@ -1088,6 +1102,29 @@ fragment {
// Apply clouds to reflection
reflection = mix(reflection, reflCloudLayer, reflCloudDensity);
// Add Milky Way to Reflection
// Calculate Fade based on Sun Elevation
highp float sunElvSin = L.y;
highp float mwFade = smoothstep(STAR_FADE_SUN_ELV_HIGH, STAR_FADE_SUN_ELV_LOW, sunElvSin);
if (mwFade > 0.0) {
highp vec3 mwColor = getMilkyWay(R, milkyWayRotation, milkyWayTexture, milkyWayControl);
// Apply Atmosphere Transmittance (approximate)
mwColor *= outTransmittance;
// Apply Fades
mwColor *= mwFade;
mwColor *= (1.0 - reflMoonOcclusion); // Occlude by Moon
mwColor *= (1.0 - reflCloudDensity); // Occlude by Clouds
reflection += mwColor;
}
// Add Night Color to Reflection
reflection += nightColor;
// Fresnel
highp float F0 = WATER_FRESNEL_F0; // Water
highp float cosTheta = clamp(dot(-V, N_water), 0.0, 1.0);
@@ -1187,7 +1224,7 @@ fragment {
// 7. Stars
// Add stars before clouds (clouds cover stars)
highp vec3 starColor = getStarLayer(V, L, cloudDensityVal, transmittance, materialParams.starControl);
highp vec3 starColor = getStarLayer(V, L, cloudDensityVal, transmittance, materialParams.starControl) * materialParams.exposure * materialParams.starIntensity;
// 7b. Milky Way
// Add Milky Way behind stars (conceptually) but handled similarly
@@ -1232,7 +1269,11 @@ fragment {
materialParams.shimmerControl,
materialParams.waterControl,
L2, materialParams.sunIntensity2, materialParams.sunHalo2,
materialParams_moonTexture, materialParams_moonNormal);
materialParams_moonTexture, materialParams_moonNormal,
materialParams.nightColor,
materialParams.milkyWayRotation,
materialParams.milkyWayControl,
materialParams_milkyWayTexture);
color = applyDynamicToneMapping(color, L, materialParams.contrast);
}

View File

@@ -16,12 +16,13 @@ class SimulatedSkybox {
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.nightColor = [0.0, 3.0e-9, 7.5e-9];
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 = [0.001, 1.0, 350.0, 0.01]; // x=Density, y=Enabled, z=Frequency, w=PixelScale
this.starIntensity = 1.0;
this.focalLength = 24.0;
this.height = 1000.0;
this.planetRadius = 6360.0;
@@ -43,7 +44,7 @@ class SimulatedSkybox {
// Milky Way Parameters
// x=Intensity, y=Saturation, z=Unused
this.milkyWayControl = [1.0, 1.0, 0.07];
this.milkyWayControl = [1.0, 1.2, 0.05];
this.milkyWayEnabled = true;
this.milkyWayRotation = [1, 0, 0, 0, 1, 0, 0, 0, 1]; // Identity by default
this.initEntity();
@@ -453,6 +454,11 @@ class SimulatedSkybox {
this.updateCoefficients();
}
setStarIntensity(intensity) {
this.starIntensity = Math.max(0.0, intensity);
this.updateCoefficients();
}
setMoonPosition(direction) {
// normalize
const len = Math.hypot(direction[0], direction[1], direction[2]);
@@ -500,6 +506,13 @@ class SimulatedSkybox {
}
}
setExposure(exposure) {
this.exposure = exposure;
this.updateCoefficients();
}
updateCoefficients() {
if (!this.materialInstance) {
console.warn("updateCoefficients called before material loaded");
@@ -585,6 +598,7 @@ class SimulatedSkybox {
this.materialInstance.setFloat4Parameter('cloudControl2', new Float32Array(this.cloudControl2));
this.materialInstance.setFloat4Parameter('waterControl', new Float32Array(this.waterControl));
this.materialInstance.setFloat4Parameter('starControl', new Float32Array(this.starControl));
this.materialInstance.setFloatParameter('starIntensity', this.starIntensity);
this.materialInstance.setFloatParameter('sunIntensity', physicalSunIntensity);
@@ -615,12 +629,13 @@ class SimulatedSkybox {
this.materialInstance.setFloatParameter('sunIntensity2', finalMoonIntensity);
// Scale Milky Way by Sun Intensity (Pre-exposed) to match dynamic range
// Calibration: 1.0 User Intensity ~ 0.025 Lux Relative (approx 2.5% of Sun Pixel Value at Day)
// At Sunny 16 (Sun ~ 2.6), this gives ~0.065 pixel brightness, which is visible but dim.
// Scale Milky Way by Sun Intensity
// Calibration: 1.0 User Intensity = 1.5e-3 cd/m^2 (Nits) approx.
// Target: 1.5e-3 Nits. SunIntensity = 100,000.
// Scale = 1.5e-3 / 1.0e5 = 1.5e-8.
const mwIntensity = this.milkyWayEnabled ? this.milkyWayControl[0] : 0.0;
const mwUniform = [
mwIntensity * this.sunIntensity * 0.025,
mwIntensity * this.sunIntensity * 1.5e-8,
this.milkyWayControl[1],
this.milkyWayControl[2]
];
@@ -634,6 +649,7 @@ class SimulatedSkybox {
const moonHaloUpload = [...this.moonHalo];
moonHaloUpload[2] *= moonRadConv;
this.materialInstance.setFloat4Parameter('sunHalo2', new Float32Array(moonHaloUpload));
this.materialInstance.setFloatParameter('exposure', this.exposure !== undefined ? this.exposure : 1.0);
// Solar Eclipse (CPU Calculation)
const sunRadius = Math.acos(this.sunHalo[0]);

View File

@@ -218,6 +218,7 @@ class App {
const exposure = this.getExposure();
const preExposedIntensity = this.params.sunIntensity * exposure;
this.skybox.setSunIntensity(preExposedIntensity);
this.skybox.setExposure(exposure); // Update Skybox Exposure Uniform
// Moon Exposure
if (this.mParams) {
@@ -389,7 +390,7 @@ class App {
};
mwFolder.add(this.mwParams, 'enabled').name('Enabled').onChange(updateMW);
mwFolder.add(this.mwParams, 'intensity', 0.0, 5.0).onChange(updateMW);
mwFolder.add(this.mwParams, 'intensity', 0.0, 100.0).onChange(updateMW);
mwFolder.add(this.mwParams, 'saturation', 0.0, 2.0).onChange(updateMW);
mwFolder.add(this.mwParams, 'blackPoint', 0.0, 0.5).name('Black Point').onChange(updateMW);
mwFolder.add(this.mwParams, 'siderealTime', 0.0, 24.0).name('Sidereal Time').listen().onChange(updateMW);
@@ -464,18 +465,23 @@ class App {
const starFolder = gui.addFolder('Stars');
this.sParams = {
enabled: true,
density: 0.001
density: 0.001,
intensity: 0.0 // 2^0 = 1.0
};
// Initialize defaults (Density 0.001, Enabled True)
sky.setStarControl(0.001, true);
sky.setStarIntensity(Math.pow(2.0, 0.0));
const updateStars = () => {
sky.setStarControl(this.sParams.density, this.sParams.enabled);
sky.setStarIntensity(Math.pow(2.0, this.sParams.intensity));
};
starFolder.add(this.sParams, 'enabled').name('Enabled').onChange(updateStars);
starFolder.add(this.sParams, 'density', 0.0, 0.01, 0.0001).name('Density').onChange(updateStars);
starFolder.add(this.sParams, 'intensity', 0.0, 24.0).name('Intensity (Exp)').onChange(updateStars);
starFolder.close();
this.updateStars = updateStars;
const artFolder = gui.addFolder('Artistic');
// Set Horizon Glow default to 0.0
@@ -489,7 +495,7 @@ class App {
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
@@ -502,7 +508,7 @@ class App {
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, 'shutterSpeed', 0.05, 1000.0).onChange(() => this.updateCameraExposure());
camFolder.add(this.params, 'iso', 50.0, 3200.0).onChange(() => this.updateCameraExposure());
const bloomFolder = camFolder.addFolder('Bloom');
@@ -692,13 +698,15 @@ class App {
const s = this.sParams;
const b = this.bParams;
const m = this.mParams;
const mw = this.mwParams;
const sk = this.skybox;
return {
p: { a: p.aperture, ss: p.shutterSpeed, i: p.iso, st: p.sunTheta, sp: p.sunPhi, fl: p.focalLength, si: p.sunIntensity },
c: { v: c.volumetrics, co: c.coverage, d: c.density, h: c.height, s: c.speed, e: c.evolution },
w: { dt: w.derivativeTrick, st: w.strength, s: w.speed, o: w.octaves },
s: { e: s.enabled, d: s.density },
s: { e: s.enabled, d: s.density, i: s.intensity },
mw: { e: mw.enabled, i: mw.intensity, s: mw.saturation, bp: mw.blackPoint, st: mw.siderealTime, l: mw.latitude },
b: { e: b.enabled, lf: b.lensFlare },
m: { e: m.enabled, az: m.azimuth, h: m.height, r: m.radius, i: m.intensity },
cm: { t: this.camState.theta, p: this.camState.phi },
@@ -723,6 +731,7 @@ class App {
const c = state.c;
const w = state.w;
const s = state.s;
const mw = state.mw;
const b = state.b;
const m = state.m;
const k = state.k;
@@ -757,6 +766,18 @@ class App {
if (s) {
if (s.e !== undefined) this.sParams.enabled = s.e;
if (s.d !== undefined) this.sParams.density = s.d;
if (s.i !== undefined) this.sParams.intensity = s.i;
if (this.updateStars) this.updateStars();
}
if (mw) {
if (mw.e !== undefined) this.mwParams.enabled = mw.e;
if (mw.i !== undefined) this.mwParams.intensity = mw.i;
if (mw.s !== undefined) this.mwParams.saturation = mw.s;
if (mw.bp !== undefined) this.mwParams.blackPoint = mw.bp;
if (mw.st !== undefined) this.mwParams.siderealTime = mw.st;
if (mw.l !== undefined) this.mwParams.latitude = mw.l;
if (this.updateMW) this.updateMW();
}
if (b) {

View File

@@ -96,10 +96,20 @@ material {
name : starControl, // x=Density, y=Enabled, z=Frequency, w=PixelScale
precision : high
},
{
type : float,
name : starIntensity,
precision : high
},
{
type : sampler2d,
name : moonTexture
},
{
type : float,
name : exposure,
precision : high
},
{
type : sampler2d,
name : moonNormal
@@ -162,9 +172,9 @@ fragment {
// --- CONFIGURATION ---
// Stars
#define STAR_GLOBAL_INTENSITY 150.0 // Master brightness multiplier [0.0 - 500.0]
#define STAR_BRIGHTNESS_BASE 0.5 // Minimum random brightness [0.0 - 1.0]
#define STAR_BRIGHTNESS_VAR 4.0 // Random brightness variance range [0.0 - 10.0]
#define STAR_GLOBAL_INTENSITY 100.0 // Master brightness multiplier [0.0 - 500.0]
#define STAR_BRIGHTNESS_BASE 0.01 // Minimum random brightness [0.0 - 1.0]
#define STAR_BRIGHTNESS_VAR 15.0 // Random brightness variance range [0.0 - 10.0]
#define STAR_FADE_SUN_ELV_HIGH 0.10 // Sun elevation (sin) where stars are fully hidden [0.0 - 0.5]
#define STAR_FADE_SUN_ELV_LOW -0.20 // Sun elevation (sin) where stars are fully visible [-0.5 - 0.0]
#define STAR_CLOUD_OCCLUSION 0.1 // Visibility when covered by clouds [0.0 - 1.0]
@@ -961,7 +971,11 @@ fragment {
highp vec4 sunHalo, highp vec4 cloudControl, highp vec4 cloudControl2,
highp vec4 shimmerControl, highp vec4 waterControl,
highp vec3 L2, highp float sunIntensity2, highp vec4 sunHalo2,
sampler2D moonTex, sampler2D moonNormal) {
sampler2D moonTex, sampler2D moonNormal,
highp vec3 nightColor,
highp mat3 milkyWayRotation,
highp vec3 milkyWayControl,
sampler2D milkyWayTexture) {
// Project to plane y=0
highp float t = WATER_PLANE_HEIGHT / min(V.y, -0.0002); // Reduced clamp to minimize "wall" artifact
@@ -1057,7 +1071,7 @@ fragment {
highp float rHorizonMask = 1.0 - smoothstep(0.0, REFLECTION_HORIZON_FADE, R.y);
if (rHorizonMask > 0.0) {
reflection += getStarLayer(R, L, reflCloudDensity, outTransmittance, materialParams.starControl) * rHorizonMask;
reflection += getStarLayer(R, L, reflCloudDensity, outTransmittance, materialParams.starControl) * rHorizonMask * materialParams.exposure;
}
// Sun Disk Reflection
@@ -1088,6 +1102,29 @@ fragment {
// Apply clouds to reflection
reflection = mix(reflection, reflCloudLayer, reflCloudDensity);
// Add Milky Way to Reflection
// Calculate Fade based on Sun Elevation
highp float sunElvSin = L.y;
highp float mwFade = smoothstep(STAR_FADE_SUN_ELV_HIGH, STAR_FADE_SUN_ELV_LOW, sunElvSin);
if (mwFade > 0.0) {
highp vec3 mwColor = getMilkyWay(R, milkyWayRotation, milkyWayTexture, milkyWayControl);
// Apply Atmosphere Transmittance (approximate)
mwColor *= outTransmittance;
// Apply Fades
mwColor *= mwFade;
mwColor *= (1.0 - reflMoonOcclusion); // Occlude by Moon
mwColor *= (1.0 - reflCloudDensity); // Occlude by Clouds
reflection += mwColor;
}
// Add Night Color to Reflection
reflection += nightColor;
// Fresnel
highp float F0 = WATER_FRESNEL_F0; // Water
highp float cosTheta = clamp(dot(-V, N_water), 0.0, 1.0);
@@ -1187,7 +1224,7 @@ fragment {
// 7. Stars
// Add stars before clouds (clouds cover stars)
highp vec3 starColor = getStarLayer(V, L, cloudDensityVal, transmittance, materialParams.starControl);
highp vec3 starColor = getStarLayer(V, L, cloudDensityVal, transmittance, materialParams.starControl) * materialParams.exposure * materialParams.starIntensity;
// 7b. Milky Way
// Add Milky Way behind stars (conceptually) but handled similarly
@@ -1232,7 +1269,11 @@ fragment {
materialParams.shimmerControl,
materialParams.waterControl,
L2, materialParams.sunIntensity2, materialParams.sunHalo2,
materialParams_moonTexture, materialParams_moonNormal);
materialParams_moonTexture, materialParams_moonNormal,
materialParams.nightColor,
materialParams.milkyWayRotation,
materialParams.milkyWayControl,
materialParams_milkyWayTexture);
color = applyDynamicToneMapping(color, L, materialParams.contrast);
}

View File

@@ -578,6 +578,7 @@ if (APPLE OR LINUX)
test/Shader.cpp
test/SharedShaders.cpp
test/Skip.cpp
test/test_Autoresolve.cpp
test/test_FeedbackLoops.cpp
test/test_Blit.cpp
test/test_MissingRequiredAttributes.cpp

View File

@@ -1092,7 +1092,7 @@ MetalRenderTarget::MetalRenderTarget(MetalContext* context, uint32_t width, uint
// a multisampled sidecar texture and do a resolve automatically.
if (samples > 1 && texture->samples == 1) {
auto& sidecar = texture->msaaSidecar;
if (!sidecar) {
if (!sidecar || sidecar.sampleCount != samples) {
sidecar = createMultisampledTexture(mtlTexture.pixelFormat, texture->width,
texture->height, samples);
}
@@ -1123,7 +1123,7 @@ MetalRenderTarget::MetalRenderTarget(MetalContext* context, uint32_t width, uint
// a multisampled sidecar texture and do a resolve automatically.
if (samples > 1 && texture->samples == 1) {
auto& sidecar = texture->msaaSidecar;
if (!sidecar) {
if (!sidecar || sidecar.sampleCount != samples) {
sidecar = createMultisampledTexture(mtlTexture.pixelFormat, texture->width,
texture->height, samples);
}
@@ -1155,7 +1155,7 @@ MetalRenderTarget::MetalRenderTarget(MetalContext* context, uint32_t width, uint
// a multisampled sidecar texture and do a resolve automatically.
if (samples > 1 && texture->samples == 1) {
auto& sidecar = texture->msaaSidecar;
if (!sidecar) {
if (!sidecar || sidecar.sampleCount != samples) {
sidecar = createMultisampledTexture(mtlTexture.pixelFormat, texture->width,
texture->height, samples);
}

View File

@@ -3623,6 +3623,8 @@ void OpenGLDriver::detachStream(GLTexture* t) noexcept {
case StreamType::NATIVE:
mPlatform.detach(t->hwStream->stream);
// ^ this deletes the texture id
// We still need to call unbind to update the bookkeeping.
gl.unbindTexture(t->gl.target, t->gl.id);
break;
case StreamType::ACQUIRED:
gl.unbindTexture(t->gl.target, t->gl.id);

View File

@@ -120,33 +120,27 @@ bool PlatformEGL::isOpenGL() const noexcept {
PlatformEGL::ExternalImageEGL::~ExternalImageEGL() = default;
Driver* PlatformEGL::createDriver(void* sharedContext, const DriverConfig& driverConfig) {
static constexpr int kMaxNumEGLDevices = 32;
mEGLDisplay = eglGetDisplay(EGL_DEFAULT_DISPLAY);
assert_invariant(mEGLDisplay != EGL_NO_DISPLAY);
EGLint major, minor;
EGLBoolean initialized = false;
EGLBoolean initialized = eglInitialize(mEGLDisplay, &major, &minor);
PFNEGLQUERYDEVICESEXTPROC const eglQueryDevicesEXT =
PFNEGLQUERYDEVICESEXTPROC(eglGetProcAddress("eglQueryDevicesEXT"));
PFNEGLGETPLATFORMDISPLAYEXTPROC const getPlatformDisplay =
PFNEGLGETPLATFORMDISPLAYEXTPROC(eglGetProcAddress("eglGetPlatformDisplay"));
if (eglQueryDevicesEXT != nullptr && getPlatformDisplay != nullptr) {
EGLint numDevices = 0;
EGLDeviceEXT eglDevices[kMaxNumEGLDevices];
if (eglQueryDevicesEXT(kMaxNumEGLDevices, eglDevices, &numDevices)) {
for (int i = 0; i < numDevices && !initialized; ++i) {
mEGLDisplay = getPlatformDisplay(EGL_PLATFORM_DEVICE_EXT, eglDevices[i], nullptr);
if (!initialized) {
EGLDeviceEXT eglDevice;
EGLint numDevices;
PFNEGLQUERYDEVICESEXTPROC const eglQueryDevicesEXT =
PFNEGLQUERYDEVICESEXTPROC(eglGetProcAddress("eglQueryDevicesEXT"));
if (eglQueryDevicesEXT != nullptr) {
eglQueryDevicesEXT(1, &eglDevice, &numDevices);
if(auto* getPlatformDisplay = reinterpret_cast<PFNEGLGETPLATFORMDISPLAYEXTPROC>(
eglGetProcAddress("eglGetPlatformDisplay"))) {
mEGLDisplay = getPlatformDisplay(EGL_PLATFORM_DEVICE_EXT, eglDevice, nullptr);
initialized = eglInitialize(mEGLDisplay, &major, &minor);
}
}
}
if (!initialized) {
mEGLDisplay = eglGetDisplay(EGL_DEFAULT_DISPLAY);
assert_invariant(mEGLDisplay != EGL_NO_DISPLAY);
initialized = eglInitialize(mEGLDisplay, &major, &minor);
}
if (UTILS_UNLIKELY(!initialized)) {
LOG(ERROR) << "eglInitialize failed";
return nullptr;

View File

@@ -36,6 +36,7 @@ VK_DEFINE_HANDLE(VmaPool)
namespace filament::backend {
struct VulkanCommandBuffer;
struct VulkanRenderPass;
struct VulkanRenderTarget;
struct VulkanSwapChain;
struct VulkanTexture;
@@ -58,11 +59,11 @@ struct VulkanAttachment {
VkImageSubresourceRange getSubresourceRange() const;
};
struct VulkanRenderPass {
struct VulkanRenderPassContext {
// Between the begin and end command render pass we cache the command buffer
VulkanCommandBuffer* commandBuffer;
fvkmemory::resource_ptr<VulkanRenderTarget> renderTarget;
VkRenderPass renderPass;
fvkmemory::resource_ptr<VulkanRenderPass> renderPass;
RenderPassParams params;
int currentSubpass;
};

View File

@@ -1975,17 +1975,19 @@ void VulkanDriver::beginRenderPass(Handle<HwRenderTarget> rth, const RenderPassP
rpkey.initialDepthLayout = currentDepthLayout;
rpkey.subpassMask = uint8_t(params.subpassMask);
VkRenderPass renderPass = mFramebufferCache.getRenderPass(rpkey);
fvkmemory::resource_ptr<VulkanRenderPass> renderPass =
mFramebufferCache.getRenderPass(rpkey, &mResourceManager);
mPipelineCache.bindRenderPass(renderPass, 0);
// Create the VkFramebuffer or fetch it from cache.
VulkanFboCache::FboKey fbkey = rt->getFboKey();
fbkey.renderPass = renderPass;
fbkey.renderPass = renderPass->getVkRenderPass();
fbkey.layers = 1;
rt->emitBarriersBeginRenderPass(*commandBuffer);
VkFramebuffer vkfb = mFramebufferCache.getFramebuffer(fbkey);
fvkmemory::resource_ptr<VulkanFramebuffer> vkfb =
mFramebufferCache.getFramebuffer(fbkey, &mResourceManager);
// Assign a label to the framebuffer for debugging purposes.
#if FVK_ENABLED(FVK_DEBUG_GROUP_MARKERS | FVK_DEBUG_DEBUG_UTILS)
@@ -1998,12 +2000,14 @@ void VulkanDriver::beginRenderPass(Handle<HwRenderTarget> rth, const RenderPassP
// The current command buffer now has references to the render target and its attachments.
commandBuffer->acquire(rt);
commandBuffer->acquire(renderPass);
commandBuffer->acquire(vkfb);
// Populate the structures required for vkCmdBeginRenderPass.
VkRenderPassBeginInfo renderPassInfo {
.sType = VK_STRUCTURE_TYPE_RENDER_PASS_BEGIN_INFO,
.renderPass = renderPass,
.framebuffer = vkfb,
.renderPass = renderPass->getVkRenderPass(),
.framebuffer = vkfb->getVkFramebuffer(),
// The renderArea field constrains the LoadOp, but scissoring does not.
// Therefore, we do not set the scissor rect here, we only need it in draw().
@@ -2058,7 +2062,7 @@ void VulkanDriver::beginRenderPass(Handle<HwRenderTarget> rth, const RenderPassP
mCurrentRenderPass = {
.commandBuffer = commandBuffer,
.renderTarget = rt,
.renderPass = renderPassInfo.renderPass,
.renderPass = renderPass,
.params = params,
.currentSubpass = 0,
};
@@ -2078,8 +2082,7 @@ void VulkanDriver::endRenderPass(int) {
rt->emitBarriersEndRenderPass(*mCurrentRenderPass.commandBuffer);
mCurrentRenderPass.renderTarget = {};
mCurrentRenderPass.renderPass = VK_NULL_HANDLE;
mCurrentRenderPass.renderPass = {};
mCurrentRenderPass.commandBuffer = nullptr;
}
@@ -2244,7 +2247,7 @@ void VulkanDriver::resolve(
Handle<HwTexture> src, uint8_t dstLevel, uint8_t dstLayer) {
FVK_SYSTRACE_SCOPE();
FILAMENT_CHECK_PRECONDITION(mCurrentRenderPass.renderPass == VK_NULL_HANDLE)
FILAMENT_CHECK_PRECONDITION(!mCurrentRenderPass.renderPass)
<< "resolve() cannot be invoked inside a render pass.";
auto srcTexture = resource_ptr<VulkanTexture>::cast(&mResourceManager, src);
@@ -2287,7 +2290,7 @@ void VulkanDriver::blit(
math::uint2 size) {
FVK_SYSTRACE_SCOPE();
FILAMENT_CHECK_PRECONDITION(mCurrentRenderPass.renderPass == VK_NULL_HANDLE)
FILAMENT_CHECK_PRECONDITION(!mCurrentRenderPass.renderPass)
<< "blit() cannot be invoked inside a render pass.";
auto srcTexture = resource_ptr<VulkanTexture>::cast(&mResourceManager, src);
@@ -2329,7 +2332,7 @@ void VulkanDriver::blitDEPRECATED(TargetBufferFlags buffers,
// Note: blitDEPRECATED is only used for Renderer::copyFrame()
FILAMENT_CHECK_PRECONDITION(mCurrentRenderPass.renderPass == VK_NULL_HANDLE)
FILAMENT_CHECK_PRECONDITION(!mCurrentRenderPass.renderPass)
<< "blitDEPRECATED() cannot be invoked inside a render pass.";
FILAMENT_CHECK_PRECONDITION(buffers == TargetBufferFlags::COLOR0)

View File

@@ -139,7 +139,7 @@ private:
resource_ptr<VulkanSwapChain> mCurrentSwapChain;
resource_ptr<VulkanRenderTarget> mDefaultRenderTarget;
VulkanRenderPass mCurrentRenderPass = {};
VulkanRenderPassContext mCurrentRenderPass = {};
VmaAllocator mAllocator = VK_NULL_HANDLE;
VkDebugReportCallbackEXT mDebugCallback = VK_NULL_HANDLE;

View File

@@ -17,6 +17,7 @@
#include "VulkanFboCache.h"
#include "VulkanConstants.h"
#include "VulkanHandles.h"
#include "vulkan/utils/Image.h"
#include <utils/Panic.h>
@@ -69,9 +70,10 @@ VulkanFboCache::~VulkanFboCache() {
<< "Please explicitly call terminate() while the VkDevice is still alive.";
}
VkFramebuffer VulkanFboCache::getFramebuffer(FboKey const& config) noexcept {
fvkmemory::resource_ptr<VulkanFramebuffer> VulkanFboCache::getFramebuffer(
FboKey const& config, fvkmemory::ResourceManager* resManager) noexcept {
FboMap::iterator iter = mFramebufferCache.find(config);
if (UTILS_LIKELY(iter != mFramebufferCache.end() && iter->second.handle != VK_NULL_HANDLE)) {
if (UTILS_LIKELY(iter != mFramebufferCache.end())) {
iter.value().timestamp = mCurrentTime;
return iter->second.handle;
}
@@ -117,13 +119,16 @@ VkFramebuffer VulkanFboCache::getFramebuffer(FboKey const& config) noexcept {
VkResult error = vkCreateFramebuffer(mDevice, &info, VKALLOC, &framebuffer);
FILAMENT_CHECK_POSTCONDITION(error == VK_SUCCESS) << "Unable to create framebuffer."
<< " error=" << static_cast<int32_t>(error);
mFramebufferCache[config] = {framebuffer, mCurrentTime};
return framebuffer;
fvkmemory::resource_ptr<VulkanFramebuffer> fbh =
fvkmemory::resource_ptr<VulkanFramebuffer>::construct(resManager, mDevice, framebuffer);
mFramebufferCache[config] = {fbh, mCurrentTime};
return fbh;
}
VkRenderPass VulkanFboCache::getRenderPass(RenderPassKey const& config) noexcept {
fvkmemory::resource_ptr<VulkanRenderPass> VulkanFboCache::getRenderPass(
RenderPassKey const& config, fvkmemory::ResourceManager* resManager) noexcept {
auto iter = mRenderPassCache.find(config);
if (UTILS_LIKELY(iter != mRenderPassCache.end() && iter->second.handle != VK_NULL_HANDLE)) {
if (UTILS_LIKELY(iter != mRenderPassCache.end())) {
iter.value().timestamp = mCurrentTime;
return iter->second.handle;
}
@@ -326,7 +331,9 @@ VkRenderPass VulkanFboCache::getRenderPass(RenderPassKey const& config) noexcept
VkResult error = vkCreateRenderPass(mDevice, &renderPassInfo, VKALLOC, &renderPass);
FILAMENT_CHECK_POSTCONDITION(error == VK_SUCCESS) << "Unable to create render pass."
<< " error=" << error;
mRenderPassCache[config] = {renderPass, mCurrentTime};
fvkmemory::resource_ptr<VulkanRenderPass> rph =
fvkmemory::resource_ptr<VulkanRenderPass>::construct(resManager, mDevice, renderPass);
mRenderPassCache[config] = {rph, mCurrentTime};
#if FVK_ENABLED(FVK_DEBUG_FBO_CACHE)
FVK_LOGD << "Created render pass " << renderPass << " with ";
@@ -343,13 +350,12 @@ VkRenderPass VulkanFboCache::getRenderPass(RenderPassKey const& config) noexcept
<< "colorAttachmentCount[0] = " << subpasses[0].colorAttachmentCount;
#endif
return renderPass;
return rph;
}
void VulkanFboCache::resetFramebuffers() noexcept {
for (const auto& pair: mFramebufferCache) {
mRenderPassRefCount[pair.first.renderPass]--;
vkDestroyFramebuffer(mDevice, pair.second.handle, VKALLOC);
}
mFramebufferCache.clear();
}
@@ -357,9 +363,6 @@ void VulkanFboCache::resetFramebuffers() noexcept {
void VulkanFboCache::terminate() noexcept {
resetFramebuffers();
for (const auto& pair: mRenderPassCache) {
vkDestroyRenderPass(mDevice, pair.second.handle, VKALLOC);
}
mRenderPassRefCount.clear();
mRenderPassCache.clear();
}
@@ -380,8 +383,6 @@ void VulkanFboCache::gc() noexcept {
const FboVal fbo = iter->second;
if (fbo.timestamp < evictTime && fbo.handle) {
mRenderPassRefCount[iter->first.renderPass]--;
vkDestroyFramebuffer(mDevice, fbo.handle, VKALLOC);
iter.value().handle = VK_NULL_HANDLE;
// erase(iterator) returns the iterator to the next element.
iter = mFramebufferCache.erase(iter);
@@ -391,11 +392,8 @@ void VulkanFboCache::gc() noexcept {
}
for (RenderPassMap::iterator iter = mRenderPassCache.begin(); iter != mRenderPassCache.end(); ) {
const VkRenderPass handle = iter->second.handle;
const VkRenderPass handle = iter->second.handle->getVkRenderPass();
if (iter->second.timestamp < evictTime && handle && mRenderPassRefCount[handle] == 0) {
vkDestroyRenderPass(mDevice, handle, VKALLOC);
iter.value().handle = VK_NULL_HANDLE;
// erase(iterator) returns the iterator to the next element.
iter = mRenderPassCache.erase(iter);
mRenderPassRefCount.erase(handle);

View File

@@ -18,6 +18,9 @@
#define TNT_FILAMENT_BACKEND_VULKANFBOCACHE_H
#include "VulkanContext.h"
#include "vulkan/memory/Resource.h"
#include "vulkan/memory/ResourceManager.h"
#include "vulkan/memory/ResourcePointer.h"
#include <utils/Hash.h>
@@ -27,6 +30,9 @@
namespace filament::backend {
struct VulkanFramebuffer;
struct VulkanRenderPass;
// Simple manager for VkFramebuffer and VkRenderPass objects.
//
// Note that a VkFramebuffer is just a binding between a render pass and a set of image views. So,
@@ -57,7 +63,7 @@ public:
uint8_t padding[2];
};
struct RenderPassVal {
VkRenderPass handle;
fvkmemory::resource_ptr<VulkanRenderPass> handle;
uint32_t timestamp;
};
static_assert(0 == MRT::MAX_SUPPORTED_RENDER_TARGET_COUNT % 8);
@@ -83,7 +89,7 @@ public:
VkImageView depth; // 8 bytes
};
struct FboVal {
VkFramebuffer handle;
fvkmemory::resource_ptr<VulkanFramebuffer> handle;
uint32_t timestamp;
};
static_assert(sizeof(VkRenderPass) == 8, "VkRenderPass has unexpected size.");
@@ -98,10 +104,12 @@ public:
~VulkanFboCache();
// Retrieves or creates a VkFramebuffer handle.
VkFramebuffer getFramebuffer(FboKey const& config) noexcept;
fvkmemory::resource_ptr<VulkanFramebuffer> getFramebuffer(
FboKey const& config, fvkmemory::ResourceManager* resManager) noexcept;
// Retrieves or creates a VkRenderPass handle.
VkRenderPass getRenderPass(RenderPassKey const& config) noexcept;
fvkmemory::resource_ptr<VulkanRenderPass> getRenderPass(
RenderPassKey const& config, fvkmemory::ResourceManager* resManager) noexcept;
// Evicts old unused Vulkan objects. Call this once per frame.
void gc() noexcept;

View File

@@ -565,7 +565,7 @@ void VulkanRenderTarget::transformViewportToPlatform(VkViewport* bounds) const {
flipVertically(bounds, getExtent().height);
}
uint8_t VulkanRenderTarget::getColorTargetCount(const VulkanRenderPass& pass) const {
uint8_t VulkanRenderTarget::getColorTargetCount(const VulkanRenderPassContext& pass) const {
if (!mOffscreen) {
return 1;
}
@@ -715,4 +715,18 @@ VulkanRenderPrimitive::VulkanRenderPrimitive(PrimitiveType pt,
vertexBuffer(vb),
indexBuffer(ib) {}
VulkanFramebuffer::VulkanFramebuffer(VkDevice device, VkFramebuffer framebuffer)
: mDevice(device), mFramebuffer(framebuffer) {}
VulkanFramebuffer::~VulkanFramebuffer() {
vkDestroyFramebuffer(mDevice, mFramebuffer, VKALLOC);
}
VulkanRenderPass::VulkanRenderPass(VkDevice device, VkRenderPass renderPass)
: mDevice(device), mRenderPass(renderPass) {}
VulkanRenderPass::~VulkanRenderPass() {
vkDestroyRenderPass(mDevice, mRenderPass, VKALLOC);
}
} // namespace filament::backend

View File

@@ -419,7 +419,7 @@ struct VulkanRenderTarget : private HwRenderTarget, fvkmemory::Resource {
return mInfo->fbkey.samples;
}
uint8_t getColorTargetCount(VulkanRenderPass const& pass) const;
uint8_t getColorTargetCount(VulkanRenderPassContext const& pass) const;
inline bool hasDepth() const { return mInfo->depthIndex != Auxiliary::UNDEFINED_INDEX; }
@@ -585,6 +585,32 @@ struct VulkanRenderPrimitive : public HwRenderPrimitive, fvkmemory::Resource {
fvkmemory::resource_ptr<VulkanIndexBuffer> indexBuffer;
};
struct VulkanFramebuffer : public fvkmemory::Resource {
VulkanFramebuffer(VkDevice device, VkFramebuffer framebuffer);
~VulkanFramebuffer();
inline VkFramebuffer getVkFramebuffer() const noexcept {
return mFramebuffer;
}
private:
VkDevice mDevice;
VkFramebuffer mFramebuffer;
};
struct VulkanRenderPass : public fvkmemory::Resource {
VulkanRenderPass(VkDevice device, VkRenderPass renderPass);
~VulkanRenderPass();
inline VkRenderPass getVkRenderPass() const noexcept {
return mRenderPass;
}
private:
VkDevice mDevice;
VkRenderPass mRenderPass;
};
} // namespace filament::backend
#endif // TNT_FILAMENT_BACKEND_VULKANHANDLES_H

View File

@@ -459,8 +459,10 @@ void VulkanPipelineCache::bindStencilState(StencilState const& stencilState) noe
mPipelineRequirements.stencilState = stencilState;
}
void VulkanPipelineCache::bindRenderPass(VkRenderPass renderPass, int subpassIndex) noexcept {
mPipelineRequirements.renderPass = renderPass;
void VulkanPipelineCache::bindRenderPass(
fvkmemory::resource_ptr<VulkanRenderPass> renderPass,
int subpassIndex) noexcept {
mPipelineRequirements.renderPass = renderPass->getVkRenderPass();
mPipelineRequirements.subpassIndex = subpassIndex;
}

View File

@@ -123,7 +123,8 @@ public:
void bindProgram(fvkmemory::resource_ptr<VulkanProgram> program) noexcept;
void bindRasterState(RasterState const& rasterState) noexcept;
void bindStencilState(StencilState const& stencilState) noexcept;
void bindRenderPass(VkRenderPass renderPass, int subpassIndex) noexcept;
void bindRenderPass(fvkmemory::resource_ptr<VulkanRenderPass> renderPass,
int subpassIndex) noexcept;
void bindPrimitiveTopology(VkPrimitiveTopology topology) noexcept;
void bindVertexArray(VkVertexInputAttributeDescription const* attribDesc,
VkVertexInputBindingDescription const* bufferDesc, uint8_t count);

View File

@@ -42,6 +42,8 @@ template ResourceType getTypeEnum<VulkanSync>() noexcept;
template ResourceType getTypeEnum<VulkanMemoryMappedBuffer>() noexcept;
template ResourceType getTypeEnum<VulkanSemaphore>() noexcept;
template ResourceType getTypeEnum<VulkanStream>() noexcept;
template ResourceType getTypeEnum<VulkanFramebuffer>() noexcept;
template ResourceType getTypeEnum<VulkanRenderPass>() noexcept;
template<typename D>
ResourceType getTypeEnum() noexcept {
@@ -108,6 +110,12 @@ ResourceType getTypeEnum() noexcept {
if constexpr (std::is_same_v<D, VulkanStream>) {
return ResourceType::STREAM;
}
if constexpr (std::is_same_v<D, VulkanFramebuffer>) {
return ResourceType::FRAMEBUFFER;
}
if constexpr (std::is_same_v<D, VulkanRenderPass>) {
return ResourceType::RENDER_PASS;
}
return ResourceType::UNDEFINED_TYPE;
}
@@ -155,6 +163,10 @@ std::string_view getTypeStr(ResourceType type) {
return "Semaphore";
case ResourceType::STREAM:
return "VulkanStream";
case ResourceType::FRAMEBUFFER:
return "Framebuffer";
case ResourceType::RENDER_PASS:
return "RenderPass";
case ResourceType::UNDEFINED_TYPE:
return "";
}

View File

@@ -56,7 +56,9 @@ enum class ResourceType : uint8_t {
MEMORY_MAPPED_BUFFER = 18,
SEMAPHORE = 19,
STREAM = 20,
UNDEFINED_TYPE = 21, // Must be the last enum because we use it for iterating over the enums.
FRAMEBUFFER = 21,
RENDER_PASS = 22,
UNDEFINED_TYPE = 23, // Must be the last enum because we use it for iterating over the enums.
};
template<typename D>

View File

@@ -126,6 +126,12 @@ void ResourceManager::destroyWithType(ResourceType type, HandleId id) {
case ResourceType::STREAM:
destruct<VulkanStream>(Handle<VulkanStream>(id));
break;
case ResourceType::FRAMEBUFFER:
destruct<VulkanFramebuffer>(Handle<VulkanFramebuffer>(id));
break;
case ResourceType::RENDER_PASS:
destruct<VulkanRenderPass>(Handle<VulkanRenderPass>(id));
break;
case ResourceType::UNDEFINED_TYPE:
break;
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.7 KiB

View File

@@ -0,0 +1,120 @@
/*
* 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 "BackendTest.h"
#include "Shader.h"
#include "SharedShaders.h"
#include "Skip.h"
#include "TrianglePrimitive.h"
#include <backend/PixelBufferDescriptor.h>
#include <utils/FixedCapacityVector.h>
namespace test {
using namespace filament;
using namespace filament::backend;
using namespace filament::math;
TEST_F(BackendTest, AutoresolveDifferingSampleCounts) {
SKIP_IF(SkipEnvironment(OperatingSystem::CI, Backend::OPENGL), "see b/486954356");
SKIP_IF(SkipEnvironment(OperatingSystem::CI, Backend::VULKAN), "see b/486954356");
auto& api = getDriverApi();
constexpr int kRenderTargetSize = 512;
auto swapChain = addCleanup(createSwapChain());
api.makeCurrent(swapChain, swapChain);
Shader shader = SharedShaders::makeShader(api, *mCleanup,
{
.mVertexType = VertexShaderType::Simple,
.mFragmentType = FragmentShaderType::SolidColored,
.mUniformType = ShaderUniformType::Simple,
});
TrianglePrimitive triangle(api);
PipelineState ps = getColorWritePipelineState();
shader.addProgramToPipelineState(ps);
auto ubuffer = addCleanup(api.createBufferObject(sizeof(SimpleMaterialParams),
BufferObjectBinding::UNIFORM, BufferUsage::STATIC));
shader.bindUniform<SimpleMaterialParams>(api, ubuffer);
// Create a texture with sample count = 1.
Handle<HwTexture> texture = addCleanup(api.createTexture(SamplerType::SAMPLER_2D, 1,
TextureFormat::RGBA8, 1, kRenderTargetSize, kRenderTargetSize, 1,
TextureUsage::COLOR_ATTACHMENT | TextureUsage::SAMPLEABLE));
// First render pass: render a red triangle to a RenderTarget with 8 samples.
{
Handle<HwRenderTarget> renderTarget =
addCleanup(api.createRenderTarget(TargetBufferFlags::COLOR, kRenderTargetSize,
kRenderTargetSize, 8, 1, { { texture } }, {}, {}));
shader.uploadUniform(api, ubuffer,
SimpleMaterialParams{
.color = float4(1, 0, 0, 1),
});
RenderPassParams params = getClearColorRenderPass(float4(0));
params.viewport = { 0, 0, kRenderTargetSize, kRenderTargetSize };
RenderFrame frame(api);
api.beginRenderPass(renderTarget, params);
ps.primitiveType = PrimitiveType::TRIANGLES;
ps.vertexBufferInfo = triangle.getVertexBufferInfo();
api.bindPipeline(ps);
api.bindRenderPrimitive(triangle.getRenderPrimitive());
api.draw2(0, 3, 1);
api.endRenderPass();
api.commit(swapChain);
}
// Second render pass: render a green triangle to a RenderTarget with 4 samples, attached to
// the same texture.
{
Handle<HwRenderTarget> renderTarget =
addCleanup(api.createRenderTarget(TargetBufferFlags::COLOR, kRenderTargetSize,
kRenderTargetSize, 4, 1, { { texture } }, {}, {}));
shader.uploadUniform(api, ubuffer,
SimpleMaterialParams{
.color = float4(0, 1, 0, 1),
});
RenderPassParams params = getClearColorRenderPass(float4(0));
params.viewport = { 0, 0, kRenderTargetSize, kRenderTargetSize };
RenderFrame frame(api);
api.beginRenderPass(renderTarget, params);
ps.primitiveType = PrimitiveType::TRIANGLES;
ps.vertexBufferInfo = triangle.getVertexBufferInfo();
api.bindPipeline(ps);
api.bindRenderPrimitive(triangle.getRenderPrimitive());
api.draw2(0, 3, 1);
api.endRenderPass();
EXPECT_IMAGE(renderTarget, ScreenshotParams(kRenderTargetSize, kRenderTargetSize,
"AutoresolveDifferingSampleCounts", 1048576));
api.commit(swapChain);
}
}
} // namespace test

View File

@@ -798,16 +798,19 @@ RenderPass::Command* RenderPass::generateCommandsImpl(CommandTypeFlags extraFlag
cmd.info.rasterState.culling = cullingMode;
// FIXME: should writeDepthForShadowCasters take precedence over mi->getDepthWrite()?
cmd.info.rasterState.depthWrite = (1 // only keep bit 0
& (mi->isDepthWriteEnabled() | (mode == TransparencyMode::TWO_PASSES_ONE_SIDE)
| isPickingVariant)
& !(filterTranslucentObjects & translucent)
& !(depthFilterAlphaMaskedObjects & rs.alphaToCoverage))
| writeDepthForShadowCasters;
cmd.info.rasterState.depthWrite =
(1 // only keep bit 0
& (mi->isDepthWriteEnabled() |
(mode == TransparencyMode::TWO_PASSES_ONE_SIDE) |
isPickingVariant) &
!(depthFilterAlphaMaskedObjects & rs.alphaToCoverage)) |
writeDepthForShadowCasters;
*curr = cmd;
// cancel command if both front and back faces are culled
curr->key |= select(cullingMode == CullingMode::FRONT_AND_BACK);
// cancel command if asked to filter translucent objects
curr->key |= select(filterTranslucentObjects & translucent);
}
++curr;

View File

@@ -798,6 +798,12 @@ void FEngine::submitFrame() {
void FEngine::flush() {
// flush the command buffer
flushCommandBuffer(mCommandBufferQueue);
// In single-threaded mode, we have to call execute() to drain the command
// buffer to really free up space
if constexpr (!UTILS_HAS_THREADING) {
execute();
}
}
void FEngine::flushAndWait() {

View File

@@ -378,8 +378,14 @@ private:
Variant variant = {}) const noexcept;
bool isSharedVariant(Variant const variant) const {
return (mDefinition.materialDomain == MaterialDomain::SURFACE) && !mIsDefaultMaterial &&
!mDefinition.hasCustomDepthShader && Variant::isValidDepthVariant(variant);
// HACK: The default material "should" have VSM | DEP, but then we'd have to compile it as a
// lit material, which would increase binary size. Perhaps we could specially compile it
// with this variant, but with the shader program cache in active development, the days of
// the default material are numbered anyway.
constexpr Variant::type_t vsmAndDep = Variant::VSM | Variant::DEP;
return mDefinition.materialDomain == MaterialDomain::SURFACE && !mIsDefaultMaterial &&
!mDefinition.hasCustomDepthShader && Variant::isValidDepthVariant(variant) &&
(variant.key & vsmAndDep) != vsmAndDep;
}
mutable utils::FixedCapacityVector<backend::Handle<backend::HwProgram>> mCachedPrograms;

View File

@@ -1,12 +1,12 @@
Pod::Spec.new do |spec|
spec.name = "Filament"
spec.version = "1.69.3"
spec.version = "1.69.5"
spec.license = { :type => "Apache 2.0", :file => "LICENSE" }
spec.homepage = "https://google.github.io/filament"
spec.authors = "Google LLC."
spec.summary = "Filament is a real-time physically based rendering engine for Android, iOS, Windows, Linux, macOS, and WASM/WebGL."
spec.platform = :ios, "11.0"
spec.source = { :http => "https://github.com/google/filament/releases/download/v1.69.3/filament-v1.69.3-ios.tgz" }
spec.source = { :http => "https://github.com/google/filament/releases/download/v1.69.5/filament-v1.69.5-ios.tgz" }
spec.libraries = 'c++'

View File

@@ -60,11 +60,13 @@
#define GLTFIO_WARN(msg) slog.w << msg << io::endl
#endif
#ifndef GLTFIO_USE_FILESYSTEM
#if defined(__EMSCRIPTEN__) || defined(__ANDROID__) || defined(FILAMENT_IOS)
#define GLTFIO_USE_FILESYSTEM 0
#else
#define GLTFIO_USE_FILESYSTEM 1
#endif
#endif
namespace utils {
class NameComponentManager;

View File

@@ -94,7 +94,7 @@ public:
} backend;
struct {
bool check_crc32_after_loading = false;
bool enable_material_instance_uniform_batching = true;
bool enable_material_instance_uniform_batching = false;
bool enable_fog_as_postprocess = false;
} material;
} features;

View File

@@ -114,6 +114,24 @@ Doing the above has multiple effects:
- If the PR is merged, then there is another workflow that will merge `my-pr-branch-golden`
to the `main` branch of the golden repo.
### Automated update via commit message
Alternatively, if you are confident in your changes and want the CI to handle the update
for you, you can use the following tag in your commit message:
- In the commit message of your working branch on `filament`, add the following line:
```
RDIFF_ACCEPT_NEW_GOLDENS
```
This has the following effects:
- The presubmit test `test-renderdiff` will be bypassed (it will not perform rendering or
comparison).
- When the PR is merged, the postsubmit CI will automatically:
1. Build Filament and generate the new images.
2. Upload them to a temporary branch in `filament-assets`.
3. Merge that branch into `main`.
## Viewing test results
We provide a viewer for looking at the result of a test run. The viewer is a webapp that can

View File

@@ -18,6 +18,7 @@ import re
from utils import execute, ArgParseImpl
RDIFF_UPDATE_GOLDEN_STR = 'RDIFF_BRANCH'
RDIFF_ACCEPT_NEW_GOLDENS_STR = 'RDIFF_ACCEPT_NEW_GOLDENS'
def _parse_commit(commit_str):
lines = commit_str.split('\n')
@@ -42,9 +43,11 @@ def _parse_commit(commit_str):
if __name__ == "__main__":
RE_STR = rf"{RDIFF_UPDATE_GOLDEN_STR}(?:\s)?\=(?:\s)?([a-zA-Z0-9\s\-\/]+)"
RE_ACCEPT = rf"^{RDIFF_ACCEPT_NEW_GOLDENS_STR}$"
parser = ArgParseImpl()
parser.add_argument('--file', help='A file containing the commit message')
parser.add_argument('--mode', choices=['branch', 'accept_new_goldens'], default='branch', help='Mode of operation')
args, _ = parser.parse_known_args(sys.argv[1:])
if not args.file:
@@ -53,14 +56,21 @@ if __name__ == "__main__":
with open(args.file, 'r') as f:
msg = f.read()
to_update = []
commit, title, description = _parse_commit(msg)
if args.mode == 'accept_new_goldens':
for line in description:
if re.match(RE_ACCEPT, line):
sys.exit(0)
sys.exit(1)
# args.mode == 'branch'
for line in description:
m = re.match(RE_STR, line)
if not m:
continue
print(m.group(1))
exit(0)
sys.exit(0)
# Always default to the main branch
print('main')

View File

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