Jump to content

I made a DCTL that brings Lightroom-style controls to Davinci Resolve


D Verco
 Share

Recommended Posts

Hey everyone! I wanted to share something I've been working on for the past while - Lumap, a DCTL for Resolve that consolidates all your basic grading adjustments into one intuitive panel.

The Problem I Was Trying to Solve

I got tired of using complex node structures just to do basic exposure, contrast, and saturation adjustments that would look natural. Coming from a photography background using Capture One and Lightroom, I missed having simple sliders in one place that just worked without having to think about CSTs, colour spaces, and multiple different nodes. 

What Lumap Does

Instead of wrestling with lift/gamma/gain wheels, Lumap gives you: 

Photometric exposure - Linear stops like changing exposure in-camera

Temperature and Tint - For white balance

Intelligent contrast - References your source’s format's middle grey without overcooking the toe/shoulder

Brightness - Targets midtones while preserving highlights and shadows

Organic highlight/shadow recovery - create organic, film-like roll-offs that seamlessly blend into your midtones

Smart saturation - Preserves luminance and prevents skin tone oversaturation

Film-style density - Control the intensity and depth of colours you get from subtractive saturation techniques 

 

Why Sliders Instead of Wheels?

Most of us aren't using professional grading panels - we're using mice and trackpads. Sliders are just faster and more responsive for cursor input. Plus, if you're coming from photo editing, this interface will feel immediately familiar.

There's a free demo version so you can test if it fits your workflow. 

https://www.dhyanverco.com/lumap

Would love to hear your thoughts if you give it a try! And happy to answer any questions.

 

965931903_Screenshot2025-04-28at3_23.07PM_2.webp.a6d30ebcb217b65eb80b6f2cf42afdb9.webp

Link to comment
Share on other sites

EOSHD Pro Color 5 for Sony cameras EOSHD Z LOG for Nikon CamerasEOSHD C-LOG and Film Profiles for All Canon DSLRs

I've been working on a similar one, but in L*a*b colour space and offering a lot of more advanced tools to quickly do things I do all the time.

One advantage of doing things in a DCTL rather than using the GUI controls is that when grading in Resolve while travelling etc, where you just have a small monitor and no control surfaces etc, you can make the viewer larger (IIRC using Shift-F) and it essentially gives you the viewer and the DCTL control panel on the right-hand-side of the screen, so it's a really efficient layout for grading using only the keyboard/mouse.

@D Verco if you're looking for ideas on how to expand the tool I'd suggest thinking about it for use with power-windows as well as over the whole image.  For example, my standard node graph has about 6 nodes with power-windows already defined that are ready to just enable if I want them.  I have ones for a vignette, gradients for sky and left and right, and four large soft power windows for people where I will typically do things like brighten / add contrast / sharpen, and do basic skin operations like hue rotations / hue compressions / etc.  
Most of the operations I'd do with those windows are covered by your tool, but not all of them, and if a tool can be used for a range of other tasks other than just basic image processing then all the better.
If you're taking a leaf from how Lightroom works, one of the most powerful features I used to use all the time (and wedding photogs would absolutely swear by) was the preset brushes.  I had brushes for skin smoothing, skin brightening, under-eye, redness, etc, and of course they all used the standard Lightroom controls, but in specific combinations they really worked well.

Something to think about.

I'm all for people being able to charge money for their efforts, but in todays climate, the more value you can provide the easier it will be to get people to part with their (often hard-earned) money.

Link to comment
Share on other sites

This is an interesting approach. The DCTL basically mimics photo editing and puts all corrections in one node. It is clearly for people who don't want to go deeper in Resolve and color correction/grading in general. They also have some or a lot of Photoshop / Lightroom experience and habits. These Resolve users do exists. I was one of them many years back and I hope your DCTL would find its users.   

For more sophisticated users like me now and basically anybody interested in going deeper into Resovle it is too simple and lacks fine controls. 

For users like me and serious colorists there are many DCTLs that address the complexity problems of split toning, S curve, better contrast, film density, etc. in Resolve. They provide much finer control over each aspect of the image. Most of those DCTLs are created by professional colorists and used by other colorist and editors. I also use some of them. 

Mononodes DCTLs
IridescentColor DCTLs
PixelTools DCTLs
Many free DCTLs 
Cullen Kelly - Contour toolset DCTLs
Waqas Qazi - Qazi's Toolkit DCTLs


Behind those tools also lies a methodology well explained by colorists. For me personally understanding image editing for film and video at a deeper level is much more rewarding process. Once I created my node tree and establish each aspect of the image, editing and color correction is a piece of cake. 

So not for me but there are people who may like your approach.

Link to comment
Share on other sites

18 hours ago, kye said:

I've been working on a similar one, but in L*a*b colour space and offering a lot of more advanced tools to quickly do things I do all the time.

One advantage of doing things in a DCTL rather than using the GUI controls is that when grading in Resolve while travelling etc, where you just have a small monitor and no control surfaces etc, you can make the viewer larger (IIRC using Shift-F) and it essentially gives you the viewer and the DCTL control panel on the right-hand-side of the screen, so it's a really efficient layout for grading using only the keyboard/mouse.

@D Verco if you're looking for ideas on how to expand the tool I'd suggest thinking about it for use with power-windows as well as over the whole image.  For example, my standard node graph has about 6 nodes with power-windows already defined that are ready to just enable if I want them.  I have ones for a vignette, gradients for sky and left and right, and four large soft power windows for people where I will typically do things like brighten / add contrast / sharpen, and do basic skin operations like hue rotations / hue compressions / etc.  
Most of the operations I'd do with those windows are covered by your tool, but not all of them, and if a tool can be used for a range of other tasks other than just basic image processing then all the better.
If you're taking a leaf from how Lightroom works, one of the most powerful features I used to use all the time (and wedding photogs would absolutely swear by) was the preset brushes.  I had brushes for skin smoothing, skin brightening, under-eye, redness, etc, and of course they all used the standard Lightroom controls, but in specific combinations they really worked well.

Something to think about.

I'm all for people being able to charge money for their efforts, but in todays climate, the more value you can provide the easier it will be to get people to part with their (often hard-earned) money.

I'm actually doing saturation in LAB. There's a couple of difference colour/ gamma spaces being used to get better results than you'd get from Resolve's basic tools. 

And great point on the UI with DCTL's as a side panel. I find resolve's ui to be designed around control panels, which most people don't have. 

I think adding an vignette adjustment could definitely be possible. But other power window/ brush masks might be be off using Resolve's tools on the node. You definitely should be able to use it with power windows though. 

I do like the idea of having those retouching tools in Resolve, but I think it might have to be done in openfx as DCTLs have limitations. 

Also I originally want to add something like Capture One's colour editor or a hue control, but then Resolve added the colour slice tool. And while there are better possible implementations it did feel mostly redundant.

Link to comment
Share on other sites

17 hours ago, stephen said:

This is an interesting approach. The DCTL basically mimics photo editing and puts all corrections in one node. It is clearly for people who don't want to go deeper in Resolve and color correction/grading in general. They also have some or a lot of Photoshop / Lightroom experience and habits. These Resolve users do exists. I was one of them many years back and I hope your DCTL would find its users.   

For more sophisticated users like me now and basically anybody interested in going deeper into Resovle it is too simple and lacks fine controls. 

For users like me and serious colorists there are many DCTLs that address the complexity problems of split toning, S curve, better contrast, film density, etc. in Resolve. They provide much finer control over each aspect of the image. Most of those DCTLs are created by professional colorists and used by other colorist and editors. I also use some of them. 

Mononodes DCTLs
IridescentColor DCTLs
PixelTools DCTLs
Many free DCTLs 
Cullen Kelly - Contour toolset DCTLs
Waqas Qazi - Qazi's Toolkit DCTLs


Behind those tools also lies a methodology well explained by colorists. For me personally understanding image editing for film and video at a deeper level is much more rewarding process. Once I created my node tree and establish each aspect of the image, editing and color correction is a piece of cake. 

So not for me but there are people who may like your approach.

I've seen and tried many of those other plugins and they're fantastic, which is why I wanted to take a different approach (and more affordable) with Lumap. I've found a lot of workflows in Resolve involve complex node trees meaning you're jumping across multipe nodes for basic adjustments. For instance, Mononodes has around 5 DCTLs for colour adjustments. 

Contour has 8 parameters for contrast. I've seen another that had over 10. What I like about Capture One/ Lightroom is that the contrast adjustment is built to just look naturally good. Paired with highlight/ shadow/ brightness sliders it gives you a lot of control, very quickly. 

The great thing is that for people who do want to go really deep those tools are already there. 

 

Link to comment
Share on other sites

51 minutes ago, D Verco said:

I'm actually doing saturation in LAB. There's a couple of difference colour/ gamma spaces being used to get better results than you'd get from Resolve's basic tools. 

And great point on the UI with DCTL's as a side panel. I find resolve's ui to be designed around control panels, which most people don't have. 

I think adding an vignette adjustment could definitely be possible. But other power window/ brush masks might be be off using Resolve's tools on the node. You definitely should be able to use it with power windows though. 

I do like the idea of having those retouching tools in Resolve, but I think it might have to be done in openfx as DCTLs have limitations. 

Also I originally want to add something like Capture One's colour editor or a hue control, but then Resolve added the colour slice tool. And while there are better possible implementations it did feel mostly redundant.

Definitely agree that lots of operations are better done in colour spaces other than the ones supported by Resolve natively.  Are you using OKLab for your Lab conversion?  I plan in integrating that into my tool once I get back to developing it.

I'd keep all the secondary adjustments like vignetting etc as power-windows in Resolve, as getting a single vignette slider that looks good across many/all scenarios probably isn't possible.

I haven't played with spatial adjustments in DCTLs yet, so I'm not sure what the performance hit is compared to OpenFX tools.  I'll probably investigate this at some point though, as there are a few operations I'd do that might benefit from being integrated into a single DCTL.

I played with the colour slice tool and I think it is actually very disappointing as I found it broke images incredibly quickly, while also simultaneously being too broad for lots of adjustments I'd like to make.  I've had much more success in doing more targeted adjustments in Lab that didn't go anywhere near breaking the image.  I find Lab is a far cleaner space to work in for lots of operations as the things you might want to do that are colour-slice-esque are often far simpler and far more universal.  There are tonnes of things you can do in Lab that target certain ranges but are applied globally, so won't break the image, like how doing (most) adjustments with the Channel Mixer can't break the image. 

Link to comment
Share on other sites

I don't think Resolve supports oklab yet. According to google's ai you'd need to use an openfx plugin to provide support. CIELAB is still a good colourspace though. It's a huge improvement for saturation compared to Resolve's default in HSL. 

 

On 9/17/2025 at 12:58 PM, kye said:

There are tonnes of things you can do in Lab that target certain ranges but are applied globally, so won't break the image, like how doing (most) adjustments with the Channel Mixer can't break the image. 

Do you have an example of "target certain ranges but are applied globally". 
Definitely agree that LAB or even HSV are better spaces than the default. And I do think it's surprising the defaults aren't improved to provide better results- especially when the software is mainly known for colour grading. 

Link to comment
Share on other sites

7 hours ago, D Verco said:

I don't think Resolve supports oklab yet. According to google's ai you'd need to use an openfx plugin to provide support. CIELAB is still a good colourspace though. It's a huge improvement for saturation compared to Resolve's default in HSL. 

The way I've used it in the past is there's a DCTL that will convert to/from OKLab.  The pain is that it assumes you're in LogC3, so I have to do a CST before/after to get from/to DWG/DI which I use as my timeline colour space.

My next steps were to look at the licensing for the OKLab DCTL, and work out how to just go directly from DWG/DI to OKLab in my DCTL.  I'd assume I'd just need to combine the matrices for DWG->LogC3 and the LogC3->OKLab together into one step, but not sure if that's right.

7 hours ago, D Verco said:

Do you have an example of "target certain ranges but are applied globally". 

The way I'd do it is to implement a function that can be applied to the LAB variables that is stronger in the target areas than the others, or only apply in the target areas.  

A simple way is to say something like:
if(A<0) { A=0; } 
if(B<0) { B=0; } 
X = (A * B);
Then use that as a mask where the transformation is multiplied by X, so if X=1 the full effect is applied and if X=0 no effect is applied.

What that will do is not effect the three quarters of the colour circle where either A or B are negative, and for the quarter they're both positive it will start at zero on the edges and then ramp up.  This will ensure nothing in the image breaks because every adjacent colour before the operation remains adjacent afterwards.

That will create sudden transitions in the mask though, so you could (for example) square the mask before using it, so values of X that are very low will be vanishingly low and the surface created will be a smooth curve.  A bit more sophistication can make the transitions at the top (near 1) smooth too.

This above is a transform that targets the A=B vector, but if you calculate Hue and Sat from the A and B values, then you can start to combine all sorts of values together, like having the value target a hue with a certain range, a saturation with a certain range, and then square that to target a location on the A-B plane.  

You can localise the intensity further by cubing X, or better yet, apply a variable factor which shifts X closer to 1 before it gets squared or cubed, so you can adjust it to the strength you want.

While you're developing these things I'd map the variables to sliders in the plugin and then you can tune them and when you find good values just hard-code them.

7 hours ago, D Verco said:

Definitely agree that LAB or even HSV are better spaces than the default. And I do think it's surprising the defaults aren't improved to provide better results- especially when the software is mainly known for colour grading. 

People have been calling for many years to be able to change the colour space of lots of controls, the way you can with tools like the Colour Warper in the spiderweb view.

This would make grading so much easier if you could just set the colour space of the Saturation knob to be OKLab and be done with it, etc.

Link to comment
Share on other sites

  • 5 months later...
On 9/23/2025 at 10:59 AM, kye said:

The way I've used it in the past is there's a DCTL that will convert to/from OKLab.  The pain is that it assumes you're in LogC3, so I have to do a CST before/after to get from/to DWG/DI which I use as my timeline colour space.

My next steps were to look at the licensing for the OKLab DCTL, and work out how to just go directly from DWG/DI to OKLab in my DCTL.  I'd assume I'd just need to combine the matrices for DWG->LogC3 and the LogC3->OKLab together into one step, but not sure if that's right.

 


Hi! I just stumbled upon this thread and thought I'd share an OKLab conversion DCTL I wrote about a year ago. It;s written as a header file to be included in any other DCTL you may need it for. It supports conversion from ACES, Davinci Wide and Rec.709/sRGB. 

OKLab_Transform.h:

#line 2

#ifndef ENCODING_ENUMS_DEFINED_IN_UI
enum Encoding
{
    gAcc,
    gAcct,
    gDWI,
    gLIN,
    g709,
    gSRGB
};
#endif
#ifndef COLORSPACE_ENUMS_DEFINED_IN_UI
enum ColorSpace
{
    cACES0,
    cACES1,
    cDWG,
    c709
};
#endif

// =============================================================
// Util
// =============================================================
// =============================================================

__DEVICE__ float powCf(float base, float exp)
{
    return _copysignf(_powf(_fabs(base), exp), base);
}

__DEVICE__ float3 VecMatMul3x3(const float3 m[3], float3 v)
{
    float3 r;
    r.x = m[0].x * v.x + m[0].y * v.y + m[0].z * v.z;
    r.y = m[1].x * v.x + m[1].y * v.y + m[1].z * v.z;
    r.z = m[2].x * v.x + m[2].y * v.y + m[2].z * v.z;
    return r;
}

// =============================================================
// Matrices
// =============================================================
// =============================================================

// These matrices are the concatenated forms of (colorspace -> XYZ -> OKlms)
// or, XYZToLMS @ ColorspaceToXYZ and, XYZToColorspace @ LMSToXYZ
// original matrices are included as comments at the bottom of this script

// ACES (AP0)
// ==============================

__CONSTANT__ float3 mat_ACES0_LMS[3] =
{
    {0.90454662f,   0.26349909f,    -0.15602258f    },
    {0.35107161f,   0.6766934f,     -0.03056591f    },
    {0.13684644f,   0.19250255f,     0.62038067f    }
};

__CONSTANT__ float3 mat_LMS_ACES0[3] =
{
    {1.2881401f,    -0.58554348f,   0.29511118f     },
    {-0.67171287f,  1.76268516f,    -0.08208556f    },
    {-0.0757131f,   -0.41779486f,   1.57228749f     }
};

// ACES (AP1)
// ==============================

__CONSTANT__ float3 mat_ACES1_LMS[3] =
{
    {0.64173446f,   0.35314498f,    0.0171437f      },
    {0.27463463f,   0.63099904f,    0.09156544f     },
    {0.10036508f,   0.18723743f,    0.66212716f     }
};

__CONSTANT__ float3 mat_LMS_ACES1[3] =
{
    {2.04479741f,   -1.17697875f,   0.10982058f     },
    {-0.88115384f,  2.15979229f,    -0.27586256f    },
    {-0.06077576f,  -0.43234352f,   1.57164623f     }
};

// ITU BT.709
// ==============================

__CONSTANT__ float3 mat_709_LMS[3] =
{
    { 0.4122214708f, 0.5363325363f, 0.0514459929f    },
	{ 0.2119034982f, 0.6806995451f, 0.1073969566f    },
	{ 0.0883024619f, 0.2817188376f, 0.6299787005f    }
};

__CONSTANT__ float3 mat_LMS_709[3] =
{
    {  4.0767416621f,  -3.3077115913f,   0.2309699292f   },
    { -1.2684380046f,   2.6097574011f,  -0.3413193965f   },
    { -0.0041960863f,  -0.7034186147f,   1.7076147010f   }
};

// Davinci Wide
// ==============================

__CONSTANT__ float3 mat_DWG_LMS[3] =
{
    { 0.68570951f,  0.45574409f, -0.14156279f },
    { 0.27427422f,  0.81179945f, -0.08604675f },
    { 0.04351009f,  0.15072461f,  0.80624495f }
};

__CONSTANT__ float3 mat_LMS_DWG[3] =
{
    {  1.8836253f,  -1.09713301f, 0.21364045f },
    { -0.63460063f,  1.57752473f, 0.05693684f },
    {  0.01698397f, -0.23570436f, 1.21814431f }
};

// OKLab <-> Cone Response
// ==============================

__CONSTANT__ float3 mat_LMS_LAB[3] =
{
    { 0.2104542553f,  0.7936177850f, -0.0040720468f },
    { 1.9779984951f, -2.4285922050f,  0.4505937099f },
    { 0.0259040371f,  0.7827717662f, -0.8086757660f }
};

__CONSTANT__ float3 mat_LAB_LMS[3] =
{
    { 1.0f,  0.3963377774f,  0.2158037573f },
    { 1.0f, -0.1055613458f, -0.0638541728f },
    { 1.0f, -0.0894841775f, -1.2914855480f }
};

// =============================================================
// Transfer Functions
// =============================================================
// =============================================================

// ACEScc
// ==============================

__DEVICE__ float ACEScc_DecodeBase(float v, float a, float b, float upperClampThreshold, float lowerDecodeThreshold, float two_m16)
{
    float out = v;
    if (v >= upperClampThreshold)
        out = 65504.0f;
    else if (v < lowerDecodeThreshold)
        out = (_exp2f(v * b - a) - two_m16) * 2.0f;
    else
        out = _exp2f(v * b - a);
    return out;
}

__DEVICE__ float3 ACEScc_Decode(float3 in)
{
    const float two_m16 = _exp2f(-16.0f);
    const float a = 9.72f;
    const float b = 17.52f;

    const float lowerDecodeThreshold = (a - 15.0f) / b;
    const float upperClampThreshold = (_log2f(65504.0f) + a) / b;

    float3 out = in;

    out.x = ACEScc_DecodeBase(out.x, a, b, upperClampThreshold, lowerDecodeThreshold, two_m16);
    out.y = ACEScc_DecodeBase(out.y, a, b, upperClampThreshold, lowerDecodeThreshold, two_m16);
    out.z = ACEScc_DecodeBase(out.z, a, b, upperClampThreshold, lowerDecodeThreshold, two_m16);

    return out;
}

__DEVICE__ float ACEScc_EncodeBase(float v, float a, float b, float negConstant, float two_m15, float two_m16)
{
    float out;
    if (v < 0.0f)
        out = negConstant;
    else if (v < two_m15)
        out = (_log2f(two_m16 + v * 0.5f) + a) / b;
    else
        out = (_log2f(v) + a) / b;
    return out;
}

__DEVICE__ float3 ACEScc_Encode(float3 in)
{
    const float two_m16 = _exp2f(-16.0f);
    const float two_m15 = _exp2f(-15.0f);
    const float a = 9.72f;
    const float b = 17.52f;

    const float negConstant = (_log2f(two_m16) + a) / b;

    float3 out = in;

    out.x = ACEScc_EncodeBase(out.x, a, b, negConstant, two_m15, two_m16);
    out.y = ACEScc_EncodeBase(out.y, a, b, negConstant, two_m15, two_m16);
    out.z = ACEScc_EncodeBase(out.z, a, b, negConstant, two_m15, two_m16);

    return out;
}

// ACEScct
// ==============================

__DEVICE__ float3 ACEScct_Encode(float3 in)
{
    const float a = 9.72f;
    const float b = 17.52f;

    const float X_BRK = 0.0078125f;
    const float A = 10.5402377416545f;
    const float B = 0.0729055341958355f;

    float3 out;

    out.x = (in.x <= X_BRK) ? (A * in.x + B) : ((_log2f(in.x) + a) / b);
    out.y = (in.y <= X_BRK) ? (A * in.y + B) : ((_log2f(in.y) + a) / b);
    out.z = (in.z <= X_BRK) ? (A * in.z + B) : ((_log2f(in.z) + a) / b);

    return out;
}

__DEVICE__ float3 ACEScct_Decode(float3 in)
{
    const float a = 9.72f;
    const float b = 17.52f;

    const float Y_BRK = 0.155251141552511f;
    const float A = 10.5402377416545f;
    const float B = 0.0729055341958355f;

    float3 out = in;

    out.x = (in.x > Y_BRK) ? _exp2f(in.x * b - a) : ((in.x - B) / A);
    out.y = (in.y > Y_BRK) ? _exp2f(in.y * b - a) : ((in.y - B) / A);
    out.z = (in.z > Y_BRK) ? _exp2f(in.z * b - a) : ((in.z - B) / A);

    return out;
}

// Davinci Intermediate
// ==============================

__DEVICE__ float3 DWI_Decode(float3 in)
{
    float3 out = in;

    float a = 0.0075;
    float b = 7.0;
    float c = 0.07329248;
    float m = 10.44426855;
    float log_cut = 0.02740668;

    out.x = in.x > log_cut ? powCf(2.0f, (in.x / c) - b) - a : in.x / m;
    out.y = in.y > log_cut ? powCf(2.0f, (in.y / c) - b) - a : in.y / m;
    out.z = in.z > log_cut ? powCf(2.0f, (in.z / c) - b) - a : in.z / m;

    return out;
}

__DEVICE__ float3 DWI_Encode(float3 in)
{
    float3 out = in;

    float a = 0.0075;
    float b = 7.0;
    float c = 0.07329248;
    float m = 10.44426855;
    float lin_cut = 0.00262409;

    out.x = in.x > lin_cut ? (_log2f(in.x + a) + b) * c : in.x * m;
    out.y = in.y > lin_cut ? (_log2f(in.y + a) + b) * c : in.y * m;
    out.z = in.z > lin_cut ? (_log2f(in.z + a) + b) * c : in.z * m;

    return out;
}

// ITU BT.709
// ==============================

__DEVICE__ float3 BT709_Decode(float3 in)
{
    float3 out = in;

    out.x = out.x < 0.081f ? out.x / 4.5f : powCf((out.x + 0.099f) / 1.099f, 1.0f / 0.45f);
    out.y = out.y < 0.081f ? out.y / 4.5f : powCf((out.y + 0.099f) / 1.099f, 1.0f / 0.45f);
    out.z = out.z < 0.081f ? out.z / 4.5f : powCf((out.z + 0.099f) / 1.099f, 1.0f / 0.45f);

    return out;
}

__DEVICE__ float3 BT709_Encode(float3 in)
{
    float3 out = in;

    out.x = out.x < 0.018 ? out.x * 4.5f : 1.099f * powCf(out.x, 0.45f) - 0.099f;
    out.y = out.y < 0.018 ? out.y * 4.5f : 1.099f * powCf(out.y, 0.45f) - 0.099f;
    out.z = out.z < 0.018 ? out.z * 4.5f : 1.099f * powCf(out.z, 0.45f) - 0.099f;

    return out;
}

// sRGB
// ==============================

__DEVICE__ float3 sRGB_Decode(float3 in)
{
    float3 out = in;

    out.x = out.x < 0.04045 ? out.x / 12.92f : powCf((out.x + 0.055f) / 1.055f, 2.4f);
    out.y = out.y < 0.04045 ? out.y / 12.92f : powCf((out.y + 0.055f) / 1.055f, 2.4f);
    out.z = out.z < 0.04045 ? out.z / 12.92f : powCf((out.z + 0.055f) / 1.055f, 2.4f);

    return out;
}

__DEVICE__ float3 sRGB_Encode(float3 in)
{
    float3 out = in;

    out.x = out.x < 0.0031308 ? out.x * 12.92f : 1.055f * powCf(out.x, 1.0f / 2.4f) - 0.055f;
    out.y = out.y < 0.0031308 ? out.y * 12.92f : 1.055f * powCf(out.y, 1.0f / 2.4f) - 0.055f;
    out.z = out.z < 0.0031308 ? out.z * 12.92f : 1.055f * powCf(out.z, 1.0f / 2.4f) - 0.055f;

    return out;
}

// =============================================================
// Convert
// =============================================================
// =============================================================

__DEVICE__ float3 Decode(float3 in, int tFunction)
{
    float3 out = in;
    switch (tFunction)
    {
        case gAcc:
            out = ACEScc_Decode(in);
            break;
        case gAcct:
            out = ACEScct_Decode(in);
            break;
        case gDWI:
            out = DWI_Decode(in);
            break;
        case g709:
            out = BT709_Decode(in);
            break;
        case gSRGB:
            out = sRGB_Decode(in);
            break;
    }
    return out;
}

__DEVICE__ float3 Encode(float3 in, int tFunction)
{
    float3 out = in;
    switch (tFunction)
    {
        case gAcc:
            out = ACEScc_Encode(in);
            break;
        case gAcct:
            out = ACEScct_Encode(in);
            break;
        case gDWI:
            out = DWI_Encode(in);
            break;
        case g709:
            out = BT709_Encode(in);
            break;
        case gSRGB:
            out = sRGB_Encode(in);
            break;
    }
    return out;
}

__DEVICE__ float3 OKLab_OKLCh(float3 lab)
{
    float C = _hypotf(lab.y, lab.z);
    float h = _atan2f(lab.z, lab.y);
    return make_float3(lab.x, C, h);
}

__DEVICE__ float3 OKLCh_OKLab(float3 lch)
{
    float a = lch.y * cosf(lch.z);
    float b = lch.y * sinf(lch.z);
    return make_float3(lch.x, a, b);
}

__DEVICE__ float3 RGB_OKLab(float3 rgb, int colorspace)
{
    const float3* mat;
    switch (colorspace)
    {
        case cACES0:
            mat = mat_ACES0_LMS;
            break;
        case cACES1:
            mat = mat_ACES1_LMS;
            break;
        case cDWG:
            mat = mat_DWG_LMS;
            break;
        case c709:
            mat = mat_709_LMS;
            break;
    }

    float3 lms = VecMatMul3x3(mat, rgb);

    float3 lms_;
    lms_.x = cbrt(lms.x);
    lms_.y = cbrt(lms.y);
    lms_.z = cbrt(lms.z);

    return VecMatMul3x3(mat_LMS_LAB, lms_);
}

__DEVICE__ float3 OKLab_RGB(float3 lab, int colorspace)
{
    const float3* mat;
    switch (colorspace)
    {
        case cACES0:
            mat = mat_LMS_ACES0;
            break;
        case cACES1:
            mat = mat_LMS_ACES1;
            break;
        case cDWG:
            mat = mat_LMS_DWG;
            break;
        case c709:
            mat = mat_LMS_709;
            break;
    }

    float3 lms = VecMatMul3x3(mat_LAB_LMS, lab);

    lms.x = powCf(lms.x, 3.0f);
    lms.y = powCf(lms.y, 3.0f);
    lms.z = powCf(lms.z, 3.0f);

    return VecMatMul3x3(mat, lms);
}

__DEVICE__ float3 RGB_OkLCh(float3 rgb, int colorspace)
{
    float3 lab = RGB_OKLab(rgb, colorspace);
    return OKLab_OKLCh(lab);
}

__DEVICE__ float3 OKLCh_RGB(float3 lch, int colorspace)
{
    float3 lab = OKLCh_OKLab(lch);
    return OKLab_RGB(lab, colorspace);
}

// =============================================================
// Ref Matrices
// =============================================================
// =============================================================

// DWG -> XYZ
    //  0.70062239,  0.14877482,   0.10105872
    //  0.27411851,  0.87363190,  -0.14775041
    // -0.09896291, -0.13789533,   1.32591599

// XYZ -> DWG
    //   1.51667204, -0.28147805, -0.14696363
    //  -0.46491710,  1.25142378,  0.17488461
    //   0.06484905,  0.10913934,  0.76141462

// 709 -> XYZ
    //  0.4123908,   0.35758434,    0.18048079
    //  0.21263901,  0.71516868,    0.07219232
    //  0.01933082,  0.11919478,    0.95053215

// XYZ -> 709
    //  3.24096994, -1.53738318,    -0.49861076
    // -0.96924364,  1.8759675,      0.04155506
    //  0.05563008, -0.20397696 ,    1.05697151

// XYZ -> LMS
    // 0.8189330101, 0.3618667424, -0.1288597137
    // 0.0329845436, 0.9293118715,  0.0361456387
    // 0.0482003018, 0.2643662691,  0.6338517070

// LMS -> XYZ
    //   1.22701385, -0.55779998,  0.28125615
    //  -0.04058018,  1.11225687, -0.07167668
    //  -0.07638128, -0.42148198,  1.58616322


Using it another DCTL looks something like this:
 

#line 2

#define ENCODING_ENUMS_DEFINED_IN_UI
#define COLORSPACE_ENUMS_DEFINED_IN_UI
#include "OKLab_Transforms.h"

DEFINE_UI_PARAMS(p_InCSpace, Input Color Space, DCTLUI_COMBO_BOX, 2, {cACES0, cACES1, cDWG, c709}, {ACES (AP0), ACES (AP1), Davinci Wide Gamut, Rec.709 / sRGB / BT.1886});
DEFINE_UI_PARAMS(p_InGamma, Input Gamma, DCTLUI_COMBO_BOX, 2, {gAcc, gAcct, gDWI, gLIN, g709, gSRGB}, {ACEScc, ACEScct, Davinci Intermediate, Linear, Rec.709, sRGB});

__DEVICE__ float3 transform(int p_Width, int p_Height, int p_X, int p_Y, float p_R, float p_G, float p_B)
{
    float3 in = make_float3(p_R, p_G, p_B);
    float3 out = in;

    // Convert to OKLCh:
    float3 linear = Decode(in, p_InGamma);
    float3 oklch = RGB_OkLCh(linear, p_InCSpace);

    // Convert back:
    linear = OKLCh_RGB(oklch, p_InCSpace);
    out = Encode(linear, p_InGamma);

    return out;
}

 

Link to comment
Share on other sites

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now
 Share

  • EOSHD Pro Color 5 for All Sony cameras
    EOSHD C-LOG and Film Profiles for All Canon DSLRs
    EOSHD Dynamic Range Enhancer for H.264/H.265
×
×
  • Create New...