VEX/VOP - Overlay /other blending modes?

   2485   5   0
User Avatar
Member
4 posts
Joined: April 2016
Offline
Hey,
is there a way to change @Cd by another attribute(ramp),
but in a way that Overlay blending mode does?

Let say i have @Cd and f@mask (0 to 1 range) or v@colorgradient attribute
and i want to affect @Cd using @mask/@colorgradaient like overlay blending mode does.

Is there a relatively simple way to do this or i have to do some more complicated math?
Edited by Ya_rp - Aug. 26, 2021 07:37:36
Houdini Indie / Redshift
instagram.com/jakubrupa
User Avatar
Member
4523 posts
Joined: Feb. 2012
Offline
Hi,

You can use Color Mix VOP for some basic blending operations:
https://www.sidefx.com/docs/houdini/nodes/vop/colormix.html [www.sidefx.com]

For an exhaustive Photoshop style blending, you need to implement your own:
https://www.adobe.com/content/dam/acom/en/devnet/pdf/pdf_reference_archive/blend_modes.pdf [www.adobe.com]
Senior FX TD @ Industrial Light & Magic
Get to the NEXT level in Houdini & VEX with Pragmatic VEX! [www.pragmatic-vfx.com]

youtube.com/@pragmaticvfx | patreon.com/animatrix | pragmaticvfx.gumroad.com
User Avatar
Member
4 posts
Joined: April 2016
Offline
Thank you animatrix,
this is what i wanted to know.
Colormix works, but gives me washed out colors.
So there is no easy premade solution.
Implementing my own is to much for me, for now.
But now i have an idea how to do this on the Redshift side.

Thanks again animatrix
Houdini Indie / Redshift
instagram.com/jakubrupa
User Avatar
Member
4523 posts
Joined: Feb. 2012
Offline
If you want to blend colors intuitively I would recommend using the LCh color space:
https://en.wikipedia.org/wiki/HCL_color_space [en.wikipedia.org]

LCh is a perceptually uniform reference color space based on actual studies of how people perceive colors, which is just a polar transform of LAB.

https://www.wolfram.com/mathematica/new-in-10/enhanced-color-support-and-processing/cie-lchab-rendered-in-lab-space.html [www.wolfram.com]


I implemented this years ago as a VOP:
https://www.dropbox.com/s/ty9vsuz3oqrza15/BlendColor_VOP_Animatrix.otl?dl=1 [www.dropbox.com]



By default this operator blends colors using the LCh color space.

To implement the LCh color space, you first need to implement the Lab color space. Note that the Lab color space provided by this operator is different than the one in Houdini. If you use the Lab color space available in Houdini by default to implement the LCh color space, you won't get correct results. At least in my extensive tests.

RFE pending.



Here is the code for completeness:

{
    //  Original color conversion methods from ColorMine library
    //  http://github.com/THEjoezack/ColorMine
    
    
    
    //  Conversion methods to LAB color space
    
    float pivotxyz ( float n )
    {
        float epsilon = 0.008856;
        float kappa = 903.3;
        return n > epsilon ? cbrt ( n ) : ( kappa * n + 16 ) / 116;
    }
    
    vector xyz2lab ( vector xyz )
    {
        vector white = { 95.047, 100.000, 108.883 };
        
        float x = pivotxyz ( xyz.x / white.x );
        float y = pivotxyz ( xyz.y / white.y );
        float z = pivotxyz ( xyz.z / white.z );
        
        float l = max ( 0, 116 * y - 16 );
        float a = 500 * ( x - y );
        float b = 200 * ( y - z );
        
        return set ( l, a, b );
    }
    
    vector lch2lab ( vector lch )
    {
        float h = radians ( lch.z );
        
        float l = lch.x;
        float a = cos ( h ) * lch.y;
        float b = sin ( h ) * lch.y;
        
        return set ( l, a, b );
    }
    
    
    
    
    //  Conversion methods to XYZ color space
    
    vector lab2xyz ( vector lab )
    {
        float epsilon = 0.008856;
        float kappa = 903.3;
        vector white = { 95.047, 100.000, 108.883 };
        
        float y = ( lab.x + 16.0 ) / 116.0;
        float x = lab.y / 500.0 + y;
        float z = y - lab.z / 200.0;
        float x3 = x * x * x;
        float z3 = z * z * z;
        
        x = white.x * ( x3 > epsilon ? x3 : ( x - 16.0 / 116.0 ) / 7.787 );
        y = white.y * ( lab.x > ( kappa * epsilon ) ? pow ( ( ( lab.x + 16.0 ) / 116.0 ), 3 ) : lab.x / kappa );
        z = white.z * ( z3 > epsilon ? z3 : ( z - 16.0 / 116.0 ) / 7.787 );
        
        return set ( x, y, z );
    }
    
    vector lch2xyz ( vector lch )
    {
        return lab2xyz ( lch2lab ( lch ) );
    }
    
    float pivotrgb ( float n )
    {
        return ( n > 0.04045 ? pow ( ( n + 0.055 ) / 1.055, 2.4 ) : n / 12.92 ) * 100.0;
    }
    
    vector rgb2xyz ( vector rgb )
    {
        float r = pivotrgb ( rgb.x / 255.0 );
        float g = pivotrgb ( rgb.y / 255.0 );
        float b = pivotrgb ( rgb.z / 255.0 );
        
        //  Observer = 2°, Illuminant = D65
        float x = r * 0.4124 + g * 0.3576 + b * 0.1805;
        float y = r * 0.2126 + g * 0.7152 + b * 0.0722;
        float z = r * 0.0193 + g * 0.1192 + b * 0.9505;
        
        return set ( x, y, z );
    }
    
    
    
    //  Conversion methods to LAB color space [Separated due to VEX compiler limitations]
    
    vector rgb2lab ( vector rgb )
    {
        return xyz2lab ( rgb2xyz ( rgb ) );
    }
    
    
    
    //  Conversion methods to LCH color space
    
    vector lab2lch ( vector lab )
    {
        float h = atan2 ( lab.z, lab.y );
        h = degrees ( h );
        
        if ( h < 0 )
            h += 360.0;
        else if ( h >= 360 )
            h -= 360.0;
            
        float l = lab.x;
        float c = sqrt ( lab.y * lab.y + lab.z * lab.z );
        
        return set ( l, c, h );
    }
    
    vector xyz2lch ( vector xyz )
    {
        return lab2lch ( xyz2lab ( xyz ) );
    }
    
    
    vector rgb2lch ( vector rgb )
    {
        return lab2lch ( rgb2lab ( rgb ) );
    }
    
    
    
    //  Conversion methods to RGB color space
    
    vector xyz2rgb ( vector xyz )
    {
        //  Observer = 2°, Illuminant = D65
        float x = xyz.x / 100.0;
        float y = xyz.y / 100.0;
        float z = xyz.z / 100.0;
        
        float r = x * 3.2406 + y * -1.5372 + z * -0.4986;
        float g = x * -0.9689 + y * 1.8758 + z * 0.0415;
        float b = x * 0.0557 + y * -0.2040 + z * 1.0570;
        
        r = r > 0.0031308 ? 1.055 * pow ( r, 1 / 2.4 ) - 0.055 : 12.92 * r;
        g = g > 0.0031308 ? 1.055 * pow ( g, 1 / 2.4 ) - 0.055 : 12.92 * g;
        b = b > 0.0031308 ? 1.055 * pow ( b, 1 / 2.4 ) - 0.055 : 12.92 * b;
        
        r = clamp ( r * 255, 0, 255 );
        g = clamp ( g * 255, 0, 255 );
        b = clamp ( b * 255, 0, 255 );
        
        return set ( r, g, b );
    }
    
    vector lab2rgb ( vector lab )
    {
        return xyz2rgb ( lab2xyz ( lab ) );
    }
    
    vector lch2rgb ( vector lch )
    {
        return lab2rgb ( lch2lab ( lch ) );
    }
    
    
    
    //  Shortest path interpolation for HSV/HSL color models
    //  SESI's implementation sometimes returns the shortest, sometimes the longest path
    vector blendHsvHsl ( vector c0; vector c1; float amount )
    {
        vector diff = c1 - c0;
        if ( diff.x > 0.5 )
            diff.x -= 1;
        else if ( diff.x < -0.5 )
            diff.x += 1;
            
        return c0 + diff * amount;
    }
    
    //  Shortest path interpolation for LAB/LCH color models
    vector blendLabLch ( vector c0; vector c1; float amount )
    {
        vector diff = c1 - c0;
        if ( diff.z > 180 )
            diff.z -= 360;
        else if ( diff.z < -180 )
            diff.z += 360;
            
        return c0 + diff * amount;
    }
    
    
    
    vector blendColor ( vector a; vector b; float amount; int sourceSpace; int blendingSpace )
    {
        vector c = a;
        vector d = b;
        vector f = 0;
        string spaces [ ] = array ( "cspace:rgb", "cspace:hsl", "cspace:hsv", "cspace:XYZ", "cspace::tmi", "cspace:Lab" );
        if ( blendingSpace == 5 ) //    LAB
        {
            if ( sourceSpace == 3 ) //  XYZ
            {
                c = xyz2lab ( c );
                d = xyz2lab ( d );
            }
            else if ( sourceSpace == 6 ) //  LCH
            {
                c = lch2lab ( c );
                d = lch2lab ( d );
            }
            else if ( sourceSpace != 5 ) //  not LAB
            {
                if ( sourceSpace != 0 ) //  not RGB
                {
                    c = ctransform ( spaces [ sourceSpace ], "cspace:rgb", c );
                    d = ctransform ( spaces [ sourceSpace ], "cspace:rgb", d );
                }
                c = rgb2lab ( c );
                d = rgb2lab ( d );
            }
            
            f = blendLabLch ( c, d, amount );
            
            if ( sourceSpace == 3 ) //  XYZ
                f = lab2xyz ( f );
            else if ( sourceSpace == 6 ) //  LCH
                f = lab2lch ( f );
            else if ( sourceSpace != 6 ) //  not LCH
            {
                f = lab2rgb ( f );
                if ( sourceSpace != 0 ) //  not RGB
                    f = ctransform ( "cspace:rgb", spaces [ sourceSpace ], f );
            }
        }
        else if ( blendingSpace == 6 ) //    LCH
        {
            if ( sourceSpace == 3 ) //  XYZ
            {
                c = xyz2lch ( c );
                d = xyz2lch ( d );
            }
            else if ( sourceSpace == 5 ) //  LAB
            {
                c = lab2lch ( c );
                d = lab2lch ( d );
            }
            else if ( sourceSpace != 6 ) //  not LCH
            {
                if ( sourceSpace != 0 ) //  not RGB
                {
                    c = ctransform ( spaces [ sourceSpace ], "cspace:rgb", c );
                    d = ctransform ( spaces [ sourceSpace ], "cspace:rgb", d );
                }
                c = rgb2lch ( c );
                d = rgb2lch ( d );
            }
            
            f = blendLabLch ( c, d, amount );
            
            if ( sourceSpace == 3 ) //  XYZ
                f = lch2xyz ( f );
            else if ( sourceSpace == 5 ) //  LAB
                f = lch2lab ( f );
            else if ( sourceSpace != 6 ) //  not LCH
            {
                f = lch2rgb ( f );
                if ( sourceSpace != 0 ) //  not RGB
                    f = ctransform ( "cspace:rgb", spaces [ sourceSpace ], f );
            }
        }
        else
        {
            c = ctransform ( spaces [ sourceSpace ], spaces [ blendingSpace ], c );
            d = ctransform ( spaces [ sourceSpace ], spaces [ blendingSpace ], d );
            
            if ( blendingSpace == 1 || blendingSpace == 2 ) //  Shortest path blending for HSV & HSL
                f = blendHsvHsl ( c, d, amount );
            else
                f = c + ( d - c ) * amount;
            
            f = ctransform ( spaces [ blendingSpace ], spaces [ sourceSpace ], f );
        }
        return f;
    }
    
    $cnew = blendColor ( $c0, $c1, $amount, $source, $blending );
}
Senior FX TD @ Industrial Light & Magic
Get to the NEXT level in Houdini & VEX with Pragmatic VEX! [www.pragmatic-vfx.com]

youtube.com/@pragmaticvfx | patreon.com/animatrix | pragmaticvfx.gumroad.com
User Avatar
Member
117 posts
Joined: Aug. 2017
Offline
Have Fun

Attachments:
101.jpeg (29.0 KB)
102.jpeg (22.5 KB)
103.jpeg (17.0 KB)
104.jpeg (42.8 KB)
105.jpeg (40.8 KB)

Conservation of Momentum
User Avatar
Member
8583 posts
Joined: July 2007
Online
there is also Composite VOP with a few modes (overlay is one of them)
Tomas Slancik
FX Supervisor
Method Studios, NY
  • Quick Links