승인된 Blender 컷의 실제 합성과 최종 검수 근거 추가

This commit is contained in:
NINI
2026-10-10 04:11:08 +09:00
parent 78fcd49a54
commit ff28b7b47c
26 changed files with 6955 additions and 220 deletions
@@ -1,6 +1,7 @@
import React, {useEffect, useLayoutEffect, useRef, useState} from 'react';
import {AbsoluteFill, Composition, OffthreadVideo, Sequence, continueRender,
delayRender, getInputProps, registerRoot, staticFile, useCurrentFrame} from 'remotion';
import {blenderLabels} from './scene-plan.mjs';
const clamp = (n) => Math.max(0, Math.min(1, n));
const ease = (n) => n * n * (3 - 2 * n);
@@ -83,16 +84,22 @@ function Motion({clip}) {
}
function TextLayer({data}) {
const ms = useCurrentFrame() * 20;
const frame = useCurrentFrame(), ms = frame * 20;
const caption = data.captions.find((c) => ms >= c.start_ms && ms < c.end_ms);
const labels = data.labels.filter((l) => ms >= l.start && ms < l.end);
const clip = data.clips.find((c) => ms >= c.start_ms && ms < c.end_ms);
const blender = data.visual_engine === 'Blender';
const labels = blender ? blenderLabels(clip, frame) : data.labels.filter((l) => ms >= l.start && ms < l.end);
return <AbsoluteFill style={{fontFamily: 'Malgun Gothic, sans-serif', color: '#17314c'}}>
{ms < 7920 && <div style={{position: 'absolute', top: 35, left: 85, right: 85,
{ms < 7920 && <div style={{position: 'absolute', top: 240, left: 85, right: 85,
fontSize: 64, lineHeight: 1.1, fontWeight: 800, textAlign: 'center'}}>
물방울은 왜<br/>동그랗게 뭉칠까?
{data.title}
</div>}
{labels.map((l, i) => <div key={i} style={{position: 'absolute', left: l.x / 945 * 1080,
top: l.y / 1680 * 1920, transform: 'translate(-50%, -50%)', fontSize: (l.size ?? 36) / 945 * 1080,
{blender && <svg width="1080" height="1920" style={{position: 'absolute', inset: 0}}>
{labels.filter((l) => l.leader).map((l, i) => <line key={i} x1={l.x} y1={l.y + 20}
x2={l.target[0]} y2={l.target[1]} stroke="#17314c" strokeWidth="3"/>)}
</svg>}
{labels.map((l, i) => <div key={i} style={{position: 'absolute', left: blender ? l.x : l.x / 945 * 1080,
top: blender ? l.y : l.y / 1680 * 1920, transform: 'translate(-50%, -50%)', fontSize: blender ? l.size : (l.size ?? 36) / 945 * 1080,
fontWeight: 700, whiteSpace: 'nowrap', background: l.text.startsWith('δ') ? 'transparent' : '#fffdf2ee',
padding: l.text.startsWith('δ') ? 0 : '9px 16px', borderRadius: 12}}>{l.text}</div>)}
{caption && <div style={{position: 'absolute', left: 80, right: 80, bottom: 140,
@@ -106,7 +113,7 @@ function TextLayer({data}) {
function Video({data, composed = false}) {
return <AbsoluteFill style={{background: '#fffdf2'}}>
{data.clips.map((clip) => <Sequence key={clip.id} from={clip.start_ms / 20} durationInFrames={clip.frames}>
{composed ? <OffthreadVideo src={staticFile(`${clip.id}.mp4`)} muted style={{width: '100%', height: '100%'}}/>
{composed ? <OffthreadVideo src={staticFile(clip.video ?? `${clip.id}.mp4`)} muted style={{width: '100%', height: '100%'}}/>
: <Motion clip={clip}/>}
</Sequence>)}
{composed && <TextLayer data={data}/>}