Test Yourself!

These ten questions turn on Three.js 30,815 behaviors that surprise developers who know the library well: chained calls, shared materials, stale matrices, asynchronous renderers, color spaces, program caching, GPU memory and the renderer's counters. Save each page beside three-map.js and bookshelf.js from demos/ch05 (and Canvas 2D's booknest-cover-1.png) and run it with the check.mjs script of Test Yourself!, which is how the answers in Appendix H were produced in Chrome 154 1 . Write down what each line prints, and why, before you look.

Questions

questions-1-5.html: the scene graph, clones, matrices and WebGPURendererHTML
<!-- 1. A light added and positioned in one chained line. How many draw calls does the
        frame make, and where is the scene? (Section 5.2.1) -->
<!-- 2. A book cloned and its clone recolored. What color is the original? (Section 5.2.1) -->
<!-- 3. A new Vector3 assigned to a mesh's position. What happens? (Section 5.2.1) -->
<!-- 4. A book moved, then its world matrix read before and after getWorldPosition().
        What x does each read give? (Sections 5.2.1 and 5.9.1) -->
<!-- 5. A WebGPURenderer asked to render straight after construction. What does it do?
        (Section 5.13.2) -->
<script src="three-map.js"></script>
<script type="module">
import * as THREE from 'three';
import { WebGPURenderer } from 'three/webgpu';
import * as BN from './bookshelf.js';
const renderer = new THREE.WebGLRenderer();
const scene = new THREE.Scene(), camera = new THREE.PerspectiveCamera(35, 2, 0.1, 50);
camera.position.z = 3;
const book = BN.createBook(BN.BOOKS[0]);
scene.add(book);
scene.add(new THREE.DirectionalLight()).position.set(0, 0, 10);
renderer.render(scene, camera);
console.log('1:', renderer.info.render.calls, scene.position.toArray().join(' '));
const copy = book.clone();
copy.material.color.set('#b5452f');
console.log('2:', book.material.color.getHexString());
try {
  book.position = new THREE.Vector3(1, 2, 3);
  console.log('3:', book.position.toArray().join(' '));
} catch (error) { console.log('3:', error.constructor.name, error.message); }
book.position.x = 2;
const before = book.matrixWorld.elements[12];
console.log('4:', before, book.getWorldPosition(new THREE.Vector3()).x,
  book.matrixWorld.elements[12]);
const gpu = new WebGPURenderer();
try {
  gpu.render(scene, camera);
  console.log('5: rendered');
} catch (error) { console.log('5:', error.message); }
window.__done = true;
</script>
questions-6-10.html: color spaces, lil-gui, programs, memory and renderer.infoHTML
<!-- 6. A texture from TextureLoader, and a Color set from BookNest's plank hex. What
        colorSpace and red channel do they report? (Sections 5.4.5 and 5.6.1) -->
<!-- 7. lil-gui bound straight to a material's color. What hex does its swatch show?
        (Section 5.15.3) -->
<!-- 8. onBeforeCompile assigned after the first frame. Does the next frame use it?
        (Sections 5.12.2 and 5.16.2) -->
<!-- 9. A mesh removed from the scene. Is its geometry still on the GPU? (Section 5.14.3) -->
<!-- 10. The shelf scene rendered, then a one-mesh scene. What does renderer.info.render
         report? (Section 5.14.4) -->
<script src="three-map.js"></script>
<script type="module">
import * as THREE from 'three';
import GUI from 'https://cdn.jsdelivr.net/npm/lil-gui@0.21.0/+esm';
import * as BN from './bookshelf.js';
const texture = new THREE.TextureLoader().load('booknest-cover-1.png');
const plankColor = new THREE.Color('#8a6240');
console.log('6:', JSON.stringify(texture.colorSpace), plankColor.r.toFixed(4));
const { renderer, scene, camera } = BN.createStage({ width: 320, height: 160 });
const shelf = BN.createShelf();
scene.add(shelf);
const plank = shelf.getObjectByName('plank');
const gui = new GUI();
gui.addColor(plank.material, 'color');
console.log('7:', gui.controllers[0].$text.value);
renderer.render(scene, camera);
const programs = () => renderer.info.programs.length;
const first = programs();
BN.addSaturation(plank.material, { value: 0 });
renderer.render(scene, camera);
const second = programs();
plank.material.needsUpdate = true;
renderer.render(scene, camera);
console.log('8:', first, second, programs());
shelf.remove(plank);
renderer.render(scene, camera);
const kept = renderer.info.memory.geometries;
plank.geometry.dispose();
console.log('9:', kept, renderer.info.memory.geometries);
renderer.render(scene, camera);
const calls = renderer.info.render.calls;
const small = new THREE.Scene().add(new THREE.Mesh(new THREE.BoxGeometry()));
renderer.render(small, camera);
console.log('10:', calls, renderer.info.render.calls, renderer.info.render.frame);
window.__done = true;
</script>