Casa · render 3D scene
Get the render passes out of your 3D scene without opening a browser
- beauty
- depth
- normal
- lines
- ids
- mask
- info
If you have a 3D model of a building and you want a photo of it, prompting gives you a different building. Not a worse one - a different one. A facade with one extra storey, a balcony nobody modelled. In real estate that's not a style miss, it's misrepresentation, which is the entire reason this node pack exists.
CasaRender3D is the front door. It takes a named camera from the project's scene definition and renders five passes - beauty, depth, normals, a line map, and a material-ID map - plus a mask and a one-line info string, straight into the graph as IMAGE tensors.
What it actually does
No magic, and no GPU inference. The node shells out:
cmd = ["node", str(REPO / "scripts" / "export-passes.mjs"), camera] + (["--force"] if force_rerender else [])
The exporter drives the repo's Three.js scene through headless Chrome (Playwright, channel: "msedge", so it uses the Microsoft Edge you already have instead of downloading a browser), writes PNGs into out/passes/<camera>/, and the node reads them back as tensors. The mask comes from the ID map: anything that isn't material ID 0 is "the house".
The line pass is the interesting one, because it is not Canny on the beauty. It's built from G-buffer discontinuities - a material-ID change, a relative depth jump, a normal crease past 35° - supersampled 2×. Shadows, foliage and brick texture therefore can't produce a false edge, which is exactly the failure mode you get when you Canny a SketchUp screenshot and wonder why the ControlNet keeps inventing shutters. Cameras use a vertical lens shift rather than tilting, so verticals stay vertical.
Everything is content-hash cached: the key covers the scene spec, the camera, output size, lighting and the scene source. Re-run the graph and it says cache hit and costs you nothing.
Inputs and outputs that matter
Two inputs, both simple. camera is a dropdown built at load time from config/cameras.json - in the shipped project, street (facade, eye level, 1216×832), aerial and patio. force_rerender ignores the cache; use it after you edit scene/house.json, otherwise you'll stare at stale passes and blame the sampler.
Seven outputs. Wire beauty twice: into VAEEncode as the starting latent (the pack's master graph runs the KSampler at denoise 0.85) and into ControlNetApplyAdvanced as the control image. lines goes to the ControlNet - the master graph sets the union type to canny/lineart/anime_lineart/mlsd at strength 0.7, ending at 85% of the steps. mask goes to CasaQAGate, and ids is what you'd use for framing crops on the building later. info (resolution, hash, cache hit or render ms) is a plain string, so drop it on any text-display node.
Why the beauty pass matters so much: line-only conditioning held the facade rhythm and still invented a second storey with a balcony - edge recall 0.51–0.63, all three candidates rejected. Feeding the beauty in as the start latent pushed it to 0.79–0.88. Lines tell the model where edges are; they say nothing about volume.
Install
ComfyUI Manager → search From 3D to Campaign → install → restart. Or:
cd ComfyUI/custom_nodes
git clone https://github.com/meryboth/from-3d-to-campaign
cd from-3d-to-campaign
npm install
That npm install is not optional for this node - the exporter imports playwright-core and three. Keep it in mind after a Manager install too: the pack's .comfyignore excludes node_modules/, so if the headless renderer can't find a module, that's why.
The pack's requirements.txt is deliberately tiny (numpy, pillow, websocket-client) and pulls no torch. Models are never downloaded for you; for the render stage you want RealVisXL V5.0 fp16 in models/checkpoints, the SDXL-Lightning 8-step LoRA in models/loras and xinsir's ControlNet Union SDXL Promax in models/controlnet.
Where it bites
Load3D looks like the right tool and isn't. ComfyUI's native Load3D renders in the frontend viewport, so a queued or API-driven run produces nothing - the README says so plainly, and it's the reason this node exists. If you found CasaRender3D while trying to get a GLB into a batch run, you're in the right place.
Node isn't optional. No Node.js on PATH and the subprocess dies before it starts; the node re-raises the failure as pass exporter failed: plus the tail of stderr, so read that string before assuming the scene is broken.
Path layout matters. The node resolves everything relative to the repo root (config/, scene/, out/), which is why the registry install is a whole repo and not a .py file. Don't copy the node out on its own.
The 900-second subprocess timeout is a guard rail, not a target. First render with a cold cache is the slow one; on the pack's own 6 GB laptop GPU the whole graph - render, sample, gate, upscale - ran in 133 seconds.
Inputs (2)
| Name | Type | Default | Description |
|---|---|---|---|
| camera | COMBO | 3 options: street, aerial, patio | |
| force_rerender | BOOLEAN | false | — |
Outputs (7)
| Name | Type | Description |
|---|---|---|
| beauty | IMAGE | — |
| depth | IMAGE | — |
| normal | IMAGE | — |
| lines | IMAGE | — |
| ids | IMAGE | — |
| mask | MASK | — |
| info | STRING | — |