hermes-agent/skills/creative/touchdesigner-mcp/references/3d-scene.md
SHL0MS c3e3a9c184 feat(skills): add Tier A references — external-data, panel-ui, replicator, dat-scripting, 3d-scene
Five additional reference docs covering common TD use cases that were not yet
documented in any reference (operators.md lists the ops, but no usage patterns).

- external-data.md: webDAT, webclientDAT, webserverDAT, websocketDAT,
  mqttClientDAT, serialDAT, tcpipDAT — auth, polling, push, JSON parsing
- panel-ui.md: custom parameter pages, button/slider/field/list COMPs,
  containerCOMP layouts, panelExecuteDAT callbacks
- replicator.md: replicatorCOMP for data-driven cloning, per-row overrides,
  recreatemissing pattern, replicator vs Python loop
- dat-scripting.md: full Execute DAT family — chopExecuteDAT, datExecuteDAT,
  parameterExecuteDAT, panelExecuteDAT, opExecuteDAT, executeDAT lifecycle
- 3d-scene.md: light types, three-point rigs, shadows, IBL/cubemaps,
  PBR materials with idiom table, multi-camera, DOF

Same conventions as existing refs: code-first, verify param names with
td_get_par_info, no token-budget impact (load on demand).
2026-04-27 19:35:18 -07:00

10 KiB

3D Scene Reference

Lighting rigs, shadows, IBL/cubemaps, multi-camera, and PBR materials. For wireframe rendering and feedback TOPs see operator-tips.md. For instancing geometry see geometry-comp.md. For shader code see glsl.md.


Anatomy of a 3D Scene

[Geometry COMP]    ← contains SOPs (the shapes)
[Material]         ← Phong/PBR/GLSL/Constant MAT
[Light COMPs]      ← point/directional/spot/area/environment
[Camera COMP]      ← view position, FOV
        │
        ▼
   [Render TOP]    ← combines geo + lights + camera into a 2D image
        │
        ▼
   [post-FX chain] ← bloomTOP, glsl shaders, etc.
        │
        ▼
   [windowCOMP]    ← actual display

Render TOP is the heart. It takes an explicit geometry path, an explicit camera path, and lights via the lights table or an envlight reference.


Minimal Scene

# Geometry
geo = root.create(geometryCOMP, 'scene_geo')
sphere = geo.create(sphereSOP, 'shape')
sphere.par.rad = 1.0; sphere.par.rows = 64; sphere.par.cols = 64

# Material — start with PBR
mat = root.create(pbrMAT, 'mat')
mat.par.basecolorr = 0.7; mat.par.basecolorg = 0.7; mat.par.basecolorb = 0.7
mat.par.metallic = 0.0
mat.par.roughness = 0.4

geo.par.material = mat.path

# Camera
cam = root.create(cameraCOMP, 'cam1')
cam.par.tx = 0; cam.par.ty = 0; cam.par.tz = 4
cam.par.fov = 45
cam.par.near = 0.1; cam.par.far = 100

# Key light
key = root.create(lightCOMP, 'key_light')
key.par.lighttype = 'point'
key.par.tx = 3; key.par.ty = 3; key.par.tz = 3
key.par.dimmer = 1.5

# Render
render = root.create(renderTOP, 'render1')
render.par.outputresolution = 'custom'
render.par.resolutionw = 1920; render.par.resolutionh = 1080
render.par.camera = cam.path
render.par.geometry = geo.path
render.par.lights = key.path                 # single light path; for multi, see below
render.par.bgcolorr = 0; render.par.bgcolorg = 0; render.par.bgcolorb = 0

For multiple lights, leave par.lights blank — Render TOP scans the network for all lightCOMP and envlightCOMP ops by default. To restrict to specific lights, set par.lights = '/project1/key_light /project1/fill_light' (space-separated paths).


Light Types

Type What Common params
point Omnidirectional, falls off with distance dimmer, coneangle (n/a), attenuation
directional Parallel rays, infinite distance (sun) dimmer, light's rotation only matters
spot Cone, falls off with distance + angle coneangle, conedelta, dimmer
cone Like spot but harder edge same
area Rectangular soft light source sizex, sizey

For all: colorr, colorg, colorb, tx/ty/tz, rx/ry/rz, dimmer.

Three-Point Lighting (Studio Setup)

# Key — main light, ~45° front
key = root.create(lightCOMP, 'key')
key.par.lighttype = 'point'
key.par.tx = 4; key.par.ty = 3; key.par.tz = 4
key.par.dimmer = 1.5
key.par.colorr = 1.0; key.par.colorg = 0.95; key.par.colorb = 0.85

# Fill — softer, opposite side
fill = root.create(lightCOMP, 'fill')
fill.par.lighttype = 'area'
fill.par.tx = -4; fill.par.ty = 2; fill.par.tz = 3
fill.par.dimmer = 0.5
fill.par.colorr = 0.7; fill.par.colorg = 0.8; fill.par.colorb = 1.0
fill.par.sizex = 4; fill.par.sizey = 4

# Rim/back — outline from behind
rim = root.create(lightCOMP, 'rim')
rim.par.lighttype = 'spot'
rim.par.tx = 0; rim.par.ty = 4; rim.par.tz = -4
rim.par.coneangle = 30
rim.par.dimmer = 1.0

# Optional: ambient lift to prevent pure-black shadows
amb = root.create(ambientlightCOMP, 'ambient')
amb.par.dimmer = 0.15

Shadows

Spot and directional lights cast shadows when par.shadowtype != 'none'.

key.par.shadowtype = 'softshadow'        # 'none' | 'hardshadow' | 'softshadow'
key.par.shadowsize = 1024                # shadow map resolution
key.par.shadowsoftness = 0.02            # softshadow only

Tips:

  • Soft shadows are GPU-expensive. Start with shadowsize = 1024 and only go higher (2048/4096) if shadow edges look pixelated at your resolution.
  • Set the spot light's near/far to JUST contain the scene. Wider range = wasted shadow map precision.
  • Multiple shadow-casting lights compound cost. Limit to 1-2 in real-time work; pre-bake the rest into the materials.

Image-Based Lighting (IBL) / Environment Light

For realistic PBR materials you need a cubemap for reflections.

# Environment light from an HDR
env = root.create(envlightCOMP, 'env')
env.par.envmap = '/project1/cube_in'         # path to a TOP that produces a cubemap
env.par.envlightmap = ...                    # diffuse irradiance map (often same as envmap)
env.par.dimmer = 1.0

# Cubemap source — option A: built-in cubeTOP from 6 faces
cube = root.create(cubeTOP, 'cube_in')
# (assign 6 face TOPs)

# Option B: HDR equirectangular → cubemap conversion
# Use a moviefileinTOP loading .hdr or .exr, then projectTOP type='cubemapfromequirect'
hdr = root.create(moviefileinTOP, 'hdr_src')
hdr.par.file = '/path/to/environment.hdr'

proj = root.create(projectTOP, 'cube_proj')
proj.par.projecttype = 'cubemapfromequirect'
proj.inputConnectors[0].connect(hdr)

PBR materials sample the environment automatically when envlightCOMP is in the scene. Verify param names with td_get_par_info(op_type='envlightCOMP') — TD versions vary.


PBR Material Setup

mat = root.create(pbrMAT, 'pbr_metal')
mat.par.basecolorr = 0.95; mat.par.basecolorg = 0.65; mat.par.basecolorb = 0.4
mat.par.metallic = 1.0
mat.par.roughness = 0.25
mat.par.specularlevel = 0.5
mat.par.emitcolorr = 0; mat.par.emitcolorg = 0; mat.par.emitcolorb = 0

# Texture maps
mat.par.basecolormap = '/project1/textures/albedo'         # TOP path
mat.par.metallicroughnessmap = '/project1/textures/mr'      # G=roughness, B=metallic (glTF convention)
mat.par.normalmap = '/project1/textures/normal'
mat.par.emitmap = '/project1/textures/emit'
mat.par.occlusionmap = '/project1/textures/ao'

Material idioms:

Look metallic roughness basecolor
Brushed steel 1.0 0.4 (0.7, 0.7, 0.7)
Polished gold 1.0 0.1 (1.0, 0.85, 0.4)
Plastic 0.0 0.5 mid-saturated
Rubber 0.0 0.9 dark
Glass 0.0 0.05 (1, 1, 1), low alpha + transmission
Glowing emitter 0.0 1.0 dark, high emitcolor

For glass/transmission, recent TD versions support transmission in PBR; older versions need glslMAT.


Multi-Camera Setups

For comparison views, instant replay, multi-screen mapping, etc.

# Camera A — main scene
cam_a = root.create(cameraCOMP, 'cam_main')
cam_a.par.tz = 5

# Camera B — orbiting top-down
cam_b = root.create(cameraCOMP, 'cam_top')
cam_b.par.ty = 6; cam_b.par.rx = -90

# Render each via separate Render TOPs
render_a = root.create(renderTOP, 'render_main')
render_a.par.camera = cam_a.path
render_a.par.geometry = geo.path

render_b = root.create(renderTOP, 'render_top')
render_b.par.camera = cam_b.path
render_b.par.geometry = geo.path

Composite both with a multiplyTOP/compositeTOP for picture-in-picture, or route to separate windowCOMPs for multi-display.

Camera animation

Drive camera params via expressions (orbit), animationCOMP (waypoint), or LFO (oscillation):

# Orbiting camera
cam_a.par.tx.mode = ParMode.EXPRESSION
cam_a.par.tx.expr = "cos(absTime.seconds * 0.3) * 6"
cam_a.par.tz.mode = ParMode.EXPRESSION
cam_a.par.tz.expr = "sin(absTime.seconds * 0.3) * 6"
cam_a.par.lookat = '/project1/scene_geo'        # auto-aim at target

par.lookat is the simplest "always look at target" mechanism.

Depth of field

PBR + Render TOP supports DOF when par.dof = 'on'.

render.par.dof = 'on'
render.par.focusdistance = 5.0
render.par.aperture = 0.05         # blur strength
render.par.bokehshape = 'hexagon'

DOF is GPU-heavy. Render at lower res then upscale for performance.


Common Pitfalls

  1. Render TOP shows black — most common cause: no light. Even with PBR you need at least one lightCOMP or envlightCOMP. Add an ambientlightCOMP at low dimmer as a safety net.
  2. Material doesn't appeargeo.par.material must be a string PATH, not the material op itself. Use mat.path, not mat.
  3. Lights ignored — by default Render TOP picks up ALL lightCOMPs in the network. If you have leftover lights from another scene, they leak in. Set par.lights explicitly.
  4. PBR looks flat — without an envlightCOMP providing reflections, PBR materials look like Phong. Add one even if you don't have an HDR (use a constantTOP cubemap as fallback).
  5. Shadow acne / striping — increase par.shadowbias slightly. Tune per-light.
  6. Camera inside geometry — if cam.par.tz is INSIDE a sphere, you see the inside (or nothing if backface culled). Move the camera further out.
  7. Light range too small — point lights have implicit attenuation. Far-away geometry receives little light. Increase par.dimmer or move lights closer.
  8. Multiple cameras conflict — one render TOP = one camera. Don't try to share. Use multiple render TOPs.
  9. Wrong handedness — TD is right-handed Y-up. Imported assets from Z-up apps (Blender, Maya in Z-up) need a 90° X rotation on the geo COMP.
  10. Cooking budget — PBR + IBL + shadows + DOF at 1080p60 is fine on modern GPUs but 4K + 4 lights + soft shadows + DOF will tank. Profile via td_get_perf and downgrade settings before adding more.

Quick Recipes

Goal Recipe
Studio portrait 3-point rig (key + fill + rim) + ambient + PBR mat + DOF
Outdoor daylight One directional lightCOMP (sun) + envlight (sky HDR) + soft shadows
Dramatic / film noir Single spot light from upper side, hard shadows, deep ambient = 0.05
Abstract / dreamy Multiple area lights at low dimmer, no shadows, bloomTOP post
Product render Three-point + IBL + neutral PBR + bgcolorr=g=b=1 (white seamless)
Game-style Phong MAT + 1-2 lights + no IBL + flat ambient (cheap, stylized)
Wireframe + solid Two render TOPs (one with wireframeMAT, one with PBR), composite via addTOP
Orbiting camera par.lookat + expressions on tx/tz using sin/cos