threejs-materials

Three.js materials - PBR, basic, phong, shader materials, material properties. Use when styling meshes, working with textures, creating custom shaders, or optimizing material performance.

By cloudai-x · 9,400 installs

npx skills add cloudai-x/threejs-skills --skill threejs-materials

Source repository · Upstream listing

Three.js Materials Targets three.js r186. Examples use WebGLRenderer; the WebGPU and TSL section uses WebGPURenderer. Quick Start Material Types Overview Material Use case Lighting MeshBasicMaterial Unlit color, textures, wireframes None MeshLambertMaterial Matte diffuse surfaces Diffuse MeshPhongMaterial Shiny plastic, cheap specular Diffuse + specular MeshStandardMaterial PBR metal/rough workflow PBR MeshPhysicalMaterial PBR plus clearcoat, transmission, sheen, iridescence PBR MeshToonMaterial Cel shading Toon steps MeshMatcapMaterial Lit look baked into a matcap texture, no lights needed Baked ShadowMaterial Transparent surface that shows only the shadows it receives Shadows only PointsMaterial Point clouds None LineBasicMaterial / LineDashedMaterial Lines None ShaderMaterial / RawShaderMaterial Custom GLSL, WebGLRenderer only Custom Color data textures (map, emissiveMap, specularMap, sheenColorMap, specularColorMap, LDR matcap, a canvas or video used as color) need texture.colorSpace = THREE.SRGBColorSpace , or colors render washed out. Scalar maps (normal, bump, displacement, roughness, metalness, ao, alpha, toon gradientMap) keep the default. HDR loaders set their own color space. MeshBasicMaterial No lighting calculations. Fast, always visible. Supports envMap with reflectivity (0 to 1) and combine ( THREE.MultiplyOperation , THREE.MixOperation , THREE.AddOperation ); specularMap (sRGB) masks the env map contribution. MeshLambertMaterial and MeshPhongMaterial Lambert is diffuse only. Phong adds a specular highlight; it is the cheap alternative when Standard is too heavy. Both also support map , emissiveMap , specularMap (sRGB; masks the env map reflection, and the highlight on Phong), normalMap , bumpMap , displacementMap , aoMap , envMap , envMapIntensity and envMapRotation . MeshStandardMaterial (PBR) Physically based rendering. Recommended for realistic results. lightMap (with lightMapIntensity ) bakes indirect light the same way aoMap bakes occlusion; an HDR or EXR lightMap is LinearSRGBColorSpace , which the loaders set. UV channels: aoMap and lightMap read the channel you pick Every UV mapped texture samples the attribute selected by texture.channel : 0 reads uv (the default), 1 reads uv1 , 2 reads uv2 , 3 reads uv3 . aoMap and lightMap need no second UV set they sample uv unless you change channel . GLTFLoader loads TEXCOORD 1 as uv1 and sets channel itself. MeshPhysicalMaterial (Advanced PBR) Extends MeshStandardMaterial with layer and volume features. Texture variants exist for most of them: clearcoatMap , clearcoatRoughnessMap , clearcoatNormalMap , transmissionMap , thicknessMap , iridescenceMap , iridescenceThicknessMap , sheenColorMap , sheenRoughnessMap , anisotropyMap , specularIntensityMap , specularColorMap . Glass transmission with a smooth surface and something behind it to refract: MeshToonMaterial Cel shading. The gradient map is non color data keep its default color space. MeshNormalMaterial and MeshDepthMaterial PointsMaterial For point clouds. size is in pixels; with sizeAttenuation (the default, perspective cameras only) it is multiplied by half the canvas height and divided by the point's view space depth. LineBasicMaterial and LineDashedMaterial WebGL and WebGPU always draw line primitives 1 px wide; linewidth , linecap and linejoin only affect the SVGRenderer. For thick lines under WebGLRenderer use Line2 with LineGeometry and LineMaterial from three/addons/lines/ ( linewidth in pixels); LineMaterial is a ShaderMaterial, so WebGPURenderer needs Line2 from three/addons/lines/webgpu/Line2.js , which uses Line2NodeMaterial . ShaderMaterial (WebGLRenderer only) Custom GLSL with three.js uniforms injected for you. It always compiles as GLSL ES 3.00: texture() , textureLod() and dFdx work, and texture2D / gl FragColor remain as aliases leave glslVersion unset. ShaderMaterial, RawShaderMaterial and onBeforeCompile have no WebGPURenderer path; there, use node materials and TSL. Declared for you in the shader: modelMatrix , modelViewMatrix , projectionMatrix , viewMatrix , normalMatrix , cameraPosition , and the attributes position , normal , uv (plus instanceMatrix on an InstancedMesh, which you apply yourself: projectionMatrix modelViewMatrix instanceMatrix vec4(pos, 1.0) , and color with vertexColors). The extra uv sets and tangent are not declared add in vec2 uv1; yourself. RawShaderMaterial (WebGLRenderer only) No declarations are prepended (only version from glslVersion, the SHADER TYPE and SHADER NAME defines, and your defines ), but the renderer still sets projectionMatrix , modelViewMatrix , viewMatrix , normalMatrix , modelMatrix and cameraPosition declare them in the shader and never pass them in uniforms : a same named entry is overwritten every draw ( modelMatrix , modelViewMatrix , normalMatrix ) or replaces the camera's value ( projectionMatrix , viewMatrix , cameraPosition ). Common Properties Multiple Materials per Mesh Environment Maps An equirectangular HDR file lights and reflects every PBR material in the scene. Both renderers prefilter it themselves no PMREM step needed. scene.environment lights MeshStandardMaterial and MeshPhysicalMaterial on both renderers. With WebGLRenderer it also lights MeshLambertMaterial and MeshPhongMaterial; WebGPURenderer does not. Runtime Changes, Cloning, Disposal Dispose a material nothing else uses: material.dispose() releases its GPU programs, which are deleted once no other material uses them; textures need their own texture.dispose() . Performance, in order of impact: 1. Share one material instance across meshes the renderer re uploads material uniforms whenever consecutive draws switch material (identical settings already share one compiled program). 2. Prefer opaque rendering: transparent materials are sorted back to front by object and drawn after the opaque ones, still writing depth unless depthWrite: false , so stacked layers overdraw and intersecting ones misorder; for cutouts, alphaTest keeps the material opaque, so it draws in the opaque pass, correctly in any order. 3. Pick the cheapest material that looks right: Basic, then Lambert, Phong, Standard, Physical. WebGPU and TSL MeshStandardMaterial and the other built in classes run under WebGPURenderer unchanged the renderer converts them to node materials automatically; ShaderMaterial, RawShaderMaterial, MeshDepthMaterial and MeshDistanceMaterial have no node equivalent and log Material "..." is not compatible . For custom shading use the node material classes from three/webgpu with TSL nodes from three/tsl ; they target WebGPURenderer, and WebGLRenderer draws them only through the WebGLNodesHandler addon ( three/addons/tsl/WebGLNodesHandler.js , passed to renderer.setNodesHandler() ), a migration aid without VSM shadows, MRT, transmission or the WebGPU post processing stack. Common Mistakes geometry.setAttribute("uv2", geometry.attributes.uv) → const material = new THREE.MeshStandardMaterial({ aoMap }); : aoMap samples the attribute picked by aoMap.channel , 0 ( uv ) by default, so it needs no extra attribute and uv2 is read only with aoMap.channel = 2 ; only a separate unwrap goes in uv1 with aoMap.channel = 1 . new RGBELoader() → new HDRLoader() : HDRLoader was renamed from RGBELoader in r180; RGBELoader is deprecated and still loads, with a warning import HDRLoader from "three/addons/loaders/HDRLoader.js". "three/examples/jsm/loaders/HDRLoader.js" → "three/addons/loaders/HDRLoader.js" : the browser import maps map three/addons/ to examples/jsm/ and define no three/examples/ key, so the examples path fails there. material.blending = THREE.MultiplyBlending; → material.blending = THREE.MultiplyBlending; material.premultipliedAlpha = true; : multiply and subtractive blending require premultiplied alpha, or the renderer logs an error and sets no blend function. material.stencilMask = 0xff; → material.stencilFuncMask = 0xff; material.stencilWriteMask = 0xff; : stencilMask does not exist; assigning it just creates an unused field. new THREE.WebGLRenderer({ antialias: true }) → new THREE.WebGLRenderer({ antialias: true, stencil: true }) : the stencil buffer is off by default, so material stencil settings are silently ignored without it. material.envMapIntensity = 0.5; → scene.environmentIntensity = 0.5; : to dim scene.environment use the scene property envMapIntensity attenuates only the material's own envMap. material.uniforms.time.value = clock.getElapsedTime(); → material.uniforms.time.value = timer.getElapsed(); : Clock is deprecated in r183 create a THREE.Timer, call timer.connect(document) once, and timer.update() (no argument) first in the loop. new THREE.ShaderMaterial({ glslVersion: THREE.GLSL3, fragmentShader }) → new THREE.ShaderMaterial({ fragmentShader }) : ShaderMaterial already compiles as GLSL ES 3.00, so texture() works with glslVersion unset, while GLSL3 removes the gl FragColor alias and a shader that writes it fails to compile. uniforms: { modelViewMatrix: { value: new THREE.Matrix4() } } → uniform mat4 modelViewMatrix; : declare the built in matrices in a RawShaderMaterial's shader source a same named uniforms entry is overwritten every draw (modelMatrix, modelViewMatrix, normalMatrix) or replaces the camera's value (projectionMatrix, viewMatrix, cameraPosition). new THREE.LineBasicMaterial({ linewidth: 5 }) → new Line2(new LineGeometry().setPositions([ 1, 0, 0, 1, 0, 0]), new LineMaterial({ linewidth: 5 })) : WebGLRenderer always draws 1 px line primitives; Line2 needs a LineGeometry, not a BufferGeometry, and all three come from three/addons/lines/ (under WebGPURenderer, Line2 comes from three/addons/lines/webgpu/Line2.js with Line2NodeMaterial). new THREE.ShaderMaterial({ vertexShader, fragmentShader }) → new THREE.MeshBasicNodeMaterial() : under WebGPURenderer use node materials and TSL ShaderMaterial and RawShaderMaterial have no node equivalent and log "is not compatible". import { texture } from "three/nodes"; → import { texture } from "three/tsl"; : three/nodes does not exist node materials come from three/webgpu , TSL functions from three/tsl . import as THREE from "three"; const m = new THREE.MeshStandardNodeMaterial(); → import as THREE from "three/webgpu"; const m = new THREE.MeshStandardNodeMaterial(); : node materials are exported only by three/webgpu and target WebGPURenderer. new THREE.PointsMaterial({ size: 5 }) → new THREE.Sprite(new THREE.PointsNodeMaterial({ positionNode: instancedBufferAttribute(positions) })) : the WebGPU backend draws Points at 1 px whatever the size set sprite.count to the number of points. material.positionNode = positionGeometry.add(offset); → material.positionNode = positionLocal.add(offset); : positionLocal carries skinning, morph targets, batching and instancing; positionGeometry is the raw rest pose attribute. map.encoding = THREE.sRGBEncoding; → map.colorSpace = THREE.SRGBColorSpace; : encoding and sRGBEncoding no longer exist, so the old line does nothing and the color map renders washed out. const m = new THREE.MeshStandardNodeMaterial(); m.colorNode = color(0xff0000); → const m = new THREE.MeshBasicNodeMaterial(); m.colorNode = color(0xff0000); : MeshStandardNodeMaterial is lit and renders black without lights or an environment, so an unlit effect uses MeshBasicNodeMaterial. new THREE.ShaderMaterial({ extensions: { derivatives: true }, fragmentShader }) → new THREE.ShaderMaterial({ fragmentShader }) : the WebGL1 e