Ir al contenido

3D preview pipeline

Esta página aún no está disponible en tu idioma.

Artist Alley generates several preview artifacts per 3D asset so the viewer has something to show immediately and the browse feed can hover-scrub a turntable. Rendering runs in-process — there is no separate render container to deploy.

The renderer is headless Chromium on software WebGL, driven by scripts/threejs/worker.mjs and orchestrated by the Go ModelHandler in app/internal/preview/model.go. No GPU is required.

flowchart LR
  upload[3D asset uploaded] --> probe["preview.3d: stage model + companions"]
  probe --> render["worker.mjs<br/>headless three.js"]
  render --> poster[poster.png]
  render --> frames["turntable/frame_0000..NNNN.png"]
  render --> views["views/top.png<br/>views/bottom.png"]
  poster --> col[col / preview / screen thumbnails]
  frames --> sheet["6×6 sprite sheet + WebVTT cues"]
  sheet --> hover[Browse-feed hover-scrub]

The pipeline previously used a headless Blender container. It was replaced in v0.6.0 and Blender was removed from the image entirely in v0.7.0, halving the image from 3.64 GB to 1.82 GB.

The replacement is roughly 20–30× faster, needs no GPU, and works on arm64 — the Blender path was amd64-only, so arm64 deployments had no 3D previews at all before v0.6.0.

It also uses the same three.js loaders as the interactive viewer, which is the property that matters most: the offline render and the in-browser view are meant to agree.

The worker writes a fixed on-disk layout that the Go side fans out into stored variants:

<output>/poster.png
<output>/turntable/frame_0000.png … frame_{N-1}.png
<output>/views/top.png
<output>/views/bottom.png
ArtifactBecomesNotes
poster.pngcol / preview / screen raster ladderThe thumbnail you see in the grid.
turntable/frame_NNNN.pngPer-frame variants36 frames by default, one every 10°.
Sprite sheetsprites.jpg + WebVTT cues6×6 grid of 160px cells — a 960×960 sheet. Same mechanism as video sprites, so the browse feed scrubs a turntable the way it scrubs a video.
views/top.png, views/bottom.pngReference viewsNot part of the scrub.

Each artifact is written only if missing, so a re-run after a partial failure resumes rather than re-rendering everything.

Rendered directly by the worker:

ExtensionLoader
glb, gltfGLTFLoader
fbxFBXLoader
objOBJLoader
stlSTLLoader
plyPLYLoader
daeColladaLoader

glb / gltf / fbx / obj are also what the interactive viewer loads. stl / ply / dae are render-only.

Converted first, then rendered: md2, md3, mdl, ms3d, mview are decoded by pure-Go importers that emit .glb, which then takes the normal path above.

Not currently rendered: blend, x3d, wrl, usd / usda / usdc / usdz, abc. These have no stock three.js loader. They upload and store fine — they just get no turntable until a converter ships as an optional plugin (#499).

The dispatch in render.html is the source of truth; the Go side keeps a mirrored copy, and a smoke test renders one fixture per supported format so the two cannot silently drift apart.

3D assets routinely carry siblings: .mtl next to .obj, .bin next to .gltf, separately-uploaded textures. The Go handler stages the model and its companions into the same working directory before rendering, so the loaders resolve them by relative URL with no operator configuration.

FlagDefaultNotes
--inputrequiredPath to the source model.
--workdirrequiredMust contain the model and its companions.
--outputrequiredDirectory root for the artifacts above.
--frames36Turntable frame count. 36 fills the 6×6 sprite grid exactly — changing it without changing the grid will not tile correctly.
--res512Square render resolution for frames and views.
--poster-res2048Poster resolution.

Worker concurrency is bounded by the job queue rather than by these flags; see the jobs configuration for preview.3d.