### Start Local Server with npx serve Source: https://github.com/mrdoob/three.js/blob/dev/manual/en/installation.html Install Node.js, then use `npx serve` to start a local static file server in the current directory. This allows web browsers to access project files via a URL. ```bash npx serve . ``` -------------------------------- ### Three.js WebGPU Camera Setup and Interaction Source: https://github.com/mrdoob/three.js/blob/dev/examples/webgpu_camera.html This comprehensive JavaScript example initializes a WebGPU renderer, sets up a scene with both perspective and orthographic cameras, adds camera helpers, creates animated meshes and particles, and handles keyboard input to switch the active camera. ```javascript { "imports": { "three": "../build/three.webgpu.js", "three/webgpu": "../build/three.webgpu.js", "three/tsl": "../build/three.tsl.js", "three/addons/": "./jsm/" } } import * as THREE from 'three/webgpu'; let SCREEN_WIDTH = window.innerWidth; let SCREEN_HEIGHT = window.innerHeight; let aspect = SCREEN_WIDTH / SCREEN_HEIGHT; let container; let camera, scene, renderer, mesh; let cameraRig, activeCamera, activeHelper; let cameraPerspective, cameraOrtho; let cameraPerspectiveHelper, cameraOrthoHelper; const frustumSize = 600; init(); function init() { container = document.createElement( 'div' ); document.body.appendChild( container ); scene = new THREE.Scene(); // camera = new THREE.PerspectiveCamera( 50, 0.5 * aspect, 1, 10000 ); camera.position.z = 2500; cameraPerspective = new THREE.PerspectiveCamera( 50, 0.5 * aspect, 150, 1000 ); cameraPerspectiveHelper = new THREE.CameraHelper( cameraPerspective ); scene.add( cameraPerspectiveHelper ); // cameraOrtho = new THREE.OrthographicCamera( 0.5 * frustumSize * aspect / - 2, 0.5 * frustumSize * aspect / 2, frustumSize / 2, frustumSize / - 2, 150, 1000 ); cameraOrthoHelper = new THREE.CameraHelper( cameraOrtho ); scene.add( cameraOrthoHelper ); // activeCamera = cameraPerspective; activeHelper = cameraPerspectiveHelper; // counteract different front orientation of cameras vs rig cameraOrtho.rotation.y = Math.PI; cameraPerspective.rotation.y = Math.PI; cameraRig = new THREE.Group(); cameraRig.add( cameraPerspective ); cameraRig.add( cameraOrtho ); scene.add( cameraRig ); // mesh = new THREE.Mesh( new THREE.SphereGeometry( 100, 16, 8 ), new THREE.MeshBasicMaterial( { color: 0xffffff, wireframe: true } ) ); scene.add( mesh ); const mesh2 = new THREE.Mesh( new THREE.SphereGeometry( 50, 16, 8 ), new THREE.MeshBasicMaterial( { color: 0x00ff00, wireframe: true } ) ); mesh2.position.y = 150; mesh.add( mesh2 ); const mesh3 = new THREE.Mesh( new THREE.SphereGeometry( 5, 16, 8 ), new THREE.MeshBasicMaterial( { color: 0x0000ff, wireframe: true } ) ); mesh3.position.z = 150; cameraRig.add( mesh3 ); // const geometry = new THREE.BufferGeometry(); const vertices = []; for ( let i = 0; i < 10000; i ++ ) { vertices.push( THREE.MathUtils.randFloatSpread( 2000 ) ); // x vertices.push( THREE.MathUtils.randFloatSpread( 2000 ) ); // y vertices.push( THREE.MathUtils.randFloatSpread( 2000 ) ); // z } geometry.setAttribute( 'position', new THREE.Float32BufferAttribute( vertices, 3 ) ); const particles = new THREE.Points( geometry, new THREE.PointsMaterial( { color: 0xffffff } ) ); scene.add( particles ); // renderer = new THREE.WebGPURenderer( { antialias: true } ); renderer.setPixelRatio( window.devicePixelRatio ); renderer.setSize( SCREEN_WIDTH, SCREEN_HEIGHT ); renderer.setAnimationLoop( animate ); container.appendChild( renderer.domElement ); renderer.setScissorTest( true ); renderer.setClearColor( 0x000000, 1 ); // window.addEventListener( 'resize', onWindowResize ); document.addEventListener( 'keydown', onKeyDown ); } // function onKeyDown( event ) { switch ( event.keyCode ) { case 79: /*O*/ activeCamera = cameraOrtho; activeHelper = cameraOrthoHelper; break; case 80: /*P*/ activeCamera = cameraPerspective; activeHelper = cameraPerspectiveHelper; break; } } // function onWindowResize() { SCREEN_WIDTH = window.innerWidth; SCREEN_HEIGHT = window.innerHeight; aspect = SCREEN_WIDTH / SCREEN_HEIGHT; renderer.setSize( SCREEN_WIDTH, SCREEN_HEIGHT ); camera.aspect = 0.5 * aspect; camera.updateProjectionMatrix(); cameraPerspective.aspect = 0.5 * aspect; cameraPerspective.updateProjectionMatrix(); cameraOrtho.left = - 0.5 * frustumSize * aspect / 2; cameraOrtho.right = 0.5 * frustumSize * aspect / 2; cameraOrtho.top = frustumSize / 2; cameraOrtho.bottom = - frustumSize / 2; cameraOrtho.updateProjectionMatrix(); } // function animate() { render(); } function render() { const r = Date.now() * 0.0005; mesh.position.x = 700 * Math.cos( r ); mesh.position.z = 700 * Math.sin( r ); mesh.position.y = 700 * Math.sin( r ); mesh.children[ 0 ].position.x = 70 * Math.cos( 2 * r ); mesh.children[ 0 ].position.z = 70 * Math.sin( r ); if ( activeCamera === cameraPerspective ) { cameraPerspective.fov = 35 + 30 * Math.sin( 0.5 * r ); cameraPerspective.far = mesh.position.length(); cameraPerspective.updateProjectionMatrix(); cameraPerspectiveHelper.update(); cameraPerspectiveHelper.visible = true; cameraOrthoHelper.visible = false; } else { cameraOrtho.far = mesh.position.length(); cameraOrtho.updateProjectionMatrix(); cameraOrthoHelper.update(); cameraOrthoHelper.visible = true; cameraPerspectiveHelper.visible = false; } cameraRig.lookAt( mesh.position ); // activeHelper.visible = false; renderer.setClearColor( 0x000000, 1 ); renderer.setScissor( 0, 0, SCREEN_WIDTH / 2, SCREEN_HEIGHT ); re ``` -------------------------------- ### .setup(builder) Source: https://github.com/mrdoob/three.js/blob/dev/docs/pages/ClippingNode.html Setups the node based on the selected scope, overriding Node#setup. ```APIDOC ## .setup(builder) ### Description Setups the node depending on the selected scope. ### Parameters #### Method Parameters - **builder** (NodeBuilder) - Required - The current node builder. ### Returns - (Node) - The result node. ``` -------------------------------- ### .setup() Source: https://github.com/mrdoob/three.js/blob/dev/docs/pages/SampleNode.html Sets up the node by sampling with the default UV accessor, overriding the base Node's setup method. ```APIDOC ## Method: .setup() ### Description Sets up the node by sampling with the default UV accessor. Overrides Node#setup. ### Returns - **Node** - The result of the callback function when called with the UV node. ``` -------------------------------- ### .setup( builder ) Source: https://github.com/mrdoob/three.js/blob/dev/docs/pages/LightsNode.html The implementation makes sure that for each light in the scene there is a corresponding light node. By building the light nodes and evaluating the lighting model the outgoing light is computed. ```APIDOC ## .setup( builder ) ### Description The implementation makes sure that for each light in the scene there is a corresponding light node. By building the light nodes and evaluating the lighting model the outgoing light is computed. ### Parameters - **builder** ([NodeBuilder](NodeBuilder.html)) - A reference to the current node builder. ### Returns [Node](Node.html). - A node representing the outgoing light. ``` -------------------------------- ### Import ParametricGeometry Addon (Three.js) Source: https://github.com/mrdoob/three.js/blob/dev/docs/pages/ParametricGeometry.html Import `ParametricGeometry` explicitly as it is an addon. Refer to the Three.js installation guide for more details on addons. ```javascript import { ParametricGeometry } from 'three/addons/geometries/ParametricGeometry.js'; ``` -------------------------------- ### Main Application Setup with WebGPU Renderer and GUI Source: https://github.com/mrdoob/three.js/blob/dev/examples/webgpu_mesh_batch.html Initializes the camera, WebGPURenderer, scene, OrbitControls, and a GUI for controlling application parameters like object count and opacity. ```javascript function init( forceWebGL = false ) { if ( renderer ) { renderer.dispose(); controls.dispose(); document.body.removeChild( renderer.domElement ); } // camera const aspect = window.innerWidth / window.innerHeight; camera = new THREE.PerspectiveCamera( 70, aspect, 1, 100 ); camera.position.z = 30; // renderer renderer = new THREE.WebGPURenderer( { antialias: true, forceWebGL } ); renderer.setPixelRatio( window.devicePixelRatio ); renderer.setSize( window.innerWidth, window.innerHeight ); renderer.inspector = new Inspector(); renderer.setAnimationLoop( animate ); // scene scene = new THREE.Scene(); scene.background = forceWebGL ? new THREE.Color( 0xffc1c1 ) : new THREE.Color( 0xc1c1ff ); document.body.appendChild( renderer.domElement ); initGeometries(); initMesh(); // controls controls = new OrbitControls( camera, renderer.domElement ); controls.autoRotate = true; controls.autoRotateSpeed = 1.0; // gui gui = renderer.inspector.createParameters( 'Settings' ); gui.add( api, 'webgpu' ).onChange( () => { init( ! api.webgpu ); } ); gui.add( api, 'count', 1, MAX_GEOMETRY_COUNT, 1 ).onChange( initMesh ); gui.add( api, 'dynamic', 0, MAX_GEOMETRY_COUNT, 1 ); gui.add( api, 'opacity', 0, 1 ).onChange( v => { if ( v < 1 ) { material.transparent = true; material.depthWrite = false; } else { material.transparent = false; material.depthWrite = true; } material.opacity = v; material.needsUpdate = true; } ); gui.add( api, 'sortObjects' ); gui.add( api, 'perObjectFrustumCulled' ); gui.add( api, 'useC ``` -------------------------------- ### Initiate Three.js Application and Render Loop Source: https://github.com/mrdoob/three.js/blob/dev/manual/examples/lights-rectarea.html Starts the animation loop by calling `requestAnimationFrame` with the `render` function and then executes the `main` setup function. ```javascript requestAnimationFrame( render ); } main(); ``` -------------------------------- ### Complete three.js WebGPU Tiled Shadow Map Example Source: https://github.com/mrdoob/three.js/blob/dev/examples/webgpu_shadowmap_array.html This comprehensive example initializes a WebGPU renderer, sets up a scene with a camera, lights, and objects, and demonstrates how to integrate TileShadowNode for advanced shadow mapping. It includes scene setup, object creation, and camera controls. ```javascript { "imports": { "three": "../build/three.webgpu.js", "three/webgpu": "../build/three.webgpu.js", "three/tsl": "../build/three.tsl.js", "three/addons/": "./jsm/" } } import * as THREE from 'three/webgpu'; import { mx_fractal_noise_vec3, positionWorld, Fn, color } from 'three/tsl'; import { TileShadowNode } from 'three/addons/tsl/shadows/TileShadowNode.js'; import { TileShadowNodeHelper } from 'three/addons/tsl/shadows/TileShadowNodeHelper.js'; import { OrbitControls } from 'three/addons/controls/OrbitControls.js'; import { Inspector } from 'three/addons/inspector/Inspector.js'; let camera, scene, renderer, timer; let dirLight; let torusKnot, dirGroup; let tsmHelper; init(); async function init() { // Renderer setup renderer = new THREE.WebGPURenderer( { antialias: true } ); renderer.setPixelRatio( window.devicePixelRatio ); renderer.setSize( window.innerWidth, window.innerHeight ); renderer.setAnimationLoop( animate ); renderer.inspector = new Inspector(); renderer.shadowMap.enabled = true; renderer.shadowMap.type = THREE.BasicShadowMap; // renderer.shadowMap.type = THREE.PCFSoftShadowMap; renderer.toneMapping = THREE.ACESFilmicToneMapping; renderer.toneMappingExposure = 1.2; document.body.appendChild( renderer.domElement ); await renderer.init(); camera = new THREE.PerspectiveCamera( 45, window.innerWidth / window.innerHeight, 1, 1000 ); camera.position.set( 45, 60, 100 ); scene = new THREE.Scene(); scene.backgroundNode = color( 0xCCCCFF ); // Brighter blue sky scene.fog = new THREE.Fog( 0xCCCCFF, 700, 1000 ); // Enhanced lighting for a brighter scene scene.add( new THREE.AmbientLight( 0xCCCCFF, 3 ) ); // Main directional light (sun) dirLight = new THREE.DirectionalLight( 0xFFFFAA, 5 ); dirLight.position.set( 0, 80, 30 ); dirLight.castShadow = true; dirLight.shadow.camera.near = 1; dirLight.shadow.camera.far = 200; dirLight.shadow.camera.right = 180; dirLight.shadow.camera.left = - 180; dirLight.shadow.camera.top = 180; dirLight.shadow.camera.bottom = - 160; dirLight.shadow.mapSize.width = 1024 * 4; dirLight.shadow.mapSize.height = 1024 * 4; dirLight.shadow.radius = 1; // Set up the tile shadow mapping const tsm = new TileShadowNode( dirLight, { tilesX: 2, tilesY: 2 } ); dirLight.shadow.shadowNode = tsm; scene.add( dirLight ); tsmHelper = new TileShadowNodeHelper( tsm ); scene.add( tsmHelper ); dirGroup = new THREE.Group(); dirGroup.add( dirLight ); scene.add( dirGroup ); // Create the ground with enhanced texture const planeGeometry = new THREE.PlaneGeometry( 1500, 1500, 2, 2 ); const planeMaterial = new THREE.MeshPhongMaterial( { color: 0x88AA44, shininess: 5, specular: 0x222222 } ); planeMaterial.colorNode = Fn( () => { const noise = mx_fractal_noise_vec3( positionWorld.mul( 0.05 ) ).saturate(); // Mix of greens and browns for a more natural ground const green = color( 0.4, 0.7, 0.3 ); const brown = color( 0.6, 0.5, 0.3 ); return noise.x.mix( green, brown ); } )(); const ground = new THREE.Mesh( planeGeometry, planeMaterial ); ground.rotation.x = - Math.PI / 2; ground.receiveShadow = true; scene.add( ground ); // Spread various objects across the scene createScenery(); // Camera controls const controls = new OrbitControls( camera, renderer.domElement ); controls.target.set( 0, 5, 0 ); controls.minDistance = 0.01; controls.maxDistance = 400; controls.maxPolarAngle = Math.PI / 2 - 0.1; // Prevent camera from going below ground controls.update(); timer = new THREE.Timer(); timer.connect( document ); window.addEventListener( 'resize', resize ); } function createScenery() { // 1. Columns using instanced mesh const columnGeometry = new THREE.CylinderGeometry( 0.8, 1, 1, 16 ); const columnMaterial = new THREE.MeshPhongMaterial( { color: 0xDDDDDD, shininess: 20 } ); const columnPositions = []; const columnScales = []; for ( let x = - 100; x <= 100; x += 40 ) { for ( let z = - 100; z <= 100; z += 40 ) { if ( Math.random() > 0.3 ) { const height = 5 + Math.random() * 10; const posX = x + ( Math.random() * 10 - 5 ); const posY = height / 2; const posZ = z + ( Math.random() * 10 - 5 ); columnPositions.push( posX, posY, posZ ); columnScales.push( 1, height, 1 ); // Only scale Y to match height } } } const columnCount = columnPositions.length / 3; const columnInstancedMesh = new THREE.InstancedMesh( columnGeometry, columnMaterial, columnCount ); const matrix = new THREE.Matrix4(); for ( let i = 0; i < columnCount; i ++ ) { const x = columnPositions[ i * 3 ]; const y = columnPositions[ i * 3 + 1 ]; const z = columnPositions[ i * 3 + 2 ]; const scaleY = columnScales[ i * 3 + 1 ]; matrix.makeScale( 1, scaleY, 1 ); matrix.setPosition( x, y, z ); columnInstancedMesh.setMatrixAt( i, matrix ); } columnInstancedMesh.castShadow = true; columnInstancedMesh.receiveShadow = true; scene.add( columnInstancedMesh ); // 2. Add a ce ``` -------------------------------- ### .setupLightingModel() Source: https://github.com/mrdoob/three.js/blob/dev/docs/pages/MeshStandardNodeMaterial.html.md Setups the lighting model. ```APIDOC ## .setupLightingModel() ### Description Setups the lighting model. ### Method .setupLightingModel ### Returns PhysicalLightingModel - The lighting model. ``` -------------------------------- ### Full Example: Initializing and Animating THREE.ArrayCamera Source: https://github.com/mrdoob/three.js/blob/dev/examples/webgl_camera_array.html This comprehensive example initializes a three.js scene using THREE.ArrayCamera to render a grid of sub-camera views. It includes scene setup, light, objects, renderer configuration, and animation loop, along with a commented-out window resize handler. ```javascript import * as THREE from 'three'; let camera, scene, renderer; let mesh; const AMOUNT = 6; init(); function init() { const ASPECT_RATIO = window.innerWidth / window.innerHeight; const WIDTH = ( window.innerWidth / AMOUNT ) * window.devicePixelRatio; const HEIGHT = ( window.innerHeight / AMOUNT ) * window.devicePixelRatio; const cameras = []; for ( let y = 0; y < AMOUNT; y ++ ) { for ( let x = 0; x < AMOUNT; x ++ ) { const subcamera = new THREE.PerspectiveCamera( 40, ASPECT_RATIO, 0.1, 10 ); subcamera.viewport = new THREE.Vector4( Math.floor( x * WIDTH ), Math.floor( y * HEIGHT ), Math.ceil( WIDTH ), Math.ceil( HEIGHT ) ); subcamera.position.x = ( x / AMOUNT ) - 0.5; subcamera.position.y = 0.5 - ( y / AMOUNT ); subcamera.position.z = 1.5; subcamera.position.multiplyScalar( 2 ); subcamera.lookAt( 0, 0, 0 ); subcamera.updateMatrixWorld(); cameras.push( subcamera ); } } camera = new THREE.ArrayCamera( cameras ); camera.position.z = 3; scene = new THREE.Scene(); scene.add( new THREE.AmbientLight( 0x999999 ) ); const light = new THREE.DirectionalLight( 0xffffff, 3 ); light.position.set( 0.5, 0.5, 1 ); light.castShadow = true; light.shadow.camera.zoom = 4; // tighter shadow map scene.add( light ); const geometryBackground = new THREE.PlaneGeometry( 100, 100 ); const materialBackground = new THREE.MeshPhongMaterial( { color: 0x000066 } ); const background = new THREE.Mesh( geometryBackground, materialBackground ); background.receiveShadow = true; background.position.set( 0, 0, - 1 ); scene.add( background ); const geometryCylinder = new THREE.CylinderGeometry( 0.5, 0.5, 1, 32 ); const materialCylinder = new THREE.MeshPhongMaterial( { color: 0xff0000 } ); mesh = new THREE.Mesh( geometryCylinder, materialCylinder ); mesh.castShadow = true; mesh.receiveShadow = true; scene.add( mesh ); renderer = new THREE.WebGLRenderer(); renderer.setPixelRatio( window.devicePixelRatio ); renderer.setSize( window.innerWidth, window.innerHeight ); renderer.setAnimationLoop( animate ); renderer.shadowMap.enabled = true; document.body.appendChild( renderer.domElement ); // window.addEventListener( 'resize', onWindowResize ); } function onWindowResize() { const ASPECT_RATIO = window.innerWidth / window.innerHeight; const WIDTH = ( window.innerWidth / AMOUNT ) * window.devicePixelRatio; const HEIGHT = ( window.innerHeight / AMOUNT ) * window.devicePixelRatio; camera.aspect = ASPECT_RATIO; camera.updateProjectionMatrix(); for ( let y = 0; y < AMOUNT; y ++ ) { for ( let x = 0; x < AMOUNT; x ++ ) { const subcamera = camera.cameras[ AMOUNT * y + x ]; subcamera.viewport.set( Math.floor( x * WIDTH ), Math.floor( y * HEIGHT ), Math.ceil( WIDTH ), Math.ceil( HEIGHT ) ); subcamera.aspect = ASPECT_RATIO; subcamera.updateProjectionMatrix(); } } renderer.setSize( window.innerWidth, window.innerHeight ); } function animate() { mesh.rotation.x += 0.005; mesh.rotation.z += 0.01; renderer.render( scene, camera ); } ``` -------------------------------- ### .setupLightingModel() Source: https://github.com/mrdoob/three.js/blob/dev/docs/pages/MeshBasicNodeMaterial.html Setups the lighting model. ```APIDOC ## Method: .setupLightingModel() ### Description Setups the lighting model. ### Returns BasicLightingModel - The lighting model. ### Overrides [NodeMaterial#setupLightingModel](NodeMaterial.html#setupLightingModel) ``` -------------------------------- ### .setupLightingModel() : BasicLightingModel Source: https://github.com/mrdoob/three.js/blob/dev/docs/pages/MeshBasicNodeMaterial.html.md Setups the lighting model. ```APIDOC ## Method: .setupLightingModel() ### Description Setups the lighting model. ### Returns - (BasicLightingModel) - The lighting model. ``` -------------------------------- ### Full Three.js BufferGeometry Scene Setup and Animation Source: https://github.com/mrdoob/three.js/blob/dev/examples/webgl_buffergeometry.html This comprehensive JavaScript example initializes a Three.js scene, generates a large number of random triangles, and populates a BufferGeometry with their position, normal, and color attributes. It includes camera, lighting, material setup, and an animation loop for rotation. ```javascript import * as THREE from 'three'; import Stats from 'three/addons/libs/stats.module.js'; let container, stats; let camera, scene, renderer; let mesh; init(); animate(); function init() { container = document.getElementById( 'container' ); // camera = new THREE.PerspectiveCamera( 27, window.innerWidth / window.innerHeight, 1, 3500 ); camera.position.z = 2750; scene = new THREE.Scene(); scene.background = new THREE.Color( 0x050505 ); scene.fog = new THREE.Fog( 0x050505, 2000, 3500 ); // scene.add( new THREE.AmbientLight( 0xcccccc ) ); const light1 = new THREE.DirectionalLight( 0xffffff, 1.5 ); light1.position.set( 1, 1, 1 ); scene.add( light1 ); const light2 = new THREE.DirectionalLight( 0xffffff, 4.5 ); light2.position.set( 0, - 1, 0 ); scene.add( light2 ); // const triangles = 160000; const geometry = new THREE.BufferGeometry(); const positions = []; const normals = []; const colors = []; const color = new THREE.Color(); const n = 800, n2 = n / 2; // triangles spread in the cube const d = 12, d2 = d / 2; // individual triangle size const pA = new THREE.Vector3(); const pB = new THREE.Vector3(); const pC = new THREE.Vector3(); const cb = new THREE.Vector3(); const ab = new THREE.Vector3(); for ( let i = 0; i < triangles; i ++ ) { // positions const x = Math.random() * n - n2; const y = Math.random() * n - n2; const z = Math.random() * n - n2; const ax = x + Math.random() * d - d2; const ay = y + Math.random() * d - d2; const az = z + Math.random() * d - d2; const bx = x + Math.random() * d - d2; const by = y + Math.random() * d - d2; const bz = z + Math.random() * d - d2; const cx = x + Math.random() * d - d2; const cy = y + Math.random() * d - d2; const cz = z + Math.random() * d - d2; positions.push( ax, ay, az ); positions.push( bx, by, bz ); positions.push( cx, cy, cz ); // flat face normals pA.set( ax, ay, az ); pB.set( bx, by, bz ); pC.set( cx, cy, cz ); cb.subVectors( pC, pB ); ab.subVectors( pA, pB ); cb.cross( ab ); cb.normalize(); const nx = cb.x; const ny = cb.y; const nz = cb.z; normals.push( nx, ny, nz ); normals.push( nx, ny, nz ); normals.push( nx, ny, nz ); // colors const vx = ( x / n ) + 0.5; const vy = ( y / n ) + 0.5; const vz = ( z / n ) + 0.5; color.setRGB( vx, vy, vz ); const alpha = Math.random(); colors.push( color.r, color.g, color.b, alpha ); colors.push( color.r, color.g, color.b, alpha ); colors.push( color.r, color.g, color.b, alpha ); } function disposeArray() { this.array = null; } geometry.setAttribute( 'position', new THREE.Float32BufferAttribute( positions, 3 ).onUpload( disposeArray ) ); geometry.setAttribute( 'normal', new THREE.Float32BufferAttribute( normals, 3 ).onUpload( disposeArray ) ); geometry.setAttribute( 'color', new THREE.Float32BufferAttribute( colors, 4 ).onUpload( disposeArray ) ); geometry.computeBoundingSphere(); const material = new THREE.MeshPhongMaterial( { color: 0xd5d5d5, specular: 0xffffff, shininess: 250, side: THREE.DoubleSide, vertexColors: true, transparent: true } ); mesh = new THREE.Mesh( geometry, material ); scene.add( mesh ); // renderer = new THREE.WebGLRenderer( { antialias: true } ); renderer.setPixelRatio( window.devicePixelRatio ); renderer.setSize( window.innerWidth, window.innerHeight ); renderer.setAnimationLoop( animate ); container.appendChild( renderer.domElement ); // stats = new Stats(); container.appendChild( stats.dom ); // window.addEventListener( 'resize', onWindowResize ); } function onWindowResize() { camera.aspect = window.innerWidth / window.innerHeight; camera.updateProjectionMatrix(); renderer.setSize( window.innerWidth, window.innerHeight ); } // function animate() { const time = Date.now() * 0.001; mesh.rotation.x = time * 0.25; mesh.rotation.y = time * 0.5; renderer.render( scene, camera ); stats.update(); } ``` -------------------------------- ### .setup() Source: https://github.com/mrdoob/three.js/blob/dev/docs/pages/ReferenceBaseNode.html The output of the reference node is the internal uniform node. ```APIDOC ## .setup(): UniformNode ### Description The output of the reference node is the internal uniform node. ### Returns (UniformNode) - The output node. ``` -------------------------------- ### Full three.js WebGPU Morph Targets Example Source: https://github.com/mrdoob/three.js/blob/dev/examples/webgpu_morphtargets.html This complete JavaScript example sets up a three.js WebGPU scene, creates a BoxGeometry with two morph targets (spherify and twist), and integrates a GUI to control their influence. It includes scene initialization, geometry manipulation, renderer setup, and event handling. ```javascript import * as THREE from 'three/webgpu'; import { Inspector } from 'three/addons/inspector/Inspector.js'; import { OrbitControls } from 'three/addons/controls/OrbitControls.js'; let container, camera, scene, renderer, mesh; init(); function init() { container = document.getElementById( 'container' ); scene = new THREE.Scene(); scene.background = new THREE.Color( 0x8FBCD4 ); camera = new THREE.PerspectiveCamera( 45, window.innerWidth / window.innerHeight, 1, 20 ); camera.position.z = 10; scene.add( camera ); scene.add( new THREE.AmbientLight( 0x8FBCD4, 1.5 ) ); const pointLight = new THREE.PointLight( 0xffffff, 200 ); camera.add( pointLight ); const geometry = createGeometry(); const material = new THREE.MeshPhongMaterial( { color: 0xff0000, flatShading: true } ); mesh = new THREE.Mesh( geometry, material ); scene.add( mesh ); renderer = new THREE.WebGPURenderer( { antialias: true } ); renderer.inspector = new Inspector(); renderer.setPixelRatio( window.devicePixelRatio ); renderer.setSize( window.innerWidth, window.innerHeight ); renderer.setAnimationLoop( function () { renderer.render( scene, camera ); } ); container.appendChild( renderer.domElement ); const controls = new OrbitControls( camera, renderer.domElement ); controls.enableZoom = false; window.addEventListener( 'resize', onWindowResize ); initGUI(); } function createGeometry() { const geometry = new THREE.BoxGeometry( 2, 2, 2, 32, 32, 32 ); // create an empty array to hold targets for the attribute we want to morph // morphing positions and normals is supported geometry.morphAttributes.position = []; // the original positions of the cube's vertices const positionAttribute = geometry.attributes.position; // for the first morph target we'll move the cube's vertices onto the surface of a sphere const spherePositions = []; // for the second morph target, we'll twist the cubes vertices const twistPositions = []; const direction = new THREE.Vector3( 1, 0, 0 ); const vertex = new THREE.Vector3(); for ( let i = 0; i < positionAttribute.count; i ++ ) { const x = positionAttribute.getX( i ); const y = positionAttribute.getY( i ); const z = positionAttribute.getZ( i ); spherePositions.push( x * Math.sqrt( 1 - ( y * y / 2 ) - ( z * z / 2 ) + ( y * y * z * z / 3 ) ), y * Math.sqrt( 1 - ( z * z / 2 ) - ( x * x / 2 ) + ( z * z * x * x / 3 ) ), z * Math.sqrt( 1 - ( x * x / 2 ) - ( y * y / 2 ) + ( x * x * y * y / 3 ) ) ); // stretch along the x-axis so we can see the twist better vertex.set( x * 2, y, z ); vertex.applyAxisAngle( direction, Math.PI * x / 2 ).toArray( twistPositions, twistPositions.length ); } // add the spherical positions as the first morph target geometry.morphAttributes.position[ 0 ] = new THREE.Float32BufferAttribute( spherePositions, 3 ); // add the twisted positions as the second morph target geometry.morphAttributes.position[ 1 ] = new THREE.Float32BufferAttribute( twistPositions, 3 ); return geometry; } function initGUI() { // Set up dat.GUI to control targets const params = { Spherify: 0, Twist: 0, }; const gui = renderer.inspector.createParameters( 'Morph Targets' ); gui.add( params, 'Spherify', 0, 1, 0.01 ).onChange( function ( value ) { mesh.morphTargetInfluences[ 0 ] = value; } ); gui.add( params, 'Twist', 0, 1, 0.01 ).onChange( function ( value ) { mesh.morphTargetInfluences[ 1 ] = value; } ); } function onWindowResize() { camera.aspect = window.innerWidth / window.innerHeight; camera.updateProjectionMatrix(); renderer.setSize( window.innerWidth, window.innerHeight ); } ``` -------------------------------- ### Complete three.js Alpha Hash Example Source: https://github.com/mrdoob/three.js/blob/dev/examples/webgl_materials_alphahash.html This comprehensive example initializes a three.js scene with instanced meshes, applies alpha hashing to the material, sets up temporal anti-aliasing, and includes a GUI for interactive control. It demonstrates the full setup for rendering transparent objects with alpha hash and post-processing effects. ```javascript import * as THREE from 'three'; import Stats from 'three/addons/libs/stats.module.js'; import { GUI } from 'three/addons/libs/lil-gui.module.min.js'; import { OrbitControls } from 'three/addons/controls/OrbitControls.js'; import { RoomEnvironment } from 'three/addons/environments/RoomEnvironment.js'; import { EffectComposer } from 'three/addons/postprocessing/EffectComposer.js'; import { RenderPass } from 'three/addons/postprocessing/RenderPass.js'; import { TAARenderPass } from 'three/addons/postprocessing/TAARenderPass.js'; import { OutputPass } from 'three/addons/postprocessing/OutputPass.js'; let camera, scene, renderer, controls, stats, mesh, material; let composer, renderPass, taaRenderPass, outputPass; let needsUpdate = false; const amount = parseInt( window.location.search.slice( 1 ) ) || 3; const count = Math.pow( amount, 3 ); const color = new THREE.Color(); const params = { alpha: 0.5, alphaHash: true, taa: true, sampleLevel: 2, }; init(); function init() { camera = new THREE.PerspectiveCamera( 60, window.innerWidth / window.innerHeight, 0.1, 100 ); camera.position.set( amount, amount, amount ); camera.lookAt( 0, 0, 0 ); scene = new THREE.Scene(); const geometry = new THREE.IcosahedronGeometry( 0.5, 3 ); material = new THREE.MeshStandardMaterial( { color: 0xffffff, alphaHash: params.alphaHash, opacity: params.alpha } ); mesh = new THREE.InstancedMesh( geometry, material, count ); let i = 0; const offset = ( amount - 1 ) / 2; const matrix = new THREE.Matrix4(); for ( let x = 0; x < amount; x ++ ) { for ( let y = 0; y < amount; y ++ ) { for ( let z = 0; z < amount; z ++ ) { matrix.setPosition( offset - x, offset - y, offset - z ); mesh.setMatrixAt( i, matrix ); mesh.setColorAt( i, color.setHex( Math.random() * 0xffffff ) ); i ++; } } } scene.add( mesh ); renderer = new THREE.WebGLRenderer( { antialias: true } ); renderer.setPixelRatio( window.devicePixelRatio ); renderer.setSize( window.innerWidth, window.innerHeight ); renderer.setAnimationLoop( animate ); document.body.appendChild( renderer.domElement ); const environment = new RoomEnvironment(); const pmremGenerator = new THREE.PMREMGenerator( renderer ); scene.environment = pmremGenerator.fromScene( environment, 0.04 ).texture; environment.dispose(); composer = new EffectComposer( renderer ); renderPass = new RenderPass( scene, camera ); renderPass.enabled = false; taaRenderPass = new TAARenderPass( scene, camera ); outputPass = new OutputPass(); composer.addPass( renderPass ); composer.addPass( taaRenderPass ); composer.addPass( outputPass ); controls = new OrbitControls( camera, renderer.domElement ); controls.enableZoom = false; controls.enablePan = false; controls.addEventListener( 'change', () => ( needsUpdate = true ) ); const gui = new GUI(); gui.add( params, 'alpha', 0, 1 ).onChange( onMaterialUpdate ); gui.add( params, 'alphaHash' ).onChange( onMaterialUpdate ); const taaFolder = gui.addFolder( 'Temporal Anti-Aliasing' ); taaFolder.add( params, 'taa' ).name( 'enabled' ).onChange( () => { renderPass.enabled = ! params.taa; taaRenderPass.enabled = params.taa; sampleLevelCtrl.enable( params.taa ); needsUpdate = true; } ); const sampleLevelCtrl = taaFolder.add( params, 'sampleLevel', 0, 6, 1 ).onChange( () => ( needsUpdate = true ) ); stats = new Stats(); document.body.appendChild( stats.dom ); window.addEventListener( 'resize', onWindowResize ); } function onWindowResize() { camera.aspect = window.innerWidth / window.innerHeight; camera.updateProjectionMatrix(); renderer.setSize( window.innerWidth, window.innerHeight ); composer.setSize( window.innerWidth, window.innerHeight ); needsUpdate = true; } function onMaterialUpdate() { material.opacity = params.alpha; material.alphaHash = params.alphaHash; material.transparent = ! params.alphaHash; material.depthWrite = params.alphaHash; material.needsUpdate = true; needsUpdate = true; } function animate() { render(); stats.update(); } function render() { if ( needsUpdate ) { taaRenderPass.accumulate = false; taaRenderPass.sampleLevel = 0; needsUpdate = false; } else { taaRenderPass.accumulate = true; taaRenderPass.sampleLevel = params.sampleLevel; } composer.render(); } ``` -------------------------------- ### .setupFog( builder : NodeBuilder, outputNode : Node. ) : Node. Source: https://github.com/mrdoob/three.js/blob/dev/docs/pages/NodeMaterial.html Setup the fog. ```APIDOC ## .setupFog( builder : NodeBuilder, outputNode : Node. ) : Node. ### Description Setup the fog. ### Parameters - **builder** (NodeBuilder) - The current node builder. - **outputNode** (Node.) - The existing output node. ### Returns Node. - The output node. ``` -------------------------------- ### Initialize AudioAnalyser and Get Average Frequency Source: https://github.com/mrdoob/three.js/blob/dev/docs/pages/AudioAnalyser.html This example demonstrates how to set up an AudioListener, load an audio file, and then use THREE.AudioAnalyser to obtain the average frequency of the playing sound. ```javascript // create an AudioListener and add it to the camera const listener = new THREE.AudioListener(); camera.add( listener ); // create an Audio source const sound = new THREE.Audio( listener ); // load a sound and set it as the Audio object's buffer const audioLoader = new THREE.AudioLoader(); audioLoader.load( 'sounds/ambient.ogg', function( buffer ) { sound.setBuffer( buffer ); sound.setLoop(true); sound.setVolume(0.5); sound.play(); }); // create an AudioAnalyser, passing in the sound and desired fftSize const analyser = new THREE.AudioAnalyser( sound, 32 ); // get the average frequency of the sound const data = analyser.getAverageFrequency(); ``` -------------------------------- ### .setupFog( builder : NodeBuilder, outputNode : Node. ) : Node. Source: https://github.com/mrdoob/three.js/blob/dev/docs/pages/NodeMaterial.html.md Setup the fog. ```APIDOC ## Method: .setupFog( builder : NodeBuilder, outputNode : Node. ) : Node. ### Description Setup the fog. ### Parameters - **builder** (NodeBuilder) - Required - The current node builder. - **outputNode** (Node.) - Required - The existing output node. ### Returns - **Node.** - The output node. ``` -------------------------------- ### WebGPU Compute Ping/Pong Texture Example with TSL Source: https://github.com/mrdoob/three.js/blob/dev/examples/webgpu_compute_texture_pingpong.html This example initializes WebGPU, sets up two storage textures (ping and pong), and defines compute shaders using Three.js TSL to perform a blur operation, swapping between textures each frame for continuous processing. It includes setup, compute logic, and rendering. ```javascript { "imports": { "three": "../build/three.webgpu.js", "three/webgpu": "../build/three.webgpu.js", "three/tsl": "../build/three.tsl.js", "three/addons/": "./jsm/" } } import * as THREE from 'three/webgpu'; import { storageTexture, textureStore, Fn, instanceIndex, uniform, float, vec2, vec4, uvec2, ivec2, int, NodeAccess } from 'three/tsl'; import WebGPU from 'three/addons/capabilities/WebGPU.js'; let camera, scene, renderer; let computeInitNode, computeToPing, computeToPong; let pingTexture, pongTexture; let material; let phase = true; let lastUpdate = - 1; const width = 512, height = 512; const seed = uniform( new THREE.Vector2() ); init(); async function init() { if ( WebGPU.isAvailable() === false ) { document.body.appendChild( WebGPU.getErrorMessage() ); throw new Error( 'No WebGPU support' ); } const aspect = window.innerWidth / window.innerHeight; camera = new THREE.OrthographicCamera( - aspect, aspect, 1, - 1, 0, 2 ); camera.position.z = 1; scene = new THREE.Scene(); // texture const hdr = true; pingTexture = new THREE.StorageTexture( width, height ); pongTexture = new THREE.StorageTexture( width, height ); if ( hdr ) { pingTexture.type = THREE.HalfFloatType; pongTexture.type = THREE.HalfFloatType; } const rand2 = Fn( ( [ n ] ) => { return n.dot( vec2( 12.9898, 4.1414 ) ).sin().mul( 43758.5453 ).fract(); } ); // Create storage texture nodes with proper access const writePing = storageTexture( pingTexture ).setAccess( NodeAccess.WRITE_ONLY ); const readPing = storageTexture( pingTexture ).setAccess( NodeAccess.READ_ONLY ); const writePong = storageTexture( pongTexture ).setAccess( NodeAccess.WRITE_ONLY ); const readPong = storageTexture( pongTexture ).setAccess( NodeAccess.READ_ONLY ); const computeInit = Fn( () => { const posX = instanceIndex.mod( width ); const posY = instanceIndex.div( width ); const indexUV = uvec2( posX, posY ); const uv = vec2( float( posX ).div( width ), float( posY ).div( height ) ); const r = rand2( uv.add( seed.mul( 100 ) ) ).sub( rand2( uv.add( seed.mul( 300 ) ) ) ); const g = rand2( uv.add( seed.mul( 200 ) ) ).sub( rand2( uv.add( seed.mul( 300 ) ) ) ); const b = rand2( uv.add( seed.mul( 200 ) ) ).sub( rand2( uv.add( seed.mul( 100 ) ) ) ); textureStore( writePing, indexUV, vec4( r, g, b, 1 ) ); } ); computeInitNode = computeInit().compute( width * height ); // compute ping-pong: blur function using .load() for textureLoad const blur = Fn( ( [ readTex, uv ] ) => { const c0 = readTex.load( uv.add( ivec2( - 1, 1 ) ) ); const c1 = readTex.load( uv.add( ivec2( - 1, - 1 ) ) ); const c2 = readTex.load( uv.add( ivec2( 0, 0 ) ) ); const c3 = readTex.load( uv.add( ivec2( 1, - 1 ) ) ); const c4 = readTex.load( uv.add( ivec2( 1, 1 ) ) ); return c0.add( c1 ).add( c2 ).add( c3 ).add( c4 ).div( 5.0 ); } ); // compute loop: read from one texture, blur, write to another const computePingPong = Fn( ( [ readTex, writeTex ] ) => { const posX = instanceIndex.mod( width ); const posY = instanceIndex.div( width ); const indexUV = ivec2( int( posX ), int( posY ) ); const color = blur( readTex, indexUV ); textureStore( writeTex, indexUV, vec4( color.rgb.mul( 1.05 ), 1 ) ); } ); computeToPong = computePingPong( readPing, writePong ).compute( width * height ); computeToPing = computePingPong( readPong, writePing ).compute( width * height ); // material = new THREE.MeshBasicMaterial( { color: 0xffffff, map: pongTexture } ); const plane = new THREE.Mesh( new THREE.PlaneGeometry( 1, 1 ), material ); scene.add( plane ); renderer = new THREE.WebGPURenderer( { antialias: true } ); renderer.setPixelRatio( window.devicePixelRatio ); renderer.setSize( window.innerWidth, window.innerHeight ); renderer.setAnimationLoop( render ); document.body.appendChild( renderer.domElement ); await renderer.init(); window.addEventListener( 'resize', onWindowResize ); // compute init renderer.compute( computeInitNode ); } function onWindowResize() { renderer.setSize( window.innerWidth, window.innerHeight ); const aspect = window.innerWidth / window.innerHeight; const frustumHeight = camera.top - camera.bottom; camera.left = - frustumHeight * aspect / 2; camera.right = frustumHeight * aspect / 2; camera.updateProjectionMatrix(); } function render() { const time = performance.now(); const seconds = Math.floor( time / 1000 ); // reset every second if ( phase && seconds !== lastUpdate ) { seed.value.set( Math.random(), Math.random() ); renderer.compute( computeInitNode ); lastUpdate = seconds; } // compute step renderer.compute( phase ? computeToPong : computeToPing ); material.map = phase ? pongTexture : pingTexture; phase = ! phase; // render step // update material texture node renderer.render( scene, camera ); } ```