Code & Libraries

Mixwell_GelGit.osl

OSLRDF pass

Applies Gel-Git (bidirectional comb) reverse-drift to pMixwell.

// SPDX-License-Identifier: MIT
// Copyright (c) 2026 Doug L. James and Ethan James

///////////////////////////////////////////////////////////////////////
////  Mixwell Gel-Git RDF Shader for Redshift OSL                    //
////  Applies Gel-Git (bidirectional comb) reverse-drift to pMixwell //
////  @author Doug L James, 2026                                     //
///////////////////////////////////////////////////////////////////////

// Constants
#define PI 3.14159265358979323846
#define RDSEGMENT_MASK_R_FACTOR 10.0

/*** UTILITIES ***/

vector rot90(vector v) {
    return vector(-v[1], v[0], 0.0);
}

/*** RDF CORE FUNCTIONS ***/

// Line RDF: Matched series approximation to 1D drift.
// Input: height above flow line, and cylinder radius, r.
float rdLine1DS(float height, float r) {
    float y = height / r;
    float dx = 0.0;
    float eps = 0.002;
    y = sqrt(y * y + eps * eps);
    
    if (y > 6.50416858646776) {
        float h = 1.0 / y;
        float h2 = h * h;
        float h3 = h * h2;
        float poly = ((((6615.0 * h2 - 1960.0) * h2 + 600.0) * h2 - 192.0) * h2 + 64.0);
        dx = -(PI / 256.0) * h3 * poly;
    }
    else if (y > 0.8220844420096408) {
        float dy = y - 2.3;
        float num = (((((-1.01973e-7 * dy + 1.77539e-6) * dy - 0.0068946) * dy - 0.043355) * dy - 0.0937878) * dy - 0.0413839);
        float den = ((((((0.00875686 * dy + 0.115772) * dy + 0.665761) * dy + 2.08615) * dy + 3.66495) * dy + 3.26035) * dy + 1.0);
        dx = num / den;
    }
    else {
        float z = y * y;
        float A = ((((0.00015811568 * z - 0.00096477622) * z + 0.0076619254) * z - 0.16369764) * z - 0.039720771);
        float B = ((((-0.00018775463 * z + 0.0010681152) * z - 0.0073242188) * z + 0.09375) * z + 0.5);
        dx = A + B * log(y);
    }
    return 2.0 * r * dx;
}

// Min distance from y to a tine of a comb with pitch hy, rooted at the origin.
float minCombDist(float y, float hy) {
    return hy * abs(mod(y / hy - 0.5, 1.0) - 0.5);
}

// RDF for Gel-Git pattern.
// Alternating forward/backward combing creates characteristic zigzag patterns.
vector rdGelGit(vector p, float combR, vector dir_in, float combGap, int nPasses) {
    vector dir = normalize(dir_in);
    vector n = rot90(dir);
    float y = dot(p, n);
    float passGap = (float)nPasses * combGap * 2.0;
    vector rdf = vector(0.0, 0.0, 0.0);
    
    // GIT ("left") - combing in negative direction
    for (int k = 0; k < nPasses; k++) {
        float yOffset = (0.5 + (float)k) * combGap * 2.0;
        float height = minCombDist(y + yOffset, passGap);
        rdf -= rdLine1DS(height, combR) * dir;
    }
    
    // GEL ("right") - combing in positive direction
    for (int k2 = 0; k2 < nPasses; k2++) {
        float yOffset = (float)k2 * combGap * 2.0;
        float height = minCombDist(y + yOffset, passGap);
        rdf += rdLine1DS(height, combR) * dir;
    }
    
    return rdf;
}

///////////////////////////////////////////////////////////////////////
////  MAIN SHADER: Gel-Git Pass                                      //
///////////////////////////////////////////////////////////////////////

shader Mixwell_GelGit(
    // Input position
    vector pMixwell_in = 0
        [[ string label = "pMixwell In",
           string help = "Input 2D Mixwell position" ]],
    
    // Gel-Git parameters
    float tineRadius = 0.05
        [[ string label = "Tine Radius",
           string help = "Radius of combing tines",
           float min = 0.001,
           float max = 0.5 ]],
    float angle = 0.0
        [[ string label = "Angle (deg)",
           string help = "Combing direction (0 = +X direction)",
           float min = 0.0,
           float max = 360.0 ]],
    float combGap = 0.125
        [[ string label = "Comb Gap",
           string help = "Spacing between tines",
           float min = 0.001,
           float max = 1.0 ]],
    int nPasses = 2
        [[ string label = "N Passes",
           string help = "Number of interleaved comb passes",
           int min = 1,
           int max = 10 ]],
    
    // Outputs
    output vector pMixwell = 0
        [[ string label = "pMixwell Out",
           string help = "Output 2D Mixwell position with RDF applied" ]],
    output vector displacement = 0
        [[ string label = "Displacement",
           string help = "RDF displacement vector from this pass" ]],
    output float displacementMag = 0
        [[ string label = "Displacement Magnitude" ]]
)
{
    // Compute direction from angle
    float ang = radians(angle);
    vector dir = vector(cos(ang), sin(ang), 0.0);
    
    // Compute Gel-Git RDF
    vector rdf = rdGelGit(pMixwell_in, tineRadius, dir, combGap, nPasses);
    
    // Output displaced position and displacement
    pMixwell = pMixwell_in + rdf;
    displacement = rdf;
    displacementMag = length(rdf);
}

Part of Mixwell OSL for Houdini and Redshift. Released under the MIT license.