TouchDesigner 2025 · LEDMatrixSystem.toe · 2026-08-26

ขยายสเกล & คุมกล้องสไลซ์

จาก LED 6,912 ดวงที่ผูกกล้องไว้ตายตัว 12 ตัว → เป็น 248,832 ดวง ที่สร้างชั้นเองอัตโนมัติ หมุน/ซูม/เลื่อนมุมที่ content เข้าไปในก้อน LED ได้ และวางก้อนไว้กลาง origin

grid 72×72×48 248,832 LED texture 5,184×48 render 0.09 ms ชั้น 72
1 ปัญหาเดิม — rig ที่ผูกไว้ตายตัว

ระบบสไลซ์ต้องมีของ 3 อย่างต่อ 1 ชั้น: กล้อง ortho 1 ตัว (ตั้ง near/far ให้เห็นเฉพาะแผ่นบางของตัวเอง), renderpass 1 ตัว, และ out 1 ตัว  → เดิมวางมือไว้ 12 ชุด แปลว่าสเกลได้แค่ 12 ชั้นเท่านั้น ลากสไลเดอร์ดูว่าถ้าจะไป 144 โมดูลจริงต้องมีของกี่ชิ้น

🖱️ ลากเพื่อหมุนดู · แต่ละแผ่น = 1 ชั้นที่กล้อง 1 ตัวถ่ายได้
ชั้น (Layers)
12
กว้าง/ชั้น (Slicew)
12
LED ทั้งหมด
6,912
texture
144×48
node ที่ต้องมี
36
ถ้าวางมือ (2 นาที/ชิ้น)
1.2 ชม.
ทางออก: ใส่ตรรกะสร้าง rig ไว้ในไฟล์ .toe เอง — slices_build (textDAT) + slices_exec (parameterexecuteDAT) → เปลี่ยน Modz แล้วกด Rebuildslices ระบบสร้าง/ลบ/ต่อสายให้ครบเอง ทั้งตอนขยายและตอนย่อ
🎥
1. กล้อง
copy จาก item1 เข้า camRig
near/far ผูกกับ Layers
🔗
2. renderpass
ต่อ chain จาก render2U
camera = ../camRig/itemK
📤
3. out
outK ← renderpass(N+1−K)
ล็อก nodeY = ลำดับ connector
🧩
4. layout
รื้อสายเก่าทั้งหมด
ต่อ input i ← out(N−i)
2 คุมมุมกล้องสไลซ์ด้วย camRig

กล้องทั้ง N ตัวถูกทำให้เป็นลูกของ null ตัวเดียว (camRig) → ขยับ/หมุน rig ตัวเดียว กล้องตามทั้งชุดโดยที่ระยะ slab ยังเรียงถูก ซ้าย = มองจากด้านบน เห็น scene กับ "กล่องที่กล้องจับ" · ขวา = สิ่งที่ไปโผล่ในก้อน LED

มุมบน (แกน X-Z) · กรอบฟ้า = พื้นที่ที่กล้องสไลซ์จับได้
ก้อน LED มองจากด้านบน · เม็ดที่ติด = จุดที่ตกอยู่ในกรอบ
ortho ไม่มี "ถอยกล้อง" — กล้อง ortho ระยะห่างไม่มีผลกับขนาดภาพเลย สิ่งที่ทำหน้าที่ถอย/เข้าใกล้คือ Zoom ซึ่งคุม orthowidth + near/far + tz พร้อมกัน (ถ้าคุมแค่ orthowidth แกนลึกจะไม่ย่อตาม → สัดส่วนเพี้ยน)
near = (me.digits-1) * (Zoom / Layers)      far = (me.digits) * (Zoom / Layers)
tz   = Zoom * 0.5                          orthowidth = Zoom
camRig:  tx = Panx   ty = Pany   tz = Depth   rx/ry/rz = Rotx/Roty/Rotz
3 กับดักสัดส่วน — จอดำสนิทโดยไม่มี error

กล้อง ortho กว้าง 1 หน่วยเสมอ แต่ความสูงที่เห็น = resh / resw พอสไลซ์กว้างขึ้น (โมดูลเยอะขึ้น) กรอบแนวตั้งแคบลง → scene ที่วางไว้กับ volume ทรงหอสูงจะหลุดกรอบทั้งหมด ผลคือ renderpass ดำหมดทุกชั้น และ TouchDesigner ไม่ฟ้อง error เลย

มุมด้านหน้า (แกน X-Y) · กรอบฟ้า = สิ่งที่กล้องเห็น · รูปเขียว = scene
1 สไลซ์ (px)
12 × 48
กรอบที่เห็น
1 × 4.00
scene สูง
4.00
ผล
พอดี
ตอนนี้พอดี — scene สูง 4 หน่วย กรอบสูง 4 หน่วย
วิธีไล่จับ: ไม่มี error ให้ดู → ต้อง sample ค่า max ของ TOP ทีละสเตปในสาย (op.numpyArray(delayed=False).max()) แล้วเทียบกับ container ที่ทำงานได้ วันนี้จับได้ว่า renderpass = 0 ทุกตัวทั้งที่ POP มีจุด 800 จุด → ปัญหาอยู่ที่กรอบกล้อง ไม่ใช่ที่ content
4 จัดก้อน LED ให้อยู่กลาง origin
🖱️ ลากเพื่อหมุน · เส้นไขว้ = origin (จุดที่กล้อง preview หมุนรอบ)

posTOP เขียนตำแหน่งเริ่มที่ (ix+0.5)·pitch → ก้อน LED กิน 0 → 4.5 ทั้งแกน X และ Z แปลว่า มุมกล่องอยู่ที่ origin ไม่ใช่จุดกึ่งกลาง กล้อง preview ที่ orbit รอบ origin เลยเห็นก้อนเยื้องตลอด

x -= float(NX)    * 0.5 * uPitch.x;   // NX = pixel ต่อชั้น
y -= float(H - 1) * 0.5 * uPitch.y;   // H  = Height
z -= float(NZ)    * 0.5 * uPitch.x;   // NZ = จำนวนชั้น
X / Z
0.031 → 4.469
Y
0 → 2.999
ทำไม Y ใช้ H−1: แถวล่างสุดอยู่ที่ 0·pitch พอดี (ไม่มี +0.5 เหมือน X/Z) ถ้าใช้ H จะเยื้องลงครึ่งเม็ด · ไม่กระทบ Art-Net เพราะ pixel mapping อ้างจาก index ไม่ใช่พิกัด
5 ผลวัดจริง — คอขวดย้ายที่แล้ว

ทุกตัวเลขวัดด้วย wall-clock (force cook ซ้ำ 5–20 รอบ) ไม่ใช่ cookTime ซึ่งค้างค่าตอน compile ครั้งแรก

งาน2×2 (12 ชั้น)12×12 (72 ชั้น)เพิ่มขึ้น
LED6,912248,83236×
เรนเดอร์ LED @4K—0.093 msแทบไม่ขยับ
content: POP scene (สไลซ์)2.95 ms20.6 ms7×
content: GLSL generator—0.67 ms—
สรุป: จำนวน LED ไม่ใช่ปัญหา (ไม่มี GPU→CPU readback แล้ว) — ตัวที่แพงคือ จำนวนชั้น เพราะ scene ถูก submit ใหม่ทุก renderpass (~0.29 ms/ชั้น) ถ้าต้องการ 60fps ที่ 72 ชั้น ต้องลดชั้น หรือใช้ content ที่วาดจากพิกัด pixel ตรงๆ (Contentsrc = glsl)
6 Gotcha ที่เจ็บมาแล้ว
💥
.eval() บน OP-path param ลาก cook ทั้งสาย
render.par.camera.eval() ทำ bridge timeout 10 วิ และ WebSocket DAT หลุด → อ่านด้วย .val เสมอ
📐
ลำดับ output connector ของ COMP เรียงตาม nodeY ไม่ใช่ชื่อ
สร้าง out ใหม่ต้องวาง Y ต่อเนื่อง ไม่งั้น layout ต่อสลับชั้นแบบเงียบๆ
🎯
parameterexecuteDAT.par.op = '.' คือตัว DAT เอง
ต้องใช้ '..' ถึงจะเป็น COMP ที่มันอยู่ — ผิดตรงนี้ callback เงียบสนิท ไม่มี error
🕰️
par ที่เป็น expression ไม่ trigger onValueChange
เปลี่ยน Modz แล้ว Layers ขยับตาม แต่ callback ไม่ fire → ต้องมี pulse ให้กดเอง
🧊
constantCHOP / glslTOP ที่ static ค้างค่าเก่า
ref['layers'] ยัง 12 ทั้งที่ Layers = 72 จนกว่าจะ cook(force=True)
🔗
copy COMP แล้ว absolute path ไม่ถูก rewrite
6 จุด: geo1.instanceop / instancecolorop / material · render1.camera / geometry / lights ยังชี้ container เดิม
🌑
248k เม็ดทึบ = ก้อนดำ
ต้องใช้ additive: SimMat blending=On · srcblend='sa' · destblend='one' · depthwriting=Off + geo1.instancea='a'
🎬
เทียบภาพ scene ที่แอนิเมทไม่ได้
ตั้ง me.time.play = 0 ก่อนทดสอบ ไม่งั้นแยกไม่ออกว่าภาพเปลี่ยนเพราะพารามิเตอร์หรือเพราะเวลา
7 สร้างใหม่ได้จากสคริปต์
python td.py script ../ClaudeControl/build/01_baseMatrix2x2GPU.py    # GPU version 2×2
python td.py script ../ClaudeControl/build/03_slices_auto.py         # auto slice rig + camRig (ทั้ง 2 container)
python td.py script ../ClaudeControl/build/02_baseMatrix12x12GPU.py  # 12×12 = 248,832 LED
ทุกสคริปต์ idempotent — รันซ้ำได้ · ตรรกะ rebuild ถูกฝังไว้ในไฟล์ .toe แล้ว (slices_build) จึงทำงานต่อได้แม้ไม่มี bridge