Skip to content

Instantly share code, notes, and snippets.

@jazzathedev
Created April 2, 2026 15:35
Show Gist options
  • Select an option

  • Save jazzathedev/2f842a78d0ec62b356312337f3a5cf80 to your computer and use it in GitHub Desktop.

Select an option

Save jazzathedev/2f842a78d0ec62b356312337f3a5cf80 to your computer and use it in GitHub Desktop.
/*
* fluid_noise.osl — Fluid-like noise shader for Blender Cycles
*
* Presets (dropdown in node UI):
* jupiter — toxic lime/orange alien storm
* deep_ocean — slow deep-sea thermals
* magma — lava flow / pyroclastic
* aurora — polar light curtains
* inkdrop — ink diffusing in water, near-monochrome
* custom — use all manual inputs as-is
*/
#include "stdosl.h"
/* ═══════════════════════════════════════════════════════════════════════
Utilities
═══════════════════════════════════════════════════════════════════════ */
float fbm(point p, int octaves, float persistence, float lacunarity)
{
float val = 0.0, amp = 0.5, freq = 1.0, norm = 0.0;
for (int i = 0; i < octaves; i++) {
val += amp * (float)noise("perlin", p * freq);
norm += amp;
amp *= persistence;
freq *= lacunarity;
}
return val / max(norm, 1e-5);
}
float pot(point p, float seed)
{
return (float)noise("perlin",
p + vector(seed*1.71934, seed*0.31271, seed*2.18413));
}
vector curlNoise(point p, float eps)
{
float ie = 0.5 / eps;
vector ex = vector(eps,0,0), ey = vector(0,eps,0), ez = vector(0,0,eps);
float cx = (pot(p+ey,2.0)-pot(p-ey,2.0)-pot(p+ez,1.0)+pot(p-ez,1.0))*ie;
float cy = (pot(p+ez,0.0)-pot(p-ez,0.0)-pot(p+ex,2.0)+pot(p-ex,2.0))*ie;
float cz = (pot(p+ex,1.0)-pot(p-ex,1.0)-pot(p+ey,0.0)+pot(p-ey,0.0))*ie;
return vector(cx, cy, cz);
}
color ramp(float t,
float s0, color c0, float s1, color c1,
float s2, color c2, float s3, color c3,
float s4, color c4)
{
float tt = clamp(t, 0.0, 1.0);
if (tt <= s0) return c0;
if (tt >= s4) return c4;
if (tt < s1) return mix(c0, c1, (tt-s0)/max(s1-s0,1e-5));
if (tt < s2) return mix(c1, c2, (tt-s1)/max(s2-s1,1e-5));
if (tt < s3) return mix(c2, c3, (tt-s2)/max(s3-s2,1e-5));
return mix(c3, c4, (tt-s3)/max(s4-s3,1e-5));
}
/* ═══════════════════════════════════════════════════════════════════════
Main shader
═══════════════════════════════════════════════════════════════════════ */
shader fluid_noise(
/* ── Spatial / time ─────────────────────────────────── */
point Pos = P,
float Scale = 2.5,
float Time = 0.0,
/* ── Preset selector — renders as a dropdown in Blender ─ */
string Preset = "jupiter"
[[ string widget = "popup",
string options = "jupiter|deep_ocean|magma|aurora|inkdrop|custom" ]],
/* ── Manual overrides (only matter when Preset = "custom") ─ */
int Octaves = 8,
float Persistence = 0.52,
float Lacunarity = 2.3,
float WarpStrength = 1.4,
int WarpIterations = 4,
float CurlEps = 0.004,
float VorticityBlend = 0.55,
float Contrast = 1.6,
float Brightness = 0.0,
float TimeDrift = 0.07,
/* ── Custom colour stops ────────────────────────────── */
color Col0 = color(0.00, 0.00, 0.00),
color Col1 = color(0.05, 0.18, 0.02),
color Col2 = color(0.30, 0.72, 0.00),
color Col3 = color(0.82, 0.88, 0.00),
color Col4 = color(0.92, 0.38, 0.00),
output float Fac = 0.0,
output color Color = 0.0
)
{
/* ═══════════════════════════════════════════════════════════════════
Stage 0 — load preset values
Each preset writes into these working variables, which are then
used for ALL subsequent computation, not just colour.
═══════════════════════════════════════════════════════════════════ */
int p_octaves = Octaves;
float p_persist = Persistence;
float p_lacun = Lacunarity;
float p_warpStr = WarpStrength;
int p_warpIter = WarpIterations;
float p_curlEps = CurlEps;
float p_vortBlend = VorticityBlend;
float p_contrast = Contrast;
float p_bright = Brightness;
float p_drift = TimeDrift;
float p_scale = Scale;
/* colour stops */
float ps0 = 0.00; color pc0 = Col0;
float ps1 = 0.25; color pc1 = Col1;
float ps2 = 0.50; color pc2 = Col2;
float ps3 = 0.75; color pc3 = Col3;
float ps4 = 1.00; color pc4 = Col4;
/* ── Jupiter ─────────────────────────────────────────────────────────
* Tight high-frequency swirls, strong vortex filaments, fast motion.
* Bulk of the field sits in the lime band; orange only at peaks. */
if (Preset == "jupiter") {
p_octaves = 8;
p_persist = 0.52;
p_lacun = 2.30;
p_warpStr = 1.40;
p_warpIter = 4;
p_curlEps = 0.004;
p_vortBlend = 0.55;
p_contrast = 1.60;
p_bright = 0.00;
p_drift = 0.07;
p_scale = 2.50;
ps0 = 0.00; pc0 = color(0.01, 0.02, 0.00);
ps1 = 0.22; pc1 = color(0.04, 0.18, 0.01);
ps2 = 0.48; pc2 = color(0.45, 0.82, 0.00);
ps3 = 0.70; pc3 = color(0.78, 0.76, 0.00);
ps4 = 1.00; pc4 = color(0.90, 0.34, 0.00);
}
/* ── Deep ocean ──────────────────────────────────────────────────────
* Very slow large-scale rolls, low lacunarity (broad swirls), gentle
* vorticity. Feels like thermohaline convection. */
else if (Preset == "deep_ocean") {
p_octaves = 6;
p_persist = 0.58; /* smoother, rounder shapes */
p_lacun = 1.80; /* lower = larger, fewer curls */
p_warpStr = 0.90;
p_warpIter = 3;
p_curlEps = 0.006;
p_vortBlend = 0.20; /* soft, not filament-y */
p_contrast = 1.20;
p_bright = 0.05;
p_drift = 0.025; /* very slow evolution */
p_scale = 1.80;
ps0 = 0.00; pc0 = color(0.00, 0.00, 0.05);
ps1 = 0.20; pc1 = color(0.00, 0.06, 0.24);
ps2 = 0.45; pc2 = color(0.00, 0.28, 0.55);
ps3 = 0.68; pc3 = color(0.00, 0.62, 0.72);
ps4 = 1.00; pc4 = color(0.40, 0.95, 0.95);
}
/* ── Magma ───────────────────────────────────────────────────────────
* Sluggish top-level warp but lots of fine detail octaves. Strong
* contrast to keep most of the field dark with bright crack-like
* filaments. Slow drift speed. */
else if (Preset == "magma") {
p_octaves = 9; /* lots of fine crust-crack detail */
p_persist = 0.48;
p_lacun = 2.50; /* very fine veining */
p_warpStr = 1.80; /* heavy warp to break up any regularity */
p_warpIter = 4;
p_curlEps = 0.003;
p_vortBlend = 0.70; /* strong filaments = glowing crack edges*/
p_contrast = 2.20; /* most field is dark; peaks burn bright */
p_bright = -0.08; /* pull midtones down toward black */
p_drift = 0.018; /* lava is slow */
p_scale = 3.00;
ps0 = 0.00; pc0 = color(0.00, 0.00, 0.00);
ps1 = 0.28; pc1 = color(0.22, 0.00, 0.00);
ps2 = 0.55; pc2 = color(0.72, 0.06, 0.00);
ps3 = 0.78; pc3 = color(0.98, 0.50, 0.00);
ps4 = 1.00; pc4 = color(1.00, 0.96, 0.82);
}
/* ── Aurora ──────────────────────────────────────────────────────────
* Tall vertical curtains — low warp iterations, higher persistence
* for dreamy soft bands. Very low drift (aurora moves slowly). */
else if (Preset == "aurora") {
p_octaves = 5;
p_persist = 0.65; /* soft, washy forms */
p_lacun = 1.90;
p_warpStr = 0.70; /* gentle — keeps the curtain structure */
p_warpIter = 2; /* fewer warp passes = less tangled */
p_curlEps = 0.007;
p_vortBlend = 0.30;
p_contrast = 1.10;
p_bright = 0.08;
p_drift = 0.012;
p_scale = 1.50;
ps0 = 0.00; pc0 = color(0.01, 0.00, 0.08);
ps1 = 0.22; pc1 = color(0.08, 0.00, 0.32);
ps2 = 0.48; pc2 = color(0.00, 0.52, 0.62);
ps3 = 0.72; pc3 = color(0.22, 0.90, 0.72);
ps4 = 1.00; pc4 = color(0.88, 0.96, 1.00);
}
/* ── Inkdrop ─────────────────────────────────────────────────────────
* Ink diffusing in water. Maximum domain warp iterations and high
* vorticity for micro-tendrils. Near-monochrome: deep indigo → cream.
* Medium speed. */
else if (Preset == "inkdrop") {
p_octaves = 8;
p_persist = 0.55;
p_lacun = 2.20;
p_warpStr = 2.20; /* very tangled — ink doesn't flow clean */
p_warpIter = 5; /* maximum nesting */
p_curlEps = 0.003;
p_vortBlend = 0.80; /* the tendrils ARE the point */
p_contrast = 1.80;
p_bright = -0.05;
p_drift = 0.055;
p_scale = 2.80;
ps0 = 0.00; pc0 = color(0.02, 0.01, 0.06);
ps1 = 0.25; pc1 = color(0.10, 0.06, 0.28);
ps2 = 0.50; pc2 = color(0.28, 0.22, 0.50);
ps3 = 0.74; pc3 = color(0.70, 0.68, 0.78);
ps4 = 1.00; pc4 = color(0.96, 0.94, 0.92);
}
/* "custom" — all p_ variables already hold the manual input values */
/* ═══════════════════════════════════════════════════════════════════
Stage 1 — base position
═══════════════════════════════════════════════════════════════════ */
point base = Pos * p_scale + vector(0.0, 0.0, Time * p_drift);
/* ═══════════════════════════════════════════════════════════════════
Stage 2 — iterative curl domain warp
═══════════════════════════════════════════════════════════════════ */
point warped = base;
float wAmp = p_warpStr;
for (int i = 0; i < p_warpIter; i++) {
float s = pow(1.75, (float)i);
vector c = curlNoise(warped * s, p_curlEps) / s;
warped = warped + c * wAmp;
wAmp *= 0.52;
}
/* ═══════════════════════════════════════════════════════════════════
Stage 3 — FBM at warped position
═══════════════════════════════════════════════════════════════════ */
float f = fbm(warped, p_octaves, p_persist, p_lacun);
/* ═══════════════════════════════════════════════════════════════════
Stage 4 — vorticity confinement overlay
═══════════════════════════════════════════════════════════════════ */
vector cv = curlNoise(warped * p_lacun, p_curlEps * 0.45);
float vrt = smoothstep(0.0, 1.6, length(cv));
f = mix(f, f + vrt * 0.30, p_vortBlend);
/* ═══════════════════════════════════════════════════════════════════
Stage 5 — large-eddy drift
═══════════════════════════════════════════════════════════════════ */
float driftX = (float)noise("perlin", base*0.22 + vector(Time*0.035, 0.0, 0.0));
float driftY = (float)noise("perlin", base*0.22 + vector(0.0, Time*0.028, 0.0));
point drifted = warped + vector(driftX, driftY, 0.0) * 0.16;
f += (float)noise("perlin", drifted * 0.45) * 0.10;
/* ═══════════════════════════════════════════════════════════════════
Stage 6 — remap, contrast, output
═══════════════════════════════════════════════════════════════════ */
float raw = clamp(f * 0.5 + 0.5, 0.0, 1.0);
Fac = clamp((raw - 0.5) * p_contrast + 0.5 + p_bright, 0.0, 1.0);
Color = ramp(Fac, ps0,pc0, ps1,pc1, ps2,pc2, ps3,pc3, ps4,pc4);
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment