Nodes/From 3D to Campaign/Casa · render 3D scene
ComfyUI Node

Casa · render 3D scene

Get the render passes out of your 3D scene without opening a browser

By meryboth·Created 25 days ago·Updated 25 days ago· 0
Casa · render 3D scene
    • beauty
    • depth
    • normal
    • lines
    • ids
    • mask
    • info
    ◄camera▾►
    ◄force_rerenderfalse►

    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.

    Categoryfrom 3d to campaign

    Inputs (2)

    NameTypeDefaultDescription
    cameraCOMBO3 options: street, aerial, patio
    force_rerenderBOOLEANfalse—

    Outputs (7)

    NameTypeDescription
    beautyIMAGE—
    depthIMAGE—
    normalIMAGE—
    linesIMAGE—
    idsIMAGE—
    maskMASK—
    infoSTRING—