UltraTex Custom Rig (views list)
More than one extra ring of views, and what it costs
- prep
- prep
- normal_views
UltraTex paints an untextured mesh from a reference image, and it only knows six camera views: front, left, back, right, top, bottom. Six is fine until you care about a shoulder, a hat brim, or the underside of a skirt - then you're asking for views the model never learned to compose, and you get them from a second sampling pass with the rig turned 45°.
UltraTex Rotate Rig gives you one turned ring. UltraTex Custom Rig (views list) is for when one isn't enough: hand it absolute azimuths and elevations and it produces a stack of second passes - four views from 20° above plus four from 20° below, in one go.
Where this sits in the pipeline
Image-to-3D hands you the shape; the paint is the part that looks good, since the geometry underneath is triangle soup with machine-made UVs. That's the job UltraTex takes over once you have a .glb with UVs. This node is strictly a second-pass upward thing - it derives its cameras from a first pass, so it can't be an entry point in your graph.
How the four-per-group trick works
The model needs its six canonical tiles, but you want arbitrary angles. So for each group of four, the node finds the assignment of your angles to the four side slots (front / left / back / right) that minimises total angular distance, then turns each slot by the residual azimuth and tilts it by your elevation around that slot's horizontal axis. Each view renders at exactly the direction you asked for, upright, no roll.
Because the angles are absolute, order in the list doesn't matter - 45,135,225,315 and 315,225,135,45 give the same rig. And elevated views land in side slots: +60° doesn't hand the work to the top camera, it tilts a side camera by 60°, while the top and bottom tiles keep only the front slot's yaw. Off-spec for the model, which is why the pass starts from the re-projected first pass rather than noise. Each group's rig also reaches the bake, so texels stay weighted by how head-on your cameras see them.
The inputs and outputs that matter
Three fields, nothing optional:
- prep - the first pass's prep: the original UltraTex Prep output, not a rotated rig.
- azimuths - comma-separated degrees,
0= front,90= right side,180= back,270= left. A multiple of four values; each group of four is one second pass. Semicolons work as separators too. - elevations - one value per azimuth, or a single value applied to all. Positive is from above. Default
"20".
Both outputs are lists: prep gives one rig per group, normal_views the matching G-buffer renders - feed that to a Preview Image to confirm your cameras went where you meant. Downstream the lists fan out: Render Views and the second Sampler run once per rig, and UltraTex Bake Texture takes the whole list on prep_2 / albedo_views_2.
Wiring
UltraTex Prep ─ prep ─┬───────────── UltraTex Sampler (1st) ─ views ─┬─ Bake.albedo_views
│ │
├─ UltraTex Custom Rig ─ prep ─┬─ Render Views.prep
│ (azimuths, elevations) │ Render Views.source_prep ← first prep
│ │ Render Views.source_views ← Sampler 1 views
│ │ init_views, keep_mask ─┐
│ └─ Sampler 2.prep │
│ Sampler 2 (denoise 0.6) ←─┘
│ ─ views ─ Bake.albedo_views_2
│ Bake.prep_2 ← Custom Rig prep (list)
Don't shortcut Sampler 1's views into Sampler 2's init_views - they're seen from the other rig and the sampler refuses misaligned ones. Render Views bakes pass 1 and re-projects it onto your cameras, handing you a keep_mask that protects whatever pass 1 saw at least keep_above head-on (default 0.75, ~41°).
Install
ComfyUI Manager, search the pack title. Or:
cd ComfyUI/custom_nodes
git clone https://github.com/visualbruno/ComfyUI-UltraTex
# restart ComfyUI
requirements.txt is four packages - trimesh, moderngl, xatlas, scipy. No compiled 3D extensions, no CUDA build step: unusual here, and half the reason the pack is pleasant. moderngl does need a live OpenGL context for the G-buffer renderer, so headless servers are where it bites. Models either way: FLUX.2-Klein base 9B, the qwen_3_8b text encoder, flux2-vae, plus the UltraTex albedo LoRA and decoder.pt in models/ultratex. Triton is optional - the sampler falls back to ComfyUI's own attention.
Where people get burned
Time multiplies, linearly. Each group is a full second-pass run. At 2048 the README measures ~19 s/step, and pass 2 runs denoise × steps ≈ 11 of them - a couple of minutes per group before Render Views and the bake. Eight extra views is two groups, not one bigger pass. Prove the rig at 1024 first.
No bundled example uses this node. All four example JSONs skip it; you'll be hand-wiring from the diagram.
A high-denoise second pass won't agree with the first. That's what keep_mask and Bake's views_2_weight (default 0.6) are for - leave them default. If a ring comes out muddy anyway, views_2_use drops views at bake time.
The multiple-of-four error isn't a suggestion. Four side slots exist. Six extra angles means two groups and two repeats - and you pay for both.
Inputs (3)
| Name | Type | Default | Description |
|---|---|---|---|
| prep | ULTRATEX_PREP | — | |
| azimuths | STRING | 45,135,225,315 | Camera azimuths in degrees, comma separated: 0 = front, 90 = right side, 180 = back, 270 = left side. A multiple of 4 values; each group of 4 is one second pass. |
| elevations | STRING | 20 | Camera elevations in degrees (> 0 from above, < 0 from below), one per azimuth, or a single value for all. |
Outputs (2)
| Name | Type | Description |
|---|---|---|
| prep | ULTRATEX_PREP | — |
| normal_views | IMAGE | — |