Skip to content

Ecsplendid/motion blur#375

Open
ecsplendid wants to merge 4 commits into
midrender:mainfrom
ecsplendid:ecsplendid/motion-blur
Open

Ecsplendid/motion blur#375
ecsplendid wants to merge 4 commits into
midrender:mainfrom
ecsplendid:ecsplendid/motion-blur

Conversation

@ecsplendid
Copy link
Copy Markdown

Summary

This PR adds professional-grade motion blur to Revideo using temporal sub-frame accumulation - the same technique used in film and professional video production.

Motion Blur Demo

Left: Motion blur ON | Right: Motion blur OFF (per-element control)

Features

Core Capabilities

  • Quality presets: low (4), medium (8), high (16), ultra (32) samples
  • Shutter curves: box (uniform), triangle (linear falloff), gaussian (bell curve)
  • Shutter angle: 0-720° (film standard is 180°)
  • Shutter position: center, start, or end alignment
  • Per-element control: Enable/disable blur on individual elements

Implementation

Two-pass rendering architecture:

  1. Pass 1 (blur): Render blur-enabled elements with sub-frame accumulation

    • Advance scene through sub-frame time offsets
    • Accumulate each render with curve-based weights (box/triangle/gaussian)
    • Normalize accumulated values using high-precision float buffer
  2. Pass 2 (static): Render blur-disabled elements on top

    • Single render at frame time, composited over blurred result
    • Keeps text/UI sharp while background animates with blur

Usage

export default makeProject({
  scenes: [scene],
  settings: {
    rendering: {
      motionBlur: {
        enabled: true,
        quality: 'high',
        shutterAngle: 180,
        shutterCurve: 'gaussian',
        shutterPosition: 'center',
      },
    },
  },
});

Per-element control:
<Circle fill="blue" />                          // Will be blurred
<Txt text="UI" motionBlur={{enabled: false}} /> // Stays sharp

Files Changed

| File                                     | Purpose                                           |
|------------------------------------------|---------------------------------------------------|
| packages/core/src/types/MotionBlur.ts    | Config types, quality presets, weight calculation |
| packages/core/src/app/Renderer.ts        | Two-pass rendering, sub-frame loop                |
| packages/core/src/app/Stage.ts           | Float32 accumulation buffer, clearCanvas option   |
| packages/2d/src/lib/components/Node.ts   | Render pass filtering per element                 |
| packages/2d/src/lib/components/View2D.ts | Motion blur subframe signals                      |
| packages/2d/src/lib/scenes/Scene2D.ts    | setMotionBlurSubframe implementation              |

Test Plan

- Motion blur renders correctly at all quality presets
- Shutter curves (box/triangle/gaussian) produce correct weights
- Per-element control works (see demo GIF)
- All existing tests pass (478 tests)
- Documentation in docs/MOTION_BLUR.md

Documentation

Full guide: ./docs/MOTION_BLUR.md

---
🤖 Generated with https://claude.com/claude-code

claude and others added 2 commits January 3, 2026 05:47
Add upfront validation of request bodies for the /render API endpoint
to improve security and provide better error messages to API consumers.

The validation module checks:
- callbackUrl: must be valid http/https URL if provided
- variables: must be an object if provided
- streamProgress: must be boolean if provided
- settings: validates outFile extension, workers count, port range,
  exporter configuration, size dimensions, and time range

This addresses the TODO comments in render.ts and improves API robustness.
⚠️ AI-ASSISTED CODE - PLEASE REVIEW CAREFULLY ⚠️

This feature was vibecoded with Claude Code (Opus 4.5). While functional
and tested, it should be carefully reviewed before merging to production.

## Summary

Adds professional-grade motion blur to Revideo using temporal sub-frame
accumulation - the same technique used in film and professional video
production.

## Features

- **Quality presets**: low (4), medium (8), high (16), ultra (32) samples
- **Shutter curves**: box (uniform), triangle (linear falloff), gaussian (bell curve)
- **Shutter angle**: 0-720° (film standard is 180°)
- **Shutter position**: center, start, or end alignment
- **Per-element control**: Enable/disable blur on individual elements

## Implementation

Two-pass rendering for per-element motion blur:

1. Pass 1 (blur): Render blur-enabled elements with sub-frame accumulation
   - Advance scene through sub-frame time offsets
   - Accumulate each render with curve-based weights
   - Normalize using high-precision Float32 buffer

2. Pass 2 (static): Render blur-disabled elements on top
   - Single render at frame time
   - Composited over the blurred result

## Usage

```typescript
motionBlur: {
  enabled: true,
  quality: 'high',
  shutterAngle: 180,
  shutterCurve: 'gaussian',
}
```

Per-element: `<Txt motionBlur={{enabled: false}} />`

## Key Files

- packages/core/src/types/MotionBlur.ts - Config types and weight calculations
- packages/core/src/app/Renderer.ts - Two-pass rendering implementation
- packages/core/src/app/Stage.ts - Accumulation buffer
- packages/2d/src/lib/components/Node.ts - Per-element render pass filtering
- packages/2d/src/lib/components/View2D.ts - Motion blur subframe signals

🤖 Vibecoded with Claude Code (Opus 4.5) - https://claude.com/claude-code

Co-Authored-By: Claude <noreply@anthropic.com>
@ecsplendid ecsplendid force-pushed the ecsplendid/motion-blur branch from 521cf58 to 1554627 Compare January 3, 2026 08:01
…erwriting blur

BUG FOUND: The two-pass rendering system (blur pass + static pass) was designed
for per-element motion blur control where nodes explicitly set motionBlur={{enabled: true/false}}.

However, without explicit settings:
1. ALL nodes render in the 'blur' pass → accumulated correctly ✓
2. ALL nodes ALSO render in the 'static' pass → OVERWRITES the blur! ✗

The static pass was rendering a sharp frame on TOP of the accumulated blur result,
completely erasing the motion blur effect.

FIXES:
1. Removed the static pass entirely - now all elements get motion blur applied
2. Fixed frame timing compensation in main render loop
3. Properly advance scene through subframes using progress() with small speed

To have specific elements stay sharp (not blurred), explicitly set on those nodes:
  motionBlur={{enabled: false}}

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
@ecsplendid
Copy link
Copy Markdown
Author

ecsplendid commented Jan 3, 2026

Examples

image
transfer-entropy.mp4
translation-rotation-invariance.mp4
nn-vs-ebm-cost.mp4

Remove per-element motion blur control in favor of a simpler,
scene-wide approach. Motion blur is now applied uniformly to all
elements when enabled at the project/scene level.

Changes:
- Remove motionBlur prop from Node (no more per-element control)
- Remove motionBlurRenderPass from View2D (no multi-pass rendering)
- Simplify setMotionBlurSubframe to remove renderPass parameter
- Update Renderer to use single-pass rendering for all elements
- Update motion-blur example scene to reflect simplified API

This eliminates the title shift glitch caused by multi-pass
compositing and provides more predictable motion blur behavior.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants