# REPORT — 05 · 3D 渲染系 「绵朵 · 云朵软糖」

A 10-second C4D/Octane-style pastel product ident, rendered in real 3D.

---

## 1. Concept, in two sentences

A soft-pastel candy brand, 「绵朵 MIANDUO」, presents its cloud gummy as a living object:
a MoGraph cloner field of glossy pastel pellets ripples under a soft studio key, an inflated
jelly cushion falls into it, squashes on contact and punches a shockwave through the array —
and the film lands on a poster frame of the product inside its own chrome halo.
The pleasure is entirely tactile: everything in the frame is soft, glossy and slightly
out of focus except the thing you are meant to want.

**Brand / copy (all invented):** 「绵朵 / MIANDUO」 · product 「云朵软糖」 ·
tagline 「一口落在云上」. No real trademark appears.

---

## 2. Beat sheet

| time | beat | what happens |
|---|---|---|
| 0.00–0.55 | **唤醒 (hook)** | We open *inside* the cloner field — never on an empty frame. 667 pellets are already blooming outward from the foreground, each scaling in on its own 2–4 frame stagger; a plain-effector wave is already travelling; the camera rakes low through the rows at 33.5° with a 3.0-unit focal band, so the near pellets are enormous and the far ones melt into bokeh. |
| 0.55–3.30 | **铺陈 (escalation)** | The camera lifts and pulls back on a long-tail ease (80 % of the travel happens in the first 20 % of the move) and reveals the full 23 × 29 array. The wave keeps rolling; warm mallet notes ride the crests. The hero cloud-gummy drops into frame at 2.2 s, still high, and hangs — visible anticipation. |
| 3.30–3.56 | **落下 (anticipation → plunge)** | A quart ease-in plunge. The camera punches in slightly; a riser and a whoosh resolve into silence. |
| **3.56** | **第一次落地 (impact 1)** | The cushion squashes to 66 % of its height (volume-preserving: 1.24× wider), a radial shock ring throws the surrounding pellets up, outward and tumbling, the camera takes a damped shake, the FOV kicks 1°, and the soundtrack lands a soft thud + squish + sub **on the exact frame**. |
| 3.56–6.18 | **回弹 (rebound / calm)** | Damped-spring bounces (period 0.34 s, λ 5.4) with the squash oscillating through the rebounds; the jelly keeps rippling via a vertex-shader modal wobble; the field settles into a gently rippling crater; the camera arcs around the product. This is the film's quiet valley. |
| 6.18–6.60 | **蓄力 (second anticipation)** | The cushion leaps, hangs, then slams. Meanwhile a negative MoGraph effector sucks the whole field radially inward — the array visibly *compresses* before it is thrown. |
| **6.60** | **HERO MOMENT (66 % of runtime)** | The slam. A 2.05-strength shockwave explodes the cloner field outward; pellets are lifted, pushed, stretched and tumbled; the chrome halo snaps out of the cushion and grows to 2.6 units; bloom flashes to 0.78; a 1.9° FOV punch and a shake hit the frame; the riser resolves into impact + sub + squish + a chrome ting, with sparkle pops decaying for the next 1.3 s. This is the loudest, busiest half-second of the film (43 % of pixels moved per frame pair, see §5). |
| 6.60–8.30 | **落定 (settle)** | The halo keeps expanding; sparks decay; the array relaxes back into its grid; the camera eases to the final composition on a long-tail quint. |
| 7.55–8.52 | **字标 (wordmark)** | 「绵朵」 assembles glyph by glyph with a 2-frame stagger, a 34 px rise and a tracking settle (26 px → 12 px); then `M I A N D U O`, then a hairline that grows out of the centre, then the tagline. |
| 8.30–10.00 | **定格 (composed end hold, 1.4 s)** | The calmest stretch of the film — and the ground-truth poster frame. Kept alive by a rolling field breath, the cushion's slow rotation and 1.6 % scale breath, a two-frequency camera drift, and the background bokeh orbs' continuous drift. |

The single cue sheet lives at the top of `parts/scene.mjs` (`HIT1 = 3.56`, `HIT2 = 6.60`,
`WORD = 7.55`, …) and is repeated verbatim in `audio.py`; picture and sound read the same table.

---

## 3. Signature features — where and how

| signature feature | how it is realised |
|---|---|
| **PBR 真实材质 (PBR materials)** | `MeshPhysicalMaterial` throughout: the pellets are clearcoated candy (roughness 0.26, clearcoat 0.85, clearcoatRoughness 0.10); the hero cushion is a high-clearcoat jelly with a sheen lobe; the accent ring is metalness 1.0 / roughness 0.11 chrome. Reflections come from a real PMREM environment map (below), not from a flat ambient term. |
| **柔和棚拍布光 + HDRI (soft studio light)** | The "HDRI" is generated in-page: a vertical gradient dome plus four emissive soft-box slabs (overhead key, warm side fill, cool rim, back bounce) are rendered through `PMREMGenerator` with a 0.075 blur, giving broad, soft speculars. On top of that sits a real shadow-casting key from the back-left (so the contact shadows fall *toward* the camera and actually read), a warm front fill and a cool rim. |
| **克隆器阵列 (cloner array)** | A 23 × 29 `InstancedMesh` (667 capsules) with per-instance colour. Colour is not confetti: it is a smooth value-noise field mapped onto a weighted pastel ramp (cream/peach/pink dominate; lilac and mint are rare accents) so the array reads as a designed gradient. |
| **MoGraph Plain Effector 群体波浪** | Every frame each instance's matrix is recomputed in closed form from *t*: a travelling wave `sin(r·0.88 − t·rate)`, a slower second harmonic, a per-pellet phase offset, and a scale-in stagger. Amplitude and rate are themselves keyframed (1.0 → 1.72 after the first landing → 2.15 through the hero moment → 0.84 in the hold). |
| **软体碰撞 / squash & stretch** | Both landings squash the cushion with a volume-preserving damped spring (`sy = 1 − A·e^(−λτ)·cos(ωτ)`, `sxz = 1/√sy`), giving 66 % and 53 % height at the two impacts with two or three decaying rebounds. On top of that, a vertex-shader modal wobble (5-lobe + 3-lobe angular modes × a vertical band) keeps the jelly rippling — and the *same* injection is applied to the hero's `customDepthMaterial`, so the cast shadow deforms with it. |
| **糖果色软胶质感 (SSS-ish gummy)** | High clearcoat + a broad sheen lobe + the soft PMREM reflections give the gummy read; a very low emissive tint (`#a81f4a` at 0.06) stands in for subsurface glow. See §6 — this is a *fake*, not true SSS. |
| **浅景深 f/1.8 (shallow DOF)** | A hand-written single-pass DOF: 28 golden-angle taps with a circle of confusion derived from a linearised 24-bit depth buffer, weighted slightly toward bright taps so speculars read as bokeh. The radius is keyframed (10.5 px at the raking open, 7 px mid-film, 6 px on the poster frame) and the focal plane racks with the camera. |
| **长尾 ease (long-tail eases)** | The camera moves are `easeOutQuint`/`easeOutQuart` — the opening pull-back, the orbit and the final settle all complete the great majority of their travel early and creep into place. |
| **24 fps + motion blur** | 24 fps, and every moving frame is rendered as **8 stratified-jitter sub-samples across a 0.46-frame shutter** and averaged (van der Corput jitter, so no combing on fast high-contrast edges). The shutter amount is itself a keyframed curve: wide through the opening rake, narrow during the long-tail pull-back, wide on both landings and through the hero moment, closed to zero in the end hold. |
| **产品为轴心，粒子围绕 (product as axis)** | The field, the halo, the shockwave and the drifting bokeh orbs are all choreographed around the cushion; the camera never loses it after 2.2 s. |

---

## 4. Technical route

**WebGL2 / three.js r180, hand-written forward renderer + hand-written post stack.**
The page draws any *t* on demand; the harness screenshots each frame in headless Chrome and
muxes with ffmpeg. Chrome reports `ANGLE (Apple, ANGLE Metal Renderer: Apple M3 Pro)` —
the film is GPU-rendered, not software-rasterised.

- `parts/scene.mjs` — scene construction, materials, the cue sheet, the camera tracks, the
  per-frame closed-form animation, the render-target management and the 2D type/grain overlay.
- `parts/post.mjs` — the post stack, written from scratch: `accumulate → DOF → bright pass →
  separable Gaussian bloom → ACES tonemap + sRGB encode + vignette + 1-LSB dither`.
  The renderer is kept in linear space (`outputColorSpace = LinearSRGBColorSpace`,
  `NoToneMapping`) and tonemaps only in the final composite, into half-float targets.
- Geometry: the cloner pellets are stock `CapsuleGeometry`; the hero cushion is a
  **procedurally generated superellipsoid** (`parts/scene.mjs :: superellipsoid()`) so its
  puffy, bevelled form is authored, not downloaded.
- Type: a second, transparent 2D canvas layered over the WebGL canvas. This keeps the
  wordmark crisp and pixel-perfect (real kerning, real tracking animation, real CJK) instead
  of blurring it through the DOF pass. Film grain is a coarse (4 px cell) seeded tile,
  drifting slowly rather than re-randomised per frame — which is why the file stays at
  13.8 Mbps instead of ballooning.
- Determinism: no `Math.random`, no `Date.now`, no CSS animation, no rAF-driven state.
  `mulberry32` and value noise only; every frame — including the motion-blur sub-frames —
  is a pure function of *t*. Audio is seeded Python and bit-identical per run.

**Why not Blender?** Cycles would have given true SSS and true path-traced motion blur, but
240 frames of Cycles-with-denoise on a machine shared with a dozen other render jobs would
have made the iteration loop (which is where the quality actually comes from) impossibly
slow. The WebGL route renders the whole film in ~2 minutes, which is what paid for the
four look-development passes and the three motion-blur iterations.

---

## 5. Check numbers (from `out/05-3d-render/qa.json`)

| requirement | measured |
|---|---|
| resolution | 1920 × 1080 ✓ |
| frame rate | 24.000 fps ✓ |
| duration | 10.000 s, 240 frames ✓ |
| video codec | H.264 (libx264), yuv420p ✓ |
| bitrate | **13.8 Mbps** (17.3 MB) — inside the 8–25 Mbps band ✓ |
| audio | AAC 48 kHz stereo, 192 kbps ✓ |
| integrated loudness | **−13.6 LUFS** (target −14 ± 2) ✓ |
| true peak | **−3.4 dBTP** (≤ −1.5 required) ✓ |
| black frames | 0 ✓ |
| frozen / stuck frames | 0 ✓ |
| console errors | 0 ✓ |
| per-frame cost | 0.51–0.54 s wall clock including Chrome's two-rAF presentation wait, the PNG encode and the pipe; the page's own draw is a small fraction of that, well inside the ~250 ms budget for the draw itself ✓ |
| rhythm: dead spans | **none** ✓ |
| rhythm: peak energy | **43.1 %** of pixels moved per frame pair at 6.5–7.04 s — the hero moment, exactly where it should be ✓ |
| rhythm: end hold | decrescendo 10.9 % → 3.6 % → 3.2 % → 2.0 % → **0.90 %** across 7.5–10.0 s; the hold is the calmest window and still well above the 0.25 % dead threshold ✓ |

**Sync check (measured, not asserted):** the RMS envelope of `audio.wav` jumps from 0.151 to
0.404 in the 20 ms bin starting at 3.56 s, and from 0.131 to 0.471 in the bin starting at
6.60 s. Both picture hits and both audio transients land on the same frame.

---

## 6. Honest self-critique

### Top three weaknesses

1. **No true subsurface scattering, and no genuine 3D wordmark.** The gummy read leans on
   clearcoat, sheen and a slight emissive tint rather than a real BSSRDF, so the cushions are
   *glossy* where a real Octane candy shot would be *translucent* — you never see light pass
   through them. Relatedly, the brand mark is a crisp 2D overlay rather than extruded,
   inflated 3D type. Extruding CJK would have needed a typeface asset and a font parser
   (three.js' `FontLoader` is an addon that was not vendored), so I chose a clean 2D lockup.
   A jury that reads "inflated 3D wordmark" literally will dock this.
2. **The field's physics is choreographed, not simulated.** The cloner wave, the shock rings
   and the pull-in are authored closed-form functions. There is no pellet-pellet collision
   and no real rigid-body solver, so on a close look the array behaves like a very good
   effector rig rather than like a simulation. In one place this is visible: pellets pass
   through each other during the hero-moment burst.
3. **A bright, narrow tonal range, and a very calm mid-film dip.** The high-key pastel studio
   is correct for the style, but it means the frame lives between roughly 55 % and 90 %
   luminance, and the pellets' specular tops occasionally clip to white. The 4.5–6.0 s
   stretch is deliberately the quiet valley of the film (7.2 % moved) but on a small screen
   it can read as flat rather than as restraint.

Two smaller things I would fix next: the motion blur is 8 discrete samples, so on the single
fastest frame the cushion's silhouette retains a trace of ghosting; and the DOF is a
single-pass approximation, so out-of-focus highlights are smooth discs rather than true
polygonal bokeh.

### What I would do with more time

Path-trace the hero shots in Blender for real SSS and true bokeh, keep the WebGL build for
the wider array shots; add a real rigid-body pass for the hero-moment burst; and build a
proper extruded/inflated 3D wordmark from hand-authored letter outlines.

### Scores

| dimension | score | note |
|---|---|---|
| style fidelity | **8.5** | Unmistakably C4D/Octane pastel-ident territory: cloner + plain effector, candy materials, soft studio light, shallow DOF, long-tail camera ease, 24 fps motion blur. Docked for the missing true SSS and the 2D wordmark. |
| concept wow | **7.5** | A coherent, genuinely tactile product ident with a real narrative arc and an ASMR-correct sound design — but it is the canonical story for this style, not a surprising idea. |
| motion craft | **8.5** | Long-tail camera eases, three-phase entrances, volume-preserving squash & stretch on a damped spring, tumble and stretch on the shockwave, staggered pop-in, jittered motion blur, transitions carried by the elements themselves (no crossfade anywhere). Docked for designed-instead-of-simulated field physics. |
| design & typography | **8.5** | Clear hierarchy (wordmark → latin → rule → tagline), correct CJK punctuation, controlled pastel palette with rare accents, generous negative space, ~5 % safe margins, nothing clipped. |
| finish & texture | **8.0** | Dithered gradients (no banding), coarse drifting grain rather than per-frame noise (no boiling), no jitter, no popping. Docked for occasional clipped speculars and residual ghosting on the fastest frame. |
| sound & sync | **8.5** | Genre-correct ASMR-pastel palette (marimba mallets, bubbly pops, soft low thuds, squish, airy Eb-major pads) with every hit landing on the exact frame, verified numerically. The music is well-made but functional; it is not a melody you would hum. |
| technical | **9.0** | Exact spec (1920×1080, 24 fps, 10.000 s), fully deterministic and re-render-reproducible, console-clean, 13.8 Mbps, zero black or frozen frames, zero dead spans. |

**Overall: ≈ 8.4 / 10.** Strong high-end-agency territory; the shortest path to a 9 would be
real subsurface scattering and a genuinely extruded 3D wordmark.

---

## 7. Files

```
demos/05-3d-render/
  index.html          two stacked canvases (WebGL + 2D type/grain overlay)
  demo.json           1920x1080, 24 fps, 10 s, audio, posterT 9.3
  audio.py            the whole soundtrack, synthesised from shared/audio.py primitives
  parts/scene.mjs     scene, materials, cue sheet, camera tracks, animation, overlay
  parts/post.mjs      hand-written motion blur / DOF / bloom / tonemap composite
  parts/three.module.js, parts/three.core.js    three.js r180 (MIT), vendored
  CREDITS.md          every asset and its licence
  REPORT.md           this file
out/05-3d-render/
  video.mp4           the deliverable
  poster.jpg          frame at t = 9.3 s
  qa.json, rhythm.json, stills/
```
