Automação de render
Disparar renders automaticamente, monitorar progresso, enviar notificação quando termina, limpar a cena do Houdini antes de renderizar — Python conecta tudo isso sem você precisar ficar olhando para a tela.
Renderizando o after via linha de comando
import subprocess
def render_ae(projeto, comp, saida): aerender = "/Applications/Adobe After Effects 2024/aerender"
cmd = [ aerender, "-project", projeto, "-comp", comp, "-output", saida, ]
print(f"Renderizando: {comp}") subprocess.run(cmd, check=True) print(f"Concluido: {saida}")
renders = [ ("/projetos/abertura.aep", "Main_1920x1080", "/renders/abertura.####.exr"), ("/projetos/abertura.aep", "Main_1080x1920", "/renders/abertura_v.####.exr"),]
for projeto, comp, saida in renders: render_ae(projeto, comp, saida)Monitorando frames renderizados
import timeimport globimport os
def monitorar_render(pasta, total_frames, intervalo=10): print(f"Monitorando: {pasta}")
while True: frames = glob.glob(os.path.join(pasta, "*.exr")) concluidos = len(frames) pct = (concluidos / total_frames) * 100
print(f"\r{concluidos}/{total_frames} frames ({pct:.1f}%)", end="")
if concluidos >= total_frames: print("\nRender concluido!") break
time.sleep(intervalo)
monitorar_render("/renders/cena01", total_frames=240)Notificação no Mac quando o render termina
import subprocess
def notificar(titulo, mensagem): script = f'display notification "{mensagem}" with title "{titulo}"' subprocess.run(["osascript", "-e", script])
notificar("Render Concluido", "cena01 finalizada, 240 frames")Pipeline completo: simular, renderizar, converter
import subprocess
pasta = "/projeto/cena01"
print("1. Simulando...")subprocess.run(["hython", f"{pasta}/scripts/simular.py"], check=True)
print("2. Renderizando...")subprocess.run(["hython", f"{pasta}/scripts/renderizar.py"], check=True)
print("3. Convertendo...")subprocess.run([ "ffmpeg", "-i", f"{pasta}/renders/frame.%04d.exr", "-c:v", "libx264", "-crf", "18", f"{pasta}/entrega/cena01_v01.mp4"], check=True)
print("Pipeline completo!")Scripts úteis no Houdini antes do render
Limpar nós não conectados
import hou
def clean_unconnected_nodes(context_path='/obj/geo1'): """Remove nós sem conexões no network especificado.""" network = hou.node(context_path) if not network: print(f"Network não encontrado: {context_path}") return
removed = 0 for node in network.children(): has_connections = any([node.inputs(), node.outputs()]) if not has_connections and node.type().name() not in ('output',): node.destroy() removed += 1
print(f"{removed} nós removidos.")
clean_unconnected_nodes()Cache automático de simulações
import houimport os
def cache_all_sims(output_dir='/tmp/cache'): os.makedirs(output_dir, exist_ok=True)
for node in hou.node('/obj').recursiveGlob('*'): if node.type().name() == 'filecache': current_path = node.parm('file').eval() node.parm('file').set( os.path.join(output_dir, os.path.basename(current_path)) )
print(f"Cacheando: {node.path()}") node.parm('execute').pressButton()
print("Cache completo.")
cache_all_sims('/renders/project_cache')Exportar câmera para JSON (para importar no after)
import hou, json
def export_camera_to_json(cam_path, output_path, frame_range=(1, 100)): cam = hou.node(cam_path) if not cam: print("Camera não encontrada.") return
data = {'frames': {}}
for frame in range(frame_range[0], frame_range[1] + 1): hou.setFrame(frame) pos = cam.worldTransform().extractTranslates() rot = cam.worldTransform().extractRotates()
data['frames'][frame] = { 'tx': pos[0], 'ty': pos[1], 'tz': pos[2], 'rx': rot[0], 'ry': rot[1], 'rz': rot[2], 'focal': cam.parm('focal').eval(), 'aperture': cam.parm('aperture').eval(), }
with open(output_path, 'w') as f: json.dump(data, f, indent=2)
print(f"Camera exportada: {output_path}")
export_camera_to_json('/obj/cam1', '/tmp/camera_data.json', (1, 120))Info
O JSON de câmera exportado do Houdini pode ser lido por um ExtendScript no after para recriar o movimento de câmera como nulos animados.