| Deferred shadows never render when MSAA is on, with zero console errors | Use shadowMode = .forward plus a .shadowOnly catcher. Debug any missing shadow by first giving the scene a visible gray lambert floor. |
The first frame of a fresh SCNView compiles Metal pipelines in the middle of your entrance animation | prepare([scene]) in the background, park the actor offstage, delay the first entrance. Never start an animation on frame one. |
fillMode = .forwards + isRemovedOnCompletion = false pins the presentation, and removal is per object | One central clearAnimations that sweeps the node, every child geometry, the lights, and running actions, called from every entry point. |
| Face-on real metal renders as a gray or black hole | A mirror viewed head-on reflects the environment zone behind the camera. Keep the approved painted base and add a thin additive layer with its own reflection map. |
Toggling castsShadow pops a blurred penumbra in one frame; shadowBias and the light's categoryBitMask are ignored for forward directional shadows | Keep castsShadow on permanently and animate shadowColor alpha, synchronized with the motion. |
| A fixed warm-up delay still hitches on cold devices and the Simulator; a particle effect's first frame compiles its own pipeline and drops exactly when it fires | Two-key ignition: gate the entrance on the minimum delay AND prepare's completion handler. Warm particle pipelines with a zero-opacity burst matching the real effect's flags. |
The default UIGraphicsImageRenderer format inherits screen scale, inflating every generated texture 9x in pixels - deadly for textures re-rendered live (per-keystroke engraving) | Pin the renderer format's scale to 1 and size the canvas to the actor's on-screen projection; coalesce multi-input retargets to one render per update pass. |
| Reproducing a real object's motion from photos yields confident rigs that fail sideways - each fix reveals a new wrong | Stills carry poses, not paths or mechanisms; end-pose fits do not determine the trajectory. Model the path: a calibration rig with direct pose controls, the owner authoring keyframes against the physical object. |
| A keyframed motion stutters rhythmically at its keyframes and survives every rendering and timing fix | The jerks are baked into the curve: the uniform Catmull-Rom basis on unevenly spaced keyframes steps velocity at every knot. Interpolate with span-weighted Hermite tangents (or a natural cubic) over distance-based phases, and gate on a numeric continuity check. |
| A sub-mesh cut from a larger model measures as if it were the whole object, with no error anywhere | The cut trimmed only the index buffer; the vertex buffer still holds every vertex of the original. Measure only vertices referenced by the submesh indices. |
A square image assigned to scene.lightingEnvironment is silently ignored - zero reflections, every mirror material renders black, no console output | Paint the environment map in a recognized cube-map layout, easiest a 2:1 spherical canvas (1024x512). Only material.reflective accepts a square sphere map. |
| A scene animated only by the shader clock draws one frame and freezes; two screenshots seconds apart are pixel-identical | The on-demand render loop cannot see shader time: set rendersContinuously = true, and verify motion with a pixel-diff, never by eye. |
| A speed dial on a shader-time pattern teleports the pattern when snapped - and tweening the dial makes the stream visibly race, or flow BACKWARD when slowing | Phase must be the integral of speed, never speed * absoluteTime: accumulate a clock in the renderer delegate, ease the speed toward its target, and let the clock only advance. |
| A translucent sheet waved by a geometry modifier prints a bright hairline along every fold silhouette; banded grazing fades either keep the razor or paint straight dark stripes | Modifiers move vertices, not normals: tilt the normal by the wave's analytic slope, then scale alpha by thickness compensation (1+k)*facing/(facing+k) - smooth, zero at tangency, face-on fog untouched. |
| Chrome lit by a hand-painted environment renders as cartoon metal: one flat paper-white highlight with a hard edge, reflections posterized into gray bands | The painted map's ceiling is 1.0 - there is no dynamic range to roll off. Use a photographic .hdr HDRI passed as a FILE URL (a UIImage re-encode silently clamps it back to LDR) plus wantsHDR on the camera. |
lightingEnvironment has no orientation control, and the panorama's frontal lamp prints one big blob dead ahead in the reflection | Rotate the CAMERA RIG instead: with a symmetric actor and radial floor the framing is identical, only the reflection layout moves. Build screen-space gestures rig-aware (lift axes through pointOfView). |
A full clearCoat on a white dielectric turns pearl into chrome with a white core; two finishes collapse into one look | clearCoat is a mirror layer. Pearl wants ~0.3-0.4 with roughness ~0.2 - gloss over cream, not silver. |
A tap on a shader-deformed actor misses exactly on the bulges - hitTest sees only the undisplaced mesh | Give the actor an oversized invisible collider (colorBufferWriteMask = []), hit-test with .all, filter by node name. |
| Frozen-clock snapshots that should be identical diff nonzero with no visible difference | Adaptive exposure renders the same instant differently depending on scene history: wantsExposureAdaptation = false, fix exposure by hand. |
| Particle debris slides forever or never comes to rest, and every friction tweak makes it worse | particleFriction is INVERTED from physical intuition: 1.0 slides freely, 0.0 sticks. A low value (~0.25) is what parks a grain after its last hop. |
| A surface a mechanic creates at runtime (a cut face, a toppled underside) renders near-black while the rest of the actor looks fine | Faces standing nearly parallel to the view axis graze off the key and the shadow sun. Give the scene a real ambient floor and judge lighting in EVERY orientation the mechanic can produce, not just the authored pose. |
| A stage is silently empty - no errors, no scene, nothing to debug | A narrowing init (Int32(...)) after 64-bit hash arithmetic traps at runtime, and inside an async task the crash is invisible. Do hash math in the target width via truncatingIfNeeded, and check the system crash reports before debugging scene logic. |