3d-metal-material
Skill file agent-skills/3d/3d-metal-material/SKILL.md in MengTo/Skills. Read the publisher’s upstream source.
Description could not be read from the source metadata.
Try your first task
3d-metal-material
Start a fresh conversation. Replace the highlighted placeholders.
Use 3d-metal-material to implement a polished steel material and structured dark-studio reflections in [Three.js scene and version] for [object scale and camera framing], using linear steel F0, metalness 1, explicit PMREM envMap binding, footprint-filtered roughness variation, bevel/contact cues, and reduced-motion-safe rendering for [desired finish].
Required inputs
- [Three.js scene and version]
- [object scale and camera framing]
- [desired finish]
User-approved starter prompt based on a pinned source version; check current skill requirements before running. Task evidence ↗
SKILL.md
name: 3d-metal-materialdescription: Build hyperrealistic PBR metal in Three.js: steel, brass, gold, copper and iron as true conductors (metalness 1, measured F0 tint, no albedo) with roughness-varied finishes (polished, brushed anisotropic, aged with patina and worn edges) lit by a dark HDR studio of real bright window cards prefiltered with PMREM. Use when metal looks like grey plastic, a dull blur, or a black hole; when blades, tools, rings, ingots, hardware or product metal need reflections with edges and structure; or when a finish needs scratches, streaks, fingerprints and bevels that catch light.3D Metal Material
Mechanism: a conductor has no colour of its own, so it looks like metal only when a dark environment holds a few very bright, hard-edged things to reflect, and the surface then bends those reflections with roughness that varies across it. Remove the bright cards and the same steel goes flat grey; remove the roughness variation and it goes CG-clean.
Boundary. This skill is the material and the environment it reads. Reach for 3d-specular-rim-glow when the job is one bright edge catch and halo on a product, 3d-high-resolution-textures when you ship authored PBR texture sets and need texel density and mip budgets, 3d-ultra-realistic-water for water. Siblings 3d-wood-material, 3d-cloth-material and 3d-paper-material cover the non-metal surfaces that usually sit next to metal; use them for the bench, handle or wrapping. The demo has a bench and a chisel handle only as staging.
Verified stack of the demo: Three.js r169 (three@0.169.0), WebGL 2, GLSL patched into MeshStandardMaterial/MeshPhysicalMaterial with onBeforeCompile, PMREMGenerator, a HalfFloat MSAA target and a hand-written ACES pass. No post-processing library.
Build order
Do these in order; each one fixes a named failure the previous step cannot.
-
Conductor, not painted.
metalness: 1,color= the metal's F0 (linear reflectance at normal incidence), nothing else incolor. A bright base colour turns lit faces into white paint, and metalness < 1 lets a diffuse term leak in as plastic. -
A dark room with bright cards. Build the environment as a real
SceneofMeshBasicMaterialplanes withtoneMapped:falseand HDR radiance above 1, on a near-black background, thenPMREMGenerator.fromScene. A bright uniform sky makes every reflection the same mean tone, and raisingenvMapIntensityonly lifts that mean. A canvas equirect capped at display white cannot make a window brighter than the paper. Cards used in the demo:card size, position linear radiance window, 4 x 3 panes 1.08 x 1.20 panes on 1.30 x 1.47 pitch, at (-5, 4.2, 4.8) 3.8, 3.55, 3.05 cool side card 3.0 x 5.4 at (6, 1.6, 2) 0.52, 0.68, 0.90 warm back card 2.0 x 4.6 at (2.5, 2.2, -5.5) 1.50, 1.22, 0.88 top card 7.5 x 2.2 at (0, 6, 0) 0.52, 0.55, 0.60 floor bounce 9 x 9 at (0, -4, 0) 0.18, 0.12, 0.07 background 0.045, 0.050, 0.060 -
Aim a card at the object. The eye sees the reflection of whatever lies along the mirror direction. The hero plate is turned to the bisector of eye and window (yaw -0.38, pitch -0.36 rad). Left face-on to a dark region, the same polished steel reads black.
-
Vary the roughness. Roughness is the finish, not the constant. Layer low-frequency smears (fingerprints), two angles of micro-scratches, and a fine grain into
roughnessFactor, and fade each below its pixel footprint withfw = max(length(dFdx(p)), length(dFdy(p))). Uniform roughness is what makes metal look rendered. -
Give it a bevel. Use a chamfered box (round the arrises 1 to 3 mm at object scale, 3 to 5 segments). A sharp 90 degree edge has no normals between faces, so it catches no highlight. Above 6 to 19 percent of the smallest side it reads as soap.
-
Ground it in darkness. Contact shadow blobs and a shadow-casting key light on a dark bench. Metal on a bright ground reflects the ground and loses contrast.
-
Tone map last. Render into a HalfFloat target, add the bloom, then ACES and the sRGB encode by hand. Keep
renderer.toneMapping = NoToneMappingwhile rendering to the target.
Constants
F0 goes in color as linear values (Color.setRGB(r,g,b, LinearSRGBColorSpace)), so you do not lose the tint to an sRGB round trip.
| metal | F0 linear | aged surface tint (uPatCol) |
|---|---|---|
| steel | 0.56, 0.57, 0.58 | rust 0.16, 0.055, 0.02 |
| brass | 0.89, 0.72, 0.36 | dark tarnish 0.09, 0.055, 0.02 |
| gold | 1.00, 0.71, 0.29 | dirty wear 0.13, 0.085, 0.03 |
| copper | 0.95, 0.64, 0.54 | verdigris 0.10, 0.34, 0.27 |
| finish | base roughness | envMapIntensity | extra |
|---|---|---|---|
| polished | 0.09 (0.045 to 0.6 after variation) | 1.0 | smear +0.11, scratch +0.24, grain +-0.025 |
| brushed | 0.26 (0.10 to 0.75) | 0.72 | MeshPhysicalMaterial.anisotropy 0.85, streaks 520 and 1500 cycles per unit along the grain |
| aged | 0.36 (0.10 to 0.95) | 0.85 | patina lowers metalness to 0.28, raises roughness +0.42, bump 0.9 |
Intensities fall as roughness rises because a rough finish spreads the same window over more of the surface and clips it. Kibori's own steel is envMapIntensity 0.94, roughness 0.16 to 0.5, F0 0xc9ced2; its baked floor room for a polished blade uses 3.0 (the map is dim, so the gain is high).
Bloom: threshold 5.0 in linear, clamp 1.0, gain 0.22, quarter resolution. Lower thresholds turn one hot lamp glint into an orange halo above the object.
Symptom-first gotchas
- Metal looks like grey plastic, and
envMapIntensitychanges nothing. On r163+material.envMapIntensityis ignored when the environment comes only fromscene.environment. Setmaterial.envMap = envTexturetoo, and re-bind it whenever the environment is rebuilt. Sweep it to an extreme and diff if you doubt it. - Reflection shows a soft cloud, no window bars. PMREM
fromScenerenders a 256 px cube and itssigmablurs. Kibori's 0.06 unit mullions and sigma 0.025 vanished. Widen the gaps to 0.22 units and use sigma 0.006. - Window reflection is a flat white block. ACES rolls linear 1.0 to about 204, so a window at 3.8 times F0 clips. Cut
envMapIntensityfor rougher finishes (table above) and let the panes read as luminous with bars, not as a paper rectangle. Measure>250percent on the object's own mask, not on the frame. - Polished blade reflects a black nothing. A face that looks up at a dark ceiling has nothing to show. Aim it at a card, or bake a floor room (Kibori's blade room: near black above 0.07 to 0.12, warm boards, five soft sun patches with kumiko bars, 1024 x 512 equirect, about 424 ms once) and give only that object the map.
- Rough metal goes flat and dark, not satin. Metal needs the roughness spread; a constant 0.4 with no variation is a matte grey. Add the layered terms above.
- Detail shimmers or disappears at distance. Every high-frequency term needs a footprint fade (
smoothstep(0.15..0.8, fw * frequency)), or it aliases to noise. The macro shot only resolves the 0.4 mm grain because the camera is close. - Anisotropy does nothing. It needs
MeshPhysicalMaterialand UVs; streak your roughness along the same axis asanisotropyRotation, or the highlight stretches across the streaks. - Aged patina looks like camouflage. One big fbm blob field. Multiply a large region mask, a fine fbm at about 70 cycles per unit and a dust term, and wear it off the bevels (
edgeKfrom the object-space normal:1 - max(|n.x|, |n.y|, |n.z|)). Drop metalness inside the patina or it stays mirror. - A custom shader program is shared between finishes. Set
customProgramCacheKeyper finish, oronBeforeCompilefor the second finish is never used. - GLSL inside a JS template with a
//comment. In a one-line string it comments out the rest of the shader and the mesh silently draws nothing. Use/* */.
Lifecycle and cost
- Reduced motion: no orbit, light parked at -0.3 (the window lands across the plate), one still render on every control change. Controls stay live.
- Pause on
document.hidden; reset the time base on resume; clampdtto 1/30 s; cap DPR at 2; size from aResizeObserver. - Measured on an Apple GPU through ANGLE Metal, 1440 x 900, 4x MSAA HalfFloat target: 1.1 ms per frame polished, 2.9 ms brushed, 3.1 ms aged at DPR 1; 9.4 and 13.0 ms at DPR 2. 13 shader programs and 9 textures alive after cycling all three finishes. The expensive part is the fragment noise on a full-screen-sized plate, not the environment. The cheap lever is DPR; the thing that does not matter is bloom at quarter resolution. Boot is about 1.4 s cold, of which the floor-room bake is 0.42 s.
- Contrast, not brightness, separates real from grey: the same plate measured mean luminance 174 under the studio and 169 under a flat grey room, but standard deviation 76 against 23.
Provenance
Extracted from KIBORI 木彫, a scroll-driven three.js woodworking film where a chisel blade, saw plate and plane iron had to hold up at macro range in a dark workshop. Ported unchanged: steelMat (forge, polish lines, lamination), buildEnvironment (with the two window changes above), chamferBox, the blade-room bake and its envMapIntensity 3.0, the crowned blade normals, the ACES pass. New in this skill: the finish layers, the metal library, the environment switch.
Self-score
Anchors: 0 flat colour, 3 PBR with a stock environment, 5 structured reflections but uniform roughness, 7 finish variation and bevels, 9 macro-legible with contact, 10 indistinguishable from a photograph.
| axis | score | evidence |
|---|---|---|
| reflections have structure | 8 | window bars, cool and warm cards visible on plate, ball and ring |
| surface texture at macro | 7 | scratches, grain and streaks resolve; no normal-mapped pitting on polished |
| edge and bevel highlights | 7 | chamfer arrises catch a continuous line |
| contact and darkness | 7 | shadow map plus blobs; no true AO |
| tone and no blown highlights | 7 | window clips on flat plate at some angles |
| overall | 7 | an honest 7; a photograph would still win on micro-pitting and dust |