[{"id": "03027137a4678fdc", "url": "https://threejs.org/manual/", "page_title": "three.js manual", "page_type": "overview", "page_summary": "The main index page for the three.js manual, listing all available guides, tutorials, references, and solutions organized by topic category including Getting Started, Fundamentals, Tips, Optimization, Solutions, WebGPU, and WebXR.", "heading": "Getting Started", "content": "Page: three.js manual\nSection: Getting Started\n\n- Installation\n- Creating a Scene\n- Creating Text\n- Drawing Lines\n- FAQ\n- Libraries and Plugins\n- Loading 3D Models\n- Uniform Types\n- Useful Links\n- WebGL Compatibility Check", "code_blocks": [], "chunk_position": 0, "heading_path": "Getting Started > Getting Started", "breadcrumbs": "three.js manual > Getting Started > Getting Started"}, {"id": "b6ad18bc0a5337ba", "url": "https://threejs.org/manual/", "page_title": "three.js manual", "page_type": "overview", "page_summary": "The main index page for the three.js manual, listing all available guides, tutorials, references, and solutions organized by topic category including Getting Started, Fundamentals, Tips, Optimization, Solutions, WebGPU, and WebXR.", "heading": "Next Steps", "content": "Page: three.js manual\nSection: Next Steps\n\n- Animation System\n- Color Management\n- How to create VR content\n- How to dispose of Objects\n- How to update Things\n- How to use Post Processing\n- Matrix Transformations", "code_blocks": [], "chunk_position": 0, "heading_path": "Next Steps > Next Steps", "breadcrumbs": "three.js manual > Next Steps > Next Steps"}, {"id": "dd579052a01413c8", "url": "https://threejs.org/manual/", "page_title": "three.js manual", "page_type": "overview", "page_summary": "The main index page for the three.js manual, listing all available guides, tutorials, references, and solutions organized by topic category including Getting Started, Fundamentals, Tips, Optimization, Solutions, WebGPU, and WebXR.", "heading": "Basics", "content": "Page: three.js manual\nSection: Basics\n\n- Fundamentals\n- Responsive Design\n- Prerequisites\n- Setup", "code_blocks": [], "chunk_position": 0, "heading_path": "Basics > Basics", "breadcrumbs": "three.js manual > Basics > Basics"}, {"id": "78d327aa7c73e497", "url": "https://threejs.org/manual/", "page_title": "three.js manual", "page_type": "overview", "page_summary": "The main index page for the three.js manual, listing all available guides, tutorials, references, and solutions organized by topic category including Getting Started, Fundamentals, Tips, Optimization, Solutions, WebGPU, and WebXR.", "heading": "Fundamentals", "content": "Page: three.js manual\nSection: Fundamentals\n\n- Primitives\n- Scenegraph\n- Materials\n- Textures\n- Lights\n- Cameras\n- Shadows\n- Fog\n- Render Targets\n- Custom BufferGeometry\n- Physics", "code_blocks": [], "chunk_position": 0, "heading_path": "Fundamentals > Fundamentals", "breadcrumbs": "three.js manual > Fundamentals > Fundamentals"}, {"id": "e926f7bb29e2aec5", "url": "https://threejs.org/manual/", "page_title": "three.js manual", "page_type": "overview", "page_summary": "The main index page for the three.js manual, listing all available guides, tutorials, references, and solutions organized by topic category including Getting Started, Fundamentals, Tips, Optimization, Solutions, WebGPU, and WebXR.", "heading": "Tips", "content": "Page: three.js manual\nSection: Tips\n\n- Rendering On Demand\n- Debugging JavaScript\n- Debugging GLSL\n- Taking a screenshot\n- Prevent the Canvas Being Cleared\n- Get Keyboard Input From a Canvas\n- Make the Canvas Transparent\n- Use three.js as Background in HTML", "code_blocks": [], "chunk_position": 0, "heading_path": "Tips > Tips", "breadcrumbs": "three.js manual > Tips > Tips"}, {"id": "ed8701fe92b668b7", "url": "https://threejs.org/manual/", "page_title": "three.js manual", "page_type": "overview", "page_summary": "The main index page for the three.js manual, listing all available guides, tutorials, references, and solutions organized by topic category including Getting Started, Fundamentals, Tips, Optimization, Solutions, WebGPU, and WebXR.", "heading": "Optimization", "content": "Page: three.js manual\nSection: Optimization\n\n- Optimizing Lots of Objects\n- Optimizing Lots of Objects Animated\n- Using OffscreenCanvas in a Web Worker", "code_blocks": [], "chunk_position": 0, "heading_path": "Optimization > Optimization", "breadcrumbs": "three.js manual > Optimization > Optimization"}, {"id": "b9d5c5e777150691", "url": "https://threejs.org/manual/", "page_title": "three.js manual", "page_type": "overview", "page_summary": "The main index page for the three.js manual, listing all available guides, tutorials, references, and solutions organized by topic category including Getting Started, Fundamentals, Tips, Optimization, Solutions, WebGPU, and WebXR.", "heading": "Solutions", "content": "Page: three.js manual\nSection: Solutions\n\n- Load an .OBJ file\n- Load a .GLTF file\n- Add a Background or Skybox\n- How to Draw Transparent Objects\n- Multiple Canvases, Multiple Scenes\n- Picking Objects with the mouse\n- Post Processing\n- Using Shadertoy shaders\n- Aligning HTML Elements to 3D\n- Using Indexed Textures for Picking and Color\n- Using A Canvas for Dynamic Textures\n- Billboards and Facades\n- Freeing Resources\n- Making Voxel Geometry (Minecraft)\n- Start making a Game", "code_blocks": [], "chunk_position": 0, "heading_path": "Solutions > Solutions", "breadcrumbs": "three.js manual > Solutions > Solutions"}, {"id": "ccf34ddffbd2c15a", "url": "https://threejs.org/manual/", "page_title": "three.js manual", "page_type": "overview", "page_summary": "The main index page for the three.js manual, listing all available guides, tutorials, references, and solutions organized by topic category including Getting Started, Fundamentals, Tips, Optimization, Solutions, WebGPU, and WebXR.", "heading": "WebGPU", "content": "Page: three.js manual\nSection: WebGPU\n\n- WebGPURenderer\n- Post-Processing", "code_blocks": [], "chunk_position": 0, "heading_path": "WebGPU > WebGPU", "breadcrumbs": "three.js manual > WebGPU > WebGPU"}, {"id": "55e1e411895e5dcd", "url": "https://threejs.org/manual/", "page_title": "three.js manual", "page_type": "overview", "page_summary": "The main index page for the three.js manual, listing all available guides, tutorials, references, and solutions organized by topic category including Getting Started, Fundamentals, Tips, Optimization, Solutions, WebGPU, and WebXR.", "heading": "WebXR", "content": "Page: three.js manual\nSection: WebXR\n\n- VR - Basics\n- VR - Look To Select\n- VR - Point To Select", "code_blocks": [], "chunk_position": 0, "heading_path": "WebXR > WebXR", "breadcrumbs": "three.js manual > WebXR > WebXR"}, {"id": "56f1e6d09d61bbb3", "url": "https://threejs.org/manual/", "page_title": "three.js manual", "page_type": "overview", "page_summary": "The main index page for the three.js manual, listing all available guides, tutorials, references, and solutions organized by topic category including Getting Started, Fundamentals, Tips, Optimization, Solutions, WebGPU, and WebXR.", "heading": "Reference", "content": "Page: three.js manual\nSection: Reference\n\n- Material Table", "code_blocks": [], "chunk_position": 0, "heading_path": "Reference > Reference", "breadcrumbs": "three.js manual > Reference > Reference"}, {"id": "e47b755b06638df8", "url": "https://threejs.org/docs", "page_title": "three.js docs", "page_type": "overview", "page_summary": "The main documentation index for three.js, a JavaScript 3D library. This page provides a categorized listing of all classes, modules, and functions available in the Core library, Addons, and the Three Shading Language (TSL).", "heading": "Core", "content": "Page: three.js docs\nSection: Core\n\nThe Core library provides the fundamental classes and modules for three.js, organized into the following categories: Animation, Audio, Cameras, Core, Extras, Geometries, Helpers, Lights, Loaders, Materials, Math, Nodes, Objects, Renderers, Scenes, and Textures.", "code_blocks": [], "chunk_position": 1, "heading_path": "Core > Core", "breadcrumbs": "three.js docs > Core > Core"}, {"id": "b2353059f5e33f74", "url": "https://threejs.org/docs", "page_title": "three.js docs", "page_type": "overview", "page_summary": "The main documentation index for three.js, a JavaScript 3D library. This page provides a categorized listing of all classes, modules, and functions available in the Core library, Addons, and the Three Shading Language (TSL).", "heading": "Core / Animation", "content": "Page: three.js docs\nSection: Core / Animation\n\nAnimationAction, AnimationClip, AnimationMixer, AnimationObjectGroup, AnimationUtils, BooleanKeyframeTrack, ColorKeyframeTrack, KeyframeTrack, NumberKeyframeTrack, PropertyBinding, PropertyMixer, QuaternionKeyframeTrack, StringKeyframeTrack, VectorKeyframeTrack", "code_blocks": [], "chunk_position": 1, "heading_path": "Core / Animation > Core / Animation", "breadcrumbs": "three.js docs > Core / Animation > Core / Animation"}, {"id": "3086e10b4bbd8cdf", "url": "https://threejs.org/docs", "page_title": "three.js docs", "page_type": "overview", "page_summary": "The main documentation index for three.js, a JavaScript 3D library. This page provides a categorized listing of all classes, modules, and functions available in the Core library, Addons, and the Three Shading Language (TSL).", "heading": "Core / Audio", "content": "Page: three.js docs\nSection: Core / Audio\n\nAudio, AudioAnalyser, AudioContext, AudioListener, PositionalAudio", "code_blocks": [], "chunk_position": 1, "heading_path": "Core / Audio > Core / Audio", "breadcrumbs": "three.js docs > Core / Audio > Core / Audio"}, {"id": "d74b152e334a7808", "url": "https://threejs.org/docs", "page_title": "three.js docs", "page_type": "overview", "page_summary": "The main documentation index for three.js, a JavaScript 3D library. This page provides a categorized listing of all classes, modules, and functions available in the Core library, Addons, and the Three Shading Language (TSL).", "heading": "Core / Cameras", "content": "Page: three.js docs\nSection: Core / Cameras\n\nArrayCamera, Camera, CubeCamera, OrthographicCamera, PerspectiveCamera, StereoCamera", "code_blocks": [], "chunk_position": 1, "heading_path": "Core / Cameras > Core / Cameras", "breadcrumbs": "three.js docs > Core / Cameras > Core / Cameras"}, {"id": "73cf3bf8ba7ee560", "url": "https://threejs.org/docs", "page_title": "three.js docs", "page_type": "overview", "page_summary": "The main documentation index for three.js, a JavaScript 3D library. This page provides a categorized listing of all classes, modules, and functions available in the Core library, Addons, and the Three Shading Language (TSL).", "heading": "Core / Core", "content": "Page: three.js docs\nSection: Core / Core\n\nBufferAttribute, BufferGeometry, Clock, EventDispatcher, Float16BufferAttribute, Float32BufferAttribute, GLBufferAttribute, InstancedBufferAttribute, InstancedBufferGeometry, InstancedInterleavedBuffer, Int16BufferAttribute, Int32BufferAttribute, Int8BufferAttribute, InterleavedBuffer, InterleavedBufferAttribute, Layers, Object3D, Raycaster, RenderTarget, RenderTarget3D, Timer, Uint16BufferAttribute, Uint32BufferAttribute, Uint8BufferAttribute, Uint8ClampedBufferAttribute, Uniform, UniformsGroup", "code_blocks": [], "chunk_position": 1, "heading_path": "Core / Core > Core / Core", "breadcrumbs": "three.js docs > Core / Core > Core / Core"}, {"id": "1c8696f22b0e748e", "url": "https://threejs.org/docs", "page_title": "three.js docs", "page_type": "overview", "page_summary": "The main documentation index for three.js, a JavaScript 3D library. This page provides a categorized listing of all classes, modules, and functions available in the Core library, Addons, and the Three Shading Language (TSL).", "heading": "Core / Extras", "content": "Page: three.js docs\nSection: Core / Extras\n\nArcCurve, CatmullRomCurve3, Controls, CubicBezierCurve, CubicBezierCurve3, Curve, CurvePath, DataUtils, Earcut, EllipseCurve, ImageUtils, LineCurve, LineCurve3, PMREMGenerator, Path, QuadraticBezierCurve, QuadraticBezierCurve3, Shape, ShapePath, ShapeUtils, SplineCurve, TextureUtils, Interpolations", "code_blocks": [], "chunk_position": 1, "heading_path": "Core / Extras > Core / Extras", "breadcrumbs": "three.js docs > Core / Extras > Core / Extras"}, {"id": "21b1e6d153249cda", "url": "https://threejs.org/docs", "page_title": "three.js docs", "page_type": "overview", "page_summary": "The main documentation index for three.js, a JavaScript 3D library. This page provides a categorized listing of all classes, modules, and functions available in the Core library, Addons, and the Three Shading Language (TSL).", "heading": "Core / Geometries", "content": "Page: three.js docs\nSection: Core / Geometries\n\nBoxGeometry, CapsuleGeometry, CircleGeometry, ConeGeometry, CylinderGeometry, DodecahedronGeometry, EdgesGeometry, ExtrudeGeometry, IcosahedronGeometry, LatheGeometry, OctahedronGeometry, PlaneGeometry, PolyhedronGeometry, RingGeometry, ShapeGeometry, SphereGeometry, TetrahedronGeometry, TorusGeometry, TorusKnotGeometry, TubeGeometry, WireframeGeometry", "code_blocks": [], "chunk_position": 1, "heading_path": "Core / Geometries > Core / Geometries", "breadcrumbs": "three.js docs > Core / Geometries > Core / Geometries"}, {"id": "fc77f50193ee72db", "url": "https://threejs.org/docs", "page_title": "three.js docs", "page_type": "overview", "page_summary": "The main documentation index for three.js, a JavaScript 3D library. This page provides a categorized listing of all classes, modules, and functions available in the Core library, Addons, and the Three Shading Language (TSL).", "heading": "Core / Helpers", "content": "Page: three.js docs\nSection: Core / Helpers\n\nArrowHelper, AxesHelper, Box3Helper, BoxHelper, CameraHelper, DirectionalLightHelper, GridHelper, HemisphereLightHelper, PlaneHelper, PointLightHelper, PolarGridHelper, SkeletonHelper, SpotLightHelper", "code_blocks": [], "chunk_position": 1, "heading_path": "Core / Helpers > Core / Helpers", "breadcrumbs": "three.js docs > Core / Helpers > Core / Helpers"}, {"id": "e846c5ec0a38240e", "url": "https://threejs.org/docs", "page_title": "three.js docs", "page_type": "overview", "page_summary": "The main documentation index for three.js, a JavaScript 3D library. This page provides a categorized listing of all classes, modules, and functions available in the Core library, Addons, and the Three Shading Language (TSL).", "heading": "Core / Lights", "content": "Page: three.js docs\nSection: Core / Lights\n\nAmbientLight, DirectionalLight, DirectionalLightShadow, HemisphereLight, IESSpotLight, Light, LightProbe, LightShadow, PointLight, PointLightShadow, ProjectorLight, RectAreaLight, SpotLight, SpotLightShadow", "code_blocks": [], "chunk_position": 1, "heading_path": "Core / Lights > Core / Lights", "breadcrumbs": "three.js docs > Core / Lights > Core / Lights"}, {"id": "7ebc6418a2a52c99", "url": "https://threejs.org/docs", "page_title": "three.js docs", "page_type": "overview", "page_summary": "The main documentation index for three.js, a JavaScript 3D library. This page provides a categorized listing of all classes, modules, and functions available in the Core library, Addons, and the Three Shading Language (TSL).", "heading": "Core / Loaders", "content": "Page: three.js docs\nSection: Core / Loaders\n\nAnimationLoader, AudioLoader, BufferGeometryLoader, Cache, CompressedTextureLoader, CubeTextureLoader, DataTextureLoader, FileLoader, ImageBitmapLoader, ImageLoader, Loader, LoaderUtils, LoadingManager, MaterialLoader, NodeLoader, NodeMaterialLoader, NodeObjectLoader, ObjectLoader, TextureLoader", "code_blocks": [], "chunk_position": 1, "heading_path": "Core / Loaders > Core / Loaders", "breadcrumbs": "three.js docs > Core / Loaders > Core / Loaders"}, {"id": "520d432063d47846", "url": "https://threejs.org/docs", "page_title": "three.js docs", "page_type": "overview", "page_summary": "The main documentation index for three.js, a JavaScript 3D library. This page provides a categorized listing of all classes, modules, and functions available in the Core library, Addons, and the Three Shading Language (TSL).", "heading": "Core / Materials", "content": "Page: three.js docs\nSection: Core / Materials\n\nLine2NodeMaterial, LineBasicMaterial, LineBasicNodeMaterial, LineDashedMaterial, LineDashedNodeMaterial, Material, MeshBasicMaterial, MeshBasicNodeMaterial, MeshDepthMaterial, MeshDistanceMaterial, MeshLambertMaterial, MeshLambertNodeMaterial, MeshMatcapMaterial, MeshMatcapNodeMaterial, MeshNormalMaterial, MeshNormalNodeMaterial, MeshPhongMaterial, MeshPhongNodeMaterial, MeshPhysicalMaterial, MeshPhysicalNodeMaterial, MeshSSSNodeMaterial, MeshStandardMaterial, MeshStandardNodeMaterial, MeshToonMaterial, MeshToonNodeMaterial, NodeMaterial, NodeMaterialObserver, PointsMaterial, PointsNodeMaterial, RawShaderMaterial, SSSLightingModel, ShaderMaterial, ShadowMaterial, ShadowNodeMaterial, SpriteMaterial, SpriteNodeMaterial, VolumeNodeMaterial", "code_blocks": [], "chunk_position": 1, "heading_path": "Core / Materials > Core / Materials", "breadcrumbs": "three.js docs > Core / Materials > Core / Materials"}, {"id": "9b6542f4b0ab05f2", "url": "https://threejs.org/docs", "page_title": "three.js docs", "page_type": "overview", "page_summary": "The main documentation index for three.js, a JavaScript 3D library. This page provides a categorized listing of all classes, modules, and functions available in the Core library, Addons, and the Three Shading Language (TSL).", "heading": "Core / Math", "content": "Page: three.js docs\nSection: Core / Math\n\nBezierInterpolant, Box2, Box3, Color, CubicInterpolant, Cylindrical, DiscreteInterpolant, Euler, Frustum, FrustumArray, Interpolant, Line3, LinearInterpolant, MathUtils, Matrix2, Matrix3, Matrix4, Plane, Quaternion, QuaternionLinearInterpolant, Ray, Sphere, Spherical, SphericalHarmonics3, Triangle, Vector2, Vector3, Vector4", "code_blocks": [], "chunk_position": 1, "heading_path": "Core / Math > Core / Math", "breadcrumbs": "three.js docs > Core / Math > Core / Math"}, {"id": "8022e278bf287bb1", "url": "https://threejs.org/docs", "page_title": "three.js docs", "page_type": "overview", "page_summary": "The main documentation index for three.js, a JavaScript 3D library. This page provides a categorized listing of all classes, modules, and functions available in the Core library, Addons, and the Three Shading Language (TSL).", "heading": "Core / Nodes", "content": "Page: three.js docs\nSection: Core / Nodes\n\nAONode, AmbientLightNode, AnalyticLightNode, ArrayElementNode, ArrayNode, AssignNode, AtomicFunctionNode, AttributeNode, BarrierNode, BasicEnvironmentNode, BasicLightMapNode, BasicLightingModel, BitcastNode, BitcountNode, BufferAttributeNode, BufferNode, BuiltinNode, BumpMapNode, BypassNode, ClippingNode, CodeNode, ColorSpaceNode, ComputeBuiltinNode, ComputeNode, ConditionalNode, ConstNode, ContextNode, ConvertNode, CubeMapNode, CubeTextureNode, DirectionalLightNode, EnvironmentNode, EventNode, ExpressionNode, FlipNode, FrontFacingNode, FunctionCallNode, FunctionNode, FunctionOverloadingNode, GLSLNodeFunction, GLSLNodeParser, HemisphereLightNode, IESSpotLightNode, IndexNode, InputNode, InspectorNode, IrradianceNode, IsolateNode, JoinNode, LightProbeNode, LightingContextNode, LightingModel, LightingNode, LightsNode, LoopNode, MRTNode, MaterialNode, MaterialReferenceNode, MathNode, MaxMipLevelNode, MemberNode, ModelNode, Node, NodeAttribute, NodeBuilder, NodeCache, NodeCode, NodeError, NodeFrame, NodeFunction, NodeFunctionInput, NodeParser, NodeUniform, NodeVar, NodeVarying, NormalMapNode, Object3DNode, OperatorNode, OutputStructNode, OverrideContextNode, PMREMNode, PackFloatNode, ParameterNode, PassMultipleTextureNode, PassNode, PassTextureNode, PhongLightingModel, PhysicalLightingModel, PointLightNode, PointShadowNode, PointUVNode, ProjectorLightNode, PropertyNode, RTTNode, RangeNode, RectAreaLightNode, ReferenceBaseNode, ReferenceElementNode, ReferenceNode, ReflectorNode, RenderOutputNode, RendererReferenceNode, RotateNode, SampleNode, ScreenNode, SetNode, ShadowBaseNode, ShadowMaskModel, ShadowNode, SplitNode, SpotLightNode, StackNode, StackTrace, StorageArrayElementNode, StorageBufferNode, StorageTexture3DNode, StorageTextureNode, StructNode, StructTypeNode, SubBuildNode, SubgroupFunctionNode, TempNode, Texture3DNode, TextureNode, TextureSizeNode, ToneMappingNode, ToonLightingModel, ToonOutlinePassNode, UniformArrayElementNode, UniformArrayNode, UniformGroupNode, UniformNode, UnpackFloatNode, UserDataNode, VarNode, VaryingNode, VelocityNode, VertexColorNode, ViewportDepthNode, ViewportDepthTextureNode, ViewportSharedTextureNode, ViewportTextureNode, VolumetricLightingModel, WorkgroupInfoElementNode, WorkgroupInfoNode", "code_blocks": [], "chunk_position": 1, "heading_path": "Core / Nodes > Core / Nodes", "breadcrumbs": "three.js docs > Core / Nodes > Core / Nodes"}, {"id": "a2908f26626bb329", "url": "https://threejs.org/docs", "page_title": "three.js docs", "page_type": "overview", "page_summary": "The main documentation index for three.js, a JavaScript 3D library. This page provides a categorized listing of all classes, modules, and functions available in the Core library, Addons, and the Three Shading Language (TSL).", "heading": "Core / Objects", "content": "Page: three.js docs\nSection: Core / Objects\n\nBatchedMesh, Bone, ClippingGroup, Group, InstancedMesh, LOD, Line, LineLoop, LineSegments, Mesh, Points, Skeleton, SkinnedMesh, Sprite", "code_blocks": [], "chunk_position": 1, "heading_path": "Core / Objects > Core / Objects", "breadcrumbs": "three.js docs > Core / Objects > Core / Objects"}, {"id": "8c2209224a81ce97", "url": "https://threejs.org/docs", "page_title": "three.js docs", "page_type": "overview", "page_summary": "The main documentation index for three.js, a JavaScript 3D library. This page provides a categorized listing of all classes, modules, and functions available in the Core library, Addons, and the Three Shading Language (TSL).", "heading": "Core / Renderers", "content": "Page: three.js docs\nSection: Core / Renderers\n\nBackend, BlendMode, BundleGroup, CanvasTarget, CubeRenderTarget, GLSLNodeBuilder, IndirectStorageBufferAttribute, Info, InspectorBase, PostProcessing, QuadMesh, ReadbackBuffer, RenderPipeline, Renderer, StandardNodeLibrary, Storage3DTexture, StorageArrayTexture, StorageBufferAttribute, StorageInstancedBufferAttribute, StorageTexture, TimestampQueryPool, WGSLNodeBuilder, WGSLNodeFunction, WGSLNodeParser, WebGL3DRenderTarget, WebGLArrayRenderTarget, WebGLCubeRenderTarget, WebGLRenderTarget, WebGLRenderer, WebGLTimestampQueryPool, WebGPURenderer, WebGPUTimestampQueryPool, WebXRDepthSensing, WebXRManager, XRManager, UniformsUtils", "code_blocks": [], "chunk_position": 1, "heading_path": "Core / Renderers > Core / Renderers", "breadcrumbs": "three.js docs > Core / Renderers > Core / Renderers"}, {"id": "031cff7fa573b01d", "url": "https://threejs.org/docs", "page_title": "three.js docs", "page_type": "overview", "page_summary": "The main documentation index for three.js, a JavaScript 3D library. This page provides a categorized listing of all classes, modules, and functions available in the Core library, Addons, and the Three Shading Language (TSL).", "heading": "Core / Scenes", "content": "Page: three.js docs\nSection: Core / Scenes\n\nFog, FogExp2, Scene", "code_blocks": [], "chunk_position": 1, "heading_path": "Core / Scenes > Core / Scenes", "breadcrumbs": "three.js docs > Core / Scenes > Core / Scenes"}, {"id": "3c3fa40439ba6f0a", "url": "https://threejs.org/docs", "page_title": "three.js docs", "page_type": "overview", "page_summary": "The main documentation index for three.js, a JavaScript 3D library. This page provides a categorized listing of all classes, modules, and functions available in the Core library, Addons, and the Three Shading Language (TSL).", "heading": "Core / Textures", "content": "Page: three.js docs\nSection: Core / Textures\n\nCanvasTexture, CompressedArrayTexture, CompressedCubeTexture, CompressedTexture, CubeDepthTexture, CubeTexture, Data3DTexture, DataArrayTexture, DataTexture, DepthTexture, ExternalTexture, FramebufferTexture, HTMLTexture, Source, Texture, VideoFrameTexture, VideoTexture", "code_blocks": [], "chunk_position": 1, "heading_path": "Core / Textures > Core / Textures", "breadcrumbs": "three.js docs > Core / Textures > Core / Textures"}, {"id": "c6dc3d430bd0045d", "url": "https://threejs.org/docs", "page_title": "three.js docs", "page_type": "overview", "page_summary": "The main documentation index for three.js, a JavaScript 3D library. This page provides a categorized listing of all classes, modules, and functions available in the Core library, Addons, and the Three Shading Language (TSL).", "heading": "Addons", "content": "Page: three.js docs\nSection: Addons\n\nThe Addons section provides additional classes and modules extending three.js functionality, organized into the following categories: Animation, Capabilities, Controls, Csm, Curves, Effects, Environments, Exporters, Generators, Geometries, Gpgpu, Helpers, Inspector, Interaction, Interactive, Lighting, Lights, Lines, Loaders, Materials, Math, Misc, Modifiers, Objects, Physics, Postprocessing, Renderers, Shaders, TSL, Textures, Transpiler, Utils, and Webxr.", "code_blocks": [], "chunk_position": 1, "heading_path": "Addons > Addons", "breadcrumbs": "three.js docs > Addons > Addons"}, {"id": "caf5781aaee19546", "url": "https://threejs.org/docs", "page_title": "three.js docs", "page_type": "overview", "page_summary": "The main documentation index for three.js, a JavaScript 3D library. This page provides a categorized listing of all classes, modules, and functions available in the Core library, Addons, and the Three Shading Language (TSL).", "heading": "Addons / Animation", "content": "Page: three.js docs\nSection: Addons / Animation\n\nAnimationClipCreator, CCDIKHelper, CCDIKSolver", "code_blocks": [], "chunk_position": 1, "heading_path": "Addons / Animation > Addons / Animation", "breadcrumbs": "three.js docs > Addons / Animation > Addons / Animation"}, {"id": "774ee3650cf46b3e", "url": "https://threejs.org/docs", "page_title": "three.js docs", "page_type": "overview", "page_summary": "The main documentation index for three.js, a JavaScript 3D library. This page provides a categorized listing of all classes, modules, and functions available in the Core library, Addons, and the Three Shading Language (TSL).", "heading": "Addons / Capabilities", "content": "Page: three.js docs\nSection: Addons / Capabilities\n\nWebGL, WebGPU", "code_blocks": [], "chunk_position": 1, "heading_path": "Addons / Capabilities > Addons / Capabilities", "breadcrumbs": "three.js docs > Addons / Capabilities > Addons / Capabilities"}, {"id": "41e9471065f4780f", "url": "https://threejs.org/docs", "page_title": "three.js docs", "page_type": "overview", "page_summary": "The main documentation index for three.js, a JavaScript 3D library. This page provides a categorized listing of all classes, modules, and functions available in the Core library, Addons, and the Three Shading Language (TSL).", "heading": "Addons / Controls", "content": "Page: three.js docs\nSection: Addons / Controls\n\nArcballControls, DragControls, FirstPersonControls, FlyControls, MapControls, OrbitControls, PointerLockControls, TrackballControls, TransformControls", "code_blocks": [], "chunk_position": 1, "heading_path": "Addons / Controls > Addons / Controls", "breadcrumbs": "three.js docs > Addons / Controls > Addons / Controls"}, {"id": "c98e12744375bcc5", "url": "https://threejs.org/docs", "page_title": "three.js docs", "page_type": "overview", "page_summary": "The main documentation index for three.js, a JavaScript 3D library. This page provides a categorized listing of all classes, modules, and functions available in the Core library, Addons, and the Three Shading Language (TSL).", "heading": "Addons / Csm", "content": "Page: three.js docs\nSection: Addons / Csm\n\nCSM, CSMFrustum, CSMHelper, CSMShadowNode, CSMShader", "code_blocks": [], "chunk_position": 1, "heading_path": "Addons / Csm > Addons / Csm", "breadcrumbs": "three.js docs > Addons / Csm > Addons / Csm"}, {"id": "5bae63c216bd3a70", "url": "https://threejs.org/docs", "page_title": "three.js docs", "page_type": "overview", "page_summary": "The main documentation index for three.js, a JavaScript 3D library. This page provides a categorized listing of all classes, modules, and functions available in the Core library, Addons, and the Three Shading Language (TSL).", "heading": "Addons / Curves", "content": "Page: three.js docs\nSection: Addons / Curves\n\nCinquefoilKnot, DecoratedTorusKnot4a, DecoratedTorusKnot4b, DecoratedTorusKnot5a, DecoratedTorusKnot5c, FigureEightPolynomialKnot, GrannyKnot, HeartCurve, HelixCurve, KnotCurve, NURBSCurve, NURBSSurface, NURBSVolume, TorusKnot, TrefoilKnot, TrefoilPolynomialKnot, VivianiCurve, NURBSUtils", "code_blocks": [], "chunk_position": 1, "heading_path": "Addons / Curves > Addons / Curves", "breadcrumbs": "three.js docs > Addons / Curves > Addons / Curves"}, {"id": "6bc92465166182cc", "url": "https://threejs.org/docs", "page_title": "three.js docs", "page_type": "overview", "page_summary": "The main documentation index for three.js, a JavaScript 3D library. This page provides a categorized listing of all classes, modules, and functions available in the Core library, Addons, and the Three Shading Language (TSL).", "heading": "Addons / Effects", "content": "Page: three.js docs\nSection: Addons / Effects\n\nAnaglyphEffect, AsciiEffect, OutlineEffect, ParallaxBarrierEffect, StereoEffect", "code_blocks": [], "chunk_position": 1, "heading_path": "Addons / Effects > Addons / Effects", "breadcrumbs": "three.js docs > Addons / Effects > Addons / Effects"}, {"id": "ef6df0e770ae2bd0", "url": "https://threejs.org/docs", "page_title": "three.js docs", "page_type": "overview", "page_summary": "The main documentation index for three.js, a JavaScript 3D library. This page provides a categorized listing of all classes, modules, and functions available in the Core library, Addons, and the Three Shading Language (TSL).", "heading": "Addons / Environments", "content": "Page: three.js docs\nSection: Addons / Environments\n\nColorEnvironment, DebugEnvironment, RoomEnvironment", "code_blocks": [], "chunk_position": 1, "heading_path": "Addons / Environments > Addons / Environments", "breadcrumbs": "three.js docs > Addons / Environments > Addons / Environments"}, {"id": "85ac51d0d1d70f8a", "url": "https://threejs.org/docs", "page_title": "three.js docs", "page_type": "overview", "page_summary": "The main documentation index for three.js, a JavaScript 3D library. This page provides a categorized listing of all classes, modules, and functions available in the Core library, Addons, and the Three Shading Language (TSL).", "heading": "Addons / Exporters", "content": "Page: three.js docs\nSection: Addons / Exporters\n\nDRACOExporter, EXRExporter, GLTFExporter, KTX2Exporter, OBJExporter, PLYExporter, STLExporter, USDZExporter", "code_blocks": [], "chunk_position": 1, "heading_path": "Addons / Exporters > Addons / Exporters", "breadcrumbs": "three.js docs > Addons / Exporters > Addons / Exporters"}, {"id": "6c872ebb05158056", "url": "https://threejs.org/docs", "page_title": "three.js docs", "page_type": "overview", "page_summary": "The main documentation index for three.js, a JavaScript 3D library. This page provides a categorized listing of all classes, modules, and functions available in the Core library, Addons, and the Three Shading Language (TSL).", "heading": "Addons / Generators", "content": "Page: three.js docs\nSection: Addons / Generators\n\nCityGenerator, FaceFrame, ForestGenerator, SidewalkGenerator, SkyscraperGenerator, TerrainGenerator, TreeGenerator", "code_blocks": [], "chunk_position": 1, "heading_path": "Addons / Generators > Addons / Generators", "breadcrumbs": "three.js docs > Addons / Generators > Addons / Generators"}, {"id": "1c1415c071b81044", "url": "https://threejs.org/docs", "page_title": "three.js docs", "page_type": "overview", "page_summary": "The main documentation index for three.js, a JavaScript 3D library. This page provides a categorized listing of all classes, modules, and functions available in the Core library, Addons, and the Three Shading Language (TSL).", "heading": "Addons / Geometries", "content": "Page: three.js docs\nSection: Addons / Geometries\n\nBoxLineGeometry, ConvexGeometry, DecalGeometry, LoftGeometry, ParametricGeometry, RoundedBoxGeometry, TeapotGeometry, TextGeometry, ParametricFunctions", "code_blocks": [], "chunk_position": 1, "heading_path": "Addons / Geometries > Addons / Geometries", "breadcrumbs": "three.js docs > Addons / Geometries > Addons / Geometries"}, {"id": "8e9142d8ef6e59cd", "url": "https://threejs.org/docs", "page_title": "three.js docs", "page_type": "overview", "page_summary": "The main documentation index for three.js, a JavaScript 3D library. This page provides a categorized listing of all classes, modules, and functions available in the Core library, Addons, and the Three Shading Language (TSL).", "heading": "Addons / Gpgpu", "content": "Page: three.js docs\nSection: Addons / Gpgpu\n\nBitonicSort", "code_blocks": [], "chunk_position": 1, "heading_path": "Addons / Gpgpu > Addons / Gpgpu", "breadcrumbs": "three.js docs > Addons / Gpgpu > Addons / Gpgpu"}, {"id": "cbbe20f30b007e53", "url": "https://threejs.org/docs", "page_title": "three.js docs", "page_type": "overview", "page_summary": "The main documentation index for three.js, a JavaScript 3D library. This page provides a categorized listing of all classes, modules, and functions available in the Core library, Addons, and the Three Shading Language (TSL).", "heading": "Addons / Helpers", "content": "Page: three.js docs\nSection: Addons / Helpers\n\nAnimationPathHelper, LightProbeGridHelper, LightProbeHelper, OctreeHelper, PositionalAudioHelper, RapierHelper, RectAreaLightHelper, TextureHelper, VertexNormalsHelper, VertexTangentsHelper, ViewHelper", "code_blocks": [], "chunk_position": 1, "heading_path": "Addons / Helpers > Addons / Helpers", "breadcrumbs": "three.js docs > Addons / Helpers > Addons / Helpers"}, {"id": "813709ead20f449f", "url": "https://threejs.org/docs", "page_title": "three.js docs", "page_type": "overview", "page_summary": "The main documentation index for three.js, a JavaScript 3D library. This page provides a categorized listing of all classes, modules, and functions available in the Core library, Addons, and the Three Shading Language (TSL).", "heading": "Addons / Inspector", "content": "Page: three.js docs\nSection: Addons / Inspector\n\nTab", "code_blocks": [], "chunk_position": 1, "heading_path": "Addons / Inspector > Addons / Inspector", "breadcrumbs": "three.js docs > Addons / Inspector > Addons / Inspector"}, {"id": "9378be6bcbe9b31c", "url": "https://threejs.org/docs", "page_title": "three.js docs", "page_type": "overview", "page_summary": "The main documentation index for three.js, a JavaScript 3D library. This page provides a categorized listing of all classes, modules, and functions available in the Core library, Addons, and the Three Shading Language (TSL).", "heading": "Addons / Interaction", "content": "Page: three.js docs\nSection: Addons / Interaction\n\nInteractionManager", "code_blocks": [], "chunk_position": 1, "heading_path": "Addons / Interaction > Addons / Interaction", "breadcrumbs": "three.js docs > Addons / Interaction > Addons / Interaction"}, {"id": "bcaae3a26d927d45", "url": "https://threejs.org/docs", "page_title": "three.js docs", "page_type": "overview", "page_summary": "The main documentation index for three.js, a JavaScript 3D library. This page provides a categorized listing of all classes, modules, and functions available in the Core library, Addons, and the Three Shading Language (TSL).", "heading": "Addons / Interactive", "content": "Page: three.js docs\nSection: Addons / Interactive\n\nHTMLMesh, InteractiveGroup, SelectionBox, SelectionHelper", "code_blocks": [], "chunk_position": 1, "heading_path": "Addons / Interactive > Addons / Interactive", "breadcrumbs": "three.js docs > Addons / Interactive > Addons / Interactive"}, {"id": "d0909c085897386d", "url": "https://threejs.org/docs", "page_title": "three.js docs", "page_type": "overview", "page_summary": "The main documentation index for three.js, a JavaScript 3D library. This page provides a categorized listing of all classes, modules, and functions available in the Core library, Addons, and the Three Shading Language (TSL).", "heading": "Addons / Lighting", "content": "Page: three.js docs\nSection: Addons / Lighting\n\nClusteredLighting, DynamicLighting, LightProbeGrid", "code_blocks": [], "chunk_position": 1, "heading_path": "Addons / Lighting > Addons / Lighting", "breadcrumbs": "three.js docs > Addons / Lighting > Addons / Lighting"}, {"id": "59c9a8badab2d9c3", "url": "https://threejs.org/docs", "page_title": "three.js docs", "page_type": "overview", "page_summary": "The main documentation index for three.js, a JavaScript 3D library. This page provides a categorized listing of all classes, modules, and functions available in the Core library, Addons, and the Three Shading Language (TSL).", "heading": "Addons / Lights", "content": "Page: three.js docs\nSection: Addons / Lights\n\nLightProbeGenerator, RectAreaLightTexturesLib, RectAreaLightUniformsLib", "code_blocks": [], "chunk_position": 1, "heading_path": "Addons / Lights > Addons / Lights", "breadcrumbs": "three.js docs > Addons / Lights > Addons / Lights"}, {"id": "fcc489c5e3f33c8a", "url": "https://threejs.org/docs", "page_title": "three.js docs", "page_type": "overview", "page_summary": "The main documentation index for three.js, a JavaScript 3D library. This page provides a categorized listing of all classes, modules, and functions available in the Core library, Addons, and the Three Shading Language (TSL).", "heading": "Addons / Lines", "content": "Page: three.js docs\nSection: Addons / Lines\n\nLine2, LineGeometry, LineMaterial, LineSegments2, LineSegmentsGeometry, Wireframe, WireframeGeometry2", "code_blocks": [], "chunk_position": 1, "heading_path": "Addons / Lines > Addons / Lines", "breadcrumbs": "three.js docs > Addons / Lines > Addons / Lines"}, {"id": "7658dd02f1083435", "url": "https://threejs.org/docs", "page_title": "three.js docs", "page_type": "overview", "page_summary": "The main documentation index for three.js, a JavaScript 3D library. This page provides a categorized listing of all classes, modules, and functions available in the Core library, Addons, and the Three Shading Language (TSL).", "heading": "Addons / Loaders", "content": "Page: three.js docs\nSection: Addons / Loaders\n\nAMFLoader, BVHLoader, ColladaComposer, ColladaLoader, ColladaParser, DDSLoader, DRACOLoader, EXRLoader, FBXLoader, Font, FontLoader, GCodeLoader, GLTFLoader, HDRCubeTextureLoader, HDRLoader, IESLoader, KMZLoader, KTX2Loader, KTXLoader, LDrawLoader, LUT3dlLoader, LUTCubeLoader, LUTImageLoader, LWOLoader, LottieLoader, MD2Loader, MDDLoader, MTLLoader, MaterialXLoader, NRRDLoader, OBJLoader, PCDLoader, PDBLoader, PLYLoader, PVRLoader, Rhino3dmLoader, STLLoader, SVGLoader, TDSLoader, TGALoader, TIFFLoader, TTFLoader, ThreeMFLoader, USDComposer, USDLoader, UltraHDRLoader, VOXLoader, VRMLLoader, VTKLoader, XYZLoader", "code_blocks": [], "chunk_position": 1, "heading_path": "Addons / Loaders > Addons / Loaders", "breadcrumbs": "three.js docs > Addons / Loaders > Addons / Loaders"}, {"id": "8525a0cdd9bbeb06", "url": "https://threejs.org/docs", "page_title": "three.js docs", "page_type": "overview", "page_summary": "The main documentation index for three.js, a JavaScript 3D library. This page provides a categorized listing of all classes, modules, and functions available in the Core library, Addons, and the Three Shading Language (TSL).", "heading": "Addons / Materials", "content": "Page: three.js docs\nSection: Addons / Materials\n\nLDrawConditionalLineMaterial, WoodNodeMaterial", "code_blocks": [], "chunk_position": 1, "heading_path": "Addons / Materials > Addons / Materials", "breadcrumbs": "three.js docs > Addons / Materials > Addons / Materials"}, {"id": "3c969213b11add8d", "url": "https://threejs.org/docs", "page_title": "three.js docs", "page_type": "overview", "page_summary": "The main documentation index for three.js, a JavaScript 3D library. This page provides a categorized listing of all classes, modules, and functions available in the Core library, Addons, and the Three Shading Language (TSL).", "heading": "Addons / Math", "content": "Page: three.js docs\nSection: Addons / Math\n\nCapsule, ColorConverter, ConvexHull, ImprovedNoise, Lut, MeshSurfaceSampler, OBB, Octree, SimplexNoise, ColorSpaces", "code_blocks": [], "chunk_position": 1, "heading_path": "Addons / Math > Addons / Math", "breadcrumbs": "three.js docs > Addons / Math > Addons / Math"}, {"id": "12361ee2d7589eec", "url": "https://threejs.org/docs", "page_title": "three.js docs", "page_type": "overview", "page_summary": "The main documentation index for three.js, a JavaScript 3D library. This page provides a categorized listing of all classes, modules, and functions available in the Core library, Addons, and the Three Shading Language (TSL).", "heading": "Addons / Misc", "content": "Page: three.js docs\nSection: Addons / Misc\n\nConvexObjectBreaker, GPUComputationRenderer, Gyroscope, MD2Character, MD2CharacterComplex, MorphAnimMesh, MorphBlendMesh, ProgressiveLightMap, RollerCoasterGeometry, RollerCoasterLiftersGeometry, RollerCoasterShadowGeometry, SkyGeometry, TileCreasedNormalsPlugin, TreesGeometry, TubePainter, Volume, VolumeSlice", "code_blocks": [], "chunk_position": 1, "heading_path": "Addons / Misc > Addons / Misc", "breadcrumbs": "three.js docs > Addons / Misc > Addons / Misc"}, {"id": "ce09195c8f7924e3", "url": "https://threejs.org/docs", "page_title": "three.js docs", "page_type": "overview", "page_summary": "The main documentation index for three.js, a JavaScript 3D library. This page provides a categorized listing of all classes, modules, and functions available in the Core library, Addons, and the Three Shading Language (TSL).", "heading": "Addons / Modifiers", "content": "Page: three.js docs\nSection: Addons / Modifiers\n\nEdgeSplitModifier, Flow, InstancedFlow, SimplifyModifier, TessellateModifier", "code_blocks": [], "chunk_position": 1, "heading_path": "Addons / Modifiers > Addons / Modifiers", "breadcrumbs": "three.js docs > Addons / Modifiers > Addons / Modifiers"}, {"id": "f8202403d6851b16", "url": "https://threejs.org/docs", "page_title": "three.js docs", "page_type": "overview", "page_summary": "The main documentation index for three.js, a JavaScript 3D library. This page provides a categorized listing of all classes, modules, and functions available in the Core library, Addons, and the Three Shading Language (TSL).", "heading": "Addons / Objects", "content": "Page: three.js docs\nSection: Addons / Objects\n\nGroundedSkybox, Lensflare, LensflareElement, LensflareMesh, MarchingCubes, Reflector, ReflectorForSSRPass, Refractor, ShadowMesh, Sky, SkyMesh, Water, WaterMesh", "code_blocks": [], "chunk_position": 1, "heading_path": "Addons / Objects > Addons / Objects", "breadcrumbs": "three.js docs > Addons / Objects > Addons / Objects"}, {"id": "0e47a3884280aa5e", "url": "https://threejs.org/docs", "page_title": "three.js docs", "page_type": "overview", "page_summary": "The main documentation index for three.js, a JavaScript 3D library. This page provides a categorized listing of all classes, modules, and functions available in the Core library, Addons, and the Three Shading Language (TSL).", "heading": "Addons / Physics", "content": "Page: three.js docs\nSection: Addons / Physics\n\nAmmoPhysics, JoltPhysics, RapierPhysics", "code_blocks": [], "chunk_position": 1, "heading_path": "Addons / Physics > Addons / Physics", "breadcrumbs": "three.js docs > Addons / Physics > Addons / Physics"}, {"id": "db046c05e8a11eb1", "url": "https://threejs.org/docs", "page_title": "three.js docs", "page_type": "overview", "page_summary": "The main documentation index for three.js, a JavaScript 3D library. This page provides a categorized listing of all classes, modules, and functions available in the Core library, Addons, and the Three Shading Language (TSL).", "heading": "Addons / Postprocessing", "content": "Page: three.js docs\nSection: Addons / Postprocessing\n\nAfterimagePass, BloomPass, BokehPass, ClearMaskPass, ClearPass, CubeTexturePass, DotScreenPass, EffectComposer, FXAAPass, FilmPass, FullScreenQuad, GTAOPass, GlitchPass, HalftonePass, LUTPass, MaskPass, OutlinePass, OutputPass, Pass, RenderPass, RenderPixelatedPass, RenderTransitionPass, SAOPass, SMAAPass, SSAARenderPass, SSAOPass, SSRPass, SavePass, ShaderPass, TAARenderPass, TexturePass, UnrealBloomPass", "code_blocks": [], "chunk_position": 1, "heading_path": "Addons / Postprocessing > Addons / Postprocessing", "breadcrumbs": "three.js docs > Addons / Postprocessing > Addons / Postprocessing"}, {"id": "0d954482dce7f586", "url": "https://threejs.org/docs", "page_title": "three.js docs", "page_type": "overview", "page_summary": "The main documentation index for three.js, a JavaScript 3D library. This page provides a categorized listing of all classes, modules, and functions available in the Core library, Addons, and the Three Shading Language (TSL).", "heading": "Addons / Renderers", "content": "Page: three.js docs\nSection: Addons / Renderers\n\nCSS2DObject, CSS2DRenderer, CSS3DObject, CSS3DRenderer, CSS3DSprite, Projector, SVGObject, SVGRenderer", "code_blocks": [], "chunk_position": 1, "heading_path": "Addons / Renderers > Addons / Renderers", "breadcrumbs": "three.js docs > Addons / Renderers > Addons / Renderers"}, {"id": "30cf59c1744abec0", "url": "https://threejs.org/docs", "page_title": "three.js docs", "page_type": "overview", "page_summary": "The main documentation index for three.js, a JavaScript 3D library. This page provides a categorized listing of all classes, modules, and functions available in the Core library, Addons, and the Three Shading Language (TSL).", "heading": "Addons / Shaders", "content": "Page: three.js docs\nSection: Addons / Shaders\n\nACESFilmicToneMappingShader, AfterimageShader, BasicShader, BleachBypassShader, BlendShader, BokehShader, BokehShader2, BrightnessContrastShader, ColorCorrectionShader, ColorifyShader, ConvolutionShader, CopyShader, DOFMipMapShader, DepthLimitedBlurShader, DigitalGlitch, DotScreenShader, ExposureShader, FXAAShader, FilmShader, FocusShader, FreiChenShader, GTAOShader, GammaCorrectionShader, HalftoneShader, HorizontalBlurShader, HorizontalTiltShiftShader, HueSaturationShader, KaleidoShader, LuminosityHighPassShader, LuminosityShader, MirrorShader, NormalMapShader, OutputShader, PoissonDenoiseShader, RGBShiftShader, SAOShader, SMAAShader, SSAOShader, SSRShader, SepiaShader, SobelOperatorShader, SubsurfaceScatteringShader, TriangleBlurShader, UnpackDepthRGBAShader, VelocityShader, VerticalBlurShader, VerticalTiltShiftShader, VignetteShader, VolumeShader, WaterRefractionShader", "code_blocks": [], "chunk_position": 1, "heading_path": "Addons / Shaders > Addons / Shaders", "breadcrumbs": "three.js docs > Addons / Shaders > Addons / Shaders"}, {"id": "9d268e781d414591", "url": "https://threejs.org/docs", "page_title": "three.js docs", "page_type": "overview", "page_summary": "The main documentation index for three.js, a JavaScript 3D library. This page provides a categorized listing of all classes, modules, and functions available in the Core library, Addons, and the Three Shading Language (TSL).", "heading": "Addons / TSL", "content": "Page: three.js docs\nSection: Addons / TSL\n\nAfterImageNode, AmbientLightDataNode, AnaglyphPassNode, BilateralBlurNode, BloomNode, ChromaticAberrationNode, ClusteredLightsNode, DenoiseNode, DepthOfFieldNode, DirectionalLightDataNode, DotScreenNode, DynamicLightsNode, EnvMapCDFGenerator, FSR1Node, FXAANode, FilmNode, GTAONode, GaussianBlurNode, GodraysNode, HemisphereLightDataNode, ImportanceSampledEnvironment, LensflareNode, Lut3DNode, OutlineNode, ParallaxBarrierPassNode, PixelationNode, PixelationPassNode, PointLightDataNode, RGBShiftNode, RecurrentDenoiseNode, RetroPassNode, SMAANode, SSAAPassNode, SSGINode, SSRNode, SSSNode, SharpenNode, SobelOperatorNode, SpotLightDataNode, StereoCompositePassNode, StereoPassNode, TAAUNode, TRAANode, TemporalReprojectNode, TileShadowNode, TileShadowNodeHelper, TransitionNode, WebGLNodesHandler, Bayer, GroundedSkybox, Raymarching", "code_blocks": [], "chunk_position": 1, "heading_path": "Addons / TSL > Addons / TSL", "breadcrumbs": "three.js docs > Addons / TSL > Addons / TSL"}, {"id": "401456782dc1592f", "url": "https://threejs.org/docs", "page_title": "three.js docs", "page_type": "overview", "page_summary": "The main documentation index for three.js, a JavaScript 3D library. This page provides a categorized listing of all classes, modules, and functions available in the Core library, Addons, and the Three Shading Language (TSL).", "heading": "Addons / Textures", "content": "Page: three.js docs\nSection: Addons / Textures\n\nFlakesTexture", "code_blocks": [], "chunk_position": 1, "heading_path": "Addons / Textures > Addons / Textures", "breadcrumbs": "three.js docs > Addons / Textures > Addons / Textures"}, {"id": "7e768704716a2b37", "url": "https://threejs.org/docs", "page_title": "three.js docs", "page_type": "overview", "page_summary": "The main documentation index for three.js, a JavaScript 3D library. This page provides a categorized listing of all classes, modules, and functions available in the Core library, Addons, and the Three Shading Language (TSL).", "heading": "Addons / Transpiler", "content": "Page: three.js docs\nSection: Addons / Transpiler\n\nTranspiler", "code_blocks": [], "chunk_position": 1, "heading_path": "Addons / Transpiler > Addons / Transpiler", "breadcrumbs": "three.js docs > Addons / Transpiler > Addons / Transpiler"}, {"id": "dda07573a12f2d0f", "url": "https://threejs.org/docs", "page_title": "three.js docs", "page_type": "overview", "page_summary": "The main documentation index for three.js, a JavaScript 3D library. This page provides a categorized listing of all classes, modules, and functions available in the Core library, Addons, and the Three Shading Language (TSL).", "heading": "Addons / Utils", "content": "Page: three.js docs\nSection: Addons / Utils\n\nLDrawUtils, SceneOptimizer, ShadowMapViewer, WorkerPool, BufferGeometryUtils, CameraUtils, ColorUtils, GeometryCompressionUtils, GeometryUtils, SceneUtils, SkeletonUtils, SortUtils, UVsDebug, WebGLTextureUtils, WebGPUTextureUtils", "code_blocks": [], "chunk_position": 1, "heading_path": "Addons / Utils > Addons / Utils", "breadcrumbs": "three.js docs > Addons / Utils > Addons / Utils"}, {"id": "9bb5ccb003d2e361", "url": "https://threejs.org/docs", "page_title": "three.js docs", "page_type": "overview", "page_summary": "The main documentation index for three.js, a JavaScript 3D library. This page provides a categorized listing of all classes, modules, and functions available in the Core library, Addons, and the Three Shading Language (TSL).", "heading": "Addons / Webxr", "content": "Page: three.js docs\nSection: Addons / Webxr\n\nARButton, OculusHandModel, OculusHandPointerModel, VRButton, XRButton, XRControllerModel, XRControllerModelFactory, XREstimatedLight, XRHandMeshModel, XRHandModel, XRHandModelFactory, XRHandPrimitiveModel, XRPlanes, Text2D", "code_blocks": [], "chunk_position": 1, "heading_path": "Addons / Webxr > Addons / Webxr", "breadcrumbs": "three.js docs > Addons / Webxr > Addons / Webxr"}, {"id": "2c759cc89a988dca", "url": "https://threejs.org/docs", "page_title": "three.js docs", "page_type": "overview", "page_summary": "The main documentation index for three.js, a JavaScript 3D library. This page provides a categorized listing of all classes, modules, and functions available in the Core library, Addons, and the Three Shading Language (TSL).", "heading": "TSL", "content": "Page: three.js docs\nSection: TSL\n\nThe Three Shading Language (TSL) is a JavaScript-based shading language for three.js. It provides functions for building shader graphs and manipulating nodes in a programmatic way. TSL entries include control flow functions, math operations, lighting models, texture sampling, geometry access, and screen-space effects. Key entries include: Break, Const, Continue, Discard, EPSILON, HALF_PI, INFINITY, If, Loop, PI, PI2, Return, Switch, TBNViewMatrix, TWO_PI, Var, VarIntent, abs, acesFilmicToneMapping, acos, acosh, add, afterImage, agxToneMapping, all, alphaLine, alphaT, ambientOcclusion, anaglyphPass, and, anisotropy, anisotropyB, anisotropyT, any, ao, append, applyVarianceClipping, array, asin, asinh, assign, atan, atanh, atomicAdd, atomicAnd, atomicFunc, atomicLoad, atomicMax, atomicMin, atomicNode, atomicOr, atomicStore, atomicSub, atomicXor, attenuationColor, attenuationDistance, attribute, attributeArray, backgroundBlurriness, backgroundIntensity, backgroundRotation, barrelMask, barrelUV, barrier, batch, beautyTexelFromScreen, bentNormalView, bilateralBlur, billboarding, bitAnd, bitNot, bitOr, bitXor, bitangentGeometry, bitangentLocal, bitangentView, bitangentViewFrame, bitangentWorld, bitcast, bleach, blendBurn, blendColor, blendDodge, blendOverlay, blendScreen, bloom, boxBlur, buffer, bufferAttribute, builtin, builtinAOContext, builtinShadowContext, bumpMap, bypass, cache, cameraFar, cameraIndex, cameraNear, cameraNormalMatrix, cameraPosition, cameraProjectionMatrix, cameraProjectionMatrixInverse, cameraViewMatrix, cameraViewport, cameraWorldMatrix, cbrt, cdl, ceil, checker, chromaticAberration, cineonToneMapping, circle, clamp, clearcoat, clearcoatNormalView, clearcoatRoughness, clipSpace, clipToAABB, clipping, clippingAlpha, clusteredLights, code, collectNeighborhood, colorBleeding, colorSpaceToWorking, colorToDirection, compute, computeBuiltin, computeFrustumSize, computeHitDistFactor, computeKernel, computeSkinning, context, convertColorSpace, convertToTexture, cos, cosh, countLeadingZeros, countOneBits, countTrailingZeros, createVar, cross, cubeMapNode, cubeTexture, cubeTextureBase, curlNoise, dFdx, dFdy, dashSize, debug, decrement, decrementBefore, degrees, deltaTime, denoise, densityFogFactor, depth, depthBase, depthPass, determinant, difference, diffuseColor, diffuseColorDistance, diffuseContribution, directionToColor, directionToFaceDirection, dispersion, distance, div, dof, dot, dotScreen, drawIndex, dynamicBufferAttribute, dynamicLights, emissive, equal, equirectDirection, equirectUV, exp, exp2, exponentialHeightFogFactor, expression, faceDirection, faceForward, film, floatBitsToInt, floatBitsToUint, floor, fog, fract, frameGroup, frameId, frontFacing, fsr1, fwidth, fxaa, gain, gapSize, gaussianBlur, getNormalFromDepth, getParallaxCorrectNormal, getScreenPosition, getShadowMaterial, getShadowRenderObjectFunction, getSpecularDominantDirection, getTemporalVarianceFactor, getViewPosition, globalId, glsl, godrays, grayscale, greaterThan, greaterThanEqual, hardwareClipping, hash, hashBlur, highpModelNormalViewMatrix, highpModelViewMatrix, hue, increment, incrementBefore, inspector, instance, instanceIndex, instancedArray, instancedBufferAttribute, instancedDynamicBufferAttribute, instancedMesh, intBitsToFloat, interleavedGradientNoise, inverse, inverseSqrt, invocationLocalIndex, invocationSubgroupIndex, ior, iridescence, iridescenceIOR, iridescenceThickness, isolate, js, karisTemporalBlend, label, length, lengthSq, lensflare, lessThan, lessThanEqual, lightPosition, lightProjectionUV, lightShadowMatrix, lightTargetDirection, lightTargetPosition, lightViewPosition, lights, linearDepth, linearToneMapping, lobeNormalFalloff, lobeNormalWeight", "code_blocks": [], "chunk_position": 1, "heading_path": "TSL > TSL", "breadcrumbs": "three.js docs > TSL > TSL"}, {"id": "7e5191d5dacbc1db", "url": "https://threejs.org/manual/en/installation.html", "page_title": "Installation", "page_type": "guide", "page_summary": "This page covers how to set up a three.js project, including the basic project structure and two installation methods: installing via NPM with a build tool, or importing from a CDN.", "heading": "Project structure", "content": "Page: Installation\nSection: Project structure\n\nEvery 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\n- *index.html*\n\n- *main.js*\n\n- *public/*\n\nThe *public/* 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\nNow 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.", "code_blocks": [{"language": "html", "code": "<!DOCTYPE html>\n<html lang=\"en\">\n  <head>\n    <meta charset=\"utf-8\">\n    <title>My first three.js app</title>\n    <style>\n      body { margin: 0; }\n    </style>\n  </head>\n  <body>\n    <script type=\"module\" src=\"/main.js\"></script>\n  </body>\n</html>", "filename": "index.html"}, {"language": "javascript", "code": "import * as THREE from 'three';\n\n...", "filename": "main.js"}], "chunk_position": 2, "heading_path": "Project structure > Project structure", "breadcrumbs": "Installation > Project structure > Project structure"}, {"id": "5b4861718b9c27d8", "url": "https://threejs.org/manual/en/installation.html", "page_title": "Installation", "page_type": "guide", "page_summary": "This page covers how to set up a three.js project, including the basic project structure and two installation methods: installing via NPM with a build tool, or importing from a CDN.", "heading": "Option 1: Install with NPM and a build tool", "content": "Page: Installation\nSection: Option 1: Install with NPM and a build tool\n\nInstalling from the npm package registry and using a build tool 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\nSteps:\n- Install Node.js. We'll need it to manage dependencies and to run our build tool.\n- Install three.js and a build tool, Vite, using a terminal 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 ES Modules.\n\nInstallation added *node_modules/* and *package.json* to my project. What are they?\n\nnpm uses *package.json* 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 *npm install*. If you're using version history, commit *package.json*.\n\nnpm installs the code for each dependency in a new *node_modules/* folder. When Vite builds your application, it sees imports for 'three' and pulls three.js files automatically from this folder. The *node_modules/* folder is used only during development, and shouldn't be uploaded to your web hosting provider or committed to version history.\n\nUsing three.js with TypeScript: Community-maintained TypeScript type definitions for three.js are available at three-types/three-ts-types.\n\nFrom your terminal, run:\n\nWhat is *npx*? 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 *node_modules/* yourself. If you prefer, you can put Vite's common commands into the package.json:scripts list, and use *npm run dev* instead.\n\nIf everything went well, you'll see a URL like *http://localhost:5173* appear in your terminal, and can open that URL to see your web application.\n\nThe page will be blank — you're ready to create a scene.\n\nIf you want to learn more about these tools before you continue, see:\n- three.js journey: Local Server\n- Vite: Command Line Interface\n- MDN: Package management basics\n\nLater, when you're ready to deploy your web application, you'll just need to tell Vite to run a production build — *npx vite build*. Everything used by the application will be compiled, optimized, and copied into the *dist/* folder. The contents of that folder are ready to be hosted on your website.", "code_blocks": [{"language": "shell", "code": "# three.js\nnpm install --save three\n\n# vite\nnpm install --save-dev vite", "filename": ""}, {"language": "shell", "code": "npx vite", "filename": ""}, {"language": "shell", "code": "npx vite build", "filename": ""}], "chunk_position": 2, "heading_path": "Option 1: Install with NPM and a build tool > Option 1: Install with NPM and a build tool", "breadcrumbs": "Installation > Option 1: Install with NPM and a build tool > Option 1: Install with NPM and a build tool"}, {"id": "38809905abef7dbc", "url": "https://threejs.org/manual/en/installation.html", "page_title": "Installation", "page_type": "guide", "page_summary": "This page covers how to set up a three.js project, including the basic project structure and two installation methods: installing via NPM with a build tool, or importing from a CDN.", "heading": "Option 2: Import from a CDN", "content": "Page: Installation\nSection: Option 2: Import from a CDN\n\nInstalling without build tools will require some changes to the project structure given above.\n\nWe imported code from 'three' (an npm package) in *main.js*, and web browsers don't know what that means. In *index.html* we'll need to add an import map defining where to get the package. Put the code below inside the  tag, after the styles.\n\nDon't forget to replace *<version>* with an actual version of three.js, like *\"v0.149.0\"*. The most recent version can be found on the npm version list.\n\nWe'll also need to run a *local server* 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\nInstall Node.js, then run serve to start a local server in the project's directory:\n\nIf 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\nThe page will be blank — you're ready to create a scene.\n\nMany 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\nMore local servers:\n\nCommand Line: Command line local servers run from a terminal window. The associated programming language may need to be installed first.\n- *npx http-server* (Node.js)\n- *npx five-server* (Node.js)\n- *python -m SimpleHTTPServer* (Python 2.x)\n- *python -m http.server* (Python 3.x)\n- *php -S localhost:8000* (PHP 5.4+)\n\nGUI: GUI local servers run as an application window on your computer, and may have a user interface.\n- Servez\n\nCode Editor Plugins: Some code editors have plugins that spawn a simple server on demand.\n- Five Server for Visual Studio Code\n- Live Server for Visual Studio Code\n- Live Server for Atom\n\nWhen 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\n**IMPORTANT:** 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.", "code_blocks": [{"language": "html", "code": "<script type=\"importmap\">\n{\n  \"imports\": {\n    \"three\": \"https://cdn.jsdelivr.net/npm/three@<version>/build/three.module.js\",\n    \"three/addons/\": \"https://cdn.jsdelivr.net/npm/three@<version>/examples/jsm/\"\n  }\n}\n</script>", "filename": "index.html"}, {"language": "shell", "code": "npx serve .", "filename": ""}], "chunk_position": 2, "heading_path": "Option 2: Import from a CDN > Option 2: Import from a CDN", "breadcrumbs": "Installation > Option 2: Import from a CDN > Option 2: Import from a CDN"}, {"id": "d1f50856e46cc1a0", "url": "https://threejs.org/manual/en/installation.html", "page_title": "Installation", "page_type": "guide", "page_summary": "This page covers how to set up a three.js project, including the basic project structure and two installation methods: installing via NPM with a build tool, or importing from a CDN.", "heading": "Addons", "content": "Page: Installation\nSection: Addons\n\nOut 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 addons/ directory. Addons do not need to be *installed* separately, but do need to be *imported* separately.\n\nThe example below shows how to import three.js with the `OrbitControls` and `GLTFLoader` addons. Where necessary, this will also be mentioned in each addon's documentation or examples.\n\nSome excellent third-party projects are available for three.js, too. These need to be installed separately — see Libraries and Plugins.", "code_blocks": [{"language": "javascript", "code": "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();", "filename": "main.js"}], "chunk_position": 2, "heading_path": "Addons > Addons", "breadcrumbs": "Installation > Addons > Addons"}, {"id": "ba6dc006d37cd7fa", "url": "https://threejs.org/manual/en/installation.html", "page_title": "Installation", "page_type": "guide", "page_summary": "This page covers how to set up a three.js project, including the basic project structure and two installation methods: installing via NPM with a build tool, or importing from a CDN.", "heading": "Next Steps", "content": "Page: Installation\nSection: Next Steps\n\nYou're now ready to create a scene.", "code_blocks": [], "chunk_position": 2, "heading_path": "Next Steps > Next Steps", "breadcrumbs": "Installation > Next Steps > Next Steps"}, {"id": "cae6b68bc5f21c6f", "url": "https://threejs.org/manual/en/creating-a-scene.html", "page_title": "Creating a scene", "page_type": "guide", "page_summary": "A brief introduction to three.js that walks through setting up a scene with a spinning cube, covering scene/camera/renderer setup, geometry, materials, meshes, and animation loops.", "heading": "Before we start", "content": "Page: Creating a scene\nSection: Before we start\n\nThe 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.\n\nIf you haven't yet, go through the `Installation` guide. We'll assume you've already set up the same project structure (including *index.html* and *main.js* ), have installed three.js, and are either running a build tool, or using a local server with a CDN and import maps.", "code_blocks": [], "chunk_position": 3, "heading_path": "Before we start > Before we start", "breadcrumbs": "Creating a scene > Before we start > Before we start"}, {"id": "f3afafa232a9b71f", "url": "https://threejs.org/manual/en/creating-a-scene.html", "page_title": "Creating a scene", "page_type": "guide", "page_summary": "A brief introduction to three.js that walks through setting up a scene with a spinning cube, covering scene/camera/renderer setup, geometry, materials, meshes, and animation loops.", "heading": "Creating the scene", "content": "Page: Creating a scene\nSection: Creating the scene\n\nTo 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.\n\nLet's take a moment to explain what's going on here. We have now set up the scene, our camera and the renderer.\n\nThere are a few different cameras in three.js. For now, let's use a `PerspectiveCamera`.\n\nThe first attribute is the `field of view`. FOV is the extent of the scene that is seen on the display at any given moment. The value is in degrees.\n\nThe second one is the `aspect ratio`. 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.\n\nThe next two attributes are the `near` and `far` clipping plane. What that means, is that objects further away from the camera than the value of `far` or closer than `near` 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.\n\nNext 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 `setSize` smaller values, like `window.innerWidth/2` and `window.innerHeight/2`, which will make the app render at quarter size.\n\nIf you wish to keep the size of your app but render it at a lower resolution, you can do so by calling `setSize` with false as `updateStyle` (the third argument). For example, `setSize(window.innerWidth/2, window.innerHeight/2, false)` will render your app at half resolution, given that your <canvas> has 100% width and height.\n\nLast but not least, we add the `renderer` element to our HTML document. This is a <canvas> element the renderer uses to display the scene to us.\n\n\"That's all good, but where's that cube you promised?\" Let's add it now.\n\nTo create a cube, we need a `BoxGeometry`. This is an object that contains all the points (`vertices`) and fill (`faces`) of the cube. We'll explore this more in the future.\n\nIn addition to the geometry, we need a material to color it. Three.js comes with several materials, but we'll stick to the `MeshBasicMaterial` 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 `0x00ff00`, which is green. This works the same way that colors work in CSS or Photoshop (`hex colors`).\n\nThe third thing we need is a `Mesh`. 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.\n\nBy default, when we call `scene.add()`, the thing we add will be added to the coordinates `(0,0,0)`. 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.", "code_blocks": [{"language": "javascript", "code": "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 );", "filename": "main.js"}, {"language": "javascript", "code": "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;", "filename": ""}], "chunk_position": 3, "heading_path": "Creating the scene > Creating the scene", "breadcrumbs": "Creating a scene > Creating the scene > Creating the scene"}, {"id": "1e636dc489cdf0c1", "url": "https://threejs.org/manual/en/creating-a-scene.html", "page_title": "Creating a scene", "page_type": "guide", "page_summary": "A brief introduction to three.js that walks through setting up a scene with a spinning cube, covering scene/camera/renderer setup, geometry, materials, meshes, and animation loops.", "heading": "Rendering the scene", "content": "Page: Creating a scene\nSection: Rendering the scene\n\nIf 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.\n\nThis 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 \"why don't we just create a setInterval ?\" The thing is - we could, but `requestAnimationFrame` which is internally used in `WebGLRenderer` 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.", "code_blocks": [{"language": "javascript", "code": "function animate( time ) {\n  renderer.render( scene, camera );\n}\nrenderer.setAnimationLoop( animate );", "filename": ""}], "chunk_position": 3, "heading_path": "Rendering the scene > Rendering the scene", "breadcrumbs": "Creating a scene > Rendering the scene > Rendering the scene"}, {"id": "20597282493187ae", "url": "https://threejs.org/manual/en/creating-a-scene.html", "page_title": "Creating a scene", "page_type": "guide", "page_summary": "A brief introduction to three.js that walks through setting up a scene with a spinning cube, covering scene/camera/renderer setup, geometry, materials, meshes, and animation loops.", "heading": "Animating the cube", "content": "Page: Creating a scene\nSection: Animating the cube\n\nIf 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.\n\nAdd the following code right above the `renderer.render` call in your `animate` function:\n\nThis 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 `animate` function that's hundreds of lines.", "code_blocks": [{"language": "javascript", "code": "cube.rotation.x = time / 2000;\ncube.rotation.y = time / 1000;", "filename": ""}], "chunk_position": 3, "heading_path": "Animating the cube > Animating the cube", "breadcrumbs": "Creating a scene > Animating the cube > Animating the cube"}, {"id": "6d1dcc0845f9fe31", "url": "https://threejs.org/manual/en/creating-a-scene.html", "page_title": "Creating a scene", "page_type": "guide", "page_summary": "A brief introduction to three.js that walks through setting up a scene with a spinning cube, covering scene/camera/renderer setup, geometry, materials, meshes, and animation loops.", "heading": "The result", "content": "Page: Creating a scene\nSection: The result\n\nCongratulations! You have now completed your first three.js application. It's simple, but you have to start somewhere.\n\nThe full code is available below and as an editable live example. Play around with it to get a better understanding of how it works.", "code_blocks": [{"language": "html", "code": "<!DOCTYPE html>\n<html lang=\"en\">\n  <head>\n    <meta charset=\"utf-8\">\n    <title>My first three.js app</title>\n    <style>\n      body { margin: 0; }\n    </style>\n  </head>\n  <body>\n    <script type=\"module\" src=\"/main.js\"></script>\n  </body>\n</html>", "filename": "index.html"}, {"language": "javascript", "code": "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}", "filename": "main.js"}], "chunk_position": 3, "heading_path": "The result > The result", "breadcrumbs": "Creating a scene > The result > The result"}, {"id": "7bed4d2834e3fef6", "url": "https://threejs.org/manual/en/creating-text.html", "page_title": "Creating Text", "page_type": "guide", "page_summary": "A guide covering multiple approaches to adding and rendering text in a three.js application, from simple DOM overlays to procedural 3D text geometries, bitmap fonts, and Troika Text.", "heading": "1. DOM + CSS", "content": "Page: Creating Text\nSection: 1. DOM + CSS\n\nUsing HTML is generally the easiest and fastest manner to add text. This is the method used for descriptive overlays in most three.js examples.\n\nYou can add content to a\n\nand use CSS markup to position absolutely at a position above all others with a z-index especially if you are running three.js full screen.", "code_blocks": [{"language": "html", "code": "<div id=\"info\">Description</div>", "filename": ""}, {"language": "css", "code": "#info {\n  position: absolute;\n  top: 10px;\n  width: 100%;\n  text-align: center;\n  z-index: 100;\n  display:block;\n}", "filename": ""}], "chunk_position": 4, "heading_path": "1. DOM + CSS > 1. DOM + CSS", "breadcrumbs": "Creating Text > 1. DOM + CSS > 1. DOM + CSS"}, {"id": "f9223a5bbb7854a9", "url": "https://threejs.org/manual/en/creating-text.html", "page_title": "Creating Text", "page_type": "guide", "page_summary": "A guide covering multiple approaches to adding and rendering text in a three.js application, from simple DOM overlays to procedural 3D text geometries, bitmap fonts, and Troika Text.", "heading": "2. Use `CSS2DRenderer` or `CSS3DRenderer`", "content": "Page: Creating Text\nSection: 2. Use `CSS2DRenderer` or `CSS3DRenderer`\n\nUse these renderers to draw high-quality text contained in DOM elements to your three.js scene. This is similar to 1. except that with these renderers elements can be integrated more tightly and dynamically into the scene.", "code_blocks": [], "chunk_position": 4, "heading_path": "2. Use `CSS2DRenderer` or `CSS3DRenderer` > 2. Use `CSS2DRenderer` or `CSS3DRenderer`", "breadcrumbs": "Creating Text > 2. Use `CSS2DRenderer` or `CSS3DRenderer` > 2. Use `CSS2DRenderer` or `CSS3DRenderer`"}, {"id": "7b3c1ef0286c0e90", "url": "https://threejs.org/manual/en/creating-text.html", "page_title": "Creating Text", "page_type": "guide", "page_summary": "A guide covering multiple approaches to adding and rendering text in a three.js application, from simple DOM overlays to procedural 3D text geometries, bitmap fonts, and Troika Text.", "heading": "3. Draw text to canvas and use as a `Texture`", "content": "Page: Creating Text\nSection: 3. Draw text to canvas and use as a `Texture`\n\nUse this method if you wish to draw text easily on a plane in your three.js scene.", "code_blocks": [], "chunk_position": 4, "heading_path": "3. Draw text to canvas and use as a `Texture` > 3. Draw text to canvas and use as a `Texture`", "breadcrumbs": "Creating Text > 3. Draw text to canvas and use as a `Texture` > 3. Draw text to canvas and use as a `Texture`"}, {"id": "883cfbe715d8e3eb", "url": "https://threejs.org/manual/en/creating-text.html", "page_title": "Creating Text", "page_type": "guide", "page_summary": "A guide covering multiple approaches to adding and rendering text in a three.js application, from simple DOM overlays to procedural 3D text geometries, bitmap fonts, and Troika Text.", "heading": "4. Create a model in your favourite 3D application and export to three.js", "content": "Page: Creating Text\nSection: 4. Create a model in your favourite 3D application and export to three.js\n\nUse this method if you prefer working with your 3d applications and importing the models to three.js.", "code_blocks": [], "chunk_position": 4, "heading_path": "4. Create a model in your favourite 3D application and export to three.js > 4. Create a model in your favourite 3D application and export to three.js", "breadcrumbs": "Creating Text > 4. Create a model in your favourite 3D application and export to three.js > 4. Create a model in your favourite 3D application and export to three.js"}, {"id": "6c5a386d71c5e3eb", "url": "https://threejs.org/manual/en/creating-text.html", "page_title": "Creating Text", "page_type": "guide", "page_summary": "A guide covering multiple approaches to adding and rendering text in a three.js application, from simple DOM overlays to procedural 3D text geometries, bitmap fonts, and Troika Text.", "heading": "5. Procedural Text Geometry", "content": "Page: Creating Text\nSection: 5. Procedural Text Geometry\n\nIf you prefer to work purely in THREE.js or to create procedural and dynamic 3D text geometries, you can create a mesh whose geometry is an instance of THREE.TextGeometry:\n\n`new THREE.TextGeometry( text, parameters );`\n\nIn order for this to work, however, your TextGeometry will need an instance of THREE.Font to be set on its \"font\" parameter.\n\nSee the `TextGeometry` page for more info on how this can be done, descriptions of each accepted parameter, and a list of the JSON fonts that come with the THREE.js distribution itself.", "code_blocks": [], "chunk_position": 4, "heading_path": "5. Procedural Text Geometry > 5. Procedural Text Geometry", "breadcrumbs": "Creating Text > 5. Procedural Text Geometry > 5. Procedural Text Geometry"}, {"id": "fcd526119c48c89a", "url": "https://threejs.org/manual/en/creating-text.html", "page_title": "Creating Text", "page_type": "guide", "page_summary": "A guide covering multiple approaches to adding and rendering text in a three.js application, from simple DOM overlays to procedural 3D text geometries, bitmap fonts, and Troika Text.", "heading": "Examples", "content": "Page: Creating Text\nSection: Examples\n\nWebGL / geometry / text\n\nWebGL / shadowmap\n\nIf Typeface is down, or you want to use a font that is not there, there's a tutorial with a python script for blender that allows you to export text to Three.js's JSON format: http://www.jaanga.com/2012/03/blender-to-threejs-create-3d-text-with.html", "code_blocks": [], "chunk_position": 4, "heading_path": "Examples > Examples", "breadcrumbs": "Creating Text > Examples > Examples"}, {"id": "24968ba504946fd7", "url": "https://threejs.org/manual/en/creating-text.html", "page_title": "Creating Text", "page_type": "guide", "page_summary": "A guide covering multiple approaches to adding and rendering text in a three.js application, from simple DOM overlays to procedural 3D text geometries, bitmap fonts, and Troika Text.", "heading": "6. Bitmap Fonts", "content": "Page: Creating Text\nSection: 6. Bitmap Fonts\n\nBMFonts (bitmap fonts) allow batching glyphs into a single BufferGeometry. BMFont rendering supports word-wrapping, letter spacing, kerning, signed distance fields with standard derivatives, multi-channel signed distance fields, multi-texture fonts, and more. See three-mesh-ui or three-bmfont-text.\n\nStock fonts are available in projects like A-Frame Fonts, or you can create your own from any .TTF font, optimizing to include only characters required for a project.\n\nSome helpful tools:\n\n- msdf-bmfont-web  *(web-based)*\n- msdf-bmfont-xml  *(commandline)*\n- hiero  *(desktop app)*", "code_blocks": [], "chunk_position": 4, "heading_path": "6. Bitmap Fonts > 6. Bitmap Fonts", "breadcrumbs": "Creating Text > 6. Bitmap Fonts > 6. Bitmap Fonts"}, {"id": "6f4782edc4a13960", "url": "https://threejs.org/manual/en/creating-text.html", "page_title": "Creating Text", "page_type": "guide", "page_summary": "A guide covering multiple approaches to adding and rendering text in a three.js application, from simple DOM overlays to procedural 3D text geometries, bitmap fonts, and Troika Text.", "heading": "7. Troika Text", "content": "Page: Creating Text\nSection: 7. Troika Text\n\nThe troika-three-text package renders quality antialiased text using a similar technique as BMFonts, but works directly with any .TTF or .WOFF font file so you don't have to pregenerate a glyph texture offline. It also adds capabilities including:\n\n- Effects like strokes, drop shadows, and curvature\n- The ability to apply any three.js Material, even a custom ShaderMaterial\n- Support for font ligatures, scripts with joined letters, and right-to-left/bidirectional layout\n- Optimization for large amounts of dynamic text, performing most work off the main thread in a web worker", "code_blocks": [], "chunk_position": 4, "heading_path": "7. Troika Text > 7. Troika Text", "breadcrumbs": "Creating Text > 7. Troika Text > 7. Troika Text"}, {"id": "0e278b9ecb859f1a", "url": "https://threejs.org/manual/en/drawing-lines.html", "page_title": "Drawing Lines", "page_type": "guide", "page_summary": "A tutorial on how to draw lines in Three.js using LineBasicMaterial, BufferGeometry with vertices, and the Line object, rather than wireframe Mesh geometry.", "heading": "Drawing Lines", "content": "Page: Drawing Lines\nSection: Drawing Lines\n\nLet's say you want to draw a line or a circle, not a wireframe `Mesh`. First we need to set up the renderer, scene and camera (see the Creating a scene page).\n\nHere is the code that we will use:\n\nNext thing we will do is define a material. For lines we have to use `LineBasicMaterial` or `LineDashedMaterial`.\n\nAfter material we will need a geometry with some vertices:\n\nNote that lines are drawn between each consecutive pair of vertices, but not between the first and last (the line is not closed.)\n\nNow that we have points for two lines and a material, we can put them together to form a line.\n\nAll that's left is to add it to the scene and call `renderer.render()`.\n\nYou should now be seeing an arrow pointing upwards, made from two blue lines.", "code_blocks": [{"language": "javascript", "code": "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();", "filename": ""}, {"language": "javascript", "code": "//create a blue LineBasicMaterial\nconst material = new THREE.LineBasicMaterial( { color: 0x0000ff } );", "filename": ""}, {"language": "javascript", "code": "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 );", "filename": ""}, {"language": "javascript", "code": "const line = new THREE.Line( geometry, material );", "filename": ""}, {"language": "javascript", "code": "scene.add( line );\nrenderer.render( scene, camera );", "filename": ""}], "chunk_position": 5, "heading_path": "Drawing Lines > Drawing Lines", "breadcrumbs": "Drawing Lines > Drawing Lines > Drawing Lines"}, {"id": "41fc7a36ddee02a4", "url": "https://threejs.org/manual/en/faq.html", "page_title": "FAQ", "page_type": "guide", "page_summary": "A frequently asked questions page covering best-supported 3D model formats, viewport tags, scene scaling on resize, face culling, input validation, and Node.js usage in three.js.", "heading": "Which 3D model format is best supported?", "content": "Page: FAQ\nSection: Which 3D model format is best supported?\n\nThe 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\nthree.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.", "code_blocks": [], "chunk_position": 6, "heading_path": "Which 3D model format is best supported? > Which 3D model format is best supported?", "breadcrumbs": "FAQ > Which 3D model format is best supported? > Which 3D model format is best supported?"}, {"id": "ad059ac147136568", "url": "https://threejs.org/manual/en/faq.html", "page_title": "FAQ", "page_type": "guide", "page_summary": "A frequently asked questions page covering best-supported 3D model formats, viewport tags, scene scaling on resize, face culling, input validation, and Node.js usage in three.js.", "heading": "Why are there meta viewport tags in examples?", "content": "Page: FAQ\nSection: Why are there meta viewport tags in examples?\n\nThese tags control viewport size and scale for mobile browsers (where page content may be rendered at different size than visible viewport).", "code_blocks": [{"language": "html", "code": "<meta name=\"viewport\" content=\"width=device-width, user-scalable=no, minimum-scale=1.0, maximum-scale=1.0\">", "filename": ""}], "chunk_position": 6, "heading_path": "Why are there meta viewport tags in examples? > Why are there meta viewport tags in examples?", "breadcrumbs": "FAQ > Why are there meta viewport tags in examples? > Why are there meta viewport tags in examples?"}, {"id": "90798cfd159b7b48", "url": "https://threejs.org/manual/en/faq.html", "page_title": "FAQ", "page_type": "guide", "page_summary": "A frequently asked questions page covering best-supported 3D model formats, viewport tags, scene scaling on resize, face culling, input validation, and Node.js usage in three.js.", "heading": "How can scene scale be preserved on resize?", "content": "Page: FAQ\nSection: How can scene scale be preserved on resize?\n\nWe want all objects, regardless of their distance from the camera, to appear the same size, even as the window is resized.\n\nThe key equation to solving this is this formula for the visible height at a given distance:\n\nIf we increase the window height by a certain percentage, then what we want is the visible height at all distances to increase by the same percentage.\n\nThis can not be done by changing the camera position. Instead you have to change the camera field-of-view.", "code_blocks": [{"language": "javascript", "code": "visible_height = 2 * Math.tan( ( Math.PI / 180 ) * camera.fov / 2 ) * distance_from_camera;", "filename": ""}], "chunk_position": 6, "heading_path": "How can scene scale be preserved on resize? > How can scene scale be preserved on resize?", "breadcrumbs": "FAQ > How can scene scale be preserved on resize? > How can scene scale be preserved on resize?"}, {"id": "4301db5a69fd841c", "url": "https://threejs.org/manual/en/faq.html", "page_title": "FAQ", "page_type": "guide", "page_summary": "A frequently asked questions page covering best-supported 3D model formats, viewport tags, scene scaling on resize, face culling, input validation, and Node.js usage in three.js.", "heading": "Why is part of my object invisible?", "content": "Page: FAQ\nSection: Why is part of my object invisible?\n\nThis 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\nTo see if this is your problem, change the material side to THREE.DoubleSide.", "code_blocks": [{"language": "javascript", "code": "material.side = THREE.DoubleSide", "filename": ""}], "chunk_position": 6, "heading_path": "Why is part of my object invisible? > Why is part of my object invisible?", "breadcrumbs": "FAQ > Why is part of my object invisible? > Why is part of my object invisible?"}, {"id": "8e192e1ae0d5b5e8", "url": "https://threejs.org/manual/en/faq.html", "page_title": "FAQ", "page_type": "guide", "page_summary": "A frequently asked questions page covering best-supported 3D model formats, viewport tags, scene scaling on resize, face culling, input validation, and Node.js usage in three.js.", "heading": "Why does three.js sometimes return strange results for invalid inputs?", "content": "Page: FAQ\nSection: Why does three.js sometimes return strange results for invalid inputs?\n\nFor 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.", "code_blocks": [], "chunk_position": 6, "heading_path": "Why does three.js sometimes return strange results for invalid inputs? > Why does three.js sometimes return strange results for invalid inputs?", "breadcrumbs": "FAQ > Why does three.js sometimes return strange results for invalid inputs? > Why does three.js sometimes return strange results for invalid inputs?"}, {"id": "8d0a63b651cba394", "url": "https://threejs.org/manual/en/faq.html", "page_title": "FAQ", "page_type": "guide", "page_summary": "A frequently asked questions page covering best-supported 3D model formats, viewport tags, scene scaling on resize, face culling, input validation, and Node.js usage in three.js.", "heading": "Can I use three.js in Node.js?", "content": "Page: FAQ\nSection: Can I use three.js in Node.js?\n\nBecause 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 headless-gl and jsdom-global, or by replacing components like `TextureLoader` 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 requests to improve Node.js support, but recommend opening an issue to discuss your improvements first.", "code_blocks": [], "chunk_position": 6, "heading_path": "Can I use three.js in Node.js? > Can I use three.js in Node.js?", "breadcrumbs": "FAQ > Can I use three.js in Node.js? > Can I use three.js in Node.js?"}, {"id": "4101050e399235de", "url": "https://threejs.org/manual/en/libraries-and-plugins.html", "page_title": "Libraries and Plugins", "page_type": "reference", "page_summary": "A community-maintained list of externally developed compatible libraries and plugins for three.js, organized by category including physics, postprocessing, file formats, geometry, 3D text, particle systems, IK, game AI, and wrappers/frameworks.", "heading": "Libraries and Plugins", "content": "Page: Libraries and Plugins\nSection: Libraries and Plugins\n\nListed here are externally developed compatible libraries and plugins for three.js. This list and the associated packages are maintained by the community and not guaranteed to be up to date. If you'd like to update this list make a PR!", "code_blocks": [], "chunk_position": 7, "heading_path": "Libraries and Plugins > Libraries and Plugins", "breadcrumbs": "Libraries and Plugins > Libraries and Plugins > Libraries and Plugins"}, {"id": "456bc43a2a8e696b", "url": "https://threejs.org/manual/en/libraries-and-plugins.html", "page_title": "Libraries and Plugins", "page_type": "reference", "page_summary": "A community-maintained list of externally developed compatible libraries and plugins for three.js, organized by category including physics, postprocessing, file formats, geometry, 3D text, particle systems, IK, game AI, and wrappers/frameworks.", "heading": "Physics", "content": "Page: Libraries and Plugins\nSection: Physics\n\n", "code_blocks": [], "chunk_position": 7, "heading_path": "Physics > Physics", "breadcrumbs": "Libraries and Plugins > Physics > Physics"}, {"id": "f2f06395cd5c642a", "url": "https://threejs.org/manual/en/libraries-and-plugins.html", "page_title": "Libraries and Plugins", "page_type": "reference", "page_summary": "A community-maintained list of externally developed compatible libraries and plugins for three.js, organized by category including physics, postprocessing, file formats, geometry, 3D text, particle systems, IK, game AI, and wrappers/frameworks.", "heading": "Postprocessing", "content": "Page: Libraries and Plugins\nSection: Postprocessing\n\nIn addition to the official three.js postprocessing effects, support for some additional effects and frameworks are available through external libraries.", "code_blocks": [], "chunk_position": 7, "heading_path": "Postprocessing > Postprocessing", "breadcrumbs": "Libraries and Plugins > Postprocessing > Postprocessing"}, {"id": "28d83c92b78b5b36", "url": "https://threejs.org/manual/en/libraries-and-plugins.html", "page_title": "Libraries and Plugins", "page_type": "reference", "page_summary": "A community-maintained list of externally developed compatible libraries and plugins for three.js, organized by category including physics, postprocessing, file formats, geometry, 3D text, particle systems, IK, game AI, and wrappers/frameworks.", "heading": "Intersection and Raycast Performance", "content": "Page: Libraries and Plugins\nSection: Intersection and Raycast Performance\n\n", "code_blocks": [], "chunk_position": 7, "heading_path": "Intersection and Raycast Performance > Intersection and Raycast Performance", "breadcrumbs": "Libraries and Plugins > Intersection and Raycast Performance > Intersection and Raycast Performance"}, {"id": "3cbee00eab7880bd", "url": "https://threejs.org/manual/en/libraries-and-plugins.html", "page_title": "Libraries and Plugins", "page_type": "reference", "page_summary": "A community-maintained list of externally developed compatible libraries and plugins for three.js, organized by category including physics, postprocessing, file formats, geometry, 3D text, particle systems, IK, game AI, and wrappers/frameworks.", "heading": "Path Tracing", "content": "Page: Libraries and Plugins\nSection: Path Tracing\n\n", "code_blocks": [], "chunk_position": 7, "heading_path": "Path Tracing > Path Tracing", "breadcrumbs": "Libraries and Plugins > Path Tracing > Path Tracing"}, {"id": "c1b1b48615ddb0ad", "url": "https://threejs.org/manual/en/libraries-and-plugins.html", "page_title": "Libraries and Plugins", "page_type": "reference", "page_summary": "A community-maintained list of externally developed compatible libraries and plugins for three.js, organized by category including physics, postprocessing, file formats, geometry, 3D text, particle systems, IK, game AI, and wrappers/frameworks.", "heading": "File Formats", "content": "Page: Libraries and Plugins\nSection: File Formats\n\nIn addition to the official three.js loaders, support for some additional formats is available through external libraries.", "code_blocks": [], "chunk_position": 7, "heading_path": "File Formats > File Formats", "breadcrumbs": "Libraries and Plugins > File Formats > File Formats"}, {"id": "4a4d85f1ce491f4d", "url": "https://threejs.org/manual/en/libraries-and-plugins.html", "page_title": "Libraries and Plugins", "page_type": "reference", "page_summary": "A community-maintained list of externally developed compatible libraries and plugins for three.js, organized by category including physics, postprocessing, file formats, geometry, 3D text, particle systems, IK, game AI, and wrappers/frameworks.", "heading": "Geometry", "content": "Page: Libraries and Plugins\nSection: Geometry\n\n", "code_blocks": [], "chunk_position": 7, "heading_path": "Geometry > Geometry", "breadcrumbs": "Libraries and Plugins > Geometry > Geometry"}, {"id": "2f8e17dc50666fef", "url": "https://threejs.org/manual/en/libraries-and-plugins.html", "page_title": "Libraries and Plugins", "page_type": "reference", "page_summary": "A community-maintained list of externally developed compatible libraries and plugins for three.js, organized by category including physics, postprocessing, file formats, geometry, 3D text, particle systems, IK, game AI, and wrappers/frameworks.", "heading": "3D Text and Layout", "content": "Page: Libraries and Plugins\nSection: 3D Text and Layout\n\n", "code_blocks": [], "chunk_position": 7, "heading_path": "3D Text and Layout > 3D Text and Layout", "breadcrumbs": "Libraries and Plugins > 3D Text and Layout > 3D Text and Layout"}, {"id": "aa9eac3fe5d8f0a6", "url": "https://threejs.org/manual/en/libraries-and-plugins.html", "page_title": "Libraries and Plugins", "page_type": "reference", "page_summary": "A community-maintained list of externally developed compatible libraries and plugins for three.js, organized by category including physics, postprocessing, file formats, geometry, 3D text, particle systems, IK, game AI, and wrappers/frameworks.", "heading": "Particle Systems", "content": "Page: Libraries and Plugins\nSection: Particle Systems\n\n", "code_blocks": [], "chunk_position": 7, "heading_path": "Particle Systems > Particle Systems", "breadcrumbs": "Libraries and Plugins > Particle Systems > Particle Systems"}, {"id": "a32ba27034d37c23", "url": "https://threejs.org/manual/en/libraries-and-plugins.html", "page_title": "Libraries and Plugins", "page_type": "reference", "page_summary": "A community-maintained list of externally developed compatible libraries and plugins for three.js, organized by category including physics, postprocessing, file formats, geometry, 3D text, particle systems, IK, game AI, and wrappers/frameworks.", "heading": "Inverse Kinematics", "content": "Page: Libraries and Plugins\nSection: Inverse Kinematics\n\n", "code_blocks": [], "chunk_position": 7, "heading_path": "Inverse Kinematics > Inverse Kinematics", "breadcrumbs": "Libraries and Plugins > Inverse Kinematics > Inverse Kinematics"}, {"id": "030a5c8ba58ec0ed", "url": "https://threejs.org/manual/en/libraries-and-plugins.html", "page_title": "Libraries and Plugins", "page_type": "reference", "page_summary": "A community-maintained list of externally developed compatible libraries and plugins for three.js, organized by category including physics, postprocessing, file formats, geometry, 3D text, particle systems, IK, game AI, and wrappers/frameworks.", "heading": "Game AI", "content": "Page: Libraries and Plugins\nSection: Game AI\n\n", "code_blocks": [], "chunk_position": 7, "heading_path": "Game AI > Game AI", "breadcrumbs": "Libraries and Plugins > Game AI > Game AI"}, {"id": "83c8f644ab5e506d", "url": "https://threejs.org/manual/en/libraries-and-plugins.html", "page_title": "Libraries and Plugins", "page_type": "reference", "page_summary": "A community-maintained list of externally developed compatible libraries and plugins for three.js, organized by category including physics, postprocessing, file formats, geometry, 3D text, particle systems, IK, game AI, and wrappers/frameworks.", "heading": "Wrappers and Frameworks", "content": "Page: Libraries and Plugins\nSection: Wrappers and Frameworks\n\n", "code_blocks": [], "chunk_position": 7, "heading_path": "Wrappers and Frameworks > Wrappers and Frameworks", "breadcrumbs": "Libraries and Plugins > Wrappers and Frameworks > Wrappers and Frameworks"}, {"id": "1d0c8cacc1b64824", "url": "https://threejs.org/manual/en/loading-3d-models.html", "page_title": "Loading 3D Models", "page_type": "guide", "page_summary": "A guide on loading 3D models in three.js, recommending the glTF format and providing workflow, loading instructions, troubleshooting tips, and guidance on asking for help.", "heading": "Loading 3D Models", "content": "Page: Loading 3D Models\nSection: Loading 3D Models\n\n3D models are available in hundreds of file formats, each with different purposes, assorted features, and varying complexity. Although three.js provides many loaders, choosing the right format and workflow will save time and frustration later on. Some formats are difficult to work with, inefficient for realtime experiences, or simply not fully supported at this time.\n\nThis guide provides a workflow recommended for most users, and suggestions for what to try if things don't go as expected.", "code_blocks": [], "chunk_position": 8, "heading_path": "Loading 3D Models > Loading 3D Models", "breadcrumbs": "Loading 3D Models > Loading 3D Models > Loading 3D Models"}, {"id": "71a9e3d43ddaf0d3", "url": "https://threejs.org/manual/en/loading-3d-models.html", "page_title": "Loading 3D Models", "page_type": "guide", "page_summary": "A guide on loading 3D models in three.js, recommending the glTF format and providing workflow, loading instructions, troubleshooting tips, and guidance on asking for help.", "heading": "Before we start", "content": "Page: Loading 3D Models\nSection: Before we start\n\nIf you're new to running a local server, begin with Installation first. Many common errors viewing 3D models can be avoided by hosting files correctly.", "code_blocks": [], "chunk_position": 8, "heading_path": "Before we start > Before we start", "breadcrumbs": "Loading 3D Models > Before we start > Before we start"}, {"id": "8c6d53e349088bb4", "url": "https://threejs.org/manual/en/loading-3d-models.html", "page_title": "Loading 3D Models", "page_type": "guide", "page_summary": "A guide on loading 3D models in three.js, recommending the glTF format and providing workflow, loading instructions, troubleshooting tips, and guidance on asking for help.", "heading": "Recommended workflow", "content": "Page: Loading 3D Models\nSection: Recommended workflow\n\nWhere possible, we recommend using glTF (GL Transmission Format). Both .GLB and .GLTF versions of the format are well supported. Because glTF is focused on runtime asset delivery, it is compact to transmit and fast to load. Features include meshes, materials, textures, skins, skeletons, morph targets, animations, lights, and cameras.\n\nPublic-domain glTF files are available on sites like Sketchfab, or various tools include glTF export:\n\n- Blender by the Blender Foundation\n- Substance Painter by Allegorithmic\n- Modo by Foundry\n- Toolbag by Marmoset\n- Houdini by SideFX\n- Cinema 4D by MAXON\n- COLLADA2GLTF by the Khronos Group\n- FBX2GLTF by Facebook\n- OBJ2GLTF by Analytical Graphics Inc\n- …and many more\n\nIf your preferred tools do not support glTF, consider requesting glTF export from the authors, or posting on the glTF roadmap thread.\n\nWhen glTF is not an option, popular formats such as FBX, OBJ, or COLLADA are also available and regularly maintained.", "code_blocks": [], "chunk_position": 8, "heading_path": "Recommended workflow > Recommended workflow", "breadcrumbs": "Loading 3D Models > Recommended workflow > Recommended workflow"}, {"id": "240e67f00afbd97e", "url": "https://threejs.org/manual/en/loading-3d-models.html", "page_title": "Loading 3D Models", "page_type": "guide", "page_summary": "A guide on loading 3D models in three.js, recommending the glTF format and providing workflow, loading instructions, troubleshooting tips, and guidance on asking for help.", "heading": "Loading", "content": "Page: Loading 3D Models\nSection: Loading\n\nOnly a few loaders (e.g. ObjectLoader) are included by default with three.js — others should be added to your app individually.\n\nOnce you've imported a loader, you're ready to add a model to your scene. Syntax varies among different loaders — when using another format, check the examples and documentation for that loader. For glTF, usage with global scripts would be:", "code_blocks": [{"language": "javascript", "code": "import { GLTFLoader } from 'three/addons/loaders/GLTFLoader.js';", "filename": ""}, {"language": "javascript", "code": "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} );", "filename": ""}], "chunk_position": 8, "heading_path": "Loading > Loading", "breadcrumbs": "Loading 3D Models > Loading > Loading"}, {"id": "4df45ad4f1e6f448", "url": "https://threejs.org/manual/en/loading-3d-models.html", "page_title": "Loading 3D Models", "page_type": "guide", "page_summary": "A guide on loading 3D models in three.js, recommending the glTF format and providing workflow, loading instructions, troubleshooting tips, and guidance on asking for help.", "heading": "Troubleshooting", "content": "Page: Loading 3D Models\nSection: Troubleshooting\n\nYou've spent hours modeling an artisanal masterpiece, you load it into the webpage, and — oh no! 😭 It's distorted, miscolored, or missing entirely. Start with these troubleshooting steps:\n\n- Check the JavaScript console for errors, and make sure you've used an `onError` callback when calling `.load()` to log the result.\n- View the model in another application. For glTF, drag-and-drop viewers are available for three.js and babylon.js. If the model appears correctly in one or more applications, file a bug against three.js. If the model cannot be shown in any application, we strongly encourage filing a bug with the application used to create the model.\n- Try scaling the model up or down by a factor of 1000. Many models are scaled differently, and large models may not appear if the camera is inside the model.\n- Try to add and position a light source. The model may be hidden in the dark.\n- Look for failed texture requests in the network tab, like `\"C:\\\\Path\\To\\Model\\texture.jpg\"`. Use paths relative to your model instead, such as `images/texture.jpg` — this may require editing the model file in a text editor.", "code_blocks": [], "chunk_position": 8, "heading_path": "Troubleshooting > Troubleshooting", "breadcrumbs": "Loading 3D Models > Troubleshooting > Troubleshooting"}, {"id": "ccb10788f6ff29a7", "url": "https://threejs.org/manual/en/loading-3d-models.html", "page_title": "Loading 3D Models", "page_type": "guide", "page_summary": "A guide on loading 3D models in three.js, recommending the glTF format and providing workflow, loading instructions, troubleshooting tips, and guidance on asking for help.", "heading": "Asking for help", "content": "Page: Loading 3D Models\nSection: Asking for help\n\nIf you've gone through the troubleshooting process above and your model still isn't working, the right approach to asking for help will get you to a solution faster. Post a question on the three.js forum and, whenever possible, include your model (or a simpler model with the same problem) in any formats you have available. Include enough information for someone else to reproduce the issue quickly — ideally, a live demo.", "code_blocks": [], "chunk_position": 8, "heading_path": "Asking for help > Asking for help", "breadcrumbs": "Loading 3D Models > Asking for help > Asking for help"}, {"id": "4c3d72e7229f1cd9", "url": "https://threejs.org/manual/en/uniform-types.html", "page_title": "Uniform Types", "page_type": "reference", "page_summary": "Documents the mapping between GLSL uniform types and their corresponding JavaScript types in three.js, including support for uniform structures and arrays.", "heading": "Uniform Types", "content": "Page: Uniform Types\nSection: Uniform Types\n\nEach uniform must have a `value` property. The type of the value must correspond to the type of the uniform variable in the GLSL code as specified for the primitive GLSL types in the table below. Uniform structures and arrays are also supported. GLSL arrays of primitive type must either be specified as an array of the corresponding THREE objects or as a flat array containing the data of all the objects. In other words; GLSL primitives in arrays must not be represented by arrays. This rule does not apply transitively. An array of `vec2` arrays, each with a length of five vectors, must be an array of arrays, of either five `Vector2` objects or ten `number`s.\n\n| GLSL type | JavaScript type |\n| --- | --- |\n| int | Number |\n| uint | Number |\n| float | Number |\n| bool | Boolean |\n| bool | Number |\n| vec2 | Vector2 |\n| vec2 | Float32Array (*) |\n| vec2 | Array (*) |\n| vec3 | Vector3 |\n| vec3 | Color |\n| vec3 | Float32Array (*) |\n| vec3 | Array (*) |\n| vec4 | Vector4 |\n| vec4 | Quaternion |\n| vec4 | Float32Array (*) |\n| vec4 | Array (*) |\n| mat2 | Float32Array (*) |\n| mat2 | Array (*) |\n| mat3 | Matrix3 |\n| mat3 | Float32Array (*) |\n| mat3 | Array (*) |\n| mat4 | Matrix4 |\n| mat4 | Float32Array (*) |\n| mat4 | Array (*) |\n| ivec2, bvec2 | Float32Array (*) |\n| ivec2, bvec2 | Array (*) |\n| ivec3, bvec3 | Int32Array (*) |\n| ivec3, bvec3 | Array (*) |\n| ivec4, bvec4 | Int32Array (*) |\n| ivec4, bvec4 | Array (*) |\n| sampler2D | Texture |\n| samplerCube | CubeTexture |\n\n(*) Same for an (innermost) array (dimension) of the same GLSL type, containing the components of all vectors or matrices in the array.", "code_blocks": [], "chunk_position": 9, "heading_path": "Uniform Types > Uniform Types", "breadcrumbs": "Uniform Types > Uniform Types > Uniform Types"}, {"id": "c6d676ea5f78d803", "url": "https://threejs.org/manual/en/uniform-types.html", "page_title": "Uniform Types", "page_type": "reference", "page_summary": "Documents the mapping between GLSL uniform types and their corresponding JavaScript types in three.js, including support for uniform structures and arrays.", "heading": "Structured Uniforms", "content": "Page: Uniform Types\nSection: Structured Uniforms\n\nSometimes you want to organize uniforms as `structs` in your shader code. The following style must be used so `three.js` is able to process structured uniform data.\n\nThis definition can be mapped on the following GLSL code:", "code_blocks": [{"language": "javascript", "code": "uniforms = {\n  data: { \n    value: {\n      position: new Vector3(), \n      direction: new Vector3( 0, 0, 1 ) \n    } \n  } \n};", "filename": ""}, {"language": "glsl", "code": "struct Data { \n  vec3 position;\n  vec3 direction;\n};\nuniform Data data;", "filename": ""}], "chunk_position": 9, "heading_path": "Structured Uniforms > Structured Uniforms", "breadcrumbs": "Uniform Types > Structured Uniforms > Structured Uniforms"}, {"id": "45f74f32540710d8", "url": "https://threejs.org/manual/en/uniform-types.html", "page_title": "Uniform Types", "page_type": "reference", "page_summary": "Documents the mapping between GLSL uniform types and their corresponding JavaScript types in three.js, including support for uniform structures and arrays.", "heading": "Structured Uniforms with Arrays", "content": "Page: Uniform Types\nSection: Structured Uniforms with Arrays\n\nIt's also possible to manage `structs` in arrays. The syntax for this use case looks like so:\n\nThis definition can be mapped on the following GLSL code:", "code_blocks": [{"language": "javascript", "code": "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};", "filename": ""}, {"language": "glsl", "code": "struct Data { \n  vec3 position; \n  vec3 direction; \n};\nuniform Data data[ 2 ];", "filename": ""}], "chunk_position": 9, "heading_path": "Structured Uniforms with Arrays > Structured Uniforms with Arrays", "breadcrumbs": "Uniform Types > Structured Uniforms with Arrays > Structured Uniforms with Arrays"}, {"id": "7dba643e94174b2c", "url": "https://threejs.org/manual/en/useful-links.html", "page_title": "Useful Links", "page_type": "reference", "page_summary": "A curated collection of links to tutorials, courses, examples, tools, forums, and other resources useful when learning three.js.", "heading": "Introduction", "content": "Page: Useful Links\nSection: Introduction\n\nThe following is a collection of links that you might find useful when learning three.js.\n\nIf you find something that you'd like to add here, or think that one of the links below is no longer relevant or working, feel free to click the 'edit' button in the bottom right and make some changes!\n\nNote also that as three.js is under rapid development, a lot of these links will contain information that is out of date - if something isn't working as you'd expect or as one of these links says it should, check the browser console for warnings or errors. Also check the relevant docs pages.", "code_blocks": [], "chunk_position": 10, "heading_path": "Introduction > Introduction", "breadcrumbs": "Useful Links > Introduction > Introduction"}, {"id": "ec79bf741407220f", "url": "https://threejs.org/manual/en/useful-links.html", "page_title": "Useful Links", "page_type": "reference", "page_summary": "A curated collection of links to tutorials, courses, examples, tools, forums, and other resources useful when learning three.js.", "heading": "Help forums", "content": "Page: Useful Links\nSection: Help forums\n\nThree.js officially uses the forum (https://discourse.threejs.org/) and Stack Overflow (http://stackoverflow.com/tags/three.js/info) for help requests. If you need assistance with something, that's the place to go. Do NOT open an issue on Github for help requests.", "code_blocks": [], "chunk_position": 10, "heading_path": "Help forums > Help forums", "breadcrumbs": "Useful Links > Help forums > Help forums"}, {"id": "2967ef37e0e8f7d0", "url": "https://threejs.org/manual/en/useful-links.html", "page_title": "Useful Links", "page_type": "reference", "page_summary": "A curated collection of links to tutorials, courses, examples, tools, forums, and other resources useful when learning three.js.", "heading": "Tutorials and courses", "content": "Page: Useful Links\nSection: Tutorials and courses\n\n", "code_blocks": [], "chunk_position": 10, "heading_path": "Tutorials and courses > Tutorials and courses", "breadcrumbs": "Useful Links > Tutorials and courses > Tutorials and courses"}, {"id": "54f74456107d3154", "url": "https://threejs.org/manual/en/useful-links.html", "page_title": "Useful Links", "page_type": "reference", "page_summary": "A curated collection of links to tutorials, courses, examples, tools, forums, and other resources useful when learning three.js.", "heading": "Getting started with three.js", "content": "Page: Useful Links\nSection: Getting started with three.js\n\n- Three.js Fundamentals starting lesson (https://threejs.org/manual/#en/fundamentals)\n\n- Beginning with 3D WebGL (https://codepen.io/rachsmith/post/beginning-with-3d-webgl-pt-1-the-scene) by Rachel Smith (https://codepen.io/rachsmith/).\n\n- Animating scenes with WebGL and three.js (https://www.august.com.au/blog/animating-scenes-with-webgl-three-js/)", "code_blocks": [], "chunk_position": 10, "heading_path": "Getting started with three.js > Getting started with three.js", "breadcrumbs": "Useful Links > Getting started with three.js > Getting started with three.js"}, {"id": "d8ee074cd7c6bda6", "url": "https://threejs.org/manual/en/useful-links.html", "page_title": "Useful Links", "page_type": "reference", "page_summary": "A curated collection of links to tutorials, courses, examples, tools, forums, and other resources useful when learning three.js.", "heading": "More extensive / advanced articles and courses", "content": "Page: Useful Links\nSection: More extensive / advanced articles and courses\n\n- Three Journey (https://threejs-journey.com/) Course by Bruno Simon (https://bruno-simon.com/) - Teaches beginners how to use Three.js step by step\n\n- Discover three.js (https://discoverthreejs.com/)\n\n- Collection of tutorials (http://blog.cjgammon.com/) by CJ Gammon (http://www.cjgammon.com/).\n\n- Glossy spheres in three.js (https://medium.com/soffritti.pierfrancesco/glossy-spheres-in-three-js-bfd2785d4857).\n\n- Interactive 3D Graphics (https://www.udacity.com/course/interactive-3d-graphics--cs291) - a free course on Udacity that teaches the fundamentals of 3D Graphics, and uses three.js as its coding tool.\n\n- Aerotwist (https://aerotwist.com/tutorials/) tutorials by Paul Lewis (https://github.com/paullewis/).\n\n- Three.js Bookshelf (https://discourse.threejs.org/t/three-js-bookshelf/2468) - Looking for more resources about three.js or computer graphics in general? Check out the selection of literature recommended by the community.", "code_blocks": [], "chunk_position": 10, "heading_path": "More extensive / advanced articles and courses > More extensive / advanced articles and courses", "breadcrumbs": "Useful Links > More extensive / advanced articles and courses > More extensive / advanced articles and courses"}, {"id": "99aed0b8ad4d6258", "url": "https://threejs.org/manual/en/useful-links.html", "page_title": "Useful Links", "page_type": "reference", "page_summary": "A curated collection of links to tutorials, courses, examples, tools, forums, and other resources useful when learning three.js.", "heading": "News and Updates", "content": "Page: Useful Links\nSection: News and Updates\n\n- Three.js on Twitter (https://twitter.com/hashtag/threejs)\n\n- Three.js on reddit (http://www.reddit.com/r/threejs/)\n\n- WebGL on reddit (http://www.reddit.com/r/webgl/)", "code_blocks": [], "chunk_position": 10, "heading_path": "News and Updates > News and Updates", "breadcrumbs": "Useful Links > News and Updates > News and Updates"}, {"id": "512975fa5835e0a8", "url": "https://threejs.org/manual/en/useful-links.html", "page_title": "Useful Links", "page_type": "reference", "page_summary": "A curated collection of links to tutorials, courses, examples, tools, forums, and other resources useful when learning three.js.", "heading": "Examples", "content": "Page: Useful Links\nSection: Examples\n\n- three-seed (https://github.com/edwinwebb/three-seed/) - three.js starter project with ES6 and Webpack\n\n- Professor Stemkoskis Examples (http://stemkoski.github.io/Three.js/index.html) - a collection of beginner friendly examples built using three.js r60.\n\n- Official three.js examples (https://threejs.org/examples/) - these examples are maintained as part of the three.js repository, and always use the latest version of three.js.\n\n- Official three.js dev branch examples (https://raw.githack.com/mrdoob/three.js/dev/examples/) - Same as the above, except these use the dev branch of three.js, and are used to check that everything is working as three.js being is developed.", "code_blocks": [], "chunk_position": 10, "heading_path": "Examples > Examples", "breadcrumbs": "Useful Links > Examples > Examples"}, {"id": "a9025cf7058b0453", "url": "https://threejs.org/manual/en/useful-links.html", "page_title": "Useful Links", "page_type": "reference", "page_summary": "A curated collection of links to tutorials, courses, examples, tools, forums, and other resources useful when learning three.js.", "heading": "Tools", "content": "Page: Useful Links\nSection: Tools\n\n- physgl.org (https://github.com/tbensky/physgl) - JavaScript front-end with wrappers to three.js, to bring WebGL graphics to students learning physics and math.\n\n- Whitestorm.js (https://whsjs.readme.io/) – Modular three.js framework with AmmoNext physics plugin.\n\n- Three.js Inspector (http://zz85.github.io/zz85-bookmarklets/threelabs.html)\n\n- ThreeNodes.js (http://idflood.github.io/ThreeNodes.js/).\n\n- vscode shader (https://marketplace.visualstudio.com/items?itemName=slevesque.shader) - Syntax highlighter for shader language.\n\n- vscode comment-tagged-templates (https://marketplace.visualstudio.com/items?itemName=bierner.comment-tagged-templates) - Syntax highlighting for tagged template strings using comments to shader language, like: glsl.js.\n\n- WebXR-emulator-extension (https://github.com/MozillaReality/WebXR-emulator-extension)", "code_blocks": [], "chunk_position": 10, "heading_path": "Tools > Tools", "breadcrumbs": "Useful Links > Tools > Tools"}, {"id": "d6cf961ba0ec53eb", "url": "https://threejs.org/manual/en/useful-links.html", "page_title": "Useful Links", "page_type": "reference", "page_summary": "A curated collection of links to tutorials, courses, examples, tools, forums, and other resources useful when learning three.js.", "heading": "WebGL References", "content": "Page: Useful Links\nSection: WebGL References\n\n- webgl-reference-card.pdf (https://www.khronos.org/files/webgl/webgl-reference-card-1_0.pdf) - Reference of all WebGL and GLSL keywords, terminology, syntax and definitions.", "code_blocks": [], "chunk_position": 10, "heading_path": "WebGL References > WebGL References", "breadcrumbs": "Useful Links > WebGL References > WebGL References"}, {"id": "a3fee6e4aabee4e1", "url": "https://threejs.org/manual/en/useful-links.html", "page_title": "Useful Links", "page_type": "reference", "page_summary": "A curated collection of links to tutorials, courses, examples, tools, forums, and other resources useful when learning three.js.", "heading": "Old Links", "content": "Page: Useful Links\nSection: Old Links\n\nThese links are kept for historical purposes - you may still find them useful, but be warned that they may have information relating to very old versions of three.js.\n\n- AlterQualia at WebGL Camp 3 (https://www.youtube.com/watch?v=Dir4KO9RdhM)\n\n- Yomotsus Examples (http://yomotsu.github.io/threejs-examples/) - a collection of examples using three.js r45.\n\n- Introduction to Three.js (http://fhtr.org/BasicsOfThreeJS/#1) by Ilmari Heikkinen (http://github.com/kig/) (slideshow).\n\n- WebGL and Three.js (http://www.slideshare.net/yomotsu/webgl-and-threejs) by Akihiro Oyamada (http://github.com/yomotsu) (slideshow).\n\n- Trigger Rally (https://www.youtube.com/watch?v=VdQnOaolrPA) by jareiko (https://github.com/jareiko) (video).\n\n- ThreeFab (http://blackjk3.github.io/threefab/) - scene editor, maintained up until around three.js r50.\n\n- Max to Three.js workflow tips and tricks (http://bkcore.com/blog/3d/webgl-three-js-workflow-tips.html) by BKcore (https://github.com/BKcore)\n\n- A whirlwind look at Three.js (http://12devsofxmas.co.uk/2012/01/webgl-and-three-js/) by Paul King (http://github.com/nrocy)\n\n- Animated selective glow in Three.js (http://bkcore.com/blog/3d/webgl-three-js-animated-selective-glow.html) by BKcore (https://github.com/BKcore)\n\n- Building A Physics Simulation Environment (http://www.natural-science.or.jp/article/20120220155529.php) - three.js tutorial in Japanese", "code_blocks": [], "chunk_position": 10, "heading_path": "Old Links > Old Links", "breadcrumbs": "Useful Links > Old Links > Old Links"}, {"id": "8edc1de41f0890cf", "url": "https://threejs.org/manual/en/webgl-compatibility-check.html", "page_title": "WebGL Compatibility Check", "page_type": "guide", "page_summary": "Explains how to check for WebGL 2 support in Three.js and display a fallback message to users when it is unavailable.", "heading": "WebGL Compatibility Check", "content": "Page: WebGL Compatibility Check\nSection: WebGL Compatibility Check\n\nEven though this is becoming less and less of a problem, some devices or browsers may still not support WebGL 2. The following method allows you to check if it is supported and display a message to the user if it is not. Import the WebGL support detection module, and run the following before attempting to render anything.", "code_blocks": [{"language": "javascript", "code": "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}", "filename": ""}], "chunk_position": 11, "heading_path": "WebGL Compatibility Check > WebGL Compatibility Check", "breadcrumbs": "WebGL Compatibility Check > WebGL Compatibility Check > WebGL Compatibility Check"}, {"id": "620ef6021e8e893d", "url": "https://threejs.org/manual/en/animation-system.html", "page_title": "Animation System", "page_type": "reference", "page_summary": "Extraction fallback content. [stop]", "heading": "Animation System", "content": "Page: Animation System\nSection: Animation System\n\n\n\n\n\n##### Animation System\n\n\n\n\n\n\n\n##### Overview\n\n\n\n\n\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.\n\n\n\n\t\t\tTo achieve all this in one homogeneous system, the three.js animation system\n\t\t\t[has completely changed in 2015](https://github.com/mrdoob/three.js/issues/6881)\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\n\n\n\n\n###### Animation Clips\n\n\n\n\n\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[glTF Blender exporter](https://github.com/KhronosGroup/glTF-Blender-IO) and\n\t\t\tloading it into a three.js scene using `GLTFLoader` — 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).\n\n\n\n\t\t\tEach `AnimationClip` 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\n\n\n\n\n###### Keyframe Tracks\n\n\n\n\n\n\n\t\t\tInside of such an `AnimationClip` the data for each animated property are stored in a\n\t\t\tseparate `KeyframeTrack`. 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.\n\n\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\n\n\n\n\n###### Animation Mixer\n\n\n\n\n\n\n\t\t\tThe stored data forms only the basis for the animations - actual playback is controlled by\n\t\t\tthe `AnimationMixer`. 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\n\n\n\n\n###### Animation Actions\n\n\n\n\n\n\n\t\t\tThe `AnimationMixer` 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`AnimationAction` you can determine when a certain `AnimationClip` 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\n\n\n\n\n###### Animation Object Groups\n\n\n\n\n\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`AnimationObjectGroup`.\n\n\t\t\n\n\n\n\n###### Supported Formats and Loaders\n\n\n\n\n\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 `AnimationClip` sequences. Several that  *do* \n\t\t\tsupport this animation type:\n\t\t\n\n\n\n\n\n\n- THREE.ObjectLoader\n\n- THREE.BVHLoader\n\n- THREE.ColladaLoader\n\n- THREE.FBXLoader\n\n- THREE.GLTFLoader\n\n\n\n\n\n\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\n\n\n\n\n##### Example\n\n\n\n\n\n", "code_blocks": [{"language": "js", "code": "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} );", "filename": ""}], "chunk_position": 12, "heading_path": "Animation System > Animation System", "breadcrumbs": "Animation System > Animation System > Animation System"}, {"id": "6b10c9f78681e9ae", "url": "https://threejs.org/manual/en/color-management.html", "page_title": "Color Management", "page_type": "guide", "page_summary": "An overview of color space concepts in three.js, covering color primaries, white points, transfer functions, and how input, working, and output color spaces are managed during rendering. Includes guidance on working with THREE.Color instances and common pitfalls.", "heading": "What is a color space?", "content": "Page: Color Management\nSection: What is a color space?\n\nEvery color space is a collection of several design decisions, chosen together to support a large range of colors while satisfying technical constraints related to precision and display technologies. When creating a 3D asset, or assembling 3D assets together into a scene, it is important to know what these properties are, and how the properties of one color space relate to other color spaces in the scene.\n\nsRGB colors and white point (D65) displayed in the reference CIE 1931 chromaticity diagram. Colored region represents a 2D projection of the sRGB gamut, which is a 3D volume.\n\n- Color primaries: Primary colors (e.g. red, green, blue) are not absolutes; they are selected from the visible spectrum based on constraints of limited precision and capabilities of available display devices. Colors are expressed as a ratio of the primary colors.\n\n- White point: Most color spaces are engineered such that an equally weighted sum of primaries R = G = B will appear to be without color, or \"achromatic\". The appearance of achromatic values (like white or grey) depend on human perception, which in turn depends heavily on the context of the observer. A color space specifies its \"white point\" to balance these needs. The white point defined by the sRGB color space is D65.\n\n- Transfer functions: After choosing the color gamut and a color model, we still need to define mappings (\"transfer functions\") of numerical values to/from the color space. Does r = 0.5 represent 50% less physical illumination than r = 1.0? Or 50% less bright, as perceived by an average human eye? These are different things, and that difference can be represented as a mathematical function. Transfer functions may be linear or nonlinear, depending on the objectives of the color space. sRGB defines nonlinear transfer functions. Those functions are sometimes approximated as gamma functions, but the term \"gamma\" is ambiguous and should be avoided in this context.\n\nThese three parameters — color primaries, white point, and transfer functions — define a color space, with each chosen for particular goals. Having defined the parameters, a few additional terms are helpful:\n\n- Color model: Syntax for numerically identifying colors within chosen the color gamut — a coordinate system for colors. In three.js we're mainly concerned with the RGB color model, having three coordinates r, g, b ∈ [0,1] (\"closed domain\") or r, g, b ∈ [0,∞] (\"open domain\") each representing a fraction of a primary color. Other color models (HSL, Lab, LCH) are commonly used for artistic control.\n\n- Color gamut: Once color primaries and a white point have been chosen, these represent a volume within the visible spectrum (a \"gamut\"). Colors not within this volume (\"out of gamut\") cannot be expressed by closed domain [0,1] RGB values. In the open domain [0,∞], the gamut is technically infinite.\n\nConsider two very common color spaces: SRGBColorSpace (\"sRGB\") and LinearSRGBColorSpace (\"Linear-sRGB\"). Both use the same primaries and white point, and therefore have the same color gamut. Both use the RGB color model. They differ only in the transfer functions — Linear-sRGB is linear with respect to physical light intensity. sRGB uses the nonlinear sRGB transfer functions, and more closely resembles the way that the human eye perceives light and the responsiveness of common display devices.\n\nThat difference is important. Lighting calculations and other rendering operations must generally occur in a linear color space. However, a linear colors are less efficient to store in an image or framebuffer, and do not look correct when viewed by a human observer. As a result, input textures and the final rendered image will generally use the nonlinear sRGB color space.\n\nNOTICE: While some modern displays support wider gamuts like Display-P3, the web platform's graphics APIs largely rely on sRGB. Applications using three.js today will typically use only the sRGB and Linear-sRGB color spaces.", "code_blocks": [], "chunk_position": 13, "heading_path": "What is a color space? > What is a color space?", "breadcrumbs": "Color Management > What is a color space? > What is a color space?"}, {"id": "e297db6ba510ed3f", "url": "https://threejs.org/manual/en/color-management.html", "page_title": "Color Management", "page_type": "guide", "page_summary": "An overview of color space concepts in three.js, covering color primaries, white points, transfer functions, and how input, working, and output color spaces are managed during rendering. Includes guidance on working with THREE.Color instances and common pitfalls.", "heading": "Roles of color spaces", "content": "Page: Color Management\nSection: Roles of color spaces\n\nLinear workflows — required for modern rendering methods — generally involve more than one color space, each assigned to a particular role. Linear and nonlinear color spaces are appropriate for different roles, explained below.", "code_blocks": [], "chunk_position": 13, "heading_path": "Roles of color spaces > Roles of color spaces", "breadcrumbs": "Color Management > Roles of color spaces > Roles of color spaces"}, {"id": "8fa28cb7b8dc6463", "url": "https://threejs.org/manual/en/color-management.html", "page_title": "Color Management", "page_type": "guide", "page_summary": "An overview of color space concepts in three.js, covering color primaries, white points, transfer functions, and how input, working, and output color spaces are managed during rendering. Includes guidance on working with THREE.Color instances and common pitfalls.", "heading": "Input color space", "content": "Page: Color Management\nSection: Input color space\n\nColors supplied to three.js — from color pickers, textures, 3D models, and other sources — each have an associated color space. Those not already in the Linear-sRGB working color space must be converted, and textures be given the correct texture.colorSpace assignment. Certain conversions (for hexadecimal and CSS colors in sRGB) can be made automatically if the THREE.ColorManagement API is enabled before initializing colors:\n\nTHREE.ColorManagement is enabled by default.\n\n- Materials, lights, and shaders: Colors in materials, lights, and shaders store RGB components in the Linear-sRGB working color space.\n\n- Vertex colors: BufferAttribute store RGB components in the Linear-sRGB working color space.\n\n- Color textures: PNG or JPEG Texture containing color information (like .map or .emissiveMap) use the closed domain sRGB color space, and must be annotated with texture.colorSpace = SRGBColorSpace. Formats like OpenEXR (sometimes used for .envMap or .lightMap) use the Linear-sRGB color space indicated with texture.colorSpace = LinearSRGBColorSpace, and may contain values in the open domain [0,∞].\n\n- Non-color textures: Textures that do not store color information (like .normalMap or .roughnessMap) do not have an associated color space, and generally use the (default) texture annotation of texture.colorSpace = NoColorSpace. In rare cases, non-color data may be represented with other nonlinear encodings for technical reasons.\n\nWARNING: Many formats for 3D models do not correctly or consistently define color space information. While three.js attempts to handle most cases, problems are common with older file formats. For best results, use glTF 2.0 (GLTFLoader) and test 3D models in online viewers early to confirm the asset itself is correct.", "code_blocks": [{"language": "javascript", "code": "THREE.ColorManagement.enabled = true;", "filename": ""}], "chunk_position": 13, "heading_path": "Input color space > Input color space", "breadcrumbs": "Color Management > Input color space > Input color space"}, {"id": "f39d180be7178fb7", "url": "https://threejs.org/manual/en/color-management.html", "page_title": "Color Management", "page_type": "guide", "page_summary": "An overview of color space concepts in three.js, covering color primaries, white points, transfer functions, and how input, working, and output color spaces are managed during rendering. Includes guidance on working with THREE.Color instances and common pitfalls.", "heading": "Working color space", "content": "Page: Color Management\nSection: Working color space\n\nRendering, interpolation, and many other operations must be performed in an open domain linear working color space, in which RGB components are proportional to physical illumination. In three.js, the working color space is Linear-sRGB.", "code_blocks": [], "chunk_position": 13, "heading_path": "Working color space > Working color space", "breadcrumbs": "Color Management > Working color space > Working color space"}, {"id": "e8fc714d81dcae70", "url": "https://threejs.org/manual/en/color-management.html", "page_title": "Color Management", "page_type": "guide", "page_summary": "An overview of color space concepts in three.js, covering color primaries, white points, transfer functions, and how input, working, and output color spaces are managed during rendering. Includes guidance on working with THREE.Color instances and common pitfalls.", "heading": "Output color space", "content": "Page: Color Management\nSection: Output color space\n\nOutput to a display device, image, or video may involve conversion from the open domain Linear-sRGB working color space to another color space. The conversion is defined by (WebGLRenderer.outputColorSpace). When using post-processing, this requires OutputPass.\n\n- Display: Colors written to a WebGL canvas for display should be in the sRGB color space.\n\n- Image: Colors written to an image should use the color space appropriate for the format and usage. Fully-rendered images written to PNG or JPEG textures generally use the sRGB color space. Images containing emission, light maps, or other data not confined to the [0,1] range will generally use the open domain Linear-sRGB color space, and a compatible image format like OpenEXR.\n\nWARNING: Render targets may use either sRGB or Linear-sRGB. sRGB makes better use of limited precision. In the closed domain, 8 bits often suffice for sRGB whereas ≥12 bits (half float) may be required for Linear-sRGB. If later pipeline stages require Linear-sRGB input, the additional conversions may have a small performance cost.\n\nCustom materials based on ShaderMaterial and RawShaderMaterial have to implement their own output color space conversion. For instances of ShaderMaterial, adding the colorspace_fragment shader chunk to the fragment shader's main() function should be sufficient.", "code_blocks": [], "chunk_position": 13, "heading_path": "Output color space > Output color space", "breadcrumbs": "Color Management > Output color space > Output color space"}, {"id": "e251dbacb7204080", "url": "https://threejs.org/manual/en/color-management.html", "page_title": "Color Management", "page_type": "guide", "page_summary": "An overview of color space concepts in three.js, covering color primaries, white points, transfer functions, and how input, working, and output color spaces are managed during rendering. Includes guidance on working with THREE.Color instances and common pitfalls.", "heading": "Working with THREE.Color instances", "content": "Page: Color Management\nSection: Working with THREE.Color instances\n\nMethods reading or modifying Color instances assume data is already in the three.js working color space, Linear-sRGB. RGB and HSL components are direct representations of data stored by the Color instance, and are never converted implicitly. Color data may be explicitly converted with .convertLinearToSRGB() or .convertSRGBToLinear().\n\nWith ColorManagement.enabled = true set (recommended), certain conversions are made automatically. Because hexadecimal and CSS colors are generally sRGB, Color methods will automatically convert these inputs from sRGB to Linear-sRGB in setters, or convert from Linear-sRGB to sRGB when returning hexadecimal or CSS output from getters.", "code_blocks": [{"language": "javascript", "code": "// 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", "filename": ""}, {"language": "javascript", "code": "// 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", "filename": ""}], "chunk_position": 13, "heading_path": "Working with THREE.Color instances > Working with THREE.Color instances", "breadcrumbs": "Color Management > Working with THREE.Color instances > Working with THREE.Color instances"}, {"id": "fc5d3817cd823895", "url": "https://threejs.org/manual/en/color-management.html", "page_title": "Color Management", "page_type": "guide", "page_summary": "An overview of color space concepts in three.js, covering color primaries, white points, transfer functions, and how input, working, and output color spaces are managed during rendering. Includes guidance on working with THREE.Color instances and common pitfalls.", "heading": "Common mistakes", "content": "Page: Color Management\nSection: Common mistakes\n\nWhen an individual color or texture is misconfigured, it will appear darker or lighter than expected. When the renderer's output color space is misconfigured, the entire scene may appear darker (e.g. missing conversion to sRGB) or lighter (e.g. a double conversion to sRGB with post-processing). In each case the problem may not be uniform, and simply increasing/decreasing lighting does not solve it.\n\nA more subtle issue appears when both the input color spaces and the output color spaces are incorrect — the overall brightness levels may be fine, but colors may change unexpectedly under different lighting, or shading may appear more blown-out and less soft than intended. These two wrongs do not make a right, and it's important that the working color space be linear (\"scene referred\") and the output color space be nonlinear (\"display referred\").", "code_blocks": [], "chunk_position": 13, "heading_path": "Common mistakes > Common mistakes", "breadcrumbs": "Color Management > Common mistakes > Common mistakes"}, {"id": "1fd7b165a9e0d25a", "url": "https://threejs.org/manual/en/color-management.html", "page_title": "Color Management", "page_type": "guide", "page_summary": "An overview of color space concepts in three.js, covering color primaries, white points, transfer functions, and how input, working, and output color spaces are managed during rendering. Includes guidance on working with THREE.Color instances and common pitfalls.", "heading": "Further reading", "content": "Page: Color Management\nSection: Further reading\n\n- GPU Gems 3: The Importance of Being Linear, by Larry Gritz and Eugene d'Eon\n- What every coder should know about gamma, by John Novak\n- The Hitchhiker's Guide to Digital Color, by Troy Sobotka\n- Color Management, Blender", "code_blocks": [], "chunk_position": 13, "heading_path": "Further reading > Further reading", "breadcrumbs": "Color Management > Further reading > Further reading"}, {"id": "f82a8c9631cfa40d", "url": "https://threejs.org/manual/en/how-to-create-vr-content.html", "page_title": "How to create VR content", "page_type": "guide", "page_summary": "This guide provides a brief overview of the basic components of a web-based VR application made with three.js, covering VRButton setup, XR rendering enablement, and the animation loop.", "heading": "Overview", "content": "Page: How to create VR content\nSection: Overview\n\nThis guide provides a brief overview of the basic components of a web-based VR application made with three.js.", "code_blocks": [], "chunk_position": 14, "heading_path": "Overview > Overview", "breadcrumbs": "How to create VR content > Overview > Overview"}, {"id": "e62b4f7767fb26fd", "url": "https://threejs.org/manual/en/how-to-create-vr-content.html", "page_title": "How to create VR content", "page_type": "guide", "page_summary": "This guide provides a brief overview of the basic components of a web-based VR application made with three.js, covering VRButton setup, XR rendering enablement, and the animation loop.", "heading": "Workflow", "content": "Page: How to create VR content\nSection: Workflow\n\nFirst, you have to include VRButton.js into your project.\n\nVRButton.createButton() does two important things: It creates a button which indicates VR compatibility. Besides, it initiates a VR session if the user activates the button. The only thing you have to do is to add the following line of code to your app.\n\nNext, you have to tell your instance of WebGLRenderer to enable XR rendering.\n\nFinally, you have to adjust your animation loop since we can't use our well known window.requestAnimationFrame() function. For VR projects we use renderer.setAnimationLoop(). The minimal code looks like this:", "code_blocks": [{"language": "javascript", "code": "import { VRButton } from 'three/addons/webxr/VRButton.js';", "filename": ""}, {"language": "javascript", "code": "document.body.appendChild( VRButton.createButton( renderer ) );", "filename": ""}, {"language": "javascript", "code": "renderer.xr.enabled = true;", "filename": ""}, {"language": "javascript", "code": "renderer.setAnimationLoop( function () {\n\n  renderer.render( scene, camera );\n\n} );", "filename": ""}], "chunk_position": 14, "heading_path": "Workflow > Workflow", "breadcrumbs": "How to create VR content > Workflow > Workflow"}, {"id": "e4d56cdd7cc79e14", "url": "https://threejs.org/manual/en/how-to-create-vr-content.html", "page_title": "How to create VR content", "page_type": "guide", "page_summary": "This guide provides a brief overview of the basic components of a web-based VR application made with three.js, covering VRButton setup, XR rendering enablement, and the animation loop.", "heading": "Next Steps", "content": "Page: How to create VR content\nSection: Next Steps\n\nHave a look at one of the official WebVR examples to see this workflow in action.\n\nWebXR / XR / ballshooter\nWebXR / XR / cubes\nWebXR / XR / dragging\nWebXR / XR / marching cubes\nWebXR / XR / paint\nWebXR / VR / panorama_depth\nWebXR / VR / panorama\nWebXR / VR / rollercoaster\nWebXR / VR / sandbox\nWebXR / VR / video", "code_blocks": [], "chunk_position": 14, "heading_path": "Next Steps > Next Steps", "breadcrumbs": "How to create VR content > Next Steps > Next Steps"}, {"id": "b15b349706f225fb", "url": "https://threejs.org/manual/en/how-to-dispose-of-objects.html", "page_title": "How to dispose of Objects", "page_type": "guide", "page_summary": "A guide covering how to properly dispose of three.js objects such as geometries, materials, textures, render targets, and skinned meshes to free WebGL-related resources and avoid memory leaks.", "heading": "How to dispose of Objects", "content": "Page: How to dispose of Objects\nSection: How to dispose of Objects\n\nOne important aspect in order to improve performance and avoid memory leaks in your application is the disposal of unused library entities. Whenever you create an instance of a *three.js* type, you allocate a certain amount of memory. However, *three.js* creates for specific objects like geometries or materials WebGL related entities like buffers or shader programs which are necessary for rendering. It's important to highlight that these objects are not released automatically. Instead, the application has to use a special API in order to free such resources. This guide provides a brief overview about how this API is used and what objects are relevant in this context.", "code_blocks": [], "chunk_position": 15, "heading_path": "How to dispose of Objects > How to dispose of Objects", "breadcrumbs": "How to dispose of Objects > How to dispose of Objects > How to dispose of Objects"}, {"id": "ebaf975496d060ac", "url": "https://threejs.org/manual/en/how-to-dispose-of-objects.html", "page_title": "How to dispose of Objects", "page_type": "guide", "page_summary": "A guide covering how to properly dispose of three.js objects such as geometries, materials, textures, render targets, and skinned meshes to free WebGL-related resources and avoid memory leaks.", "heading": "Geometries", "content": "Page: How to dispose of Objects\nSection: Geometries\n\nA geometry usually represents vertex information defined as a collection of attributes. *three.js* internally creates an object of type [WebGLBuffer](https://developer.mozilla.org/en-US/docs/Web/API/WebGLBuffer) for each attribute. These entities are only deleted if you call `BufferGeometry.dispose()`. If a geometry becomes obsolete in your application, execute the method to free all related resources.", "code_blocks": [], "chunk_position": 15, "heading_path": "Geometries > Geometries", "breadcrumbs": "How to dispose of Objects > Geometries > Geometries"}, {"id": "e2c2e42da7246719", "url": "https://threejs.org/manual/en/how-to-dispose-of-objects.html", "page_title": "How to dispose of Objects", "page_type": "guide", "page_summary": "A guide covering how to properly dispose of three.js objects such as geometries, materials, textures, render targets, and skinned meshes to free WebGL-related resources and avoid memory leaks.", "heading": "Materials", "content": "Page: How to dispose of Objects\nSection: Materials\n\nA material defines how objects are rendered. *three.js* uses the information of a material definition in order to construct a shader program for rendering. Shader programs can only be deleted if the respective material is disposed. For performance reasons, *three.js* tries to reuse existing 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 executing `Material.dispose()`.", "code_blocks": [], "chunk_position": 15, "heading_path": "Materials > Materials", "breadcrumbs": "How to dispose of Objects > Materials > Materials"}, {"id": "7b169b4e8a68d8c3", "url": "https://threejs.org/manual/en/how-to-dispose-of-objects.html", "page_title": "How to dispose of Objects", "page_type": "guide", "page_summary": "A guide covering how to properly dispose of three.js objects such as geometries, materials, textures, render targets, and skinned meshes to free WebGL-related resources and avoid memory leaks.", "heading": "Textures", "content": "Page: How to dispose of Objects\nSection: Textures\n\nThe 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. Whenever you create an instance of `Texture`, three.js internally creates an instance of [WebGLTexture](https://developer.mozilla.org/en-US/docs/Web/API/WebGLTexture). Similar to buffers, this object can only be deleted by calling `Texture.dispose()`.\n\nIf you use an `ImageBitmap` as the texture's data source, you have to call [ImageBitmap.close](https://developer.mozilla.org/en-US/docs/Web/API/ImageBitmap/close)() at the application level to dispose of all CPU-side resources. An automated call of `ImageBitmap.close()` in `Texture.dispose()` is not possible, since the image bitmap becomes unusable, and the engine has no way of knowing if the image bitmap is used elsewhere.", "code_blocks": [], "chunk_position": 15, "heading_path": "Textures > Textures", "breadcrumbs": "How to dispose of Objects > Textures > Textures"}, {"id": "1c5632963ef7d37a", "url": "https://threejs.org/manual/en/how-to-dispose-of-objects.html", "page_title": "How to dispose of Objects", "page_type": "guide", "page_summary": "A guide covering how to properly dispose of three.js objects such as geometries, materials, textures, render targets, and skinned meshes to free WebGL-related resources and avoid memory leaks.", "heading": "Render Targets", "content": "Page: How to dispose of Objects\nSection: Render Targets\n\nObjects of type `WebGLRenderTarget` not only allocate an instance of [WebGLTexture](https://developer.mozilla.org/en-US/docs/Web/API/WebGLTexture) but also [WebGLFramebuffer](https://developer.mozilla.org/en-US/docs/Web/API/WebGLFramebuffer)s and [WebGLRenderbuffer](https://developer.mozilla.org/en-US/docs/Web/API/WebGLRenderbuffer)s for realizing custom rendering destinations. These objects are only deallocated by executing `WebGLRenderTarget.dispose()`.", "code_blocks": [], "chunk_position": 15, "heading_path": "Render Targets > Render Targets", "breadcrumbs": "How to dispose of Objects > Render Targets > Render Targets"}, {"id": "563aee03fbcff02c", "url": "https://threejs.org/manual/en/how-to-dispose-of-objects.html", "page_title": "How to dispose of Objects", "page_type": "guide", "page_summary": "A guide covering how to properly dispose of three.js objects such as geometries, materials, textures, render targets, and skinned meshes to free WebGL-related resources and avoid memory leaks.", "heading": "Skinned Mesh", "content": "Page: How to dispose of Objects\nSection: Skinned Mesh\n\nSkinned meshes represent their bone hierarchy as skeletons. If you don't need a skinned mesh anymore, consider to call `Skeleton.dispose()` on the skeleton to free internal resources. Keep in mind that skeletons can be shared across multiple skinned meshes, so only call `dispose()` if the skeleton is not used by other active skinned meshes.", "code_blocks": [], "chunk_position": 15, "heading_path": "Skinned Mesh > Skinned Mesh", "breadcrumbs": "How to dispose of Objects > Skinned Mesh > Skinned Mesh"}, {"id": "45acf6d3e52a4d88", "url": "https://threejs.org/manual/en/how-to-dispose-of-objects.html", "page_title": "How to dispose of Objects", "page_type": "guide", "page_summary": "A guide covering how to properly dispose of three.js objects such as geometries, materials, textures, render targets, and skinned meshes to free WebGL-related resources and avoid memory leaks.", "heading": "Miscellaneous", "content": "Page: How to dispose of Objects\nSection: Miscellaneous\n\nThere are other classes from the examples directory like controls or post processing passes which provide `dispose()` methods in order to remove internal event listeners or render targets. In general, it's recommended to check the API or documentation of a class and watch for `dispose()`. If present, you should use it when cleaning things up.", "code_blocks": [], "chunk_position": 15, "heading_path": "Miscellaneous > Miscellaneous", "breadcrumbs": "How to dispose of Objects > Miscellaneous > Miscellaneous"}, {"id": "a6c49ed8ff30ac5d", "url": "https://threejs.org/manual/en/how-to-dispose-of-objects.html", "page_title": "How to dispose of Objects", "page_type": "guide", "page_summary": "A guide covering how to properly dispose of three.js objects such as geometries, materials, textures, render targets, and skinned meshes to free WebGL-related resources and avoid memory leaks.", "heading": "Why can't *three.js* dispose objects automatically?", "content": "Page: How to dispose of Objects\nSection: Why can't *three.js* dispose objects automatically?\n\nThis 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 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, 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 `dispose()` method.", "code_blocks": [], "chunk_position": 15, "heading_path": "Why can't *three.js* dispose objects automatically? > Why can't *three.js* dispose objects automatically?", "breadcrumbs": "How to dispose of Objects > Why can't *three.js* dispose objects automatically? > Why can't *three.js* dispose objects automatically?"}, {"id": "377432e415b9965a", "url": "https://threejs.org/manual/en/how-to-dispose-of-objects.html", "page_title": "How to dispose of Objects", "page_type": "guide", "page_summary": "A guide covering how to properly dispose of three.js objects such as geometries, materials, textures, render targets, and skinned meshes to free WebGL-related resources and avoid memory leaks.", "heading": "Does removing a mesh from the scene also dispose its geometry and material?", "content": "Page: How to dispose of Objects\nSection: Does removing a mesh from the scene also dispose its geometry and material?\n\nNo, 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.", "code_blocks": [], "chunk_position": 15, "heading_path": "Does removing a mesh from the scene also dispose its geometry and material? > Does removing a mesh from the scene also dispose its geometry and material?", "breadcrumbs": "How to dispose of Objects > Does removing a mesh from the scene also dispose its geometry and material? > Does removing a mesh from the scene also dispose its geometry and material?"}, {"id": "09789c8b41212d70", "url": "https://threejs.org/manual/en/how-to-dispose-of-objects.html", "page_title": "How to dispose of Objects", "page_type": "guide", "page_summary": "A guide covering how to properly dispose of three.js objects such as geometries, materials, textures, render targets, and skinned meshes to free WebGL-related resources and avoid memory leaks.", "heading": "Does *three.js* provide information about the amount of cached objects?", "content": "Page: How to dispose of Objects\nSection: Does *three.js* provide information about the amount of cached objects?\n\nYes. It's possible to evaluate `renderer.info`, a special property of the renderer with a series of statistical information about the graphics board memory 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 in your application, it's a good idea to debug this property in order to easily identify a memory leak.", "code_blocks": [], "chunk_position": 15, "heading_path": "Does *three.js* provide information about the amount of cached objects? > Does *three.js* provide information about the amount of cached objects?", "breadcrumbs": "How to dispose of Objects > Does *three.js* provide information about the amount of cached objects? > Does *three.js* provide information about the amount of cached objects?"}, {"id": "e2eebf0eb6a099bb", "url": "https://threejs.org/manual/en/how-to-dispose-of-objects.html", "page_title": "How to dispose of Objects", "page_type": "guide", "page_summary": "A guide covering how to properly dispose of three.js objects such as geometries, materials, textures, render targets, and skinned meshes to free WebGL-related resources and avoid memory leaks.", "heading": "What happens when you call `dispose()` on a texture but the image is not loaded yet?", "content": "Page: How to dispose of Objects\nSection: What happens when you call `dispose()` on a texture but the image is not loaded yet?\n\nInternal resources for a texture are only allocated if the image has fully loaded. If you dispose a texture before the image was loaded, nothing happens. No resources were allocated so there is also no need for clean up.", "code_blocks": [], "chunk_position": 15, "heading_path": "What happens when you call `dispose()` on a texture but the image is not loaded yet? > What happens when you call `dispose()` on a texture but the image is not loaded yet?", "breadcrumbs": "How to dispose of Objects > What happens when you call `dispose()` on a texture but the image is not loaded yet? > What happens when you call `dispose()` on a texture but the image is not loaded yet?"}, {"id": "4cb048801cd8c99a", "url": "https://threejs.org/manual/en/how-to-dispose-of-objects.html", "page_title": "How to dispose of Objects", "page_type": "guide", "page_summary": "A guide covering how to properly dispose of three.js objects such as geometries, materials, textures, render targets, and skinned meshes to free WebGL-related resources and avoid memory leaks.", "heading": "What happens when I call `dispose()` and then use the respective object at a later point?", "content": "Page: How to dispose of Objects\nSection: What happens when I call `dispose()` and then use the respective object at a later point?\n\nThat depends. For geometries, materials, textures, render targets and post processing passes the deleted internal resources can be created again by the engine. 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\nControls and renderers are an exception. Instances of these classes can not be used after `dispose()` has been called. You have to create new instances in this case.", "code_blocks": [], "chunk_position": 15, "heading_path": "What happens when I call `dispose()` and then use the respective object at a later point? > What happens when I call `dispose()` and then use the respective object at a later point?", "breadcrumbs": "How to dispose of Objects > What happens when I call `dispose()` and then use the respective object at a later point? > What happens when I call `dispose()` and then use the respective object at a later point?"}, {"id": "cd46ffac167cf765", "url": "https://threejs.org/manual/en/how-to-dispose-of-objects.html", "page_title": "How to dispose of Objects", "page_type": "guide", "page_summary": "A guide covering how to properly dispose of three.js objects such as geometries, materials, textures, render targets, and skinned meshes to free WebGL-related resources and avoid memory leaks.", "heading": "How should I manage *three.js* objects in my app? When do I know how to dispose things?", "content": "Page: How to dispose of Objects\nSection: How should I manage *three.js* objects in my app? When do I know how to dispose things?\n\nIn general, there is no definite recommendation for this. It highly depends on the specific use case when calling `dispose()` is appropriate. It's important to highlight that 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 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 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.", "code_blocks": [], "chunk_position": 15, "heading_path": "How should I manage *three.js* objects in my app? When do I know how to dispose things? > How should I manage *three.js* objects in my app? When do I know how to dispose things?", "breadcrumbs": "How to dispose of Objects > How should I manage *three.js* objects in my app? When do I know how to dispose things? > How should I manage *three.js* objects in my app? When do I know how to dispose things?"}, {"id": "f006464c1c5b0109", "url": "https://threejs.org/manual/en/how-to-dispose-of-objects.html", "page_title": "How to dispose of Objects", "page_type": "guide", "page_summary": "A guide covering how to properly dispose of three.js objects such as geometries, materials, textures, render targets, and skinned meshes to free WebGL-related resources and avoid memory leaks.", "heading": "Why `renderer.info.memory` is still reporting geometries and textures after traversing the scene and disposing all reachable textures and geometries?", "content": "Page: How to dispose of Objects\nSection: Why `renderer.info.memory` is still reporting geometries and textures after traversing the scene and disposing all reachable textures and geometries?\n\nIn certain cases, there are some textures and geometries used internally by Three.js that are not reachable when traversing the scene graph in order to be disposed. It is expected that `renderer.info.memory` will still report them even after a full scene cleanup. However, they do not leak, but they are reused on consecutive scene cleanup/repopulating cycles.\n\nThese cases could be related to using `material.envMap`, `scene.background`, `scene.environment`, or other contexts that would require the engine to create textures or geometries for internal use.", "code_blocks": [], "chunk_position": 15, "heading_path": "Why `renderer.info.memory` is still reporting geometries and textures after traversing the scene and disposing all reachable textures and geometries? > Why `renderer.info.memory` is still reporting geometries and textures after traversing the scene and disposing all reachable textures and geometries?", "breadcrumbs": "How to dispose of Objects > Why `renderer.info.memory` is still reporting geometries and textures after traversing the scene and disposing all reachable textures and geometries? > Why `renderer.info.memory` is still reporting geometries and textures after traversing the scene and disposing all reachable textures and geometries?"}, {"id": "275bb5b659907243", "url": "https://threejs.org/manual/en/how-to-dispose-of-objects.html", "page_title": "How to dispose of Objects", "page_type": "guide", "page_summary": "A guide covering how to properly dispose of three.js objects such as geometries, materials, textures, render targets, and skinned meshes to free WebGL-related resources and avoid memory leaks.", "heading": "Examples that demonstrate the usage of dispose()", "content": "Page: How to dispose of Objects\nSection: Examples that demonstrate the usage of dispose()\n\n[WebGL / test / memory](../../examples/#webgl_test_memory)\n\n[WebGL / test / memory2](../../examples/#webgl_test_memory2)", "code_blocks": [], "chunk_position": 15, "heading_path": "Examples that demonstrate the usage of dispose() > Examples that demonstrate the usage of dispose()", "breadcrumbs": "How to dispose of Objects > Examples that demonstrate the usage of dispose() > Examples that demonstrate the usage of dispose()"}, {"id": "e5d8fe8726e981b1", "url": "https://threejs.org/manual/en/how-to-update-things.html", "page_title": "How to update Things", "page_type": "guide", "page_summary": "A guide explaining how to update various Three.js objects including matrix transforms, BufferGeometries, Materials, Textures, Cameras, InstancedMesh, and SkinnedMesh.", "heading": "How to update Things", "content": "Page: How to update Things\nSection: How to update Things\n\nAll objects by default automatically update their matrices if they have been added to the scene with\n\n```\nconst object = new THREE.Object3D();\nscene.add( object );\n```\n\nor if they are the child of another object that has been added to the scene:\n\n```\nconst 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```\n\nHowever, if you know the object will be static, you can disable this and update the transform matrix manually just when needed.\n\n```\nobject.matrixAutoUpdate = false;\nobject.updateMatrix();\n```", "code_blocks": [{"language": "javascript", "code": "const object = new THREE.Object3D();\nscene.add( object );", "filename": ""}, {"language": "javascript", "code": "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", "filename": ""}, {"language": "javascript", "code": "object.matrixAutoUpdate = false;\nobject.updateMatrix();", "filename": ""}], "chunk_position": 16, "heading_path": "How to update Things > How to update Things", "breadcrumbs": "How to update Things > How to update Things > How to update Things"}, {"id": "8290cf4e3006efac", "url": "https://threejs.org/manual/en/how-to-update-things.html", "page_title": "How to update Things", "page_type": "guide", "page_summary": "A guide explaining how to update various Three.js objects including matrix transforms, BufferGeometries, Materials, Textures, Cameras, InstancedMesh, and SkinnedMesh.", "heading": "BufferGeometry", "content": "Page: How to update Things\nSection: BufferGeometry\n\nBufferGeometries store information (such as vertex positions, face indices, normals, colors, UVs, and any custom attributes) in attribute buffers - that is, typed arrays. This makes them generally faster than standard Geometries, at the cost of being somewhat harder to work with.\n\nWith regards to updating BufferGeometries, the most important thing to understand is that you cannot resize buffers (this is very costly,\tbasically the equivalent to creating a new geometry). You can however update the content of buffers.\n\nThis means that if you know an attribute of your BufferGeometry will grow, say the number of vertices, you must pre-allocate a buffer large enough to hold any new vertices that may be created. Of course, this also means that there will be a maximum size for your BufferGeometry - there is no way to create a BufferGeometry that can efficiently be extended indefinitely.\n\nWe'll use the example of a line that gets extended at render time. We'll allocate space in the buffer for 500 vertices but draw only two at first, using `BufferGeometry.drawRange`.\n\nNext we'll randomly add points to the line using a pattern like:\n\nIf you want to change the number of points rendered after the first render, do this:\n\nIf you want to change the position data values after the first render, you need to set the needsUpdate flag like so:\n\nIf you change the position data values after the initial render, you may need to recompute bounding volumes so other features of the engine like view frustum culling or helpers properly work.\n\nHere is a fiddle showing an animated line which you can adapt to your use case.", "code_blocks": [{"language": "javascript", "code": "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 );", "filename": ""}, {"language": "javascript", "code": "const positionAttribute = line.geometry.getAttribute( 'position' );\n\nlet x = 0, y = 0, z = 0;\n\nfor ( let i = 0; i < 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}", "filename": ""}, {"language": "javascript", "code": "line.geometry.setDrawRange( 0, newValue );", "filename": ""}, {"language": "javascript", "code": "positionAttribute.needsUpdate = true; // required after the first render", "filename": ""}, {"language": "javascript", "code": "line.geometry.computeBoundingBox();\nline.geometry.computeBoundingSphere();", "filename": ""}], "chunk_position": 16, "heading_path": "BufferGeometry > BufferGeometry", "breadcrumbs": "How to update Things > BufferGeometry > BufferGeometry"}, {"id": "b265d7fa2a745e96", "url": "https://threejs.org/manual/en/how-to-update-things.html", "page_title": "How to update Things", "page_type": "guide", "page_summary": "A guide explaining how to update various Three.js objects including matrix transforms, BufferGeometries, Materials, Textures, Cameras, InstancedMesh, and SkinnedMesh.", "heading": "Examples", "content": "Page: How to update Things\nSection: Examples\n\n[WebGL / custom / attributes](../../examples/#webgl_custom_attributes)\n\n[WebGL / buffergeometry / custom / attributes / particles](../../examples/#webgl_buffergeometry_custom_attributes_particles)", "code_blocks": [], "chunk_position": 16, "heading_path": "Examples > Examples", "breadcrumbs": "How to update Things > Examples > Examples"}, {"id": "4eaccc04e06997b2", "url": "https://threejs.org/manual/en/how-to-update-things.html", "page_title": "How to update Things", "page_type": "guide", "page_summary": "A guide explaining how to update various Three.js objects including matrix transforms, BufferGeometries, Materials, Textures, Cameras, InstancedMesh, and SkinnedMesh.", "heading": "Materials", "content": "Page: How to update Things\nSection: Materials\n\nAll uniforms values can be changed freely (e.g. colors, textures, opacity, etc), values are sent to the shader every frame.\n\nAlso GLstate related parameters can change any time (depthTest, blending, polygonOffset, etc).\n\nThe following properties can't be easily changed at runtime (once the material is rendered at least once):\n\n- numbers and types of uniforms\n- presence or not of\n                 \n- texture\n- fog\n- vertex colors\n- morphing\n- shadow map\n- alpha test\n- transparent\n\nChanges in these require building of new shader program. You'll need to set\n\n`material.needsUpdate = true`\n\nBear in mind this might be quite slow and induce jerkiness in framerate (especially on Windows, as shader compilation is slower in DirectX than OpenGL).\n\nFor 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.\n\nYou can freely change the material used for geometry chunks, however you cannot change how an object is divided into chunks (according to face materials).", "code_blocks": [], "chunk_position": 16, "heading_path": "Materials > Materials", "breadcrumbs": "How to update Things > Materials > Materials"}, {"id": "068d3742ec637248", "url": "https://threejs.org/manual/en/how-to-update-things.html", "page_title": "How to update Things", "page_type": "guide", "page_summary": "A guide explaining how to update various Three.js objects including matrix transforms, BufferGeometries, Materials, Textures, Cameras, InstancedMesh, and SkinnedMesh.", "heading": "If you need to have different configurations of materials during runtime:", "content": "Page: How to update Things\nSection: If you need to have different configurations of materials during runtime:\n\nIf 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).\n\nIf 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.", "code_blocks": [], "chunk_position": 16, "heading_path": "If you need to have different configurations of materials during runtime: > If you need to have different configurations of materials during runtime:", "breadcrumbs": "How to update Things > If you need to have different configurations of materials during runtime: > If you need to have different configurations of materials during runtime:"}, {"id": "5ae512a0ea94312a", "url": "https://threejs.org/manual/en/how-to-update-things.html", "page_title": "How to update Things", "page_type": "guide", "page_summary": "A guide explaining how to update various Three.js objects including matrix transforms, BufferGeometries, Materials, Textures, Cameras, InstancedMesh, and SkinnedMesh.", "heading": "Examples", "content": "Page: How to update Things\nSection: Examples\n\n[WebGL / materials / car](../../examples/#webgl_materials_car)\n\n[WebGL / webgl_postprocessing / dof](../../examples/#webgl_postprocessing_dof)", "code_blocks": [], "chunk_position": 16, "heading_path": "Examples > Examples", "breadcrumbs": "How to update Things > Examples > Examples"}, {"id": "ae327d4802c6e1bc", "url": "https://threejs.org/manual/en/how-to-update-things.html", "page_title": "How to update Things", "page_type": "guide", "page_summary": "A guide explaining how to update various Three.js objects including matrix transforms, BufferGeometries, Materials, Textures, Cameras, InstancedMesh, and SkinnedMesh.", "heading": "Textures", "content": "Page: How to update Things\nSection: Textures\n\nImage, canvas, video and data textures need to have the following flag set if they are changed:\n\n`\n              texture.needsUpdate = true;\n            `\n\nRender targets update automatically.", "code_blocks": [{"language": "javascript", "code": "texture.needsUpdate = true;", "filename": ""}], "chunk_position": 16, "heading_path": "Textures > Textures", "breadcrumbs": "How to update Things > Textures > Textures"}, {"id": "fe970e48e09e4969", "url": "https://threejs.org/manual/en/how-to-update-things.html", "page_title": "How to update Things", "page_type": "guide", "page_summary": "A guide explaining how to update various Three.js objects including matrix transforms, BufferGeometries, Materials, Textures, Cameras, InstancedMesh, and SkinnedMesh.", "heading": "Examples", "content": "Page: How to update Things\nSection: Examples\n\n[WebGL / materials / video](../../examples/#webgl_materials_video)\n\n[WebGL / rtt](../../examples/#webgl_rtt)", "code_blocks": [], "chunk_position": 16, "heading_path": "Examples > Examples", "breadcrumbs": "How to update Things > Examples > Examples"}, {"id": "6d5d340d6958bf70", "url": "https://threejs.org/manual/en/how-to-update-things.html", "page_title": "How to update Things", "page_type": "guide", "page_summary": "A guide explaining how to update various Three.js objects including matrix transforms, BufferGeometries, Materials, Textures, Cameras, InstancedMesh, and SkinnedMesh.", "heading": "Cameras", "content": "Page: How to update Things\nSection: Cameras\n\nA camera's position and target is updated automatically. If you need to change\n\n- fov\n- aspect\n- near\n- far\n\nthen you'll need to recompute the projection matrix:", "code_blocks": [{"language": "javascript", "code": "camera.aspect = window.innerWidth / window.innerHeight;\ncamera.updateProjectionMatrix();", "filename": ""}], "chunk_position": 16, "heading_path": "Cameras > Cameras", "breadcrumbs": "How to update Things > Cameras > Cameras"}, {"id": "5ce5707eaf37527b", "url": "https://threejs.org/manual/en/how-to-update-things.html", "page_title": "How to update Things", "page_type": "guide", "page_summary": "A guide explaining how to update various Three.js objects including matrix transforms, BufferGeometries, Materials, Textures, Cameras, InstancedMesh, and SkinnedMesh.", "heading": "InstancedMesh", "content": "Page: How to update Things\nSection: InstancedMesh\n\n`InstancedMesh` is a class for conveniently access instanced rendering in `three.js`. Certain library features like view frustum culling or ray casting rely on up-to-date bounding volumes (bounding sphere and bounding box). Because of the way how `InstancedMesh` works, the class has its own `boundingBox` and `boundingSphere` properties that supersede the bounding volumes on geometry level.\n\nSimilar to geometries you have to recompute the bounding box and sphere whenever you change the underlying data. In context of `InstancedMesh`, that happens when you transform instances via `setMatrixAt()`. You can use the same pattern like with geometries.", "code_blocks": [{"language": "javascript", "code": "instancedMesh.computeBoundingBox();\ninstancedMesh.computeBoundingSphere();", "filename": ""}], "chunk_position": 16, "heading_path": "InstancedMesh > InstancedMesh", "breadcrumbs": "How to update Things > InstancedMesh > InstancedMesh"}, {"id": "d88c0fe02ea5aec5", "url": "https://threejs.org/manual/en/how-to-update-things.html", "page_title": "How to update Things", "page_type": "guide", "page_summary": "A guide explaining how to update various Three.js objects including matrix transforms, BufferGeometries, Materials, Textures, Cameras, InstancedMesh, and SkinnedMesh.", "heading": "SkinnedMesh", "content": "Page: How to update Things\nSection: SkinnedMesh\n\n`SkinnedMesh` follows the same principles like `InstancedMesh` in context of bounding volumes. Meaning the class has its own version of `boundingBox` and `boundingSphere` to correctly enclose animated meshes. When calling `computeBoundingBox()` and `computeBoundingSphere()`, the class computes the respective bounding volumes based on the current bone transformation (or in other words the current animation state).", "code_blocks": [], "chunk_position": 16, "heading_path": "SkinnedMesh > SkinnedMesh", "breadcrumbs": "How to update Things > SkinnedMesh > SkinnedMesh"}, {"id": "bd8ad860dd4b38d3", "url": "https://threejs.org/manual/en/how-to-use-post-processing.html", "page_title": "How to use Post Processing", "page_type": "guide", "page_summary": "A guide explaining how to use three.js's post-processing pipeline via EffectComposer to apply graphical effects such as Depth-Of-Field, Bloom, Film Grain, and Anti-aliasing to rendered scenes.", "heading": "How to use Post Processing", "content": "Page: How to use Post Processing\nSection: How to use Post Processing\n\nMany three.js applications render their 3D objects directly to the screen. Sometimes, however, you want to apply one or more graphical effects like Depth-Of-Field, Bloom, Film Grain or various types of Anti-aliasing. Post-processing is a widely used approach to implement such effects. First, the scene is rendered to a render target which represents a buffer in the video card's memory. In the next step one or more post-processing passes apply filters and effects to the image buffer before it is eventually rendered to the screen.\n\nthree.js provides a complete post-processing solution via `EffectComposer` to implement such a workflow.", "code_blocks": [], "chunk_position": 17, "heading_path": "How to use Post Processing > How to use Post Processing", "breadcrumbs": "How to use Post Processing > How to use Post Processing > How to use Post Processing"}, {"id": "427ed9c1f6dfd80e", "url": "https://threejs.org/manual/en/how-to-use-post-processing.html", "page_title": "How to use Post Processing", "page_type": "guide", "page_summary": "A guide explaining how to use three.js's post-processing pipeline via EffectComposer to apply graphical effects such as Depth-Of-Field, Bloom, Film Grain, and Anti-aliasing to rendered scenes.", "heading": "Workflow", "content": "Page: How to use Post Processing\nSection: Workflow\n\nThe first step in the process is to import all necessary files from the examples directory. The guide assumes you are using the official npm package of three.js. For our basic demo in this guide we need the following files.\n\nAfter all files are successfully imported, we can create our composer by passing in an instance of `WebGLRenderer`.\n\nWhen using a composer, it's necessary to change the application's animation loop. Instead of calling the render method of `WebGLRenderer`, we now use the respective counterpart of `EffectComposer`.\n\nOur composer is now ready so it's possible to configure the chain of post-processing passes. These passes are responsible for creating the final visual output of the application. They are processed in order of their addition/insertion. In our example, the instance of `RenderPass` is executed first, then the instance of `GlitchPass` and finally `OutputPass`. The last enabled pass in the chain is automatically rendered to the screen. The setup of the passes looks like so:\n\n`RenderPass` 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, `GlitchPass` is going to use these image data to apply a wild glitch effect. `OutputPass` is usually the last pass in the chain which performs sRGB color space conversion and tone mapping. Check out this live example to see it in action.", "code_blocks": [{"language": "javascript", "code": "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';", "filename": ""}, {"language": "javascript", "code": "const composer = new EffectComposer( renderer );", "filename": ""}, {"language": "javascript", "code": "function animate() {\n\n  requestAnimationFrame( animate );\n\n  composer.render();\n\n}", "filename": ""}, {"language": "javascript", "code": "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 );", "filename": ""}], "chunk_position": 17, "heading_path": "Workflow > Workflow", "breadcrumbs": "How to use Post Processing > Workflow > Workflow"}, {"id": "967f117f49c2ed42", "url": "https://threejs.org/manual/en/how-to-use-post-processing.html", "page_title": "How to use Post Processing", "page_type": "guide", "page_summary": "A guide explaining how to use three.js's post-processing pipeline via EffectComposer to apply graphical effects such as Depth-Of-Field, Bloom, Film Grain, and Anti-aliasing to rendered scenes.", "heading": "Built-in Passes", "content": "Page: How to use Post Processing\nSection: Built-in Passes\n\nYou can use a wide range of pre-defined post-processing passes provided by the engine. They are located in the postprocessing directory.", "code_blocks": [], "chunk_position": 17, "heading_path": "Built-in Passes > Built-in Passes", "breadcrumbs": "How to use Post Processing > Built-in Passes > Built-in Passes"}, {"id": "995a636e35d4d8a1", "url": "https://threejs.org/manual/en/how-to-use-post-processing.html", "page_title": "How to use Post Processing", "page_type": "guide", "page_summary": "A guide explaining how to use three.js's post-processing pipeline via EffectComposer to apply graphical effects such as Depth-Of-Field, Bloom, Film Grain, and Anti-aliasing to rendered scenes.", "heading": "Custom Passes", "content": "Page: How to use Post Processing\nSection: Custom Passes\n\nSometimes you want to write a custom post-processing shader and include it into the chain of post-processing passes. For this scenario, you can utilize `ShaderPass`. After importing the file and your custom shader, you can use the following code to setup the pass.\n\nThe repository provides a file called CopyShader which is a good starting code for your own custom shader. `CopyShader` just copies the image contents of the `EffectComposer`'s read buffer to its write buffer without applying any effects.", "code_blocks": [{"language": "javascript", "code": "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 );", "filename": ""}], "chunk_position": 17, "heading_path": "Custom Passes > Custom Passes", "breadcrumbs": "How to use Post Processing > Custom Passes > Custom Passes"}, {"id": "37b8b052aeb1b70f", "url": "https://threejs.org/manual/en/matrix-transformations.html", "page_title": "Matrix Transformations", "page_type": "guide", "page_summary": "Explains how Three.js uses matrices to encode 3D transformations (translations, rotations, and scaling) for Object3D instances, including how to update an object's transformation manually or automatically.", "heading": "Matrix Transformations", "content": "Page: Matrix Transformations\nSection: Matrix Transformations\n\nThree.js uses `matrices` to encode 3D transformations---translations (position), rotations, and scaling. Every instance of `Object3D` has a `matrix` which stores that object's position, rotation, and scale. This page describes how to update an object's transformation.", "code_blocks": [], "chunk_position": 18, "heading_path": "Matrix Transformations > Matrix Transformations", "breadcrumbs": "Matrix Transformations > Matrix Transformations > Matrix Transformations"}, {"id": "530f1f79a90df3d7", "url": "https://threejs.org/manual/en/matrix-transformations.html", "page_title": "Matrix Transformations", "page_type": "guide", "page_summary": "Explains how Three.js uses matrices to encode 3D transformations (translations, rotations, and scaling) for Object3D instances, including how to update an object's transformation manually or automatically.", "heading": "Convenience properties and `matrixAutoUpdate`", "content": "Page: Matrix Transformations\nSection: Convenience properties and `matrixAutoUpdate`\n\nThere are two ways to update an object's transformation:\n\n- Modify the object's `position`, `quaternion`, and `scale` properties, and let three.js recompute the object's matrix from these properties:\n\nBy default, the `matrixAutoUpdate` property is set true, and the matrix will be automatically recalculated. 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\nAnd after changing any properties, manually update the matrix:\n\n- Modify the object's matrix directly. The `Matrix4` class has various methods for modifying the matrix:\n\nNote that `matrixAutoUpdate` *must* be set to `false` in this case, and you should make sure *not* to call `updateMatrix`. Calling `updateMatrix` will clobber the manual changes made to the matrix, recalculating the matrix from `position`, `scale`, and so on.", "code_blocks": [{"language": "javascript", "code": "object.position.copy( start_position );\nobject.quaternion.copy( quaternion );", "filename": ""}, {"language": "javascript", "code": "object.matrixAutoUpdate = false;", "filename": ""}, {"language": "javascript", "code": "object.updateMatrix();", "filename": ""}, {"language": "javascript", "code": "object.matrix.makeRotationFromQuaternion( quaternion );\nobject.matrix.setPosition( start_position );\nobject.matrixAutoUpdate = false;", "filename": ""}], "chunk_position": 18, "heading_path": "Convenience properties and `matrixAutoUpdate` > Convenience properties and `matrixAutoUpdate`", "breadcrumbs": "Matrix Transformations > Convenience properties and `matrixAutoUpdate` > Convenience properties and `matrixAutoUpdate`"}, {"id": "c65b71dce3bacaf7", "url": "https://threejs.org/manual/en/matrix-transformations.html", "page_title": "Matrix Transformations", "page_type": "guide", "page_summary": "Explains how Three.js uses matrices to encode 3D transformations (translations, rotations, and scaling) for Object3D instances, including how to update an object's transformation manually or automatically.", "heading": "Object and world matrices", "content": "Page: Matrix Transformations\nSection: Object and world matrices\n\nAn object's matrix stores the object's transformation *relative* to the object's parent; to get the object's transformation in *world* coordinates, you must access the object's world matrix.\n\nWhen either the parent or the child object's transformation changes, you can request that the child object's world matrix be updated by calling `object.updateMatrixWorld()`.\n\nAn object can be transformed via `applyMatrix4()`. Note: Under-the-hood, this method relies on `Matrix4.decompose()`, 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.", "code_blocks": [], "chunk_position": 18, "heading_path": "Object and world matrices > Object and world matrices", "breadcrumbs": "Matrix Transformations > Object and world matrices > Object and world matrices"}, {"id": "c62ad6f843f07262", "url": "https://threejs.org/manual/en/matrix-transformations.html", "page_title": "Matrix Transformations", "page_type": "guide", "page_summary": "Explains how Three.js uses matrices to encode 3D transformations (translations, rotations, and scaling) for Object3D instances, including how to update an object's transformation manually or automatically.", "heading": "Rotation and Quaternion", "content": "Page: Matrix Transformations\nSection: Rotation and Quaternion\n\nThree.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 *always* stored in the object's quaternion.\n\nPrevious versions of the library included a `useQuaternion` 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 `object.setRotationFromEuler()` method, which will update the quaternion.", "code_blocks": [], "chunk_position": 18, "heading_path": "Rotation and Quaternion > Rotation and Quaternion", "breadcrumbs": "Matrix Transformations > Rotation and Quaternion > Rotation and Quaternion"}, {"id": "77e316e59f0af17c", "url": "https://threejs.org/manual/en/fundamentals.html", "page_title": "Fundamentals", "page_type": "guide", "page_summary": "An introductory article covering the basics of three.js, including the structure of a three.js application, building a 'Hello Cube' scene with renderer, camera, geometry, material, and mesh, adding animation and lighting, and setting up ES6 modules with import maps.", "heading": "Introduction to Three.js", "content": "Page: Fundamentals\nSection: Introduction to Three.js\n\nThis is the first article in a series of articles about three.js. Three.js is a 3D library that tries to make it as easy as possible to get 3D content on a webpage.\n\nThree.js is often confused with WebGL since more often than not, but not always, three.js uses WebGL to draw 3D. WebGL is a very low-level system that only draws points, lines, and triangles. To do anything useful with WebGL generally requires quite a bit of code and that is where three.js comes in. It handles stuff like scenes, lights, shadows, materials, textures, 3d math, all things that you'd have to write yourself if you were to use WebGL directly.\n\nThese tutorials assume you already know JavaScript and, for the most part they will use ES6 style. Most browsers that support three.js are auto-updated so most users should be able to run this code. If you'd like to make this code run on really old browsers look into a transpiler like Babel. Of course users running really old browsers probably have machines that can't run three.js.\n\nWhen learning most programming languages the first thing people do is make the computer print \"Hello World!\". For 3D one of the most common first things to do is to make a 3D cube. So let's start with \"Hello Cube!\"", "code_blocks": [], "chunk_position": 19, "heading_path": "Introduction to Three.js > Introduction to Three.js", "breadcrumbs": "Fundamentals > Introduction to Three.js > Introduction to Three.js"}, {"id": "767caf089049a60d", "url": "https://threejs.org/manual/en/fundamentals.html", "page_title": "Fundamentals", "page_type": "guide", "page_summary": "An introductory article covering the basics of three.js, including the structure of a three.js application, building a 'Hello Cube' scene with renderer, camera, geometry, material, and mesh, adding animation and lighting, and setting up ES6 modules with import maps.", "heading": "Structure of a Three.js App", "content": "Page: Fundamentals\nSection: Structure of a Three.js App\n\nBefore we get started let's try to give you an idea of the structure of a three.js app. A three.js app requires you to create a bunch of objects and connect them together.\n\nThings to notice about the diagram:\n\n- There is a Renderer. This is arguably the main object of three.js. You pass a Scene and a Camera to a Renderer and it renders (draws) the portion of the 3D scene that is inside the frustum of the camera as a 2D image to a canvas.\n\n- There is a scenegraph which is a tree like structure, consisting of various objects like a Scene object, multiple Mesh objects, Light objects, Group, Object3D, and Camera objects. A Scene object defines the root of the scenegraph and contains properties like the background color and fog. These objects define a hierarchical parent/child tree like structure and represent where objects appear and how they are oriented. Children are positioned and oriented relative to their parent. For example the wheels on a car might be children of the car so that moving and orienting the car's object automatically moves the wheels.\n\nNote in the diagram Camera is half in half out of the scenegraph. This is to represent that in three.js, unlike the other objects, a Camera does not have to be in the scenegraph to function. Just like other objects, a Camera, as a child of some other object, will move and orient relative to its parent object.\n\n- Mesh objects represent drawing a specific Geometry with a specific Material. Both Material objects and Geometry objects can be used by multiple Mesh objects. For example to draw two blue cubes in different locations we could need two Mesh objects to represent the position and orientation of each cube. We would only need one Geometry to hold the vertex data for a cube and we would only need one Material to specify the color blue. Both Mesh objects could reference the same Geometry object and the same Material object.\n\n- Geometry objects represent the vertex data of some piece of geometry like a sphere, cube, plane, dog, cat, human, tree, building, etc... Three.js provides many kinds of built in geometry primitives. You can also create custom geometry as well as load geometry from files.\n\n- Material objects represent the surface properties used to draw geometry including things like the color to use and how shiny it is. A Material can also reference one or more Texture objects which can be used, for example, to wrap an image onto the surface of a geometry.\n\n- Texture objects generally represent images either loaded from image files, generated from a canvas, or rendered from another scene.\n\n- Light objects represent different kinds of lights.", "code_blocks": [], "chunk_position": 19, "heading_path": "Structure of a Three.js App > Structure of a Three.js App", "breadcrumbs": "Fundamentals > Structure of a Three.js App > Structure of a Three.js App"}, {"id": "f26fb3ec75e54146", "url": "https://threejs.org/manual/en/fundamentals.html", "page_title": "Fundamentals", "page_type": "guide", "page_summary": "An introductory article covering the basics of three.js, including the structure of a three.js application, building a 'Hello Cube' scene with renderer, camera, geometry, material, and mesh, adding animation and lighting, and setting up ES6 modules with import maps.", "heading": "Hello Cube Setup", "content": "Page: Fundamentals\nSection: Hello Cube Setup\n\nGiven all of that we're going to make the smallest \"Hello Cube\" setup.\n\nFirst let's load three.js. It's important you put `type=\"module\"` in the script tag. This enables us to use the `import` keyword to load three.js. As of r147, this is the only way to load three.js properly. Modules have the advantage that they can easily import other modules they need. That saves us from having to manually load extra scripts they are dependent on.\n\nNext we need is a `<canvas>` tag. We will ask three.js to draw into that canvas so we need to look it up.\n\nAfter we look up the canvas we create a WebGLRenderer. The renderer is the thing responsible for actually taking all the data you provide and rendering it to the canvas.\n\nNote there are some esoteric details here. If you don't pass a canvas into three.js it will create one for you but then you have to add it to your document. Where to add it may change depending on your use case and you'll have to change your code so I find that passing a canvas to three.js feels a little more flexible. I can put the canvas anywhere and the code will find it whereas if I had code to insert the canvas into to the document I'd likely have to change that code if my use case changed.", "code_blocks": [{"language": "html", "code": "<script type=\"module\">\nimport * as THREE from 'three';\n</script>", "filename": ""}, {"language": "html", "code": "<body>\n  <canvas id=\"c\"></canvas>\n</body>", "filename": ""}, {"language": "html", "code": "<script type=\"module\">\nimport * as THREE from 'three';\n\nfunction main() {\n  const canvas = document.querySelector('#c');\n  const renderer = new THREE.WebGLRenderer({antialias: true, canvas});\n  ...\n</script>", "filename": ""}], "chunk_position": 19, "heading_path": "Hello Cube Setup > Hello Cube Setup", "breadcrumbs": "Fundamentals > Hello Cube Setup > Hello Cube Setup"}, {"id": "ff8f9d1109770bc3", "url": "https://threejs.org/manual/en/fundamentals.html", "page_title": "Fundamentals", "page_type": "guide", "page_summary": "An introductory article covering the basics of three.js, including the structure of a three.js application, building a 'Hello Cube' scene with renderer, camera, geometry, material, and mesh, adding animation and lighting, and setting up ES6 modules with import maps.", "heading": "Camera Setup", "content": "Page: Fundamentals\nSection: Camera Setup\n\nNext up we need a camera. We'll create a PerspectiveCamera.\n\n`fov` is short for `field of view`. In this case 75 degrees in the vertical dimension. Note that most angles in three.js are in radians but for some reason the perspective camera takes degrees.\n\n`aspect` is the display aspect of the canvas. By default a canvas is 300x150 pixels which makes the aspect 300/150 or 2.\n\n`near` and `far` represent the space in front of the camera that will be rendered. Anything before that range or after that range will be clipped (not drawn).\n\nThose four settings define a \"frustum\". A frustum is the name of a 3d shape that is like a pyramid with the tip sliced off. In other words think of the word \"frustum\" as another 3D shape like sphere, cube, prism, frustum.\n\nThe height of the near and far planes are determined by the field of view. The width of both planes is determined by the field of view and the aspect.\n\nAnything inside the defined frustum will be drawn. Anything outside will not.\n\nThe camera defaults to looking down the -Z axis with +Y up. We'll put our cube at the origin so we need to move the camera back a little from the origin in order to see anything.", "code_blocks": [{"language": "javascript", "code": "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);", "filename": ""}, {"language": "javascript", "code": "camera.position.z = 2;", "filename": ""}], "chunk_position": 19, "heading_path": "Camera Setup > Camera Setup", "breadcrumbs": "Fundamentals > Camera Setup > Camera Setup"}, {"id": "a52d93f5c480da15", "url": "https://threejs.org/manual/en/fundamentals.html", "page_title": "Fundamentals", "page_type": "guide", "page_summary": "An introductory article covering the basics of three.js, including the structure of a three.js application, building a 'Hello Cube' scene with renderer, camera, geometry, material, and mesh, adding animation and lighting, and setting up ES6 modules with import maps.", "heading": "Scene, Geometry, Material, and Mesh", "content": "Page: Fundamentals\nSection: Scene, Geometry, Material, and Mesh\n\nNext we make a Scene. A Scene in three.js is the root of a form of scene graph. Anything you want three.js to draw needs to be added to the scene.\n\nNext up we create a BoxGeometry which contains the data for a box. Almost anything we want to display in Three.js needs geometry which defines the vertices that make up our 3D object.\n\nWe then create a basic material and set its color. Colors can be specified using standard CSS style 6 digit hex color values.\n\nWe then create a Mesh. A Mesh in three.js represents the combination of three things:\n\n- A Geometry (the shape of the object)\n- A Material (how to draw the object, shiny or flat, what color, what texture(s) to apply. Etc.)\n- The position, orientation, and scale of that object in the scene relative to its parent. In the code below that parent is the scene.\n\nAnd finally we add that mesh to the scene. We can then render the scene by calling the renderer's render function and passing it the scene and the camera.\n\nIt's kind of hard to tell that is a 3D cube since we're viewing it directly down the -Z axis and the cube itself is axis aligned so we're only seeing a single face.", "code_blocks": [{"language": "javascript", "code": "const scene = new THREE.Scene();", "filename": ""}, {"language": "javascript", "code": "const boxWidth = 1;\nconst boxHeight = 1;\nconst boxDepth = 1;\nconst geometry = new THREE.BoxGeometry(boxWidth, boxHeight, boxDepth);", "filename": ""}, {"language": "javascript", "code": "const material = new THREE.MeshBasicMaterial({color: 0x44aa88});", "filename": ""}, {"language": "javascript", "code": "const cube = new THREE.Mesh(geometry, material);", "filename": ""}, {"language": "javascript", "code": "scene.add(cube);", "filename": ""}, {"language": "javascript", "code": "renderer.render(scene, camera);", "filename": ""}], "chunk_position": 19, "heading_path": "Scene, Geometry, Material, and Mesh > Scene, Geometry, Material, and Mesh", "breadcrumbs": "Fundamentals > Scene, Geometry, Material, and Mesh > Scene, Geometry, Material, and Mesh"}, {"id": "65127b3af9b5e68b", "url": "https://threejs.org/manual/en/fundamentals.html", "page_title": "Fundamentals", "page_type": "guide", "page_summary": "An introductory article covering the basics of three.js, including the structure of a three.js application, building a 'Hello Cube' scene with renderer, camera, geometry, material, and mesh, adding animation and lighting, and setting up ES6 modules with import maps.", "heading": "Animating with requestAnimationFrame", "content": "Page: Fundamentals\nSection: Animating with requestAnimationFrame\n\nLet's animate it spinning and hopefully that will make it clear it's being drawn in 3D. To animate it we'll render inside a render loop using requestAnimationFrame.\n\n`requestAnimationFrame` is a request to the browser that you want to animate something. You pass it a function to be called. In our case that function is `render`. The browser will call your function and if you update anything related to the display of the page the browser will re-render the page. In our case we are calling three's `renderer.render` function which will draw our scene.\n\n`requestAnimationFrame` passes the time since the page loaded to our function. That time is passed in milliseconds. I find it's much easier to work with seconds so here we're converting that to seconds.\n\nWe then set the cube's X and Y rotation to the current time. These rotations are in radians. There are 2 pi radians in a circle so our cube should turn around once on each axis in about 6.28 seconds.\n\nWe then render the scene and request another animation frame to continue our loop. Outside the loop we call `requestAnimationFrame` one time to start the loop.\n\nIt's a little better but it's still hard to see the 3d.", "code_blocks": [{"language": "javascript", "code": "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);", "filename": ""}], "chunk_position": 19, "heading_path": "Animating with requestAnimationFrame > Animating with requestAnimationFrame", "breadcrumbs": "Fundamentals > Animating with requestAnimationFrame > Animating with requestAnimationFrame"}, {"id": "1933d18aee190a37", "url": "https://threejs.org/manual/en/fundamentals.html", "page_title": "Fundamentals", "page_type": "guide", "page_summary": "An introductory article covering the basics of three.js, including the structure of a three.js application, building a 'Hello Cube' scene with renderer, camera, geometry, material, and mesh, adding animation and lighting, and setting up ES6 modules with import maps.", "heading": "Adding Lighting", "content": "Page: Fundamentals\nSection: Adding Lighting\n\nWhat would help is to add some lighting so let's add a light. There are many kinds of lights in three.js which we'll go over in a future article. For now let's create a directional light.\n\nDirectional lights have a position and a target. Both default to 0, 0, 0. In our case we're setting the light's position to -1, 2, 4 so it's slightly on the left, above, and behind our camera. The target is still 0, 0, 0 so it will shine toward the origin.\n\nWe also need to change the material. The MeshBasicMaterial is not affected by lights. Let's change it to a MeshPhongMaterial which is affected by lights.", "code_blocks": [{"language": "javascript", "code": "const color = 0xFFFFFF;\nconst intensity = 3;\nconst light = new THREE.DirectionalLight(color, intensity);\nlight.position.set(-1, 2, 4);\nscene.add(light);", "filename": ""}, {"language": "javascript", "code": "const material = new THREE.MeshPhongMaterial({color: 0x44aa88});  // greenish blue", "filename": ""}], "chunk_position": 19, "heading_path": "Adding Lighting > Adding Lighting", "breadcrumbs": "Fundamentals > Adding Lighting > Adding Lighting"}, {"id": "d2ed0188b0ee47ce", "url": "https://threejs.org/manual/en/fundamentals.html", "page_title": "Fundamentals", "page_type": "guide", "page_summary": "An introductory article covering the basics of three.js, including the structure of a three.js application, building a 'Hello Cube' scene with renderer, camera, geometry, material, and mesh, adding animation and lighting, and setting up ES6 modules with import maps.", "heading": "Multiple Cubes", "content": "Page: Fundamentals\nSection: Multiple Cubes\n\nJust for the fun of it let's add 2 more cubes.\n\nWe'll use the same geometry for each cube but make a different material so each cube can be a different color.\n\nFirst we'll make a function that creates a new material with the specified color. Then it creates a mesh using the specified geometry and adds it to the scene and sets its X position.\n\nThen we'll call it 3 times with 3 different colors and X positions saving the Mesh instances in an array.\n\nFinally we'll spin all 3 cubes in our render function. We compute a slightly different rotation for each one.\n\nIf you compare it to the top down diagram above you can see it matches our expectations. With cubes at X = -2 and X = +2 they are partially outside our frustum. They are also somewhat exaggeratedly warped since the field of view across the canvas is so extreme.\n\nAs you can see we have 3 Mesh objects each referencing the same BoxGeometry. Each Mesh references a unique MeshPhongMaterial so that each cube can have a different color.\n\nI hope this short intro helps to get things started. Next up we'll cover making our code responsive so it is adaptable to multiple situations.", "code_blocks": [{"language": "javascript", "code": "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}", "filename": ""}, {"language": "javascript", "code": "const cubes = [\n  makeInstance(geometry, 0x44aa88,  0),\n  makeInstance(geometry, 0x8844aa, -2),\n  makeInstance(geometry, 0xaa8844,  2),\n];", "filename": ""}, {"language": "javascript", "code": "function render(time) {\n  time *= 0.001;  // convert time to seconds\n\n  cubes.forEach((cube, ndx) => {\n    const speed = 1 + ndx * .1;\n    const rot = time * speed;\n    cube.rotation.x = rot;\n    cube.rotation.y = rot;\n  });\n\n  ...", "filename": ""}], "chunk_position": 19, "heading_path": "Multiple Cubes > Multiple Cubes", "breadcrumbs": "Fundamentals > Multiple Cubes > Multiple Cubes"}, {"id": "ed1309fb5d9ffc16", "url": "https://threejs.org/manual/en/fundamentals.html", "page_title": "Fundamentals", "page_type": "guide", "page_summary": "An introductory article covering the basics of three.js, including the structure of a three.js application, building a 'Hello Cube' scene with renderer, camera, geometry, material, and mesh, adding animation and lighting, and setting up ES6 modules with import maps.", "heading": "ES6 Modules, Three.js, and Folder Structure", "content": "Page: Fundamentals\nSection: ES6 Modules, Three.js, and Folder Structure\n\nAs of version r147 the preferred way to use three.js is via es6 modules and import maps.\n\nes6 modules can be loaded via the `import` keyword in a script or inline via a `<script type=\"module\">` tag. Notice `'three'` specifier there. If you leave it as it is, it will likely produce an error. An import map should be used to tell the browser where to find three.js. Note that path specifier can start only with `./` or `../`.\n\nTo import addons like OrbitControls.js use the following. Don't forget to add addons to the import map. You can also use a CDN.\n\nTo conclude, the recommended way of using three.js is to set up an import map pointing to the three.js module file and addons, then use `<script type=\"module\">` with import statements to load three.js and its addons.", "code_blocks": [{"language": "html", "code": "<script type=\"module\">\nimport * as THREE from 'three';\n\n...\n</script>", "filename": ""}, {"language": "html", "code": "<script type=\"importmap\">\n{\n  \"imports\": {\n    \"three\": \"./path/to/three.module.js\"\n  }\n}\n</script>", "filename": ""}, {"language": "javascript", "code": "import {OrbitControls} from 'three/addons/controls/OrbitControls.js';", "filename": ""}, {"language": "html", "code": "<script type=\"importmap\">\n{\n  \"imports\": {\n    \"three\": \"./path/to/three.module.js\",\n    \"three/addons/\": \"./different/path/to/examples/jsm/\"\n  }\n}\n</script>", "filename": ""}, {"language": "html", "code": "<script type=\"importmap\">\n{\n  \"imports\": {\n    \"three\": \"https://cdn.jsdelivr.net/npm/three@<version>/build/three.module.js\",\n    \"three/addons/\": \"https://cdn.jsdelivr.net/npm/three@<version>/examples/jsm/\"\n  }\n}\n</script>", "filename": ""}, {"language": "html", "code": "<script type=\"importmap\">\n{\n  \"imports\": {\n    \"three\": \"./path/to/three.module.js\",\n    \"three/addons/\": \"./different/path/to/examples/jsm/\"\n  }\n}\n</script>\n\n<script type=\"module\">\nimport * as THREE from 'three';\nimport {OrbitControls} from 'three/addons/controls/OrbitControls.js';\n\n...\n</script>", "filename": ""}], "chunk_position": 19, "heading_path": "ES6 Modules, Three.js, and Folder Structure > ES6 Modules, Three.js, and Folder Structure", "breadcrumbs": "Fundamentals > ES6 Modules, Three.js, and Folder Structure > ES6 Modules, Three.js, and Folder Structure"}, {"id": "550590f04393c484", "url": "https://threejs.org/manual/en/responsive.html", "page_title": "Responsive Design", "page_type": "guide", "page_summary": "This article explains how to make three.js applications responsive to different screen sizes and display situations. It covers CSS-based sizing, handling aspect ratios, managing canvas resolution, and dealing with HD-DPI displays.", "heading": "Responsive Design", "content": "Page: Responsive Design\nSection: Responsive Design\n\nThis is the second article in a series of articles about three.js. The first article was about fundamentals. If you haven't read that yet you might want to start there.\n\nThis article is about how to make your three.js app be responsive to any situation. Making a webpage responsive generally refers to the page displaying well on different sized displays from desktops to tablets to phones.\n\nFor three.js there are even more situations to consider. For example, a 3D editor with controls on the left, right, top, or bottom is something we might want to handle. A live diagram in the middle of a document is another example.\n\nThe last sample we had used a plain canvas with no CSS and no size", "code_blocks": [{"language": "html", "code": "<canvas id=\"c\"></canvas>", "filename": ""}], "chunk_position": 20, "heading_path": "Responsive Design > Responsive Design", "breadcrumbs": "Responsive Design > Responsive Design > Responsive Design"}, {"id": "0941568fa1a80eef", "url": "https://threejs.org/manual/en/responsive.html", "page_title": "Responsive Design", "page_type": "guide", "page_summary": "This article explains how to make three.js applications responsive to different screen sizes and display situations. It covers CSS-based sizing, handling aspect ratios, managing canvas resolution, and dealing with HD-DPI displays.", "heading": "Making the Canvas Fill the Page", "content": "Page: Responsive Design\nSection: Making the Canvas Fill the Page\n\nThat canvas defaults to 300x150 CSS pixels in size.\n\nIn the web platform the recommended way to set the size of something is to use CSS.\n\nLet's make the canvas fill the page by adding CSS", "code_blocks": [{"language": "css", "code": "<style>\nhtml, body {\n   margin: 0;\n   height: 100%;\n}\n#c {\n   width: 100%;\n   height: 100%;\n   display: block;\n}\n</style>", "filename": ""}], "chunk_position": 20, "heading_path": "Making the Canvas Fill the Page > Making the Canvas Fill the Page", "breadcrumbs": "Responsive Design > Making the Canvas Fill the Page > Making the Canvas Fill the Page"}, {"id": "1fdc76a80fc0727f", "url": "https://threejs.org/manual/en/responsive.html", "page_title": "Responsive Design", "page_type": "guide", "page_summary": "This article explains how to make three.js applications responsive to different screen sizes and display situations. It covers CSS-based sizing, handling aspect ratios, managing canvas resolution, and dealing with HD-DPI displays.", "heading": "Understanding the CSS Setup", "content": "Page: Responsive Design\nSection: Understanding the CSS Setup\n\nIn HTML the body has a margin of 5 pixels by default so setting the margin to 0 removes the margin. Setting the html and body height to 100% makes them fill the window. Otherwise they are only as large as the content that fills them.\n\nNext we tell the `id=c` element to be 100% the size of its container which in this case is the body of the document.\n\nFinally we set its `display` mode to `block`. A canvas's default display mode is `inline`. Inline elements can end up adding whitespace to what is displayed. By setting the canvas to `block` that issue goes away.\n\nYou can see the canvas is now filling the page but there are 2 problems. One our cubes are stretched. They are not cubes they are more like boxes. Too tall or too wide. Open the example in its own window and resize it. You'll see how the cubes get stretched wide and tall.\n\nThe second problem is they look low resolution or blocky and blurry. Stretch the window really large and you'll really see the issue.", "code_blocks": [], "chunk_position": 20, "heading_path": "Understanding the CSS Setup > Understanding the CSS Setup", "breadcrumbs": "Responsive Design > Understanding the CSS Setup > Understanding the CSS Setup"}, {"id": "830f3388f6eba6aa", "url": "https://threejs.org/manual/en/responsive.html", "page_title": "Responsive Design", "page_type": "guide", "page_summary": "This article explains how to make three.js applications responsive to different screen sizes and display situations. It covers CSS-based sizing, handling aspect ratios, managing canvas resolution, and dealing with HD-DPI displays.", "heading": "Fixing the Stretchy Problem", "content": "Page: Responsive Design\nSection: Fixing the Stretchy Problem\n\nLet's fix the stretchy problem first. To do that we need to set the aspect of the camera to the aspect of the canvas's display size. We can do that by looking at the canvas's `clientWidth` and `clientHeight` properties.\n\nWe'll update our render loop like this", "code_blocks": [{"language": "javascript", "code": "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  ...", "filename": ""}], "chunk_position": 20, "heading_path": "Fixing the Stretchy Problem > Fixing the Stretchy Problem", "breadcrumbs": "Responsive Design > Fixing the Stretchy Problem > Fixing the Stretchy Problem"}, {"id": "1af30583bef4b903", "url": "https://threejs.org/manual/en/responsive.html", "page_title": "Responsive Design", "page_type": "guide", "page_summary": "This article explains how to make three.js applications responsive to different screen sizes and display situations. It covers CSS-based sizing, handling aspect ratios, managing canvas resolution, and dealing with HD-DPI displays.", "heading": "Verifying the Fix", "content": "Page: Responsive Design\nSection: Verifying the Fix\n\nNow the cubes should stop being distorted.\n\nOpen the example in a separate window and resize the window and you should see the cubes are no longer stretched tall or wide. They stay the correct aspect regardless of window size.", "code_blocks": [], "chunk_position": 20, "heading_path": "Verifying the Fix > Verifying the Fix", "breadcrumbs": "Responsive Design > Verifying the Fix > Verifying the Fix"}, {"id": "5130cee651b29ef5", "url": "https://threejs.org/manual/en/responsive.html", "page_title": "Responsive Design", "page_type": "guide", "page_summary": "This article explains how to make three.js applications responsive to different screen sizes and display situations. It covers CSS-based sizing, handling aspect ratios, managing canvas resolution, and dealing with HD-DPI displays.", "heading": "Fixing the Blockiness", "content": "Page: Responsive Design\nSection: Fixing the Blockiness\n\nCanvas elements have 2 sizes. One size is the size the canvas is displayed on the page. That's what we set with CSS. The other size is the number of pixels in the canvas itself. This is no different than an image. For example we might have a 128x64 pixel image and using CSS we might display as 400x200 pixels.", "code_blocks": [{"language": "html", "code": "<img src=\"some128x64image.jpg\" style=\"width:400px; height:200px\">", "filename": ""}], "chunk_position": 20, "heading_path": "Fixing the Blockiness > Fixing the Blockiness", "breadcrumbs": "Responsive Design > Fixing the Blockiness > Fixing the Blockiness"}, {"id": "826f57f36fce413d", "url": "https://threejs.org/manual/en/responsive.html", "page_title": "Responsive Design", "page_type": "guide", "page_summary": "This article explains how to make three.js applications responsive to different screen sizes and display situations. It covers CSS-based sizing, handling aspect ratios, managing canvas resolution, and dealing with HD-DPI displays.", "heading": "Understanding Drawing Buffer Size", "content": "Page: Responsive Design\nSection: Understanding Drawing Buffer Size\n\nA canvas's internal size, its resolution, is often called its drawingbuffer size. In three.js we can set the canvas's drawingbuffer size by calling `renderer.setSize`. What size should we pick? The most obvious answer is \"the same size the canvas is displayed\". Again, to do that we can look at the canvas's `clientWidth` and `clientHeight` properties.\n\nLet's write a function that checks if the renderer's canvas is not already the size it is being displayed as and if so set its size.", "code_blocks": [], "chunk_position": 20, "heading_path": "Understanding Drawing Buffer Size > Understanding Drawing Buffer Size", "breadcrumbs": "Responsive Design > Understanding Drawing Buffer Size > Understanding Drawing Buffer Size"}, {"id": "985e7d0a0581e979", "url": "https://threejs.org/manual/en/responsive.html", "page_title": "Responsive Design", "page_type": "guide", "page_summary": "This article explains how to make three.js applications responsive to different screen sizes and display situations. It covers CSS-based sizing, handling aspect ratios, managing canvas resolution, and dealing with HD-DPI displays.", "heading": "Creating the resizeRendererToDisplaySize Function", "content": "Page: Responsive Design\nSection: Creating the resizeRendererToDisplaySize Function\n\nNotice we check if the canvas actually needs to be resized. Resizing the canvas is an interesting part of the canvas spec and it's best not to set the same size if it's already the size we want.\n\nOnce we know if we need to resize or not we then call `renderer.setSize` and pass in the new width and height. It's important to pass `false` at the end. `renderer.setSize` by default sets the canvas's CSS size but doing so is not what we want. We want the browser to continue to work how it does for all other elements which is to use CSS to determine the display size of the element. We don't want canvases used by three to be different than other elements.\n\nNote that our function returns true if the canvas was resized. We can use this to check if there are other things we should update. Let's modify our render loop to use the new function", "code_blocks": [{"language": "javascript", "code": "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}", "filename": ""}], "chunk_position": 20, "heading_path": "Creating the resizeRendererToDisplaySize Function > Creating the resizeRendererToDisplaySize Function", "breadcrumbs": "Responsive Design > Creating the resizeRendererToDisplaySize Function > Creating the resizeRendererToDisplaySize Function"}, {"id": "17f5ba9eeea72284", "url": "https://threejs.org/manual/en/responsive.html", "page_title": "Responsive Design", "page_type": "guide", "page_summary": "This article explains how to make three.js applications responsive to different screen sizes and display situations. It covers CSS-based sizing, handling aspect ratios, managing canvas resolution, and dealing with HD-DPI displays.", "heading": "Updating the Render Loop", "content": "Page: Responsive Design\nSection: Updating the Render Loop\n\nSince the aspect is only going to change if the canvas's display size changed we only set the camera's aspect if `resizeRendererToDisplaySize` returns `true`.", "code_blocks": [{"language": "javascript", "code": "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  ...", "filename": ""}], "chunk_position": 20, "heading_path": "Updating the Render Loop > Updating the Render Loop", "breadcrumbs": "Responsive Design > Updating the Render Loop > Updating the Render Loop"}, {"id": "e262692d21e100d9", "url": "https://threejs.org/manual/en/responsive.html", "page_title": "Responsive Design", "page_type": "guide", "page_summary": "This article explains how to make three.js applications responsive to different screen sizes and display situations. It covers CSS-based sizing, handling aspect ratios, managing canvas resolution, and dealing with HD-DPI displays.", "heading": "Final Result", "content": "Page: Responsive Design\nSection: Final Result\n\nIt should now render with a resolution that matches the display size of the canvas.\n\nTo make the point about letting CSS handle the resizing let's take our code and put it in a separate `.js` file. Here then are a few more examples where we let CSS choose the size and notice we had to change zero code for them to work.\n\nLet's put our cubes in the middle of a paragraph of text.\n\nand here's our same code used in an editor style layout where the control area on the right can be resized.\n\nThe important part to notice is no code changed. Only our HTML and CSS changed.", "code_blocks": [], "chunk_position": 20, "heading_path": "Final Result > Final Result", "breadcrumbs": "Responsive Design > Final Result > Final Result"}, {"id": "efc7633056d9d9be", "url": "https://threejs.org/manual/en/responsive.html", "page_title": "Responsive Design", "page_type": "guide", "page_summary": "This article explains how to make three.js applications responsive to different screen sizes and display situations. It covers CSS-based sizing, handling aspect ratios, managing canvas resolution, and dealing with HD-DPI displays.", "heading": "Handling HD-DPI displays", "content": "Page: Responsive Design\nSection: Handling HD-DPI displays\n\nHD-DPI stands for high-density dot per inch displays. That's most Macs nowadays and many Windows machines as well as pretty much all smartphones.\n\nThe way this works in the browser is they use CSS pixels to set the sizes which are supposed to be the same regardless of how high res the display is. The browser will just render text with more detail but the same physical size.\n\nThere are various ways to handle HD-DPI with three.js.\n\nThe first one is just not to do anything special. This is arguably the most common. Rendering 3D graphics takes a lot of GPU processing power. Mobile GPUs have less power than desktops, at least as of 2018, and yet mobile phones often have very high resolution displays. The current top of the line phones have an HD-DPI ratio of 3x meaning for every one pixel from a non-HD-DPI display those phones have 9 pixels. That means they have to do 9x the rendering.\n\nComputing 9x the pixels is a lot of work so if we just leave the code as it is we'll compute 1x the pixels and the browser will just draw it at 3x the size (3x by 3x = 9x pixels).\n\nFor any heavy three.js app that's probably what you want otherwise you're likely to get a slow framerate.\n\nThat said if you actually do want to render at the resolution of the device there are a couple of ways to do this in three.js.\n\nOne is to tell three.js a resolution multiplier using `renderer.setPixelRatio`. You ask the browser what the multiplier is from CSS pixels to device pixels and pass that to three.js", "code_blocks": [{"language": "javascript", "code": "renderer.setPixelRatio(window.devicePixelRatio);", "filename": ""}], "chunk_position": 20, "heading_path": "Handling HD-DPI displays > Handling HD-DPI displays", "breadcrumbs": "Responsive Design > Handling HD-DPI displays > Handling HD-DPI displays"}, {"id": "b27636a2afe6ae0d", "url": "https://threejs.org/manual/en/responsive.html", "page_title": "Responsive Design", "page_type": "guide", "page_summary": "This article explains how to make three.js applications responsive to different screen sizes and display situations. It covers CSS-based sizing, handling aspect ratios, managing canvas resolution, and dealing with HD-DPI displays.", "heading": "Why setPixelRatio is Not Recommended", "content": "Page: Responsive Design\nSection: Why setPixelRatio is Not Recommended\n\nAfter that any calls to `renderer.setSize` will magically use the size you request multiplied by whatever pixel ratio you passed in.  This is strongly NOT RECOMMENDED . See below\n\nThe other way is to do it yourself when you resize the canvas.", "code_blocks": [{"language": "javascript", "code": "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    }", "filename": ""}], "chunk_position": 20, "heading_path": "Why setPixelRatio is Not Recommended > Why setPixelRatio is Not Recommended", "breadcrumbs": "Responsive Design > Why setPixelRatio is Not Recommended > Why setPixelRatio is Not Recommended"}, {"id": "5ce470a21a3cf3b9", "url": "https://threejs.org/manual/en/responsive.html", "page_title": "Responsive Design", "page_type": "guide", "page_summary": "This article explains how to make three.js applications responsive to different screen sizes and display situations. It covers CSS-based sizing, handling aspect ratios, managing canvas resolution, and dealing with HD-DPI displays.", "heading": "Why Manual Pixel Ratio is Better", "content": "Page: Responsive Design\nSection: Why Manual Pixel Ratio is Better\n\nThis second way is objectively better. Why? Because it means I get what I ask for. There are many cases when using three.js where we need to know the actual size of the canvas's drawingBuffer. For example when making a post processing filter, or if we are making a shader that accesses `gl_FragCoord`, if we are making a screenshot, or reading pixels for GPU picking, for drawing into a 2D canvas, etc... There are many cases where if we use `setPixelRatio` then our actual size will be different than the size we requested and we'll have to guess when to use the size we asked for and when to use the size three.js is actually using. By doing it ourselves we always know the size being used is the size we requested. There is no special case where magic is happening behind the scenes.\n\nHere's an example using the code above.\n\nIt might be hard to see the difference but if you have an HD-DPI display and you compare this sample to those above you should notice the edges are more crisp.", "code_blocks": [], "chunk_position": 20, "heading_path": "Why Manual Pixel Ratio is Better > Why Manual Pixel Ratio is Better", "breadcrumbs": "Responsive Design > Why Manual Pixel Ratio is Better > Why Manual Pixel Ratio is Better"}, {"id": "868177141f0f4b2b", "url": "https://threejs.org/manual/en/responsive.html", "page_title": "Responsive Design", "page_type": "guide", "page_summary": "This article explains how to make three.js applications responsive to different screen sizes and display situations. It covers CSS-based sizing, handling aspect ratios, managing canvas resolution, and dealing with HD-DPI displays.", "heading": "HD-DPI: Limiting maximum drawing buffer size", "content": "Page: Responsive Design\nSection: HD-DPI: Limiting maximum drawing buffer size\n\nWhen using a fractional UI scaling factor on some operating system (eg: 150% on OSX or Linux) the assumption that the real physical resolution equals `width * window.devicePixelRatio` and `height * window.devicePixelRatio` no longer holds. This may lead to excessive GPU load, lower frame rates and high power consumption.\n\nA possible mitigation is to cap the maximum internal resolution (e.g. limit width × height) so the buffer remains within safe bounds.", "code_blocks": [{"language": "javascript", "code": "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 > 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    }", "filename": ""}], "chunk_position": 20, "heading_path": "HD-DPI: Limiting maximum drawing buffer size > HD-DPI: Limiting maximum drawing buffer size", "breadcrumbs": "Responsive Design > HD-DPI: Limiting maximum drawing buffer size > HD-DPI: Limiting maximum drawing buffer size"}, {"id": "96e0fc43e7d3ff33", "url": "https://threejs.org/manual/en/responsive.html", "page_title": "Responsive Design", "page_type": "guide", "page_summary": "This article explains how to make three.js applications responsive to different screen sizes and display situations. It covers CSS-based sizing, handling aspect ratios, managing canvas resolution, and dealing with HD-DPI displays.", "heading": "Conclusion", "content": "Page: Responsive Design\nSection: Conclusion\n\nThis article covered a very basic but fundamental topic. Next up lets quickly go over the basic primitives that three.js provides.", "code_blocks": [], "chunk_position": 20, "heading_path": "Conclusion > Conclusion", "breadcrumbs": "Responsive Design > Conclusion > Conclusion"}, {"id": "1f5084cbb7e82350", "url": "https://threejs.org/manual/en/prerequisites.html", "page_title": "Prerequisites", "page_type": "guide", "page_summary": "This page outlines the JavaScript and web development knowledge assumed by the three.js articles, including ES6 modules, CSS selectors, closures, ES5/ES6/ES7 features, and recommended tooling like Visual Studio Code with ESLint.", "heading": "Prerequisites", "content": "Page: Prerequisites\nSection: Prerequisites\n\nThese articles are meant to help you learn how to use three.js. They assume you know how to program in JavaScript. They assume you know what the DOM is, how to write HTML as well as create DOM elements in JavaScript. They assume you know how to use es6 modules via import and via `<script type=\"module\">` tags. They assume you know how to use import maps. They assume you know some CSS and that you know what CSS selectors are. They also assume you know ES5, ES6 and maybe some ES7. They assume you know that the browser runs JavaScript only via events and callbacks. They assume you know what a closure is.\n\nHere's some brief refreshers and notes", "code_blocks": [], "chunk_position": 21, "heading_path": "Prerequisites > Prerequisites", "breadcrumbs": "Prerequisites > Prerequisites > Prerequisites"}, {"id": "46a671dca52e1270", "url": "https://threejs.org/manual/en/prerequisites.html", "page_title": "Prerequisites", "page_type": "guide", "page_summary": "This page outlines the JavaScript and web development knowledge assumed by the three.js articles, including ES6 modules, CSS selectors, closures, ES5/ES6/ES7 features, and recommended tooling like Visual Studio Code with ESLint.", "heading": "es6 modules", "content": "Page: Prerequisites\nSection: es6 modules\n\nes6 modules can be loaded via the `import` keyword in a script or inline via a `<script type=\"module\">` tag. Here's an example\n\nSee more details at the bottom of this article.", "code_blocks": [{"language": "html", "code": "<script type=\"importmap\">\n{\n  \"imports\": {\n    \"three\": \"./path/to/three.module.js\",\n    \"three/addons/\": \"./different/path/to/examples/jsm/\"\n  }\n}\n</script>\n\n<script type=\"module\">\nimport * as THREE from 'three';\nimport {OrbitControls} from 'three/addons/controls/OrbitControls.js';\n\n...\n\n</script>", "filename": ""}], "chunk_position": 21, "heading_path": "es6 modules > es6 modules", "breadcrumbs": "Prerequisites > es6 modules > es6 modules"}, {"id": "7de11c8ccc9d9352", "url": "https://threejs.org/manual/en/prerequisites.html", "page_title": "Prerequisites", "page_type": "guide", "page_summary": "This page outlines the JavaScript and web development knowledge assumed by the three.js articles, including ES6 modules, CSS selectors, closures, ES5/ES6/ES7 features, and recommended tooling like Visual Studio Code with ESLint.", "heading": "`document.querySelector` and `document.querySelectorAll`", "content": "Page: Prerequisites\nSection: `document.querySelector` and `document.querySelectorAll`\n\nYou can use `document.querySelector` to select the first element that matches a CSS selector. `document.querySelectorAll` returns all elements that match a CSS selector.", "code_blocks": [], "chunk_position": 21, "heading_path": "`document.querySelector` and `document.querySelectorAll` > `document.querySelector` and `document.querySelectorAll`", "breadcrumbs": "Prerequisites > `document.querySelector` and `document.querySelectorAll` > `document.querySelector` and `document.querySelectorAll`"}, {"id": "7a076448acb2ff84", "url": "https://threejs.org/manual/en/prerequisites.html", "page_title": "Prerequisites", "page_type": "guide", "page_summary": "This page outlines the JavaScript and web development knowledge assumed by the three.js articles, including ES6 modules, CSS selectors, closures, ES5/ES6/ES7 features, and recommended tooling like Visual Studio Code with ESLint.", "heading": "You don't need `onload`", "content": "Page: Prerequisites\nSection: You don't need `onload`\n\nLots of 20yr old pages use HTML like\n\nThat style is deprecated. Put your scripts at the bottom of the page.\n\nor use the `defer` property.", "code_blocks": [{"language": "html", "code": "<body onload=\"somefunction()\">", "filename": ""}, {"language": "html", "code": "<html>\n  <head>\n    ...\n  </head>\n  <body>\n     ...\n  </body>\n  <script>\n    // inline javascript\n  </script>\n</html>", "filename": ""}], "chunk_position": 21, "heading_path": "You don't need `onload` > You don't need `onload`", "breadcrumbs": "Prerequisites > You don't need `onload` > You don't need `onload`"}, {"id": "54f8743c6124b2e7", "url": "https://threejs.org/manual/en/prerequisites.html", "page_title": "Prerequisites", "page_type": "guide", "page_summary": "This page outlines the JavaScript and web development knowledge assumed by the three.js articles, including ES6 modules, CSS selectors, closures, ES5/ES6/ES7 features, and recommended tooling like Visual Studio Code with ESLint.", "heading": "Know how closures work", "content": "Page: Prerequisites\nSection: Know how closures work\n\nIn the code above the function `a` creates a new function every time it's called. That function *closes* over the variable `foo`. Here's more info.", "code_blocks": [{"language": "javascript", "code": "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", "filename": ""}], "chunk_position": 21, "heading_path": "Know how closures work > Know how closures work", "breadcrumbs": "Prerequisites > Know how closures work > Know how closures work"}, {"id": "a1e087b7de083b28", "url": "https://threejs.org/manual/en/prerequisites.html", "page_title": "Prerequisites", "page_type": "guide", "page_summary": "This page outlines the JavaScript and web development knowledge assumed by the three.js articles, including ES6 modules, CSS selectors, closures, ES5/ES6/ES7 features, and recommended tooling like Visual Studio Code with ESLint.", "heading": "Understand how `this` works", "content": "Page: Prerequisites\nSection: Understand how `this` works\n\n`this` is not magic. It's effectively a variable that is automatically passed to functions just like an argument is passed to function. The simple explanation is when you call a function directly like\n\n`this` will be `null` (when in strict mode or in a module) where as when you call a function via the dot operator `.` like this\n\n`this` will be set to `someobject`.\n\nThe parts where people get confused is with callbacks.\n\ndoesn't work as someone inexperienced might expect because when `loader.load` calls the callback it's not calling it with the dot `.` operator so by default `this` will be null (unless the loader explicitly sets it to something). If you want `this` to be `someobject` when the callback happens you need to tell JavaScript that by binding it to the function.\n\n*this* article might help explain `this`.", "code_blocks": [{"language": "javascript", "code": "somefunction(a, b, c);", "filename": ""}, {"language": "javascript", "code": "someobject.somefunction(a, b, c);", "filename": ""}, {"language": "javascript", "code": "const callback = someobject.somefunction;\n loader.load(callback);", "filename": ""}, {"language": "javascript", "code": "const callback = someobject.somefunction.bind(someobject);\n loader.load(callback);", "filename": ""}], "chunk_position": 21, "heading_path": "Understand how `this` works > Understand how `this` works", "breadcrumbs": "Prerequisites > Understand how `this` works > Understand how `this` works"}, {"id": "9851e7053875d327", "url": "https://threejs.org/manual/en/prerequisites.html", "page_title": "Prerequisites", "page_type": "guide", "page_summary": "This page outlines the JavaScript and web development knowledge assumed by the three.js articles, including ES6 modules, CSS selectors, closures, ES5/ES6/ES7 features, and recommended tooling like Visual Studio Code with ESLint.", "heading": "ES5/ES6/ES7 stuff", "content": "Page: Prerequisites\nSection: ES5/ES6/ES7 stuff\n\n", "code_blocks": [], "chunk_position": 21, "heading_path": "ES5/ES6/ES7 stuff > ES5/ES6/ES7 stuff", "breadcrumbs": "Prerequisites > ES5/ES6/ES7 stuff > ES5/ES6/ES7 stuff"}, {"id": "9fc987ef303fb7a9", "url": "https://threejs.org/manual/en/prerequisites.html", "page_title": "Prerequisites", "page_type": "guide", "page_summary": "This page outlines the JavaScript and web development knowledge assumed by the three.js articles, including ES6 modules, CSS selectors, closures, ES5/ES6/ES7 features, and recommended tooling like Visual Studio Code with ESLint.", "heading": "`var` is deprecated. Use `const` and/or `let`", "content": "Page: Prerequisites\nSection: `var` is deprecated. Use `const` and/or `let`\n\nThere is no reason to use `var` **EVER** and at this point it's considered bad practice to use it at all. Use `const` if the variable will never be reassigned which is most of the time. Use `let` in those cases where the value changes. This will help avoid tons of bugs.", "code_blocks": [], "chunk_position": 21, "heading_path": "`var` is deprecated. Use `const` and/or `let` > `var` is deprecated. Use `const` and/or `let`", "breadcrumbs": "Prerequisites > `var` is deprecated. Use `const` and/or `let` > `var` is deprecated. Use `const` and/or `let`"}, {"id": "ab6995224504c982", "url": "https://threejs.org/manual/en/prerequisites.html", "page_title": "Prerequisites", "page_type": "guide", "page_summary": "This page outlines the JavaScript and web development knowledge assumed by the three.js articles, including ES6 modules, CSS selectors, closures, ES5/ES6/ES7 features, and recommended tooling like Visual Studio Code with ESLint.", "heading": "Use `for(elem of collection)` never `for(elem in collection)`", "content": "Page: Prerequisites\nSection: Use `for(elem of collection)` never `for(elem in collection)`\n\n`for of` is new, `for in` is old. `for in` had issues that are solved by `for of`\n\nAs one example you can iterate over all the key/value pairs of an object with", "code_blocks": [{"language": "javascript", "code": "for (const [key, value] of Object.entries(someObject)) {\n  console.log(key, value);\n}", "filename": ""}], "chunk_position": 21, "heading_path": "Use `for(elem of collection)` never `for(elem in collection)` > Use `for(elem of collection)` never `for(elem in collection)`", "breadcrumbs": "Prerequisites > Use `for(elem of collection)` never `for(elem in collection)` > Use `for(elem of collection)` never `for(elem in collection)`"}, {"id": "48994b5b0c3703b6", "url": "https://threejs.org/manual/en/prerequisites.html", "page_title": "Prerequisites", "page_type": "guide", "page_summary": "This page outlines the JavaScript and web development knowledge assumed by the three.js articles, including ES6 modules, CSS selectors, closures, ES5/ES6/ES7 features, and recommended tooling like Visual Studio Code with ESLint.", "heading": "Use `forEach`, `map`, and `filter` where useful", "content": "Page: Prerequisites\nSection: Use `forEach`, `map`, and `filter` where useful\n\nArrays added the functions `forEach`, `map`, and `filter` and are used fairly extensively in modern JavaScript.", "code_blocks": [], "chunk_position": 21, "heading_path": "Use `forEach`, `map`, and `filter` where useful > Use `forEach`, `map`, and `filter` where useful", "breadcrumbs": "Prerequisites > Use `forEach`, `map`, and `filter` where useful > Use `forEach`, `map`, and `filter` where useful"}, {"id": "9e418d1497499e19", "url": "https://threejs.org/manual/en/prerequisites.html", "page_title": "Prerequisites", "page_type": "guide", "page_summary": "This page outlines the JavaScript and web development knowledge assumed by the three.js articles, including ES6 modules, CSS selectors, closures, ES5/ES6/ES7 features, and recommended tooling like Visual Studio Code with ESLint.", "heading": "Use destructuring", "content": "Page: Prerequisites\nSection: Use destructuring\n\nAssume an object `const dims = {width: 300, height: 150}`\n\nold code\n\nnew code\n\nDestructuring works with arrays too. Assume an array `const position = [5, 6, 7, 1]`;\n\nold code\n\nnew code\n\nDestructuring also works in function arguments", "code_blocks": [{"language": "javascript", "code": "const width = dims.width;\nconst height = dims.height;", "filename": ""}, {"language": "javascript", "code": "const {width, height} = dims;", "filename": ""}, {"language": "javascript", "code": "const y = position[1];\nconst z = position[2];", "filename": ""}, {"language": "javascript", "code": "const [, y, z] = position;", "filename": ""}, {"language": "javascript", "code": "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", "filename": ""}], "chunk_position": 21, "heading_path": "Use destructuring > Use destructuring", "breadcrumbs": "Prerequisites > Use destructuring > Use destructuring"}, {"id": "0121454a5aca4dd7", "url": "https://threejs.org/manual/en/prerequisites.html", "page_title": "Prerequisites", "page_type": "guide", "page_summary": "This page outlines the JavaScript and web development knowledge assumed by the three.js articles, including ES6 modules, CSS selectors, closures, ES5/ES6/ES7 features, and recommended tooling like Visual Studio Code with ESLint.", "heading": "Use object declaration short cuts", "content": "Page: Prerequisites\nSection: Use object declaration short cuts\n\nold code\n\nnew code", "code_blocks": [{"language": "javascript", "code": "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 };", "filename": ""}, {"language": "javascript", "code": "const width = 300;\n const height = 150;\n const obj = {\n   width,\n   height,\n   area() {\n     return this.width * this.height;\n   },\n };", "filename": ""}], "chunk_position": 21, "heading_path": "Use object declaration short cuts > Use object declaration short cuts", "breadcrumbs": "Prerequisites > Use object declaration short cuts > Use object declaration short cuts"}, {"id": "e867f8d11c43429b", "url": "https://threejs.org/manual/en/prerequisites.html", "page_title": "Prerequisites", "page_type": "guide", "page_summary": "This page outlines the JavaScript and web development knowledge assumed by the three.js articles, including ES6 modules, CSS selectors, closures, ES5/ES6/ES7 features, and recommended tooling like Visual Studio Code with ESLint.", "heading": "Use the rest parameter and the spread operator `...`", "content": "Page: Prerequisites\nSection: Use the rest parameter and the spread operator `...`\n\nThe rest parameter can be used to consume any number of parameters. Example\n\nThe spread operator can be used to expand an iterable into arguments\n\nor copy an array\n\nor to merge objects", "code_blocks": [{"language": "javascript", "code": "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 }", "filename": ""}, {"language": "javascript", "code": "const position = [1, 2, 3];\nsomeMesh.position.set(...position);", "filename": ""}, {"language": "javascript", "code": "const copiedPositionArray = [...position];\ncopiedPositionArray.push(4); // [1,2,3,4]\nconsole.log(position); // [1,2,3] position is unaffected", "filename": ""}, {"language": "javascript", "code": "const a = {abc: 123};\nconst b = {def: 456};\nconst c = {...a, ...b};  // c is now {abc: 123, def: 456}", "filename": ""}], "chunk_position": 21, "heading_path": "Use the rest parameter and the spread operator `...` > Use the rest parameter and the spread operator `...`", "breadcrumbs": "Prerequisites > Use the rest parameter and the spread operator `...` > Use the rest parameter and the spread operator `...`"}, {"id": "556da8b036640368", "url": "https://threejs.org/manual/en/prerequisites.html", "page_title": "Prerequisites", "page_type": "guide", "page_summary": "This page outlines the JavaScript and web development knowledge assumed by the three.js articles, including ES6 modules, CSS selectors, closures, ES5/ES6/ES7 features, and recommended tooling like Visual Studio Code with ESLint.", "heading": "Use `class`", "content": "Page: Prerequisites\nSection: Use `class`\n\nThe syntax for making class like objects pre ES5 was unfamiliar to most programmers. As of ES5 you can now use the `class` keyword which is closer to the style of C++/C#/Java.", "code_blocks": [], "chunk_position": 21, "heading_path": "Use `class` > Use `class`", "breadcrumbs": "Prerequisites > Use `class` > Use `class`"}, {"id": "0887d7de931061fa", "url": "https://threejs.org/manual/en/prerequisites.html", "page_title": "Prerequisites", "page_type": "guide", "page_summary": "This page outlines the JavaScript and web development knowledge assumed by the three.js articles, including ES6 modules, CSS selectors, closures, ES5/ES6/ES7 features, and recommended tooling like Visual Studio Code with ESLint.", "heading": "Understand getters and setters", "content": "Page: Prerequisites\nSection: Understand getters and setters\n\nGetters and setters are common in most modern languages. The `class` syntax of ES5 makes them much easier than pre ES5.", "code_blocks": [], "chunk_position": 21, "heading_path": "Understand getters and setters > Understand getters and setters", "breadcrumbs": "Prerequisites > Understand getters and setters > Understand getters and setters"}, {"id": "288318c4ce215be2", "url": "https://threejs.org/manual/en/prerequisites.html", "page_title": "Prerequisites", "page_type": "guide", "page_summary": "This page outlines the JavaScript and web development knowledge assumed by the three.js articles, including ES6 modules, CSS selectors, closures, ES5/ES6/ES7 features, and recommended tooling like Visual Studio Code with ESLint.", "heading": "Use arrow functions where appropriate", "content": "Page: Prerequisites\nSection: Use arrow functions where appropriate\n\nThis is especially useful with callbacks and promises.\n\nArrow functions bind `this` to the context in which you create the arrow function.\n\nis a shortcut for\n\nSee link above for more info on `this`.", "code_blocks": [{"language": "javascript", "code": "loader.load((texture) => {\n  // use texture\n});", "filename": ""}, {"language": "javascript", "code": "const foo = (args) => {/* code */};", "filename": ""}, {"language": "javascript", "code": "const foo = (function(args) {/* code */}).bind(this));", "filename": ""}], "chunk_position": 21, "heading_path": "Use arrow functions where appropriate > Use arrow functions where appropriate", "breadcrumbs": "Prerequisites > Use arrow functions where appropriate > Use arrow functions where appropriate"}, {"id": "2f5cc0f9ff58e593", "url": "https://threejs.org/manual/en/prerequisites.html", "page_title": "Prerequisites", "page_type": "guide", "page_summary": "This page outlines the JavaScript and web development knowledge assumed by the three.js articles, including ES6 modules, CSS selectors, closures, ES5/ES6/ES7 features, and recommended tooling like Visual Studio Code with ESLint.", "heading": "Promises as well as async/await", "content": "Page: Prerequisites\nSection: Promises as well as async/await\n\nPromises help with asynchronous code. Async/await help use promises.\n\nIt's too big a topic to go into here but you can read up on promises here and async/await here.", "code_blocks": [], "chunk_position": 21, "heading_path": "Promises as well as async/await > Promises as well as async/await", "breadcrumbs": "Prerequisites > Promises as well as async/await > Promises as well as async/await"}, {"id": "94d3553bd26381b8", "url": "https://threejs.org/manual/en/prerequisites.html", "page_title": "Prerequisites", "page_type": "guide", "page_summary": "This page outlines the JavaScript and web development knowledge assumed by the three.js articles, including ES6 modules, CSS selectors, closures, ES5/ES6/ES7 features, and recommended tooling like Visual Studio Code with ESLint.", "heading": "Use Template Literals", "content": "Page: Prerequisites\nSection: Use Template Literals\n\nTemplate literals are strings using backticks instead of quotes.\n\nTemplate literals have basically 2 features. One is they can be multi-line\n\n`foo` and `bar` above are the same.\n\nThe other is that you can pop out of string mode and insert snippets of JavaScript using `${javascript-expression}`. This is the template part. Example:\n\nor\n\nor", "code_blocks": [{"language": "javascript", "code": "const foo = `this is a template literal`;", "filename": ""}, {"language": "javascript", "code": "const foo = `this\nis\na\ntemplate\nliteral`;\nconst bar = \"this\\nis\\na\\ntemplate\\nliteral\";", "filename": ""}, {"language": "javascript", "code": "const r = 192;\nconst g = 255;\nconst b = 64;\nconst rgbCSSColor = `rgb(${r},${g},${b})`;", "filename": ""}, {"language": "javascript", "code": "const color = [192, 255, 64];\nconst rgbCSSColor = `rgb(${color.join(',')})`;", "filename": ""}, {"language": "javascript", "code": "const aWidth = 10;\nconst bWidth = 20;\nsomeElement.style.width = `${aWidth + bWidth}px`;", "filename": ""}], "chunk_position": 21, "heading_path": "Use Template Literals > Use Template Literals", "breadcrumbs": "Prerequisites > Use Template Literals > Use Template Literals"}, {"id": "5a43f00ecc132b20", "url": "https://threejs.org/manual/en/prerequisites.html", "page_title": "Prerequisites", "page_type": "guide", "page_summary": "This page outlines the JavaScript and web development knowledge assumed by the three.js articles, including ES6 modules, CSS selectors, closures, ES5/ES6/ES7 features, and recommended tooling like Visual Studio Code with ESLint.", "heading": "Learn JavaScript coding conventions.", "content": "Page: Prerequisites\nSection: Learn JavaScript coding conventions.\n\nWhile you're welcome to format your code any way you chose there is at least one convention you should be aware of. Variables, function names, method names, in JavaScript are all lowerCasedCamelCase. Constructors, the names of classes are CapitalizedCamelCase. If you follow this rule your code will match most other JavaScript. Many linters, programs that check for obvious errors in your code, will point out errors if you use the wrong case since by following the convention above they can know when you're using something incorrectly.", "code_blocks": [{"language": "javascript", "code": "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.", "filename": ""}], "chunk_position": 21, "heading_path": "Learn JavaScript coding conventions. > Learn JavaScript coding conventions.", "breadcrumbs": "Prerequisites > Learn JavaScript coding conventions. > Learn JavaScript coding conventions."}, {"id": "15687383dbb5bb50", "url": "https://threejs.org/manual/en/prerequisites.html", "page_title": "Prerequisites", "page_type": "guide", "page_summary": "This page outlines the JavaScript and web development knowledge assumed by the three.js articles, including ES6 modules, CSS selectors, closures, ES5/ES6/ES7 features, and recommended tooling like Visual Studio Code with ESLint.", "heading": "Consider using Visual Studio Code", "content": "Page: Prerequisites\nSection: Consider using Visual Studio Code\n\nOf course use whatever editor you want but if you haven't tried it consider using Visual Studio Code for JavaScript and after installing it setup eslint. It might take a few minutes to setup but it will help you immensely with finding bugs in your JavaScript.\n\nSome examples\n\nIf you enable the `no-undef` rule then VSCode via ESLint will warn you of many undefined variables.\n\nAbove you can see I mis-spelled `doTheThing` as `doThing`. There's a red squiggle under `doThing` and hovering over it it tells me it's undefined. One error avoided.\n\nIf you're using `<script>` tags to include three.js you'll get warnings using `THREE` so add `/* global THREE */` at the top of your JavaScript files to tell eslint that `THREE` exists. (or better, use `import` 😉)\n\nAbove you can see eslint knows the rule that `UpperCaseNames` are constructors and so you should be using `new`. Another error caught and avoided. This is the `new-cap` rule.\n\nThere are 100s of rules you can turn on or off or customize. For example above I mentioned you should use `const` and `let` over `var`.\n\nHere I used `var` and it warned me I should use `let` or `const`\n\nHere I used `let` but it saw I never change the value so it suggested I use `const`.\n\nOf course if you'd prefer to keep using `var` you can just turn off that rule. As I said above though I prefer to use `const` and `let` over `var` as they just work better and prevent bugs.\n\nFor those cases where you really need to override a rule you can add comments to disable them for a single line or a section of code.", "code_blocks": [], "chunk_position": 21, "heading_path": "Consider using Visual Studio Code > Consider using Visual Studio Code", "breadcrumbs": "Prerequisites > Consider using Visual Studio Code > Consider using Visual Studio Code"}, {"id": "3ad435bbbec9e921", "url": "https://threejs.org/manual/en/prerequisites.html", "page_title": "Prerequisites", "page_type": "guide", "page_summary": "This page outlines the JavaScript and web development knowledge assumed by the three.js articles, including ES6 modules, CSS selectors, closures, ES5/ES6/ES7 features, and recommended tooling like Visual Studio Code with ESLint.", "heading": "If you really need to support legacy browsers use a transpiler", "content": "Page: Prerequisites\nSection: If you really need to support legacy browsers use a transpiler\n\nMost modern browsers are auto-updated so using all these features will help you be productive and avoid bugs. That said, if you're on a project that absolutely must support old browsers there are tools that will take your ES5/ES6/ES7 code and transpile the code back to pre ES5 Javascript.", "code_blocks": [], "chunk_position": 21, "heading_path": "If you really need to support legacy browsers use a transpiler > If you really need to support legacy browsers use a transpiler", "breadcrumbs": "Prerequisites > If you really need to support legacy browsers use a transpiler > If you really need to support legacy browsers use a transpiler"}, {"id": "c057cce5da93f0aa", "url": "https://threejs.org/manual/en/setup.html", "page_title": "Setup", "page_type": "guide", "page_summary": "A guide to setting up your computer as a development environment for three.js, including installing a local web server needed for WebGL development due to browser security restrictions.", "heading": "Setup", "content": "Page: Setup\nSection: Setup\n\nThis article is one in a series of articles about three.js. The first article was about three.js fundamentals. If you haven't read that yet you might want to start there.\n\nBefore we go any further we need to talk about setting up your computer as a development environment. In particular, for security reasons, WebGL cannot use images from your hard drive directly. That means in order to do development you need to use a web server. Fortunately development web servers are super easy to setup and use.\n\nFirst off if you'd like you can download this entire site from this link. Once downloaded double click the zip file to unpack the files.\n\nNext download one of these simple web servers.", "code_blocks": [], "chunk_position": 22, "heading_path": "Setup > Setup", "breadcrumbs": "Setup > Setup > Setup"}, {"id": "cf94d25944fc9da0", "url": "https://threejs.org/manual/en/setup.html", "page_title": "Setup", "page_type": "guide", "page_summary": "A guide to setting up your computer as a development environment for three.js, including installing a local web server needed for WebGL development due to browser security restrictions.", "heading": "Servez (GUI option)", "content": "Page: Setup\nSection: Servez (GUI option)\n\nIf you'd prefer a web server with a user interface there's Servez.\n\nJust point it at the folder where you unzipped the files, click \"Start\", then go to in your browser http://localhost:8080/ or if you'd like to browse the samples go to http://localhost:8080/threejs.\n\nTo stop serving click stop or quit Servez.", "code_blocks": [], "chunk_position": 22, "heading_path": "Servez (GUI option) > Servez (GUI option)", "breadcrumbs": "Setup > Servez (GUI option) > Servez (GUI option)"}, {"id": "bfe55b0389ff0d6d", "url": "https://threejs.org/manual/en/setup.html", "page_title": "Setup", "page_type": "guide", "page_summary": "A guide to setting up your computer as a development environment for three.js, including installing a local web server needed for WebGL development due to browser security restrictions.", "heading": "Node.js servez (command line option)", "content": "Page: Setup\nSection: Node.js servez (command line option)\n\nIf you prefer the command line (I do), another way is to use node.js. Download 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.\n\nThen install the servez by typing", "code_blocks": [{"language": "shell", "code": "npm -g install servez", "filename": ""}], "chunk_position": 22, "heading_path": "Node.js servez (command line option) > Node.js servez (command line option)", "breadcrumbs": "Setup > Node.js servez (command line option) > Node.js servez (command line option)"}, {"id": "3abd88bd2b1abff7", "url": "https://threejs.org/manual/en/setup.html", "page_title": "Setup", "page_type": "guide", "page_summary": "A guide to setting up your computer as a development environment for three.js, including installing a local web server needed for WebGL development due to browser security restrictions.", "heading": "OSX installation", "content": "Page: Setup\nSection: OSX installation\n\nIf you're on OSX use", "code_blocks": [{"language": "shell", "code": "sudo npm -g install servez", "filename": ""}], "chunk_position": 22, "heading_path": "OSX installation > OSX installation", "breadcrumbs": "Setup > OSX installation > OSX installation"}, {"id": "ad11c8772bcb2e7d", "url": "https://threejs.org/manual/en/setup.html", "page_title": "Setup", "page_type": "guide", "page_summary": "A guide to setting up your computer as a development environment for three.js, including installing a local web server needed for WebGL development due to browser security restrictions.", "heading": "Running servez", "content": "Page: Setup\nSection: Running servez\n\nOnce you've done that type\n\nservez path/to/folder/where/you/unzipped/files\n\nOr if you're like me\n\ncd path/to/folder/where/you/unzipped/files\nservez\n\nIt should print something like\n\nThen in your browser go to http://localhost:8080/.\n\nIf you don't specify a path then servez will serve the current folder.\n\nIf either of those options are not to your liking there are many other simple servers to choose from.\n\nNow that you have a server setup we can move on to textures.", "code_blocks": [{"language": "shell", "code": "servez path/to/folder/where/you/unzipped/files", "filename": ""}, {"language": "shell", "code": "cd path/to/folder/where/you/unzipped/files\nservez", "filename": ""}], "chunk_position": 22, "heading_path": "Running servez > Running servez", "breadcrumbs": "Setup > Running servez > Running servez"}, {"id": "e2355dd46b651e79", "url": "https://threejs.org/manual/en/primitives.html", "page_title": "Primitives", "page_type": "guide", "page_summary": "This article covers the built-in 3D primitive geometries available in three.js, including shapes like boxes, spheres, cones, and more advanced options like extrusion, text, and wireframes. It also walks through a complete example demonstrating how to instantiate these primitives with random colors in a scene.", "heading": "Primitives", "content": "Page: Primitives\nSection: Primitives\n\nThree.js has a large number of primitives. Primitives are generally 3D shapes that are generated at runtime with a bunch of parameters.\n\nIt's common to use primitives for things like a sphere for a globe or a bunch of boxes to draw a 3D graph. It's especially common to use primitives to experiment and get started with 3D. For the majority of 3D apps it's more common to have an artist make 3D models in a 3D modeling program like Blender or Maya or Cinema 4D. Later in this series we'll cover making and loading data from several 3D modeling programs. For now let's go over some of the available primitives.\n\nMany of the primitives below have defaults for some or all of their parameters so you can use more or less depending on your needs.", "code_blocks": [], "chunk_position": 23, "heading_path": "Primitives > Primitives", "breadcrumbs": "Primitives > Primitives > Primitives"}, {"id": "8709fa0e1a6b7fb4", "url": "https://threejs.org/manual/en/primitives.html", "page_title": "Primitives", "page_type": "guide", "page_summary": "This article covers the built-in 3D primitive geometries available in three.js, including shapes like boxes, spheres, cones, and more advanced options like extrusion, text, and wireframes. It also walks through a complete example demonstrating how to instantiate these primitives with random colors in a scene.", "heading": "BoxGeometry", "content": "Page: Primitives\nSection: BoxGeometry\n\nA Box", "code_blocks": [{"language": "javascript", "code": "const width = 8;  // ui: width\nconst height = 8;  // ui: height\nconst depth = 8;  // ui: depth\nconst geometry = new THREE.BoxGeometry( width, height, depth );", "filename": ""}, {"language": "javascript", "code": "const 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 );", "filename": ""}], "chunk_position": 23, "heading_path": "BoxGeometry > BoxGeometry", "breadcrumbs": "Primitives > BoxGeometry > BoxGeometry"}, {"id": "8bdc7362934a5033", "url": "https://threejs.org/manual/en/primitives.html", "page_title": "Primitives", "page_type": "guide", "page_summary": "This article covers the built-in 3D primitive geometries available in three.js, including shapes like boxes, spheres, cones, and more advanced options like extrusion, text, and wireframes. It also walks through a complete example demonstrating how to instantiate these primitives with random colors in a scene.", "heading": "CircleGeometry", "content": "Page: Primitives\nSection: CircleGeometry\n\nA flat circle", "code_blocks": [{"language": "javascript", "code": "const radius = 7;  // ui: radius\nconst segments = 24;  // ui: segments\nconst geometry = new THREE.CircleGeometry( radius, segments );", "filename": ""}, {"language": "javascript", "code": "const 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 );", "filename": ""}], "chunk_position": 23, "heading_path": "CircleGeometry > CircleGeometry", "breadcrumbs": "Primitives > CircleGeometry > CircleGeometry"}, {"id": "09a5d3c607ef2cd7", "url": "https://threejs.org/manual/en/primitives.html", "page_title": "Primitives", "page_type": "guide", "page_summary": "This article covers the built-in 3D primitive geometries available in three.js, including shapes like boxes, spheres, cones, and more advanced options like extrusion, text, and wireframes. It also walks through a complete example demonstrating how to instantiate these primitives with random colors in a scene.", "heading": "ConeGeometry", "content": "Page: Primitives\nSection: ConeGeometry\n\nA Cone", "code_blocks": [{"language": "javascript", "code": "const radius = 6;  // ui: radius\nconst height = 8;  // ui: height\nconst radialSegments = 16;  // ui: radialSegments\nconst geometry = new THREE.ConeGeometry( radius, height, radialSegments );", "filename": ""}, {"language": "javascript", "code": "const 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 );", "filename": ""}], "chunk_position": 23, "heading_path": "ConeGeometry > ConeGeometry", "breadcrumbs": "Primitives > ConeGeometry > ConeGeometry"}, {"id": "54a1c707a7cdf079", "url": "https://threejs.org/manual/en/primitives.html", "page_title": "Primitives", "page_type": "guide", "page_summary": "This article covers the built-in 3D primitive geometries available in three.js, including shapes like boxes, spheres, cones, and more advanced options like extrusion, text, and wireframes. It also walks through a complete example demonstrating how to instantiate these primitives with random colors in a scene.", "heading": "CylinderGeometry", "content": "Page: Primitives\nSection: CylinderGeometry\n\nA Cylinder", "code_blocks": [{"language": "javascript", "code": "const 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 );", "filename": ""}, {"language": "javascript", "code": "const 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 );", "filename": ""}], "chunk_position": 23, "heading_path": "CylinderGeometry > CylinderGeometry", "breadcrumbs": "Primitives > CylinderGeometry > CylinderGeometry"}, {"id": "e486987b2b87defe", "url": "https://threejs.org/manual/en/primitives.html", "page_title": "Primitives", "page_type": "guide", "page_summary": "This article covers the built-in 3D primitive geometries available in three.js, including shapes like boxes, spheres, cones, and more advanced options like extrusion, text, and wireframes. It also walks through a complete example demonstrating how to instantiate these primitives with random colors in a scene.", "heading": "DodecahedronGeometry", "content": "Page: Primitives\nSection: DodecahedronGeometry\n\nA dodecahedron (12 sides)", "code_blocks": [{"language": "javascript", "code": "const radius = 7;  // ui: radius\nconst geometry = new THREE.DodecahedronGeometry( radius );", "filename": ""}, {"language": "javascript", "code": "const radius = 7;  // ui: radius\nconst detail = 2;  // ui: detail\nconst geometry = new THREE.DodecahedronGeometry( radius, detail );", "filename": ""}], "chunk_position": 23, "heading_path": "DodecahedronGeometry > DodecahedronGeometry", "breadcrumbs": "Primitives > DodecahedronGeometry > DodecahedronGeometry"}, {"id": "9b306ef0d28a0961", "url": "https://threejs.org/manual/en/primitives.html", "page_title": "Primitives", "page_type": "guide", "page_summary": "This article covers the built-in 3D primitive geometries available in three.js, including shapes like boxes, spheres, cones, and more advanced options like extrusion, text, and wireframes. It also walks through a complete example demonstrating how to instantiate these primitives with random colors in a scene.", "heading": "ExtrudeGeometry", "content": "Page: Primitives\nSection: ExtrudeGeometry\n\nAn extruded 2d shape with optional bevelling. Here we are extruding a heart shape. Note this is the basis for TextGeometry.", "code_blocks": [{"language": "javascript", "code": "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);", "filename": ""}, {"language": "javascript", "code": "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 => 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 => new THREE.Vector3(...p, 0));\nfor (let i = 0; i < 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;", "filename": ""}], "chunk_position": 23, "heading_path": "ExtrudeGeometry > ExtrudeGeometry", "breadcrumbs": "Primitives > ExtrudeGeometry > ExtrudeGeometry"}, {"id": "e4709563a7cbd249", "url": "https://threejs.org/manual/en/primitives.html", "page_title": "Primitives", "page_type": "guide", "page_summary": "This article covers the built-in 3D primitive geometries available in three.js, including shapes like boxes, spheres, cones, and more advanced options like extrusion, text, and wireframes. It also walks through a complete example demonstrating how to instantiate these primitives with random colors in a scene.", "heading": "IcosahedronGeometry", "content": "Page: Primitives\nSection: IcosahedronGeometry\n\nAn icosahedron (20 sides)", "code_blocks": [{"language": "javascript", "code": "const radius = 7;  // ui: radius\nconst geometry = new THREE.IcosahedronGeometry( radius );", "filename": ""}, {"language": "javascript", "code": "const radius = 7;  // ui: radius\nconst detail = 2;  // ui: detail\nconst geometry = new THREE.IcosahedronGeometry( radius, detail );", "filename": ""}], "chunk_position": 23, "heading_path": "IcosahedronGeometry > IcosahedronGeometry", "breadcrumbs": "Primitives > IcosahedronGeometry > IcosahedronGeometry"}, {"id": "889d1516039f2430", "url": "https://threejs.org/manual/en/primitives.html", "page_title": "Primitives", "page_type": "guide", "page_summary": "This article covers the built-in 3D primitive geometries available in three.js, including shapes like boxes, spheres, cones, and more advanced options like extrusion, text, and wireframes. It also walks through a complete example demonstrating how to instantiate these primitives with random colors in a scene.", "heading": "LatheGeometry", "content": "Page: Primitives\nSection: LatheGeometry\n\nA 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.", "code_blocks": [{"language": "javascript", "code": "const points = [];\nfor ( let i = 0; i < 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 );", "filename": ""}, {"language": "javascript", "code": "const points = [];\nfor ( let i = 0; i < 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 );", "filename": ""}], "chunk_position": 23, "heading_path": "LatheGeometry > LatheGeometry", "breadcrumbs": "Primitives > LatheGeometry > LatheGeometry"}, {"id": "581059e1d784b1e9", "url": "https://threejs.org/manual/en/primitives.html", "page_title": "Primitives", "page_type": "guide", "page_summary": "This article covers the built-in 3D primitive geometries available in three.js, including shapes like boxes, spheres, cones, and more advanced options like extrusion, text, and wireframes. It also walks through a complete example demonstrating how to instantiate these primitives with random colors in a scene.", "heading": "OctahedronGeometry", "content": "Page: Primitives\nSection: OctahedronGeometry\n\nAn Octahedron (8 sides)", "code_blocks": [{"language": "javascript", "code": "const radius = 7;  // ui: radius\nconst geometry = new THREE.OctahedronGeometry( radius );", "filename": ""}, {"language": "javascript", "code": "const radius = 7;  // ui: radius\nconst detail = 2;  // ui: detail\nconst geometry = new THREE.OctahedronGeometry( radius, detail );", "filename": ""}], "chunk_position": 23, "heading_path": "OctahedronGeometry > OctahedronGeometry", "breadcrumbs": "Primitives > OctahedronGeometry > OctahedronGeometry"}, {"id": "643dd03754355221", "url": "https://threejs.org/manual/en/primitives.html", "page_title": "Primitives", "page_type": "guide", "page_summary": "This article covers the built-in 3D primitive geometries available in three.js, including shapes like boxes, spheres, cones, and more advanced options like extrusion, text, and wireframes. It also walks through a complete example demonstrating how to instantiate these primitives with random colors in a scene.", "heading": "ParametricGeometry", "content": "Page: Primitives\nSection: ParametricGeometry\n\nA surface generated by providing a function that takes a 2D point from a grid and returns the corresponding 3d point.", "code_blocks": [{"language": "javascript", "code": "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 < 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 );", "filename": ""}], "chunk_position": 23, "heading_path": "ParametricGeometry > ParametricGeometry", "breadcrumbs": "Primitives > ParametricGeometry > ParametricGeometry"}, {"id": "0b39208b8a93ac24", "url": "https://threejs.org/manual/en/primitives.html", "page_title": "Primitives", "page_type": "guide", "page_summary": "This article covers the built-in 3D primitive geometries available in three.js, including shapes like boxes, spheres, cones, and more advanced options like extrusion, text, and wireframes. It also walks through a complete example demonstrating how to instantiate these primitives with random colors in a scene.", "heading": "PlaneGeometry", "content": "Page: Primitives\nSection: PlaneGeometry\n\nA 2D plane", "code_blocks": [{"language": "javascript", "code": "const width = 9;  // ui: width\nconst height = 9;  // ui: height\nconst geometry = new THREE.PlaneGeometry( width, height );", "filename": ""}, {"language": "javascript", "code": "const 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 );", "filename": ""}], "chunk_position": 23, "heading_path": "PlaneGeometry > PlaneGeometry", "breadcrumbs": "Primitives > PlaneGeometry > PlaneGeometry"}, {"id": "bcfd372eca72e735", "url": "https://threejs.org/manual/en/primitives.html", "page_title": "Primitives", "page_type": "guide", "page_summary": "This article covers the built-in 3D primitive geometries available in three.js, including shapes like boxes, spheres, cones, and more advanced options like extrusion, text, and wireframes. It also walks through a complete example demonstrating how to instantiate these primitives with random colors in a scene.", "heading": "PolyhedronGeometry", "content": "Page: Primitives\nSection: PolyhedronGeometry\n\nTakes a set of triangles centered around a point and projects them onto a sphere", "code_blocks": [{"language": "javascript", "code": "const 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 );", "filename": ""}], "chunk_position": 23, "heading_path": "PolyhedronGeometry > PolyhedronGeometry", "breadcrumbs": "Primitives > PolyhedronGeometry > PolyhedronGeometry"}, {"id": "24373ce97d72da5e", "url": "https://threejs.org/manual/en/primitives.html", "page_title": "Primitives", "page_type": "guide", "page_summary": "This article covers the built-in 3D primitive geometries available in three.js, including shapes like boxes, spheres, cones, and more advanced options like extrusion, text, and wireframes. It also walks through a complete example demonstrating how to instantiate these primitives with random colors in a scene.", "heading": "RingGeometry", "content": "Page: Primitives\nSection: RingGeometry\n\nA 2D disc with a hole in the center", "code_blocks": [{"language": "javascript", "code": "const innerRadius = 2;  // ui: innerRadius\nconst outerRadius = 7;  // ui: outerRadius\nconst thetaSegments = 18;  // ui: thetaSegments\nconst geometry = new THREE.RingGeometry(\n\tinnerRadius, outerRadius, thetaSegments );", "filename": ""}, {"language": "javascript", "code": "const 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 );", "filename": ""}], "chunk_position": 23, "heading_path": "RingGeometry > RingGeometry", "breadcrumbs": "Primitives > RingGeometry > RingGeometry"}, {"id": "205f9a56d2734667", "url": "https://threejs.org/manual/en/primitives.html", "page_title": "Primitives", "page_type": "guide", "page_summary": "This article covers the built-in 3D primitive geometries available in three.js, including shapes like boxes, spheres, cones, and more advanced options like extrusion, text, and wireframes. It also walks through a complete example demonstrating how to instantiate these primitives with random colors in a scene.", "heading": "ShapeGeometry", "content": "Page: Primitives\nSection: ShapeGeometry\n\nA 2D outline that gets triangulated", "code_blocks": [{"language": "javascript", "code": "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 );\nconst geometry = new THREE.ShapeGeometry( shape );", "filename": ""}, {"language": "javascript", "code": "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 );\nconst curveSegments = 5;  // ui: curveSegments\nconst geometry = new THREE.ShapeGeometry( shape, curveSegments );", "filename": ""}], "chunk_position": 23, "heading_path": "ShapeGeometry > ShapeGeometry", "breadcrumbs": "Primitives > ShapeGeometry > ShapeGeometry"}, {"id": "944204990f8f9050", "url": "https://threejs.org/manual/en/primitives.html", "page_title": "Primitives", "page_type": "guide", "page_summary": "This article covers the built-in 3D primitive geometries available in three.js, including shapes like boxes, spheres, cones, and more advanced options like extrusion, text, and wireframes. It also walks through a complete example demonstrating how to instantiate these primitives with random colors in a scene.", "heading": "SphereGeometry", "content": "Page: Primitives\nSection: SphereGeometry\n\nA sphere", "code_blocks": [{"language": "javascript", "code": "const radius = 7;  // ui: radius\nconst widthSegments = 12;  // ui: widthSegments\nconst heightSegments = 8;  // ui: heightSegments\nconst geometry = new THREE.SphereGeometry( radius, widthSegments, heightSegments );", "filename": ""}, {"language": "javascript", "code": "const 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 );", "filename": ""}], "chunk_position": 23, "heading_path": "SphereGeometry > SphereGeometry", "breadcrumbs": "Primitives > SphereGeometry > SphereGeometry"}, {"id": "59d910b10854d302", "url": "https://threejs.org/manual/en/primitives.html", "page_title": "Primitives", "page_type": "guide", "page_summary": "This article covers the built-in 3D primitive geometries available in three.js, including shapes like boxes, spheres, cones, and more advanced options like extrusion, text, and wireframes. It also walks through a complete example demonstrating how to instantiate these primitives with random colors in a scene.", "heading": "TetrahedronGeometry", "content": "Page: Primitives\nSection: TetrahedronGeometry\n\nA tetrahedron (4 sides)", "code_blocks": [{"language": "javascript", "code": "const radius = 7;  // ui: radius\nconst geometry = new THREE.TetrahedronGeometry( radius );", "filename": ""}, {"language": "javascript", "code": "const radius = 7;  // ui: radius\nconst detail = 2;  // ui: detail\nconst geometry = new THREE.TetrahedronGeometry( radius, detail );", "filename": ""}], "chunk_position": 23, "heading_path": "TetrahedronGeometry > TetrahedronGeometry", "breadcrumbs": "Primitives > TetrahedronGeometry > TetrahedronGeometry"}, {"id": "b03975686338c469", "url": "https://threejs.org/manual/en/primitives.html", "page_title": "Primitives", "page_type": "guide", "page_summary": "This article covers the built-in 3D primitive geometries available in three.js, including shapes like boxes, spheres, cones, and more advanced options like extrusion, text, and wireframes. It also walks through a complete example demonstrating how to instantiate these primitives with random colors in a scene.", "heading": "TextGeometry", "content": "Page: Primitives\nSection: TextGeometry\n\n3D text generated from a 3D font and a string", "code_blocks": [{"language": "javascript", "code": "const loader = new THREE.FontLoader();\n\nloader.load('../resources/threejs/fonts/helvetiker_regular.typeface.json', (font) => {\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});", "filename": ""}], "chunk_position": 23, "heading_path": "TextGeometry > TextGeometry", "breadcrumbs": "Primitives > TextGeometry > TextGeometry"}, {"id": "544b52365177f0d1", "url": "https://threejs.org/manual/en/primitives.html", "page_title": "Primitives", "page_type": "guide", "page_summary": "This article covers the built-in 3D primitive geometries available in three.js, including shapes like boxes, spheres, cones, and more advanced options like extrusion, text, and wireframes. It also walks through a complete example demonstrating how to instantiate these primitives with random colors in a scene.", "heading": "TorusGeometry", "content": "Page: Primitives\nSection: TorusGeometry\n\nA torus (donut)", "code_blocks": [{"language": "javascript", "code": "const 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 );", "filename": ""}], "chunk_position": 23, "heading_path": "TorusGeometry > TorusGeometry", "breadcrumbs": "Primitives > TorusGeometry > TorusGeometry"}, {"id": "18b64910f89fbfd0", "url": "https://threejs.org/manual/en/primitives.html", "page_title": "Primitives", "page_type": "guide", "page_summary": "This article covers the built-in 3D primitive geometries available in three.js, including shapes like boxes, spheres, cones, and more advanced options like extrusion, text, and wireframes. It also walks through a complete example demonstrating how to instantiate these primitives with random colors in a scene.", "heading": "TorusKnotGeometry", "content": "Page: Primitives\nSection: TorusKnotGeometry\n\nA torus knot", "code_blocks": [{"language": "javascript", "code": "const 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 );", "filename": ""}], "chunk_position": 23, "heading_path": "TorusKnotGeometry > TorusKnotGeometry", "breadcrumbs": "Primitives > TorusKnotGeometry > TorusKnotGeometry"}, {"id": "f426a7431d422e2c", "url": "https://threejs.org/manual/en/primitives.html", "page_title": "Primitives", "page_type": "guide", "page_summary": "This article covers the built-in 3D primitive geometries available in three.js, including shapes like boxes, spheres, cones, and more advanced options like extrusion, text, and wireframes. It also walks through a complete example demonstrating how to instantiate these primitives with random colors in a scene.", "heading": "TubeGeometry", "content": "Page: Primitives\nSection: TubeGeometry\n\nA circle traced down a path", "code_blocks": [{"language": "javascript", "code": "class 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 );", "filename": ""}], "chunk_position": 23, "heading_path": "TubeGeometry > TubeGeometry", "breadcrumbs": "Primitives > TubeGeometry > TubeGeometry"}, {"id": "aa1e66187cd17c99", "url": "https://threejs.org/manual/en/primitives.html", "page_title": "Primitives", "page_type": "guide", "page_summary": "This article covers the built-in 3D primitive geometries available in three.js, including shapes like boxes, spheres, cones, and more advanced options like extrusion, text, and wireframes. It also walks through a complete example demonstrating how to instantiate these primitives with random colors in a scene.", "heading": "EdgesGeometry", "content": "Page: Primitives\nSection: EdgesGeometry\n\nA 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 EdgesGeometry instead the middle lines are removed. Adjust the thresholdAngle below and you'll see the edges below that threshold disappear.", "code_blocks": [{"language": "javascript", "code": "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);", "filename": ""}, {"language": "javascript", "code": "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);", "filename": ""}], "chunk_position": 23, "heading_path": "EdgesGeometry > EdgesGeometry", "breadcrumbs": "Primitives > EdgesGeometry > EdgesGeometry"}, {"id": "24230f4c3739812e", "url": "https://threejs.org/manual/en/primitives.html", "page_title": "Primitives", "page_type": "guide", "page_summary": "This article covers the built-in 3D primitive geometries available in three.js, including shapes like boxes, spheres, cones, and more advanced options like extrusion, text, and wireframes. It also walks through a complete example demonstrating how to instantiate these primitives with random colors in a scene.", "heading": "WireframeGeometry", "content": "Page: Primitives\nSection: WireframeGeometry\n\nGenerates 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 wireframe: true you would only get a single line. Passing that triangle geometry to a WireframeGeometry will generate a new geometry that has 3 lines segments using 6 points.", "code_blocks": [{"language": "javascript", "code": "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));", "filename": ""}], "chunk_position": 23, "heading_path": "WireframeGeometry > WireframeGeometry", "breadcrumbs": "Primitives > WireframeGeometry > WireframeGeometry"}, {"id": "7969a78853c83c38", "url": "https://threejs.org/manual/en/primitives.html", "page_title": "Primitives", "page_type": "guide", "page_summary": "This article covers the built-in 3D primitive geometries available in three.js, including shapes like boxes, spheres, cones, and more advanced options like extrusion, text, and wireframes. It also walks through a complete example demonstrating how to instantiate these primitives with random colors in a scene.", "heading": "Building an Example", "content": "Page: Primitives\nSection: Building an Example\n\nWe'll go over creating custom geometry in another article. For now let's make an example creating each type of primitive. We'll start with the examples from the previous article.\n\nNear the top let's set a background color\n\nThis tells three.js to clear to lightish gray.\n\nThe camera needs to change position so that we can see all the objects.\n\nLet's add a function, addObject, that takes an x, y position and an Object3D and adds the object to the scene.\n\nLet's also make a function to create a random colored material. We'll use a feature of Color that lets you set a color based on hue, saturation, and luminance.\n\nhue goes from 0 to 1 around the color wheel with red at 0, green at .33 and blue at .66. saturation goes from 0 to 1 with 0 having no color and 1 being most saturated. luminance goes from 0 to 1 with 0 being black, 1 being white and 0.5 being the maximum amount of color. In other words as luminance goes from 0.0 to 0.5 the color will go from black to hue. From 0.5 to 1.0 the color will go from hue to white.\n\nWe also passed side: THREE.DoubleSide to the material. This tells three to draw both sides of the triangles that make up a shape. For a solid shape like a sphere or a cube there's usually no reason to draw the back sides of triangles as they all face inside the shape. In our case though we are drawing a few things like the PlaneGeometry and the ShapeGeometry which are 2 dimensional and so have no inside. Without setting side: THREE.DoubleSide they would disappear when looking at their back sides.\n\nI should note that it's faster to draw when not setting side: THREE.DoubleSide so ideally we'd set it only on the materials that really need it but in this case we are not drawing too much so there isn't much reason to worry about it.\n\nLet's make a function, addSolidGeometry, that we pass a geometry and it creates a random colored material via createMaterial and adds it to the scene via addObject.\n\nNow we can use this for the majority of the primitives we create. For example creating a box\n\nIf you look in the code below you'll see a similar section for each type of geometry.", "code_blocks": [{"language": "javascript", "code": "const scene = new THREE.Scene();\n+scene.background = new THREE.Color(0xAAAAAA);", "filename": ""}, {"language": "javascript", "code": "-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;", "filename": ""}, {"language": "javascript", "code": "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}", "filename": ""}, {"language": "javascript", "code": "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}", "filename": ""}, {"language": "javascript", "code": "function addSolidGeometry(x, y, geometry) {\n  const mesh = new THREE.Mesh(geometry, createMaterial());\n  addObject(x, y, mesh);\n}", "filename": ""}, {"language": "javascript", "code": "{\n  const width = 8;\n  const height = 8;\n  const depth = 8;\n  addSolidGeometry(-2, -2, new THREE.BoxGeometry(width, height, depth));\n}", "filename": ""}], "chunk_position": 23, "heading_path": "Building an Example > Building an Example", "breadcrumbs": "Primitives > Building an Example > Building an Example"}, {"id": "370fe8d45f7c7e30", "url": "https://threejs.org/manual/en/primitives.html", "page_title": "Primitives", "page_type": "guide", "page_summary": "This article covers the built-in 3D primitive geometries available in three.js, including shapes like boxes, spheres, cones, and more advanced options like extrusion, text, and wireframes. It also walks through a complete example demonstrating how to instantiate these primitives with random colors in a scene.", "heading": "TextGeometry and Font Loading", "content": "Page: Primitives\nSection: TextGeometry and Font Loading\n\nThere are a couple of notable exceptions to the pattern above. The biggest is probably the TextGeometry. It needs to load 3D font data before it can generate a mesh for the text. That data loads asynchronously so we need to wait for it to load before trying to create the geometry. By promisifiying font loading we can make it mush easier. We create a FontLoader and then a function loadFont that returns a promise that on resolve will give us the font. We then create an async function called doit and load the font using await. And finally create the geometry and call addObject to add it the scene.\n\nThere's one other difference. We want to spin the text around its center but by default three.js creates the text such that its center of rotation is on the left edge. To work around this we can ask three.js to compute the bounding box of the geometry. We can then call the getCenter method of the bounding box and pass it our mesh's position object. getCenter copies the center of the box into the position. It also returns the position object so we can call multiplyScalar(-1) to position the entire object such that its center of rotation is at the center of the object.\n\nIf we then just called addSolidGeometry like with previous examples it would set the position again which is no good. So, in this case we create an Object3D which is the standard node for the three.js scene graph. Mesh is inherited from Object3D as well. We'll cover how the scene graph works in another article. For now it's enough to know that like DOM nodes, children are drawn relative to their parent. By making an Object3D and making our mesh a child of that we can position the Object3D wherever we want and still keep the center offset we set earlier.\n\nIf we didn't do this the text would spin off center.", "code_blocks": [{"language": "javascript", "code": "{\n  const loader = new FontLoader();\n  // promisify font loading\n  function loadFont(url) {\n    return new Promise((resolve, reject) => {\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}", "filename": ""}], "chunk_position": 23, "heading_path": "TextGeometry and Font Loading > TextGeometry and Font Loading", "breadcrumbs": "Primitives > TextGeometry and Font Loading > TextGeometry and Font Loading"}, {"id": "4577546814a489ca", "url": "https://threejs.org/manual/en/primitives.html", "page_title": "Primitives", "page_type": "guide", "page_summary": "This article covers the built-in 3D primitive geometries available in three.js, including shapes like boxes, spheres, cones, and more advanced options like extrusion, text, and wireframes. It also walks through a complete example demonstrating how to instantiate these primitives with random colors in a scene.", "heading": "Line Geometry Helpers", "content": "Page: Primitives\nSection: Line Geometry Helpers\n\nThe other exceptions are the 2 line based examples for EdgesGeometry and WireframeGeometry. Instead of calling addSolidGeometry they call addLineGeometry which looks like this\n\nIt creates a black LineBasicMaterial and then creates a LineSegments object which is a wrapper for Mesh that helps three know you're rendering line segments (2 points per segment).\n\nEach of the primitives has several parameters you can pass on creation and it's best to look in the documentation for all of them rather than repeat them here. You can also click the links above next to each shape to take you directly to the docs for that shape.", "code_blocks": [{"language": "javascript", "code": "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}", "filename": ""}], "chunk_position": 23, "heading_path": "Line Geometry Helpers > Line Geometry Helpers", "breadcrumbs": "Primitives > Line Geometry Helpers > Line Geometry Helpers"}, {"id": "54afa23a382c1b2a", "url": "https://threejs.org/manual/en/primitives.html", "page_title": "Primitives", "page_type": "guide", "page_summary": "This article covers the built-in 3D primitive geometries available in three.js, including shapes like boxes, spheres, cones, and more advanced options like extrusion, text, and wireframes. It also walks through a complete example demonstrating how to instantiate these primitives with random colors in a scene.", "heading": "Points and PointsMaterial", "content": "Page: Primitives\nSection: Points and PointsMaterial\n\nThere is one other pair of classes that doesn't really fit the patterns above. Those are the PointsMaterial and the Points class. Points is like LineSegments above in that it takes a a BufferGeometry but draws points at each vertex instead of lines. To use it you also need to pass it a PointsMaterial which take a size for how large to make the points.\n\nYou can turn off sizeAttenuation by setting it to false if you want the points to be the same size regardless of their distance from the camera.", "code_blocks": [{"language": "javascript", "code": "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);", "filename": ""}, {"language": "javascript", "code": "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...", "filename": ""}], "chunk_position": 23, "heading_path": "Points and PointsMaterial > Points and PointsMaterial", "breadcrumbs": "Primitives > Points and PointsMaterial > Points and PointsMaterial"}, {"id": "31916349061ddb00", "url": "https://threejs.org/manual/en/primitives.html", "page_title": "Primitives", "page_type": "guide", "page_summary": "This article covers the built-in 3D primitive geometries available in three.js, including shapes like boxes, spheres, cones, and more advanced options like extrusion, text, and wireframes. It also walks through a complete example demonstrating how to instantiate these primitives with random colors in a scene.", "heading": "Subdivisions", "content": "Page: Primitives\nSection: Subdivisions\n\nOne other thing that's important to cover is that almost all shapes have various settings for how much to subdivide them. A good example might be the sphere geometries. Spheres take parameters for how many divisions to make around and how many top to bottom. For example\n\nThe first sphere has 5 segments around and 3 high which is 15 segments or 30 triangles. The second sphere has 24 segments by 10. That's 240 segments or 480 triangles. The last one has 50 by 50 which is 2500 segments or 5000 triangles.\n\nIt's up to you to decide how many subdivisions you need. It might look like you need a high number of segments but remove the lines and the flat shading and we get this\n\nIt's now not so clear that the one on the right with 5000 triangles is entirely better than the one in the middle with only 480. If you're only drawing a few spheres, like say a single globe for a map of the earth, then a single 10000 triangle sphere is not a bad choice. If on the other hand you're trying to draw 1000 spheres then 1000 spheres times 10000 triangles each is 10 million triangles. To animate smoothly you need the browser to draw at 60 frames per second so you'd be asking the browser to draw 600 million triangles per second. That's a lot of computing.\n\nSometimes it's easy to choose. For example you can also choose to subdivide a plane.\n\nThe plane on the left is 2 triangles. The plane on the right is 200 triangles. Unlike the sphere there is really no trade off in quality for most use cases of a plane. You'd most likely only subdivide a plane if you expected to want to modify or warp it in some way. A box is similar.\n\nSo, choose whatever is appropriate for your situation. The less subdivisions you choose the more likely things will run smoothly and the less memory they'll take. You'll have to decide for yourself what the correct tradeoff is for your particular situation.\n\nIf none of the shapes above fit your use case you can load geometry for example from a .obj file or a .gltf file. You can also create your own custom BufferGeometry.", "code_blocks": [], "chunk_position": 23, "heading_path": "Subdivisions > Subdivisions", "breadcrumbs": "Primitives > Subdivisions > Subdivisions"}, {"id": "e7f0dbcf88678fe8", "url": "https://threejs.org/manual/en/scenegraph.html", "page_title": "Scene Graph", "page_type": "reference", "page_summary": "Extraction fallback content. [stop]", "heading": "Scene Graph", "content": "Page: Scene Graph\nSection: Scene Graph\n\n\n\n\n\n##### Scene Graph\n\n\n\n\n\n\n\nThis article is part of a series of articles about three.js. The\nfirst article is [three.js fundamentals](fundamentals.html). If\nyou haven't read that yet you might want to consider starting there.\n\n\n\n\nThree.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.\n\n\n\n\n<img src=\"../resources/images/scenegraph-generic.svg\">\n\n\n\n\nThat's kind of abstract so let's try to give some examples.\n\n\n\n\nOne example might be solar system, sun, earth, moon.\n\n\n\n\n<img src=\"../resources/images/scenegraph-solarsystem.svg\">\n\n\n\n\nThe 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.\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\nTo 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\".\n\n\n\n\nLet'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.\n\n\n\n\n\nWe're using a really low-polygon sphere. Only 6 subdivisions around its equator.\nThis is so it's easy to see the rotation.\n\n\n\n\nWe're going to reuse the same sphere for everything so we'll set a scale\nfor the sun mesh of 5x.\n\n\n\n\nWe also set the phong material's `emissive` 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.\n\n\n\n\nLet'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.\n\n\n\n\n\nTo 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 `lookAt` function. The `lookAt`\nfunction will orient the camera from its position to \"look at\" the position\nwe pass to `lookAt`. 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.\n\n\n\n\n\nIn the render loop, adapted from previous examples, we're rotating all\nobjects in our `objects` array with this code.\n\n\n\n\n\nSince we added the `sunMesh` to the `objects` array it will rotate.\n\n\n\n\n\n\n\n\n[click here to open in a separate window](/manual/examples/scenegraph-sun.html)\n\n\n\n\n\n\n\n\nNow let's add in the earth.\n\n\n\n\n\nWe make a material that is blue but we gave it a small amount of  *emissive*  blue\nso that it will show up against our black background.\n\n\n\n\nWe use the same `sphereGeometry` with our new blue `earthMaterial` to make\nan `earthMesh`. We position that 10 units to the left of the sun\nand add it to the scene.  Since we added it to our `objects` array it will\nrotate too.\n\n\n\n\n\n\n\n\n[click here to open in a separate window](/manual/examples/scenegraph-sun-earth.html)\n\n\n\n\n\n\n\n\nYou 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\n\n\n\n\n\nand...\n\n\n\n\n\n\n\n\n[click here to open in a separate window](/manual/examples/scenegraph-sun-earth-orbit.html)\n\n\n\n\n\n\n\n\nWhat 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.\n\n\n\n\nWe made the `earthMesh` a child of the `sunMesh`. The `sunMesh` has\nits scale set to 5x with `sunMesh.scale.set(5, 5, 5)`. That means the\n`sunMesh`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 (`earthMesh.position.x = 10`) is also\n 5x as well.\n\n\n\n\n Our scene graph currently looks like this\n\n\n\n\n<img src=\"../resources/images/scenegraph-sun-earth.svg\">\n\n\n\n\nTo fix it let's add an empty scene graph node. We'll parent both the sun and the earth\nto that node.\n\n\n\n\n\nHere we made an [`Object3D`](/docs/#api/en/core/Object3D). Like a [`Mesh`](/docs/#api/en/objects/Mesh) it is also a node in the scene graph\nbut unlike a [`Mesh`](/docs/#api/en/objects/Mesh) it has no material or geometry. It just represents a local space.\n\n\n\n\nOur new scene graph looks like this\n\n\n\n\n<img src=\"../resources/images/scenegraph-sun-earth-fixed.svg\">\n\n\n\n\nBoth the `sunMesh` and the `earthMesh` are children of the `solarSystem`. All 3\nare being rotated and now because the `earthMesh` is not a child of the `sunMesh`\nit is no longer scaled by 5x.\n\n\n\n\n\n\n\n\n[click here to open in a separate window](/manual/examples/scenegraph-sun-earth-orbit-fixed.html)\n\n\n\n\n\n\n\n\nMuch better. The earth is smaller than the sun and it's rotating around the sun\nand rotating itself.\n\n\n\n\nContinuing that same pattern let's add a moon.\n\n\n\n\n\nAgain we added more invisible scene graph nodes. The first, an [`Object3D`](/docs/#api/en/core/Object3D) called `earthOrbit`\nand added both the `earthMesh` and the `moonOrbit` to it, also new. We then added the `moonMesh`\nto the `moonOrbit`. The new scene graph looks like this.\n\n\n\n\n<img src=\"../resources/images/scenegraph-sun-earth-moon.svg\">\n\n\n\n\nand here's that\n\n\n\n\n\n\n\n\n[click here to open in a separate window](/manual/examples/scenegraph-sun-earth-moon.html)\n\n\n\n\n\n\n\n\nYou 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.\n\n\n\n\nIt 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.\n\n\n\n\nOne is called an [`AxesHelper`](/docs/#api/en/helpers/AxesHelper). It draws 3 lines representing the local\nX,\nY, and\nZ axes. Let's add one to every node we\ncreated.\n\n\n\n\n\nOn our case we want the axes to appear even though they are inside the spheres.\nTo do this we set their material's `depthTest` to false which means they will\nnot check to see if they are drawing behind something else. We also\nset their `renderOrder` 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.\n\n\n\n\n\n\n\n\n[click here to open in a separate window](/manual/examples/scenegraph-sun-earth-moon-axes.html)\n\n\n\n\n\n\n\n\nWe can see the\nx (red) and\nz (blue) 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 y (green) axes.\n\n\n\n\nIt might be hard to see some of them as there are 2 pairs of overlapping axes. Both the `sunMesh`\nand the `solarSystem` are at the same position. Similarly the `earthMesh` and\n`earthOrbit` 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 [`GridHelper`](/docs/#api/en/helpers/GridHelper). It\nmakes a 2D grid on the X,Z plane. By default the grid is 10x10 units.\n\n\n\n\nWe're also going to use [lil-gui](https://github.com/georgealways/lil-gui) 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.\n\n\n\n\nWe want to make both a [`GridHelper`](/docs/#api/en/helpers/GridHelper) and an [`AxesHelper`](/docs/#api/en/helpers/AxesHelper) 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\n\n\n\n\n\n`makeAxisGrid` makes an `AxisGridHelper` 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 `bool` 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 [`AxesHelper`](/docs/#api/en/helpers/AxesHelper) and [`GridHelper`](/docs/#api/en/helpers/GridHelper) for a node.\n\n\n\n\n\nOne thing to notice is we set the `renderOrder` of the [`AxesHelper`](/docs/#api/en/helpers/AxesHelper)\nto 2 and for the [`GridHelper`](/docs/#api/en/helpers/GridHelper) to 1 so that the axes get drawn after the grid.\nOtherwise the grid might overwrite the axes.\n\n\n\n\n\n\n\n\n[click here to open in a separate window](/manual/examples/scenegraph-sun-earth-moon-axes-grids.html)\n\n\n\n\n\n\n\n\nTurn on the `solarSystem` 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  *local space*  of the `solarSystem`. Similarly if you\nturn on the `earthOrbit` you'll see how the moon is exactly 2 units\nfrom the center of the  *local space*  of the `earthOrbit`.\n\n\n\n\nA few more examples of scene graphs. An automobile in a simple game world might have a scene graph like this\n\n\n\n\n<img src=\"../resources/images/scenegraph-car.svg\">\n\n\n\n\nIf 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.\n\n\n\n\nAnother example is a human in a game world.\n\n\n\n\n<img src=\"../resources/images/scenegraph-human.svg\">\n\n\n\n\nYou 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.\n\n\n\n\nLet'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.\n\n\n\n\nHere's the scene graph. The meshes are colored in green, the [`Object3D`](/docs/#api/en/core/Object3D)s in blue,\nthe lights in gold, and the cameras in purple. One camera has not been added\nto the scene graph.\n\n\n<img src=\"../resources/images/scenegraph-tank.svg\">\n\n\nLook in the code to see the setup of all of these nodes.\n\n\n\n\nFor the target, the thing the tank is aiming at, there is a `targetOrbit`\n([`Object3D`](/docs/#api/en/core/Object3D)) which just rotates similar to the `earthOrbit` above. A\n`targetElevation` ([`Object3D`](/docs/#api/en/core/Object3D)) which is a child of the `targetOrbit` provides an\noffset from the `targetOrbit` and a base elevation. Childed to that is another\n[`Object3D`](/docs/#api/en/core/Object3D) called `targetBob` which just bobs up and down relative to the\n`targetElevation`. Finally there's the `targetMesh` which is just a cube we\nrotate and change its colors\n\n\n\n\n\nFor the tank there's an [`Object3D`](/docs/#api/en/core/Object3D) called `tank` which is used to move everything\nbelow it around. The code uses a [`SplineCurve`](/docs/#api/en/extras/curves/SplineCurve) 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 [`Object3D.lookAt`](/docs/#api/en/core/Object3D.lookAt).\n\n\n\n\n\nThe 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 [`Object3D.lookAt`](/docs/#api/en/core/Object3D.lookAt)\n\n\n\n\n\nThere's a `turretCamera` which is a child of the `turretMesh` so\nit will move up and down and rotate with the turret. We make that\naim at the target.\n\n\n\n\n\nThere is also a `targetCameraPivot` which is a child of `targetBob` so it floats\naround with the target. We aim that back at the tank. Its purpose is to allow the\n`targetCamera` to be offset from the target itself. If we instead made the camera\na child of `targetBob` and just aimed the camera itself it would be inside the\ntarget.\n\n\n\n\n\nFinally we rotate all the wheels\n\n\n\n\n\nFor the cameras we setup an array of all 4 cameras at init time with descriptions.\n\n\n\n\n\nand cycle through our cameras at render time.\n\n\n\n\n\n\n\n\n\n[click here to open in a separate window](/manual/examples/scenegraph-tank.html)\n\n\n\n\n\n\n\n\nI hope this gives some idea of how scene graphs work and how you might use them.\nMaking [`Object3D`](/docs/#api/en/core/Object3D) 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.\n\n\n\n\n[Next up we'll go over materials](materials.html).\n\n\n\n\n", "code_blocks": [{"language": "js", "code": "// 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);", "filename": ""}, {"language": "js", "code": "{\n  const color = 0xFFFFFF;\n  const intensity = 500;\n  const light = new THREE.PointLight(color, intensity);\n  scene.add(light);\n}", "filename": ""}, {"language": "js", "code": "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);", "filename": ""}, {"language": "js", "code": "objects.forEach((obj) => {\n  obj.rotation.y = time;\n});", "filename": ""}, {"language": "js", "code": "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);", "filename": ""}, {"language": "js", "code": "-scene.add(earthMesh);\n+sunMesh.add(earthMesh);", "filename": ""}, {"language": "js", "code": "+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);", "filename": ""}, {"language": "js", "code": "+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);", "filename": ""}, {"language": "js", "code": "// add an AxesHelper to each node\nobjects.forEach((node) => {\n  const axes = new THREE.AxesHelper();\n  axes.material.depthTest = false;\n  axes.renderOrder = 1;\n  node.add(axes);\n});", "filename": ""}, {"language": "js", "code": "-// add an AxesHelper to each node\n-objects.forEach((node) => {\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');", "filename": ""}, {"language": "js", "code": "visible", "filename": ""}, {"language": "js", "code": "// 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);", "filename": ""}, {"language": "js", "code": "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);", "filename": ""}, {"language": "js", "code": "const targetPosition = new THREE.Vector3();\n\n...\n\n// face turret at target\ntargetMesh.getWorldPosition(targetPosition);\nturretPivot.lookAt(targetPosition);", "filename": ""}, {"language": "js", "code": "// make the turretCamera look at target\nturretCamera.lookAt(targetPosition);", "filename": ""}, {"language": "js", "code": "// make the targetCameraPivot look at the tank\ntank.getWorldPosition(targetPosition);\ntargetCameraPivot.lookAt(targetPosition);", "filename": ""}, {"language": "js", "code": "wheelMeshes.forEach((obj) => {\n  obj.rotation.x = time * 3;\n});", "filename": ""}, {"language": "js", "code": "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');", "filename": ""}, {"language": "js", "code": "const camera = cameras[time * .25 % cameras.length | 0];\ninfoElem.textContent = camera.desc;", "filename": ""}], "chunk_position": 24, "heading_path": "Scene Graph > Scene Graph", "breadcrumbs": "Scene Graph > Scene Graph > Scene Graph"}, {"id": "24771af2599a0709", "url": "https://threejs.org/manual/en/materials.html", "page_title": "Materials", "page_type": "guide", "page_summary": "An overview of the materials available in three.js, covering how to set material properties, the different material types (Basic, Lambert, Phong, Toon, Standard, Physical, and special-use materials), and common properties shared across materials.", "heading": "Introduction to Materials", "content": "Page: Materials\nSection: Introduction to Materials\n\nThree.js provides several types of materials. They define how objects will appear in the scene. Which materials you use really depends on what you're trying to accomplish.\n\nThis article is part of a series of articles about three.js. The first article is three.js fundamentals. If you haven't read that yet and you're new to three.js you might want to consider starting there.", "code_blocks": [], "chunk_position": 25, "heading_path": "Introduction to Materials > Introduction to Materials", "breadcrumbs": "Materials > Introduction to Materials > Introduction to Materials"}, {"id": "982c1dd4923738cd", "url": "https://threejs.org/manual/en/materials.html", "page_title": "Materials", "page_type": "guide", "page_summary": "An overview of the materials available in three.js, covering how to set material properties, the different material types (Basic, Lambert, Phong, Toon, Standard, Physical, and special-use materials), and common properties shared across materials.", "heading": "Setting Material Properties", "content": "Page: Materials\nSection: Setting Material Properties\n\nThere are 2 ways to set most material properties. One at creation time which we've seen before.\n\nThe other is after creation.", "code_blocks": [{"language": "javascript", "code": "const material = new THREE.MeshPhongMaterial({\n  color: 0xFF0000,    // red (can also use a CSS color string here)\n  flatShading: true,\n});", "filename": ""}, {"language": "javascript", "code": "const material = new THREE.MeshPhongMaterial();\nmaterial.color.setHSL(0, 1, .5);  // red\nmaterial.flatShading = true;", "filename": ""}], "chunk_position": 25, "heading_path": "Setting Material Properties > Setting Material Properties", "breadcrumbs": "Materials > Setting Material Properties > Setting Material Properties"}, {"id": "cc1407c4e2eb79ea", "url": "https://threejs.org/manual/en/materials.html", "page_title": "Materials", "page_type": "guide", "page_summary": "An overview of the materials available in three.js, covering how to set material properties, the different material types (Basic, Lambert, Phong, Toon, Standard, Physical, and special-use materials), and common properties shared across materials.", "heading": "THREE.Color Properties", "content": "Page: Materials\nSection: THREE.Color Properties\n\nNote that properties of type THREE.Color have multiple ways to be set.", "code_blocks": [{"language": "javascript", "code": "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", "filename": ""}], "chunk_position": 25, "heading_path": "THREE.Color Properties > THREE.Color Properties", "breadcrumbs": "Materials > THREE.Color Properties > THREE.Color Properties"}, {"id": "944c6df121f65b07", "url": "https://threejs.org/manual/en/materials.html", "page_title": "Materials", "page_type": "guide", "page_summary": "An overview of the materials available in three.js, covering how to set material properties, the different material types (Basic, Lambert, Phong, Toon, Standard, Physical, and special-use materials), and common properties shared across materials.", "heading": "Color Strings at Creation Time", "content": "Page: Materials\nSection: Color Strings at Creation Time\n\nAt creation time you can pass either a hex number or a CSS string.", "code_blocks": [{"language": "javascript", "code": "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", "filename": ""}], "chunk_position": 25, "heading_path": "Color Strings at Creation Time > Color Strings at Creation Time", "breadcrumbs": "Materials > Color Strings at Creation Time > Color Strings at Creation Time"}, {"id": "aa62aeace3528f1d", "url": "https://threejs.org/manual/en/materials.html", "page_title": "Materials", "page_type": "guide", "page_summary": "An overview of the materials available in three.js, covering how to set material properties, the different material types (Basic, Lambert, Phong, Toon, Standard, Physical, and special-use materials), and common properties shared across materials.", "heading": "Basic, Lambert, and Phong Materials", "content": "Page: Materials\nSection: Basic, Lambert, and Phong Materials\n\nThe MeshBasicMaterial is not affected by lights. The MeshLambertMaterial computes lighting only at the vertices vs the MeshPhongMaterial which computes lighting at every pixel. The MeshPhongMaterial also supports specular highlights.\n\nWhy have all 3 when MeshPhongMaterial can do the same things as MeshBasicMaterial and MeshLambertMaterial? The reason is the more sophisticated material takes more GPU power to draw. On a slower GPU like say a mobile phone you might want to reduce the GPU power needed to draw your scene by using one of the less complex materials. It also follows that if you don't need the extra features then use the simplest material. If you don't need the lighting and the specular highlight then use the MeshBasicMaterial.", "code_blocks": [], "chunk_position": 25, "heading_path": "Basic, Lambert, and Phong Materials > Basic, Lambert, and Phong Materials", "breadcrumbs": "Materials > Basic, Lambert, and Phong Materials > Basic, Lambert, and Phong Materials"}, {"id": "c75e695ee07235a4", "url": "https://threejs.org/manual/en/materials.html", "page_title": "Materials", "page_type": "guide", "page_summary": "An overview of the materials available in three.js, covering how to set material properties, the different material types (Basic, Lambert, Phong, Toon, Standard, Physical, and special-use materials), and common properties shared across materials.", "heading": "Shininess", "content": "Page: Materials\nSection: Shininess\n\nThe shininess setting of the MeshPhongMaterial determines the shininess of the specular highlight. It defaults to 30.\n\nNote that setting the emissive property to a color on either a MeshLambertMaterial or a MeshPhongMaterial and setting the color to black (and shininess to 0 for phong) ends up looking just like the MeshBasicMaterial.", "code_blocks": [], "chunk_position": 25, "heading_path": "Shininess > Shininess", "breadcrumbs": "Materials > Shininess > Shininess"}, {"id": "3998167cbd1c7e2a", "url": "https://threejs.org/manual/en/materials.html", "page_title": "Materials", "page_type": "guide", "page_summary": "An overview of the materials available in three.js, covering how to set material properties, the different material types (Basic, Lambert, Phong, Toon, Standard, Physical, and special-use materials), and common properties shared across materials.", "heading": "MeshToonMaterial", "content": "Page: Materials\nSection: MeshToonMaterial\n\nThe MeshToonMaterial is similar to the MeshPhongMaterial with one big difference. Rather than shading smoothly it uses a gradient map (an X by 1 texture) to decide how to shade. The default uses a gradient map that is 70% brightness for the first 70% and 100% after but you can supply your own gradient map. This ends up giving a 2 tone look that looks like a cartoon.", "code_blocks": [], "chunk_position": 25, "heading_path": "MeshToonMaterial > MeshToonMaterial", "breadcrumbs": "Materials > MeshToonMaterial > MeshToonMaterial"}, {"id": "bd98d1ad8d64f69c", "url": "https://threejs.org/manual/en/materials.html", "page_title": "Materials", "page_type": "guide", "page_summary": "An overview of the materials available in three.js, covering how to set material properties, the different material types (Basic, Lambert, Phong, Toon, Standard, Physical, and special-use materials), and common properties shared across materials.", "heading": "Physically Based Rendering (PBR) Materials", "content": "Page: Materials\nSection: Physically Based Rendering (PBR) Materials\n\nNext up there are 2 physically based rendering materials. Physically Based Rendering is often abbreviated PBR.\n\nThe materials above use simple math to make materials that look 3D but they aren't what actually happens in real world. The 2 PBR materials use much more complex math to come close to what actually happens in the real world.", "code_blocks": [], "chunk_position": 25, "heading_path": "Physically Based Rendering (PBR) Materials > Physically Based Rendering (PBR) Materials", "breadcrumbs": "Materials > Physically Based Rendering (PBR) Materials > Physically Based Rendering (PBR) Materials"}, {"id": "043a7ca7287e541d", "url": "https://threejs.org/manual/en/materials.html", "page_title": "Materials", "page_type": "guide", "page_summary": "An overview of the materials available in three.js, covering how to set material properties, the different material types (Basic, Lambert, Phong, Toon, Standard, Physical, and special-use materials), and common properties shared across materials.", "heading": "MeshStandardMaterial", "content": "Page: Materials\nSection: MeshStandardMaterial\n\nThe first one is MeshStandardMaterial. The biggest difference between MeshPhongMaterial and MeshStandardMaterial is it uses different parameters. MeshPhongMaterial had a shininess setting. MeshStandardMaterial has 2 settings roughness and metalness.\n\nAt a basic level roughness is the opposite of shininess. Something that has a high roughness, like a baseball doesn't have hard reflections whereas something that's not rough, like a billiard ball, is very shiny. Roughness goes from 0 to 1.\n\nThe other setting, metalness, says how metal the material is. Metals behave differently than non-metals. 0 for non-metal and 1 for metal.", "code_blocks": [], "chunk_position": 25, "heading_path": "MeshStandardMaterial > MeshStandardMaterial", "breadcrumbs": "Materials > MeshStandardMaterial > MeshStandardMaterial"}, {"id": "06d9e895d963dc13", "url": "https://threejs.org/manual/en/materials.html", "page_title": "Materials", "page_type": "guide", "page_summary": "An overview of the materials available in three.js, covering how to set material properties, the different material types (Basic, Lambert, Phong, Toon, Standard, Physical, and special-use materials), and common properties shared across materials.", "heading": "MeshPhysicalMaterial", "content": "Page: Materials\nSection: MeshPhysicalMaterial\n\nThe MeshPhysicalMaterial is same as the MeshStandardMaterial but it adds a clearcoat parameter that goes from 0 to 1 for how much to apply a clearcoat gloss layer and a clearCoatRoughness parameter that specifies how rough the gloss layer is.", "code_blocks": [], "chunk_position": 25, "heading_path": "MeshPhysicalMaterial > MeshPhysicalMaterial", "breadcrumbs": "Materials > MeshPhysicalMaterial > MeshPhysicalMaterial"}, {"id": "ee694929ad6c05e7", "url": "https://threejs.org/manual/en/materials.html", "page_title": "Materials", "page_type": "guide", "page_summary": "An overview of the materials available in three.js, covering how to set material properties, the different material types (Basic, Lambert, Phong, Toon, Standard, Physical, and special-use materials), and common properties shared across materials.", "heading": "Material Performance Progression", "content": "Page: Materials\nSection: Material Performance Progression\n\nThe various standard materials progress from fastest to slowest MeshBasicMaterial ➡ MeshLambertMaterial ➡ MeshPhongMaterial ➡ MeshStandardMaterial ➡ MeshPhysicalMaterial. The slower materials can make more realistic looking scenes but you might need to design your code to use the faster materials on low powered or mobile machines.", "code_blocks": [], "chunk_position": 25, "heading_path": "Material Performance Progression > Material Performance Progression", "breadcrumbs": "Materials > Material Performance Progression > Material Performance Progression"}, {"id": "4ccc9447d90a004a", "url": "https://threejs.org/manual/en/materials.html", "page_title": "Materials", "page_type": "guide", "page_summary": "An overview of the materials available in three.js, covering how to set material properties, the different material types (Basic, Lambert, Phong, Toon, Standard, Physical, and special-use materials), and common properties shared across materials.", "heading": "ShadowMaterial", "content": "Page: Materials\nSection: ShadowMaterial\n\nThere are 3 materials that have special uses. ShadowMaterial is used to get the data created from shadows. We haven't covered shadows yet. When we do we'll use this material to take a peek at what's happening behind the scenes.", "code_blocks": [], "chunk_position": 25, "heading_path": "ShadowMaterial > ShadowMaterial", "breadcrumbs": "Materials > ShadowMaterial > ShadowMaterial"}, {"id": "8a95b649c901a541", "url": "https://threejs.org/manual/en/materials.html", "page_title": "Materials", "page_type": "guide", "page_summary": "An overview of the materials available in three.js, covering how to set material properties, the different material types (Basic, Lambert, Phong, Toon, Standard, Physical, and special-use materials), and common properties shared across materials.", "heading": "MeshDepthMaterial", "content": "Page: Materials\nSection: MeshDepthMaterial\n\nThe MeshDepthMaterial renders the depth of each pixel where pixels at negative near of the camera are 0 and negative far are 1. Certain special effects can use this data which we'll get into at another time.", "code_blocks": [], "chunk_position": 25, "heading_path": "MeshDepthMaterial > MeshDepthMaterial", "breadcrumbs": "Materials > MeshDepthMaterial > MeshDepthMaterial"}, {"id": "f66ed3b5f230d7ba", "url": "https://threejs.org/manual/en/materials.html", "page_title": "Materials", "page_type": "guide", "page_summary": "An overview of the materials available in three.js, covering how to set material properties, the different material types (Basic, Lambert, Phong, Toon, Standard, Physical, and special-use materials), and common properties shared across materials.", "heading": "MeshNormalMaterial", "content": "Page: Materials\nSection: MeshNormalMaterial\n\nThe MeshNormalMaterial will show you the normals of geometry. Normals are the direction a particular triangle or pixel faces. MeshNormalMaterial draws the view space normals (the normals relative to the camera). x is red, y is green, and z is blue so things facing to the right will be pink, to the left will be aqua, up will be light green, down will be purple, and toward the screen will be lavender.", "code_blocks": [], "chunk_position": 25, "heading_path": "MeshNormalMaterial > MeshNormalMaterial", "breadcrumbs": "Materials > MeshNormalMaterial > MeshNormalMaterial"}, {"id": "4c1541a2a32d9b4e", "url": "https://threejs.org/manual/en/materials.html", "page_title": "Materials", "page_type": "guide", "page_summary": "An overview of the materials available in three.js, covering how to set material properties, the different material types (Basic, Lambert, Phong, Toon, Standard, Physical, and special-use materials), and common properties shared across materials.", "heading": "ShaderMaterial and RawShaderMaterial", "content": "Page: Materials\nSection: ShaderMaterial and RawShaderMaterial\n\nShaderMaterial is for making custom materials using the three.js shader system. RawShaderMaterial is for making entirely custom shaders with no help from three.js. Both of these topics are large and will be covered later.", "code_blocks": [], "chunk_position": 25, "heading_path": "ShaderMaterial and RawShaderMaterial > ShaderMaterial and RawShaderMaterial", "breadcrumbs": "Materials > ShaderMaterial and RawShaderMaterial > ShaderMaterial and RawShaderMaterial"}, {"id": "3b41475f951e9689", "url": "https://threejs.org/manual/en/materials.html", "page_title": "Materials", "page_type": "guide", "page_summary": "An overview of the materials available in three.js, covering how to set material properties, the different material types (Basic, Lambert, Phong, Toon, Standard, Physical, and special-use materials), and common properties shared across materials.", "heading": "Common Material Properties", "content": "Page: Materials\nSection: Common Material Properties\n\nMost materials share a bunch of settings all defined by Material. See the docs for all of them but let's go over two of the most commonly used properties.\n\nflatShading: whether or not the object looks faceted or smooth. default = false.\n\nside: which sides of triangles to show. The default is THREE.FrontSide. Other options are THREE.BackSide and THREE.DoubleSide (both sides). Most 3D objects drawn in three are probably opaque solids so the back sides (the sides facing inside the solid) do not need to be drawn. The most common reason to set side is for planes or other non-solid objects where it is common to see the back sides of triangles.", "code_blocks": [], "chunk_position": 25, "heading_path": "Common Material Properties > Common Material Properties", "breadcrumbs": "Materials > Common Material Properties > Common Material Properties"}, {"id": "ceb47b6d91b61f5d", "url": "https://threejs.org/manual/en/materials.html", "page_title": "Materials", "page_type": "guide", "page_summary": "An overview of the materials available in three.js, covering how to set material properties, the different material types (Basic, Lambert, Phong, Toon, Standard, Physical, and special-use materials), and common properties shared across materials.", "heading": "material.needsUpdate", "content": "Page: Materials\nSection: material.needsUpdate\n\nThis topic rarely affects most three.js apps but just as an FYI... Three.js applies material settings when a material is used where \"used\" means \"something is rendered that uses the material\". Some material settings are only applied once as changing them requires lots of work by three.js. In those cases you need to set material.needsUpdate = true to tell three.js to apply your material changes. The most common settings that require you to set needsUpdate if you change the settings after using the material are:\n\n- flatShading\n- adding or removing a texture\n\nChanging a texture is ok, but if want to switch from using no texture to using a texture or from using a texture to using no texture then you need to set needsUpdate = true.\n\nIn the case of going from texture to no-texture it is often just better to use a 1x1 pixel white texture.\n\nAs mentioned above most apps never run into these issues. Most apps do not switch between flat shaded and non flat shaded. Most apps also either use textures or a solid color for a given material, they rarely switch from using one to using the other.", "code_blocks": [], "chunk_position": 25, "heading_path": "material.needsUpdate > material.needsUpdate", "breadcrumbs": "Materials > material.needsUpdate > material.needsUpdate"}, {"id": "3d99d1ab80daa198", "url": "https://threejs.org/manual/en/textures.html", "page_title": "Textures", "page_type": "guide", "page_summary": "A comprehensive guide to textures in Three.js covering how to create, load, and apply textures to materials, including memory usage considerations, filtering and mipmaps, and texture wrapping/repeating/offsetting/rotating settings.", "heading": "Hello Texture", "content": "Page: Textures\nSection: Hello Texture\n\nTextures are *generally* images that are most often created in some 3rd party program like Photoshop or GIMP. For example let's put this image on cube.\n\nWe'll modify one of our first samples. All we need to do is create a TextureLoader. Call its load method with the URL of an image and set the material's `map` property to the result instead of setting its `color`.\n\nNote that we're using MeshBasicMaterial so no need for any lights.", "code_blocks": [{"language": "javascript", "code": "const loader = new THREE.TextureLoader();\nconst texture = loader.load( 'resources/images/wall.jpg' );\ntexture.colorSpace = THREE.SRGBColorSpace;\n\nconst material = new THREE.MeshBasicMaterial({\n  map: texture,\n});", "filename": ""}], "chunk_position": 26, "heading_path": "Hello Texture > Hello Texture", "breadcrumbs": "Textures > Hello Texture > Hello Texture"}, {"id": "41fda79843226c21", "url": "https://threejs.org/manual/en/textures.html", "page_title": "Textures", "page_type": "guide", "page_summary": "A comprehensive guide to textures in Three.js covering how to create, load, and apply textures to materials, including memory usage considerations, filtering and mipmaps, and texture wrapping/repeating/offsetting/rotating settings.", "heading": "6 Textures, a different one on each face of a cube", "content": "Page: Textures\nSection: 6 Textures, a different one on each face of a cube\n\nHow about 6 textures, one on each face of a cube?\n\nWe just make 6 materials and pass them as an array when we create the Mesh\n\nIt works!\n\nIt should be noted though that not all geometry types supports multiple materials. BoxGeometry can use 6 materials one for each face. ConeGeometry can use 2 materials, one for the bottom and one for the cone. CylinderGeometry can use 3 materials, bottom, top, and side. For other cases you will need to build or load custom geometry and/or modify texture coordinates.\n\nIt's far more common in other 3D engines and far more performant to use a Texture Atlas if you want to allow multiple images on a single geometry. A Texture atlas is where you put multiple images in a single texture and then use texture coordinates on the vertices of your geometry to select which parts of a texture are used on each triangle in your geometry.\n\nWhat are texture coordinates? They are data added to each vertex of a piece of geometry that specify what part of the texture corresponds to that specific vertex. We'll go over them when we start building custom geometry.", "code_blocks": [{"language": "javascript", "code": "const loader = new THREE.TextureLoader();\nconst 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];\nconst cube = new THREE.Mesh(geometry, materials);\n\nfunction loadColorTexture( path ) {\n  const texture = loader.load( path );\n  texture.colorSpace = THREE.SRGBColorSpace;\n  return texture;\n}", "filename": ""}], "chunk_position": 26, "heading_path": "6 Textures, a different one on each face of a cube > 6 Textures, a different one on each face of a cube", "breadcrumbs": "Textures > 6 Textures, a different one on each face of a cube > 6 Textures, a different one on each face of a cube"}, {"id": "2f8936d6765e3e7e", "url": "https://threejs.org/manual/en/textures.html", "page_title": "Textures", "page_type": "guide", "page_summary": "A comprehensive guide to textures in Three.js covering how to create, load, and apply textures to materials, including memory usage considerations, filtering and mipmaps, and texture wrapping/repeating/offsetting/rotating settings.", "heading": "Loading Textures", "content": "Page: Textures\nSection: Loading Textures\n\n", "code_blocks": [], "chunk_position": 26, "heading_path": "Loading Textures > Loading Textures", "breadcrumbs": "Textures > Loading Textures > Loading Textures"}, {"id": "576f418780cf7e15", "url": "https://threejs.org/manual/en/textures.html", "page_title": "Textures", "page_type": "guide", "page_summary": "A comprehensive guide to textures in Three.js covering how to create, load, and apply textures to materials, including memory usage considerations, filtering and mipmaps, and texture wrapping/repeating/offsetting/rotating settings.", "heading": "The Easy Way", "content": "Page: Textures\nSection: The Easy Way\n\nMost of the code on this site uses the easiest method of loading textures. We create a TextureLoader and then call its load method. This returns a Texture object.\n\nIt's important to note that using this method our texture will be transparent until the image is loaded asynchronously by three.js at which point it will update the texture with the downloaded image.\n\nThis has the big advantage that we don't have to wait for the texture to load and our page will start rendering immediately. That's probably okay for a great many use cases but if we want we can ask three.js to tell us when the texture has finished downloading.", "code_blocks": [{"language": "javascript", "code": "const texture = loader.load('resources/images/flower-1.jpg');", "filename": ""}], "chunk_position": 26, "heading_path": "The Easy Way > The Easy Way", "breadcrumbs": "Textures > The Easy Way > The Easy Way"}, {"id": "761121ec8c6235e6", "url": "https://threejs.org/manual/en/textures.html", "page_title": "Textures", "page_type": "guide", "page_summary": "A comprehensive guide to textures in Three.js covering how to create, load, and apply textures to materials, including memory usage considerations, filtering and mipmaps, and texture wrapping/repeating/offsetting/rotating settings.", "heading": "Waiting for a texture to load", "content": "Page: Textures\nSection: Waiting for a texture to load\n\nTo wait for a texture to load the `load` method of the texture loader takes a callback that will be called when the texture has finished loading. Going back to our top example we can wait for the texture to load before creating our Mesh and adding it to scene like this\n\nUnless you clear your browser's cache and have a slow connection you're unlikely to see the any difference but rest assured it is waiting for the texture to load.", "code_blocks": [{"language": "javascript", "code": "const loader = new THREE.TextureLoader();\nloader.load('resources/images/wall.jpg', (texture) => {\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});", "filename": ""}], "chunk_position": 26, "heading_path": "Waiting for a texture to load > Waiting for a texture to load", "breadcrumbs": "Textures > Waiting for a texture to load > Waiting for a texture to load"}, {"id": "aefdabd6becfe1a9", "url": "https://threejs.org/manual/en/textures.html", "page_title": "Textures", "page_type": "guide", "page_summary": "A comprehensive guide to textures in Three.js covering how to create, load, and apply textures to materials, including memory usage considerations, filtering and mipmaps, and texture wrapping/repeating/offsetting/rotating settings.", "heading": "Waiting for multiple textures to load", "content": "Page: Textures\nSection: Waiting for multiple textures to load\n\nTo wait until all textures have loaded you can use a LoadingManager. Create one and pass it to the TextureLoader then set its onLoad property to a callback.\n\nThe LoadingManager also has an onProgress property we can set to another callback to show a progress indicator.\n\nFirst we'll add a progress bar in HTML\n\nand the CSS for it\n\nThen in the code we'll update the scale of the `progressbar` in our `onProgress` callback. It gets called with the URL of the last item loaded, the number of items loaded so far, and the total number of items loaded.\n\nUnless you clear your cache and have a slow connection you might not see the loading bar.", "code_blocks": [{"language": "javascript", "code": "const loadManager = new THREE.LoadingManager();\nconst 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\nloadManager.onLoad = () => {\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};", "filename": ""}, {"language": "html", "code": "<body>\n  <canvas id=\"c\"></canvas>\n  <div id=\"loading\">\n    <div class=\"progress\"><div class=\"progressbar\"></div></div>\n  </div>\n</body>", "filename": ""}, {"language": "css", "code": "#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}", "filename": ""}, {"language": "javascript", "code": "const loadingElem = document.querySelector('#loading');\nconst progressBarElem = loadingElem.querySelector('.progressbar');\n\nloadManager.onLoad = () => {\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\nloadManager.onProgress = (urlOfLastItemLoaded, itemsLoaded, itemsTotal) => {\n  const progress = itemsLoaded / itemsTotal;\n  progressBarElem.style.transform = scaleX(${progress});\n};", "filename": ""}], "chunk_position": 26, "heading_path": "Waiting for multiple textures to load > Waiting for multiple textures to load", "breadcrumbs": "Textures > Waiting for multiple textures to load > Waiting for multiple textures to load"}, {"id": "cb3932f1c9d9c806", "url": "https://threejs.org/manual/en/textures.html", "page_title": "Textures", "page_type": "guide", "page_summary": "A comprehensive guide to textures in Three.js covering how to create, load, and apply textures to materials, including memory usage considerations, filtering and mipmaps, and texture wrapping/repeating/offsetting/rotating settings.", "heading": "Loading textures from other origins", "content": "Page: Textures\nSection: Loading textures from other origins\n\nTo use images from other servers those servers need to send the correct headers. If they don't you cannot use the images in three.js and will get an error. If you run the server providing the images make sure it sends the correct headers. If you don't control the server hosting the images and it does not send the permission headers then you can't use the images from that server.\n\nFor example imgur, flickr, and github all send headers allowing you to use images hosted on their servers in three.js. Most other websites do not.", "code_blocks": [], "chunk_position": 26, "heading_path": "Loading textures from other origins > Loading textures from other origins", "breadcrumbs": "Textures > Loading textures from other origins > Loading textures from other origins"}, {"id": "a0ac2aa2b0abf9a5", "url": "https://threejs.org/manual/en/textures.html", "page_title": "Textures", "page_type": "guide", "page_summary": "A comprehensive guide to textures in Three.js covering how to create, load, and apply textures to materials, including memory usage considerations, filtering and mipmaps, and texture wrapping/repeating/offsetting/rotating settings.", "heading": "Memory Usage", "content": "Page: Textures\nSection: Memory Usage\n\nTextures are often the part of a three.js app that use the most memory. It's important to understand that *in general*, textures take `width * height * 4 * 1.33` bytes of memory.\n\nNotice that says nothing about compression. I can make a .jpg image and set its compression super high. For example let's say I was making a scene of a house. Inside the house there is a table and I decide to put this wood texture on the top surface of the table\n\nThat image is only 157k so it will download relatively quickly but it is actually 3024 x 3761 pixels in size. Following the equation above that's\n\n3024 * 3761 * 4 * 1.33 = 60505764.5\n\nThat image will take 60 MEG OF MEMORY! in three.js. A few textures like that and you'll be out of memory.\n\nI bring this up because it's important to know that using textures has a hidden cost. In order for three.js to use the texture it has to hand it off to the GPU and the GPU *in general* requires the texture data to be uncompressed.\n\nThe moral of the story is make your textures small in dimensions not just small in file size. Small in file size = fast to download. Small in dimensions = takes less memory. How small should you make them? As small as you can and still look as good as you need them to look.", "code_blocks": [], "chunk_position": 26, "heading_path": "Memory Usage > Memory Usage", "breadcrumbs": "Textures > Memory Usage > Memory Usage"}, {"id": "8a875e7a74f0f84e", "url": "https://threejs.org/manual/en/textures.html", "page_title": "Textures", "page_type": "guide", "page_summary": "A comprehensive guide to textures in Three.js covering how to create, load, and apply textures to materials, including memory usage considerations, filtering and mipmaps, and texture wrapping/repeating/offsetting/rotating settings.", "heading": "JPG vs PNG", "content": "Page: Textures\nSection: JPG vs PNG\n\nThis is pretty much the same as regular HTML in that JPGs have lossy compression, PNGs have lossless compression so PNGs are generally slower to download. But, PNGs support transparency. PNGs are also probably the appropriate format for non-image data like normal maps, and other kinds of non-image maps which we'll go over later.\n\nIt's important to remember that a JPG doesn't use less memory than a PNG in WebGL. See above.", "code_blocks": [], "chunk_position": 26, "heading_path": "JPG vs PNG > JPG vs PNG", "breadcrumbs": "Textures > JPG vs PNG > JPG vs PNG"}, {"id": "6e036d19b6c6dd49", "url": "https://threejs.org/manual/en/textures.html", "page_title": "Textures", "page_type": "guide", "page_summary": "A comprehensive guide to textures in Three.js covering how to create, load, and apply textures to materials, including memory usage considerations, filtering and mipmaps, and texture wrapping/repeating/offsetting/rotating settings.", "heading": "Filtering and Mips", "content": "Page: Textures\nSection: Filtering and Mips\n\nLet's apply this 16x16 texture\n\nTo a cube\n\nLet's draw that cube really small\n\nHmmm, I guess that's hard to see. Let's magnify that tiny cube\n\nHow does the GPU know which colors to make each pixel it's drawing for the tiny cube? What if the cube was so small that it's just 1 or 2 pixels?\n\nThis is what filtering is about.\n\nIf it was Photoshop, Photoshop would average nearly all the pixels together to figure out what color to make those 1 or 2 pixels. That would be a very slow operation. GPUs solve this issue using mipmaps.\n\nMips are copies of the texture, each one half as wide and half as tall as the previous mip where the pixels have been blended to make the next smaller mip. Mips are created until we get all the way to a 1x1 pixel mip.\n\nNow, when the cube is drawn so small that it's only 1 or 2 pixels large the GPU can choose to use just the smallest or next to smallest mip level to decide what color to make the tiny cube.\n\nIn three.js you can choose what happens both when the texture is drawn larger than its original size and what happens when it's drawn smaller than its original size.\n\nFor setting the filter when the texture is drawn larger than its original size you set `texture.magFilter` property to either `THREE.NearestFilter` or `THREE.LinearFilter`. `NearestFilter` means just pick the closet single pixel from the original texture. With a low resolution texture this gives you a very pixelated look like Minecraft.\n\n`LinearFilter` means choose the 4 pixels from the texture that are closest to the where we should be choosing a color from and blend them in the appropriate proportions relative to how far away the actual point is from each of the 4 pixels.\n\nNearest vs Linear comparison shown.\n\nFor setting the filter when the texture is drawn smaller than its original size you set the `texture.minFilter` property to one of 6 values:\n\n- `THREE.NearestFilter` — same as above, choose the closest pixel in the texture\n- `THREE.LinearFilter` — same as above, choose 4 pixels from the texture and blend them\n- `THREE.NearestMipmapNearestFilter` — choose the appropriate mip then choose one pixel\n- `THREE.NearestMipmapLinearFilter` — choose 2 mips, choose one pixel from each, blend the 2 pixels\n- `THREE.LinearMipmapNearestFilter` — chose the appropriate mip then choose 4 pixels and blend them\n- `THREE.LinearMipmapLinearFilter` — choose 2 mips, choose 4 pixels from each and blend all 8 into 1 pixel\n\nHere's an example showing all 6 settings\n\nOne thing to notice is the top left and top middle using `NearestFilter` and `LinearFilter` don't use the mips. Because of that they flicker in the distance because the GPU is picking pixels from the original texture. On the left just one pixel is chosen and in the middle 4 are chosen and blended but it's not enough come up with a good representative color. The other 4 strips do better with the bottom right, `LinearMipmapLinearFilter` being best.\n\nIf you click the picture above it will toggle between the texture we've been using above and a texture where every mip level is a different color.\n\nThis makes it more clear what is happening. You can see in the top left and top middle the first mip is used all the way into the distance. The top right and bottom middle you can clearly see where a different mip is used.\n\nSwitching back to the original texture you can see the bottom right is the smoothest, highest quality. You might ask why not always use that mode. The most obvious reason is sometimes you want things to be pixelated for a retro look or some other reason. The next most common reason is that reading 8 pixels and blending them is slower than reading 1 pixel and blending. While it's unlikely that a single texture is going to be the difference between fast and slow as we progress further into these articles we'll eventually have materials that use 4 or 5 textures all at once. 4 textures * 8 pixels per texture is looking up 32 pixels for ever pixel rendered. This can be especially important to consider on mobile devices.", "code_blocks": [], "chunk_position": 26, "heading_path": "Filtering and Mips > Filtering and Mips", "breadcrumbs": "Textures > Filtering and Mips > Filtering and Mips"}, {"id": "5b688c83d5c22084", "url": "https://threejs.org/manual/en/textures.html", "page_title": "Textures", "page_type": "guide", "page_summary": "A comprehensive guide to textures in Three.js covering how to create, load, and apply textures to materials, including memory usage considerations, filtering and mipmaps, and texture wrapping/repeating/offsetting/rotating settings.", "heading": "Repeating, offseting, rotating, wrapping a texture", "content": "Page: Textures\nSection: Repeating, offseting, rotating, wrapping a texture\n\nTextures have settings for repeating, offseting, and rotating a texture.\n\nBy default textures in three.js do not repeat. To set whether or not a texture repeats there are 2 properties, `wrapS` for horizontal wrapping and `wrapT` for vertical wrapping.\n\nThey can be set to one of:\n\n- `THREE.ClampToEdgeWrapping` — the last pixel on each edge is repeated forever\n- `THREE.RepeatWrapping` — the texture is repeated\n- `THREE.MirroredRepeatWrapping` — the texture is mirrored and repeated\n\nFor example to turn on wrapping in both directions:\n\nRepeating is set with the `repeat` property.\n\nOffseting the texture can be done by setting the `offset` property. Textures are offset with units where 1 unit = 1 texture size. On other words 0 = no offset and 1 = offset one full texture amount.\n\nRotating the texture can be set by setting the `rotation` property in radians as well as the `center` property for choosing the center of rotation. It defaults to 0,0 which rotates from the bottom left corner. Like offset these units are in texture size so setting them to `.5, .5` would rotate around the center of the texture.\n\nLet's modify the top sample above to play with these values\n\nFirst we'll keep a reference to the texture so we can manipulate it\n\nThen we'll use lil-gui again to provide a simple interface.\n\nAs we did in previous lil-gui examples we'll use a simple class to give lil-gui an object that it can manipulate in degrees but that will set a property in radians.\n\nWe also need a class that will convert from a string like `\"123\"` into a number like `123` since three.js requires numbers for enum settings like `wrapS` and `wrapT` but lil-gui only uses strings for enums.\n\nUsing those classes we can setup a simple GUI for the settings above\n\nThe last thing to note about the example is that if you change `wrapS` or `wrapT` on the texture you must also set `texture.needsUpdate` so three.js knows to apply those settings. The other settings are automatically applied.\n\nThis is only one step into the topic of textures. At some point we'll go over texture coordinates as well as 9 other types of textures that can be applied to materials.\n\nFor now let's move on to lights.", "code_blocks": [{"language": "javascript", "code": "someTexture.wrapS = THREE.RepeatWrapping;\nsomeTexture.wrapT = THREE.RepeatWrapping;", "filename": ""}, {"language": "javascript", "code": "const timesToRepeatHorizontally = 4;\nconst timesToRepeatVertically = 2;\nsomeTexture.repeat.set(timesToRepeatHorizontally, timesToRepeatVertically);", "filename": ""}, {"language": "javascript", "code": "const xOffset = .5;   // offset by half the texture\nconst yOffset = .25;  // offset by 1/4 the texture\nsomeTexture.offset.set(xOffset, yOffset);", "filename": ""}, {"language": "javascript", "code": "someTexture.center.set(.5, .5);\nsomeTexture.rotation = THREE.MathUtils.degToRad(45);", "filename": ""}, {"language": "javascript", "code": "const texture = loader.load('resources/images/wall.jpg');\nconst material = new THREE.MeshBasicMaterial({\n  map: texture,\n});", "filename": ""}, {"language": "javascript", "code": "import {GUI} from 'three/addons/libs/lil-gui.module.min.js';", "filename": ""}, {"language": "javascript", "code": "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.degToDeg(v);\n  }\n}", "filename": ""}, {"language": "javascript", "code": "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}", "filename": ""}, {"language": "javascript", "code": "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');", "filename": ""}], "chunk_position": 26, "heading_path": "Repeating, offseting, rotating, wrapping a texture > Repeating, offseting, rotating, wrapping a texture", "breadcrumbs": "Textures > Repeating, offseting, rotating, wrapping a texture > Repeating, offseting, rotating, wrapping a texture"}, {"id": "b2787a4025bd919f", "url": "https://threejs.org/manual/en/lights.html", "page_title": "Lights", "page_type": "guide", "page_summary": "A tutorial on how to use the various kinds of lights in three.js, covering AmbientLight, HemisphereLight, DirectionalLight, PointLight, SpotLight, and RectAreaLight, with progressively built code examples and interactive demos.", "heading": "Introduction", "content": "Page: Lights\nSection: Introduction\n\nThis article is part of a series of articles about three.js. The first article is three.js fundamentals. If you haven't read that yet and you're new to three.js you might want to consider starting there and also the article on setting up your environment. The previous article was about textures.\n\nLet's go over how to use the various kinds of lights in three.\n\nStarting with one of our previous samples let's update the camera. We'll set the field of view to 45 degrees, the far plane to 100 units, and we'll move the camera 10 units up and 20 units back from the origin\n\nNext let's add OrbitControls. OrbitControls let the user spin or orbit the camera around some point. The OrbitControls are an optional feature of three.js so first we need to include them in our page\n\nThen we can use them. We pass the OrbitControls a camera to control and the DOM element to use to get input events\n\nWe also set the target to orbit around to 5 units above the origin and then call controls.update so the controls will use the new target.\n\nNext up let's make some things to light up. First we'll make ground plane. We'll apply a tiny 2x2 pixel checkerboard texture that looks like this\n\nFirst we load the texture, set it to repeating, set the filtering to nearest, and set how many times we want it to repeat. Since the texture is a 2x2 pixel checkerboard, by repeating and setting the repeat to half the size of the plane each check on the checkerboard will be exactly 1 unit large;\n\nWe then make a plane geometry, a material for the plane, and a mesh to insert it in the scene. Planes default to being in the XY plane but the ground is in the XZ plane so we rotate it.\n\nLet's add a cube and a sphere so we have 3 things to light including the plane\n\nNow that we have a scene to light up let's add lights!", "code_blocks": [{"language": "javascript", "code": "*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);", "filename": ""}, {"language": "javascript", "code": "import * as THREE from 'three';\n+import {OrbitControls} from 'three/addons/controls/OrbitControls.js';", "filename": ""}, {"language": "javascript", "code": "const controls = new OrbitControls(camera, canvas);\ncontrols.target.set(0, 5, 0);\ncontrols.update();", "filename": ""}, {"language": "javascript", "code": "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);", "filename": ""}, {"language": "javascript", "code": "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);", "filename": ""}, {"language": "javascript", "code": "{\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}", "filename": ""}], "chunk_position": 27, "heading_path": "Introduction > Introduction", "breadcrumbs": "Lights > Introduction > Introduction"}, {"id": "a86de6db0bbd1126", "url": "https://threejs.org/manual/en/lights.html", "page_title": "Lights", "page_type": "guide", "page_summary": "A tutorial on how to use the various kinds of lights in three.js, covering AmbientLight, HemisphereLight, DirectionalLight, PointLight, SpotLight, and RectAreaLight, with progressively built code examples and interactive demos.", "heading": "AmbientLight", "content": "Page: Lights\nSection: AmbientLight\n\nFirst let's make an AmbientLight\n\nLet's also make it so we can adjust the light's parameters. We'll use lil-gui again. To be able to adjust the color via lil-gui we need a small helper that presents a property to lil-gui that looks like a CSS hex color string (eg: #FF8844). Our helper will get the color from a named property, convert it to a hex string to offer to lil-gui. When lil-gui tries to set the helper's property we'll assign the result back to the light's color.\n\nHere's the helper:\n\nAnd here's our code setting up lil-gui\n\nAnd here's the result (click here to open in a separate window)\n\nClick and drag in the scene to orbit the camera.\n\nNotice there is no definition. The shapes are flat. The AmbientLight effectively just multiplies the material's color by the light's color times the intensity.\n\nThat's it. It has no direction. This style of ambient lighting is actually not all that useful as lighting as it's 100% even so other than changing the color of everything in the scene it doesn't look much like lighting. What it does help with is making the darks not too dark.", "code_blocks": [{"language": "javascript", "code": "const color = 0xFFFFFF;\nconst intensity = 1;\nconst light = new THREE.AmbientLight(color, intensity);\nscene.add(light);", "filename": ""}, {"language": "javascript", "code": "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}", "filename": ""}, {"language": "javascript", "code": "const gui = new GUI();\ngui.addColor(new ColorGUIHelper(light, 'color'), 'value').name('color');\ngui.add(light, 'intensity', 0, 5, 0.01);", "filename": ""}, {"language": "javascript", "code": "color = materialColor * light.color * light.intensity;", "filename": ""}], "chunk_position": 27, "heading_path": "AmbientLight > AmbientLight", "breadcrumbs": "Lights > AmbientLight > AmbientLight"}, {"id": "4b13b683b2a15b2f", "url": "https://threejs.org/manual/en/lights.html", "page_title": "Lights", "page_type": "guide", "page_summary": "A tutorial on how to use the various kinds of lights in three.js, covering AmbientLight, HemisphereLight, DirectionalLight, PointLight, SpotLight, and RectAreaLight, with progressively built code examples and interactive demos.", "heading": "HemisphereLight", "content": "Page: Lights\nSection: HemisphereLight\n\nLet's switch the code to a HemisphereLight. A HemisphereLight takes a sky color and a ground color and just multiplies the material's color between those 2 colors—the sky color if the surface of the object is pointing up and the ground color if the surface of the object is pointing down.\n\nHere's the new code\n\nLet's also update the lil-gui code to edit both colors\n\nThe result (click here to open in a separate window)\n\nNotice again there is almost no definition, everything looks kind of flat. The HemisphereLight used in combination with another light can help give a nice kind of influence of the color of the sky and ground. In that way it's best used in combination with some other light or a substitute for an AmbientLight.", "code_blocks": [{"language": "javascript", "code": "-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);", "filename": ""}, {"language": "javascript", "code": "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);", "filename": ""}], "chunk_position": 27, "heading_path": "HemisphereLight > HemisphereLight", "breadcrumbs": "Lights > HemisphereLight > HemisphereLight"}, {"id": "4c2087d1b23e62c9", "url": "https://threejs.org/manual/en/lights.html", "page_title": "Lights", "page_type": "guide", "page_summary": "A tutorial on how to use the various kinds of lights in three.js, covering AmbientLight, HemisphereLight, DirectionalLight, PointLight, SpotLight, and RectAreaLight, with progressively built code examples and interactive demos.", "heading": "DirectionalLight", "content": "Page: Lights\nSection: DirectionalLight\n\nLet's switch the code to a DirectionalLight. A DirectionalLight is often used to represent the sun.\n\nNotice that we had to add the light and the light.target to the scene. A three.js DirectionalLight will shine in the direction of its target.\n\nLet's make it so we can move the target by adding it to our GUI.\n\nIt's kind of hard to see what's going on. Three.js has a bunch of helper objects we can add to our scene to help visualize invisible parts of a scene. In this case we'll use the DirectionalLightHelper which will draw a plane, to represent the light, and a line from the light to the target. We just pass it the light and add it to the scene.\n\nWhile we're at it let's make it so we can set both the position of the light and the target. To do this we'll make a function that given a Vector3 will adjust its x, y, and z properties using lil-gui.\n\nNote that we need to call the helper's update function anytime we change something so the helper knows to update itself. As such we pass in an onChangeFn function to get called anytime lil-gui updates a value.\n\nThen we can use that for both the light's position and the target's position like this\n\nNow we can move the light, and its target (click here to open in a separate window)\n\nOrbit the camera and it gets easier to see. The plane represents a DirectionalLight because a directional light computes light coming in one direction. There is no point the light comes from, it's an infinite plane of light shooting out parallel rays of light.", "code_blocks": [{"language": "javascript", "code": "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);", "filename": ""}, {"language": "javascript", "code": "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);", "filename": ""}, {"language": "javascript", "code": "const helper = new THREE.DirectionalLightHelper(light);\nscene.add(helper);", "filename": ""}, {"language": "javascript", "code": "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}", "filename": ""}, {"language": "javascript", "code": "+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);", "filename": ""}], "chunk_position": 27, "heading_path": "DirectionalLight > DirectionalLight", "breadcrumbs": "Lights > DirectionalLight > DirectionalLight"}, {"id": "9105293ef707dd9d", "url": "https://threejs.org/manual/en/lights.html", "page_title": "Lights", "page_type": "guide", "page_summary": "A tutorial on how to use the various kinds of lights in three.js, covering AmbientLight, HemisphereLight, DirectionalLight, PointLight, SpotLight, and RectAreaLight, with progressively built code examples and interactive demos.", "heading": "PointLight", "content": "Page: Lights\nSection: PointLight\n\nA PointLight is a light that sits at a point and shoots light in all directions from that point. Let's change the code.\n\nLet's also switch to a PointLightHelper\n\nand as there is no target the onChange function can be simpler.\n\nNote that at some level a PointLightHelper has no um, point. It just draws a small wireframe diamond. It could just as easily be any shape you want, just add a mesh to the light itself.\n\nA PointLight has the added property of distance. If the distance is 0 then the PointLight shines to infinity. If the distance is greater than 0 then the light shines its full intensity at the light and fades to no influence at distance units away from the light.\n\nLet's setup the GUI so we can adjust the distance.\n\nAnd now try it out (click here to open in a separate window)\n\nNotice when distance is > 0 how the light fades out.", "code_blocks": [{"language": "javascript", "code": "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);", "filename": ""}, {"language": "javascript", "code": "-const helper = new THREE.DirectionalLightHelper(light);\n+const helper = new THREE.PointLightHelper(light);\nscene.add(helper);", "filename": ""}, {"language": "javascript", "code": "function updateLight() {\n-  light.target.updateMatrixWorld();\n  helper.update();\n}\n-updateLight();", "filename": ""}, {"language": "javascript", "code": "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);", "filename": ""}], "chunk_position": 27, "heading_path": "PointLight > PointLight", "breadcrumbs": "Lights > PointLight > PointLight"}, {"id": "eaed3cfe412e39d7", "url": "https://threejs.org/manual/en/lights.html", "page_title": "Lights", "page_type": "guide", "page_summary": "A tutorial on how to use the various kinds of lights in three.js, covering AmbientLight, HemisphereLight, DirectionalLight, PointLight, SpotLight, and RectAreaLight, with progressively built code examples and interactive demos.", "heading": "SpotLight", "content": "Page: Lights\nSection: SpotLight\n\nSpotlights are effectively a point light with a cone attached where the light only shines inside the cone. There's actually 2 cones. An outer cone and an inner cone. Between the inner cone and the outer cone the light fades from full intensity to zero.\n\nTo use a SpotLight we need a target just like the directional light. The light's cone will open toward the target.\n\nModifying our DirectionalLight with helper from above\n\nThe spotlight's cone's angle is set with the angle property in radians. We'll use our DegRadHelper from the texture article to present a UI in degrees.\n\nThe inner cone is defined by setting the penumbra property as a percentage from the outer cone. In other words when penumbra is 0 then the inner cone is the same size (0 = no difference) from the outer cone. When the penumbra is 1 then the light fades starting in the center of the cone to the outer cone. When penumbra is .5 then the light fades starting from 50% between the center of the outer cone.\n\nNotice with the default penumbra of 0 the spotlight has a very sharp edge whereas as you adjust the penumbra toward 1 the edge blurs.\n\nIt might be hard to see the cone of the spotlight. The reason is it's below the ground. Shorten the distance to around 5 and you'll see the open end of the cone.", "code_blocks": [{"language": "javascript", "code": "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);", "filename": ""}, {"language": "javascript", "code": "gui.add(new DegRadHelper(light, 'angle'), 'value', 0, 90).name('angle').onChange(updateLight);", "filename": ""}, {"language": "javascript", "code": "gui.add(light, 'penumbra', 0, 1, 0.01);", "filename": ""}], "chunk_position": 27, "heading_path": "SpotLight > SpotLight", "breadcrumbs": "Lights > SpotLight > SpotLight"}, {"id": "5d63d5dd2ffdc831", "url": "https://threejs.org/manual/en/lights.html", "page_title": "Lights", "page_type": "guide", "page_summary": "A tutorial on how to use the various kinds of lights in three.js, covering AmbientLight, HemisphereLight, DirectionalLight, PointLight, SpotLight, and RectAreaLight, with progressively built code examples and interactive demos.", "heading": "RectAreaLight", "content": "Page: Lights\nSection: RectAreaLight\n\nThere's one more type of light, the RectAreaLight, which represents exactly what it sounds like, a rectangular area of light like a long fluorescent light or maybe a frosted sky light in a ceiling.\n\nThe RectAreaLight only works with the MeshStandardMaterial and the MeshPhysicalMaterial so let's change all our materials to MeshStandardMaterial\n\nTo use the RectAreaLight we need to include some extra three.js optional data and we'll include the RectAreaLightHelper to help us visualize the light\n\nand we need to call RectAreaLightUniformsLib.init\n\nIf you forget the data the light will still work but it will look funny so be sure to remember to include the extra data.\n\nNow we can create the light\n\nOne thing to notice is that unlike the DirectionalLight and the SpotLight, the RectAreaLight does not use a target. It just uses its rotation. Another thing to notice is the helper needs to be a child of the light. It is not a child of the scene like other helpers.\n\nLet's also adjust the GUI. We'll make it so we can rotate the light and adjust its width and height\n\nAnd here is that (click here to open in a separate window)\n\nIt's important to note each light you add to the scene slows down how fast three.js renders the scene so you should always try to use as few as possible to achieve your goals.\n\nNext up let's go over dealing with cameras.", "code_blocks": [{"language": "javascript", "code": "...\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}", "filename": ""}, {"language": "javascript", "code": "import * as THREE from 'three';\n+import {RectAreaLightUniformsLib} from 'three/addons/lights/RectAreaLightUniformsLib.js';\n+import {RectAreaLightHelper} from 'three/addons/helpers/RectAreaLightHelper.js';", "filename": ""}, {"language": "javascript", "code": "function main() {\n  const canvas = document.querySelector('#c');\n  const renderer = new THREE.WebGLRenderer({antialias: true, canvas});\n+  RectAreaLightUniformsLib.init();", "filename": ""}, {"language": "javascript", "code": "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);", "filename": ""}, {"language": "javascript", "code": "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');", "filename": ""}], "chunk_position": 27, "heading_path": "RectAreaLight > RectAreaLight", "breadcrumbs": "Lights > RectAreaLight > RectAreaLight"}, {"id": "beab051f4662b9fa", "url": "https://threejs.org/manual/en/cameras.html", "page_title": "Cameras", "page_type": "guide", "page_summary": "An in-depth guide to cameras in three.js, covering PerspectiveCamera and OrthographicCamera, frustum concepts, depth buffer precision issues (z-fighting), and practical examples including split-screen views and 2D rendering.", "heading": "Cameras", "content": "Page: Cameras\nSection: Cameras\n\nThis article is one in a series of articles about three.js. The first article was about fundamentals. If you haven't read that yet you might want to start there.\n\nLet's talk about cameras in three.js. We covered some of this in the first article but we'll cover it in more detail here.\n\nThe most common camera in three.js and the one we've been using up to this point is the PerspectiveCamera. It gives a 3d view where things in the distance appear smaller than things up close.\n\nThe PerspectiveCamera defines a frustum. A frustum is a solid pyramid shape with the tip cut off. By name of a solid I mean for example a cube, a cone, a sphere, a cylinder, and a frustum are all names of different kinds of solids.\n\ncube\ncone\nsphere\ncylinder\nfrustum\n\nI only point that out because I didn't know it for years. Some book or page would mention frustum and my eyes would glaze over. Understanding it's the name of a type of solid shape made those descriptions suddenly make more sense 😅\n\nA PerspectiveCamera defines its frustum based on 4 properties. near defines where the front of the frustum starts. far defines where it ends. fov, the field of view, defines how tall the front and back of the frustum are by computing the correct height to get the specified field of view at near units from the camera. The aspect defines how wide the front and back of the frustum are. The width of the frustum is just the height multiplied by the aspect.", "code_blocks": [], "chunk_position": 28, "heading_path": "Cameras > Cameras", "breadcrumbs": "Cameras > Cameras > Cameras"}, {"id": "ec8cf23bb4541b5b", "url": "https://threejs.org/manual/en/cameras.html", "page_title": "Cameras", "page_type": "guide", "page_summary": "An in-depth guide to cameras in three.js, covering PerspectiveCamera and OrthographicCamera, frustum concepts, depth buffer precision issues (z-fighting), and practical examples including split-screen views and 2D rendering.", "heading": "Adjusting Camera Settings with GUI", "content": "Page: Cameras\nSection: Adjusting Camera Settings with GUI\n\nLet's use the scene from the previous article that has a ground plane, a sphere, and a cube and make it so we can adjust the camera's settings.\n\nTo do that we'll make a MinMaxGUIHelper for the near and far settings so far is always greater than near. It will have min and max properties that lil-gui will adjust. When adjusted they'll set the 2 properties we specify.\n\nNow we can setup our GUI like this.\n\nAnytime the camera's settings change we need to call the camera's updateProjectionMatrix function so we made a function called updateCamera add passed it to lil-gui to call it when things change.\n\nYou can adjust the values and see how they work. Note we didn't make aspect settable since it's taken from the size of the window so if you want to adjust the aspect open the example in a new window and then size the window.", "code_blocks": [{"language": "javascript", "code": "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}", "filename": ""}, {"language": "javascript", "code": "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);", "filename": ""}], "chunk_position": 28, "heading_path": "Adjusting Camera Settings with GUI > Adjusting Camera Settings with GUI", "breadcrumbs": "Cameras > Adjusting Camera Settings with GUI > Adjusting Camera Settings with GUI"}, {"id": "a363ff4fb5bbe3d4", "url": "https://threejs.org/manual/en/cameras.html", "page_title": "Cameras", "page_type": "guide", "page_summary": "An in-depth guide to cameras in three.js, covering PerspectiveCamera and OrthographicCamera, frustum concepts, depth buffer precision issues (z-fighting), and practical examples including split-screen views and 2D rendering.", "heading": "Two Cameras with Scissor and CameraHelper", "content": "Page: Cameras\nSection: Two Cameras with Scissor and CameraHelper\n\nStill, I think it's a little hard to see so let's change the example so it has 2 cameras. One will show our scene as we see it above, the other will show another camera looking at the scene the first camera is drawing and showing that camera's frustum.\n\nTo do this we can use the scissor function of three.js. Let's change it to draw 2 scenes with 2 cameras side by side using the scissor function.\n\nFirst off let's use some HTML and CSS to define 2 side by side elements. This will also help us with events so both cameras can easily have their own OrbitControls.\n\nAnd the CSS that will make those 2 views show up side by side overlaid on top of the canvas.\n\nThen in our code we'll add a CameraHelper. A CameraHelper draws the frustum for a Camera.\n\nNow let's look up the 2 view elements.\n\nAnd we'll set our existing OrbitControls to respond to the first view element only.\n\nLet's make a second PerspectiveCamera and a second OrbitControls. The second OrbitControls is tied to the second camera and gets input from the second view element.\n\nFinally we need to render the scene from the point of view of each camera using the scissor function to only render to part of the canvas.\n\nHere is a function that given an element will compute the rectangle of that element that overlaps the canvas. It will then set the scissor and viewport to that rectangle and return the aspect for that size.\n\nAnd now we can use that function to draw the scene twice in our render function.\n\nThe code above sets the background color of the scene when rendering the second view to dark blue just to make it easier to distinguish the two views.\n\nWe can also remove our updateCamera code since we're updating everything in the render function.\n\nAnd now you can use one view to see the frustum of the other.\n\nOn the left you can see the original view and on the right you can see a view showing the frustum of the camera on the left. As you adjust near, far, fov and move the camera with mouse you can see that only what's inside the frustum shown on the right appears in the scene on the left.\n\nAdjust near up to around 20 and you'll easily see the front of objects disappear as they are no longer in the frustum. Adjust far below about 35 and you'll start to see the ground plane disappear as it's no longer in the frustum.\n\nThis brings up the question, why not just set near to 0.0000000001 and far to 10000000000000 or something like that so you can just see everything? The reason is your GPU only has so much precision to decide if something is in front or behind something else. That precision is spread out between near and far. Worse, by default the precision close the camera is detailed and the precision far from the camera is coarse. The units start with near and slowly expand as they approach far.", "code_blocks": [{"language": "html", "code": "<body>\n  <canvas id=\"c\"></canvas>\n+  <div class=\"split\">\n+     <div id=\"view1\" tabindex=\"1\"></div>\n+     <div id=\"view2\" tabindex=\"2\"></div>\n+  </div>\n</body>", "filename": ""}, {"language": "css", "code": ".split {\n  position: absolute;\n  left: 0;\n  top: 0;\n  width: 100%;\n  height: 100%;\n  display: flex;\n}\n.split>div {\n  width: 100%;\n  height: 100%;\n}", "filename": ""}, {"language": "javascript", "code": "const cameraHelper = new THREE.CameraHelper(camera);\n\n...\n\nscene.add(cameraHelper);", "filename": ""}, {"language": "javascript", "code": "const view1Elem = document.querySelector('#view1');\nconst view2Elem = document.querySelector('#view2');", "filename": ""}, {"language": "javascript", "code": "-const controls = new OrbitControls(camera, canvas);\n+const controls = new OrbitControls(camera, view1Elem);", "filename": ""}, {"language": "javascript", "code": "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();", "filename": ""}, {"language": "javascript", "code": "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}", "filename": ""}, {"language": "javascript", "code": "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}", "filename": ""}, {"language": "javascript", "code": "-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');", "filename": ""}], "chunk_position": 28, "heading_path": "Two Cameras with Scissor and CameraHelper > Two Cameras with Scissor and CameraHelper", "breadcrumbs": "Cameras > Two Cameras with Scissor and CameraHelper > Two Cameras with Scissor and CameraHelper"}, {"id": "10b4bed832b0c9f7", "url": "https://threejs.org/manual/en/cameras.html", "page_title": "Cameras", "page_type": "guide", "page_summary": "An in-depth guide to cameras in three.js, covering PerspectiveCamera and OrthographicCamera, frustum concepts, depth buffer precision issues (z-fighting), and practical examples including split-screen views and 2D rendering.", "heading": "Z-Fighting and Depth Precision", "content": "Page: Cameras\nSection: Z-Fighting and Depth Precision\n\nStarting with the top example, let's change the code to insert 20 spheres in a row.\n\nand let's set near to 0.00001\n\nWe also need to tweak the GUI code a little to allow 0.00001 if the value is edited\n\nWhat do you think will happen?\n\nThis is an example of z fighting where the GPU on your computer does not have enough precision to decide which pixels are in front and which pixels are behind.\n\nOne solution is to tell three.js use to a different method to compute which pixels are in front and which are behind. We can do that by enabling logarithmicDepthBuffer when we create the WebGLRenderer.\n\nIf this didn't fix the issue for you then you've run into one reason why you can't always use this solution. That reason is because only certain GPUs support it. As of September 2018 almost no mobile devices support this solution whereas most desktops do.\n\nAnother reason not to choose this solution is it can be significantly slower than the standard solution.\n\nEven with this solution there is still limited resolution. Make near even smaller or far even bigger and you'll eventually run into the same issues.\n\nWhat that means is that you should always make an effort to choose a near and far setting that fits your use case. Set near as far away from the camera as you can and not have things disappear. Set far as close to the camera as you can and not have things disappear. If you're trying to draw a giant scene and show a close up of someone's face so you can see their eyelashes while in the background you can see all the way to mountains 50 kilometers in the distance well then you'll need to find other creative solutions that maybe we'll go over later. For now, just be aware you should take care to choose appropriate near and far values for your needs.", "code_blocks": [{"language": "javascript", "code": "{\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 < 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}", "filename": ""}, {"language": "javascript", "code": "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);", "filename": ""}, {"language": "javascript", "code": "-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);", "filename": ""}, {"language": "javascript", "code": "-const renderer = new THREE.WebGLRenderer({antialias: true, canvas});\n+const renderer = new THREE.WebGLRenderer({\n+  antialias: true,\n+  canvas,\n+  logarithmicDepthBuffer: true,\n+});", "filename": ""}], "chunk_position": 28, "heading_path": "Z-Fighting and Depth Precision > Z-Fighting and Depth Precision", "breadcrumbs": "Cameras > Z-Fighting and Depth Precision > Z-Fighting and Depth Precision"}, {"id": "f98086f6881a55a0", "url": "https://threejs.org/manual/en/cameras.html", "page_title": "Cameras", "page_type": "guide", "page_summary": "An in-depth guide to cameras in three.js, covering PerspectiveCamera and OrthographicCamera, frustum concepts, depth buffer precision issues (z-fighting), and practical examples including split-screen views and 2D rendering.", "heading": "OrthographicCamera", "content": "Page: Cameras\nSection: OrthographicCamera\n\nThe 2nd most common camera is the OrthographicCamera. Rather than specify a frustum it specifies a box with the settings left, right top, bottom, near, and far. Because it's projecting a box there is no perspective.\n\nLet's change the 2 view example above to use an OrthographicCamera in the first view.\n\nFirst let's setup an OrthographicCamera.\n\nWe set left and bottom to -1 and right and top to 1. This would make a box 2 units wide and 2 units tall but we're going to adjust the left and top by the aspect of the rectangle we're drawing to. We'll use the zoom property to make it easy to adjust how many units are actually shown by the camera.\n\nLet's add a GUI setting for zoom\n\nThe call to listen tells lil-gui to watch for changes. This is here because the OrbitControls can also control zoom. For example the scroll wheel on a mouse will zoom via the OrbitControls.\n\nLast we just need to change the part that renders the left side to update the OrthographicCamera.\n\nand now you can see an OrthographicCamera at work.\n\nAn OrthographicCamera is most often used if using three.js to draw 2D things. You'd decide how many units you want the camera to show. For example if you want one pixel of canvas to match one unit in the camera you could do something like\n\nTo put the origin at the center and have 1 pixel = 1 three.js unit something like\n\nOr if we wanted the origin to be in the top left just like a 2D canvas we could use this\n\nIn which case the top left corner would be 0,0 just like a 2D canvas\n\nLet's try it! First let's set the camera up\n\nThen let's load 6 textures and make 6 planes, one for each texture. We'll parent each plane to a THREE.Object3D to make it easy to offset the plane so its center appears to be at its top left corner.\n\nIf you're running locally you'll also need to have setup. You might also want to read about using textures.\n\nand we need to update the camera if the size of the canvas changes.\n\nplanes is an array of THREE.Mesh, one for each plane. Let's move them around based on the time.\n\nAnd you can see the images bounce pixel perfect off the edges of the canvas using pixel math just like a 2D canvas\n\nAnother common use for an OrthographicCamera is to draw the up, down, left, right, front, back views of a 3D modeling program or a game engine's editor.\n\nIn the screenshot above you can see 1 view is a perspective view and 3 views are orthographic views.\n\nThat's the fundamentals of cameras. We'll cover a few common ways to move cameras in other articles. For now let's move on to shadows.", "code_blocks": [{"language": "javascript", "code": "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;", "filename": ""}, {"language": "javascript", "code": "const gui = new GUI();\n+gui.add(camera, 'zoom', 0.01, 1, 0.01).listen();", "filename": ""}, {"language": "javascript", "code": "{\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}", "filename": ""}, {"language": "javascript", "code": "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;", "filename": ""}, {"language": "javascript", "code": "camera.left = 0;\ncamera.right = canvas.width;\ncamera.top = 0;\ncamera.bottom = canvas.height;\ncamera.near = -1;\ncamera.far = 1;\ncamera.zoom = 1;", "filename": ""}, {"language": "javascript", "code": "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;", "filename": ""}, {"language": "javascript", "code": "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) => {\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});", "filename": ""}, {"language": "javascript", "code": "function render() {\n\n  if (resizeRendererToDisplaySize(renderer)) {\n    camera.right = canvas.width;\n    camera.bottom = canvas.height;\n    camera.updateProjectionMatrix();\n  }\n\n  ...", "filename": ""}, {"language": "javascript", "code": "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) => {\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 < distAcross ? xt : xRange - xt;\n    const y = yt < distDown   ? yt : yRange - yt;\n\n    plane.position.set(x, y, 0);\n  });\n\n  renderer.render(scene, camera);", "filename": ""}], "chunk_position": 28, "heading_path": "OrthographicCamera > OrthographicCamera", "breadcrumbs": "Cameras > OrthographicCamera > OrthographicCamera"}, {"id": "a0ce191b4577d5a1", "url": "https://threejs.org/manual/en/shadows.html", "page_title": "Shadows", "page_type": "guide", "page_summary": "A comprehensive guide to shadows in three.js, covering fake shadow techniques using textures, shadow maps for DirectionalLight, SpotLight, and PointLight, including configuring shadow cameras, shadow map resolution, and troubleshooting common issues like missing shadows and shadow acne.", "heading": "Shadows", "content": "Page: Shadows\nSection: Shadows\n\nThis article is part of a series of articles about three.js. The first article is three.js fundamentals. If you haven't read that yet and you're new to three.js you might want to consider starting there. The previous article was about cameras which is important to have read before you read this article as well as the article before that one about lights.\n\nShadows on computers can be a complicated topic. There are various solutions and all of them have tradeoffs including the solutions available in three.js.\n\nThree.js by default uses shadow maps. The way a shadow map works is, for every light that casts shadows all objects marked to cast shadows are rendered from the point of view of the light. READ THAT AGAIN! and let it sink in.\n\nIn other words, if you have 20 objects, and 5 lights, and all 20 objects are casting shadows and all 5 lights are casting shadows then your entire scene will be drawn 6 times. All 20 objects will be drawn for light #1, then all 20 objects will be drawn for light #2, then #3, etc and finally the actual scene will be drawn using data from the first 5 renders.\n\nIt gets worse, if you have a point light casting shadows the scene has to be drawn 6 times just for that light!\n\nFor these reasons it's common to find other solutions than to have a bunch of lights all generating shadows. One common solution is to have multiple lights but only one directional light generating shadows.\n\nYet another solution is to use lightmaps and or ambient occlusion maps to pre-compute the effects of lighting offline. This results in static lighting or static lighting hints but at least it's fast. We'll cover both of those in another article.\n\nAnother solution is to use fake shadows. Make a plane, put a grayscale texture in the plane that approximates a shadow, draw it above the ground below your object.\n\nFor example let's use this texture as a fake shadow", "code_blocks": [{"language": "javascript", "code": "const scene = new THREE.Scene();\nscene.background = new THREE.Color('white');", "filename": ""}], "chunk_position": 29, "heading_path": "Shadows > Shadows", "breadcrumbs": "Shadows > Shadows > Shadows"}, {"id": "8fc684dbbdc41c27", "url": "https://threejs.org/manual/en/shadows.html", "page_title": "Shadows", "page_type": "guide", "page_summary": "A comprehensive guide to shadows in three.js, covering fake shadow techniques using textures, shadow maps for DirectionalLight, SpotLight, and PointLight, including configuring shadow cameras, shadow map resolution, and troubleshooting common issues like missing shadows and shadow acne.", "heading": "Setting up the scene with checkerboard ground", "content": "Page: Shadows\nSection: Setting up the scene with checkerboard ground\n\nWe'll use some of the code from the previous article.\n\nLet's set the background color to white.\n\nThen we'll setup the same checkerboard ground but this time it's using a MeshBasicMaterial as we don't need lighting for the ground.\n\nNote we're setting the color to 1.5, 1.5, 1.5. This will multiply the checkerboard texture's colors by 1.5, 1.5, 1.5. Since the texture's colors are 0x808080 and 0xC0C0C0 which is medium gray and light gray, multiplying them by 1.5 will give us a white and light grey checkerboard.", "code_blocks": [{"language": "javascript", "code": "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}", "filename": ""}], "chunk_position": 29, "heading_path": "Setting up the scene with checkerboard ground > Setting up the scene with checkerboard ground", "breadcrumbs": "Shadows > Setting up the scene with checkerboard ground > Setting up the scene with checkerboard ground"}, {"id": "a5bef810482e4869", "url": "https://threejs.org/manual/en/shadows.html", "page_title": "Shadows", "page_type": "guide", "page_summary": "A comprehensive guide to shadows in three.js, covering fake shadow techniques using textures, shadow maps for DirectionalLight, SpotLight, and PointLight, including configuring shadow cameras, shadow map resolution, and troubleshooting common issues like missing shadows and shadow acne.", "heading": "Loading the shadow texture", "content": "Page: Shadows\nSection: Loading the shadow texture\n\nLet's load the shadow texture and make an array to remember each sphere and associated objects.\n\nThen we'll make a sphere geometry and a plane geometry for the fake shadow.", "code_blocks": [{"language": "javascript", "code": "const shadowTexture = loader.load('resources/images/roundshadow.png');", "filename": ""}, {"language": "javascript", "code": "const sphereShadowBases = [];", "filename": ""}, {"language": "javascript", "code": "const sphereRadius = 1;\nconst sphereWidthDivisions = 32;\nconst sphereHeightDivisions = 16;\nconst sphereGeo = new THREE.SphereGeometry(sphereRadius, sphereWidthDivisions, sphereHeightDivisions);", "filename": ""}, {"language": "javascript", "code": "const planeSize = 1;\nconst shadowGeo = new THREE.PlaneGeometry(planeSize, planeSize);", "filename": ""}], "chunk_position": 29, "heading_path": "Loading the shadow texture > Loading the shadow texture", "breadcrumbs": "Shadows > Loading the shadow texture > Loading the shadow texture"}, {"id": "d1a6bf66b58d54fe", "url": "https://threejs.org/manual/en/shadows.html", "page_title": "Shadows", "page_type": "guide", "page_summary": "A comprehensive guide to shadows in three.js, covering fake shadow techniques using textures, shadow maps for DirectionalLight, SpotLight, and PointLight, including configuring shadow cameras, shadow map resolution, and troubleshooting common issues like missing shadows and shadow acne.", "heading": "Creating spheres with fake shadows", "content": "Page: Shadows\nSection: Creating spheres with fake shadows\n\nNow we'll make a bunch of spheres. For each sphere we'll create a base THREE.Object3D and we'll make both the shadow plane mesh and the sphere mesh children of the base. That way if we move the base both the sphere and the shadow will move. We need to put the shadow slightly above the ground to prevent z-fighting. We also set depthWrite to false so that the shadows don't mess each other up. We'll go over both of these issues in another article. The shadow is a MeshBasicMaterial because it doesn't need lighting.\n\nWe make each sphere a different hue and then save off the base, the sphere mesh, the shadow mesh and the initial y position of each sphere.", "code_blocks": [{"language": "javascript", "code": "const numSpheres = 15;\nfor (let i = 0; i < 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}", "filename": ""}], "chunk_position": 29, "heading_path": "Creating spheres with fake shadows > Creating spheres with fake shadows", "breadcrumbs": "Shadows > Creating spheres with fake shadows > Creating spheres with fake shadows"}, {"id": "42461d0a6aaae9dc", "url": "https://threejs.org/manual/en/shadows.html", "page_title": "Shadows", "page_type": "guide", "page_summary": "A comprehensive guide to shadows in three.js, covering fake shadow techniques using textures, shadow maps for DirectionalLight, SpotLight, and PointLight, including configuring shadow cameras, shadow map resolution, and troubleshooting common issues like missing shadows and shadow acne.", "heading": "Setting up lights", "content": "Page: Shadows\nSection: Setting up lights\n\nWe setup 2 lights. One is a HemisphereLight with the intensity set to 2 to really brighten things up. The other is a DirectionalLight so the spheres get some definition.", "code_blocks": [{"language": "javascript", "code": "{\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}", "filename": ""}, {"language": "javascript", "code": "{\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}", "filename": ""}], "chunk_position": 29, "heading_path": "Setting up lights > Setting up lights", "breadcrumbs": "Shadows > Setting up lights > Setting up lights"}, {"id": "85c7bce275568f77", "url": "https://threejs.org/manual/en/shadows.html", "page_title": "Shadows", "page_type": "guide", "page_summary": "A comprehensive guide to shadows in three.js, covering fake shadow techniques using textures, shadow maps for DirectionalLight, SpotLight, and PointLight, including configuring shadow cameras, shadow map resolution, and troubleshooting common issues like missing shadows and shadow acne.", "heading": "Animating the spheres and shadows", "content": "Page: Shadows\nSection: Animating the spheres and shadows\n\nIt would render as is but let's animate there spheres. For each sphere, shadow, base set we move the base in the xz plane, we move the sphere up and down using Math.abs(Math.sin(time)) which gives us a bouncy animation. And, we also set the shadow material's opacity so that as each sphere goes higher its shadow fades out.\n\nAnd here's 15 kind of bouncing balls.\n\nIn some apps it's common to use a round or oval shadow for everything but of course you could also use different shaped shadow textures. You might also give the shadow a harder edge. A good example of using this type of shadow is Animal Crossing Pocket Camp where you can see each character has a simple round shadow. It's effective and cheap. Monument Valley appears to also use this kind of shadow for the main character.", "code_blocks": [{"language": "javascript", "code": "function render(time) {\n  time *= 0.001;  // convert to seconds\n\n  ...\n\n  sphereShadowBases.forEach((sphereShadowBase, ndx) => {\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  ...", "filename": ""}], "chunk_position": 29, "heading_path": "Animating the spheres and shadows > Animating the spheres and shadows", "breadcrumbs": "Shadows > Animating the spheres and shadows > Animating the spheres and shadows"}, {"id": "2a8bea9da174d366", "url": "https://threejs.org/manual/en/shadows.html", "page_title": "Shadows", "page_type": "guide", "page_summary": "A comprehensive guide to shadows in three.js, covering fake shadow techniques using textures, shadow maps for DirectionalLight, SpotLight, and PointLight, including configuring shadow cameras, shadow map resolution, and troubleshooting common issues like missing shadows and shadow acne.", "heading": "Shadow Maps Overview", "content": "Page: Shadows\nSection: Shadow Maps Overview\n\nSo, moving on to shadow maps, there are 3 lights which can cast shadows. The DirectionalLight, the PointLight, and the SpotLight.\n\nLet's start with the DirectionalLight with the helper example from the lights article.", "code_blocks": [], "chunk_position": 29, "heading_path": "Shadow Maps Overview > Shadow Maps Overview", "breadcrumbs": "Shadows > Shadow Maps Overview > Shadow Maps Overview"}, {"id": "6fbfdc2f2a187768", "url": "https://threejs.org/manual/en/shadows.html", "page_title": "Shadows", "page_type": "guide", "page_summary": "A comprehensive guide to shadows in three.js, covering fake shadow techniques using textures, shadow maps for DirectionalLight, SpotLight, and PointLight, including configuring shadow cameras, shadow map resolution, and troubleshooting common issues like missing shadows and shadow acne.", "heading": "Enabling shadows in the renderer", "content": "Page: Shadows\nSection: Enabling shadows in the renderer\n\nThe first thing we need to do is turn on shadows in the renderer. Then we also need to tell the light to cast a shadow.\n\nWe also need to go to each mesh in the scene and decide if it should both cast shadows and/or receive shadows.\n\nLet's make the plane (the ground) only receive shadows since we don't really care what happens underneath. For the cube and the sphere let's have them both receive and cast shadows.\n\nAnd then we run it.", "code_blocks": [{"language": "javascript", "code": "const renderer = new THREE.WebGLRenderer({antialias: true, canvas});\nrenderer.shadowMap.enabled = true;", "filename": ""}, {"language": "javascript", "code": "const light = new THREE.DirectionalLight(color, intensity);\nlight.castShadow = true;", "filename": ""}, {"language": "javascript", "code": "const mesh = new THREE.Mesh(planeGeo, planeMat);\nmesh.receiveShadow = true;", "filename": ""}, {"language": "javascript", "code": "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;", "filename": ""}], "chunk_position": 29, "heading_path": "Enabling shadows in the renderer > Enabling shadows in the renderer", "breadcrumbs": "Shadows > Enabling shadows in the renderer > Enabling shadows in the renderer"}, {"id": "74f749dd0ea4a317", "url": "https://threejs.org/manual/en/shadows.html", "page_title": "Shadows", "page_type": "guide", "page_summary": "A comprehensive guide to shadows in three.js, covering fake shadow techniques using textures, shadow maps for DirectionalLight, SpotLight, and PointLight, including configuring shadow cameras, shadow map resolution, and troubleshooting common issues like missing shadows and shadow acne.", "heading": "Debugging missing shadows", "content": "Page: Shadows\nSection: Debugging missing shadows\n\nWhat happened? Why are parts of the shadows missing?\n\nThe reason is shadow maps are created by rendering the scene from the point of view of the light. In this case there is a camera at the DirectionalLight that is looking at its target. Just like the camera's we previously covered the light's shadow camera defines an area inside of which the shadows get rendered. In the example above that area is too small.\n\nIn order to visualize that area we can get the light's shadow camera and add a CameraHelper to the scene.\n\nAnd now you can see the area for which shadows are cast and received.\n\nAdjust the target x value back and forth and it should be pretty clear that only what's inside the light's shadow camera box is where shadows are drawn.", "code_blocks": [{"language": "javascript", "code": "const cameraHelper = new THREE.CameraHelper(light.shadow.camera);\nscene.add(cameraHelper);", "filename": ""}], "chunk_position": 29, "heading_path": "Debugging missing shadows > Debugging missing shadows", "breadcrumbs": "Shadows > Debugging missing shadows > Debugging missing shadows"}, {"id": "d9609ee4a95fbe33", "url": "https://threejs.org/manual/en/shadows.html", "page_title": "Shadows", "page_type": "guide", "page_summary": "A comprehensive guide to shadows in three.js, covering fake shadow techniques using textures, shadow maps for DirectionalLight, SpotLight, and PointLight, including configuring shadow cameras, shadow map resolution, and troubleshooting common issues like missing shadows and shadow acne.", "heading": "Adjusting the shadow camera", "content": "Page: Shadows\nSection: Adjusting the shadow camera\n\nWe can adjust the size of that box by adjusting the light's shadow camera.\n\nLet's add some GUI setting to adjust the light's shadow camera box. Since a DirectionalLight represents light all going in a parallel direction, the DirectionalLight uses an OrthographicCamera for its shadow camera. We went over how an OrthographicCamera works in the previous article about cameras.\n\nRecall an OrthographicCamera defines its box or view frustum by its left, right, top, bottom, near, far, and zoom properties.\n\nAgain let's make a helper class for the lil-gui. We'll make a DimensionGUIHelper that we'll pass an object and 2 properties. It will present one property that lil-gui can adjust and in response will set the two properties one positive and one negative. We can use this to set left and right as width and up and down as height.\n\nWe'll also use the MinMaxGUIHelper we created in the camera article to adjust near and far.\n\nWe tell the GUI to call our updateCamera function anytime anything changes. Let's write that function to update the light, the helper for the light, the light's shadow camera, and the helper showing the light's shadow camera.\n\nAnd now that we've given the light's shadow camera a GUI we can play with the values.\n\nSet the width and height to about 30 and you can see the shadows are correct and the areas that need to be in shadow for this scene are entirely covered.", "code_blocks": [{"language": "javascript", "code": "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}", "filename": ""}, {"language": "javascript", "code": "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}", "filename": ""}, {"language": "javascript", "code": "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();", "filename": ""}], "chunk_position": 29, "heading_path": "Adjusting the shadow camera > Adjusting the shadow camera", "breadcrumbs": "Shadows > Adjusting the shadow camera > Adjusting the shadow camera"}, {"id": "d1fdea35819365c4", "url": "https://threejs.org/manual/en/shadows.html", "page_title": "Shadows", "page_type": "guide", "page_summary": "A comprehensive guide to shadows in three.js, covering fake shadow techniques using textures, shadow maps for DirectionalLight, SpotLight, and PointLight, including configuring shadow cameras, shadow map resolution, and troubleshooting common issues like missing shadows and shadow acne.", "heading": "Shadow map resolution", "content": "Page: Shadows\nSection: Shadow map resolution\n\nBut this brings up the question, why not just set width and height to some giant numbers to just cover everything? Set the width and height to 100 and you might see something like this\n\nWhat's going on with these low-res shadows?!\n\nThis issue is yet another shadow related setting to be aware of. Shadow maps are textures the shadows get drawn into. Those textures have a size. The shadow camera's area we set above is stretched across that size. That means the larger area you set, the more blocky your shadows will be.\n\nYou can set the resolution of the shadow map's texture by setting light.shadow.mapSize.width and light.shadow.mapSize.height. They default to 512x512. The larger you make them the more memory they take and the slower they are to compute so you want to set them as small as you can and still make your scene work. The same is true with the light's shadow camera area. Smaller means better looking shadows so make the area as small as you can and still cover your scene. Be aware that each user's machine has a maximum texture size allowed which is available on the renderer as renderer.capabilities.maxTextureSize.\n\nOk but what about near and far I hear you thinking. Can we set near to 0.00001 and far to 100000000", "code_blocks": [], "chunk_position": 29, "heading_path": "Shadow map resolution > Shadow map resolution", "breadcrumbs": "Shadows > Shadow map resolution > Shadow map resolution"}, {"id": "c31c454f32a77ca4", "url": "https://threejs.org/manual/en/shadows.html", "page_title": "Shadows", "page_type": "guide", "page_summary": "A comprehensive guide to shadows in three.js, covering fake shadow techniques using textures, shadow maps for DirectionalLight, SpotLight, and PointLight, including configuring shadow cameras, shadow map resolution, and troubleshooting common issues like missing shadows and shadow acne.", "heading": "SpotLight shadows", "content": "Page: Shadows\nSection: SpotLight shadows\n\nSwitching to the SpotLight the light's shadow camera becomes a PerspectiveCamera. Unlike the DirectionalLight's shadow camera where we could manually set most its settings, SpotLight's shadow camera is controlled by the SpotLight itself. The fov for the shadow camera is directly connected to the SpotLight's angle setting. The aspect is set automatically based on the size of the shadow map.\n\nand we added back in the penumbra and angle settings from our article about lights.\n\nYou can notice, just like the last example if we set the angle high then the shadow map, the texture is spread over a very large area and the resolution of our shadows gets really low.\n\nYou can increase the size of the shadow map as mentioned above. You can also blur the result", "code_blocks": [{"language": "javascript", "code": "const light = new THREE.DirectionalLight(color, intensity);\nconst light = new THREE.SpotLight(color, intensity);", "filename": ""}], "chunk_position": 29, "heading_path": "SpotLight shadows > SpotLight shadows", "breadcrumbs": "Shadows > SpotLight shadows > SpotLight shadows"}, {"id": "befe5bb752287ee6", "url": "https://threejs.org/manual/en/shadows.html", "page_title": "Shadows", "page_type": "guide", "page_summary": "A comprehensive guide to shadows in three.js, covering fake shadow techniques using textures, shadow maps for DirectionalLight, SpotLight, and PointLight, including configuring shadow cameras, shadow map resolution, and troubleshooting common issues like missing shadows and shadow acne.", "heading": "PointLight shadows", "content": "Page: Shadows\nSection: PointLight shadows\n\nAnd finally there's shadows with a PointLight. Since a PointLight shines in all directions the only relevant settings are near and far. Otherwise the PointLight shadow is effectively 6 SpotLight shadows each one pointing to the face of a cube around the light. This means PointLight shadows are much slower since the entire scene must be drawn 6 times, one for each direction.\n\nLet's put a box around our scene so we can see shadows on the walls and ceiling. We'll set the material's side property to THREE.BackSide so we render the inside of the box instead of the outside. Like the floor we'll set it only to receive shadows. Also we'll set the position of the box so its bottom is slightly below the floor so the floor and the bottom of the box don't z-fight.\n\nAnd of course we need to switch the light to a PointLight.\n\nUse the position GUI settings to move the light around and you'll see the shadows fall on all the walls. You can also adjust near and far settings and see just like the other shadows when things are closer than near they no longer receive a shadow and they are further than far they are always in shadow.", "code_blocks": [{"language": "javascript", "code": "{\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}", "filename": ""}, {"language": "javascript", "code": "const light = new THREE.SpotLight(color, intensity);\nconst light = new THREE.PointLight(color, intensity);\n\n....\n\n// so we can easily see where the point light is\nconst helper = new THREE.PointLightHelper(light);\nscene.add(helper);", "filename": ""}], "chunk_position": 29, "heading_path": "PointLight shadows > PointLight shadows", "breadcrumbs": "Shadows > PointLight shadows > PointLight shadows"}, {"id": "84f3eb2feab74a9f", "url": "https://threejs.org/manual/en/shadows.html", "page_title": "Shadows", "page_type": "guide", "page_summary": "A comprehensive guide to shadows in three.js, covering fake shadow techniques using textures, shadow maps for DirectionalLight, SpotLight, and PointLight, including configuring shadow cameras, shadow map resolution, and troubleshooting common issues like missing shadows and shadow acne.", "heading": "Self shadow, shadow acne", "content": "Page: Shadows\nSection: Self shadow, shadow acne\n\n", "code_blocks": [], "chunk_position": 29, "heading_path": "Self shadow, shadow acne > Self shadow, shadow acne", "breadcrumbs": "Shadows > Self shadow, shadow acne > Self shadow, shadow acne"}, {"id": "deeec59b8b705b32", "url": "https://threejs.org/manual/en/fog.html", "page_title": "Fog", "page_type": "guide", "page_summary": "This article explains how to add fog to a three.js scene using Fog and FogExp2 classes, how to synchronize fog with the background color, how to create GUI controls for adjusting fog, and how to use the material-level fog property.", "heading": "Fog", "content": "Page: Fog\nSection: Fog\n\nThis article is part of a series of articles about three.js. The first article is three.js fundamentals. If you haven't read that yet and you're new to three.js you might want to consider starting there. If you haven't read about cameras you might want to start with this article.\n\nFog in a 3D engine is generally a way of fading to a specific color based on the distance from the camera. In three.js you add fog by creating Fog or FogExp2 object and setting it on the scene's fog property.\n\nFog lets you choose near and far settings which are distances from the camera. Anything closer than near is unaffected by fog. Anything further than far is completely the fog color. Parts between near and far fade from their material color to the fog color.\n\nThere's also FogExp2 which grows exponentially with distance from the camera.\n\nTo use either type of fog you create one and and assign it to the scene as in", "code_blocks": [{"language": "javascript", "code": "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}", "filename": ""}], "chunk_position": 30, "heading_path": "Fog > Fog", "breadcrumbs": "Fog > Fog > Fog"}, {"id": "103a5f5994e43bb3", "url": "https://threejs.org/manual/en/fog.html", "page_title": "Fog", "page_type": "guide", "page_summary": "This article explains how to add fog to a three.js scene using Fog and FogExp2 classes, how to synchronize fog with the background color, how to create GUI controls for adjusting fog, and how to use the material-level fog property.", "heading": "FogExp2", "content": "Page: Fog\nSection: FogExp2\n\nor for FogExp2 it would be", "code_blocks": [{"language": "javascript", "code": "const scene = new THREE.Scene();\n{\n  const color = 0xFFFFFF;\n  const density = 0.1;\n  scene.fog = new THREE.FogExp2(color, density);\n}", "filename": ""}], "chunk_position": 30, "heading_path": "FogExp2 > FogExp2", "breadcrumbs": "Fog > FogExp2 > FogExp2"}, {"id": "327d0f2914fa0c81", "url": "https://threejs.org/manual/en/fog.html", "page_title": "Fog", "page_type": "guide", "page_summary": "This article explains how to add fog to a three.js scene using Fog and FogExp2 classes, how to synchronize fog with the background color, how to create GUI controls for adjusting fog, and how to use the material-level fog property.", "heading": "Choosing Between Fog and FogExp2", "content": "Page: Fog\nSection: Choosing Between Fog and FogExp2\n\nFogExp2 is closer to reality but Fog is used more commonly since it lets you choose a place to apply the fog so you can decide to show a clear scene up to a certain distance and then fade out to some color past that distance.\n\nIt's important to note that the fog is applied to *things that are rendered*. It is part of the calculation of each pixel of the color of the object. What that means is if you want your scene to fade to a certain color you need to set the fog **and** the background color to the same color. The background color is set using the scene.background property. To pick a background color you attach a THREE.Color to it. For example", "code_blocks": [{"language": "javascript", "code": "scene.background = new THREE.Color('#F00');  // red", "filename": ""}], "chunk_position": 30, "heading_path": "Choosing Between Fog and FogExp2 > Choosing Between Fog and FogExp2", "breadcrumbs": "Fog > Choosing Between Fog and FogExp2 > Choosing Between Fog and FogExp2"}, {"id": "ce73ee69d455b1f9", "url": "https://threejs.org/manual/en/fog.html", "page_title": "Fog", "page_type": "guide", "page_summary": "This article explains how to add fog to a three.js scene using Fog and FogExp2 classes, how to synchronize fog with the background color, how to create GUI controls for adjusting fog, and how to use the material-level fog property.", "heading": "Example with Fog Added", "content": "Page: Fog\nSection: Example with Fog Added\n\nHere is one of our previous examples with fog added. The only addition is right after setting up the scene we add the fog and set the scene's background color", "code_blocks": [{"language": "javascript", "code": "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}", "filename": ""}], "chunk_position": 30, "heading_path": "Example with Fog Added > Example with Fog Added", "breadcrumbs": "Fog > Example with Fog Added > Example with Fog Added"}, {"id": "5da777d909041f24", "url": "https://threejs.org/manual/en/fog.html", "page_title": "Fog", "page_type": "guide", "page_summary": "This article explains how to add fog to a three.js scene using Fog and FogExp2 classes, how to synchronize fog with the background color, how to create GUI controls for adjusting fog, and how to use the material-level fog property.", "heading": "Camera and Cube Setup", "content": "Page: Fog\nSection: Camera and Cube Setup\n\nIn the example below the camera's near is 0.1 and its far is 5. The camera is at z = 2. The cubes are 1 unit large and at Z = 0. This means with a fog setting of near = 1 and far = 2 the cubes will fade out right around their center.", "code_blocks": [], "chunk_position": 30, "heading_path": "Camera and Cube Setup > Camera and Cube Setup", "breadcrumbs": "Fog > Camera and Cube Setup > Camera and Cube Setup"}, {"id": "a8ea4e4efa5fdc7d", "url": "https://threejs.org/manual/en/fog.html", "page_title": "Fog", "page_type": "guide", "page_summary": "This article explains how to add fog to a three.js scene using Fog and FogExp2 classes, how to synchronize fog with the background color, how to create GUI controls for adjusting fog, and how to use the material-level fog property.", "heading": "Adding GUI Controls", "content": "Page: Fog\nSection: Adding GUI Controls\n\nLet's add an interface so we can adjust the fog. Again we'll use lil-gui. lil-gui takes an object and a property and automagically makes an interface for that type of property. We could just simply let it manipulate the fog's near and far properties but it's invalid to have near be greater than far so let's make a helper so lil-gui can manipulate a near and far property but we'll make sure near is less than or equal to far and far is greater than or equal near.", "code_blocks": [{"language": "javascript", "code": "// We use this class to pass to lil-gui\n// so when it manipulates near or far\n// near is never > far and far is never < 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}", "filename": ""}], "chunk_position": 30, "heading_path": "Adding GUI Controls > Adding GUI Controls", "breadcrumbs": "Fog > Adding GUI Controls > Adding GUI Controls"}, {"id": "4115760b5b92afd8", "url": "https://threejs.org/manual/en/fog.html", "page_title": "Fog", "page_type": "guide", "page_summary": "This article explains how to add fog to a three.js scene using Fog and FogExp2 classes, how to synchronize fog with the background color, how to create GUI controls for adjusting fog, and how to use the material-level fog property.", "heading": "Wiring Up the GUI Helper", "content": "Page: Fog\nSection: Wiring Up the GUI Helper\n\nWe can then add it like this", "code_blocks": [{"language": "javascript", "code": "{\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}", "filename": ""}], "chunk_position": 30, "heading_path": "Wiring Up the GUI Helper > Wiring Up the GUI Helper", "breadcrumbs": "Fog > Wiring Up the GUI Helper > Wiring Up the GUI Helper"}, {"id": "92a616de03728a63", "url": "https://threejs.org/manual/en/fog.html", "page_title": "Fog", "page_type": "guide", "page_summary": "This article explains how to add fog to a three.js scene using Fog and FogExp2 classes, how to synchronize fog with the background color, how to create GUI controls for adjusting fog, and how to use the material-level fog property.", "heading": "GUI Parameter Ranges and Listen", "content": "Page: Fog\nSection: GUI Parameter Ranges and Listen\n\nThe near and far parameters set the minimum and maximum values for adjusting the fog. They are set when we setup the camera.\n\nThe .listen() at the end of the last 2 lines tells lil-gui to *listen* for changes. That way when we change near because of an edit to far or we change far in response to an edit to near lil-gui will update the other property's UI for us.", "code_blocks": [], "chunk_position": 30, "heading_path": "GUI Parameter Ranges and Listen > GUI Parameter Ranges and Listen", "breadcrumbs": "Fog > GUI Parameter Ranges and Listen > GUI Parameter Ranges and Listen"}, {"id": "5f50d9d3be94d6f4", "url": "https://threejs.org/manual/en/fog.html", "page_title": "Fog", "page_type": "guide", "page_summary": "This article explains how to add fog to a three.js scene using Fog and FogExp2 classes, how to synchronize fog with the background color, how to create GUI controls for adjusting fog, and how to use the material-level fog property.", "heading": "Adding Color Control", "content": "Page: Fog\nSection: Adding Color Control\n\nIt might also be nice to be able to change the fog color but like was mentioned above we need to keep both the fog color and the background color in sync. So, let's add another *virtual* property to our helper that will set both colors when lil-gui manipulates it.\n\nlil-gui can manipulate colors in 4 ways, as a CSS 6 digit hex string (eg: #112233). As an hue, saturation, value, object (eg: {h: 60, s: 1, v: }). As an RGB array (eg: [255, 128, 64]). Or, as an RGBA array (eg: [127, 200, 75, 0.3]).\n\nIt's easiest for our purpose to use the hex string version since that way lil-gui is only manipulating a single value. Fortunately THREE.Color as a getHexString method we get use to easily get such a string, we just have to prepend a '#' to the front.", "code_blocks": [{"language": "javascript", "code": "// We use this class to pass to lil-gui\n// so when it manipulates near or far\n// near is never > far and far is never < 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 `#${this.fog.color.getHexString()}`;\n  }\n  set color(hexString) {\n    this.fog.color.set(hexString);\n    this.backgroundColor.set(hexString);\n  }\n}", "filename": ""}], "chunk_position": 30, "heading_path": "Adding Color Control > Adding Color Control", "breadcrumbs": "Fog > Adding Color Control > Adding Color Control"}, {"id": "9548c598c1d6fa58", "url": "https://threejs.org/manual/en/fog.html", "page_title": "Fog", "page_type": "guide", "page_summary": "This article explains how to add fog to a three.js scene using Fog and FogExp2 classes, how to synchronize fog with the background color, how to create GUI controls for adjusting fog, and how to use the material-level fog property.", "heading": "Final GUI Setup with Color", "content": "Page: Fog\nSection: Final GUI Setup with Color\n\nWe then call gui.addColor to add a color UI for our helper's virtual property.", "code_blocks": [{"language": "javascript", "code": "{\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}", "filename": ""}], "chunk_position": 30, "heading_path": "Final GUI Setup with Color > Final GUI Setup with Color", "breadcrumbs": "Fog > Final GUI Setup with Color > Final GUI Setup with Color"}, {"id": "70a9de218c9521ea", "url": "https://threejs.org/manual/en/fog.html", "page_title": "Fog", "page_type": "guide", "page_summary": "This article explains how to add fog to a three.js scene using Fog and FogExp2 classes, how to synchronize fog with the background color, how to create GUI controls for adjusting fog, and how to use the material-level fog property.", "heading": "Sharp vs Smooth Transitions", "content": "Page: Fog\nSection: Sharp vs Smooth Transitions\n\nYou can see setting near to like 1.9 and far to 2.0 gives a very sharp transition between un-fogged and completely fogged. where as near = 1.1 and far = 2.9 should just about be the smoothest given our cubes are spinning 2 units away from the camera.", "code_blocks": [], "chunk_position": 30, "heading_path": "Sharp vs Smooth Transitions > Sharp vs Smooth Transitions", "breadcrumbs": "Fog > Sharp vs Smooth Transitions > Sharp vs Smooth Transitions"}, {"id": "648d7c67a6ca8a71", "url": "https://threejs.org/manual/en/fog.html", "page_title": "Fog", "page_type": "guide", "page_summary": "This article explains how to add fog to a three.js scene using Fog and FogExp2 classes, how to synchronize fog with the background color, how to create GUI controls for adjusting fog, and how to use the material-level fog property.", "heading": "Material Fog Property", "content": "Page: Fog\nSection: Material Fog Property\n\nOne last thing, there is a boolean fog property on a material for whether or not objects rendered with that material are affected by fog. It defaults to true for most materials. As an example of why you might want to turn the fog off, imagine you're making a 3D vehicle simulator with a view from the driver's seat or cockpit. You probably want the fog off for everything inside the vehicle when viewing from inside the vehicle.\n\nA better example might be a house and thick fog outside house. Let's say the fog is set to start 2 meters away (near = 2) and completely fogged out at 4 meters (far = 4). Rooms are longer than 2 meters and the house is probably longer than 4 meters so you need to set the materials for the inside of the house to not apply fog otherwise when standing inside the house looking outside the wall at the far end of the room will look like it's in the fog.\n\nNotice the walls and ceiling at the far end of the room are getting fog applied. By turning fog off on the materials for the house we can fix that issue.", "code_blocks": [], "chunk_position": 30, "heading_path": "Material Fog Property > Material Fog Property", "breadcrumbs": "Fog > Material Fog Property > Material Fog Property"}, {"id": "9301f0f4b6bfb49e", "url": "https://threejs.org/manual/en/rendertargets.html", "page_title": "Render Targets", "page_type": "guide", "page_summary": "A tutorial on using WebGLRenderTarget in three.js to render a scene into a texture, then apply that texture to other objects. Covers creation, rendering setup, use cases, depth/stencil buffer options, and dynamic resizing.", "heading": "Introduction", "content": "Page: Render Targets\nSection: Introduction\n\nA render target in three.js is basically a texture you can render to. After you render to it you can use that texture like any other texture.\n\nLet's make a simple example. We'll start with an example from the article on responsiveness.\n\nRendering to a render target is almost exactly the same as normal rendering. First we create a WebGLRenderTarget.", "code_blocks": [{"language": "javascript", "code": "const rtWidth = 512;\nconst rtHeight = 512;\nconst renderTarget = new THREE.WebGLRenderTarget(rtWidth, rtHeight);", "filename": ""}], "chunk_position": 31, "heading_path": "Introduction > Introduction", "breadcrumbs": "Render Targets > Introduction > Introduction"}, {"id": "93f3424e488e23d3", "url": "https://threejs.org/manual/en/rendertargets.html", "page_title": "Render Targets", "page_type": "guide", "page_summary": "A tutorial on using WebGLRenderTarget in three.js to render a scene into a texture, then apply that texture to other objects. Covers creation, rendering setup, use cases, depth/stencil buffer options, and dynamic resizing.", "heading": "Setting up a Camera and Scene for the Render Target", "content": "Page: Render Targets\nSection: Setting up a Camera and Scene for the Render Target\n\nThen we need a Camera and a Scene.\n\nNotice we set the aspect to the aspect for the render target, not the canvas. The correct aspect to use depends on what we are rendering for. In this case we'll use the render target's texture on the side of a cube. Since faces of the cube are square we want an aspect of 1.0.", "code_blocks": [{"language": "javascript", "code": "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');", "filename": ""}], "chunk_position": 31, "heading_path": "Setting up a Camera and Scene for the Render Target > Setting up a Camera and Scene for the Render Target", "breadcrumbs": "Render Targets > Setting up a Camera and Scene for the Render Target > Setting up a Camera and Scene for the Render Target"}, {"id": "dd439e2ba457790a", "url": "https://threejs.org/manual/en/rendertargets.html", "page_title": "Render Targets", "page_type": "guide", "page_summary": "A tutorial on using WebGLRenderTarget in three.js to render a scene into a texture, then apply that texture to other objects. Covers creation, rendering setup, use cases, depth/stencil buffer options, and dynamic resizing.", "heading": "Adding Objects to the Render Target Scene", "content": "Page: Render Targets\nSection: Adding Objects to the Render Target Scene\n\nWe fill the scene with stuff. In this case we're using the light and the 3 cubes from the previous article.", "code_blocks": [{"language": "javascript", "code": "{\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];", "filename": ""}], "chunk_position": 31, "heading_path": "Adding Objects to the Render Target Scene > Adding Objects to the Render Target Scene", "breadcrumbs": "Render Targets > Adding Objects to the Render Target Scene > Adding Objects to the Render Target Scene"}, {"id": "03c640a005e30cd7", "url": "https://threejs.org/manual/en/rendertargets.html", "page_title": "Render Targets", "page_type": "guide", "page_summary": "A tutorial on using WebGLRenderTarget in three.js to render a scene into a texture, then apply that texture to other objects. Covers creation, rendering setup, use cases, depth/stencil buffer options, and dynamic resizing.", "heading": "Using the Render Target as a Texture", "content": "Page: Render Targets\nSection: Using the Render Target as a Texture\n\nThe Scene and Camera from the previous article are still there. We'll use them to render to the canvas. We just need to add stuff to render.\n\nLet's add a cube that uses the render target's texture.", "code_blocks": [{"language": "javascript", "code": "const material = new THREE.MeshPhongMaterial({\n  map: renderTarget.texture,\n});\nconst cube = new THREE.Mesh(geometry, material);\nscene.add(cube);", "filename": ""}], "chunk_position": 31, "heading_path": "Using the Render Target as a Texture > Using the Render Target as a Texture", "breadcrumbs": "Render Targets > Using the Render Target as a Texture > Using the Render Target as a Texture"}, {"id": "280a5f72f1e86f6a", "url": "https://threejs.org/manual/en/rendertargets.html", "page_title": "Render Targets", "page_type": "guide", "page_summary": "A tutorial on using WebGLRenderTarget in three.js to render a scene into a texture, then apply that texture to other objects. Covers creation, rendering setup, use cases, depth/stencil buffer options, and dynamic resizing.", "heading": "Rendering in the Animation Loop", "content": "Page: Render Targets\nSection: Rendering in the Animation Loop\n\nNow at render time first we render the render target scene to the render target.\n\nThen we render the scene with the single cube that is using the render target's texture to the canvas.\n\nAnd voilà.", "code_blocks": [{"language": "javascript", "code": "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) => {\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);", "filename": ""}, {"language": "javascript", "code": "// 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);", "filename": ""}], "chunk_position": 31, "heading_path": "Rendering in the Animation Loop > Rendering in the Animation Loop", "breadcrumbs": "Render Targets > Rendering in the Animation Loop > Rendering in the Animation Loop"}, {"id": "8b055f4fe8a48a14", "url": "https://threejs.org/manual/en/rendertargets.html", "page_title": "Render Targets", "page_type": "guide", "page_summary": "A tutorial on using WebGLRenderTarget in three.js to render a scene into a texture, then apply that texture to other objects. Covers creation, rendering setup, use cases, depth/stencil buffer options, and dynamic resizing.", "heading": "Result", "content": "Page: Render Targets\nSection: Result\n\n[click here to open in a separate window](/manual/examples/render-target.html)\n\nThe cube is red because we set the `background` of the `rtScene` to red so the render target's texture is being cleared to red.", "code_blocks": [], "chunk_position": 31, "heading_path": "Result > Result", "breadcrumbs": "Render Targets > Result > Result"}, {"id": "7444186e3c0c3138", "url": "https://threejs.org/manual/en/rendertargets.html", "page_title": "Render Targets", "page_type": "guide", "page_summary": "A tutorial on using WebGLRenderTarget in three.js to render a scene into a texture, then apply that texture to other objects. Covers creation, rendering setup, use cases, depth/stencil buffer options, and dynamic resizing.", "heading": "Common Uses for Render Targets", "content": "Page: Render Targets\nSection: Common Uses for Render Targets\n\nRender targets are used for all kinds of things. Shadows use render targets. Picking can use a render target. Various kinds of post processing effects require render targets. Rendering a rear view mirror in a car or a live view on a monitor inside a 3D scene might use a render target.", "code_blocks": [], "chunk_position": 31, "heading_path": "Common Uses for Render Targets > Common Uses for Render Targets", "breadcrumbs": "Render Targets > Common Uses for Render Targets > Common Uses for Render Targets"}, {"id": "a5db582d86007646", "url": "https://threejs.org/manual/en/rendertargets.html", "page_title": "Render Targets", "page_type": "guide", "page_summary": "A tutorial on using WebGLRenderTarget in three.js to render a scene into a texture, then apply that texture to other objects. Covers creation, rendering setup, use cases, depth/stencil buffer options, and dynamic resizing.", "heading": "Notes on WebGLRenderTarget", "content": "Page: Render Targets\nSection: Notes on WebGLRenderTarget\n\nA few notes about using WebGLRenderTarget.", "code_blocks": [], "chunk_position": 31, "heading_path": "Notes on WebGLRenderTarget > Notes on WebGLRenderTarget", "breadcrumbs": "Render Targets > Notes on WebGLRenderTarget > Notes on WebGLRenderTarget"}, {"id": "e242a990aff5a38f", "url": "https://threejs.org/manual/en/rendertargets.html", "page_title": "Render Targets", "page_type": "guide", "page_summary": "A tutorial on using WebGLRenderTarget in three.js to render a scene into a texture, then apply that texture to other objects. Covers creation, rendering setup, use cases, depth/stencil buffer options, and dynamic resizing.", "heading": "Disabling Depth and Stencil Buffers", "content": "Page: Render Targets\nSection: Disabling Depth and Stencil Buffers\n\nBy default WebGLRenderTarget 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:", "code_blocks": [{"language": "javascript", "code": "const rt = new THREE.WebGLRenderTarget(width, height, {\n    depthBuffer: false,\n    stencilBuffer: false,\n  });", "filename": ""}], "chunk_position": 31, "heading_path": "Disabling Depth and Stencil Buffers > Disabling Depth and Stencil Buffers", "breadcrumbs": "Render Targets > Disabling Depth and Stencil Buffers > Disabling Depth and Stencil Buffers"}, {"id": "7bb3ba6a35529398", "url": "https://threejs.org/manual/en/rendertargets.html", "page_title": "Render Targets", "page_type": "guide", "page_summary": "A tutorial on using WebGLRenderTarget in three.js to render a scene into a texture, then apply that texture to other objects. Covers creation, rendering setup, use cases, depth/stencil buffer options, and dynamic resizing.", "heading": "Resizing a Render Target", "content": "Page: Render Targets\nSection: Resizing a Render Target\n\nYou might need to change the size of a render target\n\nIn 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:", "code_blocks": [{"language": "javascript", "code": "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}", "filename": ""}], "chunk_position": 31, "heading_path": "Resizing a Render Target > Resizing a Render Target", "breadcrumbs": "Render Targets > Resizing a Render Target > Resizing a Render Target"}, {"id": "e7c337333162bb22", "url": "https://threejs.org/manual/en/custom-buffergeometry.html", "page_title": "Custom BufferGeometry", "page_type": "guide", "page_summary": "A guide on how to use BufferGeometry directly in three.js to create custom geometry, including building a cube from parallel arrays, using indices to reduce duplication, working with TypedArrays, and dynamically updating vertex data.", "heading": "Custom BufferGeometry", "content": "Page: Custom BufferGeometry\nSection: Custom BufferGeometry\n\nBufferGeometry is three.js's way of representing all geometry. A BufferGeometry is essentially a collection of named BufferAttributes. Each BufferAttribute represents an array of one type of data: positions, normals, colors, uv, etc... Together, the named BufferAttributes represent parallel arrays of all the data for each vertex.\n\nAbove you can see we have 4 attributes: position, normal, color, uv. They represent parallel arrays which means that the Nth set of data in each attribute belongs to the same vertex. The vertex at index = 4 is highlighted to show that the parallel data across all attributes defines one vertex.\n\nThis brings up a point, here's a diagram of a cube with one corner highlighted.\n\nThinking about it that single corner needs a different normal for each face of the cube. A normal is info about which direction something faces. In the diagram the normals are presented by the arrows around the corner vertex showing that each face that shares that vertex position needs a normal that points in a different direction.\n\nThat corner needs different UVs for each face as well. UVs are texture coordinates that specify which part of a texture being drawn on a triangle corresponds to that vertex position. You can see the green face needs that vertex to have a UV that corresponds to the top right corner of the F texture, the blue face needs a UV that corresponds to the top left corner of the F texture, and the red face needs a UV that corresponds to the bottom left corner of the F texture.\n\nA single vertex is the combination of all of its parts. If a vertex needs any part to be different then it must be a different vertex.\n\nAs a simple example let's make a cube using BufferGeometry. A cube is interesting because it appears to share vertices at the corners but really does not. For our example we'll list out all the vertices with all their data and then convert that data into parallel arrays and finally use those to make BufferAttributes and add them to a BufferGeometry.", "code_blocks": [], "chunk_position": 32, "heading_path": "Custom BufferGeometry > Custom BufferGeometry", "breadcrumbs": "Custom BufferGeometry > Custom BufferGeometry > Custom BufferGeometry"}, {"id": "31c1c911d3a9c3e3", "url": "https://threejs.org/manual/en/custom-buffergeometry.html", "page_title": "Custom BufferGeometry", "page_type": "guide", "page_summary": "A guide on how to use BufferGeometry directly in three.js to create custom geometry, including building a cube from parallel arrays, using indices to reduce duplication, working with TypedArrays, and dynamically updating vertex data.", "heading": "Listing Cube Vertices", "content": "Page: Custom BufferGeometry\nSection: Listing Cube Vertices\n\nWe start with a list of all the data needed for the cube. Remember again that if a vertex has any unique parts it has to be a separate vertex. As such to make a cube requires 36 vertices. 2 triangles per face, 3 vertices per triangle, 6 faces = 36 vertices.", "code_blocks": [{"language": "javascript", "code": "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];", "filename": ""}], "chunk_position": 32, "heading_path": "Listing Cube Vertices > Listing Cube Vertices", "breadcrumbs": "Custom BufferGeometry > Listing Cube Vertices > Listing Cube Vertices"}, {"id": "dcfd3a2fd03009ff", "url": "https://threejs.org/manual/en/custom-buffergeometry.html", "page_title": "Custom BufferGeometry", "page_type": "guide", "page_summary": "A guide on how to use BufferGeometry directly in three.js to create custom geometry, including building a cube from parallel arrays, using indices to reduce duplication, working with TypedArrays, and dynamically updating vertex data.", "heading": "Converting to Parallel Arrays", "content": "Page: Custom BufferGeometry\nSection: Converting to Parallel Arrays\n\nWe can then translate all of that into 3 parallel arrays", "code_blocks": [{"language": "javascript", "code": "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}", "filename": ""}], "chunk_position": 32, "heading_path": "Converting to Parallel Arrays > Converting to Parallel Arrays", "breadcrumbs": "Custom BufferGeometry > Converting to Parallel Arrays > Converting to Parallel Arrays"}, {"id": "b844c181615fc0af", "url": "https://threejs.org/manual/en/custom-buffergeometry.html", "page_title": "Custom BufferGeometry", "page_type": "guide", "page_summary": "A guide on how to use BufferGeometry directly in three.js to create custom geometry, including building a cube from parallel arrays, using indices to reduce duplication, working with TypedArrays, and dynamically updating vertex data.", "heading": "Creating BufferAttributes", "content": "Page: Custom BufferGeometry\nSection: Creating BufferAttributes\n\nFinally we can create a BufferGeometry and then a BufferAttribute for each array and add it to the BufferGeometry.\n\nNote that the names are significant. You must name your attributes the names that match what three.js expects (unless you are creating a custom shader). In this case position, normal, and uv. If you want vertex colors then name your attribute color.\n\nAbove we created 3 JavaScript native arrays, positions, normals and uvs. We then convert those into TypedArrays of type Float32Array. A BufferAttribute requires a TypedArray not a native array. A BufferAttribute also requires you to tell it how many components there are per vertex. For the positions and normals we have 3 components per vertex, x, y, and z. For the UVs we have 2, u and v.", "code_blocks": [{"language": "javascript", "code": "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));", "filename": ""}], "chunk_position": 32, "heading_path": "Creating BufferAttributes > Creating BufferAttributes", "breadcrumbs": "Custom BufferGeometry > Creating BufferAttributes > Creating BufferAttributes"}, {"id": "a9491d90f501d45f", "url": "https://threejs.org/manual/en/custom-buffergeometry.html", "page_title": "Custom BufferGeometry", "page_type": "guide", "page_summary": "A guide on how to use BufferGeometry directly in three.js to create custom geometry, including building a cube from parallel arrays, using indices to reduce duplication, working with TypedArrays, and dynamically updating vertex data.", "heading": "Using Indices", "content": "Page: Custom BufferGeometry\nSection: Using Indices\n\nThat's a lot of data. A small thing we can do is use indices to reference the vertices. Looking back at our cube data, each face is made from 2 triangles with 3 vertices each, 6 vertices total, but 2 of those vertices are exactly the same; The same position, the same normal, and the same uv. So, we can remove the matching vertices and then reference them by index. First we remove the matching vertices.\n\nSo now we have 24 unique vertices. Then we specify 36 indices for the 36 vertices we need drawn to make 12 triangles by calling BufferGeometry.setIndex with an array of indices.", "code_blocks": [{"language": "javascript", "code": "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: [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], }, // 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], }, // 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], }, // 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], }, // 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], }, // 22\n  { pos: [-1, -1, -1], norm: [ 0, -1,  0], uv: [1, 1], }, // 23\n];", "filename": ""}, {"language": "javascript", "code": "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\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]);", "filename": ""}], "chunk_position": 32, "heading_path": "Using Indices > Using Indices", "breadcrumbs": "Custom BufferGeometry > Using Indices > Using Indices"}, {"id": "e74e391995717aac", "url": "https://threejs.org/manual/en/custom-buffergeometry.html", "page_title": "Custom BufferGeometry", "page_type": "guide", "page_summary": "A guide on how to use BufferGeometry directly in three.js to create custom geometry, including building a cube from parallel arrays, using indices to reduce duplication, working with TypedArrays, and dynamically updating vertex data.", "heading": "Computing Normals Automatically", "content": "Page: Custom BufferGeometry\nSection: Computing Normals Automatically\n\nBufferGeometry has a computeVertexNormals method for computing normals if you are not supplying them. Unfortunately, since positions can not be shared if any other part of a vertex is different, the results of calling computeVertexNormals will generate seams if your geometry is supposed to connect to itself like a sphere or a cylinder.\n\nFor the cylinder above the normals were created using computeVertexNormals. If you look closely there is a seam on the cylinder. This is because there is no way to share the vertices at the start and end of the cylinder since they require different UVs so the function to compute them has no idea those are the same vertices to smooth over them. Just a small thing to be aware of. The solution is to supply your own normals.", "code_blocks": [], "chunk_position": 32, "heading_path": "Computing Normals Automatically > Computing Normals Automatically", "breadcrumbs": "Custom BufferGeometry > Computing Normals Automatically > Computing Normals Automatically"}, {"id": "fc10de76729ad7e9", "url": "https://threejs.org/manual/en/custom-buffergeometry.html", "page_title": "Custom BufferGeometry", "page_type": "guide", "page_summary": "A guide on how to use BufferGeometry directly in three.js to create custom geometry, including building a cube from parallel arrays, using indices to reduce duplication, working with TypedArrays, and dynamically updating vertex data.", "heading": "Using TypedArrays", "content": "Page: Custom BufferGeometry\nSection: Using TypedArrays\n\nWe can also use TypedArrays from the start instead of native JavaScript arrays. The disadvantage to TypedArrays is you must specify their size up front. Of course that's not that large of a burden but with native arrays we can just push values onto them and look at what size they end up by checking their length at the end. With TypedArrays there is no push function so we need to do our own bookkeeping when adding values to them.\n\nIn this example knowing the length up front is pretty easy since we're using a big block of static data to start.", "code_blocks": [{"language": "javascript", "code": "const numVertices = vertices.length;\nconst positionNumComponents = 3;\nconst normalNumComponents = 3;\nconst uvNumComponents = 2;\nconst positions = new Float32Array(numVertices * positionNumComponents);\nconst normals = new Float32Array(numVertices * normalNumComponents);\nconst uvs = new Float32Array(numVertices * uvNumComponents);\nlet posNdx = 0;\nlet nrmNdx = 0;\nlet uvNdx = 0;\nfor (const vertex of vertices) {\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(positions, positionNumComponents));\ngeometry.setAttribute(\n    'normal',\n    new THREE.BufferAttribute(normals, normalNumComponents));\ngeometry.setAttribute(\n    'uv',\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]);", "filename": ""}], "chunk_position": 32, "heading_path": "Using TypedArrays > Using TypedArrays", "breadcrumbs": "Custom BufferGeometry > Using TypedArrays > Using TypedArrays"}, {"id": "4d14e2022eb55a2c", "url": "https://threejs.org/manual/en/custom-buffergeometry.html", "page_title": "Custom BufferGeometry", "page_type": "guide", "page_summary": "A guide on how to use BufferGeometry directly in three.js to create custom geometry, including building a cube from parallel arrays, using indices to reduce duplication, working with TypedArrays, and dynamically updating vertex data.", "heading": "Dynamic Updates", "content": "Page: Custom BufferGeometry\nSection: Dynamic Updates\n\nA good reason to use typedarrays is if you want to dynamically update any part of the vertices.\n\nI couldn't think of a really good example of dynamically updating the vertices so I decided to make a sphere and move each quad in and out from the center. Hopefully it's a useful example.\n\nHere's the code to generate positions and indices for a sphere. The code is sharing vertices within a quad but it's not sharing vertices between quads because we want to be able to move each quad separately.\n\nBecause I'm lazy I used a small hierarchy of 3 Object3D objects to compute sphere points. How this works is explained in the article on optimizing lots of objects.", "code_blocks": [{"language": "javascript", "code": "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 < 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 < 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}", "filename": ""}, {"language": "javascript", "code": "const segmentsAround = 24;\nconst segmentsDown = 16;\nconst {positions, indices} = makeSpherePositions(segmentsAround, segmentsDown);", "filename": ""}, {"language": "javascript", "code": "const normals = positions.slice();", "filename": ""}, {"language": "javascript", "code": "const geometry = new THREE.BufferGeometry();\nconst positionNumComponents = 3;\nconst normalNumComponents = 3;\n\nconst positionAttribute = new THREE.BufferAttribute(positions, positionNumComponents);\npositionAttribute.setUsage(THREE.DynamicDrawUsage);\ngeometry.setAttribute(\n    'position',\n    positionAttribute);\ngeometry.setAttribute(\n    'normal',\n    new THREE.BufferAttribute(normals, normalNumComponents));\ngeometry.setIndex(indices);", "filename": ""}, {"language": "javascript", "code": "const temp = new THREE.Vector3();\n\n...\n\nfor (let i = 0; i < 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;", "filename": ""}], "chunk_position": 32, "heading_path": "Dynamic Updates > Dynamic Updates", "breadcrumbs": "Custom BufferGeometry > Dynamic Updates > Dynamic Updates"}, {"id": "addca27cb2dcd7df", "url": "https://threejs.org/manual/en/custom-buffergeometry.html", "page_title": "Custom BufferGeometry", "page_type": "guide", "page_summary": "A guide on how to use BufferGeometry directly in three.js to create custom geometry, including building a cube from parallel arrays, using indices to reduce duplication, working with TypedArrays, and dynamically updating vertex data.", "heading": "Dynamic Attribute Setup Notes", "content": "Page: Custom BufferGeometry\nSection: Dynamic Attribute Setup Notes\n\nI've highlighted a few differences. We save a reference to the position attribute. We also mark it as dynamic. This is a hint to THREE.js that we're going to be changing the contents of the attribute often.\n\nIn our render loop we update the positions based off their normals every frame.\n\nAnd we set positionAttribute.needsUpdate to tell THREE.js to use our changes.", "code_blocks": [], "chunk_position": 32, "heading_path": "Dynamic Attribute Setup Notes > Dynamic Attribute Setup Notes", "breadcrumbs": "Custom BufferGeometry > Dynamic Attribute Setup Notes > Dynamic Attribute Setup Notes"}, {"id": "e0d273ed325f4f4b", "url": "https://threejs.org/manual/en/custom-buffergeometry.html", "page_title": "Custom BufferGeometry", "page_type": "guide", "page_summary": "A guide on how to use BufferGeometry directly in three.js to create custom geometry, including building a cube from parallel arrays, using indices to reduce duplication, working with TypedArrays, and dynamically updating vertex data.", "heading": "Conclusion", "content": "Page: Custom BufferGeometry\nSection: Conclusion\n\nI hope these were useful examples of how to use BufferGeometry directly to make your own geometry and how to dynamically update the contents of a BufferAttribute.", "code_blocks": [], "chunk_position": 32, "heading_path": "Conclusion > Conclusion", "breadcrumbs": "Custom BufferGeometry > Conclusion > Conclusion"}, {"id": "4873da97a8c02825", "url": "https://threejs.org/manual/en/physics.html", "page_title": "Physics", "page_type": "guide", "page_summary": "An overview of how to integrate physics engines into three.js projects, covering three main approaches: using the official physics addons, third-party JS/TS libraries, and WASM-based engines.", "heading": "Physics", "content": "Page: Physics\nSection: Physics\n\nPhysics engines allow you to simulate physical phenomena like gravity, collisions, and forces within your 3D capabilities. In a typical three.js scene, objects are moved by directly modifying their position or rotation. When using a physics engine, however, you create a parallel physics world where bodies react to forces and collisions. You then synchronize the three.js meshes with these physics bodies on every frame, creating the illusion of a physically simulated environment.\n\nIt should be noted that the physics engine does not necessarily have to be updated every frame. Usually, to keep experiences consistent, physics are updated at fixed time steps. For instance, it could be that we are running the game loop at 60fps but the physics engine at 30fps (that is 1/30=3.33ms) while updating the three.js meshes' transforms (e.g., positions and rotations) with the most recent state from the physics engine.\n\nPhysics simulations are particularly useful for games, interactive visualizations, and any application requiring realistic object behavior, such as objects falling, bouncing, or sliding.", "code_blocks": [], "chunk_position": 33, "heading_path": "Physics > Physics", "breadcrumbs": "Physics > Physics > Physics"}, {"id": "5d9b21f0c1267de8", "url": "https://threejs.org/manual/en/physics.html", "page_title": "Physics", "page_type": "guide", "page_summary": "An overview of how to integrate physics engines into three.js projects, covering three main approaches: using the official physics addons, third-party JS/TS libraries, and WASM-based engines.", "heading": "Integration Approaches", "content": "Page: Physics\nSection: Integration Approaches\n\nThere are three main ways to integrate a physics engine into a three.js project:", "code_blocks": [], "chunk_position": 33, "heading_path": "Integration Approaches > Integration Approaches", "breadcrumbs": "Physics > Integration Approaches > Integration Approaches"}, {"id": "0f7098b34d8af10c", "url": "https://threejs.org/manual/en/physics.html", "page_title": "Physics", "page_type": "guide", "page_summary": "An overview of how to integrate physics engines into three.js projects, covering three main approaches: using the official physics addons, third-party JS/TS libraries, and WASM-based engines.", "heading": "1. Using Three.js Physics Addons", "content": "Page: Physics\nSection: 1. Using Three.js Physics Addons\n\nThree.js provides wrapper classes for several popular physics engines in the *examples/jsm/physics* directory. These addons simplify the setup process by handling the initialization of the physics world and the synchronization of meshes.\n\nAvailable addons include:\n\n- **AmmoPhysics:** A wrapper for Ammo.js (Bullet Physics).\n- **JoltPhysics:** A wrapper for Jolt Physics.\n- **RapierPhysics:** A wrapper for Rapier.\n\nThese addons effectively hide much of the complexity of the underlying engines. For standard use cases, they offer a very quick way to get started.\n\n###### Examples\n\n- [physics / ammo / instancing](https://threejs.org/examples/physics_ammo_instancing.html)\n- [physics / jolt / instancing](https://threejs.org/examples/physics_jolt_instancing.html)\n- [physics / rapier / instancing](https://threejs.org/examples/physics_rapier_instancing.html)", "code_blocks": [], "chunk_position": 33, "heading_path": "1. Using Three.js Physics Addons > 1. Using Three.js Physics Addons", "breadcrumbs": "Physics > 1. Using Three.js Physics Addons > 1. Using Three.js Physics Addons"}, {"id": "adcdeadda13e4ea3", "url": "https://threejs.org/manual/en/physics.html", "page_title": "Physics", "page_type": "guide", "page_summary": "An overview of how to integrate physics engines into three.js projects, covering three main approaches: using the official physics addons, third-party JS/TS libraries, and WASM-based engines.", "heading": "2. Using 3rd-Party Physics JS/TS Libraries", "content": "Page: Physics\nSection: 2. Using 3rd-Party Physics JS/TS Libraries\n\nMany physics engines are written directly in JavaScript or TypeScript and are designed to work easily with the web ecosystem. Libraries like **cannon-es** are popular choices because they are lightweight and easy to integrate specifically with three.js.\n\nWhen using these libraries, you instantiate the physics world and bodies yourself, then manually copy the position and quaternion from the physics body to the three.js mesh in your animation loop.\n\n###### Projects\n\n- **[cannon-es](https://github.com/pmndrs/cannon-es)**: 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).\n- **[cannon.js](https://github.com/schteppe/cannon.js)**: 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.\n- **[phy](https://github.com/lo-th/phy)**: Physics engine for three.js purely written in JavaScript. Under an MIT license. Latest commit. Currently maintained.\n- **[Oimo.js](https://github.com/lo-th/Oimo.js)**: 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).\n\nIt 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\n- **[Physijs](https://github.com/chandlerprall/Physijs)**: 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).\n- **[enable3d](https://github.com/enable3d/enable3d)**: 3D physics framework for three.js built on top of ammo.js. Under LGPL-3.0 license. Apparently maintained.", "code_blocks": [], "chunk_position": 33, "heading_path": "2. Using 3rd-Party Physics JS/TS Libraries > 2. Using 3rd-Party Physics JS/TS Libraries", "breadcrumbs": "Physics > 2. Using 3rd-Party Physics JS/TS Libraries > 2. Using 3rd-Party Physics JS/TS Libraries"}, {"id": "a16f0e4c350928d2", "url": "https://threejs.org/manual/en/physics.html", "page_title": "Physics", "page_type": "guide", "page_summary": "An overview of how to integrate physics engines into three.js projects, covering three main approaches: using the official physics addons, third-party JS/TS libraries, and WASM-based engines.", "heading": "3. Importing WASM-based Engines", "content": "Page: Physics\nSection: 3. Importing WASM-based Engines\n\nFor maximum performance, stability, and precision, especially with complex simulations, you can use physics engines written in C++ or Rust (or any other language that supports WASM) that have been compiled to WebAssembly (WASM). Engines like **Ammo.js** (a port of Bullet Physics) and **Rapier** fall into this category.\n\nWhile this approach offers the most features and best performance, it often requires more setup code to handle the WASM memory management and interaction with the physics API directly.\n\n###### Examples\n\n- [physics / ammo / break](https://threejs.org/examples/physics_ammo_break.html)\n- [physics / ammo / cloth](https://threejs.org/examples/physics_ammo_cloth.html)\n- [physics / ammo / rope](https://threejs.org/examples/physics_ammo_rope.html)\n- [physics / ammo / terrain](https://threejs.org/examples/physics_ammo_terrain.html)\n- [physics / ammo / volume](https://threejs.org/examples/physics_ammo_volume.html)\n\n###### Projects\n\n- **[JoltPhysics](https://github.com/jrouwe/JoltPhysics)**: 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.\n- **[PhysX](https://github.com/NVIDIA-Omniverse/PhysX)**: Industry-standard realtime 3D physics engine provided by NVIDIA. Under BSD-3-Clause license. Actively maintained and very stable.\n- **[Rapier](https://github.com/dimforge/rapier)**: 2D and 3D physics engines focused on performance. Written in Rust. Under an MIT license. Actively maintained.\n- **[Bullet](https://github.com/bulletphysics/bullet3)**: 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.\n\nSome of these multi-platform 3D physics engines have a ready-to-use WASM port, including:\n\n- **[JoltPhysics.js](https://github.com/jrouwe/JoltPhysics.js)**: Port of JoltPhysics to JavaScript using Emscripten. Under MIT license. Currently maintained.\n- **[physx-js-webidl](https://github.com/fabmax/physx-js-webidl)**: Javascript WASM bindings for Nvidia PhysX. Under MIT license. Currently maintained.\n- **[Rapier.js](https://github.com/dimforge/rapier.js)**: Official JavaScript bindings for the Rapier physics engine. Under Apache-2.0 license. Actively maintained.\n- **[Ammo.js](https://github.com/kripken/ammo.js)**: 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.", "code_blocks": [], "chunk_position": 33, "heading_path": "3. Importing WASM-based Engines > 3. Importing WASM-based Engines", "breadcrumbs": "Physics > 3. Importing WASM-based Engines > 3. Importing WASM-based Engines"}, {"id": "8d66dbc233f6058a", "url": "https://threejs.org/manual/en/rendering-on-demand.html", "page_title": "Rendering on Demand", "page_type": "guide", "page_summary": "A guide explaining how to render Three.js scenes on demand rather than continuously using requestAnimationFrame, to save power and battery on devices where continuous animation is unnecessary.", "heading": "Rendering on Demand", "content": "Page: Rendering on Demand\nSection: Rendering on Demand\n\nThe topic might be obvious to many people but just in case ... most Three.js examples render continuously. In other words they setup a `requestAnimationFrame` loop or \" *rAF loop* \" something like this\n\nFor something that animates this makes sense but what about for something that does not animate? In that case rendering continuously is a waste of the devices power and if the user is on portable device it wastes the user's battery.\n\nThe most obvious way to solve this is to render once at the start and then render only when something changes. Changes include textures or models finally loading, data arriving from some external source, the user adjusting a setting or the camera or giving other relevant input.\n\nLet's take an example from [the article on responsiveness](responsive.html) and modify it to render on demand.\n\nFirst we'll add in the [`OrbitControls`](/docs/#examples/controls/OrbitControls) so there is something that could change that we can render in response to.\n\nand set them up\n\nSince we won't be animating the cubes anymore we no longer need to keep track of them\n\nWe can remove the code to animate the cubes and the calls to `requestAnimationFrame`\n\nthen we need to render once\n\nWe need to render anytime the [`OrbitControls`](/docs/#examples/controls/OrbitControls) change the camera settings. Fortunately the [`OrbitControls`](/docs/#examples/controls/OrbitControls) dispatch a `change` event anytime something changes.\n\nWe also need to handle the case where the user resizes the window. That was handled automatically before since we were rendering continuously but now what we are not we need to render when the window changes size.\n\nAnd with that we get something that renders on demand.\n\n[click here to open in a separate window](/manual/examples/render-on-demand.html)\n\nThe [`OrbitControls`](/docs/#examples/controls/OrbitControls) have options to add a kind of inertia to make them feel less stiff. We can enable this by setting the `enableDamping` property to true.\n\nWith `enableDamping` on we need to call `controls.update` in our render function so that the [`OrbitControls`](/docs/#examples/controls/OrbitControls) can continue to give us new camera settings as they smooth out the movement. But, that means we can't call `render` directly from the `change` event because we'll end up in an infinite loop. The controls will send us a `change` event and call `render`, `render` will call `controls.update`. `controls.update` will send another `change` event.\n\nWe can fix that by using `requestAnimationFrame` to call `render` but we need to make sure we only ask for a new frame if one has not already been requested which we can do by keeping a variable that tracks if we've already requested a frame.\n\nWe should probably also use `requestRenderIfNotRequested` for resizing as well\n\nIt might be hard to see the difference. Try clicking on the example below and use the arrow keys to move around or dragging to spin. Then try clicking on the example above and do the same thing and you should be able to tell the difference. The one above snaps when you press an arrow key or drag, the one below slides.\n\n[click here to open in a separate window](/manual/examples/render-on-demand-w-damping.html)\n\nLet's also add a simple lil-gui GUI and make its changes render on demand.\n\nLet's allow setting the color and x scale of each cube. To be able to set the color we'll use the `ColorGUIHelper` we created in the [article on lights](lights.html).\n\nFirst we need to create a GUI\n\nand then for each cube we'll create a folder and add 2 controls, one for `material.color` and another for `cube.scale.x`.\n\nYou can see above lil-gui controls have an `onChange` method that you can pass a callback to be called when the GUI changes a value. In our case we just need it to call `requestRenderIfNotRequested`. The call to `folder.open` makes the folder start expanded.\n\n[click here to open in a separate window](/manual/examples/render-on-demand-w-gui.html)\n\nI hope this gives some idea of how to make three.js render on demand instead of continuously. Apps/pages that render three.js on demand are not as common as most pages using three.js are either games or 3d animated art but examples of pages that might be better rendering on demand would be say a map viewer, a 3d editor, a 3d graph generator, a product catalog, etc...", "code_blocks": [{"language": "javascript", "code": "function render() {\n  ...\n  requestAnimationFrame(render);\n}\nrequestAnimationFrame(render);", "filename": ""}, {"language": "javascript", "code": "import * as THREE from 'three';\n+import {OrbitControls} from 'three/addons/controls/OrbitControls.js';", "filename": ""}, {"language": "javascript", "code": "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();", "filename": ""}, {"language": "javascript", "code": "-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);", "filename": ""}, {"language": "javascript", "code": "-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) => {\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);", "filename": ""}, {"language": "javascript", "code": "render();", "filename": ""}, {"language": "javascript", "code": "controls.addEventListener('change', render);", "filename": ""}, {"language": "javascript", "code": "window.addEventListener('resize', render);", "filename": ""}, {"language": "javascript", "code": "controls.enableDamping = true;", "filename": ""}, {"language": "javascript", "code": "+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);", "filename": ""}, {"language": "javascript", "code": "-window.addEventListener('resize', render);\n+window.addEventListener('resize', requestRenderIfNotRequested);", "filename": ""}, {"language": "javascript", "code": "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';", "filename": ""}, {"language": "javascript", "code": "const gui = new GUI();", "filename": ""}, {"language": "javascript", "code": "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(Cube${x});\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}", "filename": ""}], "chunk_position": 34, "heading_path": "Rendering on Demand > Rendering on Demand", "breadcrumbs": "Rendering on Demand > Rendering on Demand > Rendering on Demand"}, {"id": "a1feeadfb1c2eb4b", "url": "https://threejs.org/manual/en/debugging-glsl.html", "page_title": "Debugging - GLSL", "page_type": "guide", "page_summary": "A guide on debugging GLSL shaders in Three.js, offering practical tips for troubleshooting fragment and vertex shader issues through visualization techniques, simplified shaders, and developer tools.", "heading": "Debugging - GLSL", "content": "Page: Debugging - GLSL\nSection: Debugging - GLSL\n\nThis site so far does not teach GLSL just like it does not teach JavaScript. Those are really large topics. If you want to learn GLSL consider checking out these articles as a starting place.\n\nIf you already know GLSL then here are a few tips for debugging.\n\nWhen I'm making a new GLSL shader and nothing appears generally the first thing I do is change the fragment shader to return a solid color. For example at the very bottom of the shader I might put\n\n```\nvoid main() {\n\n  ...\n\n  gl_FragColor = vec4(1, 0, 0, 1);  // red\n}\n```\n\nIf I see the object I was trying to draw then I know the issue is related to my fragment shader. It could be anything like bad textures, uninitialized uniforms, uniforms with the wrong values but at least I have a direction to look.\n\nTo test some of those I might start trying to draw some of the inputs. For example if I'm using normals in the fragment shader then I might add\n\n```\ngl_FragColor = vec4(vNormal * 0.5 + 0.5, 1);\n```\n\nNormals go from -1 to +1 so by multiplying by 0.5 and adding 0.5 we get values that go from 0.0 to 1.0 which makes them useful for colors.\n\nTry this with some things you know work and you'll start getting an idea of what normals *normally* look like. If your normals don't look normal then you have some clue where to look. If you're manipulating normals in the fragments shader you can use the same technique to draw the result of that manipulation.\n\nSimilarly if we're using textures there will be texture coordinates and we can draw them with something like\n\n```\ngl_FragColor = vec4(fract(vUv), 0, 1);\n```\n\nThe `fract` is there in case we're using texture coordinates that go outside the 0 to 1 range. This is common if `texture.repeat` is set to something greater than 1.\n\nYou can do similar things for all values in your fragment shader. Figure out what their range is likely to be, add some code to set `gl_FragColor` with that range scaled to 0.0 to 1.0\n\nTo check textures try a [`CanvasTexture`](/docs/#api/en/textures/CanvasTexture) or a [`DataTexture`](/docs/#api/en/textures/DataTexture) that you know works.\n\nConversely, if after setting `gl_FragColor` to red I still see nothing then I have a hint my issue might be in the direction of the things related to the vertex shader. Some matrices might be wrong or my attributes might have bad data or be setup incorrectly.\n\nI'd first look at the matrices. I might put a breakpoint right after my call to `renderer.render(scene, camera)` and then start expanding things in the inspector. Is the camera's world matrix and projection matrix at least not full of `NaN`s? Expanding the scene and looking at its `children` I'd check that the world matrices look reasonable (no `NaN`s) and last 4 values of each matrix look reasonable for my scene. If I expect my scene to be 50x50x50 units and some matrix shows 552352623.123 clearly something is wrong there.\n\nJust like we did for the fragment shader we can also draw values from the vertex shader by passing them to the fragment shader. Declare a varying in both and pass the value you're not sure is correct. In fact if my shader use using normals I'll change the fragment shader to display them like is mentioned above and then just set `vNormal` to the value I want to display but scaled so the values go from 0.0 to 1.0. I then look at the results and see if they fit my expectations.\n\nAnother good thing to do is use a simpler shader. Can you draw your data with [`MeshBasicMaterial`](/docs/#api/en/materials/MeshBasicMaterial)? If you can then try it and make sure it shows up as expected.\n\nIf not what's the simplest vertex shader that will let you visualize your geometry? Usually it's as simple as\n\n```\ngl_Position = projection * modelView * vec4(position.xyz, 1);\n```\n\nIf that works start adding in your changes a little at a time.\n\nYet another thing you can do is use the Shader Editor extension for Chrome or similar for other browsers. It's a great way to look at how other shaders are working. It's also good as you can make some of the changes suggested above live while the code is running.", "code_blocks": [{"language": "glsl", "code": "void main() {\n\n  ...\n\n  gl_FragColor = vec4(1, 0, 0, 1);  // red\n}", "filename": ""}, {"language": "glsl", "code": "gl_FragColor = vec4(vNormal * 0.5 + 0.5, 1);", "filename": ""}, {"language": "glsl", "code": "gl_FragColor = vec4(fract(vUv), 0, 1);", "filename": ""}, {"language": "glsl", "code": "gl_Position = projection * modelView * vec4(position.xyz, 1);", "filename": ""}], "chunk_position": 35, "heading_path": "Debugging - GLSL > Debugging - GLSL", "breadcrumbs": "Debugging - GLSL > Debugging - GLSL > Debugging - GLSL"}, {"id": "ac482d7859001d5d", "url": "https://threejs.org/manual/en/tips.html", "page_title": "Tips", "page_type": "guide", "page_summary": "A collection of small tips and solutions for common issues encountered when using three.js, including taking screenshots, keyboard input, transparency, and background animations.", "heading": "Taking A Screenshot of the Canvas", "content": "Page: Tips\nSection: Taking A Screenshot of the Canvas\n\nIn the browser there are effectively 2 functions that will take a screenshot. The old one\n[`canvas.toDataURL`](https://developer.mozilla.org/en-US/docs/Web/API/HTMLCanvasElement/toDataURL)\nand the new better one\n[`canvas.toBlob`](https://developer.mozilla.org/en-US/docs/Web/API/HTMLCanvasElement/toBlob)\n\nSo you'd think it would be easy to take a screenshot by just adding some code like\n\n[click here to open in a separate window](/manual/examples/tips-screenshot-bad.html)\n\nWhen I tried it I got this screenshot\n\nYes, it's just a black image.\n\nIt's possible it worked for you depending on your browser/OS but in general\nit's not likely to work.\n\nThe 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.\n\nThe solution is to call your rendering code just before capturing.\n\nIn our code we need to adjust a few things. First let's separate\nout the rendering code.\n\nNow that `render` is only concerned with actually rendering\nwe can call it just before capturing the canvas.\n\nAnd now it should work.\n\n[click here to open in a separate window](/manual/examples/tips-screenshot-good.html)\n\nFor a different solution see the next item.", "code_blocks": [{"language": "html", "code": "<canvas id=\"c\"></canvas>\n+<button id=\"screenshot\" type=\"button\">Save...</button>", "filename": ""}, {"language": "javascript", "code": "const elem = document.querySelector('#screenshot');\nelem.addEventListener('click', () => {\n  canvas.toBlob((blob) => {\n    saveBlob(blob, `screencapture-${canvas.width}x${canvas.height}.png`);\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}());", "filename": ""}, {"language": "javascript", "code": "+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) => {\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);", "filename": ""}, {"language": "javascript", "code": "const elem = document.querySelector('#screenshot');\nelem.addEventListener('click', () => {\n+  render();\n  canvas.toBlob((blob) => {\n    saveBlob(blob, `screencapture-${canvas.width}x${canvas.height}.png`);\n  });\n});", "filename": ""}], "chunk_position": 36, "heading_path": "Taking A Screenshot of the Canvas > Taking A Screenshot of the Canvas", "breadcrumbs": "Tips > Taking A Screenshot of the Canvas > Taking A Screenshot of the Canvas"}, {"id": "1db8764aa042d882", "url": "https://threejs.org/manual/en/tips.html", "page_title": "Tips", "page_type": "guide", "page_summary": "A collection of small tips and solutions for common issues encountered when using three.js, including taking screenshots, keyboard input, transparency, and background animations.", "heading": "Preventing the canvas being cleared", "content": "Page: Tips\nSection: Preventing the canvas being cleared\n\nLet's say you wanted to let the user paint with an animated\nobject. You need to pass in `preserveDrawingBuffer: true` when\nyou create the [`WebGLRenderer`](/docs/#api/en/renderers/WebGLRenderer). This prevents the browser from\nclearing the canvas. You also need to tell three.js not to clear\nthe canvas as well.\n\n[click here to open in a separate window](/manual/examples/tips-preservedrawingbuffer.html)\n\nNote 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.\n\nIf you really wanted to make a drawing program you'd\n[render to a texture using a render target](rendertargets.html).", "code_blocks": [{"language": "javascript", "code": "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;", "filename": ""}], "chunk_position": 36, "heading_path": "Preventing the canvas being cleared > Preventing the canvas being cleared", "breadcrumbs": "Tips > Preventing the canvas being cleared > Preventing the canvas being cleared"}, {"id": "44725311c3f26ce7", "url": "https://threejs.org/manual/en/tips.html", "page_title": "Tips", "page_type": "guide", "page_summary": "A collection of small tips and solutions for common issues encountered when using three.js, including taking screenshots, keyboard input, transparency, and background animations.", "heading": "Getting Keyboard Input", "content": "Page: Tips\nSection: Getting Keyboard Input\n\nThroughout these tutorials we've often attached event listeners to the `canvas`.\nWhile many events work, one that does not work by default is keyboard\nevents.\n\nTo get keyboard events, set the [`tabindex`](https://developer.mozilla.org/en-US/docs/Web/API/HTMLElement/tabIndex)\nof the canvas to 0 or more. Eg.\n\nThis ends up causing a new issue though. Anything that has a `tabindex` set\nwill get highlighted when it has the focus. To fix that set its focus CSS outline\nto none\n\nTo demonstrate here are 3 canvases\n\nand some css just for the last canvas\n\nLet's attach the same event listeners to all of them\n\nNotice 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.\n\n[click here to open in a separate window](/manual/examples/tips-tabindex.html)", "code_blocks": [{"language": "html", "code": "<canvas tabindex=\"0\"></canvas>", "filename": ""}, {"language": "css", "code": "canvas:focus {\n  outline:none;\n}", "filename": ""}, {"language": "html", "code": "<canvas id=\"c1\"></canvas>\n<canvas id=\"c2\" tabindex=\"0\"></canvas>\n<canvas id=\"c3\" tabindex=\"1\"></canvas>", "filename": ""}, {"language": "css", "code": "#c3:focus {\n    outline: none;\n}", "filename": ""}, {"language": "javascript", "code": "document.querySelectorAll('canvas').forEach((canvas) => {\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', () => {\n    draw('has focus press a key');\n  });\n\n  canvas.addEventListener('blur', () => {\n    draw('lost focus');\n  });\n\n  canvas.addEventListener('keydown', (e) => {\n    draw(`keyCode: ${e.keyCode}`);\n  });\n});", "filename": ""}], "chunk_position": 36, "heading_path": "Getting Keyboard Input > Getting Keyboard Input", "breadcrumbs": "Tips > Getting Keyboard Input > Getting Keyboard Input"}, {"id": "9722c6a243549d4e", "url": "https://threejs.org/manual/en/tips.html", "page_title": "Tips", "page_type": "guide", "page_summary": "A collection of small tips and solutions for common issues encountered when using three.js, including taking screenshots, keyboard input, transparency, and background animations.", "heading": "Making the Canvas Transparent", "content": "Page: Tips\nSection: Making the Canvas Transparent\n\nBy default THREE.js makes the canvas opaque. If you want the\ncanvas to be transparent pass in [`alpha:true`](/docs/#api/en/renderers/WebGLRenderer#alpha) when you create\nthe [`WebGLRenderer`](/docs/#api/en/renderers/WebGLRenderer)\n\nYou probably also want to tell it that your results are **not** using premultiplied alpha\n\nThree.js defaults to the canvas using\n[`premultipliedAlpha: true`](/docs/#api/en/renderers/WebGLRenderer#premultipliedAlpha) but defaults\nto materials outputting [`premultipliedAlpha: false`](/docs/#api/en/materials/Material#premultipliedAlpha).\n\nIf you'd like a better understanding of when and when not to use premultiplied alpha\nhere's [a good article on it](https://developer.nvidia.com/content/alpha-blending-pre-or-not-pre).\n\nIn any case let's setup a simple example with a transparent canvas.\n\nWe applied the settings above to the example from [the article on responsiveness](responsive.html).\nLet's also make the materials more transparent.\n\nAnd let's add some HTML content\n\nas well as some CSS to put the canvas in front\n\nnote that `pointer-events: none` makes the canvas invisible to the mouse\nand touch events so you can select the text beneath.\n\n[click here to open in a separate window](/manual/examples/tips-transparent-canvas.html)", "code_blocks": [{"language": "javascript", "code": "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+});", "filename": ""}, {"language": "javascript", "code": "const canvas = document.querySelector('#c');\nconst renderer = new THREE.WebGLRenderer({\n  canvas,\n  alpha: true,\n+  premultipliedAlpha: false,\n});", "filename": ""}, {"language": "javascript", "code": "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...", "filename": ""}, {"language": "html", "code": "<body>\n  <canvas id=\"c\"></canvas>\n+  <div id=\"content\">\n+    <div>\n+      <h1>Cubes-R-Us!</h1>\n+      <p>We make the best cubes!</p>\n+    </div>\n+  </div>\n</body>", "filename": ""}, {"language": "css", "code": "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+}", "filename": ""}], "chunk_position": 36, "heading_path": "Making the Canvas Transparent > Making the Canvas Transparent", "breadcrumbs": "Tips > Making the Canvas Transparent > Making the Canvas Transparent"}, {"id": "3edfc3f4842b918e", "url": "https://threejs.org/manual/en/tips.html", "page_title": "Tips", "page_type": "guide", "page_summary": "A collection of small tips and solutions for common issues encountered when using three.js, including taking screenshots, keyboard input, transparency, and background animations.", "heading": "Making your background a three.js animation", "content": "Page: Tips\nSection: Making your background a three.js animation\n\nA common question is how to make a three.js animation be the background of\na webpage.\n\nThere are 2 obvious ways.\n\n- Set the canvas CSS `position` to `fixed` as in\n\nYou can basically see this exact solution on the previous example. Just set `z-index` to -1\nand the cubes will appear behind the text.\n\nA 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.\n\n- Use an `iframe`\n\nThis is the solution used on [the front page of this site](/).\n\nIn your webpage just insert an iframe, for example\n\nThen 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 `border` to `none` since iframes have\na border by default.\n\n[click here to open in a separate window](/manual/examples/tips-html-background.html)", "code_blocks": [{"language": "css", "code": "#c {\n position: fixed;\n left: 0;\n top: 0;\n ...\n}", "filename": ""}, {"language": "html", "code": "<iframe id=\"background\" src=\"responsive.html\">\n<div>\n  Your content goes here.\n</div>", "filename": ""}, {"language": "css", "code": "#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}", "filename": ""}], "chunk_position": 36, "heading_path": "Making your background a three.js animation > Making your background a three.js animation", "breadcrumbs": "Tips > Making your background a three.js animation > Making your background a three.js animation"}, {"id": "8e848757dc3b4605", "url": "https://threejs.org/manual/en/optimize-lots-of-objects.html", "page_title": "Optimize Lots of Objects", "page_type": "guide", "page_summary": "A guide on optimizing three.js performance by merging many separate geometries into a single mesh, using the recreation of a WebGL Globe with demographic data as an example. Covers data loading, initial slow implementation, geometry merging with BufferGeometryUtils, and restoring per-object colors using vertex colors.", "heading": "Optimize Lots of Objects", "content": "Page: Optimize Lots of Objects\nSection: Optimize Lots of Objects\n\nThere are many ways to optimize things for three.js. One way is often referred to as *merging geometry*. Every [`Mesh`](/docs/#api/en/objects/Mesh) you create and three.js represents 1 or more requests by the system to draw something. Drawing 2 things has more overhead than drawing 1 even if the results are the same so one way to optimize is to merge meshes.\n\nLet's show an example of when this is a good solution for an issue. Let's re-create the [WebGL Globe](https://globe.chromeexperiments.com/).\n\nThe first thing we need to do is get some data. The WebGL Globe said the data they use comes from [SEDAC](http://sedac.ciesin.columbia.edu/gpw/). Checking out the site I saw there was [demographic data in a grid format](https://beta.sedac.ciesin.columbia.edu/data/set/gpw-v4-basic-demographic-characteristics-rev10). I downloaded the data at 60 minute resolution. Then I took a look at the data\n\nIt looks like this\n\n```\n 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 ...\n```\n\nThere's a few lines that are like key/value pairs followed by lines with a value per grid point, one line for each row of data points.\n\nTo make sure we understand the data let's try to plot it in 2D.\n\nFirst some code to load the text file\n\n```js\nasync function loadFile(url) {\n  const res = await fetch(url);\n  return res.text();\n}\n```\n\nThe code above returns a `Promise` with the contents of the file at `url`;\n\nThen we need some code to parse the file\n\n```js\nfunction 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) => {\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 > 2) {\n      // more than 2 parts, must be data\n      const values = parts.map((v) => {\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```\n\nThe code above returns an object with all the key/value pairs from the file as well as a `data` property with all the data in one large array and the `min` and `max` values found in the data.\n\nThen we need some code to draw that data\n\n```js\nfunction 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) => {\n    row.forEach((value, lonNdx) => {\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 ${v | 0}px;\n}\n\nfunction hsl(h, s, l) {\n  return hsl(${h * 360 | 0},${s * 100 | 0}%,${l * 100 | 0}%);\n}\n```\n\nAnd finally gluing it all together\n\n```js\nloadFile('resources/data/gpw/gpw_v4_basic_demographic_characteristics_rev10_a000_014mt_2010_cntm_1_deg.asc')\n  .then(parseData)\n  .then(drawData);\n```\n\nGives us this result\n\n[click here to open in a separate window](/manual/examples/gpw-data-viewer.html)\n\nSo that seems to work.\n\nLet's try it in 3D. Starting with the code from [rendering on demand](rendering-on-demand.html) We'll make one box per data in the file.\n\nFirst let's make a simple sphere with a texture of the world. Here's the texture\n\n<img src=\"../examples/resources/images/world.jpg\">\n\nAnd the code to set it up.\n\n```js\n{\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```\n\nNotice the call to `render` when the texture has finished loading. We need this because we're [rendering on demand](rendering-on-demand.html) instead of continuously so we need to render once when the texture is loaded.\n\nThen we need to change the code that drew a dot per data point above to instead make a box per data point.\n\n```js\nfunction 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) => {\n    row.forEach((value, lonNdx) => {\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```\n\nThe code is mostly straight forward from our test drawing code.\n\nWe make one box and adjust its center so it scales away from positive Z. If we didn't do this it would scale from the center but we want them to grow away from the origin.\n\nOf course we could also solve that by parenting the box to more [`THREE.Object3D`](/docs/#api/en/core/Object3D) objects like we covered in [scene graphs](scenegraph.html) but the more nodes we add to a scene graph the slower it gets.\n\nWe also setup this small hierarchy of nodes of `lonHelper`, `latHelper`, and `positionHelper`. We use these objects to compute a position around the sphere were to place the box.\n\nAbove the green bar represents `lonHelper` and is used to rotate toward longitude on the equator. The blue bar represents `latHelper` which is used to rotate to a latitude above or below the equator. The red sphere represents the offset that that `positionHelper` provides.\n\nWe could do all of the math manually to figure out positions on the globe but doing it this way leaves most of the math to the library itself so we don't need to deal with.\n\nFor each data point we create a [`MeshBasicMaterial`](/docs/#api/en/materials/MeshBasicMaterial) and a [`Mesh`](/docs/#api/en/objects/Mesh) and then we ask for the world matrix of the `positionHelper` and apply that to the new [`Mesh`](/docs/#api/en/objects/Mesh). Finally we scale the mesh at its new position.\n\nLike above, we could also have created a `latHelper`, `lonHelper`, and `positionHelper` for every new box but that would be even slower.\n\nThere are up to 360x145 boxes we're going to create. That's up to 52000 boxes. Because some data points are marked as \"NO_DATA\" the actual number of boxes we're going to create is around 19000. If we added 3 extra helper objects per box that would be nearly 80000 scene graph nodes that THREE.js would have to compute positions for. By instead using one set of helpers to just position the meshes we save around 60000 operations.\n\nA note about `lonFudge` and `latFudge`. `lonFudge` is π/2 which is a quarter of a turn. That makes sense. It just means the texture or texture coordinates start at a different offset around the globe. `latFudge` on the other hand I have no idea why it needs to be π * -0.135, that's just an amount that made the boxes line up with the texture.\n\nThe last thing we need to do is call our loader\n\n```js\nloadFile('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```\n\nOnce the data has finished loading and parsing then we need to render at least once since we're [rendering on demand](rendering-on-demand.html).\n\n[click here to open in a separate window](/manual/examples/lots-of-objects-slow.html)\n\nIf you try to rotate the example above by dragging on the sample you'll likely notice it's slow.\n\nWe can check the framerate by [opening the devtools](debugging-javascript.html) and turning on the browser's frame rate meter.\n\nOn my machine I see a framerate under 20fps.\n\nThat doesn't feel very good to me and I suspect many people have slower machines which would make it even worse. We'd better look into optimizing.\n\nFor this particular problem we can merge all the boxes into a single geometry. We're currently drawing around 19000 boxes. By merging them into a single geometry we'd remove 18999 operations.\n\nHere's the new code to merge the boxes into a single geometry.\n\n```js\nfunction addBoxes(file) {\n  const {min, max, data} = file;\n  const range = max - min;\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) => {\n    row.forEach((value, lonNdx) => {\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+      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```\n\nAbove we removed the code that was changing the box geometry's center point and are instead doing it by adding an `originHelper`. Before we were using the same geometry 19000 times. This time we are creating new geometry for every single box and since we are going to use `applyMatrix` to move the vertices of each box geometry we might as well do it once instead of twice.\n\nAt the end we pass an array of all the geometries to `BufferGeometryUtils.mergeGeometries` which will combined all of them into a single mesh.\n\nWe also need to include the `BufferGeometryUtils`\n\n```js\nimport * as BufferGeometryUtils from 'three/addons/utils/BufferGeometryUtils.js';\n```\n\nAnd now, at least on my machine, I get 60 frames per second\n\n[click here to open in a separate window](/manual/examples/lots-of-objects-merged.html)\n\nSo that worked but because it's one mesh we only get one material which means we only get one color where as before we had a different color on each box. We can fix that by using vertex colors.\n\nVertex colors add a color per vertex. By setting all the colors of each vertex of each box to specific colors every box will have a different color.\n\n```js\n+const color = new THREE.Color();\n\nconst lonFudge = Math.PI * .5;\nconst latFudge = Math.PI * -0.135;\nconst geometries = [];\ndata.forEach((row, latNdx) => {\n  row.forEach((value, lonNdx) => {\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 => 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) => {\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```\n\nThe code above looks up the number or vertices needed by getting the `position` attribute from the geometry. We then create a `Uint8Array` to put the colors in. It then adds that as an attribute by calling `geometry.setAttribute`.\n\nLastly we need to tell three.js to use the vertex colors.\n\n```js\nconst 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```\n\nAnd with that we get our colors back\n\n[click here to open in a separate window](/manual/examples/lots-of-objects-merged-vertexcolors.html)\n\nMerging geometry is a common optimization technique. For example rather than 100 trees you might merge the trees into 1 geometry, a pile of individual rocks into a single geometry of rocks, a picket fence from individual pickets into one fence mesh. Another example in Minecraft it doesn't likely draw each cube individually but rather creates groups of merged cubes and also selectively removing faces that are never visible.\n\nThe problem with making everything one mesh though is it's no longer easy to move any part that was previously separate. Depending on our use case though there are creative solutions. We'll explore one in [another article](optimize-lots-of-objects-animated.html).", "code_blocks": [{"language": "text", "code": "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 ...", "filename": ""}, {"language": "javascript", "code": "async function loadFile(url) {\n  const res = await fetch(url);\n  return res.text();\n}", "filename": ""}, {"language": "javascript", "code": "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) => {\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 > 2) {\n      // more than 2 parts, must be data\n      const values = parts.map((v) => {\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}", "filename": ""}, {"language": "javascript", "code": "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) => {\n    row.forEach((value, lonNdx) => {\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 ${v | 0}px;\n}\n\nfunction hsl(h, s, l) {\n  return hsl(${h * 360 | 0},${s * 100 | 0}%,${l * 100 | 0}%);\n}", "filename": ""}, {"language": "javascript", "code": "loadFile('resources/data/gpw/gpw_v4_basic_demographic_characteristics_rev10_a000_014mt_2010_cntm_1_deg.asc')\n  .then(parseData)\n  .then(drawData);", "filename": ""}, {"language": "javascript", "code": "{\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}", "filename": ""}, {"language": "javascript", "code": "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) => {\n    row.forEach((value, lonNdx) => {\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}", "filename": ""}, {"language": "javascript", "code": "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);", "filename": ""}, {"language": "javascript", "code": "function addBoxes(file) {\n  const {min, max, data} = file;\n  const range = max - min;\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) => {\n    row.forEach((value, lonNdx) => {\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+      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}", "filename": ""}, {"language": "javascript", "code": "import * as BufferGeometryUtils from 'three/addons/utils/BufferGeometryUtils.js';", "filename": ""}, {"language": "javascript", "code": "+const color = new THREE.Color();\n\nconst lonFudge = Math.PI * .5;\nconst latFudge = Math.PI * -0.135;\nconst geometries = [];\ndata.forEach((row, latNdx) => {\n  row.forEach((value, lonNdx) => {\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 => 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) => {\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});", "filename": ""}, {"language": "javascript", "code": "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);", "filename": ""}], "chunk_position": 37, "heading_path": "Optimize Lots of Objects > Optimize Lots of Objects", "breadcrumbs": "Optimize Lots of Objects > Optimize Lots of Objects > Optimize Lots of Objects"}, {"id": "d2733e5788f85e76", "url": "https://threejs.org/manual/en/optimize-lots-of-objects-animated.html", "page_title": "Optimize Lots of Objects Animated", "page_type": "guide", "page_summary": "A continuation of the article about optimizing lots of objects, this tutorial covers how to graph and animate between multiple sets of data using morphtargets in Three.js, including loading multiple data files, generating comparison datasets, building a UI to switch between them, and creating smooth transitions with a TweenManager.", "heading": "Optimize Lots of Objects Animated", "content": "Page: Optimize Lots of Objects Animated\nSection: Optimize Lots of Objects Animated\n\nThis article is a continuation of an article about optimizing lots of objects. If you haven't read that yet please read it before proceeding.\n\nIn the previous article we merged around 19000 cubes into a single geometry. This had the advantage that it optimized our drawing of 19000 cubes but it had the disadvantage of make it harder to move any individual cube.\n\nDepending on what we are trying to accomplish there are different solutions. In this case let's graph multiple sets of data and animate between the sets.\n\nThe first thing we need to do is get multiple sets of data. Ideally we'd probably pre-process data offline but in this case let's load 2 sets of data and generate 2 more\n\nHere's our old loading code\n\nThe code above will load all the files in `fileInfos` and when done each object in `fileInfos` will have a `file` property with the loaded file. `name` and `hueRange` we'll use later. `name` will be for a UI field. `hueRange` will be used to choose a range of hues to map over.\n\nThe two files above are apparently the number of men per area and the number of women per area as of 2010. Note, I have no idea if this data is correct but it's not important really. The important part is showing different sets of data.\n\nLet's generate 2 more sets of data. One being the places where the number men are greater than the number of women and vice versa, the places where the number of women are greater than the number of men.\n\nThe first thing let's write a function that given a 2 dimensional array of arrays like we had before will map over it to generate a new 2 dimensional array of arrays\n\nLike the normal `Array.map` function the `mapValues` function calls a function `fn` for each value in the array of arrays. It passes it the value and both the row and column indices.\n\nNow let's make some code to generate a new file that is a comparison between 2 files\n\nThe code above uses `mapValues` to generate a new set of data that is a comparison based on the `compareFn` function passed in. It also tracks the `min` and `max` comparison results. Finally it makes a new file with all the same properties as `baseFile` except with a new `min`, `max` and `data`.\n\nThen let's use that to make 2 new sets of data\n\nNow let's generate a UI to select between these sets of data. First we need some UI html\n\nand some CSS to make it appear in the top left area\n\nThen we can go over each file and generate a set of merged boxes per set of data and an element which when hovered over will show that set and hide all others.\n\nThe one more change we need from the previous example is we need to make `addBoxes` take a `hueRange`\n\nand with that we should be able to show 4 sets of data. Hover the mouse over the labels or touch them to switch sets\n\nNote, there are a few strange data points that really stick out. I wonder what's up with those!??! In any case how do we animate between these 4 sets of data.\n\nLots of ideas.\n\n- Just fade between them using `Material.opacity`\n\nThe problem with this solution is the cubes perfectly overlap which means there will be z-fighting issues. It's possible we could fix that by changing the depth function and using blending. We should probably look into it.\n\n- Scale up the set we want to see and scale down the other sets\n\nBecause all the boxes have their origin at the center of the planet if we scale them below 1.0 they will sink into the planet. At first that sounds like a good idea but the issue is all the low height boxes will disappear almost immediately and not be replaced until the new data set scales up to 1.0. This makes the transition not very pleasant. We could maybe fix that with a fancy custom shader.\n\n- Use Morphtargets\n\nMorphtargets are a way were we supply multiple values for each vertex in the geometry and morph or lerp (linear interpolate) between them. Morphtargets are most commonly used for facial animation of 3D characters but that's not their only use.\n\nLet's try morphtargets.\n\nWe'll still make a geometry for each set of data but we'll then extract the `position` attribute from each one and use them as morphtargets.\n\nFirst let's change `addBoxes` to just make and return the merged geometry.\n\nThere's one more thing we need to do here though. Morphtargets are required to all have exactly the same number of vertices. Vertex #123 in one target needs have a corresponding Vertex #123 in all other targets. But, as it is now different data sets might have some data points with no data so no box will be generated for that point which would mean no corresponding vertices for another set. So, we need to check across all data sets and either always generate something if there is data in any set or, generate nothing if there is data missing in any set. Let's do the latter.\n\nNow we'll change the code that was calling `addBoxes` to use `makeBoxes` and setup morphtargets\n\nAbove we make geometry for each data set, use the first one as the base, then get a `position` attribute from each geometry and add it as a morphtarget to the base geometry for `position`.\n\nNow we need to change how we're showing and hiding the various data sets. Instead of showing or hiding a mesh we need to change the influence of the morphtargets. For the data set we want to see we need to have an influence of 1 and for all the ones we don't want to see to we need to have an influence of 0.\n\nWe could just set them to 0 or 1 directly but if we did that we wouldn't see any animation, it would just snap which would be no different than what we already have. We could also write some custom animation code which would be easy but because the original webgl globe uses an animation library let's use the same one here.\n\nWe need to include the library\n\nAnd then create a `Tween` to animate the influences.\n\nWe're also suppose to call `TWEEN.update` every frame inside our render loop but that points out a problem. \"tween.js\" is designed for continuous rendering but we are rendering on demand. We could switch to continuous rendering but it's sometimes nice to only render on demand as it well stop using the user's power when nothing is happening so let's see if we can make it animate on demand.\n\nWe'll make a `TweenManager` to help. We'll use it to create the `Tween`s and track them. It will have an `update` method that will return `true` if we need to call it again and `false` if all the animations are finished.\n\nTo use it we'll create one\n\nWe'll use it to create our `Tween`s.\n\nThen we'll update our render loop to update the tweens and keep rendering if there are still animations running.\n\nAnd with that we should be animating between data sets.\n\nI hope going through this was helpful. Using morphtargets is a common technique to move lots of objects. As an example we could give every cube a random place in another target and morph from that to their first positions on the globe. That might be a cool way to introduce the globe.\n\nNext you might be interested in adding labels to a globe which is covered in Aligning HTML Elements to 3D.\n\nNote: We could try to just graph percent of men or percent of women or the raw difference but based on how we are displaying the info, cubes that grow from the surface of the earth, we'd prefer most cubes to be low. If we used one of these other comparisons most cubes would be about 1/2 their maximum height which would not make a good visualization. Feel free to change the `amountGreaterThan` from `Math.max(a - b, 0)` to something like `(a - b)` \"raw difference\" or `a / (a + b)` \"percent\" and you'll see what I mean.", "code_blocks": [{"language": "javascript", "code": "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);", "filename": ""}, {"language": "javascript", "code": "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();", "filename": ""}, {"language": "javascript", "code": "function mapValues(data, fn) {\n  return data.map((row, rowNdx) => {\n    return row.map((value, colNdx) => {\n      return fn(value, rowNdx, colNdx);\n    });\n  });\n}", "filename": ""}, {"language": "javascript", "code": "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) => {\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}", "filename": ""}, {"language": "javascript", "code": "{\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: '>50%men',\n    hueRange: [0.6, 1.1],\n    file: makeDiffFile(menFile, womenFile, (men, women) => {\n      return amountGreaterThan(men, women);\n    }),\n  });\n  fileInfos.push({\n    name: '>50% women',\n    hueRange: [0.0, 0.4],\n    file: makeDiffFile(womenFile, menFile, (women, men) => {\n      return amountGreaterThan(women, men);\n    }),\n  });\n}", "filename": ""}, {"language": "html", "code": "<body>\n  <canvas id=\"c\"></canvas>\n+  <div id=\"ui\"></div>\n</body>", "filename": ""}, {"language": "css", "code": "#ui {\n  position: absolute;\n  left: 1em;\n  top: 1em;\n}\n#ui>div {\n  font-size: 20pt;\n  padding: 1em;\n  display: inline-block;\n}\n#ui>div.selected {\n  color: red;\n}", "filename": ""}, {"language": "javascript", "code": "// show the selected data, hide the rest\nfunction showFileInfo(fileInfos, fileInfo) {\n  fileInfos.forEach((info) => {\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) => {\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', () => {\n    showFileInfo(fileInfos, info);\n  });\n});\n// show the first set of data\nshowFileInfo(fileInfos, fileInfos[0]);", "filename": ""}, {"language": "javascript", "code": "-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  ...", "filename": ""}, {"language": "javascript", "code": "-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}", "filename": ""}, {"language": "javascript", "code": "+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) => {\n    row.forEach((value, lonNdx) => {\n+      if (dataMissingInAnySet(fileInfos, latNdx, lonNdx)) {\n+        return;\n+      }\n      const amount = (value - min) / range;\n\n  ...", "filename": ""}, {"language": "javascript", "code": "+// make geometry for each data set\n+const geometries = fileInfos.map((info) => {\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) => {\n+  const attribute = geometry.getAttribute('position');\n+  const name = target${ndx};\n+  attribute.name = name;\n+  return attribute;\n+});\n+baseGeometry.morphAttributes.color = geometries.map((geometry, ndx) => {\n+  const attribute = geometry.getAttribute('color');\n+  const name = target${ndx};\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) => {\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]);", "filename": ""}, {"language": "javascript", "code": "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';", "filename": ""}, {"language": "javascript", "code": "// show the selected data, hide the rest\nfunction showFileInfo(fileInfos, fileInfo) {\n+  const targets = {};\n-  fileInfos.forEach((info) => {\n+  fileInfos.forEach((info, i) => {\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}", "filename": ""}, {"language": "javascript", "code": "class TweenManger {\n  constructor() {\n    this.numTweensRunning = 0;\n  }\n  _handleComplete() {\n    --this.numTweensRunning;\n    console.assert(this.numTweensRunning >= 0);\n  }\n  createTween(targetObject) {\n    const self = this;\n    ++this.numTweensRunning;\n    let userCompleteFn = () => {};\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) => {\n      userCompleteFn = fn;\n      return tween;\n    };\n    return tween;\n  }\n  update() {\n    TWEEN.update();\n    return this.numTweensRunning > 0;\n  }\n}", "filename": ""}, {"language": "javascript", "code": "function main() {\n  const canvas = document.querySelector('#c');\n  const renderer = new THREE.WebGLRenderer({antialias: true, canvas});\n+  const tweenManager = new TweenManger();\n\n  ...", "filename": ""}, {"language": "javascript", "code": "// show the selected data, hide the rest\nfunction showFileInfo(fileInfos, fileInfo) {\n  const targets = {};\n  fileInfos.forEach((info, i) => {\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}", "filename": ""}, {"language": "javascript", "code": "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();", "filename": ""}], "chunk_position": 38, "heading_path": "Optimize Lots of Objects Animated > Optimize Lots of Objects Animated", "breadcrumbs": "Optimize Lots of Objects Animated > Optimize Lots of Objects Animated > Optimize Lots of Objects Animated"}, {"id": "046d44bd05a581a8", "url": "https://threejs.org/manual/en/offscreencanvas.html", "page_title": "OffscreenCanvas", "page_type": "guide", "page_summary": "A tutorial on using OffscreenCanvas with Three.js to offload rendering work to web workers, including basic setup, picking, OrbitControls integration, and fallback for browsers without OffscreenCanvas support.", "heading": "OffscreenCanvas", "content": "Page: OffscreenCanvas\nSection: OffscreenCanvas\n\n[`OffscreenCanvas`](https://developer.mozilla.org/en-US/docs/Web/API/OffscreenCanvas) is a relatively new browser feature currently only available in Chrome but apparently coming to other browsers. `OffscreenCanvas` allows a web worker to render to a canvas. This is a way to offload heavy work, like rendering a complex 3D scene, to a web worker so as not to slow down the responsiveness of the browser. It also means data is loaded and parsed in the worker so possibly less jank while the page loads.\n\nGetting *started* using it is pretty straight forward. Let's port the 3 spinning cube example from [the article on responsiveness](responsive.html).\n\nWorkers generally have their code separated into another script file whereas most of the examples on this site have had their scripts embedded into the HTML file of the page they are on.\n\nIn our case we'll make a file called `offscreencanvas-cubes.js` and copy all the JavaScript from [the responsive example](responsive.html) into it. We'll then make the changes needed for it to run in a worker.\n\nWe still need some JavaScript in our HTML file. The first thing we need to do there is look up the canvas and then transfer control of that canvas to be offscreen by calling `canvas.transferControlToOffscreen`.", "code_blocks": [{"language": "javascript", "code": "function main() {\n  const canvas = document.querySelector('#c');\n  const offscreen = canvas.transferControlToOffscreen();\n\n  ...", "filename": ""}], "chunk_position": 39, "heading_path": "OffscreenCanvas > OffscreenCanvas", "breadcrumbs": "OffscreenCanvas > OffscreenCanvas > OffscreenCanvas"}, {"id": "4fc1683f21d7d12e", "url": "https://threejs.org/manual/en/offscreencanvas.html", "page_title": "OffscreenCanvas", "page_type": "guide", "page_summary": "A tutorial on using OffscreenCanvas with Three.js to offload rendering work to web workers, including basic setup, picking, OrbitControls integration, and fallback for browsers without OffscreenCanvas support.", "heading": "Starting the Worker", "content": "Page: OffscreenCanvas\nSection: Starting the Worker\n\nWe can then start our worker with `new Worker(pathToScript, {type: 'module'})`. and pass the `offscreen` object to it.", "code_blocks": [{"language": "javascript", "code": "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();", "filename": ""}], "chunk_position": 39, "heading_path": "Starting the Worker > Starting the Worker", "breadcrumbs": "OffscreenCanvas > Starting the Worker > Starting the Worker"}, {"id": "5696f36cb2d9c184", "url": "https://threejs.org/manual/en/offscreencanvas.html", "page_title": "OffscreenCanvas", "page_type": "guide", "page_summary": "A tutorial on using OffscreenCanvas with Three.js to offload rendering work to web workers, including basic setup, picking, OrbitControls integration, and fallback for browsers without OffscreenCanvas support.", "heading": "Worker Message Communication", "content": "Page: OffscreenCanvas\nSection: Worker Message Communication\n\nIt's important to note that workers can't access the `DOM`. They can't look at HTML elements nor can they receive mouse events or keyboard events. The only thing they can generally do is respond to messages sent to them and send messages back to the page.\n\nTo send a message to a worker we call [`worker.postMessage`](https://developer.mozilla.org/en-US/docs/Web/API/Worker/postMessage) and pass it 1 or 2 arguments. The first argument is a JavaScript object that will be [cloned](https://developer.mozilla.org/en-US/docs/Web/API/Web_Workers_API/Structured_clone_algorithm) and sent to the worker. The second argument is an optional array of objects that are part of the first object that we want *transferred* to the worker. These objects will not be cloned. Instead they will be *transferred* and will cease to exist in the main page. Cease to exist is the probably the wrong description, rather they are neutered. Only certain types of objects can be transferred instead of cloned. They include `OffscreenCanvas` so once transferred the `offscreen` object back in the main page is useless.\n\nWorkers receive messages from their `onmessage` handler. The object we passed to `postMessage` arrives on `event.data` passed to the `onmessage` handler on the worker. The code above declares a `type: 'main'` in the object it passes to the worker. This object has no meaning to the browser. It's entirely for our own usage. We'll make a handler that based on `type` calls a different function in the worker. Then we can add functions as needed and easily call them from the main page.", "code_blocks": [{"language": "javascript", "code": "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};", "filename": ""}], "chunk_position": 39, "heading_path": "Worker Message Communication > Worker Message Communication", "breadcrumbs": "OffscreenCanvas > Worker Message Communication > Worker Message Communication"}, {"id": "8362012d52c500cb", "url": "https://threejs.org/manual/en/offscreencanvas.html", "page_title": "OffscreenCanvas", "page_type": "guide", "page_summary": "A tutorial on using OffscreenCanvas with Three.js to offload rendering work to web workers, including basic setup, picking, OrbitControls integration, and fallback for browsers without OffscreenCanvas support.", "heading": "Adapting the Main Function for Worker", "content": "Page: OffscreenCanvas\nSection: Adapting the Main Function for Worker\n\nYou can see above we just look up the handler based on the `type` pass it the `data` that was sent from the main page.\n\nSo now we just need to start changing the `main` we pasted into `offscreencanvas-cubes.js` from [the responsive article](responsive.html).\n\nInstead of looking up the canvas from the DOM we'll receive it from the event data.", "code_blocks": [{"language": "javascript", "code": "-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  ...", "filename": ""}], "chunk_position": 39, "heading_path": "Adapting the Main Function for Worker > Adapting the Main Function for Worker", "breadcrumbs": "OffscreenCanvas > Adapting the Main Function for Worker > Adapting the Main Function for Worker"}, {"id": "6661966a826ff556", "url": "https://threejs.org/manual/en/offscreencanvas.html", "page_title": "OffscreenCanvas", "page_type": "guide", "page_summary": "A tutorial on using OffscreenCanvas with Three.js to offload rendering work to web workers, including basic setup, picking, OrbitControls integration, and fallback for browsers without OffscreenCanvas support.", "heading": "Handling Size Changes", "content": "Page: OffscreenCanvas\nSection: Handling Size Changes\n\nRemembering that workers can't see the DOM at all the first problem we run into is `resizeRendererToDisplaySize` can't look at `canvas.clientWidth` and `canvas.clientHeight` as those are DOM values. Here's the original code", "code_blocks": [{"language": "javascript", "code": "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}", "filename": ""}], "chunk_position": 39, "heading_path": "Handling Size Changes > Handling Size Changes", "breadcrumbs": "OffscreenCanvas > Handling Size Changes > Handling Size Changes"}, {"id": "145606ba39bcc222", "url": "https://threejs.org/manual/en/offscreencanvas.html", "page_title": "OffscreenCanvas", "page_type": "guide", "page_summary": "A tutorial on using OffscreenCanvas with Three.js to offload rendering work to web workers, including basic setup, picking, OrbitControls integration, and fallback for browsers without OffscreenCanvas support.", "heading": "", "content": "Page: OffscreenCanvas\nSection: \n\nInstead we'll need to send sizes as they change to the worker. So, let's add some global state and keep the width and height there.", "code_blocks": [{"language": "javascript", "code": "const state = {\n  width: 300,  // canvas default\n  height: 150,  // canvas default\n};", "filename": ""}], "chunk_position": 39, "heading_path": " > ", "breadcrumbs": "OffscreenCanvas >  > "}, {"id": "5f3c04a1a3e1bb1a", "url": "https://threejs.org/manual/en/offscreencanvas.html", "page_title": "OffscreenCanvas", "page_type": "guide", "page_summary": "A tutorial on using OffscreenCanvas with Three.js to offload rendering work to web workers, including basic setup, picking, OrbitControls integration, and fallback for browsers without OffscreenCanvas support.", "heading": "Then let's add a `'size'` handler to update those values.", "content": "Page: OffscreenCanvas\nSection: Then let's add a `'size'` handler to update those values.\n\n", "code_blocks": [{"language": "javascript", "code": "+function size(data) {\n+  state.width = data.width;\n+  state.height = data.height;\n+}\n\nconst handlers = {\n  main,\n+  size,\n};", "filename": ""}], "chunk_position": 39, "heading_path": "Then let's add a `'size'` handler to update those values. > Then let's add a `'size'` handler to update those values.", "breadcrumbs": "OffscreenCanvas > Then let's add a `'size'` handler to update those values. > Then let's add a `'size'` handler to update those values."}, {"id": "825a00e2f0049d31", "url": "https://threejs.org/manual/en/offscreencanvas.html", "page_title": "OffscreenCanvas", "page_type": "guide", "page_summary": "A tutorial on using OffscreenCanvas with Three.js to offload rendering work to web workers, including basic setup, picking, OrbitControls integration, and fallback for browsers without OffscreenCanvas support.", "heading": "", "content": "Page: OffscreenCanvas\nSection: \n\nNow we can change `resizeRendererToDisplaySize` to use `state.width` and `state.height`", "code_blocks": [{"language": "javascript", "code": "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}", "filename": ""}], "chunk_position": 39, "heading_path": " > ", "breadcrumbs": "OffscreenCanvas >  > "}, {"id": "4fc4cd0dec851eae", "url": "https://threejs.org/manual/en/offscreencanvas.html", "page_title": "OffscreenCanvas", "page_type": "guide", "page_summary": "A tutorial on using OffscreenCanvas with Three.js to offload rendering work to web workers, including basic setup, picking, OrbitControls integration, and fallback for browsers without OffscreenCanvas support.", "heading": "and where we compute the aspect we need similar changes", "content": "Page: OffscreenCanvas\nSection: and where we compute the aspect we need similar changes\n\n", "code_blocks": [{"language": "javascript", "code": "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  ...", "filename": ""}], "chunk_position": 39, "heading_path": "and where we compute the aspect we need similar changes > and where we compute the aspect we need similar changes", "breadcrumbs": "OffscreenCanvas > and where we compute the aspect we need similar changes > and where we compute the aspect we need similar changes"}, {"id": "a8b843fa52772798", "url": "https://threejs.org/manual/en/offscreencanvas.html", "page_title": "OffscreenCanvas", "page_type": "guide", "page_summary": "A tutorial on using OffscreenCanvas with Three.js to offload rendering work to web workers, including basic setup, picking, OrbitControls integration, and fallback for browsers without OffscreenCanvas support.", "heading": "Back in the main page we'll send a `size` event anytime the page changes size.", "content": "Page: OffscreenCanvas\nSection: Back in the main page we'll send a `size` event anytime the page changes size.\n\n", "code_blocks": [{"language": "javascript", "code": "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();", "filename": ""}], "chunk_position": 39, "heading_path": "Back in the main page we'll send a `size` event anytime the page changes size. > Back in the main page we'll send a `size` event anytime the page changes size.", "breadcrumbs": "OffscreenCanvas > Back in the main page we'll send a `size` event anytime the page changes size. > Back in the main page we'll send a `size` event anytime the page changes size."}, {"id": "e233167809766e6b", "url": "https://threejs.org/manual/en/offscreencanvas.html", "page_title": "OffscreenCanvas", "page_type": "guide", "page_summary": "A tutorial on using OffscreenCanvas with Three.js to offload rendering work to web workers, including basic setup, picking, OrbitControls integration, and fallback for browsers without OffscreenCanvas support.", "heading": "", "content": "Page: OffscreenCanvas\nSection: \n\nWe also call it once to send the initial size.\n\nAnd with just those few changes, assuming your browser fully supports `OffscreenCanvas` it should work. Before we run it though let's check if the browser actually supports `OffscreenCanvas` and if not display an error. First let's add some HTML to display the error.", "code_blocks": [{"language": "html", "code": "<body>\n  <canvas id=\"c\"></canvas>\n+  <div id=\"noOffscreenCanvas\" style=\"display:none;\">\n+    <div>no OffscreenCanvas support</div>\n+  </div>\n</body>", "filename": ""}], "chunk_position": 39, "heading_path": " > ", "breadcrumbs": "OffscreenCanvas >  > "}, {"id": "a22347bfdcfb4fb1", "url": "https://threejs.org/manual/en/offscreencanvas.html", "page_title": "OffscreenCanvas", "page_type": "guide", "page_summary": "A tutorial on using OffscreenCanvas with Three.js to offload rendering work to web workers, including basic setup, picking, OrbitControls integration, and fallback for browsers without OffscreenCanvas support.", "heading": "and some CSS for that", "content": "Page: OffscreenCanvas\nSection: and some CSS for that\n\n", "code_blocks": [{"language": "css", "code": "#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}", "filename": ""}], "chunk_position": 39, "heading_path": "and some CSS for that > and some CSS for that", "breadcrumbs": "OffscreenCanvas > and some CSS for that > and some CSS for that", "duplicates": ["a22347bfdcfb4fb1"]}, {"id": "671b83aacf79f177", "url": "https://threejs.org/manual/en/offscreencanvas.html", "page_title": "OffscreenCanvas", "page_type": "guide", "page_summary": "A tutorial on using OffscreenCanvas with Three.js to offload rendering work to web workers, including basic setup, picking, OrbitControls integration, and fallback for browsers without OffscreenCanvas support.", "heading": "", "content": "Page: OffscreenCanvas\nSection: \n\nand then we can check for the existence of `transferControlToOffscreen` to see if the browser supports `OffscreenCanvas`", "code_blocks": [{"language": "javascript", "code": "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  ...", "filename": ""}], "chunk_position": 39, "heading_path": " > ", "breadcrumbs": "OffscreenCanvas >  > "}, {"id": "d913755e283bace3", "url": "https://threejs.org/manual/en/offscreencanvas.html", "page_title": "OffscreenCanvas", "page_type": "guide", "page_summary": "A tutorial on using OffscreenCanvas with Three.js to offload rendering work to web workers, including basic setup, picking, OrbitControls integration, and fallback for browsers without OffscreenCanvas support.", "heading": "", "content": "Page: OffscreenCanvas\nSection: \n\nand with that, if your browser supports `OffscreenCanvas` this example should work\n\n[click here to open in a separate window](/manual/examples/offscreencanvas.html)\n\nSo that's great but since not every browser supports `OffscreenCanvas` at the moment let's change the code to work with both `OffscreenCanvas` and if not then fallback to using the canvas in the main page like normal.\n\n> As an aside, if you need OffscreenCanvas to make your page responsive then it's not clear what the point of having a fallback is. Maybe based on if you end up running on the main page or in a worker you might adjust the amount of work done so that when running in a worker you can do more than when running in the main page. What you do is really up to you.", "code_blocks": [], "chunk_position": 39, "heading_path": " > ", "breadcrumbs": "OffscreenCanvas >  > "}, {"id": "813a822a7efa44cc", "url": "https://threejs.org/manual/en/offscreencanvas.html", "page_title": "OffscreenCanvas", "page_type": "guide", "page_summary": "A tutorial on using OffscreenCanvas with Three.js to offload rendering work to web workers, including basic setup, picking, OrbitControls integration, and fallback for browsers without OffscreenCanvas support.", "heading": "Adding Fallback Support", "content": "Page: OffscreenCanvas\nSection: Adding Fallback Support\n\nThe first thing we should probably do is separate out the three.js code from the code that is specific to the worker. That way we can use the same code on both the main page and the worker. In other words we will now have 3 files\n\n- our html file.\n\n`threejs-offscreencanvas-w-fallback.html`\n\n- a JavaScript that contains our three.js code.\n\n`shared-cubes.js`\n\n- our worker support code\n\n`offscreencanvas-worker-cubes.js`\n\n`shared-cubes.js` and `offscreencanvas-worker-cubes.js` are basically the split of our previous `offscreencanvas-cubes.js` file. First we copy all of `offscreencanvas-cubes.js` to `shared-cube.js`. Then we rename `main` to `init` since we already have a `main` in our HTML file and we need to export `init` and `state`", "code_blocks": [{"language": "javascript", "code": "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});", "filename": ""}], "chunk_position": 39, "heading_path": "Adding Fallback Support > Adding Fallback Support", "breadcrumbs": "OffscreenCanvas > Adding Fallback Support > Adding Fallback Support"}, {"id": "9d9f30b6b1538990", "url": "https://threejs.org/manual/en/offscreencanvas.html", "page_title": "OffscreenCanvas", "page_type": "guide", "page_summary": "A tutorial on using OffscreenCanvas with Three.js to offload rendering work to web workers, including basic setup, picking, OrbitControls integration, and fallback for browsers without OffscreenCanvas support.", "heading": "and cut out the just the non three.js relates parts", "content": "Page: OffscreenCanvas\nSection: and cut out the just the non three.js relates parts\n\n", "code_blocks": [{"language": "javascript", "code": "-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-};", "filename": ""}], "chunk_position": 39, "heading_path": "and cut out the just the non three.js relates parts > and cut out the just the non three.js relates parts", "breadcrumbs": "OffscreenCanvas > and cut out the just the non three.js relates parts > and cut out the just the non three.js relates parts"}, {"id": "c016ff7a66173cc0", "url": "https://threejs.org/manual/en/offscreencanvas.html", "page_title": "OffscreenCanvas", "page_type": "guide", "page_summary": "A tutorial on using OffscreenCanvas with Three.js to offload rendering work to web workers, including basic setup, picking, OrbitControls integration, and fallback for browsers without OffscreenCanvas support.", "heading": "", "content": "Page: OffscreenCanvas\nSection: \n\nThen we copy those parts we just deleted to `offscreencanvas-worker-cubes.js` and import `shared-cubes.js` as well as call `init` instead of `main`.", "code_blocks": [{"language": "javascript", "code": "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};", "filename": ""}], "chunk_position": 39, "heading_path": " > ", "breadcrumbs": "OffscreenCanvas >  > "}, {"id": "b6bea798ca36d9c3", "url": "https://threejs.org/manual/en/offscreencanvas.html", "page_title": "OffscreenCanvas", "page_type": "guide", "page_summary": "A tutorial on using OffscreenCanvas with Three.js to offload rendering work to web workers, including basic setup, picking, OrbitControls integration, and fallback for browsers without OffscreenCanvas support.", "heading": "Similarly we need to include `shared-cubes.js` in the main page", "content": "Page: OffscreenCanvas\nSection: Similarly we need to include `shared-cubes.js` in the main page\n\n", "code_blocks": [{"language": "html", "code": "<script type=\"module\">\n+import {init, state} from './shared-cubes.js';", "filename": ""}], "chunk_position": 39, "heading_path": "Similarly we need to include `shared-cubes.js` in the main page > Similarly we need to include `shared-cubes.js` in the main page", "breadcrumbs": "OffscreenCanvas > Similarly we need to include `shared-cubes.js` in the main page > Similarly we need to include `shared-cubes.js` in the main page"}, {"id": "b5f1ddcf5af5a03e", "url": "https://threejs.org/manual/en/offscreencanvas.html", "page_title": "OffscreenCanvas", "page_type": "guide", "page_summary": "A tutorial on using OffscreenCanvas with Three.js to offload rendering work to web workers, including basic setup, picking, OrbitControls integration, and fallback for browsers without OffscreenCanvas support.", "heading": "We can remove the HTML and CSS we added previously", "content": "Page: OffscreenCanvas\nSection: We can remove the HTML and CSS we added previously\n\n", "code_blocks": [{"language": "html", "code": "<body>\n  <canvas id=\"c\"></canvas>\n-  <div id=\"noOffscreenCanvas\" style=\"display:none;\">\n-    <div>no OffscreenCanvas support</div>\n-  </div>\n</body>", "filename": ""}], "chunk_position": 39, "heading_path": "We can remove the HTML and CSS we added previously > We can remove the HTML and CSS we added previously", "breadcrumbs": "OffscreenCanvas > We can remove the HTML and CSS we added previously > We can remove the HTML and CSS we added previously"}, {"id": "0d326b7a716f5ab1", "url": "https://threejs.org/manual/en/offscreencanvas.html", "page_title": "OffscreenCanvas", "page_type": "guide", "page_summary": "A tutorial on using OffscreenCanvas with Three.js to offload rendering work to web workers, including basic setup, picking, OrbitControls integration, and fallback for browsers without OffscreenCanvas support.", "heading": "", "content": "Page: OffscreenCanvas\nSection: \n\nThen let's change the code in the main page to call one start function or another depending on if the browser supports `OffscreenCanvas`.", "code_blocks": [{"language": "javascript", "code": "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  ...", "filename": ""}], "chunk_position": 39, "heading_path": " > ", "breadcrumbs": "OffscreenCanvas >  > "}, {"id": "085759b721f5975f", "url": "https://threejs.org/manual/en/offscreencanvas.html", "page_title": "OffscreenCanvas", "page_type": "guide", "page_summary": "A tutorial on using OffscreenCanvas with Three.js to offload rendering work to web workers, including basic setup, picking, OrbitControls integration, and fallback for browsers without OffscreenCanvas support.", "heading": "We'll move all the code we had to setup the worker inside `startWorker`", "content": "Page: OffscreenCanvas\nSection: We'll move all the code we had to setup the worker inside `startWorker`\n\n", "code_blocks": [{"language": "javascript", "code": "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}", "filename": ""}], "chunk_position": 39, "heading_path": "We'll move all the code we had to setup the worker inside `startWorker` > We'll move all the code we had to setup the worker inside `startWorker`", "breadcrumbs": "OffscreenCanvas > We'll move all the code we had to setup the worker inside `startWorker` > We'll move all the code we had to setup the worker inside `startWorker`"}, {"id": "19262e267a4bd063", "url": "https://threejs.org/manual/en/offscreencanvas.html", "page_title": "OffscreenCanvas", "page_type": "guide", "page_summary": "A tutorial on using OffscreenCanvas with Three.js to offload rendering work to web workers, including basic setup, picking, OrbitControls integration, and fallback for browsers without OffscreenCanvas support.", "heading": "and send `init` instead of `main`", "content": "Page: OffscreenCanvas\nSection: and send `init` instead of `main`\n\n", "code_blocks": [{"language": "javascript", "code": "-  worker.postMessage({type: 'main', canvas: offscreen}, [offscreen]);\n+  worker.postMessage({type: 'init', canvas: offscreen}, [offscreen]);", "filename": ""}], "chunk_position": 39, "heading_path": "and send `init` instead of `main` > and send `init` instead of `main`", "breadcrumbs": "OffscreenCanvas > and send `init` instead of `main` > and send `init` instead of `main`"}, {"id": "342154987588447e", "url": "https://threejs.org/manual/en/offscreencanvas.html", "page_title": "OffscreenCanvas", "page_type": "guide", "page_summary": "A tutorial on using OffscreenCanvas with Three.js to offload rendering work to web workers, including basic setup, picking, OrbitControls integration, and fallback for browsers without OffscreenCanvas support.", "heading": "for starting in the main page we can do this", "content": "Page: OffscreenCanvas\nSection: for starting in the main page we can do this\n\n", "code_blocks": [{"language": "javascript", "code": "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}", "filename": ""}], "chunk_position": 39, "heading_path": "for starting in the main page we can do this > for starting in the main page we can do this", "breadcrumbs": "OffscreenCanvas > for starting in the main page we can do this > for starting in the main page we can do this"}, {"id": "22b64b32b111fd09", "url": "https://threejs.org/manual/en/offscreencanvas.html", "page_title": "OffscreenCanvas", "page_type": "guide", "page_summary": "A tutorial on using OffscreenCanvas with Three.js to offload rendering work to web workers, including basic setup, picking, OrbitControls integration, and fallback for browsers without OffscreenCanvas support.", "heading": "", "content": "Page: OffscreenCanvas\nSection: \n\nand with that our example will run either in an OffscreenCanvas or fallback to running in the main page.\n\n[click here to open in a separate window](/manual/examples/offscreencanvas-w-fallback.html)\n\nSo that was relatively easy. Let's try picking. We'll take some code from the `RayCaster` example from [the article on picking](picking.html) and make it work offscreen.\n\nLet's copy the `shared-cube.js` to `shared-picking.js` and add the picking parts. We copy in the `PickHelper`", "code_blocks": [{"language": "javascript", "code": "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 > 1 ? 0xFFFF00 : 0xFF0000);\n    }\n  }\n}\n\nconst pickPosition = {x: 0, y: 0};\nconst pickHelper = new PickHelper();", "filename": ""}], "chunk_position": 39, "heading_path": " > ", "breadcrumbs": "OffscreenCanvas >  > "}, {"id": "ad485e70430c1c84", "url": "https://threejs.org/manual/en/offscreencanvas.html", "page_title": "OffscreenCanvas", "page_type": "guide", "page_summary": "A tutorial on using OffscreenCanvas with Three.js to offload rendering work to web workers, including basic setup, picking, OrbitControls integration, and fallback for browsers without OffscreenCanvas support.", "heading": "Picking with OffscreenCanvas", "content": "Page: OffscreenCanvas\nSection: Picking with OffscreenCanvas\n\nWe updated `pickPosition` from the mouse like this", "code_blocks": [{"language": "javascript", "code": "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);", "filename": ""}], "chunk_position": 39, "heading_path": "Picking with OffscreenCanvas > Picking with OffscreenCanvas", "breadcrumbs": "OffscreenCanvas > Picking with OffscreenCanvas > Picking with OffscreenCanvas"}, {"id": "6baf2e80c6058ba4", "url": "https://threejs.org/manual/en/offscreencanvas.html", "page_title": "OffscreenCanvas", "page_type": "guide", "page_summary": "A tutorial on using OffscreenCanvas with Three.js to offload rendering work to web workers, including basic setup, picking, OrbitControls integration, and fallback for browsers without OffscreenCanvas support.", "heading": "", "content": "Page: OffscreenCanvas\nSection: \n\nA worker can't read the mouse position directly so just like the size code let's send a message with the mouse position. Like the size code we'll send the mouse position and update `pickPosition`", "code_blocks": [{"language": "javascript", "code": "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};", "filename": ""}], "chunk_position": 39, "heading_path": " > ", "breadcrumbs": "OffscreenCanvas >  > "}, {"id": "9722cc8399d2fbf2", "url": "https://threejs.org/manual/en/offscreencanvas.html", "page_title": "OffscreenCanvas", "page_type": "guide", "page_summary": "A tutorial on using OffscreenCanvas with Three.js to offload rendering work to web workers, including basic setup, picking, OrbitControls integration, and fallback for browsers without OffscreenCanvas support.", "heading": "", "content": "Page: OffscreenCanvas\nSection: \n\nBack in our main page we need to add code to pass the mouse to the worker or the main page.", "code_blocks": [{"language": "javascript", "code": "+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) => {\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) => {\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}", "filename": ""}], "chunk_position": 39, "heading_path": " > ", "breadcrumbs": "OffscreenCanvas >  > "}, {"id": "ea813e7d7c6482cc", "url": "https://threejs.org/manual/en/offscreencanvas.html", "page_title": "OffscreenCanvas", "page_type": "guide", "page_summary": "A tutorial on using OffscreenCanvas with Three.js to offload rendering work to web workers, including basic setup, picking, OrbitControls integration, and fallback for browsers without OffscreenCanvas support.", "heading": "", "content": "Page: OffscreenCanvas\nSection: \n\nThen we can copy in all the mouse handling code to the main page and make just minor changes to use `sendMouse`", "code_blocks": [{"language": "javascript", "code": "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) => {\n  // prevent the window from scrolling\n  event.preventDefault();\n  setPickPosition(event.touches[0]);\n}, {passive: false});\n\nwindow.addEventListener('touchmove', (event) => {\n  setPickPosition(event.touches[0]);\n});\n\nwindow.addEventListener('touchend', clearPickPosition);", "filename": ""}], "chunk_position": 39, "heading_path": " > ", "breadcrumbs": "OffscreenCanvas >  > "}, {"id": "c7486eaa8adbc331", "url": "https://threejs.org/manual/en/offscreencanvas.html", "page_title": "OffscreenCanvas", "page_type": "guide", "page_summary": "A tutorial on using OffscreenCanvas with Three.js to offload rendering work to web workers, including basic setup, picking, OrbitControls integration, and fallback for browsers without OffscreenCanvas support.", "heading": "", "content": "Page: OffscreenCanvas\nSection: \n\nand with that picking should be working with `OffscreenCanvas`.\n\n[click here to open in a separate window](/manual/examples/offscreencanvas-w-picking.html)\n\nLet's take it one more step and add in the [`OrbitControls`](/docs/#examples/controls/OrbitControls). This will be little more involved. The [`OrbitControls`](/docs/#examples/controls/OrbitControls) use the DOM pretty extensively checking the mouse, touch events, and the keyboard.\n\nUnlike our code so far we can't really use a global `state` object without re-writing all the OrbitControls code to work with it. The OrbitControls take an `HTMLElement` to which they attach most of the DOM events they use. Maybe we could pass in our own object that has the same API surface as a DOM element. We only need to support the features the OrbitControls need.\n\nDigging through the [OrbitControls source code](https://github.com/mrdoob/three.js/blob/master/examples/jsm/controls/OrbitControls.js) it looks like we need to handle the following events.\n\n- contextmenu\n- pointerdown\n- pointermove\n- pointerup\n- touchstart\n- touchmove\n- touchend\n- wheel\n- keydown\n\nFor the pointer events we need the `ctrlKey`, `metaKey`, `shiftKey`, `button`, `pointerType`, `clientX`, `clientY`, `pageX`, and `pageY`, properties.\n\nFor the keydown events we need the `ctrlKey`, `metaKey`, `shiftKey`, and `keyCode` properties.\n\nFor the wheel event we only need the `deltaY` property.\n\nAnd for the touch events we only need `pageX` and `pageY` from the `touches` property.\n\nSo, let's make a proxy object pair. One part will run in the main page, get all those events, and pass on the relevant property values to the worker. The other part will run in the worker, receive those events and pass them on using events that have the same structure as the original DOM events so the OrbitControls won't be able to tell the difference.\n\nHere's the code for the worker part.", "code_blocks": [], "chunk_position": 39, "heading_path": " > ", "breadcrumbs": "OffscreenCanvas >  > "}, {"id": "4979db4e5a7771c2", "url": "https://threejs.org/manual/en/offscreencanvas.html", "page_title": "OffscreenCanvas", "page_type": "guide", "page_summary": "A tutorial on using OffscreenCanvas with Three.js to offload rendering work to web workers, including basic setup, picking, OrbitControls integration, and fallback for browsers without OffscreenCanvas support.", "heading": "OrbitControls with OffscreenCanvas", "content": "Page: OffscreenCanvas\nSection: OrbitControls with OffscreenCanvas\n\n", "code_blocks": [{"language": "javascript", "code": "import {EventDispatcher} from 'three';\n\nclass ElementProxyReceiver extends EventDispatcher {\n  constructor() {\n    super();\n  }\n  handleEvent(data) {\n    this.dispatchEvent(data);\n  }\n}", "filename": ""}], "chunk_position": 39, "heading_path": "OrbitControls with OffscreenCanvas > OrbitControls with OffscreenCanvas", "breadcrumbs": "OffscreenCanvas > OrbitControls with OffscreenCanvas > OrbitControls with OffscreenCanvas"}, {"id": "6d91e1bec77188e0", "url": "https://threejs.org/manual/en/offscreencanvas.html", "page_title": "OffscreenCanvas", "page_type": "guide", "page_summary": "A tutorial on using OffscreenCanvas with Three.js to offload rendering work to web workers, including basic setup, picking, OrbitControls integration, and fallback for browsers without OffscreenCanvas support.", "heading": "", "content": "Page: OffscreenCanvas\nSection: \n\nAll it does is if it receives a message it dispatches it. It inherits from [`EventDispatcher`](/docs/#api/en/core/EventDispatcher) which provides methods like `addEventListener` and `removeEventListener` just like a DOM element so if we pass it to the OrbitControls it should work.\n\n`ElementProxyReceiver` handles 1 element. In our case we only need one but it's best to think head so lets make a manager to manage more than one of them.", "code_blocks": [{"language": "javascript", "code": "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}", "filename": ""}], "chunk_position": 39, "heading_path": " > ", "breadcrumbs": "OffscreenCanvas >  > "}, {"id": "7ab42771a97df617", "url": "https://threejs.org/manual/en/offscreencanvas.html", "page_title": "OffscreenCanvas", "page_type": "guide", "page_summary": "A tutorial on using OffscreenCanvas with Three.js to offload rendering work to web workers, including basic setup, picking, OrbitControls integration, and fallback for browsers without OffscreenCanvas support.", "heading": "", "content": "Page: OffscreenCanvas\nSection: \n\nWe can make a instance of `ProxyManager` and call its `makeProxy` method with an id which will make an `ElementProxyReceiver` that responds to messages with that id.\n\nLet's hook it up to our worker's message handler.", "code_blocks": [{"language": "javascript", "code": "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};", "filename": ""}], "chunk_position": 39, "heading_path": " > ", "breadcrumbs": "OffscreenCanvas >  > "}, {"id": "9aa95c0db41f2c0f", "url": "https://threejs.org/manual/en/offscreencanvas.html", "page_title": "OffscreenCanvas", "page_type": "guide", "page_summary": "A tutorial on using OffscreenCanvas with Three.js to offload rendering work to web workers, including basic setup, picking, OrbitControls integration, and fallback for browsers without OffscreenCanvas support.", "heading": "", "content": "Page: OffscreenCanvas\nSection: \n\nIn our shared three.js code we need to import the [`OrbitControls`](/docs/#examples/controls/OrbitControls) and set them up.", "code_blocks": [{"language": "javascript", "code": "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();", "filename": ""}], "chunk_position": 39, "heading_path": " > ", "breadcrumbs": "OffscreenCanvas >  > "}, {"id": "c777719b3ff6ee6f", "url": "https://threejs.org/manual/en/offscreencanvas.html", "page_title": "OffscreenCanvas", "page_type": "guide", "page_summary": "A tutorial on using OffscreenCanvas with Three.js to offload rendering work to web workers, including basic setup, picking, OrbitControls integration, and fallback for browsers without OffscreenCanvas support.", "heading": "", "content": "Page: OffscreenCanvas\nSection: \n\nNotice we're passing the OrbitControls our proxy via `inputElement` instead of passing in the canvas like we do in other non-OffscreenCanvas examples.\n\nNext we can move all the picking event code from the HTML file to the shared three.js code as well while changing `canvas` to `inputElement`.", "code_blocks": [{"language": "javascript", "code": "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) => {\n  // prevent the window from scrolling\n  event.preventDefault();\n  setPickPosition(event.touches[0]);\n}, {passive: false});\n\n*inputElement.addEventListener('touchmove', (event) => {\n  setPickPosition(event.touches[0]);\n});\n\n*inputElement.addEventListener('touchend', clearPickPosition);", "filename": ""}], "chunk_position": 39, "heading_path": " > ", "breadcrumbs": "OffscreenCanvas >  > "}, {"id": "71e54474487d69ce", "url": "https://threejs.org/manual/en/offscreencanvas.html", "page_title": "OffscreenCanvas", "page_type": "guide", "page_summary": "A tutorial on using OffscreenCanvas with Three.js to offload rendering work to web workers, including basic setup, picking, OrbitControls integration, and fallback for browsers without OffscreenCanvas support.", "heading": "", "content": "Page: OffscreenCanvas\nSection: \n\nBack in the main page we need code to send messages for all the events we enumerated above.", "code_blocks": [{"language": "javascript", "code": "let nextProxyId = 0;\nclass ElementProxy {\n  constructor(element, worker, eventHandlers) {\n    this.id = nextProxyId++;\n    this.worker = worker;\n    const sendEvent = (data) => {\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}", "filename": ""}], "chunk_position": 39, "heading_path": " > ", "breadcrumbs": "OffscreenCanvas >  > "}, {"id": "4bc447a770020c95", "url": "https://threejs.org/manual/en/offscreencanvas.html", "page_title": "OffscreenCanvas", "page_type": "guide", "page_summary": "A tutorial on using OffscreenCanvas with Three.js to offload rendering work to web workers, including basic setup, picking, OrbitControls integration, and fallback for browsers without OffscreenCanvas support.", "heading": "", "content": "Page: OffscreenCanvas\nSection: \n\n`ElementProxy` takes the element who's events we want to proxy. It then registers an id with the worker by picking one and sending it via the `makeProxy` message we setup earlier. The worker will make an `ElementProxyReceiver` and register it to that id.\n\nWe then have an object of event handlers to register. This way we can pass handlers only for these events we want to forward to the worker.\n\nWhen we start the worker we first make a proxy and pass in our event handlers.", "code_blocks": [{"language": "javascript", "code": "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}", "filename": ""}], "chunk_position": 39, "heading_path": " > ", "breadcrumbs": "OffscreenCanvas >  > "}, {"id": "27f6dd44fff566a4", "url": "https://threejs.org/manual/en/offscreencanvas.html", "page_title": "OffscreenCanvas", "page_type": "guide", "page_summary": "A tutorial on using OffscreenCanvas with Three.js to offload rendering work to web workers, including basic setup, picking, OrbitControls integration, and fallback for browsers without OffscreenCanvas support.", "heading": "", "content": "Page: OffscreenCanvas\nSection: \n\nAnd here are the event handlers. All they do is copy a list of properties from the event they receive. They are passed a `sendEvent` function to which they pass the data they make. That function will add the correct id and send it to the worker.", "code_blocks": [{"language": "javascript", "code": "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 < 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}", "filename": ""}], "chunk_position": 39, "heading_path": " > ", "breadcrumbs": "OffscreenCanvas >  > "}, {"id": "c7b03954a68c9300", "url": "https://threejs.org/manual/en/offscreencanvas.html", "page_title": "OffscreenCanvas", "page_type": "guide", "page_summary": "A tutorial on using OffscreenCanvas with Three.js to offload rendering work to web workers, including basic setup, picking, OrbitControls integration, and fallback for browsers without OffscreenCanvas support.", "heading": "", "content": "Page: OffscreenCanvas\nSection: \n\nThis seems close to running but if we actually try it we'll see that the [`OrbitControls`](/docs/#examples/controls/OrbitControls) need a few more things.\n\nOne is they call `element.focus`. We don't need that to happen in the worker so let's just add a stub.", "code_blocks": [{"language": "javascript", "code": "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}", "filename": ""}], "chunk_position": 39, "heading_path": " > ", "breadcrumbs": "OffscreenCanvas >  > "}, {"id": "00500736e4682ad4", "url": "https://threejs.org/manual/en/offscreencanvas.html", "page_title": "OffscreenCanvas", "page_type": "guide", "page_summary": "A tutorial on using OffscreenCanvas with Three.js to offload rendering work to web workers, including basic setup, picking, OrbitControls integration, and fallback for browsers without OffscreenCanvas support.", "heading": "", "content": "Page: OffscreenCanvas\nSection: \n\nAnother is they call `event.preventDefault` and `event.stopPropagation`. We're already handling that in the main page so those can also be a noop.", "code_blocks": [{"language": "javascript", "code": "+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}", "filename": ""}], "chunk_position": 39, "heading_path": " > ", "breadcrumbs": "OffscreenCanvas >  > "}, {"id": "cd53bbf57d4cbe7d", "url": "https://threejs.org/manual/en/offscreencanvas.html", "page_title": "OffscreenCanvas", "page_type": "guide", "page_summary": "A tutorial on using OffscreenCanvas with Three.js to offload rendering work to web workers, including basic setup, picking, OrbitControls integration, and fallback for browsers without OffscreenCanvas support.", "heading": "", "content": "Page: OffscreenCanvas\nSection: \n\nAnother is they look at `clientWidth` and `clientHeight`. We were passing the size before but we can update the proxy pair to pass that as well.\n\nIn the worker...", "code_blocks": [{"language": "javascript", "code": "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}", "filename": ""}], "chunk_position": 39, "heading_path": " > ", "breadcrumbs": "OffscreenCanvas >  > "}, {"id": "40773a4bb73bf899", "url": "https://threejs.org/manual/en/offscreencanvas.html", "page_title": "OffscreenCanvas", "page_type": "guide", "page_summary": "A tutorial on using OffscreenCanvas with Three.js to offload rendering work to web workers, including basic setup, picking, OrbitControls integration, and fallback for browsers without OffscreenCanvas support.", "heading": "", "content": "Page: OffscreenCanvas\nSection: \n\nback in the main page we need to send the size and the left and top positions as well. Note that as is we don't handle if the canvas moves, only if it resizes. If you wanted to handle moving you'd need to call `sendSize` anytime something moved the canvas.", "code_blocks": [{"language": "javascript", "code": "class ElementProxy {\n  constructor(element, worker, eventHandlers) {\n    this.id = nextProxyId++;\n    this.worker = worker;\n    const sendEvent = (data) => {\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}", "filename": ""}], "chunk_position": 39, "heading_path": " > ", "breadcrumbs": "OffscreenCanvas >  > "}, {"id": "135a8327ab565054", "url": "https://threejs.org/manual/en/offscreencanvas.html", "page_title": "OffscreenCanvas", "page_type": "guide", "page_summary": "A tutorial on using OffscreenCanvas with Three.js to offload rendering work to web workers, including basic setup, picking, OrbitControls integration, and fallback for browsers without OffscreenCanvas support.", "heading": "and in our shared three.js code we no longer need `state`", "content": "Page: OffscreenCanvas\nSection: and in our shared three.js code we no longer need `state`\n\n", "code_blocks": [{"language": "javascript", "code": "-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  ...", "filename": ""}], "chunk_position": 39, "heading_path": "and in our shared three.js code we no longer need `state` > and in our shared three.js code we no longer need `state`", "breadcrumbs": "OffscreenCanvas > and in our shared three.js code we no longer need `state` > and in our shared three.js code we no longer need `state`"}, {"id": "56da6ac02d1a85fe", "url": "https://threejs.org/manual/en/offscreencanvas.html", "page_title": "OffscreenCanvas", "page_type": "guide", "page_summary": "A tutorial on using OffscreenCanvas with Three.js to offload rendering work to web workers, including basic setup, picking, OrbitControls integration, and fallback for browsers without OffscreenCanvas support.", "heading": "", "content": "Page: OffscreenCanvas\nSection: \n\nA few more hacks. The OrbitControls add `pointermove` and `pointerup` events to the `ownerDocument` of the element to handle mouse capture (when the mouse goes outside the window).\n\nFurther the code references the global `document` but there is no global document in a worker.\n\nWe can solve all of these with a 2 quick hacks. In our worker code we'll re-use our proxy for both problems.", "code_blocks": [{"language": "javascript", "code": "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}", "filename": ""}], "chunk_position": 39, "heading_path": " > ", "breadcrumbs": "OffscreenCanvas >  > "}, {"id": "8ffd8709260657c5", "url": "https://threejs.org/manual/en/offscreencanvas.html", "page_title": "OffscreenCanvas", "page_type": "guide", "page_summary": "A tutorial on using OffscreenCanvas with Three.js to offload rendering work to web workers, including basic setup, picking, OrbitControls integration, and fallback for browsers without OffscreenCanvas support.", "heading": "", "content": "Page: OffscreenCanvas\nSection: \n\nThis will give the [`OrbitControls`](/docs/#examples/controls/OrbitControls) something to inspect which matches their expectations.\n\nI know that was kind of hard to follow. The short version is: `ElementProxy` runs on the main page and forwards DOM events to `ElementProxyReceiver` in the worker which masquerades as an `HTMLElement` that we can use both with the [`OrbitControls`](/docs/#examples/controls/OrbitControls) and with our own code.\n\nThe final thing is our fallback when we are not using OffscreenCanvas. All we have to do is pass the canvas itself as our `inputElement`.", "code_blocks": [{"language": "javascript", "code": "function startMainPage(canvas) {\n-  init({canvas});\n+  init({canvas, inputElement: canvas});\n  console.log('using regular canvas');\n}", "filename": ""}], "chunk_position": 39, "heading_path": " > ", "breadcrumbs": "OffscreenCanvas >  > "}, {"id": "6631f89c1fb109de", "url": "https://threejs.org/manual/en/offscreencanvas.html", "page_title": "OffscreenCanvas", "page_type": "guide", "page_summary": "A tutorial on using OffscreenCanvas with Three.js to offload rendering work to web workers, including basic setup, picking, OrbitControls integration, and fallback for browsers without OffscreenCanvas support.", "heading": "", "content": "Page: OffscreenCanvas\nSection: \n\nand now we should have OrbitControls working with OffscreenCanvas\n\n[click here to open in a separate window](/manual/examples/offscreencanvas-w-orbitcontrols.html)\n\nThis is probably the most complicated example on this site. It's a little hard to follow because there are 3 files involved for each sample. The HTML file, the worker file, the shared three.js code.\n\nI hope it wasn't too difficult to understand and that it provided some useful examples of working with three.js, OffscreenCanvas and web workers.", "code_blocks": [], "chunk_position": 39, "heading_path": " > ", "breadcrumbs": "OffscreenCanvas >  > "}, {"id": "f3bdb6130138d736", "url": "https://threejs.org/manual/en/load-obj.html", "page_title": "Loading a .OBJ File", "page_type": "guide", "page_summary": "A guide on loading and displaying .OBJ 3D models in three.js, covering the OBJLoader and MTLLoader, handling materials and textures, automatically framing the camera to fit loaded models, and troubleshooting common loading issues.", "heading": "Loading a .OBJ File", "content": "Page: Loading a .OBJ File\nSection: Loading a .OBJ File\n\nOne of the most common things people want to do with three.js is to load and display 3D models. A common format is the .OBJ 3D format so let's try loading one.\n\nSearching the net I found this CC-BY-NC 3.0 windmill 3D model by ahedov. I downloaded the .blend file from that site, loaded it into Blender and exported it as an .OBJ file.\n\n> Note: If you've never used Blender you might be in for a surprise in that Blender does things differently than just about every other program you've ever used. Just be aware you might need to set aside some time to read some basic UI navigation for Blender.\n> \n> Let me also add that 3D programs in general are giant beasts with 1000s of features. They are some of the most complicated software there is. When I first learned 3D Studio Max in 1996 I read through 70% of the 600 page manual spending a few hours a day for around 3 weeks. That paid off in that when I learned Maya a few years later some of the lessons learned before were applicable to Maya. So, just be aware that if you really want to be able to use 3D software to either build 3D assets or to modify existing ones put it on your schedule and clear sometime to really go through some lessons.\n\nIn any case I used these export options. Let's try to display it!\n\nI started with the directional lighting example from the lights article and I combined it with the hemispherical lighting example so I ended up with one HemisphereLight and one DirectionalLight. I also removed all the GUI stuff related to adjusting the lights. I also removed the cube and sphere that were being added to the scene.", "code_blocks": [], "chunk_position": 40, "heading_path": "Loading a .OBJ File > Loading a .OBJ File", "breadcrumbs": "Loading a .OBJ File > Loading a .OBJ File > Loading a .OBJ File"}, {"id": "3a60bcba293059aa", "url": "https://threejs.org/manual/en/load-obj.html", "page_title": "Loading a .OBJ File", "page_type": "guide", "page_summary": "A guide on loading and displaying .OBJ 3D models in three.js, covering the OBJLoader and MTLLoader, handling materials and textures, automatically framing the camera to fit loaded models, and troubleshooting common loading issues.", "heading": "Setting up the OBJLoader", "content": "Page: Loading a .OBJ File\nSection: Setting up the OBJLoader\n\nFrom that the first thing we need to do is include the OBJLoader loader in our script. Then to load the .OBJ file we create an instance of OBJLoader, pass it the URL of our .OBJ file, and pass in a callback that adds the loaded model to our scene.\n\nIf we run that what happens? Well it's close but we're getting errors about materials since we haven't given the scene any materials and .OBJ files don't have material parameters.\n\nThe .OBJ loader can be passed an object of name / material pairs. When it loads the .OBJ file, any material name it finds it will look for the corresponding material in the map of materials set on the loader. If it finds a material that matches by name it will use that material. If not it will use the loader's default material.", "code_blocks": [{"language": "javascript", "code": "import {OBJLoader} from 'three/addons/loaders/OBJLoader.js';", "filename": ""}, {"language": "javascript", "code": "{\n  const objLoader = new OBJLoader();\n  objLoader.load('resources/models/windmill/windmill.obj', (root) => {\n    scene.add(root);\n  });\n}", "filename": ""}], "chunk_position": 40, "heading_path": "Setting up the OBJLoader > Setting up the OBJLoader", "breadcrumbs": "Loading a .OBJ File > Setting up the OBJLoader > Setting up the OBJLoader"}, {"id": "b52ace5acfdf40fd", "url": "https://threejs.org/manual/en/load-obj.html", "page_title": "Loading a .OBJ File", "page_type": "guide", "page_summary": "A guide on loading and displaying .OBJ 3D models in three.js, covering the OBJLoader and MTLLoader, handling materials and textures, automatically framing the camera to fit loaded models, and troubleshooting common loading issues.", "heading": "Loading .MTL Materials", "content": "Page: Loading a .OBJ File\nSection: Loading .MTL Materials\n\nSometimes .OBJ files come with a .MTL file that defines materials. In our case the exporter also created a .MTL file. .MTL format is plain ASCII so it's easy to look at. Looking at it here we can see there are 2 materials referencing 5 jpg textures but where are the texture files?\n\nAll we got was an .OBJ file and an .MTL file.\n\nAt least for this model it turns out the textures are embedded in the .blend file we downloaded. We can ask blender to export those files to by picking File->External Data->Unpack All Into Files and then choosing Write Files to Current Directory. This ends up writing the files in the same folder as the .blend file in a sub folder called textures. I copied those textures into the same folder I exported the .OBJ file to.\n\nNow that we have the textures available we can load the .MTL file. First we need to include the MTLLoader. Then we first load the .MTL file. When it's finished loading we add the just loaded materials on to the OBJLoader itself via the setMaterials and then load the .OBJ file.", "code_blocks": [{"language": "javascript", "code": "import * as THREE from 'three';\nimport {OrbitControls} from 'three/addons/controls/OrbitControls.js';\nimport {OBJLoader} from 'three/addons/loaders/OBJLoader.js';\nimport {MTLLoader} from 'three/addons/loaders/MTLLoader.js';", "filename": ""}, {"language": "javascript", "code": "{\n  const mtlLoader = new MTLLoader();\n  mtlLoader.load('resources/models/windmill/windmill.mtl', (mtl) => {\n    mtl.preload();\n    objLoader.setMaterials(mtl);\n    objLoader.load('resources/models/windmill/windmill.obj', (root) => {\n      scene.add(root);\n    });\n  });\n}", "filename": ""}, {"language": "plaintext", "code": "# 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", "filename": ""}], "chunk_position": 40, "heading_path": "Loading .MTL Materials > Loading .MTL Materials", "breadcrumbs": "Loading a .OBJ File > Loading .MTL Materials > Loading .MTL Materials"}, {"id": "f3fc16ac0a71ff47", "url": "https://threejs.org/manual/en/load-obj.html", "page_title": "Loading a .OBJ File", "page_type": "guide", "page_summary": "A guide on loading and displaying .OBJ 3D models in three.js, covering the OBJLoader and MTLLoader, handling materials and textures, automatically framing the camera to fit loaded models, and troubleshooting common loading issues.", "heading": "Fixing Missing Cloth on Blades", "content": "Page: Loading a .OBJ File\nSection: Fixing Missing Cloth on Blades\n\nNote that if we spin the model around you'll see the windmill cloth disappears. We need the material on the blades to be double sided, something we went over in the article on materials. There is no easy way to fix this in the .MTL file. Off the top of my head I can think of 3 ways to fix this.\n\n- Loop over all the materials after loading them and set them all to double sided. This solution works but ideally we only want materials that need to be double sided to be double sided because drawing double sided is slower than single sided.\n- Manually set a specific material. Looking in the .MTL file there are 2 materials. One called \"windmill\" and the other called \"Material\". Through trial and error I figured out the blades use the material called \"Material\" so we could set that one specifically.\n- Realizing that the .MTL file is limited we could just not use it and instead create materials ourselves. In this case we'd need to look up the Mesh object after loading the obj file.\n\nWhich one you pick is up to you. 1 is easiest. 3 is most flexible. 2 somewhere in between. For now I'll pick 2.", "code_blocks": [{"language": "javascript", "code": "const mtlLoader = new MTLLoader();\n mtlLoader.load('resources/models/windmill/windmill.mtl', (mtl) => {\n   mtl.preload();\n   for (const material of Object.values(mtl.materials)) {\n     material.side = THREE.DoubleSide;\n   }\n   ...", "filename": ""}, {"language": "javascript", "code": "const mtlLoader = new MTLLoader();\n mtlLoader.load('resources/models/windmill/windmill.mtl', (mtl) => {\n   mtl.preload();\n   mtl.materials.Material.side = THREE.DoubleSide;\n   ...", "filename": ""}, {"language": "javascript", "code": "objLoader.load('resources/models/windmill/windmill.obj', (root) => {\n   const materials = {\n     Material: new THREE.MeshPhongMaterial({...}),\n     windmill: new THREE.MeshPhongMaterial({...}),\n   };\n   root.traverse(node => {\n     const material = materials[node.material?.name];\n     if (material) {\n       node.material = material;\n     }\n   })\n   scene.add(root);\n });", "filename": ""}], "chunk_position": 40, "heading_path": "Fixing Missing Cloth on Blades > Fixing Missing Cloth on Blades", "breadcrumbs": "Loading a .OBJ File > Fixing Missing Cloth on Blades > Fixing Missing Cloth on Blades"}, {"id": "767be2fc657d30fc", "url": "https://threejs.org/manual/en/load-obj.html", "page_title": "Loading a .OBJ File", "page_type": "guide", "page_summary": "A guide on loading and displaying .OBJ 3D models in three.js, covering the OBJLoader and MTLLoader, handling materials and textures, automatically framing the camera to fit loaded models, and troubleshooting common loading issues.", "heading": "Fixing Blocky Appearance with Normal Maps", "content": "Page: Loading a .OBJ File\nSection: Fixing Blocky Appearance with Normal Maps\n\nAnd with that change you should still see the cloth on the blades when looking from behind but there's one more issue. If we zoom in close we see things are turning blocky.\n\nWhat's going on? Looking at the textures there are 2 textures labelled NOR for NORmal map. And looking at them they look like normal maps. Normal maps are generally purple where as bump maps are black and white. Normal maps represent the direction of the surface where as bump maps represent the height of the surface.\n\nLooking at the source for the MTLLoader it expects the keyword norm for normal maps so let's edit the .MTL file and now when we load it it will be using the normal maps as normal maps and we can see the back of the blades.", "code_blocks": [{"language": "plaintext", "code": "# 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\nnorm 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\nnorm windmill_001_base_NOR.jpg\nmap_Ns windmill_001_base_SPEC.jpg", "filename": ""}], "chunk_position": 40, "heading_path": "Fixing Blocky Appearance with Normal Maps > Fixing Blocky Appearance with Normal Maps", "breadcrumbs": "Loading a .OBJ File > Fixing Blocky Appearance with Normal Maps > Fixing Blocky Appearance with Normal Maps"}, {"id": "54e1d4b6765d7f7b", "url": "https://threejs.org/manual/en/load-obj.html", "page_title": "Loading a .OBJ File", "page_type": "guide", "page_summary": "A guide on loading and displaying .OBJ 3D models in three.js, covering the OBJLoader and MTLLoader, handling materials and textures, automatically framing the camera to fit loaded models, and troubleshooting common loading issues.", "heading": "Loading a Different Model", "content": "Page: Loading a .OBJ File\nSection: Loading a Different Model\n\nLet's load a different file. Searching the net I found this CC-BY-NC windmill 3D model made by Roger Gerzner / GERIZ.3D Art. It had a .OBJ version already available. Let's load it up (note I removed the .MTL loader for now).\n\nHmmm, nothing appears. What's the problem? I wonder what size the model is? We can ask THREE.js what size the model is and try to set our camera automatically.", "code_blocks": [{"language": "javascript", "code": "objLoader.load('resources/models/windmill_2/windmill.obj', (root) => {\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);", "filename": ""}, {"language": "plaintext", "code": "size 2123.6499788469982\ncenter p {x: -0.00006103515625, y: 770.0909731090069, z: -3.313507080078125}", "filename": ""}], "chunk_position": 40, "heading_path": "Loading a Different Model > Loading a Different Model", "breadcrumbs": "Loading a .OBJ File > Loading a Different Model > Loading a Different Model"}, {"id": "53cee74205b05d4f", "url": "https://threejs.org/manual/en/load-obj.html", "page_title": "Loading a .OBJ File", "page_type": "guide", "page_summary": "A guide on loading and displaying .OBJ 3D models in three.js, covering the OBJLoader and MTLLoader, handling materials and textures, automatically framing the camera to fit loaded models, and troubleshooting common loading issues.", "heading": "Auto-Framing the Camera", "content": "Page: Loading a .OBJ File\nSection: Auto-Framing the Camera\n\nOur camera is currently only showing about 100 units with near at 0.1 and far at 100. Our ground plane is only 40 units across so basically this windmill model is so big, 2000 units, that it's surrounding our camera and all parts of it our outside our frustum.\n\nWe could manually fix that but we could also make the camera auto frame our scene. Let's try that. We can then use the box we just computed adjust the camera settings to view the entire scene. Note that there is no right answer on where to put the camera. We could be facing the scene from any direction at any altitude so we'll just have to pick something.\n\nAs we went over in the article on cameras the camera defines a frustum. That frustum is defined by the field of view (fov) and the near and far settings. We want to know given whatever field of view the camera currently has, how far away does the camera need to be so the box containing the scene fits inside the frustum assuming the frustum extended forever. In other words let's assume near is 0.00000001 and far is infinity.\n\nSince we know the size of the box and we know the field of view we have this triangle. You can see on the left is the camera and the blue frustum is projecting out in front of it. We just computed the box that contains the windmill. We need to compute how far way the camera should be from the box so that the box appears inside the frustum.\n\nUsing basic right triangle trigonometry and SOHCAHTOA, given we know the field of view for the frustum and we know the size of the box we can compute the distance.\n\nBased on that diagram the formula for computing distance is:\n\ndistance = halfSizeToFitOnScreen / tangent(halfFovY)\n\nLet's translate that to code. First let's make a function that will compute distance and then move the camera that distance units from the center of the box. We'll then point the camera at the center of the box.\n\nWe pass in 2 sizes. The boxSize and the sizeToFitOnScreen. If we just passed in boxSize and used that as sizeToFitOnScreen then the math would make the box fit perfectly inside the frustum. We want a little extra space above and below so we'll pass in a slightly larger size.", "code_blocks": [{"language": "javascript", "code": "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}", "filename": ""}, {"language": "javascript", "code": "{\n  const objLoader = new OBJLoader();\n  objLoader.load('resources/models/windmill_2/windmill.obj', (root) => {\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}", "filename": ""}], "chunk_position": 40, "heading_path": "Auto-Framing the Camera > Auto-Framing the Camera", "breadcrumbs": "Loading a .OBJ File > Auto-Framing the Camera > Auto-Framing the Camera"}, {"id": "9183681c69ec4056", "url": "https://threejs.org/manual/en/load-obj.html", "page_title": "Loading a .OBJ File", "page_type": "guide", "page_summary": "A guide on loading and displaying .OBJ 3D models in three.js, covering the OBJLoader and MTLLoader, handling materials and textures, automatically framing the camera to fit loaded models, and troubleshooting common loading issues.", "heading": "Adjusting Camera Direction and Ground Plane", "content": "Page: Loading a .OBJ File\nSection: Adjusting Camera Direction and Ground Plane\n\nThis almost works. Use the mouse to rotate the camera and you should 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 starts (0, 10, 20) to distance units way from the center in the direction the camera is relative to the center that's moving the camera almost straight down below the windmill.\n\nLet's change it to move sideways from the center of the box to in whatever direction the camera is from the center. All we need to do to do that is zero out the y component of the vector from the box to the camera. Then, when we normalize that vector it will become a vector parallel to the XZ plane. In other words parallel to the ground.\n\nIf you look at the bottom of the windmill you'll see a small square. That is our ground plane. It's only 40x40 units and so is way too small relative to the windmill. Since the windmill is over 2000 units big let's change the size of the ground plane to something more fitting. We also need to adjust the repeat otherwise our checkerboard will be so fine we won't even be able to see it unless we zoom way way in.", "code_blocks": [{"language": "javascript", "code": "// compute a unit vector that points in the direction the camera is now\n// in the xz plane from the center of the box\nconst direction = (new THREE.Vector3())\n    .subVectors(camera.position, boxCenter)\n    .multiply(new THREE.Vector3(1, 0, 1))\n    .normalize();", "filename": ""}, {"language": "javascript", "code": "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;\nconst repeats = planeSize / 200;\ntexture.repeat.set(repeats, repeats);", "filename": ""}], "chunk_position": 40, "heading_path": "Adjusting Camera Direction and Ground Plane > Adjusting Camera Direction and Ground Plane", "breadcrumbs": "Loading a .OBJ File > Adjusting Camera Direction and Ground Plane > Adjusting Camera Direction and Ground Plane"}, {"id": "fc28b815976b9245", "url": "https://threejs.org/manual/en/load-obj.html", "page_title": "Loading a .OBJ File", "page_type": "guide", "page_summary": "A guide on loading and displaying .OBJ 3D models in three.js, covering the OBJLoader and MTLLoader, handling materials and textures, automatically framing the camera to fit loaded models, and troubleshooting common loading issues.", "heading": "Converting TGA Textures to JPG", "content": "Page: Loading a .OBJ File\nSection: Converting TGA Textures to JPG\n\nLet's add the materials back. Like before there is a .MTL file that references some textures but looking at the files I quickly see an issue.\n\nThere are TARGA (.tga) files and they are giant!\n\nTHREE.js actually has a TGA loader but it's arguably wrong to use it for most use cases. If you're making a viewer where you want to allow users to view random 3D files they find on the net then maybe, just maybe, you might want to load TGA files.\n\nOne problem with TGA files are they can't be compressed well at all. TGA only supports very simple compression and looking above we can see the files are not compressed at all as the odds of them being all exactly the same size are extremely low. Further they are 12 megabytes each!!! If we used those files the user would have to download 36meg to see the windmill.\n\nAnother issue with TGA is the browser itself has no support for them so loading them is likely going to be slower than loading supported formats like .JPG and .PNG\n\nI'm pretty sure for our purposes converting them to .JPG will be the best option. Looking inside I see they are 3 channels each, RGB, there is no alpha channel. JPG only supports 3 channels so that's a good fit. JPG also supports lossy compression so we can make the files much smaller to download.\n\nLoading the files up they were each 2048x2048. That seemed like a waste to me but of course it depends on your use case. I made them each 1024x1024 and saved them at a 50% quality setting in Photoshop.\n\nWe went from 36meg to 0.55meg! Of course the artist might not be pleased with this compression so be sure to consult with them to discuss the tradeoffs.\n\nNow, to use the .MTL file we need to edit it to reference the .JPG files instead of the .TGA files. Fortunately it's a simple text file so it's easy to edit.", "code_blocks": [{"language": "plaintext", "code": "newmtl blinn1SG\nKa 0.10 0.10 0.10\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\nmap_Kd windmill_diffuse.jpg\nmap_Ks windmill_spec.jpg\nmap_bump windmill_normal.jpg\nbump windmill_normal.jpg", "filename": ""}], "chunk_position": 40, "heading_path": "Converting TGA Textures to JPG > Converting TGA Textures to JPG", "breadcrumbs": "Loading a .OBJ File > Converting TGA Textures to JPG > Converting TGA Textures to JPG"}, {"id": "ee6be886be13cda5", "url": "https://threejs.org/manual/en/load-obj.html", "page_title": "Loading a .OBJ File", "page_type": "guide", "page_summary": "A guide on loading and displaying .OBJ 3D models in three.js, covering the OBJLoader and MTLLoader, handling materials and textures, automatically framing the camera to fit loaded models, and troubleshooting common loading issues.", "heading": "Fixing MTL File Issues", "content": "Page: Loading a .OBJ File\nSection: Fixing MTL File Issues\n\nNow 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 and then set them on the OBJLoader.\n\nBefore we actually try it out I ran into some issues that rather than show a failure I'm just going to go over them.\n\nIssue #1: The three MTLLoader creates materials that multiply the material's diffuse color by the diffuse texture map.\n\nThat's a useful feature but looking a the .MTL file above the line:\n\nKd 0.00 0.00 0.00\n\nsets the diffuse color to 0. Texture map * 0 = black! It's possible the modeling tool used to make the windmill did not multiply the diffuse texture map by the diffuse color. That's why it worked for the artists that made this windmill.\n\nTo fix this we can change the line to:\n\nKd 1.00 1.00 1.00\n\nsince Texture Map * 1 = Texture Map.\n\nIssue #2: The specular color is also black\n\nThe line that starts with Ks specifies the specular color. It's likely the modeling software used to make the windmill did something similar as it did with diffuse maps in that it used the specular map's color for specular highlights. Three.js uses only the red channel of a specular map as input to how much of the specular color to reflect but three still needs a specular color set.\n\nLike above we can fix that by editing the .MTL file.\n\nIssue #3: The windmill_normal.jpg is a normal map not a bump map.\n\nJust like above we just need to edit the .MTL file.", "code_blocks": [{"language": "javascript", "code": "{\n  const mtlLoader = new MTLLoader();\n  mtlLoader.load('resources/models/windmill_2/windmill-fixed.mtl', (mtl) => {\n    mtl.preload();\n    const objLoader = new OBJLoader();\n    objLoader.setMaterials(mtl);\n    objLoader.load('resources/models/windmill/windmill.obj', (root) => {\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}", "filename": ""}, {"language": "plaintext", "code": "Kd 1.00 1.00 1.00", "filename": ""}, {"language": "plaintext", "code": "Ks 1.00 1.00 1.00", "filename": ""}, {"language": "plaintext", "code": "norm windmill_normal.jpg", "filename": ""}], "chunk_position": 40, "heading_path": "Fixing MTL File Issues > Fixing MTL File Issues", "breadcrumbs": "Loading a .OBJ File > Fixing MTL File Issues > Fixing MTL File Issues"}, {"id": "b4c8f3e70d3bdb6a", "url": "https://threejs.org/manual/en/load-obj.html", "page_title": "Loading a .OBJ File", "page_type": "guide", "page_summary": "A guide on loading and displaying .OBJ 3D models in three.js, covering the OBJLoader and MTLLoader, handling materials and textures, automatically framing the camera to fit loaded models, and troubleshooting common loading issues.", "heading": "Common Loading Issues", "content": "Page: Loading a .OBJ File\nSection: Common Loading Issues\n\nLoading models often runs into these kinds of issues. Common issues include:\n\n- Needing to know the size: 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 to 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.\n- Orientation Wrong: THREE.js is generally Y = up. Some modeling packages default to Z = up, some Y = up. Some are settable. If 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), or 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 just 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 change the orientation.\n- No .MTL file or wrong materials or incompatible parameters: Above we used a .MTL file above which helped us load materials but there were issues. We manually edited the .MTL file to fix. It'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 scene and print out all the materials. Then, go modify the code to make custom materials and assign them where appropriate either by 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 scene and fixing things.\n- Textures too large: Most 3D models are made for either architecture, movies and commercials, or games. For architecture and movies no one really cares about the size of the textures since. For games people care because games have limited memory but most games run locally. Webpages though you want to load as fast as possible and so you need to look at the textures and try to make them as small as possible and still look good. In fact the first windmill we should arguably done something about the textures. They are currently a total of 10meg!!!\n\nAlso remember like we mentioned in the article on textures that textures take memory so a 50k JPG that expands to 4096x4096 will download fast but still take a ton of memory.", "code_blocks": [], "chunk_position": 40, "heading_path": "Common Loading Issues > Common Loading Issues", "breadcrumbs": "Loading a .OBJ File > Common Loading Issues > Common Loading Issues"}, {"id": "be8da03f42fd7b71", "url": "https://threejs.org/manual/en/load-obj.html", "page_title": "Loading a .OBJ File", "page_type": "guide", "page_summary": "A guide on loading and displaying .OBJ 3D models in three.js, covering the OBJLoader and MTLLoader, handling materials and textures, automatically framing the camera to fit loaded models, and troubleshooting common loading issues.", "heading": "Spinning Limitation and Next Steps", "content": "Page: Loading a .OBJ File\nSection: Spinning Limitation and Next Steps\n\nThe last thing I wanted to show is spinning the windmills. Unfortunately, .OBJ files have no hierarchy. That means all parts of each windmill 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.\n\nThis 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 is because it's simple and doesn't support many features it works more often than not. Especially if you're making something still like an architectural image and there's no need to animate anything it's not a bad way to get static props into a scene.\n\nNext up we'll try loading a gLTF scene. The gLTF format supports many more features.", "code_blocks": [], "chunk_position": 40, "heading_path": "Spinning Limitation and Next Steps > Spinning Limitation and Next Steps", "breadcrumbs": "Loading a .OBJ File > Spinning Limitation and Next Steps > Spinning Limitation and Next Steps"}, {"id": "1817a29fb5422c61", "url": "https://threejs.org/manual/en/load-gltf.html", "page_title": "Loading a .GLTF File", "page_type": "guide", "page_summary": "A tutorial on loading and working with .GLTF files in three.js, covering loading the file, inspecting the scene graph, animating objects, creating paths, and adding shadows.", "heading": "Loading a .GLTF File", "content": "Page: Loading a .GLTF File\nSection: Loading a .GLTF File\n\nIn a previous lesson we loaded an .OBJ file. If you haven't read it you might want to check it out first.\n\nAs pointed out over there the .OBJ file format is very old and fairly simple. It provides no scene graph so everything loaded is one large mesh. It was designed mostly as a simple way to pass data between 3D editors.\n\nThe gLTF format is actually a format designed from the ground up for be used for displaying graphics. 3D formats can be divided into 3 or 4 basic types.\n\n- 3D Editor Formats: This are formats specific to a single app. .blend (Blender), .max (3d Studio Max), .mb and .ma (Maya), etc...\n\n- Exchange formats: These are formats like .OBJ, .DAE (Collada), .FBX. They are designed to help exchange information between 3D editors. As such they are usually much larger than needed with extra info used only inside 3d editors\n\n- App formats: These are usually specific to certain apps, usually games.\n\n- Transmission formats: gLTF might be the first true transmission format. I suppose VRML might be considered one but VRML was actually a pretty poor format.\n\ngLTF is designed to do some things well that all those other formats don't do:\n\n- Be small for transmission: For example this means much of their large data, like vertices, is stored in binary. When you download a .gLTF file that data can be uploaded to the GPU with zero processing. It's ready as is. This is in contrast to say VRML, .OBJ, or .DAE where vertices are stored as text and have to be parsed. Text vertex positions can easily be 3x to 5x larger than binary.\n\n- Be ready to render: This again is different from other formats except maybe App formats. The data in a glTF file is mean to be rendered, not edited. Data that's not important to rendering has generally been removed. Polygons have been converted to triangles. Materials have known values that are supposed to work everywhere.\n\ngLTF was specifically designed so you should be able to download a glTF file and display it with a minimum of trouble. Let's cross our fingers that's truly the case as none of the other formats have been able to do this.", "code_blocks": [], "chunk_position": 41, "heading_path": "Loading a .GLTF File > Loading a .GLTF File", "breadcrumbs": "Loading a .GLTF File > Loading a .GLTF File > Loading a .GLTF File"}, {"id": "7b33afe2c131680d", "url": "https://threejs.org/manual/en/load-gltf.html", "page_title": "Loading a .GLTF File", "page_type": "guide", "page_summary": "A tutorial on loading and working with .GLTF files in three.js, covering loading the file, inspecting the scene graph, animating objects, creating paths, and adding shadows.", "heading": "Loading a gLTF Model", "content": "Page: Loading a .GLTF File\nSection: Loading a gLTF Model\n\nI wasn't really sure what I should show. At some level loading and displaying a gLTF file is simpler than an .OBJ file. Unlike a .OBJ file materials are directly part of the format. That said I thought I should at least load one up and I think going over the issues I ran into might provide some good info.\n\nSearching the net I found this low-poly city by antonmoek which seemed like if we're lucky might make a good example.\n\nStarting with an example from the .OBJ article I removed the code for loading .OBJ and replaced it with code for loading .GLTF", "code_blocks": [{"language": "javascript", "code": "const mtlLoader = new MTLLoader();\nmtlLoader.loadMtl('resources/models/windmill/windmill-fixed.mtl', (mtl) => {\n  mtl.preload();\n  mtl.materials.Material.side = THREE.DoubleSide;\n  objLoader.setMaterials(mtl);\n  objLoader.load('resources/models/windmill/windmill.obj', (event) => {\n    const root = event.detail.loaderRootNode;\n    scene.add(root);\n    ...\n  });\n});", "filename": ""}, {"language": "javascript", "code": "{\n  const gltfLoader = new GLTFLoader();\n  const url = 'resources/models/cartoon_lowpoly_small_city_free_pack/scene.gltf';\n  gltfLoader.load(url, (gltf) => {\n    const root = gltf.scene;\n    scene.add(root);\n    ...\n  });\n}", "filename": ""}, {"language": "javascript", "code": "-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';", "filename": ""}], "chunk_position": 41, "heading_path": "Loading a gLTF Model > Loading a gLTF Model", "breadcrumbs": "Loading a .GLTF File > Loading a gLTF Model > Loading a gLTF Model"}, {"id": "925852796a33028d", "url": "https://threejs.org/manual/en/load-gltf.html", "page_title": "Loading a .GLTF File", "page_type": "guide", "page_summary": "A tutorial on loading and working with .GLTF files in three.js, covering loading the file, inspecting the scene graph, animating objects, creating paths, and adding shadows.", "heading": "Inspecting the Scene Graph", "content": "Page: Loading a .GLTF File\nSection: Inspecting the Scene Graph\n\nI kept the auto framing code as before. We also need to include the GLTFLoader and we can get rid of the OBJLoader. Magic! It just works, textures and all.\n\nNext I wanted to see if I could animate the cars driving around so I needed to check if the scene had the cars as separate entities and if they were setup in a way I could use them.\n\nI wrote some code to dump put the scenegraph to the JavaScript console. Here's the code to print out the scenegraph.\n\nAnd I just called it right after loading the scene. Running that I got this listing:", "code_blocks": [{"language": "javascript", "code": "function dumpObject(obj, lines = [], isLast = true, prefix = '') {\n  const localPrefix = isLast ? '└─' : '├─';\n  lines.push(${prefix}${prefix ? localPrefix : ''}${obj.name || '*no-name*'} [${obj.type}]);\n  const newPrefix = prefix + (isLast ? '  ' : '│ ');\n  const lastNdx = obj.children.length - 1;\n  obj.children.forEach((child, ndx) => {\n    const isLast = ndx === lastNdx;\n    dumpObject(child, lines, isLast, newPrefix);\n  });\n  return lines;\n}", "filename": ""}, {"language": "javascript", "code": "const gltfLoader = new GLTFLoader();\ngltfLoader.load('resources/models/cartoon_lowpoly_small_city_free_pack/scene.gltf', (gltf) => {\n  const root = gltf.scene;\n  scene.add(root);\n  console.log(dumpObject(root).join('\\n'));", "filename": ""}, {"language": "text", "code": "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          ...", "filename": ""}], "chunk_position": 41, "heading_path": "Inspecting the Scene Graph > Inspecting the Scene Graph", "breadcrumbs": "Loading a .GLTF File > Inspecting the Scene Graph > Inspecting the Scene Graph"}, {"id": "710faee6849e433e", "url": "https://threejs.org/manual/en/load-gltf.html", "page_title": "Loading a .GLTF File", "page_type": "guide", "page_summary": "A tutorial on loading and working with .GLTF files in three.js, covering loading the file, inspecting the scene graph, animating objects, creating paths, and adding shadows.", "heading": "Rotating the Cars", "content": "Page: Loading a .GLTF File\nSection: Rotating the Cars\n\nFrom that we can see all the cars happen to be under a parent called \"Cars\".\n\nSo as a simple test I thought I would just try rotating all the children of the \"Cars\" node around their Y axis.\n\nI looked up the \"Cars\" node after loading the scene and saved the result.\n\nThen in the render function we can just set the rotation of each child of cars.\n\nAnd we get... Hmmm, it looks like unfortunately this scene wasn't designed to animate the cars as their origins are not setup for that purpose. The trucks are rotating in the wrong direction.\n\nThis brings up an important point which is if you're going to do something in 3D you need to plan ahead and design your assets so they have their origins in the correct places, so they are the correct scale, etc.", "code_blocks": [{"language": "javascript", "code": "+let cars;\n{\n  const gltfLoader = new GLTFLoader();\n  gltfLoader.load('resources/models/cartoon_lowpoly_small_city_free_pack/scene.gltf', (gltf) => {\n    const root = gltf.scene;\n    scene.add(root);\n+    cars = root.getObjectByName('Cars');", "filename": ""}, {"language": "javascript", "code": "+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}", "filename": ""}], "chunk_position": 41, "heading_path": "Rotating the Cars > Rotating the Cars", "breadcrumbs": "Loading a .GLTF File > Rotating the Cars > Rotating the Cars"}, {"id": "007782283bd127de", "url": "https://threejs.org/manual/en/load-gltf.html", "page_title": "Loading a .GLTF File", "page_type": "guide", "page_summary": "A tutorial on loading and working with .GLTF files in three.js, covering loading the file, inspecting the scene graph, animating objects, creating paths, and adding shadows.", "heading": "Fixing Car Orientation", "content": "Page: Loading a .GLTF File\nSection: Fixing Car Orientation\n\nSince I'm not an artist and I don't know blender that well I will hack this example. We'll take each car and parent it to another Object3D. We will then move those Object3D objects to move the cars but separately we can set the car's original Object3D to re-orient it so it's about where we really need it.\n\nLooking back at the scene graph listing it looks like there are really only 3 types of cars, \"Car_08\", \"CAR_03\", and \"Car_04\". Hopefully each type of car will work with the same adjustments.\n\nI wrote this code to go through each car, parent it to a new Object3D, parent that new Object3D to the scene, and apply some per car type settings to fix its orientation, and add the new Object3D a cars array.\n\nThis fixes the orientation of the cars.", "code_blocks": [{"language": "javascript", "code": "-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) => {\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 => 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     ...", "filename": ""}], "chunk_position": 41, "heading_path": "Fixing Car Orientation > Fixing Car Orientation", "breadcrumbs": "Loading a .GLTF File > Fixing Car Orientation > Fixing Car Orientation"}, {"id": "034a0ab66b725715", "url": "https://threejs.org/manual/en/load-gltf.html", "page_title": "Loading a .GLTF File", "page_type": "guide", "page_summary": "A tutorial on loading and working with .GLTF files in three.js, covering loading the file, inspecting the scene graph, animating objects, creating paths, and adding shadows.", "heading": "Creating a Driving Path", "content": "Page: Loading a .GLTF File\nSection: Creating a Driving Path\n\nNow let's drive them around.\n\nMaking even a simple driving system is too much for this post but it seems instead we could just make one convoluted path that drives down all the roads and then put the cars on the path. Here's a picture from Blender about half way through building the path.\n\nI needed a way to get the data for that path out of Blender. Fortunately I was able to select just my path and export .OBJ checking \"write nurbs\".\n\nOpening the .OBJ file I was able to get a list of points which I formatted into this control points array.\n\nTHREE.js has some curve classes. The CatmullRomCurve3 seemed like it might work. The thing about that kind of curve is it tries to make a smooth curve going through the points.\n\nIn fact putting those points in directly will generate a curve like this (before image), but we want sharper corners. It seemed like if we computed some extra points we could get what we want. For each pair of points we'll compute a point 10% of the way between the 2 points and another 90% of the way between the 2 points and pass the result to CatmullRomCurve3.\n\nThis will give us a curve like this (after image).\n\nHere's the code to make the curve. The first part of that code makes a curve. The second part of that code generates 250 points from the curve and then creates an object to display the lines made by connecting those 250 points.\n\nRunning the example I didn't see the curve. To make it visible I made it ignore the depth test and render last. And that's when I discovered it was way too small.\n\nChecking the hierarchy in Blender I found out that the artist had scaled the node all the cars are parented to.\n\nScaling is bad for real time 3D apps. It causes all kinds of issues and ends up being no end of frustration when doing real time 3D. Artists often don't know this because it's so easy to scale an entire scene in a 3D editing program but if you decide to make a real time 3D app I suggest you request your artists to never scale anything. If they change the scale they should find a way to apply that scale to the vertices so that when it ends up making it to your app you can ignore scale.\n\nAnd, not just scale, in this case the cars are rotated and offset by their parent, the Cars node. This will make it hard at runtime to move the cars around in world space. To be clear, in this case we want cars to drive around in world space which is why these issues are coming up. If something that is meant to be manipulated in a local space, like the moon revolving around the earth this is less of an issue.", "code_blocks": [{"language": "javascript", "code": "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];", "filename": ""}, {"language": "javascript", "code": "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) => {\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}", "filename": ""}, {"language": "javascript", "code": "curveObject = new THREE.Line(geometry, material);\n+    material.depthTest = false;\n+    curveObject.renderOrder = 1;", "filename": ""}], "chunk_position": 41, "heading_path": "Creating a Driving Path > Creating a Driving Path", "breadcrumbs": "Loading a .GLTF File > Creating a Driving Path > Creating a Driving Path"}, {"id": "77e42745cfe80ff6", "url": "https://threejs.org/manual/en/load-gltf.html", "page_title": "Loading a .GLTF File", "page_type": "guide", "page_summary": "A tutorial on loading and working with .GLTF files in three.js, covering loading the file, inspecting the scene graph, animating objects, creating paths, and adding shadows.", "heading": "Debugging Scene Graph with Transforms", "content": "Page: Loading a .GLTF File\nSection: Debugging Scene Graph with Transforms\n\nGoing back to the function we wrote above to dump the scene graph, let's dump the position, rotation, and scale of each node.\n\nAnd the result from running it shows us that Cars in the original scene has had its rotation and scale removed and applied to its children. That suggests either whatever exporter was used to create the .GLTF file did some special work here or more likely the artist exported a different version of the file than the corresponding .blend file, which is why things don't match.\n\nThe moral of that is I should have probably downloaded the .blend file and exported myself. Before exporting I should have inspected all the major nodes and removed any transformations.\n\nAll these nodes at the top of the hierarchy are also a waste.\n\nIdeally the scene would consist of a single \"root\" node with no position, rotation, or scale. At runtime I could then pull all the children out of that root and parent them to the scene itself. There might be children of the root like \"Cars\" which would help me find all the cars but ideally it would also have no translation, rotation, or scale so I could re-parent the cars to the scene with the minimal amount of work.", "code_blocks": [{"language": "javascript", "code": "+function dumpVec3(v3, precision = 3) {\n+  return ${v3.x.toFixed(precision)}, ${v3.y.toFixed(precision)}, ${v3.z.toFixed(precision)};\n+}\n\nfunction dumpObject(obj, lines, isLast = true, prefix = '') {\n  const localPrefix = isLast ? '└─' : '├─';\n  lines.push(${prefix}${prefix ? localPrefix : ''}${obj.name || '*no-name*'} [${obj.type}]);\n+  const dataPrefix = obj.children.length\n+     ? (isLast ? '  │ ' : '│ │ ')\n+     : (isLast ? '    ' : '│   ');\n+  lines.push(${prefix}${dataPrefix}  pos: ${dumpVec3(obj.position)});\n+  lines.push(${prefix}${dataPrefix}  rot: ${dumpVec3(obj.rotation)});\n+  lines.push(${prefix}${dataPrefix}  scl: ${dumpVec3(obj.scale)});\n  const newPrefix = prefix + (isLast ? '  ' : '│ ');\n  const lastNdx = obj.children.length - 1;\n  obj.children.forEach((child, ndx) => {\n    const isLast = ndx === lastNdx;\n    dumpObject(child, lines, isLast, newPrefix);\n  });\n  return lines;\n}", "filename": ""}, {"language": "text", "code": "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", "filename": ""}], "chunk_position": 41, "heading_path": "Debugging Scene Graph with Transforms > Debugging Scene Graph with Transforms", "breadcrumbs": "Loading a .GLTF File > Debugging Scene Graph with Transforms > Debugging Scene Graph with Transforms"}, {"id": "b9fb21daeafbb96b", "url": "https://threejs.org/manual/en/load-gltf.html", "page_title": "Loading a .GLTF File", "page_type": "guide", "page_summary": "A tutorial on loading and working with .GLTF files in three.js, covering loading the file, inspecting the scene graph, animating objects, creating paths, and adding shadows.", "heading": "Moving Cars Along the Path", "content": "Page: Loading a .GLTF File\nSection: Moving Cars Along the Path\n\nIn any case the quickest though maybe not the best fix is to just adjust the object we're using to view the curve.\n\nHere's what I ended up with. First I adjusted the position of the curve and found values that seemed to work. I then hid it.\n\nThen I wrote code to move the cars along the curve. For each car we pick a position from 0 to 1 along the curve and compute a point in world space using the curveObject to transform the point. We then pick another point slightly further down the curve. We set the car's orientation using lookAt and put the car at the mid point between the 2 points.\n\nand when I ran it I found out for each type of car, their height above their origins are not consistently set and so I needed to offset each one a little.\n\nAnd the result. Not bad for a few minutes work.", "code_blocks": [{"language": "javascript", "code": "{\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}", "filename": ""}, {"language": "javascript", "code": "// 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) => {\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+  }", "filename": ""}, {"language": "javascript", "code": "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 => 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}", "filename": ""}], "chunk_position": 41, "heading_path": "Moving Cars Along the Path > Moving Cars Along the Path", "breadcrumbs": "Loading a .GLTF File > Moving Cars Along the Path > Moving Cars Along the Path"}, {"id": "786c1f7b87716171", "url": "https://threejs.org/manual/en/load-gltf.html", "page_title": "Loading a .GLTF File", "page_type": "guide", "page_summary": "A tutorial on loading and working with .GLTF files in three.js, covering loading the file, inspecting the scene graph, animating objects, creating paths, and adding shadows.", "heading": "Adding Shadows", "content": "Page: Loading a .GLTF File\nSection: Adding Shadows\n\nThe last thing I wanted to do is turn on shadows.\n\nTo do this I grabbed all the GUI code from the DirectionalLight shadows example in the article on shadows and pasted it into our latest code.\n\nThen, after loading, we need to turn on shadows on all the objects.\n\nI then spent nearly 4 hours trying to figure out why the shadow helpers were not working. It was because I forgot to enable shadows with renderer.shadowMap.enabled = true.\n\nI then adjusted the values until our DirectionLight's shadow camera had a frustum that covered the entire scene. These are the settings I ended up with.\n\nand I set the background color to light blue.\n\nAnd ... shadows!", "code_blocks": [{"language": "javascript", "code": "{\n  const gltfLoader = new GLTFLoader();\n  gltfLoader.load('resources/models/cartoon_lowpoly_small_city_free_pack/scene.gltf', (gltf) => {\n    const root = gltf.scene;\n    scene.add(root);\n\n+    root.traverse((obj) => {\n+      if (obj.castShadow !== undefined) {\n+        obj.castShadow = true;\n+        obj.receiveShadow = true;\n+      }\n+    });", "filename": ""}, {"language": "javascript", "code": "renderer.shadowMap.enabled = true;", "filename": ""}, {"language": "javascript", "code": "{\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...", "filename": ""}, {"language": "javascript", "code": "const scene = new THREE.Scene();\n-scene.background = new THREE.Color('black');\n+scene.background = new THREE.Color('#DEFEFF');", "filename": ""}], "chunk_position": 41, "heading_path": "Adding Shadows > Adding Shadows", "breadcrumbs": "Loading a .GLTF File > Adding Shadows > Adding Shadows"}, {"id": "228d2c96a7321fc0", "url": "https://threejs.org/manual/en/load-gltf.html", "page_title": "Loading a .GLTF File", "page_type": "guide", "page_summary": "A tutorial on loading and working with .GLTF files in three.js, covering loading the file, inspecting the scene graph, animating objects, creating paths, and adding shadows.", "heading": "Conclusion", "content": "Page: Loading a .GLTF File\nSection: Conclusion\n\nI hope walking through this project was useful and showed some good examples of working though some of the issues of loading a file with a scenegraph.\n\nOne interesting thing is that comparing the .blend file to the .gltf file, the .blend file has several lights but they are not lights after being loaded into the scene. A .GLTF file is just a JSON file so you can easily look inside. It consists of several arrays of things and each item in an array is referenced by index else where. While there are extensions in the works they point to a problem with almost all 3d formats. They can never cover every case.\n\nThere is always a need for more data. For example we manually exported a path for the cars to follow. Ideally that info could have been in the .GLTF file but to do that we'd need to write our own exporter and some how mark nodes for how we want them exported or use a naming scheme or something along those lines to get data from whatever tool we're using to create the data into our app.\n\nAll of that is left as an exercise to the reader.", "code_blocks": [], "chunk_position": 41, "heading_path": "Conclusion > Conclusion", "breadcrumbs": "Loading a .GLTF File > Conclusion > Conclusion"}, {"id": "78481f5fb2e3bb84", "url": "https://threejs.org/manual/en/backgrounds.html", "page_title": "Backgrounds and Skyboxes", "page_type": "reference", "page_summary": "Extraction fallback content. [stop]", "heading": "Backgrounds and Skyboxes", "content": "Page: Backgrounds and Skyboxes\nSection: Backgrounds and Skyboxes\n\n\n\n\n\n##### Backgrounds and Skyboxes\n\n\n\n\n\n\n\nMost of the articles here use a solid color for a background.\n\n\n\n\nAdding as static background can be as simple as setting some CSS. Taking\nan example from [the article on making THREE.js responsive](responsive.html)\nwe only need to change 2 things.\n\n\n\n\nWe need to add some CSS to our canvas to set its background to an image\n\n\n\n\n\nand we need to tell the [`WebGLRenderer`](/docs/#api/en/renderers/WebGLRenderer) to use `alpha` so places we are not\ndrawing anything are transparent.\n\n\n\n\n\nAnd we get a background.\n\n\n\n\n\n\n\n\n[click here to open in a separate window](/manual/examples/background-css.html)\n\n\n\n\n\n\n\n\nIf we want the background to be able to be affected by [post processing\neffects](post-processing.html) then we need to draw the background using\nTHREE.js.\n\n\n\n\nTHREE.js makes this some what simple. We can just set the background of the scene to\na texture.\n\n\n\n\n\nwhich gives us\n\n\n\n\n\n\n\n\n[click here to open in a separate window](/manual/examples/background-scene-background.html)\n\n\n\n\n\n\n\n\nThis gets us a background image but its stretched to fit the screen.\n\n\n\n\nWe can solve this issue by setting the `repeat` and `offset` properties of\nthe texture to show only a portion of image.\n\n\n\n\n\nand now THREE.js drawing the background. There is no visible difference from\nthe CSS version at the top but now if we used a [post processing\neffect](post-processing.html) the background would be affected too.\n\n\n\n\n\n\n\n\n[click here to open in a separate window](/manual/examples/background-scene-background-fixed-aspect.html)\n\n\n\n\n\n\n\n\nOf course a static background is not usually what we want in a 3D scene. Instead\nwe usually want some kind of  *skybox* . 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.\n\n\n\n\nThe 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[apply a texture](textures.html), mark it as `THREE.BackSide` 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 [`PerspectiveCamera`](/docs/#api/en/cameras/PerspectiveCamera) to\ndraw. No need for the [`OrthographicCamera`](/docs/#api/en/cameras/OrthographicCamera).\n\n\n\n\nAnother solution is to use a  *Cubemap* . 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.\n\n\n\n\nHere are the 6 images of a cubemap from the computer history museum in Mountain\nView, California.\n\n\n\n<img src=\"../examples/resources/images/cubemaps/computer-history-museum/pos-x.jpg\">\n<img src=\"../examples/resources/images/cubemaps/computer-history-museum/neg-x.jpg\">\n<img src=\"../examples/resources/images/cubemaps/computer-history-museum/pos-y.jpg\">\n\n\n<img src=\"../examples/resources/images/cubemaps/computer-history-museum/neg-y.jpg\">\n<img src=\"../examples/resources/images/cubemaps/computer-history-museum/pos-z.jpg\">\n<img src=\"../examples/resources/images/cubemaps/computer-history-museum/neg-z.jpg\">\n\n\n\nTo use them we use [`CubeTextureLoader`](/docs/#api/en/loaders/CubeTextureLoader) to load them and then use that as a the\nscene's background.\n\n\n\n\n\nAt render time we don't need to adjust the texture like we did above\n\n\n\n\n\nLet's add some controls in so we can rotate the camera.\n\n\n\n\n\n\nand try it out. Drag on the example to rotate the camera and see the cubemap\nsurrounds us.\n\n\n\n\n\n\n\n\n[click here to open in a separate window](/manual/examples/background-cubemap.html)\n\n\n\n\n\n\n\n\nAnother option is to use an Equirectangular map. This is the kind of picture a\n[360 camera](https://google.com/search?q=360+camera) takes.\n\n\n\n\n[Here's one](https://hdrihaven.com/hdri/?h=tears_of_steel_bridge) I found from\n[this site](https://hdrihaven.com).\n\n\n<img src=\"../examples/resources/images/equirectangularmaps/tears_of_steel_bridge_2k.jpg\">\n\n\n\nAnd that's all there is to it.\n\n\n\n\n\n\n\n\n[click here to open in a separate window](/manual/examples/background-equirectangularmap.html)\n\n\n\n\n\n\n\n\nRather than do it at load time you can also convert an equirectangular image\nto a cubemap beforehand. [Here's a site that will do it for you](https://matheowis.github.io/HDRI-to-CubeMap/).\n\n\n\n\n", "code_blocks": [{"language": "html", "code": "<style>\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</style>", "filename": ""}, {"language": "js", "code": "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+  });", "filename": ""}, {"language": "js", "code": "const loader = new THREE.TextureLoader();\nconst bgTexture = loader.load('resources/images/daikanyama.jpg');\nbgTexture.colorSpace = THREE.SRGBColorSpace;\nscene.background = bgTexture;", "filename": ""}, {"language": "js", "code": "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 > 1 ? (1 - 1 / aspect) / 2 : 0;\n+  bgTexture.repeat.x = aspect > 1 ? 1 / aspect : 1;\n+\n+  bgTexture.offset.y = aspect > 1 ? 0 : (1 - aspect) / 2;\n+  bgTexture.repeat.y = aspect > 1 ? 1 : aspect;\n\n  ...\n\n  renderer.render(scene, camera);\n\n  requestAnimationFrame(render);\n}", "filename": ""}, {"language": "js", "code": "{\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}", "filename": ""}, {"language": "js", "code": "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 > 1 ? (1 - 1 / aspect) / 2 : 0;\n-  bgTexture.repeat.x = aspect > 1 ? 1 / aspect : 1;\n-\n-  bgTexture.offset.y = aspect > 1 ? 0 : (1 - aspect) / 2;\n-  bgTexture.repeat.y = aspect > 1 ? 1 : aspect;\n\n  ...\n\n  renderer.render(scene, camera);\n\n  requestAnimationFrame(render);\n}", "filename": ""}, {"language": "js", "code": "import {OrbitControls} from 'three/addons/controls/OrbitControls.js';", "filename": ""}, {"language": "js", "code": "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();", "filename": ""}, {"language": "js", "code": "{\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+    () => {\n+      texture.mapping = THREE.EquirectangularReflectionMapping;\n+      texture.colorSpace = THREE.SRGBColorSpace;\n+      scene.background = texture;\n+    });\n}", "filename": ""}], "chunk_position": 42, "heading_path": "Backgrounds and Skyboxes > Backgrounds and Skyboxes", "breadcrumbs": "Backgrounds and Skyboxes > Backgrounds and Skyboxes > Backgrounds and Skyboxes"}, {"id": "bef4f7dd8f24d118", "url": "https://threejs.org/manual/en/transparency.html", "page_title": "Transparency", "page_type": "guide", "page_summary": "This guide explains how to work with transparency in three.js, covering basic transparency settings, back-face rendering issues, depth sorting problems, and various solutions like DoubleSide, alpha testing, and geometry splitting techniques.", "heading": "Introduction", "content": "Page: Transparency\nSection: Introduction\n\nTransparency in three.js is both easy and hard.\n\nFirst we'll go over the easy part. Let's make a scene with 8 cubes placed in a 2x2x2 grid.\n\nWe'll start with the example from the article on rendering on demand which had 3 cubes and modify it to have 8. First let's change our `makeInstance` function to take an x, y, and z", "code_blocks": [{"language": "javascript", "code": "-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}", "filename": ""}], "chunk_position": 43, "heading_path": "Introduction > Introduction", "breadcrumbs": "Transparency > Introduction > Introduction"}, {"id": "7958ad009c10d01e", "url": "https://threejs.org/manual/en/transparency.html", "page_title": "Transparency", "page_type": "guide", "page_summary": "This guide explains how to work with transparency in three.js, covering basic transparency settings, back-face rendering issues, depth sorting problems, and various solutions like DoubleSide, alpha testing, and geometry splitting techniques.", "heading": "Creating 8 Cubes", "content": "Page: Transparency\nSection: Creating 8 Cubes\n\nThen we can create 8 cubes", "code_blocks": [{"language": "javascript", "code": "+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+}", "filename": ""}], "chunk_position": 43, "heading_path": "Creating 8 Cubes > Creating 8 Cubes", "breadcrumbs": "Transparency > Creating 8 Cubes > Creating 8 Cubes"}, {"id": "e1448f752483550b", "url": "https://threejs.org/manual/en/transparency.html", "page_title": "Transparency", "page_type": "guide", "page_summary": "This guide explains how to work with transparency in three.js, covering basic transparency settings, back-face rendering issues, depth sorting problems, and various solutions like DoubleSide, alpha testing, and geometry splitting techniques.", "heading": "Camera Adjustments", "content": "Page: Transparency\nSection: Camera Adjustments\n\nI also adjusted the camera", "code_blocks": [{"language": "javascript", "code": "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;", "filename": ""}], "chunk_position": 43, "heading_path": "Camera Adjustments > Camera Adjustments", "breadcrumbs": "Transparency > Camera Adjustments > Camera Adjustments"}, {"id": "0b7d3e4c170bb974", "url": "https://threejs.org/manual/en/transparency.html", "page_title": "Transparency", "page_type": "guide", "page_summary": "This guide explains how to work with transparency in three.js, covering basic transparency settings, back-face rendering issues, depth sorting problems, and various solutions like DoubleSide, alpha testing, and geometry splitting techniques.", "heading": "Background and Lighting", "content": "Page: Transparency\nSection: Background and Lighting\n\nSet the background to white", "code_blocks": [{"language": "javascript", "code": "const scene = new THREE.Scene();\n+scene.background = new THREE.Color('white');", "filename": ""}], "chunk_position": 43, "heading_path": "Background and Lighting > Background and Lighting", "breadcrumbs": "Transparency > Background and Lighting > Background and Lighting"}, {"id": "49fde5af8f174f0d", "url": "https://threejs.org/manual/en/transparency.html", "page_title": "Transparency", "page_type": "guide", "page_summary": "This guide explains how to work with transparency in three.js, covering basic transparency settings, back-face rendering issues, depth sorting problems, and various solutions like DoubleSide, alpha testing, and geometry splitting techniques.", "heading": "Adding a Second Light", "content": "Page: Transparency\nSection: Adding a Second Light\n\nAnd added a second light so all sides of the cubes get some lighting.", "code_blocks": [{"language": "javascript", "code": "-{\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);", "filename": ""}], "chunk_position": 43, "heading_path": "Adding a Second Light > Adding a Second Light", "breadcrumbs": "Transparency > Adding a Second Light > Adding a Second Light"}, {"id": "3fa0a5c99730114f", "url": "https://threejs.org/manual/en/transparency.html", "page_title": "Transparency", "page_type": "guide", "page_summary": "This guide explains how to work with transparency in three.js, covering basic transparency settings, back-face rendering issues, depth sorting problems, and various solutions like DoubleSide, alpha testing, and geometry splitting techniques.", "heading": "Making Cubes Transparent", "content": "Page: Transparency\nSection: Making Cubes Transparent\n\nTo make the cubes transparent we just need to set the `transparent` flag and to set an `opacity` level with 1 being completely opaque and 0 being completely transparent.", "code_blocks": [{"language": "javascript", "code": "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}", "filename": ""}], "chunk_position": 43, "heading_path": "Making Cubes Transparent > Making Cubes Transparent", "breadcrumbs": "Transparency > Making Cubes Transparent > Making Cubes Transparent"}, {"id": "69ff5ea648dabe3f", "url": "https://threejs.org/manual/en/transparency.html", "page_title": "Transparency", "page_type": "guide", "page_summary": "This guide explains how to work with transparency in three.js, covering basic transparency settings, back-face rendering issues, depth sorting problems, and various solutions like DoubleSide, alpha testing, and geometry splitting techniques.", "heading": "The Missing Backs Problem", "content": "Page: Transparency\nSection: The Missing Backs Problem\n\nand with that we get 8 transparent cubes\n\nDrag on the example to rotate the view.\n\nSo it seems easy but ... look closer. The cubes are missing their backs.\n\nWe learned about the `side` material property in the article on materials. So, let's set it to `THREE.DoubleSide` to get both sides of each cube to be drawn.", "code_blocks": [{"language": "javascript", "code": "const material = new THREE.MeshPhongMaterial({\n  color,\n  map: loader.load(url),\n  opacity: 0.5,\n  transparent: true,\n+  side: THREE.DoubleSide,\n});", "filename": ""}], "chunk_position": 43, "heading_path": "The Missing Backs Problem > The Missing Backs Problem", "breadcrumbs": "Transparency > The Missing Backs Problem > The Missing Backs Problem"}, {"id": "8762825a24d83ef5", "url": "https://threejs.org/manual/en/transparency.html", "page_title": "Transparency", "page_type": "guide", "page_summary": "This guide explains how to work with transparency in three.js, covering basic transparency settings, back-face rendering issues, depth sorting problems, and various solutions like DoubleSide, alpha testing, and geometry splitting techniques.", "heading": "Depth Sorting Issues", "content": "Page: Transparency\nSection: Depth Sorting Issues\n\nAnd we get\n\nGive it a spin. It kind of looks like it's working as we can see backs except on closer inspection sometimes we can't.\n\nThis happens because of the way 3D objects are generally drawn. For each geometry each triangle is drawn one at a time. When each pixel of the triangle is drawn 2 things are recorded. One, the color for that pixel and two, the depth of that pixel. When the next triangle is drawn, for each pixel if the depth is deeper than the previously recorded depth no pixel is drawn.\n\nThis works great for opaque things but it fails for transparent things.\n\nThe solution is to sort transparent things and draw the stuff in back before drawing the stuff in front. THREE.js does this for objects like `Mesh` otherwise the very first example would have failed between cubes with some cubes blocking out others. Unfortunately for individual triangles shorting would be extremely slow.\n\nThe cube has 12 triangles, 2 for each face, and the order they are drawn is the same order they are built in the geometry so depending on which direction we are looking the triangles closer to the camera might get drawn first. In that case the triangles in the back aren't drawn. This is why sometimes we don't see the backs.", "code_blocks": [], "chunk_position": 43, "heading_path": "Depth Sorting Issues > Depth Sorting Issues", "breadcrumbs": "Transparency > Depth Sorting Issues > Depth Sorting Issues"}, {"id": "2b0eb92ea59309d1", "url": "https://threejs.org/manual/en/transparency.html", "page_title": "Transparency", "page_type": "guide", "page_summary": "This guide explains how to work with transparency in three.js, covering basic transparency settings, back-face rendering issues, depth sorting problems, and various solutions like DoubleSide, alpha testing, and geometry splitting techniques.", "heading": "BackSide and FrontSide Hack", "content": "Page: Transparency\nSection: BackSide and FrontSide Hack\n\nFor a convex object like a sphere or a cube one kind of solution is to add every cube to the scene twice. Once with a material that draws only the back facing triangles and another with a material that only draws the front facing triangles.", "code_blocks": [{"language": "javascript", "code": "function makeInstance(geometry, color, x, y, z) {\n+  [THREE.BackSide, THREE.FrontSide].forEach((side) => {\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}", "filename": ""}], "chunk_position": 43, "heading_path": "BackSide and FrontSide Hack > BackSide and FrontSide Hack", "breadcrumbs": "Transparency > BackSide and FrontSide Hack > BackSide and FrontSide Hack"}, {"id": "fdf1f4a5fc64cca1", "url": "https://threejs.org/manual/en/transparency.html", "page_title": "Transparency", "page_type": "guide", "page_summary": "This guide explains how to work with transparency in three.js, covering basic transparency settings, back-face rendering issues, depth sorting problems, and various solutions like DoubleSide, alpha testing, and geometry splitting techniques.", "heading": "Draw Order Assumption", "content": "Page: Transparency\nSection: Draw Order Assumption\n\nAny with that it *seems* to work.\n\nIt assumes that the three.js's sorting is stable. Meaning that because we added the `side: THREE.BackSide` mesh first and because it's at the exact same position that it will be drawn before the `side: THREE.FrontSide` mesh.", "code_blocks": [], "chunk_position": 43, "heading_path": "Draw Order Assumption > Draw Order Assumption", "breadcrumbs": "Transparency > Draw Order Assumption > Draw Order Assumption"}, {"id": "94e777cf09388e23", "url": "https://threejs.org/manual/en/transparency.html", "page_title": "Transparency", "page_type": "guide", "page_summary": "This guide explains how to work with transparency in three.js, covering basic transparency settings, back-face rendering issues, depth sorting problems, and various solutions like DoubleSide, alpha testing, and geometry splitting techniques.", "heading": "Intersecting Planes", "content": "Page: Transparency\nSection: Intersecting Planes\n\nLet's make 2 intersecting planes (after deleting all the code related to cubes). We'll add a texture to each plane.", "code_blocks": [{"language": "javascript", "code": "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');", "filename": ""}], "chunk_position": 43, "heading_path": "Intersecting Planes > Intersecting Planes", "breadcrumbs": "Transparency > Intersecting Planes > Intersecting Planes"}, {"id": "3223acdbf0671517", "url": "https://threejs.org/manual/en/transparency.html", "page_title": "Transparency", "page_type": "guide", "page_summary": "This guide explains how to work with transparency in three.js, covering basic transparency settings, back-face rendering issues, depth sorting problems, and various solutions like DoubleSide, alpha testing, and geometry splitting techniques.", "heading": "Texture Rendering on Demand", "content": "Page: Transparency\nSection: Texture Rendering on Demand\n\nThis time we can use `side: THREE.DoubleSide` since we can only ever see one side of a plane at a time. Also note we pass our `render` function to the texture loading function so that when the texture finishes loading we re-render the scene. This is because this sample is rendering on demand instead of rendering continuously.\n\nAnd again we see a similar issue.", "code_blocks": [], "chunk_position": 43, "heading_path": "Texture Rendering on Demand > Texture Rendering on Demand", "breadcrumbs": "Transparency > Texture Rendering on Demand > Texture Rendering on Demand"}, {"id": "cfc8f91be6f759f8", "url": "https://threejs.org/manual/en/transparency.html", "page_title": "Transparency", "page_type": "guide", "page_summary": "This guide explains how to work with transparency in three.js, covering basic transparency settings, back-face rendering issues, depth sorting problems, and various solutions like DoubleSide, alpha testing, and geometry splitting techniques.", "heading": "Splitting Planes Solution", "content": "Page: Transparency\nSection: Splitting Planes Solution\n\nThe solution here is to manually split the each pane into 2 panes so that there really is no intersection.", "code_blocks": [{"language": "javascript", "code": "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) => {\n    const texture = loader.load(url, render);\n+    texture.offset.x = x < 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}", "filename": ""}], "chunk_position": 43, "heading_path": "Splitting Planes Solution > Splitting Planes Solution", "breadcrumbs": "Transparency > Splitting Planes Solution > Splitting Planes Solution"}, {"id": "f8b2190e9c531f93", "url": "https://threejs.org/manual/en/transparency.html", "page_title": "Transparency", "page_type": "guide", "page_summary": "This guide explains how to work with transparency in three.js, covering basic transparency settings, back-face rendering issues, depth sorting problems, and various solutions like DoubleSide, alpha testing, and geometry splitting techniques.", "heading": "Texture Coordinate Manipulation", "content": "Page: Transparency\nSection: Texture Coordinate Manipulation\n\nHow you accomplish that is up to you. If I was using modeling package like Blender I'd probably do this manually by adjusting texture coordinates. Here though we're using `PlaneGeometry` which by default stretches the texture across the plane. Like we covered before By setting the `texture.repeat` and `texture.offset` we can scale and move the texture to get the correct half of the face texture on each plane.\n\nThe code above also makes a `Object3D` and parents the 2 planes to it. It seemed easier to rotate a parent `Object3D` than to do the math required do it without.\n\nThis solution really only works for simple things like 2 planes that are not changing their intersection position.", "code_blocks": [], "chunk_position": 43, "heading_path": "Texture Coordinate Manipulation > Texture Coordinate Manipulation", "breadcrumbs": "Transparency > Texture Coordinate Manipulation > Texture Coordinate Manipulation"}, {"id": "3a37df867d1eb4a5", "url": "https://threejs.org/manual/en/transparency.html", "page_title": "Transparency", "page_type": "guide", "page_summary": "This guide explains how to work with transparency in three.js, covering basic transparency settings, back-face rendering issues, depth sorting problems, and various solutions like DoubleSide, alpha testing, and geometry splitting techniques.", "heading": "Alpha Test Solution", "content": "Page: Transparency\nSection: Alpha Test Solution\n\nFor textured objects one more solution is to set an alpha test.\n\nAn alpha test is a level of *alpha* below which three.js will not draw the pixel. If we don't draw a pixel at all then the depth issues mentioned above disappear. For relatively sharp edged textures this works pretty well. Examples include leaf textures on a plant or tree or often a patch of grass.\n\nLet's try on the 2 planes. First let's use different textures. The textures above were 100% opaque. These 2 use transparency.\n\nGoing back to the 2 planes that intersect (before we split them) let's use these textures and set an `alphaTest`.", "code_blocks": [{"language": "javascript", "code": "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');", "filename": ""}], "chunk_position": 43, "heading_path": "Alpha Test Solution > Alpha Test Solution", "breadcrumbs": "Transparency > Alpha Test Solution > Alpha Test Solution"}, {"id": "0875226aeef414e7", "url": "https://threejs.org/manual/en/transparency.html", "page_title": "Transparency", "page_type": "guide", "page_summary": "This guide explains how to work with transparency in three.js, covering basic transparency settings, back-face rendering issues, depth sorting problems, and various solutions like DoubleSide, alpha testing, and geometry splitting techniques.", "heading": "Adding lil-gui Controls", "content": "Page: Transparency\nSection: Adding lil-gui Controls\n\nBefore we run this let's add a small UI so we can more easily play with the `alphaTest` and `transparent` settings. We'll use lil-gui like we introduced in the article on three.js's scenegraph.\n\nFirst we'll make a helper for lil-gui that sets every material in the scene to a value", "code_blocks": [{"language": "javascript", "code": "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) => {\n      if (obj.material && 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) => {\n      if (obj.material && obj.material[prop] !== undefined) {\n        obj.material[prop] = v;\n        obj.material.needsUpdate = true;\n      }\n    });\n  }\n}", "filename": ""}, {"language": "javascript", "code": "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);", "filename": ""}, {"language": "javascript", "code": "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';", "filename": ""}], "chunk_position": 43, "heading_path": "Adding lil-gui Controls > Adding lil-gui Controls", "breadcrumbs": "Transparency > Adding lil-gui Controls > Adding lil-gui Controls"}, {"id": "707fe0b664aaeec5", "url": "https://threejs.org/manual/en/transparency.html", "page_title": "Transparency", "page_type": "guide", "page_summary": "This guide explains how to work with transparency in three.js, covering basic transparency settings, back-face rendering issues, depth sorting problems, and various solutions like DoubleSide, alpha testing, and geometry splitting techniques.", "heading": "Alpha Test Limitations", "content": "Page: Transparency\nSection: Alpha Test Limitations\n\nYou can see it works but zoom in and you'll see one plane has white lines.\n\nThis is the same depth issue from before. That plane was drawn first so the plane behind is not drawn. There is no perfect solution. Adjust the `alphaTest` and/or turn off `transparent` to find a solution that fits your use case.", "code_blocks": [], "chunk_position": 43, "heading_path": "Alpha Test Limitations > Alpha Test Limitations", "breadcrumbs": "Transparency > Alpha Test Limitations > Alpha Test Limitations"}, {"id": "8b2780838d7ad266", "url": "https://threejs.org/manual/en/transparency.html", "page_title": "Transparency", "page_type": "guide", "page_summary": "This guide explains how to work with transparency in three.js, covering basic transparency settings, back-face rendering issues, depth sorting problems, and various solutions like DoubleSide, alpha testing, and geometry splitting techniques.", "heading": "Conclusion", "content": "Page: Transparency\nSection: Conclusion\n\nThe take way from this article is perfect transparency is hard. There are issues and trade offs and workarounds.\n\nFor example say you have a car. Cars usually have windshields on all 4 sides. If you want to avoid the sorting issues above you'd have to make each window its own object so that three.js can sort the windows and draw them in the correct order.\n\nIf you are making some plants or grass the alpha test solution is common.\n\nWhich solution you pick depends on your needs.", "code_blocks": [], "chunk_position": 43, "heading_path": "Conclusion > Conclusion", "breadcrumbs": "Transparency > Conclusion > Conclusion"}, {"id": "35803baf5668d45b", "url": "https://threejs.org/manual/en/multiple-scenes.html", "page_title": "Multiple Canvases Multiple Scenes", "page_type": "guide", "page_summary": "A guide explaining how to use THREE.js with multiple virtual scenes rendered into a single canvas, addressing the browser's WebGL context limit and resource sharing issues. It covers the scissor test approach, syncing with scroll, making the system generic, using HTML dataset, adding controls, and an alternative using off-screen canvas copying.", "heading": "Multiple Canvases Multiple Scenes", "content": "Page: Multiple Canvases Multiple Scenes\nSection: Multiple Canvases Multiple Scenes\n\nA common question is how to use THREE.js with multiple canvases. Let's say you want to make an e-commerce site or you want to make a page with lots of 3D diagrams. At first glance it appears easy. Just make a canvas every where you want a diagram. For each canvas make a Renderer. You'll quickly find though that you run into problems.\n\nThe browser limits how many WebGL contexts you can have. Typically that limit is around 8 of them. As soon as you create the 9th context the oldest one will be lost.\n\nWebGL resources can not be shared across contexts. That means if you want to load a 10 meg model into 2 canvases and that model uses 20 meg of textures your 10 meg model will have to be loaded twice and your textures will also be loaded twice. Nothing can be shared across contexts. This also means things have to be initialized twice, shaders compiled twice, etc. It gets worse as there are more canvases.\n\nSo what's the solution? 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 Renderer and then one Scene 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.\n\nWith this solution there is only 1 canvas so we solve both problem 1 and 2 above. We won't run into the WebGL context limit because we will only be using one context. We also won't run into the sharing issues for the same reasons.\n\nLet's start with a simple example with just 2 scenes. First we'll make the HTML\n\nThen we can setup the CSS maybe something like this\n\nWe set the canvas to fill the screen and we set its z-index to -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.\n\nNow we'll make 2 scenes each with a light and a camera. To one scene we'll add a cube and to another a diamond.\n\nAnd then we'll make a function to render each scene only if the element is on the screen. We can tell THREE.js to only render to part of the canvas by turning on the scissor test with Renderer.setScissorTest and then setting both the scissor and the viewport with Renderer.setViewport and Renderer.setScissor.\n\nAnd then our render function will just first clear the screen and then render each scene.\n\nAnd here it is\n\nYou can see where the first <span> is there's a red cube and where the second span is there's a blue diamond.", "code_blocks": [{"language": "html", "code": "<canvas id=\"c\"></canvas>\n<p>\n  <span id=\"box\" class=\"diagram left\"></span>\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</p>\n<p>\n  <span id=\"pyramid\" class=\"diagram right\"></span>\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</p>", "filename": ""}, {"language": "css", "code": "#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}", "filename": ""}, {"language": "javascript", "code": "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();", "filename": ""}, {"language": "javascript", "code": "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 < 0 ||\n      top > renderer.domElement.clientHeight ||\n      right < 0 ||\n      left > 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}", "filename": ""}, {"language": "javascript", "code": "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}", "filename": ""}], "chunk_position": 44, "heading_path": "Multiple Canvases Multiple Scenes > Multiple Canvases Multiple Scenes", "breadcrumbs": "Multiple Canvases Multiple Scenes > Multiple Canvases Multiple Scenes > Multiple Canvases Multiple Scenes"}, {"id": "59c34ef328ffa0fb", "url": "https://threejs.org/manual/en/multiple-scenes.html", "page_title": "Multiple Canvases Multiple Scenes", "page_type": "guide", "page_summary": "A guide explaining how to use THREE.js with multiple virtual scenes rendered into a single canvas, addressing the browser's WebGL context limit and resource sharing issues. It covers the scissor test approach, syncing with scroll, making the system generic, using HTML dataset, adding controls, and an alternative using off-screen canvas copying.", "heading": "Syncing up", "content": "Page: Multiple Canvases Multiple Scenes\nSection: Syncing up\n\nThe code above works but there is one minor issue. If your scenes are complicated or if for whatever reason it takes too long to render, the position of the scenes drawn into the canvas will lag behind the rest of the page.\n\nIf we give each area a border\n\nAnd we set the background of each scene\n\nAnd if we quickly scroll up and down we'll see the issue. Here's an animation of scrolling slowed down by 10x.\n\nWe can switch to a different method which has a different tradeoff. We'll switch the canvas's CSS from position: fixed to position: absolute.\n\nThen we'll set the canvas's transform to move it so the top of the canvas is at the top of whatever part the page is currently scrolled to.\n\nposition: fixed kept the canvas from scrolling at all while the rest of the page scrolled over it. position: absolute 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 the stuff in the middle of the page should match up and not slide. Here's a view of the results of the new method slowed down 10x.", "code_blocks": [{"language": "css", "code": ".diagram {\n  display: inline-block;\n  width: 5em;\n  height: 3em;\n+  border: 1px solid black;\n}", "filename": ""}, {"language": "javascript", "code": "const scene = new THREE.Scene();\n+scene.background = new THREE.Color('red');", "filename": ""}, {"language": "css", "code": "#c {\n-  position: fixed;\n+  position: absolute;", "filename": ""}, {"language": "javascript", "code": "function render(time) {\n  ...\n\n  const transform = translateY(${window.scrollY}px);\n  renderer.domElement.style.transform = transform;", "filename": ""}], "chunk_position": 44, "heading_path": "Syncing up > Syncing up", "breadcrumbs": "Multiple Canvases Multiple Scenes > Syncing up > Syncing up"}, {"id": "6940bf32efe62c4c", "url": "https://threejs.org/manual/en/multiple-scenes.html", "page_title": "Multiple Canvases Multiple Scenes", "page_type": "guide", "page_summary": "A guide explaining how to use THREE.js with multiple virtual scenes rendered into a single canvas, addressing the browser's WebGL context limit and resource sharing issues. It covers the scissor test approach, syncing with scroll, making the system generic, using HTML dataset, adding controls, and an alternative using off-screen canvas copying.", "heading": "Making it more Generic", "content": "Page: Multiple Canvases Multiple Scenes\nSection: Making it more Generic\n\nNow that we've gotten multiple scenes working let's make this just slightly more generic.\n\nWe 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.\n\nHere's the main render function\n\nYou can see it loops over sceneElements which it expects is an array of objects each of which have an elem and fn property.\n\nIt checks if the element is on screen. If it is it calls fn and passes it the current time and its rectangle.\n\nNow the setup code for each scene just adds itself to the list of scenes\n\nWith that we no longer need sceneInfo1 and sceneInfo2 and the code that was rotating the meshes is now specific to each scene.", "code_blocks": [{"language": "javascript", "code": "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 = translateY(${window.scrollY}px);\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 < 0 ||\n        top > renderer.domElement.clientHeight ||\n        right < 0 ||\n        left > 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}", "filename": ""}, {"language": "javascript", "code": "{\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) => {\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) => {\n    camera.aspect = rect.width / rect.height;\n    camera.updateProjectionMatrix();\n    mesh.rotation.y = time * .1;\n    renderer.render(scene, camera);\n  });\n}", "filename": ""}], "chunk_position": 44, "heading_path": "Making it more Generic > Making it more Generic", "breadcrumbs": "Multiple Canvases Multiple Scenes > Making it more Generic > Making it more Generic"}, {"id": "4d4884b7d2f52e40", "url": "https://threejs.org/manual/en/multiple-scenes.html", "page_title": "Multiple Canvases Multiple Scenes", "page_type": "guide", "page_summary": "A guide explaining how to use THREE.js with multiple virtual scenes rendered into a single canvas, addressing the browser's WebGL context limit and resource sharing issues. It covers the scissor test approach, syncing with scroll, making the system generic, using HTML dataset, adding controls, and an alternative using off-screen canvas copying.", "heading": "Using HTML Dataset", "content": "Page: Multiple Canvases Multiple Scenes\nSection: Using HTML Dataset\n\nOne last even more generic thing we can do is use HTML dataset. This is a way to add your own data to an HTML element. Instead of using id=\"...\" we'll use data-diagram=\"...\" like this\n\nWe can them change the CSS selector to select for that\n\nWe'll change the scene setup code to just be a map of names to scene initialization functions that return a scene render function.\n\nAnd to init we can just use querySelectorAll to find all the diagrams and call the corresponding init function for that diagram.\n\nNo change to the visuals but the code is even more generic.", "code_blocks": [{"language": "html", "code": "<canvas id=\"c\"></canvas>\n<p>\n-  <span id=\"box\" class=\"diagram left\"></span>\n+  <span data-diagram=\"box\" class=\"left\"></span>\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</p>\n<p>\n-  <span id=\"pyramid\" class=\"diagram left\"></span>\n+  <span data-diagram=\"pyramid\" class=\"right\"></span>\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</p>", "filename": ""}, {"language": "css", "code": "-.diagram\n+*[data-diagram] {\n  display: inline-block;\n  width: 5em;\n  height: 3em;\n}", "filename": ""}, {"language": "javascript", "code": "const sceneInitFunctionsByName = {\n  '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    return (time, rect) => {\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': () => {\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) => {\n      mesh.rotation.y = time * .1;\n      camera.aspect = rect.width / rect.height;\n      camera.updateProjectionMatrix();\n      renderer.render(scene, camera);\n    };\n  },\n};", "filename": ""}, {"language": "javascript", "code": "document.querySelectorAll('[data-diagram]').forEach((elem) => {\n  const sceneName = elem.dataset.diagram;\n  const sceneInitFunction = sceneInitFunctionsByName[sceneName];\n  const sceneRenderFunction = sceneInitFunction(elem);\n  addScene(elem, sceneRenderFunction);\n});", "filename": ""}], "chunk_position": 44, "heading_path": "Using HTML Dataset > Using HTML Dataset", "breadcrumbs": "Multiple Canvases Multiple Scenes > Using HTML Dataset > Using HTML Dataset"}, {"id": "5bd230d5fc16ee12", "url": "https://threejs.org/manual/en/multiple-scenes.html", "page_title": "Multiple Canvases Multiple Scenes", "page_type": "guide", "page_summary": "A guide explaining how to use THREE.js with multiple virtual scenes rendered into a single canvas, addressing the browser's WebGL context limit and resource sharing issues. It covers the scissor test approach, syncing with scroll, making the system generic, using HTML dataset, adding controls, and an alternative using off-screen canvas copying.", "heading": "Adding Controls to each element", "content": "Page: Multiple Canvases Multiple Scenes\nSection: Adding Controls to each element\n\nAdding interactively, for example a TrackballControls is just as easy. First we add the script for the control.\n\nAnd then we can add a TrackballControls to each scene passing in the element associated with that scene.\n\nYou'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 TrackballControls 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.\n\nWe need up update those controls in our render functions\n\nAnd now if you drag the objects they'll rotate.\n\nThese techniques are used on this site itself. In particular the article about primitives and the article about materials use this technique to add the various examples throughout the article.\n\nOne more solution would be to render to an off screen canvas and copy the result to a 2D canvas at each element. The advantage to this solution is there is no limit on how you can composite each separate area. With the previous solution we and a single canvas in the background. With this solution we have normal HTML elements.\n\nThe disadvantage is it's slower because a copy has to happen for each area. How much slower depends on the browser and the GPU.\n\nThe changes needed are pretty small\n\nFirst we'll change HTML as we no longer need a canvas in the page\n\nthen we'll change the CSS\n\nWe've made all canvases fill their container.\n\nNow let's change the JavaScript. First we no longer look up the canvas. Instead we create one. We also just turn on the scissor test at the beginning.\n\nThen for each scene we create a 2D rendering context and append its canvas to the element for that scene\n\nThen when rendering, if the renderer's canvas is not big enough to render this area we increase its size. As well if this area's canvas is the wrong size we change its size. Finally we set the scissor and viewport, render the scene for this area, then copy the result to the area's canvas.\n\nThe result looks the same\n\nOne other advantage to this solution is you could potentially use OffscreenCanvas to render from a web worker and still use this technique.", "code_blocks": [{"language": "javascript", "code": "import {TrackballControls} from 'three/addons/controls/TrackballControls.js';", "filename": ""}, {"language": "javascript", "code": "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    camera.add(light);\n  }\n\n  return {scene, camera, controls};\n}", "filename": ""}, {"language": "javascript", "code": "const sceneInitFunctionsByName = {\n 'box': (elem) => {\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) => {\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': (elem) => {\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) => {\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};", "filename": ""}, {"language": "html", "code": "<body>\n-  <canvas id=\"c\"></canvas>\n  ...\n</body>", "filename": ""}, {"language": "css", "code": "canvas {\n  width: 100%;\n  height: 100%;\n  display: block;\n}\n*[data-diagram] {\n  display: inline-block;\n  width: 5em;\n  height: 3em;\n}", "filename": ""}, {"language": "javascript", "code": "function main() {\n  const canvas = document.createElement('canvas');\n  const renderer = new THREE.WebGLRenderer({antialias: true, canvas, alpha: true});\n  renderer.setScissorTest(true);\n\n  ...", "filename": ""}, {"language": "javascript", "code": "const sceneElements = [];\nfunction addScene(elem, fn) {\n  const ctx = document.createElement('canvas').getContext('2d');\n  elem.appendChild(ctx.canvas);\n  sceneElements.push({elem, ctx, fn});\n}", "filename": ""}, {"language": "javascript", "code": "function render(time) {\n  time *= 0.001;\n\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 < 0 ||\n        top > window.innerHeight ||\n        right < 0 ||\n        left > window.innerWidth;\n\n    if (!isOffscreen) {\n      // make sure the renderer's canvas is big enough\n      if (rendererCanvas.width < width || rendererCanvas.height < 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}", "filename": ""}], "chunk_position": 44, "heading_path": "Adding Controls to each element > Adding Controls to each element", "breadcrumbs": "Multiple Canvases Multiple Scenes > Adding Controls to each element > Adding Controls to each element"}, {"id": "4e77b39e781ea4c2", "url": "https://threejs.org/manual/en/picking.html", "page_title": "Picking", "page_type": "guide", "page_summary": "This page explains two common methods for implementing picking (determining which object a user clicked) in THREE.js: CPU-based raycasting using the RayCaster class, and GPU-based picking using unique object colors rendered offscreen. It includes working code examples, discusses the tradeoffs of each approach, and demonstrates both implementations with a scene of 100 cubes.", "heading": "Introduction to Picking", "content": "Page: Picking\nSection: Introduction to Picking\n\n*Picking* 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.\n\nProbably the most common way of *picking* is by doing raycasting which means to *cast* a ray from the mouse through the frustum of the scene and computing which objects that ray intersects. Conceptually it's very simple.\n\nFirst 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.\n\nA 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.\n\nTHREE.js provides a `RayCaster` class that does exactly this.\n\nLet's make a scene with a 100 objects and try picking them. We'll start with an example from the article on responsive pages.", "code_blocks": [], "chunk_position": 45, "heading_path": "Introduction to Picking > Introduction to Picking", "breadcrumbs": "Picking > Introduction to Picking > Introduction to Picking"}, {"id": "783d5fae39adb013", "url": "https://threejs.org/manual/en/picking.html", "page_title": "Picking", "page_type": "guide", "page_summary": "This page explains two common methods for implementing picking (determining which object a user clicked) in THREE.js: CPU-based raycasting using the RayCaster class, and GPU-based picking using unique object colors rendered offscreen. It includes working code examples, discusses the tradeoffs of each approach, and demonstrates both implementations with a scene of 100 cubes.", "heading": "Setting Up the Scene with a Camera Pole", "content": "Page: Picking\nSection: Setting Up the Scene with a Camera Pole\n\nWe'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.", "code_blocks": [{"language": "javascript", "code": "const fov = 60;\nconst aspect = 2;  // the canvas default\nconst near = 0.1;\nconst far = 200;\nconst camera = new THREE.PerspectiveCamera(fov, aspect, near, far);\ncamera.position.z = 30;\n\nconst scene = new THREE.Scene();\nscene.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\nconst cameraPole = new THREE.Object3D();\nscene.add(cameraPole);\ncameraPole.add(camera);", "filename": ""}], "chunk_position": 45, "heading_path": "Setting Up the Scene with a Camera Pole > Setting Up the Scene with a Camera Pole", "breadcrumbs": "Picking > Setting Up the Scene with a Camera Pole > Setting Up the Scene with a Camera Pole"}, {"id": "f10bc7f4dcb80c85", "url": "https://threejs.org/manual/en/picking.html", "page_title": "Picking", "page_type": "guide", "page_summary": "This page explains two common methods for implementing picking (determining which object a user clicked) in THREE.js: CPU-based raycasting using the RayCaster class, and GPU-based picking using unique object colors rendered offscreen. It includes working code examples, discusses the tradeoffs of each approach, and demonstrates both implementations with a scene of 100 cubes.", "heading": "Spinning the Camera Pole", "content": "Page: Picking\nSection: Spinning the Camera Pole\n\nIn the `render` function we'll spin the camera pole.", "code_blocks": [{"language": "javascript", "code": "cameraPole.rotation.y = time * .1;", "filename": ""}], "chunk_position": 45, "heading_path": "Spinning the Camera Pole > Spinning the Camera Pole", "breadcrumbs": "Picking > Spinning the Camera Pole > Spinning the Camera Pole"}, {"id": "23cdaf65d647f75d", "url": "https://threejs.org/manual/en/picking.html", "page_title": "Picking", "page_type": "guide", "page_summary": "This page explains two common methods for implementing picking (determining which object a user clicked) in THREE.js: CPU-based raycasting using the RayCaster class, and GPU-based picking using unique object colors rendered offscreen. It includes working code examples, discusses the tradeoffs of each approach, and demonstrates both implementations with a scene of 100 cubes.", "heading": "Moving the Light with the Camera", "content": "Page: Picking\nSection: Moving the Light with the Camera\n\nAlso let's put the light on the camera so the light moves with it.", "code_blocks": [{"language": "javascript", "code": "scene.add(light);\ncamera.add(light);", "filename": ""}], "chunk_position": 45, "heading_path": "Moving the Light with the Camera > Moving the Light with the Camera", "breadcrumbs": "Picking > Moving the Light with the Camera > Moving the Light with the Camera"}, {"id": "a94e36e217a38f39", "url": "https://threejs.org/manual/en/picking.html", "page_title": "Picking", "page_type": "guide", "page_summary": "This page explains two common methods for implementing picking (determining which object a user clicked) in THREE.js: CPU-based raycasting using the RayCaster class, and GPU-based picking using unique object colors rendered offscreen. It includes working code examples, discusses the tradeoffs of each approach, and demonstrates both implementations with a scene of 100 cubes.", "heading": "Generating 100 Cubes", "content": "Page: Picking\nSection: Generating 100 Cubes\n\nLet's generate 100 cubes with random colors in random positions, orientations, and scales.", "code_blocks": [{"language": "javascript", "code": "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 `hsl(${rand(360) | 0}, ${rand(50, 100) | 0}%, 50%)`;\n}\n\nconst numObjects = 100;\nfor (let i = 0; i < 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}", "filename": ""}], "chunk_position": 45, "heading_path": "Generating 100 Cubes > Generating 100 Cubes", "breadcrumbs": "Picking > Generating 100 Cubes > Generating 100 Cubes"}, {"id": "4148e65bebb6570b", "url": "https://threejs.org/manual/en/picking.html", "page_title": "Picking", "page_type": "guide", "page_summary": "This page explains two common methods for implementing picking (determining which object a user clicked) in THREE.js: CPU-based raycasting using the RayCaster class, and GPU-based picking using unique object colors rendered offscreen. It includes working code examples, discusses the tradeoffs of each approach, and demonstrates both implementations with a scene of 100 cubes.", "heading": "Creating the PickHelper Class", "content": "Page: Picking\nSection: Creating the PickHelper Class\n\nLet's make a simple class to manage the picking. You can see we create a `RayCaster` and then we can call the `pick` function to cast a ray through the scene. If the ray hits something we change the color of the first thing it hits.", "code_blocks": [{"language": "javascript", "code": "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 > 1 ? 0xFFFF00 : 0xFF0000);\n    }\n  }\n}", "filename": ""}], "chunk_position": 45, "heading_path": "Creating the PickHelper Class > Creating the PickHelper Class", "breadcrumbs": "Picking > Creating the PickHelper Class > Creating the PickHelper Class"}, {"id": "1997e7388df3806d", "url": "https://threejs.org/manual/en/picking.html", "page_title": "Picking", "page_type": "guide", "page_summary": "This page explains two common methods for implementing picking (determining which object a user clicked) in THREE.js: CPU-based raycasting using the RayCaster class, and GPU-based picking using unique object colors rendered offscreen. It includes working code examples, discusses the tradeoffs of each approach, and demonstrates both implementations with a scene of 100 cubes.", "heading": "Tracking Mouse Position", "content": "Page: Picking\nSection: Tracking Mouse Position\n\nOf course we could call this function only when the user pressed the mouse *down* 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 is.\n\nNotice 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.", "code_blocks": [{"language": "javascript", "code": "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);", "filename": ""}], "chunk_position": 45, "heading_path": "Tracking Mouse Position > Tracking Mouse Position", "breadcrumbs": "Picking > Tracking Mouse Position > Tracking Mouse Position"}, {"id": "b3d0fef7eb0dc250", "url": "https://threejs.org/manual/en/picking.html", "page_title": "Picking", "page_type": "guide", "page_summary": "This page explains two common methods for implementing picking (determining which object a user clicked) in THREE.js: CPU-based raycasting using the RayCaster class, and GPU-based picking using unique object colors rendered offscreen. It includes working code examples, discusses the tradeoffs of each approach, and demonstrates both implementations with a scene of 100 cubes.", "heading": "Adding Mobile Touch Support", "content": "Page: Picking\nSection: Adding Mobile Touch Support\n\nWhile we're at it lets support mobile as well.", "code_blocks": [{"language": "javascript", "code": "window.addEventListener('touchstart', (event) => {\n  // prevent the window from scrolling\n  event.preventDefault();\n  setPickPosition(event.touches[0]);\n}, {passive: false});\n\nwindow.addEventListener('touchmove', (event) => {\n  setPickPosition(event.touches[0]);\n});\n\nwindow.addEventListener('touchend', clearPickPosition);", "filename": ""}], "chunk_position": 45, "heading_path": "Adding Mobile Touch Support > Adding Mobile Touch Support", "breadcrumbs": "Picking > Adding Mobile Touch Support > Adding Mobile Touch Support"}, {"id": "ab2b78f50ecc0d43", "url": "https://threejs.org/manual/en/picking.html", "page_title": "Picking", "page_type": "guide", "page_summary": "This page explains two common methods for implementing picking (determining which object a user clicked) in THREE.js: CPU-based raycasting using the RayCaster class, and GPU-based picking using unique object colors rendered offscreen. It includes working code examples, discusses the tradeoffs of each approach, and demonstrates both implementations with a scene of 100 cubes.", "heading": "Calling PickHelper in the Render Function", "content": "Page: Picking\nSection: Calling PickHelper in the Render Function\n\nAnd finally in our `render` function we call the `PickHelper`'s `pick` function.", "code_blocks": [{"language": "javascript", "code": "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}", "filename": ""}], "chunk_position": 45, "heading_path": "Calling PickHelper in the Render Function > Calling PickHelper in the Render Function", "breadcrumbs": "Picking > Calling PickHelper in the Render Function > Calling PickHelper in the Render Function"}, {"id": "63ca163e2ef18301", "url": "https://threejs.org/manual/en/picking.html", "page_title": "Picking", "page_type": "guide", "page_summary": "This page explains two common methods for implementing picking (determining which object a user clicked) in THREE.js: CPU-based raycasting using the RayCaster class, and GPU-based picking using unique object colors rendered offscreen. It includes working code examples, discusses the tradeoffs of each approach, and demonstrates both implementations with a scene of 100 cubes.", "heading": "Issues with Raycasting", "content": "Page: Picking\nSection: Issues with Raycasting\n\nThis appears to work great and it probably does for many use cases but there are several issues.\n\nIt's CPU based.\n\nJavaScript is going through each object and checking if the ray intersects that object's bounding box or bounding sphere. If it does then JavaScript has to go through each and every triangle in that object and check if the ray intersects the triangle.\n\nThe good part of this is JavaScript can easily compute exactly where the ray intersected the triangle and provide us with that data. For example if you wanted to put a marker where the intersection happened.\n\nThe bad part is that's a lot of work for the CPU to do. If you have objects with lots of triangles it might be slow.\n\nIt doesn't handle any strange shaders or displacements.\n\nIf you have a shader that deforms or morphs the geometry JavaScript has no knowledge of that deformation and so will give the wrong answer. For example AFAIK you can't use this method with skinned objects.\n\nIt doesn't handle transparent holes.\n\nAs an example let's apply this texture to the cubes.", "code_blocks": [], "chunk_position": 45, "heading_path": "Issues with Raycasting > Issues with Raycasting", "breadcrumbs": "Picking > Issues with Raycasting > Issues with Raycasting"}, {"id": "0ecea88b37ca9379", "url": "https://threejs.org/manual/en/picking.html", "page_title": "Picking", "page_type": "guide", "page_summary": "This page explains two common methods for implementing picking (determining which object a user clicked) in THREE.js: CPU-based raycasting using the RayCaster class, and GPU-based picking using unique object colors rendered offscreen. It includes working code examples, discusses the tradeoffs of each approach, and demonstrates both implementations with a scene of 100 cubes.", "heading": "Demonstrating the Transparency Problem", "content": "Page: Picking\nSection: Demonstrating the Transparency Problem\n\nWe'll just make these changes.", "code_blocks": [{"language": "javascript", "code": "const loader = new THREE.TextureLoader();\nconst texture = loader.load('resources/images/frame.png');\n\nconst numObjects = 100;\nfor (let i = 0; i < 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}", "filename": ""}], "chunk_position": 45, "heading_path": "Demonstrating the Transparency Problem > Demonstrating the Transparency Problem", "breadcrumbs": "Picking > Demonstrating the Transparency Problem > Demonstrating the Transparency Problem"}, {"id": "68223566e5eaab5c", "url": "https://threejs.org/manual/en/picking.html", "page_title": "Picking", "page_type": "guide", "page_summary": "This page explains two common methods for implementing picking (determining which object a user clicked) in THREE.js: CPU-based raycasting using the RayCaster class, and GPU-based picking using unique object colors rendered offscreen. It includes working code examples, discusses the tradeoffs of each approach, and demonstrates both implementations with a scene of 100 cubes.", "heading": "Why Transparent Picking Fails", "content": "Page: Picking\nSection: Why Transparent Picking Fails\n\nTry to pick something through a box and you can't. 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.", "code_blocks": [], "chunk_position": 45, "heading_path": "Why Transparent Picking Fails > Why Transparent Picking Fails", "breadcrumbs": "Picking > Why Transparent Picking Fails > Why Transparent Picking Fails"}, {"id": "3fe1684df19cfac2", "url": "https://threejs.org/manual/en/picking.html", "page_title": "Picking", "page_type": "guide", "page_summary": "This page explains two common methods for implementing picking (determining which object a user clicked) in THREE.js: CPU-based raycasting using the RayCaster class, and GPU-based picking using unique object colors rendered offscreen. It includes working code examples, discusses the tradeoffs of each approach, and demonstrates both implementations with a scene of 100 cubes.", "heading": "Introducing GPU-Based Picking", "content": "Page: Picking\nSection: Introducing GPU-Based Picking\n\nA 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.\n\nTo 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.\n\nThis 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.\n\nOne 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 `PerspectiveCamera.setViewOffset` 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.\n\nTo 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.", "code_blocks": [], "chunk_position": 45, "heading_path": "Introducing GPU-Based Picking > Introducing GPU-Based Picking", "breadcrumbs": "Picking > Introducing GPU-Based Picking > Introducing GPU-Based Picking"}, {"id": "fcfad3af7a9a7899", "url": "https://threejs.org/manual/en/picking.html", "page_title": "Picking", "page_type": "guide", "page_summary": "This page explains two common methods for implementing picking (determining which object a user clicked) in THREE.js: CPU-based raycasting using the RayCaster class, and GPU-based picking using unique object colors rendered offscreen. It includes working code examples, discusses the tradeoffs of each approach, and demonstrates both implementations with a scene of 100 cubes.", "heading": "Creating the Picking Scene", "content": "Page: Picking\nSection: Creating the Picking Scene\n\nSo, first create a second scene and make sure it clears to black.", "code_blocks": [{"language": "javascript", "code": "const scene = new THREE.Scene();\nscene.background = new THREE.Color('white');\nconst pickingScene = new THREE.Scene();\npickingScene.background = new THREE.Color(0);", "filename": ""}], "chunk_position": 45, "heading_path": "Creating the Picking Scene > Creating the Picking Scene", "breadcrumbs": "Picking > Creating the Picking Scene > Creating the Picking Scene"}, {"id": "2c721ff371ac8bd2", "url": "https://threejs.org/manual/en/picking.html", "page_title": "Picking", "page_type": "guide", "page_summary": "This page explains two common methods for implementing picking (determining which object a user clicked) in THREE.js: CPU-based raycasting using the RayCaster class, and GPU-based picking using unique object colors rendered offscreen. It includes working code examples, discusses the tradeoffs of each approach, and demonstrates both implementations with a scene of 100 cubes.", "heading": "Creating Picking Cubes for Each Object", "content": "Page: Picking\nSection: Creating Picking Cubes for Each Object\n\nThen, 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 `pickingScene`, 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.\n\nNote that we are abusing the `MeshPhongMaterial` here. By setting its `emissive` to our id and the `color` and `specular` to 0 that will end up rendering the id only where the texture's alpha is greater than `alphaTest`. We also need to set `blending` to `NoBlending` so that the id is not multiplied by alpha.\n\nNote that abusing the `MeshPhongMaterial` 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 `alphaTest`.", "code_blocks": [{"language": "javascript", "code": "const idToObject = {};\nconst numObjects = 100;\nfor (let i = 0; i < 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}", "filename": ""}], "chunk_position": 45, "heading_path": "Creating Picking Cubes for Each Object > Creating Picking Cubes for Each Object", "breadcrumbs": "Picking > Creating Picking Cubes for Each Object > Creating Picking Cubes for Each Object"}, {"id": "b0d957f1a8587c7e", "url": "https://threejs.org/manual/en/picking.html", "page_title": "Picking", "page_type": "guide", "page_summary": "This page explains two common methods for implementing picking (determining which object a user clicked) in THREE.js: CPU-based raycasting using the RayCaster class, and GPU-based picking using unique object colors rendered offscreen. It includes working code examples, discusses the tradeoffs of each approach, and demonstrates both implementations with a scene of 100 cubes.", "heading": "Updating Pick Position for Pixel-Based Picking", "content": "Page: Picking\nSection: Updating Pick Position for Pixel-Based Picking\n\nBecause we're picking from pixels instead of ray casting we can change the code that sets the pick position to just use pixels.", "code_blocks": [{"language": "javascript", "code": "function setPickPosition(event) {\n  const pos = getCanvasRelativePosition(event);\n  pickPosition.x = pos.x;\n  pickPosition.y = pos.y;\n}", "filename": ""}], "chunk_position": 45, "heading_path": "Updating Pick Position for Pixel-Based Picking > Updating Pick Position for Pixel-Based Picking", "breadcrumbs": "Picking > Updating Pick Position for Pixel-Based Picking > Updating Pick Position for Pixel-Based Picking"}, {"id": "fadada517be2730a", "url": "https://threejs.org/manual/en/picking.html", "page_title": "Picking", "page_type": "guide", "page_summary": "This page explains two common methods for implementing picking (determining which object a user clicked) in THREE.js: CPU-based raycasting using the RayCaster class, and GPU-based picking using unique object colors rendered offscreen. It includes working code examples, discusses the tradeoffs of each approach, and demonstrates both implementations with a scene of 100 cubes.", "heading": "Creating the GPUPickHelper Class", "content": "Page: Picking\nSection: Creating the GPUPickHelper Class\n\nThen let's change the `PickHelper` into a `GPUPickHelper`. It will use a `WebGLRenderTarget` like we covered the article on render targets. Our render target here is only a single pixel in size, 1x1.", "code_blocks": [{"language": "javascript", "code": "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, 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] << 16) |\n        (pixelBuffer[1] <<  8) |\n        (pixelBuffer[2]      );\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 > 1 ? 0xFFFF00 : 0xFF0000);\n    }\n  }\n}", "filename": ""}], "chunk_position": 45, "heading_path": "Creating the GPUPickHelper Class > Creating the GPUPickHelper Class", "breadcrumbs": "Picking > Creating the GPUPickHelper Class > Creating the GPUPickHelper Class"}, {"id": "cac53be81c68e50b", "url": "https://threejs.org/manual/en/picking.html", "page_title": "Picking", "page_type": "guide", "page_summary": "This page explains two common methods for implementing picking (determining which object a user clicked) in THREE.js: CPU-based raycasting using the RayCaster class, and GPU-based picking using unique object colors rendered offscreen. It includes working code examples, discusses the tradeoffs of each approach, and demonstrates both implementations with a scene of 100 cubes.", "heading": "Using the GPUPickHelper", "content": "Page: Picking\nSection: Using the GPUPickHelper\n\nThen we just need to use it, and pass it the `pickScene` instead of the `scene`. And now it should let you pick through the transparent parts.", "code_blocks": [{"language": "javascript", "code": "const pickHelper = new GPUPickHelper();\n\n  pickHelper.pick(pickPosition, pickScene, camera, time);", "filename": ""}], "chunk_position": 45, "heading_path": "Using the GPUPickHelper > Using the GPUPickHelper", "breadcrumbs": "Picking > Using the GPUPickHelper > Using the GPUPickHelper"}, {"id": "ba5c4183ce4f0f38", "url": "https://threejs.org/manual/en/picking.html", "page_title": "Picking", "page_type": "guide", "page_summary": "This page explains two common methods for implementing picking (determining which object a user clicked) in THREE.js: CPU-based raycasting using the RayCaster class, and GPU-based picking using unique object colors rendered offscreen. It includes working code examples, discusses the tradeoffs of each approach, and demonstrates both implementations with a scene of 100 cubes.", "heading": "Conclusion", "content": "Page: Picking\nSection: Conclusion\n\nI 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.", "code_blocks": [], "chunk_position": 45, "heading_path": "Conclusion > Conclusion", "breadcrumbs": "Picking > Conclusion > Conclusion"}, {"id": "fd5bba012d3cc826", "url": "https://threejs.org/manual/en/post-processing.html", "page_title": "Post Processing", "page_type": "guide", "page_summary": "An introduction to post processing in THREE.js, covering the EffectComposer pipeline, built-in passes such as RenderPass, BloomPass, FilmPass, and OutputPass, runtime parameter adjustment via uniforms, and creating custom shader passes with ShaderPass.", "heading": "Overview", "content": "Page: Post Processing\nSection: Overview\n\nPost processing generally refers to applying some kind of effect or filter to a 2D image. In the case of THREE.js we have a scene with a bunch of meshes in it. We render that scene into a 2D image. Normally that image is rendered directly into the canvas and displayed in the browser but instead we can render it to a render target and then apply some post processing effects to the result before drawing it to the canvas. It's called post processing because it happens after (post) the main scene processing.\n\nExamples of post processing are Instagram like filters, Photoshop filters, etc...\n\nTHREE.js has some example classes to help setup a post processing pipeline. The way it works is you create an EffectComposer and to it you add multiple Pass objects. You then call EffectComposer.render and it renders your scene to a render target and then applies each Pass.\n\nEach Pass can be some post processing effect like adding a vignette, blurring, applying a bloom, applying film grain, adjusting the hue, saturation, contrast, etc... and finally rendering the result to the canvas.\n\nIt's a little bit important to understand how EffectComposer functions. It creates two render targets. Let's call them rtA and rtB.\n\nThen, you call EffectComposer.addPass to add each pass in the order you want to apply them. The passes are then applied something like this. First the scene you passed into RenderPass is rendered to rtA, then rtA is passed to the next pass, whatever it is. That pass uses rtA as input to do whatever it does and writes the results to rtB. rtB is then passed to the next pass which uses rtB as input and writes back to rtA. This continues through all the passes.\n\nEach Pass has 4 basic options", "code_blocks": [], "chunk_position": 46, "heading_path": "Overview > Overview", "breadcrumbs": "Post Processing > Overview > Overview"}, {"id": "d4da9547f7b49756", "url": "https://threejs.org/manual/en/post-processing.html", "page_title": "Post Processing", "page_type": "guide", "page_summary": "An introduction to post processing in THREE.js, covering the EffectComposer pipeline, built-in passes such as RenderPass, BloomPass, FilmPass, and OutputPass, runtime parameter adjustment via uniforms, and creating custom shader passes with ShaderPass.", "heading": "enabled", "content": "Page: Post Processing\nSection: enabled\n\nWhether or not to use this pass", "code_blocks": [], "chunk_position": 46, "heading_path": "enabled > enabled", "breadcrumbs": "Post Processing > enabled > enabled"}, {"id": "019679700f3d0034", "url": "https://threejs.org/manual/en/post-processing.html", "page_title": "Post Processing", "page_type": "guide", "page_summary": "An introduction to post processing in THREE.js, covering the EffectComposer pipeline, built-in passes such as RenderPass, BloomPass, FilmPass, and OutputPass, runtime parameter adjustment via uniforms, and creating custom shader passes with ShaderPass.", "heading": "needsSwap", "content": "Page: Post Processing\nSection: needsSwap\n\nWhether or not to swap rtA and rtB after finishing this pass", "code_blocks": [], "chunk_position": 46, "heading_path": "needsSwap > needsSwap", "breadcrumbs": "Post Processing > needsSwap > needsSwap"}, {"id": "bef5ae0cb49ddd85", "url": "https://threejs.org/manual/en/post-processing.html", "page_title": "Post Processing", "page_type": "guide", "page_summary": "An introduction to post processing in THREE.js, covering the EffectComposer pipeline, built-in passes such as RenderPass, BloomPass, FilmPass, and OutputPass, runtime parameter adjustment via uniforms, and creating custom shader passes with ShaderPass.", "heading": "clear", "content": "Page: Post Processing\nSection: clear\n\nWhether or not to clear before rendering this pass", "code_blocks": [], "chunk_position": 46, "heading_path": "clear > clear", "breadcrumbs": "Post Processing > clear > clear"}, {"id": "d257f2a093a2bb86", "url": "https://threejs.org/manual/en/post-processing.html", "page_title": "Post Processing", "page_type": "guide", "page_summary": "An introduction to post processing in THREE.js, covering the EffectComposer pipeline, built-in passes such as RenderPass, BloomPass, FilmPass, and OutputPass, runtime parameter adjustment via uniforms, and creating custom shader passes with ShaderPass.", "heading": "renderToScreen", "content": "Page: Post Processing\nSection: renderToScreen\n\nWhether or not to render to the canvas instead the current destination render target. In most use cases you do not set this flag explicitly since the last pass in the pass chain is automatically rendered to screen.", "code_blocks": [], "chunk_position": 46, "heading_path": "renderToScreen > renderToScreen", "breadcrumbs": "Post Processing > renderToScreen > renderToScreen"}, {"id": "820b505505375e02", "url": "https://threejs.org/manual/en/post-processing.html", "page_title": "Post Processing", "page_type": "guide", "page_summary": "An introduction to post processing in THREE.js, covering the EffectComposer pipeline, built-in passes such as RenderPass, BloomPass, FilmPass, and OutputPass, runtime parameter adjustment via uniforms, and creating custom shader passes with ShaderPass.", "heading": "Basic Example", "content": "Page: Post Processing\nSection: Basic Example\n\nLet's put together a basic example. We'll start with the example from the article on responsiveness.\n\nTo that first we create an EffectComposer.\n\nThen as the first pass we add a RenderPass that will render our scene with our camera into the first render target.\n\nNext we add a BloomPass. A BloomPass renders its input to a generally smaller render target and blurs the result. It then adds that blurred result on top of the original input. This makes the scene bloom.\n\nNext we had a FilmPass that draws noise and scanlines on top of its input.\n\nFinally we had a OutputPass which performs color space conversion to sRGB and optional tone mapping. This pass is usually the last pass of the pass chain.\n\nTo use these classes we need to import a bunch of scripts.\n\nFor pretty much any post processing EffectComposer.js, RenderPass.js and OutputPass.js are required.\n\nThe last things we need to do are to use EffectComposer.render instead of WebGLRenderer.render and to tell the EffectComposer to match the size of the canvas.\n\nEffectComposer.render takes a deltaTime which is the time in seconds since the last frame was rendered. It passes this to the various effects in case any of them are animated. In this case the FilmPass is animated.", "code_blocks": [{"language": "javascript", "code": "const composer = new EffectComposer(renderer);", "filename": ""}, {"language": "javascript", "code": "composer.addPass(new RenderPass(scene, camera));", "filename": ""}, {"language": "javascript", "code": "const bloomPass = new BloomPass(\n    1,    // strength\n    25,   // kernel size\n    4,    // sigma ?\n    256,  // blur render target resolution\n);\ncomposer.addPass(bloomPass);", "filename": ""}, {"language": "javascript", "code": "const filmPass = new FilmPass(\n    0.5,   // intensity\n    false,  // grayscale\n);\ncomposer.addPass(filmPass);", "filename": ""}, {"language": "javascript", "code": "const outputPass = new OutputPass();\ncomposer.addPass(outputPass);", "filename": ""}, {"language": "javascript", "code": "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';", "filename": ""}, {"language": "javascript", "code": "-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) => {\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}", "filename": ""}], "chunk_position": 46, "heading_path": "Basic Example > Basic Example", "breadcrumbs": "Post Processing > Basic Example > Basic Example"}, {"id": "2b02d46138135fff", "url": "https://threejs.org/manual/en/post-processing.html", "page_title": "Post Processing", "page_type": "guide", "page_summary": "An introduction to post processing in THREE.js, covering the EffectComposer pipeline, built-in passes such as RenderPass, BloomPass, FilmPass, and OutputPass, runtime parameter adjustment via uniforms, and creating custom shader passes with ShaderPass.", "heading": "Runtime Parameter Adjustment", "content": "Page: Post Processing\nSection: Runtime Parameter Adjustment\n\nTo change effect parameters at runtime usually requires setting uniform values. Let's add a gui to adjust some of the parameters. Figuring out which values you can easily adjust and how to adjust them requires digging through the code for that effect.\n\nLooking inside BloomPass.js I found this line:\n\nSo we can set the strength by setting\n\nSimilarly looking in FilmPass.js I found these lines:\n\nSo which makes it pretty clear how to set them.\n\nLet's make a quick GUI to set those values\n\nand\n\nand now we can adjust those settings", "code_blocks": [{"language": "javascript", "code": "this.combineUniforms[ 'strength' ].value = strength;", "filename": ""}, {"language": "javascript", "code": "bloomPass.combineUniforms.strength.value = someValue;", "filename": ""}, {"language": "javascript", "code": "this.uniforms.intensity.value = intensity;\nthis.uniforms.grayscale.value = grayscale;", "filename": ""}, {"language": "javascript", "code": "import {GUI} from 'three/addons/libs/lil-gui.module.min.js';", "filename": ""}, {"language": "javascript", "code": "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}", "filename": ""}], "chunk_position": 46, "heading_path": "Runtime Parameter Adjustment > Runtime Parameter Adjustment", "breadcrumbs": "Post Processing > Runtime Parameter Adjustment > Runtime Parameter Adjustment"}, {"id": "9e974395778e02ea", "url": "https://threejs.org/manual/en/post-processing.html", "page_title": "Post Processing", "page_type": "guide", "page_summary": "An introduction to post processing in THREE.js, covering the EffectComposer pipeline, built-in passes such as RenderPass, BloomPass, FilmPass, and OutputPass, runtime parameter adjustment via uniforms, and creating custom shader passes with ShaderPass.", "heading": "Custom Shaders", "content": "Page: Post Processing\nSection: Custom Shaders\n\nThat was a small step to making our own effect.\n\nPost processing effects use shaders. Shaders are written in a language called GLSL (Graphics Library Shading Language). Going over the entire language is way too large a topic for these articles. A few resources to get start from would be maybe this article and maybe the Book of Shaders.\n\nI think an example to get you started would be helpful though so let's make a simple GLSL post processing shader. We'll make one that lets us multiply the image by a color.\n\nFor post processing THREE.js provides a useful helper called the ShaderPass. It takes an object with info defining a vertex shader, a fragment shader, and the default inputs. It will handling setting up which texture to read from to get the previous pass's results and where to render to, either one of the EffectComposer's render target or the canvas.\n\nHere's a simple post processing shader that multiplies the previous pass's result by a color.\n\nAbove tDiffuse is the name that ShaderPass uses to pass in the previous pass's result texture so we pretty much always need that. We then declare color as a THREE.js Color.\n\nNext we need a vertex shader. For post processing the vertex shader shown here is pretty much standard and rarely needs to be changed. Without going into too many details (see articles linked above) the variables uv, projectionMatrix, modelViewMatrix and position are all magically added by THREE.js.\n\nFinally we create a fragment shader. In it we get a pixel color from the previous pass with this line\n\nwe multiply it by our color and set gl_FragColor to the result\n\nAdding some simple GUI to set the 3 values of the color\n\nGives us a simple postprocessing effect that multiplies by a color.", "code_blocks": [{"language": "javascript", "code": "const colorShader = {\n  uniforms: {\n    tDiffuse: { value: null },\n    color:    { value: new THREE.Color(0x88CCFF) },\n  },\n  vertexShader: \n    varying vec2 vUv;\n    void main() {\n      vUv = uv;\n      gl_Position = projectionMatrix * modelViewMatrix * vec4(position, 1);\n    }\n  ,\n  fragmentShader: \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  ,\n};", "filename": ""}, {"language": "glsl", "code": "vec4 previousPassColor = texture2D(tDiffuse, vUv);", "filename": ""}, {"language": "glsl", "code": "gl_FragColor = vec4(\n    previousPassColor.rgb * color,\n    previousPassColor.a);", "filename": ""}, {"language": "javascript", "code": "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');", "filename": ""}], "chunk_position": 46, "heading_path": "Custom Shaders > Custom Shaders", "breadcrumbs": "Post Processing > Custom Shaders > Custom Shaders"}, {"id": "0ab13c29010703b5", "url": "https://threejs.org/manual/en/post-processing.html", "page_title": "Post Processing", "page_type": "guide", "page_summary": "An introduction to post processing in THREE.js, covering the EffectComposer pipeline, built-in passes such as RenderPass, BloomPass, FilmPass, and OutputPass, runtime parameter adjustment via uniforms, and creating custom shader passes with ShaderPass.", "heading": "Additional Resources", "content": "Page: Post Processing\nSection: Additional Resources\n\nAs mentioned about all the details of how to write GLSL and custom shaders is too much for these articles. If you really want to know how WebGL itself works then check out these articles. Another great resources is just to read through the existing post processing shaders in the THREE.js repo. Some are more complicated than others but if you start with the smaller ones you can hopefully get an idea of how they work.\n\nMost of the post processing effects in the THREE.js repo are unfortunately undocumented so to use them you'll have to read through the examples or the code for the effects themselves. Hopefully these simple example and the article on render targets provide enough context to get started.", "code_blocks": [], "chunk_position": 46, "heading_path": "Additional Resources > Additional Resources", "breadcrumbs": "Post Processing > Additional Resources > Additional Resources"}, {"id": "5c62457b8a397ddf", "url": "https://threejs.org/manual/en/shadertoy.html", "page_title": "Three.js and Shadertoy", "page_type": "guide", "page_summary": "A guide explaining how to integrate Shadertoy shaders into Three.js, covering shader setup, uniform handling, texture inputs, vertex shaders, and procedural textures.", "heading": "Three.js and Shadertoy", "content": "Page: Three.js and Shadertoy\nSection: Three.js and Shadertoy\n\nShadertoy is a famous website hosting amazing shader experiments. People often ask how they can use those shaders with Three.js.\n\nIt's important to recognize it's called Shader TOY for a reason. In general shadertoy shaders are not about best practices. Rather they are a fun challenge similar to say dwitter (write code in 140 characters) or js13kGames (make a game in 13k or less).\n\nIn the case of Shadertoy the puzzle is, write a function that for a given pixel location outputs a color that draws something interesting. It's a fun challenge and many of the result are amazing. But, it is not best practice.\n\nCompare this amazing shadertoy shader that draws an entire city\n\nFullscreen on my GPU it runs at about 5 frames a second. Contrast that to a game like Cities: Skylines\n\nThis game runs 30-60 frames a second on the same machine because it uses more traditional techniques, drawing buildings made from triangles with textures on them, etc...\n\nStill, let's go over using a Shadertoy shader with three.js.\n\nThis is the default shadertoy shader if you pick \"New\" on shadertoy.com, at least as of January 2019.\n\nOne thing important to understand about shaders is they are written in a language called GLSL (Graphics Library Shading Language) designed for 3D math which includes special types. Above we see vec4, vec2, vec3 as 3 such special types. A vec2 has 2 values, a vec3 3, a vec4 4 values. They can be addressed in a bunch of ways. The most common ways are with x, y, z, and w as in\n\nUnlike JavaScript, GLSL is more like C/C++ where variables have to have their type declared so instead of var v = 1.2; it's float v = 1.2; declaring v to be a floating point number.\n\nExplaining GLSL in detail is more than we can do in this article. For a quick overview see this article and maybe follow that up with this series.\n\nIt should be noted that, at least as of January 2019, shadertoy.com only concerns itself with fragment shaders. A fragment shader's responsibility is, given a pixel location output a color for that pixel.\n\nLooking at the function above we can see the shader has an out parameter called fragColor. out stands for output. It's a parameter the function is expected to provide a value for. We need to set this to some color.\n\nIt also has an in (for input) parameter called fragCoord. This is the pixel coordinate that is about to be drawn. We can use that coordinate to decide on a color. If the canvas we're drawing to is 400x300 pixels then the function will be called 400x300 times or 120,000 times. Each time fragCoord will be a different pixel coordinate.\n\nThere are 2 more variables being used that are not defined in the code. One is iResolution. This is set to the resolution of the canvas. If the canvas is 400x300 then iResolution would be 400,300 so as the pixel coordinates change that makes uv go from 0.0 to 1.0 across and up the texture. Working with normalized values often makes things easier and so the majority of shadertoy shaders start with something like this.\n\nThe other undefined variable in the shader is iTime. This is the time since the page loaded in seconds.\n\nIn shader jargon these global variables are called uniform variables. They are called uniform because they don't change, they stay uniform from one iteration of the shader to the next. It's important to note all of them are specific to shadertoy. They not official GLSL variables. They are variables the makers of shadertoy made up.\n\nThe Shadertoy docs define several more. For now let's write something that handles the two being used in the shader above.\n\nThe first thing to do is let's make a single plane that fills the canvas. If you haven't read it yet we did this in the article on backgrounds so let's grab that example but remove the cubes. It's pretty short so here's the entire thing\n\nAs explained in the backgrounds article an OrthographicCamera with these parameters and a 2 unit plane will fill the canvas. For now all we'll get is a red canvas as our plane is using a red MeshBasicMaterial.\n\nNow that we have something working let's add the shadertoy shader.\n\nAbove we declared the 2 uniform variables we talked about. Then we inserted the shader GLSL code from shadertoy. Finally we called mainImage passing it gl_FragColor and gl_FragCoord.xy. gl_FragColor is an official WebGL global variable the shader is responsible for setting to whatever color it wants the current pixel to be. gl_FragCoord is another official WebGL global variable that tells us the coordinate of the pixel we're currently choosing a color for.\n\nWe then need to setup three.js uniforms so we can supply values to the shader.\n\nEach uniform in THREE.js has value parameter. That value has to match the type of the uniform.\n\nThen we pass both the fragment shader and uniforms to a ShaderMaterial.\n\nand before rendering we need to set the values of the uniforms\n\nNote: I have no idea why iResolution is a vec3 and what's in the 3rd value is not documented on shadertoy.com. It's not used above so just setting it to 1 for now. ¯\\_(ツ)_/¯\n\nThis matches what we see on Shadertoy for a new shader, at least as of January 2019 😉. What's the shader above doing?\n\n- uv goes from 0 to 1.\n- cos(uv.xyx) gives us 3 cosine values as a vec3. One for uv.x, another for uv.y and another for uv.x again.\n- Adding in the time, cos(iTime+uv.xyx) makes them animate.\n- Adding in vec3(0,2,4) as in cos(iTime+uv.xyx+vec3(0,2,4)) offsets the cosine waves\n- cos goes from -1 to 1 so the 0.5 * 0.5 + cos(...) converts from -1 <-> 1 to 0.0 <-> 1.0\n- the results are then used as the RGB color for the current pixel\n\nA minor change will make it easier to see the cosine waves. Right now uv only goes from 0 to 1. A cosine repeats at 2π so let's make it go from 0 to 40 by multiplying by 40.0. That should make it repeat about 6.3 times.\n\nCounting below I see about 6.3 repeats. We can see the blue between the red since it's offset by 4 via the +vec3(0,2,4). Without that the blue and red would overlap perfectly making purple.\n\nKnowing how simple the inputs are and then seeing results like a city canal, a forest, a snail, a mushroom make the challenge all that much more impressive. Hopefully they also make it clear why it's not generally the right approach vs the more traditional ways of making scenes from triangles. The fact that so much math has to be put into computing the color of every pixel means those examples run very slow.\n\nSome shadertoy shaders take textures as inputs like this one.\n\nPassing a texture into a shader is similar to passing one into a normal material but we need to set up the texture on the uniforms.\n\nFirst we'll add the uniform for the texture to the shader. They're referred to as sampler2D in GLSL.\n\nThen we can load a texture like we covered here and assign the uniform's value.\n\nSo far we've been using Shadertoy shaders as they are used on Shadertoy.com, namely drawing to cover the canvas. There's no reason we need to limit it to just that use case though. The important part to remember is the functions people write on shadertoy generally just take a fragCoord input and a iResolution. fragCoord does not have to come from pixel coordinates, we could use something else like texture coordinates instead and could then use them kind of like other textures. This technique of using a function to generate textures is often called a procedural texture.\n\nLet's change the shader above to do this. The simplest thing to do might be to take the texture coordinates that three.js normally supplies, multiply them by iResolution and pass that in for fragCoords.\n\nTo do that we add in a varying. A varying is a value passed from the vertex shader to the fragment shader that gets interpolated (or varied) between vertices. To use it in our fragment shader we declare it. Three.js refers to its texture coordinates as uv with the v in front meaning varying.\n\nThen we need to also provide our own vertex shader. Here is a fairly common minimal three.js vertex shader. Three.js declares and will provide values for uv, projectionMatrix, modelViewMatrix, and position.\n\nWe need to pass the vertex shader to the ShaderMaterial\n\nWe can set the iResolution uniform value at init time since it will no longer change.\n\nand we no longer need to set it at render time\n\nOtherwise I copied back in the original camera and code that sets up 3 rotating cubes from the article on responsiveness. The result:\n\nI hope this at least gets you started on how to use a shadertoy shader with three.js. Again, it's important to remember that most shadertoy shaders are an interesting challenge (draw everything with a single function) rather than the recommended way to actually display things in a performant way. Still, they are amazing, impressive, beautiful, and you can learn a ton by seeing how they work.", "code_blocks": [{"language": "glsl", "code": "// 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}", "filename": ""}, {"language": "glsl", "code": "vec4 v1 = vec4(1.0, 2.0, 3.0, 4.0);\nfloat v2 = v1.x + v1.y;  // adds 1.0 + 2.0", "filename": ""}, {"language": "javascript", "code": "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();", "filename": ""}, {"language": "glsl", "code": "const fragmentShader = \n#include <common>\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;", "filename": ""}, {"language": "javascript", "code": "const uniforms = {\n  iTime: { value: 0 },\n  iResolution:  { value: new THREE.Vector3() },\n};", "filename": ""}, {"language": "javascript", "code": "-const material = new THREE.MeshBasicMaterial({\n-    color: 'red',\n-});\n+const material = new THREE.ShaderMaterial({\n+  fragmentShader,\n+  uniforms,\n+});", "filename": ""}, {"language": "javascript", "code": "-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}", "filename": ""}, {"language": "glsl", "code": "-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));", "filename": ""}, {"language": "glsl", "code": "// 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}", "filename": ""}, {"language": "glsl", "code": "const fragmentShader = \n#include <common>\n\nuniform vec3 iResolution;\nuniform float iTime;\n+uniform sampler2D iChannel0;\n\n...", "filename": ""}, {"language": "javascript", "code": "+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};", "filename": ""}, {"language": "glsl", "code": "+varying vec2 vUv;\n\nvoid main() {\n-  mainImage(gl_FragColor, gl_FragCoord.xy);\n+  mainImage(gl_FragColor, vUv * iResolution.xy);\n}", "filename": ""}, {"language": "glsl", "code": "const vertexShader = \n  varying vec2 vUv;\n  void main() {\n    vUv = uv;\n    gl_Position = projectionMatrix * modelViewMatrix * vec4( position, 1.0 );\n  }\n`;", "filename": ""}, {"language": "javascript", "code": "const material = new THREE.ShaderMaterial({\n  vertexShader,\n  fragmentShader,\n  uniforms,\n});", "filename": ""}, {"language": "javascript", "code": "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};", "filename": ""}, {"language": "javascript", "code": "-const canvas = renderer.domElement;\n-uniforms.iResolution.value.set(canvas.width, canvas.height, 1);\nuniforms.iTime.value = time;", "filename": ""}], "chunk_position": 47, "heading_path": "Three.js and Shadertoy > Three.js and Shadertoy", "breadcrumbs": "Three.js and Shadertoy > Three.js and Shadertoy > Three.js and Shadertoy"}, {"id": "896703a978cc644a", "url": "https://threejs.org/manual/en/align-html-elements-to-3d.html", "page_title": "Aligning HTML Elements to 3D", "page_type": "guide", "page_summary": "This guide explains how to align HTML elements to 3D objects in a three.js scene, covering basic positioning, handling overlapping labels, frustum checks for off-screen objects, zIndex sorting for correct stacking order, and advanced techniques like dot-product visibility checks for a globe example.", "heading": "Introduction", "content": "Page: Aligning HTML Elements to 3D\nSection: Introduction\n\nThis article is part of a series of articles about three.js. The first article is three.js fundamentals. If you haven't read that yet and you're new to three.js you might want to consider starting there.\n\nSometimes you'd like to display some text in your 3D scene. You have many options each with pluses and minuses.\n\n- Use 3D text\n\nIf you look at the primitives article you'll see TextGeometry which makes 3D text. This might be useful for flying logos but probably not so useful for stats, info, or labelling lots of things.\n\n- Use a texture with 2D text drawn into it.\n\nThe article on using a Canvas as a texture shows using a canvas as a texture. You can draw text into a canvas and display it as a billboard. The plus here might be that the text is integrated into the 3D scene. For something like a computer terminal shown in a 3D scene this might be perfect.\n\n- Use HTML Elements and position them to match the 3D\n\nThe benefits to this approach is you can use all of HTML. Your HTML can have multiple elements. It can by styled with CSS. It can also be selected by the user as it is actual text.\n\nThis article will cover this last approach.", "code_blocks": [{"language": "javascript", "code": "import * as THREE from 'three';\nimport {OrbitControls} from 'three/addons/controls/OrbitControls.js';", "filename": ""}], "chunk_position": 48, "heading_path": "Introduction > Introduction", "breadcrumbs": "Aligning HTML Elements to 3D > Introduction > Introduction"}, {"id": "11fb00d3ecb0cfb8", "url": "https://threejs.org/manual/en/align-html-elements-to-3d.html", "page_title": "Aligning HTML Elements to 3D", "page_type": "guide", "page_summary": "This guide explains how to align HTML elements to 3D objects in a three.js scene, covering basic positioning, handling overlapping labels, frustum checks for off-screen objects, zIndex sorting for correct stacking order, and advanced techniques like dot-product visibility checks for a globe example.", "heading": "Basic Setup", "content": "Page: Aligning HTML Elements to 3D\nSection: Basic Setup\n\nLet's start simple. We'll make a 3D scene with a few primitives and then add a label to each primitive. We'll start with an example from the article on responsive pages.\n\nWe'll add some OrbitControls like we did in the article on lighting.\n\nWe need to provide an HTML element to contain our label elements.\n\nBy putting both the canvas and the `<div id=\"labels\">` inside a parent container we can make them overlap with this CSS.\n\nlet's also add some CSS for the labels themselves.\n\nNow into our code we don't have to add too much. We had a function `makeInstance` that we used to generate cubes. Let's make it so it also adds a label element.\n\nAs you can see we're adding a `<div>` to the container, one for each cube. We're also returning an object with both the `cube` and the `elem` for the label.\n\nCalling it we need to provide a name for each.\n\nWhat remains is positioning the label elements at render time.\n\nAnd with that we have labels aligned to their corresponding objects.", "code_blocks": [{"language": "javascript", "code": "const controls = new OrbitControls(camera, canvas);\ncontrols.target.set(0, 0, 0);\ncontrols.update();", "filename": ""}, {"language": "html", "code": "<body>\n  <div id=\"container\">\n    <canvas id=\"c\"></canvas>\n    <div id=\"labels\"></div>\n  </div>\n</body>", "filename": ""}, {"language": "css", "code": "#c {\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}", "filename": ""}, {"language": "css", "code": "#labels>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>div:hover {\n  color: red;\n}", "filename": ""}, {"language": "javascript", "code": "const labelContainerElem = document.querySelector('#labels');\n\nfunction 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, elem};\n}", "filename": ""}, {"language": "javascript", "code": "const cubes = [\n  makeInstance(geometry, 0x44aa88,  0, 'Aqua'),\n  makeInstance(geometry, 0x8844aa, -2, 'Purple'),\n  makeInstance(geometry, 0xaa8844,  2, 'Gold'),\n];", "filename": ""}, {"language": "javascript", "code": "const tempV = new THREE.Vector3();\n\n...\n\ncubes.forEach((cubeInfo, ndx) => {\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 = translate(-50%, -50%) translate(${x}px,${y}px);\n});", "filename": ""}], "chunk_position": 48, "heading_path": "Basic Setup > Basic Setup", "breadcrumbs": "Aligning HTML Elements to 3D > Basic Setup > Basic Setup"}, {"id": "6ab546126d133a9d", "url": "https://threejs.org/manual/en/align-html-elements-to-3d.html", "page_title": "Aligning HTML Elements to 3D", "page_type": "guide", "page_summary": "This guide explains how to align HTML elements to 3D objects in a three.js scene, covering basic positioning, handling overlapping labels, frustum checks for off-screen objects, zIndex sorting for correct stacking order, and advanced techniques like dot-product visibility checks for a globe example.", "heading": "Handling Overlapping Labels", "content": "Page: Aligning HTML Elements to 3D\nSection: Handling Overlapping Labels\n\nThere are a couple of issues we probably want to deal with.\n\nOne is that if we rotate the objects so they overlap all the labels overlap as well.\n\nAnother is that if we zoom way out so that the objects go outside the frustum the labels will still appear.\n\nA possible solution to the problem of overlapping objects is to use the picking code from the article on picking. We'll pass in the position of the object on the screen and then ask the RayCaster to tell us which objects were intersected. If our object is not the first one then we are not in the front.\n\nThis handles overlapping.", "code_blocks": [{"language": "javascript", "code": "const tempV = new THREE.Vector3();\nconst raycaster = new THREE.Raycaster();\n\n...\n\ncubes.forEach((cubeInfo, ndx) => {\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 && 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 = translate(-50%, -50%) translate(${x}px,${y}px);\n  }\n});", "filename": ""}], "chunk_position": 48, "heading_path": "Handling Overlapping Labels > Handling Overlapping Labels", "breadcrumbs": "Aligning HTML Elements to 3D > Handling Overlapping Labels > Handling Overlapping Labels"}, {"id": "0fcdb4d100ddbf81", "url": "https://threejs.org/manual/en/align-html-elements-to-3d.html", "page_title": "Aligning HTML Elements to 3D", "page_type": "guide", "page_summary": "This guide explains how to align HTML elements to 3D objects in a three.js scene, covering basic positioning, handling overlapping labels, frustum checks for off-screen objects, zIndex sorting for correct stacking order, and advanced techniques like dot-product visibility checks for a globe example.", "heading": "Handling Labels Outside the Frustum", "content": "Page: Aligning HTML Elements to 3D\nSection: Handling Labels Outside the Frustum\n\nTo handle going outside the frustum we can add this check if the origin of the object is outside the frustum by checking tempV.z.\n\nThis *kind of* works because the normalized coordinates we computed include a z value that goes from -1 when at the near part of our camera frustum to +1 when at the far part of our camera frustum.\n\nFor the frustum check, the solution above fails as we're only checking the origin of the object. For a large object. That origin might go outside the frustum but half of the object might still be in the frustum.\n\nA more correct solution would be to check if the object itself is in the frustum or not. Unfortunate that check is slow. For 3 cubes it will not be a problem but for many objects it might be.\n\nThree.js provides some functions to check if an object's bounding sphere is in a frustum.\n\nOur current overlapping solution has similar issues. Picking is slow. We could use gpu based picking like we covered in the picking article but that is also not free. Which solution you chose depends on your needs.", "code_blocks": [{"language": "javascript", "code": "if (!show || Math.abs(tempV.z) > 1) {\n    // hide the label\n    elem.style.display = 'none';", "filename": ""}, {"language": "javascript", "code": "// 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));", "filename": ""}], "chunk_position": 48, "heading_path": "Handling Labels Outside the Frustum > Handling Labels Outside the Frustum", "breadcrumbs": "Aligning HTML Elements to 3D > Handling Labels Outside the Frustum > Handling Labels Outside the Frustum"}, {"id": "ce51d161aea8202a", "url": "https://threejs.org/manual/en/align-html-elements-to-3d.html", "page_title": "Aligning HTML Elements to 3D", "page_type": "guide", "page_summary": "This guide explains how to align HTML elements to 3D objects in a three.js scene, covering basic positioning, handling overlapping labels, frustum checks for off-screen objects, zIndex sorting for correct stacking order, and advanced techniques like dot-product visibility checks for a globe example.", "heading": "Sorting Labels with zIndex", "content": "Page: Aligning HTML Elements to 3D\nSection: Sorting Labels with zIndex\n\nAnother issue is the order the labels appear. If we change the code to have longer labels and set the CSS so these don't wrap, then we can run into an issue where the purple box is in the back but its label is in front of the aqua box.\n\nWe can fix this by setting the zIndex of each element. The projected position has a z value that goes from -1 in front to positive 1 in back. zIndex is required to be an integer and goes the opposite direction meaning for zIndex greater values are in front so the following code should work.\n\nBecause of the way the projected z value works we need to pick a large number to spread out the values otherwise many will have the same value. To make sure the labels don't overlap with other parts of the page we can tell the browser to create a new stacking context by setting the z-index of the container of the labels.\n\nAnd now the labels should always be in the correct order.", "code_blocks": [{"language": "javascript", "code": "const cubes = [\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];", "filename": ""}, {"language": "css", "code": "#labels>div {\n  white-space: nowrap;", "filename": ""}, {"language": "javascript", "code": "// 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 = translate(-50%, -50%) translate(${x}px,${y}px);\n\n// set the zIndex for sorting\nelem.style.zIndex = (-tempV.z * .5 + .5) * 100000 | 0;", "filename": ""}, {"language": "css", "code": "#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}", "filename": ""}], "chunk_position": 48, "heading_path": "Sorting Labels with zIndex > Sorting Labels with zIndex", "breadcrumbs": "Aligning HTML Elements to 3D > Sorting Labels with zIndex > Sorting Labels with zIndex"}, {"id": "d4cfae9bd11cce56", "url": "https://threejs.org/manual/en/align-html-elements-to-3d.html", "page_title": "Aligning HTML Elements to 3D", "page_type": "guide", "page_summary": "This guide explains how to align HTML elements to 3D objects in a three.js scene, covering basic positioning, handling overlapping labels, frustum checks for off-screen objects, zIndex sorting for correct stacking order, and advanced techniques like dot-product visibility checks for a globe example.", "heading": "Globe Example - Loading Country Data", "content": "Page: Aligning HTML Elements to 3D\nSection: Globe Example - Loading Country Data\n\nWhile we're at it let's do one more example to show one more issue. Let's draw a globe like Google Maps and label the countries.\n\nI found this data which contains the borders of countries. It's licensed as CC-BY-SA.\n\nI wrote some code to load the data, and generate country outlines and some JSON data with the names of the countries and their locations.\n\nThe JSON data is an array of entries something like this where min, max, lat, lon, are all in latitude and longitude degrees.\n\nLet's load it up. The code is based on the examples from optimizing lots of objects though we are not drawing lots of objects we'll be using the same solutions for rendering on demand.\n\nThe first thing is to make a sphere and use the outline texture.\n\nThen let's load the JSON file by first making a loader and then calling it.", "code_blocks": [{"language": "json", "code": "[\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  ...", "filename": ""}, {"language": "javascript", "code": "{\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}", "filename": ""}, {"language": "javascript", "code": "async function loadJSON(url) {\n  const req = await fetch(url);\n  return req.json();\n}", "filename": ""}, {"language": "javascript", "code": "let countryInfos;\nasync function loadCountryData() {\n  countryInfos = await loadJSON('resources/data/world/country-info.json');\n     ...\n  }\n  requestRenderIfNotRequested();\n}\nloadCountryData();", "filename": ""}], "chunk_position": 48, "heading_path": "Globe Example - Loading Country Data > Globe Example - Loading Country Data", "breadcrumbs": "Aligning HTML Elements to 3D > Globe Example - Loading Country Data > Globe Example - Loading Country Data"}, {"id": "1acd1657cc68ffa5", "url": "https://threejs.org/manual/en/align-html-elements-to-3d.html", "page_title": "Aligning HTML Elements to 3D", "page_type": "guide", "page_summary": "This guide explains how to align HTML elements to 3D objects in a three.js scene, covering basic positioning, handling overlapping labels, frustum checks for off-screen objects, zIndex sorting for correct stacking order, and advanced techniques like dot-product visibility checks for a globe example.", "heading": "Globe Example - Placing Labels", "content": "Page: Aligning HTML Elements to 3D\nSection: Globe Example - Placing Labels\n\nNow let's use that data to generate and place the labels.\n\nIn the article on optimizing lots of objects we had setup a small scene graph of helper objects to make it easy to compute latitude and longitude positions on our globe. See that article for an explanation of how they work.\n\nWe'll use that to compute a position for each label.\n\nThe code above looks very similar to the code we wrote for making cube labels making an element per label. When we're done we have an array, countryInfos, with one entry for each country to which we've added an elem property for the label element for that country and a position with its position on the globe.\n\nJust like we did for the cubes we need to update the position of the labels and render time.\n\nYou can see the code above is substantially similar to the cube example before. The only major difference is we pre-computed the label positions at init time. We can do this because the globe never moves. Only our camera moves.\n\nLastly we need to call updateLabels in our render loop.\n\nAnd this is what we get. That is way too many labels!\n\nWe have 2 problems:\n- Labels facing away from us are showing up.\n- There are too many labels.", "code_blocks": [{"language": "javascript", "code": "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);", "filename": ""}, {"language": "javascript", "code": "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;", "filename": ""}, {"language": "javascript", "code": "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 = translate(-50%, -50%) translate(${x}px,${y}px);\n\n    // set the zIndex for sorting\n    elem.style.zIndex = (-tempV.z * .5 + .5) * 100000 | 0;\n  }\n}", "filename": ""}, {"language": "javascript", "code": "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}", "filename": ""}], "chunk_position": 48, "heading_path": "Globe Example - Placing Labels > Globe Example - Placing Labels", "breadcrumbs": "Aligning HTML Elements to 3D > Globe Example - Placing Labels > Globe Example - Placing Labels"}, {"id": "669f1c983742b16b", "url": "https://threejs.org/manual/en/align-html-elements-to-3d.html", "page_title": "Aligning HTML Elements to 3D", "page_type": "guide", "page_summary": "This guide explains how to align HTML elements to 3D objects in a three.js scene, covering basic positioning, handling overlapping labels, frustum checks for off-screen objects, zIndex sorting for correct stacking order, and advanced techniques like dot-product visibility checks for a globe example.", "heading": "Hiding Back-Facing Labels", "content": "Page: Aligning HTML Elements to 3D\nSection: Hiding Back-Facing Labels\n\nFor issue #1 we can't really use the RayCaster like we did above as there is nothing to intersect except the sphere. Instead what we can do is check if that particular country is facing away from us or not. This works because the label positions are around a sphere. In fact we're using a unit sphere, a sphere with a radius of 1.0. That means the positions are already unit directions making the math relatively easy.\n\nAbove we use the positions as a direction and get that direction relative to the camera. Then we get the camera relative direction from the camera to that position on the globe and take the dot product. The dot product returns the cosine of the angle between the to vectors. This gives us a value from -1 to +1 where -1 means the label is facing the camera, 0 means the label is directly on the edge of the sphere relative to the camera, and anything greater than zero is behind. We then use that value to show or hide the element.\n\nIn the diagram we can see the dot product of the direction the label is facing to direction from the camera to that position. If you rotate the direction you'll see the dot product is -1.0 when the direction is directly facing the camera, it's 0.0 when exactly on the tangent of the sphere relative to the camera or to put it another way it's 0 when the 2 vectors are perpendicular to each other, 90 degrees It's greater than zero with the label is behind the sphere.", "code_blocks": [{"language": "javascript", "code": "const tempV = new THREE.Vector3();\nconst cameraToPoint = new THREE.Vector3();\nconst cameraPosition = new THREE.Vector3();\nconst 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    // < 0 = facing away\n    const dot = tempV.dot(cameraToPoint);\n\n    // if the orientation is not facing us hide it.\n    if (dot < 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 = translate(-50%, -50%) translate(${x}px,${y}px);\n\n    // set the zIndex for sorting\n    elem.style.zIndex = (-tempV.z * .5 + .5) * 100000 | 0;\n  }\n}", "filename": ""}], "chunk_position": 48, "heading_path": "Hiding Back-Facing Labels > Hiding Back-Facing Labels", "breadcrumbs": "Aligning HTML Elements to 3D > Hiding Back-Facing Labels > Hiding Back-Facing Labels"}, {"id": "fd27978eff4af860", "url": "https://threejs.org/manual/en/align-html-elements-to-3d.html", "page_title": "Aligning HTML Elements to 3D", "page_type": "guide", "page_summary": "This guide explains how to align HTML elements to 3D objects in a three.js scene, covering basic positioning, handling overlapping labels, frustum checks for off-screen objects, zIndex sorting for correct stacking order, and advanced techniques like dot-product visibility checks for a globe example.", "heading": "Filtering Labels by Area and Adding GUI", "content": "Page: Aligning HTML Elements to 3D\nSection: Filtering Labels by Area and Adding GUI\n\nFor issue #2, too many labels we need some way to decide which labels to show. One way would be to only show labels for large countries. The data we're loading contains min and max values for the area a country covers. From that we can compute an area and then use that area to decide whether or not to display the country.\n\nAt init time let's compute the area.\n\nThen at render time let's use the area to decide to display the label or not.\n\nFinally, since I'm not sure what good values are for these settings lets add a GUI so we can play with them.\n\nYou can see as you rotate the earth labels that go behind disappear. Adjust the minVisibleDot to see the cutoff change. You can also adjust the minArea value to see larger or smaller countries appear.\n\nThe more I worked on this the more I realized just how much work is put into Google Maps. They have also have to decide which labels to show. I'm pretty sure they use all kinds of criteria. For example your current location, your default language setting, your account settings if you have an account, they probably use population or popularity, they might give priority to the countries in the center of the view, etc ... Lots to think about.\n\nIn any case I hope these examples gave you some idea of how to align HTML elements with your 3D.", "code_blocks": [{"language": "javascript", "code": "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}", "filename": ""}, {"language": "javascript", "code": "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, area} = countryInfo;\n  // large enough?\n  if (area < large) {\n    elem.style.display = 'none';\n    continue;\n  }\n\n  ...", "filename": ""}, {"language": "javascript", "code": "import * as THREE from 'three';\nimport {OrbitControls} from 'three/addons/controls/OrbitControls.js';\nimport {GUI} from 'three/addons/libs/lil-gui.module.min.js';", "filename": ""}, {"language": "javascript", "code": "const settings = {\n  minArea: 20,\n  maxVisibleDot: -0.2,\n};\nconst gui = new GUI({width: 300});\ngui.add(settings, 'minArea', 0, 50).onChange(requestRenderIfNotRequested);\ngui.add(settings, 'maxVisibleDot', -1, 1, 0.01).onChange(requestRenderIfNotRequested);\n\nfunction updateLabels() {\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\n    ...\n\n    // if the orientation is not facing us hide it.\n    if (dot > settings.maxVisibleDot) {\n      elem.style.display = 'none';\n      continue;\n    }", "filename": ""}], "chunk_position": 48, "heading_path": "Filtering Labels by Area and Adding GUI > Filtering Labels by Area and Adding GUI", "breadcrumbs": "Aligning HTML Elements to 3D > Filtering Labels by Area and Adding GUI > Filtering Labels by Area and Adding GUI"}, {"id": "fc5451bab75f59e3", "url": "https://threejs.org/manual/en/indexed-textures.html", "page_title": "Indexed Textures for Picking and Color", "page_type": "guide", "page_summary": "A tutorial on using indexed textures in Three.js for GPU-based picking and per-country highlighting on a 3D globe, including shader modifications via onBeforeCompile to implement paletted graphics for highlighting selections.", "heading": "Introduction", "content": "Page: Indexed Textures for Picking and Color\nSection: Introduction\n\nThis article is a continuation of an article about aligning html elements to 3d. If you haven't read that yet you should start there before continuing here.\n\nSometimes using three.js requires coming up with creative solutions. I'm not sure this is a great solution but I thought I'd share it and you can see if it suggests any solutions for your needs.\n\nIn the previous article we displayed country names around a 3d globe. How would we go about letting the user select a country and show their selection?", "code_blocks": [], "chunk_position": 49, "heading_path": "Introduction > Introduction", "breadcrumbs": "Indexed Textures for Picking and Color > Introduction > Introduction"}, {"id": "77a9d81eaa138b80", "url": "https://threejs.org/manual/en/indexed-textures.html", "page_title": "Indexed Textures for Picking and Color", "page_type": "guide", "page_summary": "A tutorial on using indexed textures in Three.js for GPU-based picking and per-country highlighting on a 3D globe, including shader modifications via onBeforeCompile to implement paletted graphics for highlighting selections.", "heading": "The Problem: Picking Countries on a Globe", "content": "Page: Indexed Textures for Picking and Color\nSection: The Problem: Picking Countries on a Globe\n\nThe first idea that comes to mind is to generate geometry for each country. We could use a picking solution like we covered before. We'd build 3D geometry for each country. If the user clicks on the mesh for that country we'd know what country was clicked.\n\nSo, just to check that solution I tried generating 3D meshes of all the countries using the same data I used to generate the outlines in the previous article. The result was a 15.5meg binary GLTF (.glb) file. Making the user download 15.5meg sounds like too much to me.\n\nThere are lots of ways to compress the data. The first would probably be to apply some algorithm to lower the resolution of the outlines. I didn't spend any time pursuing that solution. For borders of the USA that's probably a huge win. For a borders of Canada probably much less.\n\nAnother solution would be to use just actual data compression. For example gzipping the file brought it down to 11meg. That's 30% less but arguably not enough.\n\nWe could store all the data as 16bit ranged values instead of 32bit float values. Or we could use something like draco compression and maybe that would be enough. I didn't check and I would encourage you to check yourself and tell me how it goes as I'd love to know. 😅", "code_blocks": [], "chunk_position": 49, "heading_path": "The Problem: Picking Countries on a Globe > The Problem: Picking Countries on a Globe", "breadcrumbs": "Indexed Textures for Picking and Color > The Problem: Picking Countries on a Globe > The Problem: Picking Countries on a Globe"}, {"id": "cf6f1b0a9ef91346", "url": "https://threejs.org/manual/en/indexed-textures.html", "page_title": "Indexed Textures for Picking and Color", "page_type": "guide", "page_summary": "A tutorial on using indexed textures in Three.js for GPU-based picking and per-country highlighting on a 3D globe, including shader modifications via onBeforeCompile to implement paletted graphics for highlighting selections.", "heading": "The Solution: Index Texture for Picking", "content": "Page: Indexed Textures for Picking and Color\nSection: The Solution: Index Texture for Picking\n\nIn my case I thought about the GPU picking solution we covered at the end of the article on picking. In that solution we drew every mesh with a unique color that represented that mesh's id. We then drew all the meshes and looked at the color that was clicked on.\n\nTaking inspiration from that we could pre-generate a map of countries where each country's color is its index number in our array of countries. We could then use a similar GPU picking technique. We'd draw the globe off screen using this index texture. Looking at the color of the pixel the user clicks would tell us the country id.\n\nSo, I wrote some code to generate such a texture. Here it is.\n\nNote: The data used to generate this texture comes from this website and is therefore licensed as CC-BY-SA.\n\nIt's only 217k, much better than the 14meg for the country meshes. In fact we could probably even lower the resolution but 217k seems good enough for now.\n\nSo let's try using it for picking countries.", "code_blocks": [], "chunk_position": 49, "heading_path": "The Solution: Index Texture for Picking > The Solution: Index Texture for Picking", "breadcrumbs": "Indexed Textures for Picking and Color > The Solution: Index Texture for Picking > The Solution: Index Texture for Picking"}, {"id": "18706f883fc2fdf7", "url": "https://threejs.org/manual/en/indexed-textures.html", "page_title": "Indexed Textures for Picking and Color", "page_type": "guide", "page_summary": "A tutorial on using indexed textures in Three.js for GPU-based picking and per-country highlighting on a 3D globe, including shader modifications via onBeforeCompile to implement paletted graphics for highlighting selections.", "heading": "Creating a Picking Scene", "content": "Page: Indexed Textures for Picking and Color\nSection: Creating a Picking Scene\n\nGrabbing code from the gpu picking example we need a scene for picking.", "code_blocks": [{"language": "javascript", "code": "const pickingScene = new THREE.Scene();\npickingScene.background = new THREE.Color(0);", "filename": ""}], "chunk_position": 49, "heading_path": "Creating a Picking Scene > Creating a Picking Scene", "breadcrumbs": "Indexed Textures for Picking and Color > Creating a Picking Scene > Creating a Picking Scene"}, {"id": "882536579af2addc", "url": "https://threejs.org/manual/en/indexed-textures.html", "page_title": "Indexed Textures for Picking and Color", "page_type": "guide", "page_summary": "A tutorial on using indexed textures in Three.js for GPU-based picking and per-country highlighting on a 3D globe, including shader modifications via onBeforeCompile to implement paletted graphics for highlighting selections.", "heading": "Adding the Globe with Index Texture to the Picking Scene", "content": "Page: Indexed Textures for Picking and Color\nSection: Adding the Globe with Index Texture to the Picking Scene\n\nand we need to add the globe with the our index texture to the picking scene.", "code_blocks": [{"language": "javascript", "code": "{\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}", "filename": ""}], "chunk_position": 49, "heading_path": "Adding the Globe with Index Texture to the Picking Scene > Adding the Globe with Index Texture to the Picking Scene", "breadcrumbs": "Indexed Textures for Picking and Color > Adding the Globe with Index Texture to the Picking Scene > Adding the Globe with Index Texture to the Picking Scene"}, {"id": "093765258830cad3", "url": "https://threejs.org/manual/en/indexed-textures.html", "page_title": "Indexed Textures for Picking and Color", "page_type": "guide", "page_summary": "A tutorial on using indexed textures in Three.js for GPU-based picking and per-country highlighting on a 3D globe, including shader modifications via onBeforeCompile to implement paletted graphics for highlighting selections.", "heading": "The GPUPickHelper Class", "content": "Page: Indexed Textures for Picking and Color\nSection: The GPUPickHelper Class\n\nThen let's copy over the `GPUPickingHelper` class we used before with a few minor changes.", "code_blocks": [{"language": "javascript", "code": "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] << 16) |\n+        (pixelBuffer[1] <<  8) |\n+        (pixelBuffer[2] <<  0);\n+\n+    return id;\n-    const id =\n-        (pixelBuffer[0] << 16) |\n-        (pixelBuffer[1] <<  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 > 1 ? 0xFFFF00 : 0xFF0000);\n-    }\n  }\n}", "filename": ""}], "chunk_position": 49, "heading_path": "The GPUPickHelper Class > The GPUPickHelper Class", "breadcrumbs": "Indexed Textures for Picking and Color > The GPUPickHelper Class > The GPUPickHelper Class"}, {"id": "69bfe8d869ac6ab6", "url": "https://threejs.org/manual/en/indexed-textures.html", "page_title": "Indexed Textures for Picking and Color", "page_type": "guide", "page_summary": "A tutorial on using indexed textures in Three.js for GPU-based picking and per-country highlighting on a 3D globe, including shader modifications via onBeforeCompile to implement paletted graphics for highlighting selections.", "heading": "Picking Countries", "content": "Page: Indexed Textures for Picking and Color\nSection: Picking Countries\n\nNow we can use that to pick countries.\n\nThe code above sets/unsets the `selected` property on the array of countries. If `shift` or `ctrl` or `cmd` is pressed then you can select more than one country.", "code_blocks": [{"language": "javascript", "code": "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 > 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 && !event.shiftKey && !event.ctrlKey && !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) => {\n    countryInfo.selected = false;\n  });\n}\n\ncanvas.addEventListener('pointerup', pickCountry);", "filename": ""}], "chunk_position": 49, "heading_path": "Picking Countries > Picking Countries", "breadcrumbs": "Indexed Textures for Picking and Color > Picking Countries > Picking Countries"}, {"id": "a569ac1245d5a373", "url": "https://threejs.org/manual/en/indexed-textures.html", "page_title": "Indexed Textures for Picking and Color", "page_type": "guide", "page_summary": "A tutorial on using indexed textures in Three.js for GPU-based picking and per-country highlighting on a 3D globe, including shader modifications via onBeforeCompile to implement paletted graphics for highlighting selections.", "heading": "Showing Selected Countries", "content": "Page: Indexed Textures for Picking and Color\nSection: Showing Selected Countries\n\nAll that's left is showing the selected countries. For now let's just update the labels.", "code_blocks": [{"language": "javascript", "code": "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 < large) {\n+    const {position, elem, area, selected} = countryInfo;\n+    const largeEnough = area >= large;\n+    const show = selected || (numCountriesSelected === 0 && largeEnough);\n+    if (!show) {\n      elem.style.display = 'none';\n      continue;\n    }\n\n    ...", "filename": ""}], "chunk_position": 49, "heading_path": "Showing Selected Countries > Showing Selected Countries", "breadcrumbs": "Indexed Textures for Picking and Color > Showing Selected Countries > Showing Selected Countries"}, {"id": "73764c77796d1e4b", "url": "https://threejs.org/manual/en/indexed-textures.html", "page_title": "Indexed Textures for Picking and Color", "page_type": "guide", "page_summary": "A tutorial on using indexed textures in Three.js for GPU-based picking and per-country highlighting on a 3D globe, including shader modifications via onBeforeCompile to implement paletted graphics for highlighting selections.", "heading": "Paletted Graphics / Indexed Color", "content": "Page: Indexed Textures for Picking and Color\nSection: Paletted Graphics / Indexed Color\n\nThe code stills shows countries based on their area but if you click one just that one will have a label.\n\nSo that seems like a reasonable solution for picking countries but what about highlighting the selected countries?\n\nFor that we can take inspiration from paletted graphics.\n\nPaletted graphics or Indexed Color is what older systems like the Atari 800, Amiga, NES, Super Nintendo, and even older IBM PCs used. Instead of storing bitmaps as RGBA colors 8bits per color, 32 bytes per pixel or more, they stored bitmaps as 8bit values or less. The value for each pixel was an index into a palette. So for example a value of 3 in the image means \"display color 3\". What color color#3 is is defined somewhere else called a \"palette\".\n\nIn JavaScript you can think of it like this", "code_blocks": [{"language": "javascript", "code": "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];", "filename": ""}], "chunk_position": 49, "heading_path": "Paletted Graphics / Indexed Color > Paletted Graphics / Indexed Color", "breadcrumbs": "Indexed Textures for Picking and Color > Paletted Graphics / Indexed Color > Paletted Graphics / Indexed Color"}, {"id": "3b7fb74bc71d32fa", "url": "https://threejs.org/manual/en/indexed-textures.html", "page_title": "Indexed Textures for Picking and Color", "page_type": "guide", "page_summary": "A tutorial on using indexed textures in Three.js for GPU-based picking and per-country highlighting on a 3D globe, including shader modifications via onBeforeCompile to implement paletted graphics for highlighting selections.", "heading": "Applying Palettes to Country Textures", "content": "Page: Indexed Textures for Picking and Color\nSection: Applying Palettes to Country Textures\n\nWhere each pixel in the image data is an index into palette. If you interpreted the image data through the palette above you'd get this image\n\nIn our case we already have a texture above that has a different id per country. So, we could use that same texture through a palette texture to give each country its own color. By changing the palette texture we can color each individual country. For example by setting the entire palette texture to black and then for one country's entry in the palette a different color, we can highlight just that country.\n\nTo do paletted index graphics requires some custom shader code. Let's modify the default shaders in three.js. That way we can use lighting and other features if we want.\n\nLike we covered in the article on animating lots of objects we can modify the default shaders by adding a function to a material's `onBeforeCompile` property.", "code_blocks": [], "chunk_position": 49, "heading_path": "Applying Palettes to Country Textures > Applying Palettes to Country Textures", "breadcrumbs": "Indexed Textures for Picking and Color > Applying Palettes to Country Textures > Applying Palettes to Country Textures"}, {"id": "7386609d2b14557a", "url": "https://threejs.org/manual/en/indexed-textures.html", "page_title": "Indexed Textures for Picking and Color", "page_type": "guide", "page_summary": "A tutorial on using indexed textures in Three.js for GPU-based picking and per-country highlighting on a 3D globe, including shader modifications via onBeforeCompile to implement paletted graphics for highlighting selections.", "heading": "Default Fragment Shader", "content": "Page: Indexed Textures for Picking and Color\nSection: Default Fragment Shader\n\nThe default fragment shader looks something like this before compiling.", "code_blocks": [{"language": "glsl", "code": "#include <common>\n#include <color_pars_fragment>\n#include <uv_pars_fragment>\n#include <map_pars_fragment>\n#include <alphamap_pars_fragment>\n#include <aomap_pars_fragment>\n#include <lightmap_pars_fragment>\n#include <envmap_pars_fragment>\n#include <fog_pars_fragment>\n#include <specularmap_pars_fragment>\n#include <logdepthbuf_pars_fragment>\n#include <clipping_planes_pars_fragment>\nvoid main() {\n    #include <clipping_planes_fragment>\n    vec4 diffuseColor = vec4( diffuse, opacity );\n    #include <logdepthbuf_fragment>\n    #include <map_fragment>\n    #include <color_fragment>\n    #include <alphamap_fragment>\n    #include <alphatest_fragment>\n    #include <specularmap_fragment>\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 <aomap_fragment>\n    reflectedLight.indirectDiffuse *= diffuseColor.rgb;\n    vec3 outgoingLight = reflectedLight.indirectDiffuse;\n    #include <envmap_fragment>\n    gl_FragColor = vec4( outgoingLight, diffuseColor.a );\n    #include <premultiplied_alpha_fragment>\n    #include <tonemapping_fragment>\n    #include <colorspace_fragment>\n    #include <fog_fragment>\n}", "filename": ""}], "chunk_position": 49, "heading_path": "Default Fragment Shader > Default Fragment Shader", "breadcrumbs": "Indexed Textures for Picking and Color > Default Fragment Shader > Default Fragment Shader"}, {"id": "6ed61c01e980baf8", "url": "https://threejs.org/manual/en/indexed-textures.html", "page_title": "Indexed Textures for Picking and Color", "page_type": "guide", "page_summary": "A tutorial on using indexed textures in Three.js for GPU-based picking and per-country highlighting on a 3D globe, including shader modifications via onBeforeCompile to implement paletted graphics for highlighting selections.", "heading": "Modifying the Shader with onBeforeCompile", "content": "Page: Indexed Textures for Picking and Color\nSection: Modifying the Shader with onBeforeCompile\n\nDigging through all those snippets we find that three.js uses a variable called `diffuseColor` to manage the base material color. It sets this in the `<color_fragment>` snippet so we should be able to modify it after that point.\n\n`diffuseColor` at that point in the shader should already be the color from our outline texture so we can look up the color from a palette texture and mix them for the final result.\n\nLike we did before we'll make an array of search and replacement strings and apply them to the shader in `Material.onBeforeCompile`.\n\nAbove can see above we add 3 uniforms, `indexTexture`, `paletteTexture`, and `paletteTextureWidth`. We get a color from the `indexTexture` and convert it to an index. `vUv` is the texture coordinates provided by three.js. We then use that index to get a color out of the palette texture. We then mix the result with the current `diffuseColor`. The `diffuseColor` at this point is our black and white outline texture so if we add the 2 colors we'll get white outlines. If we subtract the current diffuse color we'll get black outlines.", "code_blocks": [{"language": "javascript", "code": "{\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 <common>',\n+      to: \n+        #include <common>\n+        uniform sampler2D indexTexture;\n+        uniform sampler2D paletteTexture;\n+        uniform float paletteTextureWidth;\n+      ,\n+    },\n+    {\n+      from: '#include <color_fragment>',\n+      to: \n+        #include <color_fragment>\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+      ,\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) => {\n+      shader.fragmentShader = shader.fragmentShader.replace(rep.from, rep.to);\n+    });\n+  };\n  scene.add(new THREE.Mesh(geometry, material));\n}", "filename": ""}], "chunk_position": 49, "heading_path": "Modifying the Shader with onBeforeCompile > Modifying the Shader with onBeforeCompile", "breadcrumbs": "Indexed Textures for Picking and Color > Modifying the Shader with onBeforeCompile > Modifying the Shader with onBeforeCompile"}, {"id": "d0a1bae549ff98b5", "url": "https://threejs.org/manual/en/indexed-textures.html", "page_title": "Indexed Textures for Picking and Color", "page_type": "guide", "page_summary": "A tutorial on using indexed textures in Three.js for GPU-based picking and per-country highlighting on a 3D globe, including shader modifications via onBeforeCompile to implement paletted graphics for highlighting selections.", "heading": "Setting Up the Palette Texture", "content": "Page: Indexed Textures for Picking and Color\nSection: Setting Up the Palette Texture\n\nBefore we can render we need to setup the palette texture and these 3 uniforms.\n\nFor the palette texture it just needs to be wide enough to hold one color per country + one for the ocean (id = 0). There are 240 something countries. We could wait until the list of countries loads to get an exact number or look it up. There's not much harm in just picking some larger number so let's choose 512.\n\nHere's the code to create the palette texture", "code_blocks": [{"language": "javascript", "code": "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;", "filename": ""}], "chunk_position": 49, "heading_path": "Setting Up the Palette Texture > Setting Up the Palette Texture", "breadcrumbs": "Indexed Textures for Picking and Color > Setting Up the Palette Texture > Setting Up the Palette Texture"}, {"id": "d5099b67b453b1da", "url": "https://threejs.org/manual/en/indexed-textures.html", "page_title": "Indexed Textures for Picking and Color", "page_type": "guide", "page_summary": "A tutorial on using indexed textures in Three.js for GPU-based picking and per-country highlighting on a 3D globe, including shader modifications via onBeforeCompile to implement paletted graphics for highlighting selections.", "heading": "DataTexture Description", "content": "Page: Indexed Textures for Picking and Color\nSection: DataTexture Description\n\nA `DataTexture` let's us give a texture raw data. In this case we're giving it 512 RGBA colors, 4 bytes each where each byte is red, green, and blue respectively using values that go from 0 to 255.\n\nLet's fill it with random colors just to see it work", "code_blocks": [{"language": "javascript", "code": "for (let i = 1; i < 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;", "filename": ""}], "chunk_position": 49, "heading_path": "DataTexture Description > DataTexture Description", "breadcrumbs": "Indexed Textures for Picking and Color > DataTexture Description > DataTexture Description"}, {"id": "f1e8ac69d6c3e61a", "url": "https://threejs.org/manual/en/indexed-textures.html", "page_title": "Indexed Textures for Picking and Color", "page_type": "guide", "page_summary": "A tutorial on using indexed textures in Three.js for GPU-based picking and per-country highlighting on a 3D globe, including shader modifications via onBeforeCompile to implement paletted graphics for highlighting selections.", "heading": "Updating the Palette Texture", "content": "Page: Indexed Textures for Picking and Color\nSection: Updating the Palette Texture\n\nAnytime we want three.js to update the palette texture with the contents of the `palette` array we need to set `paletteTexture.needsUpdate` to `true`.\n\nAnd then we still need to set the uniforms on the material.", "code_blocks": [{"language": "javascript", "code": "const geometry = new THREE.SphereGeometry(1, 64, 32);\nconst material = new THREE.MeshBasicMaterial({map: texture});\nmaterial.onBeforeCompile = function(shader) {\n  fragmentShaderReplacements.forEach((rep) => {\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));", "filename": ""}], "chunk_position": 49, "heading_path": "Updating the Palette Texture > Updating the Palette Texture", "breadcrumbs": "Indexed Textures for Picking and Color > Updating the Palette Texture > Updating the Palette Texture"}, {"id": "2ccd8b5b68de62cb", "url": "https://threejs.org/manual/en/indexed-textures.html", "page_title": "Indexed Textures for Picking and Color", "page_type": "guide", "page_summary": "A tutorial on using indexed textures in Three.js for GPU-based picking and per-country highlighting on a 3D globe, including shader modifications via onBeforeCompile to implement paletted graphics for highlighting selections.", "heading": "Color Helper Function", "content": "Page: Indexed Textures for Picking and Color\nSection: Color Helper Function\n\nNow that we can see the index and palette textures are working let's manipulate the palette for highlighting.\n\nFirst let's make function that will let us pass in a three.js style color and give us values we can put in the palette texture.", "code_blocks": [{"language": "javascript", "code": "const tempColor = new THREE.Color();\nfunction get255BasedColor(color) {\n  tempColor.set(color);\n  const base = tempColor.toArray().map(v => v * 255);\n  base.push(255); // alpha\n  return base;\n}", "filename": ""}], "chunk_position": 49, "heading_path": "Color Helper Function > Color Helper Function", "breadcrumbs": "Indexed Textures for Picking and Color > Color Helper Function > Color Helper Function"}, {"id": "019c6ea2412140b0", "url": "https://threejs.org/manual/en/indexed-textures.html", "page_title": "Indexed Textures for Picking and Color", "page_type": "guide", "page_summary": "A tutorial on using indexed textures in Three.js for GPU-based picking and per-country highlighting on a 3D globe, including shader modifications via onBeforeCompile to implement paletted graphics for highlighting selections.", "heading": "Building the Palette", "content": "Page: Indexed Textures for Picking and Color\nSection: Building the Palette\n\nCalling it like this `color = get255BasedColor('red')` will return an array like `[255, 0, 0, 255]`.\n\nNext let's use it to make a few colors and fill out the palette.", "code_blocks": [{"language": "javascript", "code": "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 < maxNumCountries; ++i) {\n    setPaletteColor(i, unselectedColor);\n  }\n\n  // set the ocean color (index #0)\n  setPaletteColor(0, oceanColor);\n  paletteTexture.needsUpdate = true;\n}", "filename": ""}], "chunk_position": 49, "heading_path": "Building the Palette > Building the Palette", "breadcrumbs": "Indexed Textures for Picking and Color > Building the Palette > Building the Palette"}, {"id": "0a268ac06df1ba86", "url": "https://threejs.org/manual/en/indexed-textures.html", "page_title": "Indexed Textures for Picking and Color", "page_type": "guide", "page_summary": "A tutorial on using indexed textures in Three.js for GPU-based picking and per-country highlighting on a 3D globe, including shader modifications via onBeforeCompile to implement paletted graphics for highlighting selections.", "heading": "Updating Palette on Country Selection", "content": "Page: Indexed Textures for Picking and Color\nSection: Updating Palette on Country Selection\n\nNow let's use those functions to update the palette when a country is selected", "code_blocks": [{"language": "javascript", "code": "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 > 0) {\n    const countryInfo = countryInfos[id - 1];\n    const selected = !countryInfo.selected;\n    if (selected && !event.shiftKey && !event.ctrlKey && !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) => {\n    countryInfo.selected = false;\n  });\n+  resetPalette();\n}", "filename": ""}], "chunk_position": 49, "heading_path": "Updating Palette on Country Selection > Updating Palette on Country Selection", "breadcrumbs": "Indexed Textures for Picking and Color > Updating Palette on Country Selection > Updating Palette on Country Selection"}, {"id": "19f4da3f5c4c122a", "url": "https://threejs.org/manual/en/indexed-textures.html", "page_title": "Indexed Textures for Picking and Color", "page_type": "guide", "page_summary": "A tutorial on using indexed textures in Three.js for GPU-based picking and per-country highlighting on a 3D globe, including shader modifications via onBeforeCompile to implement paletted graphics for highlighting selections.", "heading": "Handling Click vs Drag", "content": "Page: Indexed Textures for Picking and Color\nSection: Handling Click vs Drag\n\nOne minor thing is we can't spin the globe without changing the selection state. If we select a country and then want to rotate the globe the selection will change.\n\nLet's try to fix that. Off the top of my head we can check 2 things. How much time passed between clicking and letting go. Another is did the user actually move the mouse. If the time is short or if they didn't move the mouse then it was probably a click. Otherwise they were probably trying to drag the globe.", "code_blocks": [{"language": "javascript", "code": "+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 > 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 > 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 > 0) {\n    const countryInfo = countryInfos[id - 1];\n    const selected = !countryInfo.selected;\n    if (selected && !event.shiftKey && !event.ctrlKey && !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) => {\n    countryInfo.selected = false;\n  });\n  resetPalette();\n}\n\n+canvas.addEventListener('pointerdown', recordStartTimeAndPosition);\ncanvas.addEventListener('pointerup', pickCountry);", "filename": ""}], "chunk_position": 49, "heading_path": "Handling Click vs Drag > Handling Click vs Drag", "breadcrumbs": "Indexed Textures for Picking and Color > Handling Click vs Drag > Handling Click vs Drag"}, {"id": "1c46dfcfb6753ea0", "url": "https://threejs.org/manual/en/indexed-textures.html", "page_title": "Indexed Textures for Picking and Color", "page_type": "guide", "page_summary": "A tutorial on using indexed textures in Three.js for GPU-based picking and per-country highlighting on a 3D globe, including shader modifications via onBeforeCompile to implement paletted graphics for highlighting selections.", "heading": "Conclusion", "content": "Page: Indexed Textures for Picking and Color\nSection: Conclusion\n\nI'm not a UX expert so I'd love to hear if there is a better solution.\n\nI hope that gave you some idea of how indexed graphics can be useful and how you can modify the shaders three.js makes to add simple features. How to use GLSL, the language the shaders are written in, is too much for this article. There are a few links to some info in the article on post processing.", "code_blocks": [], "chunk_position": 49, "heading_path": "Conclusion > Conclusion", "breadcrumbs": "Indexed Textures for Picking and Color > Conclusion > Conclusion"}, {"id": "b7f969a1c99ceb65", "url": "https://threejs.org/manual/en/canvas-textures.html", "page_title": "Canvas Textures", "page_type": "reference", "page_summary": "Extraction fallback content. [stop]", "heading": "Canvas Textures", "content": "Page: Canvas Textures\nSection: Canvas Textures\n\n\n\n\n\n##### Canvas Textures\n\n\n\n\n\n\n\nThis article continues from [the article on textures](textures.html).\nIf you haven't read that yet you should probably start there.\n\n\n\n\nIn [the previous article on textures](textures.html) 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 [`CanvasTexture`](/docs/#api/en/textures/CanvasTexture).\n\n\n\n\nA canvas texture takes a `<canvas>` as its input. If you don't know how to\ndraw with the 2D canvas API on a canvas [there's a good tutorial on MDN](https://developer.mozilla.org/en-US/docs/Web/API/Canvas_API/Tutorial).\n\n\n\n\nLet's make a simple canvas program. Here's one that draws dots at random places in random colors.\n\n\n\n\n\nit's pretty straight forward.\n\n\n\n\n\n\n\n\n[click here to open in a separate window](/manual/examples/canvas-random-dots.html)\n\n\n\n\n\n\n\n\nNow let's use it to texture something. We'll start with the example of texturing\na cube from [the previous article](textures.html).\nWe'll remove the code that loads an image and instead use\nour canvas by creating a [`CanvasTexture`](/docs/#api/en/textures/CanvasTexture) and passing it the canvas we created.\n\n\n\n\n\nAnd then call the code to draw a random dot in our render loop\n\n\n\n\n\nThe only extra thing we need to do is set the `needsUpdate` property\nof the [`CanvasTexture`](/docs/#api/en/textures/CanvasTexture) to tell three.js to update the texture with\nthe latest contents of the canvas.\n\n\n\n\nAnd with that we have a canvas textured cube\n\n\n\n\n\n\n\n\n[click here to open in a separate window](/manual/examples/canvas-textured-cube.html)\n\n\n\n\n\n\n\n\nNote that if you want to use three.js to draw into the canvas you're\nbetter off using a `RenderTarget` which is covered in [this article](rendertargets.html).\n\n\n\n\nA 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.\n\n\n\n\nLet's make a scene with 3 people and give each person a badge\nor label.\n\n\n\n\nLet's take the example above and remove all the cube related\nstuff. Then let's set the background to white and add two [lights](lights.html).\n\n\n\n\n\nLet's make some code to make a label using canvas 2D\n\n\n\n\n\nThen we'll make simple people from a cylinder for the body, a sphere\nfor the head, and a plane for the label.\n\n\n\n\nFirst let's make the shared geometry.\n\n\n\n\n\nThen let's make a function to build a person from these\nparts.\n\n\n\n\n\nYou can see above we put the body, head, and label on a root\n[`Object3D`](/docs/#api/en/core/Object3D) 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.\n\n\n\n\nWe can then make people with labels\n\n\n\n\n\nWhat's left is to add some [`OrbitControls`](/docs/#examples/controls/OrbitControls) so we can move\nthe camera.\n\n\n\n\n\n\nand we get simple labels.\n\n\n\n\n\n\n\n\n[click here to open in a separate window](/manual/examples/canvas-textured-labels.html)\n\n\n\n\n\n\n\n\nSome things to notice.\n\n\n\n\n\n\n- If you zoom in the labels get pretty low-res.\n\n\n\n\n\nThere 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.\n\n\n\n\nOne solution is to increase the resolution of the labels.\nTry setting the size passed into to double what it is now\nand setting `labelBaseScale` to half what it currently is.\n\n\n\n\n\n\n- The labels get longer the longer the name.\n\n\n\n\n\nIf you wanted to fix this you'd instead choose a fixed sized\nlabel and then squish the text.\n\n\n\n\nThis is pretty easy. Pass in a base width and scale the text to fit that\nwidth like this\n\n\n\n\n\nThen we can pass in a width for the labels\n\n\n\n\n\nand we get labels where the text is centered and scaled to fit\n\n\n\n\n\n\n\n\n[click here to open in a separate window](/manual/examples/canvas-textured-labels-scale-to-fit.html)\n\n\n\n\n\n\n\n\nAbove 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.\n\n\n\n\n\n\n\n\n\n[click here to open in a separate window](/manual/examples/canvas-textured-labels-one-canvas.html)\n\n\n\n\n\n\n\n\nAnother 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 [an article on billboards](billboards.html).\n\n\n\n\nFor labels in particular, [another solution is to use HTML](align-html-elements-to-3d.html).\nThe labels in this article are  *inside the 3D world*  which is good if you want them\nto be hidden by other objects where as [HTML labels](align-html-elements-to-3d.html) are always on top.\n\n\n\n\n", "code_blocks": [{"language": "js", "code": "#${randInt(0x1000000).toString(16).padStart(6, '0')}", "filename": ""}, {"language": "js", "code": "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", "filename": ""}, {"language": "js", "code": "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) => {\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}", "filename": ""}, {"language": "js", "code": "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]);", "filename": ""}, {"language": "js", "code": "${size}px bold sans-serif", "filename": ""}, {"language": "js", "code": "+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);", "filename": ""}, {"language": "js", "code": "+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+}", "filename": ""}, {"language": "js", "code": "+makePerson(-3, 32, 'Purple People Eater', 'purple');\n+makePerson(-0, 32, 'Green Machine', 'green');\n+makePerson(+3, 32, 'Red Menace', 'red');", "filename": ""}, {"language": "js", "code": "import * as THREE from 'three';\n+import {OrbitControls} from 'three/addons/controls/OrbitControls.js';", "filename": ""}, {"language": "js", "code": "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();", "filename": ""}, {"language": "js", "code": "${size}px bold sans-serif", "filename": ""}, {"language": "js", "code": "-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');", "filename": ""}, {"language": "js", "code": "${size}px bold sans-serif", "filename": ""}], "chunk_position": 50, "heading_path": "Canvas Textures > Canvas Textures", "breadcrumbs": "Canvas Textures > Canvas Textures > Canvas Textures"}, {"id": "2ef83d7be63dc4b3", "url": "https://threejs.org/manual/en/billboards.html", "page_title": "Billboards", "page_type": "guide", "page_summary": "A guide on using Three.js Sprite and SpriteMaterial to create billboards — 2D planes that always face the camera — for purposes like character labels and rendering facades of 3D objects to improve performance.", "heading": "Billboards", "content": "Page: Billboards\nSection: Billboards\n\nIn a previous article we used a CanvasTexture to make labels / badges on characters. Sometimes we'd like to make labels or other things that always face the camera. Three.js provides the Sprite and SpriteMaterial to make this happen.\n\nLet's change the badge example from the article on canvas textures to use Sprite and SpriteMaterial", "code_blocks": [{"language": "javascript", "code": "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;", "filename": ""}], "chunk_position": 51, "heading_path": "Billboards > Billboards", "breadcrumbs": "Billboards > Billboards > Billboards"}, {"id": "73ebe38c1330c6b5", "url": "https://threejs.org/manual/en/billboards.html", "page_title": "Billboards", "page_type": "guide", "page_summary": "A guide on using Three.js Sprite and SpriteMaterial to create billboards — 2D planes that always face the camera — for purposes like character labels and rendering facades of 3D objects to improve performance.", "heading": "Fixing label intersection", "content": "Page: Billboards\nSection: Fixing label intersection\n\nand the labels now always face the camera\n\n[click here to open in a separate window]\n\nOne problem is from certain angles the labels now intersect the characters.\n\nWe can move the position of the labels to fix.", "code_blocks": [{"language": "javascript", "code": "+// 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;", "filename": ""}], "chunk_position": 51, "heading_path": "Fixing label intersection > Fixing label intersection", "breadcrumbs": "Billboards > Fixing label intersection > Fixing label intersection"}, {"id": "ace6c395dae1f351", "url": "https://threejs.org/manual/en/billboards.html", "page_title": "Billboards", "page_type": "guide", "page_summary": "A guide on using Three.js Sprite and SpriteMaterial to create billboards — 2D planes that always face the camera — for purposes like character labels and rendering facades of 3D objects to improve performance.", "heading": "Facades with billboards", "content": "Page: Billboards\nSection: Facades with billboards\n\nAnother thing we can do with billboards is draw facades.\n\nInstead of drawing 3D objects we draw 2D planes with an image of 3D objects. This is often faster than drawing 3D objects.\n\nFor example let's make a scene with grid of trees. We'll make each tree from a cylinder for the base and a cone for the top.\n\nFirst we make the cone and cylinder geometry and materials that all the trees will share", "code_blocks": [{"language": "javascript", "code": "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'});", "filename": ""}], "chunk_position": 51, "heading_path": "Facades with billboards > Facades with billboards", "breadcrumbs": "Billboards > Facades with billboards > Facades with billboards"}, {"id": "1d2415a745d53edd", "url": "https://threejs.org/manual/en/billboards.html", "page_title": "Billboards", "page_type": "guide", "page_summary": "A guide on using Three.js Sprite and SpriteMaterial to create billboards — 2D planes that always face the camera — for purposes like character labels and rendering facades of 3D objects to improve performance.", "heading": "Making tree meshes", "content": "Page: Billboards\nSection: Making tree meshes\n\nThen we'll make a function that makes a Mesh each for the trunk and top of a tree and parents both to an Object3D.", "code_blocks": [{"language": "javascript", "code": "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}", "filename": ""}], "chunk_position": 51, "heading_path": "Making tree meshes > Making tree meshes", "breadcrumbs": "Billboards > Making tree meshes > Making tree meshes"}, {"id": "1558e0a2af6fdac1", "url": "https://threejs.org/manual/en/billboards.html", "page_title": "Billboards", "page_type": "guide", "page_summary": "A guide on using Three.js Sprite and SpriteMaterial to create billboards — 2D planes that always face the camera — for purposes like character labels and rendering facades of 3D objects to improve performance.", "heading": "Placing trees in a grid", "content": "Page: Billboards\nSection: Placing trees in a grid\n\nThen we'll make a loop to place a grid of trees.", "code_blocks": [{"language": "javascript", "code": "for (let z = -50; z <= 50; z += 10) {\n  for (let x = -50; x <= 50; x += 10) {\n    makeTree(x, z);\n  }\n}", "filename": ""}], "chunk_position": 51, "heading_path": "Placing trees in a grid > Placing trees in a grid", "breadcrumbs": "Billboards > Placing trees in a grid > Placing trees in a grid"}, {"id": "8935f2fa422984f0", "url": "https://threejs.org/manual/en/billboards.html", "page_title": "Billboards", "page_type": "guide", "page_summary": "A guide on using Three.js Sprite and SpriteMaterial to create billboards — 2D planes that always face the camera — for purposes like character labels and rendering facades of 3D objects to improve performance.", "heading": "Adding ground and background", "content": "Page: Billboards\nSection: Adding ground and background\n\nLet's also add a ground plane while we're at it", "code_blocks": [{"language": "javascript", "code": "// 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}", "filename": ""}], "chunk_position": 51, "heading_path": "Adding ground and background > Adding ground and background", "breadcrumbs": "Billboards > Adding ground and background > Adding ground and background"}, {"id": "35d909b0d27576ae", "url": "https://threejs.org/manual/en/billboards.html", "page_title": "Billboards", "page_type": "guide", "page_summary": "A guide on using Three.js Sprite and SpriteMaterial to create billboards — 2D planes that always face the camera — for purposes like character labels and rendering facades of 3D objects to improve performance.", "heading": "Setting the scene background", "content": "Page: Billboards\nSection: Setting the scene background\n\nand change the background to light blue", "code_blocks": [{"language": "javascript", "code": "const scene = new THREE.Scene();\n-scene.background = new THREE.Color('white');\n+scene.background = new THREE.Color('lightblue');", "filename": ""}], "chunk_position": 51, "heading_path": "Setting the scene background > Setting the scene background", "breadcrumbs": "Billboards > Setting the scene background > Setting the scene background"}, {"id": "bb3fd4ed2d5006c2", "url": "https://threejs.org/manual/en/billboards.html", "page_title": "Billboards", "page_type": "guide", "page_summary": "A guide on using Three.js Sprite and SpriteMaterial to create billboards — 2D planes that always face the camera — for purposes like character labels and rendering facades of 3D objects to improve performance.", "heading": "Rendering an object to a texture", "content": "Page: Billboards\nSection: Rendering an object to a texture\n\nand we get a grid of trees\n\n[click here to open in a separate window]\n\nThere are 11x11 or 121 trees. Each tree is made from a 12 polygon cone and a 48 polygon trunk so each tree is 60 polygons. 121 * 60 is 7260 polygons. That's not that many but of course a more detailed 3D tree might be 1000-3000 polygons. If they were 3000 polygons each then 121 trees would be 363000 polygons to draw.\n\nUsing facades we can bring that number down.\n\nWe could manually create a facade in some painting program but let's write some code to try to generate one.\n\nLet's write some code to render an object to a texture using a RenderTarget. We covered rendering to a RenderTarget in the article on render targets.", "code_blocks": [{"language": "javascript", "code": "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}", "filename": ""}], "chunk_position": 51, "heading_path": "Rendering an object to a texture > Rendering an object to a texture", "breadcrumbs": "Billboards > Rendering an object to a texture > Rendering an object to a texture"}, {"id": "f7ca20849d8caf78", "url": "https://threejs.org/manual/en/billboards.html", "page_title": "Billboards", "page_type": "guide", "page_summary": "A guide on using Three.js Sprite and SpriteMaterial to create billboards — 2D planes that always face the camera — for purposes like character labels and rendering facades of 3D objects to improve performance.", "heading": "Notes on the facade rendering code", "content": "Page: Billboards\nSection: Notes on the facade rendering code\n\nSome things to note about the code above:\n\nWe're using the field of view (fov) defined above this code.\n\nWe're computing a box that contains the tree the same way we did in the article on loading a .obj file with a few minor changes.\n\nWe call frameArea again adapted the article on loading a .obj file. In this case we compute how far the camera needs to be away from the object given its field of view to contain the object. We then position the camera -z that distance from the center of the box that contains the object.\n\nWe multiply the size we want to fit by 1.1 (fudge) to make sure the tree fits completely in the render target. The issue here is the size we're using to calculate if the object fits in the camera's view is not taking into account that the very edges of the object will end up dipping outside area we calculated. We could compute how to make 100% of the box fit but that would waste space as well so instead we just *fudge* it.\n\nThen we render to the render target and remove the object from the scene.\n\nIt's important to note we need the lights in the scene but we need to make sure nothing else is in the scene.\n\nWe also need to not set a background color on the scene", "code_blocks": [{"language": "javascript", "code": "const scene = new THREE.Scene();\n-scene.background = new THREE.Color('lightblue');", "filename": ""}], "chunk_position": 51, "heading_path": "Notes on the facade rendering code > Notes on the facade rendering code", "breadcrumbs": "Billboards > Notes on the facade rendering code > Notes on the facade rendering code"}, {"id": "84106ba63c0885bf", "url": "https://threejs.org/manual/en/billboards.html", "page_title": "Billboards", "page_type": "guide", "page_summary": "A guide on using Three.js Sprite and SpriteMaterial to create billboards — 2D planes that always face the camera — for purposes like character labels and rendering facades of 3D objects to improve performance.", "heading": "Making facades for a grid of trees", "content": "Page: Billboards\nSection: Making facades for a grid of trees\n\nFinally we've made the texture we return it and the position and scale we need to make the facade so that it will appear to be in the same place.\n\nWe then make a tree and call this code and pass it in", "code_blocks": [{"language": "javascript", "code": "// make billboard texture\nconst tree = makeTree(0, 0);\nconst facadeSize = 64;\nconst treeSpriteInfo = makeSpriteTexture(facadeSize, tree);", "filename": ""}], "chunk_position": 51, "heading_path": "Making facades for a grid of trees > Making facades for a grid of trees", "breadcrumbs": "Billboards > Making facades for a grid of trees > Making facades for a grid of trees"}, {"id": "1cfa1f754561207f", "url": "https://threejs.org/manual/en/billboards.html", "page_title": "Billboards", "page_type": "guide", "page_summary": "A guide on using Three.js Sprite and SpriteMaterial to create billboards — 2D planes that always face the camera — for purposes like character labels and rendering facades of 3D objects to improve performance.", "heading": "Replacing tree models with sprites", "content": "Page: Billboards\nSection: Replacing tree models with sprites\n\nWe can then make a grid of facades instead of a grid of tree models", "code_blocks": [{"language": "javascript", "code": "+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 <= 50; z += 10) {\n  for (let x = -50; x <= 50; x += 10) {\n-    makeTree(x, z);\n+    makeSprite(treeSpriteInfo, x, z);\n  }\n}", "filename": ""}], "chunk_position": 51, "heading_path": "Replacing tree models with sprites > Replacing tree models with sprites", "breadcrumbs": "Billboards > Replacing tree models with sprites > Replacing tree models with sprites"}, {"id": "53788baea362e14e", "url": "https://threejs.org/manual/en/billboards.html", "page_title": "Billboards", "page_type": "guide", "page_summary": "A guide on using Three.js Sprite and SpriteMaterial to create billboards — 2D planes that always face the camera — for purposes like character labels and rendering facades of 3D objects to improve performance.", "heading": "Restoring the background", "content": "Page: Billboards\nSection: Restoring the background\n\nIn the code above we apply the offset and scale needed to position the facade so it appears the same place the original tree would have appeared.\n\nNow that we're done making the tree facade texture we can set the background again", "code_blocks": [{"language": "javascript", "code": "scene.background = new THREE.Color('lightblue');", "filename": ""}], "chunk_position": 51, "heading_path": "Restoring the background > Restoring the background", "breadcrumbs": "Billboards > Restoring the background > Restoring the background"}, {"id": "1e0dd98fa1a69465", "url": "https://threejs.org/manual/en/billboards.html", "page_title": "Billboards", "page_type": "guide", "page_summary": "A guide on using Three.js Sprite and SpriteMaterial to create billboards — 2D planes that always face the camera — for purposes like character labels and rendering facades of 3D objects to improve performance.", "heading": "Conclusion", "content": "Page: Billboards\nSection: Conclusion\n\nand now we get a scene of tree facades\n\n[click here to open in a separate window]\n\nCompare to the trees models above and you can see it looks fairly similar. We used a low-res texture, just 64x64 pixels so the facades are blocky. You could increase the resolution. Often facades are used only in the far distance when they are fairly small so a low-res texture is enough and it saves on drawing detailed trees that are only a few pixels big when far away.\n\nAnother issue is we are only viewing the tree from one side. This is often solved by rendering more facades, say from 8 directions around the object and then setting which facade to show based on which direction the camera is looking at the facade.\n\nWhether or not you use facades is up to you but hopefully this article gave you some ideas and suggested some solutions if you decide to use them.", "code_blocks": [], "chunk_position": 51, "heading_path": "Conclusion > Conclusion", "breadcrumbs": "Billboards > Conclusion > Conclusion"}, {"id": "cd1a56b9abe112d7", "url": "https://threejs.org/manual/en/cleanup.html", "page_title": "Cleanup", "page_type": "guide", "page_summary": "A guide to managing memory and cleaning up Three.js resources (geometries, textures, materials, loaded files) by implementing a ResourceTracker class to automate disposal.", "heading": "Cleanup", "content": "Page: Cleanup\nSection: Cleanup\n\nThree.js apps often use lots of memory. A 3D model might be 1 to 20 meg memory for all of its vertices. A model might use many textures that even if they are compressed into jpg files they have to be expanded to their uncompressed form to use. Each 1024x1024 texture takes 4 to 6meg of memory.\n\nMost three.js apps load resources at init time and then use those resources forever until the page is closed. But, what if you want to load and change resources over time?\n\nUnlike most JavaScript, three.js can not automatically clean these resources up. The browser will clean them up if you switch pages but otherwise it's up to you to manage them. This is an issue of how WebGL is designed and so three.js has no recourse but to pass on the responsibility to free resources back to you.\n\nYou free three.js resource this by calling the `dispose` function on [textures](textures.html), [geometries](primitives.html), and [materials](materials.html).\n\nYou could do this manually. At the start you might create some of these resources\n\n```\nconst boxGeometry = new THREE.BoxGeometry(...);\nconst boxTexture = textureLoader.load(...);\nconst boxMaterial = new THREE.MeshPhongMaterial({map: texture});\n```\n\nand then when you're done with them you'd free them\n\n```\nboxGeometry.dispose();\nboxTexture.dispose();\nboxMaterial.dispose();\n```\n\nAs you use more and more resources that would get more and more tedious.\n\nTo help remove some of the tedium let's make a class to track the resources. We'll then ask that class to do the cleanup for us.\n\nHere's a first pass at such a class\n\n```\nclass 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```\n\nLet's use this class with the first example from [the article on textures](textures.html). We can create an instance of this class\n\n```\nconst resTracker = new ResourceTracker();\n```\n\nand then just to make it easier to use let's create a bound function for the `track` method\n\n```\nconst resTracker = new ResourceTracker();\n+const track = resTracker.track.bind(resTracker);\n```\n\nNow to use it we just need to call `track` with for each geometry, texture, and material we create\n\n```\nconst 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```\n\nAnd then to free them we'd want to remove the cubes from the scene and then call `resTracker.dispose`\n\n```\nfor (const cube of cubes) {\n  scene.remove(cube);\n}\ncubes.length = 0;  // clears the cubes array\nresTracker.dispose();\n```\n\nThat would work but I find having to remove the cubes from the scene kind of tedious. Let's add that functionality to the `ResourceTracker`.\n\n```\nclass 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```\n\nAnd now we can track the cubes\n\n```\nconst 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```\n\nWe no longer need the code to remove the cubes from the scene.\n\n```\n-for (const cube of cubes) {\n-  scene.remove(cube);\n-}\ncubes.length = 0;  // clears the cube array\nresTracker.dispose();\n```\n\nLet's arrange this code so that we can re-add the cube, texture, and material.\n\n```\nconst 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```\n\nAnd then let's write some code to add and remove things over time.\n\n```\nfunction waitSeconds(seconds = 0) {\n  return new Promise(resolve => 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```\n\nThis code will create the cube, texture and material, wait for 2 seconds, then dispose of them and wait for 1 second and repeat.\n\n[click here to open in a separate window](/manual/examples/cleanup-simple.html)\n\nSo that seems to work.\n\nFor a loaded file though it's a little more work. Most loaders only return an [`Object3D`](/docs/#api/en/core/Object3D) as a root of the hierarchy of objects they load so we need to discover what all the resources are.\n\nLet's update our `ResourceTracker` to try to do that.\n\nFirst we'll check if the object is an [`Object3D`](/docs/#api/en/core/Object3D) then track its geometry, material, and children\n\n```\nclass 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```\n\nNow, because any of `resource.geometry`, `resource.material`, and `resource.children` might be null or undefined we'll check at the top of `track`.\n\n```\nclass 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```\n\nAlso because `resource.children` is an array and because `resource.material` can be an array let's check for arrays\n\n```\nclass 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 => 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```\n\nAnd finally we need to walk the properties and uniforms of a material looking for textures.\n\n```\nclass 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 => 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```\n\nAnd with that let's take an example from [the article on loading gltf files](load-gltf.html) and make it load and free files.\n\n```\nconst gltfLoader = new GLTFLoader();\nfunction loadGLTF(url) {\n  return new Promise((resolve, reject) => {\n    gltfLoader.load(url, resolve, undefined, reject);\n  });\n}\n\nfunction waitSeconds(seconds = 0) {\n  return new Promise(resolve => 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```\n\nand we get\n\n[click here to open in a separate window](/manual/examples/cleanup-loaded-files.html)\n\nSome notes about the code.\n\nIf we wanted to load 2 or more files at once and free them at anytime we would use one `ResourceTracker` per file.\n\nAbove we are only tracking `gltf.scene` right after loading. Based on our current implementation of `ResourceTracker` that will track all the resources just loaded. If we added more things to the scene we need to decide whether or not to track them.\n\nFor example let's say after we loaded a character we put a tool in their hand by making the tool a child of their hand. As it is that tool will not be freed. I'm guessing more often than not this is what we want.\n\nThat brings up a point. Originally when I first wrote the `ResourceTracker` above I walked through everything inside the `dispose` method instead of `track`. It was only later as I thought about the tool as a child of hand case above that it became clear that tracking exactly what to free in `track` was more flexible and arguably more correct since we could then track what was loaded from the file rather than just freeing the state of the scene graph later.\n\nI honestly am not 100% happy with `ResourceTracker`. Doing things this way is not common in 3D engines. We shouldn't have to guess what resources were loaded, we should know. It would be nice if three.js changed so that all file loaders returned some standard object with references to all the resources loaded. At least at the moment, three.js doesn't give us any more info when loading a scene so this solution seems to work.\n\nI hope you find this example useful or at least a good reference for what is required to free resources in three.js", "code_blocks": [{"language": "javascript", "code": "const boxGeometry = new THREE.BoxGeometry(...);\nconst boxTexture = textureLoader.load(...);\nconst boxMaterial = new THREE.MeshPhongMaterial({map: texture});", "filename": ""}, {"language": "javascript", "code": "boxGeometry.dispose();\nboxTexture.dispose();\nboxMaterial.dispose();", "filename": ""}, {"language": "javascript", "code": "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}", "filename": ""}, {"language": "javascript", "code": "const resTracker = new ResourceTracker();", "filename": ""}, {"language": "javascript", "code": "const resTracker = new ResourceTracker();\n+const track = resTracker.track.bind(resTracker);", "filename": ""}, {"language": "javascript", "code": "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", "filename": ""}, {"language": "javascript", "code": "for (const cube of cubes) {\n  scene.remove(cube);\n}\ncubes.length = 0;  // clears the cubes array\nresTracker.dispose();", "filename": ""}, {"language": "javascript", "code": "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}", "filename": ""}, {"language": "javascript", "code": "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", "filename": ""}, {"language": "javascript", "code": "-for (const cube of cubes) {\n-  scene.remove(cube);\n-}\ncubes.length = 0;  // clears the cube array\nresTracker.dispose();", "filename": ""}, {"language": "javascript", "code": "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+}", "filename": ""}, {"language": "javascript", "code": "function waitSeconds(seconds = 0) {\n  return new Promise(resolve => 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();", "filename": ""}, {"language": "javascript", "code": "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}", "filename": ""}, {"language": "javascript", "code": "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}", "filename": ""}, {"language": "javascript", "code": "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 => 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}", "filename": ""}, {"language": "javascript", "code": "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 => 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}", "filename": ""}, {"language": "javascript", "code": "const gltfLoader = new GLTFLoader();\nfunction loadGLTF(url) {\n  return new Promise((resolve, reject) => {\n    gltfLoader.load(url, resolve, undefined, reject);\n  });\n}\n\nfunction waitSeconds(seconds = 0) {\n  return new Promise(resolve => 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();", "filename": ""}], "chunk_position": 52, "heading_path": "Cleanup > Cleanup", "breadcrumbs": "Cleanup > Cleanup > Cleanup"}, {"id": "ea5886f60372259b", "url": "https://threejs.org/manual/en/voxel-geometry.html", "page_title": "Voxel(Minecraft Like) Geometry", "page_type": "guide", "page_summary": "A tutorial on how to efficiently generate voxel-based geometry in Three.js, similar to Minecraft, by avoiding the common mistake of creating one mesh per voxel and instead building custom BufferGeometry with face culling, cells, textures, and editing support.", "heading": "Introduction", "content": "Page: Voxel(Minecraft Like) Geometry\nSection: Introduction\n\nI've seen this topic come up more than once in various places. That is basically, \"How do I make a voxel display like Minecraft\".\n\nMost people first attempt this by making a cube geometry and then making a mesh at each voxel position. Just for fun I tried this. I made a 16777216 element `Uint8Array` to represent a 256x256x256 cube of voxels.", "code_blocks": [{"language": "javascript", "code": "const cellSize = 256;\nconst cell = new Uint8Array(cellSize * cellSize * cellSize);", "filename": ""}], "chunk_position": 53, "heading_path": "Introduction > Introduction", "breadcrumbs": "Voxel(Minecraft Like) Geometry > Introduction > Introduction"}, {"id": "e113e9369b94009d", "url": "https://threejs.org/manual/en/voxel-geometry.html", "page_title": "Voxel(Minecraft Like) Geometry", "page_type": "guide", "page_summary": "A tutorial on how to efficiently generate voxel-based geometry in Three.js, similar to Minecraft, by avoiding the common mistake of creating one mesh per voxel and instead building custom BufferGeometry with face culling, cells, textures, and editing support.", "heading": "Filling Voxels with Hills", "content": "Page: Voxel(Minecraft Like) Geometry\nSection: Filling Voxels with Hills\n\nI then made a single layer with a kind of hills of sine waves like this", "code_blocks": [{"language": "javascript", "code": "for (let y = 0; y < cellSize; ++y) {\n  for (let z = 0; z < cellSize; ++z) {\n    for (let x = 0; x < 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 > y && height < y + 1) {\n        const offset = y * cellSize * cellSize +\n                       z * cellSize +\n                       x;\n        cell[offset] = 1;\n      }\n    }\n  }\n}", "filename": ""}], "chunk_position": 53, "heading_path": "Filling Voxels with Hills > Filling Voxels with Hills", "breadcrumbs": "Voxel(Minecraft Like) Geometry > Filling Voxels with Hills > Filling Voxels with Hills"}, {"id": "517da2a63e0840f2", "url": "https://threejs.org/manual/en/voxel-geometry.html", "page_title": "Voxel(Minecraft Like) Geometry", "page_type": "guide", "page_summary": "A tutorial on how to efficiently generate voxel-based geometry in Three.js, similar to Minecraft, by avoiding the common mistake of creating one mesh per voxel and instead building custom BufferGeometry with face culling, cells, textures, and editing support.", "heading": "Creating a Mesh per Voxel", "content": "Page: Voxel(Minecraft Like) Geometry\nSection: Creating a Mesh per Voxel\n\nI then walked through all the cells and if they were not 0 I created a mesh with a cube.", "code_blocks": [{"language": "javascript", "code": "const geometry = new THREE.BoxGeometry(1, 1, 1);\nconst material = new THREE.MeshPhongMaterial({color: 'green'});\n\nfor (let y = 0; y < cellSize; ++y) {\n  for (let z = 0; z < cellSize; ++z) {\n    for (let x = 0; x < 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}", "filename": ""}], "chunk_position": 53, "heading_path": "Creating a Mesh per Voxel > Creating a Mesh per Voxel", "breadcrumbs": "Voxel(Minecraft Like) Geometry > Creating a Mesh per Voxel > Creating a Mesh per Voxel"}, {"id": "ef67c2fb368ab88b", "url": "https://threejs.org/manual/en/voxel-geometry.html", "page_title": "Voxel(Minecraft Like) Geometry", "page_type": "guide", "page_summary": "A tutorial on how to efficiently generate voxel-based geometry in Three.js, similar to Minecraft, by avoiding the common mistake of creating one mesh per voxel and instead building custom BufferGeometry with face culling, cells, textures, and editing support.", "heading": "Performance Issues", "content": "Page: Voxel(Minecraft Like) Geometry\nSection: Performance Issues\n\nThe rest of the code is based on the example from the article on rendering on demand.\n\n[click here to open in a separate window](/manual/examples/voxel-geometry-separate-cubes.html)\n\nIt takes a while to start and if you try to move the camera it's likely too slow. Like the article on how to optimize lots of objects the problem is there are just way too many objects. 256x256 is 65536 boxes!\n\nUsing the technique of merging the geometry will fix the issue for this example but what if instead of just making a single layer we filled in everything below the ground with voxel. In other words change the loop filling in the voxels to this", "code_blocks": [{"language": "javascript", "code": "for (let y = 0; y < cellSize; ++y) {\n  for (let z = 0; z < cellSize; ++z) {\n    for (let x = 0; x < 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 > y && height < y + 1) {\n+      if (height < y + 1) {\n        const offset = y * cellSize * cellSize +\n                       z * cellSize +\n                       x;\n        cell[offset] = 1;\n      }\n    }\n  }\n}", "filename": ""}], "chunk_position": 53, "heading_path": "Performance Issues > Performance Issues", "breadcrumbs": "Voxel(Minecraft Like) Geometry > Performance Issues > Performance Issues"}, {"id": "f7f4c95c49137495", "url": "https://threejs.org/manual/en/voxel-geometry.html", "page_title": "Voxel(Minecraft Like) Geometry", "page_type": "guide", "page_summary": "A tutorial on how to efficiently generate voxel-based geometry in Three.js, similar to Minecraft, by avoiding the common mistake of creating one mesh per voxel and instead building custom BufferGeometry with face culling, cells, textures, and editing support.", "heading": "Memory and Face Culling", "content": "Page: Voxel(Minecraft Like) Geometry\nSection: Memory and Face Culling\n\nI tried it once just to see the results. It churned for about a minute and then crashed with *out of memory* 😅\n\nThere are several issues but the biggest issue is we're making all these faces inside the cubes that we can actually never see.\n\nIn other words lets say we have a box of voxels 3x2x2. By merging cubes we're getting this\n\nbut we really want this\n\nIn the top box there are faces between the voxels. Faces that are a waste since they can't be seen. It's not just one face between each voxel, there are 2 faces, one for each voxel facing its neighbor that are a waste. All these extra faces, especially for a large volume of voxels will kill performance.\n\nIt should be clear that we can't just merge geometry. We need to build it ourselves, taking into account that if a voxel has an adjacent neighbor it doesn't need the face facing that neighbor.\n\nThe next issue is that 256x256x256 is just too big. 16meg is a lot of memory and if nothing else in much of the space nothing is there so that's a lot of wasted memory. It's also a huge number of voxels, 16 million! That's too much to consider at once.\n\nA solution is to divide the area into smaller areas. Any area that has nothing in it needs no storage. Let's use 32x32x32 areas (that's 32k) and only create an area if something is in it. We'll call one of these larger 32x32x32 areas a \"cell\".\n\nLet's break this into pieces. First let's make a class to manage the voxel data.", "code_blocks": [{"language": "javascript", "code": "class VoxelWorld {\n  constructor(cellSize) {\n    this.cellSize = cellSize;\n  }\n}", "filename": ""}], "chunk_position": 53, "heading_path": "Memory and Face Culling > Memory and Face Culling", "breadcrumbs": "Voxel(Minecraft Like) Geometry > Memory and Face Culling > Memory and Face Culling"}, {"id": "934235ab3c8a6cce", "url": "https://threejs.org/manual/en/voxel-geometry.html", "page_title": "Voxel(Minecraft Like) Geometry", "page_type": "guide", "page_summary": "A tutorial on how to efficiently generate voxel-based geometry in Three.js, similar to Minecraft, by avoiding the common mistake of creating one mesh per voxel and instead building custom BufferGeometry with face culling, cells, textures, and editing support.", "heading": "Generating Geometry Data for a Cell", "content": "Page: Voxel(Minecraft Like) Geometry\nSection: Generating Geometry Data for a Cell\n\nLet's make the function that makes geometry for a cell. Let's assume you pass in a cell position. In other words if you want the geometry for the cell that covers voxels (0-31x, 0-31y, 0-31z) then you'd pass in 0,0,0. For the cell that covers voxels (32-63x, 0-31y, 0-31z) you'd pass in 1,0,0.\n\nWe need to be able to check the neighboring voxels so let's assume our class has a function `getVoxel` that given a voxel position returns the value of the voxel there. In other words if you pass it 35,0,0 and the cellSize is 32 it's going to look at cell 1,0,0 and in that cell it will look at voxel 3,0,0. Using this function we can look at a voxel's neighboring voxels even if they happen to be in neighboring cells.", "code_blocks": [{"language": "javascript", "code": "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 < cellSize; ++y) {\n+      const voxelY = startY + y;\n+      for (let z = 0; z < cellSize; ++z) {\n+        const voxelZ = startZ + z;\n+        for (let x = 0; x < 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+];", "filename": ""}], "chunk_position": 53, "heading_path": "Generating Geometry Data for a Cell > Generating Geometry Data for a Cell", "breadcrumbs": "Voxel(Minecraft Like) Geometry > Generating Geometry Data for a Cell > Generating Geometry Data for a Cell"}, {"id": "24a0d006469f7329", "url": "https://threejs.org/manual/en/voxel-geometry.html", "page_title": "Voxel(Minecraft Like) Geometry", "page_type": "guide", "page_summary": "A tutorial on how to efficiently generate voxel-based geometry in Three.js, similar to Minecraft, by avoiding the common mistake of creating one mesh per voxel and instead building custom BufferGeometry with face culling, cells, textures, and editing support.", "heading": "Building the Faces with Positions, Normals, and Indices", "content": "Page: Voxel(Minecraft Like) Geometry\nSection: Building the Faces with Positions, Normals, and Indices\n\nSo using the code above we know when we need a face. Let's generate the faces.", "code_blocks": [{"language": "javascript", "code": "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 < cellSize; ++y) {\n      const voxelY = startY + y;\n      for (let z = 0; z < cellSize; ++z) {\n        const voxelZ = startZ + z;\n        for (let x = 0; x < 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];", "filename": ""}], "chunk_position": 53, "heading_path": "Building the Faces with Positions, Normals, and Indices > Building the Faces with Positions, Normals, and Indices", "breadcrumbs": "Voxel(Minecraft Like) Geometry > Building the Faces with Positions, Normals, and Indices > Building the Faces with Positions, Normals, and Indices"}, {"id": "8c6a2656d42969bb", "url": "https://threejs.org/manual/en/voxel-geometry.html", "page_title": "Voxel(Minecraft Like) Geometry", "page_type": "guide", "page_summary": "A tutorial on how to efficiently generate voxel-based geometry in Three.js, similar to Minecraft, by avoiding the common mistake of creating one mesh per voxel and instead building custom BufferGeometry with face culling, cells, textures, and editing support.", "heading": "Supplying the getVoxel Function", "content": "Page: Voxel(Minecraft Like) Geometry\nSection: Supplying the getVoxel Function\n\nThe code above would make basic geometry data for us. We just need to supply the `getVoxel` function. Let's start with just one hard coded cell.", "code_blocks": [{"language": "javascript", "code": "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}", "filename": ""}], "chunk_position": 53, "heading_path": "Supplying the getVoxel Function > Supplying the getVoxel Function", "breadcrumbs": "Voxel(Minecraft Like) Geometry > Supplying the getVoxel Function > Supplying the getVoxel Function"}, {"id": "f4a709e8c851b22c", "url": "https://threejs.org/manual/en/voxel-geometry.html", "page_title": "Voxel(Minecraft Like) Geometry", "page_type": "guide", "page_summary": "A tutorial on how to efficiently generate voxel-based geometry in Three.js, similar to Minecraft, by avoiding the common mistake of creating one mesh per voxel and instead building custom BufferGeometry with face culling, cells, textures, and editing support.", "heading": "Adding setVoxel", "content": "Page: Voxel(Minecraft Like) Geometry\nSection: Adding setVoxel\n\nThis seems like it would work. Let's make a `setVoxel` function so we can set some data.", "code_blocks": [{"language": "javascript", "code": "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}", "filename": ""}], "chunk_position": 53, "heading_path": "Adding setVoxel > Adding setVoxel", "breadcrumbs": "Voxel(Minecraft Like) Geometry > Adding setVoxel > Adding setVoxel"}, {"id": "5fbda415a5160939", "url": "https://threejs.org/manual/en/voxel-geometry.html", "page_title": "Voxel(Minecraft Like) Geometry", "page_type": "guide", "page_summary": "A tutorial on how to efficiently generate voxel-based geometry in Three.js, similar to Minecraft, by avoiding the common mistake of creating one mesh per voxel and instead building custom BufferGeometry with face culling, cells, textures, and editing support.", "heading": "Refactoring with computeVoxelOffset", "content": "Page: Voxel(Minecraft Like) Geometry\nSection: Refactoring with computeVoxelOffset\n\nHmmm, I see a lot of repeated code. Let's fix that up", "code_blocks": [{"language": "javascript", "code": "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}", "filename": ""}], "chunk_position": 53, "heading_path": "Refactoring with computeVoxelOffset > Refactoring with computeVoxelOffset", "breadcrumbs": "Voxel(Minecraft Like) Geometry > Refactoring with computeVoxelOffset > Refactoring with computeVoxelOffset"}, {"id": "91455f57ad37fdc5", "url": "https://threejs.org/manual/en/voxel-geometry.html", "page_title": "Voxel(Minecraft Like) Geometry", "page_type": "guide", "page_summary": "A tutorial on how to efficiently generate voxel-based geometry in Three.js, similar to Minecraft, by avoiding the common mistake of creating one mesh per voxel and instead building custom BufferGeometry with face culling, cells, textures, and editing support.", "heading": "Filling the First Cell", "content": "Page: Voxel(Minecraft Like) Geometry\nSection: Filling the First Cell\n\nNow let's make some code to fill out the first cell with voxels.", "code_blocks": [{"language": "javascript", "code": "const cellSize = 32;\n\nconst world = new VoxelWorld(cellSize);\n\nfor (let y = 0; y < cellSize; ++y) {\n  for (let z = 0; z < cellSize; ++z) {\n    for (let x = 0; x < 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 < height) {\n        world.setVoxel(x, y, z, 1);\n      }\n    }\n  }\n}", "filename": ""}], "chunk_position": 53, "heading_path": "Filling the First Cell > Filling the First Cell", "breadcrumbs": "Voxel(Minecraft Like) Geometry > Filling the First Cell > Filling the First Cell"}, {"id": "cd022fed7c4d37af", "url": "https://threejs.org/manual/en/voxel-geometry.html", "page_title": "Voxel(Minecraft Like) Geometry", "page_type": "guide", "page_summary": "A tutorial on how to efficiently generate voxel-based geometry in Three.js, similar to Minecraft, by avoiding the common mistake of creating one mesh per voxel and instead building custom BufferGeometry with face culling, cells, textures, and editing support.", "heading": "Generating BufferGeometry from Voxel Data", "content": "Page: Voxel(Minecraft Like) Geometry\nSection: Generating BufferGeometry from Voxel Data\n\nand some code to actually generate geometry like we covered in the article on custom BufferGeometry.", "code_blocks": [{"language": "javascript", "code": "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);", "filename": ""}], "chunk_position": 53, "heading_path": "Generating BufferGeometry from Voxel Data > Generating BufferGeometry from Voxel Data", "breadcrumbs": "Voxel(Minecraft Like) Geometry > Generating BufferGeometry from Voxel Data > Generating BufferGeometry from Voxel Data"}, {"id": "a659106eed7859dd", "url": "https://threejs.org/manual/en/voxel-geometry.html", "page_title": "Voxel(Minecraft Like) Geometry", "page_type": "guide", "page_summary": "A tutorial on how to efficiently generate voxel-based geometry in Three.js, similar to Minecraft, by avoiding the common mistake of creating one mesh per voxel and instead building custom BufferGeometry with face culling, cells, textures, and editing support.", "heading": "Adding Textures", "content": "Page: Voxel(Minecraft Like) Geometry\nSection: Adding Textures\n\nlet's try it\n\n[click here to open in a separate window](/manual/examples/voxel-geometry-culled-faces.html)\n\nThat seems to be working! Okay, let's add in textures.\n\nSearching on the net I found [this set](https://www.minecraftforum.net/forums/mapping-and-modding-java-edition/resource-packs/1245961-16x-1-7-4-wip-flourish) of [CC-BY-NC-SA](https://creativecommons.org/licenses/by-nc-sa/4.0/) licensed minecraft textures by [Joshtimus](https://www.minecraftforum.net/members/Joshtimus). I picked a few at random and built this [texture atlas](https://www.google.com/search?q=texture+atlas).\n\n<img src=\"../examples/resources/images/minecraft/flourish-cc-by-nc-sa.png\">\n\nTo make things simple they are arranged a voxel type per column where the top row is the side of a voxel. The 2nd row is the top of voxel, and the 3rd row is the bottom of the voxel.\n\nKnowing that we can add info to our `VoxelWorld.faces` data to specify for each face which row to use and the UVs to use for that face.", "code_blocks": [{"language": "javascript", "code": "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];", "filename": ""}], "chunk_position": 53, "heading_path": "Adding Textures > Adding Textures", "breadcrumbs": "Voxel(Minecraft Like) Geometry > Adding Textures > Adding Textures"}, {"id": "b2530da5e726800e", "url": "https://threejs.org/manual/en/voxel-geometry.html", "page_title": "Voxel(Minecraft Like) Geometry", "page_type": "guide", "page_summary": "A tutorial on how to efficiently generate voxel-based geometry in Three.js, similar to Minecraft, by avoiding the common mistake of creating one mesh per voxel and instead building custom BufferGeometry with face culling, cells, textures, and editing support.", "heading": "Updating Geometry Generation for UVs", "content": "Page: Voxel(Minecraft Like) Geometry\nSection: Updating Geometry Generation for UVs\n\nAnd we can update the code to use that data. We need to know the size of a tile in the texture atlas and the dimensions of the texture.", "code_blocks": [{"language": "javascript", "code": "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 < cellSize; ++y) {\n      const voxelY = startY + y;\n      for (let z = 0; z < cellSize; ++z) {\n        const voxelZ = startZ + z;\n        for (let x = 0; x < 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}", "filename": ""}], "chunk_position": 53, "heading_path": "Updating Geometry Generation for UVs > Updating Geometry Generation for UVs", "breadcrumbs": "Voxel(Minecraft Like) Geometry > Updating Geometry Generation for UVs > Updating Geometry Generation for UVs"}, {"id": "a4e94eafc6c659d1", "url": "https://threejs.org/manual/en/voxel-geometry.html", "page_title": "Voxel(Minecraft Like) Geometry", "page_type": "guide", "page_summary": "A tutorial on how to efficiently generate voxel-based geometry in Three.js, similar to Minecraft, by avoiding the common mistake of creating one mesh per voxel and instead building custom BufferGeometry with face culling, cells, textures, and editing support.", "heading": "Loading and Configuring the Texture", "content": "Page: Voxel(Minecraft Like) Geometry\nSection: Loading and Configuring the Texture\n\nWe then need to load the texture", "code_blocks": [{"language": "javascript", "code": "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;", "filename": ""}], "chunk_position": 53, "heading_path": "Loading and Configuring the Texture > Loading and Configuring the Texture", "breadcrumbs": "Voxel(Minecraft Like) Geometry > Loading and Configuring the Texture > Loading and Configuring the Texture"}, {"id": "1dc376c04da7e78e", "url": "https://threejs.org/manual/en/voxel-geometry.html", "page_title": "Voxel(Minecraft Like) Geometry", "page_type": "guide", "page_summary": "A tutorial on how to efficiently generate voxel-based geometry in Three.js, similar to Minecraft, by avoiding the common mistake of creating one mesh per voxel and instead building custom BufferGeometry with face culling, cells, textures, and editing support.", "heading": "Passing Settings to VoxelWorld", "content": "Page: Voxel(Minecraft Like) Geometry\nSection: Passing Settings to VoxelWorld\n\nand pass the settings to the `VoxelWorld` class", "code_blocks": [{"language": "javascript", "code": "+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+});", "filename": ""}], "chunk_position": 53, "heading_path": "Passing Settings to VoxelWorld > Passing Settings to VoxelWorld", "breadcrumbs": "Voxel(Minecraft Like) Geometry > Passing Settings to VoxelWorld > Passing Settings to VoxelWorld"}, {"id": "398b51df4b8d3269", "url": "https://threejs.org/manual/en/voxel-geometry.html", "page_title": "Voxel(Minecraft Like) Geometry", "page_type": "guide", "page_summary": "A tutorial on how to efficiently generate voxel-based geometry in Three.js, similar to Minecraft, by avoiding the common mistake of creating one mesh per voxel and instead building custom BufferGeometry with face culling, cells, textures, and editing support.", "heading": "Using UVs and Texture in the Material", "content": "Page: Voxel(Minecraft Like) Geometry\nSection: Using UVs and Texture in the Material\n\nLet's actually use the UVs when we create the geometry and the texture when we make the material", "code_blocks": [{"language": "javascript", "code": "-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);", "filename": ""}], "chunk_position": 53, "heading_path": "Using UVs and Texture in the Material > Using UVs and Texture in the Material", "breadcrumbs": "Voxel(Minecraft Like) Geometry > Using UVs and Texture in the Material > Using UVs and Texture in the Material"}, {"id": "205824f8acd90bda", "url": "https://threejs.org/manual/en/voxel-geometry.html", "page_title": "Voxel(Minecraft Like) Geometry", "page_type": "guide", "page_summary": "A tutorial on how to efficiently generate voxel-based geometry in Three.js, similar to Minecraft, by avoiding the common mistake of creating one mesh per voxel and instead building custom BufferGeometry with face culling, cells, textures, and editing support.", "heading": "Using Multiple Voxel Types", "content": "Page: Voxel(Minecraft Like) Geometry\nSection: Using Multiple Voxel Types\n\nOne last thing, we actually need to set some voxels to use different textures.", "code_blocks": [{"language": "javascript", "code": "for (let y = 0; y < cellSize; ++y) {\n  for (let z = 0; z < cellSize; ++z) {\n    for (let x = 0; x < 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 < 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+}", "filename": ""}], "chunk_position": 53, "heading_path": "Using Multiple Voxel Types > Using Multiple Voxel Types", "breadcrumbs": "Voxel(Minecraft Like) Geometry > Using Multiple Voxel Types > Using Multiple Voxel Types"}, {"id": "a5d700f52a39420b", "url": "https://threejs.org/manual/en/voxel-geometry.html", "page_title": "Voxel(Minecraft Like) Geometry", "page_type": "guide", "page_summary": "A tutorial on how to efficiently generate voxel-based geometry in Three.js, similar to Minecraft, by avoiding the common mistake of creating one mesh per voxel and instead building custom BufferGeometry with face culling, cells, textures, and editing support.", "heading": "Result with Textures", "content": "Page: Voxel(Minecraft Like) Geometry\nSection: Result with Textures\n\nand with that we get textures!\n\n[click here to open in a separate window](/manual/examples/voxel-geometry-culled-faces-with-textures.html)", "code_blocks": [], "chunk_position": 53, "heading_path": "Result with Textures > Result with Textures", "breadcrumbs": "Voxel(Minecraft Like) Geometry > Result with Textures > Result with Textures"}, {"id": "786b0cfb9c98ca35", "url": "https://threejs.org/manual/en/voxel-geometry.html", "page_title": "Voxel(Minecraft Like) Geometry", "page_type": "guide", "page_summary": "A tutorial on how to efficiently generate voxel-based geometry in Three.js, similar to Minecraft, by avoiding the common mistake of creating one mesh per voxel and instead building custom BufferGeometry with face culling, cells, textures, and editing support.", "heading": "Supporting Multiple Cells", "content": "Page: Voxel(Minecraft Like) Geometry\nSection: Supporting Multiple Cells\n\nLet's make it support more than one cell.\n\nTo do this lets store cells in an object using cell ids. A cell id will just be a cell's coordinates separated by a comma. In other words if we ask for voxel 35,0,0 that is in cell 1,0,0 so its id is `\"1,0,0\"`.", "code_blocks": [{"language": "javascript", "code": "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 ${cellX},${cellY},${cellZ};\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}", "filename": ""}], "chunk_position": 53, "heading_path": "Supporting Multiple Cells > Supporting Multiple Cells", "breadcrumbs": "Voxel(Minecraft Like) Geometry > Supporting Multiple Cells > Supporting Multiple Cells"}, {"id": "3ba8214a6a9c5929", "url": "https://threejs.org/manual/en/voxel-geometry.html", "page_title": "Voxel(Minecraft Like) Geometry", "page_type": "guide", "page_summary": "A tutorial on how to efficiently generate voxel-based geometry in Three.js, similar to Minecraft, by avoiding the common mistake of creating one mesh per voxel and instead building custom BufferGeometry with face culling, cells, textures, and editing support.", "heading": "Adding New Cells Automatically", "content": "Page: Voxel(Minecraft Like) Geometry\nSection: Adding New Cells Automatically\n\nand now we can make `setVoxel` add new cells if we try to set a voxel in a cell that does not yet exist", "code_blocks": [{"language": "javascript", "code": "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+  }", "filename": ""}], "chunk_position": 53, "heading_path": "Adding New Cells Automatically > Adding New Cells Automatically", "breadcrumbs": "Voxel(Minecraft Like) Geometry > Adding New Cells Automatically > Adding New Cells Automatically"}, {"id": "7b8f6321eea09b35", "url": "https://threejs.org/manual/en/voxel-geometry.html", "page_title": "Voxel(Minecraft Like) Geometry", "page_type": "guide", "page_summary": "A tutorial on how to efficiently generate voxel-based geometry in Three.js, similar to Minecraft, by avoiding the common mistake of creating one mesh per voxel and instead building custom BufferGeometry with face culling, cells, textures, and editing support.", "heading": "Adding the UI", "content": "Page: Voxel(Minecraft Like) Geometry\nSection: Adding the UI\n\nLet's make this editable.\n\nFirst we`ll add a UI. Using radio buttons we can make an 8x2 array of tiles", "code_blocks": [{"language": "html", "code": "<body>\n  <canvas id=\"c\"></canvas>\n+  <div id=\"ui\">\n+    <div class=\"tiles\">\n+      <input type=\"radio\" name=\"voxel\" id=\"voxel1\" value=\"1\"><label for=\"voxel1\" style=\"background-position:   -0% -0%\"></label>\n+      <input type=\"radio\" name=\"voxel\" id=\"voxel2\" value=\"2\"><label for=\"voxel2\" style=\"background-position: -100% -0%\"></label>\n+      <input type=\"radio\" name=\"voxel\" id=\"voxel3\" value=\"3\"><label for=\"voxel3\" style=\"background-position: -200% -0%\"></label>\n+      <input type=\"radio\" name=\"voxel\" id=\"voxel4\" value=\"4\"><label for=\"voxel4\" style=\"background-position: -300% -0%\"></label>\n+      <input type=\"radio\" name=\"voxel\" id=\"voxel5\" value=\"5\"><label for=\"voxel5\" style=\"background-position: -400% -0%\"></label>\n+      <input type=\"radio\" name=\"voxel\" id=\"voxel6\" value=\"6\"><label for=\"voxel6\" style=\"background-position: -500% -0%\"></label>\n+      <input type=\"radio\" name=\"voxel\" id=\"voxel7\" value=\"7\"><label for=\"voxel7\" style=\"background-position: -600% -0%\"></label>\n+      <input type=\"radio\" name=\"voxel\" id=\"voxel8\" value=\"8\"><label for=\"voxel8\" style=\"background-position: -700% -0%\"></label>\n+    </div>\n+    <div class=\"tiles\">\n+      <input type=\"radio\" name=\"voxel\" id=\"voxel9\"  value=\"9\" ><label for=\"voxel9\"  style=\"background-position:  -800% -0%\"></label>\n+      <input type=\"radio\" name=\"voxel\" id=\"voxel10\" value=\"10\"><label for=\"voxel10\" style=\"background-position:  -900% -0%\"></label>\n+      <input type=\"radio\" name=\"voxel\" id=\"voxel11\" value=\"11\"><label for=\"voxel11\" style=\"background-position: -1000% -0%\"></label>\n+      <input type=\"radio\" name=\"voxel\" id=\"voxel12\" value=\"12\"><label for=\"voxel12\" style=\"background-position: -1100% -0%\"></label>\n+      <input type=\"radio\" name=\"voxel\" id=\"voxel13\" value=\"13\"><label for=\"voxel13\" style=\"background-position: -1200% -0%\"></label>\n+      <input type=\"radio\" name=\"voxel\" id=\"voxel14\" value=\"14\"><label for=\"voxel14\" style=\"background-position: -1300% -0%\"></label>\n+      <input type=\"radio\" name=\"voxel\" id=\"voxel15\" value=\"15\"><label for=\"voxel15\" style=\"background-position: -1400% -0%\"></label>\n+      <input type=\"radio\" name=\"voxel\" id=\"voxel16\" value=\"16\"><label for=\"voxel16\" style=\"background-position: -1500% -0%\"></label>\n+    </div>\n+  </div>\n</body>", "filename": ""}], "chunk_position": 53, "heading_path": "Adding the UI > Adding the UI", "breadcrumbs": "Voxel(Minecraft Like) Geometry > Adding the UI > Adding the UI"}, {"id": "10d901b74c3990ed", "url": "https://threejs.org/manual/en/voxel-geometry.html", "page_title": "Voxel(Minecraft Like) Geometry", "page_type": "guide", "page_summary": "A tutorial on how to efficiently generate voxel-based geometry in Three.js, similar to Minecraft, by avoiding the common mistake of creating one mesh per voxel and instead building custom BufferGeometry with face culling, cells, textures, and editing support.", "heading": "CSS for the UI", "content": "Page: Voxel(Minecraft Like) Geometry\nSection: CSS for the UI\n\nAnd add some CSS to style it, display the tiles and highlight the current selection", "code_blocks": [{"language": "css", "code": "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+}", "filename": ""}], "chunk_position": 53, "heading_path": "CSS for the UI > CSS for the UI", "breadcrumbs": "Voxel(Minecraft Like) Geometry > CSS for the UI > CSS for the UI"}, {"id": "6c3c09f61f78d45d", "url": "https://threejs.org/manual/en/voxel-geometry.html", "page_title": "Voxel(Minecraft Like) Geometry", "page_type": "guide", "page_summary": "A tutorial on how to efficiently generate voxel-based geometry in Three.js, similar to Minecraft, by avoiding the common mistake of creating one mesh per voxel and instead building custom BufferGeometry with face culling, cells, textures, and editing support.", "heading": "UX Behavior", "content": "Page: Voxel(Minecraft Like) Geometry\nSection: UX Behavior\n\nThe UX will be as follows. If no tile is selected and you click a voxel that voxel will be erased or if you click a voxel and are holding the shift key it will be erased. Otherwise if a tiles is selected it will be added. You can deselect the selected tile type by clicking it again.\n\nThis code will let the user unselect the highlighted radio button.", "code_blocks": [{"language": "javascript", "code": "let currentVoxel = 0;\nlet currentId;\n\ndocument.querySelectorAll('#ui .tiles input[type=radio][name=voxel]').forEach((elem) => {\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}", "filename": ""}], "chunk_position": 53, "heading_path": "UX Behavior > UX Behavior", "breadcrumbs": "Voxel(Minecraft Like) Geometry > UX Behavior > UX Behavior"}, {"id": "5ad1144a6e579f8f", "url": "https://threejs.org/manual/en/voxel-geometry.html", "page_title": "Voxel(Minecraft Like) Geometry", "page_type": "guide", "page_summary": "A tutorial on how to efficiently generate voxel-based geometry in Three.js, similar to Minecraft, by avoiding the common mistake of creating one mesh per voxel and instead building custom BufferGeometry with face culling, cells, textures, and editing support.", "heading": "Placing Voxels on Click", "content": "Page: Voxel(Minecraft Like) Geometry\nSection: Placing Voxels on Click\n\nAnd this below code will let us set a voxel based on where the user clicks. It uses code similar to the code we made in the article on picking but it's not using the built in `RayCaster`. Instead it's using `VoxelWorld.intersectRay` which returns the position of intersection and the normal of the face hit.", "code_blocks": [{"language": "javascript", "code": "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  > 0)\n    const pos = intersection.position.map((v, ndx) => {\n      return v + intersection.normal[ndx] * (voxelId > 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 < 5 && mouse.moveY < 5) {\n    placeVoxel(event);\n  }\n  window.removeEventListener('pointermove', recordMovement);\n  window.removeEventListener('pointerup', placeVoxelIfNoMovement);\n}\ncanvas.addEventListener('pointerdown', (event) => {\n  event.preventDefault();\n  recordStartPosition(event);\n  window.addEventListener('pointermove', recordMovement);\n  window.addEventListener('pointerup', placeVoxelIfNoMovement);\n}, {passive: false});\ncanvas.addEventListener('touchstart', (event) => {\n  // stop scrolling\n  event.preventDefault();\n}, {passive: false});", "filename": ""}], "chunk_position": 53, "heading_path": "Placing Voxels on Click > Placing Voxels on Click", "breadcrumbs": "Voxel(Minecraft Like) Geometry > Placing Voxels on Click > Placing Voxels on Click"}, {"id": "69ec37c5db38490e", "url": "https://threejs.org/manual/en/voxel-geometry.html", "page_title": "Voxel(Minecraft Like) Geometry", "page_type": "guide", "page_summary": "A tutorial on how to efficiently generate voxel-based geometry in Three.js, similar to Minecraft, by avoiding the common mistake of creating one mesh per voxel and instead building custom BufferGeometry with face culling, cells, textures, and editing support.", "heading": "Mouse Behavior Explanation", "content": "Page: Voxel(Minecraft Like) Geometry\nSection: Mouse Behavior Explanation\n\nThere's a lot going on in the code above. Basically the mouse has a dual purpose. One is to move the camera. The other is to edit the world. Placing/Erasing a voxel happen when you let off the mouse but only if you have not moved the mouse since you first pressed down. This is just a guess that if you did move the mouse you were trying to move the camera, not place a block. `moveX` and `moveY` are in absolute movement so if you move to the left 10 and then back to the right 10 you'll have moved 20 units. In that case the user likely was just rotating the model back and forth and does not want to place a block. I didn't do any testing to see if `5` is a good range or not.\n\nIn the code we call `world.setVoxel` to set a voxel and then `updateVoxelGeometry` to update the three.js geometry based on what's changed.\n\nLet's make that now. If the user clicks a voxel on the edge of a cell then the geometry for the voxel in the adjacent cell might need new geometry. This means we need to check the cell for the voxel we just edited as well as in all 6 directions from that cell.", "code_blocks": [{"language": "javascript", "code": "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}", "filename": ""}], "chunk_position": 53, "heading_path": "Mouse Behavior Explanation > Mouse Behavior Explanation", "breadcrumbs": "Voxel(Minecraft Like) Geometry > Mouse Behavior Explanation > Mouse Behavior Explanation"}, {"id": "78a7855b75942333", "url": "https://threejs.org/manual/en/voxel-geometry.html", "page_title": "Voxel(Minecraft Like) Geometry", "page_type": "guide", "page_summary": "A tutorial on how to efficiently generate voxel-based geometry in Three.js, similar to Minecraft, by avoiding the common mistake of creating one mesh per voxel and instead building custom BufferGeometry with face culling, cells, textures, and editing support.", "heading": "Simplifying Adjacent Cell Updates", "content": "Page: Voxel(Minecraft Like) Geometry\nSection: Simplifying Adjacent Cell Updates\n\nI thought about checking for adjacent cells like \n\n```\nconst 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```\n\nand there would be 4 more checks for the other 4 directions but it occurred to me the code would be much simpler with just an array of offsets and saving off the cell ids of the cells we already updated. If the updated voxel is not on the edge of a cell then the test will quickly reject updating the same cell.\n\nFor `updateCellGeometry` we're just going to take the code we had before that was generating the geometry for one cell and make it handle multiple cells.", "code_blocks": [{"language": "javascript", "code": "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}", "filename": ""}], "chunk_position": 53, "heading_path": "Simplifying Adjacent Cell Updates > Simplifying Adjacent Cell Updates", "breadcrumbs": "Voxel(Minecraft Like) Geometry > Simplifying Adjacent Cell Updates > Simplifying Adjacent Cell Updates"}, {"id": "83041f91dc01b881", "url": "https://threejs.org/manual/en/voxel-geometry.html", "page_title": "Voxel(Minecraft Like) Geometry", "page_type": "guide", "page_summary": "A tutorial on how to efficiently generate voxel-based geometry in Three.js, similar to Minecraft, by avoiding the common mistake of creating one mesh per voxel and instead building custom BufferGeometry with face culling, cells, textures, and editing support.", "heading": "Final Result", "content": "Page: Voxel(Minecraft Like) Geometry\nSection: Final Result\n\nThe code above checks a map of cell ids to meshes. If we ask for a cell that doesn't exist a new [`Mesh`](/docs/#api/en/objects/Mesh) is made and added to the correct place in world space. At the end we update the attributes and indices with the new data.\n\n[click here to open in a separate window](/manual/examples/voxel-geometry-culled-faces-ui.html)", "code_blocks": [], "chunk_position": 53, "heading_path": "Final Result > Final Result", "breadcrumbs": "Voxel(Minecraft Like) Geometry > Final Result > Final Result"}, {"id": "c2fe327d5dc7b425", "url": "https://threejs.org/manual/en/voxel-geometry.html", "page_title": "Voxel(Minecraft Like) Geometry", "page_type": "guide", "page_summary": "A tutorial on how to efficiently generate voxel-based geometry in Three.js, similar to Minecraft, by avoiding the common mistake of creating one mesh per voxel and instead building custom BufferGeometry with face culling, cells, textures, and editing support.", "heading": "Notes and Further Considerations", "content": "Page: Voxel(Minecraft Like) Geometry\nSection: Notes and Further Considerations\n\nSome notes:\n\n`RayCaster` might have worked just fine. I didn't try it. Instead I found [a voxel specific raycaster](https://citeseerx.ist.psu.edu/viewdoc/download?doi=10.1.1.42.3443&rep=rep1&type=pdf). that is optimized for voxels.\n\nI made `intersectRay` part of VoxelWorld because it seemed like if it gets too slow we could raycast against cells before raycasting on voxels as a simple speed up if it becomes too slow.\n\nYou might want to change the length of the raycast as currently it's all the way to Z-far. I expect if the user clicks something too far way they don't really want to be placing blocks on the other side of the world that are 1 or 2 pixel large.\n\nCalling `geometry.computeBoundingSphere` might be slow. We could just manually set the bounding sphere to the fit the entire cell.\n\nDo we want remove cells if all voxels in that cell are 0? That would probably be reasonable change if we wanted to ship this.\n\nThinking about how this works it's clear the absolute worst case is a checkerboard of on and off voxels. I don't know off the top of my head what other strategies to use if things get too slow. Maybe getting too slow would just encourage the user not to make giant checkerboard areas.\n\nTo keep it simple the texture atlas is just 1 column per voxel type. It would be better to make something more flexible where we have a table of voxel types and each type can specify where its face textures are in the atlas. As it is lots of space is wasted.\n\nLooking at real minecraft there are tiles that are not voxels, not cubes. Like a fence tile or flowers. To do that we'd again need some table of voxel types and for each voxel whether it's a cube or some other geometry. If it's not a cube the neighbor check when generating the geometry would also need to change. A flower voxel next to another voxel should not remove the faces between them.\n\nIf you want to make some minecraft like thing using three.js I hope this has given you some ideas where to start and how to generate some what efficient geometry.", "code_blocks": [], "chunk_position": 53, "heading_path": "Notes and Further Considerations > Notes and Further Considerations", "breadcrumbs": "Voxel(Minecraft Like) Geometry > Notes and Further Considerations > Notes and Further Considerations"}, {"id": "d20b12c513c57ef5", "url": "https://threejs.org/manual/en/game.html", "page_title": "Making a Game", "page_type": "guide", "page_summary": "A comprehensive guide on building a game with three.js, covering model loading with LoadingManager, animation playback, Entity Component System architecture, input systems, camera frustum checks, and a Finite State Machine for AI behavior.", "heading": "Introduction", "content": "Page: Making a Game\nSection: Introduction\n\nMany people want to write games using three.js. This article will hopefully give you some ideas on how to start.\n\nAt least at the time I'm writing this article it's probably going to be the longest article on this site. It's possible the code here is massively over engineered but as I wrote each new feature I'd run into a problem that needed a solution I'm used to from other games I've written. In other words each new solution seemed important so I'll try to show why. Of course the smaller your game the less you might need some of the solutions shown here but this is a pretty small game and yet with the complexities of 3D characters many things take more organization than they might with 2D characters.\n\nAs an example if you're making PacMan in 2D, when PacMan turns a corner that happens instantly at 90 degrees. There is no in-between step. But in a 3D game often we need the character to rotate over several frames. That simple change can add a bunch of complexity and require different solutions.\n\nThe majority of the code here will not really be three.js and that's important to note, three.js is not a game engine. Three.js is a 3D library. It provides a scene graph and features for displaying 3D objects added to that scene graph but it does not provide all the other things needed to make a game. No collisions, no physics, no input systems, no path finding, etc, etc... So, we'll have to provide those things ourselves.\n\nI ended up writing quite a bit of code to make this simple unfinished game like thing and again, it's certainly possible I over engineered and there are simpler solutions but I feel like I actually didn't write enough code and hopefully I can explain what I think is missing.\n\nMany of the ideas here are heavily influenced by Unity. If you're not familiar with Unity that probably does not matter. I only bring it up as 10s of 1000s of games have shipped using these ideas.\n\nLet's start with the three.js parts. We need to load models for our game.\n\nAt opengameart.org I found this animated knight model by quaternius\n\nquaternius also made these animated animals.\n\nThese seem like good models to start with so the first thing we need to do is load them.", "code_blocks": [], "chunk_position": 54, "heading_path": "Introduction > Introduction", "breadcrumbs": "Making a Game > Introduction > Introduction"}, {"id": "12409ffb2a7904f5", "url": "https://threejs.org/manual/en/game.html", "page_title": "Making a Game", "page_type": "guide", "page_summary": "A comprehensive guide on building a game with three.js, covering model loading with LoadingManager, animation playback, Entity Component System architecture, input systems, camera frustum checks, and a Finite State Machine for AI behavior.", "heading": "Loading Models with LoadingManager", "content": "Page: Making a Game\nSection: Loading Models with LoadingManager\n\nWe covered loading glTF files before. The difference this time is we need to load multiple models and we can't start the game until all the models are loaded.\n\nFortunately three.js provides the LoadingManager just for this purpose. We create a LoadingManager and pass it to the other loaders. The LoadingManager provides both onProgress and onLoad properties we can attach callbacks to. The onLoad callback will be called when all files have been loaded. The onProgress callback as called after each individual file arrives to give as a chance to show loading progress.\n\nStarting with the code from loading a glTF file I removed all the code related to framing the scene and added this code to load all models.\n\nThis code will load all the models above and the LoadingManager will call init when done. We'll use the models object later to let us access the loaded models so the GLTFLoader callback for each individual model attaches the loaded data to that model's info.", "code_blocks": [{"language": "javascript", "code": "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) => {\n      model.gltf = gltf;\n    });\n  }\n}\n\nfunction init() {\n  // TBD\n}", "filename": ""}], "chunk_position": 54, "heading_path": "Loading Models with LoadingManager > Loading Models with LoadingManager", "breadcrumbs": "Making a Game > Loading Models with LoadingManager > Loading Models with LoadingManager"}, {"id": "3d9af4852d43a8a5", "url": "https://threejs.org/manual/en/game.html", "page_title": "Making a Game", "page_type": "guide", "page_summary": "A comprehensive guide on building a game with three.js, covering model loading with LoadingManager, animation playback, Entity Component System architecture, input systems, camera frustum checks, and a Finite State Machine for AI behavior.", "heading": "Adding a Progress Bar", "content": "Page: Making a Game\nSection: Adding a Progress Bar\n\nAll the models with all their animation are currently about 6.6meg. That's a pretty big download. Assuming your server supports compression (the server this site runs on does) it's able to compress them to around 1.4meg. That's definitely better than 6.6meg bit it's still not a tiny amount of data. It would probably be good if we added a progress bar so the user has some idea how much longer they have to wait.\n\nSo, let's add an onProgress callback. It will be called with 3 arguments, the url of the last loaded object and then the number of items loaded so far as well as the total number of items.\n\nLet's setup some HTML for a loading bar\n\nWe'll look up the #progressbar div and we can set the width from 0% to 100% to show our progress. All we need to do is set that in our callback.\n\nWe already setup init to be called when all the models are loaded so we can turn off the progress bar by hiding the #loading element.\n\nHere's a bunch of CSS for styling the bar. The CSS makes the #loading div the full size of the page and centers its children. The CSS makes a .progress area to contain the progress bar. The CSS also gives the progress bar a CSS animation of diagonal stripes.", "code_blocks": [{"language": "html", "code": "<body>\n  <canvas id=\"c\"></canvas>\n+  <div id=\"loading\">\n+    <div>\n+      <div>...loading...</div>\n+      <div class=\"progress\"><div id=\"progressbar\"></div></div>\n+    </div>\n+  </div>\n</body>", "filename": ""}, {"language": "javascript", "code": "const manager = new THREE.LoadingManager();\nmanager.onLoad = init;\n\n+const progressbarElem = document.querySelector('#progressbar');\n+manager.onProgress = (url, itemsLoaded, itemsTotal) => {\n+  progressbarElem.style.width = ${itemsLoaded / itemsTotal * 100 | 0}%;\n+};", "filename": ""}, {"language": "javascript", "code": "function init() {\n+  // hide the loading bar\n+  const loadingElem = document.querySelector('#loading');\n+  loadingElem.style.display = 'none';\n}", "filename": ""}, {"language": "css", "code": "#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>div>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}", "filename": ""}], "chunk_position": 54, "heading_path": "Adding a Progress Bar > Adding a Progress Bar", "breadcrumbs": "Making a Game > Adding a Progress Bar > Adding a Progress Bar"}, {"id": "569c3bd235037165", "url": "https://threejs.org/manual/en/game.html", "page_title": "Making a Game", "page_type": "guide", "page_summary": "A comprehensive guide on building a game with three.js, covering model loading with LoadingManager, animation playback, Entity Component System architecture, input systems, camera frustum checks, and a Finite State Machine for AI behavior.", "heading": "Prepping Models and Animations", "content": "Page: Making a Game\nSection: Prepping Models and Animations\n\nNow that we have a progress bar let's deal with the models. These models have animations and we want to be able to access those animations. Animations are stored in an array by default be we'd like to be able to easily access them by name so let's setup an animations property for each model to do that. Note of course this means animations must have unique names.", "code_blocks": [{"language": "javascript", "code": "+function prepModelsAndAnimations() {\n+  Object.values(models).forEach(model => {\n+    const animsByName = {};\n+    model.gltf.animations.forEach((clip) => {\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}", "filename": ""}], "chunk_position": 54, "heading_path": "Prepping Models and Animations > Prepping Models and Animations", "breadcrumbs": "Making a Game > Prepping Models and Animations > Prepping Models and Animations"}, {"id": "a306259901fad87f", "url": "https://threejs.org/manual/en/game.html", "page_title": "Making a Game", "page_type": "guide", "page_summary": "A comprehensive guide on building a game with three.js, covering model loading with LoadingManager, animation playback, Entity Component System architecture, input systems, camera frustum checks, and a Finite State Machine for AI behavior.", "heading": "Displaying Animated Models with SkeletonUtils.clone", "content": "Page: Making a Game\nSection: Displaying Animated Models with SkeletonUtils.clone\n\nLet's display the animated models.\n\nUnlike the previous example of loading a glTF file This time we probably want to be able to display more than one instance of each model. To do this, instead of adding the loaded gltf scene directly like we did in the article on loading a glTF, we instead want to clone the scene and in particular we want to clone it for skinned animated characters. Fortunately there's a utility function, SkeletonUtils.clone we can use to do this. So, first we need to include the utils.\n\nThen we can clone the models we just loaded\n\nAbove, for each model, we clone the gltf.scene we loaded and we parent that to a new Object3D. We need to parent it to another object because when we play animations the animation will apply animated positions to the nodes in the loaded scene which means we won't have control over those positions.", "code_blocks": [{"language": "javascript", "code": "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';", "filename": ""}, {"language": "javascript", "code": "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) => {\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}", "filename": ""}], "chunk_position": 54, "heading_path": "Displaying Animated Models with SkeletonUtils.clone > Displaying Animated Models with SkeletonUtils.clone", "breadcrumbs": "Making a Game > Displaying Animated Models with SkeletonUtils.clone > Displaying Animated Models with SkeletonUtils.clone"}, {"id": "b8afce7bc09df9a0", "url": "https://threejs.org/manual/en/game.html", "page_title": "Making a Game", "page_type": "guide", "page_summary": "A comprehensive guide on building a game with three.js, covering model loading with LoadingManager, animation playback, Entity Component System architecture, input systems, camera frustum checks, and a Finite State Machine for AI behavior.", "heading": "Playing Animations with AnimationMixer", "content": "Page: Making a Game\nSection: Playing Animations with AnimationMixer\n\nTo play the animations each model we clone needs an AnimationMixer. An AnimationMixer contains 1 or more AnimationActions. An AnimationAction references an AnimationClip. AnimationActions have all kinds of settings for playing then chaining to another action or cross fading between actions. Let's just get the first AnimationClip and create an action for it. The default is for an action to play its clip in a loop forever.\n\nWe called play to start the action and stored off all the AnimationMixers in an array called mixers. Finally we need to update each AnimationMixer in our render loop by computing the time since the last frame and passing that to AnimationMixer.update.\n\nAnd with that we should get each model loaded and playing its first animation.", "code_blocks": [{"language": "javascript", "code": "+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) => {\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}", "filename": ""}, {"language": "javascript", "code": "+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}", "filename": ""}], "chunk_position": 54, "heading_path": "Playing Animations with AnimationMixer > Playing Animations with AnimationMixer", "breadcrumbs": "Making a Game > Playing Animations with AnimationMixer > Playing Animations with AnimationMixer"}, {"id": "6d4118364fe033b1", "url": "https://threejs.org/manual/en/game.html", "page_title": "Making a Game", "page_type": "guide", "page_summary": "A comprehensive guide on building a game with three.js, covering model loading with LoadingManager, animation playback, Entity Component System architecture, input systems, camera frustum checks, and a Finite State Machine for AI behavior.", "heading": "Cycling Through Animations", "content": "Page: Making a Game\nSection: Cycling Through Animations\n\nLet's make it so we can check all of the animations. We'll add all of the clips as actions and then enable just one at a time.\n\nThe code above makes an array of AnimationActions, one for each AnimationClip. It makes an array of objects, mixerInfos, with references to the AnimationMixer and all the AnimationActions for each model. It then calls playNextAction which sets enabled on all but one action for that mixer.\n\nWe need to update the render loop for the new array\n\nLet's make it so pressing a key 1 to 8 will play the next animation for each model\n\nNow you should be able to click on the example and then press keys 1 through 8 to cycle each of the models through their available animations.", "code_blocks": [{"language": "javascript", "code": "-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) => {\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) => {\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) => {\n+    const enabled = ndx === nextActionNdx;\n+    action.enabled = enabled;\n+    if (enabled) {\n+      action.play();\n+    }\n+  });\n+}", "filename": ""}, {"language": "javascript", "code": "-for (const mixer of mixers) {\n+for (const {mixer} of mixerInfos) {\n  mixer.update(deltaTime);\n}", "filename": ""}, {"language": "javascript", "code": "window.addEventListener('keydown', (e) => {\n  const mixerInfo = mixerInfos[e.keyCode - 49];\n  if (!mixerInfo) {\n    return;\n  }\n  playNextAction(mixerInfo);\n});", "filename": ""}], "chunk_position": 54, "heading_path": "Cycling Through Animations > Cycling Through Animations", "breadcrumbs": "Making a Game > Cycling Through Animations > Cycling Through Animations"}, {"id": "ba2cb659f4078fd3", "url": "https://threejs.org/manual/en/game.html", "page_title": "Making a Game", "page_type": "guide", "page_summary": "A comprehensive guide on building a game with three.js, covering model loading with LoadingManager, animation playback, Entity Component System architecture, input systems, camera frustum checks, and a Finite State Machine for AI behavior.", "heading": "Entity Component System: GameObject and Component", "content": "Page: Making a Game\nSection: Entity Component System: GameObject and Component\n\nSo that is arguably the sum-total of the three.js portion of this article. We covered loading multiple files, cloning skinned models, and playing animations on them. In a real game you'd have to do a ton more manipulation of AnimationAction objects.\n\nLet's start making a game infrastructure\n\nA common pattern for making a modern game is to use an Entity Component System. In an Entity Component System an object in a game is called an entity that consists of a bunch of components. You build up entities by deciding which components to attach to them. So, let's make an Entity Component System.\n\nWe'll call our entities GameObject. It's effectively just a collection of components and a three.js Object3D.\n\nCalling GameObject.update calls update on all the components.\n\nI included a name only to help in debugging so if I look at a GameObject in the debugger I can see a name to help identify it.\n\nSome things that might seem a little strange:\n\nGameObject.addComponent is used to create components. Whether or not this a good idea or a bad idea I'm not sure. My thinking was it makes no sense for a component to exist outside of a gameobject so I thought it might be good if creating a component automatically added that component to the gameobject and passed the gameobject to the component's constructor. In other words to add a component you do this\n\nIf I didn't do it this way you'd instead do something like this\n\nIs it better that the first way is shorter and more automated or is it worse because it looks out of the ordinary? I don't know.\n\nGameObject.getComponent looks up components by type. That has the implication that you can not have 2 components of the same type on a single game object or at least if you do you can only look up the first one without adding some other API.\n\nIt's common for one component to look up another and when looking them up they have to match by type otherwise you might get the wrong one. We could instead give each component a name and you could look them up by name. That would be more flexible in that you could have more than one component of the same type but it would also be more tedious. Again, I'm not sure which is better.\n\nOn to the components themselves. Here is their base class.\n\nDo components need a base class? JavaScript is not like most strictly typed languages so effectively we could have no base class and just leave it up to each component to do whatever it wants in its constructor knowing that the first argument is always the component's gameobject. If it doesn't care about gameobject it wouldn't store it. I kind of feel like this common base is good though. It means if you have a reference to a component you know you can find its parent gameobject always and from its parent you can easily look up other components as well as look at its transform.", "code_blocks": [{"language": "javascript", "code": "function removeArrayElement(array, element) {\n  const ndx = array.indexOf(element);\n  if (ndx >= 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 => c instanceof ComponentType);\n  }\n  update() {\n    for (const component of this.components) {\n      component.update();\n    }\n  }\n}", "filename": ""}, {"language": "javascript", "code": "const gameObject = new GameObject(scene, 'foo');\ngameObject.addComponent(TypeOfComponent);", "filename": ""}, {"language": "javascript", "code": "const gameObject = new GameObject(scene, 'foo');\nconst component = new TypeOfComponent(gameObject);\ngameObject.addComponent(component);", "filename": ""}, {"language": "javascript", "code": "// Base for all components\nclass Component {\n  constructor(gameObject) {\n    this.gameObject = gameObject;\n  }\n  update() {\n  }\n}", "filename": ""}], "chunk_position": 54, "heading_path": "Entity Component System: GameObject and Component > Entity Component System: GameObject and Component", "breadcrumbs": "Making a Game > Entity Component System: GameObject and Component > Entity Component System: GameObject and Component"}, {"id": "488043402928126e", "url": "https://threejs.org/manual/en/game.html", "page_title": "Making a Game", "page_type": "guide", "page_summary": "A comprehensive guide on building a game with three.js, covering model loading with LoadingManager, animation playback, Entity Component System architecture, input systems, camera frustum checks, and a Finite State Machine for AI behavior.", "heading": "GameObjectManager with SafeArray", "content": "Page: Making a Game\nSection: GameObjectManager with SafeArray\n\nTo manage the gameobjects we probably need some kind of gameobject manager. You might think we could just keep an array of gameobjects but in a real game the components of a gameobject might add and remove other gameobjects at runtime. For example a gun gameobject might add a bullet gameobject every time the gun fires. A monster gameobject might remove itself if it has been killed. We then would have an issue that we might have code like this\n\nThe loop above would fail or do un-expected things if gameobjects are added or removed from globalArrayOfGameObjects in the middle of the loop in some component's update function.\n\nTo try to prevent that problem we need something a little safer. Here's one attempt.\n\nThe class above lets you add or remove elements from the SafeArray but won't mess with the array itself while it's being iterated over. Instead new elements get added to addQueue and removed elements to the removeQueue and then added or removed outside of the loop.\n\nUsing that here is our class to manage gameobjects.", "code_blocks": [{"language": "javascript", "code": "for (const gameObject of globalArrayOfGameObjects) {\n  gameObject.update();\n}", "filename": ""}, {"language": "javascript", "code": "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 > 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 => !this.removeQueue.has(element));\n      this.removeQueue.clear();\n    }\n  }\n}", "filename": ""}, {"language": "javascript", "code": "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 => gameObject.update());\n  }\n}", "filename": ""}], "chunk_position": 54, "heading_path": "GameObjectManager with SafeArray > GameObjectManager with SafeArray", "breadcrumbs": "Making a Game > GameObjectManager with SafeArray > GameObjectManager with SafeArray"}, {"id": "5f7c65d31b1d8e8f", "url": "https://threejs.org/manual/en/game.html", "page_title": "Making a Game", "page_type": "guide", "page_summary": "A comprehensive guide on building a game with three.js, covering model loading with LoadingManager, animation playback, Entity Component System architecture, input systems, camera frustum checks, and a Finite State Machine for AI behavior.", "heading": "SkinInstance Component", "content": "Page: Making a Game\nSection: SkinInstance Component\n\nWith all that now let's make our first component. This component will just manage a skinned three.js object like the ones we just created. To keep it simple it will just have one method, setAnimation that takes the name of the animation to play and plays it.\n\nYou can see it's basically the code we had before that clones the scene we loaded, then sets up an AnimationMixer. setAnimation adds an AnimationAction for a particular AnimationClip if one does not already exist and disables all existing actions.\n\nThe code references globals.deltaTime. Let's make a globals object\n\nAnd update it in the render loop\n\nThe check above for making sure deltaTime is not more than 1/20th of a second is because otherwise we'd get a huge value for deltaTime if we hide the tab. We might hide it for seconds or minutes and then when our tab was brought to the front deltaTime would be huge and might teleport characters across our game world if we had code like\n\nBy limiting the maximum deltaTime that issue is prevented.", "code_blocks": [{"language": "javascript", "code": "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}", "filename": ""}, {"language": "javascript", "code": "const globals = {\n  time: 0,\n  deltaTime: 0,\n};", "filename": ""}, {"language": "javascript", "code": "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;", "filename": ""}, {"language": "javascript", "code": "position += velocity * deltaTime;", "filename": ""}], "chunk_position": 54, "heading_path": "SkinInstance Component > SkinInstance Component", "breadcrumbs": "Making a Game > SkinInstance Component > SkinInstance Component"}, {"id": "970fc4f50ef7d34d", "url": "https://threejs.org/manual/en/game.html", "page_title": "Making a Game", "page_type": "guide", "page_summary": "A comprehensive guide on building a game with three.js, covering model loading with LoadingManager, animation playback, Entity Component System architecture, input systems, camera frustum checks, and a Finite State Machine for AI behavior.", "heading": "Player Component and Animation Names", "content": "Page: Making a Game\nSection: Player Component and Animation Names\n\nNow let's make a component for the player.\n\nThe player calls setAnimation with 'Run'. To know which animations are available I modified our previous example to print out the names of the animations\n\nAnd running it got this list in the JavaScript console.\n\nFortunately the names of the animations for all the animals match which will come in handy later. For now we only care the that the player has an animation called Run.\n\nLet's use these components. Here's the updated init function. All it does is create a GameObject and add a Player component to it.\n\nAnd we need to call gameObjectManager.update in our render loop\n\nand if we run that we get a single player.\n\nThat was a lot of code just for an entity component system but it's infrastructure that most games need.", "code_blocks": [{"language": "javascript", "code": "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}", "filename": ""}, {"language": "javascript", "code": "function prepModelsAndAnimations() {\n  Object.values(models).forEach(model => {\n+    console.log('------->:', model.url);\n    const animsByName = {};\n    model.gltf.animations.forEach((clip) => {\n      animsByName[clip.name] = clip;\n+      console.log('  ', clip.name);\n    });\n    model.animations = animsByName;\n  });\n}", "filename": ""}, {"language": "javascript", "code": "------->:  resources/models/animals/Pig.gltf\n    Idle\n    Death\n    WalkSlow\n    Jump\n    Walk\n ------->:  resources/models/animals/Cow.gltf\n    Walk\n    Jump\n    WalkSlow\n    Death\n    Idle\n ------->:  resources/models/animals/Llama.gltf\n    Jump\n    Idle\n    Walk\n    Death\n    WalkSlow\n ------->:  resources/models/animals/Pug.gltf\n    Jump\n    Walk\n    Idle\n    WalkSlow\n    Death\n ------->:  resources/models/animals/Sheep.gltf\n    WalkSlow\n    Death\n    Jump\n    Walk\n    Idle\n ------->:  resources/models/animals/Zebra.gltf\n    Jump\n    Walk\n    Death\n    WalkSlow\n    Idle\n ------->:  resources/models/animals/Horse.gltf\n    Jump\n    WalkSlow\n    Death\n    Walk\n    Idle\n ------->:  resources/models/knight/KnightCharacter.gltf\n    Run_swordRight\n    Run\n    Idle_swordLeft\n    Roll_sword\n    Idle\n    Run_swordAttack", "filename": ""}, {"language": "javascript", "code": "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}", "filename": ""}, {"language": "javascript", "code": "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}", "filename": ""}], "chunk_position": 54, "heading_path": "Player Component and Animation Names > Player Component and Animation Names", "breadcrumbs": "Making a Game > Player Component and Animation Names > Player Component and Animation Names"}, {"id": "c930d44c017eebc3", "url": "https://threejs.org/manual/en/game.html", "page_title": "Making a Game", "page_type": "guide", "page_summary": "A comprehensive guide on building a game with three.js, covering model loading with LoadingManager, animation playback, Entity Component System architecture, input systems, camera frustum checks, and a Finite State Machine for AI behavior.", "heading": "Input System", "content": "Page: Making a Game\nSection: Input System\n\nLet's add an input system. Rather than read keys directly we'll make a class that other parts of the code can check left or right. That way we can assign multiple ways to input left or right etc.. We'll start with just keys\n\nThe code above tracks whether keys are up or down and you can check if a key is currently pressed by checking for example inputManager.keys.left.down. It also has a justPressed property for each key so that you can check the user just pressed the key. For example a jump key you don't want to know if the button is being held down, you want to know did the user press it now.\n\nLet's create an instance of InputManager\n\nand update it in our render loop\n\nIt needs to be called after gameObjectManager.update otherwise justPressed would never be true inside a component's update function.\n\nLet's use it in the Player component\n\nThe code above uses Object3D.transformOnAxis to move the player forward. Object3D.transformOnAxis works in local space so it only works if the object in question is at the root of the scene, not if it's parented to something else.\n\nWe also added a global moveSpeed and based a turnSpeed on the move speed. The turn speed is based on the move speed to try to make sure a character can turn sharply enough to meet its target. If turnSpeed so too small a character will turn around and around circling its target but never hitting it. I didn't bother to do the math to calculate the required turn speed for a given move speed. I just guessed.", "code_blocks": [{"language": "javascript", "code": "// 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) => {\n      const keyState = this.keys[keyName];\n      keyState.justPressed = pressed && !keyState.down;\n      keyState.down = pressed;\n    };\n\n    const addKey = (keyCode, name) => {\n      this.keys[name] = { down: false, justPressed: false };\n      keyMap.set(keyCode, name);\n    };\n\n    const setKeyFromKeyCode = (keyCode, pressed) => {\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) => {\n      setKeyFromKeyCode(e.keyCode, true);\n    });\n    window.addEventListener('keyup', (e) => {\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}", "filename": ""}, {"language": "javascript", "code": "const globals = {\n  time: 0,\n  deltaTime: 0,\n};\nconst gameObjectManager = new GameObjectManager();\n+const inputManager = new InputManager();", "filename": ""}, {"language": "javascript", "code": "function render(now) {\n\n  ...\n\n  gameObjectManager.update();\n+  inputManager.update();\n\n  ...\n}", "filename": ""}, {"language": "javascript", "code": "+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}", "filename": ""}], "chunk_position": 54, "heading_path": "Input System > Input System", "breadcrumbs": "Making a Game > Input System > Input System"}, {"id": "e98868cb203db557", "url": "https://threejs.org/manual/en/game.html", "page_title": "Making a Game", "page_type": "guide", "page_summary": "A comprehensive guide on building a game with three.js, covering model loading with LoadingManager, animation playback, Entity Component System architecture, input systems, camera frustum checks, and a Finite State Machine for AI behavior.", "heading": "Camera Frustum Check (Offscreen Detection)", "content": "Page: Making a Game\nSection: Camera Frustum Check (Offscreen Detection)\n\nThe code so far would work but if the player runs off the screen there's no way to find out where they are. Let's make it so if they are offscreen for more than a certain time they get teleported back to the origin. We can do that by using the three.js Frustum class to check if a point is inside the camera's view frustum.\n\nWe need to build a frustum from the camera. We could do this in the Player component but other objects might want to use this too so let's add another gameobject with a component to manage a frustum.\n\nThen let's setup another gameobject at init time.\n\nand now we can use it in the Player component.", "code_blocks": [{"language": "javascript", "code": "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}", "filename": ""}, {"language": "javascript", "code": "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}", "filename": ""}, {"language": "javascript", "code": "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 >= this.maxTimeOffScreen) {\n+        transform.position.set(0, 0, 0);\n+      }\n+    }\n  }\n}", "filename": ""}], "chunk_position": 54, "heading_path": "Camera Frustum Check (Offscreen Detection) > Camera Frustum Check (Offscreen Detection)", "breadcrumbs": "Making a Game > Camera Frustum Check (Offscreen Detection) > Camera Frustum Check (Offscreen Detection)"}, {"id": "89ba4d5eaa8fca73", "url": "https://threejs.org/manual/en/game.html", "page_title": "Making a Game", "page_type": "guide", "page_summary": "A comprehensive guide on building a game with three.js, covering model loading with LoadingManager, animation playback, Entity Component System architecture, input systems, camera frustum checks, and a Finite State Machine for AI behavior.", "heading": "Touchscreen Support", "content": "Page: Making a Game\nSection: Touchscreen Support\n\nOne more thing before we try it out, let's add touchscreen support for mobile. First let's add some HTML to touch\n\nand some CSS to style it\n\nThe idea here is there is one div, #ui, that covers the entire page. Inside will be 2 divs, #left and #right both of which are almost half the page wide and the entire screen tall. In between there is a 40px separator. If the user slides their finger over the left or right side then we need up update keys.left and keys.right in the InputManager. This makes the entire screen sensitive to being touched which seemed better than just small arrows.\n\nAnd now we should be able to control the character with the left and right cursor keys or with our fingers on a touchscreen.\n\nIdeally we'd do something else if the player went off the screen like move the camera or maybe offscreen = death but this article is already going to be too long so for now teleporting to the middle was the simplest thing.", "code_blocks": [{"language": "html", "code": "<body>\n  <canvas id=\"c\"></canvas>\n+  <div id=\"ui\">\n+    <div id=\"left\"><img src=\"../resources/images/left.svg\"></div>\n+    <div style=\"flex: 0 0 40px;\"></div>\n+    <div id=\"right\"><img src=\"../resources/images/right.svg\"></div>\n+  </div>\n  <div id=\"loading\">\n    <div>\n      <div>...loading...</div>\n      <div class=\"progress\"><div id=\"progressbar\"></div></div>\n    </div>\n  </div>\n</body>", "filename": ""}, {"language": "css", "code": "#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>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}", "filename": ""}, {"language": "javascript", "code": "class InputManager {\n  constructor() {\n    this.keys = {};\n    const keyMap = new Map();\n\n    const setKey = (keyName, pressed) => {\n      const keyState = this.keys[keyName];\n      keyState.justPressed = pressed && !keyState.down;\n      keyState.down = pressed;\n    };\n\n    const addKey = (keyCode, name) => {\n      this.keys[name] = { down: false, justPressed: false };\n      keyMap.set(keyCode, name);\n    };\n\n    const setKeyFromKeyCode = (keyCode, pressed) => {\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) => {\n      setKeyFromKeyCode(e.keyCode, true);\n    });\n    window.addEventListener('keyup', (e) => {\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 = () => {\n+      for (const {key} of sides) {\n+          setKey(key, false);\n+      }\n+    };\n+\n+    const handleMouseMove = (e) => {\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 >= rect.left && x < rect.right &&\n+                       y >= rect.top && y < 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) => {\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}", "filename": ""}], "chunk_position": 54, "heading_path": "Touchscreen Support > Touchscreen Support", "breadcrumbs": "Making a Game > Touchscreen Support > Touchscreen Support"}, {"id": "5bc8396958400d23", "url": "https://threejs.org/manual/en/game.html", "page_title": "Making a Game", "page_type": "guide", "page_summary": "A comprehensive guide on building a game with three.js, covering model loading with LoadingManager, animation playback, Entity Component System architecture, input systems, camera frustum checks, and a Finite State Machine for AI behavior.", "heading": "Animal Component Setup", "content": "Page: Making a Game\nSection: Animal Component Setup\n\nLets add some animals. We can start it off similar to the Player by making an Animal component.\n\nThe code above sets the AnimationMixer.timeScale to set the playback speed of the animations relative to the move speed. This way if we adjust the move speed the animation will speed up or slow down as well.\n\nTo start we could setup one of each type of animal\n\nAnd that would get us animals standing on the screen but we want them to do something.", "code_blocks": [{"language": "javascript", "code": "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}", "filename": ""}, {"language": "javascript", "code": "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) => {\n+    const gameObject = gameObjectManager.createGameObject(scene, name);\n+    gameObject.addComponent(Animal, models[name]);\n+    gameObject.transform.position.x = (ndx + 1) * 5;\n+  });\n}", "filename": ""}], "chunk_position": 54, "heading_path": "Animal Component Setup > Animal Component Setup", "breadcrumbs": "Making a Game > Animal Component Setup > Animal Component Setup"}, {"id": "ffa1906c58db3aad", "url": "https://threejs.org/manual/en/game.html", "page_title": "Making a Game", "page_type": "guide", "page_summary": "A comprehensive guide on building a game with three.js, covering model loading with LoadingManager, animation playback, Entity Component System architecture, input systems, camera frustum checks, and a Finite State Machine for AI behavior.", "heading": "Animal AI States (Conga Line)", "content": "Page: Making a Game\nSection: Animal AI States (Conga Line)\n\nLet's make them follow the player in a conga line but only if the player gets near enough. To do this we need several states.\n\n- Idle: Animal is waiting for player to get close\n- Wait for End of Line: Animal was tagged by player but now needs to wait for the animal at the end of the line to come by so they can join the end of the line.\n- Go to Last: Animal needs to walk to where the animal they are following was, at the same time recording a history of where the animal they are following is currently.\n- Follow: Animal needs to keep recording a history of where the animal they are following is while moving to where the animal they are following was before.\n\nThere are many ways to handle different states like this. A common one is to use a Finite State Machine and to build some class to help us manage the state.\n\nSo, let's do that.\n\nHere's a simple class. We pass it an object with a bunch of states. Each state as 3 optional functions, enter, update, and exit. To switch states we call FiniteStateMachine.transition and pass it the name of the new state. If the current state has an exit function it's called. Then if the new state has an enter function it's called. Finally each frame FiniteStateMachine.update calls the update function of the current state.\n\nLet's use it to manage the states of the animals.\n\nThat was big chunk of code but it does what was described above. Hopefully of you walk through each state it will be clear.\n\nA few things we need to add. We need the player to add itself to the globals so the animals can find it and we need to start the conga line with the player's GameObject.\n\nWe also need to compute a size for each model\n\nAnd we need the player to record their size\n\nThinking about it now it would probably have been smarter for the animals to just target the head of the conga line instead of the player specifically. Maybe I'll come back and change that later.\n\nWhen I first started this I used just one radius for all animals but of course that was no good as the pug is much smaller than the horse. So I added the difference sizes but I wanted to be able to visualize things. To do that I made a StateDisplayHelper component.\n\nI uses a PolarGridHelper to draw a circle around each character and it uses html elements to let each character show some status using the techniques covered in the article on aligning html elements to 3D.\n\nFirst we need to add some HTML to host these elements\n\nAnd add some CSS for them", "code_blocks": [{"language": "javascript", "code": "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 && 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}", "filename": ""}, {"language": "javascript", "code": "// 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 < minDist;\n}\n\n// keeps v between -min and +min\nfunction minMagnitude(v, min) {\n  return Math.abs(v) > 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: () => {\n+          skinInstance.setAnimation('Idle');\n+        },\n+        update: () => {\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: () => {\n+          skinInstance.setAnimation('Jump');\n+        },\n+        update: () => {\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: () => {\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: () => {\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 <= maxVelocity) {\n+            this.fsm.transition('follow');\n+          }\n+        },\n+      },\n+      follow: {\n+        update: () => {\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}", "filename": ""}, {"language": "javascript", "code": "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}", "filename": ""}, {"language": "javascript", "code": "function prepModelsAndAnimations() {\n+  const box = new THREE.Box3();\n+  const size = new THREE.Vector3();\n  Object.values(models).forEach(model => {\n+    box.setFromObject(model.gltf.scene);\n+    box.getSize(size);\n+    model.size = size.length();\n    const animsByName = {};\n    model.gltf.animations.forEach((clip) => {\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}", "filename": ""}, {"language": "javascript", "code": "class Player extends Component {\n  constructor(gameObject) {\n    super(gameObject);\n    const model = models.knight;\n+    globals.playerRadius = model.size / 2;", "filename": ""}, {"language": "html", "code": "<body>\n  <canvas id=\"c\"></canvas>\n  <div id=\"ui\">\n    <div id=\"left\"><img src=\"../resources/images/left.svg\"></div>\n    <div style=\"flex: 0 0 40px;\"></div>\n    <div id=\"right\"><img src=\"../resources/images/right.svg\"></div>\n  </div>\n  <div id=\"loading\">\n    <div>\n      <div>...loading...</div>\n      <div class=\"progress\"><div id=\"progressbar\"></div></div>\n    </div>\n  </div>\n+  <div id=\"labels\"></div>\n</body>", "filename": ""}], "chunk_position": 54, "heading_path": "Animal AI States (Conga Line) > Animal AI States (Conga Line)", "breadcrumbs": "Making a Game > Animal AI States (Conga Line) > Animal AI States (Conga Line)"}, {"id": "42aa81e25200a92e", "url": "https://threejs.org/manual/en/webgpurenderer.html", "page_title": "WebGPURenderer", "page_type": "guide", "page_summary": "An overview of three.js's next-generation WebGPURenderer, which uses WebGPU by default with a WebGL 2 fallback, including usage instructions, code examples, and migration considerations from WebGLRenderer.", "heading": "WebGPURenderer", "content": "Page: WebGPURenderer\nSection: WebGPURenderer\n\nThe new `WebGPURenderer` is the next-generation renderer for three.js. This article provides a short overview about the new renderer and basic guidelines about the usage.", "code_blocks": [], "chunk_position": 55, "heading_path": "WebGPURenderer > WebGPURenderer", "breadcrumbs": "WebGPURenderer > WebGPURenderer > WebGPURenderer"}, {"id": "61a08543d7c556e2", "url": "https://threejs.org/manual/en/webgpurenderer.html", "page_title": "WebGPURenderer", "page_type": "guide", "page_summary": "An overview of three.js's next-generation WebGPURenderer, which uses WebGPU by default with a WebGL 2 fallback, including usage instructions, code examples, and migration considerations from WebGLRenderer.", "heading": "Overview", "content": "Page: WebGPURenderer\nSection: Overview\n\n`WebGPURenderer` is designed to be the modern alternative to the long-standing `WebGLRenderer`. 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 universal renderer. If a device/browser doesn't support WebGPU, the renderer can automatically fall back to using a WebGL 2 backend.\n\nProviding a WebGL 2 backend as a fallback is a crucial design decision since it allows applications to benefit from WebGPU but without sacrificing the support for devices which only support WebGL 2.\n\nApart from the fact that `WebGPURenderer` enables access to WebGPU, it offers an exciting feature set:\n\n- `WebGPURenderer` comes with a new node-based material system which allows to develop custom materials with greater flexibility and more robustness.\n- The renderer supports TSL, the three.js shading language. With TSL, developers can write shader code with JavaScript in a platform-independent manner. Shader code written in TSL can be transpiled to WGSL or GLSL depending on the available backend.\n- `WebGPURenderer` comes with a new post-processing stack with built-in Multiple Render Targets (MRT) support and automatic pass combination thanks to the node material.\n\nLet's find out how to integrate `WebGPURenderer` in three.js applications.", "code_blocks": [], "chunk_position": 55, "heading_path": "Overview > Overview", "breadcrumbs": "WebGPURenderer > Overview > Overview"}, {"id": "0a20b962d15710dd", "url": "https://threejs.org/manual/en/webgpurenderer.html", "page_title": "WebGPURenderer", "page_type": "guide", "page_summary": "An overview of three.js's next-generation WebGPURenderer, which uses WebGPU by default with a WebGL 2 fallback, including usage instructions, code examples, and migration considerations from WebGLRenderer.", "heading": "Usage", "content": "Page: WebGPURenderer\nSection: Usage\n\n`WebGPURenderer` has different build files so the way you import three.js changes:\n\nIf you are using an import map, it's recommended to change it to the following (the paths differ depending on your setup):\n\nYou can create an instance of the renderer just like with `WebGLRenderer`:\n\nIt's important to understand that WebGPU is initialized in an asynchronous fashion. Hence, it is recommended to use `setAnimationLoop()` to define the animation loop of your app since this approach will automatically ensure the renderer is initialized when rendering the first frame. If you prefer to manage your animation loop via `window.requestAnimationFrame()` or if you have to use the renderer in your init routine, you need an additional line in the above code section.\n\nMost common methods known from `WebGLRenderer` like `clear()`, `setRenderTarget()` or `dispose()` are also present in `WebGPURenderer`. Please have a look at the API documentation for a full overview of the renderer's public interface.\n\nLike mentioned in the initial part of the guide, `WebGPURenderer` uses a WebGPU backend by default and a WebGL 2 backend as a fallback. 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 make use of the `forceWebGL` parameter.", "code_blocks": [{"language": "javascript", "code": "-  import * as THREE from 'three';\n+  import * as THREE from 'three/webgpu';", "filename": ""}, {"language": "html", "code": "<script type=\"importmap\">\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</script>", "filename": ""}, {"language": "javascript", "code": "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 );", "filename": ""}, {"language": "javascript", "code": "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();", "filename": ""}, {"language": "javascript", "code": "-  const renderer = new THREE.WebGPURenderer( { antialias: true } );\n+  const renderer = new THREE.WebGPURenderer( { antialias: true, forceWebGL: true } );", "filename": ""}], "chunk_position": 55, "heading_path": "Usage > Usage", "breadcrumbs": "WebGPURenderer > Usage > Usage"}, {"id": "ab46d18afad310c7", "url": "https://threejs.org/manual/en/webgpurenderer.html", "page_title": "WebGPURenderer", "page_type": "guide", "page_summary": "An overview of three.js's next-generation WebGPURenderer, which uses WebGPU by default with a WebGL 2 fallback, including usage instructions, code examples, and migration considerations from WebGLRenderer.", "heading": "Migration", "content": "Page: WebGPURenderer\nSection: Migration\n\nIf you want to give `WebGPURenderer` a try, you have to be aware of the following.\n\n- Custom materials based on `ShaderMaterial`, `RawShaderMaterial` and modifications of built-in materials via `onBeforeCompile()` are not supported in `WebGPURenderer`. This part of your application must be ported to node materials and TSL.\n- `EffectComposer` with its effect passes are not supported because `WebGPURenderer` comes with a new, more modern post-processing stack. Similar to materials, post-processing effects are now written in TSL and the effect chain is expressed as a node composition. All common effects have already been ported to `WebGPURenderer` and exist in a more performant version 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 official examples to get an overview of the current supported effects.\n- The renderer itself is still in an experimental state although its maturity level has been greatly improved in the last years. Still, depending on your application and scene setup, you will encounter missing features or a better performance with `WebGLRenderer`. Feel free to file an issue at GitHub so we are aware of open tasks. We are improving `WebGPURenderer` with each release so it's recommended to upgrade to the latest version whenever possible.", "code_blocks": [], "chunk_position": 55, "heading_path": "Migration > Migration", "breadcrumbs": "WebGPURenderer > Migration > Migration"}, {"id": "16c6bc7b89614b11", "url": "https://threejs.org/manual/en/webgpurenderer.html", "page_title": "WebGPURenderer", "page_type": "guide", "page_summary": "An overview of three.js's next-generation WebGPURenderer, which uses WebGPU by default with a WebGL 2 fallback, including usage instructions, code examples, and migration considerations from WebGLRenderer.", "heading": "State of WebGLRenderer", "content": "Page: WebGPURenderer\nSection: State of WebGLRenderer\n\nAlthough in the meanwhile a lot of work happens in context of `WebGPURenderer`, the node material and TSL, `WebGLRenderer` is still maintained and the recommended choice for pure WebGL 2 applications. However, keep in mind that there are no plans to add larger new features to the renderer since the project's focus is now on `WebGPURenderer` which you can easily see at the latest release notes. That said, we are currently investigating the possibility to add limited node material support to `WebGLRenderer` in order to make the transition to `WebGPURenderer` easier for certain projects.", "code_blocks": [], "chunk_position": 55, "heading_path": "State of WebGLRenderer > State of WebGLRenderer", "breadcrumbs": "WebGPURenderer > State of WebGLRenderer > State of WebGLRenderer"}, {"id": "7b00a3d5556cc31b", "url": "https://threejs.org/manual/en/webgpu-postprocessing.html", "page_title": "Post-Processing with WebGPURenderer", "page_type": "guide", "page_summary": "This guide explains how to use the new post-processing stack built into WebGPURenderer, including MRT support, effect composition via TSL nodes, tone mapping control, and advanced MRT packing techniques.", "heading": "Overview", "content": "Page: Post-Processing with WebGPURenderer\nSection: Overview\n\nThe previous post-processing for `WebGLRenderer` had many conceptual issues. Making use of Multiple Render Targets (MRT) was cumbersome due to the limited support in the renderer and there was no automatic pass/effect combination to improve the overall performance.\n\nThe new post-processing stack for `WebGPURenderer` was designed to support these use cases right from the beginning.\n\n- `WebGPURenderer` comes with full, built-in MRT support.\n- The system combines effects if possible which reduces the overall number of render passes.\n- The effect chain is expressed as a node composition which allows a more flexible effect setup.\n\nLet's find out how to integrate post-processing in three.js applications.", "code_blocks": [], "chunk_position": 56, "heading_path": "Overview > Overview", "breadcrumbs": "Post-Processing with WebGPURenderer > Overview > Overview"}, {"id": "c38def7e8612e223", "url": "https://threejs.org/manual/en/webgpu-postprocessing.html", "page_title": "Post-Processing with WebGPURenderer", "page_type": "guide", "page_summary": "This guide explains how to use the new post-processing stack built into WebGPURenderer, including MRT support, effect composition via TSL nodes, tone mapping control, and advanced MRT packing techniques.", "heading": "Basics", "content": "Page: Post-Processing with WebGPURenderer\nSection: Basics\n\nFirst, please read the instructions in the guide about [WebGPURenderer](webgpurenderer) to correctly configure your imports. After that, you can create an instance of the render pipleine module like so:\n\nThe instance of `RenderPipeline` replaces the previous instance of `EffectComposer`. To make sure you actually use the output of the module, you have to update your animation loop like so:\n\nMany post-processing setups start with a so called \"scene pass\" or \"beauty pass\" that represents the image of you rendered scene. This image should be subsequently enhanced by different effects like Bloom, Depth-of-Field or SSR. Start by importing the `pass()` TSL function from the TSL namespace and use it to create the pass.\n\nThe basic idea of the node system is to represent materials or post-processing effects as node compositions. To configure a basic Dotscreen and RGB shift effect, you create effect nodes with TSL functions and compose them together.\n\nWhen you are done, you can simply assign the final node to the `RenderPipeline` instance.", "code_blocks": [{"language": "javascript", "code": "const renderPipeline = new THREE.RenderPipeline( renderer );", "filename": ""}, {"language": "diff", "code": "-  renderer.render( scene, camera );\n+  renderPipeline.render();", "filename": ""}, {"language": "javascript", "code": "import { pass } from 'three/tsl';\n\n// in your init routine\n\nconst scenePass = pass( scene, camera );", "filename": ""}, {"language": "diff", "code": "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 );", "filename": ""}, {"language": "javascript", "code": "renderPipeline.outputNode = rgbShiftPass;", "filename": ""}], "chunk_position": 56, "heading_path": "Basics > Basics", "breadcrumbs": "Post-Processing with WebGPURenderer > Basics > Basics"}, {"id": "7ce2e54c37fabf9d", "url": "https://threejs.org/manual/en/webgpu-postprocessing.html", "page_title": "Post-Processing with WebGPURenderer", "page_type": "guide", "page_summary": "This guide explains how to use the new post-processing stack built into WebGPURenderer, including MRT support, effect composition via TSL nodes, tone mapping control, and advanced MRT packing techniques.", "heading": "Tone Mapping and Color Spaces", "content": "Page: Post-Processing with WebGPURenderer\nSection: Tone Mapping and Color Spaces\n\nWhen using post-processing, tone mapping and color space conversion are automatically applied at the end of your effect chain. Sometimes you want full control over how and when these steps are executed though. For example if you want to apply FXAA with `FXAANode` or color grading with `Lut3DNode`, you can disable automatic tone mapping and color space conversion and apply it via `renderOutput()` by yourself.\n\nIt is not mandatory to use `renderOutput()`, you can also implement a custom tone mapping and color space conversion based on your requirements.", "code_blocks": [{"language": "javascript", "code": "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;", "filename": ""}], "chunk_position": 56, "heading_path": "Tone Mapping and Color Spaces > Tone Mapping and Color Spaces", "breadcrumbs": "Post-Processing with WebGPURenderer > Tone Mapping and Color Spaces > Tone Mapping and Color Spaces"}, {"id": "b9ae998fe7fe32f8", "url": "https://threejs.org/manual/en/webgpu-postprocessing.html", "page_title": "Post-Processing with WebGPURenderer", "page_type": "guide", "page_summary": "This guide explains how to use the new post-processing stack built into WebGPURenderer, including MRT support, effect composition via TSL nodes, tone mapping control, and advanced MRT packing techniques.", "heading": "MRT", "content": "Page: Post-Processing with WebGPURenderer\nSection: MRT\n\nThe new post-processing stack has built-in Multiple Render Targets (MRT) support which is crucial for more advanced setups. MRT allows you to produce multiple outputs in a single render pass. So for example when rendering your scene with TRAA, you need below setup to prepare the inputs for the anti-aliasing.\n\nThe configuration object you assign to the `mrt()` TSL function describes the different outputs of the pass. In this case, we save the default output (the scene's beauty) and scene's velocity since we want to setup a TRAA. If you also require 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 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\nThe MRT configuration varies depending on your setup. There are many different TSL objects like `output`, `velocity`, `normalView` or `emissive` than you can use to save per-fragment data in MRT attachments. To improve performance and avoid hitting memory restrictions, it's important to pack and optimize your data in complex MRT setups. By default all attachments are RGBA16 (Half-Float) in precision which is not necessary for all types of data. As an example, below code queries the `diffuseColor` attachment and sets its format to RGBA8 which cuts down the memory and bandwidth by half.\n\nBelow setup for Scree-Space Reflections (SSR) converts the default FP16 normals into RGBA8 colors and packs metalness/roughness into a single attachment. The usage of the `sample()` TSL functions allows to implement custom unpacking. In this instance, it converts the color back to a (normalized) direction vector.\n\nWe want to further improve the packing/unpacking features in the future to offer more ways to pack/unpack MRT data. In the meanwhile, please have a look at the [official examples](https://threejs.org/examples/?q=webgpu%20postprocessing) to get an overview about the existing effects and setups.", "code_blocks": [{"language": "javascript", "code": "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} ) );", "filename": ""}, {"language": "javascript", "code": "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;", "filename": ""}, {"language": "javascript", "code": "const diffuseTexture = scenePass.getTexture( 'diffuseColor' );\ndiffuseTexture.type = THREE.UnsignedByteType;", "filename": ""}, {"language": "javascript", "code": "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 ) => {\n\n  return unpackRGBToNormal( scenePassNormal.sample( uv ) );\n\n} );", "filename": ""}], "chunk_position": 56, "heading_path": "MRT > MRT", "breadcrumbs": "Post-Processing with WebGPURenderer > MRT > MRT"}, {"id": "f1232b2a16b1b8e8", "url": "https://threejs.org/manual/en/webxr-basics.html", "page_title": "VR", "page_type": "guide", "page_summary": "A guide to building VR applications with three.js using WebXR, covering setup, device considerations, and supporting both VR and non-VR modes.", "heading": "Introduction", "content": "Page: VR\nSection: Introduction\n\nMaking a VR app in three.js is pretty simple. You basically just have to tell three.js you want to use WebXR. If you think about it a few things about WebXR should be clear. Which way the camera is pointing is supplied by the VR system itself since the user turns their head to choose a direction to look. Similarly the field of view and aspect will be supplied by the VR system since each system has a different field of view and display aspect.\n\nLet's take an example from the article on making a responsive webpage and make it support VR.", "code_blocks": [], "chunk_position": 57, "heading_path": "Introduction > Introduction", "breadcrumbs": "VR > Introduction > Introduction"}, {"id": "c19e34a14699833c", "url": "https://threejs.org/manual/en/webxr-basics.html", "page_title": "VR", "page_type": "guide", "page_summary": "A guide to building VR applications with three.js using WebXR, covering setup, device considerations, and supporting both VR and non-VR modes.", "heading": "Requirements", "content": "Page: VR\nSection: Requirements\n\nBefore we get started you're going to need a VR capable device like an Android smartphone, Google Daydream, Oculus Go, Oculus Rift, Vive, Samsung Gear VR., an iPhone with a WebXR browser.\n\nNext, if you are running locally you need to run a simple web server like is covered in the article on setting up.\n\nIf the device you are using to view VR is not the same computer you're running on you need to serve your webpage via https or else the browser will not allow using the WebXR API. The server mentioned in the article on setting up called Servez has an option to use https. Check it and start the server.\n\nThe note the URLs. You need the one that is your computer's local ipaddress. It will usually start with `192`, `172` or `10`. Type that full address, including the `https://` part into your VR device's browser. Note: Your computer and your VR device need to be on the same local network or WiFi and you probably need to be on a home network. note: Many cafes are setup to disallow this kind of machine to machine connection.\n\nYou'll be greeted with an error something like the one below. Click \"advanced\" and then click *proceed*.\n\nNow you can run your examples.\n\nIf you're really going to do WebXR development another thing you should learn about is remote debugging so that you can see console warnings, errors, and of course actually debug your code.\n\nIf you just want to see the code work below you can just run the code from this site.", "code_blocks": [], "chunk_position": 57, "heading_path": "Requirements > Requirements", "breadcrumbs": "VR > Requirements > Requirements"}, {"id": "082d354b9fa07531", "url": "https://threejs.org/manual/en/webxr-basics.html", "page_title": "VR", "page_type": "guide", "page_summary": "A guide to building VR applications with three.js using WebXR, covering setup, device considerations, and supporting both VR and non-VR modes.", "heading": "Basic Setup", "content": "Page: VR\nSection: Basic Setup\n\nThe first thing we need to do is include the VR support after including three.js.\n\nThen we need to enable three.js's WebXR support and add its VR button to our page.", "code_blocks": [{"language": "javascript", "code": "import * as THREE from 'three';\nimport {VRButton} from 'three/addons/webxr/VRButton.js';", "filename": ""}, {"language": "javascript", "code": "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));", "filename": ""}], "chunk_position": 57, "heading_path": "Basic Setup > Basic Setup", "breadcrumbs": "VR > Basic Setup > Basic Setup"}, {"id": "a51ad2c99d499a80", "url": "https://threejs.org/manual/en/webxr-basics.html", "page_title": "VR", "page_type": "guide", "page_summary": "A guide to building VR applications with three.js using WebXR, covering setup, device considerations, and supporting both VR and non-VR modes.", "heading": "Render Loop", "content": "Page: VR\nSection: Render Loop\n\nWe need to let three.js run our render loop. Until now we have used a `requestAnimationFrame` loop but to support VR we need to let three.js handle our render loop for us. We can do that by calling `WebGLRenderer.setAnimationLoop` and passing a function to call for the loop.", "code_blocks": [{"language": "javascript", "code": "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) => {\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\nrequestAnimationFrame(render);\nrenderer.setAnimationLoop(render);", "filename": ""}], "chunk_position": 57, "heading_path": "Render Loop > Render Loop", "breadcrumbs": "VR > Render Loop > Render Loop"}, {"id": "60011ce28072d2b2", "url": "https://threejs.org/manual/en/webxr-basics.html", "page_title": "VR", "page_type": "guide", "page_summary": "A guide to building VR applications with three.js using WebXR, covering setup, device considerations, and supporting both VR and non-VR modes.", "heading": "Camera Positioning and Units in VR", "content": "Page: VR\nSection: Camera Positioning and Units in VR\n\nThere is one more detail. We should probably set a camera height that's kind of average for a standing user.\n\nand move the cubes up to be in front of the camera\n\nWe set them to `z = -2` since the camera will now be at `z = 0` and camera defaults to looking down the -z axis.\n\nThis brings up an extremely important point. **Units in VR are in meters**. In other words **One Unit = One Meter**. This means the camera is 1.6 meters above 0. The cube's centers are 2 meters in front of the camera. Each cube is 1x1x1 meter large. This is important because VR needs to adjust things to the user *in the real world*. That means we need the units used in three.js to match the user's own movements.\n\nAnd with that we should get 3 spinning cubes in front of the camera with a button to enter VR.\n\n[click here to open in a separate window](/manual/examples/webxr-basic.html)", "code_blocks": [{"language": "javascript", "code": "const camera = new THREE.PerspectiveCamera(fov, aspect, near, far);\ncamera.position.set(0, 1.6, 0);", "filename": ""}, {"language": "javascript", "code": "const cube = new THREE.Mesh(geometry, material);\nscene.add(cube);\n\ncube.position.x = x;\ncube.position.y = 1.6;\ncube.position.z = -2;", "filename": ""}], "chunk_position": 57, "heading_path": "Camera Positioning and Units in VR > Camera Positioning and Units in VR", "breadcrumbs": "VR > Camera Positioning and Units in VR > Camera Positioning and Units in VR"}, {"id": "24f80e6485629050", "url": "https://threejs.org/manual/en/webxr-basics.html", "page_title": "VR", "page_type": "guide", "page_summary": "A guide to building VR applications with three.js using WebXR, covering setup, device considerations, and supporting both VR and non-VR modes.", "heading": "Adding a Background", "content": "Page: VR\nSection: Adding a Background\n\nI find that VR works better if we have something surrounding the camera like room for reference so let's add a simple grid cubemap like we covered in the article on backgrounds. We'll just use the same grid texture for each side of the cube which will give as a grid room.\n\nThat's better.\n\n[click here to open in a separate window](/manual/examples/webxr-basic-w-background.html)\n\nNote: To actually see VR you will need a WebXR compatible device. I believe most Android Phones can support WebXR using Chrome or Firefox. For iOS you might be able to use this WebXR App though in general WebXR support on iOS is unsupported as of May 2019.", "code_blocks": [{"language": "javascript", "code": "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}", "filename": ""}], "chunk_position": 57, "heading_path": "Adding a Background > Adding a Background", "breadcrumbs": "VR > Adding a Background > Adding a Background"}, {"id": "bfaac11ed3e5b810", "url": "https://threejs.org/manual/en/webxr-basics.html", "page_title": "VR", "page_type": "guide", "page_summary": "A guide to building VR applications with three.js using WebXR, covering setup, device considerations, and supporting both VR and non-VR modes.", "heading": "VR Device Considerations", "content": "Page: VR\nSection: VR Device Considerations\n\nTo use WebXR on Android or iPhone you'll need a *VR Headset* for phones. You can get them for anywhere from $5 for one made of cardboard to $100. Unfortunately I don't know which ones to recommend. I've purchased 6 of them over the years and they are all of varying quality. I've never paid more than about $25.\n\nJust to mention some of the issues\n\n- **Do they fit your phone**\n\nPhones come in a variety of sizes and so the VR headsets need to match. Many headsets claim to match a large variety of sizes. My experience is the more sizes they match the worse they actually are since instead of being designed for a specific size they have to make compromises to match more sizes. Unfortunately multi-size headsets are the most common type.\n\n- **Can they focus for your face**\n\nSome devices have more adjustments than others. Generally there are at most 2 adjustments. How far the lenses are from your eyes and how far apart the lenses are.\n\n- **Are they too reflective**\n\nMany headsets of a cone of plastic from your eye to the phone. If that plastic is shinny or reflective then it will act like a mirror reflecting the screen and be very distracting.\n\nFew if any of the reviews seem to cover this issue.\n\n- **Are the comfortable on your face.**\n\nMost of the devices rest on your nose like a pair of glasses. That can hurt after a few minutes. Some have straps that go around your head. Others have a 3rd strap that goes over your head. These may or may not help keep the device at the right place.\n\nIt turns out for most (all?) devices, you eyes need to be centered with the lenses. If the lenses are slightly above or below your eyes the image gets out of focus. This can be very frustrating as things might start in focus but 45-60 seconds later the device has shifted up or down 1 millimeter and you suddenly realize you've been struggling to focus on a blurry image.\n\n- **Can they support your glasses.**\n\nIf you wear eye glasses then you'll need to read the reviews to see if a particular headset works well with eye glasses.\n\nI really can't make any recommendations unfortunately. Google has some cheap recommendations made from cardboard some of them as low as $5 so maybe start there and if you enjoy it then consider upgrading. $5 is like the price of 1 coffee so seriously, give it try!", "code_blocks": [], "chunk_position": 57, "heading_path": "VR Device Considerations > VR Device Considerations", "breadcrumbs": "VR > VR Device Considerations > VR Device Considerations"}, {"id": "7f8b642d0e815500", "url": "https://threejs.org/manual/en/webxr-basics.html", "page_title": "VR", "page_type": "guide", "page_summary": "A guide to building VR applications with three.js using WebXR, covering setup, device considerations, and supporting both VR and non-VR modes.", "heading": "Types of VR Devices", "content": "Page: VR\nSection: Types of VR Devices\n\nThere are also 3 basic types of devices.\n\n- **3 degrees of freedom (3dof), no input device**\n\nThis is generally the phone style although sometimes you can buy a 3rd party input device. The 3 degrees of freedom mean you can look up/down (1), left/right(2) and you can tilt your head left and right (3).\n\n- **3 degrees of freedom (3dof) with 1 input device (3dof)**\n\nThis is basically Google Daydream and Oculus GO\n\nThese also allow 3 degrees of freedom and include a small controller that acts like a laser pointer inside VR. The laser pointer also only has 3 degrees of freedom. The system can tell which way the input device is pointing but it can not tell where the device is.\n\n- **6 degrees of freedom (6dof) with input devices (6dof)**\n\nThese are *the real deal* haha. 6 degrees of freedom means not only do these device know which way you are looking but they also know where your head actually is. That means if you move from left to right or forward and back or stand up / sit down the devices can register this and everything in VR moves accordingly. It's spookily and amazingly real feeling. With a good demo you'll be blown away or at least I was and still am.\n\nFurther these devices usually include 2 controllers, one for each hand and the system can tell exactly where your hands are and which way they are oriented and so you can manipulate things in VR by just reaching out, touching, pushing, twisting, etc...\n\n6 degree of freedom devices include the Vive and Vive Pro, the Oculus Rift and Quest, and I believe all of the Windows MR devices.\n\nWith all that covered I don't for sure know which devices will work with WebXR. I'm 99% sure that most Android phones will work when running Chrome. You may need to turn on WebXR support in `about:flags`. I also know Google Daydream will also work and similarly you need to enable WebXR support in `about:flags`. Oculus Rift, Vive, and Vive Pro will work via Chrome or Firefox. I'm less sure about Oculus Go and Oculus Quest as both of them use custom OSes but according to the internet they both appear to work.", "code_blocks": [], "chunk_position": 57, "heading_path": "Types of VR Devices > Types of VR Devices", "breadcrumbs": "VR > Types of VR Devices > Types of VR Devices"}, {"id": "487d22b83ecbd76b", "url": "https://threejs.org/manual/en/webxr-basics.html", "page_title": "VR", "page_type": "guide", "page_summary": "A guide to building VR applications with three.js using WebXR, covering setup, device considerations, and supporting both VR and non-VR modes.", "heading": "Supporting both VR and Non-VR", "content": "Page: VR\nSection: Supporting both VR and Non-VR\n\nOkay, after that long detour about VR Devices and WebXR there's some things to cover\n\nAFAICT, at least as of r112, there is no easy way to support both VR and non-VR modes with three.js. Ideally if not in VR mode you'd be able to control the camera using whatever means you want, for example the `OrbitControls`, and you'd get some kind of event when switching into and out of VR mode so that you could turn the controls on/off.\n\nIf three.js adds some support to do both I'll try to update this article. Until then you might need 2 versions of your site OR pass in a flag in the URL, something like\n\nThen we could add some links in to switch modes\n\nand some CSS to position them\n\nin your code you could use that parameter like this\n\nWhether that's good or bad I don't know. I have a feeling the differences between what's needed for VR and what's needed for non-VR are often very different so for all but the most simple things maybe 2 separate pages are better? You'll have to decide.\n\nNote for various reasons this will not work in the live editor on this site so if you want to check it out click here. It should start in non-VR mode and you can use the mouse or fingers to move the camera. Clicking \"Allow VR\" should switch to allow VR mode and you should be able to click \"Enter VR\" if you're on a VR device.", "code_blocks": [{"language": "text", "code": "https://mysite.com/mycooldemo?allowvr=true", "filename": ""}, {"language": "html", "code": "<body>\n  <canvas id=\"c\"></canvas>\n  <div class=\"mode\">\n    <a href=\"?allowvr=true\" id=\"vr\">Allow VR</a>\n    <a href=\"?\" id=\"nonvr\">Use Non-VR Mode</a>\n  </div>\n</body>", "filename": ""}, {"language": "css", "code": "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}", "filename": ""}, {"language": "javascript", "code": "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  }", "filename": ""}], "chunk_position": 57, "heading_path": "Supporting both VR and Non-VR > Supporting both VR and Non-VR", "breadcrumbs": "VR > Supporting both VR and Non-VR > Supporting both VR and Non-VR"}, {"id": "be62cd4d6f3eaed6", "url": "https://threejs.org/manual/en/webxr-basics.html", "page_title": "VR", "page_type": "guide", "page_summary": "A guide to building VR applications with three.js using WebXR, covering setup, device considerations, and supporting both VR and non-VR modes.", "heading": "Deciding on the Level of VR Support", "content": "Page: VR\nSection: Deciding on the Level of VR Support\n\nAbove we covered 3 types of VR devices.\n\n- 3DOF no input\n- 3DOF + 3DOF input\n- 6DOF + 6DOF input\n\nYou need to decide how much effort you're willing to put in to support each type of device.\n\nFor example the simplest device has no input. The best you can generally do is make it so there are some buttons or objects in the user's view and if the user aligns some marker in the center of the display on those objects for 1/2 a second or so then that button is clicked. A common UX is to display a small timer that will appear over the object indicating if you keep the marker there for a moment the object/button will be selected.\n\nSince there is no other input that's about the best you can do\n\nThe next level up you have one 3DOF input device. Generally it can point at things and the user has at least 2 buttons. The Daydream also has a touchpad which provides normal touch inputs.\n\nIn any case if a user has this type of device it's far more comfortable for the user to by able to point at things with their controller than it is to make them do it with their head by looking at things.\n\nA similar level to that might be 3DOF or 6DOF device with a game console controller. You'll have to decide what to do here. I suspect the most common thing is the user still has to look to point and the controller is just used for buttons.\n\nThe last level is a user with a 6DOF headset and 2 6DOF controllers. Those users will find an experience that is only 3DOF to often be frustrating. Similarly they usually expect to be able to virtually manipulate things with their hands in VR so you'll have to decide if you want to support that or not.\n\nAs you can see getting started in VR is pretty easy but actually making something shippable in VR will require lots of decision making and design.\n\nThis was a pretty brief intro into VR with three.js. We'll cover some of the input methods in future articles.", "code_blocks": [], "chunk_position": 57, "heading_path": "Deciding on the Level of VR Support > Deciding on the Level of VR Support", "breadcrumbs": "VR > Deciding on the Level of VR Support > Deciding on the Level of VR Support"}, {"id": "697c27f7198bb2b3", "url": "https://threejs.org/manual/en/webxr-look-to-select.html", "page_title": "VR - Look to Select", "page_type": "guide", "page_summary": "A tutorial on implementing a 'look to select' interaction pattern for Google Cardboard-style VR in Three.js, where users select objects by pointing their head at them. Covers building a PickHelper, creating a visual selection gauge using texture offset animation, and pairing objects for selection feedback.", "heading": "Introduction", "content": "Page: VR - Look to Select\nSection: Introduction\n\n**NOTE: The examples on this page require a VR capable device. Without one they won't work.**\n\nIn the previous article we went over a very simple VR example using three.js and we discussed the various kinds of VR systems.\n\nThe simplest and possibly most common is the Google Cardboard style of VR which is basically a phone put into a $5 - $50 face mask. This kind of VR has no controller so people have to come up with creative solutions for allowing user input.\n\nThe most common solution is \"look to select\" where if the user points their head at something for a moment it gets selected.\n\nLet's implement \"look to select\"! We'll start with an example from the previous article and to do it we'll add the `PickHelper` we made in the article on picking.", "code_blocks": [], "chunk_position": 58, "heading_path": "Introduction > Introduction", "breadcrumbs": "VR - Look to Select > Introduction > Introduction"}, {"id": "647757d4bb20f813", "url": "https://threejs.org/manual/en/webxr-look-to-select.html", "page_title": "VR - Look to Select", "page_type": "guide", "page_summary": "A tutorial on implementing a 'look to select' interaction pattern for Google Cardboard-style VR in Three.js, where users select objects by pointing their head at them. Covers building a PickHelper, creating a visual selection gauge using texture offset animation, and pairing objects for selection feedback.", "heading": "Adding PickHelper for Basic Object Picking", "content": "Page: VR - Look to Select\nSection: Adding PickHelper for Basic Object Picking\n\nFor an explanation of that code see the article on picking.\n\nTo use it we just need to create an instance and call it in our render loop.\n\nIn the original picking example we converted the mouse coordinates from CSS pixels into normalized coordinates that go from -1 to +1 across the canvas.\n\nIn this case though we will always pick where the camera is facing which is the center of the screen so we pass in `0` for both `x` and `y` which is the center in normalized coordinates.\n\nAnd with that objects will flash when we look at them.", "code_blocks": [{"language": "javascript", "code": "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 > 1 ? 0xFFFF00 : 0xFF0000);\n    }\n  }\n}", "filename": ""}, {"language": "javascript", "code": "+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);", "filename": ""}], "chunk_position": 58, "heading_path": "Adding PickHelper for Basic Object Picking > Adding PickHelper for Basic Object Picking", "breadcrumbs": "VR - Look to Select > Adding PickHelper for Basic Object Picking > Adding PickHelper for Basic Object Picking"}, {"id": "7f6054064fe0ae86", "url": "https://threejs.org/manual/en/webxr-look-to-select.html", "page_title": "VR - Look to Select", "page_type": "guide", "page_summary": "A tutorial on implementing a 'look to select' interaction pattern for Google Cardboard-style VR in Three.js, where users select objects by pointing their head at them. Covers building a PickHelper, creating a visual selection gauge using texture offset animation, and pairing objects for selection feedback.", "heading": "Creating a Selection Timer Gauge", "content": "Page: VR - Look to Select\nSection: Creating a Selection Timer Gauge\n\nTypically we don't want selection to be immediate. Instead we require the user to keep the camera on the thing they want to select for a few moments to give them a chance not to select something by accident.\n\nTo do that we need some kind of meter or gauge or some way to convey that the user must keep looking and for how long.\n\nOne easy way we could do that is to make a 2 color texture and use a texture offset to slide the texture across a model.\n\nLet's do this by itself to see it work before we add it to the VR example.\n\nFirst we make an `OrthographicCamera`.\n\nAnd of course update it if the canvas changes size.\n\nWe now have a camera that shows 2 units above and below the center and aspect units left and right.\n\nNext let's make a 2 color texture. We'll use a `DataTexture` which we've used a few other places.\n\nWe'll then use that texture on a `TorusGeometry`.\n\n`THREE.MathUtils.mapLinear` takes a value that goes between `fromStart` and `fromEnd` and maps it to a value between `toStart` and `toEnd`. In the case above we're taking `time % 2` which means a value that goes from 0 to 2 and maps that to a value that goes from -0.5 to 0.5.\n\nTextures are mapped to geometry using normalized texture coordinates that go from 0 to 1. That means our 2x1 pixel image, set to the default wrapping mode of `THREE.ClampToEdge`, if we adjust the texture coordinates by -0.5 then the entire mesh will be the first color and if we adjust the texture coordinates by +0.5 the entire mesh will be the second color. In between with the filtering set to `THREE.NearestFilter` we'll be able to move the transition between the 2 colors through the geometry.", "code_blocks": [{"language": "javascript", "code": "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);", "filename": ""}, {"language": "javascript", "code": "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  ...", "filename": ""}, {"language": "javascript", "code": "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);", "filename": ""}, {"language": "javascript", "code": "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);", "filename": ""}, {"language": "javascript", "code": "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}", "filename": ""}, {"language": "javascript", "code": "+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;", "filename": ""}], "chunk_position": 58, "heading_path": "Creating a Selection Timer Gauge > Creating a Selection Timer Gauge", "breadcrumbs": "VR - Look to Select > Creating a Selection Timer Gauge > Creating a Selection Timer Gauge"}, {"id": "e062df27edaae88e", "url": "https://threejs.org/manual/en/webxr-look-to-select.html", "page_title": "VR - Look to Select", "page_type": "guide", "page_summary": "A tutorial on implementing a 'look to select' interaction pattern for Google Cardboard-style VR in Three.js, where users select objects by pointing their head at them. Covers building a PickHelper, creating a visual selection gauge using texture offset animation, and pairing objects for selection feedback.", "heading": "Notes on Cursor Implementation", "content": "Page: VR - Look to Select\nSection: Notes on Cursor Implementation\n\nA few things to notice **and try**.\n\n- We set the `cursorMaterial`'s `blending`, `blendSrc` and `blendDst` properties as follows:\n\n  ```\n    blending: THREE.CustomBlending,\n    blendSrc: THREE.OneMinusDstColorFactor,\n    blendDst: THREE.OneMinusSrcColorFactor,\n  ```\n\n  This gives as an *inverse* type of effect. Comment out those 3 lines and you'll see the difference. I'm just guessing the inverse effect is best here as that way we can hopefully see the cursor regardless of the colors it is over.\n\n- We use a `TorusGeometry` and not a `RingGeometry`.\n\n  For whatever reason the `RingGeometry` uses a flat UV mapping scheme. Because of this if we use a `RingGeometry` the texture slides horizontally across the ring instead of around it like it does above.\n\n  Try it out, change the `TorusGeometry` to a `RingGeometry` (it's just commented out in the example above) and you'll see what I mean.\n\n  The *proper* thing to do (for some definition of *proper*) would be to either use the `RingGeometry` but fix the texture coordinates so they go around the ring. Or else, generate our own ring geometry. But, the torus works just fine. Placed directly in front of the camera with a `MeshBasicMaterial` it will look exactly like a ring and the texture coordinates go around the ring so it works for our needs.", "code_blocks": [], "chunk_position": 58, "heading_path": "Notes on Cursor Implementation > Notes on Cursor Implementation", "breadcrumbs": "VR - Look to Select > Notes on Cursor Implementation > Notes on Cursor Implementation"}, {"id": "aee1c7e656c963e2", "url": "https://threejs.org/manual/en/webxr-look-to-select.html", "page_title": "VR - Look to Select", "page_type": "guide", "page_summary": "A tutorial on implementing a 'look to select' interaction pattern for Google Cardboard-style VR in Three.js, where users select objects by pointing their head at them. Covers building a PickHelper, creating a visual selection gauge using texture offset animation, and pairing objects for selection feedback.", "heading": "Integrating the Cursor into VR Code", "content": "Page: VR - Look to Select\nSection: Integrating the Cursor into VR Code\n\nLet's integrate it with our VR code above.\n\nYou can see the code above we added all the code to create the cursor geometry, texture, and material and we added it as a child of the camera so it will always be in front of the camera. Note we need to add the camera to the scene otherwise the cursor won't be rendered.\n\nWe then check if the thing we're picking this time is the same as it was last time. If so we add the elapsed time to a timer and if the timer reaches its limit we return the selected item.", "code_blocks": [{"language": "javascript", "code": "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 > 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 && lastPickedObject === this.pickedObject) {\n+      this.selectTimer += elapsedTime;\n+      if (this.selectTimer >= 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}", "filename": ""}, {"language": "javascript", "code": "+scene.add(camera);", "filename": ""}], "chunk_position": 58, "heading_path": "Integrating the Cursor into VR Code > Integrating the Cursor into VR Code", "breadcrumbs": "VR - Look to Select > Integrating the Cursor into VR Code > Integrating the Cursor into VR Code"}, {"id": "0b6e04dea292d128", "url": "https://threejs.org/manual/en/webxr-look-to-select.html", "page_title": "VR - Look to Select", "page_type": "guide", "page_summary": "A tutorial on implementing a 'look to select' interaction pattern for Google Cardboard-style VR in Three.js, where users select objects by pointing their head at them. Covers building a PickHelper, creating a visual selection gauge using texture offset animation, and pairing objects for selection feedback.", "heading": "Creating Paired Box and Sphere Meshes", "content": "Page: VR - Look to Select\nSection: Creating Paired Box and Sphere Meshes\n\nNow let's use that to pick the cubes. As a simple example we'll add 3 spheres as well. When a cube is picked with hide the cube and un-hide the corresponding sphere.\n\nSo first we'll make a sphere geometry.\n\nThen let's create 3 pairs of box and sphere meshes. We'll use a `Map` so that we can associate each `Mesh` with its partner.\n\nIn `render` where we rotate the cubes we need to iterate over `meshToMeshMap` instead of `cubes`.", "code_blocks": [{"language": "javascript", "code": "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);", "filename": ""}, {"language": "javascript", "code": "-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) => {\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+});", "filename": ""}, {"language": "javascript", "code": "-cubes.forEach((cube, ndx) => {\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+}", "filename": ""}], "chunk_position": 58, "heading_path": "Creating Paired Box and Sphere Meshes > Creating Paired Box and Sphere Meshes", "breadcrumbs": "VR - Look to Select > Creating Paired Box and Sphere Meshes > Creating Paired Box and Sphere Meshes"}, {"id": "74669679da56a3a2", "url": "https://threejs.org/manual/en/webxr-look-to-select.html", "page_title": "VR - Look to Select", "page_type": "guide", "page_summary": "A tutorial on implementing a 'look to select' interaction pattern for Google Cardboard-style VR in Three.js, where users select objects by pointing their head at them. Covers building a PickHelper, creating a visual selection gauge using texture offset animation, and pairing objects for selection feedback.", "heading": "Final Selection Logic", "content": "Page: VR - Look to Select\nSection: Final Selection Logic\n\nAnd now we can use our new `PickHelper` implementation to select one of the objects. When selected we hide that object and un-hide its partner.\n\nAnd with that we should have a pretty decent *look to select* implementation.", "code_blocks": [{"language": "javascript", "code": "// 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+}", "filename": ""}], "chunk_position": 58, "heading_path": "Final Selection Logic > Final Selection Logic", "breadcrumbs": "VR - Look to Select > Final Selection Logic > Final Selection Logic"}, {"id": "7fe0dce75b9c03c7", "url": "https://threejs.org/manual/en/webxr-look-to-select.html", "page_title": "VR - Look to Select", "page_type": "guide", "page_summary": "A tutorial on implementing a 'look to select' interaction pattern for Google Cardboard-style VR in Three.js, where users select objects by pointing their head at them. Covers building a PickHelper, creating a visual selection gauge using texture offset animation, and pairing objects for selection feedback.", "heading": "Conclusion", "content": "Page: VR - Look to Select\nSection: Conclusion\n\nI hope this example gave some ideas of how to implement a \"look to select\" type of Google Cardboard level UX. Sliding textures using texture coordinates offsets is also a commonly useful technique.\n\nNext up let's allow the user that has a VR controller to point at and move things.", "code_blocks": [], "chunk_position": 58, "heading_path": "Conclusion > Conclusion", "breadcrumbs": "VR - Look to Select > Conclusion > Conclusion"}, {"id": "16cf995b04557c5a", "url": "https://threejs.org/manual/en/webxr-point-to-select.html", "page_title": "VR - 3DOF Point to Select", "page_type": "guide", "page_summary": "A tutorial on implementing point-to-select functionality using VR controllers in Three.js, covering raycasting from controller positions, event dispatching, highlighting, and moving objects via controller interaction.", "heading": "VR - 3DOF Point to Select", "content": "Page: VR - 3DOF Point to Select\nSection: VR - 3DOF Point to Select\n\n**NOTE: The examples on this page require a VR capable device with a pointing device. Without one they won't work. See this article as to why**\n\nIn the previous article we went over a very simple VR example where we let the user choose things by pointing via looking. In this article we will take it one step further and let the user choose with a pointing device\n\nThree.js makes is relatively easy by providing 2 controller objects in VR and tries to handle both cases of a single 3DOF controller and two 6DOF controllers. Each of the controllers are Object3D objects which give the orientation and position of that controller. They also provide `selectstart`, `select` and `selectend` events when the user starts pressing, is pressing, and stops pressing (ends) the \"main\" button on the controller.\n\nStarting with the last example from the previous article let's change the `PickHelper` into a `ControllerPickHelper`.\n\nOur new implementation will emit a `select` event that gives us the object that was picked so to use it we'll just need to do this.\n\nRemember from our previous code `meshToMeshMap` maps our boxes and spheres to each other so if we have one we can look up its partner through `meshToMeshMap` so here we're just hiding the selected object and un-hiding its partner.\n\nAs for the actual implementation of `ControllerPickHelper`, first we need to add the VR controller objects to the scene and to those add some 3D lines we can use to display where the user is pointing. We save off both the controllers and their lines.\n\nWithout doing anything else this alone would give us 1 or 2 lines in the scene showing where the user's pointing devices are and which way they are pointing.\n\nOne problem we have though, we don't want have our `RayCaster` pick the line itself so an easy solution is separate the objects we wanted to be able to pick from the objects we don't by parenting them under another Object3D.\n\nNext let's add some code to pick from the controllers. This is the first time we've picked with something not the camera. In our article on picking the user uses the mouse or finger to pick which means picking comes from the camera into the screen. In the previous article we were picking based on which way the user is looking so again that comes from the camera. This time though we're picking from the position of the controllers so we're not using the camera.\n\nLike before we use a Raycaster but this time we take the ray from the controller. Our previous `PickHelper` there was only one thing picking but here we have up to 2 controllers, one for each hand. We save off which object each controller is looking at in `controllerToObjectMap`. We also save off the original emissive color in `objectToColorMap` and we make the line long enough to touch whatever it's pointing at.\n\nWe need to add some code to reset these settings every frame.\n\nNext we want to emit a `select` event when the user clicks the controller. To do that we can extend three.js's EventDispatcher and then we'll check when we get a `select` event from the controller, then if that controller is pointing at something we emit what that controller is pointing at as our own `select` event.\n\nAll that is left is to call `update` in our render loop\n\nand assuming you have a VR device with a controller you should be able to use the controllers to pick things.\n\nAnd what if we wanted to be able to move the objects?\n\nThat's relatively easy. Let's move our controller 'select' listener code out into a function so we can use it for more than one thing.\n\nThen let's use it for both `selectstart` and `select`\n\nand let's also pass on the `selectend` event which three.js sends out when you user lets of the button on the controller.\n\nNow let's change the code so when we get a `selectstart` event we'll remove the selected object from the scene and make it a child of the controller. This means it will move with the controller. When we get a `selectend` event we'll put it back in the scene.\n\nWhen an object is selected we save off that object and its original parent. When the user is done we can put the object back.\n\nWe use the Object3D.attach to re-parent the selected objects. These functions let us change the parent of an object without changing its orientation and position in the scene.\n\nAnd with that we should be able to move the objects around with a 6DOF controller or at least change their orientation with a 3DOF controller\n\nTo be honest I'm not 100% sure this `ControllerPickHelper` is the best way to organize the code but it's useful to demonstrating the various parts of getting something simple working in VR in three.js", "code_blocks": [{"language": "javascript", "code": "const pickHelper = new ControllerPickHelper(scene);\npickHelper.addEventListener('select', (event) => {\n  event.selectedObject.visible = false;\n  const partnerObject = meshToMeshMap.get(event.selectedObject);\n  partnerObject.visible = true;\n});", "filename": ""}, {"language": "javascript", "code": "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 < 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}", "filename": ""}, {"language": "javascript", "code": "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...", "filename": ""}, {"language": "javascript", "code": "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 < 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 > 1 ? 0xFF2000 : 0xFF0000);\n+        }\n+      } else {\n+        line.scale.z = 5;\n+      }\n+    }\n+  }\n}", "filename": ""}, {"language": "javascript", "code": "class ControllerPickHelper {\n\n  ...\n\n+  _reset() {\n+    // restore the colors\n+    this.objectToColorMap.forEach((color, object) => {\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}", "filename": ""}, {"language": "javascript", "code": "-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 < 2; ++i) {\n      const controller = renderer.xr.getController(i);\n+      controller.addEventListener('select', (event) => {\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}", "filename": ""}, {"language": "javascript", "code": "function render(time) {\n\n  ...\n\n+  pickHelper.update(pickablesParent, time);\n\n  renderer.render(scene, camera);\n}", "filename": ""}, {"language": "javascript", "code": "class ControllerPickHelper extends THREE.EventDispatcher {\n  constructor(scene) {\n    super();\n\n    ...\n\n    this.controllers = [];\n\n+    const selectListener = (event) => {\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 < 2; ++i) {\n      const controller = renderer.xr.getController(i);\n-      controller.addEventListener('select', (event) => {\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       ...", "filename": ""}, {"language": "javascript", "code": "class ControllerPickHelper extends THREE.EventDispatcher {\n  constructor(scene) {\n    super();\n\n    ...\n\n    this.controllers = [];\n\n    const selectListener = (event) => {\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 < 2; ++i) {\n      const controller = renderer.xr.getController(i);\n      controller.addEventListener('select', selectListener);\n      controller.addEventListener('selectstart', selectListener);\n\n       ...", "filename": ""}, {"language": "javascript", "code": "class ControllerPickHelper extends THREE.EventDispatcher {\n  constructor(scene) {\n    super();\n\n    ...\n\n    this.controllers = [];\n\n    const selectListener = (event) => {\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) => {\n+      const controller = event.target;\n+      this.dispatchEvent({type: event.type, controller});\n+    };\n\n    for (let i = 0; i < 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       ...", "filename": ""}, {"language": "javascript", "code": "const pickHelper = new ControllerPickHelper(scene);\n-pickHelper.addEventListener('select', (event) => {\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) => {\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) => {\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+});", "filename": ""}], "chunk_position": 59, "heading_path": "VR - 3DOF Point to Select > VR - 3DOF Point to Select", "breadcrumbs": "VR - 3DOF Point to Select > VR - 3DOF Point to Select > VR - 3DOF Point to Select"}, {"id": "29e4ead7dd3b7ad2", "url": "https://threejs.org/manual/en/material-table.html", "page_title": "Material Feature Table", "page_type": "reference", "page_summary": "A reference table showing which properties and features are supported by each of the five main Mesh materials in three.js (Basic, Lambert, Phong, Standard, Physical).", "heading": "Material Feature Table", "content": "Page: Material Feature Table\nSection: Material Feature Table\n\nThe most common materials in three.js are the Mesh materials. Here is a table showing which material support which features.\n\nThe table below compares feature support across the five main Mesh materials. Each column header links to the corresponding material documentation; each table cell with a bullet (•) links to that property's documentation page.\n\nMaterials compared: Basic, Lambert, Phong, Standard, Physical.\n\nFeature comparison (• indicates support):\n\n| Feature | Basic | Lambert | Phong | Standard | Physical |\n|---|---|---|---|---|---|\n| alphaMap | • | • | • | • | • |\n| anisotropy |  |  |  |  | • |\n| anisotropyMap |  |  |  |  | • |\n| anisotropyRotation |  |  |  |  | • |\n| aoMap | • | • | • | • | • |\n| aoMapIntensity | • | • | • | • | • |\n| attenuationColor |  |  |  |  | • |\n| attenuationDistance |  |  |  |  | • |\n| bumpMap |  | • | • | • | • |\n| bumpScale |  | • | • | • | • |\n| clearcoat |  |  |  |  | • |\n| clearcoatMap |  |  |  |  | • |\n| clearcoatNormalMap |  |  |  |  | • |\n| clearcoatNormalScale |  |  |  |  | • |\n| clearcoatRoughness |  |  |  |  | • |\n| clearcoatRoughnessMap |  |  |  |  | • |\n| color | • | • | • | • | • |\n| combine | • | • | • |  |  |\n| displacementBias |  | • | • | • | • |\n| displacementMap |  | • | • | • | • |\n| displacementScale |  | • | • | • | • |\n| emissive |  | • | • | • | • |\n| emissiveIntensity |  | • | • | • | • |\n| emissiveMap |  | • | • | • | • |\n| envMap | • | • | • | • | • |\n| envMapIntensity |  |  |  | • | • |\n| envMapRotation | • | • | • | • | • |\n| flatShading |  | • | • | • | • |\n| fog | • | • | • | • | • |\n| ior |  |  |  |  | • |\n| iridescence |  |  |  |  | • |\n| iridescenceIOR |  |  |  |  | • |\n| iridescenceMap |  |  |  |  | • |\n| iridescenceThicknessMap |  |  |  |  | • |\n| iridescenceThicknessRange |  |  |  |  | • |\n| lightMap | • | • | • | • | • |\n| lightMapIntensity | • | • | • | • | • |\n| map | • | • | • | • | • |\n| metalness |  |  |  | • | • |\n| metalnessMap |  |  |  | • | • |\n| normalMap |  | • | • | • | • |\n| normalMapType |  | • | • | • | • |\n| normalScale |  | • | • | • | • |\n| reflectivity | • | • | • |  | • |\n| refractionRatio | • | • | • |  |  |\n| roughness |  |  |  | • | • |\n| roughnessMap |  |  |  | • | • |\n| sheen |  |  |  |  | • |\n| sheenColor |  |  |  |  | • |\n| sheenColorMap |  |  |  |  | • |\n| sheenRoughness |  |  |  |  | • |\n| sheenRoughnessMap |  |  |  |  | • |\n| shininess |  |  | • |  |  |\n| specular |  |  | • |  |  |\n| specularColor |  |  |  |  | • |\n| specularColorMap |  |  |  |  | • |\n| specularIntensity |  |  |  |  | • |\n| specularIntensityMap |  |  |  |  | • |\n| specularMap | • | • | • |  |  |\n| thickness |  |  |  |  | • |\n| thicknessMap |  |  |  |  | • |\n| transmission |  |  |  |  | • |\n| transmissionMap |  |  |  |  | • |\n| wireframe | • | • | • | • | • |\n| wireframeLinecap | • | • | • | • | • |\n| wireframeLinejoin | • | • | • | • | • |\n| wireframeLinewidth | • | • | • | • | • |", "code_blocks": [], "chunk_position": 60, "heading_path": "Material Feature Table > Material Feature Table", "breadcrumbs": "Material Feature Table > Material Feature Table > Material Feature Table"}]