feat(effects): a vhs tape-tear scene effect - #396
Merged
Merged
Conversation
`PostEffect` had `grain`, `vignette`, `pixelate`, `progressive_blur` and `flash`. None of them displaces bands of the image, and grain plus aberration does not read as a tape tear — the channel-splitting effects act on a cut (`chromatic_wipe`) or on one node (`chromatic_aberration`), never on the whole frame as a timed state. `vhs` breaks the frame into horizontal bands slid sideways, over noise, scanlines and a tracking line travelling down. Bounded by `at` and `duration` like `flash`, and for the same reason: a reference reel holds it for well under a second on a rewind. Outside its window the buffer is not touched at all. Two decisions worth recording. The band layout is redrawn twelve times a second rather than every frame. A tear that changes at 30 or 60 fps shimmers instead of catching; twelve is slow enough to read as a mechanical fault. The seed and the instant decide together, so two renders of the same file are byte-identical. A band slid sideways leaves the freed edge black, not smeared or wrapped. That gap is what makes the displacement read as a tear rather than as a blur. Five tests. Three fail without the effect — the frame must differ inside the window, even rows must darken, the tracking line must paint its colour. The other two assert equality and pass both ways on purpose: outside the window nothing changes, and one seed at one instant is deterministic. Verified end to end on a contact sheet: clean at 0.10 s, torn with the tracking line near the top at 0.40 s, torn with it lower at 0.60 s, clean again at 1.20 s. Closes #386
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Closes #386. Refs #388.
PostEffecthadgrain,vignette,pixelate,progressive_blurandflash. None displaces bands of the image. The channel-splitting effects act on a cut (chromatic_wipe) or on one node (chromatic_aberration), never on the whole frame as a timed state — so grain plus aberration was the best available approximation, and it does not read as a tape tear.Two decisions worth recording
The band layout is redrawn twelve times a second, not every frame. A tear that changes at 30 or 60 fps shimmers instead of catching; twelve is slow enough to read as a mechanical fault. The seed and the instant decide together, so two renders of the same file are byte-identical.
A slid band leaves the freed edge black, not smeared or wrapped. That gap is what makes the displacement read as a tear rather than a blur. On a light background it is very visible — the rule file says to lower
offsetrather than hope it blends.It is a beat, not a filter
atanddurationbound it likeflash, because the reference holds it for well under a second. Outside the window the buffer is not touched — not lightly, not at all, which one of the tests asserts byte-for-byte.Verification
Five tests. Three fail without the effect:
The other two assert equality and pass both ways on purpose — outside the window nothing changes, and one seed at one instant is deterministic.
End to end on a contact sheet at 0.10 / 0.40 / 0.60 / 1.20 s: clean, torn with the tracking line near the top, torn with it lower, clean again.
cargo fmt --all --checkandcargo clippy --workspace --all-targets -- -D warningsclean;cargo test --workspace1632.Two
ffmpeg-dependent tests (integration_audio_track_from_embedded_video,successful_extraction_leaves_no_partial_file_behind) failed once in a full run under heavy parallel load and pass 23/23 in isolation. That is the third distinct wall-clock-dependent test to do this today under agent load; worth its own issue, unrelated to this change.Written comment-free, per the codebase-wide rule from #345.