// ═══════════════════════════════════════════════════════════════ // ODIX Parade Match v1.0 // Per-channel lift/gain matching from RGB parade readings // // USAGE: // 1. Open your reference shot. Read its parade: // note the shadow floor and highlight ceiling per channel. // 2. Enter those values in SOURCE BLACK R/G/B and // SOURCE WHITE R/G/B. // 3. Switch to your target shot (the one to match). // Enter its parade values in TARGET BLACK/WHITE. // 4. The tool remaps each channel so the parade aligns. // 5. Use Strength to blend back if needed. // // TIP: For log footage, typical shadow values are 0.06–0.12 // and highlight values are 0.85–1.0 depending on camera. // In scene-linear, shadows near 0.0, highlights near 1.0+. // // COLOR SPACE: // Scene-linear or log-encoded video. // Do NOT apply after output LUT. // // by Odbat Batsaikhan — odixbat.com — @odixbat // ═══════════════════════════════════════════════════════════════ // ── Source parade readings (reference shot) ── DEFINE_UI_PARAMS(p_SrcBlkR, Src Black R, DCTLUI_SLIDER_FLOAT, 0.0, -0.2, 0.6, 0.001) DEFINE_UI_PARAMS(p_SrcBlkG, Src Black G, DCTLUI_SLIDER_FLOAT, 0.0, -0.2, 0.6, 0.001) DEFINE_UI_PARAMS(p_SrcBlkB, Src Black B, DCTLUI_SLIDER_FLOAT, 0.0, -0.2, 0.6, 0.001) DEFINE_UI_PARAMS(p_SrcWhtR, Src White R, DCTLUI_SLIDER_FLOAT, 1.0, 0.3, 2.0, 0.001) DEFINE_UI_PARAMS(p_SrcWhtG, Src White G, DCTLUI_SLIDER_FLOAT, 1.0, 0.3, 2.0, 0.001) DEFINE_UI_PARAMS(p_SrcWhtB, Src White B, DCTLUI_SLIDER_FLOAT, 1.0, 0.3, 2.0, 0.001) // ── Target parade values (shot you're matching) ── DEFINE_UI_PARAMS(p_TgtBlkR, Tgt Black R, DCTLUI_SLIDER_FLOAT, 0.0, -0.2, 0.6, 0.001) DEFINE_UI_PARAMS(p_TgtBlkG, Tgt Black G, DCTLUI_SLIDER_FLOAT, 0.0, -0.2, 0.6, 0.001) DEFINE_UI_PARAMS(p_TgtBlkB, Tgt Black B, DCTLUI_SLIDER_FLOAT, 0.0, -0.2, 0.6, 0.001) DEFINE_UI_PARAMS(p_TgtWhtR, Tgt White R, DCTLUI_SLIDER_FLOAT, 1.0, 0.3, 2.0, 0.001) DEFINE_UI_PARAMS(p_TgtWhtG, Tgt White G, DCTLUI_SLIDER_FLOAT, 1.0, 0.3, 2.0, 0.001) DEFINE_UI_PARAMS(p_TgtWhtB, Tgt White B, DCTLUI_SLIDER_FLOAT, 1.0, 0.3, 2.0, 0.001) // ── Global ── DEFINE_UI_PARAMS(p_Strength, Strength, DCTLUI_SLIDER_FLOAT, 1.0, 0.0, 1.0, 0.01) DEFINE_UI_PARAMS(p_MidCorrect, Mid Correction, DCTLUI_SLIDER_FLOAT, 1.0, 0.5, 2.0, 0.01) __DEVICE__ float channelRemap(float v, float srcLo, float srcHi, float dstLo, float dstHi) { float range = srcHi - srcLo; if (_fabs(range) < 0.00001f) return dstLo; float t = (v - srcLo) / range; // Apply mild gamma (MidCorrect) to midtone mapping return dstLo + t * (dstHi - dstLo); } __DEVICE__ float3 transform(int p_Width, int p_Height, int p_X, int p_Y, float p_R, float p_G, float p_B) { float r_matched = channelRemap(p_R, p_SrcBlkR, p_SrcWhtR, p_TgtBlkR, p_TgtWhtR); float g_matched = channelRemap(p_G, p_SrcBlkG, p_SrcWhtG, p_TgtBlkG, p_TgtWhtG); float b_matched = channelRemap(p_B, p_SrcBlkB, p_SrcWhtB, p_TgtBlkB, p_TgtWhtB); // Mid correction — gamma-like adjustment on matched result float midPivot = (p_TgtBlkR + p_TgtWhtR) * 0.5f; if (p_MidCorrect != 1.0f) { float g_val = 1.0f / p_MidCorrect; r_matched = midPivot + _powf(_fmaxf((r_matched - midPivot) / (1.0f - midPivot + 0.001f), 0.0f), g_val) * (1.0f - midPivot); g_matched = midPivot + _powf(_fmaxf((g_matched - midPivot) / (1.0f - midPivot + 0.001f), 0.0f), g_val) * (1.0f - midPivot); b_matched = midPivot + _powf(_fmaxf((b_matched - midPivot) / (1.0f - midPivot + 0.001f), 0.0f), g_val) * (1.0f - midPivot); } float r = p_R + (r_matched - p_R) * p_Strength; float g = p_G + (g_matched - p_G) * p_Strength; float b = p_B + (b_matched - p_B) * p_Strength; return make_float3(r, g, b); }