Flesh out the JavaScript tutorials.

This commit is contained in:
Philip Rideout
2018-10-18 08:16:55 -07:00
parent b0853b5eb6
commit 356c3a2c24
3 changed files with 75 additions and 25 deletions

View File

@@ -34,8 +34,9 @@ PREAMBLE = """
The markdown source for this tutorial is not only used to generate this
website, 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). This ensures that the tutorial is kept up to date and
that the code is highly readable.
(generating JS). In the code samples, you'll often see
`// TODO: <some task>`. These are special markers that get replaced by
subsequent code blocks.
"""
# The pipenv command in the shebang needs a certain working directory.
@@ -213,6 +214,6 @@ if __name__ == "__main__":
copy_built_file('samples/web/public/pillars_2k/pillars_2k_skybox.ktx')
copy_built_file('samples/web/public/pillars_2k/pillars_2k_ibl.ktx')
copy_demo_filamat('bakedColor', 'triangle')
copy_demo_filamat('sandboxLit', 'redball')
copy_demo_filamat('sandboxLit', 'plastic')
if len(sys.argv) > 1 and sys.argv[1] == 'serve':
spawn_local_server()

View File

@@ -1,19 +1,20 @@
## Create materials and textures
TODO: Describe how to use `matc` and `mipgen` to create `simple.filamat` and the two `pillars_2k`
TODO: Describe how to use `matc` and `cmgen` to create `plastic.filamat` and the two `pillars_2k`
KTX files.
## Start your project
Create a text file called `redball.html` and fill it with the same HTML you used in the
[previous tutorial]() but change the last `<script>` src from `triangle.js` to `redball.js`.
Create a text file called `redball.html` and fill it with the same HTML you used in the [previous
tutorial](tutorial_triangle.html) but change the last script tag from `triangle.js` to
`redball.js`.
Next, create `redball.js` with the following content.
```js {fragment="root"}
Filament.loadMathExtensions();
Filament.init([ 'redball.filamat', 'pillars_2k_ibl.ktx', 'pillars_2k_skybox.ktx' ], () => {
Filament.init([ 'plastic.filamat', 'pillars_2k_ibl.ktx', 'pillars_2k_skybox.ktx' ], () => {
// Create some global aliases to enums for convenience.
window.VertexAttribute = Filament.VertexAttribute;
window.AttributeType = Filament.VertexBuffer$AttributeType;
@@ -76,22 +77,36 @@ class App {
}
```
TODO: Verbiage
The above boilerplate should be familiar to you from the previous tutorial, although it loads in a
new set of assets and the camera uses a perspective projection.
Next let's create a material instance from the package that we built at the beginning the tutorial.
Replace the **create material** todo with the following snippet.
```js {fragment="create material"}
const material_package = Filament.Buffer(Filament.assets['redball.filamat']);
const material_package = Filament.Buffer(Filament.assets['plastic.filamat']);
const material = engine.createMaterial(material_package);
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("reflectance", 0.3);
matinstance.setFloatParameter("clearCoat", 0.7);
matinstance.setFloatParameter("reflectance", 0.5);
matinstance.setFloatParameter("clearCoat", 1.0);
matinstance.setFloatParameter("clearCoatRoughness", 0.3);
```
TODO: Verbiage
The next step is to create a renderable for the sphere. To help with this, we'll use the `IcoSphere`
utility class, whose constructor takes a LOD. Its job is to subdivide an icosadedron, producing
three arrays:
- `icosphere.vertices` Float32Array of XYZ coordinates.
- `icosphere.tangents` Uint16Array (interpreted as half-floats) encoding the surface orientation
as quaternions.
- `icosphere.triangles` Uint16Array with triangle indices.
Let's go ahead use these arrays to build the vertex buffer and index buffer. Replace **create
sphere** with the following snippet.
```js {fragment="create sphere"}
const renderable = Filament.EntityManager.get().create();
@@ -127,7 +142,14 @@ const tcm = this.engine.getTransformManager();
tcm.setTransform(tcm.getInstance(renderable), transform);
```
TODO: Verbiage
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 `setClearColor`, like we
did in the first tutorial.
The next step is to add some lighting. We'll be creating two types of light sources: a directional
light source that represents the sun, and an image-based light (IBL) defined by one of the KTX files
we built at the start of the demo. First, replace the **create sunlight** todo with the following
snippet.
```js {fragment="create sunlight"}
const sunlight = Filament.EntityManager.get().create();
@@ -144,12 +166,17 @@ Filament.LightManager.Builder(LightType.SUN)
.build(engine, sunlight);
```
TODO: Verbiage
We are using a light type of `SUN`, which is similar to `DIRECTIONAL`, but it has some extra
parameters because Filament will automatically draw a disk into the skybox.
Next let's create a `IndirectLight` object from the KTX IBL. One way of doing this is the following
(don't type this out, there's an easier way).
```js {fragment="create IBL"}
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 iblktx = new Filament.KtxBundle(ibl_package);
const ibltex = Filament.Texture.Builder()
@@ -160,26 +187,39 @@ const ibltex = Filament.Texture.Builder()
.format(Filament.Texture$InternalFormat.RGBA8)
.rgbm(true)
.build(engine);
for (let level = 0; level < iblktx.getNumMipLevels(); ++level) {
const uint8array = iblktx.getCubeBlob(level).getBytes();
const pixelbuffer = Filament.PixelBuffer(uint8array, format, datatype);
ibltex.setImageCube(engine, level, pixelbuffer);
}
// Parse the spherical harmonics metadata.
const shstring = iblktx.getMetadata("sh");
const shfloats = shstring.split(/\s/, 9 * 3).map(parseFloat);
// Build the IBL object and insert it into the scene.
const indirectLight = Filament.IndirectLight.Builder()
.reflections(ibltex)
.irradianceSh(3, shfloats)
.intensity(30000.0)
.intensity(50000.0)
.build(engine);
scene.setIndirectLight(indirectLight);
```
This is a lot of boilerplate, so Filament provides a JavaScript utilitiy to make this simpler;
simply replace the **create IBL** todo with the following snippet. *NOTE: not yet implemented.*
```js
const ibl_package = Filament.Buffer(Filament.assets['pillars_2k_ibl.ktx']);
const indirectLight = Filament.createIblFromKtx(ibl_package);
indirectLight.setIntensity(50000);
scene.setIndirectLight(indirectLight);
```
TODO: Verbiage
At the 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 aren't truly representative of the its surroundings.
Here's one way to create a texture for the skybox:
```js {fragment="create skybox"}
const sky_package = Filament.Buffer(Filament.assets['pillars_2k_skybox.ktx']);
@@ -196,12 +236,18 @@ const skytex = Filament.Texture.Builder()
const uint8array = skyktx.getCubeBlob(0).getBytes();
const pixelbuffer = Filament.PixelBuffer(uint8array, format, datatype);
skytex.setImageCube(engine, 0, pixelbuffer);
```
const skybox = Filament.Skybox.Builder()
.environment(skytex)
.build(engine);
Again, this is a lot of boilerplate, so Filament provides a Javascript utility for you. Replace
**create skybox** with the following. *NOTE: not yet implemented.*
```js
const sky_package = Filament.Buffer(Filament.assets['pillars_2k_skybox.ktx']);
const skytex = Filament.createTextureFromKtx(sky_package, {'rgbm': True});
```
```js {fragment="create skybox"}
const skybox = Filament.Skybox.Builder().environment(skytex).build(engine);
scene.setSkybox(skybox);
```
TODO: Verbiage
This completes the tutorial; the completed JavaScript is available [here](tutorial_redball.js).

View File

@@ -30,9 +30,11 @@ a mobile-friendly page with a full-screen canvas.
```
The above HTML loads three JavaScript files:
- **filament.js** will download and compile the Filament WASM module.
- **gl-matrix-min.js** is a small library that provides vector math functionality.
- **triangle.js** will contain your application code.
- `filament.js` does a couple things:
- Downloads assets and compiles the Filament WASM module.
- Contains high-level utilities, e.g. to simplify loading KTX textures from JavaScript.
- `gl-matrix-min.js` is a small library that provides vector math functionality.
- `triangle.js` will contain your application code.
Go ahead and create `triangle.js` with the following content.
@@ -271,5 +273,6 @@ const Projection = Filament.Camera$Projection;
this.camera.setProjection(Projection.ORTHO, -aspect, aspect, -1, 1, 0, 1);
```
You should now have a spinning triangle! In the next tutorial we'll take a closer look at Filament
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.