Add tutorial that describes compression and async loading.
This commit is contained in:
@@ -4,15 +4,19 @@ date: 2018-10-30T13:13:14-07:00
|
||||
menu: main
|
||||
---
|
||||
|
||||
## documentation
|
||||
## tutorials
|
||||
|
||||
- [JavaScript API reference](https://google.github.io/filament/webgl/reference.html)
|
||||
- [Triangle Tutorial](https://google.github.io/filament/webgl/tutorial_triangle.html)
|
||||
- [Redball Tutorial](https://google.github.io/filament/webgl/tutorial_redball.html)
|
||||
- [WebGL Meetup Slides](https://prideout.net/slides/filawasm) (2018)
|
||||
1. [Triangle Tutorial](https://google.github.io/filament/webgl/tutorial_triangle.html)
|
||||
2. [Redball Tutorial](https://google.github.io/filament/webgl/tutorial_redball.html)
|
||||
3. [Suzanne Tutorial](https://google.github.io/filament/webgl/tutorial_suzanne.html)
|
||||
|
||||
## demos
|
||||
|
||||
- [parquet](https://google.github.io/filament/webgl/parquet.html)
|
||||
- [suzanne](https://google.github.io/filament/webgl/suzanne.html)
|
||||
- [knotess](https://prideout.net/knotess/)
|
||||
|
||||
## other documentation
|
||||
|
||||
- [JavaScript API reference](https://google.github.io/filament/webgl/reference.html)
|
||||
- [WebGL Meetup Slides](https://prideout.net/slides/filawasm) (2018)
|
||||
|
||||
@@ -11,7 +11,7 @@ else()
|
||||
|
||||
set(CUSTOM_OUTPUTS)
|
||||
set(CUSTOM_INPUTS)
|
||||
foreach (NAME triangle redball)
|
||||
foreach (NAME triangle redball suzanne)
|
||||
list(APPEND CUSTOM_OUTPUTS ${PROJECT_BINARY_DIR}/demo_${NAME}.html)
|
||||
list(APPEND CUSTOM_OUTPUTS ${PROJECT_BINARY_DIR}/tutorial_${NAME}.html)
|
||||
list(APPEND CUSTOM_OUTPUTS ${PROJECT_BINARY_DIR}/tutorial_${NAME}.js)
|
||||
|
||||
+1
-1
@@ -495,7 +495,7 @@ if __name__ == "__main__":
|
||||
ENABLE_EMBEDDED_DEMO = not args.disable_demo
|
||||
os.makedirs(os.path.realpath(OUTPUT_DIR), exist_ok=True)
|
||||
|
||||
for name in ["triangle", "redball"]:
|
||||
for name in ["triangle", "redball", "suzanne"]:
|
||||
weave(name)
|
||||
tangle(name)
|
||||
generate_demo_html(name)
|
||||
|
||||
@@ -14,6 +14,7 @@
|
||||
<canvas></canvas>
|
||||
<script src="filament.js"></script>
|
||||
<script src="gl-matrix-min.js"></script>
|
||||
<script src="https://unpkg.com/gltumble@1.0.0/gltumble.js"></script>
|
||||
<script src="$SCRIPT"></script>
|
||||
</body>
|
||||
</html>
|
||||
@@ -0,0 +1,25 @@
|
||||
material {
|
||||
name : textured,
|
||||
requires : [ uv0 ],
|
||||
shadingModel : lit,
|
||||
parameters : [
|
||||
{ type : sampler2d, name : albedo },
|
||||
{ type : sampler2d, name : roughness },
|
||||
{ type : sampler2d, name : metallic },
|
||||
{ type : float, name : clearCoat },
|
||||
{ type : sampler2d, name : normal },
|
||||
{ type : sampler2d, name : ao }
|
||||
],
|
||||
}
|
||||
|
||||
fragment {
|
||||
void material(inout MaterialInputs material) {
|
||||
material.normal = texture(materialParams_normal, getUV0()).xyz * 2.0 - 1.0;
|
||||
prepareMaterial(material);
|
||||
material.baseColor = texture(materialParams_albedo, getUV0());
|
||||
material.roughness = texture(materialParams_roughness, getUV0()).r;
|
||||
material.metallic = texture(materialParams_metallic, getUV0()).r;
|
||||
material.clearCoat = materialParams.clearCoat;
|
||||
material.ambientOcclusion = texture(materialParams_ao, getUV0()).r;
|
||||
}
|
||||
}
|
||||
@@ -1,8 +1,11 @@
|
||||
|
||||
This tutorial will describe how to create the redball demo, introducing you to materials and
|
||||
This tutorial will describe how to create the **redball** demo, introducing you to materials and
|
||||
textures.
|
||||
|
||||
You'll need to use a couple command-line tools: `matc` and `cmgen`. You can find these in the
|
||||
For starters, create a text file called `redball.html` and copy over the HTML that we used in the
|
||||
[previous tutorial]. Change the last script tag from `triangle.js` to `redball.js`.
|
||||
|
||||
Next you'll need to get a couple command-line tools: `matc` and `cmgen`. You can find these in the
|
||||
appropriate [Filament release](//github.com/google/filament/releases). You should choose the
|
||||
archive that corresponds to your development machine rather than the one for web.
|
||||
|
||||
@@ -10,7 +13,7 @@ archive that corresponds to your development machine rather than the one for web
|
||||
|
||||
The `matc` 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 [Filament Material System]().
|
||||
information, see the official document describing the [Filament Material System].
|
||||
|
||||
Let's try out `matc`. Create the following file in your favorite text editor and call it
|
||||
`plastic.mat`.
|
||||
@@ -52,9 +55,6 @@ tutorial.
|
||||
Next we'll use Filament's `cmgen` tool to consume a HDR environment map in latlong format, and
|
||||
produce two cubemap files: a mipmapped IBL and a blurry skybox.
|
||||
|
||||
[pillars_2k.hdr]:
|
||||
//github.com/google/filament/blob/master/third_party/environments/pillars_2k.hdr
|
||||
|
||||
Download [pillars_2k.hdr], then invoke the following command in your terminal.
|
||||
|
||||
```bash
|
||||
@@ -62,19 +62,20 @@ cmgen -x . --format=ktx --size=256 --extract-blur=0.1 pillars_2k.hdr
|
||||
```
|
||||
|
||||
You should now have a `pillars_2k` folder containing a couple KTX files for the IBL and skybox, as
|
||||
well as a text file with spherical harmonics coefficients. Move the KTX files into your project
|
||||
folder. You can discard the text file because the IBL KTX contains these coefficients in its
|
||||
metadata.
|
||||
well as a text file with spherical harmonics coefficients. You can discard the text file because the
|
||||
IBL KTX contains these coefficients in its metadata.
|
||||
|
||||
## Create HTML and JavaScript
|
||||
|
||||
Create a text file called `redball.html` and copy over the HTML that we used in the [previous
|
||||
tutorial](tutorial_triangle.html). Change the last script tag from `triangle.js` to `redball.js`.
|
||||
## Create JavaScript
|
||||
|
||||
Next, create `redball.js` with the following content.
|
||||
|
||||
```js {fragment="root"}
|
||||
Filament.init([ 'plastic.filamat', 'pillars_2k_ibl.ktx', 'pillars_2k_skybox.ktx' ], () => {
|
||||
const environ = 'pillars_2k';
|
||||
const ibl_url = `${environ}/${environ}_ibl.ktx`;
|
||||
const sky_url = `${environ}/${environ}_skybox.ktx`;
|
||||
const filamat_url = 'plastic.filamat'
|
||||
|
||||
Filament.init([ filamat_url, ibl_url, sky_url ], () => {
|
||||
// Create some global aliases to enums for convenience.
|
||||
window.VertexAttribute = Filament.VertexAttribute;
|
||||
window.AttributeType = Filament.VertexBuffer$AttributeType;
|
||||
@@ -109,7 +110,7 @@ class App {
|
||||
this.resize();
|
||||
this.render = this.render.bind(this);
|
||||
this.resize = this.resize.bind(this);
|
||||
window.addEventListener("resize", this.resize);
|
||||
window.addEventListener('resize', this.resize);
|
||||
window.requestAnimationFrame(this.render);
|
||||
}
|
||||
|
||||
@@ -139,14 +140,14 @@ Next let's create a material instance from the package that we built at the begi
|
||||
Replace the **create material** comment with the following snippet.
|
||||
|
||||
```js {fragment="create material"}
|
||||
const material = engine.createMaterial('plastic.filamat');
|
||||
const material = engine.createMaterial(filamat_url);
|
||||
const matinstance = material.createInstance();
|
||||
|
||||
const red = [0.8, 0.0, 0.0];
|
||||
matinstance.setColorParameter("baseColor", Filament.RgbType.sRGB, red);
|
||||
matinstance.setFloatParameter("roughness", 0.5);
|
||||
matinstance.setFloatParameter("clearCoat", 1.0);
|
||||
matinstance.setFloatParameter("clearCoatRoughness", 0.3);
|
||||
matinstance.setColorParameter('baseColor', Filament.RgbType.sRGB, red);
|
||||
matinstance.setFloatParameter('roughness', 0.5);
|
||||
matinstance.setFloatParameter('clearCoat', 1.0);
|
||||
matinstance.setFloatParameter('clearCoatRoughness', 0.3);
|
||||
```
|
||||
|
||||
The next step is to create a renderable for the sphere. To help with this, we'll use the `IcoSphere`
|
||||
@@ -232,7 +233,7 @@ const format = Filament.PixelDataFormat.RGBM;
|
||||
const datatype = Filament.PixelDataType.UBYTE;
|
||||
|
||||
// Create a Texture object for the mipmapped cubemap.
|
||||
const ibl_package = Filament.Buffer(Filament.assets['pillars_2k_ibl.ktx']);
|
||||
const ibl_package = Filament.Buffer(Filament.assets[ibl_url]);
|
||||
const iblktx = new Filament.KtxBundle(ibl_package);
|
||||
|
||||
const ibltex = Filament.Texture.Builder()
|
||||
@@ -251,7 +252,7 @@ for (let level = 0; level < iblktx.getNumMipLevels(); ++level) {
|
||||
}
|
||||
|
||||
// Parse the spherical harmonics metadata.
|
||||
const shstring = iblktx.getMetadata("sh");
|
||||
const shstring = iblktx.getMetadata('sh');
|
||||
const shfloats = shstring.split(/\s/, 9 * 3).map(parseFloat);
|
||||
|
||||
// Build the IBL object and insert it into the scene.
|
||||
@@ -268,7 +269,7 @@ Filament provides a JavaScript utility to make this simpler,
|
||||
simply replace the **create IBL** comment with the following snippet.
|
||||
|
||||
```js {fragment="create IBL"}
|
||||
const indirectLight = engine.createIblFromKtx('pillars_2k_ibl.ktx');
|
||||
const indirectLight = engine.createIblFromKtx(ibl_url);
|
||||
indirectLight.setIntensity(50000);
|
||||
scene.setIndirectLight(indirectLight);
|
||||
```
|
||||
@@ -280,7 +281,7 @@ Without a skybox, the reflections on the ball are not representative of its surr
|
||||
Here's one way to create a texture for the skybox:
|
||||
|
||||
```js
|
||||
const sky_package = Filament.Buffer(Filament.assets['pillars_2k_skybox.ktx']);
|
||||
const sky_package = Filament.Buffer(Filament.assets[sky_url]);
|
||||
const skyktx = new Filament.KtxBundle(sky_package);
|
||||
const skytex = Filament.Texture.Builder()
|
||||
.width(skyktx.info().pixelWidth)
|
||||
@@ -300,9 +301,19 @@ Filament provides a Javascript utility to make this easier.
|
||||
Replace **create skybox** with the following.
|
||||
|
||||
```js {fragment="create skybox"}
|
||||
const skybox = engine.createSkyFromKtx('pillars_2k_skybox.ktx');
|
||||
const skybox = engine.createSkyFromKtx(sky_url);
|
||||
scene.setSkybox(skybox);
|
||||
```
|
||||
|
||||
That's it, we now have a shiny red ball floating in an environment! The complete JavaScript
|
||||
file is available [here](tutorial_redball.js).
|
||||
That's it, we now have a shiny red ball floating in an environment! The complete JavaScript file is
|
||||
available [here](tutorial_redball.js).
|
||||
|
||||
In the [next tutorial], we'll take a closer look at textures and interaction.
|
||||
|
||||
[pillars_2k.hdr]:
|
||||
//github.com/google/filament/blob/master/third_party/environments/pillars_2k.hdr
|
||||
|
||||
[next tutorial]: tutorial_suzanne.html
|
||||
[previous tutorial]: tutorial_triangle.html
|
||||
[Filament release]: //github.com/google/filament/releases
|
||||
[Filament Material System]: https://google.github.io/filament/Materials.md.html
|
||||
|
||||
@@ -0,0 +1,311 @@
|
||||
|
||||
This tutorial will describe how to create the **suzanne** demo, introducing you to compressed
|
||||
textures, mipmap generation, asynchronous texture loading, and trackball rotation.
|
||||
|
||||
Much like the [previous tutorial], you'll need to use the command-line tools that can be found in
|
||||
the appropriate [Filament release] for your development machine. In addition to `matc` and `cmgen`,
|
||||
we'll also be using `filamesh` and `mipgen`.
|
||||
|
||||
## Create filamesh file
|
||||
|
||||
Filament does not have an asset loading system, but it does provide a simple binary mesh format
|
||||
called `filamesh` for demonstration purposes. Let's create a filamesh file for suzanne by converting
|
||||
[this OBJ file]:
|
||||
|
||||
```bash
|
||||
filamesh monkey.obj suzanne.filamesh
|
||||
```
|
||||
|
||||
## Create mipmapped textures
|
||||
|
||||
Next, let's create mipmapped KTX files using filament's `mipgen` 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 [monkey folder], then do:
|
||||
|
||||
```bash
|
||||
# Create mipmaps for base color and two compressed variants.
|
||||
mipgen albedo.png albedo.ktx
|
||||
mipgen --compression=astc_fast_ldr_4x4 albedo.png albedo_astc.ktx
|
||||
mipgen --compression=s3tc_rgb_dxt1 albedo.png albedo_s3tc.ktx
|
||||
|
||||
# Create mipmaps for the normal map and a compressed variant.
|
||||
mipgen --kernel=NORMALS --linear normal.png normal.ktx
|
||||
mipgen --kernel=NORMALS --linear --compression=etc_rgb8_normalxyz_40 normal.png normal_etc.ktx
|
||||
|
||||
# Create mipmaps for the single-component roughness map and a compressed variant.
|
||||
mipgen --grayscale roughness.png roughness.ktx
|
||||
mipgen --grayscale --compression=etc_r11_numeric_40 roughness.png roughness_etc.ktx
|
||||
|
||||
# Create mipmaps for the single-component metallic map and a compressed variant.
|
||||
mipgen --grayscale metallic.png metallic.ktx
|
||||
mipgen --grayscale --compression=etc_r11_numeric_40 metallic.png metallic_etc.ktx
|
||||
|
||||
# Create mipmaps for the single-component occlusion map and a compressed variant.
|
||||
mipgen --grayscale ao.png ao.ktx
|
||||
mipgen --grayscale --compression=etc_r11_numeric_40 ao.png ao_etc.ktx
|
||||
```
|
||||
|
||||
For more information on mipgen's arguments and supported formats, do `mipgen --help`.
|
||||
|
||||
In a production setting, you'd want to invoke these commands with a script or build system.
|
||||
|
||||
## Bake environment map
|
||||
|
||||
Much like the [previous tutorial] we need to use Filament's `cmgen` tool to produce cubemap files,
|
||||
but this time we'll create compressed variants.
|
||||
|
||||
Download [syferfontein_18d_clear_2k.hdr], then invoke the following commands in your terminal.
|
||||
|
||||
```bash
|
||||
# Create S3TC variant of the IBL, then rename it to have a _s3tc suffix.
|
||||
cmgen -x . --format=ktx --size=256 --extract-blur=0.1 --compression=s3tc_rgba_dxt5 \
|
||||
syferfontein_18d_clear_2k.hdr
|
||||
cd syfer* ; mv syfer*_ibl.ktx syferfontein_18d_clear_2k_ibl_s3tc.ktx ; cd -
|
||||
|
||||
# Create ETC variant of the IBL, then rename it to have a _s3tc suffix.
|
||||
cmgen -x . --format=ktx --size=256 --extract-blur=0.1 --compression=etc_rgba8_rgba_40 \
|
||||
syferfontein_18d_clear_2k.hdr
|
||||
cd syfer* ; mv syfer*_ibl.ktx syferfontein_18d_clear_2k_ibl_etc.ktx ; cd -
|
||||
|
||||
# Create small uncompressed Skybox variant, then rename it to have a _tiny suffix.
|
||||
cmgen -x . --format=ktx --size=64 --extract-blur=0.1 syferfontein_18d_clear_2k.hdr
|
||||
cd syfer* ; mv syfer*_ibl.ktx syferfontein_18d_clear_2k_skybox_tiny.ktx ; cd -
|
||||
|
||||
# Create full-size uncompressed Skybox and IBL
|
||||
cmgen -x . --format=ktx --size=256 --extract-blur=0.1 syferfontein_18d_clear_2k.hdr
|
||||
```
|
||||
|
||||
## Define textured material
|
||||
|
||||
You might recall the `filamat` file we generated in the previous tutorial for red plastic. For this
|
||||
demo, we'll create a material that uses textures for several parameters.
|
||||
|
||||
Create the following textz file call it `textured.mat`. Note that our material definition now
|
||||
requires a `uv0` attribute.
|
||||
|
||||
```text
|
||||
material {
|
||||
name : textured,
|
||||
requires : [ uv0 ],
|
||||
shadingModel : lit,
|
||||
parameters : [
|
||||
{ type : sampler2d, name : albedo },
|
||||
{ type : sampler2d, name : roughness },
|
||||
{ type : sampler2d, name : metallic },
|
||||
{ type : float, name : clearCoat },
|
||||
{ type : sampler2d, name : normal },
|
||||
{ type : sampler2d, name : ao }
|
||||
],
|
||||
}
|
||||
|
||||
fragment {
|
||||
void material(inout MaterialInputs material) {
|
||||
material.normal = texture(materialParams_normal, getUV0()).xyz * 2.0 - 1.0;
|
||||
prepareMaterial(material);
|
||||
material.baseColor = texture(materialParams_albedo, getUV0());
|
||||
material.roughness = texture(materialParams_roughness, getUV0()).r;
|
||||
material.metallic = texture(materialParams_metallic, getUV0()).r;
|
||||
material.clearCoat = materialParams.clearCoat;
|
||||
material.ambientOcclusion = texture(materialParams_ao, getUV0()).r;
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
Next, invoke `matc` as follows.
|
||||
|
||||
```bash
|
||||
matc -O -a opengl -p mobile -o textured.filamat textured.mat
|
||||
```
|
||||
|
||||
You should now have a material archive in your working directory. For the suzanne asset, the normal
|
||||
map adds scratches, the albedo map paints the eyes white, and so on. For more information on
|
||||
materials, consult the official document describing the [Filament Material System].
|
||||
|
||||
## Create app skeleton
|
||||
|
||||
Create a text file called `suzanne.html` and copy over the HTML that we used in the [previous
|
||||
tutorial]. Change the last script tag from `redball.js` to `suzanne.js`. Next, create `suzanne.js`
|
||||
with the following content.
|
||||
|
||||
```js {fragment="root"}
|
||||
// TODO: declare asset URLs
|
||||
|
||||
Filament.init([ filamat_url, filamesh_url, sky_small_url, ibl_url ], () => {
|
||||
window.app = new App(document.getElementsByTagName('canvas')[0]);
|
||||
});
|
||||
|
||||
class App {
|
||||
constructor(canvas) {
|
||||
this.canvas = canvas;
|
||||
this.engine = Filament.Engine.create(canvas);
|
||||
this.scene = this.engine.createScene();
|
||||
|
||||
const material = this.engine.createMaterial(filamat_url);
|
||||
this.matinstance = material.createInstance();
|
||||
|
||||
const filamesh = this.engine.loadFilamesh(filamesh_url, this.matinstance);
|
||||
this.suzanne = filamesh.renderable;
|
||||
|
||||
// TODO: create sky box and IBL
|
||||
// TODO: initialize gltumble
|
||||
// TODO: fetch larger assets
|
||||
|
||||
this.swapChain = this.engine.createSwapChain();
|
||||
this.renderer = this.engine.createRenderer();
|
||||
this.camera = this.engine.createCamera();
|
||||
this.view = this.engine.createView();
|
||||
this.view.setCamera(this.camera);
|
||||
this.view.setScene(this.scene);
|
||||
this.render = this.render.bind(this);
|
||||
this.resize = this.resize.bind(this);
|
||||
window.addEventListener('resize', this.resize);
|
||||
|
||||
const eye = [0, 0, 4], center = [0, 0, 0], up = [0, 1, 0];
|
||||
this.camera.lookAt(eye, center, up);
|
||||
|
||||
this.resize();
|
||||
window.requestAnimationFrame(this.render);
|
||||
}
|
||||
|
||||
render() {
|
||||
// TODO: apply gltumble matrix
|
||||
this.renderer.render(this.swapChain, this.view);
|
||||
window.requestAnimationFrame(this.render);
|
||||
}
|
||||
|
||||
resize() {
|
||||
const dpr = window.devicePixelRatio;
|
||||
const width = this.canvas.width = window.innerWidth * dpr;
|
||||
const height = this.canvas.height = window.innerHeight * dpr;
|
||||
this.view.setViewport([0, 0, width, height]);
|
||||
|
||||
const aspect = width / height;
|
||||
const Fov = Filament.Camera$Fov, fov = aspect < 1 ? Fov.HORIZONTAL : Fov.VERTICAL;
|
||||
this.camera.setProjectionFov(45, aspect, 1.0, 10.0, fov);
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
Our app will use 10 downloaded assets, but it only requires 4 of them to be present for `App`
|
||||
construction. We'll download the other 6 assets after construction. By using a progressive loading
|
||||
strategy, we can reduce the perceived load time.
|
||||
|
||||
Next we need to supply the URLs for various assets. This is actually a bit tricky, because different
|
||||
clients have different capabilities for compressed textures.
|
||||
|
||||
To help you download only the texture assets that you need, Filament provides a
|
||||
`getSupportedFormatSuffix` function. This takes a space-separated list of desired format types
|
||||
(`etc`, `s3tc`, or `astc`) that the app developer knows is available from the server. The function
|
||||
performs an intersection of the *desired* set with the *supported* set, then returns an appropriate
|
||||
string -- which might be empty.
|
||||
|
||||
In our case, we know that our web server will have `etc` and `s3tc` variants for the IBL, `astc` and
|
||||
`s3tc` variants for albedo, and `etc` variants for the other textures. The uncompressed variants
|
||||
(empty string) are always available as a last resort. Go ahead and replace the **declare asset
|
||||
URLs** comment with the following snippet.
|
||||
|
||||
```js {fragment="declare asset URLs"}
|
||||
const ibl_suffix = Filament.getSupportedFormatSuffix('etc s3tc');
|
||||
const albedo_suffix = Filament.getSupportedFormatSuffix('astc s3tc');
|
||||
const texture_suffix = Filament.getSupportedFormatSuffix('etc');
|
||||
|
||||
const environ = 'syferfontein_18d_clear_2k'
|
||||
const ibl_url = `${environ}/${environ}_ibl${ibl_suffix}.ktx`;
|
||||
const sky_small_url = `${environ}/${environ}_skybox_tiny.ktx`;
|
||||
const sky_large_url = `${environ}/${environ}_skybox.ktx`;
|
||||
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';
|
||||
```
|
||||
|
||||
## Create skybox and IBL
|
||||
|
||||
Next, let's create the low-resolution skybox and IBL in the `App` constructor.
|
||||
|
||||
```js {fragment="create sky box and IBL"}
|
||||
this.skybox = this.engine.createSkyFromKtx(sky_small_url);
|
||||
this.scene.setSkybox(this.skybox);
|
||||
this.indirectLight = this.engine.createIblFromKtx(ibl_url);
|
||||
this.indirectLight.setIntensity(100000);
|
||||
this.scene.setIndirectLight(this.indirectLight);
|
||||
```
|
||||
|
||||
This allows users to see a reasonable background fairly quickly, before larger assets have finished
|
||||
loading in.
|
||||
|
||||
## Fetch assets asychronously
|
||||
|
||||
Next we'll invoke the `Filament.fetch` function from within the app constructor. This function is
|
||||
very similar to `Filament.init`. It takes a list of asset URLs and a callback function that triggers
|
||||
when the assets have finished downloading.
|
||||
|
||||
In our callback, we'll make several `setTextureParameter` 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.
|
||||
|
||||
```js {fragment="fetch larger assets"}
|
||||
Filament.fetch([sky_large_url, albedo_url, roughness_url, metallic_url, normal_url, ao_url], () => {
|
||||
const albedo = this.engine.createTextureFromKtx(albedo_url, {srgb: true});
|
||||
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);
|
||||
this.matinstance.setTextureParameter('roughness', roughness, sampler);
|
||||
this.matinstance.setTextureParameter('metallic', metallic, sampler);
|
||||
this.matinstance.setTextureParameter('normal', normal, sampler);
|
||||
this.matinstance.setTextureParameter('ao', ao, sampler);
|
||||
|
||||
// Replace low-res skybox with high-res skybox.
|
||||
this.engine.destroySkybox(this.skybox);
|
||||
this.skybox = this.engine.createSkyFromKtx(sky_large_url);
|
||||
this.scene.setSkybox(this.skybox);
|
||||
|
||||
this.scene.addEntity(this.suzanne);
|
||||
});
|
||||
```
|
||||
|
||||
## Introduce trackball rotation
|
||||
|
||||
Add the following script tag to your HTML file. This imports a small third-party library that
|
||||
listens for drag events and computes a rotation matrix.
|
||||
|
||||
```html
|
||||
<script src="https://unpkg.com/gltumble"></script>
|
||||
```
|
||||
|
||||
Next, replace the **initialize gltumble** and **apply gltumble matrix** comments with the following
|
||||
two code snippets.
|
||||
|
||||
```js {fragment="initialize gltumble"}
|
||||
this.trackball = new Trackball(canvas, {startSpin: 0.035});
|
||||
```
|
||||
|
||||
```js {fragment="apply gltumble matrix"}
|
||||
const tcm = this.engine.getTransformManager();
|
||||
const inst = tcm.getInstance(this.suzanne);
|
||||
tcm.setTransform(inst, this.trackball.getMatrix());
|
||||
inst.delete();
|
||||
```
|
||||
|
||||
That's it, we now have a fast-loading interactive demo. The complete JavaScript file is available
|
||||
[here](tutorial_suzanne.js).
|
||||
|
||||
[Filament release]: //github.com/google/filament/releases
|
||||
[previous tutorial]: tutorial_redball.html
|
||||
[Filament Material System]: https://google.github.io/filament/Materials.md.html
|
||||
[this OBJ file]: https://github.com/google/filament/blob/master/assets/models/monkey/monkey.obj
|
||||
[monkey folder]: https://github.com/google/filament/blob/master/assets/models/monkey
|
||||
|
||||
[syferfontein_18d_clear_2k.hdr]:
|
||||
//github.com/google/filament/blob/master/third_party/environments/syferfontein_18d_clear_2k.hdr
|
||||
@@ -39,7 +39,7 @@ class App {
|
||||
// TODO: create entities
|
||||
this.render = this.render.bind(this);
|
||||
this.resize = this.resize.bind(this);
|
||||
window.addEventListener("resize", this.resize);
|
||||
window.addEventListener('resize', this.resize);
|
||||
window.requestAnimationFrame(this.render);
|
||||
}
|
||||
render() {
|
||||
@@ -71,16 +71,20 @@ that define a PBR material. We'll learn more about material packages in the next
|
||||
## Spawn a local server
|
||||
|
||||
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:
|
||||
file system. One way around this is to create a temporary server using Python or node:
|
||||
|
||||
```bash
|
||||
python3 -m http.server # Python 3
|
||||
python -m SimpleHTTPServer # Python 2.7
|
||||
npx http-server -p 8000 # nodejs
|
||||
```
|
||||
|
||||
To see if this works, navigate to [http://localhost:8000](http://localhost:8000) and check if you
|
||||
can load the page without any errors appearing in the developer console.
|
||||
|
||||
Take care not to use Python's simple server in production since it does not serve WebAssembly files
|
||||
with the correct MIME type.
|
||||
|
||||
## Create the Engine and Scene
|
||||
|
||||
We now have a basic skeleton that can respond to paint and resize events. Let's start adding
|
||||
@@ -257,5 +261,8 @@ this.camera.setProjection(Projection.ORTHO, -aspect, aspect, -1, 1, 0, 1);
|
||||
```
|
||||
|
||||
You should now have a spinning triangle! The completed JavaScript is available
|
||||
[here](tutorial_triangle.js). In the next tutorial, we'll take a closer look at Filament
|
||||
materials and 3D rendering.
|
||||
[here](tutorial_triangle.js).
|
||||
|
||||
In the [next tutorial], we'll take a closer look at Filament materials and 3D rendering.
|
||||
|
||||
[next tutorial]: tutorial_redball.html
|
||||
|
||||
@@ -90,6 +90,7 @@ add_mesh("third_party/shader_ball/shader_ball.obj" "shader_ball.filamesh")
|
||||
|
||||
# Generate IBL and skybox images using cmgen.
|
||||
set(CMGEN_ARGS -x . --format=ktx --size=256 --extract-blur=0.1)
|
||||
set(CMGEN_ARGS_TINY -x . --format=ktx --size=64 --extract-blur=0.1)
|
||||
set(CMGEN_ARGS_S3TC ${CMGEN_ARGS} --compression=s3tc_rgba_dxt5)
|
||||
set(CMGEN_ARGS_ETC ${CMGEN_ARGS} --compression=etc_rgba8_rgba_40)
|
||||
function(add_envmap SOURCE TARGET)
|
||||
@@ -103,12 +104,14 @@ function(add_envmap SOURCE TARGET)
|
||||
set(target_envmap_etc "${SERVER_DIR}/${TARGET}/${TARGET}_ibl_etc.ktx")
|
||||
set(target_envmap_s3tc "${SERVER_DIR}/${TARGET}/${TARGET}_ibl_s3tc.ktx")
|
||||
|
||||
set(target_skyboxes ${target_skyboxes} ${target_skybox} PARENT_SCOPE)
|
||||
set(target_skybox_tiny "${SERVER_DIR}/${TARGET}/${TARGET}_skybox_tiny.ktx")
|
||||
|
||||
set(target_skyboxes ${target_skyboxes} ${target_skybox} ${target_skybox_tiny} PARENT_SCOPE)
|
||||
|
||||
set(target_envmaps ${target_envmaps}
|
||||
${target_envmap} ${target_envmap_etc} ${target_envmap_s3tc} PARENT_SCOPE)
|
||||
|
||||
add_custom_command(OUTPUT ${target_skybox}
|
||||
add_custom_command(OUTPUT ${target_skybox} ${target_skybox_tiny}
|
||||
${target_envmap} ${target_envmap_etc} ${target_envmap_s3tc}
|
||||
|
||||
# First create an S3TC-encoded envmap, then rename it.
|
||||
@@ -119,6 +122,10 @@ function(add_envmap SOURCE TARGET)
|
||||
COMMAND cmgen ${CMGEN_ARGS_ETC} ${source_envmap}
|
||||
COMMAND mv ${target_envmap} ${target_envmap_etc}
|
||||
|
||||
# Create a low-resolution skybox, then rename it.
|
||||
COMMAND cmgen ${CMGEN_ARGS_TINY} ${source_envmap}
|
||||
COMMAND mv ${target_skybox} ${target_skybox_tiny}
|
||||
|
||||
# Finally, create KTX files for the uncompressed envmap.
|
||||
COMMAND cmgen ${CMGEN_ARGS} ${source_envmap}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user