[
  {
    "url": "https://threejs.org/manual/",
    "title": "three.js manual",
    "content": "<div><div><h3>Getting Started</h3><ul><li><a href=\"https://threejs.org/manual/en/installation.html\" target=\"viewer\">Installation</a></li><li><a href=\"https://threejs.org/manual/en/creating-a-scene.html\" target=\"viewer\">Creating a Scene</a></li><li><a href=\"https://threejs.org/manual/en/creating-text.html\" target=\"viewer\">Creating Text</a></li><li><a href=\"https://threejs.org/manual/en/drawing-lines.html\" target=\"viewer\">Drawing Lines</a></li><li><a href=\"https://threejs.org/manual/en/faq.html\" target=\"viewer\">FAQ</a></li><li><a href=\"https://threejs.org/manual/en/libraries-and-plugins.html\" target=\"viewer\">Libraries and Plugins</a></li><li><a href=\"https://threejs.org/manual/en/loading-3d-models.html\" target=\"viewer\">Loading 3D Models</a></li><li><a href=\"https://threejs.org/manual/en/uniform-types.html\" target=\"viewer\">Uniform Types</a></li><li><a href=\"https://threejs.org/manual/en/useful-links.html\" target=\"viewer\">Useful Links</a></li><li><a href=\"https://threejs.org/manual/en/webgl-compatibility-check.html\" target=\"viewer\">WebGL Compatibility Check</a></li></ul></div><div><h3>Next Steps</h3><ul><li><a href=\"https://threejs.org/manual/en/animation-system.html\" target=\"viewer\">Animation System</a></li><li><a href=\"https://threejs.org/manual/en/color-management.html\" target=\"viewer\">Color Management</a></li><li><a href=\"https://threejs.org/manual/en/how-to-create-vr-content.html\" target=\"viewer\">How to create VR content</a></li><li><a href=\"https://threejs.org/manual/en/how-to-dispose-of-objects.html\" target=\"viewer\">How to dispose of Objects</a></li><li><a href=\"https://threejs.org/manual/en/how-to-update-things.html\" target=\"viewer\">How to update Things</a></li><li><a href=\"https://threejs.org/manual/en/how-to-use-post-processing.html\" target=\"viewer\">How to use Post Processing</a></li><li><a href=\"https://threejs.org/manual/en/matrix-transformations.html\" target=\"viewer\">Matrix Transformations</a></li></ul></div><hr><div><h3>Basics</h3><ul><li><a href=\"https://threejs.org/manual/en/fundamentals.html\" target=\"viewer\">Fundamentals</a></li><li><a href=\"https://threejs.org/manual/en/responsive.html\" target=\"viewer\">Responsive Design</a></li><li><a href=\"https://threejs.org/manual/en/prerequisites.html\" target=\"viewer\">Prerequisites</a></li><li><a href=\"https://threejs.org/manual/en/setup.html\" target=\"viewer\">Setup</a></li></ul></div><div><h3>Fundamentals</h3><ul><li><a href=\"https://threejs.org/manual/en/primitives.html\" target=\"viewer\">Primitives</a></li><li><a href=\"https://threejs.org/manual/en/scenegraph.html\" target=\"viewer\">Scenegraph</a></li><li><a href=\"https://threejs.org/manual/en/materials.html\" target=\"viewer\">Materials</a></li><li><a href=\"https://threejs.org/manual/en/textures.html\" target=\"viewer\">Textures</a></li><li><a href=\"https://threejs.org/manual/en/lights.html\" target=\"viewer\">Lights</a></li><li><a href=\"https://threejs.org/manual/en/cameras.html\" target=\"viewer\">Cameras</a></li><li><a href=\"https://threejs.org/manual/en/shadows.html\" target=\"viewer\">Shadows</a></li><li><a href=\"https://threejs.org/manual/en/fog.html\" target=\"viewer\">Fog</a></li><li><a href=\"https://threejs.org/manual/en/rendertargets.html\" target=\"viewer\">Render Targets</a></li><li><a href=\"https://threejs.org/manual/en/custom-buffergeometry.html\" target=\"viewer\">Custom BufferGeometry</a></li><li><a href=\"https://threejs.org/manual/en/physics.html\" target=\"viewer\">Physics</a></li></ul></div><div><h3>Tips</h3><ul><li><a href=\"https://threejs.org/manual/en/rendering-on-demand.html\" target=\"viewer\">Rendering On Demand</a></li><li><a href=\"https://threejs.org/manual/en/debugging-javascript.html\" target=\"viewer\">Debugging JavaScript</a></li><li><a href=\"https://threejs.org/manual/en/debugging-glsl.html\" target=\"viewer\">Debugging GLSL</a></li><li><a href=\"https://threejs.org/manual/en/tips.html#screenshot\" target=\"viewer\">Taking a screenshot</a></li><li><a href=\"https://threejs.org/manual/en/tips.html#preservedrawingbuffer\" target=\"viewer\">Prevent the Canvas Being Cleared</a></li><li><a href=\"https://threejs.org/manual/en/tips.html#tabindex\" target=\"viewer\">Get Keyboard Input From a Canvas</a></li><li><a href=\"https://threejs.org/manual/en/tips.html#transparent-canvas\" target=\"viewer\">Make the Canvas Transparent</a></li><li><a href=\"https://threejs.org/manual/en/tips.html#html-background\" target=\"viewer\">Use three.js as Background in HTML</a></li></ul></div><div><h3>Optimization</h3><ul><li><a href=\"https://threejs.org/manual/en/optimize-lots-of-objects.html\" target=\"viewer\">Optimizing Lots of Objects</a></li><li><a href=\"https://threejs.org/manual/en/optimize-lots-of-objects-animated.html\" target=\"viewer\">Optimizing Lots of Objects Animated</a></li><li><a href=\"https://threejs.org/manual/en/offscreencanvas.html\" target=\"viewer\">Using OffscreenCanvas in a Web Worker</a></li></ul></div><div><h3>Solutions</h3><ul><li><a href=\"https://threejs.org/manual/en/load-obj.html\" target=\"viewer\">Load an .OBJ file</a></li><li><a href=\"https://threejs.org/manual/en/load-gltf.html\" target=\"viewer\">Load a .GLTF file</a></li><li><a href=\"https://threejs.org/manual/en/backgrounds.html\" target=\"viewer\">Add a Background or Skybox</a></li><li><a href=\"https://threejs.org/manual/en/transparency.html\" target=\"viewer\">How to Draw Transparent Objects</a></li><li><a href=\"https://threejs.org/manual/en/multiple-scenes.html\" target=\"viewer\">Multiple Canvases, Multiple Scenes</a></li><li><a href=\"https://threejs.org/manual/en/picking.html\" target=\"viewer\">Picking Objects with the mouse</a></li><li><a href=\"https://threejs.org/manual/en/post-processing.html\" target=\"viewer\">Post Processing</a></li><li><a href=\"https://threejs.org/manual/en/shadertoy.html\" target=\"viewer\">Using Shadertoy shaders</a></li><li><a href=\"https://threejs.org/manual/en/align-html-elements-to-3d.html\" target=\"viewer\">Aligning HTML Elements to 3D</a></li><li><a href=\"https://threejs.org/manual/en/indexed-textures.html\" target=\"viewer\">Using Indexed Textures for Picking and Color</a></li><li><a href=\"https://threejs.org/manual/en/canvas-textures.html\" target=\"viewer\">Using A Canvas for Dynamic Textures</a></li><li><a href=\"https://threejs.org/manual/en/billboards.html\" target=\"viewer\">Billboards and Facades</a></li><li><a href=\"https://threejs.org/manual/en/cleanup.html\" target=\"viewer\">Freeing Resources</a></li><li><a href=\"https://threejs.org/manual/en/voxel-geometry.html\" target=\"viewer\">Making Voxel Geometry (Minecraft)</a></li><li><a href=\"https://threejs.org/manual/en/game.html\" target=\"viewer\">Start making a Game</a></li></ul></div><div><h3>WebGPU</h3><ul><li><a href=\"https://threejs.org/manual/en/webgpurenderer.html\" target=\"viewer\">WebGPURenderer</a></li><li><a href=\"https://threejs.org/manual/en/webgpu-postprocessing.html\" target=\"viewer\">Post-Processing</a></li></ul></div><div><h3>WebXR</h3><ul><li><a href=\"https://threejs.org/manual/en/webxr-basics.html\" target=\"viewer\">VR - Basics</a></li><li><a href=\"https://threejs.org/manual/en/webxr-look-to-select.html\" target=\"viewer\">VR - Look To Select</a></li><li><a href=\"https://threejs.org/manual/en/webxr-point-to-select.html\" target=\"viewer\">VR - Point To Select</a></li></ul></div><div><h3>Reference</h3><ul><li><a href=\"https://threejs.org/manual/en/material-table.html\" target=\"viewer\">Material Table</a></li></ul></div></div>",
    "success": true,
    "error": "",
    "crawled_at": 1786376809.300204,
    "is_shell": true
  },
  {
    "url": "https://threejs.org/docs",
    "title": "three.js docs",
    "content": "<h2>Core</h2>\n\t\t\t\t\t<h3>Animation</h3>\n\t\t\t\t\t<ul>\n\t\t\t\t\t\t<li><a href=\"pages/AnimationAction.html\" target=\"viewer\">AnimationAction</a></li>\n\t\t\t\t\t\t<li><a href=\"pages/AnimationClip.html\" target=\"viewer\">AnimationClip</a></li>\n\t\t\t\t\t\t<li><a href=\"pages/AnimationMixer.html\" target=\"viewer\">AnimationMixer</a></li>\n\t\t\t\t\t\t<li><a href=\"pages/AnimationObjectGroup.html\" target=\"viewer\">AnimationObjectGroup</a></li>\n\t\t\t\t\t\t<li><a href=\"pages/AnimationUtils.html\" target=\"viewer\">AnimationUtils</a></li>\n\t\t\t\t\t\t<li><a href=\"pages/BooleanKeyframeTrack.html\" target=\"viewer\">BooleanKeyframeTrack</a></li>\n\t\t\t\t\t\t<li><a href=\"pages/ColorKeyframeTrack.html\" target=\"viewer\">ColorKeyframeTrack</a></li>\n\t\t\t\t\t\t<li><a href=\"pages/KeyframeTrack.html\" target=\"viewer\">KeyframeTrack</a></li>\n\t\t\t\t\t\t<li><a href=\"pages/NumberKeyframeTrack.html\" target=\"viewer\">NumberKeyframeTrack</a></li>\n\t\t\t\t\t\t<li><a href=\"pages/PropertyBinding.html\" target=\"viewer\">PropertyBinding</a></li>\n\t\t\t\t\t\t<li><a href=\"pages/PropertyMixer.html\" target=\"viewer\">PropertyMixer</a></li>\n\t\t\t\t\t\t<li><a href=\"pages/QuaternionKeyframeTrack.html\" target=\"viewer\">QuaternionKeyframeTrack</a></li>\n\t\t\t\t\t\t<li><a href=\"pages/StringKeyframeTrack.html\" target=\"viewer\">StringKeyframeTrack</a></li>\n\t\t\t\t\t\t<li><a href=\"pages/VectorKeyframeTrack.html\" target=\"viewer\">VectorKeyframeTrack</a></li>\n\t\t\t\t\t</ul>\n\t\t\t\t\t<h3>Audio</h3>\n\t\t\t\t\t<ul>\n\t\t\t\t\t\t<li><a href=\"pages/Audio.html\" target=\"viewer\">Audio</a></li>\n\t\t\t\t\t\t<li><a href=\"pages/AudioAnalyser.html\" target=\"viewer\">AudioAnalyser</a></li>\n\t\t\t\t\t\t<li><a href=\"pages/AudioContext.html\" target=\"viewer\">AudioContext</a></li>\n\t\t\t\t\t\t<li><a href=\"pages/AudioListener.html\" target=\"viewer\">AudioListener</a></li>\n\t\t\t\t\t\t<li><a href=\"pages/PositionalAudio.html\" target=\"viewer\">PositionalAudio</a></li>\n\t\t\t\t\t</ul>\n\t\t\t\t\t<h3>Cameras</h3>\n\t\t\t\t\t<ul>\n\t\t\t\t\t\t<li><a href=\"pages/ArrayCamera.html\" target=\"viewer\">ArrayCamera</a></li>\n\t\t\t\t\t\t<li><a href=\"pages/Camera.html\" target=\"viewer\">Camera</a></li>\n\t\t\t\t\t\t<li><a href=\"pages/CubeCamera.html\" target=\"viewer\">CubeCamera</a></li>\n\t\t\t\t\t\t<li><a href=\"pages/OrthographicCamera.html\" target=\"viewer\">OrthographicCamera</a></li>\n\t\t\t\t\t\t<li><a href=\"pages/PerspectiveCamera.html\" target=\"viewer\">PerspectiveCamera</a></li>\n\t\t\t\t\t\t<li><a href=\"pages/StereoCamera.html\" target=\"viewer\">StereoCamera</a></li>\n\t\t\t\t\t</ul>\n\t\t\t\t\t<h3>Core</h3>\n\t\t\t\t\t<ul>\n\t\t\t\t\t\t<li><a href=\"pages/BufferAttribute.html\" target=\"viewer\">BufferAttribute</a></li>\n\t\t\t\t\t\t<li><a href=\"pages/BufferGeometry.html\" target=\"viewer\">BufferGeometry</a></li>\n\t\t\t\t\t\t<li><a href=\"pages/Clock.html\" target=\"viewer\">Clock</a></li>\n\t\t\t\t\t\t<li><a href=\"pages/EventDispatcher.html\" target=\"viewer\">EventDispatcher</a></li>\n\t\t\t\t\t\t<li><a href=\"pages/Float16BufferAttribute.html\" target=\"viewer\">Float16BufferAttribute</a></li>\n\t\t\t\t\t\t<li><a href=\"pages/Float32BufferAttribute.html\" target=\"viewer\">Float32BufferAttribute</a></li>\n\t\t\t\t\t\t<li><a href=\"pages/GLBufferAttribute.html\" target=\"viewer\">GLBufferAttribute</a></li>\n\t\t\t\t\t\t<li><a href=\"pages/InstancedBufferAttribute.html\" target=\"viewer\">InstancedBufferAttribute</a></li>\n\t\t\t\t\t\t<li><a href=\"pages/InstancedBufferGeometry.html\" target=\"viewer\">InstancedBufferGeometry</a></li>\n\t\t\t\t\t\t<li><a href=\"pages/InstancedInterleavedBuffer.html\" target=\"viewer\">InstancedInterleavedBuffer</a></li>\n\t\t\t\t\t\t<li><a href=\"pages/Int16BufferAttribute.html\" target=\"viewer\">Int16BufferAttribute</a></li>\n\t\t\t\t\t\t<li><a href=\"pages/Int32BufferAttribute.html\" target=\"viewer\">Int32BufferAttribute</a></li>\n\t\t\t\t\t\t<li><a href=\"pages/Int8BufferAttribute.html\" target=\"viewer\">Int8BufferAttribute</a></li>\n\t\t\t\t\t\t<li><a href=\"pages/InterleavedBuffer.html\" target=\"viewer\">InterleavedBuffer</a></li>\n\t\t\t\t\t\t<li><a href=\"pages/InterleavedBufferAttribute.html\" target=\"viewer\">InterleavedBufferAttribute</a></li>\n\t\t\t\t\t\t<li><a href=\"pages/Layers.html\" target=\"viewer\">Layers</a></li>\n\t\t\t\t\t\t<li><a href=\"pages/Object3D.html\" target=\"viewer\">Object3D</a></li>\n\t\t\t\t\t\t<li><a href=\"pages/Raycaster.html\" target=\"viewer\">Raycaster</a></li>\n\t\t\t\t\t\t<li><a href=\"pages/RenderTarget.html\" target=\"viewer\">RenderTarget</a></li>\n\t\t\t\t\t\t<li><a href=\"pages/RenderTarget3D.html\" target=\"viewer\">RenderTarget3D</a></li>\n\t\t\t\t\t\t<li><a href=\"pages/Timer.html\" target=\"viewer\">Timer</a></li>\n\t\t\t\t\t\t<li><a href=\"pages/Uint16BufferAttribute.html\" target=\"viewer\">Uint16BufferAttribute</a></li>\n\t\t\t\t\t\t<li><a href=\"pages/Uint32BufferAttribute.html\" target=\"viewer\">Uint32BufferAttribute</a></li>\n\t\t\t\t\t\t<li><a href=\"pages/Uint8BufferAttribute.html\" target=\"viewer\">Uint8BufferAttribute</a></li>\n\t\t\t\t\t\t<li><a href=\"pages/Uint8ClampedBufferAttribute.html\" target=\"viewer\">Uint8ClampedBufferAttribute</a></li>\n\t\t\t\t\t\t<li><a href=\"pages/Uniform.html\" target=\"viewer\">Uniform</a></li>\n\t\t\t\t\t\t<li><a href=\"pages/UniformsGroup.html\" target=\"viewer\">UniformsGroup</a></li>\n\t\t\t\t\t</ul>\n\t\t\t\t\t<h3>Extras</h3>\n\t\t\t\t\t<ul>\n\t\t\t\t\t\t<li><a href=\"pages/ArcCurve.html\" target=\"viewer\">ArcCurve</a></li>\n\t\t\t\t\t\t<li><a href=\"pages/CatmullRomCurve3.html\" target=\"viewer\">CatmullRomCurve3</a></li>\n\t\t\t\t\t\t<li><a href=\"pages/Controls.html\" target=\"viewer\">Controls</a></li>\n\t\t\t\t\t\t<li><a href=\"pages/CubicBezierCurve.html\" target=\"viewer\">CubicBezierCurve</a></li>\n\t\t\t\t\t\t<li><a href=\"pages/CubicBezierCurve3.html\" target=\"viewer\">CubicBezierCurve3</a></li>\n\t\t\t\t\t\t<li><a href=\"pages/Curve.html\" target=\"viewer\">Curve</a></li>\n\t\t\t\t\t\t<li><a href=\"pages/CurvePath.html\" target=\"viewer\">CurvePath</a></li>\n\t\t\t\t\t\t<li><a href=\"pages/DataUtils.html\" target=\"viewer\">DataUtils</a></li>\n\t\t\t\t\t\t<li><a href=\"pages/Earcut.html\" target=\"viewer\">Earcut</a></li>\n\t\t\t\t\t\t<li><a href=\"pages/EllipseCurve.html\" target=\"viewer\">EllipseCurve</a></li>\n\t\t\t\t\t\t<li><a href=\"pages/ImageUtils.html\" target=\"viewer\">ImageUtils</a></li>\n\t\t\t\t\t\t<li><a href=\"pages/LineCurve.html\" target=\"viewer\">LineCurve</a></li>\n\t\t\t\t\t\t<li><a href=\"pages/LineCurve3.html\" target=\"viewer\">LineCurve3</a></li>\n\t\t\t\t\t\t<li><a href=\"pages/PMREMGenerator.html\" target=\"viewer\">PMREMGenerator</a></li>\n\t\t\t\t\t\t<li><a href=\"pages/Path.html\" target=\"viewer\">Path</a></li>\n\t\t\t\t\t\t<li><a href=\"pages/QuadraticBezierCurve.html\" target=\"viewer\">QuadraticBezierCurve</a></li>\n\t\t\t\t\t\t<li><a href=\"pages/QuadraticBezierCurve3.html\" target=\"viewer\">QuadraticBezierCurve3</a></li>\n\t\t\t\t\t\t<li><a href=\"pages/Shape.html\" target=\"viewer\">Shape</a></li>\n\t\t\t\t\t\t<li><a href=\"pages/ShapePath.html\" target=\"viewer\">ShapePath</a></li>\n\t\t\t\t\t\t<li><a href=\"pages/ShapeUtils.html\" target=\"viewer\">ShapeUtils</a></li>\n\t\t\t\t\t\t<li><a href=\"pages/SplineCurve.html\" target=\"viewer\">SplineCurve</a></li>\n\t\t\t\t\t\t<li><a href=\"pages/TextureUtils.html\" target=\"viewer\">TextureUtils</a></li>\n\t\t\t\t\t\t<li><a href=\"pages/module-Interpolations.html\" target=\"viewer\">Interpolations</a></li>\n\t\t\t\t\t</ul>\n\t\t\t\t\t<h3>Geometries</h3>\n\t\t\t\t\t<ul>\n\t\t\t\t\t\t<li><a href=\"pages/BoxGeometry.html\" target=\"viewer\">BoxGeometry</a></li>\n\t\t\t\t\t\t<li><a href=\"pages/CapsuleGeometry.html\" target=\"viewer\">CapsuleGeometry</a></li>\n\t\t\t\t\t\t<li><a href=\"pages/CircleGeometry.html\" target=\"viewer\">CircleGeometry</a></li>\n\t\t\t\t\t\t<li><a href=\"pages/ConeGeometry.html\" target=\"viewer\">ConeGeometry</a></li>\n\t\t\t\t\t\t<li><a href=\"pages/CylinderGeometry.html\" target=\"viewer\">CylinderGeometry</a></li>\n\t\t\t\t\t\t<li><a href=\"pages/DodecahedronGeometry.html\" target=\"viewer\">DodecahedronGeometry</a></li>\n\t\t\t\t\t\t<li><a href=\"pages/EdgesGeometry.html\" target=\"viewer\">EdgesGeometry</a></li>\n\t\t\t\t\t\t<li><a href=\"pages/ExtrudeGeometry.html\" target=\"viewer\">ExtrudeGeometry</a></li>\n\t\t\t\t\t\t<li><a href=\"pages/IcosahedronGeometry.html\" target=\"viewer\">IcosahedronGeometry</a></li>\n\t\t\t\t\t\t<li><a href=\"pages/LatheGeometry.html\" target=\"viewer\">LatheGeometry</a></li>\n\t\t\t\t\t\t<li><a href=\"pages/OctahedronGeometry.html\" target=\"viewer\">OctahedronGeometry</a></li>\n\t\t\t\t\t\t<li><a href=\"pages/PlaneGeometry.html\" target=\"viewer\">PlaneGeometry</a></li>\n\t\t\t\t\t\t<li><a href=\"pages/PolyhedronGeometry.html\" target=\"viewer\">PolyhedronGeometry</a></li>\n\t\t\t\t\t\t<li><a href=\"pages/RingGeometry.html\" target=\"viewer\">RingGeometry</a></li>\n\t\t\t\t\t\t<li><a href=\"pages/ShapeGeometry.html\" target=\"viewer\">ShapeGeometry</a></li>\n\t\t\t\t\t\t<li><a href=\"pages/SphereGeometry.html\" target=\"viewer\">SphereGeometry</a></li>\n\t\t\t\t\t\t<li><a href=\"pages/TetrahedronGeometry.html\" target=\"viewer\">TetrahedronGeometry</a></li>\n\t\t\t\t\t\t<li><a href=\"pages/TorusGeometry.html\" target=\"viewer\">TorusGeometry</a></li>\n\t\t\t\t\t\t<li><a href=\"pages/TorusKnotGeometry.html\" target=\"viewer\">TorusKnotGeometry</a></li>\n\t\t\t\t\t\t<li><a href=\"pages/TubeGeometry.html\" target=\"viewer\">TubeGeometry</a></li>\n\t\t\t\t\t\t<li><a href=\"pages/WireframeGeometry.html\" target=\"viewer\">WireframeGeometry</a></li>\n\t\t\t\t\t</ul>\n\t\t\t\t\t<h3>Helpers</h3>\n\t\t\t\t\t<ul>\n\t\t\t\t\t\t<li><a href=\"pages/ArrowHelper.html\" target=\"viewer\">ArrowHelper</a></li>\n\t\t\t\t\t\t<li><a href=\"pages/AxesHelper.html\" target=\"viewer\">AxesHelper</a></li>\n\t\t\t\t\t\t<li><a href=\"pages/Box3Helper.html\" target=\"viewer\">Box3Helper</a></li>\n\t\t\t\t\t\t<li><a href=\"pages/BoxHelper.html\" target=\"viewer\">BoxHelper</a></li>\n\t\t\t\t\t\t<li><a href=\"pages/CameraHelper.html\" target=\"viewer\">CameraHelper</a></li>\n\t\t\t\t\t\t<li><a href=\"pages/DirectionalLightHelper.html\" target=\"viewer\">DirectionalLightHelper</a></li>\n\t\t\t\t\t\t<li><a href=\"pages/GridHelper.html\" target=\"viewer\">GridHelper</a></li>\n\t\t\t\t\t\t<li><a href=\"pages/HemisphereLightHelper.html\" target=\"viewer\">HemisphereLightHelper</a></li>\n\t\t\t\t\t\t<li><a href=\"pages/PlaneHelper.html\" target=\"viewer\">PlaneHelper</a></li>\n\t\t\t\t\t\t<li><a href=\"pages/PointLightHelper.html\" target=\"viewer\">PointLightHelper</a></li>\n\t\t\t\t\t\t<li><a href=\"pages/PolarGridHelper.html\" target=\"viewer\">PolarGridHelper</a></li>\n\t\t\t\t\t\t<li><a href=\"pages/SkeletonHelper.html\" target=\"viewer\">SkeletonHelper</a></li>\n\t\t\t\t\t\t<li><a href=\"pages/SpotLightHelper.html\" target=\"viewer\">SpotLightHelper</a></li>\n\t\t\t\t\t</ul>\n\t\t\t\t\t<h3>Lights</h3>\n\t\t\t\t\t<ul>\n\t\t\t\t\t\t<li><a href=\"pages/AmbientLight.html\" target=\"viewer\">AmbientLight</a></li>\n\t\t\t\t\t\t<li><a href=\"pages/DirectionalLight.html\" target=\"viewer\">DirectionalLight</a></li>\n\t\t\t\t\t\t<li><a href=\"pages/DirectionalLightShadow.html\" target=\"viewer\">DirectionalLightShadow</a></li>\n\t\t\t\t\t\t<li><a href=\"pages/HemisphereLight.html\" target=\"viewer\">HemisphereLight</a></li>\n\t\t\t\t\t\t<li><a href=\"pages/IESSpotLight.html\" target=\"viewer\">IESSpotLight</a></li>\n\t\t\t\t\t\t<li><a href=\"pages/Light.html\" target=\"viewer\">Light</a></li>\n\t\t\t\t\t\t<li><a href=\"pages/LightProbe.html\" target=\"viewer\">LightProbe</a></li>\n\t\t\t\t\t\t<li><a href=\"pages/LightShadow.html\" target=\"viewer\">LightShadow</a></li>\n\t\t\t\t\t\t<li><a href=\"pages/PointLight.html\" target=\"viewer\">PointLight</a></li>\n\t\t\t\t\t\t<li><a href=\"pages/PointLightShadow.html\" target=\"viewer\">PointLightShadow</a></li>\n\t\t\t\t\t\t<li><a href=\"pages/ProjectorLight.html\" target=\"viewer\">ProjectorLight</a></li>\n\t\t\t\t\t\t<li><a href=\"pages/RectAreaLight.html\" target=\"viewer\">RectAreaLight</a></li>\n\t\t\t\t\t\t<li><a href=\"pages/SpotLight.html\" target=\"viewer\">SpotLight</a></li>\n\t\t\t\t\t\t<li><a href=\"pages/SpotLightShadow.html\" target=\"viewer\">SpotLightShadow</a></li>\n\t\t\t\t\t</ul>\n\t\t\t\t\t<h3>Loaders</h3>\n\t\t\t\t\t<ul>\n\t\t\t\t\t\t<li><a href=\"pages/AnimationLoader.html\" target=\"viewer\">AnimationLoader</a></li>\n\t\t\t\t\t\t<li><a href=\"pages/AudioLoader.html\" target=\"viewer\">AudioLoader</a></li>\n\t\t\t\t\t\t<li><a href=\"pages/BufferGeometryLoader.html\" target=\"viewer\">BufferGeometryLoader</a></li>\n\t\t\t\t\t\t<li><a href=\"pages/Cache.html\" target=\"viewer\">Cache</a></li>\n\t\t\t\t\t\t<li><a href=\"pages/CompressedTextureLoader.html\" target=\"viewer\">CompressedTextureLoader</a></li>\n\t\t\t\t\t\t<li><a href=\"pages/CubeTextureLoader.html\" target=\"viewer\">CubeTextureLoader</a></li>\n\t\t\t\t\t\t<li><a href=\"pages/DataTextureLoader.html\" target=\"viewer\">DataTextureLoader</a></li>\n\t\t\t\t\t\t<li><a href=\"pages/FileLoader.html\" target=\"viewer\">FileLoader</a></li>\n\t\t\t\t\t\t<li><a href=\"pages/ImageBitmapLoader.html\" target=\"viewer\">ImageBitmapLoader</a></li>\n\t\t\t\t\t\t<li><a href=\"pages/ImageLoader.html\" target=\"viewer\">ImageLoader</a></li>\n\t\t\t\t\t\t<li><a href=\"pages/Loader.html\" target=\"viewer\">Loader</a></li>\n\t\t\t\t\t\t<li><a href=\"pages/LoaderUtils.html\" target=\"viewer\">LoaderUtils</a></li>\n\t\t\t\t\t\t<li><a href=\"pages/LoadingManager.html\" target=\"viewer\">LoadingManager</a></li>\n\t\t\t\t\t\t<li><a href=\"pages/MaterialLoader.html\" target=\"viewer\">MaterialLoader</a></li>\n\t\t\t\t\t\t<li><a href=\"pages/NodeLoader.html\" target=\"viewer\">NodeLoader</a></li>\n\t\t\t\t\t\t<li><a href=\"pages/NodeMaterialLoader.html\" target=\"viewer\">NodeMaterialLoader</a></li>\n\t\t\t\t\t\t<li><a href=\"pages/NodeObjectLoader.html\" target=\"viewer\">NodeObjectLoader</a></li>\n\t\t\t\t\t\t<li><a href=\"pages/ObjectLoader.html\" target=\"viewer\">ObjectLoader</a></li>\n\t\t\t\t\t\t<li><a href=\"pages/TextureLoader.html\" target=\"viewer\">TextureLoader</a></li>\n\t\t\t\t\t</ul>\n\t\t\t\t\t<h3>Materials</h3>\n\t\t\t\t\t<ul>\n\t\t\t\t\t\t<li><a href=\"pages/Line2NodeMaterial.html\" target=\"viewer\">Line2NodeMaterial</a></li>\n\t\t\t\t\t\t<li><a href=\"pages/LineBasicMaterial.html\" target=\"viewer\">LineBasicMaterial</a></li>\n\t\t\t\t\t\t<li><a href=\"pages/LineBasicNodeMaterial.html\" target=\"viewer\">LineBasicNodeMaterial</a></li>\n\t\t\t\t\t\t<li><a href=\"pages/LineDashedMaterial.html\" target=\"viewer\">LineDashedMaterial</a></li>\n\t\t\t\t\t\t<li><a href=\"pages/LineDashedNodeMaterial.html\" target=\"viewer\">LineDashedNodeMaterial</a></li>\n\t\t\t\t\t\t<li><a href=\"pages/Material.html\" target=\"viewer\">Material</a></li>\n\t\t\t\t\t\t<li><a href=\"pages/MeshBasicMaterial.html\" target=\"viewer\">MeshBasicMaterial</a></li>\n\t\t\t\t\t\t<li><a href=\"pages/MeshBasicNodeMaterial.html\" target=\"viewer\">MeshBasicNodeMaterial</a></li>\n\t\t\t\t\t\t<li><a href=\"pages/MeshDepthMaterial.html\" target=\"viewer\">MeshDepthMaterial</a></li>\n\t\t\t\t\t\t<li><a href=\"pages/MeshDistanceMaterial.html\" target=\"viewer\">MeshDistanceMaterial</a></li>\n\t\t\t\t\t\t<li><a href=\"pages/MeshLambertMaterial.html\" target=\"viewer\">MeshLambertMaterial</a></li>\n\t\t\t\t\t\t<li><a href=\"pages/MeshLambertNodeMaterial.html\" target=\"viewer\">MeshLambertNodeMaterial</a></li>\n\t\t\t\t\t\t<li><a href=\"pages/MeshMatcapMaterial.html\" target=\"viewer\">MeshMatcapMaterial</a></li>\n\t\t\t\t\t\t<li><a href=\"pages/MeshMatcapNodeMaterial.html\" target=\"viewer\">MeshMatcapNodeMaterial</a></li>\n\t\t\t\t\t\t<li><a href=\"pages/MeshNormalMaterial.html\" target=\"viewer\">MeshNormalMaterial</a></li>\n\t\t\t\t\t\t<li><a href=\"pages/MeshNormalNodeMaterial.html\" target=\"viewer\">MeshNormalNodeMaterial</a></li>\n\t\t\t\t\t\t<li><a href=\"pages/MeshPhongMaterial.html\" target=\"viewer\">MeshPhongMaterial</a></li>\n\t\t\t\t\t\t<li><a href=\"pages/MeshPhongNodeMaterial.html\" target=\"viewer\">MeshPhongNodeMaterial</a></li>\n\t\t\t\t\t\t<li><a href=\"pages/MeshPhysicalMaterial.html\" target=\"viewer\">MeshPhysicalMaterial</a></li>\n\t\t\t\t\t\t<li><a href=\"pages/MeshPhysicalNodeMaterial.html\" target=\"viewer\">MeshPhysicalNodeMaterial</a></li>\n\t\t\t\t\t\t<li><a href=\"pages/MeshSSSNodeMaterial.html\" target=\"viewer\">MeshSSSNodeMaterial</a></li>\n\t\t\t\t\t\t<li><a href=\"pages/MeshStandardMaterial.html\" target=\"viewer\">MeshStandardMaterial</a></li>\n\t\t\t\t\t\t<li><a href=\"pages/MeshStandardNodeMaterial.html\" target=\"viewer\">MeshStandardNodeMaterial</a></li>\n\t\t\t\t\t\t<li><a href=\"pages/MeshToonMaterial.html\" target=\"viewer\">MeshToonMaterial</a></li>\n\t\t\t\t\t\t<li><a href=\"pages/MeshToonNodeMaterial.html\" target=\"viewer\">MeshToonNodeMaterial</a></li>\n\t\t\t\t\t\t<li><a href=\"pages/NodeMaterial.html\" target=\"viewer\">NodeMaterial</a></li>\n\t\t\t\t\t\t<li><a href=\"pages/NodeMaterialObserver.html\" target=\"viewer\">NodeMaterialObserver</a></li>\n\t\t\t\t\t\t<li><a href=\"pages/PointsMaterial.html\" target=\"viewer\">PointsMaterial</a></li>\n\t\t\t\t\t\t<li><a href=\"pages/PointsNodeMaterial.html\" target=\"viewer\">PointsNodeMaterial</a></li>\n\t\t\t\t\t\t<li><a href=\"pages/RawShaderMaterial.html\" target=\"viewer\">RawShaderMaterial</a></li>\n\t\t\t\t\t\t<li><a href=\"pages/SSSLightingModel.html\" target=\"viewer\">SSSLightingModel</a></li>\n\t\t\t\t\t\t<li><a href=\"pages/ShaderMaterial.html\" target=\"viewer\">ShaderMaterial</a></li>\n\t\t\t\t\t\t<li><a href=\"pages/ShadowMaterial.html\" target=\"viewer\">ShadowMaterial</a></li>\n\t\t\t\t\t\t<li><a href=\"pages/ShadowNodeMaterial.html\" target=\"viewer\">ShadowNodeMaterial</a></li>\n\t\t\t\t\t\t<li><a href=\"pages/SpriteMaterial.html\" target=\"viewer\">SpriteMaterial</a></li>\n\t\t\t\t\t\t<li><a href=\"pages/SpriteNodeMaterial.html\" target=\"viewer\">SpriteNodeMaterial</a></li>\n\t\t\t\t\t\t<li><a href=\"pages/VolumeNodeMaterial.html\" target=\"viewer\">VolumeNodeMaterial</a></li>\n\t\t\t\t\t</ul>\n\t\t\t\t\t<h3>Math</h3>\n\t\t\t\t\t<ul>\n\t\t\t\t\t\t<li><a href=\"pages/BezierInterpolant.html\" target=\"viewer\">BezierInterpolant</a></li>\n\t\t\t\t\t\t<li><a href=\"pages/Box2.html\" target=\"viewer\">Box2</a></li>\n\t\t\t\t\t\t<li><a href=\"pages/Box3.html\" target=\"viewer\">Box3</a></li>\n\t\t\t\t\t\t<li><a href=\"pages/Color.html\" target=\"viewer\">Color</a></li>\n\t\t\t\t\t\t<li><a href=\"pages/CubicInterpolant.html\" target=\"viewer\">CubicInterpolant</a></li>\n\t\t\t\t\t\t<li><a href=\"pages/Cylindrical.html\" target=\"viewer\">Cylindrical</a></li>\n\t\t\t\t\t\t<li><a href=\"pages/DiscreteInterpolant.html\" target=\"viewer\">DiscreteInterpolant</a></li>\n\t\t\t\t\t\t<li><a href=\"pages/Euler.html\" target=\"viewer\">Euler</a></li>\n\t\t\t\t\t\t<li><a href=\"pages/Frustum.html\" target=\"viewer\">Frustum</a></li>\n\t\t\t\t\t\t<li><a href=\"pages/FrustumArray.html\" target=\"viewer\">FrustumArray</a></li>\n\t\t\t\t\t\t<li><a href=\"pages/Interpolant.html\" target=\"viewer\">Interpolant</a></li>\n\t\t\t\t\t\t<li><a href=\"pages/Line3.html\" target=\"viewer\">Line3</a></li>\n\t\t\t\t\t\t<li><a href=\"pages/LinearInterpolant.html\" target=\"viewer\">LinearInterpolant</a></li>\n\t\t\t\t\t\t<li><a href=\"pages/MathUtils.html\" target=\"viewer\">MathUtils</a></li>\n\t\t\t\t\t\t<li><a href=\"pages/Matrix2.html\" target=\"viewer\">Matrix2</a></li>\n\t\t\t\t\t\t<li><a href=\"pages/Matrix3.html\" target=\"viewer\">Matrix3</a></li>\n\t\t\t\t\t\t<li><a href=\"pages/Matrix4.html\" target=\"viewer\">Matrix4</a></li>\n\t\t\t\t\t\t<li><a href=\"pages/Plane.html\" target=\"viewer\">Plane</a></li>\n\t\t\t\t\t\t<li><a href=\"pages/Quaternion.html\" target=\"viewer\">Quaternion</a></li>\n\t\t\t\t\t\t<li><a href=\"pages/QuaternionLinearInterpolant.html\" target=\"viewer\">QuaternionLinearInterpolant</a></li>\n\t\t\t\t\t\t<li><a href=\"pages/Ray.html\" target=\"viewer\">Ray</a></li>\n\t\t\t\t\t\t<li><a href=\"pages/Sphere.html\" target=\"viewer\">Sphere</a></li>\n\t\t\t\t\t\t<li><a href=\"pages/Spherical.html\" target=\"viewer\">Spherical</a></li>\n\t\t\t\t\t\t<li><a href=\"pages/SphericalHarmonics3.html\" target=\"viewer\">SphericalHarmonics3</a></li>\n\t\t\t\t\t\t<li><a href=\"pages/Triangle.html\" target=\"viewer\">Triangle</a></li>\n\t\t\t\t\t\t<li><a href=\"pages/Vector2.html\" target=\"viewer\">Vector2</a></li>\n\t\t\t\t\t\t<li><a href=\"pages/Vector3.html\" target=\"viewer\">Vector3</a></li>\n\t\t\t\t\t\t<li><a href=\"pages/Vector4.html\" target=\"viewer\">Vector4</a></li>\n\t\t\t\t\t</ul>\n\t\t\t\t\t<h3>Nodes</h3>\n\t\t\t\t\t<ul>\n\t\t\t\t\t\t<li><a href=\"pages/AONode.html\" target=\"viewer\">AONode</a></li>\n\t\t\t\t\t\t<li><a href=\"pages/AmbientLightNode.html\" target=\"viewer\">AmbientLightNode</a></li>\n\t\t\t\t\t\t<li><a href=\"pages/AnalyticLightNode.html\" target=\"viewer\">AnalyticLightNode</a></li>\n\t\t\t\t\t\t<li><a href=\"pages/ArrayElementNode.html\" target=\"viewer\">ArrayElementNode</a></li>\n\t\t\t\t\t\t<li><a href=\"pages/ArrayNode.html\" target=\"viewer\">ArrayNode</a></li>\n\t\t\t\t\t\t<li><a href=\"pages/AssignNode.html\" target=\"viewer\">AssignNode</a></li>\n\t\t\t\t\t\t<li><a href=\"pages/AtomicFunctionNode.html\" target=\"viewer\">AtomicFunctionNode</a></li>\n\t\t\t\t\t\t<li><a href=\"pages/AttributeNode.html\" target=\"viewer\">AttributeNode</a></li>\n\t\t\t\t\t\t<li><a href=\"pages/BarrierNode.html\" target=\"viewer\">BarrierNode</a></li>\n\t\t\t\t\t\t<li><a href=\"pages/BasicEnvironmentNode.html\" target=\"viewer\">BasicEnvironmentNode</a></li>\n\t\t\t\t\t\t<li><a href=\"pages/BasicLightMapNode.html\" target=\"viewer\">BasicLightMapNode</a></li>\n\t\t\t\t\t\t<li><a href=\"pages/BasicLightingModel.html\" target=\"viewer\">BasicLightingModel</a></li>\n\t\t\t\t\t\t<li><a href=\"pages/BitcastNode.html\" target=\"viewer\">BitcastNode</a></li>\n\t\t\t\t\t\t<li><a href=\"pages/BitcountNode.html\" target=\"viewer\">BitcountNode</a></li>\n\t\t\t\t\t\t<li><a href=\"pages/BufferAttributeNode.html\" target=\"viewer\">BufferAttributeNode</a></li>\n\t\t\t\t\t\t<li><a href=\"pages/BufferNode.html\" target=\"viewer\">BufferNode</a></li>\n\t\t\t\t\t\t<li><a href=\"pages/BuiltinNode.html\" target=\"viewer\">BuiltinNode</a></li>\n\t\t\t\t\t\t<li><a href=\"pages/BumpMapNode.html\" target=\"viewer\">BumpMapNode</a></li>\n\t\t\t\t\t\t<li><a href=\"pages/BypassNode.html\" target=\"viewer\">BypassNode</a></li>\n\t\t\t\t\t\t<li><a href=\"pages/ClippingNode.html\" target=\"viewer\">ClippingNode</a></li>\n\t\t\t\t\t\t<li><a href=\"pages/CodeNode.html\" target=\"viewer\">CodeNode</a></li>\n\t\t\t\t\t\t<li><a href=\"pages/ColorSpaceNode.html\" target=\"viewer\">ColorSpaceNode</a></li>\n\t\t\t\t\t\t<li><a href=\"pages/ComputeBuiltinNode.html\" target=\"viewer\">ComputeBuiltinNode</a></li>\n\t\t\t\t\t\t<li><a href=\"pages/ComputeNode.html\" target=\"viewer\">ComputeNode</a></li>\n\t\t\t\t\t\t<li><a href=\"pages/ConditionalNode.html\" target=\"viewer\">ConditionalNode</a></li>\n\t\t\t\t\t\t<li><a href=\"pages/ConstNode.html\" target=\"viewer\">ConstNode</a></li>\n\t\t\t\t\t\t<li><a href=\"pages/ContextNode.html\" target=\"viewer\">ContextNode</a></li>\n\t\t\t\t\t\t<li><a href=\"pages/ConvertNode.html\" target=\"viewer\">ConvertNode</a></li>\n\t\t\t\t\t\t<li><a href=\"pages/CubeMapNode.html\" target=\"viewer\">CubeMapNode</a></li>\n\t\t\t\t\t\t<li><a href=\"pages/CubeTextureNode.html\" target=\"viewer\">CubeTextureNode</a></li>\n\t\t\t\t\t\t<li><a href=\"pages/DirectionalLightNode.html\" target=\"viewer\">DirectionalLightNode</a></li>\n\t\t\t\t\t\t<li><a href=\"pages/EnvironmentNode.html\" target=\"viewer\">EnvironmentNode</a></li>\n\t\t\t\t\t\t<li><a href=\"pages/EventNode.html\" target=\"viewer\">EventNode</a></li>\n\t\t\t\t\t\t<li><a href=\"pages/ExpressionNode.html\" target=\"viewer\">ExpressionNode</a></li>\n\t\t\t\t\t\t<li><a href=\"pages/FlipNode.html\" target=\"viewer\">FlipNode</a></li>\n\t\t\t\t\t\t<li><a href=\"pages/FrontFacingNode.html\" target=\"viewer\">FrontFacingNode</a></li>\n\t\t\t\t\t\t<li><a href=\"pages/FunctionCallNode.html\" target=\"viewer\">FunctionCallNode</a></li>\n\t\t\t\t\t\t<li><a href=\"pages/FunctionNode.html\" target=\"viewer\">FunctionNode</a></li>\n\t\t\t\t\t\t<li><a href=\"pages/FunctionOverloadingNode.html\" target=\"viewer\">FunctionOverloadingNode</a></li>\n\t\t\t\t\t\t<li><a href=\"pages/GLSLNodeFunction.html\" target=\"viewer\">GLSLNodeFunction</a></li>\n\t\t\t\t\t\t<li><a href=\"pages/GLSLNodeParser.html\" target=\"viewer\">GLSLNodeParser</a></li>\n\t\t\t\t\t\t<li><a href=\"pages/HemisphereLightNode.html\" target=\"viewer\">HemisphereLightNode</a></li>\n\t\t\t\t\t\t<li><a href=\"pages/IESSpotLightNode.html\" target=\"viewer\">IESSpotLightNode</a></li>\n\t\t\t\t\t\t<li><a href=\"pages/IndexNode.html\" target=\"viewer\">IndexNode</a></li>\n\t\t\t\t\t\t<li><a href=\"pages/InputNode.html\" target=\"viewer\">InputNode</a></li>\n\t\t\t\t\t\t<li><a href=\"pages/InspectorNode.html\" target=\"viewer\">InspectorNode</a></li>\n\t\t\t\t\t\t<li><a href=\"pages/IrradianceNode.html\" target=\"viewer\">IrradianceNode</a></li>\n\t\t\t\t\t\t<li><a href=\"pages/IsolateNode.html\" target=\"viewer\">IsolateNode</a></li>\n\t\t\t\t\t\t<li><a href=\"pages/JoinNode.html\" target=\"viewer\">JoinNode</a></li>\n\t\t\t\t\t\t<li><a href=\"pages/LightProbeNode.html\" target=\"viewer\">LightProbeNode</a></li>\n\t\t\t\t\t\t<li><a href=\"pages/LightingContextNode.html\" target=\"viewer\">LightingContextNode</a></li>\n\t\t\t\t\t\t<li><a href=\"pages/LightingModel.html\" target=\"viewer\">LightingModel</a></li>\n\t\t\t\t\t\t<li><a href=\"pages/LightingNode.html\" target=\"viewer\">LightingNode</a></li>\n\t\t\t\t\t\t<li><a href=\"pages/LightsNode.html\" target=\"viewer\">LightsNode</a></li>\n\t\t\t\t\t\t<li><a href=\"pages/LoopNode.html\" target=\"viewer\">LoopNode</a></li>\n\t\t\t\t\t\t<li><a href=\"pages/MRTNode.html\" target=\"viewer\">MRTNode</a></li>\n\t\t\t\t\t\t<li><a href=\"pages/MaterialNode.html\" target=\"viewer\">MaterialNode</a></li>\n\t\t\t\t\t\t<li><a href=\"pages/MaterialReferenceNode.html\" target=\"viewer\">MaterialReferenceNode</a></li>\n\t\t\t\t\t\t<li><a href=\"pages/MathNode.html\" target=\"viewer\">MathNode</a></li>\n\t\t\t\t\t\t<li><a href=\"pages/MaxMipLevelNode.html\" target=\"viewer\">MaxMipLevelNode</a></li>\n\t\t\t\t\t\t<li><a href=\"pages/MemberNode.html\" target=\"viewer\">MemberNode</a></li>\n\t\t\t\t\t\t<li><a href=\"pages/ModelNode.html\" target=\"viewer\">ModelNode</a></li>\n\t\t\t\t\t\t<li><a href=\"pages/Node.html\" target=\"viewer\">Node</a></li>\n\t\t\t\t\t\t<li><a href=\"pages/NodeAttribute.html\" target=\"viewer\">NodeAttribute</a></li>\n\t\t\t\t\t\t<li><a href=\"pages/NodeBuilder.html\" target=\"viewer\">NodeBuilder</a></li>\n\t\t\t\t\t\t<li><a href=\"pages/NodeCache.html\" target=\"viewer\">NodeCache</a></li>\n\t\t\t\t\t\t<li><a href=\"pages/NodeCode.html\" target=\"viewer\">NodeCode</a></li>\n\t\t\t\t\t\t<li><a href=\"pages/NodeError.html\" target=\"viewer\">NodeError</a></li>\n\t\t\t\t\t\t<li><a href=\"pages/NodeFrame.html\" target=\"viewer\">NodeFrame</a></li>\n\t\t\t\t\t\t<li><a href=\"pages/NodeFunction.html\" target=\"viewer\">NodeFunction</a></li>\n\t\t\t\t\t\t<li><a href=\"pages/NodeFunctionInput.html\" target=\"viewer\">NodeFunctionInput</a></li>\n\t\t\t\t\t\t<li><a href=\"pages/NodeParser.html\" target=\"viewer\">NodeParser</a></li>\n\t\t\t\t\t\t<li><a href=\"pages/NodeUniform.html\" target=\"viewer\">NodeUniform</a></li>\n\t\t\t\t\t\t<li><a href=\"pages/NodeVar.html\" target=\"viewer\">NodeVar</a></li>\n\t\t\t\t\t\t<li><a href=\"pages/NodeVarying.html\" target=\"viewer\">NodeVarying</a></li>\n\t\t\t\t\t\t<li><a href=\"pages/NormalMapNode.html\" target=\"viewer\">NormalMapNode</a></li>\n\t\t\t\t\t\t<li><a href=\"pages/Object3DNode.html\" target=\"viewer\">Object3DNode</a></li>\n\t\t\t\t\t\t<li><a href=\"pages/OperatorNode.html\" target=\"viewer\">OperatorNode</a></li>\n\t\t\t\t\t\t<li><a href=\"pages/OutputStructNode.html\" target=\"viewer\">OutputStructNode</a></li>\n\t\t\t\t\t\t<li><a href=\"pages/OverrideContextNode.html\" target=\"viewer\">OverrideContextNode</a></li>\n\t\t\t\t\t\t<li><a href=\"pages/PMREMNode.html\" target=\"viewer\">PMREMNode</a></li>\n\t\t\t\t\t\t<li><a href=\"pages/PackFloatNode.html\" target=\"viewer\">PackFloatNode</a></li>\n\t\t\t\t\t\t<li><a href=\"pages/ParameterNode.html\" target=\"viewer\">ParameterNode</a></li>\n\t\t\t\t\t\t<li><a href=\"pages/PassMultipleTextureNode.html\" target=\"viewer\">PassMultipleTextureNode</a></li>\n\t\t\t\t\t\t<li><a href=\"pages/PassNode.html\" target=\"viewer\">PassNode</a></li>\n\t\t\t\t\t\t<li><a href=\"pages/PassTextureNode.html\" target=\"viewer\">PassTextureNode</a></li>\n\t\t\t\t\t\t<li><a href=\"pages/PhongLightingModel.html\" target=\"viewer\">PhongLightingModel</a></li>\n\t\t\t\t\t\t<li><a href=\"pages/PhysicalLightingModel.html\" target=\"viewer\">PhysicalLightingModel</a></li>\n\t\t\t\t\t\t<li><a href=\"pages/PointLightNode.html\" target=\"viewer\">PointLightNode</a></li>\n\t\t\t\t\t\t<li><a href=\"pages/PointShadowNode.html\" target=\"viewer\">PointShadowNode</a></li>\n\t\t\t\t\t\t<li><a href=\"pages/PointUVNode.html\" target=\"viewer\">PointUVNode</a></li>\n\t\t\t\t\t\t<li><a href=\"pages/ProjectorLightNode.html\" target=\"viewer\">ProjectorLightNode</a></li>\n\t\t\t\t\t\t<li><a href=\"pages/PropertyNode.html\" target=\"viewer\">PropertyNode</a></li>\n\t\t\t\t\t\t<li><a href=\"pages/RTTNode.html\" target=\"viewer\">RTTNode</a></li>\n\t\t\t\t\t\t<li><a href=\"pages/RangeNode.html\" target=\"viewer\">RangeNode</a></li>\n\t\t\t\t\t\t<li><a href=\"pages/RectAreaLightNode.html\" target=\"viewer\">RectAreaLightNode</a></li>\n\t\t\t\t\t\t<li><a href=\"pages/ReferenceBaseNode.html\" target=\"viewer\">ReferenceBaseNode</a></li>\n\t\t\t\t\t\t<li><a href=\"pages/ReferenceElementNode.html\" target=\"viewer\">ReferenceElementNode</a></li>\n\t\t\t\t\t\t<li><a href=\"pages/ReferenceNode.html\" target=\"viewer\">ReferenceNode</a></li>\n\t\t\t\t\t\t<li><a href=\"pages/ReflectorNode.html\" target=\"viewer\">ReflectorNode</a></li>\n\t\t\t\t\t\t<li><a href=\"pages/RenderOutputNode.html\" target=\"viewer\">RenderOutputNode</a></li>\n\t\t\t\t\t\t<li><a href=\"pages/RendererReferenceNode.html\" target=\"viewer\">RendererReferenceNode</a></li>\n\t\t\t\t\t\t<li><a href=\"pages/RotateNode.html\" target=\"viewer\">RotateNode</a></li>\n\t\t\t\t\t\t<li><a href=\"pages/SampleNode.html\" target=\"viewer\">SampleNode</a></li>\n\t\t\t\t\t\t<li><a href=\"pages/ScreenNode.html\" target=\"viewer\">ScreenNode</a></li>\n\t\t\t\t\t\t<li><a href=\"pages/SetNode.html\" target=\"viewer\">SetNode</a></li>\n\t\t\t\t\t\t<li><a href=\"pages/ShadowBaseNode.html\" target=\"viewer\">ShadowBaseNode</a></li>\n\t\t\t\t\t\t<li><a href=\"pages/ShadowMaskModel.html\" target=\"viewer\">ShadowMaskModel</a></li>\n\t\t\t\t\t\t<li><a href=\"pages/ShadowNode.html\" target=\"viewer\">ShadowNode</a></li>\n\t\t\t\t\t\t<li><a href=\"pages/SplitNode.html\" target=\"viewer\">SplitNode</a></li>\n\t\t\t\t\t\t<li><a href=\"pages/SpotLightNode.html\" target=\"viewer\">SpotLightNode</a></li>\n\t\t\t\t\t\t<li><a href=\"pages/StackNode.html\" target=\"viewer\">StackNode</a></li>\n\t\t\t\t\t\t<li><a href=\"pages/StackTrace.html\" target=\"viewer\">StackTrace</a></li>\n\t\t\t\t\t\t<li><a href=\"pages/StorageArrayElementNode.html\" target=\"viewer\">StorageArrayElementNode</a></li>\n\t\t\t\t\t\t<li><a href=\"pages/StorageBufferNode.html\" target=\"viewer\">StorageBufferNode</a></li>\n\t\t\t\t\t\t<li><a href=\"pages/StorageTexture3DNode.html\" target=\"viewer\">StorageTexture3DNode</a></li>\n\t\t\t\t\t\t<li><a href=\"pages/StorageTextureNode.html\" target=\"viewer\">StorageTextureNode</a></li>\n\t\t\t\t\t\t<li><a href=\"pages/StructNode.html\" target=\"viewer\">StructNode</a></li>\n\t\t\t\t\t\t<li><a href=\"pages/StructTypeNode.html\" target=\"viewer\">StructTypeNode</a></li>\n\t\t\t\t\t\t<li><a href=\"pages/SubBuildNode.html\" target=\"viewer\">SubBuildNode</a></li>\n\t\t\t\t\t\t<li><a href=\"pages/SubgroupFunctionNode.html\" target=\"viewer\">SubgroupFunctionNode</a></li>\n\t\t\t\t\t\t<li><a href=\"pages/TempNode.html\" target=\"viewer\">TempNode</a></li>\n\t\t\t\t\t\t<li><a href=\"pages/Texture3DNode.html\" target=\"viewer\">Texture3DNode</a></li>\n\t\t\t\t\t\t<li><a href=\"pages/TextureNode.html\" target=\"viewer\">TextureNode</a></li>\n\t\t\t\t\t\t<li><a href=\"pages/TextureSizeNode.html\" target=\"viewer\">TextureSizeNode</a></li>\n\t\t\t\t\t\t<li><a href=\"pages/ToneMappingNode.html\" target=\"viewer\">ToneMappingNode</a></li>\n\t\t\t\t\t\t<li><a href=\"pages/ToonLightingModel.html\" target=\"viewer\">ToonLightingModel</a></li>\n\t\t\t\t\t\t<li><a href=\"pages/ToonOutlinePassNode.html\" target=\"viewer\">ToonOutlinePassNode</a></li>\n\t\t\t\t\t\t<li><a href=\"pages/UniformArrayElementNode.html\" target=\"viewer\">UniformArrayElementNode</a></li>\n\t\t\t\t\t\t<li><a href=\"pages/UniformArrayNode.html\" target=\"viewer\">UniformArrayNode</a></li>\n\t\t\t\t\t\t<li><a href=\"pages/UniformGroupNode.html\" target=\"viewer\">UniformGroupNode</a></li>\n\t\t\t\t\t\t<li><a href=\"pages/UniformNode.html\" target=\"viewer\">UniformNode</a></li>\n\t\t\t\t\t\t<li><a href=\"pages/UnpackFloatNode.html\" target=\"viewer\">UnpackFloatNode</a></li>\n\t\t\t\t\t\t<li><a href=\"pages/UserDataNode.html\" target=\"viewer\">UserDataNode</a></li>\n\t\t\t\t\t\t<li><a href=\"pages/VarNode.html\" target=\"viewer\">VarNode</a></li>\n\t\t\t\t\t\t<li><a href=\"pages/VaryingNode.html\" target=\"viewer\">VaryingNode</a></li>\n\t\t\t\t\t\t<li><a href=\"pages/VelocityNode.html\" target=\"viewer\">VelocityNode</a></li>\n\t\t\t\t\t\t<li><a href=\"pages/VertexColorNode.html\" target=\"viewer\">VertexColorNode</a></li>\n\t\t\t\t\t\t<li><a href=\"pages/ViewportDepthNode.html\" target=\"viewer\">ViewportDepthNode</a></li>\n\t\t\t\t\t\t<li><a href=\"pages/ViewportDepthTextureNode.html\" target=\"viewer\">ViewportDepthTextureNode</a></li>\n\t\t\t\t\t\t<li><a href=\"pages/ViewportSharedTextureNode.html\" target=\"viewer\">ViewportSharedTextureNode</a></li>\n\t\t\t\t\t\t<li><a href=\"pages/ViewportTextureNode.html\" target=\"viewer\">ViewportTextureNode</a></li>\n\t\t\t\t\t\t<li><a href=\"pages/VolumetricLightingModel.html\" target=\"viewer\">VolumetricLightingModel</a></li>\n\t\t\t\t\t\t<li><a href=\"pages/WorkgroupInfoElementNode.html\" target=\"viewer\">WorkgroupInfoElementNode</a></li>\n\t\t\t\t\t\t<li><a href=\"pages/WorkgroupInfoNode.html\" target=\"viewer\">WorkgroupInfoNode</a></li>\n\t\t\t\t\t</ul>\n\t\t\t\t\t<h3>Objects</h3>\n\t\t\t\t\t<ul>\n\t\t\t\t\t\t<li><a href=\"pages/BatchedMesh.html\" target=\"viewer\">BatchedMesh</a></li>\n\t\t\t\t\t\t<li><a href=\"pages/Bone.html\" target=\"viewer\">Bone</a></li>\n\t\t\t\t\t\t<li><a href=\"pages/ClippingGroup.html\" target=\"viewer\">ClippingGroup</a></li>\n\t\t\t\t\t\t<li><a href=\"pages/Group.html\" target=\"viewer\">Group</a></li>\n\t\t\t\t\t\t<li><a href=\"pages/InstancedMesh.html\" target=\"viewer\">InstancedMesh</a></li>\n\t\t\t\t\t\t<li><a href=\"pages/LOD.html\" target=\"viewer\">LOD</a></li>\n\t\t\t\t\t\t<li><a href=\"pages/Line.html\" target=\"viewer\">Line</a></li>\n\t\t\t\t\t\t<li><a href=\"pages/LineLoop.html\" target=\"viewer\">LineLoop</a></li>\n\t\t\t\t\t\t<li><a href=\"pages/LineSegments.html\" target=\"viewer\">LineSegments</a></li>\n\t\t\t\t\t\t<li><a href=\"pages/Mesh.html\" target=\"viewer\">Mesh</a></li>\n\t\t\t\t\t\t<li><a href=\"pages/Points.html\" target=\"viewer\">Points</a></li>\n\t\t\t\t\t\t<li><a href=\"pages/Skeleton.html\" target=\"viewer\">Skeleton</a></li>\n\t\t\t\t\t\t<li><a href=\"pages/SkinnedMesh.html\" target=\"viewer\">SkinnedMesh</a></li>\n\t\t\t\t\t\t<li><a href=\"pages/Sprite.html\" target=\"viewer\">Sprite</a></li>\n\t\t\t\t\t</ul>\n\t\t\t\t\t<h3>Renderers</h3>\n\t\t\t\t\t<ul>\n\t\t\t\t\t\t<li><a href=\"pages/Backend.html\" target=\"viewer\">Backend</a></li>\n\t\t\t\t\t\t<li><a href=\"pages/BlendMode.html\" target=\"viewer\">BlendMode</a></li>\n\t\t\t\t\t\t<li><a href=\"pages/BundleGroup.html\" target=\"viewer\">BundleGroup</a></li>\n\t\t\t\t\t\t<li><a href=\"pages/CanvasTarget.html\" target=\"viewer\">CanvasTarget</a></li>\n\t\t\t\t\t\t<li><a href=\"pages/CubeRenderTarget.html\" target=\"viewer\">CubeRenderTarget</a></li>\n\t\t\t\t\t\t<li><a href=\"pages/GLSLNodeBuilder.html\" target=\"viewer\">GLSLNodeBuilder</a></li>\n\t\t\t\t\t\t<li><a href=\"pages/IndirectStorageBufferAttribute.html\" target=\"viewer\">IndirectStorageBufferAttribute</a></li>\n\t\t\t\t\t\t<li><a href=\"pages/Info.html\" target=\"viewer\">Info</a></li>\n\t\t\t\t\t\t<li><a href=\"pages/InspectorBase.html\" target=\"viewer\">InspectorBase</a></li>\n\t\t\t\t\t\t<li><a href=\"pages/PostProcessing.html\" target=\"viewer\">PostProcessing</a></li>\n\t\t\t\t\t\t<li><a href=\"pages/QuadMesh.html\" target=\"viewer\">QuadMesh</a></li>\n\t\t\t\t\t\t<li><a href=\"pages/ReadbackBuffer.html\" target=\"viewer\">ReadbackBuffer</a></li>\n\t\t\t\t\t\t<li><a href=\"pages/RenderPipeline.html\" target=\"viewer\">RenderPipeline</a></li>\n\t\t\t\t\t\t<li><a href=\"pages/Renderer.html\" target=\"viewer\">Renderer</a></li>\n\t\t\t\t\t\t<li><a href=\"pages/StandardNodeLibrary.html\" target=\"viewer\">StandardNodeLibrary</a></li>\n\t\t\t\t\t\t<li><a href=\"pages/Storage3DTexture.html\" target=\"viewer\">Storage3DTexture</a></li>\n\t\t\t\t\t\t<li><a href=\"pages/StorageArrayTexture.html\" target=\"viewer\">StorageArrayTexture</a></li>\n\t\t\t\t\t\t<li><a href=\"pages/StorageBufferAttribute.html\" target=\"viewer\">StorageBufferAttribute</a></li>\n\t\t\t\t\t\t<li><a href=\"pages/StorageInstancedBufferAttribute.html\" target=\"viewer\">StorageInstancedBufferAttribute</a></li>\n\t\t\t\t\t\t<li><a href=\"pages/StorageTexture.html\" target=\"viewer\">StorageTexture</a></li>\n\t\t\t\t\t\t<li><a href=\"pages/TimestampQueryPool.html\" target=\"viewer\">TimestampQueryPool</a></li>\n\t\t\t\t\t\t<li><a href=\"pages/WGSLNodeBuilder.html\" target=\"viewer\">WGSLNodeBuilder</a></li>\n\t\t\t\t\t\t<li><a href=\"pages/WGSLNodeFunction.html\" target=\"viewer\">WGSLNodeFunction</a></li>\n\t\t\t\t\t\t<li><a href=\"pages/WGSLNodeParser.html\" target=\"viewer\">WGSLNodeParser</a></li>\n\t\t\t\t\t\t<li><a href=\"pages/WebGL3DRenderTarget.html\" target=\"viewer\">WebGL3DRenderTarget</a></li>\n\t\t\t\t\t\t<li><a href=\"pages/WebGLArrayRenderTarget.html\" target=\"viewer\">WebGLArrayRenderTarget</a></li>\n\t\t\t\t\t\t<li><a href=\"pages/WebGLCubeRenderTarget.html\" target=\"viewer\">WebGLCubeRenderTarget</a></li>\n\t\t\t\t\t\t<li><a href=\"pages/WebGLRenderTarget.html\" target=\"viewer\">WebGLRenderTarget</a></li>\n\t\t\t\t\t\t<li><a href=\"pages/WebGLRenderer.html\" target=\"viewer\">WebGLRenderer</a></li>\n\t\t\t\t\t\t<li><a href=\"pages/WebGLTimestampQueryPool.html\" target=\"viewer\">WebGLTimestampQueryPool</a></li>\n\t\t\t\t\t\t<li><a href=\"pages/WebGPURenderer.html\" target=\"viewer\">WebGPURenderer</a></li>\n\t\t\t\t\t\t<li><a href=\"pages/WebGPUTimestampQueryPool.html\" target=\"viewer\">WebGPUTimestampQueryPool</a></li>\n\t\t\t\t\t\t<li><a href=\"pages/WebXRDepthSensing.html\" target=\"viewer\">WebXRDepthSensing</a></li>\n\t\t\t\t\t\t<li><a href=\"pages/WebXRManager.html\" target=\"viewer\">WebXRManager</a></li>\n\t\t\t\t\t\t<li><a href=\"pages/XRManager.html\" target=\"viewer\">XRManager</a></li>\n\t\t\t\t\t\t<li><a href=\"pages/module-UniformsUtils.html\" target=\"viewer\">UniformsUtils</a></li>\n\t\t\t\t\t</ul>\n\t\t\t\t\t<h3>Scenes</h3>\n\t\t\t\t\t<ul>\n\t\t\t\t\t\t<li><a href=\"pages/Fog.html\" target=\"viewer\">Fog</a></li>\n\t\t\t\t\t\t<li><a href=\"pages/FogExp2.html\" target=\"viewer\">FogExp2</a></li>\n\t\t\t\t\t\t<li><a href=\"pages/Scene.html\" target=\"viewer\">Scene</a></li>\n\t\t\t\t\t</ul>\n\t\t\t\t\t<h3>Textures</h3>\n\t\t\t\t\t<ul>\n\t\t\t\t\t\t<li><a href=\"pages/CanvasTexture.html\" target=\"viewer\">CanvasTexture</a></li>\n\t\t\t\t\t\t<li><a href=\"pages/CompressedArrayTexture.html\" target=\"viewer\">CompressedArrayTexture</a></li>\n\t\t\t\t\t\t<li><a href=\"pages/CompressedCubeTexture.html\" target=\"viewer\">CompressedCubeTexture</a></li>\n\t\t\t\t\t\t<li><a href=\"pages/CompressedTexture.html\" target=\"viewer\">CompressedTexture</a></li>\n\t\t\t\t\t\t<li><a href=\"pages/CubeDepthTexture.html\" target=\"viewer\">CubeDepthTexture</a></li>\n\t\t\t\t\t\t<li><a href=\"pages/CubeTexture.html\" target=\"viewer\">CubeTexture</a></li>\n\t\t\t\t\t\t<li><a href=\"pages/Data3DTexture.html\" target=\"viewer\">Data3DTexture</a></li>\n\t\t\t\t\t\t<li><a href=\"pages/DataArrayTexture.html\" target=\"viewer\">DataArrayTexture</a></li>\n\t\t\t\t\t\t<li><a href=\"pages/DataTexture.html\" target=\"viewer\">DataTexture</a></li>\n\t\t\t\t\t\t<li><a href=\"pages/DepthTexture.html\" target=\"viewer\">DepthTexture</a></li>\n\t\t\t\t\t\t<li><a href=\"pages/ExternalTexture.html\" target=\"viewer\">ExternalTexture</a></li>\n\t\t\t\t\t\t<li><a href=\"pages/FramebufferTexture.html\" target=\"viewer\">FramebufferTexture</a></li>\n\t\t\t\t\t\t<li><a href=\"pages/HTMLTexture.html\" target=\"viewer\">HTMLTexture</a></li>\n\t\t\t\t\t\t<li><a href=\"pages/Source.html\" target=\"viewer\">Source</a></li>\n\t\t\t\t\t\t<li><a href=\"pages/Texture.html\" target=\"viewer\">Texture</a></li>\n\t\t\t\t\t\t<li><a href=\"pages/VideoFrameTexture.html\" target=\"viewer\">VideoFrameTexture</a></li>\n\t\t\t\t\t\t<li><a href=\"pages/VideoTexture.html\" target=\"viewer\">VideoTexture</a></li>\n\t\t\t\t\t</ul>\n\t\t\t\t\t<h2>Addons</h2>\n\t\t\t\t\t<h3>Animation</h3>\n\t\t\t\t\t<ul>\n\t\t\t\t\t\t<li><a href=\"pages/AnimationClipCreator.html\" target=\"viewer\">AnimationClipCreator</a></li>\n\t\t\t\t\t\t<li><a href=\"pages/CCDIKHelper.html\" target=\"viewer\">CCDIKHelper</a></li>\n\t\t\t\t\t\t<li><a href=\"pages/CCDIKSolver.html\" target=\"viewer\">CCDIKSolver</a></li>\n\t\t\t\t\t</ul>\n\t\t\t\t\t<h3>Capabilities</h3>\n\t\t\t\t\t<ul>\n\t\t\t\t\t\t<li><a href=\"pages/WebGL.html\" target=\"viewer\">WebGL</a></li>\n\t\t\t\t\t\t<li><a href=\"pages/WebGPU.html\" target=\"viewer\">WebGPU</a></li>\n\t\t\t\t\t</ul>\n\t\t\t\t\t<h3>Controls</h3>\n\t\t\t\t\t<ul>\n\t\t\t\t\t\t<li><a href=\"pages/ArcballControls.html\" target=\"viewer\">ArcballControls</a></li>\n\t\t\t\t\t\t<li><a href=\"pages/DragControls.html\" target=\"viewer\">DragControls</a></li>\n\t\t\t\t\t\t<li><a href=\"pages/FirstPersonControls.html\" target=\"viewer\">FirstPersonControls</a></li>\n\t\t\t\t\t\t<li><a href=\"pages/FlyControls.html\" target=\"viewer\">FlyControls</a></li>\n\t\t\t\t\t\t<li><a href=\"pages/MapControls.html\" target=\"viewer\">MapControls</a></li>\n\t\t\t\t\t\t<li><a href=\"pages/OrbitControls.html\" target=\"viewer\">OrbitControls</a></li>\n\t\t\t\t\t\t<li><a href=\"pages/PointerLockControls.html\" target=\"viewer\">PointerLockControls</a></li>\n\t\t\t\t\t\t<li><a href=\"pages/TrackballControls.html\" target=\"viewer\">TrackballControls</a></li>\n\t\t\t\t\t\t<li><a href=\"pages/TransformControls.html\" target=\"viewer\">TransformControls</a></li>\n\t\t\t\t\t</ul>\n\t\t\t\t\t<h3>Csm</h3>\n\t\t\t\t\t<ul>\n\t\t\t\t\t\t<li><a href=\"pages/CSM.html\" target=\"viewer\">CSM</a></li>\n\t\t\t\t\t\t<li><a href=\"pages/CSMFrustum.html\" target=\"viewer\">CSMFrustum</a></li>\n\t\t\t\t\t\t<li><a href=\"pages/CSMHelper.html\" target=\"viewer\">CSMHelper</a></li>\n\t\t\t\t\t\t<li><a href=\"pages/CSMShadowNode.html\" target=\"viewer\">CSMShadowNode</a></li>\n\t\t\t\t\t\t<li><a href=\"pages/module-CSMShader.html\" target=\"viewer\">CSMShader</a></li>\n\t\t\t\t\t</ul>\n\t\t\t\t\t<h3>Curves</h3>\n\t\t\t\t\t<ul>\n\t\t\t\t\t\t<li><a href=\"pages/CinquefoilKnot.html\" target=\"viewer\">CinquefoilKnot</a></li>\n\t\t\t\t\t\t<li><a href=\"pages/DecoratedTorusKnot4a.html\" target=\"viewer\">DecoratedTorusKnot4a</a></li>\n\t\t\t\t\t\t<li><a href=\"pages/DecoratedTorusKnot4b.html\" target=\"viewer\">DecoratedTorusKnot4b</a></li>\n\t\t\t\t\t\t<li><a href=\"pages/DecoratedTorusKnot5a.html\" target=\"viewer\">DecoratedTorusKnot5a</a></li>\n\t\t\t\t\t\t<li><a href=\"pages/DecoratedTorusKnot5c.html\" target=\"viewer\">DecoratedTorusKnot5c</a></li>\n\t\t\t\t\t\t<li><a href=\"pages/FigureEightPolynomialKnot.html\" target=\"viewer\">FigureEightPolynomialKnot</a></li>\n\t\t\t\t\t\t<li><a href=\"pages/GrannyKnot.html\" target=\"viewer\">GrannyKnot</a></li>\n\t\t\t\t\t\t<li><a href=\"pages/HeartCurve.html\" target=\"viewer\">HeartCurve</a></li>\n\t\t\t\t\t\t<li><a href=\"pages/HelixCurve.html\" target=\"viewer\">HelixCurve</a></li>\n\t\t\t\t\t\t<li><a href=\"pages/KnotCurve.html\" target=\"viewer\">KnotCurve</a></li>\n\t\t\t\t\t\t<li><a href=\"pages/NURBSCurve.html\" target=\"viewer\">NURBSCurve</a></li>\n\t\t\t\t\t\t<li><a href=\"pages/NURBSSurface.html\" target=\"viewer\">NURBSSurface</a></li>\n\t\t\t\t\t\t<li><a href=\"pages/NURBSVolume.html\" target=\"viewer\">NURBSVolume</a></li>\n\t\t\t\t\t\t<li><a href=\"pages/TorusKnot.html\" target=\"viewer\">TorusKnot</a></li>\n\t\t\t\t\t\t<li><a href=\"pages/TrefoilKnot.html\" target=\"viewer\">TrefoilKnot</a></li>\n\t\t\t\t\t\t<li><a href=\"pages/TrefoilPolynomialKnot.html\" target=\"viewer\">TrefoilPolynomialKnot</a></li>\n\t\t\t\t\t\t<li><a href=\"pages/VivianiCurve.html\" target=\"viewer\">VivianiCurve</a></li>\n\t\t\t\t\t\t<li><a href=\"pages/module-NURBSUtils.html\" target=\"viewer\">NURBSUtils</a></li>\n\t\t\t\t\t</ul>\n\t\t\t\t\t<h3>Effects</h3>\n\t\t\t\t\t<ul>\n\t\t\t\t\t\t<li><a href=\"pages/AnaglyphEffect.html\" target=\"viewer\">AnaglyphEffect</a></li>\n\t\t\t\t\t\t<li><a href=\"pages/AsciiEffect.html\" target=\"viewer\">AsciiEffect</a></li>\n\t\t\t\t\t\t<li><a href=\"pages/OutlineEffect.html\" target=\"viewer\">OutlineEffect</a></li>\n\t\t\t\t\t\t<li><a href=\"pages/ParallaxBarrierEffect.html\" target=\"viewer\">ParallaxBarrierEffect</a></li>\n\t\t\t\t\t\t<li><a href=\"pages/StereoEffect.html\" target=\"viewer\">StereoEffect</a></li>\n\t\t\t\t\t</ul>\n\t\t\t\t\t<h3>Environments</h3>\n\t\t\t\t\t<ul>\n\t\t\t\t\t\t<li><a href=\"pages/ColorEnvironment.html\" target=\"viewer\">ColorEnvironment</a></li>\n\t\t\t\t\t\t<li><a href=\"pages/DebugEnvironment.html\" target=\"viewer\">DebugEnvironment</a></li>\n\t\t\t\t\t\t<li><a href=\"pages/RoomEnvironment.html\" target=\"viewer\">RoomEnvironment</a></li>\n\t\t\t\t\t</ul>\n\t\t\t\t\t<h3>Exporters</h3>\n\t\t\t\t\t<ul>\n\t\t\t\t\t\t<li><a href=\"pages/DRACOExporter.html\" target=\"viewer\">DRACOExporter</a></li>\n\t\t\t\t\t\t<li><a href=\"pages/EXRExporter.html\" target=\"viewer\">EXRExporter</a></li>\n\t\t\t\t\t\t<li><a href=\"pages/GLTFExporter.html\" target=\"viewer\">GLTFExporter</a></li>\n\t\t\t\t\t\t<li><a href=\"pages/KTX2Exporter.html\" target=\"viewer\">KTX2Exporter</a></li>\n\t\t\t\t\t\t<li><a href=\"pages/OBJExporter.html\" target=\"viewer\">OBJExporter</a></li>\n\t\t\t\t\t\t<li><a href=\"pages/PLYExporter.html\" target=\"viewer\">PLYExporter</a></li>\n\t\t\t\t\t\t<li><a href=\"pages/STLExporter.html\" target=\"viewer\">STLExporter</a></li>\n\t\t\t\t\t\t<li><a href=\"pages/USDZExporter.html\" target=\"viewer\">USDZExporter</a></li>\n\t\t\t\t\t</ul>\n\t\t\t\t\t<h3>Generators</h3>\n\t\t\t\t\t<ul>\n\t\t\t\t\t\t<li><a href=\"pages/CityGenerator.html\" target=\"viewer\">CityGenerator</a></li>\n\t\t\t\t\t\t<li><a href=\"pages/FaceFrame.html\" target=\"viewer\">FaceFrame</a></li>\n\t\t\t\t\t\t<li><a href=\"pages/ForestGenerator.html\" target=\"viewer\">ForestGenerator</a></li>\n\t\t\t\t\t\t<li><a href=\"pages/SidewalkGenerator.html\" target=\"viewer\">SidewalkGenerator</a></li>\n\t\t\t\t\t\t<li><a href=\"pages/SkyscraperGenerator.html\" target=\"viewer\">SkyscraperGenerator</a></li>\n\t\t\t\t\t\t<li><a href=\"pages/TerrainGenerator.html\" target=\"viewer\">TerrainGenerator</a></li>\n\t\t\t\t\t\t<li><a href=\"pages/TreeGenerator.html\" target=\"viewer\">TreeGenerator</a></li>\n\t\t\t\t\t</ul>\n\t\t\t\t\t<h3>Geometries</h3>\n\t\t\t\t\t<ul>\n\t\t\t\t\t\t<li><a href=\"pages/BoxLineGeometry.html\" target=\"viewer\">BoxLineGeometry</a></li>\n\t\t\t\t\t\t<li><a href=\"pages/ConvexGeometry.html\" target=\"viewer\">ConvexGeometry</a></li>\n\t\t\t\t\t\t<li><a href=\"pages/DecalGeometry.html\" target=\"viewer\">DecalGeometry</a></li>\n\t\t\t\t\t\t<li><a href=\"pages/LoftGeometry.html\" target=\"viewer\">LoftGeometry</a></li>\n\t\t\t\t\t\t<li><a href=\"pages/ParametricGeometry.html\" target=\"viewer\">ParametricGeometry</a></li>\n\t\t\t\t\t\t<li><a href=\"pages/RoundedBoxGeometry.html\" target=\"viewer\">RoundedBoxGeometry</a></li>\n\t\t\t\t\t\t<li><a href=\"pages/TeapotGeometry.html\" target=\"viewer\">TeapotGeometry</a></li>\n\t\t\t\t\t\t<li><a href=\"pages/TextGeometry.html\" target=\"viewer\">TextGeometry</a></li>\n\t\t\t\t\t\t<li><a href=\"pages/module-ParametricFunctions.html\" target=\"viewer\">ParametricFunctions</a></li>\n\t\t\t\t\t</ul>\n\t\t\t\t\t<h3>Gpgpu</h3>\n\t\t\t\t\t<ul>\n\t\t\t\t\t\t<li><a href=\"pages/BitonicSort_BitonicSort.html\" target=\"viewer\">BitonicSort</a></li>\n\t\t\t\t\t</ul>\n\t\t\t\t\t<h3>Helpers</h3>\n\t\t\t\t\t<ul>\n\t\t\t\t\t\t<li><a href=\"pages/AnimationPathHelper.html\" target=\"viewer\">AnimationPathHelper</a></li>\n\t\t\t\t\t\t<li><a href=\"pages/LightProbeGridHelper.html\" target=\"viewer\">LightProbeGridHelper</a></li>\n\t\t\t\t\t\t<li><a href=\"pages/LightProbeHelper.html\" target=\"viewer\">LightProbeHelper</a></li>\n\t\t\t\t\t\t<li><a href=\"pages/OctreeHelper.html\" target=\"viewer\">OctreeHelper</a></li>\n\t\t\t\t\t\t<li><a href=\"pages/PositionalAudioHelper.html\" target=\"viewer\">PositionalAudioHelper</a></li>\n\t\t\t\t\t\t<li><a href=\"pages/RapierHelper.html\" target=\"viewer\">RapierHelper</a></li>\n\t\t\t\t\t\t<li><a href=\"pages/RectAreaLightHelper.html\" target=\"viewer\">RectAreaLightHelper</a></li>\n\t\t\t\t\t\t<li><a href=\"pages/TextureHelper.html\" target=\"viewer\">TextureHelper</a></li>\n\t\t\t\t\t\t<li><a href=\"pages/VertexNormalsHelper.html\" target=\"viewer\">VertexNormalsHelper</a></li>\n\t\t\t\t\t\t<li><a href=\"pages/VertexTangentsHelper.html\" target=\"viewer\">VertexTangentsHelper</a></li>\n\t\t\t\t\t\t<li><a href=\"pages/ViewHelper.html\" target=\"viewer\">ViewHelper</a></li>\n\t\t\t\t\t</ul>\n\t\t\t\t\t<h3>Inspector</h3>\n\t\t\t\t\t<ul>\n\t\t\t\t\t\t<li><a href=\"pages/Tab.html\" target=\"viewer\">Tab</a></li>\n\t\t\t\t\t</ul>\n\t\t\t\t\t<h3>Interaction</h3>\n\t\t\t\t\t<ul>\n\t\t\t\t\t\t<li><a href=\"pages/InteractionManager.html\" target=\"viewer\">InteractionManager</a></li>\n\t\t\t\t\t</ul>\n\t\t\t\t\t<h3>Interactive</h3>\n\t\t\t\t\t<ul>\n\t\t\t\t\t\t<li><a href=\"pages/HTMLMesh.html\" target=\"viewer\">HTMLMesh</a></li>\n\t\t\t\t\t\t<li><a href=\"pages/InteractiveGroup.html\" target=\"viewer\">InteractiveGroup</a></li>\n\t\t\t\t\t\t<li><a href=\"pages/SelectionBox.html\" target=\"viewer\">SelectionBox</a></li>\n\t\t\t\t\t\t<li><a href=\"pages/SelectionHelper.html\" target=\"viewer\">SelectionHelper</a></li>\n\t\t\t\t\t</ul>\n\t\t\t\t\t<h3>Lighting</h3>\n\t\t\t\t\t<ul>\n\t\t\t\t\t\t<li><a href=\"pages/ClusteredLighting.html\" target=\"viewer\">ClusteredLighting</a></li>\n\t\t\t\t\t\t<li><a href=\"pages/DynamicLighting.html\" target=\"viewer\">DynamicLighting</a></li>\n\t\t\t\t\t\t<li><a href=\"pages/LightProbeGrid.html\" target=\"viewer\">LightProbeGrid</a></li>\n\t\t\t\t\t</ul>\n\t\t\t\t\t<h3>Lights</h3>\n\t\t\t\t\t<ul>\n\t\t\t\t\t\t<li><a href=\"pages/LightProbeGenerator.html\" target=\"viewer\">LightProbeGenerator</a></li>\n\t\t\t\t\t\t<li><a href=\"pages/RectAreaLightTexturesLib.html\" target=\"viewer\">RectAreaLightTexturesLib</a></li>\n\t\t\t\t\t\t<li><a href=\"pages/RectAreaLightUniformsLib.html\" target=\"viewer\">RectAreaLightUniformsLib</a></li>\n\t\t\t\t\t</ul>\n\t\t\t\t\t<h3>Lines</h3>\n\t\t\t\t\t<ul>\n\t\t\t\t\t\t<li><a href=\"pages/Line2.html\" target=\"viewer\">Line2</a></li>\n\t\t\t\t\t\t<li><a href=\"pages/LineGeometry.html\" target=\"viewer\">LineGeometry</a></li>\n\t\t\t\t\t\t<li><a href=\"pages/LineMaterial.html\" target=\"viewer\">LineMaterial</a></li>\n\t\t\t\t\t\t<li><a href=\"pages/LineSegments2.html\" target=\"viewer\">LineSegments2</a></li>\n\t\t\t\t\t\t<li><a href=\"pages/LineSegmentsGeometry.html\" target=\"viewer\">LineSegmentsGeometry</a></li>\n\t\t\t\t\t\t<li><a href=\"pages/Wireframe.html\" target=\"viewer\">Wireframe</a></li>\n\t\t\t\t\t\t<li><a href=\"pages/WireframeGeometry2.html\" target=\"viewer\">WireframeGeometry2</a></li>\n\t\t\t\t\t</ul>\n\t\t\t\t\t<h3>Loaders</h3>\n\t\t\t\t\t<ul>\n\t\t\t\t\t\t<li><a href=\"pages/AMFLoader.html\" target=\"viewer\">AMFLoader</a></li>\n\t\t\t\t\t\t<li><a href=\"pages/BVHLoader.html\" target=\"viewer\">BVHLoader</a></li>\n\t\t\t\t\t\t<li><a href=\"pages/ColladaComposer.html\" target=\"viewer\">ColladaComposer</a></li>\n\t\t\t\t\t\t<li><a href=\"pages/ColladaLoader.html\" target=\"viewer\">ColladaLoader</a></li>\n\t\t\t\t\t\t<li><a href=\"pages/ColladaParser.html\" target=\"viewer\">ColladaParser</a></li>\n\t\t\t\t\t\t<li><a href=\"pages/DDSLoader.html\" target=\"viewer\">DDSLoader</a></li>\n\t\t\t\t\t\t<li><a href=\"pages/DRACOLoader.html\" target=\"viewer\">DRACOLoader</a></li>\n\t\t\t\t\t\t<li><a href=\"pages/EXRLoader.html\" target=\"viewer\">EXRLoader</a></li>\n\t\t\t\t\t\t<li><a href=\"pages/FBXLoader.html\" target=\"viewer\">FBXLoader</a></li>\n\t\t\t\t\t\t<li><a href=\"pages/Font.html\" target=\"viewer\">Font</a></li>\n\t\t\t\t\t\t<li><a href=\"pages/FontLoader.html\" target=\"viewer\">FontLoader</a></li>\n\t\t\t\t\t\t<li><a href=\"pages/GCodeLoader.html\" target=\"viewer\">GCodeLoader</a></li>\n\t\t\t\t\t\t<li><a href=\"pages/GLTFLoader.html\" target=\"viewer\">GLTFLoader</a></li>\n\t\t\t\t\t\t<li><a href=\"pages/HDRCubeTextureLoader.html\" target=\"viewer\">HDRCubeTextureLoader</a></li>\n\t\t\t\t\t\t<li><a href=\"pages/HDRLoader.html\" target=\"viewer\">HDRLoader</a></li>\n\t\t\t\t\t\t<li><a href=\"pages/IESLoader.html\" target=\"viewer\">IESLoader</a></li>\n\t\t\t\t\t\t<li><a href=\"pages/KMZLoader.html\" target=\"viewer\">KMZLoader</a></li>\n\t\t\t\t\t\t<li><a href=\"pages/KTX2Loader.html\" target=\"viewer\">KTX2Loader</a></li>\n\t\t\t\t\t\t<li><a href=\"pages/KTXLoader.html\" target=\"viewer\">KTXLoader</a></li>\n\t\t\t\t\t\t<li><a href=\"pages/LDrawLoader.html\" target=\"viewer\">LDrawLoader</a></li>\n\t\t\t\t\t\t<li><a href=\"pages/LUT3dlLoader.html\" target=\"viewer\">LUT3dlLoader</a></li>\n\t\t\t\t\t\t<li><a href=\"pages/LUTCubeLoader.html\" target=\"viewer\">LUTCubeLoader</a></li>\n\t\t\t\t\t\t<li><a href=\"pages/LUTImageLoader.html\" target=\"viewer\">LUTImageLoader</a></li>\n\t\t\t\t\t\t<li><a href=\"pages/LWOLoader.html\" target=\"viewer\">LWOLoader</a></li>\n\t\t\t\t\t\t<li><a href=\"pages/LottieLoader.html\" target=\"viewer\">LottieLoader</a></li>\n\t\t\t\t\t\t<li><a href=\"pages/MD2Loader.html\" target=\"viewer\">MD2Loader</a></li>\n\t\t\t\t\t\t<li><a href=\"pages/MDDLoader.html\" target=\"viewer\">MDDLoader</a></li>\n\t\t\t\t\t\t<li><a href=\"pages/MTLLoader.html\" target=\"viewer\">MTLLoader</a></li>\n\t\t\t\t\t\t<li><a href=\"pages/MaterialXLoader.html\" target=\"viewer\">MaterialXLoader</a></li>\n\t\t\t\t\t\t<li><a href=\"pages/NRRDLoader.html\" target=\"viewer\">NRRDLoader</a></li>\n\t\t\t\t\t\t<li><a href=\"pages/OBJLoader.html\" target=\"viewer\">OBJLoader</a></li>\n\t\t\t\t\t\t<li><a href=\"pages/PCDLoader.html\" target=\"viewer\">PCDLoader</a></li>\n\t\t\t\t\t\t<li><a href=\"pages/PDBLoader.html\" target=\"viewer\">PDBLoader</a></li>\n\t\t\t\t\t\t<li><a href=\"pages/PLYLoader.html\" target=\"viewer\">PLYLoader</a></li>\n\t\t\t\t\t\t<li><a href=\"pages/PVRLoader.html\" target=\"viewer\">PVRLoader</a></li>\n\t\t\t\t\t\t<li><a href=\"pages/Rhino3dmLoader.html\" target=\"viewer\">Rhino3dmLoader</a></li>\n\t\t\t\t\t\t<li><a href=\"pages/STLLoader.html\" target=\"viewer\">STLLoader</a></li>\n\t\t\t\t\t\t<li><a href=\"pages/SVGLoader.html\" target=\"viewer\">SVGLoader</a></li>\n\t\t\t\t\t\t<li><a href=\"pages/TDSLoader.html\" target=\"viewer\">TDSLoader</a></li>\n\t\t\t\t\t\t<li><a href=\"pages/TGALoader.html\" target=\"viewer\">TGALoader</a></li>\n\t\t\t\t\t\t<li><a href=\"pages/TIFFLoader.html\" target=\"viewer\">TIFFLoader</a></li>\n\t\t\t\t\t\t<li><a href=\"pages/TTFLoader.html\" target=\"viewer\">TTFLoader</a></li>\n\t\t\t\t\t\t<li><a href=\"pages/ThreeMFLoader.html\" target=\"viewer\">ThreeMFLoader</a></li>\n\t\t\t\t\t\t<li><a href=\"pages/USDComposer.html\" target=\"viewer\">USDComposer</a></li>\n\t\t\t\t\t\t<li><a href=\"pages/USDLoader.html\" target=\"viewer\">USDLoader</a></li>\n\t\t\t\t\t\t<li><a href=\"pages/UltraHDRLoader.html\" target=\"viewer\">UltraHDRLoader</a></li>\n\t\t\t\t\t\t<li><a href=\"pages/VOXLoader.html\" target=\"viewer\">VOXLoader</a></li>\n\t\t\t\t\t\t<li><a href=\"pages/VRMLLoader.html\" target=\"viewer\">VRMLLoader</a></li>\n\t\t\t\t\t\t<li><a href=\"pages/VTKLoader.html\" target=\"viewer\">VTKLoader</a></li>\n\t\t\t\t\t\t<li><a href=\"pages/XYZLoader.html\" target=\"viewer\">XYZLoader</a></li>\n\t\t\t\t\t</ul>\n\t\t\t\t\t<h3>Materials</h3>\n\t\t\t\t\t<ul>\n\t\t\t\t\t\t<li><a href=\"pages/LDrawConditionalLineMaterial.html\" target=\"viewer\">LDrawConditionalLineMaterial</a></li>\n\t\t\t\t\t\t<li><a href=\"pages/WoodNodeMaterial.html\" target=\"viewer\">WoodNodeMaterial</a></li>\n\t\t\t\t\t</ul>\n\t\t\t\t\t<h3>Math</h3>\n\t\t\t\t\t<ul>\n\t\t\t\t\t\t<li><a href=\"pages/Capsule.html\" target=\"viewer\">Capsule</a></li>\n\t\t\t\t\t\t<li><a href=\"pages/ColorConverter.html\" target=\"viewer\">ColorConverter</a></li>\n\t\t\t\t\t\t<li><a href=\"pages/ConvexHull.html\" target=\"viewer\">ConvexHull</a></li>\n\t\t\t\t\t\t<li><a href=\"pages/ImprovedNoise.html\" target=\"viewer\">ImprovedNoise</a></li>\n\t\t\t\t\t\t<li><a href=\"pages/Lut.html\" target=\"viewer\">Lut</a></li>\n\t\t\t\t\t\t<li><a href=\"pages/MeshSurfaceSampler.html\" target=\"viewer\">MeshSurfaceSampler</a></li>\n\t\t\t\t\t\t<li><a href=\"pages/OBB.html\" target=\"viewer\">OBB</a></li>\n\t\t\t\t\t\t<li><a href=\"pages/Octree.html\" target=\"viewer\">Octree</a></li>\n\t\t\t\t\t\t<li><a href=\"pages/SimplexNoise.html\" target=\"viewer\">SimplexNoise</a></li>\n\t\t\t\t\t\t<li><a href=\"pages/module-ColorSpaces.html\" target=\"viewer\">ColorSpaces</a></li>\n\t\t\t\t\t</ul>\n\t\t\t\t\t<h3>Misc</h3>\n\t\t\t\t\t<ul>\n\t\t\t\t\t\t<li><a href=\"pages/ConvexObjectBreaker.html\" target=\"viewer\">ConvexObjectBreaker</a></li>\n\t\t\t\t\t\t<li><a href=\"pages/GPUComputationRenderer.html\" target=\"viewer\">GPUComputationRenderer</a></li>\n\t\t\t\t\t\t<li><a href=\"pages/Gyroscope.html\" target=\"viewer\">Gyroscope</a></li>\n\t\t\t\t\t\t<li><a href=\"pages/MD2Character.html\" target=\"viewer\">MD2Character</a></li>\n\t\t\t\t\t\t<li><a href=\"pages/MD2CharacterComplex.html\" target=\"viewer\">MD2CharacterComplex</a></li>\n\t\t\t\t\t\t<li><a href=\"pages/MorphAnimMesh.html\" target=\"viewer\">MorphAnimMesh</a></li>\n\t\t\t\t\t\t<li><a href=\"pages/MorphBlendMesh.html\" target=\"viewer\">MorphBlendMesh</a></li>\n\t\t\t\t\t\t<li><a href=\"pages/ProgressiveLightMap.html\" target=\"viewer\">ProgressiveLightMap</a></li>\n\t\t\t\t\t\t<li><a href=\"pages/RollerCoasterGeometry.html\" target=\"viewer\">RollerCoasterGeometry</a></li>\n\t\t\t\t\t\t<li><a href=\"pages/RollerCoasterLiftersGeometry.html\" target=\"viewer\">RollerCoasterLiftersGeometry</a></li>\n\t\t\t\t\t\t<li><a href=\"pages/RollerCoasterShadowGeometry.html\" target=\"viewer\">RollerCoasterShadowGeometry</a></li>\n\t\t\t\t\t\t<li><a href=\"pages/SkyGeometry.html\" target=\"viewer\">SkyGeometry</a></li>\n\t\t\t\t\t\t<li><a href=\"pages/TileCreasedNormalsPlugin.html\" target=\"viewer\">TileCreasedNormalsPlugin</a></li>\n\t\t\t\t\t\t<li><a href=\"pages/TreesGeometry.html\" target=\"viewer\">TreesGeometry</a></li>\n\t\t\t\t\t\t<li><a href=\"pages/TubePainter.html\" target=\"viewer\">TubePainter</a></li>\n\t\t\t\t\t\t<li><a href=\"pages/Volume.html\" target=\"viewer\">Volume</a></li>\n\t\t\t\t\t\t<li><a href=\"pages/VolumeSlice.html\" target=\"viewer\">VolumeSlice</a></li>\n\t\t\t\t\t</ul>\n\t\t\t\t\t<h3>Modifiers</h3>\n\t\t\t\t\t<ul>\n\t\t\t\t\t\t<li><a href=\"pages/EdgeSplitModifier.html\" target=\"viewer\">EdgeSplitModifier</a></li>\n\t\t\t\t\t\t<li><a href=\"pages/Flow.html\" target=\"viewer\">Flow</a></li>\n\t\t\t\t\t\t<li><a href=\"pages/InstancedFlow.html\" target=\"viewer\">InstancedFlow</a></li>\n\t\t\t\t\t\t<li><a href=\"pages/SimplifyModifier.html\" target=\"viewer\">SimplifyModifier</a></li>\n\t\t\t\t\t\t<li><a href=\"pages/TessellateModifier.html\" target=\"viewer\">TessellateModifier</a></li>\n\t\t\t\t\t</ul>\n\t\t\t\t\t<h3>Objects</h3>\n\t\t\t\t\t<ul>\n\t\t\t\t\t\t<li><a href=\"pages/GroundedSkybox.html\" target=\"viewer\">GroundedSkybox</a></li>\n\t\t\t\t\t\t<li><a href=\"pages/Lensflare.html\" target=\"viewer\">Lensflare</a></li>\n\t\t\t\t\t\t<li><a href=\"pages/LensflareElement.html\" target=\"viewer\">LensflareElement</a></li>\n\t\t\t\t\t\t<li><a href=\"pages/LensflareMesh.html\" target=\"viewer\">LensflareMesh</a></li>\n\t\t\t\t\t\t<li><a href=\"pages/MarchingCubes.html\" target=\"viewer\">MarchingCubes</a></li>\n\t\t\t\t\t\t<li><a href=\"pages/Reflector.html\" target=\"viewer\">Reflector</a></li>\n\t\t\t\t\t\t<li><a href=\"pages/ReflectorForSSRPass.html\" target=\"viewer\">ReflectorForSSRPass</a></li>\n\t\t\t\t\t\t<li><a href=\"pages/Refractor.html\" target=\"viewer\">Refractor</a></li>\n\t\t\t\t\t\t<li><a href=\"pages/ShadowMesh.html\" target=\"viewer\">ShadowMesh</a></li>\n\t\t\t\t\t\t<li><a href=\"pages/Sky.html\" target=\"viewer\">Sky</a></li>\n\t\t\t\t\t\t<li><a href=\"pages/SkyMesh.html\" target=\"viewer\">SkyMesh</a></li>\n\t\t\t\t\t\t<li><a href=\"pages/Water.html\" target=\"viewer\">Water</a></li>\n\t\t\t\t\t\t<li><a href=\"pages/WaterMesh.html\" target=\"viewer\">WaterMesh</a></li>\n\t\t\t\t\t</ul>\n\t\t\t\t\t<h3>Physics</h3>\n\t\t\t\t\t<ul>\n\t\t\t\t\t\t<li><a href=\"pages/AmmoPhysics.html\" target=\"viewer\">AmmoPhysics</a></li>\n\t\t\t\t\t\t<li><a href=\"pages/JoltPhysics.html\" target=\"viewer\">JoltPhysics</a></li>\n\t\t\t\t\t\t<li><a href=\"pages/RapierPhysics.html\" target=\"viewer\">RapierPhysics</a></li>\n\t\t\t\t\t</ul>\n\t\t\t\t\t<h3>Postprocessing</h3>\n\t\t\t\t\t<ul>\n\t\t\t\t\t\t<li><a href=\"pages/AfterimagePass.html\" target=\"viewer\">AfterimagePass</a></li>\n\t\t\t\t\t\t<li><a href=\"pages/BloomPass.html\" target=\"viewer\">BloomPass</a></li>\n\t\t\t\t\t\t<li><a href=\"pages/BokehPass.html\" target=\"viewer\">BokehPass</a></li>\n\t\t\t\t\t\t<li><a href=\"pages/ClearMaskPass.html\" target=\"viewer\">ClearMaskPass</a></li>\n\t\t\t\t\t\t<li><a href=\"pages/ClearPass.html\" target=\"viewer\">ClearPass</a></li>\n\t\t\t\t\t\t<li><a href=\"pages/CubeTexturePass.html\" target=\"viewer\">CubeTexturePass</a></li>\n\t\t\t\t\t\t<li><a href=\"pages/DotScreenPass.html\" target=\"viewer\">DotScreenPass</a></li>\n\t\t\t\t\t\t<li><a href=\"pages/EffectComposer.html\" target=\"viewer\">EffectComposer</a></li>\n\t\t\t\t\t\t<li><a href=\"pages/FXAAPass.html\" target=\"viewer\">FXAAPass</a></li>\n\t\t\t\t\t\t<li><a href=\"pages/FilmPass.html\" target=\"viewer\">FilmPass</a></li>\n\t\t\t\t\t\t<li><a href=\"pages/FullScreenQuad.html\" target=\"viewer\">FullScreenQuad</a></li>\n\t\t\t\t\t\t<li><a href=\"pages/GTAOPass.html\" target=\"viewer\">GTAOPass</a></li>\n\t\t\t\t\t\t<li><a href=\"pages/GlitchPass.html\" target=\"viewer\">GlitchPass</a></li>\n\t\t\t\t\t\t<li><a href=\"pages/HalftonePass.html\" target=\"viewer\">HalftonePass</a></li>\n\t\t\t\t\t\t<li><a href=\"pages/LUTPass.html\" target=\"viewer\">LUTPass</a></li>\n\t\t\t\t\t\t<li><a href=\"pages/MaskPass.html\" target=\"viewer\">MaskPass</a></li>\n\t\t\t\t\t\t<li><a href=\"pages/OutlinePass.html\" target=\"viewer\">OutlinePass</a></li>\n\t\t\t\t\t\t<li><a href=\"pages/OutputPass.html\" target=\"viewer\">OutputPass</a></li>\n\t\t\t\t\t\t<li><a href=\"pages/Pass.html\" target=\"viewer\">Pass</a></li>\n\t\t\t\t\t\t<li><a href=\"pages/RenderPass.html\" target=\"viewer\">RenderPass</a></li>\n\t\t\t\t\t\t<li><a href=\"pages/RenderPixelatedPass.html\" target=\"viewer\">RenderPixelatedPass</a></li>\n\t\t\t\t\t\t<li><a href=\"pages/RenderTransitionPass.html\" target=\"viewer\">RenderTransitionPass</a></li>\n\t\t\t\t\t\t<li><a href=\"pages/SAOPass.html\" target=\"viewer\">SAOPass</a></li>\n\t\t\t\t\t\t<li><a href=\"pages/SMAAPass.html\" target=\"viewer\">SMAAPass</a></li>\n\t\t\t\t\t\t<li><a href=\"pages/SSAARenderPass.html\" target=\"viewer\">SSAARenderPass</a></li>\n\t\t\t\t\t\t<li><a href=\"pages/SSAOPass.html\" target=\"viewer\">SSAOPass</a></li>\n\t\t\t\t\t\t<li><a href=\"pages/SSRPass.html\" target=\"viewer\">SSRPass</a></li>\n\t\t\t\t\t\t<li><a href=\"pages/SavePass.html\" target=\"viewer\">SavePass</a></li>\n\t\t\t\t\t\t<li><a href=\"pages/ShaderPass.html\" target=\"viewer\">ShaderPass</a></li>\n\t\t\t\t\t\t<li><a href=\"pages/TAARenderPass.html\" target=\"viewer\">TAARenderPass</a></li>\n\t\t\t\t\t\t<li><a href=\"pages/TexturePass.html\" target=\"viewer\">TexturePass</a></li>\n\t\t\t\t\t\t<li><a href=\"pages/UnrealBloomPass.html\" target=\"viewer\">UnrealBloomPass</a></li>\n\t\t\t\t\t</ul>\n\t\t\t\t\t<h3>Renderers</h3>\n\t\t\t\t\t<ul>\n\t\t\t\t\t\t<li><a href=\"pages/CSS2DObject.html\" target=\"viewer\">CSS2DObject</a></li>\n\t\t\t\t\t\t<li><a href=\"pages/CSS2DRenderer.html\" target=\"viewer\">CSS2DRenderer</a></li>\n\t\t\t\t\t\t<li><a href=\"pages/CSS3DObject.html\" target=\"viewer\">CSS3DObject</a></li>\n\t\t\t\t\t\t<li><a href=\"pages/CSS3DRenderer.html\" target=\"viewer\">CSS3DRenderer</a></li>\n\t\t\t\t\t\t<li><a href=\"pages/CSS3DSprite.html\" target=\"viewer\">CSS3DSprite</a></li>\n\t\t\t\t\t\t<li><a href=\"pages/Projector.html\" target=\"viewer\">Projector</a></li>\n\t\t\t\t\t\t<li><a href=\"pages/SVGObject.html\" target=\"viewer\">SVGObject</a></li>\n\t\t\t\t\t\t<li><a href=\"pages/SVGRenderer.html\" target=\"viewer\">SVGRenderer</a></li>\n\t\t\t\t\t</ul>\n\t\t\t\t\t<h3>Shaders</h3>\n\t\t\t\t\t<ul>\n\t\t\t\t\t\t<li><a href=\"pages/module-ACESFilmicToneMappingShader.html\" target=\"viewer\">ACESFilmicToneMappingShader</a></li>\n\t\t\t\t\t\t<li><a href=\"pages/module-AfterimageShader.html\" target=\"viewer\">AfterimageShader</a></li>\n\t\t\t\t\t\t<li><a href=\"pages/module-BasicShader.html\" target=\"viewer\">BasicShader</a></li>\n\t\t\t\t\t\t<li><a href=\"pages/module-BleachBypassShader.html\" target=\"viewer\">BleachBypassShader</a></li>\n\t\t\t\t\t\t<li><a href=\"pages/module-BlendShader.html\" target=\"viewer\">BlendShader</a></li>\n\t\t\t\t\t\t<li><a href=\"pages/module-BokehShader.html\" target=\"viewer\">BokehShader</a></li>\n\t\t\t\t\t\t<li><a href=\"pages/module-BokehShader2.html\" target=\"viewer\">BokehShader2</a></li>\n\t\t\t\t\t\t<li><a href=\"pages/module-BrightnessContrastShader.html\" target=\"viewer\">BrightnessContrastShader</a></li>\n\t\t\t\t\t\t<li><a href=\"pages/module-ColorCorrectionShader.html\" target=\"viewer\">ColorCorrectionShader</a></li>\n\t\t\t\t\t\t<li><a href=\"pages/module-ColorifyShader.html\" target=\"viewer\">ColorifyShader</a></li>\n\t\t\t\t\t\t<li><a href=\"pages/module-ConvolutionShader.html\" target=\"viewer\">ConvolutionShader</a></li>\n\t\t\t\t\t\t<li><a href=\"pages/module-CopyShader.html\" target=\"viewer\">CopyShader</a></li>\n\t\t\t\t\t\t<li><a href=\"pages/module-DOFMipMapShader.html\" target=\"viewer\">DOFMipMapShader</a></li>\n\t\t\t\t\t\t<li><a href=\"pages/module-DepthLimitedBlurShader.html\" target=\"viewer\">DepthLimitedBlurShader</a></li>\n\t\t\t\t\t\t<li><a href=\"pages/module-DigitalGlitch.html\" target=\"viewer\">DigitalGlitch</a></li>\n\t\t\t\t\t\t<li><a href=\"pages/module-DotScreenShader.html\" target=\"viewer\">DotScreenShader</a></li>\n\t\t\t\t\t\t<li><a href=\"pages/module-ExposureShader.html\" target=\"viewer\">ExposureShader</a></li>\n\t\t\t\t\t\t<li><a href=\"pages/module-FXAAShader.html\" target=\"viewer\">FXAAShader</a></li>\n\t\t\t\t\t\t<li><a href=\"pages/module-FilmShader.html\" target=\"viewer\">FilmShader</a></li>\n\t\t\t\t\t\t<li><a href=\"pages/module-FocusShader.html\" target=\"viewer\">FocusShader</a></li>\n\t\t\t\t\t\t<li><a href=\"pages/module-FreiChenShader.html\" target=\"viewer\">FreiChenShader</a></li>\n\t\t\t\t\t\t<li><a href=\"pages/module-GTAOShader.html\" target=\"viewer\">GTAOShader</a></li>\n\t\t\t\t\t\t<li><a href=\"pages/module-GammaCorrectionShader.html\" target=\"viewer\">GammaCorrectionShader</a></li>\n\t\t\t\t\t\t<li><a href=\"pages/module-HalftoneShader.html\" target=\"viewer\">HalftoneShader</a></li>\n\t\t\t\t\t\t<li><a href=\"pages/module-HorizontalBlurShader.html\" target=\"viewer\">HorizontalBlurShader</a></li>\n\t\t\t\t\t\t<li><a href=\"pages/module-HorizontalTiltShiftShader.html\" target=\"viewer\">HorizontalTiltShiftShader</a></li>\n\t\t\t\t\t\t<li><a href=\"pages/module-HueSaturationShader.html\" target=\"viewer\">HueSaturationShader</a></li>\n\t\t\t\t\t\t<li><a href=\"pages/module-KaleidoShader.html\" target=\"viewer\">KaleidoShader</a></li>\n\t\t\t\t\t\t<li><a href=\"pages/module-LuminosityHighPassShader.html\" target=\"viewer\">LuminosityHighPassShader</a></li>\n\t\t\t\t\t\t<li><a href=\"pages/module-LuminosityShader.html\" target=\"viewer\">LuminosityShader</a></li>\n\t\t\t\t\t\t<li><a href=\"pages/module-MirrorShader.html\" target=\"viewer\">MirrorShader</a></li>\n\t\t\t\t\t\t<li><a href=\"pages/module-NormalMapShader.html\" target=\"viewer\">NormalMapShader</a></li>\n\t\t\t\t\t\t<li><a href=\"pages/module-OutputShader.html\" target=\"viewer\">OutputShader</a></li>\n\t\t\t\t\t\t<li><a href=\"pages/module-PoissonDenoiseShader.html\" target=\"viewer\">PoissonDenoiseShader</a></li>\n\t\t\t\t\t\t<li><a href=\"pages/module-RGBShiftShader.html\" target=\"viewer\">RGBShiftShader</a></li>\n\t\t\t\t\t\t<li><a href=\"pages/module-SAOShader.html\" target=\"viewer\">SAOShader</a></li>\n\t\t\t\t\t\t<li><a href=\"pages/module-SMAAShader.html\" target=\"viewer\">SMAAShader</a></li>\n\t\t\t\t\t\t<li><a href=\"pages/module-SSAOShader.html\" target=\"viewer\">SSAOShader</a></li>\n\t\t\t\t\t\t<li><a href=\"pages/module-SSRShader.html\" target=\"viewer\">SSRShader</a></li>\n\t\t\t\t\t\t<li><a href=\"pages/module-SepiaShader.html\" target=\"viewer\">SepiaShader</a></li>\n\t\t\t\t\t\t<li><a href=\"pages/module-SobelOperatorShader.html\" target=\"viewer\">SobelOperatorShader</a></li>\n\t\t\t\t\t\t<li><a href=\"pages/module-SubsurfaceScatteringShader.html\" target=\"viewer\">SubsurfaceScatteringShader</a></li>\n\t\t\t\t\t\t<li><a href=\"pages/module-TriangleBlurShader.html\" target=\"viewer\">TriangleBlurShader</a></li>\n\t\t\t\t\t\t<li><a href=\"pages/module-UnpackDepthRGBAShader.html\" target=\"viewer\">UnpackDepthRGBAShader</a></li>\n\t\t\t\t\t\t<li><a href=\"pages/module-VelocityShader.html\" target=\"viewer\">VelocityShader</a></li>\n\t\t\t\t\t\t<li><a href=\"pages/module-VerticalBlurShader.html\" target=\"viewer\">VerticalBlurShader</a></li>\n\t\t\t\t\t\t<li><a href=\"pages/module-VerticalTiltShiftShader.html\" target=\"viewer\">VerticalTiltShiftShader</a></li>\n\t\t\t\t\t\t<li><a href=\"pages/module-VignetteShader.html\" target=\"viewer\">VignetteShader</a></li>\n\t\t\t\t\t\t<li><a href=\"pages/module-VolumeShader.html\" target=\"viewer\">VolumeShader</a></li>\n\t\t\t\t\t\t<li><a href=\"pages/module-WaterRefractionShader.html\" target=\"viewer\">WaterRefractionShader</a></li>\n\t\t\t\t\t</ul>\n\t\t\t\t\t<h3>TSL</h3>\n\t\t\t\t\t<ul>\n\t\t\t\t\t\t<li><a href=\"pages/AfterImageNode.html\" target=\"viewer\">AfterImageNode</a></li>\n\t\t\t\t\t\t<li><a href=\"pages/AmbientLightDataNode.html\" target=\"viewer\">AmbientLightDataNode</a></li>\n\t\t\t\t\t\t<li><a href=\"pages/AnaglyphPassNode.html\" target=\"viewer\">AnaglyphPassNode</a></li>\n\t\t\t\t\t\t<li><a href=\"pages/BilateralBlurNode.html\" target=\"viewer\">BilateralBlurNode</a></li>\n\t\t\t\t\t\t<li><a href=\"pages/BloomNode.html\" target=\"viewer\">BloomNode</a></li>\n\t\t\t\t\t\t<li><a href=\"pages/ChromaticAberrationNode.html\" target=\"viewer\">ChromaticAberrationNode</a></li>\n\t\t\t\t\t\t<li><a href=\"pages/ClusteredLightsNode.html\" target=\"viewer\">ClusteredLightsNode</a></li>\n\t\t\t\t\t\t<li><a href=\"pages/DenoiseNode.html\" target=\"viewer\">DenoiseNode</a></li>\n\t\t\t\t\t\t<li><a href=\"pages/DepthOfFieldNode.html\" target=\"viewer\">DepthOfFieldNode</a></li>\n\t\t\t\t\t\t<li><a href=\"pages/DirectionalLightDataNode.html\" target=\"viewer\">DirectionalLightDataNode</a></li>\n\t\t\t\t\t\t<li><a href=\"pages/DotScreenNode.html\" target=\"viewer\">DotScreenNode</a></li>\n\t\t\t\t\t\t<li><a href=\"pages/DynamicLightsNode.html\" target=\"viewer\">DynamicLightsNode</a></li>\n\t\t\t\t\t\t<li><a href=\"pages/EnvMapCDFGenerator.html\" target=\"viewer\">EnvMapCDFGenerator</a></li>\n\t\t\t\t\t\t<li><a href=\"pages/FSR1Node.html\" target=\"viewer\">FSR1Node</a></li>\n\t\t\t\t\t\t<li><a href=\"pages/FXAANode.html\" target=\"viewer\">FXAANode</a></li>\n\t\t\t\t\t\t<li><a href=\"pages/FilmNode.html\" target=\"viewer\">FilmNode</a></li>\n\t\t\t\t\t\t<li><a href=\"pages/GTAONode.html\" target=\"viewer\">GTAONode</a></li>\n\t\t\t\t\t\t<li><a href=\"pages/GaussianBlurNode.html\" target=\"viewer\">GaussianBlurNode</a></li>\n\t\t\t\t\t\t<li><a href=\"pages/GodraysNode.html\" target=\"viewer\">GodraysNode</a></li>\n\t\t\t\t\t\t<li><a href=\"pages/HemisphereLightDataNode.html\" target=\"viewer\">HemisphereLightDataNode</a></li>\n\t\t\t\t\t\t<li><a href=\"pages/ImportanceSampledEnvironment.html\" target=\"viewer\">ImportanceSampledEnvironment</a></li>\n\t\t\t\t\t\t<li><a href=\"pages/LensflareNode.html\" target=\"viewer\">LensflareNode</a></li>\n\t\t\t\t\t\t<li><a href=\"pages/Lut3DNode.html\" target=\"viewer\">Lut3DNode</a></li>\n\t\t\t\t\t\t<li><a href=\"pages/OutlineNode.html\" target=\"viewer\">OutlineNode</a></li>\n\t\t\t\t\t\t<li><a href=\"pages/ParallaxBarrierPassNode.html\" target=\"viewer\">ParallaxBarrierPassNode</a></li>\n\t\t\t\t\t\t<li><a href=\"pages/PixelationNode.html\" target=\"viewer\">PixelationNode</a></li>\n\t\t\t\t\t\t<li><a href=\"pages/PixelationPassNode.html\" target=\"viewer\">PixelationPassNode</a></li>\n\t\t\t\t\t\t<li><a href=\"pages/PointLightDataNode.html\" target=\"viewer\">PointLightDataNode</a></li>\n\t\t\t\t\t\t<li><a href=\"pages/RGBShiftNode.html\" target=\"viewer\">RGBShiftNode</a></li>\n\t\t\t\t\t\t<li><a href=\"pages/RecurrentDenoiseNode.html\" target=\"viewer\">RecurrentDenoiseNode</a></li>\n\t\t\t\t\t\t<li><a href=\"pages/RetroPassNode.html\" target=\"viewer\">RetroPassNode</a></li>\n\t\t\t\t\t\t<li><a href=\"pages/SMAANode.html\" target=\"viewer\">SMAANode</a></li>\n\t\t\t\t\t\t<li><a href=\"pages/SSAAPassNode.html\" target=\"viewer\">SSAAPassNode</a></li>\n\t\t\t\t\t\t<li><a href=\"pages/SSGINode.html\" target=\"viewer\">SSGINode</a></li>\n\t\t\t\t\t\t<li><a href=\"pages/SSRNode.html\" target=\"viewer\">SSRNode</a></li>\n\t\t\t\t\t\t<li><a href=\"pages/SSSNode.html\" target=\"viewer\">SSSNode</a></li>\n\t\t\t\t\t\t<li><a href=\"pages/SharpenNode.html\" target=\"viewer\">SharpenNode</a></li>\n\t\t\t\t\t\t<li><a href=\"pages/SobelOperatorNode.html\" target=\"viewer\">SobelOperatorNode</a></li>\n\t\t\t\t\t\t<li><a href=\"pages/SpotLightDataNode.html\" target=\"viewer\">SpotLightDataNode</a></li>\n\t\t\t\t\t\t<li><a href=\"pages/StereoCompositePassNode.html\" target=\"viewer\">StereoCompositePassNode</a></li>\n\t\t\t\t\t\t<li><a href=\"pages/StereoPassNode.html\" target=\"viewer\">StereoPassNode</a></li>\n\t\t\t\t\t\t<li><a href=\"pages/TAAUNode.html\" target=\"viewer\">TAAUNode</a></li>\n\t\t\t\t\t\t<li><a href=\"pages/TRAANode.html\" target=\"viewer\">TRAANode</a></li>\n\t\t\t\t\t\t<li><a href=\"pages/TemporalReprojectNode.html\" target=\"viewer\">TemporalReprojectNode</a></li>\n\t\t\t\t\t\t<li><a href=\"pages/TileShadowNode.html\" target=\"viewer\">TileShadowNode</a></li>\n\t\t\t\t\t\t<li><a href=\"pages/TileShadowNodeHelper.html\" target=\"viewer\">TileShadowNodeHelper</a></li>\n\t\t\t\t\t\t<li><a href=\"pages/TransitionNode.html\" target=\"viewer\">TransitionNode</a></li>\n\t\t\t\t\t\t<li><a href=\"pages/WebGLNodesHandler.html\" target=\"viewer\">WebGLNodesHandler</a></li>\n\t\t\t\t\t\t<li><a href=\"pages/module-Bayer.html\" target=\"viewer\">Bayer</a></li>\n\t\t\t\t\t\t<li><a href=\"pages/module-GroundedSkybox.html\" target=\"viewer\">GroundedSkybox</a></li>\n\t\t\t\t\t\t<li><a href=\"pages/module-Raymarching.html\" target=\"viewer\">Raymarching</a></li>\n\t\t\t\t\t</ul>\n\t\t\t\t\t<h3>Textures</h3>\n\t\t\t\t\t<ul>\n\t\t\t\t\t\t<li><a href=\"pages/FlakesTexture.html\" target=\"viewer\">FlakesTexture</a></li>\n\t\t\t\t\t</ul>\n\t\t\t\t\t<h3>Transpiler</h3>\n\t\t\t\t\t<ul>\n\t\t\t\t\t\t<li><a href=\"pages/Transpiler.html\" target=\"viewer\">Transpiler</a></li>\n\t\t\t\t\t</ul>\n\t\t\t\t\t<h3>Utils</h3>\n\t\t\t\t\t<ul>\n\t\t\t\t\t\t<li><a href=\"pages/LDrawUtils.html\" target=\"viewer\">LDrawUtils</a></li>\n\t\t\t\t\t\t<li><a href=\"pages/SceneOptimizer.html\" target=\"viewer\">SceneOptimizer</a></li>\n\t\t\t\t\t\t<li><a href=\"pages/ShadowMapViewer.html\" target=\"viewer\">ShadowMapViewer</a></li>\n\t\t\t\t\t\t<li><a href=\"pages/WorkerPool.html\" target=\"viewer\">WorkerPool</a></li>\n\t\t\t\t\t\t<li><a href=\"pages/module-BufferGeometryUtils.html\" target=\"viewer\">BufferGeometryUtils</a></li>\n\t\t\t\t\t\t<li><a href=\"pages/module-CameraUtils.html\" target=\"viewer\">CameraUtils</a></li>\n\t\t\t\t\t\t<li><a href=\"pages/module-ColorUtils.html\" target=\"viewer\">ColorUtils</a></li>\n\t\t\t\t\t\t<li><a href=\"pages/module-GeometryCompressionUtils.html\" target=\"viewer\">GeometryCompressionUtils</a></li>\n\t\t\t\t\t\t<li><a href=\"pages/module-GeometryUtils.html\" target=\"viewer\">GeometryUtils</a></li>\n\t\t\t\t\t\t<li><a href=\"pages/module-SceneUtils.html\" target=\"viewer\">SceneUtils</a></li>\n\t\t\t\t\t\t<li><a href=\"pages/module-SkeletonUtils.html\" target=\"viewer\">SkeletonUtils</a></li>\n\t\t\t\t\t\t<li><a href=\"pages/module-SortUtils.html\" target=\"viewer\">SortUtils</a></li>\n\t\t\t\t\t\t<li><a href=\"pages/module-UVsDebug.html\" target=\"viewer\">UVsDebug</a></li>\n\t\t\t\t\t\t<li><a href=\"pages/module-WebGLTextureUtils.html\" target=\"viewer\">WebGLTextureUtils</a></li>\n\t\t\t\t\t\t<li><a href=\"pages/module-WebGPUTextureUtils.html\" target=\"viewer\">WebGPUTextureUtils</a></li>\n\t\t\t\t\t</ul>\n\t\t\t\t\t<h3>Webxr</h3>\n\t\t\t\t\t<ul>\n\t\t\t\t\t\t<li><a href=\"pages/ARButton.html\" target=\"viewer\">ARButton</a></li>\n\t\t\t\t\t\t<li><a href=\"pages/OculusHandModel.html\" target=\"viewer\">OculusHandModel</a></li>\n\t\t\t\t\t\t<li><a href=\"pages/OculusHandPointerModel.html\" target=\"viewer\">OculusHandPointerModel</a></li>\n\t\t\t\t\t\t<li><a href=\"pages/VRButton.html\" target=\"viewer\">VRButton</a></li>\n\t\t\t\t\t\t<li><a href=\"pages/XRButton.html\" target=\"viewer\">XRButton</a></li>\n\t\t\t\t\t\t<li><a href=\"pages/XRControllerModel.html\" target=\"viewer\">XRControllerModel</a></li>\n\t\t\t\t\t\t<li><a href=\"pages/XRControllerModelFactory.html\" target=\"viewer\">XRControllerModelFactory</a></li>\n\t\t\t\t\t\t<li><a href=\"pages/XREstimatedLight.html\" target=\"viewer\">XREstimatedLight</a></li>\n\t\t\t\t\t\t<li><a href=\"pages/XRHandMeshModel.html\" target=\"viewer\">XRHandMeshModel</a></li>\n\t\t\t\t\t\t<li><a href=\"pages/XRHandModel.html\" target=\"viewer\">XRHandModel</a></li>\n\t\t\t\t\t\t<li><a href=\"pages/XRHandModelFactory.html\" target=\"viewer\">XRHandModelFactory</a></li>\n\t\t\t\t\t\t<li><a href=\"pages/XRHandPrimitiveModel.html\" target=\"viewer\">XRHandPrimitiveModel</a></li>\n\t\t\t\t\t\t<li><a href=\"pages/XRPlanes.html\" target=\"viewer\">XRPlanes</a></li>\n\t\t\t\t\t\t<li><a href=\"pages/module-Text2D.html\" target=\"viewer\">Text2D</a></li>\n\t\t\t\t\t</ul>\n\t\t\t\t\t<h2>TSL</h2>\n\t\t\t\t\t<ul>\n\t\t\t\t\t\t<li><a href=\"pages/TSL.html#Break\" target=\"viewer\">Break</a></li>\n\t\t\t\t\t\t<li><a href=\"pages/TSL.html#Const\" target=\"viewer\">Const</a></li>\n\t\t\t\t\t\t<li><a href=\"pages/TSL.html#Continue\" target=\"viewer\">Continue</a></li>\n\t\t\t\t\t\t<li><a href=\"pages/TSL.html#Discard\" target=\"viewer\">Discard</a></li>\n\t\t\t\t\t\t<li><a href=\"pages/TSL.html#EPSILON\" target=\"viewer\">EPSILON</a></li>\n\t\t\t\t\t\t<li><a href=\"pages/TSL.html#HALF_PI\" target=\"viewer\">HALF_PI</a></li>\n\t\t\t\t\t\t<li><a href=\"pages/TSL.html#INFINITY\" target=\"viewer\">INFINITY</a></li>\n\t\t\t\t\t\t<li><a href=\"pages/TSL.html#If\" target=\"viewer\">If</a></li>\n\t\t\t\t\t\t<li><a href=\"pages/TSL.html#Loop\" target=\"viewer\">Loop</a></li>\n\t\t\t\t\t\t<li><a href=\"pages/TSL.html#PI\" target=\"viewer\">PI</a></li>\n\t\t\t\t\t\t<li><a href=\"pages/TSL.html#PI2\" target=\"viewer\">PI2</a></li>\n\t\t\t\t\t\t<li><a href=\"pages/TSL.html#Return\" target=\"viewer\">Return</a></li>\n\t\t\t\t\t\t<li><a href=\"pages/TSL.html#Switch\" target=\"viewer\">Switch</a></li>\n\t\t\t\t\t\t<li><a href=\"pages/TSL.html#TBNViewMatrix\" target=\"viewer\">TBNViewMatrix</a></li>\n\t\t\t\t\t\t<li><a href=\"pages/TSL.html#TWO_PI\" target=\"viewer\">TWO_PI</a></li>\n\t\t\t\t\t\t<li><a href=\"pages/TSL.html#Var\" target=\"viewer\">Var</a></li>\n\t\t\t\t\t\t<li><a href=\"pages/TSL.html#VarIntent\" target=\"viewer\">VarIntent</a></li>\n\t\t\t\t\t\t<li><a href=\"pages/TSL.html#abs\" target=\"viewer\">abs</a></li>\n\t\t\t\t\t\t<li><a href=\"pages/TSL.html#acesFilmicToneMapping\" target=\"viewer\">acesFilmicToneMapping</a></li>\n\t\t\t\t\t\t<li><a href=\"pages/TSL.html#acos\" target=\"viewer\">acos</a></li>\n\t\t\t\t\t\t<li><a href=\"pages/TSL.html#acosh\" target=\"viewer\">acosh</a></li>\n\t\t\t\t\t\t<li><a href=\"pages/TSL.html#add\" target=\"viewer\">add</a></li>\n\t\t\t\t\t\t<li><a href=\"pages/TSL.html#afterImage\" target=\"viewer\">afterImage</a></li>\n\t\t\t\t\t\t<li><a href=\"pages/TSL.html#agxToneMapping\" target=\"viewer\">agxToneMapping</a></li>\n\t\t\t\t\t\t<li><a href=\"pages/TSL.html#all\" target=\"viewer\">all</a></li>\n\t\t\t\t\t\t<li><a href=\"pages/TSL.html#alphaLine\" target=\"viewer\">alphaLine</a></li>\n\t\t\t\t\t\t<li><a href=\"pages/TSL.html#alphaT\" target=\"viewer\">alphaT</a></li>\n\t\t\t\t\t\t<li><a href=\"pages/TSL.html#ambientOcclusion\" target=\"viewer\">ambientOcclusion</a></li>\n\t\t\t\t\t\t<li><a href=\"pages/TSL.html#anaglyphPass\" target=\"viewer\">anaglyphPass</a></li>\n\t\t\t\t\t\t<li><a href=\"pages/TSL.html#and\" target=\"viewer\">and</a></li>\n\t\t\t\t\t\t<li><a href=\"pages/TSL.html#anisotropy\" target=\"viewer\">anisotropy</a></li>\n\t\t\t\t\t\t<li><a href=\"pages/TSL.html#anisotropyB\" target=\"viewer\">anisotropyB</a></li>\n\t\t\t\t\t\t<li><a href=\"pages/TSL.html#anisotropyT\" target=\"viewer\">anisotropyT</a></li>\n\t\t\t\t\t\t<li><a href=\"pages/TSL.html#any\" target=\"viewer\">any</a></li>\n\t\t\t\t\t\t<li><a href=\"pages/TSL.html#ao\" target=\"viewer\">ao</a></li>\n\t\t\t\t\t\t<li><a href=\"pages/TSL.html#append\" target=\"viewer\">append</a></li>\n\t\t\t\t\t\t<li><a href=\"pages/TSL.html#applyVarianceClipping\" target=\"viewer\">applyVarianceClipping</a></li>\n\t\t\t\t\t\t<li><a href=\"pages/TSL.html#array\" target=\"viewer\">array</a></li>\n\t\t\t\t\t\t<li><a href=\"pages/TSL.html#asin\" target=\"viewer\">asin</a></li>\n\t\t\t\t\t\t<li><a href=\"pages/TSL.html#asinh\" target=\"viewer\">asinh</a></li>\n\t\t\t\t\t\t<li><a href=\"pages/TSL.html#assign\" target=\"viewer\">assign</a></li>\n\t\t\t\t\t\t<li><a href=\"pages/TSL.html#atan\" target=\"viewer\">atan</a></li>\n\t\t\t\t\t\t<li><a href=\"pages/TSL.html#atanh\" target=\"viewer\">atanh</a></li>\n\t\t\t\t\t\t<li><a href=\"pages/TSL.html#atomicAdd\" target=\"viewer\">atomicAdd</a></li>\n\t\t\t\t\t\t<li><a href=\"pages/TSL.html#atomicAnd\" target=\"viewer\">atomicAnd</a></li>\n\t\t\t\t\t\t<li><a href=\"pages/TSL.html#atomicFunc\" target=\"viewer\">atomicFunc</a></li>\n\t\t\t\t\t\t<li><a href=\"pages/TSL.html#atomicLoad\" target=\"viewer\">atomicLoad</a></li>\n\t\t\t\t\t\t<li><a href=\"pages/TSL.html#atomicMax\" target=\"viewer\">atomicMax</a></li>\n\t\t\t\t\t\t<li><a href=\"pages/TSL.html#atomicMin\" target=\"viewer\">atomicMin</a></li>\n\t\t\t\t\t\t<li><a href=\"pages/TSL.html#atomicNode\" target=\"viewer\">atomicNode</a></li>\n\t\t\t\t\t\t<li><a href=\"pages/TSL.html#atomicOr\" target=\"viewer\">atomicOr</a></li>\n\t\t\t\t\t\t<li><a href=\"pages/TSL.html#atomicStore\" target=\"viewer\">atomicStore</a></li>\n\t\t\t\t\t\t<li><a href=\"pages/TSL.html#atomicSub\" target=\"viewer\">atomicSub</a></li>\n\t\t\t\t\t\t<li><a href=\"pages/TSL.html#atomicXor\" target=\"viewer\">atomicXor</a></li>\n\t\t\t\t\t\t<li><a href=\"pages/TSL.html#attenuationColor\" target=\"viewer\">attenuationColor</a></li>\n\t\t\t\t\t\t<li><a href=\"pages/TSL.html#attenuationDistance\" target=\"viewer\">attenuationDistance</a></li>\n\t\t\t\t\t\t<li><a href=\"pages/TSL.html#attribute\" target=\"viewer\">attribute</a></li>\n\t\t\t\t\t\t<li><a href=\"pages/TSL.html#attributeArray\" target=\"viewer\">attributeArray</a></li>\n\t\t\t\t\t\t<li><a href=\"pages/TSL.html#backgroundBlurriness\" target=\"viewer\">backgroundBlurriness</a></li>\n\t\t\t\t\t\t<li><a href=\"pages/TSL.html#backgroundIntensity\" target=\"viewer\">backgroundIntensity</a></li>\n\t\t\t\t\t\t<li><a href=\"pages/TSL.html#backgroundRotation\" target=\"viewer\">backgroundRotation</a></li>\n\t\t\t\t\t\t<li><a href=\"pages/TSL.html#barrelMask\" target=\"viewer\">barrelMask</a></li>\n\t\t\t\t\t\t<li><a href=\"pages/TSL.html#barrelUV\" target=\"viewer\">barrelUV</a></li>\n\t\t\t\t\t\t<li><a href=\"pages/TSL.html#barrier\" target=\"viewer\">barrier</a></li>\n\t\t\t\t\t\t<li><a href=\"pages/TSL.html#batch\" target=\"viewer\">batch</a></li>\n\t\t\t\t\t\t<li><a href=\"pages/TSL.html#beautyTexelFromScreen\" target=\"viewer\">beautyTexelFromScreen</a></li>\n\t\t\t\t\t\t<li><a href=\"pages/TSL.html#bentNormalView\" target=\"viewer\">bentNormalView</a></li>\n\t\t\t\t\t\t<li><a href=\"pages/TSL.html#bilateralBlur\" target=\"viewer\">bilateralBlur</a></li>\n\t\t\t\t\t\t<li><a href=\"pages/TSL.html#billboarding\" target=\"viewer\">billboarding</a></li>\n\t\t\t\t\t\t<li><a href=\"pages/TSL.html#bitAnd\" target=\"viewer\">bitAnd</a></li>\n\t\t\t\t\t\t<li><a href=\"pages/TSL.html#bitNot\" target=\"viewer\">bitNot</a></li>\n\t\t\t\t\t\t<li><a href=\"pages/TSL.html#bitOr\" target=\"viewer\">bitOr</a></li>\n\t\t\t\t\t\t<li><a href=\"pages/TSL.html#bitXor\" target=\"viewer\">bitXor</a></li>\n\t\t\t\t\t\t<li><a href=\"pages/TSL.html#bitangentGeometry\" target=\"viewer\">bitangentGeometry</a></li>\n\t\t\t\t\t\t<li><a href=\"pages/TSL.html#bitangentLocal\" target=\"viewer\">bitangentLocal</a></li>\n\t\t\t\t\t\t<li><a href=\"pages/TSL.html#bitangentView\" target=\"viewer\">bitangentView</a></li>\n\t\t\t\t\t\t<li><a href=\"pages/TSL.html#bitangentViewFrame\" target=\"viewer\">bitangentViewFrame</a></li>\n\t\t\t\t\t\t<li><a href=\"pages/TSL.html#bitangentWorld\" target=\"viewer\">bitangentWorld</a></li>\n\t\t\t\t\t\t<li><a href=\"pages/TSL.html#bitcast\" target=\"viewer\">bitcast</a></li>\n\t\t\t\t\t\t<li><a href=\"pages/TSL.html#bleach\" target=\"viewer\">bleach</a></li>\n\t\t\t\t\t\t<li><a href=\"pages/TSL.html#blendBurn\" target=\"viewer\">blendBurn</a></li>\n\t\t\t\t\t\t<li><a href=\"pages/TSL.html#blendColor\" target=\"viewer\">blendColor</a></li>\n\t\t\t\t\t\t<li><a href=\"pages/TSL.html#blendDodge\" target=\"viewer\">blendDodge</a></li>\n\t\t\t\t\t\t<li><a href=\"pages/TSL.html#blendOverlay\" target=\"viewer\">blendOverlay</a></li>\n\t\t\t\t\t\t<li><a href=\"pages/TSL.html#blendScreen\" target=\"viewer\">blendScreen</a></li>\n\t\t\t\t\t\t<li><a href=\"pages/TSL.html#bloom\" target=\"viewer\">bloom</a></li>\n\t\t\t\t\t\t<li><a href=\"pages/TSL.html#boxBlur\" target=\"viewer\">boxBlur</a></li>\n\t\t\t\t\t\t<li><a href=\"pages/TSL.html#buffer\" target=\"viewer\">buffer</a></li>\n\t\t\t\t\t\t<li><a href=\"pages/TSL.html#bufferAttribute\" target=\"viewer\">bufferAttribute</a></li>\n\t\t\t\t\t\t<li><a href=\"pages/TSL.html#builtin\" target=\"viewer\">builtin</a></li>\n\t\t\t\t\t\t<li><a href=\"pages/TSL.html#builtinAOContext\" target=\"viewer\">builtinAOContext</a></li>\n\t\t\t\t\t\t<li><a href=\"pages/TSL.html#builtinShadowContext\" target=\"viewer\">builtinShadowContext</a></li>\n\t\t\t\t\t\t<li><a href=\"pages/TSL.html#bumpMap\" target=\"viewer\">bumpMap</a></li>\n\t\t\t\t\t\t<li><a href=\"pages/TSL.html#bypass\" target=\"viewer\">bypass</a></li>\n\t\t\t\t\t\t<li><a href=\"pages/TSL.html#cache\" target=\"viewer\">cache</a></li>\n\t\t\t\t\t\t<li><a href=\"pages/TSL.html#cameraFar\" target=\"viewer\">cameraFar</a></li>\n\t\t\t\t\t\t<li><a href=\"pages/TSL.html#cameraIndex\" target=\"viewer\">cameraIndex</a></li>\n\t\t\t\t\t\t<li><a href=\"pages/TSL.html#cameraNear\" target=\"viewer\">cameraNear</a></li>\n\t\t\t\t\t\t<li><a href=\"pages/TSL.html#cameraNormalMatrix\" target=\"viewer\">cameraNormalMatrix</a></li>\n\t\t\t\t\t\t<li><a href=\"pages/TSL.html#cameraPosition\" target=\"viewer\">cameraPosition</a></li>\n\t\t\t\t\t\t<li><a href=\"pages/TSL.html#cameraProjectionMatrix\" target=\"viewer\">cameraProjectionMatrix</a></li>\n\t\t\t\t\t\t<li><a href=\"pages/TSL.html#cameraProjectionMatrixInverse\" target=\"viewer\">cameraProjectionMatrixInverse</a></li>\n\t\t\t\t\t\t<li><a href=\"pages/TSL.html#cameraViewMatrix\" target=\"viewer\">cameraViewMatrix</a></li>\n\t\t\t\t\t\t<li><a href=\"pages/TSL.html#cameraViewport\" target=\"viewer\">cameraViewport</a></li>\n\t\t\t\t\t\t<li><a href=\"pages/TSL.html#cameraWorldMatrix\" target=\"viewer\">cameraWorldMatrix</a></li>\n\t\t\t\t\t\t<li><a href=\"pages/TSL.html#cbrt\" target=\"viewer\">cbrt</a></li>\n\t\t\t\t\t\t<li><a href=\"pages/TSL.html#cdl\" target=\"viewer\">cdl</a></li>\n\t\t\t\t\t\t<li><a href=\"pages/TSL.html#ceil\" target=\"viewer\">ceil</a></li>\n\t\t\t\t\t\t<li><a href=\"pages/TSL.html#checker\" target=\"viewer\">checker</a></li>\n\t\t\t\t\t\t<li><a href=\"pages/TSL.html#chromaticAberration\" target=\"viewer\">chromaticAberration</a></li>\n\t\t\t\t\t\t<li><a href=\"pages/TSL.html#cineonToneMapping\" target=\"viewer\">cineonToneMapping</a></li>\n\t\t\t\t\t\t<li><a href=\"pages/TSL.html#circle\" target=\"viewer\">circle</a></li>\n\t\t\t\t\t\t<li><a href=\"pages/TSL.html#clamp\" target=\"viewer\">clamp</a></li>\n\t\t\t\t\t\t<li><a href=\"pages/TSL.html#clearcoat\" target=\"viewer\">clearcoat</a></li>\n\t\t\t\t\t\t<li><a href=\"pages/TSL.html#clearcoatNormalView\" target=\"viewer\">clearcoatNormalView</a></li>\n\t\t\t\t\t\t<li><a href=\"pages/TSL.html#clearcoatRoughness\" target=\"viewer\">clearcoatRoughness</a></li>\n\t\t\t\t\t\t<li><a href=\"pages/TSL.html#clipSpace\" target=\"viewer\">clipSpace</a></li>\n\t\t\t\t\t\t<li><a href=\"pages/TSL.html#clipToAABB\" target=\"viewer\">clipToAABB</a></li>\n\t\t\t\t\t\t<li><a href=\"pages/TSL.html#clipping\" target=\"viewer\">clipping</a></li>\n\t\t\t\t\t\t<li><a href=\"pages/TSL.html#clippingAlpha\" target=\"viewer\">clippingAlpha</a></li>\n\t\t\t\t\t\t<li><a href=\"pages/TSL.html#clusteredLights\" target=\"viewer\">clusteredLights</a></li>\n\t\t\t\t\t\t<li><a href=\"pages/TSL.html#code\" target=\"viewer\">code</a></li>\n\t\t\t\t\t\t<li><a href=\"pages/TSL.html#collectNeighborhood\" target=\"viewer\">collectNeighborhood</a></li>\n\t\t\t\t\t\t<li><a href=\"pages/TSL.html#colorBleeding\" target=\"viewer\">colorBleeding</a></li>\n\t\t\t\t\t\t<li><a href=\"pages/TSL.html#colorSpaceToWorking\" target=\"viewer\">colorSpaceToWorking</a></li>\n\t\t\t\t\t\t<li><a href=\"pages/TSL.html#colorToDirection\" target=\"viewer\">colorToDirection</a></li>\n\t\t\t\t\t\t<li><a href=\"pages/TSL.html#compute\" target=\"viewer\">compute</a></li>\n\t\t\t\t\t\t<li><a href=\"pages/TSL.html#computeBuiltin\" target=\"viewer\">computeBuiltin</a></li>\n\t\t\t\t\t\t<li><a href=\"pages/TSL.html#computeFrustumSize\" target=\"viewer\">computeFrustumSize</a></li>\n\t\t\t\t\t\t<li><a href=\"pages/TSL.html#computeHitDistFactor\" target=\"viewer\">computeHitDistFactor</a></li>\n\t\t\t\t\t\t<li><a href=\"pages/TSL.html#computeKernel\" target=\"viewer\">computeKernel</a></li>\n\t\t\t\t\t\t<li><a href=\"pages/TSL.html#computeSkinning\" target=\"viewer\">computeSkinning</a></li>\n\t\t\t\t\t\t<li><a href=\"pages/TSL.html#context\" target=\"viewer\">context</a></li>\n\t\t\t\t\t\t<li><a href=\"pages/TSL.html#convertColorSpace\" target=\"viewer\">convertColorSpace</a></li>\n\t\t\t\t\t\t<li><a href=\"pages/TSL.html#convertToTexture\" target=\"viewer\">convertToTexture</a></li>\n\t\t\t\t\t\t<li><a href=\"pages/TSL.html#cos\" target=\"viewer\">cos</a></li>\n\t\t\t\t\t\t<li><a href=\"pages/TSL.html#cosh\" target=\"viewer\">cosh</a></li>\n\t\t\t\t\t\t<li><a href=\"pages/TSL.html#countLeadingZeros\" target=\"viewer\">countLeadingZeros</a></li>\n\t\t\t\t\t\t<li><a href=\"pages/TSL.html#countOneBits\" target=\"viewer\">countOneBits</a></li>\n\t\t\t\t\t\t<li><a href=\"pages/TSL.html#countTrailingZeros\" target=\"viewer\">countTrailingZeros</a></li>\n\t\t\t\t\t\t<li><a href=\"pages/TSL.html#createVar\" target=\"viewer\">createVar</a></li>\n\t\t\t\t\t\t<li><a href=\"pages/TSL.html#cross\" target=\"viewer\">cross</a></li>\n\t\t\t\t\t\t<li><a href=\"pages/TSL.html#cubeMapNode\" target=\"viewer\">cubeMapNode</a></li>\n\t\t\t\t\t\t<li><a href=\"pages/TSL.html#cubeTexture\" target=\"viewer\">cubeTexture</a></li>\n\t\t\t\t\t\t<li><a href=\"pages/TSL.html#cubeTextureBase\" target=\"viewer\">cubeTextureBase</a></li>\n\t\t\t\t\t\t<li><a href=\"pages/TSL.html#curlNoise\" target=\"viewer\">curlNoise</a></li>\n\t\t\t\t\t\t<li><a href=\"pages/TSL.html#dFdx\" target=\"viewer\">dFdx</a></li>\n\t\t\t\t\t\t<li><a href=\"pages/TSL.html#dFdy\" target=\"viewer\">dFdy</a></li>\n\t\t\t\t\t\t<li><a href=\"pages/TSL.html#dashSize\" target=\"viewer\">dashSize</a></li>\n\t\t\t\t\t\t<li><a href=\"pages/TSL.html#debug\" target=\"viewer\">debug</a></li>\n\t\t\t\t\t\t<li><a href=\"pages/TSL.html#decrement\" target=\"viewer\">decrement</a></li>\n\t\t\t\t\t\t<li><a href=\"pages/TSL.html#decrementBefore\" target=\"viewer\">decrementBefore</a></li>\n\t\t\t\t\t\t<li><a href=\"pages/TSL.html#degrees\" target=\"viewer\">degrees</a></li>\n\t\t\t\t\t\t<li><a href=\"pages/TSL.html#deltaTime\" target=\"viewer\">deltaTime</a></li>\n\t\t\t\t\t\t<li><a href=\"pages/TSL.html#denoise\" target=\"viewer\">denoise</a></li>\n\t\t\t\t\t\t<li><a href=\"pages/TSL.html#densityFogFactor\" target=\"viewer\">densityFogFactor</a></li>\n\t\t\t\t\t\t<li><a href=\"pages/TSL.html#depth\" target=\"viewer\">depth</a></li>\n\t\t\t\t\t\t<li><a href=\"pages/TSL.html#depthBase\" target=\"viewer\">depthBase</a></li>\n\t\t\t\t\t\t<li><a href=\"pages/TSL.html#depthPass\" target=\"viewer\">depthPass</a></li>\n\t\t\t\t\t\t<li><a href=\"pages/TSL.html#determinant\" target=\"viewer\">determinant</a></li>\n\t\t\t\t\t\t<li><a href=\"pages/TSL.html#difference\" target=\"viewer\">difference</a></li>\n\t\t\t\t\t\t<li><a href=\"pages/TSL.html#diffuseColor\" target=\"viewer\">diffuseColor</a></li>\n\t\t\t\t\t\t<li><a href=\"pages/TSL.html#diffuseColorDistance\" target=\"viewer\">diffuseColorDistance</a></li>\n\t\t\t\t\t\t<li><a href=\"pages/TSL.html#diffuseContribution\" target=\"viewer\">diffuseContribution</a></li>\n\t\t\t\t\t\t<li><a href=\"pages/TSL.html#directionToColor\" target=\"viewer\">directionToColor</a></li>\n\t\t\t\t\t\t<li><a href=\"pages/TSL.html#directionToFaceDirection\" target=\"viewer\">directionToFaceDirection</a></li>\n\t\t\t\t\t\t<li><a href=\"pages/TSL.html#dispersion\" target=\"viewer\">dispersion</a></li>\n\t\t\t\t\t\t<li><a href=\"pages/TSL.html#distance\" target=\"viewer\">distance</a></li>\n\t\t\t\t\t\t<li><a href=\"pages/TSL.html#div\" target=\"viewer\">div</a></li>\n\t\t\t\t\t\t<li><a href=\"pages/TSL.html#dof\" target=\"viewer\">dof</a></li>\n\t\t\t\t\t\t<li><a href=\"pages/TSL.html#dot\" target=\"viewer\">dot</a></li>\n\t\t\t\t\t\t<li><a href=\"pages/TSL.html#dotScreen\" target=\"viewer\">dotScreen</a></li>\n\t\t\t\t\t\t<li><a href=\"pages/TSL.html#drawIndex\" target=\"viewer\">drawIndex</a></li>\n\t\t\t\t\t\t<li><a href=\"pages/TSL.html#dynamicBufferAttribute\" target=\"viewer\">dynamicBufferAttribute</a></li>\n\t\t\t\t\t\t<li><a href=\"pages/TSL.html#dynamicLights\" target=\"viewer\">dynamicLights</a></li>\n\t\t\t\t\t\t<li><a href=\"pages/TSL.html#emissive\" target=\"viewer\">emissive</a></li>\n\t\t\t\t\t\t<li><a href=\"pages/TSL.html#equal\" target=\"viewer\">equal</a></li>\n\t\t\t\t\t\t<li><a href=\"pages/TSL.html#equirectDirection\" target=\"viewer\">equirectDirection</a></li>\n\t\t\t\t\t\t<li><a href=\"pages/TSL.html#equirectUV\" target=\"viewer\">equirectUV</a></li>\n\t\t\t\t\t\t<li><a href=\"pages/TSL.html#exp\" target=\"viewer\">exp</a></li>\n\t\t\t\t\t\t<li><a href=\"pages/TSL.html#exp2\" target=\"viewer\">exp2</a></li>\n\t\t\t\t\t\t<li><a href=\"pages/TSL.html#exponentialHeightFogFactor\" target=\"viewer\">exponentialHeightFogFactor</a></li>\n\t\t\t\t\t\t<li><a href=\"pages/TSL.html#expression\" target=\"viewer\">expression</a></li>\n\t\t\t\t\t\t<li><a href=\"pages/TSL.html#faceDirection\" target=\"viewer\">faceDirection</a></li>\n\t\t\t\t\t\t<li><a href=\"pages/TSL.html#faceForward\" target=\"viewer\">faceForward</a></li>\n\t\t\t\t\t\t<li><a href=\"pages/TSL.html#film\" target=\"viewer\">film</a></li>\n\t\t\t\t\t\t<li><a href=\"pages/TSL.html#floatBitsToInt\" target=\"viewer\">floatBitsToInt</a></li>\n\t\t\t\t\t\t<li><a href=\"pages/TSL.html#floatBitsToUint\" target=\"viewer\">floatBitsToUint</a></li>\n\t\t\t\t\t\t<li><a href=\"pages/TSL.html#floor\" target=\"viewer\">floor</a></li>\n\t\t\t\t\t\t<li><a href=\"pages/TSL.html#fog\" target=\"viewer\">fog</a></li>\n\t\t\t\t\t\t<li><a href=\"pages/TSL.html#fract\" target=\"viewer\">fract</a></li>\n\t\t\t\t\t\t<li><a href=\"pages/TSL.html#frameGroup\" target=\"viewer\">frameGroup</a></li>\n\t\t\t\t\t\t<li><a href=\"pages/TSL.html#frameId\" target=\"viewer\">frameId</a></li>\n\t\t\t\t\t\t<li><a href=\"pages/TSL.html#frontFacing\" target=\"viewer\">frontFacing</a></li>\n\t\t\t\t\t\t<li><a href=\"pages/TSL.html#fsr1\" target=\"viewer\">fsr1</a></li>\n\t\t\t\t\t\t<li><a href=\"pages/TSL.html#fwidth\" target=\"viewer\">fwidth</a></li>\n\t\t\t\t\t\t<li><a href=\"pages/TSL.html#fxaa\" target=\"viewer\">fxaa</a></li>\n\t\t\t\t\t\t<li><a href=\"pages/TSL.html#gain\" target=\"viewer\">gain</a></li>\n\t\t\t\t\t\t<li><a href=\"pages/TSL.html#gapSize\" target=\"viewer\">gapSize</a></li>\n\t\t\t\t\t\t<li><a href=\"pages/TSL.html#gaussianBlur\" target=\"viewer\">gaussianBlur</a></li>\n\t\t\t\t\t\t<li><a href=\"pages/TSL.html#getNormalFromDepth\" target=\"viewer\">getNormalFromDepth</a></li>\n\t\t\t\t\t\t<li><a href=\"pages/TSL.html#getParallaxCorrectNormal\" target=\"viewer\">getParallaxCorrectNormal</a></li>\n\t\t\t\t\t\t<li><a href=\"pages/TSL.html#getScreenPosition\" target=\"viewer\">getScreenPosition</a></li>\n\t\t\t\t\t\t<li><a href=\"pages/TSL.html#getShadowMaterial\" target=\"viewer\">getShadowMaterial</a></li>\n\t\t\t\t\t\t<li><a href=\"pages/TSL.html#getShadowRenderObjectFunction\" target=\"viewer\">getShadowRenderObjectFunction</a></li>\n\t\t\t\t\t\t<li><a href=\"pages/TSL.html#getSpecularDominantDirection\" target=\"viewer\">getSpecularDominantDirection</a></li>\n\t\t\t\t\t\t<li><a href=\"pages/TSL.html#getTemporalVarianceFactor\" target=\"viewer\">getTemporalVarianceFactor</a></li>\n\t\t\t\t\t\t<li><a href=\"pages/TSL.html#getViewPosition\" target=\"viewer\">getViewPosition</a></li>\n\t\t\t\t\t\t<li><a href=\"pages/TSL.html#globalId\" target=\"viewer\">globalId</a></li>\n\t\t\t\t\t\t<li><a href=\"pages/TSL.html#glsl\" target=\"viewer\">glsl</a></li>\n\t\t\t\t\t\t<li><a href=\"pages/TSL.html#godrays\" target=\"viewer\">godrays</a></li>\n\t\t\t\t\t\t<li><a href=\"pages/TSL.html#grayscale\" target=\"viewer\">grayscale</a></li>\n\t\t\t\t\t\t<li><a href=\"pages/TSL.html#greaterThan\" target=\"viewer\">greaterThan</a></li>\n\t\t\t\t\t\t<li><a href=\"pages/TSL.html#greaterThanEqual\" target=\"viewer\">greaterThanEqual</a></li>\n\t\t\t\t\t\t<li><a href=\"pages/TSL.html#hardwareClipping\" target=\"viewer\">hardwareClipping</a></li>\n\t\t\t\t\t\t<li><a href=\"pages/TSL.html#hash\" target=\"viewer\">hash</a></li>\n\t\t\t\t\t\t<li><a href=\"pages/TSL.html#hashBlur\" target=\"viewer\">hashBlur</a></li>\n\t\t\t\t\t\t<li><a href=\"pages/TSL.html#highpModelNormalViewMatrix\" target=\"viewer\">highpModelNormalViewMatrix</a></li>\n\t\t\t\t\t\t<li><a href=\"pages/TSL.html#highpModelViewMatrix\" target=\"viewer\">highpModelViewMatrix</a></li>\n\t\t\t\t\t\t<li><a href=\"pages/TSL.html#hue\" target=\"viewer\">hue</a></li>\n\t\t\t\t\t\t<li><a href=\"pages/TSL.html#increment\" target=\"viewer\">increment</a></li>\n\t\t\t\t\t\t<li><a href=\"pages/TSL.html#incrementBefore\" target=\"viewer\">incrementBefore</a></li>\n\t\t\t\t\t\t<li><a href=\"pages/TSL.html#inspector\" target=\"viewer\">inspector</a></li>\n\t\t\t\t\t\t<li><a href=\"pages/TSL.html#instance\" target=\"viewer\">instance</a></li>\n\t\t\t\t\t\t<li><a href=\"pages/TSL.html#instanceIndex\" target=\"viewer\">instanceIndex</a></li>\n\t\t\t\t\t\t<li><a href=\"pages/TSL.html#instancedArray\" target=\"viewer\">instancedArray</a></li>\n\t\t\t\t\t\t<li><a href=\"pages/TSL.html#instancedBufferAttribute\" target=\"viewer\">instancedBufferAttribute</a></li>\n\t\t\t\t\t\t<li><a href=\"pages/TSL.html#instancedDynamicBufferAttribute\" target=\"viewer\">instancedDynamicBufferAttribute</a></li>\n\t\t\t\t\t\t<li><a href=\"pages/TSL.html#instancedMesh\" target=\"viewer\">instancedMesh</a></li>\n\t\t\t\t\t\t<li><a href=\"pages/TSL.html#intBitsToFloat\" target=\"viewer\">intBitsToFloat</a></li>\n\t\t\t\t\t\t<li><a href=\"pages/TSL.html#interleavedGradientNoise\" target=\"viewer\">interleavedGradientNoise</a></li>\n\t\t\t\t\t\t<li><a href=\"pages/TSL.html#inverse\" target=\"viewer\">inverse</a></li>\n\t\t\t\t\t\t<li><a href=\"pages/TSL.html#inverseSqrt\" target=\"viewer\">inverseSqrt</a></li>\n\t\t\t\t\t\t<li><a href=\"pages/TSL.html#invocationLocalIndex\" target=\"viewer\">invocationLocalIndex</a></li>\n\t\t\t\t\t\t<li><a href=\"pages/TSL.html#invocationSubgroupIndex\" target=\"viewer\">invocationSubgroupIndex</a></li>\n\t\t\t\t\t\t<li><a href=\"pages/TSL.html#ior\" target=\"viewer\">ior</a></li>\n\t\t\t\t\t\t<li><a href=\"pages/TSL.html#iridescence\" target=\"viewer\">iridescence</a></li>\n\t\t\t\t\t\t<li><a href=\"pages/TSL.html#iridescenceIOR\" target=\"viewer\">iridescenceIOR</a></li>\n\t\t\t\t\t\t<li><a href=\"pages/TSL.html#iridescenceThickness\" target=\"viewer\">iridescenceThickness</a></li>\n\t\t\t\t\t\t<li><a href=\"pages/TSL.html#isolate\" target=\"viewer\">isolate</a></li>\n\t\t\t\t\t\t<li><a href=\"pages/TSL.html#js\" target=\"viewer\">js</a></li>\n\t\t\t\t\t\t<li><a href=\"pages/TSL.html#karisTemporalBlend\" target=\"viewer\">karisTemporalBlend</a></li>\n\t\t\t\t\t\t<li><a href=\"pages/TSL.html#label\" target=\"viewer\">label</a></li>\n\t\t\t\t\t\t<li><a href=\"pages/TSL.html#length\" target=\"viewer\">length</a></li>\n\t\t\t\t\t\t<li><a href=\"pages/TSL.html#lengthSq\" target=\"viewer\">lengthSq</a></li>\n\t\t\t\t\t\t<li><a href=\"pages/TSL.html#lensflare\" target=\"viewer\">lensflare</a></li>\n\t\t\t\t\t\t<li><a href=\"pages/TSL.html#lessThan\" target=\"viewer\">lessThan</a></li>\n\t\t\t\t\t\t<li><a href=\"pages/TSL.html#lessThanEqual\" target=\"viewer\">lessThanEqual</a></li>\n\t\t\t\t\t\t<li><a href=\"pages/TSL.html#lightPosition\" target=\"viewer\">lightPosition</a></li>\n\t\t\t\t\t\t<li><a href=\"pages/TSL.html#lightProjectionUV\" target=\"viewer\">lightProjectionUV</a></li>\n\t\t\t\t\t\t<li><a href=\"pages/TSL.html#lightShadowMatrix\" target=\"viewer\">lightShadowMatrix</a></li>\n\t\t\t\t\t\t<li><a href=\"pages/TSL.html#lightTargetDirection\" target=\"viewer\">lightTargetDirection</a></li>\n\t\t\t\t\t\t<li><a href=\"pages/TSL.html#lightTargetPosition\" target=\"viewer\">lightTargetPosition</a></li>\n\t\t\t\t\t\t<li><a href=\"pages/TSL.html#lightViewPosition\" target=\"viewer\">lightViewPosition</a></li>\n\t\t\t\t\t\t<li><a href=\"pages/TSL.html#lights\" target=\"viewer\">lights</a></li>\n\t\t\t\t\t\t<li><a href=\"pages/TSL.html#linearDepth\" target=\"viewer\">linearDepth</a></li>\n\t\t\t\t\t\t<li><a href=\"pages/TSL.html#linearToneMapping\" target=\"viewer\">linearToneMapping</a></li>\n\t\t\t\t\t\t<li><a href=\"pages/TSL.html#lobeNormalFalloff\" target=\"viewer\">lobeNormalFalloff</a></li>\n\t\t\t\t\t\t<li><a href=\"pages/TSL.html#lobeNormalWeight\" target=\"viewer\">lobeNormalWeight</a></li>\n\t\t\t\t\t\t<li><a href=\"pages/TSL.html#localId\" target=\"viewer\">localId</a></li>\n\t\t\t\t\t\t<li><a href=\"pages/TSL.html#log\" target=\"viewer\">log</a></li>\n\t\t\t\t\t\t<li><a href=\"pages/TSL.html#log2\" target=\"viewer\">log2</a></li>\n\t\t\t\t\t\t<li><a href=\"pages/TSL.html#logarithmicDepthToViewZ\" target=\"viewer\">logarithmicDepthToViewZ</a></li>\n\t\t\t\t\t\t<li><a href=\"pages/TSL.html#luminance\" target=\"viewer\">luminance</a></li>\n\t\t\t\t\t\t<li><a href=\"pages/TSL.html#lut3D\" target=\"viewer\">lut3D</a></li>\n\t\t\t\t\t\t<li><a href=\"pages/TSL.html#mapAo\" target=\"viewer\">mapAo</a></li>\n\t\t\t\t\t\t<li><a href=\"pages/TSL.html#matcapUV\" target=\"viewer\">matcapUV</a></li>\n\t\t\t\t\t\t<li><a href=\"pages/TSL.html#materialAO\" target=\"viewer\">materialAO</a></li>\n\t\t\t\t\t\t<li><a href=\"pages/TSL.html#materialAlphaTest\" target=\"viewer\">materialAlphaTest</a></li>\n\t\t\t\t\t\t<li><a href=\"pages/TSL.html#materialAnisotropy\" target=\"viewer\">materialAnisotropy</a></li>\n\t\t\t\t\t\t<li><a href=\"pages/TSL.html#materialAnisotropyVector\" target=\"viewer\">materialAnisotropyVector</a></li>\n\t\t\t\t\t\t<li><a href=\"pages/TSL.html#materialAttenuationColor\" target=\"viewer\">materialAttenuationColor</a></li>\n\t\t\t\t\t\t<li><a href=\"pages/TSL.html#materialAttenuationDistance\" target=\"viewer\">materialAttenuationDistance</a></li>\n\t\t\t\t\t\t<li><a href=\"pages/TSL.html#materialClearcoat\" target=\"viewer\">materialClearcoat</a></li>\n\t\t\t\t\t\t<li><a href=\"pages/TSL.html#materialClearcoatNormal\" target=\"viewer\">materialClearcoatNormal</a></li>\n\t\t\t\t\t\t<li><a href=\"pages/TSL.html#materialClearcoatRoughness\" target=\"viewer\">materialClearcoatRoughness</a></li>\n\t\t\t\t\t\t<li><a href=\"pages/TSL.html#materialColor\" target=\"viewer\">materialColor</a></li>\n\t\t\t\t\t\t<li><a href=\"pages/TSL.html#materialDispersion\" target=\"viewer\">materialDispersion</a></li>\n\t\t\t\t\t\t<li><a href=\"pages/TSL.html#materialEmissive\" target=\"viewer\">materialEmissive</a></li>\n\t\t\t\t\t\t<li><a href=\"pages/TSL.html#materialEnvIntensity\" target=\"viewer\">materialEnvIntensity</a></li>\n\t\t\t\t\t\t<li><a href=\"pages/TSL.html#materialEnvRotation\" target=\"viewer\">materialEnvRotation</a></li>\n\t\t\t\t\t\t<li><a href=\"pages/TSL.html#materialIOR\" target=\"viewer\">materialIOR</a></li>\n\t\t\t\t\t\t<li><a href=\"pages/TSL.html#materialIridescence\" target=\"viewer\">materialIridescence</a></li>\n\t\t\t\t\t\t<li><a href=\"pages/TSL.html#materialIridescenceIOR\" target=\"viewer\">materialIridescenceIOR</a></li>\n\t\t\t\t\t\t<li><a href=\"pages/TSL.html#materialIridescenceThickness\" target=\"viewer\">materialIridescenceThickness</a></li>\n\t\t\t\t\t\t<li><a href=\"pages/TSL.html#materialLightMap\" target=\"viewer\">materialLightMap</a></li>\n\t\t\t\t\t\t<li><a href=\"pages/TSL.html#materialLineDashOffset\" target=\"viewer\">materialLineDashOffset</a></li>\n\t\t\t\t\t\t<li><a href=\"pages/TSL.html#materialLineDashSize\" target=\"viewer\">materialLineDashSize</a></li>\n\t\t\t\t\t\t<li><a href=\"pages/TSL.html#materialLineGapSize\" target=\"viewer\">materialLineGapSize</a></li>\n\t\t\t\t\t\t<li><a href=\"pages/TSL.html#materialLineScale\" target=\"viewer\">materialLineScale</a></li>\n\t\t\t\t\t\t<li><a href=\"pages/TSL.html#materialLineWidth\" target=\"viewer\">materialLineWidth</a></li>\n\t\t\t\t\t\t<li><a href=\"pages/TSL.html#materialMetalness\" target=\"viewer\">materialMetalness</a></li>\n\t\t\t\t\t\t<li><a href=\"pages/TSL.html#materialNormal\" target=\"viewer\">materialNormal</a></li>\n\t\t\t\t\t\t<li><a href=\"pages/TSL.html#materialOpacity\" target=\"viewer\">materialOpacity</a></li>\n\t\t\t\t\t\t<li><a href=\"pages/TSL.html#materialPointSize\" target=\"viewer\">materialPointSize</a></li>\n\t\t\t\t\t\t<li><a href=\"pages/TSL.html#materialReference\" target=\"viewer\">materialReference</a></li>\n\t\t\t\t\t\t<li><a href=\"pages/TSL.html#materialReflectivity\" target=\"viewer\">materialReflectivity</a></li>\n\t\t\t\t\t\t<li><a href=\"pages/TSL.html#materialRefractionRatio\" target=\"viewer\">materialRefractionRatio</a></li>\n\t\t\t\t\t\t<li><a href=\"pages/TSL.html#materialRotation\" target=\"viewer\">materialRotation</a></li>\n\t\t\t\t\t\t<li><a href=\"pages/TSL.html#materialRoughness\" target=\"viewer\">materialRoughness</a></li>\n\t\t\t\t\t\t<li><a href=\"pages/TSL.html#materialSheen\" target=\"viewer\">materialSheen</a></li>\n\t\t\t\t\t\t<li><a href=\"pages/TSL.html#materialSheenRoughness\" target=\"viewer\">materialSheenRoughness</a></li>\n\t\t\t\t\t\t<li><a href=\"pages/TSL.html#materialShininess\" target=\"viewer\">materialShininess</a></li>\n\t\t\t\t\t\t<li><a href=\"pages/TSL.html#materialSpecular\" target=\"viewer\">materialSpecular</a></li>\n\t\t\t\t\t\t<li><a href=\"pages/TSL.html#materialSpecularColor\" target=\"viewer\">materialSpecularColor</a></li>\n\t\t\t\t\t\t<li><a href=\"pages/TSL.html#materialSpecularIntensity\" target=\"viewer\">materialSpecularIntensity</a></li>\n\t\t\t\t\t\t<li><a href=\"pages/TSL.html#materialSpecularStrength\" target=\"viewer\">materialSpecularStrength</a></li>\n\t\t\t\t\t\t<li><a href=\"pages/TSL.html#materialThickness\" target=\"viewer\">materialThickness</a></li>\n\t\t\t\t\t\t<li><a href=\"pages/TSL.html#materialTransmission\" target=\"viewer\">materialTransmission</a></li>\n\t\t\t\t\t\t<li><a href=\"pages/TSL.html#max\" target=\"viewer\">max</a></li>\n\t\t\t\t\t\t<li><a href=\"pages/TSL.html#maxMipLevel\" target=\"viewer\">maxMipLevel</a></li>\n\t\t\t\t\t\t<li><a href=\"pages/TSL.html#mediumpModelViewMatrix\" target=\"viewer\">mediumpModelViewMatrix</a></li>\n\t\t\t\t\t\t<li><a href=\"pages/TSL.html#metalness\" target=\"viewer\">metalness</a></li>\n\t\t\t\t\t\t<li><a href=\"pages/TSL.html#min\" target=\"viewer\">min</a></li>\n\t\t\t\t\t\t<li><a href=\"pages/TSL.html#misPowerHeuristic\" target=\"viewer\">misPowerHeuristic</a></li>\n\t\t\t\t\t\t<li><a href=\"pages/TSL.html#mix\" target=\"viewer\">mix</a></li>\n\t\t\t\t\t\t<li><a href=\"pages/TSL.html#mixElement\" target=\"viewer\">mixElement</a></li>\n\t\t\t\t\t\t<li><a href=\"pages/TSL.html#mod\" target=\"viewer\">mod</a></li>\n\t\t\t\t\t\t<li><a href=\"pages/TSL.html#modelDirection\" target=\"viewer\">modelDirection</a></li>\n\t\t\t\t\t\t<li><a href=\"pages/TSL.html#modelNormalMatrix\" target=\"viewer\">modelNormalMatrix</a></li>\n\t\t\t\t\t\t<li><a href=\"pages/TSL.html#modelPosition\" target=\"viewer\">modelPosition</a></li>\n\t\t\t\t\t\t<li><a href=\"pages/TSL.html#modelRadius\" target=\"viewer\">modelRadius</a></li>\n\t\t\t\t\t\t<li><a href=\"pages/TSL.html#modelScale\" target=\"viewer\">modelScale</a></li>\n\t\t\t\t\t\t<li><a href=\"pages/TSL.html#modelViewMatrix\" target=\"viewer\">modelViewMatrix</a></li>\n\t\t\t\t\t\t<li><a href=\"pages/TSL.html#modelViewPosition\" target=\"viewer\">modelViewPosition</a></li>\n\t\t\t\t\t\t<li><a href=\"pages/TSL.html#modelViewProjection\" target=\"viewer\">modelViewProjection</a></li>\n\t\t\t\t\t\t<li><a href=\"pages/TSL.html#modelWorldMatrix\" target=\"viewer\">modelWorldMatrix</a></li>\n\t\t\t\t\t\t<li><a href=\"pages/TSL.html#modelWorldMatrixInverse\" target=\"viewer\">modelWorldMatrixInverse</a></li>\n\t\t\t\t\t\t<li><a href=\"pages/TSL.html#morphReference\" target=\"viewer\">morphReference</a></li>\n\t\t\t\t\t\t<li><a href=\"pages/TSL.html#motionBlur\" target=\"viewer\">motionBlur</a></li>\n\t\t\t\t\t\t<li><a href=\"pages/TSL.html#mrt\" target=\"viewer\">mrt</a></li>\n\t\t\t\t\t\t<li><a href=\"pages/TSL.html#mul\" target=\"viewer\">mul</a></li>\n\t\t\t\t\t\t<li><a href=\"pages/TSL.html#mvpLine\" target=\"viewer\">mvpLine</a></li>\n\t\t\t\t\t\t<li><a href=\"pages/TSL.html#negate\" target=\"viewer\">negate</a></li>\n\t\t\t\t\t\t<li><a href=\"pages/TSL.html#negateOnBackSide\" target=\"viewer\">negateOnBackSide</a></li>\n\t\t\t\t\t\t<li><a href=\"pages/TSL.html#neutralToneMapping\" target=\"viewer\">neutralToneMapping</a></li>\n\t\t\t\t\t\t<li><a href=\"pages/TSL.html#normalFlat\" target=\"viewer\">normalFlat</a></li>\n\t\t\t\t\t\t<li><a href=\"pages/TSL.html#normalGeometry\" target=\"viewer\">normalGeometry</a></li>\n\t\t\t\t\t\t<li><a href=\"pages/TSL.html#normalLocal\" target=\"viewer\">normalLocal</a></li>\n\t\t\t\t\t\t<li><a href=\"pages/TSL.html#normalMap\" target=\"viewer\">normalMap</a></li>\n\t\t\t\t\t\t<li><a href=\"pages/TSL.html#normalView\" target=\"viewer\">normalView</a></li>\n\t\t\t\t\t\t<li><a href=\"pages/TSL.html#normalViewGeometry\" target=\"viewer\">normalViewGeometry</a></li>\n\t\t\t\t\t\t<li><a href=\"pages/TSL.html#normalWorld\" target=\"viewer\">normalWorld</a></li>\n\t\t\t\t\t\t<li><a href=\"pages/TSL.html#normalWorldGeometry\" target=\"viewer\">normalWorldGeometry</a></li>\n\t\t\t\t\t\t<li><a href=\"pages/TSL.html#normalize\" target=\"viewer\">normalize</a></li>\n\t\t\t\t\t\t<li><a href=\"pages/TSL.html#not\" target=\"viewer\">not</a></li>\n\t\t\t\t\t\t<li><a href=\"pages/TSL.html#notEqual\" target=\"viewer\">notEqual</a></li>\n\t\t\t\t\t\t<li><a href=\"pages/TSL.html#numWorkgroups\" target=\"viewer\">numWorkgroups</a></li>\n\t\t\t\t\t\t<li><a href=\"pages/TSL.html#objectDirection\" target=\"viewer\">objectDirection</a></li>\n\t\t\t\t\t\t<li><a href=\"pages/TSL.html#objectGroup\" target=\"viewer\">objectGroup</a></li>\n\t\t\t\t\t\t<li><a href=\"pages/TSL.html#objectPosition\" target=\"viewer\">objectPosition</a></li>\n\t\t\t\t\t\t<li><a href=\"pages/TSL.html#objectRadius\" target=\"viewer\">objectRadius</a></li>\n\t\t\t\t\t\t<li><a href=\"pages/TSL.html#objectScale\" target=\"viewer\">objectScale</a></li>\n\t\t\t\t\t\t<li><a href=\"pages/TSL.html#objectViewPosition\" target=\"viewer\">objectViewPosition</a></li>\n\t\t\t\t\t\t<li><a href=\"pages/TSL.html#objectWorldMatrix\" target=\"viewer\">objectWorldMatrix</a></li>\n\t\t\t\t\t\t<li><a href=\"pages/TSL.html#oneMinus\" target=\"viewer\">oneMinus</a></li>\n\t\t\t\t\t\t<li><a href=\"pages/TSL.html#or\" target=\"viewer\">or</a></li>\n\t\t\t\t\t\t<li><a href=\"pages/TSL.html#orthographicDepthToViewZ\" target=\"viewer\">orthographicDepthToViewZ</a></li>\n\t\t\t\t\t\t<li><a href=\"pages/TSL.html#oscSawtooth\" target=\"viewer\">oscSawtooth</a></li>\n\t\t\t\t\t\t<li><a href=\"pages/TSL.html#oscSine\" target=\"viewer\">oscSine</a></li>\n\t\t\t\t\t\t<li><a href=\"pages/TSL.html#oscSquare\" target=\"viewer\">oscSquare</a></li>\n\t\t\t\t\t\t<li><a href=\"pages/TSL.html#oscTriangle\" target=\"viewer\">oscTriangle</a></li>\n\t\t\t\t\t\t<li><a href=\"pages/TSL.html#outline\" target=\"viewer\">outline</a></li>\n\t\t\t\t\t\t<li><a href=\"pages/TSL.html#output\" target=\"viewer\">output</a></li>\n\t\t\t\t\t\t<li><a href=\"pages/TSL.html#outputStruct\" target=\"viewer\">outputStruct</a></li>\n\t\t\t\t\t\t<li><a href=\"pages/TSL.html#overloadingFn\" target=\"viewer\">overloadingFn</a></li>\n\t\t\t\t\t\t<li><a href=\"pages/TSL.html#overrideNode\" target=\"viewer\">overrideNode</a></li>\n\t\t\t\t\t\t<li><a href=\"pages/TSL.html#overrideNodes\" target=\"viewer\">overrideNodes</a></li>\n\t\t\t\t\t\t<li><a href=\"pages/TSL.html#packHalf2x16\" target=\"viewer\">packHalf2x16</a></li>\n\t\t\t\t\t\t<li><a href=\"pages/TSL.html#packNormalToRGB\" target=\"viewer\">packNormalToRGB</a></li>\n\t\t\t\t\t\t<li><a href=\"pages/TSL.html#packSnorm2x16\" target=\"viewer\">packSnorm2x16</a></li>\n\t\t\t\t\t\t<li><a href=\"pages/TSL.html#packUnorm2x16\" target=\"viewer\">packUnorm2x16</a></li>\n\t\t\t\t\t\t<li><a href=\"pages/TSL.html#parabola\" target=\"viewer\">parabola</a></li>\n\t\t\t\t\t\t<li><a href=\"pages/TSL.html#parallaxBarrierPass\" target=\"viewer\">parallaxBarrierPass</a></li>\n\t\t\t\t\t\t<li><a href=\"pages/TSL.html#parallaxDirection\" target=\"viewer\">parallaxDirection</a></li>\n\t\t\t\t\t\t<li><a href=\"pages/TSL.html#parallaxUV\" target=\"viewer\">parallaxUV</a></li>\n\t\t\t\t\t\t<li><a href=\"pages/TSL.html#parameter\" target=\"viewer\">parameter</a></li>\n\t\t\t\t\t\t<li><a href=\"pages/TSL.html#pass\" target=\"viewer\">pass</a></li>\n\t\t\t\t\t\t<li><a href=\"pages/TSL.html#passTexture\" target=\"viewer\">passTexture</a></li>\n\t\t\t\t\t\t<li><a href=\"pages/TSL.html#pcurve\" target=\"viewer\">pcurve</a></li>\n\t\t\t\t\t\t<li><a href=\"pages/TSL.html#permute\" target=\"viewer\">permute</a></li>\n\t\t\t\t\t\t<li><a href=\"pages/TSL.html#perspectiveDepthToViewZ\" target=\"viewer\">perspectiveDepthToViewZ</a></li>\n\t\t\t\t\t\t<li><a href=\"pages/TSL.html#pixelationPass\" target=\"viewer\">pixelationPass</a></li>\n\t\t\t\t\t\t<li><a href=\"pages/TSL.html#planeDistance\" target=\"viewer\">planeDistance</a></li>\n\t\t\t\t\t\t<li><a href=\"pages/TSL.html#pmremTexture\" target=\"viewer\">pmremTexture</a></li>\n\t\t\t\t\t\t<li><a href=\"pages/TSL.html#pointShadow\" target=\"viewer\">pointShadow</a></li>\n\t\t\t\t\t\t<li><a href=\"pages/TSL.html#pointUV\" target=\"viewer\">pointUV</a></li>\n\t\t\t\t\t\t<li><a href=\"pages/TSL.html#pointWidth\" target=\"viewer\">pointWidth</a></li>\n\t\t\t\t\t\t<li><a href=\"pages/TSL.html#positionGeometry\" target=\"viewer\">positionGeometry</a></li>\n\t\t\t\t\t\t<li><a href=\"pages/TSL.html#positionLocal\" target=\"viewer\">positionLocal</a></li>\n\t\t\t\t\t\t<li><a href=\"pages/TSL.html#positionPrevious\" target=\"viewer\">positionPrevious</a></li>\n\t\t\t\t\t\t<li><a href=\"pages/TSL.html#positionView\" target=\"viewer\">positionView</a></li>\n\t\t\t\t\t\t<li><a href=\"pages/TSL.html#positionViewDirection\" target=\"viewer\">positionViewDirection</a></li>\n\t\t\t\t\t\t<li><a href=\"pages/TSL.html#positionWorld\" target=\"viewer\">positionWorld</a></li>\n\t\t\t\t\t\t<li><a href=\"pages/TSL.html#positionWorldDirection\" target=\"viewer\">positionWorldDirection</a></li>\n\t\t\t\t\t\t<li><a href=\"pages/TSL.html#posterize\" target=\"viewer\">posterize</a></li>\n\t\t\t\t\t\t<li><a href=\"pages/TSL.html#pow\" target=\"viewer\">pow</a></li>\n\t\t\t\t\t\t<li><a href=\"pages/TSL.html#pow2\" target=\"viewer\">pow2</a></li>\n\t\t\t\t\t\t<li><a href=\"pages/TSL.html#pow3\" target=\"viewer\">pow3</a></li>\n\t\t\t\t\t\t<li><a href=\"pages/TSL.html#pow4\" target=\"viewer\">pow4</a></li>\n\t\t\t\t\t\t<li><a href=\"pages/TSL.html#premultipliedGaussianBlur\" target=\"viewer\">premultipliedGaussianBlur</a></li>\n\t\t\t\t\t\t<li><a href=\"pages/TSL.html#premultiplyAlpha\" target=\"viewer\">premultiplyAlpha</a></li>\n\t\t\t\t\t\t<li><a href=\"pages/TSL.html#projectWorldToUV\" target=\"viewer\">projectWorldToUV</a></li>\n\t\t\t\t\t\t<li><a href=\"pages/TSL.html#property\" target=\"viewer\">property</a></li>\n\t\t\t\t\t\t<li><a href=\"pages/TSL.html#quadBroadcast\" target=\"viewer\">quadBroadcast</a></li>\n\t\t\t\t\t\t<li><a href=\"pages/TSL.html#quadSwapDiagonal\" target=\"viewer\">quadSwapDiagonal</a></li>\n\t\t\t\t\t\t<li><a href=\"pages/TSL.html#quadSwapX\" target=\"viewer\">quadSwapX</a></li>\n\t\t\t\t\t\t<li><a href=\"pages/TSL.html#quadSwapY\" target=\"viewer\">quadSwapY</a></li>\n\t\t\t\t\t\t<li><a href=\"pages/TSL.html#radialBlur\" target=\"viewer\">radialBlur</a></li>\n\t\t\t\t\t\t<li><a href=\"pages/TSL.html#radians\" target=\"viewer\">radians</a></li>\n\t\t\t\t\t\t<li><a href=\"pages/TSL.html#rand\" target=\"viewer\">rand</a></li>\n\t\t\t\t\t\t<li><a href=\"pages/TSL.html#range\" target=\"viewer\">range</a></li>\n\t\t\t\t\t\t<li><a href=\"pages/TSL.html#rangeFogFactor\" target=\"viewer\">rangeFogFactor</a></li>\n\t\t\t\t\t\t<li><a href=\"pages/TSL.html#reciprocal\" target=\"viewer\">reciprocal</a></li>\n\t\t\t\t\t\t<li><a href=\"pages/TSL.html#recurrentDenoise\" target=\"viewer\">recurrentDenoise</a></li>\n\t\t\t\t\t\t<li><a href=\"pages/TSL.html#reference\" target=\"viewer\">reference</a></li>\n\t\t\t\t\t\t<li><a href=\"pages/TSL.html#referenceBuffer\" target=\"viewer\">referenceBuffer</a></li>\n\t\t\t\t\t\t<li><a href=\"pages/TSL.html#reflect\" target=\"viewer\">reflect</a></li>\n\t\t\t\t\t\t<li><a href=\"pages/TSL.html#reflectVector\" target=\"viewer\">reflectVector</a></li>\n\t\t\t\t\t\t<li><a href=\"pages/TSL.html#reflectView\" target=\"viewer\">reflectView</a></li>\n\t\t\t\t\t\t<li><a href=\"pages/TSL.html#reflector\" target=\"viewer\">reflector</a></li>\n\t\t\t\t\t\t<li><a href=\"pages/TSL.html#refract\" target=\"viewer\">refract</a></li>\n\t\t\t\t\t\t<li><a href=\"pages/TSL.html#refractVector\" target=\"viewer\">refractVector</a></li>\n\t\t\t\t\t\t<li><a href=\"pages/TSL.html#refractView\" target=\"viewer\">refractView</a></li>\n\t\t\t\t\t\t<li><a href=\"pages/TSL.html#reinhardToneMapping\" target=\"viewer\">reinhardToneMapping</a></li>\n\t\t\t\t\t\t<li><a href=\"pages/TSL.html#remap\" target=\"viewer\">remap</a></li>\n\t\t\t\t\t\t<li><a href=\"pages/TSL.html#remapClamp\" target=\"viewer\">remapClamp</a></li>\n\t\t\t\t\t\t<li><a href=\"pages/TSL.html#renderGroup\" target=\"viewer\">renderGroup</a></li>\n\t\t\t\t\t\t<li><a href=\"pages/TSL.html#renderOutput\" target=\"viewer\">renderOutput</a></li>\n\t\t\t\t\t\t<li><a href=\"pages/TSL.html#rendererReference\" target=\"viewer\">rendererReference</a></li>\n\t\t\t\t\t\t<li><a href=\"pages/TSL.html#replaceDefaultUV\" target=\"viewer\">replaceDefaultUV</a></li>\n\t\t\t\t\t\t<li><a href=\"pages/TSL.html#reprojectHitPoint\" target=\"viewer\">reprojectHitPoint</a></li>\n\t\t\t\t\t\t<li><a href=\"pages/TSL.html#reprojectionStretchConfidence\" target=\"viewer\">reprojectionStretchConfidence</a></li>\n\t\t\t\t\t\t<li><a href=\"pages/TSL.html#retroPass\" target=\"viewer\">retroPass</a></li>\n\t\t\t\t\t\t<li><a href=\"pages/TSL.html#rgbShift\" target=\"viewer\">rgbShift</a></li>\n\t\t\t\t\t\t<li><a href=\"pages/TSL.html#rotate\" target=\"viewer\">rotate</a></li>\n\t\t\t\t\t\t<li><a href=\"pages/TSL.html#rotateUV\" target=\"viewer\">rotateUV</a></li>\n\t\t\t\t\t\t<li><a href=\"pages/TSL.html#roughness\" target=\"viewer\">roughness</a></li>\n\t\t\t\t\t\t<li><a href=\"pages/TSL.html#round\" target=\"viewer\">round</a></li>\n\t\t\t\t\t\t<li><a href=\"pages/TSL.html#rtt\" target=\"viewer\">rtt</a></li>\n\t\t\t\t\t\t<li><a href=\"pages/TSL.html#sRGBTransferEOTF\" target=\"viewer\">sRGBTransferEOTF</a></li>\n\t\t\t\t\t\t<li><a href=\"pages/TSL.html#sRGBTransferOETF\" target=\"viewer\">sRGBTransferOETF</a></li>\n\t\t\t\t\t\t<li><a href=\"pages/TSL.html#sampleBilinearTap\" target=\"viewer\">sampleBilinearTap</a></li>\n\t\t\t\t\t\t<li><a href=\"pages/TSL.html#sampleHistory4Tap\" target=\"viewer\">sampleHistory4Tap</a></li>\n\t\t\t\t\t\t<li><a href=\"pages/TSL.html#sampler\" target=\"viewer\">sampler</a></li>\n\t\t\t\t\t\t<li><a href=\"pages/TSL.html#samplerComparison\" target=\"viewer\">samplerComparison</a></li>\n\t\t\t\t\t\t<li><a href=\"pages/TSL.html#saturate\" target=\"viewer\">saturate</a></li>\n\t\t\t\t\t\t<li><a href=\"pages/TSL.html#saturation\" target=\"viewer\">saturation</a></li>\n\t\t\t\t\t\t<li><a href=\"pages/TSL.html#scanlines\" target=\"viewer\">scanlines</a></li>\n\t\t\t\t\t\t<li><a href=\"pages/TSL.html#screenCoordinate\" target=\"viewer\">screenCoordinate</a></li>\n\t\t\t\t\t\t<li><a href=\"pages/TSL.html#screenDPR\" target=\"viewer\">screenDPR</a></li>\n\t\t\t\t\t\t<li><a href=\"pages/TSL.html#screenSize\" target=\"viewer\">screenSize</a></li>\n\t\t\t\t\t\t<li><a href=\"pages/TSL.html#screenUV\" target=\"viewer\">screenUV</a></li>\n\t\t\t\t\t\t<li><a href=\"pages/TSL.html#select\" target=\"viewer\">select</a></li>\n\t\t\t\t\t\t<li><a href=\"pages/TSL.html#sepia\" target=\"viewer\">sepia</a></li>\n\t\t\t\t\t\t<li><a href=\"pages/TSL.html#setName\" target=\"viewer\">setName</a></li>\n\t\t\t\t\t\t<li><a href=\"pages/TSL.html#shadow\" target=\"viewer\">shadow</a></li>\n\t\t\t\t\t\t<li><a href=\"pages/TSL.html#shadowPositionWorld\" target=\"viewer\">shadowPositionWorld</a></li>\n\t\t\t\t\t\t<li><a href=\"pages/TSL.html#shapeCircle\" target=\"viewer\">shapeCircle</a></li>\n\t\t\t\t\t\t<li><a href=\"pages/TSL.html#sharedUniformGroup\" target=\"viewer\">sharedUniformGroup</a></li>\n\t\t\t\t\t\t<li><a href=\"pages/TSL.html#sharpen\" target=\"viewer\">sharpen</a></li>\n\t\t\t\t\t\t<li><a href=\"pages/TSL.html#sheen\" target=\"viewer\">sheen</a></li>\n\t\t\t\t\t\t<li><a href=\"pages/TSL.html#sheenRoughness\" target=\"viewer\">sheenRoughness</a></li>\n\t\t\t\t\t\t<li><a href=\"pages/TSL.html#shiftLeft\" target=\"viewer\">shiftLeft</a></li>\n\t\t\t\t\t\t<li><a href=\"pages/TSL.html#shiftRight\" target=\"viewer\">shiftRight</a></li>\n\t\t\t\t\t\t<li><a href=\"pages/TSL.html#shininess\" target=\"viewer\">shininess</a></li>\n\t\t\t\t\t\t<li><a href=\"pages/TSL.html#sign\" target=\"viewer\">sign</a></li>\n\t\t\t\t\t\t<li><a href=\"pages/TSL.html#sin\" target=\"viewer\">sin</a></li>\n\t\t\t\t\t\t<li><a href=\"pages/TSL.html#sinc\" target=\"viewer\">sinc</a></li>\n\t\t\t\t\t\t<li><a href=\"pages/TSL.html#sinh\" target=\"viewer\">sinh</a></li>\n\t\t\t\t\t\t<li><a href=\"pages/TSL.html#skinning\" target=\"viewer\">skinning</a></li>\n\t\t\t\t\t\t<li><a href=\"pages/TSL.html#smaa\" target=\"viewer\">smaa</a></li>\n\t\t\t\t\t\t<li><a href=\"pages/TSL.html#smoothstep\" target=\"viewer\">smoothstep</a></li>\n\t\t\t\t\t\t<li><a href=\"pages/TSL.html#smoothstepElement\" target=\"viewer\">smoothstepElement</a></li>\n\t\t\t\t\t\t<li><a href=\"pages/TSL.html#snoise\" target=\"viewer\">snoise</a></li>\n\t\t\t\t\t\t<li><a href=\"pages/TSL.html#snoiseVec3\" target=\"viewer\">snoiseVec3</a></li>\n\t\t\t\t\t\t<li><a href=\"pages/TSL.html#sobel\" target=\"viewer\">sobel</a></li>\n\t\t\t\t\t\t<li><a href=\"pages/TSL.html#specularColor\" target=\"viewer\">specularColor</a></li>\n\t\t\t\t\t\t<li><a href=\"pages/TSL.html#specularColorBlended\" target=\"viewer\">specularColorBlended</a></li>\n\t\t\t\t\t\t<li><a href=\"pages/TSL.html#specularF90\" target=\"viewer\">specularF90</a></li>\n\t\t\t\t\t\t<li><a href=\"pages/TSL.html#specularLobeTanHalfAngle\" target=\"viewer\">specularLobeTanHalfAngle</a></li>\n\t\t\t\t\t\t<li><a href=\"pages/TSL.html#spherizeUV\" target=\"viewer\">spherizeUV</a></li>\n\t\t\t\t\t\t<li><a href=\"pages/TSL.html#spritesheetUV\" target=\"viewer\">spritesheetUV</a></li>\n\t\t\t\t\t\t<li><a href=\"pages/TSL.html#sqrt\" target=\"viewer\">sqrt</a></li>\n\t\t\t\t\t\t<li><a href=\"pages/TSL.html#ssaaPass\" target=\"viewer\">ssaaPass</a></li>\n\t\t\t\t\t\t<li><a href=\"pages/TSL.html#ssgi\" target=\"viewer\">ssgi</a></li>\n\t\t\t\t\t\t<li><a href=\"pages/TSL.html#ssr\" target=\"viewer\">ssr</a></li>\n\t\t\t\t\t\t<li><a href=\"pages/TSL.html#sss\" target=\"viewer\">sss</a></li>\n\t\t\t\t\t\t<li><a href=\"pages/TSL.html#stack\" target=\"viewer\">stack</a></li>\n\t\t\t\t\t\t<li><a href=\"pages/TSL.html#step\" target=\"viewer\">step</a></li>\n\t\t\t\t\t\t<li><a href=\"pages/TSL.html#stepElement\" target=\"viewer\">stepElement</a></li>\n\t\t\t\t\t\t<li><a href=\"pages/TSL.html#stereoPass\" target=\"viewer\">stereoPass</a></li>\n\t\t\t\t\t\t<li><a href=\"pages/TSL.html#storage\" target=\"viewer\">storage</a></li>\n\t\t\t\t\t\t<li><a href=\"pages/TSL.html#storageBarrier\" target=\"viewer\">storageBarrier</a></li>\n\t\t\t\t\t\t<li><a href=\"pages/TSL.html#storageElement\" target=\"viewer\">storageElement</a></li>\n\t\t\t\t\t\t<li><a href=\"pages/TSL.html#storageTexture\" target=\"viewer\">storageTexture</a></li>\n\t\t\t\t\t\t<li><a href=\"pages/TSL.html#storageTexture3D\" target=\"viewer\">storageTexture3D</a></li>\n\t\t\t\t\t\t<li><a href=\"pages/TSL.html#struct\" target=\"viewer\">struct</a></li>\n\t\t\t\t\t\t<li><a href=\"pages/TSL.html#sub\" target=\"viewer\">sub</a></li>\n\t\t\t\t\t\t<li><a href=\"pages/TSL.html#subBuild\" target=\"viewer\">subBuild</a></li>\n\t\t\t\t\t\t<li><a href=\"pages/TSL.html#subgroupAdd\" target=\"viewer\">subgroupAdd</a></li>\n\t\t\t\t\t\t<li><a href=\"pages/TSL.html#subgroupAll\" target=\"viewer\">subgroupAll</a></li>\n\t\t\t\t\t\t<li><a href=\"pages/TSL.html#subgroupAnd\" target=\"viewer\">subgroupAnd</a></li>\n\t\t\t\t\t\t<li><a href=\"pages/TSL.html#subgroupAny\" target=\"viewer\">subgroupAny</a></li>\n\t\t\t\t\t\t<li><a href=\"pages/TSL.html#subgroupBallot\" target=\"viewer\">subgroupBallot</a></li>\n\t\t\t\t\t\t<li><a href=\"pages/TSL.html#subgroupBroadcast\" target=\"viewer\">subgroupBroadcast</a></li>\n\t\t\t\t\t\t<li><a href=\"pages/TSL.html#subgroupBroadcastFirst\" target=\"viewer\">subgroupBroadcastFirst</a></li>\n\t\t\t\t\t\t<li><a href=\"pages/TSL.html#subgroupElect\" target=\"viewer\">subgroupElect</a></li>\n\t\t\t\t\t\t<li><a href=\"pages/TSL.html#subgroupExclusiveAdd\" target=\"viewer\">subgroupExclusiveAdd</a></li>\n\t\t\t\t\t\t<li><a href=\"pages/TSL.html#subgroupExclusiveMul\" target=\"viewer\">subgroupExclusiveMul</a></li>\n\t\t\t\t\t\t<li><a href=\"pages/TSL.html#subgroupInclusiveAdd\" target=\"viewer\">subgroupInclusiveAdd</a></li>\n\t\t\t\t\t\t<li><a href=\"pages/TSL.html#subgroupInclusiveMul\" target=\"viewer\">subgroupInclusiveMul</a></li>\n\t\t\t\t\t\t<li><a href=\"pages/TSL.html#subgroupIndex\" target=\"viewer\">subgroupIndex</a></li>\n\t\t\t\t\t\t<li><a href=\"pages/TSL.html#subgroupMax\" target=\"viewer\">subgroupMax</a></li>\n\t\t\t\t\t\t<li><a href=\"pages/TSL.html#subgroupMin\" target=\"viewer\">subgroupMin</a></li>\n\t\t\t\t\t\t<li><a href=\"pages/TSL.html#subgroupMul\" target=\"viewer\">subgroupMul</a></li>\n\t\t\t\t\t\t<li><a href=\"pages/TSL.html#subgroupOr\" target=\"viewer\">subgroupOr</a></li>\n\t\t\t\t\t\t<li><a href=\"pages/TSL.html#subgroupShuffle\" target=\"viewer\">subgroupShuffle</a></li>\n\t\t\t\t\t\t<li><a href=\"pages/TSL.html#subgroupShuffleDown\" target=\"viewer\">subgroupShuffleDown</a></li>\n\t\t\t\t\t\t<li><a href=\"pages/TSL.html#subgroupShuffleUp\" target=\"viewer\">subgroupShuffleUp</a></li>\n\t\t\t\t\t\t<li><a href=\"pages/TSL.html#subgroupShuffleXor\" target=\"viewer\">subgroupShuffleXor</a></li>\n\t\t\t\t\t\t<li><a href=\"pages/TSL.html#subgroupSize\" target=\"viewer\">subgroupSize</a></li>\n\t\t\t\t\t\t<li><a href=\"pages/TSL.html#subgroupXor\" target=\"viewer\">subgroupXor</a></li>\n\t\t\t\t\t\t<li><a href=\"pages/TSL.html#taau\" target=\"viewer\">taau</a></li>\n\t\t\t\t\t\t<li><a href=\"pages/TSL.html#tan\" target=\"viewer\">tan</a></li>\n\t\t\t\t\t\t<li><a href=\"pages/TSL.html#tangentGeometry\" target=\"viewer\">tangentGeometry</a></li>\n\t\t\t\t\t\t<li><a href=\"pages/TSL.html#tangentLocal\" target=\"viewer\">tangentLocal</a></li>\n\t\t\t\t\t\t<li><a href=\"pages/TSL.html#tangentView\" target=\"viewer\">tangentView</a></li>\n\t\t\t\t\t\t<li><a href=\"pages/TSL.html#tangentViewFrame\" target=\"viewer\">tangentViewFrame</a></li>\n\t\t\t\t\t\t<li><a href=\"pages/TSL.html#tangentWorld\" target=\"viewer\">tangentWorld</a></li>\n\t\t\t\t\t\t<li><a href=\"pages/TSL.html#tanh\" target=\"viewer\">tanh</a></li>\n\t\t\t\t\t\t<li><a href=\"pages/TSL.html#texture\" target=\"viewer\">texture</a></li>\n\t\t\t\t\t\t<li><a href=\"pages/TSL.html#texture3D\" target=\"viewer\">texture3D</a></li>\n\t\t\t\t\t\t<li><a href=\"pages/TSL.html#texture3DLevel\" target=\"viewer\">texture3DLevel</a></li>\n\t\t\t\t\t\t<li><a href=\"pages/TSL.html#texture3DLoad\" target=\"viewer\">texture3DLoad</a></li>\n\t\t\t\t\t\t<li><a href=\"pages/TSL.html#textureBarrier\" target=\"viewer\">textureBarrier</a></li>\n\t\t\t\t\t\t<li><a href=\"pages/TSL.html#textureBase\" target=\"viewer\">textureBase</a></li>\n\t\t\t\t\t\t<li><a href=\"pages/TSL.html#textureBicubic\" target=\"viewer\">textureBicubic</a></li>\n\t\t\t\t\t\t<li><a href=\"pages/TSL.html#textureBicubicLevel\" target=\"viewer\">textureBicubicLevel</a></li>\n\t\t\t\t\t\t<li><a href=\"pages/TSL.html#textureLoad\" target=\"viewer\">textureLoad</a></li>\n\t\t\t\t\t\t<li><a href=\"pages/TSL.html#textureSize\" target=\"viewer\">textureSize</a></li>\n\t\t\t\t\t\t<li><a href=\"pages/TSL.html#textureStore\" target=\"viewer\">textureStore</a></li>\n\t\t\t\t\t\t<li><a href=\"pages/TSL.html#thickness\" target=\"viewer\">thickness</a></li>\n\t\t\t\t\t\t<li><a href=\"pages/TSL.html#time\" target=\"viewer\">time</a></li>\n\t\t\t\t\t\t<li><a href=\"pages/TSL.html#toneMapping\" target=\"viewer\">toneMapping</a></li>\n\t\t\t\t\t\t<li><a href=\"pages/TSL.html#toneMappingExposure\" target=\"viewer\">toneMappingExposure</a></li>\n\t\t\t\t\t\t<li><a href=\"pages/TSL.html#toonOutlinePass\" target=\"viewer\">toonOutlinePass</a></li>\n\t\t\t\t\t\t<li><a href=\"pages/TSL.html#traa\" target=\"viewer\">traa</a></li>\n\t\t\t\t\t\t<li><a href=\"pages/TSL.html#transformDirection\" target=\"viewer\">transformDirection</a></li>\n\t\t\t\t\t\t<li><a href=\"pages/TSL.html#transformNormal\" target=\"viewer\">transformNormal</a></li>\n\t\t\t\t\t\t<li><a href=\"pages/TSL.html#transformNormalByInverseViewMatrix\" target=\"viewer\">transformNormalByInverseViewMatrix</a></li>\n\t\t\t\t\t\t<li><a href=\"pages/TSL.html#transformNormalByViewMatrix\" target=\"viewer\">transformNormalByViewMatrix</a></li>\n\t\t\t\t\t\t<li><a href=\"pages/TSL.html#transformNormalToView\" target=\"viewer\">transformNormalToView</a></li>\n\t\t\t\t\t\t<li><a href=\"pages/TSL.html#transformedClearcoatNormalView\" target=\"viewer\">transformedClearcoatNormalView</a></li>\n\t\t\t\t\t\t<li><a href=\"pages/TSL.html#transformedNormalView\" target=\"viewer\">transformedNormalView</a></li>\n\t\t\t\t\t\t<li><a href=\"pages/TSL.html#transformedNormalWorld\" target=\"viewer\">transformedNormalWorld</a></li>\n\t\t\t\t\t\t<li><a href=\"pages/TSL.html#transition\" target=\"viewer\">transition</a></li>\n\t\t\t\t\t\t<li><a href=\"pages/TSL.html#transmission\" target=\"viewer\">transmission</a></li>\n\t\t\t\t\t\t<li><a href=\"pages/TSL.html#transpose\" target=\"viewer\">transpose</a></li>\n\t\t\t\t\t\t<li><a href=\"pages/TSL.html#triNoise3D\" target=\"viewer\">triNoise3D</a></li>\n\t\t\t\t\t\t<li><a href=\"pages/TSL.html#triplanarTexture\" target=\"viewer\">triplanarTexture</a></li>\n\t\t\t\t\t\t<li><a href=\"pages/TSL.html#triplanarTextures\" target=\"viewer\">triplanarTextures</a></li>\n\t\t\t\t\t\t<li><a href=\"pages/TSL.html#trunc\" target=\"viewer\">trunc</a></li>\n\t\t\t\t\t\t<li><a href=\"pages/TSL.html#uintBitsToFloat\" target=\"viewer\">uintBitsToFloat</a></li>\n\t\t\t\t\t\t<li><a href=\"pages/TSL.html#uniform\" target=\"viewer\">uniform</a></li>\n\t\t\t\t\t\t<li><a href=\"pages/TSL.html#uniformArray\" target=\"viewer\">uniformArray</a></li>\n\t\t\t\t\t\t<li><a href=\"pages/TSL.html#uniformCubeTexture\" target=\"viewer\">uniformCubeTexture</a></li>\n\t\t\t\t\t\t<li><a href=\"pages/TSL.html#uniformFlow\" target=\"viewer\">uniformFlow</a></li>\n\t\t\t\t\t\t<li><a href=\"pages/TSL.html#uniformGroup\" target=\"viewer\">uniformGroup</a></li>\n\t\t\t\t\t\t<li><a href=\"pages/TSL.html#uniformTexture\" target=\"viewer\">uniformTexture</a></li>\n\t\t\t\t\t\t<li><a href=\"pages/TSL.html#unpackHalf2x16\" target=\"viewer\">unpackHalf2x16</a></li>\n\t\t\t\t\t\t<li><a href=\"pages/TSL.html#unpackNormal\" target=\"viewer\">unpackNormal</a></li>\n\t\t\t\t\t\t<li><a href=\"pages/TSL.html#unpackRGBToNormal\" target=\"viewer\">unpackRGBToNormal</a></li>\n\t\t\t\t\t\t<li><a href=\"pages/TSL.html#unpackSnorm2x16\" target=\"viewer\">unpackSnorm2x16</a></li>\n\t\t\t\t\t\t<li><a href=\"pages/TSL.html#unpackUnorm2x16\" target=\"viewer\">unpackUnorm2x16</a></li>\n\t\t\t\t\t\t<li><a href=\"pages/TSL.html#unpremultiplyAlpha\" target=\"viewer\">unpremultiplyAlpha</a></li>\n\t\t\t\t\t\t<li><a href=\"pages/TSL.html#userData\" target=\"viewer\">userData</a></li>\n\t\t\t\t\t\t<li><a href=\"pages/TSL.html#uv\" target=\"viewer\">uv</a></li>\n\t\t\t\t\t\t<li><a href=\"pages/TSL.html#varying\" target=\"viewer\">varying</a></li>\n\t\t\t\t\t\t<li><a href=\"pages/TSL.html#varyingProperty\" target=\"viewer\">varyingProperty</a></li>\n\t\t\t\t\t\t<li><a href=\"pages/TSL.html#velocity\" target=\"viewer\">velocity</a></li>\n\t\t\t\t\t\t<li><a href=\"pages/TSL.html#velocityToUVOffset\" target=\"viewer\">velocityToUVOffset</a></li>\n\t\t\t\t\t\t<li><a href=\"pages/TSL.html#vertexColor\" target=\"viewer\">vertexColor</a></li>\n\t\t\t\t\t\t<li><a href=\"pages/TSL.html#vertexIndex\" target=\"viewer\">vertexIndex</a></li>\n\t\t\t\t\t\t<li><a href=\"pages/TSL.html#vertexStage\" target=\"viewer\">vertexStage</a></li>\n\t\t\t\t\t\t<li><a href=\"pages/TSL.html#vibrance\" target=\"viewer\">vibrance</a></li>\n\t\t\t\t\t\t<li><a href=\"pages/TSL.html#viewZToLogarithmicDepth\" target=\"viewer\">viewZToLogarithmicDepth</a></li>\n\t\t\t\t\t\t<li><a href=\"pages/TSL.html#viewZToOrthographicDepth\" target=\"viewer\">viewZToOrthographicDepth</a></li>\n\t\t\t\t\t\t<li><a href=\"pages/TSL.html#viewZToPerspectiveDepth\" target=\"viewer\">viewZToPerspectiveDepth</a></li>\n\t\t\t\t\t\t<li><a href=\"pages/TSL.html#viewZToReversedOrthographicDepth\" target=\"viewer\">viewZToReversedOrthographicDepth</a></li>\n\t\t\t\t\t\t<li><a href=\"pages/TSL.html#viewZToReversedPerspectiveDepth\" target=\"viewer\">viewZToReversedPerspectiveDepth</a></li>\n\t\t\t\t\t\t<li><a href=\"pages/TSL.html#viewport\" target=\"viewer\">viewport</a></li>\n\t\t\t\t\t\t<li><a href=\"pages/TSL.html#viewportCoordinate\" target=\"viewer\">viewportCoordinate</a></li>\n\t\t\t\t\t\t<li><a href=\"pages/TSL.html#viewportDepthTexture\" target=\"viewer\">viewportDepthTexture</a></li>\n\t\t\t\t\t\t<li><a href=\"pages/TSL.html#viewportLinearDepth\" target=\"viewer\">viewportLinearDepth</a></li>\n\t\t\t\t\t\t<li><a href=\"pages/TSL.html#viewportMipTexture\" target=\"viewer\">viewportMipTexture</a></li>\n\t\t\t\t\t\t<li><a href=\"pages/TSL.html#viewportOpaqueMipTexture\" target=\"viewer\">viewportOpaqueMipTexture</a></li>\n\t\t\t\t\t\t<li><a href=\"pages/TSL.html#viewportSafeUV\" target=\"viewer\">viewportSafeUV</a></li>\n\t\t\t\t\t\t<li><a href=\"pages/TSL.html#viewportSharedTexture\" target=\"viewer\">viewportSharedTexture</a></li>\n\t\t\t\t\t\t<li><a href=\"pages/TSL.html#viewportSize\" target=\"viewer\">viewportSize</a></li>\n\t\t\t\t\t\t<li><a href=\"pages/TSL.html#viewportTexture\" target=\"viewer\">viewportTexture</a></li>\n\t\t\t\t\t\t<li><a href=\"pages/TSL.html#viewportUV\" target=\"viewer\">viewportUV</a></li>\n\t\t\t\t\t\t<li><a href=\"pages/TSL.html#vignette\" target=\"viewer\">vignette</a></li>\n\t\t\t\t\t\t<li><a href=\"pages/TSL.html#vogelDisk\" target=\"viewer\">vogelDisk</a></li>\n\t\t\t\t\t\t<li><a href=\"pages/TSL.html#vogelDiskSample\" target=\"viewer\">vogelDiskSample</a></li>\n\t\t\t\t\t\t<li><a href=\"pages/TSL.html#wgsl\" target=\"viewer\">wgsl</a></li>\n\t\t\t\t\t\t<li><a href=\"pages/TSL.html#workgroupArray\" target=\"viewer\">workgroupArray</a></li>\n\t\t\t\t\t\t<li><a href=\"pages/TSL.html#workgroupBarrier\" target=\"viewer\">workgroupBarrier</a></li>\n\t\t\t\t\t\t<li><a href=\"pages/TSL.html#workgroupId\" target=\"viewer\">workgroupId</a></li>\n\t\t\t\t\t\t<li><a href=\"pages/TSL.html#workingToColorSpace\" target=\"viewer\">workingToColorSpace</a></li>\n\t\t\t\t\t\t<li><a href=\"pages/TSL.html#xor\" target=\"viewer\">xor</a></li>\n\t\t\t\t\t</ul>\n\t\t\t\t\t<h2>Global</h2>\n\t\t\t\t\t<ul>\n\t\t\t\t\t\t<li><a href=\"pages/global.html#ACESFilmicToneMapping\" target=\"viewer\">ACESFilmicToneMapping</a></li>\n\t\t\t\t\t\t<li><a href=\"pages/global.html#AddEquation\" target=\"viewer\">AddEquation</a></li>\n\t\t\t\t\t\t<li><a href=\"pages/global.html#AddOperation\" target=\"viewer\">AddOperation</a></li>\n\t\t\t\t\t\t<li><a href=\"pages/global.html#AdditiveAnimationBlendMode\" target=\"viewer\">AdditiveAnimationBlendMode</a></li>\n\t\t\t\t\t\t<li><a href=\"pages/global.html#AdditiveBlending\" target=\"viewer\">AdditiveBlending</a></li>\n\t\t\t\t\t\t<li><a href=\"pages/global.html#AgXToneMapping\" target=\"viewer\">AgXToneMapping</a></li>\n\t\t\t\t\t\t<li><a href=\"pages/global.html#AlphaFormat\" target=\"viewer\">AlphaFormat</a></li>\n\t\t\t\t\t\t<li><a href=\"pages/global.html#AlwaysCompare\" target=\"viewer\">AlwaysCompare</a></li>\n\t\t\t\t\t\t<li><a href=\"pages/global.html#AlwaysDepth\" target=\"viewer\">AlwaysDepth</a></li>\n\t\t\t\t\t\t<li><a href=\"pages/global.html#AlwaysStencilFunc\" target=\"viewer\">AlwaysStencilFunc</a></li>\n\t\t\t\t\t\t<li><a href=\"pages/global.html#AnaglyphAlgorithm\" target=\"viewer\">AnaglyphAlgorithm</a></li>\n\t\t\t\t\t\t<li><a href=\"pages/global.html#AnaglyphColorMode\" target=\"viewer\">AnaglyphColorMode</a></li>\n\t\t\t\t\t\t<li><a href=\"pages/global.html#AttachedBindMode\" target=\"viewer\">AttachedBindMode</a></li>\n\t\t\t\t\t\t<li><a href=\"pages/global.html#BackSide\" target=\"viewer\">BackSide</a></li>\n\t\t\t\t\t\t<li><a href=\"pages/global.html#BasicDepthPacking\" target=\"viewer\">BasicDepthPacking</a></li>\n\t\t\t\t\t\t<li><a href=\"pages/global.html#BasicShadowFilter\" target=\"viewer\">BasicShadowFilter</a></li>\n\t\t\t\t\t\t<li><a href=\"pages/global.html#BasicShadowMap\" target=\"viewer\">BasicShadowMap</a></li>\n\t\t\t\t\t\t<li><a href=\"pages/global.html#ByteType\" target=\"viewer\">ByteType</a></li>\n\t\t\t\t\t\t<li><a href=\"pages/global.html#CineonToneMapping\" target=\"viewer\">CineonToneMapping</a></li>\n\t\t\t\t\t\t<li><a href=\"pages/global.html#ClampToEdgeWrapping\" target=\"viewer\">ClampToEdgeWrapping</a></li>\n\t\t\t\t\t\t<li><a href=\"pages/global.html#Compatibility\" target=\"viewer\">Compatibility</a></li>\n\t\t\t\t\t\t<li><a href=\"pages/global.html#ConstantAlphaFactor\" target=\"viewer\">ConstantAlphaFactor</a></li>\n\t\t\t\t\t\t<li><a href=\"pages/global.html#ConstantColorFactor\" target=\"viewer\">ConstantColorFactor</a></li>\n\t\t\t\t\t\t<li><a href=\"pages/global.html#CubeReflectionMapping\" target=\"viewer\">CubeReflectionMapping</a></li>\n\t\t\t\t\t\t<li><a href=\"pages/global.html#CubeRefractionMapping\" target=\"viewer\">CubeRefractionMapping</a></li>\n\t\t\t\t\t\t<li><a href=\"pages/global.html#CubeUVReflectionMapping\" target=\"viewer\">CubeUVReflectionMapping</a></li>\n\t\t\t\t\t\t<li><a href=\"pages/global.html#CullFaceBack\" target=\"viewer\">CullFaceBack</a></li>\n\t\t\t\t\t\t<li><a href=\"pages/global.html#CullFaceFront\" target=\"viewer\">CullFaceFront</a></li>\n\t\t\t\t\t\t<li><a href=\"pages/global.html#CullFaceFrontBack\" target=\"viewer\">CullFaceFrontBack</a></li>\n\t\t\t\t\t\t<li><a href=\"pages/global.html#CullFaceNone\" target=\"viewer\">CullFaceNone</a></li>\n\t\t\t\t\t\t<li><a href=\"pages/global.html#CustomBlending\" target=\"viewer\">CustomBlending</a></li>\n\t\t\t\t\t\t<li><a href=\"pages/global.html#CustomToneMapping\" target=\"viewer\">CustomToneMapping</a></li>\n\t\t\t\t\t\t<li><a href=\"pages/global.html#DATA\" target=\"viewer\">DATA</a></li>\n\t\t\t\t\t\t<li><a href=\"pages/global.html#DecrementStencilOp\" target=\"viewer\">DecrementStencilOp</a></li>\n\t\t\t\t\t\t<li><a href=\"pages/global.html#DecrementWrapStencilOp\" target=\"viewer\">DecrementWrapStencilOp</a></li>\n\t\t\t\t\t\t<li><a href=\"pages/global.html#DefaultLoadingManager\" target=\"viewer\">DefaultLoadingManager</a></li>\n\t\t\t\t\t\t<li><a href=\"pages/global.html#DepthFormat\" target=\"viewer\">DepthFormat</a></li>\n\t\t\t\t\t\t<li><a href=\"pages/global.html#DepthStencilFormat\" target=\"viewer\">DepthStencilFormat</a></li>\n\t\t\t\t\t\t<li><a href=\"pages/global.html#DetachedBindMode\" target=\"viewer\">DetachedBindMode</a></li>\n\t\t\t\t\t\t<li><a href=\"pages/global.html#DoubleSide\" target=\"viewer\">DoubleSide</a></li>\n\t\t\t\t\t\t<li><a href=\"pages/global.html#DstAlphaFactor\" target=\"viewer\">DstAlphaFactor</a></li>\n\t\t\t\t\t\t<li><a href=\"pages/global.html#DstColorFactor\" target=\"viewer\">DstColorFactor</a></li>\n\t\t\t\t\t\t<li><a href=\"pages/global.html#DynamicCopyUsage\" target=\"viewer\">DynamicCopyUsage</a></li>\n\t\t\t\t\t\t<li><a href=\"pages/global.html#DynamicDrawUsage\" target=\"viewer\">DynamicDrawUsage</a></li>\n\t\t\t\t\t\t<li><a href=\"pages/global.html#DynamicReadUsage\" target=\"viewer\">DynamicReadUsage</a></li>\n\t\t\t\t\t\t<li><a href=\"pages/global.html#ENV_RAY_LENGTH\" target=\"viewer\">ENV_RAY_LENGTH</a></li>\n\t\t\t\t\t\t<li><a href=\"pages/global.html#ENV_RAY_LENGTH_THRESHOLD\" target=\"viewer\">ENV_RAY_LENGTH_THRESHOLD</a></li>\n\t\t\t\t\t\t<li><a href=\"pages/global.html#EqualCompare\" target=\"viewer\">EqualCompare</a></li>\n\t\t\t\t\t\t<li><a href=\"pages/global.html#EqualDepth\" target=\"viewer\">EqualDepth</a></li>\n\t\t\t\t\t\t<li><a href=\"pages/global.html#EqualStencilFunc\" target=\"viewer\">EqualStencilFunc</a></li>\n\t\t\t\t\t\t<li><a href=\"pages/global.html#EquirectangularReflectionMapping\" target=\"viewer\">EquirectangularReflectionMapping</a></li>\n\t\t\t\t\t\t<li><a href=\"pages/global.html#EquirectangularRefractionMapping\" target=\"viewer\">EquirectangularRefractionMapping</a></li>\n\t\t\t\t\t\t<li><a href=\"pages/global.html#F_Schlick\" target=\"viewer\">F_Schlick</a></li>\n\t\t\t\t\t\t<li><a href=\"pages/global.html#FloatType\" target=\"viewer\">FloatType</a></li>\n\t\t\t\t\t\t<li><a href=\"pages/global.html#FrontSide\" target=\"viewer\">FrontSide</a></li>\n\t\t\t\t\t\t<li><a href=\"pages/global.html#GLSL1\" target=\"viewer\">GLSL1</a></li>\n\t\t\t\t\t\t<li><a href=\"pages/global.html#GLSL3\" target=\"viewer\">GLSL3</a></li>\n\t\t\t\t\t\t<li><a href=\"pages/global.html#GreaterCompare\" target=\"viewer\">GreaterCompare</a></li>\n\t\t\t\t\t\t<li><a href=\"pages/global.html#GreaterDepth\" target=\"viewer\">GreaterDepth</a></li>\n\t\t\t\t\t\t<li><a href=\"pages/global.html#GreaterEqualCompare\" target=\"viewer\">GreaterEqualCompare</a></li>\n\t\t\t\t\t\t<li><a href=\"pages/global.html#GreaterEqualDepth\" target=\"viewer\">GreaterEqualDepth</a></li>\n\t\t\t\t\t\t<li><a href=\"pages/global.html#GreaterEqualStencilFunc\" target=\"viewer\">GreaterEqualStencilFunc</a></li>\n\t\t\t\t\t\t<li><a href=\"pages/global.html#GreaterStencilFunc\" target=\"viewer\">GreaterStencilFunc</a></li>\n\t\t\t\t\t\t<li><a href=\"pages/global.html#HalfFloatType\" target=\"viewer\">HalfFloatType</a></li>\n\t\t\t\t\t\t<li><a href=\"pages/global.html#IncrementStencilOp\" target=\"viewer\">IncrementStencilOp</a></li>\n\t\t\t\t\t\t<li><a href=\"pages/global.html#IncrementWrapStencilOp\" target=\"viewer\">IncrementWrapStencilOp</a></li>\n\t\t\t\t\t\t<li><a href=\"pages/global.html#IntType\" target=\"viewer\">IntType</a></li>\n\t\t\t\t\t\t<li><a href=\"pages/global.html#InterpolateBezier\" target=\"viewer\">InterpolateBezier</a></li>\n\t\t\t\t\t\t<li><a href=\"pages/global.html#InterpolateDiscrete\" target=\"viewer\">InterpolateDiscrete</a></li>\n\t\t\t\t\t\t<li><a href=\"pages/global.html#InterpolateLinear\" target=\"viewer\">InterpolateLinear</a></li>\n\t\t\t\t\t\t<li><a href=\"pages/global.html#InterpolateSmooth\" target=\"viewer\">InterpolateSmooth</a></li>\n\t\t\t\t\t\t<li><a href=\"pages/global.html#InterpolationSamplingMode\" target=\"viewer\">InterpolationSamplingMode</a></li>\n\t\t\t\t\t\t<li><a href=\"pages/global.html#InterpolationSamplingType\" target=\"viewer\">InterpolationSamplingType</a></li>\n\t\t\t\t\t\t<li><a href=\"pages/global.html#InvertStencilOp\" target=\"viewer\">InvertStencilOp</a></li>\n\t\t\t\t\t\t<li><a href=\"pages/global.html#KHR_mesh_quantization_ExtraAttrTypes\" target=\"viewer\">KHR_mesh_quantization_ExtraAttrTypes</a></li>\n\t\t\t\t\t\t<li><a href=\"pages/global.html#KeepStencilOp\" target=\"viewer\">KeepStencilOp</a></li>\n\t\t\t\t\t\t<li><a href=\"pages/global.html#LessCompare\" target=\"viewer\">LessCompare</a></li>\n\t\t\t\t\t\t<li><a href=\"pages/global.html#LessDepth\" target=\"viewer\">LessDepth</a></li>\n\t\t\t\t\t\t<li><a href=\"pages/global.html#LessEqualCompare\" target=\"viewer\">LessEqualCompare</a></li>\n\t\t\t\t\t\t<li><a href=\"pages/global.html#LessEqualDepth\" target=\"viewer\">LessEqualDepth</a></li>\n\t\t\t\t\t\t<li><a href=\"pages/global.html#LessEqualStencilFunc\" target=\"viewer\">LessEqualStencilFunc</a></li>\n\t\t\t\t\t\t<li><a href=\"pages/global.html#LessStencilFunc\" target=\"viewer\">LessStencilFunc</a></li>\n\t\t\t\t\t\t<li><a href=\"pages/global.html#LinearFilter\" target=\"viewer\">LinearFilter</a></li>\n\t\t\t\t\t\t<li><a href=\"pages/global.html#LinearMipmapLinearFilter\" target=\"viewer\">LinearMipmapLinearFilter</a></li>\n\t\t\t\t\t\t<li><a href=\"pages/global.html#LinearMipmapNearestFilter\" target=\"viewer\">LinearMipmapNearestFilter</a></li>\n\t\t\t\t\t\t<li><a href=\"pages/global.html#LinearSRGBColorSpace\" target=\"viewer\">LinearSRGBColorSpace</a></li>\n\t\t\t\t\t\t<li><a href=\"pages/global.html#LinearToneMapping\" target=\"viewer\">LinearToneMapping</a></li>\n\t\t\t\t\t\t<li><a href=\"pages/global.html#LinearTransfer\" target=\"viewer\">LinearTransfer</a></li>\n\t\t\t\t\t\t<li><a href=\"pages/global.html#LoopOnce\" target=\"viewer\">LoopOnce</a></li>\n\t\t\t\t\t\t<li><a href=\"pages/global.html#LoopPingPong\" target=\"viewer\">LoopPingPong</a></li>\n\t\t\t\t\t\t<li><a href=\"pages/global.html#LoopRepeat\" target=\"viewer\">LoopRepeat</a></li>\n\t\t\t\t\t\t<li><a href=\"pages/global.html#MOUSE\" target=\"viewer\">MOUSE</a></li>\n\t\t\t\t\t\t<li><a href=\"pages/global.html#MaterialBlending\" target=\"viewer\">MaterialBlending</a></li>\n\t\t\t\t\t\t<li><a href=\"pages/global.html#MaxEquation\" target=\"viewer\">MaxEquation</a></li>\n\t\t\t\t\t\t<li><a href=\"pages/global.html#MinEquation\" target=\"viewer\">MinEquation</a></li>\n\t\t\t\t\t\t<li><a href=\"pages/global.html#MirroredRepeatWrapping\" target=\"viewer\">MirroredRepeatWrapping</a></li>\n\t\t\t\t\t\t<li><a href=\"pages/global.html#MixOperation\" target=\"viewer\">MixOperation</a></li>\n\t\t\t\t\t\t<li><a href=\"pages/global.html#MultiplyBlending\" target=\"viewer\">MultiplyBlending</a></li>\n\t\t\t\t\t\t<li><a href=\"pages/global.html#MultiplyOperation\" target=\"viewer\">MultiplyOperation</a></li>\n\t\t\t\t\t\t<li><a href=\"pages/global.html#NearestFilter\" target=\"viewer\">NearestFilter</a></li>\n\t\t\t\t\t\t<li><a href=\"pages/global.html#NearestMipmapLinearFilter\" target=\"viewer\">NearestMipmapLinearFilter</a></li>\n\t\t\t\t\t\t<li><a href=\"pages/global.html#NearestMipmapNearestFilter\" target=\"viewer\">NearestMipmapNearestFilter</a></li>\n\t\t\t\t\t\t<li><a href=\"pages/global.html#NeutralToneMapping\" target=\"viewer\">NeutralToneMapping</a></li>\n\t\t\t\t\t\t<li><a href=\"pages/global.html#NeverCompare\" target=\"viewer\">NeverCompare</a></li>\n\t\t\t\t\t\t<li><a href=\"pages/global.html#NeverDepth\" target=\"viewer\">NeverDepth</a></li>\n\t\t\t\t\t\t<li><a href=\"pages/global.html#NeverStencilFunc\" target=\"viewer\">NeverStencilFunc</a></li>\n\t\t\t\t\t\t<li><a href=\"pages/global.html#NoBlending\" target=\"viewer\">NoBlending</a></li>\n\t\t\t\t\t\t<li><a href=\"pages/global.html#NoColorSpace\" target=\"viewer\">NoColorSpace</a></li>\n\t\t\t\t\t\t<li><a href=\"pages/global.html#NoNormalPacking\" target=\"viewer\">NoNormalPacking</a></li>\n\t\t\t\t\t\t<li><a href=\"pages/global.html#NoToneMapping\" target=\"viewer\">NoToneMapping</a></li>\n\t\t\t\t\t\t<li><a href=\"pages/global.html#NodeAccess\" target=\"viewer\">NodeAccess</a></li>\n\t\t\t\t\t\t<li><a href=\"pages/global.html#NodeShaderStage\" target=\"viewer\">NodeShaderStage</a></li>\n\t\t\t\t\t\t<li><a href=\"pages/global.html#NodeType\" target=\"viewer\">NodeType</a></li>\n\t\t\t\t\t\t<li><a href=\"pages/global.html#NodeUpdateType\" target=\"viewer\">NodeUpdateType</a></li>\n\t\t\t\t\t\t<li><a href=\"pages/global.html#NormalAnimationBlendMode\" target=\"viewer\">NormalAnimationBlendMode</a></li>\n\t\t\t\t\t\t<li><a href=\"pages/global.html#NormalBlending\" target=\"viewer\">NormalBlending</a></li>\n\t\t\t\t\t\t<li><a href=\"pages/global.html#NormalGAPacking\" target=\"viewer\">NormalGAPacking</a></li>\n\t\t\t\t\t\t<li><a href=\"pages/global.html#NormalRGPacking\" target=\"viewer\">NormalRGPacking</a></li>\n\t\t\t\t\t\t<li><a href=\"pages/global.html#NotEqualCompare\" target=\"viewer\">NotEqualCompare</a></li>\n\t\t\t\t\t\t<li><a href=\"pages/global.html#NotEqualDepth\" target=\"viewer\">NotEqualDepth</a></li>\n\t\t\t\t\t\t<li><a href=\"pages/global.html#NotEqualStencilFunc\" target=\"viewer\">NotEqualStencilFunc</a></li>\n\t\t\t\t\t\t<li><a href=\"pages/global.html#ObjectSpaceNormalMap\" target=\"viewer\">ObjectSpaceNormalMap</a></li>\n\t\t\t\t\t\t<li><a href=\"pages/global.html#OnBeforeFrameUpdate\" target=\"viewer\">OnBeforeFrameUpdate</a></li>\n\t\t\t\t\t\t<li><a href=\"pages/global.html#OnBeforeMaterialUpdate\" target=\"viewer\">OnBeforeMaterialUpdate</a></li>\n\t\t\t\t\t\t<li><a href=\"pages/global.html#OnBeforeObjectUpdate\" target=\"viewer\">OnBeforeObjectUpdate</a></li>\n\t\t\t\t\t\t<li><a href=\"pages/global.html#OnFrameUpdate\" target=\"viewer\">OnFrameUpdate</a></li>\n\t\t\t\t\t\t<li><a href=\"pages/global.html#OnMaterialUpdate\" target=\"viewer\">OnMaterialUpdate</a></li>\n\t\t\t\t\t\t<li><a href=\"pages/global.html#OnObjectUpdate\" target=\"viewer\">OnObjectUpdate</a></li>\n\t\t\t\t\t\t<li><a href=\"pages/global.html#OneFactor\" target=\"viewer\">OneFactor</a></li>\n\t\t\t\t\t\t<li><a href=\"pages/global.html#OneMinusConstantAlphaFactor\" target=\"viewer\">OneMinusConstantAlphaFactor</a></li>\n\t\t\t\t\t\t<li><a href=\"pages/global.html#OneMinusConstantColorFactor\" target=\"viewer\">OneMinusConstantColorFactor</a></li>\n\t\t\t\t\t\t<li><a href=\"pages/global.html#OneMinusDstAlphaFactor\" target=\"viewer\">OneMinusDstAlphaFactor</a></li>\n\t\t\t\t\t\t<li><a href=\"pages/global.html#OneMinusDstColorFactor\" target=\"viewer\">OneMinusDstColorFactor</a></li>\n\t\t\t\t\t\t<li><a href=\"pages/global.html#OneMinusSrcAlphaFactor\" target=\"viewer\">OneMinusSrcAlphaFactor</a></li>\n\t\t\t\t\t\t<li><a href=\"pages/global.html#OneMinusSrcColorFactor\" target=\"viewer\">OneMinusSrcColorFactor</a></li>\n\t\t\t\t\t\t<li><a href=\"pages/global.html#PCFShadowFilter\" target=\"viewer\">PCFShadowFilter</a></li>\n\t\t\t\t\t\t<li><a href=\"pages/global.html#PCFShadowMap\" target=\"viewer\">PCFShadowMap</a></li>\n\t\t\t\t\t\t<li><a href=\"pages/global.html#PCFSoftShadowFilter\" target=\"viewer\">PCFSoftShadowFilter</a></li>\n\t\t\t\t\t\t<li><a href=\"pages/global.html#PCFSoftShadowMap\" target=\"viewer\">PCFSoftShadowMap</a></li>\n\t\t\t\t\t\t<li><a href=\"pages/global.html#PointShadowFilter\" target=\"viewer\">PointShadowFilter</a></li>\n\t\t\t\t\t\t<li><a href=\"pages/global.html#R11_EAC_Format\" target=\"viewer\">R11_EAC_Format</a></li>\n\t\t\t\t\t\t<li><a href=\"pages/global.html#RED_GREEN_RGTC2_Format\" target=\"viewer\">RED_GREEN_RGTC2_Format</a></li>\n\t\t\t\t\t\t<li><a href=\"pages/global.html#RED_RGTC1_Format\" target=\"viewer\">RED_RGTC1_Format</a></li>\n\t\t\t\t\t\t<li><a href=\"pages/global.html#RG11_EAC_Format\" target=\"viewer\">RG11_EAC_Format</a></li>\n\t\t\t\t\t\t<li><a href=\"pages/global.html#RGBADepthPacking\" target=\"viewer\">RGBADepthPacking</a></li>\n\t\t\t\t\t\t<li><a href=\"pages/global.html#RGBAFormat\" target=\"viewer\">RGBAFormat</a></li>\n\t\t\t\t\t\t<li><a href=\"pages/global.html#RGBAIntegerFormat\" target=\"viewer\">RGBAIntegerFormat</a></li>\n\t\t\t\t\t\t<li><a href=\"pages/global.html#RGBA_ASTC_10x10_Format\" target=\"viewer\">RGBA_ASTC_10x10_Format</a></li>\n\t\t\t\t\t\t<li><a href=\"pages/global.html#RGBA_ASTC_10x5_Format\" target=\"viewer\">RGBA_ASTC_10x5_Format</a></li>\n\t\t\t\t\t\t<li><a href=\"pages/global.html#RGBA_ASTC_10x6_Format\" target=\"viewer\">RGBA_ASTC_10x6_Format</a></li>\n\t\t\t\t\t\t<li><a href=\"pages/global.html#RGBA_ASTC_10x8_Format\" target=\"viewer\">RGBA_ASTC_10x8_Format</a></li>\n\t\t\t\t\t\t<li><a href=\"pages/global.html#RGBA_ASTC_12x10_Format\" target=\"viewer\">RGBA_ASTC_12x10_Format</a></li>\n\t\t\t\t\t\t<li><a href=\"pages/global.html#RGBA_ASTC_12x12_Format\" target=\"viewer\">RGBA_ASTC_12x12_Format</a></li>\n\t\t\t\t\t\t<li><a href=\"pages/global.html#RGBA_ASTC_4x4_Format\" target=\"viewer\">RGBA_ASTC_4x4_Format</a></li>\n\t\t\t\t\t\t<li><a href=\"pages/global.html#RGBA_ASTC_5x4_Format\" target=\"viewer\">RGBA_ASTC_5x4_Format</a></li>\n\t\t\t\t\t\t<li><a href=\"pages/global.html#RGBA_ASTC_5x5_Format\" target=\"viewer\">RGBA_ASTC_5x5_Format</a></li>\n\t\t\t\t\t\t<li><a href=\"pages/global.html#RGBA_ASTC_6x5_Format\" target=\"viewer\">RGBA_ASTC_6x5_Format</a></li>\n\t\t\t\t\t\t<li><a href=\"pages/global.html#RGBA_ASTC_6x6_Format\" target=\"viewer\">RGBA_ASTC_6x6_Format</a></li>\n\t\t\t\t\t\t<li><a href=\"pages/global.html#RGBA_ASTC_8x5_Format\" target=\"viewer\">RGBA_ASTC_8x5_Format</a></li>\n\t\t\t\t\t\t<li><a href=\"pages/global.html#RGBA_ASTC_8x6_Format\" target=\"viewer\">RGBA_ASTC_8x6_Format</a></li>\n\t\t\t\t\t\t<li><a href=\"pages/global.html#RGBA_ASTC_8x8_Format\" target=\"viewer\">RGBA_ASTC_8x8_Format</a></li>\n\t\t\t\t\t\t<li><a href=\"pages/global.html#RGBA_BPTC_Format\" target=\"viewer\">RGBA_BPTC_Format</a></li>\n\t\t\t\t\t\t<li><a href=\"pages/global.html#RGBA_ETC2_EAC_Format\" target=\"viewer\">RGBA_ETC2_EAC_Format</a></li>\n\t\t\t\t\t\t<li><a href=\"pages/global.html#RGBA_PVRTC_2BPPV1_Format\" target=\"viewer\">RGBA_PVRTC_2BPPV1_Format</a></li>\n\t\t\t\t\t\t<li><a href=\"pages/global.html#RGBA_PVRTC_4BPPV1_Format\" target=\"viewer\">RGBA_PVRTC_4BPPV1_Format</a></li>\n\t\t\t\t\t\t<li><a href=\"pages/global.html#RGBA_S3TC_DXT1_Format\" target=\"viewer\">RGBA_S3TC_DXT1_Format</a></li>\n\t\t\t\t\t\t<li><a href=\"pages/global.html#RGBA_S3TC_DXT3_Format\" target=\"viewer\">RGBA_S3TC_DXT3_Format</a></li>\n\t\t\t\t\t\t<li><a href=\"pages/global.html#RGBA_S3TC_DXT5_Format\" target=\"viewer\">RGBA_S3TC_DXT5_Format</a></li>\n\t\t\t\t\t\t<li><a href=\"pages/global.html#RGBDepthPacking\" target=\"viewer\">RGBDepthPacking</a></li>\n\t\t\t\t\t\t<li><a href=\"pages/global.html#RGBFormat\" target=\"viewer\">RGBFormat</a></li>\n\t\t\t\t\t\t<li><a href=\"pages/global.html#RGBIntegerFormat\" target=\"viewer\">RGBIntegerFormat</a></li>\n\t\t\t\t\t\t<li><a href=\"pages/global.html#RGB_BPTC_SIGNED_Format\" target=\"viewer\">RGB_BPTC_SIGNED_Format</a></li>\n\t\t\t\t\t\t<li><a href=\"pages/global.html#RGB_BPTC_UNSIGNED_Format\" target=\"viewer\">RGB_BPTC_UNSIGNED_Format</a></li>\n\t\t\t\t\t\t<li><a href=\"pages/global.html#RGB_ETC1_Format\" target=\"viewer\">RGB_ETC1_Format</a></li>\n\t\t\t\t\t\t<li><a href=\"pages/global.html#RGB_ETC2_Format\" target=\"viewer\">RGB_ETC2_Format</a></li>\n\t\t\t\t\t\t<li><a href=\"pages/global.html#RGB_PVRTC_2BPPV1_Format\" target=\"viewer\">RGB_PVRTC_2BPPV1_Format</a></li>\n\t\t\t\t\t\t<li><a href=\"pages/global.html#RGB_PVRTC_4BPPV1_Format\" target=\"viewer\">RGB_PVRTC_4BPPV1_Format</a></li>\n\t\t\t\t\t\t<li><a href=\"pages/global.html#RGB_S3TC_DXT1_Format\" target=\"viewer\">RGB_S3TC_DXT1_Format</a></li>\n\t\t\t\t\t\t<li><a href=\"pages/global.html#RGDepthPacking\" target=\"viewer\">RGDepthPacking</a></li>\n\t\t\t\t\t\t<li><a href=\"pages/global.html#RGFormat\" target=\"viewer\">RGFormat</a></li>\n\t\t\t\t\t\t<li><a href=\"pages/global.html#RGIntegerFormat\" target=\"viewer\">RGIntegerFormat</a></li>\n\t\t\t\t\t\t<li><a href=\"pages/global.html#RedFormat\" target=\"viewer\">RedFormat</a></li>\n\t\t\t\t\t\t<li><a href=\"pages/global.html#RedIntegerFormat\" target=\"viewer\">RedIntegerFormat</a></li>\n\t\t\t\t\t\t<li><a href=\"pages/global.html#ReinhardToneMapping\" target=\"viewer\">ReinhardToneMapping</a></li>\n\t\t\t\t\t\t<li><a href=\"pages/global.html#RepeatWrapping\" target=\"viewer\">RepeatWrapping</a></li>\n\t\t\t\t\t\t<li><a href=\"pages/global.html#ReplaceStencilOp\" target=\"viewer\">ReplaceStencilOp</a></li>\n\t\t\t\t\t\t<li><a href=\"pages/global.html#ReverseSubtractEquation\" target=\"viewer\">ReverseSubtractEquation</a></li>\n\t\t\t\t\t\t<li><a href=\"pages/global.html#SIGNED_R11_EAC_Format\" target=\"viewer\">SIGNED_R11_EAC_Format</a></li>\n\t\t\t\t\t\t<li><a href=\"pages/global.html#SIGNED_RED_GREEN_RGTC2_Format\" target=\"viewer\">SIGNED_RED_GREEN_RGTC2_Format</a></li>\n\t\t\t\t\t\t<li><a href=\"pages/global.html#SIGNED_RED_RGTC1_Format\" target=\"viewer\">SIGNED_RED_RGTC1_Format</a></li>\n\t\t\t\t\t\t<li><a href=\"pages/global.html#SIGNED_RG11_EAC_Format\" target=\"viewer\">SIGNED_RG11_EAC_Format</a></li>\n\t\t\t\t\t\t<li><a href=\"pages/global.html#SRGBColorSpace\" target=\"viewer\">SRGBColorSpace</a></li>\n\t\t\t\t\t\t<li><a href=\"pages/global.html#SRGBTransfer\" target=\"viewer\">SRGBTransfer</a></li>\n\t\t\t\t\t\t<li><a href=\"pages/global.html#SRGB_TO_LINEAR\" target=\"viewer\">SRGB_TO_LINEAR</a></li>\n\t\t\t\t\t\t<li><a href=\"pages/global.html#ShortType\" target=\"viewer\">ShortType</a></li>\n\t\t\t\t\t\t<li><a href=\"pages/global.html#SrcAlphaFactor\" target=\"viewer\">SrcAlphaFactor</a></li>\n\t\t\t\t\t\t<li><a href=\"pages/global.html#SrcAlphaSaturateFactor\" target=\"viewer\">SrcAlphaSaturateFactor</a></li>\n\t\t\t\t\t\t<li><a href=\"pages/global.html#SrcColorFactor\" target=\"viewer\">SrcColorFactor</a></li>\n\t\t\t\t\t\t<li><a href=\"pages/global.html#Stack\" target=\"viewer\">Stack</a></li>\n\t\t\t\t\t\t<li><a href=\"pages/global.html#StaticCopyUsage\" target=\"viewer\">StaticCopyUsage</a></li>\n\t\t\t\t\t\t<li><a href=\"pages/global.html#StaticDrawUsage\" target=\"viewer\">StaticDrawUsage</a></li>\n\t\t\t\t\t\t<li><a href=\"pages/global.html#StaticReadUsage\" target=\"viewer\">StaticReadUsage</a></li>\n\t\t\t\t\t\t<li><a href=\"pages/global.html#StreamCopyUsage\" target=\"viewer\">StreamCopyUsage</a></li>\n\t\t\t\t\t\t<li><a href=\"pages/global.html#StreamDrawUsage\" target=\"viewer\">StreamDrawUsage</a></li>\n\t\t\t\t\t\t<li><a href=\"pages/global.html#StreamReadUsage\" target=\"viewer\">StreamReadUsage</a></li>\n\t\t\t\t\t\t<li><a href=\"pages/global.html#SubtractEquation\" target=\"viewer\">SubtractEquation</a></li>\n\t\t\t\t\t\t<li><a href=\"pages/global.html#SubtractiveBlending\" target=\"viewer\">SubtractiveBlending</a></li>\n\t\t\t\t\t\t<li><a href=\"pages/global.html#TOUCH\" target=\"viewer\">TOUCH</a></li>\n\t\t\t\t\t\t<li><a href=\"pages/global.html#TangentSpaceNormalMap\" target=\"viewer\">TangentSpaceNormalMap</a></li>\n\t\t\t\t\t\t<li><a href=\"pages/global.html#TimestampQuery\" target=\"viewer\">TimestampQuery</a></li>\n\t\t\t\t\t\t<li><a href=\"pages/global.html#TriangleFanDrawMode\" target=\"viewer\">TriangleFanDrawMode</a></li>\n\t\t\t\t\t\t<li><a href=\"pages/global.html#TriangleStripDrawMode\" target=\"viewer\">TriangleStripDrawMode</a></li>\n\t\t\t\t\t\t<li><a href=\"pages/global.html#TrianglesDrawMode\" target=\"viewer\">TrianglesDrawMode</a></li>\n\t\t\t\t\t\t<li><a href=\"pages/global.html#UVMapping\" target=\"viewer\">UVMapping</a></li>\n\t\t\t\t\t\t<li><a href=\"pages/global.html#UnsignedByteType\" target=\"viewer\">UnsignedByteType</a></li>\n\t\t\t\t\t\t<li><a href=\"pages/global.html#UnsignedInt101111Type\" target=\"viewer\">UnsignedInt101111Type</a></li>\n\t\t\t\t\t\t<li><a href=\"pages/global.html#UnsignedInt248Type\" target=\"viewer\">UnsignedInt248Type</a></li>\n\t\t\t\t\t\t<li><a href=\"pages/global.html#UnsignedInt5999Type\" target=\"viewer\">UnsignedInt5999Type</a></li>\n\t\t\t\t\t\t<li><a href=\"pages/global.html#UnsignedIntType\" target=\"viewer\">UnsignedIntType</a></li>\n\t\t\t\t\t\t<li><a href=\"pages/global.html#UnsignedShort4444Type\" target=\"viewer\">UnsignedShort4444Type</a></li>\n\t\t\t\t\t\t<li><a href=\"pages/global.html#UnsignedShort5551Type\" target=\"viewer\">UnsignedShort5551Type</a></li>\n\t\t\t\t\t\t<li><a href=\"pages/global.html#UnsignedShortType\" target=\"viewer\">UnsignedShortType</a></li>\n\t\t\t\t\t\t<li><a href=\"pages/global.html#VK_FORMAT_MAP\" target=\"viewer\">VK_FORMAT_MAP</a></li>\n\t\t\t\t\t\t<li><a href=\"pages/global.html#VSMShadowFilter\" target=\"viewer\">VSMShadowFilter</a></li>\n\t\t\t\t\t\t<li><a href=\"pages/global.html#VSMShadowMap\" target=\"viewer\">VSMShadowMap</a></li>\n\t\t\t\t\t\t<li><a href=\"pages/global.html#WebGLCoordinateSystem\" target=\"viewer\">WebGLCoordinateSystem</a></li>\n\t\t\t\t\t\t<li><a href=\"pages/global.html#WebGPUCoordinateSystem\" target=\"viewer\">WebGPUCoordinateSystem</a></li>\n\t\t\t\t\t\t<li><a href=\"pages/global.html#WrapAroundEnding\" target=\"viewer\">WrapAroundEnding</a></li>\n\t\t\t\t\t\t<li><a href=\"pages/global.html#ZeroCurvatureEnding\" target=\"viewer\">ZeroCurvatureEnding</a></li>\n\t\t\t\t\t\t<li><a href=\"pages/global.html#ZeroFactor\" target=\"viewer\">ZeroFactor</a></li>\n\t\t\t\t\t\t<li><a href=\"pages/global.html#ZeroSlopeEnding\" target=\"viewer\">ZeroSlopeEnding</a></li>\n\t\t\t\t\t\t<li><a href=\"pages/global.html#ZeroStencilOp\" target=\"viewer\">ZeroStencilOp</a></li>\n\t\t\t\t\t\t<li><a href=\"pages/global.html#addArcade\" target=\"viewer\">addArcade</a></li>\n\t\t\t\t\t\t<li><a href=\"pages/global.html#addCornice\" target=\"viewer\">addCornice</a></li>\n\t\t\t\t\t\t<li><a href=\"pages/global.html#addParapet\" target=\"viewer\">addParapet</a></li>\n\t\t\t\t\t\t<li><a href=\"pages/global.html#addSpandrelBands\" target=\"viewer\">addSpandrelBands</a></li>\n\t\t\t\t\t\t<li><a href=\"pages/global.html#bakeGroups\" target=\"viewer\">bakeGroups</a></li>\n\t\t\t\t\t\t<li><a href=\"pages/global.html#batchColor\" target=\"viewer\">batchColor</a></li>\n\t\t\t\t\t\t<li><a href=\"pages/global.html#bilinearHistoryTap\" target=\"viewer\">bilinearHistoryTap</a></li>\n\t\t\t\t\t\t<li><a href=\"pages/global.html#bindAnalyticNoise\" target=\"viewer\">bindAnalyticNoise</a></li>\n\t\t\t\t\t\t<li><a href=\"pages/global.html#bindTemporalCameraUniforms\" target=\"viewer\">bindTemporalCameraUniforms</a></li>\n\t\t\t\t\t\t<li><a href=\"pages/global.html#buildData3DTexture\" target=\"viewer\">buildData3DTexture</a></li>\n\t\t\t\t\t\t<li><a href=\"pages/global.html#buildFaces\" target=\"viewer\">buildFaces</a></li>\n\t\t\t\t\t\t<li><a href=\"pages/global.html#buildFootprint\" target=\"viewer\">buildFootprint</a></li>\n\t\t\t\t\t\t<li><a href=\"pages/global.html#buildMesh\" target=\"viewer\">buildMesh</a></li>\n\t\t\t\t\t\t<li><a href=\"pages/global.html#buildingPalette\" target=\"viewer\">buildingPalette</a></li>\n\t\t\t\t\t\t<li><a href=\"pages/global.html#ceilPowerOfTwo\" target=\"viewer\">ceilPowerOfTwo</a></li>\n\t\t\t\t\t\t<li><a href=\"pages/global.html#closestLineToLine\" target=\"viewer\">closestLineToLine</a></li>\n\t\t\t\t\t\t<li><a href=\"pages/global.html#contain\" target=\"viewer\">contain</a></li>\n\t\t\t\t\t\t<li><a href=\"pages/global.html#convertArray\" target=\"viewer\">convertArray</a></li>\n\t\t\t\t\t\t<li><a href=\"pages/global.html#cover\" target=\"viewer\">cover</a></li>\n\t\t\t\t\t\t<li><a href=\"pages/global.html#createBuildingMaterial\" target=\"viewer\">createBuildingMaterial</a></li>\n\t\t\t\t\t\t<li><a href=\"pages/global.html#createCanvasElement\" target=\"viewer\">createCanvasElement</a></li>\n\t\t\t\t\t\t<li><a href=\"pages/global.html#createEvent\" target=\"viewer\">createEvent</a></li>\n\t\t\t\t\t\t<li><a href=\"pages/global.html#createForestMaterial\" target=\"viewer\">createForestMaterial</a></li>\n\t\t\t\t\t\t<li><a href=\"pages/global.html#createInstanceMatrixNode\" target=\"viewer\">createInstanceMatrixNode</a></li>\n\t\t\t\t\t\t<li><a href=\"pages/global.html#createRoadMaterial\" target=\"viewer\">createRoadMaterial</a></li>\n\t\t\t\t\t\t<li><a href=\"pages/global.html#createSkyscraperMaterial\" target=\"viewer\">createSkyscraperMaterial</a></li>\n\t\t\t\t\t\t<li><a href=\"pages/global.html#createTreeMaterial\" target=\"viewer\">createTreeMaterial</a></li>\n\t\t\t\t\t\t<li><a href=\"pages/global.html#damp\" target=\"viewer\">damp</a></li>\n\t\t\t\t\t\t<li><a href=\"pages/global.html#dampenForVarianceClip\" target=\"viewer\">dampenForVarianceClip</a></li>\n\t\t\t\t\t\t<li><a href=\"pages/global.html#degToRad\" target=\"viewer\">degToRad</a></li>\n\t\t\t\t\t\t<li><a href=\"pages/global.html#denormalize\" target=\"viewer\">denormalize</a></li>\n\t\t\t\t\t\t<li><a href=\"pages/global.html#depthAwareBlend\" target=\"viewer\">depthAwareBlend</a></li>\n\t\t\t\t\t\t<li><a href=\"pages/global.html#disposeShadowMaterial\" target=\"viewer\">disposeShadowMaterial</a></li>\n\t\t\t\t\t\t<li><a href=\"pages/global.html#enhanceLogMessage\" target=\"viewer\">enhanceLogMessage</a></li>\n\t\t\t\t\t\t<li><a href=\"pages/global.html#equirectUvToDir\" target=\"viewer\">equirectUvToDir</a></li>\n\t\t\t\t\t\t<li><a href=\"pages/global.html#error\" target=\"viewer\">error</a></li>\n\t\t\t\t\t\t<li><a href=\"pages/global.html#euclideanModulo\" target=\"viewer\">euclideanModulo</a></li>\n\t\t\t\t\t\t<li><a href=\"pages/global.html#fill\" target=\"viewer\">fill</a></li>\n\t\t\t\t\t\t<li><a href=\"pages/global.html#flattenJSON\" target=\"viewer\">flattenJSON</a></li>\n\t\t\t\t\t\t<li><a href=\"pages/global.html#floorPowerOfTwo\" target=\"viewer\">floorPowerOfTwo</a></li>\n\t\t\t\t\t\t<li><a href=\"pages/global.html#fromHalfFloat\" target=\"viewer\">fromHalfFloat</a></li>\n\t\t\t\t\t\t<li><a href=\"pages/global.html#generateMagicSquare\" target=\"viewer\">generateMagicSquare</a></li>\n\t\t\t\t\t\t<li><a href=\"pages/global.html#generateMagicSquareNoise\" target=\"viewer\">generateMagicSquareNoise</a></li>\n\t\t\t\t\t\t<li><a href=\"pages/global.html#generateUUID\" target=\"viewer\">generateUUID</a></li>\n\t\t\t\t\t\t<li><a href=\"pages/global.html#getBatchingColor\" target=\"viewer\">getBatchingColor</a></li>\n\t\t\t\t\t\t<li><a href=\"pages/global.html#getByteLength\" target=\"viewer\">getByteLength</a></li>\n\t\t\t\t\t\t<li><a href=\"pages/global.html#getCacheKey\" target=\"viewer\">getCacheKey</a></li>\n\t\t\t\t\t\t<li><a href=\"pages/global.html#getConsoleFunction\" target=\"viewer\">getConsoleFunction</a></li>\n\t\t\t\t\t\t<li><a href=\"pages/global.html#getDistanceAttenuation\" target=\"viewer\">getDistanceAttenuation</a></li>\n\t\t\t\t\t\t<li><a href=\"pages/global.html#getElementsByTagName\" target=\"viewer\">getElementsByTagName</a></li>\n\t\t\t\t\t\t<li><a href=\"pages/global.html#getEntry\" target=\"viewer\">getEntry</a></li>\n\t\t\t\t\t\t<li><a href=\"pages/global.html#getFilteredStack\" target=\"viewer\">getFilteredStack</a></li>\n\t\t\t\t\t\t<li><a href=\"pages/global.html#getFloatLength\" target=\"viewer\">getFloatLength</a></li>\n\t\t\t\t\t\t<li><a href=\"pages/global.html#getFormat\" target=\"viewer\">getFormat</a></li>\n\t\t\t\t\t\t<li><a href=\"pages/global.html#getIndirectIndex\" target=\"viewer\">getIndirectIndex</a></li>\n\t\t\t\t\t\t<li><a href=\"pages/global.html#getKeyframeOrder\" target=\"viewer\">getKeyframeOrder</a></li>\n\t\t\t\t\t\t<li><a href=\"pages/global.html#getMembersLayout\" target=\"viewer\">getMembersLayout</a></li>\n\t\t\t\t\t\t<li><a href=\"pages/global.html#getMorph\" target=\"viewer\">getMorph</a></li>\n\t\t\t\t\t\t<li><a href=\"pages/global.html#getPreviousInstance\" target=\"viewer\">getPreviousInstance</a></li>\n\t\t\t\t\t\t<li><a href=\"pages/global.html#getPreviousSkinnedPosition\" target=\"viewer\">getPreviousSkinnedPosition</a></li>\n\t\t\t\t\t\t<li><a href=\"pages/global.html#getSkinnedNormalAndTangent\" target=\"viewer\">getSkinnedNormalAndTangent</a></li>\n\t\t\t\t\t\t<li><a href=\"pages/global.html#getSkinnedPosition\" target=\"viewer\">getSkinnedPosition</a></li>\n\t\t\t\t\t\t<li><a href=\"pages/global.html#getSpecularDominantFactor\" target=\"viewer\">getSpecularDominantFactor</a></li>\n\t\t\t\t\t\t<li><a href=\"pages/global.html#getStrideLength\" target=\"viewer\">getStrideLength</a></li>\n\t\t\t\t\t\t<li><a href=\"pages/global.html#getTextureIndex\" target=\"viewer\">getTextureIndex</a></li>\n\t\t\t\t\t\t<li><a href=\"pages/global.html#getUniforms\" target=\"viewer\">getUniforms</a></li>\n\t\t\t\t\t\t<li><a href=\"pages/global.html#getVectorLength\" target=\"viewer\">getVectorLength</a></li>\n\t\t\t\t\t\t<li><a href=\"pages/global.html#getViewZNode\" target=\"viewer\">getViewZNode</a></li>\n\t\t\t\t\t\t<li><a href=\"pages/global.html#ggxReflectionSample\" target=\"viewer\">ggxReflectionSample</a></li>\n\t\t\t\t\t\t<li><a href=\"pages/global.html#ggxReflectionStruct\" target=\"viewer\">ggxReflectionStruct</a></li>\n\t\t\t\t\t\t<li><a href=\"pages/global.html#instanceColor\" target=\"viewer\">instanceColor</a></li>\n\t\t\t\t\t\t<li><a href=\"pages/global.html#inverseLerp\" target=\"viewer\">inverseLerp</a></li>\n\t\t\t\t\t\t<li><a href=\"pages/global.html#isPowerOfTwo\" target=\"viewer\">isPowerOfTwo</a></li>\n\t\t\t\t\t\t<li><a href=\"pages/global.html#isTypedArray\" target=\"viewer\">isTypedArray</a></li>\n\t\t\t\t\t\t<li><a href=\"pages/global.html#lerp\" target=\"viewer\">lerp</a></li>\n\t\t\t\t\t\t<li><a href=\"pages/global.html#lineDistance\" target=\"viewer\">lineDistance</a></li>\n\t\t\t\t\t\t<li><a href=\"pages/global.html#makeClipAdditive\" target=\"viewer\">makeClipAdditive</a></li>\n\t\t\t\t\t\t<li><a href=\"pages/global.html#mapLinear\" target=\"viewer\">mapLinear</a></li>\n\t\t\t\t\t\t<li><a href=\"pages/global.html#outgoingLight\" target=\"viewer\">outgoingLight</a></li>\n\t\t\t\t\t\t<li><a href=\"pages/global.html#pickBuildingColor\" target=\"viewer\">pickBuildingColor</a></li>\n\t\t\t\t\t\t<li><a href=\"pages/global.html#pingpong\" target=\"viewer\">pingpong</a></li>\n\t\t\t\t\t\t<li><a href=\"pages/global.html#radToDeg\" target=\"viewer\">radToDeg</a></li>\n\t\t\t\t\t\t<li><a href=\"pages/global.html#randFloat\" target=\"viewer\">randFloat</a></li>\n\t\t\t\t\t\t<li><a href=\"pages/global.html#randFloatSpread\" target=\"viewer\">randFloatSpread</a></li>\n\t\t\t\t\t\t<li><a href=\"pages/global.html#randInt\" target=\"viewer\">randInt</a></li>\n\t\t\t\t\t\t<li><a href=\"pages/global.html#rgbToYCoCg\" target=\"viewer\">rgbToYCoCg</a></li>\n\t\t\t\t\t\t<li><a href=\"pages/global.html#sample\" target=\"viewer\">sample</a></li>\n\t\t\t\t\t\t<li><a href=\"pages/global.html#seededRandom\" target=\"viewer\">seededRandom</a></li>\n\t\t\t\t\t\t<li><a href=\"pages/global.html#setConsoleFunction\" target=\"viewer\">setConsoleFunction</a></li>\n\t\t\t\t\t\t<li><a href=\"pages/global.html#setProjectionFromUnion\" target=\"viewer\">setProjectionFromUnion</a></li>\n\t\t\t\t\t\t<li><a href=\"pages/global.html#setQuaternionFromProperEuler\" target=\"viewer\">setQuaternionFromProperEuler</a></li>\n\t\t\t\t\t\t<li><a href=\"pages/global.html#setupWebGLXRFallback\" target=\"viewer\">setupWebGLXRFallback</a></li>\n\t\t\t\t\t\t<li><a href=\"pages/global.html#shadowRenderObjectFunction\" target=\"viewer\">shadowRenderObjectFunction</a></li>\n\t\t\t\t\t\t<li><a href=\"pages/global.html#slab\" target=\"viewer\">slab</a></li>\n\t\t\t\t\t\t<li><a href=\"pages/global.html#smootherstep\" target=\"viewer\">smootherstep</a></li>\n\t\t\t\t\t\t<li><a href=\"pages/global.html#sortedArray\" target=\"viewer\">sortedArray</a></li>\n\t\t\t\t\t\t<li><a href=\"pages/global.html#subclip\" target=\"viewer\">subclip</a></li>\n\t\t\t\t\t\t<li><a href=\"pages/global.html#temporalReproject\" target=\"viewer\">temporalReproject</a></li>\n\t\t\t\t\t\t<li><a href=\"pages/global.html#toHalfFloat\" target=\"viewer\">toHalfFloat</a></li>\n\t\t\t\t\t\t<li><a href=\"pages/global.html#totalDiffuse\" target=\"viewer\">totalDiffuse</a></li>\n\t\t\t\t\t\t<li><a href=\"pages/global.html#totalSpecular\" target=\"viewer\">totalSpecular</a></li>\n\t\t\t\t\t\t<li><a href=\"pages/global.html#trimSegmentAlpha\" target=\"viewer\">trimSegmentAlpha</a></li>\n\t\t\t\t\t\t<li><a href=\"pages/global.html#updateCamera\" target=\"viewer\">updateCamera</a></li>\n\t\t\t\t\t\t<li><a href=\"pages/global.html#updateUserCamera\" target=\"viewer\">updateUserCamera</a></li>\n\t\t\t\t\t\t<li><a href=\"pages/global.html#viewportResolution\" target=\"viewer\">viewportResolution</a></li>\n\t\t\t\t\t\t<li><a href=\"pages/global.html#warn\" target=\"viewer\">warn</a></li>\n\t\t\t\t\t\t<li><a href=\"pages/global.html#warnOnce\" target=\"viewer\">warnOnce</a></li>\n\t\t\t\t\t\t<li><a href=\"pages/global.html#worldEnd\" target=\"viewer\">worldEnd</a></li>\n\t\t\t\t\t\t<li><a href=\"pages/global.html#worldPos\" target=\"viewer\">worldPos</a></li>\n\t\t\t\t\t\t<li><a href=\"pages/global.html#worldStart\" target=\"viewer\">worldStart</a></li>\n\t\t\t\t\t\t<li><a href=\"pages/global.html#ycocgToRGB\" target=\"viewer\">ycocgToRGB</a></li>\n\t\t\t\t\t\t<li><a href=\"pages/global.html#yieldToMain\" target=\"viewer\">yieldToMain</a></li>\n\t\t\t\t\t</ul>",
    "success": true,
    "error": "",
    "crawled_at": 1786376809.300204,
    "is_shell": false
  },
  {
    "url": "https://threejs.org/manual/en/installation.html",
    "title": "Installation",
    "content": "<div class=\"container\">\n      <div class=\"lesson-title\">\n        <h1>Installation</h1>\n      </div>\n      <div class=\"lesson\">\n        <div class=\"lesson-main\">\n          \n          <h2>Project structure</h2>\n\n          <p>\n            Every three.js project needs at least one HTML file to define the webpage, and a JavaScript file to run your three.js code. The structure and naming choices below aren't required, but will be used throughout this guide for consistency.\n          </p>\n\n          <ul>\n            <li>\n              <i>index.html</i>\n    <pre class=\"prettyprint notranslate lang-js\" translate=\"no\">&lt;!DOCTYPE html&gt;\n&lt;html lang=\"en\"&gt;\n  &lt;head&gt;\n    &lt;meta charset=\"utf-8\"&gt;\n    &lt;title&gt;My first three.js app&lt;/title&gt;\n    &lt;style&gt;\n      body { margin: 0; }\n    &lt;/style&gt;\n  &lt;/head&gt;\n  &lt;body&gt;\n    &lt;script type=\"module\" src=\"/main.js\"&gt;&lt;/script&gt;\n  &lt;/body&gt;\n&lt;/html&gt;\n    </pre>\n  </li>\n  <li>\n    <i>main.js</i>\n<pre class=\"prettyprint notranslate lang-js\" translate=\"no\">import * as THREE from 'three';\n\n...\n</pre>\n            </li>\n            <li>\n              <i>public/</i>\n              <ul>\n                <li>\n                  The <i>public/</i> folder is sometimes also called a \"static\" folder, because the files it contains are pushed to the website unchanged. Usually textures, audio, and 3D models will go here.\n                </li>\n              </ul>\n            </li>\n          </ul>\n\n          <p>\n            Now that we've set up the basic project structure, we need a way to run the project locally and access it through a web browser. Installation and local development can be accomplished with npm and a build tool, or by importing three.js from a CDN. Both options are explained in the sections below.\n          </p>\n\n          <h2>Option 1: Install with NPM and a build tool</h2>\n\n          <h3>Development</h3>\n\n          <p>\n            Installing from the <a href=\"https://www.npmjs.com/\" target=\"_blank\">npm package registry</a> and using a <a href=\"https://eloquentjavascript.net/10_modules.html#h_zWTXAU93DC\" target=\"_blank\">build tool</a> is the recommended approach for most users — the more dependencies your project needs, the more likely you are to run into problems that the static hosting cannot easily resolve. With a build tool, importing local JavaScript files and npm packages should work out of the box, without import maps.\n          </p>\n\n\n          <ol>\n            <li>\n              Install <a href=\"https://nodejs.org/\" target=\"_blank\">Node.js</a>. We'll need it to manage dependencies and to run our build tool.\n            </li>\n            <li>\n              <p>\n                Install three.js and a build tool, <a href=\"https://vitejs.dev/\" target=\"_blank\">Vite</a>, using a <a href=\"https://www.joshwcomeau.com/javascript/terminal-for-js-devs/\" target=\"_blank\">terminal</a> in your project folder. Vite will be used during development, but it isn't part of the final webpage. If you prefer to use another build tool, that's fine — we support modern build tools that can import <a href=\"https://eloquentjavascript.net/10_modules.html#h_zWTXAU93DC\" target=\"_blank\">ES Modules</a>.\n              </p>\n<pre class=\"prettyprint notranslate\" translate=\"no\"># three.js\nnpm install --save three\n\n# vite\nnpm install --save-dev vite\n</pre>\n              <aside>\n                <details>\n                  <summary>Installation added <i>node_modules/</i> and <i>package.json</i> to my project. What are they?</summary>\n                  <p>\n                    npm uses <i>package.json</i> to describe which versions of each dependency you've installed. If you have other people working on the project with you, they can install the original versions of each dependency simply by running <i>npm install</i>. If you're using version history, commit <i>package.json</i>.\n                  </p>\n                  <p>\n                    npm installs the code for each dependency in a new <i>node_modules/</i> folder. When Vite builds your application, it sees imports for 'three' and pulls three.js files automatically from this folder. The <i>node_modules/</i> folder is used only during development, and shouldn't be uploaded to your web hosting provider or committed to version history.\n                  </p>\n                </details>\n                <details>\n                  <summary>Using three.js with TypeScript</summary>\n                  <p>\n                    Community-maintained TypeScript type definitions for three.js are available at <a href=\"https://github.com/three-types/three-ts-types\" target=\"_blank\">three-types/three-ts-types</a>.\n                  </p>\n                </details>\n              </aside>\n            </li>\n            <li>\n              From your terminal, run:\n              <pre class=\"prettyprint notranslate\" translate=\"no\">npx vite </pre>\n              <aside>\n                <details>\n                  <summary>What is <i>npx</i>?</summary>\n                  <p>\n                    npx is installed with Node.js, and runs command line programs like Vite so that you don't have to search for the right file in <i>node_modules/</i> yourself. If you prefer, you can put <a href=\"https://vitejs.dev/guide/#command-line-interface\" target=\"_blank\">Vite's common commands</a> into the <a href=\"https://docs.npmjs.com/cli/v9/using-npm/scripts\" target=\"_blank\">package.json:scripts</a> list, and use <i>npm run dev</i> instead.\n                  </p>\n                </details>\n              </aside>\n            </li>\n            <li>\n              If everything went well, you'll see a URL like <i>http://localhost:5173</i> appear in your terminal, and can open that URL to see your web application.\n            </li>\n          </ol>\n\n          <p>\n            The page will be blank — you're ready to <a href=\"creating-a-scene.html\">create a scene</a>.\n          </p>\n\n          <p>\n            If you want to learn more about these tools before you continue, see:\n          </p>\n\n          <ul>\n            <li>\n              <a href=\"https://threejs-journey.com/lessons/local-server\" target=\"_blank\">three.js journey: Local Server</a>\n            </li>\n            <li>\n              <a href=\"https://vitejs.dev/guide/cli.html\" target=\"_blank\">Vite: Command Line Interface</a>\n            </li>\n            <li>\n              <a href=\"https://developer.mozilla.org/en-US/docs/Learn/Tools_and_testing/Understanding_client-side_tools/Package_management\" target=\"_blank\">MDN: Package management basics</a>\n            </li>\n          </ul>\n\n          <h3>Production</h3>\n\n          <p>\n            Later, when you're ready to deploy your web application, you'll just need to tell Vite to run a production build — <i>npx vite build</i>. Everything used by the application will be compiled, optimized, and copied into the <i>dist/</i> folder. The contents of that folder are ready to be hosted on your website.\n          </p>\n\n          <h2>Option 2: Import from a CDN</h2>\n\n          <h3>Development</h3>\n\n          <p>Installing without build tools will require some changes to the project structure given above.</p>\n\n          <ol>\n            <li>\n              <p>\n                We imported code from 'three' (an npm package) in <i>main.js</i>, and web browsers don't know what that means. In <i>index.html</i> we'll need to add an <a href=\"https://developer.mozilla.org/en-US/docs/Web/HTML/Element/script/type/importmap\" target=\"_blank\">import map</a> defining where to get the package. Put the code below inside the <i>&lt;head&gt;&lt;/head&gt;</i> tag, after the styles.\n              </p>\n<pre class=\"prettyprint notranslate lang-js\" translate=\"no\">&lt;script type=\"importmap\"&gt;\n{\n  \"imports\": {\n    \"three\": \"https://cdn.jsdelivr.net/npm/three@&lt;version&gt;/build/three.module.js\",\n    \"three/addons/\": \"https://cdn.jsdelivr.net/npm/three@&lt;version&gt;/examples/jsm/\"\n  }\n}\n&lt;/script&gt;\n</pre>\n              <p>\n                Don't forget to replace <i>&lt;version&gt;</i> with an actual version of three.js, like <i>\"v0.149.0\"</i>. The most recent version can be found on the <a href=\"https://www.npmjs.com/package/three?activeTab=versions\" target=\"_blank\">npm version list</a>.\n              </p>\n            </li>\n            <li>\n              <p>\n                We'll also need to run a <i>local server</i> to host these files at URL where the web browser can access them. While it's technically possible to double-click an HTML file and open it in your browser, important features that we'll later implement, do not work when the page is opened this way, for security reasons.\n              </p>\n              <p>\n                Install <a href=\"https://nodejs.org/\" target=\"_blank\">Node.js</a>, then run <a href=\"https://www.npmjs.com/package/serve\" target=\"_blank\">serve</a> to start a local server in the project's directory:\n              </p>\n              <pre class=\"prettyprint notranslate\" translate=\"no\">npx serve .</pre>\n            </li>\n            <li>\n              If everything went well, you'll see a URL like http://localhost:3000 appear in your terminal, and can open that URL to see your web application.\n            </li>\n          </ol>\n\n          <p>\n            The page will be blank — you're ready to <a href=\"#manual/introduction/Creating-a-scene\" target=\"_blank\">create a scene</a>.\n          </p>\n\n          <p>\n            Many other local static servers are available — some use different languages instead of Node.js, and others are desktop applications. They all work basically the same way, and we've provided a few alternatives below.\n          </p>\n\n          <details>\n            <summary>More local servers</summary>\n\n            <h3>Command Line</h3>\n\n            <p>Command line local servers run from a terminal window. The associated programming language may need to be installed first.</p>\n\n            <ul>\n              <li><i>npx http-server</i> (Node.js)</li>\n              <li><i>npx five-server</i> (Node.js)</li>\n              <li><i>python -m SimpleHTTPServer</i> (Python 2.x)</li>\n              <li><i>python -m http.server</i> (Python 3.x)</li>\n              <li><i>php -S localhost:8000</i> (PHP 5.4+)</li>\n            </ul>\n\n\n            <h3>GUI</h3>\n\n            <p>GUI local servers run as an application window on your computer, and may have a user interface.</p>\n\n            <ul>\n              <li><a href=\"https://greggman.github.io/servez\" target=\"_blank\">Servez</a></li>\n            </ul>\n\n            <h3>Code Editor Plugins</h3>\n\n            <p>Some code editors have plugins that spawn a simple server on demand.</p>\n\n            <ul>\n              <li><a href=\"https://marketplace.visualstudio.com/items?itemName=yandeu.five-server\" target=\"_blank\">Five Server</a> for Visual Studio Code</li>\n              <li><a href=\"https://marketplace.visualstudio.com/items?itemName=ritwickdey.LiveServer\" target=\"_blank\">Live Server</a> for Visual Studio Code</li>\n              <li><a href=\"https://atom.io/packages/atom-live-server\" target=\"_blank\">Live Server</a> for Atom</li>\n            </ul>\n\n\n          </details>\n\n          <h3>Production</h3>\n\n          <p>\n            When you're ready to deploy your web application, push the source files to your web hosting provider — no need to build or compile anything. The downside of that tradeoff is that you'll need to be careful to keep the import map updated with any dependencies (and dependencies of dependencies!) that your application requires. If the CDN hosting your dependencies goes down temporarily, your website will stop working too.\n          </p>\n\n          <p>\n            <i><b>IMPORTANT:</b> Import all dependencies from the same version of three.js, and from the same CDN. Mixing files from different sources may cause duplicate code to be included, or even break the application in unexpected ways.</i>\n          </p>\n\n          <h2>Addons</h2>\n\n          <p>\n            Out of the box, three.js includes the fundamentals of a 3D engine. Other three.js components — such as controls, loaders, and post-processing effects — are part of the <a href=\"https://github.com/mrdoob/three.js/tree/dev/examples/jsm\" target=\"_blank\">addons/</a> directory. Addons do not need to be <i>installed</i> separately, but do need to be <i>imported</i> separately.\n          </p>\n\n          <p>\n            The example below shows how to import three.js with the <code class=\"notranslate\" translate=\"no\">OrbitControls</code> and <code class=\"notranslate\" translate=\"no\">GLTFLoader</code> addons. Where necessary, this will also be mentioned in each addon's documentation or examples.\n          </p>\n\n<pre class=\"prettyprint notranslate lang-js\" translate=\"no\">import * as THREE from 'three';\nimport { OrbitControls } from 'three/addons/controls/OrbitControls.js';\nimport { GLTFLoader } from 'three/addons/loaders/GLTFLoader.js';\n\nconst controls = new OrbitControls( camera, renderer.domElement );\nconst loader = new GLTFLoader();\n</pre>\n\n          <p>\n            Some excellent third-party projects are available for three.js, too. These need to be installed separately — see <a href=\"libraries-and-plugins\">Libraries and Plugins</a>.\n          </p>\n\n          <h2>Next Steps</h2>\n\n          <p>\n            You're now ready to <a href=\"creating-a-scene.html\">create a scene</a>.\n          </p>\n\n        </div>\n      </div>\n    </div>",
    "success": true,
    "error": "",
    "crawled_at": 1786376809.300204,
    "is_shell": false
  },
  {
    "url": "https://threejs.org/manual/en/creating-a-scene.html",
    "title": "Creating a scene",
    "content": "<div class=\"container\">\n      <div class=\"lesson-title\">\n        <h1>Creating a scene</h1>\n      </div>\n      <div class=\"lesson\">\n        <div class=\"lesson-main\">\n            \n          <p>The goal of this section is to give a brief introduction to three.js. We will start by setting up a scene, with a spinning cube. A working example is provided at the bottom of the page in case you get stuck and need help.</p>\n\n\t\t<h2>Before we start</h2>\n\n\t\t<p>\n\t\t\tIf you haven't yet, go through the <code class=\"notranslate\" translate=\"no\">Installation</code> guide. We'll assume you've already set up the same project structure (including <i>index.html</i> and <i>main.js</i>), have installed three.js, and are either running a build tool, or using a local server with a CDN and import maps.\n\t\t</p>\n\n\t\t<h2>Creating the scene</h2>\n\n\t\t<p>To actually be able to display anything with three.js, we need three things: scene, camera and renderer, so that we can render the scene with camera.</p>\n\n\t\t<p><i>main.js —</i></p>\n\n<pre class=\"prettyprint notranslate lang-js\" translate=\"no\">import * as THREE from 'three';\n\nconst scene = new THREE.Scene();\nconst camera = new THREE.PerspectiveCamera( 75, window.innerWidth / window.innerHeight, 0.1, 1000 );\n\nconst renderer = new THREE.WebGLRenderer();\nrenderer.setSize( window.innerWidth, window.innerHeight );\ndocument.body.appendChild( renderer.domElement );\n</pre>\n\n\t\t<p>Let's take a moment to explain what's going on here. We have now set up the scene, our camera and the renderer.</p>\n\n\t\t<p>There are a few different cameras in three.js. For now, let's use a <code class=\"notranslate\" translate=\"no\">PerspectiveCamera</code>.</p>\n\n\t\t<p>The first attribute is the <code class=\"notranslate\" translate=\"no\">field of view</code>. FOV is the extent of the scene that is seen on the display at any given moment. The value is in degrees.</p>\n\n\t\t<p>The second one is the <code class=\"notranslate\" translate=\"no\">aspect ratio</code>. You almost always want to use the width of the element divided by the height, or you'll get the same result as when you play old movies on a widescreen TV - the image looks squished.</p>\n\n\t\t<p>The next two attributes are the <code class=\"notranslate\" translate=\"no\">near</code> and <code class=\"notranslate\" translate=\"no\">far</code> clipping plane. What that means, is that objects further away from the camera than the value of <code class=\"notranslate\" translate=\"no\">far</code> or closer than <code class=\"notranslate\" translate=\"no\">near</code> won't be rendered. You don't have to worry about this now, but you may want to use other values in your apps to get better performance.</p>\n\n\t\t<p>Next up is the renderer. In addition to creating the renderer instance, we also need to set the size at which we want it to render our app. It's a good idea to use the width and height of the area we want to fill with our app - in this case, the width and height of the browser window. For performance intensive apps, you can also give <code class=\"notranslate\" translate=\"no\">setSize</code> smaller values, like <code class=\"notranslate\" translate=\"no\">window.innerWidth/2</code> and <code class=\"notranslate\" translate=\"no\">window.innerHeight/2</code>, which will make the app render at quarter size.</p>\n\n\t\t<p>If you wish to keep the size of your app but render it at a lower resolution, you can do so by calling <code class=\"notranslate\" translate=\"no\">setSize</code> with false as <code class=\"notranslate\" translate=\"no\">updateStyle</code> (the third argument). For example, <code class=\"notranslate\" translate=\"no\">setSize(window.innerWidth/2, window.innerHeight/2, false)</code> will render your app at half resolution, given that your &lt;canvas&gt; has 100% width and height.</p>\n\n\t\t<p>Last but not least, we add the <code class=\"notranslate\" translate=\"no\">renderer</code> element to our HTML document. This is a &lt;canvas&gt; element the renderer uses to display the scene to us.</p>\n\n\t\t<p><em>\"That's all good, but where's that cube you promised?\"</em> Let's add it now.</p>\n\n<pre class=\"prettyprint notranslate lang-js\" translate=\"no\">const geometry = new THREE.BoxGeometry( 1, 1, 1 );\nconst material = new THREE.MeshBasicMaterial( { color: 0x00ff00 } );\nconst cube = new THREE.Mesh( geometry, material );\nscene.add( cube );\n\ncamera.position.z = 5;\n</pre>\n\n\t\t<p>To create a cube, we need a <code class=\"notranslate\" translate=\"no\">BoxGeometry</code>. This is an object that contains all the points (<code class=\"notranslate\" translate=\"no\">vertices</code>) and fill (<code class=\"notranslate\" translate=\"no\">faces</code>) of the cube. We'll explore this more in the future.</p>\n\n\t\t<p>In addition to the geometry, we need a material to color it. Three.js comes with several materials, but we'll stick to the <code class=\"notranslate\" translate=\"no\">MeshBasicMaterial</code> for now. All materials take an object of properties which will be applied to them. To keep things very simple, we only supply a color attribute of <code class=\"notranslate\" translate=\"no\">0x00ff00</code>, which is green. This works the same way that colors work in CSS or Photoshop (<code class=\"notranslate\" translate=\"no\">hex colors</code>).</p>\n\n\t\t<p>The third thing we need is a <code class=\"notranslate\" translate=\"no\">Mesh</code>. A mesh is an object that takes a geometry, and applies a material to it, which we then can insert to our scene, and move freely around.</p>\n\n\t\t<p>By default, when we call <code class=\"notranslate\" translate=\"no\">scene.add()</code>, the thing we add will be added to the coordinates <code class=\"notranslate\" translate=\"no\">(0,0,0)</code>. This would cause both the camera and the cube to be inside each other. To avoid this, we simply move the camera out a bit.</p>\n\n\t\t<h2>Rendering the scene</h2>\n\n\t\t<p>If you copied the code from above into the main.js file we created earlier, you wouldn't be able to see anything. This is because we're not actually rendering anything yet. For that, we need what's called a render or animation loop.</p>\n\n<pre class=\"prettyprint notranslate lang-js\" translate=\"no\">function animate( time ) {\n  renderer.render( scene, camera );\n}\nrenderer.setAnimationLoop( animate );\n</pre>\n\n\t\t<p>This will create a loop that causes the renderer to draw the scene every time the screen is refreshed (on a typical screen this means 60 times per second). If you're new to writing games in the browser, you might say <em>\"why don't we just create a setInterval ?\"</em> The thing is - we could, but <code class=\"notranslate\" translate=\"no\">requestAnimationFrame</code> which is internally used in <code class=\"notranslate\" translate=\"no\">WebGLRenderer</code> has a number of advantages. Perhaps the most important one is that it pauses when the user navigates to another browser tab, hence not wasting their precious processing power and battery life.</p>\n\n\t\t<h2>Animating the cube</h2>\n\n\t\t<p>If you insert all the code above into the file you created before we began, you should see a green box. Let's make it all a little more interesting by rotating it.</p>\n\n\t\t<p>Add the following code right above the <code class=\"notranslate\" translate=\"no\">renderer.render</code> call in your <code class=\"notranslate\" translate=\"no\">animate</code> function:</p>\n\n<pre class=\"prettyprint notranslate lang-js\" translate=\"no\">cube.rotation.x = time / 2000;\ncube.rotation.y = time / 1000;\n</pre>\n\n\t\t<p>This will be run every frame (normally 60 times per second), and give the cube a nice rotation animation. Basically, anything you want to move or change while the app is running has to go through the animation loop. You can of course call other functions from there, so that you don't end up with an <code class=\"notranslate\" translate=\"no\">animate</code> function that's hundreds of lines.</p>\n\n\t\t<h2>The result</h2>\n\t\t<p>Congratulations! You have now completed your first three.js application. It's simple, but you have to start somewhere.</p>\n\n\t\t<p>The full code is available below and as an editable <a href=\"https://jsfiddle.net/zycqb61k/\" target=\"_blank\">live example</a>. Play around with it to get a better understanding of how it works.</p>\n\n\t\t<p><i>index.html —</i></p>\n\n<pre class=\"prettyprint notranslate lang-js\" translate=\"no\">&lt;!DOCTYPE html&gt;\n&lt;html lang=\"en\"&gt;\n  &lt;head&gt;\n    &lt;meta charset=\"utf-8\"&gt;\n    &lt;title&gt;My first three.js app&lt;/title&gt;\n    &lt;style&gt;\n      body { margin: 0; }\n    &lt;/style&gt;\n  &lt;/head&gt;\n  &lt;body&gt;\n    &lt;script type=\"module\" src=\"/main.js\"&gt;&lt;/script&gt;\n  &lt;/body&gt;\n&lt;/html&gt;\n</pre>\n\n\t\t<p><i>main.js —</i></p>\n\n<pre class=\"prettyprint notranslate lang-js\" translate=\"no\">import * as THREE from 'three';\n\nconst scene = new THREE.Scene();\nconst camera = new THREE.PerspectiveCamera( 75, window.innerWidth / window.innerHeight, 0.1, 1000 );\n\nconst renderer = new THREE.WebGLRenderer();\nrenderer.setSize( window.innerWidth, window.innerHeight );\nrenderer.setAnimationLoop( animate );\ndocument.body.appendChild( renderer.domElement );\n\nconst geometry = new THREE.BoxGeometry( 1, 1, 1 );\nconst material = new THREE.MeshBasicMaterial( { color: 0x00ff00 } );\nconst cube = new THREE.Mesh( geometry, material );\nscene.add( cube );\n\ncamera.position.z = 5;\n\nfunction animate( time ) {\n\n  cube.rotation.x = time / 2000;\n  cube.rotation.y = time / 1000;\n\n  renderer.render( scene, camera );\n\n}\n</pre>\n\n        </div>\n      </div>\n    </div>",
    "success": true,
    "error": "",
    "crawled_at": 1786376809.300204,
    "is_shell": false
  },
  {
    "url": "https://threejs.org/manual/en/creating-text.html",
    "title": "Creating Text",
    "content": "<div class=\"container\">\n      <div class=\"lesson-title\">\n        <h1>Creating Text</h1>\n      </div>\n      <div class=\"lesson\">\n        <div class=\"lesson-main\">\n           \n          <div>\n            <p>\n              There are often times when you might need to use text in your three.js application - here are\n              a couple of ways that you can do so.\n            </p>\n          </div>\n      \n          <h2>1. DOM + CSS</h2>\n          <div>\n            <p>\n              Using HTML is generally the easiest and fastest manner to add text. This is the method\n              used for descriptive overlays in most three.js examples.\n            </p>\n            <p>You can add content to a</p>\n<pre class=\"prettyprint notranslate lang-js\" translate=\"no\">&lt;div id=\"info\"&gt;Description&lt;/div&gt;\n</pre>\n            <p>\n              and use CSS markup to position absolutely at a position above all others with a\n              z-index especially if you are running three.js full screen.\n            </p>\n      \n<pre class=\"prettyprint notranslate lang-js\" translate=\"no\">#info {\n  position: absolute;\n  top: 10px;\n  width: 100%;\n  text-align: center;\n  z-index: 100;\n  display:block;\n}\n</pre>\n      \n          </div>\n      \n          \n          <h2>2. Use <code class=\"notranslate\" translate=\"no\">CSS2DRenderer</code> or <code class=\"notranslate\" translate=\"no\">CSS3DRenderer</code></h2>\n          <div>\n            <p>\n              Use these renderers to draw high-quality text contained in DOM elements to your three.js scene.\n              This is similar to 1. except that with these renderers elements can be integrated more tightly and dynamically into the scene.\n            </p>\n          </div>\n          \n      \n          <h2>3. Draw text to canvas and use as a <code class=\"notranslate\" translate=\"no\">Texture</code></h2>\n          <div>\n            <p>Use this method if you wish to draw text easily on a plane in your three.js scene.</p>\n          </div>\n      \n      \n          <h2>4. Create a model in your favourite 3D application and export to three.js</h2>\n          <div>\n            <p>Use this method if you prefer working with your 3d applications and importing the models to three.js.</p>\n          </div>\n      \n      \n          <h2>5. Procedural Text Geometry</h2>\n          <div>\n            <p>\n              If you prefer to work purely in THREE.js or to create procedural and dynamic 3D\n              text geometries, you can create a mesh whose geometry is an instance of THREE.TextGeometry:\n            </p>\n            <p>\n              <code>new THREE.TextGeometry( text, parameters );</code>\n            </p>\n            <p>\n              In order for this to work, however, your TextGeometry will need an instance of THREE.Font\n              to be set on its \"font\" parameter.\n      \n              See the <code class=\"notranslate\" translate=\"no\">TextGeometry</code> page for more info on how this can be done, descriptions of each\n              accepted parameter, and a list of the JSON fonts that come with the THREE.js distribution itself.\n            </p>\n\n            <h3>Examples</h3>\n\n            <p>\n              <a href=\"../../examples/#webgl_geometry_text\" target=\"_blank\">WebGL / geometry / text</a><br>\n              <a href=\"../../examples/#webgl_shadowmap\" target=\"_blank\">WebGL / shadowmap</a>\n            </p>\n      \n            <p>\n              If Typeface is down, or you want to use a font that is not there, there's a tutorial\n              with a python script for blender that allows you to export text to Three.js's JSON format:\n              <a href=\"http://www.jaanga.com/2012/03/blender-to-threejs-create-3d-text-with.html\" target=\"_blank\">http://www.jaanga.com/2012/03/blender-to-threejs-create-3d-text-with.html</a>\n            </p>\n      \n          </div>\n      \n      \n          <h2>6. Bitmap Fonts</h2>\n          <div>\n            <p>\n              BMFonts (bitmap fonts) allow batching glyphs into a single BufferGeometry. BMFont rendering\n              supports word-wrapping, letter spacing, kerning, signed distance fields with standard\n              derivatives, multi-channel signed distance fields, multi-texture fonts, and more.\n              See <a href=\"https://github.com/felixmariotto/three-mesh-ui\" target=\"_blank\">three-mesh-ui</a> or <a href=\"https://github.com/Jam3/three-bmfont-text\" target=\"_blank\">three-bmfont-text</a>.\n            </p>\n            <p>\n              Stock fonts are available in projects like\n              <a href=\"https://github.com/etiennepinchon/aframe-fonts\" target=\"_blank\">A-Frame Fonts</a>, or you can create your own\n              from any .TTF font, optimizing to include only characters required for a project.\n            </p>\n            <p>\n              Some helpful tools:\n            </p>\n            <ul>\n              <li><a href=\"http://msdf-bmfont.donmccurdy.com/\" target=\"_blank\">msdf-bmfont-web</a> <i>(web-based)</i></li>\n              <li><a href=\"https://github.com/soimy/msdf-bmfont-xml\" target=\"_blank\">msdf-bmfont-xml</a> <i>(commandline)</i></li>\n              <li><a href=\"https://github.com/libgdx/libgdx/wiki/Hiero\" target=\"_blank\">hiero</a> <i>(desktop app)</i></li>\n            </ul>\n          </div>\n      \n      \n          <h2>7. Troika Text</h2>\n          <div>\n            <p>\n              The <a href=\"https://www.npmjs.com/package/troika-three-text\" target=\"_blank\">troika-three-text</a> package renders \n              quality antialiased text using a similar technique as BMFonts, but works directly with any .TTF \n              or .WOFF font file so you don't have to pregenerate a glyph texture offline. It also adds \n              capabilities including:\n            </p>\n            <ul>\n              <li>Effects like strokes, drop shadows, and curvature</li>\n              <li>The ability to apply any three.js Material, even a custom ShaderMaterial</li>\n              <li>Support for font ligatures, scripts with joined letters, and right-to-left/bidirectional layout</li>\n              <li>Optimization for large amounts of dynamic text, performing most work off the main thread in a web worker</li>\n            </ul>\n          </div>\n\n        </div>\n      </div>\n    </div>",
    "success": true,
    "error": "",
    "crawled_at": 1786376809.300204,
    "is_shell": false
  },
  {
    "url": "https://threejs.org/manual/en/drawing-lines.html",
    "title": "Drawing Lines",
    "content": "<div class=\"container\">\n      <div class=\"lesson-title\">\n        <h1>Drawing Lines</h1>\n      </div>\n      <div class=\"lesson\">\n        <div class=\"lesson-main\">\n          \n          <p>\n            Let's say you want to draw a line or a circle, not a wireframe <code class=\"notranslate\" translate=\"no\">Mesh</code>.\n            First we need to set up the renderer, scene and camera (see the Creating a scene page).\n          </p>\n    \n          <p>Here is the code that we will use:</p>\n<pre class=\"prettyprint notranslate lang-js\" translate=\"no\">const renderer = new THREE.WebGLRenderer();\nrenderer.setSize( window.innerWidth, window.innerHeight );\ndocument.body.appendChild( renderer.domElement );\n\nconst camera = new THREE.PerspectiveCamera( 45, window.innerWidth / window.innerHeight, 1, 500 );\ncamera.position.set( 0, 0, 100 );\ncamera.lookAt( 0, 0, 0 );\n\nconst scene = new THREE.Scene();\n</pre>\n          <p>Next thing we will do is define a material. For lines we have to use <code class=\"notranslate\" translate=\"no\">LineBasicMaterial</code> or <code class=\"notranslate\" translate=\"no\">LineDashedMaterial</code>.</p>\n<pre class=\"prettyprint notranslate lang-js\" translate=\"no\">//create a blue LineBasicMaterial\nconst material = new THREE.LineBasicMaterial( { color: 0x0000ff } );\n</pre>\n    \n          <p>\n            After material we will need a geometry with some vertices:\n          </p>\n    \n<pre class=\"prettyprint notranslate lang-js\" translate=\"no\">const points = [];\npoints.push( new THREE.Vector3( - 10, 0, 0 ) );\npoints.push( new THREE.Vector3( 0, 10, 0 ) );\npoints.push( new THREE.Vector3( 10, 0, 0 ) );\n\nconst geometry = new THREE.BufferGeometry().setFromPoints( points );\n</pre>\n    \n          <p>Note that lines are drawn between each consecutive pair of vertices, but not between the first and last (the line is not closed.)</p>\n    \n          <p>Now that we have points for two lines and a material, we can put them together to form a line.</p>\n<pre class=\"prettyprint notranslate lang-js\" translate=\"no\">const line = new THREE.Line( geometry, material );\n</pre>\n          <p>All that's left is to add it to the scene and call <code class=\"notranslate\" translate=\"no\">renderer.render()</code>.</p>\n    \n<pre class=\"prettyprint notranslate lang-js\" translate=\"no\">scene.add( line );\nrenderer.render( scene, camera );\n</pre>\n    \n          <p>You should now be seeing an arrow pointing upwards, made from two blue lines.</p>\n\n        </div>\n      </div>\n    </div>",
    "success": true,
    "error": "",
    "crawled_at": 1786376809.300204,
    "is_shell": false
  },
  {
    "url": "https://threejs.org/manual/en/faq.html",
    "title": "FAQ",
    "content": "<div class=\"container\">\n      <div class=\"lesson-title\">\n        <h1>FAQ</h1>\n      </div>\n      <div class=\"lesson\">\n        <div class=\"lesson-main\">\n          \n          <h2>Which 3D model format is best supported?</h2>\n          <div>\n            <p>\n              The recommended format for importing and exporting assets is glTF (GL Transmission Format). Because glTF is focused on runtime asset delivery, it is compact to transmit and fast to load.\n            </p>\n            <p>\n              three.js provides loaders for many other popular formats like FBX, Collada or OBJ as well. Nevertheless, you should always try to establish a glTF based workflow in your projects first. \n            </p>\n          </div>\n      \n          <h2>Why are there meta viewport tags in examples?</h2>\n          <div>\n            <pre class=\"prettyprint notranslate lang-js\" translate=\"no\">&lt;meta name=\"viewport\" content=\"width=device-width, user-scalable=no, minimum-scale=1.0, maximum-scale=1.0\"&gt;</pre>\n      \n              <p>These tags control viewport size and scale for mobile browsers (where page content may be rendered at different size than visible viewport).</p>\n      \n              <p><a href=\"https://developer.apple.com/library/content/documentation/AppleApplications/Reference/SafariWebContent/UsingtheViewport/UsingtheViewport.html\" target=\"_blank\">Safari: Using the Viewport</a></p>\n      \n              <p><a href=\"https://developer.mozilla.org/en-US/docs/Web/HTML/Viewport_meta_tag\" target=\"_blank\">MDN: Using the viewport meta tag</a></p>\n          </div>\n      \n          <h2>How can scene scale be preserved on resize?</h2>\n          <p>\n            We want all objects, regardless of their distance from the camera, to appear the same size, even as the window is resized.\n      \n            The key equation to solving this is this formula for the visible height at a given distance:\n      \n            </p><pre class=\"prettyprint notranslate lang-js\" translate=\"no\">visible_height = 2 * Math.tan( ( Math.PI / 180 ) * camera.fov / 2 ) * distance_from_camera;</pre>\n            If we increase the window height by a certain percentage, then what we want is the visible height at all distances\n            to increase by the same percentage.\n      \n            This can not be done by changing the camera position. Instead you have to change the camera field-of-view.\n            <a href=\"http://jsfiddle.net/Q4Jpu/\" target=\"_blank\">Example</a>.\n          <p></p>\n      \n          <h2>Why is part of my object invisible?</h2>\n          <p>\n            This could be because of face culling. Faces have an orientation that decides which side is which. And the culling removes the backside in normal circumstances. \n            To see if this is your problem, change the material side to THREE.DoubleSide.\n            </p><pre class=\"prettyprint notranslate lang-js\" translate=\"no\">material.side = THREE.DoubleSide</pre>\n          <p></p>\n      \n          <h2>Why does three.js sometimes return strange results for invalid inputs?</h2>\n          <p>\n            For performance reasons, three.js doesn't validate inputs in most cases. It's your app's responsibility to make sure that all inputs are valid.\n          </p>\n      \n          <h2>Can I use three.js in Node.js?</h2>\n          <p>\n            Because three.js is built for the web, it depends on browser and DOM APIs that don't always exist in Node.js. Some of these issues can be avoided by using shims like \n            <a href=\"https://github.com/stackgl/headless-gl\" target=\"_blank\">headless-gl</a> and <a href=\"https://github.com/rstacruz/jsdom-global\" target=\"_blank\">jsdom-global</a>, or by replacing components like <code class=\"notranslate\" translate=\"no\">TextureLoader</code> \n            with custom alternatives. Other DOM APIs may be deeply intertwined with the code that uses them, and will be harder to work around. We welcome simple and maintainable pull \n            requests to improve Node.js support, but recommend opening an issue to discuss your improvements first.\n          </p>\n\n        </div>\n      </div>\n    </div>",
    "success": true,
    "error": "",
    "crawled_at": 1786376809.300204,
    "is_shell": false
  },
  {
    "url": "https://threejs.org/manual/en/libraries-and-plugins.html",
    "title": "Libraries and Plugins",
    "content": "<div class=\"container\">\n      <div class=\"lesson-title\">\n        <h1>Libraries and Plugins</h1>\n      </div>\n      <div class=\"lesson\">\n        <div class=\"lesson-main\">\n          \n          <p class=\"desc\">\n            Listed here are externally developed compatible libraries and plugins for three.js. This\n            list and the associated packages are maintained by the community and not guaranteed\n            to be up to date. If you'd like to update this list make a PR!\n          </p>\n      \n          <h3>Physics</h3>\n      \n          <ul>\n            <li><a href=\"https://github.com/lo-th/Oimo.js/\" target=\"_blank\">Oimo.js</a></li>\n            <li><a href=\"https://enable3d.io/\" target=\"_blank\">enable3d</a></li>\n            <li><a href=\"https://github.com/kripken/ammo.js/\" target=\"_blank\">ammo.js</a></li>\n            <li><a href=\"https://github.com/pmndrs/cannon-es\" target=\"_blank\">cannon-es</a></li>\n            <li><a href=\"https://rapier.rs/\" target=\"_blank\">rapier</a></li>\n            <li><a href=\"https://github.com/jrouwe/JoltPhysics.js\" target=\"_blank\">Jolt</a></li>\n            \n          </ul>\n      \n          <h3>Postprocessing</h3>\n      \n          <p>\n            In addition to the <a href=\"https://github.com/mrdoob/three.js/tree/dev/examples/jsm/postprocessing\" target=\"_blank\">official three.js postprocessing effects</a>,\n            support for some additional effects and frameworks are available through external libraries.\n          </p>\n      \n          <ul>\n            <li><a href=\"https://github.com/vanruesc/postprocessing\" target=\"_blank\">postprocessing</a></li>\n          </ul>\n      \n          <h3>Intersection and Raycast Performance</h3>\n      \n          <ul>\n            <li><a href=\"https://github.com/gkjohnson/three-mesh-bvh\" target=\"_blank\">three-mesh-bvh</a></li>\n          </ul>\n      \n          <h3>Path Tracing</h3>\n          \n          <ul>\n            <li><a href=\"https://github.com/gkjohnson/three-gpu-pathtracer\" target=\"_blank\">three-gpu-pathtracer</a></li>\n          </ul>\n          \n          <h3>File Formats</h3>\n      \n          <p>\n            In addition to the <a href=\"https://github.com/mrdoob/three.js/tree/dev/examples/jsm/loaders\" target=\"_blank\">official three.js loaders</a>,\n            support for some additional formats is available through external libraries.\n          </p>\n      \n          <ul>\n            <li><a href=\"https://github.com/gkjohnson/urdf-loaders/tree/master/javascript\" target=\"_blank\">urdf-loader</a></li>\n            <li><a href=\"https://github.com/NASA-AMMOS/3DTilesRendererJS\" target=\"_blank\">3d-tiles-renderer-js</a></li>\n            <li><a href=\"https://github.com/kaisalmen/WWOBJLoader\" target=\"_blank\">WebWorker OBJLoader</a></li>\n            <li><a href=\"https://github.com/IFCjs/web-ifc-three\" target=\"_blank\">IFC.js</a></li>\n          </ul>\n      \n          <h3>Geometry</h3>\n      \n          <ul>\n            <li><a href=\"https://github.com/spite/THREE.MeshLine\" target=\"_blank\">THREE.MeshLine</a></li>\n          </ul>\n      \n          <h3>3D Text and Layout</h3>\n      \n          <ul>\n            <li><a href=\"https://github.com/protectwise/troika/tree/master/packages/troika-three-text\" target=\"_blank\">troika-three-text</a></li>\n            <li><a href=\"https://github.com/felixmariotto/three-mesh-ui\" target=\"_blank\">three-mesh-ui</a></li>\n          </ul>\n      \n          <h3>Particle Systems</h3>\n      \n          <ul>\n            <li><a href=\"https://github.com/Alchemist0823/three.quarks\" target=\"_blank\">three.quarks</a></li>\n            <li><a href=\"https://github.com/creativelifeform/three-nebula\" target=\"_blank\">three-nebula</a></li>\n          </ul>\n      \n          <h3>Inverse Kinematics</h3>\n      \n          <ul>\n            <li><a href=\"https://github.com/jsantell/THREE.IK\" target=\"_blank\">THREE.IK</a></li>\n            <li><a href=\"https://github.com/lo-th/fullik\" target=\"_blank\">fullik</a></li>\n            <li><a href=\"https://github.com/gkjohnson/closed-chain-ik-js\" target=\"_blank\">closed-chain-ik</a></li>\n          </ul>\n      \n          <h3>Game AI</h3>\n      \n          <ul>\n            <li><a href=\"https://mugen87.github.io/yuka/\" target=\"_blank\">yuka</a></li>\n            <li><a href=\"https://github.com/donmccurdy/three-pathfinding\" target=\"_blank\">three-pathfinding</a></li>\n            <li><a href=\"https://github.com/isaac-mason/recast-navigation-js\" target=\"_blank\">recast-navigation-js</a></li>\n          </ul>\n      \n          <h3>Wrappers and Frameworks</h3>\n      \n          <ul>\n            <li><a href=\"https://aframe.io/\" target=\"_blank\">A-Frame</a></li>\n            <li><a href=\"https://lume.io/\" target=\"_blank\">Lume</a> - HTML elements for 3D graphics built on Three.</li>\n            <li><a href=\"https://github.com/pmndrs/react-three-fiber\" target=\"_blank\">react-three-fiber</a> - React components for 3D graphics built on Three.</li>\n            <li><a href=\"https://threepipe.org/\" target=\"_blank\">threepipe</a> - A versatile 3D viewer framework using three.js for rendering.</li>\n            <li><a href=\"https://github.com/ecsyjs/ecsy-three\" target=\"_blank\">ECSY</a></li>\n            <li><a href=\"https://threlte.xyz/\" target=\"_blank\">Threlte</a> - Svelte components for 3D graphics built on Three.</li>\n            <li><a href=\"https://needle.tools/\" target=\"_blank\">Needle Engine</a></li>\n            <li><a href=\"https://tresjs.org/\" target=\"_blank\">tresjs</a> - Vue components for 3D graphics built on Three.</li>\n            <li><a href=\"https://giro3d.org\" target=\"_blank\">Giro3D</a> - Versatile framework built on Three for visualizing and interacting with Geospatial 2D, 2.5D and 3D data.</li>\n            <li><a href=\"https://zap.works/mattercraft/\" target=\"_blank\">Mattercraft</a> - Browser-based visual editor for AR, WebXR and 3D web content, built on three.js with real-time preview and physics engine.</li>\n          </ul>\n\n        </div>\n      </div>\n    </div>",
    "success": true,
    "error": "",
    "crawled_at": 1786376809.300204,
    "is_shell": false
  },
  {
    "url": "https://threejs.org/manual/en/loading-3d-models.html",
    "title": "Loading 3D Models",
    "content": "<div class=\"container\">\n      <div class=\"lesson-title\">\n        <h1>Loading 3D Models</h1>\n      </div>\n      <div class=\"lesson\">\n        <div class=\"lesson-main\">\n          \n          <p>\n            3D models are available in hundreds of file formats, each with different\n            purposes, assorted features, and varying complexity. Although\n            <a href=\"https://github.com/mrdoob/three.js/tree/dev/examples/jsm/loaders\" target=\"_blank\" rel=\"noopener\">\n            three.js provides many loaders</a>, choosing the right format and\n            workflow will save time and frustration later on. Some formats are\n            difficult to work with, inefficient for realtime experiences, or simply not\n            fully supported at this time.\n          </p>\n        \n          <p>\n            This guide provides a workflow recommended for most users, and suggestions\n            for what to try if things don't go as expected.\n          </p>\n        \n          <h2>Before we start</h2>\n        \n          <p>\n            If you're new to running a local server, begin with\n            <a href=\"installation.html\">Installation</a>\n            first. Many common errors viewing 3D models can be avoided by hosting files\n            correctly.\n          </p>\n        \n          <h2>Recommended workflow</h2>\n        \n          <p>\n            Where possible, we recommend using glTF (GL Transmission Format). Both\n            <small>.GLB</small> and <small>.GLTF</small> versions of the format are\n            well supported. Because glTF is focused on runtime asset delivery, it is\n            compact to transmit and fast to load. Features include meshes, materials,\n            textures, skins, skeletons, morph targets, animations, lights, and\n            cameras.\n          </p>\n        \n          <p>\n            Public-domain glTF files are available on sites like\n            <a href=\"https://sketchfab.com/models?features=downloadable&amp;sort_by=-likeCount&amp;type=models\" target=\"_blank\" rel=\"noopener\">\n            Sketchfab</a>, or various tools include glTF export:\n          </p>\n        \n          <ul>\n            <li><a href=\"https://www.blender.org/\" target=\"_blank\" rel=\"noopener\">Blender</a> by the Blender Foundation</li>\n            <li><a href=\"https://www.allegorithmic.com/products/substance-painter\" target=\"_blank\" rel=\"noopener\">Substance Painter</a> by Allegorithmic</li>\n            <li><a href=\"https://www.foundry.com/products/modo\" target=\"_blank\" rel=\"noopener\">Modo</a> by Foundry</li>\n            <li><a href=\"https://www.marmoset.co/toolbag/\" target=\"_blank\" rel=\"noopener\">Toolbag</a> by Marmoset</li>\n            <li><a href=\"https://www.sidefx.com/products/houdini/\" target=\"_blank\" rel=\"noopener\">Houdini</a> by SideFX</li>\n            <li><a href=\"https://labs.maxon.net/?p=3360\" target=\"_blank\" rel=\"noopener\">Cinema 4D</a> by MAXON</li>\n            <li><a href=\"https://github.com/KhronosGroup/COLLADA2GLTF\" target=\"_blank\" rel=\"noopener\">COLLADA2GLTF</a> by the Khronos Group</li>\n            <li><a href=\"https://github.com/facebookincubator/FBX2glTF\" target=\"_blank\" rel=\"noopener\">FBX2GLTF</a> by Facebook</li>\n            <li><a href=\"https://github.com/AnalyticalGraphicsInc/obj2gltf\" target=\"_blank\" rel=\"noopener\">OBJ2GLTF</a> by Analytical Graphics Inc</li>\n            <li>…and <a href=\"http://github.khronos.org/glTF-Project-Explorer/\" target=\"_blank\" rel=\"noopener\">many more</a></li>\n          </ul>\n        \n          <p>\n            If your preferred tools do not support glTF, consider requesting glTF\n            export from the authors, or posting on\n            <a href=\"https://github.com/KhronosGroup/glTF/issues/1051\" target=\"_blank\" rel=\"noopener\">the glTF roadmap thread</a>.\n          </p>\n        \n          <p>\n            When glTF is not an option, popular formats such as FBX, OBJ, or COLLADA\n            are also available and regularly maintained.\n          </p>\n        \n          <h2>Loading</h2>\n        \n          <p>\n            Only a few loaders (e.g. <code class=\"notranslate\" translate=\"no\">ObjectLoader</code>) are included by default with\n            three.js — others should be added to your app individually.\n          </p>\n        \n<pre class=\"prettyprint notranslate lang-js\" translate=\"no\">import { GLTFLoader } from 'three/addons/loaders/GLTFLoader.js';\n</pre>\n\n          <p>\n            Once you've imported a loader, you're ready to add a model to your scene. Syntax varies among\n            different loaders — when using another format, check the examples and documentation for that\n            loader. For glTF, usage with global scripts would be:\n          </p>\n        \n<pre class=\"prettyprint notranslate lang-js\" translate=\"no\">const loader = new GLTFLoader();\n\nloader.load( 'path/to/model.glb', function ( gltf ) {\n\n  scene.add( gltf.scene );\n\n}, undefined, function ( error ) {\n\n  console.error( error );\n\n} );\n</pre>\n        \n          <h2>Troubleshooting</h2>\n        \n          <p>\n            You've spent hours modeling an artisanal masterpiece, you load it into\n            the webpage, and — oh no! 😭 It's distorted, miscolored, or missing entirely.\n            Start with these troubleshooting steps:\n          </p>\n        \n          <ol>\n            <li>\n              Check the JavaScript console for errors, and make sure you've used an\n              <code class=\"notranslate\" translate=\"no\">onError</code> callback when calling <code class=\"notranslate\" translate=\"no\">.load()</code> to log the result.\n            </li>\n            <li>\n              View the model in another application. For glTF, drag-and-drop viewers\n              are available for\n              <a href=\"https://gltf-viewer.donmccurdy.com/\" target=\"_blank\" rel=\"noopener\">three.js</a> and\n              <a href=\"https://sandbox.babylonjs.com/\" target=\"_blank\" rel=\"noopener\">babylon.js</a>. If the model\n              appears correctly in one or more applications,\n              <a href=\"https://github.com/mrdoob/three.js/issues/new\" target=\"_blank\" rel=\"noopener\">file a bug against three.js</a>.\n              If the model cannot be shown in any application, we strongly encourage\n              filing a bug with the application used to create the model.\n            </li>\n            <li>\n              Try scaling the model up or down by a factor of 1000. Many models are\n              scaled differently, and large models may not appear if the camera is\n              inside the model.\n            </li>\n            <li>\n              Try to add and position a light source. The model may be hidden in the dark.\n            </li>\n            <li>\n              Look for failed texture requests in the network tab, like\n              <code class=\"notranslate\" translate=\"no\">\"C:\\\\Path\\To\\Model\\texture.jpg\"</code>. Use paths relative to your\n              model instead, such as <code class=\"notranslate\" translate=\"no\">images/texture.jpg</code> — this may require\n              editing the model file in a text editor.\n            </li>\n          </ol>\n        \n          <h2>Asking for help</h2>\n        \n          <p>\n            If you've gone through the troubleshooting process above and your model\n            still isn't working, the right approach to asking for help will get you to\n            a solution faster. Post a question on the\n            <a href=\"https://discourse.threejs.org/\" target=\"_blank\" rel=\"noopener\">three.js forum</a> and, whenever possible,\n            include your model (or a simpler model with the same problem) in any formats\n            you have available. Include enough information for someone else to reproduce\n            the issue quickly — ideally, a live demo.\n          </p>\n\n        </div>\n      </div>\n    </div>",
    "success": true,
    "error": "",
    "crawled_at": 1786376809.300204,
    "is_shell": false
  },
  {
    "url": "https://threejs.org/manual/en/uniform-types.html",
    "title": "Uniform Types",
    "content": "<div class=\"container\">\n      <div class=\"lesson-title\">\n        <h1>Uniform Types</h1>\n      </div>\n      <div class=\"lesson\">\n        <div class=\"lesson-main\">\n          \n          <p>\n            Each uniform must have a <code class=\"notranslate\" translate=\"no\">value</code> property. The type of the value must\n            correspond to the type of the uniform variable in the GLSL code as\n            specified for the primitive GLSL types in the table below. Uniform\n            structures and arrays are also supported. GLSL arrays of primitive type\n            must either be specified as an array of the corresponding THREE objects or\n            as a flat array containing the data of all the objects. In other words;\n            GLSL primitives in arrays must not be represented by arrays. This rule\n            does not apply transitively. An array of <code class=\"notranslate\" translate=\"no\">vec2</code> arrays, each with a length\n            of five vectors, must be an array of arrays, of either five <code class=\"notranslate\" translate=\"no\">Vector2</code>\n            objects or ten <code class=\"notranslate\" translate=\"no\">number</code>s.\n          </p>\n      \n          <table>\n            <thead>\n              <tr>\n                <th>GLSL type</th>\n                <th>JavaScript type</th>\n              </tr>\n            </thead>\n            <tbody>\n              <tr>\n                <td>int</td>\n                <td>Number</td>\n              </tr>\n              <tr>\n                <td>uint</td>\n                <td>Number</td>\n              </tr>\n              <tr>\n                <td>float</td>\n                <td>Number</td>\n              </tr>\n              <tr>\n                <td>bool</td>\n                <td>Boolean</td>\n              </tr>\n              <tr>\n                <td>bool</td>\n                <td>Number</td>\n              </tr>\n              <tr>\n                <td>vec2</td>\n                <td>Vector2</td>\n              </tr>\n              <tr>\n                <td>vec2</td>\n                <td>Float32Array (*)</td>\n              </tr>\n              <tr>\n                <td>vec2</td>\n                <td>Array (*)</td>\n              </tr>\n              <tr>\n                <td>vec3</td>\n                <td>Vector3</td>\n              </tr>\n              <tr>\n                <td>vec3</td>\n                <td>Color</td>\n              </tr>\n              <tr>\n                <td>vec3</td>\n                <td>Float32Array (*)</td>\n              </tr>\n              <tr>\n                <td>vec3</td>\n                <td>Array (*)</td>\n              </tr>\n              <tr>\n                <td>vec4</td>\n                <td>Vector4</td>\n              </tr>\n              <tr>\n                <td>vec4</td>\n                <td>Quaternion</td>\n              </tr>\n              <tr>\n                <td>vec4</td>\n                <td>Float32Array (*)</td>\n              </tr>\n              <tr>\n                <td>vec4</td>\n                <td>Array (*)</td>\n              </tr>\n              <tr>\n                <td>mat2</td>\n                <td>Float32Array (*)</td>\n              </tr>\n              <tr>\n                <td>mat2</td>\n                <td>Array (*)</td>\n              </tr>\n              <tr>\n                <td>mat3</td>\n                <td>Matrix3</td>\n              </tr>\n              <tr>\n                <td>mat3</td>\n                <td>Float32Array (*)</td>\n              </tr>\n              <tr>\n                <td>mat3</td>\n                <td>Array (*)</td>\n              </tr>\n              <tr>\n                <td>mat4</td>\n                <td>Matrix4</td>\n              </tr>\n              <tr>\n                <td>mat4</td>\n                <td>Float32Array (*)</td>\n              </tr>\n              <tr>\n                <td>mat4</td>\n                <td>Array (*)</td>\n              </tr>\n              <tr>\n                <td>ivec2, bvec2</td>\n                <td>Float32Array (*)</td>\n              </tr>\n              <tr>\n                <td>ivec2, bvec2</td>\n                <td>Array (*)</td>\n              </tr>\n              <tr>\n                <td>ivec3, bvec3</td>\n                <td>Int32Array (*)</td>\n              </tr>\n              <tr>\n                <td>ivec3, bvec3</td>\n                <td>Array (*)</td>\n              </tr>\n              <tr>\n                <td>ivec4, bvec4</td>\n                <td>Int32Array (*)</td>\n              </tr>\n              <tr>\n                <td>ivec4, bvec4</td>\n                <td>Array (*)</td>\n              </tr>\n              <tr>\n                <td>sampler2D</td>\n                <td>Texture</td>\n              </tr>\n              <tr>\n                <td>samplerCube</td>\n                <td>CubeTexture</td>\n              </tr>\n            </tbody>\n          </table>\n      \n          <p>\n            (*) Same for an (innermost) array (dimension) of the same GLSL type,\n            containing the components of all vectors or matrices in the array.\n          </p>\n      \n          <h2>Structured Uniforms</h2>\n      \n          <p>\n            Sometimes you want to organize uniforms as <code class=\"notranslate\" translate=\"no\">structs</code> in your shader code.\n            The following style must be used so <code class=\"notranslate\" translate=\"no\">three.js</code> is able to process\n            structured uniform data.\n          </p>\n<pre class=\"prettyprint notranslate lang-js\" translate=\"no\">uniforms = {\n  data: { \n    value: {\n      position: new Vector3(), \n      direction: new Vector3( 0, 0, 1 ) \n    } \n  } \n};\n</pre>\n          This definition can be mapped on the following GLSL code:\n<pre class=\"prettyprint notranslate lang-js\" translate=\"no\">struct Data { \n  vec3 position;\n  vec3 direction;\n};\nuniform Data data;\n</pre>\n      \n          <h2>Structured Uniforms with Arrays</h2>\n      \n          <p>\n            It's also possible to manage <code class=\"notranslate\" translate=\"no\">structs</code> in arrays. The syntax for this use\n            case looks like so:\n          </p>\n<pre class=\"prettyprint notranslate lang-js\" translate=\"no\">const entry1 = {\n  position: new Vector3(),\n  direction: new Vector3( 0, 0, 1 )\n};\nconst entry2 = {\n  position: new Vector3( 1, 1, 1 ),\n  direction: new Vector3( 0, 1, 0 )\n};\n\nuniforms = {\n  data: {\n    value: [ entry1, entry2 ]\n  }\n};\n</pre>\n          This definition can be mapped on the following GLSL code:\n<pre class=\"prettyprint notranslate lang-js\" translate=\"no\">struct Data { \n  vec3 position; \n  vec3 direction; \n};\nuniform Data data[ 2 ];\n</pre>\n\n        </div>\n      </div>\n    </div>",
    "success": true,
    "error": "",
    "crawled_at": 1786376809.300204,
    "is_shell": false
  },
  {
    "url": "https://threejs.org/manual/en/useful-links.html",
    "title": "Useful Links",
    "content": "<div class=\"container\">\n      <div class=\"lesson-title\">\n        <h1>Useful Links</h1>\n      </div>\n      <div class=\"lesson\">\n        <div class=\"lesson-main\">\n          \n          <p class=\"desc\">\n            The following is a collection of links that you might find useful when learning three.js.<br>\n            If you find something that you'd like to add here, or think that one of the links below is no longer\n            relevant or working, feel free to click the 'edit' button in the bottom right and make some changes!<br><br>\n      \n            Note also that as three.js is under rapid development, a lot of these links will contain information that is\n            out of date - if something isn't working as you'd expect or as one of these links says it should,\n            check the browser console for warnings or errors. Also check the relevant docs pages.\n          </p>\n      \n          <h2>Help forums</h2>\n          <p>\n            Three.js officially uses the <a href=\"https://discourse.threejs.org/\" target=\"_blank\">forum</a> and <a href=\"http://stackoverflow.com/tags/three.js/info\" target=\"_blank\">Stack Overflow</a> for help requests.\n            If you need assistance with something, that's the place to go. Do NOT open an issue on Github for help requests.\n          </p>\n      \n          <h2>Tutorials and courses</h2>\n      \n          <h3>Getting started with three.js</h3>\n          <ul>\n            <li>\n              <a href=\"https://threejs.org/manual/#en/fundamentals\" target=\"_blank\">Three.js Fundamentals starting lesson</a>\n            </li>\n            <li>\n              <a href=\"https://codepen.io/rachsmith/post/beginning-with-3d-webgl-pt-1-the-scene\" target=\"_blank\">Beginning with 3D WebGL</a> by <a href=\"https://codepen.io/rachsmith/\" target=\"_blank\">Rachel Smith</a>.\n            </li>\n            <li>\n              <a href=\"https://www.august.com.au/blog/animating-scenes-with-webgl-three-js/\" target=\"_blank\">Animating scenes with WebGL and three.js</a>\n            </li>\n          </ul>\n      \n          <h3>More extensive / advanced articles and courses</h3>\n          <ul>\n            <li>\n              <a href=\"https://threejs-journey.com/\" target=\"_blank\">Three Journey</a> Course by <a href=\"https://bruno-simon.com/\" target=\"_blank\">Bruno Simon</a> - Teaches beginners how to use Three.js step by step\n            </li>\n            <li>\n              <a href=\"https://discoverthreejs.com/\" target=\"_blank\">Discover three.js</a>\n            </li>\n            <li>\n              <a href=\"http://blog.cjgammon.com/\" target=\"_blank\">Collection of tutorials</a> by <a href=\"http://www.cjgammon.com/\" target=\"_blank\">CJ Gammon</a>.\n            </li>\n            <li>\n              <a href=\"https://medium.com/soffritti.pierfrancesco/glossy-spheres-in-three-js-bfd2785d4857\" target=\"_blank\">Glossy spheres in three.js</a>.\n            </li>\n           <li>\n             <a href=\"https://www.udacity.com/course/interactive-3d-graphics--cs291\" target=\"_blank\">Interactive 3D Graphics</a> - a free course on Udacity that teaches the fundamentals of 3D Graphics,\n             and uses three.js as its coding tool.\n           </li>\n           <li>\n            <a href=\"https://aerotwist.com/tutorials/\" target=\"_blank\">Aerotwist</a> tutorials by <a href=\"https://github.com/paullewis/\" target=\"_blank\">Paul Lewis</a>.\n           </li>\n           <li>\n             <a href=\"https://discourse.threejs.org/t/three-js-bookshelf/2468\" target=\"_blank\">Three.js Bookshelf</a> - Looking for more resources about three.js or computer graphics in general?\n             Check out the selection of literature recommended by the community.\n           </li>\n          </ul>\n      \n          <h2>News and Updates</h2>\n          <ul>\n            <li>\n              <a href=\"https://twitter.com/hashtag/threejs\" target=\"_blank\">Three.js on Twitter</a>\n            </li>\n            <li>\n              <a href=\"http://www.reddit.com/r/threejs/\" target=\"_blank\">Three.js on reddit</a>\n            </li>\n            <li>\n              <a href=\"http://www.reddit.com/r/webgl/\" target=\"_blank\">WebGL on reddit</a>\n            </li>\n          </ul>\n      \n          <h2>Examples</h2>\n          <ul>\n            <li>\n              <a href=\"https://github.com/edwinwebb/three-seed/\" target=\"_blank\">three-seed</a> - three.js starter project with ES6 and Webpack\n            </li>\n            <li>\n              <a href=\"http://stemkoski.github.io/Three.js/index.html\" target=\"_blank\">Professor Stemkoskis Examples</a> - a collection of beginner friendly\n              examples built using three.js r60.\n            </li>\n            <li>\n              <a href=\"https://threejs.org/examples/\" target=\"_blank\">Official three.js examples</a> - these examples are\n              maintained as part of the three.js repository, and always use the latest version of three.js.\n            </li>\n            <li>\n              <a href=\"https://raw.githack.com/mrdoob/three.js/dev/examples/\" target=\"_blank\">Official three.js dev branch examples</a>  -\n              Same as the above, except these use the dev branch of three.js,\tand are used to check that\n              everything is working as three.js being is developed.\n            </li>\n          </ul>\n      \n        <h2>Tools</h2>\n        <ul>\n          <li>\n            <a href=\"https://github.com/tbensky/physgl\" target=\"_blank\">physgl.org</a> - JavaScript front-end with wrappers to three.js, to bring WebGL\n            graphics to students learning physics and math.\n          </li>\n          <li>\n            <a href=\"https://whsjs.readme.io/\" target=\"_blank\">Whitestorm.js</a> – Modular three.js framework with AmmoNext physics plugin.\n          </li>\n          <li>\n            <a href=\"http://zz85.github.io/zz85-bookmarklets/threelabs.html\" target=\"_blank\">Three.js Inspector</a>\n          </li>\n          <li>\n            <a href=\"http://idflood.github.io/ThreeNodes.js/\" target=\"_blank\">ThreeNodes.js</a>.\n          </li>\n          <li>\n            <a href=\"https://marketplace.visualstudio.com/items?itemName=slevesque.shader\" target=\"_blank\">vscode shader</a> - Syntax highlighter for shader language.\n            <br>\n            <a href=\"https://marketplace.visualstudio.com/items?itemName=bierner.comment-tagged-templates\" target=\"_blank\">vscode comment-tagged-templates</a> - Syntax highlighting for tagged template strings using comments to shader language, like: glsl.js.\n          </li>\n          <li>\n            <a href=\"https://github.com/MozillaReality/WebXR-emulator-extension\" target=\"_blank\">WebXR-emulator-extension</a>\n          </li>\n        </ul>\n      \n        <h2>WebGL References</h2>\n          <ul>\n            <li>\n            <a href=\"https://www.khronos.org/files/webgl/webgl-reference-card-1_0.pdf\" target=\"_blank\">webgl-reference-card.pdf</a> - Reference of all WebGL and GLSL keywords, terminology, syntax and definitions.\n            </li>\n          </ul>\n      \n        <h2>Old Links</h2>\n        <p>\n          These links are kept for historical purposes - you may still find them useful, but be warned that\n          they may have information relating to very old versions of three.js.\n        </p>\n      \n        <ul>\n          <li>\n            <a href=\"https://www.youtube.com/watch?v=Dir4KO9RdhM\" target=\"_blank\">AlterQualia at WebGL Camp 3</a>\n          </li>\n          <li>\n            <a href=\"http://yomotsu.github.io/threejs-examples/\" target=\"_blank\">Yomotsus Examples</a> - a collection of examples using three.js r45.\n          </li>\n          <li>\n            <a href=\"http://fhtr.org/BasicsOfThreeJS/#1\" target=\"_blank\">Introduction to Three.js</a> by <a href=\"http://github.com/kig/\" target=\"_blank\">Ilmari Heikkinen</a> (slideshow).\n          </li>\n          <li>\n            <a href=\"http://www.slideshare.net/yomotsu/webgl-and-threejs\" target=\"_blank\">WebGL and Three.js</a> by <a href=\"http://github.com/yomotsu\" target=\"_blank\">Akihiro Oyamada</a> (slideshow).\n          </li>\n          <li>\n            <a href=\"https://www.youtube.com/watch?v=VdQnOaolrPA\" target=\"_blank\">Trigger Rally</a>  by <a href=\"https://github.com/jareiko\" target=\"_blank\">jareiko</a> (video).\n          </li>\n          <li>\n            <a href=\"http://blackjk3.github.io/threefab/\" target=\"_blank\">ThreeFab</a> - scene editor, maintained up until around three.js r50.\n          </li>\n          <li>\n            <a href=\"http://bkcore.com/blog/3d/webgl-three-js-workflow-tips.html\" target=\"_blank\">Max to Three.js workflow tips and tricks</a> by <a href=\"https://github.com/BKcore\" target=\"_blank\">BKcore</a>\n          </li>\n          <li>\n            <a href=\"http://12devsofxmas.co.uk/2012/01/webgl-and-three-js/\" target=\"_blank\">A whirlwind look at Three.js</a>\n            by <a href=\"http://github.com/nrocy\" target=\"_blank\">Paul King</a>\n          </li>\n          <li>\n            <a href=\"http://bkcore.com/blog/3d/webgl-three-js-animated-selective-glow.html\" target=\"_blank\">Animated selective glow in Three.js</a>\n            by <a href=\"https://github.com/BKcore\" target=\"_blank\">BKcore</a>\n          </li>\n          <li>\n            <a href=\"http://www.natural-science.or.jp/article/20120220155529.php\" target=\"_blank\">Building A Physics Simulation Environment</a> - three.js tutorial in Japanese\n          </li>\n         </ul>\n\n        </div>\n      </div>\n    </div>",
    "success": true,
    "error": "",
    "crawled_at": 1786376809.300204,
    "is_shell": false
  },
  {
    "url": "https://threejs.org/manual/en/webgl-compatibility-check.html",
    "title": "WebGL Compatibility Check",
    "content": "<div class=\"container\">\n      <div class=\"lesson-title\">\n        <h1>WebGL Compatibility Check</h1>\n      </div>\n      <div class=\"lesson\">\n        <div class=\"lesson-main\">\n          \n          <p>\n            Even though this is becoming less and less of a problem, some devices or browsers may still not support WebGL 2.\n            The following method allows you to check if it is supported and display a message to the user if it is not.\n            Import the WebGL support detection module, and run the following before attempting to render anything.\n          </p>\n      \n<pre class=\"prettyprint notranslate lang-js\" translate=\"no\">import WebGL from 'three/addons/capabilities/WebGL.js';\n\nif ( WebGL.isWebGL2Available() ) {\n\n  // Initiate function or other initializations here\n  animate();\n\n} else {\n\n  const warning = WebGL.getWebGL2ErrorMessage();\n  document.getElementById( 'container' ).appendChild( warning );\n\n}\n</pre>\n          \n        </div>\n      </div>\n    </div>",
    "success": true,
    "error": "",
    "crawled_at": 1786376809.300204,
    "is_shell": false
  },
  {
    "url": "https://threejs.org/manual/en/animation-system.html",
    "title": "Animation System",
    "content": "<div class=\"container\">\n      <div class=\"lesson-title\">\n        <h1>Animation System</h1>\n      </div>\n      <div class=\"lesson\">\n        <div class=\"lesson-main\">\n \n          <h2>Overview</h2>\n\n\t\t<p class=\"desc\">\n\t\t\tWithin the three.js animation system you can animate various properties of your models:\n\t\t\tthe bones of a skinned and rigged model, morph targets, different material properties\n\t\t\t(colors, opacity, booleans), visibility and transforms. The animated properties can be faded in,\n\t\t\tfaded out, crossfaded and warped. The weight and time scales of different simultaneous\n\t\t\tanimations on the same object as well as on different objects can be changed\n\t\t\tindependently. Various animations on the same and on different objects can be\n\t\t\tsynchronized.<br><br>\n\n\t\t\tTo achieve all this in one homogeneous system, the three.js animation system\n\t\t\t<a href=\"https://github.com/mrdoob/three.js/issues/6881\" target=\"_blank\">has completely changed in 2015</a>\n\t\t\t(beware of outdated information!), and it has now an architecture similar to\n\t\t\tUnity/Unreal Engine 4. This page gives a short overview of the main components of the\n\t\t\tsystem and how they work together.\n\n\t\t</p>\n\n\t\t<h3>Animation Clips</h3>\n\n\t\t<p class=\"desc\">\n\n\t\t\tIf you have successfully imported an animated 3D object (it doesn't matter if it has\n\t\t\tbones or morph targets or both) — for example exporting it from Blender with the\n\t\t\t<a href=\"https://github.com/KhronosGroup/glTF-Blender-IO\" target=\"_blank\">glTF Blender exporter</a> and\n\t\t\tloading it into a three.js scene using <code class=\"notranslate\" translate=\"no\">GLTFLoader</code> — one of the response fields\n\t\t\tshould be an array named \"animations\", containing the animation clips\n\t\t\tfor this model (see a list of possible loaders below).<br><br>\n\n\t\t\tEach <code class=\"notranslate\" translate=\"no\">AnimationClip</code> usually holds the data for a certain activity of the object. If the\n\t\t\tmesh is a character, for example, there may be one AnimationClip for a walkcycle, a second\n\t\t\tfor a jump, a third for sidestepping and so on.\n\n\t\t</p>\n\n\t\t<h3>Keyframe Tracks</h3>\n\n\t\t<p class=\"desc\">\n\n\t\t\tInside of such an <code class=\"notranslate\" translate=\"no\">AnimationClip</code> the data for each animated property are stored in a\n\t\t\tseparate <code class=\"notranslate\" translate=\"no\">KeyframeTrack</code>. Assuming a character object has a skeleton,\n\t\t\tone keyframe track could store the data for the position changes of the lower arm bone\n\t\t\tover time, a different track the data for the rotation changes of the same bone, a third\n\t\t\tthe track position, rotation or scaling of another bone, and so on. It should be clear,\n\t\t\tthat an AnimationClip can be composed of lots of such tracks.<br><br>\n\n\t\t\tAssuming the model has morph targets (for example one morph\n\t\t\ttarget showing a friendly face and another showing an angry face), each track holds the\n\t\t\tinformation as to how the influence of a certain morph target changes during the performance \n\t\t\tof the clip.\n\n\t\t</p>\n\n\t\t<h3>Animation Mixer</h3>\n\n\t\t<p class=\"desc\">\n\n\t\t\tThe stored data forms only the basis for the animations - actual playback is controlled by\n\t\t\tthe <code class=\"notranslate\" translate=\"no\">AnimationMixer</code>. You can imagine this not only as a player for animations, but\n\t\t\tas a simulation of a hardware like a real mixer console, which can control several animations\n\t\t\tsimultaneously, blending and merging them.\n\n\t\t</p>\n\n\t\t<h3>Animation Actions</h3>\n\n\t\t<p class=\"desc\">\n\n\t\t\tThe <code class=\"notranslate\" translate=\"no\">AnimationMixer</code> itself has only very few (general) properties and methods, because it\n\t\t\tcan be controlled by the animation actions. By configuring an\n\t\t\t<code class=\"notranslate\" translate=\"no\">AnimationAction</code> you can determine when a certain <code class=\"notranslate\" translate=\"no\">AnimationClip</code> shall be played, paused\n\t\t\tor stopped on one of the mixers, if and how often the clip has to be repeated, whether it\n\t\t\tshall be performed with a fade or a time scaling, and some additional things, such crossfading\n\t\t\tor synchronizing.\n\n\t\t</p>\n\n\t\t<h3>Animation Object Groups</h3>\n\n\t\t<p class=\"desc\">\n\n\t\t\tIf you want a group of objects to receive a shared animation state, you can use an\n\t\t\t<code class=\"notranslate\" translate=\"no\">AnimationObjectGroup</code>.\n\n\t\t</p>\n\n\t\t<h3>Supported Formats and Loaders</h3>\n\n\t\t<p class=\"desc\">\n\t\t\tNote that not all model formats include animation (OBJ notably does not), and that only some\n\t\t\tthree.js loaders support <code class=\"notranslate\" translate=\"no\">AnimationClip</code> sequences. Several that <i>do</i>\n\t\t\tsupport this animation type:\n\t\t</p>\n\n\t\t\t<ul>\n\t\t\t\t<li>THREE.ObjectLoader</li>\n\t\t\t\t<li>THREE.BVHLoader</li>\n\t\t\t\t<li>THREE.ColladaLoader</li>\n\t\t\t\t<li>THREE.FBXLoader</li>\n\t\t\t\t<li>THREE.GLTFLoader</li>\n\t\t\t</ul>\n\n\t\t<p class=\"desc\">\n\t\t\tNote that 3ds max and Maya currently can't export multiple animations (meaning animations which are not\n\t\t\ton the same timeline) directly to a single file.\n\t\t</p>\n\n\t\t<h2>Example</h2>\n\n<pre class=\"prettyprint notranslate lang-js\" translate=\"no\">let mesh;\n\n// Create an AnimationMixer, and get the list of AnimationClip instances\nconst mixer = new THREE.AnimationMixer( mesh );\nconst clips = mesh.animations;\n\n// Update the mixer on each frame\nfunction update () {\n  mixer.update( deltaSeconds );\n}\n\n// Play a specific animation\nconst clip = THREE.AnimationClip.findByName( clips, 'dance' );\nconst action = mixer.clipAction( clip );\naction.play();\n\n// Play all animations\nclips.forEach( function ( clip ) {\n  mixer.clipAction( clip ).play();\n} );\n</pre>\n         \n        </div>\n      </div>\n    </div>",
    "success": true,
    "error": "",
    "crawled_at": 1786376809.300204,
    "is_shell": false
  },
  {
    "url": "https://threejs.org/manual/en/color-management.html",
    "title": "Color Management",
    "content": "<div class=\"container\">\n      <div class=\"lesson-title\">\n        <h1>Color Management</h1>\n      </div>\n      <div class=\"lesson\">\n        <div class=\"lesson-main\">\n        \n\t\t\t<h2>What is a color space?</h2>\n\n\t<p>\n\t\tEvery color space is a collection of several design decisions, chosen together to support a\n\t\tlarge range of colors while satisfying technical constraints related to precision and display\n\t\ttechnologies. When creating a 3D asset, or assembling 3D assets together into a scene, it is\n\t\timportant to know what these properties are, and how the properties of one color space relate\n\t\tto other color spaces in the scene.\n\t</p>\n\n\t<figure class=\"float\">\n\t\t<img src=\"../resources/srgb_gamut.png\" alt=\"\">\n\t\t<figcaption>\n\t\t\tsRGB colors and white point (D65) displayed in the reference CIE 1931 chromaticity\n\t\t\tdiagram. Colored region represents a 2D projection of the sRGB gamut, which is a 3D\n\t\t\tvolume. Source: <a href=\"https://en.wikipedia.org/wiki/SRGB\" target=\"_blank\" rel=\"noopener\">Wikipedia</a>\n\t\t</figcaption>\n\t</figure>\n\n\t<ul>\n\t\t<li>\n\t\t\t<b>Color primaries:</b> Primary colors (e.g. red, green, blue) are not absolutes; they are\n\t\t\tselected from the visible spectrum based on constraints of limited precision and\n\t\t\tcapabilities of available display devices. Colors are expressed as a ratio of the primary colors.\n\t\t</li>\n\t\t<li>\n\t\t\t<b>White point:</b> Most color spaces are engineered such that an equally weighted sum of\n\t\t\tprimaries <i>R = G = B</i> will appear to be without color, or \"achromatic\". The appearance\n\t\t\tof achromatic values (like white or grey) depend on human perception, which in turn depends\n\t\t\theavily on the context of the observer. A color space specifies its \"white point\" to balance\n\t\t\tthese needs. The white point defined by the sRGB color space is\n\t\t\t<a href=\"https://en.wikipedia.org/wiki/Illuminant_D65\" target=\"_blank\">D65</a>.\n\t\t</li>\n\t\t<li>\n\t\t\t<b>Transfer functions:</b> After choosing the color gamut and a color model, we still need to\n\t\t\tdefine mappings (\"transfer functions\") of numerical values to/from the color space. Does <i>r = 0.5</i>\n\t\t\trepresent 50% less physical illumination than <i>r = 1.0</i>? Or 50% less bright, as perceived\n\t\t\tby an average human eye? These are different things, and that difference can be represented as\n\t\t\ta mathematical function. Transfer functions may be <i>linear</i> or <i>nonlinear</i>, depending\n\t\t\ton the objectives of the color space. sRGB defines nonlinear transfer functions. Those\n\t\t\tfunctions are sometimes approximated as <i>gamma functions</i>, but the term \"gamma\" is\n\t\t\tambiguous and should be avoided in this context.\n\t\t</li>\n\t</ul>\n\n\tThese three parameters — color primaries, white point, and transfer functions — define a color\n\tspace, with each chosen for particular goals. Having defined the parameters, a few additional terms\n\tare helpful:\n\n\t<ul>\n\t\t<li>\n\t\t\t<b>Color model:</b> Syntax for numerically identifying colors within chosen the color gamut —\n\t\t\ta coordinate system for colors. In three.js we're mainly concerned with the RGB color\n\t\t\tmodel, having three coordinates <i>r, g, b ∈ [0,1]</i> (\"closed domain\") or\n\t\t\t<i>r, g, b ∈ [0,∞]</i> (\"open domain\") each representing a fraction of a primary\n\t\t\tcolor. Other color models (HSL, Lab, LCH) are commonly used for artistic control.\n\t\t</li>\n\t\t<li>\n\t\t\t<b>Color gamut:</b> Once color primaries and a white point have been chosen, these represent\n\t\t\ta volume within the visible spectrum (a \"gamut\"). Colors not within this volume (\"out of gamut\")\n\t\t\tcannot be expressed by closed domain [0,1] RGB values. In the open domain [0,∞], the gamut is\n\t\t\ttechnically infinite.\n\t\t</li>\n\t</ul>\n\n\t<p>\n\t\tConsider two very common color spaces: <code class=\"notranslate\" translate=\"no\">SRGBColorSpace</code> (\"sRGB\") and\n\t\t<code class=\"notranslate\" translate=\"no\">LinearSRGBColorSpace</code> (\"Linear-sRGB\"). Both use the same primaries and white point,\n\t\tand therefore have the same color gamut. Both use the RGB color model. They differ only in\n\t\tthe transfer functions — Linear-sRGB is linear with respect to physical light intensity.\n\t\tsRGB uses the nonlinear sRGB transfer functions, and more closely resembles the way that\n\t\tthe human eye perceives light and the responsiveness of common display devices.\n\t</p>\n\n\t<p>\n\t\tThat difference is important. Lighting calculations and other rendering operations must\n\t\tgenerally occur in a linear color space. However, a linear colors are less efficient to\n\t\tstore in an image or framebuffer, and do not look correct when viewed by a human observer.\n\t\tAs a result, input textures and the final rendered image will generally use the nonlinear\n\t\tsRGB color space.\n\t</p>\n\n\t<blockquote>\n\t\t<p>\n\t\t\tℹ️ <i><b>NOTICE:</b> While some modern displays support wider gamuts like Display-P3,\n\t\t\t\tthe web platform's graphics APIs largely rely on sRGB. Applications using three.js\n\t\t\t\ttoday will typically use only the sRGB and Linear-sRGB color spaces.</i>\n\t\t</p>\n\t</blockquote>\n\n\t<h2>Roles of color spaces</h2>\n\n\t<p>\n\t\tLinear workflows — required for modern rendering methods — generally involve more than\n\t\tone color space, each assigned to a particular role. Linear and nonlinear color spaces are\n\t\tappropriate for different roles, explained below.\n\t</p>\n\n\t<h3>Input color space</h3>\n\n\t<p>\n\t\tColors supplied to three.js — from color pickers, textures, 3D models, and other sources —\n\t\teach have an associated color space. Those not already in the Linear-sRGB working color\n\t\tspace must be converted, and textures be given the correct <i>texture.colorSpace</i> assignment.\n\t\tCertain conversions (for hexadecimal and CSS colors in sRGB) can be made automatically if\n\t\tthe THREE.ColorManagement API is enabled before initializing colors:\n\t</p>\n\n\t<code>\nTHREE.ColorManagement.enabled = true;\n\t</code>\n\n\t<p>\n\t\tTHREE.ColorManagement is enabled by default.\n\t</p>\n\n\t<ul>\n\t\t<li>\n\t\t\t<b>Materials, lights, and shaders:</b> Colors in materials, lights, and shaders store\n\t\t\tRGB components in the Linear-sRGB working color space.\n\t\t</li>\n\t\t<li>\n\t\t\t<b>Vertex colors:</b> <code class=\"notranslate\" translate=\"no\">BufferAttribute</code> store RGB components in the\n\t\t\tLinear-sRGB working color space.\n\t\t</li>\n\t\t<li>\n\t\t\t<b>Color textures:</b> PNG or JPEG <code class=\"notranslate\" translate=\"no\">Texture</code> containing color information\n\t\t\t(like .map or .emissiveMap) use the closed domain sRGB color space, and must be annotated with\n\t\t\t<i>texture.colorSpace = SRGBColorSpace</i>. Formats like OpenEXR (sometimes used for .envMap or\n\t\t\t.lightMap) use the Linear-sRGB color space indicated with <i>texture.colorSpace = LinearSRGBColorSpace</i>,\n\t\t\tand may contain values in the open domain [0,∞].\n\t\t</li>\n\t\t<li>\n\t\t\t<b>Non-color textures:</b> Textures that do not store color information (like .normalMap\n\t\t\tor .roughnessMap) do not have an associated color space, and generally use the (default) texture\n\t\t\tannotation of <i>texture.colorSpace = NoColorSpace</i>. In rare cases, non-color data\n\t\t\tmay be represented with other nonlinear encodings for technical reasons.\n\t\t</li>\n\t</ul>\n\n\t<blockquote>\n\t\t<p>\n\t\t\t⚠️ <i><b>WARNING:</b> Many formats for 3D models do not correctly or consistently\n\t\t\tdefine color space information. While three.js attempts to handle most cases, problems\n\t\t\tare common with older file formats. For best results, use glTF 2.0 (<code class=\"notranslate\" translate=\"no\">GLTFLoader</code>)\n\t\t\tand test 3D models in online viewers early to confirm the asset itself is correct.</i>\n\t\t</p>\n\t</blockquote>\n\n\t<h3>Working color space</h3>\n\n\t<p>\n\t\tRendering, interpolation, and many other operations must be performed in an open domain\n\t\tlinear working color space, in which RGB components are proportional to physical\n\t\tillumination. In three.js, the working color space is Linear-sRGB.\n\t</p>\n\n\t<h3>Output color space</h3>\n\n\t<p>\n\t\tOutput to a display device, image, or video may involve conversion from the open domain\n\t\tLinear-sRGB working color space to another color space. The conversion is defined by\n\t\t(<code class=\"notranslate\" translate=\"no\">WebGLRenderer.outputColorSpace</code>). When using post-processing, this requires OutputPass.\n\t</p>\n\n\t<ul>\n\t\t<li>\n\t\t\t<b>Display:</b> Colors written to a WebGL canvas for display should be in the sRGB\n\t\t\tcolor space.\n\t\t</li>\n\t\t<li>\n\t\t\t<b>Image:</b> Colors written to an image should use the color space appropriate for\n\t\t\tthe format and usage. Fully-rendered images written to PNG or JPEG textures generally\n\t\t\tuse the sRGB color space. Images containing emission, light maps, or other data not\n\t\t\tconfined to the [0,1] range will generally use the open domain Linear-sRGB color space,\n\t\t\tand a compatible image format like OpenEXR.\n\t\t</li>\n\t</ul>\n\n\t<blockquote>\n\t\t<p>\n\t\t\t⚠️ <i><b>WARNING:</b> Render targets may use either sRGB or Linear-sRGB. sRGB makes\n\t\t\tbetter use of limited precision. In the closed domain, 8 bits often suffice for sRGB\n\t\t\twhereas ≥12 bits (half float) may be required for Linear-sRGB. If later pipeline\n\t\t\tstages require Linear-sRGB input, the additional conversions may have a small\n\t\t\tperformance cost.</i>\n\t\t</p>\n\t</blockquote>\n\n\t<p>\n\t\tCustom materials based on <code class=\"notranslate\" translate=\"no\">ShaderMaterial</code> and <code class=\"notranslate\" translate=\"no\">RawShaderMaterial</code> have to implement their own output color space conversion.\n\t\tFor instances of <code class=\"notranslate\" translate=\"no\">ShaderMaterial</code>, adding the <code class=\"notranslate\" translate=\"no\">colorspace_fragment</code> shader chunk to the fragment shader's <code class=\"notranslate\" translate=\"no\">main()</code> function should be sufficient.\n\t</p>\n\n\t<h2>Working with THREE.Color instances</h2>\n\n\t<p>\n\t\tMethods reading or modifying <code class=\"notranslate\" translate=\"no\">Color</code> instances assume data is already in the\n\t\tthree.js working color space, Linear-sRGB. RGB and HSL components are direct\n\t\trepresentations of data stored by the Color instance, and are never converted\n\t\timplicitly. Color data may be explicitly converted with <i>.convertLinearToSRGB()</i>\n\t\tor <i>.convertSRGBToLinear()</i>.\n\t</p>\n\n<pre class=\"prettyprint notranslate lang-js\" translate=\"no\">// RGB components (no change).\ncolor.r = color.g = color.b = 0.5;\nconsole.log( color.r ); // → 0.5\n\n// Manual conversion.\ncolor.r = 0.5;\ncolor.convertSRGBToLinear();\nconsole.log( color.r ); // → 0.214041140\n</pre>\n\n\t<p>\n\t\tWith <i>ColorManagement.enabled = true</i> set (recommended), certain conversions\n\t\tare made automatically. Because hexadecimal and CSS colors are generally sRGB, <code class=\"notranslate\" translate=\"no\">Color</code>\n\t\tmethods will automatically convert these inputs from sRGB to Linear-sRGB in setters, or\n\t\tconvert from Linear-sRGB to sRGB when returning hexadecimal or CSS output from getters.\n\t</p>\n\n<pre class=\"prettyprint notranslate lang-js\" translate=\"no\">// Hexadecimal conversion.\ncolor.setHex( 0x808080 );\nconsole.log( color.r ); // → 0.214041140\nconsole.log( color.getHex() ); // → 0x808080\n\n// CSS conversion.\ncolor.setStyle( 'rgb( 0.5, 0.5, 0.5 )' );\nconsole.log( color.r ); // → 0.214041140\n\n// Override conversion with 'colorSpace' argument.\ncolor.setHex( 0x808080, LinearSRGBColorSpace );\nconsole.log( color.r ); // → 0.5\nconsole.log( color.getHex( LinearSRGBColorSpace ) ); // → 0x808080\nconsole.log( color.getHex( SRGBColorSpace ) ); // → 0xBCBCBC\n</pre>\n\n\t<h2>Common mistakes</h2>\n\n\t<p>\n\t\tWhen an individual color or texture is misconfigured, it will appear darker or lighter than\n\t\texpected. When the renderer's output color space is misconfigured, the entire scene may appear\n\t\tdarker (e.g. missing conversion to sRGB) or lighter (e.g. a double conversion to sRGB with\n\t\tpost-processing). In each case the problem may not be uniform, and simply increasing/decreasing\n\t\tlighting does not solve it.\n\t</p>\n\n\t<p>\n\t\tA more subtle issue appears when <i>both</i> the input color spaces and the output color\n\t\tspaces are incorrect — the overall brightness levels may be fine, but colors may change\n\t\tunexpectedly under different lighting, or shading may appear more blown-out and less soft\n\t\tthan intended. These two wrongs do not make a right, and it's important that the working\n\t\tcolor space be linear (\"scene referred\") and the output color space be nonlinear\n\t\t(\"display referred\").\n\t</p>\n\n\t<h2>Further reading</h2>\n\n\t<ul>\n\t\t<li>\n\t\t\t<a href=\"https://developer.nvidia.com/gpugems/gpugems3/part-iv-image-effects/chapter-24-importance-being-linear\" target=\"_blank\" rel=\"noopener\">GPU Gems 3: The Importance of Being Linear</a>, by Larry Gritz and Eugene d'Eon\n\t\t</li>\n\t\t<li>\n\t\t\t<a href=\"https://blog.johnnovak.net/2016/09/21/what-every-coder-should-know-about-gamma/\" target=\"_blank\" rel=\"noopener\">What every coder should know about gamma</a>, by John Novak\n\t\t</li>\n\t\t<li>\n\t\t\t<a href=\"https://hg2dc.com/\" target=\"_blank\" rel=\"noopener\">The Hitchhiker's Guide to Digital Color</a>, by Troy Sobotka\n\t\t</li>\n\t\t<li>\n\t\t\t<a href=\"https://docs.blender.org/manual/en/latest/render/color_management.html\" target=\"_blank\" rel=\"noopener\">Color Management</a>, Blender\n\t\t</li>\n\t</ul>\n\n        </div>\n      </div>\n    </div>",
    "success": true,
    "error": "",
    "crawled_at": 1786376809.300204,
    "is_shell": false
  },
  {
    "url": "https://threejs.org/manual/en/how-to-create-vr-content.html",
    "title": "How to create VR content",
    "content": "<div class=\"container\">\n      <div class=\"lesson-title\">\n        <h1>How to create VR content</h1>\n      </div>\n      <div class=\"lesson\">\n        <div class=\"lesson-main\">\n          \n          <p>\n            This guide provides a brief overview of the basic components of a web-based VR application\n            made with three.js.\n          </p>\n        \n          <h2>Workflow</h2>\n        \n          <p>\n            First, you have to include <a href=\"https://github.com/mrdoob/three.js/blob/master/examples/jsm/webxr/VRButton.js\" target=\"_blank\">VRButton.js</a>\n            into your project.\n          </p>\n        \n<pre class=\"prettyprint notranslate lang-js\" translate=\"no\">import { VRButton } from 'three/addons/webxr/VRButton.js';\n</pre>\n        \n          <p>\n            *VRButton.createButton()* does two important things: It creates a button which indicates\n            VR compatibility. Besides, it initiates a VR session if the user activates the button. The only thing you have\n            to do is to add the following line of code to your app.\n          </p>\n        \n<pre class=\"prettyprint notranslate lang-js\" translate=\"no\">document.body.appendChild( VRButton.createButton( renderer ) );\n</pre>\n        \n          <p>\n            Next, you have to tell your instance of <code class=\"notranslate\" translate=\"no\">WebGLRenderer</code> to enable XR rendering.\n          </p>\n        \n<pre class=\"prettyprint notranslate lang-js\" translate=\"no\">renderer.xr.enabled = true;\n</pre>\n        \n          <p>\n            Finally, you have to adjust your animation loop since we can't use our well known\n            *window.requestAnimationFrame()* function. For VR projects we use <code class=\"notranslate\" translate=\"no\">renderer.setAnimationLoop()</code>.\n            The minimal code looks like this:\n          </p>\n        \n<pre class=\"prettyprint notranslate lang-js\" translate=\"no\">renderer.setAnimationLoop( function () {\n\n  renderer.render( scene, camera );\n\n} );\n</pre>\n        \n          <h2>Next Steps</h2>\n        \n          <p>\n            Have a look at one of the official WebVR examples to see this workflow in action.<br><br>\n        \n            <a href=\"../../examples/#webxr_xr_ballshooter\" target=\"_blank\">WebXR / XR / ballshooter</a><br>\n            <a href=\"../../examples/#webxr_xr_cubes\" target=\"_blank\">WebXR / XR / cubes</a><br>\n            <a href=\"../../examples/#webxr_xr_dragging\" target=\"_blank\">WebXR / XR / dragging</a><br>\n            <a href=\"../../examples/#webxr_xr_marchingcubes\" target=\"_blank\">WebXR / XR / marching cubes</a><br>\n            <a href=\"../../examples/#webxr_xr_paint\" target=\"_blank\">WebXR / XR / paint</a><br>\n            <a href=\"../../examples/#webxr_vr_panorama_depth\" target=\"_blank\">WebXR / VR / panorama_depth</a><br>\n            <a href=\"../../examples/#webxr_vr_panorama\" target=\"_blank\">WebXR / VR / panorama</a><br>\n            <a href=\"../../examples/#webxr_vr_rollercoaster\" target=\"_blank\">WebXR / VR / rollercoaster</a><br>\n            <a href=\"../../examples/#webxr_vr_sandbox\" target=\"_blank\">WebXR / VR / sandbox</a><br>\n            <a href=\"../../examples/#webxr_vr_video\" target=\"_blank\">WebXR / VR / video</a>\n          </p>\n\n        </div>\n      </div>\n    </div>",
    "success": true,
    "error": "",
    "crawled_at": 1786376809.300204,
    "is_shell": false
  },
  {
    "url": "https://threejs.org/manual/en/how-to-dispose-of-objects.html",
    "title": "How to dispose of Objects",
    "content": "<div class=\"container\">\n      <div class=\"lesson-title\">\n        <h1>How to dispose of Objects</h1>\n      </div>\n      <div class=\"lesson\">\n        <div class=\"lesson-main\">\n          \n          <p>\n            One important aspect in order to improve performance and avoid memory leaks in your application is the disposal of unused library entities.\n            Whenever you create an instance of a *three.js* type, you allocate a certain amount of memory. However, *three.js* creates for specific objects\n            like geometries or materials WebGL related entities like buffers or shader programs which are necessary for rendering. It's important to\n            highlight that these objects are not released automatically. Instead, the application has to use a special API in order to free such resources.\n            This guide provides a brief overview about how this API is used and what objects are relevant in this context.\n          </p>\n        \n          <h2>Geometries</h2>\n        \n          <p>\n            A geometry usually represents vertex information defined as a collection of attributes. *three.js* internally creates an object of type <a href=\"https://developer.mozilla.org/en-US/docs/Web/API/WebGLBuffer\" target=\"_blank\">WebGLBuffer</a>\n            for each attribute. These entities are only deleted if you call <code class=\"notranslate\" translate=\"no\">BufferGeometry.dispose()</code>. If a geometry becomes obsolete in your application,\n            execute the method to free all related resources.\n          </p>\n        \n          <h2>Materials</h2>\n        \n          <p>\n            A material defines how objects are rendered. *three.js* uses the information of a material definition in order to construct a shader program for rendering.\n            Shader programs can only be deleted if the respective material is disposed. For performance reasons, *three.js* tries to reuse existing\n            shader programs if possible. So a shader program is only deleted if all related materials are disposed. You can indicate the disposal of a material by\n            executing <code class=\"notranslate\" translate=\"no\">Material.dispose()</code>.\n          </p>\n        \n          <h2>Textures</h2>\n        \n          <p>\n            The disposal of a material has no effect on textures. They are handled separately since a single texture can be used by multiple materials at the same time.\n            Whenever you create an instance of <code class=\"notranslate\" translate=\"no\">Texture</code>, three.js internally creates an instance of <a href=\"https://developer.mozilla.org/en-US/docs/Web/API/WebGLTexture\" target=\"_blank\">WebGLTexture</a>.\n            Similar to buffers, this object can only be deleted by calling <code class=\"notranslate\" translate=\"no\">Texture.dispose()</code>.\n          </p>\n        \n          <p>\n            If you use an <code class=\"notranslate\" translate=\"no\">ImageBitmap</code> as the texture's data source, you have to call <a href=\"https://developer.mozilla.org/en-US/docs/Web/API/ImageBitmap/close\" target=\"_blank\">ImageBitmap.close</a>() at the application level to dispose of all CPU-side resources.\n            An automated call of <code class=\"notranslate\" translate=\"no\">ImageBitmap.close()</code> in <code class=\"notranslate\" translate=\"no\">Texture.dispose()</code> is not possible, since the image bitmap becomes unusable, and the engine has no way of knowing if the image bitmap is used elsewhere.\n          </p>\n        \n          <h2>Render Targets</h2>\n        \n          <p>\n            Objects of type <code class=\"notranslate\" translate=\"no\">WebGLRenderTarget</code> not only allocate an instance of <a href=\"https://developer.mozilla.org/en-US/docs/Web/API/WebGLTexture\" target=\"_blank\">WebGLTexture</a> but also\n            <a href=\"https://developer.mozilla.org/en-US/docs/Web/API/WebGLFramebuffer\" target=\"_blank\">WebGLFramebuffer</a>s and <a href=\"https://developer.mozilla.org/en-US/docs/Web/API/WebGLRenderbuffer\" target=\"_blank\">WebGLRenderbuffer</a>s\n            for realizing custom rendering destinations. These objects are only deallocated by executing <code class=\"notranslate\" translate=\"no\">WebGLRenderTarget.dispose()</code>.\n          </p>\n        \n          <h2>Skinned Mesh</h2>\n        \n          <p>\n            Skinned meshes represent their bone hierarchy as skeletons. If you don't need a skinned mesh anymore, consider to call <code class=\"notranslate\" translate=\"no\">Skeleton.dispose()</code> on the skeleton to free internal resources.\n            Keep in mind that skeletons can be shared across multiple skinned meshes, so only call <code class=\"notranslate\" translate=\"no\">dispose()</code> if the skeleton is not used by other active skinned meshes.\n          </p>\n        \n          <h2>Miscellaneous</h2>\n        \n          <p>\n            There are other classes from the examples directory like controls or post processing passes which provide <code class=\"notranslate\" translate=\"no\">dispose()</code> methods in order to remove internal event listeners\n            or render targets. In general, it's recommended to check the API or documentation of a class and watch for <code class=\"notranslate\" translate=\"no\">dispose()</code>. If present, you should use it when cleaning things up.\n          </p>\n        \n          <h2>FAQ</h2>\n        \n          <h3>Why can't *three.js* dispose objects automatically?</h3>\n        \n          <p>\n            This question was asked many times by the community so it's important to clarify this matter. Fact is that *three.js* does not know the lifetime or scope\n            of user-created entities like geometries or materials. This is the responsibility of the application. For example even if a material is currently not used for rendering,\n            it might be necessary for the next frame. So if the application decides that a certain object can be deleted, it has to\tnotify the engine via calling the respective\n            <code class=\"notranslate\" translate=\"no\">dispose()</code> method.\n          </p>\n        \n          <h3>Does removing a mesh from the scene also dispose its geometry and material?</h3>\n        \n          <p>\n            No, you have to explicitly dispose the geometry and material via *dispose()*. Keep in mind that geometries and materials can be shared among 3D objects like meshes.\n          </p>\n        \n          <h3>Does *three.js* provide information about the amount of cached objects?</h3>\n        \n          <p>\n            Yes. It's possible to evaluate <code class=\"notranslate\" translate=\"no\">renderer.info</code>, a special property of the renderer with a series of statistical information about the graphics board memory\n            and the rendering process. Among other things, it tells you how many textures, geometries and shader programs are internally stored. If you notice performance problems\n            in your application, it's a good idea to debug this property in order to easily identify a memory leak.\n          </p>\n        \n          <h3>What happens when you call <code class=\"notranslate\" translate=\"no\">dispose()</code> on a texture but the image is not loaded yet?</h3>\n        \n          <p>\n            Internal resources for a texture are only allocated if the image has fully loaded. If you dispose a texture before the image was loaded,\n            nothing happens. No resources were allocated so there is also no need for clean up.\n          </p>\n        \n          <h3>What happens when I call <code class=\"notranslate\" translate=\"no\">dispose()</code> and then use the respective object at a later point?</h3>\n        \n          <p>\n            That depends. For geometries, materials, textures, render targets and post processing passes the deleted internal resources can be created again by the engine.\n            So no runtime error will occur but you might notice a negative performance impact for the current frame, especially when shader programs have to be compiled.\n        \n            Controls and renderers are an exception. Instances of these classes can not be used after <code class=\"notranslate\" translate=\"no\">dispose()</code> has been called. You have to create new instances in this case.\n          </p>\n        \n          <h3>How should I manage *three.js* objects in my app? When do I know how to dispose things?</h3>\n        \n          <p>\n            In general, there is no definite recommendation for this. It highly depends on the specific use case when calling <code class=\"notranslate\" translate=\"no\">dispose()</code> is appropriate. It's important to highlight that\n            it's not always necessary to dispose objects all the time. A good example for this is a game which consists of multiple levels. A good place for object disposal is when\n            switching the level. The app could traverse through the old scene and dispose all obsolete materials, geometries and textures. As mentioned in the previous section, it does not\n            produce a runtime error if you dispose an object that is actually still in use. The worst thing that can happen is performance drop for a single frame.\n          </p>\n        \n          <h3>Why <code class=\"notranslate\" translate=\"no\">renderer.info.memory</code> is still reporting geometries and textures after traversing the scene and disposing all reachable textures and geometries?</h3>\n        \n          <p>\n            In certain cases, there are some textures and geometries used internally by Three.js\n            that are not reachable when traversing the scene graph in order to be disposed.\n            It is expected that <code class=\"notranslate\" translate=\"no\">renderer.info.memory</code> will still report them even after a full scene cleanup.\n            However, they do not leak, but they are reused on consecutive scene cleanup/repopulating cycles.\n        \n            These cases could be related to using <code class=\"notranslate\" translate=\"no\">material.envMap</code>, <code class=\"notranslate\" translate=\"no\">scene.background</code>, <code class=\"notranslate\" translate=\"no\">scene.environment</code>,\n            or other contexts that would require the engine to create textures or geometries for internal use.\n          </p>\n        \n          <h2>Examples that demonstrate the usage of dispose()</h2>\n        \n          <p>\n            <a href=\"../../examples/#webgl_test_memory\" target=\"_blank\">WebGL / test / memory</a><br>\n            <a href=\"../../examples/#webgl_test_memory2\" target=\"_blank\">WebGL / test / memory2</a><br>\n          </p>\n\n        </div>\n      </div>\n    </div>",
    "success": true,
    "error": "",
    "crawled_at": 1786376809.300204,
    "is_shell": false
  },
  {
    "url": "https://threejs.org/manual/en/how-to-update-things.html",
    "title": "How to update Things",
    "content": "<div class=\"container\">\n      <div class=\"lesson-title\">\n        <h1>How to update Things</h1>\n      </div>\n      <div class=\"lesson\">\n        <div class=\"lesson-main\">\n          \n          <div>\n            <p>All objects by default automatically update their matrices if they have been added to the scene with</p>\n<pre class=\"prettyprint notranslate lang-js\" translate=\"no\">const object = new THREE.Object3D();\nscene.add( object );\n</pre>\n            or if they are the child of another object that has been added to the scene:\n<pre class=\"prettyprint notranslate lang-js\" translate=\"no\">const object1 = new THREE.Object3D();\nconst object2 = new THREE.Object3D();\n\nobject1.add( object2 );\nscene.add( object1 ); //object1 and object2 will automatically update their matrices\n</pre>\n          </div>\n      \n          <p>However, if you know the object will be static, you can disable this and update the transform matrix manually just when needed.</p>\n      \n<pre class=\"prettyprint notranslate lang-js\" translate=\"no\">object.matrixAutoUpdate = false;\nobject.updateMatrix();\n</pre>\n      \n          <h2>BufferGeometry</h2>\n          <div>\n            <p>\n              BufferGeometries store information (such as vertex positions, face indices, normals, colors,\n              UVs, and any custom attributes) in attribute buffers - that is,\n              <a href=\"https://developer.mozilla.org/en-US/docs/Web/JavaScript/Typed_arrays\" target=\"_blank\">typed arrays</a>.\n              This makes them generally faster than standard Geometries, at the cost of being somewhat harder to\n              work with.\n            </p>\n            <p>\n              With regards to updating BufferGeometries, the most important thing to understand is that\n              you cannot resize buffers (this is very costly,\tbasically the equivalent to creating a new geometry).\n              You can however update the content of buffers.\n            </p>\n            <p>\n              This means that if you know an attribute of your BufferGeometry will grow, say the number of vertices,\n              you must pre-allocate a buffer large enough to hold any new vertices that may be created. Of\n              course, this also means that there will be a maximum size for your BufferGeometry - there is\n              no way to create a BufferGeometry that can efficiently be extended indefinitely.\n            </p>\n            <p>\n              We'll use the example of a line that gets extended at render time. We'll allocate space\n              in the buffer for 500 vertices but draw only two at first, using <code class=\"notranslate\" translate=\"no\">BufferGeometry.drawRange</code>.\n            </p>\n<pre class=\"prettyprint notranslate lang-js\" translate=\"no\">const MAX_POINTS = 500;\n\n// geometry\nconst geometry = new THREE.BufferGeometry();\n\n// attributes\nconst positions = new Float32Array( MAX_POINTS * 3 ); // 3 floats (x, y and z) per point\ngeometry.setAttribute( 'position', new THREE.BufferAttribute( positions, 3 ) );\n\n// draw range\nconst drawCount = 2; // draw the first 2 points, only\ngeometry.setDrawRange( 0, drawCount );\n\n// material\nconst material = new THREE.LineBasicMaterial( { color: 0xff0000 } );\n\n// line\nconst line = new THREE.Line( geometry, material );\nscene.add( line );\n</pre>\n             <p>\n              Next we'll randomly add points to the line using a pattern like:\n            </p>\n<pre class=\"prettyprint notranslate lang-js\" translate=\"no\">const positionAttribute = line.geometry.getAttribute( 'position' );\n\nlet x = 0, y = 0, z = 0;\n\nfor ( let i = 0; i &lt; positionAttribute.count; i ++ ) {\n\n    positionAttribute.setXYZ( i, x, y, z );\n\n    x += ( Math.random() - 0.5 ) * 30;\n    y += ( Math.random() - 0.5 ) * 30;\n    z += ( Math.random() - 0.5 ) * 30;\n\n}\n</pre>\n            <p>\n              If you want to change the <em>number of points</em> rendered after the first render, do this:\n            </p>\n<pre class=\"prettyprint notranslate lang-js\" translate=\"no\">line.geometry.setDrawRange( 0, newValue );\n</pre>\n            <p>\n              If you want to change the position data values after the first render, you need to\n              set the needsUpdate flag like so:\n            </p>\n<pre class=\"prettyprint notranslate lang-js\" translate=\"no\">positionAttribute.needsUpdate = true; // required after the first render\n</pre>\n      \n            <p>\n              If you change the position data values after the initial render, you may need to recompute\n              bounding volumes so other features of the engine like view frustum culling or helpers properly work.\n            </p>\n<pre class=\"prettyprint notranslate lang-js\" translate=\"no\">line.geometry.computeBoundingBox();\nline.geometry.computeBoundingSphere();\n</pre>\n      \n            <p>\n              <a href=\"https://jsfiddle.net/t4m85pLr/1/\" target=\"_blank\">Here is a fiddle</a> showing an animated line which you can adapt to your use case.\n            </p>\n      \n            <h3>Examples</h3>\n      \n            <p>\n              <a href=\"../../examples/#webgl_custom_attributes\" target=\"_blank\">WebGL / custom / attributes</a><br>\n              <a href=\"../../examples/#webgl_buffergeometry_custom_attributes_particles\" target=\"_blank\">WebGL / buffergeometry / custom / attributes / particles</a>\n            </p>\n      \n          </div>\n      \n          <h2>Materials</h2>\n          <div>\n            <p>All uniforms values can be changed freely (e.g. colors, textures, opacity, etc), values are sent to the shader every frame.</p>\n      \n            <p>Also GLstate related parameters can change any time (depthTest, blending, polygonOffset, etc).</p>\n      \n            <p>The following properties can't be easily changed at runtime (once the material is rendered at least once):</p>\n            <ul>\n              <li>numbers and types of uniforms</li>\n              <li>presence or not of\n                <ul>\n                  <li>texture</li>\n                  <li>fog</li>\n                  <li>vertex colors</li>\n                  <li>morphing</li>\n                  <li>shadow map</li>\n                  <li>alpha test</li>\n                  <li>transparent</li>\n                </ul>\n              </li>\n            </ul>\n      \n            <p>Changes in these require building of new shader program. You'll need to set</p>\n            <code>material.needsUpdate = true</code>\n      \n            <p>Bear in mind this might be quite slow and induce jerkiness in framerate (especially on Windows, as shader compilation is slower in DirectX than OpenGL).</p>\n      \n            <p>For smoother experience you can emulate changes in these features to some degree by having \"dummy\" values like zero intensity lights, white textures, or zero density fog.</p>\n      \n            <p>You can freely change the material used for geometry chunks, however you cannot change how an object is divided into chunks (according to face materials). </p>\n      \n            <h3>If you need to have different configurations of materials during runtime:</h3>\n            <p>If the number of materials / chunks is small, you could pre-divide the object beforehand (e.g. hair / face / body / upper clothes / trousers for a human, front / sides / top / glass / tire / interior for a car). </p>\n      \n            <p>If the number is large (e.g. each face could be potentially different), consider a different solution, such as using attributes / textures to drive different per-face look.</p>\n      \n            <h3>Examples</h3>\n            <p>\n              <a href=\"../../examples/#webgl_materials_car\" target=\"_blank\">WebGL / materials / car</a><br>\n              <a href=\"../../examples/#webgl_postprocessing_dof\" target=\"_blank\">WebGL / webgl_postprocessing / dof</a>\n            </p>\n          </div>\n      \n      \n          <h2>Textures</h2>\n          <div>\n            <p>Image, canvas, video and data textures need to have the following flag set if they are changed:</p>\n            <code>\n              texture.needsUpdate = true;\n            </code>\n            <p>Render targets update automatically.</p>\n      \n            <h3>Examples</h3>\n            <p>\n              <a href=\"../../examples/#webgl_materials_video\" target=\"_blank\">WebGL / materials / video</a><br>\n              <a href=\"../../examples/#webgl_rtt\" target=\"_blank\">WebGL / rtt</a>\n            </p>\n      \n          </div>\n      \n          <h2>Cameras</h2>\n          <div>\n            <p>A camera's position and target is updated automatically. If you need to change</p>\n            <ul>\n              <li>\n                fov\n              </li>\n              <li>\n                aspect\n              </li>\n              <li>\n                near\n              </li>\n              <li>\n                far\n              </li>\n            </ul>\n            <p>\n              then you'll need to recompute the projection matrix:\n            </p>\n<pre class=\"prettyprint notranslate lang-js\" translate=\"no\">camera.aspect = window.innerWidth / window.innerHeight;\ncamera.updateProjectionMatrix();\n</pre>\n          </div>\n      \n          <h2>InstancedMesh</h2>\n          <div>\n            <p>\n              <code class=\"notranslate\" translate=\"no\">InstancedMesh</code> is a class for conveniently access instanced rendering in <code class=\"notranslate\" translate=\"no\">three.js</code>. Certain library features like view frustum culling or\n              ray casting rely on up-to-date bounding volumes (bounding sphere and bounding box). Because of the way how <code class=\"notranslate\" translate=\"no\">InstancedMesh</code> works, the class\n              has its own <code class=\"notranslate\" translate=\"no\">boundingBox</code> and <code class=\"notranslate\" translate=\"no\">boundingSphere</code> properties that supersede the bounding volumes on geometry level.\n            </p>\n            <p>\n              Similar to geometries you have to recompute the bounding box and sphere whenever you change the underlying data. In context of <code class=\"notranslate\" translate=\"no\">InstancedMesh</code>, that\n              happens when you transform instances via <code class=\"notranslate\" translate=\"no\">setMatrixAt()</code>. You can use the same pattern like with geometries.\n            </p>\n<pre class=\"prettyprint notranslate lang-js\" translate=\"no\">instancedMesh.computeBoundingBox();\ninstancedMesh.computeBoundingSphere();\n</pre>\n      \n          </div>\n      \n          <h2>SkinnedMesh</h2>\n          <div>\n            <p>\n              <code class=\"notranslate\" translate=\"no\">SkinnedMesh</code> follows the same principles like <code class=\"notranslate\" translate=\"no\">InstancedMesh</code> in context of bounding volumes. Meaning the class has its own version of\n              <code class=\"notranslate\" translate=\"no\">boundingBox</code> and <code class=\"notranslate\" translate=\"no\">boundingSphere</code> to correctly enclose animated meshes.\n              When calling <code class=\"notranslate\" translate=\"no\">computeBoundingBox()</code> and <code class=\"notranslate\" translate=\"no\">computeBoundingSphere()</code>, the class computes the respective bounding volumes based on the current\n              bone transformation (or in other words the current animation state).\n            </p>\n          </div>\n\n        </div>\n      </div>\n    </div>",
    "success": true,
    "error": "",
    "crawled_at": 1786376809.300204,
    "is_shell": false
  },
  {
    "url": "https://threejs.org/manual/en/how-to-use-post-processing.html",
    "title": "How to use Post Processing",
    "content": "<div class=\"container\">\n      <div class=\"lesson-title\">\n        <h1>How to use Post Processing</h1>\n      </div>\n      <div class=\"lesson\">\n        <div class=\"lesson-main\">\n          \n          <p>\n            Many three.js applications render their 3D objects directly to the screen. Sometimes, however, you want to apply one or more graphical\n            effects like Depth-Of-Field, Bloom, Film Grain or various types of Anti-aliasing. Post-processing is a widely used approach\n            to implement such effects. First, the scene is rendered to a render target which represents a buffer in the video card's memory.\n            In the next step one or more post-processing passes apply filters and effects to the image buffer before it is eventually rendered to\n            the screen.\n          </p>\n          <p>\n            three.js provides a complete post-processing solution via <code class=\"notranslate\" translate=\"no\">EffectComposer</code> to implement such a workflow.\n          </p>\n      \n          <h2>Workflow</h2>\n      \n          <p>\n            The first step in the process is to import all necessary files from the examples directory. The guide assumes you are using the official\n            <a href=\"https://www.npmjs.com/package/three\" target=\"_blank\">npm package</a> of three.js. For our basic demo in this guide we need the following files.\n          </p>\n      \n<pre class=\"prettyprint notranslate lang-js\" translate=\"no\">import { EffectComposer } from 'three/addons/postprocessing/EffectComposer.js';\nimport { RenderPass } from 'three/addons/postprocessing/RenderPass.js';\nimport { GlitchPass } from 'three/addons/postprocessing/GlitchPass.js';\nimport { OutputPass } from 'three/addons/postprocessing/OutputPass.js';\n</pre>\n      \n          <p>\n            After all files are successfully imported, we can create our composer by passing in an instance of <code class=\"notranslate\" translate=\"no\">WebGLRenderer</code>.\n          </p>\n      \n<pre class=\"prettyprint notranslate lang-js\" translate=\"no\">const composer = new EffectComposer( renderer );\n</pre>\n      \n          <p>\n            When using a composer, it's necessary to change the application's animation loop. Instead of calling the render method of\n            <code class=\"notranslate\" translate=\"no\">WebGLRenderer</code>, we now use the respective counterpart of <code class=\"notranslate\" translate=\"no\">EffectComposer</code>.\n          </p>\n      \n<pre class=\"prettyprint notranslate lang-js\" translate=\"no\">function animate() {\n\n  requestAnimationFrame( animate );\n\n  composer.render();\n\n}\n</pre>\n      \n          <p>\n            Our composer is now ready so it's possible to configure the chain of post-processing passes. These passes are responsible for creating\n            the final visual output of the application. They are processed in order of their addition/insertion. In our example, the instance of <code class=\"notranslate\" translate=\"no\">RenderPass</code>\n            is executed first, then the instance of <code class=\"notranslate\" translate=\"no\">GlitchPass</code> and finally <code class=\"notranslate\" translate=\"no\">OutputPass</code>. The last enabled pass in the chain is automatically rendered to the screen. \n            The setup of the passes looks like so:\n          </p>\n      \n<pre class=\"prettyprint notranslate lang-js\" translate=\"no\">const renderPass = new RenderPass( scene, camera );\ncomposer.addPass( renderPass );\n\nconst glitchPass = new GlitchPass();\ncomposer.addPass( glitchPass );\n\nconst outputPass = new OutputPass();\ncomposer.addPass( outputPass );\n</pre>\n      \n          <p>\n            <code class=\"notranslate\" translate=\"no\">RenderPass</code> is normally placed at the beginning of the chain in order to provide the rendered scene as an input for the next post-processing step. In our case,\n            <code class=\"notranslate\" translate=\"no\">GlitchPass</code> is going to use these image data to apply a wild glitch effect. <code class=\"notranslate\" translate=\"no\">OutputPass</code> is usually the last pass in the chain which performs sRGB color space conversion and tone mapping.\n            Check out this <a href=\"https://threejs.org/examples/webgl_postprocessing_glitch\" target=\"_blank\">live example</a> to see it in action.\n          </p>\n      \n          <h2>Built-in Passes</h2>\n      \n          <p>\n            You can use a wide range of pre-defined post-processing passes provided by the engine. They are located in the\n            <a href=\"https://github.com/mrdoob/three.js/tree/dev/examples/jsm/postprocessing\" target=\"_blank\">postprocessing</a> directory.\n          </p>\n      \n          <h2>Custom Passes</h2>\n      \n          <p>\n            Sometimes you want to write a custom post-processing shader and include it into the chain of post-processing passes. For this scenario,\n            you can utilize <code class=\"notranslate\" translate=\"no\">ShaderPass</code>. After importing the file and your custom shader, you can use the following code to setup the pass.\n          </p>\n      \n<pre class=\"prettyprint notranslate lang-js\" translate=\"no\">import { ShaderPass } from 'three/addons/postprocessing/ShaderPass.js';\nimport { LuminosityShader } from 'three/addons/shaders/LuminosityShader.js';\n\n// later in your init routine\n\nconst luminosityPass = new ShaderPass( LuminosityShader );\ncomposer.addPass( luminosityPass );\n</pre>\n      \n          <p>\n            The repository provides a file called <a href=\"https://github.com/mrdoob/three.js/blob/master/examples/jsm/shaders/CopyShader.js\" target=\"_blank\">CopyShader</a> which is a\n            good starting code for your own custom shader. <code class=\"notranslate\" translate=\"no\">CopyShader</code> just copies the image contents of the <code class=\"notranslate\" translate=\"no\">EffectComposer</code>'s read buffer\n            to its write buffer without applying any effects.\n          </p>\n\n        </div>\n      </div>\n    </div>",
    "success": true,
    "error": "",
    "crawled_at": 1786376809.300204,
    "is_shell": false
  },
  {
    "url": "https://threejs.org/manual/en/matrix-transformations.html",
    "title": "Matrix Transformations",
    "content": "<div class=\"container\">\n      <div class=\"lesson-title\">\n        <h1>Matrix Transformations</h1>\n      </div>\n      <div class=\"lesson\">\n        <div class=\"lesson-main\">\n          \n          <p>\n            Three.js uses <code class=\"notranslate\" translate=\"no\">matrices</code> to encode 3D transformations---translations (position), rotations, and scaling. Every instance of <code class=\"notranslate\" translate=\"no\">Object3D</code> has a <code class=\"notranslate\" translate=\"no\">matrix</code> which stores that object's position, rotation, and scale. This page describes how to update an object's transformation.\n            </p>\n        \n            <h2>Convenience properties and <code class=\"notranslate\" translate=\"no\">matrixAutoUpdate</code></h2>\n        \n            <p>\n              There are two ways to update an object's transformation:\n            </p>\n            <ol>\n              <li>\n                Modify the object's <code class=\"notranslate\" translate=\"no\">position</code>, <code class=\"notranslate\" translate=\"no\">quaternion</code>, and <code class=\"notranslate\" translate=\"no\">scale</code> properties, and let three.js recompute\n                the object's matrix from these properties:\n<pre class=\"prettyprint notranslate lang-js\" translate=\"no\">object.position.copy( start_position );\nobject.quaternion.copy( quaternion );\n</pre>\n                By default, the <code class=\"notranslate\" translate=\"no\">matrixAutoUpdate</code> property is set true, and the matrix will be automatically recalculated.\n                If the object is static, or you wish to manually control when recalculation occurs, better performance can be obtained by setting the property false:\n<pre class=\"prettyprint notranslate lang-js\" translate=\"no\">object.matrixAutoUpdate = false;\n</pre>\n                And after changing any properties, manually update the matrix:\n<pre class=\"prettyprint notranslate lang-js\" translate=\"no\">object.updateMatrix();\n</pre>\n              </li>\n              <li>\n                Modify the object's matrix directly. The <code class=\"notranslate\" translate=\"no\">Matrix4</code> class has various methods for modifying the matrix:\n<pre class=\"prettyprint notranslate lang-js\" translate=\"no\">object.matrix.makeRotationFromQuaternion( quaternion );\nobject.matrix.setPosition( start_position );\nobject.matrixAutoUpdate = false;\n</pre>\n                Note that <code class=\"notranslate\" translate=\"no\">matrixAutoUpdate</code> <em>must</em> be set to <code class=\"notranslate\" translate=\"no\">false</code> in this case, and you should make sure <em>not</em> to call <code class=\"notranslate\" translate=\"no\">updateMatrix</code>. Calling <code class=\"notranslate\" translate=\"no\">updateMatrix</code> will clobber the manual changes made to the matrix, recalculating the matrix from <code class=\"notranslate\" translate=\"no\">position</code>, <code class=\"notranslate\" translate=\"no\">scale</code>, and so on.\n              </li>\n            </ol>\n        \n            <h2>Object and world matrices</h2>\n            <p>\n            An object's  matrix stores the object's transformation <em>relative</em> to the object's parent; to get the object's transformation in <em>world</em> coordinates, you must access the object's world matrix.\n            </p>\n            <p>\n            When either the parent or the child object's transformation changes, you can request that the child object's world matrix be updated by calling <code class=\"notranslate\" translate=\"no\">object.updateMatrixWorld()</code>.\n            </p>\n            <p>\n            An object can be transformed via <code class=\"notranslate\" translate=\"no\">applyMatrix4()</code>. Note: Under-the-hood, this method relies on <code class=\"notranslate\" translate=\"no\">Matrix4.decompose()</code>, and not all matrices are decomposable in this way. For example, if an object has a non-uniformly scaled parent, then the object's world matrix may not be decomposable, and this method may not be appropriate.\n            </p>\n        \n            <h2>Rotation and Quaternion</h2>\n            <p>\n            Three.js provides two ways of representing 3D rotations: Euler angles and Quaternions, as well as methods for converting between the two. Euler angles are subject to a problem called \"gimbal lock,\" where certain configurations can lose a degree of freedom (preventing the object from being rotated about one axis). For this reason, object rotations are <em>always</em> stored in the object's quaternion.\n            </p>\n            <p>\n            Previous versions of the library included a <code class=\"notranslate\" translate=\"no\">useQuaternion</code> property which, when set to false, would cause the object's matrix to be calculated from an Euler angle. This practice is deprecated---instead, you should use the <code class=\"notranslate\" translate=\"no\">object.setRotationFromEuler()</code> method, which will update the quaternion.\n            </p>\n\n        </div>\n      </div>\n    </div>",
    "success": true,
    "error": "",
    "crawled_at": 1786376809.300204,
    "is_shell": false
  },
  {
    "url": "https://threejs.org/manual/en/fundamentals.html",
    "title": "Fundamentals",
    "content": "<div class=\"container\">\n      <div class=\"lesson-title\">\n        <h1>Fundamentals</h1>\n      </div>\n      <div class=\"lesson\">\n        <div class=\"lesson-main\">\n          <p>This is the first article in a series of articles about three.js.\n<a href=\"https://threejs.org\">Three.js</a> is a 3D library that tries to make\nit as easy as possible to get 3D content on a webpage.</p>\n<p>Three.js is often confused with WebGL since more often than\nnot, but not always, three.js uses WebGL to draw 3D.\n<a href=\"https://webglfundamentals.org\">WebGL is a very low-level system that only draws points, lines, and triangles</a>.\nTo do anything useful with WebGL generally requires quite a bit of\ncode and that is where three.js comes in. It handles stuff\nlike scenes, lights, shadows, materials, textures, 3d math, all things that you'd\nhave to write yourself if you were to use WebGL directly.</p>\n<p>These tutorials assume you already know JavaScript and, for the\nmost part they will use ES6 style. <a href=\"prerequisites.html\">See here for a\nterse list of things you're expected to already know</a>.\nMost browsers that support three.js are auto-updated so most users should\nbe able to run this code. If you'd like to make this code run\non really old browsers look into a transpiler like <a href=\"https://babeljs.io\">Babel</a>.\nOf course users running really old browsers probably have machines\nthat can't run three.js.</p>\n<p>When learning most programming languages the first thing people\ndo is make the computer print <code class=\"notranslate\" translate=\"no\">\"Hello World!\"</code>. For 3D one\nof the most common first things to do is to make a 3D cube.\nSo let's start with \"Hello Cube!\"</p>\n<p>Before we get started let's try to give you an idea of the structure\nof a three.js app. A three.js app requires you to create a bunch of\nobjects and connect them together. Here's a diagram that represents\na small three.js app</p>\n<div class=\"threejs_center\"><img src=\"../resources/images/threejs-structure.svg\" style=\"width: 768px;\"></div>\n\n<p>Things to notice about the diagram above.</p>\n<ul>\n<li><p>There is a <a href=\"/docs/#api/en/constants/Renderer\"><code class=\"notranslate\" translate=\"no\">Renderer</code></a>. This is arguably the main object of three.js. You pass a\n<a href=\"/docs/#api/en/scenes/Scene\"><code class=\"notranslate\" translate=\"no\">Scene</code></a> and a <a href=\"/docs/#api/en/cameras/Camera\"><code class=\"notranslate\" translate=\"no\">Camera</code></a> to a <a href=\"/docs/#api/en/constants/Renderer\"><code class=\"notranslate\" translate=\"no\">Renderer</code></a> and it renders (draws) the portion of\nthe 3D scene that is inside the <em>frustum</em> of the camera as a 2D image to a\ncanvas.</p>\n</li>\n<li><p>There is a <a href=\"scenegraph.html\">scenegraph</a> which is a tree like\nstructure, consisting of various objects like a <a href=\"/docs/#api/en/scenes/Scene\"><code class=\"notranslate\" translate=\"no\">Scene</code></a> object, multiple\n<a href=\"/docs/#api/en/objects/Mesh\"><code class=\"notranslate\" translate=\"no\">Mesh</code></a> objects, <a href=\"/docs/#api/en/lights/Light\"><code class=\"notranslate\" translate=\"no\">Light</code></a> objects, <a href=\"/docs/#api/en/objects/Group\"><code class=\"notranslate\" translate=\"no\">Group</code></a>, <a href=\"/docs/#api/en/core/Object3D\"><code class=\"notranslate\" translate=\"no\">Object3D</code></a>, and <a href=\"/docs/#api/en/cameras/Camera\"><code class=\"notranslate\" translate=\"no\">Camera</code></a> objects. A\n<a href=\"/docs/#api/en/scenes/Scene\"><code class=\"notranslate\" translate=\"no\">Scene</code></a> object defines the root of the scenegraph and contains properties like\nthe background color and fog. These objects define a hierarchical parent/child\ntree like structure and represent where objects appear and how they are\noriented. Children are positioned and oriented relative to their parent. For\nexample the wheels on a car might be children of the car so that moving and\norienting the car's object automatically moves the wheels. You can read more\nabout this in <a href=\"scenegraph.html\">the article on scenegraphs</a>.</p>\n<p>Note in the diagram <a href=\"/docs/#api/en/cameras/Camera\"><code class=\"notranslate\" translate=\"no\">Camera</code></a> is half in half out of the scenegraph. This is to\nrepresent that in three.js, unlike the other objects, a <a href=\"/docs/#api/en/cameras/Camera\"><code class=\"notranslate\" translate=\"no\">Camera</code></a> does not have\nto be in the scenegraph to function. Just like other objects, a <a href=\"/docs/#api/en/cameras/Camera\"><code class=\"notranslate\" translate=\"no\">Camera</code></a>, as a\nchild of some other object, will move and orient relative to its parent object.\nThere is an example of putting multiple <a href=\"/docs/#api/en/cameras/Camera\"><code class=\"notranslate\" translate=\"no\">Camera</code></a> objects in a scenegraph at\nthe end of <a href=\"scenegraph.html\">the article on scenegraphs</a>.</p>\n</li>\n<li><p><a href=\"/docs/#api/en/objects/Mesh\"><code class=\"notranslate\" translate=\"no\">Mesh</code></a> objects represent drawing a specific <code class=\"notranslate\" translate=\"no\">Geometry</code> with a specific\n <a href=\"/docs/#api/en/materials/Material\"><code class=\"notranslate\" translate=\"no\">Material</code></a>. Both <a href=\"/docs/#api/en/materials/Material\"><code class=\"notranslate\" translate=\"no\">Material</code></a> objects and <code class=\"notranslate\" translate=\"no\">Geometry</code> objects can be used by\n multiple <a href=\"/docs/#api/en/objects/Mesh\"><code class=\"notranslate\" translate=\"no\">Mesh</code></a> objects. For example to draw two blue cubes in different\n locations we could need two <a href=\"/docs/#api/en/objects/Mesh\"><code class=\"notranslate\" translate=\"no\">Mesh</code></a> objects to represent the position and\n orientation of each cube. We would only need one <code class=\"notranslate\" translate=\"no\">Geometry</code> to hold the vertex\n data for a cube and we would only need one <a href=\"/docs/#api/en/materials/Material\"><code class=\"notranslate\" translate=\"no\">Material</code></a> to specify the color\n blue. Both <a href=\"/docs/#api/en/objects/Mesh\"><code class=\"notranslate\" translate=\"no\">Mesh</code></a> objects could reference the same <code class=\"notranslate\" translate=\"no\">Geometry</code> object and the\n same <a href=\"/docs/#api/en/materials/Material\"><code class=\"notranslate\" translate=\"no\">Material</code></a> object.</p>\n</li>\n<li><p><code class=\"notranslate\" translate=\"no\">Geometry</code> objects represent the vertex data of some piece of geometry like\n a sphere, cube, plane, dog, cat, human, tree, building, etc...\n Three.js provides many kinds of built in\n <a href=\"primitives.html\">geometry primitives</a>. You can also\n <a href=\"custom-buffergeometry.html\">create custom geometry</a> as well as\n <a href=\"load-obj.html\">load geometry from files</a>.</p>\n</li>\n<li><p><a href=\"/docs/#api/en/materials/Material\"><code class=\"notranslate\" translate=\"no\">Material</code></a> objects represent\n<a href=\"materials.html\">the surface properties used to draw geometry</a>\nincluding things like the color to use and how shiny it is. A <a href=\"/docs/#api/en/materials/Material\"><code class=\"notranslate\" translate=\"no\">Material</code></a> can also\nreference one or more <a href=\"/docs/#api/en/textures/Texture\"><code class=\"notranslate\" translate=\"no\">Texture</code></a> objects which can be used, for example,\nto wrap an image onto the surface of a geometry.</p>\n</li>\n<li><p><a href=\"/docs/#api/en/textures/Texture\"><code class=\"notranslate\" translate=\"no\">Texture</code></a> objects generally represent images either <a href=\"textures.html\">loaded from image files</a>,\n<a href=\"canvas-textures.html\">generated from a canvas</a> or <a href=\"rendertargets.html\">rendered from another scene</a>.</p>\n</li>\n<li><p><a href=\"/docs/#api/en/lights/Light\"><code class=\"notranslate\" translate=\"no\">Light</code></a> objects represent <a href=\"lights.html\">different kinds of lights</a>.</p>\n</li>\n</ul>\n<p>Given all of that we're going to make the smallest <em>\"Hello Cube\"</em> setup\nthat looks like this</p>\n<div class=\"threejs_center\"><img src=\"../resources/images/threejs-1cube-no-light-scene.svg\" style=\"width: 500px;\"></div>\n\n<p>First let's load three.js</p>\n<pre class=\"prettyprint showlinemods notranslate lang-html\" translate=\"no\">&lt;script type=\"module\"&gt;\nimport * as THREE from 'three';\n&lt;/script&gt;\n</pre>\n<p>It's important you put <code class=\"notranslate\" translate=\"no\">type=\"module\"</code> in the script tag. This enables\nus to use the <code class=\"notranslate\" translate=\"no\">import</code> keyword to load three.js. As of r147, this is the\nonly way to load three.js properly. Modules have the advantage that they can easily import other modules\nthey need. That saves us from having to manually load extra scripts\nthey are dependent on.</p>\n<p>Next we need is a <code class=\"notranslate\" translate=\"no\">&lt;canvas&gt;</code> tag so...</p>\n<pre class=\"prettyprint showlinemods notranslate lang-html\" translate=\"no\">&lt;body&gt;\n  &lt;canvas id=\"c\"&gt;&lt;/canvas&gt;\n&lt;/body&gt;\n</pre>\n<p>We will ask three.js to draw into that canvas so we need to look it up.</p>\n<pre class=\"prettyprint showlinemods notranslate lang-html\" translate=\"no\">&lt;script type=\"module\"&gt;\nimport * as THREE from 'three';\n\n+function main() {\n+  const canvas = document.querySelector('#c');\n+  const renderer = new THREE.WebGLRenderer({antialias: true, canvas});\n+  ...\n&lt;/script&gt;\n</pre>\n<p>After we look up the canvas we create a <a href=\"/docs/#api/en/renderers/WebGLRenderer\"><code class=\"notranslate\" translate=\"no\">WebGLRenderer</code></a>. The renderer\nis the thing responsible for actually taking all the data you provide\nand rendering it to the canvas.</p>\n<p>Note there are some esoteric details here. If you don't pass a canvas\ninto three.js it will create one for you but then you have to add it\nto your document. Where to add it may change depending on your use case\nand you'll have to change your code so I find that passing a canvas\nto three.js feels a little more flexible. I can put the canvas anywhere\nand the code will find it whereas if I had code to insert the canvas\ninto to the document I'd likely have to change that code if my use case\nchanged.</p>\n<p>Next up we need a camera. We'll create a <a href=\"/docs/#api/en/cameras/PerspectiveCamera\"><code class=\"notranslate\" translate=\"no\">PerspectiveCamera</code></a>.</p>\n<pre class=\"prettyprint showlinemods notranslate lang-js\" translate=\"no\">const fov = 75;\nconst aspect = 2;  // the canvas default\nconst near = 0.1;\nconst far = 5;\nconst camera = new THREE.PerspectiveCamera(fov, aspect, near, far);\n</pre>\n<p><code class=\"notranslate\" translate=\"no\">fov</code> is short for <code class=\"notranslate\" translate=\"no\">field of view</code>. In this case 75 degrees in the vertical\ndimension. Note that most angles in three.js are in radians but for some\nreason the perspective camera takes degrees.</p>\n<p><code class=\"notranslate\" translate=\"no\">aspect</code> is the display aspect of the canvas. We'll go over the details\n<a href=\"responsive.html\">in another article</a> but by default a canvas is\n 300x150 pixels which makes the aspect 300/150 or 2.</p>\n<p><code class=\"notranslate\" translate=\"no\">near</code> and <code class=\"notranslate\" translate=\"no\">far</code> represent the space in front of the camera\nthat will be rendered. Anything before that range or after that range\nwill be clipped (not drawn).</p>\n<p>Those four settings define a <em>\"frustum\"</em>. A <em>frustum</em> is the name of\na 3d shape that is like a pyramid with the tip sliced off. In other\nwords think of the word \"frustum\" as another 3D shape like sphere,\ncube, prism, frustum.</p>\n<p><img src=\"../resources/frustum-3d.svg\" width=\"500\" class=\"threejs_center\"></p>\n<p>The height of the near and far planes are determined by the field of view.\nThe width of both planes is determined by the field of view and the aspect.</p>\n<p>Anything inside the defined frustum will be drawn. Anything outside\nwill not.</p>\n<p>The camera defaults to looking down the -Z axis with +Y up. We'll put our cube\nat the origin so we need to move the camera back a little from the origin\nin order to see anything.</p>\n<pre class=\"prettyprint showlinemods notranslate lang-js\" translate=\"no\">camera.position.z = 2;\n</pre>\n<p>Here's what we're aiming for.</p>\n<p><img src=\"../resources/scene-down.svg\" width=\"500\" class=\"threejs_center\"></p>\n<p>In the diagram above we can see our camera is at <code class=\"notranslate\" translate=\"no\">z = 2</code>. It's looking\ndown the -Z axis. Our frustum starts 0.1 units from the front of the camera\nand goes to 5 units in front of the camera. Because in this diagram we are looking down,\nthe field of view is affected by the aspect. Our canvas is twice as wide\nas it is tall so across the canvas the field of view will be much wider than\nour specified 75 degrees which is the vertical field of view.</p>\n<p>Next we make a <a href=\"/docs/#api/en/scenes/Scene\"><code class=\"notranslate\" translate=\"no\">Scene</code></a>. A <a href=\"/docs/#api/en/scenes/Scene\"><code class=\"notranslate\" translate=\"no\">Scene</code></a> in three.js is the root of a form of scene graph.\nAnything you want three.js to draw needs to be added to the scene. We'll\ncover more details of <a href=\"scenegraph.html\">how scenes work in a future article</a>.</p>\n<pre class=\"prettyprint showlinemods notranslate lang-js\" translate=\"no\">const scene = new THREE.Scene();\n</pre>\n<p>Next up we create a <a href=\"/docs/#api/en/geometries/BoxGeometry\"><code class=\"notranslate\" translate=\"no\">BoxGeometry</code></a> which contains the data for a box.\nAlmost anything we want to display in Three.js needs geometry which defines\nthe vertices that make up our 3D object.</p>\n<pre class=\"prettyprint showlinemods notranslate lang-js\" translate=\"no\">const boxWidth = 1;\nconst boxHeight = 1;\nconst boxDepth = 1;\nconst geometry = new THREE.BoxGeometry(boxWidth, boxHeight, boxDepth);\n</pre>\n<p>We then create a basic material and set its color. Colors can\nbe specified using standard CSS style 6 digit hex color values.</p>\n<pre class=\"prettyprint showlinemods notranslate lang-js\" translate=\"no\">const material = new THREE.MeshBasicMaterial({color: 0x44aa88});\n</pre>\n<p>We then create a <a href=\"/docs/#api/en/objects/Mesh\"><code class=\"notranslate\" translate=\"no\">Mesh</code></a>. A <a href=\"/docs/#api/en/objects/Mesh\"><code class=\"notranslate\" translate=\"no\">Mesh</code></a> in three.js represents the combination\nof three things</p>\n<ol>\n<li>A <code class=\"notranslate\" translate=\"no\">Geometry</code> (the shape of the object)</li>\n<li>A <a href=\"/docs/#api/en/materials/Material\"><code class=\"notranslate\" translate=\"no\">Material</code></a> (how to draw the object, shiny or flat, what color, what texture(s) to apply. Etc.)</li>\n<li>The position, orientation, and scale of that object in the scene relative to its parent. In the code below that parent is the scene.</li>\n</ol>\n<pre class=\"prettyprint showlinemods notranslate lang-js\" translate=\"no\">const cube = new THREE.Mesh(geometry, material);\n</pre>\n<p>And finally we add that mesh to the scene</p>\n<pre class=\"prettyprint showlinemods notranslate lang-js\" translate=\"no\">scene.add(cube);\n</pre>\n<p>We can then render the scene by calling the renderer's render function\nand passing it the scene and the camera</p>\n<pre class=\"prettyprint showlinemods notranslate lang-js\" translate=\"no\">renderer.render(scene, camera);\n</pre>\n<p>Here's a working example</p>\n<p></p><div translate=\"no\" class=\"threejs_example_container notranslate\">\n  <div></div>\n  <a class=\"threejs_center\" href=\"/manual/examples/fundamentals.html\" target=\"_blank\">click here to open in a separate window</a>\n</div>\n\n<p></p>\n<p>It's kind of hard to tell that is a 3D cube since we're viewing\nit directly down the -Z axis and the cube itself is axis aligned\nso we're only seeing a single face.</p>\n<p>Let's animate it spinning and hopefully that will make\nit clear it's being drawn in 3D. To animate it we'll render inside a render loop using\n<a href=\"https://developer.mozilla.org/en-US/docs/Web/API/window/requestAnimationFrame\"><code class=\"notranslate\" translate=\"no\">requestAnimationFrame</code></a>.</p>\n<p>Here's our loop</p>\n<pre class=\"prettyprint showlinemods notranslate lang-js\" translate=\"no\">function render(time) {\n  time *= 0.001;  // convert time to seconds\n\n  cube.rotation.x = time;\n  cube.rotation.y = time;\n\n  renderer.render(scene, camera);\n\n  requestAnimationFrame(render);\n}\nrequestAnimationFrame(render);\n</pre>\n<p><code class=\"notranslate\" translate=\"no\">requestAnimationFrame</code> is a request to the browser that you want to animate something.\nYou pass it a function to be called. In our case that function is <code class=\"notranslate\" translate=\"no\">render</code>. The browser\nwill call your function and if you update anything related to the display of the\npage the browser will re-render the page. In our case we are calling three's\n<code class=\"notranslate\" translate=\"no\">renderer.render</code> function which will draw our scene.</p>\n<p><code class=\"notranslate\" translate=\"no\">requestAnimationFrame</code> passes the time since the page loaded to\nour function. That time is passed in milliseconds. I find it's much\neasier to work with seconds so here we're converting that to seconds.</p>\n<p>We then set the cube's X and Y rotation to the current time. These rotations\nare in <a href=\"https://en.wikipedia.org/wiki/Radian\">radians</a>. There are 2 pi radians\nin a circle so our cube should turn around once on each axis in about 6.28\nseconds.</p>\n<p>We then render the scene and request another animation frame to continue\nour loop.</p>\n<p>Outside the loop we call <code class=\"notranslate\" translate=\"no\">requestAnimationFrame</code> one time to start the loop.</p>\n<p></p><div translate=\"no\" class=\"threejs_example_container notranslate\">\n  <div></div>\n  <a class=\"threejs_center\" href=\"/manual/examples/fundamentals-with-animation.html\" target=\"_blank\">click here to open in a separate window</a>\n</div>\n\n<p></p>\n<p>It's a little better but it's still hard to see the 3d. What would help is to\nadd some lighting so let's add a light. There are many kinds of lights in\nthree.js which we'll go over in <a href=\"lights.html\">a future article</a>. For now let's create a directional light.</p>\n<pre class=\"prettyprint showlinemods notranslate lang-js\" translate=\"no\">const color = 0xFFFFFF;\nconst intensity = 3;\nconst light = new THREE.DirectionalLight(color, intensity);\nlight.position.set(-1, 2, 4);\nscene.add(light);\n</pre>\n<p>Directional lights have a position and a target. Both default to 0, 0, 0. In our\ncase we're setting the light's position to -1, 2, 4 so it's slightly on the left,\nabove, and behind our camera. The target is still 0, 0, 0 so it will shine\ntoward the origin.</p>\n<p>We also need to change the material. The <a href=\"/docs/#api/en/materials/MeshBasicMaterial\"><code class=\"notranslate\" translate=\"no\">MeshBasicMaterial</code></a> is not affected by\nlights. Let's change it to a <a href=\"/docs/#api/en/materials/MeshPhongMaterial\"><code class=\"notranslate\" translate=\"no\">MeshPhongMaterial</code></a> which is affected by lights.</p>\n<pre class=\"prettyprint showlinemods notranslate lang-js\" translate=\"no\">-const material = new THREE.MeshBasicMaterial({color: 0x44aa88});  // greenish blue\n+const material = new THREE.MeshPhongMaterial({color: 0x44aa88});  // greenish blue\n</pre>\n<p>Here is our new program structure</p>\n<div class=\"threejs_center\"><img src=\"../resources/images/threejs-1cube-with-directionallight.svg\" style=\"width: 500px;\"></div>\n\n<p>And here it is working.</p>\n<p></p><div translate=\"no\" class=\"threejs_example_container notranslate\">\n  <div></div>\n  <a class=\"threejs_center\" href=\"/manual/examples/fundamentals-with-light.html\" target=\"_blank\">click here to open in a separate window</a>\n</div>\n\n<p></p>\n<p>It should now be pretty clearly 3D.</p>\n<p>Just for the fun of it let's add 2 more cubes.</p>\n<p>We'll use the same geometry for each cube but make a different\nmaterial so each cube can be a different color.</p>\n<p>First we'll make a function that creates a new material\nwith the specified color. Then it creates a mesh using\nthe specified geometry and adds it to the scene and\nsets its X position.</p>\n<pre class=\"prettyprint showlinemods notranslate lang-js\" translate=\"no\">function makeInstance(geometry, color, x) {\n  const material = new THREE.MeshPhongMaterial({color});\n\n  const cube = new THREE.Mesh(geometry, material);\n  scene.add(cube);\n\n  cube.position.x = x;\n\n  return cube;\n}\n</pre>\n<p>Then we'll call it 3 times with 3 different colors and X positions\nsaving the <a href=\"/docs/#api/en/objects/Mesh\"><code class=\"notranslate\" translate=\"no\">Mesh</code></a> instances in an array.</p>\n<pre class=\"prettyprint showlinemods notranslate lang-js\" translate=\"no\">const cubes = [\n  makeInstance(geometry, 0x44aa88,  0),\n  makeInstance(geometry, 0x8844aa, -2),\n  makeInstance(geometry, 0xaa8844,  2),\n];\n</pre>\n<p>Finally we'll spin all 3 cubes in our render function. We\ncompute a slightly different rotation for each one.</p>\n<pre class=\"prettyprint showlinemods notranslate lang-js\" translate=\"no\">function render(time) {\n  time *= 0.001;  // convert time to seconds\n\n  cubes.forEach((cube, ndx) =&gt; {\n    const speed = 1 + ndx * .1;\n    const rot = time * speed;\n    cube.rotation.x = rot;\n    cube.rotation.y = rot;\n  });\n\n  ...\n</pre>\n<p>and here's that.</p>\n<p></p><div translate=\"no\" class=\"threejs_example_container notranslate\">\n  <div></div>\n  <a class=\"threejs_center\" href=\"/manual/examples/fundamentals-3-cubes.html\" target=\"_blank\">click here to open in a separate window</a>\n</div>\n\n<p></p>\n<p>If you compare it to the top down diagram above you can see\nit matches our expectations. With cubes at X = -2 and X = +2\nthey are partially outside our frustum. They are also\nsomewhat exaggeratedly warped since the field of view\nacross the canvas is so extreme.</p>\n<p>Our program now has this structure</p>\n<div class=\"threejs_center\"><img src=\"../resources/images/threejs-3cubes-scene.svg\" style=\"width: 610px;\"></div>\n\n<p>As you can see we have 3 <a href=\"/docs/#api/en/objects/Mesh\"><code class=\"notranslate\" translate=\"no\">Mesh</code></a> objects each referencing the same <a href=\"/docs/#api/en/geometries/BoxGeometry\"><code class=\"notranslate\" translate=\"no\">BoxGeometry</code></a>.\nEach <a href=\"/docs/#api/en/objects/Mesh\"><code class=\"notranslate\" translate=\"no\">Mesh</code></a> references a unique <a href=\"/docs/#api/en/materials/MeshPhongMaterial\"><code class=\"notranslate\" translate=\"no\">MeshPhongMaterial</code></a> so that each cube can have\na different color.</p>\n<p>I hope this short intro helps to get things started. <a href=\"responsive.html\">Next up we'll cover\nmaking our code responsive so it is adaptable to multiple situations</a>.</p>\n<div id=\"es6\" class=\"threejs_bottombar\">\n<h3>es6 modules, three.js, and folder structure</h3>\n<p>As of version r147 the preferred way to use three.js is via <a href=\"https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/import\">es6 modules</a> and import maps.</p>\n<p>\nes6 modules can be loaded via the <code class=\"notranslate\" translate=\"no\">import</code> keyword in a script\nor inline via a <code class=\"notranslate\" translate=\"no\">&lt;script type=\"module\"&gt;</code> tag. Here's an example\n</p>\n<pre class=\"prettyprint showlinemods notranslate lang-html\" translate=\"no\">&lt;script type=\"module\"&gt;\nimport * as THREE from 'three';\n\n...\n\n&lt;/script&gt;\n</pre>\n<p>\nNotice <code class=\"notranslate\" translate=\"no\">'three'</code> specifier there. If you leave it as it is, it will likely produce an error. An <i>import map</i> should be used to tell the browser where to find three.js\n</p>\n<pre class=\"prettyprint showlinemods notranslate lang-html\" translate=\"no\">&lt;script type=\"importmap\"&gt;\n{\n  \"imports\": {\n    \"three\": \"./path/to/three.module.js\"\n  }\n}\n&lt;/script&gt;\n</pre>\n<p>\nNote that path specifier can start only with <code class=\"notranslate\" translate=\"no\">./</code> or <code class=\"notranslate\" translate=\"no\">../</code>.\n</p>\n<p>\nTo import addons like <a href=\"https://github.com/mrdoob/three.js/blob/master/examples/jsm/controls/OrbitControls.js\"><code class=\"notranslate\" translate=\"no\">OrbitControls.js</code></a> use the following\n</p>\n<pre class=\"prettyprint showlinemods notranslate lang-js\" translate=\"no\">import {OrbitControls} from 'three/addons/controls/OrbitControls.js';\n</pre>\n<p>\nDon't forget to add addons to the import map like so\n</p>\n<pre class=\"prettyprint showlinemods notranslate lang-html\" translate=\"no\">&lt;script type=\"importmap\"&gt;\n{\n  \"imports\": {\n    \"three\": \"./path/to/three.module.js\",\n    \"three/addons/\": \"./different/path/to/examples/jsm/\"\n  }\n}\n&lt;/script&gt;\n</pre>\n<p>\nYou can also use a CDN\n</p>\n<pre class=\"prettyprint showlinemods notranslate lang-html\" translate=\"no\">&lt;script type=\"importmap\"&gt;\n{\n  \"imports\": {\n    \"three\": \"https://cdn.jsdelivr.net/npm/three@&lt;version&gt;/build/three.module.js\",\n    \"three/addons/\": \"https://cdn.jsdelivr.net/npm/three@&lt;version&gt;/examples/jsm/\"\n  }\n}\n&lt;/script&gt;\n</pre>\n<p>\nTo conclude, the recommended way of using three.js is\n</p>\n<pre class=\"prettyprint showlinemods notranslate lang-html\" translate=\"no\">&lt;script type=\"importmap\"&gt;\n{\n  \"imports\": {\n    \"three\": \"./path/to/three.module.js\",\n    \"three/addons/\": \"./different/path/to/examples/jsm/\"\n  }\n}\n&lt;/script&gt;\n\n&lt;script type=\"module\"&gt;\nimport * as THREE from 'three';\nimport {OrbitControls} from 'three/addons/controls/OrbitControls.js';\n\n...\n\n&lt;/script&gt;\n</pre>\n</div>\n\n<!-- needed in English only to prevent warning from outdated translations -->\n<p><a href=\"geometry.html\"></a>\n<a href=\"Geometry\"></a></p>\n\n        </div>\n      </div>\n    </div>",
    "success": true,
    "error": "",
    "crawled_at": 1786376809.300204,
    "is_shell": false
  },
  {
    "url": "https://threejs.org/manual/en/responsive.html",
    "title": "Responsive Design",
    "content": "<div class=\"container\">\n      <div class=\"lesson-title\">\n        <h1>Responsive Design</h1>\n      </div>\n      <div class=\"lesson\">\n        <div class=\"lesson-main\">\n          <p>This is the second article in a series of articles about three.js.\nThe first article was <a href=\"fundamentals.html\">about fundamentals</a>.\nIf you haven't read that yet you might want to start there.</p>\n<p>This article is about how to make your three.js app be responsive\nto any situation. Making a webpage responsive generally refers\nto the page displaying well on different sized displays from\ndesktops to tablets to phones.</p>\n<p>For three.js there are even more situations to consider. For\nexample, a 3D editor with controls on the left, right, top, or\nbottom is something we might want to handle. A live diagram\nin the middle of a document is another example.</p>\n<p>The last sample we had used a plain canvas with no CSS and\nno size</p>\n<pre class=\"prettyprint showlinemods notranslate lang-html\" translate=\"no\">&lt;canvas id=\"c\"&gt;&lt;/canvas&gt;\n</pre>\n<p>That canvas defaults to 300x150 CSS pixels in size.</p>\n<p>In the web platform the recommended way to set the size\nof something is to use CSS.</p>\n<p>Let's make the canvas fill the page by adding CSS</p>\n<pre class=\"prettyprint showlinemods notranslate lang-html\" translate=\"no\">&lt;style&gt;\nhtml, body {\n   margin: 0;\n   height: 100%;\n}\n#c {\n   width: 100%;\n   height: 100%;\n   display: block;\n}\n&lt;/style&gt;\n</pre>\n<p>In HTML the body has a margin of 5 pixels by default so setting the\nmargin to 0 removes the margin. Setting the html and body height to 100%\nmakes them fill the window. Otherwise they are only as large\nas the content that fills them.</p>\n<p>Next we tell the <code class=\"notranslate\" translate=\"no\">id=c</code> element to be\n100% the size of its container which in this case is the body of\nthe document.</p>\n<p>Finally we set its <code class=\"notranslate\" translate=\"no\">display</code> mode to <code class=\"notranslate\" translate=\"no\">block</code>. A canvas's\ndefault display mode is <code class=\"notranslate\" translate=\"no\">inline</code>. Inline\nelements can end up adding whitespace to what is displayed. By\nsetting the canvas to <code class=\"notranslate\" translate=\"no\">block</code> that issue goes away.</p>\n<p>Here's the result</p>\n<p></p><div translate=\"no\" class=\"threejs_example_container notranslate\">\n  <div></div>\n  <a class=\"threejs_center\" href=\"/manual/examples/responsive-no-resize.html\" target=\"_blank\">click here to open in a separate window</a>\n</div>\n\n<p></p>\n<p>You can see the canvas is now filling the page but there are 2\nproblems. One our cubes are stretched. They are not cubes they\nare more like boxes. Too tall or too wide. Open the\nexample in its own window and resize it. You'll see how\nthe cubes get stretched wide and tall.</p>\n<p><img src=\"../resources/images/resize-incorrect-aspect.png\" width=\"407\" class=\"threejs_center nobg\"></p>\n<p>The second problem is they look low resolution or blocky and\nblurry. Stretch the window really large and you'll really see\nthe issue.</p>\n<p><img src=\"../resources/images/resize-low-res.png\" class=\"threejs_center nobg\"></p>\n<p>Let's fix the stretchy problem first. To do that we need\nto set the aspect of the camera to the aspect of the canvas's\ndisplay size. We can do that by looking at the canvas's\n<code class=\"notranslate\" translate=\"no\">clientWidth</code> and <code class=\"notranslate\" translate=\"no\">clientHeight</code> properties.</p>\n<p>We'll update our render loop like this</p>\n<pre class=\"prettyprint showlinemods notranslate lang-js\" translate=\"no\">function render(time) {\n  time *= 0.001;\n\n+  const canvas = renderer.domElement;\n+  camera.aspect = canvas.clientWidth / canvas.clientHeight;\n+  camera.updateProjectionMatrix();\n\n  ...\n</pre>\n<p>Now the cubes should stop being distorted.</p>\n<p></p><div translate=\"no\" class=\"threejs_example_container notranslate\">\n  <div></div>\n  <a class=\"threejs_center\" href=\"/manual/examples/responsive-update-camera.html\" target=\"_blank\">click here to open in a separate window</a>\n</div>\n\n<p></p>\n<p>Open the example in a separate window and resize the window\nand you should see the cubes are no longer stretched tall or wide.\nThey stay the correct aspect regardless of window size.</p>\n<p><img src=\"../resources/images/resize-correct-aspect.png\" width=\"407\" class=\"threejs_center nobg\"></p>\n<p>Now let's fix the blockiness.</p>\n<p>Canvas elements have 2 sizes. One size is the size the canvas is displayed\non the page. That's what we set with CSS. The other size is the\nnumber of pixels in the canvas itself. This is no different than an image.\nFor example we might have a 128x64 pixel image and using\nCSS we might display as 400x200 pixels.</p>\n<pre class=\"prettyprint showlinemods notranslate lang-html\" translate=\"no\">&lt;img src=\"some128x64image.jpg\" style=\"width:400px; height:200px\"&gt;\n</pre>\n<p>A canvas's internal size, its resolution, is often called its drawingbuffer size.\nIn three.js we can set the canvas's drawingbuffer size by calling <code class=\"notranslate\" translate=\"no\">renderer.setSize</code>.\nWhat size should we pick? The most obvious answer is \"the same size the canvas is displayed\".\nAgain, to do that we can look at the canvas's <code class=\"notranslate\" translate=\"no\">clientWidth</code> and <code class=\"notranslate\" translate=\"no\">clientHeight</code>\nproperties.</p>\n<p>Let's write a function that checks if the renderer's canvas is not\nalready the size it is being displayed as and if so set its size.</p>\n<pre class=\"prettyprint showlinemods notranslate lang-js\" translate=\"no\">function resizeRendererToDisplaySize(renderer) {\n  const canvas = renderer.domElement;\n  const width = canvas.clientWidth;\n  const height = canvas.clientHeight;\n  const needResize = canvas.width !== width || canvas.height !== height;\n  if (needResize) {\n    renderer.setSize(width, height, false);\n  }\n  return needResize;\n}\n</pre>\n<p>Notice we check if the canvas actually needs to be resized. Resizing the canvas\nis an interesting part of the canvas spec and it's best not to set the same\nsize if it's already the size we want.</p>\n<p>Once we know if we need to resize or not we then call <code class=\"notranslate\" translate=\"no\">renderer.setSize</code> and\npass in the new width and height. It's important to pass <code class=\"notranslate\" translate=\"no\">false</code> at the end.\n<code class=\"notranslate\" translate=\"no\">renderer.setSize</code> by default sets the canvas's CSS size but doing so is not\nwhat we want. We want the browser to continue to work how it does for all other\nelements which is to use CSS to determine the display size of the element. We don't\nwant canvases used by three to be different than other elements.</p>\n<p>Note that our function returns true if the canvas was resized. We can use\nthis to check if there are other things we should update. Let's modify\nour render loop to use the new function</p>\n<pre class=\"prettyprint showlinemods notranslate lang-js\" translate=\"no\">function render(time) {\n  time *= 0.001;\n\n+  if (resizeRendererToDisplaySize(renderer)) {\n+    const canvas = renderer.domElement;\n+    camera.aspect = canvas.clientWidth / canvas.clientHeight;\n+    camera.updateProjectionMatrix();\n+  }\n\n  ...\n</pre>\n<p>Since the aspect is only going to change if the canvas's display size\nchanged we only set the camera's aspect if <code class=\"notranslate\" translate=\"no\">resizeRendererToDisplaySize</code>\nreturns <code class=\"notranslate\" translate=\"no\">true</code>.</p>\n<p></p><div translate=\"no\" class=\"threejs_example_container notranslate\">\n  <div></div>\n  <a class=\"threejs_center\" href=\"/manual/examples/responsive.html\" target=\"_blank\">click here to open in a separate window</a>\n</div>\n\n<p></p>\n<p>It should now render with a resolution that matches the display\nsize of the canvas.</p>\n<p>To make the point about letting CSS handle the resizing let's take\nour code and put it in a <a href=\"../examples/threejs-responsive.js\">separate <code class=\"notranslate\" translate=\"no\">.js</code> file</a>.\nHere then are a few more examples where we let CSS choose the size and notice we had\nto change zero code for them to work.</p>\n<p>Let's put our cubes in the middle of a paragraph of text.</p>\n<p></p><div translate=\"no\" class=\"threejs_example_container notranslate\">\n  <div></div>\n  <a class=\"threejs_center\" href=\"/manual/examples/responsive-paragraph.html\" target=\"_blank\">click here to open in a separate window</a>\n</div>\n\n<p></p>\n<p>and here's our same code used in an editor style layout\nwhere the control area on the right can be resized.</p>\n<p></p><div translate=\"no\" class=\"threejs_example_container notranslate\">\n  <div></div>\n  <a class=\"threejs_center\" href=\"/manual/examples/responsive-editor.html\" target=\"_blank\">click here to open in a separate window</a>\n</div>\n\n<p></p>\n<p>The important part to notice is no code changed. Only our HTML and CSS\nchanged.</p>\n<h2 id=\"handling-hd-dpi-displays\">Handling HD-DPI displays</h2>\n<p>HD-DPI stands for high-density dot per inch displays.\nThat's most Macs nowadays and many Windows machines\nas well as pretty much all smartphones.</p>\n<p>The way this works in the browser is they use\nCSS pixels to set the sizes which are supposed to be the same\nregardless of how high res the display is. The browser\nwill just render text with more detail but the\nsame physical size.</p>\n<p>There are various ways to handle HD-DPI with three.js.</p>\n<p>The first one is just not to do anything special. This\nis arguably the most common. Rendering 3D graphics\ntakes a lot of GPU processing power. Mobile GPUs have\nless power than desktops, at least as of 2018, and yet\nmobile phones often have very high resolution displays.\nThe current top of the line phones have an HD-DPI ratio\nof 3x meaning for every one pixel from a non-HD-DPI display\nthose phones have 9 pixels. That means they have to do 9x\nthe rendering.</p>\n<p>Computing 9x the pixels is a lot of work so if we just\nleave the code as it is we'll compute 1x the pixels and the\nbrowser will just draw it at 3x the size (3x by 3x = 9x pixels).</p>\n<p>For any heavy three.js app that's probably what you want\notherwise you're likely to get a slow framerate.</p>\n<p>That said if you actually do want to render at the resolution\nof the device there are a couple of ways to do this in three.js.</p>\n<p>One is to tell three.js a resolution multiplier using <code class=\"notranslate\" translate=\"no\">renderer.setPixelRatio</code>.\nYou ask the browser what the multiplier is from CSS pixels to device pixels\nand pass that to three.js</p>\n<pre class=\"prettyprint showlinemods notranslate notranslate\" translate=\"no\"> renderer.setPixelRatio(window.devicePixelRatio);\n</pre><p>After that any calls to <code class=\"notranslate\" translate=\"no\">renderer.setSize</code> will magically\nuse the size you request multiplied by whatever pixel ratio\nyou passed in. <strong>This is strongly NOT RECOMMENDED</strong>. See below</p>\n<p>The other way is to do it yourself when you resize the canvas.</p>\n<pre class=\"prettyprint showlinemods notranslate lang-js\" translate=\"no\">    function resizeRendererToDisplaySize(renderer) {\n      const canvas = renderer.domElement;\n      const pixelRatio = window.devicePixelRatio;\n      const width  = Math.floor( canvas.clientWidth  * pixelRatio );\n      const height = Math.floor( canvas.clientHeight * pixelRatio );\n      const needResize = canvas.width !== width || canvas.height !== height;\n      if (needResize) {\n        renderer.setSize(width, height, false);\n      }\n      return needResize;\n    }\n</pre>\n<p>This second way is objectively better. Why? Because it means I get what I ask for.\nThere are many cases when using three.js where we need to know the actual\nsize of the canvas's drawingBuffer. For example when making a post processing filter,\nor if we are making a shader that accesses <code class=\"notranslate\" translate=\"no\">gl_FragCoord</code>, if we are making\na screenshot, or reading pixels for GPU picking, for drawing into a 2D canvas,\netc... There are many cases where if we use <code class=\"notranslate\" translate=\"no\">setPixelRatio</code> then our actual size will be different\nthan the size we requested and we'll have to guess when to use the size\nwe asked for and when to use the size three.js is actually using.\nBy doing it ourselves we always know the size being used is the size we requested.\nThere is no special case where magic is happening behind the scenes.</p>\n<p>Here's an example using the code above.</p>\n<p></p><div translate=\"no\" class=\"threejs_example_container notranslate\">\n  <div></div>\n  <a class=\"threejs_center\" href=\"/manual/examples/responsive-hd-dpi.html\" target=\"_blank\">click here to open in a separate window</a>\n</div>\n\n<p></p>\n<p>It might be hard to see the difference but if you have an HD-DPI\ndisplay and you compare this sample to those above you should\nnotice the edges are more crisp.</p>\n\n<h2 id=\"hd-dpi-limiting-maximum-drawing-buffer-size\">\n  HD-DPI: Limiting maximum drawing buffer size\n</h2>\n\n<p>When using a fractional UI scaling factor on some operating system\n(eg: 150% on OSX or Linux) the assumption that the real physical\nresolution equals <code>width * window.devicePixelRatio</code> and\n<code>height * window.devicePixelRatio</code> no longer holds.\nThis may lead to excessive GPU load, lower frame rates and high power \nconsumption.</p> \n<p>A possible mitigation is to cap the maximum internal resolution (e.g. limit\nwidth × height) so the buffer remains within safe bounds.</p> \n\n<pre class=\"prettyprint showlinemods notranslate lang-js\" translate=\"no\">    function resizeRendererToDisplaySize(renderer, maxPixelCount=3840*2160) {\n      const canvas = renderer.domElement;\n      const pixelRatio = window.devicePixelRatio;\n      let width  = Math.floor( canvas.clientWidth  * pixelRatio );\n      let height = Math.floor( canvas.clientHeight * pixelRatio );\n      const pixelCount = width * height;\n      const renderScale = pixelCount &gt; maxPixelCount ? Math.sqrt(maxPixelCount / pixelCount) : 1;\n      width = Math.floor(width * renderScale);\n      height = Math.floor(height * renderScale);\n\n      const needResize = canvas.width !== width || canvas.height !== height;\n      if (needResize) {\n        renderer.setSize(width, height, false);\n      }\n      return needResize;\n    }\n</pre>\n<p>This article covered a very basic but fundamental topic. Next up lets quickly\n<a href=\"primitives.html\">go over the basic primitives that three.js provides</a>.</p>\n        </div>\n      </div>\n    </div>",
    "success": true,
    "error": "",
    "crawled_at": 1786376809.300204,
    "is_shell": false
  },
  {
    "url": "https://threejs.org/manual/en/prerequisites.html",
    "title": "Prerequisites",
    "content": "<div class=\"container\">\n      <div class=\"lesson-title\">\n        <h1>Prerequisites</h1>\n      </div>\n      <div class=\"lesson\">\n        <div class=\"lesson-main\">\n          <p>These articles are meant to help you learn how to use three.js.\nThey assume you know how to program in JavaScript. They assume\nyou know what the DOM is, how to write HTML as well as create DOM elements\nin JavaScript. They assume you know how to use\n<a href=\"https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/import\">es6 modules</a>\nvia import and via <code class=\"notranslate\" translate=\"no\">&lt;script type=\"module\"&gt;</code> tags. They assume you know how to use import maps.\nThey assume you know some CSS and that you know what\n<a href=\"https://developer.mozilla.org/en-US/docs/Learn/CSS/Introduction_to_CSS/Selectors\">CSS selectors are</a>.\nThey also assume you know ES5, ES6 and maybe some ES7.\nThey assume you know that the browser runs JavaScript only via events and callbacks.\nThey assume you know what a closure is.</p>\n<p>Here's some brief refreshers and notes</p>\n<h2 id=\"es6-modules\">es6 modules</h2>\n<p>es6 modules can be loaded via the <code class=\"notranslate\" translate=\"no\">import</code> keyword in a script\nor inline via a <code class=\"notranslate\" translate=\"no\">&lt;script type=\"module\"&gt;</code> tag. Here's an example</p>\n<pre class=\"prettyprint showlinemods notranslate lang-html\" translate=\"no\">&lt;script type=\"importmap\"&gt;\n{\n  \"imports\": {\n    \"three\": \"./path/to/three.module.js\",\n    \"three/addons/\": \"./different/path/to/examples/jsm/\"\n  }\n}\n&lt;/script&gt;\n\n&lt;script type=\"module\"&gt;\nimport * as THREE from 'three';\nimport {OrbitControls} from 'three/addons/controls/OrbitControls.js';\n\n...\n\n&lt;/script&gt;\n</pre>\n<p>See more details at the bottom of <a href=\"fundamentals.html\">this article</a>.</p>\n<h2 id=\"-document-queryselector-and-document-queryselectorall-\"><code class=\"notranslate\" translate=\"no\">document.querySelector</code> and <code class=\"notranslate\" translate=\"no\">document.querySelectorAll</code></h2>\n<p>You can use <code class=\"notranslate\" translate=\"no\">document.querySelector</code> to select the first element\nthat matches a CSS selector. <code class=\"notranslate\" translate=\"no\">document.querySelectorAll</code> returns\nall elements that match a CSS selector.</p>\n<h2 id=\"you-don-t-need-onload-\">You don't need <code class=\"notranslate\" translate=\"no\">onload</code></h2>\n<p>Lots of 20yr old pages use HTML like</p>\n<pre class=\"prettyprint showlinemods notranslate notranslate\" translate=\"no\">&lt;body onload=\"somefunction()\"&gt;\n</pre><p>That style is deprecated. Put your scripts\nat the bottom of the page.</p>\n<pre class=\"prettyprint showlinemods notranslate lang-html\" translate=\"no\">&lt;html&gt;\n  &lt;head&gt;\n    ...\n  &lt;/head&gt;\n  &lt;body&gt;\n     ...\n  &lt;/body&gt;\n  &lt;script&gt;\n    // inline javascript\n  &lt;/script&gt;\n&lt;/html&gt;\n</pre>\n<p>or <a href=\"https://developer.mozilla.org/en-US/docs/Web/HTML/Element/script\">use the <code class=\"notranslate\" translate=\"no\">defer</code> property</a>.</p>\n<h2 id=\"know-how-closures-work\">Know how closures work</h2>\n<pre class=\"prettyprint showlinemods notranslate lang-js\" translate=\"no\">function a(v) {\n  const foo = v;\n  return function() {\n     return foo;\n  };\n}\n\nconst f = a(123);\nconst g = a(456);\nconsole.log(f());  // prints 123\nconsole.log(g());  // prints 456\n</pre>\n<p>In the code above the function <code class=\"notranslate\" translate=\"no\">a</code> creates a new function every time it's called. That\nfunction <em>closes</em> over the variable <code class=\"notranslate\" translate=\"no\">foo</code>. Here's <a href=\"https://developer.mozilla.org/en-US/docs/Web/JavaScript/Closures\">more info</a>.</p>\n<h2 id=\"understand-how-this-works\">Understand how <code class=\"notranslate\" translate=\"no\">this</code> works</h2>\n<p><code class=\"notranslate\" translate=\"no\">this</code> is not magic. It's effectively a variable that is automatically passed to functions just like\nan argument is passed to function. The simple explanation is when you call a function directly\nlike</p>\n<pre class=\"prettyprint showlinemods notranslate notranslate\" translate=\"no\">somefunction(a, b, c);\n</pre><p><code class=\"notranslate\" translate=\"no\">this</code> will be <code class=\"notranslate\" translate=\"no\">null</code> (when in strict mode or in a module) where as when you call a function via the dot operator <code class=\"notranslate\" translate=\"no\">.</code> like this</p>\n<pre class=\"prettyprint showlinemods notranslate notranslate\" translate=\"no\">someobject.somefunction(a, b, c);\n</pre><p><code class=\"notranslate\" translate=\"no\">this</code> will be set to <code class=\"notranslate\" translate=\"no\">someobject</code>.</p>\n<p>The parts where people get confused is with callbacks.</p>\n<pre class=\"prettyprint showlinemods notranslate notranslate\" translate=\"no\"> const callback = someobject.somefunction;\n loader.load(callback);\n</pre><p>doesn't work as someone inexperienced might expect because when\n<code class=\"notranslate\" translate=\"no\">loader.load</code> calls the callback it's not calling it with the dot <code class=\"notranslate\" translate=\"no\">.</code> operator\nso by default <code class=\"notranslate\" translate=\"no\">this</code> will be null (unless the loader explicitly sets it to something).\nIf you want <code class=\"notranslate\" translate=\"no\">this</code> to be <code class=\"notranslate\" translate=\"no\">someobject</code> when the callback happens you need to\ntell JavaScript that by binding it to the function.</p>\n<pre class=\"prettyprint showlinemods notranslate notranslate\" translate=\"no\"> const callback = someobject.somefunction.bind(someobject);\n loader.load(callback);\n</pre><p><a href=\"https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/this\"><em>this</em> article might help explain <code class=\"notranslate\" translate=\"no\">this</code></a>.</p>\n<h2 id=\"es5-es6-es7-stuff\">ES5/ES6/ES7 stuff</h2>\n<h3 id=\"-var-is-deprecated-use-const-and-or-let-\"><code class=\"notranslate\" translate=\"no\">var</code> is deprecated. Use <code class=\"notranslate\" translate=\"no\">const</code> and/or <code class=\"notranslate\" translate=\"no\">let</code></h3>\n<p>There is no reason to use <code class=\"notranslate\" translate=\"no\">var</code> <strong>EVER</strong> and at this point it's considered bad practice\nto use it at all. Use <code class=\"notranslate\" translate=\"no\">const</code> if the variable will never be reassigned which is most of\nthe time. Use <code class=\"notranslate\" translate=\"no\">let</code> in those cases where the value changes. This will help avoid tons of bugs.</p>\n<h3 id=\"use-for-elem-of-collection-never-for-elem-in-collection-\">Use <code class=\"notranslate\" translate=\"no\">for(elem of collection)</code> never <code class=\"notranslate\" translate=\"no\">for(elem in collection)</code></h3>\n<p><code class=\"notranslate\" translate=\"no\">for of</code> is new, <code class=\"notranslate\" translate=\"no\">for in</code> is old. <code class=\"notranslate\" translate=\"no\">for in</code> had issues that are solved by <code class=\"notranslate\" translate=\"no\">for of</code></p>\n<p>As one example you can iterate over all the key/value pairs of an object with</p>\n<pre class=\"prettyprint showlinemods notranslate lang-js\" translate=\"no\">for (const [key, value] of Object.entries(someObject)) {\n  console.log(key, value);\n}\n</pre>\n<h3 id=\"use-foreach-map-and-filter-where-useful\">Use <code class=\"notranslate\" translate=\"no\">forEach</code>, <code class=\"notranslate\" translate=\"no\">map</code>, and <code class=\"notranslate\" translate=\"no\">filter</code>  where useful</h3>\n<p>Arrays added the functions <a href=\"https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/forEach\"><code class=\"notranslate\" translate=\"no\">forEach</code></a>,\n<a href=\"https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/map\"><code class=\"notranslate\" translate=\"no\">map</code></a>, and\n<a href=\"https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/filter\"><code class=\"notranslate\" translate=\"no\">filter</code></a> and\nare used fairly extensively in modern JavaScript.</p>\n<h3 id=\"use-destructuring\">Use destructuring</h3>\n<p>Assume an object <code class=\"notranslate\" translate=\"no\">const dims = {width: 300, height: 150}</code></p>\n<p>old code</p>\n<pre class=\"prettyprint showlinemods notranslate lang-js\" translate=\"no\">const width = dims.width;\nconst height = dims.height;\n</pre>\n<p>new code</p>\n<pre class=\"prettyprint showlinemods notranslate lang-js\" translate=\"no\">const {width, height} = dims;\n</pre>\n<p>Destructuring works with arrays too. Assume an array <code class=\"notranslate\" translate=\"no\">const position = [5, 6, 7, 1]</code>;</p>\n<p>old code</p>\n<pre class=\"prettyprint showlinemods notranslate lang-js\" translate=\"no\">const y = position[1];\nconst z = position[2];\n</pre>\n<p>new code</p>\n<pre class=\"prettyprint showlinemods notranslate lang-js\" translate=\"no\">const [, y, z] = position;\n</pre>\n<p>Destructuring also works in function arguments</p>\n<pre class=\"prettyprint showlinemods notranslate lang-js\" translate=\"no\">const dims = {width: 300, height: 150};\nconst vector = [3, 4];\n\nfunction lengthOfVector([x, y]) {\n  return Math.sqrt(x * x + y * y);\n}\n\nconst dist = lengthOfVector(vector);  // dist = 5\n\nfunction area({width, height}) {\n  return width * height;\n}\nconst a = area(dims);  // a = 45000\n</pre>\n<h3 id=\"use-object-declaration-short-cuts\">Use object declaration short cuts</h3>\n<p>old code</p>\n<pre class=\"prettyprint showlinemods notranslate lang-js\" translate=\"no\"> const width = 300;\n const height = 150;\n const obj = {\n   width: width,\n   height: height,\n   area: function() {\n     return this.width * this.height\n   },\n };\n</pre>\n<p>new code</p>\n<pre class=\"prettyprint showlinemods notranslate lang-js\" translate=\"no\"> const width = 300;\n const height = 150;\n const obj = {\n   width,\n   height,\n   area() {\n     return this.width * this.height;\n   },\n };\n</pre>\n<h3 id=\"use-the-rest-parameter-and-the-spread-operator-\">Use the rest parameter and the spread operator <code class=\"notranslate\" translate=\"no\">...</code></h3>\n<p>The rest parameter can be used to consume any number of parameters. Example</p>\n<pre class=\"prettyprint showlinemods notranslate lang-js\" translate=\"no\"> function log(className, ...args) {\n   const elem = document.createElement('div');\n   elem.className = className;\n   elem.textContent = args.join(' ');\n   document.body.appendChild(elem);\n }\n</pre>\n<p>The spread operator can be used to expand an iterable into arguments</p>\n<pre class=\"prettyprint showlinemods notranslate lang-js\" translate=\"no\">const position = [1, 2, 3];\nsomeMesh.position.set(...position);\n</pre>\n<p>or copy an array</p>\n<pre class=\"prettyprint showlinemods notranslate lang-js\" translate=\"no\">const copiedPositionArray = [...position];\ncopiedPositionArray.push(4); // [1,2,3,4]\nconsole.log(position); // [1,2,3] position is unaffected\n</pre>\n<p>or to merge objects</p>\n<pre class=\"prettyprint showlinemods notranslate notranslate\" translate=\"no\">const a = {abc: 123};\nconst b = {def: 456};\nconst c = {...a, ...b};  // c is now {abc: 123, def: 456}\n</pre><h3 id=\"use-class-\">Use <code class=\"notranslate\" translate=\"no\">class</code></h3>\n<p>The syntax for making class like objects pre ES5 was unfamiliar to most\nprogrammers. As of ES5 you can now <a href=\"https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Classes\">use the <code class=\"notranslate\" translate=\"no\">class</code>\nkeyword</a>\nwhich is closer to the style of C++/C#/Java.</p>\n<h3 id=\"understand-getters-and-setters\">Understand getters and setters</h3>\n<p><a href=\"https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Functions/get\">Getters</a> and\n<a href=\"https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Functions/set\">setters</a> are\ncommon in most modern languages. The <code class=\"notranslate\" translate=\"no\">class</code> syntax\nof ES5 makes them much easier than pre ES5.</p>\n<h3 id=\"use-arrow-functions-where-appropriate\">Use arrow functions where appropriate</h3>\n<p>This is especially useful with callbacks and promises.</p>\n<pre class=\"prettyprint showlinemods notranslate lang-js\" translate=\"no\">loader.load((texture) =&gt; {\n  // use texture\n});\n</pre>\n<p>Arrow functions bind <code class=\"notranslate\" translate=\"no\">this</code> to the context in which you create the arrow function.</p>\n<pre class=\"prettyprint showlinemods notranslate lang-js\" translate=\"no\">const foo = (args) =&gt; {/* code */};\n</pre>\n<p>is a shortcut for</p>\n<pre class=\"prettyprint showlinemods notranslate lang-js\" translate=\"no\">const foo = (function(args) {/* code */}).bind(this));\n</pre>\n<p>See link above for more info on <code class=\"notranslate\" translate=\"no\">this</code>.</p>\n<h3 id=\"promises-as-well-as-async-await\">Promises as well as async/await</h3>\n<p>Promises help with asynchronous code. Async/await help\nuse promises.</p>\n<p>It's too big a topic to go into here but you can <a href=\"https://developer.mozilla.org/en-US/docs/Web/JavaScript/Guide/Using_promises\">read up\non promises here</a>\nand <a href=\"https://developer.mozilla.org/en-US/docs/Learn/JavaScript/Asynchronous/Async_await\">async/await here</a>.</p>\n<h3 id=\"use-template-literals\">Use Template Literals</h3>\n<p>Template literals are strings using backticks instead of quotes.</p>\n<pre class=\"prettyprint showlinemods notranslate notranslate\" translate=\"no\">const foo = `this is a template literal`;\n</pre><p>Template literals have basically 2 features. One is they can be multi-line</p>\n<pre class=\"prettyprint showlinemods notranslate lang-js\" translate=\"no\">const foo = `this\nis\na\ntemplate\nliteral`;\nconst bar = \"this\\nis\\na\\ntemplate\\nliteral\";\n</pre>\n<p><code class=\"notranslate\" translate=\"no\">foo</code> and <code class=\"notranslate\" translate=\"no\">bar</code> above are the same.</p>\n<p>The other is that you can pop out of string mode and insert snippets of\nJavaScript using <code class=\"notranslate\" translate=\"no\">${javascript-expression}</code>. This is the template part. Example:</p>\n<pre class=\"prettyprint showlinemods notranslate lang-js\" translate=\"no\">const r = 192;\nconst g = 255;\nconst b = 64;\nconst rgbCSSColor = `rgb(${r},${g},${b})`;\n</pre>\n<p>or</p>\n<pre class=\"prettyprint showlinemods notranslate lang-js\" translate=\"no\">const color = [192, 255, 64];\nconst rgbCSSColor = `rgb(${color.join(',')})`;\n</pre>\n<p>or</p>\n<pre class=\"prettyprint showlinemods notranslate lang-js\" translate=\"no\">const aWidth = 10;\nconst bWidth = 20;\nsomeElement.style.width = `${aWidth + bWidth}px`;\n</pre>\n<h1 id=\"learn-javascript-coding-conventions-\">Learn JavaScript coding conventions.</h1>\n<p>While you're welcome to format your code any way you chose there is at least one\nconvention you should be aware of. Variables, function names, method names, in\nJavaScript are all lowerCasedCamelCase. Constructors, the names of classes are\nCapitalizedCamelCase. If you follow this rule your code will match most other\nJavaScript. Many <a href=\"https://eslint.org\">linters</a>, programs that check for obvious errors in your code,\nwill point out errors if you use the wrong case since by following the convention\nabove they can know when you're using something incorrectly.</p>\n<pre class=\"prettyprint showlinemods notranslate lang-js\" translate=\"no\">const v = new vector(); // clearly an error if all classes start with a capital letter\nconst v = Vector();     // clearly an error if all functions start with a lowercase letter.\n</pre>\n<h1 id=\"consider-using-visual-studio-code\">Consider using Visual Studio Code</h1>\n<p>Of course use whatever editor you want but if you haven't tried it consider\nusing <a href=\"https://code.visualstudio.com/\">Visual Studio Code</a> for JavaScript and\nafter installing it <a href=\"https://marketplace.visualstudio.com/items?itemName=dbaeumer.vscode-eslint\">setup\neslint</a>.\nIt might take a few minutes to setup but it will help you immensely with finding\nbugs in your JavaScript.</p>\n<p>Some examples</p>\n<p>If you enable <a href=\"https://eslint.org/docs/rules/no-undef\">the <code class=\"notranslate\" translate=\"no\">no-undef</code> rule</a> then\nVSCode via ESLint will warn you of many undefined variables. </p>\n<div class=\"threejs_center\"><img style=\"width: 615px;\" src=\"../resources/images/vscode-eslint-not-defined.png\"></div>\n\n<p>Above you can see I mis-spelled <code class=\"notranslate\" translate=\"no\">doTheThing</code> as <code class=\"notranslate\" translate=\"no\">doThing</code>. There's a red squiggle\nunder <code class=\"notranslate\" translate=\"no\">doThing</code> and hovering over it it tells me it's undefined. One error\navoided.</p>\n<p>If you're using <code class=\"notranslate\" translate=\"no\">&lt;script&gt;</code> tags to include three.js you'll get warnings using <code class=\"notranslate\" translate=\"no\">THREE</code> so add <code class=\"notranslate\" translate=\"no\">/* global THREE */</code> at the top of your\nJavaScript files to tell eslint that <code class=\"notranslate\" translate=\"no\">THREE</code> exists. (or better, use <code class=\"notranslate\" translate=\"no\">import</code> 😉)</p>\n<div class=\"threejs_center\"><img style=\"width: 615px;\" src=\"../resources/images/vscode-eslint-not-a-constructor.png\"></div>\n\n<p>Above you can see eslint knows the rule that <code class=\"notranslate\" translate=\"no\">UpperCaseNames</code> are constructors\nand so you should be using <code class=\"notranslate\" translate=\"no\">new</code>. Another error caught and avoided. This is <a href=\"https://eslint.org/docs/rules/new-cap\">the\n<code class=\"notranslate\" translate=\"no\">new-cap</code> rule</a>.</p>\n<p>There are <a href=\"https://eslint.org/docs/rules/\">100s of rules you can turn on or off or\ncustomize</a>. For example above I mentioned you\nshould use <code class=\"notranslate\" translate=\"no\">const</code> and <code class=\"notranslate\" translate=\"no\">let</code> over <code class=\"notranslate\" translate=\"no\">var</code>.</p>\n<p>Here I used <code class=\"notranslate\" translate=\"no\">var</code> and it warned me I should use <code class=\"notranslate\" translate=\"no\">let</code> or <code class=\"notranslate\" translate=\"no\">const</code></p>\n<div class=\"threejs_center\"><img style=\"width: 615px;\" src=\"../resources/images/vscode-eslint-var.png\"></div>\n\n<p>Here I used <code class=\"notranslate\" translate=\"no\">let</code> but it saw I never change the value so it suggested I use <code class=\"notranslate\" translate=\"no\">const</code>.</p>\n<div class=\"threejs_center\"><img style=\"width: 615px;\" src=\"../resources/images/vscode-eslint-let.png\"></div>\n\n<p>Of course if you'd prefer to keep using <code class=\"notranslate\" translate=\"no\">var</code> you can just turn off that rule.\nAs I said above though I prefer to use <code class=\"notranslate\" translate=\"no\">const</code> and <code class=\"notranslate\" translate=\"no\">let</code> over <code class=\"notranslate\" translate=\"no\">var</code> as they just\nwork better and prevent bugs.</p>\n<p>For those cases where you really need to override a rule <a href=\"https://eslint.org/docs/user-guide/configuring#disabling-rules-with-inline-comments\">you can add comments\nto disable\nthem</a>\nfor a single line or a section of code.</p>\n<h1 id=\"if-you-really-need-to-support-legacy-browsers-use-a-transpiler\">If you really need to support legacy browsers use a transpiler</h1>\n<p>Most modern browsers are auto-updated so using all these features will help you\nbe productive and avoid bugs. That said, if you're on a project that absolutely\nmust support old browsers there are <a href=\"https://babeljs.io\">tools that will take your ES5/ES6/ES7 code\nand transpile the code back to pre ES5 Javascript</a>.</p>\n\n        </div>\n      </div>\n    </div>",
    "success": true,
    "error": "",
    "crawled_at": 1786376809.300204,
    "is_shell": false
  },
  {
    "url": "https://threejs.org/manual/en/setup.html",
    "title": "Setup",
    "content": "<div class=\"container\">\n      <div class=\"lesson-title\">\n        <h1>Setup</h1>\n      </div>\n      <div class=\"lesson\">\n        <div class=\"lesson-main\">\n          <p>This article is one in a series of articles about three.js.\nThe first article was <a href=\"fundamentals.html\">about three.js fundamentals</a>.\nIf you haven't read that yet you might want to start there.</p>\n<p>Before we go any further we need to talk about setting up your\ncomputer as a development environment. In particular, for security reasons,\nWebGL cannot use images from your hard drive directly. That means\nin order to do development you need to use a web server. Fortunately\ndevelopment web servers are super easy to setup and use.</p>\n<p>First off if you'd like you can download this entire site from <a href=\"https://github.com/gfxfundamentals/threejsfundamentals/archive/gh-pages.zip\">this link</a>.\nOnce downloaded double click the zip file to unpack the files.</p>\n<p>Next download one of these simple web servers.</p>\n<p>If you'd prefer a web server with a user interface there's\n<a href=\"https://greggman.github.io/servez\" target=\"_blank\">Servez</a>.</p>\n<p></p><div class=\"threejs_image border\">\n  <img class=\"\" src=\"../resources/servez.gif\">\n</div>\n\n<p></p>\n<p>Just point it at the folder where you unzipped the files, click \"Start\", then go to\nin your browser <a href=\"http://localhost:8080/\"><code class=\"notranslate\" translate=\"no\">http://localhost:8080/</code></a> or if you'd\nlike to browse the samples go to <a href=\"http://localhost:8080/threejs\"><code class=\"notranslate\" translate=\"no\">http://localhost:8080/threejs</code></a>.</p>\n<p>To stop serving click stop or quit Servez.</p>\n<p>If you prefer the command line (I do), another way is to use <a href=\"https://nodejs.org\" target=\"_blank\">node.js</a>.\nDownload it, install it, then open a command prompt / console / terminal window. If you're on Windows the installer will add a special \"Node Command Prompt\" so use that.</p>\n<p>Then install the <a href=\"https://github.com/greggman/servez-cli\"><code class=\"notranslate\" translate=\"no\">servez</code></a> by typing</p>\n<pre class=\"prettyprint showlinemods notranslate notranslate\" translate=\"no\">npm -g install servez\n</pre><p>If you're on OSX use</p>\n<pre class=\"prettyprint showlinemods notranslate notranslate\" translate=\"no\">sudo npm -g install servez\n</pre><p>Once you've done that type</p>\n<pre class=\"prettyprint showlinemods notranslate notranslate\" translate=\"no\">servez path/to/folder/where/you/unzipped/files\n</pre><p>Or if you're like me</p>\n<pre class=\"prettyprint showlinemods notranslate notranslate\" translate=\"no\">cd path/to/folder/where/you/unzipped/files\nservez\n</pre><p>It should print something like</p>\n<p></p><div class=\"threejs_image \">\n  <img class=\"\" src=\"../resources/servez-response.png\">\n</div>\n\n<p></p>\n<p>Then in your browser go to <a href=\"http://localhost:8080/\"><code class=\"notranslate\" translate=\"no\">http://localhost:8080/</code></a>.</p>\n<p>If you don't specify a path then servez will serve the current folder.</p>\n<p>If either of those options are not to your liking\n<a href=\"https://stackoverflow.com/questions/12905426/what-is-a-faster-alternative-to-pythons-servez-or-simplehttpserver\" target=\"_blank\">there are many other simple servers to choose from</a>.</p>\n<p>Now that you have a server setup we can move on to <a href=\"textures.html\">textures</a>.</p>\n\n        </div>\n      </div>\n    </div>",
    "success": true,
    "error": "",
    "crawled_at": 1786376809.300204,
    "is_shell": false
  },
  {
    "url": "https://threejs.org/manual/en/primitives.html",
    "title": "Primitives",
    "content": "<div class=\"container\">\n      <div class=\"lesson-title\">\n        <h1>Primitives</h1>\n      </div>\n      <div class=\"lesson\">\n        <div class=\"lesson-main\">\n          <p>This article is one in a series of articles about three.js.\nThe first article was <a href=\"fundamentals.html\">about fundamentals</a>.\nIf you haven't read that yet you might want to start there.</p>\n<p>Three.js has a large number of primitives. Primitives\nare generally 3D shapes that are generated at runtime\nwith a bunch of parameters.</p>\n<p>It's common to use primitives for things like a sphere\nfor a globe or a bunch of boxes to draw a 3D graph. It's\nespecially common to use primitives to experiment\nand get started with 3D. For the majority of 3D apps\nit's more common to have an artist make 3D models\nin a 3D modeling program like <a href=\"https://blender.org\">Blender</a>\nor <a href=\"https://www.autodesk.com/products/maya/\">Maya</a> or <a href=\"https://www.maxon.net/en-us/products/cinema-4d/\">Cinema 4D</a>. Later in this series we'll\ncover making and loading data from several 3D modeling\nprograms. For now let's go over some of the available\nprimitives.</p>\n<p>Many of the primitives below have defaults for some or all of their\nparameters so you can use more or less depending on your needs.</p>\n<div id=\"Diagram-BoxGeometry\" data-primitive=\"BoxGeometry\"><div class=\"pair\"><div class=\"shape\"></div><div class=\"desc\"><a href=\"#Diagram-BoxGeometry\" class=\"deep-link\">#</a><a target=\"_blank\" href=\"https://threejs.org/docs/#api/en/geometries/BoxGeometry\"><code>BoxGeometry</code></a><div class=\".note\">A Box</div></div></div><pre class=\"prettyprint showmods\">\nconst width = 8;  // ui: width\nconst height = 8;  // ui: height\nconst depth = 8;  // ui: depth\nconst geometry = new THREE.BoxGeometry( width, height, depth );\n</pre><div class=\"shape\"></div><pre class=\"prettyprint showmods\">\nconst width = 8;  // ui: width\nconst height = 8;  // ui: height\nconst depth = 8;  // ui: depth\nconst widthSegments = 4;  // ui: widthSegments\nconst heightSegments = 4;  // ui: heightSegments\nconst depthSegments = 4;  // ui: depthSegments\nconst geometry = new THREE.BoxGeometry(\n\twidth, height, depth,\n\twidthSegments, heightSegments, depthSegments );\n</pre></div>\n<div id=\"Diagram-CircleGeometry\" data-primitive=\"CircleGeometry\"><div class=\"pair\"><div class=\"shape\"></div><div class=\"desc\"><a href=\"#Diagram-CircleGeometry\" class=\"deep-link\">#</a><a target=\"_blank\" href=\"https://threejs.org/docs/#api/en/geometries/CircleGeometry\"><code>CircleGeometry</code></a><div class=\".note\">A flat circle</div></div></div><pre class=\"prettyprint showmods\">\nconst radius = 7;  // ui: radius\nconst segments = 24;  // ui: segments\nconst geometry = new THREE.CircleGeometry( radius, segments );\n</pre><div class=\"shape\"></div><pre class=\"prettyprint showmods\">\nconst radius = 7;  // ui: radius\nconst segments = 24;  // ui: segments\nconst thetaStart = Math.PI * 0.25;  // ui: thetaStart\nconst thetaLength = Math.PI * 1.5;  // ui: thetaLength\nconst geometry = new THREE.CircleGeometry(\n\tradius, segments, thetaStart, thetaLength );\n</pre></div>\n<div id=\"Diagram-ConeGeometry\" data-primitive=\"ConeGeometry\"><div class=\"pair\"><div class=\"shape\"></div><div class=\"desc\"><a href=\"#Diagram-ConeGeometry\" class=\"deep-link\">#</a><a target=\"_blank\" href=\"https://threejs.org/docs/#api/en/geometries/ConeGeometry\"><code>ConeGeometry</code></a><div class=\".note\">A Cone</div></div></div><pre class=\"prettyprint showmods\">\nconst radius = 6;  // ui: radius\nconst height = 8;  // ui: height\nconst radialSegments = 16;  // ui: radialSegments\nconst geometry = new THREE.ConeGeometry( radius, height, radialSegments );\n</pre><div class=\"shape\"></div><pre class=\"prettyprint showmods\">\nconst radius = 6;  // ui: radius\nconst height = 8;  // ui: height\nconst radialSegments = 16;  // ui: radialSegments\nconst heightSegments = 2;  // ui: heightSegments\nconst openEnded = true;  // ui: openEnded\nconst thetaStart = Math.PI * 0.25;  // ui: thetaStart\nconst thetaLength = Math.PI * 1.5;  // ui: thetaLength\nconst geometry = new THREE.ConeGeometry(\n\tradius, height,\n\tradialSegments, heightSegments,\n\topenEnded,\n\tthetaStart, thetaLength );\n</pre></div>\n<div id=\"Diagram-CylinderGeometry\" data-primitive=\"CylinderGeometry\"><div class=\"pair\"><div class=\"shape\"></div><div class=\"desc\"><a href=\"#Diagram-CylinderGeometry\" class=\"deep-link\">#</a><a target=\"_blank\" href=\"https://threejs.org/docs/#api/en/geometries/CylinderGeometry\"><code>CylinderGeometry</code></a><div class=\".note\">A Cylinder</div></div></div><pre class=\"prettyprint showmods\">\nconst radiusTop = 4;  // ui: radiusTop\nconst radiusBottom = 4;  // ui: radiusBottom\nconst height = 8;  // ui: height\nconst radialSegments = 12;  // ui: radialSegments\nconst geometry = new THREE.CylinderGeometry(\n\tradiusTop, radiusBottom, height, radialSegments );\n</pre><div class=\"shape\"></div><pre class=\"prettyprint showmods\">\nconst radiusTop = 4;  // ui: radiusTop\nconst radiusBottom = 4;  // ui: radiusBottom\nconst height = 8;  // ui: height\nconst radialSegments = 12;  // ui: radialSegments\nconst heightSegments = 2;  // ui: heightSegments\nconst openEnded = false;  // ui: openEnded\nconst thetaStart = Math.PI * 0.25;  // ui: thetaStart\nconst thetaLength = Math.PI * 1.5;  // ui: thetaLength\nconst geometry = new THREE.CylinderGeometry(\n\tradiusTop, radiusBottom, height,\n\tradialSegments, heightSegments,\n\topenEnded,\n\tthetaStart, thetaLength );\n</pre></div>\n<div id=\"Diagram-DodecahedronGeometry\" data-primitive=\"DodecahedronGeometry\"><div class=\"pair\"><div class=\"shape\"></div><div class=\"desc\"><a href=\"#Diagram-DodecahedronGeometry\" class=\"deep-link\">#</a><a target=\"_blank\" href=\"https://threejs.org/docs/#api/en/geometries/DodecahedronGeometry\"><code>DodecahedronGeometry</code></a><div class=\".note\">A dodecahedron (12 sides)</div></div></div><pre class=\"prettyprint showmods\">\nconst radius = 7;  // ui: radius\nconst geometry = new THREE.DodecahedronGeometry( radius );\n</pre><div class=\"shape\"></div><pre class=\"prettyprint showmods\">\nconst radius = 7;  // ui: radius\nconst detail = 2;  // ui: detail\nconst geometry = new THREE.DodecahedronGeometry( radius, detail );\n</pre></div>\n<div id=\"Diagram-ExtrudeGeometry\" data-primitive=\"ExtrudeGeometry\"><div class=\"pair\"><div class=\"shape\"></div><div class=\"desc\"><a href=\"#Diagram-ExtrudeGeometry\" class=\"deep-link\">#</a><a target=\"_blank\" href=\"https://threejs.org/docs/#api/en/geometries/ExtrudeGeometry\"><code>ExtrudeGeometry</code></a><div class=\".note\">An extruded 2d shape with optional bevelling.\nHere we are extruding a heart shape. Note this is the basis\nfor <a href=\"/docs/#examples/en/geometries/TextGeometry\"><code class=\"notranslate\" translate=\"no\">TextGeometry</code></a>.</div></div></div><pre class=\"prettyprint showmods\">const shape = new THREE.Shape();\nconst x = -2.5;\nconst y = -5;\nshape.moveTo(x + 2.5, y + 2.5);\nshape.bezierCurveTo(x + 2.5, y + 2.5, x + 2, y, x, y);\nshape.bezierCurveTo(x - 3, y, x - 3, y + 3.5, x - 3, y + 3.5);\nshape.bezierCurveTo(x - 3, y + 5.5, x - 1.5, y + 7.7, x + 2.5, y + 9.5);\nshape.bezierCurveTo(x + 6, y + 7.7, x + 8, y + 4.5, x + 8, y + 3.5);\nshape.bezierCurveTo(x + 8, y + 3.5, x + 8, y, x + 5, y);\nshape.bezierCurveTo(x + 3.5, y, x + 2.5, y + 2.5, x + 2.5, y + 2.5);\n\nconst extrudeSettings = {\n  steps: 2,  // ui: steps\n  depth: 2,  // ui: depth\n  bevelEnabled: true,  // ui: bevelEnabled\n  bevelThickness: 1,  // ui: bevelThickness\n  bevelSize: 1,  // ui: bevelSize\n  bevelSegments: 2,  // ui: bevelSegments\n};\n\nconst geometry = THREE.ExtrudeGeometry(shape, extrudeSettings);</pre><div class=\"shape\"></div><pre class=\"prettyprint showmods\">const outline = new THREE.Shape([\n  [ -2, -0.1], [  2, -0.1], [ 2,  0.6],\n  [1.6,  0.6], [1.6,  0.1], [-2,  0.1],\n].map(p =&gt; new THREE.Vector2(...p)));\n\nconst x = -2.5;\nconst y = -5;\nconst shape = new THREE.CurvePath();\nconst points = [\n  [x + 2.5, y + 2.5],\n  [x + 2.5, y + 2.5], [x + 2,   y      ], [x,       y      ],\n  [x - 3,   y      ], [x - 3,   y + 3.5], [x - 3,   y + 3.5],\n  [x - 3,   y + 5.5], [x - 1.5, y + 7.7], [x + 2.5, y + 9.5],\n  [x + 6,   y + 7.7], [x + 8,   y + 4.5], [x + 8,   y + 3.5],\n  [x + 8,   y + 3.5], [x + 8,   y      ], [x + 5,   y      ],\n  [x + 3.5, y      ], [x + 2.5, y + 2.5], [x + 2.5, y + 2.5],\n].map(p =&gt; new THREE.Vector3(...p, 0));\nfor (let i = 0; i &lt; points.length; i += 3) {\n  shape.add(new THREE.CubicBezierCurve3(...points.slice(i, i + 4)));\n}\n\nconst extrudeSettings = {\n  steps: 100,  // ui: steps\n  bevelEnabled: false,\n  extrudePath: shape,\n};\n\nconst geometry =  new THREE.ExtrudeGeometry(outline, extrudeSettings);\nreturn geometry;</pre></div>\n<div id=\"Diagram-IcosahedronGeometry\" data-primitive=\"IcosahedronGeometry\"><div class=\"pair\"><div class=\"shape\"></div><div class=\"desc\"><a href=\"#Diagram-IcosahedronGeometry\" class=\"deep-link\">#</a><a target=\"_blank\" href=\"https://threejs.org/docs/#api/en/geometries/IcosahedronGeometry\"><code>IcosahedronGeometry</code></a><div class=\".note\">An icosahedron (20 sides)</div></div></div><pre class=\"prettyprint showmods\">\nconst radius = 7;  // ui: radius\nconst geometry = new THREE.IcosahedronGeometry( radius );\n</pre><div class=\"shape\"></div><pre class=\"prettyprint showmods\">\nconst radius = 7;  // ui: radius\nconst detail = 2;  // ui: detail\nconst geometry = new THREE.IcosahedronGeometry( radius, detail );\n</pre></div>\n<div id=\"Diagram-LatheGeometry\" data-primitive=\"LatheGeometry\"><div class=\"pair\"><div class=\"shape\"></div><div class=\"desc\"><a href=\"#Diagram-LatheGeometry\" class=\"deep-link\">#</a><a target=\"_blank\" href=\"https://threejs.org/docs/#api/en/geometries/LatheGeometry\"><code>LatheGeometry</code></a><div class=\".note\">A shape generated by spinning a line. Examples would be: lamps, bowling pins, candles, candle holders, wine glasses, drinking glasses, etc... You provide the 2d silhouette as series of points and then tell three.js how many subdivisions to make as it spins the silhouette around an axis.</div></div></div><pre class=\"prettyprint showmods\">\nconst points = [];\nfor ( let i = 0; i &lt; 10; ++ i ) {\n\n\tpoints.push( new THREE.Vector2( Math.sin( i * 0.2 ) * 3 + 3, ( i - 5 ) * .8 ) );\n\n}\n\nconst geometry = new THREE.LatheGeometry( points );\n</pre><div class=\"shape\"></div><pre class=\"prettyprint showmods\">\nconst points = [];\nfor ( let i = 0; i &lt; 10; ++ i ) {\n\n\tpoints.push( new THREE.Vector2( Math.sin( i * 0.2 ) * 3 + 3, ( i - 5 ) * .8 ) );\n\n}\n\nconst segments = 12;  // ui: segments\nconst phiStart = Math.PI * 0.25;  // ui: phiStart\nconst phiLength = Math.PI * 1.5;  // ui: phiLength\nconst geometry = new THREE.LatheGeometry(\n\tpoints, segments, phiStart, phiLength );\n</pre></div>\n<div id=\"Diagram-OctahedronGeometry\" data-primitive=\"OctahedronGeometry\"><div class=\"pair\"><div class=\"shape\"></div><div class=\"desc\"><a href=\"#Diagram-OctahedronGeometry\" class=\"deep-link\">#</a><a target=\"_blank\" href=\"https://threejs.org/docs/#api/en/geometries/OctahedronGeometry\"><code>OctahedronGeometry</code></a><div class=\".note\">An Octahedron (8 sides)</div></div></div><pre class=\"prettyprint showmods\">\nconst radius = 7;  // ui: radius\nconst geometry = new THREE.OctahedronGeometry( radius );\n</pre><div class=\"shape\"></div><pre class=\"prettyprint showmods\">\nconst radius = 7;  // ui: radius\nconst detail = 2;  // ui: detail\nconst geometry = new THREE.OctahedronGeometry( radius, detail );\n</pre></div>\n<div id=\"Diagram-ParametricGeometry\" data-primitive=\"ParametricGeometry\"><div class=\"pair\"><div class=\"shape\"></div><div class=\"desc\"><a href=\"#Diagram-ParametricGeometry\" class=\"deep-link\">#</a><a target=\"_blank\" href=\"https://threejs.org/docs/#api/en/geometries/ParametricGeometry\"><code>ParametricGeometry</code></a><div class=\".note\">A surface generated by providing a function that takes a 2D point from a grid and returns the corresponding 3d point.</div></div></div><pre class=\"prettyprint showmods\">const slices = 25;  // ui: slices\nconst stacks = 25;  // ui: stacks\n\n// from: https://github.com/mrdoob/three.js/blob/b8d8a8625465bd634aa68e5846354d69f34d2ff5/examples/js/ParametricGeometries.js\nfunction klein( v, u, target ) {\n\n\tu *= Math.PI;\n\tv *= 2 * Math.PI;\n\tu = u * 2;\n\n\tlet x;\n\tlet z;\n\n\tif ( u &lt; Math.PI ) {\n\n\t\tx = 3 * Math.cos( u ) * ( 1 + Math.sin( u ) ) + ( 2 * ( 1 - Math.cos( u ) / 2 ) ) * Math.cos( u ) * Math.cos( v );\n\t\tz = - 8 * Math.sin( u ) - 2 * ( 1 - Math.cos( u ) / 2 ) * Math.sin( u ) * Math.cos( v );\n\n\t} else {\n\n\t\tx = 3 * Math.cos( u ) * ( 1 + Math.sin( u ) ) + ( 2 * ( 1 - Math.cos( u ) / 2 ) ) * Math.cos( v + Math.PI );\n\t\tz = - 8 * Math.sin( u );\n\n\t}\n\n\tconst y = - 2 * ( 1 - Math.cos( u ) / 2 ) * Math.sin( v );\n\n\ttarget.set( x, y, z ).multiplyScalar( 0.75 );\n\n}\n\nreturn new ParametricGeometry(\n\tklein, slices, stacks );\n</pre></div>\n<div id=\"Diagram-PlaneGeometry\" data-primitive=\"PlaneGeometry\"><div class=\"pair\"><div class=\"shape\"></div><div class=\"desc\"><a href=\"#Diagram-PlaneGeometry\" class=\"deep-link\">#</a><a target=\"_blank\" href=\"https://threejs.org/docs/#api/en/geometries/PlaneGeometry\"><code>PlaneGeometry</code></a><div class=\".note\">A 2D plane</div></div></div><pre class=\"prettyprint showmods\">\nconst width = 9;  // ui: width\nconst height = 9;  // ui: height\nconst geometry = new THREE.PlaneGeometry( width, height );\n</pre><div class=\"shape\"></div><pre class=\"prettyprint showmods\">\nconst width = 9;  // ui: width\nconst height = 9;  // ui: height\nconst widthSegments = 2;  // ui: widthSegments\nconst heightSegments = 2;  // ui: heightSegments\nconst geometry = new THREE.PlaneGeometry(\n\twidth, height,\n\twidthSegments, heightSegments );\n</pre></div>\n<div id=\"Diagram-PolyhedronGeometry\" data-primitive=\"PolyhedronGeometry\"><div class=\"pair\"><div class=\"shape\"></div><div class=\"desc\"><a href=\"#Diagram-PolyhedronGeometry\" class=\"deep-link\">#</a><a target=\"_blank\" href=\"https://threejs.org/docs/#api/en/geometries/PolyhedronGeometry\"><code>PolyhedronGeometry</code></a><div class=\".note\">Takes a set of triangles centered around a point and projects them onto a sphere</div></div></div><pre class=\"prettyprint showmods\">\nconst verticesOfCube = [\n\t- 1, - 1, - 1, 1, - 1, - 1, 1, 1, - 1, - 1, 1, - 1,\n\t- 1, - 1, 1, 1, - 1, 1, 1, 1, 1, - 1, 1, 1,\n];\nconst indicesOfFaces = [\n\t2, 1, 0, 0, 3, 2,\n\t0, 4, 7, 7, 3, 0,\n\t0, 1, 5, 5, 4, 0,\n\t1, 2, 6, 6, 5, 1,\n\t2, 3, 7, 7, 6, 2,\n\t4, 5, 6, 6, 7, 4,\n];\nconst radius = 7;  // ui: radius\nconst detail = 2;  // ui: detail\nconst geometry = new THREE.PolyhedronGeometry(\n\tverticesOfCube, indicesOfFaces, radius, detail );\n</pre></div>\n<div id=\"Diagram-RingGeometry\" data-primitive=\"RingGeometry\"><div class=\"pair\"><div class=\"shape\"></div><div class=\"desc\"><a href=\"#Diagram-RingGeometry\" class=\"deep-link\">#</a><a target=\"_blank\" href=\"https://threejs.org/docs/#api/en/geometries/RingGeometry\"><code>RingGeometry</code></a><div class=\".note\">A 2D disc with a hole in the center</div></div></div><pre class=\"prettyprint showmods\">\nconst innerRadius = 2;  // ui: innerRadius\nconst outerRadius = 7;  // ui: outerRadius\nconst thetaSegments = 18;  // ui: thetaSegments\nconst geometry = new THREE.RingGeometry(\n\tinnerRadius, outerRadius, thetaSegments );\n</pre><div class=\"shape\"></div><pre class=\"prettyprint showmods\">\nconst innerRadius = 2;  // ui: innerRadius\nconst outerRadius = 7;  // ui: outerRadius\nconst thetaSegments = 18;  // ui: thetaSegments\nconst phiSegments = 2;  // ui: phiSegments\nconst thetaStart = Math.PI * 0.25;  // ui: thetaStart\nconst thetaLength = Math.PI * 1.5;  // ui: thetaLength\nconst geometry = new THREE.RingGeometry(\n\tinnerRadius, outerRadius,\n\tthetaSegments, phiSegments,\n\tthetaStart, thetaLength );\n</pre></div>\n<div id=\"Diagram-ShapeGeometry\" data-primitive=\"ShapeGeometry\"><div class=\"pair\"><div class=\"shape\"></div><div class=\"desc\"><a href=\"#Diagram-ShapeGeometry\" class=\"deep-link\">#</a><a target=\"_blank\" href=\"https://threejs.org/docs/#api/en/geometries/ShapeGeometry\"><code>ShapeGeometry</code></a><div class=\".note\">A 2D outline that gets triangulated</div></div></div><pre class=\"prettyprint showmods\">\nconst shape = new THREE.Shape();\nconst x = - 2.5;\nconst y = - 5;\nshape.moveTo( x + 2.5, y + 2.5 );\nshape.bezierCurveTo( x + 2.5, y + 2.5, x + 2, y, x, y );\nshape.bezierCurveTo( x - 3, y, x - 3, y + 3.5, x - 3, y + 3.5 );\nshape.bezierCurveTo( x - 3, y + 5.5, x - 1.5, y + 7.7, x + 2.5, y + 9.5 );\nshape.bezierCurveTo( x + 6, y + 7.7, x + 8, y + 4.5, x + 8, y + 3.5 );\nshape.bezierCurveTo( x + 8, y + 3.5, x + 8, y, x + 5, y );\nshape.bezierCurveTo( x + 3.5, y, x + 2.5, y + 2.5, x + 2.5, y + 2.5 );\nconst geometry = new THREE.ShapeGeometry( shape );\n</pre><div class=\"shape\"></div><pre class=\"prettyprint showmods\">\nconst shape = new THREE.Shape();\nconst x = - 2.5;\nconst y = - 5;\nshape.moveTo( x + 2.5, y + 2.5 );\nshape.bezierCurveTo( x + 2.5, y + 2.5, x + 2, y, x, y );\nshape.bezierCurveTo( x - 3, y, x - 3, y + 3.5, x - 3, y + 3.5 );\nshape.bezierCurveTo( x - 3, y + 5.5, x - 1.5, y + 7.7, x + 2.5, y + 9.5 );\nshape.bezierCurveTo( x + 6, y + 7.7, x + 8, y + 4.5, x + 8, y + 3.5 );\nshape.bezierCurveTo( x + 8, y + 3.5, x + 8, y, x + 5, y );\nshape.bezierCurveTo( x + 3.5, y, x + 2.5, y + 2.5, x + 2.5, y + 2.5 );\nconst curveSegments = 5;  // ui: curveSegments\nconst geometry = new THREE.ShapeGeometry( shape, curveSegments );\n</pre></div>\n<div id=\"Diagram-SphereGeometry\" data-primitive=\"SphereGeometry\"><div class=\"pair\"><div class=\"shape\"></div><div class=\"desc\"><a href=\"#Diagram-SphereGeometry\" class=\"deep-link\">#</a><a target=\"_blank\" href=\"https://threejs.org/docs/#api/en/geometries/SphereGeometry\"><code>SphereGeometry</code></a><div class=\".note\">A sphere</div></div></div><pre class=\"prettyprint showmods\">\nconst radius = 7;  // ui: radius\nconst widthSegments = 12;  // ui: widthSegments\nconst heightSegments = 8;  // ui: heightSegments\nconst geometry = new THREE.SphereGeometry( radius, widthSegments, heightSegments );\n</pre><div class=\"shape\"></div><pre class=\"prettyprint showmods\">\nconst radius = 7;  // ui: radius\nconst widthSegments = 12;  // ui: widthSegments\nconst heightSegments = 8;  // ui: heightSegments\nconst phiStart = Math.PI * 0.25;  // ui: phiStart\nconst phiLength = Math.PI * 1.5;  // ui: phiLength\nconst thetaStart = Math.PI * 0.25;  // ui: thetaStart\nconst thetaLength = Math.PI * 0.5;  // ui: thetaLength\nconst geometry = new THREE.SphereGeometry(\n\tradius,\n\twidthSegments, heightSegments,\n\tphiStart, phiLength,\n\tthetaStart, thetaLength );\n</pre></div>\n<div id=\"Diagram-TetrahedronGeometry\" data-primitive=\"TetrahedronGeometry\"><div class=\"pair\"><div class=\"shape\"></div><div class=\"desc\"><a href=\"#Diagram-TetrahedronGeometry\" class=\"deep-link\">#</a><a target=\"_blank\" href=\"https://threejs.org/docs/#api/en/geometries/TetrahedronGeometry\"><code>TetrahedronGeometry</code></a><div class=\".note\">A tetrahedron (4 sides)</div></div></div><pre class=\"prettyprint showmods\">\nconst radius = 7;  // ui: radius\nconst geometry = new THREE.TetrahedronGeometry( radius );\n</pre><div class=\"shape\"></div><pre class=\"prettyprint showmods\">\nconst radius = 7;  // ui: radius\nconst detail = 2;  // ui: detail\nconst geometry = new THREE.TetrahedronGeometry( radius, detail );\n</pre></div>\n<div id=\"Diagram-TextGeometry\" data-primitive=\"TextGeometry\"><div class=\"pair\"><div class=\"shape\"></div><div class=\"desc\"><a href=\"#Diagram-TextGeometry\" class=\"deep-link\">#</a><a target=\"_blank\" href=\"https://threejs.org/docs/#api/en/geometries/TextGeometry\"><code>TextGeometry</code></a><div class=\".note\">3D text generated from a 3D font and a string</div></div></div><pre class=\"prettyprint showmods\">const loader = new THREE.FontLoader();\n\nloader.load('../resources/threejs/fonts/helvetiker_regular.typeface.json', (font) =&gt; {\n  const text = 'three.js';  // ui: text\n  const geometry = new THREE.TextGeometry(text, {\n    font: font,\n    size: 3,  // ui: size\n    depth: 0.2,  // ui: depth\n    curveSegments: 12,  // ui: curveSegments\n    bevelEnabled: true,  // ui: bevelEnabled\n    bevelThickness: 0.15,  // ui: bevelThickness\n    bevelSize: 0.3,  // ui: bevelSize\n    bevelSegments: 5,  // ui: bevelSegments\n  });\n  ...\n});</pre></div>\n<div id=\"Diagram-TorusGeometry\" data-primitive=\"TorusGeometry\"><div class=\"pair\"><div class=\"shape\"></div><div class=\"desc\"><a href=\"#Diagram-TorusGeometry\" class=\"deep-link\">#</a><a target=\"_blank\" href=\"https://threejs.org/docs/#api/en/geometries/TorusGeometry\"><code>TorusGeometry</code></a><div class=\".note\">A torus (donut)</div></div></div><pre class=\"prettyprint showmods\">\nconst radius = 5;  // ui: radius\nconst tubeRadius = 2;  // ui: tubeRadius\nconst radialSegments = 8;  // ui: radialSegments\nconst tubularSegments = 24;  // ui: tubularSegments\nconst geometry = new THREE.TorusGeometry(\n\tradius, tubeRadius,\n\tradialSegments, tubularSegments );\n</pre></div>\n<div id=\"Diagram-TorusKnotGeometry\" data-primitive=\"TorusKnotGeometry\"><div class=\"pair\"><div class=\"shape\"></div><div class=\"desc\"><a href=\"#Diagram-TorusKnotGeometry\" class=\"deep-link\">#</a><a target=\"_blank\" href=\"https://threejs.org/docs/#api/en/geometries/TorusKnotGeometry\"><code>TorusKnotGeometry</code></a><div class=\".note\">A torus knot</div></div></div><pre class=\"prettyprint showmods\">\nconst radius = 3.5;  // ui: radius\nconst tubeRadius = 1.5;  // ui: tubeRadius\nconst radialSegments = 8;  // ui: radialSegments\nconst tubularSegments = 64;  // ui: tubularSegments\nconst p = 2;  // ui: p\nconst q = 3;  // ui: q\nconst geometry = new THREE.TorusKnotGeometry(\n\tradius, tubeRadius, tubularSegments, radialSegments, p, q );\n</pre></div>\n<div id=\"Diagram-TubeGeometry\" data-primitive=\"TubeGeometry\"><div class=\"pair\"><div class=\"shape\"></div><div class=\"desc\"><a href=\"#Diagram-TubeGeometry\" class=\"deep-link\">#</a><a target=\"_blank\" href=\"https://threejs.org/docs/#api/en/geometries/TubeGeometry\"><code>TubeGeometry</code></a><div class=\".note\">A circle traced down a path</div></div></div><pre class=\"prettyprint showmods\">\nclass CustomSinCurve extends THREE.Curve {\n\n\tconstructor( scale ) {\n\n\t\tsuper();\n\t\tthis.scale = scale;\n\n\t}\n\tgetPoint( t ) {\n\n\t\tconst tx = t * 3 - 1.5;\n\t\tconst ty = Math.sin( 2 * Math.PI * t );\n\t\tconst tz = 0;\n\t\treturn new THREE.Vector3( tx, ty, tz ).multiplyScalar( this.scale );\n\n\t}\n\n}\n\nconst path = new CustomSinCurve( 4 );\nconst tubularSegments = 20;  // ui: tubularSegments\nconst radius = 1;  // ui: radius\nconst radialSegments = 8;  // ui: radialSegments\nconst closed = false;  // ui: closed\nconst geometry = new THREE.TubeGeometry(\n\tpath, tubularSegments, radius, radialSegments, closed );\n</pre></div>\n<div id=\"Diagram-EdgesGeometry\" data-primitive=\"EdgesGeometry\"><div class=\"pair\"><div class=\"shape\"></div><div class=\"desc\"><a href=\"#Diagram-EdgesGeometry\" class=\"deep-link\">#</a><a target=\"_blank\" href=\"https://threejs.org/docs/#api/en/geometries/EdgesGeometry\"><code>EdgesGeometry</code></a><div class=\".note\">A helper object that takes another geometry as input and generates edges only if the angle between faces is greater than some threshold. For example if you look at the box at the top it shows a line going through each face showing every triangle that makes the box. Using an <a href=\"/docs/#api/en/geometries/EdgesGeometry\"><code class=\"notranslate\" translate=\"no\">EdgesGeometry</code></a> instead the middle lines are removed. Adjust the thresholdAngle below and you'll see the edges below that threshold disappear.</div></div></div><pre class=\"prettyprint showmods\">const size = 8;\nconst widthSegments = 2;\nconst heightSegments = 2;\nconst depthSegments = 2;\nconst boxGeometry = new THREE.BoxGeometry(\n    size, size, size,\n    widthSegments, heightSegments, depthSegments);\nconst geometry = new THREE.EdgesGeometry(boxGeometry);</pre><div class=\"shape\"></div><pre class=\"prettyprint showmods\">const radius = 7;\nconst widthSegments = 6;\nconst heightSegments = 3;\nconst sphereGeometry = new THREE.SphereGeometry(\n    radius, widthSegments, heightSegments);\nconst thresholdAngle = 1;  // ui: thresholdAngle\nconst geometry = new THREE.EdgesGeometry(sphereGeometry, thresholdAngle);</pre></div>\n<div id=\"Diagram-WireframeGeometry\" data-primitive=\"WireframeGeometry\"><div class=\"pair\"><div class=\"shape\"></div><div class=\"desc\"><a href=\"#Diagram-WireframeGeometry\" class=\"deep-link\">#</a><a target=\"_blank\" href=\"https://threejs.org/docs/#api/en/geometries/WireframeGeometry\"><code>WireframeGeometry</code></a><div class=\".note\">Generates geometry that contains one line segment (2 points) per edge in the given geometry. Without this you'd often be missing edges or get extra edges since WebGL generally requires 2 points per line segment. For example if all you had was a single triangle there would only be 3 points. If you tried to draw it using a material with <code class=\"notranslate\" translate=\"no\">wireframe: true</code> you would only get a single line. Passing that triangle geometry to a <a href=\"/docs/#api/en/geometries/WireframeGeometry\"><code class=\"notranslate\" translate=\"no\">WireframeGeometry</code></a> will generate a new geometry that has 3 lines segments using 6 points..</div></div></div><pre class=\"prettyprint showmods\">const size = 8;\nconst widthSegments = 2;  // ui: widthSegments\nconst heightSegments = 2;  // ui: heightSegments\nconst depthSegments = 2;  // ui: depthSegments\nconst geometry = new THREE.WireframeGeometry(\n    new THREE.BoxGeometry(\n      size, size, size,\n      widthSegments, heightSegments, depthSegments));</pre></div>\n\n<p>We'll go over creating custom geometry in <a href=\"custom-buffergeometry.html\">another article</a>. For now\nlet's make an example creating each type of primitive. We'll start\nwith the <a href=\"responsive.html\">examples from the previous article</a>.</p>\n<p>Near the top let's set a background color</p>\n<pre class=\"prettyprint showlinemods notranslate lang-js\" translate=\"no\">const scene = new THREE.Scene();\n+scene.background = new THREE.Color(0xAAAAAA);\n</pre>\n<p>This tells three.js to clear to lightish gray.</p>\n<p>The camera needs to change position so that we can see all the\nobjects.</p>\n<pre class=\"prettyprint showlinemods notranslate lang-js\" translate=\"no\">-const fov = 75;\n+const fov = 40;\nconst aspect = 2;  // the canvas default\nconst near = 0.1;\n-const far = 5;\n+const far = 1000;\nconst camera = new THREE.PerspectiveCamera(fov, aspect, near, far);\n-camera.position.z = 2;\n+camera.position.z = 120;\n</pre>\n<p>Let's add a function, <code class=\"notranslate\" translate=\"no\">addObject</code>, that takes an x, y position and an <a href=\"/docs/#api/en/core/Object3D\"><code class=\"notranslate\" translate=\"no\">Object3D</code></a> and adds\nthe object to the scene.</p>\n<pre class=\"prettyprint showlinemods notranslate lang-js\" translate=\"no\">const objects = [];\nconst spread = 15;\n\nfunction addObject(x, y, obj) {\n  obj.position.x = x * spread;\n  obj.position.y = y * spread;\n\n  scene.add(obj);\n  objects.push(obj);\n}\n</pre>\n<p>Let's also make a function to create a random colored material.\nWe'll use a feature of <a href=\"/docs/#api/en/math/Color\"><code class=\"notranslate\" translate=\"no\">Color</code></a> that lets you set a color\nbased on hue, saturation, and luminance.</p>\n<p><code class=\"notranslate\" translate=\"no\">hue</code> goes from 0 to 1 around the color wheel with\nred at 0, green at .33 and blue at .66. <code class=\"notranslate\" translate=\"no\">saturation</code>\ngoes from 0 to 1 with 0 having no color and 1 being\nmost saturated. <code class=\"notranslate\" translate=\"no\">luminance</code> goes from 0 to 1\nwith 0 being black, 1 being white and 0.5 being\nthe maximum amount of color. In other words\nas <code class=\"notranslate\" translate=\"no\">luminance</code> goes from 0.0 to 0.5 the color\nwill go from black to <code class=\"notranslate\" translate=\"no\">hue</code>. From 0.5 to 1.0\nthe color will go from <code class=\"notranslate\" translate=\"no\">hue</code> to white.</p>\n<pre class=\"prettyprint showlinemods notranslate lang-js\" translate=\"no\">function createMaterial() {\n  const material = new THREE.MeshPhongMaterial({\n    side: THREE.DoubleSide,\n  });\n\n  const hue = Math.random();\n  const saturation = 1;\n  const luminance = .5;\n  material.color.setHSL(hue, saturation, luminance);\n\n  return material;\n}\n</pre>\n<p>We also passed <code class=\"notranslate\" translate=\"no\">side: THREE.DoubleSide</code> to the material.\nThis tells three to draw both sides of the triangles\nthat make up a shape. For a solid shape like a sphere\nor a cube there's usually no reason to draw the\nback sides of triangles as they all face inside the\nshape. In our case though we are drawing a few things\nlike the <a href=\"/docs/#api/en/geometries/PlaneGeometry\"><code class=\"notranslate\" translate=\"no\">PlaneGeometry</code></a> and the <a href=\"/docs/#api/en/geometries/ShapeGeometry\"><code class=\"notranslate\" translate=\"no\">ShapeGeometry</code></a>\nwhich are 2 dimensional and so have no inside. Without\nsetting <code class=\"notranslate\" translate=\"no\">side: THREE.DoubleSide</code> they would disappear\nwhen looking at their back sides.</p>\n<p>I should note that it's faster to draw when <strong>not</strong> setting\n<code class=\"notranslate\" translate=\"no\">side: THREE.DoubleSide</code> so ideally we'd set it only on\nthe materials that really need it but in this case we\nare not drawing too much so there isn't much reason to\nworry about it.</p>\n<p>Let's make a function, <code class=\"notranslate\" translate=\"no\">addSolidGeometry</code>, that\nwe pass a geometry and it creates a random colored\nmaterial via <code class=\"notranslate\" translate=\"no\">createMaterial</code> and adds it to the scene\nvia <code class=\"notranslate\" translate=\"no\">addObject</code>.</p>\n<pre class=\"prettyprint showlinemods notranslate lang-js\" translate=\"no\">function addSolidGeometry(x, y, geometry) {\n  const mesh = new THREE.Mesh(geometry, createMaterial());\n  addObject(x, y, mesh);\n}\n</pre>\n<p>Now we can use this for the majority of the primitives we create.\nFor example creating a box</p>\n<pre class=\"prettyprint showlinemods notranslate lang-js\" translate=\"no\">{\n  const width = 8;\n  const height = 8;\n  const depth = 8;\n  addSolidGeometry(-2, -2, new THREE.BoxGeometry(width, height, depth));\n}\n</pre>\n<p>If you look in the code below you'll see a similar section for each type of geometry.</p>\n<p>Here's the result:</p>\n<p></p><div translate=\"no\" class=\"threejs_example_container notranslate\">\n  <div></div>\n  <a class=\"threejs_center\" href=\"/manual/examples/primitives.html\" target=\"_blank\">click here to open in a separate window</a>\n</div>\n\n<p></p>\n<p>There are a couple of notable exceptions to the pattern above.\nThe biggest is probably the <a href=\"/docs/#examples/en/geometries/TextGeometry\"><code class=\"notranslate\" translate=\"no\">TextGeometry</code></a>. It needs to load\n3D font data before it can generate a mesh for the text.\nThat data loads asynchronously so we need to wait for it\nto load before trying to create the geometry. By promisifiying\nfont loading we can make it mush easier.\nWe create a <a href=\"/docs/#api/en/loaders/FontLoader\"><code class=\"notranslate\" translate=\"no\">FontLoader</code></a> and then a function <code class=\"notranslate\" translate=\"no\">loadFont</code> that returns\na promise that on resolve will give us the font. We then create\nan <code class=\"notranslate\" translate=\"no\">async</code> function called <code class=\"notranslate\" translate=\"no\">doit</code> and load the font using <code class=\"notranslate\" translate=\"no\">await</code>.\nAnd finally create the geometry and call <code class=\"notranslate\" translate=\"no\">addObject</code> to add it the scene.</p>\n<pre class=\"prettyprint showlinemods notranslate lang-js\" translate=\"no\">{\n  const loader = new FontLoader();\n  // promisify font loading\n  function loadFont(url) {\n    return new Promise((resolve, reject) =&gt; {\n      loader.load(url, resolve, undefined, reject);\n    });\n  }\n\n  async function doit() {\n    const font = await loadFont('resources/threejs/fonts/helvetiker_regular.typeface.json');  /* threejs.org: url */\n    const geometry = new TextGeometry('three.js', {\n      font: font,\n      size: 3.0,\n      depth: .2,\n      curveSegments: 12,\n      bevelEnabled: true,\n      bevelThickness: 0.15,\n      bevelSize: .3,\n      bevelSegments: 5,\n    });\n    const mesh = new THREE.Mesh(geometry, createMaterial());\n    geometry.computeBoundingBox();\n    geometry.boundingBox.getCenter(mesh.position).multiplyScalar(-1);\n\n    const parent = new THREE.Object3D();\n    parent.add(mesh);\n\n    addObject(-1, -1, parent);\n  }\n  doit();\n}\n</pre>\n<p>There's one other difference. We want to spin the text around its\ncenter but by default three.js creates the text such that its center of rotation\nis on the left edge. To work around this we can ask three.js to compute the bounding\nbox of the geometry. We can then call the <code class=\"notranslate\" translate=\"no\">getCenter</code> method\nof the bounding box and pass it our mesh's position object.\n<code class=\"notranslate\" translate=\"no\">getCenter</code> copies the center of the box into the position.\nIt also returns the position object so we can call <code class=\"notranslate\" translate=\"no\">multiplyScalar(-1)</code>\nto position the entire object such that its center of rotation\nis at the center of the object.</p>\n<p>If we then just called <code class=\"notranslate\" translate=\"no\">addSolidGeometry</code> like with previous\nexamples it would set the position again which is\nno good. So, in this case we create an <a href=\"/docs/#api/en/core/Object3D\"><code class=\"notranslate\" translate=\"no\">Object3D</code></a> which\nis the standard node for the three.js scene graph. <a href=\"/docs/#api/en/objects/Mesh\"><code class=\"notranslate\" translate=\"no\">Mesh</code></a>\nis inherited from <a href=\"/docs/#api/en/core/Object3D\"><code class=\"notranslate\" translate=\"no\">Object3D</code></a> as well. We'll cover <a href=\"scenegraph.html\">how the scene graph\nworks in another article</a>.\nFor now it's enough to know that\nlike DOM nodes, children are drawn relative to their parent.\nBy making an <a href=\"/docs/#api/en/core/Object3D\"><code class=\"notranslate\" translate=\"no\">Object3D</code></a> and making our mesh a child of that\nwe can position the <a href=\"/docs/#api/en/core/Object3D\"><code class=\"notranslate\" translate=\"no\">Object3D</code></a> wherever we want and still\nkeep the center offset we set earlier.</p>\n<p>If we didn't do this the text would spin off center.</p>\n<p></p><div translate=\"no\" class=\"threejs_example_container notranslate\">\n  <div></div>\n  <a class=\"threejs_center\" href=\"/manual/examples/primitives-text.html\" target=\"_blank\">click here to open in a separate window</a>\n</div>\n\n<p></p>\n<p>Notice the one on the left is not spinning around its center\nwhereas the one on the right is.</p>\n<p>The other exceptions are the 2 line based examples for <a href=\"/docs/#api/en/geometries/EdgesGeometry\"><code class=\"notranslate\" translate=\"no\">EdgesGeometry</code></a>\nand <a href=\"/docs/#api/en/geometries/WireframeGeometry\"><code class=\"notranslate\" translate=\"no\">WireframeGeometry</code></a>. Instead of calling <code class=\"notranslate\" translate=\"no\">addSolidGeometry</code> they call\n<code class=\"notranslate\" translate=\"no\">addLineGeometry</code> which looks like this</p>\n<pre class=\"prettyprint showlinemods notranslate lang-js\" translate=\"no\">function addLineGeometry(x, y, geometry) {\n  const material = new THREE.LineBasicMaterial({color: 0x000000});\n  const mesh = new THREE.LineSegments(geometry, material);\n  addObject(x, y, mesh);\n}\n</pre>\n<p>It creates a black <a href=\"/docs/#api/en/materials/LineBasicMaterial\"><code class=\"notranslate\" translate=\"no\">LineBasicMaterial</code></a> and then creates a <a href=\"/docs/#api/en/objects/LineSegments\"><code class=\"notranslate\" translate=\"no\">LineSegments</code></a>\nobject which is a wrapper for <a href=\"/docs/#api/en/objects/Mesh\"><code class=\"notranslate\" translate=\"no\">Mesh</code></a> that helps three know you're rendering\nline segments (2 points per segment).</p>\n<p>Each of the primitives has several parameters you can pass on creation\nand it's best to <a href=\"https://threejs.org/docs/\">look in the documentation</a> for all of them rather than\nrepeat them here. You can also click the links above next to each shape\nto take you directly to the docs for that shape.</p>\n<p>There is one other pair of classes that doesn't really fit the patterns above. Those are\nthe <a href=\"/docs/#api/en/materials/PointsMaterial\"><code class=\"notranslate\" translate=\"no\">PointsMaterial</code></a> and the <a href=\"/docs/#api/en/objects/Points\"><code class=\"notranslate\" translate=\"no\">Points</code></a> class. <a href=\"/docs/#api/en/objects/Points\"><code class=\"notranslate\" translate=\"no\">Points</code></a> is like <a href=\"/docs/#api/en/objects/LineSegments\"><code class=\"notranslate\" translate=\"no\">LineSegments</code></a> above in that it takes a\na <a href=\"/docs/#api/en/core/BufferGeometry\"><code class=\"notranslate\" translate=\"no\">BufferGeometry</code></a> but draws points at each vertex instead of lines.\nTo use it you also need to pass it a <a href=\"/docs/#api/en/materials/PointsMaterial\"><code class=\"notranslate\" translate=\"no\">PointsMaterial</code></a> which\ntake a <a href=\"/docs/#api/en/materials/PointsMaterial#size\"><code class=\"notranslate\" translate=\"no\">size</code></a> for how large to make the points.</p>\n<pre class=\"prettyprint showlinemods notranslate lang-js\" translate=\"no\">const radius = 7;\nconst widthSegments = 12;\nconst heightSegments = 8;\nconst geometry = new THREE.SphereGeometry(radius, widthSegments, heightSegments);\nconst material = new THREE.PointsMaterial({\n    color: 'red',\n    size: 0.2,     // in world units\n});\nconst points = new THREE.Points(geometry, material);\nscene.add(points);\n</pre>\n<div class=\"spread\">\n<div data-diagram=\"Points\"></div>\n</div>\n\n<p>You can turn off <a href=\"/docs/#api/en/materials/PointsMaterial#sizeAttenuation\"><code class=\"notranslate\" translate=\"no\">sizeAttenuation</code></a> by setting it to false if you want the points to\nbe the same size regardless of their distance from the camera.</p>\n<pre class=\"prettyprint showlinemods notranslate lang-js\" translate=\"no\">const material = new THREE.PointsMaterial({\n    color: 'red',\n+    sizeAttenuation: false,\n+    size: 3,       // in pixels\n-    size: 0.2,     // in world units\n});\n...\n</pre>\n<div class=\"spread\">\n<div data-diagram=\"PointsUniformSize\"></div>\n</div>\n\n<p>One other thing that's important to cover is that almost all shapes\nhave various settings for how much to subdivide them. A good example\nmight be the sphere geometries. Spheres take parameters for\nhow many divisions to make around and how many top to bottom. For example</p>\n<div class=\"spread\">\n<div data-diagram=\"SphereGeometryLow\"></div>\n<div data-diagram=\"SphereGeometryMedium\"></div>\n<div data-diagram=\"SphereGeometryHigh\"></div>\n</div>\n\n<p>The first sphere has 5 segments around and 3 high which is 15 segments\nor 30 triangles. The second sphere has 24 segments by 10. That's 240 segments\nor 480 triangles. The last one has 50 by 50 which is 2500 segments or 5000 triangles.</p>\n<p>It's up to you to decide how many subdivisions you need. It might\nlook like you need a high number of segments but remove the lines\nand the flat shading and we get this</p>\n<div class=\"spread\">\n<div data-diagram=\"SphereGeometryLowSmooth\"></div>\n<div data-diagram=\"SphereGeometryMediumSmooth\"></div>\n<div data-diagram=\"SphereGeometryHighSmooth\"></div>\n</div>\n\n<p>It's now not so clear that the one on the right with 5000 triangles\nis entirely better than the one in the middle with only 480.\nIf you're only drawing a few spheres, like say a single globe for\na map of the earth, then a single 10000 triangle sphere is not a bad\nchoice. If on the other hand you're trying to draw 1000 spheres\nthen 1000 spheres times 10000 triangles each is 10 million triangles.\nTo animate smoothly you need the browser to draw at 60 frames per\nsecond so you'd be asking the browser to draw 600 million triangles\nper second. That's a lot of computing.</p>\n<p>Sometimes it's easy to choose. For example you can also choose\nto subdivide a plane.</p>\n<div class=\"spread\">\n<div data-diagram=\"PlaneGeometryLow\"></div>\n<div data-diagram=\"PlaneGeometryHigh\"></div>\n</div>\n\n<p>The plane on the left is 2 triangles. The plane on the right\nis 200 triangles. Unlike the sphere there is really no trade off in quality for most\nuse cases of a plane. You'd most likely only subdivide a plane\nif you expected to want to modify or warp it in some way. A box\nis similar.</p>\n<p>So, choose whatever is appropriate for your situation. The less\nsubdivisions you choose the more likely things will run smoothly and the less\nmemory they'll take. You'll have to decide for yourself what the correct\ntradeoff is for your particular situation.</p>\n<p>If none of the shapes above fit your use case you can load\ngeometry for example from a <a href=\"load-obj.html\">.obj file</a>\nor a <a href=\"load-gltf.html\">.gltf file</a>.\nYou can also create your own <a href=\"custom-buffergeometry.html\">custom BufferGeometry</a>.</p>\n<p>Next up let's go over <a href=\"scenegraph.html\">how three's scene graph works and how\nto use it</a>.</p>\n<p><link rel=\"stylesheet\" href=\"../resources/threejs-primitives.css\"></p>\n\n\n\n        </div>\n      </div>\n    </div>\n\n  \n  \n\n\n\n\n<canvas id=\"c\" data-engine=\"three.js r185\"></canvas><canvas id=\"c\" data-engine=\"three.js r185\"></canvas><canvas id=\"c\" data-engine=\"three.js r185\"></canvas><canvas id=\"c\" data-engine=\"three.js r185\"></canvas><canvas id=\"c\" data-engine=\"three.js r185\"></canvas><canvas id=\"c\" data-engine=\"three.js r185\"></canvas><canvas id=\"c\" data-engine=\"three.js r185\"></canvas><canvas id=\"c\" data-engine=\"three.js r185\"></canvas><canvas id=\"c\" data-engine=\"three.js r185\"></canvas><canvas id=\"c\" data-engine=\"three.js r185\"></canvas><canvas id=\"c\" data-engine=\"three.js r185\"></canvas><canvas id=\"c\" data-engine=\"three.js r185\"></canvas><canvas id=\"c\" data-engine=\"three.js r185\"></canvas><canvas id=\"c\" data-engine=\"three.js r185\"></canvas><canvas id=\"c\" data-engine=\"three.js r185\"></canvas><canvas id=\"c\" data-engine=\"three.js r185\"></canvas><canvas id=\"c\" data-engine=\"three.js r185\"></canvas><canvas id=\"c\" data-engine=\"three.js r185\"></canvas><canvas id=\"c\" data-engine=\"three.js r185\"></canvas><canvas id=\"c\" data-engine=\"three.js r185\"></canvas><canvas id=\"c\" data-engine=\"three.js r185\"></canvas><canvas id=\"c\" data-engine=\"three.js r185\"></canvas><canvas id=\"c\" data-engine=\"three.js r185\"></canvas><canvas id=\"c\" data-engine=\"three.js r185\"></canvas><canvas id=\"c\" data-engine=\"three.js r185\"></canvas><canvas id=\"c\" data-engine=\"three.js r185\"></canvas><canvas id=\"c\" data-engine=\"three.js r185\"></canvas><canvas id=\"c\" data-engine=\"three.js r185\"></canvas><canvas id=\"c\" data-engine=\"three.js r185\"></canvas><canvas id=\"c\" data-engine=\"three.js r185\"></canvas><canvas id=\"c\" data-engine=\"three.js r185\"></canvas><canvas id=\"c\" data-engine=\"three.js r185\"></canvas><canvas id=\"c\" data-engine=\"three.js r185\"></canvas><canvas id=\"c\" data-engine=\"three.js r185\"></canvas><canvas id=\"c\" data-engine=\"three.js r185\"></canvas><canvas id=\"c\" data-engine=\"three.js r185\"></canvas><canvas id=\"c\" data-engine=\"three.js r185\"></canvas><canvas id=\"c\" data-engine=\"three.js r185\"></canvas><canvas id=\"c\" data-engine=\"three.js r185\"></canvas><canvas id=\"c\" data-engine=\"three.js r185\"></canvas><canvas id=\"c\" data-engine=\"three.js r185\"></canvas><canvas id=\"c\" data-engine=\"three.js r185\"></canvas><canvas id=\"c\" data-engine=\"three.js r185\"></canvas><canvas id=\"c\" data-engine=\"three.js r185\"></canvas><canvas id=\"c\" data-engine=\"three.js r185\"></canvas><canvas id=\"c\" data-engine=\"three.js r185\"></canvas><canvas id=\"c\" data-engine=\"three.js r185\"></canvas>",
    "success": true,
    "error": "",
    "crawled_at": 1786376809.300204,
    "is_shell": false
  },
  {
    "url": "https://threejs.org/manual/en/scenegraph.html",
    "title": "Scene Graph",
    "content": "<div class=\"container\">\n      <div class=\"lesson-title\">\n        <h1>Scene Graph</h1>\n      </div>\n      <div class=\"lesson\">\n        <div class=\"lesson-main\">\n          <p>This article is part of a series of articles about three.js. The\nfirst article is <a href=\"fundamentals.html\">three.js fundamentals</a>. If\nyou haven't read that yet you might want to consider starting there.</p>\n<p>Three.js's core is arguably its scene graph. A scene graph in a 3D\nengine is a hierarchy of nodes in a graph where each node represents\na local space.</p>\n<p><img src=\"../resources/images/scenegraph-generic.svg\" align=\"center\"></p>\n<p>That's kind of abstract so let's try to give some examples.</p>\n<p>One example might be solar system, sun, earth, moon.</p>\n<p><img src=\"../resources/images/scenegraph-solarsystem.svg\" align=\"center\"></p>\n<p>The Earth orbits the Sun. The Moon orbits the Earth. The Moon\nmoves in a circle around the Earth. From the Moon's point of\nview it's rotating in the \"local space\" of the Earth. Even though\nits motion relative to the Sun is some crazy spirograph like\ncurve from the Moon's point of view it just has to concern itself with rotating\naround the Earth's local space.</p>\n<p></p><div class=\"threejs_diagram_container\">\n  \n</div>\n\n<p></p>\n<p>To think of it another way, you living on the Earth do not have to think\nabout the Earth's rotation on its axis nor its rotation around the\nSun. You just walk or drive or swim or run as though the Earth is\nnot moving or rotating at all. You walk, drive, swim, run, and live\nin the Earth's \"local space\" even though relative to the sun you are\nspinning around the earth at around 1000 miles per hour and around\nthe sun at around 67,000 miles per hour. Your position in the solar\nsystem is similar to that of the moon above but you don't have to concern\nyourself. You just worry about your position relative to the earth in its\n\"local space\".</p>\n<p>Let's take it one step at a time. Imagine we want to make\na diagram of the sun, earth, and moon. We'll start with the sun by\njust making a sphere and putting it at the origin. Note: We're using\nsun, earth, moon as a demonstration of how to use a scene graph. Of course\nthe real sun, earth, and moon use physics but for our purposes we'll\nfake it with a scene graph.</p>\n<pre class=\"prettyprint showlinemods notranslate lang-js\" translate=\"no\">// an array of objects whose rotation to update\nconst objects = [];\n\n// use just one sphere for everything\nconst radius = 1;\nconst widthSegments = 6;\nconst heightSegments = 6;\nconst sphereGeometry = new THREE.SphereGeometry(\n    radius, widthSegments, heightSegments);\n\nconst sunMaterial = new THREE.MeshPhongMaterial({emissive: 0xFFFF00});\nconst sunMesh = new THREE.Mesh(sphereGeometry, sunMaterial);\nsunMesh.scale.set(5, 5, 5);  // make the sun large\nscene.add(sunMesh);\nobjects.push(sunMesh);\n</pre>\n<p>We're using a really low-polygon sphere. Only 6 subdivisions around its equator.\nThis is so it's easy to see the rotation.</p>\n<p>We're going to reuse the same sphere for everything so we'll set a scale\nfor the sun mesh of 5x.</p>\n<p>We also set the phong material's <code class=\"notranslate\" translate=\"no\">emissive</code> property to yellow. A phong material's\nemissive property is basically the color that will be drawn with no light hitting\nthe surface. Light is added to that color.</p>\n<p>Let's also put a single point light in the center of the scene. We'll go into more\ndetails about point lights later but for now the simple version is a point light\nrepresents light that emanates from a single point.</p>\n<pre class=\"prettyprint showlinemods notranslate lang-js\" translate=\"no\">{\n  const color = 0xFFFFFF;\n  const intensity = 500;\n  const light = new THREE.PointLight(color, intensity);\n  scene.add(light);\n}\n</pre>\n<p>To make it easy to see we're going to put the camera directly above the origin\nlooking down. The easiest way to do that is to use the <code class=\"notranslate\" translate=\"no\">lookAt</code> function. The <code class=\"notranslate\" translate=\"no\">lookAt</code>\nfunction will orient the camera from its position to \"look at\" the position\nwe pass to <code class=\"notranslate\" translate=\"no\">lookAt</code>. Before we do that though we need to tell the camera\nwhich way the top of the camera is facing or rather which way is \"up\" for the\ncamera. For most situations positive Y being up is good enough but since\nwe are looking straight down we need to tell the camera that positive Z is up.</p>\n<pre class=\"prettyprint showlinemods notranslate lang-js\" translate=\"no\">const camera = new THREE.PerspectiveCamera(fov, aspect, near, far);\ncamera.position.set(0, 50, 0);\ncamera.up.set(0, 0, 1);\ncamera.lookAt(0, 0, 0);\n</pre>\n<p>In the render loop, adapted from previous examples, we're rotating all\nobjects in our <code class=\"notranslate\" translate=\"no\">objects</code> array with this code.</p>\n<pre class=\"prettyprint showlinemods notranslate lang-js\" translate=\"no\">objects.forEach((obj) =&gt; {\n  obj.rotation.y = time;\n});\n</pre>\n<p>Since we added the <code class=\"notranslate\" translate=\"no\">sunMesh</code> to the <code class=\"notranslate\" translate=\"no\">objects</code> array it will rotate.</p>\n<p></p><div translate=\"no\" class=\"threejs_example_container notranslate\">\n  <div></div>\n  <a class=\"threejs_center\" href=\"/manual/examples/scenegraph-sun.html\" target=\"_blank\">click here to open in a separate window</a>\n</div>\n\n<p></p>\n<p>Now let's add in the earth.</p>\n<pre class=\"prettyprint showlinemods notranslate lang-js\" translate=\"no\">const earthMaterial = new THREE.MeshPhongMaterial({color: 0x2233FF, emissive: 0x112244});\nconst earthMesh = new THREE.Mesh(sphereGeometry, earthMaterial);\nearthMesh.position.x = 10;\nscene.add(earthMesh);\nobjects.push(earthMesh);\n</pre>\n<p>We make a material that is blue but we gave it a small amount of <em>emissive</em> blue\nso that it will show up against our black background.</p>\n<p>We use the same <code class=\"notranslate\" translate=\"no\">sphereGeometry</code> with our new blue <code class=\"notranslate\" translate=\"no\">earthMaterial</code> to make\nan <code class=\"notranslate\" translate=\"no\">earthMesh</code>. We position that 10 units to the left of the sun\nand add it to the scene.  Since we added it to our <code class=\"notranslate\" translate=\"no\">objects</code> array it will\nrotate too.</p>\n<p></p><div translate=\"no\" class=\"threejs_example_container notranslate\">\n  <div></div>\n  <a class=\"threejs_center\" href=\"/manual/examples/scenegraph-sun-earth.html\" target=\"_blank\">click here to open in a separate window</a>\n</div>\n\n<p></p>\n<p>You can see both the sun and the earth are rotating but the earth is not\ngoing around the sun. Let's make the earth a child of the sun</p>\n<pre class=\"prettyprint showlinemods notranslate lang-js\" translate=\"no\">-scene.add(earthMesh);\n+sunMesh.add(earthMesh);\n</pre>\n<p>and...</p>\n<p></p><div translate=\"no\" class=\"threejs_example_container notranslate\">\n  <div></div>\n  <a class=\"threejs_center\" href=\"/manual/examples/scenegraph-sun-earth-orbit.html\" target=\"_blank\">click here to open in a separate window</a>\n</div>\n\n<p></p>\n<p>What happened? Why is the earth the same size as the sun and why is it so far away?\nI actually had to move the camera from 50 units above to 150 units above to see the earth.</p>\n<p>We made the <code class=\"notranslate\" translate=\"no\">earthMesh</code> a child of the <code class=\"notranslate\" translate=\"no\">sunMesh</code>. The <code class=\"notranslate\" translate=\"no\">sunMesh</code> has\nits scale set to 5x with <code class=\"notranslate\" translate=\"no\">sunMesh.scale.set(5, 5, 5)</code>. That means the\n<code class=\"notranslate\" translate=\"no\">sunMesh</code>s local space is 5 times as big. Anything put in that space\n will be multiplied by 5. That means the earth is now 5x larger and\n its distance from the sun (<code class=\"notranslate\" translate=\"no\">earthMesh.position.x = 10</code>) is also\n 5x as well.</p>\n<p> Our scene graph currently looks like this</p>\n<p><img src=\"../resources/images/scenegraph-sun-earth.svg\" align=\"center\"></p>\n<p>To fix it let's add an empty scene graph node. We'll parent both the sun and the earth\nto that node.</p>\n<pre class=\"prettyprint showlinemods notranslate lang-js\" translate=\"no\">+const solarSystem = new THREE.Object3D();\n+scene.add(solarSystem);\n+objects.push(solarSystem);\n\nconst sunMaterial = new THREE.MeshPhongMaterial({emissive: 0xFFFF00});\nconst sunMesh = new THREE.Mesh(sphereGeometry, sunMaterial);\nsunMesh.scale.set(5, 5, 5);\n-scene.add(sunMesh);\n+solarSystem.add(sunMesh);\nobjects.push(sunMesh);\n\nconst earthMaterial = new THREE.MeshPhongMaterial({color: 0x2233FF, emissive: 0x112244});\nconst earthMesh = new THREE.Mesh(sphereGeometry, earthMaterial);\nearthMesh.position.x = 10;\n-sunMesh.add(earthMesh);\n+solarSystem.add(earthMesh);\nobjects.push(earthMesh);\n</pre>\n<p>Here we made an <a href=\"/docs/#api/en/core/Object3D\"><code class=\"notranslate\" translate=\"no\">Object3D</code></a>. Like a <a href=\"/docs/#api/en/objects/Mesh\"><code class=\"notranslate\" translate=\"no\">Mesh</code></a> it is also a node in the scene graph\nbut unlike a <a href=\"/docs/#api/en/objects/Mesh\"><code class=\"notranslate\" translate=\"no\">Mesh</code></a> it has no material or geometry. It just represents a local space.</p>\n<p>Our new scene graph looks like this</p>\n<p><img src=\"../resources/images/scenegraph-sun-earth-fixed.svg\" align=\"center\"></p>\n<p>Both the <code class=\"notranslate\" translate=\"no\">sunMesh</code> and the <code class=\"notranslate\" translate=\"no\">earthMesh</code> are children of the <code class=\"notranslate\" translate=\"no\">solarSystem</code>. All 3\nare being rotated and now because the <code class=\"notranslate\" translate=\"no\">earthMesh</code> is not a child of the <code class=\"notranslate\" translate=\"no\">sunMesh</code>\nit is no longer scaled by 5x.</p>\n<p></p><div translate=\"no\" class=\"threejs_example_container notranslate\">\n  <div></div>\n  <a class=\"threejs_center\" href=\"/manual/examples/scenegraph-sun-earth-orbit-fixed.html\" target=\"_blank\">click here to open in a separate window</a>\n</div>\n\n<p></p>\n<p>Much better. The earth is smaller than the sun and it's rotating around the sun\nand rotating itself.</p>\n<p>Continuing that same pattern let's add a moon.</p>\n<pre class=\"prettyprint showlinemods notranslate lang-js\" translate=\"no\">+const earthOrbit = new THREE.Object3D();\n+earthOrbit.position.x = 10;\n+solarSystem.add(earthOrbit);\n+objects.push(earthOrbit);\n\nconst earthMaterial = new THREE.MeshPhongMaterial({color: 0x2233FF, emissive: 0x112244});\nconst earthMesh = new THREE.Mesh(sphereGeometry, earthMaterial);\n-earthMesh.position.x = 10; // note that this offset is already set in its parent's THREE.Object3D object \"earthOrbit\"\n-solarSystem.add(earthMesh);\n+earthOrbit.add(earthMesh);\nobjects.push(earthMesh);\n\n+const moonOrbit = new THREE.Object3D();\n+moonOrbit.position.x = 2;\n+earthOrbit.add(moonOrbit);\n\n+const moonMaterial = new THREE.MeshPhongMaterial({color: 0x888888, emissive: 0x222222});\n+const moonMesh = new THREE.Mesh(sphereGeometry, moonMaterial);\n+moonMesh.scale.set(.5, .5, .5);\n+moonOrbit.add(moonMesh);\n+objects.push(moonMesh);\n</pre>\n<p>Again we added more invisible scene graph nodes. The first, an <a href=\"/docs/#api/en/core/Object3D\"><code class=\"notranslate\" translate=\"no\">Object3D</code></a> called <code class=\"notranslate\" translate=\"no\">earthOrbit</code>\nand added both the <code class=\"notranslate\" translate=\"no\">earthMesh</code> and the <code class=\"notranslate\" translate=\"no\">moonOrbit</code> to it, also new. We then added the <code class=\"notranslate\" translate=\"no\">moonMesh</code>\nto the <code class=\"notranslate\" translate=\"no\">moonOrbit</code>. The new scene graph looks like this.</p>\n<p><img src=\"../resources/images/scenegraph-sun-earth-moon.svg\" align=\"center\"></p>\n<p>and here's that</p>\n<p></p><div translate=\"no\" class=\"threejs_example_container notranslate\">\n  <div></div>\n  <a class=\"threejs_center\" href=\"/manual/examples/scenegraph-sun-earth-moon.html\" target=\"_blank\">click here to open in a separate window</a>\n</div>\n\n<p></p>\n<p>You can see the moon follows the spirograph pattern shown at the top\nof this article but we didn't have to manually compute it. We just\nsetup our scene graph to do it for us.</p>\n<p>It is often useful to draw something to visualize the nodes in the scene graph.\nThree.js has some helpful ummmm, helpers to ummm, ... help with this.</p>\n<p>One is called an <a href=\"/docs/#api/en/helpers/AxesHelper\"><code class=\"notranslate\" translate=\"no\">AxesHelper</code></a>. It draws 3 lines representing the local\n<span style=\"color:red\">X</span>,\n<span style=\"color:green\">Y</span>, and\n<span style=\"color:blue\">Z</span> axes. Let's add one to every node we\ncreated.</p>\n<pre class=\"prettyprint showlinemods notranslate lang-js\" translate=\"no\">// add an AxesHelper to each node\nobjects.forEach((node) =&gt; {\n  const axes = new THREE.AxesHelper();\n  axes.material.depthTest = false;\n  axes.renderOrder = 1;\n  node.add(axes);\n});\n</pre>\n<p>On our case we want the axes to appear even though they are inside the spheres.\nTo do this we set their material's <code class=\"notranslate\" translate=\"no\">depthTest</code> to false which means they will\nnot check to see if they are drawing behind something else. We also\nset their <code class=\"notranslate\" translate=\"no\">renderOrder</code> to 1 (the default is 0) so that they get drawn after\nall the spheres. Otherwise a sphere might draw over them and cover them up.</p>\n<p></p><div translate=\"no\" class=\"threejs_example_container notranslate\">\n  <div></div>\n  <a class=\"threejs_center\" href=\"/manual/examples/scenegraph-sun-earth-moon-axes.html\" target=\"_blank\">click here to open in a separate window</a>\n</div>\n\n<p></p>\n<p>We can see the\n<span style=\"color:red\">x (red)</span> and\n<span style=\"color:blue\">z (blue)</span> axes. Since we are looking\nstraight down and each of our objects is only rotating around its\ny axis we don't see much of the <span style=\"color:green\">y (green)</span> axes.</p>\n<p>It might be hard to see some of them as there are 2 pairs of overlapping axes. Both the <code class=\"notranslate\" translate=\"no\">sunMesh</code>\nand the <code class=\"notranslate\" translate=\"no\">solarSystem</code> are at the same position. Similarly the <code class=\"notranslate\" translate=\"no\">earthMesh</code> and\n<code class=\"notranslate\" translate=\"no\">earthOrbit</code> are at the same position. Let's add some simple controls to allow us\nto turn them on/off for each node.\nWhile we're at it let's also add another helper called the <a href=\"/docs/#api/en/helpers/GridHelper\"><code class=\"notranslate\" translate=\"no\">GridHelper</code></a>. It\nmakes a 2D grid on the X,Z plane. By default the grid is 10x10 units.</p>\n<p>We're also going to use <a href=\"https://github.com/georgealways/lil-gui\">lil-gui</a> which is\na UI library that is very popular with three.js projects. lil-gui takes an\nobject and a property name on that object and based on the type of the property\nautomatically makes a UI to manipulate that property.</p>\n<p>We want to make both a <a href=\"/docs/#api/en/helpers/GridHelper\"><code class=\"notranslate\" translate=\"no\">GridHelper</code></a> and an <a href=\"/docs/#api/en/helpers/AxesHelper\"><code class=\"notranslate\" translate=\"no\">AxesHelper</code></a> for each node. We need\na label for each node so we'll get rid of the old loop and switch to calling\nsome function to add the helpers for each node</p>\n<pre class=\"prettyprint showlinemods notranslate lang-js\" translate=\"no\">-// add an AxesHelper to each node\n-objects.forEach((node) =&gt; {\n-  const axes = new THREE.AxesHelper();\n-  axes.material.depthTest = false;\n-  axes.renderOrder = 1;\n-  node.add(axes);\n-});\n\n+function makeAxisGrid(node, label, units) {\n+  const helper = new AxisGridHelper(node, units);\n+  gui.add(helper, 'visible').name(label);\n+}\n+\n+makeAxisGrid(solarSystem, 'solarSystem', 25);\n+makeAxisGrid(sunMesh, 'sunMesh');\n+makeAxisGrid(earthOrbit, 'earthOrbit');\n+makeAxisGrid(earthMesh, 'earthMesh');\n+makeAxisGrid(moonOrbit, 'moonOrbit');\n+makeAxisGrid(moonMesh, 'moonMesh');\n</pre>\n<p><code class=\"notranslate\" translate=\"no\">makeAxisGrid</code> makes an <code class=\"notranslate\" translate=\"no\">AxisGridHelper</code> which is a class we'll create\nto make lil-gui happy. Like it says above lil-gui\nwill automagically make a UI that manipulates the named property\nof some object. It will create a different UI depending on the type\nof property. We want it to create a checkbox so we need to specify\na <code class=\"notranslate\" translate=\"no\">bool</code> property. But, we want both the axes and the grid\nto appear/disappear based on a single property so we'll make a class\nthat has a getter and setter for a property. That way we can let lil-gui\nthink it's manipulating a single property but internally we can set\nthe visible property of both the <a href=\"/docs/#api/en/helpers/AxesHelper\"><code class=\"notranslate\" translate=\"no\">AxesHelper</code></a> and <a href=\"/docs/#api/en/helpers/GridHelper\"><code class=\"notranslate\" translate=\"no\">GridHelper</code></a> for a node.</p>\n<pre class=\"prettyprint showlinemods notranslate lang-js\" translate=\"no\">// Turns both axes and grid visible on/off\n// lil-gui requires a property that returns a bool\n// to decide to make a checkbox so we make a setter\n// and getter for <code class=\"notranslate\" translate=\"no\">visible</code> which we can tell lil-gui\n// to look at.\nclass AxisGridHelper {\n  constructor(node, units = 10) {\n    const axes = new THREE.AxesHelper();\n    axes.material.depthTest = false;\n    axes.renderOrder = 2;  // after the grid\n    node.add(axes);\n\n    const grid = new THREE.GridHelper(units, units);\n    grid.material.depthTest = false;\n    grid.renderOrder = 1;\n    node.add(grid);\n\n    this.grid = grid;\n    this.axes = axes;\n    this.visible = false;\n  }\n  get visible() {\n    return this._visible;\n  }\n  set visible(v) {\n    this._visible = v;\n    this.grid.visible = v;\n    this.axes.visible = v;\n  }\n}\n</pre>\n<p>One thing to notice is we set the <code class=\"notranslate\" translate=\"no\">renderOrder</code> of the <a href=\"/docs/#api/en/helpers/AxesHelper\"><code class=\"notranslate\" translate=\"no\">AxesHelper</code></a>\nto 2 and for the <a href=\"/docs/#api/en/helpers/GridHelper\"><code class=\"notranslate\" translate=\"no\">GridHelper</code></a> to 1 so that the axes get drawn after the grid.\nOtherwise the grid might overwrite the axes.</p>\n<p></p><div translate=\"no\" class=\"threejs_example_container notranslate\">\n  <div></div>\n  <a class=\"threejs_center\" href=\"/manual/examples/scenegraph-sun-earth-moon-axes-grids.html\" target=\"_blank\">click here to open in a separate window</a>\n</div>\n\n<p></p>\n<p>Turn on the <code class=\"notranslate\" translate=\"no\">solarSystem</code> and you'll see how the earth is exactly 10\nunits out from the center just like we set above. You can see how the\nearth is in the <em>local space</em> of the <code class=\"notranslate\" translate=\"no\">solarSystem</code>. Similarly if you\nturn on the <code class=\"notranslate\" translate=\"no\">earthOrbit</code> you'll see how the moon is exactly 2 units\nfrom the center of the <em>local space</em> of the <code class=\"notranslate\" translate=\"no\">earthOrbit</code>.</p>\n<p>A few more examples of scene graphs. An automobile in a simple game world might have a scene graph like this</p>\n<p><img src=\"../resources/images/scenegraph-car.svg\" align=\"center\"></p>\n<p>If you move the car's body all the wheels will move with it. If you wanted the body\nto bounce separate from the wheels you might parent the body and the wheels to a \"frame\" node\nthat represents the car's frame.</p>\n<p>Another example is a human in a game world.</p>\n<p><img src=\"../resources/images/scenegraph-human.svg\" align=\"center\"></p>\n<p>You can see the scene graph gets pretty complex for a human. In fact\nthat scene graph above is simplified. For example you might extend it\nto cover every finger (at least another 28 nodes) and every toe\n(yet another 28 nodes) plus ones for the face and jaw, the eyes and maybe more.</p>\n<p>Let's make one semi-complex scene graph. We'll make a tank. The tank will have\n6 wheels and a turret. The tank will follow a path. There will be a sphere that\nmoves around and the tank will target the sphere.</p>\n<p>Here's the scene graph. The meshes are colored in green, the <a href=\"/docs/#api/en/core/Object3D\"><code class=\"notranslate\" translate=\"no\">Object3D</code></a>s in blue,\nthe lights in gold, and the cameras in purple. One camera has not been added\nto the scene graph.</p>\n<div class=\"threejs_center\"><img src=\"../resources/images/scenegraph-tank.svg\" style=\"width: 800px;\"></div>\n\n<p>Look in the code to see the setup of all of these nodes.</p>\n<p>For the target, the thing the tank is aiming at, there is a <code class=\"notranslate\" translate=\"no\">targetOrbit</code>\n(<a href=\"/docs/#api/en/core/Object3D\"><code class=\"notranslate\" translate=\"no\">Object3D</code></a>) which just rotates similar to the <code class=\"notranslate\" translate=\"no\">earthOrbit</code> above. A\n<code class=\"notranslate\" translate=\"no\">targetElevation</code> (<a href=\"/docs/#api/en/core/Object3D\"><code class=\"notranslate\" translate=\"no\">Object3D</code></a>) which is a child of the <code class=\"notranslate\" translate=\"no\">targetOrbit</code> provides an\noffset from the <code class=\"notranslate\" translate=\"no\">targetOrbit</code> and a base elevation. Childed to that is another\n<a href=\"/docs/#api/en/core/Object3D\"><code class=\"notranslate\" translate=\"no\">Object3D</code></a> called <code class=\"notranslate\" translate=\"no\">targetBob</code> which just bobs up and down relative to the\n<code class=\"notranslate\" translate=\"no\">targetElevation</code>. Finally there's the <code class=\"notranslate\" translate=\"no\">targetMesh</code> which is just a cube we\nrotate and change its colors</p>\n<pre class=\"prettyprint showlinemods notranslate lang-js\" translate=\"no\">// move target\ntargetOrbit.rotation.y = time * .27;\ntargetBob.position.y = Math.sin(time * 2) * 4;\ntargetMesh.rotation.x = time * 7;\ntargetMesh.rotation.y = time * 13;\ntargetMaterial.emissive.setHSL(time * 10 % 1, 1, .25);\ntargetMaterial.color.setHSL(time * 10 % 1, 1, .25);\n</pre>\n<p>For the tank there's an <a href=\"/docs/#api/en/core/Object3D\"><code class=\"notranslate\" translate=\"no\">Object3D</code></a> called <code class=\"notranslate\" translate=\"no\">tank</code> which is used to move everything\nbelow it around. The code uses a <a href=\"/docs/#api/en/extras/curves/SplineCurve\"><code class=\"notranslate\" translate=\"no\">SplineCurve</code></a> which it can ask for positions\nalong that curve. 0.0 is the start of the curve. 1.0 is the end of the curve. It\nasks for the current position where it puts the tank. It then asks for a\nposition slightly further down the curve and uses that to point the tank in that\ndirection using <a href=\"/docs/#api/en/core/Object3D.lookAt\"><code class=\"notranslate\" translate=\"no\">Object3D.lookAt</code></a>.</p>\n<pre class=\"prettyprint showlinemods notranslate lang-js\" translate=\"no\">const tankPosition = new THREE.Vector2();\nconst tankTarget = new THREE.Vector2();\n\n...\n\n// move tank\nconst tankTime = time * .05;\ncurve.getPointAt(tankTime % 1, tankPosition);\ncurve.getPointAt((tankTime + 0.01) % 1, tankTarget);\ntank.position.set(tankPosition.x, 0, tankPosition.y);\ntank.lookAt(tankTarget.x, 0, tankTarget.y);\n</pre>\n<p>The turret on top of the tank is moved automatically by being a child\nof the tank. To point it at the target we just ask for the target's world position\nand then again use <a href=\"/docs/#api/en/core/Object3D.lookAt\"><code class=\"notranslate\" translate=\"no\">Object3D.lookAt</code></a></p>\n<pre class=\"prettyprint showlinemods notranslate lang-js\" translate=\"no\">const targetPosition = new THREE.Vector3();\n\n...\n\n// face turret at target\ntargetMesh.getWorldPosition(targetPosition);\nturretPivot.lookAt(targetPosition);\n</pre>\n<p>There's a <code class=\"notranslate\" translate=\"no\">turretCamera</code> which is a child of the <code class=\"notranslate\" translate=\"no\">turretMesh</code> so\nit will move up and down and rotate with the turret. We make that\naim at the target.</p>\n<pre class=\"prettyprint showlinemods notranslate lang-js\" translate=\"no\">// make the turretCamera look at target\nturretCamera.lookAt(targetPosition);\n</pre>\n<p>There is also a <code class=\"notranslate\" translate=\"no\">targetCameraPivot</code> which is a child of <code class=\"notranslate\" translate=\"no\">targetBob</code> so it floats\naround with the target. We aim that back at the tank. Its purpose is to allow the\n<code class=\"notranslate\" translate=\"no\">targetCamera</code> to be offset from the target itself. If we instead made the camera\na child of <code class=\"notranslate\" translate=\"no\">targetBob</code> and just aimed the camera itself it would be inside the\ntarget.</p>\n<pre class=\"prettyprint showlinemods notranslate lang-js\" translate=\"no\">// make the targetCameraPivot look at the tank\ntank.getWorldPosition(targetPosition);\ntargetCameraPivot.lookAt(targetPosition);\n</pre>\n<p>Finally we rotate all the wheels</p>\n<pre class=\"prettyprint showlinemods notranslate lang-js\" translate=\"no\">wheelMeshes.forEach((obj) =&gt; {\n  obj.rotation.x = time * 3;\n});\n</pre>\n<p>For the cameras we setup an array of all 4 cameras at init time with descriptions.</p>\n<pre class=\"prettyprint showlinemods notranslate lang-js\" translate=\"no\">const cameras = [\n  { cam: camera, desc: 'detached camera', },\n  { cam: turretCamera, desc: 'on turret looking at target', },\n  { cam: targetCamera, desc: 'near target looking at tank', },\n  { cam: tankCamera, desc: 'above back of tank', },\n];\n\nconst infoElem = document.querySelector('#info');\n</pre>\n<p>and cycle through our cameras at render time.</p>\n<pre class=\"prettyprint showlinemods notranslate lang-js\" translate=\"no\">const camera = cameras[time * .25 % cameras.length | 0];\ninfoElem.textContent = camera.desc;\n</pre>\n<p></p><div translate=\"no\" class=\"threejs_example_container notranslate\">\n  <div></div>\n  <a class=\"threejs_center\" href=\"/manual/examples/scenegraph-tank.html\" target=\"_blank\">click here to open in a separate window</a>\n</div>\n\n<p></p>\n<p>I hope this gives some idea of how scene graphs work and how you might use them.\nMaking <a href=\"/docs/#api/en/core/Object3D\"><code class=\"notranslate\" translate=\"no\">Object3D</code></a> nodes and parenting things to them is an important step to using\na 3D engine like three.js well. Often it might seem like some complex math is necessary\nto make something move and rotate the way you want. For example without a scene graph\ncomputing the motion of the moon or where to put the wheels of the car relative to its\nbody would be very complicated but using a scene graph it becomes much easier.</p>\n<p><a href=\"materials.html\">Next up we'll go over materials</a>.</p>\n\n        </div>\n      </div>\n    </div>",
    "success": true,
    "error": "",
    "crawled_at": 1786376809.300204,
    "is_shell": false
  },
  {
    "url": "https://threejs.org/manual/en/materials.html",
    "title": "Materials",
    "content": "<div class=\"container\">\n      <div class=\"lesson-title\">\n        <h1>Materials</h1>\n      </div>\n      <div class=\"lesson\">\n        <div class=\"lesson-main\">\n          <p>This article is part of a series of articles about three.js. The\nfirst article is <a href=\"fundamentals.html\">three.js fundamentals</a>. If\nyou haven't read that yet and you're new to three.js you might want to\nconsider starting there.</p>\n<p>Three.js provides several types of materials.\nThey define how objects will appear in the scene.\nWhich materials you use really depends on what you're trying to\naccomplish.</p>\n<p>There are 2 ways to set most material properties. One at creation time which\nwe've seen before.</p>\n<pre class=\"prettyprint showlinemods notranslate lang-js\" translate=\"no\">const material = new THREE.MeshPhongMaterial({\n  color: 0xFF0000,    // red (can also use a CSS color string here)\n  flatShading: true,\n});\n</pre>\n<p>The other is after creation</p>\n<pre class=\"prettyprint showlinemods notranslate lang-js\" translate=\"no\">const material = new THREE.MeshPhongMaterial();\nmaterial.color.setHSL(0, 1, .5);  // red\nmaterial.flatShading = true;\n</pre>\n<p>note that properties of type <a href=\"/docs/#api/en/math/Color\"><code class=\"notranslate\" translate=\"no\">THREE.Color</code></a> have multiple ways to be set.</p>\n<pre class=\"prettyprint showlinemods notranslate lang-js\" translate=\"no\">material.color.set(0x00FFFF);    // same as CSS's #RRGGBB style\nmaterial.color.set(cssString);   // any CSS color, eg 'purple', '#F32',\n                                 // 'rgb(255, 127, 64)',\n                                 // 'hsl(180, 50%, 25%)'\nmaterial.color.set(someColor)    // some other THREE.Color\nmaterial.color.setHSL(h, s, l)   // where h, s, and l are 0 to 1\nmaterial.color.setRGB(r, g, b)   // where r, g, and b are 0 to 1\n</pre>\n<p>And at creation time you can pass either a hex number or a CSS string</p>\n<pre class=\"prettyprint showlinemods notranslate lang-js\" translate=\"no\">const m1 = new THREE.MeshBasicMaterial({color: 0xFF0000});         // red\nconst m2 = new THREE.MeshBasicMaterial({color: 'red'});            // red\nconst m3 = new THREE.MeshBasicMaterial({color: '#F00'});           // red\nconst m4 = new THREE.MeshBasicMaterial({color: 'rgb(255,0,0)'});   // red\nconst m5 = new THREE.MeshBasicMaterial({color: 'hsl(0,100%,50%)'}); // red\n</pre>\n<p>So let's go over three.js's set of materials.</p>\n<p>The <a href=\"/docs/#api/en/materials/MeshBasicMaterial\"><code class=\"notranslate\" translate=\"no\">MeshBasicMaterial</code></a> is not affected by lights.\nThe <a href=\"/docs/#api/en/materials/MeshLambertMaterial\"><code class=\"notranslate\" translate=\"no\">MeshLambertMaterial</code></a> computes lighting only at the vertices vs the <a href=\"/docs/#api/en/materials/MeshPhongMaterial\"><code class=\"notranslate\" translate=\"no\">MeshPhongMaterial</code></a> which computes lighting at every pixel. The <a href=\"/docs/#api/en/materials/MeshPhongMaterial\"><code class=\"notranslate\" translate=\"no\">MeshPhongMaterial</code></a>\nalso supports specular highlights.</p>\n<div class=\"spread\">\n  <div>\n    <div data-diagram=\"MeshBasicMaterial\"></div>\n    <div class=\"code\">Basic</div>\n  </div>\n  <div>\n    <div data-diagram=\"MeshLambertMaterial\"></div>\n    <div class=\"code\">Lambert</div>\n  </div>\n  <div>\n    <div data-diagram=\"MeshPhongMaterial\"></div>\n    <div class=\"code\">Phong</div>\n  </div>\n</div>\n<div class=\"spread\">\n  <div>\n    <div data-diagram=\"MeshBasicMaterialLowPoly\"></div>\n  </div>\n  <div>\n    <div data-diagram=\"MeshLambertMaterialLowPoly\"></div>\n  </div>\n  <div>\n    <div data-diagram=\"MeshPhongMaterialLowPoly\"></div>\n  </div>\n</div>\n<div class=\"threejs_center code\">low-poly models with same materials</div>\n\n<p>The <code class=\"notranslate\" translate=\"no\">shininess</code> setting of the <a href=\"/docs/#api/en/materials/MeshPhongMaterial\"><code class=\"notranslate\" translate=\"no\">MeshPhongMaterial</code></a> determines the <em>shininess</em> of the specular highlight. It defaults to 30.</p>\n<div class=\"spread\">\n  <div>\n    <div data-diagram=\"MeshPhongMaterialShininess0\"></div>\n    <div class=\"code\">shininess: 0</div>\n  </div>\n  <div>\n    <div data-diagram=\"MeshPhongMaterialShininess30\"></div>\n    <div class=\"code\">shininess: 30</div>\n  </div>\n  <div>\n    <div data-diagram=\"MeshPhongMaterialShininess150\"></div>\n    <div class=\"code\">shininess: 150</div>\n  </div>\n</div>\n\n<p>Note that setting the <code class=\"notranslate\" translate=\"no\">emissive</code> property to a color on either a\n<a href=\"/docs/#api/en/materials/MeshLambertMaterial\"><code class=\"notranslate\" translate=\"no\">MeshLambertMaterial</code></a> or a <a href=\"/docs/#api/en/materials/MeshPhongMaterial\"><code class=\"notranslate\" translate=\"no\">MeshPhongMaterial</code></a> and setting the <code class=\"notranslate\" translate=\"no\">color</code> to black\n(and <code class=\"notranslate\" translate=\"no\">shininess</code> to 0 for phong) ends up looking just like the <a href=\"/docs/#api/en/materials/MeshBasicMaterial\"><code class=\"notranslate\" translate=\"no\">MeshBasicMaterial</code></a>.</p>\n<div class=\"spread\">\n  <div>\n    <div data-diagram=\"MeshBasicMaterialCompare\"></div>\n    <div class=\"code\">\n      <div>Basic</div>\n      <div>color: 'purple'</div>\n    </div>\n  </div>\n  <div>\n    <div data-diagram=\"MeshLambertMaterialCompare\"></div>\n    <div class=\"code\">\n      <div>Lambert</div>\n      <div>color: 'black'</div>\n      <div>emissive: 'purple'</div>\n    </div>\n  </div>\n  <div>\n    <div data-diagram=\"MeshPhongMaterialCompare\"></div>\n    <div class=\"code\">\n      <div>Phong</div>\n      <div>color: 'black'</div>\n      <div>emissive: 'purple'</div>\n      <div>shininess: 0</div>\n    </div>\n  </div>\n</div>\n\n<p>Why have all 3 when <a href=\"/docs/#api/en/materials/MeshPhongMaterial\"><code class=\"notranslate\" translate=\"no\">MeshPhongMaterial</code></a> can do the same things as <a href=\"/docs/#api/en/materials/MeshBasicMaterial\"><code class=\"notranslate\" translate=\"no\">MeshBasicMaterial</code></a>\nand <a href=\"/docs/#api/en/materials/MeshLambertMaterial\"><code class=\"notranslate\" translate=\"no\">MeshLambertMaterial</code></a>? The reason is the more sophisticated material\ntakes more GPU power to draw. On a slower GPU like say a mobile phone\nyou might want to reduce the GPU power needed to draw your scene by\nusing one of the less complex materials. It also follows that if you\ndon't need the extra features then use the simplest material. If you don't\nneed the lighting and the specular highlight then use the <a href=\"/docs/#api/en/materials/MeshBasicMaterial\"><code class=\"notranslate\" translate=\"no\">MeshBasicMaterial</code></a>.</p>\n<p>The <a href=\"/docs/#api/en/materials/MeshToonMaterial\"><code class=\"notranslate\" translate=\"no\">MeshToonMaterial</code></a> is similar to the <a href=\"/docs/#api/en/materials/MeshPhongMaterial\"><code class=\"notranslate\" translate=\"no\">MeshPhongMaterial</code></a>\nwith one big difference. Rather than shading smoothly it uses a gradient map\n(an X by 1 texture) to decide how to shade. The default uses a gradient map\nthat is 70% brightness for the first 70% and 100% after but you can supply your\nown gradient map. This ends up giving a 2 tone look that looks like a cartoon.</p>\n<div class=\"spread\">\n  <div data-diagram=\"MeshToonMaterial\"></div>\n</div>\n\n<p>Next up there are 2 <em>physically based rendering</em> materials. Physically Based\nRendering is often abbreviated PBR.</p>\n<p>The materials above use simple math to make materials that look 3D but they\naren't what actually happens in real world. The 2 PBR materials use much more\ncomplex math to come close to what actually happens in the real world.</p>\n<p>The first one is <a href=\"/docs/#api/en/materials/MeshStandardMaterial\"><code class=\"notranslate\" translate=\"no\">MeshStandardMaterial</code></a>. The biggest difference between\n<a href=\"/docs/#api/en/materials/MeshPhongMaterial\"><code class=\"notranslate\" translate=\"no\">MeshPhongMaterial</code></a> and <a href=\"/docs/#api/en/materials/MeshStandardMaterial\"><code class=\"notranslate\" translate=\"no\">MeshStandardMaterial</code></a> is it uses different parameters.\n<a href=\"/docs/#api/en/materials/MeshPhongMaterial\"><code class=\"notranslate\" translate=\"no\">MeshPhongMaterial</code></a> had a <code class=\"notranslate\" translate=\"no\">shininess</code> setting. <a href=\"/docs/#api/en/materials/MeshStandardMaterial\"><code class=\"notranslate\" translate=\"no\">MeshStandardMaterial</code></a> has 2\nsettings <code class=\"notranslate\" translate=\"no\">roughness</code> and <code class=\"notranslate\" translate=\"no\">metalness</code>.</p>\n<p>At a basic level <a href=\"/docs/#api/en/materials/MeshStandardMaterial#roughness\"><code class=\"notranslate\" translate=\"no\">roughness</code></a> is the opposite\nof <code class=\"notranslate\" translate=\"no\">shininess</code>. Something that has a high roughness, like a baseball doesn't\nhave hard reflections whereas something that's not rough, like a billiard ball,\nis very shiny. Roughness goes from 0 to 1.</p>\n<p>The other setting, <a href=\"/docs/#api/en/materials/MeshStandardMaterial#metalness\"><code class=\"notranslate\" translate=\"no\">metalness</code></a>, says\nhow metal the material is. Metals behave differently than non-metals. 0\nfor non-metal and 1 for metal.</p>\n<p>Here's a quick sample of <a href=\"/docs/#api/en/materials/MeshStandardMaterial\"><code class=\"notranslate\" translate=\"no\">MeshStandardMaterial</code></a> with <code class=\"notranslate\" translate=\"no\">roughness</code> from 0 to 1\nacross and <code class=\"notranslate\" translate=\"no\">metalness</code> from 0 to 1 down.</p>\n<div data-diagram=\"MeshStandardMaterial\" style=\"min-height: 400px\"></div>\n\n<p>The <a href=\"/docs/#api/en/materials/MeshPhysicalMaterial\"><code class=\"notranslate\" translate=\"no\">MeshPhysicalMaterial</code></a> is same as the <a href=\"/docs/#api/en/materials/MeshStandardMaterial\"><code class=\"notranslate\" translate=\"no\">MeshStandardMaterial</code></a> but it\nadds a <code class=\"notranslate\" translate=\"no\">clearcoat</code> parameter that goes from 0 to 1 for how much to\napply a clearcoat gloss layer and a <code class=\"notranslate\" translate=\"no\">clearCoatRoughness</code> parameter\nthat specifies how rough the gloss layer is.</p>\n<p>Here's the same grid of <code class=\"notranslate\" translate=\"no\">roughness</code> by <code class=\"notranslate\" translate=\"no\">metalness</code> as above but with\n<code class=\"notranslate\" translate=\"no\">clearcoat</code> and <code class=\"notranslate\" translate=\"no\">clearCoatRoughness</code> settings.</p>\n<div data-diagram=\"MeshPhysicalMaterial\" style=\"min-height: 400px\"></div>\n\n<p>The various standard materials progress from fastest to slowest\n<a href=\"/docs/#api/en/materials/MeshBasicMaterial\"><code class=\"notranslate\" translate=\"no\">MeshBasicMaterial</code></a> ➡ <a href=\"/docs/#api/en/materials/MeshLambertMaterial\"><code class=\"notranslate\" translate=\"no\">MeshLambertMaterial</code></a> ➡ <a href=\"/docs/#api/en/materials/MeshPhongMaterial\"><code class=\"notranslate\" translate=\"no\">MeshPhongMaterial</code></a> ➡\n<a href=\"/docs/#api/en/materials/MeshStandardMaterial\"><code class=\"notranslate\" translate=\"no\">MeshStandardMaterial</code></a> ➡ <a href=\"/docs/#api/en/materials/MeshPhysicalMaterial\"><code class=\"notranslate\" translate=\"no\">MeshPhysicalMaterial</code></a>. The slower materials\ncan make more realistic looking scenes but you might need to design\nyour code to use the faster materials on low powered or mobile machines.</p>\n<p>There are 3 materials that have special uses. <a href=\"/docs/#api/en/materials/ShadowMaterial\"><code class=\"notranslate\" translate=\"no\">ShadowMaterial</code></a>\nis used to get the data created from shadows. We haven't\ncovered shadows yet. When we do we'll use this material\nto take a peek at what's happening behind the scenes.</p>\n<p>The <a href=\"/docs/#api/en/materials/MeshDepthMaterial\"><code class=\"notranslate\" translate=\"no\">MeshDepthMaterial</code></a> renders the depth of each pixel where\npixels at negative <a href=\"/docs/#api/en/cameras/PerspectiveCamera#near\"><code class=\"notranslate\" translate=\"no\">near</code></a> of the camera are 0 and negative <a href=\"/docs/#api/en/cameras/PerspectiveCamera#far\"><code class=\"notranslate\" translate=\"no\">far</code></a> are 1. Certain special effects can use this data which we'll\nget into at another time.</p>\n<div class=\"spread\">\n  <div>\n    <div data-diagram=\"MeshDepthMaterial\"></div>\n  </div>\n</div>\n\n<p>The <a href=\"/docs/#api/en/materials/MeshNormalMaterial\"><code class=\"notranslate\" translate=\"no\">MeshNormalMaterial</code></a> will show you the <em>normals</em> of geometry.\n<em>Normals</em> are the direction a particular triangle or pixel faces.\n<a href=\"/docs/#api/en/materials/MeshNormalMaterial\"><code class=\"notranslate\" translate=\"no\">MeshNormalMaterial</code></a> draws the view space normals (the normals relative to the camera).\n<span style=\"background: red;\" class=\"color\">x is red</span>,\n<span style=\"background: green;\" class=\"dark-color\">y is green</span>, and\n<span style=\"background: blue;\" class=\"dark-color\">z is blue</span> so things facing\nto the right will be <span style=\"background: #FF7F7F;\" class=\"color\">pink</span>,\nto the left will be <span style=\"background: #007F7F;\" class=\"dark-color\">aqua</span>,\nup will be <span style=\"background: #7FFF7F;\" class=\"color\">light green</span>,\ndown will be <span style=\"background: #7F007F;\" class=\"dark-color\">purple</span>,\nand toward the screen will be <span style=\"background: #7F7FFF;\" class=\"color\">lavender</span>.</p>\n<div class=\"spread\">\n  <div>\n    <div data-diagram=\"MeshNormalMaterial\"></div>\n  </div>\n</div>\n\n<p><a href=\"/docs/#api/en/materials/ShaderMaterial\"><code class=\"notranslate\" translate=\"no\">ShaderMaterial</code></a> is for making custom materials using the three.js shader\nsystem. <a href=\"/docs/#api/en/materials/RawShaderMaterial\"><code class=\"notranslate\" translate=\"no\">RawShaderMaterial</code></a> is for making entirely custom shaders with\nno help from three.js. Both of these topics are large and will be\ncovered later.</p>\n<p>Most materials share a bunch of settings all defined by <a href=\"/docs/#api/en/materials/Material\"><code class=\"notranslate\" translate=\"no\">Material</code></a>.\n<a href=\"/docs/#api/en/materials/Material\">See the docs</a>\nfor all of them but let's go over two of the most commonly used\nproperties.</p>\n<p><a href=\"/docs/#api/en/materials/Material#flatShading\"><code class=\"notranslate\" translate=\"no\">flatShading</code></a>:\nwhether or not the object looks faceted or smooth. default = <code class=\"notranslate\" translate=\"no\">false</code>.</p>\n<div class=\"spread\">\n  <div>\n    <div data-diagram=\"smoothShading\"></div>\n    <div class=\"code\">flatShading: false</div>\n  </div>\n  <div>\n    <div data-diagram=\"flatShading\"></div>\n    <div class=\"code\">flatShading: true</div>\n  </div>\n</div>\n\n<p><a href=\"/docs/#api/en/materials/Material#side\"><code class=\"notranslate\" translate=\"no\">side</code></a>: which sides of triangles to show. The default is <code class=\"notranslate\" translate=\"no\">THREE.FrontSide</code>.\nOther options are <code class=\"notranslate\" translate=\"no\">THREE.BackSide</code> and <code class=\"notranslate\" translate=\"no\">THREE.DoubleSide</code> (both sides).\nMost 3D objects drawn in three are probably opaque solids so the back sides\n(the sides facing inside the solid) do not need to be drawn. The most common\nreason to set <code class=\"notranslate\" translate=\"no\">side</code> is for planes or other non-solid objects where it is\ncommon to see the back sides of triangles.</p>\n<p>Here are 6 planes drawn with <code class=\"notranslate\" translate=\"no\">THREE.FrontSide</code> and <code class=\"notranslate\" translate=\"no\">THREE.DoubleSide</code>.</p>\n<div class=\"spread\">\n  <div>\n    <div data-diagram=\"sideDefault\" style=\"height: 250px;\"></div>\n    <div class=\"code\">side: THREE.FrontSide</div>\n  </div>\n  <div>\n    <div data-diagram=\"sideDouble\" style=\"height: 250px;\"></div>\n    <div class=\"code\">side: THREE.DoubleSide</div>\n  </div>\n</div>\n\n<p>There's really a lot to consider with materials and we actually still\nhave a bunch more to go. In particular we've mostly ignored textures\nwhich open up a whole slew of options. Before we cover textures though\nwe need to take a break and cover\n<a href=\"setup.html\">setting up your development environment</a></p>\n<div class=\"threejs_bottombar\">\n<h3>material.needsUpdate</h3>\n<p>\nThis topic rarely affects most three.js apps but just as an FYI...\nThree.js applies material settings when a material is used where \"used\"\nmeans \"something is rendered that uses the material\". Some material settings are\nonly applied once as changing them requires lots of work by three.js.\nIn those cases you need to set <code class=\"notranslate\" translate=\"no\">material.needsUpdate = true</code> to tell\nthree.js to apply your material changes. The most common settings\nthat require you to set <code class=\"notranslate\" translate=\"no\">needsUpdate</code> if you change the settings after\nusing the material are:\n</p>\n<ul>\n  <li><code class=\"notranslate\" translate=\"no\">flatShading</code></li>\n  <li>adding or removing a texture\n    <p>\n    Changing a texture is ok, but if want to switch from using no texture\n    to using a texture or from using a texture to using no texture\n    then you need to set <code class=\"notranslate\" translate=\"no\">needsUpdate = true</code>.\n    </p>\n    <p>In the case of going from texture to no-texture it is often\n    just better to use a 1x1 pixel white texture.</p>\n  </li>\n</ul>\n<p>As mentioned above most apps never run into these issues. Most apps\ndo not switch between flat shaded and non flat shaded. Most apps also\neither use textures or a solid color for a given material, they rarely\nswitch from using one to using the other.\n</p>\n</div>\n\n<p><canvas id=\"c\"></canvas></p>\n\n\n\n        </div>\n      </div>\n    </div>\n\n  \n  \n\n\n\n\n<canvas id=\"c\" data-engine=\"three.js r185\"></canvas>",
    "success": true,
    "error": "",
    "crawled_at": 1786376809.300204,
    "is_shell": false
  },
  {
    "url": "https://threejs.org/manual/en/textures.html",
    "title": "Textures",
    "content": "<div class=\"container\">\n      <div class=\"lesson-title\">\n        <h1>Textures</h1>\n      </div>\n      <div class=\"lesson\">\n        <div class=\"lesson-main\">\n          <p>This article is one in a series of articles about three.js.\nThe first article was <a href=\"fundamentals.html\">about three.js fundamentals</a>.\nThe <a href=\"setup.html\">previous article</a> was about setting up for this article.\nIf you haven't read that yet you might want to start there.</p>\n<p>Textures are a kind of large topic in Three.js and\nI'm not 100% sure at what level to explain them but I will try.\nThere are many topics and many of them interrelate so it's hard to explain\nthem all at once. Here's quick table of contents for this article.</p>\n<ul>\n<li><a href=\"#hello\">Hello Texture</a></li>\n<li><a href=\"#six\">6 textures, a different one on each face of a cube</a></li>\n<li><a href=\"#loading\">Loading textures</a></li>\n<ul>\n  <li><a href=\"#easy\">The easy way</a></li>\n  <li><a href=\"#wait1\">Waiting for a texture to load</a></li>\n  <li><a href=\"#waitmany\">Waiting for multiple textures to load</a></li>\n  <li><a href=\"#cors\">Loading textures from other origins</a></li>\n</ul>\n<li><a href=\"#memory\">Memory usage</a></li>\n<li><a href=\"#format\">JPG vs PNG</a></li>\n<li><a href=\"#filtering-and-mips\">Filtering and mips</a></li>\n<li><a href=\"#uvmanipulation\">Repeating, offseting, rotating, wrapping</a></li>\n</ul>\n\n<h2 id=\"-a-name-hello-a-hello-texture\"><a name=\"hello\"></a> Hello Texture</h2>\n<p>Textures are <em>generally</em> images that are most often created\nin some 3rd party program like Photoshop or GIMP. For example let's\nput this image on cube.</p>\n<div class=\"threejs_center\">\n  <img src=\"../examples/resources/images/wall.jpg\" style=\"width: 600px;\" class=\"border\">\n</div>\n\n<p>We'll modify one of our first samples. All we need to do is create a <a href=\"/docs/#api/en/loaders/TextureLoader\"><code class=\"notranslate\" translate=\"no\">TextureLoader</code></a>. Call its\n<a href=\"/docs/#api/en/loaders/TextureLoader#load\"><code class=\"notranslate\" translate=\"no\">load</code></a> method with the URL of an\nimage and set the material's <code class=\"notranslate\" translate=\"no\">map</code> property to the result instead of setting its <code class=\"notranslate\" translate=\"no\">color</code>.</p>\n<pre class=\"prettyprint showlinemods notranslate lang-js\" translate=\"no\">+const loader = new THREE.TextureLoader();\n+const texture = loader.load( 'resources/images/wall.jpg' );\n+texture.colorSpace = THREE.SRGBColorSpace;\n\nconst material = new THREE.MeshBasicMaterial({\n-  color: 0xFF8844,\n+  map: texture,\n});\n</pre>\n<p>Note that we're using <a href=\"/docs/#api/en/materials/MeshBasicMaterial\"><code class=\"notranslate\" translate=\"no\">MeshBasicMaterial</code></a> so no need for any lights.</p>\n<p></p><div translate=\"no\" class=\"threejs_example_container notranslate\">\n  <div></div>\n  <a class=\"threejs_center\" href=\"/manual/examples/textured-cube.html\" target=\"_blank\">click here to open in a separate window</a>\n</div>\n\n<p></p>\n<h2 id=\"-a-name-six-a-6-textures-a-different-one-on-each-face-of-a-cube\"><a name=\"six\"></a> 6 Textures, a different one on each face of a cube</h2>\n<p>How about 6 textures, one on each face of a cube?</p>\n<div class=\"threejs_center\">\n  <div>\n    <img src=\"../examples/resources/images/flower-1.jpg\" style=\"width: 100px;\" class=\"border\">\n    <img src=\"../examples/resources/images/flower-2.jpg\" style=\"width: 100px;\" class=\"border\">\n    <img src=\"../examples/resources/images/flower-3.jpg\" style=\"width: 100px;\" class=\"border\">\n  </div>\n  <div>\n    <img src=\"../examples/resources/images/flower-4.jpg\" style=\"width: 100px;\" class=\"border\">\n    <img src=\"../examples/resources/images/flower-5.jpg\" style=\"width: 100px;\" class=\"border\">\n    <img src=\"../examples/resources/images/flower-6.jpg\" style=\"width: 100px;\" class=\"border\">\n  </div>\n</div>\n\n<p>We just make 6 materials and pass them as an array when we create the <a href=\"/docs/#api/en/objects/Mesh\"><code class=\"notranslate\" translate=\"no\">Mesh</code></a></p>\n<pre class=\"prettyprint showlinemods notranslate lang-js\" translate=\"no\">const loader = new THREE.TextureLoader();\n-const texture = loader.load( 'resources/images/wall.jpg' );\n-texture.colorSpace = THREE.SRGBColorSpace;\n\n-const material = new THREE.MeshBasicMaterial({\n-  map: texture,\n-});\n+const materials = [\n+  new THREE.MeshBasicMaterial({map: loadColorTexture('resources/images/flower-1.jpg')}),\n+  new THREE.MeshBasicMaterial({map: loadColorTexture('resources/images/flower-2.jpg')}),\n+  new THREE.MeshBasicMaterial({map: loadColorTexture('resources/images/flower-3.jpg')}),\n+  new THREE.MeshBasicMaterial({map: loadColorTexture('resources/images/flower-4.jpg')}),\n+  new THREE.MeshBasicMaterial({map: loadColorTexture('resources/images/flower-5.jpg')}),\n+  new THREE.MeshBasicMaterial({map: loadColorTexture('resources/images/flower-6.jpg')}),\n+];\n-const cube = new THREE.Mesh(geometry, material);\n+const cube = new THREE.Mesh(geometry, materials);\n\n+function loadColorTexture( path ) {\n+  const texture = loader.load( path );\n+  texture.colorSpace = THREE.SRGBColorSpace;\n+  return texture;\n+}\n</pre>\n<p>It works!</p>\n<p></p><div translate=\"no\" class=\"threejs_example_container notranslate\">\n  <div></div>\n  <a class=\"threejs_center\" href=\"/manual/examples/textured-cube-6-textures.html\" target=\"_blank\">click here to open in a separate window</a>\n</div>\n\n<p></p>\n<p>It should be noted though that not all geometry types supports multiple\nmaterials. <a href=\"/docs/#api/en/geometries/BoxGeometry\"><code class=\"notranslate\" translate=\"no\">BoxGeometry</code></a> can use 6 materials one for each face.\n<a href=\"/docs/#api/en/geometries/ConeGeometry\"><code class=\"notranslate\" translate=\"no\">ConeGeometry</code></a> can use 2 materials, one for the bottom and one for the cone.\n<a href=\"/docs/#api/en/geometries/CylinderGeometry\"><code class=\"notranslate\" translate=\"no\">CylinderGeometry</code></a> can use 3 materials, bottom, top, and side.\nFor other cases you will need to build or load custom geometry and/or modify texture coordinates.</p>\n<p>It's far more common in other 3D engines and far more performant to use a\n<a href=\"https://en.wikipedia.org/wiki/Texture_atlas\">Texture Atlas</a>\nif you want to allow multiple images on a single geometry. A Texture atlas\nis where you put multiple images in a single texture and then use texture coordinates\non the vertices of your geometry to select which parts of a texture are used on\neach triangle in your geometry.</p>\n<p>What are texture coordinates? They are data added to each vertex of a piece of geometry\nthat specify what part of the texture corresponds to that specific vertex.\nWe'll go over them when we start <a href=\"custom-buffergeometry.html\">building custom geometry</a>.</p>\n<h2 id=\"-a-name-loading-a-loading-textures\"><a name=\"loading\"></a> Loading Textures</h2>\n<h3 id=\"-a-name-easy-a-the-easy-way\"><a name=\"easy\"></a> The Easy Way</h3>\n<p>Most of the code on this site uses the easiest method of loading textures.\nWe create a <a href=\"/docs/#api/en/loaders/TextureLoader\"><code class=\"notranslate\" translate=\"no\">TextureLoader</code></a> and then call its <a href=\"/docs/#api/en/loaders/TextureLoader#load\"><code class=\"notranslate\" translate=\"no\">load</code></a> method.\nThis returns a <a href=\"/docs/#api/en/textures/Texture\"><code class=\"notranslate\" translate=\"no\">Texture</code></a> object.</p>\n<pre class=\"prettyprint showlinemods notranslate lang-js\" translate=\"no\">const texture = loader.load('resources/images/flower-1.jpg');\n</pre>\n<p>It's important to note that using this method our texture will be transparent until\nthe image is loaded asynchronously by three.js at which point it will update the texture\nwith the downloaded image.</p>\n<p>This has the big advantage that we don't have to wait for the texture to load and our\npage will start rendering immediately. That's probably okay for a great many use cases\nbut if we want we can ask three.js to tell us when the texture has finished downloading.</p>\n<h3 id=\"-a-name-wait1-a-waiting-for-a-texture-to-load\"><a name=\"wait1\"></a> Waiting for a texture to load</h3>\n<p>To wait for a texture to load the <code class=\"notranslate\" translate=\"no\">load</code> method of the texture loader takes a callback\nthat will be called when the texture has finished loading. Going back to our top example\nwe can wait for the texture to load before creating our <a href=\"/docs/#api/en/objects/Mesh\"><code class=\"notranslate\" translate=\"no\">Mesh</code></a> and adding it to scene\nlike this</p>\n<pre class=\"prettyprint showlinemods notranslate lang-js\" translate=\"no\">const loader = new THREE.TextureLoader();\nloader.load('resources/images/wall.jpg', (texture) =&gt; {\n  texture.colorSpace = THREE.SRGBColorSpace;\n  const material = new THREE.MeshBasicMaterial({\n    map: texture,\n  });\n  const cube = new THREE.Mesh(geometry, material);\n  scene.add(cube);\n  cubes.push(cube);  // add to our list of cubes to rotate\n});\n</pre>\n<p>Unless you clear your browser's cache and have a slow connection you're unlikely\nto see the any difference but rest assured it is waiting for the texture to load.</p>\n<p></p><div translate=\"no\" class=\"threejs_example_container notranslate\">\n  <div></div>\n  <a class=\"threejs_center\" href=\"/manual/examples/textured-cube-wait-for-texture.html\" target=\"_blank\">click here to open in a separate window</a>\n</div>\n\n<p></p>\n<h3 id=\"-a-name-waitmany-a-waiting-for-multiple-textures-to-load\"><a name=\"waitmany\"></a> Waiting for multiple textures to load</h3>\n<p>To wait until all textures have loaded you can use a <a href=\"/docs/#api/en/loaders/managers/LoadingManager\"><code class=\"notranslate\" translate=\"no\">LoadingManager</code></a>. Create one\nand pass it to the <a href=\"/docs/#api/en/loaders/TextureLoader\"><code class=\"notranslate\" translate=\"no\">TextureLoader</code></a> then set its  <a href=\"/docs/#api/en/loaders/managers/LoadingManager#onLoad\"><code class=\"notranslate\" translate=\"no\">onLoad</code></a>\nproperty to a callback.</p>\n<pre class=\"prettyprint showlinemods notranslate lang-js\" translate=\"no\">+const loadManager = new THREE.LoadingManager();\n*const loader = new THREE.TextureLoader(loadManager);\n\nconst materials = [\n  new THREE.MeshBasicMaterial({map: loader.load('resources/images/flower-1.jpg')}),\n  new THREE.MeshBasicMaterial({map: loader.load('resources/images/flower-2.jpg')}),\n  new THREE.MeshBasicMaterial({map: loader.load('resources/images/flower-3.jpg')}),\n  new THREE.MeshBasicMaterial({map: loader.load('resources/images/flower-4.jpg')}),\n  new THREE.MeshBasicMaterial({map: loader.load('resources/images/flower-5.jpg')}),\n  new THREE.MeshBasicMaterial({map: loader.load('resources/images/flower-6.jpg')}),\n];\n\n+loadManager.onLoad = () =&gt; {\n+  const cube = new THREE.Mesh(geometry, materials);\n+  scene.add(cube);\n+  cubes.push(cube);  // add to our list of cubes to rotate\n+};\n</pre>\n<p>The <a href=\"/docs/#api/en/loaders/managers/LoadingManager\"><code class=\"notranslate\" translate=\"no\">LoadingManager</code></a> also has an <a href=\"/docs/#api/en/loaders/managers/LoadingManager#onProgress\"><code class=\"notranslate\" translate=\"no\">onProgress</code></a> property\nwe can set to another callback to show a progress indicator.</p>\n<p>First we'll add a progress bar in HTML</p>\n<pre class=\"prettyprint showlinemods notranslate lang-html\" translate=\"no\">&lt;body&gt;\n  &lt;canvas id=\"c\"&gt;&lt;/canvas&gt;\n+  &lt;div id=\"loading\"&gt;\n+    &lt;div class=\"progress\"&gt;&lt;div class=\"progressbar\"&gt;&lt;/div&gt;&lt;/div&gt;\n+  &lt;/div&gt;\n&lt;/body&gt;\n</pre>\n<p>and the CSS for it</p>\n<pre class=\"prettyprint showlinemods notranslate lang-css\" translate=\"no\">#loading {\n    position: fixed;\n    top: 0;\n    left: 0;\n    width: 100%;\n    height: 100%;\n    display: flex;\n    justify-content: center;\n    align-items: center;\n}\n#loading .progress {\n    margin: 1.5em;\n    border: 1px solid white;\n    width: 50vw;\n}\n#loading .progressbar {\n    margin: 2px;\n    background: white;\n    height: 1em;\n    transform-origin: top left;\n    transform: scaleX(0);\n}\n</pre>\n<p>Then in the code we'll update the scale of the <code class=\"notranslate\" translate=\"no\">progressbar</code> in our <code class=\"notranslate\" translate=\"no\">onProgress</code> callback. It gets\ncalled with the URL of the last item loaded, the number of items loaded so far, and the total\nnumber of items loaded.</p>\n<pre class=\"prettyprint showlinemods notranslate lang-js\" translate=\"no\">+const loadingElem = document.querySelector('#loading');\n+const progressBarElem = loadingElem.querySelector('.progressbar');\n\nloadManager.onLoad = () =&gt; {\n+  loadingElem.style.display = 'none';\n  const cube = new THREE.Mesh(geometry, materials);\n  scene.add(cube);\n  cubes.push(cube);  // add to our list of cubes to rotate\n};\n\n+loadManager.onProgress = (urlOfLastItemLoaded, itemsLoaded, itemsTotal) =&gt; {\n+  const progress = itemsLoaded / itemsTotal;\n+  progressBarElem.style.transform = <code class=\"notranslate\" translate=\"no\">scaleX(${progress})</code>;\n+};\n</pre>\n<p>Unless you clear your cache and have a slow connection you might not see\nthe loading bar.</p>\n<p></p><div translate=\"no\" class=\"threejs_example_container notranslate\">\n  <div></div>\n  <a class=\"threejs_center\" href=\"/manual/examples/textured-cube-wait-for-all-textures.html\" target=\"_blank\">click here to open in a separate window</a>\n</div>\n\n<p></p>\n<h2 id=\"-a-name-cors-a-loading-textures-from-other-origins\"><a name=\"cors\"></a> Loading textures from other origins</h2>\n<p>To use images from other servers those servers need to send the correct headers.\nIf they don't you cannot use the images in three.js and will get an error.\nIf you run the server providing the images make sure it\n<a href=\"https://developer.mozilla.org/en-US/docs/Web/HTTP/CORS\">sends the correct headers</a>.\nIf you don't control the server hosting the images and it does not send the\npermission headers then you can't use the images from that server.</p>\n<p>For example <a href=\"https://imgur.com\">imgur</a>, <a href=\"https://flickr.com\">flickr</a>, and\n<a href=\"https://github.com\">github</a> all send headers allowing you to use images\nhosted on their servers in three.js. Most other websites do not.</p>\n<h2 id=\"-a-name-memory-a-memory-usage\"><a name=\"memory\"></a> Memory Usage</h2>\n<p>Textures are often the part of a three.js app that use the most memory. It's important to understand\nthat <em>in general</em>, textures take <code class=\"notranslate\" translate=\"no\">width * height * 4 * 1.33</code> bytes of memory.</p>\n<p>Notice that says nothing about compression. I can make a .jpg image and set its compression super\nhigh. For example let's say I was making a scene of a house. Inside the house there is a table\nand I decide to put this wood texture on the top surface of the table</p>\n<div class=\"threejs_center\"><img class=\"border\" src=\"../resources/images/compressed-but-large-wood-texture.jpg\" align=\"center\" style=\"width: 300px\"></div>\n\n<p>That image is only 157k so it will download relatively quickly but <a href=\"resources/images/compressed-but-large-wood-texture.jpg\">it is actually\n3024 x 3761 pixels in size</a>.\nFollowing the equation above that's</p>\n<pre class=\"prettyprint showlinemods notranslate notranslate\" translate=\"no\">3024 * 3761 * 4 * 1.33 = 60505764.5\n</pre><p>That image will take <strong>60 MEG OF MEMORY!</strong> in three.js.\nA few textures like that and you'll be out of memory.</p>\n<p>I bring this up because it's important to know that using textures has a hidden cost.\nIn order for three.js to use the texture it has to hand it off to the GPU and the\nGPU <em>in general</em> requires the texture data to be uncompressed.</p>\n<p>The moral of the story is make your textures small in dimensions not just small\nin file size. Small in file size = fast to download. Small in dimensions = takes\nless memory. How small should you make them?\nAs small as you can and still look as good as you need them to look.</p>\n<h2 id=\"-a-name-format-a-jpg-vs-png\"><a name=\"format\"></a> JPG vs PNG</h2>\n<p>This is pretty much the same as regular HTML in that JPGs have lossy compression,\nPNGs have lossless compression so PNGs are generally slower to download.\nBut, PNGs support transparency. PNGs are also probably the appropriate format\nfor non-image data like normal maps, and other kinds of non-image maps which we'll go over later.</p>\n<p>It's important to remember that a JPG doesn't use\nless memory than a PNG in WebGL. See above.</p>\n<h2 id=\"-a-name-filtering-and-mips-a-filtering-and-mips\"><a name=\"filtering-and-mips\"></a> Filtering and Mips</h2>\n<p>Let's apply this 16x16 texture</p>\n<div class=\"threejs_center\"><img src=\"../resources/images/mip-low-res-enlarged.png\" class=\"nobg\" align=\"center\"></div>\n\n<p>To a cube</p>\n<div class=\"spread\"><div data-diagram=\"filterCube\"></div></div>\n\n<p>Let's draw that cube really small</p>\n<div class=\"spread\"><div data-diagram=\"filterCubeSmall\"></div></div>\n\n<p>Hmmm, I guess that's hard to see. Let's magnify that tiny cube</p>\n<div class=\"spread\"><div data-diagram=\"filterCubeSmallLowRes\"></div></div>\n\n<p>How does the GPU know which colors to make each pixel it's drawing for the tiny cube?\nWhat if the cube was so small that it's just 1 or 2 pixels?</p>\n<p>This is what filtering is about.</p>\n<p>If it was Photoshop, Photoshop would average nearly all the pixels together to figure out what color\nto make those 1 or 2 pixels. That would be a very slow operation. GPUs solve this issue\nusing mipmaps.</p>\n<p>Mips are copies of the texture, each one half as wide and half as tall as the previous\nmip where the pixels have been blended to make the next smaller mip. Mips are created\nuntil we get all the way to a 1x1 pixel mip. For the image above all of the mips would\nend up being something like this</p>\n<div class=\"threejs_center\"><img src=\"../resources/images/mipmap-low-res-enlarged.png\" class=\"nobg\" align=\"center\"></div>\n\n<p>Now, when the cube is drawn so small that it's only 1 or 2 pixels large the GPU can choose\nto use just the smallest or next to smallest mip level to decide what color to make the\ntiny cube.</p>\n<p>In three.js you can choose what happens both when the texture is drawn\nlarger than its original size and what happens when it's drawn smaller than its\noriginal size.</p>\n<p>For setting the filter when the texture is drawn larger than its original size\nyou set <a href=\"/docs/#api/en/textures/Texture#magFilter\"><code class=\"notranslate\" translate=\"no\">texture.magFilter</code></a> property to either <code class=\"notranslate\" translate=\"no\">THREE.NearestFilter</code> or\n <code class=\"notranslate\" translate=\"no\">THREE.LinearFilter</code>.  <code class=\"notranslate\" translate=\"no\">NearestFilter</code> means\njust pick the closet single pixel from the original texture. With a low\nresolution texture this gives you a very pixelated look like Minecraft.</p>\n<p><code class=\"notranslate\" translate=\"no\">LinearFilter</code> means choose the 4 pixels from the texture that are closest\nto the where we should be choosing a color from and blend them in the\nappropriate proportions relative to how far away the actual point is from\neach of the 4 pixels.</p>\n<div class=\"spread\">\n  <div>\n    <div data-diagram=\"filterCubeMagNearest\" style=\"height: 250px;\"></div>\n    <div class=\"code\">Nearest</div>\n  </div>\n  <div>\n    <div data-diagram=\"filterCubeMagLinear\" style=\"height: 250px;\"></div>\n    <div class=\"code\">Linear</div>\n  </div>\n</div>\n\n<p>For setting the filter when the texture is drawn smaller than its original size\nyou set the <a href=\"/docs/#api/en/textures/Texture#minFilter\"><code class=\"notranslate\" translate=\"no\">texture.minFilter</code></a> property to one of 6 values.</p>\n<ul>\n<li><p><code class=\"notranslate\" translate=\"no\">THREE.NearestFilter</code></p>\n<p> same as above, choose the closest pixel in the texture</p>\n</li>\n<li><p><code class=\"notranslate\" translate=\"no\">THREE.LinearFilter</code></p>\n<p> same as above, choose 4 pixels from the texture and blend them</p>\n</li>\n<li><p><code class=\"notranslate\" translate=\"no\">THREE.NearestMipmapNearestFilter</code></p>\n<p> choose the appropriate mip then choose one pixel</p>\n</li>\n<li><p><code class=\"notranslate\" translate=\"no\">THREE.NearestMipmapLinearFilter</code></p>\n<p> choose 2 mips, choose one pixel from each, blend the 2 pixels</p>\n</li>\n<li><p><code class=\"notranslate\" translate=\"no\">THREE.LinearMipmapNearestFilter</code></p>\n<p> chose the appropriate mip then choose 4 pixels and blend them</p>\n</li>\n<li><p><code class=\"notranslate\" translate=\"no\">THREE.LinearMipmapLinearFilter</code></p>\n<p>choose 2 mips, choose 4 pixels from each and blend all 8 into 1 pixel</p>\n</li>\n</ul>\n<p>Here's an example showing all 6 settings</p>\n<div class=\"spread\">\n  <div data-diagram=\"filterModes\" style=\"\n    height: 450px;\n    position: relative;\n  \">\n    <div style=\"\n      width: 100%;\n      height: 100%;\n      display: flex;\n      align-items: center;\n      justify-content: flex-start;\n    \">\n      <div style=\"\n        background: rgba(255,0,0,.8);\n        color: white;\n        padding: .5em;\n        margin: 1em;\n        font-size: small;\n        border-radius: .5em;\n        line-height: 1.2;\n        user-select: none;\">click to<br>change<br>texture</div>\n    </div>\n    <div class=\"filter-caption\" style=\"left: 0.5em; top: 0.5em;\">nearest</div>\n    <div class=\"filter-caption\" style=\"width: 100%; text-align: center; top: 0.5em;\">linear</div>\n    <div class=\"filter-caption\" style=\"right: 0.5em; text-align: right; top: 0.5em;\">nearest<br>mipmap<br>nearest</div>\n    <div class=\"filter-caption\" style=\"left: 0.5em; text-align: left; bottom: 0.5em;\">nearest<br>mipmap<br>linear</div>\n    <div class=\"filter-caption\" style=\"width: 100%; text-align: center; bottom: 0.5em;\">linear<br>mipmap<br>nearest</div>\n    <div class=\"filter-caption\" style=\"right: 0.5em; text-align: right; bottom: 0.5em;\">linear<br>mipmap<br>linear</div>\n  </div>\n</div>\n\n<p>One thing to notice is the top left and top middle using <code class=\"notranslate\" translate=\"no\">NearestFilter</code> and <code class=\"notranslate\" translate=\"no\">LinearFilter</code>\ndon't use the mips. Because of that they flicker in the distance because the GPU is\npicking pixels from the original texture. On the left just one pixel is chosen and\nin the middle 4 are chosen and blended but it's not enough come up with a good\nrepresentative color. The other 4 strips do better with the bottom right,\n<code class=\"notranslate\" translate=\"no\">LinearMipmapLinearFilter</code> being best.</p>\n<p>If you click the picture above it will toggle between the texture we've been using above\nand a texture where every mip level is a different color.</p>\n<div class=\"threejs_center\">\n  <div data-texture-diagram=\"differentColoredMips\"></div>\n</div>\n\n<p>This makes it more clear\nwhat is happening. You can see in the top left and top middle the first mip is used all the way\ninto the distance. The top right and bottom middle you can clearly see where a different mip\nis used.</p>\n<p>Switching back to the original texture you can see the bottom right is the smoothest,\nhighest quality. You might ask why not always use that mode. The most obvious reason\nis sometimes you want things to be pixelated for a retro look or some other reason.\nThe next most common reason is that reading 8 pixels and blending them is slower\nthan reading 1 pixel and blending. While it's unlikely that a single texture is going\nto be the difference between fast and slow as we progress further into these articles\nwe'll eventually have materials that use 4 or 5 textures all at once. 4 textures * 8\npixels per texture is looking up 32 pixels for ever pixel rendered.\nThis can be especially important to consider on mobile devices.</p>\n<h2 id=\"-a-name-uvmanipulation-a-repeating-offseting-rotating-wrapping-a-texture\"><a name=\"uvmanipulation\"></a> Repeating, offseting, rotating, wrapping a texture</h2>\n<p>Textures have settings for repeating, offseting, and rotating a texture.</p>\n<p>By default textures in three.js do not repeat. To set whether or not a\ntexture repeats there are 2 properties, <a href=\"/docs/#api/en/textures/Texture#wrapS\"><code class=\"notranslate\" translate=\"no\">wrapS</code></a> for horizontal wrapping\nand <a href=\"/docs/#api/en/textures/Texture#wrapT\"><code class=\"notranslate\" translate=\"no\">wrapT</code></a> for vertical wrapping.</p>\n<p>They can be set to one of:</p>\n<ul>\n<li><p><code class=\"notranslate\" translate=\"no\">THREE.ClampToEdgeWrapping</code></p>\n<p> the last pixel on each edge is repeated forever</p>\n</li>\n<li><p><code class=\"notranslate\" translate=\"no\">THREE.RepeatWrapping</code></p>\n<p> the texture is repeated</p>\n</li>\n<li><p><code class=\"notranslate\" translate=\"no\">THREE.MirroredRepeatWrapping</code></p>\n<p> the texture is mirrored and repeated</p>\n</li>\n</ul>\n<p>For example to turn on wrapping in both directions:</p>\n<pre class=\"prettyprint showlinemods notranslate lang-js\" translate=\"no\">someTexture.wrapS = THREE.RepeatWrapping;\nsomeTexture.wrapT = THREE.RepeatWrapping;\n</pre>\n<p>Repeating is set with the [repeat] repeat property.</p>\n<pre class=\"prettyprint showlinemods notranslate lang-js\" translate=\"no\">const timesToRepeatHorizontally = 4;\nconst timesToRepeatVertically = 2;\nsomeTexture.repeat.set(timesToRepeatHorizontally, timesToRepeatVertically);\n</pre>\n<p>Offseting the texture can be done by setting the <code class=\"notranslate\" translate=\"no\">offset</code> property. Textures\nare offset with units where 1 unit = 1 texture size. On other words 0 = no offset\nand 1 = offset one full texture amount.</p>\n<pre class=\"prettyprint showlinemods notranslate lang-js\" translate=\"no\">const xOffset = .5;   // offset by half the texture\nconst yOffset = .25;  // offset by 1/4 the texture\nsomeTexture.offset.set(xOffset, yOffset);\n</pre>\n<p>Rotating the texture can be set by setting the <code class=\"notranslate\" translate=\"no\">rotation</code> property in radians\nas well as the <code class=\"notranslate\" translate=\"no\">center</code> property for choosing the center of rotation.\nIt defaults to 0,0 which rotates from the bottom left corner. Like offset\nthese units are in texture size so setting them to <code class=\"notranslate\" translate=\"no\">.5, .5</code> would rotate\naround the center of the texture.</p>\n<pre class=\"prettyprint showlinemods notranslate lang-js\" translate=\"no\">someTexture.center.set(.5, .5);\nsomeTexture.rotation = THREE.MathUtils.degToRad(45);\n</pre>\n<p>Let's modify the top sample above to play with these values</p>\n<p>First we'll keep a reference to the texture so we can manipulate it</p>\n<pre class=\"prettyprint showlinemods notranslate lang-js\" translate=\"no\">+const texture = loader.load('resources/images/wall.jpg');\nconst material = new THREE.MeshBasicMaterial({\n-  map: loader.load('resources/images/wall.jpg');\n+  map: texture,\n});\n</pre>\n<p>Then we'll use <a href=\"https://github.com/georgealways/lil-gui\">lil-gui</a> again to provide a simple interface.</p>\n<pre class=\"prettyprint showlinemods notranslate lang-js\" translate=\"no\">import {GUI} from 'three/addons/libs/lil-gui.module.min.js';\n</pre>\n<p>As we did in previous lil-gui examples we'll use a simple class to\ngive lil-gui an object that it can manipulate in degrees\nbut that will set a property in radians.</p>\n<pre class=\"prettyprint showlinemods notranslate lang-js\" translate=\"no\">class DegRadHelper {\n  constructor(obj, prop) {\n    this.obj = obj;\n    this.prop = prop;\n  }\n  get value() {\n    return THREE.MathUtils.radToDeg(this.obj[this.prop]);\n  }\n  set value(v) {\n    this.obj[this.prop] = THREE.MathUtils.degToRad(v);\n  }\n}\n</pre>\n<p>We also need a class that will convert from a string like <code class=\"notranslate\" translate=\"no\">\"123\"</code> into\na number like <code class=\"notranslate\" translate=\"no\">123</code> since three.js requires numbers for enum settings\nlike <code class=\"notranslate\" translate=\"no\">wrapS</code> and <code class=\"notranslate\" translate=\"no\">wrapT</code> but lil-gui only uses strings for enums.</p>\n<pre class=\"prettyprint showlinemods notranslate lang-js\" translate=\"no\">class StringToNumberHelper {\n  constructor(obj, prop) {\n    this.obj = obj;\n    this.prop = prop;\n  }\n  get value() {\n    return this.obj[this.prop];\n  }\n  set value(v) {\n    this.obj[this.prop] = parseFloat(v);\n  }\n}\n</pre>\n<p>Using those classes we can setup a simple GUI for the settings above</p>\n<pre class=\"prettyprint showlinemods notranslate lang-js\" translate=\"no\">const wrapModes = {\n  'ClampToEdgeWrapping': THREE.ClampToEdgeWrapping,\n  'RepeatWrapping': THREE.RepeatWrapping,\n  'MirroredRepeatWrapping': THREE.MirroredRepeatWrapping,\n};\n\nfunction updateTexture() {\n  texture.needsUpdate = true;\n}\n\nconst gui = new GUI();\ngui.add(new StringToNumberHelper(texture, 'wrapS'), 'value', wrapModes)\n  .name('texture.wrapS')\n  .onChange(updateTexture);\ngui.add(new StringToNumberHelper(texture, 'wrapT'), 'value', wrapModes)\n  .name('texture.wrapT')\n  .onChange(updateTexture);\ngui.add(texture.repeat, 'x', 0, 5, .01).name('texture.repeat.x');\ngui.add(texture.repeat, 'y', 0, 5, .01).name('texture.repeat.y');\ngui.add(texture.offset, 'x', -2, 2, .01).name('texture.offset.x');\ngui.add(texture.offset, 'y', -2, 2, .01).name('texture.offset.y');\ngui.add(texture.center, 'x', -.5, 1.5, .01).name('texture.center.x');\ngui.add(texture.center, 'y', -.5, 1.5, .01).name('texture.center.y');\ngui.add(new DegRadHelper(texture, 'rotation'), 'value', -360, 360)\n  .name('texture.rotation');\n</pre>\n<p>The last thing to note about the example is that if you change <code class=\"notranslate\" translate=\"no\">wrapS</code> or\n<code class=\"notranslate\" translate=\"no\">wrapT</code> on the texture you must also set <a href=\"/docs/#api/en/textures/Texture#needsUpdate\"><code class=\"notranslate\" translate=\"no\">texture.needsUpdate</code></a>\nso three.js knows to apply those settings. The other settings are automatically applied.</p>\n<p></p><div translate=\"no\" class=\"threejs_example_container notranslate\">\n  <div></div>\n  <a class=\"threejs_center\" href=\"/manual/examples/textured-cube-adjust.html\" target=\"_blank\">click here to open in a separate window</a>\n</div>\n\n<p></p>\n<p>This is only one step into the topic of textures. At some point we'll go over\ntexture coordinates as well as 9 other types of textures that can be applied\nto materials.</p>\n<p>For now let's move on to <a href=\"lights.html\">lights</a>.</p>\n<!--\nalpha\nao\nenv\nlight\nspecular\nbumpmap ?\nnormalmap ?\nmetalness\nroughness\n-->\n<p><link rel=\"stylesheet\" href=\"../resources/threejs-textures.css\"></p>\n\n\n        </div>\n      </div>\n    </div>\n\n  \n  \n\n\n\n\n<canvas id=\"c\" data-engine=\"three.js r185\"></canvas>",
    "success": true,
    "error": "",
    "crawled_at": 1786376809.300204,
    "is_shell": false
  },
  {
    "url": "https://threejs.org/manual/en/lights.html",
    "title": "Lights",
    "content": "<div class=\"container\">\n      <div class=\"lesson-title\">\n        <h1>Lights</h1>\n      </div>\n      <div class=\"lesson\">\n        <div class=\"lesson-main\">\n          <p>This article is part of a series of articles about three.js. The\nfirst article is <a href=\"fundamentals.html\">three.js fundamentals</a>. If\nyou haven't read that yet and you're new to three.js you might want to\nconsider starting there and also the article on <a href=\"setup.html\">setting up your environment</a>. The\n<a href=\"textures.html\">previous article was about textures</a>.</p>\n<p>Let's go over how to use the various kinds of lights in three.</p>\n<p>Starting with one of our previous samples let's update the camera.\nWe'll set the field of view to 45 degrees, the far plane to 100 units,\nand we'll move the camera 10 units up and 20 units back from the origin</p>\n<pre class=\"prettyprint showlinemods notranslate lang-js\" translate=\"no\">*const fov = 45;\nconst aspect = 2;  // the canvas default\nconst near = 0.1;\n*const far = 100;\nconst camera = new THREE.PerspectiveCamera(fov, aspect, near, far);\n+camera.position.set(0, 10, 20);\n</pre>\n<p>Next let's add <a href=\"/docs/#examples/en/controls/OrbitControls\" target=\"_blank\"><code class=\"notranslate\" translate=\"no\">OrbitControls</code></a>. <a href=\"/docs/#examples/en/controls/OrbitControls\" target=\"_blank\"><code class=\"notranslate\" translate=\"no\">OrbitControls</code></a> let the user spin\nor <em>orbit</em> the camera around some point. The <a href=\"/docs/#examples/en/controls/OrbitControls\" target=\"_blank\"><code class=\"notranslate\" translate=\"no\">OrbitControls</code></a> are\nan optional feature of three.js so first we need to include them\nin our page</p>\n<pre class=\"prettyprint showlinemods notranslate lang-js\" translate=\"no\">import * as THREE from 'three';\n+import {OrbitControls} from 'three/addons/controls/OrbitControls.js';\n</pre>\n<p>Then we can use them. We pass the <a href=\"/docs/#examples/en/controls/OrbitControls\" target=\"_blank\"><code class=\"notranslate\" translate=\"no\">OrbitControls</code></a> a camera to\ncontrol and the DOM element to use to get input events</p>\n<pre class=\"prettyprint showlinemods notranslate lang-js\" translate=\"no\">const controls = new OrbitControls(camera, canvas);\ncontrols.target.set(0, 5, 0);\ncontrols.update();\n</pre>\n<p>We also set the target to orbit around to 5 units above the origin\nand then call <code class=\"notranslate\" translate=\"no\">controls.update</code> so the controls will use the new\ntarget.</p>\n<p>Next up let's make some things to light up. First we'll make ground\nplane. We'll apply a tiny 2x2 pixel checkerboard texture that looks\nlike this</p>\n<div class=\"threejs_center\">\n  <img src=\"../examples/resources/images/checker.png\" class=\"border\" style=\"\n    image-rendering: pixelated;\n    width: 128px;\n  \">\n</div>\n\n<p>First we load the texture, set it to repeating, set the filtering to\nnearest, and set how many times we want it to repeat. Since the\ntexture is a 2x2 pixel checkerboard, by repeating and setting the\nrepeat to half the size of the plane each check on the checkerboard\nwill be exactly 1 unit large;</p>\n<pre class=\"prettyprint showlinemods notranslate lang-js\" translate=\"no\">const planeSize = 40;\n\nconst loader = new THREE.TextureLoader();\nconst texture = loader.load('resources/images/checker.png');\ntexture.wrapS = THREE.RepeatWrapping;\ntexture.wrapT = THREE.RepeatWrapping;\ntexture.magFilter = THREE.NearestFilter;\ntexture.colorSpace = THREE.SRGBColorSpace;\nconst repeats = planeSize / 2;\ntexture.repeat.set(repeats, repeats);\n</pre>\n<p>We then make a plane geometry, a material for the plane, and a mesh\nto insert it in the scene. Planes default to being in the XY plane\nbut the ground is in the XZ plane so we rotate it.</p>\n<pre class=\"prettyprint showlinemods notranslate lang-js\" translate=\"no\">const planeGeo = new THREE.PlaneGeometry(planeSize, planeSize);\nconst planeMat = new THREE.MeshPhongMaterial({\n  map: texture,\n  side: THREE.DoubleSide,\n});\nconst mesh = new THREE.Mesh(planeGeo, planeMat);\nmesh.rotation.x = Math.PI * -.5;\nscene.add(mesh);\n</pre>\n<p>Let's add a cube and a sphere so we have 3 things to light including the plane</p>\n<pre class=\"prettyprint showlinemods notranslate lang-js\" translate=\"no\">{\n  const cubeSize = 4;\n  const cubeGeo = new THREE.BoxGeometry(cubeSize, cubeSize, cubeSize);\n  const cubeMat = new THREE.MeshPhongMaterial({color: '#8AC'});\n  const mesh = new THREE.Mesh(cubeGeo, cubeMat);\n  mesh.position.set(cubeSize + 1, cubeSize / 2, 0);\n  scene.add(mesh);\n}\n{\n  const sphereRadius = 3;\n  const sphereWidthDivisions = 32;\n  const sphereHeightDivisions = 16;\n  const sphereGeo = new THREE.SphereGeometry(sphereRadius, sphereWidthDivisions, sphereHeightDivisions);\n  const sphereMat = new THREE.MeshPhongMaterial({color: '#CA8'});\n  const mesh = new THREE.Mesh(sphereGeo, sphereMat);\n  mesh.position.set(-sphereRadius - 1, sphereRadius + 2, 0);\n  scene.add(mesh);\n}\n</pre>\n<p>Now that we have a scene to light up let's add lights!</p>\n<h2 id=\"-ambientlight-\"><a href=\"/docs/#api/en/lights/AmbientLight\"><code class=\"notranslate\" translate=\"no\">AmbientLight</code></a></h2>\n<p>First let's make an <a href=\"/docs/#api/en/lights/AmbientLight\"><code class=\"notranslate\" translate=\"no\">AmbientLight</code></a></p>\n<pre class=\"prettyprint showlinemods notranslate lang-js\" translate=\"no\">const color = 0xFFFFFF;\nconst intensity = 1;\nconst light = new THREE.AmbientLight(color, intensity);\nscene.add(light);\n</pre>\n<p>Let's also make it so we can adjust the light's parameters.\nWe'll use <a href=\"https://github.com/georgealways/lil-gui\">lil-gui</a> again.\nTo be able to adjust the color via lil-gui we need a small helper\nthat presents a property to lil-gui that looks like a CSS hex color string\n(eg: <code class=\"notranslate\" translate=\"no\">#FF8844</code>). Our helper will get the color from a named property,\nconvert it to a hex string to offer to lil-gui. When lil-gui tries\nto set the helper's property we'll assign the result back to the light's\ncolor.</p>\n<p>Here's the helper:</p>\n<pre class=\"prettyprint showlinemods notranslate lang-js\" translate=\"no\">class ColorGUIHelper {\n  constructor(object, prop) {\n    this.object = object;\n    this.prop = prop;\n  }\n  get value() {\n    return '#' + this.object[this.prop].getHexString();\n  }\n  set value(hexString) {\n    this.object[this.prop].set(hexString);\n  }\n}\n</pre>\n<p>And here's our code setting up lil-gui</p>\n<pre class=\"prettyprint showlinemods notranslate lang-js\" translate=\"no\">const gui = new GUI();\ngui.addColor(new ColorGUIHelper(light, 'color'), 'value').name('color');\ngui.add(light, 'intensity', 0, 5, 0.01);\n</pre>\n<p>And here's the result</p>\n<p></p><div translate=\"no\" class=\"threejs_example_container notranslate\">\n  <div></div>\n  <a class=\"threejs_center\" href=\"/manual/examples/lights-ambient.html\" target=\"_blank\">click here to open in a separate window</a>\n</div>\n\n<p></p>\n<p>Click and drag in the scene to <em>orbit</em> the camera.</p>\n<p>Notice there is no definition. The shapes are flat. The <a href=\"/docs/#api/en/lights/AmbientLight\"><code class=\"notranslate\" translate=\"no\">AmbientLight</code></a> effectively\njust multiplies the material's color by the light's color times the\nintensity.</p>\n<pre class=\"prettyprint showlinemods notranslate notranslate\" translate=\"no\">color = materialColor * light.color * light.intensity;\n</pre><p>That's it. It has no direction.\nThis style of ambient lighting is actually not all that\nuseful as lighting as it's 100% even so other than changing the color\nof everything in the scene it doesn't look much like <em>lighting</em>.\nWhat it does help with is making the darks not too dark.</p>\n<h2 id=\"-hemispherelight-\"><a href=\"/docs/#api/en/lights/HemisphereLight\"><code class=\"notranslate\" translate=\"no\">HemisphereLight</code></a></h2>\n<p>Let's switch the code to a <a href=\"/docs/#api/en/lights/HemisphereLight\"><code class=\"notranslate\" translate=\"no\">HemisphereLight</code></a>. A <a href=\"/docs/#api/en/lights/HemisphereLight\"><code class=\"notranslate\" translate=\"no\">HemisphereLight</code></a>\ntakes a sky color and a ground color and just multiplies the\nmaterial's color between those 2 colors—the sky color if the\nsurface of the object is pointing up and the ground color if\nthe surface of the object is pointing down.</p>\n<p>Here's the new code</p>\n<pre class=\"prettyprint showlinemods notranslate lang-js\" translate=\"no\">-const color = 0xFFFFFF;\n+const skyColor = 0xB1E1FF;  // light blue\n+const groundColor = 0xB97A20;  // brownish orange\nconst intensity = 1;\n-const light = new THREE.AmbientLight(color, intensity);\n+const light = new THREE.HemisphereLight(skyColor, groundColor, intensity);\nscene.add(light);\n</pre>\n<p>Let's also update the lil-gui code to edit both colors</p>\n<pre class=\"prettyprint showlinemods notranslate lang-js\" translate=\"no\">const gui = new GUI();\n-gui.addColor(new ColorGUIHelper(light, 'color'), 'value').name('color');\n+gui.addColor(new ColorGUIHelper(light, 'color'), 'value').name('skyColor');\n+gui.addColor(new ColorGUIHelper(light, 'groundColor'), 'value').name('groundColor');\ngui.add(light, 'intensity', 0, 5, 0.01);\n</pre>\n<p>The result:</p>\n<p></p><div translate=\"no\" class=\"threejs_example_container notranslate\">\n  <div></div>\n  <a class=\"threejs_center\" href=\"/manual/examples/lights-hemisphere.html\" target=\"_blank\">click here to open in a separate window</a>\n</div>\n\n<p></p>\n<p>Notice again there is almost no definition, everything looks kind\nof flat. The <a href=\"/docs/#api/en/lights/HemisphereLight\"><code class=\"notranslate\" translate=\"no\">HemisphereLight</code></a> used in combination with another light\ncan help give a nice kind of influence of the color of the sky\nand ground. In that way it's best used in combination with some\nother light or a substitute for an <a href=\"/docs/#api/en/lights/AmbientLight\"><code class=\"notranslate\" translate=\"no\">AmbientLight</code></a>.</p>\n<h2 id=\"-directionallight-\"><a href=\"/docs/#api/en/lights/DirectionalLight\"><code class=\"notranslate\" translate=\"no\">DirectionalLight</code></a></h2>\n<p>Let's switch the code to a <a href=\"/docs/#api/en/lights/DirectionalLight\"><code class=\"notranslate\" translate=\"no\">DirectionalLight</code></a>.\nA <a href=\"/docs/#api/en/lights/DirectionalLight\"><code class=\"notranslate\" translate=\"no\">DirectionalLight</code></a> is often used to represent the sun.</p>\n<pre class=\"prettyprint showlinemods notranslate lang-js\" translate=\"no\">const color = 0xFFFFFF;\nconst intensity = 1;\nconst light = new THREE.DirectionalLight(color, intensity);\nlight.position.set(0, 10, 0);\nlight.target.position.set(-5, 0, 0);\nscene.add(light);\nscene.add(light.target);\n</pre>\n<p>Notice that we had to add the <code class=\"notranslate\" translate=\"no\">light</code> and the <code class=\"notranslate\" translate=\"no\">light.target</code>\nto the scene. A three.js <a href=\"/docs/#api/en/lights/DirectionalLight\"><code class=\"notranslate\" translate=\"no\">DirectionalLight</code></a> will shine\nin the direction of its target.</p>\n<p>Let's make it so we can move the target by adding it to\nour GUI.</p>\n<pre class=\"prettyprint showlinemods notranslate lang-js\" translate=\"no\">const gui = new GUI();\ngui.addColor(new ColorGUIHelper(light, 'color'), 'value').name('color');\ngui.add(light, 'intensity', 0, 5, 0.01);\ngui.add(light.target.position, 'x', -10, 10);\ngui.add(light.target.position, 'z', -10, 10);\ngui.add(light.target.position, 'y', 0, 10);\n</pre>\n<p></p><div translate=\"no\" class=\"threejs_example_container notranslate\">\n  <div></div>\n  <a class=\"threejs_center\" href=\"/manual/examples/lights-directional.html\" target=\"_blank\">click here to open in a separate window</a>\n</div>\n\n<p></p>\n<p>It's kind of hard to see what's going on. Three.js has a bunch\nof helper objects we can add to our scene to help visualize\ninvisible parts of a scene. In this case we'll use the\n<a href=\"/docs/#api/en/helpers/DirectionalLightHelper\"><code class=\"notranslate\" translate=\"no\">DirectionalLightHelper</code></a> which will draw a plane, to represent\nthe light, and a line from the light to the target. We just\npass it the light and add it to the scene.</p>\n<pre class=\"prettyprint showlinemods notranslate lang-js\" translate=\"no\">const helper = new THREE.DirectionalLightHelper(light);\nscene.add(helper);\n</pre>\n<p>While we're at it let's make it so we can set both the position\nof the light and the target. To do this we'll make a function\nthat given a <a href=\"/docs/#api/en/math/Vector3\"><code class=\"notranslate\" translate=\"no\">Vector3</code></a> will adjust its <code class=\"notranslate\" translate=\"no\">x</code>, <code class=\"notranslate\" translate=\"no\">y</code>, and <code class=\"notranslate\" translate=\"no\">z</code> properties\nusing <code class=\"notranslate\" translate=\"no\">lil-gui</code>.</p>\n<pre class=\"prettyprint showlinemods notranslate lang-js\" translate=\"no\">function makeXYZGUI(gui, vector3, name, onChangeFn) {\n  const folder = gui.addFolder(name);\n  folder.add(vector3, 'x', -10, 10).onChange(onChangeFn);\n  folder.add(vector3, 'y', 0, 10).onChange(onChangeFn);\n  folder.add(vector3, 'z', -10, 10).onChange(onChangeFn);\n  folder.open();\n}\n</pre>\n<p>Note that we need to call the helper's <code class=\"notranslate\" translate=\"no\">update</code> function\nanytime we change something so the helper knows to update\nitself. As such we pass in an <code class=\"notranslate\" translate=\"no\">onChangeFn</code> function to\nget called anytime lil-gui updates a value.</p>\n<p>Then we can use that for both the light's position\nand the target's position like this</p>\n<pre class=\"prettyprint showlinemods notranslate lang-js\" translate=\"no\">+function updateLight() {\n+  light.target.updateMatrixWorld();\n+  helper.update();\n+}\n+updateLight();\n\nconst gui = new GUI();\ngui.addColor(new ColorGUIHelper(light, 'color'), 'value').name('color');\ngui.add(light, 'intensity', 0, 5, 0.01);\n\n+makeXYZGUI(gui, light.position, 'position', updateLight);\n+makeXYZGUI(gui, light.target.position, 'target', updateLight);\n</pre>\n<p>Now we can move the light, and its target</p>\n<p></p><div translate=\"no\" class=\"threejs_example_container notranslate\">\n  <div></div>\n  <a class=\"threejs_center\" href=\"/manual/examples/lights-directional-w-helper.html\" target=\"_blank\">click here to open in a separate window</a>\n</div>\n\n<p></p>\n<p>Orbit the camera and it gets easier to see. The plane\nrepresents a <a href=\"/docs/#api/en/lights/DirectionalLight\"><code class=\"notranslate\" translate=\"no\">DirectionalLight</code></a> because a directional\nlight computes light coming in one direction. There is no\n<em>point</em> the light comes from, it's an infinite plane of light\nshooting out parallel rays of light.</p>\n<h2 id=\"-pointlight-\"><a href=\"/docs/#api/en/lights/PointLight\"><code class=\"notranslate\" translate=\"no\">PointLight</code></a></h2>\n<p>A <a href=\"/docs/#api/en/lights/PointLight\"><code class=\"notranslate\" translate=\"no\">PointLight</code></a> is a light that sits at a point and shoots light\nin all directions from that point. Let's change the code.</p>\n<pre class=\"prettyprint showlinemods notranslate lang-js\" translate=\"no\">const color = 0xFFFFFF;\n-const intensity = 1;\n+const intensity = 150;\n-const light = new THREE.DirectionalLight(color, intensity);\n+const light = new THREE.PointLight(color, intensity);\nlight.position.set(0, 10, 0);\n-light.target.position.set(-5, 0, 0);\nscene.add(light);\n-scene.add(light.target);\n</pre>\n<p>Let's also switch to a <a href=\"/docs/#api/en/helpers/PointLightHelper\"><code class=\"notranslate\" translate=\"no\">PointLightHelper</code></a></p>\n<pre class=\"prettyprint showlinemods notranslate lang-js\" translate=\"no\">-const helper = new THREE.DirectionalLightHelper(light);\n+const helper = new THREE.PointLightHelper(light);\nscene.add(helper);\n</pre>\n<p>and as there is no target the <code class=\"notranslate\" translate=\"no\">onChange</code> function can be simpler.</p>\n<pre class=\"prettyprint showlinemods notranslate lang-js\" translate=\"no\">function updateLight() {\n-  light.target.updateMatrixWorld();\n  helper.update();\n}\n-updateLight();\n</pre>\n<p>Note that at some level a <a href=\"/docs/#api/en/helpers/PointLightHelper\"><code class=\"notranslate\" translate=\"no\">PointLightHelper</code></a> has no um, point.\nIt just draws a small wireframe diamond. It could just as easily\nbe any shape you want, just add a mesh to the light itself.</p>\n<p>A <a href=\"/docs/#api/en/lights/PointLight\"><code class=\"notranslate\" translate=\"no\">PointLight</code></a> has the added property of <a href=\"/docs/#api/en/lights/PointLight#distance\"><code class=\"notranslate\" translate=\"no\">distance</code></a>.\nIf the <code class=\"notranslate\" translate=\"no\">distance</code> is 0 then the <a href=\"/docs/#api/en/lights/PointLight\"><code class=\"notranslate\" translate=\"no\">PointLight</code></a> shines to\ninfinity. If the <code class=\"notranslate\" translate=\"no\">distance</code> is greater than 0 then the light shines\nits full intensity at the light and fades to no influence at <code class=\"notranslate\" translate=\"no\">distance</code>\nunits away from the light.</p>\n<p>Let's setup the GUI so we can adjust the distance.</p>\n<pre class=\"prettyprint showlinemods notranslate lang-js\" translate=\"no\">const gui = new GUI();\ngui.addColor(new ColorGUIHelper(light, 'color'), 'value').name('color');\ngui.add(light, 'intensity', 0, 250, 1);\n+gui.add(light, 'distance', 0, 40).onChange(updateLight);\n\nmakeXYZGUI(gui, light.position, 'position', updateLight);\n-makeXYZGUI(gui, light.target.position, 'target', updateLight);\n</pre>\n<p>And now try it out.</p>\n<p></p><div translate=\"no\" class=\"threejs_example_container notranslate\">\n  <div></div>\n  <a class=\"threejs_center\" href=\"/manual/examples/lights-point.html\" target=\"_blank\">click here to open in a separate window</a>\n</div>\n\n<p></p>\n<p>Notice when <code class=\"notranslate\" translate=\"no\">distance</code> is &gt; 0 how the light fades out.</p>\n<h2 id=\"-spotlight-\"><a href=\"/docs/#api/en/lights/SpotLight\"><code class=\"notranslate\" translate=\"no\">SpotLight</code></a></h2>\n<p>Spotlights are effectively a point light with a cone\nattached where the light only shines inside the cone.\nThere's actually 2 cones. An outer cone and an inner\ncone. Between the inner cone and the outer cone the\nlight fades from full intensity to zero.</p>\n<p>To use a <a href=\"/docs/#api/en/lights/SpotLight\"><code class=\"notranslate\" translate=\"no\">SpotLight</code></a> we need a target just like\nthe directional light. The light's cone will\nopen toward the target.</p>\n<p>Modifying our <a href=\"/docs/#api/en/lights/DirectionalLight\"><code class=\"notranslate\" translate=\"no\">DirectionalLight</code></a> with helper from above</p>\n<pre class=\"prettyprint showlinemods notranslate lang-js\" translate=\"no\">const color = 0xFFFFFF;\n-const intensity = 1;\n+const intensity = 150;\n-const light = new THREE.DirectionalLight(color, intensity);\n+const light = new THREE.SpotLight(color, intensity);\nscene.add(light);\nscene.add(light.target);\n\n-const helper = new THREE.DirectionalLightHelper(light);\n+const helper = new THREE.SpotLightHelper(light);\nscene.add(helper);\n</pre>\n<p>The spotlight's cone's angle is set with the <a href=\"/docs/#api/en/lights/SpotLight#angle\"><code class=\"notranslate\" translate=\"no\">angle</code></a>\nproperty in radians. We'll use our <code class=\"notranslate\" translate=\"no\">DegRadHelper</code> from the\n<a href=\"textures.html\">texture article</a> to present a UI in\ndegrees.</p>\n<pre class=\"prettyprint showlinemods notranslate lang-js\" translate=\"no\">gui.add(new DegRadHelper(light, 'angle'), 'value', 0, 90).name('angle').onChange(updateLight);\n</pre>\n<p>The inner cone is defined by setting the <a href=\"/docs/#api/en/lights/SpotLight#penumbra\"><code class=\"notranslate\" translate=\"no\">penumbra</code></a> property\nas a percentage from the outer cone. In other words when <code class=\"notranslate\" translate=\"no\">penumbra</code> is 0 then the\ninner cone is the same size (0 = no difference) from the outer cone. When the\n<code class=\"notranslate\" translate=\"no\">penumbra</code> is 1 then the light fades starting in the center of the cone to the\nouter cone. When <code class=\"notranslate\" translate=\"no\">penumbra</code> is .5 then the light fades starting from 50% between\nthe center of the outer cone.</p>\n<pre class=\"prettyprint showlinemods notranslate lang-js\" translate=\"no\">gui.add(light, 'penumbra', 0, 1, 0.01);\n</pre>\n<p></p><div translate=\"no\" class=\"threejs_example_container notranslate\">\n  <div></div>\n  <a class=\"threejs_center\" href=\"/manual/examples/lights-spot-w-helper.html\" target=\"_blank\">click here to open in a separate window</a>\n</div>\n\n<p></p>\n<p>Notice with the default <code class=\"notranslate\" translate=\"no\">penumbra</code> of 0 the spotlight has a very sharp edge\nwhereas as you adjust the <code class=\"notranslate\" translate=\"no\">penumbra</code> toward 1 the edge blurs.</p>\n<p>It might be hard to see the <em>cone</em> of the spotlight. The reason is it's\nbelow the ground. Shorten the distance to around 5 and you'll see the open\nend of the cone.</p>\n<h2 id=\"-rectarealight-\"><a href=\"/docs/#api/en/lights/RectAreaLight\"><code class=\"notranslate\" translate=\"no\">RectAreaLight</code></a></h2>\n<p>There's one more type of light, the <a href=\"/docs/#api/en/lights/RectAreaLight\"><code class=\"notranslate\" translate=\"no\">RectAreaLight</code></a>, which represents\nexactly what it sounds like, a rectangular area of light like a long\nfluorescent light or maybe a frosted sky light in a ceiling.</p>\n<p>The <a href=\"/docs/#api/en/lights/RectAreaLight\"><code class=\"notranslate\" translate=\"no\">RectAreaLight</code></a> only works with the <a href=\"/docs/#api/en/materials/MeshStandardMaterial\"><code class=\"notranslate\" translate=\"no\">MeshStandardMaterial</code></a> and the\n<a href=\"/docs/#api/en/materials/MeshPhysicalMaterial\"><code class=\"notranslate\" translate=\"no\">MeshPhysicalMaterial</code></a> so let's change all our materials to <a href=\"/docs/#api/en/materials/MeshStandardMaterial\"><code class=\"notranslate\" translate=\"no\">MeshStandardMaterial</code></a></p>\n<pre class=\"prettyprint showlinemods notranslate lang-js\" translate=\"no\">  ...\n\n  const planeGeo = new THREE.PlaneGeometry(planeSize, planeSize);\n-  const planeMat = new THREE.MeshPhongMaterial({\n+  const planeMat = new THREE.MeshStandardMaterial({\n    map: texture,\n    side: THREE.DoubleSide,\n  });\n  const mesh = new THREE.Mesh(planeGeo, planeMat);\n  mesh.rotation.x = Math.PI * -.5;\n  scene.add(mesh);\n}\n{\n  const cubeSize = 4;\n  const cubeGeo = new THREE.BoxGeometry(cubeSize, cubeSize, cubeSize);\n- const cubeMat = new THREE.MeshPhongMaterial({color: '#8AC'});\n+ const cubeMat = new THREE.MeshStandardMaterial({color: '#8AC'});\n  const mesh = new THREE.Mesh(cubeGeo, cubeMat);\n  mesh.position.set(cubeSize + 1, cubeSize / 2, 0);\n  scene.add(mesh);\n}\n{\n  const sphereRadius = 3;\n  const sphereWidthDivisions = 32;\n  const sphereHeightDivisions = 16;\n  const sphereGeo = new THREE.SphereGeometry(sphereRadius, sphereWidthDivisions, sphereHeightDivisions);\n-  const sphereMat = new THREE.MeshPhongMaterial({color: '#CA8'});\n+ const sphereMat = new THREE.MeshStandardMaterial({color: '#CA8'});\n  const mesh = new THREE.Mesh(sphereGeo, sphereMat);\n  mesh.position.set(-sphereRadius - 1, sphereRadius + 2, 0);\n  scene.add(mesh);\n}\n</pre>\n<p>To use the <a href=\"/docs/#api/en/lights/RectAreaLight\"><code class=\"notranslate\" translate=\"no\">RectAreaLight</code></a> we need to include some extra three.js optional data and we'll\ninclude the <a href=\"/docs/#api/en/helpers/RectAreaLightHelper\"><code class=\"notranslate\" translate=\"no\">RectAreaLightHelper</code></a> to help us visualize the light</p>\n<pre class=\"prettyprint showlinemods notranslate lang-js\" translate=\"no\">import * as THREE from 'three';\n+import {RectAreaLightUniformsLib} from 'three/addons/lights/RectAreaLightUniformsLib.js';\n+import {RectAreaLightHelper} from 'three/addons/helpers/RectAreaLightHelper.js';\n</pre>\n<p>and we need to call <code class=\"notranslate\" translate=\"no\">RectAreaLightUniformsLib.init</code></p>\n<pre class=\"prettyprint showlinemods notranslate lang-js\" translate=\"no\">function main() {\n  const canvas = document.querySelector('#c');\n  const renderer = new THREE.WebGLRenderer({antialias: true, canvas});\n+  RectAreaLightUniformsLib.init();\n</pre>\n<p>If you forget the data the light will still work but it will look funny so\nbe sure to remember to include the extra data.</p>\n<p>Now we can create the light</p>\n<pre class=\"prettyprint showlinemods notranslate lang-js\" translate=\"no\">const color = 0xFFFFFF;\n*const intensity = 5;\n+const width = 12;\n+const height = 4;\n*const light = new THREE.RectAreaLight(color, intensity, width, height);\nlight.position.set(0, 10, 0);\n+light.rotation.x = THREE.MathUtils.degToRad(-90);\nscene.add(light);\n\n*const helper = new RectAreaLightHelper(light);\n*light.add(helper);\n</pre>\n<p>One thing to notice is that unlike the <a href=\"/docs/#api/en/lights/DirectionalLight\"><code class=\"notranslate\" translate=\"no\">DirectionalLight</code></a> and the <a href=\"/docs/#api/en/lights/SpotLight\"><code class=\"notranslate\" translate=\"no\">SpotLight</code></a>, the\n<a href=\"/docs/#api/en/lights/RectAreaLight\"><code class=\"notranslate\" translate=\"no\">RectAreaLight</code></a> does not use a target. It just uses its rotation. Another thing\nto notice is the helper needs to be a child of the light. It is not a child of the\nscene like other helpers.</p>\n<p>Let's also adjust the GUI. We'll make it so we can rotate the light and adjust\nits <code class=\"notranslate\" translate=\"no\">width</code> and <code class=\"notranslate\" translate=\"no\">height</code></p>\n<pre class=\"prettyprint showlinemods notranslate lang-js\" translate=\"no\">const gui = new GUI();\ngui.addColor(new ColorGUIHelper(light, 'color'), 'value').name('color');\ngui.add(light, 'intensity', 0, 10, 0.01);\ngui.add(light, 'width', 0, 20);\ngui.add(light, 'height', 0, 20);\ngui.add(new DegRadHelper(light.rotation, 'x'), 'value', -180, 180).name('x rotation');\ngui.add(new DegRadHelper(light.rotation, 'y'), 'value', -180, 180).name('y rotation');\ngui.add(new DegRadHelper(light.rotation, 'z'), 'value', -180, 180).name('z rotation');\n\nmakeXYZGUI(gui, light.position, 'position');\n</pre>\n<p>And here is that.</p>\n<p></p><div translate=\"no\" class=\"threejs_example_container notranslate\">\n  <div></div>\n  <a class=\"threejs_center\" href=\"/manual/examples/lights-rectarea.html\" target=\"_blank\">click here to open in a separate window</a>\n</div>\n\n<p></p>\n<p>It's important to note each light you add to the scene slows down how fast\nthree.js renders the scene so you should always try to use as few as\npossible to achieve your goals.</p>\n<p>Next up let's go over <a href=\"cameras.html\">dealing with cameras</a>.</p>\n<p><canvas id=\"c\"></canvas></p>\n\n\n        </div>\n      </div>\n    </div>",
    "success": true,
    "error": "",
    "crawled_at": 1786376809.300204,
    "is_shell": false
  },
  {
    "url": "https://threejs.org/manual/en/cameras.html",
    "title": "Cameras",
    "content": "<div class=\"container\">\n      <div class=\"lesson-title\">\n        <h1>Cameras</h1>\n      </div>\n      <div class=\"lesson\">\n        <div class=\"lesson-main\">\n          <p>This article is one in a series of articles about three.js.\nThe first article was <a href=\"fundamentals.html\">about fundamentals</a>.\nIf you haven't read that yet you might want to start there.</p>\n<p>Let's talk about cameras in three.js. We covered some of this in the <a href=\"fundamentals.html\">first article</a> but we'll cover it in more detail here.</p>\n<p>The most common camera in three.js and the one we've been using up to this point is\nthe <a href=\"/docs/#api/en/cameras/PerspectiveCamera\"><code class=\"notranslate\" translate=\"no\">PerspectiveCamera</code></a>. It gives a 3d view where things in the distance appear\nsmaller than things up close.</p>\n<p>The <a href=\"/docs/#api/en/cameras/PerspectiveCamera\"><code class=\"notranslate\" translate=\"no\">PerspectiveCamera</code></a> defines a <em>frustum</em>. <a href=\"https://en.wikipedia.org/wiki/Frustum\">A <em>frustum</em> is a solid pyramid shape with\nthe tip cut off</a>.\nBy name of a solid I mean for example a cube, a cone, a sphere, a cylinder,\nand a frustum are all names of different kinds of solids.</p>\n<div class=\"spread\">\n  <div><div data-diagram=\"shapeCube\"></div><div>cube</div></div>\n  <div><div data-diagram=\"shapeCone\"></div><div>cone</div></div>\n  <div><div data-diagram=\"shapeSphere\"></div><div>sphere</div></div>\n  <div><div data-diagram=\"shapeCylinder\"></div><div>cylinder</div></div>\n  <div><div data-diagram=\"shapeFrustum\"></div><div>frustum</div></div>\n</div>\n\n<p>I only point that out because I didn't know it for years. Some book or page would mention\n<em>frustum</em> and my eyes would glaze over. Understanding it's the name of a type of solid\nshape made those descriptions suddenly make more sense 😅</p>\n<p>A <a href=\"/docs/#api/en/cameras/PerspectiveCamera\"><code class=\"notranslate\" translate=\"no\">PerspectiveCamera</code></a> defines its frustum based on 4 properties. <code class=\"notranslate\" translate=\"no\">near</code> defines where the\nfront of the frustum starts. <code class=\"notranslate\" translate=\"no\">far</code> defines where it ends. <code class=\"notranslate\" translate=\"no\">fov</code>, the field of view, defines\nhow tall the front and back of the frustum are by computing the correct height to get\nthe specified field of view at <code class=\"notranslate\" translate=\"no\">near</code> units from the camera. The <code class=\"notranslate\" translate=\"no\">aspect</code> defines how\nwide the front and back of the frustum are. The width of the frustum is just the height\nmultiplied by the aspect.</p>\n<p><img src=\"../resources/frustum-3d.svg\" width=\"500\" class=\"threejs_center\"></p>\n<p>Let's use the scene from <a href=\"lights.html\">the previous article</a> that has a ground\nplane, a sphere, and a cube and make it so we can adjust the camera's settings.</p>\n<p>To do that we'll make a <code class=\"notranslate\" translate=\"no\">MinMaxGUIHelper</code> for the <code class=\"notranslate\" translate=\"no\">near</code> and <code class=\"notranslate\" translate=\"no\">far</code> settings so <code class=\"notranslate\" translate=\"no\">far</code>\nis always greater than <code class=\"notranslate\" translate=\"no\">near</code>. It will have <code class=\"notranslate\" translate=\"no\">min</code> and <code class=\"notranslate\" translate=\"no\">max</code> properties that lil-gui\nwill adjust. When adjusted they'll set the 2 properties we specify.</p>\n<pre class=\"prettyprint showlinemods notranslate lang-js\" translate=\"no\">class MinMaxGUIHelper {\n  constructor(obj, minProp, maxProp, minDif) {\n    this.obj = obj;\n    this.minProp = minProp;\n    this.maxProp = maxProp;\n    this.minDif = minDif;\n  }\n  get min() {\n    return this.obj[this.minProp];\n  }\n  set min(v) {\n    this.obj[this.minProp] = v;\n    this.obj[this.maxProp] = Math.max(this.obj[this.maxProp], v + this.minDif);\n  }\n  get max() {\n    return this.obj[this.maxProp];\n  }\n  set max(v) {\n    this.obj[this.maxProp] = v;\n    this.min = this.min;  // this will call the min setter\n  }\n}\n</pre>\n<p>Now we can setup our GUI like this</p>\n<pre class=\"prettyprint showlinemods notranslate lang-js\" translate=\"no\">function updateCamera() {\n  camera.updateProjectionMatrix();\n}\n\nconst gui = new GUI();\ngui.add(camera, 'fov', 1, 180).onChange(updateCamera);\nconst minMaxGUIHelper = new MinMaxGUIHelper(camera, 'near', 'far', 0.1);\ngui.add(minMaxGUIHelper, 'min', 0.1, 50, 0.1).name('near').onChange(updateCamera);\ngui.add(minMaxGUIHelper, 'max', 0.1, 50, 0.1).name('far').onChange(updateCamera);\n</pre>\n<p>Anytime the camera's settings change we need to call the camera's\n<a href=\"/docs/#api/en/cameras/PerspectiveCamera#updateProjectionMatrix\"><code class=\"notranslate\" translate=\"no\">updateProjectionMatrix</code></a> function\nso we made a function called <code class=\"notranslate\" translate=\"no\">updateCamera</code> add passed it to lil-gui to call it when things change.</p>\n<p></p><div translate=\"no\" class=\"threejs_example_container notranslate\">\n  <div></div>\n  <a class=\"threejs_center\" href=\"/manual/examples/cameras-perspective.html\" target=\"_blank\">click here to open in a separate window</a>\n</div>\n\n<p></p>\n<p>You can adjust the values and see how they work. Note we didn't make <code class=\"notranslate\" translate=\"no\">aspect</code> settable since\nit's taken from the size of the window so if you want to adjust the aspect open the example\nin a new window and then size the window.</p>\n<p>Still, I think it's a little hard to see so let's change the example so it has 2 cameras.\nOne will show our scene as we see it above, the other will show another camera looking at the\nscene the first camera is drawing and showing that camera's frustum.</p>\n<p>To do this we can use the scissor function of three.js.\nLet's change it to draw 2 scenes with 2 cameras side by side using the scissor function</p>\n<p>First off let's use some HTML and CSS to define 2 side by side elements. This will also\nhelp us with events so both cameras can easily have their own <a href=\"/docs/#examples/controls/OrbitControls\"><code class=\"notranslate\" translate=\"no\">OrbitControls</code></a>.</p>\n<pre class=\"prettyprint showlinemods notranslate lang-html\" translate=\"no\">&lt;body&gt;\n  &lt;canvas id=\"c\"&gt;&lt;/canvas&gt;\n+  &lt;div class=\"split\"&gt;\n+     &lt;div id=\"view1\" tabindex=\"1\"&gt;&lt;/div&gt;\n+     &lt;div id=\"view2\" tabindex=\"2\"&gt;&lt;/div&gt;\n+  &lt;/div&gt;\n&lt;/body&gt;\n</pre>\n<p>And the CSS that will make those 2 views show up side by side overlaid on top of\nthe canvas</p>\n<pre class=\"prettyprint showlinemods notranslate lang-css\" translate=\"no\">.split {\n  position: absolute;\n  left: 0;\n  top: 0;\n  width: 100%;\n  height: 100%;\n  display: flex;\n}\n.split&gt;div {\n  width: 100%;\n  height: 100%;\n}\n</pre>\n<p>Then in our code we'll add a <a href=\"/docs/#api/en/helpers/CameraHelper\"><code class=\"notranslate\" translate=\"no\">CameraHelper</code></a>. A <a href=\"/docs/#api/en/helpers/CameraHelper\"><code class=\"notranslate\" translate=\"no\">CameraHelper</code></a> draws the frustum for a <a href=\"/docs/#api/en/cameras/Camera\"><code class=\"notranslate\" translate=\"no\">Camera</code></a></p>\n<pre class=\"prettyprint showlinemods notranslate lang-js\" translate=\"no\">const cameraHelper = new THREE.CameraHelper(camera);\n\n...\n\nscene.add(cameraHelper);\n</pre>\n<p>Now let's look up the 2 view elements.</p>\n<pre class=\"prettyprint showlinemods notranslate lang-js\" translate=\"no\">const view1Elem = document.querySelector('#view1');\nconst view2Elem = document.querySelector('#view2');\n</pre>\n<p>And we'll set our existing <a href=\"/docs/#examples/controls/OrbitControls\"><code class=\"notranslate\" translate=\"no\">OrbitControls</code></a> to respond to the first\nview element only.</p>\n<pre class=\"prettyprint showlinemods notranslate lang-js\" translate=\"no\">-const controls = new OrbitControls(camera, canvas);\n+const controls = new OrbitControls(camera, view1Elem);\n</pre>\n<p>Let's make a second <a href=\"/docs/#api/en/cameras/PerspectiveCamera\"><code class=\"notranslate\" translate=\"no\">PerspectiveCamera</code></a> and a second <a href=\"/docs/#examples/controls/OrbitControls\"><code class=\"notranslate\" translate=\"no\">OrbitControls</code></a>.\nThe second <a href=\"/docs/#examples/controls/OrbitControls\"><code class=\"notranslate\" translate=\"no\">OrbitControls</code></a> is tied to the second camera and gets input\nfrom the second view element.</p>\n<pre class=\"prettyprint showlinemods notranslate lang-js\" translate=\"no\">const camera2 = new THREE.PerspectiveCamera(\n  60,  // fov\n  2,   // aspect\n  0.1, // near\n  500, // far\n);\ncamera2.position.set(40, 10, 30);\ncamera2.lookAt(0, 5, 0);\n\nconst controls2 = new OrbitControls(camera2, view2Elem);\ncontrols2.target.set(0, 5, 0);\ncontrols2.update();\n</pre>\n<p>Finally we need to render the scene from the point of view of each\ncamera using the scissor function to only render to part of the canvas.</p>\n<p>Here is a function that given an element will compute the rectangle\nof that element that overlaps the canvas. It will then set the scissor\nand viewport to that rectangle and return the aspect for that size.</p>\n<pre class=\"prettyprint showlinemods notranslate lang-js\" translate=\"no\">function setScissorForElement(elem) {\n  const canvasRect = canvas.getBoundingClientRect();\n  const elemRect = elem.getBoundingClientRect();\n\n  // compute a canvas relative rectangle\n  const right = Math.min(elemRect.right, canvasRect.right) - canvasRect.left;\n  const left = Math.max(0, elemRect.left - canvasRect.left);\n  const bottom = Math.min(elemRect.bottom, canvasRect.bottom) - canvasRect.top;\n  const top = Math.max(0, elemRect.top - canvasRect.top);\n\n  const width = Math.min(canvasRect.width, right - left);\n  const height = Math.min(canvasRect.height, bottom - top);\n\n  // setup the scissor to only render to that part of the canvas\n  const positiveYUpBottom = canvasRect.height - bottom;\n  renderer.setScissor(left, positiveYUpBottom, width, height);\n  renderer.setViewport(left, positiveYUpBottom, width, height);\n\n  // return the aspect\n  return width / height;\n}\n</pre>\n<p>And now we can use that function to draw the scene twice in our <code class=\"notranslate\" translate=\"no\">render</code> function</p>\n<pre class=\"prettyprint showlinemods notranslate lang-js\" translate=\"no\">  function render() {\n\n-    if (resizeRendererToDisplaySize(renderer)) {\n-      const canvas = renderer.domElement;\n-      camera.aspect = canvas.clientWidth / canvas.clientHeight;\n-      camera.updateProjectionMatrix();\n-    }\n\n+    resizeRendererToDisplaySize(renderer);\n+\n+    // turn on the scissor\n+    renderer.setScissorTest(true);\n+\n+    // render the original view\n+    {\n+      const aspect = setScissorForElement(view1Elem);\n+\n+      // adjust the camera for this aspect\n+      camera.aspect = aspect;\n+      camera.updateProjectionMatrix();\n+      cameraHelper.update();\n+\n+      // don't draw the camera helper in the original view\n+      cameraHelper.visible = false;\n+\n+      scene.background.set(0x000000);\n+\n+      // render\n+      renderer.render(scene, camera);\n+    }\n+\n+    // render from the 2nd camera\n+    {\n+      const aspect = setScissorForElement(view2Elem);\n+\n+      // adjust the camera for this aspect\n+      camera2.aspect = aspect;\n+      camera2.updateProjectionMatrix();\n+\n+      // draw the camera helper in the 2nd view\n+      cameraHelper.visible = true;\n+\n+      scene.background.set(0x000040);\n+\n+      renderer.render(scene, camera2);\n+    }\n\n-    renderer.render(scene, camera);\n\n    requestAnimationFrame(render);\n  }\n\n  requestAnimationFrame(render);\n}\n</pre>\n<p>The code above sets the background color of the scene when rendering the\nsecond view to dark blue just to make it easier to distinguish the two views.</p>\n<p>We can also remove our <code class=\"notranslate\" translate=\"no\">updateCamera</code> code since we're updating everything\nin the <code class=\"notranslate\" translate=\"no\">render</code> function.</p>\n<pre class=\"prettyprint showlinemods notranslate lang-js\" translate=\"no\">-function updateCamera() {\n-  camera.updateProjectionMatrix();\n-}\n\nconst gui = new GUI();\n-gui.add(camera, 'fov', 1, 180).onChange(updateCamera);\n+gui.add(camera, 'fov', 1, 180);\nconst minMaxGUIHelper = new MinMaxGUIHelper(camera, 'near', 'far', 0.1);\n-gui.add(minMaxGUIHelper, 'min', 0.1, 50, 0.1).name('near').onChange(updateCamera);\n-gui.add(minMaxGUIHelper, 'max', 0.1, 50, 0.1).name('far').onChange(updateCamera);\n+gui.add(minMaxGUIHelper, 'min', 0.1, 50, 0.1).name('near');\n+gui.add(minMaxGUIHelper, 'max', 0.1, 50, 0.1).name('far');\n</pre>\n<p>And now you can use one view to see the frustum of the other.</p>\n<p></p><div translate=\"no\" class=\"threejs_example_container notranslate\">\n  <div></div>\n  <a class=\"threejs_center\" href=\"/manual/examples/cameras-perspective-2-scenes.html\" target=\"_blank\">click here to open in a separate window</a>\n</div>\n\n<p></p>\n<p>On the left you can see the original view and on the right you can\nsee a view showing the frustum of the camera on the left. As you adjust\n<code class=\"notranslate\" translate=\"no\">near</code>, <code class=\"notranslate\" translate=\"no\">far</code>, <code class=\"notranslate\" translate=\"no\">fov</code> and move the camera with mouse you can see that\nonly what's inside the frustum shown on the right appears in the scene on\nthe left.</p>\n<p>Adjust <code class=\"notranslate\" translate=\"no\">near</code> up to around 20 and you'll easily see the front of objects\ndisappear as they are no longer in the frustum. Adjust <code class=\"notranslate\" translate=\"no\">far</code> below about 35\nand you'll start to see the ground plane disappear as it's no longer in\nthe frustum.</p>\n<p>This brings up the question, why not just set <code class=\"notranslate\" translate=\"no\">near</code> to 0.0000000001 and <code class=\"notranslate\" translate=\"no\">far</code>\nto 10000000000000 or something like that so you can just see everything?\nThe reason is your GPU only has so much precision to decide if something\nis in front or behind something else. That precision is spread out between\n<code class=\"notranslate\" translate=\"no\">near</code> and <code class=\"notranslate\" translate=\"no\">far</code>. Worse, by default the precision close the camera is detailed\nand the precision far from the camera is coarse. The units start with <code class=\"notranslate\" translate=\"no\">near</code>\nand slowly expand as they approach <code class=\"notranslate\" translate=\"no\">far</code>.</p>\n<p>Starting with the top example, let's change the code to insert 20 spheres in a\nrow.</p>\n<pre class=\"prettyprint showlinemods notranslate lang-js\" translate=\"no\">{\n  const sphereRadius = 3;\n  const sphereWidthDivisions = 32;\n  const sphereHeightDivisions = 16;\n  const sphereGeo = new THREE.SphereGeometry(sphereRadius, sphereWidthDivisions, sphereHeightDivisions);\n  const numSpheres = 20;\n  for (let i = 0; i &lt; numSpheres; ++i) {\n    const sphereMat = new THREE.MeshPhongMaterial();\n    sphereMat.color.setHSL(i * .73, 1, 0.5);\n    const mesh = new THREE.Mesh(sphereGeo, sphereMat);\n    mesh.position.set(-sphereRadius - 1, sphereRadius + 2, i * sphereRadius * -2.2);\n    scene.add(mesh);\n  }\n}\n</pre>\n<p>and let's set <code class=\"notranslate\" translate=\"no\">near</code> to 0.00001</p>\n<pre class=\"prettyprint showlinemods notranslate lang-js\" translate=\"no\">const fov = 45;\nconst aspect = 2;  // the canvas default\n-const near = 0.1;\n+const near = 0.00001;\nconst far = 100;\nconst camera = new THREE.PerspectiveCamera(fov, aspect, near, far);\n</pre>\n<p>We also need to tweak the GUI code a little to allow 0.00001 if the value is edited</p>\n<pre class=\"prettyprint showlinemods notranslate lang-js\" translate=\"no\">-gui.add(minMaxGUIHelper, 'min', 0.1, 50, 0.1).name('near').onChange(updateCamera);\n+gui.add(minMaxGUIHelper, 'min', 0.00001, 50, 0.00001).name('near').onChange(updateCamera);\n</pre>\n<p>What do you think will happen?</p>\n<p></p><div translate=\"no\" class=\"threejs_example_container notranslate\">\n  <div></div>\n  <a class=\"threejs_center\" href=\"/manual/examples/cameras-z-fighting.html\" target=\"_blank\">click here to open in a separate window</a>\n</div>\n\n<p></p>\n<p>This is an example of <em>z fighting</em> where the GPU on your computer does not have\nenough precision to decide which pixels are in front and which pixels are behind.</p>\n<p>Just in case the issue doesn't show on your machine here's what I see on mine</p>\n<div class=\"threejs_center\"><img src=\"../resources/images/z-fighting.png\" style=\"width: 570px;\"></div>\n\n<p>One solution is to tell three.js use to a different method to compute which\npixels are in front and which are behind. We can do that by enabling\n<code class=\"notranslate\" translate=\"no\">logarithmicDepthBuffer</code> when we create the <a href=\"/docs/#api/en/renderers/WebGLRenderer\"><code class=\"notranslate\" translate=\"no\">WebGLRenderer</code></a></p>\n<pre class=\"prettyprint showlinemods notranslate lang-js\" translate=\"no\">-const renderer = new THREE.WebGLRenderer({antialias: true, canvas});\n+const renderer = new THREE.WebGLRenderer({\n+  antialias: true,\n+  canvas,\n+  logarithmicDepthBuffer: true,\n+});\n</pre>\n<p>and with that it might work</p>\n<p></p><div translate=\"no\" class=\"threejs_example_container notranslate\">\n  <div></div>\n  <a class=\"threejs_center\" href=\"/manual/examples/cameras-logarithmic-depth-buffer.html\" target=\"_blank\">click here to open in a separate window</a>\n</div>\n\n<p></p>\n<p>If this didn't fix the issue for you then you've run into one reason why\nyou can't always use this solution. That reason is because only certain GPUs\nsupport it. As of September 2018 almost no mobile devices support this\nsolution whereas most desktops do.</p>\n<p>Another reason not to choose this solution is it can be significantly slower\nthan the standard solution.</p>\n<p>Even with this solution there is still limited resolution. Make <code class=\"notranslate\" translate=\"no\">near</code> even\nsmaller or <code class=\"notranslate\" translate=\"no\">far</code> even bigger and you'll eventually run into the same issues.</p>\n<p>What that means is that you should always make an effort to choose a <code class=\"notranslate\" translate=\"no\">near</code>\nand <code class=\"notranslate\" translate=\"no\">far</code> setting that fits your use case. Set <code class=\"notranslate\" translate=\"no\">near</code> as far away from the camera\nas you can and not have things disappear. Set <code class=\"notranslate\" translate=\"no\">far</code> as close to the camera\nas you can and not have things disappear. If you're trying to draw a giant\nscene and show a close up of someone's face so you can see their eyelashes\nwhile in the background you can see all the way to mountains 50 kilometers\nin the distance well then you'll need to find other creative solutions that\nmaybe we'll go over later. For now, just be aware you should take care\nto choose appropriate <code class=\"notranslate\" translate=\"no\">near</code> and <code class=\"notranslate\" translate=\"no\">far</code> values for your needs.</p>\n<p>The 2nd most common camera is the <a href=\"/docs/#api/en/cameras/OrthographicCamera\"><code class=\"notranslate\" translate=\"no\">OrthographicCamera</code></a>. Rather than\nspecify a frustum it specifies a box with the settings <code class=\"notranslate\" translate=\"no\">left</code>, <code class=\"notranslate\" translate=\"no\">right</code>\n<code class=\"notranslate\" translate=\"no\">top</code>, <code class=\"notranslate\" translate=\"no\">bottom</code>, <code class=\"notranslate\" translate=\"no\">near</code>, and <code class=\"notranslate\" translate=\"no\">far</code>. Because it's projecting a box\nthere is no perspective.</p>\n<p>Let's change the 2 view example above to use an <a href=\"/docs/#api/en/cameras/OrthographicCamera\"><code class=\"notranslate\" translate=\"no\">OrthographicCamera</code></a>\nin the first view.</p>\n<p>First let's setup an <a href=\"/docs/#api/en/cameras/OrthographicCamera\"><code class=\"notranslate\" translate=\"no\">OrthographicCamera</code></a>.</p>\n<pre class=\"prettyprint showlinemods notranslate lang-js\" translate=\"no\">const left = -1;\nconst right = 1;\nconst top = 1;\nconst bottom = -1;\nconst near = 5;\nconst far = 50;\nconst camera = new THREE.OrthographicCamera(left, right, top, bottom, near, far);\ncamera.zoom = 0.2;\n</pre>\n<p>We set <code class=\"notranslate\" translate=\"no\">left</code> and <code class=\"notranslate\" translate=\"no\">bottom</code> to -1 and <code class=\"notranslate\" translate=\"no\">right</code> and <code class=\"notranslate\" translate=\"no\">top</code> to 1. This would make\na box 2 units wide and 2 units tall but we're going to adjust the <code class=\"notranslate\" translate=\"no\">left</code> and <code class=\"notranslate\" translate=\"no\">top</code>\nby the aspect of the rectangle we're drawing to. We'll use the <code class=\"notranslate\" translate=\"no\">zoom</code> property\nto make it easy to adjust how many units are actually shown by the camera.</p>\n<p>Let's add a GUI setting for <code class=\"notranslate\" translate=\"no\">zoom</code></p>\n<pre class=\"prettyprint showlinemods notranslate lang-js\" translate=\"no\">const gui = new GUI();\n+gui.add(camera, 'zoom', 0.01, 1, 0.01).listen();\n</pre>\n<p>The call to <code class=\"notranslate\" translate=\"no\">listen</code> tells lil-gui to watch for changes. This is here because\nthe <a href=\"/docs/#examples/controls/OrbitControls\"><code class=\"notranslate\" translate=\"no\">OrbitControls</code></a> can also control zoom. For example the scroll wheel on\na mouse will zoom via the <a href=\"/docs/#examples/controls/OrbitControls\"><code class=\"notranslate\" translate=\"no\">OrbitControls</code></a>.</p>\n<p>Last we just need to change the part that renders the left\nside to update the <a href=\"/docs/#api/en/cameras/OrthographicCamera\"><code class=\"notranslate\" translate=\"no\">OrthographicCamera</code></a>.</p>\n<pre class=\"prettyprint showlinemods notranslate lang-js\" translate=\"no\">{\n  const aspect = setScissorForElement(view1Elem);\n\n  // update the camera for this aspect\n-  camera.aspect = aspect;\n+  camera.left   = -aspect;\n+  camera.right  =  aspect;\n  camera.updateProjectionMatrix();\n  cameraHelper.update();\n\n  // don't draw the camera helper in the original view\n  cameraHelper.visible = false;\n\n  scene.background.set(0x000000);\n  renderer.render(scene, camera);\n}\n</pre>\n<p>and now you can see an <a href=\"/docs/#api/en/cameras/OrthographicCamera\"><code class=\"notranslate\" translate=\"no\">OrthographicCamera</code></a> at work.</p>\n<p></p><div translate=\"no\" class=\"threejs_example_container notranslate\">\n  <div></div>\n  <a class=\"threejs_center\" href=\"/manual/examples/cameras-orthographic-2-scenes.html\" target=\"_blank\">click here to open in a separate window</a>\n</div>\n\n<p></p>\n<p>An <a href=\"/docs/#api/en/cameras/OrthographicCamera\"><code class=\"notranslate\" translate=\"no\">OrthographicCamera</code></a> is most often used if using three.js\nto draw 2D things. You'd decide how many units you want the camera\nto show. For example if you want one pixel of canvas to match\none unit in the camera you could do something like</p>\n<p>To put the origin at the center and have 1 pixel = 1 three.js unit\nsomething like</p>\n<pre class=\"prettyprint showlinemods notranslate lang-js\" translate=\"no\">camera.left = -canvas.width / 2;\ncamera.right = canvas.width / 2;\ncamera.top = canvas.height / 2;\ncamera.bottom = -canvas.height / 2;\ncamera.near = -1;\ncamera.far = 1;\ncamera.zoom = 1;\n</pre>\n<p>Or if we wanted the origin to be in the top left just like a\n2D canvas we could use this</p>\n<pre class=\"prettyprint showlinemods notranslate lang-js\" translate=\"no\">camera.left = 0;\ncamera.right = canvas.width;\ncamera.top = 0;\ncamera.bottom = canvas.height;\ncamera.near = -1;\ncamera.far = 1;\ncamera.zoom = 1;\n</pre>\n<p>In which case the top left corner would be 0,0 just like a 2D canvas</p>\n<p>Let's try it! First let's set the camera up</p>\n<pre class=\"prettyprint showlinemods notranslate lang-js\" translate=\"no\">const left = 0;\nconst right = 300;  // default canvas size\nconst top = 0;\nconst bottom = 150;  // default canvas size\nconst near = -1;\nconst far = 1;\nconst camera = new THREE.OrthographicCamera(left, right, top, bottom, near, far);\ncamera.zoom = 1;\n</pre>\n<p>Then let's load 6 textures and make 6 planes, one for each texture.\nWe'll parent each plane to a <a href=\"/docs/#api/en/core/Object3D\"><code class=\"notranslate\" translate=\"no\">THREE.Object3D</code></a> to make it easy to offset\nthe plane so its center appears to be at its top left corner.</p>\n<p>If you're running locally you'll also need to have <a href=\"setup.html\">setup</a>.\nYou might also want to read about <a href=\"textures.html\">using textures</a>.</p>\n<pre class=\"prettyprint showlinemods notranslate lang-js\" translate=\"no\">const loader = new THREE.TextureLoader();\nconst textures = [\n  loader.load('resources/images/flower-1.jpg'),\n  loader.load('resources/images/flower-2.jpg'),\n  loader.load('resources/images/flower-3.jpg'),\n  loader.load('resources/images/flower-4.jpg'),\n  loader.load('resources/images/flower-5.jpg'),\n  loader.load('resources/images/flower-6.jpg'),\n];\nconst planeSize = 256;\nconst planeGeo = new THREE.PlaneGeometry(planeSize, planeSize);\nconst planes = textures.map((texture) =&gt; {\n  const planePivot = new THREE.Object3D();\n  scene.add(planePivot);\n  texture.magFilter = THREE.NearestFilter;\n  const planeMat = new THREE.MeshBasicMaterial({\n    map: texture,\n    side: THREE.DoubleSide,\n  });\n  const mesh = new THREE.Mesh(planeGeo, planeMat);\n  planePivot.add(mesh);\n  // move plane so top left corner is origin\n  mesh.position.set(planeSize / 2, planeSize / 2, 0);\n  return planePivot;\n});\n</pre>\n<p>and we need to update the camera if the size of the canvas\nchanges.</p>\n<pre class=\"prettyprint showlinemods notranslate lang-js\" translate=\"no\">function render() {\n\n  if (resizeRendererToDisplaySize(renderer)) {\n    camera.right = canvas.width;\n    camera.bottom = canvas.height;\n    camera.updateProjectionMatrix();\n  }\n\n  ...\n</pre>\n<p><code class=\"notranslate\" translate=\"no\">planes</code> is an array of <a href=\"/docs/#api/en/objects/Mesh\"><code class=\"notranslate\" translate=\"no\">THREE.Mesh</code></a>, one for each plane.\nLet's move them around based on the time.</p>\n<pre class=\"prettyprint showlinemods notranslate lang-js\" translate=\"no\">function render(time) {\n  time *= 0.001;  // convert to seconds;\n\n  ...\n\n  const distAcross = Math.max(20, canvas.width - planeSize);\n  const distDown = Math.max(20, canvas.height - planeSize);\n\n  // total distance to move across and back\n  const xRange = distAcross * 2;\n  const yRange = distDown * 2;\n  const speed = 180;\n\n  planes.forEach((plane, ndx) =&gt; {\n    // compute a unique time for each plane\n    const t = time * speed + ndx * 300;\n\n    // get a value between 0 and range\n    const xt = t % xRange;\n    const yt = t % yRange;\n\n    // set our position going forward if 0 to half of range\n    // and backward if half of range to range\n    const x = xt &lt; distAcross ? xt : xRange - xt;\n    const y = yt &lt; distDown   ? yt : yRange - yt;\n\n    plane.position.set(x, y, 0);\n  });\n\n  renderer.render(scene, camera);\n</pre>\n<p>And you can see the images bounce pixel perfect off the edges of the\ncanvas using pixel math just like a 2D canvas</p>\n<p></p><div translate=\"no\" class=\"threejs_example_container notranslate\">\n  <div></div>\n  <a class=\"threejs_center\" href=\"/manual/examples/cameras-orthographic-canvas-top-left-origin.html\" target=\"_blank\">click here to open in a separate window</a>\n</div>\n\n<p></p>\n<p>Another common use for an <a href=\"/docs/#api/en/cameras/OrthographicCamera\"><code class=\"notranslate\" translate=\"no\">OrthographicCamera</code></a> is to draw the\nup, down, left, right, front, back views of a 3D modeling\nprogram or a game engine's editor.</p>\n<div class=\"threejs_center\"><img src=\"../resources/images/quad-viewport.png\" style=\"width: 574px;\"></div>\n\n<p>In the screenshot above you can see 1 view is a perspective view and 3 views are\northographic views.</p>\n<p>That's the fundamentals of cameras. We'll cover a few common ways to move cameras\nin other articles. For now let's move on to <a href=\"shadows.html\">shadows</a>.</p>\n<p><canvas id=\"c\"></canvas></p>\n\n\n        </div>\n      </div>\n    </div>\n\n  \n  \n\n\n\n\n<canvas id=\"c\" data-engine=\"three.js r185\"></canvas>",
    "success": true,
    "error": "",
    "crawled_at": 1786376809.300204,
    "is_shell": false
  },
  {
    "url": "https://threejs.org/manual/en/shadows.html",
    "title": "Shadows",
    "content": "<div class=\"container\">\n      <div class=\"lesson-title\">\n        <h1>Shadows</h1>\n      </div>\n      <div class=\"lesson\">\n        <div class=\"lesson-main\">\n          <p>This article is part of a series of articles about three.js. The\nfirst article is <a href=\"fundamentals.html\">three.js fundamentals</a>. If\nyou haven't read that yet and you're new to three.js you might want to\nconsider starting there. The\n<a href=\"cameras.html\">previous article was about cameras</a> which is\nimportant to have read before you read this article as well as\nthe <a href=\"lights.html\">article before that one about lights</a>.</p>\n<p>Shadows on computers can be a complicated topic. There are various\nsolutions and all of them have tradeoffs including the solutions\navailable in three.js.</p>\n<p>Three.js by default uses <em>shadow maps</em>. The way a shadow map works\nis, <em>for every light that casts shadows all objects marked to cast\nshadows are rendered from the point of view of the light</em>. <strong>READ THAT\nAGAIN!</strong> and let it sink in.</p>\n<p>In other words, if you have 20 objects, and 5 lights, and\nall 20 objects are casting shadows and all 5 lights are casting\nshadows then your entire scene will be drawn 6 times. All 20 objects\nwill be drawn for light #1, then all 20 objects will be drawn for\nlight #2, then #3, etc and finally the actual scene will be drawn\nusing data from the first 5 renders.</p>\n<p>It gets worse, if you have a point light casting shadows the scene\nhas to be drawn 6 times just for that light!</p>\n<p>For these reasons it's common to find other solutions than to have\na bunch of lights all generating shadows. One common solution\nis to have multiple lights but only one directional light generating\nshadows.</p>\n<p>Yet another solution is to use lightmaps and or ambient occlusion maps\nto pre-compute the effects of lighting offline. This results in static\nlighting or static lighting hints but at least it's fast. We'll\ncover both of those in another article.</p>\n<p>Another solution is to use fake shadows. Make a plane, put a grayscale\ntexture in the plane that approximates a shadow,\ndraw it above the ground below your object.</p>\n<p>For example let's use this texture as a fake shadow</p>\n<div class=\"threejs_center\"><img src=\"../examples/resources/images/roundshadow.png\"></div>\n\n<p>We'll use some of the code from <a href=\"cameras.html\">the previous article</a>.</p>\n<p>Let's set the background color to white.</p>\n<pre class=\"prettyprint showlinemods notranslate lang-js\" translate=\"no\">const scene = new THREE.Scene();\n+scene.background = new THREE.Color('white');\n</pre>\n<p>Then we'll setup the same checkerboard ground but this time it's using\na <a href=\"/docs/#api/en/materials/MeshBasicMaterial\"><code class=\"notranslate\" translate=\"no\">MeshBasicMaterial</code></a> as we don't need lighting for the ground.</p>\n<pre class=\"prettyprint showlinemods notranslate lang-js\" translate=\"no\">+const loader = new THREE.TextureLoader();\n\n{\n  const planeSize = 40;\n\n-  const loader = new THREE.TextureLoader();\n  const texture = loader.load('resources/images/checker.png');\n  texture.wrapS = THREE.RepeatWrapping;\n  texture.wrapT = THREE.RepeatWrapping;\n  texture.magFilter = THREE.NearestFilter;\n  const repeats = planeSize / 2;\n  texture.repeat.set(repeats, repeats);\n\n  const planeGeo = new THREE.PlaneGeometry(planeSize, planeSize);\n  const planeMat = new THREE.MeshBasicMaterial({\n    map: texture,\n    side: THREE.DoubleSide,\n  });\n+  planeMat.color.setRGB(1.5, 1.5, 1.5);\n  const mesh = new THREE.Mesh(planeGeo, planeMat);\n  mesh.rotation.x = Math.PI * -.5;\n  scene.add(mesh);\n}\n</pre>\n<p>Note we're setting the color to <code class=\"notranslate\" translate=\"no\">1.5, 1.5, 1.5</code>. This will multiply the checkerboard\ntexture's colors by 1.5, 1.5, 1.5. Since the texture's colors are 0x808080 and 0xC0C0C0\nwhich is medium gray and light gray, multiplying them by 1.5 will give us a white and\nlight grey checkerboard.</p>\n<p>Let's load the shadow texture</p>\n<pre class=\"prettyprint showlinemods notranslate lang-js\" translate=\"no\">const shadowTexture = loader.load('resources/images/roundshadow.png');\n</pre>\n<p>and make an array to remember each sphere and associated objects.</p>\n<pre class=\"prettyprint showlinemods notranslate lang-js\" translate=\"no\">const sphereShadowBases = [];\n</pre>\n<p>Then we'll make a sphere geometry</p>\n<pre class=\"prettyprint showlinemods notranslate lang-js\" translate=\"no\">const sphereRadius = 1;\nconst sphereWidthDivisions = 32;\nconst sphereHeightDivisions = 16;\nconst sphereGeo = new THREE.SphereGeometry(sphereRadius, sphereWidthDivisions, sphereHeightDivisions);\n</pre>\n<p>And a plane geometry for the fake shadow</p>\n<pre class=\"prettyprint showlinemods notranslate lang-js\" translate=\"no\">const planeSize = 1;\nconst shadowGeo = new THREE.PlaneGeometry(planeSize, planeSize);\n</pre>\n<p>Now we'll make a bunch of spheres. For each sphere we'll create a <code class=\"notranslate\" translate=\"no\">base</code>\n<a href=\"/docs/#api/en/core/Object3D\"><code class=\"notranslate\" translate=\"no\">THREE.Object3D</code></a> and we'll make both the shadow plane mesh and the sphere mesh\nchildren of the base. That way if we move the base both the sphere and the shadow\nwill move. We need to put the shadow slightly above the ground to prevent z-fighting.\nWe also set <code class=\"notranslate\" translate=\"no\">depthWrite</code> to false so that the shadows don't mess each other up.\nWe'll go over both of these issues in <a href=\"transparency.html\">another article</a>.\nThe shadow is a <a href=\"/docs/#api/en/materials/MeshBasicMaterial\"><code class=\"notranslate\" translate=\"no\">MeshBasicMaterial</code></a> because it doesn't need lighting.</p>\n<p>We make each sphere a different hue and then save off the base, the sphere mesh,\nthe shadow mesh and the initial y position of each sphere.</p>\n<pre class=\"prettyprint showlinemods notranslate lang-js\" translate=\"no\">const numSpheres = 15;\nfor (let i = 0; i &lt; numSpheres; ++i) {\n  // make a base for the shadow and the sphere\n  // so they move together.\n  const base = new THREE.Object3D();\n  scene.add(base);\n\n  // add the shadow to the base\n  // note: we make a new material for each sphere\n  // so we can set that sphere's material transparency\n  // separately.\n  const shadowMat = new THREE.MeshBasicMaterial({\n    map: shadowTexture,\n    transparent: true,    // so we can see the ground\n    depthWrite: false,    // so we don't have to sort\n  });\n  const shadowMesh = new THREE.Mesh(shadowGeo, shadowMat);\n  shadowMesh.position.y = 0.001;  // so we're above the ground slightly\n  shadowMesh.rotation.x = Math.PI * -.5;\n  const shadowSize = sphereRadius * 4;\n  shadowMesh.scale.set(shadowSize, shadowSize, shadowSize);\n  base.add(shadowMesh);\n\n  // add the sphere to the base\n  const u = i / numSpheres;   // goes from 0 to 1 as we iterate the spheres.\n  const sphereMat = new THREE.MeshPhongMaterial();\n  sphereMat.color.setHSL(u, 1, .75);\n  const sphereMesh = new THREE.Mesh(sphereGeo, sphereMat);\n  sphereMesh.position.set(0, sphereRadius + 2, 0);\n  base.add(sphereMesh);\n\n  // remember all 3 plus the y position\n  sphereShadowBases.push({base, sphereMesh, shadowMesh, y: sphereMesh.position.y});\n}\n</pre>\n<p>We setup 2 lights. One is a <a href=\"/docs/#api/en/lights/HemisphereLight\"><code class=\"notranslate\" translate=\"no\">HemisphereLight</code></a> with the intensity set to 2 to really\nbrighten things up.</p>\n<pre class=\"prettyprint showlinemods notranslate lang-js\" translate=\"no\">{\n  const skyColor = 0xB1E1FF;  // light blue\n  const groundColor = 0xB97A20;  // brownish orange\n  const intensity = 2;\n  const light = new THREE.HemisphereLight(skyColor, groundColor, intensity);\n  scene.add(light);\n}\n</pre>\n<p>The other is a <a href=\"/docs/#api/en/lights/DirectionalLight\"><code class=\"notranslate\" translate=\"no\">DirectionalLight</code></a> so the spheres get some definition</p>\n<pre class=\"prettyprint showlinemods notranslate lang-js\" translate=\"no\">{\n  const color = 0xFFFFFF;\n  const intensity = 1;\n  const light = new THREE.DirectionalLight(color, intensity);\n  light.position.set(0, 10, 5);\n  light.target.position.set(-5, 0, 0);\n  scene.add(light);\n  scene.add(light.target);\n}\n</pre>\n<p>It would render as is but let's animate there spheres.\nFor each sphere, shadow, base set we move the base in the xz plane, we\nmove the sphere up and down using <a href=\"/docs/#api/en/math/Math.abs(Math.sin(time))\"><code class=\"notranslate\" translate=\"no\">Math.abs(Math.sin(time))</code></a>\nwhich gives us a bouncy animation. And, we also set the shadow material's\nopacity so that as each sphere goes higher its shadow fades out.</p>\n<pre class=\"prettyprint showlinemods notranslate lang-js\" translate=\"no\">function render(time) {\n  time *= 0.001;  // convert to seconds\n\n  ...\n\n  sphereShadowBases.forEach((sphereShadowBase, ndx) =&gt; {\n    const {base, sphereMesh, shadowMesh, y} = sphereShadowBase;\n\n    // u is a value that goes from 0 to 1 as we iterate the spheres\n    const u = ndx / sphereShadowBases.length;\n\n    // compute a position for the base. This will move\n    // both the sphere and its shadow\n    const speed = time * .2;\n    const angle = speed + u * Math.PI * 2 * (ndx % 1 ? 1 : -1);\n    const radius = Math.sin(speed - ndx) * 10;\n    base.position.set(Math.cos(angle) * radius, 0, Math.sin(angle) * radius);\n\n    // yOff is a value that goes from 0 to 1\n    const yOff = Math.abs(Math.sin(time * 2 + ndx));\n    // move the sphere up and down\n    sphereMesh.position.y = y + THREE.MathUtils.lerp(-2, 2, yOff);\n    // fade the shadow as the sphere goes up\n    shadowMesh.material.opacity = THREE.MathUtils.lerp(1, .25, yOff);\n  });\n\n  ...\n</pre>\n<p>And here's 15 kind of bouncing balls.</p>\n<p></p><div translate=\"no\" class=\"threejs_example_container notranslate\">\n  <div></div>\n  <a class=\"threejs_center\" href=\"/manual/examples/shadows-fake.html\" target=\"_blank\">click here to open in a separate window</a>\n</div>\n\n<p></p>\n<p>In some apps it's common to use a round or oval shadow for everything but\nof course you could also use different shaped shadow textures. You might also\ngive the shadow a harder edge. A good example of using this type\nof shadow is <a href=\"https://www.google.com/search?tbm=isch&amp;q=animal+crossing+pocket+camp+screenshots\">Animal Crossing Pocket Camp</a>\nwhere you can see each character has a simple round shadow. It's effective and cheap.\n<a href=\"https://www.google.com/search?q=monument+valley+screenshots&amp;tbm=isch\">Monument Valley</a>\nappears to also use this kind of shadow for the main character.</p>\n<p>So, moving on to shadow maps, there are 3 lights which can cast shadows. The <a href=\"/docs/#api/en/lights/DirectionalLight\"><code class=\"notranslate\" translate=\"no\">DirectionalLight</code></a>,\nthe <a href=\"/docs/#api/en/lights/PointLight\"><code class=\"notranslate\" translate=\"no\">PointLight</code></a>, and the <a href=\"/docs/#api/en/lights/SpotLight\"><code class=\"notranslate\" translate=\"no\">SpotLight</code></a>.</p>\n<p>Let's start with the <a href=\"/docs/#api/en/lights/DirectionalLight\"><code class=\"notranslate\" translate=\"no\">DirectionalLight</code></a> with the helper example from <a href=\"lights.html\">the lights article</a>.</p>\n<p>The first thing we need to do is turn on shadows in the renderer.</p>\n<pre class=\"prettyprint showlinemods notranslate lang-js\" translate=\"no\">const renderer = new THREE.WebGLRenderer({antialias: true, canvas});\n+renderer.shadowMap.enabled = true;\n</pre>\n<p>Then we also need to tell the light to cast a shadow</p>\n<pre class=\"prettyprint showlinemods notranslate lang-js\" translate=\"no\">const light = new THREE.DirectionalLight(color, intensity);\n+light.castShadow = true;\n</pre>\n<p>We also need to go to each mesh in the scene and decide if it should\nboth cast shadows and/or receive shadows.</p>\n<p>Let's make the plane (the ground) only receive shadows since we don't\nreally care what happens underneath.</p>\n<pre class=\"prettyprint showlinemods notranslate lang-js\" translate=\"no\">const mesh = new THREE.Mesh(planeGeo, planeMat);\nmesh.receiveShadow = true;\n</pre>\n<p>For the cube and the sphere let's have them both receive and cast shadows</p>\n<pre class=\"prettyprint showlinemods notranslate lang-js\" translate=\"no\">const mesh = new THREE.Mesh(cubeGeo, cubeMat);\nmesh.castShadow = true;\nmesh.receiveShadow = true;\n\n...\n\nconst mesh = new THREE.Mesh(sphereGeo, sphereMat);\nmesh.castShadow = true;\nmesh.receiveShadow = true;\n</pre>\n<p>And then we run it.</p>\n<p></p><div translate=\"no\" class=\"threejs_example_container notranslate\">\n  <div></div>\n  <a class=\"threejs_center\" href=\"/manual/examples/shadows-directional-light.html\" target=\"_blank\">click here to open in a separate window</a>\n</div>\n\n<p></p>\n<p>What happened? Why are parts of the shadows missing?</p>\n<p>The reason is shadow maps are created by rendering the scene from the point\nof view of the light. In this case there is a camera at the <a href=\"/docs/#api/en/lights/DirectionalLight\"><code class=\"notranslate\" translate=\"no\">DirectionalLight</code></a>\nthat is looking at its target. Just like <a href=\"cameras.html\">the camera's we previously covered</a>\nthe light's shadow camera defines an area inside of which\nthe shadows get rendered. In the example above that area is too small.</p>\n<p>In order to visualize that area we can get the light's shadow camera and add\na <a href=\"/docs/#api/en/helpers/CameraHelper\"><code class=\"notranslate\" translate=\"no\">CameraHelper</code></a> to the scene.</p>\n<pre class=\"prettyprint showlinemods notranslate lang-js\" translate=\"no\">const cameraHelper = new THREE.CameraHelper(light.shadow.camera);\nscene.add(cameraHelper);\n</pre>\n<p>And now you can see the area for which shadows are cast and received.</p>\n<p></p><div translate=\"no\" class=\"threejs_example_container notranslate\">\n  <div></div>\n  <a class=\"threejs_center\" href=\"/manual/examples/shadows-directional-light-with-camera-helper.html\" target=\"_blank\">click here to open in a separate window</a>\n</div>\n\n<p></p>\n<p>Adjust the target x value back and forth and it should be pretty clear that only\nwhat's inside the light's shadow camera box is where shadows are drawn.</p>\n<p>We can adjust the size of that box by adjusting the light's shadow camera.</p>\n<p>Let's add some GUI setting to adjust the light's shadow camera box. Since a\n<a href=\"/docs/#api/en/lights/DirectionalLight\"><code class=\"notranslate\" translate=\"no\">DirectionalLight</code></a> represents light all going in a parallel direction, the\n<a href=\"/docs/#api/en/lights/DirectionalLight\"><code class=\"notranslate\" translate=\"no\">DirectionalLight</code></a> uses an <a href=\"/docs/#api/en/cameras/OrthographicCamera\"><code class=\"notranslate\" translate=\"no\">OrthographicCamera</code></a> for its shadow camera.\nWe went over how an <a href=\"/docs/#api/en/cameras/OrthographicCamera\"><code class=\"notranslate\" translate=\"no\">OrthographicCamera</code></a> works in <a href=\"cameras.html\">the previous article about cameras</a>.</p>\n<p>Recall an <a href=\"/docs/#api/en/cameras/OrthographicCamera\"><code class=\"notranslate\" translate=\"no\">OrthographicCamera</code></a> defines\nits box or <em>view frustum</em> by its <code class=\"notranslate\" translate=\"no\">left</code>, <code class=\"notranslate\" translate=\"no\">right</code>, <code class=\"notranslate\" translate=\"no\">top</code>, <code class=\"notranslate\" translate=\"no\">bottom</code>, <code class=\"notranslate\" translate=\"no\">near</code>, <code class=\"notranslate\" translate=\"no\">far</code>,\nand <code class=\"notranslate\" translate=\"no\">zoom</code> properties.</p>\n<p>Again let's make a helper class for the lil-gui. We'll make a <code class=\"notranslate\" translate=\"no\">DimensionGUIHelper</code>\nthat we'll pass an object and 2 properties. It will present one property that lil-gui\ncan adjust and in response will set the two properties one positive and one negative.\nWe can use this to set <code class=\"notranslate\" translate=\"no\">left</code> and <code class=\"notranslate\" translate=\"no\">right</code> as <code class=\"notranslate\" translate=\"no\">width</code> and <code class=\"notranslate\" translate=\"no\">up</code> and <code class=\"notranslate\" translate=\"no\">down</code> as <code class=\"notranslate\" translate=\"no\">height</code>.</p>\n<pre class=\"prettyprint showlinemods notranslate lang-js\" translate=\"no\">class DimensionGUIHelper {\n  constructor(obj, minProp, maxProp) {\n    this.obj = obj;\n    this.minProp = minProp;\n    this.maxProp = maxProp;\n  }\n  get value() {\n    return this.obj[this.maxProp] * 2;\n  }\n  set value(v) {\n    this.obj[this.maxProp] = v /  2;\n    this.obj[this.minProp] = v / -2;\n  }\n}\n</pre>\n<p>We'll also use the <code class=\"notranslate\" translate=\"no\">MinMaxGUIHelper</code> we created in the <a href=\"cameras.html\">camera article</a>\nto adjust <code class=\"notranslate\" translate=\"no\">near</code> and <code class=\"notranslate\" translate=\"no\">far</code>.</p>\n<pre class=\"prettyprint showlinemods notranslate lang-js\" translate=\"no\">const gui = new GUI();\ngui.addColor(new ColorGUIHelper(light, 'color'), 'value').name('color');\ngui.add(light, 'intensity', 0, 2, 0.01);\n+{\n+  const folder = gui.addFolder('Shadow Camera');\n+  folder.open();\n+  folder.add(new DimensionGUIHelper(light.shadow.camera, 'left', 'right'), 'value', 1, 100)\n+    .name('width')\n+    .onChange(updateCamera);\n+  folder.add(new DimensionGUIHelper(light.shadow.camera, 'bottom', 'top'), 'value', 1, 100)\n+    .name('height')\n+    .onChange(updateCamera);\n+  const minMaxGUIHelper = new MinMaxGUIHelper(light.shadow.camera, 'near', 'far', 0.1);\n+  folder.add(minMaxGUIHelper, 'min', 0.1, 50, 0.1).name('near').onChange(updateCamera);\n+  folder.add(minMaxGUIHelper, 'max', 0.1, 50, 0.1).name('far').onChange(updateCamera);\n+  folder.add(light.shadow.camera, 'zoom', 0.01, 1.5, 0.01).onChange(updateCamera);\n+}\n</pre>\n<p>We tell the GUI to call our <code class=\"notranslate\" translate=\"no\">updateCamera</code> function anytime anything changes.\nLet's write that function to update the light, the helper for the light, the\nlight's shadow camera, and the helper showing the light's shadow camera.</p>\n<pre class=\"prettyprint showlinemods notranslate lang-js\" translate=\"no\">function updateCamera() {\n  // update the light target's matrixWorld because it's needed by the helper\n  light.target.updateMatrixWorld();\n  helper.update();\n  // update the light's shadow camera's projection matrix\n  light.shadow.camera.updateProjectionMatrix();\n  // and now update the camera helper we're using to show the light's shadow camera\n  cameraHelper.update();\n}\nupdateCamera();\n</pre>\n<p>And now that we've given the light's shadow camera a GUI we can play with the values.</p>\n<p></p><div translate=\"no\" class=\"threejs_example_container notranslate\">\n  <div></div>\n  <a class=\"threejs_center\" href=\"/manual/examples/shadows-directional-light-with-camera-gui.html\" target=\"_blank\">click here to open in a separate window</a>\n</div>\n\n<p></p>\n<p>Set the <code class=\"notranslate\" translate=\"no\">width</code> and <code class=\"notranslate\" translate=\"no\">height</code> to about 30 and you can see the shadows are correct\nand the areas that need to be in shadow for this scene are entirely covered.</p>\n<p>But this brings up the question, why not just set <code class=\"notranslate\" translate=\"no\">width</code> and <code class=\"notranslate\" translate=\"no\">height</code> to some\ngiant numbers to just cover everything? Set the <code class=\"notranslate\" translate=\"no\">width</code> and <code class=\"notranslate\" translate=\"no\">height</code> to 100\nand you might see something like this</p>\n<div class=\"threejs_center\"><img src=\"../resources/images/low-res-shadow-map.png\" style=\"width: 369px\"></div>\n\n<p>What's going on with these low-res shadows?!</p>\n<p>This issue is yet another shadow related setting to be aware of.\nShadow maps are textures the shadows get drawn into.\nThose textures have a size. The shadow camera's area we set above is stretched\nacross that size. That means the larger area you set, the more blocky your shadows will\nbe.</p>\n<p>You can set the resolution of the shadow map's texture by setting <code class=\"notranslate\" translate=\"no\">light.shadow.mapSize.width</code>\nand <code class=\"notranslate\" translate=\"no\">light.shadow.mapSize.height</code>. They default to 512x512.\nThe larger you make them the more memory they take and the slower they are to compute so you want\nto set them as small as you can and still make your scene work. The same is true with the\nlight's shadow camera area. Smaller means better looking shadows so make the area as small as you\ncan and still cover your scene. Be aware that each user's machine has a maximum texture size\nallowed which is available on the renderer as <a href=\"/docs/#api/en/renderers/WebGLRenderer#capabilities\"><code class=\"notranslate\" translate=\"no\">renderer.capabilities.maxTextureSize</code></a>.</p>\n<!--\nOk but what about <code class=\"notranslate\" translate=\"no\">near</code> and <code class=\"notranslate\" translate=\"no\">far</code> I hear you thinking. Can we set <code class=\"notranslate\" translate=\"no\">near</code> to 0.00001 and far to <code class=\"notranslate\" translate=\"no\">100000000</code>\n-->\n<p>Switching to the <a href=\"/docs/#api/en/lights/SpotLight\"><code class=\"notranslate\" translate=\"no\">SpotLight</code></a> the light's shadow camera becomes a <a href=\"/docs/#api/en/cameras/PerspectiveCamera\"><code class=\"notranslate\" translate=\"no\">PerspectiveCamera</code></a>. Unlike the <a href=\"/docs/#api/en/lights/DirectionalLight\"><code class=\"notranslate\" translate=\"no\">DirectionalLight</code></a>'s shadow camera\nwhere we could manually set most its settings, <a href=\"/docs/#api/en/lights/SpotLight\"><code class=\"notranslate\" translate=\"no\">SpotLight</code></a>'s shadow camera is controlled by the <a href=\"/docs/#api/en/lights/SpotLight\"><code class=\"notranslate\" translate=\"no\">SpotLight</code></a> itself. The <code class=\"notranslate\" translate=\"no\">fov</code> for the shadow\ncamera is directly connected to the <a href=\"/docs/#api/en/lights/SpotLight\"><code class=\"notranslate\" translate=\"no\">SpotLight</code></a>'s <code class=\"notranslate\" translate=\"no\">angle</code> setting.\nThe <code class=\"notranslate\" translate=\"no\">aspect</code> is set automatically based on the size of the shadow map.</p>\n<pre class=\"prettyprint showlinemods notranslate lang-js\" translate=\"no\">-const light = new THREE.DirectionalLight(color, intensity);\n+const light = new THREE.SpotLight(color, intensity);\n</pre>\n<p>and we added back in the <code class=\"notranslate\" translate=\"no\">penumbra</code> and <code class=\"notranslate\" translate=\"no\">angle</code> settings\nfrom our <a href=\"lights.html\">article about lights</a>.</p>\n<p></p><div translate=\"no\" class=\"threejs_example_container notranslate\">\n  <div></div>\n  <a class=\"threejs_center\" href=\"/manual/examples/shadows-spot-light-with-camera-gui.html\" target=\"_blank\">click here to open in a separate window</a>\n</div>\n\n<p></p>\n<!--\nYou can notice, just like the last example if we set the angle high\nthen the shadow map, the texture is spread over a very large area and\nthe resolution of our shadows gets really low.\n\ndiv class=\"threejs_center\"><img src=\"../resources/images/low-res-shadow-map-spotlight.png\" style=\"width: 344px\"></div>\n\nYou can increase the size of the shadow map as mentioned above. You can\nalso blur the result\n\n<div translate=\"no\" class=\"threejs_example_container notranslate\">\n  <div><iframe class=\"threejs_example notranslate\" translate=\"no\" style=\" \" src=\"/manual/examples/resources/editor.html?url=/manual/examples/shadows-spot-light-with-shadow-radius\"></iframe></div>\n  <a class=\"threejs_center\" href=\"/manual/examples/shadows-spot-light-with-shadow-radius\" target=\"_blank\">click here to open in a separate window</a>\n</div>\n\n\n-->\n<p>And finally there's shadows with a <a href=\"/docs/#api/en/lights/PointLight\"><code class=\"notranslate\" translate=\"no\">PointLight</code></a>. Since a <a href=\"/docs/#api/en/lights/PointLight\"><code class=\"notranslate\" translate=\"no\">PointLight</code></a>\nshines in all directions the only relevant settings are <code class=\"notranslate\" translate=\"no\">near</code> and <code class=\"notranslate\" translate=\"no\">far</code>.\nOtherwise the <a href=\"/docs/#api/en/lights/PointLight\"><code class=\"notranslate\" translate=\"no\">PointLight</code></a> shadow is effectively 6 <a href=\"/docs/#api/en/lights/SpotLight\"><code class=\"notranslate\" translate=\"no\">SpotLight</code></a> shadows\neach one pointing to the face of a cube around the light. This means\n<a href=\"/docs/#api/en/lights/PointLight\"><code class=\"notranslate\" translate=\"no\">PointLight</code></a> shadows are much slower since the entire scene must be\ndrawn 6 times, one for each direction.</p>\n<p>Let's put a box around our scene so we can see shadows on the walls\nand ceiling. We'll set the material's <code class=\"notranslate\" translate=\"no\">side</code> property to <code class=\"notranslate\" translate=\"no\">THREE.BackSide</code>\nso we render the inside of the box instead of the outside. Like the floor\nwe'll set it only to receive shadows. Also we'll set the position of the\nbox so its bottom is slightly below the floor so the floor and the bottom\nof the box don't z-fight.</p>\n<pre class=\"prettyprint showlinemods notranslate lang-js\" translate=\"no\">{\n  const cubeSize = 30;\n  const cubeGeo = new THREE.BoxGeometry(cubeSize, cubeSize, cubeSize);\n  const cubeMat = new THREE.MeshPhongMaterial({\n    color: '#CCC',\n    side: THREE.BackSide,\n  });\n  const mesh = new THREE.Mesh(cubeGeo, cubeMat);\n  mesh.receiveShadow = true;\n  mesh.position.set(0, cubeSize / 2 - 0.1, 0);\n  scene.add(mesh);\n}\n</pre>\n<p>And of course we need to switch the light to a <a href=\"/docs/#api/en/lights/PointLight\"><code class=\"notranslate\" translate=\"no\">PointLight</code></a>.</p>\n<pre class=\"prettyprint showlinemods notranslate lang-js\" translate=\"no\">-const light = new THREE.SpotLight(color, intensity);\n+const light = new THREE.PointLight(color, intensity);\n\n....\n\n// so we can easily see where the point light is\n+const helper = new THREE.PointLightHelper(light);\n+scene.add(helper);\n</pre>\n<p></p><div translate=\"no\" class=\"threejs_example_container notranslate\">\n  <div></div>\n  <a class=\"threejs_center\" href=\"/manual/examples/shadows-point-light.html\" target=\"_blank\">click here to open in a separate window</a>\n</div>\n\n<p></p>\n<p>Use the <code class=\"notranslate\" translate=\"no\">position</code> GUI settings to move the light around\nand you'll see the shadows fall on all the walls. You can\nalso adjust <code class=\"notranslate\" translate=\"no\">near</code> and <code class=\"notranslate\" translate=\"no\">far</code> settings and see just like\nthe other shadows when things are closer than <code class=\"notranslate\" translate=\"no\">near</code> they\nno longer receive a shadow and they are further than <code class=\"notranslate\" translate=\"no\">far</code>\nthey are always in shadow.</p>\n<!--\nself shadow, shadow acne\n-->\n\n        </div>\n      </div>\n    </div>",
    "success": true,
    "error": "",
    "crawled_at": 1786376809.300204,
    "is_shell": false
  },
  {
    "url": "https://threejs.org/manual/en/fog.html",
    "title": "Fog",
    "content": "<div class=\"container\">\n      <div class=\"lesson-title\">\n        <h1>Fog</h1>\n      </div>\n      <div class=\"lesson\">\n        <div class=\"lesson-main\">\n          <p>This article is part of a series of articles about three.js. The\nfirst article is <a href=\"fundamentals.html\">three.js fundamentals</a>. If\nyou haven't read that yet and you're new to three.js you might want to\nconsider starting there. If you haven't read about cameras you might\nwant to start with <a href=\"cameras.html\">this article</a>.</p>\n<p>Fog in a 3D engine is generally a way of fading to a specific color\nbased on the distance from the camera. In three.js you add fog by\ncreating <a href=\"/docs/#api/en/scenes/Fog\"><code class=\"notranslate\" translate=\"no\">Fog</code></a> or <a href=\"/docs/#api/en/scenes/FogExp2\"><code class=\"notranslate\" translate=\"no\">FogExp2</code></a> object and setting it on the scene's\n<a href=\"/docs/#api/en/scenes/Scene#fog\"><code class=\"notranslate\" translate=\"no\">fog</code></a> property.</p>\n<p><a href=\"/docs/#api/en/scenes/Fog\"><code class=\"notranslate\" translate=\"no\">Fog</code></a> lets you choose <code class=\"notranslate\" translate=\"no\">near</code> and <code class=\"notranslate\" translate=\"no\">far</code> settings which are distances\nfrom the camera. Anything closer than <code class=\"notranslate\" translate=\"no\">near</code> is unaffected by fog.\nAnything further than <code class=\"notranslate\" translate=\"no\">far</code> is completely the fog color. Parts between\n<code class=\"notranslate\" translate=\"no\">near</code> and <code class=\"notranslate\" translate=\"no\">far</code> fade from their material color to the fog color.</p>\n<p>There's also <a href=\"/docs/#api/en/scenes/FogExp2\"><code class=\"notranslate\" translate=\"no\">FogExp2</code></a> which grows exponentially with distance from the camera.</p>\n<p>To use either type of fog you create one and and assign it to the scene as in</p>\n<pre class=\"prettyprint showlinemods notranslate lang-js\" translate=\"no\">const scene = new THREE.Scene();\n{\n  const color = 0xFFFFFF;  // white\n  const near = 10;\n  const far = 100;\n  scene.fog = new THREE.Fog(color, near, far);\n}\n</pre>\n<p>or for <a href=\"/docs/#api/en/scenes/FogExp2\"><code class=\"notranslate\" translate=\"no\">FogExp2</code></a> it would be</p>\n<pre class=\"prettyprint showlinemods notranslate lang-js\" translate=\"no\">const scene = new THREE.Scene();\n{\n  const color = 0xFFFFFF;\n  const density = 0.1;\n  scene.fog = new THREE.FogExp2(color, density);\n}\n</pre>\n<p><a href=\"/docs/#api/en/scenes/FogExp2\"><code class=\"notranslate\" translate=\"no\">FogExp2</code></a> is closer to reality but <a href=\"/docs/#api/en/scenes/Fog\"><code class=\"notranslate\" translate=\"no\">Fog</code></a> is used\nmore commonly since it lets you choose a place to apply\nthe fog so you can decide to show a clear scene\nup to a certain distance and then fade out to some color\npast that distance.</p>\n<div class=\"spread\">\n  <div>\n    <div data-diagram=\"fog\" style=\"height: 300px;\"></div>\n    <div class=\"code\">THREE.Fog</div>\n  </div>\n  <div>\n    <div data-diagram=\"fogExp2\" style=\"height: 300px;\"></div>\n    <div class=\"code\">THREE.FogExp2</div>\n  </div>\n</div>\n\n<p>It's important to note that the fog is applied to <em>things that are rendered</em>.\nIt is part of the calculation of each pixel of the color of the object.\nWhat that means is if you want your scene to fade to a certain color you\nneed to set the fog <strong>and</strong> the background color to the same color.\nThe background color is set using the\n<a href=\"/docs/#api/en/scenes/Scene#background\"><code class=\"notranslate\" translate=\"no\">scene.background</code></a>\nproperty. To pick a background color you attach a <a href=\"/docs/#api/en/math/Color\"><code class=\"notranslate\" translate=\"no\">THREE.Color</code></a> to it. For example</p>\n<pre class=\"prettyprint showlinemods notranslate lang-js\" translate=\"no\">scene.background = new THREE.Color('#F00');  // red\n</pre>\n<div class=\"spread\">\n  <div>\n    <div data-diagram=\"fogBlueBackgroundRed\" style=\"height: 300px;\" class=\"border\"></div>\n    <div class=\"code\">fog blue, background red</div>\n  </div>\n  <div>\n    <div data-diagram=\"fogBlueBackgroundBlue\" style=\"height: 300px;\" class=\"border\"></div>\n    <div class=\"code\">fog blue, background blue</div>\n  </div>\n</div>\n\n<p>Here is one of our previous examples with fog added. The only addition\nis right after setting up the scene we add the fog and set the scene's\nbackground color</p>\n<pre class=\"prettyprint showlinemods notranslate lang-js\" translate=\"no\">const scene = new THREE.Scene();\n\n+{\n+  const near = 1;\n+  const far = 2;\n+  const color = 'lightblue';\n+  scene.fog = new THREE.Fog(color, near, far);\n+  scene.background = new THREE.Color(color);\n+}\n</pre>\n<p>In the example below the camera's <code class=\"notranslate\" translate=\"no\">near</code> is 0.1 and its <code class=\"notranslate\" translate=\"no\">far</code> is 5.\nThe camera is at <code class=\"notranslate\" translate=\"no\">z = 2</code>. The cubes are 1 unit large and at Z = 0.\nThis means with a fog setting of <code class=\"notranslate\" translate=\"no\">near = 1</code> and <code class=\"notranslate\" translate=\"no\">far = 2</code> the cubes\nwill fade out right around their center.</p>\n<p></p><div translate=\"no\" class=\"threejs_example_container notranslate\">\n  <div></div>\n  <a class=\"threejs_center\" href=\"/manual/examples/fog.html\" target=\"_blank\">click here to open in a separate window</a>\n</div>\n\n<p></p>\n<p>Let's add an interface so we can adjust the fog. Again we'll use\n<a href=\"https://github.com/georgealways/lil-gui\">lil-gui</a>. lil-gui takes\nan object and a property and automagically makes an interface\nfor that type of property. We could just simply let it manipulate\nthe fog's <code class=\"notranslate\" translate=\"no\">near</code> and <code class=\"notranslate\" translate=\"no\">far</code> properties but it's invalid to have\n<code class=\"notranslate\" translate=\"no\">near</code> be greater than <code class=\"notranslate\" translate=\"no\">far</code> so let's make a helper so lil-gui\ncan manipulate a <code class=\"notranslate\" translate=\"no\">near</code> and <code class=\"notranslate\" translate=\"no\">far</code> property but we'll make sure <code class=\"notranslate\" translate=\"no\">near</code>\nis less than or equal to <code class=\"notranslate\" translate=\"no\">far</code> and <code class=\"notranslate\" translate=\"no\">far</code> is greater than or equal <code class=\"notranslate\" translate=\"no\">near</code>.</p>\n<pre class=\"prettyprint showlinemods notranslate lang-js\" translate=\"no\">// We use this class to pass to lil-gui\n// so when it manipulates near or far\n// near is never &gt; far and far is never &lt; near\nclass FogGUIHelper {\n  constructor(fog) {\n    this.fog = fog;\n  }\n  get near() {\n    return this.fog.near;\n  }\n  set near(v) {\n    this.fog.near = v;\n    this.fog.far = Math.max(this.fog.far, v);\n  }\n  get far() {\n    return this.fog.far;\n  }\n  set far(v) {\n    this.fog.far = v;\n    this.fog.near = Math.min(this.fog.near, v);\n  }\n}\n</pre>\n<p>We can then add it like this</p>\n<pre class=\"prettyprint showlinemods notranslate lang-js\" translate=\"no\">{\n  const near = 1;\n  const far = 2;\n  const color = 'lightblue';\n  scene.fog = new THREE.Fog(color, near, far);\n  scene.background = new THREE.Color(color);\n+\n+  const fogGUIHelper = new FogGUIHelper(scene.fog);\n+  gui.add(fogGUIHelper, 'near', near, far).listen();\n+  gui.add(fogGUIHelper, 'far', near, far).listen();\n}\n</pre>\n<p>The <code class=\"notranslate\" translate=\"no\">near</code> and <code class=\"notranslate\" translate=\"no\">far</code> parameters set the minimum and maximum values\nfor adjusting the fog. They are set when we setup the camera.</p>\n<p>The <code class=\"notranslate\" translate=\"no\">.listen()</code> at the end of the last 2 lines tells lil-gui to <em>listen</em>\nfor changes. That way when we change <code class=\"notranslate\" translate=\"no\">near</code> because of an edit to <code class=\"notranslate\" translate=\"no\">far</code>\nor we change <code class=\"notranslate\" translate=\"no\">far</code> in response to an edit to <code class=\"notranslate\" translate=\"no\">near</code> lil-gui will update\nthe other property's UI for us.</p>\n<p>It might also be nice to be able to change the fog color but like was\nmentioned above we need to keep both the fog color and the background\ncolor in sync. So, let's add another <em>virtual</em> property to our helper\nthat will set both colors when lil-gui manipulates it.</p>\n<p>lil-gui can manipulate colors in 4 ways, as a CSS 6 digit hex string (eg: <code class=\"notranslate\" translate=\"no\">#112233</code>). As an hue, saturation, value, object (eg: <code class=\"notranslate\" translate=\"no\">{h: 60, s: 1, v: }</code>).\nAs an RGB array (eg: <code class=\"notranslate\" translate=\"no\">[255, 128, 64]</code>). Or, as an RGBA array (eg: <code class=\"notranslate\" translate=\"no\">[127, 200, 75, 0.3]</code>).</p>\n<p>It's easiest for our purpose to use the hex string version since that way\nlil-gui is only manipulating a single value. Fortunately <a href=\"/docs/#api/en/math/Color\"><code class=\"notranslate\" translate=\"no\">THREE.Color</code></a>\nas a <a href=\"/docs/#api/en/math/Color#getHexString\"><code class=\"notranslate\" translate=\"no\">getHexString</code></a> method\nwe get use to easily get such a string, we just have to prepend a '#' to the front.</p>\n<pre class=\"prettyprint showlinemods notranslate lang-js\" translate=\"no\">// We use this class to pass to lil-gui\n// so when it manipulates near or far\n// near is never &gt; far and far is never &lt; near\n+// Also when lil-gui manipulates color we'll\n+// update both the fog and background colors.\nclass FogGUIHelper {\n*  constructor(fog, backgroundColor) {\n    this.fog = fog;\n+    this.backgroundColor = backgroundColor;\n  }\n  get near() {\n    return this.fog.near;\n  }\n  set near(v) {\n    this.fog.near = v;\n    this.fog.far = Math.max(this.fog.far, v);\n  }\n  get far() {\n    return this.fog.far;\n  }\n  set far(v) {\n    this.fog.far = v;\n    this.fog.near = Math.min(this.fog.near, v);\n  }\n+  get color() {\n+    return <code class=\"notranslate\" translate=\"no\">#${this.fog.color.getHexString()}</code>;\n+  }\n+  set color(hexString) {\n+    this.fog.color.set(hexString);\n+    this.backgroundColor.set(hexString);\n+  }\n}\n</pre>\n<p>We then call <code class=\"notranslate\" translate=\"no\">gui.addColor</code> to add a color UI for our helper's virtual property.</p>\n<pre class=\"prettyprint showlinemods notranslate lang-js\" translate=\"no\">{\n  const near = 1;\n  const far = 2;\n  const color = 'lightblue';\n  scene.fog = new THREE.Fog(color, near, far);\n  scene.background = new THREE.Color(color);\n\n*  const fogGUIHelper = new FogGUIHelper(scene.fog, scene.background);\n  gui.add(fogGUIHelper, 'near', near, far).listen();\n  gui.add(fogGUIHelper, 'far', near, far).listen();\n+  gui.addColor(fogGUIHelper, 'color');\n}\n</pre>\n<p></p><div translate=\"no\" class=\"threejs_example_container notranslate\">\n  <div></div>\n  <a class=\"threejs_center\" href=\"/manual/examples/fog-gui.html\" target=\"_blank\">click here to open in a separate window</a>\n</div>\n\n<p></p>\n<p>You can see setting <code class=\"notranslate\" translate=\"no\">near</code> to like 1.9 and <code class=\"notranslate\" translate=\"no\">far</code> to 2.0 gives\na very sharp transition between un-fogged and completely fogged.\nwhere as <code class=\"notranslate\" translate=\"no\">near</code> = 1.1 and <code class=\"notranslate\" translate=\"no\">far</code> = 2.9 should just about be\nthe smoothest given our cubes are spinning 2 units away from the camera.</p>\n<p>One last thing, there is a boolean <a href=\"/docs/#api/en/materials/Material#fog\"><code class=\"notranslate\" translate=\"no\">fog</code></a>\nproperty on a material for whether or not objects rendered\nwith that material are affected by fog. It defaults to <code class=\"notranslate\" translate=\"no\">true</code>\nfor most materials. As an example of why you might want\nto turn the fog off, imagine you're making a 3D vehicle\nsimulator with a view from the driver's seat or cockpit.\nYou probably want the fog off for everything inside the vehicle when\nviewing from inside the vehicle.</p>\n<p>A better example might be a house\nand thick fog outside house. Let's say the fog is set to start\n2 meters away (near = 2) and completely fogged out at 4 meters (far = 4).\nRooms are longer than 2 meters and the house is probably longer\nthan 4 meters so you need to set the materials for the inside\nof the house to not apply fog otherwise when standing inside the\nhouse looking outside the wall at the far end of the room will look\nlike it's in the fog.</p>\n<div class=\"spread\">\n  <div>\n    <div data-diagram=\"fogHouseAll\" style=\"height: 300px;\" class=\"border\"></div>\n    <div class=\"code\">fog: true, all</div>\n  </div>\n</div>\n\n<p>Notice the walls and ceiling at the far end of the room are getting fog applied.\nBy turning fog off on the materials for the house we can fix that issue.</p>\n<div class=\"spread\">\n  <div>\n    <div data-diagram=\"fogHouseInsideNoFog\" style=\"height: 300px;\" class=\"border\"></div>\n    <div class=\"code\">fog: true, only outside materials</div>\n  </div>\n</div>\n\n<p><canvas id=\"c\"></canvas></p>\n\n\n        </div>\n      </div>\n    </div>\n\n  \n  \n\n\n\n\n<canvas id=\"c\" data-engine=\"three.js r185\"></canvas>",
    "success": true,
    "error": "",
    "crawled_at": 1786376809.300204,
    "is_shell": false
  },
  {
    "url": "https://threejs.org/manual/en/rendertargets.html",
    "title": "Render Targets",
    "content": "<div class=\"container\">\n      <div class=\"lesson-title\">\n        <h1>Render Targets</h1>\n      </div>\n      <div class=\"lesson\">\n        <div class=\"lesson-main\">\n          <p>A render target in three.js is basically a texture you can render to.\nAfter you render to it you can use that texture like any other texture.</p>\n<p>Let's make a simple example. We'll start with an example from <a href=\"responsive.html\">the article on responsiveness</a>.</p>\n<p>Rendering to a render target is almost exactly the same as normal rendering. First we create a <a href=\"/docs/#api/en/renderers/WebGLRenderTarget\"><code class=\"notranslate\" translate=\"no\">WebGLRenderTarget</code></a>.</p>\n<pre class=\"prettyprint showlinemods notranslate lang-js\" translate=\"no\">const rtWidth = 512;\nconst rtHeight = 512;\nconst renderTarget = new THREE.WebGLRenderTarget(rtWidth, rtHeight);\n</pre>\n<p>Then we need a <a href=\"/docs/#api/en/cameras/Camera\"><code class=\"notranslate\" translate=\"no\">Camera</code></a> and a <a href=\"/docs/#api/en/scenes/Scene\"><code class=\"notranslate\" translate=\"no\">Scene</code></a></p>\n<pre class=\"prettyprint showlinemods notranslate lang-js\" translate=\"no\">const rtFov = 75;\nconst rtAspect = rtWidth / rtHeight;\nconst rtNear = 0.1;\nconst rtFar = 5;\nconst rtCamera = new THREE.PerspectiveCamera(rtFov, rtAspect, rtNear, rtFar);\nrtCamera.position.z = 2;\n\nconst rtScene = new THREE.Scene();\nrtScene.background = new THREE.Color('red');\n</pre>\n<p>Notice we set the aspect to the aspect for the render target, not the canvas.\nThe correct aspect to use depends on what we are rendering for. In this case\nwe'll use the render target's texture on the side of a cube. Since faces of\nthe cube are square we want an aspect of 1.0.</p>\n<p>We fill the scene with stuff. In this case we're using the light and the 3 cubes <a href=\"responsive.html\">from the previous article</a>.</p>\n<pre class=\"prettyprint showlinemods notranslate lang-js\" translate=\"no\">{\n  const color = 0xFFFFFF;\n  const intensity = 1;\n  const light = new THREE.DirectionalLight(color, intensity);\n  light.position.set(-1, 2, 4);\n*  rtScene.add(light);\n}\n\nconst boxWidth = 1;\nconst boxHeight = 1;\nconst boxDepth = 1;\nconst geometry = new THREE.BoxGeometry(boxWidth, boxHeight, boxDepth);\n\nfunction makeInstance(geometry, color, x) {\n  const material = new THREE.MeshPhongMaterial({color});\n\n  const cube = new THREE.Mesh(geometry, material);\n*  rtScene.add(cube);\n\n  cube.position.x = x;\n\n  return cube;\n}\n\n*const rtCubes = [\n  makeInstance(geometry, 0x44aa88,  0),\n  makeInstance(geometry, 0x8844aa, -2),\n  makeInstance(geometry, 0xaa8844,  2),\n];\n</pre>\n<p>The <a href=\"/docs/#api/en/scenes/Scene\"><code class=\"notranslate\" translate=\"no\">Scene</code></a> and <a href=\"/docs/#api/en/cameras/Camera\"><code class=\"notranslate\" translate=\"no\">Camera</code></a> from the previous article are still there. We'll use them to render to the canvas.\nWe just need to add stuff to render.</p>\n<p>Let's add a cube that uses the render target's texture.</p>\n<pre class=\"prettyprint showlinemods notranslate lang-js\" translate=\"no\">const material = new THREE.MeshPhongMaterial({\n  map: renderTarget.texture,\n});\nconst cube = new THREE.Mesh(geometry, material);\nscene.add(cube);\n</pre>\n<p>Now at render time first we render the render target scene to the render target.</p>\n<pre class=\"prettyprint showlinemods notranslate lang-js\" translate=\"no\">function render(time) {\n  time *= 0.001;\n\n  ...\n\n  // rotate all the cubes in the render target scene\n  rtCubes.forEach((cube, ndx) =&gt; {\n    const speed = 1 + ndx * .1;\n    const rot = time * speed;\n    cube.rotation.x = rot;\n    cube.rotation.y = rot;\n  });\n\n  // draw render target scene to render target\n  renderer.setRenderTarget(renderTarget);\n  renderer.render(rtScene, rtCamera);\n  renderer.setRenderTarget(null);\n</pre>\n<p>Then we render the scene with the single cube that is using the render target's texture to the canvas.</p>\n<pre class=\"prettyprint showlinemods notranslate lang-js\" translate=\"no\">  // rotate the cube in the scene\n  cube.rotation.x = time;\n  cube.rotation.y = time * 1.1;\n\n  // render the scene to the canvas\n  renderer.render(scene, camera);\n</pre>\n<p>And voilà</p>\n<p></p><div translate=\"no\" class=\"threejs_example_container notranslate\">\n  <div></div>\n  <a class=\"threejs_center\" href=\"/manual/examples/render-target.html\" target=\"_blank\">click here to open in a separate window</a>\n</div>\n\n<p></p>\n<p>The cube is red because we set the <code class=\"notranslate\" translate=\"no\">background</code> of the <code class=\"notranslate\" translate=\"no\">rtScene</code> to red so the\nrender target's texture is being cleared to red.</p>\n<p>Render targets are used for all kinds of things. <a href=\"shadows.html\">Shadows</a> use render targets.\n<a href=\"picking.html\">Picking can use a render target</a>. Various kinds of\n<a href=\"post-processing.html\">post processing effects</a> require render targets.\nRendering a rear view mirror in a car or a live view on a monitor inside a 3D\nscene might use a render target.</p>\n<p>A few notes about using <a href=\"/docs/#api/en/renderers/WebGLRenderTarget\"><code class=\"notranslate\" translate=\"no\">WebGLRenderTarget</code></a>.</p>\n<ul>\n<li><p>By default <a href=\"/docs/#api/en/renderers/WebGLRenderTarget\"><code class=\"notranslate\" translate=\"no\">WebGLRenderTarget</code></a> creates 2 textures. A color texture and a depth/stencil texture. If you don't need the depth or stencil textures you can request to not create them by passing in options. Example:</p>\n<pre class=\"prettyprint showlinemods notranslate lang-js\" translate=\"no\">  const rt = new THREE.WebGLRenderTarget(width, height, {\n    depthBuffer: false,\n    stencilBuffer: false,\n  });\n</pre>\n</li>\n<li><p>You might need to change the size of a render target</p>\n<p>In the example above we make a render target of a fixed size, 512x512. For things like post processing you generally need to make a render target the same size as your canvas. In our code that would mean when we change the canvas size we would also update both the render target size and the camera we're using when rendering to the render target. Example:</p>\n<pre class=\"prettyprint showlinemods notranslate notranslate\" translate=\"no\">function render(time) {\n  time *= 0.001;\n\n  if (resizeRendererToDisplaySize(renderer)) {\n    const canvas = renderer.domElement;\n    camera.aspect = canvas.clientWidth / canvas.clientHeight;\n    camera.updateProjectionMatrix();\n\n+    renderTarget.setSize(canvas.width, canvas.height);\n+    rtCamera.aspect = camera.aspect;\n+    rtCamera.updateProjectionMatrix();\n}\n</pre></li>\n</ul>\n\n        </div>\n      </div>\n    </div>",
    "success": true,
    "error": "",
    "crawled_at": 1786376809.300204,
    "is_shell": false
  },
  {
    "url": "https://threejs.org/manual/en/custom-buffergeometry.html",
    "title": "Custom BufferGeometry",
    "content": "<div class=\"container\">\n      <div class=\"lesson-title\">\n        <h1>Custom BufferGeometry</h1>\n      </div>\n      <div class=\"lesson\">\n        <div class=\"lesson-main\">\n          <p><a href=\"/docs/#api/en/core/BufferGeometry\"><code class=\"notranslate\" translate=\"no\">BufferGeometry</code></a> is three.js's way of representing all geometry. A <a href=\"/docs/#api/en/core/BufferGeometry\"><code class=\"notranslate\" translate=\"no\">BufferGeometry</code></a>\nessentially a collection <em>named</em> of <a href=\"/docs/#api/en/core/BufferAttribute\"><code class=\"notranslate\" translate=\"no\">BufferAttribute</code></a>s.\nEach <a href=\"/docs/#api/en/core/BufferAttribute\"><code class=\"notranslate\" translate=\"no\">BufferAttribute</code></a> represents an array of one type of data: positions,\nnormals, colors, uv, etc... Together, the named <a href=\"/docs/#api/en/core/BufferAttribute\"><code class=\"notranslate\" translate=\"no\">BufferAttribute</code></a>s represent\n<em>parallel arrays</em> of all the data for each vertex.</p>\n<div class=\"threejs_center\"><img src=\"../resources/threejs-attributes.svg\" style=\"width: 700px\"></div>\n\n<p>Above you can see we have 4 attributes: <code class=\"notranslate\" translate=\"no\">position</code>, <code class=\"notranslate\" translate=\"no\">normal</code>, <code class=\"notranslate\" translate=\"no\">color</code>, <code class=\"notranslate\" translate=\"no\">uv</code>.\nThey represent <em>parallel arrays</em> which means that the Nth set of data in each\nattribute belongs to the same vertex. The vertex at index = 4 is highlighted\nto show that the parallel data across all attributes defines one vertex.</p>\n<p>This brings up a point, here's a diagram of a cube with one corner highlighted.</p>\n<div class=\"threejs_center\"><img src=\"../resources/cube-faces-vertex.svg\" style=\"width: 500px\"></div>\n\n<p>Thinking about it that single corner needs a different normal for each face of the\ncube. A normal is info about which direction something faces. In the diagram\nthe normals are presented by the arrows around the corner vertex showing that each\nface that shares that vertex position needs a normal that points in a different direction.</p>\n<p>That corner needs different UVs for each face as well. UVs are texture coordinates\nthat specify which part of a texture being drawn on a triangle corresponds to that\nvertex position. You can see the green face needs that vertex to have a UV that corresponds\nto the top right corner of the F texture, the blue face needs a UV that corresponds to the\ntop left corner of the F texture, and the red face needs a UV that corresponds to the bottom\nleft corner of the F texture.</p>\n<p>A single <em>vertex</em> is the combination of all of its parts. If a vertex needs any\npart to be different then it must be a different vertex.</p>\n<p>As a simple example let's make a cube using <a href=\"/docs/#api/en/core/BufferGeometry\"><code class=\"notranslate\" translate=\"no\">BufferGeometry</code></a>. A cube is interesting\nbecause it appears to share vertices at the corners but really\ndoes not. For our example we'll list out all the vertices with all their data\nand then convert that data into parallel arrays and finally use those to make\n<a href=\"/docs/#api/en/core/BufferAttribute\"><code class=\"notranslate\" translate=\"no\">BufferAttribute</code></a>s and add them to a <a href=\"/docs/#api/en/core/BufferGeometry\"><code class=\"notranslate\" translate=\"no\">BufferGeometry</code></a>.</p>\n<p>We start with a list of all the data needed for the cube. Remember again\nthat if a vertex has any unique parts it has to be a separate vertex. As such\nto make a cube requires 36 vertices. 2 triangles per face, 3 vertices per triangle,\n6 faces = 36 vertices.</p>\n<pre class=\"prettyprint showlinemods notranslate lang-js\" translate=\"no\">const vertices = [\n  // front\n  { pos: [-1, -1,  1], norm: [ 0,  0,  1], uv: [0, 0], },\n  { pos: [ 1, -1,  1], norm: [ 0,  0,  1], uv: [1, 0], },\n  { pos: [-1,  1,  1], norm: [ 0,  0,  1], uv: [0, 1], },\n\n  { pos: [-1,  1,  1], norm: [ 0,  0,  1], uv: [0, 1], },\n  { pos: [ 1, -1,  1], norm: [ 0,  0,  1], uv: [1, 0], },\n  { pos: [ 1,  1,  1], norm: [ 0,  0,  1], uv: [1, 1], },\n  // right\n  { pos: [ 1, -1,  1], norm: [ 1,  0,  0], uv: [0, 0], },\n  { pos: [ 1, -1, -1], norm: [ 1,  0,  0], uv: [1, 0], },\n  { pos: [ 1,  1,  1], norm: [ 1,  0,  0], uv: [0, 1], },\n\n  { pos: [ 1,  1,  1], norm: [ 1,  0,  0], uv: [0, 1], },\n  { pos: [ 1, -1, -1], norm: [ 1,  0,  0], uv: [1, 0], },\n  { pos: [ 1,  1, -1], norm: [ 1,  0,  0], uv: [1, 1], },\n  // back\n  { pos: [ 1, -1, -1], norm: [ 0,  0, -1], uv: [0, 0], },\n  { pos: [-1, -1, -1], norm: [ 0,  0, -1], uv: [1, 0], },\n  { pos: [ 1,  1, -1], norm: [ 0,  0, -1], uv: [0, 1], },\n\n  { pos: [ 1,  1, -1], norm: [ 0,  0, -1], uv: [0, 1], },\n  { pos: [-1, -1, -1], norm: [ 0,  0, -1], uv: [1, 0], },\n  { pos: [-1,  1, -1], norm: [ 0,  0, -1], uv: [1, 1], },\n  // left\n  { pos: [-1, -1, -1], norm: [-1,  0,  0], uv: [0, 0], },\n  { pos: [-1, -1,  1], norm: [-1,  0,  0], uv: [1, 0], },\n  { pos: [-1,  1, -1], norm: [-1,  0,  0], uv: [0, 1], },\n\n  { pos: [-1,  1, -1], norm: [-1,  0,  0], uv: [0, 1], },\n  { pos: [-1, -1,  1], norm: [-1,  0,  0], uv: [1, 0], },\n  { pos: [-1,  1,  1], norm: [-1,  0,  0], uv: [1, 1], },\n  // top\n  { pos: [ 1,  1, -1], norm: [ 0,  1,  0], uv: [0, 0], },\n  { pos: [-1,  1, -1], norm: [ 0,  1,  0], uv: [1, 0], },\n  { pos: [ 1,  1,  1], norm: [ 0,  1,  0], uv: [0, 1], },\n\n  { pos: [ 1,  1,  1], norm: [ 0,  1,  0], uv: [0, 1], },\n  { pos: [-1,  1, -1], norm: [ 0,  1,  0], uv: [1, 0], },\n  { pos: [-1,  1,  1], norm: [ 0,  1,  0], uv: [1, 1], },\n  // bottom\n  { pos: [ 1, -1,  1], norm: [ 0, -1,  0], uv: [0, 0], },\n  { pos: [-1, -1,  1], norm: [ 0, -1,  0], uv: [1, 0], },\n  { pos: [ 1, -1, -1], norm: [ 0, -1,  0], uv: [0, 1], },\n\n  { pos: [ 1, -1, -1], norm: [ 0, -1,  0], uv: [0, 1], },\n  { pos: [-1, -1,  1], norm: [ 0, -1,  0], uv: [1, 0], },\n  { pos: [-1, -1, -1], norm: [ 0, -1,  0], uv: [1, 1], },\n];\n</pre>\n<p>We can then translate all of that into 3 parallel arrays</p>\n<pre class=\"prettyprint showlinemods notranslate lang-js\" translate=\"no\">const positions = [];\nconst normals = [];\nconst uvs = [];\nfor (const vertex of vertices) {\n  positions.push(...vertex.pos);\n  normals.push(...vertex.norm);\n  uvs.push(...vertex.uv);\n}\n</pre>\n<p>Finally we can create a <a href=\"/docs/#api/en/core/BufferGeometry\"><code class=\"notranslate\" translate=\"no\">BufferGeometry</code></a> and then a <a href=\"/docs/#api/en/core/BufferAttribute\"><code class=\"notranslate\" translate=\"no\">BufferAttribute</code></a> for each array\nand add it to the <a href=\"/docs/#api/en/core/BufferGeometry\"><code class=\"notranslate\" translate=\"no\">BufferGeometry</code></a>.</p>\n<pre class=\"prettyprint showlinemods notranslate lang-js\" translate=\"no\">  const geometry = new THREE.BufferGeometry();\n  const positionNumComponents = 3;\n  const normalNumComponents = 3;\n  const uvNumComponents = 2;\n  geometry.setAttribute(\n      'position',\n      new THREE.BufferAttribute(new Float32Array(positions), positionNumComponents));\n  geometry.setAttribute(\n      'normal',\n      new THREE.BufferAttribute(new Float32Array(normals), normalNumComponents));\n  geometry.setAttribute(\n      'uv',\n      new THREE.BufferAttribute(new Float32Array(uvs), uvNumComponents));\n</pre>\n<p>Note that the names are significant. You must name your attributes the names\nthat match what three.js expects (unless you are creating a custom shader).\nIn this case <code class=\"notranslate\" translate=\"no\">position</code>, <code class=\"notranslate\" translate=\"no\">normal</code>, and <code class=\"notranslate\" translate=\"no\">uv</code>. If you want vertex colors then\nname your attribute <code class=\"notranslate\" translate=\"no\">color</code>.</p>\n<p>Above we created 3 JavaScript native arrays, <code class=\"notranslate\" translate=\"no\">positions</code>, <code class=\"notranslate\" translate=\"no\">normals</code> and <code class=\"notranslate\" translate=\"no\">uvs</code>.\nWe then convert those into\n<a href=\"https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/TypedArray\">TypedArrays</a>\nof type <code class=\"notranslate\" translate=\"no\">Float32Array</code>. A <a href=\"/docs/#api/en/core/BufferAttribute\"><code class=\"notranslate\" translate=\"no\">BufferAttribute</code></a> requires a TypedArray not a native\narray. A <a href=\"/docs/#api/en/core/BufferAttribute\"><code class=\"notranslate\" translate=\"no\">BufferAttribute</code></a> also requires you to tell it how many components there\nare per vertex. For the positions and normals we have 3 components per vertex,\nx, y, and z. For the UVs we have 2, u and v.</p>\n<p></p><div translate=\"no\" class=\"threejs_example_container notranslate\">\n  <div></div>\n  <a class=\"threejs_center\" href=\"/manual/examples/custom-buffergeometry-cube.html\" target=\"_blank\">click here to open in a separate window</a>\n</div>\n\n<p></p>\n<p>That's a lot of data. A small thing we can do is use indices to reference\nthe vertices. Looking back at our cube data, each face is made from 2 triangles\nwith 3 vertices each, 6 vertices total, but 2 of those vertices are exactly the same;\nThe same position, the same normal, and the same uv.\nSo, we can remove the matching vertices and then\nreference them by index. First we remove the matching vertices.</p>\n<pre class=\"prettyprint showlinemods notranslate lang-js\" translate=\"no\">const vertices = [\n  // front\n  { pos: [-1, -1,  1], norm: [ 0,  0,  1], uv: [0, 0], }, // 0\n  { pos: [ 1, -1,  1], norm: [ 0,  0,  1], uv: [1, 0], }, // 1\n  { pos: [-1,  1,  1], norm: [ 0,  0,  1], uv: [0, 1], }, // 2\n-\n-  { pos: [-1,  1,  1], norm: [ 0,  0,  1], uv: [0, 1], },\n-  { pos: [ 1, -1,  1], norm: [ 0,  0,  1], uv: [1, 0], },\n  { pos: [ 1,  1,  1], norm: [ 0,  0,  1], uv: [1, 1], }, // 3\n  // right\n  { pos: [ 1, -1,  1], norm: [ 1,  0,  0], uv: [0, 0], }, // 4\n  { pos: [ 1, -1, -1], norm: [ 1,  0,  0], uv: [1, 0], }, // 5\n-\n-  { pos: [ 1,  1,  1], norm: [ 1,  0,  0], uv: [0, 1], },\n-  { pos: [ 1, -1, -1], norm: [ 1,  0,  0], uv: [1, 0], },\n  { pos: [ 1,  1,  1], norm: [ 1,  0,  0], uv: [0, 1], }, // 6\n  { pos: [ 1,  1, -1], norm: [ 1,  0,  0], uv: [1, 1], }, // 7\n  // back\n  { pos: [ 1, -1, -1], norm: [ 0,  0, -1], uv: [0, 0], }, // 8\n  { pos: [-1, -1, -1], norm: [ 0,  0, -1], uv: [1, 0], }, // 9\n-\n-  { pos: [ 1,  1, -1], norm: [ 0,  0, -1], uv: [0, 1], },\n-  { pos: [-1, -1, -1], norm: [ 0,  0, -1], uv: [1, 0], },\n  { pos: [ 1,  1, -1], norm: [ 0,  0, -1], uv: [0, 1], }, // 10\n  { pos: [-1,  1, -1], norm: [ 0,  0, -1], uv: [1, 1], }, // 11\n  // left\n  { pos: [-1, -1, -1], norm: [-1,  0,  0], uv: [0, 0], }, // 12\n  { pos: [-1, -1,  1], norm: [-1,  0,  0], uv: [1, 0], }, // 13\n-\n-  { pos: [-1,  1, -1], norm: [-1,  0,  0], uv: [0, 1], },\n-  { pos: [-1, -1,  1], norm: [-1,  0,  0], uv: [1, 0], },\n  { pos: [-1,  1, -1], norm: [-1,  0,  0], uv: [0, 1], }, // 14\n  { pos: [-1,  1,  1], norm: [-1,  0,  0], uv: [1, 1], }, // 15\n  // top\n  { pos: [ 1,  1, -1], norm: [ 0,  1,  0], uv: [0, 0], }, // 16\n  { pos: [-1,  1, -1], norm: [ 0,  1,  0], uv: [1, 0], }, // 17\n-\n-  { pos: [ 1,  1,  1], norm: [ 0,  1,  0], uv: [0, 1], },\n-  { pos: [-1,  1, -1], norm: [ 0,  1,  0], uv: [1, 0], },\n  { pos: [ 1,  1,  1], norm: [ 0,  1,  0], uv: [0, 1], }, // 18\n  { pos: [-1,  1,  1], norm: [ 0,  1,  0], uv: [1, 1], }, // 19\n  // bottom\n  { pos: [ 1, -1,  1], norm: [ 0, -1,  0], uv: [0, 0], }, // 20\n  { pos: [-1, -1,  1], norm: [ 0, -1,  0], uv: [1, 0], }, // 21\n-\n-  { pos: [ 1, -1, -1], norm: [ 0, -1,  0], uv: [0, 1], },\n-  { pos: [-1, -1,  1], norm: [ 0, -1,  0], uv: [1, 0], },\n  { pos: [ 1, -1, -1], norm: [ 0, -1,  0], uv: [0, 1], }, // 22\n  { pos: [-1, -1, -1], norm: [ 0, -1,  0], uv: [1, 1], }, // 23\n];\n</pre>\n<p>So now we have 24 unique vertices. Then we specify 36 indices\nfor the 36 vertices we need drawn to make 12 triangles by calling <a href=\"/docs/#api/en/core/BufferGeometry.setIndex\"><code class=\"notranslate\" translate=\"no\">BufferGeometry.setIndex</code></a> with an array of indices.</p>\n<pre class=\"prettyprint showlinemods notranslate lang-js\" translate=\"no\">geometry.setAttribute(\n    'position',\n    new THREE.BufferAttribute(positions, positionNumComponents));\ngeometry.setAttribute(\n    'normal',\n    new THREE.BufferAttribute(normals, normalNumComponents));\ngeometry.setAttribute(\n    'uv',\n    new THREE.BufferAttribute(uvs, uvNumComponents));\n\n+geometry.setIndex([\n+   0,  1,  2,   2,  1,  3,  // front\n+   4,  5,  6,   6,  5,  7,  // right\n+   8,  9, 10,  10,  9, 11,  // back\n+  12, 13, 14,  14, 13, 15,  // left\n+  16, 17, 18,  18, 17, 19,  // top\n+  20, 21, 22,  22, 21, 23,  // bottom\n+]);\n</pre>\n<p></p><div translate=\"no\" class=\"threejs_example_container notranslate\">\n  <div></div>\n  <a class=\"threejs_center\" href=\"/manual/examples/custom-buffergeometry-cube-indexed.html\" target=\"_blank\">click here to open in a separate window</a>\n</div>\n\n<p></p>\n<p><a href=\"/docs/#api/en/core/BufferGeometry\"><code class=\"notranslate\" translate=\"no\">BufferGeometry</code></a> has a <a href=\"/docs/#api/en/core/BufferGeometry#computeVertexNormals\"><code class=\"notranslate\" translate=\"no\">computeVertexNormals</code></a> method for computing normals if you\nare not supplying them. Unfortunately,\nsince positions can not be shared if any other part of a vertex is different,\nthe results of calling <code class=\"notranslate\" translate=\"no\">computeVertexNormals</code> will generate seams if your\ngeometry is supposed to connect to itself like a sphere or a cylinder.</p>\n<div class=\"spread\">\n  <div>\n    <div data-diagram=\"bufferGeometryCylinder\"></div>\n  </div>\n</div>\n\n<p>For the cylinder above the normals were created using <code class=\"notranslate\" translate=\"no\">computeVertexNormals</code>.\nIf you look closely there is a seam on the cylinder. This is because there\nis no way to share the vertices at the start and end of the cylinder since they\nrequire different UVs so the function to compute them has no idea those are\nthe same vertices to smooth over them. Just a small thing to be aware of.\nThe solution is to supply your own normals.</p>\n<p>We can also use <a href=\"https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/TypedArray\">TypedArrays</a> from the start instead of native JavaScript arrays.\nThe disadvantage to TypedArrays is you must specify their size up front. Of\ncourse that's not that large of a burden but with native arrays we can just\n<code class=\"notranslate\" translate=\"no\">push</code> values onto them and look at what size they end up by checking their\n<code class=\"notranslate\" translate=\"no\">length</code> at the end. With TypedArrays there is no push function so we need\nto do our own bookkeeping when adding values to them.</p>\n<p>In this example knowing the length up front is pretty easy since we're using\na big block of static data to start.</p>\n<pre class=\"prettyprint showlinemods notranslate lang-js\" translate=\"no\">-const positions = [];\n-const normals = [];\n-const uvs = [];\n+const numVertices = vertices.length;\n+const positionNumComponents = 3;\n+const normalNumComponents = 3;\n+const uvNumComponents = 2;\n+const positions = new Float32Array(numVertices * positionNumComponents);\n+const normals = new Float32Array(numVertices * normalNumComponents);\n+const uvs = new Float32Array(numVertices * uvNumComponents);\n+let posNdx = 0;\n+let nrmNdx = 0;\n+let uvNdx = 0;\nfor (const vertex of vertices) {\n-  positions.push(...vertex.pos);\n-  normals.push(...vertex.norm);\n-  uvs.push(...vertex.uv);\n+  positions.set(vertex.pos, posNdx);\n+  normals.set(vertex.norm, nrmNdx);\n+  uvs.set(vertex.uv, uvNdx);\n+  posNdx += positionNumComponents;\n+  nrmNdx += normalNumComponents;\n+  uvNdx += uvNumComponents;\n}\n\ngeometry.setAttribute(\n    'position',\n-    new THREE.BufferAttribute(new Float32Array(positions), positionNumComponents));\n+    new THREE.BufferAttribute(positions, positionNumComponents));\ngeometry.setAttribute(\n    'normal',\n-    new THREE.BufferAttribute(new Float32Array(normals), normalNumComponents));\n+    new THREE.BufferAttribute(normals, normalNumComponents));\ngeometry.setAttribute(\n    'uv',\n-    new THREE.BufferAttribute(new Float32Array(uvs), uvNumComponents));\n+    new THREE.BufferAttribute(uvs, uvNumComponents));\n\ngeometry.setIndex([\n   0,  1,  2,   2,  1,  3,  // front\n   4,  5,  6,   6,  5,  7,  // right\n   8,  9, 10,  10,  9, 11,  // back\n  12, 13, 14,  14, 13, 15,  // left\n  16, 17, 18,  18, 17, 19,  // top\n  20, 21, 22,  22, 21, 23,  // bottom\n]);\n</pre>\n<p></p><div translate=\"no\" class=\"threejs_example_container notranslate\">\n  <div></div>\n  <a class=\"threejs_center\" href=\"/manual/examples/custom-buffergeometry-cube-typedarrays.html\" target=\"_blank\">click here to open in a separate window</a>\n</div>\n\n<p></p>\n<p>A good reason to use typedarrays is if you want to dynamically update any\npart of the vertices.</p>\n<p>I couldn't think of a really good example of dynamically updating the vertices\nso I decided to make a sphere and move each quad in and out from the center. Hopefully\nit's a useful example.</p>\n<p>Here's the code to generate positions and indices for a sphere. The code\nis sharing vertices within a quad but it's not sharing vertices between\nquads because we want to be able to move each quad separately.</p>\n<p>Because I'm lazy I used a small hierarchy of 3 <a href=\"/docs/#api/en/core/Object3D\"><code class=\"notranslate\" translate=\"no\">Object3D</code></a> objects to compute\nsphere points. How this works is explained in <a href=\"optimize-lots-of-objects.html\">the article on optimizing lots of objects</a>.</p>\n<pre class=\"prettyprint showlinemods notranslate lang-js\" translate=\"no\">function makeSpherePositions(segmentsAround, segmentsDown) {\n  const numVertices = segmentsAround * segmentsDown * 6;\n  const numComponents = 3;\n  const positions = new Float32Array(numVertices * numComponents);\n  const indices = [];\n\n  const longHelper = new THREE.Object3D();\n  const latHelper = new THREE.Object3D();\n  const pointHelper = new THREE.Object3D();\n  longHelper.add(latHelper);\n  latHelper.add(pointHelper);\n  pointHelper.position.z = 1;\n  const temp = new THREE.Vector3();\n\n  function getPoint(lat, long) {\n    latHelper.rotation.x = lat;\n    longHelper.rotation.y = long;\n    longHelper.updateMatrixWorld(true);\n    return pointHelper.getWorldPosition(temp).toArray();\n  }\n\n  let posNdx = 0;\n  let ndx = 0;\n  for (let down = 0; down &lt; segmentsDown; ++down) {\n    const v0 = down / segmentsDown;\n    const v1 = (down + 1) / segmentsDown;\n    const lat0 = (v0 - 0.5) * Math.PI;\n    const lat1 = (v1 - 0.5) * Math.PI;\n\n    for (let across = 0; across &lt; segmentsAround; ++across) {\n      const u0 = across / segmentsAround;\n      const u1 = (across + 1) / segmentsAround;\n      const long0 = u0 * Math.PI * 2;\n      const long1 = u1 * Math.PI * 2;\n\n      positions.set(getPoint(lat0, long0), posNdx);  posNdx += numComponents;\n      positions.set(getPoint(lat1, long0), posNdx);  posNdx += numComponents;\n      positions.set(getPoint(lat0, long1), posNdx);  posNdx += numComponents;\n      positions.set(getPoint(lat1, long1), posNdx);  posNdx += numComponents;\n\n      indices.push(\n        ndx, ndx + 1, ndx + 2,\n        ndx + 2, ndx + 1, ndx + 3,\n      );\n      ndx += 4;\n    }\n  }\n  return {positions, indices};\n}\n</pre>\n<p>We can then call it like this</p>\n<pre class=\"prettyprint showlinemods notranslate lang-js\" translate=\"no\">const segmentsAround = 24;\nconst segmentsDown = 16;\nconst {positions, indices} = makeSpherePositions(segmentsAround, segmentsDown);\n</pre>\n<p>Because positions returned are unit sphere positions so they are exactly the same\nvalues we need for normals so we can just duplicated them for the normals.</p>\n<pre class=\"prettyprint showlinemods notranslate lang-js\" translate=\"no\">const normals = positions.slice();\n</pre>\n<p>And then we setup the attributes like before</p>\n<pre class=\"prettyprint showlinemods notranslate lang-js\" translate=\"no\">const geometry = new THREE.BufferGeometry();\nconst positionNumComponents = 3;\nconst normalNumComponents = 3;\n\n+const positionAttribute = new THREE.BufferAttribute(positions, positionNumComponents);\n+positionAttribute.setUsage(THREE.DynamicDrawUsage);\ngeometry.setAttribute(\n    'position',\n+    positionAttribute);\ngeometry.setAttribute(\n    'normal',\n    new THREE.BufferAttribute(normals, normalNumComponents));\ngeometry.setIndex(indices);\n</pre>\n<p>I've highlighted a few differences. We save a reference to the position attribute.\nWe also mark it as dynamic. This is a hint to THREE.js that we're going to be changing\nthe contents of the attribute often.</p>\n<p>In our render loop we update the positions based off their normals every frame.</p>\n<pre class=\"prettyprint showlinemods notranslate lang-js\" translate=\"no\">const temp = new THREE.Vector3();\n\n...\n\nfor (let i = 0; i &lt; positions.length; i += 3) {\n  const quad = (i / 12 | 0);\n  const ringId = quad / segmentsAround | 0;\n  const ringQuadId = quad % segmentsAround;\n  const ringU = ringQuadId / segmentsAround;\n  const angle = ringU * Math.PI * 2;\n  temp.fromArray(normals, i);\n  temp.multiplyScalar(THREE.MathUtils.lerp(1, 1.4, Math.sin(time + ringId + angle) * .5 + .5));\n  temp.toArray(positions, i);\n}\npositionAttribute.needsUpdate = true;\n</pre>\n<p>And we set <code class=\"notranslate\" translate=\"no\">positionAttribute.needsUpdate</code> to tell THREE.js to use our changes.</p>\n<p></p><div translate=\"no\" class=\"threejs_example_container notranslate\">\n  <div></div>\n  <a class=\"threejs_center\" href=\"/manual/examples/custom-buffergeometry-dynamic.html\" target=\"_blank\">click here to open in a separate window</a>\n</div>\n\n<p></p>\n<p>I hope these were useful examples of how to use <a href=\"/docs/#api/en/core/BufferGeometry\"><code class=\"notranslate\" translate=\"no\">BufferGeometry</code></a> directly to\nmake your own geometry and how to dynamically update the contents of a\n<a href=\"/docs/#api/en/core/BufferAttribute\"><code class=\"notranslate\" translate=\"no\">BufferAttribute</code></a>.</p>\n<!-- needed in English only to prevent warning from outdated translations -->\n<p><a href=\"resources/threejs-geometry.svg\"></a></p>\n<p><canvas id=\"c\"></canvas></p>\n\n\n\n        </div>\n      </div>\n    </div>\n\n  \n  \n\n\n\n\n<canvas id=\"c\" data-engine=\"three.js r185\"></canvas>",
    "success": true,
    "error": "",
    "crawled_at": 1786376809.300204,
    "is_shell": false
  },
  {
    "url": "https://threejs.org/manual/en/physics.html",
    "title": "Physics",
    "content": "<div class=\"container\">\n        <div class=\"lesson-title\">\n            <h1>Physics</h1>\n        </div>\n        <div class=\"lesson\">\n            <div class=\"lesson-main\">\n\n                <p>\n                    Physics engines allow you to simulate physical phenomena like gravity, collisions, and forces within\n                    your 3D capabilities. In a typical three.js scene, objects are moved by directly modifying their\n                    position or rotation. When using a physics engine, however, you create a parallel physics world\n                    where bodies react to forces and collisions. You then synchronize the three.js meshes with these\n                    physics bodies on every frame, creating the illusion of a physically simulated environment.\n                </p>\n\n                <p>\n                    It should be noted that the physics engine does not necessarily have to be updated every frame.\n                    Usually, to keep experiences consistent, physics are updated at fixed time steps. For instance, it could\n                    be that we are running the game loop at 60fps but the physics engine at 30fps (that is 1/30=3.33ms)\n                    while updating the three.js meshes' transforms (e.g., positions and rotations) with the most recent\n                    state from the physics engine.\n                </p>\n\n                <p>\n                    Physics simulations are particularly useful for games, interactive visualizations, and any\n                    application requiring realistic object behavior, such as objects falling, bouncing, or sliding.\n                </p>\n\n                <h2>Integration Approaches</h2>\n\n                <p>\n                    There are three main ways to integrate a physics engine into a three.js project:\n                </p>\n\n                <h3>1. Using Three.js Physics Addons</h3>\n\n                <p>\n                    Three.js provides wrapper classes for several popular physics engines in the\n                    <i>examples/jsm/physics</i> directory. These addons simplify the setup process by handling the\n                    initialization of the physics world and the synchronization of meshes.\n                </p>\n\n                <p>\n                    Available addons include:\n                </p>\n\n                <ul>\n                    <li><b>AmmoPhysics:</b> A wrapper for Ammo.js (Bullet Physics).</li>\n                    <li><b>JoltPhysics:</b> A wrapper for Jolt Physics.</li>\n                    <li><b>RapierPhysics:</b> A wrapper for Rapier.</li>\n                </ul>\n\n                <p>\n                    These addons effectively hide much of the complexity of the underlying engines. For standard use\n                    cases, they offer a very quick way to get started.\n                </p>\n\n                <h4>\n                    Examples\n                </h4>\n                <ul>\n                    <li><a href=\"https://threejs.org/examples/physics_ammo_instancing.html\" target=\"_blank\">physics / ammo / instancing</a></li>\n                    <li><a href=\"https://threejs.org/examples/physics_jolt_instancing.html\" target=\"_blank\">physics / jolt / instancing</a></li>\n                    <li><a href=\"https://threejs.org/examples/physics_rapier_instancing.html\" target=\"_blank\">physics / rapier / instancing</a></li>\n                </ul>\n\n                <h3>2. Using 3rd-Party Physics JS/TS Libraries</h3>\n\n                <p>\n                    Many physics engines are written directly in JavaScript or TypeScript and are designed to work\n                    easily with the web ecosystem. Libraries like <b>cannon-es</b> are popular choices because they are\n                    lightweight and easy to integrate specifically with three.js.\n                </p>\n\n                <p>\n                    When using these libraries, you instantiate the physics world and bodies yourself, then manually\n                    copy the position and quaternion from the physics body to the three.js mesh in your animation loop.\n                </p>\n\n\n                <h4>\n                    Projects\n                </h4>\n                <ul>\n                    <li><b><a href=\"https://github.com/pmndrs/cannon-es\" target=\"_blank\">cannon-es</a></b>: A lightweight 3D physics engine purely written in JS/TS. Under an MIT license. Apparently no longer maintained (latest commit more than a couple years ago).</li>\n                    <li><b><a href=\"https://github.com/schteppe/cannon.js\" target=\"_blank\">cannon.js</a></b>: A lightweight 3D physics engine purely written in JavaScript. Under an MIT license. No longer maintained (latest commit more than a couple years ago). Consider using its more recent fork cannon-es.</li>\n                    <li><b><a href=\"https://github.com/lo-th/phy\" target=\"_blank\">phy</a></b>: Physics engine for three.js purely written in JavaScript. Under an MIT license. Latest commit. Currently maintained.</li>\n                    <li><b><a href=\"https://github.com/lo-th/Oimo.js\" target=\"_blank\">Oimo.js</a></b>: A no-longer maintained lightweight 3D physics engine written purely in JavaScript. Under an MIT license. Consider using phy instead (as per the author's advise).</li>\n                </ul>\n                <p>\n                    It should also be noted that there are a couple of 3D physics engines that are seemingly written in JS/TS but are in reality calling other standalone 3D physics engines. For example:\n                </p>\n                <ul>\n                    <li><b><a href=\"https://github.com/chandlerprall/Physijs\" target=\"_blank\">Physijs</a></b>: Calls ammo.js under the hood to do physics work in a separate thread (using web workers). Under MIT license. Apparently no longer maintained (latest commit more than a couple of years ago).</li>\n                    <li><b><a href=\"https://github.com/enable3d/enable3d\" target=\"_blank\">enable3d</a></b>: 3D physics framework for three.js built on top of ammo.js. Under LGPL-3.0 license. Apparently maintained.</li>\n                </ul>\n\n                <h3>3. Importing WASM-based Engines</h3>\n\n                <p>\n                    For maximum performance, stability, and precision, especially with complex simulations, you can use physics engines written in\n                    C++ or Rust (or any other language that supports WASM) that have been compiled to WebAssembly (WASM). Engines like <b>Ammo.js</b> (a port of\n                    Bullet Physics) and <b>Rapier</b> fall into this category.\n                </p>\n\n                <p>\n                    While this approach offers the most features and best performance, it often requires more setup code\n                    to handle the WASM memory management and interaction with the physics API directly.\n                </p>\n\n                <h4>\n                    Examples\n                </h4>\n                <ul>\n                    <li><a href=\"https://threejs.org/examples/physics_ammo_break.html\" target=\"_blank\">physics / ammo / break</a></li>\n                    <li><a href=\"https://threejs.org/examples/physics_ammo_cloth.html\" target=\"_blank\">physics / ammo / cloth</a></li>\n                    <li><a href=\"https://threejs.org/examples/physics_ammo_rope.html\" target=\"_blank\">physics / ammo / rope</a></li>\n                    <li><a href=\"https://threejs.org/examples/physics_ammo_terrain.html\" target=\"_blank\">physics / ammo / terrain</a></li>\n                    <li><a href=\"https://threejs.org/examples/physics_ammo_volume.html\" target=\"_blank\">physics / ammo / volume</a></li>\n                </ul>\n\n                <h4>\n                    Projects\n                </h4>\n                <ul>\n                    <li><b><a href=\"https://github.com/jrouwe/JoltPhysics\" target=\"_blank\">JoltPhysics</a></b>:  A multi core friendly rigid body physics and collision detection library. Written in C++. Under MIT license. Actively maintained. Proven with its usage in world-renowned titles and game engines including: Horizon Forbidden West, Death Stranding 2, and official supported by the Godot game engine.</li>\n                    <li><b><a href=\"https://github.com/NVIDIA-Omniverse/PhysX\" target=\"_blank\">PhysX</a></b>:  Industry-standard realtime 3D physics engine provided by NVIDIA. Under BSD-3-Clause license. Actively maintained and very stable.</li>\n                    <li><b><a href=\"https://github.com/dimforge/rapier\" target=\"_blank\">Rapier</a></b>: 2D and 3D physics engines focused on performance. Written in Rust. Under an MIT license. Actively maintained.</li>\n                    <li><b><a href=\"https://github.com/bulletphysics/bullet3\" target=\"_blank\">Bullet</a></b>: \n                    Real-time collision detection and multi-physics simulation for VR, games, visual effects, robotics, machine learning etc. Written in C++. Under a ZLIB license. Potentially no longer maintained.</li>\n                </ul>\n                <p>\n                    Some of these multi-platform 3D physics engines have a ready-to-use WASM port, including:\n                </p>\n                <ul>\n                    <li><b><a href=\"https://github.com/jrouwe/JoltPhysics.js\" target=\"_blank\">JoltPhysics.js</a></b>:  Port of JoltPhysics to JavaScript using Emscripten. Under MIT license. Currently maintained.</li>\n                    <li><b><a href=\"https://github.com/fabmax/physx-js-webidl\" target=\"_blank\">physx-js-webidl</a></b>:   Javascript WASM bindings for Nvidia PhysX. Under MIT license. Currently maintained.</li>\n                    <li><b><a href=\"https://github.com/dimforge/rapier.js\" target=\"_blank\">Rapier.js</a></b>: Official JavaScript bindings for the Rapier physics engine. Under Apache-2.0 license. Actively maintained.</li>\n                    <li><b><a href=\"https://github.com/kripken/ammo.js\" target=\"_blank\">Ammo.js</a></b>: Direct port of the Bullet physics engine to JavaScript using Emscripten. No longer maintained (latest commit a couple of years ago). Under an MIT-like custom permissive license.</li>\n                </ul>\n\n            </div>\n        </div>\n    </div>",
    "success": true,
    "error": "",
    "crawled_at": 1786376809.300204,
    "is_shell": false
  },
  {
    "url": "https://threejs.org/manual/en/rendering-on-demand.html",
    "title": "Rendering on Demand",
    "content": "<div class=\"container\">\n      <div class=\"lesson-title\">\n        <h1>Rendering on Demand</h1>\n      </div>\n      <div class=\"lesson\">\n        <div class=\"lesson-main\">\n          <p>The topic might be obvious to many people but just in case ... most Three.js\nexamples render continuously. In other words they setup a\n<code class=\"notranslate\" translate=\"no\">requestAnimationFrame</code> loop or \"<em>rAF loop</em>\" something like this</p>\n<pre class=\"prettyprint showlinemods notranslate lang-js\" translate=\"no\">function render() {\n  ...\n  requestAnimationFrame(render);\n}\nrequestAnimationFrame(render);\n</pre>\n<p>For something that animates this makes sense but what about for something that\ndoes not animate? In that case rendering continuously is a waste of the devices\npower and if the user is on portable device it wastes the user's battery. </p>\n<p>The most obvious way to solve this is to render once at the start and then\nrender only when something changes. Changes include textures or models finally\nloading, data arriving from some external source, the user adjusting a setting\nor the camera or giving other relevant input.</p>\n<p>Let's take an example from <a href=\"responsive.html\">the article on responsiveness</a>\nand modify it to render on demand.</p>\n<p>First we'll add in the <a href=\"/docs/#examples/controls/OrbitControls\"><code class=\"notranslate\" translate=\"no\">OrbitControls</code></a> so there is something that could change\nthat we can render in response to.</p>\n<pre class=\"prettyprint showlinemods notranslate lang-js\" translate=\"no\">import * as THREE from 'three';\n+import {OrbitControls} from 'three/addons/controls/OrbitControls.js';\n</pre>\n<p>and set them up</p>\n<pre class=\"prettyprint showlinemods notranslate lang-js\" translate=\"no\">const fov = 75;\nconst aspect = 2;  // the canvas default\nconst near = 0.1;\nconst far = 5;\nconst camera = new THREE.PerspectiveCamera(fov, aspect, near, far);\ncamera.position.z = 2;\n\n+const controls = new OrbitControls(camera, canvas);\n+controls.target.set(0, 0, 0);\n+controls.update();\n</pre>\n<p>Since we won't be animating the cubes anymore we no longer need to keep track of them</p>\n<pre class=\"prettyprint showlinemods notranslate lang-js\" translate=\"no\">-const cubes = [\n-  makeInstance(geometry, 0x44aa88,  0),\n-  makeInstance(geometry, 0x8844aa, -2),\n-  makeInstance(geometry, 0xaa8844,  2),\n-];\n+makeInstance(geometry, 0x44aa88,  0);\n+makeInstance(geometry, 0x8844aa, -2);\n+makeInstance(geometry, 0xaa8844,  2);\n</pre>\n<p>We can remove the code to animate the cubes and the calls to <code class=\"notranslate\" translate=\"no\">requestAnimationFrame</code></p>\n<pre class=\"prettyprint showlinemods notranslate lang-js\" translate=\"no\">-function render(time) {\n-  time *= 0.001;\n+function render() {\n\n  if (resizeRendererToDisplaySize(renderer)) {\n    const canvas = renderer.domElement;\n    camera.aspect = canvas.clientWidth / canvas.clientHeight;\n    camera.updateProjectionMatrix();\n  }\n\n-  cubes.forEach((cube, ndx) =&gt; {\n-    const speed = 1 + ndx * .1;\n-    const rot = time * speed;\n-    cube.rotation.x = rot;\n-    cube.rotation.y = rot;\n-  });\n\n  renderer.render(scene, camera);\n\n-  requestAnimationFrame(render);\n}\n\n-requestAnimationFrame(render);\n</pre>\n<p>then we need to render once</p>\n<pre class=\"prettyprint showlinemods notranslate lang-js\" translate=\"no\">render();\n</pre>\n<p>We need to render anytime the <a href=\"/docs/#examples/controls/OrbitControls\"><code class=\"notranslate\" translate=\"no\">OrbitControls</code></a> change the camera settings.\nFortunately the <a href=\"/docs/#examples/controls/OrbitControls\"><code class=\"notranslate\" translate=\"no\">OrbitControls</code></a> dispatch a <code class=\"notranslate\" translate=\"no\">change</code> event anytime something\nchanges.</p>\n<pre class=\"prettyprint showlinemods notranslate lang-js\" translate=\"no\">controls.addEventListener('change', render);\n</pre>\n<p>We also need to handle the case where the user resizes the window. That was\nhandled automatically before since we were rendering continuously but now what\nwe are not we need to render when the window changes size.</p>\n<pre class=\"prettyprint showlinemods notranslate lang-js\" translate=\"no\">window.addEventListener('resize', render);\n</pre>\n<p>And with that we get something that renders on demand.</p>\n<p></p><div translate=\"no\" class=\"threejs_example_container notranslate\">\n  <div></div>\n  <a class=\"threejs_center\" href=\"/manual/examples/render-on-demand.html\" target=\"_blank\">click here to open in a separate window</a>\n</div>\n\n<p></p>\n<p>The <a href=\"/docs/#examples/controls/OrbitControls\"><code class=\"notranslate\" translate=\"no\">OrbitControls</code></a> have options to add a kind of inertia to make them feel less\nstiff. We can enable this by setting the <code class=\"notranslate\" translate=\"no\">enableDamping</code> property to true.</p>\n<pre class=\"prettyprint showlinemods notranslate lang-js\" translate=\"no\">controls.enableDamping = true;\n</pre>\n<p>With <code class=\"notranslate\" translate=\"no\">enableDamping</code> on we need to call <code class=\"notranslate\" translate=\"no\">controls.update</code> in our render function\nso that the <a href=\"/docs/#examples/controls/OrbitControls\"><code class=\"notranslate\" translate=\"no\">OrbitControls</code></a> can continue to give us new camera settings as they\nsmooth out the movement. But, that means we can't call <code class=\"notranslate\" translate=\"no\">render</code> directly from\nthe <code class=\"notranslate\" translate=\"no\">change</code> event because we'll end up in an infinite loop. The controls will\nsend us a <code class=\"notranslate\" translate=\"no\">change</code> event and call <code class=\"notranslate\" translate=\"no\">render</code>, <code class=\"notranslate\" translate=\"no\">render</code> will call <code class=\"notranslate\" translate=\"no\">controls.update</code>.\n<code class=\"notranslate\" translate=\"no\">controls.update</code> will send another <code class=\"notranslate\" translate=\"no\">change</code> event.</p>\n<p>We can fix that by using <code class=\"notranslate\" translate=\"no\">requestAnimationFrame</code> to call <code class=\"notranslate\" translate=\"no\">render</code> but we need to\nmake sure we only ask for a new frame if one has not already been requested\nwhich we can do by keeping a variable that tracks if we've already requested a frame.</p>\n<pre class=\"prettyprint showlinemods notranslate lang-js\" translate=\"no\">+let renderRequested = false;\n\nfunction render() {\n+  renderRequested = false;\n\n  if (resizeRendererToDisplaySize(renderer)) {\n    const canvas = renderer.domElement;\n    camera.aspect = canvas.clientWidth / canvas.clientHeight;\n    camera.updateProjectionMatrix();\n  }\n\n  renderer.render(scene, camera);\n}\nrender();\n\n+function requestRenderIfNotRequested() {\n+  if (!renderRequested) {\n+    renderRequested = true;\n+    requestAnimationFrame(render);\n+  }\n+}\n\n-controls.addEventListener('change', render);\n+controls.addEventListener('change', requestRenderIfNotRequested);\n</pre>\n<p>We should probably also use <code class=\"notranslate\" translate=\"no\">requestRenderIfNotRequested</code> for resizing as well</p>\n<pre class=\"prettyprint showlinemods notranslate lang-js\" translate=\"no\">-window.addEventListener('resize', render);\n+window.addEventListener('resize', requestRenderIfNotRequested);\n</pre>\n<p>It might be hard to see the difference. Try clicking on the example below and\nuse the arrow keys to move around or dragging to spin. Then try clicking on the\nexample above and do the same thing and you should be able to tell the\ndifference. The one above snaps when you press an arrow key or drag, the one\nbelow slides.</p>\n<p></p><div translate=\"no\" class=\"threejs_example_container notranslate\">\n  <div></div>\n  <a class=\"threejs_center\" href=\"/manual/examples/render-on-demand-w-damping.html\" target=\"_blank\">click here to open in a separate window</a>\n</div>\n\n<p></p>\n<p>Let's also add a simple lil-gui GUI and make its changes render on demand.</p>\n<pre class=\"prettyprint showlinemods notranslate lang-js\" translate=\"no\">import * as THREE from 'three';\nimport {OrbitControls} from 'three/addons/controls/OrbitControls.js';\n+import {GUI} from 'three/addons/libs/lil-gui.module.min.js';\n</pre>\n<p>Let's allow setting the color and x scale of each cube. To be able to set the\ncolor we'll use the <code class=\"notranslate\" translate=\"no\">ColorGUIHelper</code> we created in the <a href=\"lights.html\">article on\nlights</a>.</p>\n<p>First we need to create a GUI</p>\n<pre class=\"prettyprint showlinemods notranslate lang-js\" translate=\"no\">const gui = new GUI();\n</pre>\n<p>and then for each cube we'll create a folder and add 2 controls, one for\n<code class=\"notranslate\" translate=\"no\">material.color</code> and another for <code class=\"notranslate\" translate=\"no\">cube.scale.x</code>.</p>\n<pre class=\"prettyprint showlinemods notranslate lang-js\" translate=\"no\">function makeInstance(geometry, color, x) {\n  const material = new THREE.MeshPhongMaterial({color});\n\n  const cube = new THREE.Mesh(geometry, material);\n  scene.add(cube);\n\n  cube.position.x = x;\n\n+  const folder = gui.addFolder(<code class=\"notranslate\" translate=\"no\">Cube${x}</code>);\n+  folder.addColor(new ColorGUIHelper(material, 'color'), 'value')\n+      .name('color')\n+      .onChange(requestRenderIfNotRequested);\n+  folder.add(cube.scale, 'x', .1, 1.5)\n+      .name('scale x')\n+      .onChange(requestRenderIfNotRequested);\n+  folder.open();\n\n  return cube;\n}\n</pre>\n<p>You can see above lil-gui controls have an <code class=\"notranslate\" translate=\"no\">onChange</code> method that you can pass a\ncallback to be called when the GUI changes a value. In our case we just need it\nto call <code class=\"notranslate\" translate=\"no\">requestRenderIfNotRequested</code>. The call to <code class=\"notranslate\" translate=\"no\">folder.open</code> makes the\nfolder start expanded.</p>\n<p></p><div translate=\"no\" class=\"threejs_example_container notranslate\">\n  <div></div>\n  <a class=\"threejs_center\" href=\"/manual/examples/render-on-demand-w-gui.html\" target=\"_blank\">click here to open in a separate window</a>\n</div>\n\n<p></p>\n<p>I hope this gives some idea of how to make three.js render on demand instead of\ncontinuously. Apps/pages that render three.js on demand are not as common as\nmost pages using three.js are either games or 3D animated art but examples of\npages that might be better rendering on demand would be say a map viewer, a 3d\neditor, a 3d graph generator, a product catalog, etc...</p>\n\n        </div>\n      </div>\n    </div>",
    "success": true,
    "error": "",
    "crawled_at": 1786376809.300204,
    "is_shell": false
  },
  {
    "url": "https://threejs.org/manual/en/debugging-glsl.html",
    "title": "Debugging - GLSL",
    "content": "<div class=\"container\">\n      <div class=\"lesson-title\">\n        <h1>Debugging - GLSL</h1>\n      </div>\n      <div class=\"lesson\">\n        <div class=\"lesson-main\">\n          <p>This site so far does not teach GLSL just like it does not teach JavaScript.\nThose are really large topics. If you want to learn GLSL consider checking out\n<a href=\"https://webglfundamentals.org\">these articles</a> as a starting place.</p>\n<p>If you already know GLSL then here are a few tips for debugging.</p>\n<p>When I'm making a new GLSL shader and nothing appears generally\nthe first thing I do is change the fragment shader to return a solid\ncolor. For example at the very bottom of the shader I might put</p>\n<pre class=\"prettyprint showlinemods notranslate lang-glsl\" translate=\"no\">void main() {\n\n  ...\n\n  gl_FragColor = vec4(1, 0, 0, 1);  // red\n}\n</pre>\n<p>If I see the object I was trying to draw then I know the issue is\nrelated to my fragment shader. It could be anything like bad textures,\nuninitialized uniforms, uniforms with the wrong values but at least\nI have a direction to look.</p>\n<p>To test some of those I might start trying to draw some of the inputs.\nFor example if I'm using normals in the fragment shader then I might\nadd</p>\n<pre class=\"prettyprint showlinemods notranslate lang-glsl\" translate=\"no\">gl_FragColor = vec4(vNormal * 0.5 + 0.5, 1);\n</pre>\n<p>Normals go from -1 to +1 so by multiplying by 0.5 and adding 0.5 we get\nvalues that go from 0.0 to 1.0 which makes them useful for colors.</p>\n<p>Try this with some things you know work and you'll start getting an idea\nof what normals <em>normally</em> look like. If your normals don't look normal\nthen you have some clue where to look. If you're manipulating normals\nin the fragments shader you can use the same technique to draw the\nresult of that manipulation.</p>\n<div class=\"threejs_center\"><img src=\"../resources/images/standard-primitive-normals.jpg\" style=\"width: 650px;\"></div>\n\n<p>Similarly if we're using textures there will be texture coordinates and we\ncan draw them with something like</p>\n<pre class=\"prettyprint showlinemods notranslate lang-glsl\" translate=\"no\">gl_FragColor = vec4(fract(vUv), 0, 1);\n</pre>\n<p>The <code class=\"notranslate\" translate=\"no\">fract</code> is there in case we're using texture coordinates that go outside\nthe 0 to 1 range. This is common if <code class=\"notranslate\" translate=\"no\">texture.repeat</code> is set to something greater\nthan 1.</p>\n<div class=\"threejs_center\"><img src=\"../resources/images/standard-primitive-uvs.jpg\" style=\"width: 650px;\"></div>\n\n<p>You can do similar things for all values in your fragment shader. Figure out\nwhat their range is likely to be, add some code to set <code class=\"notranslate\" translate=\"no\">gl_FragColor</code> with\nthat range scaled to 0.0 to 1.0</p>\n<p>To check textures try a <a href=\"/docs/#api/en/textures/CanvasTexture\"><code class=\"notranslate\" translate=\"no\">CanvasTexture</code></a> or a <a href=\"/docs/#api/en/textures/DataTexture\"><code class=\"notranslate\" translate=\"no\">DataTexture</code></a> that you\nknow works.</p>\n<p>Conversely, if after setting <code class=\"notranslate\" translate=\"no\">gl_FragColor</code> to red I still see nothing\nthen I have a hint my issue might be in the direction of the things\nrelated to the vertex shader. Some matrices might be wrong or my\nattributes might have bad data or be setup incorrectly.</p>\n<p>I'd first look at the matrices. I might put a breakpoint right after\nmy call to <code class=\"notranslate\" translate=\"no\">renderer.render(scene, camera)</code> and then start expanding\nthings in the inspector. Is the camera's world matrix and projection\nmatrix at least not full of <code class=\"notranslate\" translate=\"no\">NaN</code>s? Expanding the scene and looking\nat its <code class=\"notranslate\" translate=\"no\">children</code> I'd check that the world matrices look reasonable (no <code class=\"notranslate\" translate=\"no\">NaN</code>s)\nand last 4 values of each matrix look reasonable for my scene. If I\nexpect my scene to be 50x50x50 units and some matrix shows 552352623.123\nclearly something is wrong there.</p>\n<div class=\"threejs_center\"><img src=\"../resources/images/inspect-matrices.gif\"></div>\n\n<p>Just like we did for the fragment shader we can also draw values from the\nvertex shader by passing them to the fragment shader. Declare a varying\nin both and pass the value you're not sure is correct. In fact if my\nshader use using normals I'll change the fragment shader to display them\nlike is mentioned above and then just set <code class=\"notranslate\" translate=\"no\">vNormal</code> to the value I want\nto display but scaled so the values go from 0.0 to 1.0. I then look at the\nresults and see if they fit my expectations.</p>\n<p>Another good thing to do is use a simpler shader. Can you draw your data\nwith <a href=\"/docs/#api/en/materials/MeshBasicMaterial\"><code class=\"notranslate\" translate=\"no\">MeshBasicMaterial</code></a>? If you can then try it and make sure it shows\nup as expected.</p>\n<p>If not what's the simplest vertex shader that will let you visualize your\ngeometry? Usually it's as simple as</p>\n<pre class=\"prettyprint showlinemods notranslate lang-glsl\" translate=\"no\">gl_Position = projection * modelView * vec4(position.xyz, 1);\n</pre>\n<p>If that works start adding in your changes a little at a time.</p>\n<p>Yet another thing you can do is use the\n<a href=\"https://chrome.google.com/webstore/detail/shader-editor/ggeaidddejpbakgafapihjbgdlbbbpob?hl=en\">Shader Editor extension for Chrome</a>\nor similar for other browsers. It's a great way to look at how other shaders\nare working. It's also good as you can make some of the changes suggested above\nlive while the code is running.</p>\n\n        </div>\n      </div>\n    </div>",
    "success": true,
    "error": "",
    "crawled_at": 1786376809.300204,
    "is_shell": false
  },
  {
    "url": "https://threejs.org/manual/en/tips.html",
    "title": "Tips",
    "content": "<div class=\"container\">\n      <div class=\"lesson-title\">\n        <h1>Tips</h1>\n      </div>\n      <div class=\"lesson\">\n        <div class=\"lesson-main\">\n          <p>This article is a collection of small issues you might run into\nusing three.js that seemed too small to have their own article.</p>\n<hr>\n<p><a id=\"screenshot\" data-toc=\"Taking a screenshot\"></a></p>\n<h1 id=\"taking-a-screenshot-of-the-canvas\">Taking A Screenshot of the Canvas</h1>\n<p>In the browser there are effectively 2 functions that will take a screenshot.\nThe old one\n<a href=\"https://developer.mozilla.org/en-US/docs/Web/API/HTMLCanvasElement/toDataURL\"><code class=\"notranslate\" translate=\"no\">canvas.toDataURL</code></a>\nand the new better one\n<a href=\"https://developer.mozilla.org/en-US/docs/Web/API/HTMLCanvasElement/toBlob\"><code class=\"notranslate\" translate=\"no\">canvas.toBlob</code></a></p>\n<p>So you'd think it would be easy to take a screenshot by just adding some code like</p>\n<pre class=\"prettyprint showlinemods notranslate lang-html\" translate=\"no\">&lt;canvas id=\"c\"&gt;&lt;/canvas&gt;\n+&lt;button id=\"screenshot\" type=\"button\"&gt;Save...&lt;/button&gt;\n</pre>\n<pre class=\"prettyprint showlinemods notranslate lang-js\" translate=\"no\">const elem = document.querySelector('#screenshot');\nelem.addEventListener('click', () =&gt; {\n  canvas.toBlob((blob) =&gt; {\n    saveBlob(blob, <code class=\"notranslate\" translate=\"no\">screencapture-${canvas.width}x${canvas.height}.png</code>);\n  });\n});\n\nconst saveBlob = (function() {\n  const a = document.createElement('a');\n  document.body.appendChild(a);\n  a.style.display = 'none';\n  return function saveData(blob, fileName) {\n     const url = window.URL.createObjectURL(blob);\n     a.href = url;\n     a.download = fileName;\n     a.click();\n  };\n}());\n</pre>\n<p>Here's the example from <a href=\"responsive.html\">the article on responsiveness</a>\nwith the code above added and some CSS to place the button</p>\n<p></p><div translate=\"no\" class=\"threejs_example_container notranslate\">\n  <div></div>\n  <a class=\"threejs_center\" href=\"/manual/examples/tips-screenshot-bad.html\" target=\"_blank\">click here to open in a separate window</a>\n</div>\n\n<p></p>\n<p>When I tried it I got this screenshot</p>\n<div class=\"threejs_center\"><img src=\"../resources/images/screencapture-413x313.png\"></div>\n\n<p>Yes, it's just a black image.</p>\n<p>It's possible it worked for you depending on your browser/OS but in general\nit's not likely to work.</p>\n<p>The issue is that for performance and compatibility reasons, by default the browser\nwill clear a WebGL canvas's drawing buffer after you've drawn to it.</p>\n<p>The solution is to call your rendering code just before capturing.</p>\n<p>In our code we need to adjust a few things. First let's separate\nout the rendering code.</p>\n<pre class=\"prettyprint showlinemods notranslate lang-js\" translate=\"no\">+const state = {\n+  time: 0,\n+};\n\n-function render(time) {\n-  time *= 0.001;\n+function render() {\n  if (resizeRendererToDisplaySize(renderer)) {\n    const canvas = renderer.domElement;\n    camera.aspect = canvas.clientWidth / canvas.clientHeight;\n    camera.updateProjectionMatrix();\n  }\n\n  cubes.forEach((cube, ndx) =&gt; {\n    const speed = 1 + ndx * .1;\n-    const rot = time * speed;\n+    const rot = state.time * speed;\n    cube.rotation.x = rot;\n    cube.rotation.y = rot;\n  });\n\n  renderer.render(scene, camera);\n\n-  requestAnimationFrame(render);\n}\n\n+function animate(time) {\n+  state.time = time * 0.001;\n+\n+  render();\n+\n+  requestAnimationFrame(animate);\n+}\n+requestAnimationFrame(animate);\n</pre>\n<p>Now that <code class=\"notranslate\" translate=\"no\">render</code> is only concerned with actually rendering\nwe can call it just before capturing the canvas.</p>\n<pre class=\"prettyprint showlinemods notranslate lang-js\" translate=\"no\">const elem = document.querySelector('#screenshot');\nelem.addEventListener('click', () =&gt; {\n+  render();\n  canvas.toBlob((blob) =&gt; {\n    saveBlob(blob, <code class=\"notranslate\" translate=\"no\">screencapture-${canvas.width}x${canvas.height}.png</code>);\n  });\n});\n</pre>\n<p>And now it should work.</p>\n<p></p><div translate=\"no\" class=\"threejs_example_container notranslate\">\n  <div></div>\n  <a class=\"threejs_center\" href=\"/manual/examples/tips-screenshot-good.html\" target=\"_blank\">click here to open in a separate window</a>\n</div>\n\n<p></p>\n<p>For a different solution see the next item.</p>\n<hr>\n<p><a id=\"preservedrawingbuffer\" data-toc=\"Prevent the Canvas Being Cleared\"></a></p>\n<h1 id=\"preventing-the-canvas-being-cleared\">Preventing the canvas being cleared</h1>\n<p>Let's say you wanted to let the user paint with an animated\nobject. You need to pass in <code class=\"notranslate\" translate=\"no\">preserveDrawingBuffer: true</code> when\nyou create the <a href=\"/docs/#api/en/renderers/WebGLRenderer\"><code class=\"notranslate\" translate=\"no\">WebGLRenderer</code></a>. This prevents the browser from\nclearing the canvas. You also need to tell three.js not to clear\nthe canvas as well.</p>\n<pre class=\"prettyprint showlinemods notranslate lang-js\" translate=\"no\">const canvas = document.querySelector('#c');\n-const renderer = new THREE.WebGLRenderer({antialias: true, canvas});\n+const renderer = new THREE.WebGLRenderer({\n+  canvas,\n+  preserveDrawingBuffer: true,\n+  alpha: true,\n+});\n+renderer.autoClearColor = false;\n</pre>\n<p></p><div translate=\"no\" class=\"threejs_example_container notranslate\">\n  <div></div>\n  <a class=\"threejs_center\" href=\"/manual/examples/tips-preservedrawingbuffer.html\" target=\"_blank\">click here to open in a separate window</a>\n</div>\n\n<p></p>\n<p>Note that if you were serious about making a drawing program this would not be a\nsolution as the browser will still clear the canvas anytime we change its\nresolution. We're changing is resolution based on its display size. Its display\nsize changes when the window changes size. That includes when the user downloads\na file, even in another tab, and the browser adds a status bar. It also includes when\nthe user turns their phone and the browser switches from portrait to landscape.</p>\n<p>If you really wanted to make a drawing program you'd\n<a href=\"rendertargets.html\">render to a texture using a render target</a>.</p>\n<hr>\n<p><a id=\"tabindex\" data-toc=\"Get Keyboard Input From a Canvas\"></a></p>\n<h1 id=\"getting-keyboard-input\">Getting Keyboard Input</h1>\n<p>Throughout these tutorials we've often attached event listeners to the <code class=\"notranslate\" translate=\"no\">canvas</code>.\nWhile many events work, one that does not work by default is keyboard\nevents.</p>\n<p>To get keyboard events, set the <a href=\"https://developer.mozilla.org/en-US/docs/Web/API/HTMLElement/tabIndex\"><code class=\"notranslate\" translate=\"no\">tabindex</code></a>\nof the canvas to 0 or more. Eg.</p>\n<pre class=\"prettyprint showlinemods notranslate lang-html\" translate=\"no\">&lt;canvas tabindex=\"0\"&gt;&lt;/canvas&gt;\n</pre>\n<p>This ends up causing a new issue though. Anything that has a <code class=\"notranslate\" translate=\"no\">tabindex</code> set\nwill get highlighted when it has the focus. To fix that set its focus CSS outline\nto none</p>\n<pre class=\"prettyprint showlinemods notranslate lang-css\" translate=\"no\">canvas:focus {\n  outline:none;\n}\n</pre>\n<p>To demonstrate here are 3 canvases </p>\n<pre class=\"prettyprint showlinemods notranslate lang-html\" translate=\"no\">&lt;canvas id=\"c1\"&gt;&lt;/canvas&gt;\n&lt;canvas id=\"c2\" tabindex=\"0\"&gt;&lt;/canvas&gt;\n&lt;canvas id=\"c3\" tabindex=\"1\"&gt;&lt;/canvas&gt;\n</pre>\n<p>and some css just for the last canvas </p>\n<pre class=\"prettyprint showlinemods notranslate lang-css\" translate=\"no\">#c3:focus {\n    outline: none;\n}\n</pre>\n<p>Let's attach the same event listeners to all of them</p>\n<pre class=\"prettyprint showlinemods notranslate lang-js\" translate=\"no\">document.querySelectorAll('canvas').forEach((canvas) =&gt; {\n  const ctx = canvas.getContext('2d');\n\n  function draw(str) {\n    ctx.clearRect(0, 0, canvas.width, canvas.height);\n    ctx.textAlign = 'center';\n    ctx.textBaseline = 'middle';\n    ctx.fillText(str, canvas.width / 2, canvas.height / 2);\n  }\n  draw(canvas.id);\n\n  canvas.addEventListener('focus', () =&gt; {\n    draw('has focus press a key');\n  });\n\n  canvas.addEventListener('blur', () =&gt; {\n    draw('lost focus');\n  });\n\n  canvas.addEventListener('keydown', (e) =&gt; {\n    draw(<code class=\"notranslate\" translate=\"no\">keyCode: ${e.keyCode}</code>);\n  });\n});\n</pre>\n<p>Notice you can't get the first canvas to accept keyboard input.\nThe second canvas you can but it gets highlighted. The 3rd\ncanvas has both solutions applied.</p>\n<p></p><div translate=\"no\" class=\"threejs_example_container notranslate\">\n  <div></div>\n  <a class=\"threejs_center\" href=\"/manual/examples/tips-tabindex.html\" target=\"_blank\">click here to open in a separate window</a>\n</div>\n\n<p></p>\n<hr>\n<p><a id=\"transparent-canvas\" data-toc=\"Make the Canvas Transparent\"></a></p>\n<h1 id=\"making-the-canvas-transparent\">Making the Canvas Transparent</h1>\n<p>By default THREE.js makes the canvas opaque. If you want the\ncanvas to be transparent pass in <a href=\"/docs/#api/en/renderers/WebGLRenderer#alpha\"><code class=\"notranslate\" translate=\"no\">alpha:true</code></a> when you create\nthe <a href=\"/docs/#api/en/renderers/WebGLRenderer\"><code class=\"notranslate\" translate=\"no\">WebGLRenderer</code></a></p>\n<pre class=\"prettyprint showlinemods notranslate lang-js\" translate=\"no\">const canvas = document.querySelector('#c');\n-const renderer = new THREE.WebGLRenderer({antialias: true, canvas});\n+const renderer = new THREE.WebGLRenderer({\n+  canvas,\n+  alpha: true,\n+});\n</pre>\n<p>You probably also want to tell it that your results are <strong>not</strong> using premultiplied alpha</p>\n<pre class=\"prettyprint showlinemods notranslate lang-js\" translate=\"no\">const canvas = document.querySelector('#c');\nconst renderer = new THREE.WebGLRenderer({\n  canvas,\n  alpha: true,\n+  premultipliedAlpha: false,\n});\n</pre>\n<p>Three.js defaults to the canvas using\n<a href=\"/docs/#api/en/renderers/WebGLRenderer#premultipliedAlpha\"><code class=\"notranslate\" translate=\"no\">premultipliedAlpha: true</code></a> but defaults\nto materials outputting <a href=\"/docs/#api/en/materials/Material#premultipliedAlpha\"><code class=\"notranslate\" translate=\"no\">premultipliedAlpha: false</code></a>.</p>\n<p>If you'd like a better understanding of when and when not to use premultiplied alpha\nhere's <a href=\"https://developer.nvidia.com/content/alpha-blending-pre-or-not-pre\">a good article on it</a>.</p>\n<p>In any case let's setup a simple example with a transparent canvas.</p>\n<p>We applied the settings above to the example from <a href=\"responsive.html\">the article on responsiveness</a>.\nLet's also make the materials more transparent.</p>\n<pre class=\"prettyprint showlinemods notranslate lang-js\" translate=\"no\">function makeInstance(geometry, color, x) {\n-  const material = new THREE.MeshPhongMaterial({color});\n+  const material = new THREE.MeshPhongMaterial({\n+    color,\n+    opacity: 0.5,\n+  });\n\n...\n</pre>\n<p>And let's add some HTML content</p>\n<pre class=\"prettyprint showlinemods notranslate lang-html\" translate=\"no\">&lt;body&gt;\n  &lt;canvas id=\"c\"&gt;&lt;/canvas&gt;\n+  &lt;div id=\"content\"&gt;\n+    &lt;div&gt;\n+      &lt;h1&gt;Cubes-R-Us!&lt;/h1&gt;\n+      &lt;p&gt;We make the best cubes!&lt;/p&gt;\n+    &lt;/div&gt;\n+  &lt;/div&gt;\n&lt;/body&gt;\n</pre>\n<p>as well as some CSS to put the canvas in front</p>\n<pre class=\"prettyprint showlinemods notranslate lang-css\" translate=\"no\">body {\n    margin: 0;\n}\n#c {\n    width: 100%;\n    height: 100%;\n    display: block;\n+    position: fixed;\n+    left: 0;\n+    top: 0;\n+    z-index: 2;\n+    pointer-events: none;\n}\n+#content {\n+  font-size: 7vw;\n+  font-family: sans-serif;\n+  text-align: center;\n+  width: 100%;\n+  height: 100%;\n+  display: flex;\n+  justify-content: center;\n+  align-items: center;\n+}\n</pre>\n<p>note that <code class=\"notranslate\" translate=\"no\">pointer-events: none</code> makes the canvas invisible to the mouse\nand touch events so you can select the text beneath.</p>\n<p></p><div translate=\"no\" class=\"threejs_example_container notranslate\">\n  <div></div>\n  <a class=\"threejs_center\" href=\"/manual/examples/tips-transparent-canvas.html\" target=\"_blank\">click here to open in a separate window</a>\n</div>\n\n<p></p>\n<hr>\n<p><a id=\"html-background\" data-toc=\"Use three.js as Background in HTML\"></a></p>\n<h1 id=\"making-your-background-a-three-js-animation\">Making your background a three.js animation</h1>\n<p>A common question is how to make a three.js animation be the background of\na webpage.</p>\n<p>There are 2 obvious ways.</p>\n<ul>\n<li>Set the canvas CSS <code class=\"notranslate\" translate=\"no\">position</code> to <code class=\"notranslate\" translate=\"no\">fixed</code> as in</li>\n</ul>\n<pre class=\"prettyprint showlinemods notranslate lang-css\" translate=\"no\">#c {\n position: fixed;\n left: 0;\n top: 0;\n ...\n}\n</pre>\n<p>You can basically see this exact solution on the previous example. Just set <code class=\"notranslate\" translate=\"no\">z-index</code> to -1\nand the cubes will appear behind the text.</p>\n<p>A small disadvantage to this solution is your JavaScript must integrate with the page\nand if you have a complex page then you need to make sure none of the JavaScript in your\nthree.js visualization conflict with the JavaScript doing other things in the page.</p>\n<ul>\n<li>Use an <code class=\"notranslate\" translate=\"no\">iframe</code></li>\n</ul>\n<p>This is the solution used on <a href=\"/\">the front page of this site</a>.</p>\n<p>In your webpage just insert an iframe, for example</p>\n<pre class=\"prettyprint showlinemods notranslate lang-html\" translate=\"no\">&lt;iframe id=\"background\" src=\"responsive.html\"&gt;\n&lt;div&gt;\n  Your content goes here.\n&lt;/div&gt;\n</pre>\n<p>Then style the iframe to fill the window and be in the background\nwhich is basically the same code as we used above for the canvas\nexcept we also need to set <code class=\"notranslate\" translate=\"no\">border</code> to <code class=\"notranslate\" translate=\"no\">none</code> since iframes have\na border by default.</p>\n<pre class=\"prettyprint showlinemods notranslate notranslate\" translate=\"no\">#background {\n    position: fixed;\n    width: 100%;\n    height: 100%;\n    left: 0;\n    top: 0;\n    z-index: -1;\n    border: none;\n    pointer-events: none;\n}\n</pre><p></p><div translate=\"no\" class=\"threejs_example_container notranslate\">\n  <div></div>\n  <a class=\"threejs_center\" href=\"/manual/examples/tips-html-background.html\" target=\"_blank\">click here to open in a separate window</a>\n</div>\n\n<p></p>\n\n        </div>\n      </div>\n    </div>",
    "success": true,
    "error": "",
    "crawled_at": 1786376809.300204,
    "is_shell": false
  },
  {
    "url": "https://threejs.org/manual/en/optimize-lots-of-objects.html",
    "title": "Optimize Lots of Objects",
    "content": "<div class=\"container\">\n      <div class=\"lesson-title\">\n        <h1>Optimize Lots of Objects</h1>\n      </div>\n      <div class=\"lesson\">\n        <div class=\"lesson-main\">\n          <p>This article is part of a series of articles about three.js. The first article\nis <a href=\"fundamentals.html\">three.js fundamentals</a>. If you haven't read that\nyet and you're new to three.js you might want to consider starting there. </p>\n<p>There are many ways to optimize things for three.js. One way is often referred\nto as <em>merging geometry</em>. Every <a href=\"/docs/#api/en/objects/Mesh\"><code class=\"notranslate\" translate=\"no\">Mesh</code></a> you create and three.js represents 1 or\nmore requests by the system to draw something. Drawing 2 things has more\noverhead than drawing 1 even if the results are the same so one way to optimize\nis to merge meshes.</p>\n<p>Let's show an example of when this is a good solution for an issue. Let's\nre-create the <a href=\"https://globe.chromeexperiments.com/\">WebGL Globe</a>.</p>\n<p>The first thing we need to do is get some data. The WebGL Globe said the data\nthey use comes from <a href=\"http://sedac.ciesin.columbia.edu/gpw/\">SEDAC</a>. Checking out\nthe site I saw there was <a href=\"https://beta.sedac.ciesin.columbia.edu/data/set/gpw-v4-basic-demographic-characteristics-rev10\">demographic data in a grid\nformat</a>.\nI downloaded the data at 60 minute resolution. Then I took a look at the data</p>\n<p>It looks like this</p>\n<pre class=\"prettyprint showlinemods notranslate lang-txt\" translate=\"no\"> ncols         360\n nrows         145\n xllcorner     -180\n yllcorner     -60\n cellsize      0.99999999999994\n NODATA_value  -9999\n -9999 -9999 -9999 -9999 -9999 -9999 -9999 -9999 -9999 -9999 -9999 -9999 ...\n -9999 -9999 -9999 -9999 -9999 -9999 -9999 -9999 -9999 -9999 -9999 -9999 ...\n -9999 -9999 -9999 -9999 -9999 -9999 -9999 -9999 -9999 -9999 -9999 -9999 ...\n -9999 -9999 -9999 -9999 -9999 -9999 -9999 -9999 -9999 -9999 -9999 -9999 ...\n -9999 -9999 -9999 -9999 -9999 -9999 -9999 -9999 -9999 -9999 -9999 -9999 ...\n -9999 -9999 -9999 -9999 -9999 -9999 -9999 -9999 -9999 -9999 -9999 -9999 ...\n -9999 -9999 -9999 -9999 -9999 -9999 -9999 -9999 -9999 -9999 -9999 -9999 ...\n -9999 -9999 -9999 -9999 -9999 -9999 -9999 -9999 -9999 -9999 -9999 -9999 ...\n -9999 -9999 -9999 -9999 -9999 -9999 -9999 -9999 -9999 -9999 -9999 -9999 ...\n -9999 -9999 -9999 -9999 -9999 -9999 -9999 -9999 -9999 -9999 -9999 -9999 ...\n -9999 -9999 -9999 -9999 -9999 -9999 -9999 -9999 -9999 -9999 -9999 -9999 ...\n -9999 -9999 -9999 -9999 -9999 -9999 -9999 -9999 -9999 -9999 -9999 -9999 ...\n -9999 -9999 -9999 -9999 -9999 -9999 -9999 -9999 -9999 -9999 -9999 -9999 ...\n 9.241768 8.790958 2.095345 -9999 0.05114867 -9999 -9999 -9999 -9999 -999...\n 1.287993 0.4395509 -9999 -9999 -9999 -9999 -9999 -9999 -9999 -9999 -9999...\n -9999 -9999 -9999 -9999 -9999 -9999 -9999 -9999 -9999 -9999 -9999 -9999 ...\n</pre>\n<p>There's a few lines that are like key/value pairs followed by lines with a value\nper grid point, one line for each row of data points.</p>\n<p>To make sure we understand the data let's try to plot it in 2D.</p>\n<p>First some code to load the text file</p>\n<pre class=\"prettyprint showlinemods notranslate lang-js\" translate=\"no\">async function loadFile(url) {\n  const res = await fetch(url);\n  return res.text();\n}\n</pre>\n<p>The code above returns a <code class=\"notranslate\" translate=\"no\">Promise</code> with the contents of the file at <code class=\"notranslate\" translate=\"no\">url</code>;</p>\n<p>Then we need some code to parse the file</p>\n<pre class=\"prettyprint showlinemods notranslate lang-js\" translate=\"no\">function parseData(text) {\n  const data = [];\n  const settings = {data};\n  let max;\n  let min;\n  // split into lines\n  text.split('\\n').forEach((line) =&gt; {\n    // split the line by whitespace\n    const parts = line.trim().split(/\\s+/);\n    if (parts.length === 2) {\n      // only 2 parts, must be a key/value pair\n      settings[parts[0]] = parseFloat(parts[1]);\n    } else if (parts.length &gt; 2) {\n      // more than 2 parts, must be data\n      const values = parts.map((v) =&gt; {\n        const value = parseFloat(v);\n        if (value === settings.NODATA_value) {\n          return undefined;\n        }\n        max = Math.max(max === undefined ? value : max, value);\n        min = Math.min(min === undefined ? value : min, value);\n        return value;\n      });\n      data.push(values);\n    }\n  });\n  return Object.assign(settings, {min, max});\n}\n</pre>\n<p>The code above returns an object with all the key/value pairs from the file as\nwell as a <code class=\"notranslate\" translate=\"no\">data</code> property with all the data in one large array and the <code class=\"notranslate\" translate=\"no\">min</code> and\n<code class=\"notranslate\" translate=\"no\">max</code> values found in the data.</p>\n<p>Then we need some code to draw that data</p>\n<pre class=\"prettyprint showlinemods notranslate lang-js\" translate=\"no\">function drawData(file) {\n  const {min, max, data} = file;\n  const range = max - min;\n  const ctx = document.querySelector('canvas').getContext('2d');\n  // make the canvas the same size as the data\n  ctx.canvas.width = ncols;\n  ctx.canvas.height = nrows;\n  // but display it double size so it's not too small\n  ctx.canvas.style.width = px(ncols * 2);\n  ctx.canvas.style.height = px(nrows * 2);\n  // fill the canvas to dark gray\n  ctx.fillStyle = '#444';\n  ctx.fillRect(0, 0, ctx.canvas.width, ctx.canvas.height);\n  // draw each data point\n  data.forEach((row, latNdx) =&gt; {\n    row.forEach((value, lonNdx) =&gt; {\n      if (value === undefined) {\n        return;\n      }\n      const amount = (value - min) / range;\n      const hue = 1;\n      const saturation = 1;\n      const lightness = amount;\n      ctx.fillStyle = hsl(hue, saturation, lightness);\n      ctx.fillRect(lonNdx, latNdx, 1, 1);\n    });\n  });\n}\n\nfunction px(v) {\n  return <code class=\"notranslate\" translate=\"no\">${v | 0}px</code>;\n}\n\nfunction hsl(h, s, l) {\n  return <code class=\"notranslate\" translate=\"no\">hsl(${h * 360 | 0},${s * 100 | 0}%,${l * 100 | 0}%)</code>;\n}\n</pre>\n<p>And finally gluing it all together</p>\n<pre class=\"prettyprint showlinemods notranslate lang-js\" translate=\"no\">loadFile('resources/data/gpw/gpw_v4_basic_demographic_characteristics_rev10_a000_014mt_2010_cntm_1_deg.asc')\n  .then(parseData)\n  .then(drawData);\n</pre>\n<p>Gives us this result</p>\n<p></p><div translate=\"no\" class=\"threejs_example_container notranslate\">\n  <div></div>\n  <a class=\"threejs_center\" href=\"/manual/examples/gpw-data-viewer.html\" target=\"_blank\">click here to open in a separate window</a>\n</div>\n\n<p></p>\n<p>So that seems to work. </p>\n<p>Let's try it in 3D. Starting with the code from <a href=\"rendering-on-demand.html\">rendering on\ndemand</a> We'll make one box per data in the\nfile.</p>\n<p>First let's make a simple sphere with a texture of the world. Here's the texture</p>\n<div class=\"threejs_center\"><img src=\"../examples/resources/images/world.jpg\" style=\"width: 600px\"></div>\n\n<p>And the code to set it up.</p>\n<pre class=\"prettyprint showlinemods notranslate lang-js\" translate=\"no\">{\n  const loader = new THREE.TextureLoader();\n  const texture = loader.load('resources/images/world.jpg', render);\n  const geometry = new THREE.SphereGeometry(1, 64, 32);\n  const material = new THREE.MeshBasicMaterial({map: texture});\n  scene.add(new THREE.Mesh(geometry, material));\n}\n</pre>\n<p>Notice the call to <code class=\"notranslate\" translate=\"no\">render</code> when the texture has finished loading. We need this\nbecause we're <a href=\"rendering-on-demand.html\">rendering on demand</a> instead of\ncontinuously so we need to render once when the texture is loaded.</p>\n<p>Then we need to change the code that drew a dot per data point above to instead\nmake a box per data point.</p>\n<pre class=\"prettyprint showlinemods notranslate lang-js\" translate=\"no\">function addBoxes(file) {\n  const {min, max, data} = file;\n  const range = max - min;\n\n  // make one box geometry\n  const boxWidth = 1;\n  const boxHeight = 1;\n  const boxDepth = 1;\n  const geometry = new THREE.BoxGeometry(boxWidth, boxHeight, boxDepth);\n  // make it so it scales away from the positive Z axis\n  geometry.applyMatrix4(new THREE.Matrix4().makeTranslation(0, 0, 0.5));\n\n  // these helpers will make it easy to position the boxes\n  // We can rotate the lon helper on its Y axis to the longitude\n  const lonHelper = new THREE.Object3D();\n  scene.add(lonHelper);\n  // We rotate the latHelper on its X axis to the latitude\n  const latHelper = new THREE.Object3D();\n  lonHelper.add(latHelper);\n  // The position helper moves the object to the edge of the sphere\n  const positionHelper = new THREE.Object3D();\n  positionHelper.position.z = 1;\n  latHelper.add(positionHelper);\n\n  const lonFudge = Math.PI * .5;\n  const latFudge = Math.PI * -0.135;\n  data.forEach((row, latNdx) =&gt; {\n    row.forEach((value, lonNdx) =&gt; {\n      if (value === undefined) {\n        return;\n      }\n      const amount = (value - min) / range;\n      const material = new THREE.MeshBasicMaterial();\n      const hue = THREE.MathUtils.lerp(0.7, 0.3, amount);\n      const saturation = 1;\n      const lightness = THREE.MathUtils.lerp(0.1, 1.0, amount);\n      material.color.setHSL(hue, saturation, lightness);\n      const mesh = new THREE.Mesh(geometry, material);\n      scene.add(mesh);\n\n      // adjust the helpers to point to the latitude and longitude\n      lonHelper.rotation.y = THREE.MathUtils.degToRad(lonNdx + file.xllcorner) + lonFudge;\n      latHelper.rotation.x = THREE.MathUtils.degToRad(latNdx + file.yllcorner) + latFudge;\n\n      // use the world matrix of the position helper to\n      // position this mesh.\n      positionHelper.updateWorldMatrix(true, false);\n      mesh.applyMatrix4(positionHelper.matrixWorld);\n\n      mesh.scale.set(0.005, 0.005, THREE.MathUtils.lerp(0.01, 0.5, amount));\n    });\n  });\n}\n</pre>\n<p>The code is mostly straight forward from our test drawing code. </p>\n<p>We make one box and adjust its center so it scales away from positive Z. If we\ndidn't do this it would scale from the center but we want them to grow away from the origin.</p>\n<div class=\"spread\">\n  <div>\n    <div data-diagram=\"scaleCenter\" style=\"height: 250px\"></div>\n    <div class=\"code\">default</div>\n  </div>\n  <div>\n    <div data-diagram=\"scalePositiveZ\" style=\"height: 250px\"></div>\n    <div class=\"code\">adjusted</div>\n  </div>\n</div>\n\n<p>Of course we could also solve that by parenting the box to more <a href=\"/docs/#api/en/core/Object3D\"><code class=\"notranslate\" translate=\"no\">THREE.Object3D</code></a>\nobjects like we covered in <a href=\"scenegraph.html\">scene graphs</a> but the more\nnodes we add to a scene graph the slower it gets.</p>\n<p>We also setup this small hierarchy of nodes of <code class=\"notranslate\" translate=\"no\">lonHelper</code>, <code class=\"notranslate\" translate=\"no\">latHelper</code>, and\n<code class=\"notranslate\" translate=\"no\">positionHelper</code>. We use these objects to compute a position around the sphere\nwere to place the box. </p>\n<div class=\"spread\">\n  <div data-diagram=\"lonLatPos\" style=\"width: 600px; height: 400px;\"></div>\n</div>\n\n<p>Above the <span style=\"color: green;\">green bar</span> represents <code class=\"notranslate\" translate=\"no\">lonHelper</code> and\nis used to rotate toward longitude on the equator. The <span style=\"color: blue;\">\nblue bar</span> represents <code class=\"notranslate\" translate=\"no\">latHelper</code> which is used to rotate to a\nlatitude above or below the equator. The <span style=\"color: red;\">red\nsphere</span> represents the offset that that <code class=\"notranslate\" translate=\"no\">positionHelper</code> provides.</p>\n<p>We could do all of the math manually to figure out positions on the globe but\ndoing it this way leaves most of the math to the library itself so we don't need\nto deal with.</p>\n<p>For each data point we create a <a href=\"/docs/#api/en/materials/MeshBasicMaterial\"><code class=\"notranslate\" translate=\"no\">MeshBasicMaterial</code></a> and a <a href=\"/docs/#api/en/objects/Mesh\"><code class=\"notranslate\" translate=\"no\">Mesh</code></a> and then we ask\nfor the world matrix of the <code class=\"notranslate\" translate=\"no\">positionHelper</code> and apply that to the new <a href=\"/docs/#api/en/objects/Mesh\"><code class=\"notranslate\" translate=\"no\">Mesh</code></a>.\nFinally we scale the mesh at its new position.</p>\n<p>Like above, we could also have created a <code class=\"notranslate\" translate=\"no\">latHelper</code>, <code class=\"notranslate\" translate=\"no\">lonHelper</code>, and\n<code class=\"notranslate\" translate=\"no\">positionHelper</code> for every new box but that would be even slower.</p>\n<p>There are up to 360x145 boxes we're going to create. That's up to 52000 boxes.\nBecause some data points are marked as \"NO_DATA\" the actual number of boxes\nwe're going to create is around 19000. If we added 3 extra helper objects per\nbox that would be nearly 80000 scene graph nodes that THREE.js would have to\ncompute positions for. By instead using one set of helpers to just position the\nmeshes we save around 60000 operations.</p>\n<p>A note about <code class=\"notranslate\" translate=\"no\">lonFudge</code> and <code class=\"notranslate\" translate=\"no\">latFudge</code>. <code class=\"notranslate\" translate=\"no\">lonFudge</code> is π/2 which is a quarter of a turn.\nThat makes sense. It just means the texture or texture coordinates start at a\ndifferent offset around the globe. <code class=\"notranslate\" translate=\"no\">latFudge</code> on the other hand I have no idea\nwhy it needs to be π * -0.135, that's just an amount that made the boxes line up\nwith the texture.</p>\n<p>The last thing we need to do is call our loader</p>\n<pre class=\"prettyprint showlinemods notranslate notranslate\" translate=\"no\">loadFile('resources/data/gpw/gpw_v4_basic_demographic_characteristics_rev10_a000_014mt_2010_cntm_1_deg.asc')\n  .then(parseData)\n-  .then(drawData)\n+  .then(addBoxes)\n+  .then(render);\n</pre><p>Once the data has finished loading and parsing then we need to render at least\nonce since we're <a href=\"rendering-on-demand.html\">rendering on demand</a>.</p>\n<p></p><div translate=\"no\" class=\"threejs_example_container notranslate\">\n  <div></div>\n  <a class=\"threejs_center\" href=\"/manual/examples/lots-of-objects-slow.html\" target=\"_blank\">click here to open in a separate window</a>\n</div>\n\n<p></p>\n<p>If you try to rotate the example above by dragging on the sample you'll likely\nnotice it's slow.</p>\n<p>We can check the framerate by <a href=\"debugging-javascript.html\">opening the\ndevtools</a> and turning on the browser's frame\nrate meter.</p>\n<div class=\"threejs_center\"><img src=\"../resources/images/bring-up-fps-meter.gif\"></div>\n\n<p>On my machine I see a framerate under 20fps.</p>\n<div class=\"threejs_center\"><img src=\"../resources/images/fps-meter.gif\"></div>\n\n<p>That doesn't feel very good to me and I suspect many people have slower machines\nwhich would make it even worse. We'd better look into optimizing.</p>\n<p>For this particular problem we can merge all the boxes into a single geometry.\nWe're currently drawing around 19000 boxes. By merging them into a single\ngeometry we'd remove 18999 operations.</p>\n<p>Here's the new code to merge the boxes into a single geometry.</p>\n<pre class=\"prettyprint showlinemods notranslate lang-js\" translate=\"no\">function addBoxes(file) {\n  const {min, max, data} = file;\n  const range = max - min;\n\n-  // make one box geometry\n-  const boxWidth = 1;\n-  const boxHeight = 1;\n-  const boxDepth = 1;\n-  const geometry = new THREE.BoxGeometry(boxWidth, boxHeight, boxDepth);\n-  // make it so it scales away from the positive Z axis\n-  geometry.applyMatrix4(new THREE.Matrix4().makeTranslation(0, 0, 0.5));\n\n  // these helpers will make it easy to position the boxes\n  // We can rotate the lon helper on its Y axis to the longitude\n  const lonHelper = new THREE.Object3D();\n  scene.add(lonHelper);\n  // We rotate the latHelper on its X axis to the latitude\n  const latHelper = new THREE.Object3D();\n  lonHelper.add(latHelper);\n  // The position helper moves the object to the edge of the sphere\n  const positionHelper = new THREE.Object3D();\n  positionHelper.position.z = 1;\n  latHelper.add(positionHelper);\n+  // Used to move the center of the box so it scales from the position Z axis\n+  const originHelper = new THREE.Object3D();\n+  originHelper.position.z = 0.5;\n+  positionHelper.add(originHelper);\n\n  const lonFudge = Math.PI * .5;\n  const latFudge = Math.PI * -0.135;\n+  const geometries = [];\n  data.forEach((row, latNdx) =&gt; {\n    row.forEach((value, lonNdx) =&gt; {\n      if (value === undefined) {\n        return;\n      }\n      const amount = (value - min) / range;\n\n-      const material = new THREE.MeshBasicMaterial();\n-      const hue = THREE.MathUtils.lerp(0.7, 0.3, amount);\n-      const saturation = 1;\n-      const lightness = THREE.MathUtils.lerp(0.1, 1.0, amount);\n-      material.color.setHSL(hue, saturation, lightness);\n-      const mesh = new THREE.Mesh(geometry, material);\n-      scene.add(mesh);\n\n+      const boxWidth = 1;\n+      const boxHeight = 1;\n+      const boxDepth = 1;\n+      const geometry = new THREE.BoxGeometry(boxWidth, boxHeight, boxDepth);\n\n      // adjust the helpers to point to the latitude and longitude\n      lonHelper.rotation.y = THREE.MathUtils.degToRad(lonNdx + file.xllcorner) + lonFudge;\n      latHelper.rotation.x = THREE.MathUtils.degToRad(latNdx + file.yllcorner) + latFudge;\n\n-      // use the world matrix of the position helper to\n-      // position this mesh.\n-      positionHelper.updateWorldMatrix(true, false);\n-      mesh.applyMatrix4(positionHelper.matrixWorld);\n-\n-      mesh.scale.set(0.005, 0.005, THREE.MathUtils.lerp(0.01, 0.5, amount));\n\n+      // use the world matrix of the origin helper to\n+      // position this geometry\n+      positionHelper.scale.set(0.005, 0.005, THREE.MathUtils.lerp(0.01, 0.5, amount));\n+      originHelper.updateWorldMatrix(true, false);\n+      geometry.applyMatrix4(originHelper.matrixWorld);\n+\n+      geometries.push(geometry);\n    });\n  });\n\n+  const mergedGeometry = BufferGeometryUtils.mergeGeometries(\n+      geometries, false);\n+  const material = new THREE.MeshBasicMaterial({color:'red'});\n+  const mesh = new THREE.Mesh(mergedGeometry, material);\n+  scene.add(mesh);\n\n}\n</pre>\n<p>Above we removed the code that was changing the box geometry's center point and\nare instead doing it by adding an <code class=\"notranslate\" translate=\"no\">originHelper</code>. Before we were using the same\ngeometry 19000 times. This time we are creating new geometry for every single\nbox and since we are going to use <code class=\"notranslate\" translate=\"no\">applyMatrix</code> to move the vertices of each box\ngeometry we might as well do it once instead of twice.</p>\n<p>At the end we pass an array of all the geometries to\n<code class=\"notranslate\" translate=\"no\">BufferGeometryUtils.mergeGeometries</code> which will combined all of\nthem into a single mesh.</p>\n<p>We also need to include the <code class=\"notranslate\" translate=\"no\">BufferGeometryUtils</code></p>\n<pre class=\"prettyprint showlinemods notranslate lang-js\" translate=\"no\">import * as BufferGeometryUtils from 'three/addons/utils/BufferGeometryUtils.js';\n</pre>\n<p>And now, at least on my machine, I get 60 frames per second</p>\n<p></p><div translate=\"no\" class=\"threejs_example_container notranslate\">\n  <div></div>\n  <a class=\"threejs_center\" href=\"/manual/examples/lots-of-objects-merged.html\" target=\"_blank\">click here to open in a separate window</a>\n</div>\n\n<p></p>\n<p>So that worked but because it's one mesh we only get one material which means we\nonly get one color where as before we had a different color on each box. We can\nfix that by using vertex colors.</p>\n<p>Vertex colors add a color per vertex. By setting all the colors of each vertex\nof each box to specific colors every box will have a different color.</p>\n<pre class=\"prettyprint showlinemods notranslate lang-js\" translate=\"no\">+const color = new THREE.Color();\n\nconst lonFudge = Math.PI * .5;\nconst latFudge = Math.PI * -0.135;\nconst geometries = [];\ndata.forEach((row, latNdx) =&gt; {\n  row.forEach((value, lonNdx) =&gt; {\n    if (value === undefined) {\n      return;\n    }\n    const amount = (value - min) / range;\n\n    const boxWidth = 1;\n    const boxHeight = 1;\n    const boxDepth = 1;\n    const geometry = new THREE.BoxGeometry(boxWidth, boxHeight, boxDepth);\n\n    // adjust the helpers to point to the latitude and longitude\n    lonHelper.rotation.y = THREE.MathUtils.degToRad(lonNdx + file.xllcorner) + lonFudge;\n    latHelper.rotation.x = THREE.MathUtils.degToRad(latNdx + file.yllcorner) + latFudge;\n\n    // use the world matrix of the origin helper to\n    // position this geometry\n    positionHelper.scale.set(0.005, 0.005, THREE.MathUtils.lerp(0.01, 0.5, amount));\n    originHelper.updateWorldMatrix(true, false);\n    geometry.applyMatrix4(originHelper.matrixWorld);\n\n+    // compute a color\n+    const hue = THREE.MathUtils.lerp(0.7, 0.3, amount);\n+    const saturation = 1;\n+    const lightness = THREE.MathUtils.lerp(0.4, 1.0, amount);\n+    color.setHSL(hue, saturation, lightness);\n+    // get the colors as an array of values from 0 to 255\n+    const rgb = color.toArray().map(v =&gt; v * 255);\n+\n+    // make an array to store colors for each vertex\n+    const numVerts = geometry.getAttribute('position').count;\n+    const itemSize = 3;  // r, g, b\n+    const colors = new Uint8Array(itemSize * numVerts);\n+\n+    // copy the color into the colors array for each vertex\n+    colors.forEach((v, ndx) =&gt; {\n+      colors[ndx] = rgb[ndx % 3];\n+    });\n+\n+    const normalized = true;\n+    const colorAttrib = new THREE.BufferAttribute(colors, itemSize, normalized);\n+    geometry.setAttribute('color', colorAttrib);\n\n    geometries.push(geometry);\n  });\n});\n</pre>\n<p>The code above looks up the number or vertices needed by getting the <code class=\"notranslate\" translate=\"no\">position</code>\nattribute from the geometry. We then create a <code class=\"notranslate\" translate=\"no\">Uint8Array</code> to put the colors in.\nIt then adds that as an attribute by calling <code class=\"notranslate\" translate=\"no\">geometry.setAttribute</code>.</p>\n<p>Lastly we need to tell three.js to use the vertex colors. </p>\n<pre class=\"prettyprint showlinemods notranslate lang-js\" translate=\"no\">const mergedGeometry = BufferGeometryUtils.mergeGeometries(\n    geometries, false);\n-const material = new THREE.MeshBasicMaterial({color:'red'});\n+const material = new THREE.MeshBasicMaterial({\n+  vertexColors: true,\n+});\nconst mesh = new THREE.Mesh(mergedGeometry, material);\nscene.add(mesh);\n</pre>\n<p>And with that we get our colors back</p>\n<p></p><div translate=\"no\" class=\"threejs_example_container notranslate\">\n  <div></div>\n  <a class=\"threejs_center\" href=\"/manual/examples/lots-of-objects-merged-vertexcolors.html\" target=\"_blank\">click here to open in a separate window</a>\n</div>\n\n<p></p>\n<p>Merging geometry is a common optimization technique. For example rather than\n100 trees you might merge the trees into 1 geometry, a pile of individual rocks\ninto a single geometry of rocks, a picket fence from individual pickets into\none fence mesh. Another example in Minecraft it doesn't likely draw each cube\nindividually but rather creates groups of merged cubes and also selectively removing\nfaces that are never visible.</p>\n<p>The problem with making everything one mesh though is it's no longer easy\nto move any part that was previously separate. Depending on our use case\nthough there are creative solutions. We'll explore one in\n<a href=\"optimize-lots-of-objects-animated.html\">another article</a>.</p>\n<p><canvas id=\"c\"></canvas></p>\n\n        </div>\n      </div>\n    </div>\n\n  \n  \n\n\n\n\n<canvas id=\"c\" data-engine=\"three.js r185\"></canvas>",
    "success": true,
    "error": "",
    "crawled_at": 1786376809.300204,
    "is_shell": false
  },
  {
    "url": "https://threejs.org/manual/en/optimize-lots-of-objects-animated.html",
    "title": "Optimize Lots of Objects Animated",
    "content": "<div class=\"container\">\n      <div class=\"lesson-title\">\n        <h1>Optimize Lots of Objects Animated</h1>\n      </div>\n      <div class=\"lesson\">\n        <div class=\"lesson-main\">\n          <p>This article is a continuation of <a href=\"optimize-lots-of-objects.html\">an article about optimizing lots of objects\n</a>. If you haven't read that\nyet please read it before proceeding. </p>\n<p>In the previous article we merged around 19000 cubes into a\nsingle geometry. This had the advantage that it optimized our drawing\nof 19000 cubes but it had the disadvantage of make it harder to\nmove any individual cube.</p>\n<p>Depending on what we are trying to accomplish there are different solutions.\nIn this case let's graph multiple sets of data and animate between the sets.</p>\n<p>The first thing we need to do is get multiple sets of data. Ideally we'd\nprobably pre-process data offline but in this case let's load 2 sets of\ndata and generate 2 more</p>\n<p>Here's our old loading code</p>\n<pre class=\"prettyprint showlinemods notranslate lang-js\" translate=\"no\">loadFile('resources/data/gpw/gpw_v4_basic_demographic_characteristics_rev10_a000_014mt_2010_cntm_1_deg.asc')\n  .then(parseData)\n  .then(addBoxes)\n  .then(render);\n</pre>\n<p>Let's change it to something like this</p>\n<pre class=\"prettyprint showlinemods notranslate lang-js\" translate=\"no\">async function loadData(info) {\n  const text = await loadFile(info.url);\n  info.file = parseData(text);\n}\n\nasync function loadAll() {\n  const fileInfos = [\n    {name: 'men',   hueRange: [0.7, 0.3], url: 'resources/data/gpw/gpw_v4_basic_demographic_characteristics_rev10_a000_014mt_2010_cntm_1_deg.asc' },\n    {name: 'women', hueRange: [0.9, 1.1], url: 'resources/data/gpw/gpw_v4_basic_demographic_characteristics_rev10_a000_014ft_2010_cntm_1_deg.asc' },\n  ];\n\n  await Promise.all(fileInfos.map(loadData));\n\n  ...\n}\nloadAll();\n</pre>\n<p>The code above will load all the files in <code class=\"notranslate\" translate=\"no\">fileInfos</code> and when done each object\nin <code class=\"notranslate\" translate=\"no\">fileInfos</code> will have a <code class=\"notranslate\" translate=\"no\">file</code> property with the loaded file. <code class=\"notranslate\" translate=\"no\">name</code> and <code class=\"notranslate\" translate=\"no\">hueRange</code>\nwe'll use later. <code class=\"notranslate\" translate=\"no\">name</code> will be for a UI field. <code class=\"notranslate\" translate=\"no\">hueRange</code> will be used to\nchoose a range of hues to map over.</p>\n<p>The two files above are apparently the number of men per area and the number of\nwomen per area as of 2010. Note, I have no idea if this data is correct but\nit's not important really. The important part is showing different sets\nof data.</p>\n<p>Let's generate 2 more sets of data. One being the places where the number\nmen are greater than the number of women and vice versa, the places where\nthe number of women are greater than the number of men. </p>\n<p>The first thing let's write a function that given a 2 dimensional array\nof arrays like we had before will map over it to generate a new 2 dimensional\narray of arrays</p>\n<pre class=\"prettyprint showlinemods notranslate lang-js\" translate=\"no\">function mapValues(data, fn) {\n  return data.map((row, rowNdx) =&gt; {\n    return row.map((value, colNdx) =&gt; {\n      return fn(value, rowNdx, colNdx);\n    });\n  });\n}\n</pre>\n<p>Like the normal <code class=\"notranslate\" translate=\"no\">Array.map</code> function the <code class=\"notranslate\" translate=\"no\">mapValues</code> function calls a function\n<code class=\"notranslate\" translate=\"no\">fn</code> for each value in the array of arrays. It passes it the value and both the\nrow and column indices.</p>\n<p>Now let's make some code to generate a new file that is a comparison between 2\nfiles</p>\n<pre class=\"prettyprint showlinemods notranslate lang-js\" translate=\"no\">function makeDiffFile(baseFile, otherFile, compareFn) {\n  let min;\n  let max;\n  const baseData = baseFile.data;\n  const otherData = otherFile.data;\n  const data = mapValues(baseData, (base, rowNdx, colNdx) =&gt; {\n    const other = otherData[rowNdx][colNdx];\n      if (base === undefined || other === undefined) {\n        return undefined;\n      }\n      const value = compareFn(base, other);\n      min = Math.min(min === undefined ? value : min, value);\n      max = Math.max(max === undefined ? value : max, value);\n      return value;\n  });\n  // make a copy of baseFile and replace min, max, and data\n  // with the new data\n  return {...baseFile, min, max, data};\n}\n</pre>\n<p>The code above uses <code class=\"notranslate\" translate=\"no\">mapValues</code> to generate a new set of data that is\na comparison based on the <code class=\"notranslate\" translate=\"no\">compareFn</code> function passed in. It also tracks\nthe <code class=\"notranslate\" translate=\"no\">min</code> and <code class=\"notranslate\" translate=\"no\">max</code> comparison results. Finally it makes a new file with\nall the same properties as <code class=\"notranslate\" translate=\"no\">baseFile</code> except with a new <code class=\"notranslate\" translate=\"no\">min</code>, <code class=\"notranslate\" translate=\"no\">max</code> and <code class=\"notranslate\" translate=\"no\">data</code>.</p>\n<p>Then let's use that to make 2 new sets of data</p>\n<pre class=\"prettyprint showlinemods notranslate lang-js\" translate=\"no\">{\n  const menInfo = fileInfos[0];\n  const womenInfo = fileInfos[1];\n  const menFile = menInfo.file;\n  const womenFile = womenInfo.file;\n\n  function amountGreaterThan(a, b) {\n    return Math.max(a - b, 0);\n  }\n  fileInfos.push({\n    name: '&gt;50%men',\n    hueRange: [0.6, 1.1],\n    file: makeDiffFile(menFile, womenFile, (men, women) =&gt; {\n      return amountGreaterThan(men, women);\n    }),\n  });\n  fileInfos.push({\n    name: '&gt;50% women',\n    hueRange: [0.0, 0.4],\n    file: makeDiffFile(womenFile, menFile, (women, men) =&gt; {\n      return amountGreaterThan(women, men);\n    }),\n  });\n}\n</pre>\n<p>Now let's generate a UI to select between these sets of data. First we need\nsome UI html</p>\n<pre class=\"prettyprint showlinemods notranslate lang-html\" translate=\"no\">&lt;body&gt;\n  &lt;canvas id=\"c\"&gt;&lt;/canvas&gt;\n+  &lt;div id=\"ui\"&gt;&lt;/div&gt;\n&lt;/body&gt;\n</pre>\n<p>and some CSS to make it appear in the top left area</p>\n<pre class=\"prettyprint showlinemods notranslate lang-css\" translate=\"no\">#ui {\n  position: absolute;\n  left: 1em;\n  top: 1em;\n}\n#ui&gt;div {\n  font-size: 20pt;\n  padding: 1em;\n  display: inline-block;\n}\n#ui&gt;div.selected {\n  color: red;\n}\n</pre>\n<p>Then we can go over each file and generate a set of merged boxes per\nset of data and an element which when hovered over will show that set\nand hide all others.</p>\n<pre class=\"prettyprint showlinemods notranslate lang-js\" translate=\"no\">// show the selected data, hide the rest\nfunction showFileInfo(fileInfos, fileInfo) {\n  fileInfos.forEach((info) =&gt; {\n    const visible = fileInfo === info;\n    info.root.visible = visible;\n    info.elem.className = visible ? 'selected' : '';\n  });\n  requestRenderIfNotRequested();\n}\n\nconst uiElem = document.querySelector('#ui');\nfileInfos.forEach((info) =&gt; {\n  const boxes = addBoxes(info.file, info.hueRange);\n  info.root = boxes;\n  const div = document.createElement('div');\n  info.elem = div;\n  div.textContent = info.name;\n  uiElem.appendChild(div);\n  div.addEventListener('mouseover', () =&gt; {\n    showFileInfo(fileInfos, info);\n  });\n});\n// show the first set of data\nshowFileInfo(fileInfos, fileInfos[0]);\n</pre>\n<p>The one more change we need from the previous example is we need to make\n<code class=\"notranslate\" translate=\"no\">addBoxes</code> take a <code class=\"notranslate\" translate=\"no\">hueRange</code></p>\n<pre class=\"prettyprint showlinemods notranslate lang-js\" translate=\"no\">-function addBoxes(file) {\n+function addBoxes(file, hueRange) {\n\n  ...\n\n    // compute a color\n-    const hue = THREE.MathUtils.lerp(0.7, 0.3, amount);\n+    const hue = THREE.MathUtils.lerp(...hueRange, amount);\n\n  ...\n</pre>\n<p>and with that we should be able to show 4 sets of data. Hover the mouse over the labels\nor touch them to switch sets</p>\n<p></p><div translate=\"no\" class=\"threejs_example_container notranslate\">\n  <div></div>\n  <a class=\"threejs_center\" href=\"/manual/examples/lots-of-objects-multiple-data-sets.html\" target=\"_blank\">click here to open in a separate window</a>\n</div>\n\n<p></p>\n<p>Note, there are a few strange data points that really stick out. I wonder what's up\nwith those!??! In any case how do we animate between these 4 sets of data.</p>\n<p>Lots of ideas.</p>\n<ul>\n<li><p>Just fade between them using <a href=\"/docs/#api/en/materials/Material.opacity\"><code class=\"notranslate\" translate=\"no\">Material.opacity</code></a></p>\n<p>The problem with this solution is the cubes perfectly overlap which\nmeans there will be z-fighting issues. It's possible we could fix\nthat by changing the depth function and using blending. We should\nprobably look into it.</p>\n</li>\n<li><p>Scale up the set we want to see and scale down the other sets</p>\n<p>Because all the boxes have their origin at the center of the planet\nif we scale them below 1.0 they will sink into the planet. At first that\nsounds like a good idea but the issue is all the low height boxes\nwill disappear almost immediately and not be replaced until the new\ndata set scales up to 1.0. This makes the transition not very pleasant.\nWe could maybe fix that with a fancy custom shader.</p>\n</li>\n<li><p>Use Morphtargets</p>\n<p>Morphtargets are a way were we supply multiple values for each vertex\nin the geometry and <em>morph</em> or lerp (linear interpolate) between them.\nMorphtargets are most commonly used for facial animation of 3D characters\nbut that's not their only use.</p>\n</li>\n</ul>\n<p>Let's try morphtargets.</p>\n<p>We'll still make a geometry for each set of data but we'll then extract\nthe <code class=\"notranslate\" translate=\"no\">position</code> attribute from each one and use them as morphtargets.</p>\n<p>First let's change <code class=\"notranslate\" translate=\"no\">addBoxes</code> to just make and return the merged geometry.</p>\n<pre class=\"prettyprint showlinemods notranslate lang-js\" translate=\"no\">-function addBoxes(file, hueRange) {\n+function makeBoxes(file, hueRange) {\n  const {min, max, data} = file;\n  const range = max - min;\n\n  ...\n\n-  const mergedGeometry = BufferGeometryUtils.mergeGeometries(\n-      geometries, false);\n-  const material = new THREE.MeshBasicMaterial({\n-    vertexColors: true,\n-  });\n-  const mesh = new THREE.Mesh(mergedGeometry, material);\n-  scene.add(mesh);\n-  return mesh;\n+  return BufferGeometryUtils.mergeGeometries(\n+     geometries, false);\n}\n</pre>\n<p>There's one more thing we need to do here though. Morphtargets are required to\nall have exactly the same number of vertices. Vertex #123 in one target needs\nhave a corresponding Vertex #123 in all other targets. But, as it is now\ndifferent data sets might have some data points with no data so no box will be\ngenerated for that point which would mean no corresponding vertices for another\nset. So, we need to check across all data sets and either always generate\nsomething if there is data in any set or, generate nothing if there is data\nmissing in any set. Let's do the latter.</p>\n<pre class=\"prettyprint showlinemods notranslate lang-js\" translate=\"no\">+function dataMissingInAnySet(fileInfos, latNdx, lonNdx) {\n+  for (const fileInfo of fileInfos) {\n+    if (fileInfo.file.data[latNdx][lonNdx] === undefined) {\n+      return true;\n+    }\n+  }\n+  return false;\n+}\n\n-function makeBoxes(file, hueRange) {\n+function makeBoxes(file, hueRange, fileInfos) {\n  const {min, max, data} = file;\n  const range = max - min;\n\n  ...\n\n  const geometries = [];\n  data.forEach((row, latNdx) =&gt; {\n    row.forEach((value, lonNdx) =&gt; {\n+      if (dataMissingInAnySet(fileInfos, latNdx, lonNdx)) {\n+        return;\n+      }\n      const amount = (value - min) / range;\n\n  ...\n</pre>\n<p>Now we'll change the code that was calling <code class=\"notranslate\" translate=\"no\">addBoxes</code> to use <code class=\"notranslate\" translate=\"no\">makeBoxes</code>\nand setup morphtargets</p>\n<pre class=\"prettyprint showlinemods notranslate lang-js\" translate=\"no\">+// make geometry for each data set\n+const geometries = fileInfos.map((info) =&gt; {\n+  return makeBoxes(info.file, info.hueRange, fileInfos);\n+});\n+\n+// use the first geometry as the base\n+// and add all the geometries as morphtargets\n+const baseGeometry = geometries[0];\n+baseGeometry.morphAttributes.position = geometries.map((geometry, ndx) =&gt; {\n+  const attribute = geometry.getAttribute('position');\n+  const name = <code class=\"notranslate\" translate=\"no\">target${ndx}</code>;\n+  attribute.name = name;\n+  return attribute;\n+});\n+baseGeometry.morphAttributes.color = geometries.map((geometry, ndx) =&gt; {\n+  const attribute = geometry.getAttribute('color');\n+  const name = <code class=\"notranslate\" translate=\"no\">target${ndx}</code>;\n+  attribute.name = name;\n+  return attribute;\n+});\n+const material = new THREE.MeshBasicMaterial({\n+  vertexColors: true,\n+});\n+const mesh = new THREE.Mesh(baseGeometry, material);\n+scene.add(mesh);\n\nconst uiElem = document.querySelector('#ui');\nfileInfos.forEach((info) =&gt; {\n-  const boxes = addBoxes(info.file, info.hueRange);\n-  info.root = boxes;\n  const div = document.createElement('div');\n  info.elem = div;\n  div.textContent = info.name;\n  uiElem.appendChild(div);\n  function show() {\n    showFileInfo(fileInfos, info);\n  }\n  div.addEventListener('mouseover', show);\n  div.addEventListener('touchstart', show);\n});\n// show the first set of data\nshowFileInfo(fileInfos, fileInfos[0]);\n</pre>\n<p>Above we make geometry for each data set, use the first one as the base,\nthen get a <code class=\"notranslate\" translate=\"no\">position</code> attribute from each geometry and add it as\na morphtarget to the base geometry for <code class=\"notranslate\" translate=\"no\">position</code>.</p>\n<p>Now we need to change how we're showing and hiding the various data sets.\nInstead of showing or hiding a mesh we need to change the influence of the\nmorphtargets. For the data set we want to see we need to have an influence of 1\nand for all the ones we don't want to see to we need to have an influence of 0.</p>\n<p>We could just set them to 0 or 1 directly but if we did that we wouldn't see any\nanimation, it would just snap which would be no different than what we already\nhave. We could also write some custom animation code which would be easy but\nbecause the original webgl globe uses\n<a href=\"https://github.com/tweenjs/tween.js/\">an animation library</a> let's use the same one here.</p>\n<p>We need to include the library</p>\n<pre class=\"prettyprint showlinemods notranslate lang-js\" translate=\"no\">import * as THREE from 'three';\nimport * as BufferGeometryUtils from 'three/addons/utils/BufferGeometryUtils.js';\nimport {OrbitControls} from 'three/addons/controls/OrbitControls.js';\n+import TWEEN from 'three/addons/libs/tween.module.js';\n</pre>\n<p>And then create a <code class=\"notranslate\" translate=\"no\">Tween</code> to animate the influences.</p>\n<pre class=\"prettyprint showlinemods notranslate lang-js\" translate=\"no\">// show the selected data, hide the rest\nfunction showFileInfo(fileInfos, fileInfo) {\n+  const targets = {};\n-  fileInfos.forEach((info) =&gt; {\n+  fileInfos.forEach((info, i) =&gt; {\n    const visible = fileInfo === info;\n-    info.root.visible = visible;\n    info.elem.className = visible ? 'selected' : '';\n+    targets[i] = visible ? 1 : 0;\n  });\n+  const durationInMs = 1000;\n+  new TWEEN.Tween(mesh.morphTargetInfluences)\n+    .to(targets, durationInMs)\n+    .start();\n  requestRenderIfNotRequested();\n}\n</pre>\n<p>We're also suppose to call <code class=\"notranslate\" translate=\"no\">TWEEN.update</code> every frame inside our render loop\nbut that points out a problem. \"tween.js\" is designed for continuous rendering\nbut we are <a href=\"rendering-on-demand.html\">rendering on demand</a>. We could\nswitch to continuous rendering but it's sometimes nice to only render on demand\nas it well stop using the user's power when nothing is happening\nso let's see if we can make it animate on demand.</p>\n<p>We'll make a <code class=\"notranslate\" translate=\"no\">TweenManager</code> to help. We'll use it to create the <code class=\"notranslate\" translate=\"no\">Tween</code>s and\ntrack them. It will have an <code class=\"notranslate\" translate=\"no\">update</code> method that will return <code class=\"notranslate\" translate=\"no\">true</code>\nif we need to call it again and <code class=\"notranslate\" translate=\"no\">false</code> if all the animations are finished.</p>\n<pre class=\"prettyprint showlinemods notranslate lang-js\" translate=\"no\">class TweenManger {\n  constructor() {\n    this.numTweensRunning = 0;\n  }\n  _handleComplete() {\n    --this.numTweensRunning;\n    console.assert(this.numTweensRunning &gt;= 0);\n  }\n  createTween(targetObject) {\n    const self = this;\n    ++this.numTweensRunning;\n    let userCompleteFn = () =&gt; {};\n    // create a new tween and install our own onComplete callback\n    const tween = new TWEEN.Tween(targetObject).onComplete(function(...args) {\n      self._handleComplete();\n      userCompleteFn.call(this, ...args);\n    });\n    // replace the tween's onComplete function with our own\n    // so we can call the user's callback if they supply one.\n    tween.onComplete = (fn) =&gt; {\n      userCompleteFn = fn;\n      return tween;\n    };\n    return tween;\n  }\n  update() {\n    TWEEN.update();\n    return this.numTweensRunning &gt; 0;\n  }\n}\n</pre>\n<p>To use it we'll create one </p>\n<pre class=\"prettyprint showlinemods notranslate lang-js\" translate=\"no\">function main() {\n  const canvas = document.querySelector('#c');\n  const renderer = new THREE.WebGLRenderer({antialias: true, canvas});\n+  const tweenManager = new TweenManger();\n\n  ...\n</pre>\n<p>We'll use it to create our <code class=\"notranslate\" translate=\"no\">Tween</code>s.</p>\n<pre class=\"prettyprint showlinemods notranslate lang-js\" translate=\"no\">// show the selected data, hide the rest\nfunction showFileInfo(fileInfos, fileInfo) {\n  const targets = {};\n  fileInfos.forEach((info, i) =&gt; {\n    const visible = fileInfo === info;\n    info.elem.className = visible ? 'selected' : '';\n    targets[i] = visible ? 1 : 0;\n  });\n  const durationInMs = 1000;\n-  new TWEEN.Tween(mesh.morphTargetInfluences)\n+  tweenManager.createTween(mesh.morphTargetInfluences)\n    .to(targets, durationInMs)\n    .start();\n  requestRenderIfNotRequested();\n}\n</pre>\n<p>Then we'll update our render loop to update the tweens and keep rendering\nif there are still animations running.</p>\n<pre class=\"prettyprint showlinemods notranslate lang-js\" translate=\"no\">function render() {\n  renderRequested = false;\n\n  if (resizeRendererToDisplaySize(renderer)) {\n    const canvas = renderer.domElement;\n    camera.aspect = canvas.clientWidth / canvas.clientHeight;\n    camera.updateProjectionMatrix();\n  }\n\n+  if (tweenManager.update()) {\n+    requestRenderIfNotRequested();\n+  }\n\n  controls.update();\n  renderer.render(scene, camera);\n}\nrender();\n</pre>\n<p>And with that we should be animating between data sets.</p>\n<p></p><div translate=\"no\" class=\"threejs_example_container notranslate\">\n  <div></div>\n  <a class=\"threejs_center\" href=\"/manual/examples/lots-of-objects-morphtargets.html\" target=\"_blank\">click here to open in a separate window</a>\n</div>\n\n<p></p>\n<p>I hope going through this was helpful. Using morphtargets is a common technique to\nmove lots of objects. As an example we could give every cube a random place in\nanother target and morph from that to their first positions on the globe. That\nmight be a cool way to introduce the globe.</p>\n<p>Next you might be interested in adding labels to a globe which is covered\nin <a href=\"align-html-elements-to-3d.html\">Aligning HTML Elements to 3D</a>.</p>\n<p>Note: We could try to just graph percent of men or percent of women or the raw\ndifference but based on how we are displaying the info, cubes that grow from the\nsurface of the earth, we'd prefer most cubes to be low. If we used one of these\nother comparisons most cubes would be about 1/2 their maximum height which would\nnot make a good visualization. Feel free to change the <code class=\"notranslate\" translate=\"no\">amountGreaterThan</code> from\n<a href=\"/docs/#api/en/math/Math.max(a - b, 0)\"><code class=\"notranslate\" translate=\"no\">Math.max(a - b, 0)</code></a> to something like <code class=\"notranslate\" translate=\"no\">(a - b)</code> \"raw difference\" or <code class=\"notranslate\" translate=\"no\">a / (a +\nb)</code> \"percent\" and you'll see what I mean.</p>\n\n        </div>\n      </div>\n    </div>",
    "success": true,
    "error": "",
    "crawled_at": 1786376809.300204,
    "is_shell": false
  },
  {
    "url": "https://threejs.org/manual/en/offscreencanvas.html",
    "title": "OffscreenCanvas",
    "content": "<div class=\"container\">\n      <div class=\"lesson-title\">\n        <h1>OffscreenCanvas</h1>\n      </div>\n      <div class=\"lesson\">\n        <div class=\"lesson-main\">\n          <p><a href=\"https://developer.mozilla.org/en-US/docs/Web/API/OffscreenCanvas\"><code class=\"notranslate\" translate=\"no\">OffscreenCanvas</code></a>\nis a relatively new browser feature currently only available in Chrome but apparently\ncoming to other browsers. <code class=\"notranslate\" translate=\"no\">OffscreenCanvas</code> allows a web worker to render\nto a canvas. This is a way to offload heavy work, like rendering a complex 3D scene,\nto a web worker so as not to slow down the responsiveness of the browser. It\nalso means data is loaded and parsed in the worker so possibly less jank while\nthe page loads.</p>\n<p>Getting <em>started</em> using it is pretty straight forward. Let's port the 3 spinning cube\nexample from <a href=\"responsive.html\">the article on responsiveness</a>.</p>\n<p>Workers generally have their code separated\ninto another script file whereas most of the examples on this site have had\ntheir scripts embedded into the HTML file of the page they are on.</p>\n<p>In our case we'll make a file called <code class=\"notranslate\" translate=\"no\">offscreencanvas-cubes.js</code> and\ncopy all the JavaScript from <a href=\"responsive.html\">the responsive example</a> into it. We'll then\nmake the changes needed for it to run in a worker.</p>\n<p>We still need some JavaScript in our HTML file. The first thing\nwe need to do there is look up the canvas and then transfer control of that\ncanvas to be offscreen by calling <code class=\"notranslate\" translate=\"no\">canvas.transferControlToOffscreen</code>.</p>\n<pre class=\"prettyprint showlinemods notranslate lang-js\" translate=\"no\">function main() {\n  const canvas = document.querySelector('#c');\n  const offscreen = canvas.transferControlToOffscreen();\n\n  ...\n</pre>\n<p>We can then start our worker with <code class=\"notranslate\" translate=\"no\">new Worker(pathToScript, {type: 'module'})</code>.\nand pass the <code class=\"notranslate\" translate=\"no\">offscreen</code> object to it.</p>\n<pre class=\"prettyprint showlinemods notranslate lang-js\" translate=\"no\">function main() {\n  const canvas = document.querySelector('#c');\n  const offscreen = canvas.transferControlToOffscreen();\n  const worker = new Worker('offscreencanvas-cubes.js', {type: 'module'});\n  worker.postMessage({type: 'main', canvas: offscreen}, [offscreen]);\n}\nmain();\n</pre>\n<p>It's important to note that workers can't access the <code class=\"notranslate\" translate=\"no\">DOM</code>. They\ncan't look at HTML elements nor can they receive mouse events or\nkeyboard events. The only thing they can generally do is respond\nto messages sent to them and send messages back to the page.</p>\n<p>To send a message to a worker we call <a href=\"https://developer.mozilla.org/en-US/docs/Web/API/Worker/postMessage\"><code class=\"notranslate\" translate=\"no\">worker.postMessage</code></a> and\npass it 1 or 2 arguments. The first argument is a JavaScript object\nthat will be <a href=\"https://developer.mozilla.org/en-US/docs/Web/API/Web_Workers_API/Structured_clone_algorithm\">cloned</a>\nand sent to the worker. The second argument is an optional array\nof objects that are part of the first object that we want <em>transferred</em>\nto the worker. These objects will not be cloned. Instead they will be <em>transferred</em>\nand will cease to exist in the main page. Cease to exist is the probably\nthe wrong description, rather they are neutered. Only certain types of\nobjects can be transferred instead of cloned. They include <code class=\"notranslate\" translate=\"no\">OffscreenCanvas</code>\nso once transferred the <code class=\"notranslate\" translate=\"no\">offscreen</code> object back in the main page is useless.</p>\n<p>Workers receive messages from their <code class=\"notranslate\" translate=\"no\">onmessage</code> handler. The object\nwe passed to <code class=\"notranslate\" translate=\"no\">postMessage</code> arrives on <code class=\"notranslate\" translate=\"no\">event.data</code> passed to the <code class=\"notranslate\" translate=\"no\">onmessage</code>\nhandler on the worker. The code above declares a <code class=\"notranslate\" translate=\"no\">type: 'main'</code> in the object it passes\nto the worker. This object has no meaning to the browser. It's entirely for\nour own usage. We'll make a handler that based on <code class=\"notranslate\" translate=\"no\">type</code> calls\na different function in the worker. Then we can add functions as\nneeded and easily call them from the main page.</p>\n<pre class=\"prettyprint showlinemods notranslate lang-js\" translate=\"no\">const handlers = {\n  main,\n};\n\nself.onmessage = function(e) {\n  const fn = handlers[e.data.type];\n  if (typeof fn !== 'function') {\n    throw new Error('no handler for type: ' + e.data.type);\n  }\n  fn(e.data);\n};\n</pre>\n<p>You can see above we just look up the handler based on the <code class=\"notranslate\" translate=\"no\">type</code> pass it the <code class=\"notranslate\" translate=\"no\">data</code>\nthat was sent from the main page.</p>\n<p>So now we just need to start changing the <code class=\"notranslate\" translate=\"no\">main</code> we pasted into\n<code class=\"notranslate\" translate=\"no\">offscreencanvas-cubes.js</code> from <a href=\"responsive.html\">the responsive article</a>.</p>\n<p>Instead of looking up the canvas from the DOM we'll receive it from the\nevent data.</p>\n<pre class=\"prettyprint showlinemods notranslate lang-js\" translate=\"no\">-function main() {\n-  const canvas = document.querySelector('#c');\n+function main(data) {\n+  const {canvas} = data;\n  const renderer = new THREE.WebGLRenderer({antialias: true, canvas});\n\n  ...\n</pre>\n<p>Remembering that workers can't see the DOM at all the first problem\nwe run into is <code class=\"notranslate\" translate=\"no\">resizeRendererToDisplaySize</code> can't look at <code class=\"notranslate\" translate=\"no\">canvas.clientWidth</code>\nand <code class=\"notranslate\" translate=\"no\">canvas.clientHeight</code> as those are DOM values. Here's the original code</p>\n<pre class=\"prettyprint showlinemods notranslate lang-js\" translate=\"no\">function resizeRendererToDisplaySize(renderer) {\n  const canvas = renderer.domElement;\n  const width = canvas.clientWidth;\n  const height = canvas.clientHeight;\n  const needResize = canvas.width !== width || canvas.height !== height;\n  if (needResize) {\n    renderer.setSize(width, height, false);\n  }\n  return needResize;\n}\n</pre>\n<p>Instead we'll need to send sizes as they change to the worker.\nSo, let's add some global state and keep the width and height there.</p>\n<pre class=\"prettyprint showlinemods notranslate lang-js\" translate=\"no\">const state = {\n  width: 300,  // canvas default\n  height: 150,  // canvas default\n};\n</pre>\n<p>Then let's add a <code class=\"notranslate\" translate=\"no\">'size'</code> handler to update those values. </p>\n<pre class=\"prettyprint showlinemods notranslate lang-js\" translate=\"no\">+function size(data) {\n+  state.width = data.width;\n+  state.height = data.height;\n+}\n\nconst handlers = {\n  main,\n+  size,\n};\n</pre>\n<p>Now we can change <code class=\"notranslate\" translate=\"no\">resizeRendererToDisplaySize</code> to use <code class=\"notranslate\" translate=\"no\">state.width</code> and <code class=\"notranslate\" translate=\"no\">state.height</code></p>\n<pre class=\"prettyprint showlinemods notranslate lang-js\" translate=\"no\">function resizeRendererToDisplaySize(renderer) {\n  const canvas = renderer.domElement;\n-  const width = canvas.clientWidth;\n-  const height = canvas.clientHeight;\n+  const width = state.width;\n+  const height = state.height;\n  const needResize = canvas.width !== width || canvas.height !== height;\n  if (needResize) {\n    renderer.setSize(width, height, false);\n  }\n  return needResize;\n}\n</pre>\n<p>and where we compute the aspect we need similar changes</p>\n<pre class=\"prettyprint showlinemods notranslate lang-js\" translate=\"no\">function render(time) {\n  time *= 0.001;\n\n  if (resizeRendererToDisplaySize(renderer)) {\n-    camera.aspect = canvas.clientWidth / canvas.clientHeight;\n+    camera.aspect = state.width / state.height;\n    camera.updateProjectionMatrix();\n  }\n\n  ...\n</pre>\n<p>Back in the main page we'll send a <code class=\"notranslate\" translate=\"no\">size</code> event anytime the page changes size.</p>\n<pre class=\"prettyprint showlinemods notranslate lang-js\" translate=\"no\">const worker = new Worker('offscreencanvas-picking.js', {type: 'module'});\nworker.postMessage({type: 'main', canvas: offscreen}, [offscreen]);\n\n+function sendSize() {\n+  worker.postMessage({\n+    type: 'size',\n+    width: canvas.clientWidth,\n+    height: canvas.clientHeight,\n+  });\n+}\n+\n+window.addEventListener('resize', sendSize);\n+sendSize();\n</pre>\n<p>We also call it once to send the initial size.</p>\n<p>And with just those few changes, assuming your browser fully supports <code class=\"notranslate\" translate=\"no\">OffscreenCanvas</code>\nit should work. Before we run it though let's check if the browser actually supports\n<code class=\"notranslate\" translate=\"no\">OffscreenCanvas</code> and if not display an error. First let's add some HTML to display the error.</p>\n<pre class=\"prettyprint showlinemods notranslate lang-html\" translate=\"no\">&lt;body&gt;\n  &lt;canvas id=\"c\"&gt;&lt;/canvas&gt;\n+  &lt;div id=\"noOffscreenCanvas\" style=\"display:none;\"&gt;\n+    &lt;div&gt;no OffscreenCanvas support&lt;/div&gt;\n+  &lt;/div&gt;\n&lt;/body&gt;\n</pre>\n<p>and some CSS for that</p>\n<pre class=\"prettyprint showlinemods notranslate lang-css\" translate=\"no\">#noOffscreenCanvas {\n    display: flex;\n    width: 100%;\n    height: 100%;\n    align-items: center;\n    justify-content: center;\n    background: red;\n    color: white;\n}\n</pre>\n<p>and then we can check for the existence of <code class=\"notranslate\" translate=\"no\">transferControlToOffscreen</code> to see\nif the browser supports <code class=\"notranslate\" translate=\"no\">OffscreenCanvas</code></p>\n<pre class=\"prettyprint showlinemods notranslate lang-js\" translate=\"no\">function main() {\n  const canvas = document.querySelector('#c');\n+  if (!canvas.transferControlToOffscreen) {\n+    canvas.style.display = 'none';\n+    document.querySelector('#noOffscreenCanvas').style.display = '';\n+    return;\n+  }\n  const offscreen = canvas.transferControlToOffscreen();\n  const worker = new Worker('offscreencanvas-picking.js', {type: 'module});\n  worker.postMessage({type: 'main', canvas: offscreen}, [offscreen]);\n\n  ...\n</pre>\n<p>and with that, if your browser supports <code class=\"notranslate\" translate=\"no\">OffscreenCanvas</code> this example should work</p>\n<p></p><div translate=\"no\" class=\"threejs_example_container notranslate\">\n  <div></div>\n  <a class=\"threejs_center\" href=\"/manual/examples/offscreencanvas.html\" target=\"_blank\">click here to open in a separate window</a>\n</div>\n\n<p></p>\n<p>So that's great but since not every browser supports <code class=\"notranslate\" translate=\"no\">OffscreenCanvas</code> at the moment\nlet's change the code to work with both <code class=\"notranslate\" translate=\"no\">OffscreenCanvas</code> and if not then fallback to using\nthe canvas in the main page like normal.</p>\n<blockquote>\n<p>As an aside, if you need OffscreenCanvas to make your page responsive then\nit's not clear what the point of having a fallback is. Maybe based on if\nyou end up running on the main page or in a worker you might adjust the amount\nof work done so that when running in a worker you can do more than when\nrunning in the main page. What you do is really up to you.</p>\n</blockquote>\n<p>The first thing we should probably do is separate out the three.js\ncode from the code that is specific to the worker. That way we can\nuse the same code on both the main page and the worker. In other words\nwe will now have 3 files</p>\n<ol>\n<li><p>our html file.</p>\n<p><code class=\"notranslate\" translate=\"no\">threejs-offscreencanvas-w-fallback.html</code></p>\n</li>\n<li><p>a JavaScript that contains our three.js code.</p>\n<p><code class=\"notranslate\" translate=\"no\">shared-cubes.js</code></p>\n</li>\n<li><p>our worker support code</p>\n<p><code class=\"notranslate\" translate=\"no\">offscreencanvas-worker-cubes.js</code></p>\n</li>\n</ol>\n<p><code class=\"notranslate\" translate=\"no\">shared-cubes.js</code> and <code class=\"notranslate\" translate=\"no\">offscreencanvas-worker-cubes.js</code> are basically\nthe split of our previous <code class=\"notranslate\" translate=\"no\">offscreencanvas-cubes.js</code> file. First we\ncopy all of <code class=\"notranslate\" translate=\"no\">offscreencanvas-cubes.js</code> to <code class=\"notranslate\" translate=\"no\">shared-cube.js</code>. Then\nwe rename <code class=\"notranslate\" translate=\"no\">main</code> to <code class=\"notranslate\" translate=\"no\">init</code> since we already have a <code class=\"notranslate\" translate=\"no\">main</code> in our\nHTML file and we need to export <code class=\"notranslate\" translate=\"no\">init</code> and <code class=\"notranslate\" translate=\"no\">state</code></p>\n<pre class=\"prettyprint showlinemods notranslate lang-js\" translate=\"no\">import * as THREE from 'three';\n\n-const state = {\n+export const state = {\n  width: 300,   // canvas default\n  height: 150,  // canvas default\n};\n\n-function main(data) {\n+export function init(data) {\n  const {canvas} = data;\n  const renderer = new THREE.WebGLRenderer({antialias: true, canvas});\n</pre>\n<p>and cut out the just the non three.js relates parts</p>\n<pre class=\"prettyprint showlinemods notranslate lang-js\" translate=\"no\">-function size(data) {\n-  state.width = data.width;\n-  state.height = data.height;\n-}\n-\n-const handlers = {\n-  main,\n-  size,\n-};\n-\n-self.onmessage = function(e) {\n-  const fn = handlers[e.data.type];\n-  if (typeof fn !== 'function') {\n-    throw new Error('no handler for type: ' + e.data.type);\n-  }\n-  fn(e.data);\n-};\n</pre>\n<p>Then we copy those parts we just deleted to <code class=\"notranslate\" translate=\"no\">offscreencanvas-worker-cubes.js</code>\nand import <code class=\"notranslate\" translate=\"no\">shared-cubes.js</code> as well as call <code class=\"notranslate\" translate=\"no\">init</code> instead of <code class=\"notranslate\" translate=\"no\">main</code>.</p>\n<pre class=\"prettyprint showlinemods notranslate lang-js\" translate=\"no\">import {init, state} from './shared-cubes.js';\n\nfunction size(data) {\n  state.width = data.width;\n  state.height = data.height;\n}\n\nconst handlers = {\n-  main,\n+  init,\n  size,\n};\n\nself.onmessage = function(e) {\n  const fn = handlers[e.data.type];\n  if (typeof fn !== 'function') {\n    throw new Error('no handler for type: ' + e.data.type);\n  }\n  fn(e.data);\n};\n</pre>\n<p>Similarly we need to include <code class=\"notranslate\" translate=\"no\">shared-cubes.js</code> in the main page</p>\n<pre class=\"prettyprint showlinemods notranslate lang-html\" translate=\"no\">&lt;script type=\"module\"&gt;\n+import {init, state} from './shared-cubes.js';\n</pre>\n<p>We can remove the HTML and CSS we added previously</p>\n<pre class=\"prettyprint showlinemods notranslate lang-html\" translate=\"no\">&lt;body&gt;\n  &lt;canvas id=\"c\"&gt;&lt;/canvas&gt;\n-  &lt;div id=\"noOffscreenCanvas\" style=\"display:none;\"&gt;\n-    &lt;div&gt;no OffscreenCanvas support&lt;/div&gt;\n-  &lt;/div&gt;\n&lt;/body&gt;\n</pre>\n<p>and some CSS for that</p>\n<pre class=\"prettyprint showlinemods notranslate lang-css\" translate=\"no\">-#noOffscreenCanvas {\n-    display: flex;\n-    width: 100%;\n-    height: 100%;\n-    align-items: center;\n-    justify-content: center;\n-    background: red;\n-    color: white;\n-}\n</pre>\n<p>Then let's change the code in the main page to call one start\nfunction or another depending on if the browser supports <code class=\"notranslate\" translate=\"no\">OffscreenCanvas</code>.</p>\n<pre class=\"prettyprint showlinemods notranslate lang-js\" translate=\"no\">function main() {\n  const canvas = document.querySelector('#c');\n-  if (!canvas.transferControlToOffscreen) {\n-    canvas.style.display = 'none';\n-    document.querySelector('#noOffscreenCanvas').style.display = '';\n-    return;\n-  }\n-  const offscreen = canvas.transferControlToOffscreen();\n-  const worker = new Worker('offscreencanvas-picking.js', {type: 'module'});\n-  worker.postMessage({type: 'main', canvas: offscreen}, [offscreen]);\n+  if (canvas.transferControlToOffscreen) {\n+    startWorker(canvas);\n+  } else {\n+    startMainPage(canvas);\n+  }\n  ...\n</pre>\n<p>We'll move all the code we had to setup the worker inside <code class=\"notranslate\" translate=\"no\">startWorker</code></p>\n<pre class=\"prettyprint showlinemods notranslate lang-js\" translate=\"no\">function startWorker(canvas) {\n  const offscreen = canvas.transferControlToOffscreen();\n  const worker = new Worker('offscreencanvas-worker-cubes.js', {type: 'module'});\n  worker.postMessage({type: 'main', canvas: offscreen}, [offscreen]);\n\n  function sendSize() {\n    worker.postMessage({\n      type: 'size',\n      width: canvas.clientWidth,\n      height: canvas.clientHeight,\n    });\n  }\n\n  window.addEventListener('resize', sendSize);\n  sendSize();\n\n  console.log('using OffscreenCanvas');\n}\n</pre>\n<p>and send <code class=\"notranslate\" translate=\"no\">init</code> instead of <code class=\"notranslate\" translate=\"no\">main</code></p>\n<pre class=\"prettyprint showlinemods notranslate lang-js\" translate=\"no\">-  worker.postMessage({type: 'main', canvas: offscreen}, [offscreen]);\n+  worker.postMessage({type: 'init', canvas: offscreen}, [offscreen]);\n</pre>\n<p>for starting in the main page we can do this</p>\n<pre class=\"prettyprint showlinemods notranslate lang-js\" translate=\"no\">function startMainPage(canvas) {\n  init({canvas});\n\n  function sendSize() {\n    state.width = canvas.clientWidth;\n    state.height = canvas.clientHeight;\n  }\n  window.addEventListener('resize', sendSize);\n  sendSize();\n\n  console.log('using regular canvas');\n}\n</pre>\n<p>and with that our example will run either in an OffscreenCanvas or\nfallback to running in the main page.</p>\n<p></p><div translate=\"no\" class=\"threejs_example_container notranslate\">\n  <div></div>\n  <a class=\"threejs_center\" href=\"/manual/examples/offscreencanvas-w-fallback.html\" target=\"_blank\">click here to open in a separate window</a>\n</div>\n\n<p></p>\n<p>So that was relatively easy. Let's try picking. We'll take some code from\nthe <code class=\"notranslate\" translate=\"no\">RayCaster</code> example from <a href=\"picking.html\">the article on picking</a>\nand make it work offscreen.</p>\n<p>Let's copy the <code class=\"notranslate\" translate=\"no\">shared-cube.js</code> to <code class=\"notranslate\" translate=\"no\">shared-picking.js</code> and add the\npicking parts. We copy in the <code class=\"notranslate\" translate=\"no\">PickHelper</code> </p>\n<pre class=\"prettyprint showlinemods notranslate lang-js\" translate=\"no\">class PickHelper {\n  constructor() {\n    this.raycaster = new THREE.Raycaster();\n    this.pickedObject = null;\n    this.pickedObjectSavedColor = 0;\n  }\n  pick(normalizedPosition, scene, camera, time) {\n    // restore the color if there is a picked object\n    if (this.pickedObject) {\n      this.pickedObject.material.emissive.setHex(this.pickedObjectSavedColor);\n      this.pickedObject = undefined;\n    }\n\n    // cast a ray through the frustum\n    this.raycaster.setFromCamera(normalizedPosition, camera);\n    // get the list of objects the ray intersected\n    const intersectedObjects = this.raycaster.intersectObjects(scene.children);\n    if (intersectedObjects.length) {\n      // pick the first object. It's the closest one\n      this.pickedObject = intersectedObjects[0].object;\n      // save its color\n      this.pickedObjectSavedColor = this.pickedObject.material.emissive.getHex();\n      // set its emissive color to flashing red/yellow\n      this.pickedObject.material.emissive.setHex((time * 8) % 2 &gt; 1 ? 0xFFFF00 : 0xFF0000);\n    }\n  }\n}\n\nconst pickPosition = {x: 0, y: 0};\nconst pickHelper = new PickHelper();\n</pre>\n<p>We updated <code class=\"notranslate\" translate=\"no\">pickPosition</code> from the mouse like this</p>\n<pre class=\"prettyprint showlinemods notranslate lang-js\" translate=\"no\">function getCanvasRelativePosition(event) {\n  const rect = canvas.getBoundingClientRect();\n  return {\n    x: (event.clientX - rect.left) * canvas.width  / rect.width,\n    y: (event.clientY - rect.top ) * canvas.height / rect.height,\n  };\n}\n\nfunction setPickPosition(event) {\n  const pos = getCanvasRelativePosition(event);\n  pickPosition.x = (pos.x / canvas.width ) *  2 - 1;\n  pickPosition.y = (pos.y / canvas.height) * -2 + 1;  // note we flip Y\n}\nwindow.addEventListener('mousemove', setPickPosition);\n</pre>\n<p>A worker can't read the mouse position directly so just like the size code\nlet's send a message with the mouse position. Like the size code we'll\nsend the mouse position and update <code class=\"notranslate\" translate=\"no\">pickPosition</code></p>\n<pre class=\"prettyprint showlinemods notranslate lang-js\" translate=\"no\">function size(data) {\n  state.width = data.width;\n  state.height = data.height;\n}\n\n+function mouse(data) {\n+  pickPosition.x = data.x;\n+  pickPosition.y = data.y;\n+}\n\nconst handlers = {\n  init,\n+  mouse,\n  size,\n};\n\nself.onmessage = function(e) {\n  const fn = handlers[e.data.type];\n  if (typeof fn !== 'function') {\n    throw new Error('no handler for type: ' + e.data.type);\n  }\n  fn(e.data);\n};\n</pre>\n<p>Back in our main page we need to add code to pass the mouse\nto the worker or the main page.</p>\n<pre class=\"prettyprint showlinemods notranslate lang-js\" translate=\"no\">+let sendMouse;\n\nfunction startWorker(canvas) {\n  const offscreen = canvas.transferControlToOffscreen();\n  const worker = new Worker('offscreencanvas-worker-picking.js', {type: 'module'});\n  worker.postMessage({type: 'init', canvas: offscreen}, [offscreen]);\n\n+  sendMouse = (x, y) =&gt; {\n+    worker.postMessage({\n+      type: 'mouse',\n+      x,\n+      y,\n+    });\n+  };\n\n  function sendSize() {\n    worker.postMessage({\n      type: 'size',\n      width: canvas.clientWidth,\n      height: canvas.clientHeight,\n    });\n  }\n\n  window.addEventListener('resize', sendSize);\n  sendSize();\n\n  console.log('using OffscreenCanvas');  /* eslint-disable-line no-console */\n}\n\nfunction startMainPage(canvas) {\n  init({canvas});\n\n+  sendMouse = (x, y) =&gt; {\n+    pickPosition.x = x;\n+    pickPosition.y = y;\n+  };\n\n  function sendSize() {\n    state.width = canvas.clientWidth;\n    state.height = canvas.clientHeight;\n  }\n  window.addEventListener('resize', sendSize);\n  sendSize();\n\n  console.log('using regular canvas');  /* eslint-disable-line no-console */\n}\n</pre>\n<p>Then we can copy in all the mouse handling code to the main page and\nmake just minor changes to use <code class=\"notranslate\" translate=\"no\">sendMouse</code></p>\n<pre class=\"prettyprint showlinemods notranslate lang-js\" translate=\"no\">function setPickPosition(event) {\n  const pos = getCanvasRelativePosition(event);\n-  pickPosition.x = (pos.x / canvas.clientWidth ) *  2 - 1;\n-  pickPosition.y = (pos.y / canvas.clientHeight) * -2 + 1;  // note we flip Y\n+  sendMouse(\n+      (pos.x / canvas.clientWidth ) *  2 - 1,\n+      (pos.y / canvas.clientHeight) * -2 + 1);  // note we flip Y\n}\n\nfunction clearPickPosition() {\n  // unlike the mouse which always has a position\n  // if the user stops touching the screen we want\n  // to stop picking. For now we just pick a value\n  // unlikely to pick something\n-  pickPosition.x = -100000;\n-  pickPosition.y = -100000;\n+  sendMouse(-100000, -100000);\n}\nwindow.addEventListener('mousemove', setPickPosition);\nwindow.addEventListener('mouseout', clearPickPosition);\nwindow.addEventListener('mouseleave', clearPickPosition);\n\nwindow.addEventListener('touchstart', (event) =&gt; {\n  // prevent the window from scrolling\n  event.preventDefault();\n  setPickPosition(event.touches[0]);\n}, {passive: false});\n\nwindow.addEventListener('touchmove', (event) =&gt; {\n  setPickPosition(event.touches[0]);\n});\n\nwindow.addEventListener('touchend', clearPickPosition);\n</pre>\n<p>and with that picking should be working with <code class=\"notranslate\" translate=\"no\">OffscreenCanvas</code>.</p>\n<p></p><div translate=\"no\" class=\"threejs_example_container notranslate\">\n  <div></div>\n  <a class=\"threejs_center\" href=\"/manual/examples/offscreencanvas-w-picking.html\" target=\"_blank\">click here to open in a separate window</a>\n</div>\n\n<p></p>\n<p>Let's take it one more step and add in the <a href=\"/docs/#examples/controls/OrbitControls\"><code class=\"notranslate\" translate=\"no\">OrbitControls</code></a>.\nThis will be little more involved. The <a href=\"/docs/#examples/controls/OrbitControls\"><code class=\"notranslate\" translate=\"no\">OrbitControls</code></a> use\nthe DOM pretty extensively checking the mouse, touch events,\nand the keyboard.</p>\n<p>Unlike our code so far we can't really use a global <code class=\"notranslate\" translate=\"no\">state</code> object\nwithout re-writing all the OrbitControls code to work with it.\nThe OrbitControls take an <code class=\"notranslate\" translate=\"no\">HTMLElement</code> to which they attach most\nof the DOM events they use. Maybe we could pass in our own\nobject that has the same API surface as a DOM element.\nWe only need to support the features the OrbitControls need.</p>\n<p>Digging through the <a href=\"https://github.com/mrdoob/three.js/blob/master/examples/jsm/controls/OrbitControls.js\">OrbitControls source code</a>\nit looks like we need to handle the following events.</p>\n<ul>\n<li>contextmenu</li>\n<li>pointerdown</li>\n<li>pointermove</li>\n<li>pointerup</li>\n<li>touchstart</li>\n<li>touchmove</li>\n<li>touchend</li>\n<li>wheel</li>\n<li>keydown</li>\n</ul>\n<p>For the pointer events we need the <code class=\"notranslate\" translate=\"no\">ctrlKey</code>, <code class=\"notranslate\" translate=\"no\">metaKey</code>, <code class=\"notranslate\" translate=\"no\">shiftKey</code>,\n<code class=\"notranslate\" translate=\"no\">button</code>, <code class=\"notranslate\" translate=\"no\">pointerType</code>, <code class=\"notranslate\" translate=\"no\">clientX</code>, <code class=\"notranslate\" translate=\"no\">clientY</code>, <code class=\"notranslate\" translate=\"no\">pageX</code>, and <code class=\"notranslate\" translate=\"no\">pageY</code>, properties.</p>\n<p>For the keydown events we need the <code class=\"notranslate\" translate=\"no\">ctrlKey</code>, <code class=\"notranslate\" translate=\"no\">metaKey</code>, <code class=\"notranslate\" translate=\"no\">shiftKey</code>,\nand <code class=\"notranslate\" translate=\"no\">keyCode</code> properties.</p>\n<p>For the wheel event we only need the <code class=\"notranslate\" translate=\"no\">deltaY</code> property.</p>\n<p>And for the touch events we only need <code class=\"notranslate\" translate=\"no\">pageX</code> and <code class=\"notranslate\" translate=\"no\">pageY</code> from\nthe <code class=\"notranslate\" translate=\"no\">touches</code> property.</p>\n<p>So, let's make a proxy object pair. One part will run in the main page,\nget all those events, and pass on the relevant property values\nto the worker. The other part will run in the worker, receive those\nevents and pass them on using events that have the same structure\nas the original DOM events so the OrbitControls won't be able to\ntell the difference.</p>\n<p>Here's the code for the worker part.</p>\n<pre class=\"prettyprint showlinemods notranslate lang-js\" translate=\"no\">import {EventDispatcher} from 'three';\n\nclass ElementProxyReceiver extends EventDispatcher {\n  constructor() {\n    super();\n  }\n  handleEvent(data) {\n    this.dispatchEvent(data);\n  }\n}\n</pre>\n<p>All it does is if it receives a message it dispatches it.\nIt inherits from <a href=\"/docs/#api/en/core/EventDispatcher\"><code class=\"notranslate\" translate=\"no\">EventDispatcher</code></a> which provides methods like\n<code class=\"notranslate\" translate=\"no\">addEventListener</code> and <code class=\"notranslate\" translate=\"no\">removeEventListener</code> just like a DOM\nelement so if we pass it to the OrbitControls it should work.</p>\n<p><code class=\"notranslate\" translate=\"no\">ElementProxyReceiver</code> handles 1 element. In our case we only need\none but it's best to think head so lets make a manager to manage\nmore than one of them.</p>\n<pre class=\"prettyprint showlinemods notranslate lang-js\" translate=\"no\">class ProxyManager {\n  constructor() {\n    this.targets = {};\n    this.handleEvent = this.handleEvent.bind(this);\n  }\n  makeProxy(data) {\n    const {id} = data;\n    const proxy = new ElementProxyReceiver();\n    this.targets[id] = proxy;\n  }\n  getProxy(id) {\n    return this.targets[id];\n  }\n  handleEvent(data) {\n    this.targets[data.id].handleEvent(data.data);\n  }\n}\n</pre>\n<p>We can make a instance of <code class=\"notranslate\" translate=\"no\">ProxyManager</code> and call its <code class=\"notranslate\" translate=\"no\">makeProxy</code>\nmethod with an id which will make an <code class=\"notranslate\" translate=\"no\">ElementProxyReceiver</code> that\nresponds to messages with that id.</p>\n<p>Let's hook it up to our worker's message handler.</p>\n<pre class=\"prettyprint showlinemods notranslate lang-js\" translate=\"no\">const proxyManager = new ProxyManager();\n\nfunction start(data) {\n  const proxy = proxyManager.getProxy(data.canvasId);\n  init({\n    canvas: data.canvas,\n    inputElement: proxy,\n  });\n}\n\nfunction makeProxy(data) {\n  proxyManager.makeProxy(data);\n}\n\n...\n\nconst handlers = {\n-  init,\n-  mouse,\n+  start,\n+  makeProxy,\n+  event: proxyManager.handleEvent,\n   size,\n};\n\nself.onmessage = function(e) {\n  const fn = handlers[e.data.type];\n  if (typeof fn !== 'function') {\n    throw new Error('no handler for type: ' + e.data.type);\n  }\n  fn(e.data);\n};\n</pre>\n<p>In our shared three.js code we need to import the <a href=\"/docs/#examples/controls/OrbitControls\"><code class=\"notranslate\" translate=\"no\">OrbitControls</code></a> and set them up.</p>\n<pre class=\"prettyprint showlinemods notranslate lang-js\" translate=\"no\">import * as THREE from 'three';\n+import {OrbitControls} from 'three/addons/controls/OrbitControls.js';\n\nexport function init(data) {\n-  const {canvas} = data;\n+  const {canvas, inputElement} = data;\n  const renderer = new THREE.WebGLRenderer({antialias: true, canvas});\n\n+  const controls = new OrbitControls(camera, inputElement);\n+  controls.target.set(0, 0, 0);\n+  controls.update();\n</pre>\n<p>Notice we're passing the OrbitControls our proxy via <code class=\"notranslate\" translate=\"no\">inputElement</code>\ninstead of passing in the canvas like we do in other non-OffscreenCanvas\nexamples.</p>\n<p>Next we can move all the picking event code from the HTML file\nto the shared three.js code as well while changing\n<code class=\"notranslate\" translate=\"no\">canvas</code> to <code class=\"notranslate\" translate=\"no\">inputElement</code>.</p>\n<pre class=\"prettyprint showlinemods notranslate lang-js\" translate=\"no\">function getCanvasRelativePosition(event) {\n-  const rect = canvas.getBoundingClientRect();\n+  const rect = inputElement.getBoundingClientRect();\n  return {\n    x: event.clientX - rect.left,\n    y: event.clientY - rect.top,\n  };\n}\n\nfunction setPickPosition(event) {\n  const pos = getCanvasRelativePosition(event);\n-  sendMouse(\n-      (pos.x / canvas.clientWidth ) *  2 - 1,\n-      (pos.y / canvas.clientHeight) * -2 + 1);  // note we flip Y\n+  pickPosition.x = (pos.x / inputElement.clientWidth ) *  2 - 1;\n+  pickPosition.y = (pos.y / inputElement.clientHeight) * -2 + 1;  // note we flip Y\n}\n\nfunction clearPickPosition() {\n  // unlike the mouse which always has a position\n  // if the user stops touching the screen we want\n  // to stop picking. For now we just pick a value\n  // unlikely to pick something\n-  sendMouse(-100000, -100000);\n+  pickPosition.x = -100000;\n+  pickPosition.y = -100000;\n}\n\n*inputElement.addEventListener('mousemove', setPickPosition);\n*inputElement.addEventListener('mouseout', clearPickPosition);\n*inputElement.addEventListener('mouseleave', clearPickPosition);\n\n*inputElement.addEventListener('touchstart', (event) =&gt; {\n  // prevent the window from scrolling\n  event.preventDefault();\n  setPickPosition(event.touches[0]);\n}, {passive: false});\n\n*inputElement.addEventListener('touchmove', (event) =&gt; {\n  setPickPosition(event.touches[0]);\n});\n\n*inputElement.addEventListener('touchend', clearPickPosition);\n</pre>\n<p>Back in the main page we need code to send messages for\nall the events we enumerated above.</p>\n<pre class=\"prettyprint showlinemods notranslate lang-js\" translate=\"no\">let nextProxyId = 0;\nclass ElementProxy {\n  constructor(element, worker, eventHandlers) {\n    this.id = nextProxyId++;\n    this.worker = worker;\n    const sendEvent = (data) =&gt; {\n      this.worker.postMessage({\n        type: 'event',\n        id: this.id,\n        data,\n      });\n    };\n\n    // register an id\n    worker.postMessage({\n      type: 'makeProxy',\n      id: this.id,\n    });\n    for (const [eventName, handler] of Object.entries(eventHandlers)) {\n      element.addEventListener(eventName, function(event) {\n        handler(event, sendEvent);\n      });\n    }\n  }\n}\n</pre>\n<p><code class=\"notranslate\" translate=\"no\">ElementProxy</code> takes the element who's events we want to proxy. It\nthen registers an id with the worker by picking one and sending it\nvia the <code class=\"notranslate\" translate=\"no\">makeProxy</code> message we setup earlier. The worker will make\nan <code class=\"notranslate\" translate=\"no\">ElementProxyReceiver</code> and register it to that id.</p>\n<p>We then have an object of event handlers to register. This way\nwe can pass handlers only for these events we want to forward to\nthe worker.</p>\n<p>When we start the worker we first make a proxy and pass in our event handlers.</p>\n<pre class=\"prettyprint showlinemods notranslate lang-js\" translate=\"no\">function startWorker(canvas) {\n  const offscreen = canvas.transferControlToOffscreen();\n  const worker = new Worker('offscreencanvas-worker-orbitcontrols.js', {type: 'module'});\n\n+  const eventHandlers = {\n+    contextmenu: preventDefaultHandler,\n+    mousedown: mouseEventHandler,\n+    mousemove: mouseEventHandler,\n+    mouseup: mouseEventHandler,\n+    pointerdown: mouseEventHandler,\n+    pointermove: mouseEventHandler,\n+    pointerup: mouseEventHandler,\n+    touchstart: touchEventHandler,\n+    touchmove: touchEventHandler,\n+    touchend: touchEventHandler,\n+    wheel: wheelEventHandler,\n+    keydown: filteredKeydownEventHandler,\n+  };\n+  const proxy = new ElementProxy(canvas, worker, eventHandlers);\n  worker.postMessage({\n    type: 'start',\n    canvas: offscreen,\n+    canvasId: proxy.id,\n  }, [offscreen]);\n  console.log('using OffscreenCanvas');  /* eslint-disable-line no-console */\n}\n</pre>\n<p>And here are the event handlers. All they do is copy a list of properties\nfrom the event they receive. They are passed a <code class=\"notranslate\" translate=\"no\">sendEvent</code> function to which they pass the data\nthey make. That function will add the correct id and send it to the worker.</p>\n<pre class=\"prettyprint showlinemods notranslate lang-js\" translate=\"no\">const mouseEventHandler = makeSendPropertiesHandler([\n  'ctrlKey',\n  'metaKey',\n  'shiftKey',\n  'button',\n  'pointerType',\n  'clientX',\n  'clientY',\n  'pointerId',\n  'pageX',\n  'pageY',\n]);\nconst wheelEventHandlerImpl = makeSendPropertiesHandler([\n  'deltaX',\n  'deltaY',\n]);\nconst keydownEventHandler = makeSendPropertiesHandler([\n  'ctrlKey',\n  'metaKey',\n  'shiftKey',\n  'keyCode',\n]);\n\nfunction wheelEventHandler(event, sendFn) {\n  event.preventDefault();\n  wheelEventHandlerImpl(event, sendFn);\n}\n\nfunction preventDefaultHandler(event) {\n  event.preventDefault();\n}\n\nfunction copyProperties(src, properties, dst) {\n  for (const name of properties) {\n      dst[name] = src[name];\n  }\n}\n\nfunction makeSendPropertiesHandler(properties) {\n  return function sendProperties(event, sendFn) {\n    const data = {type: event.type};\n    copyProperties(event, properties, data);\n    sendFn(data);\n  };\n}\n\nfunction touchEventHandler(event, sendFn) {\n  // preventDefault() fixes mousemove, mouseup and mousedown \n  // firing when doing a simple touchup touchdown\n  // Happens only at offscreen canvas\n  event.preventDefault(); \n  const touches = [];\n  const data = {type: event.type, touches};\n  for (let i = 0; i &lt; event.touches.length; ++i) {\n    const touch = event.touches[i];\n    touches.push({\n      pageX: touch.pageX,\n      pageY: touch.pageY,\n      clientX: touch.clientX,\n      clientY: touch.clientY,\n    });\n  }\n  sendFn(data);\n}\n\n// The four arrow keys\nconst orbitKeys = {\n  '37': true,  // left\n  '38': true,  // up\n  '39': true,  // right\n  '40': true,  // down\n};\nfunction filteredKeydownEventHandler(event, sendFn) {\n  const {keyCode} = event;\n  if (orbitKeys[keyCode]) {\n    event.preventDefault();\n    keydownEventHandler(event, sendFn);\n  }\n}\n</pre>\n<p>This seems close to running but if we actually try it we'll see\nthat the <a href=\"/docs/#examples/controls/OrbitControls\"><code class=\"notranslate\" translate=\"no\">OrbitControls</code></a> need a few more things.</p>\n<p>One is they call <code class=\"notranslate\" translate=\"no\">element.focus</code>. We don't need that to happen\nin the worker so let's just add a stub.</p>\n<pre class=\"prettyprint showlinemods notranslate lang-js\" translate=\"no\">class ElementProxyReceiver extends THREE.EventDispatcher {\n  constructor() {\n    super();\n  }\n  handleEvent(data) {\n    this.dispatchEvent(data);\n  }\n+  focus() {\n+    // no-op\n+  }\n}\n</pre>\n<p>Another is they call <code class=\"notranslate\" translate=\"no\">event.preventDefault</code> and <code class=\"notranslate\" translate=\"no\">event.stopPropagation</code>.\nWe're already handling that in the main page so those can also be a noop.</p>\n<pre class=\"prettyprint showlinemods notranslate lang-js\" translate=\"no\">+function noop() {\n+}\n\nclass ElementProxyReceiver extends THREE.EventDispatcher {\n  constructor() {\n    super();\n  }\n  handleEvent(data) {\n+    data.preventDefault = noop;\n+    data.stopPropagation = noop;\n    this.dispatchEvent(data);\n  }\n  focus() {\n    // no-op\n  }\n}\n</pre>\n<p>Another is they look at <code class=\"notranslate\" translate=\"no\">clientWidth</code> and <code class=\"notranslate\" translate=\"no\">clientHeight</code>. We\nwere passing the size before but we can update the proxy pair\nto pass that as well.</p>\n<p>In the worker...</p>\n<pre class=\"prettyprint showlinemods notranslate lang-js\" translate=\"no\">class ElementProxyReceiver extends THREE.EventDispatcher {\n  constructor() {\n    super();\n  }\n+  get clientWidth() {\n+    return this.width;\n+  }\n+  get clientHeight() {\n+    return this.height;\n+  }\n+  getBoundingClientRect() {\n+    return {\n+      left: this.left,\n+      top: this.top,\n+      width: this.width,\n+      height: this.height,\n+      right: this.left + this.width,\n+      bottom: this.top + this.height,\n+    };\n+  }\n  handleEvent(data) {\n+    if (data.type === 'size') {\n+      this.left = data.left;\n+      this.top = data.top;\n+      this.width = data.width;\n+      this.height = data.height;\n+      return;\n+    }\n    data.preventDefault = noop;\n    data.stopPropagation = noop;\n    this.dispatchEvent(data);\n  }\n  focus() {\n    // no-op\n  }\n}\n</pre>\n<p>back in the main page we need to send the size and the left and top positions as well.\nNote that as is we don't handle if the canvas moves, only if it resizes. If you wanted\nto handle moving you'd need to call <code class=\"notranslate\" translate=\"no\">sendSize</code> anytime something moved the canvas.</p>\n<pre class=\"prettyprint showlinemods notranslate lang-js\" translate=\"no\">class ElementProxy {\n  constructor(element, worker, eventHandlers) {\n    this.id = nextProxyId++;\n    this.worker = worker;\n    const sendEvent = (data) =&gt; {\n      this.worker.postMessage({\n        type: 'event',\n        id: this.id,\n        data,\n      });\n    };\n\n    // register an id\n    worker.postMessage({\n      type: 'makeProxy',\n      id: this.id,\n    });\n+    sendSize();\n    for (const [eventName, handler] of Object.entries(eventHandlers)) {\n      element.addEventListener(eventName, function(event) {\n        handler(event, sendEvent);\n      });\n    }\n\n+    function sendSize() {\n+      const rect = element.getBoundingClientRect();\n+      sendEvent({\n+        type: 'size',\n+        left: rect.left,\n+        top: rect.top,\n+        width: element.clientWidth,\n+        height: element.clientHeight,\n+      });\n+    }\n+\n+    window.addEventListener('resize', sendSize);\n  }\n}\n</pre>\n<p>and in our shared three.js code we no longer need <code class=\"notranslate\" translate=\"no\">state</code></p>\n<pre class=\"prettyprint showlinemods notranslate lang-js\" translate=\"no\">-export const state = {\n-  width: 300,   // canvas default\n-  height: 150,  // canvas default\n-};\n\n...\n\nfunction resizeRendererToDisplaySize(renderer) {\n  const canvas = renderer.domElement;\n-  const width = state.width;\n-  const height = state.height;\n+  const width = inputElement.clientWidth;\n+  const height = inputElement.clientHeight;\n  const needResize = canvas.width !== width || canvas.height !== height;\n  if (needResize) {\n    renderer.setSize(width, height, false);\n  }\n  return needResize;\n}\n\nfunction render(time) {\n  time *= 0.001;\n\n  if (resizeRendererToDisplaySize(renderer)) {\n-    camera.aspect = state.width / state.height;\n+    camera.aspect = inputElement.clientWidth / inputElement.clientHeight;\n    camera.updateProjectionMatrix();\n  }\n\n  ...\n</pre>\n<p>A few more hacks. The OrbitControls add <code class=\"notranslate\" translate=\"no\">pointermove</code> and <code class=\"notranslate\" translate=\"no\">pointerup</code> events to the\n<code class=\"notranslate\" translate=\"no\">ownerDocument</code> of the element to handle mouse capture (when the mouse goes\noutside the window).</p>\n<p>Further the code references the global <code class=\"notranslate\" translate=\"no\">document</code> but there is no global document\nin a worker. </p>\n<p>We can solve all of these with a 2 quick hacks. In our worker\ncode we'll re-use our proxy for both problems.</p>\n<pre class=\"prettyprint showlinemods notranslate lang-js\" translate=\"no\">function start(data) {\n  const proxy = proxyManager.getProxy(data.canvasId);\n+  proxy.ownerDocument = proxy; // HACK!\n+  self.document = {} // HACK!\n  init({\n    canvas: data.canvas,\n    inputElement: proxy,\n  });\n}\n</pre>\n<p>This will give the <a href=\"/docs/#examples/controls/OrbitControls\"><code class=\"notranslate\" translate=\"no\">OrbitControls</code></a> something to inspect which\nmatches their expectations.</p>\n<p>I know that was kind of hard to follow. The short version is:\n<code class=\"notranslate\" translate=\"no\">ElementProxy</code> runs on the main page and forwards DOM events\nto <code class=\"notranslate\" translate=\"no\">ElementProxyReceiver</code> in the worker which\nmasquerades as an <code class=\"notranslate\" translate=\"no\">HTMLElement</code> that we can use both with the\n<a href=\"/docs/#examples/controls/OrbitControls\"><code class=\"notranslate\" translate=\"no\">OrbitControls</code></a> and with our own code.</p>\n<p>The final thing is our fallback when we are not using OffscreenCanvas.\nAll we have to do is pass the canvas itself as our <code class=\"notranslate\" translate=\"no\">inputElement</code>.</p>\n<pre class=\"prettyprint showlinemods notranslate lang-js\" translate=\"no\">function startMainPage(canvas) {\n-  init({canvas});\n+  init({canvas, inputElement: canvas});\n  console.log('using regular canvas');\n}\n</pre>\n<p>and now we should have OrbitControls working with OffscreenCanvas</p>\n<p></p><div translate=\"no\" class=\"threejs_example_container notranslate\">\n  <div></div>\n  <a class=\"threejs_center\" href=\"/manual/examples/offscreencanvas-w-orbitcontrols.html\" target=\"_blank\">click here to open in a separate window</a>\n</div>\n\n<p></p>\n<p>This is probably the most complicated example on this site. It's a\nlittle hard to follow because there are 3 files involved for each\nsample. The HTML file, the worker file, the shared three.js code.</p>\n<p>I hope it wasn't too difficult to understand and that it provided some\nuseful examples of working with three.js, OffscreenCanvas and web workers.</p>\n\n        </div>\n      </div>\n    </div>",
    "success": true,
    "error": "",
    "crawled_at": 1786376809.300204,
    "is_shell": false
  },
  {
    "url": "https://threejs.org/manual/en/load-obj.html",
    "title": "Loading a .OBJ File",
    "content": "<div class=\"container\">\n      <div class=\"lesson-title\">\n        <h1>Loading a .OBJ File</h1>\n      </div>\n      <div class=\"lesson\">\n        <div class=\"lesson-main\">\n          <p>One of the most common things people want to do with three.js\nis to load and display 3D models. A common format is the .OBJ\n3D format so let's try loading one.</p>\n<p>Searching the net I found <a href=\"https://www.blendswap.com/blends/view/69174\">this CC-BY-NC 3.0 windmill 3D model</a> by <a href=\"https://www.blendswap.com/user/ahedov\">ahedov</a>.</p>\n<div class=\"threejs_center\"><img src=\"../resources/images/windmill-obj.jpg\"></div>\n\n<p>I downloaded the .blend file from that site, loaded it into <a href=\"https://blender.org\">Blender</a>\nand exported it as an .OBJ file.</p>\n<div class=\"threejs_center\"><img style=\"width: 827px;\" src=\"../resources/images/windmill-export-as-obj.jpg\"></div>\n\n<blockquote>\n<p>Note: If you've never used Blender you might be in for a surprise\nin that Blender does things differently than just about every\nother program you've ever used. Just be aware you might need to\nset aside some time to read some basic UI navigation for Blender.</p>\n<p>Let me also add that 3D programs in general are giant beasts with\n1000s of features. They are some of the most complicated software there\nis. When I first learned 3D Studio Max in 1996 I read through 70% of the\n600 page manual spending a few hours a day for around 3 weeks. That paid\noff in that when I learned Maya a few years later some of the lessons\nlearned before were applicable to Maya. So, just be aware that if you\nreally want to be able to use 3D software to either build 3D assets\nor to modify existing ones put it on your schedule and clear sometime\nto really go through some lessons.</p>\n</blockquote>\n<p>In any case I used these export options</p>\n<div class=\"threejs_center\"><img style=\"width: 239px;\" src=\"../resources/images/windmill-export-options.jpg\"></div>\n\n<p>Let's try to display it!</p>\n<p>I started with the directional lighting example from\n<a href=\"lights.html\">the lights article</a> and I combined it with\nthe hemispherical lighting example so I ended up with one\n<a href=\"/docs/#api/en/lights/HemisphereLight\"><code class=\"notranslate\" translate=\"no\">HemisphereLight</code></a> and one <a href=\"/docs/#api/en/lights/DirectionalLight\"><code class=\"notranslate\" translate=\"no\">DirectionalLight</code></a>. I also removed all the GUI stuff\nrelated to adjusting the lights. I also removed the cube and sphere\nthat were being added to the scene.</p>\n<p>From that the first thing we need to do is include the <a href=\"/docs/#examples/loaders/OBJLoader\"><code class=\"notranslate\" translate=\"no\">OBJLoader</code></a> loader in our script.</p>\n<pre class=\"prettyprint showlinemods notranslate lang-js\" translate=\"no\">import {OBJLoader} from 'three/addons/loaders/OBJLoader.js';\n</pre>\n<p>Then to load the .OBJ file we create an instance of <a href=\"/docs/#examples/loaders/OBJLoader\"><code class=\"notranslate\" translate=\"no\">OBJLoader</code></a>,\npass it the URL of our .OBJ file, and pass in a callback that adds\nthe loaded model to our scene.</p>\n<pre class=\"prettyprint showlinemods notranslate lang-js\" translate=\"no\">{\n  const objLoader = new OBJLoader();\n  objLoader.load('resources/models/windmill/windmill.obj', (root) =&gt; {\n    scene.add(root);\n  });\n}\n</pre>\n<p>If we run that what happens?</p>\n<p></p><div translate=\"no\" class=\"threejs_example_container notranslate\">\n  <div></div>\n  <a class=\"threejs_center\" href=\"/manual/examples/load-obj-no-materials.html\" target=\"_blank\">click here to open in a separate window</a>\n</div>\n\n<p></p>\n<p>Well it's close but we're getting errors about materials since we haven't\ngiven the scene any materials and .OBJ files don't have material\nparameters. </p>\n<p>The .OBJ loader can be passed an\nobject of name / material pairs. When it loads the .OBJ file,\nany material name it finds it will look for the corresponding material\nin the map of materials set on the loader. If it finds a\nmaterial that matches by name it will use that material. If\nnot it will use the loader's default material.</p>\n<p>Sometimes .OBJ files come with a .MTL file that defines\nmaterials. In our case the exporter also created a .MTL file.\n.MTL format is plain ASCII so it's easy to look at. Looking at it here</p>\n<pre class=\"prettyprint showlinemods notranslate lang-mtl\" translate=\"no\"># Blender MTL File: 'windmill_001.blend'\n# Material Count: 2\n\nnewmtl Material\nNs 0.000000\nKa 1.000000 1.000000 1.000000\nKd 0.800000 0.800000 0.800000\nKs 0.000000 0.000000 0.000000\nKe 0.000000 0.000000 0.000000\nNi 1.000000\nd 1.000000\nillum 1\nmap_Kd windmill_001_lopatky_COL.jpg\nmap_Bump windmill_001_lopatky_NOR.jpg\n\nnewmtl windmill\nNs 0.000000\nKa 1.000000 1.000000 1.000000\nKd 0.800000 0.800000 0.800000\nKs 0.000000 0.000000 0.000000\nKe 0.000000 0.000000 0.000000\nNi 1.000000\nd 1.000000\nillum 1\nmap_Kd windmill_001_base_COL.jpg\nmap_Bump windmill_001_base_NOR.jpg\nmap_Ns windmill_001_base_SPEC.jpg\n</pre>\n<p>We can see there are 2 materials referencing 5 jpg textures\nbut where are the texture files?</p>\n<div class=\"threejs_center\"><img style=\"width: 757px;\" src=\"../resources/images/windmill-exported-files.png\"></div>\n\n<p>All we got was an .OBJ file and an .MTL file.</p>\n<p>At least for this model it turns out the textures are embedded\nin the .blend file we downloaded. We can ask blender to\nexport those files to by picking <strong>File-&gt;External Data-&gt;Unpack All Into Files</strong></p>\n<div class=\"threejs_center\"><img style=\"width: 828px;\" src=\"../resources/images/windmill-export-textures.jpg\"></div>\n\n<p>and then choosing <strong>Write Files to Current Directory</strong></p>\n<div class=\"threejs_center\"><img style=\"width: 828px;\" src=\"../resources/images/windmill-overwrite.jpg\"></div>\n\n<p>This ends up writing the files in the same folder as the .blend file\nin a sub folder called <strong>textures</strong>.</p>\n<div class=\"threejs_center\"><img style=\"width: 758px;\" src=\"../resources/images/windmill-exported-texture-files.png\"></div>\n\n<p>I copied those textures into the same folder I exported the .OBJ\nfile to.</p>\n<div class=\"threejs_center\"><img style=\"width: 757px;\" src=\"../resources/images/windmill-exported-files-with-textures.png\"></div>\n\n<p>Now that we have the textures available we can load the .MTL file.</p>\n<p>First we need to include the <a href=\"/docs/#examples/loaders/MTLLoader\"><code class=\"notranslate\" translate=\"no\">MTLLoader</code></a>;</p>\n<pre class=\"prettyprint showlinemods notranslate lang-js\" translate=\"no\">import * as THREE from 'three';\nimport {OrbitControls} from 'three/addons/controls/OrbitControls.js';\nimport {OBJLoader} from 'three/addons/loaders/OBJLoader.js';\n+import {MTLLoader} from 'three/addons/loaders/MTLLoader.js';\n</pre>\n<p>Then we first load the .MTL file. When it's finished loading we add\nthe just loaded materials on to the <a href=\"/docs/#examples/loaders/OBJLoader\"><code class=\"notranslate\" translate=\"no\">OBJLoader</code></a> itself via the <code class=\"notranslate\" translate=\"no\">setMaterials</code>\nand then load the .OBJ file.</p>\n<pre class=\"prettyprint showlinemods notranslate lang-js\" translate=\"no\">{\n+  const mtlLoader = new MTLLoader();\n+  mtlLoader.load('resources/models/windmill/windmill.mtl', (mtl) =&gt; {\n+    mtl.preload();\n+    objLoader.setMaterials(mtl);\n    objLoader.load('resources/models/windmill/windmill.obj', (root) =&gt; {\n      scene.add(root);\n    });\n+  });\n}\n</pre>\n<p>And if we try that...</p>\n<p></p><div translate=\"no\" class=\"threejs_example_container notranslate\">\n  <div></div>\n  <a class=\"threejs_center\" href=\"/manual/examples/load-obj-materials.html\" target=\"_blank\">click here to open in a separate window</a>\n</div>\n\n<p></p>\n<p>Note that if we spin the model around you'll see the windmill cloth\ndisappears</p>\n<div class=\"threejs_center\"><img style=\"width: 528px;\" src=\"../resources/images/windmill-missing-cloth.jpg\"></div>\n\n<p>We need the material on the blades to be double sided, something\nwe went over in <a href=\"materials.html\">the article on materials</a>.\nThere is no easy way to fix this in the .MTL file. Off the top of my\nhead I can think of 3 ways to fix this.</p>\n<ol>\n<li><p>Loop over all the materials after loading them and set them all to double sided.</p>\n<pre class=\"prettyprint showlinemods notranslate notranslate\" translate=\"no\"> const mtlLoader = new MTLLoader();\n mtlLoader.load('resources/models/windmill/windmill.mtl', (mtl) =&gt; {\n   mtl.preload();\n   for (const material of Object.values(mtl.materials)) {\n     material.side = THREE.DoubleSide;\n   }\n   ...\n</pre><p>This solution works but ideally we only want materials that need\nto be double sided to be double sided because drawing double sided\nis slower than single sided.</p>\n</li>\n<li><p>Manually set a specific material</p>\n<p>Looking in the .MTL file there are 2 materials. One called <code class=\"notranslate\" translate=\"no\">\"windmill\"</code>\nand the other called <code class=\"notranslate\" translate=\"no\">\"Material\"</code>. Through trial and error I figured\nout the blades use the material called <code class=\"notranslate\" translate=\"no\">\"Material\"</code>so we could set\nthat one specifically </p>\n<pre class=\"prettyprint showlinemods notranslate notranslate\" translate=\"no\"> const mtlLoader = new MTLLoader();\n mtlLoader.load('resources/models/windmill/windmill.mtl', (mtl) =&gt; {\n   mtl.preload();\n   mtl.materials.Material.side = THREE.DoubleSide;\n   ...\n</pre></li>\n<li><p>Realizing that the .MTL file is limited we could just not use it\nand instead create materials ourselves.</p>\n<p>In this case we'd need to look up the <a href=\"/docs/#api/en/objects/Mesh\"><code class=\"notranslate\" translate=\"no\">Mesh</code></a> object after\nloading the obj file.</p>\n<pre class=\"prettyprint showlinemods notranslate notranslate\" translate=\"no\"> objLoader.load('resources/models/windmill/windmill.obj', (root) =&gt; {\n   const materials = {\n     Material: new THREE.MeshPhongMaterial({...}),\n     windmill: new THREE.MeshPhongMaterial({...}),\n   };\n   root.traverse(node =&gt; {\n     const material = materials[node.material?.name];\n     if (material) {\n       node.material = material;\n     }\n   })\n   scene.add(root);\n });\n</pre></li>\n</ol>\n<p>Which one you pick is up to you. 1 is easiest. 3 is most flexible.\n2 somewhere in between. For now I'll pick 2.</p>\n<p>And with that change you should still see the cloth on the blades\nwhen looking from behind but there's one more issue. If we zoom in close\nwe see things are turning blocky.</p>\n<div class=\"threejs_center\"><img style=\"width: 700px;\" src=\"../resources/images/windmill-blocky.jpg\"></div>\n\n<p>What's going on?</p>\n<p>Looking at the textures there are 2 textures labelled NOR for NORmal map.\nAnd looking at them they look like normal maps. Normal maps are generally\npurple where as bump maps are black and white. Normal maps represent\nthe direction of the surface where as bump maps represent the height of\nthe surface.</p>\n<div class=\"threejs_center\"><img style=\"width: 256px;\" src=\"../examples/resources/models/windmill/windmill_001_base_NOR.jpg\"></div>\n\n<p>Looking at <a href=\"https://github.com/mrdoob/three.js/blob/1a560a3426e24bbfc9ca1f5fb0dfb4c727d59046/examples/js/loaders/MTLLoader.js#L432\">the source for the MTLLoader</a>\nit expects the keyword <code class=\"notranslate\" translate=\"no\">norm</code> for normal maps so let's edit the .MTL file</p>\n<pre class=\"prettyprint showlinemods notranslate lang-mtl\" translate=\"no\"># Blender MTL File: 'windmill_001.blend'\n# Material Count: 2\n\nnewmtl Material\nNs 0.000000\nKa 1.000000 1.000000 1.000000\nKd 0.800000 0.800000 0.800000\nKs 0.000000 0.000000 0.000000\nKe 0.000000 0.000000 0.000000\nNi 1.000000\nd 1.000000\nillum 1\nmap_Kd windmill_001_lopatky_COL.jpg\n-map_Bump windmill_001_lopatky_NOR.jpg\n+norm windmill_001_lopatky_NOR.jpg\n\nnewmtl windmill\nNs 0.000000\nKa 1.000000 1.000000 1.000000\nKd 0.800000 0.800000 0.800000\nKs 0.000000 0.000000 0.000000\nKe 0.000000 0.000000 0.000000\nNi 1.000000\nd 1.000000\nillum 1\nmap_Kd windmill_001_base_COL.jpg\n-map_Bump windmill_001_base_NOR.jpg\n+norm windmill_001_base_NOR.jpg\nmap_Ns windmill_001_base_SPEC.jpg\n</pre>\n<p>and now when we load it it will be using the normal maps as normal maps and\nwe can see the back of the blades.</p>\n<p></p><div translate=\"no\" class=\"threejs_example_container notranslate\">\n  <div></div>\n  <a class=\"threejs_center\" href=\"/manual/examples/load-obj-materials-fixed.html\" target=\"_blank\">click here to open in a separate window</a>\n</div>\n\n<p></p>\n<p>Let's load a different file.</p>\n<p>Searching the net I found this <a href=\"https://creativecommons.org/licenses/by-nc/4.0/\">CC-BY-NC</a> windmill 3D model made by <a href=\"http://www.gerzi.ch/\">Roger Gerzner / GERIZ.3D Art</a>.</p>\n<div class=\"threejs_center\"><img src=\"../resources/images/windmill-obj-2.jpg\"></div>\n\n<p>It had a .OBJ version already available. Let's load it up (note I removed the .MTL loader for now)</p>\n<pre class=\"prettyprint showlinemods notranslate lang-js\" translate=\"no\">-  objLoader.load('resources/models/windmill/windmill.obj', ...\n+  objLoader.load('resources/models/windmill-2/windmill.obj', ...\n</pre>\n<p></p><div translate=\"no\" class=\"threejs_example_container notranslate\">\n  <div></div>\n  <a class=\"threejs_center\" href=\"/manual/examples/load-obj-wat.html\" target=\"_blank\">click here to open in a separate window</a>\n</div>\n\n<p></p>\n<p>Hmmm, nothing appears. What's the problem? I wonder what size the model is?\nWe can ask THREE.js what size the model is and try to set our\ncamera automatically.</p>\n<p>First off we can ask THREE.js to compute a box that contains the scene\nwe just loaded and ask for its size and center</p>\n<pre class=\"prettyprint showlinemods notranslate lang-js\" translate=\"no\">objLoader.load('resources/models/windmill_2/windmill.obj', (root) =&gt; {\n  scene.add(root);\n\n+  const box = new THREE.Box3().setFromObject(root);\n+  const boxSize = box.getSize(new THREE.Vector3()).length();\n+  const boxCenter = box.getCenter(new THREE.Vector3());\n+  console.log(boxSize);\n+  console.log(boxCenter);\n</pre>\n<p>Looking in <a href=\"debugging-javascript.html\">the JavaScript console</a> I see</p>\n<pre class=\"prettyprint showlinemods notranslate lang-js\" translate=\"no\">size 2123.6499788469982\ncenter p {x: -0.00006103515625, y: 770.0909731090069, z: -3.313507080078125}\n</pre>\n<p>Our camera is currently only showing about 100 units with <code class=\"notranslate\" translate=\"no\">near</code> at 0.1 and <code class=\"notranslate\" translate=\"no\">far</code> at 100.\nOur ground plane is only 40 units across so basically this windmill model is so big, 2000 units,\nthat it's surrounding our camera and all parts of it our outside our frustum.</p>\n<div class=\"threejs_center\"><img style=\"width: 280px;\" src=\"../resources/images/camera-inside-windmill.svg\"></div>\n\n<p>We could manually fix that but we could also make the camera auto frame our scene.\nLet's try that. We can then use the box we just computed adjust the camera settings to\nview the entire scene. Note that there is no <em>right</em> answer\non where to put the camera. We could be facing the scene from any direction at any\naltitude so we'll just have to pick something.</p>\n<p>As we went over in <a href=\"cameras.html\">the article on cameras</a> the camera defines a frustum.\nThat frustum is defined by the field of view (<code class=\"notranslate\" translate=\"no\">fov</code>) and the <code class=\"notranslate\" translate=\"no\">near</code> and <code class=\"notranslate\" translate=\"no\">far</code> settings. We\nwant to know given whatever field of view the camera currently has, how far away does the camera\nneed to be so the box containing the scene fits inside the frustum assuming the frustum\nextended forever. In other words let's assume <code class=\"notranslate\" translate=\"no\">near</code> is 0.00000001 and <code class=\"notranslate\" translate=\"no\">far</code> is infinity.</p>\n<p>Since we know the size of the box and we know the field of view we have this triangle</p>\n<div class=\"threejs_center\"><img style=\"width: 600px;\" src=\"../resources/images/camera-fit-scene.svg\"></div>\n\n<p>You can see on the left is the camera and the blue frustum is projecting out in\nfront of it. We just computed the box that contains the windmill. We need to\ncompute how far way the camera should be from the box so that the box appears\ninside the frustum.</p>\n<p>Using basic <em>right triangle</em> trigonometry and <a href=\"https://www.google.com/search?q=SOHCAHTOA\">SOHCAHTOA</a>,\ngiven we know the field of view for the frustum and we know the size of the box we can compute the <em>distance</em>.</p>\n<div class=\"threejs_center\"><img style=\"width: 600px;\" src=\"../resources/images/field-of-view-camera.svg\"></div>\n\n<p>Based on that diagram the formula for computing distance is</p>\n<pre class=\"prettyprint showlinemods notranslate lang-js\" translate=\"no\">distance = halfSizeToFitOnScreen / tangent(halfFovY)\n</pre>\n<p>Let's translate that to code. First let's make a function that will compute <code class=\"notranslate\" translate=\"no\">distance</code> and then move the\ncamera that <code class=\"notranslate\" translate=\"no\">distance</code> units from the center of the box. We'll then point the\ncamera at the <code class=\"notranslate\" translate=\"no\">center</code> of the box.</p>\n<pre class=\"prettyprint showlinemods notranslate lang-js\" translate=\"no\">function frameArea(sizeToFitOnScreen, boxSize, boxCenter, camera) {\n  const halfSizeToFitOnScreen = sizeToFitOnScreen * 0.5;\n  const halfFovY = THREE.MathUtils.degToRad(camera.fov * .5);\n  const distance = halfSizeToFitOnScreen / Math.tan(halfFovY);\n\n  // compute a unit vector that points in the direction the camera is now\n  // from the center of the box\n  const direction = (new THREE.Vector3()).subVectors(camera.position, boxCenter).normalize();\n\n  // move the camera to a position distance units way from the center\n  // in whatever direction the camera was from the center already\n  camera.position.copy(direction.multiplyScalar(distance).add(boxCenter));\n\n  // pick some near and far values for the frustum that\n  // will contain the box.\n  camera.near = boxSize / 100;\n  camera.far = boxSize * 100;\n\n  camera.updateProjectionMatrix();\n\n  // point the camera to look at the center of the box\n  camera.lookAt(boxCenter.x, boxCenter.y, boxCenter.z);\n}\n</pre>\n<p>We pass in 2 sizes. The <code class=\"notranslate\" translate=\"no\">boxSize</code> and the <code class=\"notranslate\" translate=\"no\">sizeToFitOnScreen</code>. If we just passed in <code class=\"notranslate\" translate=\"no\">boxSize</code>\nand used that as <code class=\"notranslate\" translate=\"no\">sizeToFitOnScreen</code> then the math would make the box fit perfectly inside\nthe frustum. We want a little extra space above and below so we'll pass in a slightly\nlarger size. </p>\n<pre class=\"prettyprint showlinemods notranslate lang-js\" translate=\"no\">{\n  const objLoader = new OBJLoader();\n  objLoader.load('resources/models/windmill_2/windmill.obj', (root) =&gt; {\n    scene.add(root);\n+    // compute the box that contains all the stuff\n+    // from root and below\n+    const box = new THREE.Box3().setFromObject(root);\n+\n+    const boxSize = box.getSize(new THREE.Vector3()).length();\n+    const boxCenter = box.getCenter(new THREE.Vector3());\n+\n+    // set the camera to frame the box\n+    frameArea(boxSize * 1.2, boxSize, boxCenter, camera);\n+\n+    // update the Trackball controls to handle the new size\n+    controls.maxDistance = boxSize * 10;\n+    controls.target.copy(boxCenter);\n+    controls.update();\n  });\n}\n</pre>\n<p>You can see above we pass in <code class=\"notranslate\" translate=\"no\">boxSize * 1.2</code> to give us 20% more space above and below the box when trying\nto fit it inside the frustum. We also updated the <a href=\"/docs/#examples/controls/OrbitControls\"><code class=\"notranslate\" translate=\"no\">OrbitControls</code></a> so the camera will orbit the center\nof the scene.</p>\n<p>Now if we try that we get...</p>\n<p></p><div translate=\"no\" class=\"threejs_example_container notranslate\">\n  <div></div>\n  <a class=\"threejs_center\" href=\"/manual/examples/load-obj-auto-camera.html\" target=\"_blank\">click here to open in a separate window</a>\n</div>\n\n<p></p>\n<p>This almost works. Use the mouse to rotate the camera and you\nshould see the windmill. The problem is the windmill is large and the box's center is at about (0, 770, 0). So, when we move the camera from where it\nstarts (0, 10, 20) to <code class=\"notranslate\" translate=\"no\">distance</code> units way from the center in the direction the camera\nis relative to the center that's moving the camera almost straight down below\nthe windmill.</p>\n<div class=\"threejs_center\"><img style=\"width: 360px;\" src=\"../resources/images/computed-camera-position.svg\"></div>\n\n<p>Let's change it to move sideways from the center of the box to in whatever direction\nthe camera is from the center. All we need to do to do that is zero out the <code class=\"notranslate\" translate=\"no\">y</code> component\nof the vector from the box to the camera. Then, when we normalize that vector it will\nbecome a vector parallel to the XZ plane. In other words parallel to the ground.</p>\n<pre class=\"prettyprint showlinemods notranslate lang-js\" translate=\"no\">-// compute a unit vector that points in the direction the camera is now\n-// from the center of the box\n-const direction = (new THREE.Vector3()).subVectors(camera.position, boxCenter).normalize();\n+// compute a unit vector that points in the direction the camera is now\n+// in the xz plane from the center of the box\n+const direction = (new THREE.Vector3())\n+    .subVectors(camera.position, boxCenter)\n+    .multiply(new THREE.Vector3(1, 0, 1))\n+    .normalize();\n</pre>\n<p>If you look at the bottom of the windmill you'll see a small square. That is our ground\nplane. </p>\n<div class=\"threejs_center\"><img style=\"width: 365px;\" src=\"../resources/images/tiny-ground-plane.jpg\"></div>\n\n<p>It's only 40x40 units and so is way too small relative to the windmill.\nSince the windmill is over 2000 units big let's change the size of the ground plane to\nsomething more fitting. We also need to adjust the repeat otherwise our checkerboard\nwill be so fine we won't even be able to see it unless we zoom way way in.</p>\n<pre class=\"prettyprint showlinemods notranslate lang-js\" translate=\"no\">-const planeSize = 40;\n+const planeSize = 4000;\n\nconst loader = new THREE.TextureLoader();\nconst texture = loader.load('resources/images/checker.png');\ntexture.wrapS = THREE.RepeatWrapping;\ntexture.wrapT = THREE.RepeatWrapping;\ntexture.magFilter = THREE.NearestFilter;\n-const repeats = planeSize / 2;\n+const repeats = planeSize / 200;\ntexture.repeat.set(repeats, repeats);\n</pre>\n<p>and now we can see this windmill</p>\n<p></p><div translate=\"no\" class=\"threejs_example_container notranslate\">\n  <div></div>\n  <a class=\"threejs_center\" href=\"/manual/examples/load-obj-auto-camera-xz.html\" target=\"_blank\">click here to open in a separate window</a>\n</div>\n\n<p></p>\n<p>Let's add the materials back. Like before there is a .MTL file that references\nsome textures but looking at the files I quickly see an issue.</p>\n<pre class=\"prettyprint showlinemods notranslate lang-shell\" translate=\"no\"> $ ls -l windmill\n -rw-r--r--@ 1 gregg  staff       299 May 20  2009 windmill.mtl\n -rw-r--r--@ 1 gregg  staff    142989 May 20  2009 windmill.obj\n -rw-r--r--@ 1 gregg  staff  12582956 Apr 19  2009 windmill_diffuse.tga\n -rw-r--r--@ 1 gregg  staff  12582956 Apr 20  2009 windmill_normal.tga\n -rw-r--r--@ 1 gregg  staff  12582956 Apr 19  2009 windmill_spec.tga\n</pre>\n<p>There are TARGA (.tga) files and they are giant!</p>\n<p>THREE.js actually has a TGA loader but it's arguably wrong to use it for most use cases.\nIf you're making a viewer where you want to allow users to view random 3D files they\nfind on the net then maybe, just maybe, you might want to load TGA files. (<a href=\"#loading-scenes\">*</a>)</p>\n<p>One problem with TGA files are they can't be compressed well at all. TGA only supports very\nsimple compression and looking above we can see the files are not compressed at all\nas the odds of them being all exactly the same size are extremely low. Further they\nare 12 megabytes each!!! If we used those files the user would have to download 36meg\nto see the windmill.</p>\n<p>Another issue with TGA is the browser itself has no support for them so loading them\nis likely going to be slower than loading supported formats like .JPG and .PNG</p>\n<p>I'm pretty sure for our purposes converting them to .JPG will be the best option.\nLooking inside I see they are 3 channels each, RGB, there is no alpha channel. JPG\nonly supports 3 channels so that's a good fit. JPG also supports lossy compression\nso we can make the files much smaller to download</p>\n<p>Loading the files up they were each 2048x2048. That seemed like a waste to me but of\ncourse it depends on your use case. I made them each 1024x1024 and saved them at a\n50% quality setting in Photoshop. Getting a file listing</p>\n<pre class=\"prettyprint showlinemods notranslate lang-shell\" translate=\"no\"> $ ls -l ../threejs.org/manual/examples/resources/models/windmill\n -rw-r--r--@ 1 gregg  staff     299 May 20  2009 windmill.mtl\n -rw-r--r--@ 1 gregg  staff  142989 May 20  2009 windmill.obj\n -rw-r--r--@ 1 gregg  staff  259927 Nov  7 18:37 windmill_diffuse.jpg\n -rw-r--r--@ 1 gregg  staff   98013 Nov  7 18:38 windmill_normal.jpg\n -rw-r--r--@ 1 gregg  staff  191864 Nov  7 18:39 windmill_spec.jpg\n</pre>\n<p>We went from 36meg to 0.55meg! Of course the artist might not be pleased\nwith this compression so be sure to consult with them to discuss the tradeoffs.</p>\n<p>Now, to use the .MTL file we need to edit it to reference the .JPG files\ninstead of the .TGA files. Fortunately it's a simple text file so it's easy to edit</p>\n<pre class=\"prettyprint showlinemods notranslate lang-mtl\" translate=\"no\">newmtl blinn1SG\nKa 0.10 0.10 0.10\n\nKd 0.00 0.00 0.00\nKs 0.00 0.00 0.00\nKe 0.00 0.00 0.00\nNs 0.060000\nNi 1.500000\nd 1.000000\nTr 0.000000\nTf 1.000000 1.000000 1.000000\nillum 2\n-map_Kd windmill_diffuse.tga\n+map_Kd windmill_diffuse.jpg\n\n-map_Ks windmill_spec.tga\n+map_Ks windmill_spec.jpg\n\n-map_bump windmill_normal.tga\n-bump windmill_normal.tga\n+map_bump windmill_normal.jpg\n+bump windmill_normal.jpg\n</pre>\n<p>Now that the .MTL file points to some reasonable size textures we need to load it so we'll just do like we did above, first load the materials\nand then set them on the <a href=\"/docs/#examples/loaders/OBJLoader\"><code class=\"notranslate\" translate=\"no\">OBJLoader</code></a></p>\n<pre class=\"prettyprint showlinemods notranslate lang-js\" translate=\"no\">{\n+  const mtlLoader = new MTLLoader();\n+  mtlLoader.load('resources/models/windmill_2/windmill-fixed.mtl', (mtl) =&gt; {\n+    mtl.preload();\n+    const objLoader = new OBJLoader();\n+    objLoader.setMaterials(mtl);\n    objLoader.load('resources/models/windmill/windmill.obj', (root) =&gt; {\n      root.updateMatrixWorld();\n      scene.add(root);\n      // compute the box that contains all the stuff\n      // from root and below\n      const box = new THREE.Box3().setFromObject(root);\n\n      const boxSize = box.getSize(new THREE.Vector3()).length();\n      const boxCenter = box.getCenter(new THREE.Vector3());\n\n      // set the camera to frame the box\n      frameArea(boxSize * 1.2, boxSize, boxCenter, camera);\n\n      // update the Trackball controls to handle the new size\n      controls.maxDistance = boxSize * 10;\n      controls.target.copy(boxCenter);\n      controls.update();\n    });\n+  });\n}\n</pre>\n<p>Before we actually try it out I ran into some issues that rather than show a failure I'm just going to go over them.</p>\n<p>Issue #1: The three <a href=\"/docs/#examples/loaders/MTLLoader\"><code class=\"notranslate\" translate=\"no\">MTLLoader</code></a> creates materials that multiply the material's diffuse color by the diffuse texture map.</p>\n<p>That's a useful feature but looking a the .MTL file above the line</p>\n<pre class=\"prettyprint showlinemods notranslate lang-mtl\" translate=\"no\">Kd 0.00 0.00 0.00\n</pre>\n<p>sets the diffuse color to 0. Texture map * 0 = black! It's possible the modeling tool used to make the windmill\ndid not multiply the diffuse texture map by the diffuse color. That's why it worked for the artists that made this windmill.</p>\n<p>To fix this we can change the line to</p>\n<pre class=\"prettyprint showlinemods notranslate lang-mtl\" translate=\"no\">Kd 1.00 1.00 1.00\n</pre>\n<p>since Texture Map * 1 = Texture Map.</p>\n<p>Issue #2: The specular color is also black</p>\n<p>The line that starts with <code class=\"notranslate\" translate=\"no\">Ks</code> specifies the specular color. It's likely the modeling software used to make the windmill\ndid something similar as it did with diffuse maps in that it used the specular map's color for specular highlights.\nThree.js uses only the red channel of a specular map as input to how much of the specular color to reflect but three still\nneeds a specular color set.</p>\n<p>Like above we can fix that by editing the .MTL file like this.</p>\n<pre class=\"prettyprint showlinemods notranslate lang-mtl\" translate=\"no\">-Ks 0.00 0.00 0.00\n+Ks 1.00 1.00 1.00\n</pre>\n<p>Issue #3: The <code class=\"notranslate\" translate=\"no\">windmill_normal.jpg</code> is a normal map not a bump map.</p>\n<p>Just like above we just need to edit the .MTL file</p>\n<pre class=\"prettyprint showlinemods notranslate lang-mtl\" translate=\"no\">-map_bump windmill_normal.jpg\n-bump windmill_normal.jpg\n+norm windmill_normal.jpg\n</pre>\n<p>Given all that if we now try it out it should load up with materials.</p>\n<p></p><div translate=\"no\" class=\"threejs_example_container notranslate\">\n  <div></div>\n  <a class=\"threejs_center\" href=\"/manual/examples/load-obj-materials-windmill2.html\" target=\"_blank\">click here to open in a separate window</a>\n</div>\n\n<p></p>\n<p>Loading models often runs into these kinds of issues. Common issues include:</p>\n<ul>\n<li><p>Needing to know the size</p>\n<p>Above we made the camera try to frame the scene but that's not always the appropriate thing to do. Generally the most appropriate thing\nto do is to make your own models or download the models, load them up in some 3D software and look at their scale and adjust if need be.</p>\n</li>\n<li><p>Orientation Wrong</p>\n<p>THREE.js is generally Y = up. Some modeling packages default to Z = up, some Y = up. Some are settable.\nIf you run into this case where you load a model and it's on its side. You can either hack your code to rotate the model after loading (not recommended),\nor you can load the model into your favorite modeling package or use some command line tools to rotate the object in the orientation you need it to be\njust like you'd edit an image for your website rather than download it and apply code to adjust it. Blender even has options when you export to\nchange the orientation.</p>\n</li>\n<li><p>No .MTL file or wrong materials or incompatible parameters</p>\n<p>Above we used a .MTL file above which helped us load materials but there were issues. We manually edited the .MTL file to fix.\nIt's also common to look inside the .OBJ file to see what materials there are, or to load the .OBJ file in THREE.js and walk the\nscene and print out all the materials. Then, go modify the code to make custom materials and assign them where appropriate either\nby making a name/material pair object to pass to the loader instead of loading the .MTL file, OR, after the scene has loaded, walking the\nscene and fixing things.</p>\n</li>\n<li><p>Textures too large</p>\n<p>Most 3D models are made for either architecture, movies and commercials, or\ngames. For architecture and movies no one really cares about the size\nof the textures since. For games people care because games have limited\nmemory but most games run locally. Webpages though you want to load\nas fast as possible and so you need to look at the textures and try\nto make them as small as possible and still look good. In fact the first windmill we should arguably done something about\nthe textures. They are currently a total of 10meg!!!</p>\n<p>Also remember\nlike we mentioned in the <a href=\"textures.html\">article on textures</a> that\ntextures take memory so a 50k JPG that expands to 4096x4096 will download\nfast but still take a ton of memory.</p>\n</li>\n</ul>\n<p>The last thing I wanted to show is spinning the windmills. Unfortunately, .OBJ files have no hierarchy. That means all parts of each\nwindmill are basically considered 1 single mesh. You can't spin the blades of the mill as they aren't separated from the rest of the building.</p>\n<p>This is one of the main reasons why .OBJ is not really a good format. If I was to guess, the reason it's more common than other formats\nis because it's simple and doesn't support many features it works more often than not. Especially if you're making something still like\nan architectural image and there's no need to animate anything it's not a bad way to get static props into a scene.</p>\n<p>Next up we'll try <a href=\"load-gltf.html\">loading a gLTF scene</a>. The gLTF format supports many more features.</p>\n\n        </div>\n      </div>\n    </div>",
    "success": true,
    "error": "",
    "crawled_at": 1786376809.300204,
    "is_shell": false
  },
  {
    "url": "https://threejs.org/manual/en/load-gltf.html",
    "title": "Loading a .GLTF File",
    "content": "<div class=\"container\">\n      <div class=\"lesson-title\">\n        <h1>Loading a .GLTF File</h1>\n      </div>\n      <div class=\"lesson\">\n        <div class=\"lesson-main\">\n          <p>In a previous lesson we <a href=\"load-obj.html\">loaded an .OBJ file</a>. If\nyou haven't read it you might want to check it out first.</p>\n<p>As pointed out over there the .OBJ file format is very old and fairly\nsimple. It provides no scene graph so everything loaded is one large\nmesh. It was designed mostly as a simple way to pass data between\n3D editors.</p>\n<p><a href=\"https://github.com/KhronosGroup/glTF\">The gLTF format</a> is actually\na format designed from the ground up for be used for displaying\ngraphics. 3D formats can be divided into 3 or 4 basic types.</p>\n<ul>\n<li><p>3D Editor Formats</p>\n<p>This are formats specific to a single app. .blend (Blender), .max (3d Studio Max),\n.mb and .ma (Maya), etc...</p>\n</li>\n<li><p>Exchange formats</p>\n<p>These are formats like .OBJ, .DAE (Collada), .FBX. They are designed to help exchange\ninformation between 3D editors. As such they are usually much larger than needed with\nextra info used only inside 3d editors</p>\n</li>\n<li><p>App formats</p>\n<p>These are usually specific to certain apps, usually games.</p>\n</li>\n<li><p>Transmission formats</p>\n<p>gLTF might be the first true transmission format. I suppose VRML might be considered\none but VRML was actually a pretty poor format.</p>\n<p>gLTF is designed to do some things well that all those other formats don't do</p>\n<ol>\n<li><p>Be small for transmission</p>\n<p>For example this means much of their large data, like vertices, is stored in\nbinary. When you download a .gLTF file that data can be uploaded to the GPU\nwith zero processing. It's ready as is. This is in contrast to say VRML, .OBJ,\nor .DAE where vertices are stored as text and have to be parsed. Text vertex\npositions can easily be 3x to 5x larger than binary.</p>\n</li>\n<li><p>Be ready to render</p>\n<p>This again is different from other formats except maybe App formats. The data\nin a glTF file is mean to be rendered, not edited. Data that's not important to\nrendering has generally been removed. Polygons have been converted to triangles.\nMaterials have known values that are supposed to work everywhere.</p>\n</li>\n</ol>\n</li>\n</ul>\n<p>gLTF was specifically designed so you should be able to download a glTF file and\ndisplay it with a minimum of trouble. Let's cross our fingers that's truly the case\nas none of the other formats have been able to do this.</p>\n<p>I wasn't really sure what I should show. At some level loading and displaying a gLTF file\nis simpler than an .OBJ file. Unlike a .OBJ file materials are directly part of the format.\nThat said I thought I should at least load one up and I think going over the issues I ran\ninto might provide some good info.</p>\n<p>Searching the net I found <a href=\"https://sketchfab.com/models/edd1c604e1e045a0a2a552ddd9a293e6\">this low-poly city</a>\nby <a href=\"https://sketchfab.com/antonmoek\">antonmoek</a> which seemed like if we're lucky\nmight make a good example.</p>\n<div class=\"threejs_center\"><img src=\"../resources/images/cartoon_lowpoly_small_city_free_pack.jpg\"></div>\n\n<p>Starting with <a href=\"load-obj.html\">an example from the .OBJ article</a> I removed the code\nfor loading .OBJ and replaced it with code for loading .GLTF</p>\n<p>The old .OBJ code was</p>\n<pre class=\"prettyprint showlinemods notranslate lang-js\" translate=\"no\">const mtlLoader = new MTLLoader();\nmtlLoader.loadMtl('resources/models/windmill/windmill-fixed.mtl', (mtl) =&gt; {\n  mtl.preload();\n  mtl.materials.Material.side = THREE.DoubleSide;\n  objLoader.setMaterials(mtl);\n  objLoader.load('resources/models/windmill/windmill.obj', (event) =&gt; {\n    const root = event.detail.loaderRootNode;\n    scene.add(root);\n    ...\n  });\n});\n</pre>\n<p>The new .GLTF code is</p>\n<pre class=\"prettyprint showlinemods notranslate lang-js\" translate=\"no\">{\n  const gltfLoader = new GLTFLoader();\n  const url = 'resources/models/cartoon_lowpoly_small_city_free_pack/scene.gltf';\n  gltfLoader.load(url, (gltf) =&gt; {\n    const root = gltf.scene;\n    scene.add(root);\n    ...\n  });\n</pre>\n<p>I kept the auto framing code as before</p>\n<p>We also need to include the <a href=\"/docs/#examples/loaders/GLTFLoader\"><code class=\"notranslate\" translate=\"no\">GLTFLoader</code></a> and we can get rid of the <a href=\"/docs/#examples/loaders/OBJLoader\"><code class=\"notranslate\" translate=\"no\">OBJLoader</code></a>.</p>\n<pre class=\"prettyprint showlinemods notranslate lang-html\" translate=\"no\">-import {LoaderSupport} from 'three/addons/loaders/LoaderSupport.js';\n-import {OBJLoader} from 'three/addons/loaders/OBJLoader.js';\n-import {MTLLoader} from 'three/addons/loaders/MTLLoader.js';\n+import {GLTFLoader} from 'three/addons/loaders/GLTFLoader.js';\n</pre>\n<p>And running that we get</p>\n<p></p><div translate=\"no\" class=\"threejs_example_container notranslate\">\n  <div></div>\n  <a class=\"threejs_center\" href=\"/manual/examples/load-gltf.html\" target=\"_blank\">click here to open in a separate window</a>\n</div>\n\n<p></p>\n<p>Magic! It just works, textures and all.</p>\n<p>Next I wanted to see if I could animate the cars driving around so\nI needed to check if the scene had the cars as separate entities\nand if they were setup in a way I could use them.</p>\n<p>I wrote some code to dump put the scenegraph to the <a href=\"debugging-javascript.html\">JavaScript\nconsole</a>.</p>\n<p>Here's the code to print out the scenegraph.</p>\n<pre class=\"prettyprint showlinemods notranslate lang-js\" translate=\"no\">function dumpObject(obj, lines = [], isLast = true, prefix = '') {\n  const localPrefix = isLast ? '└─' : '├─';\n  lines.push(<code class=\"notranslate\" translate=\"no\">${prefix}${prefix ? localPrefix : ''}${obj.name || '*no-name*'} [${obj.type}]</code>);\n  const newPrefix = prefix + (isLast ? '  ' : '│ ');\n  const lastNdx = obj.children.length - 1;\n  obj.children.forEach((child, ndx) =&gt; {\n    const isLast = ndx === lastNdx;\n    dumpObject(child, lines, isLast, newPrefix);\n  });\n  return lines;\n}\n</pre>\n<p>And I just called it right after loading the scene.</p>\n<pre class=\"prettyprint showlinemods notranslate lang-js\" translate=\"no\">const gltfLoader = new GLTFLoader();\ngltfLoader.load('resources/models/cartoon_lowpoly_small_city_free_pack/scene.gltf', (gltf) =&gt; {\n  const root = gltf.scene;\n  scene.add(root);\n  console.log(dumpObject(root).join('\\n'));\n</pre>\n<p><a href=\"../examples/load-gltf-dump-scenegraph.html\">Running that</a> I got this listing</p>\n<pre class=\"prettyprint showlinemods notranslate lang-text\" translate=\"no\">OSG_Scene [Scene]\n  └─RootNode_(gltf_orientation_matrix) [Object3D]\n    └─RootNode_(model_correction_matrix) [Object3D]\n      └─4d4100bcb1c640e69699a87140df79d7fbx [Object3D]\n        └─RootNode [Object3D]\n          │ ...\n          ├─Cars [Object3D]\n          │ ├─CAR_03_1 [Object3D]\n          │ │ └─CAR_03_1_World_ap_0 [Mesh]\n          │ ├─CAR_03 [Object3D]\n          │ │ └─CAR_03_World_ap_0 [Mesh]\n          │ ├─Car_04 [Object3D]\n          │ │ └─Car_04_World_ap_0 [Mesh]\n          │ ├─CAR_03_2 [Object3D]\n          │ │ └─CAR_03_2_World_ap_0 [Mesh]\n          │ ├─Car_04_1 [Object3D]\n          │ │ └─Car_04_1_World_ap_0 [Mesh]\n          │ ├─Car_04_2 [Object3D]\n          │ │ └─Car_04_2_World_ap_0 [Mesh]\n          │ ├─Car_04_3 [Object3D]\n          │ │ └─Car_04_3_World_ap_0 [Mesh]\n          │ ├─Car_04_4 [Object3D]\n          │ │ └─Car_04_4_World_ap_0 [Mesh]\n          │ ├─Car_08_4 [Object3D]\n          │ │ └─Car_08_4_World_ap8_0 [Mesh]\n          │ ├─Car_08_3 [Object3D]\n          │ │ └─Car_08_3_World_ap9_0 [Mesh]\n          │ ├─Car_04_1_2 [Object3D]\n          │ │ └─Car_04_1_2_World_ap_0 [Mesh]\n          │ ├─Car_08_2 [Object3D]\n          │ │ └─Car_08_2_World_ap11_0 [Mesh]\n          │ ├─CAR_03_1_2 [Object3D]\n          │ │ └─CAR_03_1_2_World_ap_0 [Mesh]\n          │ ├─CAR_03_2_2 [Object3D]\n          │ │ └─CAR_03_2_2_World_ap_0 [Mesh]\n          │ ├─Car_04_2_2 [Object3D]\n          │ │ └─Car_04_2_2_World_ap_0 [Mesh]\n          ...\n</pre>\n<p>From that we can see all the cars happen to be under a parent\ncalled <code class=\"notranslate\" translate=\"no\">\"Cars\"</code></p>\n<pre class=\"prettyprint showlinemods notranslate lang-text\" translate=\"no\">*          ├─Cars [Object3D]\n          │ ├─CAR_03_1 [Object3D]\n          │ │ └─CAR_03_1_World_ap_0 [Mesh]\n          │ ├─CAR_03 [Object3D]\n          │ │ └─CAR_03_World_ap_0 [Mesh]\n          │ ├─Car_04 [Object3D]\n          │ │ └─Car_04_World_ap_0 [Mesh]\n</pre>\n<p>So as a simple test I thought I would just try rotating\nall the children of the \"Cars\" node around their Y axis.</p>\n<p>I looked up the \"Cars\" node after loading the scene\nand saved the result.</p>\n<pre class=\"prettyprint showlinemods notranslate lang-js\" translate=\"no\">+let cars;\n{\n  const gltfLoader = new GLTFLoader();\n  gltfLoader.load('resources/models/cartoon_lowpoly_small_city_free_pack/scene.gltf', (gltf) =&gt; {\n    const root = gltf.scene;\n    scene.add(root);\n+    cars = root.getObjectByName('Cars');\n</pre>\n<p>Then in the <code class=\"notranslate\" translate=\"no\">render</code> function we can just set the rotation\nof each child of <code class=\"notranslate\" translate=\"no\">cars</code>.</p>\n<pre class=\"prettyprint showlinemods notranslate lang-js\" translate=\"no\">+function render(time) {\n+  time *= 0.001;  // convert to seconds\n\n  if (resizeRendererToDisplaySize(renderer)) {\n    const canvas = renderer.domElement;\n    camera.aspect = canvas.clientWidth / canvas.clientHeight;\n    camera.updateProjectionMatrix();\n  }\n\n+  if (cars) {\n+    for (const car of cars.children) {\n+      car.rotation.y = time;\n+    }\n+  }\n\n  renderer.render(scene, camera);\n\n  requestAnimationFrame(render);\n}\n</pre>\n<p>And we get</p>\n<p></p><div translate=\"no\" class=\"threejs_example_container notranslate\">\n  <div></div>\n  <a class=\"threejs_center\" href=\"/manual/examples/load-gltf-rotate-cars.html\" target=\"_blank\">click here to open in a separate window</a>\n</div>\n\n<p></p>\n<p>Hmmm, it looks like unfortunately this scene wasn't designed to\nanimate the cars as their origins are not setup for that purpose.\nThe trucks are rotating in the wrong direction.</p>\n<p>This brings up an important point which is if you're going to\ndo something in 3D you need to plan ahead and design your assets\nso they have their origins in the correct places, so they are\nthe correct scale, etc.</p>\n<p>Since I'm not an artist and I don't know blender that well I\nwill hack this example. We'll take each car and parent it to\nanother <a href=\"/docs/#api/en/core/Object3D\"><code class=\"notranslate\" translate=\"no\">Object3D</code></a>. We will then move those <a href=\"/docs/#api/en/core/Object3D\"><code class=\"notranslate\" translate=\"no\">Object3D</code></a> objects\nto move the cars but separately we can set the car's original\n<a href=\"/docs/#api/en/core/Object3D\"><code class=\"notranslate\" translate=\"no\">Object3D</code></a> to re-orient it so it's about where we really need it.</p>\n<p>Looking back at the scene graph listing it looks like there\nare really only 3 types of cars, \"Car_08\", \"CAR_03\", and \"Car_04\".\nHopefully each type of car will work with the same adjustments.</p>\n<p>I wrote this code to go through each car, parent it to a new\n<a href=\"/docs/#api/en/core/Object3D\"><code class=\"notranslate\" translate=\"no\">Object3D</code></a>, parent that new <a href=\"/docs/#api/en/core/Object3D\"><code class=\"notranslate\" translate=\"no\">Object3D</code></a> to the scene, and apply\nsome per car <em>type</em> settings to fix its orientation, and add\nthe new <a href=\"/docs/#api/en/core/Object3D\"><code class=\"notranslate\" translate=\"no\">Object3D</code></a> a <code class=\"notranslate\" translate=\"no\">cars</code> array.</p>\n<pre class=\"prettyprint showlinemods notranslate lang-js\" translate=\"no\">-let cars;\n+const cars = [];\n{\n  const gltfLoader = new GLTFLoader();\n  gltfLoader.load('resources/models/cartoon_lowpoly_small_city_free_pack/scene.gltf', (gltf) =&gt; {\n    const root = gltf.scene;\n    scene.add(root);\n\n-    cars = root.getObjectByName('Cars');\n+    const loadedCars = root.getObjectByName('Cars');\n+    const fixes = [\n+      { prefix: 'Car_08', rot: [Math.PI * .5, 0, Math.PI * .5], },\n+      { prefix: 'CAR_03', rot: [0, Math.PI, 0], },\n+      { prefix: 'Car_04', rot: [0, Math.PI, 0], },\n+    ];\n+\n+    root.updateMatrixWorld();\n+    for (const car of loadedCars.children.slice()) {\n+      const fix = fixes.find(fix =&gt; car.name.startsWith(fix.prefix));\n+      const obj = new THREE.Object3D();\n+      car.getWorldPosition(obj.position);\n+      car.position.set(0, 0, 0);\n+      car.rotation.set(...fix.rot);\n+      obj.add(car);\n+      scene.add(obj);\n+      cars.push(obj);\n+    }\n     ...\n</pre>\n<p>This fixes the orientation of the cars. </p>\n<p></p><div translate=\"no\" class=\"threejs_example_container notranslate\">\n  <div></div>\n  <a class=\"threejs_center\" href=\"/manual/examples/load-gltf-rotate-cars-fixed.html\" target=\"_blank\">click here to open in a separate window</a>\n</div>\n\n<p></p>\n<p>Now let's drive them around.</p>\n<p>Making even a simple driving system is too much for this post but\nit seems instead we could just make one convoluted path that\ndrives down all the roads and then put the cars on the path.\nHere's a picture from Blender about half way through building\nthe path.</p>\n<div class=\"threejs_center\"><img src=\"../resources/images/making-path-for-cars.jpg\" style=\"width: 1094px\"></div>\n\n<p>I needed a way to get the data for that path out of Blender.\nFortunately I was able to select just my path and export .OBJ checking \"write nurbs\".</p>\n<div class=\"threejs_center\"><img src=\"../resources/images/blender-export-obj-write-nurbs.jpg\" style=\"width: 498px\"></div>\n\n<p>Opening the .OBJ file I was able to get a list of points\nwhich I formatted into this</p>\n<pre class=\"prettyprint showlinemods notranslate lang-js\" translate=\"no\">const controlPoints = [\n  [1.118281, 5.115846, -3.681386],\n  [3.948875, 5.115846, -3.641834],\n  [3.960072, 5.115846, -0.240352],\n  [3.985447, 5.115846, 4.585005],\n  [-3.793631, 5.115846, 4.585006],\n  [-3.826839, 5.115846, -14.736200],\n  [-14.542292, 5.115846, -14.765865],\n  [-14.520929, 5.115846, -3.627002],\n  [-5.452815, 5.115846, -3.634418],\n  [-5.467251, 5.115846, 4.549161],\n  [-13.266233, 5.115846, 4.567083],\n  [-13.250067, 5.115846, -13.499271],\n  [4.081842, 5.115846, -13.435463],\n  [4.125436, 5.115846, -5.334928],\n  [-14.521364, 5.115846, -5.239871],\n  [-14.510466, 5.115846, 5.486727],\n  [5.745666, 5.115846, 5.510492],\n  [5.787942, 5.115846, -14.728308],\n  [-5.423720, 5.115846, -14.761919],\n  [-5.373599, 5.115846, -3.704133],\n  [1.004861, 5.115846, -3.641834],\n];\n</pre>\n<p>THREE.js has some curve classes. The <a href=\"/docs/#api/en/extras/curves/CatmullRomCurve3\"><code class=\"notranslate\" translate=\"no\">CatmullRomCurve3</code></a> seemed\nlike it might work. The thing about that kind of curve is\nit tries to make a smooth curve going through the points.</p>\n<p>In fact putting those points in directly will generate\na curve like this</p>\n<div class=\"threejs_center\"><img src=\"../resources/images/car-curves-before.png\" style=\"width: 400px\"></div>\n\n<p>but we want a sharper corners. It seemed like if we computed\nsome extra points we could get what we want. For each pair\nof points we'll compute a point 10% of the way between\nthe 2 points and another 90% of the way between the 2 points\nand pass the result to <a href=\"/docs/#api/en/extras/curves/CatmullRomCurve3\"><code class=\"notranslate\" translate=\"no\">CatmullRomCurve3</code></a>.</p>\n<p>This will give us a curve like this</p>\n<div class=\"threejs_center\"><img src=\"../resources/images/car-curves-after.png\" style=\"width: 400px\"></div>\n\n<p>Here's the code to make the curve </p>\n<pre class=\"prettyprint showlinemods notranslate lang-js\" translate=\"no\">let curve;\nlet curveObject;\n{\n  const controlPoints = [\n    [1.118281, 5.115846, -3.681386],\n    [3.948875, 5.115846, -3.641834],\n    [3.960072, 5.115846, -0.240352],\n    [3.985447, 5.115846, 4.585005],\n    [-3.793631, 5.115846, 4.585006],\n    [-3.826839, 5.115846, -14.736200],\n    [-14.542292, 5.115846, -14.765865],\n    [-14.520929, 5.115846, -3.627002],\n    [-5.452815, 5.115846, -3.634418],\n    [-5.467251, 5.115846, 4.549161],\n    [-13.266233, 5.115846, 4.567083],\n    [-13.250067, 5.115846, -13.499271],\n    [4.081842, 5.115846, -13.435463],\n    [4.125436, 5.115846, -5.334928],\n    [-14.521364, 5.115846, -5.239871],\n    [-14.510466, 5.115846, 5.486727],\n    [5.745666, 5.115846, 5.510492],\n    [5.787942, 5.115846, -14.728308],\n    [-5.423720, 5.115846, -14.761919],\n    [-5.373599, 5.115846, -3.704133],\n    [1.004861, 5.115846, -3.641834],\n  ];\n  const p0 = new THREE.Vector3();\n  const p1 = new THREE.Vector3();\n  curve = new THREE.CatmullRomCurve3(\n    controlPoints.map((p, ndx) =&gt; {\n      p0.set(...p);\n      p1.set(...controlPoints[(ndx + 1) % controlPoints.length]);\n      return [\n        (new THREE.Vector3()).copy(p0),\n        (new THREE.Vector3()).lerpVectors(p0, p1, 0.1),\n        (new THREE.Vector3()).lerpVectors(p0, p1, 0.9),\n      ];\n    }).flat(),\n    true,\n  );\n  {\n    const points = curve.getPoints(250);\n    const geometry = new THREE.BufferGeometry().setFromPoints(points);\n    const material = new THREE.LineBasicMaterial({color: 0xff0000});\n    curveObject = new THREE.Line(geometry, material);\n    scene.add(curveObject);\n  }\n}\n</pre>\n<p>The first part of that code makes a curve.\nThe second part of that code generates 250 points\nfrom the curve and then creates an object to display\nthe lines made by connecting those 250 points.</p>\n<p>Running <a href=\"../examples/load-gltf-car-path.html\">the example</a> I didn't see\nthe curve. To make it visible I made it ignore the depth test and\nrender last</p>\n<pre class=\"prettyprint showlinemods notranslate lang-js\" translate=\"no\">    curveObject = new THREE.Line(geometry, material);\n+    material.depthTest = false;\n+    curveObject.renderOrder = 1;\n</pre>\n<p>And that's when I discovered it was way too small.</p>\n<div class=\"threejs_center\"><img src=\"../resources/images/car-curves-too-small.png\" style=\"width: 498px\"></div>\n\n<p>Checking the hierarchy in Blender I found out that the artist had\nscaled the node all the cars are parented to.</p>\n<div class=\"threejs_center\"><img src=\"../resources/images/cars-scale-0.01.png\" style=\"width: 342px;\"></div>\n\n<p>Scaling is bad for real time 3D apps. It causes all kinds of\nissues and ends up being no end of frustration when doing\nreal time 3D. Artists often don't know this because it's so\neasy to scale an entire scene in a 3D editing program but\nif you decide to make a real time 3D app I suggest you request your\nartists to never scale anything. If they change the scale\nthey should find a way to apply that scale to the vertices\nso that when it ends up making it to your app you can ignore\nscale.</p>\n<p>And, not just scale, in this case the cars are rotated and offset\nby their parent, the <code class=\"notranslate\" translate=\"no\">Cars</code> node. This will make it hard at runtime\nto move the cars around in world space. To be clear, in this case\nwe want cars to drive around in world space which is why these\nissues are coming up. If something that is meant to be manipulated\nin a local space, like the moon revolving around the earth this\nis less of an issue.</p>\n<p>Going back to the function we wrote above to dump the scene graph,\nlet's dump the position, rotation, and scale of each node.</p>\n<pre class=\"prettyprint showlinemods notranslate lang-js\" translate=\"no\">+function dumpVec3(v3, precision = 3) {\n+  return <code class=\"notranslate\" translate=\"no\">${v3.x.toFixed(precision)}, ${v3.y.toFixed(precision)}, ${v3.z.toFixed(precision)}</code>;\n+}\n\nfunction dumpObject(obj, lines, isLast = true, prefix = '') {\n  const localPrefix = isLast ? '└─' : '├─';\n  lines.push(<code class=\"notranslate\" translate=\"no\">${prefix}${prefix ? localPrefix : ''}${obj.name || '*no-name*'} [${obj.type}]</code>);\n+  const dataPrefix = obj.children.length\n+     ? (isLast ? '  │ ' : '│ │ ')\n+     : (isLast ? '    ' : '│   ');\n+  lines.push(<code class=\"notranslate\" translate=\"no\">${prefix}${dataPrefix}  pos: ${dumpVec3(obj.position)}</code>);\n+  lines.push(<code class=\"notranslate\" translate=\"no\">${prefix}${dataPrefix}  rot: ${dumpVec3(obj.rotation)}</code>);\n+  lines.push(<code class=\"notranslate\" translate=\"no\">${prefix}${dataPrefix}  scl: ${dumpVec3(obj.scale)}</code>);\n  const newPrefix = prefix + (isLast ? '  ' : '│ ');\n  const lastNdx = obj.children.length - 1;\n  obj.children.forEach((child, ndx) =&gt; {\n    const isLast = ndx === lastNdx;\n    dumpObject(child, lines, isLast, newPrefix);\n  });\n  return lines;\n}\n</pre>\n<p>And the result from <a href=\"../examples/load-gltf-dump-scenegraph-extra.html\">running it</a></p>\n<pre class=\"prettyprint showlinemods notranslate lang-text\" translate=\"no\">OSG_Scene [Scene]\n  │   pos: 0.000, 0.000, 0.000\n  │   rot: 0.000, 0.000, 0.000\n  │   scl: 1.000, 1.000, 1.000\n  └─RootNode_(gltf_orientation_matrix) [Object3D]\n    │   pos: 0.000, 0.000, 0.000\n    │   rot: -1.571, 0.000, 0.000\n    │   scl: 1.000, 1.000, 1.000\n    └─RootNode_(model_correction_matrix) [Object3D]\n      │   pos: 0.000, 0.000, 0.000\n      │   rot: 0.000, 0.000, 0.000\n      │   scl: 1.000, 1.000, 1.000\n      └─4d4100bcb1c640e69699a87140df79d7fbx [Object3D]\n        │   pos: 0.000, 0.000, 0.000\n        │   rot: 1.571, 0.000, 0.000\n        │   scl: 1.000, 1.000, 1.000\n        └─RootNode [Object3D]\n          │   pos: 0.000, 0.000, 0.000\n          │   rot: 0.000, 0.000, 0.000\n          │   scl: 1.000, 1.000, 1.000\n          ├─Cars [Object3D]\n*          │ │   pos: -369.069, -90.704, -920.159\n*          │ │   rot: 0.000, 0.000, 0.000\n*          │ │   scl: 1.000, 1.000, 1.000\n          │ ├─CAR_03_1 [Object3D]\n          │ │ │   pos: 22.131, 14.663, -475.071\n          │ │ │   rot: -3.142, 0.732, 3.142\n          │ │ │   scl: 1.500, 1.500, 1.500\n          │ │ └─CAR_03_1_World_ap_0 [Mesh]\n          │ │       pos: 0.000, 0.000, 0.000\n          │ │       rot: 0.000, 0.000, 0.000\n          │ │       scl: 1.000, 1.000, 1.000\n</pre>\n<p>This shows us that <code class=\"notranslate\" translate=\"no\">Cars</code> in the original scene has had its rotation and scale\nremoved and applied to its children. That suggests either whatever exporter was\nused to create the .GLTF file did some special work here or more likely the\nartist exported a different version of the file than the corresponding .blend\nfile, which is why things don't match.</p>\n<p>The moral of that is I should have probably downloaded the .blend\nfile and exported myself. Before exporting I should have inspected\nall the major nodes and removed any transformations.</p>\n<p>All these nodes at the top</p>\n<pre class=\"prettyprint showlinemods notranslate lang-text\" translate=\"no\">OSG_Scene [Scene]\n  │   pos: 0.000, 0.000, 0.000\n  │   rot: 0.000, 0.000, 0.000\n  │   scl: 1.000, 1.000, 1.000\n  └─RootNode_(gltf_orientation_matrix) [Object3D]\n    │   pos: 0.000, 0.000, 0.000\n    │   rot: -1.571, 0.000, 0.000\n    │   scl: 1.000, 1.000, 1.000\n    └─RootNode_(model_correction_matrix) [Object3D]\n      │   pos: 0.000, 0.000, 0.000\n      │   rot: 0.000, 0.000, 0.000\n      │   scl: 1.000, 1.000, 1.000\n      └─4d4100bcb1c640e69699a87140df79d7fbx [Object3D]\n        │   pos: 0.000, 0.000, 0.000\n        │   rot: 1.571, 0.000, 0.000\n        │   scl: 1.000, 1.000, 1.000\n</pre>\n<p>are also a waste.</p>\n<p>Ideally the scene would consist of a single \"root\" node with no position,\nrotation, or scale. At runtime I could then pull all the children out of that\nroot and parent them to the scene itself. There might be children of the root\nlike \"Cars\" which would help me find all the cars but ideally it would also have\nno translation, rotation, or scale so I could re-parent the cars to the scene\nwith the minimal amount of work.</p>\n<p>In any case the quickest though maybe not the best fix is to just\nadjust the object we're using to view the curve.</p>\n<p>Here's what I ended up with.</p>\n<p>First I adjusted the position of the curve and found values\nthat seemed to work. I then hid it.</p>\n<pre class=\"prettyprint showlinemods notranslate lang-js\" translate=\"no\">{\n  const points = curve.getPoints(250);\n  const geometry = new THREE.BufferGeometry().setFromPoints(points);\n  const material = new THREE.LineBasicMaterial({color: 0xff0000});\n  curveObject = new THREE.Line(geometry, material);\n+  curveObject.scale.set(100, 100, 100);\n+  curveObject.position.y = -621;\n+  curveObject.visible = false;\n  material.depthTest = false;\n  curveObject.renderOrder = 1;\n  scene.add(curveObject);\n}\n</pre>\n<p>Then I wrote code to move the cars along the curve. For each car we pick a\nposition from 0 to 1 along the curve and compute a point in world space using\nthe <code class=\"notranslate\" translate=\"no\">curveObject</code> to transform the point. We then pick another point slightly\nfurther down the curve. We set the car's orientation using <code class=\"notranslate\" translate=\"no\">lookAt</code> and put the\ncar at the mid point between the 2 points.</p>\n<pre class=\"prettyprint showlinemods notranslate lang-js\" translate=\"no\">// create 2 Vector3s we can use for path calculations\nconst carPosition = new THREE.Vector3();\nconst carTarget = new THREE.Vector3();\n\nfunction render(time) {\n  ...\n\n-  for (const car of cars) {\n-    car.rotation.y = time;\n-  }\n\n+  {\n+    const pathTime = time * .01;\n+    const targetOffset = 0.01;\n+    cars.forEach((car, ndx) =&gt; {\n+      // a number between 0 and 1 to evenly space the cars\n+      const u = pathTime + ndx / cars.length;\n+\n+      // get the first point\n+      curve.getPointAt(u % 1, carPosition);\n+      carPosition.applyMatrix4(curveObject.matrixWorld);\n+\n+      // get a second point slightly further down the curve\n+      curve.getPointAt((u + targetOffset) % 1, carTarget);\n+      carTarget.applyMatrix4(curveObject.matrixWorld);\n+\n+      // put the car at the first point (temporarily)\n+      car.position.copy(carPosition);\n+      // point the car the second point\n+      car.lookAt(carTarget);\n+\n+      // put the car between the 2 points\n+      car.position.lerpVectors(carPosition, carTarget, 0.5);\n+    });\n+  }\n</pre>\n<p>and when I ran it I found out for each type of car, their height above their origins\nare not consistently set and so I needed to offset each one\na little.</p>\n<pre class=\"prettyprint showlinemods notranslate lang-js\" translate=\"no\">const loadedCars = root.getObjectByName('Cars');\nconst fixes = [\n-  { prefix: 'Car_08', rot: [Math.PI * .5, 0, Math.PI * .5], },\n-  { prefix: 'CAR_03', rot: [0, Math.PI, 0], },\n-  { prefix: 'Car_04', rot: [0, Math.PI, 0], },\n+  { prefix: 'Car_08', y: 0,  rot: [Math.PI * .5, 0, Math.PI * .5], },\n+  { prefix: 'CAR_03', y: 33, rot: [0, Math.PI, 0], },\n+  { prefix: 'Car_04', y: 40, rot: [0, Math.PI, 0], },\n];\n\nroot.updateMatrixWorld();\nfor (const car of loadedCars.children.slice()) {\n  const fix = fixes.find(fix =&gt; car.name.startsWith(fix.prefix));\n  const obj = new THREE.Object3D();\n  car.getWorldPosition(obj.position);\n-  car.position.set(0, 0, 0);\n+  car.position.set(0, fix.y, 0);\n  car.rotation.set(...fix.rot);\n  obj.add(car);\n  scene.add(obj);\n  cars.push(obj);\n}\n</pre>\n<p>And the result.</p>\n<p></p><div translate=\"no\" class=\"threejs_example_container notranslate\">\n  <div></div>\n  <a class=\"threejs_center\" href=\"/manual/examples/load-gltf-animated-cars.html\" target=\"_blank\">click here to open in a separate window</a>\n</div>\n\n<p></p>\n<p>Not bad for a few minutes work.</p>\n<p>The last thing I wanted to do is turn on shadows.</p>\n<p>To do this I grabbed all the GUI code from the <a href=\"/docs/#api/en/lights/DirectionalLight\"><code class=\"notranslate\" translate=\"no\">DirectionalLight</code></a> shadows\nexample in <a href=\"shadows.html\">the article on shadows</a> and pasted it\ninto our latest code.</p>\n<p>Then, after loading, we need to turn on shadows on all the objects.</p>\n<pre class=\"prettyprint showlinemods notranslate lang-js\" translate=\"no\">{\n  const gltfLoader = new GLTFLoader();\n  gltfLoader.load('resources/models/cartoon_lowpoly_small_city_free_pack/scene.gltf', (gltf) =&gt; {\n    const root = gltf.scene;\n    scene.add(root);\n\n+    root.traverse((obj) =&gt; {\n+      if (obj.castShadow !== undefined) {\n+        obj.castShadow = true;\n+        obj.receiveShadow = true;\n+      }\n+    });\n</pre>\n<p>I then spent nearly 4 hours trying to figure out why the shadow helpers\nwere not working. It was because I forgot to enable shadows with</p>\n<pre class=\"prettyprint showlinemods notranslate lang-js\" translate=\"no\">renderer.shadowMap.enabled = true;\n</pre>\n<p>😭</p>\n<p>I then adjusted the values until our <code class=\"notranslate\" translate=\"no\">DirectionLight</code>'s shadow camera\nhad a frustum that covered the entire scene. These are the settings\nI ended up with.</p>\n<pre class=\"prettyprint showlinemods notranslate lang-js\" translate=\"no\">{\n  const color = 0xFFFFFF;\n  const intensity = 1;\n  const light = new THREE.DirectionalLight(color, intensity);\n+  light.castShadow = true;\n*  light.position.set(-250, 800, -850);\n*  light.target.position.set(-550, 40, -450);\n\n+  light.shadow.bias = -0.004;\n+  light.shadow.mapSize.width = 2048;\n+  light.shadow.mapSize.height = 2048;\n\n  scene.add(light);\n  scene.add(light.target);\n+  const cam = light.shadow.camera;\n+  cam.near = 1;\n+  cam.far = 2000;\n+  cam.left = -1500;\n+  cam.right = 1500;\n+  cam.top = 1500;\n+  cam.bottom = -1500;\n...\n</pre>\n<p>and I set the background color to light blue.</p>\n<pre class=\"prettyprint showlinemods notranslate lang-js\" translate=\"no\">const scene = new THREE.Scene();\n-scene.background = new THREE.Color('black');\n+scene.background = new THREE.Color('#DEFEFF');\n</pre>\n<p>And ... shadows</p>\n<p></p><div translate=\"no\" class=\"threejs_example_container notranslate\">\n  <div></div>\n  <a class=\"threejs_center\" href=\"/manual/examples/load-gltf-shadows.html\" target=\"_blank\">click here to open in a separate window</a>\n</div>\n\n<p></p>\n<p>I hope walking through this project was useful and showed some\ngood examples of working though some of the issues of loading\na file with a scenegraph.</p>\n<p>One interesting thing is that comparing the .blend file to the .gltf\nfile, the .blend file has several lights but they are not lights\nafter being loaded into the scene. A .GLTF file is just a JSON\nfile so you can easily look inside. It consists of several\narrays of things and each item in an array is referenced by index\nelse where. While there are extensions in the works they point\nto a problem with almost all 3d formats. <strong>They can never cover every\ncase</strong>.</p>\n<p>There is always a need for more data. For example we manually exported\na path for the cars to follow. Ideally that info could have been in\nthe .GLTF file but to do that we'd need to write our own exporter\nand some how mark nodes for how we want them exported or use a\nnaming scheme or something along those lines to get data from\nwhatever tool we're using to create the data into our app.</p>\n<p>All of that is left as an exercise to the reader.</p>\n\n        </div>\n      </div>\n    </div>",
    "success": true,
    "error": "",
    "crawled_at": 1786376809.300204,
    "is_shell": false
  },
  {
    "url": "https://threejs.org/manual/en/backgrounds.html",
    "title": "Backgrounds and Skyboxes",
    "content": "<div class=\"container\">\n      <div class=\"lesson-title\">\n        <h1>Backgrounds and Skyboxes</h1>\n      </div>\n      <div class=\"lesson\">\n        <div class=\"lesson-main\">\n          <p>Most of the articles here use a solid color for a background.</p>\n<p>Adding as static background can be as simple as setting some CSS. Taking\nan example from <a href=\"responsive.html\">the article on making THREE.js responsive</a>\nwe only need to change 2 things.</p>\n<p>We need to add some CSS to our canvas to set its background to an image</p>\n<pre class=\"prettyprint showlinemods notranslate lang-html\" translate=\"no\">&lt;style&gt;\nbody {\n    margin: 0;\n}\n#c {\n    width: 100%;\n    height: 100%;\n    display: block;\n+    background: url(resources/images/daikanyama.jpg) no-repeat center center;\n+    background-size: cover;\n}\n&lt;/style&gt;\n</pre>\n<p>and we need to tell the <a href=\"/docs/#api/en/renderers/WebGLRenderer\"><code class=\"notranslate\" translate=\"no\">WebGLRenderer</code></a> to use <code class=\"notranslate\" translate=\"no\">alpha</code> so places we are not\ndrawing anything are transparent.</p>\n<pre class=\"prettyprint showlinemods notranslate lang-js\" translate=\"no\">function main() {\n  const canvas = document.querySelector('#c');\n-  const renderer = new THREE.WebGLRenderer({antialias: true, canvas});\n+  const renderer = new THREE.WebGLRenderer({\n+    antialias: true,\n+    canvas,\n+    alpha: true,\n+  });\n</pre>\n<p>And we get a background.</p>\n<p></p><div translate=\"no\" class=\"threejs_example_container notranslate\">\n  <div></div>\n  <a class=\"threejs_center\" href=\"/manual/examples/background-css.html\" target=\"_blank\">click here to open in a separate window</a>\n</div>\n\n<p></p>\n<p>If we want the background to be able to be affected by <a href=\"post-processing.html\">post processing\neffects</a> then we need to draw the background using\nTHREE.js.</p>\n<p>THREE.js makes this some what simple. We can just set the background of the scene to\na texture.</p>\n<pre class=\"prettyprint showlinemods notranslate lang-js\" translate=\"no\">const loader = new THREE.TextureLoader();\nconst bgTexture = loader.load('resources/images/daikanyama.jpg');\nbgTexture.colorSpace = THREE.SRGBColorSpace;\nscene.background = bgTexture;\n</pre>\n<p>which gives us</p>\n<p></p><div translate=\"no\" class=\"threejs_example_container notranslate\">\n  <div></div>\n  <a class=\"threejs_center\" href=\"/manual/examples/background-scene-background.html\" target=\"_blank\">click here to open in a separate window</a>\n</div>\n\n<p></p>\n<p>This gets us a background image but its stretched to fit the screen.</p>\n<p>We can solve this issue by setting the <code class=\"notranslate\" translate=\"no\">repeat</code> and <code class=\"notranslate\" translate=\"no\">offset</code> properties of\nthe texture to show only a portion of image.</p>\n<pre class=\"prettyprint showlinemods notranslate lang-js\" translate=\"no\">function render(time) {\n\n   ...\n\n+  // Set the repeat and offset properties of the background texture\n+  // to keep the image's aspect correct.\n+  // Note the image may not have loaded yet.\n+  const canvasAspect = canvas.clientWidth / canvas.clientHeight;\n+  const imageAspect = bgTexture.image ? bgTexture.image.width / bgTexture.image.height : 1;\n+  const aspect = imageAspect / canvasAspect;\n+\n+  bgTexture.offset.x = aspect &gt; 1 ? (1 - 1 / aspect) / 2 : 0;\n+  bgTexture.repeat.x = aspect &gt; 1 ? 1 / aspect : 1;\n+\n+  bgTexture.offset.y = aspect &gt; 1 ? 0 : (1 - aspect) / 2;\n+  bgTexture.repeat.y = aspect &gt; 1 ? 1 : aspect;\n\n  ...\n\n  renderer.render(scene, camera);\n\n  requestAnimationFrame(render);\n}\n</pre>\n<p>and now THREE.js drawing the background. There is no visible difference from\nthe CSS version at the top but now if we used a <a href=\"post-processing.html\">post processing\neffect</a> the background would be affected too.</p>\n<p></p><div translate=\"no\" class=\"threejs_example_container notranslate\">\n  <div></div>\n  <a class=\"threejs_center\" href=\"/manual/examples/background-scene-background-fixed-aspect.html\" target=\"_blank\">click here to open in a separate window</a>\n</div>\n\n<p></p>\n<p>Of course a static background is not usually what we want in a 3D scene. Instead\nwe usually want some kind of <em>skybox</em>. A skybox is just that, box with the sky\ndraw on it. We put the camera inside the box and it looks like there is a sky in\nthe background.</p>\n<p>The most common way to implement a skybox is to make a cube, apply a texture to\nit, draw it from the inside. On each side of the cube put a texture (using\ntexture coordinates) that looks like some image of the horizon. It's also often\ncommon to use a sky sphere or a sky dome with a texture drawn on it. You can\nprobably figure that one out on your own. Just make a cube or sphere,\n<a href=\"textures.html\">apply a texture</a>, mark it as <code class=\"notranslate\" translate=\"no\">THREE.BackSide</code> so we\nrender the inside instead of the outside, and either put it in your scene directly\nor like above, or, make 2 scenes, a special one to draw the skybox/sphere/dome and the\nnormal one to draw everything else. You'd use your normal <a href=\"/docs/#api/en/cameras/PerspectiveCamera\"><code class=\"notranslate\" translate=\"no\">PerspectiveCamera</code></a> to\ndraw. No need for the <a href=\"/docs/#api/en/cameras/OrthographicCamera\"><code class=\"notranslate\" translate=\"no\">OrthographicCamera</code></a>.</p>\n<p>Another solution is to use a <em>Cubemap</em>. A Cubemap is a special kind of texture\nthat has 6 sides, the sides of a cube. Instead of using standard texture\ncoordinates it uses a direction from the center pointing outward to decide where\nto get a color.</p>\n<p>Here are the 6 images of a cubemap from the computer history museum in Mountain\nView, California.</p>\n<div class=\"threejs_center\">\n  <img src=\"../examples/resources/images/cubemaps/computer-history-museum/pos-x.jpg\" style=\"width: 200px\" class=\"border\">\n  <img src=\"../examples/resources/images/cubemaps/computer-history-museum/neg-x.jpg\" style=\"width: 200px\" class=\"border\">\n  <img src=\"../examples/resources/images/cubemaps/computer-history-museum/pos-y.jpg\" style=\"width: 200px\" class=\"border\">\n</div>\n<div class=\"threejs_center\">\n  <img src=\"../examples/resources/images/cubemaps/computer-history-museum/neg-y.jpg\" style=\"width: 200px\" class=\"border\">\n  <img src=\"../examples/resources/images/cubemaps/computer-history-museum/pos-z.jpg\" style=\"width: 200px\" class=\"border\">\n  <img src=\"../examples/resources/images/cubemaps/computer-history-museum/neg-z.jpg\" style=\"width: 200px\" class=\"border\">\n</div>\n\n<p>To use them we use <a href=\"/docs/#api/en/loaders/CubeTextureLoader\"><code class=\"notranslate\" translate=\"no\">CubeTextureLoader</code></a> to load them and then use that as a the\nscene's background.</p>\n<pre class=\"prettyprint showlinemods notranslate lang-js\" translate=\"no\">{\n  const loader = new THREE.CubeTextureLoader();\n  const texture = loader.load([\n    'resources/images/cubemaps/computer-history-museum/pos-x.jpg',\n    'resources/images/cubemaps/computer-history-museum/neg-x.jpg',\n    'resources/images/cubemaps/computer-history-museum/pos-y.jpg',\n    'resources/images/cubemaps/computer-history-museum/neg-y.jpg',\n    'resources/images/cubemaps/computer-history-museum/pos-z.jpg',\n    'resources/images/cubemaps/computer-history-museum/neg-z.jpg',\n  ]);\n  scene.background = texture;\n}\n</pre>\n<p>At render time we don't need to adjust the texture like we did above</p>\n<pre class=\"prettyprint showlinemods notranslate lang-js\" translate=\"no\">function render(time) {\n\n   ...\n\n-  // Set the repeat and offset properties of the background texture\n-  // to keep the image's aspect correct.\n-  // Note the image may not have loaded yet.\n-  const canvasAspect = canvas.clientWidth / canvas.clientHeight;\n-  const imageAspect = bgTexture.image ? bgTexture.image.width / bgTexture.image.height : 1;\n-  const aspect = imageAspect / canvasAspect;\n-\n-  bgTexture.offset.x = aspect &gt; 1 ? (1 - 1 / aspect) / 2 : 0;\n-  bgTexture.repeat.x = aspect &gt; 1 ? 1 / aspect : 1;\n-\n-  bgTexture.offset.y = aspect &gt; 1 ? 0 : (1 - aspect) / 2;\n-  bgTexture.repeat.y = aspect &gt; 1 ? 1 : aspect;\n\n  ...\n\n  renderer.render(scene, camera);\n\n  requestAnimationFrame(render);\n}\n</pre>\n<p>Let's add some controls in so we can rotate the camera.</p>\n<pre class=\"prettyprint showlinemods notranslate lang-js\" translate=\"no\">import {OrbitControls} from 'three/addons/controls/OrbitControls.js';\n</pre>\n<pre class=\"prettyprint showlinemods notranslate lang-js\" translate=\"no\">const fov = 75;\nconst aspect = 2;  // the canvas default\nconst near = 0.1;\n-const far = 5;\n+const far = 100;\nconst camera = new THREE.PerspectiveCamera(fov, aspect, near, far);\n-camera.position.z = 2;\n+camera.position.z = 3;\n\n+const controls = new OrbitControls(camera, canvas);\n+controls.target.set(0, 0, 0);\n+controls.update();\n</pre>\n<p>and try it out. Drag on the example to rotate the camera and see the cubemap\nsurrounds us.</p>\n<p></p><div translate=\"no\" class=\"threejs_example_container notranslate\">\n  <div></div>\n  <a class=\"threejs_center\" href=\"/manual/examples/background-cubemap.html\" target=\"_blank\">click here to open in a separate window</a>\n</div>\n\n<p></p>\n<p>Another option is to use an Equirectangular map. This is the kind of picture a\n<a href=\"https://google.com/search?q=360+camera\">360 camera</a> takes.</p>\n<p><a href=\"https://hdrihaven.com/hdri/?h=tears_of_steel_bridge\">Here's one</a> I found from\n<a href=\"https://hdrihaven.com\">this site</a>.</p>\n<div class=\"threejs_center\"><img src=\"../examples/resources/images/equirectangularmaps/tears_of_steel_bridge_2k.jpg\" style=\"width: 600px\"></div>\n\n<pre class=\"prettyprint showlinemods notranslate lang-js\" translate=\"no\">{\n-  const loader = new THREE.CubeTextureLoader();\n-  const texture = loader.load([\n-    'resources/images/cubemaps/computer-history-museum/pos-x.jpg',\n-    'resources/images/cubemaps/computer-history-museum/neg-x.jpg',\n-    'resources/images/cubemaps/computer-history-museum/pos-y.jpg',\n-    'resources/images/cubemaps/computer-history-museum/neg-y.jpg',\n-    'resources/images/cubemaps/computer-history-museum/pos-z.jpg',\n-    'resources/images/cubemaps/computer-history-museum/neg-z.jpg',\n-  ]);\n-  scene.background = texture;\n+  const loader = new THREE.TextureLoader();\n+  const texture = loader.load(\n+    'resources/images/equirectangularmaps/tears_of_steel_bridge_2k.jpg',\n+    () =&gt; {\n+      texture.mapping = THREE.EquirectangularReflectionMapping;\n+      texture.colorSpace = THREE.SRGBColorSpace;\n+      scene.background = texture;\n+    });\n}\n</pre>\n<p>And that's all there is to it.</p>\n<p></p><div translate=\"no\" class=\"threejs_example_container notranslate\">\n  <div></div>\n  <a class=\"threejs_center\" href=\"/manual/examples/background-equirectangularmap.html\" target=\"_blank\">click here to open in a separate window</a>\n</div>\n\n<p></p>\n<p>Rather than do it at load time you can also convert an equirectangular image\nto a cubemap beforehand. <a href=\"https://matheowis.github.io/HDRI-to-CubeMap/\">Here's a site that will do it for you</a>.</p>\n\n        </div>\n      </div>\n    </div>",
    "success": true,
    "error": "",
    "crawled_at": 1786376809.300204,
    "is_shell": false
  },
  {
    "url": "https://threejs.org/manual/en/transparency.html",
    "title": "Transparency",
    "content": "<div class=\"container\">\n      <div class=\"lesson-title\">\n        <h1>Transparency</h1>\n      </div>\n      <div class=\"lesson\">\n        <div class=\"lesson-main\">\n          <p>Transparency in three.js is both easy and hard.</p>\n<p>First we'll go over the easy part. Let's make a\nscene with 8 cubes placed in a 2x2x2 grid.</p>\n<p>We'll start with the example from\n<a href=\"rendering-on-demand.html\">the article on rendering on demand</a>\nwhich had 3 cubes and modify it to have 8. First\nlet's change our <code class=\"notranslate\" translate=\"no\">makeInstance</code> function to take\nan x, y, and z</p>\n<pre class=\"prettyprint showlinemods notranslate lang-js\" translate=\"no\">-function makeInstance(geometry, color) {\n+function makeInstance(geometry, color, x, y, z) {\n  const material = new THREE.MeshPhongMaterial({color});\n\n  const cube = new THREE.Mesh(geometry, material);\n  scene.add(cube);\n\n-  cube.position.x = x;\n+  cube.position.set(x, y, z);\n\n  return cube;\n}\n</pre>\n<p>Then we can create 8 cubes</p>\n<pre class=\"prettyprint showlinemods notranslate lang-js\" translate=\"no\">+function hsl(h, s, l) {\n+  return (new THREE.Color()).setHSL(h, s, l);\n+}\n\n-makeInstance(geometry, 0x44aa88,  0);\n-makeInstance(geometry, 0x8844aa, -2);\n-makeInstance(geometry, 0xaa8844,  2);\n\n+{\n+  const d = 0.8;\n+  makeInstance(geometry, hsl(0 / 8, 1, .5), -d, -d, -d);\n+  makeInstance(geometry, hsl(1 / 8, 1, .5),  d, -d, -d);\n+  makeInstance(geometry, hsl(2 / 8, 1, .5), -d,  d, -d);\n+  makeInstance(geometry, hsl(3 / 8, 1, .5),  d,  d, -d);\n+  makeInstance(geometry, hsl(4 / 8, 1, .5), -d, -d,  d);\n+  makeInstance(geometry, hsl(5 / 8, 1, .5),  d, -d,  d);\n+  makeInstance(geometry, hsl(6 / 8, 1, .5), -d,  d,  d);\n+  makeInstance(geometry, hsl(7 / 8, 1, .5),  d,  d,  d);\n+}\n</pre>\n<p>I also adjusted the camera</p>\n<pre class=\"prettyprint showlinemods notranslate lang-js\" translate=\"no\">const fov = 75;\nconst aspect = 2;  // the canvas default\nconst near = 0.1;\n-const far = 5;\n+const far = 25;\nconst camera = new THREE.PerspectiveCamera(fov, aspect, near, far);\n-camera.position.z = 4;\n+camera.position.z = 2;\n</pre>\n<p>Set the background to white</p>\n<pre class=\"prettyprint showlinemods notranslate lang-js\" translate=\"no\">const scene = new THREE.Scene();\n+scene.background = new THREE.Color('white');\n</pre>\n<p>And added a second light so all sides of the cubes get some lighting.</p>\n<pre class=\"prettyprint showlinemods notranslate lang-js\" translate=\"no\">-{\n+function addLight(...pos) {\n  const color = 0xFFFFFF;\n  const intensity = 1;\n  const light = new THREE.DirectionalLight(color, intensity);\n-  light.position.set(-1, 2, 4);\n+  light.position.set(...pos);\n  scene.add(light);\n}\n+addLight(-1, 2, 4);\n+addLight( 1, -1, -2);\n</pre>\n<p>To make the cubes transparent we just need to set the\n<a href=\"/docs/#api/en/materials/Material#transparent\"><code class=\"notranslate\" translate=\"no\">transparent</code></a> flag and to set an\n<a href=\"/docs/#api/en/materials/Material#opacity\"><code class=\"notranslate\" translate=\"no\">opacity</code></a> level with 1 being completely opaque\nand 0 being completely transparent.</p>\n<pre class=\"prettyprint showlinemods notranslate lang-js\" translate=\"no\">function makeInstance(geometry, color, x, y, z) {\n-  const material = new THREE.MeshPhongMaterial({color});\n+  const material = new THREE.MeshPhongMaterial({\n+    color,\n+    opacity: 0.5,\n+    transparent: true,\n+  });\n\n  const cube = new THREE.Mesh(geometry, material);\n  scene.add(cube);\n\n  cube.position.set(x, y, z);\n\n  return cube;\n}\n</pre>\n<p>and with that we get 8 transparent cubes</p>\n<p></p><div translate=\"no\" class=\"threejs_example_container notranslate\">\n  <div></div>\n  <a class=\"threejs_center\" href=\"/manual/examples/transparency.html\" target=\"_blank\">click here to open in a separate window</a>\n</div>\n\n<p></p>\n<p>Drag on the example to rotate the view. </p>\n<p>So it seems easy but ... look closer. The cubes are\nmissing their backs.</p>\n<div class=\"threejs_center\"><img src=\"../resources/images/transparency-cubes-no-backs.png\" style=\"width: 416px;\"></div>\n<div class=\"threejs_center\">no backs</div>\n\n<p>We learned about the <a href=\"/docs/#api/en/materials/Material#side\"><code class=\"notranslate\" translate=\"no\">side</code></a> material property in\n<a href=\"materials.html\">the article on materials</a>.\nSo, let's set it to <code class=\"notranslate\" translate=\"no\">THREE.DoubleSide</code> to get both sides of each cube to be drawn.</p>\n<pre class=\"prettyprint showlinemods notranslate lang-js\" translate=\"no\">const material = new THREE.MeshPhongMaterial({\n  color,\n  map: loader.load(url),\n  opacity: 0.5,\n  transparent: true,\n+  side: THREE.DoubleSide,\n});\n</pre>\n<p>And we get</p>\n<p></p><div translate=\"no\" class=\"threejs_example_container notranslate\">\n  <div></div>\n  <a class=\"threejs_center\" href=\"/manual/examples/transparency-doubleside.html\" target=\"_blank\">click here to open in a separate window</a>\n</div>\n\n<p></p>\n<p>Give it a spin. It kind of looks like it's working as we can see backs\nexcept on closer inspection sometimes we can't.</p>\n<div class=\"threejs_center\"><img src=\"../resources/images/transparency-cubes-some-backs.png\" style=\"width: 368px;\"></div>\n<div class=\"threejs_center\">the left back face of each cube is missing</div>\n\n<p>This happens because of the way 3D objects are generally drawn. For each geometry\neach triangle is drawn one at a time. When each pixel of the triangle is drawn\n2 things are recorded. One, the color for that pixel and two, the depth of that pixel.\nWhen the next triangle is drawn, for each pixel if the depth is deeper than the\npreviously recorded depth no pixel is drawn.</p>\n<p>This works great for opaque things but it fails for transparent things.</p>\n<p>The solution is to sort transparent things and draw the stuff in back before\ndrawing the stuff in front. THREE.js does this for objects like <a href=\"/docs/#api/en/objects/Mesh\"><code class=\"notranslate\" translate=\"no\">Mesh</code></a> otherwise\nthe very first example would have failed between cubes with some cubes blocking\nout others. Unfortunately for individual triangles shorting would be extremely slow. </p>\n<p>The cube has 12 triangles, 2 for each face, and the order they are drawn is\n<a href=\"custom-buffergeometry.html\">the same order they are built in the geometry</a>\nso depending on which direction we are looking the triangles closer to the camera\nmight get drawn first. In that case the triangles in the back aren't drawn.\nThis is why sometimes we don't see the backs.</p>\n<p>For a convex object like a sphere or a cube one kind of solution is to add\nevery cube to the scene twice. Once with a material that draws\nonly the back facing triangles and another with a material that only\ndraws the front facing triangles.</p>\n<pre class=\"prettyprint showlinemods notranslate lang-js\" translate=\"no\">function makeInstance(geometry, color, x, y, z) {\n+  [THREE.BackSide, THREE.FrontSide].forEach((side) =&gt; {\n    const material = new THREE.MeshPhongMaterial({\n      color,\n      opacity: 0.5,\n      transparent: true,\n+      side,\n    });\n\n    const cube = new THREE.Mesh(geometry, material);\n    scene.add(cube);\n\n    cube.position.set(x, y, z);\n+  });\n}\n</pre>\n<p>Any with that it <em>seems</em> to work.</p>\n<p></p><div translate=\"no\" class=\"threejs_example_container notranslate\">\n  <div></div>\n  <a class=\"threejs_center\" href=\"/manual/examples/transparency-doubleside-hack.html\" target=\"_blank\">click here to open in a separate window</a>\n</div>\n\n<p></p>\n<p>It assumes that the three.js's sorting is stable. Meaning that because we\nadded the <code class=\"notranslate\" translate=\"no\">side: THREE.BackSide</code> mesh first and because it's at the exact same\nposition that it will be drawn before the <code class=\"notranslate\" translate=\"no\">side: THREE.FrontSide</code> mesh.</p>\n<p>Let's make 2 intersecting planes (after deleting all the code related to cubes).\nWe'll <a href=\"textures.html\">add a texture</a> to each plane.</p>\n<pre class=\"prettyprint showlinemods notranslate lang-js\" translate=\"no\">const planeWidth = 1;\nconst planeHeight = 1;\nconst geometry = new THREE.PlaneGeometry(planeWidth, planeHeight);\n\nconst loader = new THREE.TextureLoader();\n\nfunction makeInstance(geometry, color, rotY, url) {\n  const texture = loader.load(url, render);\n  const material = new THREE.MeshPhongMaterial({\n    color,\n    map: texture,\n    opacity: 0.5,\n    transparent: true,\n    side: THREE.DoubleSide,\n  });\n\n  const mesh = new THREE.Mesh(geometry, material);\n  scene.add(mesh);\n\n  mesh.rotation.y = rotY;\n}\n\nmakeInstance(geometry, 'pink',       0,             'resources/images/happyface.png');\nmakeInstance(geometry, 'lightblue',  Math.PI * 0.5, 'resources/images/hmmmface.png');\n</pre>\n<p>This time we can use <code class=\"notranslate\" translate=\"no\">side: THREE.DoubleSide</code> since we can only ever see one\nside of a plane at a time. Also note we pass our <code class=\"notranslate\" translate=\"no\">render</code> function to the texture\nloading function so that when the texture finishes loading we re-render the scene.\nThis is because this sample is <a href=\"rendering-on-demand.html\">rendering on demand</a>\ninstead of rendering continuously.</p>\n<p></p><div translate=\"no\" class=\"threejs_example_container notranslate\">\n  <div></div>\n  <a class=\"threejs_center\" href=\"/manual/examples/transparency-intersecting-planes.html\" target=\"_blank\">click here to open in a separate window</a>\n</div>\n\n<p></p>\n<p>And again we see a similar issue.</p>\n<div class=\"threejs_center\"><img src=\"../resources/images/transparency-planes.png\" style=\"width: 408px;\"></div>\n<div class=\"threejs_center\">half a face is missing</div>\n\n<p>The solution here is to manually split the each pane into 2 panes\nso that there really is no intersection.</p>\n<pre class=\"prettyprint showlinemods notranslate lang-js\" translate=\"no\">function makeInstance(geometry, color, rotY, url) {\n+  const base = new THREE.Object3D();\n+  scene.add(base);\n+  base.rotation.y = rotY;\n\n+  [-1, 1].forEach((x) =&gt; {\n    const texture = loader.load(url, render);\n+    texture.offset.x = x &lt; 0 ? 0 : 0.5;\n+    texture.repeat.x = .5;\n    const material = new THREE.MeshPhongMaterial({\n      color,\n      map: texture,\n      opacity: 0.5,\n      transparent: true,\n      side: THREE.DoubleSide,\n    });\n\n    const mesh = new THREE.Mesh(geometry, material);\n-    scene.add(mesh);\n+    base.add(mesh);\n\n-    mesh.rotation.y = rotY;\n+    mesh.position.x = x * .25;\n  });\n}\n</pre>\n<p>How you accomplish that is up to you. If I was using modeling package like\n<a href=\"https://blender.org\">Blender</a> I'd probably do this manually by adjusting\ntexture coordinates. Here though we're using <a href=\"/docs/#api/en/geometries/PlaneGeometry\"><code class=\"notranslate\" translate=\"no\">PlaneGeometry</code></a> which by\ndefault stretches the texture across the plane. Like we <a href=\"textures.html\">covered\nbefore</a> By setting the <a href=\"/docs/#api/en/textures/Texture#repeat\"><code class=\"notranslate\" translate=\"no\">texture.repeat</code></a>\nand <a href=\"/docs/#api/en/textures/Texture#offset\"><code class=\"notranslate\" translate=\"no\">texture.offset</code></a> we can scale and move the texture to get\nthe correct half of the face texture on each plane.</p>\n<p>The code above also makes a <a href=\"/docs/#api/en/core/Object3D\"><code class=\"notranslate\" translate=\"no\">Object3D</code></a> and parents the 2 planes to it.\nIt seemed easier to rotate a parent <a href=\"/docs/#api/en/core/Object3D\"><code class=\"notranslate\" translate=\"no\">Object3D</code></a> than to do the math\nrequired do it without. </p>\n<p></p><div translate=\"no\" class=\"threejs_example_container notranslate\">\n  <div></div>\n  <a class=\"threejs_center\" href=\"/manual/examples/transparency-intersecting-planes-fixed.html\" target=\"_blank\">click here to open in a separate window</a>\n</div>\n\n<p></p>\n<p>This solution really only works for simple things like 2 planes that\nare not changing their intersection position.</p>\n<p>For textured objects one more solution is to set an alpha test.</p>\n<p>An alpha test is a level of <em>alpha</em> below which three.js will not\ndraw the pixel. If we don't draw a pixel at all then the depth\nissues mentioned above disappear. For relatively sharp edged textures\nthis works pretty well. Examples include leaf textures on a plant or tree\nor often a patch of grass.</p>\n<p>Let's try on the 2 planes. First let's use different textures.\nThe textures above were 100% opaque. These 2 use transparency.</p>\n<div class=\"spread\">\n  <div><img class=\"checkerboard\" src=\"../examples/resources/images/tree-01.png\"></div>\n  <div><img class=\"checkerboard\" src=\"../examples/resources/images/tree-02.png\"></div>\n</div>\n\n<p>Going back to the 2 planes that intersect (before we split them) let's\nuse these textures and set an <a href=\"/docs/#api/en/materials/Material#alphaTest\"><code class=\"notranslate\" translate=\"no\">alphaTest</code></a>.</p>\n<pre class=\"prettyprint showlinemods notranslate lang-js\" translate=\"no\">function makeInstance(geometry, color, rotY, url) {\n  const texture = loader.load(url, render);\n  const material = new THREE.MeshPhongMaterial({\n    color,\n    map: texture,\n-    opacity: 0.5,\n    transparent: true,\n+    alphaTest: 0.5,\n    side: THREE.DoubleSide,\n  });\n\n  const mesh = new THREE.Mesh(geometry, material);\n  scene.add(mesh);\n\n  mesh.rotation.y = rotY;\n}\n\n-makeInstance(geometry, 'pink',       0,             'resources/images/happyface.png');\n-makeInstance(geometry, 'lightblue',  Math.PI * 0.5, 'resources/images/hmmmface.png');\n+makeInstance(geometry, 'white', 0,             'resources/images/tree-01.png');\n+makeInstance(geometry, 'white', Math.PI * 0.5, 'resources/images/tree-02.png');\n</pre>\n<p>Before we run this let's add a small UI so we can more easily play with the <code class=\"notranslate\" translate=\"no\">alphaTest</code>\nand <code class=\"notranslate\" translate=\"no\">transparent</code> settings. We'll use lil-gui like we introduced\nin the <a href=\"scenegraph.html\">article on three.js's scenegraph</a>.</p>\n<p>First we'll make a helper for lil-gui that sets every material in the scene\nto a value</p>\n<pre class=\"prettyprint showlinemods notranslate lang-js\" translate=\"no\">class AllMaterialPropertyGUIHelper {\n  constructor(prop, scene) {\n    this.prop = prop;\n    this.scene = scene;\n  }\n  get value() {\n    const {scene, prop} = this;\n    let v;\n    scene.traverse((obj) =&gt; {\n      if (obj.material &amp;&amp; obj.material[prop] !== undefined) {\n        v = obj.material[prop];\n      }\n    });\n    return v;\n  }\n  set value(v) {\n    const {scene, prop} = this;\n    scene.traverse((obj) =&gt; {\n      if (obj.material &amp;&amp; obj.material[prop] !== undefined) {\n        obj.material[prop] = v;\n        obj.material.needsUpdate = true;\n      }\n    });\n  }\n}\n</pre>\n<p>Then we'll add the gui</p>\n<pre class=\"prettyprint showlinemods notranslate lang-js\" translate=\"no\">const gui = new GUI();\ngui.add(new AllMaterialPropertyGUIHelper('alphaTest', scene), 'value', 0, 1)\n    .name('alphaTest')\n    .onChange(requestRenderIfNotRequested);\ngui.add(new AllMaterialPropertyGUIHelper('transparent', scene), 'value')\n    .name('transparent')\n    .onChange(requestRenderIfNotRequested);\n</pre>\n<p>and of course we need to include lil-gui</p>\n<pre class=\"prettyprint showlinemods notranslate lang-js\" translate=\"no\">import * as THREE from 'three';\nimport {OrbitControls} from 'three/addons/controls/OrbitControls.js';\n+import {GUI} from 'three/addons/libs/lil-gui.module.min.js';\n</pre>\n<p>and here's the results</p>\n<p></p><div translate=\"no\" class=\"threejs_example_container notranslate\">\n  <div></div>\n  <a class=\"threejs_center\" href=\"/manual/examples/transparency-intersecting-planes-alphatest.html\" target=\"_blank\">click here to open in a separate window</a>\n</div>\n\n<p></p>\n<p>You can see it works but zoom in and you'll see one plane has white lines.</p>\n<div class=\"threejs_center\"><img src=\"../resources/images/transparency-alphatest-issues.png\" style=\"width: 532px;\"></div>\n\n<p>This is the same depth issue from before. That plane was drawn first\nso the plane behind is not drawn. There is no perfect solution.\nAdjust the <code class=\"notranslate\" translate=\"no\">alphaTest</code> and/or turn off <code class=\"notranslate\" translate=\"no\">transparent</code> to find a solution\nthat fits your use case.</p>\n<p>The take way from this article is perfect transparency is hard.\nThere are issues and trade offs and workarounds.</p>\n<p>For example say you have a car.\nCars usually have windshields on all 4 sides. If you want to avoid the sorting issues\nabove you'd have to make each window its own object so that three.js can\nsort the windows and draw them in the correct order.</p>\n<p>If you are making some plants or grass the alpha test solution is common.</p>\n<p>Which solution you pick depends on your needs. </p>\n\n        </div>\n      </div>\n    </div>",
    "success": true,
    "error": "",
    "crawled_at": 1786376809.300204,
    "is_shell": false
  },
  {
    "url": "https://threejs.org/manual/en/multiple-scenes.html",
    "title": "Multiple Canvases Multiple Scenes",
    "content": "<div class=\"container\">\n      <div class=\"lesson-title\">\n        <h1>Multiple Canvases Multiple Scenes</h1>\n      </div>\n      <div class=\"lesson\">\n        <div class=\"lesson-main\">\n          <p>A common question is how to use THREE.js with multiple canvases.\nLet's say you want to make an e-commerce site or you want to make\na page with lots of 3D diagrams. At first glance it appears easy.\nJust make a canvas every where you want a diagram. For each canvas\nmake a <a href=\"/docs/#api/en/constants/Renderer\"><code class=\"notranslate\" translate=\"no\">Renderer</code></a>.</p>\n<p>You'll quickly find though that you run into problems.</p>\n<ol>\n<li><p>The browser limits how many WebGL contexts you can have.</p>\n<p>Typically that limit is around 8 of them. As soon as you create\nthe 9th context the oldest one will be lost.</p>\n</li>\n<li><p>WebGL resources can not be shared across contexts</p>\n<p>That means if you want to load a 10 meg model into 2 canvases\nand that model uses 20 meg of textures your 10 meg model will\nhave to be loaded twice and your textures will also be loaded\ntwice. Nothing can be shared across contexts. This also\nmeans things have to be initialized twice, shaders compiled twice,\netc. It gets worse as there are more canvases.</p>\n</li>\n</ol>\n<p>So what's the solution?</p>\n<p>The solution is one canvas that fills the viewport in the background and some other element to represent each \"virtual\" canvas. We make a single <a href=\"/docs/#api/en/constants/Renderer\"><code class=\"notranslate\" translate=\"no\">Renderer</code></a> and then one <a href=\"/docs/#api/en/scenes/Scene\"><code class=\"notranslate\" translate=\"no\">Scene</code></a> for each virtual canvas. We'll then check the positions of the virtual canvas elements and if they are on the screen we'll tell THREE.js to draw their scene at the correct place.</p>\n<p>With this solution there is only 1 canvas so we solve both problem 1\nand 2 above. We won't run into the WebGL context limit because we\nwill only be using one context. We also won't run into the sharing\nissues for the same reasons.</p>\n<p>Let's start with a simple example with just 2 scenes. First we'll\nmake the HTML</p>\n<pre class=\"prettyprint showlinemods notranslate lang-html\" translate=\"no\">&lt;canvas id=\"c\"&gt;&lt;/canvas&gt;\n&lt;p&gt;\n  &lt;span id=\"box\" class=\"diagram left\"&gt;&lt;/span&gt;\n  I love boxes. Presents come in boxes.\n  When I find a new box I'm always excited to find out what's inside.\n&lt;/p&gt;\n&lt;p&gt;\n  &lt;span id=\"pyramid\" class=\"diagram right\"&gt;&lt;/span&gt;\n  When I was a kid I dreamed of going on an expedition inside a pyramid\n  and finding a undiscovered tomb full of mummies and treasure.\n&lt;/p&gt;\n</pre>\n<p>Then we can setup the CSS maybe something like this</p>\n<pre class=\"prettyprint showlinemods notranslate lang-css\" translate=\"no\">#c {\n  position: fixed;\n  left: 0;\n  top: 0;\n  width: 100%;\n  height: 100%;\n  display: block;\n  z-index: -1;\n}\n.diagram {\n  display: inline-block;\n  width: 5em;\n  height: 3em;\n  border: 1px solid black;\n}\n.left {\n  float: left;\n  margin-right: .25em;\n}\n.right {\n  float: right;\n  margin-left: .25em;\n}\n</pre>\n<p>We set the canvas to fill the screen and we set its <code class=\"notranslate\" translate=\"no\">z-index</code> to\n-1 to make it appear behind other elements. We also need to specify some kind of width and height for our virtual canvas elements since there is nothing inside to give them any size.</p>\n<p>Now we'll make 2 scenes each with a light and a camera.\nTo one scene we'll add a cube and to another a diamond.</p>\n<pre class=\"prettyprint showlinemods notranslate lang-js\" translate=\"no\">function makeScene(elem) {\n  const scene = new THREE.Scene();\n\n  const fov = 45;\n  const aspect = 2;  // the canvas default\n  const near = 0.1;\n  const far = 5;\n  const camera = new THREE.PerspectiveCamera(fov, aspect, near, far);\n  camera.position.z = 2;\n  camera.position.set(0, 1, 2);\n  camera.lookAt(0, 0, 0);\n\n  {\n    const color = 0xFFFFFF;\n    const intensity = 1;\n    const light = new THREE.DirectionalLight(color, intensity);\n    light.position.set(-1, 2, 4);\n    scene.add(light);\n  }\n\n  return {scene, camera, elem};\n}\n\nfunction setupScene1() {\n  const sceneInfo = makeScene(document.querySelector('#box'));\n  const geometry = new THREE.BoxGeometry(1, 1, 1);\n  const material = new THREE.MeshPhongMaterial({color: 'red'});\n  const mesh = new THREE.Mesh(geometry, material);\n  sceneInfo.scene.add(mesh);\n  sceneInfo.mesh = mesh;\n  return sceneInfo;\n}\n\nfunction setupScene2() {\n  const sceneInfo = makeScene(document.querySelector('#pyramid'));\n  const radius = .8;\n  const widthSegments = 4;\n  const heightSegments = 2;\n  const geometry = new THREE.SphereGeometry(radius, widthSegments, heightSegments);\n  const material = new THREE.MeshPhongMaterial({\n    color: 'blue',\n    flatShading: true,\n  });\n  const mesh = new THREE.Mesh(geometry, material);\n  sceneInfo.scene.add(mesh);\n  sceneInfo.mesh = mesh;\n  return sceneInfo;\n}\n\nconst sceneInfo1 = setupScene1();\nconst sceneInfo2 = setupScene2();\n</pre>\n<p>And then we'll make a function to render each scene\nonly if the element is on the screen. We can tell THREE.js\nto only render to part of the canvas by turning on the <em>scissor</em>\ntest with <a href=\"/docs/#api/en/constants/Renderer.setScissorTest\"><code class=\"notranslate\" translate=\"no\">Renderer.setScissorTest</code></a> and then setting both the scissor and the viewport with <a href=\"/docs/#api/en/constants/Renderer.setViewport\"><code class=\"notranslate\" translate=\"no\">Renderer.setViewport</code></a> and <a href=\"/docs/#api/en/constants/Renderer.setScissor\"><code class=\"notranslate\" translate=\"no\">Renderer.setScissor</code></a>.</p>\n<pre class=\"prettyprint showlinemods notranslate lang-js\" translate=\"no\">function renderSceneInfo(sceneInfo) {\n  const {scene, camera, elem} = sceneInfo;\n\n  // get the viewport relative position of this element\n  const {left, right, top, bottom, width, height} =\n      elem.getBoundingClientRect();\n\n  const isOffscreen =\n      bottom &lt; 0 ||\n      top &gt; renderer.domElement.clientHeight ||\n      right &lt; 0 ||\n      left &gt; renderer.domElement.clientWidth;\n\n  if (isOffscreen) {\n    return;\n  }\n\n  camera.aspect = width / height;\n  camera.updateProjectionMatrix();\n\n  const positiveYUpBottom = canvasRect.height - bottom;\n  renderer.setScissor(left, positiveYUpBottom, width, height);\n  renderer.setViewport(left, positiveYUpBottom, width, height);\n\n  renderer.render(scene, camera);\n}\n</pre>\n<p>And then our render function will just first clear the screen\nand then render each scene.</p>\n<pre class=\"prettyprint showlinemods notranslate lang-js\" translate=\"no\">function render(time) {\n  time *= 0.001;\n\n  resizeRendererToDisplaySize(renderer);\n\n  renderer.setScissorTest(false);\n  renderer.clear(true, true);\n  renderer.setScissorTest(true);\n\n  sceneInfo1.mesh.rotation.y = time * .1;\n  sceneInfo2.mesh.rotation.y = time * .1;\n\n  renderSceneInfo(sceneInfo1);\n  renderSceneInfo(sceneInfo2);\n\n  requestAnimationFrame(render);\n}\n</pre>\n<p>And here it is</p>\n<p></p><div translate=\"no\" class=\"threejs_example_container notranslate\">\n  <div></div>\n  <a class=\"threejs_center\" href=\"/manual/examples/multiple-scenes-v1.html\" target=\"_blank\">click here to open in a separate window</a>\n</div>\n\n<p></p>\n<p>You can see where the first <code class=\"notranslate\" translate=\"no\">&lt;span&gt;</code> is there's a red cube and where the second <code class=\"notranslate\" translate=\"no\">span</code> is there's a blue diamond.</p>\n<h2 id=\"syncing-up\">Syncing up</h2>\n<p>The code above works but there is one minor issue.\nIf your scenes are complicated or if for whatever reason\nit takes too long to render, the position of the scenes\ndrawn into the canvas will lag behind the rest of the page.</p>\n<p>If we give each area a border </p>\n<pre class=\"prettyprint showlinemods notranslate lang-css\" translate=\"no\">.diagram {\n  display: inline-block;\n  width: 5em;\n  height: 3em;\n+  border: 1px solid black;\n}\n</pre>\n<p>And we set the background of each scene</p>\n<pre class=\"prettyprint showlinemods notranslate lang-js\" translate=\"no\">const scene = new THREE.Scene();\n+scene.background = new THREE.Color('red');\n</pre>\n<p>And if we <a href=\"../examples/multiple-scenes-v2.html\" target=\"_blank\">quickly scroll up and down</a> we'll see the issue. Here's an animation of scrolling slowed down by 10x.</p>\n<div class=\"threejs_center\"><img class=\"border\" src=\"../resources/images/multi-view-skew.gif\"></div>\n\n<p>We can switch to a different method which has a different tradeoff. We'll switch the canvas's CSS from <code class=\"notranslate\" translate=\"no\">position: fixed</code> to <code class=\"notranslate\" translate=\"no\">position: absolute</code>. </p>\n<pre class=\"prettyprint showlinemods notranslate lang-css\" translate=\"no\">#c {\n-  position: fixed;\n+  position: absolute;\n</pre>\n<p>Then we'll set the canvas's transform to move it so\nthe top of the canvas is at the top of whatever part\nthe page is currently scrolled to.</p>\n<pre class=\"prettyprint showlinemods notranslate lang-js\" translate=\"no\">function render(time) {\n  ...\n\n  const transform = <code class=\"notranslate\" translate=\"no\">translateY(${window.scrollY}px)</code>;\n  renderer.domElement.style.transform = transform;\n</pre>\n<p><code class=\"notranslate\" translate=\"no\">position: fixed</code> kept the canvas from scrolling at all\nwhile the rest of the page scrolled over it. <code class=\"notranslate\" translate=\"no\">position: absolute</code> will let the canvas scroll with the rest of the page which means whatever we draw will stick with the page as it scrolls even if we're too slow to render. When we finally get a chance to render then we move the canvas so it matches where the page has been scrolled and then we re-render. This means only the edges of the window will show some un-rendered bits for a moment but <a href=\"../examples/multiple-scenes-v2.html\" target=\"_blank\">the stuff in the middle of the page should match up</a> and not slide. Here's a view of the results of the new method slowed down 10x.</p>\n<div class=\"threejs_center\"><img class=\"border\" src=\"../resources/images/multi-view-fixed.gif\"></div>\n\n<h2 id=\"making-it-more-generic\">Making it more Generic</h2>\n<p>Now that we've gotten multiple scenes working let's make this just slightly more generic.</p>\n<p>We could make it so the main render function, the one managing the canvas, just has a list of elements and their associated render function. For each element it would check if the element is on screen and if so call the corresponding render function. In this way we'd have a generic system where individual scenes aren't really aware they are being rendered in some smaller space.</p>\n<p>Here's the main render function</p>\n<pre class=\"prettyprint showlinemods notranslate lang-js\" translate=\"no\">const sceneElements = [];\nfunction addScene(elem, fn) {\n  sceneElements.push({elem, fn});\n}\n\nfunction render(time) {\n  time *= 0.001;\n\n  resizeRendererToDisplaySize(renderer);\n\n  renderer.setScissorTest(false);\n  renderer.setClearColor(clearColor, 0);\n  renderer.clear(true, true);\n  renderer.setScissorTest(true);\n\n  const transform = <code class=\"notranslate\" translate=\"no\">translateY(${window.scrollY}px)</code>;\n  renderer.domElement.style.transform = transform;\n\n  for (const {elem, fn} of sceneElements) {\n    // get the viewport relative position of this element\n    const rect = elem.getBoundingClientRect();\n    const {left, right, top, bottom, width, height} = rect;\n\n    const isOffscreen =\n        bottom &lt; 0 ||\n        top &gt; renderer.domElement.clientHeight ||\n        right &lt; 0 ||\n        left &gt; renderer.domElement.clientWidth;\n\n    if (!isOffscreen) {\n      const positiveYUpBottom = renderer.domElement.clientHeight - bottom;\n      renderer.setScissor(left, positiveYUpBottom, width, height);\n      renderer.setViewport(left, positiveYUpBottom, width, height);\n\n      fn(time, rect);\n    }\n  }\n\n  requestAnimationFrame(render);\n}\n</pre>\n<p>You can see it loops over <code class=\"notranslate\" translate=\"no\">sceneElements</code> which it expects is an array of objects each of which have an <code class=\"notranslate\" translate=\"no\">elem</code> and <code class=\"notranslate\" translate=\"no\">fn</code> property.</p>\n<p>It checks if the element is on screen. If it is it calls <code class=\"notranslate\" translate=\"no\">fn</code> and passes it the current time and its rectangle.</p>\n<p>Now the setup code for each scene just adds itself to the list of scenes</p>\n<pre class=\"prettyprint showlinemods notranslate lang-js\" translate=\"no\">{\n  const elem = document.querySelector('#box');\n  const {scene, camera} = makeScene();\n  const geometry = new THREE.BoxGeometry(1, 1, 1);\n  const material = new THREE.MeshPhongMaterial({color: 'red'});\n  const mesh = new THREE.Mesh(geometry, material);\n  scene.add(mesh);\n  addScene(elem, (time, rect) =&gt; {\n    camera.aspect = rect.width / rect.height;\n    camera.updateProjectionMatrix();\n    mesh.rotation.y = time * .1;\n    renderer.render(scene, camera);\n  });\n}\n\n{\n  const elem = document.querySelector('#pyramid');\n  const {scene, camera} = makeScene();\n  const radius = .8;\n  const widthSegments = 4;\n  const heightSegments = 2;\n  const geometry = new THREE.SphereGeometry(radius, widthSegments, heightSegments);\n  const material = new THREE.MeshPhongMaterial({\n    color: 'blue',\n    flatShading: true,\n  });\n  const mesh = new THREE.Mesh(geometry, material);\n  scene.add(mesh);\n  addScene(elem, (time, rect) =&gt; {\n    camera.aspect = rect.width / rect.height;\n    camera.updateProjectionMatrix();\n    mesh.rotation.y = time * .1;\n    renderer.render(scene, camera);\n  });\n}\n</pre>\n<p>With that we no longer need <code class=\"notranslate\" translate=\"no\">sceneInfo1</code> and <code class=\"notranslate\" translate=\"no\">sceneInfo2</code> and the code that was rotating the meshes is now specific to each scene.</p>\n<p></p><div translate=\"no\" class=\"threejs_example_container notranslate\">\n  <div></div>\n  <a class=\"threejs_center\" href=\"/manual/examples/multiple-scenes-generic.html\" target=\"_blank\">click here to open in a separate window</a>\n</div>\n\n<p></p>\n<h2 id=\"using-html-dataset\">Using HTML Dataset</h2>\n<p>One last even more generic thing we can do is use HTML <a href=\"https://developer.mozilla.org/en-US/docs/Web/API/HTMLElement/dataset\">dataset</a>. This is a way to add your own data to an HTML element. Instead of using <code class=\"notranslate\" translate=\"no\">id=\"...\"</code> we'll use <code class=\"notranslate\" translate=\"no\">data-diagram=\"...\"</code> like this</p>\n<pre class=\"prettyprint showlinemods notranslate lang-html\" translate=\"no\">&lt;canvas id=\"c\"&gt;&lt;/canvas&gt;\n&lt;p&gt;\n-  &lt;span id=\"box\" class=\"diagram left\"&gt;&lt;/span&gt;\n+  &lt;span data-diagram=\"box\" class=\"left\"&gt;&lt;/span&gt;\n  I love boxes. Presents come in boxes.\n  When I find a new box I'm always excited to find out what's inside.\n&lt;/p&gt;\n&lt;p&gt;\n-  &lt;span id=\"pyramid\" class=\"diagram left\"&gt;&lt;/span&gt;\n+  &lt;span data-diagram=\"pyramid\" class=\"right\"&gt;&lt;/span&gt;\n  When I was a kid I dreamed of going on an expedition inside a pyramid\n  and finding a undiscovered tomb full of mummies and treasure.\n&lt;/p&gt;\n</pre>\n<p>We can them change the CSS selector to select for that</p>\n<pre class=\"prettyprint showlinemods notranslate lang-css\" translate=\"no\">-.diagram\n+*[data-diagram] {\n  display: inline-block;\n  width: 5em;\n  height: 3em;\n}\n</pre>\n<p>We'll change the scene setup code to just be a map of names to <em>scene initialization functions</em> that return a <em>scene render function</em>.</p>\n<pre class=\"prettyprint showlinemods notranslate lang-js\" translate=\"no\">const sceneInitFunctionsByName = {\n  'box': () =&gt; {\n    const {scene, camera} = makeScene();\n    const geometry = new THREE.BoxGeometry(1, 1, 1);\n    const material = new THREE.MeshPhongMaterial({color: 'red'});\n    const mesh = new THREE.Mesh(geometry, material);\n    scene.add(mesh);\n    return (time, rect) =&gt; {\n      mesh.rotation.y = time * .1;\n      camera.aspect = rect.width / rect.height;\n      camera.updateProjectionMatrix();\n      renderer.render(scene, camera);\n    };\n  },\n  'pyramid': () =&gt; {\n    const {scene, camera} = makeScene();\n    const radius = .8;\n    const widthSegments = 4;\n    const heightSegments = 2;\n    const geometry = new THREE.SphereGeometry(radius, widthSegments, heightSegments);\n    const material = new THREE.MeshPhongMaterial({\n      color: 'blue',\n      flatShading: true,\n    });\n    const mesh = new THREE.Mesh(geometry, material);\n    scene.add(mesh);\n    return (time, rect) =&gt; {\n      mesh.rotation.y = time * .1;\n      camera.aspect = rect.width / rect.height;\n      camera.updateProjectionMatrix();\n      renderer.render(scene, camera);\n    };\n  },\n};\n</pre>\n<p>And to init we can just use <code class=\"notranslate\" translate=\"no\">querySelectorAll</code> to find all the diagrams and call the corresponding init function for that diagram. </p>\n<pre class=\"prettyprint showlinemods notranslate lang-js\" translate=\"no\">document.querySelectorAll('[data-diagram]').forEach((elem) =&gt; {\n  const sceneName = elem.dataset.diagram;\n  const sceneInitFunction = sceneInitFunctionsByName[sceneName];\n  const sceneRenderFunction = sceneInitFunction(elem);\n  addScene(elem, sceneRenderFunction);\n});\n</pre>\n<p>No change to the visuals but the code is even more generic.</p>\n<p></p>\n<h2 id=\"adding-controls-to-each-element\">Adding Controls to each element</h2>\n<p>Adding interactively, for example a <code class=\"notranslate\" translate=\"no\">TrackballControls</code> is just as easy. First we add the script for the control.</p>\n<pre class=\"prettyprint showlinemods notranslate lang-js\" translate=\"no\">import {TrackballControls} from 'three/addons/controls/TrackballControls.js';\n</pre>\n<p>And then we can add a <code class=\"notranslate\" translate=\"no\">TrackballControls</code> to each scene passing in the element associated with that scene.</p>\n<pre class=\"prettyprint showlinemods notranslate lang-js\" translate=\"no\">-function makeScene() {\n+function makeScene(elem) {\n  const scene = new THREE.Scene();\n\n  const fov = 45;\n  const aspect = 2;  // the canvas default\n  const near = 0.1;\n  const far = 5;\n  const camera = new THREE.PerspectiveCamera(fov, aspect, near, far);\n  camera.position.set(0, 1, 2);\n  camera.lookAt(0, 0, 0);\n+  scene.add(camera);\n\n+  const controls = new TrackballControls(camera, elem);\n+  controls.noZoom = true;\n+  controls.noPan = true;\n\n  {\n    const color = 0xFFFFFF;\n    const intensity = 1;\n    const light = new THREE.DirectionalLight(color, intensity);\n    light.position.set(-1, 2, 4);\n-    scene.add(light);\n+    camera.add(light);\n  }\n\n-  return {scene, camera};\n+ return {scene, camera, controls};\n}\n</pre>\n<p>You'll notice we added the camera to the scene and the light to the camera. This makes the light relative to the camera. Since the <code class=\"notranslate\" translate=\"no\">TrackballControls</code> are moving the camera this is probably what we want. It keeps the light shining on the side of the object we are looking at.</p>\n<p>We need up update those controls in our render functions</p>\n<pre class=\"prettyprint showlinemods notranslate lang-js\" translate=\"no\">const sceneInitFunctionsByName = {\n- 'box': () =&gt; {\n-    const {scene, camera} = makeScene();\n+ 'box': (elem) =&gt; {\n+    const {scene, camera, controls} = makeScene(elem);\n    const geometry = new THREE.BoxGeometry(1, 1, 1);\n    const material = new THREE.MeshPhongMaterial({color: 'red'});\n    const mesh = new THREE.Mesh(geometry, material);\n    scene.add(mesh);\n    return (time, rect) =&gt; {\n      mesh.rotation.y = time * .1;\n      camera.aspect = rect.width / rect.height;\n      camera.updateProjectionMatrix();\n+      controls.handleResize();\n+      controls.update();\n      renderer.render(scene, camera);\n    };\n  },\n-  'pyramid': () =&gt; {\n-    const {scene, camera} = makeScene();\n+  'pyramid': (elem) =&gt; {\n+    const {scene, camera, controls} = makeScene(elem);\n    const radius = .8;\n    const widthSegments = 4;\n    const heightSegments = 2;\n    const geometry = new THREE.SphereGeometry(radius, widthSegments, heightSegments);\n    const material = new THREE.MeshPhongMaterial({\n      color: 'blue',\n      flatShading: true,\n    });\n    const mesh = new THREE.Mesh(geometry, material);\n    scene.add(mesh);\n    return (time, rect) =&gt; {\n      mesh.rotation.y = time * .1;\n      camera.aspect = rect.width / rect.height;\n      camera.updateProjectionMatrix();\n+      controls.handleResize();\n+      controls.update();\n      renderer.render(scene, camera);\n    };\n  },\n};\n</pre>\n<p>And now if you drag the objects they'll rotate.</p>\n<p></p><div translate=\"no\" class=\"threejs_example_container notranslate\">\n  <div></div>\n  <a class=\"threejs_center\" href=\"/manual/examples/multiple-scenes-controls.html\" target=\"_blank\">click here to open in a separate window</a>\n</div>\n\n<p></p>\n<p>These techniques are used on this site itself. In particular <a href=\"primitives.html\">the article about primitives</a> and <a href=\"materials.html\">the article about materials</a> use this technique to add the various examples throughout the article.</p>\n<p>One more solution would be to render to an off screen canvas and copy the result to a 2D canvas at each element.\nThe advantage to this solution is there is no limit on how you can composite each separate area. With the previous\nsolution we and a single canvas in the background. With this solution we have normal HTML elements.</p>\n<p>The disadvantage is it's slower because a copy has to happen for each area. How much slower depends on the browser\nand the GPU.</p>\n<p>The changes needed are pretty small</p>\n<p>First we'll change HTML as we no longer need a canvas in the page</p>\n<pre class=\"prettyprint showlinemods notranslate lang-html\" translate=\"no\">&lt;body&gt;\n-  &lt;canvas id=\"c\"&gt;&lt;/canvas&gt;\n  ...\n&lt;/body&gt;\n</pre>\n<p>then we'll change the CSS</p>\n<pre class=\"prettyprint showlinemods notranslate notranslate\" translate=\"no\">-#c {\n-  position: absolute;\n-  left: 0;\n-  top: 0;\n-  width: 100%;\n-  height: 100%;\n-  display: block;\n-  z-index: -1;\n-}\ncanvas {\n  width: 100%;\n  height: 100%;\n  display: block;\n}\n*[data-diagram] {\n  display: inline-block;\n  width: 5em;\n  height: 3em;\n}\n</pre><p>We've made all canvases fill their container.</p>\n<p>Now let's change the JavaScript. First we no longer\nlook up the canvas. Instead we create one. We also\njust turn on the scissor test at the beginning.</p>\n<pre class=\"prettyprint showlinemods notranslate lang-js\" translate=\"no\">function main() {\n-  const canvas = document.querySelector('#c');\n+  const canvas = document.createElement('canvas');\n  const renderer = new THREE.WebGLRenderer({antialias: true, canvas, alpha: true});\n+  renderer.setScissorTest(true);\n\n  ...\n</pre>\n<p>Then for each scene we create a 2D rendering context and\nappend its canvas to the element for that scene</p>\n<pre class=\"prettyprint showlinemods notranslate lang-js\" translate=\"no\">const sceneElements = [];\nfunction addScene(elem, fn) {\n+  const ctx = document.createElement('canvas').getContext('2d');\n+  elem.appendChild(ctx.canvas);\n-  sceneElements.push({elem, fn});\n+  sceneElements.push({elem, ctx, fn});\n}\n</pre>\n<p>Then when rendering, if the renderer's canvas is not\nbig enough to render this area we increase its size.\nAs well if this area's canvas is the wrong size we\nchange its size. Finally we set the scissor and viewport,\nrender the scene for this area, then copy the result to the area's canvas.</p>\n<pre class=\"prettyprint showlinemods notranslate lang-js\" translate=\"no\">function render(time) {\n  time *= 0.001;\n\n-  resizeRendererToDisplaySize(renderer);\n-\n-  renderer.setScissorTest(false);\n-  renderer.setClearColor(clearColor, 0);\n-  renderer.clear(true, true);\n-  renderer.setScissorTest(true);\n-\n-  const transform = <code class=\"notranslate\" translate=\"no\">translateY(${window.scrollY}px)</code>;\n-  renderer.domElement.style.transform = transform;\n\n-  for (const {elem, fn} of sceneElements) {\n+  for (const {elem, fn, ctx} of sceneElements) {\n    // get the viewport relative position of this element\n    const rect = elem.getBoundingClientRect();\n    const {left, right, top, bottom, width, height} = rect;\n+    const rendererCanvas = renderer.domElement;\n\n    const isOffscreen =\n        bottom &lt; 0 ||\n-        top &gt; renderer.domElement.clientHeight ||\n+        top &gt; window.innerHeight ||\n        right &lt; 0 ||\n-        left &gt; renderer.domElement.clientWidth;\n+        left &gt; window.innerWidth;\n\n    if (!isOffscreen) {\n-      const positiveYUpBottom = renderer.domElement.clientHeight - bottom;\n-      renderer.setScissor(left, positiveYUpBottom, width, height);\n-      renderer.setViewport(left, positiveYUpBottom, width, height);\n\n+      // make sure the renderer's canvas is big enough\n+      if (rendererCanvas.width &lt; width || rendererCanvas.height &lt; height) {\n+        renderer.setSize(width, height, false);\n+      }\n+\n+      // make sure the canvas for this area is the same size as the area\n+      if (ctx.canvas.width !== width || ctx.canvas.height !== height) {\n+        ctx.canvas.width = width;\n+        ctx.canvas.height = height;\n+      }\n+\n+      renderer.setScissor(0, 0, width, height);\n+      renderer.setViewport(0, 0, width, height);\n\n      fn(time, rect);\n\n+      // copy the rendered scene to this element's canvas\n+      ctx.globalCompositeOperation = 'copy';\n+      ctx.drawImage(\n+          rendererCanvas,\n+          0, rendererCanvas.height - height, width, height,  // src rect\n+          0, 0, width, height);                              // dst rect\n    }\n  }\n\n  requestAnimationFrame(render);\n}\n</pre>\n<p>The result looks the same</p>\n<p></p><div translate=\"no\" class=\"threejs_example_container notranslate\">\n  <div></div>\n  <a class=\"threejs_center\" href=\"/manual/examples/multiple-scenes-copy-canvas.html\" target=\"_blank\">click here to open in a separate window</a>\n</div>\n\n<p></p>\n<p>One other advantage to this solution is you could potentially use\n<a href=\"https://developer.mozilla.org/en-US/docs/Web/API/OffscreenCanvas\"><code class=\"notranslate\" translate=\"no\">OffscreenCanvas</code></a>\nto render from a web worker and still use this technique.</p>\n\n        </div>\n      </div>\n    </div>",
    "success": true,
    "error": "",
    "crawled_at": 1786376809.300204,
    "is_shell": false
  },
  {
    "url": "https://threejs.org/manual/en/picking.html",
    "title": "Picking",
    "content": "<div class=\"container\">\n      <div class=\"lesson-title\">\n        <h1>Picking</h1>\n      </div>\n      <div class=\"lesson\">\n        <div class=\"lesson-main\">\n          <p><em>Picking</em> refers to the process of figuring out which object a user clicked on or touched. There are tons of ways to implement picking each with their tradeoffs. Let's go over the 2 most common.</p>\n<p>Probably the most common way of <em>picking</em> is by doing raycasting which means to <em>cast</em> a ray from the mouse through the frustum of the scene and computing which objects that ray intersects. Conceptually it's very simple.</p>\n<p>First we'd take the position of the mouse. We'd convert that into world space by applying the camera's projection and orientation. We'd compute a ray from the near plane of the camera's frustum to the far plane. Then, for every triangle of every object in the scene we'd check if that ray intersects that triangle. If your scene has 1000 objects and each object has 1000 triangles then 1 million triangles will need to be checked.</p>\n<p>A few optimizations would include first checking if the ray intersects with an object's bounding sphere or bounding box, the sphere or box that contains the entire object. If the ray doesn't intersect one of those then we don't have to check the triangles of that object.</p>\n<p>THREE.js provides a <code class=\"notranslate\" translate=\"no\">RayCaster</code> class that does exactly this.</p>\n<p>Let's make a scene with a 100 objects and try picking them. We'll\nstart with an example from <a href=\"responsive.html\">the article on responsive pages</a></p>\n<p>A few changes</p>\n<p>We'll parent the camera to another object so we can spin that other object and the camera will move around the scene just like a selfie stick.</p>\n<pre class=\"prettyprint showlinemods notranslate lang-js\" translate=\"no\">*const fov = 60;\nconst aspect = 2;  // the canvas default\nconst near = 0.1;\n*const far = 200;\nconst camera = new THREE.PerspectiveCamera(fov, aspect, near, far);\n*camera.position.z = 30;\n\nconst scene = new THREE.Scene();\n+scene.background = new THREE.Color('white');\n\n+// put the camera on a pole (parent it to an object)\n+// so we can spin the pole to move the camera around the scene\n+const cameraPole = new THREE.Object3D();\n+scene.add(cameraPole);\n+cameraPole.add(camera);\n</pre>\n<p>and in the <code class=\"notranslate\" translate=\"no\">render</code> function we'll spin the camera pole.</p>\n<pre class=\"prettyprint showlinemods notranslate lang-js\" translate=\"no\">cameraPole.rotation.y = time * .1;\n</pre>\n<p>Also let's put the light on the camera so the light moves with it.</p>\n<pre class=\"prettyprint showlinemods notranslate lang-js\" translate=\"no\">-scene.add(light);\n+camera.add(light);\n</pre>\n<p>Let's generate 100 cubes with random colors in random positions, orientations,\nand scales.</p>\n<pre class=\"prettyprint showlinemods notranslate lang-js\" translate=\"no\">const boxWidth = 1;\nconst boxHeight = 1;\nconst boxDepth = 1;\nconst geometry = new THREE.BoxGeometry(boxWidth, boxHeight, boxDepth);\n\nfunction rand(min, max) {\n  if (max === undefined) {\n    max = min;\n    min = 0;\n  }\n  return min + (max - min) * Math.random();\n}\n\nfunction randomColor() {\n  return <code class=\"notranslate\" translate=\"no\">hsl(${rand(360) | 0}, ${rand(50, 100) | 0}%, 50%)</code>;\n}\n\nconst numObjects = 100;\nfor (let i = 0; i &lt; numObjects; ++i) {\n  const material = new THREE.MeshPhongMaterial({\n    color: randomColor(),\n  });\n\n  const cube = new THREE.Mesh(geometry, material);\n  scene.add(cube);\n\n  cube.position.set(rand(-20, 20), rand(-20, 20), rand(-20, 20));\n  cube.rotation.set(rand(Math.PI), rand(Math.PI), 0);\n  cube.scale.set(rand(3, 6), rand(3, 6), rand(3, 6));\n}\n</pre>\n<p>And finally let's pick.</p>\n<p>Let's make a simple class to manage the picking</p>\n<pre class=\"prettyprint showlinemods notranslate lang-js\" translate=\"no\">class PickHelper {\n  constructor() {\n    this.raycaster = new THREE.Raycaster();\n    this.pickedObject = null;\n    this.pickedObjectSavedColor = 0;\n  }\n  pick(normalizedPosition, scene, camera, time) {\n    // restore the color if there is a picked object\n    if (this.pickedObject) {\n      this.pickedObject.material.emissive.setHex(this.pickedObjectSavedColor);\n      this.pickedObject = undefined;\n    }\n\n    // cast a ray through the frustum\n    this.raycaster.setFromCamera(normalizedPosition, camera);\n    // get the list of objects the ray intersected\n    const intersectedObjects = this.raycaster.intersectObjects(scene.children);\n    if (intersectedObjects.length) {\n      // pick the first object. It's the closest one\n      this.pickedObject = intersectedObjects[0].object;\n      // save its color\n      this.pickedObjectSavedColor = this.pickedObject.material.emissive.getHex();\n      // set its emissive color to flashing red/yellow\n      this.pickedObject.material.emissive.setHex((time * 8) % 2 &gt; 1 ? 0xFFFF00 : 0xFF0000);\n    }\n  }\n}\n</pre>\n<p>You can see we create a <code class=\"notranslate\" translate=\"no\">RayCaster</code> and then we can call the <code class=\"notranslate\" translate=\"no\">pick</code> function to cast a ray through the scene. If the ray hits something we change the color of the first thing it hits.</p>\n<p>Of course we could call this function only when the user pressed the mouse <em>down</em> which is probably usually what you want but for this example we'll pick every frame whatever is under the mouse. To do this we first need to track where the mouse\nis</p>\n<pre class=\"prettyprint showlinemods notranslate lang-js\" translate=\"no\">const pickPosition = {x: 0, y: 0};\nclearPickPosition();\n\n...\n\nfunction getCanvasRelativePosition(event) {\n  const rect = canvas.getBoundingClientRect();\n  return {\n    x: (event.clientX - rect.left) * canvas.width  / rect.width,\n    y: (event.clientY - rect.top ) * canvas.height / rect.height,\n  };\n}\n\nfunction setPickPosition(event) {\n  const pos = getCanvasRelativePosition(event);\n  pickPosition.x = (pos.x / canvas.width ) *  2 - 1;\n  pickPosition.y = (pos.y / canvas.height) * -2 + 1;  // note we flip Y\n}\n\nfunction clearPickPosition() {\n  // unlike the mouse which always has a position\n  // if the user stops touching the screen we want\n  // to stop picking. For now we just pick a value\n  // unlikely to pick something\n  pickPosition.x = -100000;\n  pickPosition.y = -100000;\n}\n\nwindow.addEventListener('mousemove', setPickPosition);\nwindow.addEventListener('mouseout', clearPickPosition);\nwindow.addEventListener('mouseleave', clearPickPosition);\n</pre>\n<p>Notice we're recording a normalized mouse position. Regardless of the size of the canvas we need a value that goes from -1 on the left to +1 on the right. Similarly we need a value that goes from -1 on the bottom to +1 on the top.</p>\n<p>While we're at it lets support mobile as well</p>\n<pre class=\"prettyprint showlinemods notranslate lang-js\" translate=\"no\">window.addEventListener('touchstart', (event) =&gt; {\n  // prevent the window from scrolling\n  event.preventDefault();\n  setPickPosition(event.touches[0]);\n}, {passive: false});\n\nwindow.addEventListener('touchmove', (event) =&gt; {\n  setPickPosition(event.touches[0]);\n});\n\nwindow.addEventListener('touchend', clearPickPosition);\n</pre>\n<p>And finally in our <code class=\"notranslate\" translate=\"no\">render</code> function we call the <code class=\"notranslate\" translate=\"no\">PickHelper</code>'s <code class=\"notranslate\" translate=\"no\">pick</code> function.</p>\n<pre class=\"prettyprint showlinemods notranslate lang-js\" translate=\"no\">+const pickHelper = new PickHelper();\n\nfunction render(time) {\n  time *= 0.001;  // convert to seconds;\n\n  ...\n\n+  pickHelper.pick(pickPosition, scene, camera, time);\n\n  renderer.render(scene, camera);\n\n  ...\n</pre>\n<p>and here's the result</p>\n<p></p><div translate=\"no\" class=\"threejs_example_container notranslate\">\n  <div></div>\n  <a class=\"threejs_center\" href=\"/manual/examples/picking-raycaster.html\" target=\"_blank\">click here to open in a separate window</a>\n</div>\n\n<p></p>\n<p>This appears to work great and it probably does for many use cases\nbut there are several issues.</p>\n<ol>\n<li><p>It's CPU based.</p>\n<p>JavaScript is going through each object and checking if the ray intersects\nthat object's bounding box or bounding sphere. If it does then JavaScript\nhas to go through each and every triangle in that object and check if the\nray intersects the triangle.</p>\n<p>The good part of this is JavaScript can easily compute exactly where the\nray intersected the triangle and provide us with that data. For example\nif you wanted to put a marker where the intersection happened.</p>\n<p>The bad part is that's a lot of work for the CPU to do. If you have\nobjects with lots of triangles it might be slow.</p>\n</li>\n<li><p>It doesn't handle any strange shaders or displacements.</p>\n<p>If you have a shader that deforms or morphs the geometry JavaScript\nhas no knowledge of that deformation and so will give the wrong answer.\nFor example AFAIK you can't use this method with skinned objects.</p>\n</li>\n<li><p>It doesn't handle transparent holes.</p>\n</li>\n</ol>\n<p>As an example let's apply this texture to the cubes.</p>\n<div class=\"threejs_center\"><img class=\"checkerboard\" src=\"../examples/resources/images/frame.png\"></div>\n\n<p>We'll just make these changes</p>\n<pre class=\"prettyprint showlinemods notranslate lang-js\" translate=\"no\">+const loader = new THREE.TextureLoader();\n+const texture = loader.load('resources/images/frame.png');\n\nconst numObjects = 100;\nfor (let i = 0; i &lt; numObjects; ++i) {\n  const material = new THREE.MeshPhongMaterial({\n    color: randomColor(),\n    +map: texture,\n    +transparent: true,\n    +side: THREE.DoubleSide,\n    +alphaTest: 0.1,\n  });\n\n  const cube = new THREE.Mesh(geometry, material);\n  scene.add(cube);\n\n  ...\n</pre>\n<p>And running that you should quickly see the issue</p>\n<p></p><div translate=\"no\" class=\"threejs_example_container notranslate\">\n  <div></div>\n  <a class=\"threejs_center\" href=\"/manual/examples/picking-raycaster-transparency.html\" target=\"_blank\">click here to open in a separate window</a>\n</div>\n\n<p></p>\n<p>Try to pick something through a box and you can't</p>\n<div class=\"threejs_center\"><img src=\"../resources/images/picking-transparent-issue.jpg\" style=\"width: 635px;\"></div>\n\n<p>This is because JavaScript can't easily look into the textures and materials and figure out if part of your object is really transparent or not.</p>\n<p>A solution all of these issues is to use GPU based picking. Unfortunately while it is conceptually simple it is more complicated to use than the ray casting method above.</p>\n<p>To do GPU picking we render each object in a unique color offscreen. We then look up the color of the pixel corresponding to the mouse position. The color tells us which object was picked.</p>\n<p>This can solve issue 2 and 3 above. As for issue 1, speed, it really depends. Every object has to be drawn twice. Once to draw it for viewing and again to draw it for picking. It's possible with fancier solutions maybe both of those could be done at the same time but we're not going to try that.</p>\n<p>One thing we can do though is since we're only going to be reading one pixel we can just setup the camera so only that one pixel is drawn. We can do this using <a href=\"/docs/#api/en/cameras/PerspectiveCamera.setViewOffset\"><code class=\"notranslate\" translate=\"no\">PerspectiveCamera.setViewOffset</code></a> which lets us tell THREE.js to compute a camera that just renders a smaller part of a larger rectangle. This should save some time.</p>\n<p>To do this type of picking in THREE.js at the moment requires we create 2 scenes. One we will fill with our normal meshes. The other we'll fill with meshes that use our picking material.</p>\n<p>So, first create a second scene and make sure it clears to black.</p>\n<pre class=\"prettyprint showlinemods notranslate lang-js\" translate=\"no\">const scene = new THREE.Scene();\nscene.background = new THREE.Color('white');\nconst pickingScene = new THREE.Scene();\npickingScene.background = new THREE.Color(0);\n</pre>\n<p>Then, for each cube we place in the main scene we make a corresponding \"picking cube\" at the same position as the original cube, put it in the <code class=\"notranslate\" translate=\"no\">pickingScene</code>, and set its material to something that will draw the object's id as its color. Also we keep a map of ids to objects so when we look up an id later we can map it back to its corresponding object.</p>\n<pre class=\"prettyprint showlinemods notranslate lang-js\" translate=\"no\">const idToObject = {};\n+const numObjects = 100;\nfor (let i = 0; i &lt; numObjects; ++i) {\n+  const id = i + 1;\n  const material = new THREE.MeshPhongMaterial({\n    color: randomColor(),\n    map: texture,\n    transparent: true,\n    side: THREE.DoubleSide,\n    alphaTest: 0.1,\n  });\n\n  const cube = new THREE.Mesh(geometry, material);\n  scene.add(cube);\n+  idToObject[id] = cube;\n\n  cube.position.set(rand(-20, 20), rand(-20, 20), rand(-20, 20));\n  cube.rotation.set(rand(Math.PI), rand(Math.PI), 0);\n  cube.scale.set(rand(3, 6), rand(3, 6), rand(3, 6));\n\n+  const pickingMaterial = new THREE.MeshPhongMaterial({\n+    emissive: new THREE.Color().setHex(id, THREE.NoColorSpace),\n+    color: new THREE.Color(0, 0, 0),\n+    specular: new THREE.Color(0, 0, 0),\n+    map: texture,\n+    transparent: true,\n+    side: THREE.DoubleSide,\n+    alphaTest: 0.5,\n+    blending: THREE.NoBlending,\n+  });\n+  const pickingCube = new THREE.Mesh(geometry, pickingMaterial);\n+  pickingScene.add(pickingCube);\n+  pickingCube.position.copy(cube.position);\n+  pickingCube.rotation.copy(cube.rotation);\n+  pickingCube.scale.copy(cube.scale);\n}\n</pre>\n<p>Note that we are abusing the <a href=\"/docs/#api/en/materials/MeshPhongMaterial\"><code class=\"notranslate\" translate=\"no\">MeshPhongMaterial</code></a> here. By setting its <code class=\"notranslate\" translate=\"no\">emissive</code> to our id and the <code class=\"notranslate\" translate=\"no\">color</code> and <code class=\"notranslate\" translate=\"no\">specular</code> to 0 that will end up rendering the id only where the texture's alpha is greater than <code class=\"notranslate\" translate=\"no\">alphaTest</code>. We also need to set <code class=\"notranslate\" translate=\"no\">blending</code> to <code class=\"notranslate\" translate=\"no\">NoBlending</code> so that the id is not multiplied by alpha.</p>\n<p>Note that abusing the <a href=\"/docs/#api/en/materials/MeshPhongMaterial\"><code class=\"notranslate\" translate=\"no\">MeshPhongMaterial</code></a> might not be the best solution as it will still calculate all our lights when drawing the picking scene even though we don't need those calculations. A more optimized solution would make a custom shader that just writes the id where the texture's alpha is greater than <code class=\"notranslate\" translate=\"no\">alphaTest</code>.</p>\n<p>Because we're picking from pixels instead of ray casting we can change the code that sets the pick position to just use pixels.</p>\n<pre class=\"prettyprint showlinemods notranslate lang-js\" translate=\"no\">function setPickPosition(event) {\n  const pos = getCanvasRelativePosition(event);\n-  pickPosition.x = (pos.x / canvas.clientWidth ) *  2 - 1;\n-  pickPosition.y = (pos.y / canvas.clientHeight) * -2 + 1;  // note we flip Y\n+  pickPosition.x = pos.x;\n+  pickPosition.y = pos.y;\n}\n</pre>\n<p>Then let's change the <code class=\"notranslate\" translate=\"no\">PickHelper</code> into a <code class=\"notranslate\" translate=\"no\">GPUPickHelper</code>. It will use a <a href=\"/docs/#api/en/renderers/WebGLRenderTarget\"><code class=\"notranslate\" translate=\"no\">WebGLRenderTarget</code></a> like we covered the <a href=\"rendertargets.html\">article on render targets</a>. Our render target here is only a single pixel in size, 1x1. </p>\n<pre class=\"prettyprint showlinemods notranslate lang-js\" translate=\"no\">-class PickHelper {\n+class GPUPickHelper {\n  constructor() {\n-    this.raycaster = new THREE.Raycaster();\n+    // create a 1x1 pixel render target\n+    this.pickingTexture = new THREE.WebGLRenderTarget(1, 1);\n+    this.pixelBuffer = new Uint8Array(4);\n    this.pickedObject = null;\n    this.pickedObjectSavedColor = 0;\n  }\n  pick(cssPosition, scene, camera, time) {\n+    const {pickingTexture, pixelBuffer} = this;\n\n    // restore the color if there is a picked object\n    if (this.pickedObject) {\n      this.pickedObject.material.emissive.setHex(this.pickedObjectSavedColor);\n      this.pickedObject = undefined;\n    }\n\n+    // set the view offset to represent just a single pixel under the mouse\n+    const pixelRatio = renderer.getPixelRatio();\n+    camera.setViewOffset(\n+        renderer.getContext().drawingBufferWidth,   // full width\n+        renderer.getContext().drawingBufferHeight,  // full top\n+        cssPosition.x * pixelRatio | 0,             // rect x\n+        cssPosition.y * pixelRatio | 0,             // rect y\n+        1,                                          // rect width\n+        1,                                          // rect height\n+    );\n+    // render the scene\n+    renderer.setRenderTarget(pickingTexture)\n+    renderer.render(scene, camera);\n+    renderer.setRenderTarget(null);\n+\n+    // clear the view offset so rendering returns to normal\n+    camera.clearViewOffset();\n+    //read the pixel\n+    renderer.readRenderTargetPixels(\n+        pickingTexture,\n+        0,   // x\n+        0,   // y\n+        1,   // width\n+        1,   // height\n+        pixelBuffer);\n+\n+    const id =\n+        (pixelBuffer[0] &lt;&lt; 16) |\n+        (pixelBuffer[1] &lt;&lt;  8) |\n+        (pixelBuffer[2]      );\n\n-    // cast a ray through the frustum\n-    this.raycaster.setFromCamera(normalizedPosition, camera);\n-    // get the list of objects the ray intersected\n-    const intersectedObjects = this.raycaster.intersectObjects(scene.children);\n-    if (intersectedObjects.length) {\n-      // pick the first object. It's the closest one\n-      this.pickedObject = intersectedObjects[0].object;\n\n+    const intersectedObject = idToObject[id];\n+    if (intersectedObject) {\n+      // pick the first object. It's the closest one\n+      this.pickedObject = intersectedObject;\n      // save its color\n      this.pickedObjectSavedColor = this.pickedObject.material.emissive.getHex();\n      // set its emissive color to flashing red/yellow\n      this.pickedObject.material.emissive.setHex((time * 8) % 2 &gt; 1 ? 0xFFFF00 : 0xFF0000);\n    }\n  }\n}\n</pre>\n<p>Then we just need to use it</p>\n<pre class=\"prettyprint showlinemods notranslate lang-js\" translate=\"no\">-const pickHelper = new PickHelper();\n+const pickHelper = new GPUPickHelper();\n</pre>\n<p>and pass it the <code class=\"notranslate\" translate=\"no\">pickScene</code> instead of the <code class=\"notranslate\" translate=\"no\">scene</code>.</p>\n<pre class=\"prettyprint showlinemods notranslate lang-js\" translate=\"no\">-  pickHelper.pick(pickPosition, scene, camera, time);\n+  pickHelper.pick(pickPosition, pickScene, camera, time);\n</pre>\n<p>And now it should let you pick through the transparent parts</p>\n<p></p><div translate=\"no\" class=\"threejs_example_container notranslate\">\n  <div></div>\n  <a class=\"threejs_center\" href=\"/manual/examples/picking-gpu.html\" target=\"_blank\">click here to open in a separate window</a>\n</div>\n\n<p></p>\n<p>I hope that gives some idea of how to implement picking. In a future article maybe we can cover how to manipulate objects with the mouse.</p>\n\n        </div>\n      </div>\n    </div>",
    "success": true,
    "error": "",
    "crawled_at": 1786376809.300204,
    "is_shell": false
  },
  {
    "url": "https://threejs.org/manual/en/post-processing.html",
    "title": "Post Processing",
    "content": "<div class=\"container\">\n      <div class=\"lesson-title\">\n        <h1>Post Processing</h1>\n      </div>\n      <div class=\"lesson\">\n        <div class=\"lesson-main\">\n          <p><em>Post processing</em> generally refers to applying some kind of effect or filter to\na 2D image. In the case of THREE.js we have a scene with a bunch of meshes in\nit. We render that scene into a 2D image. Normally that image is rendered\ndirectly into the canvas and displayed in the browser but instead we can <a href=\"rendertargets.html\">render\nit to a render target</a> and then apply some <em>post\nprocessing</em> effects to the result before drawing it to the canvas. It's called\npost processing because it happens after (post) the main scene processing.</p>\n<p>Examples of post processing are Instagram like filters,\nPhotoshop filters, etc...</p>\n<p>THREE.js has some example classes to help setup a post processing pipeline. The\nway it works is you create an <code class=\"notranslate\" translate=\"no\">EffectComposer</code> and to it you add multiple <code class=\"notranslate\" translate=\"no\">Pass</code>\nobjects. You then call <code class=\"notranslate\" translate=\"no\">EffectComposer.render</code> and it renders your scene to a\n<a href=\"rendertargets.html\">render target</a> and then applies each <code class=\"notranslate\" translate=\"no\">Pass</code>.</p>\n<p>Each <code class=\"notranslate\" translate=\"no\">Pass</code> can be some post processing effect like adding a vignette, blurring,\napplying a bloom, applying film grain, adjusting the hue, saturation, contrast,\netc... and finally rendering the result to the canvas.</p>\n<p>It's a little bit important to understand how <code class=\"notranslate\" translate=\"no\">EffectComposer</code> functions. It\ncreates two <a href=\"rendertargets.html\">render targets</a>. Let's call them\n<strong>rtA</strong> and <strong>rtB</strong>.</p>\n<p>Then, you call <code class=\"notranslate\" translate=\"no\">EffectComposer.addPass</code> to add each pass in the order you want\nto apply them. The passes are then applied <em>something like</em> this.</p>\n<div class=\"threejs_center\"><img src=\"../resources/images/threejs-postprocessing.svg\" style=\"width: 600px\"></div>\n\n<p>First the scene you passed into <code class=\"notranslate\" translate=\"no\">RenderPass</code> is rendered to <strong>rtA</strong>, then\n<strong>rtA</strong> is passed to the next pass, whatever it is. That pass uses <strong>rtA</strong> as\ninput to do whatever it does and writes the results to <strong>rtB</strong>. <strong>rtB</strong> is then\npassed to the next pass which uses <strong>rtB</strong> as input and writes back to <strong>rtA</strong>.\nThis continues through all the passes. </p>\n<p>Each <code class=\"notranslate\" translate=\"no\">Pass</code> has 4 basic options</p>\n<h2 id=\"-enabled-\"><code class=\"notranslate\" translate=\"no\">enabled</code></h2>\n<p>Whether or not to use this pass</p>\n<h2 id=\"-needsswap-\"><code class=\"notranslate\" translate=\"no\">needsSwap</code></h2>\n<p>Whether or not to swap <code class=\"notranslate\" translate=\"no\">rtA</code> and <code class=\"notranslate\" translate=\"no\">rtB</code> after finishing this pass</p>\n<h2 id=\"-clear-\"><code class=\"notranslate\" translate=\"no\">clear</code></h2>\n<p>Whether or not to clear before rendering this pass</p>\n<h2 id=\"-rendertoscreen-\"><code class=\"notranslate\" translate=\"no\">renderToScreen</code></h2>\n<p>Whether or not to render to the canvas instead the current destination render\ntarget. In most use cases you do not set this flag explicitly since the last pass in the pass chain is automatically rendered to screen.</p>\n<p>Let's put together a basic example. We'll start with the example from <a href=\"responsive.html\">the\narticle on responsiveness</a>.</p>\n<p>To that first we create an <code class=\"notranslate\" translate=\"no\">EffectComposer</code>.</p>\n<pre class=\"prettyprint showlinemods notranslate lang-js\" translate=\"no\">const composer = new EffectComposer(renderer);\n</pre>\n<p>Then as the first pass we add a <code class=\"notranslate\" translate=\"no\">RenderPass</code> that will render our scene with our\ncamera into the first render target.</p>\n<pre class=\"prettyprint showlinemods notranslate lang-js\" translate=\"no\">composer.addPass(new RenderPass(scene, camera));\n</pre>\n<p>Next we add a <code class=\"notranslate\" translate=\"no\">BloomPass</code>. A <code class=\"notranslate\" translate=\"no\">BloomPass</code> renders its input to a generally\nsmaller render target and blurs the result. It then adds that blurred result on\ntop of the original input. This makes the scene <em>bloom</em></p>\n<pre class=\"prettyprint showlinemods notranslate lang-js\" translate=\"no\">const bloomPass = new BloomPass(\n    1,    // strength\n    25,   // kernel size\n    4,    // sigma ?\n    256,  // blur render target resolution\n);\ncomposer.addPass(bloomPass);\n</pre>\n<p>Next we had a <code class=\"notranslate\" translate=\"no\">FilmPass</code> that draws noise and scanlines on top of its input.</p>\n<pre class=\"prettyprint showlinemods notranslate lang-js\" translate=\"no\">const filmPass = new FilmPass(\n    0.5,   // intensity\n    false,  // grayscale\n);\ncomposer.addPass(filmPass);\n</pre>\n<p>Finally we had a <code class=\"notranslate\" translate=\"no\">OutputPass</code> which performs color space conversion to sRGB and optional tone mapping.\nThis pass is usually the last pass of the pass chain.\n</p>\n<pre class=\"prettyprint showlinemods notranslate lang-js\" translate=\"no\">const outputPass = new OutputPass();\ncomposer.addPass(outputPass);\n</pre>\n<p>To use these classes we need to import a bunch of scripts.</p>\n<pre class=\"prettyprint showlinemods notranslate lang-js\" translate=\"no\">import {EffectComposer} from 'three/addons/postprocessing/EffectComposer.js';\nimport {RenderPass} from 'three/addons/postprocessing/RenderPass.js';\nimport {BloomPass} from 'three/addons/postprocessing/BloomPass.js';\nimport {FilmPass} from 'three/addons/postprocessing/FilmPass.js';\nimport {OutputPass} from 'three/addons/postprocessing/OutputPass.js';\n</pre>\n<p>For pretty much any post processing <code class=\"notranslate\" translate=\"no\">EffectComposer.js</code>, <code class=\"notranslate\" translate=\"no\">RenderPass.js</code> and <code class=\"notranslate\" translate=\"no\">OutputPass.js</code>\nare required.</p>\n<p>The last things we need to do are to use <code class=\"notranslate\" translate=\"no\">EffectComposer.render</code> instead of\n<a href=\"/docs/#api/en/renderers/WebGLRenderer.render\"><code class=\"notranslate\" translate=\"no\">WebGLRenderer.render</code></a> <em>and</em> to tell the <code class=\"notranslate\" translate=\"no\">EffectComposer</code> to match the size of\nthe canvas.</p>\n<pre class=\"prettyprint showlinemods notranslate lang-js\" translate=\"no\">-function render(now) {\n-  time *= 0.001;\n+let then = 0;\n+function render(now) {\n+  now *= 0.001;  // convert to seconds\n+  const deltaTime = now - then;\n+  then = now;\n\n  if (resizeRendererToDisplaySize(renderer)) {\n    const canvas = renderer.domElement;\n    camera.aspect = canvas.clientWidth / canvas.clientHeight;\n    camera.updateProjectionMatrix();\n+    composer.setSize(canvas.width, canvas.height);\n  }\n\n  cubes.forEach((cube, ndx) =&gt; {\n    const speed = 1 + ndx * .1;\n-    const rot = time * speed;\n+    const rot = now * speed;\n    cube.rotation.x = rot;\n    cube.rotation.y = rot;\n  });\n\n-  renderer.render(scene, camera);\n+  composer.render(deltaTime);\n\n  requestAnimationFrame(render);\n}\n</pre>\n<p><code class=\"notranslate\" translate=\"no\">EffectComposer.render</code> takes a <code class=\"notranslate\" translate=\"no\">deltaTime</code> which is the time in seconds since\nthe last frame was rendered. It passes this to the various effects in case any\nof them are animated. In this case the <code class=\"notranslate\" translate=\"no\">FilmPass</code> is animated.</p>\n<p></p><div translate=\"no\" class=\"threejs_example_container notranslate\">\n  <div></div>\n  <a class=\"threejs_center\" href=\"/manual/examples/postprocessing.html\" target=\"_blank\">click here to open in a separate window</a>\n</div>\n\n<p></p>\n<p>To change effect parameters at runtime usually requires setting uniform values.\nLet's add a gui to adjust some of the parameters. Figuring out which values you\ncan easily adjust and how to adjust them requires digging through the code for\nthat effect.</p>\n<p>Looking inside\n<a href=\"https://github.com/mrdoob/three.js/blob/master/examples/jsm/postprocessing/BloomPass.js\"><code class=\"notranslate\" translate=\"no\">BloomPass.js</code></a>\nI found this line:</p>\n<pre class=\"prettyprint showlinemods notranslate lang-js\" translate=\"no\">this.combineUniforms[ 'strength' ].value = strength;\n</pre>\n<p>So we can set the strength by setting</p>\n<pre class=\"prettyprint showlinemods notranslate lang-js\" translate=\"no\">bloomPass.combineUniforms.strength.value = someValue;\n</pre>\n<p>Similarly looking in\n<a href=\"https://github.com/mrdoob/three.js/blob/master/examples/jsm/postprocessing/FilmPass.js\"><code class=\"notranslate\" translate=\"no\">FilmPass.js</code></a>\nI found these lines:</p>\n<pre class=\"prettyprint showlinemods notranslate lang-js\" translate=\"no\">this.uniforms.intensity.value = intensity;\nthis.uniforms.grayscale.value = grayscale;\n</pre>\n<p>So which makes it pretty clear how to set them.</p>\n<p>Let's make a quick GUI to set those values</p>\n<pre class=\"prettyprint showlinemods notranslate lang-js\" translate=\"no\">import {GUI} from 'three/addons/libs/lil-gui.module.min.js';\n</pre>\n<p>and</p>\n<pre class=\"prettyprint showlinemods notranslate lang-js\" translate=\"no\">const gui = new GUI();\n{\n  const folder = gui.addFolder('BloomPass');\n  folder.add(bloomPass.combineUniforms.strength, 'value', 0, 2).name('strength');\n  folder.open();\n}\n{\n  const folder = gui.addFolder('FilmPass');\n  folder.add(filmPass.uniforms.grayscale, 'value').name('grayscale');\n  folder.add(filmPass.uniforms.intensity, 'value', 0, 1).name('intensity');\n  folder.open();\n}\n</pre>\n<p>and now we can adjust those settings</p>\n<p></p><div translate=\"no\" class=\"threejs_example_container notranslate\">\n  <div></div>\n  <a class=\"threejs_center\" href=\"/manual/examples/postprocessing-gui.html\" target=\"_blank\">click here to open in a separate window</a>\n</div>\n\n<p></p>\n<p>That was a small step to making our own effect.</p>\n<p>Post processing effects use shaders. Shaders are written in a language called\n<a href=\"https://www.khronos.org/files/opengles_shading_language.pdf\">GLSL (Graphics Library Shading Language)</a>. Going\nover the entire language is way too large a topic for these articles. A few\nresources to get start from would be maybe <a href=\"https://webglfundamentals.org/webgl/lessons/webgl-shaders-and-glsl.html\">this article</a>\nand maybe <a href=\"https://thebookofshaders.com/\">the Book of Shaders</a>.</p>\n<p>I think an example to get you started would be helpful though so let's make a\nsimple GLSL post processing shader. We'll make one that lets us multiply the\nimage by a color.</p>\n<p>For post processing THREE.js provides a useful helper called the <code class=\"notranslate\" translate=\"no\">ShaderPass</code>.\nIt takes an object with info defining a vertex shader, a fragment shader, and\nthe default inputs. It will handling setting up which texture to read from to\nget the previous pass's results and where to render to, either one of the\n<code class=\"notranslate\" translate=\"no\">EffectComposer</code>s render target or the canvas.</p>\n<p>Here's a simple post processing shader that multiplies the previous pass's\nresult by a color. </p>\n<pre class=\"prettyprint showlinemods notranslate lang-js\" translate=\"no\">const colorShader = {\n  uniforms: {\n    tDiffuse: { value: null },\n    color:    { value: new THREE.Color(0x88CCFF) },\n  },\n  vertexShader: <code class=\"notranslate\" translate=\"no\">\n    varying vec2 vUv;\n    void main() {\n      vUv = uv;\n      gl_Position = projectionMatrix * modelViewMatrix * vec4(position, 1);\n    }\n  </code>,\n  fragmentShader: <code class=\"notranslate\" translate=\"no\">\n    varying vec2 vUv;\n    uniform sampler2D tDiffuse;\n    uniform vec3 color;\n    void main() {\n      vec4 previousPassColor = texture2D(tDiffuse, vUv);\n      gl_FragColor = vec4(\n          previousPassColor.rgb * color,\n          previousPassColor.a);\n    }\n  </code>,\n};\n</pre>\n<p>Above <code class=\"notranslate\" translate=\"no\">tDiffuse</code> is the name that <code class=\"notranslate\" translate=\"no\">ShaderPass</code> uses to pass in the previous\npass's result texture so we pretty much always need that. We then declare\n<code class=\"notranslate\" translate=\"no\">color</code> as a THREE.js <a href=\"/docs/#api/en/math/Color\"><code class=\"notranslate\" translate=\"no\">Color</code></a>.</p>\n<p>Next we need a vertex shader. For post processing the vertex shader shown here\nis pretty much standard and rarely needs to be changed. Without going into too\nmany details (see articles linked above) the variables <code class=\"notranslate\" translate=\"no\">uv</code>, <code class=\"notranslate\" translate=\"no\">projectionMatrix</code>,\n<code class=\"notranslate\" translate=\"no\">modelViewMatrix</code> and <code class=\"notranslate\" translate=\"no\">position</code> are all magically added by THREE.js.</p>\n<p>Finally we create a fragment shader. In it we get a pixel color from the\nprevious pass with this line</p>\n<pre class=\"prettyprint showlinemods notranslate lang-glsl\" translate=\"no\">vec4 previousPassColor = texture2D(tDiffuse, vUv);\n</pre>\n<p>we multiply it by our color and set <code class=\"notranslate\" translate=\"no\">gl_FragColor</code> to the result</p>\n<pre class=\"prettyprint showlinemods notranslate lang-glsl\" translate=\"no\">gl_FragColor = vec4(\n    previousPassColor.rgb * color,\n    previousPassColor.a);\n</pre>\n<p>Adding some simple GUI to set the 3 values of the color</p>\n<pre class=\"prettyprint showlinemods notranslate lang-js\" translate=\"no\">const gui = new GUI();\ngui.add(colorPass.uniforms.color.value, 'r', 0, 4).name('red');\ngui.add(colorPass.uniforms.color.value, 'g', 0, 4).name('green');\ngui.add(colorPass.uniforms.color.value, 'b', 0, 4).name('blue');\n</pre>\n<p>Gives us a simple postprocessing effect that multiplies by a color.</p>\n<p></p><div translate=\"no\" class=\"threejs_example_container notranslate\">\n  <div></div>\n  <a class=\"threejs_center\" href=\"/manual/examples/postprocessing-custom.html\" target=\"_blank\">click here to open in a separate window</a>\n</div>\n\n<p></p>\n<p>As mentioned about all the details of how to write GLSL and custom shaders is\ntoo much for these articles. If you really want to know how WebGL itself works\nthen check out <a href=\"https://webglfundamentals.org\">these articles</a>. Another great\nresources is just to\n<a href=\"https://github.com/mrdoob/three.js/tree/master/examples/jsm/shaders\">read through the existing post processing shaders in the THREE.js repo</a>. Some\nare more complicated than others but if you start with the smaller ones you can\nhopefully get an idea of how they work.</p>\n<p>Most of the post processing effects in the THREE.js repo are unfortunately\nundocumented so to use them you'll have to <a href=\"https://github.com/mrdoob/three.js/tree/master/examples\">read through the examples</a> or\n<a href=\"https://github.com/mrdoob/three.js/tree/master/examples/jsm/postprocessing\">the code for the effects themselves</a>.\nHopefully these simple example and the article on\n<a href=\"rendertargets.html\">render targets</a> provide enough context to get started.</p>\n\n        </div>\n      </div>\n    </div>",
    "success": true,
    "error": "",
    "crawled_at": 1786376809.300204,
    "is_shell": false
  },
  {
    "url": "https://threejs.org/manual/en/shadertoy.html",
    "title": "Three.js and Shadertoy",
    "content": "<div class=\"container\">\n      <div class=\"lesson-title\">\n        <h1>Three.js and Shadertoy</h1>\n      </div>\n      <div class=\"lesson\">\n        <div class=\"lesson-main\">\n          <p><a href=\"https://shadertoy.com\">Shadertoy</a> is a famous website hosting amazing shader\nexperiments. People often ask how they can use those shaders with Three.js.</p>\n<p>It's important to recognize it's called Shader<strong>TOY</strong> for a reason. In general\nshadertoy shaders are not about best practices. Rather they are a fun challenge\nsimilar to say <a href=\"https://dwitter.net\">dwitter</a> (write code in 140 characters) or\n<a href=\"https://js13kgames.com\">js13kGames</a> (make a game in 13k or less).</p>\n<p>In the case of Shadertoy the puzzle is, <em>write a function that for a given pixel\nlocation outputs a color that draws something interesting</em>. It's a fun challenge\nand many of the result are amazing. But, it is not best practice.</p>\n<p>Compare <a href=\"https://www.shadertoy.com/view/XtsSWs\">this amazing shadertoy shader that draws an entire city</a></p>\n<div class=\"threejs_center\"><img src=\"../resources/images/shadertoy-skyline.png\"></div>\n\n<p>Fullscreen on my GPU it runs at about 5 frames a second. Contrast that to\n<a href=\"https://store.steampowered.com/app/255710/Cities_Skylines/\">a game like Cities: Skylines</a></p>\n<div class=\"threejs_center\"><img src=\"../resources/images/cities-skylines.jpg\" style=\"width: 600px;\"></div>\n\n<p>This game runs 30-60 frames a second on the same machine because it uses more\ntraditional techniques, drawing buildings made from triangles with textures on\nthem, etc...</p>\n<p>Still, let's go over using a Shadertoy shader with three.js.</p>\n<p>This is the default shadertoy shader if you <a href=\"https://www.shadertoy.com/new\">pick \"New\" on shadertoy.com</a>, at least as of January 2019.</p>\n<pre class=\"prettyprint showlinemods notranslate lang-glsl\" translate=\"no\">// By iq: https://www.shadertoy.com/user/iq\n// license: Creative Commons Attribution-NonCommercial-ShareAlike 3.0 Unported License.\nvoid mainImage( out vec4 fragColor, in vec2 fragCoord )\n{\n    // Normalized pixel coordinates (from 0 to 1)\n    vec2 uv = fragCoord/iResolution.xy;\n\n    // Time varying pixel color\n    vec3 col = 0.5 + 0.5*cos(iTime+uv.xyx+vec3(0,2,4));\n\n    // Output to screen\n    fragColor = vec4(col,1.0);\n}\n</pre>\n<p>One thing important to understand about shaders is they are written in a\nlanguage called GLSL (Graphics Library Shading Language) designed for 3D math\nwhich includes special types. Above we see <code class=\"notranslate\" translate=\"no\">vec4</code>, <code class=\"notranslate\" translate=\"no\">vec2</code>, <code class=\"notranslate\" translate=\"no\">vec3</code> as 3 such\nspecial types. A <code class=\"notranslate\" translate=\"no\">vec2</code> has 2 values, a <code class=\"notranslate\" translate=\"no\">vec3</code> 3, a <code class=\"notranslate\" translate=\"no\">vec4</code> 4 values. They can be\naddressed in a bunch of ways. The most common ways are with <code class=\"notranslate\" translate=\"no\">x</code>, <code class=\"notranslate\" translate=\"no\">y</code>, <code class=\"notranslate\" translate=\"no\">z</code>, and\n<code class=\"notranslate\" translate=\"no\">w</code> as in</p>\n<pre class=\"prettyprint showlinemods notranslate lang-glsl\" translate=\"no\">vec4 v1 = vec4(1.0, 2.0, 3.0, 4.0);\nfloat v2 = v1.x + v1.y;  // adds 1.0 + 2.0\n</pre>\n<p>Unlike JavaScript, GLSL is more like C/C++ where variables have to have their\ntype declared so instead of <code class=\"notranslate\" translate=\"no\">var v = 1.2;</code> it's <code class=\"notranslate\" translate=\"no\">float v = 1.2;</code> declaring <code class=\"notranslate\" translate=\"no\">v</code>\nto be a floating point number.</p>\n<p>Explaining GLSL in detail is more than we can do in this article. For a quick\noverview see <a href=\"https://webglfundamentals.org/webgl/lessons/webgl-shaders-and-glsl.html\">this article</a>\nand maybe follow that up with <a href=\"https://thebookofshaders.com/\">this series</a>.</p>\n<p>It should be noted that, at least as of January 2019,\n<a href=\"https://shadertoy.com\">shadertoy.com</a> only concerns itself with <em>fragment\nshaders</em>. A fragment shader's responsibility is, given a pixel location output\na color for that pixel.</p>\n<p>Looking at the function above we can see the shader has an <code class=\"notranslate\" translate=\"no\">out</code> parameter\ncalled <code class=\"notranslate\" translate=\"no\">fragColor</code>. <code class=\"notranslate\" translate=\"no\">out</code> stands for <code class=\"notranslate\" translate=\"no\">output</code>. It's a parameter the function is\nexpected to provide a value for. We need to set this to some color.</p>\n<p>It also has an <code class=\"notranslate\" translate=\"no\">in</code> (for input) parameter called <code class=\"notranslate\" translate=\"no\">fragCoord</code>. This is the pixel\ncoordinate that is about to be drawn. We can use that coordinate to decide on a\ncolor. If the canvas we're drawing to is 400x300 pixels then the function will\nbe called 400x300 times or 120,000 times. Each time <code class=\"notranslate\" translate=\"no\">fragCoord</code> will be a\ndifferent pixel coordinate.</p>\n<p>There are 2 more variables being used that are not defined in the code. One is\n<code class=\"notranslate\" translate=\"no\">iResolution</code>. This is set to the resolution of the canvas. If the canvas is\n400x300 then <code class=\"notranslate\" translate=\"no\">iResolution</code> would be 400,300 so as the pixel coordinates change\nthat makes <code class=\"notranslate\" translate=\"no\">uv</code> go from 0.0 to 1.0 across and up the texture. Working with\n<em>normalized</em> values often makes things easier and so the majority of shadertoy\nshaders start with something like this.</p>\n<p>The other undefined variable in the shader is <code class=\"notranslate\" translate=\"no\">iTime</code>. This is the time since\nthe page loaded in seconds.</p>\n<p>In shader jargon these global variables are called <em>uniform</em> variables. They are\ncalled <em>uniform</em> because they don't change, they stay uniform from one iteration\nof the shader to the next. It's important to note all of them are specific to\nshadertoy. They not <em>official</em> GLSL variables. They are variables the makers of\nshadertoy made up.</p>\n<p>The <a href=\"https://www.shadertoy.com/howto\">Shadertoy docs define several more</a>. For\nnow let's write something that handles the two being used in the shader above.</p>\n<p>The first thing to do is let's make a single plane that fills the canvas. If you\nhaven't read it yet we did this in <a href=\"backgrounds.html\">the article on backgrounds</a>\nso let's grab that example but remove the cubes. It's pretty short so here's the\nentire thing</p>\n<pre class=\"prettyprint showlinemods notranslate lang-js\" translate=\"no\">function main() {\n  const canvas = document.querySelector('#c');\n  const renderer = new THREE.WebGLRenderer({antialias: true, canvas});\n  renderer.autoClearColor = false;\n\n  const camera = new THREE.OrthographicCamera(\n    -1, // left\n     1, // right\n     1, // top\n    -1, // bottom\n    -1, // near,\n     1, // far\n  );\n  const scene = new THREE.Scene();\n  const plane = new THREE.PlaneGeometry(2, 2);\n  const material = new THREE.MeshBasicMaterial({\n      color: 'red',\n  });\n  scene.add(new THREE.Mesh(plane, material));\n\n  function resizeRendererToDisplaySize(renderer) {\n    const canvas = renderer.domElement;\n    const width = canvas.clientWidth;\n    const height = canvas.clientHeight;\n    const needResize = canvas.width !== width || canvas.height !== height;\n    if (needResize) {\n      renderer.setSize(width, height, false);\n    }\n    return needResize;\n  }\n\n  function render() {\n    resizeRendererToDisplaySize(renderer);\n\n    renderer.render(scene, camera);\n\n    requestAnimationFrame(render);\n  }\n\n  requestAnimationFrame(render);\n}\n\nmain();\n</pre>\n<p>As <a href=\"backgrounds.html\">explained in the backgrounds article</a> an\n<a href=\"/docs/#api/en/cameras/OrthographicCamera\"><code class=\"notranslate\" translate=\"no\">OrthographicCamera</code></a> with these parameters and a 2 unit plane will fill the\ncanvas. For now all we'll get is a red canvas as our plane is using a red\n<a href=\"/docs/#api/en/materials/MeshBasicMaterial\"><code class=\"notranslate\" translate=\"no\">MeshBasicMaterial</code></a>.</p>\n<p></p><div translate=\"no\" class=\"threejs_example_container notranslate\">\n  <div></div>\n  <a class=\"threejs_center\" href=\"/manual/examples/shadertoy-prep.html\" target=\"_blank\">click here to open in a separate window</a>\n</div>\n\n<p></p>\n<p>Now that we have something working let's add the shadertoy shader. </p>\n<pre class=\"prettyprint showlinemods notranslate lang-js\" translate=\"no\">const fragmentShader = <code class=\"notranslate\" translate=\"no\">\n#include &lt;common&gt;\n\nuniform vec3 iResolution;\nuniform float iTime;\n\n// By iq: https://www.shadertoy.com/user/iq\n// license: Creative Commons Attribution-NonCommercial-ShareAlike 3.0 Unported License.\nvoid mainImage( out vec4 fragColor, in vec2 fragCoord )\n{\n    // Normalized pixel coordinates (from 0 to 1)\n    vec2 uv = fragCoord/iResolution.xy;\n\n    // Time varying pixel color\n    vec3 col = 0.5 + 0.5*cos(iTime+uv.xyx+vec3(0,2,4));\n\n    // Output to screen\n    fragColor = vec4(col,1.0);\n}\n\nvoid main() {\n  mainImage(gl_FragColor, gl_FragCoord.xy);\n}\n</code>;\n</pre>\n<p>Above we declared the 2 uniform variables we talked about. Then we inserted the\nshader GLSL code from shadertoy. Finally we called <code class=\"notranslate\" translate=\"no\">mainImage</code> passing it\n<code class=\"notranslate\" translate=\"no\">gl_FragColor</code> and <code class=\"notranslate\" translate=\"no\">gl_FragCoord.xy</code>.  <code class=\"notranslate\" translate=\"no\">gl_FragColor</code> is an official WebGL\nglobal variable the shader is responsible for setting to whatever color it wants\nthe current pixel to be. <code class=\"notranslate\" translate=\"no\">gl_FragCoord</code> is another official WebGL global\nvariable that tells us the coordinate of the pixel we're currently choosing a\ncolor for.</p>\n<p>We then need to setup three.js uniforms so we can supply values to the shader.</p>\n<pre class=\"prettyprint showlinemods notranslate lang-js\" translate=\"no\">const uniforms = {\n  iTime: { value: 0 },\n  iResolution:  { value: new THREE.Vector3() },\n};\n</pre>\n<p>Each uniform in THREE.js has <code class=\"notranslate\" translate=\"no\">value</code> parameter. That value has to match the type\nof the uniform.</p>\n<p>Then we pass both the fragment shader and uniforms to a <a href=\"/docs/#api/en/materials/ShaderMaterial\"><code class=\"notranslate\" translate=\"no\">ShaderMaterial</code></a>.</p>\n<pre class=\"prettyprint showlinemods notranslate lang-js\" translate=\"no\">-const material = new THREE.MeshBasicMaterial({\n-    color: 'red',\n-});\n+const material = new THREE.ShaderMaterial({\n+  fragmentShader,\n+  uniforms,\n+});\n</pre>\n<p>and before rendering we need to set the values of the uniforms</p>\n<pre class=\"prettyprint showlinemods notranslate lang-js\" translate=\"no\">-function render() {\n+function render(time) {\n+  time *= 0.001;  // convert to seconds\n\n  resizeRendererToDisplaySize(renderer);\n\n+  const canvas = renderer.domElement;\n+  uniforms.iResolution.value.set(canvas.width, canvas.height, 1);\n+  uniforms.iTime.value = time;\n\n  renderer.render(scene, camera);\n\n  requestAnimationFrame(render);\n}\n</pre>\n<blockquote>\n<p>Note: I have no idea why <code class=\"notranslate\" translate=\"no\">iResolution</code> is a <code class=\"notranslate\" translate=\"no\">vec3</code> and what's in the 3rd value\n<a href=\"https://www.shadertoy.com/howto\">is not documented on shadertoy.com</a>. It's\nnot used above so just setting it to 1 for now. ¯\\_(ツ)_/¯</p>\n</blockquote>\n<p></p><div translate=\"no\" class=\"threejs_example_container notranslate\">\n  <div></div>\n  <a class=\"threejs_center\" href=\"/manual/examples/shadertoy-basic.html\" target=\"_blank\">click here to open in a separate window</a>\n</div>\n\n<p></p>\n<p>This <a href=\"https://www.shadertoy.com/new\">matches what we see on Shadertoy for a new shader</a>,\nat least as of January 2019 😉. What's the shader above doing? </p>\n<ul>\n<li><code class=\"notranslate\" translate=\"no\">uv</code> goes from 0 to 1. </li>\n<li><code class=\"notranslate\" translate=\"no\">cos(uv.xyx)</code> gives us 3 cosine values as a <code class=\"notranslate\" translate=\"no\">vec3</code>. One for <code class=\"notranslate\" translate=\"no\">uv.x</code>, another for <code class=\"notranslate\" translate=\"no\">uv.y</code> and another for <code class=\"notranslate\" translate=\"no\">uv.x</code> again.</li>\n<li>Adding in the time, <code class=\"notranslate\" translate=\"no\">cos(iTime+uv.xyx)</code> makes them animate.</li>\n<li>Adding in <code class=\"notranslate\" translate=\"no\">vec3(0,2,4)</code> as in <code class=\"notranslate\" translate=\"no\">cos(iTime+uv.xyx+vec3(0,2,4))</code> offsets the cosine waves</li>\n<li><code class=\"notranslate\" translate=\"no\">cos</code> goes from -1 to 1 so the <code class=\"notranslate\" translate=\"no\">0.5 * 0.5 + cos(...)</code> converts from -1 &lt;-&gt; 1 to 0.0 &lt;-&gt; 1.0</li>\n<li>the results are then used as the RGB color for the current pixel</li>\n</ul>\n<p>A minor change will make it easier to see the cosine waves. Right now <code class=\"notranslate\" translate=\"no\">uv</code> only\ngoes from 0 to 1. A cosine repeats at 2π so let's make it go from 0 to 40 by\nmultiplying by 40.0. That should make it repeat about 6.3 times.</p>\n<pre class=\"prettyprint showlinemods notranslate lang-glsl\" translate=\"no\">-vec3 col = 0.5 + 0.5*cos(iTime+uv.xyx+vec3(0,2,4));\n+vec3 col = 0.5 + 0.5*cos(iTime+uv.xyx*40.0+vec3(0,2,4));\n</pre>\n<p>Counting below I see about 6.3 repeats. We can see the blue between the red\nsince it's offset by 4 via the <code class=\"notranslate\" translate=\"no\">+vec3(0,2,4)</code>. Without that the blue and red\nwould overlap perfectly making purple.</p>\n<p></p><div translate=\"no\" class=\"threejs_example_container notranslate\">\n  <div></div>\n  <a class=\"threejs_center\" href=\"/manual/examples/shadertoy-basic-x40.html\" target=\"_blank\">click here to open in a separate window</a>\n</div>\n\n<p></p>\n<p>Knowing how simple the inputs are and then seeing results like\n<a href=\"https://www.shadertoy.com/view/MdXGW2\">a city canal</a>,\n<a href=\"https://www.shadertoy.com/view/4ttSWf\">a forest</a>,\n<a href=\"https://www.shadertoy.com/view/ld3Gz2\">a snail</a>,\n<a href=\"https://www.shadertoy.com/view/4tBXR1\">a mushroom</a>\nmake the challenge all that much more impressive. Hopefully they also make it\nclear why it's not generally the right approach vs the more traditional ways of\nmaking scenes from triangles. The fact that so much math has to be put into\ncomputing the color of every pixel means those examples run very slow.</p>\n<p>Some shadertoy shaders take textures as inputs like\n<a href=\"https://www.shadertoy.com/view/MsXSzM\">this one</a>. </p>\n<pre class=\"prettyprint showlinemods notranslate lang-glsl\" translate=\"no\">// By Daedelus: https://www.shadertoy.com/user/Daedelus\n// license: Creative Commons Attribution-NonCommercial-ShareAlike 3.0 Unported License.\n#define TIMESCALE 0.25\n#define TILES 8\n#define COLOR 0.7, 1.6, 2.8\n\nvoid mainImage( out vec4 fragColor, in vec2 fragCoord )\n{\n    vec2 uv = fragCoord.xy / iResolution.xy;\n    uv.x *= iResolution.x / iResolution.y;\n\n    vec4 noise = texture2D(iChannel0, floor(uv * float(TILES)) / float(TILES));\n    float p = 1.0 - mod(noise.r + noise.g + noise.b + iTime * float(TIMESCALE), 1.0);\n    p = min(max(p * 3.0 - 1.8, 0.1), 2.0);\n\n    vec2 r = mod(uv * float(TILES), 1.0);\n    r = vec2(pow(r.x - 0.5, 2.0), pow(r.y - 0.5, 2.0));\n    p *= 1.0 - pow(min(1.0, 12.0 * dot(r, r)), 2.0);\n\n    fragColor = vec4(COLOR, 1.0) * p;\n}\n</pre>\n<p>Passing a texture into a shader is similar to\n<a href=\"textures.html\">passing one into a normal material</a> but we need to set\nup the texture on the uniforms.</p>\n<p>First we'll add the uniform for the texture to the shader. They're referred to\nas <code class=\"notranslate\" translate=\"no\">sampler2D</code> in GLSL.</p>\n<pre class=\"prettyprint showlinemods notranslate lang-js\" translate=\"no\">const fragmentShader = <code class=\"notranslate\" translate=\"no\">\n#include &lt;common&gt;\n\nuniform vec3 iResolution;\nuniform float iTime;\n+uniform sampler2D iChannel0;\n\n...\n</code></pre><code class=\"notranslate\" translate=\"no\">\n<p>Then we can load a texture like we covered <a href=\"textures.html\">here</a> and assign the uniform's value.</p>\n<pre class=\"prettyprint showlinemods notranslate lang-js\" translate=\"no\">+const loader = new THREE.TextureLoader();\n+const texture = loader.load('resources/images/bayer.png');\n+texture.minFilter = THREE.NearestFilter;\n+texture.magFilter = THREE.NearestFilter;\n+texture.wrapS = THREE.RepeatWrapping;\n+texture.wrapT = THREE.RepeatWrapping;\nconst uniforms = {\n  iTime: { value: 0 },\n  iResolution:  { value: new THREE.Vector3() },\n+  iChannel0: { value: texture },\n};\n</pre>\n<p></p><div translate=\"no\" class=\"threejs_example_container notranslate\">\n  <div></div>\n  <a class=\"threejs_center\" href=\"/manual/examples/shadertoy-bleepy-blocks.html\" target=\"_blank\">click here to open in a separate window</a>\n</div>\n\n<p></p>\n<p>So far we've been using Shadertoy shaders as they are used on\n<a href=\"https://shadertoy.com\">Shadertoy.com</a>, namely drawing to cover the canvas.\nThere's no reason we need to limit it to just that use case though. The\nimportant part to remember is the functions people write on shadertoy generally\njust take a <code class=\"notranslate\" translate=\"no\">fragCoord</code> input and a <code class=\"notranslate\" translate=\"no\">iResolution</code>. <code class=\"notranslate\" translate=\"no\">fragCoord</code> does not have to\ncome from pixel coordinates, we could use something else like texture\ncoordinates instead and could then use them kind of like other textures. This\ntechnique of using a function to generate textures is often called a\n<a href=\"https://www.google.com/search?q=procedural+texture\"><em>procedural texture</em></a>.</p>\n<p>Let's change the shader above to do this. The simplest thing to do might be to\ntake the texture coordinates that three.js normally supplies, multiply them by\n<code class=\"notranslate\" translate=\"no\">iResolution</code> and pass that in for <code class=\"notranslate\" translate=\"no\">fragCoords</code>. </p>\n<p>To do that we add in a <em>varying</em>. A varying is a value passed from the vertex\nshader to the fragment shader that gets interpolated (or varied) between\nvertices. To use it in our fragment shader we declare it. Three.js refers to its\ntexture coordinates as <code class=\"notranslate\" translate=\"no\">uv</code> with the <code class=\"notranslate\" translate=\"no\">v</code> in front meaning <em>varying</em>.</p>\n<pre class=\"prettyprint showlinemods notranslate lang-glsl\" translate=\"no\">...\n\n+varying vec2 vUv;\n\nvoid main() {\n-  mainImage(gl_FragColor, gl_FragCoord.xy);\n+  mainImage(gl_FragColor, vUv * iResolution.xy);\n}\n</pre>\n<p>Then we need to also provide our own vertex shader. Here is a fairly common\nminimal three.js vertex shader. Three.js declares and will provide values for\n<code class=\"notranslate\" translate=\"no\">uv</code>, <code class=\"notranslate\" translate=\"no\">projectionMatrix</code>, <code class=\"notranslate\" translate=\"no\">modelViewMatrix</code>, and <code class=\"notranslate\" translate=\"no\">position</code>.</p>\n</code><pre class=\"prettyprint showlinemods notranslate lang-js\" translate=\"no\"><code class=\"notranslate\" translate=\"no\">const vertexShader = </code>\n  varying vec2 vUv;\n  void main() {\n    vUv = uv;\n    gl_Position = projectionMatrix * modelViewMatrix * vec4( position, 1.0 );\n  }\n`;\n</pre>\n<p>We need to pass the vertex shader to the <a href=\"/docs/#api/en/materials/ShaderMaterial\"><code class=\"notranslate\" translate=\"no\">ShaderMaterial</code></a></p>\n<pre class=\"prettyprint showlinemods notranslate lang-js\" translate=\"no\">const material = new THREE.ShaderMaterial({\n  vertexShader,\n  fragmentShader,\n  uniforms,\n});\n</pre>\n<p>We can set the <code class=\"notranslate\" translate=\"no\">iResolution</code> uniform value at init time since it will no longer change.</p>\n<pre class=\"prettyprint showlinemods notranslate lang-js\" translate=\"no\">const uniforms = {\n  iTime: { value: 0 },\n-  iResolution:  { value: new THREE.Vector3() },\n+  iResolution:  { value: new THREE.Vector3(1, 1, 1) },\n  iChannel0: { value: texture },\n};\n</pre>\n<p>and we no longer need to set it at render time</p>\n<pre class=\"prettyprint showlinemods notranslate lang-js\" translate=\"no\">-const canvas = renderer.domElement;\n-uniforms.iResolution.value.set(canvas.width, canvas.height, 1);\nuniforms.iTime.value = time;\n</pre>\n<p>Otherwise I copied back in the original camera and code that sets up 3 rotating\ncubes from <a href=\"responsive.html\">the article on responsiveness</a>. The result:</p>\n<p></p><div translate=\"no\" class=\"threejs_example_container notranslate\">\n  <div></div>\n  <a class=\"threejs_center\" href=\"/manual/examples/shadertoy-as-texture.html\" target=\"_blank\">click here to open in a separate window</a>\n</div>\n\n<p></p>\n<p>I hope this at least gets you started on how to use a shadertoy shader with\nthree.js. Again, it's important to remember that most shadertoy shaders are an\ninteresting challenge (draw everything with a single function) rather than the\nrecommended way to actually display things in a performant way. Still, they are\namazing, impressive, beautiful, and you can learn a ton by seeing how they work.</p>\n\n        </div>\n      </div>\n    </div>",
    "success": true,
    "error": "",
    "crawled_at": 1786376809.300204,
    "is_shell": false
  },
  {
    "url": "https://threejs.org/manual/en/align-html-elements-to-3d.html",
    "title": "Aligning HTML Elements to 3D",
    "content": "<div class=\"container\">\n      <div class=\"lesson-title\">\n        <h1>Aligning HTML Elements to 3D</h1>\n      </div>\n      <div class=\"lesson\">\n        <div class=\"lesson-main\">\n          <p>This article is part of a series of articles about three.js. The first article\nis <a href=\"fundamentals.html\">three.js fundamentals</a>. If you haven't read that\nyet and you're new to three.js you might want to consider starting there. </p>\n<p>Sometimes you'd like to display some text in your 3D scene. You have many options\neach with pluses and minuses.</p>\n<ul>\n<li><p>Use 3D text</p>\n<p>If you look at the <a href=\"primitives.html\">primitives article</a> you'll see <a href=\"/docs/#examples/en/geometries/TextGeometry\"><code class=\"notranslate\" translate=\"no\">TextGeometry</code></a> which\nmakes 3D text. This might be useful for flying logos but probably not so useful for stats, info,\nor labelling lots of things.</p>\n</li>\n<li><p>Use a texture with 2D text drawn into it.</p>\n<p>The article on <a href=\"canvas-textures.html\">using a Canvas as a texture</a> shows using\na canvas as a texture. You can draw text into a canvas and <a href=\"billboards.html\">display it as a billboard</a>.\nThe plus here might be that the text is integrated into the 3D scene. For something like a computer terminal\nshown in a 3D scene this might be perfect.</p>\n</li>\n<li><p>Use HTML Elements and position them to match the 3D</p>\n<p>The benefits to this approach is you can use all of HTML. Your HTML can have multiple elements. It can\nby styled with CSS. It can also be selected by the user as it is actual text. </p>\n</li>\n</ul>\n<p>This article will cover this last approach.</p>\n<p>Let's start simple. We'll make a 3D scene with a few primitives and then add a label to each primitive. We'll start\nwith an example from <a href=\"responsive.html\">the article on responsive pages</a> </p>\n<p>We'll add some <a href=\"/docs/#examples/controls/OrbitControls\"><code class=\"notranslate\" translate=\"no\">OrbitControls</code></a> like we did in <a href=\"lights.html\">the article on lighting</a>.</p>\n<pre class=\"prettyprint showlinemods notranslate lang-js\" translate=\"no\">import * as THREE from 'three';\n+import {OrbitControls} from 'three/addons/controls/OrbitControls.js';\n</pre>\n<pre class=\"prettyprint showlinemods notranslate lang-js\" translate=\"no\">const controls = new OrbitControls(camera, canvas);\ncontrols.target.set(0, 0, 0);\ncontrols.update();\n</pre>\n<p>We need to provide an HTML element to contain our label elements</p>\n<pre class=\"prettyprint showlinemods notranslate lang-html\" translate=\"no\">&lt;body&gt;\n-  &lt;canvas id=\"c\"&gt;&lt;/canvas&gt;\n+  &lt;div id=\"container\"&gt;\n+    &lt;canvas id=\"c\"&gt;&lt;/canvas&gt;\n+    &lt;div id=\"labels\"&gt;&lt;/div&gt;\n+  &lt;/div&gt;\n&lt;/body&gt;\n</pre>\n<p>By putting both the canvas and the <code class=\"notranslate\" translate=\"no\">&lt;div id=\"labels\"&gt;</code> inside a\nparent container we can make them overlap with this CSS</p>\n<pre class=\"prettyprint showlinemods notranslate lang-css\" translate=\"no\">#c {\n-    width: 100%;\n-    height: 100%;\n+    width: 100%;  /* let our container decide our size */\n+    height: 100%;\n    display: block;\n}\n+#container {\n+  position: relative;  /* makes this the origin of its children */\n+  width: 100%;\n+  height: 100%;\n+  overflow: hidden;\n+}\n+#labels {\n+  position: absolute;  /* let us position ourself inside the container */\n+  left: 0;             /* make our position the top left of the container */\n+  top: 0;\n+  color: white;\n+}\n</pre>\n<p>let's also add some CSS for the labels themselves</p>\n<pre class=\"prettyprint showlinemods notranslate lang-css\" translate=\"no\">#labels&gt;div {\n  position: absolute;  /* let us position them inside the container */\n  left: 0;             /* make their default position the top left of the container */\n  top: 0;\n  cursor: pointer;     /* change the cursor to a hand when over us */\n  font-size: large;\n  user-select: none;   /* don't let the text get selected */\n  text-shadow:         /* create a black outline */\n    -1px -1px 0 #000,\n     0   -1px 0 #000,\n     1px -1px 0 #000,\n     1px  0   0 #000,\n     1px  1px 0 #000,\n     0    1px 0 #000,\n    -1px  1px 0 #000,\n    -1px  0   0 #000;\n}\n#labels&gt;div:hover {\n  color: red;\n}\n</pre>\n<p>Now into our code we don't have to add too much. We had a function\n<code class=\"notranslate\" translate=\"no\">makeInstance</code> that we used to generate cubes. Let's make it\nso it also adds a label element.</p>\n<pre class=\"prettyprint showlinemods notranslate lang-js\" translate=\"no\">+const labelContainerElem = document.querySelector('#labels');\n\n-function makeInstance(geometry, color, x) {\n+function makeInstance(geometry, color, x, name) {\n  const material = new THREE.MeshPhongMaterial({color});\n\n  const cube = new THREE.Mesh(geometry, material);\n  scene.add(cube);\n\n  cube.position.x = x;\n\n+  const elem = document.createElement('div');\n+  elem.textContent = name;\n+  labelContainerElem.appendChild(elem);\n\n-  return cube;\n+  return {cube, elem};\n}\n</pre>\n<p>As you can see we're adding a <code class=\"notranslate\" translate=\"no\">&lt;div&gt;</code> to the container, one for each cube. We're\nalso returning an object with both the <code class=\"notranslate\" translate=\"no\">cube</code> and the <code class=\"notranslate\" translate=\"no\">elem</code> for the label.</p>\n<p>Calling it we need to provide a name for each</p>\n<pre class=\"prettyprint showlinemods notranslate lang-js\" translate=\"no\">const cubes = [\n-  makeInstance(geometry, 0x44aa88,  0),\n-  makeInstance(geometry, 0x8844aa, -2),\n-  makeInstance(geometry, 0xaa8844,  2),\n+  makeInstance(geometry, 0x44aa88,  0, 'Aqua'),\n+  makeInstance(geometry, 0x8844aa, -2, 'Purple'),\n+  makeInstance(geometry, 0xaa8844,  2, 'Gold'),\n];\n</pre>\n<p>What remains is positioning the label elements at render time</p>\n<pre class=\"prettyprint showlinemods notranslate lang-js\" translate=\"no\">const tempV = new THREE.Vector3();\n\n...\n\n-cubes.forEach((cube, ndx) =&gt; {\n+cubes.forEach((cubeInfo, ndx) =&gt; {\n+  const {cube, elem} = cubeInfo;\n  const speed = 1 + ndx * .1;\n  const rot = time * speed;\n  cube.rotation.x = rot;\n  cube.rotation.y = rot;\n\n+  // get the position of the center of the cube\n+  cube.updateWorldMatrix(true, false);\n+  cube.getWorldPosition(tempV);\n+\n+  // get the normalized screen coordinate of that position\n+  // x and y will be in the -1 to +1 range with x = -1 being\n+  // on the left and y = -1 being on the bottom\n+  tempV.project(camera);\n+\n+  // convert the normalized position to CSS coordinates\n+  const x = (tempV.x *  .5 + .5) * canvas.clientWidth;\n+  const y = (tempV.y * -.5 + .5) * canvas.clientHeight;\n+\n+  // move the elem to that position\n+  elem.style.transform = <code class=\"notranslate\" translate=\"no\">translate(-50%, -50%) translate(${x}px,${y}px)</code>;\n});\n</pre>\n<p>And with that we have labels aligned to their corresponding objects.</p>\n<p></p><div translate=\"no\" class=\"threejs_example_container notranslate\">\n  <div></div>\n  <a class=\"threejs_center\" href=\"/manual/examples/align-html-to-3d.html\" target=\"_blank\">click here to open in a separate window</a>\n</div>\n\n<p></p>\n<p>There are a couple of issues we probably want to deal with.</p>\n<p>One is that if we rotate the objects so they overlap all the labels\noverlap as well.</p>\n<div class=\"threejs_center\"><img src=\"../resources/images/overlapping-labels.png\" style=\"width: 307px;\"></div>\n\n<p>Another is that if we zoom way out so that the objects go outside\nthe frustum the labels will still appear.</p>\n<p>A possible solution to the problem of overlapping objects is to use\nthe <a href=\"picking.html\">picking code from the article on picking</a>.\nWe'll pass in the position of the object on the screen and then\nask the <code class=\"notranslate\" translate=\"no\">RayCaster</code> to tell us which objects were intersected.\nIf our object is not the first one then we are not in the front.</p>\n<pre class=\"prettyprint showlinemods notranslate lang-js\" translate=\"no\">const tempV = new THREE.Vector3();\n+const raycaster = new THREE.Raycaster();\n\n...\n\ncubes.forEach((cubeInfo, ndx) =&gt; {\n  const {cube, elem} = cubeInfo;\n  const speed = 1 + ndx * .1;\n  const rot = time * speed;\n  cube.rotation.x = rot;\n  cube.rotation.y = rot;\n\n  // get the position of the center of the cube\n  cube.updateWorldMatrix(true, false);\n  cube.getWorldPosition(tempV);\n\n  // get the normalized screen coordinate of that position\n  // x and y will be in the -1 to +1 range with x = -1 being\n  // on the left and y = -1 being on the bottom\n  tempV.project(camera);\n\n+  // ask the raycaster for all the objects that intersect\n+  // from the eye toward this object's position\n+  raycaster.setFromCamera(tempV, camera);\n+  const intersectedObjects = raycaster.intersectObjects(scene.children);\n+  // We're visible if the first intersection is this object.\n+  const show = intersectedObjects.length &amp;&amp; cube === intersectedObjects[0].object;\n+\n+  if (!show) {\n+    // hide the label\n+    elem.style.display = 'none';\n+  } else {\n+    // un-hide the label\n+    elem.style.display = '';\n\n    // convert the normalized position to CSS coordinates\n    const x = (tempV.x *  .5 + .5) * canvas.clientWidth;\n    const y = (tempV.y * -.5 + .5) * canvas.clientHeight;\n\n    // move the elem to that position\n    elem.style.transform = <code class=\"notranslate\" translate=\"no\">translate(-50%, -50%) translate(${x}px,${y}px)</code>;\n+  }\n});\n</pre>\n<p>This handles overlapping.</p>\n<p>To handle going outside the frustum we can add this check if the origin of\nthe object is outside the frustum by checking <code class=\"notranslate\" translate=\"no\">tempV.z</code></p>\n<pre class=\"prettyprint showlinemods notranslate lang-js\" translate=\"no\">-  if (!show) {\n+  if (!show || Math.abs(tempV.z) &gt; 1) {\n    // hide the label\n    elem.style.display = 'none';\n</pre>\n<p>This <em>kind of</em> works because the normalized coordinates we computed include a <code class=\"notranslate\" translate=\"no\">z</code>\nvalue that goes from -1 when at the <code class=\"notranslate\" translate=\"no\">near</code> part of our camera frustum to +1 when\nat the <code class=\"notranslate\" translate=\"no\">far</code> part of our camera frustum.</p>\n<p></p><div translate=\"no\" class=\"threejs_example_container notranslate\">\n  <div></div>\n  <a class=\"threejs_center\" href=\"/manual/examples/align-html-to-3d-w-hiding.html\" target=\"_blank\">click here to open in a separate window</a>\n</div>\n\n<p></p>\n<p>For the frustum check, the solution above fails as we're only checking the origin of the object. For a large\nobject. That origin might go outside the frustum but half of the object might still be in the frustum.</p>\n<p>A more correct solution would be to check if the object itself is in the frustum\nor not. Unfortunate that check is slow. For 3 cubes it will not be a problem\nbut for many objects it might be.</p>\n<p>Three.js provides some functions to check if an object's bounding sphere is\nin a frustum</p>\n<pre class=\"prettyprint showlinemods notranslate lang-js\" translate=\"no\">// at init time\nconst frustum = new THREE.Frustum();\nconst viewProjection = new THREE.Matrix4();\n\n...\n\n// before checking\ncamera.updateMatrix();\ncamera.updateMatrixWorld();\ncamera.matrixWorldInverse.copy(camera.matrixWorld).invert();\n\n...\n\n// then for each mesh\nsomeMesh.updateMatrix();\nsomeMesh.updateMatrixWorld();\n\nviewProjection.multiplyMatrices(\n    camera.projectionMatrix, camera.matrixWorldInverse);\nfrustum.setFromProjectionMatrix(viewProjection);\nconst inFrustum = frustum.contains(someMesh));\n</pre>\n<p>Our current overlapping solution has similar issues. Picking is slow. We could\nuse gpu based picking like we covered in the <a href=\"picking.html\">picking\narticle</a> but that is also not free. Which solution you\nchose depends on your needs.</p>\n<p>Another issue is the order the labels appear. If we change the code to have\nlonger labels</p>\n<pre class=\"prettyprint showlinemods notranslate lang-js\" translate=\"no\">const cubes = [\n-  makeInstance(geometry, 0x44aa88,  0, 'Aqua'),\n-  makeInstance(geometry, 0x8844aa, -2, 'Purple'),\n-  makeInstance(geometry, 0xaa8844,  2, 'Gold'),\n+  makeInstance(geometry, 0x44aa88,  0, 'Aqua Colored Box'),\n+  makeInstance(geometry, 0x8844aa, -2, 'Purple Colored Box'),\n+  makeInstance(geometry, 0xaa8844,  2, 'Gold Colored Box'),\n];\n</pre>\n<p>and set the CSS so these don't wrap</p>\n<pre class=\"prettyprint showlinemods notranslate lang-css\" translate=\"no\">#labels&gt;div {\n+  white-space: nowrap;\n</pre>\n<p>Then we can run into this issue</p>\n<div class=\"threejs_center\"><img src=\"../resources/images/label-sorting-issue.png\" style=\"width: 401px;\"></div>\n\n<p>You can see above the purple box is in the back but its label is in front of the aqua box.</p>\n<p>We can fix this by setting the <code class=\"notranslate\" translate=\"no\">zIndex</code> of each element. The projected position has a <code class=\"notranslate\" translate=\"no\">z</code> value\nthat goes from -1 in front to positive 1 in back. <code class=\"notranslate\" translate=\"no\">zIndex</code> is required to be an integer and goes the\nopposite direction meaning for <code class=\"notranslate\" translate=\"no\">zIndex</code> greater values are in front so the following code should work.</p>\n<pre class=\"prettyprint showlinemods notranslate lang-js\" translate=\"no\">// convert the normalized position to CSS coordinates\nconst x = (tempV.x *  .5 + .5) * canvas.clientWidth;\nconst y = (tempV.y * -.5 + .5) * canvas.clientHeight;\n\n// move the elem to that position\nelem.style.transform = <code class=\"notranslate\" translate=\"no\">translate(-50%, -50%) translate(${x}px,${y}px)</code>;\n\n+// set the zIndex for sorting\n+elem.style.zIndex = (-tempV.z * .5 + .5) * 100000 | 0;\n</pre>\n<p>Because of the way the projected z value works we need to pick a large number to spread out the values\notherwise many will have the same value. To make sure the labels don't overlap with other parts of\nthe page we can tell the browser to create a new <a href=\"https://developer.mozilla.org/en-US/docs/Web/CSS/CSS_Positioning/Understanding_z_index/The_stacking_context\">stacking context</a>\nby setting the <code class=\"notranslate\" translate=\"no\">z-index</code> of the container of the labels</p>\n<pre class=\"prettyprint showlinemods notranslate lang-css\" translate=\"no\">#labels {\n  position: absolute;  /* let us position ourself inside the container */\n+  z-index: 0;          /* make a new stacking context so children don't sort with rest of page */\n  left: 0;             /* make our position the top left of the container */\n  top: 0;\n  color: white;\n  z-index: 0;\n}\n</pre>\n<p>and now the labels should always be in the correct order.</p>\n<p></p><div translate=\"no\" class=\"threejs_example_container notranslate\">\n  <div></div>\n  <a class=\"threejs_center\" href=\"/manual/examples/align-html-to-3d-w-sorting.html\" target=\"_blank\">click here to open in a separate window</a>\n</div>\n\n<p></p>\n<p>While we're at it let's do one more example to show one more issue.\nLet's draw a globe like Google Maps and label the countries.</p>\n<p>I found <a href=\"http://thematicmapping.org/downloads/world_borders.php\">this data</a>\nwhich contains the borders of countries. It's licensed as\n<a href=\"http://creativecommons.org/licenses/by-sa/3.0/\">CC-BY-SA</a>.</p>\n<p>I <a href=\"https://github.com/mrdoob/three.js/blob/master/manual/resources/tools/geo-picking/\">wrote some code</a>\nto load the data, and generate country outlines and some JSON data with the names\nof the countries and their locations.</p>\n<div class=\"threejs_center\"><img src=\"../examples/resources/data/world/country-outlines-4k.png\" style=\"background: black; width: 700px\"></div>\n\n<p>The JSON data is an array of entries something like this</p>\n<pre class=\"prettyprint showlinemods notranslate lang-json\" translate=\"no\">[\n  {\n    \"name\": \"Algeria\",\n    \"min\": [\n      -8.667223,\n      18.976387\n    ],\n    \"max\": [\n      11.986475,\n      37.091385\n    ],\n    \"area\": 238174,\n    \"lat\": 28.163,\n    \"lon\": 2.632,\n    \"population\": {\n      \"2005\": 32854159\n    }\n  },\n  ...\n</pre>\n<p>where min, max, lat, lon, are all in latitude and longitude degrees.</p>\n<p>Let's load it up. The code is based on the examples from <a href=\"optimize-lots-of-objects.html\">optimizing lots of\nobjects</a> though we are not drawing lots\nof objects we'll be using the same solutions for <a href=\"rendering-on-demand.html\">rendering on\ndemand</a>.</p>\n<p>The first thing is to make a sphere and use the outline texture.</p>\n<pre class=\"prettyprint showlinemods notranslate lang-js\" translate=\"no\">{\n  const loader = new THREE.TextureLoader();\n  const texture = loader.load('resources/data/world/country-outlines-4k.png', render);\n  const geometry = new THREE.SphereGeometry(1, 64, 32);\n  const material = new THREE.MeshBasicMaterial({map: texture});\n  scene.add(new THREE.Mesh(geometry, material));\n}\n</pre>\n<p>Then let's load the JSON file by first making a loader</p>\n<pre class=\"prettyprint showlinemods notranslate lang-js\" translate=\"no\">async function loadJSON(url) {\n  const req = await fetch(url);\n  return req.json();\n}\n</pre>\n<p>and then calling it</p>\n<pre class=\"prettyprint showlinemods notranslate lang-js\" translate=\"no\">let countryInfos;\nasync function loadCountryData() {\n  countryInfos = await loadJSON('resources/data/world/country-info.json');\n     ...\n  }\n  requestRenderIfNotRequested();\n}\nloadCountryData();\n</pre>\n<p>Now let's use that data to generate and place the labels.</p>\n<p>In the article on <a href=\"optimize-lots-of-objects.html\">optimizing lots of objects</a>\nwe had setup a small scene graph of helper objects to make it easy to\ncompute latitude and longitude positions on our globe. See that article\nfor an explanation of how they work.</p>\n<pre class=\"prettyprint showlinemods notranslate lang-js\" translate=\"no\">const lonFudge = Math.PI * 1.5;\nconst latFudge = Math.PI;\n// these helpers will make it easy to position the boxes\n// We can rotate the lon helper on its Y axis to the longitude\nconst lonHelper = new THREE.Object3D();\n// We rotate the latHelper on its X axis to the latitude\nconst latHelper = new THREE.Object3D();\nlonHelper.add(latHelper);\n// The position helper moves the object to the edge of the sphere\nconst positionHelper = new THREE.Object3D();\npositionHelper.position.z = 1;\nlatHelper.add(positionHelper);\n</pre>\n<p>We'll use that to compute a position for each label</p>\n<pre class=\"prettyprint showlinemods notranslate lang-js\" translate=\"no\">const labelParentElem = document.querySelector('#labels');\nfor (const countryInfo of countryInfos) {\n  const {lat, lon, name} = countryInfo;\n\n  // adjust the helpers to point to the latitude and longitude\n  lonHelper.rotation.y = THREE.MathUtils.degToRad(lon) + lonFudge;\n  latHelper.rotation.x = THREE.MathUtils.degToRad(lat) + latFudge;\n\n  // get the position of the lat/lon\n  positionHelper.updateWorldMatrix(true, false);\n  const position = new THREE.Vector3();\n  positionHelper.getWorldPosition(position);\n  countryInfo.position = position;\n\n  // add an element for each country\n  const elem = document.createElement('div');\n  elem.textContent = name;\n  labelParentElem.appendChild(elem);\n  countryInfo.elem = elem;\n</pre>\n<p>The code above looks very similar to the code we wrote for making cube labels\nmaking an element per label. When we're done we have an array, <code class=\"notranslate\" translate=\"no\">countryInfos</code>,\nwith one entry for each country to which we've added an <code class=\"notranslate\" translate=\"no\">elem</code> property for\nthe label element for that country and a <code class=\"notranslate\" translate=\"no\">position</code> with its position on the\nglobe.</p>\n<p>Just like we did for the cubes we need to update the position of the\nlabels and render time.</p>\n<pre class=\"prettyprint showlinemods notranslate lang-js\" translate=\"no\">const tempV = new THREE.Vector3();\n\nfunction updateLabels() {\n  // exit if we have not yet loaded the JSON file\n  if (!countryInfos) {\n    return;\n  }\n\n  for (const countryInfo of countryInfos) {\n    const {position, elem} = countryInfo;\n\n    // get the normalized screen coordinate of that position\n    // x and y will be in the -1 to +1 range with x = -1 being\n    // on the left and y = -1 being on the bottom\n    tempV.copy(position);\n    tempV.project(camera);\n\n    // convert the normalized position to CSS coordinates\n    const x = (tempV.x *  .5 + .5) * canvas.clientWidth;\n    const y = (tempV.y * -.5 + .5) * canvas.clientHeight;\n\n    // move the elem to that position\n    elem.style.transform = <code class=\"notranslate\" translate=\"no\">translate(-50%, -50%) translate(${x}px,${y}px)</code>;\n\n    // set the zIndex for sorting\n    elem.style.zIndex = (-tempV.z * .5 + .5) * 100000 | 0;\n  }\n}\n</pre>\n<p>You can see the code above is substantially similar to the cube example before.\nThe only major difference is we pre-computed the label positions at init time.\nWe can do this because the globe never moves. Only our camera moves.</p>\n<p>Lastly we need to call <code class=\"notranslate\" translate=\"no\">updateLabels</code> in our render loop</p>\n<pre class=\"prettyprint showlinemods notranslate lang-js\" translate=\"no\">function render() {\n  renderRequested = false;\n\n  if (resizeRendererToDisplaySize(renderer)) {\n    const canvas = renderer.domElement;\n    camera.aspect = canvas.clientWidth / canvas.clientHeight;\n    camera.updateProjectionMatrix();\n  }\n\n  controls.update();\n\n+  updateLabels();\n\n  renderer.render(scene, camera);\n}\n</pre>\n<p>And this is what we get</p>\n<p></p><div translate=\"no\" class=\"threejs_example_container notranslate\">\n  <div></div>\n  <a class=\"threejs_center\" href=\"/manual/examples/align-html-elements-to-3d-globe-too-many-labels.html\" target=\"_blank\">click here to open in a separate window</a>\n</div>\n\n<p></p>\n<p>That is way too many labels!</p>\n<p>We have 2 problems.</p>\n<ol>\n<li><p>Labels facing away from us are showing up.</p>\n</li>\n<li><p>There are too many labels.</p>\n</li>\n</ol>\n<p>For issue #1 we can't really use the <code class=\"notranslate\" translate=\"no\">RayCaster</code> like we did above as there is\nnothing to intersect except the sphere. Instead what we can do is check if that\nparticular country is facing away from us or not. This works because the label\npositions are around a sphere. In fact we're using a unit sphere, a sphere with\na radius of 1.0. That means the positions are already unit directions making\nthe math relatively easy.</p>\n<pre class=\"prettyprint showlinemods notranslate lang-js\" translate=\"no\">const tempV = new THREE.Vector3();\n+const cameraToPoint = new THREE.Vector3();\n+const cameraPosition = new THREE.Vector3();\n+const normalMatrix = new THREE.Matrix3();\n\nfunction updateLabels() {\n  // exit if we have not yet loaded the JSON file\n  if (!countryInfos) {\n    return;\n  }\n\n+  const minVisibleDot = 0.2;\n+  // get a matrix that represents a relative orientation of the camera\n+  normalMatrix.getNormalMatrix(camera.matrixWorldInverse);\n+  // get the camera's position\n+  camera.getWorldPosition(cameraPosition);\n  for (const countryInfo of countryInfos) {\n    const {position, elem} = countryInfo;\n\n+    // Orient the position based on the camera's orientation.\n+    // Since the sphere is at the origin and the sphere is a unit sphere\n+    // this gives us a camera relative direction vector for the position.\n+    tempV.copy(position);\n+    tempV.applyMatrix3(normalMatrix);\n+\n+    // compute the direction to this position from the camera\n+    cameraToPoint.copy(position);\n+    cameraToPoint.applyMatrix4(camera.matrixWorldInverse).normalize();\n+\n+    // get the dot product of camera relative direction to this position\n+    // on the globe with the direction from the camera to that point.\n+    // 1 = facing directly towards the camera\n+    // 0 = exactly on tangent of the sphere from the camera\n+    // &lt; 0 = facing away\n+    const dot = tempV.dot(cameraToPoint);\n+\n+    // if the orientation is not facing us hide it.\n+    if (dot &lt; minVisibleDot) {\n+      elem.style.display = 'none';\n+      continue;\n+    }\n+\n+    // restore the element to its default display style\n+    elem.style.display = '';\n\n    // get the normalized screen coordinate of that position\n    // x and y will be in the -1 to +1 range with x = -1 being\n    // on the left and y = -1 being on the bottom\n    tempV.copy(position);\n    tempV.project(camera);\n\n    // convert the normalized position to CSS coordinates\n    const x = (tempV.x *  .5 + .5) * canvas.clientWidth;\n    const y = (tempV.y * -.5 + .5) * canvas.clientHeight;\n\n    // move the elem to that position\n    countryInfo.elem.style.transform = <code class=\"notranslate\" translate=\"no\">translate(-50%, -50%) translate(${x}px,${y}px)</code>;\n\n    // set the zIndex for sorting\n    elem.style.zIndex = (-tempV.z * .5 + .5) * 100000 | 0;\n  }\n}\n</pre>\n<p>Above we use the positions as a direction and get that direction relative to the\ncamera. Then we get the camera relative direction from the camera to that\nposition on the globe and take the <em>dot product</em>. The dot product returns the cosine\nof the angle between the to vectors. This gives us a value from -1\nto +1 where -1 means the label is facing the camera, 0 means the label is directly\non the edge of the sphere relative to the camera, and anything greater than zero is\nbehind. We then use that value to show or hide the element.</p>\n<div class=\"spread\">\n  <div>\n    <div data-diagram=\"dotProduct\" style=\"height: 400px\"><div style=\"position: relative; width: 100%; height: 100%;\"><canvas width=\"760\" height=\"400\"></canvas><div class=\"lil-gui allow-touch-styles root\" style=\"position: absolute; top: 0px; right: 0px;\"><div class=\"title\" role=\"button\" aria-expanded=\"true\" tabindex=\"0\">Controls</div><div class=\"children\"><div class=\"controller number hasSlider\"><div class=\"name\" id=\"lil-gui-name-1\">rotation</div><div class=\"widget\"><div class=\"slider\"><div class=\"fill\" style=\"width: 54.7746%;\"></div></div><input type=\"number\" step=\"any\" aria-labelledby=\"lil-gui-name-1\"></div></div></div></div></div></div>\n  </div>\n</div>\n\n<p>In the diagram above we can see the dot product of the direction the label is\nfacing to direction from the camera to that position. If you rotate the\ndirection you'll see the dot product is -1.0 when the direction is directly\nfacing the camera, it's 0.0 when exactly on the tangent of the sphere relative\nto the camera or to put it another way it's 0 when the 2 vectors are\nperpendicular to each other, 90 degrees It's greater than zero with the label is\nbehind the sphere.</p>\n<p>For issue #2, too many labels we need some way to decide which labels\nto show. One way would be to only show labels for large countries.\nThe data we're loading contains min and max values for the area a\ncountry covers. From that we can compute an area and then use that\narea to decide whether or not to display the country.</p>\n<p>At init time let's compute the area</p>\n<pre class=\"prettyprint showlinemods notranslate lang-js\" translate=\"no\">const labelParentElem = document.querySelector('#labels');\nfor (const countryInfo of countryInfos) {\n  const {lat, lon, min, max, name} = countryInfo;\n\n  // adjust the helpers to point to the latitude and longitude\n  lonHelper.rotation.y = THREE.MathUtils.degToRad(lon) + lonFudge;\n  latHelper.rotation.x = THREE.MathUtils.degToRad(lat) + latFudge;\n\n  // get the position of the lat/lon\n  positionHelper.updateWorldMatrix(true, false);\n  const position = new THREE.Vector3();\n  positionHelper.getWorldPosition(position);\n  countryInfo.position = position;\n\n+  // compute the area for each country\n+  const width = max[0] - min[0];\n+  const height = max[1] - min[1];\n+  const area = width * height;\n+  countryInfo.area = area;\n\n  // add an element for each country\n  const elem = document.createElement('div');\n  elem.textContent = name;\n  labelParentElem.appendChild(elem);\n  countryInfo.elem = elem;\n}\n</pre>\n<p>Then at render time let's use the area to decide to display the label\nor not</p>\n<pre class=\"prettyprint showlinemods notranslate lang-js\" translate=\"no\">+const large = 20 * 20;\nconst maxVisibleDot = 0.2;\n// get a matrix that represents a relative orientation of the camera\nnormalMatrix.getNormalMatrix(camera.matrixWorldInverse);\n// get the camera's position\ncamera.getWorldPosition(cameraPosition);\nfor (const countryInfo of countryInfos) {\n-  const {position, elem} = countryInfo;\n+  const {position, elem, area} = countryInfo;\n+  // large enough?\n+  if (area &lt; large) {\n+    elem.style.display = 'none';\n+    continue;\n+  }\n\n  ...\n</pre>\n<p>Finally, since I'm not sure what good values are for these settings lets\nadd a GUI so we can play with them</p>\n<pre class=\"prettyprint showlinemods notranslate lang-js\" translate=\"no\">import * as THREE from 'three';\nimport {OrbitControls} from 'three/addons/controls/OrbitControls.js';\n+import {GUI} from 'three/addons/libs/lil-gui.module.min.js';\n</pre>\n<pre class=\"prettyprint showlinemods notranslate lang-js\" translate=\"no\">+const settings = {\n+  minArea: 20,\n+  maxVisibleDot: -0.2,\n+};\n+const gui = new GUI({width: 300});\n+gui.add(settings, 'minArea', 0, 50).onChange(requestRenderIfNotRequested);\n+gui.add(settings, 'maxVisibleDot', -1, 1, 0.01).onChange(requestRenderIfNotRequested);\n\nfunction updateLabels() {\n  if (!countryInfos) {\n    return;\n  }\n\n-  const large = 20 * 20;\n-  const maxVisibleDot = -0.2;\n+  const large = settings.minArea * settings.minArea;\n  // get a matrix that represents a relative orientation of the camera\n  normalMatrix.getNormalMatrix(camera.matrixWorldInverse);\n  // get the camera's position\n  camera.getWorldPosition(cameraPosition);\n  for (const countryInfo of countryInfos) {\n\n    ...\n\n    // if the orientation is not facing us hide it.\n-    if (dot &gt; maxVisibleDot) {\n+    if (dot &gt; settings.maxVisibleDot) {\n      elem.style.display = 'none';\n      continue;\n    }\n</pre>\n<p>and here's the result</p>\n<p></p><div translate=\"no\" class=\"threejs_example_container notranslate\">\n  <div></div>\n  <a class=\"threejs_center\" href=\"/manual/examples/align-html-elements-to-3d-globe.html\" target=\"_blank\">click here to open in a separate window</a>\n</div>\n\n<p></p>\n<p>You can see as you rotate the earth labels that go behind disappear.\nAdjust the <code class=\"notranslate\" translate=\"no\">minVisibleDot</code> to see the cutoff change.\nYou can also adjust the <code class=\"notranslate\" translate=\"no\">minArea</code> value to see larger or smaller countries\nappear.</p>\n<p>The more I worked on this the more I realized just how much\nwork is put into Google Maps. They have also have to decide which labels to\nshow. I'm pretty sure they use all kinds of criteria. For example your current\nlocation, your default language setting, your account settings if you have an\naccount, they probably use population or popularity, they might give priority\nto the countries in the center of the view, etc ... Lots to think about.</p>\n<p>In any case I hope these examples gave you some idea of how to align HTML\nelements with your 3D. A few things I might change.</p>\n<p>Next up let's make it so you can <a href=\"indexed-textures.html\">pick and highlight a country</a>.</p>\n<p><link rel=\"stylesheet\" href=\"../resources/threejs-align-html-elements-to-3d.css\"></p>\n\n\n        </div>\n      </div>\n    </div>",
    "success": true,
    "error": "",
    "crawled_at": 1786376809.300204,
    "is_shell": false
  },
  {
    "url": "https://threejs.org/manual/en/indexed-textures.html",
    "title": "Indexed Textures for Picking and Color",
    "content": "<div class=\"container\">\n      <div class=\"lesson-title\">\n        <h1>Indexed Textures for Picking and Color</h1>\n      </div>\n      <div class=\"lesson\">\n        <div class=\"lesson-main\">\n          <p>This article is a continuation of <a href=\"align-html-elements-to-3d.html\">an article about aligning html elements to 3d</a>.\nIf you haven't read that yet you should start there before continuing here.</p>\n<p>Sometimes using three.js requires coming up with creative solutions.\nI'm not sure this is a great solution but I thought I'd share it and\nyou can see if it suggests any solutions for your needs.</p>\n<p>In the <a href=\"align-html-elements-to-3d.html\">previous article</a> we\ndisplayed country names around a 3d globe. How would we go about letting\nthe user select a country and show their selection?</p>\n<p>The first idea that comes to mind is to generate geometry for each country.\nWe could <a href=\"picking.html\">use a picking solution</a> like we covered before.\nWe'd build 3D geometry for each country. If the user clicks on the mesh for\nthat country we'd know what country was clicked.</p>\n<p>So, just to check that solution I tried generating 3D meshes of all the countries\nusing the same data I used to generate the outlines\n<a href=\"align-html-elements-to-3d.html\">in the previous article</a>.\nThe result was a 15.5meg binary GLTF (.glb) file. Making the user download 15.5meg\nsounds like too much to me.</p>\n<p>There are lots of ways to compress the data. The first would probably be\nto apply some algorithm to lower the resolution of the outlines. I didn't spend\nany time pursuing that solution. For borders of the USA that's probably a huge\nwin. For a borders of Canada probably much less. </p>\n<p>Another solution would be to use just actual data compression. For example gzipping\nthe file brought it down to 11meg. That's 30% less but arguably not enough.</p>\n<p>We could store all the data as 16bit ranged values instead of 32bit float values.\nOr we could use something like <a href=\"https://google.github.io/draco/\">draco compression</a>\nand maybe that would be enough. I didn't check and I would encourage you to check\nyourself and tell me how it goes as I'd love to know. 😅</p>\n<p>In my case I thought about <a href=\"picking.html\">the GPU picking solution</a>\nwe covered at the end of <a href=\"picking.html\">the article on picking</a>. In\nthat solution we drew every mesh with a unique color that represented that\nmesh's id. We then drew all the meshes and looked at the color that was clicked\non.</p>\n<p>Taking inspiration from that we could pre-generate a map of countries where\neach country's color is its index number in our array of countries. We could\nthen use a similar GPU picking technique. We'd draw the globe off screen using\nthis index texture. Looking at the color of the pixel the user clicks would\ntell us the country id.</p>\n<p>So, I <a href=\"https://github.com/mrdoob/three.js/blob/master/manual/resources/tools/geo-picking/\">wrote some code</a>\nto generate such a texture. Here it is. </p>\n<div class=\"threejs_center\"><img src=\"../examples/resources/data/world/country-index-texture.png\" style=\"width: 700px;\"></div>\n\n<p>Note: The data used to generate this texture comes from <a href=\"http://thematicmapping.org/downloads/world_borders.php\">this website</a>\nand is therefore licensed as <a href=\"http://creativecommons.org/licenses/by-sa/3.0/\">CC-BY-SA</a>.</p>\n<p>It's only 217k, much better than the 14meg for the country meshes. In fact we could probably\neven lower the resolution but 217k seems good enough for now.</p>\n<p>So let's try using it for picking countries.</p>\n<p>Grabbing code from the <a href=\"picking.html\">gpu picking example</a> we need\na scene for picking.</p>\n<pre class=\"prettyprint showlinemods notranslate lang-js\" translate=\"no\">const pickingScene = new THREE.Scene();\npickingScene.background = new THREE.Color(0);\n</pre>\n<p>and we need to add the globe with the our index texture to the\npicking scene.</p>\n<pre class=\"prettyprint showlinemods notranslate lang-js\" translate=\"no\">{\n  const loader = new THREE.TextureLoader();\n  const geometry = new THREE.SphereGeometry(1, 64, 32);\n\n+  const indexTexture = loader.load('resources/data/world/country-index-texture.png', render);\n+  indexTexture.minFilter = THREE.NearestFilter;\n+  indexTexture.magFilter = THREE.NearestFilter;\n+\n+  const pickingMaterial = new THREE.MeshBasicMaterial({map: indexTexture});\n+  pickingScene.add(new THREE.Mesh(geometry, pickingMaterial));\n\n  const texture = loader.load('resources/data/world/country-outlines-4k.png', render);\n  const material = new THREE.MeshBasicMaterial({map: texture});\n  scene.add(new THREE.Mesh(geometry, material));\n}\n</pre>\n<p>Then let's copy over the <code class=\"notranslate\" translate=\"no\">GPUPickingHelper</code> class we used\nbefore with a few minor changes.</p>\n<pre class=\"prettyprint showlinemods notranslate lang-js\" translate=\"no\">class GPUPickHelper {\n  constructor() {\n    // create a 1x1 pixel render target\n    this.pickingTexture = new THREE.WebGLRenderTarget(1, 1);\n    this.pixelBuffer = new Uint8Array(4);\n-    this.pickedObject = null;\n-    this.pickedObjectSavedColor = 0;\n  }\n  pick(cssPosition, scene, camera) {\n    const {pickingTexture, pixelBuffer} = this;\n\n    // set the view offset to represent just a single pixel under the mouse\n    const pixelRatio = renderer.getPixelRatio();\n    camera.setViewOffset(\n        renderer.getContext().drawingBufferWidth,   // full width\n        renderer.getContext().drawingBufferHeight,  // full top\n        cssPosition.x * pixelRatio | 0,             // rect x\n        cssPosition.y * pixelRatio | 0,             // rect y\n        1,                                          // rect width\n        1,                                          // rect height\n    );\n    // render the scene\n    renderer.setRenderTarget(pickingTexture);\n    renderer.render(scene, camera);\n    renderer.setRenderTarget(null);\n    // clear the view offset so rendering returns to normal\n    camera.clearViewOffset();\n    //read the pixel\n    renderer.readRenderTargetPixels(\n        pickingTexture,\n        0,   // x\n        0,   // y\n        1,   // width\n        1,   // height\n        pixelBuffer);\n\n+    const id =\n+        (pixelBuffer[0] &lt;&lt; 16) |\n+        (pixelBuffer[1] &lt;&lt;  8) |\n+        (pixelBuffer[2] &lt;&lt;  0);\n+\n+    return id;\n-    const id =\n-        (pixelBuffer[0] &lt;&lt; 16) |\n-        (pixelBuffer[1] &lt;&lt;  8) |\n-        (pixelBuffer[2]      );\n-    const intersectedObject = idToObject[id];\n-    if (intersectedObject) {\n-      // pick the first object. It's the closest one\n-      this.pickedObject = intersectedObject;\n-      // save its color\n-      this.pickedObjectSavedColor = this.pickedObject.material.emissive.getHex();\n-      // set its emissive color to flashing red/yellow\n-      this.pickedObject.material.emissive.setHex((time * 8) % 2 &gt; 1 ? 0xFFFF00 : 0xFF0000);\n-    }\n  }\n}\n</pre>\n<p>Now we can use that to pick countries.</p>\n<pre class=\"prettyprint showlinemods notranslate lang-js\" translate=\"no\">const pickHelper = new GPUPickHelper();\n\nfunction getCanvasRelativePosition(event) {\n  const rect = canvas.getBoundingClientRect();\n  return {\n    x: (event.clientX - rect.left) * canvas.width  / rect.width,\n    y: (event.clientY - rect.top ) * canvas.height / rect.height,\n  };\n}\n\nfunction pickCountry(event) {\n  // exit if we have not loaded the data yet\n  if (!countryInfos) {\n    return;\n  }\n\n  const position = getCanvasRelativePosition(event);\n  const id = pickHelper.pick(position, pickingScene, camera);\n  if (id &gt; 0) {\n    // we clicked a country. Toggle its 'selected' property\n    const countryInfo = countryInfos[id - 1];\n    const selected = !countryInfo.selected;\n    // if we're selecting this country and modifiers are not\n    // pressed unselect everything else.\n    if (selected &amp;&amp; !event.shiftKey &amp;&amp; !event.ctrlKey &amp;&amp; !event.metaKey) {\n      unselectAllCountries();\n    }\n    numCountriesSelected += selected ? 1 : -1;\n    countryInfo.selected = selected;\n  } else if (numCountriesSelected) {\n    // the ocean or sky was clicked\n    unselectAllCountries();\n  }\n  requestRenderIfNotRequested();\n}\n\nfunction unselectAllCountries() {\n  numCountriesSelected = 0;\n  countryInfos.forEach((countryInfo) =&gt; {\n    countryInfo.selected = false;\n  });\n}\n\ncanvas.addEventListener('pointerup', pickCountry);\n</pre>\n<p>The code above sets/unsets the <code class=\"notranslate\" translate=\"no\">selected</code> property on\nthe array of countries. If <code class=\"notranslate\" translate=\"no\">shift</code> or <code class=\"notranslate\" translate=\"no\">ctrl</code> or <code class=\"notranslate\" translate=\"no\">cmd</code>\nis pressed then you can select more than one country.</p>\n<p>All that's left is showing the selected countries. For now\nlet's just update the labels.</p>\n<pre class=\"prettyprint showlinemods notranslate lang-js\" translate=\"no\">function updateLabels() {\n  // exit if we have not loaded the data yet\n  if (!countryInfos) {\n    return;\n  }\n\n  const large = settings.minArea * settings.minArea;\n  // get a matrix that represents a relative orientation of the camera\n  normalMatrix.getNormalMatrix(camera.matrixWorldInverse);\n  // get the camera's position\n  camera.getWorldPosition(cameraPosition);\n  for (const countryInfo of countryInfos) {\n-    const {position, elem, area} = countryInfo;\n-    // large enough?\n-    if (area &lt; large) {\n+    const {position, elem, area, selected} = countryInfo;\n+    const largeEnough = area &gt;= large;\n+    const show = selected || (numCountriesSelected === 0 &amp;&amp; largeEnough);\n+    if (!show) {\n      elem.style.display = 'none';\n      continue;\n    }\n\n    ...\n</pre>\n<p>and with that we should be able to pick countries</p>\n<p></p><div translate=\"no\" class=\"threejs_example_container notranslate\">\n  <div></div>\n  <a class=\"threejs_center\" href=\"/manual/examples/indexed-textures-picking.html\" target=\"_blank\">click here to open in a separate window</a>\n</div>\n\n<p></p>\n<p>The code stills shows countries based on their area but if you\nclick one just that one will have a label.</p>\n<p>So that seems like a reasonable solution for picking countries\nbut what about highlighting the selected countries?</p>\n<p>For that we can take inspiration from <em>paletted graphics</em>.</p>\n<p><a href=\"https://en.wikipedia.org/wiki/Palette_%28computing%29\">Paletted graphics</a>\nor <a href=\"https://en.wikipedia.org/wiki/Indexed_color\">Indexed Color</a> is\nwhat older systems like the Atari 800, Amiga, NES,\nSuper Nintendo, and even older IBM PCs used. Instead of storing bitmaps\nas RGBA colors 8bits per color, 32 bytes per pixel or more, they stored\nbitmaps as 8bit values or less. The value for each pixel was an index\ninto a palette. So for example a value\nof 3 in the image means \"display color 3\". What color color#3 is is\ndefined somewhere else called a \"palette\".</p>\n<p>In JavaScript you can think of it like this</p>\n<pre class=\"prettyprint showlinemods notranslate lang-js\" translate=\"no\">const face7x7PixelImageData = [\n  0, 1, 1, 1, 1, 1, 0,\n  1, 0, 0, 0, 0, 0, 1,\n  1, 0, 2, 0, 2, 0, 1,\n  1, 0, 0, 0, 0, 0, 1,\n  1, 0, 3, 3, 3, 0, 1,\n  1, 0, 0, 0, 0, 0, 1,\n  0, 1, 1, 1, 1, 1, 1,\n];\n\nconst palette = [\n  [255, 255, 255],  // white\n  [  0,   0,   0],  // black\n  [  0, 255, 255],  // cyan\n  [255,   0,   0],  // red\n];\n</pre>\n<p>Where each pixel in the image data is an index into palette. If you interpreted\nthe image data through the palette above you'd get this image</p>\n<div class=\"threejs_center\"><img src=\"../resources/images/7x7-indexed-face.png\"></div>\n\n<p>In our case we already have a texture above that has a different id\nper country. So, we could use that same texture through a palette\ntexture to give each country its own color. By changing the palette\ntexture we can color each individual country. For example by setting\nthe entire palette texture to black and then for one country's entry\nin the palette a different color, we can highlight just that country.</p>\n<p>To do paletted index graphics requires some custom shader code.\nLet's modify the default shaders in three.js.\nThat way we can use lighting and other features if we want.</p>\n<p>Like we covered in <a href=\"optimize-lots-of-objects-animated.html\">the article on animating lots of objects</a>\nwe can modify the default shaders by adding a function to a material's\n<code class=\"notranslate\" translate=\"no\">onBeforeCompile</code> property.</p>\n<p>The default fragment shader looks something like this before compiling.</p>\n<pre class=\"prettyprint showlinemods notranslate lang-glsl\" translate=\"no\">#include &lt;common&gt;\n#include &lt;color_pars_fragment&gt;\n#include &lt;uv_pars_fragment&gt;\n#include &lt;map_pars_fragment&gt;\n#include &lt;alphamap_pars_fragment&gt;\n#include &lt;aomap_pars_fragment&gt;\n#include &lt;lightmap_pars_fragment&gt;\n#include &lt;envmap_pars_fragment&gt;\n#include &lt;fog_pars_fragment&gt;\n#include &lt;specularmap_pars_fragment&gt;\n#include &lt;logdepthbuf_pars_fragment&gt;\n#include &lt;clipping_planes_pars_fragment&gt;\nvoid main() {\n    #include &lt;clipping_planes_fragment&gt;\n    vec4 diffuseColor = vec4( diffuse, opacity );\n    #include &lt;logdepthbuf_fragment&gt;\n    #include &lt;map_fragment&gt;\n    #include &lt;color_fragment&gt;\n    #include &lt;alphamap_fragment&gt;\n    #include &lt;alphatest_fragment&gt;\n    #include &lt;specularmap_fragment&gt;\n    ReflectedLight reflectedLight = ReflectedLight( vec3( 0.0 ), vec3( 0.0 ), vec3( 0.0 ), vec3( 0.0 ) );\n    #ifdef USE_LIGHTMAP\n        reflectedLight.indirectDiffuse += texture2D( lightMap, vLightMapUv ).xyz * lightMapIntensity;\n    #else\n        reflectedLight.indirectDiffuse += vec3( 1.0 );\n    #endif\n    #include &lt;aomap_fragment&gt;\n    reflectedLight.indirectDiffuse *= diffuseColor.rgb;\n    vec3 outgoingLight = reflectedLight.indirectDiffuse;\n    #include &lt;envmap_fragment&gt;\n    gl_FragColor = vec4( outgoingLight, diffuseColor.a );\n    #include &lt;premultiplied_alpha_fragment&gt;\n    #include &lt;tonemapping_fragment&gt;\n    #include &lt;colorspace_fragment&gt;\n    #include &lt;fog_fragment&gt;\n}\n</pre>\n<p><a href=\"https://github.com/mrdoob/three.js/tree/dev/src/renderers/shaders/ShaderChunk\">Digging through all those snippets</a>\nwe find that three.js uses a variable called <code class=\"notranslate\" translate=\"no\">diffuseColor</code> to manage the\nbase material color. It sets this in the <code class=\"notranslate\" translate=\"no\">&lt;color_fragment&gt;</code> <a href=\"https://github.com/mrdoob/three.js/blob/dev/src/renderers/shaders/ShaderChunk/color_fragment.glsl.js\">snippet</a>\nso we should be able to modify it after that point.</p>\n<p><code class=\"notranslate\" translate=\"no\">diffuseColor</code> at that point in the shader should already be the color from\nour outline texture so we can look up the color from a palette texture\nand mix them for the final result.</p>\n<p>Like we <a href=\"optimize-lots-of-objects-animated.html\">did before</a> we'll make an array\nof search and replacement strings and apply them to the shader in\n<a href=\"/docs/#api/en/materials/Material.onBeforeCompile\"><code class=\"notranslate\" translate=\"no\">Material.onBeforeCompile</code></a>.</p>\n<pre class=\"prettyprint showlinemods notranslate lang-js\" translate=\"no\">{\n  const loader = new THREE.TextureLoader();\n  const geometry = new THREE.SphereGeometry(1, 64, 32);\n\n  const indexTexture = loader.load('resources/data/world/country-index-texture.png', render);\n  indexTexture.minFilter = THREE.NearestFilter;\n  indexTexture.magFilter = THREE.NearestFilter;\n\n  const pickingMaterial = new THREE.MeshBasicMaterial({map: indexTexture});\n  pickingScene.add(new THREE.Mesh(geometry, pickingMaterial));\n\n+  const fragmentShaderReplacements = [\n+    {\n+      from: '#include &lt;common&gt;',\n+      to: <code class=\"notranslate\" translate=\"no\">\n+        #include &lt;common&gt;\n+        uniform sampler2D indexTexture;\n+        uniform sampler2D paletteTexture;\n+        uniform float paletteTextureWidth;\n+      </code>,\n+    },\n+    {\n+      from: '#include &lt;color_fragment&gt;',\n+      to: <code class=\"notranslate\" translate=\"no\">\n+        #include &lt;color_fragment&gt;\n+        {\n+          vec4 indexColor = texture2D(indexTexture, vUv);\n+          float index = indexColor.r * 255.0 + indexColor.g * 255.0 * 256.0;\n+          vec2 paletteUV = vec2((index + 0.5) / paletteTextureWidth, 0.5);\n+          vec4 paletteColor = texture2D(paletteTexture, paletteUV);\n+          // diffuseColor.rgb += paletteColor.rgb;   // white outlines\n+          diffuseColor.rgb = paletteColor.rgb - diffuseColor.rgb;  // black outlines\n+        }\n+      </code>,\n+    },\n+  ];\n\n  const texture = loader.load('resources/data/world/country-outlines-4k.png', render);\n  const material = new THREE.MeshBasicMaterial({map: texture});\n+  material.onBeforeCompile = function(shader) {\n+    fragmentShaderReplacements.forEach((rep) =&gt; {\n+      shader.fragmentShader = shader.fragmentShader.replace(rep.from, rep.to);\n+    });\n+  };\n  scene.add(new THREE.Mesh(geometry, material));\n}\n</pre>\n<p>Above can see above we add 3 uniforms, <code class=\"notranslate\" translate=\"no\">indexTexture</code>, <code class=\"notranslate\" translate=\"no\">paletteTexture</code>,\nand <code class=\"notranslate\" translate=\"no\">paletteTextureWidth</code>. We get a color from the <code class=\"notranslate\" translate=\"no\">indexTexture</code>\nand convert it to an index. <code class=\"notranslate\" translate=\"no\">vUv</code> is the texture coordinates provided by\nthree.js. We then use that index to get a color out of the palette texture.\nWe then mix the result with the current <code class=\"notranslate\" translate=\"no\">diffuseColor</code>. The <code class=\"notranslate\" translate=\"no\">diffuseColor</code>\nat this point is our black and white outline texture so if we add the 2 colors\nwe'll get white outlines. If we subtract the current diffuse color we'll get\nblack outlines.</p>\n<p>Before we can render we need to setup the palette texture\nand these 3 uniforms.</p>\n<p>For the palette texture it just needs to be wide enough to\nhold one color per country + one for the ocean (id = 0).\nThere are 240 something countries. We could wait until the\nlist of countries loads to get an exact number or look it up.\nThere's not much harm in just picking some larger number so\nlet's choose 512.</p>\n<p>Here's the code to create the palette texture</p>\n<pre class=\"prettyprint showlinemods notranslate lang-js\" translate=\"no\">const maxNumCountries = 512;\nconst paletteTextureWidth = maxNumCountries;\nconst paletteTextureHeight = 1;\nconst palette = new Uint8Array(paletteTextureWidth * 4);\nconst paletteTexture = new THREE.DataTexture(\n    palette, paletteTextureWidth, paletteTextureHeight);\npaletteTexture.minFilter = THREE.NearestFilter;\npaletteTexture.magFilter = THREE.NearestFilter;\n</pre>\n<p>A <a href=\"/docs/#api/en/textures/DataTexture\"><code class=\"notranslate\" translate=\"no\">DataTexture</code></a> let's us give a texture raw data. In this case\nwe're giving it 512 RGBA colors, 4 bytes each where each byte is\nred, green, and blue respectively using values that go from 0 to 255.</p>\n<p>Let's fill it with random colors just to see it work</p>\n<pre class=\"prettyprint showlinemods notranslate lang-js\" translate=\"no\">for (let i = 1; i &lt; palette.length; ++i) {\n  palette[i] = Math.random() * 256;\n}\n// set the ocean color (index #0)\npalette.set([100, 200, 255, 255], 0);\npaletteTexture.needsUpdate = true;\n</pre>\n<p>Anytime we want three.js to update the palette texture with\nthe contents of the <code class=\"notranslate\" translate=\"no\">palette</code> array we need to set <code class=\"notranslate\" translate=\"no\">paletteTexture.needsUpdate</code>\nto <code class=\"notranslate\" translate=\"no\">true</code>.</p>\n<p>And then we still need to set the uniforms on the material.</p>\n<pre class=\"prettyprint showlinemods notranslate lang-js\" translate=\"no\">const geometry = new THREE.SphereGeometry(1, 64, 32);\nconst material = new THREE.MeshBasicMaterial({map: texture});\nmaterial.onBeforeCompile = function(shader) {\n  fragmentShaderReplacements.forEach((rep) =&gt; {\n    shader.fragmentShader = shader.fragmentShader.replace(rep.from, rep.to);\n  });\n+  shader.uniforms.paletteTexture = {value: paletteTexture};\n+  shader.uniforms.indexTexture = {value: indexTexture};\n+  shader.uniforms.paletteTextureWidth = {value: paletteTextureWidth};\n};\nscene.add(new THREE.Mesh(geometry, material));\n</pre>\n<p>and with that we get randomly colored countries.</p>\n<p></p><div translate=\"no\" class=\"threejs_example_container notranslate\">\n  <div></div>\n  <a class=\"threejs_center\" href=\"/manual/examples/indexed-textures-random-colors.html\" target=\"_blank\">click here to open in a separate window</a>\n</div>\n\n<p></p>\n<p>Now that we can see the index and palette textures are working\nlet's manipulate the palette for highlighting.</p>\n<p>First let's make function that will let us pass in a three.js\nstyle color and give us values we can put in the palette texture.</p>\n<pre class=\"prettyprint showlinemods notranslate lang-js\" translate=\"no\">const tempColor = new THREE.Color();\nfunction get255BasedColor(color) {\n  tempColor.set(color);\n  const base = tempColor.toArray().map(v =&gt; v * 255);\n  base.push(255); // alpha\n  return base;\n}\n</pre>\n<p>Calling it like this <code class=\"notranslate\" translate=\"no\">color = get255BasedColor('red')</code> will\nreturn an array like <code class=\"notranslate\" translate=\"no\">[255, 0, 0, 255]</code>.</p>\n<p>Next let's use it to make a few colors and fill out the\npalette.</p>\n<pre class=\"prettyprint showlinemods notranslate lang-js\" translate=\"no\">const selectedColor = get255BasedColor('red');\nconst unselectedColor = get255BasedColor('#444');\nconst oceanColor = get255BasedColor('rgb(100,200,255)');\nresetPalette();\n\nfunction setPaletteColor(index, color) {\n  palette.set(color, index * 4);\n}\n\nfunction resetPalette() {\n  // make all colors the unselected color\n  for (let i = 1; i &lt; maxNumCountries; ++i) {\n    setPaletteColor(i, unselectedColor);\n  }\n\n  // set the ocean color (index #0)\n  setPaletteColor(0, oceanColor);\n  paletteTexture.needsUpdate = true;\n}\n</pre>\n<p>Now let's use those functions to update the palette when a country\nis selected</p>\n<pre class=\"prettyprint showlinemods notranslate lang-js\" translate=\"no\">function getCanvasRelativePosition(event) {\n  const rect = canvas.getBoundingClientRect();\n  return {\n    x: (event.clientX - rect.left) * canvas.width  / rect.width,\n    y: (event.clientY - rect.top ) * canvas.height / rect.height,\n  };\n}\n\nfunction pickCountry(event) {\n  // exit if we have not loaded the data yet\n  if (!countryInfos) {\n    return;\n  }\n\n  const position = getCanvasRelativePosition(event);\n  const id = pickHelper.pick(position, pickingScene, camera);\n  if (id &gt; 0) {\n    const countryInfo = countryInfos[id - 1];\n    const selected = !countryInfo.selected;\n    if (selected &amp;&amp; !event.shiftKey &amp;&amp; !event.ctrlKey &amp;&amp; !event.metaKey) {\n      unselectAllCountries();\n    }\n    numCountriesSelected += selected ? 1 : -1;\n    countryInfo.selected = selected;\n+    setPaletteColor(id, selected ? selectedColor : unselectedColor);\n+    paletteTexture.needsUpdate = true;\n  } else if (numCountriesSelected) {\n    unselectAllCountries();\n  }\n  requestRenderIfNotRequested();\n}\n\nfunction unselectAllCountries() {\n  numCountriesSelected = 0;\n  countryInfos.forEach((countryInfo) =&gt; {\n    countryInfo.selected = false;\n  });\n+  resetPalette();\n}\n</pre>\n<p>and we that we should be able to highlight 1 or more countries.</p>\n<p></p><div translate=\"no\" class=\"threejs_example_container notranslate\">\n  <div></div>\n  <a class=\"threejs_center\" href=\"/manual/examples/indexed-textures-picking-and-highlighting.html\" target=\"_blank\">click here to open in a separate window</a>\n</div>\n\n<p></p>\n<p>That seems to work!</p>\n<p>One minor thing is we can't spin the globe without changing\nthe selection state. If we select a country and then want to\nrotate the globe the selection will change.</p>\n<p>Let's try to fix that. Off the top of my head we can check 2 things.\nHow much time passed between clicking and letting go.\nAnother is did the user actually move the mouse. If the\ntime is short or if they didn't move the mouse then it\nwas probably a click. Otherwise they were probably trying\nto drag the globe.</p>\n<pre class=\"prettyprint showlinemods notranslate lang-js\" translate=\"no\">+const maxClickTimeMs = 200;\n+const maxMoveDeltaSq = 5 * 5;\n+const startPosition = {};\n+let startTimeMs;\n+\n+function recordStartTimeAndPosition(event) {\n+  startTimeMs = performance.now();\n+  const pos = getCanvasRelativePosition(event);\n+  startPosition.x = pos.x;\n+  startPosition.y = pos.y;\n+}\n\nfunction getCanvasRelativePosition(event) {\n  const rect = canvas.getBoundingClientRect();\n  return {\n    x: (event.clientX - rect.left) * canvas.width  / rect.width,\n    y: (event.clientY - rect.top ) * canvas.height / rect.height,\n  };\n}\n\nfunction pickCountry(event) {\n  // exit if we have not loaded the data yet\n  if (!countryInfos) {\n    return;\n  }\n\n+  // if it's been a moment since the user started\n+  // then assume it was a drag action, not a select action\n+  const clickTimeMs = performance.now() - startTimeMs;\n+  if (clickTimeMs &gt; maxClickTimeMs) {\n+    return;\n+  }\n+\n+  // if they moved assume it was a drag action\n+  const position = getCanvasRelativePosition(event);\n+  const moveDeltaSq = (startPosition.x - position.x) ** 2 +\n+                      (startPosition.y - position.y) ** 2;\n+  if (moveDeltaSq &gt; maxMoveDeltaSq) {\n+    return;\n+  }\n\n-  const position = {x: event.clientX, y: event.clientY};\n  const id = pickHelper.pick(position, pickingScene, camera);\n  if (id &gt; 0) {\n    const countryInfo = countryInfos[id - 1];\n    const selected = !countryInfo.selected;\n    if (selected &amp;&amp; !event.shiftKey &amp;&amp; !event.ctrlKey &amp;&amp; !event.metaKey) {\n      unselectAllCountries();\n    }\n    numCountriesSelected += selected ? 1 : -1;\n    countryInfo.selected = selected;\n    setPaletteColor(id, selected ? selectedColor : unselectedColor);\n    paletteTexture.needsUpdate = true;\n  } else if (numCountriesSelected) {\n    unselectAllCountries();\n  }\n  requestRenderIfNotRequested();\n}\n\nfunction unselectAllCountries() {\n  numCountriesSelected = 0;\n  countryInfos.forEach((countryInfo) =&gt; {\n    countryInfo.selected = false;\n  });\n  resetPalette();\n}\n\n+canvas.addEventListener('pointerdown', recordStartTimeAndPosition);\ncanvas.addEventListener('pointerup', pickCountry);\n</pre>\n<p>and with those changes it <em>seems</em> like it works to me.</p>\n<p></p><div translate=\"no\" class=\"threejs_example_container notranslate\">\n  <div></div>\n  <a class=\"threejs_center\" href=\"/manual/examples/indexed-textures-picking-debounced.html\" target=\"_blank\">click here to open in a separate window</a>\n</div>\n\n<p></p>\n<p>I'm not a UX expert so I'd love to hear if there is a better\nsolution.</p>\n<p>I hope that gave you some idea of how indexed graphics can be useful\nand how you can modify the shaders three.js makes to add simple features.\nHow to use GLSL, the language the shaders are written in, is too much for\nthis article. There are a few links to some info in\n<a href=\"post-processing.html\">the article on post processing</a>.</p>\n\n        </div>\n      </div>\n    </div>",
    "success": true,
    "error": "",
    "crawled_at": 1786376809.300204,
    "is_shell": false
  },
  {
    "url": "https://threejs.org/manual/en/canvas-textures.html",
    "title": "Canvas Textures",
    "content": "<div class=\"container\">\n      <div class=\"lesson-title\">\n        <h1>Canvas Textures</h1>\n      </div>\n      <div class=\"lesson\">\n        <div class=\"lesson-main\">\n          <p>This article continues from <a href=\"textures.html\">the article on textures</a>.\nIf you haven't read that yet you should probably start there.</p>\n<p>In <a href=\"textures.html\">the previous article on textures</a> we mostly used\nimage files for textures. Sometimes though we want to generate a texture\nat runtime. One way to do this is to use a <a href=\"/docs/#api/en/textures/CanvasTexture\"><code class=\"notranslate\" translate=\"no\">CanvasTexture</code></a>.</p>\n<p>A canvas texture takes a <code class=\"notranslate\" translate=\"no\">&lt;canvas&gt;</code> as its input. If you don't know how to\ndraw with the 2D canvas API on a canvas <a href=\"https://developer.mozilla.org/en-US/docs/Web/API/Canvas_API/Tutorial\">there's a good tutorial on MDN</a>.</p>\n<p>Let's make a simple canvas program. Here's one that draws dots at random places in random colors.</p>\n<pre class=\"prettyprint showlinemods notranslate lang-js\" translate=\"no\">const ctx = document.createElement('canvas').getContext('2d');\ndocument.body.appendChild(ctx.canvas);\nctx.canvas.width = 256;\nctx.canvas.height = 256;\nctx.fillStyle = '#FFF';\nctx.fillRect(0, 0, ctx.canvas.width, ctx.canvas.height);\n\nfunction randInt(min, max) {\n  if (max === undefined) {\n    max = min;\n    min = 0;\n  }\n  return Math.random() * (max - min) + min | 0;\n}\n\nfunction drawRandomDot() {\n  ctx.fillStyle = <code class=\"notranslate\" translate=\"no\">#${randInt(0x1000000).toString(16).padStart(6, '0')}</code>;\n  ctx.beginPath();\n\n  const x = randInt(256);\n  const y = randInt(256);\n  const radius = randInt(10, 64);\n  ctx.arc(x, y, radius, 0, Math.PI * 2);\n  ctx.fill();\n}\n\nfunction render() {\n  drawRandomDot();\n  requestAnimationFrame(render);\n}\nrequestAnimationFrame(render);\n</pre>\n<p>it's pretty straight forward.</p>\n<p></p><div translate=\"no\" class=\"threejs_example_container notranslate\">\n  <div></div>\n  <a class=\"threejs_center\" href=\"/manual/examples/canvas-random-dots.html\" target=\"_blank\">click here to open in a separate window</a>\n</div>\n\n<p></p>\n<p>Now let's use it to texture something. We'll start with the example of texturing\na cube from <a href=\"textures.html\">the previous article</a>.\nWe'll remove the code that loads an image and instead use\nour canvas by creating a <a href=\"/docs/#api/en/textures/CanvasTexture\"><code class=\"notranslate\" translate=\"no\">CanvasTexture</code></a> and passing it the canvas we created.</p>\n<pre class=\"prettyprint showlinemods notranslate lang-js\" translate=\"no\">const cubes = [];  // just an array we can use to rotate the cubes\n-const loader = new THREE.TextureLoader();\n-\n+const ctx = document.createElement('canvas').getContext('2d');\n+ctx.canvas.width = 256;\n+ctx.canvas.height = 256;\n+ctx.fillStyle = '#FFF';\n+ctx.fillRect(0, 0, ctx.canvas.width, ctx.canvas.height);\n+const texture = new THREE.CanvasTexture(ctx.canvas);\n\nconst material = new THREE.MeshBasicMaterial({\n-  map: loader.load('resources/images/wall.jpg'),\n+  map: texture,\n});\nconst cube = new THREE.Mesh(geometry, material);\nscene.add(cube);\ncubes.push(cube);  // add to our list of cubes to rotate\n</pre>\n<p>And then call the code to draw a random dot in our render loop</p>\n<pre class=\"prettyprint showlinemods notranslate lang-js\" translate=\"no\">function render(time) {\n  time *= 0.001;\n\n  if (resizeRendererToDisplaySize(renderer)) {\n    const canvas = renderer.domElement;\n    camera.aspect = canvas.clientWidth / canvas.clientHeight;\n    camera.updateProjectionMatrix();\n  }\n\n+  drawRandomDot();\n+  texture.needsUpdate = true;\n\n  cubes.forEach((cube, ndx) =&gt; {\n    const speed = .2 + ndx * .1;\n    const rot = time * speed;\n    cube.rotation.x = rot;\n    cube.rotation.y = rot;\n  });\n\n  renderer.render(scene, camera);\n\n  requestAnimationFrame(render);\n}\n</pre>\n<p>The only extra thing we need to do is set the <code class=\"notranslate\" translate=\"no\">needsUpdate</code> property\nof the <a href=\"/docs/#api/en/textures/CanvasTexture\"><code class=\"notranslate\" translate=\"no\">CanvasTexture</code></a> to tell three.js to update the texture with\nthe latest contents of the canvas.</p>\n<p>And with that we have a canvas textured cube</p>\n<p></p><div translate=\"no\" class=\"threejs_example_container notranslate\">\n  <div></div>\n  <a class=\"threejs_center\" href=\"/manual/examples/canvas-textured-cube.html\" target=\"_blank\">click here to open in a separate window</a>\n</div>\n\n<p></p>\n<p>Note that if you want to use three.js to draw into the canvas you're\nbetter off using a <code class=\"notranslate\" translate=\"no\">RenderTarget</code> which is covered in <a href=\"rendertargets.html\">this article</a>.</p>\n<p>A common use case for canvas textures is to provide text in a scene.\nFor example if you wanted to put a person's name on their character's\nbadge you might use a canvas texture to texture the badge.</p>\n<p>Let's make a scene with 3 people and give each person a badge\nor label.</p>\n<p>Let's take the example above and remove all the cube related\nstuff. Then let's set the background to white and add two <a href=\"lights.html\">lights</a>.</p>\n<pre class=\"prettyprint showlinemods notranslate lang-js\" translate=\"no\">const scene = new THREE.Scene();\n+scene.background = new THREE.Color('white');\n+\n+function addLight(position) {\n+  const color = 0xFFFFFF;\n+  const intensity = 1;\n+  const light = new THREE.DirectionalLight(color, intensity);\n+  light.position.set(...position);\n+  scene.add(light);\n+  scene.add(light.target);\n+}\n+addLight([-3, 1, 1]);\n+addLight([ 2, 1, .5]);\n</pre>\n<p>Let's make some code to make a label using canvas 2D</p>\n<pre class=\"prettyprint showlinemods notranslate lang-js\" translate=\"no\">+function makeLabelCanvas(size, name) {\n+  const borderSize = 2;\n+  const ctx = document.createElement('canvas').getContext('2d');\n+  const font =  <code class=\"notranslate\" translate=\"no\">${size}px bold sans-serif</code>;\n+  ctx.font = font;\n+  // measure how long the name will be\n+  const doubleBorderSize = borderSize * 2;\n+  const width = ctx.measureText(name).width + doubleBorderSize;\n+  const height = size + doubleBorderSize;\n+  ctx.canvas.width = width;\n+  ctx.canvas.height = height;\n+\n+  // need to set font again after resizing canvas\n+  ctx.font = font;\n+  ctx.textBaseline = 'top';\n+\n+  ctx.fillStyle = 'blue';\n+  ctx.fillRect(0, 0, width, height);\n+  ctx.fillStyle = 'white';\n+  ctx.fillText(name, borderSize, borderSize);\n+\n+  return ctx.canvas;\n+}\n</pre>\n<p>Then we'll make simple people from a cylinder for the body, a sphere\nfor the head, and a plane for the label.</p>\n<p>First let's make the shared geometry.</p>\n<pre class=\"prettyprint showlinemods notranslate lang-js\" translate=\"no\">+const bodyRadiusTop = .4;\n+const bodyRadiusBottom = .2;\n+const bodyHeight = 2;\n+const bodyRadialSegments = 6;\n+const bodyGeometry = new THREE.CylinderGeometry(\n+    bodyRadiusTop, bodyRadiusBottom, bodyHeight, bodyRadialSegments);\n+\n+const headRadius = bodyRadiusTop * 0.8;\n+const headLonSegments = 12;\n+const headLatSegments = 5;\n+const headGeometry = new THREE.SphereGeometry(\n+    headRadius, headLonSegments, headLatSegments);\n+\n+const labelGeometry = new THREE.PlaneGeometry(1, 1);\n</pre>\n<p>Then let's make a function to build a person from these\nparts.</p>\n<pre class=\"prettyprint showlinemods notranslate lang-js\" translate=\"no\">+function makePerson(x, size, name, color) {\n+  const canvas = makeLabelCanvas(size, name);\n+  const texture = new THREE.CanvasTexture(canvas);\n+  // because our canvas is likely not a power of 2\n+  // in both dimensions set the filtering appropriately.\n+  texture.minFilter = THREE.LinearFilter;\n+  texture.wrapS = THREE.ClampToEdgeWrapping;\n+  texture.wrapT = THREE.ClampToEdgeWrapping;\n+\n+  const labelMaterial = new THREE.MeshBasicMaterial({\n+    map: texture,\n+    side: THREE.DoubleSide,\n+    transparent: true,\n+  });\n+  const bodyMaterial = new THREE.MeshPhongMaterial({\n+    color,\n+    flatShading: true,\n+  });\n+\n+  const root = new THREE.Object3D();\n+  root.position.x = x;\n+\n+  const body = new THREE.Mesh(bodyGeometry, bodyMaterial);\n+  root.add(body);\n+  body.position.y = bodyHeight / 2;\n+\n+  const head = new THREE.Mesh(headGeometry, bodyMaterial);\n+  root.add(head);\n+  head.position.y = bodyHeight + headRadius * 1.1;\n+\n+  const label = new THREE.Mesh(labelGeometry, labelMaterial);\n+  root.add(label);\n+  label.position.y = bodyHeight * 4 / 5;\n+  label.position.z = bodyRadiusTop * 1.01;\n+\n+  // if units are meters then 0.01 here makes size\n+  // of the label into centimeters.\n+  const labelBaseScale = 0.01;\n+  label.scale.x = canvas.width  * labelBaseScale;\n+  label.scale.y = canvas.height * labelBaseScale;\n+\n+  scene.add(root);\n+  return root;\n+}\n</pre>\n<p>You can see above we put the body, head, and label on a root\n<a href=\"/docs/#api/en/core/Object3D\"><code class=\"notranslate\" translate=\"no\">Object3D</code></a> and adjust their positions. This would let us move the\nroot object if we wanted to move the people. The body is 2 units\nhigh. If 1 unit equals 1 meter then the code above tries to\nmake the label in centimeters so they will be size centimeters\ntall and however wide is needed to fit the text.</p>\n<p>We can then make people with labels</p>\n<pre class=\"prettyprint showlinemods notranslate lang-js\" translate=\"no\">+makePerson(-3, 32, 'Purple People Eater', 'purple');\n+makePerson(-0, 32, 'Green Machine', 'green');\n+makePerson(+3, 32, 'Red Menace', 'red');\n</pre>\n<p>What's left is to add some <a href=\"/docs/#examples/controls/OrbitControls\"><code class=\"notranslate\" translate=\"no\">OrbitControls</code></a> so we can move\nthe camera.</p>\n<pre class=\"prettyprint showlinemods notranslate lang-js\" translate=\"no\">import * as THREE from 'three';\n+import {OrbitControls} from 'three/addons/controls/OrbitControls.js';\n</pre>\n<pre class=\"prettyprint showlinemods notranslate lang-js\" translate=\"no\">const fov = 75;\nconst aspect = 2;  // the canvas default\nconst near = 0.1;\n-const far = 5;\n+const far = 50;\nconst camera = new THREE.PerspectiveCamera(fov, aspect, near, far);\n-camera.position.z = 2;\n+camera.position.set(0, 2, 5);\n\n+const controls = new OrbitControls(camera, canvas);\n+controls.target.set(0, 2, 0);\n+controls.update();\n</pre>\n<p>and we get simple labels.</p>\n<p></p><div translate=\"no\" class=\"threejs_example_container notranslate\">\n  <div></div>\n  <a class=\"threejs_center\" href=\"/manual/examples/canvas-textured-labels.html\" target=\"_blank\">click here to open in a separate window</a>\n</div>\n\n<p></p>\n<p>Some things to notice.</p>\n<ul>\n<li>If you zoom in the labels get pretty low-res.</li>\n</ul>\n<p>There is no easy solution. There are more complex font\nrendering techniques but I know of no plugin solutions.\nPlus they will require the user download font data which\nwould be slow.</p>\n<p>One solution is to increase the resolution of the labels.\nTry setting the size passed into to double what it is now\nand setting <code class=\"notranslate\" translate=\"no\">labelBaseScale</code> to half what it currently is.</p>\n<ul>\n<li>The labels get longer the longer the name.</li>\n</ul>\n<p>If you wanted to fix this you'd instead choose a fixed sized\nlabel and then squish the text.</p>\n<p>This is pretty easy. Pass in a base width and scale the text to fit that\nwidth like this</p>\n<pre class=\"prettyprint showlinemods notranslate lang-js\" translate=\"no\">-function makeLabelCanvas(size, name) {\n+function makeLabelCanvas(baseWidth, size, name) {\n  const borderSize = 2;\n  const ctx = document.createElement('canvas').getContext('2d');\n  const font =  <code class=\"notranslate\" translate=\"no\">${size}px bold sans-serif</code>;\n  ctx.font = font;\n  // measure how long the name will be\n+  const textWidth = ctx.measureText(name).width;\n\n  const doubleBorderSize = borderSize * 2;\n-  const width = ctx.measureText(name).width + doubleBorderSize;\n+  const width = baseWidth + doubleBorderSize;\n  const height = size + doubleBorderSize;\n  ctx.canvas.width = width;\n  ctx.canvas.height = height;\n\n  // need to set font again after resizing canvas\n  ctx.font = font;\n-  ctx.textBaseline = 'top';\n+  ctx.textBaseline = 'middle';\n+  ctx.textAlign = 'center';\n\n  ctx.fillStyle = 'blue';\n  ctx.fillRect(0, 0, width, height);\n\n+  // scale to fit but don't stretch\n+  const scaleFactor = Math.min(1, baseWidth / textWidth);\n+  ctx.translate(width / 2, height / 2);\n+  ctx.scale(scaleFactor, 1);\n  ctx.fillStyle = 'white';\n  ctx.fillText(name, borderSize, borderSize);\n\n  return ctx.canvas;\n}\n</pre>\n<p>Then we can pass in a width for the labels</p>\n<pre class=\"prettyprint showlinemods notranslate lang-js\" translate=\"no\">-function makePerson(x, size, name, color) {\n-  const canvas = makeLabelCanvas(size, name);\n+function makePerson(x, labelWidth, size, name, color) {\n+  const canvas = makeLabelCanvas(labelWidth, size, name);\n\n...\n\n}\n\n-makePerson(-3, 32, 'Purple People Eater', 'purple');\n-makePerson(-0, 32, 'Green Machine', 'green');\n-makePerson(+3, 32, 'Red Menace', 'red');\n+makePerson(-3, 150, 32, 'Purple People Eater', 'purple');\n+makePerson(-0, 150, 32, 'Green Machine', 'green');\n+makePerson(+3, 150, 32, 'Red Menace', 'red');\n</pre>\n<p>and we get labels where the text is centered and scaled to fit</p>\n<p></p><div translate=\"no\" class=\"threejs_example_container notranslate\">\n  <div></div>\n  <a class=\"threejs_center\" href=\"/manual/examples/canvas-textured-labels-scale-to-fit.html\" target=\"_blank\">click here to open in a separate window</a>\n</div>\n\n<p></p>\n<p>Above we used a new canvas for each texture. Whether or not to use a\ncanvas per texture is up to you. If you need to update them often then\nhaving one canvas per texture is probably the best option. If they are\nrarely or never updated then you can choose to use a single canvas\nfor multiple textures by forcing three.js to use the texture.\nLet's change the code above to do just that.</p>\n<pre class=\"prettyprint showlinemods notranslate lang-js\" translate=\"no\">+const ctx = document.createElement('canvas').getContext('2d');\n\nfunction makeLabelCanvas(baseWidth, size, name) {\n  const borderSize = 2;\n-  const ctx = document.createElement('canvas').getContext('2d');\n  const font =  <code class=\"notranslate\" translate=\"no\">${size}px bold sans-serif</code>;\n\n  ...\n\n}\n\n+const forceTextureInitialization = function() {\n+  const material = new THREE.MeshBasicMaterial();\n+  const geometry = new THREE.PlaneGeometry();\n+  const scene = new THREE.Scene();\n+  scene.add(new THREE.Mesh(geometry, material));\n+  const camera = new THREE.Camera();\n+\n+  return function forceTextureInitialization(texture) {\n+    material.map = texture;\n+    renderer.render(scene, camera);\n+  };\n+}();\n\nfunction makePerson(x, labelWidth, size, name, color) {\n  const canvas = makeLabelCanvas(labelWidth, size, name);\n  const texture = new THREE.CanvasTexture(canvas);\n  // because our canvas is likely not a power of 2\n  // in both dimensions set the filtering appropriately.\n  texture.minFilter = THREE.LinearFilter;\n  texture.wrapS = THREE.ClampToEdgeWrapping;\n  texture.wrapT = THREE.ClampToEdgeWrapping;\n+  forceTextureInitialization(texture);\n\n  ...\n</pre>\n<p></p><div translate=\"no\" class=\"threejs_example_container notranslate\">\n  <div></div>\n  <a class=\"threejs_center\" href=\"/manual/examples/canvas-textured-labels-one-canvas.html\" target=\"_blank\">click here to open in a separate window</a>\n</div>\n\n<p></p>\n<p>Another issue is that the labels don't always face the camera. If you're using\nlabels as badges that's probably a good thing. If you're using labels to put\nnames over players in a 3D game maybe you want the labels to always face the camera.\nWe'll cover how to do that in <a href=\"billboards.html\">an article on billboards</a>.</p>\n<p>For labels in particular, <a href=\"align-html-elements-to-3d.html\">another solution is to use HTML</a>.\nThe labels in this article are <em>inside the 3D world</em> which is good if you want them\nto be hidden by other objects where as <a href=\"align-html-elements-to-3d.html\">HTML labels</a> are always on top.</p>\n\n        </div>\n      </div>\n    </div>",
    "success": true,
    "error": "",
    "crawled_at": 1786376809.300204,
    "is_shell": false
  },
  {
    "url": "https://threejs.org/manual/en/billboards.html",
    "title": "Billboards",
    "content": "<div class=\"container\">\n      <div class=\"lesson-title\">\n        <h1>Billboards</h1>\n      </div>\n      <div class=\"lesson\">\n        <div class=\"lesson-main\">\n          <p>In <a href=\"canvas-textures.html\">a previous article</a> we used a <a href=\"/docs/#api/en/textures/CanvasTexture\"><code class=\"notranslate\" translate=\"no\">CanvasTexture</code></a>\nto make labels / badges on characters. Sometimes we'd like to make labels or\nother things that always face the camera. Three.js provides the <a href=\"/docs/#api/en/objects/Sprite\"><code class=\"notranslate\" translate=\"no\">Sprite</code></a> and\n<a href=\"/docs/#api/en/materials/SpriteMaterial\"><code class=\"notranslate\" translate=\"no\">SpriteMaterial</code></a> to make this happen.</p>\n<p>Let's change the badge example from <a href=\"canvas-textures.html\">the article on canvas textures</a>\nto use <a href=\"/docs/#api/en/objects/Sprite\"><code class=\"notranslate\" translate=\"no\">Sprite</code></a> and <a href=\"/docs/#api/en/materials/SpriteMaterial\"><code class=\"notranslate\" translate=\"no\">SpriteMaterial</code></a></p>\n<pre class=\"prettyprint showlinemods notranslate lang-js\" translate=\"no\">function makePerson(x, labelWidth, size, name, color) {\n  const canvas = makeLabelCanvas(labelWidth, size, name);\n  const texture = new THREE.CanvasTexture(canvas);\n  // because our canvas is likely not a power of 2\n  // in both dimensions set the filtering appropriately.\n  texture.minFilter = THREE.LinearFilter;\n  texture.wrapS = THREE.ClampToEdgeWrapping;\n  texture.wrapT = THREE.ClampToEdgeWrapping;\n\n-  const labelMaterial = new THREE.MeshBasicMaterial({\n+  const labelMaterial = new THREE.SpriteMaterial({\n    map: texture,\n-    side: THREE.DoubleSide,\n    transparent: true,\n  });\n\n  const root = new THREE.Object3D();\n  root.position.x = x;\n\n  const body = new THREE.Mesh(bodyGeometry, bodyMaterial);\n  root.add(body);\n  body.position.y = bodyHeight / 2;\n\n  const head = new THREE.Mesh(headGeometry, bodyMaterial);\n  root.add(head);\n  head.position.y = bodyHeight + headRadius * 1.1;\n\n-  const label = new THREE.Mesh(labelGeometry, labelMaterial);\n+  const label = new THREE.Sprite(labelMaterial);\n  root.add(label);\n  label.position.y = bodyHeight * 4 / 5;\n  label.position.z = bodyRadiusTop * 1.01;\n</pre>\n<p>and the labels now always face the camera</p>\n<p></p><div translate=\"no\" class=\"threejs_example_container notranslate\">\n  <div></div>\n  <a class=\"threejs_center\" href=\"/manual/examples/billboard-labels-w-sprites.html\" target=\"_blank\">click here to open in a separate window</a>\n</div>\n\n<p></p>\n<p>One problem is from certain angles the labels now intersect the\ncharacters. </p>\n<div class=\"threejs_center\"><img src=\"../resources/images/billboard-label-z-issue.png\" style=\"width: 455px;\"></div>\n\n<p>We can move the position of the labels to fix.</p>\n<pre class=\"prettyprint showlinemods notranslate lang-js\" translate=\"no\">+// if units are meters then 0.01 here makes size\n+// of the label into centimeters.\n+const labelBaseScale = 0.01;\nconst label = new THREE.Sprite(labelMaterial);\nroot.add(label);\n-label.position.y = bodyHeight * 4 / 5;\n-label.position.z = bodyRadiusTop * 1.01;\n+label.position.y = head.position.y + headRadius + size * labelBaseScale;\n\n-// if units are meters then 0.01 here makes size\n-// of the label into centimeters.\n-const labelBaseScale = 0.01;\nlabel.scale.x = canvas.width  * labelBaseScale;\nlabel.scale.y = canvas.height * labelBaseScale;\n</pre>\n<p></p><div translate=\"no\" class=\"threejs_example_container notranslate\">\n  <div></div>\n  <a class=\"threejs_center\" href=\"/manual/examples/billboard-labels-w-sprites-adjust-height.html\" target=\"_blank\">click here to open in a separate window</a>\n</div>\n\n<p></p>\n<p>Another thing we can do with billboards is draw facades.</p>\n<p>Instead of drawing 3D objects we draw 2D planes with an image\nof 3D objects. This is often faster than drawing 3D objects.</p>\n<p>For example let's make a scene with grid of trees. We'll make each\ntree from a cylinder for the base and a cone for the top.</p>\n<p>First we make the cone and cylinder geometry and materials that\nall the trees will share</p>\n<pre class=\"prettyprint showlinemods notranslate lang-js\" translate=\"no\">const trunkRadius = .2;\nconst trunkHeight = 1;\nconst trunkRadialSegments = 12;\nconst trunkGeometry = new THREE.CylinderGeometry(\n    trunkRadius, trunkRadius, trunkHeight, trunkRadialSegments);\n\nconst topRadius = trunkRadius * 4;\nconst topHeight = trunkHeight * 2;\nconst topSegments = 12;\nconst topGeometry = new THREE.ConeGeometry(\n    topRadius, topHeight, topSegments);\n\nconst trunkMaterial = new THREE.MeshPhongMaterial({color: 'brown'});\nconst topMaterial = new THREE.MeshPhongMaterial({color: 'green'});\n</pre>\n<p>Then we'll make a function that makes a <a href=\"/docs/#api/en/objects/Mesh\"><code class=\"notranslate\" translate=\"no\">Mesh</code></a> each for the trunk and top\nof a tree and parents both to an <a href=\"/docs/#api/en/core/Object3D\"><code class=\"notranslate\" translate=\"no\">Object3D</code></a>.</p>\n<pre class=\"prettyprint showlinemods notranslate lang-js\" translate=\"no\">function makeTree(x, z) {\n  const root = new THREE.Object3D();\n  const trunk = new THREE.Mesh(trunkGeometry, trunkMaterial);\n  trunk.position.y = trunkHeight / 2;\n  root.add(trunk);\n\n  const top = new THREE.Mesh(topGeometry, topMaterial);\n  top.position.y = trunkHeight + topHeight / 2;\n  root.add(top);\n\n  root.position.set(x, 0, z);\n  scene.add(root);\n\n  return root;\n}\n</pre>\n<p>Then we'll make a loop to place a grid of trees.</p>\n<pre class=\"prettyprint showlinemods notranslate lang-js\" translate=\"no\">for (let z = -50; z &lt;= 50; z += 10) {\n  for (let x = -50; x &lt;= 50; x += 10) {\n    makeTree(x, z);\n  }\n}\n</pre>\n<p>Let's also add a ground plane while we're at it</p>\n<pre class=\"prettyprint showlinemods notranslate lang-js\" translate=\"no\">// add ground\n{\n  const size = 400;\n  const geometry = new THREE.PlaneGeometry(size, size);\n  const material = new THREE.MeshPhongMaterial({color: 'gray'});\n  const mesh = new THREE.Mesh(geometry, material);\n  mesh.rotation.x = Math.PI * -0.5;\n  scene.add(mesh);\n}\n</pre>\n<p>and change the background to light blue</p>\n<pre class=\"prettyprint showlinemods notranslate lang-js\" translate=\"no\">const scene = new THREE.Scene();\n-scene.background = new THREE.Color('white');\n+scene.background = new THREE.Color('lightblue');\n</pre>\n<p>and we get a grid of trees</p>\n<p></p><div translate=\"no\" class=\"threejs_example_container notranslate\">\n  <div></div>\n  <a class=\"threejs_center\" href=\"/manual/examples/billboard-trees-no-billboards.html\" target=\"_blank\">click here to open in a separate window</a>\n</div>\n\n<p></p>\n<p>There are 11x11 or 121 trees. Each tree is made from a 12 polygon\ncone and a 48 polygon trunk so each tree is 60 polygons. 121 * 60\nis 7260 polygons. That's not that many but of course a more detailed\n3D tree might be 1000-3000 polygons. If they were 3000 polygons each\nthen 121 trees would be 363000 polygons to draw.</p>\n<p>Using facades we can bring that number down.</p>\n<p>We could manually create a facade in some painting program but let's write\nsome code to try to generate one.</p>\n<p>Let's write some code to render an object to a texture\nusing a <code class=\"notranslate\" translate=\"no\">RenderTarget</code>. We covered rendering to a <code class=\"notranslate\" translate=\"no\">RenderTarget</code>\nin <a href=\"rendertargets.html\">the article on render targets</a>.</p>\n<pre class=\"prettyprint showlinemods notranslate lang-js\" translate=\"no\">function frameArea(sizeToFitOnScreen, boxSize, boxCenter, camera) {\n  const halfSizeToFitOnScreen = sizeToFitOnScreen * 0.5;\n  const halfFovY = THREE.MathUtils.degToRad(camera.fov * .5);\n  const distance = halfSizeToFitOnScreen / Math.tan(halfFovY);\n\n  camera.position.copy(boxCenter);\n  camera.position.z += distance;\n\n  // pick some near and far values for the frustum that\n  // will contain the box.\n  camera.near = boxSize / 100;\n  camera.far = boxSize * 100;\n\n  camera.updateProjectionMatrix();\n}\n\nfunction makeSpriteTexture(textureSize, obj) {\n  const rt = new THREE.WebGLRenderTarget(textureSize, textureSize);\n\n  const aspect = 1;  // because the render target is square\n  const camera = new THREE.PerspectiveCamera(fov, aspect, near, far);\n\n  scene.add(obj);\n\n  // compute the box that contains obj\n  const box = new THREE.Box3().setFromObject(obj);\n\n  const boxSize = box.getSize(new THREE.Vector3());\n  const boxCenter = box.getCenter(new THREE.Vector3());\n\n  // set the camera to frame the box\n  const fudge = 1.1;\n  const size = Math.max(...boxSize.toArray()) * fudge;\n  frameArea(size, size, boxCenter, camera);\n\n  renderer.autoClear = false;\n  renderer.setRenderTarget(rt);\n  renderer.render(scene, camera);\n  renderer.setRenderTarget(null);\n  renderer.autoClear = true;\n\n  scene.remove(obj);\n\n  return {\n    position: boxCenter.multiplyScalar(fudge),\n    scale: size,\n    texture: rt.texture,\n  };\n}\n</pre>\n<p>Some things to note about the code above:</p>\n<p>We're using the field of view (<code class=\"notranslate\" translate=\"no\">fov</code>) defined above this code.</p>\n<p>We're computing a box that contains the tree the same way\nwe did in <a href=\"load-obj.html\">the article on loading a .obj file</a>\nwith a few minor changes.</p>\n<p>We call <code class=\"notranslate\" translate=\"no\">frameArea</code> again adapted <a href=\"load-obj.html\">the article on loading a .obj file</a>.\nIn this case we compute how far the camera needs to be away from the object\ngiven its field of view to contain the object. We then position the camera -z that distance\nfrom the center of the box that contains the object.</p>\n<p>We multiply the size we want to fit by 1.1 (<code class=\"notranslate\" translate=\"no\">fudge</code>) to make sure the tree fits\ncompletely in the render target. The issue here is the size we're using to\ncalculate if the object fits in the camera's view is not taking into account\nthat the very edges of the object will end up dipping outside area we\ncalculated. We could compute how to make 100% of the box fit but that would\nwaste space as well so instead we just <em>fudge</em> it.</p>\n<p>Then we render to the render target and remove the object from\nthe scene. </p>\n<p>It's important to note we need the lights in the scene but we\nneed to make sure nothing else is in the scene.</p>\n<p>We also need to not set a background color on the scene</p>\n<pre class=\"prettyprint showlinemods notranslate lang-js\" translate=\"no\">const scene = new THREE.Scene();\n-scene.background = new THREE.Color('lightblue');\n</pre>\n<p>Finally we've made the texture we return it and the position and scale we\nneed to make the facade so that it will appear to be in the same place.</p>\n<p>We then make a tree and call this code and pass it in</p>\n<pre class=\"prettyprint showlinemods notranslate lang-js\" translate=\"no\">// make billboard texture\nconst tree = makeTree(0, 0);\nconst facadeSize = 64;\nconst treeSpriteInfo = makeSpriteTexture(facadeSize, tree);\n</pre>\n<p>We can then make a grid of facades instead of a grid of tree models</p>\n<pre class=\"prettyprint showlinemods notranslate lang-js\" translate=\"no\">+function makeSprite(spriteInfo, x, z) {\n+  const {texture, offset, scale} = spriteInfo;\n+  const mat = new THREE.SpriteMaterial({\n+    map: texture,\n+    transparent: true,\n+  });\n+  const sprite = new THREE.Sprite(mat);\n+  scene.add(sprite);\n+  sprite.position.set(\n+      offset.x + x,\n+      offset.y,\n+      offset.z + z);\n+  sprite.scale.set(scale, scale, scale);\n+}\n\nfor (let z = -50; z &lt;= 50; z += 10) {\n  for (let x = -50; x &lt;= 50; x += 10) {\n-    makeTree(x, z);\n+    makeSprite(treeSpriteInfo, x, z);\n  }\n}\n</pre>\n<p>In the code above we apply the offset and scale needed to position the facade so it\nappears the same place the original tree would have appeared.</p>\n<p>Now that we're done making the tree facade texture we can set the background again</p>\n<pre class=\"prettyprint showlinemods notranslate lang-js\" translate=\"no\">scene.background = new THREE.Color('lightblue');\n</pre>\n<p>and now we get a scene of tree facades</p>\n<p></p><div translate=\"no\" class=\"threejs_example_container notranslate\">\n  <div></div>\n  <a class=\"threejs_center\" href=\"/manual/examples/billboard-trees-static-billboards.html\" target=\"_blank\">click here to open in a separate window</a>\n</div>\n\n<p></p>\n<p>Compare to the trees models above and you can see it looks fairly similar.\nWe used a low-res texture, just 64x64 pixels so the facades are blocky.\nYou could increase the resolution. Often facades are used only in the far\ndistance when they are fairly small so a low-res texture is enough and\nit saves on drawing detailed trees that are only a few pixels big when\nfar away.</p>\n<p>Another issue is we are only viewing the tree from one side. This is often\nsolved by rendering more facades, say from 8 directions around the object\nand then setting which facade to show based on which direction the camera\nis looking at the facade.</p>\n<p>Whether or not you use facades is up to you but hopefully this article\ngave you some ideas and suggested some solutions if you decide to use them.</p>\n\n        </div>\n      </div>\n    </div>",
    "success": true,
    "error": "",
    "crawled_at": 1786376809.300204,
    "is_shell": false
  },
  {
    "url": "https://threejs.org/manual/en/cleanup.html",
    "title": "Cleanup",
    "content": "<div class=\"container\">\n      <div class=\"lesson-title\">\n        <h1>Cleanup</h1>\n      </div>\n      <div class=\"lesson\">\n        <div class=\"lesson-main\">\n          <p>Three.js apps often use lots of memory. A 3D model\nmight be 1 to 20 meg memory for all of its vertices.\nA model might use many textures that even if they are\ncompressed into jpg files they have to be expanded\nto their uncompressed form to use. Each 1024x1024\ntexture takes 4 to 6meg of memory.</p>\n<p>Most three.js apps load resources at init time and\nthen use those resources forever until the page is\nclosed. But, what if you want to load and change resources\nover time?</p>\n<p>Unlike most JavaScript, three.js can not automatically\nclean these resources up. The browser will clean them\nup if you switch pages but otherwise it's up to you\nto manage them. This is an issue of how WebGL is designed\nand so three.js has no recourse but to pass on the\nresponsibility to free resources back to you.</p>\n<p>You free three.js resource this by calling the <code class=\"notranslate\" translate=\"no\">dispose</code> function on\n<a href=\"textures.html\">textures</a>,\n<a href=\"primitives.html\">geometries</a>, and\n<a href=\"materials.html\">materials</a>.</p>\n<p>You could do this manually. At the start you might create\nsome of these resources</p>\n<pre class=\"prettyprint showlinemods notranslate lang-js\" translate=\"no\">const boxGeometry = new THREE.BoxGeometry(...);\nconst boxTexture = textureLoader.load(...);\nconst boxMaterial = new THREE.MeshPhongMaterial({map: texture});\n</pre>\n<p>and then when you're done with them you'd free them</p>\n<pre class=\"prettyprint showlinemods notranslate lang-js\" translate=\"no\">boxGeometry.dispose();\nboxTexture.dispose();\nboxMaterial.dispose();\n</pre>\n<p>As you use more and more resources that would get more and\nmore tedious.</p>\n<p>To help remove some of the tedium let's make a class to track\nthe resources. We'll then ask that class to do the cleanup\nfor us.</p>\n<p>Here's a first pass at such a class</p>\n<pre class=\"prettyprint showlinemods notranslate lang-js\" translate=\"no\">class ResourceTracker {\n  constructor() {\n    this.resources = new Set();\n  }\n  track(resource) {\n    if (resource.dispose) {\n      this.resources.add(resource);\n    }\n    return resource;\n  }\n  untrack(resource) {\n    this.resources.delete(resource);\n  }\n  dispose() {\n    for (const resource of this.resources) {\n      resource.dispose();\n    }\n    this.resources.clear();\n  }\n}\n</pre>\n<p>Let's use this class with the first example from <a href=\"textures.html\">the article on textures</a>.\nWe can create an instance of this class</p>\n<pre class=\"prettyprint showlinemods notranslate lang-js\" translate=\"no\">const resTracker = new ResourceTracker();\n</pre>\n<p>and then just to make it easier to use let's create a bound function for the <code class=\"notranslate\" translate=\"no\">track</code> method</p>\n<pre class=\"prettyprint showlinemods notranslate lang-js\" translate=\"no\">const resTracker = new ResourceTracker();\n+const track = resTracker.track.bind(resTracker);\n</pre>\n<p>Now to use it we just need to call <code class=\"notranslate\" translate=\"no\">track</code> with for each geometry, texture, and material\nwe create</p>\n<pre class=\"prettyprint showlinemods notranslate lang-js\" translate=\"no\">const boxWidth = 1;\nconst boxHeight = 1;\nconst boxDepth = 1;\n-const geometry = new THREE.BoxGeometry(boxWidth, boxHeight, boxDepth);\n+const geometry = track(new THREE.BoxGeometry(boxWidth, boxHeight, boxDepth));\n\nconst cubes = [];  // an array we can use to rotate the cubes\nconst loader = new THREE.TextureLoader();\n\n-const material = new THREE.MeshBasicMaterial({\n-  map: loader.load('resources/images/wall.jpg'),\n-});\n+const material = track(new THREE.MeshBasicMaterial({\n+  map: track(loader.load('resources/images/wall.jpg')),\n+}));\nconst cube = new THREE.Mesh(geometry, material);\nscene.add(cube);\ncubes.push(cube);  // add to our list of cubes to rotate\n</pre>\n<p>And then to free them we'd want to remove the cubes from the scene\nand then call <code class=\"notranslate\" translate=\"no\">resTracker.dispose</code></p>\n<pre class=\"prettyprint showlinemods notranslate lang-js\" translate=\"no\">for (const cube of cubes) {\n  scene.remove(cube);\n}\ncubes.length = 0;  // clears the cubes array\nresTracker.dispose();\n</pre>\n<p>That would work but I find having to remove the cubes from the\nscene kind of tedious. Let's add that functionality to the <code class=\"notranslate\" translate=\"no\">ResourceTracker</code>.</p>\n<pre class=\"prettyprint showlinemods notranslate lang-js\" translate=\"no\">class ResourceTracker {\n  constructor() {\n    this.resources = new Set();\n  }\n  track(resource) {\n-    if (resource.dispose) {\n+    if (resource.dispose || resource instanceof THREE.Object3D) {\n      this.resources.add(resource);\n    }\n    return resource;\n  }\n  untrack(resource) {\n    this.resources.delete(resource);\n  }\n  dispose() {\n    for (const resource of this.resources) {\n-      resource.dispose();\n+      if (resource instanceof THREE.Object3D) {\n+        if (resource.parent) {\n+          resource.parent.remove(resource);\n+        }\n+      }\n+      if (resource.dispose) {\n+        resource.dispose();\n+      }\n+    }\n    this.resources.clear();\n  }\n}\n</pre>\n<p>And now we can track the cubes</p>\n<pre class=\"prettyprint showlinemods notranslate lang-js\" translate=\"no\">const material = track(new THREE.MeshBasicMaterial({\n  map: track(loader.load('resources/images/wall.jpg')),\n}));\nconst cube = track(new THREE.Mesh(geometry, material));\nscene.add(cube);\ncubes.push(cube);  // add to our list of cubes to rotate\n</pre>\n<p>We no longer need the code to remove the cubes from the scene.</p>\n<pre class=\"prettyprint showlinemods notranslate lang-js\" translate=\"no\">-for (const cube of cubes) {\n-  scene.remove(cube);\n-}\ncubes.length = 0;  // clears the cube array\nresTracker.dispose();\n</pre>\n<p>Let's arrange this code so that we can re-add the cube,\ntexture, and material.</p>\n<pre class=\"prettyprint showlinemods notranslate lang-js\" translate=\"no\">const scene = new THREE.Scene();\n*const cubes = [];  // just an array we can use to rotate the cubes\n\n+function addStuffToScene() {\n  const resTracker = new ResourceTracker();\n  const track = resTracker.track.bind(resTracker);\n\n  const boxWidth = 1;\n  const boxHeight = 1;\n  const boxDepth = 1;\n  const geometry = track(new THREE.BoxGeometry(boxWidth, boxHeight, boxDepth));\n\n  const loader = new THREE.TextureLoader();\n\n  const material = track(new THREE.MeshBasicMaterial({\n    map: track(loader.load('resources/images/wall.jpg')),\n  }));\n  const cube = track(new THREE.Mesh(geometry, material));\n  scene.add(cube);\n  cubes.push(cube);  // add to our list of cubes to rotate\n+  return resTracker;\n+}\n</pre>\n<p>And then let's write some code to add and remove things over time.</p>\n<pre class=\"prettyprint showlinemods notranslate lang-js\" translate=\"no\">function waitSeconds(seconds = 0) {\n  return new Promise(resolve =&gt; setTimeout(resolve, seconds * 1000));\n}\n\nasync function process() {\n  for (;;) {\n    const resTracker = addStuffToScene();\n    await wait(2);\n    cubes.length = 0;  // remove the cubes\n    resTracker.dispose();\n    await wait(1);\n  }\n}\nprocess();\n</pre>\n<p>This code will create the cube, texture and material, wait for 2 seconds, then dispose of them and wait for 1 second\nand repeat.</p>\n<p></p><div translate=\"no\" class=\"threejs_example_container notranslate\">\n  <div></div>\n  <a class=\"threejs_center\" href=\"/manual/examples/cleanup-simple.html\" target=\"_blank\">click here to open in a separate window</a>\n</div>\n\n<p></p>\n<p>So that seems to work.</p>\n<p>For a loaded file though it's a little more work. Most loaders only return an <a href=\"/docs/#api/en/core/Object3D\"><code class=\"notranslate\" translate=\"no\">Object3D</code></a>\nas a root of the hierarchy of objects they load so we need to discover what all the resources\nare.</p>\n<p>Let's update our <code class=\"notranslate\" translate=\"no\">ResourceTracker</code> to try to do that.</p>\n<p>First we'll check if the object is an <a href=\"/docs/#api/en/core/Object3D\"><code class=\"notranslate\" translate=\"no\">Object3D</code></a> then track its geometry, material, and children</p>\n<pre class=\"prettyprint showlinemods notranslate lang-js\" translate=\"no\">class ResourceTracker {\n  constructor() {\n    this.resources = new Set();\n  }\n  track(resource) {\n    if (resource.dispose || resource instanceof THREE.Object3D) {\n      this.resources.add(resource);\n    }\n+    if (resource instanceof THREE.Object3D) {\n+      this.track(resource.geometry);\n+      this.track(resource.material);\n+      this.track(resource.children);\n+    }\n    return resource;\n  }\n  ...\n}\n</pre>\n<p>Now, because any of <code class=\"notranslate\" translate=\"no\">resource.geometry</code>, <code class=\"notranslate\" translate=\"no\">resource.material</code>, and <code class=\"notranslate\" translate=\"no\">resource.children</code>\nmight be null or undefined we'll check at the top of <code class=\"notranslate\" translate=\"no\">track</code>.</p>\n<pre class=\"prettyprint showlinemods notranslate lang-js\" translate=\"no\">class ResourceTracker {\n  constructor() {\n    this.resources = new Set();\n  }\n  track(resource) {\n+    if (!resource) {\n+      return resource;\n+    }\n\n    if (resource.dispose || resource instanceof THREE.Object3D) {\n      this.resources.add(resource);\n    }\n    if (resource instanceof THREE.Object3D) {\n      this.track(resource.geometry);\n      this.track(resource.material);\n      this.track(resource.children);\n    }\n    return resource;\n  }\n  ...\n}\n</pre>\n<p>Also because <code class=\"notranslate\" translate=\"no\">resource.children</code> is an array and because <code class=\"notranslate\" translate=\"no\">resource.material</code> can be\nan array let's check for arrays</p>\n<pre class=\"prettyprint showlinemods notranslate lang-js\" translate=\"no\">class ResourceTracker {\n  constructor() {\n    this.resources = new Set();\n  }\n  track(resource) {\n    if (!resource) {\n      return resource;\n    }\n\n+    // handle children and when material is an array of materials.\n+    if (Array.isArray(resource)) {\n+      resource.forEach(resource =&gt; this.track(resource));\n+      return resource;\n+    }\n\n    if (resource.dispose || resource instanceof THREE.Object3D) {\n      this.resources.add(resource);\n    }\n    if (resource instanceof THREE.Object3D) {\n      this.track(resource.geometry);\n      this.track(resource.material);\n      this.track(resource.children);\n    }\n    return resource;\n  }\n  ...\n}\n</pre>\n<p>And finally we need to walk the properties and uniforms\nof a material looking for textures.</p>\n<pre class=\"prettyprint showlinemods notranslate lang-js\" translate=\"no\">class ResourceTracker {\n  constructor() {\n    this.resources = new Set();\n  }\n  track(resource) {\n    if (!resource) {\n      return resource;\n    }\n\n*    // handle children and when material is an array of materials or\n*    // uniform is array of textures\n    if (Array.isArray(resource)) {\n      resource.forEach(resource =&gt; this.track(resource));\n      return resource;\n    }\n\n    if (resource.dispose || resource instanceof THREE.Object3D) {\n      this.resources.add(resource);\n    }\n    if (resource instanceof THREE.Object3D) {\n      this.track(resource.geometry);\n      this.track(resource.material);\n      this.track(resource.children);\n-    }\n+    } else if (resource instanceof THREE.Material) {\n+      // We have to check if there are any textures on the material\n+      for (const value of Object.values(resource)) {\n+        if (value instanceof THREE.Texture) {\n+          this.track(value);\n+        }\n+      }\n+      // We also have to check if any uniforms reference textures or arrays of textures\n+      if (resource.uniforms) {\n+        for (const value of Object.values(resource.uniforms)) {\n+          if (value) {\n+            const uniformValue = value.value;\n+            if (uniformValue instanceof THREE.Texture ||\n+                Array.isArray(uniformValue)) {\n+              this.track(uniformValue);\n+            }\n+          }\n+        }\n+      }\n+    }\n    return resource;\n  }\n  ...\n}\n</pre>\n<p>And with that let's take an example from <a href=\"load-gltf.html\">the article on loading gltf files</a>\nand make it load and free files.</p>\n<pre class=\"prettyprint showlinemods notranslate lang-js\" translate=\"no\">const gltfLoader = new GLTFLoader();\nfunction loadGLTF(url) {\n  return new Promise((resolve, reject) =&gt; {\n    gltfLoader.load(url, resolve, undefined, reject);\n  });\n}\n\nfunction waitSeconds(seconds = 0) {\n  return new Promise(resolve =&gt; setTimeout(resolve, seconds * 1000));\n}\n\nconst fileURLs = [\n  'resources/models/cartoon_lowpoly_small_city_free_pack/scene.gltf',\n  'resources/models/3dbustchallange_submission/scene.gltf',\n  'resources/models/mountain_landscape/scene.gltf',\n  'resources/models/simple_house_scene/scene.gltf',\n];\n\nasync function loadFiles() {\n  for (;;) {\n    for (const url of fileURLs) {\n      const resMgr = new ResourceTracker();\n      const track = resMgr.track.bind(resMgr);\n      const gltf = await loadGLTF(url);\n      const root = track(gltf.scene);\n      scene.add(root);\n\n      // compute the box that contains all the stuff\n      // from root and below\n      const box = new THREE.Box3().setFromObject(root);\n\n      const boxSize = box.getSize(new THREE.Vector3()).length();\n      const boxCenter = box.getCenter(new THREE.Vector3());\n\n      // set the camera to frame the box\n      frameArea(boxSize * 1.1, boxSize, boxCenter, camera);\n\n      await waitSeconds(2);\n      renderer.render(scene, camera);\n\n      resMgr.dispose();\n\n      await waitSeconds(1);\n\n    }\n  }\n}\nloadFiles();\n</pre>\n<p>and we get</p>\n<p></p><div translate=\"no\" class=\"threejs_example_container notranslate\">\n  <div></div>\n  <a class=\"threejs_center\" href=\"/manual/examples/cleanup-loaded-files.html\" target=\"_blank\">click here to open in a separate window</a>\n</div>\n\n<p></p>\n<p>Some notes about the code.</p>\n<p>If we wanted to load 2 or more files at once and free them at\nanytime we would use one <code class=\"notranslate\" translate=\"no\">ResourceTracker</code> per file.</p>\n<p>Above we are only tracking <code class=\"notranslate\" translate=\"no\">gltf.scene</code> right after loading.\nBased on our current implementation of <code class=\"notranslate\" translate=\"no\">ResourceTracker</code> that\nwill track all the resources just loaded. If we added more\nthings to the scene we need to decide whether or not to track them.</p>\n<p>For example let's say after we loaded a character we put a tool\nin their hand by making the tool a child of their hand. As it is\nthat tool will not be freed. I'm guessing more often than not\nthis is what we want. </p>\n<p>That brings up a point. Originally when I first wrote the <code class=\"notranslate\" translate=\"no\">ResourceTracker</code>\nabove I walked through everything inside the <code class=\"notranslate\" translate=\"no\">dispose</code> method instead of <code class=\"notranslate\" translate=\"no\">track</code>.\nIt was only later as I thought about the tool as a child of hand case above\nthat it became clear that tracking exactly what to free in <code class=\"notranslate\" translate=\"no\">track</code> was more\nflexible and arguably more correct since we could then track what was loaded\nfrom the file rather than just freeing the state of the scene graph later.</p>\n<p>I honestly am not 100% happy with <code class=\"notranslate\" translate=\"no\">ResourceTracker</code>. Doing things this\nway is not common in 3D engines. We shouldn't have to guess what\nresources were loaded, we should know. It would be nice if three.js\nchanged so that all file loaders returned some standard object with\nreferences to all the resources loaded. At least at the moment,\nthree.js doesn't give us any more info when loading a scene so this\nsolution seems to work.</p>\n<p>I hope you find this example useful or at least a good reference for what is\nrequired to free resources in three.js</p>\n\n        </div>\n      </div>\n    </div>",
    "success": true,
    "error": "",
    "crawled_at": 1786376809.300204,
    "is_shell": false
  },
  {
    "url": "https://threejs.org/manual/en/voxel-geometry.html",
    "title": "Voxel(Minecraft Like) Geometry",
    "content": "<div class=\"container\">\n      <div class=\"lesson-title\">\n        <h1>Voxel(Minecraft Like) Geometry</h1>\n      </div>\n      <div class=\"lesson\">\n        <div class=\"lesson-main\">\n          <p>I've seen this topic come up more than once in various places.\nThat is basically, \"How do I make a voxel display like Minecraft\".</p>\n<p>Most people first attempt this by making a cube geometry and then\nmaking a mesh at each voxel position. Just for fun I tried\nthis. I made a 16777216 element <code class=\"notranslate\" translate=\"no\">Uint8Array</code> to represent\na 256x256x256 cube of voxels.</p>\n<pre class=\"prettyprint showlinemods notranslate lang-js\" translate=\"no\">const cellSize = 256;\nconst cell = new Uint8Array(cellSize * cellSize * cellSize);\n</pre>\n<p>I then made a single layer with a kind of hills of\nsine waves like this</p>\n<pre class=\"prettyprint showlinemods notranslate lang-js\" translate=\"no\">for (let y = 0; y &lt; cellSize; ++y) {\n  for (let z = 0; z &lt; cellSize; ++z) {\n    for (let x = 0; x &lt; cellSize; ++x) {\n      const height = (Math.sin(x / cellSize * Math.PI * 4) + Math.sin(z / cellSize * Math.PI * 6)) * 20 + cellSize / 2;\n      if (height &gt; y &amp;&amp; height &lt; y + 1) {\n        const offset = y * cellSize * cellSize +\n                       z * cellSize +\n                       x;\n        cell[offset] = 1;\n      }\n    }\n  }\n}\n</pre>\n<p>I then walked through all the cells and if they were not\n0 I created a mesh with a cube.</p>\n<pre class=\"prettyprint showlinemods notranslate lang-js\" translate=\"no\">const geometry = new THREE.BoxGeometry(1, 1, 1);\nconst material = new THREE.MeshPhongMaterial({color: 'green'});\n\nfor (let y = 0; y &lt; cellSize; ++y) {\n  for (let z = 0; z &lt; cellSize; ++z) {\n    for (let x = 0; x &lt; cellSize; ++x) {\n      const offset = y * cellSize * cellSize +\n                     z * cellSize +\n                     x;\n      const block = cell[offset];\n      const mesh = new THREE.Mesh(geometry, material);\n      mesh.position.set(x, y, z);\n      scene.add(mesh);\n    }\n  }\n}\n</pre>\n<p>The rest of the code is based on the example from\n<a href=\"rendering-on-demand.html\">the article on rendering on demand</a>.</p>\n<p></p><div translate=\"no\" class=\"threejs_example_container notranslate\">\n  <div></div>\n  <a class=\"threejs_center\" href=\"/manual/examples/voxel-geometry-separate-cubes.html\" target=\"_blank\">click here to open in a separate window</a>\n</div>\n\n<p></p>\n<p>It takes a while to start and if you try to move the camera\nit's likely too slow. Like <a href=\"optimize-lots-of-objects.html\">the article on how to optimize lots of objects</a>\nthe problem is there are just way too many objects. 256x256\nis 65536 boxes!</p>\n<p>Using <a href=\"rendering-on-demand.html\">the technique of merging the geometry</a>\nwill fix the issue for this example but what if instead of just making\na single layer we filled in everything below the ground with voxel.\nIn other words change the loop filling in the voxels to this</p>\n<pre class=\"prettyprint showlinemods notranslate lang-js\" translate=\"no\">for (let y = 0; y &lt; cellSize; ++y) {\n  for (let z = 0; z &lt; cellSize; ++z) {\n    for (let x = 0; x &lt; cellSize; ++x) {\n      const height = (Math.sin(x / cellSize * Math.PI * 4) + Math.sin(z / cellSize * Math.PI * 6)) * 20 + cellSize / 2;\n-      if (height &gt; y &amp;&amp; height &lt; y + 1) {\n+      if (height &lt; y + 1) {\n        const offset = y * cellSize * cellSize +\n                       z * cellSize +\n                       x;\n        cell[offset] = 1;\n      }\n    }\n  }\n}\n</pre>\n<p>I tried it once just to see the results. It churned for\nabout a minute and then crashed with <em>out of memory</em> 😅</p>\n<p>There are several issues but the biggest issue is\nwe're making all these faces inside the cubes that\nwe can actually never see.</p>\n<p>In other words lets say we have a box of voxels\n3x2x2. By merging cubes we're getting this</p>\n<div class=\"spread\">\n  <div data-diagram=\"mergedCubes\" style=\"height: 300px;\"></div>\n</div>\n\n<p>but we really want this</p>\n<div class=\"spread\">\n  <div data-diagram=\"culledCubes\" style=\"height: 300px;\"></div>\n</div>\n\n<p>In the top box there are faces between the voxels. Faces\nthat are a waste since they can't be seen. It's not just\none face between each voxel, there are 2 faces, one for\neach voxel facing its neighbor that are a waste. All these extra faces,\nespecially for a large volume of voxels will kill performance.</p>\n<p>It should be clear that we can't just merge geometry.\nWe need to build it ourselves, taking into account that\nif a voxel has an adjacent neighbor it doesn't need the\nface facing that neighbor.</p>\n<p>The next issue is that 256x256x256 is just too big. 16meg is a lot of memory and\nif nothing else in much of the space nothing is there so that's a lot of wasted\nmemory. It's also a huge number of voxels, 16 million! That's too much to\nconsider at once.</p>\n<p>A solution is to divide the area into smaller areas.\nAny area that has nothing in it needs no storage. Let's use\n32x32x32 areas (that's 32k) and only create an area if something is in it.\nWe'll call one of these larger 32x32x32 areas a \"cell\".</p>\n<p>Let's break this into pieces. First let's make a class to manage the voxel data.</p>\n<pre class=\"prettyprint showlinemods notranslate lang-js\" translate=\"no\">class VoxelWorld {\n  constructor(cellSize) {\n    this.cellSize = cellSize;\n  }\n}\n</pre>\n<p>Let's make the function that makes geometry for a cell.\nLet's assume you pass in a cell position.\nIn other words if you want the geometry for the cell that covers voxels (0-31x, 0-31y, 0-31z)\nthen you'd pass in 0,0,0. For the cell that covers voxels (32-63x, 0-31y, 0-31z) you'd\npass in 1,0,0.</p>\n<p>We need to be able to check the neighboring voxels so let's assume our class\nhas a function <code class=\"notranslate\" translate=\"no\">getVoxel</code> that given a voxel position returns the value of\nthe voxel there. In other words if you pass it 35,0,0 and the cellSize is 32\nit's going to look at cell 1,0,0 and in that cell it will look at voxel 3,0,0.\nUsing this function we can look at a voxel's neighboring voxels even if they\nhappen to be in neighboring cells.</p>\n<pre class=\"prettyprint showlinemods notranslate lang-js\" translate=\"no\">class VoxelWorld {\n  constructor(cellSize) {\n    this.cellSize = cellSize;\n  }\n+  generateGeometryDataForCell(cellX, cellY, cellZ) {\n+    const {cellSize} = this;\n+    const startX = cellX * cellSize;\n+    const startY = cellY * cellSize;\n+    const startZ = cellZ * cellSize;\n+\n+    for (let y = 0; y &lt; cellSize; ++y) {\n+      const voxelY = startY + y;\n+      for (let z = 0; z &lt; cellSize; ++z) {\n+        const voxelZ = startZ + z;\n+        for (let x = 0; x &lt; cellSize; ++x) {\n+          const voxelX = startX + x;\n+          const voxel = this.getVoxel(voxelX, voxelY, voxelZ);\n+          if (voxel) {\n+            for (const {dir} of VoxelWorld.faces) {\n+              const neighbor = this.getVoxel(\n+                  voxelX + dir[0],\n+                  voxelY + dir[1],\n+                  voxelZ + dir[2]);\n+              if (!neighbor) {\n+                // this voxel has no neighbor in this direction so we need a face\n+                // here.\n+              }\n+            }\n+          }\n+        }\n+      }\n+    }\n+  }\n}\n\n+VoxelWorld.faces = [\n+  { // left\n+    dir: [ -1,  0,  0, ],\n+  },\n+  { // right\n+    dir: [  1,  0,  0, ],\n+  },\n+  { // bottom\n+    dir: [  0, -1,  0, ],\n+  },\n+  { // top\n+    dir: [  0,  1,  0, ],\n+  },\n+  { // back\n+    dir: [  0,  0, -1, ],\n+  },\n+  { // front\n+    dir: [  0,  0,  1, ],\n+  },\n+];\n</pre>\n<p>So using the code above we know when we need a face. Let's generate the faces.</p>\n<pre class=\"prettyprint showlinemods notranslate lang-js\" translate=\"no\">class VoxelWorld {\n  constructor(cellSize) {\n    this.cellSize = cellSize;\n  }\n  generateGeometryDataForCell(cellX, cellY, cellZ) {\n    const {cellSize} = this;\n+    const positions = [];\n+    const normals = [];\n+    const indices = [];\n    const startX = cellX * cellSize;\n    const startY = cellY * cellSize;\n    const startZ = cellZ * cellSize;\n\n    for (let y = 0; y &lt; cellSize; ++y) {\n      const voxelY = startY + y;\n      for (let z = 0; z &lt; cellSize; ++z) {\n        const voxelZ = startZ + z;\n        for (let x = 0; x &lt; cellSize; ++x) {\n          const voxelX = startX + x;\n          const voxel = this.getVoxel(voxelX, voxelY, voxelZ);\n          if (voxel) {\n-            for (const {dir} of VoxelWorld.faces) {\n+            for (const {dir, corners} of VoxelWorld.faces) {\n              const neighbor = this.getVoxel(\n                  voxelX + dir[0],\n                  voxelY + dir[1],\n                  voxelZ + dir[2]);\n              if (!neighbor) {\n                // this voxel has no neighbor in this direction so we need a face.\n+                const ndx = positions.length / 3;\n+                for (const pos of corners) {\n+                  positions.push(pos[0] + x, pos[1] + y, pos[2] + z);\n+                  normals.push(...dir);\n+                }\n+                indices.push(\n+                  ndx, ndx + 1, ndx + 2,\n+                  ndx + 2, ndx + 1, ndx + 3,\n+                );\n              }\n            }\n          }\n        }\n      }\n    }\n+    return {\n+      positions,\n+      normals,\n+      indices,\n    };\n  }\n}\n\nVoxelWorld.faces = [\n  { // left\n    dir: [ -1,  0,  0, ],\n+    corners: [\n+      [ 0, 1, 0 ],\n+      [ 0, 0, 0 ],\n+      [ 0, 1, 1 ],\n+      [ 0, 0, 1 ],\n+    ],\n  },\n  { // right\n    dir: [  1,  0,  0, ],\n+    corners: [\n+      [ 1, 1, 1 ],\n+      [ 1, 0, 1 ],\n+      [ 1, 1, 0 ],\n+      [ 1, 0, 0 ],\n+    ],\n  },\n  { // bottom\n    dir: [  0, -1,  0, ],\n+    corners: [\n+      [ 1, 0, 1 ],\n+      [ 0, 0, 1 ],\n+      [ 1, 0, 0 ],\n+      [ 0, 0, 0 ],\n+    ],\n  },\n  { // top\n    dir: [  0,  1,  0, ],\n+    corners: [\n+      [ 0, 1, 1 ],\n+      [ 1, 1, 1 ],\n+      [ 0, 1, 0 ],\n+      [ 1, 1, 0 ],\n+    ],\n  },\n  { // back\n    dir: [  0,  0, -1, ],\n+    corners: [\n+      [ 1, 0, 0 ],\n+      [ 0, 0, 0 ],\n+      [ 1, 1, 0 ],\n+      [ 0, 1, 0 ],\n+    ],\n  },\n  { // front\n    dir: [  0,  0,  1, ],\n+    corners: [\n+      [ 0, 0, 1 ],\n+      [ 1, 0, 1 ],\n+      [ 0, 1, 1 ],\n+      [ 1, 1, 1 ],\n+    ],\n  },\n];\n</pre>\n<p>The code above would make basic geometry data for us. We just need to supply\nthe <code class=\"notranslate\" translate=\"no\">getVoxel</code> function. Let's start with just one hard coded cell.</p>\n<pre class=\"prettyprint showlinemods notranslate lang-js\" translate=\"no\">class VoxelWorld {\n  constructor(cellSize) {\n    this.cellSize = cellSize;\n+    this.cell = new Uint8Array(cellSize * cellSize * cellSize);\n  }\n+  getCellForVoxel(x, y, z) {\n+    const {cellSize} = this;\n+    const cellX = Math.floor(x / cellSize);\n+    const cellY = Math.floor(y / cellSize);\n+    const cellZ = Math.floor(z / cellSize);\n+    if (cellX !== 0 || cellY !== 0 || cellZ !== 0) {\n+      return null\n+    }\n+    return this.cell;\n+  }\n+  getVoxel(x, y, z) {\n+    const cell = this.getCellForVoxel(x, y, z);\n+    if (!cell) {\n+      return 0;\n+    }\n+    const {cellSize} = this;\n+    const voxelX = THREE.MathUtils.euclideanModulo(x, cellSize) | 0;\n+    const voxelY = THREE.MathUtils.euclideanModulo(y, cellSize) | 0;\n+    const voxelZ = THREE.MathUtils.euclideanModulo(z, cellSize) | 0;\n+    const voxelOffset = voxelY * cellSize * cellSize +\n+                        voxelZ * cellSize +\n+                        voxelX;\n+    return cell[voxelOffset];\n+  }\n  generateGeometryDataForCell(cellX, cellY, cellZ) {\n\n  ...\n}\n</pre>\n<p>This seems like it would work. Let's make a <code class=\"notranslate\" translate=\"no\">setVoxel</code> function\nso we can set some data.</p>\n<pre class=\"prettyprint showlinemods notranslate lang-js\" translate=\"no\">class VoxelWorld {\n  constructor(cellSize) {\n    this.cellSize = cellSize;\n    this.cell = new Uint8Array(cellSize * cellSize * cellSize);\n  }\n  getCellForVoxel(x, y, z) {\n    const {cellSize} = this;\n    const cellX = Math.floor(x / cellSize);\n    const cellY = Math.floor(y / cellSize);\n    const cellZ = Math.floor(z / cellSize);\n    if (cellX !== 0 || cellY !== 0 || cellZ !== 0) {\n      return null\n    }\n    return this.cell;\n  }\n+  setVoxel(x, y, z, v) {\n+    let cell = this.getCellForVoxel(x, y, z);\n+    if (!cell) {\n+      return;  // TODO: add a new cell?\n+    }\n+    const {cellSize} = this;\n+    const voxelX = THREE.MathUtils.euclideanModulo(x, cellSize) | 0;\n+    const voxelY = THREE.MathUtils.euclideanModulo(y, cellSize) | 0;\n+    const voxelZ = THREE.MathUtils.euclideanModulo(z, cellSize) | 0;\n+    const voxelOffset = voxelY * cellSize * cellSize +\n+                        voxelZ * cellSize +\n+                        voxelX;\n+    cell[voxelOffset] = v;\n+  }\n  getVoxel(x, y, z) {\n    const cell = this.getCellForVoxel(x, y, z);\n    if (!cell) {\n      return 0;\n    }\n    const {cellSize} = this;\n    const voxelX = THREE.MathUtils.euclideanModulo(x, cellSize) | 0;\n    const voxelY = THREE.MathUtils.euclideanModulo(y, cellSize) | 0;\n    const voxelZ = THREE.MathUtils.euclideanModulo(z, cellSize) | 0;\n    const voxelOffset = voxelY * cellSize * cellSize +\n                        voxelZ * cellSize +\n                        voxelX;\n    return cell[voxelOffset];\n  }\n  generateGeometryDataForCell(cellX, cellY, cellZ) {\n\n  ...\n}\n</pre>\n<p>Hmmm, I see a lot of repeated code. Let's fix that up</p>\n<pre class=\"prettyprint showlinemods notranslate lang-js\" translate=\"no\">class VoxelWorld {\n  constructor(cellSize) {\n    this.cellSize = cellSize;\n+    this.cellSliceSize = cellSize * cellSize;\n    this.cell = new Uint8Array(cellSize * cellSize * cellSize);\n  }\n  getCellForVoxel(x, y, z) {\n    const {cellSize} = this;\n    const cellX = Math.floor(x / cellSize);\n    const cellY = Math.floor(y / cellSize);\n    const cellZ = Math.floor(z / cellSize);\n    if (cellX !== 0 || cellY !== 0 || cellZ !== 0) {\n      return null;\n    }\n    return this.cell;\n  }\n+  computeVoxelOffset(x, y, z) {\n+    const {cellSize, cellSliceSize} = this;\n+    const voxelX = THREE.MathUtils.euclideanModulo(x, cellSize) | 0;\n+    const voxelY = THREE.MathUtils.euclideanModulo(y, cellSize) | 0;\n+    const voxelZ = THREE.MathUtils.euclideanModulo(z, cellSize) | 0;\n+    return voxelY * cellSliceSize +\n+           voxelZ * cellSize +\n+           voxelX;\n+  }\n  setVoxel(x, y, z, v) {\n    const cell = this.getCellForVoxel(x, y, z);\n    if (!cell) {\n      return;  // TODO: add a new cell?\n    }\n-    const {cellSize} = this;\n-    const voxelX = THREE.MathUtils.euclideanModulo(x, cellSize) | 0;\n-    const voxelY = THREE.MathUtils.euclideanModulo(y, cellSize) | 0;\n-    const voxelZ = THREE.MathUtils.euclideanModulo(z, cellSize) | 0;\n-    const voxelOffset = voxelY * cellSize * cellSize +\n-                        voxelZ * cellSize +\n-                        voxelX;\n+    const voxelOffset = this.computeVoxelOffset(x, y, z);\n    cell[voxelOffset] = v;\n  }\n  getVoxel(x, y, z) {\n    const cell = this.getCellForVoxel(x, y, z);\n    if (!cell) {\n      return 0;\n    }\n-    const {cellSize} = this;\n-    const voxelX = THREE.MathUtils.euclideanModulo(x, cellSize) | 0;\n-    const voxelY = THREE.MathUtils.euclideanModulo(y, cellSize) | 0;\n-    const voxelZ = THREE.MathUtils.euclideanModulo(z, cellSize) | 0;\n-    const voxelOffset = voxelY * cellSize * cellSize +\n-                        voxelZ * cellSize +\n-                        voxelX;\n+    const voxelOffset = this.computeVoxelOffset(x, y, z);\n    return cell[voxelOffset];\n  }\n  generateGeometryDataForCell(cellX, cellY, cellZ) {\n\n  ...\n}\n</pre>\n<p>Now let's make some code to fill out the first cell with voxels.</p>\n<pre class=\"prettyprint showlinemods notranslate lang-js\" translate=\"no\">const cellSize = 32;\n\nconst world = new VoxelWorld(cellSize);\n\nfor (let y = 0; y &lt; cellSize; ++y) {\n  for (let z = 0; z &lt; cellSize; ++z) {\n    for (let x = 0; x &lt; cellSize; ++x) {\n      const height = (Math.sin(x / cellSize * Math.PI * 2) + Math.sin(z / cellSize * Math.PI * 3)) * (cellSize / 6) + (cellSize / 2);\n      if (y &lt; height) {\n        world.setVoxel(x, y, z, 1);\n      }\n    }\n  }\n}\n</pre>\n<p>and some code to actually generate geometry like we covered in\n<a href=\"custom-buffergeometry.html\">the article on custom BufferGeometry</a>.</p>\n<pre class=\"prettyprint showlinemods notranslate lang-js\" translate=\"no\">const {positions, normals, indices} = world.generateGeometryDataForCell(0, 0, 0);\nconst geometry = new THREE.BufferGeometry();\nconst material = new THREE.MeshLambertMaterial({color: 'green'});\n\nconst positionNumComponents = 3;\nconst normalNumComponents = 3;\ngeometry.setAttribute(\n    'position',\n    new THREE.BufferAttribute(new Float32Array(positions), positionNumComponents));\ngeometry.setAttribute(\n    'normal',\n    new THREE.BufferAttribute(new Float32Array(normals), normalNumComponents));\ngeometry.setIndex(indices);\nconst mesh = new THREE.Mesh(geometry, material);\nscene.add(mesh);\n</pre>\n<p>let's try it</p>\n<p></p><div translate=\"no\" class=\"threejs_example_container notranslate\">\n  <div></div>\n  <a class=\"threejs_center\" href=\"/manual/examples/voxel-geometry-culled-faces.html\" target=\"_blank\">click here to open in a separate window</a>\n</div>\n\n<p></p>\n<p>That seems to be working! Okay, let's add in textures.</p>\n<p>Searching on the net I found <a href=\"https://www.minecraftforum.net/forums/mapping-and-modding-java-edition/resource-packs/1245961-16x-1-7-4-wip-flourish\">this set</a>\nof <a href=\"https://creativecommons.org/licenses/by-nc-sa/4.0/\">CC-BY-NC-SA</a> licensed minecraft textures\nby <a href=\"https://www.minecraftforum.net/members/Joshtimus\">Joshtimus</a>.\nI picked a few at random and built this <a href=\"https://www.google.com/search?q=texture+atlas\">texture atlas</a>.</p>\n<div class=\"threejs_center\"><img class=\"checkerboard\" src=\"../examples/resources/images/minecraft/flourish-cc-by-nc-sa.png\" style=\"width: 512px; image-rendering: pixelated;\"></div>\n\n<p>To make things simple they are arranged a voxel type per column\nwhere the top row is the side of a voxel. The 2nd row is\nthe top of voxel, and the 3rd row is the bottom of the voxel.</p>\n<p>Knowing that we can add info to our <code class=\"notranslate\" translate=\"no\">VoxelWorld.faces</code> data\nto specify for each face which row to use and the UVs to use\nfor that face.</p>\n<pre class=\"prettyprint showlinemods notranslate lang-js\" translate=\"no\">VoxelWorld.faces = [\n  { // left\n+    uvRow: 0,\n    dir: [ -1,  0,  0, ],\n    corners: [\n-      [ 0, 1, 0 ],\n-      [ 0, 0, 0 ],\n-      [ 0, 1, 1 ],\n-      [ 0, 0, 1 ],\n+      { pos: [ 0, 1, 0 ], uv: [ 0, 1 ], },\n+      { pos: [ 0, 0, 0 ], uv: [ 0, 0 ], },\n+      { pos: [ 0, 1, 1 ], uv: [ 1, 1 ], },\n+      { pos: [ 0, 0, 1 ], uv: [ 1, 0 ], },\n    ],\n  },\n  { // right\n+    uvRow: 0,\n    dir: [  1,  0,  0, ],\n    corners: [\n-      [ 1, 1, 1 ],\n-      [ 1, 0, 1 ],\n-      [ 1, 1, 0 ],\n-      [ 1, 0, 0 ],\n+      { pos: [ 1, 1, 1 ], uv: [ 0, 1 ], },\n+      { pos: [ 1, 0, 1 ], uv: [ 0, 0 ], },\n+      { pos: [ 1, 1, 0 ], uv: [ 1, 1 ], },\n+      { pos: [ 1, 0, 0 ], uv: [ 1, 0 ], },\n    ],\n  },\n  { // bottom\n+    uvRow: 1,\n    dir: [  0, -1,  0, ],\n    corners: [\n-      [ 1, 0, 1 ],\n-      [ 0, 0, 1 ],\n-      [ 1, 0, 0 ],\n-      [ 0, 0, 0 ],\n+      { pos: [ 1, 0, 1 ], uv: [ 1, 0 ], },\n+      { pos: [ 0, 0, 1 ], uv: [ 0, 0 ], },\n+      { pos: [ 1, 0, 0 ], uv: [ 1, 1 ], },\n+      { pos: [ 0, 0, 0 ], uv: [ 0, 1 ], },\n    ],\n  },\n  { // top\n+    uvRow: 2,\n    dir: [  0,  1,  0, ],\n    corners: [\n-      [ 0, 1, 1 ],\n-      [ 1, 1, 1 ],\n-      [ 0, 1, 0 ],\n-      [ 1, 1, 0 ],\n+      { pos: [ 0, 1, 1 ], uv: [ 1, 1 ], },\n+      { pos: [ 1, 1, 1 ], uv: [ 0, 1 ], },\n+      { pos: [ 0, 1, 0 ], uv: [ 1, 0 ], },\n+      { pos: [ 1, 1, 0 ], uv: [ 0, 0 ], },\n    ],\n  },\n  { // back\n+    uvRow: 0,\n    dir: [  0,  0, -1, ],\n    corners: [\n-      [ 1, 0, 0 ],\n-      [ 0, 0, 0 ],\n-      [ 1, 1, 0 ],\n-      [ 0, 1, 0 ],\n+      { pos: [ 1, 0, 0 ], uv: [ 0, 0 ], },\n+      { pos: [ 0, 0, 0 ], uv: [ 1, 0 ], },\n+      { pos: [ 1, 1, 0 ], uv: [ 0, 1 ], },\n+      { pos: [ 0, 1, 0 ], uv: [ 1, 1 ], },\n    ],\n  },\n  { // front\n+    uvRow: 0,\n    dir: [  0,  0,  1, ],\n    corners: [\n-      [ 0, 0, 1 ],\n-      [ 1, 0, 1 ],\n-      [ 0, 1, 1 ],\n-      [ 1, 1, 1 ],\n+      { pos: [ 0, 0, 1 ], uv: [ 0, 0 ], },\n+      { pos: [ 1, 0, 1 ], uv: [ 1, 0 ], },\n+      { pos: [ 0, 1, 1 ], uv: [ 0, 1 ], },\n+      { pos: [ 1, 1, 1 ], uv: [ 1, 1 ], },\n    ],\n  },\n];\n</pre>\n<p>And we can update the code to use that data. We need to\nknow the size of a tile in the texture atlas and the dimensions\nof the texture.</p>\n<pre class=\"prettyprint showlinemods notranslate lang-js\" translate=\"no\">class VoxelWorld {\n-  constructor(cellSize) {\n-    this.cellSize = cellSize;\n+  constructor(options) {\n+    this.cellSize = options.cellSize;\n+    this.tileSize = options.tileSize;\n+    this.tileTextureWidth = options.tileTextureWidth;\n+    this.tileTextureHeight = options.tileTextureHeight;\n+    const {cellSize} = this;\n+    this.cellSliceSize = cellSize * cellSize;\n+    this.cell = new Uint8Array(cellSize * cellSize * cellSize);\n  }\n\n  ...\n\n  generateGeometryDataForCell(cellX, cellY, cellZ) {\n-    const {cellSize} = this;\n+    const {cellSize, tileSize, tileTextureWidth, tileTextureHeight} = this;\n    const positions = [];\n    const normals = [];\n+    const uvs = [];\n    const indices = [];\n    const startX = cellX * cellSize;\n    const startY = cellY * cellSize;\n    const startZ = cellZ * cellSize;\n\n    for (let y = 0; y &lt; cellSize; ++y) {\n      const voxelY = startY + y;\n      for (let z = 0; z &lt; cellSize; ++z) {\n        const voxelZ = startZ + z;\n        for (let x = 0; x &lt; cellSize; ++x) {\n          const voxelX = startX + x;\n          const voxel = this.getVoxel(voxelX, voxelY, voxelZ);\n          if (voxel) {\n            const uvVoxel = voxel - 1;  // voxel 0 is sky so for UVs we start at 0\n            // There is a voxel here but do we need faces for it?\n-            for (const {dir, corners} of VoxelWorld.faces) {\n+            for (const {dir, corners, uvRow} of VoxelWorld.faces) {\n              const neighbor = this.getVoxel(\n                  voxelX + dir[0],\n                  voxelY + dir[1],\n                  voxelZ + dir[2]);\n              if (!neighbor) {\n                // this voxel has no neighbor in this direction so we need a face.\n                const ndx = positions.length / 3;\n-                for (const pos of corners) {\n+                for (const {pos, uv} of corners) {\n                  positions.push(pos[0] + x, pos[1] + y, pos[2] + z);\n                  normals.push(...dir);\n+                  uvs.push(\n+                        (uvVoxel +   uv[0]) * tileSize / tileTextureWidth,\n+                    1 - (uvRow + 1 - uv[1]) * tileSize / tileTextureHeight);\n                }\n                indices.push(\n                  ndx, ndx + 1, ndx + 2,\n                  ndx + 2, ndx + 1, ndx + 3,\n                );\n              }\n            }\n          }\n        }\n      }\n    }\n\n    return {\n      positions,\n      normals,\n      uvs,\n      indices,\n    };\n  }\n}\n</pre>\n<p>We then need to <a href=\"textures.html\">load the texture</a></p>\n<pre class=\"prettyprint showlinemods notranslate lang-js\" translate=\"no\">const loader = new THREE.TextureLoader();\nconst texture = loader.load('resources/images/minecraft/flourish-cc-by-nc-sa.png', render);\ntexture.magFilter = THREE.NearestFilter;\ntexture.minFilter = THREE.NearestFilter;\ntexture.colorSpace = THREE.SRGBColorSpace;\n</pre>\n<p>and pass the settings to the <code class=\"notranslate\" translate=\"no\">VoxelWorld</code> class</p>\n<pre class=\"prettyprint showlinemods notranslate lang-js\" translate=\"no\">+const tileSize = 16;\n+const tileTextureWidth = 256;\n+const tileTextureHeight = 64;\n-const world = new VoxelWorld(cellSize);\n+const world = new VoxelWorld({\n+  cellSize,\n+  tileSize,\n+  tileTextureWidth,\n+  tileTextureHeight,\n+});\n</pre>\n<p>Let's actually use the UVs when we create the geometry\nand the texture when we make the material</p>\n<pre class=\"prettyprint showlinemods notranslate lang-js\" translate=\"no\">-const {positions, normals, indices} = world.generateGeometryDataForCell(0, 0, 0);\n+const {positions, normals, uvs, indices} = world.generateGeometryDataForCell(0, 0, 0);\nconst geometry = new THREE.BufferGeometry();\n-const material = new THREE.MeshLambertMaterial({color: 'green'});\n+const material = new THREE.MeshLambertMaterial({\n+  map: texture,\n+  side: THREE.DoubleSide,\n+  alphaTest: 0.1,\n+  transparent: true,\n+});\n\nconst positionNumComponents = 3;\nconst normalNumComponents = 3;\n+const uvNumComponents = 2;\ngeometry.setAttribute(\n    'position',\n    new THREE.BufferAttribute(new Float32Array(positions), positionNumComponents));\ngeometry.setAttribute(\n    'normal',\n    new THREE.BufferAttribute(new Float32Array(normals), normalNumComponents));\n+geometry.setAttribute(\n+    'uv',\n+    new THREE.BufferAttribute(new Float32Array(uvs), uvNumComponents));\ngeometry.setIndex(indices);\nconst mesh = new THREE.Mesh(geometry, material);\nscene.add(mesh);\n</pre>\n<p>One last thing, we actually need to set some voxels\nto use different textures.</p>\n<pre class=\"prettyprint showlinemods notranslate lang-js\" translate=\"no\">for (let y = 0; y &lt; cellSize; ++y) {\n  for (let z = 0; z &lt; cellSize; ++z) {\n    for (let x = 0; x &lt; cellSize; ++x) {\n      const height = (Math.sin(x / cellSize * Math.PI * 2) + Math.sin(z / cellSize * Math.PI * 3)) * (cellSize / 6) + (cellSize / 2);\n      if (y &lt; height) {\n-        world.setVoxel(x, y, z, 1);\n+        world.setVoxel(x, y, z, randInt(1, 17));\n      }\n    }\n  }\n}\n\n+function randInt(min, max) {\n+  return Math.floor(Math.random() * (max - min) + min);\n+}\n</pre>\n<p>and with that we get textures!</p>\n<p></p><div translate=\"no\" class=\"threejs_example_container notranslate\">\n  <div></div>\n  <a class=\"threejs_center\" href=\"/manual/examples/voxel-geometry-culled-faces-with-textures.html\" target=\"_blank\">click here to open in a separate window</a>\n</div>\n\n<p></p>\n<p>Let's make it support more than one cell.</p>\n<p>To do this lets store cells in an object using cell ids.\nA cell id will just be a cell's coordinates separated by\na comma. In other words if we ask for voxel 35,0,0\nthat is in cell 1,0,0 so its id is <code class=\"notranslate\" translate=\"no\">\"1,0,0\"</code>.</p>\n<pre class=\"prettyprint showlinemods notranslate lang-js\" translate=\"no\">class VoxelWorld {\n  constructor(options) {\n    this.cellSize = options.cellSize;\n    this.tileSize = options.tileSize;\n    this.tileTextureWidth = options.tileTextureWidth;\n    this.tileTextureHeight = options.tileTextureHeight;\n    const {cellSize} = this;\n    this.cellSliceSize = cellSize * cellSize;\n-    this.cell = new Uint8Array(cellSize * cellSize * cellSize);\n+    this.cells = {};\n  }\n+  computeCellId(x, y, z) {\n+    const {cellSize} = this;\n+    const cellX = Math.floor(x / cellSize);\n+    const cellY = Math.floor(y / cellSize);\n+    const cellZ = Math.floor(z / cellSize);\n+    return <code class=\"notranslate\" translate=\"no\">${cellX},${cellY},${cellZ}</code>;\n+  }\n+  getCellForVoxel(x, y, z) {\n-    const cellX = Math.floor(x / cellSize);\n-    const cellY = Math.floor(y / cellSize);\n-    const cellZ = Math.floor(z / cellSize);\n-    if (cellX !== 0 || cellY !== 0 || cellZ !== 0) {\n-      return null;\n-    }\n-    return this.cell;\n+    return this.cells[this.computeCellId(x, y, z)];\n  }\n\n   ...\n}\n</pre>\n<p>and now we can make <code class=\"notranslate\" translate=\"no\">setVoxel</code> add new cells if\nwe try to set a voxel in a cell that does not yet exist</p>\n<pre class=\"prettyprint showlinemods notranslate lang-js\" translate=\"no\">  setVoxel(x, y, z, v) {\n-    const cell = this.getCellForVoxel(x, y, z);\n+    let cell = this.getCellForVoxel(x, y, z);\n    if (!cell) {\n-      return 0;\n+      cell = this.addCellForVoxel(x, y, z);\n    }\n    const voxelOffset = this.computeVoxelOffset(x, y, z);\n    cell[voxelOffset] = v;\n  }\n+  addCellForVoxel(x, y, z) {\n+    const cellId = this.computeCellId(x, y, z);\n+    let cell = this.cells[cellId];\n+    if (!cell) {\n+      const {cellSize} = this;\n+      cell = new Uint8Array(cellSize * cellSize * cellSize);\n+      this.cells[cellId] = cell;\n+    }\n+    return cell;\n+  }\n</pre>\n<p>Let's make this editable.</p>\n<p>First we`ll add a UI. Using radio buttons we can make an 8x2\narray of tiles</p>\n<pre class=\"prettyprint showlinemods notranslate lang-html\" translate=\"no\">&lt;body&gt;\n  &lt;canvas id=\"c\"&gt;&lt;/canvas&gt;\n+  &lt;div id=\"ui\"&gt;\n+    &lt;div class=\"tiles\"&gt;\n+      &lt;input type=\"radio\" name=\"voxel\" id=\"voxel1\" value=\"1\"&gt;&lt;label for=\"voxel1\" style=\"background-position:   -0% -0%\"&gt;&lt;/label&gt;\n+      &lt;input type=\"radio\" name=\"voxel\" id=\"voxel2\" value=\"2\"&gt;&lt;label for=\"voxel2\" style=\"background-position: -100% -0%\"&gt;&lt;/label&gt;\n+      &lt;input type=\"radio\" name=\"voxel\" id=\"voxel3\" value=\"3\"&gt;&lt;label for=\"voxel3\" style=\"background-position: -200% -0%\"&gt;&lt;/label&gt;\n+      &lt;input type=\"radio\" name=\"voxel\" id=\"voxel4\" value=\"4\"&gt;&lt;label for=\"voxel4\" style=\"background-position: -300% -0%\"&gt;&lt;/label&gt;\n+      &lt;input type=\"radio\" name=\"voxel\" id=\"voxel5\" value=\"5\"&gt;&lt;label for=\"voxel5\" style=\"background-position: -400% -0%\"&gt;&lt;/label&gt;\n+      &lt;input type=\"radio\" name=\"voxel\" id=\"voxel6\" value=\"6\"&gt;&lt;label for=\"voxel6\" style=\"background-position: -500% -0%\"&gt;&lt;/label&gt;\n+      &lt;input type=\"radio\" name=\"voxel\" id=\"voxel7\" value=\"7\"&gt;&lt;label for=\"voxel7\" style=\"background-position: -600% -0%\"&gt;&lt;/label&gt;\n+      &lt;input type=\"radio\" name=\"voxel\" id=\"voxel8\" value=\"8\"&gt;&lt;label for=\"voxel8\" style=\"background-position: -700% -0%\"&gt;&lt;/label&gt;\n+    &lt;/div&gt;\n+    &lt;div class=\"tiles\"&gt;\n+      &lt;input type=\"radio\" name=\"voxel\" id=\"voxel9\"  value=\"9\" &gt;&lt;label for=\"voxel9\"  style=\"background-position:  -800% -0%\"&gt;&lt;/label&gt;\n+      &lt;input type=\"radio\" name=\"voxel\" id=\"voxel10\" value=\"10\"&gt;&lt;label for=\"voxel10\" style=\"background-position:  -900% -0%\"&gt;&lt;/label&gt;\n+      &lt;input type=\"radio\" name=\"voxel\" id=\"voxel11\" value=\"11\"&gt;&lt;label for=\"voxel11\" style=\"background-position: -1000% -0%\"&gt;&lt;/label&gt;\n+      &lt;input type=\"radio\" name=\"voxel\" id=\"voxel12\" value=\"12\"&gt;&lt;label for=\"voxel12\" style=\"background-position: -1100% -0%\"&gt;&lt;/label&gt;\n+      &lt;input type=\"radio\" name=\"voxel\" id=\"voxel13\" value=\"13\"&gt;&lt;label for=\"voxel13\" style=\"background-position: -1200% -0%\"&gt;&lt;/label&gt;\n+      &lt;input type=\"radio\" name=\"voxel\" id=\"voxel14\" value=\"14\"&gt;&lt;label for=\"voxel14\" style=\"background-position: -1300% -0%\"&gt;&lt;/label&gt;\n+      &lt;input type=\"radio\" name=\"voxel\" id=\"voxel15\" value=\"15\"&gt;&lt;label for=\"voxel15\" style=\"background-position: -1400% -0%\"&gt;&lt;/label&gt;\n+      &lt;input type=\"radio\" name=\"voxel\" id=\"voxel16\" value=\"16\"&gt;&lt;label for=\"voxel16\" style=\"background-position: -1500% -0%\"&gt;&lt;/label&gt;\n+    &lt;/div&gt;\n+  &lt;/div&gt;\n&lt;/body&gt;\n</pre>\n<p>And add some CSS to style it, display the tiles and highlight\nthe current selection</p>\n<pre class=\"prettyprint showlinemods notranslate lang-css\" translate=\"no\">body {\n    margin: 0;\n}\n#c {\n    width: 100%;\n    height: 100%;\n    display: block;\n}\n+#ui {\n+    position: absolute;\n+    left: 10px;\n+    top: 10px;\n+    background: rgba(0, 0, 0, 0.8);\n+    padding: 5px;\n+}\n+#ui input[type=radio] {\n+  width: 0;\n+  height: 0;\n+  display: none;\n+}\n+#ui input[type=radio] + label {\n+  background-image: url('resources/images/minecraft/flourish-cc-by-nc-sa.png');\n+  background-size: 1600% 400%;\n+  image-rendering: pixelated;\n+  width: 64px;\n+  height: 64px;\n+  display: inline-block;\n+}\n+#ui input[type=radio]:checked + label {\n+  outline: 3px solid red;\n+}\n+@media (max-width: 600px), (max-height: 600px) {\n+  #ui input[type=radio] + label {\n+    width: 32px;\n+    height: 32px;\n+  }\n+}\n</pre>\n<p>The UX will be as follows. If no tile is selected and you click a voxel that\nvoxel will be erased or if you click a voxel and are holding the shift key it\nwill be erased. Otherwise if a tiles is selected it will be added. You can\ndeselect the selected tile type by clicking it again.</p>\n<p>This code will let the user unselect the highlighted\nradio button.</p>\n<pre class=\"prettyprint showlinemods notranslate lang-js\" translate=\"no\">let currentVoxel = 0;\nlet currentId;\n\ndocument.querySelectorAll('#ui .tiles input[type=radio][name=voxel]').forEach((elem) =&gt; {\n  elem.addEventListener('click', allowUncheck);\n});\n\nfunction allowUncheck() {\n  if (this.id === currentId) {\n    this.checked = false;\n    currentId = undefined;\n    currentVoxel = 0;\n  } else {\n    currentId = this.id;\n    currentVoxel = parseInt(this.value);\n  }\n}\n</pre>\n<p>And this below code will let us set a voxel based on where\nthe user clicks. It uses code similar to the code we\nmade in <a href=\"picking.html\">the article on picking</a>\nbut it's not using the built in <code class=\"notranslate\" translate=\"no\">RayCaster</code>. Instead\nit's using <code class=\"notranslate\" translate=\"no\">VoxelWorld.intersectRay</code> which returns\nthe position of intersection and the normal of the face\nhit.</p>\n<pre class=\"prettyprint showlinemods notranslate lang-js\" translate=\"no\">function getCanvasRelativePosition(event) {\n  const rect = canvas.getBoundingClientRect();\n  return {\n    x: (event.clientX - rect.left) * canvas.width  / rect.width,\n    y: (event.clientY - rect.top ) * canvas.height / rect.height,\n  };\n}\n\nfunction placeVoxel(event) {\n  const pos = getCanvasRelativePosition(event);\n  const x = (pos.x / canvas.width ) *  2 - 1;\n  const y = (pos.y / canvas.height) * -2 + 1;  // note we flip Y\n\n  const start = new THREE.Vector3();\n  const end = new THREE.Vector3();\n  start.setFromMatrixPosition(camera.matrixWorld);\n  end.set(x, y, 1).unproject(camera);\n\n  const intersection = world.intersectRay(start, end);\n  if (intersection) {\n    const voxelId = event.shiftKey ? 0 : currentVoxel;\n    // the intersection point is on the face. That means\n    // the math imprecision could put us on either side of the face.\n    // so go half a normal into the voxel if removing (currentVoxel = 0)\n    // our out of the voxel if adding (currentVoxel  &gt; 0)\n    const pos = intersection.position.map((v, ndx) =&gt; {\n      return v + intersection.normal[ndx] * (voxelId &gt; 0 ? 0.5 : -0.5);\n    });\n    world.setVoxel(...pos, voxelId);\n    updateVoxelGeometry(...pos);\n    requestRenderIfNotRequested();\n  }\n}\n\nconst mouse = {\n  x: 0,\n  y: 0,\n};\n\nfunction recordStartPosition(event) {\n  mouse.x = event.clientX;\n  mouse.y = event.clientY;\n  mouse.moveX = 0;\n  mouse.moveY = 0;\n}\nfunction recordMovement(event) {\n  mouse.moveX += Math.abs(mouse.x - event.clientX);\n  mouse.moveY += Math.abs(mouse.y - event.clientY);\n}\nfunction placeVoxelIfNoMovement(event) {\n  if (mouse.moveX &lt; 5 &amp;&amp; mouse.moveY &lt; 5) {\n    placeVoxel(event);\n  }\n  window.removeEventListener('pointermove', recordMovement);\n  window.removeEventListener('pointerup', placeVoxelIfNoMovement);\n}\ncanvas.addEventListener('pointerdown', (event) =&gt; {\n  event.preventDefault();\n  recordStartPosition(event);\n  window.addEventListener('pointermove', recordMovement);\n  window.addEventListener('pointerup', placeVoxelIfNoMovement);\n}, {passive: false});\ncanvas.addEventListener('touchstart', (event) =&gt; {\n  // stop scrolling\n  event.preventDefault();\n}, {passive: false});\n</pre>\n<p>There's a lot going on in the code above. Basically the mouse\nhas a dual purpose. One is to move the camera. The other is to\nedit the world. Placing/Erasing a voxel happen when you let off the mouse\nbut only if you have not moved the mouse since you first pressed down.\nThis is just a guess that if you did move the mouse you were trying\nto move the camera, not place a block. <code class=\"notranslate\" translate=\"no\">moveX</code> and <code class=\"notranslate\" translate=\"no\">moveY</code> are\nin absolute movement so if you move to the left 10 and then back to\nthe right 10 you'll have moved 20 units. In that case the user likely\nwas just rotating the model back and forth and does not want to\nplace a block. I didn't do any testing to see if <code class=\"notranslate\" translate=\"no\">5</code> is a good range or not. </p>\n<p>In the code we call <code class=\"notranslate\" translate=\"no\">world.setVoxel</code> to set a voxel and\nthen <code class=\"notranslate\" translate=\"no\">updateVoxelGeometry</code> to update the three.js geometry\nbased on what's changed.</p>\n<p>Let's make that now. If the user clicks a\nvoxel on the edge of a cell then the geometry for the voxel\nin the adjacent cell might need new geometry. This means\nwe need to check the cell for the voxel we just edited\nas well as in all 6 directions from that cell.</p>\n<pre class=\"prettyprint showlinemods notranslate lang-js\" translate=\"no\">const neighborOffsets = [\n  [ 0,  0,  0], // self\n  [-1,  0,  0], // left\n  [ 1,  0,  0], // right\n  [ 0, -1,  0], // down\n  [ 0,  1,  0], // up\n  [ 0,  0, -1], // back\n  [ 0,  0,  1], // front\n];\nfunction updateVoxelGeometry(x, y, z) {\n  const updatedCellIds = {};\n  for (const offset of neighborOffsets) {\n    const ox = x + offset[0];\n    const oy = y + offset[1];\n    const oz = z + offset[2];\n    const cellId = world.computeCellId(ox, oy, oz);\n    if (!updatedCellIds[cellId]) {\n      updatedCellIds[cellId] = true;\n      updateCellGeometry(ox, oy, oz);\n    }\n  }\n}\n</pre>\n<p>I thought about checking for adjacent cells like </p>\n<pre class=\"prettyprint showlinemods notranslate lang-js\" translate=\"no\">const voxelX = THREE.MathUtils.euclideanModulo(x, cellSize) | 0;\nif (voxelX === 0) {\n  // update cell to the left\n} else if (voxelX === cellSize - 1) {\n  // update cell to the right\n}\n</pre>\n<p>and there would be 4 more checks for the other 4 directions\nbut it occurred to me the code would be much simpler with\njust an array of offsets and saving off the cell ids of\nthe cells we already updated. If the updated voxel is not\non the edge of a cell then the test will quickly reject updating\nthe same cell.</p>\n<p>For <code class=\"notranslate\" translate=\"no\">updateCellGeometry</code> we're just going to take the code we\nhad before that was generating the geometry for one cell\nand make it handle multiple cells.</p>\n<pre class=\"prettyprint showlinemods notranslate lang-js\" translate=\"no\">const cellIdToMesh = {};\nfunction updateCellGeometry(x, y, z) {\n  const cellX = Math.floor(x / cellSize);\n  const cellY = Math.floor(y / cellSize);\n  const cellZ = Math.floor(z / cellSize);\n  const cellId = world.computeCellId(x, y, z);\n  let mesh = cellIdToMesh[cellId];\n  const geometry = mesh ? mesh.geometry : new THREE.BufferGeometry();\n\n  const {positions, normals, uvs, indices} = world.generateGeometryDataForCell(cellX, cellY, cellZ);\n  const positionNumComponents = 3;\n  geometry.setAttribute('position', new THREE.BufferAttribute(new Float32Array(positions), positionNumComponents));\n  const normalNumComponents = 3;\n  geometry.setAttribute('normal', new THREE.BufferAttribute(new Float32Array(normals), normalNumComponents));\n  const uvNumComponents = 2;\n  geometry.setAttribute('uv', new THREE.BufferAttribute(new Float32Array(uvs), uvNumComponents));\n  geometry.setIndex(indices);\n  geometry.computeBoundingSphere();\n\n  if (!mesh) {\n    mesh = new THREE.Mesh(geometry, material);\n    mesh.name = cellId;\n    cellIdToMesh[cellId] = mesh;\n    scene.add(mesh);\n    mesh.position.set(cellX * cellSize, cellY * cellSize, cellZ * cellSize);\n  }\n}\n</pre>\n<p>The code above checks a map of cell ids to meshes. If\nwe ask for a cell that doesn't exist a new <a href=\"/docs/#api/en/objects/Mesh\"><code class=\"notranslate\" translate=\"no\">Mesh</code></a> is made\nand added to the correct place in world space.\nAt the end we update the attributes and indices with the new data.</p>\n<p></p><div translate=\"no\" class=\"threejs_example_container notranslate\">\n  <div></div>\n  <a class=\"threejs_center\" href=\"/manual/examples/voxel-geometry-culled-faces-ui.html\" target=\"_blank\">click here to open in a separate window</a>\n</div>\n\n<p></p>\n<p>Some notes:</p>\n<p><code class=\"notranslate\" translate=\"no\">RayCaster</code> might have worked just fine. I didn't try it.\nInstead I found <a href=\"https://citeseerx.ist.psu.edu/viewdoc/download?doi=10.1.1.42.3443&amp;rep=rep1&amp;type=pdf\">a voxel specific raycaster</a>.\nthat is optimized for voxels.</p>\n<p>I made <code class=\"notranslate\" translate=\"no\">intersectRay</code> part of VoxelWorld because it seemed\nlike if it gets too slow we could raycast against cells\nbefore raycasting on voxels as a simple speed up if it becomes\ntoo slow.</p>\n<p>You might want to change the length of the raycast\nas currently it's all the way to Z-far. I expect if the\nuser clicks something too far way they don't really want\nto be placing blocks on the other side of the world that\nare 1 or 2 pixel large.</p>\n<p>Calling <code class=\"notranslate\" translate=\"no\">geometry.computeBoundingSphere</code> might be slow.\nWe could just manually set the bounding sphere to the fit\nthe entire cell.</p>\n<p>Do we want remove cells if all voxels in that cell are 0?\nThat would probably be reasonable change if we wanted to ship this.</p>\n<p>Thinking about how this works it's clear the absolute\nworst case is a checkerboard of on and off voxels. I don't\nknow off the top of my head what other strategies to use\nif things get too slow. Maybe getting too slow would just\nencourage the user not to make giant checkerboard areas.</p>\n<p>To keep it simple the texture atlas is just 1 column\nper voxel type. It would be better to make something more\nflexible where we have a table of voxel types and each\ntype can specify where its face textures are in the atlas.\nAs it is lots of space is wasted.</p>\n<p>Looking at real minecraft there are tiles that are not\nvoxels, not cubes. Like a fence tile or flowers. To do that\nwe'd again need some table of voxel types and for each\nvoxel whether it's a cube or some other geometry. If it's\nnot a cube the neighbor check when generating the geometry\nwould also need to change. A flower voxel next to another\nvoxel should not remove the faces between them.</p>\n<p>If you want to make some minecraft like thing using three.js\nI hope this has given you some ideas where to start and how\nto generate some what efficient geometry.</p>\n<p><canvas id=\"c\"></canvas></p>\n\n\n\n        </div>\n      </div>\n    </div>\n\n  \n  \n\n\n\n\n\n<canvas id=\"c\" data-engine=\"three.js r185\"></canvas>",
    "success": true,
    "error": "",
    "crawled_at": 1786376809.300204,
    "is_shell": false
  },
  {
    "url": "https://threejs.org/manual/en/game.html",
    "title": "Making a Game",
    "content": "<div class=\"container\">\n      <div class=\"lesson-title\">\n        <h1>Making a Game</h1>\n      </div>\n      <div class=\"lesson\">\n        <div class=\"lesson-main\">\n          <p>Many people want to write games using three.js. This article\nwill hopefully give you some ideas on how to start.</p>\n<p>At least at the time I'm writing this article it's probably going to be the\nlongest article on this site. It's possible the code here is massively over\nengineered but as I wrote each new feature I'd run into a problem that needed a\nsolution I'm used to from other games I've written. In other words each new\nsolution seemed important so I'll try to show why. Of course the smaller your\ngame the less you might need some of the solutions shown here but this is a\npretty small game and yet with the complexities of 3D characters many things\ntake more organization than they might with 2D characters.</p>\n<p>As an example if you're making PacMan in 2D, when PacMan turns a corner\nthat happens instantly at 90 degrees. There is no in-between step. But\nin a 3D game often we need the character to rotate over several frames.\nThat simple change can add a bunch of complexity and require different\nsolutions.</p>\n<p>The majority of the code here will not really be three.js and\nthat's important to note, <strong>three.js is not a game engine</strong>.\nThree.js is a 3D library. It provides a <a href=\"scenegraph.html\">scene graph</a>\nand features for displaying 3D objects added to that scene graph\nbut it does not provide all the other things needed to make a game.\nNo collisions, no physics, no input systems, no path finding, etc, etc...\nSo, we'll have to provide those things ourselves.</p>\n<p>I ended up writing quite a bit of code to make this simple <em>unfinished</em>\ngame like thing and again, it's certainly possible I over engineered and there\nare simpler solutions but I feel like I actually didn't write\nenough code and hopefully I can explain what I think is missing.</p>\n<p>Many of the ideas here are heavily influenced by <a href=\"https://unity.com\">Unity</a>.\nIf you're not familiar with Unity that probably does not matter.\nI only bring it up as 10s of 1000s of games have shipped using\nthese ideas.</p>\n<p>Let's start with the three.js parts. We need to load models for our game.</p>\n<p>At <a href=\"https://opengameart.org\">opengameart.org</a> I found this <a href=\"https://opengameart.org/content/lowpoly-animated-knight\">animated knight\nmodel</a> by <a href=\"https://opengameart.org/users/quaternius\">quaternius</a></p>\n<div class=\"threejs_center\"><img src=\"../resources/images/knight.jpg\" style=\"width: 375px;\"></div>\n\n<p><a href=\"https://opengameart.org/users/quaternius\">quaternius</a> also made <a href=\"https://opengameart.org/content/lowpoly-animated-farm-animal-pack\">these animated animals</a>.</p>\n<div class=\"threejs_center\"><img src=\"../resources/images/animals.jpg\" style=\"width: 606px;\"></div>\n\n<p>These seem like good models to start with so the first thing we need to\ndo is load them.</p>\n<p>We covered <a href=\"load-gltf.html\">loading glTF files before</a>.\nThe difference this time is we need to load multiple models and\nwe can't start the game until all the models are loaded.</p>\n<p>Fortunately three.js provides the <a href=\"/docs/#api/en/loaders/managers/LoadingManager\"><code class=\"notranslate\" translate=\"no\">LoadingManager</code></a> just for this purpose.\nWe create a <a href=\"/docs/#api/en/loaders/managers/LoadingManager\"><code class=\"notranslate\" translate=\"no\">LoadingManager</code></a> and pass it to the other loaders. The\n<a href=\"/docs/#api/en/loaders/managers/LoadingManager\"><code class=\"notranslate\" translate=\"no\">LoadingManager</code></a> provides both <a href=\"/docs/#api/en/loaders/managers/LoadingManager#onProgress\"><code class=\"notranslate\" translate=\"no\">onProgress</code></a> and\n<a href=\"/docs/#api/en/loaders/managers/LoadingManager#onLoad\"><code class=\"notranslate\" translate=\"no\">onLoad</code></a> properties we can attach callbacks to.\nThe <a href=\"/docs/#api/en/loaders/managers/LoadingManager#onLoad\"><code class=\"notranslate\" translate=\"no\">onLoad</code></a> callback will be called when\nall files have been loaded. The <a href=\"/docs/#api/en/loaders/managers/LoadingManager#onProgress\"><code class=\"notranslate\" translate=\"no\">onProgress</code></a> callback\nas called after each individual file arrives to give as a chance to show\nloading progress.</p>\n<p>Starting with the code from <a href=\"load-gltf.html\">loading a glTF file</a> I removed all\nthe code related to framing the scene and added this code to load all models.</p>\n<pre class=\"prettyprint showlinemods notranslate lang-js\" translate=\"no\">const manager = new THREE.LoadingManager();\nmanager.onLoad = init;\nconst models = {\n  pig:    { url: 'resources/models/animals/Pig.gltf' },\n  cow:    { url: 'resources/models/animals/Cow.gltf' },\n  llama:  { url: 'resources/models/animals/Llama.gltf' },\n  pug:    { url: 'resources/models/animals/Pug.gltf' },\n  sheep:  { url: 'resources/models/animals/Sheep.gltf' },\n  zebra:  { url: 'resources/models/animals/Zebra.gltf' },\n  horse:  { url: 'resources/models/animals/Horse.gltf' },\n  knight: { url: 'resources/models/knight/KnightCharacter.gltf' },\n};\n{\n  const gltfLoader = new GLTFLoader(manager);\n  for (const model of Object.values(models)) {\n    gltfLoader.load(model.url, (gltf) =&gt; {\n      model.gltf = gltf;\n    });\n  }\n}\n\nfunction init() {\n  // TBD\n}\n</pre>\n<p>This code will load all the models above and the <a href=\"/docs/#api/en/loaders/managers/LoadingManager\"><code class=\"notranslate\" translate=\"no\">LoadingManager</code></a> will call\n<code class=\"notranslate\" translate=\"no\">init</code> when done. We'll use the <code class=\"notranslate\" translate=\"no\">models</code> object later to let us access the\nloaded models so the <a href=\"/docs/#examples/loaders/GLTFLoader\"><code class=\"notranslate\" translate=\"no\">GLTFLoader</code></a> callback for each individual model attaches\nthe loaded data to that model's info.</p>\n<p>All the models with all their animation are currently about 6.6meg. That's a\npretty big download. Assuming your server supports compression (the server this\nsite runs on does) it's able to compress them to around 1.4meg. That's\ndefinitely better than 6.6meg bit it's still not a tiny amount of data. It would\nprobably be good if we added a progress bar so the user has some idea how much\nlonger they have to wait.</p>\n<p>So, let's add an <a href=\"/docs/#api/en/loaders/managers/LoadingManager#onProgress\"><code class=\"notranslate\" translate=\"no\">onProgress</code></a> callback. It will be\ncalled with 3 arguments, the <code class=\"notranslate\" translate=\"no\">url</code> of the last loaded object and then the number\nof items loaded so far as well as the total number of items.</p>\n<p>Let's setup some HTML for a loading bar</p>\n<pre class=\"prettyprint showlinemods notranslate lang-html\" translate=\"no\">&lt;body&gt;\n  &lt;canvas id=\"c\"&gt;&lt;/canvas&gt;\n+  &lt;div id=\"loading\"&gt;\n+    &lt;div&gt;\n+      &lt;div&gt;...loading...&lt;/div&gt;\n+      &lt;div class=\"progress\"&gt;&lt;div id=\"progressbar\"&gt;&lt;/div&gt;&lt;/div&gt;\n+    &lt;/div&gt;\n+  &lt;/div&gt;\n&lt;/body&gt;\n</pre>\n<p>We'll look up the <code class=\"notranslate\" translate=\"no\">#progressbar</code> div and we can set the width from 0% to 100%\nto show our progress. All we need to do is set that in our callback.</p>\n<pre class=\"prettyprint showlinemods notranslate lang-js\" translate=\"no\">const manager = new THREE.LoadingManager();\nmanager.onLoad = init;\n\n+const progressbarElem = document.querySelector('#progressbar');\n+manager.onProgress = (url, itemsLoaded, itemsTotal) =&gt; {\n+  progressbarElem.style.width = <code class=\"notranslate\" translate=\"no\">${itemsLoaded / itemsTotal * 100 | 0}%</code>;\n+};\n</pre>\n<p>We already setup <code class=\"notranslate\" translate=\"no\">init</code> to be called when all the models are loaded so\nwe can turn off the progress bar by hiding the <code class=\"notranslate\" translate=\"no\">#loading</code> element.</p>\n<pre class=\"prettyprint showlinemods notranslate lang-js\" translate=\"no\">function init() {\n+  // hide the loading bar\n+  const loadingElem = document.querySelector('#loading');\n+  loadingElem.style.display = 'none';\n}\n</pre>\n<p>Here's a bunch of CSS for styling the bar. The CSS makes the <code class=\"notranslate\" translate=\"no\">#loading</code> <code class=\"notranslate\" translate=\"no\">&lt;div&gt;</code>\nthe full size of the page and centers its children. The CSS makes a <code class=\"notranslate\" translate=\"no\">.progress</code>\narea to contain the progress bar. The CSS also gives the progress bar\na CSS animation of diagonal stripes.</p>\n<pre class=\"prettyprint showlinemods notranslate lang-css\" translate=\"no\">#loading {\n  position: absolute;\n  left: 0;\n  top: 0;\n  width: 100%;\n  height: 100%;\n  display: flex;\n  align-items: center;\n  justify-content: center;\n  text-align: center;\n  font-size: xx-large;\n  font-family: sans-serif;\n}\n#loading&gt;div&gt;div {\n  padding: 2px;\n}\n.progress {\n  width: 50vw;\n  border: 1px solid black;\n}\n#progressbar {\n  width: 0;\n  transition: width ease-out .5s;\n  height: 1em;\n  background-color: #888;\n  background-image: linear-gradient(\n    -45deg,\n    rgba(255, 255, 255, .5) 25%,\n    transparent 25%,\n    transparent 50%,\n    rgba(255, 255, 255, .5) 50%,\n    rgba(255, 255, 255, .5) 75%,\n    transparent 75%,\n    transparent\n  );\n  background-size: 50px 50px;\n  animation: progressanim 2s linear infinite;\n}\n\n@keyframes progressanim {\n  0% {\n    background-position: 50px 50px;\n  }\n  100% {\n    background-position: 0 0;\n  }\n}\n</pre>\n<p>Now that we have a progress bar let's deal with the models. These models\nhave animations and we want to be able to access those animations.\nAnimations are stored in an array by default be we'd like to be able to\neasily access them by name so let's setup an <code class=\"notranslate\" translate=\"no\">animations</code> property for\neach model to do that. Note of course this means animations must have unique names.</p>\n<pre class=\"prettyprint showlinemods notranslate lang-js\" translate=\"no\">+function prepModelsAndAnimations() {\n+  Object.values(models).forEach(model =&gt; {\n+    const animsByName = {};\n+    model.gltf.animations.forEach((clip) =&gt; {\n+      animsByName[clip.name] = clip;\n+    });\n+    model.animations = animsByName;\n+  });\n+}\n\nfunction init() {\n  // hide the loading bar\n  const loadingElem = document.querySelector('#loading');\n  loadingElem.style.display = 'none';\n\n+  prepModelsAndAnimations();\n}\n</pre>\n<p>Let's display the animated models.</p>\n<p>Unlike the <a href=\"load-gltf.html\">previous example of loading a glTF file</a>\nThis time we probably want to be able to display more than one instance\nof each model. To do this, instead of adding\nthe loaded gltf scene directly like we did in <a href=\"load-gltf.html\">the article on loading a glTF</a>,\nwe instead want to clone the scene and in particular we want to clone\nit for skinned animated characters. Fortunately there's a utility function,\n<code class=\"notranslate\" translate=\"no\">SkeletonUtils.clone</code> we can use to do this. So, first we need to include\nthe utils.</p>\n<pre class=\"prettyprint showlinemods notranslate lang-js\" translate=\"no\">import * as THREE from 'three';\nimport {OrbitControls} from 'three/addons/controls/OrbitControls.js';\nimport {GLTFLoader} from 'three/addons/loaders/GLTFLoader.js';\n+import * as SkeletonUtils from 'three/addons/utils/SkeletonUtils.js';\n</pre>\n<p>Then we can clone the models we just loaded</p>\n<pre class=\"prettyprint showlinemods notranslate lang-js\" translate=\"no\">function init() {\n  // hide the loading bar\n  const loadingElem = document.querySelector('#loading');\n  loadingElem.style.display = 'none';\n\n  prepModelsAndAnimations();\n\n+  Object.values(models).forEach((model, ndx) =&gt; {\n+    const clonedScene = SkeletonUtils.clone(model.gltf.scene);\n+    const root = new THREE.Object3D();\n+    root.add(clonedScene);\n+    scene.add(root);\n+    root.position.x = (ndx - 3) * 3;\n+  });\n}\n</pre>\n<p>Above, for each model, we clone the <code class=\"notranslate\" translate=\"no\">gltf.scene</code> we loaded and we parent that\nto a new <a href=\"/docs/#api/en/core/Object3D\"><code class=\"notranslate\" translate=\"no\">Object3D</code></a>. We need to parent it to another object because when\nwe play animations the animation will apply animated positions to the nodes\nin the loaded scene which means we won't have control over those positions.</p>\n<p>To play the animations each model we clone needs an <a href=\"/docs/#api/en/animation/AnimationMixer\"><code class=\"notranslate\" translate=\"no\">AnimationMixer</code></a>.\nAn <a href=\"/docs/#api/en/animation/AnimationMixer\"><code class=\"notranslate\" translate=\"no\">AnimationMixer</code></a> contains 1 or more <a href=\"/docs/#api/en/animation/AnimationAction\"><code class=\"notranslate\" translate=\"no\">AnimationAction</code></a>s. An\n<a href=\"/docs/#api/en/animation/AnimationAction\"><code class=\"notranslate\" translate=\"no\">AnimationAction</code></a> references an <a href=\"/docs/#api/en/animation/AnimationClip\"><code class=\"notranslate\" translate=\"no\">AnimationClip</code></a>. <a href=\"/docs/#api/en/animation/AnimationAction\"><code class=\"notranslate\" translate=\"no\">AnimationAction</code></a>s\nhave all kinds of settings for playing then chaining to another\naction or cross fading between actions. Let's just get the first\n<a href=\"/docs/#api/en/animation/AnimationClip\"><code class=\"notranslate\" translate=\"no\">AnimationClip</code></a> and create an action for it. The default is for\nan action to play its clip in a loop forever.</p>\n<pre class=\"prettyprint showlinemods notranslate lang-js\" translate=\"no\">+const mixers = [];\n\nfunction init() {\n  // hide the loading bar\n  const loadingElem = document.querySelector('#loading');\n  loadingElem.style.display = 'none';\n\n  prepModelsAndAnimations();\n\n  Object.values(models).forEach((model, ndx) =&gt; {\n    const clonedScene = SkeletonUtils.clone(model.gltf.scene);\n    const root = new THREE.Object3D();\n    root.add(clonedScene);\n    scene.add(root);\n    root.position.x = (ndx - 3) * 3;\n\n+    const mixer = new THREE.AnimationMixer(clonedScene);\n+    const firstClip = Object.values(model.animations)[0];\n+    const action = mixer.clipAction(firstClip);\n+    action.play();\n+    mixers.push(mixer);\n  });\n}\n</pre>\n<p>We called <a href=\"/docs/#api/en/animation/AnimationAction#play\"><code class=\"notranslate\" translate=\"no\">play</code></a> to start the action and stored\noff all the <code class=\"notranslate\" translate=\"no\">AnimationMixers</code> in an array called <code class=\"notranslate\" translate=\"no\">mixers</code>. Finally\nwe need to update each <a href=\"/docs/#api/en/animation/AnimationMixer\"><code class=\"notranslate\" translate=\"no\">AnimationMixer</code></a> in our render loop by computing\nthe time since the last frame and passing that to <a href=\"/docs/#api/en/animation/AnimationMixer.update\"><code class=\"notranslate\" translate=\"no\">AnimationMixer.update</code></a>.</p>\n<pre class=\"prettyprint showlinemods notranslate lang-js\" translate=\"no\">+let then = 0;\nfunction render(now) {\n+  now *= 0.001;  // convert to seconds\n+  const deltaTime = now - then;\n+  then = now;\n\n  if (resizeRendererToDisplaySize(renderer)) {\n    const canvas = renderer.domElement;\n    camera.aspect = canvas.clientWidth / canvas.clientHeight;\n    camera.updateProjectionMatrix();\n  }\n\n+  for (const mixer of mixers) {\n+    mixer.update(deltaTime);\n+  }\n\n  renderer.render(scene, camera);\n\n  requestAnimationFrame(render);\n}\n</pre>\n<p>And with that we should get each model loaded and playing its first animation.</p>\n<p></p><div translate=\"no\" class=\"threejs_example_container notranslate\">\n  <div></div>\n  <a class=\"threejs_center\" href=\"/manual/examples/game-load-models.html\" target=\"_blank\">click here to open in a separate window</a>\n</div>\n\n<p></p>\n<p>Let's make it so we can check all of the animations.\nWe'll add all of the clips as actions and then enable just one at\na time.</p>\n<pre class=\"prettyprint showlinemods notranslate lang-js\" translate=\"no\">-const mixers = [];\n+const mixerInfos = [];\n\nfunction init() {\n  // hide the loading bar\n  const loadingElem = document.querySelector('#loading');\n  loadingElem.style.display = 'none';\n\n  prepModelsAndAnimations();\n\n  Object.values(models).forEach((model, ndx) =&gt; {\n    const clonedScene = SkeletonUtils.clone(model.gltf.scene);\n    const root = new THREE.Object3D();\n    root.add(clonedScene);\n    scene.add(root);\n    root.position.x = (ndx - 3) * 3;\n\n    const mixer = new THREE.AnimationMixer(clonedScene);\n-    const firstClip = Object.values(model.animations)[0];\n-    const action = mixer.clipAction(firstClip);\n-    action.play();\n-    mixers.push(mixer);\n+    const actions = Object.values(model.animations).map((clip) =&gt; {\n+      return mixer.clipAction(clip);\n+    });\n+    const mixerInfo = {\n+      mixer,\n+      actions,\n+      actionNdx: -1,\n+    };\n+    mixerInfos.push(mixerInfo);\n+    playNextAction(mixerInfo);\n  });\n}\n\n+function playNextAction(mixerInfo) {\n+  const {actions, actionNdx} = mixerInfo;\n+  const nextActionNdx = (actionNdx + 1) % actions.length;\n+  mixerInfo.actionNdx = nextActionNdx;\n+  actions.forEach((action, ndx) =&gt; {\n+    const enabled = ndx === nextActionNdx;\n+    action.enabled = enabled;\n+    if (enabled) {\n+      action.play();\n+    }\n+  });\n+}\n</pre>\n<p>The code above makes an array of <a href=\"/docs/#api/en/animation/AnimationAction\"><code class=\"notranslate\" translate=\"no\">AnimationAction</code></a>s,\none for each <a href=\"/docs/#api/en/animation/AnimationClip\"><code class=\"notranslate\" translate=\"no\">AnimationClip</code></a>. It makes an array of objects, <code class=\"notranslate\" translate=\"no\">mixerInfos</code>,\nwith references to the <a href=\"/docs/#api/en/animation/AnimationMixer\"><code class=\"notranslate\" translate=\"no\">AnimationMixer</code></a> and all the <a href=\"/docs/#api/en/animation/AnimationAction\"><code class=\"notranslate\" translate=\"no\">AnimationAction</code></a>s\nfor each model. It then calls <code class=\"notranslate\" translate=\"no\">playNextAction</code> which sets <code class=\"notranslate\" translate=\"no\">enabled</code> on\nall but one action for that mixer.</p>\n<p>We need to update the render loop for the new array</p>\n<pre class=\"prettyprint showlinemods notranslate lang-js\" translate=\"no\">-for (const mixer of mixers) {\n+for (const {mixer} of mixerInfos) {\n  mixer.update(deltaTime);\n}\n</pre>\n<p>Let's make it so pressing a key 1 to 8 will play the next animation\nfor each model</p>\n<pre class=\"prettyprint showlinemods notranslate lang-js\" translate=\"no\">window.addEventListener('keydown', (e) =&gt; {\n  const mixerInfo = mixerInfos[e.keyCode - 49];\n  if (!mixerInfo) {\n    return;\n  }\n  playNextAction(mixerInfo);\n});\n</pre>\n<p>Now you should be able to click on the example and then press keys 1 through 8\nto cycle each of the models through their available animations.</p>\n<p></p><div translate=\"no\" class=\"threejs_example_container notranslate\">\n  <div></div>\n  <a class=\"threejs_center\" href=\"/manual/examples/game-check-animations.html\" target=\"_blank\">click here to open in a separate window</a>\n</div>\n\n<p></p>\n<p>So that is arguably the sum-total of the three.js portion of this\narticle. We covered loading multiple files, cloning skinned models,\nand playing animations on them. In a real game you'd have to do a\nton more manipulation of <a href=\"/docs/#api/en/animation/AnimationAction\"><code class=\"notranslate\" translate=\"no\">AnimationAction</code></a> objects.</p>\n<p>Let's start making a game infrastructure</p>\n<p>A common pattern for making a modern game is to use an\n<a href=\"https://www.google.com/search?q=entity+component+system\">Entity Component System</a>.\nIn an Entity Component System an object in a game is called an <em>entity</em> that consists\nof a bunch of <em>components</em>. You build up entities by deciding which components to\nattach to them. So, let's make an Entity Component System.</p>\n<p>We'll call our entities <code class=\"notranslate\" translate=\"no\">GameObject</code>. It's effectively just a collection\nof components and a three.js <a href=\"/docs/#api/en/core/Object3D\"><code class=\"notranslate\" translate=\"no\">Object3D</code></a>.</p>\n<pre class=\"prettyprint showlinemods notranslate lang-js\" translate=\"no\">function removeArrayElement(array, element) {\n  const ndx = array.indexOf(element);\n  if (ndx &gt;= 0) {\n    array.splice(ndx, 1);\n  }\n}\n\nclass GameObject {\n  constructor(parent, name) {\n    this.name = name;\n    this.components = [];\n    this.transform = new THREE.Object3D();\n    parent.add(this.transform);\n  }\n  addComponent(ComponentType, ...args) {\n    const component = new ComponentType(this, ...args);\n    this.components.push(component);\n    return component;\n  }\n  removeComponent(component) {\n    removeArrayElement(this.components, component);\n  }\n  getComponent(ComponentType) {\n    return this.components.find(c =&gt; c instanceof ComponentType);\n  }\n  update() {\n    for (const component of this.components) {\n      component.update();\n    }\n  }\n}\n</pre>\n<p>Calling <code class=\"notranslate\" translate=\"no\">GameObject.update</code> calls <code class=\"notranslate\" translate=\"no\">update</code> on all the components.</p>\n<p>I included a name only to help in debugging so if I look at a <code class=\"notranslate\" translate=\"no\">GameObject</code>\nin the debugger I can see a name to help identify it.</p>\n<p>Some things that might seem a little strange:</p>\n<p><code class=\"notranslate\" translate=\"no\">GameObject.addComponent</code> is used to create components. Whether or not\nthis a good idea or a bad idea I'm not sure. My thinking was it makes\nno sense for a component to exist outside of a gameobject so I thought\nit might be good if creating a component automatically added that component\nto the gameobject and passed the gameobject to the component's constructor.\nIn other words to add a component you do this</p>\n<pre class=\"prettyprint showlinemods notranslate lang-js\" translate=\"no\">const gameObject = new GameObject(scene, 'foo');\ngameObject.addComponent(TypeOfComponent);\n</pre>\n<p>If I didn't do it this way you'd instead do something like this</p>\n<pre class=\"prettyprint showlinemods notranslate lang-js\" translate=\"no\">const gameObject = new GameObject(scene, 'foo');\nconst component = new TypeOfComponent(gameObject);\ngameObject.addComponent(component);\n</pre>\n<p>Is it better that the first way is shorter and more automated or is it worse\nbecause it looks out of the ordinary? I don't know.</p>\n<p><code class=\"notranslate\" translate=\"no\">GameObject.getComponent</code> looks up components by type. That has\nthe implication that you can not have 2 components of the same\ntype on a single game object or at least if you do you can only\nlook up the first one without adding some other API.</p>\n<p>It's common for one component to look up another and when looking them up they\nhave to match by type otherwise you might get the wrong one. We could instead\ngive each component a name and you could look them up by name. That would be\nmore flexible in that you could have more than one component of the same type but it\nwould also be more tedious. Again, I'm not sure which is better.</p>\n<p>On to the components themselves. Here is their base class.</p>\n<pre class=\"prettyprint showlinemods notranslate lang-js\" translate=\"no\">// Base for all components\nclass Component {\n  constructor(gameObject) {\n    this.gameObject = gameObject;\n  }\n  update() {\n  }\n}\n</pre>\n<p>Do components need a base class? JavaScript is not like most strictly\ntyped languages so effectively we could have no base class and just\nleave it up to each component to do whatever it wants in its constructor\nknowing that the first argument is always the component's gameobject.\nIf it doesn't care about gameobject it wouldn't store it. I kind of feel like this\ncommon base is good though. It means if you have a reference to a\ncomponent you know you can find its parent gameobject always and from its\nparent you can easily look up other components as well as look at its\ntransform.</p>\n<p>To manage the gameobjects we probably need some kind of gameobject manager. You\nmight think we could just keep an array of gameobjects but in a real game the\ncomponents of a gameobject might add and remove other gameobjects at runtime.\nFor example a gun gameobject might add a bullet gameobject every time the gun\nfires. A monster gameobject might remove itself if it has been killed. We then\nwould have an issue that we might have code like this</p>\n<pre class=\"prettyprint showlinemods notranslate lang-js\" translate=\"no\">for (const gameObject of globalArrayOfGameObjects) {\n  gameObject.update();\n}\n</pre>\n<p>The loop above would fail or do un-expected things if\ngameobjects are added or removed from <code class=\"notranslate\" translate=\"no\">globalArrayOfGameObjects</code>\nin the middle of the loop in some component's <code class=\"notranslate\" translate=\"no\">update</code> function.</p>\n<p>To try to prevent that problem we need something a little safer.\nHere's one attempt.</p>\n<pre class=\"prettyprint showlinemods notranslate lang-js\" translate=\"no\">class SafeArray {\n  constructor() {\n    this.array = [];\n    this.addQueue = [];\n    this.removeQueue = new Set();\n  }\n  get isEmpty() {\n    return this.addQueue.length + this.array.length &gt; 0;\n  }\n  add(element) {\n    this.addQueue.push(element);\n  }\n  remove(element) {\n    this.removeQueue.add(element);\n  }\n  forEach(fn) {\n    this._addQueued();\n    this._removeQueued();\n    for (const element of this.array) {\n      if (this.removeQueue.has(element)) {\n        continue;\n      }\n      fn(element);\n    }\n    this._removeQueued();\n  }\n  _addQueued() {\n    if (this.addQueue.length) {\n      this.array.splice(this.array.length, 0, ...this.addQueue);\n      this.addQueue = [];\n    }\n  }\n  _removeQueued() {\n    if (this.removeQueue.size) {\n      this.array = this.array.filter(element =&gt; !this.removeQueue.has(element));\n      this.removeQueue.clear();\n    }\n  }\n}\n</pre>\n<p>The class above lets you add or remove elements from the <code class=\"notranslate\" translate=\"no\">SafeArray</code>\nbut won't mess with the array itself while it's being iterated over. Instead\nnew elements get added to <code class=\"notranslate\" translate=\"no\">addQueue</code> and removed elements to the <code class=\"notranslate\" translate=\"no\">removeQueue</code>\nand then added or removed outside of the loop.</p>\n<p>Using that here is our class to manage gameobjects.</p>\n<pre class=\"prettyprint showlinemods notranslate lang-js\" translate=\"no\">class GameObjectManager {\n  constructor() {\n    this.gameObjects = new SafeArray();\n  }\n  createGameObject(parent, name) {\n    const gameObject = new GameObject(parent, name);\n    this.gameObjects.add(gameObject);\n    return gameObject;\n  }\n  removeGameObject(gameObject) {\n    this.gameObjects.remove(gameObject);\n  }\n  update() {\n    this.gameObjects.forEach(gameObject =&gt; gameObject.update());\n  }\n}\n</pre>\n<p>With all that now let's make our first component. This component\nwill just manage a skinned three.js object like the ones we just created.\nTo keep it simple it will just have one method, <code class=\"notranslate\" translate=\"no\">setAnimation</code> that\ntakes the name of the animation to play and plays it.</p>\n<pre class=\"prettyprint showlinemods notranslate lang-js\" translate=\"no\">class SkinInstance extends Component {\n  constructor(gameObject, model) {\n    super(gameObject);\n    this.model = model;\n    this.animRoot = SkeletonUtils.clone(this.model.gltf.scene);\n    this.mixer = new THREE.AnimationMixer(this.animRoot);\n    gameObject.transform.add(this.animRoot);\n    this.actions = {};\n  }\n  setAnimation(animName) {\n    const clip = this.model.animations[animName];\n    // turn off all current actions\n    for (const action of Object.values(this.actions)) {\n      action.enabled = false;\n    }\n    // get or create existing action for clip\n    const action = this.mixer.clipAction(clip);\n    action.enabled = true;\n    action.reset();\n    action.play();\n    this.actions[animName] = action;\n  }\n  update() {\n    this.mixer.update(globals.deltaTime);\n  }\n}\n</pre>\n<p>You can see it's basically the code we had before that clones the scene we loaded,\nthen sets up an <a href=\"/docs/#api/en/animation/AnimationMixer\"><code class=\"notranslate\" translate=\"no\">AnimationMixer</code></a>. <code class=\"notranslate\" translate=\"no\">setAnimation</code> adds a <a href=\"/docs/#api/en/animation/AnimationAction\"><code class=\"notranslate\" translate=\"no\">AnimationAction</code></a> for a\nparticular <a href=\"/docs/#api/en/animation/AnimationClip\"><code class=\"notranslate\" translate=\"no\">AnimationClip</code></a> if one does not already exist and disables all\nexisting actions.</p>\n<p>The code references <code class=\"notranslate\" translate=\"no\">globals.deltaTime</code>. Let's make a globals object</p>\n<pre class=\"prettyprint showlinemods notranslate lang-js\" translate=\"no\">const globals = {\n  time: 0,\n  deltaTime: 0,\n};\n</pre>\n<p>And update it in the render loop</p>\n<pre class=\"prettyprint showlinemods notranslate lang-js\" translate=\"no\">let then = 0;\nfunction render(now) {\n  // convert to seconds\n  globals.time = now * 0.001;\n  // make sure delta time isn't too big.\n  globals.deltaTime = Math.min(globals.time - then, 1 / 20);\n  then = globals.time;\n</pre>\n<p>The check above for making sure <code class=\"notranslate\" translate=\"no\">deltaTime</code> is not more than 1/20th\nof a second is because otherwise we'd get a huge value for <code class=\"notranslate\" translate=\"no\">deltaTime</code>\nif we hide the tab. We might hide it for seconds or minutes and then\nwhen our tab was brought to the front <code class=\"notranslate\" translate=\"no\">deltaTime</code> would be huge\nand might teleport characters across our game world if we had code like</p>\n<pre class=\"prettyprint showlinemods notranslate lang-js\" translate=\"no\">position += velocity * deltaTime;\n</pre>\n<p>By limiting the maximum <code class=\"notranslate\" translate=\"no\">deltaTime</code> that issue is prevented.</p>\n<p>Now let's make a component for the player.</p>\n<pre class=\"prettyprint showlinemods notranslate lang-js\" translate=\"no\">class Player extends Component {\n  constructor(gameObject) {\n    super(gameObject);\n    const model = models.knight;\n    this.skinInstance = gameObject.addComponent(SkinInstance, model);\n    this.skinInstance.setAnimation('Run');\n  }\n}\n</pre>\n<p>The player calls <code class=\"notranslate\" translate=\"no\">setAnimation</code> with <code class=\"notranslate\" translate=\"no\">'Run'</code>. To know which animations\nare available I modified our previous example to print out the names of\nthe animations</p>\n<pre class=\"prettyprint showlinemods notranslate lang-js\" translate=\"no\">function prepModelsAndAnimations() {\n  Object.values(models).forEach(model =&gt; {\n+    console.log('-------&gt;:', model.url);\n    const animsByName = {};\n    model.gltf.animations.forEach((clip) =&gt; {\n      animsByName[clip.name] = clip;\n+      console.log('  ', clip.name);\n    });\n    model.animations = animsByName;\n  });\n}\n</pre>\n<p>And running it got this list in <a href=\"https://developers.google.com/web/tools/chrome-devtools/console/javascript\">the JavaScript console</a>.</p>\n<pre class=\"prettyprint showlinemods notranslate notranslate\" translate=\"no\"> -------&gt;:  resources/models/animals/Pig.gltf\n    Idle\n    Death\n    WalkSlow\n    Jump\n    Walk\n -------&gt;:  resources/models/animals/Cow.gltf\n    Walk\n    Jump\n    WalkSlow\n    Death\n    Idle\n -------&gt;:  resources/models/animals/Llama.gltf\n    Jump\n    Idle\n    Walk\n    Death\n    WalkSlow\n -------&gt;:  resources/models/animals/Pug.gltf\n    Jump\n    Walk\n    Idle\n    WalkSlow\n    Death\n -------&gt;:  resources/models/animals/Sheep.gltf\n    WalkSlow\n    Death\n    Jump\n    Walk\n    Idle\n -------&gt;:  resources/models/animals/Zebra.gltf\n    Jump\n    Walk\n    Death\n    WalkSlow\n    Idle\n -------&gt;:  resources/models/animals/Horse.gltf\n    Jump\n    WalkSlow\n    Death\n    Walk\n    Idle\n -------&gt;:  resources/models/knight/KnightCharacter.gltf\n    Run_swordRight\n    Run\n    Idle_swordLeft\n    Roll_sword\n    Idle\n    Run_swordAttack\n</pre><p>Fortunately the names of the animations for all the animals match\nwhich will come in handy later. For now we only care the that the\nplayer has an animation called <code class=\"notranslate\" translate=\"no\">Run</code>.</p>\n<p>Let's use these components. Here's the updated init function.\nAll it does is create a <code class=\"notranslate\" translate=\"no\">GameObject</code> and add a <code class=\"notranslate\" translate=\"no\">Player</code> component to it.</p>\n<pre class=\"prettyprint showlinemods notranslate lang-js\" translate=\"no\">const globals = {\n  time: 0,\n  deltaTime: 0,\n};\n+const gameObjectManager = new GameObjectManager();\n\nfunction init() {\n  // hide the loading bar\n  const loadingElem = document.querySelector('#loading');\n  loadingElem.style.display = 'none';\n\n  prepModelsAndAnimations();\n\n+  {\n+    const gameObject = gameObjectManager.createGameObject(scene, 'player');\n+    gameObject.addComponent(Player);\n+  }\n}\n</pre>\n<p>And we need to call <code class=\"notranslate\" translate=\"no\">gameObjectManager.update</code> in our render loop</p>\n<pre class=\"prettyprint showlinemods notranslate lang-js\" translate=\"no\">let then = 0;\nfunction render(now) {\n  // convert to seconds\n  globals.time = now * 0.001;\n  // make sure delta time isn't too big.\n  globals.deltaTime = Math.min(globals.time - then, 1 / 20);\n  then = globals.time;\n\n  if (resizeRendererToDisplaySize(renderer)) {\n    const canvas = renderer.domElement;\n    camera.aspect = canvas.clientWidth / canvas.clientHeight;\n    camera.updateProjectionMatrix();\n  }\n\n-  for (const {mixer} of mixerInfos) {\n-    mixer.update(deltaTime);\n-  }\n+  gameObjectManager.update();\n\n  renderer.render(scene, camera);\n\n  requestAnimationFrame(render);\n}\n</pre>\n<p>and if we run that we get a single player.</p>\n<p></p><div translate=\"no\" class=\"threejs_example_container notranslate\">\n  <div></div>\n  <a class=\"threejs_center\" href=\"/manual/examples/game-just-player.html\" target=\"_blank\">click here to open in a separate window</a>\n</div>\n\n<p></p>\n<p>That was a lot of code just for an entity component system but\nit's infrastructure that most games need.</p>\n<p>Let's add an input system. Rather than read keys directly we'll\nmake a class that other parts of the code can check <code class=\"notranslate\" translate=\"no\">left</code> or <code class=\"notranslate\" translate=\"no\">right</code>.\nThat way we can assign multiple ways to input <code class=\"notranslate\" translate=\"no\">left</code> or <code class=\"notranslate\" translate=\"no\">right</code> etc..\nWe'll start with just keys</p>\n<pre class=\"prettyprint showlinemods notranslate lang-js\" translate=\"no\">// Keeps the state of keys/buttons\n//\n// You can check\n//\n//   inputManager.keys.left.down\n//\n// to see if the left key is currently held down\n// and you can check\n//\n//   inputManager.keys.left.justPressed\n//\n// To see if the left key was pressed this frame\n//\n// Keys are 'left', 'right', 'a', 'b', 'up', 'down'\nclass InputManager {\n  constructor() {\n    this.keys = {};\n    const keyMap = new Map();\n\n    const setKey = (keyName, pressed) =&gt; {\n      const keyState = this.keys[keyName];\n      keyState.justPressed = pressed &amp;&amp; !keyState.down;\n      keyState.down = pressed;\n    };\n\n    const addKey = (keyCode, name) =&gt; {\n      this.keys[name] = { down: false, justPressed: false };\n      keyMap.set(keyCode, name);\n    };\n\n    const setKeyFromKeyCode = (keyCode, pressed) =&gt; {\n      const keyName = keyMap.get(keyCode);\n      if (!keyName) {\n        return;\n      }\n      setKey(keyName, pressed);\n    };\n\n    addKey(37, 'left');\n    addKey(39, 'right');\n    addKey(38, 'up');\n    addKey(40, 'down');\n    addKey(90, 'a');\n    addKey(88, 'b');\n\n    window.addEventListener('keydown', (e) =&gt; {\n      setKeyFromKeyCode(e.keyCode, true);\n    });\n    window.addEventListener('keyup', (e) =&gt; {\n      setKeyFromKeyCode(e.keyCode, false);\n    });\n  }\n  update() {\n    for (const keyState of Object.values(this.keys)) {\n      if (keyState.justPressed) {\n        keyState.justPressed = false;\n      }\n    }\n  }\n}\n</pre>\n<p>The code above tracks whether keys are up or down and you can check\nif a key is currently pressed by checking for example\n<code class=\"notranslate\" translate=\"no\">inputManager.keys.left.down</code>. It also has a <code class=\"notranslate\" translate=\"no\">justPressed</code> property\nfor each key so that you can check the user just pressed the key.\nFor example a jump key you don't want to know if the button is being\nheld down, you want to know did the user press it now.</p>\n<p>Let's create an instance of <code class=\"notranslate\" translate=\"no\">InputManager</code></p>\n<pre class=\"prettyprint showlinemods notranslate lang-js\" translate=\"no\">const globals = {\n  time: 0,\n  deltaTime: 0,\n};\nconst gameObjectManager = new GameObjectManager();\n+const inputManager = new InputManager();\n</pre>\n<p>and update it in our render loop</p>\n<pre class=\"prettyprint showlinemods notranslate lang-js\" translate=\"no\">function render(now) {\n\n  ...\n\n  gameObjectManager.update();\n+  inputManager.update();\n\n  ...\n}\n</pre>\n<p>It needs to be called after <code class=\"notranslate\" translate=\"no\">gameObjectManager.update</code> otherwise\n<code class=\"notranslate\" translate=\"no\">justPressed</code> would never be true inside a component's <code class=\"notranslate\" translate=\"no\">update</code> function.</p>\n<p>Let's use it in the <code class=\"notranslate\" translate=\"no\">Player</code> component</p>\n<pre class=\"prettyprint showlinemods notranslate lang-js\" translate=\"no\">+const kForward = new THREE.Vector3(0, 0, 1);\nconst globals = {\n  time: 0,\n  deltaTime: 0,\n+  moveSpeed: 16,\n};\n\nclass Player extends Component {\n  constructor(gameObject) {\n    super(gameObject);\n    const model = models.knight;\n    this.skinInstance = gameObject.addComponent(SkinInstance, model);\n    this.skinInstance.setAnimation('Run');\n+    this.turnSpeed = globals.moveSpeed / 4;\n  }\n+  update() {\n+    const {deltaTime, moveSpeed} = globals;\n+    const {transform} = this.gameObject;\n+    const delta = (inputManager.keys.left.down  ?  1 : 0) +\n+                  (inputManager.keys.right.down ? -1 : 0);\n+    transform.rotation.y += this.turnSpeed * delta * deltaTime;\n+    transform.translateOnAxis(kForward, moveSpeed * deltaTime);\n+  }\n}\n</pre>\n<p>The code above uses <a href=\"/docs/#api/en/core/Object3D.transformOnAxis\"><code class=\"notranslate\" translate=\"no\">Object3D.transformOnAxis</code></a> to move the player\nforward. <a href=\"/docs/#api/en/core/Object3D.transformOnAxis\"><code class=\"notranslate\" translate=\"no\">Object3D.transformOnAxis</code></a> works in local space so it only\nworks if the object in question is at the root of the scene, not if it's\nparented to something else <a class=\"footnote\" href=\"#parented\" id=\"parented-backref\">1</a></p>\n<p>We also added a global <code class=\"notranslate\" translate=\"no\">moveSpeed</code> and based a <code class=\"notranslate\" translate=\"no\">turnSpeed</code> on the move speed.\nThe turn speed is based on the move speed to try to make sure a character\ncan turn sharply enough to meet its target. If <code class=\"notranslate\" translate=\"no\">turnSpeed</code> so too small\na character will turn around and around circling its target but never\nhitting it. I didn't bother to do the math to calculate the required\nturn speed for a given move speed. I just guessed.</p>\n<p>The code so far would work but if the player runs off the screen there's no\nway to find out where they are. Let's make it so if they are offscreen\nfor more than a certain time they get teleported back to the origin.\nWe can do that by using the three.js <a href=\"/docs/#api/en/math/Frustum\"><code class=\"notranslate\" translate=\"no\">Frustum</code></a> class to check if a point\nis inside the camera's view frustum.</p>\n<p>We need to build a frustum from the camera. We could do this in the Player\ncomponent but other objects might want to use this too so let's add another\ngameobject with a component to manage a frustum.</p>\n<pre class=\"prettyprint showlinemods notranslate lang-js\" translate=\"no\">class CameraInfo extends Component {\n  constructor(gameObject) {\n    super(gameObject);\n    this.projScreenMatrix = new THREE.Matrix4();\n    this.frustum = new THREE.Frustum();\n  }\n  update() {\n    const {camera} = globals;\n    this.projScreenMatrix.multiplyMatrices(\n        camera.projectionMatrix,\n        camera.matrixWorldInverse);\n    this.frustum.setFromProjectionMatrix(this.projScreenMatrix);\n  }\n}\n</pre>\n<p>Then let's setup another gameobject at init time.</p>\n<pre class=\"prettyprint showlinemods notranslate lang-js\" translate=\"no\">function init() {\n  // hide the loading bar\n  const loadingElem = document.querySelector('#loading');\n  loadingElem.style.display = 'none';\n\n  prepModelsAndAnimations();\n\n+  {\n+    const gameObject = gameObjectManager.createGameObject(camera, 'camera');\n+    globals.cameraInfo = gameObject.addComponent(CameraInfo);\n+  }\n\n  {\n    const gameObject = gameObjectManager.createGameObject(scene, 'player');\n    gameObject.addComponent(Player);\n  }\n}\n</pre>\n<p>and now we can use it in the <code class=\"notranslate\" translate=\"no\">Player</code> component.</p>\n<pre class=\"prettyprint showlinemods notranslate lang-js\" translate=\"no\">class Player extends Component {\n  constructor(gameObject) {\n    super(gameObject);\n    const model = models.knight;\n    this.skinInstance = gameObject.addComponent(SkinInstance, model);\n    this.skinInstance.setAnimation('Run');\n    this.turnSpeed = globals.moveSpeed / 4;\n+    this.offscreenTimer = 0;\n+    this.maxTimeOffScreen = 3;\n  }\n  update() {\n-    const {deltaTime, moveSpeed} = globals;\n+    const {deltaTime, moveSpeed, cameraInfo} = globals;\n    const {transform} = this.gameObject;\n    const delta = (inputManager.keys.left.down  ?  1 : 0) +\n                  (inputManager.keys.right.down ? -1 : 0);\n    transform.rotation.y += this.turnSpeed * delta * deltaTime;\n    transform.translateOnAxis(kForward, moveSpeed * deltaTime);\n\n+    const {frustum} = cameraInfo;\n+    if (frustum.containsPoint(transform.position)) {\n+      this.offscreenTimer = 0;\n+    } else {\n+      this.offscreenTimer += deltaTime;\n+      if (this.offscreenTimer &gt;= this.maxTimeOffScreen) {\n+        transform.position.set(0, 0, 0);\n+      }\n+    }\n  }\n}\n</pre>\n<p>One more thing before we try it out, let's add touchscreen support\nfor mobile. First let's add some HTML to touch</p>\n<pre class=\"prettyprint showlinemods notranslate lang-html\" translate=\"no\">&lt;body&gt;\n  &lt;canvas id=\"c\"&gt;&lt;/canvas&gt;\n+  &lt;div id=\"ui\"&gt;\n+    &lt;div id=\"left\"&gt;&lt;img src=\"../resources/images/left.svg\"&gt;&lt;/div&gt;\n+    &lt;div style=\"flex: 0 0 40px;\"&gt;&lt;/div&gt;\n+    &lt;div id=\"right\"&gt;&lt;img src=\"../resources/images/right.svg\"&gt;&lt;/div&gt;\n+  &lt;/div&gt;\n  &lt;div id=\"loading\"&gt;\n    &lt;div&gt;\n      &lt;div&gt;...loading...&lt;/div&gt;\n      &lt;div class=\"progress\"&gt;&lt;div id=\"progressbar\"&gt;&lt;/div&gt;&lt;/div&gt;\n    &lt;/div&gt;\n  &lt;/div&gt;\n&lt;/body&gt;\n</pre>\n<p>and some CSS to style it</p>\n<pre class=\"prettyprint showlinemods notranslate lang-css\" translate=\"no\">#ui {\n  position: absolute;\n  left: 0;\n  top: 0;\n  width: 100%;\n  height: 100%;\n  display: flex;\n  justify-items: center;\n  align-content: stretch;\n}\n#ui&gt;div {\n  display: flex;\n  align-items: flex-end;\n  flex: 1 1 auto;\n}\n.bright {\n  filter: brightness(2);\n}\n#left {\n  justify-content: flex-end;\n}\n#right {\n  justify-content: flex-start;\n}\n#ui img {\n  padding: 10px;\n  width: 80px;\n  height: 80px;\n  display: block;\n}\n</pre>\n<p>The idea here is there is one div, <code class=\"notranslate\" translate=\"no\">#ui</code>, that\ncovers the entire page. Inside will be 2 divs, <code class=\"notranslate\" translate=\"no\">#left</code> and <code class=\"notranslate\" translate=\"no\">#right</code>\nboth of which are almost half the page wide and the entire screen tall.\nIn between there is a 40px separator. If the user slides their finger\nover the left or right side then we need up update <code class=\"notranslate\" translate=\"no\">keys.left</code> and <code class=\"notranslate\" translate=\"no\">keys.right</code>\nin the <code class=\"notranslate\" translate=\"no\">InputManager</code>. This makes the entire screen sensitive to being touched\nwhich seemed better than just small arrows.</p>\n<pre class=\"prettyprint showlinemods notranslate lang-js\" translate=\"no\">class InputManager {\n  constructor() {\n    this.keys = {};\n    const keyMap = new Map();\n\n    const setKey = (keyName, pressed) =&gt; {\n      const keyState = this.keys[keyName];\n      keyState.justPressed = pressed &amp;&amp; !keyState.down;\n      keyState.down = pressed;\n    };\n\n    const addKey = (keyCode, name) =&gt; {\n      this.keys[name] = { down: false, justPressed: false };\n      keyMap.set(keyCode, name);\n    };\n\n    const setKeyFromKeyCode = (keyCode, pressed) =&gt; {\n      const keyName = keyMap.get(keyCode);\n      if (!keyName) {\n        return;\n      }\n      setKey(keyName, pressed);\n    };\n\n    addKey(37, 'left');\n    addKey(39, 'right');\n    addKey(38, 'up');\n    addKey(40, 'down');\n    addKey(90, 'a');\n    addKey(88, 'b');\n\n    window.addEventListener('keydown', (e) =&gt; {\n      setKeyFromKeyCode(e.keyCode, true);\n    });\n    window.addEventListener('keyup', (e) =&gt; {\n      setKeyFromKeyCode(e.keyCode, false);\n    });\n\n+    const sides = [\n+      { elem: document.querySelector('#left'),  key: 'left'  },\n+      { elem: document.querySelector('#right'), key: 'right' },\n+    ];\n+\n+    const clearKeys = () =&gt; {\n+      for (const {key} of sides) {\n+          setKey(key, false);\n+      }\n+    };\n+\n+    const handleMouseMove = (e) =&gt; {\n+      e.preventDefault();\n+      // this is needed because we call preventDefault();\n+      // we also gave the canvas a tabindex so it can\n+      // become the focus\n+      canvas.focus();\n+      window.addEventListener('pointermove', handleMouseMove);\n+      window.addEventListener('pointerup', handleMouseUp);\n+\n+      for (const {elem, key} of sides) {\n+        let pressed = false;\n+        const rect = elem.getBoundingClientRect();\n+        const x = e.clientX;\n+        const y = e.clientY;\n+        const inRect = x &gt;= rect.left &amp;&amp; x &lt; rect.right &amp;&amp;\n+                       y &gt;= rect.top &amp;&amp; y &lt; rect.bottom;\n+        if (inRect) {\n+          pressed = true;\n+        }\n+        setKey(key, pressed);\n+      }\n+    };\n+\n+    function handleMouseUp() {\n+      clearKeys();\n+      window.removeEventListener('pointermove', handleMouseMove, {passive: false});\n+      window.removeEventListener('pointerup', handleMouseUp);\n+    }\n+\n+    const uiElem = document.querySelector('#ui');\n+    uiElem.addEventListener('pointerdown', handleMouseMove, {passive: false});\n+\n+    uiElem.addEventListener('touchstart', (e) =&gt; {\n+      // prevent scrolling\n+      e.preventDefault();\n+    }, {passive: false});\n  }\n  update() {\n    for (const keyState of Object.values(this.keys)) {\n      if (keyState.justPressed) {\n        keyState.justPressed = false;\n      }\n    }\n  }\n}\n</pre>\n<p>And now we should be able to control the character with the left and right\ncursor keys or with our fingers on a touchscreen</p>\n<p></p><div translate=\"no\" class=\"threejs_example_container notranslate\">\n  <div></div>\n  <a class=\"threejs_center\" href=\"/manual/examples/game-player-input.html\" target=\"_blank\">click here to open in a separate window</a>\n</div>\n\n<p></p>\n<p>Ideally we'd do something else if the player went off the screen like move\nthe camera or maybe offscreen = death but this article is already going to be\ntoo long so for now teleporting to the middle was the simplest thing.</p>\n<p>Lets add some animals. We can start it off similar to the <code class=\"notranslate\" translate=\"no\">Player</code> by making\nan <code class=\"notranslate\" translate=\"no\">Animal</code> component.</p>\n<pre class=\"prettyprint showlinemods notranslate lang-js\" translate=\"no\">class Animal extends Component {\n  constructor(gameObject, model) {\n    super(gameObject);\n    const skinInstance = gameObject.addComponent(SkinInstance, model);\n    skinInstance.mixer.timeScale = globals.moveSpeed / 4;\n    skinInstance.setAnimation('Idle');\n  }\n}\n</pre>\n<p>The code above sets the <a href=\"/docs/#api/en/animation/AnimationMixer.timeScale\"><code class=\"notranslate\" translate=\"no\">AnimationMixer.timeScale</code></a> to set the playback\nspeed of the animations relative to the move speed. This way if we\nadjust the move speed the animation will speed up or slow down as well.</p>\n<p>To start we could setup one of each type of animal</p>\n<pre class=\"prettyprint showlinemods notranslate lang-js\" translate=\"no\">function init() {\n  // hide the loading bar\n  const loadingElem = document.querySelector('#loading');\n  loadingElem.style.display = 'none';\n\n  prepModelsAndAnimations();\n  {\n    const gameObject = gameObjectManager.createGameObject(camera, 'camera');\n    globals.cameraInfo = gameObject.addComponent(CameraInfo);\n  }\n\n  {\n    const gameObject = gameObjectManager.createGameObject(scene, 'player');\n    globals.player = gameObject.addComponent(Player);\n    globals.congaLine = [gameObject];\n  }\n\n+  const animalModelNames = [\n+    'pig',\n+    'cow',\n+    'llama',\n+    'pug',\n+    'sheep',\n+    'zebra',\n+    'horse',\n+  ];\n+  animalModelNames.forEach((name, ndx) =&gt; {\n+    const gameObject = gameObjectManager.createGameObject(scene, name);\n+    gameObject.addComponent(Animal, models[name]);\n+    gameObject.transform.position.x = (ndx + 1) * 5;\n+  });\n}\n</pre>\n<p>And that would get us animals standing on the screen but we want them to do\nsomething.</p>\n<p>Let's make them follow the player in a conga line but only if the player gets near enough.\nTo do this we need several states.</p>\n<ul>\n<li><p>Idle:</p>\n<p>Animal is waiting for player to get close</p>\n</li>\n<li><p>Wait for End of Line:</p>\n<p>Animal was tagged by player but now needs to wait for the animal\nat the end of the line to come by so they can join the end of the line.</p>\n</li>\n<li><p>Go to Last:</p>\n<p>Animal needs to walk to where the animal they are following was, at the same time recording\na history of where the animal they are following is currently.</p>\n</li>\n<li><p>Follow</p>\n<p>Animal needs to keep recording a history of where the animal they are following is while\nmoving to where the animal they are following was before.</p>\n</li>\n</ul>\n<p>There are many ways to handle different states like this. A common one is to use\na <a href=\"https://www.google.com/search?q=finite+state+machine\">Finite State Machine</a> and\nto build some class to help us manage the state.</p>\n<p>So, let's do that.</p>\n<pre class=\"prettyprint showlinemods notranslate lang-js\" translate=\"no\">class FiniteStateMachine {\n  constructor(states, initialState) {\n    this.states = states;\n    this.transition(initialState);\n  }\n  get state() {\n    return this.currentState;\n  }\n  transition(state) {\n    const oldState = this.states[this.currentState];\n    if (oldState &amp;&amp; oldState.exit) {\n      oldState.exit.call(this);\n    }\n    this.currentState = state;\n    const newState = this.states[state];\n    if (newState.enter) {\n      newState.enter.call(this);\n    }\n  }\n  update() {\n    const state = this.states[this.currentState];\n    if (state.update) {\n      state.update.call(this);\n    }\n  }\n}\n</pre>\n<p>Here's a simple class. We pass it an object with a bunch of states.\nEach state as 3 optional functions, <code class=\"notranslate\" translate=\"no\">enter</code>, <code class=\"notranslate\" translate=\"no\">update</code>, and <code class=\"notranslate\" translate=\"no\">exit</code>.\nTo switch states we call <code class=\"notranslate\" translate=\"no\">FiniteStateMachine.transition</code> and pass it\nthe name of the new state. If the current state has an <code class=\"notranslate\" translate=\"no\">exit</code> function\nit's called. Then if the new state has an <code class=\"notranslate\" translate=\"no\">enter</code> function it's called.\nFinally each frame <code class=\"notranslate\" translate=\"no\">FiniteStateMachine.update</code> calls the <code class=\"notranslate\" translate=\"no\">update</code> function\nof the current state.</p>\n<p>Let's use it to manage the states of the animals.</p>\n<pre class=\"prettyprint showlinemods notranslate lang-js\" translate=\"no\">// Returns true of obj1 and obj2 are close\nfunction isClose(obj1, obj1Radius, obj2, obj2Radius) {\n  const minDist = obj1Radius + obj2Radius;\n  const dist = obj1.position.distanceTo(obj2.position);\n  return dist &lt; minDist;\n}\n\n// keeps v between -min and +min\nfunction minMagnitude(v, min) {\n  return Math.abs(v) &gt; min\n      ? min * Math.sign(v)\n      : v;\n}\n\nconst aimTowardAndGetDistance = function() {\n  const delta = new THREE.Vector3();\n\n  return function aimTowardAndGetDistance(source, targetPos, maxTurn) {\n    delta.subVectors(targetPos, source.position);\n    // compute the direction we want to be facing\n    const targetRot = Math.atan2(delta.x, delta.z) + Math.PI * 1.5;\n    // rotate in the shortest direction\n    const deltaRot = (targetRot - source.rotation.y + Math.PI * 1.5) % (Math.PI * 2) - Math.PI;\n    // make sure we don't turn faster than maxTurn\n    const deltaRotation = minMagnitude(deltaRot, maxTurn);\n    // keep rotation between 0 and Math.PI * 2\n    source.rotation.y = THREE.MathUtils.euclideanModulo(\n        source.rotation.y + deltaRotation, Math.PI * 2);\n    // return the distance to the target\n    return delta.length();\n  };\n}();\n\nclass Animal extends Component {\n  constructor(gameObject, model) {\n    super(gameObject);\n+    const hitRadius = model.size / 2;\n    const skinInstance = gameObject.addComponent(SkinInstance, model);\n    skinInstance.mixer.timeScale = globals.moveSpeed / 4;\n+    const transform = gameObject.transform;\n+    const playerTransform = globals.player.gameObject.transform;\n+    const maxTurnSpeed = Math.PI * (globals.moveSpeed / 4);\n+    const targetHistory = [];\n+    let targetNdx = 0;\n+\n+    function addHistory() {\n+      const targetGO = globals.congaLine[targetNdx];\n+      const newTargetPos = new THREE.Vector3();\n+      newTargetPos.copy(targetGO.transform.position);\n+      targetHistory.push(newTargetPos);\n+    }\n+\n+    this.fsm = new FiniteStateMachine({\n+      idle: {\n+        enter: () =&gt; {\n+          skinInstance.setAnimation('Idle');\n+        },\n+        update: () =&gt; {\n+          // check if player is near\n+          if (isClose(transform, hitRadius, playerTransform, globals.playerRadius)) {\n+            this.fsm.transition('waitForEnd');\n+          }\n+        },\n+      },\n+      waitForEnd: {\n+        enter: () =&gt; {\n+          skinInstance.setAnimation('Jump');\n+        },\n+        update: () =&gt; {\n+          // get the gameObject at the end of the conga line\n+          const lastGO = globals.congaLine[globals.congaLine.length - 1];\n+          const deltaTurnSpeed = maxTurnSpeed * globals.deltaTime;\n+          const targetPos = lastGO.transform.position;\n+          aimTowardAndGetDistance(transform, targetPos, deltaTurnSpeed);\n+          // check if last thing in conga line is near\n+          if (isClose(transform, hitRadius, lastGO.transform, globals.playerRadius)) {\n+            this.fsm.transition('goToLast');\n+          }\n+        },\n+      },\n+      goToLast: {\n+        enter: () =&gt; {\n+          // remember who we're following\n+          targetNdx = globals.congaLine.length - 1;\n+          // add ourselves to the conga line\n+          globals.congaLine.push(gameObject);\n+          skinInstance.setAnimation('Walk');\n+        },\n+        update: () =&gt; {\n+          addHistory();\n+          // walk to the oldest point in the history\n+          const targetPos = targetHistory[0];\n+          const maxVelocity = globals.moveSpeed * globals.deltaTime;\n+          const deltaTurnSpeed = maxTurnSpeed * globals.deltaTime;\n+          const distance = aimTowardAndGetDistance(transform, targetPos, deltaTurnSpeed);\n+          const velocity = distance;\n+          transform.translateOnAxis(kForward, Math.min(velocity, maxVelocity));\n+          if (distance &lt;= maxVelocity) {\n+            this.fsm.transition('follow');\n+          }\n+        },\n+      },\n+      follow: {\n+        update: () =&gt; {\n+          addHistory();\n+          // remove the oldest history and just put ourselves there.\n+          const targetPos = targetHistory.shift();\n+          transform.position.copy(targetPos);\n+          const deltaTurnSpeed = maxTurnSpeed * globals.deltaTime;\n+          aimTowardAndGetDistance(transform, targetHistory[0], deltaTurnSpeed);\n+        },\n+      },\n+    }, 'idle');\n+  }\n+  update() {\n+    this.fsm.update();\n+  }\n}\n</pre>\n<p>That was big chunk of code but it does what was described above.\nHopefully of you walk through each state it will be clear.</p>\n<p>A few things we need to add. We need the player to add itself\nto the globals so the animals can find it and we need to start the\nconga line with the player's <code class=\"notranslate\" translate=\"no\">GameObject</code>.</p>\n<pre class=\"prettyprint showlinemods notranslate lang-js\" translate=\"no\">function init() {\n\n  ...\n\n  {\n    const gameObject = gameObjectManager.createGameObject(scene, 'player');\n+    globals.player = gameObject.addComponent(Player);\n+    globals.congaLine = [gameObject];\n  }\n\n}\n</pre>\n<p>We also need to compute a size for each model</p>\n<pre class=\"prettyprint showlinemods notranslate lang-js\" translate=\"no\">function prepModelsAndAnimations() {\n+  const box = new THREE.Box3();\n+  const size = new THREE.Vector3();\n  Object.values(models).forEach(model =&gt; {\n+    box.setFromObject(model.gltf.scene);\n+    box.getSize(size);\n+    model.size = size.length();\n    const animsByName = {};\n    model.gltf.animations.forEach((clip) =&gt; {\n      animsByName[clip.name] = clip;\n      // Should really fix this in .blend file\n      if (clip.name === 'Walk') {\n        clip.duration /= 2;\n      }\n    });\n    model.animations = animsByName;\n  });\n}\n</pre>\n<p>And we need the player to record their size</p>\n<pre class=\"prettyprint showlinemods notranslate lang-js\" translate=\"no\">class Player extends Component {\n  constructor(gameObject) {\n    super(gameObject);\n    const model = models.knight;\n+    globals.playerRadius = model.size / 2;\n</pre>\n<p>Thinking about it now it would probably have been smarter\nfor the animals to just target the head of the conga line\ninstead of the player specifically. Maybe I'll come back\nand change that later.</p>\n<p>When I first started this I used just one radius for all animals\nbut of course that was no good as the pug is much smaller than the horse.\nSo I added the difference sizes but I wanted to be able to visualize\nthings. To do that I made a <code class=\"notranslate\" translate=\"no\">StateDisplayHelper</code> component.</p>\n<p>I uses a <a href=\"/docs/#api/en/helpers/PolarGridHelper\"><code class=\"notranslate\" translate=\"no\">PolarGridHelper</code></a> to draw a circle around each character\nand it uses html elements to let each character show some status using\nthe techniques covered in <a href=\"align-html-elements-to-3d.html\">the article on aligning html elements to 3D</a>.</p>\n<p>First we need to add some HTML to host these elements</p>\n<pre class=\"prettyprint showlinemods notranslate lang-html\" translate=\"no\">&lt;body&gt;\n  &lt;canvas id=\"c\"&gt;&lt;/canvas&gt;\n  &lt;div id=\"ui\"&gt;\n    &lt;div id=\"left\"&gt;&lt;img src=\"../resources/images/left.svg\"&gt;&lt;/div&gt;\n    &lt;div style=\"flex: 0 0 40px;\"&gt;&lt;/div&gt;\n    &lt;div id=\"right\"&gt;&lt;img src=\"../resources/images/right.svg\"&gt;&lt;/div&gt;\n  &lt;/div&gt;\n  &lt;div id=\"loading\"&gt;\n    &lt;div&gt;\n      &lt;div&gt;...loading...&lt;/div&gt;\n      &lt;div class=\"progress\"&gt;&lt;div id=\"progressbar\"&gt;&lt;/div&gt;&lt;/div&gt;\n    &lt;/div&gt;\n  &lt;/div&gt;\n+  &lt;div id=\"labels\"&gt;&lt;/div&gt;\n&lt;/body&gt;\n</pre>\n<p>And add some CSS for them</p>\n<pre class=\"prettyprint showlinemods notranslate lang-css\" translate=\"no\">#labels {\n  position: absolute;  /* let us position ourself inside the container */\n  left: 0;             /* make our position the top left of the container */\n  top: 0;\n  color: white;\n  width: 100%;\n  height: 100%;\n  overflow: hidden;\n  pointer-events: none;\n}\n#labels&gt;div {\n  position: absolute;  /* let us position them inside the container */\n  left: 0;             /* make their default position the top left of the container */\n  top: 0;\n  font-size: large;\n  font-family: monospace;\n  user-select: none;   /* don't let the text get selected */\n  text-shadow:         /* create a black outline */\n    -1px -1px 0 #000,\n     0   -1px 0 #000,\n     1px -1px 0 #000,\n     1px  0   0 #000,\n     1px  1px 0 #000,\n     0    1px 0 #000,\n    -1px  1px 0 #000,\n    -1px  0   0 #000;\n}\n</pre>\n<p>Then here's the component</p>\n<pre class=\"prettyprint showlinemods notranslate lang-js\" translate=\"no\">const labelContainerElem = document.querySelector('#labels');\n\nclass StateDisplayHelper extends Component {\n  constructor(gameObject, size) {\n    super(gameObject);\n    this.elem = document.createElement('div');\n    labelContainerElem.appendChild(this.elem);\n    this.pos = new THREE.Vector3();\n\n    this.helper = new THREE.PolarGridHelper(size / 2, 1, 1, 16);\n    gameObject.transform.add(this.helper);\n  }\n  setState(s) {\n    this.elem.textContent = s;\n  }\n  setColor(cssColor) {\n    this.elem.style.color = cssColor;\n    this.helper.material.color.set(cssColor);\n  }\n  update() {\n    const {pos} = this;\n    const {transform} = this.gameObject;\n    const {canvas} = globals;\n    pos.copy(transform.position);\n\n    // get the normalized screen coordinate of that position\n    // x and y will be in the -1 to +1 range with x = -1 being\n    // on the left and y = -1 being on the bottom\n    pos.project(globals.camera);\n\n    // convert the normalized position to CSS coordinates\n    const x = (pos.x *  .5 + .5) * canvas.clientWidth;\n    const y = (pos.y * -.5 + .5) * canvas.clientHeight;\n\n    // move the elem to that position\n    this.elem.style.transform = <code class=\"notranslate\" translate=\"no\">translate(-50%, -50%) translate(${x}px,${y}px)</code>;\n  }\n}\n</pre>\n<p>And we can then add them to the animals like this</p>\n<pre class=\"prettyprint showlinemods notranslate lang-js\" translate=\"no\">class Animal extends Component {\n  constructor(gameObject, model) {\n    super(gameObject);\n+    this.helper = gameObject.addComponent(StateDisplayHelper, model.size);\n\n     ...\n\n  }\n  update() {\n    this.fsm.update();\n+    const dir = THREE.MathUtils.radToDeg(this.gameObject.transform.rotation.y);\n+    this.helper.setState(<code class=\"notranslate\" translate=\"no\">${this.fsm.state}:${dir.toFixed(0)}</code>);\n  }\n}\n</pre>\n<p>While we're at it lets make it so we can turn them on/off using lil-gui like\nwe've used else where</p>\n<pre class=\"prettyprint showlinemods notranslate lang-js\" translate=\"no\">import * as THREE from 'three';\nimport {OrbitControls} from 'three/addons/controls/OrbitControls.js';\nimport {GLTFLoader} from 'three/addons/loaders/GLTFLoader.js';\nimport * as SkeletonUtils from 'three/addons/utils/SkeletonUtils.js';\n+import {GUI} from 'three/addons/libs/lil-gui.module.min.js';\n</pre>\n<pre class=\"prettyprint showlinemods notranslate lang-js\" translate=\"no\">+const gui = new GUI();\n+gui.add(globals, 'debug').onChange(showHideDebugInfo);\n+showHideDebugInfo();\n\nconst labelContainerElem = document.querySelector('#labels');\n+function showHideDebugInfo() {\n+  labelContainerElem.style.display = globals.debug ? '' : 'none';\n+}\n+showHideDebugInfo();\n\nclass StateDisplayHelper extends Component {\n\n  ...\n\n  update() {\n+    this.helper.visible = globals.debug;\n+    if (!globals.debug) {\n+      return;\n+    }\n\n    ...\n  }\n}\n</pre>\n<p>And with that we get the kind of start of a game</p>\n<p></p><div translate=\"no\" class=\"threejs_example_container notranslate\">\n  <div></div>\n  <a class=\"threejs_center\" href=\"/manual/examples/game-conga-line.html\" target=\"_blank\">click here to open in a separate window</a>\n</div>\n\n<p></p>\n<p>Originally I set out to make a <a href=\"https://www.google.com/search?q=snake+game\">snake game</a>\nwhere as you add animals to your line it gets harder because you need to avoid\ncrashing into them. I'd also have put some obstacles in the scene and maybe a fence or some\nbarrier around the perimeter.</p>\n<p>Unfortunately the animals are long and thin. From above here's the zebra.</p>\n<div class=\"threejs_center\"><img src=\"../resources/images/zebra.png\" style=\"width: 113px;\"></div>\n\n<p>The code so far is using circle collisions which means if we had obstacles like a fence\nthen this would be considered a collision</p>\n<div class=\"threejs_center\"><img src=\"../resources/images/zebra-collisions.svg\" style=\"width: 400px;\"></div>\n\n<p>That's no good. Even animal to animal we'd have the same issue</p>\n<p>I thought about writing a 2D rectangle to rectangle collision system but I\nquickly realized it could really be a lot of code. Checking that 2 arbitrarily\noriented boxes overlap is not too much code and for our game with just a few\nobjects it might work but looking into it after a few objects you quickly start\nneeding to optimize the collision checking. First you might go through all\nobjects that can possibly collide with each other and check their bounding\nspheres or bounding circles or their axially aligned bounding boxes. Once you\nknow which objects <em>might</em> be colliding then you need to do more work to check if\nthey are <em>actually</em> colliding. Often even checking the bounding spheres is too\nmuch work and you need some kind of better spacial structure for the objects so\nyou can more quickly only check objects possibly near each other.</p>\n<p>Then, once you write the code to check if 2 objects collide you generally want\nto make a collision system rather than manually asking \"do I collide with these\nobjects\". A collision system emits events or calls callbacks in relation to\nthings colliding. The advantage is it can check all the collisions at once so no\nobjects get checked more than once where as if you manually call some \"am I\ncolliding\" function often objects will be checked more than once wasting time.</p>\n<p>Making that collision system would probably not be more than 100-300 lines of\ncode for just checking arbitrarily oriented rectangles but it's still a ton more\ncode so it seemed best to leave it out.</p>\n<p>Another solution would have been to try to find other characters that are\nmostly circular from the top. Other humanoid characters for example instead\nof animals in which case the circle checking might work animal to animal.\nIt would not work animal to fence, well we'd have to add circle to rectangle\nchecking. I thought about making the fence a fence of bushes or poles, something\ncircular but then I'd need probably 120 to 200 of them to surround the play area\nwhich would run into the optimization issues mentioned above.</p>\n<p>These are reasons many games use an existing solution. Often these solutions\nare part of a physics library. The physical library needs to know if objects\ncollide with each other so on top of providing physics they can also be used\nto detect collision.</p>\n<p>If you're looking for a solution some of the three.js examples use\n<a href=\"https://github.com/kripken/ammo.js/\">ammo.js</a> so that might be one.</p>\n<p>One other solution might have been to place the obstacles on a grid\nand try to make it so each animal and the player just need to look at\nthe grid. While that would be performant I felt that's best left as an exercise\nfor the reader 😜</p>\n<p>One more thing, many game systems have something called <a href=\"https://www.google.com/search?q=coroutines\"><em>coroutines</em></a>.\nCoroutines are routines that can pause while running and continue later.</p>\n<p>Let's make the main character emit musical notes like they are leading\nthe line by singing. There are many ways we could implement this but for now\nlet's do it using coroutines.</p>\n<p>First, here's a class to manage coroutines</p>\n<pre class=\"prettyprint showlinemods notranslate lang-js\" translate=\"no\">function* waitSeconds(duration) {\n  while (duration &gt; 0) {\n    duration -= globals.deltaTime;\n    yield;\n  }\n}\n\nclass CoroutineRunner {\n  constructor() {\n    this.generatorStacks = [];\n    this.addQueue = [];\n    this.removeQueue = new Set();\n  }\n  isBusy() {\n    return this.addQueue.length + this.generatorStacks.length &gt; 0;\n  }\n  add(generator, delay = 0) {\n    const genStack = [generator];\n    if (delay) {\n      genStack.push(waitSeconds(delay));\n    }\n    this.addQueue.push(genStack);\n  }\n  remove(generator) {\n    this.removeQueue.add(generator);\n  }\n  update() {\n    this._addQueued();\n    this._removeQueued();\n    for (const genStack of this.generatorStacks) {\n      const main = genStack[0];\n      // Handle if one coroutine removes another\n      if (this.removeQueue.has(main)) {\n        continue;\n      }\n      while (genStack.length) {\n        const topGen = genStack[genStack.length - 1];\n        const {value, done} = topGen.next();\n        if (done) {\n          if (genStack.length === 1) {\n            this.removeQueue.add(topGen);\n            break;\n          }\n          genStack.pop();\n        } else if (value) {\n          genStack.push(value);\n        } else {\n          break;\n        }\n      }\n    }\n    this._removeQueued();\n  }\n  _addQueued() {\n    if (this.addQueue.length) {\n      this.generatorStacks.splice(this.generatorStacks.length, 0, ...this.addQueue);\n      this.addQueue = [];\n    }\n  }\n  _removeQueued() {\n    if (this.removeQueue.size) {\n      this.generatorStacks = this.generatorStacks.filter(genStack =&gt; !this.removeQueue.has(genStack[0]));\n      this.removeQueue.clear();\n    }\n  }\n}\n</pre>\n<p>It does things similar to <code class=\"notranslate\" translate=\"no\">SafeArray</code> to make sure that it's safe to add or remove\ncoroutines while other coroutines are running. It also handles nested coroutines.</p>\n<p>To make a coroutine you make a <a href=\"https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/function*\">JavaScript generator function</a>.\nA generator function is preceded by the keyword <code class=\"notranslate\" translate=\"no\">function*</code> (the asterisk is important!)</p>\n<p>Generator functions can <code class=\"notranslate\" translate=\"no\">yield</code>. For example</p>\n<pre class=\"prettyprint showlinemods notranslate lang-js\" translate=\"no\">function* count0To9() {\n  for (let i = 0; i &lt; 10; ++i) {\n    console.log(i);\n    yield;\n  }\n}\n</pre>\n<p>If we added this function to the <code class=\"notranslate\" translate=\"no\">CoroutineRunner</code> above it would print\nout each number, 0 to 9, once per frame or rather once per time we called <code class=\"notranslate\" translate=\"no\">runner.update</code>.</p>\n<pre class=\"prettyprint showlinemods notranslate lang-js\" translate=\"no\">const runner = new CoroutineRunner();\nrunner.add(count0To9);\nwhile(runner.isBusy()) {\n  runner.update();\n}\n</pre>\n<p>Coroutines are removed automatically when they are finished.\nTo remove a coroutine early, before it reaches the end you need to keep\na reference to its generator like this</p>\n<pre class=\"prettyprint showlinemods notranslate lang-js\" translate=\"no\">const gen = count0To9();\nrunner.add(gen);\n\n// sometime later\n\nrunner.remove(gen);\n</pre>\n<p>In any case, in the player let's use a coroutine to emit a note every half second to 1 second</p>\n<pre class=\"prettyprint showlinemods notranslate lang-js\" translate=\"no\">class Player extends Component {\n  constructor(gameObject) {\n\n    ...\n\n+    this.runner = new CoroutineRunner();\n+\n+    function* emitNotes() {\n+      for (;;) {\n+        yield waitSeconds(rand(0.5, 1));\n+        const noteGO = gameObjectManager.createGameObject(scene, 'note');\n+        noteGO.transform.position.copy(gameObject.transform.position);\n+        noteGO.transform.position.y += 5;\n+        noteGO.addComponent(Note);\n+      }\n+    }\n+\n+    this.runner.add(emitNotes());\n  }\n  update() {\n+    this.runner.update();\n\n  ...\n\n  }\n}\n\nfunction rand(min, max) {\n  if (max === undefined) {\n    max = min;\n    min = 0;\n  }\n  return Math.random() * (max - min) + min;\n}\n</pre>\n<p>You can see we make a <code class=\"notranslate\" translate=\"no\">CoroutineRunner</code> and we add an <code class=\"notranslate\" translate=\"no\">emitNotes</code> coroutine.\nThat function will run forever, waiting 0.5 to 1 seconds and then creating a game object\nwith a <code class=\"notranslate\" translate=\"no\">Note</code> component.</p>\n<p>For the <code class=\"notranslate\" translate=\"no\">Note</code> component first lets make a texture with a note on it and\ninstead of loading a note image let's make one using a canvas like we covered in <a href=\"canvas-textures.html\">the article on canvas textures</a>.</p>\n<pre class=\"prettyprint showlinemods notranslate lang-js\" translate=\"no\">function makeTextTexture(str) {\n  const ctx = document.createElement('canvas').getContext('2d');\n  ctx.canvas.width = 64;\n  ctx.canvas.height = 64;\n  ctx.font = '60px sans-serif';\n  ctx.textAlign = 'center';\n  ctx.textBaseline = 'middle';\n  ctx.fillStyle = '#FFF';\n  ctx.fillText(str, ctx.canvas.width / 2, ctx.canvas.height / 2);\n  return new THREE.CanvasTexture(ctx.canvas);\n}\nconst noteTexture = makeTextTexture('♪');\n</pre>\n<p>The texture we create above is white each means when we use it\nwe can set the material's color and get a note of any color.</p>\n<p>Now that we have a noteTexture here's the <code class=\"notranslate\" translate=\"no\">Note</code> component.\nIt uses <a href=\"/docs/#api/en/materials/SpriteMaterial\"><code class=\"notranslate\" translate=\"no\">SpriteMaterial</code></a> and a <a href=\"/docs/#api/en/objects/Sprite\"><code class=\"notranslate\" translate=\"no\">Sprite</code></a> like we covered in\n<a href=\"billboards.html\">the article on billboards</a> </p>\n<pre class=\"prettyprint showlinemods notranslate lang-js\" translate=\"no\">class Note extends Component {\n  constructor(gameObject) {\n    super(gameObject);\n    const {transform} = gameObject;\n    const noteMaterial = new THREE.SpriteMaterial({\n      color: new THREE.Color().setHSL(rand(1), 1, 0.5),\n      map: noteTexture,\n      side: THREE.DoubleSide,\n      transparent: true,\n    });\n    const note = new THREE.Sprite(noteMaterial);\n    note.scale.setScalar(3);\n    transform.add(note);\n    this.runner = new CoroutineRunner();\n    const direction = new THREE.Vector3(rand(-0.2, 0.2), 1, rand(-0.2, 0.2));\n\n    function* moveAndRemove() {\n      for (let i = 0; i &lt; 60; ++i) {\n        transform.translateOnAxis(direction, globals.deltaTime * 10);\n        noteMaterial.opacity = 1 - (i / 60);\n        yield;\n      }\n      transform.parent.remove(transform);\n      gameObjectManager.removeGameObject(gameObject);\n    }\n\n    this.runner.add(moveAndRemove());\n  }\n  update() {\n    this.runner.update();\n  }\n}\n</pre>\n<p>All it does is setup a <a href=\"/docs/#api/en/objects/Sprite\"><code class=\"notranslate\" translate=\"no\">Sprite</code></a>, then pick a random velocity and move\nthe transform at that velocity for 60 frames while fading out the note\nby setting the material's <a href=\"/docs/#api/en/materials/Material#opacity\"><code class=\"notranslate\" translate=\"no\">opacity</code></a>.\nAfter the loop it the removes the transform\nfrom the scene and the note itself from active gameobjects.</p>\n<p>One last thing, let's add a few more animals</p>\n<pre class=\"prettyprint showlinemods notranslate lang-js\" translate=\"no\">function init() {\n\n   ...\n\n  const animalModelNames = [\n    'pig',\n    'cow',\n    'llama',\n    'pug',\n    'sheep',\n    'zebra',\n    'horse',\n  ];\n+  const base = new THREE.Object3D();\n+  const offset = new THREE.Object3D();\n+  base.add(offset);\n+\n+  // position animals in a spiral.\n+  const numAnimals = 28;\n+  const arc = 10;\n+  const b = 10 / (2 * Math.PI);\n+  let r = 10;\n+  let phi = r / b;\n+  for (let i = 0; i &lt; numAnimals; ++i) {\n+    const name = animalModelNames[rand(animalModelNames.length) | 0];\n    const gameObject = gameObjectManager.createGameObject(scene, name);\n    gameObject.addComponent(Animal, models[name]);\n+    base.rotation.y = phi;\n+    offset.position.x = r;\n+    offset.updateWorldMatrix(true, false);\n+    offset.getWorldPosition(gameObject.transform.position);\n+    phi += arc / r;\n+    r = b * phi;\n  }\n</pre>\n<p></p><div translate=\"no\" class=\"threejs_example_container notranslate\">\n  <div></div>\n  <a class=\"threejs_center\" href=\"/manual/examples/game-conga-line-w-notes.html\" target=\"_blank\">click here to open in a separate window</a>\n</div>\n\n<p></p>\n<p>You might be asking, why not use <code class=\"notranslate\" translate=\"no\">setTimeout</code>? The problem with <code class=\"notranslate\" translate=\"no\">setTimeout</code>\nis it's not related to the game clock. For example above we made the maximum\namount of time allowed to elapse between frames to be 1/20th of a second.\nOur coroutine system will respect that limit but <code class=\"notranslate\" translate=\"no\">setTimeout</code> would not.</p>\n<p>Of course we could have made a simple timer ourselves</p>\n<pre class=\"prettyprint showlinemods notranslate lang-js\" translate=\"no\">class Player ... {\n  update() {\n    this.noteTimer -= globals.deltaTime;\n    if (this.noteTimer &lt;= 0) {\n      // reset timer\n      this.noteTimer = rand(0.5, 1);\n      // create a gameobject with a note component\n    }\n  }\n</pre>\n<p>And for this particular case that might have been better but as you add\nmore and things you'll get more and more variables added to your classes\nwhere as with coroutines you can often just <em>fire and forget</em>.</p>\n<p>Given our animal's simple states we could also have implemented them\nwith a coroutine in the form of</p>\n<pre class=\"prettyprint showlinemods notranslate lang-js\" translate=\"no\">// pseudo code!\nfunction* animalCoroutine() {\n   setAnimation('Idle');\n   while(playerIsTooFar()) {\n     yield;\n   }\n   const target = endOfLine;\n   setAnimation('Jump');\n   while(targetIsTooFar()) {\n     aimAt(target);\n     yield;\n   }\n   setAnimation('Walk')\n   while(notAtOldestPositionOfTarget()) {\n     addHistory();\n     aimAt(target);\n     yield;\n   }\n   for(;;) {\n     addHistory();\n     const pos = history.unshift();\n     transform.position.copy(pos);\n     aimAt(history[0]);\n     yield;\n   }\n}\n</pre>\n<p>This would have worked but of course as soon as our states were not so linear\nwe'd have had to switch to a <code class=\"notranslate\" translate=\"no\">FiniteStateMachine</code>.</p>\n<p>It also wasn't clear to me if coroutines should run independently of their\ncomponents. We could have made a global <code class=\"notranslate\" translate=\"no\">CoroutineRunner</code> and put all\ncoroutines on it. That would make cleaning them up harder. As it is now\nif the gameobject is removed all of its components are removed and\ntherefore the coroutine runners created are no longer called and it will\nall get garbage collected. If we had global runner then it would be\nthe responsibility of each component to remove any coroutines it added\nor else some other mechanism of registering coroutines with a particular\ncomponent or gameobject would be needed so that removing one removes the\nothers.</p>\n<p>There are lots more issues a\nnormal game engine would deal with. As it is there is no order to how\ngameobjects or their components are run. They are just run in the order added.\nMany game systems add a priority so the order can be set or changed.</p>\n<p>Another issue we ran into is the <code class=\"notranslate\" translate=\"no\">Note</code> removing its gameobject's transform from the scene.\nThat seems like something that should happen in <code class=\"notranslate\" translate=\"no\">GameObject</code> since it was <code class=\"notranslate\" translate=\"no\">GameObject</code>\nthat added the transform in the first place. Maybe <code class=\"notranslate\" translate=\"no\">GameObject</code> should have\na <code class=\"notranslate\" translate=\"no\">dispose</code> method that is called by <code class=\"notranslate\" translate=\"no\">GameObjectManager.removeGameObject</code>?</p>\n<p>Yet another is how we're manually calling <code class=\"notranslate\" translate=\"no\">gameObjectManager.update</code> and <code class=\"notranslate\" translate=\"no\">inputManager.update</code>.\nMaybe there should be a <code class=\"notranslate\" translate=\"no\">SystemManager</code> which these global services can add themselves\nand each service will have its <code class=\"notranslate\" translate=\"no\">update</code> function called. In this way if we added a new\nservice like <code class=\"notranslate\" translate=\"no\">CollisionManager</code> we could just add it to the system manager and not\nhave to edit the render loop.</p>\n<p>I'll leave those kinds of issues up to you.\nI hope this article has given you some ideas for your own game engine.</p>\n<p>Maybe I should promote a game jam. If you click the <em>jsfiddle</em> or <em>codepen</em> buttons\nabove the last example they'll open in those sites ready to edit. Add some features,\nChange the game to a pug leading a bunch of knights. Use the knight's rolling animation\nas a bowling ball and make an animal bowling game. Make an animal relay race.\nIf you make a cool game post a link in the comments below.</p>\n<div class=\"footnotes\">\n[<a id=\"parented\">1</a>]: technically it would still work if none of the parents have any translation, rotation, or scale <a href=\"#parented-backref\">§</a>.\n</div>\n        </div>\n      </div>\n    </div>",
    "success": true,
    "error": "",
    "crawled_at": 1786376809.300204,
    "is_shell": false
  },
  {
    "url": "https://threejs.org/manual/en/webgpurenderer.html",
    "title": "WebGPURenderer",
    "content": "<div class=\"container\">\n        <div class=\"lesson-title\">\n          <h1>WebGPURenderer</h1>\n        </div>\n        <div class=\"lesson\">\n          <div class=\"lesson-main\">\n            \n            <p>\n              The new <code class=\"notranslate\" translate=\"no\">WebGPURenderer</code> is the next-generation renderer for three.js. This article provides a short overview about the new \n              renderer and basic guidelines about the usage.\n            </p>\n\n            <h2>Overview</h2>\n\n            <p>\n              <code class=\"notranslate\" translate=\"no\">WebGPURenderer</code> is designed to be the modern alternative to the long-standing <code class=\"notranslate\" translate=\"no\">WebGLRenderer</code>.\n              Its primary goal is to use WebGPU, which is a modern, high-performance graphics and compute 3D API. However, it's built to be a \n              universal renderer. If a device/browser doesn't support WebGPU, the renderer can automatically fall back to using a WebGL 2 backend.\n            </p>\n             <p>\n              Providing a WebGL 2 backend as a fallback is a crucial design decision since it allows applications to benefit from WebGPU but without \n              sacrificing the support for devices which only support WebGL 2.\n             </p>\n\n             <p>\n             Apart from the fact that <code class=\"notranslate\" translate=\"no\">WebGPURenderer</code> enables access to WebGPU, it offers an exciting feature set:\n             </p><p>\n\n            </p><ul>\n              <li>\n                <code class=\"notranslate\" translate=\"no\">WebGPURenderer</code> comes with a new node-based material system which allows to develop custom materials with greater flexibility and \n                more robustness.\n              </li>\n              <li>\n                The renderer supports TSL, the three.js shading language. With TSL, developers can write shader code with JavaScript in a \n                platform-independent manner. Shader code written in TSL can be transpiled to WGSL or GLSL depending on the available backend.\n              </li>\n              <li>\n                <code class=\"notranslate\" translate=\"no\">WebGPURenderer</code> comes with a new post-processing stack with built-in Multiple Render Targets (MRT) support and automatic pass combination thanks to the \n                node material.\n              </li>\n            </ul>\n\n            Let's find out how to integrate <code class=\"notranslate\" translate=\"no\">WebGPURenderer</code> in three.js applications.\n\n            <h2>Usage</h2>\n\n             <p>\n             <code class=\"notranslate\" translate=\"no\">WebGPURenderer</code> has different build files so the way you import three.js changes:\n            </p>\n            \n<pre class=\"prettyprint showlinemods notranslate lang-js\" translate=\"no\">-  import * as THREE from 'three';\n+  import * as THREE from 'three/webgpu';\n</pre>\n\n            <p>\n              If you are using an import map, it's recommended to change it to the following (the paths differ depending on your setup):\n            </p>\n<pre class=\"prettyprint showlinemods notranslate lang-js\" translate=\"no\">  &lt;script type=\"importmap\"&gt;\n    {\n      \"imports\": {\n        \"three\": \"../build/three.webgpu.js\",\n        \"three/webgpu\": \"../build/three.webgpu.js\",\n        \"three/tsl\": \"../build/three.tsl.js\",\n        \"three/addons/\": \"./jsm/\"\n      }\n    }\n  &lt;/script&gt;\n</pre>\n\n             <p>\n              You can create an instance of the renderer just like with <code class=\"notranslate\" translate=\"no\">WebGLRenderer</code>:\n            </p>\n\n<pre class=\"prettyprint showlinemods notranslate lang-js\" translate=\"no\">const renderer = new THREE.WebGPURenderer( { antialias: true } );\nrenderer.setPixelRatio( window.devicePixelRatio );\nrenderer.setSize( window.innerWidth, window.innerHeight );\nrenderer.setAnimationLoop( render );\ndocument.body.appendChild( renderer.domElement );\n</pre>\n            <p>\n              It's important to understand that WebGPU is initialized in an asynchronous fashion. Hence, it is recommended to use \n              <code class=\"notranslate\" translate=\"no\">setAnimationLoop()</code> to define the animation loop of your app since this approach will automatically ensure the renderer\n              is initialized when rendering the first frame. If you prefer to manage your animation loop via <code class=\"notranslate\" translate=\"no\">window.requestAnimationFrame()</code> or\n              if you have to use the renderer in your init routine, you need an additional line in the above code section.\n            </p>\n<pre class=\"prettyprint showlinemods notranslate lang-js\" translate=\"no\">const renderer = new THREE.WebGPURenderer( { antialias: true } );\nrenderer.setPixelRatio( window.devicePixelRatio );\nrenderer.setSize( window.innerWidth, window.innerHeight );\nrenderer.setAnimationLoop( render );\ndocument.body.appendChild( renderer.domElement );\n\n+  await renderer.init();\n</pre>\n            <p>\n              Most common methods known from <code class=\"notranslate\" translate=\"no\">WebGLRenderer</code> like <code class=\"notranslate\" translate=\"no\">clear()</code>, <code class=\"notranslate\" translate=\"no\">setRenderTarget()</code>or <code class=\"notranslate\" translate=\"no\">dispose()</code> are also present in <code class=\"notranslate\" translate=\"no\">WebGPURenderer</code>. \n              Please have a look at the <a href=\"https://threejs.org/docs/#Renderer\" target=\"_blank\">API documentation</a> for a full overview of the renderer's public interface.\n            </p>\n\n             <p>\n              Like mentioned in the initial part of the guide, <code class=\"notranslate\" translate=\"no\">WebGPURenderer</code> uses a WebGPU backend by default and a WebGL 2 backend as a fallback. \n              If you want to force the usage of WebGL 2 for testing purposes or if you want to exclude the usage of WebGPU for certain reasons, you can \n              make use of the <code class=\"notranslate\" translate=\"no\">forceWebGL</code> parameter.\n            </p>\n<pre class=\"prettyprint showlinemods notranslate lang-js\" translate=\"no\">-  const renderer = new THREE.WebGPURenderer( { antialias: true } );\n+  const renderer = new THREE.WebGPURenderer( { antialias: true, forceWebGL: true } );\n</pre>\n\n            <h2>Migration</h2>\n\n            <p>\n              If you want to give <code class=\"notranslate\" translate=\"no\">WebGPURenderer</code> a try, you have to be aware of the following.\n            </p>\n\n            <ul>\n              <li>\n                Custom materials based on <code class=\"notranslate\" translate=\"no\">ShaderMaterial</code>, <code class=\"notranslate\" translate=\"no\">RawShaderMaterial</code> and modifications of built-in materials via <code class=\"notranslate\" translate=\"no\">onBeforeCompile()</code>\n                are not supported in <code class=\"notranslate\" translate=\"no\">WebGPURenderer</code>. This part of your application must be ported to node materials and TSL.\n              </li>\n              <li>\n                <code class=\"notranslate\" translate=\"no\">EffectComposer</code> with its effect passes are not supported because <code class=\"notranslate\" translate=\"no\">WebGPURenderer</code> comes with a new, more modern \n                post-processing stack. Similar to materials, post-processing effects are now written in TSL and the effect chain is expressed \n                as a node composition. All common effects have already been ported to <code class=\"notranslate\" translate=\"no\">WebGPURenderer</code> and exist in a more performant version\n                as a node class. We have also added new effects like SSGI, SSS or a better DoF exclusively for the new renderer. Check out the\n                <a href=\"https://threejs.org/examples/?q=webgpu%20postprocessing\" target=\"_blank\">official examples</a> to get an overview of\n                the current supported effects.\n              </li>\n              <li>\n                The renderer itself is still in an experimental state although its maturity level has been greatly improved in the last years. \n                Still, depending on your application and scene setup, you will encounter missing features or a better performance with <code class=\"notranslate\" translate=\"no\">WebGLRenderer</code>. \n                Feel free to file an issue at GitHub so we are aware of open tasks. We are improving <code class=\"notranslate\" translate=\"no\">WebGPURenderer</code> with each release so it's \n                recommended to upgrade to the latest version whenever possible.\n              </li>\n            </ul>\n\n            <h2>State of WebGLRenderer</h2>\n\n            <p>Although in the meanwhile a lot of work happens in context of <code class=\"notranslate\" translate=\"no\">WebGPURenderer</code>, the node material and TSL, <code class=\"notranslate\" translate=\"no\">WebGLRenderer</code> is still maintained \n              and the recommended choice for pure WebGL 2 applications. However, keep in mind that there are no plans to add larger new features to \n              the renderer since the project's focus is now on <code class=\"notranslate\" translate=\"no\">WebGPURenderer</code> which you can easily see at the latest release notes. That said,\n              we are currently investigating the possibility to add limited node material support to <code class=\"notranslate\" translate=\"no\">WebGLRenderer</code> in order to make the transition to\n              <code class=\"notranslate\" translate=\"no\">WebGPURenderer</code> easier for certain projects.\n            </p>\n\n          </div>\n        </div>\n      </div>",
    "success": true,
    "error": "",
    "crawled_at": 1786376809.300204,
    "is_shell": false
  },
  {
    "url": "https://threejs.org/manual/en/webgpu-postprocessing.html",
    "title": "Post-Processing with WebGPURenderer",
    "content": "<div class=\"container\">\n        <div class=\"lesson-title\">\n          <h1>Post-Processing with WebGPURenderer</h1>\n        </div>\n        <div class=\"lesson\">\n          <div class=\"lesson-main\">\n            \n            <p>\n              <code class=\"notranslate\" translate=\"no\">WebGPURenderer</code> comes with a brand-new component for post-processing. This article shows how the new system \n              works and provides some basic guidelines about the usage.\n            </p>\n\n            <h2>Overview</h2>\n\n             <p>\n              The previous post-processing for <code class=\"notranslate\" translate=\"no\">WebGLRenderer</code> had many conceptual issues. Making use of Multiple Render Targets \n              (MRT) was cumbersome due to the limited support in the renderer and there was no automatic pass/effect combination\n              to improve the overall performance.\n            </p>\n\n            <p>\n              The new post-processing stack for <code class=\"notranslate\" translate=\"no\">WebGPURenderer</code> was designed to support these use cases right from the beginning.\n            </p>\n            <ul>\n              <li>\n                <code class=\"notranslate\" translate=\"no\">WebGPURenderer</code> comes with full, built-in MRT support.\n              </li>\n              <li>\n                The system combines effects if possible which reduces the overall number of render passes.\n              </li>\n              <li>\n                The effect chain is expressed as a node composition which allows a more flexible effect setup.\n              </li>\n            </ul>\n            <p>\n               Let's find out how to integrate post-processing in three.js applications.\n            </p>\n\n            <h2>Basics</h2>\n\n            <p>\n              First, please read the instructions in the guide about <a href=\"webgpurenderer\">WebGPURenderer</a> to correctly configure your \n              imports. After that, you can create an instance of the render pipleine module like so:\n            </p>\n\n<pre class=\"prettyprint showlinemods notranslate lang-js\" translate=\"no\">const renderPipeline = new THREE.RenderPipeline( renderer );\n</pre>\n\n            <p>\n              The instance of <code class=\"notranslate\" translate=\"no\">RenderPipeline</code> replaces the previous instance of <code class=\"notranslate\" translate=\"no\">EffectComposer</code>. To make sure you actually\n              use the output of the module, you have to update your animation loop like so:\n            </p>\n<pre class=\"prettyprint showlinemods notranslate lang-js\" translate=\"no\">-  renderer.render( scene, camera );\n+  renderPipeline.render();\n</pre>\n\n            <p>\n             Many post-processing setups start with a so called \"scene pass\" or \"beauty pass\" that represents the image of you rendered scene.\n             This image should be subsequently enhanced by different effects like Bloom, Depth-of-Field or SSR. Start by importing the <code class=\"notranslate\" translate=\"no\">pass()</code> TSL \n             function from the TSL namespace and use it to create the pass.\n            </p>\n<pre class=\"prettyprint showlinemods notranslate lang-js\" translate=\"no\">import { pass } from 'three/tsl';\n\n// in your init routine\n\nconst scenePass = pass( scene, camera );\n</pre>\n            <p>\n             The basic idea of the node system is to represent materials or post-processing effects as node compositions. To configure a basic \n             Dotscreen and RGB shift effect, you create effect nodes with TSL functions and compose them together.\n            </p>\n\n<pre class=\"prettyprint showlinemods notranslate lang-js\" translate=\"no\">import { pass } from 'three/tsl';\n+  import { dotScreen } from 'three/addons/tsl/display/DotScreenNode.js';\n+  import { rgbShift } from 'three/addons/tsl/display/RGBShiftNode.js';\n\n// in your init routine\n\nconst scenePass = pass( scene, camera );\n\n+  const dotScreenPass = dotScreen( scenePass );\n+  const rgbShiftPass = rgbShift( dotScreenPass );\n</pre>\n\n            <p>\n            When you are done, you can simply assign the final node to the <code class=\"notranslate\" translate=\"no\">RenderPipeline</code> instance.\n            </p>\n<pre class=\"prettyprint showlinemods notranslate lang-js\" translate=\"no\">renderPipeline.outputNode = rgbShiftPass;\n</pre>\n\n            <h2>Tone Mapping and Color Spaces</h2>\n\n            <p>\n            When using post-processing, tone mapping and color space conversion are automatically applied at the end \n            of your effect chain. Sometimes you want full control over how and when these steps are executed though.\n            For example if you want to apply FXAA with <code class=\"notranslate\" translate=\"no\">FXAANode</code> or color grading with <code class=\"notranslate\" translate=\"no\">Lut3DNode</code>, you can disable automatic tone \n            mapping and color space conversion and apply it via <code class=\"notranslate\" translate=\"no\">renderOutput()</code> by yourself.\n            </p>\n\n<pre class=\"prettyprint showlinemods notranslate lang-js\" translate=\"no\">import { pass, renderOutput } from 'three/tsl';\nimport { fxaa } from 'three/addons/tsl/display/FXAANode.js';\n\n// in your init routine\n\nconst renderPipeline = new THREE.RenderPipeline( renderer );\nrenderPipeline.outputColorTransform = false; // disable default output color transform\n\nconst scenePass = pass( scene, camera );\nconst outputPass = renderOutput( scenePass ); // apply tone mapping and color space conversion here\n\n// FXAA must be computed in sRGB color space\n\nconst fxaaPass = fxaa( outputPass );\nrenderPipeline.outputNode = fxaaPass;\n</pre>\n\n          <p>\n            It is not mandatory to use <code class=\"notranslate\" translate=\"no\">renderOutput()</code>, you can also implement a custom tone mapping and color space conversion\n            based on your requirements.\n          </p>\n\n          <h2>MRT</h2>\n\n          <p>\n            The new post-processing stack has built-in Multiple Render Targets (MRT) support which is crucial for more advanced\n            setups. MRT allows you to produce multiple outputs in a single render pass. So for example when rendering your scene \n            with TRAA, you need below setup to prepare the inputs for the anti-aliasing.\n          </p>\n\n<pre class=\"prettyprint showlinemods notranslate lang-js\" translate=\"no\">import { pass, mrt, output, velocity } from 'three/tsl';\n\n// in your init routine\n\nconst scenePass = pass( scene, camera );\nscenePass.setMRT( mrt( {\n  output: output,\n  velocity: velocity\n} ) );\n</pre>\n          <p>\n            The configuration object you assign to the <code class=\"notranslate\" translate=\"no\">mrt()</code> TSL function describes the different outputs of the pass. In this case,\n            we save the default output (the scene's beauty) and scene's velocity since we want to setup a TRAA. If you also require \n            the scene's depth, there is no need to configure it as a MRT output. You get it for free in your default output pass if \n            you request it in your app. If you know want to use these outputs in subsequent effects, you can query them as texture nodes.\n          </p>\n\n<pre class=\"prettyprint showlinemods notranslate lang-js\" translate=\"no\">import { traa } from 'three/addons/tsl/display/TRAANode.js';\n\n// in your init routine\n\nconst scenePassColor = scenePass.getTextureNode( 'output' );\nconst scenePassDepth = scenePass.getTextureNode( 'depth' );\nconst scenePassVelocity = scenePass.getTextureNode( 'velocity' );\n\nconst traaPass = traa( scenePassColor, scenePassDepth, scenePassVelocity, camera );\nrenderPipeline.outputNode = traaPass;\n</pre>\n\n          <p>\n           The MRT configuration varies depending on your setup. There are many different TSL objects like <code class=\"notranslate\" translate=\"no\">output</code>, <code class=\"notranslate\" translate=\"no\">velocity</code>,\n           <code class=\"notranslate\" translate=\"no\">normalView</code> or <code class=\"notranslate\" translate=\"no\">emissive</code> than you can use to save per-fragment data in MRT attachments. To improve performance and avoid\n           hitting memory restrictions, it's important to pack and optimize your data in complex MRT setups. By default all attachments \n           are RGBA16 (Half-Float) in precision which is not necessary for all types of data. As an example, below code queries the \n           <code class=\"notranslate\" translate=\"no\">diffuseColor</code> attachment and sets its format to RGBA8 which cuts down the memory and bandwidth by half.\n          </p>\n\n<pre class=\"prettyprint showlinemods notranslate lang-js\" translate=\"no\">const diffuseTexture = scenePass.getTexture( 'diffuseColor' );\ndiffuseTexture.type = THREE.UnsignedByteType;\n</pre> \n\n          <p>\n            Below setup for Scree-Space Reflections (SSR) converts the default FP16 normals into RGBA8 colors and packs metalness/roughness \n            into a single attachment. The usage of the <code class=\"notranslate\" translate=\"no\">sample()</code> TSL functions allows to implement custom unpacking. In this instance, it\n            converts the color back to a (normalized) direction vector.\n          </p>\n\n<pre class=\"prettyprint showlinemods notranslate lang-js\" translate=\"no\">scenePass.setMRT( mrt( {\n  output: output,\n  normal: packNormalToRGB( normalView ),\n  metalrough: vec2( metalness, roughness )\n} ) );\n\n// use RGBA8 instead of RGBA16\n\nconst normalTexture = scenePass.getTexture( 'normal' );\nnormalTexture.type = THREE.UnsignedByteType;\n\nconst metalRoughTexture = scenePass.getTexture( 'metalrough' );\nmetalRoughTexture.type = THREE.UnsignedByteType;\n\n// custom unpacking. use the resulting \"sceneNormal\" instead of \"scenePassNormal\"\n// in subsequent effects\n\nconst sceneNormal = sample( ( uv ) =&gt; {\n\n  return unpackRGBToNormal( scenePassNormal.sample( uv ) );\n\n} );\n</pre>\n\n          <p>\n            We want to further improve the packing/unpacking features in the future to offer more ways to pack/unpack MRT data. In the meanwhile,\n            please have a look at the <a href=\"https://threejs.org/examples/?q=webgpu%20postprocessing\" target=\"_blank\">official examples</a> to\n            get an overview about the existing effects and setups.\n          </p>\n          </div>\n        </div>\n      </div>",
    "success": true,
    "error": "",
    "crawled_at": 1786376809.300204,
    "is_shell": false
  },
  {
    "url": "https://threejs.org/manual/en/webxr-basics.html",
    "title": "VR",
    "content": "<div class=\"container\">\n      <div class=\"lesson-title\">\n        <h1>VR</h1>\n      </div>\n      <div class=\"lesson\">\n        <div class=\"lesson-main\">\n          <p>Making a VR app in three.js is pretty simple. You basically just have to tell\nthree.js you want to use WebXR. If you think about it a few things about WebXR\nshould be clear. Which way the camera is pointing is supplied by the VR system\nitself since the user turns their head to choose a direction to look. Similarly\nthe field of view and aspect will be supplied by the VR system since each system\nhas a different field of view and display aspect.</p>\n<p>Let's take an example from the article on <a href=\"responsive.html\">making a responsive webpage</a>\nand make it support VR.</p>\n<p>Before we get started you're going to need a VR capable device like an Android\nsmartphone, Google Daydream, Oculus Go, Oculus Rift, Vive, Samsung Gear VR., an\niPhone with a <a href=\"https://apps.apple.com/us/app/webxr-viewer/id1295998056\">WebXR browser</a>.</p>\n<p>Next, if you are running locally you need to run a simple web server like is\ncovered in <a href=\"setup.html\">the article on setting up</a>. </p>\n<p>If the device you are using to view VR is not the same computer you're running\non you need to serve your webpage via https or else the browser will not allow using\nthe WebXR API. The server mentioned in <a href=\"setup.html\">the article on setting up</a>\ncalled <a href=\"https://greggman.github.io/servez\" target=\"_blank\">Servez</a> has an option to use https.\nCheck it and start the server. </p>\n<div class=\"threejs_center\"><img src=\"../resources/images/servez-https.png\" class=\"nobg\" style=\"width: 912px;\"></div>\n\n<p>The note the URLs. You need the one that is your computer's local ipaddress.\nIt will usually start with <code class=\"notranslate\" translate=\"no\">192</code>, <code class=\"notranslate\" translate=\"no\">172</code> or <code class=\"notranslate\" translate=\"no\">10</code>. Type that full address, including the <code class=\"notranslate\" translate=\"no\">https://</code> part\ninto your VR device's browser. Note: Your computer and your VR device need to be on the same local network\nor WiFi and you probably need to be on a home network. note: Many cafes are setup to disallow this kind of\nmachine to machine connection.</p>\n<p>You'll be greeted with an error something like the one below. Click \"advanced\" and then click\n<em>proceed</em>.</p>\n<div class=\"threejs_center\"><img src=\"../resources/images/https-warning.gif\"></div>\n\n<p>Now you can run your examples.</p>\n<p>If you're really going to do WebXR development another thing you should learn about is\n<a href=\"https://developers.google.com/web/tools/chrome-devtools/remote-debugging/\">remote debugging</a>\nso that you can see console warnings, errors, and of course actually\n<a href=\"debugging-javascript.html\">debug your code</a>.</p>\n<p>If you just want to see the code work below you can just run the code from\nthis site.</p>\n<p>The first thing we need to do is include the VR support after\nincluding three.js</p>\n<pre class=\"prettyprint showlinemods notranslate lang-js\" translate=\"no\">import * as THREE from 'three';\n+import {VRButton} from 'three/addons/webxr/VRButton.js';\n</pre>\n<p>Then we need to enable three.js's WebXR support and add its\nVR button to our page</p>\n<pre class=\"prettyprint showlinemods notranslate lang-js\" translate=\"no\">function main() {\n  const canvas = document.querySelector('#c');\n  const renderer = new THREE.WebGLRenderer({antialias: true, canvas});\n+  renderer.xr.enabled = true;\n+  document.body.appendChild(VRButton.createButton(renderer));\n</pre>\n<p>We need to let three.js run our render loop. Until now we have used a\n<code class=\"notranslate\" translate=\"no\">requestAnimationFrame</code> loop but to support VR we need to let three.js handle\nour render loop for us. We can do that by calling\n<a href=\"/docs/#api/en/renderers/WebGLRenderer.setAnimationLoop\"><code class=\"notranslate\" translate=\"no\">WebGLRenderer.setAnimationLoop</code></a> and passing a function to call for the loop.</p>\n<pre class=\"prettyprint showlinemods notranslate lang-js\" translate=\"no\">function render(time) {\n  time *= 0.001;\n\n  if (resizeRendererToDisplaySize(renderer)) {\n    const canvas = renderer.domElement;\n    camera.aspect = canvas.clientWidth / canvas.clientHeight;\n    camera.updateProjectionMatrix();\n  }\n\n  cubes.forEach((cube, ndx) =&gt; {\n    const speed = 1 + ndx * .1;\n    const rot = time * speed;\n    cube.rotation.x = rot;\n    cube.rotation.y = rot;\n  });\n\n  renderer.render(scene, camera);\n\n-  requestAnimationFrame(render);\n}\n\n-requestAnimationFrame(render);\n+renderer.setAnimationLoop(render);\n</pre>\n<p>There is one more detail. We should probably set a camera height\nthat's kind of average for a standing user.</p>\n<pre class=\"prettyprint showlinemods notranslate lang-js\" translate=\"no\">const camera = new THREE.PerspectiveCamera(fov, aspect, near, far);\n+camera.position.set(0, 1.6, 0);\n</pre>\n<p>and move the cubes up to be in front of the camera</p>\n<pre class=\"prettyprint showlinemods notranslate lang-js\" translate=\"no\">const cube = new THREE.Mesh(geometry, material);\nscene.add(cube);\n\ncube.position.x = x;\n+cube.position.y = 1.6;\n+cube.position.z = -2;\n</pre>\n<p>We set them to <code class=\"notranslate\" translate=\"no\">z = -2</code> since the camera will now be at <code class=\"notranslate\" translate=\"no\">z = 0</code> and\ncamera defaults to looking down the -z axis.</p>\n<p>This brings up an extremely important point. <strong>Units in VR are in meters</strong>.\nIn other words <strong>One Unit = One Meter</strong>. This means the camera is 1.6 meters above 0.\nThe cube's centers are 2 meters in front of the camera. Each cube\nis 1x1x1 meter large. This is important because VR needs to adjust things to the\nuser <em>in the real world</em>. That means we need the units used in three.js to match\nthe user's own movements.</p>\n<p>And with that we should get 3 spinning cubes in front\nof the camera with a button to enter VR.</p>\n<p></p><div translate=\"no\" class=\"threejs_example_container notranslate\">\n  <div></div>\n  <a class=\"threejs_center\" href=\"/manual/examples/webxr-basic.html\" target=\"_blank\">click here to open in a separate window</a>\n</div>\n\n<p></p>\n<p>I find that VR works better if we have something surrounding the camera like\nroom for reference so let's add a simple grid cubemap like we covered in\n<a href=\"backgrounds.html\">the article on backgrounds</a>. We'll just use the same grid\ntexture for each side of the cube which will give as a grid room.</p>\n<pre class=\"prettyprint showlinemods notranslate lang-js\" translate=\"no\">const scene = new THREE.Scene();\n+{\n+  const loader = new THREE.CubeTextureLoader();\n+  const texture = loader.load([\n+    'resources/images/grid-1024.png',\n+    'resources/images/grid-1024.png',\n+    'resources/images/grid-1024.png',\n+    'resources/images/grid-1024.png',\n+    'resources/images/grid-1024.png',\n+    'resources/images/grid-1024.png',\n+  ]);\n+  scene.background = texture;\n+}\n</pre>\n<p>That's better.</p>\n<p></p><div translate=\"no\" class=\"threejs_example_container notranslate\">\n  <div></div>\n  <a class=\"threejs_center\" href=\"/manual/examples/webxr-basic-w-background.html\" target=\"_blank\">click here to open in a separate window</a>\n</div>\n\n<p></p>\n<p>Note: To actually see VR you will need a WebXR compatible device.\nI believe most Android Phones can support WebXR using Chrome or Firefox.\nFor iOS you might be able to use this <a href=\"https://apps.apple.com/us/app/webxr-viewer/id1295998056\">WebXR App</a>\nthough in general WebXR support on iOS is unsupported as of May 2019.</p>\n<p>To use WebXR on Android or iPhone you'll need a <em>VR Headset</em>\nfor phones. You can get them for anywhere from $5 for one made of cardboard\nto $100. Unfortunately I don't know which ones to recommend. I've purchased\n6 of them over the years and they are all of varying quality. I've\nnever paid more than about $25.</p>\n<p>Just to mention some of the issues</p>\n<ol>\n<li><p>Do they fit your phone</p>\n<p>Phones come in a variety of sizes and so the VR headsets need to match.\nMany headsets claim to match a large variety of sizes. My experience\nis the more sizes they match the worse they actually are since instead\nof being designed for a specific size they have to make compromises\nto match more sizes. Unfortunately multi-size headsets are the most common type.</p>\n</li>\n<li><p>Can they focus for your face</p>\n<p>Some devices have more adjustments than others. Generally there\nare at most 2 adjustments. How far the lenses are from your eyes\nand how far apart the lenses are.</p>\n</li>\n<li><p>Are they too reflective</p>\n<p>Many headsets of a cone of plastic from your eye to the phone.\nIf that plastic is shinny or reflective then it will act like\na mirror reflecting the screen and be very distracting.</p>\n<p>Few if any of the reviews seem to cover this issue.</p>\n</li>\n<li><p>Are the comfortable on your face.</p>\n<p>Most of the devices rest on your nose like a pair of glasses.\nThat can hurt after a few minutes. Some have straps that go around\nyour head. Others have a 3rd strap that goes over your head. These\nmay or may not help keep the device at the right place.</p>\n<p>It turns out for most (all?) devices, you eyes need to be centered\nwith the lenses. If the lenses are slightly above or below your\neyes the image gets out of focus. This can be very frustrating\nas things might start in focus but 45-60 seconds later the device\nhas shifted up or down 1 millimeter and you suddenly realize you've\nbeen struggling to focus on a blurry image.</p>\n</li>\n<li><p>Can they support your glasses.</p>\n<p>If you wear eye glasses then you'll need to read the reviews to see\nif a particular headset works well with eye glasses.</p>\n</li>\n</ol>\n<p>I really can't make any recommendations unfortunately. <a href=\"https://vr.google.com/cardboard/get-cardboard/\">Google has some\ncheap recommendations made from cardboard</a>\nsome of them as low as $5 so maybe start there and if you enjoy it\nthen consider upgrading. $5 is like the price of 1 coffee so seriously, give it try!</p>\n<p>There are also 3 basic types of devices.</p>\n<ol>\n<li><p>3 degrees of freedom (3dof), no input device</p>\n<p>This is generally the phone style although sometimes you can\nbuy a 3rd party input device. The 3 degrees of freedom\nmean you can look up/down (1), left/right(2) and you can tilt\nyour head left and right (3).</p>\n</li>\n<li><p>3 degrees of freedom (3dof) with 1 input device (3dof)</p>\n<p>This is basically Google Daydream and Oculus GO</p>\n<p>These also allow 3 degrees of freedom and include a small\ncontroller that acts like a laser pointer inside VR.\nThe laser pointer also only has 3 degrees of freedom. The\nsystem can tell which way the input device is pointing but\nit can not tell where the device is.</p>\n</li>\n<li><p>6 degrees of freedom (6dof) with input devices (6dof)</p>\n<p>These are <em>the real deal</em> haha. 6 degrees of freedom\nmeans not only do these device know which way you are looking\nbut they also know where your head actually is. That means\nif you move from left to right or forward and back or stand up / sit down\nthe devices can register this and everything in VR moves accordingly.\nIt's spookily and amazingly real feeling. With a good demo\nyou'll be blown away or at least I was and still am.</p>\n<p>Further these devices usually include 2 controllers, one\nfor each hand and the system can tell exactly where your\nhands are and which way they are oriented and so you can\nmanipulate things in VR by just reaching out, touching,\npushing, twisting, etc...</p>\n<p>6 degree of freedom devices include the Vive and Vive Pro,\nthe Oculus Rift and Quest, and I believe all of the Windows MR devices.</p>\n</li>\n</ol>\n<p>With all that covered I don't for sure know which devices will work with WebXR.\nI'm 99% sure that most Android phones will work when running Chrome. You may\nneed to turn on WebXR support in <a href=\"about:flags\"><code class=\"notranslate\" translate=\"no\">about:flags</code></a>. I also know Google\nDaydream will also work and similarly you need to enable WebXR support in\n<a href=\"about:flags\"><code class=\"notranslate\" translate=\"no\">about:flags</code></a>. Oculus Rift, Vive, and Vive Pro will work via\nChrome or Firefox. I'm less sure about Oculus Go and Oculus Quest as both of\nthem use custom OSes but according to the internet they both appear to work.</p>\n<p>Okay, after that long detour about VR Devices and WebXR\nthere's some things to cover</p>\n<ul>\n<li><p>Supporting both VR and Non-VR</p>\n<p>AFAICT, at least as of r112, there is no easy way to support\nboth VR and non-VR modes with three.js. Ideally\nif not in VR mode you'd be able to control the camera using\nwhatever means you want, for example the <a href=\"/docs/#examples/controls/OrbitControls\"><code class=\"notranslate\" translate=\"no\">OrbitControls</code></a>,\nand you'd get some kind of event when switching into and\nout of VR mode so that you could turn the controls on/off.</p>\n</li>\n</ul>\n<p>If three.js adds some support to do both I'll try to update\nthis article. Until then you might need 2 versions of your\nsite OR pass in a flag in the URL, something like</p>\n<pre class=\"prettyprint showlinemods notranslate notranslate\" translate=\"no\">https://mysite.com/mycooldemo?allowvr=true\n</pre><p>Then we could add some links in to switch modes</p>\n<pre class=\"prettyprint showlinemods notranslate lang-html\" translate=\"no\">&lt;body&gt;\n  &lt;canvas id=\"c\"&gt;&lt;/canvas&gt;\n+  &lt;div class=\"mode\"&gt;\n+    &lt;a href=\"?allowvr=true\" id=\"vr\"&gt;Allow VR&lt;/a&gt;\n+    &lt;a href=\"?\" id=\"nonvr\"&gt;Use Non-VR Mode&lt;/a&gt;\n+  &lt;/div&gt;\n&lt;/body&gt;\n</pre>\n<p>and some CSS to position them</p>\n<pre class=\"prettyprint showlinemods notranslate lang-css\" translate=\"no\">body {\n    margin: 0;\n}\n#c {\n    width: 100%;\n    height: 100%;\n    display: block;\n}\n+.mode {\n+  position: absolute;\n+  right: 1em;\n+  top: 1em;\n+}\n</pre>\n<p>in your code you could use that parameter like this</p>\n<pre class=\"prettyprint showlinemods notranslate lang-js\" translate=\"no\">function main() {\n  const canvas = document.querySelector('#c');\n  const renderer = new THREE.WebGLRenderer({antialias: true, canvas});\n-  renderer.xr.enabled = true;\n-  document.body.appendChild(VRButton.createButton(renderer));\n\n  const fov = 75;\n  const aspect = 2;  // the canvas default\n  const near = 0.1;\n  const far = 5;\n  const camera = new THREE.PerspectiveCamera(fov, aspect, near, far);\n  camera.position.set(0, 1.6, 0);\n\n+  const params = (new URL(document.location)).searchParams;\n+  const allowvr = params.get('allowvr') === 'true';\n+  if (allowvr) {\n+    renderer.xr.enabled = true;\n+    document.body.appendChild(VRButton.createButton(renderer));\n+    document.querySelector('#vr').style.display = 'none';\n+  } else {\n+    // no VR, add some controls\n+    const controls = new OrbitControls(camera, canvas);\n+    controls.target.set(0, 1.6, -2);\n+    controls.update();\n+    document.querySelector('#nonvr').style.display = 'none';\n+  }\n</pre>\n<p>Whether that's good or bad I don't know. I have a feeling the differences\nbetween what's needed for VR and what's needed for non-VR are often\nvery different so for all but the most simple things maybe 2 separate pages\nare better? You'll have to decide.</p>\n<p>Note for various reasons this will not work in the live editor\non this site so if you want to check it out\n<a href=\"../examples/webxr-basic-vr-optional.html\" target=\"_blank\">click here</a>.\nIt should start in non-VR mode and you can use the mouse or fingers to move\nthe camera. Clicking \"Allow VR\" should switch to allow VR mode and you should\nbe able to click \"Enter VR\" if you're on a VR device.</p>\n<ul>\n<li><p>Deciding on the level of VR support</p>\n<p>Above we covered 3 types of VR devices. </p>\n<ul>\n<li>3DOF no input</li>\n<li>3DOF + 3DOF input</li>\n<li>6DOF + 6DOF input</li>\n</ul>\n<p>You need to decide how much effort you're willing to put in\nto support each type of device.</p>\n<p>For example the simplest device has no input. The best you can\ngenerally do is make it so there are some buttons or objects in the user's view\nand if the user aligns some marker in the center of the display\non those objects for 1/2 a second or so then that button is clicked.\nA common UX is to display a small timer that will appear over the object indicating\nif you keep the marker there for a moment the object/button will be selected.</p>\n<p>Since there is no other input that's about the best you can do</p>\n<p>The next level up you have one 3DOF input device. Generally it\ncan point at things and the user has at least 2 buttons. The Daydream\nalso has a touchpad which provides normal touch inputs.</p>\n<p>In any case if a user has this type of device it's far more\ncomfortable for the user to by able to point at things with\ntheir controller than it is to make them do it with their\nhead by looking at things.</p>\n<p>A similar level to that might be 3DOF or 6DOF device with a\ngame console controller. You'll have to decide what to do here.\nI suspect the most common thing is the user still has to look\nto point and the controller is just used for buttons.</p>\n<p>The last level is a user with a 6DOF headset and 2 6DOF controllers.\nThose users will find an experience that is only 3DOF to often\nbe frustrating. Similarly they usually expect to be able to\nvirtually manipulate things with their hands in VR so you'll\nhave to decide if you want to support that or not.</p>\n</li>\n</ul>\n<p>As you can see getting started in VR is pretty easy but\nactually making something shippable in VR will require\nlots of decision making and design.</p>\n<p>This was a pretty brief intro into VR with three.js. We'll\ncover some of the input methods in <a href=\"webxr-look-to-select.html\">future articles</a>.</p>\n\n        </div>\n      </div>\n    </div>",
    "success": true,
    "error": "",
    "crawled_at": 1786376809.300204,
    "is_shell": false
  },
  {
    "url": "https://threejs.org/manual/en/webxr-look-to-select.html",
    "title": "VR - Look to Select",
    "content": "<div class=\"container\">\n      <div class=\"lesson-title\">\n        <h1>VR - Look to Select</h1>\n      </div>\n      <div class=\"lesson\">\n        <div class=\"lesson-main\">\n          <p><strong>NOTE: The examples on this page require a VR capable\ndevice. Without one they won't work. See <a href=\"webxr.html\">previous article</a>\nas to why</strong></p>\n<p>In the <a href=\"webxr.html\">previous article</a> we went over\na very simple VR example using three.js and we discussed\nthe various kinds of VR systems.</p>\n<p>The simplest and possibly most common is the Google Cardboard style of VR which\nis basically a phone put into a $5 - $50 face mask. This kind of VR has no\ncontroller so people have to come up with creative solutions for allowing user\ninput.</p>\n<p>The most common solution is \"look to select\" where if the\nuser points their head at something for a moment it gets\nselected.</p>\n<p>Let's implement \"look to select\"! We'll start with\n<a href=\"webxr.html\">an example from the previous article</a>\nand to do it we'll add the <code class=\"notranslate\" translate=\"no\">PickHelper</code> we made in\n<a href=\"picking.html\">the article on picking</a>. Here it is.</p>\n<pre class=\"prettyprint showlinemods notranslate lang-js\" translate=\"no\">class PickHelper {\n  constructor() {\n    this.raycaster = new THREE.Raycaster();\n    this.pickedObject = null;\n    this.pickedObjectSavedColor = 0;\n  }\n  pick(normalizedPosition, scene, camera, time) {\n    // restore the color if there is a picked object\n    if (this.pickedObject) {\n      this.pickedObject.material.emissive.setHex(this.pickedObjectSavedColor);\n      this.pickedObject = undefined;\n    }\n\n    // cast a ray through the frustum\n    this.raycaster.setFromCamera(normalizedPosition, camera);\n    // get the list of objects the ray intersected\n    const intersectedObjects = this.raycaster.intersectObjects(scene.children);\n    if (intersectedObjects.length) {\n      // pick the first object. It's the closest one\n      this.pickedObject = intersectedObjects[0].object;\n      // save its color\n      this.pickedObjectSavedColor = this.pickedObject.material.emissive.getHex();\n      // set its emissive color to flashing red/yellow\n      this.pickedObject.material.emissive.setHex((time * 8) % 2 &gt; 1 ? 0xFFFF00 : 0xFF0000);\n    }\n  }\n}\n</pre>\n<p>For an explanation of that code <a href=\"picking.html\">see the article on picking</a>.</p>\n<p>To use it we just need to create an instance and call it in our render loop</p>\n<pre class=\"prettyprint showlinemods notranslate lang-js\" translate=\"no\">+const pickHelper = new PickHelper();\n\n...\nfunction render(time) {\n  time *= 0.001;\n\n  ...\n\n+  // 0, 0 is the center of the view in normalized coordinates.\n+  pickHelper.pick({x: 0, y: 0}, scene, camera, time);\n</pre>\n<p>In the original picking example we converted the mouse coordinates\nfrom CSS pixels into normalized coordinates that go from -1 to +1\nacross the canvas.</p>\n<p>In this case though we will always pick where the camera is\nfacing which is the center of the screen so we pass in <code class=\"notranslate\" translate=\"no\">0</code> for\nboth <code class=\"notranslate\" translate=\"no\">x</code> and <code class=\"notranslate\" translate=\"no\">y</code> which is the center in normalized coordinates.</p>\n<p>And with that objects will flash when we look at them</p>\n<p></p><div translate=\"no\" class=\"threejs_example_container notranslate\">\n  <div></div>\n  <a class=\"threejs_center\" href=\"/manual/examples/webxr-look-to-select.html\" target=\"_blank\">click here to open in a separate window</a>\n</div>\n\n<p></p>\n<p>Typically we don't want selection to be immediate. Instead we require the user\nto keep the camera on the thing they want to select for a few moments to give them\na chance not to select something by accident.</p>\n<p>To do that we need some kind of meter or gauge or some way\nto convey that the user must keep looking and for how long.</p>\n<p>One easy way we could do that is to make a 2 color texture\nand use a texture offset to slide the texture across a model.</p>\n<p>Let's do this by itself to see it work before we add it to\nthe VR example.</p>\n<p>First we make an <a href=\"/docs/#api/en/cameras/OrthographicCamera\"><code class=\"notranslate\" translate=\"no\">OrthographicCamera</code></a></p>\n<pre class=\"prettyprint showlinemods notranslate lang-js\" translate=\"no\">const left = -2;    // Use values for left\nconst right = 2;    // right, top and bottom\nconst top = 1;      // that match the default\nconst bottom = -1;  // canvas size.\nconst near = -1;\nconst far = 1;\nconst camera = new THREE.OrthographicCamera(left, right, top, bottom, near, far);\n</pre>\n<p>And of course update it if the canvas changes size</p>\n<pre class=\"prettyprint showlinemods notranslate lang-js\" translate=\"no\">function render(time) {\n  time *= 0.001;\n\n  if (resizeRendererToDisplaySize(renderer)) {\n    const canvas = renderer.domElement;\n    const aspect = canvas.clientWidth / canvas.clientHeight;\n+    camera.left = -aspect;\n+    camera.right = aspect;\n    camera.updateProjectionMatrix();\n  }\n  ...\n</pre>\n<p>We now have a camera that shows 2 units above and below the center and aspect units\nleft and right.</p>\n<p>Next let's make a 2 color texture. We'll use a <a href=\"/docs/#api/en/textures/DataTexture\"><code class=\"notranslate\" translate=\"no\">DataTexture</code></a>\nwhich we've used a few <a href=\"indexed-textures.html\">other</a>\n<a href=\"post-processing-3dlut.html\">places</a>.</p>\n<pre class=\"prettyprint showlinemods notranslate lang-js\" translate=\"no\">function makeDataTexture(data, width, height) {\n  const texture = new THREE.DataTexture(data, width, height, THREE.RGBAFormat);\n  texture.minFilter = THREE.NearestFilter;\n  texture.magFilter = THREE.NearestFilter;\n  texture.needsUpdate = true;\n  return texture;\n}\n\nconst cursorColors = new Uint8Array([\n  64, 64, 64, 64,       // dark gray\n  255, 255, 255, 255,   // white\n]);\nconst cursorTexture = makeDataTexture(cursorColors, 2, 1);\n</pre>\n<p>We'll then use that texture on a <a href=\"/docs/#api/en/geometries/TorusGeometry\"><code class=\"notranslate\" translate=\"no\">TorusGeometry</code></a></p>\n<pre class=\"prettyprint showlinemods notranslate lang-js\" translate=\"no\">const ringRadius = 0.4;\nconst tubeRadius = 0.1;\nconst tubeSegments = 4;\nconst ringSegments = 64;\nconst cursorGeometry = new THREE.TorusGeometry(\n    ringRadius, tubeRadius, tubeSegments, ringSegments);\n\nconst cursorMaterial = new THREE.MeshBasicMaterial({\n  color: 'white',\n  map: cursorTexture,\n  transparent: true,\n  blending: THREE.CustomBlending,\n  blendSrc: THREE.OneMinusDstColorFactor,\n  blendDst: THREE.OneMinusSrcColorFactor,\n});\nconst cursor = new THREE.Mesh(cursorGeometry, cursorMaterial);\nscene.add(cursor);\n</pre>\n<p>and then in <code class=\"notranslate\" translate=\"no\">render</code> lets adjust the texture's offset</p>\n<pre class=\"prettyprint showlinemods notranslate lang-js\" translate=\"no\">function render(time) {\n  time *= 0.001;\n\n  if (resizeRendererToDisplaySize(renderer)) {\n    const canvas = renderer.domElement;\n    const aspect = canvas.clientWidth / canvas.clientHeight;\n    camera.left = -aspect;\n    camera.right = aspect;\n    camera.updateProjectionMatrix();\n  }\n\n+  const fromStart = 0;\n+  const fromEnd = 2;\n+  const toStart = -0.5;\n+  const toEnd = 0.5;\n+  cursorTexture.offset.x = THREE.MathUtils.mapLinear(\n+      time % 2,\n+      fromStart, fromEnd,\n+      toStart, toEnd);\n\n  renderer.render(scene, camera);\n}\n</pre>\n<p><code class=\"notranslate\" translate=\"no\">THREE.MathUtils.mapLinear</code> takes a value that goes between <code class=\"notranslate\" translate=\"no\">fromStart</code> and <code class=\"notranslate\" translate=\"no\">fromEnd</code>\nand maps it to a value between <code class=\"notranslate\" translate=\"no\">toStart</code> and <code class=\"notranslate\" translate=\"no\">toEnd</code>. In the case above we're\ntaking <code class=\"notranslate\" translate=\"no\">time % 2</code> which means a value that goes from 0 to 2 and maps\nthat to a value that goes from -0.5 to 0.5</p>\n<p><a href=\"textures.html\">Textures</a> are mapped to geometry using normalized texture coordinates\nthat go from 0 to 1. That means our 2x1 pixel image, set to the default\nwrapping mode of <code class=\"notranslate\" translate=\"no\">THREE.ClampToEdge</code>, if we adjust the\ntexture coordinates by -0.5 then the entire mesh will be the first color\nand if we adjust the texture coordinates by +0.5 the entire mesh will\nbe the second color. In between with the filtering set to <code class=\"notranslate\" translate=\"no\">THREE.NearestFilter</code>\nwe'll be able to move the transition between the 2 colors through the geometry.</p>\n<p>Let's add a background texture while we're at it just like we\ncovered in <a href=\"backgrounds.html\">the article on backgrounds</a>.\nWe'll just use a 2x2 set of colors but set the texture's repeat\nsettings to give us an 8x8 grid. This will give our cursor something\nto be rendered over so we can check it against different colors.</p>\n<pre class=\"prettyprint showlinemods notranslate lang-js\" translate=\"no\">+const backgroundColors = new Uint8Array([\n+    0,   0,   0, 255,  // black\n+   90,  38,  38, 255,  // dark red\n+  100, 175, 103, 255,  // medium green\n+  255, 239, 151, 255,  // light yellow\n+]);\n+const backgroundTexture = makeDataTexture(backgroundColors, 2, 2);\n+backgroundTexture.wrapS = THREE.RepeatWrapping;\n+backgroundTexture.wrapT = THREE.RepeatWrapping;\n+backgroundTexture.repeat.set(4, 4);\n\nconst scene = new THREE.Scene();\n+scene.background = backgroundTexture;\n</pre>\n<p>Now if we run that you'll see we get a circle like gauge\nand that we can set where the gauge is.</p>\n<p></p><div translate=\"no\" class=\"threejs_example_container notranslate\">\n  <div></div>\n  <a class=\"threejs_center\" href=\"/manual/examples/webxr-look-to-select-selector.html\" target=\"_blank\">click here to open in a separate window</a>\n</div>\n\n<p></p>\n<p>A few things to notice <strong>and try</strong>.</p>\n<ul>\n<li><p>We set the <code class=\"notranslate\" translate=\"no\">cursorMaterial</code>'s <code class=\"notranslate\" translate=\"no\">blending</code>, <code class=\"notranslate\" translate=\"no\">blendSrc</code> and <code class=\"notranslate\" translate=\"no\">blendDst</code>\nproperties as follows</p>\n<pre class=\"prettyprint showlinemods notranslate notranslate\" translate=\"no\">  blending: THREE.CustomBlending,\n  blendSrc: THREE.OneMinusDstColorFactor,\n  blendDst: THREE.OneMinusSrcColorFactor,\n</pre><p>This gives as an <em>inverse</em> type of effect. Comment out\nthose 3 lines and you'll see the difference. I'm just guessing\nthe inverse effect is best here as that way we can hopefully\nsee the cursor regardless of the colors it is over.</p>\n</li>\n<li><p>We use a <a href=\"/docs/#api/en/geometries/TorusGeometry\"><code class=\"notranslate\" translate=\"no\">TorusGeometry</code></a> and not a <a href=\"/docs/#api/en/geometries/RingGeometry\"><code class=\"notranslate\" translate=\"no\">RingGeometry</code></a></p>\n<p>For whatever reason the <a href=\"/docs/#api/en/geometries/RingGeometry\"><code class=\"notranslate\" translate=\"no\">RingGeometry</code></a> uses a flat\nUV mapping scheme. Because of this if we use a <a href=\"/docs/#api/en/geometries/RingGeometry\"><code class=\"notranslate\" translate=\"no\">RingGeometry</code></a>\nthe texture slides horizontally across the ring instead of\naround it like it does above.</p>\n<p>Try it out, change the <a href=\"/docs/#api/en/geometries/TorusGeometry\"><code class=\"notranslate\" translate=\"no\">TorusGeometry</code></a> to a <a href=\"/docs/#api/en/geometries/RingGeometry\"><code class=\"notranslate\" translate=\"no\">RingGeometry</code></a>\n(it's just commented out in the example above) and you'll see what I\nmean.</p>\n<p>The <em>proper</em> thing to do (for some definition of <em>proper</em>) would be\nto either use the <a href=\"/docs/#api/en/geometries/RingGeometry\"><code class=\"notranslate\" translate=\"no\">RingGeometry</code></a> but fix the texture coordinates\nso they go around the ring. Or else, generate our own ring geometry.\nBut, the torus works just fine. Placed directly in front of the camera\nwith a <a href=\"/docs/#api/en/materials/MeshBasicMaterial\"><code class=\"notranslate\" translate=\"no\">MeshBasicMaterial</code></a> it will look exactly like a ring and the\ntexture coordinates go around the ring so it works for our needs.</p>\n</li>\n</ul>\n<p>Let's integrate it with our VR code above. </p>\n<pre class=\"prettyprint showlinemods notranslate lang-js\" translate=\"no\">class PickHelper {\n-  constructor() {\n+  constructor(camera) {\n    this.raycaster = new THREE.Raycaster();\n    this.pickedObject = null;\n-    this.pickedObjectSavedColor = 0;\n\n+    const cursorColors = new Uint8Array([\n+      64, 64, 64, 64,       // dark gray\n+      255, 255, 255, 255,   // white\n+    ]);\n+    this.cursorTexture = makeDataTexture(cursorColors, 2, 1);\n+\n+    const ringRadius = 0.4;\n+    const tubeRadius = 0.1;\n+    const tubeSegments = 4;\n+    const ringSegments = 64;\n+    const cursorGeometry = new THREE.TorusGeometry(\n+        ringRadius, tubeRadius, tubeSegments, ringSegments);\n+\n+    const cursorMaterial = new THREE.MeshBasicMaterial({\n+      color: 'white',\n+      map: this.cursorTexture,\n+      transparent: true,\n+      blending: THREE.CustomBlending,\n+      blendSrc: THREE.OneMinusDstColorFactor,\n+      blendDst: THREE.OneMinusSrcColorFactor,\n+    });\n+    const cursor = new THREE.Mesh(cursorGeometry, cursorMaterial);\n+    // add the cursor as a child of the camera\n+    camera.add(cursor);\n+    // and move it in front of the camera\n+    cursor.position.z = -1;\n+    const scale = 0.05;\n+    cursor.scale.set(scale, scale, scale);\n+    this.cursor = cursor;\n+\n+    this.selectTimer = 0;\n+    this.selectDuration = 2;\n+    this.lastTime = 0;\n  }\n  pick(normalizedPosition, scene, camera, time) {\n+    const elapsedTime = time - this.lastTime;\n+    this.lastTime = time;\n\n-    // restore the color if there is a picked object\n-    if (this.pickedObject) {\n-      this.pickedObject.material.emissive.setHex(this.pickedObjectSavedColor);\n-      this.pickedObject = undefined;\n-    }\n\n+    const lastPickedObject = this.pickedObject;\n+    this.pickedObject = undefined;\n\n    // cast a ray through the frustum\n    this.raycaster.setFromCamera(normalizedPosition, camera);\n    // get the list of objects the ray intersected\n    const intersectedObjects = this.raycaster.intersectObjects(scene.children);\n    if (intersectedObjects.length) {\n      // pick the first object. It's the closest one\n      this.pickedObject = intersectedObjects[0].object;\n-      // save its color\n-      this.pickedObjectSavedColor = this.pickedObject.material.emissive.getHex();\n-      // set its emissive color to flashing red/yellow\n-      this.pickedObject.material.emissive.setHex((time * 8) % 2 &gt; 1 ? 0xFFFF00 : 0xFF0000);\n    }\n\n+    // show the cursor only if it's hitting something\n+    this.cursor.visible = this.pickedObject ? true : false;\n+\n+    let selected = false;\n+\n+    // if we're looking at the same object as before\n+    // increment time select timer\n+    if (this.pickedObject &amp;&amp; lastPickedObject === this.pickedObject) {\n+      this.selectTimer += elapsedTime;\n+      if (this.selectTimer &gt;= this.selectDuration) {\n+        this.selectTimer = 0;\n+        selected = true;\n+      }\n+    } else {\n+      this.selectTimer = 0;\n+    }\n+\n+    // set cursor material to show the timer state\n+    const fromStart = 0;\n+    const fromEnd = this.selectDuration;\n+    const toStart = -0.5;\n+    const toEnd = 0.5;\n+    this.cursorTexture.offset.x = THREE.MathUtils.mapLinear(\n+        this.selectTimer,\n+        fromStart, fromEnd,\n+        toStart, toEnd);\n+\n+    return selected ? this.pickedObject : undefined;\n  }\n}\n</pre>\n<p>You can see the code above we added all the code to create\nthe cursor geometry, texture, and material and we added it\nas a child of the camera so it will always be in front of\nthe camera. Note we need to add the camera to the scene\notherwise the cursor won't be rendered.</p>\n<pre class=\"prettyprint showlinemods notranslate lang-js\" translate=\"no\">+scene.add(camera);\n</pre>\n<p>We then check if the thing we're picking this time is the same as it was last\ntime. If so we add the elapsed time to a timer and if the timer reaches its\nlimit we return the selected item.</p>\n<p>Now let's use that to pick the cubes. As a simple example\nwe'll add 3 spheres as well. When a cube is picked with hide\nthe cube and un-hide the corresponding sphere.</p>\n<p>So first we'll make a sphere geometry</p>\n<pre class=\"prettyprint showlinemods notranslate lang-js\" translate=\"no\">const boxWidth = 1;\nconst boxHeight = 1;\nconst boxDepth = 1;\n-const geometry = new THREE.BoxGeometry(boxWidth, boxHeight, boxDepth);\n+const boxGeometry = new THREE.BoxGeometry(boxWidth, boxHeight, boxDepth);\n+\n+const sphereRadius = 0.5;\n+const sphereGeometry = new THREE.SphereGeometry(sphereRadius);\n</pre>\n<p>Then let's create 3 pairs of box and sphere meshes. We'll\nuse a <a href=\"https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Map\"><code class=\"notranslate\" translate=\"no\">Map</code></a>\nso that we can associate each <a href=\"/docs/#api/en/objects/Mesh\"><code class=\"notranslate\" translate=\"no\">Mesh</code></a> with its partner.</p>\n<pre class=\"prettyprint showlinemods notranslate lang-js\" translate=\"no\">-const cubes = [\n-  makeInstance(geometry, 0x44aa88,  0),\n-  makeInstance(geometry, 0x8844aa, -2),\n-  makeInstance(geometry, 0xaa8844,  2),\n-];\n+const meshToMeshMap = new Map();\n+[\n+  { x:  0, boxColor: 0x44aa88, sphereColor: 0xFF4444, },\n+  { x:  2, boxColor: 0x8844aa, sphereColor: 0x44FF44, },\n+  { x: -2, boxColor: 0xaa8844, sphereColor: 0x4444FF, },\n+].forEach((info) =&gt; {\n+  const {x, boxColor, sphereColor} = info;\n+  const sphere = makeInstance(sphereGeometry, sphereColor, x);\n+  const box = makeInstance(boxGeometry, boxColor, x);\n+  // hide the sphere\n+  sphere.visible = false;\n+  // map the sphere to the box\n+  meshToMeshMap.set(box, sphere);\n+  // map the box to the sphere\n+  meshToMeshMap.set(sphere, box);\n+});\n</pre>\n<p>In <code class=\"notranslate\" translate=\"no\">render</code> where we rotate the cubes we need to iterate over <code class=\"notranslate\" translate=\"no\">meshToMeshMap</code>\ninstead of <code class=\"notranslate\" translate=\"no\">cubes</code>.</p>\n<pre class=\"prettyprint showlinemods notranslate lang-js\" translate=\"no\">-cubes.forEach((cube, ndx) =&gt; {\n+let ndx = 0;\n+for (const mesh of meshToMeshMap.keys()) {\n  const speed = 1 + ndx * .1;\n  const rot = time * speed;\n-  cube.rotation.x = rot;\n-  cube.rotation.y = rot;\n-});\n+  mesh.rotation.x = rot;\n+  mesh.rotation.y = rot;\n+  ++ndx;\n+}\n</pre>\n<p>And now we can use our new <code class=\"notranslate\" translate=\"no\">PickHelper</code> implementation\nto select one of the objects. When selected we hide\nthat object and un-hide its partner.</p>\n<pre class=\"prettyprint showlinemods notranslate lang-js\" translate=\"no\">// 0, 0 is the center of the view in normalized coordinates.\n-pickHelper.pick({x: 0, y: 0}, scene, camera, time);\n+const selectedObject = pickHelper.pick({x: 0, y: 0}, scene, camera, time);\n+if (selectedObject) {\n+  selectedObject.visible = false;\n+  const partnerObject = meshToMeshMap.get(selectedObject);\n+  partnerObject.visible = true;\n+}\n</pre>\n<p>And with that we should have a pretty decent <em>look to select</em> implementation.</p>\n<p></p><div translate=\"no\" class=\"threejs_example_container notranslate\">\n  <div></div>\n  <a class=\"threejs_center\" href=\"/manual/examples/webxr-look-to-select-w-cursor.html\" target=\"_blank\">click here to open in a separate window</a>\n</div>\n\n<p></p>\n<p>I hope this example gave some ideas of how to implement a \"look to select\"\ntype of Google Cardboard level UX. Sliding textures using texture coordinates\noffsets is also a commonly useful technique.</p>\n<p>Next up <a href=\"webxr-point-to-select.html\">let's allow the user that has a VR controller to point at and move things</a>.</p>\n\n        </div>\n      </div>\n    </div>",
    "success": true,
    "error": "",
    "crawled_at": 1786376809.300204,
    "is_shell": false
  },
  {
    "url": "https://threejs.org/manual/en/webxr-point-to-select.html",
    "title": "VR - 3DOF Point to Select",
    "content": "<div class=\"container\">\n      <div class=\"lesson-title\">\n        <h1>VR - 3DOF Point to Select</h1>\n      </div>\n      <div class=\"lesson\">\n        <div class=\"lesson-main\">\n          <p><strong>NOTE: The examples on this page require a VR capable\ndevice with a pointing device. Without one they won't work. See <a href=\"webxr.html\">this article</a>\nas to why</strong></p>\n<p>In the <a href=\"webxr-look-to-select.html\">previous article</a> we went over\na very simple VR example where we let the user choose things by\npointing via looking. In this article we will take it one step further\nand let the user choose with a pointing device </p>\n<p>Three.js makes is relatively easy by providing 2 controller objects in VR\nand tries to handle both cases of a single 3DOF controller and two 6DOF\ncontrollers. Each of the controllers are <a href=\"/docs/#api/en/core/Object3D\"><code class=\"notranslate\" translate=\"no\">Object3D</code></a> objects which give\nthe orientation and position of that controller. They also provide\n<code class=\"notranslate\" translate=\"no\">selectstart</code>, <code class=\"notranslate\" translate=\"no\">select</code> and <code class=\"notranslate\" translate=\"no\">selectend</code> events when the user starts pressing,\nis pressing, and stops pressing (ends) the \"main\" button on the controller.</p>\n<p>Starting with the last example from <a href=\"webxr-look-to-select.html\">the previous article</a>\nlet's change the <code class=\"notranslate\" translate=\"no\">PickHelper</code> into a <code class=\"notranslate\" translate=\"no\">ControllerPickHelper</code>.</p>\n<p>Our new implementation will emit a <code class=\"notranslate\" translate=\"no\">select</code> event that gives us the object that was picked\nso to use it we'll just need to do this.</p>\n<pre class=\"prettyprint showlinemods notranslate lang-js\" translate=\"no\">const pickHelper = new ControllerPickHelper(scene);\npickHelper.addEventListener('select', (event) =&gt; {\n  event.selectedObject.visible = false;\n  const partnerObject = meshToMeshMap.get(event.selectedObject);\n  partnerObject.visible = true;\n});\n</pre>\n<p>Remember from our previous code <code class=\"notranslate\" translate=\"no\">meshToMeshMap</code> maps our boxes and spheres to\neach other so if we have one we can look up its partner through <code class=\"notranslate\" translate=\"no\">meshToMeshMap</code>\nso here we're just hiding the selected object and un-hiding its partner.</p>\n<p>As for the actual implementation of <code class=\"notranslate\" translate=\"no\">ControllerPickHelper</code>, first we need\nto add the VR controller objects to the scene and to those add some 3D lines\nwe can use to display where the user is pointing. We save off both the controllers\nand their lines.</p>\n<pre class=\"prettyprint showlinemods notranslate lang-js\" translate=\"no\">class ControllerPickHelper {\n  constructor(scene) {\n    const pointerGeometry = new THREE.BufferGeometry().setFromPoints([\n      new THREE.Vector3(0, 0, 0),\n      new THREE.Vector3(0, 0, -1),\n    ]);\n\n    this.controllers = [];\n    for (let i = 0; i &lt; 2; ++i) {\n      const controller = renderer.xr.getController(i);\n      scene.add(controller);\n\n      const line = new THREE.Line(pointerGeometry);\n      line.scale.z = 5;\n      controller.add(line);\n      this.controllers.push({controller, line});\n    }\n  }\n}\n</pre>\n<p>Without doing anything else this alone would give us 1 or 2 lines in the scene\nshowing where the user's pointing devices are and which way they are pointing.</p>\n<p>One problem we have though, we don't want have our <code class=\"notranslate\" translate=\"no\">RayCaster</code> pick the line itself\nso an easy solution is separate the objects we wanted to be able to pick from the\nobjects we don't by parenting them under another <a href=\"/docs/#api/en/core/Object3D\"><code class=\"notranslate\" translate=\"no\">Object3D</code></a>.</p>\n<pre class=\"prettyprint showlinemods notranslate lang-js\" translate=\"no\">const scene = new THREE.Scene();\n+// object to put pickable objects on so we can easily\n+// separate them from non-pickable objects\n+const pickRoot = new THREE.Object3D();\n+scene.add(pickRoot);\n\n...\n\nfunction makeInstance(geometry, color, x) {\n  const material = new THREE.MeshPhongMaterial({color});\n\n  const cube = new THREE.Mesh(geometry, material);\n-  scene.add(cube);\n+  pickRoot.add(cube);\n\n...\n</pre>\n<p>Next let's add some code to pick from the controllers. This is the first time\nwe've picked with something not the camera. In our <a href=\"picking.html\">article on picking</a>\nthe user uses the mouse or finger to pick which means picking comes from the camera\ninto the screen. In <a href=\"webxr-look-to-select.html\">the previous article</a> we\nwere picking based on which way the user is looking so again that comes from the\ncamera. This time though we're picking from the position of the controllers so\nwe're not using the camera.</p>\n<pre class=\"prettyprint showlinemods notranslate lang-js\" translate=\"no\">class ControllerPickHelper {\n  constructor(scene) {\n+    this.raycaster = new THREE.Raycaster();\n+    this.objectToColorMap = new Map();\n+    this.controllerToObjectMap = new Map();\n+    this.tempMatrix = new THREE.Matrix4();\n\n    const pointerGeometry = new THREE.BufferGeometry().setFromPoints([\n      new THREE.Vector3(0, 0, 0),\n      new THREE.Vector3(0, 0, -1),\n    ]);\n\n    this.controllers = [];\n    for (let i = 0; i &lt; 2; ++i) {\n      const controller = renderer.xr.getController(i);\n      scene.add(controller);\n\n      const line = new THREE.Line(pointerGeometry);\n      line.scale.z = 5;\n      controller.add(line);\n      this.controllers.push({controller, line});\n    }\n  }\n+  update(pickablesParent, time) {\n+    this.reset();\n+    for (const {controller, line} of this.controllers) {\n+      // cast a ray through the from the controller\n+      this.tempMatrix.identity().extractRotation(controller.matrixWorld);\n+      this.raycaster.ray.origin.setFromMatrixPosition(controller.matrixWorld);\n+      this.raycaster.ray.direction.set(0, 0, -1).applyMatrix4(this.tempMatrix);\n+      // get the list of objects the ray intersected\n+      const intersections = this.raycaster.intersectObjects(pickablesParent.children);\n+      if (intersections.length) {\n+        const intersection = intersections[0];\n+        // make the line touch the object\n+        line.scale.z = intersection.distance;\n+        // pick the first object. It's the closest one\n+        const pickedObject = intersection.object;\n+        // save which object this controller picked\n+        this.controllerToObjectMap.set(controller, pickedObject);\n+        // highlight the object if we haven't already\n+        if (this.objectToColorMap.get(pickedObject) === undefined) {\n+          // save its color\n+          this.objectToColorMap.set(pickedObject, pickedObject.material.emissive.getHex());\n+          // set its emissive color to flashing red/yellow\n+          pickedObject.material.emissive.setHex((time * 8) % 2 &gt; 1 ? 0xFF2000 : 0xFF0000);\n+        }\n+      } else {\n+        line.scale.z = 5;\n+      }\n+    }\n+  }\n}\n</pre>\n<p>Like before we use a <a href=\"/docs/#api/en/core/Raycaster\"><code class=\"notranslate\" translate=\"no\">Raycaster</code></a> but this time we take the ray from the controller.\nOur previous <code class=\"notranslate\" translate=\"no\">PickHelper</code> there was only one thing picking but here we have up to 2\ncontrollers, one for each hand. We save off which object each controller is\nlooking at in <code class=\"notranslate\" translate=\"no\">controllerToObjectMap</code>. We also save off the original emissive color in\n<code class=\"notranslate\" translate=\"no\">objectToColorMap</code> and we make the line long enough to touch whatever it's pointing at.</p>\n<p>We need to add some code to reset these settings every frame.</p>\n<pre class=\"prettyprint showlinemods notranslate lang-js\" translate=\"no\">class ControllerPickHelper {\n\n  ...\n\n+  _reset() {\n+    // restore the colors\n+    this.objectToColorMap.forEach((color, object) =&gt; {\n+      object.material.emissive.setHex(color);\n+    });\n+    this.objectToColorMap.clear();\n+    this.controllerToObjectMap.clear();\n+  }\n  update(pickablesParent, time) {\n+    this._reset();\n\n    ...\n\n}\n</pre>\n<p>Next we want to emit a <code class=\"notranslate\" translate=\"no\">select</code> event when the user clicks the controller.\nTo do that we can extend three.js's <a href=\"/docs/#api/en/core/EventDispatcher\"><code class=\"notranslate\" translate=\"no\">EventDispatcher</code></a> and then we'll check\nwhen we get a <code class=\"notranslate\" translate=\"no\">select</code> event from the controller, then if that controller\nis pointing at something we emit what that controller is pointing at\nas our own <code class=\"notranslate\" translate=\"no\">select</code> event.</p>\n<pre class=\"prettyprint showlinemods notranslate lang-js\" translate=\"no\">-class ControllerPickHelper {\n+class ControllerPickHelper extends THREE.EventDispatcher {\n  constructor(scene) {\n+    super();\n    this.raycaster = new THREE.Raycaster();\n    this.objectToColorMap = new Map();  // object to save color and picked object\n    this.controllerToObjectMap = new Map();\n    this.tempMatrix = new THREE.Matrix4();\n\n    const pointerGeometry = new THREE.BufferGeometry().setFromPoints([\n      new THREE.Vector3(0, 0, 0),\n      new THREE.Vector3(0, 0, -1),\n    ]);\n\n    this.controllers = [];\n    for (let i = 0; i &lt; 2; ++i) {\n      const controller = renderer.xr.getController(i);\n+      controller.addEventListener('select', (event) =&gt; {\n+        const controller = event.target;\n+        const selectedObject = this.controllerToObjectMap.get(controller);\n+        if (selectedObject) {\n+          this.dispatchEvent({type: 'select', controller, selectedObject});\n+        }\n+      });\n      scene.add(controller);\n\n      const line = new THREE.Line(pointerGeometry);\n      line.scale.z = 5;\n      controller.add(line);\n      this.controllers.push({controller, line});\n    }\n  }\n}\n</pre>\n<p>All that is left is to call <code class=\"notranslate\" translate=\"no\">update</code> in our render loop</p>\n<pre class=\"prettyprint showlinemods notranslate lang-js\" translate=\"no\">function render(time) {\n\n  ...\n\n+  pickHelper.update(pickablesParent, time);\n\n  renderer.render(scene, camera);\n}\n</pre>\n<p>and assuming you have a VR device with a controller you should\nbe able to use the controllers to pick things.</p>\n<p></p><div translate=\"no\" class=\"threejs_example_container notranslate\">\n  <div></div>\n  <a class=\"threejs_center\" href=\"/manual/examples/webxr-point-to-select.html\" target=\"_blank\">click here to open in a separate window</a>\n</div>\n\n<p></p>\n<p>And what if we wanted to be able to move the objects?</p>\n<p>That's relatively easy. Let's move our controller 'select' listener\ncode out into a function so we can use it for more than one thing.</p>\n<pre class=\"prettyprint showlinemods notranslate lang-js\" translate=\"no\">class ControllerPickHelper extends THREE.EventDispatcher {\n  constructor(scene) {\n    super();\n\n    ...\n\n    this.controllers = [];\n\n+    const selectListener = (event) =&gt; {\n+      const controller = event.target;\n+      const selectedObject = this.controllerToObjectMap.get(event.target);\n+      if (selectedObject) {\n+        this.dispatchEvent({type: 'select', controller, selectedObject});\n+      }\n+    };\n\n    for (let i = 0; i &lt; 2; ++i) {\n      const controller = renderer.xr.getController(i);\n-      controller.addEventListener('select', (event) =&gt; {\n-        const controller = event.target;\n-        const selectedObject = this.controllerToObjectMap.get(event.target);\n-        if (selectedObject) {\n-          this.dispatchEvent({type: 'select', controller, selectedObject});\n-        }\n-      });\n+      controller.addEventListener('select', selectListener);\n\n       ...\n</pre>\n<p>Then let's use it for both <code class=\"notranslate\" translate=\"no\">selectstart</code> and <code class=\"notranslate\" translate=\"no\">select</code></p>\n<pre class=\"prettyprint showlinemods notranslate lang-js\" translate=\"no\">class ControllerPickHelper extends THREE.EventDispatcher {\n  constructor(scene) {\n    super();\n\n    ...\n\n    this.controllers = [];\n\n    const selectListener = (event) =&gt; {\n      const controller = event.target;\n      const selectedObject = this.controllerToObjectMap.get(event.target);\n      if (selectedObject) {\n-        this.dispatchEvent({type: 'select', controller, selectedObject});\n+        this.dispatchEvent({type: event.type, controller, selectedObject});\n      }\n    };\n\n    for (let i = 0; i &lt; 2; ++i) {\n      const controller = renderer.xr.getController(i);\n      controller.addEventListener('select', selectListener);\n      controller.addEventListener('selectstart', selectListener);\n\n       ...\n</pre>\n<p>and let's also pass on the <code class=\"notranslate\" translate=\"no\">selectend</code> event which three.js sends out\nwhen you user lets of the button on the controller.</p>\n<pre class=\"prettyprint showlinemods notranslate lang-js\" translate=\"no\">class ControllerPickHelper extends THREE.EventDispatcher {\n  constructor(scene) {\n    super();\n\n    ...\n\n    this.controllers = [];\n\n    const selectListener = (event) =&gt; {\n      const controller = event.target;\n      const selectedObject = this.controllerToObjectMap.get(event.target);\n      if (selectedObject) {\n        this.dispatchEvent({type: event.type, controller, selectedObject});\n      }\n    };\n\n+    const endListener = (event) =&gt; {\n+      const controller = event.target;\n+      this.dispatchEvent({type: event.type, controller});\n+    };\n\n    for (let i = 0; i &lt; 2; ++i) {\n      const controller = renderer.xr.getController(i);\n      controller.addEventListener('select', selectListener);\n      controller.addEventListener('selectstart', selectListener);\n+      controller.addEventListener('selectend', endListener);\n\n       ...\n</pre>\n<p>Now let's change the code so when we get a <code class=\"notranslate\" translate=\"no\">selectstart</code> event we'll\nremove the selected object from the scene and make it a child of the controller.\nThis means it will move with the controller. When we get a <code class=\"notranslate\" translate=\"no\">selectend</code>\nevent we'll put it back in the scene.</p>\n<pre class=\"prettyprint showlinemods notranslate lang-js\" translate=\"no\">const pickHelper = new ControllerPickHelper(scene);\n-pickHelper.addEventListener('select', (event) =&gt; {\n-  event.selectedObject.visible = false;\n-  const partnerObject = meshToMeshMap.get(event.selectedObject);\n-  partnerObject.visible = true;\n-});\n\n+const controllerToSelection = new Map();\n+pickHelper.addEventListener('selectstart', (event) =&gt; {\n+  const {controller, selectedObject} = event;\n+  const existingSelection = controllerToSelection.get(controller);\n+  if (!existingSelection) {\n+    controllerToSelection.set(controller, {\n+      object: selectedObject,\n+      parent: selectedObject.parent,\n+    });\n+    controller.attach(selectedObject);\n+  }\n+});\n+\n+pickHelper.addEventListener('selectend', (event) =&gt; {\n+  const {controller} = event;\n+  const selection = controllerToSelection.get(controller);\n+  if (selection) {\n+    controllerToSelection.delete(controller);\n+    selection.parent.attach(selection.object);\n+  }\n+});\n</pre>\n<p>When an object is selected we save off that object and its\noriginal parent. When the user is done we can put the object back.</p>\n<p>We use the <a href=\"/docs/#api/en/core/Object3D.attach\"><code class=\"notranslate\" translate=\"no\">Object3D.attach</code></a> to re-parent\nthe selected objects. These functions let us change the parent\nof an object without changing its orientation and position in the\nscene. </p>\n<p>And with that we should be able to move the objects around with a 6DOF\ncontroller or at least change their orientation with a 3DOF controller</p>\n<p></p><div translate=\"no\" class=\"threejs_example_container notranslate\">\n  <div></div>\n  <a class=\"threejs_center\" href=\"/manual/examples/webxr-point-to-select-w-move.html\" target=\"_blank\">click here to open in a separate window</a>\n</div>\n\n<p></p>\n<p>To be honest I'm not 100% sure this <code class=\"notranslate\" translate=\"no\">ControllerPickHelper</code> is\nthe best way to organize the code but it's useful to demonstrating\nthe various parts of getting something simple working in VR\nin three.js</p>\n\n        </div>\n      </div>\n    </div>",
    "success": true,
    "error": "",
    "crawled_at": 1786376809.300204,
    "is_shell": false
  },
  {
    "url": "https://threejs.org/manual/en/material-table.html",
    "title": "Material Feature Table",
    "content": "<div class=\"container\">\n      <div class=\"lesson-title\">\n        <h1>Material Feature Table</h1>\n      </div>\n      <div class=\"lesson\">\n        <div class=\"lesson-main\">\n          <p>The most common materials in three.js are the Mesh materials. Here\nis a table showing which material support which features.</p>\n<div>\n<div id=\"material-table\" class=\"threejs_center\"><table><thead><td></td><td><a href=\"https://threejs.org/docs/#MeshBasicMaterial\" target=\"_blank\">Basic</a></td><td><a href=\"https://threejs.org/docs/#MeshLambertMaterial\" target=\"_blank\">Lambert</a></td><td><a href=\"https://threejs.org/docs/#MeshPhongMaterial\" target=\"_blank\">Phong</a></td><td><a href=\"https://threejs.org/docs/#MeshStandardMaterial\" target=\"_blank\">Standard</a></td><td><a href=\"https://threejs.org/docs/#MeshPhysicalMaterial\" target=\"_blank\">Physical</a></td></thead><tr><td>alphaMap</td><td><a href=\"https://threejs.org/docs/#MeshBasicMaterial.alphaMap\" target=\"_blank\">•</a></td><td><a href=\"https://threejs.org/docs/#MeshLambertMaterial.alphaMap\" target=\"_blank\">•</a></td><td><a href=\"https://threejs.org/docs/#MeshPhongMaterial.alphaMap\" target=\"_blank\">•</a></td><td><a href=\"https://threejs.org/docs/#MeshStandardMaterial.alphaMap\" target=\"_blank\">•</a></td><td><a href=\"https://threejs.org/docs/#MeshPhysicalMaterial.alphaMap\" target=\"_blank\">•</a></td></tr><tr><td>anisotropy</td><td><a href=\"https://threejs.org/docs/#MeshBasicMaterial.anisotropy\" target=\"_blank\"></a></td><td><a href=\"https://threejs.org/docs/#MeshLambertMaterial.anisotropy\" target=\"_blank\"></a></td><td><a href=\"https://threejs.org/docs/#MeshPhongMaterial.anisotropy\" target=\"_blank\"></a></td><td><a href=\"https://threejs.org/docs/#MeshStandardMaterial.anisotropy\" target=\"_blank\"></a></td><td><a href=\"https://threejs.org/docs/#MeshPhysicalMaterial.anisotropy\" target=\"_blank\">•</a></td></tr><tr><td>anisotropyMap</td><td><a href=\"https://threejs.org/docs/#MeshBasicMaterial.anisotropyMap\" target=\"_blank\"></a></td><td><a href=\"https://threejs.org/docs/#MeshLambertMaterial.anisotropyMap\" target=\"_blank\"></a></td><td><a href=\"https://threejs.org/docs/#MeshPhongMaterial.anisotropyMap\" target=\"_blank\"></a></td><td><a href=\"https://threejs.org/docs/#MeshStandardMaterial.anisotropyMap\" target=\"_blank\"></a></td><td><a href=\"https://threejs.org/docs/#MeshPhysicalMaterial.anisotropyMap\" target=\"_blank\">•</a></td></tr><tr><td>anisotropyRotation</td><td><a href=\"https://threejs.org/docs/#MeshBasicMaterial.anisotropyRotation\" target=\"_blank\"></a></td><td><a href=\"https://threejs.org/docs/#MeshLambertMaterial.anisotropyRotation\" target=\"_blank\"></a></td><td><a href=\"https://threejs.org/docs/#MeshPhongMaterial.anisotropyRotation\" target=\"_blank\"></a></td><td><a href=\"https://threejs.org/docs/#MeshStandardMaterial.anisotropyRotation\" target=\"_blank\"></a></td><td><a href=\"https://threejs.org/docs/#MeshPhysicalMaterial.anisotropyRotation\" target=\"_blank\">•</a></td></tr><tr><td>aoMap</td><td><a href=\"https://threejs.org/docs/#MeshBasicMaterial.aoMap\" target=\"_blank\">•</a></td><td><a href=\"https://threejs.org/docs/#MeshLambertMaterial.aoMap\" target=\"_blank\">•</a></td><td><a href=\"https://threejs.org/docs/#MeshPhongMaterial.aoMap\" target=\"_blank\">•</a></td><td><a href=\"https://threejs.org/docs/#MeshStandardMaterial.aoMap\" target=\"_blank\">•</a></td><td><a href=\"https://threejs.org/docs/#MeshPhysicalMaterial.aoMap\" target=\"_blank\">•</a></td></tr><tr><td>aoMapIntensity</td><td><a href=\"https://threejs.org/docs/#MeshBasicMaterial.aoMapIntensity\" target=\"_blank\">•</a></td><td><a href=\"https://threejs.org/docs/#MeshLambertMaterial.aoMapIntensity\" target=\"_blank\">•</a></td><td><a href=\"https://threejs.org/docs/#MeshPhongMaterial.aoMapIntensity\" target=\"_blank\">•</a></td><td><a href=\"https://threejs.org/docs/#MeshStandardMaterial.aoMapIntensity\" target=\"_blank\">•</a></td><td><a href=\"https://threejs.org/docs/#MeshPhysicalMaterial.aoMapIntensity\" target=\"_blank\">•</a></td></tr><tr><td>attenuationColor</td><td><a href=\"https://threejs.org/docs/#MeshBasicMaterial.attenuationColor\" target=\"_blank\"></a></td><td><a href=\"https://threejs.org/docs/#MeshLambertMaterial.attenuationColor\" target=\"_blank\"></a></td><td><a href=\"https://threejs.org/docs/#MeshPhongMaterial.attenuationColor\" target=\"_blank\"></a></td><td><a href=\"https://threejs.org/docs/#MeshStandardMaterial.attenuationColor\" target=\"_blank\"></a></td><td><a href=\"https://threejs.org/docs/#MeshPhysicalMaterial.attenuationColor\" target=\"_blank\">•</a></td></tr><tr><td>attenuationDistance</td><td><a href=\"https://threejs.org/docs/#MeshBasicMaterial.attenuationDistance\" target=\"_blank\"></a></td><td><a href=\"https://threejs.org/docs/#MeshLambertMaterial.attenuationDistance\" target=\"_blank\"></a></td><td><a href=\"https://threejs.org/docs/#MeshPhongMaterial.attenuationDistance\" target=\"_blank\"></a></td><td><a href=\"https://threejs.org/docs/#MeshStandardMaterial.attenuationDistance\" target=\"_blank\"></a></td><td><a href=\"https://threejs.org/docs/#MeshPhysicalMaterial.attenuationDistance\" target=\"_blank\">•</a></td></tr><tr><td>bumpMap</td><td><a href=\"https://threejs.org/docs/#MeshBasicMaterial.bumpMap\" target=\"_blank\"></a></td><td><a href=\"https://threejs.org/docs/#MeshLambertMaterial.bumpMap\" target=\"_blank\">•</a></td><td><a href=\"https://threejs.org/docs/#MeshPhongMaterial.bumpMap\" target=\"_blank\">•</a></td><td><a href=\"https://threejs.org/docs/#MeshStandardMaterial.bumpMap\" target=\"_blank\">•</a></td><td><a href=\"https://threejs.org/docs/#MeshPhysicalMaterial.bumpMap\" target=\"_blank\">•</a></td></tr><tr><td>bumpScale</td><td><a href=\"https://threejs.org/docs/#MeshBasicMaterial.bumpScale\" target=\"_blank\"></a></td><td><a href=\"https://threejs.org/docs/#MeshLambertMaterial.bumpScale\" target=\"_blank\">•</a></td><td><a href=\"https://threejs.org/docs/#MeshPhongMaterial.bumpScale\" target=\"_blank\">•</a></td><td><a href=\"https://threejs.org/docs/#MeshStandardMaterial.bumpScale\" target=\"_blank\">•</a></td><td><a href=\"https://threejs.org/docs/#MeshPhysicalMaterial.bumpScale\" target=\"_blank\">•</a></td></tr><tr><td>clearcoat</td><td><a href=\"https://threejs.org/docs/#MeshBasicMaterial.clearcoat\" target=\"_blank\"></a></td><td><a href=\"https://threejs.org/docs/#MeshLambertMaterial.clearcoat\" target=\"_blank\"></a></td><td><a href=\"https://threejs.org/docs/#MeshPhongMaterial.clearcoat\" target=\"_blank\"></a></td><td><a href=\"https://threejs.org/docs/#MeshStandardMaterial.clearcoat\" target=\"_blank\"></a></td><td><a href=\"https://threejs.org/docs/#MeshPhysicalMaterial.clearcoat\" target=\"_blank\">•</a></td></tr><tr><td>clearcoatMap</td><td><a href=\"https://threejs.org/docs/#MeshBasicMaterial.clearcoatMap\" target=\"_blank\"></a></td><td><a href=\"https://threejs.org/docs/#MeshLambertMaterial.clearcoatMap\" target=\"_blank\"></a></td><td><a href=\"https://threejs.org/docs/#MeshPhongMaterial.clearcoatMap\" target=\"_blank\"></a></td><td><a href=\"https://threejs.org/docs/#MeshStandardMaterial.clearcoatMap\" target=\"_blank\"></a></td><td><a href=\"https://threejs.org/docs/#MeshPhysicalMaterial.clearcoatMap\" target=\"_blank\">•</a></td></tr><tr><td>clearcoatNormalMap</td><td><a href=\"https://threejs.org/docs/#MeshBasicMaterial.clearcoatNormalMap\" target=\"_blank\"></a></td><td><a href=\"https://threejs.org/docs/#MeshLambertMaterial.clearcoatNormalMap\" target=\"_blank\"></a></td><td><a href=\"https://threejs.org/docs/#MeshPhongMaterial.clearcoatNormalMap\" target=\"_blank\"></a></td><td><a href=\"https://threejs.org/docs/#MeshStandardMaterial.clearcoatNormalMap\" target=\"_blank\"></a></td><td><a href=\"https://threejs.org/docs/#MeshPhysicalMaterial.clearcoatNormalMap\" target=\"_blank\">•</a></td></tr><tr><td>clearcoatNormalScale</td><td><a href=\"https://threejs.org/docs/#MeshBasicMaterial.clearcoatNormalScale\" target=\"_blank\"></a></td><td><a href=\"https://threejs.org/docs/#MeshLambertMaterial.clearcoatNormalScale\" target=\"_blank\"></a></td><td><a href=\"https://threejs.org/docs/#MeshPhongMaterial.clearcoatNormalScale\" target=\"_blank\"></a></td><td><a href=\"https://threejs.org/docs/#MeshStandardMaterial.clearcoatNormalScale\" target=\"_blank\"></a></td><td><a href=\"https://threejs.org/docs/#MeshPhysicalMaterial.clearcoatNormalScale\" target=\"_blank\">•</a></td></tr><tr><td>clearcoatRoughness</td><td><a href=\"https://threejs.org/docs/#MeshBasicMaterial.clearcoatRoughness\" target=\"_blank\"></a></td><td><a href=\"https://threejs.org/docs/#MeshLambertMaterial.clearcoatRoughness\" target=\"_blank\"></a></td><td><a href=\"https://threejs.org/docs/#MeshPhongMaterial.clearcoatRoughness\" target=\"_blank\"></a></td><td><a href=\"https://threejs.org/docs/#MeshStandardMaterial.clearcoatRoughness\" target=\"_blank\"></a></td><td><a href=\"https://threejs.org/docs/#MeshPhysicalMaterial.clearcoatRoughness\" target=\"_blank\">•</a></td></tr><tr><td>clearcoatRoughnessMap</td><td><a href=\"https://threejs.org/docs/#MeshBasicMaterial.clearcoatRoughnessMap\" target=\"_blank\"></a></td><td><a href=\"https://threejs.org/docs/#MeshLambertMaterial.clearcoatRoughnessMap\" target=\"_blank\"></a></td><td><a href=\"https://threejs.org/docs/#MeshPhongMaterial.clearcoatRoughnessMap\" target=\"_blank\"></a></td><td><a href=\"https://threejs.org/docs/#MeshStandardMaterial.clearcoatRoughnessMap\" target=\"_blank\"></a></td><td><a href=\"https://threejs.org/docs/#MeshPhysicalMaterial.clearcoatRoughnessMap\" target=\"_blank\">•</a></td></tr><tr><td>color</td><td><a href=\"https://threejs.org/docs/#MeshBasicMaterial.color\" target=\"_blank\">•</a></td><td><a href=\"https://threejs.org/docs/#MeshLambertMaterial.color\" target=\"_blank\">•</a></td><td><a href=\"https://threejs.org/docs/#MeshPhongMaterial.color\" target=\"_blank\">•</a></td><td><a href=\"https://threejs.org/docs/#MeshStandardMaterial.color\" target=\"_blank\">•</a></td><td><a href=\"https://threejs.org/docs/#MeshPhysicalMaterial.color\" target=\"_blank\">•</a></td></tr><tr><td>combine</td><td><a href=\"https://threejs.org/docs/#MeshBasicMaterial.combine\" target=\"_blank\">•</a></td><td><a href=\"https://threejs.org/docs/#MeshLambertMaterial.combine\" target=\"_blank\">•</a></td><td><a href=\"https://threejs.org/docs/#MeshPhongMaterial.combine\" target=\"_blank\">•</a></td><td><a href=\"https://threejs.org/docs/#MeshStandardMaterial.combine\" target=\"_blank\"></a></td><td><a href=\"https://threejs.org/docs/#MeshPhysicalMaterial.combine\" target=\"_blank\"></a></td></tr><tr><td>displacementBias</td><td><a href=\"https://threejs.org/docs/#MeshBasicMaterial.displacementBias\" target=\"_blank\"></a></td><td><a href=\"https://threejs.org/docs/#MeshLambertMaterial.displacementBias\" target=\"_blank\">•</a></td><td><a href=\"https://threejs.org/docs/#MeshPhongMaterial.displacementBias\" target=\"_blank\">•</a></td><td><a href=\"https://threejs.org/docs/#MeshStandardMaterial.displacementBias\" target=\"_blank\">•</a></td><td><a href=\"https://threejs.org/docs/#MeshPhysicalMaterial.displacementBias\" target=\"_blank\">•</a></td></tr><tr><td>displacementMap</td><td><a href=\"https://threejs.org/docs/#MeshBasicMaterial.displacementMap\" target=\"_blank\"></a></td><td><a href=\"https://threejs.org/docs/#MeshLambertMaterial.displacementMap\" target=\"_blank\">•</a></td><td><a href=\"https://threejs.org/docs/#MeshPhongMaterial.displacementMap\" target=\"_blank\">•</a></td><td><a href=\"https://threejs.org/docs/#MeshStandardMaterial.displacementMap\" target=\"_blank\">•</a></td><td><a href=\"https://threejs.org/docs/#MeshPhysicalMaterial.displacementMap\" target=\"_blank\">•</a></td></tr><tr><td>displacementScale</td><td><a href=\"https://threejs.org/docs/#MeshBasicMaterial.displacementScale\" target=\"_blank\"></a></td><td><a href=\"https://threejs.org/docs/#MeshLambertMaterial.displacementScale\" target=\"_blank\">•</a></td><td><a href=\"https://threejs.org/docs/#MeshPhongMaterial.displacementScale\" target=\"_blank\">•</a></td><td><a href=\"https://threejs.org/docs/#MeshStandardMaterial.displacementScale\" target=\"_blank\">•</a></td><td><a href=\"https://threejs.org/docs/#MeshPhysicalMaterial.displacementScale\" target=\"_blank\">•</a></td></tr><tr><td>emissive</td><td><a href=\"https://threejs.org/docs/#MeshBasicMaterial.emissive\" target=\"_blank\"></a></td><td><a href=\"https://threejs.org/docs/#MeshLambertMaterial.emissive\" target=\"_blank\">•</a></td><td><a href=\"https://threejs.org/docs/#MeshPhongMaterial.emissive\" target=\"_blank\">•</a></td><td><a href=\"https://threejs.org/docs/#MeshStandardMaterial.emissive\" target=\"_blank\">•</a></td><td><a href=\"https://threejs.org/docs/#MeshPhysicalMaterial.emissive\" target=\"_blank\">•</a></td></tr><tr><td>emissiveIntensity</td><td><a href=\"https://threejs.org/docs/#MeshBasicMaterial.emissiveIntensity\" target=\"_blank\"></a></td><td><a href=\"https://threejs.org/docs/#MeshLambertMaterial.emissiveIntensity\" target=\"_blank\">•</a></td><td><a href=\"https://threejs.org/docs/#MeshPhongMaterial.emissiveIntensity\" target=\"_blank\">•</a></td><td><a href=\"https://threejs.org/docs/#MeshStandardMaterial.emissiveIntensity\" target=\"_blank\">•</a></td><td><a href=\"https://threejs.org/docs/#MeshPhysicalMaterial.emissiveIntensity\" target=\"_blank\">•</a></td></tr><tr><td>emissiveMap</td><td><a href=\"https://threejs.org/docs/#MeshBasicMaterial.emissiveMap\" target=\"_blank\"></a></td><td><a href=\"https://threejs.org/docs/#MeshLambertMaterial.emissiveMap\" target=\"_blank\">•</a></td><td><a href=\"https://threejs.org/docs/#MeshPhongMaterial.emissiveMap\" target=\"_blank\">•</a></td><td><a href=\"https://threejs.org/docs/#MeshStandardMaterial.emissiveMap\" target=\"_blank\">•</a></td><td><a href=\"https://threejs.org/docs/#MeshPhysicalMaterial.emissiveMap\" target=\"_blank\">•</a></td></tr><tr><td>envMap</td><td><a href=\"https://threejs.org/docs/#MeshBasicMaterial.envMap\" target=\"_blank\">•</a></td><td><a href=\"https://threejs.org/docs/#MeshLambertMaterial.envMap\" target=\"_blank\">•</a></td><td><a href=\"https://threejs.org/docs/#MeshPhongMaterial.envMap\" target=\"_blank\">•</a></td><td><a href=\"https://threejs.org/docs/#MeshStandardMaterial.envMap\" target=\"_blank\">•</a></td><td><a href=\"https://threejs.org/docs/#MeshPhysicalMaterial.envMap\" target=\"_blank\">•</a></td></tr><tr><td>envMapIntensity</td><td><a href=\"https://threejs.org/docs/#MeshBasicMaterial.envMapIntensity\" target=\"_blank\"></a></td><td><a href=\"https://threejs.org/docs/#MeshLambertMaterial.envMapIntensity\" target=\"_blank\"></a></td><td><a href=\"https://threejs.org/docs/#MeshPhongMaterial.envMapIntensity\" target=\"_blank\"></a></td><td><a href=\"https://threejs.org/docs/#MeshStandardMaterial.envMapIntensity\" target=\"_blank\">•</a></td><td><a href=\"https://threejs.org/docs/#MeshPhysicalMaterial.envMapIntensity\" target=\"_blank\">•</a></td></tr><tr><td>envMapRotation</td><td><a href=\"https://threejs.org/docs/#MeshBasicMaterial.envMapRotation\" target=\"_blank\">•</a></td><td><a href=\"https://threejs.org/docs/#MeshLambertMaterial.envMapRotation\" target=\"_blank\">•</a></td><td><a href=\"https://threejs.org/docs/#MeshPhongMaterial.envMapRotation\" target=\"_blank\">•</a></td><td><a href=\"https://threejs.org/docs/#MeshStandardMaterial.envMapRotation\" target=\"_blank\">•</a></td><td><a href=\"https://threejs.org/docs/#MeshPhysicalMaterial.envMapRotation\" target=\"_blank\">•</a></td></tr><tr><td>flatShading</td><td><a href=\"https://threejs.org/docs/#MeshBasicMaterial.flatShading\" target=\"_blank\"></a></td><td><a href=\"https://threejs.org/docs/#MeshLambertMaterial.flatShading\" target=\"_blank\">•</a></td><td><a href=\"https://threejs.org/docs/#MeshPhongMaterial.flatShading\" target=\"_blank\">•</a></td><td><a href=\"https://threejs.org/docs/#MeshStandardMaterial.flatShading\" target=\"_blank\">•</a></td><td><a href=\"https://threejs.org/docs/#MeshPhysicalMaterial.flatShading\" target=\"_blank\">•</a></td></tr><tr><td>fog</td><td><a href=\"https://threejs.org/docs/#MeshBasicMaterial.fog\" target=\"_blank\">•</a></td><td><a href=\"https://threejs.org/docs/#MeshLambertMaterial.fog\" target=\"_blank\">•</a></td><td><a href=\"https://threejs.org/docs/#MeshPhongMaterial.fog\" target=\"_blank\">•</a></td><td><a href=\"https://threejs.org/docs/#MeshStandardMaterial.fog\" target=\"_blank\">•</a></td><td><a href=\"https://threejs.org/docs/#MeshPhysicalMaterial.fog\" target=\"_blank\">•</a></td></tr><tr><td>ior</td><td><a href=\"https://threejs.org/docs/#MeshBasicMaterial.ior\" target=\"_blank\"></a></td><td><a href=\"https://threejs.org/docs/#MeshLambertMaterial.ior\" target=\"_blank\"></a></td><td><a href=\"https://threejs.org/docs/#MeshPhongMaterial.ior\" target=\"_blank\"></a></td><td><a href=\"https://threejs.org/docs/#MeshStandardMaterial.ior\" target=\"_blank\"></a></td><td><a href=\"https://threejs.org/docs/#MeshPhysicalMaterial.ior\" target=\"_blank\">•</a></td></tr><tr><td>iridescence</td><td><a href=\"https://threejs.org/docs/#MeshBasicMaterial.iridescence\" target=\"_blank\"></a></td><td><a href=\"https://threejs.org/docs/#MeshLambertMaterial.iridescence\" target=\"_blank\"></a></td><td><a href=\"https://threejs.org/docs/#MeshPhongMaterial.iridescence\" target=\"_blank\"></a></td><td><a href=\"https://threejs.org/docs/#MeshStandardMaterial.iridescence\" target=\"_blank\"></a></td><td><a href=\"https://threejs.org/docs/#MeshPhysicalMaterial.iridescence\" target=\"_blank\">•</a></td></tr><tr><td>iridescenceIOR</td><td><a href=\"https://threejs.org/docs/#MeshBasicMaterial.iridescenceIOR\" target=\"_blank\"></a></td><td><a href=\"https://threejs.org/docs/#MeshLambertMaterial.iridescenceIOR\" target=\"_blank\"></a></td><td><a href=\"https://threejs.org/docs/#MeshPhongMaterial.iridescenceIOR\" target=\"_blank\"></a></td><td><a href=\"https://threejs.org/docs/#MeshStandardMaterial.iridescenceIOR\" target=\"_blank\"></a></td><td><a href=\"https://threejs.org/docs/#MeshPhysicalMaterial.iridescenceIOR\" target=\"_blank\">•</a></td></tr><tr><td>iridescenceMap</td><td><a href=\"https://threejs.org/docs/#MeshBasicMaterial.iridescenceMap\" target=\"_blank\"></a></td><td><a href=\"https://threejs.org/docs/#MeshLambertMaterial.iridescenceMap\" target=\"_blank\"></a></td><td><a href=\"https://threejs.org/docs/#MeshPhongMaterial.iridescenceMap\" target=\"_blank\"></a></td><td><a href=\"https://threejs.org/docs/#MeshStandardMaterial.iridescenceMap\" target=\"_blank\"></a></td><td><a href=\"https://threejs.org/docs/#MeshPhysicalMaterial.iridescenceMap\" target=\"_blank\">•</a></td></tr><tr><td>iridescenceThicknessMap</td><td><a href=\"https://threejs.org/docs/#MeshBasicMaterial.iridescenceThicknessMap\" target=\"_blank\"></a></td><td><a href=\"https://threejs.org/docs/#MeshLambertMaterial.iridescenceThicknessMap\" target=\"_blank\"></a></td><td><a href=\"https://threejs.org/docs/#MeshPhongMaterial.iridescenceThicknessMap\" target=\"_blank\"></a></td><td><a href=\"https://threejs.org/docs/#MeshStandardMaterial.iridescenceThicknessMap\" target=\"_blank\"></a></td><td><a href=\"https://threejs.org/docs/#MeshPhysicalMaterial.iridescenceThicknessMap\" target=\"_blank\">•</a></td></tr><tr><td>iridescenceThicknessRange</td><td><a href=\"https://threejs.org/docs/#MeshBasicMaterial.iridescenceThicknessRange\" target=\"_blank\"></a></td><td><a href=\"https://threejs.org/docs/#MeshLambertMaterial.iridescenceThicknessRange\" target=\"_blank\"></a></td><td><a href=\"https://threejs.org/docs/#MeshPhongMaterial.iridescenceThicknessRange\" target=\"_blank\"></a></td><td><a href=\"https://threejs.org/docs/#MeshStandardMaterial.iridescenceThicknessRange\" target=\"_blank\"></a></td><td><a href=\"https://threejs.org/docs/#MeshPhysicalMaterial.iridescenceThicknessRange\" target=\"_blank\">•</a></td></tr><tr><td>lightMap</td><td><a href=\"https://threejs.org/docs/#MeshBasicMaterial.lightMap\" target=\"_blank\">•</a></td><td><a href=\"https://threejs.org/docs/#MeshLambertMaterial.lightMap\" target=\"_blank\">•</a></td><td><a href=\"https://threejs.org/docs/#MeshPhongMaterial.lightMap\" target=\"_blank\">•</a></td><td><a href=\"https://threejs.org/docs/#MeshStandardMaterial.lightMap\" target=\"_blank\">•</a></td><td><a href=\"https://threejs.org/docs/#MeshPhysicalMaterial.lightMap\" target=\"_blank\">•</a></td></tr><tr><td>lightMapIntensity</td><td><a href=\"https://threejs.org/docs/#MeshBasicMaterial.lightMapIntensity\" target=\"_blank\">•</a></td><td><a href=\"https://threejs.org/docs/#MeshLambertMaterial.lightMapIntensity\" target=\"_blank\">•</a></td><td><a href=\"https://threejs.org/docs/#MeshPhongMaterial.lightMapIntensity\" target=\"_blank\">•</a></td><td><a href=\"https://threejs.org/docs/#MeshStandardMaterial.lightMapIntensity\" target=\"_blank\">•</a></td><td><a href=\"https://threejs.org/docs/#MeshPhysicalMaterial.lightMapIntensity\" target=\"_blank\">•</a></td></tr><tr><td>map</td><td><a href=\"https://threejs.org/docs/#MeshBasicMaterial.map\" target=\"_blank\">•</a></td><td><a href=\"https://threejs.org/docs/#MeshLambertMaterial.map\" target=\"_blank\">•</a></td><td><a href=\"https://threejs.org/docs/#MeshPhongMaterial.map\" target=\"_blank\">•</a></td><td><a href=\"https://threejs.org/docs/#MeshStandardMaterial.map\" target=\"_blank\">•</a></td><td><a href=\"https://threejs.org/docs/#MeshPhysicalMaterial.map\" target=\"_blank\">•</a></td></tr><tr><td>metalness</td><td><a href=\"https://threejs.org/docs/#MeshBasicMaterial.metalness\" target=\"_blank\"></a></td><td><a href=\"https://threejs.org/docs/#MeshLambertMaterial.metalness\" target=\"_blank\"></a></td><td><a href=\"https://threejs.org/docs/#MeshPhongMaterial.metalness\" target=\"_blank\"></a></td><td><a href=\"https://threejs.org/docs/#MeshStandardMaterial.metalness\" target=\"_blank\">•</a></td><td><a href=\"https://threejs.org/docs/#MeshPhysicalMaterial.metalness\" target=\"_blank\">•</a></td></tr><tr><td>metalnessMap</td><td><a href=\"https://threejs.org/docs/#MeshBasicMaterial.metalnessMap\" target=\"_blank\"></a></td><td><a href=\"https://threejs.org/docs/#MeshLambertMaterial.metalnessMap\" target=\"_blank\"></a></td><td><a href=\"https://threejs.org/docs/#MeshPhongMaterial.metalnessMap\" target=\"_blank\"></a></td><td><a href=\"https://threejs.org/docs/#MeshStandardMaterial.metalnessMap\" target=\"_blank\">•</a></td><td><a href=\"https://threejs.org/docs/#MeshPhysicalMaterial.metalnessMap\" target=\"_blank\">•</a></td></tr><tr><td>normalMap</td><td><a href=\"https://threejs.org/docs/#MeshBasicMaterial.normalMap\" target=\"_blank\"></a></td><td><a href=\"https://threejs.org/docs/#MeshLambertMaterial.normalMap\" target=\"_blank\">•</a></td><td><a href=\"https://threejs.org/docs/#MeshPhongMaterial.normalMap\" target=\"_blank\">•</a></td><td><a href=\"https://threejs.org/docs/#MeshStandardMaterial.normalMap\" target=\"_blank\">•</a></td><td><a href=\"https://threejs.org/docs/#MeshPhysicalMaterial.normalMap\" target=\"_blank\">•</a></td></tr><tr><td>normalMapType</td><td><a href=\"https://threejs.org/docs/#MeshBasicMaterial.normalMapType\" target=\"_blank\"></a></td><td><a href=\"https://threejs.org/docs/#MeshLambertMaterial.normalMapType\" target=\"_blank\">•</a></td><td><a href=\"https://threejs.org/docs/#MeshPhongMaterial.normalMapType\" target=\"_blank\">•</a></td><td><a href=\"https://threejs.org/docs/#MeshStandardMaterial.normalMapType\" target=\"_blank\">•</a></td><td><a href=\"https://threejs.org/docs/#MeshPhysicalMaterial.normalMapType\" target=\"_blank\">•</a></td></tr><tr><td>normalScale</td><td><a href=\"https://threejs.org/docs/#MeshBasicMaterial.normalScale\" target=\"_blank\"></a></td><td><a href=\"https://threejs.org/docs/#MeshLambertMaterial.normalScale\" target=\"_blank\">•</a></td><td><a href=\"https://threejs.org/docs/#MeshPhongMaterial.normalScale\" target=\"_blank\">•</a></td><td><a href=\"https://threejs.org/docs/#MeshStandardMaterial.normalScale\" target=\"_blank\">•</a></td><td><a href=\"https://threejs.org/docs/#MeshPhysicalMaterial.normalScale\" target=\"_blank\">•</a></td></tr><tr><td>reflectivity</td><td><a href=\"https://threejs.org/docs/#MeshBasicMaterial.reflectivity\" target=\"_blank\">•</a></td><td><a href=\"https://threejs.org/docs/#MeshLambertMaterial.reflectivity\" target=\"_blank\">•</a></td><td><a href=\"https://threejs.org/docs/#MeshPhongMaterial.reflectivity\" target=\"_blank\">•</a></td><td><a href=\"https://threejs.org/docs/#MeshStandardMaterial.reflectivity\" target=\"_blank\"></a></td><td><a href=\"https://threejs.org/docs/#MeshPhysicalMaterial.reflectivity\" target=\"_blank\">•</a></td></tr><tr><td>refractionRatio</td><td><a href=\"https://threejs.org/docs/#MeshBasicMaterial.refractionRatio\" target=\"_blank\">•</a></td><td><a href=\"https://threejs.org/docs/#MeshLambertMaterial.refractionRatio\" target=\"_blank\">•</a></td><td><a href=\"https://threejs.org/docs/#MeshPhongMaterial.refractionRatio\" target=\"_blank\">•</a></td><td><a href=\"https://threejs.org/docs/#MeshStandardMaterial.refractionRatio\" target=\"_blank\"></a></td><td><a href=\"https://threejs.org/docs/#MeshPhysicalMaterial.refractionRatio\" target=\"_blank\"></a></td></tr><tr><td>roughness</td><td><a href=\"https://threejs.org/docs/#MeshBasicMaterial.roughness\" target=\"_blank\"></a></td><td><a href=\"https://threejs.org/docs/#MeshLambertMaterial.roughness\" target=\"_blank\"></a></td><td><a href=\"https://threejs.org/docs/#MeshPhongMaterial.roughness\" target=\"_blank\"></a></td><td><a href=\"https://threejs.org/docs/#MeshStandardMaterial.roughness\" target=\"_blank\">•</a></td><td><a href=\"https://threejs.org/docs/#MeshPhysicalMaterial.roughness\" target=\"_blank\">•</a></td></tr><tr><td>roughnessMap</td><td><a href=\"https://threejs.org/docs/#MeshBasicMaterial.roughnessMap\" target=\"_blank\"></a></td><td><a href=\"https://threejs.org/docs/#MeshLambertMaterial.roughnessMap\" target=\"_blank\"></a></td><td><a href=\"https://threejs.org/docs/#MeshPhongMaterial.roughnessMap\" target=\"_blank\"></a></td><td><a href=\"https://threejs.org/docs/#MeshStandardMaterial.roughnessMap\" target=\"_blank\">•</a></td><td><a href=\"https://threejs.org/docs/#MeshPhysicalMaterial.roughnessMap\" target=\"_blank\">•</a></td></tr><tr><td>sheen</td><td><a href=\"https://threejs.org/docs/#MeshBasicMaterial.sheen\" target=\"_blank\"></a></td><td><a href=\"https://threejs.org/docs/#MeshLambertMaterial.sheen\" target=\"_blank\"></a></td><td><a href=\"https://threejs.org/docs/#MeshPhongMaterial.sheen\" target=\"_blank\"></a></td><td><a href=\"https://threejs.org/docs/#MeshStandardMaterial.sheen\" target=\"_blank\"></a></td><td><a href=\"https://threejs.org/docs/#MeshPhysicalMaterial.sheen\" target=\"_blank\">•</a></td></tr><tr><td>sheenColor</td><td><a href=\"https://threejs.org/docs/#MeshBasicMaterial.sheenColor\" target=\"_blank\"></a></td><td><a href=\"https://threejs.org/docs/#MeshLambertMaterial.sheenColor\" target=\"_blank\"></a></td><td><a href=\"https://threejs.org/docs/#MeshPhongMaterial.sheenColor\" target=\"_blank\"></a></td><td><a href=\"https://threejs.org/docs/#MeshStandardMaterial.sheenColor\" target=\"_blank\"></a></td><td><a href=\"https://threejs.org/docs/#MeshPhysicalMaterial.sheenColor\" target=\"_blank\">•</a></td></tr><tr><td>sheenColorMap</td><td><a href=\"https://threejs.org/docs/#MeshBasicMaterial.sheenColorMap\" target=\"_blank\"></a></td><td><a href=\"https://threejs.org/docs/#MeshLambertMaterial.sheenColorMap\" target=\"_blank\"></a></td><td><a href=\"https://threejs.org/docs/#MeshPhongMaterial.sheenColorMap\" target=\"_blank\"></a></td><td><a href=\"https://threejs.org/docs/#MeshStandardMaterial.sheenColorMap\" target=\"_blank\"></a></td><td><a href=\"https://threejs.org/docs/#MeshPhysicalMaterial.sheenColorMap\" target=\"_blank\">•</a></td></tr><tr><td>sheenRoughness</td><td><a href=\"https://threejs.org/docs/#MeshBasicMaterial.sheenRoughness\" target=\"_blank\"></a></td><td><a href=\"https://threejs.org/docs/#MeshLambertMaterial.sheenRoughness\" target=\"_blank\"></a></td><td><a href=\"https://threejs.org/docs/#MeshPhongMaterial.sheenRoughness\" target=\"_blank\"></a></td><td><a href=\"https://threejs.org/docs/#MeshStandardMaterial.sheenRoughness\" target=\"_blank\"></a></td><td><a href=\"https://threejs.org/docs/#MeshPhysicalMaterial.sheenRoughness\" target=\"_blank\">•</a></td></tr><tr><td>sheenRoughnessMap</td><td><a href=\"https://threejs.org/docs/#MeshBasicMaterial.sheenRoughnessMap\" target=\"_blank\"></a></td><td><a href=\"https://threejs.org/docs/#MeshLambertMaterial.sheenRoughnessMap\" target=\"_blank\"></a></td><td><a href=\"https://threejs.org/docs/#MeshPhongMaterial.sheenRoughnessMap\" target=\"_blank\"></a></td><td><a href=\"https://threejs.org/docs/#MeshStandardMaterial.sheenRoughnessMap\" target=\"_blank\"></a></td><td><a href=\"https://threejs.org/docs/#MeshPhysicalMaterial.sheenRoughnessMap\" target=\"_blank\">•</a></td></tr><tr><td>shininess</td><td><a href=\"https://threejs.org/docs/#MeshBasicMaterial.shininess\" target=\"_blank\"></a></td><td><a href=\"https://threejs.org/docs/#MeshLambertMaterial.shininess\" target=\"_blank\"></a></td><td><a href=\"https://threejs.org/docs/#MeshPhongMaterial.shininess\" target=\"_blank\">•</a></td><td><a href=\"https://threejs.org/docs/#MeshStandardMaterial.shininess\" target=\"_blank\"></a></td><td><a href=\"https://threejs.org/docs/#MeshPhysicalMaterial.shininess\" target=\"_blank\"></a></td></tr><tr><td>specular</td><td><a href=\"https://threejs.org/docs/#MeshBasicMaterial.specular\" target=\"_blank\"></a></td><td><a href=\"https://threejs.org/docs/#MeshLambertMaterial.specular\" target=\"_blank\"></a></td><td><a href=\"https://threejs.org/docs/#MeshPhongMaterial.specular\" target=\"_blank\">•</a></td><td><a href=\"https://threejs.org/docs/#MeshStandardMaterial.specular\" target=\"_blank\"></a></td><td><a href=\"https://threejs.org/docs/#MeshPhysicalMaterial.specular\" target=\"_blank\"></a></td></tr><tr><td>specularColor</td><td><a href=\"https://threejs.org/docs/#MeshBasicMaterial.specularColor\" target=\"_blank\"></a></td><td><a href=\"https://threejs.org/docs/#MeshLambertMaterial.specularColor\" target=\"_blank\"></a></td><td><a href=\"https://threejs.org/docs/#MeshPhongMaterial.specularColor\" target=\"_blank\"></a></td><td><a href=\"https://threejs.org/docs/#MeshStandardMaterial.specularColor\" target=\"_blank\"></a></td><td><a href=\"https://threejs.org/docs/#MeshPhysicalMaterial.specularColor\" target=\"_blank\">•</a></td></tr><tr><td>specularColorMap</td><td><a href=\"https://threejs.org/docs/#MeshBasicMaterial.specularColorMap\" target=\"_blank\"></a></td><td><a href=\"https://threejs.org/docs/#MeshLambertMaterial.specularColorMap\" target=\"_blank\"></a></td><td><a href=\"https://threejs.org/docs/#MeshPhongMaterial.specularColorMap\" target=\"_blank\"></a></td><td><a href=\"https://threejs.org/docs/#MeshStandardMaterial.specularColorMap\" target=\"_blank\"></a></td><td><a href=\"https://threejs.org/docs/#MeshPhysicalMaterial.specularColorMap\" target=\"_blank\">•</a></td></tr><tr><td>specularIntensity</td><td><a href=\"https://threejs.org/docs/#MeshBasicMaterial.specularIntensity\" target=\"_blank\"></a></td><td><a href=\"https://threejs.org/docs/#MeshLambertMaterial.specularIntensity\" target=\"_blank\"></a></td><td><a href=\"https://threejs.org/docs/#MeshPhongMaterial.specularIntensity\" target=\"_blank\"></a></td><td><a href=\"https://threejs.org/docs/#MeshStandardMaterial.specularIntensity\" target=\"_blank\"></a></td><td><a href=\"https://threejs.org/docs/#MeshPhysicalMaterial.specularIntensity\" target=\"_blank\">•</a></td></tr><tr><td>specularIntensityMap</td><td><a href=\"https://threejs.org/docs/#MeshBasicMaterial.specularIntensityMap\" target=\"_blank\"></a></td><td><a href=\"https://threejs.org/docs/#MeshLambertMaterial.specularIntensityMap\" target=\"_blank\"></a></td><td><a href=\"https://threejs.org/docs/#MeshPhongMaterial.specularIntensityMap\" target=\"_blank\"></a></td><td><a href=\"https://threejs.org/docs/#MeshStandardMaterial.specularIntensityMap\" target=\"_blank\"></a></td><td><a href=\"https://threejs.org/docs/#MeshPhysicalMaterial.specularIntensityMap\" target=\"_blank\">•</a></td></tr><tr><td>specularMap</td><td><a href=\"https://threejs.org/docs/#MeshBasicMaterial.specularMap\" target=\"_blank\">•</a></td><td><a href=\"https://threejs.org/docs/#MeshLambertMaterial.specularMap\" target=\"_blank\">•</a></td><td><a href=\"https://threejs.org/docs/#MeshPhongMaterial.specularMap\" target=\"_blank\">•</a></td><td><a href=\"https://threejs.org/docs/#MeshStandardMaterial.specularMap\" target=\"_blank\"></a></td><td><a href=\"https://threejs.org/docs/#MeshPhysicalMaterial.specularMap\" target=\"_blank\"></a></td></tr><tr><td>thickness</td><td><a href=\"https://threejs.org/docs/#MeshBasicMaterial.thickness\" target=\"_blank\"></a></td><td><a href=\"https://threejs.org/docs/#MeshLambertMaterial.thickness\" target=\"_blank\"></a></td><td><a href=\"https://threejs.org/docs/#MeshPhongMaterial.thickness\" target=\"_blank\"></a></td><td><a href=\"https://threejs.org/docs/#MeshStandardMaterial.thickness\" target=\"_blank\"></a></td><td><a href=\"https://threejs.org/docs/#MeshPhysicalMaterial.thickness\" target=\"_blank\">•</a></td></tr><tr><td>thicknessMap</td><td><a href=\"https://threejs.org/docs/#MeshBasicMaterial.thicknessMap\" target=\"_blank\"></a></td><td><a href=\"https://threejs.org/docs/#MeshLambertMaterial.thicknessMap\" target=\"_blank\"></a></td><td><a href=\"https://threejs.org/docs/#MeshPhongMaterial.thicknessMap\" target=\"_blank\"></a></td><td><a href=\"https://threejs.org/docs/#MeshStandardMaterial.thicknessMap\" target=\"_blank\"></a></td><td><a href=\"https://threejs.org/docs/#MeshPhysicalMaterial.thicknessMap\" target=\"_blank\">•</a></td></tr><tr><td>transmission</td><td><a href=\"https://threejs.org/docs/#MeshBasicMaterial.transmission\" target=\"_blank\"></a></td><td><a href=\"https://threejs.org/docs/#MeshLambertMaterial.transmission\" target=\"_blank\"></a></td><td><a href=\"https://threejs.org/docs/#MeshPhongMaterial.transmission\" target=\"_blank\"></a></td><td><a href=\"https://threejs.org/docs/#MeshStandardMaterial.transmission\" target=\"_blank\"></a></td><td><a href=\"https://threejs.org/docs/#MeshPhysicalMaterial.transmission\" target=\"_blank\">•</a></td></tr><tr><td>transmissionMap</td><td><a href=\"https://threejs.org/docs/#MeshBasicMaterial.transmissionMap\" target=\"_blank\"></a></td><td><a href=\"https://threejs.org/docs/#MeshLambertMaterial.transmissionMap\" target=\"_blank\"></a></td><td><a href=\"https://threejs.org/docs/#MeshPhongMaterial.transmissionMap\" target=\"_blank\"></a></td><td><a href=\"https://threejs.org/docs/#MeshStandardMaterial.transmissionMap\" target=\"_blank\"></a></td><td><a href=\"https://threejs.org/docs/#MeshPhysicalMaterial.transmissionMap\" target=\"_blank\">•</a></td></tr><tr><td>wireframe</td><td><a href=\"https://threejs.org/docs/#MeshBasicMaterial.wireframe\" target=\"_blank\">•</a></td><td><a href=\"https://threejs.org/docs/#MeshLambertMaterial.wireframe\" target=\"_blank\">•</a></td><td><a href=\"https://threejs.org/docs/#MeshPhongMaterial.wireframe\" target=\"_blank\">•</a></td><td><a href=\"https://threejs.org/docs/#MeshStandardMaterial.wireframe\" target=\"_blank\">•</a></td><td><a href=\"https://threejs.org/docs/#MeshPhysicalMaterial.wireframe\" target=\"_blank\">•</a></td></tr><tr><td>wireframeLinecap</td><td><a href=\"https://threejs.org/docs/#MeshBasicMaterial.wireframeLinecap\" target=\"_blank\">•</a></td><td><a href=\"https://threejs.org/docs/#MeshLambertMaterial.wireframeLinecap\" target=\"_blank\">•</a></td><td><a href=\"https://threejs.org/docs/#MeshPhongMaterial.wireframeLinecap\" target=\"_blank\">•</a></td><td><a href=\"https://threejs.org/docs/#MeshStandardMaterial.wireframeLinecap\" target=\"_blank\">•</a></td><td><a href=\"https://threejs.org/docs/#MeshPhysicalMaterial.wireframeLinecap\" target=\"_blank\">•</a></td></tr><tr><td>wireframeLinejoin</td><td><a href=\"https://threejs.org/docs/#MeshBasicMaterial.wireframeLinejoin\" target=\"_blank\">•</a></td><td><a href=\"https://threejs.org/docs/#MeshLambertMaterial.wireframeLinejoin\" target=\"_blank\">•</a></td><td><a href=\"https://threejs.org/docs/#MeshPhongMaterial.wireframeLinejoin\" target=\"_blank\">•</a></td><td><a href=\"https://threejs.org/docs/#MeshStandardMaterial.wireframeLinejoin\" target=\"_blank\">•</a></td><td><a href=\"https://threejs.org/docs/#MeshPhysicalMaterial.wireframeLinejoin\" target=\"_blank\">•</a></td></tr><tr><td>wireframeLinewidth</td><td><a href=\"https://threejs.org/docs/#MeshBasicMaterial.wireframeLinewidth\" target=\"_blank\">•</a></td><td><a href=\"https://threejs.org/docs/#MeshLambertMaterial.wireframeLinewidth\" target=\"_blank\">•</a></td><td><a href=\"https://threejs.org/docs/#MeshPhongMaterial.wireframeLinewidth\" target=\"_blank\">•</a></td><td><a href=\"https://threejs.org/docs/#MeshStandardMaterial.wireframeLinewidth\" target=\"_blank\">•</a></td><td><a href=\"https://threejs.org/docs/#MeshPhysicalMaterial.wireframeLinewidth\" target=\"_blank\">•</a></td></tr></table></div>\n\n<link rel=\"stylesheet\" href=\"../resources/threejs-material-table.css\">\n</div>\n\n\n        </div>\n      </div>\n    </div>",
    "success": true,
    "error": "",
    "crawled_at": 1786376809.300204,
    "is_shell": false
  }
]