Compare commits

..
Author SHA1 Message Date
Benjamin Doherty d67e1a9ee8 Fix matc variantFilter 2022-11-03 15:53:48 -07:00
161 changed files with 2221 additions and 4003 deletions
+32 -56
View File
@@ -45,23 +45,18 @@ jobs:
with:
ref: ${{ steps.git_ref.outputs.ref }}
- name: Run build script
env:
TAG: ${{ steps.git_ref.outputs.tag }}
run: |
WORKFLOW_OS=`echo \`uname\` | sed "s/Darwin/mac/" | tr [:upper:] [:lower:]`
cd build/$WORKFLOW_OS && printf "y" | ./build.sh release
cd ../..
- name: Upload release assets
run: |
xargs -L 1 sudo pip3 install < build/common/requirements.txt
if [ -f out/filament-release-darwin.tgz ]; then mv out/filament-release-darwin.tgz out/filament-${TAG}-mac.tgz; fi;
if [ -f out/filament-release-linux.tgz ]; then mv out/filament-release-linux.tgz out/filament-${TAG}-linux.tgz; fi;
- uses: actions/github-script@v6
python3 build/common/upload-assets.py ${TAG} out/*.tgz
env:
TAG: ${{ steps.git_ref.outputs.tag }}
with:
script: |
const upload = require('./build/common/upload-release-assets');
const { TAG } = process.env;
const globber = await glob.create('out/*.tgz');
await upload({ github, context }, await globber.glob(), TAG);
GITHUB_API_KEY: ${{ secrets.GITHUB_API_KEY }}
build-web:
name: build-web
@@ -80,21 +75,16 @@ jobs:
with:
ref: ${{ steps.git_ref.outputs.ref }}
- name: Run build script
env:
TAG: ${{ steps.git_ref.outputs.tag }}
run: |
cd build/web && printf "y" | ./build.sh release
cd ../..
- name: Upload release assets
run: |
xargs -L 1 sudo pip3 install < build/common/requirements.txt
mv out/filament-release-web.tgz out/filament-${TAG}-web.tgz
- uses: actions/github-script@v6
python3 build/common/upload-assets.py ${TAG} out/*.tgz
env:
TAG: ${{ steps.git_ref.outputs.tag }}
with:
script: |
const upload = require('./build/common/upload-release-assets');
const { TAG } = process.env;
const globber = await glob.create('out/*.tgz');
await upload({ github, context }, await globber.glob(), TAG);
GITHUB_API_KEY: ${{ secrets.GITHUB_API_KEY }}
build-android:
name: build-android
@@ -113,16 +103,8 @@ jobs:
with:
ref: ${{ steps.git_ref.outputs.ref }}
- name: Run build script
env:
TAG: ${{ steps.git_ref.outputs.tag }}
run: |
cd build/android && printf "y" | ./build.sh release
cd ../..
mv out/filament-android-release.aar out/filament-${TAG}-android.aar
mv out/filamat-android-release.aar out/filamat-${TAG}-android.aar
mv out/filamat-android-lite-release.aar out/filamat-${TAG}-lite-android.aar
mv out/gltfio-android-release.aar out/gltfio-${TAG}-android.aar
mv out/filament-utils-android-release.aar out/filament-utils-${TAG}-android.aar
- name: Sign sample-gltf-viewer
run: |
echo "${APK_KEYSTORE_BASE64}" > filament.jks.base64
@@ -137,15 +119,18 @@ jobs:
TAG: ${{ steps.git_ref.outputs.tag }}
APK_KEYSTORE_BASE64: ${{ secrets.APK_KEYSTORE_BASE64 }}
APK_KEYSTORE_PASS: ${{ secrets.APK_KEYSTORE_PASS }}
- uses: actions/github-script@v6
- name: Upload release assets
run: |
xargs -L 1 sudo pip3 install < build/common/requirements.txt
mv out/filament-android-release.aar out/filament-${TAG}-android.aar
mv out/filamat-android-release.aar out/filamat-${TAG}-android.aar
mv out/filamat-android-lite-release.aar out/filamat-${TAG}-lite-android.aar
mv out/gltfio-android-release.aar out/gltfio-${TAG}-android.aar
mv out/filament-utils-android-release.aar out/filament-utils-${TAG}-android.aar
python3 build/common/upload-assets.py ${TAG} out/*.aar out/*.apk
env:
TAG: ${{ steps.git_ref.outputs.tag }}
with:
script: |
const upload = require('./build/common/upload-release-assets');
const { TAG } = process.env;
const globber = await glob.create(['out/*.aar', 'out/*.apk'].join('\n'));
await upload({ github, context }, await globber.glob(), TAG);
GITHUB_API_KEY: ${{ secrets.GITHUB_API_KEY }}
build-ios:
name: build-ios
@@ -164,21 +149,16 @@ jobs:
with:
ref: ${{ steps.git_ref.outputs.ref }}
- name: Run build script
env:
TAG: ${{ steps.git_ref.outputs.tag }}
run: |
cd build/ios && printf "y" | ./build.sh release
cd ../..
- name: Upload release assets
run: |
xargs -L 1 sudo pip3 install < build/common/requirements.txt
mv out/filament-release-ios.tgz out/filament-${TAG}-ios.tgz
- uses: actions/github-script@v6
python3 build/common/upload-assets.py ${TAG} out/*.tgz
env:
TAG: ${{ steps.git_ref.outputs.tag }}
with:
script: |
const upload = require('./build/common/upload-release-assets');
const { TAG } = process.env;
const globber = await glob.create('out/*.tgz');
await upload({ github, context }, await globber.glob(), TAG);
GITHUB_API_KEY: ${{ secrets.GITHUB_API_KEY }}
build-windows:
name: build-windows
@@ -198,19 +178,15 @@ jobs:
with:
ref: ${{ steps.git_ref.outputs.ref }}
- name: Run build script
env:
TAG: ${{ steps.git_ref.outputs.tag }}
run: |
build\windows\build-github.bat release
cd ..\..
move out\filament-windows.tgz out\filament-%TAG%-windows.tgz
shell: cmd
- uses: actions/github-script@v6
- name: Upload release assets
run: |
pip3 install PyGithub
mv out/filament-windows.tgz out/filament-${TAG}-windows.tgz
python build/common/upload-assets.py ${TAG} out/*.tgz
shell: bash
env:
TAG: ${{ steps.git_ref.outputs.tag }}
with:
script: |
const upload = require('./build/common/upload-release-assets');
const { TAG } = process.env;
const globber = await glob.create('out/*.tgz');
await upload({ github, context }, await globber.glob(), TAG);
GITHUB_API_KEY: ${{ secrets.GITHUB_API_KEY }}
+9 -13
View File
@@ -74,18 +74,18 @@ if (CCACHE_PROGRAM)
configure_file(build/launch-cxx.in launch-cxx)
execute_process(COMMAND chmod a+rx
"${CMAKE_CURRENT_BINARY_DIR}/launch-c"
"${CMAKE_CURRENT_BINARY_DIR}/launch-cxx"
"${CMAKE_BINARY_DIR}/launch-c"
"${CMAKE_BINARY_DIR}/launch-cxx"
)
if (CMAKE_GENERATOR STREQUAL "Xcode")
set(CMAKE_XCODE_ATTRIBUTE_CC "${CMAKE_CURRENT_BINARY_DIR}/launch-c")
set(CMAKE_XCODE_ATTRIBUTE_CXX "${CMAKE_CURRENT_BINARY_DIR}/launch-cxx")
set(CMAKE_XCODE_ATTRIBUTE_LD "${CMAKE_CURRENT_BINARY_DIR}/launch-c")
set(CMAKE_XCODE_ATTRIBUTE_LDPLUSPLUS "${CMAKE_CURRENT_BINARY_DIR}/launch-cxx")
set(CMAKE_XCODE_ATTRIBUTE_CC "${CMAKE_BINARY_DIR}/launch-c")
set(CMAKE_XCODE_ATTRIBUTE_CXX "${CMAKE_BINARY_DIR}/launch-cxx")
set(CMAKE_XCODE_ATTRIBUTE_LD "${CMAKE_BINARY_DIR}/launch-c")
set(CMAKE_XCODE_ATTRIBUTE_LDPLUSPLUS "${CMAKE_BINARY_DIR}/launch-cxx")
else()
set(CMAKE_C_COMPILER_LAUNCHER "${CMAKE_CURRENT_BINARY_DIR}/launch-c")
set(CMAKE_CXX_COMPILER_LAUNCHER "${CMAKE_CURRENT_BINARY_DIR}/launch-cxx")
set(CMAKE_C_COMPILER_LAUNCHER "${CMAKE_BINARY_DIR}/launch-c")
set(CMAKE_CXX_COMPILER_LAUNCHER "${CMAKE_BINARY_DIR}/launch-cxx")
endif()
endif()
@@ -99,10 +99,6 @@ set(CMAKE_EXPORT_COMPILE_COMMANDS ON)
# ==================================================================================================
if (UNIX AND NOT APPLE AND NOT ANDROID AND NOT WEBGL)
set(LINUX TRUE)
else()
# since cmake 3.25 LINUX is automatically set based on CMAKE_SYSTEM_NAME, which the android
# cmake files are setting to "Linux".
set(LINUX FALSE)
endif()
if (LINUX)
@@ -554,7 +550,7 @@ function(list_licenses OUTPUT MODULES)
endif()
endforeach()
configure_file(${FILAMENT}/build/licenses.inc.in ${OUTPUT})
endfunction()
endfunction(list_licenses)
set(COMBINE_SCRIPT "${CMAKE_CURRENT_SOURCE_DIR}/build/linux/combine-static-libs.sh")
if (WIN32)
+4 -4
View File
@@ -31,7 +31,7 @@ repositories {
}
dependencies {
implementation 'com.google.android.filament:filament-android:1.31.0'
implementation 'com.google.android.filament:filament-android:1.28.2'
}
```
@@ -51,7 +51,7 @@ Here are all the libraries available in the group `com.google.android.filament`:
iOS projects can use CocoaPods to install the latest release:
```
pod 'Filament', '~> 1.31.0'
pod 'Filament', '~> 1.28.2'
```
### Snapshots
@@ -117,9 +117,9 @@ steps:
- Image-based lighting
- Physically-based camera (shutter speed, sensitivity and aperture)
- Physical light units
- Point lights, spot lights, and directional light
- Point lights, spot lights and directional light
- Specular anti-aliasing
- Point, spot, and directional light shadows
- Spot and directional light shadows
- Cascaded shadows
- EVSM, PCSS, DPCF, or PCF shadows
- Transparent shadows
-24
View File
@@ -5,36 +5,12 @@ A new header is inserted each time a *tag* is created.
## main branch
## v1.31.0
- java: fix confusing typo in Camera documentation
- engine: the output color space can be set on `ColorGrading`. This API currently has limitations,
please refer to the documentation for more details
- Support for RGTC and BPTC texture compression
- engine: fix TransformManager high precision mode when using transactions
- web: added TypeScript definition for `Engine.destroy`
- materials: `getNormalizedViewportCoord()` now returns the logical (i.e. user) viewport
normalized position and keeps z reversed [⚠️ **Recompile Materials**]
- backend: workaround Adreno shader compiler bug (#6355) [⚠️ **Recompile Materials**]
- geometry: change computing tangent basis from normal vector to use Frisvad's method
## v1.30.0
- engine: optimize per-shadow UBO [⚠️ **Recompile Materials**]
- engine: fix potential memory corruption with larger scenes
- engine: VSM's MSAA setting is now per-view instead of per-light [⚠️ **API Change**]
## v1.29.0
- gltfio: calculate primitive's AABB correctly.
- gltfio: recompute bounding boxes with morph targets
- engine: add missing getters on `MaterialInstance`
- WebGL: add missing `ColorGrading` JS bindings
- engine: improvements/cleanup of Shadow mapping code [⚠️ **Recompile Materials**]
## v1.28.3
- backend: add support for GGP platform
- engine: primitives with `CullingMode::FRONT_AND_BACK` are now skipped.
@@ -79,7 +79,7 @@ Java_com_google_android_filament_LightManager_nBuilderShadowOptions(JNIEnv* env,
jfloat shadowFarHint, jboolean stable, jboolean lispsm,
jfloat polygonOffsetConstant, jfloat polygonOffsetSlope,
jboolean screenSpaceContactShadows, jint stepCount,
jfloat maxShadowDistance, jboolean elvsm, jfloat blurWidth, jfloat shadowBulbRadius) {
jfloat maxShadowDistance, jint vsmMsaaSamples, jboolean elvsm, jfloat blurWidth, jfloat shadowBulbRadius) {
LightManager::Builder *builder = (LightManager::Builder *) nativeBuilder;
LightManager::ShadowOptions shadowOptions {
.mapSize = (uint32_t)mapSize,
@@ -97,6 +97,7 @@ Java_com_google_android_filament_LightManager_nBuilderShadowOptions(JNIEnv* env,
.stepCount = uint8_t(stepCount),
.maxShadowDistance = maxShadowDistance,
.vsm = {
.msaaSamples = (uint8_t) vsmMsaaSamples,
.elvsm = (bool)elvsm,
.blurWidth = blurWidth
},
@@ -365,7 +365,7 @@ public class Camera {
* By default, this is an identity matrix.
* </p>
*
* @param inScaling diagonal of the scaling matrix to be applied after the projection matrix.
* @param scaling diagonal of the scaling matrix to be applied after the projection matrix.
*
* @see Camera#setProjection
* @see Camera#setLensProjection
@@ -409,7 +409,7 @@ public class Camera {
*
* <pre>
* engine.getTransformManager().setTransform(
* engine.getTransformManager().getInstance(camera->getEntity()), modelMatrix);
* engine.getTransformManager().getInstance(camera->getEntity()), viewMatrix);
* </pre>
*
* @param modelMatrix The camera position and orientation provided as a <b>rigid transform</b> matrix.
@@ -349,6 +349,20 @@ public class LightManager {
* @see View#setShadowType
*/
/**
* The number of MSAA samples to use when rendering VSM shadow maps.
* Must be a power-of-two and greater than or equal to 1. A value of 1 effectively turns
* off MSAA.
* Higher values may not be available depending on the underlying hardware.
*
* <p>
* <strong>Warning: This API is still experimental and subject to change.</strong>
* </p>
*/
@IntRange(from = 1)
public int vsmMsaaSamples = 1;
/**
* When elvsm is set to true, "Exponential Layered VSM without Layers" are used. It is
* an improvement to the default EVSM which suffers important light leaks. Enabling
@@ -505,7 +519,7 @@ public class LightManager {
options.shadowFarHint, options.stable, options.lispsm,
options.polygonOffsetConstant, options.polygonOffsetSlope,
options.screenSpaceContactShadows,
options.stepCount, options.maxShadowDistance,
options.stepCount, options.maxShadowDistance, options.vsmMsaaSamples,
options.elvsm, options.blurWidth, options.shadowBulbRadius);
return this;
}
@@ -1004,6 +1018,8 @@ public class LightManager {
* lightbulbs.
*
* @param efficiency Efficiency in percent. This depends on the type of lightbulb used.
*
* @return This Builder, for chaining calls.
*/
public void setIntensity(@EntityInstance int i, float watts, float efficiency) {
nSetIntensity(mNativeObject, i , watts, efficiency);
@@ -1169,7 +1185,7 @@ public class LightManager {
boolean stable, boolean lispsm,
float polygonOffsetConstant, float polygonOffsetSlope,
boolean screenSpaceContactShadows, int stepCount, float maxShadowDistance,
boolean elvsm, float blurWidth, float shadowBulbRadius);
int vsmMsaaSamples, boolean elvsm, float blurWidth, float shadowBulbRadius);
private static native void nBuilderCastLight(long nativeBuilder, boolean enabled);
private static native void nBuilderPosition(long nativeBuilder, float x, float y, float z);
private static native void nBuilderDirection(long nativeBuilder, float x, float y, float z);
@@ -124,24 +124,7 @@ public class Renderer {
*/
public static class ClearOptions {
/**
* Color (sRGB linear) to use to clear the RenderTarget (typically the SwapChain).
*
* The RenderTarget is cleared using this color, which won't be tone-mapped since
* tone-mapping is part of View rendering (this is not).
*
* When a View is rendered, there are 3 scenarios to consider:
* - Pixels rendered by the View replace the clear color (or blend with it in
* `BlendMode.TRANSLUCENT` mode).
*
* - With blending mode set to `BlendMode.TRANSLUCENT`, Pixels untouched by the View
* are considered fulling transparent and let the clear color show through.
*
* - With blending mode set to `BlendMode.OPAQUE`, Pixels untouched by the View
* are set to the clear color. However, because it is now used in the context of a View,
* it will go through the post-processing stage, which includes tone-mapping.
*
* For consistency, it is recommended to always use a Skybox to clear an opaque View's
* background, or to use black or fully-transparent (i.e. {0,0,0,0}) as the clear color.
* Color to use to clear the SwapChain
*/
@NonNull
public float[] clearColor = { 0.0f, 0.0f, 0.0f, 0.0f };
@@ -250,19 +250,7 @@ public class Texture {
SRGB8_ALPHA8_ASTC_10x8,
SRGB8_ALPHA8_ASTC_10x10,
SRGB8_ALPHA8_ASTC_12x10,
SRGB8_ALPHA8_ASTC_12x12,
// RGTC formats available with a GLES extension
RED_RGTC1, // BC4 unsigned
SIGNED_RED_RGTC1, // BC4 signed
RED_GREEN_RGTC2, // BC5 unsigned
SIGNED_RED_GREEN_RGTC2, // BC5 signed
// BPTC formats available with a GLES extension
RGB_BPTC_SIGNED_FLOAT, // BC6H signed
RGB_BPTC_UNSIGNED_FLOAT,// BC6H unsigned
RGBA_BPTC_UNORM, // BC7
SRGB_ALPHA_BPTC_UNORM // BC7 sRGB
SRGB8_ALPHA8_ASTC_12x12
}
/**
@@ -308,19 +296,7 @@ public class Texture {
SRGB8_ALPHA8_ASTC_10x8,
SRGB8_ALPHA8_ASTC_10x10,
SRGB8_ALPHA8_ASTC_12x10,
SRGB8_ALPHA8_ASTC_12x12,
// RGTC formats available with a GLES extension
RED_RGTC1, // BC4 unsigned
SIGNED_RED_RGTC1, // BC4 signed
RED_GREEN_RGTC2, // BC5 unsigned
SIGNED_RED_GREEN_RGTC2, // BC5 signed
// BPTC formats available with a GLES extension
RGB_BPTC_SIGNED_FLOAT, // BC6H signed
RGB_BPTC_UNSIGNED_FLOAT,// BC6H unsigned
RGBA_BPTC_UNORM, // BC7
SRGB_ALPHA_BPTC_UNORM // BC7 sRGB
SRGB8_ALPHA8_ASTC_12x12
}
/**
@@ -1817,13 +1817,6 @@ public class View {
* Whether to generate mipmaps for all VSM shadow maps.
*/
public boolean mipmapping = false;
/**
* The number of MSAA samples to use when rendering VSM shadow maps.
* Must be a power-of-two and greater than or equal to 1. A value of 1 effectively turns
* off MSAA.
* Higher values may not be available depending on the underlying hardware.
*/
public int msaaSamples = 1;
/**
* Whether to use a 32-bits or 16-bits texture format for VSM shadow maps. 32-bits
* precision is rarely needed, but it does reduces light leaks as well as "fading"
+1 -1
View File
@@ -1,5 +1,5 @@
GROUP=com.google.android.filament
VERSION_NAME=1.31.0
VERSION_NAME=1.28.2
POM_DESCRIPTION=Real-time physically based rendering engine for Android.
@@ -27,7 +27,6 @@ import android.view.animation.LinearInterpolator
import com.google.android.filament.*
import com.google.android.filament.RenderableManager.*
import com.google.android.filament.Renderer.ClearOptions
import com.google.android.filament.VertexBuffer.*
import com.google.android.filament.android.DisplayHelper
import com.google.android.filament.android.UiHelper
@@ -65,6 +64,7 @@ class MainActivity : Activity() {
private lateinit var view0: View
private lateinit var view1: View
private lateinit var view2: View
private lateinit var view3: View
private lateinit var view4: View
// We need skybox to set the background color
private lateinit var skybox: Skybox
@@ -119,19 +119,15 @@ class MainActivity : Activity() {
view0 = engine.createView()
view1 = engine.createView()
view2 = engine.createView()
view3 = engine.createView()
view4 = engine.createView()
view0.setName("view0");
view1.setName("view1");
view2.setName("view2");
view3.setName("view3");
view4.setName("view4");
// Useful for debugging issues
// view0.isPostProcessingEnabled = false
// view1.isPostProcessingEnabled = false
// view2.isPostProcessingEnabled = false
// view4.isPostProcessingEnabled = false
view4.blendMode = View.BlendMode.TRANSLUCENT;
skybox = Skybox.Builder().build(engine);
@@ -144,11 +140,13 @@ class MainActivity : Activity() {
view0.camera = camera
view1.camera = camera
view2.camera = camera
view3.camera = camera
view4.camera = camera
view0.scene = scene
view1.scene = scene
view2.scene = scene
view3.scene = scene
view4.scene = scene
}
@@ -381,6 +379,7 @@ class MainActivity : Activity() {
engine.destroyView(view0)
engine.destroyView(view1)
engine.destroyView(view2)
engine.destroyView(view3)
engine.destroyView(view4)
engine.destroySkybox(skybox)
engine.destroyScene(scene)
@@ -408,18 +407,19 @@ class MainActivity : Activity() {
// If beginFrame() returns false you should skip the frame
// This means you are sending frames too quickly to the GPU
if (renderer.beginFrame(swapChain!!, frameTimeNanos)) {
scene.skybox = skybox
skybox.setColor(0.35f, 0.35f, 0.35f, 1.0f);
skybox.setColor(0.035f, 0.035f, 0.035f, 1.0f);
renderer.render(view0)
skybox.setColor(1.0f, 1.0f, 0.0f, 1.0f);
skybox.setColor(1.0f, 0.0f, 0.0f, 1.0f);
renderer.render(view1)
scene.skybox = null
skybox.setColor(0.0f, 1.0f, 0.0f, 1.0f);
renderer.render(view2)
skybox.setColor(0.0f, 0.0f, 1.0f, 1.0f);
renderer.render(view3)
skybox.setColor(0.0f, 0.0f, 0.0f, 0.0f);
renderer.render(view4)
renderer.endFrame()
@@ -433,10 +433,6 @@ class MainActivity : Activity() {
swapChain?.let { engine.destroySwapChain(it) }
swapChain = engine.createSwapChain(surface)
renderer.setDisplayInfo(DisplayHelper.getDisplayInfo(surfaceView.display, Renderer.DisplayInfo()))
renderer.clearOptions = renderer.clearOptions.apply {
clear = true
clearColor = floatArrayOf( 0.0f, 0.0f, 1.0f, 0.0f )
}
}
override fun onDetachedFromSurface() {
@@ -457,6 +453,7 @@ class MainActivity : Activity() {
view0.viewport = Viewport(0, 0, width / 2, height / 2)
view1.viewport = Viewport(width / 2, 0, width / 2, height / 2)
view2.viewport = Viewport(0, height / 2, width / 2, height / 2)
view3.viewport = Viewport(width / 2, height / 2, width / 2, height / 2)
view4.viewport = Viewport(width / 4, height / 4, width / 2, height / 2)
}
}
+14
View File
@@ -0,0 +1,14 @@
setuptools==40.6.2
wheel==0.37.1
certifi==2021.10.8
cffi==1.15.0
charset-normalizer==2.0.12
Deprecated==1.2.13
idna==3.3
pycparser==2.21
PyGithub==1.55
PyJWT==2.4.0
PyNaCl==1.5.0
requests==2.27.1
urllib3==1.26.9
wrapt==1.14.0
+93
View File
@@ -0,0 +1,93 @@
# Copyright (C) 2019 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.
# requirements.txt created via:
#
# pip3 install virtualenv
# python3 -m venv env
# pip3 install PyGithub
# pip3 install wheel
# --all includes wheel and setuputils, which is needed for GitHub Actions
# pip3 freeze --all > requirements.txt
# Manually move 'setuptools' and 'wheel' lines to the beginning of the file.
# Manually remove the 'pip' line.
#
# Requirements are installed in GitHub Actions via:
# xargs -L 1 sudo pip3 install < build/common/requirements.txt
# xargs must be used because the ordering of the dependencies matters (namely, setuptools).
# sudo must be used to uninstall non-matching versions of packages.
# pip is removed to avoid conflicts with the pre-installed pip version on Actions.
# This technique does not work with Windows, unfortunately.
from github import Github
import os, sys
def print_usage():
print('Upload assets to a Filament GitHub release.')
print()
print('Usage:')
print(' upload-assets.py <tag> <asset>...')
print()
print('Notes:')
print(' The GitHub release must already be created prior to running this script. This is typically done')
print(' through the GitHub web UI.')
print()
print(' <tag> is the Git tag for the desired release to attach assets to, for example, "v1.4.2".')
print()
print(' <asset> is a path to the asset file to upload. The file name will be used as the name of the')
print(' asset.')
print()
print(' The GITHUB_API_KEY environment variable must be set to a valid authentication token for a')
print(' collaborator account of the Filament repository.')
# The first argument is the path to this script.
if len(sys.argv) < 3:
print_usage()
sys.exit(1)
tag = sys.argv[1]
assets = sys.argv[2:]
authentication_token = os.environ.get('GITHUB_API_KEY')
if not authentication_token:
sys.stderr.write('Error: the GITHUB_API_KEY is not set.\n')
sys.exit(1)
g = Github(authentication_token)
FILAMENT_REPO = "google/filament"
filament = g.get_repo(FILAMENT_REPO)
def find_release_from_tag(repo, tag):
""" Find a release in the repo for the given Git tag string. """
releases = repo.get_releases()
for r in releases:
if r.tag_name == tag:
return r
return None
release = find_release_from_tag(filament, tag)
if not release:
sys.stderr.write(f"Error: Could not find release with tag '{tag}'.\n")
sys.exit(1)
print(f"Found release with tag '{tag}'.")
for asset_path in assets:
sys.stdout.write(f'Uploding asset: {asset_path}... ')
asset_name = os.path.basename(asset_path)
asset = release.upload_asset(asset_path, name=asset_name)
if asset:
sys.stdout.write('Success!\n')
@@ -1,78 +0,0 @@
/*
* Copyright (C) 2022 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.
*/
const uploadReleaseAssets = async ({github, context}, assetPathsToUpload, releaseTag) => {
const fs = require('fs/promises');
const path = require('path');
const filesToUpload = assetPathsToUpload.map(assetPath => ({
buffer: fs.readFile(assetPath),
assetName: path.basename(assetPath)
}));
const findReleaseMatchingTag = async tag => {
const { data: releases } = await github.rest.repos.listReleases(context.repo);
const release = releases.find(release => release.tag_name === releaseTag);
if (!release) {
throw new Error(`Could not locate release with tag '${releaseTag}'`);
}
return release;
};
const release = await findReleaseMatchingTag(releaseTag);
console.log(`Found release named '${release.name}' matching tag '${release.tag_name}'.`);
const uploadPromises = [];
for (const file of filesToUpload) {
console.log(`Uploading asset ${file.assetName}.`);
const p = github.rest.repos.uploadReleaseAsset({
...context.repo,
release_id: release.id,
name: file.assetName,
data: await file.buffer
});
uploadPromises.push(p);
}
await Promise.all(uploadPromises);
console.log("Done!");
}
module.exports = uploadReleaseAssets;
////////////////////////////////////////////////////////////////////////////////////////////////////
// To test this script locally, uncomment the code below and run:
//
// npm install
// node index.js
// const { Octokit } = require("@octokit/rest");
// const glob = require("@actions/glob");
// (async () => {
// const github = new Octokit({
// auth: "" // <-- paste GitHub auth token here
// });
// const context = {
// repo: {
// owner: "bejado",
// repo: "filament",
// },
// };
// const globber = await glob.create('*.txt');
// await uploadReleaseAssets({ github, context }, await globber.glob(), 'vtest-28');
// })();
-546
View File
@@ -1,546 +0,0 @@
{
"name": "upload-release-assets",
"version": "1.0.0",
"lockfileVersion": 2,
"requires": true,
"packages": {
"": {
"version": "1.0.0",
"license": "Apache-2.0",
"dependencies": {
"@actions/glob": "^0.3.0",
"@octokit/rest": "^19.0.5"
}
},
"node_modules/@actions/core": {
"version": "1.10.0",
"resolved": "https://registry.npmjs.org/@actions/core/-/core-1.10.0.tgz",
"integrity": "sha512-2aZDDa3zrrZbP5ZYg159sNoLRb61nQ7awl5pSvIq5Qpj81vwDzdMRKzkWJGJuwVvWpvZKx7vspJALyvaaIQyug==",
"dependencies": {
"@actions/http-client": "^2.0.1",
"uuid": "^8.3.2"
}
},
"node_modules/@actions/glob": {
"version": "0.3.0",
"resolved": "https://registry.npmjs.org/@actions/glob/-/glob-0.3.0.tgz",
"integrity": "sha512-tJP1ZhF87fd6LBnaXWlahkyvdgvsLl7WnreW1EZaC8JWjpMXmzqWzQVe/IEYslrkT9ymibVrKyJN4UMD7uQM2w==",
"dependencies": {
"@actions/core": "^1.2.6",
"minimatch": "^3.0.4"
}
},
"node_modules/@actions/http-client": {
"version": "2.0.1",
"resolved": "https://registry.npmjs.org/@actions/http-client/-/http-client-2.0.1.tgz",
"integrity": "sha512-PIXiMVtz6VvyaRsGY268qvj57hXQEpsYogYOu2nrQhlf+XCGmZstmuZBbAybUl1nQGnvS1k1eEsQ69ZoD7xlSw==",
"dependencies": {
"tunnel": "^0.0.6"
}
},
"node_modules/@octokit/auth-token": {
"version": "3.0.2",
"resolved": "https://registry.npmjs.org/@octokit/auth-token/-/auth-token-3.0.2.tgz",
"integrity": "sha512-pq7CwIMV1kmzkFTimdwjAINCXKTajZErLB4wMLYapR2nuB/Jpr66+05wOTZMSCBXP6n4DdDWT2W19Bm17vU69Q==",
"dependencies": {
"@octokit/types": "^8.0.0"
},
"engines": {
"node": ">= 14"
}
},
"node_modules/@octokit/core": {
"version": "4.1.0",
"resolved": "https://registry.npmjs.org/@octokit/core/-/core-4.1.0.tgz",
"integrity": "sha512-Czz/59VefU+kKDy+ZfDwtOIYIkFjExOKf+HA92aiTZJ6EfWpFzYQWw0l54ji8bVmyhc+mGaLUbSUmXazG7z5OQ==",
"dependencies": {
"@octokit/auth-token": "^3.0.0",
"@octokit/graphql": "^5.0.0",
"@octokit/request": "^6.0.0",
"@octokit/request-error": "^3.0.0",
"@octokit/types": "^8.0.0",
"before-after-hook": "^2.2.0",
"universal-user-agent": "^6.0.0"
},
"engines": {
"node": ">= 14"
}
},
"node_modules/@octokit/endpoint": {
"version": "7.0.3",
"resolved": "https://registry.npmjs.org/@octokit/endpoint/-/endpoint-7.0.3.tgz",
"integrity": "sha512-57gRlb28bwTsdNXq+O3JTQ7ERmBTuik9+LelgcLIVfYwf235VHbN9QNo4kXExtp/h8T423cR5iJThKtFYxC7Lw==",
"dependencies": {
"@octokit/types": "^8.0.0",
"is-plain-object": "^5.0.0",
"universal-user-agent": "^6.0.0"
},
"engines": {
"node": ">= 14"
}
},
"node_modules/@octokit/graphql": {
"version": "5.0.4",
"resolved": "https://registry.npmjs.org/@octokit/graphql/-/graphql-5.0.4.tgz",
"integrity": "sha512-amO1M5QUQgYQo09aStR/XO7KAl13xpigcy/kI8/N1PnZYSS69fgte+xA4+c2DISKqUZfsh0wwjc2FaCt99L41A==",
"dependencies": {
"@octokit/request": "^6.0.0",
"@octokit/types": "^8.0.0",
"universal-user-agent": "^6.0.0"
},
"engines": {
"node": ">= 14"
}
},
"node_modules/@octokit/openapi-types": {
"version": "14.0.0",
"resolved": "https://registry.npmjs.org/@octokit/openapi-types/-/openapi-types-14.0.0.tgz",
"integrity": "sha512-HNWisMYlR8VCnNurDU6os2ikx0s0VyEjDYHNS/h4cgb8DeOxQ0n72HyinUtdDVxJhFy3FWLGl0DJhfEWk3P5Iw=="
},
"node_modules/@octokit/plugin-paginate-rest": {
"version": "5.0.1",
"resolved": "https://registry.npmjs.org/@octokit/plugin-paginate-rest/-/plugin-paginate-rest-5.0.1.tgz",
"integrity": "sha512-7A+rEkS70pH36Z6JivSlR7Zqepz3KVucEFVDnSrgHXzG7WLAzYwcHZbKdfTXHwuTHbkT1vKvz7dHl1+HNf6Qyw==",
"dependencies": {
"@octokit/types": "^8.0.0"
},
"engines": {
"node": ">= 14"
},
"peerDependencies": {
"@octokit/core": ">=4"
}
},
"node_modules/@octokit/plugin-request-log": {
"version": "1.0.4",
"resolved": "https://registry.npmjs.org/@octokit/plugin-request-log/-/plugin-request-log-1.0.4.tgz",
"integrity": "sha512-mLUsMkgP7K/cnFEw07kWqXGF5LKrOkD+lhCrKvPHXWDywAwuDUeDwWBpc69XK3pNX0uKiVt8g5z96PJ6z9xCFA==",
"peerDependencies": {
"@octokit/core": ">=3"
}
},
"node_modules/@octokit/plugin-rest-endpoint-methods": {
"version": "6.7.0",
"resolved": "https://registry.npmjs.org/@octokit/plugin-rest-endpoint-methods/-/plugin-rest-endpoint-methods-6.7.0.tgz",
"integrity": "sha512-orxQ0fAHA7IpYhG2flD2AygztPlGYNAdlzYz8yrD8NDgelPfOYoRPROfEyIe035PlxvbYrgkfUZIhSBKju/Cvw==",
"dependencies": {
"@octokit/types": "^8.0.0",
"deprecation": "^2.3.1"
},
"engines": {
"node": ">= 14"
},
"peerDependencies": {
"@octokit/core": ">=3"
}
},
"node_modules/@octokit/request": {
"version": "6.2.2",
"resolved": "https://registry.npmjs.org/@octokit/request/-/request-6.2.2.tgz",
"integrity": "sha512-6VDqgj0HMc2FUX2awIs+sM6OwLgwHvAi4KCK3mT2H2IKRt6oH9d0fej5LluF5mck1lRR/rFWN0YIDSYXYSylbw==",
"dependencies": {
"@octokit/endpoint": "^7.0.0",
"@octokit/request-error": "^3.0.0",
"@octokit/types": "^8.0.0",
"is-plain-object": "^5.0.0",
"node-fetch": "^2.6.7",
"universal-user-agent": "^6.0.0"
},
"engines": {
"node": ">= 14"
}
},
"node_modules/@octokit/request-error": {
"version": "3.0.2",
"resolved": "https://registry.npmjs.org/@octokit/request-error/-/request-error-3.0.2.tgz",
"integrity": "sha512-WMNOFYrSaX8zXWoJg9u/pKgWPo94JXilMLb2VManNOby9EZxrQaBe/QSC4a1TzpAlpxofg2X/jMnCyZgL6y7eg==",
"dependencies": {
"@octokit/types": "^8.0.0",
"deprecation": "^2.0.0",
"once": "^1.4.0"
},
"engines": {
"node": ">= 14"
}
},
"node_modules/@octokit/rest": {
"version": "19.0.5",
"resolved": "https://registry.npmjs.org/@octokit/rest/-/rest-19.0.5.tgz",
"integrity": "sha512-+4qdrUFq2lk7Va+Qff3ofREQWGBeoTKNqlJO+FGjFP35ZahP+nBenhZiGdu8USSgmq4Ky3IJ/i4u0xbLqHaeow==",
"dependencies": {
"@octokit/core": "^4.1.0",
"@octokit/plugin-paginate-rest": "^5.0.0",
"@octokit/plugin-request-log": "^1.0.4",
"@octokit/plugin-rest-endpoint-methods": "^6.7.0"
},
"engines": {
"node": ">= 14"
}
},
"node_modules/@octokit/types": {
"version": "8.0.0",
"resolved": "https://registry.npmjs.org/@octokit/types/-/types-8.0.0.tgz",
"integrity": "sha512-65/TPpOJP1i3K4lBJMnWqPUJ6zuOtzhtagDvydAWbEXpbFYA0oMKKyLb95NFZZP0lSh/4b6K+DQlzvYQJQQePg==",
"dependencies": {
"@octokit/openapi-types": "^14.0.0"
}
},
"node_modules/balanced-match": {
"version": "1.0.2",
"resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz",
"integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw=="
},
"node_modules/before-after-hook": {
"version": "2.2.3",
"resolved": "https://registry.npmjs.org/before-after-hook/-/before-after-hook-2.2.3.tgz",
"integrity": "sha512-NzUnlZexiaH/46WDhANlyR2bXRopNg4F/zuSA3OpZnllCUgRaOF2znDioDWrmbNVsuZk6l9pMquQB38cfBZwkQ=="
},
"node_modules/brace-expansion": {
"version": "1.1.11",
"resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz",
"integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==",
"dependencies": {
"balanced-match": "^1.0.0",
"concat-map": "0.0.1"
}
},
"node_modules/concat-map": {
"version": "0.0.1",
"resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz",
"integrity": "sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg=="
},
"node_modules/deprecation": {
"version": "2.3.1",
"resolved": "https://registry.npmjs.org/deprecation/-/deprecation-2.3.1.tgz",
"integrity": "sha512-xmHIy4F3scKVwMsQ4WnVaS8bHOx0DmVwRywosKhaILI0ywMDWPtBSku2HNxRvF7jtwDRsoEwYQSfbxj8b7RlJQ=="
},
"node_modules/is-plain-object": {
"version": "5.0.0",
"resolved": "https://registry.npmjs.org/is-plain-object/-/is-plain-object-5.0.0.tgz",
"integrity": "sha512-VRSzKkbMm5jMDoKLbltAkFQ5Qr7VDiTFGXxYFXXowVj387GeGNOCsOH6Msy00SGZ3Fp84b1Naa1psqgcCIEP5Q==",
"engines": {
"node": ">=0.10.0"
}
},
"node_modules/minimatch": {
"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"
},
"engines": {
"node": "*"
}
},
"node_modules/node-fetch": {
"version": "2.6.7",
"resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.6.7.tgz",
"integrity": "sha512-ZjMPFEfVx5j+y2yF35Kzx5sF7kDzxuDj6ziH4FFbOp87zKDZNx8yExJIb05OGF4Nlt9IHFIMBkRl41VdvcNdbQ==",
"dependencies": {
"whatwg-url": "^5.0.0"
},
"engines": {
"node": "4.x || >=6.0.0"
},
"peerDependencies": {
"encoding": "^0.1.0"
},
"peerDependenciesMeta": {
"encoding": {
"optional": true
}
}
},
"node_modules/once": {
"version": "1.4.0",
"resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz",
"integrity": "sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==",
"dependencies": {
"wrappy": "1"
}
},
"node_modules/tr46": {
"version": "0.0.3",
"resolved": "https://registry.npmjs.org/tr46/-/tr46-0.0.3.tgz",
"integrity": "sha512-N3WMsuqV66lT30CrXNbEjx4GEwlow3v6rr4mCcv6prnfwhS01rkgyFdjPNBYd9br7LpXV1+Emh01fHnq2Gdgrw=="
},
"node_modules/tunnel": {
"version": "0.0.6",
"resolved": "https://registry.npmjs.org/tunnel/-/tunnel-0.0.6.tgz",
"integrity": "sha512-1h/Lnq9yajKY2PEbBadPXj3VxsDDu844OnaAo52UVmIzIvwwtBPIuNvkjuzBlTWpfJyUbG3ez0KSBibQkj4ojg==",
"engines": {
"node": ">=0.6.11 <=0.7.0 || >=0.7.3"
}
},
"node_modules/universal-user-agent": {
"version": "6.0.0",
"resolved": "https://registry.npmjs.org/universal-user-agent/-/universal-user-agent-6.0.0.tgz",
"integrity": "sha512-isyNax3wXoKaulPDZWHQqbmIx1k2tb9fb3GGDBRxCscfYV2Ch7WxPArBsFEG8s/safwXTT7H4QGhaIkTp9447w=="
},
"node_modules/uuid": {
"version": "8.3.2",
"resolved": "https://registry.npmjs.org/uuid/-/uuid-8.3.2.tgz",
"integrity": "sha512-+NYs2QeMWy+GWFOEm9xnn6HCDp0l7QBD7ml8zLUmJ+93Q5NF0NocErnwkTkXVFNiX3/fpC6afS8Dhb/gz7R7eg==",
"bin": {
"uuid": "dist/bin/uuid"
}
},
"node_modules/webidl-conversions": {
"version": "3.0.1",
"resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-3.0.1.tgz",
"integrity": "sha512-2JAn3z8AR6rjK8Sm8orRC0h/bcl/DqL7tRPdGZ4I1CjdF+EaMLmYxBHyXuKL849eucPFhvBoxMsflfOb8kxaeQ=="
},
"node_modules/whatwg-url": {
"version": "5.0.0",
"resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-5.0.0.tgz",
"integrity": "sha512-saE57nupxk6v3HY35+jzBwYa0rKSy0XR8JSxZPwgLr7ys0IBzhGviA1/TUGJLmSVqs8pb9AnvICXEuOHLprYTw==",
"dependencies": {
"tr46": "~0.0.3",
"webidl-conversions": "^3.0.0"
}
},
"node_modules/wrappy": {
"version": "1.0.2",
"resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz",
"integrity": "sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ=="
}
},
"dependencies": {
"@actions/core": {
"version": "1.10.0",
"resolved": "https://registry.npmjs.org/@actions/core/-/core-1.10.0.tgz",
"integrity": "sha512-2aZDDa3zrrZbP5ZYg159sNoLRb61nQ7awl5pSvIq5Qpj81vwDzdMRKzkWJGJuwVvWpvZKx7vspJALyvaaIQyug==",
"requires": {
"@actions/http-client": "^2.0.1",
"uuid": "^8.3.2"
}
},
"@actions/glob": {
"version": "0.3.0",
"resolved": "https://registry.npmjs.org/@actions/glob/-/glob-0.3.0.tgz",
"integrity": "sha512-tJP1ZhF87fd6LBnaXWlahkyvdgvsLl7WnreW1EZaC8JWjpMXmzqWzQVe/IEYslrkT9ymibVrKyJN4UMD7uQM2w==",
"requires": {
"@actions/core": "^1.2.6",
"minimatch": "^3.0.4"
}
},
"@actions/http-client": {
"version": "2.0.1",
"resolved": "https://registry.npmjs.org/@actions/http-client/-/http-client-2.0.1.tgz",
"integrity": "sha512-PIXiMVtz6VvyaRsGY268qvj57hXQEpsYogYOu2nrQhlf+XCGmZstmuZBbAybUl1nQGnvS1k1eEsQ69ZoD7xlSw==",
"requires": {
"tunnel": "^0.0.6"
}
},
"@octokit/auth-token": {
"version": "3.0.2",
"resolved": "https://registry.npmjs.org/@octokit/auth-token/-/auth-token-3.0.2.tgz",
"integrity": "sha512-pq7CwIMV1kmzkFTimdwjAINCXKTajZErLB4wMLYapR2nuB/Jpr66+05wOTZMSCBXP6n4DdDWT2W19Bm17vU69Q==",
"requires": {
"@octokit/types": "^8.0.0"
}
},
"@octokit/core": {
"version": "4.1.0",
"resolved": "https://registry.npmjs.org/@octokit/core/-/core-4.1.0.tgz",
"integrity": "sha512-Czz/59VefU+kKDy+ZfDwtOIYIkFjExOKf+HA92aiTZJ6EfWpFzYQWw0l54ji8bVmyhc+mGaLUbSUmXazG7z5OQ==",
"requires": {
"@octokit/auth-token": "^3.0.0",
"@octokit/graphql": "^5.0.0",
"@octokit/request": "^6.0.0",
"@octokit/request-error": "^3.0.0",
"@octokit/types": "^8.0.0",
"before-after-hook": "^2.2.0",
"universal-user-agent": "^6.0.0"
}
},
"@octokit/endpoint": {
"version": "7.0.3",
"resolved": "https://registry.npmjs.org/@octokit/endpoint/-/endpoint-7.0.3.tgz",
"integrity": "sha512-57gRlb28bwTsdNXq+O3JTQ7ERmBTuik9+LelgcLIVfYwf235VHbN9QNo4kXExtp/h8T423cR5iJThKtFYxC7Lw==",
"requires": {
"@octokit/types": "^8.0.0",
"is-plain-object": "^5.0.0",
"universal-user-agent": "^6.0.0"
}
},
"@octokit/graphql": {
"version": "5.0.4",
"resolved": "https://registry.npmjs.org/@octokit/graphql/-/graphql-5.0.4.tgz",
"integrity": "sha512-amO1M5QUQgYQo09aStR/XO7KAl13xpigcy/kI8/N1PnZYSS69fgte+xA4+c2DISKqUZfsh0wwjc2FaCt99L41A==",
"requires": {
"@octokit/request": "^6.0.0",
"@octokit/types": "^8.0.0",
"universal-user-agent": "^6.0.0"
}
},
"@octokit/openapi-types": {
"version": "14.0.0",
"resolved": "https://registry.npmjs.org/@octokit/openapi-types/-/openapi-types-14.0.0.tgz",
"integrity": "sha512-HNWisMYlR8VCnNurDU6os2ikx0s0VyEjDYHNS/h4cgb8DeOxQ0n72HyinUtdDVxJhFy3FWLGl0DJhfEWk3P5Iw=="
},
"@octokit/plugin-paginate-rest": {
"version": "5.0.1",
"resolved": "https://registry.npmjs.org/@octokit/plugin-paginate-rest/-/plugin-paginate-rest-5.0.1.tgz",
"integrity": "sha512-7A+rEkS70pH36Z6JivSlR7Zqepz3KVucEFVDnSrgHXzG7WLAzYwcHZbKdfTXHwuTHbkT1vKvz7dHl1+HNf6Qyw==",
"requires": {
"@octokit/types": "^8.0.0"
}
},
"@octokit/plugin-request-log": {
"version": "1.0.4",
"resolved": "https://registry.npmjs.org/@octokit/plugin-request-log/-/plugin-request-log-1.0.4.tgz",
"integrity": "sha512-mLUsMkgP7K/cnFEw07kWqXGF5LKrOkD+lhCrKvPHXWDywAwuDUeDwWBpc69XK3pNX0uKiVt8g5z96PJ6z9xCFA==",
"requires": {}
},
"@octokit/plugin-rest-endpoint-methods": {
"version": "6.7.0",
"resolved": "https://registry.npmjs.org/@octokit/plugin-rest-endpoint-methods/-/plugin-rest-endpoint-methods-6.7.0.tgz",
"integrity": "sha512-orxQ0fAHA7IpYhG2flD2AygztPlGYNAdlzYz8yrD8NDgelPfOYoRPROfEyIe035PlxvbYrgkfUZIhSBKju/Cvw==",
"requires": {
"@octokit/types": "^8.0.0",
"deprecation": "^2.3.1"
}
},
"@octokit/request": {
"version": "6.2.2",
"resolved": "https://registry.npmjs.org/@octokit/request/-/request-6.2.2.tgz",
"integrity": "sha512-6VDqgj0HMc2FUX2awIs+sM6OwLgwHvAi4KCK3mT2H2IKRt6oH9d0fej5LluF5mck1lRR/rFWN0YIDSYXYSylbw==",
"requires": {
"@octokit/endpoint": "^7.0.0",
"@octokit/request-error": "^3.0.0",
"@octokit/types": "^8.0.0",
"is-plain-object": "^5.0.0",
"node-fetch": "^2.6.7",
"universal-user-agent": "^6.0.0"
}
},
"@octokit/request-error": {
"version": "3.0.2",
"resolved": "https://registry.npmjs.org/@octokit/request-error/-/request-error-3.0.2.tgz",
"integrity": "sha512-WMNOFYrSaX8zXWoJg9u/pKgWPo94JXilMLb2VManNOby9EZxrQaBe/QSC4a1TzpAlpxofg2X/jMnCyZgL6y7eg==",
"requires": {
"@octokit/types": "^8.0.0",
"deprecation": "^2.0.0",
"once": "^1.4.0"
}
},
"@octokit/rest": {
"version": "19.0.5",
"resolved": "https://registry.npmjs.org/@octokit/rest/-/rest-19.0.5.tgz",
"integrity": "sha512-+4qdrUFq2lk7Va+Qff3ofREQWGBeoTKNqlJO+FGjFP35ZahP+nBenhZiGdu8USSgmq4Ky3IJ/i4u0xbLqHaeow==",
"requires": {
"@octokit/core": "^4.1.0",
"@octokit/plugin-paginate-rest": "^5.0.0",
"@octokit/plugin-request-log": "^1.0.4",
"@octokit/plugin-rest-endpoint-methods": "^6.7.0"
}
},
"@octokit/types": {
"version": "8.0.0",
"resolved": "https://registry.npmjs.org/@octokit/types/-/types-8.0.0.tgz",
"integrity": "sha512-65/TPpOJP1i3K4lBJMnWqPUJ6zuOtzhtagDvydAWbEXpbFYA0oMKKyLb95NFZZP0lSh/4b6K+DQlzvYQJQQePg==",
"requires": {
"@octokit/openapi-types": "^14.0.0"
}
},
"balanced-match": {
"version": "1.0.2",
"resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz",
"integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw=="
},
"before-after-hook": {
"version": "2.2.3",
"resolved": "https://registry.npmjs.org/before-after-hook/-/before-after-hook-2.2.3.tgz",
"integrity": "sha512-NzUnlZexiaH/46WDhANlyR2bXRopNg4F/zuSA3OpZnllCUgRaOF2znDioDWrmbNVsuZk6l9pMquQB38cfBZwkQ=="
},
"brace-expansion": {
"version": "1.1.11",
"resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz",
"integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==",
"requires": {
"balanced-match": "^1.0.0",
"concat-map": "0.0.1"
}
},
"concat-map": {
"version": "0.0.1",
"resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz",
"integrity": "sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg=="
},
"deprecation": {
"version": "2.3.1",
"resolved": "https://registry.npmjs.org/deprecation/-/deprecation-2.3.1.tgz",
"integrity": "sha512-xmHIy4F3scKVwMsQ4WnVaS8bHOx0DmVwRywosKhaILI0ywMDWPtBSku2HNxRvF7jtwDRsoEwYQSfbxj8b7RlJQ=="
},
"is-plain-object": {
"version": "5.0.0",
"resolved": "https://registry.npmjs.org/is-plain-object/-/is-plain-object-5.0.0.tgz",
"integrity": "sha512-VRSzKkbMm5jMDoKLbltAkFQ5Qr7VDiTFGXxYFXXowVj387GeGNOCsOH6Msy00SGZ3Fp84b1Naa1psqgcCIEP5Q=="
},
"minimatch": {
"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"
}
},
"node-fetch": {
"version": "2.6.7",
"resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.6.7.tgz",
"integrity": "sha512-ZjMPFEfVx5j+y2yF35Kzx5sF7kDzxuDj6ziH4FFbOp87zKDZNx8yExJIb05OGF4Nlt9IHFIMBkRl41VdvcNdbQ==",
"requires": {
"whatwg-url": "^5.0.0"
}
},
"once": {
"version": "1.4.0",
"resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz",
"integrity": "sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==",
"requires": {
"wrappy": "1"
}
},
"tr46": {
"version": "0.0.3",
"resolved": "https://registry.npmjs.org/tr46/-/tr46-0.0.3.tgz",
"integrity": "sha512-N3WMsuqV66lT30CrXNbEjx4GEwlow3v6rr4mCcv6prnfwhS01rkgyFdjPNBYd9br7LpXV1+Emh01fHnq2Gdgrw=="
},
"tunnel": {
"version": "0.0.6",
"resolved": "https://registry.npmjs.org/tunnel/-/tunnel-0.0.6.tgz",
"integrity": "sha512-1h/Lnq9yajKY2PEbBadPXj3VxsDDu844OnaAo52UVmIzIvwwtBPIuNvkjuzBlTWpfJyUbG3ez0KSBibQkj4ojg=="
},
"universal-user-agent": {
"version": "6.0.0",
"resolved": "https://registry.npmjs.org/universal-user-agent/-/universal-user-agent-6.0.0.tgz",
"integrity": "sha512-isyNax3wXoKaulPDZWHQqbmIx1k2tb9fb3GGDBRxCscfYV2Ch7WxPArBsFEG8s/safwXTT7H4QGhaIkTp9447w=="
},
"uuid": {
"version": "8.3.2",
"resolved": "https://registry.npmjs.org/uuid/-/uuid-8.3.2.tgz",
"integrity": "sha512-+NYs2QeMWy+GWFOEm9xnn6HCDp0l7QBD7ml8zLUmJ+93Q5NF0NocErnwkTkXVFNiX3/fpC6afS8Dhb/gz7R7eg=="
},
"webidl-conversions": {
"version": "3.0.1",
"resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-3.0.1.tgz",
"integrity": "sha512-2JAn3z8AR6rjK8Sm8orRC0h/bcl/DqL7tRPdGZ4I1CjdF+EaMLmYxBHyXuKL849eucPFhvBoxMsflfOb8kxaeQ=="
},
"whatwg-url": {
"version": "5.0.0",
"resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-5.0.0.tgz",
"integrity": "sha512-saE57nupxk6v3HY35+jzBwYa0rKSy0XR8JSxZPwgLr7ys0IBzhGviA1/TUGJLmSVqs8pb9AnvICXEuOHLprYTw==",
"requires": {
"tr46": "~0.0.3",
"webidl-conversions": "^3.0.0"
}
},
"wrappy": {
"version": "1.0.2",
"resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz",
"integrity": "sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ=="
}
}
}
@@ -1,15 +0,0 @@
{
"name": "upload-release-assets",
"version": "1.0.0",
"description": "Upload Filament release assets",
"main": "index.js",
"scripts": {
"test": "echo \"Error: no test specified\" && exit 1"
},
"author": "Google",
"license": "Apache-2.0",
"dependencies": {
"@actions/glob": "^0.3.0",
"@octokit/rest": "^19.0.5"
}
}
+2 -2
View File
@@ -2263,7 +2263,7 @@ type aliases:
Name | Type | Description
:-----------------------------------|:--------:|:------------------------------------
**getResolution()** | float4 | Dimensions of the view's effective (physical) viewport in pixels: `width`, `height`, `1 / width`, `1 / height`. This might be different from `View::getViewport()` for instance because of added rendering guard-bands.
**getResolution()** | float4 | Dimensions of the view's effective viewport in pixels: `width`, `height`, `1 / width`, `1 / height`. This might be different from `View::getViewport()` for instance because of added rendering guard-bands. This can be used in conjunction with `getNormalizedViewportCoord()` to generate pixel coordinates.
**getWorldCameraPosition()** | float3 | Position of the camera/eye in world space
**getWorldOffset()** | float3 | The shift required to obtain API-level world space
**getTime()** | float | Current time as a remainder of 1 second. Yields a value between 0 and 1
@@ -2301,7 +2301,7 @@ The following APIs are only available from the fragment block:
**getWorldNormalVector()** | float3 | Normalized normal in world space, after bump mapping (must be used after `prepareMaterial()`)
**getWorldGeometricNormalVector()** | float3 | Normalized normal in world space, before bump mapping (can be used before `prepareMaterial()`)
**getWorldReflectedVector()** | float3 | Reflection of the view vector about the normal (must be used after `prepareMaterial()`)
**getNormalizedViewportCoord()** | float3 | Normalized user viewport position (i.e. NDC coordinates normalized to [0, 1] for the position, [1, 0] for the depth), can be used before `prepareMaterial()`). Because the user viewport is smaller than the actual physical viewport, these coordinates can be negative or superior to 1 in the non-visible area of the physical viewport.
**getNormalizedViewportCoord()** | float3 | Normalized viewport position (i.e. NDC coordinates normalized to [0, 1], can be used before `prepareMaterial()`)
**getNdotV()** | float | The result of `dot(normal, view)`, always strictly greater than 0 (must be used after `prepareMaterial()`)
**getColor()** | float4 | Interpolated color of the fragment, if the color attribute is required
**getUV0()** | float2 | First interpolated set of UV coordinates, only available if the uv0 attribute is required
+157 -343
View File
File diff suppressed because one or more lines are too long
Binary file not shown.
+1 -1
View File
@@ -261,7 +261,7 @@ class App {
this.renderer.setClearOptions({clearColor: kBackgroundColor, clear: true});
this.render = this.render.bind(this);
this.simpleViewer = new Filament.ViewerGui(engine, scene, view, kInitialSidebarWidth);
this.simpleViewer = new Filament.SimpleViewer(engine, scene, view, kInitialSidebarWidth);
this.mouseX = -1;
this.mouseY = -1;
Binary file not shown.
Binary file not shown.
Binary file not shown.
+153 -330
View File
File diff suppressed because one or more lines are too long
Binary file not shown.
-1
View File
File diff suppressed because one or more lines are too long
+20 -21
View File
@@ -10,7 +10,7 @@ html, body { height: 100%; }
body { margin: 0; overflow: hidden; }
#container { position: relative; height: 100%; }
canvas { position: absolute; width: 100%; height: 100%; }
#messages { position: absolute; width: 100%; height: 100%; padding-left: 10px; color:blue; pointer-events: none; }
#messages { position: absolute; width: 100%; height: 100%; padding-left: 10px; color:white; }
</style>
</head>
<body>
@@ -20,7 +20,7 @@ canvas { position: absolute; width: 100%; height: 100%; }
</div>
<script src="filament.js"></script>
<script src="gl-matrix-min.js"></script>
<script src="gltumble.min.js"></script>
<script src="https://unpkg.com/gltumble"></script>
<script>
const env = 'default_env';
@@ -43,19 +43,7 @@ class App {
const scene = this.scene = engine.createScene();
this.trackball = new Trackball(canvas, {startSpin: 0.035});
const messages = document.getElementById('messages');
canvas.addEventListener('pointerdown', evt => {
const x = evt.clientX;
const y = this.canvas.getBoundingClientRect().height - 1 - evt.clientY;
const dpr = window.devicePixelRatio;
this.view.pick(x * dpr, y * dpr, (results) => {
const name = this.asset.getName(results.renderable);
messages.innerText = name ? ('Picked ' + name) : '';
});
});
const indirectLight = this.ibl = engine.createIblFromKtx1(ibl_url);
const indirectLight = this.ibl = engine.createIblFromKtx(ibl_url);
this.scene.setIndirectLight(indirectLight);
const iblDirection = IndirectLight.getDirectionEstimate(indirectLight.shfloats);
@@ -70,7 +58,7 @@ class App {
mat3.fromRotation(mat, radians, [0, 1, 0]);
indirectLight.setRotation(mat);
const skybox = engine.createSkyFromKtx1(sky_url);
const skybox = engine.createSkyFromKtx(sky_url);
this.scene.setSkybox(skybox);
const sunlight = Filament.EntityManager.get().create();
@@ -88,17 +76,28 @@ class App {
const loader = this.loader = engine.createAssetLoader();
this.allowRefresh = false;
const asset = this.asset = loader.createAsset(mesh_url);
const asset = this.asset = loader.createAssetFromJson(mesh_url);
this.assetRoot = this.asset.getRoot();
const messages = document.getElementById('messages');
// Crudely indicate progress by printing the URI of each resource as it is loaded.
const onFetched = (uri) => messages.innerText += `Downloaded ${uri}\n`;
const onDone = () => {
this.allowRefresh = true;
// Clear the progress indication messages.
messages.innerText = "";
// Enable shadows on every renderable.
const entities = asset.getEntities();
const rm = engine.getRenderableManager();
for (const entity of entities) {
const instance = rm.getInstance(entity);
rm.setCastShadows(instance, true);
instance.delete();
}
// Hide the HUD.
messages.remove();
};
asset.loadResources(onDone, onFetched);
@@ -138,7 +137,7 @@ class App {
this.allowRefresh = false;
this.scene.removeEntities(this.asset.getEntities());
this.loader.destroyAsset(this.asset);
this.asset = this.loader.createAsset(mesh_url);
this.asset = this.loader.createAssetFromJson(mesh_url);
const onDone = () => { this.allowRefresh = true; }
this.asset.loadResources(onDone);
}
@@ -150,7 +149,7 @@ class App {
tcm.setTransform(inst, this.trackball.getMatrix());
inst.delete();
// Gradually add renderables to the scene as their textures become ready.
// Add renderable entities to the scene as they become ready.
let entity;
const popRenderable = () => {
entity = this.asset.popRenderable();
Binary file not shown.
+2 -2
View File
@@ -54,14 +54,14 @@ class App {
.build(engine, sunlight);
this.scene.addEntity(sunlight);
const indirectLight = this.ibl = engine.createIblFromKtx1(iblfile);
const indirectLight = this.ibl = engine.createIblFromKtx(iblfile);
this.scene.setIndirectLight(indirectLight);
const radians = 1.0;
indirectLight.setRotation(mat3.fromRotation(mat3.create(), radians, [0, 1, 0]))
indirectLight.setIntensity(10000);
const skybox = engine.createSkyFromKtx1(skyfile);
const skybox = engine.createSkyFromKtx(skyfile);
this.scene.setSkybox(skybox);
const material = engine.createMaterial('parquet.filamat');
Binary file not shown.
+128 -137
View File
@@ -49,7 +49,7 @@
</tr>
<tr>
<td align="left"><a href="#EntityManager">EntityManager</a></td>
<td align="left">Singleton used for constructing entities in Filament&#x27;s ECS.</td>
<td align="left">Singleton used for constructing entities in Filament's ECS.</td>
</tr>
<tr>
<td align="left"><a href="#Frustum">Frustum</a></td>
@@ -69,7 +69,7 @@
</tr>
<tr>
<td align="left"><a href="#KtxInfo">KtxInfo</a></td>
<td align="left">Property accessor for KTX1 header.</td>
<td align="left">Property accessor for KTX header.</td>
</tr>
<tr>
<td align="left"><a href="#LightManager%24Instance">LightManager$Instance</a></td>
@@ -89,7 +89,7 @@
</tr>
<tr>
<td align="left"><a href="#Renderer">Renderer</a></td>
<td align="left">Represents the platform&#x27;s native window.</td>
<td align="left">Represents the platform's native window.</td>
</tr>
<tr>
<td align="left"><a href="#Scene">Scene</a></td>
@@ -97,7 +97,7 @@
</tr>
<tr>
<td align="left"><a href="#SwapChain">SwapChain</a></td>
<td align="left">Represents the platform&#x27;s native rendering surface.</td>
<td align="left">Represents the platform's native rendering surface.</td>
</tr>
<tr>
<td align="left"><a href="#Texture">Texture</a></td>
@@ -190,10 +190,6 @@
<td align="left"></td>
</tr>
<tr>
<td align="left"><a href="#ColorGrading%24LutFormat">ColorGrading$LutFormat</a></td>
<td align="left"></td>
</tr>
<tr>
<td align="left"><a href="#ColorGrading%24QualityLevel">ColorGrading$QualityLevel</a></td>
<td align="left"></td>
</tr>
@@ -218,10 +214,6 @@
<td align="left"></td>
</tr>
<tr>
<td align="left"><a href="#FeatureLevel">FeatureLevel</a></td>
<td align="left"></td>
</tr>
<tr>
<td align="left"><a href="#Frustum%24Plane">Frustum$Plane</a></td>
<td align="left"></td>
</tr>
@@ -230,14 +222,6 @@
<td align="left"></td>
</tr>
<tr>
<td align="left"><a href="#Ktx2Reader%24Result">Ktx2Reader$Result</a></td>
<td align="left"></td>
</tr>
<tr>
<td align="left"><a href="#Ktx2Reader%24TransferFunction">Ktx2Reader$TransferFunction</a></td>
<td align="left"></td>
</tr>
<tr>
<td align="left"><a href="#LightManager%24Type">LightManager$Type</a></td>
<td align="left"></td>
</tr>
@@ -274,14 +258,6 @@
<td align="left"></td>
</tr>
<tr>
<td align="left"><a href="#StencilFace">StencilFace</a></td>
<td align="left"></td>
</tr>
<tr>
<td align="left"><a href="#StencilOperation">StencilOperation</a></td>
<td align="left"></td>
</tr>
<tr>
<td align="left"><a href="#Texture%24CubemapFace">Texture$CubemapFace</a></td>
<td align="left"></td>
</tr>
@@ -298,10 +274,6 @@
<td align="left"></td>
</tr>
<tr>
<td align="left"><a href="#TransparencyMode">TransparencyMode</a></td>
<td align="left"></td>
</tr>
<tr>
<td align="left"><a href="#VertexAttribute">VertexAttribute</a></td>
<td align="left"></td>
</tr>
@@ -314,6 +286,26 @@
<td align="left"></td>
</tr>
<tr>
<td align="left"><a href="#View%24AntiAliasing">View$AntiAliasing</a></td>
<td align="left"></td>
</tr>
<tr>
<td align="left"><a href="#View%24BlendMode">View$BlendMode</a></td>
<td align="left"></td>
</tr>
<tr>
<td align="left"><a href="#View%24BloomOptions%24BlendMode">View$BloomOptions$BlendMode</a></td>
<td align="left"></td>
</tr>
<tr>
<td align="left"><a href="#View%24DepthOfFieldOptions%24Filter">View$DepthOfFieldOptions$Filter</a></td>
<td align="left"></td>
</tr>
<tr>
<td align="left"><a href="#View%24QualityLevel">View$QualityLevel</a></td>
<td align="left"></td>
</tr>
<tr>
<td align="left"><a href="#WrapMode">WrapMode</a></td>
<td align="left"></td>
</tr>
@@ -379,7 +371,7 @@
<li><em>returns</em> an instance of <a href="#Camera">Camera</a></li>
</ul>
</li>
<li><strong>engine.createIblFromKtx1(buffer, options)</strong>
<li><strong>engine.createIblFromKtx(buffer, options)</strong>
<ul>
<li>Utility that creates an [IndirectLight] from a KTX file.</li>
<li><em>buffer</em> asset string, or Uint8Array, or <a href="#Buffer">Buffer</a> with KTX file contents</li>
@@ -403,7 +395,7 @@
<li><em>returns</em> an instance of <a href="#Scene">Scene</a></li>
</ul>
</li>
<li><strong>engine.createSkyFromKtx1(buffer, options)</strong>
<li><strong>engine.createSkyFromKtx(buffer, options)</strong>
<ul>
<li>Utility function that creates a [Skybox] from a KTX file.</li>
<li><em>buffer</em> asset string, or Uint8Array, or <a href="#Buffer">Buffer</a> with KTX file contents</li>
@@ -424,18 +416,10 @@
<li><em>returns</em> <a href="#Texture">Texture</a></li>
</ul>
</li>
<li><strong>engine.createTextureFromKtx1(buffer, options)</strong>
<li><strong>engine.createTextureFromKtx(buffer, options)</strong>
<ul>
<li>Utility function that creates a [Texture] from a KTX1 file.</li>
<li><em>buffer</em> asset string, or Uint8Array, or <a href="#Buffer">Buffer</a> with KTX1 file contents</li>
<li><em>options</em> Options dictionary.</li>
<li><em>returns</em> <a href="#Texture">Texture</a></li>
</ul>
</li>
<li><strong>engine.createTextureFromKtx2(buffer, options)</strong>
<ul>
<li>Utility function that creates a [Texture] from a KTX2 file.</li>
<li><em>buffer</em> asset string, or Uint8Array, or <a href="#Buffer">Buffer</a> with KTX2 file contents</li>
<li>Utility function that creates a [Texture] from a KTX file.</li>
<li><em>buffer</em> asset string, or Uint8Array, or <a href="#Buffer">Buffer</a> with KTX file contents</li>
<li><em>options</em> Options dictionary.</li>
<li><em>returns</em> <a href="#Texture">Texture</a></li>
</ul>
@@ -569,26 +553,57 @@
<li><em>entities</em> array of entities</li>
</ul>
</li>
<li><strong>engine.setAmbientOcclusionOptions()</strong></li>
<li><strong>engine.setBloomOptions()</strong></li>
<li><strong>engine.setAmbientOcclusionOptions(overrides)</strong>
<ul>
<li><em>overrides</em> Dictionary with one or more of the following properties: radius, power, bias, resolution, intensity, quality.</li>
</ul>
</li>
<li><strong>engine.setBloomOptions(overrides)</strong>
<ul>
<li><em>overrides</em> Dictionary with one or more of the following properties: enabled, strength, resolution, anomorphism, levels, blendMode, threshold, highlight.</li>
</ul>
</li>
<li><strong>engine.setClearOptions(overrides)</strong>
<ul>
<li><em>overrides</em> Dictionary with one or more of the following properties: clearColor, clear, discard.</li>
</ul>
</li>
<li><strong>engine.setDepthOfFieldOptions()</strong></li>
<li><strong>engine.setFogOptions()</strong></li>
<li><strong>engine.setGuardBandOptions()</strong></li>
<li><strong>engine.setMultiSampleAntiAliasingOptions()</strong></li>
<li><strong>engine.setScreenSpaceReflectionsOptions()</strong></li>
<li><strong>engine.setDepthOfFieldOptions(overrides)</strong>
<ul>
<li><em>overrides</em> Dictionary with one or more of the following properties: cocScale, maxApertureDiameter, enabled.</li>
</ul>
</li>
<li><strong>engine.setFogOptions(overrides)</strong>
<ul>
<li><em>overrides</em> Dictionary with one or more of the following properties: distance, maximumOpacity, height, heightFalloff, color, density, inScatteringStart,</li>
</ul>
</li>
<li><strong>engine.setMultiSampleAntiAliasingOptions(overrides)</strong>
<ul>
<li><em>overrides</em> Dictionary with one or more of the following properties: enabled, sampleCount, customResolve.</li>
</ul>
</li>
<li><strong>engine.setScreenSpaceReflectionsOptions(overrides)</strong>
<ul>
<li><em>overrides</em> Dictionary with one or more of the following properties: thickness, bias, maxDistance, stride, enabled.</li>
</ul>
</li>
<li><strong>engine.setShadowOptions(instance, overrides)</strong>
<ul>
<li><em>instance</em> Instance of a light component obtained from <code>getInstance</code>.</li>
<li><em>overrides</em> Dictionary with one or more of the following properties: mapSize, shadowCascades, constantBias, normalBias, shadowFar, shadowNearHint, shadowFarHint, stable, polygonOffsetConstant, polygonOffsetSlope, \</li>
</ul>
</li>
<li><strong>engine.setTemporalAntiAliasingOptions()</strong></li>
<li><strong>engine.setVignetteOptions()</strong></li>
<li><strong>engine.setTemporalAntiAliasingOptions(overrides)</strong>
<ul>
<li><em>overrides</em> Dictionary with one or more of the following properties: filterWidth, feedback, enabled.</li>
</ul>
</li>
<li><strong>engine.setVignetteOptions(overrides)</strong>
<ul>
<li><em>overrides</em> Dictionary with one or more of the following properties: midPoint, roundness, feather, color, enabled.</li>
</ul>
</li>
</ul>
</div>
<div class='classdoc'>
@@ -603,11 +618,11 @@
</ul>
<p>To create an entity with no components, use <a href="#EntityManager">EntityManager</a>.
TODO: It would be better to expose these as JS numbers rather than as JS objects.
This would also be more consistent with Filament&#x27;s Java bindings.</p>
This would also be more consistent with Filament's Java bindings.</p>
</div>
<div class='classdoc'>
<h2>class <a id='EntityManager' href='#EntityManager'>EntityManager</a></h2>
<p>Singleton used for constructing entities in Filament&#x27;s ECS.</p>
<p>Singleton used for constructing entities in Filament's ECS.</p>
<ul>
<li><strong>entityManager.create()</strong>
<ul>
@@ -712,9 +727,15 @@ wrapper rather than interacting with <code>Ktx1Bundle</code> directly.</p>
</div>
<div class='classdoc'>
<h2>class <a id='KtxInfo' href='#KtxInfo'>KtxInfo</a></h2>
<p>Property accessor for KTX1 header.</p>
<p>Property accessor for KTX header.</p>
<ul>
<li><strong>ktxInfo.createAsset(buffer)</strong>
<li><strong>ktxInfo.createAssetFromBinary(buffer)</strong>
<ul>
<li><em>buffer</em> asset string, or Uint8Array, or <a href="#Buffer">Buffer</a></li>
<li><em>returns</em> an instance of <a href="#FilamentAsset">FilamentAsset</a></li>
</ul>
</li>
<li><strong>ktxInfo.createAssetFromJson(buffer)</strong>
<ul>
<li><em>buffer</em> asset string, or Uint8Array, or <a href="#Buffer">Buffer</a></li>
<li><em>returns</em> an instance of <a href="#FilamentAsset">FilamentAsset</a></li>
@@ -741,7 +762,7 @@ properties.</p>
</ul>
</li>
</ul>
<p>Be sure to call the instance&#x27;s <code>delete</code> method when you&#x27;re done with it.</p>
<p>Be sure to call the instance's <code>delete</code> method when you're done with it.</p>
</div>
<div class='classdoc'>
<h2>class <a id='PixelBufferDescriptor' href='#PixelBufferDescriptor'>PixelBufferDescriptor</a></h2>
@@ -779,11 +800,11 @@ properties.</p>
</ul>
</li>
</ul>
<p>Be sure to call the instance&#x27;s <code>delete</code> method when you&#x27;re done with it.</p>
<p>Be sure to call the instance's <code>delete</code> method when you're done with it.</p>
</div>
<div class='classdoc'>
<h2>class <a id='Renderer' href='#Renderer'>Renderer</a></h2>
<p>Represents the platform&#x27;s native window.</p>
<p>Represents the platform's native window.</p>
<ul>
<li><strong>renderer.render(swapChain, view)</strong>
<ul>
@@ -802,7 +823,7 @@ properties.</p>
</div>
<div class='classdoc'>
<h2>class <a id='SwapChain' href='#SwapChain'>SwapChain</a></h2>
<p>Represents the platform&#x27;s native rendering surface.</p>
<p>Represents the platform's native rendering surface.</p>
<p>See also the <a href="#Engine">Engine</a> methods <code>createSwapChain</code> and <code>destroySwapChain</code>.</p>
</div>
<div class='classdoc'>
@@ -846,7 +867,7 @@ properties.</p>
</ul>
</li>
</ul>
<p>Be sure to call the instance&#x27;s <code>delete</code> method when you&#x27;re done with it.</p>
<p>Be sure to call the instance's <code>delete</code> method when you're done with it.</p>
</div>
<div class='classdoc'>
<h2>class <a id='VertexBuffer' href='#VertexBuffer'>VertexBuffer</a></h2>
@@ -943,7 +964,7 @@ See also the <a href="#Engine">Engine</a> methods <code>createView</code> and <c
<ul>
<li><strong>assets</strong>
<ul>
<li>Array of strings containing URL&#x27;s of required assets.</li>
<li>Array of strings containing URL's of required assets.</li>
</ul>
</li>
<li><strong>onDone</strong>
@@ -953,7 +974,7 @@ See also the <a href="#Engine">Engine</a> methods <code>createView</code> and <c
</li>
<li><strong>onFetched</strong>
<ul>
<li>optional callback that&#x27;s invoked after each asset is downloaded.</li>
<li>optional callback that's invoked after each asset is downloaded.</li>
</ul>
</li>
</ul>
@@ -1000,7 +1021,7 @@ useful for compressed textures. For example, some platforms accept ETC and other
<ul>
<li><strong>assets</strong>
<ul>
<li>Array of strings containing URL&#x27;s of required assets.</li>
<li>Array of strings containing URL's of required assets.</li>
</ul>
</li>
<li><strong>onready</strong>
@@ -1009,12 +1030,12 @@ useful for compressed textures. For example, some platforms accept ETC and other
</ul>
</li>
</ul>
<p>All JavaScript clients must call the init function, passing in a list of asset URL&#x27;s and a
<p>All JavaScript clients must call the init function, passing in a list of asset URL's and a
callback. This callback gets invoked only after all assets have been downloaded and the Filament
WebAssembly module has been loaded. Clients should only pass asset URL&#x27;s that absolutely must
WebAssembly module has been loaded. Clients should only pass asset URL's that absolutely must
be ready at initialization time.
When the callback is called, each downloaded asset is available in the <code>Filament.assets</code> global
object, which contains a mapping from URL&#x27;s to Uint8Array objects.</p>
object, which contains a mapping from URL's to Uint8Array objects.</p>
</div>
<div class='funcdoc'>
<h2>function <a id='loadMathExtensions' href='#loadMathExtensions'>loadMathExtensions</a>()</h2>
@@ -1064,13 +1085,6 @@ This defines the following functions:</p>
</ul>
</div>
<div class='enumdoc'>
<h2>enum <a id='ColorGrading$LutFormat' href='#ColorGrading$LutFormat'>ColorGrading$LutFormat</a></h2>
<ul>
<li>INTEGER</li>
<li>FLOAT</li>
</ul>
</div>
<div class='enumdoc'>
<h2>enum <a id='ColorGrading$QualityLevel' href='#ColorGrading$QualityLevel'>ColorGrading$QualityLevel</a></h2>
<ul>
<li>LOW</li>
@@ -1126,10 +1140,6 @@ This defines the following functions:</p>
<li>DXT1_RGBA</li>
<li>DXT3_RGBA</li>
<li>DXT5_RGBA</li>
<li>DXT1_SRGB</li>
<li>DXT1_SRGBA</li>
<li>DXT3_SRGBA</li>
<li>DXT5_SRGBA</li>
<li>RGBA_ASTC_4x4</li>
<li>RGBA_ASTC_5x4</li>
<li>RGBA_ASTC_5x5</li>
@@ -1170,13 +1180,6 @@ This defines the following functions:</p>
</ul>
</div>
<div class='enumdoc'>
<h2>enum <a id='FeatureLevel' href='#FeatureLevel'>FeatureLevel</a></h2>
<ul>
<li>FEATURE_LEVEL_1</li>
<li>FEATURE_LEVEL_2</li>
</ul>
</div>
<div class='enumdoc'>
<h2>enum <a id='Frustum$Plane' href='#Frustum$Plane'>Frustum$Plane</a></h2>
<ul>
<li>LEFT</li>
@@ -1195,23 +1198,6 @@ This defines the following functions:</p>
</ul>
</div>
<div class='enumdoc'>
<h2>enum <a id='Ktx2Reader$Result' href='#Ktx2Reader$Result'>Ktx2Reader$Result</a></h2>
<ul>
<li>SUCCESS</li>
<li>COMPRESSED_TRANSCODE_FAILURE</li>
<li>UNCOMPRESSED_TRANSCODE_FAILURE</li>
<li>FORMAT_UNSUPPORTED</li>
<li>FORMAT_ALREADY_REQUESTED</li>
</ul>
</div>
<div class='enumdoc'>
<h2>enum <a id='Ktx2Reader$TransferFunction' href='#Ktx2Reader$TransferFunction'>Ktx2Reader$TransferFunction</a></h2>
<ul>
<li>LINEAR</li>
<li>sRGB</li>
</ul>
</div>
<div class='enumdoc'>
<h2>enum <a id='LightManager$Type' href='#LightManager$Type'>LightManager$Type</a></h2>
<ul>
<li>SUN</li>
@@ -1289,6 +1275,7 @@ This defines the following functions:</p>
<li>LINE_STRIP</li>
<li>TRIANGLES</li>
<li>TRIANGLE_STRIP</li>
<li>NONE</li>
</ul>
</div>
<div class='enumdoc'>
@@ -1308,27 +1295,6 @@ This defines the following functions:</p>
</ul>
</div>
<div class='enumdoc'>
<h2>enum <a id='StencilFace' href='#StencilFace'>StencilFace</a></h2>
<ul>
<li>FRONT</li>
<li>BACK</li>
<li>FRONT_AND_BACK</li>
</ul>
</div>
<div class='enumdoc'>
<h2>enum <a id='StencilOperation' href='#StencilOperation'>StencilOperation</a></h2>
<ul>
<li>KEEP</li>
<li>ZERO</li>
<li>REPLACE</li>
<li>INCR_CLAMP</li>
<li>INCR_WRAP</li>
<li>DECR_CLAMP</li>
<li>DECR_WRAP</li>
<li>INVERT</li>
</ul>
</div>
<div class='enumdoc'>
<h2>enum <a id='Texture$CubemapFace' href='#Texture$CubemapFace'>Texture$CubemapFace</a></h2>
<ul>
<li>POSITIVE_X</li>
@@ -1411,10 +1377,6 @@ This defines the following functions:</p>
<li>DXT1_RGBA</li>
<li>DXT3_RGBA</li>
<li>DXT5_RGBA</li>
<li>DXT1_SRGB</li>
<li>DXT1_SRGBA</li>
<li>DXT3_SRGBA</li>
<li>DXT5_SRGBA</li>
<li>RGBA_ASTC_4x4</li>
<li>RGBA_ASTC_5x4</li>
<li>RGBA_ASTC_5x5</li>
@@ -1466,14 +1428,6 @@ This defines the following functions:</p>
</ul>
</div>
<div class='enumdoc'>
<h2>enum <a id='TransparencyMode' href='#TransparencyMode'>TransparencyMode</a></h2>
<ul>
<li>DEFAULT</li>
<li>TWO_PASSES_ONE_SIDE</li>
<li>TWO_PASSES_TWO_SIDES</li>
</ul>
</div>
<div class='enumdoc'>
<h2>enum <a id='VertexAttribute' href='#VertexAttribute'>VertexAttribute</a></h2>
<ul>
<li>POSITION</li>
@@ -1540,6 +1494,43 @@ This defines the following functions:</p>
</ul>
</div>
<div class='enumdoc'>
<h2>enum <a id='View$AntiAliasing' href='#View$AntiAliasing'>View$AntiAliasing</a></h2>
<ul>
<li>NONE</li>
<li>FXAA</li>
</ul>
</div>
<div class='enumdoc'>
<h2>enum <a id='View$BlendMode' href='#View$BlendMode'>View$BlendMode</a></h2>
<ul>
<li>OPAQUE</li>
<li>TRANSLUCENT</li>
</ul>
</div>
<div class='enumdoc'>
<h2>enum <a id='View$BloomOptions$BlendMode' href='#View$BloomOptions$BlendMode'>View$BloomOptions$BlendMode</a></h2>
<ul>
<li>ADD</li>
<li>INTERPOLATE</li>
</ul>
</div>
<div class='enumdoc'>
<h2>enum <a id='View$DepthOfFieldOptions$Filter' href='#View$DepthOfFieldOptions$Filter'>View$DepthOfFieldOptions$Filter</a></h2>
<ul>
<li>NONE</li>
<li>MEDIAN</li>
</ul>
</div>
<div class='enumdoc'>
<h2>enum <a id='View$QualityLevel' href='#View$QualityLevel'>View$QualityLevel</a></h2>
<ul>
<li>LOW</li>
<li>MEDIUM</li>
<li>HIGH</li>
<li>ULTRA</li>
</ul>
</div>
<div class='enumdoc'>
<h2>enum <a id='WrapMode' href='#WrapMode'>WrapMode</a></h2>
<ul>
<li>CLAMP_TO_EDGE</li>
Binary file not shown.
+20 -27
View File
@@ -16,14 +16,17 @@ canvas { touch-action: none; width: 100%; height: 100%; }
<script src="gl-matrix-min.js"></script>
<script>
const albedo_suffix = Filament.getSupportedFormatSuffix('astc s3tc_srgb');
const texture_suffix = Filament.getSupportedFormatSuffix('etc');
const env = 'default_env'
const ibl_url = `${env}/${env}_ibl.ktx`;
const sky_url = `${env}/${env}_skybox.ktx`;
const albedo_url = `albedo.ktx2`;
const ao_url = `ao.ktx2`;
const metallic_url = `metallic.ktx2`;
const normal_url = `normal.ktx2`;
const roughness_url = `roughness.ktx2`;
const albedo_url = `albedo${albedo_suffix}.ktx`;
const ao_url = `ao${texture_suffix}.ktx`;
const metallic_url = `metallic${texture_suffix}.ktx`;
const normal_url = `normal${texture_suffix}.ktx`;
const roughness_url = `roughness${texture_suffix}.ktx`;
const filamat_url = 'textured.filamat';
const filamesh_url = 'suzanne.filamesh';
@@ -38,13 +41,12 @@ Filament.init([
ibl_url,
sky_url
], () => {
window.AttributeType = Filament.VertexBuffer$AttributeType;
window.Format = Filament.Texture$InternalFormat;
window.Fov = Filament.Camera$Fov;
window.IndexType = Filament.IndexBuffer$IndexType;
window.LightType = Filament.LightManager$Type;
window.PrimitiveType = Filament.RenderableManager$PrimitiveType;
window.VertexAttribute = Filament.VertexAttribute;
window.AttributeType = Filament.VertexBuffer$AttributeType;
window.PrimitiveType = Filament.RenderableManager$PrimitiveType;
window.IndexType = Filament.IndexBuffer$IndexType;
window.Fov = Filament.Camera$Fov;
window.LightType = Filament.LightManager$Type;
window.app = new App(document.getElementsByTagName('canvas')[0]);
});
@@ -66,11 +68,11 @@ class App {
.build(engine, sunlight);
this.scene.addEntity(sunlight);
const indirectLight = this.ibl = engine.createIblFromKtx1(ibl_url);
const indirectLight = this.ibl = engine.createIblFromKtx(ibl_url);
this.scene.setIndirectLight(indirectLight);
indirectLight.setIntensity(80000);
const skybox = engine.createSkyFromKtx1(sky_url);
const skybox = engine.createSkyFromKtx(sky_url);
this.scene.setSkybox(skybox);
const material = engine.createMaterial(filamat_url);
@@ -81,20 +83,11 @@ class App {
Filament.MagFilter.LINEAR,
Filament.WrapMode.CLAMP_TO_EDGE);
// Filament requests support for the following extensions, but none are guaranteed.
// WEBGL_compressed_texture_s3tc, WEBGL_compressed_texture_s3tc_srgb
// WEBGL_compressed_texture_astc, WEBGL_compressed_texture_etc
const albedo = engine.createTextureFromKtx2(albedo_url, {
srgb: true,
formats: [ Format.DXT5_RGBA, Format.DXT5_SRGBA, Format.ETC2_EAC_SRGBA8 ]
});
console.assert(albedo, "Unable to create albedo texture.")
const roughness = engine.createTextureFromKtx2(roughness_url);
const metallic = engine.createTextureFromKtx2(metallic_url);
const ao = engine.createTextureFromKtx2(ao_url);
const normal = engine.createTextureFromKtx2(normal_url);
const albedo = engine.createTextureFromKtx(albedo_url, {srgb: true});
const roughness = engine.createTextureFromKtx(roughness_url);
const metallic = engine.createTextureFromKtx(metallic_url);
const normal = engine.createTextureFromKtx(normal_url);
const ao = engine.createTextureFromKtx(ao_url);
matinstance.setTextureParameter('albedo', albedo, sampler)
matinstance.setTextureParameter('roughness', roughness, sampler)
Binary file not shown.
Binary file not shown.
+155 -155
View File
@@ -10,7 +10,7 @@
textures.</p>
<p>For starters, create a text file called <code>redball.html</code> and copy over the HTML that we used in the
<a href="tutorial_triangle.html">previous tutorial</a>. Change the last script tag from <code>triangle.js</code> to <code>redball.js</code>.</p>
<p>Next you&#x27;ll need to get a couple command-line tools: <code>matc</code> and <code>cmgen</code>. You can find these in the
<p>Next you'll need to get a couple command-line tools: <code>matc</code> and <code>cmgen</code>. You can find these in the
appropriate <a href="//github.com/google/filament/releases">Filament release</a>. You should choose the
archive that corresponds to your development machine rather than the one for web, and the version
that matches the <code>unpkg.com/filament@x.x.x</code> url in the script tag of <code>redball.html</code> (you may check
@@ -19,7 +19,7 @@ out the last available release of <a href="https://www.npmjs.com/package/filamen
<p>The <code>matc</code> tool consumes a text file containing a high-level description of a PBR material, and
produces a binary material package that contains shader code and associated metadata. For more
information, see the official document describing the <a href="https://google.github.io/filament/Materials.md.html">Filament Material System</a>.</p>
<p>Let&#x27;s try out <code>matc</code>. Create the following file in your favorite text editor and call it
<p>Let's try out <code>matc</code>. Create the following file in your favorite text editor and call it
<code>plastic.mat</code>.</p>
<div class="highlight" style="background: #f8f8f8"><pre style="line-height: 125%;"><span></span>material {
name : Lit,
@@ -47,10 +47,10 @@ fragment {
<div class="highlight" style="background: #f8f8f8"><pre style="line-height: 125%;"><span></span>matc -a opengl -p mobile -o plastic.filamat plastic.mat
</pre></div>
<p>You should now have a material archive in your working directory, which we&#x27;ll use later in the
<p>You should now have a material archive in your working directory, which we'll use later in the
tutorial.</p>
<h2>Bake environment map</h2>
<p>Next we&#x27;ll use Filament&#x27;s <code>cmgen</code> tool to consume a HDR environment map in latlong format, and
<p>Next we'll use Filament's <code>cmgen</code> tool to consume a HDR environment map in latlong format, and
produce two cubemap files: a mipmapped IBL and a blurry skybox.</p>
<p>Download <a href="//github.com/google/filament/blob/main/third_party/environments/pillars_2k.hdr">pillars_2k.hdr</a>, then invoke the following command in your terminal.</p>
<div class="highlight" style="background: #f8f8f8"><pre style="line-height: 125%;"><span></span>cmgen -x pillars_2k --format<span style="color: #666666">=</span>ktx --size<span style="color: #666666">=256</span> --extract-blur<span style="color: #666666">=0</span>.1 pillars_2k.hdr
@@ -61,84 +61,84 @@ well as a text file with spherical harmonics coefficients. You can discard the t
IBL KTX contains these coefficients in its metadata.</p>
<h2>Create JavaScript</h2>
<p>Next, create <code>redball.js</code> with the following content.</p>
<div class="highlight" style="background: #f8f8f8"><pre style="line-height: 125%;"><span></span><span style="color: #008000; font-weight: bold">const</span><span style="color: #bbbbbb"> </span>environ<span style="color: #bbbbbb"> </span><span style="color: #666666">=</span><span style="color: #bbbbbb"> </span><span style="color: #BA2121">&#39;pillars_2k&#39;</span>;<span style="color: #bbbbbb"></span>
<span style="color: #008000; font-weight: bold">const</span><span style="color: #bbbbbb"> </span>ibl_url<span style="color: #bbbbbb"> </span><span style="color: #666666">=</span><span style="color: #bbbbbb"> </span><span style="color: #BA2121">`</span><span style="color: #A45A77; font-weight: bold">${</span>environ<span style="color: #A45A77; font-weight: bold">}</span><span style="color: #BA2121">/</span><span style="color: #A45A77; font-weight: bold">${</span>environ<span style="color: #A45A77; font-weight: bold">}</span><span style="color: #BA2121">_ibl.ktx`</span>;<span style="color: #bbbbbb"></span>
<span style="color: #008000; font-weight: bold">const</span><span style="color: #bbbbbb"> </span>sky_url<span style="color: #bbbbbb"> </span><span style="color: #666666">=</span><span style="color: #bbbbbb"> </span><span style="color: #BA2121">`</span><span style="color: #A45A77; font-weight: bold">${</span>environ<span style="color: #A45A77; font-weight: bold">}</span><span style="color: #BA2121">/</span><span style="color: #A45A77; font-weight: bold">${</span>environ<span style="color: #A45A77; font-weight: bold">}</span><span style="color: #BA2121">_skybox.ktx`</span>;<span style="color: #bbbbbb"></span>
<span style="color: #008000; font-weight: bold">const</span><span style="color: #bbbbbb"> </span>filamat_url<span style="color: #bbbbbb"> </span><span style="color: #666666">=</span><span style="color: #bbbbbb"> </span><span style="color: #BA2121">&#39;plastic.filamat&#39;</span><span style="color: #bbbbbb"></span>
<div class="highlight" style="background: #f8f8f8"><pre style="line-height: 125%;"><span></span><span style="color: #008000; font-weight: bold">const</span> environ <span style="color: #666666">=</span> <span style="color: #BA2121">&#39;pillars_2k&#39;</span>;
<span style="color: #008000; font-weight: bold">const</span> ibl_url <span style="color: #666666">=</span> <span style="color: #BA2121">`</span><span style="color: #A45A77; font-weight: bold">${</span>environ<span style="color: #A45A77; font-weight: bold">}</span><span style="color: #BA2121">/</span><span style="color: #A45A77; font-weight: bold">${</span>environ<span style="color: #A45A77; font-weight: bold">}</span><span style="color: #BA2121">_ibl.ktx`</span>;
<span style="color: #008000; font-weight: bold">const</span> sky_url <span style="color: #666666">=</span> <span style="color: #BA2121">`</span><span style="color: #A45A77; font-weight: bold">${</span>environ<span style="color: #A45A77; font-weight: bold">}</span><span style="color: #BA2121">/</span><span style="color: #A45A77; font-weight: bold">${</span>environ<span style="color: #A45A77; font-weight: bold">}</span><span style="color: #BA2121">_skybox.ktx`</span>;
<span style="color: #008000; font-weight: bold">const</span> filamat_url <span style="color: #666666">=</span> <span style="color: #BA2121">&#39;plastic.filamat&#39;</span>
Filament.init([<span style="color: #bbbbbb"> </span>filamat_url,<span style="color: #bbbbbb"> </span>ibl_url,<span style="color: #bbbbbb"> </span>sky_url<span style="color: #bbbbbb"> </span>],<span style="color: #bbbbbb"> </span>()<span style="color: #bbbbbb"> </span>=&gt;<span style="color: #bbbbbb"> </span>{<span style="color: #bbbbbb"></span>
<span style="color: #bbbbbb"> </span><span style="color: #3D7B7B; font-style: italic">// Create some global aliases to enums for convenience.</span><span style="color: #bbbbbb"></span>
<span style="color: #bbbbbb"> </span><span style="color: #008000">window</span>.VertexAttribute<span style="color: #bbbbbb"> </span><span style="color: #666666">=</span><span style="color: #bbbbbb"> </span>Filament.VertexAttribute;<span style="color: #bbbbbb"></span>
<span style="color: #bbbbbb"> </span><span style="color: #008000">window</span>.AttributeType<span style="color: #bbbbbb"> </span><span style="color: #666666">=</span><span style="color: #bbbbbb"> </span>Filament.VertexBuffer$AttributeType;<span style="color: #bbbbbb"></span>
<span style="color: #bbbbbb"> </span><span style="color: #008000">window</span>.PrimitiveType<span style="color: #bbbbbb"> </span><span style="color: #666666">=</span><span style="color: #bbbbbb"> </span>Filament.RenderableManager$PrimitiveType;<span style="color: #bbbbbb"></span>
<span style="color: #bbbbbb"> </span><span style="color: #008000">window</span>.IndexType<span style="color: #bbbbbb"> </span><span style="color: #666666">=</span><span style="color: #bbbbbb"> </span>Filament.IndexBuffer$IndexType;<span style="color: #bbbbbb"></span>
<span style="color: #bbbbbb"> </span><span style="color: #008000">window</span>.Fov<span style="color: #bbbbbb"> </span><span style="color: #666666">=</span><span style="color: #bbbbbb"> </span>Filament.Camera$Fov;<span style="color: #bbbbbb"></span>
<span style="color: #bbbbbb"> </span><span style="color: #008000">window</span>.LightType<span style="color: #bbbbbb"> </span><span style="color: #666666">=</span><span style="color: #bbbbbb"> </span>Filament.LightManager$Type;<span style="color: #bbbbbb"></span>
Filament.init([ filamat_url, ibl_url, sky_url ], () =&gt; {
<span style="color: #3D7B7B; font-style: italic">// Create some global aliases to enums for convenience.</span>
<span style="color: #008000">window</span>.VertexAttribute <span style="color: #666666">=</span> Filament.VertexAttribute;
<span style="color: #008000">window</span>.AttributeType <span style="color: #666666">=</span> Filament.VertexBuffer$AttributeType;
<span style="color: #008000">window</span>.PrimitiveType <span style="color: #666666">=</span> Filament.RenderableManager$PrimitiveType;
<span style="color: #008000">window</span>.IndexType <span style="color: #666666">=</span> Filament.IndexBuffer$IndexType;
<span style="color: #008000">window</span>.Fov <span style="color: #666666">=</span> Filament.Camera$Fov;
<span style="color: #008000">window</span>.LightType <span style="color: #666666">=</span> Filament.LightManager$Type;
<span style="color: #bbbbbb"> </span><span style="color: #3D7B7B; font-style: italic">// Obtain the canvas DOM object and pass it to the App.</span><span style="color: #bbbbbb"></span>
<span style="color: #bbbbbb"> </span><span style="color: #008000; font-weight: bold">const</span><span style="color: #bbbbbb"> </span>canvas<span style="color: #bbbbbb"> </span><span style="color: #666666">=</span><span style="color: #bbbbbb"> </span><span style="color: #008000">document</span>.getElementsByTagName(<span style="color: #BA2121">&#39;canvas&#39;</span>)[<span style="color: #666666">0</span>];<span style="color: #bbbbbb"></span>
<span style="color: #bbbbbb"> </span><span style="color: #008000">window</span>.app<span style="color: #bbbbbb"> </span><span style="color: #666666">=</span><span style="color: #bbbbbb"> </span><span style="color: #AA22FF; font-weight: bold">new</span><span style="color: #bbbbbb"> </span>App(canvas);<span style="color: #bbbbbb"></span>
}<span style="color: #bbbbbb"> </span>);<span style="color: #bbbbbb"></span>
<span style="color: #3D7B7B; font-style: italic">// Obtain the canvas DOM object and pass it to the App.</span>
<span style="color: #008000; font-weight: bold">const</span> canvas <span style="color: #666666">=</span> <span style="color: #008000">document</span>.getElementsByTagName(<span style="color: #BA2121">&#39;canvas&#39;</span>)[<span style="color: #666666">0</span>];
<span style="color: #008000">window</span>.app <span style="color: #666666">=</span> <span style="color: #AA22FF; font-weight: bold">new</span> App(canvas);
} );
<span style="color: #008000; font-weight: bold">class</span><span style="color: #bbbbbb"> </span>App<span style="color: #bbbbbb"> </span>{<span style="color: #bbbbbb"></span>
<span style="color: #bbbbbb"> </span><span style="color: #008000; font-weight: bold">constructor</span>(canvas)<span style="color: #bbbbbb"> </span>{<span style="color: #bbbbbb"></span>
<span style="color: #bbbbbb"> </span><span style="color: #008000; font-weight: bold">this</span>.canvas<span style="color: #bbbbbb"> </span><span style="color: #666666">=</span><span style="color: #bbbbbb"> </span>canvas;<span style="color: #bbbbbb"></span>
<span style="color: #bbbbbb"> </span><span style="color: #008000; font-weight: bold">const</span><span style="color: #bbbbbb"> </span>engine<span style="color: #bbbbbb"> </span><span style="color: #666666">=</span><span style="color: #bbbbbb"> </span><span style="color: #008000; font-weight: bold">this</span>.engine<span style="color: #bbbbbb"> </span><span style="color: #666666">=</span><span style="color: #bbbbbb"> </span>Filament.Engine.create(canvas);<span style="color: #bbbbbb"></span>
<span style="color: #bbbbbb"> </span><span style="color: #008000; font-weight: bold">const</span><span style="color: #bbbbbb"> </span>scene<span style="color: #bbbbbb"> </span><span style="color: #666666">=</span><span style="color: #bbbbbb"> </span>engine.createScene();<span style="color: #bbbbbb"></span>
<span style="color: #008000; font-weight: bold">class</span> App {
<span style="color: #008000; font-weight: bold">constructor</span>(canvas) {
<span style="color: #008000; font-weight: bold">this</span>.canvas <span style="color: #666666">=</span> canvas;
<span style="color: #008000; font-weight: bold">const</span> engine <span style="color: #666666">=</span> <span style="color: #008000; font-weight: bold">this</span>.engine <span style="color: #666666">=</span> Filament.Engine.create(canvas);
<span style="color: #008000; font-weight: bold">const</span> scene <span style="color: #666666">=</span> engine.createScene();
<span style="color: #bbbbbb"> </span><span style="color: #3D7B7B; font-style: italic">// TODO: create material</span><span style="color: #bbbbbb"></span>
<span style="color: #bbbbbb"> </span><span style="color: #3D7B7B; font-style: italic">// TODO: create sphere</span><span style="color: #bbbbbb"></span>
<span style="color: #bbbbbb"> </span><span style="color: #3D7B7B; font-style: italic">// TODO: create lights</span><span style="color: #bbbbbb"></span>
<span style="color: #bbbbbb"> </span><span style="color: #3D7B7B; font-style: italic">// TODO: create IBL</span><span style="color: #bbbbbb"></span>
<span style="color: #bbbbbb"> </span><span style="color: #3D7B7B; font-style: italic">// TODO: create skybox</span><span style="color: #bbbbbb"></span>
<span style="color: #3D7B7B; font-style: italic">// TODO: create material</span>
<span style="color: #3D7B7B; font-style: italic">// TODO: create sphere</span>
<span style="color: #3D7B7B; font-style: italic">// TODO: create lights</span>
<span style="color: #3D7B7B; font-style: italic">// TODO: create IBL</span>
<span style="color: #3D7B7B; font-style: italic">// TODO: create skybox</span>
<span style="color: #bbbbbb"> </span><span style="color: #008000; font-weight: bold">this</span>.swapChain<span style="color: #bbbbbb"> </span><span style="color: #666666">=</span><span style="color: #bbbbbb"> </span>engine.createSwapChain();<span style="color: #bbbbbb"></span>
<span style="color: #bbbbbb"> </span><span style="color: #008000; font-weight: bold">this</span>.renderer<span style="color: #bbbbbb"> </span><span style="color: #666666">=</span><span style="color: #bbbbbb"> </span>engine.createRenderer();<span style="color: #bbbbbb"></span>
<span style="color: #bbbbbb"> </span><span style="color: #008000; font-weight: bold">this</span>.camera<span style="color: #bbbbbb"> </span><span style="color: #666666">=</span><span style="color: #bbbbbb"> </span>engine.createCamera(Filament.EntityManager.get().create());<span style="color: #bbbbbb"></span>
<span style="color: #bbbbbb"> </span><span style="color: #008000; font-weight: bold">this</span>.view<span style="color: #bbbbbb"> </span><span style="color: #666666">=</span><span style="color: #bbbbbb"> </span>engine.createView();<span style="color: #bbbbbb"></span>
<span style="color: #bbbbbb"> </span><span style="color: #008000; font-weight: bold">this</span>.view.setCamera(<span style="color: #008000; font-weight: bold">this</span>.camera);<span style="color: #bbbbbb"></span>
<span style="color: #bbbbbb"> </span><span style="color: #008000; font-weight: bold">this</span>.view.setScene(scene);<span style="color: #bbbbbb"></span>
<span style="color: #bbbbbb"> </span><span style="color: #008000; font-weight: bold">this</span>.resize();<span style="color: #bbbbbb"></span>
<span style="color: #bbbbbb"> </span><span style="color: #008000; font-weight: bold">this</span>.render<span style="color: #bbbbbb"> </span><span style="color: #666666">=</span><span style="color: #bbbbbb"> </span><span style="color: #008000; font-weight: bold">this</span>.render.bind(<span style="color: #008000; font-weight: bold">this</span>);<span style="color: #bbbbbb"></span>
<span style="color: #bbbbbb"> </span><span style="color: #008000; font-weight: bold">this</span>.resize<span style="color: #bbbbbb"> </span><span style="color: #666666">=</span><span style="color: #bbbbbb"> </span><span style="color: #008000; font-weight: bold">this</span>.resize.bind(<span style="color: #008000; font-weight: bold">this</span>);<span style="color: #bbbbbb"></span>
<span style="color: #bbbbbb"> </span><span style="color: #008000">window</span>.addEventListener(<span style="color: #BA2121">&#39;resize&#39;</span>,<span style="color: #bbbbbb"> </span><span style="color: #008000; font-weight: bold">this</span>.resize);<span style="color: #bbbbbb"></span>
<span style="color: #bbbbbb"> </span><span style="color: #008000">window</span>.requestAnimationFrame(<span style="color: #008000; font-weight: bold">this</span>.render);<span style="color: #bbbbbb"></span>
<span style="color: #bbbbbb"> </span>}<span style="color: #bbbbbb"></span>
<span style="color: #008000; font-weight: bold">this</span>.swapChain <span style="color: #666666">=</span> engine.createSwapChain();
<span style="color: #008000; font-weight: bold">this</span>.renderer <span style="color: #666666">=</span> engine.createRenderer();
<span style="color: #008000; font-weight: bold">this</span>.camera <span style="color: #666666">=</span> engine.createCamera(Filament.EntityManager.get().create());
<span style="color: #008000; font-weight: bold">this</span>.view <span style="color: #666666">=</span> engine.createView();
<span style="color: #008000; font-weight: bold">this</span>.view.setCamera(<span style="color: #008000; font-weight: bold">this</span>.camera);
<span style="color: #008000; font-weight: bold">this</span>.view.setScene(scene);
<span style="color: #008000; font-weight: bold">this</span>.resize();
<span style="color: #008000; font-weight: bold">this</span>.render <span style="color: #666666">=</span> <span style="color: #008000; font-weight: bold">this</span>.render.bind(<span style="color: #008000; font-weight: bold">this</span>);
<span style="color: #008000; font-weight: bold">this</span>.resize <span style="color: #666666">=</span> <span style="color: #008000; font-weight: bold">this</span>.resize.bind(<span style="color: #008000; font-weight: bold">this</span>);
<span style="color: #008000">window</span>.addEventListener(<span style="color: #BA2121">&#39;resize&#39;</span>, <span style="color: #008000; font-weight: bold">this</span>.resize);
<span style="color: #008000">window</span>.requestAnimationFrame(<span style="color: #008000; font-weight: bold">this</span>.render);
}
<span style="color: #bbbbbb"> </span>render()<span style="color: #bbbbbb"> </span>{<span style="color: #bbbbbb"></span>
<span style="color: #bbbbbb"> </span><span style="color: #008000; font-weight: bold">const</span><span style="color: #bbbbbb"> </span>eye<span style="color: #bbbbbb"> </span><span style="color: #666666">=</span><span style="color: #bbbbbb"> </span>[<span style="color: #666666">0</span>,<span style="color: #bbbbbb"> </span><span style="color: #666666">0</span>,<span style="color: #bbbbbb"> </span><span style="color: #666666">4</span>],<span style="color: #bbbbbb"> </span>center<span style="color: #bbbbbb"> </span><span style="color: #666666">=</span><span style="color: #bbbbbb"> </span>[<span style="color: #666666">0</span>,<span style="color: #bbbbbb"> </span><span style="color: #666666">0</span>,<span style="color: #bbbbbb"> </span><span style="color: #666666">0</span>],<span style="color: #bbbbbb"> </span>up<span style="color: #bbbbbb"> </span><span style="color: #666666">=</span><span style="color: #bbbbbb"> </span>[<span style="color: #666666">0</span>,<span style="color: #bbbbbb"> </span><span style="color: #666666">1</span>,<span style="color: #bbbbbb"> </span><span style="color: #666666">0</span>];<span style="color: #bbbbbb"></span>
<span style="color: #bbbbbb"> </span><span style="color: #008000; font-weight: bold">const</span><span style="color: #bbbbbb"> </span>radians<span style="color: #bbbbbb"> </span><span style="color: #666666">=</span><span style="color: #bbbbbb"> </span><span style="color: #008000">Date</span>.now()<span style="color: #bbbbbb"> </span><span style="color: #666666">/</span><span style="color: #bbbbbb"> </span><span style="color: #666666">10000</span>;<span style="color: #bbbbbb"></span>
<span style="color: #bbbbbb"> </span>vec3.rotateY(eye,<span style="color: #bbbbbb"> </span>eye,<span style="color: #bbbbbb"> </span>center,<span style="color: #bbbbbb"> </span>radians);<span style="color: #bbbbbb"></span>
<span style="color: #bbbbbb"> </span><span style="color: #008000; font-weight: bold">this</span>.camera.lookAt(eye,<span style="color: #bbbbbb"> </span>center,<span style="color: #bbbbbb"> </span>up);<span style="color: #bbbbbb"></span>
<span style="color: #bbbbbb"> </span><span style="color: #008000; font-weight: bold">this</span>.renderer.render(<span style="color: #008000; font-weight: bold">this</span>.swapChain,<span style="color: #bbbbbb"> </span><span style="color: #008000; font-weight: bold">this</span>.view);<span style="color: #bbbbbb"></span>
<span style="color: #bbbbbb"> </span><span style="color: #008000">window</span>.requestAnimationFrame(<span style="color: #008000; font-weight: bold">this</span>.render);<span style="color: #bbbbbb"></span>
<span style="color: #bbbbbb"> </span>}<span style="color: #bbbbbb"></span>
render() {
<span style="color: #008000; font-weight: bold">const</span> eye <span style="color: #666666">=</span> [<span style="color: #666666">0</span>, <span style="color: #666666">0</span>, <span style="color: #666666">4</span>], center <span style="color: #666666">=</span> [<span style="color: #666666">0</span>, <span style="color: #666666">0</span>, <span style="color: #666666">0</span>], up <span style="color: #666666">=</span> [<span style="color: #666666">0</span>, <span style="color: #666666">1</span>, <span style="color: #666666">0</span>];
<span style="color: #008000; font-weight: bold">const</span> radians <span style="color: #666666">=</span> <span style="color: #008000">Date</span>.now() <span style="color: #666666">/</span> <span style="color: #666666">10000</span>;
vec3.rotateY(eye, eye, center, radians);
<span style="color: #008000; font-weight: bold">this</span>.camera.lookAt(eye, center, up);
<span style="color: #008000; font-weight: bold">this</span>.renderer.render(<span style="color: #008000; font-weight: bold">this</span>.swapChain, <span style="color: #008000; font-weight: bold">this</span>.view);
<span style="color: #008000">window</span>.requestAnimationFrame(<span style="color: #008000; font-weight: bold">this</span>.render);
}
<span style="color: #bbbbbb"> </span>resize()<span style="color: #bbbbbb"> </span>{<span style="color: #bbbbbb"></span>
<span style="color: #bbbbbb"> </span><span style="color: #008000; font-weight: bold">const</span><span style="color: #bbbbbb"> </span>dpr<span style="color: #bbbbbb"> </span><span style="color: #666666">=</span><span style="color: #bbbbbb"> </span><span style="color: #008000">window</span>.devicePixelRatio;<span style="color: #bbbbbb"></span>
<span style="color: #bbbbbb"> </span><span style="color: #008000; font-weight: bold">const</span><span style="color: #bbbbbb"> </span>width<span style="color: #bbbbbb"> </span><span style="color: #666666">=</span><span style="color: #bbbbbb"> </span><span style="color: #008000; font-weight: bold">this</span>.canvas.width<span style="color: #bbbbbb"> </span><span style="color: #666666">=</span><span style="color: #bbbbbb"> </span><span style="color: #008000">window</span>.innerWidth<span style="color: #bbbbbb"> </span><span style="color: #666666">*</span><span style="color: #bbbbbb"> </span>dpr;<span style="color: #bbbbbb"></span>
<span style="color: #bbbbbb"> </span><span style="color: #008000; font-weight: bold">const</span><span style="color: #bbbbbb"> </span>height<span style="color: #bbbbbb"> </span><span style="color: #666666">=</span><span style="color: #bbbbbb"> </span><span style="color: #008000; font-weight: bold">this</span>.canvas.height<span style="color: #bbbbbb"> </span><span style="color: #666666">=</span><span style="color: #bbbbbb"> </span><span style="color: #008000">window</span>.innerHeight<span style="color: #bbbbbb"> </span><span style="color: #666666">*</span><span style="color: #bbbbbb"> </span>dpr;<span style="color: #bbbbbb"></span>
<span style="color: #bbbbbb"> </span><span style="color: #008000; font-weight: bold">this</span>.view.setViewport([<span style="color: #666666">0</span>,<span style="color: #bbbbbb"> </span><span style="color: #666666">0</span>,<span style="color: #bbbbbb"> </span>width,<span style="color: #bbbbbb"> </span>height]);<span style="color: #bbbbbb"></span>
<span style="color: #bbbbbb"> </span><span style="color: #008000; font-weight: bold">this</span>.camera.setProjectionFov(<span style="color: #666666">45</span>,<span style="color: #bbbbbb"> </span>width<span style="color: #bbbbbb"> </span><span style="color: #666666">/</span><span style="color: #bbbbbb"> </span>height,<span style="color: #bbbbbb"> </span><span style="color: #666666">1.0</span>,<span style="color: #bbbbbb"> </span><span style="color: #666666">10.0</span>,<span style="color: #bbbbbb"> </span>Fov.VERTICAL);<span style="color: #bbbbbb"></span>
<span style="color: #bbbbbb"> </span>}<span style="color: #bbbbbb"></span>
}<span style="color: #bbbbbb"></span>
resize() {
<span style="color: #008000; font-weight: bold">const</span> dpr <span style="color: #666666">=</span> <span style="color: #008000">window</span>.devicePixelRatio;
<span style="color: #008000; font-weight: bold">const</span> width <span style="color: #666666">=</span> <span style="color: #008000; font-weight: bold">this</span>.canvas.width <span style="color: #666666">=</span> <span style="color: #008000">window</span>.innerWidth <span style="color: #666666">*</span> dpr;
<span style="color: #008000; font-weight: bold">const</span> height <span style="color: #666666">=</span> <span style="color: #008000; font-weight: bold">this</span>.canvas.height <span style="color: #666666">=</span> <span style="color: #008000">window</span>.innerHeight <span style="color: #666666">*</span> dpr;
<span style="color: #008000; font-weight: bold">this</span>.view.setViewport([<span style="color: #666666">0</span>, <span style="color: #666666">0</span>, width, height]);
<span style="color: #008000; font-weight: bold">this</span>.camera.setProjectionFov(<span style="color: #666666">45</span>, width <span style="color: #666666">/</span> height, <span style="color: #666666">1.0</span>, <span style="color: #666666">10.0</span>, Fov.VERTICAL);
}
}
</pre></div>
<p>The above boilerplate should be familiar to you from the previous tutorial, although it loads in a
new set of assets. We also added some animation to the camera.</p>
<p>Next let&#x27;s create a material instance from the package that we built at the beginning the tutorial.
<p>Next let's create a material instance from the package that we built at the beginning the tutorial.
Replace the <strong>create material</strong> comment with the following snippet.</p>
<div class="highlight" style="background: #f8f8f8"><pre style="line-height: 125%;"><span></span><span style="color: #008000; font-weight: bold">const</span><span style="color: #bbbbbb"> </span>material<span style="color: #bbbbbb"> </span><span style="color: #666666">=</span><span style="color: #bbbbbb"> </span>engine.createMaterial(filamat_url);<span style="color: #bbbbbb"></span>
<span style="color: #008000; font-weight: bold">const</span><span style="color: #bbbbbb"> </span>matinstance<span style="color: #bbbbbb"> </span><span style="color: #666666">=</span><span style="color: #bbbbbb"> </span>material.createInstance();<span style="color: #bbbbbb"></span>
<div class="highlight" style="background: #f8f8f8"><pre style="line-height: 125%;"><span></span><span style="color: #008000; font-weight: bold">const</span> material <span style="color: #666666">=</span> engine.createMaterial(filamat_url);
<span style="color: #008000; font-weight: bold">const</span> matinstance <span style="color: #666666">=</span> material.createInstance();
<span style="color: #008000; font-weight: bold">const</span><span style="color: #bbbbbb"> </span>red<span style="color: #bbbbbb"> </span><span style="color: #666666">=</span><span style="color: #bbbbbb"> </span>[<span style="color: #666666">0.8</span>,<span style="color: #bbbbbb"> </span><span style="color: #666666">0.0</span>,<span style="color: #bbbbbb"> </span><span style="color: #666666">0.0</span>];<span style="color: #bbbbbb"></span>
matinstance.setColor3Parameter(<span style="color: #BA2121">&#39;baseColor&#39;</span>,<span style="color: #bbbbbb"> </span>Filament.RgbType.sRGB,<span style="color: #bbbbbb"> </span>red);<span style="color: #bbbbbb"></span>
matinstance.setFloatParameter(<span style="color: #BA2121">&#39;roughness&#39;</span>,<span style="color: #bbbbbb"> </span><span style="color: #666666">0.5</span>);<span style="color: #bbbbbb"></span>
matinstance.setFloatParameter(<span style="color: #BA2121">&#39;clearCoat&#39;</span>,<span style="color: #bbbbbb"> </span><span style="color: #666666">1.0</span>);<span style="color: #bbbbbb"></span>
matinstance.setFloatParameter(<span style="color: #BA2121">&#39;clearCoatRoughness&#39;</span>,<span style="color: #bbbbbb"> </span><span style="color: #666666">0.3</span>);<span style="color: #bbbbbb"></span>
<span style="color: #008000; font-weight: bold">const</span> red <span style="color: #666666">=</span> [<span style="color: #666666">0.8</span>, <span style="color: #666666">0.0</span>, <span style="color: #666666">0.0</span>];
matinstance.setColor3Parameter(<span style="color: #BA2121">&#39;baseColor&#39;</span>, Filament.RgbType.sRGB, red);
matinstance.setFloatParameter(<span style="color: #BA2121">&#39;roughness&#39;</span>, <span style="color: #666666">0.5</span>);
matinstance.setFloatParameter(<span style="color: #BA2121">&#39;clearCoat&#39;</span>, <span style="color: #666666">1.0</span>);
matinstance.setFloatParameter(<span style="color: #BA2121">&#39;clearCoatRoughness&#39;</span>, <span style="color: #666666">0.3</span>);
</pre></div>
<p>The next step is to create a renderable for the sphere. To help with this, we&#x27;ll use the <code>IcoSphere</code>
<p>The next step is to create a renderable for the sphere. To help with this, we'll use the <code>IcoSphere</code>
utility class, whose constructor takes a LOD. Its job is to subdivide an icosadedron, producing
three arrays:</p>
<ul>
@@ -147,137 +147,137 @@ three arrays:</p>
as quaternions.</li>
<li><code>icosphere.triangles</code> Uint16Array with triangle indices.</li>
</ul>
<p>Let&#x27;s go ahead use these arrays to build the vertex buffer and index buffer. Replace <strong>create
<p>Let's go ahead use these arrays to build the vertex buffer and index buffer. Replace <strong>create
sphere</strong> with the following snippet.</p>
<div class="highlight" style="background: #f8f8f8"><pre style="line-height: 125%;"><span></span><span style="color: #008000; font-weight: bold">const</span><span style="color: #bbbbbb"> </span>renderable<span style="color: #bbbbbb"> </span><span style="color: #666666">=</span><span style="color: #bbbbbb"> </span>Filament.EntityManager.get().create();<span style="color: #bbbbbb"></span>
scene.addEntity(renderable);<span style="color: #bbbbbb"></span>
<div class="highlight" style="background: #f8f8f8"><pre style="line-height: 125%;"><span></span><span style="color: #008000; font-weight: bold">const</span> renderable <span style="color: #666666">=</span> Filament.EntityManager.get().create();
scene.addEntity(renderable);
<span style="color: #008000; font-weight: bold">const</span><span style="color: #bbbbbb"> </span>icosphere<span style="color: #bbbbbb"> </span><span style="color: #666666">=</span><span style="color: #bbbbbb"> </span><span style="color: #AA22FF; font-weight: bold">new</span><span style="color: #bbbbbb"> </span>Filament.IcoSphere(<span style="color: #666666">5</span>);<span style="color: #bbbbbb"></span>
<span style="color: #008000; font-weight: bold">const</span> icosphere <span style="color: #666666">=</span> <span style="color: #AA22FF; font-weight: bold">new</span> Filament.IcoSphere(<span style="color: #666666">5</span>);
<span style="color: #008000; font-weight: bold">const</span><span style="color: #bbbbbb"> </span>vb<span style="color: #bbbbbb"> </span><span style="color: #666666">=</span><span style="color: #bbbbbb"> </span>Filament.VertexBuffer.Builder()<span style="color: #bbbbbb"></span>
<span style="color: #bbbbbb"> </span>.vertexCount(icosphere.vertices.length<span style="color: #bbbbbb"> </span><span style="color: #666666">/</span><span style="color: #bbbbbb"> </span><span style="color: #666666">3</span>)<span style="color: #bbbbbb"></span>
<span style="color: #bbbbbb"> </span>.bufferCount(<span style="color: #666666">2</span>)<span style="color: #bbbbbb"></span>
<span style="color: #bbbbbb"> </span>.attribute(VertexAttribute.POSITION,<span style="color: #bbbbbb"> </span><span style="color: #666666">0</span>,<span style="color: #bbbbbb"> </span>AttributeType.FLOAT3,<span style="color: #bbbbbb"> </span><span style="color: #666666">0</span>,<span style="color: #bbbbbb"> </span><span style="color: #666666">0</span>)<span style="color: #bbbbbb"></span>
<span style="color: #bbbbbb"> </span>.attribute(VertexAttribute.TANGENTS,<span style="color: #bbbbbb"> </span><span style="color: #666666">1</span>,<span style="color: #bbbbbb"> </span>AttributeType.SHORT4,<span style="color: #bbbbbb"> </span><span style="color: #666666">0</span>,<span style="color: #bbbbbb"> </span><span style="color: #666666">0</span>)<span style="color: #bbbbbb"></span>
<span style="color: #bbbbbb"> </span>.normalized(VertexAttribute.TANGENTS)<span style="color: #bbbbbb"></span>
<span style="color: #bbbbbb"> </span>.build(engine);<span style="color: #bbbbbb"></span>
<span style="color: #008000; font-weight: bold">const</span> vb <span style="color: #666666">=</span> Filament.VertexBuffer.Builder()
.vertexCount(icosphere.vertices.length <span style="color: #666666">/</span> <span style="color: #666666">3</span>)
.bufferCount(<span style="color: #666666">2</span>)
.attribute(VertexAttribute.POSITION, <span style="color: #666666">0</span>, AttributeType.FLOAT3, <span style="color: #666666">0</span>, <span style="color: #666666">0</span>)
.attribute(VertexAttribute.TANGENTS, <span style="color: #666666">1</span>, AttributeType.SHORT4, <span style="color: #666666">0</span>, <span style="color: #666666">0</span>)
.normalized(VertexAttribute.TANGENTS)
.build(engine);
<span style="color: #008000; font-weight: bold">const</span><span style="color: #bbbbbb"> </span>ib<span style="color: #bbbbbb"> </span><span style="color: #666666">=</span><span style="color: #bbbbbb"> </span>Filament.IndexBuffer.Builder()<span style="color: #bbbbbb"></span>
<span style="color: #bbbbbb"> </span>.indexCount(icosphere.triangles.length)<span style="color: #bbbbbb"></span>
<span style="color: #bbbbbb"> </span>.bufferType(IndexType.USHORT)<span style="color: #bbbbbb"></span>
<span style="color: #bbbbbb"> </span>.build(engine);<span style="color: #bbbbbb"></span>
<span style="color: #008000; font-weight: bold">const</span> ib <span style="color: #666666">=</span> Filament.IndexBuffer.Builder()
.indexCount(icosphere.triangles.length)
.bufferType(IndexType.USHORT)
.build(engine);
vb.setBufferAt(engine,<span style="color: #bbbbbb"> </span><span style="color: #666666">0</span>,<span style="color: #bbbbbb"> </span>icosphere.vertices);<span style="color: #bbbbbb"></span>
vb.setBufferAt(engine,<span style="color: #bbbbbb"> </span><span style="color: #666666">1</span>,<span style="color: #bbbbbb"> </span>icosphere.tangents);<span style="color: #bbbbbb"></span>
ib.setBuffer(engine,<span style="color: #bbbbbb"> </span>icosphere.triangles);<span style="color: #bbbbbb"></span>
vb.setBufferAt(engine, <span style="color: #666666">0</span>, icosphere.vertices);
vb.setBufferAt(engine, <span style="color: #666666">1</span>, icosphere.tangents);
ib.setBuffer(engine, icosphere.triangles);
Filament.RenderableManager.Builder(<span style="color: #666666">1</span>)<span style="color: #bbbbbb"></span>
<span style="color: #bbbbbb"> </span>.boundingBox({<span style="color: #bbbbbb"> </span>center<span style="color: #666666">:</span><span style="color: #bbbbbb"> </span>[<span style="color: #666666">-1</span>,<span style="color: #bbbbbb"> </span><span style="color: #666666">-1</span>,<span style="color: #bbbbbb"> </span><span style="color: #666666">-1</span>],<span style="color: #bbbbbb"> </span>halfExtent<span style="color: #666666">:</span><span style="color: #bbbbbb"> </span>[<span style="color: #666666">1</span>,<span style="color: #bbbbbb"> </span><span style="color: #666666">1</span>,<span style="color: #bbbbbb"> </span><span style="color: #666666">1</span>]<span style="color: #bbbbbb"> </span>})<span style="color: #bbbbbb"></span>
<span style="color: #bbbbbb"> </span>.material(<span style="color: #666666">0</span>,<span style="color: #bbbbbb"> </span>matinstance)<span style="color: #bbbbbb"></span>
<span style="color: #bbbbbb"> </span>.geometry(<span style="color: #666666">0</span>,<span style="color: #bbbbbb"> </span>PrimitiveType.TRIANGLES,<span style="color: #bbbbbb"> </span>vb,<span style="color: #bbbbbb"> </span>ib)<span style="color: #bbbbbb"></span>
<span style="color: #bbbbbb"> </span>.build(engine,<span style="color: #bbbbbb"> </span>renderable);<span style="color: #bbbbbb"></span>
Filament.RenderableManager.Builder(<span style="color: #666666">1</span>)
.boundingBox({ center<span style="color: #666666">:</span> [<span style="color: #666666">-1</span>, <span style="color: #666666">-1</span>, <span style="color: #666666">-1</span>], halfExtent<span style="color: #666666">:</span> [<span style="color: #666666">1</span>, <span style="color: #666666">1</span>, <span style="color: #666666">1</span>] })
.material(<span style="color: #666666">0</span>, matinstance)
.geometry(<span style="color: #666666">0</span>, PrimitiveType.TRIANGLES, vb, ib)
.build(engine, renderable);
</pre></div>
<p>At this point, the app is rendering a sphere, but it is black so it doesn&#x27;t show up. To prove that
<p>At this point, the app is rendering a sphere, but it is black so it doesn't show up. To prove that
the sphere is there, you can try changing the background color to blue via <code>setClearColor</code>, like we
did in the first tutorial.</p>
<h2>Add lighting</h2>
<p>In this section we will create some directional light sources, as well as an image-based light (IBL)
defined by one of the KTX files we built at the start of the demo. First, replace the <strong>create
lights</strong> comment with the following snippet.</p>
<div class="highlight" style="background: #f8f8f8"><pre style="line-height: 125%;"><span></span><span style="color: #008000; font-weight: bold">const</span><span style="color: #bbbbbb"> </span>sunlight<span style="color: #bbbbbb"> </span><span style="color: #666666">=</span><span style="color: #bbbbbb"> </span>Filament.EntityManager.get().create();<span style="color: #bbbbbb"></span>
scene.addEntity(sunlight);<span style="color: #bbbbbb"></span>
Filament.LightManager.Builder(LightType.SUN)<span style="color: #bbbbbb"></span>
<span style="color: #bbbbbb"> </span>.color([<span style="color: #666666">0.98</span>,<span style="color: #bbbbbb"> </span><span style="color: #666666">0.92</span>,<span style="color: #bbbbbb"> </span><span style="color: #666666">0.89</span>])<span style="color: #bbbbbb"></span>
<span style="color: #bbbbbb"> </span>.intensity(<span style="color: #666666">110000.0</span>)<span style="color: #bbbbbb"></span>
<span style="color: #bbbbbb"> </span>.direction([<span style="color: #666666">0.6</span>,<span style="color: #bbbbbb"> </span><span style="color: #666666">-1.0</span>,<span style="color: #bbbbbb"> </span><span style="color: #666666">-0.8</span>])<span style="color: #bbbbbb"></span>
<span style="color: #bbbbbb"> </span>.sunAngularRadius(<span style="color: #666666">1.9</span>)<span style="color: #bbbbbb"></span>
<span style="color: #bbbbbb"> </span>.sunHaloSize(<span style="color: #666666">10.0</span>)<span style="color: #bbbbbb"></span>
<span style="color: #bbbbbb"> </span>.sunHaloFalloff(<span style="color: #666666">80.0</span>)<span style="color: #bbbbbb"></span>
<span style="color: #bbbbbb"> </span>.build(engine,<span style="color: #bbbbbb"> </span>sunlight);<span style="color: #bbbbbb"></span>
<div class="highlight" style="background: #f8f8f8"><pre style="line-height: 125%;"><span></span><span style="color: #008000; font-weight: bold">const</span> sunlight <span style="color: #666666">=</span> Filament.EntityManager.get().create();
scene.addEntity(sunlight);
Filament.LightManager.Builder(LightType.SUN)
.color([<span style="color: #666666">0.98</span>, <span style="color: #666666">0.92</span>, <span style="color: #666666">0.89</span>])
.intensity(<span style="color: #666666">110000.0</span>)
.direction([<span style="color: #666666">0.6</span>, <span style="color: #666666">-1.0</span>, <span style="color: #666666">-0.8</span>])
.sunAngularRadius(<span style="color: #666666">1.9</span>)
.sunHaloSize(<span style="color: #666666">10.0</span>)
.sunHaloFalloff(<span style="color: #666666">80.0</span>)
.build(engine, sunlight);
<span style="color: #008000; font-weight: bold">const</span><span style="color: #bbbbbb"> </span>backlight<span style="color: #bbbbbb"> </span><span style="color: #666666">=</span><span style="color: #bbbbbb"> </span>Filament.EntityManager.get().create();<span style="color: #bbbbbb"></span>
scene.addEntity(backlight);<span style="color: #bbbbbb"></span>
Filament.LightManager.Builder(LightType.DIRECTIONAL)<span style="color: #bbbbbb"></span>
<span style="color: #bbbbbb"> </span>.direction([<span style="color: #666666">-1</span>,<span style="color: #bbbbbb"> </span><span style="color: #666666">0</span>,<span style="color: #bbbbbb"> </span><span style="color: #666666">1</span>])<span style="color: #bbbbbb"></span>
<span style="color: #bbbbbb"> </span>.intensity(<span style="color: #666666">50000.0</span>)<span style="color: #bbbbbb"></span>
<span style="color: #bbbbbb"> </span>.build(engine,<span style="color: #bbbbbb"> </span>backlight);<span style="color: #bbbbbb"></span>
<span style="color: #008000; font-weight: bold">const</span> backlight <span style="color: #666666">=</span> Filament.EntityManager.get().create();
scene.addEntity(backlight);
Filament.LightManager.Builder(LightType.DIRECTIONAL)
.direction([<span style="color: #666666">-1</span>, <span style="color: #666666">0</span>, <span style="color: #666666">1</span>])
.intensity(<span style="color: #666666">50000.0</span>)
.build(engine, backlight);
</pre></div>
<p>The <code>SUN</code> light source is similar to the <code>DIRECTIONAL</code> light source, but has some extra
parameters because Filament will automatically draw a disk into the skybox.</p>
<p>Next we need to create an <code>IndirectLight</code> object from the KTX IBL. One way of doing this is the
following (don&#x27;t type this out, there&#x27;s an easier way).</p>
<div class="highlight" style="background: #f8f8f8"><pre style="line-height: 125%;"><span></span><span style="color: #008000; font-weight: bold">const</span><span style="color: #bbbbbb"> </span>format<span style="color: #bbbbbb"> </span><span style="color: #666666">=</span><span style="color: #bbbbbb"> </span>Filament.PixelDataFormat.RGB;<span style="color: #bbbbbb"></span>
<span style="color: #008000; font-weight: bold">const</span><span style="color: #bbbbbb"> </span>datatype<span style="color: #bbbbbb"> </span><span style="color: #666666">=</span><span style="color: #bbbbbb"> </span>Filament.PixelDataType.UINT_10F_11F_11F_REV;<span style="color: #bbbbbb"></span>
following (don't type this out, there's an easier way).</p>
<div class="highlight" style="background: #f8f8f8"><pre style="line-height: 125%;"><span></span><span style="color: #008000; font-weight: bold">const</span> format <span style="color: #666666">=</span> Filament.PixelDataFormat.RGB;
<span style="color: #008000; font-weight: bold">const</span> datatype <span style="color: #666666">=</span> Filament.PixelDataType.UINT_10F_11F_11F_REV;
<span style="color: #3D7B7B; font-style: italic">// Create a Texture object for the mipmapped cubemap.</span><span style="color: #bbbbbb"></span>
<span style="color: #008000; font-weight: bold">const</span><span style="color: #bbbbbb"> </span>ibl_package<span style="color: #bbbbbb"> </span><span style="color: #666666">=</span><span style="color: #bbbbbb"> </span>Filament.Buffer(Filament.assets[ibl_url]);<span style="color: #bbbbbb"></span>
<span style="color: #008000; font-weight: bold">const</span><span style="color: #bbbbbb"> </span>iblktx<span style="color: #bbbbbb"> </span><span style="color: #666666">=</span><span style="color: #bbbbbb"> </span><span style="color: #AA22FF; font-weight: bold">new</span><span style="color: #bbbbbb"> </span>Filament.Ktx1Bundle(ibl_package);<span style="color: #bbbbbb"></span>
<span style="color: #3D7B7B; font-style: italic">// Create a Texture object for the mipmapped cubemap.</span>
<span style="color: #008000; font-weight: bold">const</span> ibl_package <span style="color: #666666">=</span> Filament.Buffer(Filament.assets[ibl_url]);
<span style="color: #008000; font-weight: bold">const</span> iblktx <span style="color: #666666">=</span> <span style="color: #AA22FF; font-weight: bold">new</span> Filament.Ktx1Bundle(ibl_package);
<span style="color: #008000; font-weight: bold">const</span><span style="color: #bbbbbb"> </span>ibltex<span style="color: #bbbbbb"> </span><span style="color: #666666">=</span><span style="color: #bbbbbb"> </span>Filament.Texture.Builder()<span style="color: #bbbbbb"></span>
<span style="color: #bbbbbb"> </span>.width(iblktx.info().pixelWidth)<span style="color: #bbbbbb"></span>
<span style="color: #bbbbbb"> </span>.height(iblktx.info().pixelHeight)<span style="color: #bbbbbb"></span>
<span style="color: #bbbbbb"> </span>.levels(iblktx.getNumMipLevels())<span style="color: #bbbbbb"></span>
<span style="color: #bbbbbb"> </span>.sampler(Filament.Texture$Sampler.SAMPLER_CUBEMAP)<span style="color: #bbbbbb"></span>
<span style="color: #bbbbbb"> </span>.format(Filament.Texture$InternalFormat.RGBA8)<span style="color: #bbbbbb"></span>
<span style="color: #bbbbbb"> </span>.build(engine);<span style="color: #bbbbbb"></span>
<span style="color: #008000; font-weight: bold">const</span> ibltex <span style="color: #666666">=</span> Filament.Texture.Builder()
.width(iblktx.info().pixelWidth)
.height(iblktx.info().pixelHeight)
.levels(iblktx.getNumMipLevels())
.sampler(Filament.Texture$Sampler.SAMPLER_CUBEMAP)
.format(Filament.Texture$InternalFormat.RGBA8)
.build(engine);
<span style="color: #008000; font-weight: bold">for</span><span style="color: #bbbbbb"> </span>(<span style="color: #008000; font-weight: bold">let</span><span style="color: #bbbbbb"> </span>level<span style="color: #bbbbbb"> </span><span style="color: #666666">=</span><span style="color: #bbbbbb"> </span><span style="color: #666666">0</span>;<span style="color: #bbbbbb"> </span>level<span style="color: #bbbbbb"> </span><span style="color: #666666">&lt;</span><span style="color: #bbbbbb"> </span>iblktx.getNumMipLevels();<span style="color: #bbbbbb"> </span><span style="color: #666666">++</span>level)<span style="color: #bbbbbb"> </span>{<span style="color: #bbbbbb"></span>
<span style="color: #bbbbbb"> </span><span style="color: #008000; font-weight: bold">const</span><span style="color: #bbbbbb"> </span>uint8array<span style="color: #bbbbbb"> </span><span style="color: #666666">=</span><span style="color: #bbbbbb"> </span>iblktx.getCubeBlob(level).getBytes();<span style="color: #bbbbbb"></span>
<span style="color: #bbbbbb"> </span><span style="color: #008000; font-weight: bold">const</span><span style="color: #bbbbbb"> </span>pixelbuffer<span style="color: #bbbbbb"> </span><span style="color: #666666">=</span><span style="color: #bbbbbb"> </span>Filament.PixelBuffer(uint8array,<span style="color: #bbbbbb"> </span>format,<span style="color: #bbbbbb"> </span>datatype);<span style="color: #bbbbbb"></span>
<span style="color: #bbbbbb"> </span>ibltex.setImageCube(engine,<span style="color: #bbbbbb"> </span>level,<span style="color: #bbbbbb"> </span>pixelbuffer);<span style="color: #bbbbbb"></span>
}<span style="color: #bbbbbb"></span>
<span style="color: #008000; font-weight: bold">for</span> (<span style="color: #008000; font-weight: bold">let</span> level <span style="color: #666666">=</span> <span style="color: #666666">0</span>; level <span style="color: #666666">&lt;</span> iblktx.getNumMipLevels(); <span style="color: #666666">++</span>level) {
<span style="color: #008000; font-weight: bold">const</span> uint8array <span style="color: #666666">=</span> iblktx.getCubeBlob(level).getBytes();
<span style="color: #008000; font-weight: bold">const</span> pixelbuffer <span style="color: #666666">=</span> Filament.PixelBuffer(uint8array, format, datatype);
ibltex.setImageCube(engine, level, pixelbuffer);
}
<span style="color: #3D7B7B; font-style: italic">// Parse the spherical harmonics metadata.</span><span style="color: #bbbbbb"></span>
<span style="color: #008000; font-weight: bold">const</span><span style="color: #bbbbbb"> </span>shstring<span style="color: #bbbbbb"> </span><span style="color: #666666">=</span><span style="color: #bbbbbb"> </span>iblktx.getMetadata(<span style="color: #BA2121">&#39;sh&#39;</span>);<span style="color: #bbbbbb"></span>
<span style="color: #008000; font-weight: bold">const</span><span style="color: #bbbbbb"> </span>shfloats<span style="color: #bbbbbb"> </span><span style="color: #666666">=</span><span style="color: #bbbbbb"> </span>shstring.split(<span style="color: #A45A77">/\s/</span>,<span style="color: #bbbbbb"> </span><span style="color: #666666">9</span><span style="color: #bbbbbb"> </span><span style="color: #666666">*</span><span style="color: #bbbbbb"> </span><span style="color: #666666">3</span>).map(<span style="color: #008000">parseFloat</span>);<span style="color: #bbbbbb"></span>
<span style="color: #3D7B7B; font-style: italic">// Parse the spherical harmonics metadata.</span>
<span style="color: #008000; font-weight: bold">const</span> shstring <span style="color: #666666">=</span> iblktx.getMetadata(<span style="color: #BA2121">&#39;sh&#39;</span>);
<span style="color: #008000; font-weight: bold">const</span> shfloats <span style="color: #666666">=</span> shstring.split(<span style="color: #A45A77">/\s/</span>, <span style="color: #666666">9</span> <span style="color: #666666">*</span> <span style="color: #666666">3</span>).map(<span style="color: #008000">parseFloat</span>);
<span style="color: #3D7B7B; font-style: italic">// Build the IBL object and insert it into the scene.</span><span style="color: #bbbbbb"></span>
<span style="color: #008000; font-weight: bold">const</span><span style="color: #bbbbbb"> </span>indirectLight<span style="color: #bbbbbb"> </span><span style="color: #666666">=</span><span style="color: #bbbbbb"> </span>Filament.IndirectLight.Builder()<span style="color: #bbbbbb"></span>
<span style="color: #bbbbbb"> </span>.reflections(ibltex)<span style="color: #bbbbbb"></span>
<span style="color: #bbbbbb"> </span>.irradianceSh(<span style="color: #666666">3</span>,<span style="color: #bbbbbb"> </span>shfloats)<span style="color: #bbbbbb"></span>
<span style="color: #bbbbbb"> </span>.intensity(<span style="color: #666666">50000.0</span>)<span style="color: #bbbbbb"></span>
<span style="color: #bbbbbb"> </span>.build(engine);<span style="color: #bbbbbb"></span>
<span style="color: #3D7B7B; font-style: italic">// Build the IBL object and insert it into the scene.</span>
<span style="color: #008000; font-weight: bold">const</span> indirectLight <span style="color: #666666">=</span> Filament.IndirectLight.Builder()
.reflections(ibltex)
.irradianceSh(<span style="color: #666666">3</span>, shfloats)
.intensity(<span style="color: #666666">50000.0</span>)
.build(engine);
scene.setIndirectLight(indirectLight);<span style="color: #bbbbbb"></span>
scene.setIndirectLight(indirectLight);
</pre></div>
<p>Filament provides a JavaScript utility to make this simpler,
simply replace the <strong>create IBL</strong> comment with the following snippet.</p>
<div class="highlight" style="background: #f8f8f8"><pre style="line-height: 125%;"><span></span><span style="color: #008000; font-weight: bold">const</span><span style="color: #bbbbbb"> </span>indirectLight<span style="color: #bbbbbb"> </span><span style="color: #666666">=</span><span style="color: #bbbbbb"> </span>engine.createIblFromKtx1(ibl_url);<span style="color: #bbbbbb"></span>
indirectLight.setIntensity(<span style="color: #666666">50000</span>);<span style="color: #bbbbbb"></span>
scene.setIndirectLight(indirectLight);<span style="color: #bbbbbb"></span>
<div class="highlight" style="background: #f8f8f8"><pre style="line-height: 125%;"><span></span><span style="color: #008000; font-weight: bold">const</span> indirectLight <span style="color: #666666">=</span> engine.createIblFromKtx(ibl_url);
indirectLight.setIntensity(<span style="color: #666666">50000</span>);
scene.setIndirectLight(indirectLight);
</pre></div>
<h2>Add background</h2>
<p>At this point you can run the demo and you should see a red plastic ball against a black background.
Without a skybox, the reflections on the ball are not representative of its surroundings.
Here&#x27;s one way to create a texture for the skybox:</p>
<div class="highlight" style="background: #f8f8f8"><pre style="line-height: 125%;"><span></span><span style="color: #008000; font-weight: bold">const</span><span style="color: #bbbbbb"> </span>sky_package<span style="color: #bbbbbb"> </span><span style="color: #666666">=</span><span style="color: #bbbbbb"> </span>Filament.Buffer(Filament.assets[sky_url]);<span style="color: #bbbbbb"></span>
<span style="color: #008000; font-weight: bold">const</span><span style="color: #bbbbbb"> </span>skyktx<span style="color: #bbbbbb"> </span><span style="color: #666666">=</span><span style="color: #bbbbbb"> </span><span style="color: #AA22FF; font-weight: bold">new</span><span style="color: #bbbbbb"> </span>Filament.Ktx1Bundle(sky_package);<span style="color: #bbbbbb"></span>
<span style="color: #008000; font-weight: bold">const</span><span style="color: #bbbbbb"> </span>skytex<span style="color: #bbbbbb"> </span><span style="color: #666666">=</span><span style="color: #bbbbbb"> </span>Filament.Texture.Builder()<span style="color: #bbbbbb"></span>
<span style="color: #bbbbbb"> </span>.width(skyktx.info().pixelWidth)<span style="color: #bbbbbb"></span>
<span style="color: #bbbbbb"> </span>.height(skyktx.info().pixelHeight)<span style="color: #bbbbbb"></span>
<span style="color: #bbbbbb"> </span>.levels(<span style="color: #666666">1</span>)<span style="color: #bbbbbb"></span>
<span style="color: #bbbbbb"> </span>.sampler(Filament.Texture$Sampler.SAMPLER_CUBEMAP)<span style="color: #bbbbbb"></span>
<span style="color: #bbbbbb"> </span>.format(Filament.Texture$InternalFormat.RGBA8)<span style="color: #bbbbbb"></span>
<span style="color: #bbbbbb"> </span>.build(engine);<span style="color: #bbbbbb"></span>
Here's one way to create a texture for the skybox:</p>
<div class="highlight" style="background: #f8f8f8"><pre style="line-height: 125%;"><span></span><span style="color: #008000; font-weight: bold">const</span> sky_package <span style="color: #666666">=</span> Filament.Buffer(Filament.assets[sky_url]);
<span style="color: #008000; font-weight: bold">const</span> skyktx <span style="color: #666666">=</span> <span style="color: #AA22FF; font-weight: bold">new</span> Filament.Ktx1Bundle(sky_package);
<span style="color: #008000; font-weight: bold">const</span> skytex <span style="color: #666666">=</span> Filament.Texture.Builder()
.width(skyktx.info().pixelWidth)
.height(skyktx.info().pixelHeight)
.levels(<span style="color: #666666">1</span>)
.sampler(Filament.Texture$Sampler.SAMPLER_CUBEMAP)
.format(Filament.Texture$InternalFormat.RGBA8)
.build(engine);
<span style="color: #008000; font-weight: bold">const</span><span style="color: #bbbbbb"> </span>uint8array<span style="color: #bbbbbb"> </span><span style="color: #666666">=</span><span style="color: #bbbbbb"> </span>skyktx.getCubeBlob(<span style="color: #666666">0</span>).getBytes();<span style="color: #bbbbbb"></span>
<span style="color: #008000; font-weight: bold">const</span><span style="color: #bbbbbb"> </span>pixelbuffer<span style="color: #bbbbbb"> </span><span style="color: #666666">=</span><span style="color: #bbbbbb"> </span>Filament.PixelBuffer(uint8array,<span style="color: #bbbbbb"> </span>format,<span style="color: #bbbbbb"> </span>datatype);<span style="color: #bbbbbb"></span>
skytex.setImageCube(engine,<span style="color: #bbbbbb"> </span><span style="color: #666666">0</span>,<span style="color: #bbbbbb"> </span>pixelbuffer);<span style="color: #bbbbbb"></span>
<span style="color: #008000; font-weight: bold">const</span> uint8array <span style="color: #666666">=</span> skyktx.getCubeBlob(<span style="color: #666666">0</span>).getBytes();
<span style="color: #008000; font-weight: bold">const</span> pixelbuffer <span style="color: #666666">=</span> Filament.PixelBuffer(uint8array, format, datatype);
skytex.setImageCube(engine, <span style="color: #666666">0</span>, pixelbuffer);
</pre></div>
<p>Filament provides a Javascript utility to make this easier.
Replace <strong>create skybox</strong> with the following.</p>
<div class="highlight" style="background: #f8f8f8"><pre style="line-height: 125%;"><span></span><span style="color: #008000; font-weight: bold">const</span><span style="color: #bbbbbb"> </span>skybox<span style="color: #bbbbbb"> </span><span style="color: #666666">=</span><span style="color: #bbbbbb"> </span>engine.createSkyFromKtx1(sky_url);<span style="color: #bbbbbb"></span>
scene.setSkybox(skybox);<span style="color: #bbbbbb"></span>
<div class="highlight" style="background: #f8f8f8"><pre style="line-height: 125%;"><span></span><span style="color: #008000; font-weight: bold">const</span> skybox <span style="color: #666666">=</span> engine.createSkyFromKtx(sky_url);
scene.setSkybox(skybox);
</pre></div>
<p>That&#x27;s it, we now have a shiny red ball floating in an environment! The complete JavaScript file is
<p>That's it, we now have a shiny red ball floating in an environment! The complete JavaScript file is
available <a href="tutorial_redball.js">here</a>.</p>
<p>In the <a href="tutorial_suzanne.html">next tutorial</a>, we&#x27;ll take a closer look at textures and interaction.</p>
<p>In the <a href="tutorial_suzanne.html">next tutorial</a>, we'll take a closer look at textures and interaction.</p>
</body>
</html>
+2 -2
View File
@@ -70,10 +70,10 @@ class App {
.direction([-1, 0, 1])
.intensity(50000.0)
.build(engine, backlight);
const indirectLight = engine.createIblFromKtx1(ibl_url);
const indirectLight = engine.createIblFromKtx(ibl_url);
indirectLight.setIntensity(50000);
scene.setIndirectLight(indirectLight);
const skybox = engine.createSkyFromKtx1(sky_url);
const skybox = engine.createSkyFromKtx(sky_url);
scene.setSkybox(skybox);
this.swapChain = engine.createSwapChain();
this.renderer = engine.createRenderer();
+102 -102
View File
@@ -8,18 +8,18 @@
<div class="demo_frame"><iframe src="demo_suzanne.html"></iframe><a href="demo_suzanne.html">&#x1F517;</a></div>
<p>This tutorial will describe how to create the <strong>suzanne</strong> demo, introducing you to compressed
textures, mipmap generation, asynchronous texture loading, and trackball rotation.</p>
<p>Much like the <a href="tutorial_redball.html">previous tutorial</a>, you&#x27;ll need to use the command-line tools that can be found in
<p>Much like the <a href="tutorial_redball.html">previous tutorial</a>, you'll need to use the command-line tools that can be found in
the appropriate <a href="//github.com/google/filament/releases">Filament release</a> for your development machine. In addition to <code>matc</code> and <code>cmgen</code>,
we&#x27;ll also be using <code>filamesh</code> and <code>mipgen</code>.</p>
we'll also be using <code>filamesh</code> and <code>mipgen</code>.</p>
<h2>Create filamesh file</h2>
<p>Filament does not have an asset loading system, but it does provide a binary mesh format
called <code>filamesh</code> for simple use cases. Let&#x27;s create a compressed filamesh file for suzanne by
called <code>filamesh</code> for simple use cases. Let's create a compressed filamesh file for suzanne by
converting <a href="https://github.com/google/filament/blob/main/assets/models/monkey/monkey.obj">this OBJ file</a>:</p>
<div class="highlight" style="background: #f8f8f8"><pre style="line-height: 125%;"><span></span>filamesh --compress monkey.obj suzanne.filamesh
</pre></div>
<h2>Create mipmapped textures</h2>
<p>Next, let&#x27;s create mipmapped KTX files using filament&#x27;s <code>mipgen</code> tool. We&#x27;ll create compressed and
<p>Next, let's create mipmapped KTX files using filament's <code>mipgen</code> tool. We'll create compressed and
non-compressed variants for each texture, since not all platforms support the same compression
formats. First copy over the PNG files from the <a href="https://github.com/google/filament/blob/main/assets/models/monkey">monkey folder</a>, then do:</p>
<div class="highlight" style="background: #f8f8f8"><pre style="line-height: 125%;"><span></span><span style="color: #3D7B7B; font-style: italic"># Create mipmaps for base color and two compressed variants.</span>
@@ -45,10 +45,10 @@ mipgen --grayscale ao.png ao.ktx
mipgen --grayscale --compression<span style="color: #666666">=</span>etc_r11_numeric_40 ao.png ao_etc.ktx
</pre></div>
<p>For more information on mipgen&#x27;s arguments and supported formats, do <code>mipgen --help</code>.</p>
<p>In a production setting, you&#x27;d want to invoke these commands with a script or build system.</p>
<p>For more information on mipgen's arguments and supported formats, do <code>mipgen --help</code>.</p>
<p>In a production setting, you'd want to invoke these commands with a script or build system.</p>
<h2>Bake environment map</h2>
<p>Much like the <a href="tutorial_redball.html">previous tutorial</a> we need to use Filament&#x27;s <code>cmgen</code> tool to produce cubemap files.</p>
<p>Much like the <a href="tutorial_redball.html">previous tutorial</a> we need to use Filament's <code>cmgen</code> tool to produce cubemap files.</p>
<p>Download <a href="//github.com/google/filament/blob/main/third_party/environments/venetian_crossroads_2k.hdr">venetian_crossroads_2k.hdr</a>, then invoke the following commands in your terminal.</p>
<div class="highlight" style="background: #f8f8f8"><pre style="line-height: 125%;"><span></span>cmgen -x . --format<span style="color: #666666">=</span>ktx --size<span style="color: #666666">=64</span> --extract-blur<span style="color: #666666">=0</span>.1 venetian_crossroads_2k.hdr
<span style="color: #008000">cd</span> venetian* ; mv venetian*_ibl.ktx venetian_crossroads_2k_skybox_tiny.ktx ; <span style="color: #008000">cd</span> -
@@ -60,7 +60,7 @@ cmgen -x . --format<span style="color: #666666">=</span>ktx --size<span style="c
<h2>Define textured material</h2>
<p>You might recall the <code>filamat</code> file we generated in the previous tutorial for red plastic. For this
demo, we&#x27;ll create a material that uses textures for several parameters.</p>
demo, we'll create a material that uses textures for several parameters.</p>
<p>Create the following text file and call it <code>textured.mat</code>. Note that our material definition now
requires a <code>uv0</code> attribute.</p>
<div class="highlight" style="background: #f8f8f8"><pre style="line-height: 125%;"><span></span>material {
@@ -101,65 +101,65 @@ materials, consult the official document describing the <a href="https://google.
<p>Create a text file called <code>suzanne.html</code> and copy over the HTML that we used in the <a href="tutorial_redball.html">previous
tutorial</a>. Change the last script tag from <code>redball.js</code> to <code>suzanne.js</code>. Next, create <code>suzanne.js</code>
with the following content.</p>
<div class="highlight" style="background: #f8f8f8"><pre style="line-height: 125%;"><span></span><span style="color: #3D7B7B; font-style: italic">// TODO: declare asset URLs</span><span style="color: #bbbbbb"></span>
<div class="highlight" style="background: #f8f8f8"><pre style="line-height: 125%;"><span></span><span style="color: #3D7B7B; font-style: italic">// TODO: declare asset URLs</span>
Filament.init([<span style="color: #bbbbbb"> </span>filamat_url,<span style="color: #bbbbbb"> </span>filamesh_url,<span style="color: #bbbbbb"> </span>sky_small_url,<span style="color: #bbbbbb"> </span>ibl_url<span style="color: #bbbbbb"> </span>],<span style="color: #bbbbbb"> </span>()<span style="color: #bbbbbb"> </span>=&gt;<span style="color: #bbbbbb"> </span>{<span style="color: #bbbbbb"></span>
<span style="color: #bbbbbb"> </span><span style="color: #008000">window</span>.app<span style="color: #bbbbbb"> </span><span style="color: #666666">=</span><span style="color: #bbbbbb"> </span><span style="color: #AA22FF; font-weight: bold">new</span><span style="color: #bbbbbb"> </span>App(<span style="color: #008000">document</span>.getElementsByTagName(<span style="color: #BA2121">&#39;canvas&#39;</span>)[<span style="color: #666666">0</span>]);<span style="color: #bbbbbb"></span>
});<span style="color: #bbbbbb"></span>
Filament.init([ filamat_url, filamesh_url, sky_small_url, ibl_url ], () =&gt; {
<span style="color: #008000">window</span>.app <span style="color: #666666">=</span> <span style="color: #AA22FF; font-weight: bold">new</span> App(<span style="color: #008000">document</span>.getElementsByTagName(<span style="color: #BA2121">&#39;canvas&#39;</span>)[<span style="color: #666666">0</span>]);
});
<span style="color: #008000; font-weight: bold">class</span><span style="color: #bbbbbb"> </span>App<span style="color: #bbbbbb"> </span>{<span style="color: #bbbbbb"></span>
<span style="color: #bbbbbb"> </span><span style="color: #008000; font-weight: bold">constructor</span>(canvas)<span style="color: #bbbbbb"> </span>{<span style="color: #bbbbbb"></span>
<span style="color: #bbbbbb"> </span><span style="color: #008000; font-weight: bold">this</span>.canvas<span style="color: #bbbbbb"> </span><span style="color: #666666">=</span><span style="color: #bbbbbb"> </span>canvas;<span style="color: #bbbbbb"></span>
<span style="color: #bbbbbb"> </span><span style="color: #008000; font-weight: bold">this</span>.engine<span style="color: #bbbbbb"> </span><span style="color: #666666">=</span><span style="color: #bbbbbb"> </span>Filament.Engine.create(canvas);<span style="color: #bbbbbb"></span>
<span style="color: #bbbbbb"> </span><span style="color: #008000; font-weight: bold">this</span>.scene<span style="color: #bbbbbb"> </span><span style="color: #666666">=</span><span style="color: #bbbbbb"> </span><span style="color: #008000; font-weight: bold">this</span>.engine.createScene();<span style="color: #bbbbbb"></span>
<span style="color: #008000; font-weight: bold">class</span> App {
<span style="color: #008000; font-weight: bold">constructor</span>(canvas) {
<span style="color: #008000; font-weight: bold">this</span>.canvas <span style="color: #666666">=</span> canvas;
<span style="color: #008000; font-weight: bold">this</span>.engine <span style="color: #666666">=</span> Filament.Engine.create(canvas);
<span style="color: #008000; font-weight: bold">this</span>.scene <span style="color: #666666">=</span> <span style="color: #008000; font-weight: bold">this</span>.engine.createScene();
<span style="color: #bbbbbb"> </span><span style="color: #008000; font-weight: bold">const</span><span style="color: #bbbbbb"> </span>material<span style="color: #bbbbbb"> </span><span style="color: #666666">=</span><span style="color: #bbbbbb"> </span><span style="color: #008000; font-weight: bold">this</span>.engine.createMaterial(filamat_url);<span style="color: #bbbbbb"></span>
<span style="color: #bbbbbb"> </span><span style="color: #008000; font-weight: bold">this</span>.matinstance<span style="color: #bbbbbb"> </span><span style="color: #666666">=</span><span style="color: #bbbbbb"> </span>material.createInstance();<span style="color: #bbbbbb"></span>
<span style="color: #008000; font-weight: bold">const</span> material <span style="color: #666666">=</span> <span style="color: #008000; font-weight: bold">this</span>.engine.createMaterial(filamat_url);
<span style="color: #008000; font-weight: bold">this</span>.matinstance <span style="color: #666666">=</span> material.createInstance();
<span style="color: #bbbbbb"> </span><span style="color: #008000; font-weight: bold">const</span><span style="color: #bbbbbb"> </span>filamesh<span style="color: #bbbbbb"> </span><span style="color: #666666">=</span><span style="color: #bbbbbb"> </span><span style="color: #008000; font-weight: bold">this</span>.engine.loadFilamesh(filamesh_url,<span style="color: #bbbbbb"> </span><span style="color: #008000; font-weight: bold">this</span>.matinstance);<span style="color: #bbbbbb"></span>
<span style="color: #bbbbbb"> </span><span style="color: #008000; font-weight: bold">this</span>.suzanne<span style="color: #bbbbbb"> </span><span style="color: #666666">=</span><span style="color: #bbbbbb"> </span>filamesh.renderable;<span style="color: #bbbbbb"></span>
<span style="color: #008000; font-weight: bold">const</span> filamesh <span style="color: #666666">=</span> <span style="color: #008000; font-weight: bold">this</span>.engine.loadFilamesh(filamesh_url, <span style="color: #008000; font-weight: bold">this</span>.matinstance);
<span style="color: #008000; font-weight: bold">this</span>.suzanne <span style="color: #666666">=</span> filamesh.renderable;
<span style="color: #bbbbbb"> </span><span style="color: #3D7B7B; font-style: italic">// TODO: create sky box and IBL</span><span style="color: #bbbbbb"></span>
<span style="color: #bbbbbb"> </span><span style="color: #3D7B7B; font-style: italic">// TODO: initialize gltumble</span><span style="color: #bbbbbb"></span>
<span style="color: #bbbbbb"> </span><span style="color: #3D7B7B; font-style: italic">// TODO: fetch larger assets</span><span style="color: #bbbbbb"></span>
<span style="color: #3D7B7B; font-style: italic">// TODO: create sky box and IBL</span>
<span style="color: #3D7B7B; font-style: italic">// TODO: initialize gltumble</span>
<span style="color: #3D7B7B; font-style: italic">// TODO: fetch larger assets</span>
<span style="color: #bbbbbb"> </span><span style="color: #008000; font-weight: bold">this</span>.swapChain<span style="color: #bbbbbb"> </span><span style="color: #666666">=</span><span style="color: #bbbbbb"> </span><span style="color: #008000; font-weight: bold">this</span>.engine.createSwapChain();<span style="color: #bbbbbb"></span>
<span style="color: #bbbbbb"> </span><span style="color: #008000; font-weight: bold">this</span>.renderer<span style="color: #bbbbbb"> </span><span style="color: #666666">=</span><span style="color: #bbbbbb"> </span><span style="color: #008000; font-weight: bold">this</span>.engine.createRenderer();<span style="color: #bbbbbb"></span>
<span style="color: #bbbbbb"> </span><span style="color: #008000; font-weight: bold">this</span>.camera<span style="color: #bbbbbb"> </span><span style="color: #666666">=</span><span style="color: #bbbbbb"> </span><span style="color: #008000; font-weight: bold">this</span>.engine.createCamera(Filament.EntityManager.get().create());<span style="color: #bbbbbb"></span>
<span style="color: #bbbbbb"> </span><span style="color: #008000; font-weight: bold">this</span>.view<span style="color: #bbbbbb"> </span><span style="color: #666666">=</span><span style="color: #bbbbbb"> </span><span style="color: #008000; font-weight: bold">this</span>.engine.createView();<span style="color: #bbbbbb"></span>
<span style="color: #bbbbbb"> </span><span style="color: #008000; font-weight: bold">this</span>.view.setCamera(<span style="color: #008000; font-weight: bold">this</span>.camera);<span style="color: #bbbbbb"></span>
<span style="color: #bbbbbb"> </span><span style="color: #008000; font-weight: bold">this</span>.view.setScene(<span style="color: #008000; font-weight: bold">this</span>.scene);<span style="color: #bbbbbb"></span>
<span style="color: #bbbbbb"> </span><span style="color: #008000; font-weight: bold">this</span>.render<span style="color: #bbbbbb"> </span><span style="color: #666666">=</span><span style="color: #bbbbbb"> </span><span style="color: #008000; font-weight: bold">this</span>.render.bind(<span style="color: #008000; font-weight: bold">this</span>);<span style="color: #bbbbbb"></span>
<span style="color: #bbbbbb"> </span><span style="color: #008000; font-weight: bold">this</span>.resize<span style="color: #bbbbbb"> </span><span style="color: #666666">=</span><span style="color: #bbbbbb"> </span><span style="color: #008000; font-weight: bold">this</span>.resize.bind(<span style="color: #008000; font-weight: bold">this</span>);<span style="color: #bbbbbb"></span>
<span style="color: #bbbbbb"> </span><span style="color: #008000">window</span>.addEventListener(<span style="color: #BA2121">&#39;resize&#39;</span>,<span style="color: #bbbbbb"> </span><span style="color: #008000; font-weight: bold">this</span>.resize);<span style="color: #bbbbbb"></span>
<span style="color: #008000; font-weight: bold">this</span>.swapChain <span style="color: #666666">=</span> <span style="color: #008000; font-weight: bold">this</span>.engine.createSwapChain();
<span style="color: #008000; font-weight: bold">this</span>.renderer <span style="color: #666666">=</span> <span style="color: #008000; font-weight: bold">this</span>.engine.createRenderer();
<span style="color: #008000; font-weight: bold">this</span>.camera <span style="color: #666666">=</span> <span style="color: #008000; font-weight: bold">this</span>.engine.createCamera(Filament.EntityManager.get().create());
<span style="color: #008000; font-weight: bold">this</span>.view <span style="color: #666666">=</span> <span style="color: #008000; font-weight: bold">this</span>.engine.createView();
<span style="color: #008000; font-weight: bold">this</span>.view.setCamera(<span style="color: #008000; font-weight: bold">this</span>.camera);
<span style="color: #008000; font-weight: bold">this</span>.view.setScene(<span style="color: #008000; font-weight: bold">this</span>.scene);
<span style="color: #008000; font-weight: bold">this</span>.render <span style="color: #666666">=</span> <span style="color: #008000; font-weight: bold">this</span>.render.bind(<span style="color: #008000; font-weight: bold">this</span>);
<span style="color: #008000; font-weight: bold">this</span>.resize <span style="color: #666666">=</span> <span style="color: #008000; font-weight: bold">this</span>.resize.bind(<span style="color: #008000; font-weight: bold">this</span>);
<span style="color: #008000">window</span>.addEventListener(<span style="color: #BA2121">&#39;resize&#39;</span>, <span style="color: #008000; font-weight: bold">this</span>.resize);
<span style="color: #bbbbbb"> </span><span style="color: #008000; font-weight: bold">const</span><span style="color: #bbbbbb"> </span>eye<span style="color: #bbbbbb"> </span><span style="color: #666666">=</span><span style="color: #bbbbbb"> </span>[<span style="color: #666666">0</span>,<span style="color: #bbbbbb"> </span><span style="color: #666666">0</span>,<span style="color: #bbbbbb"> </span><span style="color: #666666">4</span>],<span style="color: #bbbbbb"> </span>center<span style="color: #bbbbbb"> </span><span style="color: #666666">=</span><span style="color: #bbbbbb"> </span>[<span style="color: #666666">0</span>,<span style="color: #bbbbbb"> </span><span style="color: #666666">0</span>,<span style="color: #bbbbbb"> </span><span style="color: #666666">0</span>],<span style="color: #bbbbbb"> </span>up<span style="color: #bbbbbb"> </span><span style="color: #666666">=</span><span style="color: #bbbbbb"> </span>[<span style="color: #666666">0</span>,<span style="color: #bbbbbb"> </span><span style="color: #666666">1</span>,<span style="color: #bbbbbb"> </span><span style="color: #666666">0</span>];<span style="color: #bbbbbb"></span>
<span style="color: #bbbbbb"> </span><span style="color: #008000; font-weight: bold">this</span>.camera.lookAt(eye,<span style="color: #bbbbbb"> </span>center,<span style="color: #bbbbbb"> </span>up);<span style="color: #bbbbbb"></span>
<span style="color: #008000; font-weight: bold">const</span> eye <span style="color: #666666">=</span> [<span style="color: #666666">0</span>, <span style="color: #666666">0</span>, <span style="color: #666666">4</span>], center <span style="color: #666666">=</span> [<span style="color: #666666">0</span>, <span style="color: #666666">0</span>, <span style="color: #666666">0</span>], up <span style="color: #666666">=</span> [<span style="color: #666666">0</span>, <span style="color: #666666">1</span>, <span style="color: #666666">0</span>];
<span style="color: #008000; font-weight: bold">this</span>.camera.lookAt(eye, center, up);
<span style="color: #bbbbbb"> </span><span style="color: #008000; font-weight: bold">this</span>.resize();<span style="color: #bbbbbb"></span>
<span style="color: #bbbbbb"> </span><span style="color: #008000">window</span>.requestAnimationFrame(<span style="color: #008000; font-weight: bold">this</span>.render);<span style="color: #bbbbbb"></span>
<span style="color: #bbbbbb"> </span>}<span style="color: #bbbbbb"></span>
<span style="color: #008000; font-weight: bold">this</span>.resize();
<span style="color: #008000">window</span>.requestAnimationFrame(<span style="color: #008000; font-weight: bold">this</span>.render);
}
<span style="color: #bbbbbb"> </span>render()<span style="color: #bbbbbb"> </span>{<span style="color: #bbbbbb"></span>
<span style="color: #bbbbbb"> </span><span style="color: #3D7B7B; font-style: italic">// TODO: apply gltumble matrix</span><span style="color: #bbbbbb"></span>
<span style="color: #bbbbbb"> </span><span style="color: #008000; font-weight: bold">this</span>.renderer.render(<span style="color: #008000; font-weight: bold">this</span>.swapChain,<span style="color: #bbbbbb"> </span><span style="color: #008000; font-weight: bold">this</span>.view);<span style="color: #bbbbbb"></span>
<span style="color: #bbbbbb"> </span><span style="color: #008000">window</span>.requestAnimationFrame(<span style="color: #008000; font-weight: bold">this</span>.render);<span style="color: #bbbbbb"></span>
<span style="color: #bbbbbb"> </span>}<span style="color: #bbbbbb"></span>
render() {
<span style="color: #3D7B7B; font-style: italic">// TODO: apply gltumble matrix</span>
<span style="color: #008000; font-weight: bold">this</span>.renderer.render(<span style="color: #008000; font-weight: bold">this</span>.swapChain, <span style="color: #008000; font-weight: bold">this</span>.view);
<span style="color: #008000">window</span>.requestAnimationFrame(<span style="color: #008000; font-weight: bold">this</span>.render);
}
<span style="color: #bbbbbb"> </span>resize()<span style="color: #bbbbbb"> </span>{<span style="color: #bbbbbb"></span>
<span style="color: #bbbbbb"> </span><span style="color: #008000; font-weight: bold">const</span><span style="color: #bbbbbb"> </span>dpr<span style="color: #bbbbbb"> </span><span style="color: #666666">=</span><span style="color: #bbbbbb"> </span><span style="color: #008000">window</span>.devicePixelRatio;<span style="color: #bbbbbb"></span>
<span style="color: #bbbbbb"> </span><span style="color: #008000; font-weight: bold">const</span><span style="color: #bbbbbb"> </span>width<span style="color: #bbbbbb"> </span><span style="color: #666666">=</span><span style="color: #bbbbbb"> </span><span style="color: #008000; font-weight: bold">this</span>.canvas.width<span style="color: #bbbbbb"> </span><span style="color: #666666">=</span><span style="color: #bbbbbb"> </span><span style="color: #008000">window</span>.innerWidth<span style="color: #bbbbbb"> </span><span style="color: #666666">*</span><span style="color: #bbbbbb"> </span>dpr;<span style="color: #bbbbbb"></span>
<span style="color: #bbbbbb"> </span><span style="color: #008000; font-weight: bold">const</span><span style="color: #bbbbbb"> </span>height<span style="color: #bbbbbb"> </span><span style="color: #666666">=</span><span style="color: #bbbbbb"> </span><span style="color: #008000; font-weight: bold">this</span>.canvas.height<span style="color: #bbbbbb"> </span><span style="color: #666666">=</span><span style="color: #bbbbbb"> </span><span style="color: #008000">window</span>.innerHeight<span style="color: #bbbbbb"> </span><span style="color: #666666">*</span><span style="color: #bbbbbb"> </span>dpr;<span style="color: #bbbbbb"></span>
<span style="color: #bbbbbb"> </span><span style="color: #008000; font-weight: bold">this</span>.view.setViewport([<span style="color: #666666">0</span>,<span style="color: #bbbbbb"> </span><span style="color: #666666">0</span>,<span style="color: #bbbbbb"> </span>width,<span style="color: #bbbbbb"> </span>height]);<span style="color: #bbbbbb"></span>
resize() {
<span style="color: #008000; font-weight: bold">const</span> dpr <span style="color: #666666">=</span> <span style="color: #008000">window</span>.devicePixelRatio;
<span style="color: #008000; font-weight: bold">const</span> width <span style="color: #666666">=</span> <span style="color: #008000; font-weight: bold">this</span>.canvas.width <span style="color: #666666">=</span> <span style="color: #008000">window</span>.innerWidth <span style="color: #666666">*</span> dpr;
<span style="color: #008000; font-weight: bold">const</span> height <span style="color: #666666">=</span> <span style="color: #008000; font-weight: bold">this</span>.canvas.height <span style="color: #666666">=</span> <span style="color: #008000">window</span>.innerHeight <span style="color: #666666">*</span> dpr;
<span style="color: #008000; font-weight: bold">this</span>.view.setViewport([<span style="color: #666666">0</span>, <span style="color: #666666">0</span>, width, height]);
<span style="color: #bbbbbb"> </span><span style="color: #008000; font-weight: bold">const</span><span style="color: #bbbbbb"> </span>aspect<span style="color: #bbbbbb"> </span><span style="color: #666666">=</span><span style="color: #bbbbbb"> </span>width<span style="color: #bbbbbb"> </span><span style="color: #666666">/</span><span style="color: #bbbbbb"> </span>height;<span style="color: #bbbbbb"></span>
<span style="color: #bbbbbb"> </span><span style="color: #008000; font-weight: bold">const</span><span style="color: #bbbbbb"> </span>Fov<span style="color: #bbbbbb"> </span><span style="color: #666666">=</span><span style="color: #bbbbbb"> </span>Filament.Camera$Fov,<span style="color: #bbbbbb"> </span>fov<span style="color: #bbbbbb"> </span><span style="color: #666666">=</span><span style="color: #bbbbbb"> </span>aspect<span style="color: #bbbbbb"> </span><span style="color: #666666">&lt;</span><span style="color: #bbbbbb"> </span><span style="color: #666666">1</span><span style="color: #bbbbbb"> </span><span style="color: #666666">?</span><span style="color: #bbbbbb"> </span>Fov.HORIZONTAL<span style="color: #bbbbbb"> </span><span style="color: #666666">:</span><span style="color: #bbbbbb"> </span>Fov.VERTICAL;<span style="color: #bbbbbb"></span>
<span style="color: #bbbbbb"> </span><span style="color: #008000; font-weight: bold">this</span>.camera.setProjectionFov(<span style="color: #666666">45</span>,<span style="color: #bbbbbb"> </span>aspect,<span style="color: #bbbbbb"> </span><span style="color: #666666">1.0</span>,<span style="color: #bbbbbb"> </span><span style="color: #666666">10.0</span>,<span style="color: #bbbbbb"> </span>fov);<span style="color: #bbbbbb"></span>
<span style="color: #bbbbbb"> </span>}<span style="color: #bbbbbb"></span>
}<span style="color: #bbbbbb"></span>
<span style="color: #008000; font-weight: bold">const</span> aspect <span style="color: #666666">=</span> width <span style="color: #666666">/</span> height;
<span style="color: #008000; font-weight: bold">const</span> Fov <span style="color: #666666">=</span> Filament.Camera$Fov, fov <span style="color: #666666">=</span> aspect <span style="color: #666666">&lt;</span> <span style="color: #666666">1</span> <span style="color: #666666">?</span> Fov.HORIZONTAL <span style="color: #666666">:</span> Fov.VERTICAL;
<span style="color: #008000; font-weight: bold">this</span>.camera.setProjectionFov(<span style="color: #666666">45</span>, aspect, <span style="color: #666666">1.0</span>, <span style="color: #666666">10.0</span>, fov);
}
}
</pre></div>
<p>Our app will only require a subset of assets to be present for <code>App</code> construction. We&#x27;ll download
<p>Our app will only require a subset of assets to be present for <code>App</code> construction. We'll download
the other assets after construction. By using a progressive loading strategy, we can reduce the
perceived load time.</p>
<p>Next we need to supply the URLs for various assets. This is actually a bit tricky, because different
@@ -172,65 +172,65 @@ string -- which might be empty.</p>
<p>In our case, we know that our web server will have <code>astc</code> and <code>s3tc</code> variants for albedo, and <code>etc</code>
variants for the other textures. The uncompressed variants (empty string) are always available as a
last resort. Go ahead and replace the <strong>declare asset URLs</strong> comment with the following snippet.</p>
<div class="highlight" style="background: #f8f8f8"><pre style="line-height: 125%;"><span></span><span style="color: #008000; font-weight: bold">const</span><span style="color: #bbbbbb"> </span>albedo_suffix<span style="color: #bbbbbb"> </span><span style="color: #666666">=</span><span style="color: #bbbbbb"> </span>Filament.getSupportedFormatSuffix(<span style="color: #BA2121">&#39;astc s3tc_srgb&#39;</span>);<span style="color: #bbbbbb"></span>
<span style="color: #008000; font-weight: bold">const</span><span style="color: #bbbbbb"> </span>texture_suffix<span style="color: #bbbbbb"> </span><span style="color: #666666">=</span><span style="color: #bbbbbb"> </span>Filament.getSupportedFormatSuffix(<span style="color: #BA2121">&#39;etc&#39;</span>);<span style="color: #bbbbbb"></span>
<div class="highlight" style="background: #f8f8f8"><pre style="line-height: 125%;"><span></span><span style="color: #008000; font-weight: bold">const</span> albedo_suffix <span style="color: #666666">=</span> Filament.getSupportedFormatSuffix(<span style="color: #BA2121">&#39;astc s3tc_srgb&#39;</span>);
<span style="color: #008000; font-weight: bold">const</span> texture_suffix <span style="color: #666666">=</span> Filament.getSupportedFormatSuffix(<span style="color: #BA2121">&#39;etc&#39;</span>);
<span style="color: #008000; font-weight: bold">const</span><span style="color: #bbbbbb"> </span>environ<span style="color: #bbbbbb"> </span><span style="color: #666666">=</span><span style="color: #bbbbbb"> </span><span style="color: #BA2121">&#39;venetian_crossroads_2k&#39;</span><span style="color: #bbbbbb"></span>
<span style="color: #008000; font-weight: bold">const</span><span style="color: #bbbbbb"> </span>ibl_url<span style="color: #bbbbbb"> </span><span style="color: #666666">=</span><span style="color: #bbbbbb"> </span><span style="color: #BA2121">`</span><span style="color: #A45A77; font-weight: bold">${</span>environ<span style="color: #A45A77; font-weight: bold">}</span><span style="color: #BA2121">/</span><span style="color: #A45A77; font-weight: bold">${</span>environ<span style="color: #A45A77; font-weight: bold">}</span><span style="color: #BA2121">_ibl.ktx`</span>;<span style="color: #bbbbbb"></span>
<span style="color: #008000; font-weight: bold">const</span><span style="color: #bbbbbb"> </span>sky_small_url<span style="color: #bbbbbb"> </span><span style="color: #666666">=</span><span style="color: #bbbbbb"> </span><span style="color: #BA2121">`</span><span style="color: #A45A77; font-weight: bold">${</span>environ<span style="color: #A45A77; font-weight: bold">}</span><span style="color: #BA2121">/</span><span style="color: #A45A77; font-weight: bold">${</span>environ<span style="color: #A45A77; font-weight: bold">}</span><span style="color: #BA2121">_skybox_tiny.ktx`</span>;<span style="color: #bbbbbb"></span>
<span style="color: #008000; font-weight: bold">const</span><span style="color: #bbbbbb"> </span>sky_large_url<span style="color: #bbbbbb"> </span><span style="color: #666666">=</span><span style="color: #bbbbbb"> </span><span style="color: #BA2121">`</span><span style="color: #A45A77; font-weight: bold">${</span>environ<span style="color: #A45A77; font-weight: bold">}</span><span style="color: #BA2121">/</span><span style="color: #A45A77; font-weight: bold">${</span>environ<span style="color: #A45A77; font-weight: bold">}</span><span style="color: #BA2121">_skybox.ktx`</span>;<span style="color: #bbbbbb"></span>
<span style="color: #008000; font-weight: bold">const</span><span style="color: #bbbbbb"> </span>albedo_url<span style="color: #bbbbbb"> </span><span style="color: #666666">=</span><span style="color: #bbbbbb"> </span><span style="color: #BA2121">`albedo</span><span style="color: #A45A77; font-weight: bold">${</span>albedo_suffix<span style="color: #A45A77; font-weight: bold">}</span><span style="color: #BA2121">.ktx`</span>;<span style="color: #bbbbbb"></span>
<span style="color: #008000; font-weight: bold">const</span><span style="color: #bbbbbb"> </span>ao_url<span style="color: #bbbbbb"> </span><span style="color: #666666">=</span><span style="color: #bbbbbb"> </span><span style="color: #BA2121">`ao</span><span style="color: #A45A77; font-weight: bold">${</span>texture_suffix<span style="color: #A45A77; font-weight: bold">}</span><span style="color: #BA2121">.ktx`</span>;<span style="color: #bbbbbb"></span>
<span style="color: #008000; font-weight: bold">const</span><span style="color: #bbbbbb"> </span>metallic_url<span style="color: #bbbbbb"> </span><span style="color: #666666">=</span><span style="color: #bbbbbb"> </span><span style="color: #BA2121">`metallic</span><span style="color: #A45A77; font-weight: bold">${</span>texture_suffix<span style="color: #A45A77; font-weight: bold">}</span><span style="color: #BA2121">.ktx`</span>;<span style="color: #bbbbbb"></span>
<span style="color: #008000; font-weight: bold">const</span><span style="color: #bbbbbb"> </span>normal_url<span style="color: #bbbbbb"> </span><span style="color: #666666">=</span><span style="color: #bbbbbb"> </span><span style="color: #BA2121">`normal</span><span style="color: #A45A77; font-weight: bold">${</span>texture_suffix<span style="color: #A45A77; font-weight: bold">}</span><span style="color: #BA2121">.ktx`</span>;<span style="color: #bbbbbb"></span>
<span style="color: #008000; font-weight: bold">const</span><span style="color: #bbbbbb"> </span>roughness_url<span style="color: #bbbbbb"> </span><span style="color: #666666">=</span><span style="color: #bbbbbb"> </span><span style="color: #BA2121">`roughness</span><span style="color: #A45A77; font-weight: bold">${</span>texture_suffix<span style="color: #A45A77; font-weight: bold">}</span><span style="color: #BA2121">.ktx`</span>;<span style="color: #bbbbbb"></span>
<span style="color: #008000; font-weight: bold">const</span><span style="color: #bbbbbb"> </span>filamat_url<span style="color: #bbbbbb"> </span><span style="color: #666666">=</span><span style="color: #bbbbbb"> </span><span style="color: #BA2121">&#39;textured.filamat&#39;</span>;<span style="color: #bbbbbb"></span>
<span style="color: #008000; font-weight: bold">const</span><span style="color: #bbbbbb"> </span>filamesh_url<span style="color: #bbbbbb"> </span><span style="color: #666666">=</span><span style="color: #bbbbbb"> </span><span style="color: #BA2121">&#39;suzanne.filamesh&#39;</span>;<span style="color: #bbbbbb"></span>
<span style="color: #008000; font-weight: bold">const</span> environ <span style="color: #666666">=</span> <span style="color: #BA2121">&#39;venetian_crossroads_2k&#39;</span>
<span style="color: #008000; font-weight: bold">const</span> ibl_url <span style="color: #666666">=</span> <span style="color: #BA2121">`</span><span style="color: #A45A77; font-weight: bold">${</span>environ<span style="color: #A45A77; font-weight: bold">}</span><span style="color: #BA2121">/</span><span style="color: #A45A77; font-weight: bold">${</span>environ<span style="color: #A45A77; font-weight: bold">}</span><span style="color: #BA2121">_ibl.ktx`</span>;
<span style="color: #008000; font-weight: bold">const</span> sky_small_url <span style="color: #666666">=</span> <span style="color: #BA2121">`</span><span style="color: #A45A77; font-weight: bold">${</span>environ<span style="color: #A45A77; font-weight: bold">}</span><span style="color: #BA2121">/</span><span style="color: #A45A77; font-weight: bold">${</span>environ<span style="color: #A45A77; font-weight: bold">}</span><span style="color: #BA2121">_skybox_tiny.ktx`</span>;
<span style="color: #008000; font-weight: bold">const</span> sky_large_url <span style="color: #666666">=</span> <span style="color: #BA2121">`</span><span style="color: #A45A77; font-weight: bold">${</span>environ<span style="color: #A45A77; font-weight: bold">}</span><span style="color: #BA2121">/</span><span style="color: #A45A77; font-weight: bold">${</span>environ<span style="color: #A45A77; font-weight: bold">}</span><span style="color: #BA2121">_skybox.ktx`</span>;
<span style="color: #008000; font-weight: bold">const</span> albedo_url <span style="color: #666666">=</span> <span style="color: #BA2121">`albedo</span><span style="color: #A45A77; font-weight: bold">${</span>albedo_suffix<span style="color: #A45A77; font-weight: bold">}</span><span style="color: #BA2121">.ktx`</span>;
<span style="color: #008000; font-weight: bold">const</span> ao_url <span style="color: #666666">=</span> <span style="color: #BA2121">`ao</span><span style="color: #A45A77; font-weight: bold">${</span>texture_suffix<span style="color: #A45A77; font-weight: bold">}</span><span style="color: #BA2121">.ktx`</span>;
<span style="color: #008000; font-weight: bold">const</span> metallic_url <span style="color: #666666">=</span> <span style="color: #BA2121">`metallic</span><span style="color: #A45A77; font-weight: bold">${</span>texture_suffix<span style="color: #A45A77; font-weight: bold">}</span><span style="color: #BA2121">.ktx`</span>;
<span style="color: #008000; font-weight: bold">const</span> normal_url <span style="color: #666666">=</span> <span style="color: #BA2121">`normal</span><span style="color: #A45A77; font-weight: bold">${</span>texture_suffix<span style="color: #A45A77; font-weight: bold">}</span><span style="color: #BA2121">.ktx`</span>;
<span style="color: #008000; font-weight: bold">const</span> roughness_url <span style="color: #666666">=</span> <span style="color: #BA2121">`roughness</span><span style="color: #A45A77; font-weight: bold">${</span>texture_suffix<span style="color: #A45A77; font-weight: bold">}</span><span style="color: #BA2121">.ktx`</span>;
<span style="color: #008000; font-weight: bold">const</span> filamat_url <span style="color: #666666">=</span> <span style="color: #BA2121">&#39;textured.filamat&#39;</span>;
<span style="color: #008000; font-weight: bold">const</span> filamesh_url <span style="color: #666666">=</span> <span style="color: #BA2121">&#39;suzanne.filamesh&#39;</span>;
</pre></div>
<h2>Create skybox and IBL</h2>
<p>Next, let&#x27;s create the low-resolution skybox and IBL in the <code>App</code> constructor.</p>
<div class="highlight" style="background: #f8f8f8"><pre style="line-height: 125%;"><span></span><span style="color: #008000; font-weight: bold">this</span>.skybox<span style="color: #bbbbbb"> </span><span style="color: #666666">=</span><span style="color: #bbbbbb"> </span><span style="color: #008000; font-weight: bold">this</span>.engine.createSkyFromKtx1(sky_small_url);<span style="color: #bbbbbb"></span>
<span style="color: #008000; font-weight: bold">this</span>.scene.setSkybox(<span style="color: #008000; font-weight: bold">this</span>.skybox);<span style="color: #bbbbbb"></span>
<span style="color: #008000; font-weight: bold">this</span>.indirectLight<span style="color: #bbbbbb"> </span><span style="color: #666666">=</span><span style="color: #bbbbbb"> </span><span style="color: #008000; font-weight: bold">this</span>.engine.createIblFromKtx1(ibl_url);<span style="color: #bbbbbb"></span>
<span style="color: #008000; font-weight: bold">this</span>.indirectLight.setIntensity(<span style="color: #666666">100000</span>);<span style="color: #bbbbbb"></span>
<span style="color: #008000; font-weight: bold">this</span>.scene.setIndirectLight(<span style="color: #008000; font-weight: bold">this</span>.indirectLight);<span style="color: #bbbbbb"></span>
<p>Next, let's create the low-resolution skybox and IBL in the <code>App</code> constructor.</p>
<div class="highlight" style="background: #f8f8f8"><pre style="line-height: 125%;"><span></span><span style="color: #008000; font-weight: bold">this</span>.skybox <span style="color: #666666">=</span> <span style="color: #008000; font-weight: bold">this</span>.engine.createSkyFromKtx(sky_small_url);
<span style="color: #008000; font-weight: bold">this</span>.scene.setSkybox(<span style="color: #008000; font-weight: bold">this</span>.skybox);
<span style="color: #008000; font-weight: bold">this</span>.indirectLight <span style="color: #666666">=</span> <span style="color: #008000; font-weight: bold">this</span>.engine.createIblFromKtx(ibl_url);
<span style="color: #008000; font-weight: bold">this</span>.indirectLight.setIntensity(<span style="color: #666666">100000</span>);
<span style="color: #008000; font-weight: bold">this</span>.scene.setIndirectLight(<span style="color: #008000; font-weight: bold">this</span>.indirectLight);
</pre></div>
<p>This allows users to see a reasonable background fairly quickly, before larger assets have finished
loading in.</p>
<h2>Fetch assets asychronously</h2>
<p>Next we&#x27;ll invoke the <code>Filament.fetch</code> function from within the app constructor. This function is
<p>Next we'll invoke the <code>Filament.fetch</code> function from within the app constructor. This function is
very similar to <code>Filament.init</code>. It takes a list of asset URLs and a callback function that triggers
when the assets have finished downloading.</p>
<p>In our callback, we&#x27;ll make several <code>setTextureParameter</code> calls on the material instance, then we&#x27;ll
<p>In our callback, we'll make several <code>setTextureParameter</code> calls on the material instance, then we'll
recreate the skybox using a higher-resolution texture. As a last step we unhide the renderable that
was created in the app constructor.</p>
<div class="highlight" style="background: #f8f8f8"><pre style="line-height: 125%;"><span></span>Filament.fetch([sky_large_url,<span style="color: #bbbbbb"> </span>albedo_url,<span style="color: #bbbbbb"> </span>roughness_url,<span style="color: #bbbbbb"> </span>metallic_url,<span style="color: #bbbbbb"> </span>normal_url,<span style="color: #bbbbbb"> </span>ao_url],<span style="color: #bbbbbb"> </span>()<span style="color: #bbbbbb"> </span>=&gt;<span style="color: #bbbbbb"> </span>{<span style="color: #bbbbbb"></span>
<span style="color: #bbbbbb"> </span><span style="color: #008000; font-weight: bold">const</span><span style="color: #bbbbbb"> </span>albedo<span style="color: #bbbbbb"> </span><span style="color: #666666">=</span><span style="color: #bbbbbb"> </span><span style="color: #008000; font-weight: bold">this</span>.engine.createTextureFromKtx1(albedo_url,<span style="color: #bbbbbb"> </span>{srgb<span style="color: #666666">:</span><span style="color: #bbbbbb"> </span><span style="color: #008000; font-weight: bold">true</span>});<span style="color: #bbbbbb"></span>
<span style="color: #bbbbbb"> </span><span style="color: #008000; font-weight: bold">const</span><span style="color: #bbbbbb"> </span>roughness<span style="color: #bbbbbb"> </span><span style="color: #666666">=</span><span style="color: #bbbbbb"> </span><span style="color: #008000; font-weight: bold">this</span>.engine.createTextureFromKtx1(roughness_url);<span style="color: #bbbbbb"></span>
<span style="color: #bbbbbb"> </span><span style="color: #008000; font-weight: bold">const</span><span style="color: #bbbbbb"> </span>metallic<span style="color: #bbbbbb"> </span><span style="color: #666666">=</span><span style="color: #bbbbbb"> </span><span style="color: #008000; font-weight: bold">this</span>.engine.createTextureFromKtx1(metallic_url);<span style="color: #bbbbbb"></span>
<span style="color: #bbbbbb"> </span><span style="color: #008000; font-weight: bold">const</span><span style="color: #bbbbbb"> </span>normal<span style="color: #bbbbbb"> </span><span style="color: #666666">=</span><span style="color: #bbbbbb"> </span><span style="color: #008000; font-weight: bold">this</span>.engine.createTextureFromKtx1(normal_url);<span style="color: #bbbbbb"></span>
<span style="color: #bbbbbb"> </span><span style="color: #008000; font-weight: bold">const</span><span style="color: #bbbbbb"> </span>ao<span style="color: #bbbbbb"> </span><span style="color: #666666">=</span><span style="color: #bbbbbb"> </span><span style="color: #008000; font-weight: bold">this</span>.engine.createTextureFromKtx1(ao_url);<span style="color: #bbbbbb"></span>
<div class="highlight" style="background: #f8f8f8"><pre style="line-height: 125%;"><span></span>Filament.fetch([sky_large_url, albedo_url, roughness_url, metallic_url, normal_url, ao_url], () =&gt; {
<span style="color: #008000; font-weight: bold">const</span> albedo <span style="color: #666666">=</span> <span style="color: #008000; font-weight: bold">this</span>.engine.createTextureFromKtx(albedo_url, {srgb<span style="color: #666666">:</span> <span style="color: #008000; font-weight: bold">true</span>});
<span style="color: #008000; font-weight: bold">const</span> roughness <span style="color: #666666">=</span> <span style="color: #008000; font-weight: bold">this</span>.engine.createTextureFromKtx(roughness_url);
<span style="color: #008000; font-weight: bold">const</span> metallic <span style="color: #666666">=</span> <span style="color: #008000; font-weight: bold">this</span>.engine.createTextureFromKtx(metallic_url);
<span style="color: #008000; font-weight: bold">const</span> normal <span style="color: #666666">=</span> <span style="color: #008000; font-weight: bold">this</span>.engine.createTextureFromKtx(normal_url);
<span style="color: #008000; font-weight: bold">const</span> ao <span style="color: #666666">=</span> <span style="color: #008000; font-weight: bold">this</span>.engine.createTextureFromKtx(ao_url);
<span style="color: #bbbbbb"> </span><span style="color: #008000; font-weight: bold">const</span><span style="color: #bbbbbb"> </span>sampler<span style="color: #bbbbbb"> </span><span style="color: #666666">=</span><span style="color: #bbbbbb"> </span><span style="color: #AA22FF; font-weight: bold">new</span><span style="color: #bbbbbb"> </span>Filament.TextureSampler(<span style="color: #bbbbbb"></span>
<span style="color: #bbbbbb"> </span>Filament.MinFilter.LINEAR_MIPMAP_LINEAR,<span style="color: #bbbbbb"></span>
<span style="color: #bbbbbb"> </span>Filament.MagFilter.LINEAR,<span style="color: #bbbbbb"></span>
<span style="color: #bbbbbb"> </span>Filament.WrapMode.CLAMP_TO_EDGE);<span style="color: #bbbbbb"></span>
<span style="color: #008000; font-weight: bold">const</span> sampler <span style="color: #666666">=</span> <span style="color: #AA22FF; font-weight: bold">new</span> Filament.TextureSampler(
Filament.MinFilter.LINEAR_MIPMAP_LINEAR,
Filament.MagFilter.LINEAR,
Filament.WrapMode.CLAMP_TO_EDGE);
<span style="color: #bbbbbb"> </span><span style="color: #008000; font-weight: bold">this</span>.matinstance.setTextureParameter(<span style="color: #BA2121">&#39;albedo&#39;</span>,<span style="color: #bbbbbb"> </span>albedo,<span style="color: #bbbbbb"> </span>sampler);<span style="color: #bbbbbb"></span>
<span style="color: #bbbbbb"> </span><span style="color: #008000; font-weight: bold">this</span>.matinstance.setTextureParameter(<span style="color: #BA2121">&#39;roughness&#39;</span>,<span style="color: #bbbbbb"> </span>roughness,<span style="color: #bbbbbb"> </span>sampler);<span style="color: #bbbbbb"></span>
<span style="color: #bbbbbb"> </span><span style="color: #008000; font-weight: bold">this</span>.matinstance.setTextureParameter(<span style="color: #BA2121">&#39;metallic&#39;</span>,<span style="color: #bbbbbb"> </span>metallic,<span style="color: #bbbbbb"> </span>sampler);<span style="color: #bbbbbb"></span>
<span style="color: #bbbbbb"> </span><span style="color: #008000; font-weight: bold">this</span>.matinstance.setTextureParameter(<span style="color: #BA2121">&#39;normal&#39;</span>,<span style="color: #bbbbbb"> </span>normal,<span style="color: #bbbbbb"> </span>sampler);<span style="color: #bbbbbb"></span>
<span style="color: #bbbbbb"> </span><span style="color: #008000; font-weight: bold">this</span>.matinstance.setTextureParameter(<span style="color: #BA2121">&#39;ao&#39;</span>,<span style="color: #bbbbbb"> </span>ao,<span style="color: #bbbbbb"> </span>sampler);<span style="color: #bbbbbb"></span>
<span style="color: #008000; font-weight: bold">this</span>.matinstance.setTextureParameter(<span style="color: #BA2121">&#39;albedo&#39;</span>, albedo, sampler);
<span style="color: #008000; font-weight: bold">this</span>.matinstance.setTextureParameter(<span style="color: #BA2121">&#39;roughness&#39;</span>, roughness, sampler);
<span style="color: #008000; font-weight: bold">this</span>.matinstance.setTextureParameter(<span style="color: #BA2121">&#39;metallic&#39;</span>, metallic, sampler);
<span style="color: #008000; font-weight: bold">this</span>.matinstance.setTextureParameter(<span style="color: #BA2121">&#39;normal&#39;</span>, normal, sampler);
<span style="color: #008000; font-weight: bold">this</span>.matinstance.setTextureParameter(<span style="color: #BA2121">&#39;ao&#39;</span>, ao, sampler);
<span style="color: #bbbbbb"> </span><span style="color: #3D7B7B; font-style: italic">// Replace low-res skybox with high-res skybox.</span><span style="color: #bbbbbb"></span>
<span style="color: #bbbbbb"> </span><span style="color: #008000; font-weight: bold">this</span>.engine.destroySkybox(<span style="color: #008000; font-weight: bold">this</span>.skybox);<span style="color: #bbbbbb"></span>
<span style="color: #bbbbbb"> </span><span style="color: #008000; font-weight: bold">this</span>.skybox<span style="color: #bbbbbb"> </span><span style="color: #666666">=</span><span style="color: #bbbbbb"> </span><span style="color: #008000; font-weight: bold">this</span>.engine.createSkyFromKtx1(sky_large_url);<span style="color: #bbbbbb"></span>
<span style="color: #bbbbbb"> </span><span style="color: #008000; font-weight: bold">this</span>.scene.setSkybox(<span style="color: #008000; font-weight: bold">this</span>.skybox);<span style="color: #bbbbbb"></span>
<span style="color: #3D7B7B; font-style: italic">// Replace low-res skybox with high-res skybox.</span>
<span style="color: #008000; font-weight: bold">this</span>.engine.destroySkybox(<span style="color: #008000; font-weight: bold">this</span>.skybox);
<span style="color: #008000; font-weight: bold">this</span>.skybox <span style="color: #666666">=</span> <span style="color: #008000; font-weight: bold">this</span>.engine.createSkyFromKtx(sky_large_url);
<span style="color: #008000; font-weight: bold">this</span>.scene.setSkybox(<span style="color: #008000; font-weight: bold">this</span>.skybox);
<span style="color: #bbbbbb"> </span><span style="color: #008000; font-weight: bold">this</span>.scene.addEntity(<span style="color: #008000; font-weight: bold">this</span>.suzanne);<span style="color: #bbbbbb"></span>
});<span style="color: #bbbbbb"></span>
<span style="color: #008000; font-weight: bold">this</span>.scene.addEntity(<span style="color: #008000; font-weight: bold">this</span>.suzanne);
});
</pre></div>
<h2>Introduce trackball rotation</h2>
@@ -241,16 +241,16 @@ listens for drag events and computes a rotation matrix.</p>
<p>Next, replace the <strong>initialize gltumble</strong> and <strong>apply gltumble matrix</strong> comments with the following
two code snippets.</p>
<div class="highlight" style="background: #f8f8f8"><pre style="line-height: 125%;"><span></span><span style="color: #008000; font-weight: bold">this</span>.trackball<span style="color: #bbbbbb"> </span><span style="color: #666666">=</span><span style="color: #bbbbbb"> </span><span style="color: #AA22FF; font-weight: bold">new</span><span style="color: #bbbbbb"> </span>Trackball(canvas,<span style="color: #bbbbbb"> </span>{startSpin<span style="color: #666666">:</span><span style="color: #bbbbbb"> </span><span style="color: #666666">0.035</span>});<span style="color: #bbbbbb"></span>
<div class="highlight" style="background: #f8f8f8"><pre style="line-height: 125%;"><span></span><span style="color: #008000; font-weight: bold">this</span>.trackball <span style="color: #666666">=</span> <span style="color: #AA22FF; font-weight: bold">new</span> Trackball(canvas, {startSpin<span style="color: #666666">:</span> <span style="color: #666666">0.035</span>});
</pre></div>
<div class="highlight" style="background: #f8f8f8"><pre style="line-height: 125%;"><span></span><span style="color: #008000; font-weight: bold">const</span><span style="color: #bbbbbb"> </span>tcm<span style="color: #bbbbbb"> </span><span style="color: #666666">=</span><span style="color: #bbbbbb"> </span><span style="color: #008000; font-weight: bold">this</span>.engine.getTransformManager();<span style="color: #bbbbbb"></span>
<span style="color: #008000; font-weight: bold">const</span><span style="color: #bbbbbb"> </span>inst<span style="color: #bbbbbb"> </span><span style="color: #666666">=</span><span style="color: #bbbbbb"> </span>tcm.getInstance(<span style="color: #008000; font-weight: bold">this</span>.suzanne);<span style="color: #bbbbbb"></span>
tcm.setTransform(inst,<span style="color: #bbbbbb"> </span><span style="color: #008000; font-weight: bold">this</span>.trackball.getMatrix());<span style="color: #bbbbbb"></span>
inst.<span style="color: #AA22FF; font-weight: bold">delete</span>();<span style="color: #bbbbbb"></span>
<div class="highlight" style="background: #f8f8f8"><pre style="line-height: 125%;"><span></span><span style="color: #008000; font-weight: bold">const</span> tcm <span style="color: #666666">=</span> <span style="color: #008000; font-weight: bold">this</span>.engine.getTransformManager();
<span style="color: #008000; font-weight: bold">const</span> inst <span style="color: #666666">=</span> tcm.getInstance(<span style="color: #008000; font-weight: bold">this</span>.suzanne);
tcm.setTransform(inst, <span style="color: #008000; font-weight: bold">this</span>.trackball.getMatrix());
inst.<span style="color: #AA22FF; font-weight: bold">delete</span>();
</pre></div>
<p>That&#x27;s it, we now have a fast-loading interactive demo. The complete JavaScript file is available
<p>That's it, we now have a fast-loading interactive demo. The complete JavaScript file is available
<a href="tutorial_suzanne.js">here</a>.</p>
</body>
+8 -8
View File
@@ -23,9 +23,9 @@ class App {
this.matinstance = material.createInstance();
const filamesh = this.engine.loadFilamesh(filamesh_url, this.matinstance);
this.suzanne = filamesh.renderable;
this.skybox = this.engine.createSkyFromKtx1(sky_small_url);
this.skybox = this.engine.createSkyFromKtx(sky_small_url);
this.scene.setSkybox(this.skybox);
this.indirectLight = this.engine.createIblFromKtx1(ibl_url);
this.indirectLight = this.engine.createIblFromKtx(ibl_url);
this.indirectLight.setIntensity(100000);
this.scene.setIndirectLight(this.indirectLight);
this.trackball = new Trackball(canvas, {
@@ -33,13 +33,13 @@ class App {
});
Filament.fetch([sky_large_url, albedo_url, roughness_url, metallic_url, normal_url, ao_url],
() => {
const albedo = this.engine.createTextureFromKtx1(albedo_url, {
const albedo = this.engine.createTextureFromKtx(albedo_url, {
srgb: true
});
const roughness = this.engine.createTextureFromKtx1(roughness_url);
const metallic = this.engine.createTextureFromKtx1(metallic_url);
const normal = this.engine.createTextureFromKtx1(normal_url);
const ao = this.engine.createTextureFromKtx1(ao_url);
const roughness = this.engine.createTextureFromKtx(roughness_url);
const metallic = this.engine.createTextureFromKtx(metallic_url);
const normal = this.engine.createTextureFromKtx(normal_url);
const ao = this.engine.createTextureFromKtx(ao_url);
const sampler = new Filament.TextureSampler(Filament.MinFilter.LINEAR_MIPMAP_LINEAR,
Filament.MagFilter.LINEAR, Filament.WrapMode.CLAMP_TO_EDGE);
this.matinstance.setTextureParameter('albedo', albedo, sampler);
@@ -49,7 +49,7 @@ class App {
this.matinstance.setTextureParameter('ao', ao, sampler);
// Replace low-res skybox with high-res skybox.
this.engine.destroySkybox(this.skybox);
this.skybox = this.engine.createSkyFromKtx1(sky_large_url);
this.skybox = this.engine.createSkyFromKtx(sky_large_url);
this.scene.setSkybox(this.skybox);
this.scene.addEntity(this.suzanne);
});
+97 -97
View File
@@ -8,9 +8,9 @@
<div class="demo_frame"><iframe src="demo_triangle.html"></iframe><a href="demo_triangle.html">&#x1F517;</a></div>
<h2>Literate programming</h2>
<p>The markdown source for this tutorial is not only used to generate this
web page, it&#x27;s also used to generate the JavaScript for the above demo.
web page, it's also used to generate the JavaScript for the above demo.
We use a small Python script for weaving (generating HTML) and tangling
(generating JS). In the code samples, you&#x27;ll often see
(generating JS). In the code samples, you'll often see
<code>// TODO: &lt;some task&gt;</code>. These are special markers that get replaced by
subsequent code blocks.</p>
<h2>Start your project</h2>
@@ -48,36 +48,36 @@ a mobile-friendly page with a full-screen canvas.</p>
<li><code>triangle.js</code> will contain your application code.</li>
</ul>
<p>Go ahead and create <code>triangle.js</code> with the following content.</p>
<div class="highlight" style="background: #f8f8f8"><pre style="line-height: 125%;"><span></span><span style="color: #008000; font-weight: bold">class</span><span style="color: #bbbbbb"> </span>App<span style="color: #bbbbbb"> </span>{<span style="color: #bbbbbb"></span>
<span style="color: #bbbbbb"> </span><span style="color: #008000; font-weight: bold">constructor</span>()<span style="color: #bbbbbb"> </span>{<span style="color: #bbbbbb"></span>
<span style="color: #bbbbbb"> </span><span style="color: #3D7B7B; font-style: italic">// TODO: create entities</span><span style="color: #bbbbbb"></span>
<span style="color: #bbbbbb"> </span><span style="color: #008000; font-weight: bold">this</span>.render<span style="color: #bbbbbb"> </span><span style="color: #666666">=</span><span style="color: #bbbbbb"> </span><span style="color: #008000; font-weight: bold">this</span>.render.bind(<span style="color: #008000; font-weight: bold">this</span>);<span style="color: #bbbbbb"></span>
<span style="color: #bbbbbb"> </span><span style="color: #008000; font-weight: bold">this</span>.resize<span style="color: #bbbbbb"> </span><span style="color: #666666">=</span><span style="color: #bbbbbb"> </span><span style="color: #008000; font-weight: bold">this</span>.resize.bind(<span style="color: #008000; font-weight: bold">this</span>);<span style="color: #bbbbbb"></span>
<span style="color: #bbbbbb"> </span><span style="color: #008000">window</span>.addEventListener(<span style="color: #BA2121">&#39;resize&#39;</span>,<span style="color: #bbbbbb"> </span><span style="color: #008000; font-weight: bold">this</span>.resize);<span style="color: #bbbbbb"></span>
<span style="color: #bbbbbb"> </span><span style="color: #008000">window</span>.requestAnimationFrame(<span style="color: #008000; font-weight: bold">this</span>.render);<span style="color: #bbbbbb"></span>
<span style="color: #bbbbbb"> </span>}<span style="color: #bbbbbb"></span>
<span style="color: #bbbbbb"> </span>render()<span style="color: #bbbbbb"> </span>{<span style="color: #bbbbbb"></span>
<span style="color: #bbbbbb"> </span><span style="color: #3D7B7B; font-style: italic">// TODO: render scene</span><span style="color: #bbbbbb"></span>
<span style="color: #bbbbbb"> </span><span style="color: #008000">window</span>.requestAnimationFrame(<span style="color: #008000; font-weight: bold">this</span>.render);<span style="color: #bbbbbb"></span>
<span style="color: #bbbbbb"> </span>}<span style="color: #bbbbbb"></span>
<span style="color: #bbbbbb"> </span>resize()<span style="color: #bbbbbb"> </span>{<span style="color: #bbbbbb"></span>
<span style="color: #bbbbbb"> </span><span style="color: #3D7B7B; font-style: italic">// TODO: adjust viewport and canvas</span><span style="color: #bbbbbb"></span>
<span style="color: #bbbbbb"> </span>}<span style="color: #bbbbbb"></span>
}<span style="color: #bbbbbb"></span>
<div class="highlight" style="background: #f8f8f8"><pre style="line-height: 125%;"><span></span><span style="color: #008000; font-weight: bold">class</span> App {
<span style="color: #008000; font-weight: bold">constructor</span>() {
<span style="color: #3D7B7B; font-style: italic">// TODO: create entities</span>
<span style="color: #008000; font-weight: bold">this</span>.render <span style="color: #666666">=</span> <span style="color: #008000; font-weight: bold">this</span>.render.bind(<span style="color: #008000; font-weight: bold">this</span>);
<span style="color: #008000; font-weight: bold">this</span>.resize <span style="color: #666666">=</span> <span style="color: #008000; font-weight: bold">this</span>.resize.bind(<span style="color: #008000; font-weight: bold">this</span>);
<span style="color: #008000">window</span>.addEventListener(<span style="color: #BA2121">&#39;resize&#39;</span>, <span style="color: #008000; font-weight: bold">this</span>.resize);
<span style="color: #008000">window</span>.requestAnimationFrame(<span style="color: #008000; font-weight: bold">this</span>.render);
}
render() {
<span style="color: #3D7B7B; font-style: italic">// TODO: render scene</span>
<span style="color: #008000">window</span>.requestAnimationFrame(<span style="color: #008000; font-weight: bold">this</span>.render);
}
resize() {
<span style="color: #3D7B7B; font-style: italic">// TODO: adjust viewport and canvas</span>
}
}
Filament.init([<span style="color: #BA2121">&#39;triangle.filamat&#39;</span>],<span style="color: #bbbbbb"> </span>()<span style="color: #bbbbbb"> </span>=&gt;<span style="color: #bbbbbb"> </span>{<span style="color: #bbbbbb"> </span><span style="color: #008000">window</span>.app<span style="color: #bbbbbb"> </span><span style="color: #666666">=</span><span style="color: #bbbbbb"> </span><span style="color: #AA22FF; font-weight: bold">new</span><span style="color: #bbbbbb"> </span>App()<span style="color: #bbbbbb"> </span>}<span style="color: #bbbbbb"> </span>);<span style="color: #bbbbbb"></span>
Filament.init([<span style="color: #BA2121">&#39;triangle.filamat&#39;</span>], () =&gt; { <span style="color: #008000">window</span>.app <span style="color: #666666">=</span> <span style="color: #AA22FF; font-weight: bold">new</span> App() } );
</pre></div>
<p>The two calls to <code>bind()</code> allow us to pass instance methods as callbacks for animation and resize
events.</p>
<p><code>Filament.init()</code> consumes two things: a list of asset URLs and a callback.</p>
<p>The callback will be triggered only after all assets finish downloading and the Filament module has
become ready. In our callback, we simply instantiated the <code>App</code> object, since we&#x27;ll do most of the
become ready. In our callback, we simply instantiated the <code>App</code> object, since we'll do most of the
work in its constructor. We also set the app instance into a <code>Window</code> property to make it accessible
from the developer console.</p>
<p>Go ahead and download <a href="triangle.filamat">triangle.filamat</a> and place it in your project folder.
This is a <em>material package</em>, which is a binary file that contains shaders and other bits of data
that define a PBR material. We&#x27;ll learn more about material packages in the next tutorial.</p>
that define a PBR material. We'll learn more about material packages in the next tutorial.</p>
<h2>Spawn a local server</h2>
<p>Because of CORS restrictions, your web app cannot fetch the material package directly from the
file system. One way around this is to create a temporary server using Python or node:</p>
@@ -88,23 +88,23 @@ npx http-server -p <span style="color: #666666">8000</span> <span style="colo
<p>To see if this works, navigate to <a href="http://localhost:8000">http://localhost:8000</a> and check if you
can load the page without any errors appearing in the developer console.</p>
<p>Take care not to use Python&#x27;s simple server in production since it does not serve WebAssembly files
<p>Take care not to use Python's simple server in production since it does not serve WebAssembly files
with the correct MIME type.</p>
<h2>Create the Engine and Scene</h2>
<p>We now have a basic skeleton that can respond to paint and resize events. Let&#x27;s start adding
<p>We now have a basic skeleton that can respond to paint and resize events. Let's start adding
Filament objects to the app. Insert the following code into the top of the app constructor.</p>
<div class="highlight" style="background: #f8f8f8"><pre style="line-height: 125%;"><span></span><span style="color: #008000; font-weight: bold">this</span>.canvas<span style="color: #bbbbbb"> </span><span style="color: #666666">=</span><span style="color: #bbbbbb"> </span><span style="color: #008000">document</span>.getElementsByTagName(<span style="color: #BA2121">&#39;canvas&#39;</span>)[<span style="color: #666666">0</span>];<span style="color: #bbbbbb"></span>
<span style="color: #008000; font-weight: bold">const</span><span style="color: #bbbbbb"> </span>engine<span style="color: #bbbbbb"> </span><span style="color: #666666">=</span><span style="color: #bbbbbb"> </span><span style="color: #008000; font-weight: bold">this</span>.engine<span style="color: #bbbbbb"> </span><span style="color: #666666">=</span><span style="color: #bbbbbb"> </span>Filament.Engine.create(<span style="color: #008000; font-weight: bold">this</span>.canvas);<span style="color: #bbbbbb"></span>
<div class="highlight" style="background: #f8f8f8"><pre style="line-height: 125%;"><span></span><span style="color: #008000; font-weight: bold">this</span>.canvas <span style="color: #666666">=</span> <span style="color: #008000">document</span>.getElementsByTagName(<span style="color: #BA2121">&#39;canvas&#39;</span>)[<span style="color: #666666">0</span>];
<span style="color: #008000; font-weight: bold">const</span> engine <span style="color: #666666">=</span> <span style="color: #008000; font-weight: bold">this</span>.engine <span style="color: #666666">=</span> Filament.Engine.create(<span style="color: #008000; font-weight: bold">this</span>.canvas);
</pre></div>
<p>The above snippet creates the <code>Engine</code> by passing it a canvas DOM object. The engine needs the
canvas in order to create a WebGL 2.0 context in its contructor.</p>
<p>The engine is a factory for many Filament entities, including <code>Scene</code>, which is a flat container of
entities. Let&#x27;s go ahead and create a scene, then add a blank entity called <code>triangle</code> into the
entities. Let's go ahead and create a scene, then add a blank entity called <code>triangle</code> into the
scene.</p>
<div class="highlight" style="background: #f8f8f8"><pre style="line-height: 125%;"><span></span><span style="color: #008000; font-weight: bold">this</span>.scene<span style="color: #bbbbbb"> </span><span style="color: #666666">=</span><span style="color: #bbbbbb"> </span>engine.createScene();<span style="color: #bbbbbb"></span>
<span style="color: #008000; font-weight: bold">this</span>.triangle<span style="color: #bbbbbb"> </span><span style="color: #666666">=</span><span style="color: #bbbbbb"> </span>Filament.EntityManager.get().create();<span style="color: #bbbbbb"></span>
<span style="color: #008000; font-weight: bold">this</span>.scene.addEntity(<span style="color: #008000; font-weight: bold">this</span>.triangle);<span style="color: #bbbbbb"></span>
<div class="highlight" style="background: #f8f8f8"><pre style="line-height: 125%;"><span></span><span style="color: #008000; font-weight: bold">this</span>.scene <span style="color: #666666">=</span> engine.createScene();
<span style="color: #008000; font-weight: bold">this</span>.triangle <span style="color: #666666">=</span> Filament.EntityManager.get().create();
<span style="color: #008000; font-weight: bold">this</span>.scene.addEntity(<span style="color: #008000; font-weight: bold">this</span>.triangle);
</pre></div>
<p>Filament uses an <a href="//en.wikipedia.org/wiki/Entity-component-system">Entity-Component System</a>.
@@ -112,30 +112,30 @@ The triangle entity in the above snippet does not yet have an associated compone
tutorial we will make it into a <em>renderable</em>. Renderables are entities that have associated draw
calls.</p>
<h2>Construct typed arrays</h2>
<p>Next we&#x27;ll create two typed arrays: a positions array with XY coordinates for each vertex, and a
<p>Next we'll create two typed arrays: a positions array with XY coordinates for each vertex, and a
colors array with a 32-bit word for each vertex.</p>
<div class="highlight" style="background: #f8f8f8"><pre style="line-height: 125%;"><span></span><span style="color: #008000; font-weight: bold">const</span><span style="color: #bbbbbb"> </span>TRIANGLE_POSITIONS<span style="color: #bbbbbb"> </span><span style="color: #666666">=</span><span style="color: #bbbbbb"> </span><span style="color: #AA22FF; font-weight: bold">new</span><span style="color: #bbbbbb"> </span><span style="color: #008000">Float32Array</span>([<span style="color: #bbbbbb"></span>
<span style="color: #bbbbbb"> </span><span style="color: #666666">1</span>,<span style="color: #bbbbbb"> </span><span style="color: #666666">0</span>,<span style="color: #bbbbbb"></span>
<span style="color: #bbbbbb"> </span><span style="color: #008000">Math</span>.cos(<span style="color: #008000">Math</span>.PI<span style="color: #bbbbbb"> </span><span style="color: #666666">*</span><span style="color: #bbbbbb"> </span><span style="color: #666666">2</span><span style="color: #bbbbbb"> </span><span style="color: #666666">/</span><span style="color: #bbbbbb"> </span><span style="color: #666666">3</span>),<span style="color: #bbbbbb"> </span><span style="color: #008000">Math</span>.sin(<span style="color: #008000">Math</span>.PI<span style="color: #bbbbbb"> </span><span style="color: #666666">*</span><span style="color: #bbbbbb"> </span><span style="color: #666666">2</span><span style="color: #bbbbbb"> </span><span style="color: #666666">/</span><span style="color: #bbbbbb"> </span><span style="color: #666666">3</span>),<span style="color: #bbbbbb"></span>
<span style="color: #bbbbbb"> </span><span style="color: #008000">Math</span>.cos(<span style="color: #008000">Math</span>.PI<span style="color: #bbbbbb"> </span><span style="color: #666666">*</span><span style="color: #bbbbbb"> </span><span style="color: #666666">4</span><span style="color: #bbbbbb"> </span><span style="color: #666666">/</span><span style="color: #bbbbbb"> </span><span style="color: #666666">3</span>),<span style="color: #bbbbbb"> </span><span style="color: #008000">Math</span>.sin(<span style="color: #008000">Math</span>.PI<span style="color: #bbbbbb"> </span><span style="color: #666666">*</span><span style="color: #bbbbbb"> </span><span style="color: #666666">4</span><span style="color: #bbbbbb"> </span><span style="color: #666666">/</span><span style="color: #bbbbbb"> </span><span style="color: #666666">3</span>),<span style="color: #bbbbbb"></span>
]);<span style="color: #bbbbbb"></span>
<div class="highlight" style="background: #f8f8f8"><pre style="line-height: 125%;"><span></span><span style="color: #008000; font-weight: bold">const</span> TRIANGLE_POSITIONS <span style="color: #666666">=</span> <span style="color: #AA22FF; font-weight: bold">new</span> <span style="color: #008000">Float32Array</span>([
<span style="color: #666666">1</span>, <span style="color: #666666">0</span>,
<span style="color: #008000">Math</span>.cos(<span style="color: #008000">Math</span>.PI <span style="color: #666666">*</span> <span style="color: #666666">2</span> <span style="color: #666666">/</span> <span style="color: #666666">3</span>), <span style="color: #008000">Math</span>.sin(<span style="color: #008000">Math</span>.PI <span style="color: #666666">*</span> <span style="color: #666666">2</span> <span style="color: #666666">/</span> <span style="color: #666666">3</span>),
<span style="color: #008000">Math</span>.cos(<span style="color: #008000">Math</span>.PI <span style="color: #666666">*</span> <span style="color: #666666">4</span> <span style="color: #666666">/</span> <span style="color: #666666">3</span>), <span style="color: #008000">Math</span>.sin(<span style="color: #008000">Math</span>.PI <span style="color: #666666">*</span> <span style="color: #666666">4</span> <span style="color: #666666">/</span> <span style="color: #666666">3</span>),
]);
<span style="color: #008000; font-weight: bold">const</span><span style="color: #bbbbbb"> </span>TRIANGLE_COLORS<span style="color: #bbbbbb"> </span><span style="color: #666666">=</span><span style="color: #bbbbbb"> </span><span style="color: #AA22FF; font-weight: bold">new</span><span style="color: #bbbbbb"> </span><span style="color: #008000">Uint32Array</span>([<span style="color: #666666">0xffff0000</span>,<span style="color: #bbbbbb"> </span><span style="color: #666666">0xff00ff00</span>,<span style="color: #bbbbbb"> </span><span style="color: #666666">0xff0000ff</span>]);<span style="color: #bbbbbb"></span>
<span style="color: #008000; font-weight: bold">const</span> TRIANGLE_COLORS <span style="color: #666666">=</span> <span style="color: #AA22FF; font-weight: bold">new</span> <span style="color: #008000">Uint32Array</span>([<span style="color: #666666">0xffff0000</span>, <span style="color: #666666">0xff00ff00</span>, <span style="color: #666666">0xff0000ff</span>]);
</pre></div>
<p>Next we&#x27;ll use the positions and colors buffers to create a single <code>VertexBuffer</code> object.</p>
<div class="highlight" style="background: #f8f8f8"><pre style="line-height: 125%;"><span></span><span style="color: #008000; font-weight: bold">const</span><span style="color: #bbbbbb"> </span>VertexAttribute<span style="color: #bbbbbb"> </span><span style="color: #666666">=</span><span style="color: #bbbbbb"> </span>Filament.VertexAttribute;<span style="color: #bbbbbb"></span>
<span style="color: #008000; font-weight: bold">const</span><span style="color: #bbbbbb"> </span>AttributeType<span style="color: #bbbbbb"> </span><span style="color: #666666">=</span><span style="color: #bbbbbb"> </span>Filament.VertexBuffer$AttributeType;<span style="color: #bbbbbb"></span>
<span style="color: #008000; font-weight: bold">this</span>.vb<span style="color: #bbbbbb"> </span><span style="color: #666666">=</span><span style="color: #bbbbbb"> </span>Filament.VertexBuffer.Builder()<span style="color: #bbbbbb"></span>
<span style="color: #bbbbbb"> </span>.vertexCount(<span style="color: #666666">3</span>)<span style="color: #bbbbbb"></span>
<span style="color: #bbbbbb"> </span>.bufferCount(<span style="color: #666666">2</span>)<span style="color: #bbbbbb"></span>
<span style="color: #bbbbbb"> </span>.attribute(VertexAttribute.POSITION,<span style="color: #bbbbbb"> </span><span style="color: #666666">0</span>,<span style="color: #bbbbbb"> </span>AttributeType.FLOAT2,<span style="color: #bbbbbb"> </span><span style="color: #666666">0</span>,<span style="color: #bbbbbb"> </span><span style="color: #666666">8</span>)<span style="color: #bbbbbb"></span>
<span style="color: #bbbbbb"> </span>.attribute(VertexAttribute.COLOR,<span style="color: #bbbbbb"> </span><span style="color: #666666">1</span>,<span style="color: #bbbbbb"> </span>AttributeType.UBYTE4,<span style="color: #bbbbbb"> </span><span style="color: #666666">0</span>,<span style="color: #bbbbbb"> </span><span style="color: #666666">4</span>)<span style="color: #bbbbbb"></span>
<span style="color: #bbbbbb"> </span>.normalized(VertexAttribute.COLOR)<span style="color: #bbbbbb"></span>
<span style="color: #bbbbbb"> </span>.build(engine);<span style="color: #bbbbbb"></span>
<p>Next we'll use the positions and colors buffers to create a single <code>VertexBuffer</code> object.</p>
<div class="highlight" style="background: #f8f8f8"><pre style="line-height: 125%;"><span></span><span style="color: #008000; font-weight: bold">const</span> VertexAttribute <span style="color: #666666">=</span> Filament.VertexAttribute;
<span style="color: #008000; font-weight: bold">const</span> AttributeType <span style="color: #666666">=</span> Filament.VertexBuffer$AttributeType;
<span style="color: #008000; font-weight: bold">this</span>.vb <span style="color: #666666">=</span> Filament.VertexBuffer.Builder()
.vertexCount(<span style="color: #666666">3</span>)
.bufferCount(<span style="color: #666666">2</span>)
.attribute(VertexAttribute.POSITION, <span style="color: #666666">0</span>, AttributeType.FLOAT2, <span style="color: #666666">0</span>, <span style="color: #666666">8</span>)
.attribute(VertexAttribute.COLOR, <span style="color: #666666">1</span>, AttributeType.UBYTE4, <span style="color: #666666">0</span>, <span style="color: #666666">4</span>)
.normalized(VertexAttribute.COLOR)
.build(engine);
<span style="color: #008000; font-weight: bold">this</span>.vb.setBufferAt(engine,<span style="color: #bbbbbb"> </span><span style="color: #666666">0</span>,<span style="color: #bbbbbb"> </span>TRIANGLE_POSITIONS);<span style="color: #bbbbbb"></span>
<span style="color: #008000; font-weight: bold">this</span>.vb.setBufferAt(engine,<span style="color: #bbbbbb"> </span><span style="color: #666666">1</span>,<span style="color: #bbbbbb"> </span>TRIANGLE_COLORS);<span style="color: #bbbbbb"></span>
<span style="color: #008000; font-weight: bold">this</span>.vb.setBufferAt(engine, <span style="color: #666666">0</span>, TRIANGLE_POSITIONS);
<span style="color: #008000; font-weight: bold">this</span>.vb.setBufferAt(engine, <span style="color: #666666">1</span>, TRIANGLE_COLORS);
</pre></div>
<p>The above snippet first creates aliases for two enum types, then constructs the vertex buffer using
@@ -147,74 +147,74 @@ self-documenting.</p>
<p>Our app sets up two buffer slots in the vertex buffer, and each slot is associated with a single
attribute. Alternatively, we could have interleaved or concatenated these attributes into a single
buffer slot.</p>
<p>Next we&#x27;ll construct an index buffer. The index buffer for our triangle is trivial: it simply holds
<p>Next we'll construct an index buffer. The index buffer for our triangle is trivial: it simply holds
the integers 0,1,2.</p>
<div class="highlight" style="background: #f8f8f8"><pre style="line-height: 125%;"><span></span><span style="color: #008000; font-weight: bold">this</span>.ib<span style="color: #bbbbbb"> </span><span style="color: #666666">=</span><span style="color: #bbbbbb"> </span>Filament.IndexBuffer.Builder()<span style="color: #bbbbbb"></span>
<span style="color: #bbbbbb"> </span>.indexCount(<span style="color: #666666">3</span>)<span style="color: #bbbbbb"></span>
<span style="color: #bbbbbb"> </span>.bufferType(Filament.IndexBuffer$IndexType.USHORT)<span style="color: #bbbbbb"></span>
<span style="color: #bbbbbb"> </span>.build(engine);<span style="color: #bbbbbb"></span>
<div class="highlight" style="background: #f8f8f8"><pre style="line-height: 125%;"><span></span><span style="color: #008000; font-weight: bold">this</span>.ib <span style="color: #666666">=</span> Filament.IndexBuffer.Builder()
.indexCount(<span style="color: #666666">3</span>)
.bufferType(Filament.IndexBuffer$IndexType.USHORT)
.build(engine);
<span style="color: #008000; font-weight: bold">this</span>.ib.setBuffer(engine,<span style="color: #bbbbbb"> </span><span style="color: #AA22FF; font-weight: bold">new</span><span style="color: #bbbbbb"> </span><span style="color: #008000">Uint16Array</span>([<span style="color: #666666">0</span>,<span style="color: #bbbbbb"> </span><span style="color: #666666">1</span>,<span style="color: #bbbbbb"> </span><span style="color: #666666">2</span>]));<span style="color: #bbbbbb"></span>
<span style="color: #008000; font-weight: bold">this</span>.ib.setBuffer(engine, <span style="color: #AA22FF; font-weight: bold">new</span> <span style="color: #008000">Uint16Array</span>([<span style="color: #666666">0</span>, <span style="color: #666666">1</span>, <span style="color: #666666">2</span>]));
</pre></div>
<p>Note that constructing an index buffer is similar to constructing a vertex buffer, but it only has
one buffer slot, and it can only contain two types of data (USHORT or UINT).</p>
<h2>Finish up initialization</h2>
<p>Next let&#x27;s construct an actual <code>Material</code> from the material package that was downloaded (the
<p>Next let's construct an actual <code>Material</code> from the material package that was downloaded (the
material is an object; the package is just a binary blob), then extract the default
<code>MaterialInstance</code> from the material object. Material instances have concrete values for their
parameters, and they can be bound to renderables. We&#x27;ll learn more about material instances in the
parameters, and they can be bound to renderables. We'll learn more about material instances in the
next tutorial.</p>
<p>After extracting the material instance, we can finally create a renderable component for the
triangle by setting up a bounding box and passing in the vertex and index buffers.</p>
<div class="highlight" style="background: #f8f8f8"><pre style="line-height: 125%;"><span></span><span style="color: #008000; font-weight: bold">const</span><span style="color: #bbbbbb"> </span>mat<span style="color: #bbbbbb"> </span><span style="color: #666666">=</span><span style="color: #bbbbbb"> </span>engine.createMaterial(<span style="color: #BA2121">&#39;triangle.filamat&#39;</span>);<span style="color: #bbbbbb"></span>
<span style="color: #008000; font-weight: bold">const</span><span style="color: #bbbbbb"> </span>matinst<span style="color: #bbbbbb"> </span><span style="color: #666666">=</span><span style="color: #bbbbbb"> </span>mat.getDefaultInstance();<span style="color: #bbbbbb"></span>
Filament.RenderableManager.Builder(<span style="color: #666666">1</span>)<span style="color: #bbbbbb"></span>
<span style="color: #bbbbbb"> </span>.boundingBox({<span style="color: #bbbbbb"> </span>center<span style="color: #666666">:</span><span style="color: #bbbbbb"> </span>[<span style="color: #666666">-1</span>,<span style="color: #bbbbbb"> </span><span style="color: #666666">-1</span>,<span style="color: #bbbbbb"> </span><span style="color: #666666">-1</span>],<span style="color: #bbbbbb"> </span>halfExtent<span style="color: #666666">:</span><span style="color: #bbbbbb"> </span>[<span style="color: #666666">1</span>,<span style="color: #bbbbbb"> </span><span style="color: #666666">1</span>,<span style="color: #bbbbbb"> </span><span style="color: #666666">1</span>]<span style="color: #bbbbbb"> </span>})<span style="color: #bbbbbb"></span>
<span style="color: #bbbbbb"> </span>.material(<span style="color: #666666">0</span>,<span style="color: #bbbbbb"> </span>matinst)<span style="color: #bbbbbb"></span>
<span style="color: #bbbbbb"> </span>.geometry(<span style="color: #666666">0</span>,<span style="color: #bbbbbb"> </span>Filament.RenderableManager$PrimitiveType.TRIANGLES,<span style="color: #bbbbbb"> </span><span style="color: #008000; font-weight: bold">this</span>.vb,<span style="color: #bbbbbb"> </span><span style="color: #008000; font-weight: bold">this</span>.ib)<span style="color: #bbbbbb"></span>
<span style="color: #bbbbbb"> </span>.build(engine,<span style="color: #bbbbbb"> </span><span style="color: #008000; font-weight: bold">this</span>.triangle);<span style="color: #bbbbbb"></span>
<div class="highlight" style="background: #f8f8f8"><pre style="line-height: 125%;"><span></span><span style="color: #008000; font-weight: bold">const</span> mat <span style="color: #666666">=</span> engine.createMaterial(<span style="color: #BA2121">&#39;triangle.filamat&#39;</span>);
<span style="color: #008000; font-weight: bold">const</span> matinst <span style="color: #666666">=</span> mat.getDefaultInstance();
Filament.RenderableManager.Builder(<span style="color: #666666">1</span>)
.boundingBox({ center<span style="color: #666666">:</span> [<span style="color: #666666">-1</span>, <span style="color: #666666">-1</span>, <span style="color: #666666">-1</span>], halfExtent<span style="color: #666666">:</span> [<span style="color: #666666">1</span>, <span style="color: #666666">1</span>, <span style="color: #666666">1</span>] })
.material(<span style="color: #666666">0</span>, matinst)
.geometry(<span style="color: #666666">0</span>, Filament.RenderableManager$PrimitiveType.TRIANGLES, <span style="color: #008000; font-weight: bold">this</span>.vb, <span style="color: #008000; font-weight: bold">this</span>.ib)
.build(engine, <span style="color: #008000; font-weight: bold">this</span>.triangle);
</pre></div>
<p>Next let&#x27;s wrap up the initialization routine by creating the swap chain, renderer, camera, and
<p>Next let's wrap up the initialization routine by creating the swap chain, renderer, camera, and
view.</p>
<div class="highlight" style="background: #f8f8f8"><pre style="line-height: 125%;"><span></span><span style="color: #008000; font-weight: bold">this</span>.swapChain<span style="color: #bbbbbb"> </span><span style="color: #666666">=</span><span style="color: #bbbbbb"> </span>engine.createSwapChain();<span style="color: #bbbbbb"></span>
<span style="color: #008000; font-weight: bold">this</span>.renderer<span style="color: #bbbbbb"> </span><span style="color: #666666">=</span><span style="color: #bbbbbb"> </span>engine.createRenderer();<span style="color: #bbbbbb"></span>
<span style="color: #008000; font-weight: bold">this</span>.camera<span style="color: #bbbbbb"> </span><span style="color: #666666">=</span><span style="color: #bbbbbb"> </span>engine.createCamera(Filament.EntityManager.get().create());<span style="color: #bbbbbb"></span>
<span style="color: #008000; font-weight: bold">this</span>.view<span style="color: #bbbbbb"> </span><span style="color: #666666">=</span><span style="color: #bbbbbb"> </span>engine.createView();<span style="color: #bbbbbb"></span>
<span style="color: #008000; font-weight: bold">this</span>.view.setCamera(<span style="color: #008000; font-weight: bold">this</span>.camera);<span style="color: #bbbbbb"></span>
<span style="color: #008000; font-weight: bold">this</span>.view.setScene(<span style="color: #008000; font-weight: bold">this</span>.scene);<span style="color: #bbbbbb"></span>
<div class="highlight" style="background: #f8f8f8"><pre style="line-height: 125%;"><span></span><span style="color: #008000; font-weight: bold">this</span>.swapChain <span style="color: #666666">=</span> engine.createSwapChain();
<span style="color: #008000; font-weight: bold">this</span>.renderer <span style="color: #666666">=</span> engine.createRenderer();
<span style="color: #008000; font-weight: bold">this</span>.camera <span style="color: #666666">=</span> engine.createCamera(Filament.EntityManager.get().create());
<span style="color: #008000; font-weight: bold">this</span>.view <span style="color: #666666">=</span> engine.createView();
<span style="color: #008000; font-weight: bold">this</span>.view.setCamera(<span style="color: #008000; font-weight: bold">this</span>.camera);
<span style="color: #008000; font-weight: bold">this</span>.view.setScene(<span style="color: #008000; font-weight: bold">this</span>.scene);
<span style="color: #3D7B7B; font-style: italic">// Set up a blue-green background:</span><span style="color: #bbbbbb"></span>
<span style="color: #008000; font-weight: bold">this</span>.renderer.setClearOptions({clearColor<span style="color: #666666">:</span><span style="color: #bbbbbb"> </span>[<span style="color: #666666">0.0</span>,<span style="color: #bbbbbb"> </span><span style="color: #666666">0.1</span>,<span style="color: #bbbbbb"> </span><span style="color: #666666">0.2</span>,<span style="color: #bbbbbb"> </span><span style="color: #666666">1.0</span>],<span style="color: #bbbbbb"> </span>clear<span style="color: #666666">:</span><span style="color: #bbbbbb"> </span><span style="color: #008000; font-weight: bold">true</span>});<span style="color: #bbbbbb"></span>
<span style="color: #3D7B7B; font-style: italic">// Set up a blue-green background:</span>
<span style="color: #008000; font-weight: bold">this</span>.renderer.setClearOptions({clearColor<span style="color: #666666">:</span> [<span style="color: #666666">0.0</span>, <span style="color: #666666">0.1</span>, <span style="color: #666666">0.2</span>, <span style="color: #666666">1.0</span>], clear<span style="color: #666666">:</span> <span style="color: #008000; font-weight: bold">true</span>});
<span style="color: #3D7B7B; font-style: italic">// Adjust the initial viewport:</span><span style="color: #bbbbbb"></span>
<span style="color: #008000; font-weight: bold">this</span>.resize();<span style="color: #bbbbbb"></span>
<span style="color: #3D7B7B; font-style: italic">// Adjust the initial viewport:</span>
<span style="color: #008000; font-weight: bold">this</span>.resize();
</pre></div>
<p>At this point, we&#x27;re done creating all Filament entities, and the code should run without errors.
<p>At this point, we're done creating all Filament entities, and the code should run without errors.
However the canvas is still blank!</p>
<h2>Render and resize handlers</h2>
<p>Recall that our App class has a skeletal render method, which the browser calls every time it needs
to repaint. Often this is 60 times a second.</p>
<div class="highlight" style="background: #f8f8f8"><pre style="line-height: 125%;"><span></span>render()<span style="color: #bbbbbb"> </span>{<span style="color: #bbbbbb"></span>
<span style="color: #bbbbbb"> </span><span style="color: #3D7B7B; font-style: italic">// TODO: render scene</span><span style="color: #bbbbbb"></span>
<span style="color: #bbbbbb"> </span><span style="color: #008000">window</span>.requestAnimationFrame(<span style="color: #008000; font-weight: bold">this</span>.render);<span style="color: #bbbbbb"></span>
}<span style="color: #bbbbbb"></span>
<div class="highlight" style="background: #f8f8f8"><pre style="line-height: 125%;"><span></span>render() {
<span style="color: #3D7B7B; font-style: italic">// TODO: render scene</span>
<span style="color: #008000">window</span>.requestAnimationFrame(<span style="color: #008000; font-weight: bold">this</span>.render);
}
</pre></div>
<p>Let&#x27;s flesh this out by rotating the triangle and invoking the Filament renderer. Add the following
<p>Let's flesh this out by rotating the triangle and invoking the Filament renderer. Add the following
code to the top of the render method.</p>
<div class="highlight" style="background: #f8f8f8"><pre style="line-height: 125%;"><span></span><span style="color: #3D7B7B; font-style: italic">// Rotate the triangle.</span><span style="color: #bbbbbb"></span>
<span style="color: #008000; font-weight: bold">const</span><span style="color: #bbbbbb"> </span>radians<span style="color: #bbbbbb"> </span><span style="color: #666666">=</span><span style="color: #bbbbbb"> </span><span style="color: #008000">Date</span>.now()<span style="color: #bbbbbb"> </span><span style="color: #666666">/</span><span style="color: #bbbbbb"> </span><span style="color: #666666">1000</span>;<span style="color: #bbbbbb"></span>
<span style="color: #008000; font-weight: bold">const</span><span style="color: #bbbbbb"> </span>transform<span style="color: #bbbbbb"> </span><span style="color: #666666">=</span><span style="color: #bbbbbb"> </span>mat4.fromRotation(mat4.create(),<span style="color: #bbbbbb"> </span>radians,<span style="color: #bbbbbb"> </span>[<span style="color: #666666">0</span>,<span style="color: #bbbbbb"> </span><span style="color: #666666">0</span>,<span style="color: #bbbbbb"> </span><span style="color: #666666">1</span>]);<span style="color: #bbbbbb"></span>
<span style="color: #008000; font-weight: bold">const</span><span style="color: #bbbbbb"> </span>tcm<span style="color: #bbbbbb"> </span><span style="color: #666666">=</span><span style="color: #bbbbbb"> </span><span style="color: #008000; font-weight: bold">this</span>.engine.getTransformManager();<span style="color: #bbbbbb"></span>
<span style="color: #008000; font-weight: bold">const</span><span style="color: #bbbbbb"> </span>inst<span style="color: #bbbbbb"> </span><span style="color: #666666">=</span><span style="color: #bbbbbb"> </span>tcm.getInstance(<span style="color: #008000; font-weight: bold">this</span>.triangle);<span style="color: #bbbbbb"></span>
tcm.setTransform(inst,<span style="color: #bbbbbb"> </span>transform);<span style="color: #bbbbbb"></span>
inst.<span style="color: #AA22FF; font-weight: bold">delete</span>();<span style="color: #bbbbbb"></span>
<div class="highlight" style="background: #f8f8f8"><pre style="line-height: 125%;"><span></span><span style="color: #3D7B7B; font-style: italic">// Rotate the triangle.</span>
<span style="color: #008000; font-weight: bold">const</span> radians <span style="color: #666666">=</span> <span style="color: #008000">Date</span>.now() <span style="color: #666666">/</span> <span style="color: #666666">1000</span>;
<span style="color: #008000; font-weight: bold">const</span> transform <span style="color: #666666">=</span> mat4.fromRotation(mat4.create(), radians, [<span style="color: #666666">0</span>, <span style="color: #666666">0</span>, <span style="color: #666666">1</span>]);
<span style="color: #008000; font-weight: bold">const</span> tcm <span style="color: #666666">=</span> <span style="color: #008000; font-weight: bold">this</span>.engine.getTransformManager();
<span style="color: #008000; font-weight: bold">const</span> inst <span style="color: #666666">=</span> tcm.getInstance(<span style="color: #008000; font-weight: bold">this</span>.triangle);
tcm.setTransform(inst, transform);
inst.<span style="color: #AA22FF; font-weight: bold">delete</span>();
<span style="color: #3D7B7B; font-style: italic">// Render the frame.</span><span style="color: #bbbbbb"></span>
<span style="color: #008000; font-weight: bold">this</span>.renderer.render(<span style="color: #008000; font-weight: bold">this</span>.swapChain,<span style="color: #bbbbbb"> </span><span style="color: #008000; font-weight: bold">this</span>.view);<span style="color: #bbbbbb"></span>
<span style="color: #3D7B7B; font-style: italic">// Render the frame.</span>
<span style="color: #008000; font-weight: bold">this</span>.renderer.render(<span style="color: #008000; font-weight: bold">this</span>.swapChain, <span style="color: #008000; font-weight: bold">this</span>.view);
</pre></div>
<p>The first half of our render method obtains the transform component of the triangle entity and uses
@@ -225,19 +225,19 @@ that it wants to skip a frame, hence the <code>if</code> statement.</p>
<p>One last step. Add the following code to the resize method. This adjusts the resolution of the
rendering surface when the window size changes, taking <code>devicePixelRatio</code> into account for high-DPI
displays. It also adjusts the camera frustum accordingly.</p>
<div class="highlight" style="background: #f8f8f8"><pre style="line-height: 125%;"><span></span><span style="color: #008000; font-weight: bold">const</span><span style="color: #bbbbbb"> </span>dpr<span style="color: #bbbbbb"> </span><span style="color: #666666">=</span><span style="color: #bbbbbb"> </span><span style="color: #008000">window</span>.devicePixelRatio;<span style="color: #bbbbbb"></span>
<span style="color: #008000; font-weight: bold">const</span><span style="color: #bbbbbb"> </span>width<span style="color: #bbbbbb"> </span><span style="color: #666666">=</span><span style="color: #bbbbbb"> </span><span style="color: #008000; font-weight: bold">this</span>.canvas.width<span style="color: #bbbbbb"> </span><span style="color: #666666">=</span><span style="color: #bbbbbb"> </span><span style="color: #008000">window</span>.innerWidth<span style="color: #bbbbbb"> </span><span style="color: #666666">*</span><span style="color: #bbbbbb"> </span>dpr;<span style="color: #bbbbbb"></span>
<span style="color: #008000; font-weight: bold">const</span><span style="color: #bbbbbb"> </span>height<span style="color: #bbbbbb"> </span><span style="color: #666666">=</span><span style="color: #bbbbbb"> </span><span style="color: #008000; font-weight: bold">this</span>.canvas.height<span style="color: #bbbbbb"> </span><span style="color: #666666">=</span><span style="color: #bbbbbb"> </span><span style="color: #008000">window</span>.innerHeight<span style="color: #bbbbbb"> </span><span style="color: #666666">*</span><span style="color: #bbbbbb"> </span>dpr;<span style="color: #bbbbbb"></span>
<span style="color: #008000; font-weight: bold">this</span>.view.setViewport([<span style="color: #666666">0</span>,<span style="color: #bbbbbb"> </span><span style="color: #666666">0</span>,<span style="color: #bbbbbb"> </span>width,<span style="color: #bbbbbb"> </span>height]);<span style="color: #bbbbbb"></span>
<div class="highlight" style="background: #f8f8f8"><pre style="line-height: 125%;"><span></span><span style="color: #008000; font-weight: bold">const</span> dpr <span style="color: #666666">=</span> <span style="color: #008000">window</span>.devicePixelRatio;
<span style="color: #008000; font-weight: bold">const</span> width <span style="color: #666666">=</span> <span style="color: #008000; font-weight: bold">this</span>.canvas.width <span style="color: #666666">=</span> <span style="color: #008000">window</span>.innerWidth <span style="color: #666666">*</span> dpr;
<span style="color: #008000; font-weight: bold">const</span> height <span style="color: #666666">=</span> <span style="color: #008000; font-weight: bold">this</span>.canvas.height <span style="color: #666666">=</span> <span style="color: #008000">window</span>.innerHeight <span style="color: #666666">*</span> dpr;
<span style="color: #008000; font-weight: bold">this</span>.view.setViewport([<span style="color: #666666">0</span>, <span style="color: #666666">0</span>, width, height]);
<span style="color: #008000; font-weight: bold">const</span><span style="color: #bbbbbb"> </span>aspect<span style="color: #bbbbbb"> </span><span style="color: #666666">=</span><span style="color: #bbbbbb"> </span>width<span style="color: #bbbbbb"> </span><span style="color: #666666">/</span><span style="color: #bbbbbb"> </span>height;<span style="color: #bbbbbb"></span>
<span style="color: #008000; font-weight: bold">const</span><span style="color: #bbbbbb"> </span>Projection<span style="color: #bbbbbb"> </span><span style="color: #666666">=</span><span style="color: #bbbbbb"> </span>Filament.Camera$Projection;<span style="color: #bbbbbb"></span>
<span style="color: #008000; font-weight: bold">this</span>.camera.setProjection(Projection.ORTHO,<span style="color: #bbbbbb"> </span><span style="color: #666666">-</span>aspect,<span style="color: #bbbbbb"> </span>aspect,<span style="color: #bbbbbb"> </span><span style="color: #666666">-1</span>,<span style="color: #bbbbbb"> </span><span style="color: #666666">1</span>,<span style="color: #bbbbbb"> </span><span style="color: #666666">0</span>,<span style="color: #bbbbbb"> </span><span style="color: #666666">1</span>);<span style="color: #bbbbbb"></span>
<span style="color: #008000; font-weight: bold">const</span> aspect <span style="color: #666666">=</span> width <span style="color: #666666">/</span> height;
<span style="color: #008000; font-weight: bold">const</span> Projection <span style="color: #666666">=</span> Filament.Camera$Projection;
<span style="color: #008000; font-weight: bold">this</span>.camera.setProjection(Projection.ORTHO, <span style="color: #666666">-</span>aspect, aspect, <span style="color: #666666">-1</span>, <span style="color: #666666">1</span>, <span style="color: #666666">0</span>, <span style="color: #666666">1</span>);
</pre></div>
<p>You should now have a spinning triangle! The completed JavaScript is available
<a href="tutorial_triangle.js">here</a>.</p>
<p>In the <a href="tutorial_redball.html">next tutorial</a>, we&#x27;ll take a closer look at Filament materials and 3D rendering.</p>
<p>In the <a href="tutorial_redball.html">next tutorial</a>, we'll take a closer look at Filament materials and 3D rendering.</p>
</body>
</html>
+2 -3
View File
@@ -17,7 +17,6 @@ set(PUBLIC_HDRS
include/filament/Camera.h
include/filament/Color.h
include/filament/ColorGrading.h
include/filament/ColorSpace.h
include/filament/DebugRegistry.h
include/filament/Engine.h
include/filament/Exposure.h
@@ -54,7 +53,7 @@ set(SRCS
src/BufferObject.cpp
src/Camera.cpp
src/Color.cpp
src/ColorSpaceUtils.cpp
src/ColorSpace.cpp
src/Culler.cpp
src/DFG.cpp
src/DebugRegistry.cpp
@@ -136,7 +135,7 @@ set(SRCS
set(PRIVATE_HDRS
src/Allocators.h
src/BufferPoolAllocator.h
src/ColorSpaceUtils.h
src/ColorSpace.h
src/Culler.h
src/DFG.h
src/FilamentAPI-impl.h
-1
View File
@@ -405,7 +405,6 @@ if (APPLE)
backend
getopt
gtest
filamat
SPIRV
spirv-cross-glsl)
+2 -36
View File
@@ -398,18 +398,6 @@ enum class CompressedPixelDataType : uint16_t {
SRGB8_ALPHA8_ASTC_10x10,
SRGB8_ALPHA8_ASTC_12x10,
SRGB8_ALPHA8_ASTC_12x12,
// RGTC formats available with a GLES extension
RED_RGTC1, // BC4 unsigned
SIGNED_RED_RGTC1, // BC4 signed
RED_GREEN_RGTC2, // BC5 unsigned
SIGNED_RED_GREEN_RGTC2, // BC5 signed
// BPTC formats available with a GLES extension
RGB_BPTC_SIGNED_FLOAT, // BC6H signed
RGB_BPTC_UNSIGNED_FLOAT,// BC6H unsigned
RGBA_BPTC_UNORM, // BC7
SRGB_ALPHA_BPTC_UNORM, // BC7 sRGB
};
/** Supported texel formats
@@ -564,18 +552,6 @@ enum class TextureFormat : uint16_t {
SRGB8_ALPHA8_ASTC_10x10,
SRGB8_ALPHA8_ASTC_12x10,
SRGB8_ALPHA8_ASTC_12x12,
// RGTC formats available with a GLES extension
RED_RGTC1, // BC4 unsigned
SIGNED_RED_RGTC1, // BC4 signed
RED_GREEN_RGTC2, // BC5 unsigned
SIGNED_RED_GREEN_RGTC2, // BC5 signed
// BPTC formats available with a GLES extension
RGB_BPTC_SIGNED_FLOAT, // BC6H signed
RGB_BPTC_UNSIGNED_FLOAT,// BC6H unsigned
RGBA_BPTC_UNORM, // BC7
SRGB_ALPHA_BPTC_UNORM, // BC7 sRGB
};
//! Bitmask describing the intended Texture Usage
@@ -656,7 +632,7 @@ static constexpr bool isSignedIntFormat(TextureFormat format) {
}
}
//! returns whether this format is a compressed format
//! returns whether this format a compressed format
static constexpr bool isCompressedFormat(TextureFormat format) noexcept {
return format >= TextureFormat::EAC_R11;
}
@@ -666,7 +642,7 @@ static constexpr bool isETC2Compression(TextureFormat format) noexcept {
return format >= TextureFormat::EAC_R11 && format <= TextureFormat::ETC2_EAC_SRGBA8;
}
//! returns whether this format is an S3TC compressed format
//! returns whether this format is an ETC3 compressed format
static constexpr bool isS3TCCompression(TextureFormat format) noexcept {
return format >= TextureFormat::DXT1_RGB && format <= TextureFormat::DXT5_SRGBA;
}
@@ -675,16 +651,6 @@ static constexpr bool isS3TCSRGBCompression(TextureFormat format) noexcept {
return format >= TextureFormat::DXT1_SRGB && format <= TextureFormat::DXT5_SRGBA;
}
//! returns whether this format is an RGTC compressed format
static constexpr bool isRGTCCompression(TextureFormat format) noexcept {
return format >= TextureFormat::RED_RGTC1 && format <= TextureFormat::SIGNED_RED_GREEN_RGTC2;
}
//! returns whether this format is an BPTC compressed format
static constexpr bool isBPTCCompression(TextureFormat format) noexcept {
return format >= TextureFormat::RGB_BPTC_SIGNED_FLOAT && format <= TextureFormat::SRGB_ALPHA_BPTC_UNORM;
}
static constexpr bool isASTCCompression(TextureFormat format) noexcept {
return format >= TextureFormat::RGBA_ASTC_4x4 && format <= TextureFormat::SRGB8_ALPHA8_ASTC_12x12;
}
+2 -37
View File
@@ -29,7 +29,7 @@ bool requestsGoogleLineDirectivesExtension(std::string_view source) noexcept {
}
void removeGoogleLineDirectives(char* shader, size_t length) noexcept {
std::string_view const s{ shader, length };
std::string_view s(shader, length);
size_t pos = 0;
while (true) {
@@ -173,20 +173,6 @@ size_t getFormatSize(TextureFormat format) noexcept {
case TextureFormat::DXT5_SRGBA:
return 16;
case TextureFormat::RED_RGTC1:
case TextureFormat::SIGNED_RED_RGTC1:
return 8;
case TextureFormat::RED_GREEN_RGTC2:
case TextureFormat::SIGNED_RED_GREEN_RGTC2:
return 16;
case TextureFormat::RGB_BPTC_SIGNED_FLOAT:
case TextureFormat::RGB_BPTC_UNSIGNED_FLOAT:
case TextureFormat::RGBA_BPTC_UNORM:
case TextureFormat::SRGB_ALPHA_BPTC_UNORM:
return 16;
// The block size for ASTC compression is always 16 bytes.
case TextureFormat::RGBA_ASTC_4x4:
case TextureFormat::RGBA_ASTC_5x4:
@@ -290,22 +276,16 @@ size_t getFormatComponentCount(TextureFormat format) noexcept {
// Compressed formats ---------------------------------------------------------------------
case TextureFormat::EAC_R11:
case TextureFormat::EAC_R11_SIGNED:
case TextureFormat::RED_RGTC1:
case TextureFormat::SIGNED_RED_RGTC1:
return 1;
case TextureFormat::EAC_RG11:
case TextureFormat::EAC_RG11_SIGNED:
case TextureFormat::RED_GREEN_RGTC2:
case TextureFormat::SIGNED_RED_GREEN_RGTC2:
return 2;
case TextureFormat::ETC2_RGB8:
case TextureFormat::ETC2_SRGB8:
case TextureFormat::DXT1_RGB:
case TextureFormat::DXT1_SRGB:
case TextureFormat::RGB_BPTC_SIGNED_FLOAT:
case TextureFormat::RGB_BPTC_UNSIGNED_FLOAT:
return 3;
case TextureFormat::ETC2_EAC_RGBA8:
@@ -318,8 +298,6 @@ size_t getFormatComponentCount(TextureFormat format) noexcept {
case TextureFormat::DXT3_SRGBA:
case TextureFormat::DXT5_RGBA:
case TextureFormat::DXT5_SRGBA:
case TextureFormat::RGBA_BPTC_UNORM:
case TextureFormat::SRGB_ALPHA_BPTC_UNORM:
case TextureFormat::RGBA_ASTC_4x4:
case TextureFormat::RGBA_ASTC_5x4:
case TextureFormat::RGBA_ASTC_5x5:
@@ -378,18 +356,6 @@ size_t getBlockWidth(TextureFormat format) noexcept {
case TextureFormat::DXT5_SRGBA:
return 4;
case TextureFormat::RED_RGTC1:
case TextureFormat::SIGNED_RED_RGTC1:
case TextureFormat::RED_GREEN_RGTC2:
case TextureFormat::SIGNED_RED_GREEN_RGTC2:
return 4;
case TextureFormat::RGB_BPTC_SIGNED_FLOAT:
case TextureFormat::RGB_BPTC_UNSIGNED_FLOAT:
case TextureFormat::RGBA_BPTC_UNORM:
case TextureFormat::SRGB_ALPHA_BPTC_UNORM:
return 4;
case TextureFormat::RGBA_ASTC_4x4:
case TextureFormat::SRGB8_ALPHA8_ASTC_4x4:
return 4;
@@ -489,8 +455,7 @@ bool reshape(const PixelBufferDescriptor& data, PixelBufferDescriptor& reshaped)
return false;
}
const auto freeFunc = [](void* buffer,
UTILS_UNUSED size_t size, UTILS_UNUSED void* user) { free(buffer); };
const auto freeFunc = [](void* buffer, size_t size, void* user) { free(buffer); };
const size_t reshapedSize = 4 * data.size / 3;
const PixelDataFormat reshapedFormat =
data.format == PixelDataFormat::RGB ? PixelDataFormat::RGBA : PixelDataFormat::RGBA_INTEGER;
+4 -2
View File
@@ -19,7 +19,8 @@
#include <utils/Panic.h>
#include <utils/debug.h>
namespace filament::backend {
namespace filament {
namespace backend {
PresentCallable::PresentCallable(PresentFn fn, void* user) noexcept
: mPresentFn(fn), mUser(user) {
@@ -34,4 +35,5 @@ void PresentCallable::operator()(bool presentFrame) noexcept {
mPresentFn = nullptr;
}
} // namespace filament::backend
} // namespace backend
} // namespace filament
+1 -1
View File
@@ -62,7 +62,7 @@ void* CircularBuffer::alloc(size_t size) noexcept {
void* vaddr = MAP_FAILED;
void* vaddr_shadow = MAP_FAILED;
void* vaddr_guard = MAP_FAILED;
int const fd = ashmem_create_region("filament::CircularBuffer", size + BLOCK_SIZE);
int fd = ashmem_create_region("filament::CircularBuffer", size + BLOCK_SIZE);
if (fd >= 0) {
// reserve/find enough address space
void* reserve_vaddr = mmap(nullptr, size * 2 + BLOCK_SIZE,
+8 -6
View File
@@ -26,7 +26,8 @@
using namespace utils;
namespace filament::backend {
namespace filament {
namespace backend {
CommandBufferQueue::CommandBufferQueue(size_t requiredSize, size_t bufferSize)
: mRequiredSize((requiredSize + CircularBuffer::BLOCK_MASK) & ~CircularBuffer::BLOCK_MASK),
@@ -40,13 +41,13 @@ CommandBufferQueue::~CommandBufferQueue() {
}
void CommandBufferQueue::requestExit() {
std::lock_guard<utils::Mutex> const lock(mLock);
std::lock_guard<utils::Mutex> lock(mLock);
mExitRequested = EXIT_REQUESTED;
mCondition.notify_one();
}
bool CommandBufferQueue::isExitRequested() const {
std::lock_guard<utils::Mutex> const lock(mLock);
std::lock_guard<utils::Mutex> lock(mLock);
ASSERT_PRECONDITION( mExitRequested == 0 || mExitRequested == EXIT_REQUESTED,
"mExitRequested is corrupted (value = 0x%08x)!", mExitRequested);
return (bool)mExitRequested;
@@ -72,7 +73,7 @@ void CommandBufferQueue::flush() noexcept {
void* const tail = circularBuffer.getTail();
// size of this slice
uint32_t const used = uint32_t(intptr_t(head) - intptr_t(tail));
uint32_t used = uint32_t(intptr_t(head) - intptr_t(tail));
circularBuffer.circularize();
@@ -124,9 +125,10 @@ std::vector<CommandBufferQueue::Slice> CommandBufferQueue::waitForCommands() con
}
void CommandBufferQueue::releaseBuffer(CommandBufferQueue::Slice const& buffer) {
std::lock_guard<utils::Mutex> const lock(mLock);
std::lock_guard<utils::Mutex> lock(mLock);
mFreeSpace += uintptr_t(buffer.end) - uintptr_t(buffer.begin);
mCondition.notify_one();
}
} // namespace filament::backend
} // namespace backend
} // namespace filament
+2 -5
View File
@@ -16,10 +16,7 @@
#include "private/backend/CommandStream.h"
#if DEBUG_COMMAND_STREAM
#include <utils/CallStack.h>
#endif
#include <utils/Log.h>
#include <utils/Profiler.h>
#include <utils/Systrace.h>
@@ -37,7 +34,7 @@ namespace filament::backend {
// ------------------------------------------------------------------------------------------------
// A few utility functions for debugging...
inline void printParameterPack(io::ostream&) { }
inline void printParameterPack(io::ostream& out) { }
template<typename LAST>
static void printParameterPack(io::ostream& out, const LAST& t) { out << t; }
@@ -98,7 +95,7 @@ void CommandStream::execute(void* buffer) {
if (UTILS_UNLIKELY(mUsePerformanceCounter)) {
// we want to remove all this when tracing is completely disabled
profiler.stop();
UTILS_UNUSED Profiler::Counters const counters = profiler.readCounters();
UTILS_UNUSED Profiler::Counters counters = profiler.readCounters();
SYSTRACE_VALUE32("GLThread (I)", counters.getInstructions());
SYSTRACE_VALUE32("GLThread (C)", counters.getCpuCycles());
SYSTRACE_VALUE32("GLThread (CPI x10)", counters.getCPI() * 10);
@@ -96,8 +96,6 @@ struct MetalContext {
SamplerStateCache samplerStateCache;
ArgumentEncoderCache argumentEncoderCache;
PolygonOffset currentPolygonOffset = {0.0f, 0.0f};
MetalSamplerGroup* samplerBindings[Program::SAMPLER_BINDING_COUNT] = {};
// Keeps track of sampler groups we've finalized for the current render pass.
+1 -8
View File
@@ -1005,7 +1005,6 @@ void MetalDriver::beginRenderPass(Handle<HwRenderTarget> rth,
mContext->depthStencilState.invalidate();
mContext->cullModeState.invalidate();
mContext->windingState.invalidate();
mContext->currentPolygonOffset = {0.0f, 0.0f};
mContext->finalizedSamplerGroups.clear();
}
@@ -1184,12 +1183,8 @@ void MetalDriver::startCapture(int) {
if (@available(iOS 13, *)) {
MTLCaptureDescriptor* descriptor = [MTLCaptureDescriptor new];
descriptor.captureObject = mContext->device;
#if defined(IOS)
descriptor.destination = MTLCaptureDestinationDeveloperTools;
#else
descriptor.destination = MTLCaptureDestinationGPUTraceDocument;
descriptor.outputURL = [[NSURL alloc] initFileURLWithPath:@"filament.gputrace"];
#endif
NSError* error = nil;
[[MTLCaptureManager sharedCaptureManager] startCaptureWithDescriptor:descriptor
error:&error];
@@ -1591,12 +1586,10 @@ void MetalDriver::draw(PipelineState ps, Handle<HwRenderPrimitive> rph, uint32_t
[mContext->currentRenderPassEncoder setDepthStencilState:state];
}
if (ps.polygonOffset.constant != mContext->currentPolygonOffset.constant ||
ps.polygonOffset.slope != mContext->currentPolygonOffset.slope) {
if (ps.polygonOffset.constant != 0.0 || ps.polygonOffset.slope != 0.0) {
[mContext->currentRenderPassEncoder setDepthBias:ps.polygonOffset.constant
slopeScale:ps.polygonOffset.slope
clamp:0.0];
mContext->currentPolygonOffset = ps.polygonOffset;
}
// Set scissor-rectangle.
-10
View File
@@ -198,16 +198,6 @@ MTLPixelFormat getMetalFormat(MetalContext* context, TextureFormat format) noexc
case TextureFormat::DXT1_RGB: return MTLPixelFormatInvalid;
case TextureFormat::DXT1_SRGB: return MTLPixelFormatInvalid;
case TextureFormat::RED_RGTC1: return MTLPixelFormatBC4_RUnorm;
case TextureFormat::SIGNED_RED_RGTC1: return MTLPixelFormatBC4_RSnorm;
case TextureFormat::RED_GREEN_RGTC2: return MTLPixelFormatBC5_RGUnorm;
case TextureFormat::SIGNED_RED_GREEN_RGTC2: return MTLPixelFormatBC5_RGSnorm;
case TextureFormat::RGB_BPTC_SIGNED_FLOAT: return MTLPixelFormatBC6H_RGBFloat;
case TextureFormat::RGB_BPTC_UNSIGNED_FLOAT: return MTLPixelFormatBC6H_RGBUfloat;
case TextureFormat::RGBA_BPTC_UNORM: return MTLPixelFormatBC7_RGBAUnorm;
case TextureFormat::SRGB_ALPHA_BPTC_UNORM: return MTLPixelFormatBC7_RGBAUnorm_sRGB;
default: break;
}
}
+4 -4
View File
@@ -57,7 +57,7 @@ const char* getGLError(GLenum error) noexcept {
UTILS_NOINLINE
GLenum checkGLError(io::ostream& out, const char* function, size_t line) noexcept {
GLenum const error = glGetError();
GLenum error = glGetError();
if (error != GL_NO_ERROR) {
const char* string = getGLError(error);
out << "OpenGL error " << io::hex << error << " (" << string << ") in \""
@@ -68,7 +68,7 @@ GLenum checkGLError(io::ostream& out, const char* function, size_t line) noexcep
UTILS_NOINLINE
void assertGLError(io::ostream& out, const char* function, size_t line) noexcept {
GLenum const err = checkGLError(out, function, line);
GLenum err = checkGLError(out, function, line);
if (err != GL_NO_ERROR) {
debug_trap();
}
@@ -104,7 +104,7 @@ const char* getFramebufferStatus(GLenum status) noexcept {
UTILS_NOINLINE
GLenum checkFramebufferStatus(io::ostream& out, GLenum target, const char* function, size_t line) noexcept {
GLenum const status = glCheckFramebufferStatus(target);
GLenum status = glCheckFramebufferStatus(target);
if (status != GL_FRAMEBUFFER_COMPLETE) {
const char* string = getFramebufferStatus(status);
out << "OpenGL framebuffer error " << io::hex << status << " (" << string << ") in \""
@@ -115,7 +115,7 @@ GLenum checkFramebufferStatus(io::ostream& out, GLenum target, const char* funct
UTILS_NOINLINE
void assertFramebufferStatus(io::ostream& out, GLenum target, const char* function, size_t line) noexcept {
GLenum const status = checkFramebufferStatus(out, target, function, line);
GLenum status = checkFramebufferStatus(out, target, function, line);
if (status != GL_FRAMEBUFFER_COMPLETE) {
debug_trap();
}
-28
View File
@@ -472,34 +472,6 @@ constexpr /* inline */ GLenum getInternalFormat(TextureFormat format) noexcept {
return 0;
#endif
#if defined(GL_EXT_texture_compression_rgtc)
case TextureFormat::RED_RGTC1: return GL_COMPRESSED_RED_RGTC1_EXT;
case TextureFormat::SIGNED_RED_RGTC1: return GL_COMPRESSED_SIGNED_RED_RGTC1_EXT;
case TextureFormat::RED_GREEN_RGTC2: return GL_COMPRESSED_RED_GREEN_RGTC2_EXT;
case TextureFormat::SIGNED_RED_GREEN_RGTC2: return GL_COMPRESSED_SIGNED_RED_GREEN_RGTC2_EXT;
#else
case TextureFormat::RED_RGTC1:
case TextureFormat::SIGNED_RED_RGTC1:
case TextureFormat::RED_GREEN_RGTC2:
case TextureFormat::SIGNED_RED_GREEN_RGTC2:
// this should not happen
return 0;
#endif
#if defined(GL_EXT_texture_compression_bptc)
case TextureFormat::RGB_BPTC_SIGNED_FLOAT: return GL_COMPRESSED_RGB_BPTC_SIGNED_FLOAT_EXT;
case TextureFormat::RGB_BPTC_UNSIGNED_FLOAT: return GL_COMPRESSED_RGB_BPTC_UNSIGNED_FLOAT_EXT;
case TextureFormat::RGBA_BPTC_UNORM: return GL_COMPRESSED_RGBA_BPTC_UNORM_EXT;
case TextureFormat::SRGB_ALPHA_BPTC_UNORM: return GL_COMPRESSED_SRGB_ALPHA_BPTC_UNORM_EXT;
#else
case TextureFormat::RGB_BPTC_SIGNED_FLOAT:
case TextureFormat::RGB_BPTC_UNSIGNED_FLOAT:
case TextureFormat::RGBA_BPTC_UNORM:
case TextureFormat::SRGB_ALPHA_BPTC_UNORM:
// this should not happen
return 0;
#endif
#if defined(GL_KHR_texture_compression_astc_hdr)
case TextureFormat::RGBA_ASTC_4x4: return GL_COMPRESSED_RGBA_ASTC_4x4_KHR;
case TextureFormat::RGBA_ASTC_5x4: return GL_COMPRESSED_RGBA_ASTC_5x4_KHR;
+23 -21
View File
@@ -27,7 +27,7 @@ bool OpenGLContext::queryOpenGLVersion(GLint* major, GLint* minor) noexcept {
if constexpr (BACKEND_OPENGL_VERSION == BACKEND_OPENGL_VERSION_GLES) {
char const* version = (char const*)glGetString(GL_VERSION);
// This works on all versions of GLES
int const n = version ? sscanf(version, "OpenGL ES %d.%d", major, minor) : 0;
int n = version ? sscanf(version, "OpenGL ES %d.%d", major, minor) : 0;
return n == 2;
} else if constexpr (BACKEND_OPENGL_VERSION == BACKEND_OPENGL_VERSION_GL) {
// OpenGL version
@@ -142,7 +142,7 @@ OpenGLContext::OpenGLContext() noexcept {
bugs.enable_initialize_non_used_uniform_array = true;
int maj, min, driverMajor, driverMinor;
int const c = sscanf(state.version, "OpenGL ES %d.%d V@%d.%d", // NOLINT(cert-err34-c)
int c = sscanf(state.version, "OpenGL ES %d.%d V@%d.%d", // NOLINT(cert-err34-c)
&maj, &min, &driverMajor, &driverMinor);
if (c == 4) {
// Workarounds based on version here.
@@ -176,7 +176,7 @@ OpenGLContext::OpenGLContext() noexcept {
bugs.dont_use_timer_query = true;
int maj, min, driverVersion, driverRevision, driverPatch;
int const c = sscanf(state.version, "OpenGL ES %d.%d v%d.r%dp%d", // NOLINT(cert-err34-c)
int c = sscanf(state.version, "OpenGL ES %d.%d v%d.r%dp%d", // NOLINT(cert-err34-c)
&maj, &min, &driverVersion, &driverRevision, &driverPatch);
if (c == 5) {
// Workarounds based on version here.
@@ -218,7 +218,7 @@ OpenGLContext::OpenGLContext() noexcept {
if (strstr(state.renderer, "Adreno")) {
// Qualcomm GPU
// early exit condition is flattened in EASU code
// (that should be regardless of ANGLE, but we should double-check)
// (that should be regardless of ANGLE, but we should double check)
bugs.split_easu = true;
}
// TODO: see if we could use `bugs.allow_read_only_ancillary_feedback_loop = true`
@@ -270,8 +270,8 @@ OpenGLContext::OpenGLContext() noexcept {
#if !defined(NDEBUG) && defined(GL_KHR_debug)
if (ext.KHR_debug) {
auto cb = +[](GLenum, GLenum type, GLuint, GLenum severity, GLsizei length,
const GLchar* message, const void *) {
auto cb = [](GLenum source, GLenum type, GLuint id, GLenum severity, GLsizei length,
const GLchar* message, const void *userParam) {
io::ostream* stream = &slog.i;
switch (severity) {
case GL_DEBUG_SEVERITY_HIGH: stream = &slog.e; break;
@@ -292,7 +292,7 @@ OpenGLContext::OpenGLContext() noexcept {
case GL_DEBUG_TYPE_MARKER: level = "MARKER: "; break;
default: break;
}
out << "KHR_debug " << level << std::string_view{ message, size_t(length) } << io::endl;
out << "KHR_debug " << level << message << io::endl;
};
glEnable(GL_DEBUG_OUTPUT);
glEnable(GL_DEBUG_OUTPUT_SYNCHRONOUS);
@@ -304,7 +304,7 @@ OpenGLContext::OpenGLContext() noexcept {
void OpenGLContext::setDefaultState() noexcept {
// We need to make sure our internal state matches the GL state when we start.
// (some of these calls may be unneeded as they might be the gl defaults)
GLenum const caps[] = {
GLenum caps[] = {
GL_BLEND,
GL_CULL_FACE,
GL_SCISSOR_TEST,
@@ -315,14 +315,16 @@ void OpenGLContext::setDefaultState() noexcept {
GL_SAMPLE_COVERAGE,
GL_POLYGON_OFFSET_FILL,
};
size_t capsCount = sizeof(caps) / sizeof(caps[0]);
UTILS_NOUNROLL
for (auto const capi : caps) {
size_t const capIndex = getIndexForCap(capi);
for (GLenum capi = 0; capi < capsCount; ++capi) {
size_t capIndex = getIndexForCap(caps[capi]);
GLenum cap = caps[capi];
if (state.enables.caps[capIndex]) {
glEnable(capi);
glEnable(cap);
} else {
glDisable(capi);
glDisable(cap);
}
}
@@ -351,7 +353,7 @@ void OpenGLContext::setDefaultState() noexcept {
void OpenGLContext::initExtensionsGLES() noexcept {
const char * const extensions = (const char*)glGetString(GL_EXTENSIONS);
GLUtils::unordered_string_set const exts = GLUtils::split(extensions);
GLUtils::unordered_string_set exts = GLUtils::split(extensions);
if constexpr (DEBUG_PRINT_EXTENSIONS) {
for (auto extension: exts) {
slog.d << "\"" << std::string_view(extension) << "\"\n";
@@ -375,8 +377,6 @@ void OpenGLContext::initExtensionsGLES() noexcept {
#endif
ext.EXT_texture_compression_s3tc = exts.has("GL_EXT_texture_compression_s3tc"sv);
ext.EXT_texture_compression_s3tc_srgb = exts.has("GL_EXT_texture_compression_s3tc_srgb"sv);
ext.EXT_texture_compression_rgtc = exts.has("GL_EXT_texture_compression_rgtc"sv);
ext.EXT_texture_compression_bptc = exts.has("GL_EXT_texture_compression_bptc"sv);
ext.EXT_texture_cube_map_array = exts.has("GL_EXT_texture_cube_map_array"sv) || exts.has("GL_OES_texture_cube_map_array"sv);
ext.GOOGLE_cpp_style_line_directive = exts.has("GL_GOOGLE_cpp_style_line_directive"sv);
ext.KHR_debug = exts.has("GL_KHR_debug"sv);
@@ -420,8 +420,6 @@ void OpenGLContext::initExtensionsGL() noexcept {
ext.EXT_texture_compression_etc2 = exts.has("GL_ARB_ES3_compatibility"sv);
ext.EXT_texture_compression_s3tc = exts.has("GL_EXT_texture_compression_s3tc"sv);
ext.EXT_texture_compression_s3tc_srgb = exts.has("GL_EXT_texture_compression_s3tc_srgb"sv);
ext.EXT_texture_compression_rgtc = exts.has("GL_EXT_texture_compression_rgtc"sv);
ext.EXT_texture_compression_bptc = exts.has("GL_EXT_texture_compression_bptc"sv);
ext.EXT_texture_filter_anisotropic = exts.has("GL_EXT_texture_filter_anisotropic"sv);
ext.EXT_texture_sRGB = exts.has("GL_EXT_texture_sRGB"sv);
ext.GOOGLE_cpp_style_line_directive = exts.has("GL_GOOGLE_cpp_style_line_directive"sv);
@@ -446,7 +444,7 @@ void OpenGLContext::bindBuffer(GLenum target, GLuint buffer) noexcept {
glBindBuffer(target, buffer);
}
} else {
size_t const targetIndex = getIndexForBufferTarget(target);
size_t targetIndex = getIndexForBufferTarget(target);
update_state(state.buffers.genericBinding[targetIndex], buffer, [&]() {
glBindBuffer(target, buffer);
});
@@ -613,7 +611,7 @@ void OpenGLContext::resetState() noexcept {
// Reset state.buffers to its default state to avoid the complexity and error-prone
// nature of resetting the GL state to its existing state
state.buffers = {};
GLenum const bufferTargets[] = {
GLenum bufferTargets[] = {
GL_UNIFORM_BUFFER,
GL_TRANSFORM_FEEDBACK_BUFFER,
#if !defined(__EMSCRIPTEN__)
@@ -626,7 +624,9 @@ void OpenGLContext::resetState() noexcept {
GL_PIXEL_PACK_BUFFER,
GL_PIXEL_UNPACK_BUFFER,
};
for (auto const target : bufferTargets) {
size_t bufferTargetCount = sizeof(bufferTargets) / sizeof(bufferTargets[0]);
for (size_t targetArrayIndex = 0; targetArrayIndex < bufferTargetCount; ++targetArrayIndex) {
GLenum target = bufferTargets[targetArrayIndex];
glBindBuffer(target, 0);
}
@@ -655,11 +655,13 @@ void OpenGLContext::resetState() noexcept {
GL_TEXTURE_CUBE_MAP_ARRAY,
#endif
};
const size_t textureTargetCount = sizeof(textureTargets) / sizeof(textureTargets[0]);
for (GLint unit = 0; unit < gets.max_combined_texture_image_units; ++unit) {
glActiveTexture(GL_TEXTURE0 + unit);
glBindSampler(unit, 0);
for (auto const target : textureTargets) {
for (size_t textureTargetArrayIndex = 0; textureTargetArrayIndex < textureTargetCount; ++textureTargetArrayIndex) {
GLuint target = textureTargets[textureTargetArrayIndex];
glBindTexture(target, 0);
}
}
+42 -44
View File
@@ -137,93 +137,91 @@ public:
// features supported by this version of GL or GLES
struct {
bool multisample_texture;
} features = {};
bool multisample_texture = false;
} features;
// supported extensions detected at runtime
struct {
bool APPLE_color_buffer_packed_float;
bool ARB_shading_language_packing;
bool EXT_clip_control;
bool EXT_color_buffer_float;
bool EXT_color_buffer_half_float;
bool EXT_debug_marker;
bool EXT_disjoint_timer_query;
bool EXT_multisampled_render_to_texture;
bool EXT_multisampled_render_to_texture2;
bool EXT_shader_framebuffer_fetch;
bool KHR_texture_compression_astc_hdr;
bool KHR_texture_compression_astc_ldr;
bool EXT_texture_compression_etc2;
bool EXT_texture_compression_s3tc;
bool EXT_texture_compression_s3tc_srgb;
bool EXT_texture_compression_rgtc;
bool EXT_texture_compression_bptc;
bool EXT_texture_cube_map_array;
bool EXT_texture_filter_anisotropic;
bool EXT_texture_sRGB;
bool GOOGLE_cpp_style_line_directive;
bool KHR_debug;
bool OES_EGL_image_external_essl3;
bool QCOM_tiled_rendering;
bool WEBGL_compressed_texture_etc;
bool WEBGL_compressed_texture_s3tc;
bool WEBGL_compressed_texture_s3tc_srgb;
} ext = {};
bool APPLE_color_buffer_packed_float = false;
bool ARB_shading_language_packing = false;
bool EXT_clip_control = false;
bool EXT_color_buffer_float = false;
bool EXT_color_buffer_half_float = false;
bool EXT_debug_marker = false;
bool EXT_disjoint_timer_query = false;
bool EXT_multisampled_render_to_texture = false;
bool EXT_multisampled_render_to_texture2 = false;
bool EXT_shader_framebuffer_fetch = false;
bool KHR_texture_compression_astc_hdr = false;
bool KHR_texture_compression_astc_ldr = false;
bool EXT_texture_compression_etc2 = false;
bool EXT_texture_compression_s3tc = false;
bool EXT_texture_compression_s3tc_srgb = false;
bool EXT_texture_cube_map_array = false;
bool EXT_texture_filter_anisotropic = false;
bool EXT_texture_sRGB = false;
bool GOOGLE_cpp_style_line_directive = false;
bool KHR_debug = false;
bool OES_EGL_image_external_essl3 = false;
bool QCOM_tiled_rendering = false;
bool WEBGL_compressed_texture_etc = false;
bool WEBGL_compressed_texture_s3tc = false;
bool WEBGL_compressed_texture_s3tc_srgb = false;
} ext;
struct {
// Some drivers have issues with UBOs in the fragment shader when
// glFlush() is called between draw calls.
bool disable_glFlush;
bool disable_glFlush = false;
// Some drivers seem to not store the GL_ELEMENT_ARRAY_BUFFER binding
// in the VAO state.
bool vao_doesnt_store_element_array_buffer_binding;
bool vao_doesnt_store_element_array_buffer_binding = false;
// Some drivers have gl state issues when drawing from shared contexts
bool disable_shared_context_draws;
bool disable_shared_context_draws = false;
// Some drivers require the GL_TEXTURE_EXTERNAL_OES target to be bound when
// the texture image changes, even if it's already bound to that texture
bool texture_external_needs_rebind;
bool texture_external_needs_rebind = false;
// Some web browsers seem to immediately clear the default framebuffer when calling
// glInvalidateFramebuffer with WebGL 2.0
bool disable_invalidate_framebuffer;
bool disable_invalidate_framebuffer = false;
// Some drivers declare GL_EXT_texture_filter_anisotropic but don't support
// calling glSamplerParameter() with GL_TEXTURE_MAX_ANISOTROPY_EXT
bool texture_filter_anisotropic_broken_on_sampler;
bool texture_filter_anisotropic_broken_on_sampler = false;
// Some drivers have issues when reading from a mip while writing to a different mip.
// In the OpenGL ES 3.0 specification this is covered in section 4.4.3,
// "Feedback Loops Between Textures and the Framebuffer".
bool disable_feedback_loops;
bool disable_feedback_loops = false;
// Some drivers don't implement timer queries correctly
bool dont_use_timer_query;
bool dont_use_timer_query = false;
// Some drivers can't blit from a sidecar renderbuffer into a layer of a texture array.
// This technique is used for VSM with MSAA turned on.
bool disable_sidecar_blit_into_texture_array;
bool disable_sidecar_blit_into_texture_array = false;
// Some drivers incorrectly flatten the early exit condition in the EASU code, in which
// case we need an alternative algorithm
bool split_easu;
bool split_easu = false;
// As of Android R some qualcomm drivers invalidate buffers for the whole render pass
// even if glInvalidateFramebuffer() is called at the end of it.
bool invalidate_end_only_if_invalidate_start;
bool invalidate_end_only_if_invalidate_start = false;
// GLES doesn't allow feedback loops even if writes are disabled. So take we the point of
// view that this is generally forbidden. However, this restriction is lifted on desktop
// GL and Vulkan and probably Metal.
bool allow_read_only_ancillary_feedback_loop;
bool allow_read_only_ancillary_feedback_loop = false;
// Some Adreno drivers crash in glDrawXXX() when there's an uninitialized uniform block,
// even when the shader doesn't access it.
bool enable_initialize_non_used_uniform_array;
} bugs = {};
bool enable_initialize_non_used_uniform_array = false;
} bugs;
// state getters -- as needed.
vec4gli const& getViewport() const { return state.window.viewport; }
+97 -109
View File
@@ -78,8 +78,8 @@ using namespace GLUtils;
// ------------------------------------------------------------------------------------------------
UTILS_NOINLINE
Driver* OpenGLDriver::create(OpenGLPlatform* const platform,
void* const sharedGLContext, const Platform::DriverConfig& driverConfig) noexcept {
Driver* OpenGLDriver::create(
OpenGLPlatform* const platform, void* const sharedGLContext, const Platform::DriverConfig& driverConfig) noexcept {
assert_invariant(platform);
OpenGLPlatform* const ec = platform;
@@ -120,7 +120,7 @@ Driver* OpenGLDriver::create(OpenGLPlatform* const platform,
// here we check we're on a supported version of GL before initializing the driver
GLint major = 0, minor = 0;
bool const success = OpenGLContext::queryOpenGLVersion(&major, &minor);
bool success = OpenGLContext::queryOpenGLVersion(&major, &minor);
if (UTILS_UNLIKELY(!success)) {
PANIC_LOG("Can't get OpenGL version");
@@ -142,9 +142,8 @@ Driver* OpenGLDriver::create(OpenGLPlatform* const platform,
}
}
size_t const defaultSize = FILAMENT_OPENGL_HANDLE_ARENA_SIZE_IN_MB * 1024U * 1024U;
Platform::DriverConfig const validConfig {
.handleArenaSize = std::max(driverConfig.handleArenaSize, defaultSize) };
size_t defaultSize = FILAMENT_OPENGL_HANDLE_ARENA_SIZE_IN_MB * 1024U * 1024U;
Platform::DriverConfig validConfig { .handleArenaSize = std::max(driverConfig.handleArenaSize, defaultSize) };
OpenGLDriver* const driver = new OpenGLDriver(ec, validConfig);
return driver;
}
@@ -170,8 +169,7 @@ OpenGLDriver::OpenGLDriver(OpenGLPlatform* platform, const Platform::DriverConfi
std::fill(mSamplerBindings.begin(), mSamplerBindings.end(), nullptr);
// set a reasonable default value for our stream array
mTexturesWithStreamsAttached.reserve(8);
mStreamsWithPendingAquiredImage.reserve(8);
mExternalStreams.reserve(8);
#ifndef NDEBUG
slog.i << "OS version: " << mPlatform.getOSVersion() << io::endl;
@@ -437,10 +435,10 @@ void OpenGLDriver::createIndexBufferR(
DEBUG_MARKER()
auto& gl = mContext;
uint8_t const elementSize = static_cast<uint8_t>(getElementTypeSize(elementType));
uint8_t elementSize = static_cast<uint8_t>(getElementTypeSize(elementType));
GLIndexBuffer* ib = construct<GLIndexBuffer>(ibh, elementSize, indexCount);
glGenBuffers(1, &ib->gl.buffer);
GLsizeiptr const size = elementSize * indexCount;
GLsizeiptr size = elementSize * indexCount;
gl.bindVertexArray(nullptr);
gl.bindBuffer(GL_ELEMENT_ARRAY_BUFFER, ib->gl.buffer);
glBufferData(GL_ELEMENT_ARRAY_BUFFER, size, nullptr, getBufferUsage(usage));
@@ -1040,8 +1038,7 @@ void OpenGLDriver::renderBufferStorage(GLuint rbo, GLenum internalformat, uint32
auto& gl = mContext;
if (gl.ext.EXT_multisampled_render_to_texture ||
gl.ext.EXT_multisampled_render_to_texture2) {
glext::glRenderbufferStorageMultisampleEXT(GL_RENDERBUFFER,
samples, internalformat, width, height);
glext::glRenderbufferStorageMultisampleEXT(GL_RENDERBUFFER, samples, internalformat, width, height);
} else
#endif
{
@@ -1208,11 +1205,11 @@ void OpenGLDriver::createSyncR(Handle<HwSync> fh, int) {
CHECK_GL_ERROR(utils::slog.e)
// check the status of the sync once a frame, since we must do this from our thread
std::weak_ptr<GLSync::State> const weak = f->result;
std::weak_ptr<GLSync::State> weak = f->result;
runEveryNowAndThen([sync = f->gl.sync, weak]() -> bool {
auto result = weak.lock();
if (result) {
GLenum const status = glClientWaitSync(sync, 0, 0u);
GLenum status = glClientWaitSync(sync, 0, 0u);
result->status.store(status, std::memory_order_relaxed);
return (status != GL_TIMEOUT_EXPIRED);
}
@@ -1373,23 +1370,15 @@ void OpenGLDriver::destroyStream(Handle<HwStream> sh) {
GLStream* s = handle_cast<GLStream*>(sh);
// if this stream is still attached to a texture, detach it first
auto& texturesWithStreamsAttached = mTexturesWithStreamsAttached;
auto pos = std::find_if(
texturesWithStreamsAttached.begin(), texturesWithStreamsAttached.end(),
[s](GLTexture const* t) {
return t->hwStream == s;
});
if (pos != texturesWithStreamsAttached.end()) {
auto& externalStreams = mExternalStreams;
auto pos = std::find_if(externalStreams.begin(), externalStreams.end(),
[s](GLTexture const* t) { return t->hwStream == s; });
if (pos != externalStreams.end()) {
detachStream(*pos);
}
// and then destroy the stream. Only NATIVE streams have Platform::Stream associated.
if (s->streamType == StreamType::NATIVE) {
mPlatform.destroyStream(s->stream);
}
// finally destroy the HwStream handle
destruct(sh, s);
}
}
@@ -1437,56 +1426,30 @@ Handle<HwStream> OpenGLDriver::createStreamAcquired() {
void OpenGLDriver::setAcquiredImage(Handle<HwStream> sh, void* hwbuffer,
CallbackHandler* handler, StreamCallback cb, void* userData) {
GLStream* glstream = handle_cast<GLStream*>(sh);
assert_invariant(glstream->streamType == StreamType::ACQUIRED);
if (UTILS_UNLIKELY(glstream->user_thread.pending.image)) {
if (glstream->user_thread.pending.image) {
scheduleRelease(glstream->user_thread.pending);
slog.w << "Acquired image is set more than once per frame." << io::endl;
}
glstream->user_thread.pending = mPlatform.transformAcquiredImage({
hwbuffer, cb, userData, handler });
if (glstream->user_thread.pending.image != nullptr) {
// If there's no pending image, do nothing. Note that GL_OES_EGL_image does not let you pass
// NULL to glEGLImageTargetTexture2DOES, and there is no concept of "detaching" an
// EGLimage from a texture.
mStreamsWithPendingAquiredImage.push_back(glstream);
}
}
// updateStreams() and setAcquiredImage() are both called from on the application's thread
// and therefore do not require synchronization. The former is always called immediately before
// beginFrame, the latter is called by the user from anywhere outside beginFrame / endFrame.
void OpenGLDriver::updateStreams(DriverApi* driver) {
if (UTILS_UNLIKELY(!mStreamsWithPendingAquiredImage.empty())) {
for (GLStream* s : mStreamsWithPendingAquiredImage) {
assert_invariant(s);
assert_invariant(s->streamType == StreamType::ACQUIRED);
if (UTILS_UNLIKELY(!mExternalStreams.empty())) {
for (GLTexture* t : mExternalStreams) {
assert_invariant(t);
AcquiredImage const previousImage = s->user_thread.acquired;
s->user_thread.acquired = s->user_thread.pending;
s->user_thread.pending = { nullptr };
GLStream* s = static_cast<GLStream*>(t->hwStream); // NOLINT(cppcoreguidelines-pro-type-static-cast-downcast)
if (UTILS_UNLIKELY(s == nullptr)) {
// this can happen because we're called synchronously and the setExternalStream()
// call may not have been processed yet.
continue;
}
// Bind the stashed EGLImage to its corresponding GL texture as soon as we start
// making the GL calls for the upcoming frame.
driver->queueCommand([this, s, image = s->user_thread.acquired.image, previousImage]() {
auto& streams = mTexturesWithStreamsAttached;
auto pos = std::find_if(streams.begin(), streams.end(),
[s](GLTexture const* t) {
return t->hwStream == s;
});
if (pos != streams.end()) {
setExternalTexture(*pos, image);
}
if (previousImage.image) {
scheduleRelease(AcquiredImage(previousImage));
}
});
if (s->streamType == StreamType::ACQUIRED) {
updateStreamAcquired(t, driver);
}
}
mStreamsWithPendingAquiredImage.clear();
}
}
@@ -1546,12 +1509,6 @@ bool OpenGLDriver::isTextureFormatSupported(TextureFormat format) {
return ext.EXT_texture_compression_s3tc || // this is ES specific
ext.WEBGL_compressed_texture_s3tc; // this is WEBGL specific
}
if (isRGTCCompression(format)) {
return ext.EXT_texture_compression_rgtc;
}
if (isBPTCCompression(format)) {
return ext.EXT_texture_compression_bptc;
}
if (isASTCCompression(format)) {
return ext.KHR_texture_compression_astc_hdr;
}
@@ -1852,7 +1809,7 @@ void OpenGLDriver::updateSamplerGroup(Handle<HwSamplerGroup> sbh,
DEBUG_MARKER()
#if defined(GL_EXT_texture_filter_anisotropic)
OpenGLContext const& context = getContext();
OpenGLContext& context = getContext();
const bool anisotropyWorkaround =
context.ext.EXT_texture_filter_anisotropic &&
context.bugs.texture_filter_anisotropic_broken_on_sampler;
@@ -1896,7 +1853,7 @@ void OpenGLDriver::updateSamplerGroup(Handle<HwSamplerGroup> sbh,
// Driver claims to support anisotropic filtering, but it fails when set on
// the sampler, we have to set it on the texture instead.
// The texture is already bound here.
GLfloat const anisotropy = float(1u << params.anisotropyLog2);
GLfloat anisotropy = float(1u << params.anisotropyLog2);
glTexParameterf(t->gl.target, GL_TEXTURE_MAX_ANISOTROPY_EXT,
std::min(context.gets.max_anisotropy, anisotropy));
}
@@ -1987,8 +1944,8 @@ void OpenGLDriver::setTextureData(GLTexture* t, uint32_t level,
return;
}
GLenum const glFormat = getFormat(p.format);
GLenum const glType = getType(p.type);
GLenum glFormat = getFormat(p.format);
GLenum glType = getType(p.type);
gl.pixelStore(GL_UNPACK_ROW_LENGTH, GLint(p.stride));
gl.pixelStore(GL_UNPACK_ALIGNMENT, GLint(p.alignment));
@@ -1997,7 +1954,7 @@ void OpenGLDriver::setTextureData(GLTexture* t, uint32_t level,
switch (t->target) {
case SamplerType::SAMPLER_EXTERNAL:
// if we get there, it's because the user is trying to use an external texture,
// if we get there, it's because the user is trying to use an external texture
// but it's not supported, so instead, we behave like a texture2d.
// fallthrough...
case SamplerType::SAMPLER_2D:
@@ -2041,7 +1998,7 @@ void OpenGLDriver::setTextureData(GLTexture* t, uint32_t level,
assert_invariant(zoffset + depth <= 6);
UTILS_NOUNROLL
for (size_t face = 0; face < depth; face++) {
GLenum const target = getCubemapTarget(zoffset + face);
GLenum target = getCubemapTarget(zoffset + face);
glTexSubImage2D(target, GLint(level), GLint(xoffset), GLint(yoffset),
GLsizei(width), GLsizei(height), glFormat, glType,
static_cast<uint8_t const*>(p.buffer) + faceSize * face);
@@ -2085,13 +2042,13 @@ void OpenGLDriver::setCompressedTextureData(GLTexture* t, uint32_t level,
// TODO: maybe assert that the CompressedPixelDataType is the same as the internalFormat
GLsizei const imageSize = GLsizei(p.imageSize);
GLsizei imageSize = GLsizei(p.imageSize);
// TODO: maybe assert the size is right (b/c we can compute it ourselves)
switch (t->target) {
case SamplerType::SAMPLER_EXTERNAL:
// if we get there, it's because the user is trying to use an external texture,
// if we get there, it's because the user is trying to use an external texture
// but it's not supported, so instead, we behave like a texture2d.
// fallthrough...
case SamplerType::SAMPLER_2D:
@@ -2133,7 +2090,7 @@ void OpenGLDriver::setCompressedTextureData(GLTexture* t, uint32_t level,
UTILS_NOUNROLL
for (size_t face = 0; face < depth; face++) {
GLenum const target = getCubemapTarget(zoffset + face);
GLenum target = getCubemapTarget(zoffset + face);
glCompressedTexSubImage2D(target, GLint(level), GLint(xoffset), GLint(yoffset),
GLsizei(width), GLsizei(height), t->gl.internalFormat,
imageSize, static_cast<uint8_t const*>(p.buffer) + faceSize * face);
@@ -2220,7 +2177,7 @@ void OpenGLDriver::setExternalStream(Handle<HwTexture> th, Handle<HwStream> sh)
UTILS_NOINLINE
void OpenGLDriver::attachStream(GLTexture* t, GLStream* hwStream) noexcept {
mTexturesWithStreamsAttached.push_back(t);
mExternalStreams.push_back(t);
switch (hwStream->streamType) {
case StreamType::NATIVE:
@@ -2235,10 +2192,10 @@ void OpenGLDriver::attachStream(GLTexture* t, GLStream* hwStream) noexcept {
UTILS_NOINLINE
void OpenGLDriver::detachStream(GLTexture* t) noexcept {
auto& gl = mContext;
auto& texturesWithStreamsAttached = mTexturesWithStreamsAttached;
auto pos = std::find(texturesWithStreamsAttached.begin(), texturesWithStreamsAttached.end(), t);
if (pos != texturesWithStreamsAttached.end()) {
texturesWithStreamsAttached.erase(pos);
auto& streams = mExternalStreams;
auto pos = std::find(streams.begin(), streams.end(), t);
if (pos != streams.end()) {
streams.erase(pos);
}
GLStream* s = static_cast<GLStream*>(t->hwStream); // NOLINT(cppcoreguidelines-pro-type-static-cast-downcast)
@@ -2313,7 +2270,7 @@ void OpenGLDriver::endTimerQuery(Handle<HwTimerQuery> tqh) {
bool OpenGLDriver::getTimerQueryValue(Handle<HwTimerQuery> tqh, uint64_t* elapsedTime) {
GLTimerQuery* tq = handle_cast<GLTimerQuery*>(tqh);
uint64_t const d = tq->elapsed.load(std::memory_order_relaxed);
uint64_t d = tq->elapsed.load(std::memory_order_relaxed);
if (!d) {
return false;
}
@@ -2366,7 +2323,7 @@ void OpenGLDriver::beginRenderPass(Handle<HwRenderTarget> rth,
BACKEND_OPENGL_LEVEL >= BACKEND_OPENGL_LEVEL_GLES30) {
if (!gl.bugs.disable_invalidate_framebuffer) {
AttachmentArray attachments; // NOLINT
GLsizei const attachmentCount = getAttachments(attachments, rt, discardFlags);
GLsizei attachmentCount = getAttachments(attachments, rt, discardFlags);
if (attachmentCount) {
glInvalidateFramebuffer(GL_FRAMEBUFFER, attachmentCount, attachments.data());
}
@@ -2403,10 +2360,8 @@ void OpenGLDriver::beginRenderPass(Handle<HwRenderTarget> rth,
mRenderPassDepthWrite = any(clearFlags & TargetBufferFlags::DEPTH);
mRenderPassStencilWrite = any(clearFlags & TargetBufferFlags::STENCIL);
static_assert(sizeof(GLsizei) >= sizeof(uint32_t));
gl.viewport(params.viewport.left, params.viewport.bottom,
(GLsizei)std::min(uint32_t(std::numeric_limits<int32_t>::max()), params.viewport.width),
(GLsizei)std::min(uint32_t(std::numeric_limits<int32_t>::max()), params.viewport.height));
(GLsizei)params.viewport.width, (GLsizei)params.viewport.height);
gl.depthRange(params.depthRange.near, params.depthRange.far);
@@ -2456,7 +2411,7 @@ void OpenGLDriver::endRenderPass(int) {
// we wouldn't have to bind the framebuffer if we had glInvalidateNamedFramebuffer()
gl.bindFramebuffer(GL_FRAMEBUFFER, rt->gl.fbo);
AttachmentArray attachments; // NOLINT
GLsizei const attachmentCount = getAttachments(attachments, rt, effectiveDiscardFlags);
GLsizei attachmentCount = getAttachments(attachments, rt, effectiveDiscardFlags);
if (attachmentCount) {
glInvalidateFramebuffer(GL_FRAMEBUFFER, attachmentCount, attachments.data());
}
@@ -2484,7 +2439,7 @@ void OpenGLDriver::resolvePass(ResolveAction action, GLRenderTarget const* rt,
assert_invariant(rt->gl.fbo_read);
auto& gl = mContext;
const TargetBufferFlags resolve = rt->gl.resolve & ~discardFlags;
GLbitfield const mask = getAttachmentBitfield(resolve);
GLbitfield mask = getAttachmentBitfield(resolve);
if (UTILS_UNLIKELY(mask)) {
// we can only resolve COLOR0 at the moment
@@ -2575,6 +2530,40 @@ void OpenGLDriver::setScissor(Viewport const& scissor) noexcept {
gl.enable(GL_SCISSOR_TEST);
}
// Binds the external image stashed in the associated stream.
//
// updateStreamAcquired() and setAcquiredImage() are both called from on the application's thread
// and therefore do not require synchronization. The former is always called immediately before
// beginFrame, the latter is called by the user from anywhere outside beginFrame / endFrame.
void OpenGLDriver::updateStreamAcquired(GLTexture* gltexture, DriverApi* driver) noexcept {
SYSTRACE_CALL();
GLStream* glstream = static_cast<GLStream*>(gltexture->hwStream); // NOLINT(cppcoreguidelines-pro-type-static-cast-downcast)
assert_invariant(glstream);
assert_invariant(glstream->streamType == StreamType::ACQUIRED);
// If there's no pending image, do nothing. Note that GL_OES_EGL_image does not let you pass
// NULL to glEGLImageTargetTexture2DOES, and there is no concept of "detaching" an EGLimage from
// a texture.
if (glstream->user_thread.pending.image == nullptr) {
return;
}
AcquiredImage previousImage = glstream->user_thread.acquired;
glstream->user_thread.acquired = glstream->user_thread.pending;
glstream->user_thread.pending = { nullptr };
// Bind the stashed EGLImage to its corresponding GL texture as soon as we start making the GL
// calls for the upcoming frame.
void* image = glstream->user_thread.acquired.image;
driver->queueCommand([this, gltexture, image, previousImage]() {
setExternalTexture(gltexture, image);
if (previousImage.image) {
scheduleRelease(AcquiredImage(previousImage));
}
});
}
// ------------------------------------------------------------------------------------------------
// Setting rendering state
// ------------------------------------------------------------------------------------------------
@@ -2598,7 +2587,7 @@ void OpenGLDriver::bindBufferRange(BufferObjectBinding bindingType, uint32_t ind
GLBufferObject* ub = handle_cast<GLBufferObject *>(ubh);
GLenum const target = GLUtils::getBufferBindingType(bindingType);
GLenum target = GLUtils::getBufferBindingType(bindingType);
assert_invariant(bindingType == BufferObjectBinding::SHADER_STORAGE ||
ub->gl.binding == target);
@@ -2611,7 +2600,7 @@ void OpenGLDriver::bindBufferRange(BufferObjectBinding bindingType, uint32_t ind
void OpenGLDriver::unbindBuffer(BufferObjectBinding bindingType, uint32_t index) {
DEBUG_MARKER()
auto& gl = mContext;
GLenum const target = GLUtils::getBufferBindingType(bindingType);
GLenum target = GLUtils::getBufferBindingType(bindingType);
gl.bindBufferRange(target, GLuint(index), 0, 0, 0);
CHECK_GL_ERROR(utils::slog.e)
}
@@ -2642,7 +2631,7 @@ GLuint OpenGLDriver::getSamplerSlow(SamplerParams params) const noexcept {
auto& gl = mContext;
if (gl.ext.EXT_texture_filter_anisotropic &&
!gl.bugs.texture_filter_anisotropic_broken_on_sampler) {
GLfloat const anisotropy = float(1u << params.anisotropyLog2);
GLfloat anisotropy = float(1u << params.anisotropyLog2);
glSamplerParameterf(s, GL_TEXTURE_MAX_ANISOTROPY_EXT,
std::min(gl.gets.max_anisotropy, anisotropy));
}
@@ -2703,8 +2692,8 @@ void OpenGLDriver::readPixels(Handle<HwRenderTarget> src,
DEBUG_MARKER()
auto& gl = mContext;
GLenum const glFormat = getFormat(p.format);
GLenum const glType = getType(p.type);
GLenum glFormat = getFormat(p.format);
GLenum glType = getType(p.type);
gl.pixelStore(GL_PACK_ROW_LENGTH, (GLint)p.stride);
gl.pixelStore(GL_PACK_ALIGNMENT, (GLint)p.alignment);
@@ -2755,8 +2744,9 @@ void OpenGLDriver::readPixels(Handle<HwRenderTarget> src,
auto& gl = mContext;
gl.bindBuffer(GL_PIXEL_PACK_BUFFER, pbo);
void* vaddr = nullptr;
std::unique_ptr<uint8_t> clientBuffer;
#if defined(__EMSCRIPTEN__)
std::unique_ptr<uint8_t> clientBuffer = std::make_unique<uint8_t>(p.size);
clientBuffer.reset(new uint8_t(p.size));
glGetBufferSubData(GL_PIXEL_PACK_BUFFER, 0, p.size, clientBuffer.get());
vaddr = clientBuffer.get();
#else
@@ -2764,10 +2754,10 @@ void OpenGLDriver::readPixels(Handle<HwRenderTarget> src,
#endif
if (vaddr) {
// now we need to flip the buffer vertically to match our API
size_t const stride = p.stride ? p.stride : width;
size_t const bpp = PixelBufferDescriptor::computeDataSize(
size_t stride = p.stride ? p.stride : width;
size_t bpp = PixelBufferDescriptor::computeDataSize(
p.format, p.type, 1, 1, 1);
size_t const bpr = PixelBufferDescriptor::computeDataSize(
size_t bpr = PixelBufferDescriptor::computeDataSize(
p.format, p.type, stride, 1, p.alignment);
char const* head = (char const*)vaddr + p.left * bpp + bpr * p.top;
char* tail = (char*)p.buffer + p.left * bpp + bpr * (p.top + height - 1);
@@ -2854,7 +2844,7 @@ void OpenGLDriver::executeGpuCommandsCompleteOps() noexcept {
auto& v = mGpuCommandCompleteOps;
auto it = v.begin();
while (it != v.end()) {
GLenum const status = glClientWaitSync(it->first, 0, 0);
GLenum status = glClientWaitSync(it->first, 0, 0);
if (status == GL_ALREADY_SIGNALED || status == GL_CONDITION_SATISFIED) {
it->second();
glDeleteSync(it->first);
@@ -2911,15 +2901,13 @@ void OpenGLDriver::tick(int) {
executeEveryNowAndThenOps();
}
void OpenGLDriver::beginFrame(
UTILS_UNUSED int64_t monotonic_clock_ns,
UTILS_UNUSED uint32_t frameId) {
void OpenGLDriver::beginFrame(int64_t monotonic_clock_ns, uint32_t frameId) {
DEBUG_MARKER()
auto& gl = mContext;
insertEventMarker("beginFrame");
if (UTILS_UNLIKELY(!mTexturesWithStreamsAttached.empty())) {
if (UTILS_UNLIKELY(!mExternalStreams.empty())) {
OpenGLPlatform& platform = mPlatform;
for (GLTexture const* t : mTexturesWithStreamsAttached) {
for (GLTexture const* t : mExternalStreams) {
assert_invariant(t && t->hwStream);
if (t->hwStream->streamType == StreamType::NATIVE) {
assert_invariant(t->hwStream->stream);
@@ -2947,7 +2935,7 @@ void OpenGLDriver::setPresentationTime(int64_t monotonic_clock_ns) {
mPlatform.setPresentationTime(monotonic_clock_ns);
}
void OpenGLDriver::endFrame(UTILS_UNUSED uint32_t frameId) {
void OpenGLDriver::endFrame(uint32_t frameId) {
DEBUG_MARKER()
#if defined(__EMSCRIPTEN__)
// WebGL builds are single-threaded so users might manipulate various GL state after we're
@@ -3050,7 +3038,7 @@ void OpenGLDriver::blit(TargetBufferFlags buffers,
DEBUG_MARKER()
auto& gl = mContext;
GLbitfield const mask = getAttachmentBitfield(buffers);
GLbitfield mask = getAttachmentBitfield(buffers);
if (mask) {
GLenum glFilterMode = (filter == SamplerMagFilter::NEAREST) ? GL_NEAREST : GL_LINEAR;
if (mask & (GL_DEPTH_BUFFER_BIT | GL_STENCIL_BUFFER_BIT)) {
+5 -6
View File
@@ -48,6 +48,7 @@ class PixelBufferDescriptor;
struct TargetBufferInfo;
class OpenGLProgram;
class OpenGLBlitter;
class OpenGLTimerQueryInterface;
class OpenGLDriver final : public DriverBase {
@@ -151,6 +152,8 @@ public:
struct GLStream : public HwStream {
using HwStream::HwStream;
struct Info {
// storage for the read/write textures below
Platform::ExternalTexture* ets = nullptr;
GLuint width = 0;
GLuint height = 0;
};
@@ -344,12 +347,7 @@ private:
std::array<GLSamplerGroup*, Program::SAMPLER_BINDING_COUNT> mSamplerBindings = {}; // 4 pointers
mutable tsl::robin_map<uint32_t, GLuint> mSamplerMap;
// this must be accessed from the driver thread only
std::vector<GLTexture*> mTexturesWithStreamsAttached;
// the must be accessed from the user thread only
std::vector<GLStream*> mStreamsWithPendingAquiredImage;
mutable std::vector<GLTexture*> mExternalStreams;
void attachStream(GLTexture* t, GLStream* stream) noexcept;
void detachStream(GLTexture* t) noexcept;
@@ -357,6 +355,7 @@ private:
OpenGLPlatform& mPlatform;
void updateStreamAcquired(GLTexture* t, DriverApi* driver) noexcept;
void updateTextureLodRange(GLTexture* texture, int8_t targetLevel) noexcept;
void setExternalTexture(GLTexture* t, void* image);
+27 -30
View File
@@ -81,7 +81,7 @@ OpenGLProgram::~OpenGLProgram() noexcept {
}
const GLuint program = gl.program;
UTILS_NOUNROLL
for (GLuint const shader: gl.shaders) {
for (GLuint shader: gl.shaders) {
if (shader) {
if (program) {
glDetachShader(program, shader);
@@ -103,18 +103,18 @@ void OpenGLProgram::compileShaders(OpenGLContext& context,
Program::ShaderSource shadersSource,
utils::FixedCapacityVector<Program::SpecializationConstant> const& specializationConstants,
GLuint shaderIds[Program::SHADER_TYPE_COUNT],
UTILS_UNUSED_IN_RELEASE std::array<CString, Program::SHADER_TYPE_COUNT>& outShaderSourceCode) noexcept {
std::array<CString, Program::SHADER_TYPE_COUNT>& outShaderSourceCode) noexcept {
std::string specializationConstantString;
std::string specificationConstantString;
for (auto const& sc : specializationConstants) {
specializationConstantString += "#define SPIRV_CROSS_CONSTANT_ID_" + std::to_string(sc.id) + ' ';
specializationConstantString += std::visit([](auto&& arg) {
specificationConstantString += "#define SPIRV_CROSS_CONSTANT_ID_" + std::to_string(sc.id) + ' ';
specificationConstantString += std::visit([](auto&& arg) {
return std::to_string(arg);
}, sc.value);
specializationConstantString += '\n';
specificationConstantString += '\n';
}
if (!specializationConstantString.empty()) {
specializationConstantString += '\n';
if (!specificationConstantString.empty()) {
specificationConstantString += '\n';
}
// build all shaders
@@ -132,7 +132,7 @@ void OpenGLProgram::compileShaders(OpenGLContext& context,
Program::ShaderBlob& shader = shadersSource[i];
// remove GOOGLE_cpp_style_line_directive
std::string_view const source = process_GOOGLE_cpp_style_line_directive(context,
std::string_view source = process_GOOGLE_cpp_style_line_directive(context,
reinterpret_cast<char*>(shader.data()), shader.size());
// add support for ARB_shading_language_packing if needed
@@ -143,19 +143,19 @@ void OpenGLProgram::compileShaders(OpenGLContext& context,
const std::array<const char*, 4> sources = {
prolog.data(),
specializationConstantString.c_str(),
specificationConstantString.c_str(),
packingFunctions.data(),
body.data()
};
const std::array<GLint, 4> lengths = {
(GLint)prolog.length(),
(GLint)specializationConstantString.length(),
(GLint)specificationConstantString.length(),
(GLint)packingFunctions.length(),
(GLint)body.length() - 1 // null terminated
};
GLuint const shaderId = glCreateShader(glShaderType);
GLuint shaderId = glCreateShader(glShaderType);
glShaderSource(shaderId, sources.size(), sources.data(), lengths.data());
glCompileShader(shaderId);
@@ -246,8 +246,8 @@ std::array<std::string_view, 2> OpenGLProgram::splitShaderSource(std::string_vie
auto eol = source.find('\n', pos) + 1;
assert_invariant(eol != std::string_view::npos);
std::string_view const version = source.substr(start, eol - start);
std::string_view const body = source.substr(version.length(), source.length() - version.length());
std::string_view version = source.substr(start, eol - start);
std::string_view body = source.substr(version.length(), source.length() - version.length());
return { version, body };
}
@@ -257,7 +257,7 @@ std::array<std::string_view, 2> OpenGLProgram::splitShaderSource(std::string_vie
* program itself is valid).
*/
GLuint OpenGLProgram::linkProgram(const GLuint shaderIds[Program::SHADER_TYPE_COUNT]) noexcept {
GLuint const program = glCreateProgram();
GLuint program = glCreateProgram();
for (size_t i = 0; i < Program::SHADER_TYPE_COUNT; i++) {
if (shaderIds[i]) {
glAttachShader(program, shaderIds[i]);
@@ -286,15 +286,13 @@ bool OpenGLProgram::checkProgramStatus(const char* name,
for (size_t i = 0; i < Program::SHADER_TYPE_COUNT; i++) {
const ShaderStage type = static_cast<ShaderStage>(i);
const GLuint shader = shaderIds[i];
if (shader) {
glGetShaderiv(shader, GL_COMPILE_STATUS, &status);
if (status != GL_TRUE) {
logCompilationError(slog.e, type, name, shader, shaderSourceCode[i]);
}
glDetachShader(program, shader);
glDeleteShader(shader);
shaderIds[i] = 0;
glGetShaderiv(shader, GL_COMPILE_STATUS, &status);
if (status != GL_TRUE) {
logCompilationError(slog.e, type, name, shader, shaderSourceCode[i]);
}
glDetachShader(program, shader);
glDeleteShader(shader);
shaderIds[i] = 0;
}
// log the link error as well
logProgramLinkError(slog.e, name, program);
@@ -342,9 +340,9 @@ void OpenGLProgram::initializeProgramState(OpenGLContext& context, GLuint progra
for (GLuint binding = 0, n = lazyInitializationData.uniformBlockInfo.size(); binding < n; binding++) {
auto const& name = lazyInitializationData.uniformBlockInfo[binding];
if (!name.empty()) {
GLuint const index = glGetUniformBlockIndex(program, name.c_str());
if (index != GL_INVALID_INDEX) {
glUniformBlockBinding(program, index, binding);
GLint index = glGetUniformBlockIndex(program, name.c_str());
if (index >= 0) {
glUniformBlockBinding(program, GLuint(index), binding);
}
CHECK_GL_ERROR(utils::slog.e)
}
@@ -369,7 +367,7 @@ void OpenGLProgram::initializeProgramState(OpenGLContext& context, GLuint progra
UTILS_NOUNROLL
for (const Program::Sampler& sampler: samplers) {
// find its location and associate a TMU to it
GLint const loc = glGetUniformLocation(program, sampler.name.c_str());
GLint loc = glGetUniformLocation(program, sampler.name.c_str());
if (loc >= 0) {
// this can fail if the program doesn't use this sampler
glUniform1i(loc, tmu);
@@ -404,7 +402,7 @@ void OpenGLProgram::updateSamplers(OpenGLDriver* gld) const noexcept {
assert_invariant(sb);
for (uint8_t j = 0, m = sb->textureUnitEntries.size(); j < m; ++j, ++tmu) { // "<=" on purpose here
const GLTexture* const t = sb->textureUnitEntries[j].texture;
GLuint const s = sb->textureUnitEntries[j].sampler;
GLuint s = sb->textureUnitEntries[j].sampler;
if (t) { // program may not use all samplers of sampler group
if (UTILS_UNLIKELY(t->gl.fence)) {
glWaitSync(t->gl.fence, 0, GL_TIMEOUT_IGNORED);
@@ -421,8 +419,7 @@ void OpenGLProgram::updateSamplers(OpenGLDriver* gld) const noexcept {
UTILS_NOINLINE
void logCompilationError(io::ostream& out, ShaderStage shaderType,
const char* name, GLuint shaderId,
UTILS_UNUSED_IN_RELEASE CString const& sourceCode) noexcept {
const char* name, GLuint shaderId, CString const& sourceCode) noexcept {
auto to_string = [](ShaderStage type) -> const char* {
switch (type) {
+1 -9
View File
@@ -252,14 +252,7 @@ io::ostream& operator<<(io::ostream& out, TextureFormat format) {
CASE(TextureFormat, DXT3_SRGBA)
CASE(TextureFormat, DXT5_RGBA)
CASE(TextureFormat, DXT5_SRGBA)
CASE(TextureFormat, RED_RGTC1)
CASE(TextureFormat, SIGNED_RED_RGTC1)
CASE(TextureFormat, RED_GREEN_RGTC2)
CASE(TextureFormat, SIGNED_RED_GREEN_RGTC2)
CASE(TextureFormat, RGB_BPTC_SIGNED_FLOAT)
CASE(TextureFormat, RGB_BPTC_UNSIGNED_FLOAT)
CASE(TextureFormat, RGBA_BPTC_UNORM)
CASE(TextureFormat, SRGB_ALPHA_BPTC_UNORM)
CASE(TextureFormat, UNUSED)
CASE(TextureFormat, RGBA_ASTC_4x4)
CASE(TextureFormat, RGBA_ASTC_5x4)
CASE(TextureFormat, RGBA_ASTC_5x5)
@@ -288,7 +281,6 @@ io::ostream& operator<<(io::ostream& out, TextureFormat format) {
CASE(TextureFormat, SRGB8_ALPHA8_ASTC_10x10)
CASE(TextureFormat, SRGB8_ALPHA8_ASTC_12x10)
CASE(TextureFormat, SRGB8_ALPHA8_ASTC_12x12)
CASE(TextureFormat, UNUSED)
}
return out;
}
@@ -184,16 +184,6 @@ VkFormat getVkFormat(TextureFormat format) {
case TextureFormat::DXT5_RGBA: return VK_FORMAT_BC3_UNORM_BLOCK;
case TextureFormat::DXT5_SRGBA: return VK_FORMAT_BC3_SRGB_BLOCK;
case TextureFormat::RED_RGTC1: return VK_FORMAT_BC4_UNORM_BLOCK;
case TextureFormat::SIGNED_RED_RGTC1: return VK_FORMAT_BC4_SNORM_BLOCK;
case TextureFormat::RED_GREEN_RGTC2: return VK_FORMAT_BC5_UNORM_BLOCK;
case TextureFormat::SIGNED_RED_GREEN_RGTC2: return VK_FORMAT_BC5_SNORM_BLOCK;
case TextureFormat::RGB_BPTC_SIGNED_FLOAT: return VK_FORMAT_BC6H_SFLOAT_BLOCK;
case TextureFormat::RGB_BPTC_UNSIGNED_FLOAT: return VK_FORMAT_BC6H_UFLOAT_BLOCK;
case TextureFormat::RGBA_BPTC_UNORM: return VK_FORMAT_BC7_UNORM_BLOCK;
case TextureFormat::SRGB_ALPHA_BPTC_UNORM: return VK_FORMAT_BC7_SRGB_BLOCK;
case TextureFormat::RGBA_ASTC_4x4: return VK_FORMAT_ASTC_4x4_UNORM_BLOCK;
case TextureFormat::RGBA_ASTC_5x4: return VK_FORMAT_ASTC_5x4_UNORM_BLOCK;
case TextureFormat::RGBA_ASTC_5x5: return VK_FORMAT_ASTC_5x5_UNORM_BLOCK;
+37 -13
View File
@@ -22,12 +22,8 @@
#include <spirv_glsl.hpp>
#include <spirv_msl.hpp>
#include "../src/GLSLPostProcessor.h"
#include "builtinResource.h"
#include <utils/FixedCapacityVector.h>
#include <iostream>
#include <utility>
#include <vector>
@@ -63,6 +59,38 @@ void SpvToGlsl(const SpirvBlob* spirv, std::string* outGlsl) {
*outGlsl = glslCompiler.compile();
}
void SpvToMsl(const SpirvBlob* spirv, std::string* outMsl) {
CompilerMSL mslCompiler(*spirv);
mslCompiler.set_msl_options(CompilerMSL::Options {
.msl_version = CompilerMSL::Options::make_msl_version(1, 1)
});
auto executionModel = mslCompiler.get_execution_model();
auto duplicateResourceBinding = [executionModel, &mslCompiler](const auto& resource) {
auto set = mslCompiler.get_decoration(resource.id, spv::DecorationDescriptorSet);
auto binding = mslCompiler.get_decoration(resource.id, spv::DecorationBinding);
MSLResourceBinding newBinding;
newBinding.stage = executionModel;
newBinding.desc_set = set;
newBinding.binding = binding;
newBinding.msl_texture = binding;
newBinding.msl_sampler = binding;
newBinding.msl_buffer = binding;
mslCompiler.add_msl_resource_binding(newBinding);
};
auto resources = mslCompiler.get_shader_resources();
for (const auto& resource : resources.sampled_images) {
duplicateResourceBinding(resource);
}
for (const auto& resource : resources.uniform_buffers) {
duplicateResourceBinding(resource);
}
*outMsl = mslCompiler.compile();
}
} // anonymous namespace
void ShaderGenerator::init() {
@@ -74,16 +102,15 @@ void ShaderGenerator::shutdown() {
}
ShaderGenerator::ShaderGenerator(std::string vertex, std::string fragment,
Backend backend, bool isMobile, const filament::SamplerInterfaceBlock* sib) noexcept
Backend backend, bool isMobile) noexcept
: mBackend(backend),
mVertexBlob(transpileShader(ShaderStage::VERTEX, std::move(vertex), backend, isMobile, sib)),
mVertexBlob(transpileShader(ShaderStage::VERTEX, std::move(vertex), backend, isMobile)),
mFragmentBlob(transpileShader(ShaderStage::FRAGMENT, std::move(fragment), backend,
isMobile, sib)) {
isMobile)) {
}
ShaderGenerator::Blob ShaderGenerator::transpileShader(
ShaderStage stage, std::string shader, Backend backend, bool isMobile,
const filament::SamplerInterfaceBlock* sib) noexcept {
ShaderStage stage, std::string shader, Backend backend, bool isMobile) noexcept {
TProgram program;
const EShLanguage language = stage == ShaderStage::VERTEX ? EShLangVertex : EShLangFragment;
TShader tShader(language);
@@ -146,10 +173,7 @@ ShaderGenerator::Blob ShaderGenerator::transpileShader(
}
return { result.c_str(), result.c_str() + result.length() + 1 };
} else if (backend == Backend::METAL) {
const auto sm = isMobile ? ShaderModel::MOBILE : ShaderModel::DESKTOP;
filamat::SibVector sibs = filamat::SibVector::with_capacity(1);
if (sib) { sibs.emplace_back(0, sib); }
filamat::GLSLPostProcessor::spirvToMsl(&spirv, &result, sm, false, sibs, nullptr);
SpvToMsl(&spirv, &result);
return { result.c_str(), result.c_str() + result.length() + 1 };
} else if (backend == Backend::VULKAN) {
return { (uint8_t*)spirv.data(), (uint8_t*)(spirv.data() + spirv.size()) };
+2 -4
View File
@@ -19,7 +19,6 @@
#include "PlatformRunner.h"
#include "private/filament/SamplerInterfaceBlock.h"
#include "private/backend/DriverApi.h"
#include "backend/Program.h"
@@ -39,8 +38,7 @@ public:
* @param vertex The vertex shader, written in GLSL 450 core.
* @param fragment The fragment shader, written in GLSL 450 core.
*/
ShaderGenerator(std::string vertex, std::string fragment, Backend backend, bool isMobile,
const filament::SamplerInterfaceBlock* sib = nullptr) noexcept;
ShaderGenerator(std::string vertex, std::string fragment, Backend backend, bool isMobile) noexcept;
ShaderGenerator(const ShaderGenerator& rhs) = delete;
ShaderGenerator& operator=(const ShaderGenerator& rhs) = delete;
@@ -52,7 +50,7 @@ private:
using Blob = std::vector<char>;
static Blob transpileShader(ShaderStage stage, std::string shader, Backend backend,
bool isMobile, const filament::SamplerInterfaceBlock* sib = nullptr) noexcept;
bool isMobile) noexcept;
Backend mBackend;
+1 -6
View File
@@ -133,12 +133,7 @@ TEST_F(BackendTest, FeedbackLoops) {
// Create a program.
ProgramHandle program;
{
SamplerInterfaceBlock sib = filament::SamplerInterfaceBlock::Builder()
.name("backend_test_sib")
.stageFlags(backend::ShaderStageFlags::ALL_SHADER_STAGE_FLAGS)
.add( {{"tex", SamplerType::SAMPLER_2D, SamplerFormat::FLOAT, Precision::HIGH }} )
.build();
ShaderGenerator shaderGen(fullscreenVs, fullscreenFs, sBackend, sIsMobilePlatform, &sib);
ShaderGenerator shaderGen(fullscreenVs, fullscreenFs, sBackend, sIsMobilePlatform);
Program prog = shaderGen.getProgram(api);
Program::Sampler psamplers[] = { utils::CString("tex"), 0 };
prog.setSamplerGroup(0, ShaderStageFlags::ALL_SHADER_STAGE_FLAGS, psamplers, sizeof(psamplers) / sizeof(psamplers[0]));
+12 -31
View File
@@ -19,7 +19,6 @@
#include "ShaderGenerator.h"
#include "TrianglePrimitive.h"
#include "private/filament/SamplerInterfaceBlock.h"
#include "private/backend/SamplerGroup.h"
#include <math/half.h>
@@ -55,7 +54,7 @@ layout(location = 0) out vec4 fragColor;
// Filament's Vulkan backend requires a descriptor set index of 1 for all samplers.
// This parameter is ignored for other backends.
layout(location = 0, set = 1) uniform {samplerType} test_tex;
layout(location = 0, set = 1) uniform {samplerType} tex;
void main() {
vec2 fbsize = vec2(512);
@@ -63,7 +62,7 @@ void main() {
#if defined(TARGET_METAL_ENVIRONMENT) || defined(TARGET_VULKAN_ENVIRONMENT)
uv.y = 1.0 - uv.y;
#endif
fragColor = vec4(texture(test_tex, uv).rgb, 1.0f);
fragColor = vec4(texture(tex, uv).rgb, 1.0f);
}
)");
@@ -73,7 +72,8 @@ std::string fragmentUpdateImage3DTemplate (R"(#version 450 core
layout(location = 0) out vec4 fragColor;
// Filament's Vulkan backend requires a descriptor set index of 1 for all samplers.
layout(location = 0, set = 1) uniform {samplerType} test_tex;
// This parameter is ignored for other backends.
layout(location = 0, set = 1) uniform {samplerType} tex;
float getLayer(in sampler3D s) { return 2.5f / 4.0f; }
float getLayer(in sampler2DArray s) { return 2.0f; }
@@ -84,7 +84,7 @@ void main() {
#if defined(TARGET_METAL_ENVIRONMENT) || defined(TARGET_VULKAN_ENVIRONMENT)
uv.y = 1.0 - uv.y;
#endif
fragColor = vec4(texture(test_tex, vec3(uv, getLayer(test_tex))).rgb, 1.0f);
fragColor = vec4(texture(tex, vec3(uv, getLayer(tex))).rgb, 1.0f);
}
)");
@@ -94,7 +94,8 @@ std::string fragmentUpdateImageMip (R"(#version 450 core
layout(location = 0) out vec4 fragColor;
// Filament's Vulkan backend requires a descriptor set index of 1 for all samplers.
layout(location = 0, set = 1) uniform sampler2D test_tex;
// This parameter is ignored for other backends.
layout(location = 0, set = 1) uniform sampler2D tex;
void main() {
vec2 fbsize = vec2(512);
@@ -102,7 +103,7 @@ void main() {
#if defined(TARGET_METAL_ENVIRONMENT) || defined(TARGET_VULKAN_ENVIRONMENT)
uv.y = 1.0 - uv.y;
#endif
fragColor = vec4(textureLod(test_tex, uv, 1.0f).rgb, 1.0f);
fragColor = vec4(textureLod(tex, uv, 1.0f).rgb, 1.0f);
}
)");
@@ -344,15 +345,10 @@ TEST_F(BackendTest, UpdateImage2D) {
auto defaultRenderTarget = api.createDefaultRenderTarget(0);
// Create a program.
SamplerInterfaceBlock sib = filament::SamplerInterfaceBlock::Builder()
.name("Test")
.stageFlags(backend::ShaderStageFlags::ALL_SHADER_STAGE_FLAGS)
.add( {{"tex", SamplerType::SAMPLER_2D, SamplerFormat::FLOAT, Precision::HIGH }} )
.build();
ProgramHandle program;
std::string fragment = stringReplace("{samplerType}",
getSamplerTypeName(t.textureFormat), fragmentTemplate);
ShaderGenerator shaderGen(vertex, fragment, sBackend, sIsMobilePlatform, &sib);
ShaderGenerator shaderGen(vertex, fragment, sBackend, sIsMobilePlatform);
Program prog = shaderGen.getProgram(api);
Program::Sampler psamplers[] = { utils::CString("tex"), 0 };
prog.setSamplerGroup(0, ShaderStageFlags::ALL_SHADER_STAGE_FLAGS, psamplers, sizeof(psamplers) / sizeof(psamplers[0]));
@@ -429,14 +425,9 @@ TEST_F(BackendTest, UpdateImageSRGB) {
auto defaultRenderTarget = api.createDefaultRenderTarget(0);
// Create a program.
SamplerInterfaceBlock sib = filament::SamplerInterfaceBlock::Builder()
.name("Test")
.stageFlags(backend::ShaderStageFlags::ALL_SHADER_STAGE_FLAGS)
.add( {{"tex", SamplerType::SAMPLER_2D, SamplerFormat::FLOAT, Precision::HIGH }} )
.build();
std::string fragment = stringReplace("{samplerType}",
getSamplerTypeName(textureFormat), fragmentTemplate);
ShaderGenerator shaderGen(vertex, fragment, sBackend, sIsMobilePlatform, &sib);
ShaderGenerator shaderGen(vertex, fragment, sBackend, sIsMobilePlatform);
Program prog = shaderGen.getProgram(api);
Program::Sampler psamplers[] = { utils::CString("tex"), 0 };
prog.setSamplerGroup(0, ShaderStageFlags::ALL_SHADER_STAGE_FLAGS, psamplers, sizeof(psamplers) / sizeof(psamplers[0]));
@@ -520,14 +511,9 @@ TEST_F(BackendTest, UpdateImageMipLevel) {
auto defaultRenderTarget = api.createDefaultRenderTarget(0);
// Create a program.
SamplerInterfaceBlock sib = filament::SamplerInterfaceBlock::Builder()
.name("Test")
.stageFlags(backend::ShaderStageFlags::ALL_SHADER_STAGE_FLAGS)
.add( {{"tex", SamplerType::SAMPLER_3D, SamplerFormat::FLOAT, Precision::HIGH }} )
.build();
std::string fragment = stringReplace("{samplerType}",
getSamplerTypeName(textureFormat), fragmentUpdateImageMip);
ShaderGenerator shaderGen(vertex, fragment, sBackend, sIsMobilePlatform, &sib);
ShaderGenerator shaderGen(vertex, fragment, sBackend, sIsMobilePlatform);
Program prog = shaderGen.getProgram(api);
Program::Sampler psamplers[] = { utils::CString("tex"), 0 };
prog.setSamplerGroup(0, ShaderStageFlags::ALL_SHADER_STAGE_FLAGS, psamplers, sizeof(psamplers) / sizeof(psamplers[0]));
@@ -597,14 +583,9 @@ TEST_F(BackendTest, UpdateImage3D) {
auto defaultRenderTarget = api.createDefaultRenderTarget(0);
// Create a program.
SamplerInterfaceBlock sib = filament::SamplerInterfaceBlock::Builder()
.name("Test")
.stageFlags(backend::ShaderStageFlags::ALL_SHADER_STAGE_FLAGS)
.add( {{"tex", SamplerType::SAMPLER_3D, SamplerFormat::FLOAT, Precision::HIGH }} )
.build();
std::string fragment = stringReplace("{samplerType}",
getSamplerTypeName(samplerType), fragmentUpdateImage3DTemplate);
ShaderGenerator shaderGen(vertex, fragment, sBackend, sIsMobilePlatform, &sib);
ShaderGenerator shaderGen(vertex, fragment, sBackend, sIsMobilePlatform);
Program prog = shaderGen.getProgram(api);
Program::Sampler psamplers[] = { utils::CString("tex"), 0 };
prog.setSamplerGroup(0, ShaderStageFlags::ALL_SHADER_STAGE_FLAGS, psamplers, sizeof(psamplers) / sizeof(psamplers[0]));
@@ -45,7 +45,7 @@ std::string fragment (R"(#version 450 core
layout(location = 0) out vec4 fragColor;
layout(location = 0) in vec2 uv;
layout(location = 0, set = 1) uniform sampler2D tex;
layout(set = 1, binding = 6) uniform sampler2D tex;
void main() {
fragColor = texture(tex, uv);
@@ -65,16 +65,11 @@ TEST_F(BackendTest, RenderExternalImageWithoutSet) {
auto swapChain = createSwapChain();
SamplerInterfaceBlock sib = filament::SamplerInterfaceBlock::Builder()
.name("backend_test_sib")
.stageFlags(backend::ShaderStageFlags::ALL_SHADER_STAGE_FLAGS)
.add( {{"tex", SamplerType::SAMPLER_EXTERNAL, SamplerFormat::FLOAT, Precision::HIGH }} )
.build();
ShaderGenerator shaderGen(vertex, fragment, sBackend, sIsMobilePlatform, &sib);
ShaderGenerator shaderGen(vertex, fragment, sBackend, sIsMobilePlatform);
// Create a program that samples a texture.
Program p = shaderGen.getProgram(getDriverApi());
Program::Sampler sampler { utils::CString("tex"), 0 };
Program::Sampler sampler { utils::CString("tex"), 6 };
p.setSamplerGroup(0, ShaderStageFlags::ALL_SHADER_STAGE_FLAGS, &sampler, 1);
backend::Handle<HwProgram> program = getDriverApi().createProgram(std::move(p));
@@ -144,16 +139,11 @@ TEST_F(BackendTest, RenderExternalImage) {
auto swapChain = createSwapChain();
SamplerInterfaceBlock sib = filament::SamplerInterfaceBlock::Builder()
.name("backend_test_sib")
.stageFlags(backend::ShaderStageFlags::ALL_SHADER_STAGE_FLAGS)
.add( {{"tex", SamplerType::SAMPLER_EXTERNAL, SamplerFormat::FLOAT, Precision::HIGH }} )
.build();
ShaderGenerator shaderGen(vertex, fragment, sBackend, sIsMobilePlatform, &sib);
ShaderGenerator shaderGen(vertex, fragment, sBackend, sIsMobilePlatform);
// Create a program that samples a texture.
Program p = shaderGen.getProgram(getDriverApi());
Program::Sampler sampler { utils::CString("tex"), 0 };
Program::Sampler sampler { utils::CString("tex"), 6 };
p.setSamplerGroup(0, ShaderStageFlags::ALL_SHADER_STAGE_FLAGS, &sampler, 1);
auto program = getDriverApi().createProgram(std::move(p));
+19 -23
View File
@@ -40,33 +40,33 @@ using sRGBColorA = math::float4;
//! types of RGB colors
enum class RgbType : uint8_t {
sRGB, //!< the color is defined in Rec.709-sRGB-D65 (sRGB) space
LINEAR, //!< the color is defined in Rec.709-Linear-D65 ("linear sRGB") space
sRGB, //!< the color is defined in sRGB space
LINEAR, //!< the color is defined in linear space
};
//! types of RGBA colors
enum class RgbaType : uint8_t {
/**
* the color is defined in Rec.709-sRGB-D65 (sRGB) space and the RGB values
* have not been pre-multiplied by the alpha (for instance, a 50%
* the color is defined in sRGB space and the RGB values
* have not been premultiplied by the alpha (for instance, a 50%
* transparent red is <1,0,0,0.5>)
*/
sRGB,
/**
* the color is defined in Rec.709-Linear-D65 ("linear sRGB") space and the
* RGB values have not been pre-multiplied by the alpha (for instance, a 50%
* the color is defined in linear space and the RGB values
* have not been premultiplied by the alpha (for instance, a 50%
* transparent red is <1,0,0,0.5>)
*/
LINEAR,
/**
* the color is defined in Rec.709-sRGB-D65 (sRGB) space and the RGB values
* have been pre-multiplied by the alpha (for instance, a 50%
* the color is defined in sRGB space and the RGB values
* have been premultiplied by the alpha (for instance, a 50%
* transparent red is <0.5,0,0,0.5>)
*/
PREMULTIPLIED_sRGB,
/**
* the color is defined in Rec.709-Linear-D65 ("linear sRGB") space and the
* RGB values have been pre-multiplied by the alpha (for instance, a 50%
* the color is defined in linear space and the RGB values
* have been premultiplied by the alpha (for instance, a 50%
* transparent red is <0.5,0,0,0.5>)
*/
PREMULTIPLIED_LINEAR
@@ -93,44 +93,40 @@ public:
template<ColorConversion = ACCURATE>
static LinearColor toLinear(sRGBColor const& color);
/**
* Converts an RGB color in Rec.709-Linear-D65 ("linear sRGB") space to an
* RGB color in Rec.709-sRGB-D65 (sRGB) space.
*/
//! converts an RGB color in linear space to an RGB color in sRGB space
template<ColorConversion = ACCURATE>
static sRGBColor toSRGB(LinearColor const& color);
/**
* Converts an RGBA color in Rec.709-sRGB-D65 (sRGB) space to an RGBA color in
* Rec.709-Linear-D65 ("linear sRGB") space the alpha component is left unmodified.
* converts an RGBA color in sRGB space to an RGBA color in linear space
* the alpha component is left unmodified
*/
template<ColorConversion = ACCURATE>
static LinearColorA toLinear(sRGBColorA const& color);
/**
* Converts an RGBA color in Rec.709-Linear-D65 ("linear sRGB") space to
* an RGBA color in Rec.709-sRGB-D65 (sRGB) space the alpha component is
* left unmodified.
* converts an RGBA color in linear space to an RGBA color in sRGB space
* the alpha component is left unmodified
*/
template<ColorConversion = ACCURATE>
static sRGBColorA toSRGB(LinearColorA const& color);
/**
* Converts a correlated color temperature to a linear RGB color in sRGB
* converts a correlated color temperature to a linear RGB color in sRGB
* space the temperature must be expressed in kelvin and must be in the
* range 1,000K to 15,000K.
* range 1,000K to 15,000K
*/
static LinearColor cct(float K);
/**
* Converts a CIE standard illuminant series D to a linear RGB color in
* converts a CIE standard illuminant series D to a linear RGB color in
* sRGB space the temperature must be expressed in kelvin and must be in
* the range 4,000K to 25,000K
*/
static LinearColor illuminantD(float K);
/**
* Computes the Beer-Lambert absorption coefficients from the specified
* computes the Beer-Lambert absorption coefficients from the specified
* transmittance color and distance. The computed absorption will guarantee
* the white light will become the specified color at the specified distance.
* The output of this function can be used as the absorption parameter of
-18
View File
@@ -31,10 +31,6 @@ namespace filament {
class Engine;
class FColorGrading;
namespace color {
class ColorSpace;
}
/**
* ColorGrading is used to transform (either to modify or correct) the colors of the HDR buffer
* rendered by Filament. Color grading transforms are applied after lighting, and after any lens
@@ -101,7 +97,6 @@ class ColorSpace;
* - Tone mapping: ACESLegacyToneMapper
* - Luminance scaling: false
* - Gamut mapping: false
* - Output color space: Rec709-sRGB-D65
*
* @see View
*/
@@ -452,19 +447,6 @@ public:
*/
Builder& curves(math::float3 shadowGamma, math::float3 midPoint, math::float3 highlightScale) noexcept;
/**
* Sets the output color space for this ColorGrading object. After all color grading steps
* have been applied, the final color will be converted in the desired color space.
*
* NOTE: Currently the output color space must be one of Rec709-sRGB-D65 or
* Rec709-Linear-D65. Only the transfer function is taken into account.
*
* @param colorSpace The output color space.
*
* @return This Builder, for chaining calls
*/
Builder& outputColorSpace(const color::ColorSpace& colorSpace) noexcept;
/**
* Creates the ColorGrading object and returns a pointer to it.
*
-256
View File
@@ -1,256 +0,0 @@
/*
* Copyright (C) 2015 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.
*/
#ifndef TNT_FILAMENT_COLOR_SPACE_H
#define TNT_FILAMENT_COLOR_SPACE_H
#include <math/vec2.h>
#include <math/vec3.h>
namespace filament {
namespace color {
using namespace math;
/**
* Holds the chromaticities of a color space's primaries as xy coordinates
* in xyY (Y is assumed to be 1).
*/
struct Primaries {
float2 r;
float2 g;
float2 b;
bool operator==(const Primaries& rhs) const noexcept {
return r == rhs.r && b == rhs.b && g == rhs.b;
}
};
//! Reference white for a color space, defined as the xy coordinates in the xyY space.
using WhitePoint = float2;
/**
* <p>Defines the parameters for the ICC parametric curve type 4, as
* defined in ICC.1:2004-10, section 10.15.</p>
*
* <p>The EOTF is of the form:</p>
*
* \(\begin{equation}
* Y = \begin{cases}c X + f & X \lt d \\\
* \left( a X + b \right) ^{g} + e & X \ge d \end{cases}
* \end{equation}\)
*
* <p>The corresponding OETF is simply the inverse function.</p>
*
* <p>The parameters defined by this class form a valid transfer
* function only if all the following conditions are met:</p>
* <ul>
* <li>No parameter is a NaN</li>
* <li>\(d\) is in the range \([0..1]\)</li>
* <li>The function is not constant</li>
* <li>The function is positive and increasing</li>
* </ul>
*/
struct TransferFunction {
/**
* <p>Defines the parameters for the ICC parametric curve type 3, as
* defined in ICC.1:2004-10, section 10.15.</p>
*
* <p>The EOTF is of the form:</p>
*
* \(\begin{equation}
* Y = \begin{cases}c X & X \lt d \\\
* \left( a X + b \right) ^{g} & X \ge d \end{cases}
* \end{equation}\)
*
* <p>This constructor is equivalent to setting \(e\) and \(f\) to 0.</p>
*
* @param a The value of \(a\) in the equation of the EOTF described above
* @param b The value of \(b\) in the equation of the EOTF described above
* @param c The value of \(c\) in the equation of the EOTF described above
* @param d The value of \(d\) in the equation of the EOTF described above
* @param g The value of \(g\) in the equation of the EOTF described above
*/
constexpr TransferFunction(
double a,
double b,
double c,
double d,
double e,
double f,
double g
) : a(a),
b(b),
c(c),
d(d),
e(e),
f(f),
g(g) {
}
constexpr TransferFunction(
double a,
double b,
double c,
double d,
double g
) : TransferFunction(a, b, c, d, 0.0, 0.0, g) {
}
bool operator==(const TransferFunction& rhs) const noexcept {
return
a == rhs.a &&
b == rhs.b &&
c == rhs.c &&
d == rhs.d &&
e == rhs.e &&
f == rhs.f &&
g == rhs.g;
}
double a;
double b;
double c;
double d;
double e;
double f;
double g;
};
/**
* <p>A color space in Filament is always an RGB color space. A specific RGB color space
* is defined by the following properties:</p>
* <ul>
* <li>Three chromaticities of the red, green and blue primaries, which
* define the gamut of the color space.</li>
* <li>A white point chromaticity that defines the stimulus to which
* color space values are normalized (also just called "white").</li>
* <li>An opto-electronic transfer function, also called opto-electronic
* conversion function or often, and approximately, gamma function.</li>
* <li>An electro-optical transfer function, also called electo-optical
* conversion function or often, and approximately, gamma function.</li>
* </ul>
*
* <h3>Primaries and white point chromaticities</h3>
* <p>In this implementation, the chromaticity of the primaries and the white
* point of an RGB color space is defined in the CIE xyY color space. This
* color space separates the chromaticity of a color, the x and y components,
* and its luminance, the Y component. Since the primaries and the white
* point have full brightness, the Y component is assumed to be 1 and only
* the x and y components are needed to encode them.</p>
*
* <h3>Transfer functions</h3>
* <p>A transfer function is a color component conversion function, defined as
* a single variable, monotonic mathematical function. It is applied to each
* individual component of a color. They are used to perform the mapping
* between linear tristimulus values and non-linear electronic signal value.</p>
* <p>The <em>opto-electronic transfer function</em> (OETF or OECF) encodes
* tristimulus values in a scene to a non-linear electronic signal value.</p>
*/
class ColorSpace {
public:
constexpr ColorSpace(
const Primaries primaries,
const TransferFunction transferFunction,
const WhitePoint whitePoint
) : mPrimaries(primaries),
mTransferFunction(transferFunction),
mWhitePoint(whitePoint) {
}
bool operator==(const ColorSpace& rhs) const noexcept {
return mPrimaries == rhs.mPrimaries &&
mTransferFunction == rhs.mTransferFunction &&
mWhitePoint == rhs.mWhitePoint;
}
constexpr const Primaries& getPrimaries() const { return mPrimaries; }
constexpr const TransferFunction& getTransferFunction() const { return mTransferFunction; }
constexpr const WhitePoint& getWhitePoint() const { return mWhitePoint; }
private:
Primaries mPrimaries;
TransferFunction mTransferFunction;
WhitePoint mWhitePoint;
};
/**
* Intermediate class used when building a color space using the "-" syntax:
*
* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
* // Declares a "linear sRGB" color space.
* ColorSpace myColorSpace = Rec709-Linear-sRGB;
* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
*/
class PartialColorSpace {
public:
constexpr ColorSpace operator-(const WhitePoint whitePoint) const {
return ColorSpace(mPrimaries, mTransferFunction, whitePoint);
}
private:
constexpr PartialColorSpace(
const Primaries primaries,
const TransferFunction transferFunction
) : mPrimaries(primaries),
mTransferFunction(transferFunction) {
}
Primaries mPrimaries;
TransferFunction mTransferFunction;
friend class Gamut;
};
/**
* Defines the chromaticities of the primaries for a color space. The chromaticities
* are expressed as three pairs of xy coordinates (in xyY) for the red, green, and blue
* chromaticities.
*/
class Gamut {
public:
constexpr Gamut(const Primaries primaries) : mPrimaries(primaries) {
}
constexpr Gamut(float2 r, float2 g, float2 b) : Gamut(Primaries{r, g, b}) {
}
constexpr PartialColorSpace operator-(const TransferFunction transferFunction) const {
return PartialColorSpace(mPrimaries, transferFunction);
}
constexpr const Primaries& getPrimaries() const { return mPrimaries; }
private:
Primaries mPrimaries;
};
//! Rec.709 color gamut, used in the sRGB and DisplayP3 color spaces.
constexpr Gamut Rec709 = Gamut({0.640f, 0.330f}, {0.300f, 0.600f}, {0.150f, 0.060f});
//! Linear transfer function.
constexpr TransferFunction Linear = TransferFunction(1.0, 0.0, 0.0, 0.0, 1.0);
//! sRGB transfer function.
constexpr TransferFunction sRGB =
TransferFunction(1.0 / 1.055, 0.055 / 1.055, 1.0 / 12.92, 0.04045, 2.4);
//! Standard CIE 1931 2° illuminant D65. This illuminant has a color temperature of 6504K.
constexpr WhitePoint D65 = WhitePoint({0.31271f, 0.32902f});
} // namespace color
} // namespace filament
#endif // TNT_FILAMENT_COLOR_SPACE_H
+8
View File
@@ -340,6 +340,14 @@ public:
* @see View::setShadowType
*/
struct Vsm {
/**
* The number of MSAA samples to use when rendering VSM shadow maps.
* Must be a power-of-two and greater than or equal to 1. A value of 1 effectively turns
* off MSAA.
* Higher values may not be available depending on the underlying hardware.
*/
uint8_t msaaSamples = 1;
/**
* When elvsm is set to true, "Exponential Layered VSM without Layers" are used. It is
* an improvement to the default EVSM which suffers important light leaks. Enabling
-8
View File
@@ -390,14 +390,6 @@ struct VsmShadowOptions {
*/
bool mipmapping = false;
/**
* The number of MSAA samples to use when rendering VSM shadow maps.
* Must be a power-of-two and greater than or equal to 1. A value of 1 effectively turns
* off MSAA.
* Higher values may not be available depending on the underlying hardware.
*/
uint8_t msaaSamples = 1;
/**
* Whether to use a 32-bits or 16-bits texture format for VSM shadow maps. 32-bits
* precision is rarely needed, but it does reduces light leaks as well as "fading"
+1 -23
View File
@@ -117,37 +117,15 @@ public:
* ClearOptions are used at the beginning of a frame to clear or retain the SwapChain content.
*/
struct ClearOptions {
/**
* Color (sRGB linear) to use to clear the RenderTarget (typically the SwapChain).
*
* The RenderTarget is cleared using this color, which won't be tone-mapped since
* tone-mapping is part of View rendering (this is not).
*
* When a View is rendered, there are 3 scenarios to consider:
* - Pixels rendered by the View replace the clear color (or blend with it in
* `BlendMode::TRANSLUCENT` mode).
*
* - With blending mode set to `BlendMode::TRANSLUCENT`, Pixels untouched by the View
* are considered fulling transparent and let the clear color show through.
*
* - With blending mode set to `BlendMode::OPAQUE`, Pixels untouched by the View
* are set to the clear color. However, because it is now used in the context of a View,
* it will go through the post-processing stage, which includes tone-mapping.
*
* For consistency, it is recommended to always use a Skybox to clear an opaque View's
* background, or to use black or fully-transparent (i.e. {0,0,0,0}) as the clear color.
*/
/** Color to use to clear the SwapChain */
math::float4 clearColor = {};
/** Value to clear the stencil buffer */
uint8_t clearStencil = 0u;
/**
* Whether the SwapChain should be cleared using the clearColor. Use this if translucent
* View will be drawn, for instance.
*/
bool clear = false;
/**
* Whether the SwapChain content should be discarded. clear implies discard. Set this
* to false (along with clear to false as well) if the SwapChain already has content that
+9 -11
View File
@@ -80,7 +80,14 @@ public:
*
* @return The associated Skybox, or nullptr if there is none.
*/
Skybox* getSkybox() const noexcept;
Skybox* getSkybox() noexcept;
/**
* Returns an immutable Skybox associated with the Scene.
*
* @return The associated Skybox, or nullptr if there is none.
*/
Skybox const* getSkybox() const noexcept;
/**
* Set the IndirectLight to use when rendering the Scene.
@@ -89,17 +96,8 @@ public:
* IndirectLight.
*
* @param ibl The IndirectLight to use when rendering the Scene or nullptr to unset.
* @see getIndirectLight
*/
void setIndirectLight(IndirectLight* ibl) noexcept;
/**
* Get the IndirectLight or nullptr if none is set.
*
* @return the the IndirectLight or nullptr if none is set
* @see setIndirectLight
*/
IndirectLight* getIndirectLight() const noexcept;
void setIndirectLight(IndirectLight const* ibl) noexcept;
/**
* Adds an Entity to the Scene.
+19 -35
View File
@@ -35,8 +35,8 @@ AtlasAllocator::AtlasAllocator(size_t maxTextureSize) noexcept {
mMaxTextureSizePot = (sizeof(maxTextureSize) * 8 - 1u) - utils::clz(maxTextureSize);
}
AtlasAllocator::Allocation AtlasAllocator::allocate(size_t textureSize) noexcept {
Allocation result{};
Viewport AtlasAllocator::allocate(size_t textureSize) noexcept {
Viewport result{};
const size_t powerOfTwo = (sizeof(textureSize) * 8 - 1u) - utils::clz(textureSize);
// asked for a texture size too large
@@ -45,24 +45,22 @@ AtlasAllocator::Allocation AtlasAllocator::allocate(size_t textureSize) noexcept
}
// asked for a texture size too small
if (UTILS_UNLIKELY(mMaxTextureSizePot - powerOfTwo >= QUAD_TREE_DEPTH)) {
if (UTILS_UNLIKELY(mMaxTextureSizePot - powerOfTwo >= QuadTree::height())) {
return result;
}
const size_t layer = (mMaxTextureSizePot - powerOfTwo);
const NodeId loc = allocateInLayer(LAYERS_DEPTH + layer);
const size_t layer = mMaxTextureSizePot - powerOfTwo;
const NodeId loc = allocateInLayer(layer);
if (loc.l >= 0) {
assert_invariant(loc.l - LAYERS_DEPTH == int8_t(layer));
const size_t dimension = 1u << powerOfTwo;
assert_invariant(loc.l == int8_t(layer));
size_t dimension = 1u << powerOfTwo;
// find the location of in the texture from the morton code (quadtree position)
const auto [x, y] = unmorton(loc.code);
auto [x, y] = unmorton(loc.code);
// scale to our maximum allocation size
const uint32_t mask = (1u << layer) - 1u;
result.viewport.left = int32_t(x & mask) << powerOfTwo;
result.viewport.bottom = int32_t(y & mask) << powerOfTwo;
result.viewport.width = dimension;
result.viewport.height = dimension;
result.layer = loc.code >> (2 * layer);
result.left = int32_t(x) << powerOfTwo;
result.bottom = int32_t(y) << powerOfTwo;
result.width = dimension;
result.height = dimension;
}
return result;
}
@@ -110,9 +108,9 @@ AtlasAllocator::NodeId AtlasAllocator::allocateInLayer(size_t maxHeight) noexcep
}
// We only want to find a fitting node that already has siblings, in order
// to accomplish a "best fit" allocation. So if a parent (of what we're looking for)
// has no children, we skip the whole tree below it.
if (!node.hasChildren()) {
// to accomplish a "best fit" allocation. So if we're a parent of a 'fitting'
// node and don't have children, we skip the children recursion.
if (curr.l == n - 1 && !node.hasChildren()) {
return QuadTree::TraversalResult::SKIP_CHILDREN;
}
@@ -123,8 +121,6 @@ AtlasAllocator::NodeId AtlasAllocator::allocateInLayer(size_t maxHeight) noexcep
if (candidate.l >= 0) {
const size_t i = index(candidate.l, candidate.code);
Node& allocation = mQuadTree[i];
assert_invariant(!allocation.isAllocated());
assert_invariant(!allocation.hasChildren());
if (candidate.l == n) {
allocation.allocated = true;
@@ -136,38 +132,26 @@ AtlasAllocator::NodeId AtlasAllocator::allocateInLayer(size_t maxHeight) noexcep
assert_invariant(parent.hasChildren());
assert_invariant(!parent.hasAllChildren());
parent.children++;
#ifndef NDEBUG
// check that all the parents are not allocated and have at least 1 child.
NodeId ppp = candidate;
while (ppp.l > 0) {
const size_t pi = QuadTreeUtils::parent(ppp.l, ppp.code);
ppp = NodeId{ int8_t(ppp.l - 1), uint8_t(ppp.code >> 2) };
Node& node = mQuadTree[pi];
assert_invariant(!node.isAllocated());
assert_invariant(node.hasChildren());
}
#endif
}
} else if (candidate.l < int8_t(QuadTree::height())) {
// we need to create the hierarchy down to the level we need
NodeId found{ -1, 0 };
assert_invariant(!allocation.isAllocated());
assert_invariant(!allocation.hasChildren());
NodeId found{};
QuadTree::traverse(candidate.l, candidate.code,
[this, n, &found](NodeId const& curr) -> QuadTree::TraversalResult {
size_t i = index(curr.l, curr.code);
Node& node = mQuadTree[i];
if (curr.l == n) {
found = curr;
assert_invariant(!node.hasChildren());
node.allocated = true;
return QuadTree::TraversalResult::EXIT;
}
assert_invariant(!node.hasAllChildren());
node.children++;
return QuadTree::TraversalResult::RECURSE;
});
assert_invariant(found.l != -1);
candidate = found;
}
}
+3 -19
View File
@@ -21,8 +21,6 @@
#include <filament/Viewport.h>
#include <private/filament/EngineEnums.h>
class AtlasAllocator_AllocateFirstLevel_Test;
class AtlasAllocator_AllocateSecondLevel_Test;
class AtlasAllocator_AllocateMixed0_Test;
@@ -55,18 +53,8 @@ class AtlasAllocator {
uint8_t children : 3; // 0, 1, 2, 3, 4
};
// this determines the number of layers we can use (3 layers == 64 quadtree entries)
static constexpr size_t LAYERS_DEPTH = 3u;
// this determines how many "sub-sizes" we can have from the base size.
// e.g. with a max texture size of 1024, we can allocate 1024, 512, 256 and 128 textures.
static constexpr size_t QUAD_TREE_DEPTH = 4u;
// LAYERS_DEPTH limits the number of layers
static_assert(CONFIG_MAX_SHADOW_LAYERS <= 1u << (LAYERS_DEPTH * 2u));
// QuadTreeArray is limited to a maximum depth of 7
using QuadTree = utils::QuadTreeArray<Node, LAYERS_DEPTH + QUAD_TREE_DEPTH>;
using QuadTree = utils::QuadTreeArray<Node, QUAD_TREE_DEPTH>;
using NodeId = QuadTree::NodeId;
public:
@@ -74,18 +62,14 @@ public:
* Create allocator and specify the maximum texture size. Must be a power of two.
* Allocations size allowed are the four power-of-two smaller or equal to this size.
*/
explicit AtlasAllocator(size_t maxTextureSize) noexcept;
explicit AtlasAllocator(size_t maxTextureSize = 1024) noexcept;
/*
* Allocates a square of size `textureSize`. Must be one of the power-of-two allowed
* (see above).
* Returns the location of the allocation within the maxTextureSize^2 square.
*/
struct Allocation {
int32_t layer = -1;
Viewport viewport;
};
Allocation allocate(size_t textureSize) noexcept;
Viewport allocate(size_t textureSize) noexcept;
/*
* Frees all allocations and reset the maximum texture size.
+1
View File
@@ -92,6 +92,7 @@ private:
template<size_t POOL_SIZE, size_t ALIGNMENT, typename AllocatorPolicy, typename LockingPolicy>
BufferPoolAllocator<POOL_SIZE, ALIGNMENT, AllocatorPolicy, LockingPolicy>::~BufferPoolAllocator() noexcept {
assert_invariant(mOutstandingBuffers == mEntries.size());
clearInternal();
}
+1 -1
View File
@@ -16,7 +16,7 @@
#include <filament/Color.h>
#include "ColorSpaceUtils.h"
#include "ColorSpace.h"
#include <math/mat3.h>
#include <math/scalar.h>
@@ -14,7 +14,7 @@
* limitations under the License.
*/
#include "ColorSpaceUtils.h"
#include "ColorSpace.h"
namespace filament {
@@ -296,10 +296,6 @@ inline float3 linearAP1_to_ACEScct(float3 x) noexcept {
return x;
}
inline float3 OETF_Linear(float3 x) noexcept {
return x;
}
inline float3 OETF_sRGB(float3 x) noexcept {
constexpr float a = 0.055f;
constexpr float a1 = 1.055f;
+46 -5
View File
@@ -79,12 +79,14 @@ void PerShadowMapUniforms::prepareLodBias(Transaction const& transaction,
}
void PerShadowMapUniforms::prepareViewport(Transaction const& transaction,
backend::Viewport const& viewport) noexcept {
float2 const dimensions{ viewport.width, viewport.height };
const filament::Viewport& viewport,
uint32_t xoffset, uint32_t yoffset) noexcept {
const float w = float(viewport.width);
const float h = float(viewport.height);
auto& s = edit(transaction);
s.resolution = { dimensions, 1.0f / dimensions };
s.logicalViewportScale = 1.0f;
s.logicalViewportOffset = 0.0f;
s.resolution = float4{ w, h, 1.0f / w, 1.0f / h };
s.origin = float2{ viewport.left, viewport.bottom };
s.offset = float2{ xoffset, yoffset };
}
void PerShadowMapUniforms::prepareTime(Transaction const& transaction,
@@ -96,6 +98,45 @@ void PerShadowMapUniforms::prepareTime(Transaction const& transaction,
s.userTime = userTime;
}
void PerShadowMapUniforms::prepareDirectionalLight(Transaction const& transaction,
FEngine& engine,
float exposure,
float3 const& sceneSpaceDirection,
PerShadowMapUniforms::LightManagerInstance directionalLight) noexcept {
FLightManager& lcm = engine.getLightManager();
auto& s = edit(transaction);
const float3 l = -sceneSpaceDirection; // guaranteed normalized
if (directionalLight.isValid()) {
const float4 colorIntensity = {
lcm.getColor(directionalLight), lcm.getIntensity(directionalLight) * exposure };
s.lightDirection = l;
s.lightColorIntensity = colorIntensity;
s.lightChannels = lcm.getLightChannels(directionalLight);
const bool isSun = lcm.isSunLight(directionalLight);
// The last parameter must be < 0.0f for regular directional lights
float4 sun{ 0.0f, 0.0f, 0.0f, -1.0f };
if (UTILS_UNLIKELY(isSun && colorIntensity.w > 0.0f)) {
// currently we have only a single directional light, so it's probably likely that it's
// also the Sun. However, conceptually, most directional lights won't be sun lights.
float radius = lcm.getSunAngularRadius(directionalLight);
float haloSize = lcm.getSunHaloSize(directionalLight);
float haloFalloff = lcm.getSunHaloFalloff(directionalLight);
sun.x = std::cos(radius);
sun.y = std::sin(radius);
sun.z = 1.0f / (std::cos(radius * haloSize) - sun.x);
sun.w = haloFalloff;
}
s.sun = sun;
} else {
// Disable the sun if there's no directional light
s.sun = float4{ 0.0f, 0.0f, 0.0f, -1.0f };
}
}
void PerShadowMapUniforms::prepareShadowMapping(Transaction const& transaction,
bool highPrecision) noexcept {
auto& s = edit(transaction);
+5 -1
View File
@@ -65,7 +65,7 @@ public:
float bias) noexcept;
static void prepareViewport(Transaction const& transaction,
backend::Viewport const& viewport) noexcept;
const filament::Viewport& viewport, uint32_t xoffset, uint32_t yoffset) noexcept;
static void prepareTime(Transaction const& transaction,
FEngine& engine, math::float4 const& userTime) noexcept;
@@ -73,6 +73,10 @@ public:
static void prepareShadowMapping(Transaction const& transaction,
bool highPrecision) noexcept;
static void prepareDirectionalLight(Transaction const& transaction,
FEngine& engine, float exposure,
math::float3 const& sceneSpaceDirection, LightManagerInstance instance) noexcept;
static Transaction open(backend::DriverApi& driver) noexcept;
// update local data into GPU UBO
+11 -10
View File
@@ -100,17 +100,14 @@ void PerViewUniforms::prepareExposure(float ev100) noexcept {
s.ev100 = ev100;
}
void PerViewUniforms::prepareViewport(
const filament::Viewport& physicalViewport,
const filament::Viewport& logicalViewport) noexcept {
float4 const physical{ physicalViewport.left, physicalViewport.bottom,
physicalViewport.width, physicalViewport.height };
float4 const logical{ logicalViewport.left, logicalViewport.bottom,
logicalViewport.width, logicalViewport.height };
void PerViewUniforms::prepareViewport(const filament::Viewport& viewport,
uint32_t xoffset, uint32_t yoffset) noexcept {
const float w = float(viewport.width);
const float h = float(viewport.height);
auto& s = mUniforms.edit();
s.resolution = { physical.zw, 1.0f / physical.zw };
s.logicalViewportScale = physical.zw / logical.zw;
s.logicalViewportOffset = -logical.xy / logical.zw;
s.resolution = float4{ w, h, 1.0f / w, 1.0f / h };
s.origin = float2{ viewport.left, viewport.bottom };
s.offset = float2{ xoffset, yoffset };
}
void PerViewUniforms::prepareTime(FEngine& engine, math::float4 const& userTime) noexcept {
@@ -293,10 +290,14 @@ void PerViewUniforms::prepareShadowMapping(bool highPrecision) noexcept {
void PerViewUniforms::prepareShadowSampling(PerViewUib& uniforms,
ShadowMappingUniforms const& shadowMappingUniforms) noexcept {
uniforms.lightFromWorldMatrix = shadowMappingUniforms.lightFromWorldMatrix;
uniforms.cascadeSplits = shadowMappingUniforms.cascadeSplits;
uniforms.shadowBulbRadiusLs = shadowMappingUniforms.shadowBulbRadiusLs;
uniforms.shadowBias = shadowMappingUniforms.shadowBias;
uniforms.ssContactShadowDistance = shadowMappingUniforms.ssContactShadowDistance;
uniforms.directionalShadows = shadowMappingUniforms.directionalShadows;
uniforms.cascades = shadowMappingUniforms.cascades;
uniforms.cascades |= uint32_t(shadowMappingUniforms.elvsm) << 31u;
}
void PerViewUniforms::prepareShadowVSM(Handle<HwTexture> texture,
+1 -3
View File
@@ -78,9 +78,7 @@ public:
* @param yoffset vertical rendering offset *within* the viewport.
* Non-zero when we have guard bands.
*/
void prepareViewport(
const filament::Viewport& physicalViewport,
const filament::Viewport& logicalViewport) noexcept;
void prepareViewport(const filament::Viewport& viewport, uint32_t xoffset, uint32_t yoffset) noexcept;
void prepareTime(FEngine& engine, math::float4 const& userTime) noexcept;
void prepareTemporalNoise(FEngine& engine, TemporalAntiAliasingOptions const& options) noexcept;
-10
View File
@@ -707,11 +707,6 @@ void RenderPass::Executor::overrideScissor(backend::Viewport const* scissor) noe
}
}
void RenderPass::Executor::overrideScissor(backend::Viewport const& scissor) noexcept {
mScissorOverride = true;
mScissor = scissor;
}
void RenderPass::Executor::execute(FEngine& engine, const char* name) const noexcept {
execute(engine.getDriverApi(), mCommands.begin(), mCommands.end());
}
@@ -755,11 +750,6 @@ void RenderPass::Executor::execute(backend::DriverApi& driver,
continue;
}
// primitiveHandle may be invalid if no geometry was set on the renderable.
if (UTILS_UNLIKELY(!first->primitive.primitiveHandle)) {
continue;
}
// per-renderable uniform
const PrimitiveInfo info = first->primitive;
pipeline.rasterState = info.rasterState;
-1
View File
@@ -349,7 +349,6 @@ public:
// if non-null, overrides the material's scissor
void overrideScissor(backend::Viewport const* scissor) noexcept;
void overrideScissor(backend::Viewport const& scissor) noexcept;
void execute(FEngine& engine, const char* name) const noexcept;
};
+21 -22
View File
@@ -47,6 +47,8 @@ FrameGraphId<FrameGraphTexture> RendererUtils::colorPass(
FrameGraphId<FrameGraphTexture> ssao;
FrameGraphId<FrameGraphTexture> ssr; // either screen-space reflections or refractions
FrameGraphId<FrameGraphTexture> structure;
float4 clearColor{};
TargetBufferFlags clearFlags{};
};
Blackboard& blackboard = fg.getBlackboard();
@@ -54,8 +56,8 @@ FrameGraphId<FrameGraphTexture> RendererUtils::colorPass(
auto& colorPass = fg.addPass<ColorPassData>(name,
[&](FrameGraph::Builder& builder, ColorPassData& data) {
TargetBufferFlags const clearColorFlags = config.clearFlags & TargetBufferFlags::COLOR;
TargetBufferFlags clearDepthFlags = config.clearFlags & TargetBufferFlags::DEPTH;
TargetBufferFlags clearColorFlags = config.clearFlags & TargetBufferFlags::COLOR;
TargetBufferFlags clearStencilFlags = config.clearFlags & TargetBufferFlags::STENCIL;
data.shadows = blackboard.get<FrameGraphTexture>("shadows");
@@ -98,7 +100,7 @@ FrameGraphId<FrameGraphTexture> RendererUtils::colorPass(
TargetBufferFlags::STENCIL : TargetBufferFlags::NONE;
const char* const name = config.enabledStencilBuffer ?
"Depth/Stencil Buffer" : "Depth Buffer";
TextureFormat const format = config.enabledStencilBuffer ?
TextureFormat format = config.enabledStencilBuffer ?
TextureFormat::DEPTH32F_STENCIL8 : TextureFormat::DEPTH32F;
data.depth = builder.createTexture(name, {
.width = colorBufferDesc.width,
@@ -153,11 +155,14 @@ FrameGraphId<FrameGraphTexture> RendererUtils::colorPass(
*/
builder.declareRenderPass("Color Pass Target", {
.attachments = { .color = { data.color, data.output },
.depth = data.depth,
.stencil = data.stencil },
.clearColor = config.clearColor,
.samples = config.msaa,
.depth = data.depth,
.stencil = data.stencil },
.samples = config.msaa,
.clearFlags = clearColorFlags | clearDepthFlags | clearStencilFlags });
data.clearColor = config.clearColor;
data.clearFlags = clearColorFlags | clearDepthFlags | clearStencilFlags;
blackboard["depth"] = data.depth;
},
[=, &view, &engine](FrameGraphResources const& resources,
@@ -179,7 +184,7 @@ FrameGraphId<FrameGraphTexture> RendererUtils::colorPass(
resources.getTexture(data.structure) : engine.getOneTexture());
// set screen-space reflections and screen-space refractions
TextureHandle const ssr = data.ssr ?
TextureHandle ssr = data.ssr ?
resources.getTexture(data.ssr) : engine.getOneTextureArray();
view.prepareSSR(ssr, config.ssrLodOffset,
@@ -194,12 +199,13 @@ FrameGraphId<FrameGraphTexture> RendererUtils::colorPass(
out.params.viewport.height == resources.getDescriptor(data.color).height);
view.prepareViewport(static_cast<filament::Viewport&>(out.params.viewport),
config.logicalViewport);
config.xoffset, config.yoffset);
view.commitUniforms(driver);
// TODO: this should be a parameter of FrameGraphRenderPass::Descriptor
out.params.clearColor = data.clearColor;
out.params.clearStencil = config.clearStencil;
out.params.flags.clear = data.clearFlags;
if (view.getBlendMode() == BlendMode::TRANSLUCENT) {
if (any(out.params.flags.discardStart & TargetBufferFlags::COLOR0)) {
// if the buffer is discarded (e.g. it's new) and we're blending,
@@ -235,7 +241,7 @@ FrameGraphId<FrameGraphTexture> RendererUtils::colorPass(
return output;
}
std::pair<FrameGraphId<FrameGraphTexture>, bool> RendererUtils::refractionPass(
FrameGraphId<FrameGraphTexture> RendererUtils::refractionPass(
FrameGraph& fg, FEngine& engine, FView const& view,
ColorPassConfig config,
PostProcessManager::ScreenSpaceRefConfig const& ssrConfig,
@@ -269,8 +275,8 @@ std::pair<FrameGraphId<FrameGraphTexture>, bool> RendererUtils::refractionPass(
input = RendererUtils::colorPass(fg, "Color Pass (opaque)", engine, view, {
// When rendering the opaques, we need to conserve the sample buffer,
// so create config that specifies the sample count.
.width = config.physicalViewport.width,
.height = config.physicalViewport.height,
.width = config.width,
.height = config.height,
.samples = config.msaa,
.format = config.hdrFormat
}, config, { .asSubpass = false },
@@ -285,16 +291,9 @@ std::pair<FrameGraphId<FrameGraphTexture>, bool> RendererUtils::refractionPass(
// automatically because these are set in the Blackboard (they were set by the opaque
// pass). For this reason, `desc` below is only used in colorPass() for the width and
// height.
// Since we're reusing the existing target we don't want to clear any of its buffer.
// Important: if this target ended up being an imported target, then the clearFlags
// specified here wouldn't apply (the clearFlags of the imported target take precedence),
// and we'd end up clearing the opaque pass. This scenario never happens because it is
// prevented in Renderer.cpp's final blit.
config.clearFlags = TargetBufferFlags::NONE;
output = RendererUtils::colorPass(fg, "Color Pass (transparent)", engine, view, {
.width = config.physicalViewport.width,
.height = config.physicalViewport.height },
output = RendererUtils::colorPass(fg, "Color Pass (transparent)", engine, view,
{ .width = config.width, .height = config.height },
config, colorGradingConfig, pass.getExecutor(refraction, pass.end()));
if (config.msaa > 1 && !colorGradingConfig.asSubpass) {
@@ -307,7 +306,7 @@ std::pair<FrameGraphId<FrameGraphTexture>, bool> RendererUtils::refractionPass(
} else {
output = input;
}
return { output, hasScreenSpaceRefraction };
return output;
}
UTILS_NOINLINE
+9 -8
View File
@@ -29,8 +29,6 @@
#include <stdint.h>
#include <utility>
namespace filament {
class FRenderTarget;
@@ -42,11 +40,14 @@ class RendererUtils {
public:
struct ColorPassConfig {
// Rendering viewport (e.g. scaled down viewport from dynamic resolution)
Viewport physicalViewport;
// Logical viewport (e.g. left-bottom non-zero when we have guard bands), origin
// relative to physicalViewport
Viewport logicalViewport;
// Rendering viewport width (e.g. scaled down viewport from dynamic resolution)
uint32_t width;
// Rendering viewport height (e.g. scaled down viewport from dynamic resolution)
uint32_t height;
// Rendering offset within the viewport (e.g. non-zero when we have guard bands)
uint32_t xoffset;
// Rendering offset within the viewport (e.g. non-zero when we have guard bands)
uint32_t yoffset;
// dynamic resolution scale
math::float2 scale;
// HDR format
@@ -76,7 +77,7 @@ public:
PostProcessManager::ColorGradingConfig colorGradingConfig,
RenderPass::Executor const& passExecutor) noexcept;
static std::pair<FrameGraphId<FrameGraphTexture>, bool> refractionPass(
static FrameGraphId<FrameGraphTexture> refractionPass(
FrameGraph& fg, FEngine& engine, FView const& view,
ColorPassConfig config,
PostProcessManager::ScreenSpaceRefConfig const& ssrConfig,
+5 -5
View File
@@ -27,16 +27,16 @@ void Scene::setSkybox(Skybox* skybox) noexcept {
downcast(this)->setSkybox(downcast(skybox));
}
Skybox* Scene::getSkybox() const noexcept {
Skybox* Scene::getSkybox() noexcept {
return downcast(this)->getSkybox();
}
void Scene::setIndirectLight(IndirectLight* ibl) noexcept {
downcast(this)->setIndirectLight(downcast(ibl));
Skybox const* Scene::getSkybox() const noexcept {
return downcast(this)->getSkybox();
}
IndirectLight* Scene::getIndirectLight() const noexcept {
return downcast(this)->getIndirectLight();
void Scene::setIndirectLight(IndirectLight const* ibl) noexcept {
downcast(this)->setIndirectLight(downcast(ibl));
}
void Scene::addEntity(Entity entity) {
+158 -136
View File
@@ -78,19 +78,7 @@ void ShadowMap::initialize(size_t lightIndex, ShadowType shadowType,
}
mat4f ShadowMap::getDirectionalLightViewMatrix(float3 direction, float3 position) noexcept {
auto z_axis = direction;
auto norm_up = float3{ 0, 1, 0 };
if (UTILS_UNLIKELY(std::abs(dot(z_axis, norm_up)) > 0.999f)) {
// Fix up vector if we're degenerate (looking straight up, basically)
norm_up = { norm_up.z, norm_up.x, norm_up.y };
}
auto x_axis = normalize(cross(z_axis, norm_up));
auto y_axis = cross(x_axis, z_axis);
const mat4f Mm{
float4{ x_axis, 0 },
float4{ y_axis, 0 },
float4{ -z_axis, 0 },
float4{ position, 1 }};
const mat4f Mm = mat4f::lookAt(position, position + direction, float3{ 0, 1, 0 });
return FCamera::rigidTransformInverse(Mm);
}
@@ -380,9 +368,8 @@ ShadowMap::ShaderParameters ShadowMap::updateDirectional(FEngine& engine,
// 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 backend::Viewport viewport = getViewport();
const auto [Mt, Mn] = ShadowMap::getTextureCoordsMapping(shadowMapInfo, viewport);
const mat4f St = math::highPrecisionMultiply(Mt, S);
const mat4 MbMt = getTextureCoordsMapping(shadowMapInfo);
const mat4f St = mat4f(MbMt * S);
ShadowMap::ShaderParameters shaderParameters;
@@ -390,7 +377,7 @@ ShadowMap::ShaderParameters ShadowMap::updateDirectional(FEngine& engine,
// L * Mp * Mv is a rigid transform for directional lights, and doesn't matter.
// if Wp[3][1] is 0, then LISPSM was cancelled.
if (useLispsm && Wp[3][1] != 0.0f) {
shaderParameters.texelSizeAtOneMeterWs = texelSizeWorldSpace(Wp, mat4f(Mt * F),
shaderParameters.texelSizeAtOneMeterWs = texelSizeWorldSpace(Wp, mat4f(MbMt * F),
shadowMapInfo.shadowDimension);
} else {
// We know we're using an ortho projection
@@ -403,8 +390,6 @@ ShadowMap::ShaderParameters ShadowMap::updateDirectional(FEngine& engine,
shaderParameters.lightSpace = computeVsmLightSpaceMatrix(St, Mv, znear, zfar);
}
shaderParameters.scissorNormalized = getViewportNormalized(shadowMapInfo);
// We apply the constant bias in world space (as opposed to light-space) to account
// for perspective and lispsm shadow maps. This also allows us to do this at zero-cost
// by baking it in the shadow-map itself.
@@ -419,8 +404,8 @@ ShadowMap::ShaderParameters ShadowMap::updateDirectional(FEngine& engine,
// The model matrix below is in fact inverted to get the view matrix and passed to the
// shader as 'viewFromWorldMatrix', and is used in the VSM case to compute the depth metric.
// (see depth_main.fs). Note that in the case of VSM, 'b' below is identity.
mCamera->setModelMatrix(FCamera::rigidTransformInverse(math::highPrecisionMultiplyd(Mv, b)));
mCamera->setCustomProjection(mat4(Mn * F * W * L * Mp), znear, zfar);
mCamera->setModelMatrix(mat4{ FCamera::rigidTransformInverse(Mv * b) });
mCamera->setCustomProjection(mat4(F * W * L * Mp), znear, zfar);
// for the debug camera, we need to undo the world origin
mDebugCamera->setCustomProjection(mat4(S * b * camera.worldOrigin), znear, zfar);
@@ -428,18 +413,48 @@ ShadowMap::ShaderParameters ShadowMap::updateDirectional(FEngine& engine,
return shaderParameters;
}
ShadowMap::ShaderParameters ShadowMap::updatePunctual(
mat4f const& Mv, float outerConeAngle, float nearPlane, float farPlane,
const ShadowMapInfo& shadowMapInfo, const FLightManager::ShadowParams& params) noexcept {
const mat4f Mp = mat4f::perspective(outerConeAngle * f::RAD_TO_DEG * 2.0f, 1.0f, nearPlane, farPlane);
ShadowMap::ShaderParameters ShadowMap::updateSpot(FEngine& engine,
const FScene::LightSoa& lightData, size_t index,
filament::CameraInfo const& camera,
const ShadowMapInfo& shadowMapInfo,
FScene const& scene, SceneInfo sceneInfo) noexcept {
assert_invariant(shadowMapInfo.textureDimension == mOptions->mapSize);
ShaderParameters shaderParameters;
auto& lcm = engine.getLightManager();
auto li = lightData.elementAt<FScene::LIGHT_INSTANCE>(index);
auto position = lightData.elementAt<FScene::POSITION_RADIUS>(index).xyz;
auto direction = lightData.elementAt<FScene::DIRECTION>(index);
auto radius = lightData.elementAt<FScene::POSITION_RADIUS>(index).w;
auto outerConeAngle = lcm.getSpotLightOuterCone(li);
const FLightManager::ShadowParams& params = lcm.getShadowParams(li);
/*
* Compute the light model matrix.
*/
// Choose a reasonable value for the near plane.
const mat4f Mv = getDirectionalLightViewMatrix(direction, position);
// find decent near/far
ShadowMap::updateSceneInfoSpot(Mv, scene, sceneInfo);
// if the scene was empty, near > far
mHasVisibleShadows = -sceneInfo.lsNearFar[0] < -sceneInfo.lsNearFar[1];
// FIXME: we need a configuration for minimum near plane (for now hardcoded to 1cm)
float nearPlane = std::max(0.01f, -sceneInfo.lsNearFar[0]);
float farPlane = std::min(radius, -sceneInfo.lsNearFar[1]);
float outerConeAngleDegrees = outerConeAngle * f::RAD_TO_DEG;
const mat4f Mp = mat4f::perspective(outerConeAngleDegrees * 2.0f, 1.0f, nearPlane, farPlane);
const mat4f MpMv(math::highPrecisionMultiply(Mp, Mv));
// Final shadow transform
const backend::Viewport viewport = getViewport();
const mat4f S = math::highPrecisionMultiply(Mp, Mv);
const auto [Mt, Mn] = ShadowMap::getTextureCoordsMapping(shadowMapInfo, viewport);
const mat4f St = math::highPrecisionMultiply(Mt, S);
const mat4f S = MpMv;
const mat4 MbMt = getTextureCoordsMapping(shadowMapInfo);
const mat4f St = mat4f(MbMt * S);
// TODO: focus projection
// 1) focus on the casters
@@ -454,9 +469,7 @@ ShadowMap::ShaderParameters ShadowMap::updatePunctual(
// = zInLightSpace * texelSizeAtOneMeter
// = zInLightSpace * (2*tan(halfConeAngle)/dimension)
// Note: this would not work with LISPSM, which warps the texture space.
ShaderParameters shaderParameters;
shaderParameters.texelSizeAtOneMeterWs =
(2.0f * std::tan(outerConeAngle) / float(shadowMapInfo.shadowDimension));
shaderParameters.texelSizeAtOneMeterWs = (2.0f * std::tan(outerConeAngle) / float(shadowMapInfo.shadowDimension));
shaderParameters.lightFromWorldZ = -transpose(Mv)[2]; // negate because camera looks in -Z
if (!shadowMapInfo.vsm) {
@@ -465,11 +478,9 @@ ShadowMap::ShaderParameters ShadowMap::updatePunctual(
shaderParameters.lightSpace = computeVsmLightSpaceMatrix(St, Mv, nearPlane, farPlane);
}
shaderParameters.scissorNormalized = getViewportNormalized(shadowMapInfo);
const float3 direction = -transpose(Mv)[2].xyz;
const float constantBias = shadowMapInfo.vsm ? 0.0f : params.options.constantBias;
const mat4f b = mat4f::translation(direction * constantBias);
const mat4f Sb = S * b;
// It's important to set the light camera's model matrix separately from its projection, so that
// the cameraPosition uniform gets set correctly.
@@ -479,47 +490,22 @@ ShadowMap::ShaderParameters ShadowMap::updatePunctual(
// The model matrix below is in fact inverted to get the view matrix and passed to the
// shader as 'viewFromWorldMatrix', and is used in the VSM case to compute the depth metric.
// (see depth_main.fs). Note that in the case of VSM, 'b' below is identity.
mCamera->setModelMatrix(FCamera::rigidTransformInverse(math::highPrecisionMultiplyd(Mv, b)));
mCamera->setCustomProjection(math::highPrecisionMultiplyd(Mn, Mp), nearPlane, farPlane);
mCamera->setModelMatrix(mat4{ FCamera::rigidTransformInverse(Mv * b) });
mCamera->setCustomProjection(mat4(Mp), nearPlane, farPlane);
// for the debug camera, we need to undo the world origin
mDebugCamera->setCustomProjection(mat4(Sb * camera.worldOrigin), nearPlane, radius);
return shaderParameters;
}
ShadowMap::ShaderParameters ShadowMap::updateSpot(FEngine& engine,
const FScene::LightSoa& lightData, size_t index,
filament::CameraInfo const& camera,
const ShadowMapInfo& shadowMapInfo,
FScene const& scene, SceneInfo sceneInfo) noexcept {
auto& lcm = engine.getLightManager();
auto position = lightData.elementAt<FScene::POSITION_RADIUS>(index).xyz;
auto direction = lightData.elementAt<FScene::DIRECTION>(index);
auto radius = lightData.elementAt<FScene::POSITION_RADIUS>(index).w;
auto li = lightData.elementAt<FScene::LIGHT_INSTANCE>(index);
const FLightManager::ShadowParams& params = lcm.getShadowParams(li);
const mat4f Mv = getDirectionalLightViewMatrix(direction, position);
// find decent near/far
ShadowMap::updateSceneInfoSpot(Mv, scene, sceneInfo);
// if the scene was empty, near > far
mHasVisibleShadows = -sceneInfo.lsNearFar[0] < -sceneInfo.lsNearFar[1];
if (!mHasVisibleShadows) {
return {};
}
// FIXME: we need a configuration for minimum near plane (for now hardcoded to 1cm)
float nearPlane = std::max(0.01f, -sceneInfo.lsNearFar[0]);
float farPlane = std::min(radius, -sceneInfo.lsNearFar[1]);
auto outerConeAngle = lcm.getSpotLightOuterCone(li);
return updatePunctual(Mv, outerConeAngle, nearPlane, farPlane, shadowMapInfo, params);
}
ShadowMap::ShaderParameters ShadowMap::updatePoint(FEngine& engine,
const FScene::LightSoa& lightData, size_t index,
filament::CameraInfo const& camera, const ShadowMapInfo& shadowMapInfo, FScene const& scene,
SceneInfo, uint8_t face) noexcept {
ShaderParameters shaderParameters;
// check if this shadow map has anything to render
mHasVisibleShadows = false;
FScene::RenderableSoa const& UTILS_RESTRICT soa = scene.getRenderableData();
@@ -532,16 +518,53 @@ ShadowMap::ShaderParameters ShadowMap::updatePoint(FEngine& engine,
}
}
if (!mHasVisibleShadows) {
return {};
return shaderParameters;
}
auto& lcm = engine.getLightManager();
auto li = lightData.elementAt<FScene::LIGHT_INSTANCE>(index);
auto position = lightData.elementAt<FScene::POSITION_RADIUS>(index).xyz;
auto radius = lightData.elementAt<FScene::POSITION_RADIUS>(index).w;
auto li = lightData.elementAt<FScene::LIGHT_INSTANCE>(index);
const FLightManager::ShadowParams& params = lcm.getShadowParams(li);
/*
* Compute the light model matrix.
*/
const mat4f Mv = getPointLightViewMatrix(TextureCubemapFace(face), position);
return updatePunctual(Mv, 45.0f * f::DEG_TO_RAD, 0.01f, radius, shadowMapInfo, params);
const float3 direction = -transpose(Mv)[2].xyz;
// TODO: don't hardcode near plane
// Choose a reasonable value for the near plane.
float nearPlane = 0.01f;
float farPlane = radius;
const mat4f Mp = mat4f::perspective(90.0f, 1.0f, nearPlane, farPlane);
// For calculating the point light normal bias, we need the texel size in world space at the
// sample location. Using Thales's theorem, we find:
// texelSize(zInLightSpace) = zInLightSpace * texelSizeOnTheNearPlane / near
// = zInLightSpace * texelSizeAtOneMeter
// = zInLightSpace * (2*tan(halfConeAngle)/dimension)
// Note: this would not work with LISPSM, which warps the texture space.
shaderParameters.texelSizeAtOneMeterWs =
(2.0f * std::tan(f::PI_4) / float(shadowMapInfo.shadowDimension));
const float constantBias = shadowMapInfo.vsm ? 0.0f : params.options.constantBias;
const mat4f b = mat4f::translation(direction * constantBias);
// It's important to set the light camera's model matrix separately from its projection, so that
// the cameraPosition uniform gets set correctly.
// mLightSpace is used in the shader to access the shadow map texture, and has the model matrix
// baked in.
// The model matrix below is in fact inverted to get the view matrix and passed to the
// shader as 'viewFromWorldMatrix', and is used in the VSM case to compute the depth metric.
// (see depth_main.fs). Note that in the case of VSM, 'b' below is identity.
mCamera->setModelMatrix(mat4{ FCamera::rigidTransformInverse(Mv * b) });
mCamera->setCustomProjection(mat4(Mp), nearPlane, farPlane);
return shaderParameters;
}
mat4f ShadowMap::applyLISPSM(mat4f& Wp,
@@ -623,40 +646,50 @@ mat4f ShadowMap::applyLISPSM(mat4f& Wp,
// Apply these remapping in double to maintain a high precision for the depth axis
ShadowMap::TextureCoordsMapping ShadowMap::getTextureCoordsMapping(ShadowMapInfo const& info,
backend::Viewport const& viewport) noexcept {
mat4 ShadowMap::getTextureCoordsMapping(ShadowMapInfo const& info) noexcept {
// remapping from NDC to texture coordinates (i.e. [-1,1] -> [0, 1])
// ([1, 0] for depth mapping)
constexpr mat4f Mt{
mat4f::row_major_init{
0.5f, 0.0f, 0.0f, 0.5f,
0.0f, 0.5f, 0.0f, 0.5f,
0.0f, 0.0f, -0.5f, 0.5f,
0.0f, 0.0f, 0.0f, 1.0f
}};
const mat4f Mt(info.clipSpaceFlipped ? mat4f::row_major_init{
0.5f, 0.0f, 0.0f, 0.5f,
0.0f, -0.5f, 0.0f, 0.5f,
0.0f, 0.0f, -0.5f, 0.5f,
0.0f, 0.0f, 0.0f, 1.0f
} : mat4f::row_major_init{
0.5f, 0.0f, 0.0f, 0.5f,
0.0f, 0.5f, 0.0f, 0.5f,
0.0f, 0.0f, -0.5f, 0.5f,
0.0f, 0.0f, 0.0f, 1.0f
});
// apply the viewport transform
const float2 o = float2{ viewport.left, viewport.bottom } / float(info.atlasDimension);
const float2 s = float2{ viewport.width, viewport.height } / float(info.atlasDimension);
const mat4f Mv{
mat4f::row_major_init{
s.x, 0.0f, 0.0f, o.x,
0.0f, s.y, 0.0f, o.y,
0.0f, 0.0f, 1.0f, 0.0f,
0.0f, 0.0f, 0.0f, 1.0f
}};
// the shadow map texture might be larger than the shadow map dimension, so we add a scaling
// factor
const float v = float(info.textureDimension) / float(info.atlasDimension);
const mat4f Mv(mat4f::row_major_init{
v, 0.0f, 0.0f, 0.0f,
0.0f, v, 0.0f, 0.0f,
0.0f, 0.0f, 1.0f, 0.0f,
0.0f, 0.0f, 0.0f, 1.0f
});
// this is equivalent to call uvToRenderTargetUV() in the shader in each texture access
const mat4f Mf = info.textureSpaceFlipped ? mat4f{
mat4f::row_major_init{
1.0f, 0.0f, 0.0f, 0.0f,
0.0f, -1.0f, 0.0f, 1.0f,
0.0f, 0.0f, 1.0f, 0.0f,
0.0f, 0.0f, 0.0f, 1.0f
}} : mat4f{};
// apply the 1-texel border viewport transform
const float o = 1.0f / float(info.atlasDimension);
const float s = 1.0f - 2.0f * (1.0f / float(info.textureDimension));
const mat4f Mb(mat4f::row_major_init{
s, 0.0f, 0.0f, o,
0.0f, s, 0.0f, o,
0.0f, 0.0f, 1.0f, 0.0f,
0.0f, 0.0f, 0.0f, 1.0f
});
// Compute shadow-map texture access and viewport transform
return { Mf * (Mv * Mt), inverse(Mt) * (Mv * Mt) };
const mat4f Mf = info.textureSpaceFlipped ? mat4f(mat4f::row_major_init{
1.0f, 0.0f, 0.0f, 0.0f,
0.0f, -1.0f, 0.0f, 1.0f,
0.0f, 0.0f, 1.0f, 0.0f,
0.0f, 0.0f, 0.0f, 1.0f
}) : mat4f();
// Compute shadow-map texture access transform
return mat4(Mf * Mb * Mv * Mt);
}
mat4f ShadowMap::computeVsmLightSpaceMatrix(const mat4f& lightSpacePcf,
@@ -1151,8 +1184,6 @@ void ShadowMap::visitScene(const FScene& scene, uint32_t visibleLayers,
void ShadowMap::initSceneInfo(ShadowMap::SceneInfo& sceneInfo,
uint8_t visibleLayers, FScene const& scene, mat4f const& viewMatrix) {
sceneInfo.csNearFar = { -1.0f, 1.0f };
sceneInfo.lsNearFar = {};
sceneInfo.visibleLayers = visibleLayers;
sceneInfo.vsNearFar = { std::numeric_limits<float>::lowest(), std::numeric_limits<float>::max() };
@@ -1212,42 +1243,26 @@ void ShadowMap::updateSceneInfoSpot(mat4f const& Mv, FScene const& scene,
);
}
backend::Viewport ShadowMap::getViewport() const noexcept {
filament::Viewport ShadowMap::getViewport() const noexcept {
// We set a viewport with a 1-texel border for when we index outside the
// texture. This can only happen for the directional light when "focus shadow casters is used".
// texture.
// DON'T CHANGE this unless ShadowMap::getTextureCoordsMapping() is updated too.
// see: ShadowMap::getTextureCoordsMapping()
//
// For floating-point depth textures, the 1-texel border could be set to
// FLOAT_MAX to avoid clamping in the shadow shader (see sampleDepth inside
// shadowing.fs). Unfortunately, the APIs don't seem let us clear depth
// attachments to anything greater than 1.0, so we'd need a way to do this other
// than clearing.
const uint32_t dim = mOptions->mapSize;
const uint16_t border = 1u;
return { border, border, dim - 2u * border, dim - 2u * border };
}
backend::Viewport ShadowMap::getScissor() const noexcept {
// We set a viewport with a 1-texel border for when we index outside the
// texture. This can only happen for the directional light when "focus shadow casters is used".
const uint32_t dim = mOptions->mapSize;
const uint16_t border = 1u;
switch (mShadowType) {
case ShadowType::DIRECTIONAL:
return { border, border, dim - 2u * border, dim - 2u * border };
case ShadowType::SPOT:
case ShadowType::POINT:
default:
return { 0, 0, dim, dim };
if (isPointShadow()) {
// for point-light we don't have a border
return { 0, 0, dim, dim };
} else {
return { 1, 1, dim - 2, dim - 2 };
}
}
math::float4 ShadowMap::getViewportNormalized(ShadowMapInfo const& shadowMapInfo) const noexcept {
const auto [l, b, w, h] = getViewport();
const float texel = 1.0f / float(shadowMapInfo.atlasDimension);
const float4 v = float4{ l, b, l + w, b + h } * texel;
if (shadowMapInfo.textureSpaceFlipped) {
// this is equivalent to calling uvToRenderTargetUV() in the shader *after* clamping
// texture coordinates to this normalized viewport.
return { v.x, 1.0f - v.w, v.z, 1.0f - v.y };
}
return v;
}
// ------------------------------------------------------------------------------------------------
void ShadowMap::prepareCamera(Transaction const& transaction,
@@ -1257,8 +1272,8 @@ void ShadowMap::prepareCamera(Transaction const& transaction,
}
void ShadowMap::prepareViewport(Transaction const& transaction,
backend::Viewport const& viewport) noexcept {
PerShadowMapUniforms::prepareViewport(transaction, viewport);
const filament::Viewport& viewport) noexcept {
PerShadowMapUniforms::prepareViewport(transaction, viewport, 0, 0);
}
void ShadowMap::prepareTime(Transaction const& transaction,
@@ -1266,6 +1281,13 @@ void ShadowMap::prepareTime(Transaction const& transaction,
PerShadowMapUniforms::prepareTime(transaction, engine, userTime);
}
void ShadowMap::prepareDirectionalLight(Transaction const& transaction,
FEngine& engine, math::float3 const& sceneSpaceDirection,
LightManager::Instance instance) noexcept {
PerShadowMapUniforms::prepareDirectionalLight(transaction,
engine, 1.0f, sceneSpaceDirection, instance);
}
void ShadowMap::prepareShadowMapping(Transaction const& transaction,
bool highPrecision) noexcept {
PerShadowMapUniforms::prepareShadowMapping(transaction, highPrecision);
@@ -1280,7 +1302,7 @@ void ShadowMap::commit(Transaction& transaction,
mPerShadowMapUniforms.commit(transaction, driver);
}
void ShadowMap::bind(backend::DriverApi& driver) const noexcept {
void ShadowMap::bindPerViewUniformsAndSamplers(backend::DriverApi& driver) const noexcept {
mPerShadowMapUniforms.bind(driver);
}
+13 -21
View File
@@ -24,7 +24,7 @@
#include "details/Camera.h"
#include "details/Scene.h"
#include <backend/DriverApiForward.h>
#include "private/backend/DriverApi.h"
#include <filament/Viewport.h>
@@ -87,7 +87,10 @@ public:
// e.g., for a texture dimension of 512, shadowDimension would be 510
uint16_t shadowDimension = 0;
// e.g. metal and vulkan textures
// e.g. Vulkan clip-space
bool clipSpaceFlipped = false;
// e.g. metal textures
bool textureSpaceFlipped = false;
// whether we're using vsm
@@ -127,7 +130,6 @@ public:
struct ShaderParameters {
math::mat4f lightSpace{};
math::float4 lightFromWorldZ{};
math::float4 scissorNormalized{};
float texelSizeAtOneMeterWs{};
};
@@ -172,14 +174,13 @@ public:
static void updateSceneInfoSpot(const math::mat4f& Mv, FScene const& scene,
SceneInfo& sceneInfo);
filament::Viewport getViewport() const noexcept;
LightManager::ShadowOptions const* getShadowOptions() const noexcept { return mOptions; }
size_t getLightIndex() const { return mLightIndex; }
uint16_t getShadowIndex() const { return mShadowIndex; }
void setLayer(uint8_t layer) noexcept { mLayer = layer; }
uint8_t getLayer() const noexcept { return mLayer; }
backend::Viewport getViewport() const noexcept;
backend::Viewport getScissor() const noexcept;
bool isDirectionalShadow() const noexcept { return mShadowType == ShadowType::DIRECTIONAL; }
bool isSpotShadow() const noexcept { return mShadowType == ShadowType::SPOT; }
bool isPointShadow() const noexcept { return mShadowType == ShadowType::POINT; }
@@ -191,15 +192,18 @@ public:
static void prepareCamera(Transaction const& transaction,
FEngine& engine, const CameraInfo& cameraInfo) noexcept;
static void prepareViewport(Transaction const& transaction,
backend::Viewport const& viewport) noexcept;
const filament::Viewport& viewport) noexcept;
static void prepareTime(Transaction const& transaction,
FEngine& engine, math::float4 const& userTime) noexcept;
static void prepareDirectionalLight(Transaction const& transaction,
FEngine& engine, math::float3 const& sceneSpaceDirection,
LightManager::Instance instance) noexcept;
static void prepareShadowMapping(Transaction const& transaction,
bool highPrecision) noexcept;
static PerShadowMapUniforms::Transaction open(backend::DriverApi& driver) noexcept;
void commit(Transaction& transaction,
backend::DriverApi& driver) const noexcept;
void bind(backend::DriverApi& driver) const noexcept;
void bindPerViewUniformsAndSamplers(backend::DriverApi& driver) const noexcept;
private:
struct Segment {
@@ -213,11 +217,6 @@ private:
// 8 corners, 12 segments w/ 2 intersection max -- all of this twice (8 + 12 * 2) * 2 (768 bytes)
using FrustumBoxIntersection = std::array<math::float3, 64>;
ShaderParameters updatePunctual(
math::mat4f const& Mv, float outerConeAngle, float nearPlane, float farPlane,
const ShadowMapInfo& shadowMapInfo,
const FLightManager::ShadowParams& params) noexcept;
static math::mat4f applyLISPSM(math::mat4f& Wp,
filament::CameraInfo const& camera, FLightManager::ShadowParams const& params,
const math::mat4f& LMpMv,
@@ -277,18 +276,11 @@ private:
static math::mat4f directionalLightFrustum(float n, float f) noexcept;
struct TextureCoordsMapping {
math::mat4f clipToTexture;
math::mat4f clipToNdc;
};
static TextureCoordsMapping getTextureCoordsMapping(ShadowMapInfo const& info,
backend::Viewport const& viewport) noexcept;
static math::mat4 getTextureCoordsMapping(ShadowMapInfo const& info) noexcept;
static math::mat4f computeVsmLightSpaceMatrix(const math::mat4f& lightSpacePcf,
const math::mat4f& Mv, float znear, float zfar) noexcept;
math::float4 getViewportNormalized(ShadowMapInfo const& shadowMapInfo) const noexcept;
float texelSizeWorldSpace(const math::mat3f& worldToShadowTexture,
uint16_t shadowDimension) const noexcept;
+82 -81
View File
@@ -97,10 +97,8 @@ void ShadowMapManager::setDirectionalShadowMap(size_t lightIndex,
LightManager::ShadowOptions const* options) noexcept {
assert_invariant(options->shadowCascades <= CONFIG_MAX_SHADOW_CASCADES);
for (size_t c = 0; c < options->shadowCascades; c++) {
const size_t i = c;
assert_invariant(i < CONFIG_MAX_SHADOW_CASCADES);
auto* pShadowMap = getShadowMap(i);
pShadowMap->initialize(lightIndex, ShadowType::DIRECTIONAL, i, 0, options);
auto* pShadowMap = getCascadeShadowMap(c);
pShadowMap->initialize(lightIndex, ShadowType::DIRECTIONAL, c, 0, options);
mCascadeShadowMaps.push_back(pShadowMap);
}
}
@@ -109,19 +107,17 @@ void ShadowMapManager::addShadowMap(size_t lightIndex, bool spotlight,
LightManager::ShadowOptions const* options) noexcept {
if (spotlight) {
const size_t c = mSpotShadowMaps.size();
const size_t i = c + CONFIG_MAX_SHADOW_CASCADES;
assert_invariant(i < CONFIG_MAX_SHADOWMAPS);
auto* pShadowMap = getShadowMap(i);
pShadowMap->initialize(lightIndex, ShadowType::SPOT, i, 0, options);
assert_invariant(c < CONFIG_MAX_SHADOWMAP_PUNCTUAL);
auto* pShadowMap = getPointOrSpotShadowMap(c);
pShadowMap->initialize(lightIndex, ShadowType::SPOT, c, 0, options);
mSpotShadowMaps.push_back(pShadowMap);
} else {
// point-light, generate 6 independent shadowmaps
// point-light
for (size_t face = 0; face < 6; face++) {
const size_t c = mSpotShadowMaps.size();
const size_t i = c + CONFIG_MAX_SHADOW_CASCADES;
assert_invariant(i < CONFIG_MAX_SHADOWMAPS);
auto* pShadowMap = getShadowMap(i);
pShadowMap->initialize(lightIndex, ShadowType::POINT, i, face, options);
assert_invariant(c < CONFIG_MAX_SHADOWMAP_PUNCTUAL);
auto* pShadowMap = getPointOrSpotShadowMap(c);
pShadowMap->initialize(lightIndex, ShadowType::POINT, c, face, options);
mSpotShadowMaps.push_back(pShadowMap);
}
}
@@ -159,11 +155,9 @@ FrameGraphId<FrameGraphTexture> ShadowMapManager::render(FEngine& engine, FrameG
utils::FixedCapacityVector<ShadowPass> passList;
};
VsmShadowOptions const& vsmShadowOptions = view.getVsmShadowOptions();
auto& prepareShadowPass = fg.addPass<PrepareShadowPassData>("Prepare Shadow Pass",
[&](FrameGraph::Builder& builder, auto& data) {
data.passList.reserve(CONFIG_MAX_SHADOWMAPS);
data.passList.reserve(CONFIG_MAX_SHADOW_LAYERS);
data.shadows = builder.createTexture("Shadowmap", {
.width = textureRequirements.size, .height = textureRequirements.size,
.depth = textureRequirements.layers,
@@ -205,8 +199,7 @@ FrameGraphId<FrameGraphTexture> ShadowMapManager::render(FEngine& engine, FrameG
// "read" from one of its resource (only writes), so the FrameGraph culls it.
builder.sideEffect();
},
[this, &engine, &view, vsmShadowOptions,
scene, mainCameraInfo, userTime, passTemplate = pass](
[this, &engine, &view, scene, mainCameraInfo, userTime, passTemplate = pass](
FrameGraphResources const& resources, auto const& data, DriverApi& driver) {
// Note: we could almost parallel_for the loop below, the problem currently is
@@ -236,15 +229,15 @@ FrameGraphId<FrameGraphTexture> ShadowMapManager::render(FEngine& engine, FrameG
}
if (shadowMap.hasVisibleShadows()) {
// Note: this loop can generate a lot of commands that come out of the
// "per frame command arena". The allocation persists until the
// end of the frame.
// One way to possibly mitigate this, would be to always use the
// same command buffer for all shadow map, but then we'd generate
// a lot of unneeded draw calls.
// To do this efficiently, we'd need a way to cull draw calls already
// recorded in the command buffer, per shadow map.
// TODO: this loop can generate a lot of commands. investigate if
// we could compact or reduce the number of commands.
// - we could move this into the execute of each pass, but
// then we would have to update the UBO each time.
// - we could also always use the same commands for all
// shadow maps, but then we'd generate a lot of
// unneeded draw call.
// generate and sort the commands for rendering the shadow map
// cameraInfo only valid after calling update
const CameraInfo cameraInfo{ shadowMap.getCamera() };
@@ -254,14 +247,18 @@ FrameGraphId<FrameGraphTexture> ShadowMapManager::render(FEngine& engine, FrameG
ShadowMap::prepareViewport(transaction, shadowMap.getViewport());
ShadowMap::prepareTime(transaction, engine, userTime);
ShadowMap::prepareShadowMapping(transaction,
vsmShadowOptions.highPrecision);
view.getVsmShadowOptions().highPrecision);
if (shadowMap.getShadowType() == ShadowType::DIRECTIONAL) {
ShadowMap::prepareDirectionalLight(transaction, engine,
scene->getLightData().elementAt<FScene::DIRECTION>(0),
scene->getLightData().elementAt<FScene::LIGHT_INSTANCE>(0));
}
shadowMap.commit(transaction, driver);
// updatePrimitivesLod must be run before RenderPass::appendCommands.
view.updatePrimitivesLod(engine,
cameraInfo, scene->getRenderableData(), entry.range);
// generate and sort the commands for rendering the shadow map
RenderPass pass(passTemplate);
pass.setCamera(cameraInfo);
pass.setVisibilityMask(entry.visibilityMask);
@@ -273,13 +270,22 @@ FrameGraphId<FrameGraphTexture> ShadowMapManager::render(FEngine& engine, FrameG
entry.executor = pass.getExecutor();
if (!view.hasVSM()) {
auto const* options = shadowMap.getShadowOptions();
auto li = scene->getLightData().elementAt<FScene::LIGHT_INSTANCE>(
entry.shadowMap->getLightIndex());
auto const& params = engine.getLightManager().getShadowParams(li);
const PolygonOffset polygonOffset = { // handle reversed Z
.slope = -options->polygonOffsetSlope,
.constant = -options->polygonOffsetConstant
.slope = -params.options.polygonOffsetSlope,
.constant = -params.options.polygonOffsetConstant
};
entry.executor.overridePolygonOffset(&polygonOffset);
}
constexpr const backend::Viewport disabledScissor{ 0, 0,
(uint32_t)std::numeric_limits<int32_t>::max(),
(uint32_t)std::numeric_limits<int32_t>::max() };
entry.executor.overrideScissor(&disabledScissor);
}
}
@@ -308,7 +314,6 @@ FrameGraphId<FrameGraphTexture> ShadowMapManager::render(FEngine& engine, FrameG
const uint8_t layer = entry.shadowMap->getLayer();
const auto* options = entry.shadowMap->getShadowOptions();
const auto msaaSamples = textureRequirements.msaaSamples;
auto& shadowPass = fg.addPass<ShadowPassData>("Shadow Pass",
[&](FrameGraph::Builder& builder, auto& data) {
@@ -327,7 +332,7 @@ FrameGraphId<FrameGraphTexture> ShadowMapManager::render(FEngine& engine, FrameG
// We specify the sample count here because we don't need automatic resolve.
auto depth = builder.createTexture("Temporary VSM Depth Texture", {
.width = textureRequirements.size, .height = textureRequirements.size,
.samples = msaaSamples,
.samples = options->vsm.msaaSamples,
.format = TextureFormat::DEPTH16,
});
@@ -350,7 +355,7 @@ FrameGraphId<FrameGraphTexture> ShadowMapManager::render(FEngine& engine, FrameG
TargetBufferFlags::COLOR | TargetBufferFlags::DEPTH;
// we need to clear the shadow map with the max EVSM moments
renderTargetDesc.clearColor = vsmClearColor;
renderTargetDesc.samples = msaaSamples;
renderTargetDesc.samples = options->vsm.msaaSamples;
if (blur) {
data.tempBlurSrc = builder.write(data.tempBlurSrc,
@@ -361,7 +366,7 @@ FrameGraphId<FrameGraphTexture> ShadowMapManager::render(FEngine& engine, FrameG
.color = { data.tempBlurSrc },
.depth = depth },
.clearColor = vsmClearColor,
.samples = msaaSamples,
.samples = options->vsm.msaaSamples,
.clearFlags = TargetBufferFlags::COLOR
| TargetBufferFlags::DEPTH
});
@@ -388,15 +393,16 @@ FrameGraphId<FrameGraphTexture> ShadowMapManager::render(FEngine& engine, FrameG
// `entry` lives in `PrepareShadowPassData` which is guaranteed to still
// be alive when we execute here (all passes stay alive until the FrameGraph
// is destroyed).
// It wouldn't work to capture by copy because entry.executor wouldn't be
// It wouldn't work to capture by copy because entry.pass wouldn't be
// initialized, as this happens in an `execute` block.
entry.shadowMap->bindPerViewUniformsAndSamplers(driver);
auto rt = resources.getRenderPassInfo(data.rt);
rt.params.viewport = entry.shadowMap->getViewport();
engine.flush();
driver.beginRenderPass(rt.target, rt.params);
entry.shadowMap->bind(driver);
entry.executor.overrideScissor(entry.shadowMap->getScissor());
entry.executor.execute(engine, "Shadow Pass");
driver.endRenderPass();
});
@@ -447,8 +453,8 @@ ShadowMapManager::ShadowTechnique ShadowMapManager::updateCascadeShadowMaps(FEng
.atlasDimension = mTextureAtlasRequirements.size,
.textureDimension = uint16_t(options.mapSize),
.shadowDimension = uint16_t(options.mapSize - 2u),
.textureSpaceFlipped = engine.getBackend() == Backend::METAL ||
engine.getBackend() == Backend::VULKAN,
.clipSpaceFlipped = engine.getBackend() == Backend::VULKAN,
.textureSpaceFlipped = engine.getBackend() == Backend::METAL,
.vsm = view.hasVSM()
};
@@ -458,7 +464,7 @@ ShadowMapManager::ShadowTechnique ShadowMapManager::updateCascadeShadowMaps(FEng
// entire camera frustum, as if we only had a single cascade.
ShadowMap& shadowMap = *mCascadeShadowMaps[0];
shadowMap.updateDirectional(mEngine,
auto shaderParameters = shadowMap.updateDirectional(mEngine,
lightData, 0, cameraInfo, shadowMapInfo, *scene, sceneInfo);
hasVisibleShadows = shadowMap.hasVisibleShadows();
@@ -467,6 +473,18 @@ ShadowMapManager::ShadowTechnique ShadowMapManager::updateCascadeShadowMaps(FEng
Frustum const& frustum = shadowMap.getCamera().getCullingFrustum();
FView::cullRenderables(engine.getJobSystem(), renderableData, frustum,
VISIBLE_DIR_SHADOW_RENDERABLE_BIT);
// Set shadowBias, using the first directional cascade.
// when computing the required bias we need a half-texel size, so we multiply by 0.5 here.
// note: normalBias is set to zero for VSM
const float normalBias = shadowMapInfo.vsm ? 0.0f : 0.5f * lcm.getShadowNormalBias(0);
// Texel size is constant for directional light (although that's not true when LISPSM
// is used, but in that case we're pretending it is).
const float wsTexelSize = shaderParameters.texelSizeAtOneMeterWs;
mShadowMappingUniforms.shadowBias = normalBias * wsTexelSize;
mShadowMappingUniforms.shadowBulbRadiusLs =
mSoftShadowOptions.penumbraScale * options.shadowBulbRadius / wsTexelSize;
mShadowMappingUniforms.elvsm = options.vsm.elvsm;
}
}
@@ -519,10 +537,6 @@ ShadowMapManager::ShadowTechnique ShadowMapManager::updateCascadeShadowMaps(FEng
mShadowMappingUniforms.cascadeSplits = wsSplitPositionUniform;
// when computing the required bias we need a half-texel size, so we multiply by 0.5 here.
// note: normalBias is set to zero for VSM
const float normalBias = shadowMapInfo.vsm ? 0.0f : 0.5f * lcm.getShadowNormalBias(0);
for (size_t i = 0, c = mCascadeShadowMaps.size(); i < c; i++) {
assert_invariant(mCascadeShadowMaps[i]);
@@ -536,23 +550,7 @@ ShadowMapManager::ShadowTechnique ShadowMapManager::updateCascadeShadowMaps(FEng
lightData, 0, cameraInfo, shadowMapInfo, *scene, sceneInfo);
if (shadowMap.hasVisibleShadows()) {
const size_t shadowIndex = shadowMap.getShadowIndex();
assert_invariant(shadowIndex == i);
// Texel size is constant for directional light (although that's not true when LISPSM
// is used, but in that case we're pretending it is).
const float wsTexelSize = 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 = normalBias * wsTexelSize;
s.shadows[shadowIndex].texelSizeAtOneMeter = wsTexelSize;
s.shadows[shadowIndex].elvsm = options.vsm.elvsm;
s.shadows[shadowIndex].bulbRadiusLs =
mSoftShadowOptions.penumbraScale * options.shadowBulbRadius / wsTexelSize;
mShadowMappingUniforms.lightFromWorldMatrix[i] = shaderParameters.lightSpace;
shadowTechnique |= ShadowTechnique::SHADOW_MAP;
cascadeHasVisibleShadows |= 0x1u << i;
}
@@ -632,7 +630,7 @@ void ShadowMapManager::prepareSpotShadowMap(ShadowMap& shadowMap,
// compute shadow map frustum for culling
const mat4f Mv = ShadowMap::getDirectionalLightViewMatrix(direction, position);
const mat4f Mp = mat4f::perspective(outerConeAngle * f::RAD_TO_DEG * 2.0f, 1.0f, 0.01f, radius);
const mat4f MpMv = math::highPrecisionMultiply(Mp, Mv);
const mat4f MpMv(math::highPrecisionMultiply(Mp, Mv));
const Frustum frustum(MpMv);
// Cull shadow casters
@@ -662,8 +660,8 @@ void ShadowMapManager::prepareSpotShadowMap(ShadowMap& shadowMap,
.atlasDimension = mTextureAtlasRequirements.size,
.textureDimension = uint16_t(options->mapSize),
.shadowDimension = uint16_t(options->mapSize - 2u),
.textureSpaceFlipped = engine.getBackend() == Backend::METAL ||
engine.getBackend() == Backend::VULKAN,
.clipSpaceFlipped = engine.getBackend() == Backend::VULKAN,
.textureSpaceFlipped = engine.getBackend() == Backend::METAL,
.vsm = view.hasVSM()
};
@@ -680,9 +678,8 @@ void ShadowMapManager::prepareSpotShadowMap(ShadowMap& shadowMap,
auto& s = mShadowUb.edit();
const double n = shadowMap.getCamera().getNear();
const double f = shadowMap.getCamera().getCullingFar();
s.shadows[shadowIndex].layer = shadowMap.getLayer();
s.shadows[shadowIndex].lightFromWorldMatrix = shaderParameters.lightSpace;
s.shadows[shadowIndex].scissorNormalized = shaderParameters.scissorNormalized;
s.shadows[shadowIndex].direction = direction;
s.shadows[shadowIndex].normalBias = normalBias * wsTexelSizeAtOneMeter;
s.shadows[shadowIndex].lightFromWorldZ = shaderParameters.lightFromWorldZ;
s.shadows[shadowIndex].texelSizeAtOneMeter = wsTexelSizeAtOneMeter;
@@ -690,8 +687,7 @@ void ShadowMapManager::prepareSpotShadowMap(ShadowMap& shadowMap,
s.shadows[shadowIndex].elvsm = options->vsm.elvsm;
s.shadows[shadowIndex].bulbRadiusLs =
mSoftShadowOptions.penumbraScale * options->shadowBulbRadius
/ wsTexelSizeAtOneMeter;
/ wsTexelSizeAtOneMeter;
}
}
@@ -743,8 +739,8 @@ void ShadowMapManager::preparePointShadowMap(ShadowMap& shadowMap,
.atlasDimension = mTextureAtlasRequirements.size,
.textureDimension = uint16_t(options->mapSize),
.shadowDimension = uint16_t(options->mapSize), // point-lights don't have a border
.textureSpaceFlipped = engine.getBackend() == Backend::METAL ||
engine.getBackend() == Backend::VULKAN,
.clipSpaceFlipped = engine.getBackend() == Backend::VULKAN,
.textureSpaceFlipped = engine.getBackend() == Backend::METAL,
.vsm = view.hasVSM()
};
@@ -753,6 +749,7 @@ void ShadowMapManager::preparePointShadowMap(ShadowMap& shadowMap,
// and if we need to generate it, update all the UBO data
// Note: this below is done for all six faces even if it sets identical values each time
if (shadowMap.hasVisibleShadows()) {
const size_t shadowIndex = shadowMap.getShadowIndex();
const float wsTexelSizeAtOneMeter = shaderParameters.texelSizeAtOneMeterWs;
@@ -762,11 +759,16 @@ void ShadowMapManager::preparePointShadowMap(ShadowMap& shadowMap,
auto& s = mShadowUb.edit();
const double n = shadowMap.getCamera().getNear();
const double f = shadowMap.getCamera().getCullingFar();
s.shadows[shadowIndex].layer = shadowMap.getLayer();
s.shadows[shadowIndex].lightFromWorldMatrix = shaderParameters.lightSpace;
s.shadows[shadowIndex].scissorNormalized = shaderParameters.scissorNormalized;
s.shadows[shadowIndex].lightFromWorldMatrix = {}; // no texture matrix for point lights
s.shadows[shadowIndex].direction = {}; // no direction of point lights
s.shadows[shadowIndex].normalBias = normalBias * wsTexelSizeAtOneMeter;
s.shadows[shadowIndex].lightFromWorldZ = shaderParameters.lightFromWorldZ;
s.shadows[shadowIndex].lightFromWorldZ = {
-((n + f) / (f - n)) * 0.5f + 0.5f,
(f * n) / (f - n),
-n / (f - n),
1.0f / (f - n),
};
s.shadows[shadowIndex].texelSizeAtOneMeter = wsTexelSizeAtOneMeter;
s.shadows[shadowIndex].nearOverFarMinusNear = float(n / (f - n));
s.shadows[shadowIndex].elvsm = options->vsm.elvsm;
@@ -786,12 +788,14 @@ ShadowMapManager::ShadowTechnique ShadowMapManager::updateSpotShadowMaps(FEngine
shadowTechnique |= ShadowTechnique::SHADOW_MAP;
for (auto const* pShadowMap : mSpotShadowMaps) {
const size_t lightIndex = pShadowMap->getLightIndex();
// gather the per-light (not per shadow map) information. For point lights we will
// "see" 6 shadowmaps (one per face), we must use the first face one, the shader
// knows how to find the entry for other faces (they're guaranteed to be sequential).
// FIXME: currently we have one slot per shadowmap in the UBO, but we now have up to
// 6 shadowmap per light. So for now, we only write the data of the face 0,
// and the shader will figure out where to find the other face (layer+face)
if (pShadowMap->getFace() == 0) {
shadowInfo[lightIndex].castsShadows = true; // FIXME: is that set correctly?
shadowInfo[lightIndex].index = pShadowMap->getShadowIndex();
shadowInfo[lightIndex].layer = pShadowMap->getLayer();
}
}
}
@@ -842,8 +846,6 @@ void ShadowMapManager::calculateTextureRequirements(FEngine& engine, FView& view
const bool useMipmapping = view.hasVSM() &&
((vsmShadowOptions.anisotropy > 0) || vsmShadowOptions.mipmapping);
const uint8_t msaaSamples = vsmShadowOptions.msaaSamples;
TextureFormat format = TextureFormat::DEPTH16;
if (view.hasVSM()) {
if (vsmShadowOptions.highPrecision) {
@@ -875,7 +877,6 @@ void ShadowMapManager::calculateTextureRequirements(FEngine& engine, FView& view
(uint16_t)maxDimension,
layersNeeded,
mipLevels,
msaaSamples,
format
};
}
+21 -8
View File
@@ -45,10 +45,14 @@ class FrameGraph;
class RenderPass;
struct ShadowMappingUniforms {
std::array<math::mat4f, CONFIG_MAX_SHADOW_CASCADES> lightFromWorldMatrix;
math::float4 cascadeSplits;
float shadowBulbRadiusLs;
float shadowBias;
float ssContactShadowDistance;
uint32_t directionalShadows;
uint32_t cascades;
bool elvsm;
};
class ShadowMapManager {
@@ -89,13 +93,23 @@ public:
FrameGraphId<FrameGraphTexture> render(FEngine& engine, FrameGraph& fg, RenderPass const& pass,
FView& view, CameraInfo const& mainCameraInfo, math::float4 const& userTime) noexcept;
ShadowMap* getShadowMap(size_t index) noexcept {
assert_invariant(index < CONFIG_MAX_SHADOWMAPS);
return std::launder(reinterpret_cast<ShadowMap*>(&mShadowMapCache[index]));
ShadowMap* getCascadeShadowMap(size_t cascade) noexcept {
assert_invariant(cascade < CONFIG_MAX_SHADOW_CASCADES);
return std::launder(reinterpret_cast<ShadowMap*>(&mShadowMapCache[cascade]));
}
ShadowMap const* getShadowMap(size_t index) const noexcept {
return const_cast<ShadowMapManager*>(this)->getShadowMap(index);
ShadowMap const* getCascadeShadowMap(size_t cascade) const noexcept {
return const_cast<ShadowMapManager*>(this)->getCascadeShadowMap(cascade);
}
ShadowMap* getPointOrSpotShadowMap(size_t index) noexcept {
assert_invariant(index < CONFIG_MAX_SHADOWMAP_PUNCTUAL);
return std::launder(reinterpret_cast<ShadowMap*>(
&mShadowMapCache[CONFIG_MAX_SHADOW_CASCADES + index]));
}
ShadowMap const* getPointOrSpotShadowMap(size_t spot) const noexcept {
return const_cast<ShadowMapManager*>(this)->getPointOrSpotShadowMap(spot);
}
// valid after calling update() above
@@ -180,7 +194,6 @@ private:
uint16_t size = 0;
uint8_t layers = 0;
uint8_t levels = 0;
uint8_t msaaSamples = 1;
backend::TextureFormat format = backend::TextureFormat::DEPTH16;
} mTextureAtlasRequirements;
@@ -202,13 +215,13 @@ private:
utils::FixedCapacityVector<ShadowMap*> mSpotShadowMaps{
utils::FixedCapacityVector<ShadowMap*>::with_capacity(
CONFIG_MAX_SHADOWMAPS - CONFIG_MAX_SHADOW_CASCADES) };
CONFIG_MAX_SHADOWMAP_PUNCTUAL) };
// inline storage for all our ShadowMap objects, we can't easily use a std::array<> directly.
// because ShadowMap doesn't have a default ctor, and we avoid out-of-line allocations.
// Each ShadowMap is currently 40 bytes (total of 2.5KB for 64 shadow maps)
using ShadowMapStorage = std::aligned_storage<sizeof(ShadowMap), alignof(ShadowMap)>::type;
std::array<ShadowMapStorage, CONFIG_MAX_SHADOWMAPS> mShadowMapCache;
std::array<ShadowMapStorage, CONFIG_MAX_SHADOW_LAYERS> mShadowMapCache;
};
} // namespace filament
+1 -1
View File
@@ -16,7 +16,7 @@
#include <filament/ToneMapper.h>
#include "ColorSpaceUtils.h"
#include "ColorSpace.h"
#include <math/vec3.h>
#include <math/scalar.h>
+1
View File
@@ -240,6 +240,7 @@ void FLightManager::setShadowOptions(Instance i, ShadowOptions const& options) n
params.options.shadowFar = std::max(options.shadowFar, 0.0f);
params.options.shadowNearHint = std::max(options.shadowNearHint, 0.0f);
params.options.shadowFarHint = std::max(options.shadowFarHint, 0.0f);
params.options.vsm.msaaSamples = std::max(uint8_t(0), options.vsm.msaaSamples);
params.options.vsm.blurWidth = std::max(0.0f, options.vsm.blurWidth);
}
+28 -33
View File
@@ -56,7 +56,7 @@ void FTransformManager::create(Entity entity, Instance parent, const mat4f& loca
if (UTILS_UNLIKELY(manager.hasComponent(entity))) {
destroy(entity);
}
Instance const i = manager.addComponent(entity);
Instance i = manager.addComponent(entity);
assert_invariant(i);
assert_invariant(i != parent);
@@ -78,7 +78,7 @@ void FTransformManager::create(Entity entity, Instance parent, const mat4& local
if (UTILS_UNLIKELY(manager.hasComponent(entity))) {
destroy(entity);
}
Instance const i = manager.addComponent(entity);
Instance i = manager.addComponent(entity);
assert_invariant(i);
assert_invariant(i != parent);
@@ -96,7 +96,7 @@ void FTransformManager::setParent(Instance i, Instance parent) noexcept {
validateNode(i);
if (i) {
auto& manager = mManager;
Instance const oldParent = manager[i].parent;
Instance oldParent = manager[i].parent;
if (oldParent != parent) {
// TODO: on debug builds, ensure that the new parent isn't one of our descendant
removeNode(i);
@@ -144,7 +144,7 @@ TransformManager::children_iterator FTransformManager::getChildrenEnd(Instance)
void FTransformManager::destroy(Entity e) noexcept {
// update the reference of the element we're removing
auto& manager = mManager;
Instance const i = manager.getInstance(e);
Instance i = manager.getInstance(e);
validateNode(i);
if (i) {
// 1) remove the entry from the linked lists
@@ -158,7 +158,7 @@ void FTransformManager::destroy(Entity e) noexcept {
}
// 2) remove the component
Instance const moved = manager.removeComponent(e);
Instance moved = manager.removeComponent(e);
// 3) update the references to the entry now with Instance i
if (moved != i) {
@@ -200,15 +200,14 @@ void FTransformManager::updateNodeTransform(Instance i) noexcept {
// find our parent's world transform, if any
// note: by using the raw_array() we don't need to check that parent is valid.
Instance const parent = manager[i].parent;
FTransformManager::computeWorldTransform(
manager[i].world, manager[i].worldTranslationLo,
Instance parent = manager[i].parent;
computeWorldTransform(manager[i].world, manager[i].worldTranslationLo,
manager[parent].world, manager[i].local,
manager[parent].worldTranslationLo, manager[i].localTranslationLo,
mAccurateTranslations);
// update our children's world transforms
Instance const child = manager[i].firstChild;
Instance child = manager[i].firstChild;
if (UTILS_UNLIKELY(child)) { // assume we don't have a hierarchy in the common case
transformChildren(manager, child);
}
@@ -238,11 +237,10 @@ void FTransformManager::computeAllWorldTransforms() noexcept {
while (UTILS_UNLIKELY(Instance(manager[i].parent) > i)) {
swapNode(i, manager[i].parent);
}
Instance const parent = manager[i].parent;
Instance parent = manager[i].parent;
assert_invariant(parent < i);
FTransformManager::computeWorldTransform(
manager[i].world, manager[i].worldTranslationLo,
computeWorldTransform(manager[i].world, manager[i].worldTranslationLo,
manager[parent].world, manager[i].local,
manager[parent].worldTranslationLo, manager[i].localTranslationLo,
accurate);
@@ -260,7 +258,7 @@ void FTransformManager::insertNode(Instance i, Instance parent) noexcept {
manager[i].next = 0;
if (parent) {
// we insert ourselves first in the parent's list
Instance const next = manager[parent].firstChild;
Instance next = manager[parent].firstChild;
manager[i].next = next;
// we're our parent's first child now
manager[parent].firstChild = i;
@@ -281,10 +279,8 @@ void FTransformManager::swapNode(Instance i, Instance j) noexcept {
auto& manager = mManager;
// swap the content of the nodes directly
std::swap(manager.elementAt<LOCAL>(i), manager.elementAt<LOCAL>(j));
std::swap(manager.elementAt<LOCAL_LO>(i), manager.elementAt<LOCAL_LO>(j));
std::swap(manager.elementAt<WORLD>(i), manager.elementAt<WORLD>(j));
std::swap(manager.elementAt<WORLD_LO>(i), manager.elementAt<WORLD_LO>(j));
std::swap(manager.elementAt<LOCAL>(i), manager.elementAt<LOCAL>(j));
std::swap(manager.elementAt<WORLD>(i), manager.elementAt<WORLD>(j));
manager.swap(i, j); // this swaps the data relative to SingleInstanceComponentManager
// now swap the linked-list references, to do that correctly we must use a temporary
@@ -314,13 +310,13 @@ void FTransformManager::swapNode(Instance i, Instance j) noexcept {
updateNode(j);
}
// removes a node from the graph, but doesn't remove it or its children from the array
// removes an node from the graph, but doesn't removes it or its children from the array
// (making everybody orphaned).
void FTransformManager::removeNode(Instance i) noexcept {
auto& manager = mManager;
Instance const parent = manager[i].parent;
Instance const prev = manager[i].prev;
Instance const next = manager[i].next;
Instance parent = manager[i].parent;
Instance prev = manager[i].prev;
Instance next = manager[i].next;
if (prev) {
manager[prev].next = next;
} else if (parent) {
@@ -345,9 +341,9 @@ void FTransformManager::removeNode(Instance i) noexcept {
void FTransformManager::updateNode(Instance i) noexcept {
auto& manager = mManager;
// update our preview sibling's next reference (to ourselves)
Instance const parent = manager[i].parent;
Instance const prev = manager[i].prev;
Instance const next = manager[i].next;
Instance parent = manager[i].parent;
Instance prev = manager[i].prev;
Instance next = manager[i].next;
if (prev) {
manager[prev].next = i;
} else if (parent) {
@@ -375,15 +371,14 @@ void FTransformManager::transformChildren(Sim& manager, Instance i) noexcept {
const bool accurate = mAccurateTranslations;
while (i) {
// update child's world transform
Instance const parent = manager[i].parent;
FTransformManager::computeWorldTransform(
manager[i].world, manager[i].worldTranslationLo,
Instance parent = manager[i].parent;
computeWorldTransform(manager[i].world, manager[i].worldTranslationLo,
manager[parent].world, manager[i].local,
manager[parent].worldTranslationLo, manager[i].localTranslationLo,
accurate);
// assume we don't have a deep hierarchy
Instance const child = manager[i].firstChild;
Instance child = manager[i].firstChild;
if (UTILS_UNLIKELY(child)) {
transformChildren(manager, child);
}
@@ -428,14 +423,14 @@ void FTransformManager::computeWorldTransform(
}
void FTransformManager::validateNode(UTILS_UNUSED_IN_RELEASE Instance i) noexcept {
void FTransformManager::validateNode(Instance i) noexcept {
#ifndef NDEBUG
auto& manager = mManager;
if (i) {
Instance const parent = manager[i].parent;
Instance const firstChild = manager[i].firstChild;
Instance const prev = manager[i].prev;
Instance const next = manager[i].next;
Instance parent = manager[i].parent;
Instance firstChild = manager[i].firstChild;
Instance prev = manager[i].prev;
Instance next = manager[i].next;
assert_invariant(parent != i);
assert_invariant(prev != i);
assert_invariant(next != i);
+2 -2
View File
@@ -130,7 +130,7 @@ private:
void computeAllWorldTransforms() noexcept;
static void computeWorldTransform(math::mat4f& outWorld, math::float3& inoutWorldTranslationLo,
void computeWorldTransform(math::mat4f& outWorld, math::float3& inoutWorldTranslationLo,
math::mat4f const& pt, math::mat4f const& local,
math::float3 const& ptTranslationLo, math::float3 const& localTranslationLo,
bool accurate);
@@ -166,7 +166,7 @@ private:
typename Base::SoA& getSoA() { return mData; }
struct Proxy {
// all of these gets inlined
// all of this gets inlined
UTILS_ALWAYS_INLINE
Proxy(Base& sim, utils::EntityInstanceBase::Type i) noexcept
: local{ sim, i } { }
+7 -32
View File
@@ -21,9 +21,7 @@
#include "FilamentAPI-impl.h"
#include "ColorSpaceUtils.h"
#include <filament/ColorSpace.h>
#include "ColorSpace.h"
#include <math/vec2.h>
#include <math/vec3.h>
@@ -33,14 +31,13 @@
#include <utils/SpinLock.h>
#include <utils/Systrace.h>
#include <cmath>
#include <cstdlib>
#include <math.h>
#include <stdlib.h>
namespace filament {
using namespace utils;
using namespace math;
using namespace color;
using namespace backend;
//------------------------------------------------------------------------------
@@ -92,9 +89,6 @@ struct ColorGrading::BuilderDetails {
float3 midPoint = {1.0f};
float3 highlightScale = {1.0f};
// Output color space
ColorSpace outputColorSpace = Rec709-sRGB-D65;
bool operator!=(const BuilderDetails &rhs) const {
return !(rhs == *this);
}
@@ -123,8 +117,7 @@ struct ColorGrading::BuilderDetails {
saturation == rhs.saturation &&
shadowGamma == rhs.shadowGamma &&
midPoint == rhs.midPoint &&
highlightScale == rhs.highlightScale &&
outputColorSpace == rhs.outputColorSpace;
highlightScale == rhs.highlightScale;
}
};
@@ -260,12 +253,6 @@ ColorGrading::Builder& ColorGrading::Builder::curves(
return *this;
}
ColorGrading::Builder& ColorGrading::Builder::outputColorSpace(
const ColorSpace& colorSpace) noexcept {
mImpl->outputColorSpace = colorSpace;
return *this;
}
#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Wdeprecated-declarations"
ColorGrading* ColorGrading::Builder::build(Engine& engine) {
@@ -618,27 +605,16 @@ static float3 selectColorGradingLuminance(ColorGrading::ToneMapping toneMapping)
}
#pragma clang diagnostic pop
using ColorTransform = float3(*)(float3);
static ColorTransform selectOETF(const ColorSpace& colorSpace) noexcept {
if (colorSpace.getTransferFunction() == Linear) {
return OETF_Linear;
}
return OETF_sRGB;
}
//------------------------------------------------------------------------------
// Color grading implementation
//------------------------------------------------------------------------------
struct Config {
size_t lutDimension{};
size_t lutDimension;
mat3f adaptationTransform;
mat3f colorGradingIn;
mat3f colorGradingOut;
float3 colorGradingLuminance{};
ColorTransform oetf;
float3 colorGradingLuminance;
};
// Inside the FColorGrading constructor, TSAN sporadically detects a data race on the config struct;
@@ -661,7 +637,6 @@ FColorGrading::FColorGrading(FEngine& engine, const Builder& builder) {
c.colorGradingIn = selectColorGradingTransformIn(builder->toneMapping);
c.colorGradingOut = selectColorGradingTransformOut(builder->toneMapping);
c.colorGradingLuminance = selectColorGradingLuminance(builder->toneMapping);
c.oetf = selectOETF(builder->outputColorSpace);
}
mDimension = c.lutDimension;
@@ -784,7 +759,7 @@ FColorGrading::FColorGrading(FEngine& engine, const Builder& builder) {
v = saturate(v);
// Apply OETF
v = c.oetf(v);
v = OETF_sRGB(v);
*p++ = half4{v, 0.0f};
}
-1
View File
@@ -518,7 +518,6 @@ public:
// When set to true, the backend will attempt to capture the next frame and write the
// capture to file. At the moment, only supported by the Metal backend.
bool doFrameCapture = false;
bool disable_buffer_padding = false;
} renderer;
matdbg::DebugServer* server = nullptr;
} debug;
+45 -72
View File
@@ -68,8 +68,6 @@ FRenderer::FRenderer(FEngine& engine) :
FDebugRegistry& debugRegistry = engine.getDebugRegistry();
debugRegistry.registerProperty("d.renderer.doFrameCapture",
&engine.debug.renderer.doFrameCapture);
debugRegistry.registerProperty("d.renderer.disable_buffer_padding",
&engine.debug.renderer.disable_buffer_padding);
DriverApi& driver = engine.getDriverApi();
@@ -105,8 +103,8 @@ FRenderer::~FRenderer() noexcept {
// There shouldn't be any resource left when we get here, but if there is, make sure
// to free what we can (it would probably mean something when wrong).
#ifndef NDEBUG
size_t const wm = getCommandsHighWatermark();
size_t const wmpct = wm / (mEngine.getPerFrameCommandsSize() / 100);
size_t wm = getCommandsHighWatermark();
size_t wmpct = wm / (mEngine.getPerFrameCommandsSize() / 100);
slog.d << "Renderer: Commands High watermark "
<< wm / 1024 << " KiB (" << wmpct << "%), "
<< wm / sizeof(Command) << " commands, " << sizeof(Command) << " bytes/command"
@@ -156,30 +154,25 @@ TextureFormat FRenderer::getLdrFormat(bool translucent) const noexcept {
return (translucent || !mIsRGB8Supported) ? TextureFormat::RGBA8 : TextureFormat::RGB8;
}
std::pair<Handle<HwRenderTarget>, TargetBufferFlags>
FRenderer::getRenderTarget(FView const& view) const noexcept {
Handle<HwRenderTarget> outTarget = view.getRenderTargetHandle();
TargetBufferFlags outAttachementMask = view.getRenderTargetAttachmentMask();
void FRenderer::getRenderTarget(FView const& view,
TargetBufferFlags& outAttachementMask, Handle<HwRenderTarget>& outTarget) const noexcept {
outTarget = view.getRenderTargetHandle();
outAttachementMask = view.getRenderTargetAttachmentMask();
if (!outTarget) {
outTarget = mRenderTargetHandle;
outAttachementMask = TargetBufferFlags::COLOR0 | TargetBufferFlags::DEPTH;
}
return {outTarget, outAttachementMask };
}
backend::TargetBufferFlags FRenderer::getClearFlags() const noexcept {
return (mClearOptions.clear ? TargetBufferFlags::COLOR : TargetBufferFlags::NONE)
| TargetBufferFlags::DEPTH_AND_STENCIL;
}
void FRenderer::initializeClearFlags() noexcept {
void FRenderer::initializeClearFlags() {
// We always discard and clear the depth+stencil buffers -- we don't allow sharing these
// across views (clear implies discard)
mDiscardStartFlags = ((mClearOptions.discard || mClearOptions.clear) ?
TargetBufferFlags::COLOR : TargetBufferFlags::NONE)
| TargetBufferFlags::DEPTH_AND_STENCIL;
mClearFlags = getClearFlags();
mClearFlags = (mClearOptions.clear ? TargetBufferFlags::COLOR : TargetBufferFlags::NONE)
| TargetBufferFlags::DEPTH_AND_STENCIL;
}
void FRenderer::setPresentationTime(int64_t monotonic_clock_ns) {
@@ -216,9 +209,9 @@ bool FRenderer::beginFrame(FSwapChain* swapChain, uint64_t vsyncSteadyClockTimeN
}
// latch the frame time
std::chrono::duration<double> const time(appVsync - mUserEpoch);
float const h = float(time.count());
float const l = float(time.count() - h);
std::chrono::duration<double> time(appVsync - mUserEpoch);
float h = float(time.count());
float l = float(time.count() - h);
mShaderUserTime = { h, l, 0, 0 };
mPreviousRenderTargets.clear();
@@ -468,7 +461,6 @@ void FRenderer::renderJob(ArenaScope& arena, FView& view) {
driver.debugThreading();
const bool hasPostProcess = view.hasPostProcessPass();
bool hasScreenSpaceRefraction = false;
bool hasColorGrading = hasPostProcess;
bool hasDithering = view.getDithering() == Dithering::TEMPORAL;
bool hasFXAA = view.getAntiAliasing() == AntiAliasing::FXAA;
@@ -549,11 +541,10 @@ void FRenderer::renderJob(ArenaScope& arena, FView& view) {
// a very fast rendering path -- in this case, we would need an extra blit to "resolve" the
// buffer padding (because there are no other pass that can do it as a side effect).
// In this case, it is better to skip the padding, which won't be helping much.
const bool noBufferPadding = (colorGradingConfig.asSubpass && !hasFXAA && !scaled)
|| engine.debug.renderer.disable_buffer_padding;
const bool noBufferPadding = colorGradingConfig.asSubpass && !hasFXAA && !scaled;
// guardBand must be a multiple of 16 to guarantee the same exact rendering up to 4 mip levels.
float const guardBand = guardBandOptions.enabled ? 16.0f : 0.0f;
float guardBand = guardBandOptions.enabled ? 16.0f : 0.0f;
if (hasPostProcess && !noBufferPadding) {
// We always pad the rendering viewport to dimensions multiple of 16, this guarantees
@@ -630,7 +621,7 @@ void FRenderer::renderJob(ArenaScope& arena, FView& view) {
// Allocate some space for our commands in the per-frame Arena, and use that space as
// an Arena for commands. All this space is released when we exit this method.
size_t const perFrameCommandsSize = engine.getPerFrameCommandsSize();
size_t perFrameCommandsSize = engine.getPerFrameCommandsSize();
void* const arenaBegin = arena.allocate(perFrameCommandsSize, CACHELINE_SIZE);
void* const arenaEnd = pointermath::add(arenaBegin, perFrameCommandsSize);
RenderPass::Arena commandArena("Command Arena", { arenaBegin, arenaEnd });
@@ -680,17 +671,11 @@ void FRenderer::renderJob(ArenaScope& arena, FView& view) {
initializeClearFlags();
}
// Note: it is not well-defined what colorspace this clearColor is defined into. This leads
// to inconsistent colors depending on enabled features. When the clear is performed
// into a temporary buffer (common case), the clearColor is color-graded. A problem
// arises when transparent views are used, in this case the clear color is not
// color-graded.
const float4 clearColor = mClearOptions.clearColor;
const uint8_t clearStencil = mClearOptions.clearStencil;
const TargetBufferFlags clearFlags = mClearFlags;
const TargetBufferFlags discardStartFlags = mDiscardStartFlags;
const TargetBufferFlags keepOverrideStartFlags = TargetBufferFlags::ALL & ~discardStartFlags;
TargetBufferFlags keepOverrideStartFlags = TargetBufferFlags::ALL & ~discardStartFlags;
TargetBufferFlags keepOverrideEndFlags = TargetBufferFlags::NONE;
if (currentRenderTarget) {
@@ -706,34 +691,32 @@ void FRenderer::renderJob(ArenaScope& arena, FView& view) {
mDiscardStartFlags &= ~TargetBufferFlags::COLOR;
mClearFlags &= ~TargetBufferFlags::COLOR;
// the clearFlags and clearColor set below are "sticky" to the imported target, meaning
// they will apply anytime we render into this target, THIS INCLUDES when this target
// is "replacing" another one. E.g. typically when the color pass ends-up drawing directly
// here.
auto [viewRenderTarget, attachmentMask] = getRenderTarget(view);
FrameGraphId<FrameGraphTexture> const fgViewRenderTarget = fg.import("viewRenderTarget", {
.attachments = attachmentMask,
.viewport = DEBUG_DYNAMIC_SCALING ? svp : vp,
.clearColor = clearColor,
.samples = 0,
.clearFlags = clearFlags,
.keepOverrideStart = keepOverrideStartFlags,
.keepOverrideEnd = keepOverrideEndFlags
}, viewRenderTarget);
Handle<HwRenderTarget> viewRenderTarget;
TargetBufferFlags attachmentMask;
getRenderTarget(view, attachmentMask, viewRenderTarget);
FrameGraphId<FrameGraphTexture> fgViewRenderTarget = fg.import("viewRenderTarget",
{
.attachments = attachmentMask,
.viewport = DEBUG_DYNAMIC_SCALING ? svp : vp,
.clearColor = clearColor,
.samples = 0,
.clearFlags = clearFlags,
.keepOverrideStart = keepOverrideStartFlags,
.keepOverrideEnd = keepOverrideEndFlags
}, viewRenderTarget);
const bool blending = blendModeTranslucent;
const TextureFormat hdrFormat = getHdrFormat(view, needsAlphaChannel);
// the clearFlags and clearColor specified below will only apply when rendering into the
// temporary color buffer. In particular, they won't apply when rendering into the main
// swapchain (imported render target above)
RendererUtils::ColorPassConfig config{
.physicalViewport = svp,
.logicalViewport = xvp,
.width = svp.width,
.height = svp.height,
.xoffset = (uint32_t)xvp.left,
.yoffset = (uint32_t)xvp.bottom,
.scale = scale,
.hdrFormat = hdrFormat,
.msaa = msaaSampleCount,
.clearFlags = getClearFlags(),
.clearFlags = clearFlags,
.clearColor = clearColor,
.clearStencil = clearStencil,
.ssrLodOffset = 0.0f,
@@ -778,14 +761,9 @@ void FRenderer::renderJob(ArenaScope& arena, FView& view) {
// The reason why this bug is acceptable is that the viewport parameters are
// currently only used for generating noise, so it's not too bad.
// note: aoOptions.resolution is either 1.0 or 0.5, and the result is then
// guaranteed to be an integer (because xvp is a multiple of 16).
view.prepareViewport(svp,
filament::Viewport{
int32_t(float(xvp.left ) * aoOptions.resolution),
int32_t(float(xvp.bottom) * aoOptions.resolution),
uint32_t(float(xvp.width ) * aoOptions.resolution),
uint32_t(float(xvp.height) * aoOptions.resolution)});
xvp.left * aoOptions.resolution,
xvp.bottom * aoOptions.resolution);
view.commitUniforms(driver);
@@ -821,7 +799,7 @@ void FRenderer::renderJob(ArenaScope& arena, FView& view) {
builder.sideEffect();
},
[=, &view](FrameGraphResources const& resources,
auto const&, DriverApi& driver) mutable {
auto const& data, DriverApi& driver) mutable {
auto out = resources.getRenderPassInfo();
view.executePickingQueries(driver, out.target, aoOptions.resolution);
});
@@ -846,7 +824,7 @@ void FRenderer::renderJob(ArenaScope& arena, FView& view) {
// --------------------------------------------------------------------------------------------
// prepare screen-space reflection/refraction passes
PostProcessManager::ScreenSpaceRefConfig const ssrConfig = PostProcessManager::prepareMipmapSSR(
PostProcessManager::ScreenSpaceRefConfig ssrConfig = PostProcessManager::prepareMipmapSSR(
fg, svp.width, svp.height,
ssReflectionsOptions.enabled ? TextureFormat::RGBA16F : TextureFormat::R11F_G11F_B10F,
view.getCameraUser().getFieldOfView(Camera::Fov::VERTICAL), config.scale);
@@ -865,7 +843,7 @@ void FRenderer::renderJob(ArenaScope& arena, FView& view) {
{ .width = svp.width, .height = svp.height });
// generate the mipchain
PostProcessManager::generateMipmapSSR(ppm, fg,
reflections = PostProcessManager::generateMipmapSSR(ppm, fg,
reflections, ssrConfig.reflection, false, ssrConfig);
}
@@ -878,9 +856,9 @@ void FRenderer::renderJob(ArenaScope& arena, FView& view) {
pass.appendCommands(engine, RenderPass::COLOR);
pass.sortCommands(engine);
FrameGraphTexture::Descriptor const desc = {
.width = config.physicalViewport.width,
.height = config.physicalViewport.height,
FrameGraphTexture::Descriptor desc = {
.width = config.width,
.height = config.height,
.format = config.hdrFormat
};
@@ -891,7 +869,7 @@ void FRenderer::renderJob(ArenaScope& arena, FView& view) {
if (colorGradingConfig.asSubpass) {
ppm.colorGradingPrepareSubpass(driver,
colorGrading, colorGradingConfig, vignetteOptions,
desc.width, desc.height);
config.width, config.height);
} else if (colorGradingConfig.customResolve) {
ppm.customResolvePrepareSubpass(driver,
PostProcessManager::CustomResolveOp::COMPRESS);
@@ -942,10 +920,8 @@ void FRenderer::renderJob(ArenaScope& arena, FView& view) {
if (view.isScreenSpaceRefractionEnabled() && !pass.empty()) {
// this cancels the colorPass() call above if refraction is active.
// the color pass + refraction + color-grading as subpass if needed
const auto [output, enabled] = RendererUtils::refractionPass(fg, mEngine, view,
colorPassOutput = RendererUtils::refractionPass(fg, mEngine, view,
config, ssrConfig, colorGradingConfigForColor, pass);
colorPassOutput = output;
hasScreenSpaceRefraction = enabled;
}
if (colorGradingConfig.customResolve) {
@@ -1016,7 +992,7 @@ void FRenderer::renderJob(ArenaScope& arena, FView& view) {
// The bokeh height is always correct regardless of the dynamic resolution scaling.
// (because the CoC is calculated w.r.t. the height), so we only need to adjust
// the width.
float const bokehAspectRatio = scale.x / scale.y;
float bokehAspectRatio = scale.x / scale.y;
input = ppm.dof(fg, input, depth, cameraInfo, needsAlphaChannel,
bokehAspectRatio, dofOptions);
}
@@ -1071,8 +1047,6 @@ void FRenderer::renderJob(ArenaScope& arena, FView& view) {
// as a subpass)
// * And we also can't use the default rendertarget if frame history is needed (e.g. with
// screen-space reflections)
// * And we also can't use the default rendertarget with refractions, which need to reuse
// the rendertarget due to how the clear flags work.
// * We also need an extra blit if we haven't yet handled "xvp"
// TODO: in that specific scenario it would be better to just not use xvp
// The intermediate buffer is accomplished with a "fake" opaqueBlit (i.e. blit) operation.
@@ -1084,7 +1058,6 @@ void FRenderer::renderJob(ArenaScope& arena, FView& view) {
(outputIsSwapChain &&
(msaaSampleCount > 1 ||
colorGradingConfig.asSubpass ||
hasScreenSpaceRefraction ||
ssReflectionsOptions.enabled))) {
assert_invariant(!scaled);
input = ppm.blit(fg, blending, input, xvp, {
+5 -5
View File
@@ -134,21 +134,21 @@ private:
using Epoch = clock::time_point;
using duration = clock::duration;
backend::TargetBufferFlags getClearFlags() const noexcept;
void initializeClearFlags() noexcept;
void initializeClearFlags();
backend::TextureFormat getHdrFormat(const FView& view, bool translucent) const noexcept;
backend::TextureFormat getLdrFormat(bool translucent) const noexcept;
Epoch getUserEpoch() const { return mUserEpoch; }
double getUserTime() const noexcept {
duration const d = clock::now() - getUserEpoch();
duration d = clock::now() - getUserEpoch();
// convert the duration (whatever it is) to a duration in seconds encoded as double
return std::chrono::duration<double>(d).count();
}
std::pair<backend::Handle<backend::HwRenderTarget>, backend::TargetBufferFlags>
getRenderTarget(FView const& view) const noexcept;
void getRenderTarget(FView const& view,
backend::TargetBufferFlags& outAttachementMask,
backend::Handle<backend::HwRenderTarget>& outTarget) const noexcept;
void recordHighWatermark(size_t watermark) noexcept {
mCommandsHighWatermark = std::max(mCommandsHighWatermark, watermark);

Some files were not shown because too many files have changed in this diff Show More