dorkhub

powershot-threejs

Analog / Digital camera emulation for Three.js

cl0nazepamm
JavaScript586 forksMITupdated 2 days ago
visit the demogit clone https://github.com/cl0nazepamm/powershot-threejs.gitcl0nazepamm/powershot-threejs

powershot for three.js

powershot

Authentic digicam, analog tape, film and night-vision emulation post-processing for Three.js.

Play live here.

https://cl0nazepamm.github.io/powershot-threejs/

Run the demo

npm install
npm run dev

Requires WebGPU.

Install in your Three.js project

npm install powershot-threejs three

Drop it on your scene (default workflow)

Every effect is a THREE.RenderPipeline output-node pass — wrap the node chain you already have and you're done. Sizing, frame counting, and per-frame bookkeeping are automatic:

import * as THREE from "three/webgpu";
import { pass } from "three/tsl";
import { Pipeline, powerShotPass } from "powershot-threejs";

const renderer = new THREE.WebGPURenderer({ canvas });
await renderer.init();

const powershot = new Pipeline(renderer);
powershot.setMode("analog");            // or "digital"
powershot.setPreset("powershot");
powershot.setInputEncoding("linear");   // a raw scene pass carries linear light

const renderPipeline = new THREE.RenderPipeline(renderer);
renderPipeline.outputNode = powerShotPass(pass(scene, camera), powershot);

function animate() {
  renderPipeline.render();
}

filmPass, infraredPass, nightshotPass, and solarFlarePass work exactly the same way, and any existing output node can stand in for the scene pass — stack your own manipulations first and put the camera last. Two rules:

  • Film replaces the tonemap. filmPass must be the only display transform in the chain (no ACES / AgX before it) and wants setInputEncoding("linear").
  • Feeding an already display-referred chain (post-tonemap manipulations)? Skip setInputEncoding — the default expects encoded input.

Adapters auto-size to the source; pass { autoSize: false } or { resolutionScale } to manage effect resolution yourself. See ADVANCED.md for the full adapter contract.

Processing images and videos

The same pipelines also run standalone on any texture: create a pipeline, pick a preset by name, call render(texture) every frame. Pick your mode below and copy its section. All snippets share this setup:

import * as THREE from "three/webgpu";

const renderer = new THREE.WebGPURenderer({ canvas });
await renderer.init();

The input texture can be an image, a video, or a render target's .texture. render() locks the internal processing resolution to the first frame it sees; call setSize(w, h) first to pick your own (e.g. a low authentic-camera resolution). When you need deterministic control — explicit frame numbers, fixed dt, an output render target — use renderTexture(texture, frame, options); see ADVANCED.md.

Digicam

Mid-2000s point-and-shoot look: sensor noise, sharpening halos and JPEG blocks.

import { Pipeline } from "powershot-threejs";

const powershot = new Pipeline(renderer);
powershot.setMode("digital");
powershot.setPreset("powershot");

// each frame:
powershot.render(inputTexture);

Presets: cybershot, powershot, coolpix, exilim, ixus.

Analog tape

VHS / camcorder look: chroma bleed, tracking errors, dropouts. Same Pipeline as digicam, different mode:

import { Pipeline } from "powershot-threejs";

const powershot = new Pipeline(renderer);
powershot.setMode("analog");
powershot.setPreset("powershot");

// each frame:
powershot.render(inputTexture);

Film

Motion-picture film: a real negative-to-print chain with film stocks, grain, halation, gate weave and flicker.

Important

Film runs alone, on linear light. Do not combine it with the other PowerShot modes, and do not tone-map before it (no ACES / AgX / renderer tone mapping) — the negative→print chain is the tone map. Feed it a scene-linear HDR render target with renderer.toneMapping = THREE.NoToneMapping.

import * as THREE from "three/webgpu";
import { FilmPipeline } from "powershot-threejs";

const renderer = new THREE.WebGPURenderer({ canvas });
await renderer.init();
renderer.toneMapping = THREE.NoToneMapping;

const film = new FilmPipeline(renderer);
film.setInputEncoding("linear"); // input is scene-linear, not sRGB
film.setPreset("kodak_500t");

// each frame:
film.render(linearSceneTexture);

Stocks: kodak_500t, kodak_200t, kodak_250d, kodak_50d.

White phosphor

Night-vision tube: local gain, intensifier halo, scintillation, tube vignette — tuned around a P45-style white phosphor tube.

import { InfraredPipeline } from "powershot-threejs";

const infrared = new InfraredPipeline(renderer);
infrared.setPreset("white_phosphor");

// each frame (frame timing for the tube's auto-brightness loop is automatic):
infrared.render(inputTexture);

NightShot

Sony Handycam "NightShot": a camcorder CCD with the IR-cut filter flipped out — AGC breathing, heavy noise, vertical smear, green monochrome, then the analog tape path.

import { NightshotPipeline } from "powershot-threejs";

const nightshot = new NightshotPipeline(renderer);
nightshot.setPreset("nightshot_plus");

// each frame:
nightshot.render(inputTexture);

Solar Flares

Solar Flares, the spectral sunlight flare, is a separate scene-linear optical pass, not a sprite effect or a digicam preset. It uses a traced Heliar prescription for internal reflections, an aperture FFT for diffraction, a source-centred glare halo, and a low-frequency veiling layer. Run it before PowerShot, film, exposure, tone mapping, and output conversion:

import * as THREE from "three/webgpu";
import { pass } from "three/tsl";
import {
  SolarFlarePipeline,
  loadHeliarTronnierFlareProfile,
  solarFlarePass,
} from "powershot-threejs";

const scenePass = pass(scene, camera);
const profile = await loadHeliarTronnierFlareProfile();
const flare = new SolarFlarePipeline(renderer, {
  profile,
  camera,
  sun, // THREE.DirectionalLight
});
flare.setSize(width, height);
flare.setAperture({ fNumber: 8, blades: 7 });

const renderPipeline = new THREE.RenderPipeline(renderer);
renderPipeline.outputNode = solarFlarePass(scenePass, flare, {
  camera,
  sun,
  depthTexture: scenePass.getTexture("depth"),
});

function animate() {
  renderPipeline.render();
}

The true solar direction is never clamped to the viewport, so the lens can flare when the sun is just outside frame. Scene depth resolves partial on-screen solar occlusion. For off-screen occlusion, supply a camera-to-sun visibility provider. Run npm run dev and open /powershot-threejs/flare.html for the interactive WebGPU sunlight, aperture, component-isolation, and occlusion demo. See SOLAR_FLARES.md for the optical model, controls, PowerShot ordering, and calibration contract.

Going further

Per-mode controls, linear/HDR input, output grading, THREE.RenderPipeline integration, real NIR input and the repo layout are in ADVANCED.md.

Acknowledgements

  • NTSC
  • OpenISP

more like this

lottery

🎉🌟✨🎈年会抽奖程序,基于 Express + Three.js的 3D 球体抽奖程序,奖品🧧🎁,文字,图片,抽奖规则均可配置,😜抽奖人员信息Excel一键导入😍,抽奖结果Excel导出😎,给你的抽奖活动带来全新酷炫体验🚀🚀🚀

JavaScript4.6k

urge

The Universal Ruby Game Engine (URGE) Project

C++50

search

search projects, people, and tags