Give ACRIPES a straight BT.709 ramp and end-only tone

This commit is contained in:
2026-09-16 17:17:11 +07:00
parent a5e7162561
commit 8f615f6cd7
3 changed files with 42 additions and 20 deletions
+15 -10
View File
@@ -172,17 +172,22 @@ export function getSkiaColorMatrix(baseFilter: BaseFilter, adj: ColorAdjustments
0, 0, 0, 1, 0,
];
} else if (baseFilter === 'monochrome') {
// Acros: pure BT.709 grayscale, zero colour leakage. Contrast is a touch
// above neutral and eased around mid-grey, so the darks keep texture
// instead of plugging to black. (Grain/sharpness stay the user's call —
// a film sim here is the colour grade only.)
const r = 0.2339;
const g = 0.7867;
const b = 0.0794;
// Acros: pure BT.709 grayscale, zero colour leakage, and NO gain or offset.
// The weights sum to 1.0 and the offset is 0, so this matrix is a straight
// luminance readout: nothing clips at either end and every grey step the
// sensor recorded survives. (An earlier 1.10x / -0.05 version plugged every
// black below ~5% and blew every white above ~95% — exactly the fine
// graduation this stock is prized for.) The deep black and the highlight
// shoulder live in FILM_TONE (toneShader.ts), where the curve can be shaped
// one end at a time without touching the midtones. (Grain/sharpness stay
// the user's call — a film sim here is the colour grade only.)
const r = 0.2126;
const g = 0.7152;
const b = 0.0722;
matrix = [
r, g, b, 0, -0.05,
r, g, b, 0, -0.05,
r, g, b, 0, -0.05,
r, g, b, 0, 0,
r, g, b, 0, 0,
r, g, b, 0, 0,
0, 0, 0, 1, 0,
];
}
+17 -10
View File
@@ -24,11 +24,12 @@ export const DEFAULT_ADJUSTMENTS: ColorAdjustments = {
};
// Standard Fuji film simulations. These are the fixed "look" presets shown at
// the front of the PRESETS chip row: tapping one applies the simulation via
// its baseFilter colour matrix ONLY — every other knob resets to its neutral
// default (0 / auto) so the sim is a clean starting point, never a baked
// recipe. Any look beyond the matrix (grain, DR, CC, contrast…) is added by
// the user afterwards.
// the front of the PRESETS chip row: tapping one applies the simulation — its
// baseFilter colour matrix plus whatever the stock itself carries outside the
// matrix (its FILM_TONE curve, and a grain amount when the grain IS the look,
// as on Acros). Every other knob still resets to its neutral default (0 /
// auto), so a sim is a clean starting point, never a baked recipe; any further
// look (DR, CC, contrast, more grain) is added by the user afterwards.
export interface FilmSim {
id: string; // Recipe-like id used only while the sim is the current selection
name: string;
@@ -43,7 +44,7 @@ export const FILM_SIMS: FilmSim[] = [
{ id: 'sim-classic-neg', name: 'CLASSIC NEGIPES', baseFilter: 'classic-neg', adjustments: { ...DEFAULT_ADJUSTMENTS } },
{ id: 'sim-astia', name: 'ASTIPES', baseFilter: 'astia', adjustments: { ...DEFAULT_ADJUSTMENTS } },
{ id: 'sim-eterna', name: 'ETERNIPES', baseFilter: 'eterna', adjustments: { ...DEFAULT_ADJUSTMENTS } },
{ id: 'sim-acros', name: 'ACRIPES', baseFilter: 'monochrome', adjustments: { ...DEFAULT_ADJUSTMENTS } },
{ id: 'sim-acros', name: 'ACRIPES', baseFilter: 'monochrome', adjustments: { ...DEFAULT_ADJUSTMENTS, grain: 3 } },
// The rangefinder digital look (M/Q/SL) is not a film stock, so it ships as its own
// pair instead of one look that has to compromise:
// CLASSIC — the stock character: deep solid darks, natural shadows, rich but
@@ -150,15 +151,21 @@ export const DEFAULT_RECIPES: Recipe[] = [
baseFilter: 'monochrome',
adjustments: {
exposure: 0,
contrast: 4,
// Low: the deep black comes from FILM_TONE's toe and the micro-contrast
// from CLARITY, both of which add separation WITHOUT flattening the grey
// ramp. The old 4 / highlight 3 / shadow -2 stack was three global
// contrast moves on top of a matrix that was already clipping — it
// bought its deep black by throwing away every step below 9% and every
// one above 91%.
contrast: 2,
saturation: 0,
temperature: 5000,
tint: 0,
highlight: 3,
shadow: -2,
highlight: 0,
shadow: 0,
denoise: 0,
clarity: 3,
grain: 6,
grain: 5,
colorChrome: 'none',
colorChromeBlue: 'none',
dynamicRange: 'auto',
+10
View File
@@ -189,6 +189,16 @@ export interface ToneUniforms {
const FILM_TONE: Partial<Record<BaseFilter, Partial<ToneUniforms>>> = {
'classic-chrome': { sh: -0.28 },
'classic-neg': { shT: [-0.018, 0.009, 0.013], hlT: [0.024, 0.008, -0.012] },
// Acros. A black-and-white stock IS its grey ramp, so this entry only shapes
// the two ENDS and leaves the middle an identity: a smooth shadow toe that
// reaches a true black (no film-base lift, no flat grey wash) and a highlight
// shoulder that stops just short of white instead of clipping a cloud to
// paper. Mid-tones see neither mask, so the ramp keeps every step the matrix
// handed over — which is what 'deep black' costs in a colour stock and does
// not have to cost here.
// Gains are TONE_SKSL's own (sh * 0.34, hl * 0.22), so -0.12 puts the toe at
// ~5% and -0.05 trims the top ~1%.
monochrome: { sh: -0.12, hl: -0.05 },
};
export function getToneUniforms(adj: ColorAdjustments, baseFilter?: BaseFilter): ToneUniforms {