refactor: cinematic render — linear keyframes, 3 segments, 250 frames, white bg
Changes per user feedback: - Keyframe interpolation: BEZIER → LINEAR (all fcurves set to LINEAR) - Removed segment 4 (closeup) — now 3 segments only - Frame count: 480 → 250 (10 seconds at 25fps) - FPS: 24 → 25 - Easing removed — pure linear interpolation between segment params - White background by default (World node Color = white) - Transparent bg still available as override Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -563,8 +563,8 @@ def render_cinematic_to_file(
|
||||
import time
|
||||
|
||||
# Cinematic parameters are fixed
|
||||
frame_count = 480
|
||||
fps = 24
|
||||
frame_count = 250
|
||||
fps = 25
|
||||
|
||||
blender_bin = find_blender()
|
||||
if not blender_bin:
|
||||
|
||||
@@ -249,14 +249,11 @@ def _apply_material_library(parts, mat_lib_path, mat_map, part_names_ordered=Non
|
||||
|
||||
# ── Cinematic camera animation ───────────────────────────────────────────────
|
||||
|
||||
TOTAL_FRAMES = 480
|
||||
SEGMENT_LENGTH = 120 # frames per segment
|
||||
TOTAL_FRAMES = 250
|
||||
NUM_SEGMENTS = 3
|
||||
SEGMENT_LENGTH = TOTAL_FRAMES // NUM_SEGMENTS # ~83 frames per segment
|
||||
|
||||
# Segment definitions: (start_azimuth_offset, end_azimuth_offset,
|
||||
# start_elevation, end_elevation,
|
||||
# start_dist_factor, end_dist_factor,
|
||||
# start_lens, end_lens,
|
||||
# use_dof)
|
||||
# 3 segments (no closeup): establishing, detail sweep, crane up
|
||||
SEGMENTS = [
|
||||
# Segment 1: Establishing shot — orbit 45deg, push in, 50mm
|
||||
{
|
||||
@@ -282,25 +279,9 @@ SEGMENTS = [
|
||||
"lens_start": 35.0, "lens_end": 35.0,
|
||||
"dof": False,
|
||||
},
|
||||
# Segment 4: Hero close — push in, settle down
|
||||
{
|
||||
"az_start": 120.0, "az_end": 120.0,
|
||||
"el_start": 35.0, "el_end": 25.0,
|
||||
"dist_start": 3.0, "dist_end": 2.2,
|
||||
"lens_start": 65.0, "lens_end": 65.0,
|
||||
"dof": False,
|
||||
},
|
||||
]
|
||||
|
||||
|
||||
def _ease_in_out(t: float) -> float:
|
||||
"""Cubic ease in-out, t in [0, 1]."""
|
||||
if t < 0.5:
|
||||
return 4.0 * t * t * t
|
||||
else:
|
||||
return 1.0 - (-2.0 * t + 2.0) ** 3 / 2.0
|
||||
|
||||
|
||||
def _lerp(a: float, b: float, t: float) -> float:
|
||||
"""Linear interpolation."""
|
||||
return a + (b - a) * t
|
||||
@@ -322,21 +303,10 @@ def _get_segment_params(frame: int, bsphere_radius: float):
|
||||
|
||||
Returns (azimuth_deg, elevation_deg, distance, lens_mm, use_dof).
|
||||
"""
|
||||
# Determine which segment (0-3) and local t (0-1)
|
||||
# Determine which segment and local t (0-1) — linear interpolation
|
||||
seg_index = min((frame - 1) // SEGMENT_LENGTH, len(SEGMENTS) - 1)
|
||||
local_frame = (frame - 1) - seg_index * SEGMENT_LENGTH
|
||||
raw_t = local_frame / max(SEGMENT_LENGTH - 1, 1)
|
||||
|
||||
# Apply easing: smooth start for segment 1, smooth stop for segment 4,
|
||||
# ease-in-out for segments 2 and 3
|
||||
if seg_index == 0:
|
||||
# Smooth start: ease-out (decelerate into motion)
|
||||
t = _ease_in_out(raw_t)
|
||||
elif seg_index == 3:
|
||||
# Smooth stop: ease-in-out with emphasis on deceleration
|
||||
t = _ease_in_out(raw_t)
|
||||
else:
|
||||
t = _ease_in_out(raw_t)
|
||||
t = local_frame / max(SEGMENT_LENGTH - 1, 1) # linear, no easing
|
||||
|
||||
seg = SEGMENTS[seg_index]
|
||||
|
||||
@@ -409,7 +379,21 @@ def _setup_cinematic_camera(parts, bbox_center, bsphere_radius, total_frames):
|
||||
cam_obj.data.dof.keyframe_insert(data_path="focus_distance", frame=frame)
|
||||
cam_obj.data.dof.keyframe_insert(data_path="aperture_fstop", frame=frame)
|
||||
|
||||
print(f"[cinematic_render] camera keyframed: {total_frames} frames across 4 segments")
|
||||
# Set all keyframes to LINEAR interpolation (no bezier smoothing)
|
||||
if cam_obj.animation_data and cam_obj.animation_data.action:
|
||||
for fcurve in cam_obj.animation_data.action.fcurves:
|
||||
for kp in fcurve.keyframe_points:
|
||||
kp.interpolation = 'LINEAR'
|
||||
if cam_obj.data.animation_data and cam_obj.data.animation_data.action:
|
||||
for fcurve in cam_obj.data.animation_data.action.fcurves:
|
||||
for kp in fcurve.keyframe_points:
|
||||
kp.interpolation = 'LINEAR'
|
||||
if cam_obj.data.dof.animation_data and cam_obj.data.dof.animation_data.action:
|
||||
for fcurve in cam_obj.data.dof.animation_data.action.fcurves:
|
||||
for kp in fcurve.keyframe_points:
|
||||
kp.interpolation = 'LINEAR'
|
||||
|
||||
print(f"[cinematic_render] camera keyframed: {total_frames} frames across {len(SEGMENTS)} segments (LINEAR interpolation)", flush=True)
|
||||
return cam_obj
|
||||
|
||||
|
||||
@@ -828,13 +812,25 @@ def main():
|
||||
scene.render.resolution_percentage = 100
|
||||
scene.render.image_settings.file_format = 'PNG'
|
||||
|
||||
# ── Transparent background ───────────────────────────────────────────────
|
||||
# ── White background (default for cinematic) ────────────────────────────
|
||||
# Set world to white unless template provides its own
|
||||
if not template_path:
|
||||
world = bpy.data.worlds.new("CinematicWorld")
|
||||
scene.world = world
|
||||
world.use_nodes = True
|
||||
bg_node = world.node_tree.nodes.get("Background")
|
||||
if bg_node:
|
||||
bg_node.inputs["Color"].default_value = (1.0, 1.0, 1.0, 1.0)
|
||||
bg_node.inputs["Strength"].default_value = 1.0
|
||||
print("[cinematic_render] white background set", flush=True)
|
||||
|
||||
# ── Transparent background (override if requested) ────────────────────
|
||||
if bg_color or transparent_bg:
|
||||
scene.render.film_transparent = True
|
||||
if bg_color:
|
||||
print(f"[cinematic_render] film_transparent=True for FFmpeg bg_color compositing ({bg_color})")
|
||||
print(f"[cinematic_render] film_transparent=True for FFmpeg bg_color compositing ({bg_color})", flush=True)
|
||||
else:
|
||||
print("[cinematic_render] transparent_bg enabled (alpha PNG frames)")
|
||||
print("[cinematic_render] transparent_bg enabled (alpha PNG frames)", flush=True)
|
||||
|
||||
# ── Persistent data (Cycles BVH caching between frames) ──────────────────
|
||||
scene.render.use_persistent_data = True
|
||||
|
||||
Reference in New Issue
Block a user