Mixwell_PaintColorMaps.osl
Samples the paint pattern at the drifted pMixwell position. Supports several patterns and color palettes.
// SPDX-License-Identifier: MIT
// Copyright (c) 2026 Doug L. James and Ethan James
///////////////////////////////////////////////////////////////////////
//// Mixwell Paint ColorMaps Shader for Redshift OSL //
//// Samples paint pattern at pMixwell position //
//// Supports multiple patterns and color palettes //
//// @author Doug L James, 2026 //
///////////////////////////////////////////////////////////////////////
/*** UTILITIES ***/
float iqhash(float n) {
return mod(sin(n) * 43758.5453, 1.0);
}
/*** PALETTES ***/
color paletteGoldenMeadow(int idx) {
// fb6107, 5c8001, fbb02d, 7cb518, f3de2c
if (idx == 0) return color(0.9843137, 0.3803922, 0.0274510); // Blaze Orange
if (idx == 1) return color(0.3607843, 0.5019608, 0.0039216); // Forest Moss
if (idx == 2) return color(0.9843137, 0.6901961, 0.1764706); // Sunflower Gold
if (idx == 3) return color(0.4862745, 0.7098039, 0.0941176); // Lime Moss
if (idx == 4) return color(0.9529412, 0.8705882, 0.1725490); // Golden Glow
return color(0.9529412, 0.8705882, 0.1725490);
}
color paletteCoolors1(int idx) {
// 1a344d, 5a1800, 1e8eb6, fc9843, f7f5f6 (Brown Blue)
if (idx == 0) return color(0.1019608, 0.2039216, 0.3019608); // Dark Blue
if (idx == 1) return color(0.3529412, 0.09411765, 0.0); // Dark Brown
if (idx == 2) return color(0.1176471, 0.5568628, 0.7137255); // Sky Blue
if (idx == 3) return color(0.9882353, 0.5960785, 0.2627451); // Orange
if (idx == 4) return color(0.9686275, 0.9607843, 0.9647059); // Off White
return color(0.9686275, 0.9607843, 0.9647059);
}
color paletteClassic(int idx) {
if (idx == 0) return color(0.7, 0.1, 0.1); // Deep Red
if (idx == 1) return color(0.1, 0.2, 0.5); // Navy Blue
if (idx == 2) return color(0.85, 0.7, 0.3); // Gold
if (idx == 3) return color(0.2, 0.4, 0.3); // Forest Green
if (idx == 4) return color(0.95, 0.93, 0.88); // Cream
return color(0.95, 0.93, 0.88);
}
color paletteOcean(int idx) {
if (idx == 0) return color(0.0, 0.2, 0.4); // Deep Ocean
if (idx == 1) return color(0.0, 0.5, 0.6); // Teal
if (idx == 2) return color(0.3, 0.7, 0.8); // Aqua
if (idx == 3) return color(0.9, 0.95, 0.97); // Sea Foam
if (idx == 4) return color(0.1, 0.3, 0.35); // Dark Teal
return color(0.9, 0.95, 0.97);
}
color paletteSunset(int idx) {
if (idx == 0) return color(0.9, 0.3, 0.1); // Burnt Orange
if (idx == 1) return color(0.95, 0.6, 0.2); // Tangerine
if (idx == 2) return color(0.98, 0.85, 0.4); // Pale Yellow
if (idx == 3) return color(0.6, 0.2, 0.4); // Mauve
if (idx == 4) return color(0.3, 0.1, 0.3); // Deep Purple
return color(0.98, 0.85, 0.4);
}
color paletteMonochrome(int idx) {
if (idx == 0) return color(0.1, 0.1, 0.1); // Near Black
if (idx == 1) return color(0.3, 0.3, 0.3); // Dark Gray
if (idx == 2) return color(0.5, 0.5, 0.5); // Medium Gray
if (idx == 3) return color(0.75, 0.75, 0.75); // Light Gray
if (idx == 4) return color(0.95, 0.95, 0.95); // Near White
return color(0.95, 0.95, 0.95);
}
color paletteEarth(int idx) {
if (idx == 0) return color(0.55, 0.27, 0.07); // Sienna
if (idx == 1) return color(0.36, 0.25, 0.20); // Dark Brown
if (idx == 2) return color(0.76, 0.60, 0.42); // Tan
if (idx == 3) return color(0.42, 0.56, 0.14); // Olive Green
if (idx == 4) return color(0.96, 0.87, 0.70); // Wheat
return color(0.96, 0.87, 0.70);
}
color getPaletteColor(int paletteIdx, int colorIdx) {
int idx = colorIdx % 6; // Wrap to valid range
if (paletteIdx == 0) return paletteGoldenMeadow(idx);
if (paletteIdx == 1) return paletteCoolors1(idx);
if (paletteIdx == 2) return paletteClassic(idx);
if (paletteIdx == 3) return paletteOcean(idx);
if (paletteIdx == 4) return paletteSunset(idx);
if (paletteIdx == 5) return paletteMonochrome(idx);
if (paletteIdx == 6) return paletteEarth(idx);
return paletteGoldenMeadow(idx);
}
/*** PAINT PATTERNS ***/
// Splat blob rows helper
color splatBlobRowsWithPalette(vector p, color colBG, int paletteIdx, int colorIdx,
float HX, float HY, float dY, float blobRadius, int isOddRow) {
color colBlob = getPaletteColor(paletteIdx, colorIdx);
float Yoffset = (isOddRow != 0) ? 0.5 * HY : 0.0;
float ySnap = Yoffset + round((p[1] - Yoffset) / HY) * HY;
float py = p[1] - ySnap;
float px = p[0] - sin(437.5453 * ySnap) * 437.5453;
float F = 1239.0 + ySnap;
float x0 = HX * floor(px / HX);
float x1 = x0 + HX;
vector p0 = vector(x0, dY * sin(F * x0), 0.0);
vector p1 = vector(x1, dY * sin(F * x1), 0.0);
vector pLocal = vector(px, py, 0.0);
float D0 = dot(pLocal - p0, pLocal - p0);
float D1 = dot(pLocal - p1, pLocal - p1);
float iC = (D0 < D1) ? round(x0 / HX) : round(x1 / HX);
float xC = iC * HX;
float xL = xC - HX;
float xR = xC + HX;
float yC = dY * sin(F * xC);
float yL = dY * sin(F * xL);
float yR = dY * sin(F * xR);
vector pC = vector(xC, yC, 0.0);
vector pL = vector(xL, yL, 0.0);
vector pR = vector(xR, yR, 0.0);
float e2 = 0.00001 * HX * HX;
float DC = dot(pLocal - pC, pLocal - pC) + e2;
float DL = dot(pLocal - pL, pLocal - pL) + e2;
float DR = dot(pLocal - pR, pLocal - pR) + e2;
float f = 1.0 / DC - 1.0 / DL - 1.0 / DR;
f -= 1.0 / (blobRadius * blobRadius);
float reg = smoothstep(HX, 2.0 * HX, max(0.0, f));
return mix(colBG, colBlob, reg);
}
// Blob pattern with palette
color paintBlobsWithPalette(vector p, int paletteIdx, float blobScale) {
vector ps = p / blobScale;
float HX = 0.25;
float HY = 1.0;
float dY = HX / 10.0;
color col = color(0, 0, 0);
col = splatBlobRowsWithPalette(ps, col, paletteIdx, 0, HX, HY, dY, 2.0 * HX, 0);
col = splatBlobRowsWithPalette(ps, col, paletteIdx, 1, HX, HY, dY, 2.0 * HX, 1);
col = splatBlobRowsWithPalette(ps, col, paletteIdx, 2, HX, HY, dY, HX / 2.0, 0);
col = splatBlobRowsWithPalette(ps, col, paletteIdx, 3, HX, HY, dY, HX / 3.0, 1);
col = splatBlobRowsWithPalette(ps, col, paletteIdx, 4, HX, HY, dY, HX / 6.0, 0);
col = splatBlobRowsWithPalette(ps, col, paletteIdx, 5, HX, HY, dY, HX / 7.0, 1);
return col;
}
// Chocolate pattern (fixed colors)
color paintChocolate(vector q) {
float theta = 0.3;
float c = cos(theta);
float s = sin(theta);
vector pRot = vector(c * q[0] - s * q[1], s * q[0] + c * q[1], 0.0);
float k0 = 45.0;
float k1 = 29.0;
float phi0 = 3.0;
float phi1 = 2.0;
color col0 = color(0.6862745098, 0.4, 0.1490196078); // Caramel
color col1 = color(0.6901960784, 0.9411764706, 0.9921568627); // Light Cyan
color colChocolate = color(0.1490196078, 0.0862745098, 0.0901960784); // Dark Chocolate
float splat0 = smoothstep(0.5, 0.64,
sin(k0 * pRot[0] * (1.0 - 0.153 * pRot[0]) + phi0) *
sin(k0 * pRot[1] * (1.0 + 0.6 * pRot[0]) + phi0) *
(1.0 - 0.2 * sin(12.0 * pRot[0] * pRot[0])));
float splat1 = smoothstep(0.5, 0.64,
sin(k1 * pRot[0] * (1.0 + 0.230 * pRot[0]) + phi1) *
cos(k1 * pRot[1] * (1.0 - 0.1 * pRot[0]) + phi1) *
(1.0 - 0.2 * sin(12.0 * pRot[0] * pRot[1])));
return mix(colChocolate + splat0 * col0, col1, splat1);
}
// Stripe pattern with palette
color paintStripes(vector q, int paletteIdx, float stripeFreq) {
float t = pow(smoothstep(0.0, 1.0, mod(stripeFreq * q[1] + sin(33.0 * q[0]), 1.0)), 3.0);
color col0 = getPaletteColor(paletteIdx, 0);
color col1 = getPaletteColor(paletteIdx, 4);
return mix(col0, col1, t);
}
// Checkerboard pattern with palette
color paintCheckerboard(vector q, float cellSize, int paletteIdx) {
int cellx = (int)floor(q[0] / cellSize);
int celly = (int)floor(q[1] / cellSize);
int checker = (cellx + celly) & 1;
color col0 = getPaletteColor(paletteIdx, 0);
color col1 = getPaletteColor(paletteIdx, 4);
return (checker != 0) ? col1 : col0;
}
// Gradient bands pattern
color paintGradientBands(vector q, int paletteIdx, float bandFreq) {
float t = mod(q[1] * bandFreq, 1.0);
int band = (int)floor(mod(q[1] * bandFreq, 5.0));
color col0 = getPaletteColor(paletteIdx, band);
color col1 = getPaletteColor(paletteIdx, (band + 1) % 5);
return mix(col0, col1, smoothstep(0.0, 1.0, t));
}
// Concentric rings pattern
color paintRings(vector q, int paletteIdx, float ringFreq, vector center) {
float r = length(vector(q[0] - center[0], q[1] - center[1], 0.0));
float t = mod(r * ringFreq, 1.0);
int ring = (int)floor(mod(r * ringFreq, 5.0));
color col0 = getPaletteColor(paletteIdx, ring);
color col1 = getPaletteColor(paletteIdx, (ring + 1) % 5);
return mix(col0, col1, smoothstep(0.0, 1.0, t));
}
// Noise-based organic pattern
color paintNoiseOrganic(vector q, int paletteIdx, float noiseScale) {
// Simple pseudo-noise using sin combinations
float n1 = sin(q[0] * noiseScale * 7.3 + q[1] * noiseScale * 3.7);
float n2 = sin(q[0] * noiseScale * 2.1 - q[1] * noiseScale * 5.9);
float n3 = sin(q[0] * noiseScale * 4.7 + q[1] * noiseScale * 8.3);
float n = (n1 + n2 * 0.5 + n3 * 0.25) / 1.75;
n = n * 0.5 + 0.5; // Map to [0,1]
int idx = (int)floor(n * 4.99);
float t = mod(n * 5.0, 1.0);
color col0 = getPaletteColor(paletteIdx, idx);
color col1 = getPaletteColor(paletteIdx, (idx + 1) % 5);
return mix(col0, col1, smoothstep(0.0, 1.0, t));
}
///////////////////////////////////////////////////////////////////////
//// MAIN SHADER: Paint Color Maps //
///////////////////////////////////////////////////////////////////////
shader Mixwell_PaintColorMaps(
// Input position
vector pMixwell = 0
[[ string label = "pMixwell",
string help = "Input 2D Mixwell position (displaced)" ]],
// Pattern selection
int patternType = 0
[[ string label = "Pattern Type",
string widget = "mapper",
string options = "Blobs:0|Chocolate:1|Stripes:2|Checkerboard:3|Gradient Bands:4|Concentric Rings:5|Noise Organic:6" ]],
// Palette selection
int paletteType = 0
[[ string label = "Color Palette",
string widget = "mapper",
string options = "Golden Meadow:0|Brown Blue:1|Classic:2|Ocean:3|Sunset:4|Monochrome:5|Earth:6" ]],
// Pattern-specific parameters
float blobScale = 1.0
[[ string label = "Blob Scale",
string help = "Scale factor for blob pattern (larger = bigger blobs)",
float min = 0.1,
float max = 5.0 ]],
float stripeFreq = 6.0
[[ string label = "Stripe Frequency",
string help = "Frequency of stripes",
float min = 1.0,
float max = 50.0 ]],
float checkerSize = 0.1
[[ string label = "Checker Size",
string help = "Size of checkerboard cells",
float min = 0.01,
float max = 1.0 ]],
float bandFreq = 3.0
[[ string label = "Band Frequency",
string help = "Frequency of gradient bands",
float min = 0.5,
float max = 20.0 ]],
float ringFreq = 5.0
[[ string label = "Ring Frequency",
string help = "Frequency of concentric rings",
float min = 1.0,
float max = 30.0 ]],
float ringCenterX = 0.5
[[ string label = "Ring Center X",
float min = -5.0,
float max = 5.0 ]],
float ringCenterY = 0.5
[[ string label = "Ring Center Y",
float min = -5.0,
float max = 5.0 ]],
float noiseScale = 3.0
[[ string label = "Noise Scale",
string help = "Scale of organic noise pattern",
float min = 0.5,
float max = 20.0 ]],
// Outputs
output color outColor = 0
[[ string label = "Output Color" ]],
output float outLuminance = 0
[[ string label = "Luminance" ]]
)
{
color col;
if (patternType == 0) {
col = paintBlobsWithPalette(pMixwell, paletteType, blobScale);
}
else if (patternType == 1) {
col = paintChocolate(pMixwell);
}
else if (patternType == 2) {
col = paintStripes(pMixwell, paletteType, stripeFreq);
}
else if (patternType == 3) {
col = paintCheckerboard(pMixwell, checkerSize, paletteType);
}
else if (patternType == 4) {
col = paintGradientBands(pMixwell, paletteType, bandFreq);
}
else if (patternType == 5) {
vector center = vector(ringCenterX, ringCenterY, 0.0);
col = paintRings(pMixwell, paletteType, ringFreq, center);
}
else {
col = paintNoiseOrganic(pMixwell, paletteType, noiseScale);
}
outColor = col;
// Standard luminance calculation (Rec. 709)
outLuminance = 0.2126 * col[0] + 0.7152 * col[1] + 0.0722 * col[2];
}Part of Mixwell OSL for Houdini and Redshift. Released under the MIT license.