Compare commits

..
69 changed files with 649 additions and 808 deletions
@@ -1,26 +0,0 @@
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
+7 -32
View File
@@ -10,27 +10,16 @@ jobs:
# a branch on filament-assets. # a branch on filament-assets.
update-renderdiff-goldens: update-renderdiff-goldens:
name: update-renderdiff-goldens name: update-renderdiff-goldens
runs-on: 'macos-14-xlarge' runs-on: 'ubuntu-24.04-4core'
steps: steps:
- uses: actions/checkout@v4.1.6 - uses: actions/checkout@v4.1.6
with: with:
fetch-depth: 0 fetch-depth: 0
- uses: ./.github/actions/linux-prereq
- id: get_commit_msg - id: get_commit_msg
uses: ./.github/actions/get-commit-msg uses: ./.github/actions/get-commit-msg
- name: Check if accepting new goldens - name: Build diffimg
id: check_accept run: ./build.sh release diffimg
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 - name: Run update script
env: env:
GH_TOKEN: ${{ secrets.FILAMENTBOT_TOKEN }} GH_TOKEN: ${{ secrets.FILAMENTBOT_TOKEN }}
@@ -38,24 +27,10 @@ jobs:
run: | run: |
GOLDEN_BRANCH=$(echo "${COMMIT_MESSAGE}" | python3 test/renderdiff/src/commit_msg.py) GOLDEN_BRANCH=$(echo "${COMMIT_MESSAGE}" | python3 test/renderdiff/src/commit_msg.py)
COMMIT_HASH="${{ steps.get_commit_msg.outputs.hash }}" 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 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 "branch==${GOLDEN_BRANCH}"
echo "hash==${COMMIT_HASH}" echo "hash==${COMMIT_HASH}"
python3 test/renderdiff/src/update_golden.py --branch=${GOLDEN_BRANCH} \ python3 test/renderdiff/src/update_golden.py --branch=${GOLDEN_BRANCH} \
+10 -15
View File
@@ -126,24 +126,18 @@ jobs:
- uses: actions/checkout@v4.1.6 - uses: actions/checkout@v4.1.6
with: with:
fetch-depth: 0 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 - id: get_commit_msg
uses: ./.github/actions/get-commit-msg uses: ./.github/actions/get-commit-msg
- name: Check if accepting new goldens - name: Prerequisites
id: check_accept
env:
COMMIT_MESSAGE: ${{ steps.get_commit_msg.outputs.msg }}
run: | run: |
if echo "${COMMIT_MESSAGE}" | python3 test/renderdiff/src/commit_msg.py --mode=accept_new_goldens; then # Must have at least clang-16 for a webgpu/dawn build.
echo "accept=true" >> "$GITHUB_OUTPUT" sudo xcode-select -s /Applications/Xcode_16.2.app/Contents/Developer
else
echo "accept=false" >> "$GITHUB_OUTPUT"
fi
shell: bash shell: bash
- name: Renderdiff Generate
if: steps.check_accept.outputs.accept != 'true'
uses: ./.github/actions/renderdiff-generate
- name: Render and compare - name: Render and compare
if: steps.check_accept.outputs.accept != 'true'
id: render_compare id: render_compare
env: env:
COMMIT_MESSAGE: ${{ steps.get_commit_msg.outputs.msg }} COMMIT_MESSAGE: ${{ steps.get_commit_msg.outputs.msg }}
@@ -152,6 +146,9 @@ jobs:
source ${TEST_DIR}/src/preamble.sh source ${TEST_DIR}/src/preamble.sh
set -eux set -eux
GOLDEN_BRANCH=$(echo "${COMMIT_MESSAGE}" | python3 ${TEST_DIR}/src/commit_msg.py) 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 \ python3 ${TEST_DIR}/src/golden_manager.py \
--branch=${GOLDEN_BRANCH} \ --branch=${GOLDEN_BRANCH} \
@@ -175,12 +172,10 @@ jobs:
fi fi
shell: bash shell: bash
- uses: actions/upload-artifact@v4 - uses: actions/upload-artifact@v4
if: steps.check_accept.outputs.accept != 'true'
with: with:
name: presubmit-renderdiff-result name: presubmit-renderdiff-result
path: ./out/renderdiff path: ./out/renderdiff
- name: Compare result - name: Compare result
if: steps.check_accept.outputs.accept != 'true'
run: | run: |
ERROR_STR="${{ steps.render_compare.outputs.err }}" ERROR_STR="${{ steps.render_compare.outputs.err }}"
if [ -n "${ERROR_STR}" ]; then if [ -n "${ERROR_STR}" ]; then
-1
View File
@@ -8,4 +8,3 @@ appropriate header in [RELEASE_NOTES.md](./RELEASE_NOTES.md).
## Release notes for next branch cut ## Release notes for next branch cut
- engine: fix crash when using variance shadow maps - engine: fix crash when using variance shadow maps
- materials: better shadow normal-bias calculations [⚠️ **New Material Version**]
+2 -2
View File
@@ -31,7 +31,7 @@ repositories {
} }
dependencies { dependencies {
implementation 'com.google.android.filament:filament-android:1.69.4' implementation 'com.google.android.filament:filament-android:1.69.3'
} }
``` ```
@@ -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: iOS projects can use CocoaPods to install the latest release:
```shell ```shell
pod 'Filament', '~> 1.69.4' pod 'Filament', '~> 1.69.3'
``` ```
## Documentation ## Documentation
-4
View File
@@ -7,10 +7,6 @@ A new header is inserted each time a *tag* is created.
Instead, if you are authoring a PR for the main branch, add your release note to Instead, if you are authoring a PR for the main branch, add your release note to
[NEW_RELEASE_NOTES.md](./NEW_RELEASE_NOTES.md). [NEW_RELEASE_NOTES.md](./NEW_RELEASE_NOTES.md).
## v1.69.5
- engine: fix crash when using variance shadow maps
## v1.69.4 ## v1.69.4
+1 -1
View File
@@ -1,5 +1,5 @@
GROUP=com.google.android.filament GROUP=com.google.android.filament
VERSION_NAME=1.69.4 VERSION_NAME=1.69.3
POM_DESCRIPTION=Real-time physically based rendering engine for Android. POM_DESCRIPTION=Real-time physically based rendering engine for Android.
+6 -6
View File
@@ -235,9 +235,9 @@
"license": "MIT" "license": "MIT"
}, },
"node_modules/minimatch": { "node_modules/minimatch": {
"version": "3.1.3", "version": "3.1.2",
"resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.3.tgz", "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz",
"integrity": "sha512-M2GCs7Vk83NxkUyQV1bkABc4yxgz9kILhHImZiBPAZ9ybuvCb0/H7lEl5XvIg3g+9d4eNotkZA5IWwYl0tibaA==", "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==",
"dependencies": { "dependencies": {
"brace-expansion": "^1.1.7" "brace-expansion": "^1.1.7"
}, },
@@ -429,9 +429,9 @@
"integrity": "sha512-nGqtvLrj5w0naR6tDPfB4cUmYCqouzyQiz6C5y/LtcDllJdrcc6WaWW6iXyIIOErTa/XRybj28aasdn4LkVk6Q==" "integrity": "sha512-nGqtvLrj5w0naR6tDPfB4cUmYCqouzyQiz6C5y/LtcDllJdrcc6WaWW6iXyIIOErTa/XRybj28aasdn4LkVk6Q=="
}, },
"minimatch": { "minimatch": {
"version": "3.1.3", "version": "3.1.2",
"resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.3.tgz", "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz",
"integrity": "sha512-M2GCs7Vk83NxkUyQV1bkABc4yxgz9kILhHImZiBPAZ9ybuvCb0/H7lEl5XvIg3g+9d4eNotkZA5IWwYl0tibaA==", "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==",
"requires": { "requires": {
"brace-expansion": "^1.1.7" "brace-expansion": "^1.1.7"
} }
+2 -2
View File
@@ -181,7 +181,7 @@ important for <code>matc</code> (material compiler).</p>
} }
dependencies { dependencies {
implementation 'com.google.android.filament:filament-android:1.69.4' implementation 'com.google.android.filament:filament-android:1.69.3'
} }
</code></pre> </code></pre>
<p>Here are all the libraries available in the group <code>com.google.android.filament</code>:</p> <p>Here are all the libraries available in the group <code>com.google.android.filament</code>:</p>
@@ -195,7 +195,7 @@ dependencies {
</div> </div>
<h3 id="ios"><a class="header" href="#ios">iOS</a></h3> <h3 id="ios"><a class="header" href="#ios">iOS</a></h3>
<p>iOS projects can use CocoaPods to install the latest release:</p> <p>iOS projects can use CocoaPods to install the latest release:</p>
<pre><code class="language-shell">pod 'Filament', '~&gt; 1.69.4' <pre><code class="language-shell">pod 'Filament', '~&gt; 1.69.3'
</code></pre> </code></pre>
<h2 id="documentation"><a class="header" href="#documentation">Documentation</a></h2> <h2 id="documentation"><a class="header" href="#documentation">Documentation</a></h2>
<ul> <ul>
+1 -23
View File
@@ -225,10 +225,9 @@ into <strong>branch</strong> of <code>filament-assets</code>. This branch is pai
<code>filament</code> repo.</p> <code>filament</code> repo.</p>
<p>As an example, imagine I am working on a PR, and I've uploaded my change, which is in a <p>As an example, imagine I am working on a PR, and I've uploaded my change, which is in a
branch called <code>my-pr-branch</code>, to <code>filament</code>. This PR requires updating the golden. We would do branch called <code>my-pr-branch</code>, to <code>filament</code>. This PR requires updating the golden. We would do
it in the following fashion on a macOS machine:</p> it in the following fashion</p>
<h3 id="using-a-script-to-update-the-golden-repo"><a class="header" href="#using-a-script-to-update-the-golden-repo">Using a script to update the golden repo</a></h3> <h3 id="using-a-script-to-update-the-golden-repo"><a class="header" href="#using-a-script-to-update-the-golden-repo">Using a script to update the golden repo</a></h3>
<ul> <ul>
<li>Make sure you've completed the steps in 'Setting up python'</li>
<li>Run interactive mode in the <code>update_golden.py</code> script. <li>Run interactive mode in the <code>update_golden.py</code> script.
<pre><code>python3 test/renderdiff/src/update_golden.py <pre><code>python3 test/renderdiff/src/update_golden.py
</code></pre> </code></pre>
@@ -264,27 +263,6 @@ 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> <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> to the <code>main</code> branch of the golden repo.</li>
</ul> </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> <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 <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 be used by pointing your browser to a localhost port. If you input the viewer with a PR or a
+199 -199
View File
@@ -504,9 +504,9 @@ D_{GGX}(h,\alpha) = \frac{\aa}{\pi ( (\NoH)^2 (\aa - 1) + 1)^2}
\end{equation}$$ \end{equation}$$
</p><p> </p><p>
The GLSL implementation of the NDF, shown in <a href="#listing_speculard">listing&nbsp;1</a>, is simple and efficient. 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> </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-built_in">float</span> a = NoH * roughness;</span> <span class="line"> <span class="hljs-type">float</span> a = NoH * roughness;</span>
<span class="line"> <span class="hljs-built_in">float</span> k = roughness / (<span class="hljs-number">1.0</span> - NoH * NoH + a * a);</span> <span class="line"> <span class="hljs-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 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> <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> <p>
@@ -590,10 +590,10 @@ V(v,l,\alpha) = \frac{0.5}{\NoL \sqrt{(\NoV)^2 (1 - \aa) + \aa} + \NoV \sqrt{(\N
\end{equation}$$ \end{equation}$$
</p><p> </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. 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> </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-keyword">float</span> a2 = roughness * roughness;</span> <span class="line"> <span class="hljs-type">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-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-keyword">float</span> GGXL = NoV * <span class="hljs-built_in">sqrt</span>(NoL * NoL * (<span class="hljs-number">1.0</span> - a2) + a2);</span> <span class="line"> <span class="hljs-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 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> <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> <p>
@@ -604,10 +604,10 @@ V(v,l,\alpha) = \frac{0.5}{\NoL (\NoV (1 - \alpha) + \alpha) + \NoV (\NoL (1 - \
\end{equation}$$ \end{equation}$$
</p><p> </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>. 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> </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-built_in">float</span> a = roughness;</span> <span class="line"> <span class="hljs-type">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-type">float</span> GGXV = NoL * (NoV * (<span class="hljs-number">1.0</span> - a) + a);</span>
<span class="line"> <span class="hljs-built_in">float</span> GGXL = NoV * (NoL * (<span class="hljs-number">1.0</span> - a) + a);</span> <span class="line"> <span class="hljs-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 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> <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> <p>
@@ -659,7 +659,7 @@ $$\begin{equation}
\end{equation}$$ \end{equation}$$
</p><p> </p><p>
In practice, the diffuse reflectance \(\sigma\) is multiplied later, as shown in <a href="#listing_diffusebrdf">listing&nbsp;8</a>. 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 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> <span class="line"></span>
@@ -680,14 +680,14 @@ Where:
$$\begin{equation} $$\begin{equation}
\fGrazing=0.5 + 2 \cdot \alpha cos^2(\theta_d) \fGrazing=0.5 + 2 \cdot \alpha cos^2(\theta_d)
\end{equation}$$ \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> </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) * <span class="hljs-built_in">pow</span>(<span class="hljs-number">1.0</span> - u, <span class="hljs-number">5.0</span>);</span> <span class="line"> <span 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> <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-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-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-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-keyword">float</span> lightScatter = 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">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-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-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 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> <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> <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. <strong class="asterisk">Diffuse term</strong>: a Lambertian diffuse model.
</p><p> </p><p>
The full GLSL implementation of the standard model is shown in <a href="#listing_glslbrdf">listing&nbsp;9</a>. 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> </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> a2 = a * a;</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> f = (NoH * a2 - NoH) * NoH + <span class="hljs-number">1.0</span>;</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 class="hljs-keyword">return</span> a2 / (PI * f * f);</span>
<span class="line">}</span> <span class="line">}</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">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 + (<span class="hljs-type">vec3</span>(<span class="hljs-number">1.0</span>) - f0) * <span class="hljs-built_in">pow</span>(<span class="hljs-number">1.0</span> - u, <span class="hljs-number">5.0</span>);</span> <span class="line"> <span 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> <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> <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> a2 = a * a;</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> GGXL = NoV * <span class="hljs-built_in">sqrt</span>((-NoL * a2 + NoL) * NoL + a2);</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> 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-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 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> <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 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> <span class="line"></span>
<span class="line"><span class="hljs-type">void</span> BRDF(...) {</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> h = <span class="hljs-built_in">normalize</span>(v + l);</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>
<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> <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> 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> <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> 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> <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> 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">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>
<span class="line"> <span class="hljs-comment">// perceptually linear roughness to roughness (see parameterization)</span></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>
<span class="line"> <span class="hljs-type">float</span> D = D_GGX(NoH, 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> F = F_Schlick(LoH, f0);</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> V = V_SmithGGXCorrelated(NoV, NoL, roughness);</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>
<span class="line"> <span class="hljs-comment">// specular BRDF</span></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>
<span class="line"> <span class="hljs-comment">// diffuse BRDF</span></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>
<span class="line"> <span class="hljs-comment">// apply lighting...</span></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> <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}$$ \end{equation}$$
</p><p> </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. <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> <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>
<p>The roughness set by the user, called <code>perceptualRoughness</code> here, is remapped to a perceptually linear range using the following formulation:</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. 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> </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. 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 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> <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> <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> <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> </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. <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 class="hljs-comment">// compute Fd and Fr from standard model</span></span>
<span class="line"></span> <span class="line"></span>
<span class="line"> <span class="hljs-comment">// remapping and linearization of clear coat roughness</span></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"> clearCoatRoughness = clearCoatPerceptualRoughness * clearCoatPerceptualRoughness;</span>
<span class="line"></span> <span class="line"></span>
<span class="line"> <span class="hljs-comment">// clear coat BRDF</span></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-type">float</span> Dc = <span class="hljs-built_in">D_GGX</span>(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-type">float</span> Vc = <span class="hljs-built_in">V_Kelemen</span>(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-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-built_in">float</span> Frc = (Dc * Vc) * Fc;</span> <span class="line"> <span class="hljs-type">float</span> Frc = (Dc * Vc) * Fc;</span>
<span class="line"></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-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> <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}$$ \end{equation}$$
</p><p> </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. 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"> <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"> <span class="hljs-type">float</span> a2 = 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"> <span class="hljs-type">float</span> cos2h = 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"> <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"> 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"> <span class="hljs-type">float</span> sin4h = sin2h * sin2h;</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-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> * 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-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> <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>
<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> <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> </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). [<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. 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-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-type">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-type">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> 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 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> <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> <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> <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>
<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>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-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 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">// 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"> <span class="hljs-comment">// integration over the unit sphere around the luminaire</span></span>
<span class="line"> multiplier = photometricLight.getDesiredIntensity() /</span> <span class="line"> multiplier = photometricLight.<span class="hljs-built_in">getDesiredIntensity</span>() /</span>
<span class="line"> photometricLight.getIntegratedIntensity();</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-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"> <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> <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-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> <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 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> </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> <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> </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: 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> </p><pre class="listing tilde"><code><span class="line">vec3 irradianceSH(vec3 n) {</span>
<span class="line"> <span class="hljs-comment">// uniform vec3 sphericalHarmonics[9]</span></span> <span class="line"> // uniform vec3 sphericalHarmonics<span class="hljs-selector-attr">[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"> // 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"> <span class="hljs-keyword">return</span></span> <span class="line"> return</span>
<span class="line"> sphericalHarmonics[<span class="hljs-number">0</span>]</span> <span class="line"> sphericalHarmonics<span class="hljs-selector-attr">[0]</span></span>
<span class="line"> + sphericalHarmonics[<span class="hljs-number">1</span>] * (n.y)</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-number">2</span>] * (n.z)</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-number">3</span>] * (n.x)</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-number">4</span>] * (n.y * n.x)</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-number">5</span>] * (n.y * n.z)</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-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-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-number">7</span>] * (n.z * n.x)</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-number">8</span>] * (n.x * n.x - n.y * n.y);</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> <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>
<p>Note that with 2 bands, the computation above becomes a single (4 \times 4) matrix-by-vector multiply.</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> </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>: 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.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> <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> <p>
@@ -2507,11 +2507,11 @@ using an environment made of colored vertical stripes (skybox hidden).</span></c
<p> <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>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> </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"><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">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 *= <span class="hljs-built_in">sq</span>(<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">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> <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>
<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> <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> <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> </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. 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> </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-meta-keyword">if</span> defined(SHADING_MODEL_CLOTH)</span></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-meta-keyword">if</span> defined(MATERIAL_HAS_SUBSURFACE_COLOR)</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 *= saturate((NoV + <span class="hljs-number">0.5</span>) / <span class="hljs-number">2.25</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-meta-keyword">endif</span></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-meta-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"></span>
<span class="line">vec3 indirectDiffuse = irradianceIBL(n) * diffuse;</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-meta-keyword">if</span> defined(SHADING_MODEL_CLOTH) &amp;&amp; defined(MATERIAL_HAS_SUBSURFACE_COLOR)</span></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 *= saturate(subsurfaceColor + NoV);</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-meta-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"></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> <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> <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">// 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">// shutterSpeed in seconds</span></span>
<span class="line"><span class="hljs-comment">// sensitivity in ISO</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 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> <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">// 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-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-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> / (<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-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> <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-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-keyword">float</span> exposure = exposure(ev100);</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"></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> <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>
<p>In practice the exposure factor can be pre-computed on the CPU to save shader instructions.</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">// 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/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-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-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-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-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-keyword">const</span> float3 CIE_XYZ[CIE_XYZ_COUNT] = { ... };</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>
<span class="line"><span class="hljs-comment">// CIE Standard Illuminant D65 relative spectral power distribution,</span></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> <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">// 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://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-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-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-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-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-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-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-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-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-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">float</span> CIE_D65[CIE_D65_COUNT] = { ... };</span>
<span class="line"></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">struct</span> <span class="hljs-title class_">Sample</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-type">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"> 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> <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-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-keyword">size_t</span>((w - CIE_D65_START) / CIE_D65_INTERVAL);</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, <span class="hljs-built_in">std</span>::min(i0 + <span class="hljs-number">1</span>, CIE_D65_END)};</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"></span>
<span class="line"> float2 wavelengthBounds = CIE_D65_START + float2{indexBounds} * CIE_D65_INTERVAL;</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-type">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-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> <span class="line"></span>
<span class="line"><span class="hljs-comment">// For std::lower_bound</span></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 class="hljs-keyword">return</span> lhs.w &lt; rhs.w;</span>
<span class="line">}</span> <span class="line">}</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-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-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 = <span class="hljs-built_in">std</span>::lower_bound(</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.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"> 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 class="hljs-keyword">auto</span> i0 = i1 - <span class="hljs-number">1</span>;</span>
<span class="line"></span> <span class="line"></span>
<span class="line"> <span class="hljs-comment">// Interpolate the complex IORs</span></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-type">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-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-keyword">float</span> k = lerp(i0-&gt;ior.imag(), i1-&gt;ior.imag(), 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 class="hljs-keyword">return</span> { n, k };</span>
<span class="line">}</span> <span class="line">}</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-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>)) * (<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"> <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>)) * (<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"> ((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> <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-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-keyword">const</span> mat3f XYZ_sRGB{</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">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">-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> <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> <span class="line"></span>
<span class="line"><span class="hljs-comment">// Outputs a linear sRGB color</span></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"> 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>
<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-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>
<span class="line"> <span class="hljs-comment">// Find most appropriate CIE XYZ sample for the wavelength</span></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-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>
<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-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"></span>
<span class="line"> xyz += f0 * CIE_XYZ[i] * d65;</span> <span class="line"> xyz += f0 * CIE_XYZ[i] * d65;</span>
<span class="line"> y += CIE_XYZ[i].y * 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"> <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"> xyz /= y;</span>
<span class="line"></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>
<span class="line"> <span class="hljs-comment">// Normalize out-of-gamut values</span></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>
<span class="line"> <span class="hljs-keyword">return</span> linear;</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> <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> <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"> 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 &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; <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; 0x33333333) &lt;&lt; <span class="hljs-string">2) | ((bits &amp; 0xCCCCCCCC) &gt;&gt; 2</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; 0x0F0F0F0F) &lt;&lt; <span class="hljs-string">4) | ((bits &amp; 0xF0F0F0F0) &gt;&gt; 4</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; 0x00FF00FF) &lt;&lt; <span class="hljs-string">8) | ((bits &amp; 0xFF00FF00) &gt;&gt; 8</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"> <span class="hljs-built_in">return</span> vec2f(i / numSamples, bits / exp2(32));</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> <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> <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>
<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>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> </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> a2 = a * a;</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> GGXL = NoV * <span class="hljs-built_in">sqrt</span>((-NoL * a2 + NoL) * NoL + a2);</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> 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-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 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"></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"> 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.x = sqrt(<span class="hljs-number">1.0f</span> - NoV*NoV);</span>
<span class="line"> V.y = <span class="hljs-number">0.0</span>f;</span> <span class="line"> V.y = <span class="hljs-number">0.0f</span>;</span>
<span class="line"> V.z = NoV;</span> <span class="line"> V.z = NoV;</span>
<span class="line"></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-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> i = <span class="hljs-number">0</span>; i &lt; sampleCount; i++) {</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"> float2 Xi = hammersley(i, sampleCount);</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"> float3 H = importanceSampleGGX(Xi, a, N);</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"> 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">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>
<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> <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> NoL = saturate(L.z);</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> NoH = saturate(H.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>
<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-keyword">if</span> (NoL &gt; <span class="hljs-number">0.0f</span>) {</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> <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> Gv = G * VoH / NoH;</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> 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-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.x += Gv * (<span class="hljs-number">1</span> - Fc);</span>
<span class="line"> r.y += Gv * Fc;</span> <span class="line"> r.y += Gv * Fc;</span>
<span class="line"> }</span> <span class="line"> }</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> <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> <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> <p>
@@ -3851,56 +3851,56 @@ sin(m \phi + \phi) &= sin(m \phi) cos(\phi) + cos(m \phi) sin(\phi) \Leftrightar
\end{align*}$$ \end{align*}$$
</p><p> </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}\): <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 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> <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-function"><span class="hljs-type">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-type">double</span>* <span class="hljs-type">const</span> SHb,</span>
<span class="line"> <span class="hljs-keyword">size_t</span> numBands,</span> <span class="line"> <span class="hljs-type">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-type">const</span> vec3&amp; s)</span></span>
<span class="line"></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-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-type">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_1 = <span class="hljs-number">1</span>;</span>
<span class="line"> SHb[<span class="hljs-number">0</span>] = Pml_1;</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">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-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-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_2 = Pml_1;</span>
<span class="line"> Pml_1 = Pml;</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>
<span class="line"> <span class="hljs-keyword">double</span> Pmm = <span class="hljs-number">1</span>;</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-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-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"> 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-type">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_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"> <span class="hljs-comment">// l == m</span></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[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"> <span class="hljs-keyword">if</span> (m + <span class="hljs-number">1</span> &lt; numBands) {</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"> <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[<span class="hljs-built_in">SHindex</span>(-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"> 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-keyword">ssize_t</span> l = m + <span class="hljs-number">2</span>; l &lt; numBands; l++) {</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-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"> <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"> / (l - m);</span>
<span class="line"> Pml_2 = Pml_1;</span> <span class="line"> Pml_2 = Pml_1;</span>
<span class="line"> Pml_1 = Pml;</span> <span class="line"> Pml_1 = 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[SHindex( 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>
<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-type">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-type">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-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-keyword">ssize_t</span> l = m; l &lt; numBands ; l++) {</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[SHindex(-m, l)] *= Sm;</span> <span class="line"> SHb[<span class="hljs-built_in">SHindex</span>(-m, l)] *= Sm;</span>
<span class="line"> SHb[SHindex( m, l)] *= Cm;</span> <span class="line"> SHb[<span class="hljs-built_in">SHindex</span>( m, l)] *= Cm;</span>
<span class="line"> }</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-type">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> Sm1 = Sm * s.x + Cm * s.y;</span>
<span class="line"> Cm = Cm1;</span> <span class="line"> Cm = Cm1;</span>
<span class="line"> Sm = Sm1;</span> <span class="line"> Sm = Sm1;</span>
<span class="line"> }</span> <span class="line"> }</span>
@@ -3979,10 +3979,10 @@ $$\begin{equation}
\end{equation}$$ \end{equation}$$
</p><p> </p><p>
Here is the C++ code to compute \(\hat{C}_l\): 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>
<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-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">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">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> <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">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 class="hljs-keyword">return</span> <span class="hljs-number">0</span>;</span>
<span class="line"> }</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-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-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-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-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">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 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> <span class="line"></span>
<span class="line"><span class="hljs-comment">// returns n! / d!</span></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"><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 = <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"> 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 = <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"> 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-keyword">double</span> r = <span class="hljs-number">1.0</span>;</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-keyword">if</span> (n == d) {</span>
<span class="line"> <span class="hljs-comment">// intentionally left blank</span></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> <span class="line"> } <span class="hljs-keyword">else</span> <span class="hljs-keyword">if</span> (n &gt; d) {</span>
+1 -1
View File
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
@@ -1706,8 +1706,6 @@ enum class Workaround : uint16_t {
DISABLE_DEPTH_PRECACHE_FOR_DEFAULT_MATERIAL, DISABLE_DEPTH_PRECACHE_FOR_DEFAULT_MATERIAL,
// Emulate an sRGB swapchain in shader code. // Emulate an sRGB swapchain in shader code.
EMULATE_SRGB_SWAPCHAIN, EMULATE_SRGB_SWAPCHAIN,
// Workaround for AMD Vulkan drivers on Windows crashing when SSR history is detached.
BLIT_SSR_HISTORY,
}; };
using StereoscopicType = Platform::StereoscopicType; using StereoscopicType = Platform::StereoscopicType;
@@ -27,6 +27,8 @@
#include <stddef.h> #include <stddef.h>
#include <stdint.h> #include <stdint.h>
#include <functional>
namespace filament::backend { namespace filament::backend {
/* /*
@@ -64,7 +66,7 @@ public:
// all commands buffers (Slices) written to this point are returned by waitForCommand(). This // all commands buffers (Slices) written to this point are returned by waitForCommand(). This
// call blocks until the CircularBuffer has at least mRequiredSize bytes available. // call blocks until the CircularBuffer has at least mRequiredSize bytes available.
void flush(); void flush(std::function<void(void*, void*)> const& debugPrintHistogram = nullptr);
// returns from waitForCommands() immediately. // returns from waitForCommands() immediately.
void requestExit(); void requestExit();
@@ -56,6 +56,7 @@ namespace filament::backend {
class CommandBase { class CommandBase {
static constexpr size_t FILAMENT_OBJECT_ALIGNMENT = alignof(std::max_align_t); static constexpr size_t FILAMENT_OBJECT_ALIGNMENT = alignof(std::max_align_t);
friend class CommandStream;
protected: protected:
using Execute = Dispatcher::Execute; using Execute = Dispatcher::Execute;
@@ -168,8 +169,8 @@ struct CommandType<void (Driver::*)(ARGS...)> {
class CustomCommand : public CommandBase { class CustomCommand : public CommandBase {
std::function<void()> mCommand; std::function<void()> mCommand;
static void execute(Driver&, CommandBase* base, intptr_t* next);
public: public:
static void execute(Driver&, CommandBase* base, intptr_t* next);
CustomCommand(CustomCommand&& rhs) = default; CustomCommand(CustomCommand&& rhs) = default;
explicit CustomCommand(std::function<void()> cmd) explicit CustomCommand(std::function<void()> cmd)
@@ -179,11 +180,12 @@ public:
// ------------------------------------------------------------------------------------------------ // ------------------------------------------------------------------------------------------------
class NoopCommand : public CommandBase { class NoopCommand : public CommandBase {
public:
intptr_t mNext; intptr_t mNext;
static void execute(Driver&, CommandBase* self, intptr_t* next) noexcept { static void execute(Driver&, CommandBase* self, intptr_t* next) noexcept {
*next = static_cast<NoopCommand*>(self)->mNext; *next = static_cast<NoopCommand*>(self)->mNext;
} }
public:
constexpr explicit NoopCommand(void* next) noexcept constexpr explicit NoopCommand(void* next) noexcept
: CommandBase(execute), mNext(intptr_t((char *)next - (char *)this)) { } : CommandBase(execute), mNext(intptr_t((char *)next - (char *)this)) { }
}; };
@@ -219,6 +221,36 @@ public:
CircularBuffer const& getCircularBuffer() const noexcept { return mCurrentBuffer; } CircularBuffer const& getCircularBuffer() const noexcept { return mCurrentBuffer; }
#if FILAMENT_DEBUG_COMMANDS_HISTOGRAM
using Execute = Dispatcher::Execute;
struct CommandInfo {
size_t size;
const char* name;
int index;
};
std::unordered_map<Execute, CommandInfo> mCommands;
void initializeLookup() {
int currentIndex = 0;
#define DECL_DRIVER_API_SYNCHRONOUS(RetType, methodName, paramsDecl, params)
#define DECL_DRIVER_API(methodName, paramsDecl, params) \
mCommands[mDispatcher.methodName##_] = { CommandBase::align(sizeof(COMMAND_TYPE(methodName))), \
#methodName, currentIndex++ };
#define DECL_DRIVER_API_RETURN(RetType, methodName, paramsDecl, params) \
mCommands[mDispatcher.methodName##_] = { \
CommandBase::align(sizeof(COMMAND_TYPE(methodName##R))), #methodName, currentIndex++ \
};
#include "private/backend/DriverAPI.inc"
mCommands[CustomCommand::execute] = { CommandBase::align(sizeof(CustomCommand)),
"CustomCommand", currentIndex++ };
// NoopCommands have variable size. We will handle them specially using their mNext pointer.
mCommands[NoopCommand::execute] = { 0, "NoopCommand", currentIndex++ };
}
#endif
public: public:
#define DECL_DRIVER_API(methodName, paramsDecl, params) \ #define DECL_DRIVER_API(methodName, paramsDecl, params) \
inline void methodName(paramsDecl) noexcept { \ inline void methodName(paramsDecl) noexcept { \
@@ -263,6 +295,13 @@ public:
void execute(void* buffer); void execute(void* buffer);
#if FILAMENT_DEBUG_COMMANDS_HISTOGRAM
void debugIterateCommands(void* head, void* tail,
std::function<void(CommandInfo const& info)> const& callback);
void debugPrintHistogram(void* head, void* tail);
#endif
/* /*
* queueCommand() allows to queue a lambda function as a command. * queueCommand() allows to queue a lambda function as a command.
* This is much less efficient than using the Driver* API. * This is much less efficient than using the Driver* API.
@@ -48,6 +48,11 @@
#define FILAMENT_DEBUG_COMMANDS FILAMENT_DEBUG_COMMANDS_NONE #define FILAMENT_DEBUG_COMMANDS FILAMENT_DEBUG_COMMANDS_NONE
// Upon command stream overflow, print a histogram of commands
#ifndef FILAMENT_DEBUG_COMMANDS_HISTOGRAM
#define FILAMENT_DEBUG_COMMANDS_HISTOGRAM 0
#endif
namespace filament::backend { namespace filament::backend {
class BufferDescriptor; class BufferDescriptor;
+8 -1
View File
@@ -79,7 +79,7 @@ bool CommandBufferQueue::isExitRequested() const {
} }
void CommandBufferQueue::flush() { void CommandBufferQueue::flush(std::function<void(void*, void*)> const& debugPrintHistogram) {
FILAMENT_TRACING_CALL(FILAMENT_TRACING_CATEGORY_FILAMENT); FILAMENT_TRACING_CALL(FILAMENT_TRACING_CATEGORY_FILAMENT);
CircularBuffer& circularBuffer = mCircularBuffer; CircularBuffer& circularBuffer = mCircularBuffer;
@@ -106,6 +106,13 @@ void CommandBufferQueue::flush() {
std::unique_lock lock(mLock); std::unique_lock lock(mLock);
// circular buffer is too small, we corrupted the stream // circular buffer is too small, we corrupted the stream
#if FILAMENT_DEBUG_COMMANDS_HISTOGRAM
if (UTILS_VERY_UNLIKELY(used > mFreeSpace)) {
if (debugPrintHistogram) {
debugPrintHistogram(begin, end);
}
}
#endif
FILAMENT_CHECK_POSTCONDITION(used <= mFreeSpace) << FILAMENT_CHECK_POSTCONDITION(used <= mFreeSpace) <<
"Backend CommandStream overflow. Commands are corrupted and unrecoverable.\n" "Backend CommandStream overflow. Commands are corrupted and unrecoverable.\n"
"Please increase minCommandBufferSizeMB inside the Config passed to Engine::create.\n" "Please increase minCommandBufferSizeMB inside the Config passed to Engine::create.\n"
+74
View File
@@ -30,6 +30,11 @@
#include <utils/ostream.h> #include <utils/ostream.h>
#include <utils/sstream.h> #include <utils/sstream.h>
#if FILAMENT_DEBUG_COMMANDS_HISTOGRAM
#include <algorithm>
#include <vector>
#endif
#include <cstddef> #include <cstddef>
#include <functional> #include <functional>
#include <string> #include <string>
@@ -83,6 +88,10 @@ CommandStream::CommandStream(Driver& driver, CircularBuffer& buffer) noexcept
__system_property_get("debug.filament.perfcounters", property); __system_property_get("debug.filament.perfcounters", property);
mUsePerformanceCounter = bool(atoi(property)); mUsePerformanceCounter = bool(atoi(property));
#endif #endif
#if FILAMENT_DEBUG_COMMANDS_HISTOGRAM
initializeLookup();
#endif
} }
void CommandStream::execute(void* buffer) { void CommandStream::execute(void* buffer) {
@@ -126,6 +135,71 @@ void CommandStream::execute(void* buffer) {
} }
} }
#if FILAMENT_DEBUG_COMMANDS_HISTOGRAM
void CommandStream::debugIterateCommands(void* head, void* tail,
std::function<void(CommandInfo const& info)> const& callback) {
CommandBase* UTILS_RESTRICT base = static_cast<CommandBase*>(head);
auto p = base;
while (UTILS_LIKELY(p)) {
if (p >= tail) {
break;
}
Execute e = p->mExecute;
if (e == NoopCommand::execute) {
NoopCommand* noop = static_cast<NoopCommand*>(p);
size_t size = noop->mNext;
int noopIndex = mCommands[NoopCommand::execute].index;
callback({ size, "NoopCommand", noopIndex });
p = reinterpret_cast<CommandBase*>(reinterpret_cast<char*>(p) + size);
continue;
}
if (auto it = mCommands.find(e); it != mCommands.end()) {
size_t size = it->second.size;
callback(it->second);
p = reinterpret_cast<CommandBase*>(reinterpret_cast<char*>(p) + size);
} else {
LOG(ERROR) << "Cannot find command in lookup table";
return;
}
}
}
void CommandStream::debugPrintHistogram(void* head, void* tail) {
std::unordered_map<std::string_view, int> histogram;
std::unordered_map<int, int> index_histogram;
debugIterateCommands(head, tail, [&](CommandInfo const& info) {
histogram[std::string_view(info.name)]++;
index_histogram[info.index]++;
});
std::vector<std::pair<std::string_view, int>> sorted_histogram(histogram.begin(),
histogram.end());
std::sort(sorted_histogram.begin(), sorted_histogram.end(),
[](auto const& a, auto const& b) { return a.second > b.second; });
LOG(INFO) << "Command stream histogram:";
for (auto const& [name, count]: sorted_histogram) {
LOG(INFO) << name << ": " << count;
}
std::vector<std::pair<int, int>> sorted_index_histogram(index_histogram.begin(),
index_histogram.end());
std::sort(sorted_index_histogram.begin(), sorted_index_histogram.end(),
[](auto const& a, auto const& b) { return a.second > b.second; });
std::string short_histogram = "";
for (size_t i = 0, n = sorted_index_histogram.size(); i < n; ++i) {
short_histogram += std::to_string(sorted_index_histogram[i].first) + ":" +
std::to_string(sorted_index_histogram[i].second);
short_histogram += (i < n - 1) ? ";" : ".";
}
LOG(INFO) << "CS hist: " << short_histogram;
LOG(INFO) << "";
}
#endif
void CommandStream::queueCommand(std::function<void()> command) { void CommandStream::queueCommand(std::function<void()> command) {
new(allocateCommand(CustomCommand::align(sizeof(CustomCommand)))) CustomCommand(std::move(command)); new(allocateCommand(CustomCommand::align(sizeof(CustomCommand)))) CustomCommand(std::move(command));
} }
+2 -20
View File
@@ -25,11 +25,6 @@
#include <mutex> #include <mutex>
#ifdef __ANDROID__
#include <pthread.h>
#endif
namespace filament { namespace filament {
using namespace utils; using namespace utils;
@@ -110,21 +105,8 @@ JNIEnv* VirtualMachineEnv::getEnvironmentSlow() {
FILAMENT_CHECK_PRECONDITION(mVirtualMachine) FILAMENT_CHECK_PRECONDITION(mVirtualMachine)
<< "JNI_OnLoad() has not been called"; << "JNI_OnLoad() has not been called";
#ifdef __ANDROID__ #if defined(__ANDROID__)
JavaVMAttachArgs args; jint const result = mVirtualMachine->AttachCurrentThread(&mJniEnv, nullptr);
args.version = JNI_VERSION_1_6;
args.group = nullptr;
char threadName[16]; // pthread_getname_np returns at most 16 bytes
if (__builtin_available(android 26, *)) {
if (pthread_getname_np(pthread_self(), threadName, sizeof(threadName)) == 0) {
args.name = threadName;
} else {
args.name = nullptr;
}
} else {
args.name = nullptr;
}
jint const result = mVirtualMachine->AttachCurrentThread(&mJniEnv, &args);
#else #else
jint const result = mVirtualMachine->AttachCurrentThread(reinterpret_cast<void**>(&mJniEnv), nullptr); jint const result = mVirtualMachine->AttachCurrentThread(reinterpret_cast<void**>(&mJniEnv), nullptr);
#endif #endif
+1 -1
View File
@@ -91,7 +91,7 @@ public:
GLenum getIndicesType() const noexcept { GLenum getIndicesType() const noexcept {
return indicesType; return indicesType;
} }
}; } gl;
static bool queryOpenGLVersion(GLint* major, GLint* minor) noexcept; static bool queryOpenGLVersion(GLint* major, GLint* minor) noexcept;
+2 -10
View File
@@ -36,7 +36,6 @@ VK_DEFINE_HANDLE(VmaPool)
namespace filament::backend { namespace filament::backend {
struct VulkanCommandBuffer; struct VulkanCommandBuffer;
struct VulkanRenderPass;
struct VulkanRenderTarget; struct VulkanRenderTarget;
struct VulkanSwapChain; struct VulkanSwapChain;
struct VulkanTexture; struct VulkanTexture;
@@ -59,11 +58,11 @@ struct VulkanAttachment {
VkImageSubresourceRange getSubresourceRange() const; VkImageSubresourceRange getSubresourceRange() const;
}; };
struct VulkanRenderPassContext { struct VulkanRenderPass {
// Between the begin and end command render pass we cache the command buffer // Between the begin and end command render pass we cache the command buffer
VulkanCommandBuffer* commandBuffer; VulkanCommandBuffer* commandBuffer;
fvkmemory::resource_ptr<VulkanRenderTarget> renderTarget; fvkmemory::resource_ptr<VulkanRenderTarget> renderTarget;
fvkmemory::resource_ptr<VulkanRenderPass> renderPass; VkRenderPass renderPass;
RenderPassParams params; RenderPassParams params;
int currentSubpass; int currentSubpass;
}; };
@@ -121,10 +120,6 @@ public:
return mPhysicalDeviceProperties.properties.vendorID; return mPhysicalDeviceProperties.properties.vendorID;
} }
inline char const* getDriverName() const noexcept {
return mDriverProperties.driverName;
}
/** /**
* Fetches a list of pre-registered external formats for prewarming the Vulkan * Fetches a list of pre-registered external formats for prewarming the Vulkan
* pipeline cache. * pipeline cache.
@@ -221,9 +216,6 @@ private:
VkPhysicalDeviceProperties2 mPhysicalDeviceProperties = { VkPhysicalDeviceProperties2 mPhysicalDeviceProperties = {
.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_PROPERTIES_2, .sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_PROPERTIES_2,
}; };
VkPhysicalDeviceDriverProperties mDriverProperties = {
.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_DRIVER_PROPERTIES,
};
VkPhysicalDeviceVulkan11Features mPhysicalDeviceVk11Features = { VkPhysicalDeviceVulkan11Features mPhysicalDeviceVk11Features = {
.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_VULKAN_1_1_FEATURES, .sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_VULKAN_1_1_FEATURES,
}; };
+11 -26
View File
@@ -49,7 +49,6 @@
#include <chrono> #include <chrono>
#include <mutex> #include <mutex>
#include <string_view>
using namespace bluevk; using namespace bluevk;
@@ -1657,17 +1656,6 @@ bool VulkanDriver::isWorkaroundNeeded(Workaround workaround) {
return false; return false;
case Workaround::DISABLE_BLIT_INTO_TEXTURE_ARRAY: case Workaround::DISABLE_BLIT_INTO_TEXTURE_ARRAY:
return false; return false;
case Workaround::BLIT_SSR_HISTORY: {
#if defined(WIN32)
// AMD GPU on windows crashes when SSR is enabled. It's root-caused to be writing to the
// SSR history texture. We blit the history instead of using the linear output of the
// color pass. See Issue #9680.
std::string_view deviceName{ mContext.getDriverName() };
return deviceName.find("AMD proprietary driver") != std::string_view::npos;
#else
return false;
#endif
}
default: default:
return false; return false;
} }
@@ -1987,19 +1975,17 @@ void VulkanDriver::beginRenderPass(Handle<HwRenderTarget> rth, const RenderPassP
rpkey.initialDepthLayout = currentDepthLayout; rpkey.initialDepthLayout = currentDepthLayout;
rpkey.subpassMask = uint8_t(params.subpassMask); rpkey.subpassMask = uint8_t(params.subpassMask);
fvkmemory::resource_ptr<VulkanRenderPass> renderPass = VkRenderPass renderPass = mFramebufferCache.getRenderPass(rpkey);
mFramebufferCache.getRenderPass(rpkey, &mResourceManager);
mPipelineCache.bindRenderPass(renderPass, 0); mPipelineCache.bindRenderPass(renderPass, 0);
// Create the VkFramebuffer or fetch it from cache. // Create the VkFramebuffer or fetch it from cache.
VulkanFboCache::FboKey fbkey = rt->getFboKey(); VulkanFboCache::FboKey fbkey = rt->getFboKey();
fbkey.renderPass = renderPass->getVkRenderPass(); fbkey.renderPass = renderPass;
fbkey.layers = 1; fbkey.layers = 1;
rt->emitBarriersBeginRenderPass(*commandBuffer); rt->emitBarriersBeginRenderPass(*commandBuffer);
fvkmemory::resource_ptr<VulkanFramebuffer> vkfb = VkFramebuffer vkfb = mFramebufferCache.getFramebuffer(fbkey);
mFramebufferCache.getFramebuffer(fbkey, &mResourceManager);
// Assign a label to the framebuffer for debugging purposes. // Assign a label to the framebuffer for debugging purposes.
#if FVK_ENABLED(FVK_DEBUG_GROUP_MARKERS | FVK_DEBUG_DEBUG_UTILS) #if FVK_ENABLED(FVK_DEBUG_GROUP_MARKERS | FVK_DEBUG_DEBUG_UTILS)
@@ -2012,14 +1998,12 @@ void VulkanDriver::beginRenderPass(Handle<HwRenderTarget> rth, const RenderPassP
// The current command buffer now has references to the render target and its attachments. // The current command buffer now has references to the render target and its attachments.
commandBuffer->acquire(rt); commandBuffer->acquire(rt);
commandBuffer->acquire(renderPass);
commandBuffer->acquire(vkfb);
// Populate the structures required for vkCmdBeginRenderPass. // Populate the structures required for vkCmdBeginRenderPass.
VkRenderPassBeginInfo renderPassInfo { VkRenderPassBeginInfo renderPassInfo {
.sType = VK_STRUCTURE_TYPE_RENDER_PASS_BEGIN_INFO, .sType = VK_STRUCTURE_TYPE_RENDER_PASS_BEGIN_INFO,
.renderPass = renderPass->getVkRenderPass(), .renderPass = renderPass,
.framebuffer = vkfb->getVkFramebuffer(), .framebuffer = vkfb,
// The renderArea field constrains the LoadOp, but scissoring does not. // 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(). // Therefore, we do not set the scissor rect here, we only need it in draw().
@@ -2074,7 +2058,7 @@ void VulkanDriver::beginRenderPass(Handle<HwRenderTarget> rth, const RenderPassP
mCurrentRenderPass = { mCurrentRenderPass = {
.commandBuffer = commandBuffer, .commandBuffer = commandBuffer,
.renderTarget = rt, .renderTarget = rt,
.renderPass = renderPass, .renderPass = renderPassInfo.renderPass,
.params = params, .params = params,
.currentSubpass = 0, .currentSubpass = 0,
}; };
@@ -2094,7 +2078,8 @@ void VulkanDriver::endRenderPass(int) {
rt->emitBarriersEndRenderPass(*mCurrentRenderPass.commandBuffer); rt->emitBarriersEndRenderPass(*mCurrentRenderPass.commandBuffer);
mCurrentRenderPass.renderTarget = {}; mCurrentRenderPass.renderTarget = {};
mCurrentRenderPass.renderPass = {}; mCurrentRenderPass.renderPass = VK_NULL_HANDLE;
mCurrentRenderPass.commandBuffer = nullptr; mCurrentRenderPass.commandBuffer = nullptr;
} }
@@ -2259,7 +2244,7 @@ void VulkanDriver::resolve(
Handle<HwTexture> src, uint8_t dstLevel, uint8_t dstLayer) { Handle<HwTexture> src, uint8_t dstLevel, uint8_t dstLayer) {
FVK_SYSTRACE_SCOPE(); FVK_SYSTRACE_SCOPE();
FILAMENT_CHECK_PRECONDITION(!mCurrentRenderPass.renderPass) FILAMENT_CHECK_PRECONDITION(mCurrentRenderPass.renderPass == VK_NULL_HANDLE)
<< "resolve() cannot be invoked inside a render pass."; << "resolve() cannot be invoked inside a render pass.";
auto srcTexture = resource_ptr<VulkanTexture>::cast(&mResourceManager, src); auto srcTexture = resource_ptr<VulkanTexture>::cast(&mResourceManager, src);
@@ -2302,7 +2287,7 @@ void VulkanDriver::blit(
math::uint2 size) { math::uint2 size) {
FVK_SYSTRACE_SCOPE(); FVK_SYSTRACE_SCOPE();
FILAMENT_CHECK_PRECONDITION(!mCurrentRenderPass.renderPass) FILAMENT_CHECK_PRECONDITION(mCurrentRenderPass.renderPass == VK_NULL_HANDLE)
<< "blit() cannot be invoked inside a render pass."; << "blit() cannot be invoked inside a render pass.";
auto srcTexture = resource_ptr<VulkanTexture>::cast(&mResourceManager, src); auto srcTexture = resource_ptr<VulkanTexture>::cast(&mResourceManager, src);
@@ -2344,7 +2329,7 @@ void VulkanDriver::blitDEPRECATED(TargetBufferFlags buffers,
// Note: blitDEPRECATED is only used for Renderer::copyFrame() // Note: blitDEPRECATED is only used for Renderer::copyFrame()
FILAMENT_CHECK_PRECONDITION(!mCurrentRenderPass.renderPass) FILAMENT_CHECK_PRECONDITION(mCurrentRenderPass.renderPass == VK_NULL_HANDLE)
<< "blitDEPRECATED() cannot be invoked inside a render pass."; << "blitDEPRECATED() cannot be invoked inside a render pass.";
FILAMENT_CHECK_PRECONDITION(buffers == TargetBufferFlags::COLOR0) FILAMENT_CHECK_PRECONDITION(buffers == TargetBufferFlags::COLOR0)
+1 -1
View File
@@ -139,7 +139,7 @@ private:
resource_ptr<VulkanSwapChain> mCurrentSwapChain; resource_ptr<VulkanSwapChain> mCurrentSwapChain;
resource_ptr<VulkanRenderTarget> mDefaultRenderTarget; resource_ptr<VulkanRenderTarget> mDefaultRenderTarget;
VulkanRenderPassContext mCurrentRenderPass = {}; VulkanRenderPass mCurrentRenderPass = {};
VmaAllocator mAllocator = VK_NULL_HANDLE; VmaAllocator mAllocator = VK_NULL_HANDLE;
VkDebugReportCallbackEXT mDebugCallback = VK_NULL_HANDLE; VkDebugReportCallbackEXT mDebugCallback = VK_NULL_HANDLE;
+18 -16
View File
@@ -17,7 +17,6 @@
#include "VulkanFboCache.h" #include "VulkanFboCache.h"
#include "VulkanConstants.h" #include "VulkanConstants.h"
#include "VulkanHandles.h"
#include "vulkan/utils/Image.h" #include "vulkan/utils/Image.h"
#include <utils/Panic.h> #include <utils/Panic.h>
@@ -70,10 +69,9 @@ VulkanFboCache::~VulkanFboCache() {
<< "Please explicitly call terminate() while the VkDevice is still alive."; << "Please explicitly call terminate() while the VkDevice is still alive.";
} }
fvkmemory::resource_ptr<VulkanFramebuffer> VulkanFboCache::getFramebuffer( VkFramebuffer VulkanFboCache::getFramebuffer(FboKey const& config) noexcept {
FboKey const& config, fvkmemory::ResourceManager* resManager) noexcept {
FboMap::iterator iter = mFramebufferCache.find(config); FboMap::iterator iter = mFramebufferCache.find(config);
if (UTILS_LIKELY(iter != mFramebufferCache.end())) { if (UTILS_LIKELY(iter != mFramebufferCache.end() && iter->second.handle != VK_NULL_HANDLE)) {
iter.value().timestamp = mCurrentTime; iter.value().timestamp = mCurrentTime;
return iter->second.handle; return iter->second.handle;
} }
@@ -119,16 +117,13 @@ fvkmemory::resource_ptr<VulkanFramebuffer> VulkanFboCache::getFramebuffer(
VkResult error = vkCreateFramebuffer(mDevice, &info, VKALLOC, &framebuffer); VkResult error = vkCreateFramebuffer(mDevice, &info, VKALLOC, &framebuffer);
FILAMENT_CHECK_POSTCONDITION(error == VK_SUCCESS) << "Unable to create framebuffer." FILAMENT_CHECK_POSTCONDITION(error == VK_SUCCESS) << "Unable to create framebuffer."
<< " error=" << static_cast<int32_t>(error); << " error=" << static_cast<int32_t>(error);
fvkmemory::resource_ptr<VulkanFramebuffer> fbh = mFramebufferCache[config] = {framebuffer, mCurrentTime};
fvkmemory::resource_ptr<VulkanFramebuffer>::construct(resManager, mDevice, framebuffer); return framebuffer;
mFramebufferCache[config] = {fbh, mCurrentTime};
return fbh;
} }
fvkmemory::resource_ptr<VulkanRenderPass> VulkanFboCache::getRenderPass( VkRenderPass VulkanFboCache::getRenderPass(RenderPassKey const& config) noexcept {
RenderPassKey const& config, fvkmemory::ResourceManager* resManager) noexcept {
auto iter = mRenderPassCache.find(config); auto iter = mRenderPassCache.find(config);
if (UTILS_LIKELY(iter != mRenderPassCache.end())) { if (UTILS_LIKELY(iter != mRenderPassCache.end() && iter->second.handle != VK_NULL_HANDLE)) {
iter.value().timestamp = mCurrentTime; iter.value().timestamp = mCurrentTime;
return iter->second.handle; return iter->second.handle;
} }
@@ -331,9 +326,7 @@ fvkmemory::resource_ptr<VulkanRenderPass> VulkanFboCache::getRenderPass(
VkResult error = vkCreateRenderPass(mDevice, &renderPassInfo, VKALLOC, &renderPass); VkResult error = vkCreateRenderPass(mDevice, &renderPassInfo, VKALLOC, &renderPass);
FILAMENT_CHECK_POSTCONDITION(error == VK_SUCCESS) << "Unable to create render pass." FILAMENT_CHECK_POSTCONDITION(error == VK_SUCCESS) << "Unable to create render pass."
<< " error=" << error; << " error=" << error;
fvkmemory::resource_ptr<VulkanRenderPass> rph = mRenderPassCache[config] = {renderPass, mCurrentTime};
fvkmemory::resource_ptr<VulkanRenderPass>::construct(resManager, mDevice, renderPass);
mRenderPassCache[config] = {rph, mCurrentTime};
#if FVK_ENABLED(FVK_DEBUG_FBO_CACHE) #if FVK_ENABLED(FVK_DEBUG_FBO_CACHE)
FVK_LOGD << "Created render pass " << renderPass << " with "; FVK_LOGD << "Created render pass " << renderPass << " with ";
@@ -350,12 +343,13 @@ fvkmemory::resource_ptr<VulkanRenderPass> VulkanFboCache::getRenderPass(
<< "colorAttachmentCount[0] = " << subpasses[0].colorAttachmentCount; << "colorAttachmentCount[0] = " << subpasses[0].colorAttachmentCount;
#endif #endif
return rph; return renderPass;
} }
void VulkanFboCache::resetFramebuffers() noexcept { void VulkanFboCache::resetFramebuffers() noexcept {
for (const auto& pair: mFramebufferCache) { for (const auto& pair: mFramebufferCache) {
mRenderPassRefCount[pair.first.renderPass]--; mRenderPassRefCount[pair.first.renderPass]--;
vkDestroyFramebuffer(mDevice, pair.second.handle, VKALLOC);
} }
mFramebufferCache.clear(); mFramebufferCache.clear();
} }
@@ -363,6 +357,9 @@ void VulkanFboCache::resetFramebuffers() noexcept {
void VulkanFboCache::terminate() noexcept { void VulkanFboCache::terminate() noexcept {
resetFramebuffers(); resetFramebuffers();
for (const auto& pair: mRenderPassCache) {
vkDestroyRenderPass(mDevice, pair.second.handle, VKALLOC);
}
mRenderPassRefCount.clear(); mRenderPassRefCount.clear();
mRenderPassCache.clear(); mRenderPassCache.clear();
} }
@@ -383,6 +380,8 @@ void VulkanFboCache::gc() noexcept {
const FboVal fbo = iter->second; const FboVal fbo = iter->second;
if (fbo.timestamp < evictTime && fbo.handle) { if (fbo.timestamp < evictTime && fbo.handle) {
mRenderPassRefCount[iter->first.renderPass]--; mRenderPassRefCount[iter->first.renderPass]--;
vkDestroyFramebuffer(mDevice, fbo.handle, VKALLOC);
iter.value().handle = VK_NULL_HANDLE;
// erase(iterator) returns the iterator to the next element. // erase(iterator) returns the iterator to the next element.
iter = mFramebufferCache.erase(iter); iter = mFramebufferCache.erase(iter);
@@ -392,8 +391,11 @@ void VulkanFboCache::gc() noexcept {
} }
for (RenderPassMap::iterator iter = mRenderPassCache.begin(); iter != mRenderPassCache.end(); ) { for (RenderPassMap::iterator iter = mRenderPassCache.begin(); iter != mRenderPassCache.end(); ) {
const VkRenderPass handle = iter->second.handle->getVkRenderPass(); const VkRenderPass handle = iter->second.handle;
if (iter->second.timestamp < evictTime && handle && mRenderPassRefCount[handle] == 0) { 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. // erase(iterator) returns the iterator to the next element.
iter = mRenderPassCache.erase(iter); iter = mRenderPassCache.erase(iter);
mRenderPassRefCount.erase(handle); mRenderPassRefCount.erase(handle);
+4 -12
View File
@@ -18,9 +18,6 @@
#define TNT_FILAMENT_BACKEND_VULKANFBOCACHE_H #define TNT_FILAMENT_BACKEND_VULKANFBOCACHE_H
#include "VulkanContext.h" #include "VulkanContext.h"
#include "vulkan/memory/Resource.h"
#include "vulkan/memory/ResourceManager.h"
#include "vulkan/memory/ResourcePointer.h"
#include <utils/Hash.h> #include <utils/Hash.h>
@@ -30,9 +27,6 @@
namespace filament::backend { namespace filament::backend {
struct VulkanFramebuffer;
struct VulkanRenderPass;
// Simple manager for VkFramebuffer and VkRenderPass objects. // 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, // Note that a VkFramebuffer is just a binding between a render pass and a set of image views. So,
@@ -63,7 +57,7 @@ public:
uint8_t padding[2]; uint8_t padding[2];
}; };
struct RenderPassVal { struct RenderPassVal {
fvkmemory::resource_ptr<VulkanRenderPass> handle; VkRenderPass handle;
uint32_t timestamp; uint32_t timestamp;
}; };
static_assert(0 == MRT::MAX_SUPPORTED_RENDER_TARGET_COUNT % 8); static_assert(0 == MRT::MAX_SUPPORTED_RENDER_TARGET_COUNT % 8);
@@ -89,7 +83,7 @@ public:
VkImageView depth; // 8 bytes VkImageView depth; // 8 bytes
}; };
struct FboVal { struct FboVal {
fvkmemory::resource_ptr<VulkanFramebuffer> handle; VkFramebuffer handle;
uint32_t timestamp; uint32_t timestamp;
}; };
static_assert(sizeof(VkRenderPass) == 8, "VkRenderPass has unexpected size."); static_assert(sizeof(VkRenderPass) == 8, "VkRenderPass has unexpected size.");
@@ -104,12 +98,10 @@ public:
~VulkanFboCache(); ~VulkanFboCache();
// Retrieves or creates a VkFramebuffer handle. // Retrieves or creates a VkFramebuffer handle.
fvkmemory::resource_ptr<VulkanFramebuffer> getFramebuffer( VkFramebuffer getFramebuffer(FboKey const& config) noexcept;
FboKey const& config, fvkmemory::ResourceManager* resManager) noexcept;
// Retrieves or creates a VkRenderPass handle. // Retrieves or creates a VkRenderPass handle.
fvkmemory::resource_ptr<VulkanRenderPass> getRenderPass( VkRenderPass getRenderPass(RenderPassKey const& config) noexcept;
RenderPassKey const& config, fvkmemory::ResourceManager* resManager) noexcept;
// Evicts old unused Vulkan objects. Call this once per frame. // Evicts old unused Vulkan objects. Call this once per frame.
void gc() noexcept; void gc() noexcept;
+1 -15
View File
@@ -565,7 +565,7 @@ void VulkanRenderTarget::transformViewportToPlatform(VkViewport* bounds) const {
flipVertically(bounds, getExtent().height); flipVertically(bounds, getExtent().height);
} }
uint8_t VulkanRenderTarget::getColorTargetCount(const VulkanRenderPassContext& pass) const { uint8_t VulkanRenderTarget::getColorTargetCount(const VulkanRenderPass& pass) const {
if (!mOffscreen) { if (!mOffscreen) {
return 1; return 1;
} }
@@ -715,18 +715,4 @@ VulkanRenderPrimitive::VulkanRenderPrimitive(PrimitiveType pt,
vertexBuffer(vb), vertexBuffer(vb),
indexBuffer(ib) {} 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 } // namespace filament::backend
+1 -27
View File
@@ -419,7 +419,7 @@ struct VulkanRenderTarget : private HwRenderTarget, fvkmemory::Resource {
return mInfo->fbkey.samples; return mInfo->fbkey.samples;
} }
uint8_t getColorTargetCount(VulkanRenderPassContext const& pass) const; uint8_t getColorTargetCount(VulkanRenderPass const& pass) const;
inline bool hasDepth() const { return mInfo->depthIndex != Auxiliary::UNDEFINED_INDEX; } inline bool hasDepth() const { return mInfo->depthIndex != Auxiliary::UNDEFINED_INDEX; }
@@ -585,32 +585,6 @@ struct VulkanRenderPrimitive : public HwRenderPrimitive, fvkmemory::Resource {
fvkmemory::resource_ptr<VulkanIndexBuffer> indexBuffer; 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 } // namespace filament::backend
#endif // TNT_FILAMENT_BACKEND_VULKANHANDLES_H #endif // TNT_FILAMENT_BACKEND_VULKANHANDLES_H
@@ -459,10 +459,8 @@ void VulkanPipelineCache::bindStencilState(StencilState const& stencilState) noe
mPipelineRequirements.stencilState = stencilState; mPipelineRequirements.stencilState = stencilState;
} }
void VulkanPipelineCache::bindRenderPass( void VulkanPipelineCache::bindRenderPass(VkRenderPass renderPass, int subpassIndex) noexcept {
fvkmemory::resource_ptr<VulkanRenderPass> renderPass, mPipelineRequirements.renderPass = renderPass;
int subpassIndex) noexcept {
mPipelineRequirements.renderPass = renderPass->getVkRenderPass();
mPipelineRequirements.subpassIndex = subpassIndex; mPipelineRequirements.subpassIndex = subpassIndex;
} }
@@ -123,8 +123,7 @@ public:
void bindProgram(fvkmemory::resource_ptr<VulkanProgram> program) noexcept; void bindProgram(fvkmemory::resource_ptr<VulkanProgram> program) noexcept;
void bindRasterState(RasterState const& rasterState) noexcept; void bindRasterState(RasterState const& rasterState) noexcept;
void bindStencilState(StencilState const& stencilState) noexcept; void bindStencilState(StencilState const& stencilState) noexcept;
void bindRenderPass(fvkmemory::resource_ptr<VulkanRenderPass> renderPass, void bindRenderPass(VkRenderPass renderPass, int subpassIndex) noexcept;
int subpassIndex) noexcept;
void bindPrimitiveTopology(VkPrimitiveTopology topology) noexcept; void bindPrimitiveTopology(VkPrimitiveTopology topology) noexcept;
void bindVertexArray(VkVertexInputAttributeDescription const* attribDesc, void bindVertexArray(VkVertexInputAttributeDescription const* attribDesc,
VkVertexInputBindingDescription const* bufferDesc, uint8_t count); VkVertexInputBindingDescription const* bufferDesc, uint8_t count);
@@ -42,8 +42,6 @@ template ResourceType getTypeEnum<VulkanSync>() noexcept;
template ResourceType getTypeEnum<VulkanMemoryMappedBuffer>() noexcept; template ResourceType getTypeEnum<VulkanMemoryMappedBuffer>() noexcept;
template ResourceType getTypeEnum<VulkanSemaphore>() noexcept; template ResourceType getTypeEnum<VulkanSemaphore>() noexcept;
template ResourceType getTypeEnum<VulkanStream>() noexcept; template ResourceType getTypeEnum<VulkanStream>() noexcept;
template ResourceType getTypeEnum<VulkanFramebuffer>() noexcept;
template ResourceType getTypeEnum<VulkanRenderPass>() noexcept;
template<typename D> template<typename D>
ResourceType getTypeEnum() noexcept { ResourceType getTypeEnum() noexcept {
@@ -110,12 +108,6 @@ ResourceType getTypeEnum() noexcept {
if constexpr (std::is_same_v<D, VulkanStream>) { if constexpr (std::is_same_v<D, VulkanStream>) {
return ResourceType::STREAM; 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; return ResourceType::UNDEFINED_TYPE;
} }
@@ -163,10 +155,6 @@ std::string_view getTypeStr(ResourceType type) {
return "Semaphore"; return "Semaphore";
case ResourceType::STREAM: case ResourceType::STREAM:
return "VulkanStream"; return "VulkanStream";
case ResourceType::FRAMEBUFFER:
return "Framebuffer";
case ResourceType::RENDER_PASS:
return "RenderPass";
case ResourceType::UNDEFINED_TYPE: case ResourceType::UNDEFINED_TYPE:
return ""; return "";
} }
@@ -56,9 +56,7 @@ enum class ResourceType : uint8_t {
MEMORY_MAPPED_BUFFER = 18, MEMORY_MAPPED_BUFFER = 18,
SEMAPHORE = 19, SEMAPHORE = 19,
STREAM = 20, STREAM = 20,
FRAMEBUFFER = 21, UNDEFINED_TYPE = 21, // Must be the last enum because we use it for iterating over the enums.
RENDER_PASS = 22,
UNDEFINED_TYPE = 23, // Must be the last enum because we use it for iterating over the enums.
}; };
template<typename D> template<typename D>
@@ -126,12 +126,6 @@ void ResourceManager::destroyWithType(ResourceType type, HandleId id) {
case ResourceType::STREAM: case ResourceType::STREAM:
destruct<VulkanStream>(Handle<VulkanStream>(id)); destruct<VulkanStream>(Handle<VulkanStream>(id));
break; break;
case ResourceType::FRAMEBUFFER:
destruct<VulkanFramebuffer>(Handle<VulkanFramebuffer>(id));
break;
case ResourceType::RENDER_PASS:
destruct<VulkanRenderPass>(Handle<VulkanRenderPass>(id));
break;
case ResourceType::UNDEFINED_TYPE: case ResourceType::UNDEFINED_TYPE:
break; break;
} }
@@ -952,9 +952,6 @@ void VulkanPlatform::queryAndSetDeviceFeatures(Platform::DriverConfig const& dri
chainStruct(&context.mPhysicalDeviceFeatures, &globalPriorityFeatures); chainStruct(&context.mPhysicalDeviceFeatures, &globalPriorityFeatures);
} }
// Obtain and store driver info
chainStruct(&context.mPhysicalDeviceProperties, &context.mDriverProperties);
// Initialize the following fields: physicalDeviceProperties, memoryProperties, // Initialize the following fields: physicalDeviceProperties, memoryProperties,
// physicalDeviceFeatures. // physicalDeviceFeatures.
vkGetPhysicalDeviceProperties2(mImpl->mPhysicalDevice, &context.mPhysicalDeviceProperties); vkGetPhysicalDeviceProperties2(mImpl->mPhysicalDevice, &context.mPhysicalDeviceProperties);
@@ -18,7 +18,6 @@
#include "Shader.h" #include "Shader.h"
#include "SharedShaders.h" #include "SharedShaders.h"
#include "Skip.h"
#include "TrianglePrimitive.h" #include "TrianglePrimitive.h"
#include <backend/PixelBufferDescriptor.h> #include <backend/PixelBufferDescriptor.h>
@@ -32,9 +31,6 @@ using namespace filament::backend;
using namespace filament::math; using namespace filament::math;
TEST_F(BackendTest, AutoresolveDifferingSampleCounts) { 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(); auto& api = getDriverApi();
constexpr int kRenderTargetSize = 512; constexpr int kRenderTargetSize = 512;
+16 -16
View File
@@ -271,8 +271,8 @@ std::unique_ptr<MaterialDefinition> MaterialDefinition::create(FEngine& engine,
void MaterialDefinition::terminate(FEngine& engine) { void MaterialDefinition::terminate(FEngine& engine) {
DriverApi& driver = engine.getDriverApi(); DriverApi& driver = engine.getDriverApi();
perViewDescriptorSetLayoutPcf.terminate(engine.getDescriptorSetLayoutFactory(), driver); perViewDescriptorSetLayout.terminate(engine.getDescriptorSetLayoutFactory(), driver);
perViewDescriptorSetLayoutS2d.terminate(engine.getDescriptorSetLayoutFactory(), driver); perViewDescriptorSetLayoutVsm.terminate(engine.getDescriptorSetLayoutFactory(), driver);
descriptorSetLayout.terminate(engine.getDescriptorSetLayoutFactory(), driver); descriptorSetLayout.terminate(engine.getDescriptorSetLayoutFactory(), driver);
} }
@@ -607,23 +607,23 @@ void MaterialDefinition::processDescriptorSets(FEngine& engine) {
refractionMode == RefractionMode::SCREEN_SPACE; refractionMode == RefractionMode::SCREEN_SPACE;
bool const hasFog = !(variantFilterMask & UserVariantFilterMask(UserVariantFilterBit::FOG)); bool const hasFog = !(variantFilterMask & UserVariantFilterMask(UserVariantFilterBit::FOG));
this->perViewDescriptorSetLayoutPcfDescription = descriptor_sets::getPerViewDescriptorSetLayout( this->perViewDescriptorSetLayoutDescription = descriptor_sets::getPerViewDescriptorSetLayout(
materialDomain, isLit, isSSR, hasFog, false); materialDomain, isLit, isSSR, hasFog, false);
this->perViewDescriptorSetLayoutS2dDescription = descriptor_sets::getPerViewDescriptorSetLayout( this->perViewDescriptorSetLayoutVsmDescription = descriptor_sets::getPerViewDescriptorSetLayout(
materialDomain, isLit, isSSR, hasFog, true); materialDomain, isLit, isSSR, hasFog, true);
// set the labels // set the labels
this->descriptorSetLayoutDescription.label = CString{ name }.append("_perMat"); this->descriptorSetLayoutDescription.label = CString{ name }.append("_perMat");
this->perViewDescriptorSetLayoutPcfDescription.label = CString{ name }.append("_perView"); this->perViewDescriptorSetLayoutDescription.label = CString{ name }.append("_perView");
this->perViewDescriptorSetLayoutS2dDescription.label = CString{ name }.append("_perViewVsm"); this->perViewDescriptorSetLayoutVsmDescription.label = CString{ name }.append("_perViewVsm");
// get the PER_RENDERABLE and PER_VIEW descriptor binding info // get the PER_RENDERABLE and PER_VIEW descriptor binding info
for (auto&& [bindingPoint, dsl] : { for (auto&& [bindingPoint, dsl] : {
std::pair{ DescriptorSetBindingPoints::PER_RENDERABLE, std::pair{ DescriptorSetBindingPoints::PER_RENDERABLE,
descriptor_sets::getPerRenderableLayout() }, descriptor_sets::getPerRenderableLayout() },
std::pair{ DescriptorSetBindingPoints::PER_VIEW, std::pair{ DescriptorSetBindingPoints::PER_VIEW,
this->perViewDescriptorSetLayoutPcfDescription }}) { this->perViewDescriptorSetLayoutDescription }}) {
Program::DescriptorBindingsInfo& descriptors = programDescriptorBindings[+bindingPoint]; Program::DescriptorBindingsInfo& descriptors = programDescriptorBindings[+bindingPoint];
descriptors.reserve(dsl.descriptors.size()); descriptors.reserve(dsl.descriptors.size());
for (auto const& entry: dsl.descriptors) { for (auto const& entry: dsl.descriptors) {
@@ -636,17 +636,17 @@ void MaterialDefinition::processDescriptorSets(FEngine& engine) {
descriptorSetLayoutFactory, driver, descriptorSetLayoutFactory, driver,
this->descriptorSetLayoutDescription }; this->descriptorSetLayoutDescription };
this->perViewDescriptorSetLayoutPcf = { this->perViewDescriptorSetLayout = {
descriptorSetLayoutFactory, driver, descriptorSetLayoutFactory, driver,
this->perViewDescriptorSetLayoutPcfDescription }; this->perViewDescriptorSetLayoutDescription };
this->perViewDescriptorSetLayoutS2d = { this->perViewDescriptorSetLayoutVsm = {
descriptorSetLayoutFactory, driver, descriptorSetLayoutFactory, driver,
this->perViewDescriptorSetLayoutS2dDescription }; this->perViewDescriptorSetLayoutVsmDescription };
} }
backend::DescriptorSetLayout const& MaterialDefinition::getPerViewDescriptorSetLayoutDescription( backend::DescriptorSetLayout const& MaterialDefinition::getPerViewDescriptorSetLayoutDescription(
Variant const variant, bool const useS2dDescriptorSetLayout) const noexcept { Variant const variant, bool const useVsmDescriptorSetLayout) const noexcept {
if (materialDomain == MaterialDomain::SURFACE) { if (materialDomain == MaterialDomain::SURFACE) {
if (Variant::isValidDepthVariant(variant)) { if (Variant::isValidDepthVariant(variant)) {
// Use the layout description used to create the per view depth variant layout. // Use the layout description used to create the per view depth variant layout.
@@ -657,10 +657,10 @@ backend::DescriptorSetLayout const& MaterialDefinition::getPerViewDescriptorSetL
return descriptor_sets::getSsrVariantLayout(); return descriptor_sets::getSsrVariantLayout();
} }
} }
if (useS2dDescriptorSetLayout) { if (useVsmDescriptorSetLayout) {
return perViewDescriptorSetLayoutS2dDescription; return perViewDescriptorSetLayoutVsmDescription;
} }
return perViewDescriptorSetLayoutPcfDescription; return perViewDescriptorSetLayoutDescription;
} }
Handle<HwProgram> MaterialDefinition::compileProgram( Handle<HwProgram> MaterialDefinition::compileProgram(
@@ -689,7 +689,7 @@ Handle<HwProgram> MaterialDefinition::compileProgram(
pb.descriptorLayout(+DescriptorSetBindingPoints::PER_VIEW, pb.descriptorLayout(+DescriptorSetBindingPoints::PER_VIEW,
getPerViewDescriptorSetLayoutDescription( getPerViewDescriptorSetLayoutDescription(
specialization.variant, specialization.variant,
Variant::isShadowSampler2DVariant(specialization.variant))); Variant::isVSMVariant(specialization.variant)));
pb.descriptorLayout(+DescriptorSetBindingPoints::PER_RENDERABLE, pb.descriptorLayout(+DescriptorSetBindingPoints::PER_RENDERABLE,
descriptor_sets::getPerRenderableLayout()); descriptor_sets::getPerRenderableLayout());
pb.descriptorLayout( pb.descriptorLayout(
+5 -5
View File
@@ -94,17 +94,17 @@ struct MaterialDefinition {
backend::ShaderModel const sm, bool isStereoSupported) const noexcept; backend::ShaderModel const sm, bool isStereoSupported) const noexcept;
backend::DescriptorSetLayout const& getPerViewDescriptorSetLayoutDescription( backend::DescriptorSetLayout const& getPerViewDescriptorSetLayoutDescription(
Variant const variant, bool useS2dDescriptorSetLayout) const noexcept; Variant const variant, bool useVsmDescriptorSetLayout) const noexcept;
// Keep track of the definitions of the descriptor set layouts, as these // Keep track of the definitions of the descriptor set layouts, as these
// may be used by some backends in parallel compilation of programs. // may be used by some backends in parallel compilation of programs.
backend::DescriptorSetLayout perViewDescriptorSetLayoutPcfDescription; backend::DescriptorSetLayout perViewDescriptorSetLayoutDescription;
backend::DescriptorSetLayout perViewDescriptorSetLayoutS2dDescription; backend::DescriptorSetLayout perViewDescriptorSetLayoutVsmDescription;
backend::DescriptorSetLayout descriptorSetLayoutDescription; backend::DescriptorSetLayout descriptorSetLayoutDescription;
// try to order by frequency of use // try to order by frequency of use
filament::DescriptorSetLayout perViewDescriptorSetLayoutPcf; filament::DescriptorSetLayout perViewDescriptorSetLayout;
filament::DescriptorSetLayout perViewDescriptorSetLayoutS2d; filament::DescriptorSetLayout perViewDescriptorSetLayoutVsm;
filament::DescriptorSetLayout descriptorSetLayout; filament::DescriptorSetLayout descriptorSetLayout;
backend::Program::DescriptorSetInfo programDescriptorBindings; backend::Program::DescriptorSetInfo programDescriptorBindings;
+1 -1
View File
@@ -802,7 +802,7 @@ FrameGraphId<FrameGraphTexture> PostProcessManager::ssr(FrameGraph& fg,
// use our special SSR variant, it can only be applied to object that have // use our special SSR variant, it can only be applied to object that have
// the SCREEN_SPACE ReflectionMode. // the SCREEN_SPACE ReflectionMode.
passBuilder.variant(Variant{ Variant::SPECIAL_SSR_VARIANT }); passBuilder.variant(Variant{ Variant::SPECIAL_SSR });
// generate all our drawing commands, except blended objects. // generate all our drawing commands, except blended objects.
passBuilder.commandTypeFlags(RenderPass::CommandTypeFlags::SCREEN_SPACE_REFLECTIONS); passBuilder.commandTypeFlags(RenderPass::CommandTypeFlags::SCREEN_SPACE_REFLECTIONS);
+2 -2
View File
@@ -568,7 +568,7 @@ RenderPass::Command* RenderPass::generateCommandsImpl(CommandTypeFlags extraFlag
if constexpr (isDepthPass) { if constexpr (isDepthPass) {
cmd.info.materialVariant = variant; cmd.info.materialVariant = variant;
cmd.info.rasterState = {}; cmd.info.rasterState = {};
cmd.info.rasterState.colorWrite = Variant::isPickingVariant(variant) || Variant::isDepthMomentsVariant(variant); cmd.info.rasterState.colorWrite = Variant::isPickingVariant(variant) || Variant::isVSMVariant(variant);
cmd.info.rasterState.depthWrite = true; cmd.info.rasterState.depthWrite = true;
cmd.info.rasterState.depthFunc = RasterState::DepthFunc::GE; cmd.info.rasterState.depthFunc = RasterState::DepthFunc::GE;
cmd.info.rasterState.alphaToCoverage = false; cmd.info.rasterState.alphaToCoverage = false;
@@ -616,7 +616,7 @@ RenderPass::Command* RenderPass::generateCommandsImpl(CommandTypeFlags extraFlag
bool const hasSkinningOrMorphing = hasSkinning || hasMorphing; bool const hasSkinningOrMorphing = hasSkinning || hasMorphing;
// if we are already an SSR variant, the SRE bit is already set // if we are already an SSR variant, the SRE bit is already set
static_assert(Variant::SPECIAL_SSR_VARIANT & Variant::SRE); static_assert(Variant::SPECIAL_SSR & Variant::SRE);
Variant renderableVariant{ variant }; Variant renderableVariant{ variant };
// we can't have SSR and shadowing together by construction // we can't have SSR and shadowing together by construction
+75 -58
View File
@@ -213,14 +213,12 @@ ShadowMap::ShaderParameters ShadowMap::updateDirectional(FEngine& engine,
* Final shadow map transform * Final shadow map transform
*/ */
// Final shadow transform (focused warped light-space), world space to clip space // Final shadow transform (focused warped light-space)
const mat4f S = F * (W * LMpMv); const mat4f S = F * (W * LMpMv);
// Computes St the transform to use in the shader to access the shadow map texture // Computes St the transform to use in the shader to access the shadow map texture
// i.e. it transforms a world-space vertex to a texture coordinate in the shadowmap // i.e. it transforms a world-space vertex to a texture coordinate in the shadowmap
const auto [Mt, Mn] = getTextureCoordsMapping(shadowMapInfo, getViewport()); const auto [Mt, Mn] = getTextureCoordsMapping(shadowMapInfo, getViewport());
// world space to texture atlas space
const mat4f St = highPrecisionMultiply(Mt, S); const mat4f St = highPrecisionMultiply(Mt, S);
ShaderParameters shaderParameters; ShaderParameters shaderParameters;
@@ -228,12 +226,14 @@ ShadowMap::ShaderParameters ShadowMap::updateDirectional(FEngine& engine,
// note: in texelSizeWorldSpace() below, we can use Mb * Mt * F * W because // note: in texelSizeWorldSpace() below, we can use Mb * Mt * F * W because
// L * Mp * Mv is a rigid transform for directional lights, and doesn't matter. // L * Mp * Mv is a rigid transform for directional lights, and doesn't matter.
// if Wp[3][1] is 0, then LiSPSM was canceled. // if Wp[3][1] is 0, then LISPSM was cancelled.
if (useLispsm && Wp[3][1] != 0.0f) { if (useLispsm && Wp[3][1] != 0.0f) {
shaderParameters.texelSizeAtOneMeterWs = texelSizeWorldSpace(S, shadowMapInfo.shadowDimension); shaderParameters.texelSizeAtOneMeterWs =
texelSizeWorldSpace(Wp, mat4f(Mt * F), shadowMapInfo.shadowDimension);
} else { } else {
// We know we're using an ortho projection // We know we're using an ortho projection
shaderParameters.texelSizeAtOneMeterWs = texelSizeWorldSpace(S.upperLeft(), shadowMapInfo.shadowDimension); shaderParameters.texelSizeAtOneMeterWs =
texelSizeWorldSpace(St.upperLeft(), shadowMapInfo.shadowDimension);
} }
if (!shadowMapInfo.vsm) { if (!shadowMapInfo.vsm) {
shaderParameters.lightSpace = St; shaderParameters.lightSpace = St;
@@ -1117,67 +1117,84 @@ bool ShadowMap::intersectSegmentWithPlanarQuad(float3& UTILS_RESTRICT p,
return hit; return hit;
} }
float2 ShadowMap::texelSizeWorldSpace(const mat3f& clipFromWorld, uint16_t shadowDimension) noexcept { float ShadowMap::texelSizeWorldSpace(const mat3f& worldToShadowTexture,
uint16_t shadowDimension) noexcept {
// The Jacobian of the transformation from texture-to-world is the matrix itself for // The Jacobian of the transformation from texture-to-world is the matrix itself for
// orthographic projections. We just need to inverse shadowMapFromWorld, // orthographic projections. We just need to inverse worldToShadowTexture,
// which is guaranteed to be orthographic. // which is guaranteed to be orthographic.
// The two first columns give us how a texel maps in world-space. // The two first columns give us how a texel maps in world-space.
float const oneTexel = 2.0f / float(shadowDimension); const float ures = 1.0f / float(shadowDimension);
mat3f const worldFromClip(inverse(clipFromWorld)); const float vres = 1.0f / float(shadowDimension);
float3 const Jx = worldFromClip[0]; const mat3f shadowTextureToWorld(inverse(worldToShadowTexture));
float3 const Jy = worldFromClip[1]; const float3 Jx = shadowTextureToWorld[0];
float2 const s = float2{ length(Jx), length(Jy) } * oneTexel; const float3 Jy = shadowTextureToWorld[1];
const float s = std::max(length(Jx) * ures, length(Jy) * vres);
return s; return s;
} }
/** float ShadowMap::texelSizeWorldSpace(const mat4f& Wp, const mat4f& MbMtF,
* Calculates the Jacobian matrix J = ∂(u,v,d)/∂(x,y,z) for a 4x4 perspective projection. uint16_t shadowDimension) noexcept {
* // Here we compute the Jacobian of inverse(MbMtF * Wp).
* Given a view-space point P = [x, y, z, 1]^T and a projection matrix M, // The expression below has been computed with Mathematica. However, it's not very hard,
* the projected homogeneous coordinates are [X, Y, Z, W]^T = M * P. // albeit error-prone, to do it by hand because MbMtF is a linear transform.
* The resulting NDC coordinates are u = X/W, v = Y/W, and depth d = Z/W. // So we really only need to calculate the Jacobian of inverse(Wp) at inverse(MbMtF).
* //
* To find the Jacobian on the CPU, we apply the quotient rule to each component: // Because we're only interested in the length of the columns of the Jacobian, we can use
* ∂(X/W) / ∂xi = ( (∂X/∂xi) * W - X * (∂W/∂xi) ) / W^2 // Mb * Mt * F * Wp instead of the full expression Mb * Mt * F * Wp * Wv * L * Mp * Mv,
* // because Wv * L * Mp * Mv is a rigid transform, which doesn't affect the length of
* In matrix form, this can be expressed as: // the Jacobian's column vectors.
* J = (1/W) * [ M_sub - (1/W) * (T ⊗ w_grad) ]
*
* Where:
* - W: The homogeneous w-component after projection (usually -z for standard mats).
* - M_sub: The top-left 3x3 submatrix of M.
* - T: The column vector [X, Y, Z]^T (pre-perspective divide).
* - w_grad: The row vector [m30, m31, m32] (the first three elements of M's last row).
* - ⊗: The outer product, resulting in a 3x3 matrix.
*
* This Jacobian describes the local "stretch" of the projection. For LiSPSM or
* shadow mapping, the inverse Jacobian J^-1 provides the world-space footprint
* of a shadow texel, which is essential for calculating an accurate,
* non-constant depth bias to eliminate shadow acne.
*
* @param M The 4x4 projection matrix.
* @param p The 3D point in view-space where the Jacobian is evaluated.
* @return A 3x3 matrix representing the partial derivatives of (u,v,d) w.r.t (x,y,z).
*/
static mat3f jacobian(mat4f const& M, float3 const& p) noexcept {
float4 const T = M * p;
mat3f const M_sub = M.upperLeft();
float3 const w_grad = { M[0].w, M[1].w, M[2].w };
mat3f const t_cross_w{
w_grad.x * T.xyz,
w_grad.y * T.xyz,
w_grad.z * T.xyz
};
return (M_sub - t_cross_w / T.w) / T.w;
}
float2 ShadowMap::texelSizeWorldSpace(mat4f const& S, uint16_t const shadowDimension) noexcept {
// The Jacobian is not constant, so we evaluate it in the center of the shadow-map texture. // The Jacobian is not constant, so we evaluate it in the center of the shadow-map texture.
// It might be better to do this computation in the vertex shader. // It might be better to do this computation in the vertex shader.
float3 const p = { 0.0f, 0.0f, 0.0f }; // clip-space float3 const p = { 0.5f, 0.5f, 0.0f };
float const oneTexel = 2.0f / float(shadowDimension);
mat3f const J = jacobian(inverse(S), p); const float ures = 1.0f / float(shadowDimension);
float2 const s = float2{ length(J[0]), length(J[1]) } * oneTexel; const float vres = 1.0f / float(shadowDimension);
const float dres = 1.0f / 65536.0f;
constexpr bool JACOBIAN_ESTIMATE = false;
if constexpr (JACOBIAN_ESTIMATE) {
// This estimates the Jacobian -- this is a lot heavier. This is mostly for reference
// and testing.
const mat4f Si(inverse(MbMtF * Wp));
const float3 p0 = mat4f::project(Si, p);
const float3 p1 = mat4f::project(Si, p + float3{ 1, 0, 0 } * ures);
const float3 p2 = mat4f::project(Si, p + float3{ 0, 1, 0 } * vres);
const float3 p3 = mat4f::project(Si, p + float3{ 0, 0, 1 } * dres);
const float3 Jx = p1 - p0;
const float3 Jy = p2 - p0;
const float3 UTILS_UNUSED Jz = p3 - p0;
const float s = std::max(length(Jx), length(Jy));
return s;
}
const float n = Wp[0][0];
const float A = Wp[1][1];
const float B = Wp[3][1];
const float sx = MbMtF[0][0];
const float sy = MbMtF[1][1];
const float sz = MbMtF[2][2];
const float ox = MbMtF[3][0];
const float oy = MbMtF[3][1];
const float oz = MbMtF[3][2];
const float X = p.x - ox;
const float Y = p.y - oy;
const float Z = p.z - oz;
const float dz = A * sy - Y;
const float nsxsz = n * sx * sz;
const float j = -(B * sy) / (nsxsz * dz * dz);
const mat3f J(mat3f::row_major_init{
j * dz * sz, j * X * sz, 0.0f,
0.0f, j * nsxsz, 0.0f,
0.0f, j * Z * sx, j * dz * sx
});
float3 const Jx = J[0] * ures;
float3 const Jy = J[1] * vres;
UTILS_UNUSED float3 const Jz = J[2] * dres;
const float s = std::max(length(Jx), length(Jy));
return s; return s;
} }
+6 -3
View File
@@ -135,7 +135,7 @@ public:
math::mat4f lightSpace{}; math::mat4f lightSpace{};
math::float4 lightFromWorldZ{}; math::float4 lightFromWorldZ{};
math::float4 scissorNormalized{}; math::float4 scissorNormalized{};
math::float2 texelSizeAtOneMeterWs{}; float texelSizeAtOneMeterWs{};
}; };
// Call once per frame if the light, scene (or visible layers) or camera changes. // Call once per frame if the light, scene (or visible layers) or camera changes.
@@ -319,8 +319,11 @@ private:
math::float4 getClampToEdgeCoords(ShadowMapInfo const& shadowMapInfo) const noexcept; math::float4 getClampToEdgeCoords(ShadowMapInfo const& shadowMapInfo) const noexcept;
static math::float2 texelSizeWorldSpace(const math::mat3f& clipFromWorld, uint16_t shadowDimension) noexcept; static float texelSizeWorldSpace(const math::mat3f& worldToShadowTexture,
static math::float2 texelSizeWorldSpace(const math::mat4f& clipFromWorld, uint16_t shadowDimension) noexcept; uint16_t shadowDimension) noexcept;
static float texelSizeWorldSpace(const math::mat4f& W, const math::mat4f& MbMtF,
uint16_t shadowDimension) noexcept;
static constexpr Segment sBoxSegments[12] = { static constexpr Segment sBoxSegments[12] = {
{ 0, 1 }, { 1, 3 }, { 3, 2 }, { 2, 0 }, { 0, 1 }, { 1, 3 }, { 3, 2 }, { 2, 0 },
+10 -7
View File
@@ -742,16 +742,17 @@ ShadowMapManager::ShadowTechnique ShadowMapManager::updateCascadeShadowMaps(FEng
// Texel size is constant for directional light (although that's not true when LISPSM // Texel size is constant for directional light (although that's not true when LISPSM
// is used, but in that case we're pretending it is). // is used, but in that case we're pretending it is).
float2 const wsTexelSize = shaderParameters.texelSizeAtOneMeterWs; const float wsTexelSize = shaderParameters.texelSizeAtOneMeterWs;
auto& s = mShadowUb.edit(); auto& s = mShadowUb.edit();
s.shadows[shadowIndex].layer = shadowMap.getLayer(); s.shadows[shadowIndex].layer = shadowMap.getLayer();
s.shadows[shadowIndex].lightFromWorldMatrix = shaderParameters.lightSpace; s.shadows[shadowIndex].lightFromWorldMatrix = shaderParameters.lightSpace;
s.shadows[shadowIndex].scissorNormalized = shaderParameters.scissorNormalized; s.shadows[shadowIndex].scissorNormalized = shaderParameters.scissorNormalized;
s.shadows[shadowIndex].normalBias = wsTexelSize * normalBias; s.shadows[shadowIndex].normalBias = normalBias * wsTexelSize;
s.shadows[shadowIndex].texelSizeAtOneMeter = wsTexelSize;
s.shadows[shadowIndex].elvsm = options.vsm.elvsm; s.shadows[shadowIndex].elvsm = options.vsm.elvsm;
s.shadows[shadowIndex].bulbRadiusLs = s.shadows[shadowIndex].bulbRadiusLs =
mSoftShadowOptions.penumbraScale * options.shadowBulbRadius / length(wsTexelSize); mSoftShadowOptions.penumbraScale * options.shadowBulbRadius / wsTexelSize;
shadowTechnique |= ShadowTechnique::SHADOW_MAP; shadowTechnique |= ShadowTechnique::SHADOW_MAP;
cascadeHasVisibleShadows |= 0x1u << i; cascadeHasVisibleShadows |= 0x1u << i;
@@ -832,7 +833,7 @@ void ShadowMapManager::prepareSpotShadowMap(ShadowMap& shadowMap, FEngine& engin
// and if we need to generate it, update all the UBO data // and if we need to generate it, update all the UBO data
if (shadowMap.hasVisibleShadows()) { if (shadowMap.hasVisibleShadows()) {
const size_t shadowIndex = shadowMap.getShadowIndex(); const size_t shadowIndex = shadowMap.getShadowIndex();
const float2 wsTexelSizeAtOneMeter = shaderParameters.texelSizeAtOneMeterWs; const float wsTexelSizeAtOneMeter = shaderParameters.texelSizeAtOneMeterWs;
// note: normalBias is set to zero for VSM // note: normalBias is set to zero for VSM
const float normalBias = shadowMapInfo.vsm ? 0.0f : options->normalBias; const float normalBias = shadowMapInfo.vsm ? 0.0f : options->normalBias;
@@ -844,11 +845,12 @@ void ShadowMapManager::prepareSpotShadowMap(ShadowMap& shadowMap, FEngine& engin
s.shadows[shadowIndex].scissorNormalized = shaderParameters.scissorNormalized; s.shadows[shadowIndex].scissorNormalized = shaderParameters.scissorNormalized;
s.shadows[shadowIndex].normalBias = normalBias * wsTexelSizeAtOneMeter; s.shadows[shadowIndex].normalBias = normalBias * wsTexelSizeAtOneMeter;
s.shadows[shadowIndex].lightFromWorldZ = shaderParameters.lightFromWorldZ; s.shadows[shadowIndex].lightFromWorldZ = shaderParameters.lightFromWorldZ;
s.shadows[shadowIndex].texelSizeAtOneMeter = wsTexelSizeAtOneMeter;
s.shadows[shadowIndex].nearOverFarMinusNear = float(n / (f - n)); s.shadows[shadowIndex].nearOverFarMinusNear = float(n / (f - n));
s.shadows[shadowIndex].elvsm = options->vsm.elvsm; s.shadows[shadowIndex].elvsm = options->vsm.elvsm;
s.shadows[shadowIndex].bulbRadiusLs = s.shadows[shadowIndex].bulbRadiusLs =
mSoftShadowOptions.penumbraScale * options->shadowBulbRadius mSoftShadowOptions.penumbraScale * options->shadowBulbRadius
/ length(wsTexelSizeAtOneMeter); / wsTexelSizeAtOneMeter;
} }
} }
@@ -924,7 +926,7 @@ void ShadowMapManager::preparePointShadowMap(ShadowMap& shadowMap,
// and if we need to generate it, update all the UBO data // and if we need to generate it, update all the UBO data
if (shadowMap.hasVisibleShadows()) { if (shadowMap.hasVisibleShadows()) {
const size_t shadowIndex = shadowMap.getShadowIndex(); const size_t shadowIndex = shadowMap.getShadowIndex();
const float2 wsTexelSizeAtOneMeter = shaderParameters.texelSizeAtOneMeterWs; const float wsTexelSizeAtOneMeter = shaderParameters.texelSizeAtOneMeterWs;
// note: normalBias is set to zero for VSM // note: normalBias is set to zero for VSM
const float normalBias = shadowMapInfo.vsm ? 0.0f : options->normalBias; const float normalBias = shadowMapInfo.vsm ? 0.0f : options->normalBias;
@@ -936,11 +938,12 @@ void ShadowMapManager::preparePointShadowMap(ShadowMap& shadowMap,
s.shadows[shadowIndex].scissorNormalized = shaderParameters.scissorNormalized; s.shadows[shadowIndex].scissorNormalized = shaderParameters.scissorNormalized;
s.shadows[shadowIndex].normalBias = normalBias * wsTexelSizeAtOneMeter; s.shadows[shadowIndex].normalBias = normalBias * wsTexelSizeAtOneMeter;
s.shadows[shadowIndex].lightFromWorldZ = shaderParameters.lightFromWorldZ; s.shadows[shadowIndex].lightFromWorldZ = shaderParameters.lightFromWorldZ;
s.shadows[shadowIndex].texelSizeAtOneMeter = wsTexelSizeAtOneMeter;
s.shadows[shadowIndex].nearOverFarMinusNear = float(n / (f - n)); s.shadows[shadowIndex].nearOverFarMinusNear = float(n / (f - n));
s.shadows[shadowIndex].elvsm = options->vsm.elvsm; s.shadows[shadowIndex].elvsm = options->vsm.elvsm;
s.shadows[shadowIndex].bulbRadiusLs = s.shadows[shadowIndex].bulbRadiusLs =
mSoftShadowOptions.penumbraScale * options->shadowBulbRadius mSoftShadowOptions.penumbraScale * options->shadowBulbRadius
/ length(wsTexelSizeAtOneMeter); / wsTexelSizeAtOneMeter;
} }
} }
+6 -7
View File
@@ -798,12 +798,6 @@ void FEngine::submitFrame() {
void FEngine::flush() { void FEngine::flush() {
// flush the command buffer // flush the command buffer
flushCommandBuffer(mCommandBufferQueue); 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() { void FEngine::flushAndWait() {
@@ -926,7 +920,12 @@ int FEngine::loop() {
void FEngine::flushCommandBuffer(CommandBufferQueue& commandBufferQueue) const { void FEngine::flushCommandBuffer(CommandBufferQueue& commandBufferQueue) const {
getDriver().purge(); getDriver().purge();
commandBufferQueue.flush(); commandBufferQueue.flush([this](void* begin, void* end) {
UTILS_UNUSED FEngine* engine = const_cast<FEngine*>(this);
#if FILAMENT_DEBUG_COMMANDS_HISTOGRAM
engine->getDriverApi().debugPrintHistogram(begin, end);
#endif
});
} }
const FMaterial* FEngine::getSkyboxMaterial() const noexcept { const FMaterial* FEngine::getSkyboxMaterial() const noexcept {
+2 -2
View File
@@ -257,9 +257,9 @@ filament::DescriptorSetLayout const& FMaterial::getPerViewDescriptorSetLayout(
} }
// mDefinition.perViewDescriptorSetLayout{Vsm} is already resolved for MaterialDomain // mDefinition.perViewDescriptorSetLayout{Vsm} is already resolved for MaterialDomain
if (useVsmDescriptorSetLayout) { if (useVsmDescriptorSetLayout) {
return mDefinition.perViewDescriptorSetLayoutS2d; return mDefinition.perViewDescriptorSetLayoutVsm;
} }
return mDefinition.perViewDescriptorSetLayoutPcf; return mDefinition.perViewDescriptorSetLayout;
} }
void FMaterial::compile(CompilerPriorityQueue const priority, void FMaterial::compile(CompilerPriorityQueue const priority,
+3 -3
View File
@@ -139,7 +139,7 @@ public:
// This is mostly intended to be used for post-process materials; but it's also useful for // This is mostly intended to be used for post-process materials; but it's also useful for
// Surface material that behave like post-process material (i.e. that don't really have variants or for // Surface material that behave like post-process material (i.e. that don't really have variants or for
// which VSM is nonsensical, like for unlit materials) // which VSM is nonsensical, like for unlit materials)
return mDefinition.perViewDescriptorSetLayoutPcf; return mDefinition.perViewDescriptorSetLayout;
} }
DescriptorSetLayout const& getPerViewDescriptorSetLayout( DescriptorSetLayout const& getPerViewDescriptorSetLayout(
@@ -378,11 +378,11 @@ private:
Variant variant = {}) const noexcept; Variant variant = {}) const noexcept;
bool isSharedVariant(Variant const variant) const { bool isSharedVariant(Variant const variant) const {
// HACK: The default material "should" have MNT | DEP, but then we'd have to compile it as a // 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 // 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 // with this variant, but with the shader program cache in active development, the days of
// the default material are numbered anyway. // the default material are numbered anyway.
constexpr Variant::type_t vsmAndDep = Variant::MNT | Variant::DEP; constexpr Variant::type_t vsmAndDep = Variant::VSM | Variant::DEP;
return mDefinition.materialDomain == MaterialDomain::SURFACE && !mIsDefaultMaterial && return mDefinition.materialDomain == MaterialDomain::SURFACE && !mIsDefaultMaterial &&
!mDefinition.hasCustomDepthShader && Variant::isValidDepthVariant(variant) && !mDefinition.hasCustomDepthShader && Variant::isValidDepthVariant(variant) &&
(variant.key & vsmAndDep) != vsmAndDep; (variant.key & vsmAndDep) != vsmAndDep;
+14 -27
View File
@@ -104,8 +104,6 @@ FRenderer::FRenderer(FEngine& engine) :
mIsFrameBufferFetchSupported(false), mIsFrameBufferFetchSupported(false),
mIsFrameBufferFetchMultiSampleSupported(false), mIsFrameBufferFetchMultiSampleSupported(false),
mIsAutoDepthResolveSupported(false), mIsAutoDepthResolveSupported(false),
mWorkaroundBlitSsrHistory(
engine.getDriverApi().isWorkaroundNeeded(Workaround::BLIT_SSR_HISTORY)),
mUserEpoch(engine.getEngineEpoch()), mUserEpoch(engine.getEngineEpoch()),
mResourceAllocator(std::make_unique<TextureCache>( mResourceAllocator(std::make_unique<TextureCache>(
engine.getSharedResourceAllocatorDisposer(), engine.getSharedResourceAllocatorDisposer(),
@@ -971,7 +969,10 @@ void FRenderer::renderJob(DriverApi& driver, RootArenaScope& rootArenaScope, FVi
variant.setDirectionalLighting(view.hasDirectionalLighting()); variant.setDirectionalLighting(view.hasDirectionalLighting());
variant.setDynamicLighting(view.hasDynamicLighting()); variant.setDynamicLighting(view.hasDynamicLighting());
variant.setFog(view.hasFog()); variant.setFog(view.hasFog());
variant.setShadowSampler2D(view.hasShadowing() && view.getShadowType() != ShadowType::PCF); // The VSM bit has a different meaning for STANDARD_VARIANT (as opposed to DEPTH_VARIANT),
// In the STANDARD_VARIANT case, we are *using* the shadow-map, and the VSM only decides which
// type of sampler is used (samplerShadow or sampler2D).
variant.setVsm(view.hasShadowing() && view.getShadowType() != ShadowType::PCF);
variant.setStereo(view.hasStereo()); variant.setStereo(view.hasStereo());
/* /*
@@ -980,7 +981,10 @@ void FRenderer::renderJob(DriverApi& driver, RootArenaScope& rootArenaScope, FVi
if (view.needsShadowMap()) { if (view.needsShadowMap()) {
Variant shadowVariant(Variant::DEPTH_VARIANT); Variant shadowVariant(Variant::DEPTH_VARIANT);
shadowVariant.setDepthMoments(view.getShadowType() == ShadowType::VSM); // The VSM bit has a different meaning for DEPTH_VARIANT (as opposed to STANDARD_VARIANT),
// In the DEPTH_VARIANT case, we are *generating* the shadow-map, and some computations
// are handled differently. In addition, the color buffer is used.
shadowVariant.setVsm(view.getShadowType() == ShadowType::VSM);
auto shadows = view.renderShadowMaps(engine, fg, cameraInfo, mShaderUserTime, auto shadows = view.renderShadowMaps(engine, fg, cameraInfo, mShaderUserTime,
RenderPassBuilder{ commandArena } RenderPassBuilder{ commandArena }
@@ -1339,38 +1343,21 @@ void FRenderer::renderJob(DriverApi& driver, RootArenaScope& rootArenaScope, FVi
struct ExportSSRHistoryData { struct ExportSSRHistoryData {
FrameGraphId<FrameGraphTexture> history; FrameGraphId<FrameGraphTexture> history;
}; };
// FIXME: should we use the TAA-modified cameraInfo here or not? (we are).
mat4 const projection = cameraInfo.projection * cameraInfo.getUserViewMatrix(); mat4 const projection = cameraInfo.projection * cameraInfo.getUserViewMatrix();
fg.addPass<ExportSSRHistoryData>("Export SSR history",
// we can't use colorPassOutput here because it could be tonemapped
FrameGraphId<FrameGraphTexture> history = colorPassOutput.linearColor;
if (mWorkaroundBlitSsrHistory) {
history = ppm.blit(fg, false, history,
{ 0, 0, colorBufferDesc.width, colorBufferDesc.height },
{
.width = colorBufferDesc.width,
.height = colorBufferDesc.height,
.format = config.hdrFormat,
},
SamplerMagFilter::LINEAR, SamplerMinFilter::LINEAR);
}
fg.addPass<ExportSSRHistoryData>(
mWorkaroundBlitSsrHistory ? "Export SSR history (Blit Workaround)"
: "Export SSR history",
[&](FrameGraph::Builder& builder, auto& data) { [&](FrameGraph::Builder& builder, auto& data) {
// We need to use sideEffect here to ensure this pass won't be culled. // We need to use sideEffect here to ensure this pass won't be culled.
// The "output" of this pass is going to be used during the next frame as // The "output" of this pass is going to be used during the next frame as
// an "import". // an "import".
builder.sideEffect(); builder.sideEffect();
data.history = builder.sample(history);
}, // we can't use colorPassOutput here because it could be tonemapped
[&view, projection](FrameGraphResources const& resources, auto const& data) { data.history = builder.sample(colorPassOutput.linearColor); // FIXME: an access must be declared for detach(), why?
}, [&view, projection](FrameGraphResources const& resources, auto const& data) {
auto& history = view.getFrameHistory(); auto& history = view.getFrameHistory();
auto& current = history.getCurrent(); auto& current = history.getCurrent();
current.ssr.projection = projection; current.ssr.projection = projection;
// FIXME: an access must be declared for detach(), why?
resources.detach(data.history, &current.ssr.color, &current.ssr.desc); resources.detach(data.history, &current.ssr.color, &current.ssr.desc);
}); });
} }
-1
View File
@@ -219,7 +219,6 @@ private:
bool mIsFrameBufferFetchSupported : 1; bool mIsFrameBufferFetchSupported : 1;
bool mIsFrameBufferFetchMultiSampleSupported : 1; bool mIsFrameBufferFetchMultiSampleSupported : 1;
bool mIsAutoDepthResolveSupported : 1; bool mIsAutoDepthResolveSupported : 1;
bool const mWorkaroundBlitSsrHistory : 1;
Epoch mUserEpoch; Epoch mUserEpoch;
math::float4 mShaderUserTime{}; math::float4 mShaderUserTime{};
DisplayInfo mDisplayInfo; DisplayInfo mDisplayInfo;
+1 -1
View File
@@ -143,7 +143,7 @@ FView::FView(FEngine& engine)
#ifndef NDEBUG #ifndef NDEBUG
// This can fail if another view has already registered this data source // This can fail if another view has already registered this data source
mDebugState->owner = debugRegistry.registerDataSource("d.view.frame_info", mDebugState->owner = debugRegistry.registerDataSource("d.view.frame_info",
[weak = std::weak_ptr<DebugState>(mDebugState)]() -> DebugRegistry::DataSource { [weak = std::weak_ptr(mDebugState)]() -> DebugRegistry::DataSource {
// the View could have been destroyed by the time we do this // the View could have been destroyed by the time we do this
auto const state = weak.lock(); auto const state = weak.lock();
if (!state) { if (!state) {
+2 -2
View File
@@ -1,12 +1,12 @@
Pod::Spec.new do |spec| Pod::Spec.new do |spec|
spec.name = "Filament" spec.name = "Filament"
spec.version = "1.69.4" spec.version = "1.69.3"
spec.license = { :type => "Apache 2.0", :file => "LICENSE" } spec.license = { :type => "Apache 2.0", :file => "LICENSE" }
spec.homepage = "https://google.github.io/filament" spec.homepage = "https://google.github.io/filament"
spec.authors = "Google LLC." 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.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.platform = :ios, "11.0"
spec.source = { :http => "https://github.com/google/filament/releases/download/v1.69.4/filament-v1.69.4-ios.tgz" } spec.source = { :http => "https://github.com/google/filament/releases/download/v1.69.3/filament-v1.69.3-ios.tgz" }
spec.libraries = 'c++' spec.libraries = 'c++'
@@ -36,7 +36,7 @@ backend::DescriptorSetLayout const& getPerRenderableLayout() noexcept;
backend::DescriptorSetLayout getPerViewDescriptorSetLayout( backend::DescriptorSetLayout getPerViewDescriptorSetLayout(
MaterialDomain domain, MaterialDomain domain,
bool isLit, bool isSSR, bool hasFog, bool isLit, bool isSSR, bool hasFog,
bool isShadowSampler2D) noexcept; bool isVSM) noexcept;
backend::DescriptorSetLayout getPerViewDescriptorSetLayoutWithVariant( backend::DescriptorSetLayout getPerViewDescriptorSetLayoutWithVariant(
Variant variant, Variant variant,
@@ -299,9 +299,10 @@ struct ShadowUib { // NOLINT(cppcoreguidelines-pro-type-member-init)
math::mat4f lightFromWorldMatrix; // 64 math::mat4f lightFromWorldMatrix; // 64
math::float4 lightFromWorldZ; // 16 math::float4 lightFromWorldZ; // 16
math::float4 scissorNormalized; // 16 math::float4 scissorNormalized; // 16
float texelSizeAtOneMeter; // 4
float bulbRadiusLs; // 4 float bulbRadiusLs; // 4
float nearOverFarMinusNear; // 4 float nearOverFarMinusNear; // 4
math::float2 normalBias; // 4 float normalBias; // 4
bool elvsm; // 4 bool elvsm; // 4
uint32_t layer; // 4 uint32_t layer; // 4
uint32_t reserved1; // 4 uint32_t reserved1; // 4
@@ -48,21 +48,20 @@ struct Variant {
// SRE: Shadow Receiver // SRE: Shadow Receiver
// SKN: Skinning // SKN: Skinning
// DEP: Depth only // DEP: Depth only
// FOG: Fog (standard) // FOG: Fog
// PCK: Picking (depth) // PCK: Picking (depth variant only)
// MNT: Output depth moments (depth) // VSM: Variance shadow maps (depth) / sampler type (standard)
// S2D: Sampler type for shadows (0: samplerShadowArray, 1: sampler2DArray) (standard)
// STE: Instanced stereo rendering // STE: Instanced stereo rendering
// //
// X: either 1 or 0 // X: either 1 or 0
// +-----+-----+-----+-----+-----+-----+-----+-----+ // +-----+-----+-----+-----+-----+-----+-----+-----+
// Variant | STE | S2D | FOG | DEP | SKN | SRE | DYN | DIR | 256 // Variant | STE | VSM | FOG | DEP | SKN | SRE | DYN | DIR | 256
// +-----+-----+-----+-----+-----+-----+-----+-----+ // +-----+-----+-----+-----+-----+-----+-----+-----+
// MNT PCK // PCK
// //
// Standard variants: // Standard variants:
// +-----+-----+-----+-----+-----+-----+-----+-----+ // +-----+-----+-----+-----+-----+-----+-----+-----+
// | STE | S2D | FOG | 0 | SKN | SRE | DYN | DIR | 128 - 44 = 84 // | STE | VSM | FOG | 0 | SKN | SRE | DYN | DIR | 128 - 44 = 84
// +-----+-----+-----+-----+-----+-----+-----+-----+ // +-----+-----+-----+-----+-----+-----+-----+-----+
// Vertex shader X 0 0 0 X X X X // Vertex shader X 0 0 0 X X X X
// Fragment shader 0 X X 0 0 X X X // Fragment shader 0 X X 0 0 X X X
@@ -73,7 +72,7 @@ struct Variant {
// //
// Depth variants: // Depth variants:
// +-----+-----+-----+-----+-----+-----+-----+-----+ // +-----+-----+-----+-----+-----+-----+-----+-----+
// | STE | MNT | PCK | 1 | SKN | 0 | 0 | 0 | 16 - 4 = 12 // | STE | VSM | PCK | 1 | SKN | 0 | 0 | 0 | 16 - 4 = 12
// +-----+-----+-----+-----+-----+-----+-----+-----+ // +-----+-----+-----+-----+-----+-----+-----+-----+
// Vertex depth X X 0 1 X 0 0 0 // Vertex depth X X 0 1 X 0 0 0
// Fragment depth 0 0 X 1 0 0 0 0 // Fragment depth 0 0 X 1 0 0 0 0
@@ -97,15 +96,13 @@ struct Variant {
static constexpr type_t DEP = 0x10; // depth only variants static constexpr type_t DEP = 0x10; // depth only variants
static constexpr type_t FOG = 0x20; // fog (standard) static constexpr type_t FOG = 0x20; // fog (standard)
static constexpr type_t PCK = 0x20; // picking (depth) static constexpr type_t PCK = 0x20; // picking (depth)
static constexpr type_t S2D = 0x40; // sampler type static constexpr type_t VSM = 0x40; // variance shadow maps / sampler type
static constexpr type_t MNT = 0x40; // variance shadow maps
static constexpr type_t STE = 0x80; // instanced stereo static constexpr type_t STE = 0x80; // instanced stereo
static constexpr type_t NO_VARIANT = 0u; static constexpr type_t NO_VARIANT = 0u;
// special variants (variants that use the reserved space) // special variants (variants that use the reserved space)
static constexpr type_t SPECIAL_SSR_VARIANT= S2D | SRE ; static constexpr type_t SPECIAL_SSR = VSM | SRE; // screen-space reflections variant
static constexpr type_t SPECIAL_SSR_MASK = STE | S2D | DEP | SRE | DYN | DIR;
static constexpr type_t STANDARD_MASK = DEP; static constexpr type_t STANDARD_MASK = DEP;
static constexpr type_t STANDARD_VARIANT = 0u; static constexpr type_t STANDARD_VARIANT = 0u;
@@ -130,30 +127,29 @@ struct Variant {
void setSkinning(bool v) noexcept { set(v, SKN); } void setSkinning(bool v) noexcept { set(v, SKN); }
void setFog(bool v) noexcept { set(v, FOG); } void setFog(bool v) noexcept { set(v, FOG); }
void setPicking(bool v) noexcept { set(v, PCK); } void setPicking(bool v) noexcept { set(v, PCK); }
void setShadowSampler2D(bool v) noexcept { set(v, S2D); } void setVsm(bool v) noexcept { set(v, VSM); }
void setDepthMoments(bool v) noexcept { set(v, MNT); }
void setStereo(bool v) noexcept { set(v, STE); } void setStereo(bool v) noexcept { set(v, STE); }
static constexpr bool isValidDepthVariant(Variant variant) noexcept { static constexpr bool isValidDepthVariant(Variant variant) noexcept {
// Can't have VSM and PICKING together with DEPTH variants // Can't have VSM and PICKING together with DEPTH variants
constexpr type_t RESERVED_MASK = MNT | PCK | DEP | SRE | DYN | DIR; constexpr type_t RESERVED_MASK = VSM | PCK | DEP | SRE | DYN | DIR;
constexpr type_t RESERVED_VALUE = MNT | PCK | DEP; constexpr type_t RESERVED_VALUE = VSM | PCK | DEP;
return ((variant.key & DEPTH_MASK) == DEPTH_VARIANT) && return ((variant.key & DEPTH_MASK) == DEPTH_VARIANT) &&
((variant.key & RESERVED_MASK) != RESERVED_VALUE); ((variant.key & RESERVED_MASK) != RESERVED_VALUE);
} }
static constexpr bool isValidStandardVariant(Variant variant) noexcept { static constexpr bool isValidStandardVariant(Variant variant) noexcept {
// can't have shadow receiver if we don't have any lighting // can't have shadow receiver if we don't have any lighting
constexpr type_t RESERVED0_MASK = S2D | FOG | SRE | DYN | DIR; constexpr type_t RESERVED0_MASK = VSM | FOG | SRE | DYN | DIR;
constexpr type_t RESERVED0_VALUE = S2D | FOG | SRE; constexpr type_t RESERVED0_VALUE = VSM | FOG | SRE;
// can't have shadow receiver if we don't have any lighting // can't have shadow receiver if we don't have any lighting
constexpr type_t RESERVED1_MASK = S2D | SRE | DYN | DIR; constexpr type_t RESERVED1_MASK = VSM | SRE | DYN | DIR;
constexpr type_t RESERVED1_VALUE = SRE; constexpr type_t RESERVED1_VALUE = SRE;
// can't have VSM without shadow receiver // can't have VSM without shadow receiver
constexpr type_t RESERVED2_MASK = S2D | SRE; constexpr type_t RESERVED2_MASK = VSM | SRE;
constexpr type_t RESERVED2_VALUE = S2D; constexpr type_t RESERVED2_VALUE = VSM;
return ((variant.key & STANDARD_MASK) == STANDARD_VARIANT) && return ((variant.key & STANDARD_MASK) == STANDARD_VARIANT) &&
((variant.key & RESERVED0_MASK) != RESERVED0_VALUE) && ((variant.key & RESERVED0_MASK) != RESERVED0_VALUE) &&
@@ -178,15 +174,11 @@ struct Variant {
} }
static constexpr bool isSSRVariant(Variant variant) noexcept { static constexpr bool isSSRVariant(Variant variant) noexcept {
return (variant.key & SPECIAL_SSR_MASK) == SPECIAL_SSR_VARIANT; return (variant.key & (STE | VSM | DEP | SRE | DYN | DIR)) == (VSM | SRE);
} }
static constexpr bool isShadowSampler2DVariant(Variant variant) noexcept { static constexpr bool isVSMVariant(Variant variant) noexcept {
return !isSSRVariant(variant) && ((variant.key & (S2D | DEP)) == S2D); return !isSSRVariant(variant) && ((variant.key & VSM) == VSM);
}
static constexpr bool isDepthMomentsVariant(Variant variant) noexcept {
return !isSSRVariant(variant) && ((variant.key & (MNT | DEP)) == (MNT | DEP));
} }
static constexpr bool isShadowReceiverVariant(Variant variant) noexcept { static constexpr bool isShadowReceiverVariant(Variant variant) noexcept {
@@ -210,13 +202,13 @@ struct Variant {
// vertex shader. // vertex shader.
if ((variant.key & STANDARD_MASK) == STANDARD_VARIANT) { if ((variant.key & STANDARD_MASK) == STANDARD_VARIANT) {
if (isSSRVariant(variant)) { if (isSSRVariant(variant)) {
variant.key &= ~SPECIAL_SSR_VARIANT; variant.key &= ~(VSM | SRE);
} }
return variant & (STE | SKN | SRE | DYN | DIR); return variant & (STE | SKN | SRE | DYN | DIR);
} }
if ((variant.key & DEPTH_MASK) == DEPTH_VARIANT) { if ((variant.key & DEPTH_MASK) == DEPTH_VARIANT) {
// Only MNT, skinning, and stereo affect the vertex shader's DEPTH variant // Only VSM, skinning, and stereo affect the vertex shader's DEPTH variant
return variant & (STE | MNT | SKN | DEP); return variant & (STE | VSM | SKN | DEP);
} }
return {}; return {};
} }
@@ -225,11 +217,11 @@ struct Variant {
// filter out fragment variants that are not needed. For e.g. skinning doesn't // filter out fragment variants that are not needed. For e.g. skinning doesn't
// affect the fragment shader. // affect the fragment shader.
if ((variant.key & STANDARD_MASK) == STANDARD_VARIANT) { if ((variant.key & STANDARD_MASK) == STANDARD_VARIANT) {
return variant & (S2D | FOG | SRE | DYN | DIR); return variant & (VSM | FOG | SRE | DYN | DIR);
} }
if ((variant.key & DEPTH_MASK) == DEPTH_VARIANT) { if ((variant.key & DEPTH_MASK) == DEPTH_VARIANT) {
// Only VSM & PICKING affects the fragment shader's DEPTH variant // Only VSM & PICKING affects the fragment shader's DEPTH variant
return variant & (MNT | PCK | DEP); return variant & (VSM | PCK | DEP);
} }
return {}; return {};
} }
@@ -238,8 +230,8 @@ struct Variant {
// special case for depth variant // special case for depth variant
if (isValidDepthVariant(variant)) { if (isValidDepthVariant(variant)) {
if (!isLit) { if (!isLit) {
// if we're unlit, we never need the MNT variant // if we're unlit, we never need the VSM variant
return variant & ~MNT; return variant & ~VSM;
} }
return variant; return variant;
} }
@@ -250,9 +242,9 @@ struct Variant {
// when the shading mode is unlit, remove all the lighting variants // when the shading mode is unlit, remove all the lighting variants
return variant & UNLIT_MASK; return variant & UNLIT_MASK;
} }
// if shadow receiver is disabled, we pick the shadow sampler // if shadow receiver is disabled, turn off VSM
if (!(variant.key & SRE)) { if (!(variant.key & SRE)) {
return variant & ~S2D; return variant & ~VSM;
} }
return variant; return variant;
} }
+4 -3
View File
@@ -216,7 +216,7 @@ utils::CString getDescriptorName(DescriptorSetBindingPoints const set,
DescriptorSetLayout getPerViewDescriptorSetLayout( DescriptorSetLayout getPerViewDescriptorSetLayout(
MaterialDomain const domain, MaterialDomain const domain,
bool const isLit, bool const isSSR, bool const hasFog, bool const isLit, bool const isSSR, bool const hasFog,
bool const isShadowSampler2D) noexcept { bool const isVSM) noexcept {
switch (domain) { switch (domain) {
case MaterialDomain::SURFACE: { case MaterialDomain::SURFACE: {
@@ -255,7 +255,7 @@ DescriptorSetLayout getPerViewDescriptorSetLayout(
} }
// change the SHADOW_MAP descriptor type for VSM // change the SHADOW_MAP descriptor type for VSM
if (isShadowSampler2D) { if (isVSM) {
auto const pos = std::find_if(layout.descriptors.begin(), layout.descriptors.end(), auto const pos = std::find_if(layout.descriptors.begin(), layout.descriptors.end(),
[](auto const& v) { [](auto const& v) {
return v.binding == PerViewBindingPoints::SHADOW_MAP; return v.binding == PerViewBindingPoints::SHADOW_MAP;
@@ -285,7 +285,8 @@ DescriptorSetLayout getPerViewDescriptorSetLayoutWithVariant(
return ssrVariantDescriptorSetLayout; return ssrVariantDescriptorSetLayout;
} }
// We need to filter out all the descriptors not included in the "resolved" layout below // We need to filter out all the descriptors not included in the "resolved" layout below
return getPerViewDescriptorSetLayout(domain, isLit, isSSR, hasFog, Variant::isShadowSampler2DVariant(variant)); return getPerViewDescriptorSetLayout(domain, isLit, isSSR, hasFog,
Variant::isVSMVariant(variant));
} }
DescriptorType getDescriptorType(SamplerType const type, SamplerFormat const format) { DescriptorType getDescriptorType(SamplerType const type, SamplerFormat const format) {
+16 -21
View File
@@ -32,46 +32,41 @@ namespace filament {
Variant Variant::filterUserVariant( Variant Variant::filterUserVariant(
Variant variant, UserVariantFilterMask filterMask) noexcept { Variant variant, UserVariantFilterMask filterMask) noexcept {
// these are easy to filter by just removing the corresponding bit // these are easy to filter by just removing the corresponding bit
if (filterMask & uint32_t(UserVariantFilterBit::DIRECTIONAL_LIGHTING)) { if (filterMask & (uint32_t)UserVariantFilterBit::DIRECTIONAL_LIGHTING) {
variant.key &= ~DIR; variant.key &= ~DIR;
} }
if (filterMask & (uint32_t)UserVariantFilterBit::DYNAMIC_LIGHTING) {
if (filterMask & uint32_t(UserVariantFilterBit::DYNAMIC_LIGHTING)) {
variant.key &= ~DYN; variant.key &= ~DYN;
} }
if (filterMask & (uint32_t)UserVariantFilterBit::SKINNING) {
if (filterMask & uint32_t(UserVariantFilterBit::SKINNING)) {
variant.key &= ~SKN; variant.key &= ~SKN;
} }
if (filterMask & (uint32_t)UserVariantFilterBit::STE) {
if (filterMask & uint32_t(UserVariantFilterBit::STE)) {
variant.key &= ~(filterMask & STE); variant.key &= ~(filterMask & STE);
} }
if (!isValidDepthVariant(variant)) {
if (isValidDepthVariant(variant)) {
// depth variants can have their MNT bit filtered
if (filterMask & uint32_t(UserVariantFilterBit::VSM)) {
variant.key &= ~MNT;
}
} else {
// we can't remove FOG from depth variants, this would, in fact, remove picking // we can't remove FOG from depth variants, this would, in fact, remove picking
if (filterMask & uint32_t(UserVariantFilterBit::FOG)) { if (filterMask & (uint32_t)UserVariantFilterBit::FOG) {
variant.key &= ~FOG; variant.key &= ~FOG;
} }
} else {
// depth variants can have their VSM bit filtered
if (filterMask & (uint32_t)UserVariantFilterBit::VSM) {
variant.key &= ~VSM;
}
} }
if (!isSSRVariant(variant)) { if (!isSSRVariant(variant)) {
// SSR variant needs to be handled separately // SSR variant needs to be handled separately
if (filterMask & uint32_t(UserVariantFilterBit::SHADOW_RECEIVER)) { if (filterMask & (uint32_t)UserVariantFilterBit::SHADOW_RECEIVER) {
variant.key &= ~SRE; variant.key &= ~SRE;
} }
if (filterMask & uint32_t(UserVariantFilterBit::VSM)) { if (filterMask & (uint32_t)UserVariantFilterBit::VSM) {
variant.key &= ~S2D; variant.key &= ~VSM;
} }
} else { } else {
// see if we need to filter out the SSR variants // see if we need to filter out the SSR variants
if (filterMask & uint32_t(UserVariantFilterBit::SSR)) { if (filterMask & (uint32_t)UserVariantFilterBit::SSR) {
variant.key &= ~SPECIAL_SSR_VARIANT; variant.key &= ~SPECIAL_SSR;
} }
} }
return variant; return variant;
+1 -2
View File
@@ -60,8 +60,7 @@ void ShaderGenerator::generateSurfaceMaterialVariantDefines(io::sstream& out,
CodeGenerator::generateDefine(out, "VARIANT_HAS_SHADOWING", CodeGenerator::generateDefine(out, "VARIANT_HAS_SHADOWING",
litVariants && filament::Variant::isShadowReceiverVariant(variant)); litVariants && filament::Variant::isShadowReceiverVariant(variant));
CodeGenerator::generateDefine(out, "VARIANT_HAS_VSM", CodeGenerator::generateDefine(out, "VARIANT_HAS_VSM",
filament::Variant::isShadowSampler2DVariant(variant) || filament::Variant::isVSMVariant(variant));
filament::Variant::isDepthMomentsVariant(variant));
CodeGenerator::generateDefine(out, "VARIANT_HAS_STEREO", CodeGenerator::generateDefine(out, "VARIANT_HAS_STEREO",
hasStereo(variant, featureLevel)); hasStereo(variant, featureLevel));
CodeGenerator::generateDefine(out, "VARIANT_DEPTH", CodeGenerator::generateDefine(out, "VARIANT_DEPTH",
+5 -5
View File
@@ -49,7 +49,7 @@ SamplerInterfaceBlock const& SibGenerator::getPerViewSib(Variant variant) noexce
// reason we name them "unused*" to ensure we're not using them by mistake (type/format don't // reason we name them "unused*" to ensure we're not using them by mistake (type/format don't
// matter). // matter).
static SamplerInterfaceBlock const sibShadowSamplerPcf{ SamplerInterfaceBlock::Builder() static SamplerInterfaceBlock const sibPcf{ SamplerInterfaceBlock::Builder()
.name("sampler0") .name("sampler0")
.stageFlags(backend::ShaderStageFlags::FRAGMENT) .stageFlags(backend::ShaderStageFlags::FRAGMENT)
.add( {{ "shadowMap", +PerViewBindingPoints::SHADOW_MAP, Type::SAMPLER_2D_ARRAY, Format::SHADOW, Precision::MEDIUM, FILTERABLE, !MULTISAMPLE, ALL_STAGES }, .add( {{ "shadowMap", +PerViewBindingPoints::SHADOW_MAP, Type::SAMPLER_2D_ARRAY, Format::SHADOW, Precision::MEDIUM, FILTERABLE, !MULTISAMPLE, ALL_STAGES },
@@ -62,7 +62,7 @@ SamplerInterfaceBlock const& SibGenerator::getPerViewSib(Variant variant) noexce
) )
.build() }; .build() };
static SamplerInterfaceBlock const sibShadowSampler2D{ SamplerInterfaceBlock::Builder() static SamplerInterfaceBlock const sibVsm{ SamplerInterfaceBlock::Builder()
.name("sampler0") .name("sampler0")
.stageFlags(backend::ShaderStageFlags::FRAGMENT) .stageFlags(backend::ShaderStageFlags::FRAGMENT)
.add( {{ "shadowMap", +PerViewBindingPoints::SHADOW_MAP, Type::SAMPLER_2D_ARRAY, Format::FLOAT, Precision::HIGH, FILTERABLE, !MULTISAMPLE, ALL_STAGES }, .add( {{ "shadowMap", +PerViewBindingPoints::SHADOW_MAP, Type::SAMPLER_2D_ARRAY, Format::FLOAT, Precision::HIGH, FILTERABLE, !MULTISAMPLE, ALL_STAGES },
@@ -85,10 +85,10 @@ SamplerInterfaceBlock const& SibGenerator::getPerViewSib(Variant variant) noexce
if (Variant::isSSRVariant(variant)) { if (Variant::isSSRVariant(variant)) {
return sibSsr; return sibSsr;
} else if (Variant::isShadowSampler2DVariant(variant)) { } else if (Variant::isVSMVariant(variant)) {
return sibShadowSampler2D; return sibVsm;
} else { } else {
return sibShadowSamplerPcf; return sibPcf;
} }
} }
-2
View File
@@ -60,13 +60,11 @@
#define GLTFIO_WARN(msg) slog.w << msg << io::endl #define GLTFIO_WARN(msg) slog.w << msg << io::endl
#endif #endif
#ifndef GLTFIO_USE_FILESYSTEM
#if defined(__EMSCRIPTEN__) || defined(__ANDROID__) || defined(FILAMENT_IOS) #if defined(__EMSCRIPTEN__) || defined(__ANDROID__) || defined(FILAMENT_IOS)
#define GLTFIO_USE_FILESYSTEM 0 #define GLTFIO_USE_FILESYSTEM 0
#else #else
#define GLTFIO_USE_FILESYSTEM 1 #define GLTFIO_USE_FILESYSTEM 1
#endif #endif
#endif
namespace utils { namespace utils {
class NameComponentManager; class NameComponentManager;
+1 -2
View File
@@ -48,11 +48,10 @@ std::string formatVariantString(Variant variant, MaterialDomain domain) noexcept
if (variant.key & Variant::DEP) variantString += "DEP|"; if (variant.key & Variant::DEP) variantString += "DEP|";
if (variant.key & Variant::DEP) { if (variant.key & Variant::DEP) {
if (variant.key & Variant::PCK) variantString += "PCK|"; if (variant.key & Variant::PCK) variantString += "PCK|";
if (variant.key & Variant::MNT) variantString += "MNT|";
} else { } else {
if (variant.key & Variant::FOG) variantString += "FOG|"; if (variant.key & Variant::FOG) variantString += "FOG|";
if (variant.key & Variant::S2D) variantString += "S2D|";
} }
if (variant.key & Variant::VSM) variantString += "VSM|";
if (variant.key & Variant::STE) variantString += "STE|"; if (variant.key & Variant::STE) variantString += "STE|";
variantString = variantString.substr(0, variantString.length() - 1); variantString = variantString.substr(0, variantString.length() - 1);
} }
+1 -6
View File
@@ -17,7 +17,6 @@
// TODO: Clean-up. We shouldn't need this #ifndef here, but a client has requested that perfetto be // TODO: Clean-up. We shouldn't need this #ifndef here, but a client has requested that perfetto be
// disabled due to size increase. In their case, this flag would be defined across targets. Hence // disabled due to size increase. In their case, this flag would be defined across targets. Hence
// we guard below with an #ifndef. // we guard below with an #ifndef.
#include <cstring>
#ifndef FILAMENT_TRACING_ENABLED #ifndef FILAMENT_TRACING_ENABLED
// Note: The overhead of TRACING is not negligible especially with parallel_for(). // Note: The overhead of TRACING is not negligible especially with parallel_for().
#define FILAMENT_TRACING_ENABLED false #define FILAMENT_TRACING_ENABLED false
@@ -99,11 +98,7 @@ namespace utils {
void JobSystem::setThreadName(const char* name) noexcept { void JobSystem::setThreadName(const char* name) noexcept {
#if defined(__linux__) #if defined(__linux__)
constexpr size_t MAX_PTHREAD_NAME_LEN = 16; pthread_setname_np(pthread_self(), name);
char buf[MAX_PTHREAD_NAME_LEN];
strncpy(buf, name, MAX_PTHREAD_NAME_LEN - 1);
buf[MAX_PTHREAD_NAME_LEN - 1] = '\0';
pthread_setname_np(pthread_self(), buf);
#elif defined(__APPLE__) #elif defined(__APPLE__)
pthread_setname_np(name); pthread_setname_np(name);
#elif defined(WIN32) #elif defined(WIN32)
+1 -1
View File
@@ -111,7 +111,7 @@ highp vec4 getSpotLightSpacePosition(int index, highp vec3 dir, highp float zLig
highp mat4 lightFromWorldMatrix = shadowUniforms.shadows[index].lightFromWorldMatrix; highp mat4 lightFromWorldMatrix = shadowUniforms.shadows[index].lightFromWorldMatrix;
// for spotlights, the bias depends on z // for spotlights, the bias depends on z
highp vec2 bias = shadowUniforms.shadows[index].normalBias * zLight; float bias = shadowUniforms.shadows[index].normalBias * zLight;
return computeLightSpacePosition(getWorldPosition(), getWorldGeometricNormalVector(), return computeLightSpacePosition(getWorldPosition(), getWorldGeometricNormalVector(),
dir, bias, lightFromWorldMatrix); dir, bias, lightFromWorldMatrix);
+4 -43
View File
@@ -12,51 +12,12 @@
*/ */
highp vec4 computeLightSpacePosition(highp vec3 p, const highp vec3 n, highp vec4 computeLightSpacePosition(highp vec3 p, const highp vec3 n,
const highp vec3 dir, const highp vec2 b, highp_mat4 lightFromWorldMatrix) { const highp vec3 dir, const float b, highp_mat4 lightFromWorldMatrix) {
#if !defined(VARIANT_HAS_VSM) #if !defined(VARIANT_HAS_VSM)
// -------------------------------------------------------------------------------------- highp float cosTheta = saturate(dot(n, dir));
// Anisotropic Normal Bias for Shadow Mapping highp float sinTheta = sqrt(1.0 - cosTheta * cosTheta);
// -------------------------------------------------------------------------------------- p += n * (sinTheta * b);
// To prevent shadow acne, we must push the geometry along its normal to clear the
// quantization steps of the shadow map's discrete depth grid. The exact physical depth
// error we must clear is proportional to the shadow texel's world-space dimensions.
//
// This implementation computes the exact geometric projection of the rectangular
// shadow map texel onto the surface normal.
//
// 1. Coordinate Space Transition:
// We project the world-space normal onto the light's X and Y basis vectors (L_right,
// L_up). This gives us the lateral components of the normal in Light Space (n_Lx, n_Ly).
//
// 2. The Implicit sin(theta) Slope Scale:
// Because the normal is a unit vector, the magnitude of its lateral components in
// light space inherently equals sin(theta), where theta is the angle of incidence.
// This perfectly and automatically scales the bias from 0.0 (top-down, flat surface)
// to maximum (grazing angle).
//
// 3. Exact Anisotropic Footprint (The L1 Norm):
// Shadow texels are rarely perfectly square due to Cascaded Shadow Maps (CSM) or
// Light Space Perspective Shadow Maps (LiSPSM). Jx and Jy are the physical world-space
// dimensions of the texel.
// By evaluating `abs(n_Lx * Jx) + abs(n_Ly * Jy)`, we compute the exact scalar
// projection of the rectangular texel footprint.
// - It is superior to `max(Jx, Jy)` which assumes a massive square and causes Peter Panning.
// - It is superior to `length()` which assumes an ellipse and under-biases the corners.
// --------------------------------------------------------------------------------------
// Extract the first row (Light's Right vector in World Space)
highp vec3 L_right = vec3(lightFromWorldMatrix[0][0], lightFromWorldMatrix[1][0], lightFromWorldMatrix[2][0]);
// Extract the second row (Light's Up vector in World Space)
highp vec3 L_up = vec3(lightFromWorldMatrix[0][1], lightFromWorldMatrix[1][1], lightFromWorldMatrix[2][1]);
// Project the world normal onto the shadow map's 2D grid
highp float n_Lx = dot(n, L_right);
highp float n_Ly = dot(n, L_up);
// Apply the anisotropic normal bias
p += n * (abs(n_Lx * b.x) + abs(n_Ly * b.y));
#endif #endif
return mulMat4x4Float3(lightFromWorldMatrix, p); return mulMat4x4Float3(lightFromWorldMatrix, p);
+2 -1
View File
@@ -5,9 +5,10 @@ struct ShadowData {
highp mat4 lightFromWorldMatrix; highp mat4 lightFromWorldMatrix;
highp vec4 lightFromWorldZ; highp vec4 lightFromWorldZ;
highp vec4 scissorNormalized; highp vec4 scissorNormalized;
mediump float texelSizeAtOneMeter;
mediump float bulbRadiusLs; mediump float bulbRadiusLs;
mediump float nearOverFarMinusNear; mediump float nearOverFarMinusNear;
highp vec2 normalBias; mediump float normalBias;
bool elvsm; bool elvsm;
mediump uint layer; mediump uint layer;
mediump uint reserved1; mediump uint reserved1;
+1 -20
View File
@@ -76,11 +76,10 @@ into **branch** of `filament-assets`. This branch is paired with a PR or commit
As an example, imagine I am working on a PR, and I've uploaded my change, which is in a As an example, imagine I am working on a PR, and I've uploaded my change, which is in a
branch called `my-pr-branch`, to `filament`. This PR requires updating the golden. We would do branch called `my-pr-branch`, to `filament`. This PR requires updating the golden. We would do
it in the following fashion on a macOS machine: it in the following fashion
### Using a script to update the golden repo ### Using a script to update the golden repo
- Make sure you've completed the steps in 'Setting up python'
- Run interactive mode in the `update_golden.py` script. - Run interactive mode in the `update_golden.py` script.
``` ```
python3 test/renderdiff/src/update_golden.py python3 test/renderdiff/src/update_golden.py
@@ -115,24 +114,6 @@ Doing the above has multiple effects:
- If the PR is merged, then there is another workflow that will merge `my-pr-branch-golden` - 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. 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 ## Viewing test results
We provide a viewer for looking at the result of a test run. The viewer is a webapp that can We provide a viewer for looking at the result of a test run. The viewer is a webapp that can
+2 -12
View File
@@ -18,7 +18,6 @@ import re
from utils import execute, ArgParseImpl from utils import execute, ArgParseImpl
RDIFF_UPDATE_GOLDEN_STR = 'RDIFF_BRANCH' RDIFF_UPDATE_GOLDEN_STR = 'RDIFF_BRANCH'
RDIFF_ACCEPT_NEW_GOLDENS_STR = 'RDIFF_ACCEPT_NEW_GOLDENS'
def _parse_commit(commit_str): def _parse_commit(commit_str):
lines = commit_str.split('\n') lines = commit_str.split('\n')
@@ -43,11 +42,9 @@ def _parse_commit(commit_str):
if __name__ == "__main__": if __name__ == "__main__":
RE_STR = rf"{RDIFF_UPDATE_GOLDEN_STR}(?:\s)?\=(?:\s)?([a-zA-Z0-9\s\-\/]+)" 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 = ArgParseImpl()
parser.add_argument('--file', help='A file containing the commit message') 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:]) args, _ = parser.parse_known_args(sys.argv[1:])
if not args.file: if not args.file:
@@ -56,21 +53,14 @@ if __name__ == "__main__":
with open(args.file, 'r') as f: with open(args.file, 'r') as f:
msg = f.read() msg = f.read()
to_update = []
commit, title, description = _parse_commit(msg) 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: for line in description:
m = re.match(RE_STR, line) m = re.match(RE_STR, line)
if not m: if not m:
continue continue
print(m.group(1)) print(m.group(1))
sys.exit(0) exit(0)
# Always default to the main branch # Always default to the main branch
print('main') print('main')
+7 -9
View File
@@ -16,7 +16,6 @@ import os
import shutil import shutil
import re import re
import sys import sys
import tempfile
from utils import execute, ArgParseImpl, mkdir_p from utils import execute, ArgParseImpl, mkdir_p
@@ -100,11 +99,10 @@ class GoldenManager:
code, old_commit = execute(f'git log --format=%B -n 1', cwd=assets_dir) code, old_commit = execute(f'git log --format=%B -n 1', cwd=assets_dir)
if tag and len(tag) > 0: if tag and len(tag) > 0:
old_commit += f'\nFILAMENT={tag}' old_commit += f'\nFILAMENT={tag}'
with tempfile.NamedTemporaryFile('w', delete=False) as f: COMMIT_FILE = '/tmp/golden_commit.txt'
with open(COMMIT_FILE, 'w') as f:
f.write(old_commit) f.write(old_commit)
commit_file = f.name self._git_exec(f'commit --amend -F {COMMIT_FILE}')
self._git_exec(f'commit --amend -F {commit_file}')
os.remove(commit_file)
# Do the actual merge # Do the actual merge
self._git_exec(f'checkout main') self._git_exec(f'checkout main')
@@ -141,11 +139,11 @@ class GoldenManager:
os.path.join(rdiff_dir, f)) os.path.join(rdiff_dir, f))
self._git_exec(f'add {os.path.join(GOLDENS_DIR, f)}') self._git_exec(f'add {os.path.join(GOLDENS_DIR, f)}')
with tempfile.NamedTemporaryFile('w', delete=False) as f: TMP_GOLDEN_COMMIT_FILE = '/tmp/golden_commit.txt'
with open(TMP_GOLDEN_COMMIT_FILE, 'w') as f:
f.write(commit_msg) f.write(commit_msg)
tmp_golden_commit_file = f.name self._git_exec(f'commit -a -F {TMP_GOLDEN_COMMIT_FILE}')
self._git_exec(f'commit -a -F {tmp_golden_commit_file}')
os.remove(tmp_golden_commit_file)
if push_to_remote and \ if push_to_remote and \
(self.access_token_ or self.access_type_ == ACCESS_TYPE_SSH): (self.access_token_ or self.access_type_ == ACCESS_TYPE_SSH):
self._git_exec(f'push -f origin {branch}') self._git_exec(f'push -f origin {branch}')
+19 -19
View File
@@ -64,19 +64,19 @@ def _do_update(golden_manager, config):
def _same_image_diffimg(diffimg_path, img1, img2): def _same_image_diffimg(diffimg_path, img1, img2):
cmd = [diffimg_path, img1, img2] cmd = [diffimg_path, img1, img2]
result = subprocess.run(cmd, capture_output=True, text=True)
if result.returncode != 0:
raise RuntimeError(f"diffimg failed with return code {result.returncode}:\n{result.stderr}")
output = result.stdout.strip()
if not output:
raise RuntimeError("diffimg produced no output")
try: try:
res_json = json.loads(output) result = subprocess.run(cmd, capture_output=True, text=True)
return res_json.get('passed', False) output = result.stdout.strip()
except json.JSONDecodeError as e: if not output:
raise RuntimeError(f"Failed to parse diffimg output: {e}\nOutput: {output}") return False
try:
res_json = json.loads(output)
return res_json.get('passed', False)
except json.JSONDecodeError:
return False
except Exception:
return False
def _get_deletes_updates(update_dir, golden_dir, diffimg_path): def _get_deletes_updates(update_dir, golden_dir, diffimg_path):
ret_delete = [] ret_delete = []
@@ -93,13 +93,13 @@ def _get_deletes_updates(update_dir, golden_dir, diffimg_path):
# However, update_golden typically only updates/adds based on the new render set. # However, update_golden typically only updates/adds based on the new render set.
# But strict sync might imply deleting missing ones. # But strict sync might imply deleting missing ones.
# The original logic was: delete = list(base - new). # The original logic was: delete = list(base - new).
delete_files = list(base_files - new_files) delete = list(base - new)
# Files in new but not in base are definitely updates (additions) # Files in new but not in base are definitely updates (additions)
update_files = list(new_files - base_files) update = list(new - base)
# Files in both need comparison # Files in both need comparison
for fpath in base_files.intersection(new_files): for fpath in base.intersection(new_files):
base_fpath = os.path.join(golden_dir, fpath) base_fpath = os.path.join(golden_dir, fpath)
new_fpath = os.path.join(update_dir, fpath) new_fpath = os.path.join(update_dir, fpath)
@@ -110,10 +110,10 @@ def _get_deletes_updates(update_dir, golden_dir, diffimg_path):
is_different = _file_as_str(new_fpath) != _file_as_str(base_fpath) is_different = _file_as_str(new_fpath) != _file_as_str(base_fpath)
if is_different: if is_different:
ret_update.append(fpath) update.append(fpath)
ret_update += update_files ret_update += update
ret_delete += delete_files ret_delete += delete
return ret_delete, ret_update return ret_delete, ret_update
@@ -179,7 +179,7 @@ if __name__ == "__main__":
parser = ArgParseImpl() parser = ArgParseImpl()
parser.add_argument('--branch', help='Branch of the golden repo to write to') parser.add_argument('--branch', help='Branch of the golden repo to write to')
parser.add_argument('--golden-repo-token', help='Access token for the golden repo') parser.add_argument('--golden-repo-token', help='Access token for the golden repo')
parser.add_argument('--push-to-remote', action="store_true", help='Push the golden repo changes to remote') parser.add_argument('--push-to-remote', action="store_true", help='Access token for the golden repo')
parser.add_argument('--diffimg', help='Path to the diffimg tool', parser.add_argument('--diffimg', help='Path to the diffimg tool',
default='./out/cmake-release/tools/diffimg/diffimg') default='./out/cmake-release/tools/diffimg/diffimg')
+3 -4
View File
@@ -18,7 +18,6 @@ import shutil
import argparse import argparse
import sys import sys
import pathlib import pathlib
import shlex
def execute(cmd, def execute(cmd,
cwd=None, cwd=None,
@@ -26,7 +25,7 @@ def execute(cmd,
stdin=None, stdin=None,
env=None, env=None,
raise_errors=False): raise_errors=False):
in_env = os.environ.copy() in_env = os.environ
in_env.update(env if env else {}) in_env.update(env if env else {})
home = os.environ['HOME'] home = os.environ['HOME']
if f'{home}/bin' not in in_env['PATH']: if f'{home}/bin' not in in_env['PATH']:
@@ -46,11 +45,11 @@ def execute(cmd,
'universal_newlines': True 'universal_newlines': True
} }
if capture_output: if capture_output:
process = subprocess.Popen(shlex.split(cmd), **kwargs) process = subprocess.Popen(cmd.split(' '), **kwargs)
output, err_output = process.communicate() output, err_output = process.communicate()
return_code = process.returncode return_code = process.returncode
else: else:
return_code = subprocess.call(shlex.split(cmd), **kwargs) return_code = subprocess.call(cmd.split(' '), **kwargs)
if return_code: if return_code:
# Error # Error
-2
View File
@@ -54,8 +54,6 @@
{ {
"name": "Transimssion", "name": "Transimssion",
"description": "transmission", "description": "transmission",
// Disable transmission for webgpu because it seems flaky (b/488070152)
"backends": ["opengl", "vulkan"],
"apply_presets": ["base", "transmission_models"], "apply_presets": ["base", "transmission_models"],
"rendering": { "rendering": {
"camera.focalLength": 52.0 "camera.focalLength": 52.0
+1 -1
View File
@@ -1,6 +1,6 @@
{ {
"name": "filament", "name": "filament",
"version": "1.69.4", "version": "1.69.3",
"description": "Real-time physically based rendering engine", "description": "Real-time physically based rendering engine",
"main": "filament.js", "main": "filament.js",
"module": "filament.js", "module": "filament.js",