Compare commits
1
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
f2ae43e0e1 |
@@ -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
|
||||
@@ -10,27 +10,16 @@ jobs:
|
||||
# a branch on filament-assets.
|
||||
update-renderdiff-goldens:
|
||||
name: update-renderdiff-goldens
|
||||
runs-on: 'macos-14-xlarge'
|
||||
runs-on: 'ubuntu-24.04-4core'
|
||||
steps:
|
||||
- uses: actions/checkout@v4.1.6
|
||||
with:
|
||||
fetch-depth: 0
|
||||
- uses: ./.github/actions/linux-prereq
|
||||
- id: get_commit_msg
|
||||
uses: ./.github/actions/get-commit-msg
|
||||
- name: Check if accepting new goldens
|
||||
id: check_accept
|
||||
env:
|
||||
COMMIT_MESSAGE: ${{ steps.get_commit_msg.outputs.msg }}
|
||||
run: |
|
||||
if echo "${COMMIT_MESSAGE}" | python3 test/renderdiff/src/commit_msg.py --mode=accept_new_goldens; then
|
||||
echo "accept=true" >> "$GITHUB_OUTPUT"
|
||||
else
|
||||
echo "accept=false" >> "$GITHUB_OUTPUT"
|
||||
fi
|
||||
shell: bash
|
||||
- name: Renderdiff Generate for new goldens
|
||||
if: steps.check_accept.outputs.accept == 'true'
|
||||
uses: ./.github/actions/renderdiff-generate
|
||||
- name: Build diffimg
|
||||
run: ./build.sh release diffimg
|
||||
- name: Run update script
|
||||
env:
|
||||
GH_TOKEN: ${{ secrets.FILAMENTBOT_TOKEN }}
|
||||
@@ -38,24 +27,10 @@ jobs:
|
||||
run: |
|
||||
GOLDEN_BRANCH=$(echo "${COMMIT_MESSAGE}" | python3 test/renderdiff/src/commit_msg.py)
|
||||
COMMIT_HASH="${{ steps.get_commit_msg.outputs.hash }}"
|
||||
|
||||
git config --global user.email "filament.bot@gmail.com"
|
||||
git config --global user.name "Filament Bot"
|
||||
git config --global credential.helper cache
|
||||
|
||||
if [[ "${{ steps.check_accept.outputs.accept }}" == "true" ]]; then
|
||||
SHORT_HASH="${COMMIT_HASH:0:8}"
|
||||
GOLDEN_BRANCH="accept-goldens-${SHORT_HASH}"
|
||||
echo "Generating new goldens for branch ${GOLDEN_BRANCH}"
|
||||
python3 test/renderdiff/src/update_golden.py \
|
||||
--branch=${GOLDEN_BRANCH} \
|
||||
--source=$(pwd)/out/renderdiff/renders \
|
||||
--commit-msg="Auto-update goldens from ${COMMIT_HASH}" \
|
||||
--push-to-remote \
|
||||
--golden-repo-token=${GH_TOKEN}
|
||||
fi
|
||||
|
||||
if [[ "${GOLDEN_BRANCH}" != "main" ]]; then
|
||||
git config --global user.email "filament.bot@gmail.com"
|
||||
git config --global user.name "Filament Bot"
|
||||
git config --global credential.helper cache
|
||||
echo "branch==${GOLDEN_BRANCH}"
|
||||
echo "hash==${COMMIT_HASH}"
|
||||
python3 test/renderdiff/src/update_golden.py --branch=${GOLDEN_BRANCH} \
|
||||
|
||||
@@ -67,16 +67,7 @@ jobs:
|
||||
# Only build 1 64 bit target during presubmit to cut down build times during presubmit
|
||||
# Continuous builds will build everything
|
||||
run: |
|
||||
pushd .
|
||||
cd build/android && printf "y" | ./build.sh presubmit-with-archive arm64-v8a
|
||||
popd
|
||||
- name: Check artifact sizes
|
||||
run: |
|
||||
python3 test/sizeguard/dump_artifact_size.py out/*.aar > current_size.json
|
||||
python3 test/sizeguard/check_size.py current_size.json \
|
||||
--target-branch origin/main \
|
||||
--threshold 20480 \
|
||||
--artifacts filament-android-release.aar/jni/arm64-v8a/libfilament-jni.so
|
||||
cd build/android && printf "y" | ./build.sh presubmit arm64-v8a
|
||||
|
||||
build-ios:
|
||||
name: build-iOS
|
||||
@@ -126,24 +117,18 @@ jobs:
|
||||
- uses: actions/checkout@v4.1.6
|
||||
with:
|
||||
fetch-depth: 0
|
||||
- uses: ./.github/actions/mac-prereq
|
||||
- uses: ./.github/actions/get-gltf-assets
|
||||
- uses: ./.github/actions/get-mesa
|
||||
- uses: ./.github/actions/get-vulkan-sdk
|
||||
- id: get_commit_msg
|
||||
uses: ./.github/actions/get-commit-msg
|
||||
- name: Check if accepting new goldens
|
||||
id: check_accept
|
||||
env:
|
||||
COMMIT_MESSAGE: ${{ steps.get_commit_msg.outputs.msg }}
|
||||
- name: Prerequisites
|
||||
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
|
||||
# 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: Renderdiff Generate
|
||||
if: steps.check_accept.outputs.accept != 'true'
|
||||
uses: ./.github/actions/renderdiff-generate
|
||||
- name: Render and compare
|
||||
if: steps.check_accept.outputs.accept != 'true'
|
||||
id: render_compare
|
||||
env:
|
||||
COMMIT_MESSAGE: ${{ steps.get_commit_msg.outputs.msg }}
|
||||
@@ -152,6 +137,9 @@ jobs:
|
||||
source ${TEST_DIR}/src/preamble.sh
|
||||
set -eux
|
||||
GOLDEN_BRANCH=$(echo "${COMMIT_MESSAGE}" | python3 ${TEST_DIR}/src/commit_msg.py)
|
||||
bash ${TEST_DIR}/generate.sh
|
||||
# Build diffimg tool
|
||||
./build.sh release diffimg
|
||||
|
||||
python3 ${TEST_DIR}/src/golden_manager.py \
|
||||
--branch=${GOLDEN_BRANCH} \
|
||||
@@ -175,12 +163,10 @@ jobs:
|
||||
fi
|
||||
shell: bash
|
||||
- uses: actions/upload-artifact@v4
|
||||
if: steps.check_accept.outputs.accept != 'true'
|
||||
with:
|
||||
name: presubmit-renderdiff-result
|
||||
path: ./out/renderdiff
|
||||
- name: Compare result
|
||||
if: steps.check_accept.outputs.accept != 'true'
|
||||
run: |
|
||||
ERROR_STR="${{ steps.render_compare.outputs.err }}"
|
||||
if [ -n "${ERROR_STR}" ]; then
|
||||
|
||||
@@ -6,6 +6,3 @@
|
||||
appropriate header in [RELEASE_NOTES.md](./RELEASE_NOTES.md).
|
||||
|
||||
## Release notes for next branch cut
|
||||
|
||||
- engine: fix crash when using variance shadow maps
|
||||
- materials: better shadow normal-bias calculations [⚠️ **New Material Version**]
|
||||
|
||||
@@ -31,7 +31,7 @@ repositories {
|
||||
}
|
||||
|
||||
dependencies {
|
||||
implementation 'com.google.android.filament:filament-android:1.69.4'
|
||||
implementation 'com.google.android.filament:filament-android:1.69.2'
|
||||
}
|
||||
```
|
||||
|
||||
@@ -50,7 +50,7 @@ Here are all the libraries available in the group `com.google.android.filament`:
|
||||
iOS projects can use CocoaPods to install the latest release:
|
||||
|
||||
```shell
|
||||
pod 'Filament', '~> 1.69.4'
|
||||
pod 'Filament', '~> 1.69.2'
|
||||
```
|
||||
|
||||
## Documentation
|
||||
|
||||
@@ -7,13 +7,6 @@ A new header is inserted each time a *tag* is created.
|
||||
Instead, if you are authoring a PR for the main branch, add your release note to
|
||||
[NEW_RELEASE_NOTES.md](./NEW_RELEASE_NOTES.md).
|
||||
|
||||
## v1.69.5
|
||||
|
||||
- engine: fix crash when using variance shadow maps
|
||||
|
||||
## v1.69.4
|
||||
|
||||
|
||||
## v1.69.3
|
||||
|
||||
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
GROUP=com.google.android.filament
|
||||
VERSION_NAME=1.69.4
|
||||
VERSION_NAME=1.69.2
|
||||
|
||||
POM_DESCRIPTION=Real-time physically based rendering engine for Android.
|
||||
|
||||
|
||||
@@ -39,11 +39,6 @@ if [[ "$TARGET" == "presubmit-with-test" ]]; then
|
||||
RUN_TESTS=-u
|
||||
fi
|
||||
|
||||
if [[ "$TARGET" == "presubmit-with-archive" ]]; then
|
||||
BUILD_RELEASE=release
|
||||
GENERATE_ARCHIVES=-a
|
||||
fi
|
||||
|
||||
if [[ "$TARGET" == "debug" ]]; then
|
||||
BUILD_DEBUG=debug
|
||||
GENERATE_ARCHIVES=-a
|
||||
|
||||
+6
-6
@@ -235,9 +235,9 @@
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/minimatch": {
|
||||
"version": "3.1.3",
|
||||
"resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.3.tgz",
|
||||
"integrity": "sha512-M2GCs7Vk83NxkUyQV1bkABc4yxgz9kILhHImZiBPAZ9ybuvCb0/H7lEl5XvIg3g+9d4eNotkZA5IWwYl0tibaA==",
|
||||
"version": "3.1.2",
|
||||
"resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz",
|
||||
"integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==",
|
||||
"dependencies": {
|
||||
"brace-expansion": "^1.1.7"
|
||||
},
|
||||
@@ -429,9 +429,9 @@
|
||||
"integrity": "sha512-nGqtvLrj5w0naR6tDPfB4cUmYCqouzyQiz6C5y/LtcDllJdrcc6WaWW6iXyIIOErTa/XRybj28aasdn4LkVk6Q=="
|
||||
},
|
||||
"minimatch": {
|
||||
"version": "3.1.3",
|
||||
"resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.3.tgz",
|
||||
"integrity": "sha512-M2GCs7Vk83NxkUyQV1bkABc4yxgz9kILhHImZiBPAZ9ybuvCb0/H7lEl5XvIg3g+9d4eNotkZA5IWwYl0tibaA==",
|
||||
"version": "3.1.2",
|
||||
"resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz",
|
||||
"integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==",
|
||||
"requires": {
|
||||
"brace-expansion": "^1.1.7"
|
||||
}
|
||||
|
||||
+2
-2
@@ -181,7 +181,7 @@ important for <code>matc</code> (material compiler).</p>
|
||||
}
|
||||
|
||||
dependencies {
|
||||
implementation 'com.google.android.filament:filament-android:1.69.4'
|
||||
implementation 'com.google.android.filament:filament-android:1.69.2'
|
||||
}
|
||||
</code></pre>
|
||||
<p>Here are all the libraries available in the group <code>com.google.android.filament</code>:</p>
|
||||
@@ -195,7 +195,7 @@ dependencies {
|
||||
</div>
|
||||
<h3 id="ios"><a class="header" href="#ios">iOS</a></h3>
|
||||
<p>iOS projects can use CocoaPods to install the latest release:</p>
|
||||
<pre><code class="language-shell">pod 'Filament', '~> 1.69.4'
|
||||
<pre><code class="language-shell">pod 'Filament', '~> 1.69.2'
|
||||
</code></pre>
|
||||
<h2 id="documentation"><a class="header" href="#documentation">Documentation</a></h2>
|
||||
<ul>
|
||||
|
||||
@@ -228,7 +228,6 @@ branch called <code>my-pr-branch</code>, to <code>filament</code>. This PR requi
|
||||
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>
|
||||
<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.
|
||||
<pre><code>python3 test/renderdiff/src/update_golden.py
|
||||
</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>
|
||||
to the <code>main</code> branch of the golden repo.</li>
|
||||
</ul>
|
||||
<h3 id="automated-update-via-commit-message"><a class="header" href="#automated-update-via-commit-message">Automated update via commit message</a></h3>
|
||||
<p>Alternatively, if you are confident in your changes and want the CI to handle the update
|
||||
for you, you can use the following tag in your commit message:</p>
|
||||
<ul>
|
||||
<li>In the commit message of your working branch on <code>filament</code>, add the following line:
|
||||
<pre><code>RDIFF_ACCEPT_NEW_GOLDENS
|
||||
</code></pre>
|
||||
</li>
|
||||
</ul>
|
||||
<p>This has the following effects:</p>
|
||||
<ul>
|
||||
<li>The presubmit test <code>test-renderdiff</code> will be bypassed (it will not perform rendering or
|
||||
comparison).</li>
|
||||
<li>When the PR is merged, the postsubmit CI will automatically:
|
||||
<ol>
|
||||
<li>Build Filament and generate the new images.</li>
|
||||
<li>Upload them to a temporary branch in <code>filament-assets</code>.</li>
|
||||
<li>Merge that branch into <code>main</code>.</li>
|
||||
</ol>
|
||||
</li>
|
||||
</ul>
|
||||
<h2 id="viewing-test-results"><a class="header" href="#viewing-test-results">Viewing test results</a></h2>
|
||||
<p>We provide a viewer for looking at the result of a test run. The viewer is a webapp that can
|
||||
be used by pointing your browser to a localhost port. If you input the viewer with a PR or a
|
||||
|
||||
+199
-199
@@ -504,9 +504,9 @@ D_{GGX}(h,\alpha) = \frac{\aa}{\pi ( (\NoH)^2 (\aa - 1) + 1)^2}
|
||||
\end{equation}$$
|
||||
</p><p>
|
||||
The GLSL implementation of the NDF, shown in <a href="#listing_speculard">listing 1</a>, is simple and efficient.
|
||||
</p><pre class="listing tilde"><code><span class="line"><span class="hljs-function"><span class="hljs-type">float</span> <span class="hljs-title">D_GGX</span><span class="hljs-params">(<span class="hljs-type">float</span> NoH, <span class="hljs-type">float</span> roughness)</span> </span>{</span>
|
||||
<span class="line"> <span class="hljs-type">float</span> a = NoH * roughness;</span>
|
||||
<span class="line"> <span class="hljs-type">float</span> k = roughness / (<span class="hljs-number">1.0</span> - NoH * NoH + a * a);</span>
|
||||
</p><pre class="listing tilde"><code><span class="line"><span class="hljs-function"><span class="hljs-built_in">float</span> <span class="hljs-title">D_GGX</span>(<span class="hljs-params"><span class="hljs-built_in">float</span> NoH, <span class="hljs-built_in">float</span> roughness</span>)</span> {</span>
|
||||
<span class="line"> <span class="hljs-built_in">float</span> a = NoH * roughness;</span>
|
||||
<span class="line"> <span class="hljs-built_in">float</span> k = roughness / (<span class="hljs-number">1.0</span> - NoH * NoH + a * a);</span>
|
||||
<span class="line"> <span class="hljs-keyword">return</span> k * k * (<span class="hljs-number">1.0</span> / PI);</span>
|
||||
<span class="line">}</span></code></pre><center><div class="listingcaption tilde"><a class="target" name="listing_speculard"> </a><b style="font-style:normal;">Listing 1:</b> Implementation of the specular D term in GLSL</div></center>
|
||||
<p>
|
||||
@@ -590,10 +590,10 @@ V(v,l,\alpha) = \frac{0.5}{\NoL \sqrt{(\NoV)^2 (1 - \aa) + \aa} + \NoV \sqrt{(\N
|
||||
\end{equation}$$
|
||||
</p><p>
|
||||
The GLSL implementation of the visibility term, shown in <a href="#listing_specularv">listing 3</a>, is a bit more expensive than we would like since it requires two <code>sqrt</code> operations.
|
||||
</p><pre class="listing tilde"><code><span class="line"><span class="hljs-function"><span class="hljs-type">float</span> <span class="hljs-title">V_SmithGGXCorrelated</span><span class="hljs-params">(<span class="hljs-type">float</span> NoV, <span class="hljs-type">float</span> NoL, <span class="hljs-type">float</span> roughness)</span> </span>{</span>
|
||||
<span class="line"> <span class="hljs-type">float</span> a2 = roughness * roughness;</span>
|
||||
<span class="line"> <span class="hljs-type">float</span> GGXV = NoL * <span class="hljs-built_in">sqrt</span>(NoV * NoV * (<span class="hljs-number">1.0</span> - a2) + a2);</span>
|
||||
<span class="line"> <span class="hljs-type">float</span> GGXL = NoV * <span class="hljs-built_in">sqrt</span>(NoL * NoL * (<span class="hljs-number">1.0</span> - a2) + a2);</span>
|
||||
</p><pre class="listing tilde"><code><span class="line"><span class="hljs-function"><span class="hljs-keyword">float</span> <span class="hljs-title">V_SmithGGXCorrelated</span><span class="hljs-params">(<span class="hljs-keyword">float</span> NoV, <span class="hljs-keyword">float</span> NoL, <span class="hljs-keyword">float</span> roughness)</span> </span>{</span>
|
||||
<span class="line"> <span class="hljs-keyword">float</span> a2 = roughness * roughness;</span>
|
||||
<span class="line"> <span class="hljs-keyword">float</span> GGXV = NoL * <span class="hljs-built_in">sqrt</span>(NoV * NoV * (<span class="hljs-number">1.0</span> - a2) + a2);</span>
|
||||
<span class="line"> <span class="hljs-keyword">float</span> GGXL = NoV * <span class="hljs-built_in">sqrt</span>(NoL * NoL * (<span class="hljs-number">1.0</span> - a2) + a2);</span>
|
||||
<span class="line"> <span class="hljs-keyword">return</span> <span class="hljs-number">0.5</span> / (GGXV + GGXL);</span>
|
||||
<span class="line">}</span></code></pre><center><div class="listingcaption tilde"><a class="target" name="listing_specularv"> </a><b style="font-style:normal;">Listing 3:</b> Implementation of the specular V term in GLSL</div></center>
|
||||
<p>
|
||||
@@ -604,10 +604,10 @@ V(v,l,\alpha) = \frac{0.5}{\NoL (\NoV (1 - \alpha) + \alpha) + \NoV (\NoL (1 - \
|
||||
\end{equation}$$
|
||||
</p><p>
|
||||
This approximation is mathematically wrong but saves two square root operations and is good enough for real-time mobile applications, as shown in <a href="#listing_approximatedspecularv">listing 4</a>.
|
||||
</p><pre class="listing tilde"><code><span class="line"><span class="hljs-function"><span class="hljs-type">float</span> <span class="hljs-title">V_SmithGGXCorrelatedFast</span><span class="hljs-params">(<span class="hljs-type">float</span> NoV, <span class="hljs-type">float</span> NoL, <span class="hljs-type">float</span> roughness)</span> </span>{</span>
|
||||
<span class="line"> <span class="hljs-type">float</span> a = roughness;</span>
|
||||
<span class="line"> <span class="hljs-type">float</span> GGXV = NoL * (NoV * (<span class="hljs-number">1.0</span> - a) + a);</span>
|
||||
<span class="line"> <span class="hljs-type">float</span> GGXL = NoV * (NoL * (<span class="hljs-number">1.0</span> - a) + a);</span>
|
||||
</p><pre class="listing tilde"><code><span class="line"><span class="hljs-function"><span class="hljs-built_in">float</span> <span class="hljs-title">V_SmithGGXCorrelatedFast</span>(<span class="hljs-params"><span class="hljs-built_in">float</span> NoV, <span class="hljs-built_in">float</span> NoL, <span class="hljs-built_in">float</span> roughness</span>)</span> {</span>
|
||||
<span class="line"> <span class="hljs-built_in">float</span> a = roughness;</span>
|
||||
<span class="line"> <span class="hljs-built_in">float</span> GGXV = NoL * (NoV * (<span class="hljs-number">1.0</span> - a) + a);</span>
|
||||
<span class="line"> <span class="hljs-built_in">float</span> GGXL = NoV * (NoL * (<span class="hljs-number">1.0</span> - a) + a);</span>
|
||||
<span class="line"> <span class="hljs-keyword">return</span> <span class="hljs-number">0.5</span> / (GGXV + GGXL);</span>
|
||||
<span class="line">}</span></code></pre><center><div class="listingcaption tilde"><a class="target" name="listing_approximatedspecularv"> </a><b style="font-style:normal;">Listing 4:</b> Implementation of the approximated specular V term in GLSL</div></center>
|
||||
<p>
|
||||
@@ -659,7 +659,7 @@ $$\begin{equation}
|
||||
\end{equation}$$
|
||||
</p><p>
|
||||
In practice, the diffuse reflectance \(\sigma\) is multiplied later, as shown in <a href="#listing_diffusebrdf">listing 8</a>.
|
||||
</p><pre class="listing tilde"><code><span class="line"><span class="hljs-function"><span class="hljs-built_in">float</span> <span class="hljs-title">Fd_Lambert</span>()</span> {</span>
|
||||
</p><pre class="listing tilde"><code><span class="line"><span class="hljs-function"><span class="hljs-built_in">float</span> <span class="hljs-title">Fd_Lambert</span>(<span class="hljs-params"></span>)</span> {</span>
|
||||
<span class="line"> <span class="hljs-keyword">return</span> <span class="hljs-number">1.0</span> / PI;</span>
|
||||
<span class="line">}</span>
|
||||
<span class="line"></span>
|
||||
@@ -680,14 +680,14 @@ Where:
|
||||
$$\begin{equation}
|
||||
\fGrazing=0.5 + 2 \cdot \alpha cos^2(\theta_d)
|
||||
\end{equation}$$
|
||||
</p><pre class="listing tilde"><code><span class="line"><span class="hljs-type">float</span> <span class="hljs-title function_">F_Schlick</span><span class="hljs-params">(<span class="hljs-type">float</span> u, <span class="hljs-type">float</span> f0, <span class="hljs-type">float</span> f90)</span> {</span>
|
||||
<span class="line"> <span class="hljs-keyword">return</span> f0 + (f90 - f0) * pow(<span class="hljs-number">1.0</span> - u, <span class="hljs-number">5.0</span>);</span>
|
||||
</p><pre class="listing tilde"><code><span class="line"><span class="hljs-function"><span class="hljs-keyword">float</span> <span class="hljs-title">F_Schlick</span><span class="hljs-params">(<span class="hljs-keyword">float</span> u, <span class="hljs-keyword">float</span> f0, <span class="hljs-keyword">float</span> f90)</span> </span>{</span>
|
||||
<span class="line"> <span class="hljs-keyword">return</span> f0 + (f90 - f0) * <span class="hljs-built_in">pow</span>(<span class="hljs-number">1.0</span> - u, <span class="hljs-number">5.0</span>);</span>
|
||||
<span class="line">}</span>
|
||||
<span class="line"></span>
|
||||
<span class="line"><span class="hljs-type">float</span> <span class="hljs-title function_">Fd_Burley</span><span class="hljs-params">(<span class="hljs-type">float</span> NoV, <span class="hljs-type">float</span> NoL, <span class="hljs-type">float</span> LoH, <span class="hljs-type">float</span> roughness)</span> {</span>
|
||||
<span class="line"> <span class="hljs-type">float</span> <span class="hljs-variable">f90</span> <span class="hljs-operator">=</span> <span class="hljs-number">0.5</span> + <span class="hljs-number">2.0</span> * roughness * LoH * LoH;</span>
|
||||
<span class="line"> <span class="hljs-type">float</span> <span class="hljs-variable">lightScatter</span> <span class="hljs-operator">=</span> F_Schlick(NoL, <span class="hljs-number">1.0</span>, f90);</span>
|
||||
<span class="line"> <span class="hljs-type">float</span> <span class="hljs-variable">viewScatter</span> <span class="hljs-operator">=</span> F_Schlick(NoV, <span class="hljs-number">1.0</span>, f90);</span>
|
||||
<span class="line"><span class="hljs-function"><span class="hljs-keyword">float</span> <span class="hljs-title">Fd_Burley</span><span class="hljs-params">(<span class="hljs-keyword">float</span> NoV, <span class="hljs-keyword">float</span> NoL, <span class="hljs-keyword">float</span> LoH, <span class="hljs-keyword">float</span> roughness)</span> </span>{</span>
|
||||
<span class="line"> <span class="hljs-keyword">float</span> f90 = <span class="hljs-number">0.5</span> + <span class="hljs-number">2.0</span> * roughness * LoH * LoH;</span>
|
||||
<span class="line"> <span class="hljs-keyword">float</span> lightScatter = F_Schlick(NoL, <span class="hljs-number">1.0</span>, f90);</span>
|
||||
<span class="line"> <span class="hljs-keyword">float</span> viewScatter = F_Schlick(NoV, <span class="hljs-number">1.0</span>, f90);</span>
|
||||
<span class="line"> <span class="hljs-keyword">return</span> lightScatter * viewScatter * (<span class="hljs-number">1.0</span> / PI);</span>
|
||||
<span class="line">}</span></code></pre><center><div class="listingcaption tilde"><a class="target" name="listing_diffusebrdf"> </a><b style="font-style:normal;">Listing 8:</b> Implementation of the diffuse Disney BRDF in GLSL</div></center>
|
||||
<p>
|
||||
@@ -704,47 +704,47 @@ We could allow artists/developers to choose the Disney diffuse BRDF depending on
|
||||
<strong class="asterisk">Diffuse term</strong>: a Lambertian diffuse model.
|
||||
</p><p>
|
||||
The full GLSL implementation of the standard model is shown in <a href="#listing_glslbrdf">listing 9</a>.
|
||||
</p><pre class="listing tilde"><code><span class="line"><span class="hljs-type">float</span> <span class="hljs-title function_">D_GGX</span><span class="hljs-params">(<span class="hljs-type">float</span> NoH, <span class="hljs-type">float</span> a)</span> {</span>
|
||||
<span class="line"> <span class="hljs-type">float</span> <span class="hljs-variable">a2</span> <span class="hljs-operator">=</span> a * a;</span>
|
||||
<span class="line"> <span class="hljs-type">float</span> <span class="hljs-variable">f</span> <span class="hljs-operator">=</span> (NoH * a2 - NoH) * NoH + <span class="hljs-number">1.0</span>;</span>
|
||||
</p><pre class="listing tilde"><code><span class="line"><span class="hljs-type">float</span> D_GGX(<span class="hljs-type">float</span> NoH, <span class="hljs-type">float</span> a) {</span>
|
||||
<span class="line"> <span class="hljs-type">float</span> a2 = a * a;</span>
|
||||
<span class="line"> <span class="hljs-type">float</span> f = (NoH * a2 - NoH) * NoH + <span class="hljs-number">1.0</span>;</span>
|
||||
<span class="line"> <span class="hljs-keyword">return</span> a2 / (PI * f * f);</span>
|
||||
<span class="line">}</span>
|
||||
<span class="line"></span>
|
||||
<span class="line">vec3 <span class="hljs-title function_">F_Schlick</span><span class="hljs-params">(<span class="hljs-type">float</span> u, vec3 f0)</span> {</span>
|
||||
<span class="line"> <span class="hljs-keyword">return</span> f0 + (vec3(<span class="hljs-number">1.0</span>) - f0) * pow(<span class="hljs-number">1.0</span> - u, <span class="hljs-number">5.0</span>);</span>
|
||||
<span class="line"><span class="hljs-type">vec3</span> F_Schlick(<span class="hljs-type">float</span> u, <span class="hljs-type">vec3</span> f0) {</span>
|
||||
<span class="line"> <span class="hljs-keyword">return</span> f0 + (<span class="hljs-type">vec3</span>(<span class="hljs-number">1.0</span>) - f0) * <span class="hljs-built_in">pow</span>(<span class="hljs-number">1.0</span> - u, <span class="hljs-number">5.0</span>);</span>
|
||||
<span class="line">}</span>
|
||||
<span class="line"></span>
|
||||
<span class="line"><span class="hljs-type">float</span> <span class="hljs-title function_">V_SmithGGXCorrelated</span><span class="hljs-params">(<span class="hljs-type">float</span> NoV, <span class="hljs-type">float</span> NoL, <span class="hljs-type">float</span> a)</span> {</span>
|
||||
<span class="line"> <span class="hljs-type">float</span> <span class="hljs-variable">a2</span> <span class="hljs-operator">=</span> a * a;</span>
|
||||
<span class="line"> <span class="hljs-type">float</span> <span class="hljs-variable">GGXL</span> <span class="hljs-operator">=</span> NoV * sqrt((-NoL * a2 + NoL) * NoL + a2);</span>
|
||||
<span class="line"> <span class="hljs-type">float</span> <span class="hljs-variable">GGXV</span> <span class="hljs-operator">=</span> NoL * sqrt((-NoV * a2 + NoV) * NoV + a2);</span>
|
||||
<span class="line"><span class="hljs-type">float</span> V_SmithGGXCorrelated(<span class="hljs-type">float</span> NoV, <span class="hljs-type">float</span> NoL, <span class="hljs-type">float</span> a) {</span>
|
||||
<span class="line"> <span class="hljs-type">float</span> a2 = a * a;</span>
|
||||
<span class="line"> <span class="hljs-type">float</span> GGXL = NoV * <span class="hljs-built_in">sqrt</span>((-NoL * a2 + NoL) * NoL + a2);</span>
|
||||
<span class="line"> <span class="hljs-type">float</span> GGXV = NoL * <span class="hljs-built_in">sqrt</span>((-NoV * a2 + NoV) * NoV + a2);</span>
|
||||
<span class="line"> <span class="hljs-keyword">return</span> <span class="hljs-number">0.5</span> / (GGXV + GGXL);</span>
|
||||
<span class="line">}</span>
|
||||
<span class="line"></span>
|
||||
<span class="line"><span class="hljs-type">float</span> <span class="hljs-title function_">Fd_Lambert</span><span class="hljs-params">()</span> {</span>
|
||||
<span class="line"><span class="hljs-type">float</span> Fd_Lambert() {</span>
|
||||
<span class="line"> <span class="hljs-keyword">return</span> <span class="hljs-number">1.0</span> / PI;</span>
|
||||
<span class="line">}</span>
|
||||
<span class="line"></span>
|
||||
<span class="line"><span class="hljs-keyword">void</span> <span class="hljs-title function_">BRDF</span><span class="hljs-params">(...)</span> {</span>
|
||||
<span class="line"> <span class="hljs-type">vec3</span> <span class="hljs-variable">h</span> <span class="hljs-operator">=</span> normalize(v + l);</span>
|
||||
<span class="line"><span class="hljs-type">void</span> BRDF(...) {</span>
|
||||
<span class="line"> <span class="hljs-type">vec3</span> h = <span class="hljs-built_in">normalize</span>(v + l);</span>
|
||||
<span class="line"></span>
|
||||
<span class="line"> <span class="hljs-type">float</span> <span class="hljs-variable">NoV</span> <span class="hljs-operator">=</span> abs(dot(n, v)) + <span class="hljs-number">1e-5</span>;</span>
|
||||
<span class="line"> <span class="hljs-type">float</span> <span class="hljs-variable">NoL</span> <span class="hljs-operator">=</span> clamp(dot(n, l), <span class="hljs-number">0.0</span>, <span class="hljs-number">1.0</span>);</span>
|
||||
<span class="line"> <span class="hljs-type">float</span> <span class="hljs-variable">NoH</span> <span class="hljs-operator">=</span> clamp(dot(n, h), <span class="hljs-number">0.0</span>, <span class="hljs-number">1.0</span>);</span>
|
||||
<span class="line"> <span class="hljs-type">float</span> <span class="hljs-variable">LoH</span> <span class="hljs-operator">=</span> clamp(dot(l, h), <span class="hljs-number">0.0</span>, <span class="hljs-number">1.0</span>);</span>
|
||||
<span class="line"> <span class="hljs-type">float</span> NoV = <span class="hljs-built_in">abs</span>(<span class="hljs-built_in">dot</span>(n, v)) + <span class="hljs-number">1e-5</span>;</span>
|
||||
<span class="line"> <span class="hljs-type">float</span> NoL = <span class="hljs-built_in">clamp</span>(<span class="hljs-built_in">dot</span>(n, l), <span class="hljs-number">0.0</span>, <span class="hljs-number">1.0</span>);</span>
|
||||
<span class="line"> <span class="hljs-type">float</span> NoH = <span class="hljs-built_in">clamp</span>(<span class="hljs-built_in">dot</span>(n, h), <span class="hljs-number">0.0</span>, <span class="hljs-number">1.0</span>);</span>
|
||||
<span class="line"> <span class="hljs-type">float</span> LoH = <span class="hljs-built_in">clamp</span>(<span class="hljs-built_in">dot</span>(l, h), <span class="hljs-number">0.0</span>, <span class="hljs-number">1.0</span>);</span>
|
||||
<span class="line"></span>
|
||||
<span class="line"> <span class="hljs-comment">// perceptually linear roughness to roughness (see parameterization)</span></span>
|
||||
<span class="line"> <span class="hljs-type">float</span> <span class="hljs-variable">roughness</span> <span class="hljs-operator">=</span> perceptualRoughness * perceptualRoughness;</span>
|
||||
<span class="line"> <span class="hljs-type">float</span> roughness = perceptualRoughness * perceptualRoughness;</span>
|
||||
<span class="line"></span>
|
||||
<span class="line"> <span class="hljs-type">float</span> <span class="hljs-variable">D</span> <span class="hljs-operator">=</span> D_GGX(NoH, roughness);</span>
|
||||
<span class="line"> <span class="hljs-type">vec3</span> <span class="hljs-variable">F</span> <span class="hljs-operator">=</span> F_Schlick(LoH, f0);</span>
|
||||
<span class="line"> <span class="hljs-type">float</span> <span class="hljs-variable">V</span> <span class="hljs-operator">=</span> V_SmithGGXCorrelated(NoV, NoL, roughness);</span>
|
||||
<span class="line"> <span class="hljs-type">float</span> D = D_GGX(NoH, roughness);</span>
|
||||
<span class="line"> <span class="hljs-type">vec3</span> F = F_Schlick(LoH, f0);</span>
|
||||
<span class="line"> <span class="hljs-type">float</span> V = V_SmithGGXCorrelated(NoV, NoL, roughness);</span>
|
||||
<span class="line"></span>
|
||||
<span class="line"> <span class="hljs-comment">// specular BRDF</span></span>
|
||||
<span class="line"> <span class="hljs-type">vec3</span> <span class="hljs-variable">Fr</span> <span class="hljs-operator">=</span> (D * V) * F;</span>
|
||||
<span class="line"> <span class="hljs-type">vec3</span> Fr = (D * V) * F;</span>
|
||||
<span class="line"></span>
|
||||
<span class="line"> <span class="hljs-comment">// diffuse BRDF</span></span>
|
||||
<span class="line"> <span class="hljs-type">vec3</span> <span class="hljs-variable">Fd</span> <span class="hljs-operator">=</span> diffuseColor * Fd_Lambert();</span>
|
||||
<span class="line"> <span class="hljs-type">vec3</span> Fd = diffuseColor * Fd_Lambert();</span>
|
||||
<span class="line"></span>
|
||||
<span class="line"> <span class="hljs-comment">// apply lighting...</span></span>
|
||||
<span class="line">}</span></code></pre><center><div class="listingcaption tilde"><a class="target" name="listing_glslbrdf"> </a><b style="font-style:normal;">Listing 9:</b> Evaluation of the BRDF in GLSL</div></center>
|
||||
@@ -965,7 +965,7 @@ $$\begin{equation}
|
||||
\end{equation}$$
|
||||
</p><p>
|
||||
<a href="#listing_fnormal">Listing 12</a> shows how \(\fNormal\) is computed for both dielectric and metallic materials. It shows that the color of the specular reflectance is derived from the base color in the metallic case.
|
||||
</p><pre class="listing tilde"><code><span class="line"><span class="hljs-symbol">vec3</span> <span class="hljs-built_in">f0</span> = <span class="hljs-number">0</span>.<span class="hljs-number">16</span> * reflectance * reflectance * (<span class="hljs-number">1</span>.<span class="hljs-number">0</span> - metallic) + baseColor * metallic<span class="hljs-comment">;</span></span></code></pre><center><div class="listingcaption tilde"><a class="target" name="listing_fnormal"> </a><b style="font-style:normal;">Listing 12:</b> Computing \(\fNormal\) for dielectric and metallic materials in GLSL</div></center>
|
||||
</p><pre class="listing tilde"><code><span class="line">vec3 f0 = 0.16 <span class="hljs-emphasis">* reflectance *</span> reflectance <span class="hljs-emphasis">* (1.0 - metallic) + baseColor *</span> metallic;</span></code></pre><center><div class="listingcaption tilde"><a class="target" name="listing_fnormal"> </a><b style="font-style:normal;">Listing 12:</b> Computing \(\fNormal\) for dielectric and metallic materials in GLSL</div></center>
|
||||
<a class="target" name="roughnessremappingandclamping"> </a><a class="target" name="materialsystem/parameterization/remapping/roughnessremappingandclamping"> </a><a class="target" name="toc4.8.3.3"> </a><h4 id="roughness-remapping-and-clamping"><a class="header" href="#roughness-remapping-and-clamping">Roughness remapping and clamping</a></h4>
|
||||
<p>
|
||||
<p>The roughness set by the user, called <code>perceptualRoughness</code> here, is remapped to a perceptually linear range using the following formulation:</p>
|
||||
@@ -1054,7 +1054,7 @@ V(l,h) = \frac{1}{4(\LoH)^2}
|
||||
This masking-shadowing function is not physically based, as shown in [<a href="#citation-heitz14">Heitz14</a>], but its simplicity makes it desirable for real-time rendering.
|
||||
</p><p>
|
||||
In summary, our clear coat BRDF is a Cook-Torrance specular microfacet model, with a GGX normal distribution function, a Kelemen visibility function, and a Schlick Fresnel function. <a href="#listing_kelemen">Listing 13</a> shows how trivial the GLSL implementation is.
|
||||
</p><pre class="listing tilde"><code><span class="line"><span class="hljs-function"><span class="hljs-type">float</span> <span class="hljs-title">V_Kelemen</span><span class="hljs-params">(<span class="hljs-type">float</span> LoH)</span> </span>{</span>
|
||||
</p><pre class="listing tilde"><code><span class="line"><span class="hljs-function"><span class="hljs-built_in">float</span> <span class="hljs-title">V_Kelemen</span>(<span class="hljs-params"><span class="hljs-built_in">float</span> LoH</span>)</span> {</span>
|
||||
<span class="line"> <span class="hljs-keyword">return</span> <span class="hljs-number">0.25</span> / (LoH * LoH);</span>
|
||||
<span class="line">}</span></code></pre><center><div class="listingcaption tilde"><a class="target" name="listing_kelemen"> </a><b style="font-style:normal;">Listing 13:</b> Implementation of the Kelemen visibility term in GLSL</div></center>
|
||||
<p>
|
||||
@@ -1097,18 +1097,18 @@ The clear coat roughness parameter is remapped and clamped in a similar way to t
|
||||
<center><div class="image" style=""><a href="../images/material_clear_coat2.png" target="_blank"><img class="markdeep" src="../images/material_clear_coat2.png" /></a><center><span class="imagecaption"><a class="target" name="figure_clearcoatroughness"> </a><b style="font-style:normal;">Figure 26:</b> Clear coat roughness varying from 0.0 (left) to 1.0 (right) with metallic set to 1.0, roughness to 0.8 and clear coat to 1.0</span></center></div></center>
|
||||
</p><p>
|
||||
<a href="#listing_clearcoatbrdf">Listing 14</a> shows the GLSL implementation of the clear coat material model after remapping, parameterization and integration in the standard surface response.
|
||||
</p><pre class="listing tilde"><code><span class="line"><span class="hljs-function"><span class="hljs-type">void</span> <span class="hljs-title">BRDF</span><span class="hljs-params">(...)</span> </span>{</span>
|
||||
</p><pre class="listing tilde"><code><span class="line"><span class="hljs-function"><span class="hljs-keyword">void</span> <span class="hljs-title">BRDF</span>(<span class="hljs-params">...</span>)</span> {</span>
|
||||
<span class="line"> <span class="hljs-comment">// compute Fd and Fr from standard model</span></span>
|
||||
<span class="line"></span>
|
||||
<span class="line"> <span class="hljs-comment">// remapping and linearization of clear coat roughness</span></span>
|
||||
<span class="line"> clearCoatPerceptualRoughness = <span class="hljs-built_in">clamp</span>(clearCoatPerceptualRoughness, <span class="hljs-number">0.089</span>, <span class="hljs-number">1.0</span>);</span>
|
||||
<span class="line"> clearCoatPerceptualRoughness = clamp(clearCoatPerceptualRoughness, <span class="hljs-number">0.089</span>, <span class="hljs-number">1.0</span>);</span>
|
||||
<span class="line"> clearCoatRoughness = clearCoatPerceptualRoughness * clearCoatPerceptualRoughness;</span>
|
||||
<span class="line"></span>
|
||||
<span class="line"> <span class="hljs-comment">// clear coat BRDF</span></span>
|
||||
<span class="line"> <span class="hljs-type">float</span> Dc = <span class="hljs-built_in">D_GGX</span>(clearCoatRoughness, NoH);</span>
|
||||
<span class="line"> <span class="hljs-type">float</span> Vc = <span class="hljs-built_in">V_Kelemen</span>(clearCoatRoughness, LoH);</span>
|
||||
<span class="line"> <span class="hljs-type">float</span> Fc = <span class="hljs-built_in">F_Schlick</span>(<span class="hljs-number">0.04</span>, LoH) * clearCoat; <span class="hljs-comment">// clear coat strength</span></span>
|
||||
<span class="line"> <span class="hljs-type">float</span> Frc = (Dc * Vc) * Fc;</span>
|
||||
<span class="line"> <span class="hljs-built_in">float</span> Dc = D_GGX(clearCoatRoughness, NoH);</span>
|
||||
<span class="line"> <span class="hljs-built_in">float</span> Vc = V_Kelemen(clearCoatRoughness, LoH);</span>
|
||||
<span class="line"> <span class="hljs-built_in">float</span> Fc = F_Schlick(<span class="hljs-number">0.04</span>, LoH) * clearCoat; <span class="hljs-comment">// clear coat strength</span></span>
|
||||
<span class="line"> <span class="hljs-built_in">float</span> Frc = (Dc * Vc) * Fc;</span>
|
||||
<span class="line"></span>
|
||||
<span class="line"> <span class="hljs-comment">// account for energy loss in the base layer</span></span>
|
||||
<span class="line"> <span class="hljs-keyword">return</span> color * ((Fd + Fr * (<span class="hljs-number">1.0</span> - Fc)) * (<span class="hljs-number">1.0</span> - Fc) + Frc);</span>
|
||||
@@ -1294,14 +1294,14 @@ f_{r}(v,h,\alpha) = \frac{D_{velvet}(v,h,\alpha)}{4(\NoL + \NoV - (\NoL)(\NoV))}
|
||||
\end{equation}$$
|
||||
</p><p>
|
||||
The implementation of the velvet NDF is presented in <a href="#listing_clothbrdf">listing 17</a>, optimized to properly fit in half float formats and to avoid computing a costly cotangent, relying instead on trigonometric identities. Note that we removed the Fresnel component from this BRDF.
|
||||
</p><pre class="listing tilde"><code><span class="line"><span class="hljs-function"><span class="hljs-type">float</span> <span class="hljs-title">D_Ashikhmin</span><span class="hljs-params">(<span class="hljs-type">float</span> roughness, <span class="hljs-type">float</span> NoH)</span> </span>{</span>
|
||||
</p><pre class="listing tilde"><code><span class="line">float D_Ashikhmin(float roughness, float NoH) {</span>
|
||||
<span class="line"> <span class="hljs-comment">// Ashikhmin 2007, "Distribution-based BRDFs"</span></span>
|
||||
<span class="line"> <span class="hljs-type">float</span> a2 = roughness * roughness;</span>
|
||||
<span class="line"> <span class="hljs-type">float</span> cos2h = NoH * NoH;</span>
|
||||
<span class="line"> <span class="hljs-type">float</span> sin2h = <span class="hljs-built_in">max</span>(<span class="hljs-number">1.0</span> - cos2h, <span class="hljs-number">0.0078125</span>); <span class="hljs-comment">// 2^(-14/2), so sin2h^2 > 0 in fp16</span></span>
|
||||
<span class="line"> <span class="hljs-type">float</span> sin4h = sin2h * sin2h;</span>
|
||||
<span class="line"> <span class="hljs-type">float</span> cot2 = -cos2h / (a2 * sin2h);</span>
|
||||
<span class="line"> <span class="hljs-keyword">return</span> <span class="hljs-number">1.0</span> / (PI * (<span class="hljs-number">4.0</span> * a2 + <span class="hljs-number">1.0</span>) * sin4h) * (<span class="hljs-number">4.0</span> * <span class="hljs-built_in">exp</span>(cot2) + sin4h);</span>
|
||||
<span class="line"> float a<span class="hljs-number">2</span> = roughness * roughness;</span>
|
||||
<span class="line"> float <span class="hljs-keyword">cos</span><span class="hljs-number">2</span>h = NoH * NoH;</span>
|
||||
<span class="line"> float <span class="hljs-keyword">sin</span><span class="hljs-number">2</span>h = <span class="hljs-keyword">max</span>(<span class="hljs-number">1.0</span> - <span class="hljs-keyword">cos</span><span class="hljs-number">2</span>h, <span class="hljs-number">0.0078125</span>); <span class="hljs-comment">// 2^(-14/2), so sin2h^2 > 0 in fp16</span></span>
|
||||
<span class="line"> float <span class="hljs-keyword">sin</span><span class="hljs-number">4</span>h = <span class="hljs-keyword">sin</span><span class="hljs-number">2</span>h * <span class="hljs-keyword">sin</span><span class="hljs-number">2</span>h;</span>
|
||||
<span class="line"> float cot<span class="hljs-number">2</span> = -<span class="hljs-keyword">cos</span><span class="hljs-number">2</span>h / (a<span class="hljs-number">2</span> * <span class="hljs-keyword">sin</span><span class="hljs-number">2</span>h);</span>
|
||||
<span class="line"> <span class="hljs-keyword">return</span> <span class="hljs-number">1.0</span> / (PI * (<span class="hljs-number">4.0</span> * a<span class="hljs-number">2</span> + <span class="hljs-number">1.0</span>) * <span class="hljs-keyword">sin</span><span class="hljs-number">4</span>h) * (<span class="hljs-number">4.0</span> * exp(cot<span class="hljs-number">2</span>) + <span class="hljs-keyword">sin</span><span class="hljs-number">4</span>h);</span>
|
||||
<span class="line">}</span></code></pre><center><div class="listingcaption tilde"><a class="target" name="listing_clothbrdf"> </a><b style="font-style:normal;">Listing 17:</b> Implementation of Ashikhmin's velvet NDF in GLSL</div></center>
|
||||
<p>
|
||||
<p>In [<a href="#citation-estevez17">Estevez17</a>] Estevez and Kulla propose a different NDF (called the “Charlie” sheen) that is based on an exponentiated sinusoidal instead of an inverted Gaussian. This NDF is appealing for several reasons: its parameterization feels more natural and intuitive, it provides a softer appearance and, as shown in equation (\ref{charlieNDF}), its implementation is simpler:</p>
|
||||
@@ -1312,11 +1312,11 @@ D(m) = \frac{(2 + \frac{1}{\alpha}) sin(\theta)^{\frac{1}{\alpha}}}{2 \pi}
|
||||
</p><p>
|
||||
[<a href="#citation-estevez17">Estevez17</a>] also presents a new shadowing term that we omit here because of its cost. We instead rely on the visibility term from [<a href="#citation-neubelt13">Neubelt13</a>] (shown in equation \(\ref{clothSpecularBRDF}\) above).
|
||||
The implementation of this NDF is presented in <a href="#listing_clothcharliebrdf">listing 18</a>, optimized to properly fit in half float formats.
|
||||
</p><pre class="listing tilde"><code><span class="line"><span class="hljs-function"><span class="hljs-type">float</span> <span class="hljs-title">D_Charlie</span><span class="hljs-params">(<span class="hljs-type">float</span> roughness, <span class="hljs-type">float</span> NoH)</span> </span>{</span>
|
||||
</p><pre class="listing tilde"><code><span class="line"><span class="hljs-function"><span class="hljs-keyword">float</span> <span class="hljs-title">D_Charlie</span><span class="hljs-params">(<span class="hljs-keyword">float</span> roughness, <span class="hljs-keyword">float</span> NoH)</span> </span>{</span>
|
||||
<span class="line"> <span class="hljs-comment">// Estevez and Kulla 2017, "Production Friendly Microfacet Sheen BRDF"</span></span>
|
||||
<span class="line"> <span class="hljs-type">float</span> invAlpha = <span class="hljs-number">1.0</span> / roughness;</span>
|
||||
<span class="line"> <span class="hljs-type">float</span> cos2h = NoH * NoH;</span>
|
||||
<span class="line"> <span class="hljs-type">float</span> sin2h = <span class="hljs-built_in">max</span>(<span class="hljs-number">1.0</span> - cos2h, <span class="hljs-number">0.0078125</span>); <span class="hljs-comment">// 2^(-14/2), so sin2h^2 > 0 in fp16</span></span>
|
||||
<span class="line"> <span class="hljs-keyword">float</span> invAlpha = <span class="hljs-number">1.0</span> / roughness;</span>
|
||||
<span class="line"> <span class="hljs-keyword">float</span> cos2h = NoH * NoH;</span>
|
||||
<span class="line"> <span class="hljs-keyword">float</span> sin2h = max(<span class="hljs-number">1.0</span> - cos2h, <span class="hljs-number">0.0078125</span>); <span class="hljs-comment">// 2^(-14/2), so sin2h^2 > 0 in fp16</span></span>
|
||||
<span class="line"> <span class="hljs-keyword">return</span> (<span class="hljs-number">2.0</span> + invAlpha) * <span class="hljs-built_in">pow</span>(sin2h, invAlpha * <span class="hljs-number">0.5</span>) / (<span class="hljs-number">2.0</span> * PI);</span>
|
||||
<span class="line">}</span></code></pre><center><div class="listingcaption tilde"><a class="target" name="listing_clothcharliebrdf"> </a><b style="font-style:normal;">Listing 18:</b> Implementation of the “Charlie” NDF in GLSL</div></center>
|
||||
<a class="target" name="sheencolor"> </a><a class="target" name="materialsystem/clothmodel/clothspecularbrdf/sheencolor"> </a><a class="target" name="toc4.12.1.1"> </a><h4 id="sheen-color"><a class="header" href="#sheen-color">Sheen color</a></h4>
|
||||
@@ -1745,21 +1745,21 @@ The photometric attenuation function can be easily implemented in GLSL by adding
|
||||
<span class="line">}</span></code></pre><center><div class="listingcaption tilde"><a class="target" name="listing_glslphotometricpunctuallight"> </a><b style="font-style:normal;">Listing 22:</b> Implementation of attenuation from photometric profiles in GLSL</div></center>
|
||||
<p>
|
||||
<p>The light intensity is computed CPU-side (<a href="#listing_photometriclightintensity">listing 23</a>) and depends on whether the photometric profile is used as a mask.</p>
|
||||
</p><pre class="listing tilde"><code><span class="line"><span class="hljs-type">float</span> multiplier;</span>
|
||||
</p><pre class="listing tilde"><code><span class="line"><span class="hljs-keyword">float</span> multiplier;</span>
|
||||
<span class="line"><span class="hljs-comment">// Photometric profile used as a mask</span></span>
|
||||
<span class="line"><span class="hljs-keyword">if</span> (photometricLight.<span class="hljs-built_in">isMasked</span>()) {</span>
|
||||
<span class="line"><span class="hljs-keyword">if</span> (photometricLight.isMasked()) {</span>
|
||||
<span class="line"> <span class="hljs-comment">// The desired intensity is set by the artist</span></span>
|
||||
<span class="line"> <span class="hljs-comment">// The integrated intensity comes from a Monte-Carlo</span></span>
|
||||
<span class="line"> <span class="hljs-comment">// integration over the unit sphere around the luminaire</span></span>
|
||||
<span class="line"> multiplier = photometricLight.<span class="hljs-built_in">getDesiredIntensity</span>() /</span>
|
||||
<span class="line"> photometricLight.<span class="hljs-built_in">getIntegratedIntensity</span>();</span>
|
||||
<span class="line"> multiplier = photometricLight.getDesiredIntensity() /</span>
|
||||
<span class="line"> photometricLight.getIntegratedIntensity();</span>
|
||||
<span class="line">} <span class="hljs-keyword">else</span> {</span>
|
||||
<span class="line"> <span class="hljs-comment">// Multiplier provided for convenience, set to 1.0 by default</span></span>
|
||||
<span class="line"> multiplier = photometricLight.<span class="hljs-built_in">getMultiplier</span>();</span>
|
||||
<span class="line"> multiplier = photometricLight.getMultiplier();</span>
|
||||
<span class="line">}</span>
|
||||
<span class="line"></span>
|
||||
<span class="line"><span class="hljs-comment">// The max intensity in cd comes from the IES profile</span></span>
|
||||
<span class="line"><span class="hljs-type">float</span> lightIntensity = photometricLight.<span class="hljs-built_in">getMaxIntensity</span>() * multiplier;</span></code></pre><center><div class="listingcaption tilde"><a class="target" name="listing_photometriclightintensity"> </a><b style="font-style:normal;">Listing 23:</b> Computing the intensity of a photometric light on the CPU</div></center>
|
||||
<span class="line"><span class="hljs-keyword">float</span> lightIntensity = photometricLight.getMaxIntensity() * multiplier;</span></code></pre><center><div class="listingcaption tilde"><a class="target" name="listing_photometriclightintensity"> </a><b style="font-style:normal;">Listing 23:</b> Computing the intensity of a photometric light on the CPU</div></center>
|
||||
<p>
|
||||
<div class="endnote"><a class="target" name="endnote-xarrowintensity"> </a><sup>4</sup> The XArrow profile declares a luminous intensity of 1,750 lm but a Monte-Carlo integration shows an intensity of only 350 lm.
|
||||
</div>
|
||||
@@ -2058,19 +2058,19 @@ In practice only 4 or 9 coefficients (i.e.: 2 or 3 bands) are enough for \(\cosT
|
||||
<center><div class="image" style=""><a href="../images/ibl/ibl_irradiance_sh2.png" target="_blank"><img class="markdeep" src="../images/ibl/ibl_irradiance_sh2.png" style="max-width:100%;" /></a><center><span class="imagecaption"><a class="target" name="figure_iblsh2"> </a><b style="font-style:normal;">Figure 52:</b> 2 bands (4 coefficients)</span></center></div></center>
|
||||
</p><p>
|
||||
In practice we pre-convolve \(\Lt\) with \(\cosTheta\) and pre-scale these coefficients by the basis scaling factors \(K_l^m\) so that the reconstruction code is as simple as possible in the shader:
|
||||
</p><pre class="listing tilde"><code><span class="line">vec3 irradianceSH(vec3 n) {</span>
|
||||
<span class="line"> // uniform vec3 sphericalHarmonics<span class="hljs-selector-attr">[9]</span></span>
|
||||
<span class="line"> // We can <span class="hljs-selector-tag">use</span> only the first <span class="hljs-number">2</span> bands for better performance</span>
|
||||
<span class="line"> return</span>
|
||||
<span class="line"> sphericalHarmonics<span class="hljs-selector-attr">[0]</span></span>
|
||||
<span class="line"> + sphericalHarmonics<span class="hljs-selector-attr">[1]</span> * (n<span class="hljs-selector-class">.y</span>)</span>
|
||||
<span class="line"> + sphericalHarmonics<span class="hljs-selector-attr">[2]</span> * (n<span class="hljs-selector-class">.z</span>)</span>
|
||||
<span class="line"> + sphericalHarmonics<span class="hljs-selector-attr">[3]</span> * (n<span class="hljs-selector-class">.x</span>)</span>
|
||||
<span class="line"> + sphericalHarmonics<span class="hljs-selector-attr">[4]</span> * (n<span class="hljs-selector-class">.y</span> * n<span class="hljs-selector-class">.x</span>)</span>
|
||||
<span class="line"> + sphericalHarmonics<span class="hljs-selector-attr">[5]</span> * (n<span class="hljs-selector-class">.y</span> * n<span class="hljs-selector-class">.z</span>)</span>
|
||||
<span class="line"> + sphericalHarmonics<span class="hljs-selector-attr">[6]</span> * (<span class="hljs-number">3.0</span> * n<span class="hljs-selector-class">.z</span> * n<span class="hljs-selector-class">.z</span> - <span class="hljs-number">1.0</span>)</span>
|
||||
<span class="line"> + sphericalHarmonics<span class="hljs-selector-attr">[7]</span> * (n<span class="hljs-selector-class">.z</span> * n<span class="hljs-selector-class">.x</span>)</span>
|
||||
<span class="line"> + sphericalHarmonics<span class="hljs-selector-attr">[8]</span> * (n<span class="hljs-selector-class">.x</span> * n<span class="hljs-selector-class">.x</span> - n<span class="hljs-selector-class">.y</span> * n<span class="hljs-selector-class">.y</span>);</span>
|
||||
</p><pre class="listing tilde"><code><span class="line">vec3 <span class="hljs-function"><span class="hljs-title">irradianceSH</span>(<span class="hljs-params">vec3 n</span>)</span> {</span>
|
||||
<span class="line"> <span class="hljs-comment">// uniform vec3 sphericalHarmonics[9]</span></span>
|
||||
<span class="line"> <span class="hljs-comment">// We can use only the first 2 bands for better performance</span></span>
|
||||
<span class="line"> <span class="hljs-keyword">return</span></span>
|
||||
<span class="line"> sphericalHarmonics[<span class="hljs-number">0</span>]</span>
|
||||
<span class="line"> + sphericalHarmonics[<span class="hljs-number">1</span>] * (n.y)</span>
|
||||
<span class="line"> + sphericalHarmonics[<span class="hljs-number">2</span>] * (n.z)</span>
|
||||
<span class="line"> + sphericalHarmonics[<span class="hljs-number">3</span>] * (n.x)</span>
|
||||
<span class="line"> + sphericalHarmonics[<span class="hljs-number">4</span>] * (n.y * n.x)</span>
|
||||
<span class="line"> + sphericalHarmonics[<span class="hljs-number">5</span>] * (n.y * n.z)</span>
|
||||
<span class="line"> + sphericalHarmonics[<span class="hljs-number">6</span>] * (<span class="hljs-number">3.0</span> * n.z * n.z - <span class="hljs-number">1.0</span>)</span>
|
||||
<span class="line"> + sphericalHarmonics[<span class="hljs-number">7</span>] * (n.z * n.x)</span>
|
||||
<span class="line"> + sphericalHarmonics[<span class="hljs-number">8</span>] * (n.x * n.x - n.y * n.y);</span>
|
||||
<span class="line">}</span></code></pre><center><div class="listingcaption tilde"><a class="target" name="listing_irradiancesh"> </a><b style="font-style:normal;">Listing 26:</b> GLSL code to reconstruct the irradiance from the pre-scaled SH</div></center>
|
||||
<p>
|
||||
<p>Note that with 2 bands, the computation above becomes a single (4 \times 4) matrix-by-vector multiply.</p>
|
||||
@@ -2443,7 +2443,7 @@ LD(n, \alpha) &= \frac{\sum_i^N V(l_i, n,
|
||||
$$
|
||||
</p><p>
|
||||
These two new \(DFG\) terms simply need to replace the ones used in the implementation shown in section <a href="#toc9.5">9.5</a>:
|
||||
</p><pre class="listing tilde"><code><span class="line"><span class="hljs-type">float</span> Fc = <span class="hljs-built_in">pow</span>(<span class="hljs-number">1</span> - VoH, <span class="hljs-number">5.0f</span>);</span>
|
||||
</p><pre class="listing tilde"><code><span class="line"><span class="hljs-keyword">float</span> Fc = <span class="hljs-built_in">pow</span>(<span class="hljs-number">1</span> - VoH, <span class="hljs-number">5.0f</span>);</span>
|
||||
<span class="line">r.x += Gv * Fc;</span>
|
||||
<span class="line">r.y += Gv;</span></code></pre><center><div class="listingcaption tilde"><a class="target" name="listing_multiscatteriblpreintegration"> </a><b style="font-style:normal;">Listing 29:</b> C++ implementation of the \(L_{DFG}\) term for multiscattering</div></center>
|
||||
<p>
|
||||
@@ -2507,11 +2507,11 @@ using an environment made of colored vertical stripes (skybox hidden).</span></c
|
||||
<p>
|
||||
<p>When sampling the IBL, the clear coat layer is calculated as a second specular lobe. This specular lobe is oriented along the view direction since we cannot reasonably integrate over the hemisphere. <a href="#listing_clearcoatibl">Listing 31</a> demonstrates this approximation in practice. It also shows the energy conservation step. It is important to note that this second specular lobe is computed exactly the same way as the main specular lobe, using the same DFG approximation.</p>
|
||||
</p><pre class="listing tilde"><code><span class="line"><span class="hljs-comment">// clearCoat_NoV == shading_NoV if the clear coat layer doesn't have its own normal map</span></span>
|
||||
<span class="line"><span class="hljs-type">float</span> Fc = <span class="hljs-built_in">F_Schlick</span>(<span class="hljs-number">0.04</span>, <span class="hljs-number">1.0</span>, clearCoat_NoV) * clearCoat;</span>
|
||||
<span class="line"><span class="hljs-built_in">float</span> Fc = F_Schlick(<span class="hljs-number">0.04</span>, <span class="hljs-number">1.0</span>, clearCoat_NoV) * clearCoat;</span>
|
||||
<span class="line"><span class="hljs-comment">// base layer attenuation for energy compensation</span></span>
|
||||
<span class="line">iblDiffuse *= <span class="hljs-number">1.0</span> - Fc;</span>
|
||||
<span class="line">iblSpecular *= <span class="hljs-built_in">sq</span>(<span class="hljs-number">1.0</span> - Fc);</span>
|
||||
<span class="line">iblSpecular += <span class="hljs-built_in">specularIBL</span>(r, clearCoatPerceptualRoughness) * Fc;</span></code></pre><center><div class="listingcaption tilde"><a class="target" name="listing_clearcoatibl"> </a><b style="font-style:normal;">Listing 31:</b> GLSL implementation of the clear coat specular lobe for image-based lighting</div></center>
|
||||
<span class="line">iblSpecular *= sq(<span class="hljs-number">1.0</span> - Fc);</span>
|
||||
<span class="line">iblSpecular += specularIBL(r, clearCoatPerceptualRoughness) * Fc;</span></code></pre><center><div class="listingcaption tilde"><a class="target" name="listing_clearcoatibl"> </a><b style="font-style:normal;">Listing 31:</b> GLSL implementation of the clear coat specular lobe for image-based lighting</div></center>
|
||||
<a class="target" name="anisotropy"> </a><a class="target" name="lighting/imagebasedlights/anisotropy"> </a><a class="target" name="toc5.3.6"> </a><h3 id="anisotropy"><a class="header" href="#anisotropy">Anisotropy </a></h3>
|
||||
<p>
|
||||
<p>[<a href="#citation-mcauley15">McAuley15</a>] describes a technique called “bent reflection vector”, based [<a href="#citation-revie12">Revie12</a>]. The bent reflection vector is a rough approximation of anisotropic lighting but the alternative is to use importance sampling. This approximation is sufficiently cheap to compute and provides good results, as shown in <a href="#figure_anisotropicibl1">figure 59</a> and <a href="#figure_anisotropicibl2">figure 60</a>.</p>
|
||||
@@ -2550,17 +2550,17 @@ The DG term is generated using uniform sampling as recommended in [<a href="#cit
|
||||
<center><div class="image" style=""><a href="../images/ibl/dfg_cloth.png" target="_blank"><img class="markdeep" src="../images/ibl/dfg_cloth.png" /></a><center><span class="imagecaption"><a class="target" name="figure_dfgclothlut"> </a><b style="font-style:normal;">Figure 62:</b> DFG LUT with a 3rd channel encoding the DG term of the cloth BRDF</span></center></div></center>
|
||||
</p><p>
|
||||
The remainder of the image-based lighting implementation follows the same steps as the implementation of regular lights, including the optional subsurface scattering term and its wrap diffuse component. Just as with the clear coat IBL implementation, we cannot integrate over the hemisphere and use the view direction as the dominant light direction to compute the wrap diffuse component.
|
||||
</p><pre class="listing tilde"><code><span class="line"><span class="hljs-type">float</span> diffuse = <span class="hljs-built_in">Fd_Lambert</span>() * ambientOcclusion;</span>
|
||||
<span class="line"><span class="hljs-meta">#<span class="hljs-keyword">if</span> defined(SHADING_MODEL_CLOTH)</span></span>
|
||||
<span class="line"><span class="hljs-meta">#<span class="hljs-keyword">if</span> defined(MATERIAL_HAS_SUBSURFACE_COLOR)</span></span>
|
||||
<span class="line">diffuse *= <span class="hljs-built_in">saturate</span>((NoV + <span class="hljs-number">0.5</span>) / <span class="hljs-number">2.25</span>);</span>
|
||||
<span class="line"><span class="hljs-meta">#<span class="hljs-keyword">endif</span></span></span>
|
||||
<span class="line"><span class="hljs-meta">#<span class="hljs-keyword">endif</span></span></span>
|
||||
</p><pre class="listing tilde"><code><span class="line"><span class="hljs-built_in">float</span> diffuse = Fd_Lambert() * ambientOcclusion;</span>
|
||||
<span class="line"><span class="hljs-meta">#<span class="hljs-meta-keyword">if</span> defined(SHADING_MODEL_CLOTH)</span></span>
|
||||
<span class="line"><span class="hljs-meta">#<span class="hljs-meta-keyword">if</span> defined(MATERIAL_HAS_SUBSURFACE_COLOR)</span></span>
|
||||
<span class="line">diffuse *= saturate((NoV + <span class="hljs-number">0.5</span>) / <span class="hljs-number">2.25</span>);</span>
|
||||
<span class="line"><span class="hljs-meta">#<span class="hljs-meta-keyword">endif</span></span></span>
|
||||
<span class="line"><span class="hljs-meta">#<span class="hljs-meta-keyword">endif</span></span></span>
|
||||
<span class="line"></span>
|
||||
<span class="line">vec3 indirectDiffuse = <span class="hljs-built_in">irradianceIBL</span>(n) * diffuse;</span>
|
||||
<span class="line"><span class="hljs-meta">#<span class="hljs-keyword">if</span> defined(SHADING_MODEL_CLOTH) && defined(MATERIAL_HAS_SUBSURFACE_COLOR)</span></span>
|
||||
<span class="line">indirectDiffuse *= <span class="hljs-built_in">saturate</span>(subsurfaceColor + NoV);</span>
|
||||
<span class="line"><span class="hljs-meta">#<span class="hljs-keyword">endif</span></span></span>
|
||||
<span class="line">vec3 indirectDiffuse = irradianceIBL(n) * diffuse;</span>
|
||||
<span class="line"><span class="hljs-meta">#<span class="hljs-meta-keyword">if</span> defined(SHADING_MODEL_CLOTH) && defined(MATERIAL_HAS_SUBSURFACE_COLOR)</span></span>
|
||||
<span class="line">indirectDiffuse *= saturate(subsurfaceColor + NoV);</span>
|
||||
<span class="line"><span class="hljs-meta">#<span class="hljs-meta-keyword">endif</span></span></span>
|
||||
<span class="line"></span>
|
||||
<span class="line">vec3 ibl = diffuseColor * indirectDiffuse + indirectSpecular * specularColor;</span></code></pre><center><div class="listingcaption tilde"><a class="target" name="listing_clothapprox"> </a><b style="font-style:normal;">Listing 34:</b> GLSL implementation of the DFG approximation for the cloth NDF</div></center>
|
||||
<p>
|
||||
@@ -2982,20 +2982,20 @@ L_{max} &= 2^{EV_{100}} \times 1.2
|
||||
<span class="line"><span class="hljs-comment">// aperture in f-stops</span></span>
|
||||
<span class="line"><span class="hljs-comment">// shutterSpeed in seconds</span></span>
|
||||
<span class="line"><span class="hljs-comment">// sensitivity in ISO</span></span>
|
||||
<span class="line"><span class="hljs-type">float</span> <span class="hljs-title function_">exposureSettings</span><span class="hljs-params">(<span class="hljs-type">float</span> aperture, <span class="hljs-type">float</span> shutterSpeed, <span class="hljs-type">float</span> sensitivity)</span> {</span>
|
||||
<span class="line"><span class="hljs-function"><span class="hljs-keyword">float</span> <span class="hljs-title">exposureSettings</span><span class="hljs-params">(<span class="hljs-keyword">float</span> aperture, <span class="hljs-keyword">float</span> shutterSpeed, <span class="hljs-keyword">float</span> sensitivity)</span> </span>{</span>
|
||||
<span class="line"> <span class="hljs-keyword">return</span> log2((aperture * aperture) / shutterSpeed * <span class="hljs-number">100.0</span> / sensitivity);</span>
|
||||
<span class="line">}</span>
|
||||
<span class="line"></span>
|
||||
<span class="line"><span class="hljs-comment">// Computes the exposure normalization factor from</span></span>
|
||||
<span class="line"><span class="hljs-comment">// the camera's EV100</span></span>
|
||||
<span class="line"><span class="hljs-type">float</span> <span class="hljs-title function_">exposure</span><span class="hljs-params">(<span class="hljs-type">float</span> ev100)</span> {</span>
|
||||
<span class="line"> <span class="hljs-keyword">return</span> <span class="hljs-number">1.0</span> / (pow(<span class="hljs-number">2.0</span>, ev100) * <span class="hljs-number">1.2</span>);</span>
|
||||
<span class="line"><span class="hljs-function"><span class="hljs-keyword">float</span> <span class="hljs-title">exposure</span><span class="hljs-params">(<span class="hljs-keyword">float</span> ev100)</span> </span>{</span>
|
||||
<span class="line"> <span class="hljs-keyword">return</span> <span class="hljs-number">1.0</span> / (<span class="hljs-built_in">pow</span>(<span class="hljs-number">2.0</span>, ev100) * <span class="hljs-number">1.2</span>);</span>
|
||||
<span class="line">}</span>
|
||||
<span class="line"></span>
|
||||
<span class="line"><span class="hljs-type">float</span> <span class="hljs-variable">ev100</span> <span class="hljs-operator">=</span> exposureSettings(aperture, shutterSpeed, sensitivity);</span>
|
||||
<span class="line"><span class="hljs-type">float</span> <span class="hljs-variable">exposure</span> <span class="hljs-operator">=</span> exposure(ev100);</span>
|
||||
<span class="line"><span class="hljs-keyword">float</span> ev100 = exposureSettings(aperture, shutterSpeed, sensitivity);</span>
|
||||
<span class="line"><span class="hljs-keyword">float</span> exposure = exposure(ev100);</span>
|
||||
<span class="line"></span>
|
||||
<span class="line"><span class="hljs-type">vec4</span> <span class="hljs-variable">color</span> <span class="hljs-operator">=</span> evaluateLighting();</span>
|
||||
<span class="line">vec4 color = evaluateLighting();</span>
|
||||
<span class="line">color.rgb *= exposure;</span></code></pre><center><div class="listingcaption tilde"><a class="target" name="listing_fragmentexposure"> </a><b style="font-style:normal;">Listing 42:</b> Implementation of exposure in GLSL</div></center>
|
||||
<p>
|
||||
<p>In practice the exposure factor can be pre-computed on the CPU to save shader instructions.</p>
|
||||
@@ -3466,9 +3466,9 @@ Our implementation is presented in <a href="#listing_specularcolorimpl">listing&
|
||||
<span class="line"><span class="hljs-comment">// Data source:</span></span>
|
||||
<span class="line"><span class="hljs-comment">// http://cvrl.ioo.ucl.ac.uk/cmfs.htm</span></span>
|
||||
<span class="line"><span class="hljs-comment">// http://cvrl.ioo.ucl.ac.uk/database/text/cmfs/ciexyz31.htm</span></span>
|
||||
<span class="line"><span class="hljs-type">const</span> <span class="hljs-type">size_t</span> CIE_XYZ_START = <span class="hljs-number">360</span>;</span>
|
||||
<span class="line"><span class="hljs-type">const</span> <span class="hljs-type">size_t</span> CIE_XYZ_COUNT = <span class="hljs-number">471</span>;</span>
|
||||
<span class="line"><span class="hljs-type">const</span> float3 CIE_XYZ[CIE_XYZ_COUNT] = { ... };</span>
|
||||
<span class="line"><span class="hljs-keyword">const</span> <span class="hljs-keyword">size_t</span> CIE_XYZ_START = <span class="hljs-number">360</span>;</span>
|
||||
<span class="line"><span class="hljs-keyword">const</span> <span class="hljs-keyword">size_t</span> CIE_XYZ_COUNT = <span class="hljs-number">471</span>;</span>
|
||||
<span class="line"><span class="hljs-keyword">const</span> float3 CIE_XYZ[CIE_XYZ_COUNT] = { ... };</span>
|
||||
<span class="line"></span>
|
||||
<span class="line"><span class="hljs-comment">// CIE Standard Illuminant D65 relative spectral power distribution,</span></span>
|
||||
<span class="line"><span class="hljs-comment">// from 300nm to 830, at 5nm intervals</span></span>
|
||||
@@ -3476,51 +3476,51 @@ Our implementation is presented in <a href="#listing_specularcolorimpl">listing&
|
||||
<span class="line"><span class="hljs-comment">// Data source:</span></span>
|
||||
<span class="line"><span class="hljs-comment">// https://en.wikipedia.org/wiki/Illuminant_D65</span></span>
|
||||
<span class="line"><span class="hljs-comment">// https://cielab.xyz/pdf/CIE_sel_colorimetric_tables.xls</span></span>
|
||||
<span class="line"><span class="hljs-type">const</span> <span class="hljs-type">size_t</span> CIE_D65_INTERVAL = <span class="hljs-number">5</span>;</span>
|
||||
<span class="line"><span class="hljs-type">const</span> <span class="hljs-type">size_t</span> CIE_D65_START = <span class="hljs-number">300</span>;</span>
|
||||
<span class="line"><span class="hljs-type">const</span> <span class="hljs-type">size_t</span> CIE_D65_END = <span class="hljs-number">830</span>;</span>
|
||||
<span class="line"><span class="hljs-type">const</span> <span class="hljs-type">size_t</span> CIE_D65_COUNT = <span class="hljs-number">107</span>;</span>
|
||||
<span class="line"><span class="hljs-type">const</span> <span class="hljs-type">float</span> CIE_D65[CIE_D65_COUNT] = { ... };</span>
|
||||
<span class="line"><span class="hljs-keyword">const</span> <span class="hljs-keyword">size_t</span> CIE_D65_INTERVAL = <span class="hljs-number">5</span>;</span>
|
||||
<span class="line"><span class="hljs-keyword">const</span> <span class="hljs-keyword">size_t</span> CIE_D65_START = <span class="hljs-number">300</span>;</span>
|
||||
<span class="line"><span class="hljs-keyword">const</span> <span class="hljs-keyword">size_t</span> CIE_D65_END = <span class="hljs-number">830</span>;</span>
|
||||
<span class="line"><span class="hljs-keyword">const</span> <span class="hljs-keyword">size_t</span> CIE_D65_COUNT = <span class="hljs-number">107</span>;</span>
|
||||
<span class="line"><span class="hljs-keyword">const</span> <span class="hljs-keyword">float</span> CIE_D65[CIE_D65_COUNT] = { ... };</span>
|
||||
<span class="line"></span>
|
||||
<span class="line"><span class="hljs-keyword">struct</span> <span class="hljs-title class_">Sample</span> {</span>
|
||||
<span class="line"> <span class="hljs-type">float</span> w = <span class="hljs-number">0.0f</span>; <span class="hljs-comment">// wavelength</span></span>
|
||||
<span class="line"> std::complex<<span class="hljs-type">float</span>> ior; <span class="hljs-comment">// complex IOR, n + ik</span></span>
|
||||
<span class="line"><span class="hljs-class"><span class="hljs-keyword">struct</span> <span class="hljs-title">Sample</span> {</span></span>
|
||||
<span class="line"> <span class="hljs-keyword">float</span> w = <span class="hljs-number">0.0f</span>; <span class="hljs-comment">// wavelength</span></span>
|
||||
<span class="line"> <span class="hljs-built_in">std</span>::<span class="hljs-built_in">complex</span><<span class="hljs-keyword">float</span>> ior; <span class="hljs-comment">// complex IOR, n + ik</span></span>
|
||||
<span class="line">};</span>
|
||||
<span class="line"></span>
|
||||
<span class="line"><span class="hljs-function"><span class="hljs-type">static</span> <span class="hljs-type">float</span> <span class="hljs-title">illuminantD65</span><span class="hljs-params">(<span class="hljs-type">float</span> w)</span> </span>{</span>
|
||||
<span class="line"> <span class="hljs-keyword">auto</span> i0 = <span class="hljs-built_in">size_t</span>((w - CIE_D65_START) / CIE_D65_INTERVAL);</span>
|
||||
<span class="line"> uint2 indexBounds{i0, std::<span class="hljs-built_in">min</span>(i0 + <span class="hljs-number">1</span>, CIE_D65_END)};</span>
|
||||
<span class="line"><span class="hljs-function"><span class="hljs-keyword">static</span> <span class="hljs-keyword">float</span> <span class="hljs-title">illuminantD65</span><span class="hljs-params">(<span class="hljs-keyword">float</span> w)</span> </span>{</span>
|
||||
<span class="line"> <span class="hljs-keyword">auto</span> i0 = <span class="hljs-keyword">size_t</span>((w - CIE_D65_START) / CIE_D65_INTERVAL);</span>
|
||||
<span class="line"> uint2 indexBounds{i0, <span class="hljs-built_in">std</span>::min(i0 + <span class="hljs-number">1</span>, CIE_D65_END)};</span>
|
||||
<span class="line"></span>
|
||||
<span class="line"> float2 wavelengthBounds = CIE_D65_START + float2{indexBounds} * CIE_D65_INTERVAL;</span>
|
||||
<span class="line"> <span class="hljs-type">float</span> t = (w - wavelengthBounds.x) / (wavelengthBounds.y - wavelengthBounds.x);</span>
|
||||
<span class="line"> <span class="hljs-keyword">return</span> <span class="hljs-built_in">lerp</span>(CIE_D65[indexBounds.x], CIE_D65[indexBounds.y], t);</span>
|
||||
<span class="line"> <span class="hljs-keyword">float</span> t = (w - wavelengthBounds.x) / (wavelengthBounds.y - wavelengthBounds.x);</span>
|
||||
<span class="line"> <span class="hljs-keyword">return</span> lerp(CIE_D65[indexBounds.x], CIE_D65[indexBounds.y], t);</span>
|
||||
<span class="line">}</span>
|
||||
<span class="line"></span>
|
||||
<span class="line"><span class="hljs-comment">// For std::lower_bound</span></span>
|
||||
<span class="line"><span class="hljs-type">bool</span> <span class="hljs-keyword">operator</span><(<span class="hljs-type">const</span> Sample& lhs, <span class="hljs-type">const</span> Sample& rhs) {</span>
|
||||
<span class="line"><span class="hljs-keyword">bool</span> <span class="hljs-keyword">operator</span><(<span class="hljs-keyword">const</span> Sample& lhs, <span class="hljs-keyword">const</span> Sample& rhs) {</span>
|
||||
<span class="line"> <span class="hljs-keyword">return</span> lhs.w < rhs.w;</span>
|
||||
<span class="line">}</span>
|
||||
<span class="line"></span>
|
||||
<span class="line"><span class="hljs-comment">// The wavelength w must be between 360nm and 830nm</span></span>
|
||||
<span class="line"><span class="hljs-function"><span class="hljs-type">static</span> std::complex<<span class="hljs-type">float</span>> <span class="hljs-title">findSample</span><span class="hljs-params">(<span class="hljs-type">const</span> std::vector<sample>& samples, <span class="hljs-type">float</span> w)</span> </span>{</span>
|
||||
<span class="line"> <span class="hljs-keyword">auto</span> i1 = std::<span class="hljs-built_in">lower_bound</span>(</span>
|
||||
<span class="line"> samples.<span class="hljs-built_in">begin</span>(), samples.<span class="hljs-built_in">end</span>(), Sample{w, <span class="hljs-number">0.0f</span> + <span class="hljs-number">0.0</span><span class="hljs-keyword">if</span>});</span>
|
||||
<span class="line"><span class="hljs-function"><span class="hljs-keyword">static</span> <span class="hljs-built_in">std</span>::<span class="hljs-built_in">complex</span><<span class="hljs-keyword">float</span>> <span class="hljs-title">findSample</span><span class="hljs-params">(<span class="hljs-keyword">const</span> <span class="hljs-built_in">std</span>::<span class="hljs-built_in">vector</span><sample>& samples, <span class="hljs-keyword">float</span> w)</span> </span>{</span>
|
||||
<span class="line"> <span class="hljs-keyword">auto</span> i1 = <span class="hljs-built_in">std</span>::lower_bound(</span>
|
||||
<span class="line"> samples.begin(), samples.end(), Sample{w, <span class="hljs-number">0.0f</span> + <span class="hljs-number">0.0</span><span class="hljs-keyword">if</span>});</span>
|
||||
<span class="line"> <span class="hljs-keyword">auto</span> i0 = i1 - <span class="hljs-number">1</span>;</span>
|
||||
<span class="line"></span>
|
||||
<span class="line"> <span class="hljs-comment">// Interpolate the complex IORs</span></span>
|
||||
<span class="line"> <span class="hljs-type">float</span> t = (w - i0->w) / (i1->w - i0->w);</span>
|
||||
<span class="line"> <span class="hljs-type">float</span> n = <span class="hljs-built_in">lerp</span>(i0->ior.<span class="hljs-built_in">real</span>(), i1->ior.<span class="hljs-built_in">real</span>(), t);</span>
|
||||
<span class="line"> <span class="hljs-type">float</span> k = <span class="hljs-built_in">lerp</span>(i0->ior.<span class="hljs-built_in">imag</span>(), i1->ior.<span class="hljs-built_in">imag</span>(), t);</span>
|
||||
<span class="line"> <span class="hljs-keyword">float</span> t = (w - i0->w) / (i1->w - i0->w);</span>
|
||||
<span class="line"> <span class="hljs-keyword">float</span> n = lerp(i0->ior.real(), i1->ior.real(), t);</span>
|
||||
<span class="line"> <span class="hljs-keyword">float</span> k = lerp(i0->ior.imag(), i1->ior.imag(), t);</span>
|
||||
<span class="line"> <span class="hljs-keyword">return</span> { n, k };</span>
|
||||
<span class="line">}</span>
|
||||
<span class="line"></span>
|
||||
<span class="line"><span class="hljs-function"><span class="hljs-type">static</span> <span class="hljs-type">float</span> <span class="hljs-title">fresnel</span><span class="hljs-params">(<span class="hljs-type">const</span> std::complex<<span class="hljs-type">float</span>>& sample)</span> </span>{</span>
|
||||
<span class="line"> <span class="hljs-keyword">return</span> (((sample - (<span class="hljs-number">1.0f</span> + <span class="hljs-number">0</span><span class="hljs-keyword">if</span>)) * (std::<span class="hljs-built_in">conj</span>(sample) - (<span class="hljs-number">1.0f</span> + <span class="hljs-number">0</span><span class="hljs-keyword">if</span>))) /</span>
|
||||
<span class="line"> ((sample + (<span class="hljs-number">1.0f</span> + <span class="hljs-number">0</span><span class="hljs-keyword">if</span>)) * (std::<span class="hljs-built_in">conj</span>(sample) + (<span class="hljs-number">1.0f</span> + <span class="hljs-number">0</span><span class="hljs-keyword">if</span>)))).<span class="hljs-built_in">real</span>();</span>
|
||||
<span class="line"><span class="hljs-function"><span class="hljs-keyword">static</span> <span class="hljs-keyword">float</span> <span class="hljs-title">fresnel</span><span class="hljs-params">(<span class="hljs-keyword">const</span> <span class="hljs-built_in">std</span>::<span class="hljs-built_in">complex</span><<span class="hljs-keyword">float</span>>& sample)</span> </span>{</span>
|
||||
<span class="line"> <span class="hljs-keyword">return</span> (((sample - (<span class="hljs-number">1.0f</span> + <span class="hljs-number">0</span><span class="hljs-keyword">if</span>)) * (<span class="hljs-built_in">std</span>::conj(sample) - (<span class="hljs-number">1.0f</span> + <span class="hljs-number">0</span><span class="hljs-keyword">if</span>))) /</span>
|
||||
<span class="line"> ((sample + (<span class="hljs-number">1.0f</span> + <span class="hljs-number">0</span><span class="hljs-keyword">if</span>)) * (<span class="hljs-built_in">std</span>::conj(sample) + (<span class="hljs-number">1.0f</span> + <span class="hljs-number">0</span><span class="hljs-keyword">if</span>)))).real();</span>
|
||||
<span class="line">}</span>
|
||||
<span class="line"></span>
|
||||
<span class="line"><span class="hljs-function"><span class="hljs-type">static</span> float3 <span class="hljs-title">XYZ_to_sRGB</span><span class="hljs-params">(<span class="hljs-type">const</span> float3& v)</span> </span>{</span>
|
||||
<span class="line"> <span class="hljs-type">const</span> mat3f XYZ_sRGB{</span>
|
||||
<span class="line"><span class="hljs-function"><span class="hljs-keyword">static</span> float3 <span class="hljs-title">XYZ_to_sRGB</span><span class="hljs-params">(<span class="hljs-keyword">const</span> float3& v)</span> </span>{</span>
|
||||
<span class="line"> <span class="hljs-keyword">const</span> mat3f XYZ_sRGB{</span>
|
||||
<span class="line"> <span class="hljs-number">3.2404542f</span>, <span class="hljs-number">-0.9692660f</span>, <span class="hljs-number">0.0556434f</span>,</span>
|
||||
<span class="line"> <span class="hljs-number">-1.5371385f</span>, <span class="hljs-number">1.8760108f</span>, <span class="hljs-number">-0.2040259f</span>,</span>
|
||||
<span class="line"> <span class="hljs-number">-0.4985314f</span>, <span class="hljs-number">0.0415560f</span>, <span class="hljs-number">1.0572252f</span></span>
|
||||
@@ -3529,21 +3529,21 @@ Our implementation is presented in <a href="#listing_specularcolorimpl">listing&
|
||||
<span class="line">}</span>
|
||||
<span class="line"></span>
|
||||
<span class="line"><span class="hljs-comment">// Outputs a linear sRGB color</span></span>
|
||||
<span class="line"><span class="hljs-function"><span class="hljs-type">static</span> float3 <span class="hljs-title">computeColor</span><span class="hljs-params">(<span class="hljs-type">const</span> std::vector<sample>& samples)</span> </span>{</span>
|
||||
<span class="line"><span class="hljs-function"><span class="hljs-keyword">static</span> float3 <span class="hljs-title">computeColor</span><span class="hljs-params">(<span class="hljs-keyword">const</span> <span class="hljs-built_in">std</span>::<span class="hljs-built_in">vector</span><sample>& samples)</span> </span>{</span>
|
||||
<span class="line"> float3 xyz{<span class="hljs-number">0.0f</span>};</span>
|
||||
<span class="line"> <span class="hljs-type">float</span> y = <span class="hljs-number">0.0f</span>;</span>
|
||||
<span class="line"> <span class="hljs-keyword">float</span> y = <span class="hljs-number">0.0f</span>;</span>
|
||||
<span class="line"></span>
|
||||
<span class="line"> <span class="hljs-keyword">for</span> (<span class="hljs-type">size_t</span> i = <span class="hljs-number">0</span>; i < CIE_XYZ_COUNT; i++) {</span>
|
||||
<span class="line"> <span class="hljs-keyword">for</span> (<span class="hljs-keyword">size_t</span> i = <span class="hljs-number">0</span>; i < CIE_XYZ_COUNT; i++) {</span>
|
||||
<span class="line"> <span class="hljs-comment">// Current wavelength</span></span>
|
||||
<span class="line"> <span class="hljs-type">float</span> w = CIE_XYZ_START + i;</span>
|
||||
<span class="line"> <span class="hljs-keyword">float</span> w = CIE_XYZ_START + i;</span>
|
||||
<span class="line"></span>
|
||||
<span class="line"> <span class="hljs-comment">// Find most appropriate CIE XYZ sample for the wavelength</span></span>
|
||||
<span class="line"> <span class="hljs-keyword">auto</span> sample = <span class="hljs-built_in">findSample</span>(samples, w);</span>
|
||||
<span class="line"> <span class="hljs-keyword">auto</span> sample = findSample(samples, w);</span>
|
||||
<span class="line"> <span class="hljs-comment">// Compute Fresnel reflectance at normal incidence</span></span>
|
||||
<span class="line"> <span class="hljs-type">float</span> f0 = <span class="hljs-built_in">fresnel</span>(sample);</span>
|
||||
<span class="line"> <span class="hljs-keyword">float</span> f0 = fresnel(sample);</span>
|
||||
<span class="line"></span>
|
||||
<span class="line"> <span class="hljs-comment">// We need to multiply by the spectral power distribution of the illuminant</span></span>
|
||||
<span class="line"> <span class="hljs-type">float</span> d65 = <span class="hljs-built_in">illuminantD65</span>(w);</span>
|
||||
<span class="line"> <span class="hljs-keyword">float</span> d65 = illuminantD65(w);</span>
|
||||
<span class="line"></span>
|
||||
<span class="line"> xyz += f0 * CIE_XYZ[i] * d65;</span>
|
||||
<span class="line"> y += CIE_XYZ[i].y * d65;</span>
|
||||
@@ -3552,10 +3552,10 @@ Our implementation is presented in <a href="#listing_specularcolorimpl">listing&
|
||||
<span class="line"> <span class="hljs-comment">// Normalize so that 100% reflectance at every wavelength yields Y=1</span></span>
|
||||
<span class="line"> xyz /= y;</span>
|
||||
<span class="line"></span>
|
||||
<span class="line"> float3 linear = <span class="hljs-built_in">XYZ_to_sRGB</span>(xyz);</span>
|
||||
<span class="line"> float3 linear = XYZ_to_sRGB(xyz);</span>
|
||||
<span class="line"></span>
|
||||
<span class="line"> <span class="hljs-comment">// Normalize out-of-gamut values</span></span>
|
||||
<span class="line"> <span class="hljs-keyword">if</span> (<span class="hljs-built_in">any</span>(<span class="hljs-built_in">greaterThan</span>(linear, float3{<span class="hljs-number">1.0f</span>}))) linear *= <span class="hljs-number">1.0f</span> / <span class="hljs-built_in">max</span>(linear);</span>
|
||||
<span class="line"> <span class="hljs-keyword">if</span> (any(greaterThan(linear, float3{<span class="hljs-number">1.0f</span>}))) linear *= <span class="hljs-number">1.0f</span> / max(linear);</span>
|
||||
<span class="line"></span>
|
||||
<span class="line"> <span class="hljs-keyword">return</span> linear;</span>
|
||||
<span class="line">}</span></code></pre><center><div class="listingcaption tilde"><a class="target" name="listing_specularcolorimpl"> </a><b style="font-style:normal;">Listing 46:</b> C++ implementation to compute the base color of a metallic surface from spectral data</div></center>
|
||||
@@ -3735,47 +3735,47 @@ l &= \{ cos\phi sin\theta,sin\phi sin\theta,cos\theta \}
|
||||
<pre class="listing tilde"><code><span class="line">vec2f hammersley(uint i, <span class="hljs-built_in">float</span> numSamples) {</span>
|
||||
<span class="line"> uint bits = i;</span>
|
||||
<span class="line"> bits = (bits << <span class="hljs-string">16) | (bits >> 16</span>);</span>
|
||||
<span class="line"> bits = ((bits & <span class="hljs-number">0</span>x55555555) << <span class="hljs-number">1</span>) | ((bits & <span class="hljs-number">0</span>xAAAAAAAA) >> <span class="hljs-number">1</span>);</span>
|
||||
<span class="line"> bits = ((bits & <span class="hljs-number">0</span>x33333333) << <span class="hljs-number">2</span>) | ((bits & <span class="hljs-number">0</span>xCCCCCCCC) >> <span class="hljs-number">2</span>);</span>
|
||||
<span class="line"> bits = ((bits & <span class="hljs-number">0</span>x0F0F0F0F) << <span class="hljs-number">4</span>) | ((bits & <span class="hljs-number">0</span>xF0F0F0F0) >> <span class="hljs-number">4</span>);</span>
|
||||
<span class="line"> bits = ((bits & <span class="hljs-number">0</span>x00FF00FF) << <span class="hljs-number">8</span>) | ((bits & <span class="hljs-number">0</span>xFF00FF00) >> <span class="hljs-number">8</span>);</span>
|
||||
<span class="line"> return vec2f(i / numSamples, bits / exp2(<span class="hljs-number">32</span>));</span>
|
||||
<span class="line"> bits = ((bits & 0x55555555) << <span class="hljs-string">1) | ((bits & 0xAAAAAAAA) >> 1</span>);</span>
|
||||
<span class="line"> bits = ((bits & 0x33333333) << <span class="hljs-string">2) | ((bits & 0xCCCCCCCC) >> 2</span>);</span>
|
||||
<span class="line"> bits = ((bits & 0x0F0F0F0F) << <span class="hljs-string">4) | ((bits & 0xF0F0F0F0) >> 4</span>);</span>
|
||||
<span class="line"> bits = ((bits & 0x00FF00FF) << <span class="hljs-string">8) | ((bits & 0xFF00FF00) >> 8</span>);</span>
|
||||
<span class="line"> <span class="hljs-built_in">return</span> vec2f(i / numSamples, bits / exp2(32));</span>
|
||||
<span class="line">}</span></code></pre><center><div class="listingcaption tilde">C++ implementation of a Hammersley sequence generator</div></center>
|
||||
<a class="target" name="precomputinglforimage-basedlighting"> </a><a class="target" name="annex/precomputinglforimage-basedlighting"> </a><a class="target" name="toc9.5"> </a><h2 id="precomputing-l-for-image-based-lighting"><a class="header" href="#precomputing-l-for-image-based-lighting">Precomputing L for image-based lighting</a></h2>
|
||||
<p>
|
||||
<p>The term ( L_{DFG} ) is only dependent on ( \NoV ). Below, the normal is arbitrarily set to ( n=\left[0, 0, 1\right] ) and (v) is chosen to satisfy ( \NoV ). The vector ( h_i ) is the ( D_{GGX}(\alpha) ) important direction sample (i).</p>
|
||||
</p><pre class="listing tilde"><code><span class="line"><span class="hljs-type">float</span> <span class="hljs-title function_">GDFG</span><span class="hljs-params">(<span class="hljs-type">float</span> NoV, <span class="hljs-type">float</span> NoL, <span class="hljs-type">float</span> a)</span> {</span>
|
||||
<span class="line"> <span class="hljs-type">float</span> <span class="hljs-variable">a2</span> <span class="hljs-operator">=</span> a * a;</span>
|
||||
<span class="line"> <span class="hljs-type">float</span> <span class="hljs-variable">GGXL</span> <span class="hljs-operator">=</span> NoV * sqrt((-NoL * a2 + NoL) * NoL + a2);</span>
|
||||
<span class="line"> <span class="hljs-type">float</span> <span class="hljs-variable">GGXV</span> <span class="hljs-operator">=</span> NoL * sqrt((-NoV * a2 + NoV) * NoV + a2);</span>
|
||||
</p><pre class="listing tilde"><code><span class="line"><span class="hljs-type">float</span> GDFG(<span class="hljs-type">float</span> NoV, <span class="hljs-type">float</span> NoL, <span class="hljs-type">float</span> a) {</span>
|
||||
<span class="line"> <span class="hljs-type">float</span> a2 = a * a;</span>
|
||||
<span class="line"> <span class="hljs-type">float</span> GGXL = NoV * <span class="hljs-built_in">sqrt</span>((-NoL * a2 + NoL) * NoL + a2);</span>
|
||||
<span class="line"> <span class="hljs-type">float</span> GGXV = NoL * <span class="hljs-built_in">sqrt</span>((-NoV * a2 + NoV) * NoV + a2);</span>
|
||||
<span class="line"> <span class="hljs-keyword">return</span> (<span class="hljs-number">2</span> * NoL) / (GGXV + GGXL);</span>
|
||||
<span class="line">}</span>
|
||||
<span class="line"></span>
|
||||
<span class="line">float2 <span class="hljs-title function_">DFG</span><span class="hljs-params">(<span class="hljs-type">float</span> NoV, <span class="hljs-type">float</span> a)</span> {</span>
|
||||
<span class="line">float2 DFG(<span class="hljs-type">float</span> NoV, <span class="hljs-type">float</span> a) {</span>
|
||||
<span class="line"> float3 V;</span>
|
||||
<span class="line"> V.x = sqrt(<span class="hljs-number">1.0f</span> - NoV*NoV);</span>
|
||||
<span class="line"> V.y = <span class="hljs-number">0.0f</span>;</span>
|
||||
<span class="line"> V.x = <span class="hljs-built_in">sqrt</span>(<span class="hljs-number">1.0</span>f - NoV*NoV);</span>
|
||||
<span class="line"> V.y = <span class="hljs-number">0.0</span>f;</span>
|
||||
<span class="line"> V.z = NoV;</span>
|
||||
<span class="line"></span>
|
||||
<span class="line"> <span class="hljs-type">float2</span> <span class="hljs-variable">r</span> <span class="hljs-operator">=</span> <span class="hljs-number">0.0f</span>;</span>
|
||||
<span class="line"> <span class="hljs-keyword">for</span> (<span class="hljs-type">uint</span> <span class="hljs-variable">i</span> <span class="hljs-operator">=</span> <span class="hljs-number">0</span>; i < sampleCount; i++) {</span>
|
||||
<span class="line"> <span class="hljs-type">float2</span> <span class="hljs-variable">Xi</span> <span class="hljs-operator">=</span> hammersley(i, sampleCount);</span>
|
||||
<span class="line"> <span class="hljs-type">float3</span> <span class="hljs-variable">H</span> <span class="hljs-operator">=</span> importanceSampleGGX(Xi, a, N);</span>
|
||||
<span class="line"> <span class="hljs-type">float3</span> <span class="hljs-variable">L</span> <span class="hljs-operator">=</span> <span class="hljs-number">2.0f</span> * dot(V, H) * H - V;</span>
|
||||
<span class="line"> float2 r = <span class="hljs-number">0.0</span>f;</span>
|
||||
<span class="line"> <span class="hljs-keyword">for</span> (<span class="hljs-type">uint</span> i = <span class="hljs-number">0</span>; i < sampleCount; i++) {</span>
|
||||
<span class="line"> float2 Xi = hammersley(i, sampleCount);</span>
|
||||
<span class="line"> float3 H = importanceSampleGGX(Xi, a, N);</span>
|
||||
<span class="line"> float3 L = <span class="hljs-number">2.0</span>f * <span class="hljs-built_in">dot</span>(V, H) * H - V;</span>
|
||||
<span class="line"></span>
|
||||
<span class="line"> <span class="hljs-type">float</span> <span class="hljs-variable">VoH</span> <span class="hljs-operator">=</span> saturate(dot(V, H));</span>
|
||||
<span class="line"> <span class="hljs-type">float</span> <span class="hljs-variable">NoL</span> <span class="hljs-operator">=</span> saturate(L.z);</span>
|
||||
<span class="line"> <span class="hljs-type">float</span> <span class="hljs-variable">NoH</span> <span class="hljs-operator">=</span> saturate(H.z);</span>
|
||||
<span class="line"> <span class="hljs-type">float</span> VoH = saturate(<span class="hljs-built_in">dot</span>(V, H));</span>
|
||||
<span class="line"> <span class="hljs-type">float</span> NoL = saturate(L.z);</span>
|
||||
<span class="line"> <span class="hljs-type">float</span> NoH = saturate(H.z);</span>
|
||||
<span class="line"></span>
|
||||
<span class="line"> <span class="hljs-keyword">if</span> (NoL > <span class="hljs-number">0.0f</span>) {</span>
|
||||
<span class="line"> <span class="hljs-type">float</span> <span class="hljs-variable">G</span> <span class="hljs-operator">=</span> GDFG(NoV, NoL, a);</span>
|
||||
<span class="line"> <span class="hljs-type">float</span> <span class="hljs-variable">Gv</span> <span class="hljs-operator">=</span> G * VoH / NoH;</span>
|
||||
<span class="line"> <span class="hljs-type">float</span> <span class="hljs-variable">Fc</span> <span class="hljs-operator">=</span> pow(<span class="hljs-number">1</span> - VoH, <span class="hljs-number">5.0f</span>);</span>
|
||||
<span class="line"> <span class="hljs-keyword">if</span> (NoL > <span class="hljs-number">0.0</span>f) {</span>
|
||||
<span class="line"> <span class="hljs-type">float</span> G = GDFG(NoV, NoL, a);</span>
|
||||
<span class="line"> <span class="hljs-type">float</span> Gv = G * VoH / NoH;</span>
|
||||
<span class="line"> <span class="hljs-type">float</span> Fc = <span class="hljs-built_in">pow</span>(<span class="hljs-number">1</span> - VoH, <span class="hljs-number">5.0</span>f);</span>
|
||||
<span class="line"> r.x += Gv * (<span class="hljs-number">1</span> - Fc);</span>
|
||||
<span class="line"> r.y += Gv * Fc;</span>
|
||||
<span class="line"> }</span>
|
||||
<span class="line"> }</span>
|
||||
<span class="line"> <span class="hljs-keyword">return</span> r * (<span class="hljs-number">1.0f</span> / sampleCount);</span>
|
||||
<span class="line"> <span class="hljs-keyword">return</span> r * (<span class="hljs-number">1.0</span>f / sampleCount);</span>
|
||||
<span class="line">}</span></code></pre><center><div class="listingcaption tilde">C++ implementation of the \( L_{DFG} \) term</div></center>
|
||||
<a class="target" name="sphericalharmonics"> </a><a class="target" name="annex/sphericalharmonics"> </a><a class="target" name="toc9.6"> </a><h2 id="spherical-harmonics"><a class="header" href="#spherical-harmonics">Spherical Harmonics</a></h2>
|
||||
<p>
|
||||
@@ -3851,56 +3851,56 @@ sin(m \phi + \phi) &= sin(m \phi) cos(\phi) + cos(m \phi) sin(\phi) \Leftrightar
|
||||
\end{align*}$$
|
||||
</p><p>
|
||||
<a href="#listing_nonnormalizedshbasis">Listing 47</a> shows the C++ code to compute the non-normalized SH basis \(\frac{y^m_l(s)}{\sqrt{2} K^m_l}\):
|
||||
</p><pre class="listing tilde"><code><span class="line"><span class="hljs-function"><span class="hljs-type">static</span> <span class="hljs-keyword">inline</span> <span class="hljs-type">size_t</span> <span class="hljs-title">SHindex</span><span class="hljs-params">(<span class="hljs-type">ssize_t</span> m, <span class="hljs-type">size_t</span> l)</span> </span>{</span>
|
||||
</p><pre class="listing tilde"><code><span class="line"><span class="hljs-function"><span class="hljs-keyword">static</span> <span class="hljs-keyword">inline</span> <span class="hljs-keyword">size_t</span> <span class="hljs-title">SHindex</span><span class="hljs-params">(<span class="hljs-keyword">ssize_t</span> m, <span class="hljs-keyword">size_t</span> l)</span> </span>{</span>
|
||||
<span class="line"> <span class="hljs-keyword">return</span> l * (l + <span class="hljs-number">1</span>) + m;</span>
|
||||
<span class="line">}</span>
|
||||
<span class="line"></span>
|
||||
<span class="line"><span class="hljs-function"><span class="hljs-type">void</span> <span class="hljs-title">computeShBasis</span><span class="hljs-params">(</span>
|
||||
<span class="line"> <span class="hljs-type">double</span>* <span class="hljs-type">const</span> SHb,</span>
|
||||
<span class="line"> <span class="hljs-type">size_t</span> numBands,</span>
|
||||
<span class="line"> <span class="hljs-type">const</span> vec3& s)</span></span>
|
||||
<span class="line"><span class="hljs-function"><span class="hljs-keyword">void</span> <span class="hljs-title">computeShBasis</span><span class="hljs-params">(</span>
|
||||
<span class="line"> <span class="hljs-keyword">double</span>* <span class="hljs-keyword">const</span> SHb,</span>
|
||||
<span class="line"> <span class="hljs-keyword">size_t</span> numBands,</span>
|
||||
<span class="line"> <span class="hljs-keyword">const</span> vec3& s)</span></span>
|
||||
<span class="line"></span>{</span>
|
||||
<span class="line"> <span class="hljs-comment">// handle m=0 separately, since it produces only one coefficient</span></span>
|
||||
<span class="line"> <span class="hljs-type">double</span> Pml_2 = <span class="hljs-number">0</span>;</span>
|
||||
<span class="line"> <span class="hljs-type">double</span> Pml_1 = <span class="hljs-number">1</span>;</span>
|
||||
<span class="line"> <span class="hljs-keyword">double</span> Pml_2 = <span class="hljs-number">0</span>;</span>
|
||||
<span class="line"> <span class="hljs-keyword">double</span> Pml_1 = <span class="hljs-number">1</span>;</span>
|
||||
<span class="line"> SHb[<span class="hljs-number">0</span>] = Pml_1;</span>
|
||||
<span class="line"> <span class="hljs-keyword">for</span> (<span class="hljs-type">ssize_t</span> l = <span class="hljs-number">1</span>; l < numBands; l++) {</span>
|
||||
<span class="line"> <span class="hljs-type">double</span> Pml = ((<span class="hljs-number">2</span> * l - <span class="hljs-number">1</span>) * Pml_1 * s.z - (l - <span class="hljs-number">1</span>) * Pml_2) / l;</span>
|
||||
<span class="line"> <span class="hljs-keyword">for</span> (<span class="hljs-keyword">ssize_t</span> l = <span class="hljs-number">1</span>; l < numBands; l++) {</span>
|
||||
<span class="line"> <span class="hljs-keyword">double</span> Pml = ((<span class="hljs-number">2</span> * l - <span class="hljs-number">1</span>) * Pml_1 * s.z - (l - <span class="hljs-number">1</span>) * Pml_2) / l;</span>
|
||||
<span class="line"> Pml_2 = Pml_1;</span>
|
||||
<span class="line"> Pml_1 = Pml;</span>
|
||||
<span class="line"> SHb[<span class="hljs-built_in">SHindex</span>(<span class="hljs-number">0</span>, l)] = Pml;</span>
|
||||
<span class="line"> SHb[SHindex(<span class="hljs-number">0</span>, l)] = Pml;</span>
|
||||
<span class="line"> }</span>
|
||||
<span class="line"> <span class="hljs-type">double</span> Pmm = <span class="hljs-number">1</span>;</span>
|
||||
<span class="line"> <span class="hljs-keyword">for</span> (<span class="hljs-type">ssize_t</span> m = <span class="hljs-number">1</span>; m < numBands ; m++) {</span>
|
||||
<span class="line"> <span class="hljs-keyword">double</span> Pmm = <span class="hljs-number">1</span>;</span>
|
||||
<span class="line"> <span class="hljs-keyword">for</span> (<span class="hljs-keyword">ssize_t</span> m = <span class="hljs-number">1</span>; m < numBands ; m++) {</span>
|
||||
<span class="line"> Pmm = (<span class="hljs-number">1</span> - <span class="hljs-number">2</span> * m) * Pmm;</span>
|
||||
<span class="line"> <span class="hljs-type">double</span> Pml_2 = Pmm;</span>
|
||||
<span class="line"> <span class="hljs-type">double</span> Pml_1 = (<span class="hljs-number">2</span> * m + <span class="hljs-number">1</span>)*Pmm*s.z;</span>
|
||||
<span class="line"> <span class="hljs-keyword">double</span> Pml_2 = Pmm;</span>
|
||||
<span class="line"> <span class="hljs-keyword">double</span> Pml_1 = (<span class="hljs-number">2</span> * m + <span class="hljs-number">1</span>)*Pmm*s.z;</span>
|
||||
<span class="line"> <span class="hljs-comment">// l == m</span></span>
|
||||
<span class="line"> SHb[<span class="hljs-built_in">SHindex</span>(-m, m)] = Pml_2;</span>
|
||||
<span class="line"> SHb[<span class="hljs-built_in">SHindex</span>( m, m)] = Pml_2;</span>
|
||||
<span class="line"> SHb[SHindex(-m, m)] = Pml_2;</span>
|
||||
<span class="line"> SHb[SHindex( m, m)] = Pml_2;</span>
|
||||
<span class="line"> <span class="hljs-keyword">if</span> (m + <span class="hljs-number">1</span> < numBands) {</span>
|
||||
<span class="line"> <span class="hljs-comment">// l == m+1</span></span>
|
||||
<span class="line"> SHb[<span class="hljs-built_in">SHindex</span>(-m, m + <span class="hljs-number">1</span>)] = Pml_1;</span>
|
||||
<span class="line"> SHb[<span class="hljs-built_in">SHindex</span>( m, m + <span class="hljs-number">1</span>)] = Pml_1;</span>
|
||||
<span class="line"> <span class="hljs-keyword">for</span> (<span class="hljs-type">ssize_t</span> l = m + <span class="hljs-number">2</span>; l < numBands; l++) {</span>
|
||||
<span class="line"> <span class="hljs-type">double</span> Pml = ((<span class="hljs-number">2</span> * l - <span class="hljs-number">1</span>) * Pml_1 * s.z - (l + m - <span class="hljs-number">1</span>) * Pml_2)</span>
|
||||
<span class="line"> SHb[SHindex(-m, m + <span class="hljs-number">1</span>)] = Pml_1;</span>
|
||||
<span class="line"> SHb[SHindex( m, m + <span class="hljs-number">1</span>)] = Pml_1;</span>
|
||||
<span class="line"> <span class="hljs-keyword">for</span> (<span class="hljs-keyword">ssize_t</span> l = m + <span class="hljs-number">2</span>; l < numBands; l++) {</span>
|
||||
<span class="line"> <span class="hljs-keyword">double</span> Pml = ((<span class="hljs-number">2</span> * l - <span class="hljs-number">1</span>) * Pml_1 * s.z - (l + m - <span class="hljs-number">1</span>) * Pml_2)</span>
|
||||
<span class="line"> / (l - m);</span>
|
||||
<span class="line"> Pml_2 = Pml_1;</span>
|
||||
<span class="line"> Pml_1 = Pml;</span>
|
||||
<span class="line"> SHb[<span class="hljs-built_in">SHindex</span>(-m, l)] = Pml;</span>
|
||||
<span class="line"> SHb[<span class="hljs-built_in">SHindex</span>( m, l)] = Pml;</span>
|
||||
<span class="line"> SHb[SHindex(-m, l)] = Pml;</span>
|
||||
<span class="line"> SHb[SHindex( m, l)] = Pml;</span>
|
||||
<span class="line"> }</span>
|
||||
<span class="line"> }</span>
|
||||
<span class="line"> }</span>
|
||||
<span class="line"> <span class="hljs-type">double</span> Cm = s.x;</span>
|
||||
<span class="line"> <span class="hljs-type">double</span> Sm = s.y;</span>
|
||||
<span class="line"> <span class="hljs-keyword">for</span> (<span class="hljs-type">ssize_t</span> m = <span class="hljs-number">1</span>; m <= numBands ; m++) {</span>
|
||||
<span class="line"> <span class="hljs-keyword">for</span> (<span class="hljs-type">ssize_t</span> l = m; l < numBands ; l++) {</span>
|
||||
<span class="line"> SHb[<span class="hljs-built_in">SHindex</span>(-m, l)] *= Sm;</span>
|
||||
<span class="line"> SHb[<span class="hljs-built_in">SHindex</span>( m, l)] *= Cm;</span>
|
||||
<span class="line"> <span class="hljs-keyword">double</span> Cm = s.x;</span>
|
||||
<span class="line"> <span class="hljs-keyword">double</span> Sm = s.y;</span>
|
||||
<span class="line"> <span class="hljs-keyword">for</span> (<span class="hljs-keyword">ssize_t</span> m = <span class="hljs-number">1</span>; m <= numBands ; m++) {</span>
|
||||
<span class="line"> <span class="hljs-keyword">for</span> (<span class="hljs-keyword">ssize_t</span> l = m; l < numBands ; l++) {</span>
|
||||
<span class="line"> SHb[SHindex(-m, l)] *= Sm;</span>
|
||||
<span class="line"> SHb[SHindex( m, l)] *= Cm;</span>
|
||||
<span class="line"> }</span>
|
||||
<span class="line"> <span class="hljs-type">double</span> Cm1 = Cm * s.x - Sm * s.y;</span>
|
||||
<span class="line"> <span class="hljs-type">double</span> Sm1 = Sm * s.x + Cm * s.y;</span>
|
||||
<span class="line"> <span class="hljs-keyword">double</span> Cm1 = Cm * s.x - Sm * s.y;</span>
|
||||
<span class="line"> <span class="hljs-keyword">double</span> Sm1 = Sm * s.x + Cm * s.y;</span>
|
||||
<span class="line"> Cm = Cm1;</span>
|
||||
<span class="line"> Sm = Sm1;</span>
|
||||
<span class="line"> }</span>
|
||||
@@ -3979,10 +3979,10 @@ $$\begin{equation}
|
||||
\end{equation}$$
|
||||
</p><p>
|
||||
Here is the C++ code to compute \(\hat{C}_l\):
|
||||
</p><pre class="listing tilde"><code><span class="line"><span class="hljs-function"><span class="hljs-type">static</span> <span class="hljs-type">double</span> <span class="hljs-title">factorial</span><span class="hljs-params">(<span class="hljs-type">size_t</span> n, <span class="hljs-type">size_t</span> d = <span class="hljs-number">1</span>)</span></span>;</span>
|
||||
</p><pre class="listing tilde"><code><span class="line"><span class="hljs-function"><span class="hljs-keyword">static</span> <span class="hljs-keyword">double</span> <span class="hljs-title">factorial</span><span class="hljs-params">(<span class="hljs-keyword">size_t</span> n, <span class="hljs-keyword">size_t</span> d = <span class="hljs-number">1</span>)</span></span>;</span>
|
||||
<span class="line"></span>
|
||||
<span class="line"><span class="hljs-comment">// < cos(theta) > SH coefficients pre-multiplied by 1 / K(0,l)</span></span>
|
||||
<span class="line"><span class="hljs-function"><span class="hljs-type">double</span> <span class="hljs-title">computeTruncatedCosSh</span><span class="hljs-params">(<span class="hljs-type">size_t</span> l)</span> </span>{</span>
|
||||
<span class="line"><span class="hljs-function"><span class="hljs-keyword">double</span> <span class="hljs-title">computeTruncatedCosSh</span><span class="hljs-params">(<span class="hljs-keyword">size_t</span> l)</span> </span>{</span>
|
||||
<span class="line"> <span class="hljs-keyword">if</span> (l == <span class="hljs-number">0</span>) {</span>
|
||||
<span class="line"> <span class="hljs-keyword">return</span> M_PI;</span>
|
||||
<span class="line"> } <span class="hljs-keyword">else</span> <span class="hljs-keyword">if</span> (l == <span class="hljs-number">1</span>) {</span>
|
||||
@@ -3990,17 +3990,17 @@ Here is the C++ code to compute \(\hat{C}_l\):
|
||||
<span class="line"> } <span class="hljs-keyword">else</span> <span class="hljs-keyword">if</span> (l & <span class="hljs-number">1</span>) {</span>
|
||||
<span class="line"> <span class="hljs-keyword">return</span> <span class="hljs-number">0</span>;</span>
|
||||
<span class="line"> }</span>
|
||||
<span class="line"> <span class="hljs-type">const</span> <span class="hljs-type">size_t</span> l_2 = l / <span class="hljs-number">2</span>;</span>
|
||||
<span class="line"> <span class="hljs-type">double</span> A0 = ((l_2 & <span class="hljs-number">1</span>) ? <span class="hljs-number">1.0</span> : <span class="hljs-number">-1.0</span>) / ((l + <span class="hljs-number">2</span>) * (l - <span class="hljs-number">1</span>));</span>
|
||||
<span class="line"> <span class="hljs-type">double</span> A1 = <span class="hljs-built_in">factorial</span>(l, l_2) / (<span class="hljs-built_in">factorial</span>(l_2) * (<span class="hljs-number">1</span> << l));</span>
|
||||
<span class="line"> <span class="hljs-keyword">const</span> <span class="hljs-keyword">size_t</span> l_2 = l / <span class="hljs-number">2</span>;</span>
|
||||
<span class="line"> <span class="hljs-keyword">double</span> A0 = ((l_2 & <span class="hljs-number">1</span>) ? <span class="hljs-number">1.0</span> : <span class="hljs-number">-1.0</span>) / ((l + <span class="hljs-number">2</span>) * (l - <span class="hljs-number">1</span>));</span>
|
||||
<span class="line"> <span class="hljs-keyword">double</span> A1 = factorial(l, l_2) / (factorial(l_2) * (<span class="hljs-number">1</span> << l));</span>
|
||||
<span class="line"> <span class="hljs-keyword">return</span> <span class="hljs-number">2</span> * M_PI * A0 * A1;</span>
|
||||
<span class="line">}</span>
|
||||
<span class="line"></span>
|
||||
<span class="line"><span class="hljs-comment">// returns n! / d!</span></span>
|
||||
<span class="line"><span class="hljs-function"><span class="hljs-type">double</span> <span class="hljs-title">factorial</span><span class="hljs-params">(<span class="hljs-type">size_t</span> n, <span class="hljs-type">size_t</span> d )</span> </span>{</span>
|
||||
<span class="line"> d = std::<span class="hljs-built_in">max</span>(<span class="hljs-built_in">size_t</span>(<span class="hljs-number">1</span>), d);</span>
|
||||
<span class="line"> n = std::<span class="hljs-built_in">max</span>(<span class="hljs-built_in">size_t</span>(<span class="hljs-number">1</span>), n);</span>
|
||||
<span class="line"> <span class="hljs-type">double</span> r = <span class="hljs-number">1.0</span>;</span>
|
||||
<span class="line"><span class="hljs-function"><span class="hljs-keyword">double</span> <span class="hljs-title">factorial</span><span class="hljs-params">(<span class="hljs-keyword">size_t</span> n, <span class="hljs-keyword">size_t</span> d )</span> </span>{</span>
|
||||
<span class="line"> d = <span class="hljs-built_in">std</span>::max(<span class="hljs-keyword">size_t</span>(<span class="hljs-number">1</span>), d);</span>
|
||||
<span class="line"> n = <span class="hljs-built_in">std</span>::max(<span class="hljs-keyword">size_t</span>(<span class="hljs-number">1</span>), n);</span>
|
||||
<span class="line"> <span class="hljs-keyword">double</span> r = <span class="hljs-number">1.0</span>;</span>
|
||||
<span class="line"> <span class="hljs-keyword">if</span> (n == d) {</span>
|
||||
<span class="line"> <span class="hljs-comment">// intentionally left blank</span></span>
|
||||
<span class="line"> } <span class="hljs-keyword">else</span> <span class="hljs-keyword">if</span> (n > d) {</span>
|
||||
|
||||
+1
-1
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
@@ -16,13 +16,12 @@ class SimulatedSkybox {
|
||||
this.ozone = 0.0;
|
||||
this.msFactors = [0.1, 0.5, 0.0];
|
||||
this.contrast = 1.0;
|
||||
this.nightColor = [0.0, 3.0e-9, 7.5e-9];
|
||||
this.nightColor = [0.0, 0.0003, 0.00075];
|
||||
this.shimmerControl = [0.0, 20.0, 0.1];
|
||||
this.cloudControl = [0.0, 0.1, 8000.0, 0.0];
|
||||
this.cloudControl2 = [0.0, 0.0, 0.0, 0.0];
|
||||
this.waterControl = [50.0, 1.0, 1.0, 4.0]; // x=Strength, y=Speed, z=DerivativeTrick, w=Octaves
|
||||
this.starControl = [0.001, 1.0, 350.0, 0.01]; // x=Density, y=Enabled, z=Frequency, w=PixelScale
|
||||
this.starIntensity = 1.0;
|
||||
this.focalLength = 24.0;
|
||||
this.height = 1000.0;
|
||||
this.planetRadius = 6360.0;
|
||||
@@ -44,7 +43,7 @@ class SimulatedSkybox {
|
||||
|
||||
// Milky Way Parameters
|
||||
// x=Intensity, y=Saturation, z=Unused
|
||||
this.milkyWayControl = [1.0, 1.2, 0.05];
|
||||
this.milkyWayControl = [1.0, 1.0, 0.07];
|
||||
this.milkyWayEnabled = true;
|
||||
this.milkyWayRotation = [1, 0, 0, 0, 1, 0, 0, 0, 1]; // Identity by default
|
||||
this.initEntity();
|
||||
@@ -454,11 +453,6 @@ class SimulatedSkybox {
|
||||
this.updateCoefficients();
|
||||
}
|
||||
|
||||
setStarIntensity(intensity) {
|
||||
this.starIntensity = Math.max(0.0, intensity);
|
||||
this.updateCoefficients();
|
||||
}
|
||||
|
||||
setMoonPosition(direction) {
|
||||
// normalize
|
||||
const len = Math.hypot(direction[0], direction[1], direction[2]);
|
||||
@@ -506,13 +500,6 @@ class SimulatedSkybox {
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
setExposure(exposure) {
|
||||
this.exposure = exposure;
|
||||
this.updateCoefficients();
|
||||
}
|
||||
|
||||
updateCoefficients() {
|
||||
if (!this.materialInstance) {
|
||||
console.warn("updateCoefficients called before material loaded");
|
||||
@@ -598,7 +585,6 @@ class SimulatedSkybox {
|
||||
this.materialInstance.setFloat4Parameter('cloudControl2', new Float32Array(this.cloudControl2));
|
||||
this.materialInstance.setFloat4Parameter('waterControl', new Float32Array(this.waterControl));
|
||||
this.materialInstance.setFloat4Parameter('starControl', new Float32Array(this.starControl));
|
||||
this.materialInstance.setFloatParameter('starIntensity', this.starIntensity);
|
||||
|
||||
this.materialInstance.setFloatParameter('sunIntensity', physicalSunIntensity);
|
||||
|
||||
@@ -629,13 +615,12 @@ class SimulatedSkybox {
|
||||
|
||||
this.materialInstance.setFloatParameter('sunIntensity2', finalMoonIntensity);
|
||||
|
||||
// Scale Milky Way by Sun Intensity
|
||||
// Calibration: 1.0 User Intensity = 1.5e-3 cd/m^2 (Nits) approx.
|
||||
// Target: 1.5e-3 Nits. SunIntensity = 100,000.
|
||||
// Scale = 1.5e-3 / 1.0e5 = 1.5e-8.
|
||||
// Scale Milky Way by Sun Intensity (Pre-exposed) to match dynamic range
|
||||
// Calibration: 1.0 User Intensity ~ 0.025 Lux Relative (approx 2.5% of Sun Pixel Value at Day)
|
||||
// At Sunny 16 (Sun ~ 2.6), this gives ~0.065 pixel brightness, which is visible but dim.
|
||||
const mwIntensity = this.milkyWayEnabled ? this.milkyWayControl[0] : 0.0;
|
||||
const mwUniform = [
|
||||
mwIntensity * this.sunIntensity * 1.5e-8,
|
||||
mwIntensity * this.sunIntensity * 0.025,
|
||||
this.milkyWayControl[1],
|
||||
this.milkyWayControl[2]
|
||||
];
|
||||
@@ -649,7 +634,6 @@ class SimulatedSkybox {
|
||||
const moonHaloUpload = [...this.moonHalo];
|
||||
moonHaloUpload[2] *= moonRadConv;
|
||||
this.materialInstance.setFloat4Parameter('sunHalo2', new Float32Array(moonHaloUpload));
|
||||
this.materialInstance.setFloatParameter('exposure', this.exposure !== undefined ? this.exposure : 1.0);
|
||||
|
||||
// Solar Eclipse (CPU Calculation)
|
||||
const sunRadius = Math.acos(this.sunHalo[0]);
|
||||
|
||||
Binary file not shown.
+5
-26
@@ -218,7 +218,6 @@ class App {
|
||||
const exposure = this.getExposure();
|
||||
const preExposedIntensity = this.params.sunIntensity * exposure;
|
||||
this.skybox.setSunIntensity(preExposedIntensity);
|
||||
this.skybox.setExposure(exposure); // Update Skybox Exposure Uniform
|
||||
|
||||
// Moon Exposure
|
||||
if (this.mParams) {
|
||||
@@ -390,7 +389,7 @@ class App {
|
||||
};
|
||||
|
||||
mwFolder.add(this.mwParams, 'enabled').name('Enabled').onChange(updateMW);
|
||||
mwFolder.add(this.mwParams, 'intensity', 0.0, 100.0).onChange(updateMW);
|
||||
mwFolder.add(this.mwParams, 'intensity', 0.0, 5.0).onChange(updateMW);
|
||||
mwFolder.add(this.mwParams, 'saturation', 0.0, 2.0).onChange(updateMW);
|
||||
mwFolder.add(this.mwParams, 'blackPoint', 0.0, 0.5).name('Black Point').onChange(updateMW);
|
||||
mwFolder.add(this.mwParams, 'siderealTime', 0.0, 24.0).name('Sidereal Time').listen().onChange(updateMW);
|
||||
@@ -465,23 +464,18 @@ class App {
|
||||
const starFolder = gui.addFolder('Stars');
|
||||
this.sParams = {
|
||||
enabled: true,
|
||||
density: 0.001,
|
||||
intensity: 0.0 // 2^0 = 1.0
|
||||
density: 0.001
|
||||
};
|
||||
// Initialize defaults (Density 0.001, Enabled True)
|
||||
sky.setStarControl(0.001, true);
|
||||
sky.setStarIntensity(Math.pow(2.0, 0.0));
|
||||
|
||||
const updateStars = () => {
|
||||
sky.setStarControl(this.sParams.density, this.sParams.enabled);
|
||||
sky.setStarIntensity(Math.pow(2.0, this.sParams.intensity));
|
||||
};
|
||||
|
||||
starFolder.add(this.sParams, 'enabled').name('Enabled').onChange(updateStars);
|
||||
starFolder.add(this.sParams, 'density', 0.0, 0.01, 0.0001).name('Density').onChange(updateStars);
|
||||
starFolder.add(this.sParams, 'intensity', 0.0, 24.0).name('Intensity (Exp)').onChange(updateStars);
|
||||
starFolder.close();
|
||||
this.updateStars = updateStars;
|
||||
|
||||
const artFolder = gui.addFolder('Artistic');
|
||||
// Set Horizon Glow default to 0.0
|
||||
@@ -495,7 +489,7 @@ class App {
|
||||
artFolder.add(sky.msFactors, 2, 0.0, 1.0).name('Horizon Glow').onChange(v => sky.setHorizonGlow(v));
|
||||
artFolder.add(sky, 'contrast', 0.1, 2.0).onChange(v => sky.setContrast(v));
|
||||
|
||||
|
||||
artFolder.addColor(sky, 'nightColor').onChange(v => sky.setNightColor(v));
|
||||
|
||||
const shmFolder = artFolder.addFolder('Shimmer');
|
||||
// Set Shimmer Strength default to 0.0
|
||||
@@ -508,7 +502,7 @@ class App {
|
||||
const camFolder = gui.addFolder('Camera');
|
||||
camFolder.add(this.params, 'focalLength', 8.0, 300.0).name('Focal Length').onChange(() => this.updateCameraProjection());
|
||||
camFolder.add(this.params, 'aperture', 1.4, 32.0).onChange(() => this.updateCameraExposure());
|
||||
camFolder.add(this.params, 'shutterSpeed', 0.05, 1000.0).onChange(() => this.updateCameraExposure());
|
||||
camFolder.add(this.params, 'shutterSpeed', 1.0, 1000.0).onChange(() => this.updateCameraExposure());
|
||||
camFolder.add(this.params, 'iso', 50.0, 3200.0).onChange(() => this.updateCameraExposure());
|
||||
|
||||
const bloomFolder = camFolder.addFolder('Bloom');
|
||||
@@ -698,15 +692,13 @@ class App {
|
||||
const s = this.sParams;
|
||||
const b = this.bParams;
|
||||
const m = this.mParams;
|
||||
const mw = this.mwParams;
|
||||
const sk = this.skybox;
|
||||
|
||||
return {
|
||||
p: { a: p.aperture, ss: p.shutterSpeed, i: p.iso, st: p.sunTheta, sp: p.sunPhi, fl: p.focalLength, si: p.sunIntensity },
|
||||
c: { v: c.volumetrics, co: c.coverage, d: c.density, h: c.height, s: c.speed, e: c.evolution },
|
||||
w: { dt: w.derivativeTrick, st: w.strength, s: w.speed, o: w.octaves },
|
||||
s: { e: s.enabled, d: s.density, i: s.intensity },
|
||||
mw: { e: mw.enabled, i: mw.intensity, s: mw.saturation, bp: mw.blackPoint, st: mw.siderealTime, l: mw.latitude },
|
||||
s: { e: s.enabled, d: s.density },
|
||||
b: { e: b.enabled, lf: b.lensFlare },
|
||||
m: { e: m.enabled, az: m.azimuth, h: m.height, r: m.radius, i: m.intensity },
|
||||
cm: { t: this.camState.theta, p: this.camState.phi },
|
||||
@@ -731,7 +723,6 @@ class App {
|
||||
const c = state.c;
|
||||
const w = state.w;
|
||||
const s = state.s;
|
||||
const mw = state.mw;
|
||||
const b = state.b;
|
||||
const m = state.m;
|
||||
const k = state.k;
|
||||
@@ -766,18 +757,6 @@ class App {
|
||||
if (s) {
|
||||
if (s.e !== undefined) this.sParams.enabled = s.e;
|
||||
if (s.d !== undefined) this.sParams.density = s.d;
|
||||
if (s.i !== undefined) this.sParams.intensity = s.i;
|
||||
if (this.updateStars) this.updateStars();
|
||||
}
|
||||
|
||||
if (mw) {
|
||||
if (mw.e !== undefined) this.mwParams.enabled = mw.e;
|
||||
if (mw.i !== undefined) this.mwParams.intensity = mw.i;
|
||||
if (mw.s !== undefined) this.mwParams.saturation = mw.s;
|
||||
if (mw.bp !== undefined) this.mwParams.blackPoint = mw.bp;
|
||||
if (mw.st !== undefined) this.mwParams.siderealTime = mw.st;
|
||||
if (mw.l !== undefined) this.mwParams.latitude = mw.l;
|
||||
if (this.updateMW) this.updateMW();
|
||||
}
|
||||
|
||||
if (b) {
|
||||
|
||||
@@ -96,20 +96,10 @@ material {
|
||||
name : starControl, // x=Density, y=Enabled, z=Frequency, w=PixelScale
|
||||
precision : high
|
||||
},
|
||||
{
|
||||
type : float,
|
||||
name : starIntensity,
|
||||
precision : high
|
||||
},
|
||||
{
|
||||
type : sampler2d,
|
||||
name : moonTexture
|
||||
},
|
||||
{
|
||||
type : float,
|
||||
name : exposure,
|
||||
precision : high
|
||||
},
|
||||
{
|
||||
type : sampler2d,
|
||||
name : moonNormal
|
||||
@@ -172,9 +162,9 @@ fragment {
|
||||
// --- CONFIGURATION ---
|
||||
|
||||
// Stars
|
||||
#define STAR_GLOBAL_INTENSITY 100.0 // Master brightness multiplier [0.0 - 500.0]
|
||||
#define STAR_BRIGHTNESS_BASE 0.01 // Minimum random brightness [0.0 - 1.0]
|
||||
#define STAR_BRIGHTNESS_VAR 15.0 // Random brightness variance range [0.0 - 10.0]
|
||||
#define STAR_GLOBAL_INTENSITY 150.0 // Master brightness multiplier [0.0 - 500.0]
|
||||
#define STAR_BRIGHTNESS_BASE 0.5 // Minimum random brightness [0.0 - 1.0]
|
||||
#define STAR_BRIGHTNESS_VAR 4.0 // Random brightness variance range [0.0 - 10.0]
|
||||
#define STAR_FADE_SUN_ELV_HIGH 0.10 // Sun elevation (sin) where stars are fully hidden [0.0 - 0.5]
|
||||
#define STAR_FADE_SUN_ELV_LOW -0.20 // Sun elevation (sin) where stars are fully visible [-0.5 - 0.0]
|
||||
#define STAR_CLOUD_OCCLUSION 0.1 // Visibility when covered by clouds [0.0 - 1.0]
|
||||
@@ -971,11 +961,7 @@ fragment {
|
||||
highp vec4 sunHalo, highp vec4 cloudControl, highp vec4 cloudControl2,
|
||||
highp vec4 shimmerControl, highp vec4 waterControl,
|
||||
highp vec3 L2, highp float sunIntensity2, highp vec4 sunHalo2,
|
||||
sampler2D moonTex, sampler2D moonNormal,
|
||||
highp vec3 nightColor,
|
||||
highp mat3 milkyWayRotation,
|
||||
highp vec3 milkyWayControl,
|
||||
sampler2D milkyWayTexture) {
|
||||
sampler2D moonTex, sampler2D moonNormal) {
|
||||
|
||||
// Project to plane y=0
|
||||
highp float t = WATER_PLANE_HEIGHT / min(V.y, -0.0002); // Reduced clamp to minimize "wall" artifact
|
||||
@@ -1071,7 +1057,7 @@ fragment {
|
||||
highp float rHorizonMask = 1.0 - smoothstep(0.0, REFLECTION_HORIZON_FADE, R.y);
|
||||
|
||||
if (rHorizonMask > 0.0) {
|
||||
reflection += getStarLayer(R, L, reflCloudDensity, outTransmittance, materialParams.starControl) * rHorizonMask * materialParams.exposure;
|
||||
reflection += getStarLayer(R, L, reflCloudDensity, outTransmittance, materialParams.starControl) * rHorizonMask;
|
||||
}
|
||||
|
||||
// Sun Disk Reflection
|
||||
@@ -1102,29 +1088,6 @@ fragment {
|
||||
// Apply clouds to reflection
|
||||
reflection = mix(reflection, reflCloudLayer, reflCloudDensity);
|
||||
|
||||
// Add Milky Way to Reflection
|
||||
// Calculate Fade based on Sun Elevation
|
||||
highp float sunElvSin = L.y;
|
||||
highp float mwFade = smoothstep(STAR_FADE_SUN_ELV_HIGH, STAR_FADE_SUN_ELV_LOW, sunElvSin);
|
||||
|
||||
if (mwFade > 0.0) {
|
||||
highp vec3 mwColor = getMilkyWay(R, milkyWayRotation, milkyWayTexture, milkyWayControl);
|
||||
|
||||
// Apply Atmosphere Transmittance (approximate)
|
||||
mwColor *= outTransmittance;
|
||||
|
||||
// Apply Fades
|
||||
mwColor *= mwFade;
|
||||
mwColor *= (1.0 - reflMoonOcclusion); // Occlude by Moon
|
||||
mwColor *= (1.0 - reflCloudDensity); // Occlude by Clouds
|
||||
|
||||
reflection += mwColor;
|
||||
}
|
||||
|
||||
|
||||
// Add Night Color to Reflection
|
||||
reflection += nightColor;
|
||||
|
||||
// Fresnel
|
||||
highp float F0 = WATER_FRESNEL_F0; // Water
|
||||
highp float cosTheta = clamp(dot(-V, N_water), 0.0, 1.0);
|
||||
@@ -1224,7 +1187,7 @@ fragment {
|
||||
|
||||
// 7. Stars
|
||||
// Add stars before clouds (clouds cover stars)
|
||||
highp vec3 starColor = getStarLayer(V, L, cloudDensityVal, transmittance, materialParams.starControl) * materialParams.exposure * materialParams.starIntensity;
|
||||
highp vec3 starColor = getStarLayer(V, L, cloudDensityVal, transmittance, materialParams.starControl);
|
||||
|
||||
// 7b. Milky Way
|
||||
// Add Milky Way behind stars (conceptually) but handled similarly
|
||||
@@ -1269,11 +1232,7 @@ fragment {
|
||||
materialParams.shimmerControl,
|
||||
materialParams.waterControl,
|
||||
L2, materialParams.sunIntensity2, materialParams.sunHalo2,
|
||||
materialParams_moonTexture, materialParams_moonNormal,
|
||||
materialParams.nightColor,
|
||||
materialParams.milkyWayRotation,
|
||||
materialParams.milkyWayControl,
|
||||
materialParams_milkyWayTexture);
|
||||
materialParams_moonTexture, materialParams_moonNormal);
|
||||
color = applyDynamicToneMapping(color, L, materialParams.contrast);
|
||||
}
|
||||
|
||||
|
||||
@@ -16,13 +16,12 @@ class SimulatedSkybox {
|
||||
this.ozone = 0.0;
|
||||
this.msFactors = [0.1, 0.5, 0.0];
|
||||
this.contrast = 1.0;
|
||||
this.nightColor = [0.0, 3.0e-9, 7.5e-9];
|
||||
this.nightColor = [0.0, 0.0003, 0.00075];
|
||||
this.shimmerControl = [0.0, 20.0, 0.1];
|
||||
this.cloudControl = [0.0, 0.1, 8000.0, 0.0];
|
||||
this.cloudControl2 = [0.0, 0.0, 0.0, 0.0];
|
||||
this.waterControl = [50.0, 1.0, 1.0, 4.0]; // x=Strength, y=Speed, z=DerivativeTrick, w=Octaves
|
||||
this.starControl = [0.001, 1.0, 350.0, 0.01]; // x=Density, y=Enabled, z=Frequency, w=PixelScale
|
||||
this.starIntensity = 1.0;
|
||||
this.focalLength = 24.0;
|
||||
this.height = 1000.0;
|
||||
this.planetRadius = 6360.0;
|
||||
@@ -44,7 +43,7 @@ class SimulatedSkybox {
|
||||
|
||||
// Milky Way Parameters
|
||||
// x=Intensity, y=Saturation, z=Unused
|
||||
this.milkyWayControl = [1.0, 1.2, 0.05];
|
||||
this.milkyWayControl = [1.0, 1.0, 0.07];
|
||||
this.milkyWayEnabled = true;
|
||||
this.milkyWayRotation = [1, 0, 0, 0, 1, 0, 0, 0, 1]; // Identity by default
|
||||
this.initEntity();
|
||||
@@ -454,11 +453,6 @@ class SimulatedSkybox {
|
||||
this.updateCoefficients();
|
||||
}
|
||||
|
||||
setStarIntensity(intensity) {
|
||||
this.starIntensity = Math.max(0.0, intensity);
|
||||
this.updateCoefficients();
|
||||
}
|
||||
|
||||
setMoonPosition(direction) {
|
||||
// normalize
|
||||
const len = Math.hypot(direction[0], direction[1], direction[2]);
|
||||
@@ -506,13 +500,6 @@ class SimulatedSkybox {
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
setExposure(exposure) {
|
||||
this.exposure = exposure;
|
||||
this.updateCoefficients();
|
||||
}
|
||||
|
||||
updateCoefficients() {
|
||||
if (!this.materialInstance) {
|
||||
console.warn("updateCoefficients called before material loaded");
|
||||
@@ -598,7 +585,6 @@ class SimulatedSkybox {
|
||||
this.materialInstance.setFloat4Parameter('cloudControl2', new Float32Array(this.cloudControl2));
|
||||
this.materialInstance.setFloat4Parameter('waterControl', new Float32Array(this.waterControl));
|
||||
this.materialInstance.setFloat4Parameter('starControl', new Float32Array(this.starControl));
|
||||
this.materialInstance.setFloatParameter('starIntensity', this.starIntensity);
|
||||
|
||||
this.materialInstance.setFloatParameter('sunIntensity', physicalSunIntensity);
|
||||
|
||||
@@ -629,13 +615,12 @@ class SimulatedSkybox {
|
||||
|
||||
this.materialInstance.setFloatParameter('sunIntensity2', finalMoonIntensity);
|
||||
|
||||
// Scale Milky Way by Sun Intensity
|
||||
// Calibration: 1.0 User Intensity = 1.5e-3 cd/m^2 (Nits) approx.
|
||||
// Target: 1.5e-3 Nits. SunIntensity = 100,000.
|
||||
// Scale = 1.5e-3 / 1.0e5 = 1.5e-8.
|
||||
// Scale Milky Way by Sun Intensity (Pre-exposed) to match dynamic range
|
||||
// Calibration: 1.0 User Intensity ~ 0.025 Lux Relative (approx 2.5% of Sun Pixel Value at Day)
|
||||
// At Sunny 16 (Sun ~ 2.6), this gives ~0.065 pixel brightness, which is visible but dim.
|
||||
const mwIntensity = this.milkyWayEnabled ? this.milkyWayControl[0] : 0.0;
|
||||
const mwUniform = [
|
||||
mwIntensity * this.sunIntensity * 1.5e-8,
|
||||
mwIntensity * this.sunIntensity * 0.025,
|
||||
this.milkyWayControl[1],
|
||||
this.milkyWayControl[2]
|
||||
];
|
||||
@@ -649,7 +634,6 @@ class SimulatedSkybox {
|
||||
const moonHaloUpload = [...this.moonHalo];
|
||||
moonHaloUpload[2] *= moonRadConv;
|
||||
this.materialInstance.setFloat4Parameter('sunHalo2', new Float32Array(moonHaloUpload));
|
||||
this.materialInstance.setFloatParameter('exposure', this.exposure !== undefined ? this.exposure : 1.0);
|
||||
|
||||
// Solar Eclipse (CPU Calculation)
|
||||
const sunRadius = Math.acos(this.sunHalo[0]);
|
||||
|
||||
Binary file not shown.
@@ -218,7 +218,6 @@ class App {
|
||||
const exposure = this.getExposure();
|
||||
const preExposedIntensity = this.params.sunIntensity * exposure;
|
||||
this.skybox.setSunIntensity(preExposedIntensity);
|
||||
this.skybox.setExposure(exposure); // Update Skybox Exposure Uniform
|
||||
|
||||
// Moon Exposure
|
||||
if (this.mParams) {
|
||||
@@ -390,7 +389,7 @@ class App {
|
||||
};
|
||||
|
||||
mwFolder.add(this.mwParams, 'enabled').name('Enabled').onChange(updateMW);
|
||||
mwFolder.add(this.mwParams, 'intensity', 0.0, 100.0).onChange(updateMW);
|
||||
mwFolder.add(this.mwParams, 'intensity', 0.0, 5.0).onChange(updateMW);
|
||||
mwFolder.add(this.mwParams, 'saturation', 0.0, 2.0).onChange(updateMW);
|
||||
mwFolder.add(this.mwParams, 'blackPoint', 0.0, 0.5).name('Black Point').onChange(updateMW);
|
||||
mwFolder.add(this.mwParams, 'siderealTime', 0.0, 24.0).name('Sidereal Time').listen().onChange(updateMW);
|
||||
@@ -465,23 +464,18 @@ class App {
|
||||
const starFolder = gui.addFolder('Stars');
|
||||
this.sParams = {
|
||||
enabled: true,
|
||||
density: 0.001,
|
||||
intensity: 0.0 // 2^0 = 1.0
|
||||
density: 0.001
|
||||
};
|
||||
// Initialize defaults (Density 0.001, Enabled True)
|
||||
sky.setStarControl(0.001, true);
|
||||
sky.setStarIntensity(Math.pow(2.0, 0.0));
|
||||
|
||||
const updateStars = () => {
|
||||
sky.setStarControl(this.sParams.density, this.sParams.enabled);
|
||||
sky.setStarIntensity(Math.pow(2.0, this.sParams.intensity));
|
||||
};
|
||||
|
||||
starFolder.add(this.sParams, 'enabled').name('Enabled').onChange(updateStars);
|
||||
starFolder.add(this.sParams, 'density', 0.0, 0.01, 0.0001).name('Density').onChange(updateStars);
|
||||
starFolder.add(this.sParams, 'intensity', 0.0, 24.0).name('Intensity (Exp)').onChange(updateStars);
|
||||
starFolder.close();
|
||||
this.updateStars = updateStars;
|
||||
|
||||
const artFolder = gui.addFolder('Artistic');
|
||||
// Set Horizon Glow default to 0.0
|
||||
@@ -495,7 +489,7 @@ class App {
|
||||
artFolder.add(sky.msFactors, 2, 0.0, 1.0).name('Horizon Glow').onChange(v => sky.setHorizonGlow(v));
|
||||
artFolder.add(sky, 'contrast', 0.1, 2.0).onChange(v => sky.setContrast(v));
|
||||
|
||||
|
||||
artFolder.addColor(sky, 'nightColor').onChange(v => sky.setNightColor(v));
|
||||
|
||||
const shmFolder = artFolder.addFolder('Shimmer');
|
||||
// Set Shimmer Strength default to 0.0
|
||||
@@ -508,7 +502,7 @@ class App {
|
||||
const camFolder = gui.addFolder('Camera');
|
||||
camFolder.add(this.params, 'focalLength', 8.0, 300.0).name('Focal Length').onChange(() => this.updateCameraProjection());
|
||||
camFolder.add(this.params, 'aperture', 1.4, 32.0).onChange(() => this.updateCameraExposure());
|
||||
camFolder.add(this.params, 'shutterSpeed', 0.05, 1000.0).onChange(() => this.updateCameraExposure());
|
||||
camFolder.add(this.params, 'shutterSpeed', 1.0, 1000.0).onChange(() => this.updateCameraExposure());
|
||||
camFolder.add(this.params, 'iso', 50.0, 3200.0).onChange(() => this.updateCameraExposure());
|
||||
|
||||
const bloomFolder = camFolder.addFolder('Bloom');
|
||||
@@ -698,15 +692,13 @@ class App {
|
||||
const s = this.sParams;
|
||||
const b = this.bParams;
|
||||
const m = this.mParams;
|
||||
const mw = this.mwParams;
|
||||
const sk = this.skybox;
|
||||
|
||||
return {
|
||||
p: { a: p.aperture, ss: p.shutterSpeed, i: p.iso, st: p.sunTheta, sp: p.sunPhi, fl: p.focalLength, si: p.sunIntensity },
|
||||
c: { v: c.volumetrics, co: c.coverage, d: c.density, h: c.height, s: c.speed, e: c.evolution },
|
||||
w: { dt: w.derivativeTrick, st: w.strength, s: w.speed, o: w.octaves },
|
||||
s: { e: s.enabled, d: s.density, i: s.intensity },
|
||||
mw: { e: mw.enabled, i: mw.intensity, s: mw.saturation, bp: mw.blackPoint, st: mw.siderealTime, l: mw.latitude },
|
||||
s: { e: s.enabled, d: s.density },
|
||||
b: { e: b.enabled, lf: b.lensFlare },
|
||||
m: { e: m.enabled, az: m.azimuth, h: m.height, r: m.radius, i: m.intensity },
|
||||
cm: { t: this.camState.theta, p: this.camState.phi },
|
||||
@@ -731,7 +723,6 @@ class App {
|
||||
const c = state.c;
|
||||
const w = state.w;
|
||||
const s = state.s;
|
||||
const mw = state.mw;
|
||||
const b = state.b;
|
||||
const m = state.m;
|
||||
const k = state.k;
|
||||
@@ -766,18 +757,6 @@ class App {
|
||||
if (s) {
|
||||
if (s.e !== undefined) this.sParams.enabled = s.e;
|
||||
if (s.d !== undefined) this.sParams.density = s.d;
|
||||
if (s.i !== undefined) this.sParams.intensity = s.i;
|
||||
if (this.updateStars) this.updateStars();
|
||||
}
|
||||
|
||||
if (mw) {
|
||||
if (mw.e !== undefined) this.mwParams.enabled = mw.e;
|
||||
if (mw.i !== undefined) this.mwParams.intensity = mw.i;
|
||||
if (mw.s !== undefined) this.mwParams.saturation = mw.s;
|
||||
if (mw.bp !== undefined) this.mwParams.blackPoint = mw.bp;
|
||||
if (mw.st !== undefined) this.mwParams.siderealTime = mw.st;
|
||||
if (mw.l !== undefined) this.mwParams.latitude = mw.l;
|
||||
if (this.updateMW) this.updateMW();
|
||||
}
|
||||
|
||||
if (b) {
|
||||
|
||||
@@ -96,20 +96,10 @@ material {
|
||||
name : starControl, // x=Density, y=Enabled, z=Frequency, w=PixelScale
|
||||
precision : high
|
||||
},
|
||||
{
|
||||
type : float,
|
||||
name : starIntensity,
|
||||
precision : high
|
||||
},
|
||||
{
|
||||
type : sampler2d,
|
||||
name : moonTexture
|
||||
},
|
||||
{
|
||||
type : float,
|
||||
name : exposure,
|
||||
precision : high
|
||||
},
|
||||
{
|
||||
type : sampler2d,
|
||||
name : moonNormal
|
||||
@@ -172,9 +162,9 @@ fragment {
|
||||
// --- CONFIGURATION ---
|
||||
|
||||
// Stars
|
||||
#define STAR_GLOBAL_INTENSITY 100.0 // Master brightness multiplier [0.0 - 500.0]
|
||||
#define STAR_BRIGHTNESS_BASE 0.01 // Minimum random brightness [0.0 - 1.0]
|
||||
#define STAR_BRIGHTNESS_VAR 15.0 // Random brightness variance range [0.0 - 10.0]
|
||||
#define STAR_GLOBAL_INTENSITY 150.0 // Master brightness multiplier [0.0 - 500.0]
|
||||
#define STAR_BRIGHTNESS_BASE 0.5 // Minimum random brightness [0.0 - 1.0]
|
||||
#define STAR_BRIGHTNESS_VAR 4.0 // Random brightness variance range [0.0 - 10.0]
|
||||
#define STAR_FADE_SUN_ELV_HIGH 0.10 // Sun elevation (sin) where stars are fully hidden [0.0 - 0.5]
|
||||
#define STAR_FADE_SUN_ELV_LOW -0.20 // Sun elevation (sin) where stars are fully visible [-0.5 - 0.0]
|
||||
#define STAR_CLOUD_OCCLUSION 0.1 // Visibility when covered by clouds [0.0 - 1.0]
|
||||
@@ -971,11 +961,7 @@ fragment {
|
||||
highp vec4 sunHalo, highp vec4 cloudControl, highp vec4 cloudControl2,
|
||||
highp vec4 shimmerControl, highp vec4 waterControl,
|
||||
highp vec3 L2, highp float sunIntensity2, highp vec4 sunHalo2,
|
||||
sampler2D moonTex, sampler2D moonNormal,
|
||||
highp vec3 nightColor,
|
||||
highp mat3 milkyWayRotation,
|
||||
highp vec3 milkyWayControl,
|
||||
sampler2D milkyWayTexture) {
|
||||
sampler2D moonTex, sampler2D moonNormal) {
|
||||
|
||||
// Project to plane y=0
|
||||
highp float t = WATER_PLANE_HEIGHT / min(V.y, -0.0002); // Reduced clamp to minimize "wall" artifact
|
||||
@@ -1071,7 +1057,7 @@ fragment {
|
||||
highp float rHorizonMask = 1.0 - smoothstep(0.0, REFLECTION_HORIZON_FADE, R.y);
|
||||
|
||||
if (rHorizonMask > 0.0) {
|
||||
reflection += getStarLayer(R, L, reflCloudDensity, outTransmittance, materialParams.starControl) * rHorizonMask * materialParams.exposure;
|
||||
reflection += getStarLayer(R, L, reflCloudDensity, outTransmittance, materialParams.starControl) * rHorizonMask;
|
||||
}
|
||||
|
||||
// Sun Disk Reflection
|
||||
@@ -1102,29 +1088,6 @@ fragment {
|
||||
// Apply clouds to reflection
|
||||
reflection = mix(reflection, reflCloudLayer, reflCloudDensity);
|
||||
|
||||
// Add Milky Way to Reflection
|
||||
// Calculate Fade based on Sun Elevation
|
||||
highp float sunElvSin = L.y;
|
||||
highp float mwFade = smoothstep(STAR_FADE_SUN_ELV_HIGH, STAR_FADE_SUN_ELV_LOW, sunElvSin);
|
||||
|
||||
if (mwFade > 0.0) {
|
||||
highp vec3 mwColor = getMilkyWay(R, milkyWayRotation, milkyWayTexture, milkyWayControl);
|
||||
|
||||
// Apply Atmosphere Transmittance (approximate)
|
||||
mwColor *= outTransmittance;
|
||||
|
||||
// Apply Fades
|
||||
mwColor *= mwFade;
|
||||
mwColor *= (1.0 - reflMoonOcclusion); // Occlude by Moon
|
||||
mwColor *= (1.0 - reflCloudDensity); // Occlude by Clouds
|
||||
|
||||
reflection += mwColor;
|
||||
}
|
||||
|
||||
|
||||
// Add Night Color to Reflection
|
||||
reflection += nightColor;
|
||||
|
||||
// Fresnel
|
||||
highp float F0 = WATER_FRESNEL_F0; // Water
|
||||
highp float cosTheta = clamp(dot(-V, N_water), 0.0, 1.0);
|
||||
@@ -1224,7 +1187,7 @@ fragment {
|
||||
|
||||
// 7. Stars
|
||||
// Add stars before clouds (clouds cover stars)
|
||||
highp vec3 starColor = getStarLayer(V, L, cloudDensityVal, transmittance, materialParams.starControl) * materialParams.exposure * materialParams.starIntensity;
|
||||
highp vec3 starColor = getStarLayer(V, L, cloudDensityVal, transmittance, materialParams.starControl);
|
||||
|
||||
// 7b. Milky Way
|
||||
// Add Milky Way behind stars (conceptually) but handled similarly
|
||||
@@ -1269,11 +1232,7 @@ fragment {
|
||||
materialParams.shimmerControl,
|
||||
materialParams.waterControl,
|
||||
L2, materialParams.sunIntensity2, materialParams.sunHalo2,
|
||||
materialParams_moonTexture, materialParams_moonNormal,
|
||||
materialParams.nightColor,
|
||||
materialParams.milkyWayRotation,
|
||||
materialParams.milkyWayControl,
|
||||
materialParams_milkyWayTexture);
|
||||
materialParams_moonTexture, materialParams_moonNormal);
|
||||
color = applyDynamicToneMapping(color, L, materialParams.contrast);
|
||||
}
|
||||
|
||||
|
||||
@@ -578,7 +578,6 @@ if (APPLE OR LINUX)
|
||||
test/Shader.cpp
|
||||
test/SharedShaders.cpp
|
||||
test/Skip.cpp
|
||||
test/test_Autoresolve.cpp
|
||||
test/test_FeedbackLoops.cpp
|
||||
test/test_Blit.cpp
|
||||
test/test_MissingRequiredAttributes.cpp
|
||||
|
||||
@@ -19,169 +19,21 @@
|
||||
|
||||
#include <backend/PixelBufferDescriptor.h>
|
||||
|
||||
#include <math/scalar.h>
|
||||
#include <math/half.h>
|
||||
|
||||
#include <utils/debug.h>
|
||||
#include <utils/Logger.h>
|
||||
|
||||
#include <cstdint>
|
||||
#include <cstring>
|
||||
#include <stddef.h>
|
||||
#include <stdint.h>
|
||||
|
||||
#include <math/scalar.h>
|
||||
|
||||
#include <utils/debug.h>
|
||||
|
||||
namespace filament {
|
||||
namespace backend {
|
||||
|
||||
namespace {
|
||||
|
||||
// Provides an alpha value when expanding 3-channel images to 4-channel.
|
||||
// Also used as a normalization scale when converting between numeric types.
|
||||
template<typename componentType> inline componentType getMaxValue();
|
||||
|
||||
template<> inline constexpr float getMaxValue() { return 1.0f; }
|
||||
template<> inline constexpr int32_t getMaxValue() { return 0x7fffffff; }
|
||||
template<> inline constexpr uint32_t getMaxValue() { return 0xffffffff; }
|
||||
template<> inline constexpr uint16_t getMaxValue() { return 0x3c00; } // 0x3c00 is 1.0 in half-float.
|
||||
template<> inline constexpr uint8_t getMaxValue() { return 0xff; }
|
||||
template<> inline math::half getMaxValue() { return math::half(1.0f); }
|
||||
|
||||
// We use template below to reduce code duplication across the different input/output
|
||||
// type/channle-count permutations. Morever, templates help us reduce the number of conditionals
|
||||
// in the inner-loop of the reshape operation. However, this needs to be a carefully considered
|
||||
// because too many templated params will cause a large binary size increase.
|
||||
|
||||
// Note that we intentionally do not want to expand the template params to include the channel count
|
||||
// because of the size increase.
|
||||
template<typename dstComponentType, bool hasAlpha>
|
||||
void grayscaleFill(dstComponentType* dst, uint8_t, uint8_t) {
|
||||
for (size_t channel = 1; channel < 3; ++channel) {
|
||||
dst[channel] = dst[0];
|
||||
}
|
||||
if constexpr (hasAlpha) {
|
||||
dst[3] = getMaxValue<dstComponentType>();
|
||||
}
|
||||
}
|
||||
|
||||
// Note that we intentionally do not want to expand the template params to include the channel count
|
||||
// because of the size increase.
|
||||
template<typename dstComponentType>
|
||||
inline void maxValFill(dstComponentType* dst, uint8_t srcChannelCount, uint8_t dstChannelCount) {
|
||||
dstComponentType dstMaxValue = getMaxValue<dstComponentType>();
|
||||
for (size_t channel = srcChannelCount; channel < dstChannelCount; ++channel) {
|
||||
dst[channel] = dstMaxValue;
|
||||
}
|
||||
}
|
||||
|
||||
// Converts a n-channel image of UBYTE, INT, UINT, HALF, or FLOAT to a different type.
|
||||
template<typename dstComponentType, typename srcComponentType>
|
||||
void reshapeImageImpl(uint8_t* UTILS_RESTRICT dest, const uint8_t* UTILS_RESTRICT src,
|
||||
size_t srcBytesPerRow, size_t srcChannelCount, size_t dstRowOffset, size_t dstColumnOffset,
|
||||
size_t dstBytesPerRow, size_t dstChannelCount, size_t width, size_t height, bool swizzle) {
|
||||
|
||||
static_assert(!std::is_same_v<dstComponentType, math::half>);
|
||||
const size_t minChannelCount = math::min(srcChannelCount, dstChannelCount);
|
||||
const dstComponentType dstMaxValue = getMaxValue<dstComponentType>();
|
||||
const srcComponentType srcMaxValue = getMaxValue<srcComponentType>();
|
||||
double const mFactor = dstMaxValue / ((double) srcMaxValue);
|
||||
assert_invariant(minChannelCount <= 4);
|
||||
UTILS_ASSUME(minChannelCount <= 4);
|
||||
dest += (dstRowOffset * dstBytesPerRow);
|
||||
|
||||
void (*fill)(dstComponentType*, uint8_t, uint8_t);
|
||||
if (srcChannelCount == 1 && dstChannelCount == 3) {
|
||||
fill = grayscaleFill<dstComponentType, false>;
|
||||
} else if (srcChannelCount == 1 && dstChannelCount == 4) {
|
||||
fill = grayscaleFill<dstComponentType, true>;
|
||||
} else {
|
||||
fill = maxValFill<dstComponentType>;
|
||||
}
|
||||
|
||||
const int inds[4] = { swizzle ? 2 : 0, 1, swizzle ? 0 : 2, 3 };
|
||||
for (size_t row = 0; row < height; ++row) {
|
||||
const srcComponentType* in = (const srcComponentType*) src;
|
||||
dstComponentType* out = (dstComponentType*) dest + (dstColumnOffset * dstChannelCount);
|
||||
for (size_t column = 0; column < width; ++column) {
|
||||
for (uint8_t channel = 0; channel < minChannelCount; ++channel) {
|
||||
if constexpr (std::is_same_v<dstComponentType, srcComponentType>) {
|
||||
out[channel] = in[inds[channel]];
|
||||
} else {
|
||||
// convert to double then clamp and cast to dst type.
|
||||
out[channel] = static_cast<dstComponentType>(std::clamp(
|
||||
in[inds[channel]] * mFactor, 0.0,
|
||||
static_cast<double>(std::numeric_limits<dstComponentType>::max())));
|
||||
}
|
||||
}
|
||||
|
||||
// This will fill in all the channels that are not copied.
|
||||
fill(out, srcChannelCount, dstChannelCount);
|
||||
in += srcChannelCount;
|
||||
out += dstChannelCount;
|
||||
}
|
||||
src += srcBytesPerRow;
|
||||
dest += dstBytesPerRow;
|
||||
}
|
||||
}
|
||||
|
||||
struct UnpackerR11G11B10 {
|
||||
static void unpack(const uint8_t* src, float* out) {
|
||||
uint32_t p;
|
||||
std::memcpy(&p, src, 4);
|
||||
|
||||
using R11 = math::fp<0, 5, 6>;
|
||||
using G11 = math::fp<0, 5, 6>;
|
||||
using B10 = math::fp<0, 5, 5>;
|
||||
|
||||
out[0] = R11::tof(R11(uint16_t((p >> 21) & 0x7FF)));
|
||||
out[1] = G11::tof(G11(uint16_t((p >> 10) & 0x7FF)));
|
||||
out[2] = B10::tof(B10(uint16_t(p & 0x3FF)));
|
||||
}
|
||||
};
|
||||
|
||||
template<typename dstComponentType, typename Unpacker, bool Swizzle>
|
||||
static void reshapeImagePacked(uint8_t* UTILS_RESTRICT dest, const uint8_t* UTILS_RESTRICT src,
|
||||
size_t srcBytesPerRow, size_t srcChannelCount, size_t dstRowOffset, size_t dstColumnOffset,
|
||||
size_t dstBytesPerRow, size_t dstChannelCount, size_t width, size_t height, bool /*swizzle*/) {
|
||||
|
||||
dest += (dstRowOffset * dstBytesPerRow);
|
||||
const dstComponentType dstMaxValue = getMaxValue<dstComponentType>();
|
||||
|
||||
for (size_t row = 0; row < height; ++row) {
|
||||
const uint8_t* inPtr = src;
|
||||
dstComponentType* out = (dstComponentType*) dest + (dstColumnOffset * dstChannelCount);
|
||||
|
||||
for (size_t column = 0; column < width; ++column) {
|
||||
float rgba[4] = {0.0f, 0.0f, 0.0f, 1.0f};
|
||||
Unpacker::unpack(inPtr, rgba);
|
||||
|
||||
if constexpr (Swizzle) {
|
||||
std::swap(rgba[0], rgba[2]);
|
||||
}
|
||||
|
||||
for (size_t c = 0; c < dstChannelCount; ++c) {
|
||||
if constexpr (std::is_same_v<dstComponentType, float>) {
|
||||
out[c] = rgba[c];
|
||||
} else if constexpr (std::is_same_v<dstComponentType, math::half>) {
|
||||
out[c] = math::half(rgba[c]);
|
||||
} else {
|
||||
out[c] = static_cast<dstComponentType>(std::clamp(
|
||||
static_cast<double>(rgba[c]) * static_cast<double>(dstMaxValue),
|
||||
0.0,
|
||||
static_cast<double>(std::numeric_limits<dstComponentType>::max())
|
||||
));
|
||||
}
|
||||
}
|
||||
|
||||
inPtr += 4;
|
||||
out += dstChannelCount;
|
||||
}
|
||||
src += srcBytesPerRow;
|
||||
dest += dstBytesPerRow;
|
||||
}
|
||||
}
|
||||
|
||||
} // anonymous namespace
|
||||
|
||||
class DataReshaper {
|
||||
public:
|
||||
|
||||
@@ -224,9 +76,51 @@ public:
|
||||
}
|
||||
}
|
||||
|
||||
// Converts a n-channel image of UBYTE, INT, UINT, or FLOAT to a different type.
|
||||
template<typename dstComponentType, typename srcComponentType>
|
||||
static void reshapeImage(uint8_t* UTILS_RESTRICT dest, const uint8_t* UTILS_RESTRICT src,
|
||||
size_t srcBytesPerRow,
|
||||
size_t srcChannelCount,
|
||||
size_t dstRowOffset, size_t dstColumnOffset,
|
||||
size_t dstBytesPerRow, size_t dstChannelCount,
|
||||
size_t width, size_t height, bool swizzle) {
|
||||
// TODO: there's a fast-path where memcpy will work but currently not being taken advantage
|
||||
// of.
|
||||
|
||||
const dstComponentType dstMaxValue = getMaxValue<dstComponentType>();
|
||||
const srcComponentType srcMaxValue = getMaxValue<srcComponentType>();
|
||||
const size_t minChannelCount = math::min(srcChannelCount, dstChannelCount);
|
||||
assert_invariant(minChannelCount <= 4);
|
||||
UTILS_ASSUME(minChannelCount <= 4);
|
||||
dest += (dstRowOffset * dstBytesPerRow);
|
||||
const int inds[4] = { swizzle ? 2 : 0, 1, swizzle ? 0 : 2, 3 };
|
||||
for (size_t row = 0; row < height; ++row) {
|
||||
const srcComponentType* in = (const srcComponentType*) src;
|
||||
dstComponentType* out = (dstComponentType*)dest + (dstColumnOffset * dstChannelCount);
|
||||
for (size_t column = 0; column < width; ++column) {
|
||||
for (size_t channel = 0; channel < minChannelCount; ++channel) {
|
||||
if constexpr (std::is_same_v<dstComponentType, srcComponentType>) {
|
||||
out[channel] = in[inds[channel]];
|
||||
} else {
|
||||
// FIXME: beware of overflows in the multiply
|
||||
// FIXME: probably not correct for _INTEGER src/dst
|
||||
out[channel] = in[inds[channel]] * dstMaxValue / srcMaxValue;
|
||||
}
|
||||
}
|
||||
for (size_t channel = srcChannelCount; channel < dstChannelCount; ++channel) {
|
||||
out[channel] = dstMaxValue;
|
||||
}
|
||||
in += srcChannelCount;
|
||||
out += dstChannelCount;
|
||||
}
|
||||
src += srcBytesPerRow;
|
||||
dest += dstBytesPerRow;
|
||||
}
|
||||
}
|
||||
|
||||
// Converts a n-channel image of UBYTE, INT, UINT, or FLOAT to a different type.
|
||||
static bool reshapeImage(PixelBufferDescriptor* UTILS_RESTRICT dst, PixelDataType srcType,
|
||||
uint32_t srcChannelCount, const uint8_t* UTILS_RESTRICT srcBytes, int srcBytesPerRow,
|
||||
uint32_t srcChannelCount, const uint8_t* UTILS_RESTRICT srcBytes, int srcBytesPerRow,
|
||||
int width, int height, bool swizzle) {
|
||||
size_t dstChannelCount;
|
||||
switch (dst->format) {
|
||||
@@ -238,123 +132,87 @@ public:
|
||||
case PixelDataFormat::RG: dstChannelCount = 2; break;
|
||||
case PixelDataFormat::RGB: dstChannelCount = 3; break;
|
||||
case PixelDataFormat::RGBA: dstChannelCount = 4; break;
|
||||
default:
|
||||
LOG(ERROR) << "DataReshaper: unsupported dst->format: " << (int) dst->format;
|
||||
return false;
|
||||
default: return false;
|
||||
}
|
||||
void (*reshaper)(uint8_t* dest, const uint8_t* src, size_t srcBytesPerRow,
|
||||
size_t srcChannelCount, size_t srcRowOffset, size_t srcColumnOffset,
|
||||
size_t dstBytesPerRow, size_t dstChannelCount, size_t width, size_t height,
|
||||
bool swizzle) = nullptr;
|
||||
size_t srcChannelCount,
|
||||
size_t srcRowOffset, size_t srcColumnOffset,
|
||||
size_t dstBytesPerRow, size_t dstChannelCount,
|
||||
size_t width, size_t height, bool swizzle) = nullptr;
|
||||
constexpr auto UBYTE = PixelDataType::UBYTE;
|
||||
constexpr auto FLOAT = PixelDataType::FLOAT;
|
||||
constexpr auto UINT = PixelDataType::UINT;
|
||||
constexpr auto INT = PixelDataType::INT;
|
||||
constexpr auto HALF = PixelDataType::HALF;
|
||||
constexpr auto UINT_10F_11F_11F_REV = PixelDataType::UINT_10F_11F_11F_REV;
|
||||
|
||||
switch (dst->type) {
|
||||
case UBYTE:
|
||||
switch (srcType) {
|
||||
case UBYTE:
|
||||
reshaper = reshapeImageImpl<uint8_t, uint8_t>;
|
||||
reshaper = reshapeImage<uint8_t, uint8_t>;
|
||||
if (dst->format == PixelDataFormat::RGBA &&
|
||||
dstChannelCount == srcChannelCount && !swizzle && dst->top == 0 &&
|
||||
dst->left == 0) {
|
||||
reshaper = copyImage;
|
||||
}
|
||||
break;
|
||||
case FLOAT: reshaper = reshapeImageImpl<uint8_t, float>; break;
|
||||
case INT: reshaper = reshapeImageImpl<uint8_t, int32_t>; break;
|
||||
case UINT: reshaper = reshapeImageImpl<uint8_t, uint32_t>; break;
|
||||
case HALF: reshaper = reshapeImageImpl<uint8_t, math::half>; break;
|
||||
case UINT_10F_11F_11F_REV:
|
||||
if (swizzle) reshaper = reshapeImagePacked<uint8_t, UnpackerR11G11B10, true>;
|
||||
else reshaper = reshapeImagePacked<uint8_t, UnpackerR11G11B10, false>;
|
||||
break;
|
||||
default:
|
||||
LOG(ERROR) << "DataReshaper: UBYTE dst, unsupported srcType: "
|
||||
<< (int) srcType;
|
||||
return false;
|
||||
case FLOAT: reshaper = reshapeImage<uint8_t, float>; break;
|
||||
case INT: reshaper = reshapeImage<uint8_t, int32_t>; break;
|
||||
case UINT: reshaper = reshapeImage<uint8_t, uint32_t>; break;
|
||||
default: return false;
|
||||
}
|
||||
break;
|
||||
case FLOAT:
|
||||
switch (srcType) {
|
||||
case UBYTE: reshaper = reshapeImageImpl<float, uint8_t>; break;
|
||||
case FLOAT: reshaper = reshapeImageImpl<float, float>; break;
|
||||
case INT: reshaper = reshapeImageImpl<float, int32_t>; break;
|
||||
case UINT: reshaper = reshapeImageImpl<float, uint32_t>; break;
|
||||
case UINT_10F_11F_11F_REV:
|
||||
if (swizzle) reshaper = reshapeImagePacked<float, UnpackerR11G11B10, true>;
|
||||
else reshaper = reshapeImagePacked<float, UnpackerR11G11B10, false>;
|
||||
break;
|
||||
default:
|
||||
LOG(ERROR) << "DataReshaper: FLOAT dst, unsupported srcType: "
|
||||
<< (int) srcType;
|
||||
return false;
|
||||
case UBYTE: reshaper = reshapeImage<float, uint8_t>; break;
|
||||
case FLOAT: reshaper = reshapeImage<float, float>; break;
|
||||
case INT: reshaper = reshapeImage<float, int32_t>; break;
|
||||
case UINT: reshaper = reshapeImage<float, uint32_t>; break;
|
||||
default: return false;
|
||||
}
|
||||
break;
|
||||
case INT:
|
||||
switch (srcType) {
|
||||
case UBYTE: reshaper = reshapeImageImpl<int32_t, uint8_t>; break;
|
||||
case FLOAT: reshaper = reshapeImageImpl<int32_t, float>; break;
|
||||
case INT: reshaper = reshapeImageImpl<int32_t, int32_t>; break;
|
||||
case UINT: reshaper = reshapeImageImpl<int32_t, uint32_t>; break;
|
||||
case UINT_10F_11F_11F_REV:
|
||||
if (swizzle) reshaper = reshapeImagePacked<int32_t, UnpackerR11G11B10, true>;
|
||||
else reshaper = reshapeImagePacked<int32_t, UnpackerR11G11B10, false>;
|
||||
break;
|
||||
default:
|
||||
LOG(ERROR)
|
||||
<< "DataReshaper: INT dst, unsupported srcType: " << (int) srcType;
|
||||
return false;
|
||||
case UBYTE: reshaper = reshapeImage<int32_t, uint8_t>; break;
|
||||
case FLOAT: reshaper = reshapeImage<int32_t, float>; break;
|
||||
case INT: reshaper = reshapeImage<int32_t, int32_t>; break;
|
||||
case UINT: reshaper = reshapeImage<int32_t, uint32_t>; break;
|
||||
default: return false;
|
||||
}
|
||||
break;
|
||||
case UINT:
|
||||
switch (srcType) {
|
||||
case UBYTE: reshaper = reshapeImageImpl<uint32_t, uint8_t>; break;
|
||||
case FLOAT: reshaper = reshapeImageImpl<uint32_t, float>; break;
|
||||
case INT: reshaper = reshapeImageImpl<uint32_t, int32_t>; break;
|
||||
case UINT: reshaper = reshapeImageImpl<uint32_t, uint32_t>; break;
|
||||
case UINT_10F_11F_11F_REV:
|
||||
if (swizzle) reshaper = reshapeImagePacked<uint32_t, UnpackerR11G11B10, true>;
|
||||
else reshaper = reshapeImagePacked<uint32_t, UnpackerR11G11B10, false>;
|
||||
break;
|
||||
default:
|
||||
LOG(ERROR)
|
||||
<< "DataReshaper: UINT dst, unsupported srcType: " << (int) srcType;
|
||||
return false;
|
||||
case UBYTE: reshaper = reshapeImage<uint32_t, uint8_t>; break;
|
||||
case FLOAT: reshaper = reshapeImage<uint32_t, float>; break;
|
||||
case INT: reshaper = reshapeImage<uint32_t, int32_t>; break;
|
||||
case UINT: reshaper = reshapeImage<uint32_t, uint32_t>; break;
|
||||
default: return false;
|
||||
}
|
||||
break;
|
||||
case HALF:
|
||||
switch (srcType) {
|
||||
case HALF:
|
||||
reshaper = copyImage;
|
||||
break;
|
||||
case UINT_10F_11F_11F_REV:
|
||||
if (swizzle) reshaper = reshapeImagePacked<math::half, UnpackerR11G11B10, true>;
|
||||
else reshaper = reshapeImagePacked<math::half, UnpackerR11G11B10, false>;
|
||||
break;
|
||||
default:
|
||||
LOG(ERROR)
|
||||
<< "DataReshaper: HALF dst, unsupported srcType: " << (int) srcType;
|
||||
return false;
|
||||
case HALF: reshaper = copyImage; break;
|
||||
default: return false;
|
||||
}
|
||||
break;
|
||||
default:
|
||||
LOG(ERROR) << "DataReshaper: unsupported dst->type: " << (int) dst->type;
|
||||
return false;
|
||||
}
|
||||
uint8_t* dstBytes = (uint8_t*) dst->buffer;
|
||||
const int dstBytesPerRow = PixelBufferDescriptor::computeDataSize(dst->format, dst->type,
|
||||
dst->stride ? dst->stride : width, 1, dst->alignment);
|
||||
reshaper(dstBytes, srcBytes, srcBytesPerRow, srcChannelCount, dst->top, dst->left,
|
||||
dstBytesPerRow, dstChannelCount, width, height, swizzle);
|
||||
|
||||
reshaper(dstBytes, srcBytes, srcBytesPerRow, srcChannelCount,
|
||||
dst->top, dst->left, dstBytesPerRow,
|
||||
dstChannelCount, width, height, swizzle);
|
||||
return true;
|
||||
}
|
||||
};
|
||||
|
||||
template<> inline float getMaxValue() { return 1.0f; }
|
||||
template<> inline int32_t getMaxValue() { return 0x7fffffff; }
|
||||
template<> inline uint32_t getMaxValue() { return 0xffffffff; }
|
||||
template<> inline uint16_t getMaxValue() { return 0x3c00; } // 0x3c00 is 1.0 in half-float.
|
||||
template<> inline uint8_t getMaxValue() { return 0xff; }
|
||||
|
||||
} // namespace backend
|
||||
} // namespace filament
|
||||
|
||||
|
||||
@@ -1092,7 +1092,7 @@ MetalRenderTarget::MetalRenderTarget(MetalContext* context, uint32_t width, uint
|
||||
// a multisampled sidecar texture and do a resolve automatically.
|
||||
if (samples > 1 && texture->samples == 1) {
|
||||
auto& sidecar = texture->msaaSidecar;
|
||||
if (!sidecar || sidecar.sampleCount != samples) {
|
||||
if (!sidecar) {
|
||||
sidecar = createMultisampledTexture(mtlTexture.pixelFormat, texture->width,
|
||||
texture->height, samples);
|
||||
}
|
||||
@@ -1123,7 +1123,7 @@ MetalRenderTarget::MetalRenderTarget(MetalContext* context, uint32_t width, uint
|
||||
// a multisampled sidecar texture and do a resolve automatically.
|
||||
if (samples > 1 && texture->samples == 1) {
|
||||
auto& sidecar = texture->msaaSidecar;
|
||||
if (!sidecar || sidecar.sampleCount != samples) {
|
||||
if (!sidecar) {
|
||||
sidecar = createMultisampledTexture(mtlTexture.pixelFormat, texture->width,
|
||||
texture->height, samples);
|
||||
}
|
||||
@@ -1155,7 +1155,7 @@ MetalRenderTarget::MetalRenderTarget(MetalContext* context, uint32_t width, uint
|
||||
// a multisampled sidecar texture and do a resolve automatically.
|
||||
if (samples > 1 && texture->samples == 1) {
|
||||
auto& sidecar = texture->msaaSidecar;
|
||||
if (!sidecar || sidecar.sampleCount != samples) {
|
||||
if (!sidecar) {
|
||||
sidecar = createMultisampledTexture(mtlTexture.pixelFormat, texture->width,
|
||||
texture->height, samples);
|
||||
}
|
||||
|
||||
@@ -91,7 +91,7 @@ public:
|
||||
GLenum getIndicesType() const noexcept {
|
||||
return indicesType;
|
||||
}
|
||||
};
|
||||
} gl;
|
||||
|
||||
static bool queryOpenGLVersion(GLint* major, GLint* minor) noexcept;
|
||||
|
||||
|
||||
@@ -3623,8 +3623,6 @@ void OpenGLDriver::detachStream(GLTexture* t) noexcept {
|
||||
case StreamType::NATIVE:
|
||||
mPlatform.detach(t->hwStream->stream);
|
||||
// ^ this deletes the texture id
|
||||
// We still need to call unbind to update the bookkeeping.
|
||||
gl.unbindTexture(t->gl.target, t->gl.id);
|
||||
break;
|
||||
case StreamType::ACQUIRED:
|
||||
gl.unbindTexture(t->gl.target, t->gl.id);
|
||||
|
||||
@@ -120,27 +120,33 @@ bool PlatformEGL::isOpenGL() const noexcept {
|
||||
PlatformEGL::ExternalImageEGL::~ExternalImageEGL() = default;
|
||||
|
||||
Driver* PlatformEGL::createDriver(void* sharedContext, const DriverConfig& driverConfig) {
|
||||
mEGLDisplay = eglGetDisplay(EGL_DEFAULT_DISPLAY);
|
||||
assert_invariant(mEGLDisplay != EGL_NO_DISPLAY);
|
||||
static constexpr int kMaxNumEGLDevices = 32;
|
||||
|
||||
EGLint major, minor;
|
||||
EGLBoolean initialized = eglInitialize(mEGLDisplay, &major, &minor);
|
||||
EGLBoolean initialized = false;
|
||||
|
||||
if (!initialized) {
|
||||
EGLDeviceEXT eglDevice;
|
||||
EGLint numDevices;
|
||||
PFNEGLQUERYDEVICESEXTPROC const eglQueryDevicesEXT =
|
||||
PFNEGLQUERYDEVICESEXTPROC(eglGetProcAddress("eglQueryDevicesEXT"));
|
||||
if (eglQueryDevicesEXT != nullptr) {
|
||||
eglQueryDevicesEXT(1, &eglDevice, &numDevices);
|
||||
if(auto* getPlatformDisplay = reinterpret_cast<PFNEGLGETPLATFORMDISPLAYEXTPROC>(
|
||||
eglGetProcAddress("eglGetPlatformDisplay"))) {
|
||||
mEGLDisplay = getPlatformDisplay(EGL_PLATFORM_DEVICE_EXT, eglDevice, nullptr);
|
||||
PFNEGLQUERYDEVICESEXTPROC const eglQueryDevicesEXT =
|
||||
PFNEGLQUERYDEVICESEXTPROC(eglGetProcAddress("eglQueryDevicesEXT"));
|
||||
PFNEGLGETPLATFORMDISPLAYEXTPROC const getPlatformDisplay =
|
||||
PFNEGLGETPLATFORMDISPLAYEXTPROC(eglGetProcAddress("eglGetPlatformDisplay"));
|
||||
|
||||
if (eglQueryDevicesEXT != nullptr && getPlatformDisplay != nullptr) {
|
||||
EGLint numDevices = 0;
|
||||
EGLDeviceEXT eglDevices[kMaxNumEGLDevices];
|
||||
if (eglQueryDevicesEXT(kMaxNumEGLDevices, eglDevices, &numDevices)) {
|
||||
for (int i = 0; i < numDevices && !initialized; ++i) {
|
||||
mEGLDisplay = getPlatformDisplay(EGL_PLATFORM_DEVICE_EXT, eglDevices[i], nullptr);
|
||||
initialized = eglInitialize(mEGLDisplay, &major, &minor);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (!initialized) {
|
||||
mEGLDisplay = eglGetDisplay(EGL_DEFAULT_DISPLAY);
|
||||
assert_invariant(mEGLDisplay != EGL_NO_DISPLAY);
|
||||
initialized = eglInitialize(mEGLDisplay, &major, &minor);
|
||||
}
|
||||
|
||||
if (UTILS_UNLIKELY(!initialized)) {
|
||||
LOG(ERROR) << "eglInitialize failed";
|
||||
return nullptr;
|
||||
|
||||
@@ -36,7 +36,6 @@ VK_DEFINE_HANDLE(VmaPool)
|
||||
namespace filament::backend {
|
||||
|
||||
struct VulkanCommandBuffer;
|
||||
struct VulkanRenderPass;
|
||||
struct VulkanRenderTarget;
|
||||
struct VulkanSwapChain;
|
||||
struct VulkanTexture;
|
||||
@@ -59,11 +58,11 @@ struct VulkanAttachment {
|
||||
VkImageSubresourceRange getSubresourceRange() const;
|
||||
};
|
||||
|
||||
struct VulkanRenderPassContext {
|
||||
struct VulkanRenderPass {
|
||||
// Between the begin and end command render pass we cache the command buffer
|
||||
VulkanCommandBuffer* commandBuffer;
|
||||
fvkmemory::resource_ptr<VulkanRenderTarget> renderTarget;
|
||||
fvkmemory::resource_ptr<VulkanRenderPass> renderPass;
|
||||
VkRenderPass renderPass;
|
||||
RenderPassParams params;
|
||||
int currentSubpass;
|
||||
};
|
||||
|
||||
@@ -1975,19 +1975,17 @@ void VulkanDriver::beginRenderPass(Handle<HwRenderTarget> rth, const RenderPassP
|
||||
rpkey.initialDepthLayout = currentDepthLayout;
|
||||
rpkey.subpassMask = uint8_t(params.subpassMask);
|
||||
|
||||
fvkmemory::resource_ptr<VulkanRenderPass> renderPass =
|
||||
mFramebufferCache.getRenderPass(rpkey, &mResourceManager);
|
||||
VkRenderPass renderPass = mFramebufferCache.getRenderPass(rpkey);
|
||||
mPipelineCache.bindRenderPass(renderPass, 0);
|
||||
|
||||
// Create the VkFramebuffer or fetch it from cache.
|
||||
VulkanFboCache::FboKey fbkey = rt->getFboKey();
|
||||
fbkey.renderPass = renderPass->getVkRenderPass();
|
||||
fbkey.renderPass = renderPass;
|
||||
fbkey.layers = 1;
|
||||
|
||||
rt->emitBarriersBeginRenderPass(*commandBuffer);
|
||||
|
||||
fvkmemory::resource_ptr<VulkanFramebuffer> vkfb =
|
||||
mFramebufferCache.getFramebuffer(fbkey, &mResourceManager);
|
||||
VkFramebuffer vkfb = mFramebufferCache.getFramebuffer(fbkey);
|
||||
|
||||
// Assign a label to the framebuffer for debugging purposes.
|
||||
#if FVK_ENABLED(FVK_DEBUG_GROUP_MARKERS | FVK_DEBUG_DEBUG_UTILS)
|
||||
@@ -2000,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.
|
||||
commandBuffer->acquire(rt);
|
||||
commandBuffer->acquire(renderPass);
|
||||
commandBuffer->acquire(vkfb);
|
||||
|
||||
// Populate the structures required for vkCmdBeginRenderPass.
|
||||
VkRenderPassBeginInfo renderPassInfo {
|
||||
.sType = VK_STRUCTURE_TYPE_RENDER_PASS_BEGIN_INFO,
|
||||
.renderPass = renderPass->getVkRenderPass(),
|
||||
.framebuffer = vkfb->getVkFramebuffer(),
|
||||
.renderPass = renderPass,
|
||||
.framebuffer = vkfb,
|
||||
|
||||
// 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().
|
||||
@@ -2062,7 +2058,7 @@ void VulkanDriver::beginRenderPass(Handle<HwRenderTarget> rth, const RenderPassP
|
||||
mCurrentRenderPass = {
|
||||
.commandBuffer = commandBuffer,
|
||||
.renderTarget = rt,
|
||||
.renderPass = renderPass,
|
||||
.renderPass = renderPassInfo.renderPass,
|
||||
.params = params,
|
||||
.currentSubpass = 0,
|
||||
};
|
||||
@@ -2082,7 +2078,8 @@ void VulkanDriver::endRenderPass(int) {
|
||||
rt->emitBarriersEndRenderPass(*mCurrentRenderPass.commandBuffer);
|
||||
|
||||
mCurrentRenderPass.renderTarget = {};
|
||||
mCurrentRenderPass.renderPass = {};
|
||||
mCurrentRenderPass.renderPass = VK_NULL_HANDLE;
|
||||
|
||||
mCurrentRenderPass.commandBuffer = nullptr;
|
||||
}
|
||||
|
||||
@@ -2247,7 +2244,7 @@ void VulkanDriver::resolve(
|
||||
Handle<HwTexture> src, uint8_t dstLevel, uint8_t dstLayer) {
|
||||
FVK_SYSTRACE_SCOPE();
|
||||
|
||||
FILAMENT_CHECK_PRECONDITION(!mCurrentRenderPass.renderPass)
|
||||
FILAMENT_CHECK_PRECONDITION(mCurrentRenderPass.renderPass == VK_NULL_HANDLE)
|
||||
<< "resolve() cannot be invoked inside a render pass.";
|
||||
|
||||
auto srcTexture = resource_ptr<VulkanTexture>::cast(&mResourceManager, src);
|
||||
@@ -2290,7 +2287,7 @@ void VulkanDriver::blit(
|
||||
math::uint2 size) {
|
||||
FVK_SYSTRACE_SCOPE();
|
||||
|
||||
FILAMENT_CHECK_PRECONDITION(!mCurrentRenderPass.renderPass)
|
||||
FILAMENT_CHECK_PRECONDITION(mCurrentRenderPass.renderPass == VK_NULL_HANDLE)
|
||||
<< "blit() cannot be invoked inside a render pass.";
|
||||
|
||||
auto srcTexture = resource_ptr<VulkanTexture>::cast(&mResourceManager, src);
|
||||
@@ -2332,7 +2329,7 @@ void VulkanDriver::blitDEPRECATED(TargetBufferFlags buffers,
|
||||
|
||||
// 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.";
|
||||
|
||||
FILAMENT_CHECK_PRECONDITION(buffers == TargetBufferFlags::COLOR0)
|
||||
@@ -2484,7 +2481,6 @@ void VulkanDriver::bindPipelineImpl(PipelineState const& pipelineState,
|
||||
// Push state changes to the VulkanPipelineCache instance. This is fast and does not make VK calls.
|
||||
mPipelineCache.bindProgram(program);
|
||||
mPipelineCache.bindRasterState(vulkanRasterState);
|
||||
mPipelineCache.bindStencilState(pipelineState.stencilState);
|
||||
mPipelineCache.bindPrimitiveTopology(topology);
|
||||
mPipelineCache.bindVertexArray(attribDesc, bufferDesc, vbi->getAttributeCount());
|
||||
|
||||
|
||||
@@ -139,7 +139,7 @@ private:
|
||||
|
||||
resource_ptr<VulkanSwapChain> mCurrentSwapChain;
|
||||
resource_ptr<VulkanRenderTarget> mDefaultRenderTarget;
|
||||
VulkanRenderPassContext mCurrentRenderPass = {};
|
||||
VulkanRenderPass mCurrentRenderPass = {};
|
||||
VmaAllocator mAllocator = VK_NULL_HANDLE;
|
||||
VkDebugReportCallbackEXT mDebugCallback = VK_NULL_HANDLE;
|
||||
|
||||
|
||||
@@ -17,7 +17,6 @@
|
||||
#include "VulkanFboCache.h"
|
||||
|
||||
#include "VulkanConstants.h"
|
||||
#include "VulkanHandles.h"
|
||||
#include "vulkan/utils/Image.h"
|
||||
|
||||
#include <utils/Panic.h>
|
||||
@@ -70,10 +69,9 @@ VulkanFboCache::~VulkanFboCache() {
|
||||
<< "Please explicitly call terminate() while the VkDevice is still alive.";
|
||||
}
|
||||
|
||||
fvkmemory::resource_ptr<VulkanFramebuffer> VulkanFboCache::getFramebuffer(
|
||||
FboKey const& config, fvkmemory::ResourceManager* resManager) noexcept {
|
||||
VkFramebuffer VulkanFboCache::getFramebuffer(FboKey const& config) noexcept {
|
||||
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;
|
||||
return iter->second.handle;
|
||||
}
|
||||
@@ -119,16 +117,13 @@ fvkmemory::resource_ptr<VulkanFramebuffer> VulkanFboCache::getFramebuffer(
|
||||
VkResult error = vkCreateFramebuffer(mDevice, &info, VKALLOC, &framebuffer);
|
||||
FILAMENT_CHECK_POSTCONDITION(error == VK_SUCCESS) << "Unable to create framebuffer."
|
||||
<< " error=" << static_cast<int32_t>(error);
|
||||
fvkmemory::resource_ptr<VulkanFramebuffer> fbh =
|
||||
fvkmemory::resource_ptr<VulkanFramebuffer>::construct(resManager, mDevice, framebuffer);
|
||||
mFramebufferCache[config] = {fbh, mCurrentTime};
|
||||
return fbh;
|
||||
mFramebufferCache[config] = {framebuffer, mCurrentTime};
|
||||
return framebuffer;
|
||||
}
|
||||
|
||||
fvkmemory::resource_ptr<VulkanRenderPass> VulkanFboCache::getRenderPass(
|
||||
RenderPassKey const& config, fvkmemory::ResourceManager* resManager) noexcept {
|
||||
VkRenderPass VulkanFboCache::getRenderPass(RenderPassKey const& config) noexcept {
|
||||
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;
|
||||
return iter->second.handle;
|
||||
}
|
||||
@@ -331,9 +326,7 @@ fvkmemory::resource_ptr<VulkanRenderPass> VulkanFboCache::getRenderPass(
|
||||
VkResult error = vkCreateRenderPass(mDevice, &renderPassInfo, VKALLOC, &renderPass);
|
||||
FILAMENT_CHECK_POSTCONDITION(error == VK_SUCCESS) << "Unable to create render pass."
|
||||
<< " error=" << error;
|
||||
fvkmemory::resource_ptr<VulkanRenderPass> rph =
|
||||
fvkmemory::resource_ptr<VulkanRenderPass>::construct(resManager, mDevice, renderPass);
|
||||
mRenderPassCache[config] = {rph, mCurrentTime};
|
||||
mRenderPassCache[config] = {renderPass, mCurrentTime};
|
||||
|
||||
#if FVK_ENABLED(FVK_DEBUG_FBO_CACHE)
|
||||
FVK_LOGD << "Created render pass " << renderPass << " with ";
|
||||
@@ -350,12 +343,13 @@ fvkmemory::resource_ptr<VulkanRenderPass> VulkanFboCache::getRenderPass(
|
||||
<< "colorAttachmentCount[0] = " << subpasses[0].colorAttachmentCount;
|
||||
#endif
|
||||
|
||||
return rph;
|
||||
return renderPass;
|
||||
}
|
||||
|
||||
void VulkanFboCache::resetFramebuffers() noexcept {
|
||||
for (const auto& pair: mFramebufferCache) {
|
||||
mRenderPassRefCount[pair.first.renderPass]--;
|
||||
vkDestroyFramebuffer(mDevice, pair.second.handle, VKALLOC);
|
||||
}
|
||||
mFramebufferCache.clear();
|
||||
}
|
||||
@@ -363,6 +357,9 @@ void VulkanFboCache::resetFramebuffers() noexcept {
|
||||
void VulkanFboCache::terminate() noexcept {
|
||||
resetFramebuffers();
|
||||
|
||||
for (const auto& pair: mRenderPassCache) {
|
||||
vkDestroyRenderPass(mDevice, pair.second.handle, VKALLOC);
|
||||
}
|
||||
mRenderPassRefCount.clear();
|
||||
mRenderPassCache.clear();
|
||||
}
|
||||
@@ -383,6 +380,8 @@ void VulkanFboCache::gc() noexcept {
|
||||
const FboVal fbo = iter->second;
|
||||
if (fbo.timestamp < evictTime && fbo.handle) {
|
||||
mRenderPassRefCount[iter->first.renderPass]--;
|
||||
vkDestroyFramebuffer(mDevice, fbo.handle, VKALLOC);
|
||||
iter.value().handle = VK_NULL_HANDLE;
|
||||
|
||||
// erase(iterator) returns the iterator to the next element.
|
||||
iter = mFramebufferCache.erase(iter);
|
||||
@@ -392,8 +391,11 @@ void VulkanFboCache::gc() noexcept {
|
||||
}
|
||||
|
||||
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) {
|
||||
vkDestroyRenderPass(mDevice, handle, VKALLOC);
|
||||
iter.value().handle = VK_NULL_HANDLE;
|
||||
|
||||
// erase(iterator) returns the iterator to the next element.
|
||||
iter = mRenderPassCache.erase(iter);
|
||||
mRenderPassRefCount.erase(handle);
|
||||
|
||||
@@ -18,9 +18,6 @@
|
||||
#define TNT_FILAMENT_BACKEND_VULKANFBOCACHE_H
|
||||
|
||||
#include "VulkanContext.h"
|
||||
#include "vulkan/memory/Resource.h"
|
||||
#include "vulkan/memory/ResourceManager.h"
|
||||
#include "vulkan/memory/ResourcePointer.h"
|
||||
|
||||
#include <utils/Hash.h>
|
||||
|
||||
@@ -30,9 +27,6 @@
|
||||
|
||||
namespace filament::backend {
|
||||
|
||||
struct VulkanFramebuffer;
|
||||
struct VulkanRenderPass;
|
||||
|
||||
// Simple manager for VkFramebuffer and VkRenderPass objects.
|
||||
//
|
||||
// Note that a VkFramebuffer is just a binding between a render pass and a set of image views. So,
|
||||
@@ -63,7 +57,7 @@ public:
|
||||
uint8_t padding[2];
|
||||
};
|
||||
struct RenderPassVal {
|
||||
fvkmemory::resource_ptr<VulkanRenderPass> handle;
|
||||
VkRenderPass handle;
|
||||
uint32_t timestamp;
|
||||
};
|
||||
static_assert(0 == MRT::MAX_SUPPORTED_RENDER_TARGET_COUNT % 8);
|
||||
@@ -89,7 +83,7 @@ public:
|
||||
VkImageView depth; // 8 bytes
|
||||
};
|
||||
struct FboVal {
|
||||
fvkmemory::resource_ptr<VulkanFramebuffer> handle;
|
||||
VkFramebuffer handle;
|
||||
uint32_t timestamp;
|
||||
};
|
||||
static_assert(sizeof(VkRenderPass) == 8, "VkRenderPass has unexpected size.");
|
||||
@@ -104,12 +98,10 @@ public:
|
||||
~VulkanFboCache();
|
||||
|
||||
// Retrieves or creates a VkFramebuffer handle.
|
||||
fvkmemory::resource_ptr<VulkanFramebuffer> getFramebuffer(
|
||||
FboKey const& config, fvkmemory::ResourceManager* resManager) noexcept;
|
||||
VkFramebuffer getFramebuffer(FboKey const& config) noexcept;
|
||||
|
||||
// Retrieves or creates a VkRenderPass handle.
|
||||
fvkmemory::resource_ptr<VulkanRenderPass> getRenderPass(
|
||||
RenderPassKey const& config, fvkmemory::ResourceManager* resManager) noexcept;
|
||||
VkRenderPass getRenderPass(RenderPassKey const& config) noexcept;
|
||||
|
||||
// Evicts old unused Vulkan objects. Call this once per frame.
|
||||
void gc() noexcept;
|
||||
|
||||
@@ -565,7 +565,7 @@ void VulkanRenderTarget::transformViewportToPlatform(VkViewport* bounds) const {
|
||||
flipVertically(bounds, getExtent().height);
|
||||
}
|
||||
|
||||
uint8_t VulkanRenderTarget::getColorTargetCount(const VulkanRenderPassContext& pass) const {
|
||||
uint8_t VulkanRenderTarget::getColorTargetCount(const VulkanRenderPass& pass) const {
|
||||
if (!mOffscreen) {
|
||||
return 1;
|
||||
}
|
||||
@@ -715,18 +715,4 @@ VulkanRenderPrimitive::VulkanRenderPrimitive(PrimitiveType pt,
|
||||
vertexBuffer(vb),
|
||||
indexBuffer(ib) {}
|
||||
|
||||
VulkanFramebuffer::VulkanFramebuffer(VkDevice device, VkFramebuffer framebuffer)
|
||||
: mDevice(device), mFramebuffer(framebuffer) {}
|
||||
|
||||
VulkanFramebuffer::~VulkanFramebuffer() {
|
||||
vkDestroyFramebuffer(mDevice, mFramebuffer, VKALLOC);
|
||||
}
|
||||
|
||||
VulkanRenderPass::VulkanRenderPass(VkDevice device, VkRenderPass renderPass)
|
||||
: mDevice(device), mRenderPass(renderPass) {}
|
||||
|
||||
VulkanRenderPass::~VulkanRenderPass() {
|
||||
vkDestroyRenderPass(mDevice, mRenderPass, VKALLOC);
|
||||
}
|
||||
|
||||
} // namespace filament::backend
|
||||
|
||||
@@ -419,7 +419,7 @@ struct VulkanRenderTarget : private HwRenderTarget, fvkmemory::Resource {
|
||||
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; }
|
||||
|
||||
@@ -585,32 +585,6 @@ struct VulkanRenderPrimitive : public HwRenderPrimitive, fvkmemory::Resource {
|
||||
fvkmemory::resource_ptr<VulkanIndexBuffer> indexBuffer;
|
||||
};
|
||||
|
||||
struct VulkanFramebuffer : public fvkmemory::Resource {
|
||||
VulkanFramebuffer(VkDevice device, VkFramebuffer framebuffer);
|
||||
~VulkanFramebuffer();
|
||||
|
||||
inline VkFramebuffer getVkFramebuffer() const noexcept {
|
||||
return mFramebuffer;
|
||||
}
|
||||
|
||||
private:
|
||||
VkDevice mDevice;
|
||||
VkFramebuffer mFramebuffer;
|
||||
};
|
||||
|
||||
struct VulkanRenderPass : public fvkmemory::Resource {
|
||||
VulkanRenderPass(VkDevice device, VkRenderPass renderPass);
|
||||
~VulkanRenderPass();
|
||||
|
||||
inline VkRenderPass getVkRenderPass() const noexcept {
|
||||
return mRenderPass;
|
||||
}
|
||||
|
||||
private:
|
||||
VkDevice mDevice;
|
||||
VkRenderPass mRenderPass;
|
||||
};
|
||||
|
||||
} // namespace filament::backend
|
||||
|
||||
#endif // TNT_FILAMENT_BACKEND_VULKANHANDLES_H
|
||||
|
||||
@@ -166,7 +166,6 @@ void VulkanPipelineCache::asyncPrewarmCache(
|
||||
.depthBiasConstantFactor = 0.f,
|
||||
.depthBiasSlopeFactor = 0.f,
|
||||
},
|
||||
.stencilState = {},
|
||||
.layout = layout,
|
||||
};
|
||||
PipelineDynamicOptions dynamicOptions {
|
||||
@@ -298,45 +297,24 @@ VkPipeline VulkanPipelineCache::createPipeline(
|
||||
bool const enableDepthTest =
|
||||
raster.depthCompareOp != SamplerCompareFunc::A ||
|
||||
raster.depthWriteEnable;
|
||||
// Stencil must be enabled if we're testing OR writing to the stencil buffer.
|
||||
auto const& stencil = key.stencilState;
|
||||
bool const enableStencilTest =
|
||||
stencil.front.stencilFunc != StencilState::StencilFunction::A ||
|
||||
stencil.back.stencilFunc != StencilState::StencilFunction::A ||
|
||||
stencil.front.stencilOpDepthFail != StencilOperation::KEEP ||
|
||||
stencil.back.stencilOpDepthFail != StencilOperation::KEEP ||
|
||||
stencil.front.stencilOpStencilFail != StencilOperation::KEEP ||
|
||||
stencil.back.stencilOpStencilFail != StencilOperation::KEEP ||
|
||||
stencil.front.stencilOpDepthStencilPass != StencilOperation::KEEP ||
|
||||
stencil.back.stencilOpDepthStencilPass != StencilOperation::KEEP ||
|
||||
stencil.stencilWrite;
|
||||
VkPipelineDepthStencilStateCreateInfo vkDs = {
|
||||
.sType = VK_STRUCTURE_TYPE_PIPELINE_DEPTH_STENCIL_STATE_CREATE_INFO,
|
||||
.depthTestEnable = enableDepthTest ? VK_TRUE : VK_FALSE,
|
||||
.depthWriteEnable = raster.depthWriteEnable,
|
||||
.depthCompareOp = fvkutils::getCompareOp(raster.depthCompareOp),
|
||||
.depthBoundsTestEnable = VK_FALSE,
|
||||
.stencilTestEnable = enableStencilTest ? VK_TRUE : VK_FALSE,
|
||||
.stencilTestEnable = VK_FALSE,
|
||||
.minDepthBounds = 0.0f,
|
||||
.maxDepthBounds = 0.0f,
|
||||
};
|
||||
vkDs.front = {
|
||||
.failOp = fvkutils::getStencilOp(stencil.front.stencilOpStencilFail),
|
||||
.passOp = fvkutils::getStencilOp(stencil.front.stencilOpDepthStencilPass),
|
||||
.depthFailOp = fvkutils::getStencilOp(stencil.front.stencilOpDepthFail),
|
||||
.compareOp = fvkutils::getCompareOp(stencil.front.stencilFunc),
|
||||
.compareMask = stencil.front.readMask,
|
||||
.writeMask = (uint32_t) (stencil.stencilWrite ? stencil.front.writeMask : 0u),
|
||||
.reference = (uint32_t) stencil.front.ref,
|
||||
};
|
||||
vkDs.back = {
|
||||
.failOp = fvkutils::getStencilOp(stencil.back.stencilOpStencilFail),
|
||||
.passOp = fvkutils::getStencilOp(stencil.back.stencilOpDepthStencilPass),
|
||||
.depthFailOp = fvkutils::getStencilOp(stencil.back.stencilOpDepthFail),
|
||||
.compareOp = fvkutils::getCompareOp(stencil.back.stencilFunc),
|
||||
.compareMask = stencil.back.readMask,
|
||||
.writeMask = (uint32_t) (stencil.stencilWrite ? stencil.back.writeMask : 0u),
|
||||
.reference = (uint32_t) stencil.back.ref,
|
||||
vkDs.front = vkDs.back = {
|
||||
.failOp = VK_STENCIL_OP_KEEP,
|
||||
.passOp = VK_STENCIL_OP_KEEP,
|
||||
.depthFailOp = VK_STENCIL_OP_KEEP,
|
||||
.compareOp = VK_COMPARE_OP_ALWAYS,
|
||||
.compareMask = 0u,
|
||||
.writeMask = 0u,
|
||||
.reference = 0u,
|
||||
};
|
||||
|
||||
VkGraphicsPipelineCreateInfo pipelineCreateInfo = {
|
||||
@@ -455,14 +433,8 @@ void VulkanPipelineCache::bindRasterState(RasterState const& rasterState) noexce
|
||||
mPipelineRequirements.rasterState = rasterState;
|
||||
}
|
||||
|
||||
void VulkanPipelineCache::bindStencilState(StencilState const& stencilState) noexcept {
|
||||
mPipelineRequirements.stencilState = stencilState;
|
||||
}
|
||||
|
||||
void VulkanPipelineCache::bindRenderPass(
|
||||
fvkmemory::resource_ptr<VulkanRenderPass> renderPass,
|
||||
int subpassIndex) noexcept {
|
||||
mPipelineRequirements.renderPass = renderPass->getVkRenderPass();
|
||||
void VulkanPipelineCache::bindRenderPass(VkRenderPass renderPass, int subpassIndex) noexcept {
|
||||
mPipelineRequirements.renderPass = renderPass;
|
||||
mPipelineRequirements.subpassIndex = subpassIndex;
|
||||
}
|
||||
|
||||
|
||||
@@ -122,9 +122,7 @@ public:
|
||||
void bindLayout(VkPipelineLayout layout) noexcept;
|
||||
void bindProgram(fvkmemory::resource_ptr<VulkanProgram> program) noexcept;
|
||||
void bindRasterState(RasterState const& rasterState) noexcept;
|
||||
void bindStencilState(StencilState const& stencilState) noexcept;
|
||||
void bindRenderPass(fvkmemory::resource_ptr<VulkanRenderPass> renderPass,
|
||||
int subpassIndex) noexcept;
|
||||
void bindRenderPass(VkRenderPass renderPass, int subpassIndex) noexcept;
|
||||
void bindPrimitiveTopology(VkPrimitiveTopology topology) noexcept;
|
||||
void bindVertexArray(VkVertexInputAttributeDescription const* attribDesc,
|
||||
VkVertexInputBindingDescription const* bufferDesc, uint8_t count);
|
||||
@@ -188,8 +186,8 @@ private:
|
||||
VertexInputAttributeDescription vertexAttributes[VERTEX_ATTRIBUTE_COUNT]; // 128 : 28
|
||||
VertexInputBindingDescription vertexBuffers[VERTEX_ATTRIBUTE_COUNT]; // 128 : 156
|
||||
RasterState rasterState; // 16 : 284
|
||||
StencilState stencilState; // 12 : 300
|
||||
VkPipelineLayout layout; // 8 : 312
|
||||
uint32_t padding; // 4 : 300
|
||||
VkPipelineLayout layout; // 8 : 304
|
||||
};
|
||||
|
||||
// Provides information about any dynamic state that should be used in creation of the
|
||||
@@ -205,7 +203,7 @@ private:
|
||||
uint8_t stereoscopicViewCount = 2;
|
||||
};
|
||||
|
||||
static_assert(sizeof(PipelineKey) == 320, "PipelineKey must not have implicit padding.");
|
||||
static_assert(sizeof(PipelineKey) == 312, "PipelineKey must not have implicit padding.");
|
||||
|
||||
using PipelineHashFn = utils::hash::MurmurHashFn<PipelineKey>;
|
||||
|
||||
|
||||
@@ -368,7 +368,6 @@ VulkanTexture::VulkanTexture(VkDevice device, VkPhysicalDevice physicalDevice,
|
||||
bool const isProtected = any(tusage & TextureUsage::PROTECTED);
|
||||
VkImageCreateInfo imageInfo{
|
||||
.sType = VK_STRUCTURE_TYPE_IMAGE_CREATE_INFO,
|
||||
.flags = isProtected ? VK_IMAGE_CREATE_PROTECTED_BIT : 0u,
|
||||
.imageType = target == SamplerType::SAMPLER_3D ? VK_IMAGE_TYPE_3D : VK_IMAGE_TYPE_2D,
|
||||
.format = vkFormat,
|
||||
.extent = {w, h, depth},
|
||||
|
||||
@@ -42,8 +42,6 @@ template ResourceType getTypeEnum<VulkanSync>() noexcept;
|
||||
template ResourceType getTypeEnum<VulkanMemoryMappedBuffer>() noexcept;
|
||||
template ResourceType getTypeEnum<VulkanSemaphore>() noexcept;
|
||||
template ResourceType getTypeEnum<VulkanStream>() noexcept;
|
||||
template ResourceType getTypeEnum<VulkanFramebuffer>() noexcept;
|
||||
template ResourceType getTypeEnum<VulkanRenderPass>() noexcept;
|
||||
|
||||
template<typename D>
|
||||
ResourceType getTypeEnum() noexcept {
|
||||
@@ -110,12 +108,6 @@ ResourceType getTypeEnum() noexcept {
|
||||
if constexpr (std::is_same_v<D, VulkanStream>) {
|
||||
return ResourceType::STREAM;
|
||||
}
|
||||
if constexpr (std::is_same_v<D, VulkanFramebuffer>) {
|
||||
return ResourceType::FRAMEBUFFER;
|
||||
}
|
||||
if constexpr (std::is_same_v<D, VulkanRenderPass>) {
|
||||
return ResourceType::RENDER_PASS;
|
||||
}
|
||||
return ResourceType::UNDEFINED_TYPE;
|
||||
}
|
||||
|
||||
@@ -163,10 +155,6 @@ std::string_view getTypeStr(ResourceType type) {
|
||||
return "Semaphore";
|
||||
case ResourceType::STREAM:
|
||||
return "VulkanStream";
|
||||
case ResourceType::FRAMEBUFFER:
|
||||
return "Framebuffer";
|
||||
case ResourceType::RENDER_PASS:
|
||||
return "RenderPass";
|
||||
case ResourceType::UNDEFINED_TYPE:
|
||||
return "";
|
||||
}
|
||||
|
||||
@@ -56,9 +56,7 @@ enum class ResourceType : uint8_t {
|
||||
MEMORY_MAPPED_BUFFER = 18,
|
||||
SEMAPHORE = 19,
|
||||
STREAM = 20,
|
||||
FRAMEBUFFER = 21,
|
||||
RENDER_PASS = 22,
|
||||
UNDEFINED_TYPE = 23, // Must be the last enum because we use it for iterating over the enums.
|
||||
UNDEFINED_TYPE = 21, // Must be the last enum because we use it for iterating over the enums.
|
||||
};
|
||||
|
||||
template<typename D>
|
||||
|
||||
@@ -126,12 +126,6 @@ void ResourceManager::destroyWithType(ResourceType type, HandleId id) {
|
||||
case ResourceType::STREAM:
|
||||
destruct<VulkanStream>(Handle<VulkanStream>(id));
|
||||
break;
|
||||
case ResourceType::FRAMEBUFFER:
|
||||
destruct<VulkanFramebuffer>(Handle<VulkanFramebuffer>(id));
|
||||
break;
|
||||
case ResourceType::RENDER_PASS:
|
||||
destruct<VulkanRenderPass>(Handle<VulkanRenderPass>(id));
|
||||
break;
|
||||
case ResourceType::UNDEFINED_TYPE:
|
||||
break;
|
||||
}
|
||||
|
||||
@@ -378,15 +378,10 @@ VulkanPlatform::ImageData VulkanPlatformAndroid::createVkImageFromExternal(
|
||||
externalCreateInfo.pNext = &imageFormatListInfo;
|
||||
}
|
||||
|
||||
VkImageCreateFlags imageFlags =
|
||||
(isFormatSrgb(metadata.format) ? VK_IMAGE_CREATE_MUTABLE_FORMAT_BIT : 0u) |
|
||||
(any(metadata.filamentUsage & TextureUsage::PROTECTED)
|
||||
? VK_IMAGE_CREATE_PROTECTED_BIT
|
||||
: 0u);
|
||||
VkImageCreateInfo const imageInfo = {
|
||||
.sType = VK_STRUCTURE_TYPE_IMAGE_CREATE_INFO,
|
||||
.pNext = &externalCreateInfo,
|
||||
.flags = imageFlags,
|
||||
.flags = isFormatSrgb(metadata.format) ? VK_IMAGE_CREATE_MUTABLE_FORMAT_BIT : 0u,
|
||||
.imageType = VK_IMAGE_TYPE_2D,
|
||||
// For non external images, use the same format as the AHB, which isn't in SRGB
|
||||
// Fix VUID-VkMemoryAllocateInfo-pNext-02387
|
||||
|
||||
@@ -633,19 +633,6 @@ VkCompareOp getCompareOp(SamplerCompareFunc func) {
|
||||
}
|
||||
}
|
||||
|
||||
VkStencilOp getStencilOp(StencilOperation op) {
|
||||
switch (op) {
|
||||
case StencilOperation::KEEP: return VK_STENCIL_OP_KEEP;
|
||||
case StencilOperation::ZERO: return VK_STENCIL_OP_ZERO;
|
||||
case StencilOperation::REPLACE: return VK_STENCIL_OP_REPLACE;
|
||||
case StencilOperation::INCR: return VK_STENCIL_OP_INCREMENT_AND_CLAMP;
|
||||
case StencilOperation::INCR_WRAP: return VK_STENCIL_OP_INCREMENT_AND_WRAP;
|
||||
case StencilOperation::DECR: return VK_STENCIL_OP_DECREMENT_AND_CLAMP;
|
||||
case StencilOperation::DECR_WRAP: return VK_STENCIL_OP_DECREMENT_AND_WRAP;
|
||||
case StencilOperation::INVERT: return VK_STENCIL_OP_INVERT;
|
||||
}
|
||||
}
|
||||
|
||||
VkBlendFactor getBlendFactor(BlendFunction mode) {
|
||||
switch (mode) {
|
||||
case BlendFunction::ZERO: return VK_BLEND_FACTOR_ZERO;
|
||||
|
||||
@@ -53,7 +53,6 @@ uint32_t getBytesPerPixel(TextureFormat format);
|
||||
uint8_t getTexelBlockSize(VkFormat format);
|
||||
|
||||
VkCompareOp getCompareOp(SamplerCompareFunc func);
|
||||
VkStencilOp getStencilOp(StencilOperation op);
|
||||
VkBlendFactor getBlendFactor(BlendFunction mode);
|
||||
VkCullModeFlags getCullMode(CullingMode mode);
|
||||
VkFrontFace getFrontFace(bool inverseFrontFaces);
|
||||
|
||||
Binary file not shown.
|
Before Width: | Height: | Size: 3.7 KiB |
@@ -1,120 +0,0 @@
|
||||
/*
|
||||
* Copyright (C) 2025 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#include "BackendTest.h"
|
||||
|
||||
#include "Shader.h"
|
||||
#include "SharedShaders.h"
|
||||
#include "Skip.h"
|
||||
#include "TrianglePrimitive.h"
|
||||
|
||||
#include <backend/PixelBufferDescriptor.h>
|
||||
|
||||
#include <utils/FixedCapacityVector.h>
|
||||
|
||||
namespace test {
|
||||
|
||||
using namespace filament;
|
||||
using namespace filament::backend;
|
||||
using namespace filament::math;
|
||||
|
||||
TEST_F(BackendTest, AutoresolveDifferingSampleCounts) {
|
||||
SKIP_IF(SkipEnvironment(OperatingSystem::CI, Backend::OPENGL), "see b/486954356");
|
||||
SKIP_IF(SkipEnvironment(OperatingSystem::CI, Backend::VULKAN), "see b/486954356");
|
||||
|
||||
auto& api = getDriverApi();
|
||||
constexpr int kRenderTargetSize = 512;
|
||||
|
||||
auto swapChain = addCleanup(createSwapChain());
|
||||
api.makeCurrent(swapChain, swapChain);
|
||||
|
||||
Shader shader = SharedShaders::makeShader(api, *mCleanup,
|
||||
{
|
||||
.mVertexType = VertexShaderType::Simple,
|
||||
.mFragmentType = FragmentShaderType::SolidColored,
|
||||
.mUniformType = ShaderUniformType::Simple,
|
||||
});
|
||||
|
||||
TrianglePrimitive triangle(api);
|
||||
PipelineState ps = getColorWritePipelineState();
|
||||
shader.addProgramToPipelineState(ps);
|
||||
|
||||
auto ubuffer = addCleanup(api.createBufferObject(sizeof(SimpleMaterialParams),
|
||||
BufferObjectBinding::UNIFORM, BufferUsage::STATIC));
|
||||
shader.bindUniform<SimpleMaterialParams>(api, ubuffer);
|
||||
|
||||
// Create a texture with sample count = 1.
|
||||
Handle<HwTexture> texture = addCleanup(api.createTexture(SamplerType::SAMPLER_2D, 1,
|
||||
TextureFormat::RGBA8, 1, kRenderTargetSize, kRenderTargetSize, 1,
|
||||
TextureUsage::COLOR_ATTACHMENT | TextureUsage::SAMPLEABLE));
|
||||
|
||||
// First render pass: render a red triangle to a RenderTarget with 8 samples.
|
||||
{
|
||||
Handle<HwRenderTarget> renderTarget =
|
||||
addCleanup(api.createRenderTarget(TargetBufferFlags::COLOR, kRenderTargetSize,
|
||||
kRenderTargetSize, 8, 1, { { texture } }, {}, {}));
|
||||
|
||||
shader.uploadUniform(api, ubuffer,
|
||||
SimpleMaterialParams{
|
||||
.color = float4(1, 0, 0, 1),
|
||||
});
|
||||
|
||||
RenderPassParams params = getClearColorRenderPass(float4(0));
|
||||
params.viewport = { 0, 0, kRenderTargetSize, kRenderTargetSize };
|
||||
|
||||
RenderFrame frame(api);
|
||||
api.beginRenderPass(renderTarget, params);
|
||||
ps.primitiveType = PrimitiveType::TRIANGLES;
|
||||
ps.vertexBufferInfo = triangle.getVertexBufferInfo();
|
||||
api.bindPipeline(ps);
|
||||
api.bindRenderPrimitive(triangle.getRenderPrimitive());
|
||||
api.draw2(0, 3, 1);
|
||||
api.endRenderPass();
|
||||
api.commit(swapChain);
|
||||
}
|
||||
|
||||
// Second render pass: render a green triangle to a RenderTarget with 4 samples, attached to
|
||||
// the same texture.
|
||||
{
|
||||
Handle<HwRenderTarget> renderTarget =
|
||||
addCleanup(api.createRenderTarget(TargetBufferFlags::COLOR, kRenderTargetSize,
|
||||
kRenderTargetSize, 4, 1, { { texture } }, {}, {}));
|
||||
|
||||
shader.uploadUniform(api, ubuffer,
|
||||
SimpleMaterialParams{
|
||||
.color = float4(0, 1, 0, 1),
|
||||
});
|
||||
|
||||
RenderPassParams params = getClearColorRenderPass(float4(0));
|
||||
params.viewport = { 0, 0, kRenderTargetSize, kRenderTargetSize };
|
||||
|
||||
RenderFrame frame(api);
|
||||
api.beginRenderPass(renderTarget, params);
|
||||
ps.primitiveType = PrimitiveType::TRIANGLES;
|
||||
ps.vertexBufferInfo = triangle.getVertexBufferInfo();
|
||||
api.bindPipeline(ps);
|
||||
api.bindRenderPrimitive(triangle.getRenderPrimitive());
|
||||
api.draw2(0, 3, 1);
|
||||
api.endRenderPass();
|
||||
|
||||
EXPECT_IMAGE(renderTarget, ScreenshotParams(kRenderTargetSize, kRenderTargetSize,
|
||||
"AutoresolveDifferingSampleCounts", 1048576));
|
||||
|
||||
api.commit(swapChain);
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace test
|
||||
@@ -271,8 +271,8 @@ std::unique_ptr<MaterialDefinition> MaterialDefinition::create(FEngine& engine,
|
||||
|
||||
void MaterialDefinition::terminate(FEngine& engine) {
|
||||
DriverApi& driver = engine.getDriverApi();
|
||||
perViewDescriptorSetLayoutPcf.terminate(engine.getDescriptorSetLayoutFactory(), driver);
|
||||
perViewDescriptorSetLayoutS2d.terminate(engine.getDescriptorSetLayoutFactory(), driver);
|
||||
perViewDescriptorSetLayout.terminate(engine.getDescriptorSetLayoutFactory(), driver);
|
||||
perViewDescriptorSetLayoutVsm.terminate(engine.getDescriptorSetLayoutFactory(), driver);
|
||||
descriptorSetLayout.terminate(engine.getDescriptorSetLayoutFactory(), driver);
|
||||
}
|
||||
|
||||
@@ -607,23 +607,23 @@ void MaterialDefinition::processDescriptorSets(FEngine& engine) {
|
||||
refractionMode == RefractionMode::SCREEN_SPACE;
|
||||
bool const hasFog = !(variantFilterMask & UserVariantFilterMask(UserVariantFilterBit::FOG));
|
||||
|
||||
this->perViewDescriptorSetLayoutPcfDescription = descriptor_sets::getPerViewDescriptorSetLayout(
|
||||
this->perViewDescriptorSetLayoutDescription = descriptor_sets::getPerViewDescriptorSetLayout(
|
||||
materialDomain, isLit, isSSR, hasFog, false);
|
||||
|
||||
this->perViewDescriptorSetLayoutS2dDescription = descriptor_sets::getPerViewDescriptorSetLayout(
|
||||
this->perViewDescriptorSetLayoutVsmDescription = descriptor_sets::getPerViewDescriptorSetLayout(
|
||||
materialDomain, isLit, isSSR, hasFog, true);
|
||||
|
||||
// set the labels
|
||||
this->descriptorSetLayoutDescription.label = CString{ name }.append("_perMat");
|
||||
this->perViewDescriptorSetLayoutPcfDescription.label = CString{ name }.append("_perView");
|
||||
this->perViewDescriptorSetLayoutS2dDescription.label = CString{ name }.append("_perViewVsm");
|
||||
this->perViewDescriptorSetLayoutDescription.label = CString{ name }.append("_perView");
|
||||
this->perViewDescriptorSetLayoutVsmDescription.label = CString{ name }.append("_perViewVsm");
|
||||
|
||||
// get the PER_RENDERABLE and PER_VIEW descriptor binding info
|
||||
for (auto&& [bindingPoint, dsl] : {
|
||||
std::pair{ DescriptorSetBindingPoints::PER_RENDERABLE,
|
||||
descriptor_sets::getPerRenderableLayout() },
|
||||
std::pair{ DescriptorSetBindingPoints::PER_VIEW,
|
||||
this->perViewDescriptorSetLayoutPcfDescription }}) {
|
||||
this->perViewDescriptorSetLayoutDescription }}) {
|
||||
Program::DescriptorBindingsInfo& descriptors = programDescriptorBindings[+bindingPoint];
|
||||
descriptors.reserve(dsl.descriptors.size());
|
||||
for (auto const& entry: dsl.descriptors) {
|
||||
@@ -636,17 +636,17 @@ void MaterialDefinition::processDescriptorSets(FEngine& engine) {
|
||||
descriptorSetLayoutFactory, driver,
|
||||
this->descriptorSetLayoutDescription };
|
||||
|
||||
this->perViewDescriptorSetLayoutPcf = {
|
||||
this->perViewDescriptorSetLayout = {
|
||||
descriptorSetLayoutFactory, driver,
|
||||
this->perViewDescriptorSetLayoutPcfDescription };
|
||||
this->perViewDescriptorSetLayoutDescription };
|
||||
|
||||
this->perViewDescriptorSetLayoutS2d = {
|
||||
this->perViewDescriptorSetLayoutVsm = {
|
||||
descriptorSetLayoutFactory, driver,
|
||||
this->perViewDescriptorSetLayoutS2dDescription };
|
||||
this->perViewDescriptorSetLayoutVsmDescription };
|
||||
}
|
||||
|
||||
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 (Variant::isValidDepthVariant(variant)) {
|
||||
// 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();
|
||||
}
|
||||
}
|
||||
if (useS2dDescriptorSetLayout) {
|
||||
return perViewDescriptorSetLayoutS2dDescription;
|
||||
if (useVsmDescriptorSetLayout) {
|
||||
return perViewDescriptorSetLayoutVsmDescription;
|
||||
}
|
||||
return perViewDescriptorSetLayoutPcfDescription;
|
||||
return perViewDescriptorSetLayoutDescription;
|
||||
}
|
||||
|
||||
Handle<HwProgram> MaterialDefinition::compileProgram(
|
||||
@@ -689,7 +689,7 @@ Handle<HwProgram> MaterialDefinition::compileProgram(
|
||||
pb.descriptorLayout(+DescriptorSetBindingPoints::PER_VIEW,
|
||||
getPerViewDescriptorSetLayoutDescription(
|
||||
specialization.variant,
|
||||
Variant::isShadowSampler2DVariant(specialization.variant)));
|
||||
Variant::isVSMVariant(specialization.variant)));
|
||||
pb.descriptorLayout(+DescriptorSetBindingPoints::PER_RENDERABLE,
|
||||
descriptor_sets::getPerRenderableLayout());
|
||||
pb.descriptorLayout(
|
||||
|
||||
@@ -94,17 +94,17 @@ struct MaterialDefinition {
|
||||
backend::ShaderModel const sm, bool isStereoSupported) const noexcept;
|
||||
|
||||
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
|
||||
// may be used by some backends in parallel compilation of programs.
|
||||
backend::DescriptorSetLayout perViewDescriptorSetLayoutPcfDescription;
|
||||
backend::DescriptorSetLayout perViewDescriptorSetLayoutS2dDescription;
|
||||
backend::DescriptorSetLayout perViewDescriptorSetLayoutDescription;
|
||||
backend::DescriptorSetLayout perViewDescriptorSetLayoutVsmDescription;
|
||||
backend::DescriptorSetLayout descriptorSetLayoutDescription;
|
||||
|
||||
// try to order by frequency of use
|
||||
filament::DescriptorSetLayout perViewDescriptorSetLayoutPcf;
|
||||
filament::DescriptorSetLayout perViewDescriptorSetLayoutS2d;
|
||||
filament::DescriptorSetLayout perViewDescriptorSetLayout;
|
||||
filament::DescriptorSetLayout perViewDescriptorSetLayoutVsm;
|
||||
filament::DescriptorSetLayout descriptorSetLayout;
|
||||
backend::Program::DescriptorSetInfo programDescriptorBindings;
|
||||
|
||||
|
||||
@@ -802,7 +802,7 @@ FrameGraphId<FrameGraphTexture> PostProcessManager::ssr(FrameGraph& fg,
|
||||
|
||||
// use our special SSR variant, it can only be applied to object that have
|
||||
// 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.
|
||||
passBuilder.commandTypeFlags(RenderPass::CommandTypeFlags::SCREEN_SPACE_REFLECTIONS);
|
||||
|
||||
+12
-11
@@ -568,7 +568,7 @@ RenderPass::Command* RenderPass::generateCommandsImpl(CommandTypeFlags extraFlag
|
||||
if constexpr (isDepthPass) {
|
||||
cmd.info.materialVariant = variant;
|
||||
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.depthFunc = RasterState::DepthFunc::GE;
|
||||
cmd.info.rasterState.alphaToCoverage = false;
|
||||
@@ -616,7 +616,7 @@ RenderPass::Command* RenderPass::generateCommandsImpl(CommandTypeFlags extraFlag
|
||||
bool const hasSkinningOrMorphing = hasSkinning || hasMorphing;
|
||||
|
||||
// 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 };
|
||||
|
||||
// we can't have SSR and shadowing together by construction
|
||||
@@ -798,19 +798,16 @@ RenderPass::Command* RenderPass::generateCommandsImpl(CommandTypeFlags extraFlag
|
||||
cmd.info.rasterState.culling = cullingMode;
|
||||
|
||||
// FIXME: should writeDepthForShadowCasters take precedence over mi->getDepthWrite()?
|
||||
cmd.info.rasterState.depthWrite =
|
||||
(1 // only keep bit 0
|
||||
& (mi->isDepthWriteEnabled() |
|
||||
(mode == TransparencyMode::TWO_PASSES_ONE_SIDE) |
|
||||
isPickingVariant) &
|
||||
!(depthFilterAlphaMaskedObjects & rs.alphaToCoverage)) |
|
||||
writeDepthForShadowCasters;
|
||||
cmd.info.rasterState.depthWrite = (1 // only keep bit 0
|
||||
& (mi->isDepthWriteEnabled() | (mode == TransparencyMode::TWO_PASSES_ONE_SIDE)
|
||||
| isPickingVariant)
|
||||
& !(filterTranslucentObjects & translucent)
|
||||
& !(depthFilterAlphaMaskedObjects & rs.alphaToCoverage))
|
||||
| writeDepthForShadowCasters;
|
||||
|
||||
*curr = cmd;
|
||||
// cancel command if both front and back faces are culled
|
||||
curr->key |= select(cullingMode == CullingMode::FRONT_AND_BACK);
|
||||
// cancel command if asked to filter translucent objects
|
||||
curr->key |= select(filterTranslucentObjects & translucent);
|
||||
}
|
||||
|
||||
++curr;
|
||||
@@ -904,6 +901,10 @@ void RenderPass::Executor::execute(FEngine const& engine, DriverApi& driver,
|
||||
size_t const capacity = engine.getMinCommandBufferSize();
|
||||
CircularBuffer const& circularBuffer = driver.getCircularBuffer();
|
||||
|
||||
utils::slog.e <<"circularBuffer: " << circularBuffer.size() << " used: " <<
|
||||
circularBuffer.getUsed() <<
|
||||
" commandCount: " << last - first << utils::io::endl;
|
||||
|
||||
// b/479079631: Log the number of commands in this render pass.
|
||||
size_t const commandCount = last - first;
|
||||
if (Platform* platform = engine.getPlatform(); platform->hasDebugUpdateStatFunc()) {
|
||||
|
||||
+75
-58
@@ -213,14 +213,12 @@ ShadowMap::ShaderParameters ShadowMap::updateDirectional(FEngine& engine,
|
||||
* 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);
|
||||
|
||||
// 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
|
||||
const auto [Mt, Mn] = getTextureCoordsMapping(shadowMapInfo, getViewport());
|
||||
|
||||
// world space to texture atlas space
|
||||
const mat4f St = highPrecisionMultiply(Mt, S);
|
||||
|
||||
ShaderParameters shaderParameters;
|
||||
@@ -228,12 +226,14 @@ ShadowMap::ShaderParameters ShadowMap::updateDirectional(FEngine& engine,
|
||||
|
||||
// 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.
|
||||
// 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) {
|
||||
shaderParameters.texelSizeAtOneMeterWs = texelSizeWorldSpace(S, shadowMapInfo.shadowDimension);
|
||||
shaderParameters.texelSizeAtOneMeterWs =
|
||||
texelSizeWorldSpace(Wp, mat4f(Mt * F), shadowMapInfo.shadowDimension);
|
||||
} else {
|
||||
// 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) {
|
||||
shaderParameters.lightSpace = St;
|
||||
@@ -1117,67 +1117,84 @@ bool ShadowMap::intersectSegmentWithPlanarQuad(float3& UTILS_RESTRICT p,
|
||||
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
|
||||
// orthographic projections. We just need to inverse shadowMapFromWorld,
|
||||
// orthographic projections. We just need to inverse worldToShadowTexture,
|
||||
// which is guaranteed to be orthographic.
|
||||
// The two first columns give us how a texel maps in world-space.
|
||||
float const oneTexel = 2.0f / float(shadowDimension);
|
||||
mat3f const worldFromClip(inverse(clipFromWorld));
|
||||
float3 const Jx = worldFromClip[0];
|
||||
float3 const Jy = worldFromClip[1];
|
||||
float2 const s = float2{ length(Jx), length(Jy) } * oneTexel;
|
||||
const float ures = 1.0f / float(shadowDimension);
|
||||
const float vres = 1.0f / float(shadowDimension);
|
||||
const mat3f shadowTextureToWorld(inverse(worldToShadowTexture));
|
||||
const float3 Jx = shadowTextureToWorld[0];
|
||||
const float3 Jy = shadowTextureToWorld[1];
|
||||
const float s = std::max(length(Jx) * ures, length(Jy) * vres);
|
||||
return s;
|
||||
}
|
||||
|
||||
/**
|
||||
* Calculates the Jacobian matrix J = ∂(u,v,d)/∂(x,y,z) for a 4x4 perspective projection.
|
||||
*
|
||||
* Given a view-space point P = [x, y, z, 1]^T and a projection matrix M,
|
||||
* the projected homogeneous coordinates are [X, Y, Z, W]^T = M * P.
|
||||
* The resulting NDC coordinates are u = X/W, v = Y/W, and depth d = Z/W.
|
||||
*
|
||||
* To find the Jacobian on the CPU, we apply the quotient rule to each component:
|
||||
* ∂(X/W) / ∂xi = ( (∂X/∂xi) * W - X * (∂W/∂xi) ) / W^2
|
||||
*
|
||||
* In matrix form, this can be expressed as:
|
||||
* 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;
|
||||
}
|
||||
float ShadowMap::texelSizeWorldSpace(const mat4f& Wp, const mat4f& MbMtF,
|
||||
uint16_t shadowDimension) noexcept {
|
||||
// Here we compute the Jacobian of inverse(MbMtF * Wp).
|
||||
// The expression below has been computed with Mathematica. However, it's not very hard,
|
||||
// albeit error-prone, to do it by hand because MbMtF is a linear transform.
|
||||
// So we really only need to calculate the Jacobian of inverse(Wp) at inverse(MbMtF).
|
||||
//
|
||||
// Because we're only interested in the length of the columns of the Jacobian, we can use
|
||||
// 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
|
||||
// the Jacobian's column vectors.
|
||||
|
||||
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.
|
||||
// It might be better to do this computation in the vertex shader.
|
||||
float3 const p = { 0.0f, 0.0f, 0.0f }; // clip-space
|
||||
float const oneTexel = 2.0f / float(shadowDimension);
|
||||
mat3f const J = jacobian(inverse(S), p);
|
||||
float2 const s = float2{ length(J[0]), length(J[1]) } * oneTexel;
|
||||
float3 const p = { 0.5f, 0.5f, 0.0f };
|
||||
|
||||
const float ures = 1.0f / float(shadowDimension);
|
||||
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;
|
||||
}
|
||||
|
||||
|
||||
@@ -135,7 +135,7 @@ public:
|
||||
math::mat4f lightSpace{};
|
||||
math::float4 lightFromWorldZ{};
|
||||
math::float4 scissorNormalized{};
|
||||
math::float2 texelSizeAtOneMeterWs{};
|
||||
float texelSizeAtOneMeterWs{};
|
||||
};
|
||||
|
||||
// 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;
|
||||
|
||||
static math::float2 texelSizeWorldSpace(const math::mat3f& clipFromWorld, uint16_t shadowDimension) noexcept;
|
||||
static math::float2 texelSizeWorldSpace(const math::mat4f& clipFromWorld, uint16_t shadowDimension) noexcept;
|
||||
static float texelSizeWorldSpace(const math::mat3f& worldToShadowTexture,
|
||||
uint16_t shadowDimension) noexcept;
|
||||
|
||||
static float texelSizeWorldSpace(const math::mat4f& W, const math::mat4f& MbMtF,
|
||||
uint16_t shadowDimension) noexcept;
|
||||
|
||||
static constexpr Segment sBoxSegments[12] = {
|
||||
{ 0, 1 }, { 1, 3 }, { 3, 2 }, { 2, 0 },
|
||||
|
||||
@@ -742,16 +742,17 @@ ShadowMapManager::ShadowTechnique ShadowMapManager::updateCascadeShadowMaps(FEng
|
||||
|
||||
// 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).
|
||||
float2 const wsTexelSize = shaderParameters.texelSizeAtOneMeterWs;
|
||||
const float wsTexelSize = shaderParameters.texelSizeAtOneMeterWs;
|
||||
|
||||
auto& s = mShadowUb.edit();
|
||||
s.shadows[shadowIndex].layer = shadowMap.getLayer();
|
||||
s.shadows[shadowIndex].lightFromWorldMatrix = shaderParameters.lightSpace;
|
||||
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].bulbRadiusLs =
|
||||
mSoftShadowOptions.penumbraScale * options.shadowBulbRadius / length(wsTexelSize);
|
||||
mSoftShadowOptions.penumbraScale * options.shadowBulbRadius / wsTexelSize;
|
||||
|
||||
shadowTechnique |= ShadowTechnique::SHADOW_MAP;
|
||||
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
|
||||
if (shadowMap.hasVisibleShadows()) {
|
||||
const size_t shadowIndex = shadowMap.getShadowIndex();
|
||||
const float2 wsTexelSizeAtOneMeter = shaderParameters.texelSizeAtOneMeterWs;
|
||||
const float wsTexelSizeAtOneMeter = shaderParameters.texelSizeAtOneMeterWs;
|
||||
// note: normalBias is set to zero for VSM
|
||||
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].normalBias = normalBias * wsTexelSizeAtOneMeter;
|
||||
s.shadows[shadowIndex].lightFromWorldZ = shaderParameters.lightFromWorldZ;
|
||||
s.shadows[shadowIndex].texelSizeAtOneMeter = wsTexelSizeAtOneMeter;
|
||||
s.shadows[shadowIndex].nearOverFarMinusNear = float(n / (f - n));
|
||||
s.shadows[shadowIndex].elvsm = options->vsm.elvsm;
|
||||
s.shadows[shadowIndex].bulbRadiusLs =
|
||||
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
|
||||
if (shadowMap.hasVisibleShadows()) {
|
||||
const size_t shadowIndex = shadowMap.getShadowIndex();
|
||||
const float2 wsTexelSizeAtOneMeter = shaderParameters.texelSizeAtOneMeterWs;
|
||||
const float wsTexelSizeAtOneMeter = shaderParameters.texelSizeAtOneMeterWs;
|
||||
// note: normalBias is set to zero for VSM
|
||||
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].normalBias = normalBias * wsTexelSizeAtOneMeter;
|
||||
s.shadows[shadowIndex].lightFromWorldZ = shaderParameters.lightFromWorldZ;
|
||||
s.shadows[shadowIndex].texelSizeAtOneMeter = wsTexelSizeAtOneMeter;
|
||||
s.shadows[shadowIndex].nearOverFarMinusNear = float(n / (f - n));
|
||||
s.shadows[shadowIndex].elvsm = options->vsm.elvsm;
|
||||
s.shadows[shadowIndex].bulbRadiusLs =
|
||||
mSoftShadowOptions.penumbraScale * options->shadowBulbRadius
|
||||
/ length(wsTexelSizeAtOneMeter);
|
||||
/ wsTexelSizeAtOneMeter;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -264,8 +264,9 @@ FEngine::FEngine(Builder const& builder) :
|
||||
mLightManager(*this),
|
||||
mCameraManager(*this),
|
||||
mCommandBufferQueue(
|
||||
builder->mConfig.minCommandBufferSizeMB * MiB,
|
||||
builder->mConfig.minCommandBufferSizeMB * MiB,
|
||||
builder->mConfig.commandBufferSizeMB * MiB,
|
||||
// builder->mConfig.commandBufferSizeMB * MiB,
|
||||
builder->mPaused),
|
||||
mPerRenderPassArena(
|
||||
"FEngine::mPerRenderPassAllocator",
|
||||
@@ -277,6 +278,7 @@ FEngine::FEngine(Builder const& builder) :
|
||||
mMainThreadId(ThreadUtils::getThreadId()),
|
||||
mConfig(builder->mConfig)
|
||||
{
|
||||
|
||||
// update all the features flags specified in the builder
|
||||
for (auto const& [feature, value] : builder->mFeatureFlags) {
|
||||
auto* const p = getFeatureFlagPtr(feature.c_str_safe(), true);
|
||||
@@ -348,7 +350,6 @@ void FEngine::init() {
|
||||
LOG(INFO) << "Backend feature level: " << int(driverApi.getFeatureLevel());
|
||||
LOG(INFO) << "FEngine feature level: " << int(mActiveFeatureLevel);
|
||||
|
||||
|
||||
mResourceAllocatorDisposer = std::make_shared<TextureCacheDisposer>(driverApi);
|
||||
|
||||
mFullScreenTriangleVb = downcast(VertexBuffer::Builder()
|
||||
@@ -744,12 +745,13 @@ void FEngine::prepare(DriverApi& driver) {
|
||||
if (item->getMaterial()->getMaterialDomain() == MaterialDomain::SURFACE) {
|
||||
// If the remaining space is less than half the capacity, we flush right
|
||||
// away to allow some headroom for commands that might come later.
|
||||
if (UTILS_UNLIKELY(driver.getCircularBuffer().getUsed() > capacity / 2)) {
|
||||
if (UTILS_UNLIKELY(driver.getCircularBuffer().getUsed() > capacity / 2) && false) {
|
||||
flush();
|
||||
}
|
||||
item->commit(driver, uboManager);
|
||||
}
|
||||
});
|
||||
|
||||
}
|
||||
|
||||
if (useUboBatching) {
|
||||
@@ -798,12 +800,6 @@ void FEngine::submitFrame() {
|
||||
void FEngine::flush() {
|
||||
// flush the command buffer
|
||||
flushCommandBuffer(mCommandBufferQueue);
|
||||
|
||||
// In single-threaded mode, we have to call execute() to drain the command
|
||||
// buffer to really free up space
|
||||
if constexpr (!UTILS_HAS_THREADING) {
|
||||
execute();
|
||||
}
|
||||
}
|
||||
|
||||
void FEngine::flushAndWait() {
|
||||
|
||||
@@ -257,9 +257,9 @@ filament::DescriptorSetLayout const& FMaterial::getPerViewDescriptorSetLayout(
|
||||
}
|
||||
// mDefinition.perViewDescriptorSetLayout{Vsm} is already resolved for MaterialDomain
|
||||
if (useVsmDescriptorSetLayout) {
|
||||
return mDefinition.perViewDescriptorSetLayoutS2d;
|
||||
return mDefinition.perViewDescriptorSetLayoutVsm;
|
||||
}
|
||||
return mDefinition.perViewDescriptorSetLayoutPcf;
|
||||
return mDefinition.perViewDescriptorSetLayout;
|
||||
}
|
||||
|
||||
void FMaterial::compile(CompilerPriorityQueue const priority,
|
||||
|
||||
@@ -139,7 +139,7 @@ public:
|
||||
// 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
|
||||
// which VSM is nonsensical, like for unlit materials)
|
||||
return mDefinition.perViewDescriptorSetLayoutPcf;
|
||||
return mDefinition.perViewDescriptorSetLayout;
|
||||
}
|
||||
|
||||
DescriptorSetLayout const& getPerViewDescriptorSetLayout(
|
||||
@@ -378,14 +378,8 @@ private:
|
||||
Variant variant = {}) const noexcept;
|
||||
|
||||
bool isSharedVariant(Variant const variant) const {
|
||||
// HACK: The default material "should" have MNT | DEP, but then we'd have to compile it as a
|
||||
// lit material, which would increase binary size. Perhaps we could specially compile it
|
||||
// with this variant, but with the shader program cache in active development, the days of
|
||||
// the default material are numbered anyway.
|
||||
constexpr Variant::type_t vsmAndDep = Variant::MNT | Variant::DEP;
|
||||
return mDefinition.materialDomain == MaterialDomain::SURFACE && !mIsDefaultMaterial &&
|
||||
!mDefinition.hasCustomDepthShader && Variant::isValidDepthVariant(variant) &&
|
||||
(variant.key & vsmAndDep) != vsmAndDep;
|
||||
return (mDefinition.materialDomain == MaterialDomain::SURFACE) && !mIsDefaultMaterial &&
|
||||
!mDefinition.hasCustomDepthShader && Variant::isValidDepthVariant(variant);
|
||||
}
|
||||
|
||||
mutable utils::FixedCapacityVector<backend::Handle<backend::HwProgram>> mCachedPrograms;
|
||||
|
||||
@@ -969,7 +969,10 @@ void FRenderer::renderJob(DriverApi& driver, RootArenaScope& rootArenaScope, FVi
|
||||
variant.setDirectionalLighting(view.hasDirectionalLighting());
|
||||
variant.setDynamicLighting(view.hasDynamicLighting());
|
||||
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());
|
||||
|
||||
/*
|
||||
@@ -978,7 +981,10 @@ void FRenderer::renderJob(DriverApi& driver, RootArenaScope& rootArenaScope, FVi
|
||||
|
||||
if (view.needsShadowMap()) {
|
||||
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,
|
||||
RenderPassBuilder{ commandArena }
|
||||
|
||||
@@ -143,7 +143,7 @@ FView::FView(FEngine& engine)
|
||||
#ifndef NDEBUG
|
||||
// This can fail if another view has already registered this data source
|
||||
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
|
||||
auto const state = weak.lock();
|
||||
if (!state) {
|
||||
|
||||
@@ -1,12 +1,12 @@
|
||||
Pod::Spec.new do |spec|
|
||||
spec.name = "Filament"
|
||||
spec.version = "1.69.4"
|
||||
spec.version = "1.69.2"
|
||||
spec.license = { :type => "Apache 2.0", :file => "LICENSE" }
|
||||
spec.homepage = "https://google.github.io/filament"
|
||||
spec.authors = "Google LLC."
|
||||
spec.summary = "Filament is a real-time physically based rendering engine for Android, iOS, Windows, Linux, macOS, and WASM/WebGL."
|
||||
spec.platform = :ios, "11.0"
|
||||
spec.source = { :http => "https://github.com/google/filament/releases/download/v1.69.4/filament-v1.69.4-ios.tgz" }
|
||||
spec.source = { :http => "https://github.com/google/filament/releases/download/v1.69.2/filament-v1.69.2-ios.tgz" }
|
||||
|
||||
spec.libraries = 'c++'
|
||||
|
||||
|
||||
@@ -36,7 +36,7 @@ backend::DescriptorSetLayout const& getPerRenderableLayout() noexcept;
|
||||
backend::DescriptorSetLayout getPerViewDescriptorSetLayout(
|
||||
MaterialDomain domain,
|
||||
bool isLit, bool isSSR, bool hasFog,
|
||||
bool isShadowSampler2D) noexcept;
|
||||
bool isVSM) noexcept;
|
||||
|
||||
backend::DescriptorSetLayout getPerViewDescriptorSetLayoutWithVariant(
|
||||
Variant variant,
|
||||
|
||||
@@ -299,9 +299,10 @@ struct ShadowUib { // NOLINT(cppcoreguidelines-pro-type-member-init)
|
||||
math::mat4f lightFromWorldMatrix; // 64
|
||||
math::float4 lightFromWorldZ; // 16
|
||||
math::float4 scissorNormalized; // 16
|
||||
float texelSizeAtOneMeter; // 4
|
||||
float bulbRadiusLs; // 4
|
||||
float nearOverFarMinusNear; // 4
|
||||
math::float2 normalBias; // 4
|
||||
float normalBias; // 4
|
||||
bool elvsm; // 4
|
||||
uint32_t layer; // 4
|
||||
uint32_t reserved1; // 4
|
||||
|
||||
@@ -48,21 +48,20 @@ struct Variant {
|
||||
// SRE: Shadow Receiver
|
||||
// SKN: Skinning
|
||||
// DEP: Depth only
|
||||
// FOG: Fog (standard)
|
||||
// PCK: Picking (depth)
|
||||
// MNT: Output depth moments (depth)
|
||||
// S2D: Sampler type for shadows (0: samplerShadowArray, 1: sampler2DArray) (standard)
|
||||
// FOG: Fog
|
||||
// PCK: Picking (depth variant only)
|
||||
// VSM: Variance shadow maps (depth) / sampler type (standard)
|
||||
// STE: Instanced stereo rendering
|
||||
//
|
||||
// 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:
|
||||
// +-----+-----+-----+-----+-----+-----+-----+-----+
|
||||
// | 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
|
||||
// Fragment shader 0 X X 0 0 X X X
|
||||
@@ -73,7 +72,7 @@ struct Variant {
|
||||
//
|
||||
// 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
|
||||
// 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 FOG = 0x20; // fog (standard)
|
||||
static constexpr type_t PCK = 0x20; // picking (depth)
|
||||
static constexpr type_t S2D = 0x40; // sampler type
|
||||
static constexpr type_t MNT = 0x40; // variance shadow maps
|
||||
static constexpr type_t VSM = 0x40; // variance shadow maps / sampler type
|
||||
static constexpr type_t STE = 0x80; // instanced stereo
|
||||
|
||||
static constexpr type_t NO_VARIANT = 0u;
|
||||
|
||||
// special variants (variants that use the reserved space)
|
||||
static constexpr type_t SPECIAL_SSR_VARIANT= S2D | SRE ;
|
||||
static constexpr type_t SPECIAL_SSR_MASK = STE | S2D | DEP | SRE | DYN | DIR;
|
||||
static constexpr type_t SPECIAL_SSR = VSM | SRE; // screen-space reflections variant
|
||||
|
||||
static constexpr type_t STANDARD_MASK = DEP;
|
||||
static constexpr type_t STANDARD_VARIANT = 0u;
|
||||
@@ -130,30 +127,29 @@ struct Variant {
|
||||
void setSkinning(bool v) noexcept { set(v, SKN); }
|
||||
void setFog(bool v) noexcept { set(v, FOG); }
|
||||
void setPicking(bool v) noexcept { set(v, PCK); }
|
||||
void setShadowSampler2D(bool v) noexcept { set(v, S2D); }
|
||||
void setDepthMoments(bool v) noexcept { set(v, MNT); }
|
||||
void setVsm(bool v) noexcept { set(v, VSM); }
|
||||
void setStereo(bool v) noexcept { set(v, STE); }
|
||||
|
||||
static constexpr bool isValidDepthVariant(Variant variant) noexcept {
|
||||
// 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_VALUE = MNT | PCK | DEP;
|
||||
constexpr type_t RESERVED_MASK = VSM | PCK | DEP | SRE | DYN | DIR;
|
||||
constexpr type_t RESERVED_VALUE = VSM | PCK | DEP;
|
||||
return ((variant.key & DEPTH_MASK) == DEPTH_VARIANT) &&
|
||||
((variant.key & RESERVED_MASK) != RESERVED_VALUE);
|
||||
}
|
||||
|
||||
static constexpr bool isValidStandardVariant(Variant variant) noexcept {
|
||||
// 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_VALUE = S2D | FOG | SRE;
|
||||
constexpr type_t RESERVED0_MASK = VSM | FOG | SRE | DYN | DIR;
|
||||
constexpr type_t RESERVED0_VALUE = VSM | FOG | SRE;
|
||||
|
||||
// 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;
|
||||
|
||||
// can't have VSM without shadow receiver
|
||||
constexpr type_t RESERVED2_MASK = S2D | SRE;
|
||||
constexpr type_t RESERVED2_VALUE = S2D;
|
||||
constexpr type_t RESERVED2_MASK = VSM | SRE;
|
||||
constexpr type_t RESERVED2_VALUE = VSM;
|
||||
|
||||
return ((variant.key & STANDARD_MASK) == STANDARD_VARIANT) &&
|
||||
((variant.key & RESERVED0_MASK) != RESERVED0_VALUE) &&
|
||||
@@ -178,15 +174,11 @@ struct Variant {
|
||||
}
|
||||
|
||||
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 {
|
||||
return !isSSRVariant(variant) && ((variant.key & (S2D | DEP)) == S2D);
|
||||
}
|
||||
|
||||
static constexpr bool isDepthMomentsVariant(Variant variant) noexcept {
|
||||
return !isSSRVariant(variant) && ((variant.key & (MNT | DEP)) == (MNT | DEP));
|
||||
static constexpr bool isVSMVariant(Variant variant) noexcept {
|
||||
return !isSSRVariant(variant) && ((variant.key & VSM) == VSM);
|
||||
}
|
||||
|
||||
static constexpr bool isShadowReceiverVariant(Variant variant) noexcept {
|
||||
@@ -210,13 +202,13 @@ struct Variant {
|
||||
// vertex shader.
|
||||
if ((variant.key & STANDARD_MASK) == STANDARD_VARIANT) {
|
||||
if (isSSRVariant(variant)) {
|
||||
variant.key &= ~SPECIAL_SSR_VARIANT;
|
||||
variant.key &= ~(VSM | SRE);
|
||||
}
|
||||
return variant & (STE | SKN | SRE | DYN | DIR);
|
||||
}
|
||||
if ((variant.key & DEPTH_MASK) == DEPTH_VARIANT) {
|
||||
// Only MNT, skinning, and stereo affect the vertex shader's DEPTH variant
|
||||
return variant & (STE | MNT | SKN | DEP);
|
||||
// Only VSM, skinning, and stereo affect the vertex shader's DEPTH variant
|
||||
return variant & (STE | VSM | SKN | DEP);
|
||||
}
|
||||
return {};
|
||||
}
|
||||
@@ -225,11 +217,11 @@ struct Variant {
|
||||
// filter out fragment variants that are not needed. For e.g. skinning doesn't
|
||||
// affect the fragment shader.
|
||||
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) {
|
||||
// Only VSM & PICKING affects the fragment shader's DEPTH variant
|
||||
return variant & (MNT | PCK | DEP);
|
||||
return variant & (VSM | PCK | DEP);
|
||||
}
|
||||
return {};
|
||||
}
|
||||
@@ -238,8 +230,8 @@ struct Variant {
|
||||
// special case for depth variant
|
||||
if (isValidDepthVariant(variant)) {
|
||||
if (!isLit) {
|
||||
// if we're unlit, we never need the MNT variant
|
||||
return variant & ~MNT;
|
||||
// if we're unlit, we never need the VSM variant
|
||||
return variant & ~VSM;
|
||||
}
|
||||
return variant;
|
||||
}
|
||||
@@ -250,9 +242,9 @@ struct Variant {
|
||||
// when the shading mode is unlit, remove all the lighting variants
|
||||
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)) {
|
||||
return variant & ~S2D;
|
||||
return variant & ~VSM;
|
||||
}
|
||||
return variant;
|
||||
}
|
||||
|
||||
@@ -216,7 +216,7 @@ utils::CString getDescriptorName(DescriptorSetBindingPoints const set,
|
||||
DescriptorSetLayout getPerViewDescriptorSetLayout(
|
||||
MaterialDomain const domain,
|
||||
bool const isLit, bool const isSSR, bool const hasFog,
|
||||
bool const isShadowSampler2D) noexcept {
|
||||
bool const isVSM) noexcept {
|
||||
|
||||
switch (domain) {
|
||||
case MaterialDomain::SURFACE: {
|
||||
@@ -255,7 +255,7 @@ DescriptorSetLayout getPerViewDescriptorSetLayout(
|
||||
}
|
||||
|
||||
// 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& v) {
|
||||
return v.binding == PerViewBindingPoints::SHADOW_MAP;
|
||||
@@ -285,7 +285,8 @@ DescriptorSetLayout getPerViewDescriptorSetLayoutWithVariant(
|
||||
return ssrVariantDescriptorSetLayout;
|
||||
}
|
||||
// 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) {
|
||||
|
||||
@@ -32,46 +32,41 @@ namespace filament {
|
||||
Variant Variant::filterUserVariant(
|
||||
Variant variant, UserVariantFilterMask filterMask) noexcept {
|
||||
// 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;
|
||||
}
|
||||
|
||||
if (filterMask & uint32_t(UserVariantFilterBit::DYNAMIC_LIGHTING)) {
|
||||
if (filterMask & (uint32_t)UserVariantFilterBit::DYNAMIC_LIGHTING) {
|
||||
variant.key &= ~DYN;
|
||||
}
|
||||
|
||||
if (filterMask & uint32_t(UserVariantFilterBit::SKINNING)) {
|
||||
if (filterMask & (uint32_t)UserVariantFilterBit::SKINNING) {
|
||||
variant.key &= ~SKN;
|
||||
}
|
||||
|
||||
if (filterMask & uint32_t(UserVariantFilterBit::STE)) {
|
||||
if (filterMask & (uint32_t)UserVariantFilterBit::STE) {
|
||||
variant.key &= ~(filterMask & STE);
|
||||
}
|
||||
|
||||
if (isValidDepthVariant(variant)) {
|
||||
// depth variants can have their MNT bit filtered
|
||||
if (filterMask & uint32_t(UserVariantFilterBit::VSM)) {
|
||||
variant.key &= ~MNT;
|
||||
}
|
||||
} else {
|
||||
if (!isValidDepthVariant(variant)) {
|
||||
// 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;
|
||||
}
|
||||
} else {
|
||||
// depth variants can have their VSM bit filtered
|
||||
if (filterMask & (uint32_t)UserVariantFilterBit::VSM) {
|
||||
variant.key &= ~VSM;
|
||||
}
|
||||
}
|
||||
|
||||
if (!isSSRVariant(variant)) {
|
||||
// SSR variant needs to be handled separately
|
||||
if (filterMask & uint32_t(UserVariantFilterBit::SHADOW_RECEIVER)) {
|
||||
if (filterMask & (uint32_t)UserVariantFilterBit::SHADOW_RECEIVER) {
|
||||
variant.key &= ~SRE;
|
||||
}
|
||||
if (filterMask & uint32_t(UserVariantFilterBit::VSM)) {
|
||||
variant.key &= ~S2D;
|
||||
if (filterMask & (uint32_t)UserVariantFilterBit::VSM) {
|
||||
variant.key &= ~VSM;
|
||||
}
|
||||
} else {
|
||||
// see if we need to filter out the SSR variants
|
||||
if (filterMask & uint32_t(UserVariantFilterBit::SSR)) {
|
||||
variant.key &= ~SPECIAL_SSR_VARIANT;
|
||||
if (filterMask & (uint32_t)UserVariantFilterBit::SSR) {
|
||||
variant.key &= ~SPECIAL_SSR;
|
||||
}
|
||||
}
|
||||
return variant;
|
||||
|
||||
@@ -60,8 +60,7 @@ void ShaderGenerator::generateSurfaceMaterialVariantDefines(io::sstream& out,
|
||||
CodeGenerator::generateDefine(out, "VARIANT_HAS_SHADOWING",
|
||||
litVariants && filament::Variant::isShadowReceiverVariant(variant));
|
||||
CodeGenerator::generateDefine(out, "VARIANT_HAS_VSM",
|
||||
filament::Variant::isShadowSampler2DVariant(variant) ||
|
||||
filament::Variant::isDepthMomentsVariant(variant));
|
||||
filament::Variant::isVSMVariant(variant));
|
||||
CodeGenerator::generateDefine(out, "VARIANT_HAS_STEREO",
|
||||
hasStereo(variant, featureLevel));
|
||||
CodeGenerator::generateDefine(out, "VARIANT_DEPTH",
|
||||
|
||||
@@ -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
|
||||
// matter).
|
||||
|
||||
static SamplerInterfaceBlock const sibShadowSamplerPcf{ SamplerInterfaceBlock::Builder()
|
||||
static SamplerInterfaceBlock const sibPcf{ SamplerInterfaceBlock::Builder()
|
||||
.name("sampler0")
|
||||
.stageFlags(backend::ShaderStageFlags::FRAGMENT)
|
||||
.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() };
|
||||
|
||||
static SamplerInterfaceBlock const sibShadowSampler2D{ SamplerInterfaceBlock::Builder()
|
||||
static SamplerInterfaceBlock const sibVsm{ SamplerInterfaceBlock::Builder()
|
||||
.name("sampler0")
|
||||
.stageFlags(backend::ShaderStageFlags::FRAGMENT)
|
||||
.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)) {
|
||||
return sibSsr;
|
||||
} else if (Variant::isShadowSampler2DVariant(variant)) {
|
||||
return sibShadowSampler2D;
|
||||
} else if (Variant::isVSMVariant(variant)) {
|
||||
return sibVsm;
|
||||
} else {
|
||||
return sibShadowSamplerPcf;
|
||||
return sibPcf;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -60,13 +60,11 @@
|
||||
#define GLTFIO_WARN(msg) slog.w << msg << io::endl
|
||||
#endif
|
||||
|
||||
#ifndef GLTFIO_USE_FILESYSTEM
|
||||
#if defined(__EMSCRIPTEN__) || defined(__ANDROID__) || defined(FILAMENT_IOS)
|
||||
#define GLTFIO_USE_FILESYSTEM 0
|
||||
#else
|
||||
#define GLTFIO_USE_FILESYSTEM 1
|
||||
#endif
|
||||
#endif
|
||||
|
||||
namespace utils {
|
||||
class NameComponentManager;
|
||||
|
||||
@@ -48,11 +48,10 @@ std::string formatVariantString(Variant variant, MaterialDomain domain) noexcept
|
||||
if (variant.key & Variant::DEP) variantString += "DEP|";
|
||||
if (variant.key & Variant::DEP) {
|
||||
if (variant.key & Variant::PCK) variantString += "PCK|";
|
||||
if (variant.key & Variant::MNT) variantString += "MNT|";
|
||||
} else {
|
||||
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|";
|
||||
variantString = variantString.substr(0, variantString.length() - 1);
|
||||
}
|
||||
|
||||
@@ -94,7 +94,7 @@ public:
|
||||
} backend;
|
||||
struct {
|
||||
bool check_crc32_after_loading = false;
|
||||
bool enable_material_instance_uniform_batching = false;
|
||||
bool enable_material_instance_uniform_batching = true;
|
||||
bool enable_fog_as_postprocess = false;
|
||||
} material;
|
||||
} features;
|
||||
|
||||
@@ -36,13 +36,20 @@
|
||||
#include <iostream>
|
||||
#include <string>// for printing usage/help
|
||||
|
||||
#include "filament/MaterialInstance.h"
|
||||
#include "generated/resources/resources.h"
|
||||
#include "generated/resources/monkey.h"
|
||||
|
||||
#include <utils/Log.h>
|
||||
|
||||
using namespace filament;
|
||||
using namespace filamesh;
|
||||
using namespace filament::math;
|
||||
|
||||
namespace {
|
||||
std::vector<MaterialInstance*> instances;
|
||||
}
|
||||
|
||||
using Backend = Engine::Backend;
|
||||
|
||||
struct App {
|
||||
@@ -152,6 +159,19 @@ int main(int argc, char** argv) {
|
||||
auto& tcm = engine->getTransformManager();
|
||||
auto ti = tcm.getInstance(app.mesh.renderable);
|
||||
tcm.setTransform(ti, app.transform * mat4f::rotation(now, float3{ 0, 1, 0 }));
|
||||
|
||||
static int count = 0;
|
||||
constexpr int allSize = 12000;
|
||||
if (count++ == 5) {
|
||||
for (size_t i = 0; i < allSize; ++i) {
|
||||
auto mi = app.materialInstance = app.material->createInstance();
|
||||
mi->setParameter("baseColor", RgbType::LINEAR, float3{0.8});
|
||||
mi->setParameter("metallic", 1.0f);
|
||||
mi->setParameter("roughness", 0.4f);
|
||||
mi->setParameter("reflectance", 0.5f);
|
||||
instances.push_back(mi);
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
FilamentApp::get().run(app.config, setup, cleanup);
|
||||
|
||||
@@ -111,7 +111,7 @@ highp vec4 getSpotLightSpacePosition(int index, highp vec3 dir, highp float zLig
|
||||
highp mat4 lightFromWorldMatrix = shadowUniforms.shadows[index].lightFromWorldMatrix;
|
||||
|
||||
// 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(),
|
||||
dir, bias, lightFromWorldMatrix);
|
||||
|
||||
@@ -12,51 +12,12 @@
|
||||
*/
|
||||
|
||||
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)
|
||||
// --------------------------------------------------------------------------------------
|
||||
// Anisotropic Normal Bias for Shadow Mapping
|
||||
// --------------------------------------------------------------------------------------
|
||||
// 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));
|
||||
highp float cosTheta = saturate(dot(n, dir));
|
||||
highp float sinTheta = sqrt(1.0 - cosTheta * cosTheta);
|
||||
p += n * (sinTheta * b);
|
||||
#endif
|
||||
|
||||
return mulMat4x4Float3(lightFromWorldMatrix, p);
|
||||
|
||||
@@ -5,9 +5,10 @@ struct ShadowData {
|
||||
highp mat4 lightFromWorldMatrix;
|
||||
highp vec4 lightFromWorldZ;
|
||||
highp vec4 scissorNormalized;
|
||||
mediump float texelSizeAtOneMeter;
|
||||
mediump float bulbRadiusLs;
|
||||
mediump float nearOverFarMinusNear;
|
||||
highp vec2 normalBias;
|
||||
mediump float normalBias;
|
||||
bool elvsm;
|
||||
mediump uint layer;
|
||||
mediump uint reserved1;
|
||||
|
||||
@@ -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
|
||||
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
|
||||
|
||||
- Make sure you've completed the steps in 'Setting up python'
|
||||
- Run interactive mode in the `update_golden.py` script.
|
||||
```
|
||||
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`
|
||||
to the `main` branch of the golden repo.
|
||||
|
||||
### Automated update via commit message
|
||||
|
||||
Alternatively, if you are confident in your changes and want the CI to handle the update
|
||||
for you, you can use the following tag in your commit message:
|
||||
|
||||
- In the commit message of your working branch on `filament`, add the following line:
|
||||
```
|
||||
RDIFF_ACCEPT_NEW_GOLDENS
|
||||
```
|
||||
|
||||
This has the following effects:
|
||||
- The presubmit test `test-renderdiff` will be bypassed (it will not perform rendering or
|
||||
comparison).
|
||||
- When the PR is merged, the postsubmit CI will automatically:
|
||||
1. Build Filament and generate the new images.
|
||||
2. Upload them to a temporary branch in `filament-assets`.
|
||||
3. Merge that branch into `main`.
|
||||
|
||||
## Viewing test results
|
||||
|
||||
We provide a viewer for looking at the result of a test run. The viewer is a webapp that can
|
||||
|
||||
@@ -18,7 +18,6 @@ import re
|
||||
from utils import execute, ArgParseImpl
|
||||
|
||||
RDIFF_UPDATE_GOLDEN_STR = 'RDIFF_BRANCH'
|
||||
RDIFF_ACCEPT_NEW_GOLDENS_STR = 'RDIFF_ACCEPT_NEW_GOLDENS'
|
||||
|
||||
def _parse_commit(commit_str):
|
||||
lines = commit_str.split('\n')
|
||||
@@ -43,11 +42,9 @@ def _parse_commit(commit_str):
|
||||
|
||||
if __name__ == "__main__":
|
||||
RE_STR = rf"{RDIFF_UPDATE_GOLDEN_STR}(?:\s)?\=(?:\s)?([a-zA-Z0-9\s\-\/]+)"
|
||||
RE_ACCEPT = rf"^{RDIFF_ACCEPT_NEW_GOLDENS_STR}$"
|
||||
|
||||
parser = ArgParseImpl()
|
||||
parser.add_argument('--file', help='A file containing the commit message')
|
||||
parser.add_argument('--mode', choices=['branch', 'accept_new_goldens'], default='branch', help='Mode of operation')
|
||||
args, _ = parser.parse_known_args(sys.argv[1:])
|
||||
|
||||
if not args.file:
|
||||
@@ -56,21 +53,14 @@ if __name__ == "__main__":
|
||||
with open(args.file, 'r') as f:
|
||||
msg = f.read()
|
||||
|
||||
to_update = []
|
||||
commit, title, description = _parse_commit(msg)
|
||||
|
||||
if args.mode == 'accept_new_goldens':
|
||||
for line in description:
|
||||
if re.match(RE_ACCEPT, line):
|
||||
sys.exit(0)
|
||||
sys.exit(1)
|
||||
|
||||
# args.mode == 'branch'
|
||||
for line in description:
|
||||
m = re.match(RE_STR, line)
|
||||
if not m:
|
||||
continue
|
||||
print(m.group(1))
|
||||
sys.exit(0)
|
||||
exit(0)
|
||||
|
||||
# Always default to the main branch
|
||||
print('main')
|
||||
|
||||
@@ -16,7 +16,6 @@ import os
|
||||
import shutil
|
||||
import re
|
||||
import sys
|
||||
import tempfile
|
||||
|
||||
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)
|
||||
if tag and len(tag) > 0:
|
||||
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)
|
||||
commit_file = f.name
|
||||
self._git_exec(f'commit --amend -F {commit_file}')
|
||||
os.remove(commit_file)
|
||||
self._git_exec(f'commit --amend -F {COMMIT_FILE}')
|
||||
|
||||
# Do the actual merge
|
||||
self._git_exec(f'checkout main')
|
||||
@@ -141,11 +139,11 @@ class GoldenManager:
|
||||
os.path.join(rdiff_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)
|
||||
tmp_golden_commit_file = f.name
|
||||
self._git_exec(f'commit -a -F {tmp_golden_commit_file}')
|
||||
os.remove(tmp_golden_commit_file)
|
||||
self._git_exec(f'commit -a -F {TMP_GOLDEN_COMMIT_FILE}')
|
||||
if push_to_remote and \
|
||||
(self.access_token_ or self.access_type_ == ACCESS_TYPE_SSH):
|
||||
self._git_exec(f'push -f origin {branch}')
|
||||
|
||||
@@ -64,19 +64,19 @@ def _do_update(golden_manager, config):
|
||||
|
||||
def _same_image_diffimg(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:
|
||||
res_json = json.loads(output)
|
||||
return res_json.get('passed', False)
|
||||
except json.JSONDecodeError as e:
|
||||
raise RuntimeError(f"Failed to parse diffimg output: {e}\nOutput: {output}")
|
||||
result = subprocess.run(cmd, capture_output=True, text=True)
|
||||
output = result.stdout.strip()
|
||||
if not 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):
|
||||
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.
|
||||
# But strict sync might imply deleting missing ones.
|
||||
# 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)
|
||||
update_files = list(new_files - base_files)
|
||||
update = list(new - base)
|
||||
|
||||
# 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)
|
||||
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)
|
||||
|
||||
if is_different:
|
||||
ret_update.append(fpath)
|
||||
update.append(fpath)
|
||||
|
||||
ret_update += update_files
|
||||
ret_delete += delete_files
|
||||
ret_update += update
|
||||
ret_delete += delete
|
||||
|
||||
return ret_delete, ret_update
|
||||
|
||||
@@ -179,7 +179,7 @@ if __name__ == "__main__":
|
||||
parser = ArgParseImpl()
|
||||
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('--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',
|
||||
default='./out/cmake-release/tools/diffimg/diffimg')
|
||||
|
||||
|
||||
@@ -18,7 +18,6 @@ import shutil
|
||||
import argparse
|
||||
import sys
|
||||
import pathlib
|
||||
import shlex
|
||||
|
||||
def execute(cmd,
|
||||
cwd=None,
|
||||
@@ -26,7 +25,7 @@ def execute(cmd,
|
||||
stdin=None,
|
||||
env=None,
|
||||
raise_errors=False):
|
||||
in_env = os.environ.copy()
|
||||
in_env = os.environ
|
||||
in_env.update(env if env else {})
|
||||
home = os.environ['HOME']
|
||||
if f'{home}/bin' not in in_env['PATH']:
|
||||
@@ -46,11 +45,11 @@ def execute(cmd,
|
||||
'universal_newlines': True
|
||||
}
|
||||
if capture_output:
|
||||
process = subprocess.Popen(shlex.split(cmd), **kwargs)
|
||||
process = subprocess.Popen(cmd.split(' '), **kwargs)
|
||||
output, err_output = process.communicate()
|
||||
return_code = process.returncode
|
||||
else:
|
||||
return_code = subprocess.call(shlex.split(cmd), **kwargs)
|
||||
return_code = subprocess.call(cmd.split(' '), **kwargs)
|
||||
|
||||
if return_code:
|
||||
# Error
|
||||
|
||||
@@ -1,190 +0,0 @@
|
||||
# Copyright (C) 2026 The Android Open Source Project
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||
# you may not use this file except in compliance with the License.
|
||||
# You may obtain a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS,
|
||||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
|
||||
#!/usr/bin/env python3
|
||||
|
||||
import argparse
|
||||
import json
|
||||
import os
|
||||
import sys
|
||||
import urllib.request
|
||||
import urllib.error
|
||||
import subprocess
|
||||
|
||||
# The base URL where historical size data is stored
|
||||
BASE_URL = "https://raw.githubusercontent.com/google/filament-assets/main/sizeguard/"
|
||||
|
||||
def get_merge_base(target_branch="origin/main"):
|
||||
"""Finds the merge base between HEAD and the target branch."""
|
||||
try:
|
||||
# Fetch the target branch to ensure we have the reference
|
||||
result = subprocess.run(
|
||||
["git", "merge-base", "HEAD", target_branch],
|
||||
stdout=subprocess.PIPE,
|
||||
stderr=subprocess.PIPE,
|
||||
check=True,
|
||||
text=True
|
||||
)
|
||||
return result.stdout.strip()
|
||||
except subprocess.CalledProcessError:
|
||||
print(f"Warning: Could not determine merge base with {target_branch}.", file=sys.stderr)
|
||||
return None
|
||||
|
||||
def get_ancestors(start_commit, count=50):
|
||||
"""Returns a list of ancestor commit hashes."""
|
||||
try:
|
||||
result = subprocess.run(
|
||||
["git", "rev-list", f"--max-count={count}", start_commit],
|
||||
stdout=subprocess.PIPE,
|
||||
stderr=subprocess.PIPE,
|
||||
check=True,
|
||||
text=True
|
||||
)
|
||||
return result.stdout.strip().splitlines()
|
||||
except subprocess.CalledProcessError as e:
|
||||
print(f"Error listing ancestors: {e}", file=sys.stderr)
|
||||
return []
|
||||
|
||||
def fetch_json(commit_hash):
|
||||
"""Fetches the JSON file for the given commit from the assets repo."""
|
||||
url = f"{BASE_URL}{commit_hash}.json"
|
||||
try:
|
||||
with urllib.request.urlopen(url) as response:
|
||||
if response.status == 200:
|
||||
print(f"Found historical data for commit {commit_hash}")
|
||||
return json.loads(response.read().decode('utf-8'))
|
||||
except urllib.error.HTTPError as e:
|
||||
if e.code == 404:
|
||||
return None
|
||||
print(f"Warning: HTTP error fetching {url}: {e}", file=sys.stderr)
|
||||
except Exception as e:
|
||||
print(f"Warning: Error fetching {url}: {e}", file=sys.stderr)
|
||||
return None
|
||||
|
||||
def flatten_data(data):
|
||||
"""Flattens the JSON structure into a dictionary of name -> size."""
|
||||
flat = {}
|
||||
for item in data:
|
||||
# Top level archive
|
||||
flat[item['name']] = item['size']
|
||||
# Inner content
|
||||
if 'content' in item:
|
||||
for inner in item['content']:
|
||||
# Key format: ArchiveName/InnerName
|
||||
key = f"{item['name']}/{inner['name']}"
|
||||
flat[key] = inner['size']
|
||||
return flat
|
||||
|
||||
def main():
|
||||
parser = argparse.ArgumentParser(
|
||||
description="Compare current artifact sizes against historical data."
|
||||
)
|
||||
parser.add_argument(
|
||||
"current_json", help="Path to the JSON file generated for the current build."
|
||||
)
|
||||
parser.add_argument(
|
||||
"--threshold", type=int, default=20480, help="Size increase threshold in bytes (default: 20KB)."
|
||||
)
|
||||
parser.add_argument(
|
||||
"--target-branch", default="origin/main", help="The branch to compare against."
|
||||
)
|
||||
parser.add_argument(
|
||||
"--artifacts", nargs="+",
|
||||
help="List of artifact paths to check (e.g. 'foo.aar' or 'foo.aar/lib/arm64/bar.so')."
|
||||
)
|
||||
|
||||
args = parser.parse_args()
|
||||
|
||||
if not os.path.exists(args.current_json):
|
||||
print(f"Error: Current JSON file not found: {args.current_json}", file=sys.stderr)
|
||||
sys.exit(1)
|
||||
|
||||
with open(args.current_json, 'r') as f:
|
||||
current_data = json.load(f)
|
||||
|
||||
# 1. Find the starting commit (merge base)
|
||||
start_commit = get_merge_base(args.target_branch)
|
||||
if not start_commit:
|
||||
print("Error: Could not determine a valid starting commit to search.", file=sys.stderr)
|
||||
sys.exit(1)
|
||||
|
||||
print(f"Merge base with {args.target_branch} is {start_commit}")
|
||||
|
||||
# 2. Search for historical data
|
||||
ancestors = get_ancestors(start_commit)
|
||||
base_data = None
|
||||
base_commit = None
|
||||
|
||||
for commit in ancestors:
|
||||
base_data = fetch_json(commit)
|
||||
if base_data:
|
||||
base_commit = commit
|
||||
break
|
||||
|
||||
if not base_data:
|
||||
print(f"Warning: No historical size data found in the last {len(ancestors)} ancestors.",
|
||||
file=sys.stderr)
|
||||
sys.exit(0)
|
||||
|
||||
print(f"Comparing against historical data from commit {base_commit}")
|
||||
|
||||
# 3. Compare
|
||||
current_flat = flatten_data(current_data)
|
||||
base_flat = flatten_data(base_data)
|
||||
|
||||
failures = []
|
||||
checked_count = 0
|
||||
|
||||
print(f"{'Artifact':<60} | {'Current':<10} | {'Base':<10} | {'Delta':<10} | {'Status'}")
|
||||
print("-" * 110)
|
||||
|
||||
keys_to_check = args.artifacts if args.artifacts else current_flat.keys()
|
||||
|
||||
for name in keys_to_check:
|
||||
if name not in current_flat:
|
||||
print(f"Warning: Artifact '{name}' not found in current build output.", file=sys.stderr)
|
||||
continue
|
||||
|
||||
current_size = current_flat[name]
|
||||
checked_count += 1
|
||||
|
||||
status = "OK"
|
||||
base_str = "N/A"
|
||||
diff_str = "N/A"
|
||||
|
||||
if name in base_flat:
|
||||
base_size = base_flat[name]
|
||||
base_str = str(base_size)
|
||||
diff = current_size - base_size
|
||||
diff_str = f"{diff:+}"
|
||||
|
||||
if diff > args.threshold:
|
||||
failures.append(name)
|
||||
status = "FAIL"
|
||||
else:
|
||||
status = "NEW"
|
||||
|
||||
print(f"{name:<60} | {current_size:<10} | {base_str:<10} | {diff_str:<10} | {status}")
|
||||
|
||||
print("-" * 110)
|
||||
|
||||
if failures:
|
||||
print(f"FAILURE: {len(failures)} artifacts exceeded threshold of {args.threshold} bytes.")
|
||||
sys.exit(1)
|
||||
else:
|
||||
print(f"SUCCESS: {checked_count} artifacts checked. All within acceptable threshold.")
|
||||
sys.exit(0)
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "filament",
|
||||
"version": "1.69.4",
|
||||
"version": "1.69.2",
|
||||
"description": "Real-time physically based rendering engine",
|
||||
"main": "filament.js",
|
||||
"module": "filament.js",
|
||||
|
||||
Reference in New Issue
Block a user