Selected Student Works
This is a small selection of student works from the 2025 class “Advanced Computer Graphics”. The objective was to create an image of a sea slug using a single fragment shader. You can see the full homework description if interested.
uniform vec2 u_resolution;
uniform float u_time;
float cross2(vec2 a, vec2 b) {
return a.x * b.y - a.y * b.x;
}
float sdTriangle(vec2 p, vec2 a, vec2 b, vec2 c) {
vec2 e0 = b - a;
vec2 e1 = c - b;
vec2 e2 = a - c;
vec2 v0 = p - a;
vec2 v1 = p - b;
vec2 v2 = p - c;
vec2 pq0 = v0 - e0 * clamp(dot(v0, e0) / dot(e0, e0), 0.0, 1.0);
vec2 pq1 = v1 - e1 * clamp(dot(v1, e1) / dot(e1, e1), 0.0, 1.0);
vec2 pq2 = v2 - e2 * clamp(dot(v2, e2) / dot(e2, e2), 0.0, 1.0);
float d = min(min(dot(pq0, pq0), dot(pq1, pq1)), dot(pq2, pq2));
float s0 = e0.x * v0.y - e0.y * v0.x;
float s1 = e1.x * v1.y - e1.y * v1.x;
float s2 = e2.x * v2.y - e2.y * v2.x;
bool inside = (s0 > 0.0 && s1 > 0.0 && s2 > 0.0) || (s0 < 0.0 && s1 < 0.0 && s2 < 0.0);
return (inside ? -sqrt(d) : sqrt(d));
}
float sdSegment(vec2 p, vec2 a, vec2 b) {
vec2 pa = p - a, ba = b - a;
float h = clamp(dot(pa, ba) / dot(ba, ba), 0.0, 1.0);
return length(pa - ba * h);
}
float sdTrapezoid(vec2 lp, float baseW, float topW, float h, float r) {
vec2 a = vec2(-baseW * 0.5 + 0.1, 0.0);
vec2 b = vec2(baseW * 0.5 + 0.05, 0.0);
vec2 c = vec2(topW * 0.5 + 0.06, -h);
vec2 d = vec2(-topW * 0.5 + 0.05, -h + 0.07);
float dseg = min(min(sdSegment(lp, a, b), sdSegment(lp, b, c)), min(sdSegment(lp, c, d), sdSegment(lp, d, a)));
float s0 = cross2(b - a, lp - a);
float s1 = cross2(c - b, lp - b);
float s2 = cross2(d - c, lp - c);
float s3 = cross2(a - d, lp - d);
bool inside = ((s0 > 0.0) && (s1 > 0.0) && (s2 > 0.0) && (s3 > 0.0)) || ((s0 < 0.0) && (s1 < 0.0) && (s2 < 0.0) && (s3 < 0.0));
float sd = inside ? -dseg : dseg;
return sd - r;
}
float sdRoundedBox(vec2 p, vec2 b, float r) {
vec2 q = abs(p) - b;
return length(max(q, 0.0)) - r + min(max(q.x, q.y), 0.0);
}
float orientedSector(vec2 p, vec2 c, float R, float A, float theta) {
vec2 v = p - c;
float r = length(v);
float ang = atan(v.y, v.x);
float dAng = abs(ang - theta);
dAng = min(dAng, 6.3 - dAng); // wrap to [-pi, pi]
return step(r, R) * step(dAng, A);
}
vec2 mod289(vec2 x) {
return x - floor(x * (1.0 / 289.0)) * 289.0;
}
vec3 mod289(vec3 x) {
return x - floor(x * (1.0 / 289.0)) * 289.0;
}
vec4 mod289(vec4 x) {
return x - floor(x * (1.0 / 289.0)) * 289.0;
}
vec3 permute(vec3 x) {
return mod289(((x * 34.0) + 1.0) * x);
}
vec4 permute(vec4 x) {
return mod289(((x * 34.0) + 1.0) * x);
}
float snoise(vec2 v) {
const vec4 C = vec4(0.211324865405187, 0.366025403784439, -0.577350269189626, 0.024390243902439);
vec2 i = floor(v + dot(v, C.yy));
vec2 x0 = v - i + dot(i, C.xx);
vec2 i1 = (x0.x > x0.y) ? vec2(1.0, 0.0) : vec2(0.0, 1.0);
vec2 x1 = x0 - i1 + C.xx;
vec2 x2 = x0 - 1.0 + 2.0 * C.xx;
i = mod289(i);
vec3 p = permute(permute(i.y + vec3(0.0, i1.y, 1.0)) + i.x + vec3(0.0, i1.x, 1.0));
vec3 m = max(0.5 - vec3(dot(x0, x0), dot(x1, x1), dot(x2, x2)), 0.0);
m = m * m;
m = m * m;
vec3 x = 2.0 * fract(p * C.www) - 1.0;
vec3 h = abs(x) - 0.5;
vec3 ox = floor(x + 0.5);
vec3 a0 = x - ox;
m *= 1.8 - 0.8 * (a0 * a0 + h * h);
vec3 g;
g.x = a0.x * x0.x + h.x * x0.y;
g.y = a0.y * x1.x + h.y * x1.y;
g.z = a0.z * x2.x + h.z * x2.y;
return 130.0 * dot(m, g);
}
float snoise(vec3 v) {
const vec2 C = vec2(1.0 / 6.0, 1.0 / 3.0);
const vec4 D = vec4(0.0, 0.5, 1.0, 2.0);
vec3 i = floor(v + dot(v, C.yyy));
vec3 x0 = v - i + dot(i, C.xxx);
vec3 g = step(x0.yzx, x0.xyz);
vec3 l = 1.0 - g;
vec3 i1 = min(g.xyz, l.zxy);
vec3 i2 = max(g.xyz, l.zxy);
vec3 x1 = x0 - i1 + C.xxx;
vec3 x2 = x0 - i2 + C.yyy;
vec3 x3 = x0 - D.yyy;
i = mod(i, 289.0);
vec4 p = permute(permute(permute(i.z + vec4(0.0, i1.z, i2.z, 1.0)) + i.y + vec4(0.0, i1.y, i2.y, 1.0)) + i.x + vec4(0.0, i1.x, i2.x, 1.0));
float n_ = 1.0 / 7.0;
vec3 ns = n_ * D.wyz - D.xzx;
vec4 j = p - 49.0 * floor(p * ns.z * ns.z);
vec4 x_ = floor(j * ns.z);
vec4 y_ = floor(j - 7.0 * x_);
vec4 x = x_ * ns.x + ns.yyyy;
vec4 y = y_ * ns.x + ns.yyyy;
vec4 h = 1.0 - abs(x) - abs(y);
vec4 b0 = vec4(x.xy, y.xy);
vec4 b1 = vec4(x.zw, y.zw);
vec4 s0 = floor(b0) * 2.0 + 1.0;
vec4 s1 = floor(b1) * 2.0 + 1.0;
vec4 sh = -step(h, vec4(0.0));
vec4 a0 = b0.xzyw + s0.xzyw * sh.xxyy;
vec4 a1 = b1.xzyw + s1.xzyw * sh.zzww;
vec3 g0 = vec3(a0.x, a0.y, h.x);
vec3 g1 = vec3(a0.z, a0.w, h.y);
vec3 g2 = vec3(a1.x, a1.y, h.z);
vec3 g3 = vec3(a1.z, a1.w, h.w);
vec4 norm = inversesqrt(vec4(dot(g0, g0), dot(g1, g1), dot(g2, g2), dot(g3, g3)));
g0 *= norm.x;
g1 *= norm.y;
g2 *= norm.z;
g3 *= norm.w;
vec4 m = max(0.6 - vec4(dot(x0, x0), dot(x1, x1), dot(x2, x2), dot(x3, x3)), 0.0);
m = m * m;
return 42.0 * dot(m * m, vec4(dot(g0, x0), dot(g1, x1), dot(g2, x2), dot(g3, x3)));
}
float turbulence(vec3 p) {
float amp = 0.5;
float f = 0.0;
for(int i = 0; i < 5; i++) {
f += amp * abs(snoise(p));
p = p * 2.0 + vec3(36.0, 18.0, 24.0); // rounded offsets
amp *= 0.5;
}
return f;
}
float fbm(vec2 p) {
float a = 1.68, f = 0.0;
for(int i = 0; i < 6; i++) {
f += a * snoise(p);
p = p * 2.0 + vec2(37.0, 17.0);
a *= 0.5;
}
return f;
}
// Colours
vec3 greyBG() {
return vec3(0.055, 0.110, 0.080);
}
vec3 bodyGrey() {
return vec3(0.145, 0.069, 0.039);
}
vec3 blue() {
return vec3(0.12, 0.36, 0.85);
}
vec3 turquoise() {
return vec3(0.20, 0.85, 0.75);
}
vec3 finGradient(float t) {
return mix(turquoise(), blue(), clamp(t, 0.0, 1.0));
}
vec2 hash22(vec2 p) {
p = vec2(dot(p, vec2(127.1, 311.7)), dot(p, vec2(269.5, 183.3)));
return fract(sin(p) * 43758.5453123);
}
float dotGrid(vec2 uv, float cell, float radius, float jitter) {
vec2 g = floor(uv / cell);
vec2 f = fract(uv / cell);
vec2 j = (hash22(g) - 0.5) * jitter;
vec2 c = 0.5 + j;
float d = length(f - c);
return smoothstep(radius, radius * 0.85, d); // 1 inside the dot
}
void main() {
vec2 st = gl_FragCoord.xy / u_resolution.xy; // for vignette
float s = min(u_resolution.x, u_resolution.y);
vec2 p = (gl_FragCoord.xy - 0.5 * u_resolution) / s; // [-0.5, 0.5]^2
float t = u_time;
vec3 uv = vec3(0.75 * gl_FragCoord.xy / u_resolution.y - 0.5, 0.05 * u_time);
float flow = 1.328 * turbulence(uv) + 0.5;
vec3 waterHue = mix(vec3(0.094, 0.280, 0.081), vec3(0.129, 0.315, 0.390), 0.35);
vec3 bg = mix(waterHue, greyBG(), flow);
vec3 col = bg;
// Head
vec2 A = vec2(-0.42, 0.02);
vec2 B = vec2(-0.30, 0.1);
vec2 C = vec2(-0.30, -0.07);
float headSDF = sdTriangle(p, A, B, C);
float headMask = smoothstep(0.02, -0.005, headSDF);
// Eye
vec2 eyeC = vec2(-0.35, 0.03);
float eyeR = 0.02;
float eyeD = length(p - eyeC) - eyeR;
float eyeMask = smoothstep(0.01, -0.005, eyeD);
float eyeNoise = 0.5 + 1.836 * snoise((p - eyeC) * 22.0);
vec3 eyeCol = mix(greyBG(), turquoise(), eyeNoise);
float headNoise = 0.5 + 5.5 * snoise(p * 15.0);
float headPattern = smoothstep(0.45, 0.85, headNoise);
vec3 headBase = bodyGrey();
vec3 headBlue = 1.0 * finGradient(headNoise);
vec3 headCol = mix(headBase, headBlue, headPattern);
headCol = mix(headCol, eyeCol, eyeMask);
// Body
vec2 bodyCenter = vec2(-0.02, 0.01);
vec2 bp = p - bodyCenter;
float bulge = 1.0 + 0.35 * exp(-bp.x * bp.x / 0.10);
vec2 q = vec2(bp.x, bp.y / bulge);
float bodySDF = sdRoundedBox(q, vec2(0.22, 0.03), 0.055);
float bodyMask = smoothstep(0.02, -0.005, bodySDF);
vec3 bodyBase = bodyGrey();
vec2 dotsUV = q * vec2(20.0, 20.0) + vec2(1.2, 0.0);
float dots = dotGrid(dotsUV + 0.05 * fbm(q * 4.536) * vec2(1.0), 0.5, 0.2, 0.18);
float bodyFalloff = clamp(-bodySDF / 0.20, 0.0, 1.0);
vec3 bodyBlue = 3.326 * finGradient(0.6 + 0.4 * snoise(q * 5.0));
vec3 bodyCol = mix(bodyBase, bodyBlue, dots * bodyFalloff);
float sway = 0.8 * t;
// Tail
vec2 tailBase = bodyCenter + vec2(0.25, 0.00);
float tailTheta = 0.0 + 0.08 * sin(sway);
float tailR = 0.3 + 0.02 * sin(sway * 0.7);
float tailA = 1.648 + 0.06 * sin(sway * 0.5);
float tailMask = orientedSector(p, tailBase, tailR, tailA, tailTheta);
vec2 tv = p - tailBase;
float tailStripes = 0.5 + 0.5 * sin(25.0 * atan(tv.y, tv.x) + 11.0 * length(tv) + 1.2 * t);
float tailNoise = 1.7 + 0.4 * fbm(tv * 7.0 + vec2(0.0, 1.600 * t));
float gTail = clamp(length(tv) / tailR, 0.0, 1.0); // radial gradient factor
vec3 gradTail = finGradient(gTail);
vec3 stripeTail = mix(bodyGrey(), gradTail, tailStripes); // stripes: grey→gradient
vec3 tailCol = stripeTail * (0.35 + 0.65 * tailNoise); // animated shimmer
// Top fin
vec2 topFinBase = bodyCenter + vec2(0.1, 0.02);
float topFinTheta = 1.57 + 0.06 * sin(sway * 0.9);
float topFinR = 0.282 + 0.02 * sin(sway * 0.9);
float topFinA = 0.82 + 0.05 * sin(sway);
float topFinMask = orientedSector(p, topFinBase, topFinR, topFinA, topFinTheta);
vec2 dv = p - topFinBase;
float topFinStripes = 0.5 + 0.5 * sin(25.0 * atan(dv.y, dv.x) + 9.0 * length(dv) - 0.8 * t);
float topFinNoise = 1.7 + 0.4 * fbm(tv * 7.0 + vec2(0.0, 1.600 * t));
float gTopFin = clamp(length(dv) / topFinR, 0.0, 1.0);
vec3 gradTopFin = finGradient(gTopFin);
vec3 stripeTopFin = mix(bodyGrey(), gradTopFin, topFinStripes);
vec3 topFinCol = stripeTopFin * (0.35 + 0.65 * topFinNoise);
// Bottom fin
float bodyHalfH = 0.11;
float baseY = bodyCenter.y - bodyHalfH;
vec2 ap = p - vec2(bodyCenter.x, baseY);
float trapBaseW = 0.3;
float trapTopW = 0.22;
float trapH = 0.12;
float trapR = 0.03;
float bottomFinSDF = sdTrapezoid(ap, trapBaseW, trapTopW, trapH, trapR);
float bottomFinMask = smoothstep(0.02, -0.005, bottomFinSDF);
float bottomFinStripeMask = 0.720 + sin(gl_FragCoord.x / 2.800) / 0.544;
bottomFinStripeMask = smoothstep(0.60, 0.99, bottomFinStripeMask);
float nearBody = clamp(1.0 - (-ap.y) / trapH, 0.0, 1.0);
float gBottomFin = clamp((-ap.y) / trapH, 0.0, 1.0);
vec3 gradBottomFin = finGradient(gBottomFin);
float mixStrength = bottomFinStripeMask * (0.35 + 0.65 * (1.0 - nearBody));
vec3 stripeBottomFin = mix(bodyGrey(), gradBottomFin, mixStrength);
float bottomFinNoise = 1.6 + 0.4 * fbm(ap * 6.0 + vec2(0.2, 0.5 * t));
vec3 bottomFinCol = stripeBottomFin * (0.35 + 0.65 * bottomFinNoise);
// Small fins
float baseW = 0.020;
float height = baseW * 7.0;
vec2 joint = bodyCenter + vec2(-0.20, -0.08);
vec2 BR_baseL = joint + vec2(-baseW * 0.5, -0.01);
vec2 BR_baseR = joint + vec2(+baseW * 0.5, -0.01);
vec2 BR_tip = joint + vec2(+0.13, -height);
float brSDF = sdTriangle(p, BR_baseL, BR_baseR, BR_tip);
float brMask = smoothstep(0.02, -0.005, brSDF);
vec2 BL_baseL = joint + vec2(-baseW * 0.5, -0.015);
vec2 BL_baseR = joint + vec2(+baseW * 0.5, -0.015);
vec2 BL_tip = joint + vec2(-0.04, -height);
float blSDF = sdTriangle(p, BL_baseL, BL_baseR, BL_tip);
float blMask = smoothstep(0.02, -0.005, blSDF);
float smallFinNoise = 3.5 + 0.5 * snoise(p * 12.0 + vec2(0.0, 0.9 * t));
vec3 smallFinCol = mix(bodyGrey(), blue(), smallFinNoise) * (0.1 + 0.6 * fbm(p * 9.0));
col = mix(col, tailCol, tailMask);
col = mix(col, topFinCol, topFinMask);
col = mix(col, bottomFinCol, bottomFinMask);
col = mix(col, bodyCol, bodyMask);
col = mix(col, headCol, headMask);
float ventralMask = clamp(brMask + blMask, 0.0, 1.0);
col = mix(col, smallFinCol, ventralMask);
float vign = smoothstep(0.95, 0.2, length(st - 0.5));
col *= mix(0.85, 1.0, vign);
col = pow(col, vec3(0.95));
colour_out = vec4(col, 1.0);
}
Betta mahachaiensis by Hilary
uniform vec2 u_resolution;
uniform vec2 u_mouse;
uniform float u_time;
mat2 rot(float angle){
float s = sin(angle);
float c = cos(angle);
return mat2(c, -s, s, c);
}
float random(in vec2 st){
return fract(sin(dot(st, vec2(12.9898, 78.233)))*43758.5453123);
}
float noise(vec2 st){
vec2 i = floor(st);
vec2 f = fract(st);
float a = random(i);
float b = random(i+vec2(1., 0.));
float c = random(i+vec2(0., 1.));
float d = random(i+vec2(1., 1.));
vec2 u = f*f*(3.-2.*f);
return mix(mix(a, b, u.x), mix(c, d, u.x), u.y);
}
#define OCTAVES 4
float fbm(vec2 st){
float value = 0.;
float amplitude = .5;
float frequency = 1.;
for(int i = 0;i < OCTAVES;i++){
st.x += .3;
value += amplitude*abs(noise(frequency*st));
amplitude *= .18;
frequency *= 2.;
st = 2.*rot(.7)*st+vec2(100., 0.);
}
return value;
}
#define rot2d(x) mat2(cos(x), -sin(x), sin(x), cos(x))
#define FISH 1.
#define FIN1 1.1
#define FIN2 1.2
#define FIN3 1.3
#define FIN4 1.4
#define lightPos vec3(0., 2., 15.)
vec2 smin( float a, float b, float k )
{
float h = 1.0 - min( abs(a-b)/(4.0*k), 1.0 );
float w = h*h;
float m = w*0.5;
float s = w*k;
return (a<b) ? vec2(a-s,m) : vec2(b-s,1.0-m);
}
vec3 hsv(float h, float s, float v){return ((clamp(abs(fract(h+vec3(0., 2., 1.)/3.)*6.-3.)-1., 0., 1.)*s-1.)+1.)*v;}
float sdSegment( vec3 p, vec3 a, vec3 b )
{
vec3 pa = p - a, ba = b - a;
float h = clamp( dot(pa,ba)/dot(ba,ba), 0.0, 1.0 );
return length( pa - ba*h );
}
float sdHexagon2D( in vec2 p, in float r )
{
const vec3 k = vec3(-0.866025404,0.5,0.577350269);
p = abs(p);
p -= 2.0*min(dot(k.xy,p),0.0)*k.xy;
p -= vec2(clamp(p.x, -k.z*r, k.z*r), r);
return length(p)*sign(p.y);
}
float sdSegment2D( in vec2 p, in vec2 a, in vec2 b )
{
vec2 pa = p-a, ba = b-a;
float h = clamp( dot(pa,ba)/dot(ba,ba), 0.0, 1.0 );
return length( pa - ba*h );
}
vec2 sdFish(vec3 p){
vec3 p_o = p;
p *= vec3(1., .8, 1.2);
vec3 a = vec3( -0., 0.0, 0.0);
vec3 b = vec3(-1.5, 0.0, 0.0);
vec3 c = vec3(-3., 0.0, 0.0);
vec3 d = vec3(-5.5, 0.0, 0.0);
float t = clamp(dot(p-a, d-a)/dot(d-a, d-a), 0., 1.);
float d1 = sdSegment(p, a, b);
float d2 = sdSegment(p, b, c);
float d3 = sdSegment(p, c, d);
float minDist = min(d1, min(d2, d3));
float shape = clamp(-2.2*pow(t+.2, 2.)*(t-.86)+.45, 0., 3.);
return vec2(FISH, minDist-(shape));
}
vec2 sdBezier( in vec2 pos, in vec2 A, in vec2 B, in vec2 C )
{
vec2 a = B - A;
vec2 b = A - 2.0*B + C;
vec2 c = a * 2.0;
vec2 d = A - pos;
float kk = 1.0/dot(b,b);
float kx = kk * dot(a,b);
float ky = kk * (2.0*dot(a,a)+dot(d,b))/3.0;
float kz = kk * dot(d,a);
vec2 res;
float p = ky - kx*kx;
float q = kx*(2.0*kx*kx - 3.0*ky) + kz;
float p3 = p*p*p;
float q2 = q*q;
float h = q2 + 4.0*p3;
if( h>=0.0 )
{
h = sqrt(h);
vec2 x = (vec2(h,-h)-q)/2.0;
vec2 uv = sign(x)*pow(abs(x), vec2(1.0/3.0));
float t = clamp( uv.x+uv.y-kx, 0.0, 1.0 );
vec2 q = d+(c+b*t)*t;
res = vec2(dot(q,q), t);
}
else
{
float z = sqrt(-p);
float v = acos(q/(p*z*2.0))/3.0;
float m = cos(v);
float n = sin(v)*1.732050808;
vec3 t = clamp( vec3(m+m,-n-m,n-m)*z-kx, 0.0, 1.0 );
vec2 qx = d+(c+b*t.x)*t.x; float dx=dot(qx,qx);
vec2 qy = d+(c+b*t.y)*t.y; float dy=dot(qy,qy);
res = (dx<dy) ? vec2(dx, t.x) : vec2(dy, t.y);
}
return vec2(sqrt(res.x), res.y);
}
float sdBezier3D( vec3 p, vec3 A, vec3 B, vec3 C, float thickness )
{
vec2 bezierResult = sdBezier( p.xy, A.xy, B.xy, C.xy );
float distXY = bezierResult.x;
float t = bezierResult.y;
float distZ = p.z - A.z;
return length(vec2(distXY, distZ)) - thickness * (1. - t);
}
vec2 sdFin1(vec3 p){
float bound = length(p - vec3(2.5, 1.0, 0.)) - 4.0;
if(bound > 1.) return vec2(FIN1, bound);
vec3 p_o = p;
float bold1 = 0.12;
p.z *= 1.2;
p.y = abs(p.y);
float sy = 0.2;
float d1 = sdBezier3D(p, vec3(0., 4.5*sy, 0.), vec3(1., 2., 0.), vec3(2.5, 2.9, 0.), bold1);
float d2 = sdBezier3D(p, vec3(0., 3.5*sy, 0.), vec3(1., 2., 0.), vec3(3.9, 3.-1.*2./3., 0.), bold1);
float d3 = sdBezier3D(p, vec3(0., 2.5*sy, 0.), vec3(1., 1.5, 0.), vec3(4.4, 3.-2.1*2./3., 0.), bold1);
float d4 = sdBezier3D(p, vec3(0., 1.5*sy, 0.), vec3(1., 1., 0.), vec3(4.75, 3.-3.1*2./3., 0.), bold1);
float d5 = sdBezier3D(p, vec3(0., 0.5*sy, 0.), vec3(1., .5, 0.), vec3(5.3, 3.-4.1*2./3., 0.), bold1);
float d6 = sdBezier3D(p, vec3(0., -0.5*sy, 0.), vec3(1., 0., 0.), vec3(4.3, 3.-5.*2./3., 0.), bold1);
float minDist = min(
min(min(d1, d2), min(d3, d4)),
min(d5, d6)
);
float shape = clamp(.05, 0., 3.);
return vec2(FIN1, minDist-(shape));
}
vec2 sdFin2(vec3 p){
float bound = length(p - vec3(1.5, 2.5, 0.)) - 4.5;
if(bound > 1.) return vec2(FIN2, bound);
vec3 p_o = p;
float bold1 = 0.04;
p.z *= 1.;
float offset = .2;
float d1 = sdBezier3D(p, vec3(0., 0., 0.), vec3(-2., 3., 0.), vec3(2.5, 2.7, 0.), bold1);
float d2 = sdBezier3D(p, vec3(offset, 0., 0.), vec3(-1.5+1.*offset, 3., 0.), vec3(2.5+1.*offset, 3.5, 0.), bold1);
float d3 = sdBezier3D(p, vec3(2.*offset, 0., 0.), vec3(-1.3+2.*offset, 3., 0.), vec3(2.8+2.*offset, 3.9, 0.), bold1);
float d4 = sdBezier3D(p, vec3(3.*offset, 0., 0.), vec3(1., 3., 0.), vec3(2.5+3.*offset, 3., 0.), bold1);
float d5 = sdBezier3D(p, vec3(4.*offset, 0., 0.), vec3(1.5, 1.8, 0.), vec3(4.5+4.*offset, 1.9, 0.), bold1);
float d6 = sdBezier3D(p, vec3(5.*offset, 0., 0.), vec3(2., 1.5, 0.), vec3(5.+5.*offset, 1., 0.), bold1);
float minDist = min(
min(min(d1, d2), min(d3, d4)),
min(d5, d6)
);
float shape = clamp(.05, 0., 3.);
return vec2(FIN2, minDist-(shape));
}
vec2 sdFin3(vec3 p){
float bound = length(p - vec3(0., -2.5, 0.)) - 3.5;
if(bound > 1.) return vec2(FIN3, bound);
vec3 p_o = p;
float bold1 = 0.12;
float offset = .15;
float d1 = sdBezier3D(p, vec3(0., 0., 0.), vec3(-1., -3., 0.), vec3(.5, -3.2, 0.), bold1);
for(float i = 1.;i < 20.;i++){
d1 = min(d1, sdBezier3D(p, vec3(0.+i*offset, 0., 0.), vec3(-1.+1.5*i*offset, -2.5, 0.), vec3(.1+1.2*i*offset, -3.2-i*.01, 0.), bold1));
}
return vec2(FIN3, d1);
}
vec2 sdFin4(vec3 p){
p.z = abs(p.z);
p.z -= .6;
float bound = length(p - vec3(1.0, -1.0, 0.)) - 3.5;
if(bound > 1.) return vec2(FIN3, bound);
vec3 p_o = p;
float bold1 = 0.1;
float offset = .3;
float d1 = sdBezier3D(p, vec3(0., 0., 0.), vec3(-1., -1., 0.), vec3(2., -2.2, 0.), bold1);
for(float i = 1.;i < 4.;i++){
d1 = min(d1, sdBezier3D(p, vec3(0.+i*offset*.1, 0., 0.), vec3(-1.+1.5*i*offset, -1, 0.), vec3(2.-2.2*i*offset, -2.2+i*.65, 0.), bold1));
}
return vec2(FIN3, d1);
}
#define EYE 2.
vec2 sdEyes(vec3 p){
p.z = abs(p.z);
return vec2(EYE, length(p-vec3(-4.3, 0.0, .4))-.32);
}
vec2 map(vec3 p){
vec2 objFish = sdFish(p);
vec2 objFin1 = sdFin1(p);
vec2 objFin2 = sdFin2(p-vec3(-2., 1., 0.));
vec2 objFin3 = sdFin3(p-vec3(-3., -1., 0.));
vec2 objFin4 = sdFin4(p-vec3(-3.5, -.8, 0.));
vec2 objFins = objFin1;
if(objFin2.y < objFins.y) objFins = objFin2;
if(objFin3.y < objFins.y) objFins = objFin3;
if(objFin4.y < objFins.y) objFins = objFin4;
float d = smin(objFins.y, objFish.y, .12).x;
float id = (objFins.y < objFish.y) ? objFins.x : objFish.x;
vec2 obj = sdEyes(p);
if(obj.y < d){
d = obj.y;
id = obj.x;
}
return vec2(id, d);
}
vec3 gNor(vec3 p){
vec2 e = vec2(.001, 0.);
float d = map(p).y;
return normalize(vec3(
map(p+e.xyy).y - d,
map(p+e.yxy).y - d,
map(p+e.yyx).y - d
));
}
vec3 fishCol(vec3 p){
vec3 col;
vec3 mate = 1.4*mix(hsv(.64, .9, .9), hsv(.0, .8, .8), smoothstep(-0., 3.2, p.x))*(.7+1.2*fbm(p.xy*3.));
mate = mix(hsv(.0, .2, .8), mate, smoothstep(-6., -1., p.x));
mate = mix(mate*.2, mate, smoothstep(-10., 2., p.x));
mate = mix(mate*.1, mate, smoothstep(-1., 0., p.y));
mate = mix(mate*.1, mate, smoothstep(3., 0., p.y));
vec2 p2 = p.xy*13.;
vec2 p2_o = p2;
p2.x = mod(p2.x, 3.8)-1.7;
p2.y = mod(p2.y, 11.)-5.5;
p2.x *= 1.5;
p2.xy = rot(radians(30.))*p2.xy;
if(abs(sdHexagon2D(p2, 3.))-.2<0.)mate *= .6;
p2 = p2_o;
p2.x = mod(p2.x, 3.8)-1.7;
p2.y -= 5.5;
p2.y = mod(p2.y, 11.)-5.5;
if(sdSegment2D(p2, vec2(0., 2.3), vec2(0., -2.3))-.15<0.)mate *= .6;
vec3 nor = gNor(p);
float diff = clamp(dot(normalize(lightPos-p), nor), 0., 1.);
col = mate*diff+mate*.2;
return col;
}
vec3 fin1Col(vec3 p){
vec3 col;
vec3 mate = mix(hsv(.63, .8, .8), hsv(.0, .8, .8), smoothstep(-0., 3.2, p.x))*(1.+1.2*fbm(p.xy*3.));
mate = mix(hsv(.0, .2, .8), mate, smoothstep(-6., -1., p.x));
mate = mix(mate*.1, mate, smoothstep(-1., 2., abs(p.y)));
vec3 nor = gNor(p);
float diff = clamp(dot(normalize(lightPos-p), nor), 0., 1.);
col = mate*diff+mate*.2;
return col;
}
vec3 fin2Col(vec3 p){
vec3 col;
vec3 mate = mix(hsv(.61, .8, .8), hsv(.0, .8, .6), smoothstep(-0., 1.2, p.x))*(.7+1.2*fbm(p.xy*3.));
mate = mix(hsv(.0, .2, .8), mate, smoothstep(-6., -1., p.x));
vec3 nor = gNor(p);
float diff = clamp(dot(normalize(lightPos-p), nor), 0., 1.);
col = mate*diff+mate*.2;
return col;
}
vec3 fin3Col(vec3 p){
vec3 col;
vec3 mate = hsv(.0, .7, 1.3)*(.7+1.2*fbm(p.xy*3.));
mate = mix(mate*.1, mate, smoothstep(1., 3., abs(p.y)));
vec3 nor = gNor(p);
float diff = clamp(dot(normalize(lightPos-p), nor), 0., 1.);
col = mate*diff+mate*.2;
return col;
}
vec3 fin4Col(vec3 p){
vec3 col;
vec3 mate = hsv(.0, .7, .8);
mate = mix(mate*.1, mate, smoothstep(1., 3., abs(p.y)));
vec3 nor = gNor(p);
float diff = clamp(dot(normalize(lightPos-p), nor), 0., 1.);
col = mate*diff+mate*.2;
return col;
}
vec3 eyeCol(vec3 p){
vec3 col;
vec3 mate = hsv(.0, .7, .8)*.1;
vec3 nor = gNor(p);
float diff = clamp(dot(normalize(lightPos-p), nor), 0., 1.);
col = mate*diff+mate*.2;
return col;
}
void main() {
vec2 fragCoord = gl_FragCoord.xy;
vec2 uv = fragCoord/u_resolution.xy;
vec2 uv_o = uv;
vec2 p_o = uv*2.-1.;
uv.y *= 2.;
vec2 p = (fragCoord*2.-u_resolution.xy)/u_resolution.y;
vec3 col = hsv(.6, 1., .3*mix(.6, 0., smoothstep(0., 2., length(p))));
vec3 ro=vec3(-1., 0., 8.), cUp=vec3(0., 1., 0.), cDir=vec3(0., 0., -1.), cSide=cross(cDir, cUp), rd=normalize(cSide*p.x+cUp*p.y+cDir), ray;
ro.xz = rot2d(-.2+.2*sin(-u_time*1.2))*ro.xz;
rd.xz = rot2d(-.2+.2*sin(-u_time*1.2))*rd.xz;
float t=0.;
for(float i=0.;i < 80.;i++){
ray = ro+rd*t;
vec2 obj = map(ray);
if(obj.y<.001){
if(obj.x == FISH){
col =fishCol(ray);
}else if(obj.x == FIN1){
col =fin1Col(ray);
}else if(obj.x == FIN2){
col =fin2Col(ray);
}else if(obj.x == FIN3){
col =fin3Col(ray);
}else if(obj.x == EYE){
col =eyeCol(ray);
}
break;
}
t += .9*obj.y;
}
col = pow(col, vec3(.4545));
colour_out = vec4(col,1.0);
}
Betta beta by UK0141
uniform vec2 u_resolution;
uniform float u_time;
// utility
float smin(float a, float b, float k) {
float h = clamp(0.5 + 0.5 * (b - a) / k, 0.0, 1.0);
return mix(b, a, h) - k * h * (1.0 - h);
}
mat2 rot(float a) {
float s = sin(a);
float c = cos(a);
return mat2(c, -s, s, c);
}
// water noise helpers
float hash(vec2 p) {
return fract(sin(dot(p, vec2(127.1, 311.7))) * 43758.5453);
}
float noise(vec2 p) {
vec2 i = floor(p);
vec2 f = fract(p);
f = f * f * (3.0 - 2.0 * f);
float a = hash(i);
float b = hash(i + vec2(1.0, 0.0));
float c = hash(i + vec2(0.0, 1.0));
float d = hash(i + vec2(1.0, 1.0));
return mix(mix(a, b, f.x), mix(c, d, f.x), f.y);
}
float fbm(vec2 p) {
float value = 0.0;
float amplitude = 0.5;
float frequency = 1.0;
for (int i = 0; i < 4; i++) {
value += amplitude * noise(p * frequency);
frequency *= 2.0;
amplitude *= 0.5;
}
return value;
}
float waterNoise(vec2 uv, float t) {
vec2 p = uv * 5.0;
float ripple1 = sin(length(p - vec2(sin(t * 0.3) * 2.0, cos(t * 0.3) * 2.0)) * 8.0 - t * 2.0) * 0.5 + 0.5;
float ripple2 = sin(length(p - vec2(cos(t * 0.4) * 2.5, sin(t * 0.4) * 2.5)) * 6.0 - t * 1.5) * 0.5 + 0.5;
float wave1 = fbm(p * 0.5 + vec2(t * 0.1, t * 0.08));
float wave2 = fbm(p * 0.8 + vec2(-t * 0.12, t * 0.15));
float combined = wave1 * 0.4 + wave2 * 0.3;
combined += ripple1 * 0.2 + ripple2 * 0.1;
return combined;
}
// sdf shapes
float sdEllipse(vec2 p, vec2 r) {
float k0 = length(p/r);
float k1 = length(p/(r*r));
return k0*(k0-1.0)/k1;
}
float sdCircle(vec2 p, float r) {
return length(p) - r;
}
float sdCrescent(vec2 p, float outerRadius, float innerRadius, float offset) {
float dOuter = length(p) - outerRadius;
float dInner = length(p - vec2(offset, 0.0)) - innerRadius;
return max(dOuter, -dInner);
}
float sdVesica(vec2 p, float r, float d) {
p = abs(p);
float b = sqrt(r*r - d*d);
return ((p.y-b)*d > p.x*b) ? length(p-vec2(0.0,b))
: length(p-vec2(-d,0.0))-r;
}
float sdRoundBottomTear(vec2 p, float tipRadius, float tipOffset, float baseRadius) {
if (p.y > 0.0) {
return sdVesica(p, tipRadius, tipOffset);
} else {
return length(p) - baseRadius;
}
}
vec2 applyFishWarp(vec2 p) {
vec2 fishPos = p;
float swim = sin(fishPos.x * 2.0 - u_time * 3.0) * 0.02 * smoothstep(0.5, -1.0, fishPos.x);
fishPos.y += swim;
return fishPos;
}
float sdGillShape(vec2 fishPos) {
vec2 gillUV = fishPos - vec2(0.55, 0.0);
gillUV *= rot(-3.0);
gillUV *= mat2(0.75, 0.0,
0.0, 1.0);
float dGill = sdCrescent(gillUV, 0.08, 0.12, -0.06);
return max(dGill, -(gillUV.x + 0.012));
}
// pectoral fin distance
float getPectoralDist(vec2 fishPos, float t) {
vec2 pecUV = fishPos - vec2(0.33, 0.0); //position
float flap = sin(t * -2.0) * 0.002;
pecUV *= rot(7.9 + flap);
pecUV.x += sin(pecUV.y * 80.0 - t * 4.0) * 0.003;
// overall pectoral fin scale
float k = 0.45;
float size = 0.50 * k; //len
float offset = 0.37 * k;
float base = size - offset;
float dFin = sdRoundBottomTear(pecUV, size, offset, base);
// fin cutout
vec2 cutCenter = vec2(0.02 * k, 0.0);
float cutRadius = 0.28 * k;
float cutCircle = sdCircle(pecUV - cutCenter, cutRadius);
dFin = max(dFin, cutCircle);
return dFin;
}
// main fish sdf
float mapFish(vec2 p) {
vec2 fishPos = applyFishWarp(p);
float dBody = sdEllipse(fishPos, vec2(0.75, 0.18));
float dHead = sdCircle(fishPos - vec2(0.4, -0.02), 0.12);
dBody = smin(dBody, dHead, 0.1);
// fins
//tail fin
vec2 tailUV = fishPos - vec2(-0.26, 0.0);
float fold = sin(tailUV.y * 60.0 - u_time * 2.0) * 0.012;
tailUV.x += fold;
float dTail = length(tailUV) - 0.52;
float tailCut = dot(tailUV, vec2(-0.9, 0.0)) + 0.35;
dTail = max(dTail, -tailCut);
//dorsal fin (top)
vec2 dorsalUV = fishPos - vec2(-0.15, 0.18);
dorsalUV *= rot(4.2);
dorsalUV.x += sin(dorsalUV.y * 100.0 + u_time) * 0.005;
float dDorsal = sdVesica(dorsalUV, 0.45, 0.05);
dDorsal = max(dDorsal, dorsalUV.y - 0.0);
//anal fin (bottom)
vec2 analUV = fishPos - vec2(-0.08, -0.18);
analUV *= rot(5.0);
analUV.x += sin(analUV.y * 100.0 - u_time * 1.5) * 0.005;
float dAnal = sdEllipse(analUV, vec2(0.38, 0.6));
dAnal = max(dAnal, analUV.y);
//gill
float dGill = sdGillShape(fishPos);
dBody = smin(dBody, dGill, 0.02);
// combine body and fins
float dMeltyFins = min(dTail, min(dDorsal, dAnal));
float dBaseFish = smin(dBody, dMeltyFins, 0.1);
return dBaseFish;
}
void main() {
vec2 st = (gl_FragCoord.xy * 2.0 - u_resolution) / u_resolution.y;
st *= 1.2;
float d = mapFish(st);
vec2 warpedPos = applyFishWarp(st);
float gillDist = sdGillShape(warpedPos);
float pecDist = getPectoralDist(warpedPos, u_time);
// background color
vec3 color = vec3(0,0.52,0.84);
// circular gradient for fish color
vec2 center = vec2(0.4, 0.0);
float r = length(st - center);
float t = smoothstep(0.0, 1.0, r);
vec3 innerColor = vec3(0.00, 0.34, 0.84);
vec3 outerColor = vec3(1.00, 0.15, 0.00);
vec3 fishColor = mix(innerColor, outerColor, t);
float gillMask = smoothstep(0.01, -0.015, gillDist);
float pecMask = smoothstep(0.01, -0.01, pecDist);
if (d < 0.0) {
float light = smoothstep(0.0, 0.5, -d);
color = fishColor * (0.5 + 0.5 * light);
float rim = smoothstep(-0.05, 0.0, d);
color += vec3(0.5, 0.8, 1.0) * rim * 0.005;
}
color += vec3(0.2, 0.4, 0.9) * (0.01 / abs(d));
// gill
color = mix(color, vec3(1.0, 1.0, 1.0), gillMask);
// pectoral fin
if (pecMask > 0.0) {
vec3 pecColor = vec3(0.89, 0.14, 0.00);
// semi transparent blend
color = mix(color, pecColor, pecMask * 0.2);
}
// pectoral fin outline
float pecOutline = smoothstep(0.01, 0.001, abs(pecDist));
color += vec3(0.4, 0.6, 0.9) * pecOutline * 0.5;
// eye
float eyeDist = sdCircle(warpedPos - vec2(0.6, 0.03), 0.01);
float eyeMask = smoothstep(0.02, -0.005, eyeDist);
color = mix(color, vec3(1.0, 1.0, 1.0), eyeMask);
// water layer on top
vec2 uv = gl_FragCoord.xy / u_resolution.xy;
float water = waterNoise(uv, u_time);
vec3 deepWater = vec3(0.00, 0.12, 0.34);
vec3 shallowWater = vec3(0.2, 0.6, 0.8);
vec3 foam = vec3(0.8, 0.9, 1.0);
vec3 waterColor = mix(deepWater, shallowWater, water);
waterColor = mix(waterColor, foam, smoothstep(0.2, 0.9, water));
float shimmer = fbm(uv * 10.0 + u_time * 0.5);
waterColor += shimmer * 0.8;
float waterAlpha = 0.15;
color = mix(color, waterColor, waterAlpha);
colour_out = vec4(color, 1.0);
}
Bicolor Delta Betta by JL
uniform vec2 u_resolution;
uniform float u_time;
vec3 mod289(vec3 x) {
return x - floor(x * (1.0 / 289.0)) * 289.0;
}
vec2 mod289(vec2 x) {
return x - floor(x * (1.0 / 289.0)) * 289.0;
}
vec3 permute(vec3 x) {
return mod289(((x * 34.0) + 1.0) * x);
}
float snoise(vec2 v) {
const vec4 C = vec4(0.211324865405187,
0.366025403784439,
-0.577350269189626,
0.024390243902439);
vec2 i = floor(v + dot(v, C.yy));
vec2 x0 = v - i + dot(i, C.xx);
vec2 i1;
i1 = (x0.x > x0.y) ? vec2(1.0, 0.0) : vec2(0.0, 1.0);
vec4 x12 = x0.xyxy + C.xxzz;
x12.xy -= i1;
i = mod289(i);
vec3 p = permute(permute(i.y + vec3(0.0, i1.y, 1.0)) + i.x + vec3(0.0, i1.x, 1.0));
vec3 m = max(0.5 - vec3(dot(x0, x0), dot(x12.xy, x12.xy), dot(x12.zw, x12.zw)), 0.0);
m = m * m;
m = m * m;
vec3 x = 2.0 * fract(p * C.www) - 1.0;
vec3 h = abs(x) - 0.5;
vec3 ox = floor(x + 0.5);
vec3 a0 = x - ox;
m *= 1.79284291400159 - 0.85373472095314 * (a0 * a0 + h * h);
vec3 g;
g.x = a0.x * x0.x + h.x * x0.y;
g.yz = a0.yz * x12.xz + h.yz * x12.yw;
return 130.0 * dot(m, g);
}
//Fractal Brownian Motion
float fbm(vec2 p, int octaves) {
float value = 0.0;
float amplitude = 0.5;
float frequency = 1.0;
for(int i = 0; i < 6; i++) {
if(i >= octaves) break;
value += amplitude * snoise(p * frequency);
frequency *= 2.0;
amplitude *= 0.5;
}
return value;
}
//Ellipse SDF
float sdEllipse(vec2 p, vec2 ab) {
p = abs(p);
if(p.x > p.y) { p = p.yx; ab = ab.yx; }
float l = ab.y * ab.y - ab.x * ab.x;
float m = ab.x * p.x / l;
float m2 = m * m;
float n = ab.y * p.y / l;
float n2 = n * n;
float c = (m2 + n2 - 1.0) / 3.0;
float c3 = c * c * c;
float q = c3 + m2 * n2 * 2.0;
float d = c3 + m2 * n2;
float g = m + m * n2;
float co;
if(d < 0.0) {
float h = acos(q / c3) / 3.0;
float s = cos(h);
float t = sin(h) * sqrt(3.0);
float rx = sqrt(-c * (s + t + 2.0) + m2);
float ry = sqrt(-c * (s - t + 2.0) + m2);
co = (ry + sign(l) * rx + abs(g) / (rx * ry) - m) / 2.0;
} else {
float h = 2.0 * m * n * sqrt(d);
float s = sign(q + h) * pow(abs(q + h), 1.0 / 3.0);
float u = sign(q - h) * pow(abs(q - h), 1.0 / 3.0);
float rx = -s - u - c * 4.0 + 2.0 * m2;
float ry = (s - u) * sqrt(3.0);
float rm = sqrt(rx * rx + ry * ry);
co = (ry / sqrt(rm - rx) + 2.0 * g / rm - m) / 2.0;
}
vec2 r = ab * vec2(co, sqrt(1.0 - co * co));
return length(r - p) * sign(p.y - r.y);
}
//Fish body parts SDFs
float bodyShape(vec2 p) {
vec2 bodyPos = p - vec2(0.57, 0.48);
float body = sdEllipse(bodyPos, vec2(0.24, 0.10));
return body;
}
//Tail
float tailShape(vec2 p, float time) {
vec2 tailCenter = vec2(0.47, 0.48);
vec2 tp = p - tailCenter;
float angle = atan(tp.y, tp.x);
float dist = length(tp);
float flow = snoise(vec2(angle * 3.0, time * 0.5)) * 0.03;
float flow2 = snoise(vec2(angle * 5.0 - time * 0.3, dist * 10.0)) * 0.02;
float baseRadius = 0.35;
float radiusVar = 0.04 * sin(angle * 8.0 + time * 0.5);
float noiseVar = fbm(vec2(angle * 4.0, time * 0.3), 3) * 0.04;
float tailRadius = baseRadius + radiusVar + noiseVar + flow + flow2;
float edgeDetail = snoise(vec2(angle * 12.0, time * 0.4)) * 0.02;
tailRadius += edgeDetail;
float innerRadius = 0.01;
float tail = dist - tailRadius;
float inner = innerRadius - dist;
return max(tail, inner);
}
//Fin
float ventralFinShape(vec2 p) {
vec2 finPos = p - vec2(0.60, 0.37);
//Fin rotation
float rotationAngle = -0.9;
float cosA = cos(rotationAngle);
float sinA = sin(rotationAngle);
finPos = vec2(
finPos.x * cosA - finPos.y * sinA,
finPos.x * sinA + finPos.y * cosA
);
float fin = sdEllipse(finPos, vec2(0.08, 0.04));
return fin;
}
//Colors and patterns
float scalePattern(vec2 p, float time) {
vec2 sp = p * 50.0;
sp.x += mod(floor(sp.y), 2.0) * 0.5;
vec2 cell = fract(sp) - 0.5;
float d = length(cell);
float movement = snoise(p * 20.0 + time * 0.1) * 0.1;
return smoothstep(0.4 + movement, 0.35 + movement, d);
}
float finStripes(vec2 p, float time) {
float stripes = sin(p.x * 80.0 + p.y * 40.0 + snoise(p * 10.0 + time * 0.2) * 2.0);
return stripes * 0.5 + 0.5;
}
//Body color
vec3 bodyColor(vec2 p, float time) {
vec3 tealDark = vec3(0.05, 0.25, 0.35);
vec3 tealLight = vec3(0.2, 0.55, 0.65);
vec3 tealHighlight = vec3(0.4, 0.75, 0.8);
float scales = scalePattern(p, time);
float grad = (p.y - 0.3) * 2.0;
grad += snoise(p * 8.0) * 0.2;
vec3 baseColor = mix(tealDark, tealLight, grad);
baseColor = mix(baseColor, tealHighlight, scales * 0.3);
float shimmer = snoise(p * 30.0 + vec2(time * 0.5, 0.0)) * 0.5 + 0.5;
baseColor += vec3(0.1, 0.15, 0.1) * shimmer * scales;
return baseColor;
}
//Tail color
vec3 tailColor(vec2 p, float time, float bodyDist) {
vec3 cyan = vec3(0.16, 0.71, 0.75);
vec3 blue = vec3(0.01, 0.43, 0.64);
vec3 darkBlue = vec3(0.0, 0.22, 0.48);
vec3 offWhite = vec3(0.9, 0.85, 0.85);
vec2 center = vec2(0.50, 0.48);
float distFromBody = length(p - center);
float gradientT = smoothstep(0.08, 0.35, distFromBody);
float noiseVal = fbm(p * 8.0 + time * 0.1, 3) * 0.3;
gradientT += noiseVal;
gradientT = clamp(gradientT, 0.0, 1.0);
vec3 color = mix(blue, cyan, gradientT);
color = mix(color, darkBlue, smoothstep(0.6, 1.0, gradientT));
float stripes = finStripes(p, time);
color = mix(color, color * 1.2, stripes * 0.15);
float edgeBrightness = smoothstep(0.2, 0.4, distFromBody) * 0.2;
color = mix(color, offWhite, edgeBrightness * (1.0 - abs(snoise(p * 20.0))));
return color;
}
//Small fin color
vec3 smallFinColor(vec2 p, float time) {
vec3 maroon = vec3(0.66, 0.094, 0.15);
vec3 lavender = vec3(0.71, 0.75, 0.86);
float noise = snoise(p * 15.0 + time * 0.2) * 0.5 + 0.5;
vec3 color = mix(maroon, lavender, noise);
float stripes = finStripes(p, time);
color = mix(color, color * 1.2, stripes * 0.2);
return color;
}
//Eye
float eyeShape(vec2 p) {
vec2 eyePos = p - vec2(0.78, 0.50);
return length(eyePos) - 0.012;
}
vec3 eyeColor(vec2 p) {
vec2 eyePos = p - vec2(0.68, 0.50);
vec3 color = vec3(0.02, 0.02, 0.05);
float highlight = smoothstep(0.008, 0.003, length(eyePos - vec2(-0.003, 0.003)));
color = mix(color, vec3(0.3, 0.35, 0.4), highlight * 0.5);
return color;
}
//Main
void main() {
vec2 st = gl_FragCoord.xy / u_resolution.xy;
float aspect = u_resolution.x / u_resolution.y;
vec2 p = st;
p.x *= aspect;
p.x -= (aspect - 1.0) * 0.5;
float time = u_time;
vec3 bgDark = vec3(0.02, 0.03, 0.08);
vec3 bgLight = vec3(0.05, 0.08, 0.15);
float bgNoise = fbm(st * 3.0 + time * 0.05, 4) * 0.5 + 0.5;
vec3 background = mix(bgDark, bgLight, bgNoise * 0.3 + st.y * 0.2);
float caustic = snoise(st * 8.0 + time * 0.2) * snoise(st * 12.0 - time * 0.15);
vec3 causticBlue = vec3(0.0, 0.12, 0.26);
background += causticBlue * caustic;
float body = bodyShape(p);
float tail = tailShape(p, time);
float eye = eyeShape(p);
float ventralFin = ventralFinShape(p);
//Cutoff Parameters
float bodyY = 0.48;
float upperCutoffX = 0.58;
float upperAngle = 0.3;
float lowerCutoffX = 0.65;
float lowerAngle = -0.2;
float upperLineDist = (p.x - upperCutoffX) * cos(upperAngle) + (p.y - bodyY) * sin(upperAngle);
float lowerLineDist = (p.x - lowerCutoffX) * cos(lowerAngle) + (p.y - bodyY) * sin(lowerAngle);
//Tail visibility area
bool shouldDrawTail;
if(p.y >= bodyY) {
shouldDrawTail = (upperLineDist < 0.0);
} else {
shouldDrawTail = (lowerLineDist < 0.0);
}
vec3 color = background;
//Draw tail
if(tail < 0.0 && shouldDrawTail) {
vec3 fColor = tailColor(p, time, body);
float alpha = smoothstep(0.005, -0.005, tail);
color = mix(color, fColor, alpha);
}
//Draw body
if(body < 0.0) {
vec3 bColor = bodyColor(p, time);
color = bColor;
}
//Draw eye
if(eye < 0.0) {
vec3 eColor = eyeColor(p);
float alpha = smoothstep(0.002, -0.002, eye);
color = mix(color, eColor, alpha);
}
//Draw fin
if(ventralFin < 0.0) {
vec3 fColor = smallFinColor(p, time);
float alpha = smoothstep(0.003, -0.003, ventralFin);
color = mix(color, fColor, alpha);
}
//Add glowing effect
float fishDist;
if(body < 0.0) {
fishDist = body;
} else if(tail < 0.0 && shouldDrawTail) {
fishDist = tail;
} else {
fishDist = body;
}
float glow = exp(-fishDist * 15.0) * 0.15;
vec3 glowRed = vec3(0.64, 0.035, 0.07);
color += glowRed * glow;
colour_out = vec4(color, 1.0);
}
better (betta) on the way by Sun Chang
//Author Informations
//Author: LI Zhuohang, 5125EG13-0, use ameato9ei as my pseudonym if possible;)
//Fish Name: TryMouseInteractOnMeFish
//For complete html file, refer to github: https://github.com/takko9ei/WebGL_Course/blob/main/HW3-Procedual-design/assignment/betta-fish.html
uniform vec2 u_resolution;
uniform float u_time;
uniform vec2 u_mouse;
// NOTICE: Chinese comment use UTF-8
// Function Definitions
// SDF Functions
float remap(float value, float origFrom, float origTo, float targetFrom, float targetTo) {
float normalizedValue = (value - origFrom) / (origTo - origFrom);
return targetFrom + (targetTo - targetFrom) * normalizedValue;
}
float getRuffleNoise(float angle, float time,float noiseScale) {
// numbers here has no special meaning, just random values
float noise = sin(angle * 8.3 + time * 2.0) * 0.5;
noise += sin(angle * 17.7 - time * 3.5) * 0.25;
noise += sin(angle * 37.9 + time * 4.5) * 0.125;
return noise * noiseScale; //the noise strength
}
vec3 getBodyPattern(vec2 uv, vec3 baseCol, vec3 scaleCol, vec3 hintCol, float scale, float rot, float iTime){
// geo calc
mat2 rotMat = mat2(cos(rot), -sin(rot), sin(rot), cos(rot));
vec2 st = rotMat * uv;
st *= scale * 7.0;
st.x += step(1.0, mod(st.y, 2.0)) * 0.5;
vec2 cellUV = fract(st);
cellUV.y = remap(cellUV.y, 0.0, 1.0, 0.0, 0.65); // 将 y 方向压缩为等边三角形高度比例
// dist
float d = length(cellUV - vec2(0.5, 0.0));
float scaleShape = smoothstep(0.65, 0.45, d);
// thickness
float shadow = smoothstep(0.2, 0.45, d);
// color mix
vec3 baseColor = baseCol;
vec3 scaleColor = scaleCol;
vec3 iridescent = hintCol * sin(uv.y * 5.0 + iTime*5.0 * 0.5);
// 混合:底色 + 鳞片形状 * (鳞片色 + 偏光)
// 减去 shadow 让边缘变暗,而不是变亮,这样看起来像凸起而不是孔洞
vec3 finalColor = baseColor + scaleShape * (scaleColor + iridescent) - shadow * 0.2;
return clamp(finalColor, 0.0, 1.0);
}
vec3 getFinPattern(vec2 uv, vec3 mainCol, vec3 subCol, float density, float rot, vec2 st, float scale, float iTime){
vec2 u0 = uv;
float speed = 0.5;
uv*=scale;
mat2 rotMat = mat2(cos(rot), sin(rot), -sin(rot), cos(rot));
uv = rotMat * uv;
uv -= st;
float shapeFactor = cos(clamp(uv.x * 1.2, -1.57, 1.57));
uv.y /= (shapeFactor * shapeFactor + 0.1);
for (int i = 0; i < 4; i++) {
float fi = float(i);
float offset = 0.15 / (fi + 1.0) * sin(fi * 3.0 * uv.y + iTime * speed + 0.3 * fi) + 0.5;
uv.x += offset;
uv.y += 0.1 / (fi + 1.0) * sin(fi * 4.0 * uv.x + iTime * speed * 0.8);
}
float pattern = sin(uv.x * density + uv.y * density);
pattern = abs(pattern);
pattern = 0.50 / max(pattern, 0.001); // max防止除0
pattern = pow(pattern,1.8);
pattern = min(pattern, 2.0);
vec3 col = mainCol;
col += vec3(0.2, 0.1, 0.4) * sin(u0.y * 2.0 + uv.y);
col *= pattern;
col += subCol * (1.0 - pattern);
float mask = smoothstep(0.0, 0.8, shapeFactor);
mask = pow(mask, 3.0);
//float vFade = 1.0 - smoothstep(0.5, 1.0, abs(u0.y));
col *= mask;// * vFade;
return col;
}
float sdEgg( vec2 p, float he, float ra, float rb, float bu )
{
float r = 0.5*(he + ra+rb)/bu;
float da = r - ra;
float db = r - rb;
float y = (db*db - da*da - he*he)/(2.0*he);
float x = sqrt(da*da - y*y);
p.x = abs(p.x);
float k = p.y*x - p.x*y;
if( k>0.0 && k<he*(p.x+x) )
return length(p+vec2(x,y))-r;
return min( length(p)-ra,
length(vec2(p.x,p.y-he))-rb );
}
float sdBettaTailShape( vec2 p, vec2 c, float r, float iTime )
{
float angle = atan(p.y, p.x);
float ruffleOffset = getRuffleNoise(angle, iTime,0.12) * r;
p.x = abs(p.x);
float l = length(p) - (r + ruffleOffset);
float m = length(p-c*clamp(dot(p,c),0.0,r));
return max(l,m*sign(c.y*p.x-c.x*p.y));
}
float sdOrientedVesica( vec2 p, vec2 a, vec2 b, float w )
{
float r = 0.5*length(b-a);
float d = 0.5*(r*r-w*w)/w;
vec2 v = (b-a)/r;
vec2 c = (b+a)*0.5;
vec2 q = 0.5*abs(mat2(v.y,v.x,-v.x,v.y)*(p-c));
vec3 h = (r*q.x<d*(q.y-r)) ? vec3(0.0,r,0.0) : vec3(-d,0.0,d+w);
return length( q-h.xy) - h.z;
}
float sdMoon(vec2 p, float d, float ra, float rb, float intensity, float iTime )
{
// 对称性
// 原函数利用了上下对称性。这意味着我们的噪声也会随之上下对称(镜像)。
// 如果想要非对称的噪声,需要修改这行代码的逻辑,但为了保持 SDF 算法的稳定性,
// 我们暂时保留这个对称性,这意味着月亮咬痕的上下两半是镜像的。
p.y = abs(p.y);
// 相对小圆心角度
// 小圆圆心在 (d, 0)。
// 我们需要计算当前点 p 到 (d, 0) 的向量。
vec2 relP = p - vec2(d, 0.0);
// 获取角度。atan(y, x) 返回弧度
float angle = atan(relP.y, relP.x);
//扰动后半径
// 这里的 rb 变成了变量
// 注意:我们把 getNoise 的结果加到 rb 上
float rbNoisy = rb + getRuffleNoise(angle,iTime, intensity);
//原本公式中所有用到 rb 的地方,现在全部都要换成 rbNoisy
float a = (ra*ra - rbNoisy*rbNoisy + d*d)/(2.0*d);
float b = sqrt(max(ra*ra-a*a,0.0));
if( d*(p.x*b-p.y*a) > d*d*max(b-p.y,0.0) )
return length(p-vec2(a,b));
return max( (length(p)-ra),
-(length(p-vec2(d,0))-rbNoisy));
}
//do not use this directly, use sUnion instead
//this method encapsuled in the sUnion func
float sMinCubic( float a, float b, float k )
{
float h = max( k - abs(a-b), 0.0 )/k;
return min( a, b ) - h*h*h*k*(1.0/6.0);
}
float sDiffCubic( float d1, float d2, float k )
{
float h = max( k - abs(d1 + d2), 0.0 )/k;
return max( d1, -d2 ) + h*h*h*k*(1.0/6.0);
}
//input: 1d distance and 3d color.
//this function returns the smooth union of d(sdf result) and color
vec4 sUnion(vec4 dc1, vec4 dc2, float k) {
float h = clamp( 0.5 + 0.5 * (dc1.x - dc2.x) / k, 0.0, 1.0 );
float dist = sMinCubic(dc1.x, dc2.x, k);
vec3 col = mix(dc1.yzw, dc2.yzw, h);
return vec4(dist, col);
}
float hash( vec2 p ) {
p = vec2( dot(p,vec2(127.1,311.7)),
dot(p,vec2(269.5,183.3)) );
return fract(sin(dot(p, vec2(12.9898, 78.233))) * 43758.5453);
}
//value noise
float noise( vec2 x ) {
vec2 i = floor(x);
vec2 f = fract(x);
//smoothstep for interpolation
vec2 u = f*f*(3.0-2.0*f);
//random values for the 4 corners of the cell
float a = hash( i + vec2(0.0,0.0) );
float b = hash( i + vec2(1.0,0.0) );
float c = hash( i + vec2(0.0,1.0) );
float d = hash( i + vec2(1.0,1.0) );
//bilinearly interpolate
return mix(mix( a, b, u.x), mix( c, d, u.x), u.y);
}
//returns the distances to the nearest and second-nearest cell centers
vec2 voronoi(vec2 uv, float time) {
vec2 g = floor(uv); // Grid cell
vec2 f = fract(uv); // Position within cell
vec2 closest = vec2(1.0); // .x is dist to nearest, .y is dist to 2nd nearest
//check 3x3 grid of cells around the current one
for (int y = -1; y <= 1; y++) {
for (int x = -1; x <= 1; x++) {
vec2 offset = vec2(x, y);
vec2 cellPos = g + offset;
// pseudo random anime
float h = hash(cellPos);
vec2 pointPos = 0.5 + 0.4 * vec2(sin(time * 0.7 + h * 6.28), cos(time * 0.7 + h * 6.28)); // 2*PI
float dist = length(offset + pointPos - f);
//update two closest distances
if (dist < closest.x) {
closest.y = closest.x;
closest.x = dist;
} else if (dist < closest.y) {
closest.y = dist;
}
}
}
return closest;
}
vec3 getWavePattern(vec2 uv, vec3 mainCol, vec3 subCol, float scale, float iTime) {
vec2 scaledUV = uv * scale * 6.0;
vec2 dists = voronoi(scaledUV, iTime);
// The first distance gives a nice gradient within the cell
float cellInterior = dists.x;
// The difference between the two distances defines the cell edge
float edgeDistance = dists.y - dists.x;
vec3 waterColor = mainCol;
waterColor = mix(subCol, waterColor, smoothstep(0.0, 0.4, cellInterior));
//flicker mask
float flickerNoiseSample = noise(scaledUV * 0.5 + iTime * 0.2);
//a second, smoother noise for shimmer instead of a raw hash
float shimmerNoise = noise(scaledUV * 1.5 + iTime * -0.5);
float shimmer = (0.6 + 0.4 * sin(iTime * 8.0 + shimmerNoise * 6.28));
float flickerMask = mix(0.5, 1.0, flickerNoiseSample);
flickerMask *= shimmer;
//bloom effect
float edgeWidth = fwidth(edgeDistance) * 1.5; // Get pixel width for AA
//sharp core
float sharpGlow = pow(1.0 - smoothstep(0.0, edgeWidth * 2.0, edgeDistance), 40.0);
//softer bloom
float softGlow = pow(1.0 - smoothstep(0.0, edgeWidth * 8.0, edgeDistance), 10.0);
//combine the bloom layers
float totalBloom = sharpGlow * 1.0 + softGlow * 0.3;
//combine bloom with the flicker mask
float sparkle = totalBloom * flickerMask;
vec3 sparkleColor = vec3(0.9, 1.0, 1.0);
//combine water color with the sparkling highlights
vec3 finalColor = waterColor + sparkle * sparkleColor;
return clamp(finalColor, 0.0, 1.0);
}
vec2 getWaterRipple(vec2 uv, vec2 center, float time) {
vec2 dv = center - uv;
float dis = length(dv);
//ripple parameters
float frequency = 15.0;
float speed = 5.0;
float maxRadius = 1.2;
float amplitude = 0.03;
//sine Factor (The wave)
//usually "- time" looks like expanding waves, "+ time" looks like imploding.
float sinFactor = sin(dis * frequency - time * speed);
//damping/Falloff
//limits the effect to a specific radius and fades it out at the edges
float damp = max(0.0, maxRadius - dis);
damp = pow(damp, 2.0);
vec2 dir = normalize(dv);
//avoid 0/0
if (dis < 0.0001) dir = vec2(0.0);
//final offset
return dir * sinFactor * damp * amplitude;
}
void main() {
vec2 st = gl_FragCoord.xy / u_resolution.xy;
// st: left bottom (0,0) to right top (1,1), linear
vec2 centcoord = st * 2.0 - 1.0;
vec2 mousePos = u_mouse.xy / u_resolution.xy;
vec2 rippleCenter = mousePos * 2.0 - 1.0;
//calculate the offset
vec2 rippleOffset = getWaterRipple(centcoord, rippleCenter, u_time);
//apply offset
centcoord += rippleOffset;
// centcoord: left bottom (-1,-1) to right top (1,1), linear
vec3 bodyColor = getBodyPattern(centcoord,vec3(0.4314, 0.1765, 0.502), vec3(0.2, 0.7, 0.8), vec3(0.3765, 0.1608, 0.8039),1.952,2.032, u_time);
vec3 backfinColor = getFinPattern(centcoord,vec3(0.1608, 0.5961, 0.7843),vec3(0.3451, 0.0235, 0.0235),23.0,-0.192,vec2(-0.200,0.590),1.128,u_time);
vec3 bellyfinColor = getFinPattern(centcoord,vec3(0.1647, 0.5686, 0.8784),vec3(0.3451, 0.0235, 0.0235),21.0,-0.856,vec2(0.480,0.630),1.80,u_time);
vec3 tailColor = getFinPattern(centcoord,vec3(0.1412, 0.502, 0.7804),vec3(0.3451, 0.0235, 0.0235),18.0,-0.416,vec2(-0.790,0.580),1.0,u_time);
vec3 waterColor = getWavePattern(centcoord, vec3(0.3686, 0.7176, 0.7686), vec3(0.549, 0.7922, 0.8039), 0.7, u_time);
vec2 uv = centcoord;
// --- 1. THE HEAD (ID:0) ---
vec2 p0 = centcoord - vec2(0.300, -0.080);
vec2 a0 = vec2(0.640, 0.760);
vec2 b0 = vec2(0.140, 0.120);
float w0 = 0.176;
float d0 = sdOrientedVesica(p0, a0, b0, w0);
// --- 2. THE BODY (ID:1) ---
vec2 p1 = centcoord - vec2(0.640, 0.340);
float theta1 = -4.256;
mat2 rotate1 = mat2(cos(theta1), -sin(theta1), sin(theta1), cos(theta1));
float he1 = 0.488;
float ra1 = 0.204;
float rb1 = 0.144;
float bu1 = 0.528;
float d1 = sdEgg(rotate1 * p1, he1, ra1, rb1, bu1);
// --- 3. THE TAIL (ID:2) ---
vec2 p2 = centcoord - vec2(0.140, 0.140);
float theta2 = -4.360;
mat2 rotate2 = mat2(cos(theta2), -sin(theta2), sin(theta2), cos(theta2));
vec2 c2 = vec2(0.920, 0.280);
float r2 = 1.00;
float itime2 = u_time;
float d2 = sdBettaTailShape(rotate2 * p2, c2, r2, itime2);
//--- 4. THE BACK FIN (ID:3) ---
vec2 p3 = centcoord - vec2(-0.610,0.410);
float theta3 = 0.448;
mat2 rotate3 = mat2(cos(theta3), -sin(theta3), sin(theta3), cos(theta3));
float cfactor = 0.736;
mat2 compress = mat2(cfactor*st.x,0,0,1);
float pm3 = -0.440;
float ra3 = 0.480;
float rb3 = 0.688;
float iTime3 = u_time;
float intensity3 = 0.042;
float d3 = sdMoon(rotate3*compress*p3,pm3,ra3,rb3,intensity3,iTime3);
// --- 5. The belly fin(ID:4) ---
vec2 p4 = centcoord - vec2(0.390,0.050);
float theta4 = -5.408;
mat2 rotate4 = mat2(cos(theta4), -sin(theta4), sin(theta4), cos(theta4));
float pm4 = -0.752;
float ra4 = 0.376;
float rb4 = 0.616;
float iTime4 = u_time;
float intensity4 = 0.042;
float d4 = sdMoon(rotate4*p4,pm4,ra4,rb4,intensity4,iTime4);
// COLOR
// --- 6. CLIP AND COMBINE ---
float headBodyK = 0.096;
float finK = 0.04;
//k for smooth union & color lerp factor
float clipMargin = -0.01;
float dHeadbody = sMinCubic(d0, d1, headBodyK);
float dTail = d2;
float dBackfin = d3;
float dBellyfin = d4;
//we use xxCore for and ONLY for clipping redundant d of fin
float dBodyCore = dHeadbody + clipMargin;
float dTailCore = dTail + clipMargin-0.1;
dBackfin = sDiffCubic(dBackfin, dBodyCore, 0.02);
dBellyfin = sDiffCubic(dBellyfin, dBodyCore, 0.02);
dBellyfin = sDiffCubic(dBellyfin, d3, 0.02);
dBellyfin = sDiffCubic(dBellyfin,dTailCore,0.02);
vec4 tempRes = vec4(dHeadbody, bodyColor);
tempRes = sUnion(tempRes, vec4(dBackfin, backfinColor), finK);
tempRes = sUnion(tempRes, vec4(dBellyfin, bellyfinColor), finK);
if(dBackfin<0.0&&dTail<0.01){
tempRes.yzw = tailColor;
}
tempRes = sUnion(tempRes,vec4(dTail,tailColor), finK);
float dFinal = tempRes.x;
float mask = step(dFinal,0.0);
float eyeMask = step(length(centcoord - vec2(0.800, 0.53)) , 0.0450);
eyeMask = remap(eyeMask, 0.0, 1.0, 1.0, 0.0);
float eyeMaskHighlight = step(length(centcoord - vec2(0.790, 0.54)) , 0.01);
//eyeMaskHighlight = remap(eyeMaskHighlight, 0.0, 1.0, 1.0, 0.0);
//temp cancel
// Output color
//colour_out = vec4(tempRes.yzw,1.0);
colour_out = mask * vec4(tempRes.yzw, 1.0)+ (1.0 - mask) * vec4(waterColor, 1.0);
//the most violent way to draw eye
colour_out.xyz = colour_out.xyz * eyeMask;
colour_out.xyz += eyeMaskHighlight * vec3(1.0, 1.0, 1.0);
colour_out = clamp(colour_out, 0.0, 1.0);
}
TryMouseInteractOnMeFish by ameato9ei
uniform vec2 u_resolution;
uniform float u_time;
// --- SDF関数群 ---
// 回転行列(ラジアン)
mat2 rot(float a) {
float c = cos(a), s = sin(a);
return mat2(c, -s, s, c);
}
// 角度(度)から回転した方向ベクトルを返す
vec2 rotateDir(float deg) {
vec2 dir0 = normalize(vec2(0.0, -1.0));
return rot(radians(deg)) * dir0;
}
// 楕円の SDF
float sdEllipse(vec2 p, vec2 center, vec2 radius, float angle) {
vec2 q = rot(angle) * (p - center);
return length(q / radius) - 1.0;
}
// 角丸矩形の SDF
float sdBox(vec2 p, vec2 center, vec2 halfSize, float angle, float r) {
vec2 q = rot(angle) * (p - center);
vec2 d = abs(q) - halfSize;
return length(max(d, 0.0)) - r;
}
// 円の SDF
float sdCircle(vec2 p, vec2 center, float radius) {
return length(p - center) - radius;
}
// 扇形(円と角度範囲の共通部分)
float sdFan(vec2 p, vec2 center, float radius, vec2 dir, float theta) {
vec2 q = p - center;
float r = length(q) - radius;
float ang = acos(clamp(dot(normalize(q), dir), -1.0, 1.0));
float a = ang - theta;
return max(r, a);
}
// 三角形の SDF
float sdTriangle(vec2 p, vec2 a, vec2 b, vec2 c) {
vec2 ba = b - a; vec2 pa = p - a;
vec2 cb = c - b; vec2 pb = p - b;
vec2 ac = a - c; vec2 pc = p - c;
vec2 nor = vec2(ba.y, -ba.x);
float s = sign(dot(nor, p - a));
float h1 = clamp(dot(pa, ba) / dot(ba, ba), 0.0, 1.0);
float h2 = clamp(dot(pb, cb) / dot(cb, cb), 0.0, 1.0);
float h3 = clamp(dot(pc, ac) / dot(ac, ac), 0.0, 1.0);
float d1 = length(pa - ba * h1);
float d2 = length(pb - cb * h2);
float d3 = length(pc - ac * h3);
return min(min(d1, d2), d3) * s;
}
// 平行四辺形の SDF(中心、サイズ、回転角度、シア量)
float sdParallelogram(vec2 p, vec2 center, vec2 size, float angle, float skew) {
vec2 q = p - center;
q = rot(angle) * q;
return max(
abs(q.x - skew * q.y) - size.x * 0.5,
abs(q.y) - size.y * 0.5
);
}
// Noise2D関数
// Description : Array and textureless GLSL 2D simplex noise function.
// Author : Ian McEwan, Ashima Arts.
// Maintainer : stegu
// Lastmod : 20110822 (ijm)
// License : Copyright (C) 2011 Ashima Arts. All rights reserved.
// Distributed under the MIT License. See LICENSE file.
// https://github.com/ashima/webgl-noise
// https://github.com/stegu/webgl-noise
//
vec3 mod289(vec3 x) {
return x - floor(x * (1.0 / 289.0)) * 289.0;
}
vec2 mod289(vec2 x) {
return x - floor(x * (1.0 / 289.0)) * 289.0;
}
vec3 permute(vec3 x) {
return mod289(((x*34.0)+10.0)*x);
}
float snoise(vec2 v)
{
const vec4 C = vec4(0.211324865405187, // (3.0-sqrt(3.0))/6.0
0.366025403784439, // 0.5*(sqrt(3.0)-1.0)
-0.577350269189626, // -1.0 + 2.0 * C.x
0.024390243902439); // 1.0 / 41.0
// First corner
vec2 i = floor(v + dot(v, C.yy) );
vec2 x0 = v - i + dot(i, C.xx);
// Other corners
vec2 i1;
//i1.x = step( x0.y, x0.x ); // x0.x > x0.y ? 1.0 : 0.0
//i1.y = 1.0 - i1.x;
i1 = (x0.x > x0.y) ? vec2(1.0, 0.0) : vec2(0.0, 1.0);
// x0 = x0 - 0.0 + 0.0 * C.xx ;
// x1 = x0 - i1 + 1.0 * C.xx ;
// x2 = x0 - 1.0 + 2.0 * C.xx ;
vec4 x12 = x0.xyxy + C.xxzz;
x12.xy -= i1;
// Permutations
i = mod289(i); // Avoid truncation effects in permutation
vec3 p = permute( permute( i.y + vec3(0.0, i1.y, 1.0 ))
+ i.x + vec3(0.0, i1.x, 1.0 ));
vec3 m = max(0.5 - vec3(dot(x0,x0), dot(x12.xy,x12.xy), dot(x12.zw,x12.zw)), 0.0);
m = m*m ;
m = m*m ;
// Gradients: 41 points uniformly over a line, mapped onto a diamond.
// The ring size 17*17 = 289 is close to a multiple of 41 (41*7 = 287)
vec3 x = 2.0 * fract(p * C.www) - 1.0;
vec3 h = abs(x) - 0.5;
vec3 ox = floor(x + 0.5);
vec3 a0 = x - ox;
// Normalise gradients implicitly by scaling m
// Approximation of: m *= inversesqrt( a0*a0 + h*h );
m *= 1.79284291400159 - 0.85373472095314 * ( a0*a0 + h*h );
// Compute final noise value at P
vec3 g;
g.x = a0.x * x0.x + h.x * x0.y;
g.yz = a0.yz * x12.xz + h.yz * x12.yw;
return 130.0 * dot(m, g);
}
void main() {
vec2 uv = gl_FragCoord.xy / u_resolution;
vec2 p = uv;
// 1. 背景 (黒)
vec3 col = vec3(0.0);
// 座標変換: 魚全体を少し揺らす
float t = u_time * 2.0;
vec2 offset = vec2(0.0, 0.02 * sin(t));
p -= offset;
// --- レイヤー描画 (奥から順に上書き) ---
// 2. 尾びれ (赤)
// 上の大きな扇
float tailTop = sdFan(
p,
vec2(0.32, 0.45), // ヒレの中心位置
0.60, // 半径(大きさ)
rotateDir(-110.0), // 回転後の方向ベクトル
0.7 // 開き具合(角度の広さ)
);
if (tailTop < 0.0) {
vec2 finLocal = rot(-radians(110.0)) * (p - vec2(0.32, 0.45));
float finRadius = clamp(length(finLocal) / 0.60, 0.0, 1.0);
// 中心からの角度を計算(放射状の線用)
float angle = atan(finLocal.y, finLocal.x);
float normalizedAngle = (angle + 3.14159265359) / (2.0 * 3.14159265359); // 0〜1に正規化
// ノイズを使って放射状の線を生成
vec2 noiseCoord = vec2(normalizedAngle * 20.0, finRadius * 10.0);
noiseCoord.x = noiseCoord.x * 10.0;
float noiseValue = snoise(noiseCoord);
float radialLines = smoothstep(0.3, 0.7, noiseValue); // ノイズから線を抽出
vec3 finColorA = vec3(0.80);
vec3 finColorB = vec3(0.004, 0.114, 0.275) * 1.3;
vec3 finColorC = vec3(0.655, 0.133, 0.063);
float s1 = 0.3;
float s2 = 0.75;
vec3 finColor;
if (finRadius < s1) {
// 内側: finColorA → finColorB
float t = finRadius / s1;
finColor = mix(finColorA, finColorB, t);
} else if (finRadius < s2) {
// 中間: finColorB → finColorC
float t = (finRadius - s1) / (s2 - s1);
finColor = mix(finColorB, finColorC, t);
} else {
// 外側: finColorC を維持
finColor = finColorC;
}
// 放射状の線を色に反映(線の部分を少し暗くする)
finColor = mix(finColor, finColor * 0.6, radialLines * 0.4);
col = finColor;
}
// 下の扇
float tailBot = sdFan(
p,
vec2(0.32, 0.40), // ヒレの中心位置
0.28, // 半径(大きさ)
rotateDir(-40.0), // 回転後の方向ベクトル
0.7 // 開き具合(角度の広さ)
);
if (tailBot < 0.0) {
// 中心からの距離を 0〜1 に正規化
vec2 finLocal = rot(-radians(110.0)) * (p - vec2(0.32, 0.45));
float finRadius = clamp(length(finLocal) / 0.28, 0.0, 1.0);
// 中心からの角度を計算(放射状の線用)
float angle = atan(finLocal.y, finLocal.x);
float normalizedAngle = (angle + 3.14159265359) / (2.0 * 3.14159265359); // 0〜1に正規化
// ノイズを使って放射状の線を生成
vec2 noiseCoord = vec2(normalizedAngle * 20.0, finRadius * 10.0);
noiseCoord.x = noiseCoord.x * 10.0;
float noiseValue = snoise(noiseCoord);
float radialLines = smoothstep(0.3, 0.7, noiseValue); // ノイズから線を抽出
vec3 finColorA = vec3(0.80);
vec3 finColorB = vec3(0.004, 0.114, 0.275) * 1.3;
vec3 finColorC = vec3(0.655, 0.133, 0.063);
float s1 = 0.5;
float s2 = 0.75;
vec3 finColor;
if (finRadius < s1) {
// 内側: finColorA → finColorB
float t = finRadius / s1;
finColor = mix(finColorA, finColorB, t);
} else if (finRadius < s2) {
// 中間: finColorB → finColorC
float t = (finRadius - s1) / (s2 - s1);
finColor = mix(finColorB, finColorC, t);
} else {
// 外側: finColorC を維持
finColor = finColorC;
}
// 放射状の線を色に反映(線の部分を少し暗くする)
finColor = mix(finColor, finColor * 0.7, radialLines * 0.3);
col = finColor;
}
// 3. 腹びれ
float analFin = sdParallelogram(
p,
vec2(0.45, 0.22), // 中心位置
vec2(0.18, 0.07), // サイズ(縦横)
radians(-35.0), // 回転角度
0.10 // x方向へのシア
);
if (analFin < 0.0) {
// ノイズを使って放射状の線を生成
float radius = clamp(length(p) / 0.18, 0.0, 1.0);
vec2 p_temp = vec2(p.x*10.0, p.y*10.0);
float noiseValue = snoise(p_temp);
vec3 finColorA = vec3(0.55, 0.12, 0.12)*1.3;
vec3 finColorB = vec3(0.10);
vec3 finColor_temp = mix(finColorA, finColorB, radius);
vec3 finColor = mix(finColor_temp, finColor_temp * 0.7, noiseValue * 0.3);
col = finColor;
}
// 奥のヒレ
float pecFinBack = sdFan(
p,
vec2(0.65, 0.20), // ヒレの中心位置
0.12, // 半径(大きさ)
rotateDir(-82.0), // 回転後の方向ベクトル
0.7 // 開き具合(角度の広さ)
);
if (pecFinBack < 0.0) {
// 扇形に沿ったグラデーション(中心から扇の外周へ向かう)
vec2 finLocal = rot(-radians(82.0)) * (p - vec2(0.65, 0.20));
float finRadius = clamp(length(finLocal) / 0.12, 0.0, 1.0);
// 中心からの角度を計算(放射状の線用)
float angle = atan(finLocal.y, finLocal.x);
float normalizedAngle = (angle + 3.14159265359) / (2.0 * 3.14159265359); // 0〜1に正規化
// ノイズを使って放射状の線を生成
vec2 noiseCoord = vec2(normalizedAngle * 20.0, finRadius * 10.0);
noiseCoord.x = noiseCoord.x * 10.0;
float noiseValue = snoise(noiseCoord);
float radialLines = smoothstep(0.3, 0.7, noiseValue); // ノイズから線を抽出
vec3 finColorA = vec3(0.55, 0.12, 0.12)*1.3;
vec3 finColorB = vec3(0.10);
vec3 finColor_temp = mix(finColorA, finColorB, finRadius);
vec3 finColor = mix(finColor_temp, finColor_temp * 0.7, radialLines * 0.4);
col = finColor;
}
// 6. 胴体 (グレー) - 斜めの楕円 + パターン
float body = sdEllipse(p, vec2(0.50, 0.30), vec2(0.24, 0.095), radians(-40.0));
// 5. 尾の付け根 - 胴体と尾の間にある三角形
float tailJoint = sdFan(
p,
vec2(0.41, 0.57), // 中心位置(調整可)
0.17, // 半径
rotateDir(25.0), // 方向
0.25 // 開き具合(狭めに)
);
if (body < 0.0 || tailJoint < 0.0) {
vec2 patternP = p;
patternP *= 30.0;
// 胴体の傾きに合わせてパターンを回転(-40度)
patternP = rot(radians(40.0)) * patternP;
vec2 s = vec2(2.0, 1.0);
patternP = mod(patternP, s) - vec2(0.5 * s.x, 0.0);
patternP.x = abs(patternP.x);
float d0 = length(patternP - vec2(0.0, s.y));
float d1 = length(patternP - 0.5 * s);
float d2 = length(patternP);
float d3 = length(patternP - 0.5 * vec2(s.x, -s.y));
float a = 3.14159265359 * 2.0 * 4.0;
float e1 = 0.2;
float e2 = 0.01;
float v = smoothstep(-e1, e1, sin(d0 * a));
v = mix(v, smoothstep(-e1, e1, sin(d1 * a)), 1.0 - smoothstep(s.y - e2, s.y, d1));
v = mix(v, smoothstep(-e1, e1, sin(d2 * a)), 1.0 - smoothstep(s.y - e2, s.y, d2));
v = mix(v, smoothstep(-e1, e1, sin(d3 * a)), 1.0 - smoothstep(s.y - e2, s.y, d3));
// パターンとグレーを組み合わせ
col = mix(vec3(0.8), vec3(v), 0.2);
}
// 中央の扇
float tailMid = sdFan(
p,
vec2(0.31, 0.40), // ヒレの中心位置
0.45, // 半径(大きさ)
rotateDir(-185.0), // 回転後の方向ベクトル
0.5 // 開き具合(角度の広さ)
);
if (tailMid < 0.0) {
vec2 finLocal = rot(-radians(110.0)) * (p - vec2(0.32, 0.45));
float finRadius = clamp(length(finLocal) / 0.45, 0.0, 1.0);
// 中心からの角度を計算(放射状の線用)
float angle = atan(finLocal.y, finLocal.x);
float normalizedAngle = (angle + 3.14159265359) / (2.0 * 3.14159265359); // 0〜1に正規化
// ノイズを使って放射状の線を生成
vec2 noiseCoord = vec2(normalizedAngle * 20.0, finRadius * 10.0);
noiseCoord.x = noiseCoord.x * 10.0;
float noiseValue = snoise(noiseCoord);
float radialLines = smoothstep(0.3, 0.7, noiseValue); // ノイズから線を抽出
vec3 finColorA = vec3(0.80);
vec3 finColorB = vec3(0.004, 0.114, 0.275) * 1.3;
vec3 finColorC = vec3(0.655, 0.133, 0.063);
float s1 = 0.3;
float s2 = 0.75;
vec3 finColor;
if (finRadius < s1) {
// 内側: finColorA → finColorB
float t = finRadius / s1;
finColor = mix(finColorA, finColorB, t);
} else if (finRadius < s2) {
// 中間: finColorB → finColorC
float t = (finRadius - s1) / (s2 - s1);
finColor = mix(finColorB, finColorC, t);
} else {
// 外側: finColorC を維持
finColor = finColorC;
}
// 放射状の線を色に反映(線の部分を少し暗くする)
finColor = mix(finColor, finColor * 0.7, radialLines * 0.3);
col = finColor;
}
// 7. 口 (濃いグレー) - 先端の四角
float mouth = sdParallelogram(
p,
vec2(0.662, 0.147), // 中心位置
vec2(0.06, 0.04), // サイズ(縦横)
radians(-0.0), // 回転角度
0.20 // x方向へのシア
);
if (mouth < 0.0) col = vec3(0.5);
// 9. 胸びれ (濃い赤) - エラ部分にある扇形
// 手前のヒレ
vec2 frontFinCenter = vec2(0.58, 0.20);
float pecFinFront = sdFan(p, frontFinCenter, 0.12, rotateDir(88.0), 0.8);
if (pecFinFront < 0.0) {
// 扇形に沿ったグラデーション(中心から扇の外周へ向かう)
vec2 finLocal = rot(-radians(88.0)) * (p - frontFinCenter);
float finRadius = clamp(length(finLocal) / 0.12, 0.0, 1.0);
// 中心からの角度を計算(放射状の線用)
float angle = atan(finLocal.y, finLocal.x);
float normalizedAngle = (angle + 3.14159265359) / (2.0 * 3.14159265359); // 0〜1に正規化
// ノイズを使って放射状の線を生成
vec2 noiseCoord = vec2(normalizedAngle * 20.0, finRadius * 10.0);
noiseCoord.x = noiseCoord.x * 10.0;
float noiseValue = snoise(noiseCoord);
float radialLines = smoothstep(0.3, 0.7, noiseValue); // ノイズから線を抽出
vec3 finColorA = vec3(0.55, 0.12, 0.12)*1.3;
vec3 finColorB = vec3(0.10);
vec3 finColor_temp = mix(finColorA, finColorB, finRadius);
vec3 finColor = mix(finColor_temp, finColor_temp * 0.7, radialLines * 0.4);
col = finColor;
}
// 8. 目 (白・黒)
float eyeWhite = sdEllipse(p, vec2(0.605, 0.18), vec2(0.018, 0.023), radians(-20.0));
float eyeBlack = sdEllipse(p, vec2(0.605, 0.18), vec2(0.01, 0.015), radians(-20.0));
if (eyeWhite < 0.0) col = vec3(1.0);
if (eyeBlack < 0.0) col = vec3(0.0);
colour_out = vec4(col, 1.0);
}
Unknown by Manchonot
uniform vec2 u_resolution;
uniform vec2 u_mouse;
uniform float u_time;
///////////////////////////////////
// Modulo 289 without a division (only multiplications)
vec3 mod289(vec3 x) {
return x - floor(x * (1.0 / 289.0)) * 289.0;
}
vec2 mod289(vec2 x) {
return x - floor(x * (1.0 / 289.0)) * 289.0;
}
// Modulo 7 without a division
vec3 mod7(vec3 x) {
return x - floor(x * (1.0 / 7.0)) * 7.0;
}
// Permutation polynomial: (34x^2 + x) mod 289
vec3 permute(vec3 x) {
return mod289((34.0 * x + 1.0) * x);
}
// Cellular noise, returning F1 and F2 in a vec2.
// Standard 3x3 search window for good F1 and F2 values
vec2 cellular(vec2 P) {
const float K = 0.142857142857; // 1/7
const float Ko = 0.428571428571; // 3/7
const float jitter = 1.0; // Less gives more regular pattern
vec2 Pi = mod289(floor(P));
vec2 Pf = fract(P);
vec3 oi = vec3(-1.0, 0.0, 1.0);
vec3 of = vec3(-0.5, 0.5, 1.5);
vec3 px = permute(Pi.x + oi);
vec3 p = permute(px.x + Pi.y + oi); // p11, p12, p13
vec3 ox = fract(p * K) - Ko;
vec3 oy = mod7(floor(p * K))*K - Ko;
vec3 dx = Pf.x + 0.5 + jitter * ox;
vec3 dy = Pf.y - of + jitter * oy;
vec3 d1 = dx * dx + dy * dy; // d11, d12 and d13, squared
p = permute(px.y + Pi.y + oi); // p21, p22 and p23
ox = fract(p * K) - Ko;
oy = mod7(floor(p * K))*K - Ko;
dx = Pf.x - 0.5 + jitter * ox;
dy = Pf.y - of + jitter * oy;
vec3 d2 = dx * dx + dy * dy; // d21, d22 and d23, squared
p = permute(px.z + Pi.y + oi); // p31, p32 and p33
ox = fract(p * K) - Ko;
oy = mod7(floor(p * K))*K - Ko;
dx = Pf.x - 1.5 + jitter * ox;
dy = Pf.y - of + jitter * oy;
vec3 d3 = dx * dx + dy * dy; // d31, d32 and d33, squared
// Sort out the two smallest distances (F1, F2)
vec3 d1a = min(d1, d2);
d2 = max(d1, d2); // Swap to keep candidates for F2
d2 = min(d2, d3); // neither F1 nor F2 are now in d3
d1 = min(d1a, d2); // F1 is now in d1
d2 = max(d1a, d2); // Swap to keep candidates for F2
d1.xy = (d1.x < d1.y) ? d1.xy : d1.yx; // Swap if smaller
d1.xz = (d1.x < d1.z) ? d1.xz : d1.zx; // F1 is in d1.x
d1.yz = min(d1.yz, d2.yz); // F2 is now not in d2.yz
d1.y = min(d1.y, d1.z); // nor in d1.z
d1.y = min(d1.y, d2.x); // F2 is in d1.y, we're done.
return sqrt(d1.xy);
}
// Demo mapping
float colorMapping(vec2 xy) {
return cellular(xy).x * 2.0 - 1.0;
}
///////////////////////////////////
float dot2(vec2 v) {
return dot(v, v);
}
float swimPhase() {
return u_time * 0.8;
}
float pectoralPhase(){
return u_time * 0.5;
}
const float PEC_BASE_ANGLE_DEG = -75.0;
// convert degree to rad
float deg2rad(float d) {
return d * 3.14159265 / 180.0;
}
float pectoralSwingOneSide() {
float s = sin(pectoralPhase());
return s;
}
// 1D Hash. 10 and 10000 aren't ideal, but oh well, good enough
float hash1(float n) {
return fract(sin(n * 10.0) * 10000.0);
}
// basic sdBezier
float sdBezier(in vec2 pos, in vec2 A, in vec2 B, in vec2 C)
{
vec2 a = B - A;
vec2 b = A - 2.0 * B + C;
vec2 c = a * 2.0;
vec2 d = A - pos;
float kk = 1.0 / dot(b, b + 1e-9); // anti-zero division
float kx = kk * dot(a, b);
float ky = kk * (2.0 * dot(a, a) + dot(d, b)) / 3.0;
float kz = kk * dot(d, a);
float res = 0.0;
float p = ky - kx * kx;
float p3 = p * p * p;
float q = kx * (2.0 * kx * kx - 3.0 * ky) + kz;
float h = q * q + 4.0 * p3;
if (h >= 0.0)
{
h = sqrt(h);
vec2 x = (vec2(h, -h) - q) / 2.0;
vec2 uv = sign(x) * pow(abs(x), vec2(1.0 / 3.0));
float t = clamp(uv.x + uv.y - kx, 0.0, 1.0);
res = dot2(d + (c + b * t) * t);
}
else
{
float z = sqrt(-p);
float v = acos(q / (p * z * 2.0)) / 3.0;
float m = cos(v);
float n = sin(v) * 1.732050808; // sqrt(3)
vec3 t = clamp(vec3(m + m, -n - m, n - m) * z - kx, 0.0, 1.0);
res = min(
dot2(d + (c + b * t.x) * t.x),
dot2(d + (c + b * t.y) * t.y)
);
}
return sqrt(res);
}
// basic fin for that tail. Used for most stuff
float sdTailFin(
vec2 p,
vec2 A0, vec2 B0, vec2 C0,
float freq,
float ampB, float ampC,
float phaseC,
float phaseOffset, // phase offset to change the phase of other fins
float thickness
) {
vec2 AB0 = B0 - A0;
vec2 AC0 = C0 - A0;
float rB = length(AB0);
float rC = length(AC0);
// initial angle at t=0
float baseAngleB = atan(AB0.y, AB0.x);
float baseAngleC = atan(AC0.y, AC0.x);
float basePhase = swimPhase() * freq + phaseOffset;
// angular change wrt time
float angleB = baseAngleB + ampB * sin(basePhase);
float angleC = baseAngleC + ampC * sin(basePhase + phaseC);
// new location of the three coordinates of the bezier after transformation
vec2 A = A0;
vec2 B = A0 + rB * vec2(cos(angleB), sin(angleB));
vec2 C = A0 + rC * vec2(cos(angleC), sin(angleC));
float dCenter = sdBezier(p, A, B, C);
return dCenter - thickness;
}
// simple circle
float sdCircle(vec2 p, vec2 c, float r) {
return length(p - c) - r;
}
// definition of the spine
vec2 spinePos(float t) {
vec2 headPos = vec2(0.0, 0.7);
vec2 tailPos = vec2(0.0, -0.2);
vec2 spine = mix(headPos, tailPos, t);
// bend the spine w.r.t. time
float bend = -0.2 * sin(3.14159 * (t - 0.2) + swimPhase());
spine.x += bend * 0.4;
return spine;
}
// CENTRAL PART: This controls the shape of the fish
const int NUM_KEYS = 5;
const vec2 radiusKeys[NUM_KEYS] = vec2[NUM_KEYS](
vec2(0.00, 0.10), // Head
vec2(0.05, 0.12), // Neck
vec2(0.20, 0.14), // Body
vec2(0.80, 0.06), // Tail
vec2(1.00, 0.02) // Tail tip
);
float catmullRom(float p0, float p1, float p2, float p3, float s) {
float s2 = s * s;
float s3 = s2 * s;
return 0.5 * (
2.0 * p1 +
(-p0 + p2) * s +
(2.0 * p0 - 5.0 * p1 + 4.0 * p2 - p3) * s2 +
(-p0 + 3.0 * p1 - 3.0 * p2 + p3) * s3
);
}
float radiusProfile(float t) {
if (t <= radiusKeys[0].x) return radiusKeys[0].y;
if (t >= radiusKeys[NUM_KEYS-1].x) return radiusKeys[NUM_KEYS-1].y;
int seg = 0;
for (int i = 0; i < NUM_KEYS - 1; ++i) {
if (t >= radiusKeys[i].x && t <= radiusKeys[i+1].x) {
seg = i;
break;
}
}
int i1 = seg;
int i2 = seg + 1;
int i0 = max(i1 - 1, 0);
int i3 = min(i2 + 1, NUM_KEYS - 1);
float t1 = radiusKeys[i1].x;
float t2 = radiusKeys[i2].x;
float s = (t - t1) / (t2 - t1);
float p0 = radiusKeys[i0].y;
float p1 = radiusKeys[i1].y;
float p2 = radiusKeys[i2].y;
float p3 = radiusKeys[i3].y;
return catmullRom(p0, p1, p2, p3, s);
}
// Betta body
float sdBettaBody(vec2 p) {
float d = 1e9;
const int N = 30;
for (int i = 0; i < N; ++i) {
float t = float(i) / float(N - 1);
vec2 spine = spinePos(t);
float radius = radiusProfile(t);
float dc = sdCircle(p, spine, radius);
d = min(d, dc);
}
return d;
}
// MAX FINS COUNT!!! Lessen to relax the calculation load
const int MAX_FINS = 64;
// Ultimate fin function
// tAttach : where on the body the fin is attached from 0 to 1
// numFins : FIN COUNT!!! Be careful
// lenScaleGlobal : length of the fins
// thicknessScaleGlobal : thickness of the fins
// spreadScale : spread of the fan
// baseAngleOffset : base angle offset
float sdAllTailFins(
vec2 p,
float tAttach,
int numFins,
float lenScaleGlobal,
float thicknessScaleGlobal,
float spreadScale,
float baseAngleOffset,
float phaseOffset
) {
float t = clamp(tAttach, 0.0, 1.0);
vec2 basePos = spinePos(t);
float dt = 0.02;
vec2 tangent = normalize(
spinePos(min(t + dt, 1.0)) - spinePos(max(t - dt, 0.0)) + vec2(1e-5)
);
// baseangle with offset
float baseAngle = atan(tangent.y, tangent.x) + baseAngleOffset;
int N = clamp(numFins, 1, MAX_FINS);
float spread = 1.0 * spreadScale;
float d = 1e9;
//Loop to add the fins using the fin function
for (int i = 0; i < MAX_FINS; ++i) {
if (i >= N) break;
float fi = float(i);
float denom = max(float(N - 1), 1.0);
float u = (fi / denom) - 0.5;
float angleOffset = u * spread;
float angle = baseAngle + angleOffset;
float lenScaleLocal = mix(0.8, 1.2, hash1(fi * 5.31));
float lenBBase = 0.25 * lenScaleGlobal;
float lenCBase = 0.65 * lenScaleGlobal;
float lenB = lenBBase * lenScaleLocal;
float lenC = lenCBase * lenScaleLocal;
vec2 dirFin = vec2(cos(angle), sin(angle));
vec2 A0 = basePos;
vec2 B0 = basePos + lenB * dirFin;
vec2 C0 = basePos + lenC * dirFin;
float ampScale = 0.8 + 1.3 * abs(u);
float freq = 0.8;
float ampB = 0.10 * ampScale;
float ampC = 0.25 * ampScale;
float phaseC = 0.0 + u * 0.5;
float baseThickness = 0.008 * thicknessScaleGlobal;
float thickScale = mix(0.1, 0.8, hash1(fi * 5.13));
float thickness = baseThickness * thickScale;
float dFin = sdTailFin(
p,
A0, B0, C0,
freq, ampB, ampC, phaseC,
phaseOffset,
thickness
);
d = min(d, dFin);
}
return d;
}
//Base case for testing and debugging.
//Can be used with no parameters if lazy
float sdAllTailFins(vec2 p) {
return sdAllTailFins(p, 0.9, 24, 1.0, 1.0, 1.0, 0.0, 0.0);
}
//Pectoral fin (chest fin) thingy
float sdPectoralFin(
vec2 p,
vec2 basePos,
vec2 dirForward,
float sideSign
) {
// dirForward: head → tail
vec2 f = normalize(dirForward);
vec2 n = vec2(-f.y, f.x) * sideSign;
float a = deg2rad(PEC_BASE_ANGLE_DEG);
vec2 dirBase = normalize(
n * cos(a) + (-f) * sin(a)
);
// swing only to one side
float swing = 0.8 * pectoralSwingOneSide(); // 0〜1くらい
vec2 dirB = normalize(dirBase + 0.3 * swing * (-f));
vec2 dirC = normalize(dirBase + 1.0 * swing * (-f));
float lenB = 0.12;
float lenC = 0.26;
vec2 A = basePos;
vec2 B = basePos + lenB * dirB;
vec2 C = basePos + lenC * dirC;
float thickness = 0.012;
float dCenter = sdBezier(p, A, B, C);
return dCenter - thickness;
}
float sdPectoralFinsSide(
vec2 p,
vec2 basePos,
vec2 dirForward,
float sideSign
) {
vec2 f = normalize(dirForward);
vec2 n = vec2(-f.y, f.x) * sideSign;
float a = deg2rad(PEC_BASE_ANGLE_DEG);
vec2 baseDir = normalize(
n * cos(a) + (-f) * sin(a)
);
float baseAngle = atan(baseDir.y, baseDir.x);
const int NUM_PEC_FINS = 7;
float spread = 0.1;
float d = 1e9;
for (int i = 0; i < NUM_PEC_FINS; ++i) {
float fi = float(i);
float u = (fi / float(NUM_PEC_FINS - 1)) - 0.5;
float angleOffset = u * spread;
float angle = baseAngle + angleOffset;
vec2 dirBaseFin = vec2(cos(angle), sin(angle));
float swing = 0.7 * pectoralSwingOneSide();
vec2 dirB = normalize(dirBaseFin + 0.3 * swing * (-f));
vec2 dirC = normalize(dirBaseFin + 1.0 * swing * (-f));
float lenScale = mix(0.9, 1.1, hash1(fi * 7.31 + (sideSign > 0.0 ? 1.0 : 2.0)));
float lenBBase = 0.10;
float lenCBase = 0.26;
float lenB = lenBBase * lenScale;
float lenC = lenCBase * lenScale;
vec2 A = basePos;
vec2 B = basePos + lenB * dirB;
vec2 C = basePos + lenC * dirC;
float baseThickness = 0.010;
float thickScale = mix(0.2, 0.5,
hash1(fi * 3.17 + (sideSign > 0.0 ? 5.0 : 6.0)));
float thickness = baseThickness * thickScale;
float dFin = sdBezier(p, A, B, C) - thickness;
d = min(d, dFin);
}
return d;
}
float sdAllPectoralFins(vec2 p) {
float tBase = 0.35;
vec2 spineC = spinePos(tBase);
vec2 f = normalize(spinePos(tBase + 0.02) - spinePos(tBase - 0.02) + vec2(1e-5));
vec2 n = vec2(-f.y, f.x);
float r = radiusProfile(tBase);
//base position for the left and the right
vec2 baseL = spineC + n * r;
vec2 baseR = spineC - n * r;
float dL = sdPectoralFinsSide(p, baseL, f, +1.0);
float dR = sdPectoralFinsSide(p, baseR, f, -1.0);
return min(dL, dR);
}
float sdFinsAlongSpineRange(
vec2 p,
float tStart,
float tEnd,
int numFins, //This is the total number of fins along the spine
float lenScaleGlobal,
float thicknessScaleGlobal,
float baseAngleOffset,
float phaseOffset //can be used to offset the angle of fins, but usually keep it at 0
) {
float d = 1e9;
int N = clamp(numFins, 1, MAX_FINS);
//fail safe
float t0 = min(tStart, tEnd);
float t1 = max(tStart, tEnd);
for (int i = 0; i < MAX_FINS; ++i) {
if (i >= N) break;
float fi = float(i);
float denom = max(float(N - 1), 1.0);
float u = (N == 1) ? 0.0 : (fi / denom);
float t = mix(t0, t1, u);
t = clamp(t, 0.0, 1.0);
vec2 basePos = spinePos(t);
float dt = 0.02;
vec2 tangent = normalize(
spinePos(min(t + dt, 1.0)) - spinePos(max(t - dt, 0.0)) + vec2(1e-5)
);
float baseAngle = atan(tangent.y, tangent.x) + baseAngleOffset;
// spread the angles slightly
float jitter = (hash1(fi * 3.73) - 0.5) * 0.4;
float angle = baseAngle + jitter;
// length, with slight randomness with hash 1. In between 0.9 to 1.1 times
float lenScaleLocal = mix(0.9, 1.1, hash1(fi * 5.31));
float lenBBase = 0.25 * lenScaleGlobal;
float lenCBase = 0.65 * lenScaleGlobal;
float lenB = lenBBase * lenScaleLocal;
float lenC = lenCBase * lenScaleLocal;
vec2 dirFin = vec2(cos(angle), sin(angle));
vec2 A0 = basePos;
vec2 B0 = basePos + lenB * dirFin;
vec2 C0 = basePos + lenC * dirFin;
// adjust as needed, for the animation
float ampScale = 2.0;
float freq = 0.8;
float ampB = 0.10 * ampScale;
float ampC = 0.25 * ampScale;
float phaseC = 0.0 + u * 1.2;
float baseThickness = 0.008 * thicknessScaleGlobal;
float thickScale = mix(0.3, 0.9, hash1(fi * 5.13));
float thickness = baseThickness * thickScale;
float dFin = sdTailFin(
p,
A0, B0, C0,
freq, ampB, ampC, phaseC,
phaseOffset,
thickness
);
d = min(d, dFin);
}
return d;
}
//Throw in the calculated sd stuff in here, to mix the colors.
vec3 mixColor(vec3 baseColor, float dShape, vec3 shapeColor, float alpha) {
float mask = smoothstep(0.02, 0.0, dShape);
float blend = mask * alpha;
return mix(baseColor, shapeColor, blend);
}
// -------------------- main --------------------
void main() {
vec2 p = (2.0 * gl_FragCoord.xy - u_resolution.xy) / u_resolution.y;
// All the sdf calculations
float dBody = sdBettaBody(p);
float dTail = sdAllTailFins(p, 0.9, 32, 1.0, 1.0, 0.7, 0.0, 0.0);
float dPec = sdAllPectoralFins(p);
float dTailThin = sdAllTailFins(p, 0.9, 8, 0.9, 0.4, 0.6, 0.0, 0.0);
float dTailSpread = sdAllTailFins(p, 0.9, 30, 0.8, 0.4, 0.6, 0.3, 1.4);
float dBack = sdAllTailFins(p, 0.2, 24, 1.0, 0.4, 0.4, 0.0, 0.0);
float dBack2 = sdAllTailFins(p, 0.5, 32, 1.0, 0.4, 0.7, 0.2, 0.0);
float dSpine = sdFinsAlongSpineRange(p, 0.2, 0.8, 60, 0.7, 0.4, 0.0, 0.3);
float dSpine2 = sdFinsAlongSpineRange(p, 0.2, 0.8, 60, 0.5, 0.4, 0.0, 0.1);
// color for each body segements
vec3 bg = vec3(0.6, 0.7, 1.0);
vec3 bodyCol = vec3(0.9, 0.25, 0.3);
vec3 pecCol = vec3(0.9, 0.25, 0.3);
vec3 tailCol = vec3(0.9, 0.3, 0.3);
vec3 tailColThin = vec3(0.7, 0.3, 0.3);
vec3 backCol = vec3(0.9, 0.3, 0.3);
vec3 spineCol2 = vec3(0.85, 0.3, 0.3);
vec3 bodyColBase = vec3(0.9, 0.25, 0.3); //For the base body color
vec3 bodyColEdge = vec3(0.8, 0.2, 0.2); // body color for outer rim
float rimWidth = 0.06;
float rimFactor = smoothstep(-rimWidth, 0.0, dBody);
bodyCol = mix(bodyColBase, bodyColEdge, rimFactor);
vec3 color = bg;
//MIX EVERYTHING
color = mixColor(color, dBody, bodyCol, 0.9);
color = mixColor(color, dBack, backCol, 0.4);
color = mixColor(color, dBack2, backCol, 0.55);
color = mixColor(color, dPec, pecCol, 0.6);
color = mixColor(color, dTail, tailCol, 0.6);
color = mixColor(color, dTailThin, tailColThin, 0.7);
color = mixColor(color, dSpine, tailCol, 0.6);
color = mixColor(color, dSpine2, spineCol2, 0.8);
color = mixColor(color, dTailSpread, tailCol, 0.6);
///////////////////
//noise from https://www.redblobgames.com/x/2107-webgl-noise/webgl-noise/webdemo/cellular.html
//NOISE!!!! The worley noise 2D defined here
float t = u_time;
// First noise layer
vec2 uv = gl_FragCoord.xy / u_resolution.xy;
vec2 uv1 = uv * u_resolution.xy / min(u_resolution.x, u_resolution.y);
float n1 = colorMapping(uv1 * 4.0 + vec2(t * 0.3, t * 0.1));
float v1 = 0.5 + 0.5 * n1;
vec3 col1 = vec3(v1);
float alpha1 = v1 * 0.3;
color = mix(color, col1, alpha1);
// Second noise layer
vec2 uv2 = uv * u_resolution.xy / min(u_resolution.x, u_resolution.y);
float n2 = colorMapping(uv2 * 12.0 + vec2(u_time * 0.4, u_time * 0.7));
float v2 = 0.5 + 0.5 * n2;
vec3 col2 = vec3(v2);
float alpha2 = v2 * 0.3;
color = mix(color, col2, alpha2);
colour_out = vec4(color, 1.0);
}
Red Betta (Not goldfish) by Shuma Kise
precision mediump float;
uniform vec2 u_resolution;
uniform float u_time;
in vec2 coord;
float edgeWidth = 0.05;
struct FishState {
vec2 pos; // 位置
vec2 velocity;// 速度ベクトル
float angle; // 回転角度
float bend; // 曲がり具合
float facing; // 向き(左右)
};
struct FishField {
float insideBody;
float insideCaudal;
float insideDorsal;
};
struct FishConfig {
vec2 bodyCenter;
vec2 bodySize;
vec2 caudalCenter;
vec2 caudalSize;
float caudalAngle;
vec2 dorsalCenter;
vec2 dorsalSize;
float dorsalAngle;
vec2 eyeCenter;
float eyeSize;
};
FishConfig getFishConfig() {
FishConfig c;
c.bodyCenter = vec2(0.3, 0.0);
c.bodySize = vec2(0.4, 0.16);
// 尾鰭
c.caudalCenter = vec2(0.6, 0.0);
c.caudalSize = vec2(1.2, 1.3);
c.caudalAngle = radians(70.0);
// 背鰭
c.dorsalCenter = vec2(0.1, 0.0);
c.dorsalSize = vec2(2.0, 0.7);
c.dorsalAngle = radians(50.0);
// 目
c.eyeCenter = vec2(0.05, 0.04);
c.eyeSize = 0.05;
return c;
}
vec3 mod289(vec3 x) {
return x - floor(x * (1.0 / 289.0)) * 289.0;
}
vec2 mod289(vec2 x) {
return x - floor(x * (1.0 / 289.0)) * 289.0;
}
vec3 permute(vec3 x) {
return mod289(((x*34.0)+10.0)*x);
}
float snoise(vec2 v) {
const vec4 C = vec4(0.211324865405187, // (3.0-sqrt(3.0))/6.0
0.366025403784439, // 0.5*(sqrt(3.0)-1.0)
-0.577350269189626, // -1.0 + 2.0 * C.x
0.024390243902439); // 1.0 / 41.0
// First corner
vec2 i = floor(v + dot(v, C.yy) );
vec2 x0 = v - i + dot(i, C.xx);
// Other corners
vec2 i1;
//i1.x = step( x0.y, x0.x ); // x0.x > x0.y ? 1.0 : 0.0
//i1.y = 1.0 - i1.x;
i1 = (x0.x > x0.y) ? vec2(1.0, 0.0) : vec2(0.0, 1.0);
// x0 = x0 - 0.0 + 0.0 * C.xx ;
// x1 = x0 - i1 + 1.0 * C.xx ;
// x2 = x0 - 1.0 + 2.0 * C.xx ;
vec4 x12 = x0.xyxy + C.xxzz;
x12.xy -= i1;
// Permutations
i = mod289(i); // Avoid truncation effects in permutation
vec3 p = permute( permute( i.y + vec3(0.0, i1.y, 1.0 ))
+ i.x + vec3(0.0, i1.x, 1.0 ));
vec3 m = max(0.5 - vec3(dot(x0,x0), dot(x12.xy,x12.xy), dot(x12.zw,x12.zw)), 0.0);
m = m*m ;
m = m*m ;
// Gradients: 41 points uniformly over a line, mapped onto a diamond.
// The ring size 17*17 = 289 is close to a multiple of 41 (41*7 = 287)
vec3 x = 2.0 * fract(p * C.www) - 1.0;
vec3 h = abs(x) - 0.5;
vec3 ox = floor(x + 0.5);
vec3 a0 = x - ox;
// Normalise gradients implicitly by scaling m
// Approximation of: m *= inversesqrt( a0*a0 + h*h );
m *= 1.79284291400159 - 0.85373472095314 * ( a0*a0 + h*h );
// Compute final noise value at P
vec3 g;
g.x = a0.x * x0.x + h.x * x0.y;
g.yz = a0.yz * x12.xz + h.yz * x12.yw;
return 130.0 * dot(m, g);
}
// 楕円
float sdEllipse( in vec2 p, in vec2 ab ) {
p = abs(p); if( p.x > p.y ) {p=p.yx;ab=ab.yx;}
float l = ab.y*ab.y - ab.x*ab.x;
float m = ab.x*p.x/l; float m2 = m*m;
float n = ab.y*p.y/l; float n2 = n*n;
float c = (m2+n2-1.0)/3.0; float c3 = c*c*c;
float q = c3 + m2*n2*2.0;
float d = c3 + m2*n2;
float g = m + m*n2;
float co;
if( d<0.0 )
{
float h = acos(q/c3)/3.0;
float s = cos(h);
float t = sin(h)*sqrt(3.0);
float rx = sqrt( -c*(s + t + 2.0) + m2 );
float ry = sqrt( -c*(s - t + 2.0) + m2 );
co = (ry+sign(l)*rx+abs(g)/(rx*ry)- m)/2.0;
}
else
{
float h = 2.0*m*n*sqrt( d );
float s = sign(q+h)*pow(abs(q+h), 1.0/3.0);
float u = sign(q-h)*pow(abs(q-h), 1.0/3.0);
float rx = -s - u - c*4.0 + 2.0*m2;
float ry = (s - u)*sqrt(3.0);
float rm = sqrt( rx*rx + ry*ry );
co = (ry/sqrt(rm-rx)+2.0*g/rm-m)/2.0;
}
vec2 r = ab * vec2(co, sqrt(1.0-co*co));
return length(r-p) * sign(p.y-r.y);
}
// 光の筋
float getRays(vec2 uv, float t) {
vec2 lightPos = vec2(0.0, 1.5 + sin(t * 0.5) * 0.2);
vec2 rayDir = normalize(uv - lightPos);
float angle = atan(rayDir.y, rayDir.x);
float ray = snoise(vec2(angle * 5.0, t * 0.2));
float dist = length(uv - lightPos);
float attenuation = 1.0 / (1.0 + dist * dist * 2.0);
float directionality = smoothstep(0.0, 0.8, -rayDir.y);
return max(0.0, ray * attenuation * directionality);
}
// 泡
float getBubbles(vec2 uv, float t) {
float totalBubbles = 0.0;
for (float i = 0.0; i < 3.0; i++) {
float scale = 1.0 + i * 2.0;
float speed = 0.3 + i * 0.1;
vec2 pos = uv * scale - vec2(sin(t * 0.1 + i) * 0.5, t * speed);
vec2 grid = floor(pos);
vec2 local = fract(pos) - 0.5;
float rand = fract(sin(dot(grid, vec2(12.3456, 78.91011))) * 43210.98765);
if (rand > 0.8) {
float d = length(local);
float bubble = smoothstep(0.1, 0.07, d);
totalBubbles += bubble * (0.3 / scale);
}
}
return totalBubbles;
}
// ランダムな目標点を計算
vec2 getCyclePoint(float i) {
float maxWidth = 0.9;
float maxHeight = 0.2;
float rx = fract(sin(i * 12.9898) * 43758.5453) * 2.0 - 1.0;
float ry = fract(sin(i * 98.123) * 43758.5453) * 2.0 - 1.0;
return vec2(rx * maxWidth, ry * maxHeight);
}
// 魚の位置と向きを計算
FishState generateTransform(float t, float cycle) {
// --------パラメータ--------
float minDist = 0.6;
float minAngle = radians(10.0);
// ------------------------
float timeScale = t / cycle;
float i = floor(timeScale);
float f = fract(timeScale);
float progress = clamp(f, 0.0, 1.0);
float u = 1.0 - pow(1.0 - progress, 3.0);
// ---- 目標点の取得 ----
vec2 p0 = getCyclePoint(i);
vec2 p2 = getCyclePoint(i + 1.0);
// 最小移動距離補正
vec2 dVec = p2 - p0;
float dLen = length(dVec);
if(dLen < minDist) {
vec2 dir = (dLen > 0.001) ? normalize(dVec) : vec2 (1.0, 0.0);
p2 = p0 + dir * minDist;
}
// ベジエ曲線用の中間点
vec2 mid = (p0 + p2) * 0.5;
float curveRand = fract(sin(i * 54.321) * 9999.0) * 2.0 - 1.0;
vec2 p1 = mid + vec2(0.0, curveRand * 0.15);
// 2次ベジエ曲線の微分により速度ベクトルを求め, 速度も計算
vec2 tanTerm1 = p1 - p0;
vec2 tanTerm2 = p2 - p1;
vec2 velocity = 2.0 * (1.0 - u) * tanTerm1 + 2.0 * u * tanTerm2;
vec2 targetPos = mix(mix(p0, p1, u), mix(p1, p2, u), u);
vec2 drift = vec2(
sin(t * 0.8) * 0.02,
cos(t * 1.2) * 0.015
);
targetPos += drift;
// 進行方向に対する上下の傾き
float rawAngle = atan(velocity.y, abs(velocity.x));
float angleDamp = smoothstep(0.0, 0.15, progress) * smoothstep(1.0, 0.4, progress);
float angle = clamp(rawAngle, -minAngle, minAngle) * angleDamp;
// 方向転換時のしなり
float bendIntensity = sin(progress * 3.14159);
float bend = bendIntensity * curveRand * 0.8;
// 左右の向きの決定
vec2 prev_p0 = getCyclePoint(i - 1.0);
vec2 prev_p2 = p0;
float prevFacing = (prev_p0.x >= prev_p2.x) ? 1.0 : -1.0;
float currFacing = (p0.x >= p2.x) ? 1.0 : -1.0;
float turnProgress = smoothstep(0.0, 0.15, progress);
float facing = mix(prevFacing, currFacing, turnProgress);
return FishState(targetPos, velocity, angle, bend, facing);
}
// 魚の変形
vec2 deformFish(vec2 p, FishState state){
vec2 rel = p - state.pos;
// 回転
float f = (state.facing >= 0.0) ? 1.0 : -1.0;
float c = cos(-state.angle * f);
float s = sin(-state.angle * f);
rel = mat2(c, -s, s, c) * rel;
// 左右反転
// 反転する瞬間が最高値
float turnIntensity = 1.0 - abs(state.facing);
// 部位ごとの向き
float localFacing = state.facing - rel.x * turnIntensity;
if (abs(localFacing) < 0.01) {
localFacing = 0.01 * sign(localFacing);
}
rel.x /= localFacing;
return rel;
}
// 尾鰭のSDF
float sdFin(vec2 p, vec2 diameter, float maxAngle, float t) {
float angle = atan(p.y, p.x);
float absAngle = abs(angle);
float r = length(p);
float edgeNoise = snoise(vec2(r * 4.0, t * 1.5)) * 0.05;
float angOutside = absAngle - (maxAngle + edgeNoise);
angOutside = max(angOutside, 0.0);
vec2 np = p / diameter;
float finDist = length(np) - 1.0;
return max(finDist, angOutside * diameter.x);
}
FishField mapFish(vec2 p, float t) {
FishField f;
FishConfig fishConfig = getFishConfig();
// 胴体
float bodyDist = sdEllipse(p - fishConfig.bodyCenter, fishConfig.bodySize);
f.insideBody = step(bodyDist, 0.0);
// 尾鰭
float caudalFinDist = sdFin(p - fishConfig.caudalCenter, fishConfig.caudalSize, fishConfig.caudalAngle, t);
f.insideCaudal = max(step(caudalFinDist, 0.0) - f.insideBody, 0.0);
// 背鰭
float dorsalFinDist = sdFin(p - fishConfig.dorsalCenter, fishConfig.dorsalSize, fishConfig.dorsalAngle, t);
f.insideDorsal = max(step(dorsalFinDist, 0.0) - f.insideBody, 0.0);
return f;
}
// 体の模様生成
vec3 getBodyPattern(vec2 uv, vec2 velocity) {
return vec3(0.1, 0.05, 0.45);
}
// 尾鰭の模様生成
vec4 getFinPattern(vec2 uv, vec2 velocity, float t) {
float warpFactor = 0.5;
float stripeCount = 22.0;
float minSaturate = 0.6;
float maxSaturate = 0.85;
float gradEdge = 0.8;
vec3 blue = vec3(0.0, 0.2, 0.9);
vec3 grey = vec3(0.5, 0.5, 0.5);
vec3 red = vec3(0.8, 0.2, 0.08);
vec4 outColor = vec4(1.0);
float angle = atan(uv.y, uv.x);
float r = length(uv);
// 水流の抵抗による揺らぎの計算
float n1 = snoise(vec2(angle * 3.0, t * 0.25));
float n2 = snoise(vec2(r * 4.0, t * 0.35));
vec2 vel = normalize(velocity + 0.0001);
vel = abs(vel);
float flow = dot(normalize(uv), vel);
float n3 = snoise(vec2(flow * 6.0, t * 0.5));
float warp = (n1 * 0.15) + (n2 * 0.10) + (n3 * 0.25);
warpFactor *= smoothstep(0.0, 0.2, r);
float warpedR = r + warp * warpFactor;
float warpedAngle = angle + warp * warpFactor;
// 彩度変化によるストライプ模様
float stripe = sin(warpedAngle * stripeCount);
float stripeFactor = stripe * 0.5 + 0.5;
float saturation = mix(minSaturate, maxSaturate, stripeFactor);
// 中心とエッジのフェード
float centerFade = max(smoothstep(0.05, 0.2, warpedR), 0.4);
float rimFade = 1.0 - smoothstep(0.6, 0.8, warpedR);
float radialFade = centerFade * rimFade;
// saturation *= radialFade;
// 色のグラデーション
float edge = smoothstep(0.0, 0.85 + warp * 0.15, warpedR);
vec3 gradation;
if (edge < gradEdge) {
float a = smoothstep(0.0, gradEdge, edge);
gradation = mix(blue, red, a);
} else {
float a = smoothstep(gradEdge, 1.0, edge);
gradation = mix(red, grey, a);
}
outColor.rgb = saturation * gradation;
outColor.a = radialFade;
return outColor;
}
void main() {
// ------- パラメータ -------
float cycle = 8.0;
vec3 col = vec3(0.02, 0.03, 0.07);
vec3 bgColorTop = vec3(0.0, 0.1, 0.3);
vec3 bgColorBottom = vec3(0.0, 0.02, 0.1);
vec3 bodyColor = vec3(0.0, 0.1, 0.45);
// ーーーーーーーーーーーーーーー
FishConfig fishConfig = getFishConfig();
vec2 uv = (gl_FragCoord.xy * 2.0 - u_resolution.xy) / min(u_resolution.x, u_resolution.y);
float t = u_time;
// 背景
vec3 bgCol = mix(bgColorBottom, bgColorTop, uv.y * 0.5 + 0.5);
float rays = getRays(uv, t);
bgCol += vec3(0.8, 0.9, 1.0) * rays * 0.5;
float bubbles = getBubbles(uv, t);
bgCol += vec3(1.0) * bubbles * 0.8;
vec3 finalColor = bgCol;
// 現在位置と移動方向ベクトルの計算
FishState fishState = generateTransform(t, cycle);
vec2 localUV = deformFish(uv, fishState);
vec2 caudalUV = (localUV - fishConfig.caudalCenter) / fishConfig.caudalSize;
vec2 dorsalUV = (localUV - fishConfig.dorsalCenter) / fishConfig.dorsalSize;
FishField field = mapFish(localUV, t);
vec4 caudal = getFinPattern(caudalUV, fishState.velocity, t);
vec4 dorsal = getFinPattern(dorsalUV, fishState.velocity, t + 1.0);
finalColor = mix(finalColor, bodyColor, field.insideBody);
finalColor = mix(finalColor, dorsal.rgb, dorsal.a * field.insideDorsal);
finalColor = mix(finalColor, caudal.rgb, caudal.a * field.insideCaudal);
float d = length(localUV - fishConfig.eyeCenter);
float eyeMask = 1.0 - smoothstep(fishConfig.eyeSize, fishConfig.eyeSize + 0.003, d);
float visibleEye = eyeMask * field.insideBody;
finalColor = mix(finalColor, vec3(0.0), visibleEye);
colour_out = vec4(finalColor, 1.0);
return;
}
salmon by tomato
uniform vec2 u_resolution;
uniform vec2 u_mouse;
uniform float u_time;
const float PI = 3.14159265359;
const vec3 body_color = vec3(0.1, 0.5, 1.0);
const vec3 roof_fin_color = vec3(0.5, 0.7, 1.0);
const vec3 middle_fin_color = vec3(0.1, 0.5, 1.0);
const vec3 edge_fin_color = vec3(0.9, 0.9, 1.2);
vec2 mod289(vec2 x) {return x - floor(x * (1.0 / 289.0)) * 289.0;}
vec3 mod289(vec3 x) {return x - floor(x * (1.0 / 289.0)) * 289.0;}
vec4 mod289(vec4 x) {return x - floor(x * (1.0 / 289.0)) * 289.0;}
vec3 mod7(vec3 x) {return x - floor(x * (1.0 / 7.0)) * 7.0;}
vec3 permute(vec3 x) {return mod289(((x*34.0)+10.0)*x);}
vec4 permute(vec4 x) {return mod289(((x*34.0)+1.0)*x);}
float snoise(vec2 v){
const vec4 C = vec4(0.211324865405187, // (3.0-sqrt(3.0))/6.0
0.366025403784439, // 0.5*(sqrt(3.0)-1.0)
-0.577350269189626, // -1.0 + 2.0 * C.x
0.024390243902439); // 1.0 / 41.0
// First corner
vec2 i = floor(v + dot(v, C.yy) );
vec2 x0 = v - i + dot(i, C.xx);
// Other corners
vec2 i1;
//i1.x = step( x0.y, x0.x ); // x0.x > x0.y ? 1.0 : 0.0
//i1.y = 1.0 - i1.x;
i1 = (x0.x > x0.y) ? vec2(1.0, 0.0) : vec2(0.0, 1.0);
// x0 = x0 - 0.0 + 0.0 * C.xx ;
// x1 = x0 - i1 + 1.0 * C.xx ;
// x2 = x0 - 1.0 + 2.0 * C.xx ;
vec4 x12 = x0.xyxy + C.xxzz;
x12.xy -= i1;
// Permutations
i = mod289(i); // Avoid truncation effects in permutation
vec3 p = permute( permute( i.y + vec3(0.0, i1.y, 1.0 ))
+ i.x + vec3(0.0, i1.x, 1.0 ));
vec3 m = max(0.5 - vec3(dot(x0,x0), dot(x12.xy,x12.xy), dot(x12.zw,x12.zw)), 0.0);
m = m*m ;
m = m*m ;
// Gradients: 41 points uniformly over a line, mapped onto a diamond.
// The ring size 17*17 = 289 is close to a multiple of 41 (41*7 = 287)
vec3 x = 2.0 * fract(p * C.www) - 1.0;
vec3 h = abs(x) - 0.5;
vec3 ox = floor(x + 0.5);
vec3 a0 = x - ox;
// Normalise gradients implicitly by scaling m
// Approximation of: m *= inversesqrt( a0*a0 + h*h );
m *= 1.79284291400159 - 0.85373472095314 * ( a0*a0 + h*h );
// Compute final noise value at P
vec3 g;
g.x = a0.x * x0.x + h.x * x0.y;
g.yz = a0.yz * x12.xz + h.yz * x12.yw;
return 130.0 * dot(m, g);
}
vec2 cellular(vec2 P) {
#define K 0.142857142857 // 1/7
#define Ko 0.428571428571 // 3/7
#define jitter 0.2 // Less gives more regular pattern
vec2 Pi = mod289(floor(P));
vec2 Pf = fract(P);
vec3 oi = vec3(-1.0, 0.0, 1.0);
vec3 of = vec3(-0.5, 0.5, 1.5);
vec3 px = permute(Pi.x + oi);
vec3 p = permute(px.x + Pi.y + oi); // p11, p12, p13
vec3 ox = fract(p*K) - Ko;
vec3 oy = mod7(floor(p*K))*K - Ko;
vec3 dx = Pf.x + 0.5 + jitter*ox;
vec3 dy = Pf.y - of + jitter*oy;
vec3 d1 = dx * dx + dy * dy; // d11, d12 and d13, squared
p = permute(px.y + Pi.y + oi); // p21, p22, p23
ox = fract(p*K) - Ko;
oy = mod7(floor(p*K))*K - Ko;
dx = Pf.x - 0.5 + jitter*ox;
dy = Pf.y - of + jitter*oy;
vec3 d2 = dx * dx + dy * dy; // d21, d22 and d23, squared
p = permute(px.z + Pi.y + oi); // p31, p32, p33
ox = fract(p*K) - Ko;
oy = mod7(floor(p*K))*K - Ko;
dx = Pf.x - 1.5 + jitter*ox;
dy = Pf.y - of + jitter*oy;
vec3 d3 = dx * dx + dy * dy; // d31, d32 and d33, squared
// Sort out the two smallest distances (F1, F2)
vec3 d1a = min(d1, d2);
d2 = max(d1, d2); // Swap to keep candidates for F2
d2 = min(d2, d3); // neither F1 nor F2 are now in d3
d1 = min(d1a, d2); // F1 is now in d1
d2 = max(d1a, d2); // Swap to keep candidates for F2
d1.xy = (d1.x < d1.y) ? d1.xy : d1.yx; // Swap if smaller
d1.xz = (d1.x < d1.z) ? d1.xz : d1.zx; // F1 is in d1.x
d1.yz = min(d1.yz, d2.yz); // F2 is now not in d2.yz
d1.y = min(d1.y, d1.z); // nor in d1.z
d1.y = min(d1.y, d2.x); // F2 is in d1.y, we're done.
return sqrt(d1.xy);
}
float FinLineFbm( vec2 p ) { /* Same as the fbm before. */
float f = 0.0, scale;
for (int i=0; i<2; i++) {
scale = pow( pow(2.0, 4.0/3.0), float(i) );
f += snoise( p * scale ) / scale;
}
return f;
}
float FinRingFbm( vec2 p ) { /* Same as the fbm before. */
float f = 0.0, scale;
scale = pow( pow(2.0, 4.0/3.0), 1.0 );
f += snoise( p * scale ) / scale; // calc f separatedly only when i = 1
for (int i=2; i<4; i++) {
scale = pow( pow(2.0, 4.0/3.0), float(i) );
f += snoise( p * scale ) * float(i)/20.0;
}
return f;
}
vec4 drawBody(vec2 p) {
vec2 p_eye = vec2(0.8, 0.05);
vec2 p_mouse = 2.0*u_mouse/u_resolution - 1.0;
float theta = atan(p_mouse.y - p_eye.y, p_mouse.x - p_eye.x);
float x = 0.03 * cos(theta);
float y = 0.03 * sin(theta);
float eye = step( 0.04, distance( p.xy, vec2(p_eye.x + x, p_eye.y + y) ) );
if (eye == 0.0){
return vec4(vec3(0.0), 1.0);
}else{
p *= 25.0;
p.y *= 0.6;
p.y *= 2.0/sqrt(3.0);
p.x += 0.5 * mod(floor(p.y), 2.0);
vec2 f = cellular(p);
float n = 1.0 - f.x;
float grad = pow(0.04*p.x + 0.07, -0.5);
float t = 0.08*sin(1.3*u_time + p.x/3.0);
return vec4(grad * n*body_color + t, 1.0);
}
}
vec4 drawFins(vec2 p) {
float s = sin(0.1*sin(u_time));
float c = cos(0.1*sin(u_time));
mat2 R = mat2(
c, s,
-s, c
);
R = transpose(R);
p.xy = R*p.xy;
float nL = FinLineFbm( p );
float theta = 30.0*atan(p.y, p.x) + 3.0 * nL;
float theta_mod = mod(theta, PI);
float k = pow(sin(theta_mod), 0.2);
float nR = FinRingFbm( p );
float d_noised = length(p) * 30.0 + 3.0 * nR;
vec3 base_color;
float roof_middle = 15.0;
float edge_middle = 18.0;
float edge = 25.0;
if(-30.0*PI/2.2 < theta && theta < 30.0*PI/2.2){
return vec4(0.0);
}else{
if(d_noised <= roof_middle){
base_color = roof_fin_color;
}else if((roof_middle <= d_noised) && (d_noised < edge_middle)){
base_color = middle_fin_color;
}else if((edge_middle <= d_noised) && (d_noised < edge)){
base_color = edge_fin_color;
}else{
return vec4(0.0);
}
}
float t = 0.1*sin(2.0*u_time-0.5*d_noised);
return vec4( base_color*k + t, 1.0 );
}
void main() {
vec2 p = (2.0 * gl_FragCoord.xy - u_resolution.xy) / u_resolution.y;
float mask = 1.0;
mask *= 1.0 - step( 0.8, distance( p.xy, vec2(0.45, 0.6) ) );
mask *= 1.0 - step( 0.8, distance( p.xy, vec2(0.45, -0.6) ) );
if(mask == 1.0){
// colour_out = vec4(vec2(sin(u_time)), 0.0, 1.0);
colour_out = drawBody(p);
}else{
colour_out = drawFins(p);
}
}
half moon by kuroki
uniform vec2 u_resolution;
uniform float u_time;
vec3 mod289(vec3 x) { return x - floor(x * (1.0 / 289.0)) * 289.0; }
vec2 mod289(vec2 x) { return x - floor(x * (1.0 / 289.0)) * 289.0; }
vec3 permute(vec3 x) { return mod289(((x*34.0)+1.0)*x); }
float snoise(vec2 v) {
const vec4 C = vec4(0.211324865405187, 0.366025403784439, -0.577350269189626, 0.024390243902439);
vec2 i = floor(v + dot(v, C.yy) );
vec2 x0 = v - i + dot(i, C.xx);
vec2 i1 = (x0.x > x0.y) ? vec2(1.0, 0.0) : vec2(0.0, 1.0);
vec4 x12 = x0.xyxy + C.xxzz;
x12.xy -= i1;
i = mod289(i);
vec3 p = permute( permute( i.y + vec3(0.0, i1.y, 1.0 )) + i.x + vec3(0.0, i1.x, 1.0 ));
vec3 m = max(0.5 - vec3(dot(x0,x0), dot(x12.xy,x12.xy), dot(x12.zw,x12.zw)), 0.0);
m = m*m ;
m = m*m ;
vec3 x = 2.0 * fract(p * C.www) - 1.0;
vec3 h = abs(x) - 0.5;
vec3 ox = floor(x + 0.5);
vec3 a0 = x - ox;
m *= 1.79284291400159 - 0.85373472095314 * ( a0*a0 + h*h );
vec3 g;
g.x = a0.x * x0.x + h.x * x0.y;
g.yz = a0.yz * x12.xz + h.yz * x12.yw;
return 130.0 * dot(m, g);
}
float fbm(vec2 p) {
float total = 0.0;
float amplitude = 0.5;
for (int i = 0; i < 3; i++) {
total += snoise(p) * amplitude;
p *= 2.0;
amplitude *= 0.5;
}
return total;
}
mat2 rotate2d(float angle){
return mat2(cos(angle), -sin(angle), sin(angle), cos(angle));
}
float smin(float a, float b, float k) {
float h = clamp(0.5 + 0.5 * (b - a) / k, 0.0, 1.0);
return mix(b, a, h) - k * h * (1.0 - h);
}
float sdEllipse(vec2 p, vec2 r) {
float k0 = length(p / r);
float k1 = length(p / (r * r));
return k0 * (k0 - 1.0) / k1;
}
float fishSDF(vec2 p) {
p += vec2(0.1, 0.0);
vec2 finP = p;
float wave = sin(finP.x * 10.0 - u_time * 2.0 + finP.y * 5.0) * 0.015;
float angle = atan(finP.y, finP.x);
float folds = sin(angle * 40.0) * 0.01;
finP += wave + folds;
vec2 bodyP = p;
bodyP *= rotate2d(-0.1);
float body = sdEllipse(bodyP - vec2(0.2, 0.0), vec2(0.32, 0.11));
body += smoothstep(0.1, -0.4, bodyP.x) * 0.03;
vec2 tailPos = finP - vec2(-0.35, 0.0);
float tail = sdEllipse(tailPos, vec2(0.45, 0.55));
tail += sin(p.y * 25.0) * 0.015;
vec2 dorsalPos = finP - vec2(-0.15, 0.25);
dorsalPos *= rotate2d(0.3);
float dorsal = sdEllipse(dorsalPos, vec2(0.35, 0.3));
vec2 analPos = finP - vec2(-0.1, -0.3);
analPos *= rotate2d(-0.2);
float anal = sdEllipse(analPos, vec2(0.45, 0.35));
float fins = smin(tail, dorsal, 0.15);
fins = smin(fins, anal, 0.2);
float mainShape = smin(body, fins, 0.06);
return mainShape;
}
vec3 getFishColor(vec2 p) {
vec2 texUV = p;
vec3 blueDeep = vec3(0.0, 0.1, 0.4);
vec3 blueBright = vec3(0.0, 0.7, 0.9);
vec3 redDeep = vec3(0.6, 0.05, 0.1);
vec3 redBright = vec3(1.0, 0.35, 0.15);
float blueGradFactor = smoothstep(0.4, -0.8, texUV.x) * 0.7 + smoothstep(0.3, -0.3, texUV.y) * 0.3;
vec3 dynamicBlue = mix(blueDeep, blueBright, blueGradFactor);
float redGradFactor = smoothstep(0.1, -0.6, texUV.y);
vec3 dynamicRed = mix(redDeep, redBright, redGradFactor);
// Masks Defination
float bodyMask = smoothstep(-0.6, 0.0, texUV.x);
float bottomY = 1.0 - smoothstep(-0.5, 0.05, texUV.y);
float bottomX = smoothstep(-0.780, -0.124, texUV.x);
float bottomMask = bottomY * bottomX;
// BaseColor
vec3 baseColor = mix(dynamicBlue, dynamicRed, bottomMask);
// Simple textures
float scales = snoise(texUV * 30.0);
scales = smoothstep(0.0, 0.6, scales);
vec2 finCenter = texUV - vec2(-0.3, 0.0);
float angle = atan(finCenter.y, finCenter.x);
float dist = length(finCenter);
float rayNoise = fbm(vec2(angle * 10.0, dist * 2.0 - u_time * 0.5));
float streaks = smoothstep(0.2, 0.8, rayNoise);
// scales
vec3 colCyan = vec3(0.0, 0.8, 0.9);
baseColor += colCyan * scales * 0.15 * bodyMask * (1.0 - bottomMask);
// fins
vec3 streakColor = mix(vec3(0.0, 0.0, 0.2), vec3(0.3, 0.0, 0.0), bottomMask);
baseColor += streakColor * streaks * 0.5;
baseColor += colCyan * 0.2 * streaks * (1.0 - bottomMask);
// illustration
float light = snoise(texUV * 2.0 + u_time * 0.2);
baseColor += light * 0.1;
// eyes
vec2 eyePosCenter = vec2(0.350,0.050);
float eyeRadius = 0.022;
float distToEye = length(texUV - eyePosCenter);
float eyeMask = smoothstep(eyeRadius + 0.002, eyeRadius - 0.002, distToEye);
vec3 eyeBallCol = vec3(0.02, 0.02, 0.05);
vec2 highlightPos = eyePosCenter + vec2(0.007, 0.007);
float highlight = smoothstep(0.008, 0.003, length(texUV - highlightPos));
vec3 finalEyeCol = mix(eyeBallCol, vec3(1.0), highlight);
baseColor = mix(baseColor, finalEyeCol, eyeMask);
return baseColor;
}
void main() {
vec2 st = gl_FragCoord.xy / u_resolution.xy;
st = st * 2.0 - 1.0;
st.x *= u_resolution.x / u_resolution.y;
float d = fishSDF(st);
vec3 bgColor = vec3(0.0, 0.02, 0.08);
vec3 fishColor = getFishColor(st);
float alpha = smoothstep(0.005, -0.005, d);
float outline = smoothstep(0.01, 0.0, abs(d));
vec3 outlineColor = vec3(0.4, 0.8, 1.0);
vec3 objColor = mix(fishColor, outlineColor, outline * 0.5);
vec3 finalColor = mix(bgColor, objColor, alpha);
float glow = exp(-d * 4.0) * 0.2 * alpha;
finalColor += vec3(0.0, 0.2, 0.5) * exp(-abs(d) * 10.0) * 0.5;
colour_out = vec4(finalColor, 1.0);
}
Unknown by cookie
precision mediump float;
uniform vec2 u_resolution;
uniform vec2 u_mouse;
uniform float u_time;
// Utilities
float smin(float a, float b, float k) {
float h = clamp(0.5 + 0.5 * (b - a) / k, 0.0, 1.0);
return mix(b, a, h) - k * h * (1.0 - h);
}
float smax(float a, float b, float k) {
return -smin(-a, -b, k);
}
mat2 rot(float a) {
return mat2(cos(a), -sin(a), sin(a), cos(a));
}
// Noise (Perlin)
float hash(vec2 p) {
p = fract(p * vec2(672.077, 110.911));
p += dot(p, p.yx + 19.21);
return fract(p.x * p.y);
}
float noise(vec2 p) {
vec2 i = floor(p), f = fract(p);
vec2 u = f * f * (3.0 - 2.0 * f);
float a = hash(i);
float b = hash(i + vec2(1, 0));
float c = hash(i + vec2(0, 1));
float d = hash(i + vec2(1, 1));
return mix(mix(a, b, u.x), mix(c, d, u.x), u.y);
}
float fbm(vec2 p, float t) {
float v = 0.0, a = 0.5;
p += t * 0.05;
for (int i = 0; i < 5; i++) {
v += a * noise(p);
a *= 0.5; p = rot(0.5) * p * 2.0;
}
return v;
}
float caustics(vec2 p, float t) {
return pow(noise(p * 3.0 + t * 0.3) * noise(p * 5.0 - t * 0.2 + 100.0) * noise(p * 7.0 + t * 0.19 + 200.0), 0.67) * 3.0;
}
// Animation
float finFlap(float t, float speed, float amp, float phase) {
return sin(t * speed + phase) * amp;
}
vec2 bodyWave(vec2 p, float t) {
float wave = sin(p.x * 3.0 - t * 4.0) * 0.025 * smoothstep(-0.3, 0.4, p.x);
return vec2(p.x, p.y - wave);
}
// SDF primitives
float sdVesica(vec2 p, float r, float d) {
p = abs(p.yx);
float b = sqrt(r * r - d * d);
return ((p.y - b) * d > p.x * b) ? length(p - vec2(0, b)) : length(p - vec2(-d, 0)) - r;
}
float sdEllipse(vec2 p, vec2 ab) {
p = abs(p);
if (p.x > p.y) { p = p.yx; ab = ab.yx; }
float l = ab.y * ab.y - ab.x * ab.x;
float m = ab.x * p.x / l, n = ab.y * p.y / l;
float c = (m * m + n * n - 1.0) / 3.0, c3 = c * c * c;
float q = c3 + m * m * n * n * 2.0, d = c3 + m * m * n * n;
float g = m + m * n * n, co;
if (d < 0.0) {
float h = acos(q / c3) / 3.0, s = cos(h), t = sin(h) * sqrt(3.0);
float rx = sqrt(-c * (s + t + 2.0) + m * m), ry = sqrt(-c * (s - t + 2.0) + m * m);
co = (ry + sign(l) * rx + abs(g) / (rx * ry) - m) / 2.0;
} else {
float h = 2.0 * m * n * sqrt(d);
float s = sign(q + h) * pow(abs(q + h), 1.0/3.0);
float u = sign(q - h) * pow(abs(q - h), 1.0/3.0);
float rx = -s - u - c * 4.0 + 2.0 * m * m, ry = (s - u) * sqrt(3.0);
float rm = sqrt(rx * rx + ry * ry);
co = (ry / sqrt(rm - rx) + 2.0 * g / rm - m) / 2.0;
}
vec2 r = ab * vec2(co, sqrt(1.0 - co * co));
return length(r - p) * sign(p.y - r.y);
}
// Fish Parts
float sdBody(vec2 p) {
return sdVesica(p, 0.5, 0.35);
}
float getScales(vec2 p) {
p = vec2(-p.y, p.x);
vec2 cell = p / vec2(0.035, 0.025);
if (mod(floor(cell.y), 2.0) == 1.0) cell.x += 0.5;
vec2 uv = fract(cell);
float dist = length(uv - vec2(0.5, -0.2));
float arc = abs(dist - 0.85) - 0.08;
return smoothstep(0.3, 0.9, dist / 0.85) * 0.3 +
(1.0 - smoothstep(0.0, 0.03, arc)) * step(0.2, uv.y) * 0.4;
}
float getRays(vec2 p, float n, float i) {
float rays = 0.5 + 0.5 * sin(atan(p.y, p.x) * n);
return mix(0.5, pow(rays, 0.5), smoothstep(0.0, 0.4, length(p)) * i);
}
// Tail fin
vec3 sdTail(vec2 p, float t) {
vec2 d = rot(finFlap(t, 5.0, 0.08, 0.0)) * (p - vec2(0.12, 0.0));
float angle = atan(d.y, d.x), dist = length(d);
float wave = sin(angle * 14.0 - t * 3.0) * 0.0375 + sin(angle * 23.0 - t * 4.5) * 0.018;
float shape = max(-d.x + 0.075, max(dist - 0.57 - wave,
max(d.y - d.x * 1.8 + 0.02, -d.y - d.x * 1.8 + 0.01)));
return vec3(shape, getRays(d, 70.0, 0.9), dist - 0.57 - wave);
}
// Dorsal fin
vec3 sdDorsal(vec2 p, float t) {
vec2 d = p - vec2(0.15, 0.1);
d = rot(0.1 + finFlap(t, 3.5, 0.05, 0.5) * smoothstep(0.0, 0.3, d.y)) * d;
float wave = sin(d.x * 48.0 - t * 2.5) * 0.0225 + sin(d.x * 80.0 - t * 3.25) * 0.012;
float shape = max(-d.y, max(-d.x - 0.27 + d.y * 0.4,
max(d.x - 0.18 - d.y * 0.6, d.y - 0.33 - wave)));
return vec3(shape, getRays(d, 50.0, 0.8), d.y - 0.33 - wave);
}
// Anal fin
vec3 sdAnal(vec2 p, float t) {
vec2 d = p - vec2(0.10, -0.1);
d = rot(0.1 - finFlap(t, 3.5, 0.04, 2.0) * smoothstep(0.0, -0.25, d.y)) * d;
float wave = sin(d.x * 48.0 - t * 2.5) * 0.018 + sin(d.x * 72.0 - t * 3.0) * 0.009;
float shape = max(d.y, max(-d.x - 0.225 - d.y * 0.5,
max(d.x - 0.15 + d.y * 0.7, -d.y - 0.27 - wave)));
return vec3(shape, getRays(d, 40.0, 0.8), -d.y - 0.27 - wave);
}
// Pectoral fin
vec3 sdPectoral(vec2 p, float t) {
vec2 d = rot(-0.1 + finFlap(t, 8.0, 0.25, 0.0)) * (p - vec2(-0.15, -0.05));
return vec3(sdEllipse(d - vec2(0.08, 0.0), vec2(0.093, 0.06)), getRays(d, 44.0, 0.4), d.x);
}
// Pelvic fin
vec3 sdPelvic(vec2 p, float t) {
vec2 d = rot(-1.5 + finFlap(t, 2.0, 0.2, 3.14)) * (p - vec2(-0.15, -0.10));
float taper = mix(0.045, 0.012, smoothstep(0.0, 0.28, d.x));
float wave = sin(d.y * 25.0 - t * 2.0) * 0.008;
float shape = smax(max(d.y - taper, -d.y - taper), max(-d.x - 0.01, d.x - 0.28 - wave), 0.025);
return vec3(shape, getRays(d, 30.0, 0.7), d.x);
}
// Main
void main() {
// Center the fish
vec2 uv = (gl_FragCoord.xy - 0.5 * u_resolution) / u_resolution.y * 1.5;
float t = u_time;
// Global drift
uv += vec2(sin(t * 0.5) * 0.02, sin(t * 0.7) * 0.015);
vec2 bodyUV = bodyWave(uv, t);
// Background: tropical freshwater
float depth = smoothstep(-1.0, 0.6, uv.y);
vec3 col = mix(vec3(0.85, 0.78, 0.55), vec3(0.5, 0.75, 0.65), depth);
col = mix(col, vec3(0.6, 0.85, 0.8), smoothstep(0.3, 1.0, depth) + fbm(uv * 1.5, t * 0.7) * 0.2);
col += vec3(1.0, 0.98, 0.85) * caustics(uv * 0.8, t * 0.8) * 0.12 * depth;
col += vec3(0.9, 0.95, 0.8) * smoothstep(0.78, 0.88, noise(uv * 40.0 + t * 0.3)) * 0.15;
col = mix(col, vec3(0.7, 0.82, 0.5), 0.08);
// Colors
vec3 blue = vec3(0.05, 0.35, 0.9), black = vec3(0.02, 0.02, 0.04);
// Compute all SDFs
float dBody = sdBody(bodyUV);
vec3 tail = sdTail(bodyUV, t), dorsal = sdDorsal(bodyUV, t);
vec3 anal = sdAnal(bodyUV, t), pectoral = sdPectoral(bodyUV, t), pelvic = sdPelvic(bodyUV, t);
// Fusion zones
float fusionK = 0.04;
float dBodyDorsal = smin(dBody, dorsal.x, fusionK);
float dBodyAnal = smin(dBody, anal.x, fusionK);
float dTailFused = smin(tail.x, dBody, fusionK * 0.5);
// Compute body color
#define BODY_COLOR(uv) (mix(black, blue, smoothstep(-0.3, 0.5, uv.x)) * (0.7 + 0.5 * getScales(uv)))
// Compute fin color
#define FIN_COLOR(data, t1, t2) mix(mix(blue, black, smoothstep(t1, t2, data.z)) * 0.7, \
mix(blue, black, smoothstep(t1, t2, data.z)) * 1.3, data.y)
// Layer 1-2: Dorsal & Anal (background)
if (dorsal.x < 0.0) col = FIN_COLOR(dorsal, -0.25, -0.01);
if (anal.x < 0.0) col = FIN_COLOR(anal, -0.25, -0.01);
// Layer 3: Pelvic
if (pelvic.x < 0.0) {
float m = smoothstep(0.05, 0.25, pelvic.z);
col = mix(mix(black, blue, m) * 0.7, mix(black, blue, m) * 1.3, pelvic.y);
}
// Layer 4: Body
if (dBody < 0.0) col = BODY_COLOR(bodyUV);
// Fusion zones (dorsal-body, anal-body)
if (dBodyDorsal < 0.0 && dBody > 0.0 && dorsal.x > 0.0)
col = mix(FIN_COLOR(dorsal, -0.25, -0.01), BODY_COLOR(bodyUV), smoothstep(fusionK, 0.0, dBody));
if (dBodyAnal < 0.0 && dBody > 0.0 && anal.x > 0.0)
col = mix(FIN_COLOR(anal, -0.25, -0.01), BODY_COLOR(bodyUV), smoothstep(fusionK, 0.0, dBody));
// Layer 5: Tail (foreground)
if (dTailFused < 0.0) {
if (tail.x < 0.0) col = FIN_COLOR(tail, -0.25, -0.01);
else col = mix(BODY_COLOR(bodyUV), FIN_COLOR(tail, -0.25, -0.01), smoothstep(fusionK * 0.5, 0.0, tail.x));
}
// Layer 6: Pectoral
if (pectoral.x < 0.0) {
float m = smoothstep(0.10, 0.16, pectoral.z);
col = mix(mix(black, blue, m) * 0.8, mix(black, blue, m) * 1.2, pectoral.y);
}
// Layer 7: Eye
vec2 eyeP = bodyUV - vec2(-0.2, 0.03);
float dEye = length(eyeP) - 0.028;
if (dEye < 0.0) {
col = vec3(0.0, 0.02, 0.15);
if (length(eyeP - vec2(0.005, 0)) < 0.014) col = vec3(0.05, 0.1, 0.25);
if (length(eyeP - vec2(-0.008, 0.008)) < 0.006) col = vec3(1.0);
if (abs(dEye) < 0.006) col = vec3(0.0);
}
colour_out = vec4(col, 1.0);
}
Blue Half Moon Male Siamese Fighting Fish by
Prime
uniform vec2 u_resolution;
uniform float u_time;
// Citation for code below ///////////////////////////////////////////
// Description : Array and textureless GLSL 2D simplex noise function.
// Author : Ian McEwan, Ashima Arts.
// Maintainer : stegu
// Lastmod : 20110822 (ijm)
// License : Copyright (C) 2011 Ashima Arts. All rights reserved.
// Distributed under the MIT License. See LICENSE file.
// https://github.com/ashima/webgl-noise
// https://github.com/stegu/webgl-noise
//
vec3 mod289(vec3 x) {
return x - floor(x * (1.0 / 289.0)) * 289.0;
}
vec2 mod289(vec2 x) {
return x - floor(x * (1.0 / 289.0)) * 289.0;
}
vec3 permute(vec3 x) {
return mod289(((x * 34.0) + 10.0) * x);
}
float snoise(vec2 v) {
const vec4 C = vec4(
0.211324865405187, // (3.0-sqrt(3.0))/6.0
0.366025403784439, // 0.5*(sqrt(3.0)-1.0)
-0.577350269189626, // -1.0 + 2.0 * C.x
0.024390243902439 // 1.0 / 41.0
);
// First corner
vec2 i = floor(v + dot(v, C.yy));
vec2 x0 = v - i + dot(i, C.xx);
// Other corners
vec2 i1;
// i1.x = step( x0.y, x0.x ); // x0.x > x0.y ? 1.0 : 0.0
// i1.y = 1.0 - i1.x;
i1 = (x0.x > x0.y) ? vec2(1.0, 0.0) : vec2(0.0, 1.0);
// x0 = x0 - 0.0 + 0.0 * C.xx ;
// x1 = x0 - i1 + 1.0 * C.xx ;
// x2 = x0 - 1.0 + 2.0 * C.xx ;
vec4 x12 = x0.xyxy + C.xxzz;
x12.xy -= i1;
// Permutations
i = mod289(i); // Avoid truncation effects in permutation
vec3 p = permute(
permute(i.y + vec3(0.0, i1.y, 1.0)) +
i.x + vec3(0.0, i1.x, 1.0)
);
vec3 m = max(0.5 - vec3(dot(x0, x0), dot(x12.xy, x12.xy), dot(x12.zw, x12.zw)), 0.0);
m = m * m;
m = m * m;
// Gradients: 41 points uniformly over a line, mapped onto a diamond.
// The ring size 17*17 = 289 is close to a multiple of 41 (41*7 = 287)
vec3 x = 2.0 * fract(p * C.www) - 1.0;
vec3 h = abs(x) - 0.5;
vec3 ox = floor(x + 0.5);
vec3 a0 = x - ox;
// Normalise gradients implicitly by scaling m
// Approximation of: m *= inversesqrt( a0*a0 + h*h );
m *= 1.79284291400159 - 0.85373472095314 * (a0 * a0 + h * h);
// Compute final noise value at P
vec3 g;
g.x = a0.x * x0.x + h.x * x0.y;
g.yz = a0.yz * x12.xz + h.yz * x12.yw;
return 130.0 * dot(m, g);
}
//////////////////////////////////////////////////////////////////////
// fbm
float fbm(vec2 p) {
float f = 0.0, scale;
for (int i = 0; i < 4; i++) {
scale = pow(pow(2.0, 4.0 / 3.0), float(i));
f += snoise(p * scale) / scale;
}
return f;
}
// fbm-ception (modified)
float noise(vec3 v) {
v.xy += vec2(fbm(v.xy), fbm(v.xy + v.z));
return fbm(v.xy) * 0.35 + 0.45;
}
// turbulence
float turbulence(vec2 p) {
float f = 0.0, scale;
for (int i = 0; i < 4; i++) {
scale = pow(pow(2.0, 4.0 / 3.0), float(i));
f += abs(snoise(p * scale)) / scale;
}
return f;
}
// smooth min
float smin(float a, float b, float k) {
float h = max(k - abs(a - b), 0.0) / k;
return min(a, b) - h * h * k * 0.25;
}
// sdf
float sdEllipse(vec2 p, vec2 r) {
float k0 = length(p / r);
float k1 = length(p / (r * r));
return k0 * (k0 - 1.0) / k1;
}
float sdCircle(vec2 p, float r) {
return length(p) - r;
}
void main() {
// main colours
vec3 bodyColour = vec3(0.085, 0.258, 0.600);
vec3 scaleColour = vec3(0.0);
vec3 finColour1 = vec3(0.085, 0.258, 0.600);
vec3 finColour2 = vec3(0.900, 0.803, 0.364);
vec3 finColour3 = vec3(0.0);
// normalize
vec2 p = (gl_FragCoord.xy - 0.5 * u_resolution.xy) / u_resolution.y;
// scale
p = p / 0.7;
// head
vec2 pHead = p - vec2(-0.1, 0.0);
float dHead = sdEllipse(pHead, vec2(0.3, 0.08));
// dorsal fin
vec2 pDorsal = p - vec2(0.05, 0.09);
float dorsalFrills = fbm(pDorsal * 4.0 + u_time) * 0.01;
float dDorsal = sdEllipse(pDorsal, vec2(0.18, 0.15)) - dorsalFrills;
float dDorsalBack = sdEllipse(pDorsal, vec2(0.18, 0.18)) - dorsalFrills;
float tDorsalStripe = 0.5 + sin(pDorsal.x * 100.0) * 0.5;
vec3 dorsalColour = mix(finColour1, finColour2, tDorsalStripe);
// anal fin
vec2 pAnal = p - vec2(0.05, -0.11);
float analFrills = fbm(pAnal * 4.0 + u_time) * 0.01;
float dAnal = sdEllipse(pAnal, vec2(0.28, 0.15)) - analFrills;
float dAnalBack = sdEllipse(pAnal, vec2(0.28, 0.18)) - analFrills;
float tAnalStripe = 0.5 + sin(pAnal.x * 100.0) * 0.5;
vec3 analColour = mix(finColour1, finColour2, tAnalStripe);
// caudal fin
vec2 pCaudal = p - vec2(0.35, 0.0);
float caudalFrills = fbm(pCaudal * 3.0 + u_time) * 0.01;
float dCaudal = sdCircle(pCaudal, 0.22) - caudalFrills;
float dCaudalBack = sdCircle(pCaudal - vec2(0.05, 0.0), 0.24) - caudalFrills;
float dBody = smin(dHead, dCaudal, 0.12);
vec2 caudalRadialVec = p - vec2(0.2, 0.0);
float caudalAngle = atan(caudalRadialVec.y, caudalRadialVec.x);
float caudalRays = smoothstep(0.3, 0.8, abs(sin(caudalAngle * 12.0)));
vec3 caudalColour = mix(finColour1, finColour2, caudalRays);
// pectoral fin
vec2 pPectoral = p - vec2(-0.05, 0.0);
float dPectoral = sdCircle(pPectoral, 0.08) - fbm(pPectoral * 8.0 + u_time) * 0.01;
vec2 pectoralRadialVec = pPectoral - vec2(-0.08, 0.0);
float pectoralAngle = atan(pectoralRadialVec.y, pectoralRadialVec.x);
float pectoralRays = smoothstep(0.3, 0.8, abs(sin(pectoralAngle * 10.0)));
vec3 pectoralColour = mix(finColour1, finColour3, pectoralRays);
// ventral fin
vec2 pVentral = p - vec2(-0.25, -0.1);
float dVentral = sdEllipse(pVentral, vec2(0.02, 0.15));
float dVentralBack = sdEllipse(pVentral + vec2(0.01, 0.0), vec2(0.02, 0.15)) - fbm(pVentral * 8.0 + u_time) * 0.01;
float tVentral = smoothstep(0.01, 0.0, pVentral.y);
vec3 ventralColour = mix(finColour1, finColour2, tVentral);
// eye
vec2 pEye = p - vec2(-0.35, 0.0);
float dEye = sdCircle(pEye, 0.018);
// scales
float nScale = turbulence(gl_FragCoord.xy / 10.0);
float tScale = smoothstep(0.1, 0.5, nScale);
bodyColour = mix(scaleColour, bodyColour, tScale);
// background
vec3 uv = vec3(0.5 * gl_FragCoord.xy / u_resolution.y - 0.5, 0.03 * u_time);
float bgNoise = noise(uv);
bgNoise = pow(bgNoise, 4.0) * 5.0;
vec3 colour = mix(vec3(0.222, 0.640, 0.263), vec3(0.331, 0.955, 0.392), bgNoise);
colour = mix(colour, finColour3, smoothstep(0.01, 0.0, dCaudalBack));
colour = mix(colour, finColour3, smoothstep(0.01, 0.0, dDorsalBack));
colour = mix(colour, finColour3, smoothstep(0.01, 0.0, dAnalBack));
colour = mix(colour, finColour3, smoothstep(0.01, 0.0, dVentralBack));
colour = mix(colour, ventralColour, smoothstep(0.01, 0.0, dVentral));
colour = mix(colour, dorsalColour, smoothstep(0.01, 0.0, dDorsal));
colour = mix(colour, analColour, smoothstep(0.01, 0.0, dAnal));
colour = mix(colour, caudalColour, smoothstep(0.01, 0.0, dCaudal));
colour = mix(colour, bodyColour, smoothstep(0.01, 0.0, dHead));
colour = mix(colour, pectoralColour, smoothstep(0.01, 0.0, dPectoral));
colour = mix(colour, vec3(0.1, 0.08, 0.05), smoothstep(0.003, 0.0, dEye));
colour_out = vec4(colour, 1.0);
}
blue halfmoon by hoshi
More Student Works
The rest of the student works from 2025 are shown below. Note that some are very computationally expensive and may potentially cause issues with your browser. Show Other Student Works
uniform vec2 u_resolution;
uniform vec2 u_mouse;
uniform float u_time;
void main() {
vec2 st = gl_FragCoord.xy/u_resolution.xy;
float y1 = abs(10.0 * st.y - 5.0);
float f = pow(abs((10.0 * st.x - 5.4 - pow(y1, 0.1)) / 4.0), 3.0) + pow(y1, 0.5) - 1.0;
float m1 = step(0.0, f);
float y2 = abs(3.0 * st.y - 1.5);
float base = 6.0 * st.x - 1.100 - pow(y2, 1.5);
float wave = 0.05 * sin(st.x * 100.0 + st.y * 30.0 + u_time * 5.0);
float g = pow(abs(base), 2.0) + pow(y2, 3.0) - (1.0 + wave);
float m2 = step(0.0, g);
float h = pow(2.5 * (st.x - 0.625) / (3.0*(st.y - 0.5)), 2.0) + pow(2.75 * (st.y - 0.5), 2.0) - (1.0 + 0.5 * wave);
float m3 = step(0.0, h);
vec4 c_body = vec4( 0.3, 0.8, 0.1*abs(sin(u_time+st.x))+0.8, 1.0 );
vec4 c_bg = vec4(1.0, 0.0, 1.0, 0.15*abs(sin(0.800*u_time + st.x))+0.1);
vec4 c_fin = vec4(0.3, 0.1*abs(sin(2.0*u_time+st.x*2.7))+0.8, 0.1*abs(sin(1.5*u_time+st.x))+0.8, 1.0);
vec4 final_color = c_bg * m1 * m2 * m3 + c_body * (1.0 - m1) * m2 + c_fin * max(1.0 - m2, (1.0 - m3) * m1);
colour_out = final_color;
}
Unknown by KK
uniform vec2 u_resolution;
uniform vec2 u_mouse;
uniform float u_time;
// --- Noise Functions (from webgl-noise logic) ---
vec3 permute(vec3 x) { return mod(((x*34.0)+1.0)*x, 289.0); }
float snoise(vec2 v){
const vec4 C = vec4(0.211324865405187, 0.366025403784439,
-0.577350269189626, 0.024390243902439);
vec2 i = floor(v + dot(v, C.yy) );
vec2 x0 = v - i + dot(i, C.xx);
vec2 i1;
i1 = (x0.x > x0.y) ? vec2(1.0, 0.0) : vec2(0.0, 1.0);
vec4 x12 = x0.xyxy + C.xxzz;
x12.xy -= i1;
i = mod(i, 289.0);
vec3 p = permute( permute( i.y + vec3(0.0, i1.y, 1.0 ))
+ i.x + vec3(0.0, i1.x, 1.0 ));
vec3 m = max(0.5 - vec3(dot(x0,x0), dot(x12.xy,x12.xy), dot(x12.zw,x12.zw)), 0.0);
m = m*m ;
m = m*m ;
vec3 x = 2.0 * fract(p * C.www) - 1.0;
vec3 h = abs(x) - 0.5;
vec3 ox = floor(x + 0.5);
vec3 a0 = x - ox;
m *= 1.79284291400159 - 0.85373472095314 * ( a0*a0 + h*h );
vec3 g;
g.x = a0.x * x0.x + h.x * x0.y;
g.yz = a0.yz * x12.xz + h.yz * x12.yw;
return 130.0 * dot(m, g);
}
// --- Shape Functions ---
// 2D Rotation
mat2 rotate2d(float _angle){
return mat2(cos(_angle),-sin(_angle),
sin(_angle),cos(_angle));
}
void main() {
vec2 st = gl_FragCoord.xy/u_resolution.xy;
st.x *= u_resolution.x/u_resolution.y;
// Background color (pale blue like an aquarium)
vec3 color = vec3(0.1, 0.15, 0.3) + (st.y * 0.2);
// --- Fish Coordinate Transformation ---
vec2 fishSt = st - vec2(0.5, 0.5);
// Slightly sway the whole fish
float swim = sin(u_time * 2.0) * 0.02;
fishSt.y += swim;
// --- Body (Biological/Organic shape) ---
vec2 bodyPos = fishSt;
vec2 distortedPos = bodyPos;
distortedPos.y *= 1.0 + distortedPos.x * 1.5;
// Create a perfect curve (ellipse) using length()
float dist = length(vec2(distortedPos.x * 0.8, distortedPos.y * 2.0));
// Use smoothstep to slightly blur the edges
float body = smoothstep(0.25, 0.23, dist);
// --- Fins ---
// Caudal Fin (Tail) - Right side
vec2 tailSt = fishSt - vec2(0.15, 0.0);
float tailAng = atan(tailSt.y, tailSt.x);
float tailLen = length(tailSt);
float tailWave = sin(tailAng * 10.0 - u_time * 3.0) * 0.05;
float tailShape = smoothstep(0.4 + tailWave, 0.35 + tailWave, tailLen);
tailShape *= smoothstep(-0.8, -0.5, tailAng) * smoothstep(0.8, 0.5, tailAng);
tailShape *= step(0.1, tailLen);
// Dorsal Fin - Top
vec2 dorsalSt = fishSt - vec2(0.05, 0.05);
dorsalSt = rotate2d(-0.5) * dorsalSt;
float dorsalWave = sin(dorsalSt.x * 10.0 - u_time * 4.0) * 0.03;
float shapeBase = 0.25 + dorsalWave - abs(dorsalSt.x)*0.5;
float dorsalShape = smoothstep(0.0, 0.02, dorsalSt.y) * smoothstep(shapeBase, shapeBase - 0.05, dorsalSt.y);
dorsalShape *= smoothstep(0.16, 0.10, abs(dorsalSt.x));
// Anal Fin - Bottom
vec2 analSt = fishSt - vec2(0.05, -0.05);
analSt = rotate2d(0.3) * analSt;
float analWave = sin(analSt.x * 15.0 - u_time * 5.0) * 0.02;
float analShape = step(analSt.y, 0.0) * step(-0.3 + analWave + abs(analSt.x)*0.8, analSt.y);
analShape *= step(abs(analSt.x - 0.05), 0.2);
// --- Color Application (Red from Reference Image) ---
vec3 bettaRed = vec3(0.85, 0.05, 0.15); // Main red
vec3 bettaDark = vec3(0.4, 0.0, 0.1); // Dark red
vec3 bettaPink = vec3(1.0, 0.4, 0.5); // Highlight
// Scales
float scalePattern = step(0.5, sin((fishSt.x + fishSt.y)*40.0) * sin((fishSt.x - fishSt.y)*40.0));
// Combine masks for the whole fish
float fullFish = max(body, max(tailShape, max(dorsalShape, analShape)));
// Color mixing
vec3 fishColor = mix(bettaRed, bettaDark, abs(fishSt.y)*3.0);
// Transparency and strands of the fins
float finStrands = sin(atan(tailSt.y, tailSt.x) * 40.0);
if(fullFish > 0.0 && body < 0.1) { // Fins part only
fishColor += finStrands * 0.1;
fishColor = mix(fishColor, bettaPink, tailLen * 0.5);
}
// Scales and highlights on the body
if(body > 0.0) {
fishColor = mix(fishColor, vec3(0.0, 0.0, 0.0), scalePattern * 0.3);
fishColor += bettaPink * (1.0 - distance(fishSt, vec2(-0.1, 0.0))*3.0) * 0.5;
}
// Eye center position
vec2 eyeCenter = vec2(-0.18, 0.05);
float eyeDist = length(fishSt - eyeCenter);
// Sclera (white), Pupil (black), and Highlight
float eyeWhite = smoothstep(0.035, 0.032, eyeDist);
float eyePupil = smoothstep(0.028, 0.015, eyeDist);
vec2 highlightPos = eyeCenter + vec2(0.01, 0.01);
float eyeHighlight = smoothstep(0.01, 0.1, length(fishSt - highlightPos));
vec3 eyeColor = vec3(0.1, 0.1, 0.1);
eyeColor = mix(eyeColor, vec3(0.0, 0.0, 0.0), eyePupil);
eyeColor = mix(eyeColor, vec3(1.0, 1.0, 1.0), eyeHighlight);
if (body > 0.0) {
fishColor = mix(fishColor, eyeColor, eyeWhite);
}
// Final Composition
colour_out = vec4(mix(color, fishColor, fullFish), 1.0);
}
Bouba Betta(BB) by Koji Naito
uniform vec2 u_resolution;
uniform vec2 u_mouse;
uniform float u_time;
void main() {
vec2 st = gl_FragCoord.xy / u_resolution.xy;
vec2 p = st * 2.0 - 1.0;
p.x *= u_resolution.x / u_resolution.y;
float speed = 0.5;
float xPos = sin(u_time * speed) * 0.3;
float yPos = 0.0;
vec2 tailPivot = vec2(xPos - 0.3, yPos);
vec2 r = p - tailPivot;
float ang = atan(r.y, r.x);
float rad = length(r);
float tail = abs(ang) - 1.1 + sin(ang*10.0 + u_time*3.0)*0.05 + (rad-0.15);
float tailMask = smoothstep(-0.02, 0.02, -tail);
vec3 tailCol = vec3(0.610,0.615,0.568);
vec2 bodyCenter = vec2(xPos, yPos);
vec2 bodySize = vec2(0.4, 0.2);
float bodySDF = length((p - bodyCenter) / bodySize) - 1.0;
float bodyMask = smoothstep(-0.05, 0.05, -bodySDF);
vec3 bodyCol = vec3(0.734,0.740,0.684);
vec2 eyeCenter = bodyCenter + vec2(-0.3, 0.05);
float eyeRadius = 0.03;
float eyeDist = length(p - eyeCenter);
vec3 eyeCol = vec3(0.740,0.022,0.015);
vec3 col = tailCol * tailMask;
col = mix(col, bodyCol, bodyMask);
if(eyeDist < eyeRadius){
col = eyeCol;
}
vec3 bg = vec3(0.234,0.351,0.530);
float alpha = max(tailMask, bodyMask);
colour_out = vec4(mix(bg, col, alpha), 1.0);
}
white by Asternoda
uniform vec2 u_resolution;
uniform vec2 u_mouse;
uniform float u_time;
// WebGL Noise functions (Simplex noise)
vec3 mod289(vec3 x) { return x - floor(x * (1.0 / 289.0)) * 289.0; }
vec4 mod289(vec4 x) { return x - floor(x * (1.0 / 289.0)) * 289.0; }
vec4 permute(vec4 x) { return mod289(((x*34.0)+1.0)*x); }
vec4 taylorInvSqrt(vec4 r) { return 1.79284291400159 - 0.85373472095314 * r; }
float snoise(vec3 v) {
const vec2 C = vec2(1.0/6.0, 1.0/3.0);
const vec4 D = vec4(0.0, 0.5, 1.0, 2.0);
vec3 i = floor(v + dot(v, C.yyy));
vec3 x0 = v - i + dot(i, C.xxx);
vec3 g = step(x0.yzx, x0.xyz);
vec3 l = 1.0 - g;
vec3 i1 = min(g.xyz, l.zxy);
vec3 i2 = max(g.xyz, l.zxy);
vec3 x1 = x0 - i1 + C.xxx;
vec3 x2 = x0 - i2 + C.yyy;
vec3 x3 = x0 - D.yyy;
i = mod289(i);
vec4 p = permute(permute(permute(
i.z + vec4(0.0, i1.z, i2.z, 1.0))
+ i.y + vec4(0.0, i1.y, i2.y, 1.0))
+ i.x + vec4(0.0, i1.x, i2.x, 1.0));
float n_ = 0.142857142857;
vec3 ns = n_ * D.wyz - D.xzx;
vec4 j = p - 49.0 * floor(p * ns.z * ns.z);
vec4 x_ = floor(j * ns.z);
vec4 y_ = floor(j - 7.0 * x_);
vec4 x = x_ *ns.x + ns.yyyy;
vec4 y = y_ *ns.x + ns.yyyy;
vec4 h = 1.0 - abs(x) - abs(y);
vec4 b0 = vec4(x.xy, y.xy);
vec4 b1 = vec4(x.zw, y.zw);
vec4 s0 = floor(b0)*2.0 + 1.0;
vec4 s1 = floor(b1)*2.0 + 1.0;
vec4 sh = -step(h, vec4(0.0));
vec4 a0 = b0.xzyw + s0.xzyw*sh.xxyy;
vec4 a1 = b1.xzyw + s1.xzyw*sh.zzww;
vec3 p0 = vec3(a0.xy, h.x);
vec3 p1 = vec3(a0.zw, h.y);
vec3 p2 = vec3(a1.xy, h.z);
vec3 p3 = vec3(a1.zw, h.w);
vec4 norm = taylorInvSqrt(vec4(dot(p0,p0), dot(p1,p1), dot(p2,p2), dot(p3,p3)));
p0 *= norm.x;
p1 *= norm.y;
p2 *= norm.z;
p3 *= norm.w;
vec4 m = max(0.6 - vec4(dot(x0,x0), dot(x1,x1), dot(x2,x2), dot(x3,x3)), 0.0);
m = m * m;
return 42.0 * dot(m*m, vec4(dot(p0,x0), dot(p1,x1), dot(p2,x2), dot(p3,x3)));
}
// Fractal Brownian Motion for organic texture
float fbm(vec3 p) {
float value = 0.0;
float amplitude = 0.5;
float frequency = 1.0;
//for(int i = 0; i < 5; i++) {
for(int i = 0; i < 3; i++) {
value += amplitude * snoise(p * frequency);
frequency *= 2.0;
amplitude *= 0.5;
}
return value;
}
// Smooth minimum for blending shapes
float smin(float a, float b, float k) {
float h = clamp(0.5 + 0.5*(b-a)/k, 0.0, 1.0);
return mix(b, a, h) - k*h*(1.0-h);
}
// Betta body shape using SDF
float sdEllipse(vec2 p, vec2 ab) {
p = abs(p);
if(p.x > p.y) { p = p.yx; ab = ab.yx; }
float l = ab.y*ab.y - ab.x*ab.x;
float m = ab.x*p.x/l;
float n = ab.y*p.y/l;
float m2 = m*m;
float n2 = n*n;
float c = (m2+n2-1.0)/3.0;
float c3 = c*c*c;
float q = c3 + m2*n2*2.0;
float d = c3 + m2*n2;
float g = m + m*n2;
float co;
if(d < 0.0) {
float h = acos(q/c3)/3.0;
float s = cos(h);
float t = sin(h)*sqrt(3.0);
float rx = sqrt(-c*(s + t + 2.0) + m2);
float ry = sqrt(-c*(s - t + 2.0) + m2);
co = (ry+sign(l)*rx+abs(g)/(rx*ry)-m)/2.0;
} else {
float h = 2.0*m*n*sqrt(d);
float s = sign(q+h)*pow(abs(q+h), 1.0/3.0);
float u = sign(q-h)*pow(abs(q-h), 1.0/3.0);
float rx = -s-u-c*4.0+2.0*m2;
float ry = (s-u)*sqrt(3.0);
float rm = sqrt(rx*rx+ry*ry);
co = (ry/sqrt(rm-rx)+2.0*g/rm-m)/2.0;
}
vec2 r = ab * vec2(co, sqrt(1.0-co*co));
return length(r-p) * sign(p.y-r.y);
}
// Flowing fins using animated noise
float fins(vec2 p, float time) {
float angle = atan(p.y, p.x);
float radius = length(p);
// Dorsal fin (top)
vec2 dorsalPos = p - vec2(-0.05, 0.0);
float dorsalAngle = atan(dorsalPos.y, dorsalPos.x);
float dorsalFlow = snoise(vec3(dorsalAngle * 3.0, radius * 5.0, time * 0.5)) * 0.3;
float dorsalFin = smoothstep(0.25, 0.0, abs(dorsalAngle - 1.57)) *
smoothstep(0.5, 0.0, radius - 0.15 - dorsalFlow);
// Ventral fin (bottom)
vec2 ventralPos = p - vec2(-0.05, 0.0);
float ventralAngle = atan(ventralPos.y, ventralPos.x);
float ventralFlow = snoise(vec3(ventralAngle * 3.0, radius * 5.0, time * 0.5 + 1.5)) * 0.3;
float ventralFin = smoothstep(0.25, 0.0, abs(ventralAngle + 1.57)) *
smoothstep(0.5, 0.0, radius - 0.15 - ventralFlow);
// Tail fin (caudal)
vec2 tailPos = p - vec2(-0.25, 0.0);
float tailAngle = atan(tailPos.y, tailPos.x);
float tailRadius = length(tailPos);
float tailFlow = fbm(vec3(tailAngle * 2.0, tailRadius * 3.0, time * 0.3));
float tailSpread = 0.4 + tailFlow * 0.3;
float tailFin = smoothstep(tailSpread, 0.0, abs(tailAngle)) *
smoothstep(0.6, 0.0, tailRadius - tailFlow * 0.2);
// Pectoral fins (side fins)
float pectoralFlow = snoise(vec3(p * 8.0, time * 0.8));
float pectoralFin = smoothstep(0.15, 0.0, length(p - vec2(0.1, 0.12))) *
(0.5 + pectoralFlow * 0.5);
pectoralFin += smoothstep(0.15, 0.0, length(p - vec2(0.1, -0.12))) *
(0.5 + pectoralFlow * 0.5);
return max(max(dorsalFin, ventralFin), max(tailFin, pectoralFin));
}
// Iridescent color palette for Betta
vec3 bettaColor(vec2 p, float noise, float time) {
float angle = atan(p.y, p.x);
float radius = length(p);
// Base colors: deep blue, crimson red, teal, purple
vec3 color1 = vec3(0.1, 0.3, 0.8); // Deep blue
vec3 color2 = vec3(0.8, 0.1, 0.3); // Crimson
vec3 color3 = vec3(0.2, 0.7, 0.7); // Teal
vec3 color4 = vec3(0.6, 0.2, 0.8); // Purple
// Mix colors based on position and noise
float mixFactor = sin(angle * 3.0 + time * 0.5 + noise * 2.0) * 0.5 + 0.5;
vec3 baseColor = mix(
mix(color1, color2, mixFactor),
mix(color3, color4, mixFactor),
sin(radius * 5.0 + time + noise) * 0.5 + 0.5
);
// Iridescent shimmer
float shimmer = fbm(vec3(p * 10.0, time * 0.2));
baseColor += shimmer * 0.3;
return baseColor;
}
// Bubble nest in background
float bubbles(vec2 p, float time) {
float bubble = 0.0;
for(float i = 0.0; i < 20.0; i++) {
vec2 offset = vec2(
snoise(vec3(i * 0.5, time * 0.3, 0.0)) * 0.8,
0.6 + snoise(vec3(i * 0.3, time * 0.2, 1.0)) * 0.3
);
float size = 0.02 + snoise(vec3(i, time * 0.1, 2.0)) * 0.015;
float dist = length(p - offset);
float ring = smoothstep(size, size - 0.005, dist) - smoothstep(size - 0.005, size - 0.01, dist);
bubble += ring * 0.3;
}
return bubble;
}
// Vegetation (plants)
float vegetation(vec2 p, float time) {
float plants = 0.0;
for(float i = 0.0; i < 8.0; i++) {
float xPos = -0.8 + i * 0.2;
vec2 plantPos = p - vec2(xPos, -0.7);
float sway = snoise(vec3(i, time * 0.4, 0.0)) * 0.1;
plantPos.x -= sway * (plantPos.y + 0.7);
float stem = smoothstep(0.01, 0.0, abs(plantPos.x));
stem *= smoothstep(0.5, 0.0, plantPos.y + 0.7);
plants += stem * 0.4;
// Leaves
for(float j = 0.0; j < 4.0; j++) {
float leafY = -0.5 + j * 0.15;
vec2 leafPos = plantPos - vec2(0.0, leafY);
float leafAngle = sin(time * 0.5 + i + j) * 0.5;
float leaf = smoothstep(0.05, 0.0, length(leafPos - vec2(leafAngle * 0.05, 0.0)));
plants += leaf * 0.2;
}
}
return plants;
}
void main() {
vec2 st = gl_FragCoord.xy / u_resolution.xy;
st = st * 2.0 - 1.0;
st.x *= u_resolution.x / u_resolution.y;
float time = u_time * 0.5;
// Position betta fish in center, slightly offset
vec2 fishPos = st - vec2(0.1, 0.0);
// Add gentle swimming motion
float swimMotion = sin(time * 0.8) * 0.05;
fishPos.y -= swimMotion;
fishPos.x += cos(time * 0.6) * 0.03;
// Rotate fish slightly for dynamic pose
float swimAngle = sin(time * 0.5) * 0.1;
float c = cos(swimAngle);
float s = sin(swimAngle);
fishPos = vec2(fishPos.x * c - fishPos.y * s, fishPos.x * s + fishPos.y * c);
// Betta body
float body = 1.0 - smoothstep(0.0, 0.02, sdEllipse(fishPos - vec2(0.05, 0.0), vec2(0.15, 0.08)));
// Head
float head = smoothstep(0.12, 0.0, length(fishPos - vec2(0.2, 0.0)));
body = max(body, head);
// Flowing fins
float finMask = fins(fishPos, time);
// Combine body and fins
float fishMask = max(body, finMask * 0.8);
// Generate procedural noise for texture
float noisePattern = fbm(vec3(fishPos * 8.0, time * 0.2));
// Fish colors
vec3 fishColor = bettaColor(fishPos, noisePattern, time);
// Fin colors (more translucent and vibrant)
vec3 finColor = bettaColor(fishPos, noisePattern + 0.5, time + 1.0);
finColor = mix(finColor, vec3(1.0), finMask * 0.3); // Add translucency
// Background gradient (aquarium water)
vec3 bgColor = mix(
vec3(0.02, 0.1, 0.15), // Dark blue-green
vec3(0.05, 0.15, 0.25), // Lighter blue
st.y * 0.5 + 0.5
);
// Add bubbles
float bubbleMask = bubbles(st, time);
bgColor += vec3(bubbleMask * 0.5);
// Add vegetation
float plantMask = vegetation(st, time);
vec3 plantColor = vec3(0.1, 0.4, 0.2);
bgColor = mix(bgColor, plantColor, plantMask);
// Combine all elements
vec3 finalColor = bgColor;
// Apply fish (body and fins)
finalColor = mix(finalColor, fishColor, body);
finalColor = mix(finalColor, finColor, finMask * (1.0 - body));
// Add subtle eye
float eye = smoothstep(0.02, 0.0, length(fishPos - vec2(0.22, 0.02)));
finalColor = mix(finalColor, vec3(0.0), eye);
// Add eye highlight
float eyeHighlight = smoothstep(0.008, 0.0, length(fishPos - vec2(0.225, 0.025)));
finalColor = mix(finalColor, vec3(1.0), eyeHighlight);
// Subtle water caustics
float caustics = snoise(vec3(st * 5.0, time * 0.3));
caustics = pow(max(caustics, 0.0), 2.0) * 0.15;
finalColor += caustics;
// Output color
colour_out = vec4(finalColor, 1.0);
}
Azure Flame (Betta splendens) by AquaArtist
precision mediump float;
uniform vec2 u_resolution;
uniform float u_time;
in vec2 coord;
//
// Description : Array and textureless GLSL 2D/3D/4D simplex
// noise functions.
// Author : Ian McEwan, Ashima Arts.
// Maintainer : stegu
// Lastmod : 20201014 (stegu)
// License : Copyright (C) 2011 Ashima Arts. All rights reserved.
// Distributed under the MIT License. See LICENSE file.
// https://github.com/ashima/webgl-noise
// https://github.com/stegu/webgl-noise
//
vec3 mod289(vec3 x) {
return x - floor(x * (1.0 / 289.0)) * 289.0;
}
vec4 mod289(vec4 x) {
return x - floor(x * (1.0 / 289.0)) * 289.0;
}
vec4 permute(vec4 x) {
return mod289(((x*34.0)+10.0)*x);
}
vec4 taylorInvSqrt(vec4 r) {
return 1.79284291400159 - 0.85373472095314 * r;
}
float snoise(vec3 v) {
const vec2 C = vec2(1.0/6.0, 1.0/3.0) ;
const vec4 D = vec4(0.0, 0.5, 1.0, 2.0);
// First corner
vec3 i = floor(v + dot(v, C.yyy) );
vec3 x0 = v - i + dot(i, C.xxx) ;
// Other corners
vec3 g = step(x0.yzx, x0.xyz);
vec3 l = 1.0 - g;
vec3 i1 = min( g.xyz, l.zxy );
vec3 i2 = max( g.xyz, l.zxy );
// x0 = x0 - 0.0 + 0.0 * C.xxx;
// x1 = x0 - i1 + 1.0 * C.xxx;
// x2 = x0 - i2 + 2.0 * C.xxx;
// x3 = x0 - 1.0 + 3.0 * C.xxx;
vec3 x1 = x0 - i1 + C.xxx;
vec3 x2 = x0 - i2 + C.yyy; // 2.0*C.x = 1/3 = C.y
vec3 x3 = x0 - D.yyy; // -1.0+3.0*C.x = -0.5 = -D.y
// Permutations
i = mod289(i);
vec4 p = permute( permute( permute(
i.z + vec4(0.0, i1.z, i2.z, 1.0 ))
+ i.y + vec4(0.0, i1.y, i2.y, 1.0 ))
+ i.x + vec4(0.0, i1.x, i2.x, 1.0 ));
// Gradients: 7x7 points over a square, mapped onto an octahedron.
// The ring size 17*17 = 289 is close to a multiple of 49 (49*6 = 294)
float n_ = 0.142857142857; // 1.0/7.0
vec3 ns = n_ * D.wyz - D.xzx;
vec4 j = p - 49.0 * floor(p * ns.z * ns.z); // mod(p,7*7)
vec4 x_ = floor(j * ns.z);
vec4 y_ = floor(j - 7.0 * x_ ); // mod(j,N)
vec4 x = x_ *ns.x + ns.yyyy;
vec4 y = y_ *ns.x + ns.yyyy;
vec4 h = 1.0 - abs(x) - abs(y);
vec4 b0 = vec4( x.xy, y.xy );
vec4 b1 = vec4( x.zw, y.zw );
//vec4 s0 = vec4(lessThan(b0,0.0))*2.0 - 1.0;
//vec4 s1 = vec4(lessThan(b1,0.0))*2.0 - 1.0;
vec4 s0 = floor(b0)*2.0 + 1.0;
vec4 s1 = floor(b1)*2.0 + 1.0;
vec4 sh = -step(h, vec4(0.0));
vec4 a0 = b0.xzyw + s0.xzyw*sh.xxyy ;
vec4 a1 = b1.xzyw + s1.xzyw*sh.zzww ;
vec3 p0 = vec3(a0.xy,h.x);
vec3 p1 = vec3(a0.zw,h.y);
vec3 p2 = vec3(a1.xy,h.z);
vec3 p3 = vec3(a1.zw,h.w);
//Normalise gradients
vec4 norm = taylorInvSqrt(vec4(dot(p0,p0), dot(p1,p1), dot(p2, p2), dot(p3,p3)));
p0 *= norm.x;
p1 *= norm.y;
p2 *= norm.z;
p3 *= norm.w;
// Mix final noise value
vec4 m = max(0.5 - vec4(dot(x0,x0), dot(x1,x1), dot(x2,x2), dot(x3,x3)), 0.0);
m = m * m;
return 105.0 * dot( m*m, vec4( dot(p0,x0), dot(p1,x1), dot(p2,x2), dot(p3,x3) ) );
}
//★ここから★
// 楕円の距離関数
float ellipse(vec2 coord, vec2 center, vec2 radius) {
vec2 pos = (coord - center) / radius;
return length(pos) - 1.0;
}
float MainBody(vec2 coord) {
//中心(0,0),半径(0.6,0.2)
float d = ellipse(coord, vec2(0.2, 0.0), vec2(0.6, 0.2));
return 1.0 - smoothstep(0.0, 0.2, d);
}
//腹ヒレ(ちび)
float PelvicFin(vec2 coord) {
vec2 fin_center = vec2(0.45, -0.1); // 腹ひれ位置
vec2 pos_orig = coord - fin_center;
float distortion_scale = pow(length(pos_orig) * 3.0, 1.5);
float wave_noise = snoise(vec3(pos_orig * 5.0, u_time * 2.5));
vec2 pos = pos_orig;
pos.x += sin(pos_orig.y * 20.0 + u_time * 4.0) * wave_noise * 0.03 * distortion_scale;
pos.y += sin(pos_orig.x * 20.0 + u_time * 3.5) * wave_noise * 0.02 * distortion_scale;
float fin_x_scale = 0.08; // 横幅
float fin_y_scale = 0.35; // 縦
float d = (pos.x * pos.x) / (fin_x_scale * fin_x_scale) + (pos.y * pos.y) / (fin_y_scale * fin_y_scale);
// 輪郭ノイズ
float noise_edge = snoise(vec3(pos * 30.0, u_time * 0.5)) * 0.05;
float mask = 1.0 - smoothstep(1.0 + noise_edge, 1.05 + noise_edge, d);
return (pos_orig.y < 0.0) ? mask : 0.0;
}
//台形したがわ
float TrapezoidFin(vec2 coord) {
vec2 fin_center = vec2(0.12, -0.1);
vec2 pos_orig = coord - fin_center;
vec2 pos = pos_orig;
float depth = -pos.y;
// 幅
float width = 0.35 + max(0.0, depth) * 0.2;
float fin_len = 0.7; // ヒレの長さ
float dx = abs(pos.x) / width;
float dy = depth / fin_len;
float wave = snoise(vec3(pos.x * 10.0, u_time * 3.0, 0.0)) * 0.05 * smoothstep(0.2, 1.0, dy);
float mask_x = 1.0 - smoothstep(0.8, 1.0, dx);
float mask_y = smoothstep(0.0, 0.1, dy) * (1.0 - smoothstep(0.85 + wave, 1.0 + wave, dy));
float alpha_fade = 1.0 - smoothstep(0.5, 1.0 + wave, dy);
float mask = mask_x * mask_y * alpha_fade;
return (pos_orig.y < 0.05) ? mask : 0.0;
}
//上側
float DorsalFin(vec2 coord) {
vec2 fin_center = vec2(0.0, 0.1);
vec2 pos_orig = coord - fin_center;
vec2 pos = pos_orig;
float height = pos.y;
float width = 0.25 + max(0.0, height) * 0.15;
float fin_len = 0.6; // ヒレの長さ
float dx = abs(pos.x) / width;
float dy = height / fin_len;
float wave = snoise(vec3(pos.x * 10.0, u_time * 3.0, 10.0)) * 0.05 * smoothstep(0.2, 1.0, dy);
float mask_x = 1.0 - smoothstep(0.8, 1.0, dx);
float mask_y = smoothstep(0.0, 0.1, dy) * (1.0 - smoothstep(0.85 + wave, 1.0 + wave, dy));
float alpha_fade = 1.0 - smoothstep(0.5, 1.0 + wave, dy);
float mask = mask_x * mask_y * alpha_fade;
return (pos_orig.y > -0.05) ? mask : 0.0;
}
float tail(vec2 coord) {
vec2 tail_center = vec2(-0.2, -0.010);
vec2 pos_orig = coord - tail_center;
float distortion_scale = pow(length(pos_orig) * 1.5, 1.5);
float wave_noise = snoise(vec3(pos_orig * 4.0, u_time * 3.0));
vec2 pos = pos_orig;
pos.x -= sin(pos_orig.y * 15.0 + u_time * 5.0) * wave_noise * 0.05 * distortion_scale;
pos.y += sin(pos_orig.x * 15.0 + u_time * 4.0) * wave_noise * 0.03 * distortion_scale;
float r = length(pos);
float angle = atan(pos.y, -pos.x);
float min_angle = -1.7;
float max_angle = 1.7;
float max_r = 0.8;
float tail_mask = step(min_angle, angle) * step(angle, max_angle);
// 形状と先端ノイズ
float tip_noise = snoise(vec3(pos * 10.0, u_time * 1.0)) * 0.05;
tail_mask *= 1.0 - smoothstep(max_r * 0.5, max_r + tip_noise, r);
tail_mask *= smoothstep(0.0, 0.15, r);
return tail_mask;
}
// 胸ビレ
float PectoralFin(vec2 coord) {
vec2 center = vec2(0.35, 0.0);
vec2 pos = coord - center;
// ヒレアニメーション
float flap_speed = u_time * 5.0; //早さ
float angle_sway = sin(flap_speed) * 0.15; // 振れ幅
float c = cos(angle_sway);
float s = sin(angle_sway);
pos = mat2(c, -s, s, c) * pos;
// 貝殻の形
float r = length(pos);
float a = atan(pos.y, -pos.x);
float angle_mask = step(abs(a), 0.8);
float max_len = 0.25;
float shape_edge = max_len * (0.8 + 0.2 * cos(a * 2.0));
float frill = 0.01 * sin(a * 100.0); // 細かい波
float ridges = 0.04 * sin(a * 20.0); // 大きなうねり
float shape_mask = 1.0 - smoothstep(shape_edge + ridges + frill, shape_edge + ridges + frill + 0.03, r);
return shape_mask * angle_mask;
}
float EyeMask(vec2 coord) {
// 位置
vec2 eye_center = vec2(0.55, 0.05);
float d_pupil = length(coord - (eye_center + vec2(0.01, 0.0)));
float pupil = 1.0 - smoothstep(0.02, 0.025, d_pupil);
float d_high = length(coord - (eye_center + vec2(0.02, 0.015)));
float highlight = 1.0 - smoothstep(0.005, 0.01, d_high);
return clamp(pupil + highlight, 0.0, 1.0);
}
vec3 colorMap(vec2 coord, float mask) {
vec3 base = mix(vec3(0.1, 0.3, 0.8), vec3(0.9, 0.1, 0.2), coord.x + 0.5);
float noise = snoise(vec3(coord * 8.0, u_time * 0.2));
return base + noise * 0.1 * mask;
}
vec3 BodyColor(vec2 coord, float noise) {
vec2 center = vec2(0.2, 0.0);
vec2 pos_rel = coord - center;
vec2 normalized_pos = pos_rel / vec2(0.6, 0.2);
float dist = length(normalized_pos);
float blend_factor = smoothstep(0.0, 1.0, dist);
vec3 inner_color = vec3(0.99, 0.77, 0.99); // 濃いピンク
vec3 outer_color = vec3(1.0, 0.85, 0.98); // ほぼ白に近い薄ピンク
// 距離に基づいて内側から外側へ
vec3 base = mix(inner_color, outer_color, blend_factor);
return base + noise * 0.1;
}
float stripePattern(float v, float scale) {
// sin波を使って縞模様を作る。0.95以上の部分だけを1.0にする(細い線)
return smoothstep(0.95, 1.0, sin(v * scale));
}
// 腹ヒレ (ちっちゃいやつ) の色
vec3 PelvicColor(vec2 coord, float noise) {
// (ピンク -> 少し濃いピンク)
vec3 base = mix(vec3(1.0, 0.85, 0.98), vec3(0.9, 0.6, 0.8), -coord.y * 2.0);
return base * 0.9 + noise * 0.05;
}
// 上下の台形ヒレ
vec3 AnalDorsalColor(vec2 coord, float noise) {
float dist = abs(coord.y); // 中心からの距離
vec3 base = mix(vec3(1.0, 0.85, 0.98), vec3(0.8, 0.6, 0.8), dist * 2.0);
float stripes = stripePattern(coord.x, 140.0);
vec3 stripe_color = vec3(0.9, 0.5, 0.7);
// ベース色にストライプ入れる
vec3 final_color = mix(base, stripe_color, stripes * 0.3);
return final_color + noise * 0.05;
}
// 尾鰭
vec3 TailColor(vec2 coord, float noise) {
vec2 center = vec2(-0.2, -0.010);
vec2 pos = coord - center;
float r = length(pos);
vec3 base = mix(vec3(1.0, 0.85, 0.98), vec3(0.9, 0.6, 0.8), r * 1.5);
float angle = atan(pos.y, -pos.x);
float rays = stripePattern(angle, 70.0);
// ラインの色
vec3 ray_color = vec3(0.9, 0.5, 0.7);
vec3 final_color = mix(base, ray_color, rays * 0.3);
return final_color + noise * 0.05;
}
// --- 胸ビレの色 ---
vec3 PectoralColor(vec2 coord, float noise) {
vec2 center = vec2(0.35, 0.0);
vec2 pos = coord - center;
float r = length(pos);
float angle = atan(pos.y, -pos.x);
float stripe_wave = sin(angle * 60.0);
float ridges = smoothstep(0.8, 0.95, stripe_wave);
vec3 root_color = vec3(1.0, 0.85, 0.98); // 根元のピンク
vec3 tip_color = vec3(0.9, 0.7, 0.9); // 先端(少し濃いor白っぽい)
// 外側に行くほど色が変化
vec3 base = mix(root_color, tip_color, r * 4.5);
// ストライプ色
vec3 stripe_col = vec3(0.95, 0.6, 0.8);
// 合成
vec3 col = mix(base, stripe_col, ridges * 0.3);
return col + noise * 0.03;
}
float lighting(vec2 coord) {
vec3 lightDir = normalize(vec3(0.5, 0.5, 1.0)); // 光の方向
vec3 normal = normalize(vec3(coord.x, coord.y, 1.0)); // 疑似法線
float diff = dot(lightDir, normal);
return 0.6 + 0.4 * max(diff, 0.0); // 明暗
}
void main() {
vec2 coord = gl_FragCoord.xy / u_resolution.xy;
coord = coord * 2.0 - 1.0; // [-1,1]空間
float body_mask = MainBody(coord);
float pelvic_mask = PelvicFin(coord);
float anal_dorsal_mask = TrapezoidFin(coord) + DorsalFin(coord);
float tail_mask = tail(coord);
// ★追加: 胸ビレ
float pectoral_mask = PectoralFin(coord);
float noise = snoise(vec3(coord * 8.0, u_time * 0.2));
float light = lighting(coord);
vec3 body_color_rgb = BodyColor(coord, noise) * light;
vec3 pelvic_color_rgb = PelvicColor(coord, noise) * light;
vec3 anal_dorsal_color_rgb = AnalDorsalColor(coord, noise) * light;
vec3 tail_color_rgb = TailColor(coord, noise) * light;
vec3 pectoral_color_rgb = PectoralColor(coord, noise) * light;
float shadow_strength = 0.1; // 影の強さ
// 上下ヒレ
vec3 color_layer1 = anal_dorsal_color_rgb * clamp(anal_dorsal_mask, 0.0, 1.0);
float alpha_layer1 = clamp(anal_dorsal_mask, 0.0, 1.0);
vec3 current_color = color_layer1;
float current_alpha = alpha_layer1;
// ちちゃいひれ
float shadow_pelvic = 1.0 - (anal_dorsal_mask * shadow_strength);
vec3 color_layer2 = pelvic_color_rgb * clamp(pelvic_mask, 0.0, 1.0) * shadow_pelvic;
float alpha_layer2 = clamp(pelvic_mask, 0.0, 1.0);
current_color = mix(current_color, color_layer2, alpha_layer2);
current_alpha = current_alpha + alpha_layer2 * (1.0 - current_alpha);
// 尾鰭
float lower_fins = clamp(anal_dorsal_mask + pelvic_mask, 0.0, 1.0);
float shadow_tail = 1.0 - (lower_fins * shadow_strength);
vec3 color_layer3 = tail_color_rgb * clamp(tail_mask, 0.0, 1.0) * shadow_tail;
float alpha_layer3 = clamp(tail_mask, 0.0, 1.0);
current_color = mix(current_color, color_layer3, alpha_layer3);
current_alpha = current_alpha + alpha_layer3 * (1.0 - current_alpha);
// からだ
float body_alpha = clamp(body_mask, 0.0, 1.0);
current_color = mix(current_color, body_color_rgb, body_alpha);
current_alpha = current_alpha + body_alpha * (1.0 - current_alpha);
float pectoral_alpha = clamp(pectoral_mask, 0.0, 1.0);
current_color = mix(current_color, pectoral_color_rgb, pectoral_alpha * 0.8);
current_alpha = current_alpha + pectoral_alpha * (1.0 - current_alpha);
float pupil_mask = EyeMask(coord);
vec3 eye_color = vec3(0.05);
// ハイライト
vec2 eye_center = vec2(0.55, 0.05);
float d_high = length(coord - (eye_center + vec2(0.015, 0.015)));
float highlight = 1.0 - smoothstep(0.005, 0.01, d_high);
eye_color = mix(eye_color, vec3(1.0), highlight);
current_color = mix(current_color, eye_color, pupil_mask);
current_alpha = max(current_alpha, pupil_mask);
float final_alpha = (body_mask > 0.001 || pupil_mask > 0.001) ? 1.0 : current_alpha;
colour_out = vec4(current_color, final_alpha);
}
unknown by purichan
uniform vec2 u_resolution;
uniform float u_time;
// ========================
// noise function (noise3D)
// ========================
vec3 mod289(vec3 x) {
return x - floor(x * (1.0 / 289.0)) * 289.0;
}
vec4 mod289(vec4 x) {
return x - floor(x * (1.0 / 289.0)) * 289.0;
}
vec4 permute(vec4 x) {
return mod289(((x*34.0)+10.0)*x);
}
vec4 taylorInvSqrt(vec4 r)
{
return 1.79284291400159 - 0.85373472095314 * r;
}
float snoise(vec3 v)
{
const vec2 C = vec2(1.0/6.0, 1.0/3.0) ;
const vec4 D = vec4(0.0, 0.5, 1.0, 2.0);
// First corner
vec3 i = floor(v + dot(v, C.yyy) );
vec3 x0 = v - i + dot(i, C.xxx) ;
// Other corners
vec3 g = step(x0.yzx, x0.xyz);
vec3 l = 1.0 - g;
vec3 i1 = min( g.xyz, l.zxy );
vec3 i2 = max( g.xyz, l.zxy );
// x0 = x0 - 0.0 + 0.0 * C.xxx;
// x1 = x0 - i1 + 1.0 * C.xxx;
// x2 = x0 - i2 + 2.0 * C.xxx;
// x3 = x0 - 1.0 + 3.0 * C.xxx;
vec3 x1 = x0 - i1 + C.xxx;
vec3 x2 = x0 - i2 + C.yyy; // 2.0*C.x = 1/3 = C.y
vec3 x3 = x0 - D.yyy; // -1.0+3.0*C.x = -0.5 = -D.y
// Permutations
i = mod289(i);
vec4 p = permute( permute( permute(
i.z + vec4(0.0, i1.z, i2.z, 1.0 ))
+ i.y + vec4(0.0, i1.y, i2.y, 1.0 ))
+ i.x + vec4(0.0, i1.x, i2.x, 1.0 ));
// Gradients: 7x7 points over a square, mapped onto an octahedron.
// The ring size 17*17 = 289 is close to a multiple of 49 (49*6 = 294)
float n_ = 0.142857142857; // 1.0/7.0
vec3 ns = n_ * D.wyz - D.xzx;
vec4 j = p - 49.0 * floor(p * ns.z * ns.z); // mod(p,7*7)
vec4 x_ = floor(j * ns.z);
vec4 y_ = floor(j - 7.0 * x_ ); // mod(j,N)
vec4 x = x_ *ns.x + ns.yyyy;
vec4 y = y_ *ns.x + ns.yyyy;
vec4 h = 1.0 - abs(x) - abs(y);
vec4 b0 = vec4( x.xy, y.xy );
vec4 b1 = vec4( x.zw, y.zw );
//vec4 s0 = vec4(lessThan(b0,0.0))*2.0 - 1.0;
//vec4 s1 = vec4(lessThan(b1,0.0))*2.0 - 1.0;
vec4 s0 = floor(b0)*2.0 + 1.0;
vec4 s1 = floor(b1)*2.0 + 1.0;
vec4 sh = -step(h, vec4(0.0));
vec4 a0 = b0.xzyw + s0.xzyw*sh.xxyy ;
vec4 a1 = b1.xzyw + s1.xzyw*sh.zzww ;
vec3 p0 = vec3(a0.xy,h.x);
vec3 p1 = vec3(a0.zw,h.y);
vec3 p2 = vec3(a1.xy,h.z);
vec3 p3 = vec3(a1.zw,h.w);
//Normalise gradients
vec4 norm = taylorInvSqrt(vec4(dot(p0,p0), dot(p1,p1), dot(p2, p2), dot(p3,p3)));
p0 *= norm.x;
p1 *= norm.y;
p2 *= norm.z;
p3 *= norm.w;
// Mix final noise value
vec4 m = max(0.5 - vec4(dot(x0,x0), dot(x1,x1), dot(x2,x2), dot(x3,x3)), 0.0);
m = m * m;
return 105.0 * dot( m*m, vec4( dot(p0,x0), dot(p1,x1),
dot(p2,x2), dot(p3,x3) ) );
}
// eye
float sdCircle( vec2 p, float r )
{
return length(p) - r;
}
// body
float sdVesica(vec2 p, float w, float h)
{
float d = 0.5*(w*w-h*h)/h;
p = abs(p);
vec3 c = (w*p.y<d*(p.x-w)) ? vec3(0.0,w,0.0) : vec3(-d,0.0,d+h);
return length(p-c.yx) - c.z;
}
// unitility rotation function
vec2 rot(vec2 p, float a) {
float s = sin(a), c = cos(a);
return vec2(c*p.x - s*p.y, s*p.x + c*p.y);
}
// fin
float sdPieRight(vec2 p, vec2 c, float r)
{
// wiggle the outline
float n = snoise(vec3(p * 5.0, u_time * 0.0005));
p.x += n * 0.03;
p.y += n * 0.03;
// rotate -π/2
p = rot(p, radians(-90.0));
p.x = abs(p.x);
float l = length(p) - r;
float m = length(p - c * clamp(dot(p, c), 0.0, r));
float d = max(l, m * sign(c.y*p.x - c.x*p.y));
return d;
}
// small fin
float sdPieRight2(vec2 p, vec2 c, float r)
{
// rotate -π/2
p = rot(p, radians(-90.0));
p.x = abs(p.x);
float l = length(p) - r;
float n = snoise(vec3(p * 4.0, u_time * 0.0005)) * 0.05;
l += n;
float m = length(p - c * clamp(dot(p, c), 0.0, r));
float d = max(l, m * sign(c.y*p.x - c.x*p.y));
return d;
}
void main()
{
vec2 uv = gl_FragCoord.xy/u_resolution.xy*2.0 -1.0;
vec3 temp = vec3(uv, 1.0);
mat3 translate = mat3(
1.0, 0.0, 0.0,
0.0, 1.0, 0.0,
sin(u_time * 0.001)/10.0, sin(u_time * 0.001)/15.0, 0.0
);
temp = translate * temp;
vec2 UV = temp.xy;
float eye = sdCircle(vec2(UV.x + 0.495, UV.y - 0.02), 0.02);
float body = sdVesica(UV, 0.6, 0.15);
float fin = sdPieRight(vec2(UV.x, UV.y), vec2(-1.0, 0.2), 0.8);
float sFin = sdPieRight2(vec2(UV.x + 0.3, UV.y + 0.1), vec2(-0.3, 0.5), 0.4);
if (sFin <= 0.0)
{
float rNoise = snoise(vec3(20.0 * UV.x, 2.0 * UV.y, u_time * 0.0005));
rNoise = (rNoise / 2.0) + 0.5;
colour_out = vec4(rNoise + 0.2, 0.5, 0.8, 1.0);
}
else if (eye <= 0.0)
{
colour_out = vec4(0.0, 0.0, 0.0, 1.0);
}
else if (body <= 0.0)
{
float rNoise = snoise(vec3(15.0 * UV.x, 15.0 * UV.y, u_time * 0.0005));
rNoise = (rNoise / 2.0) + 0.5;
colour_out = vec4(rNoise / 3.0, 0.50, 1.0, 1.0);
}
else if (fin <= 0.0)
{
float rNoise = snoise(vec3(8.0 * UV.x, 1.5 * UV.y, u_time * 0.0005));
rNoise = (rNoise / 2.0) + 0.5;
colour_out = vec4(rNoise / 2.0, 0.5, 0.8, 1.0);
}
else
{
colour_out = vec4(0.0, 0.0, 0.3, 1.0);
}
}
Unknown by Rosario
uniform vec2 u_resolution;
uniform vec2 u_mouse;
uniform float u_time;
vec4 blue = vec4(0., .5, 1.,1.0);
vec4 black = vec4(.1, .1, .2,1.0);
vec4 white = vec4(1.);
vec4 bg = vec4(0.8);
float cross2(vec2 v1, vec2 v2) {
return v1.x*v2.y - v1.y*v2.x;
}
// 鱗柄
vec4 Scales (vec2 xy) {
vec2 st = xy;
st.x *= 10.;st.y *= 6.; // raito
st = fract(st);
st *= 4.; // scale
float index = 0.;
index += step(1., mod(st.x,2.));
index += step(1., mod(st.y,2.))*2.;
st = fract(st);
st -= 0.5;
// index
// 0.:BL 1.:BR 2.:TL 3.:TR
if(index == 1. || index == 2.){
st.x = -st.x;
}
float l1=abs(cross2(st,normalize(vec2(1.,1.))));
float l2=length(vec2(0., st.y));
vec3 c = blue.rgb;
if (l1 <= .12 || l2 <= .07){
c = black.rgb;
}
return vec4(c ,1.);
}
float dot2( in vec2 v ) { return dot(v,v); }
// ハート型 p:中心
float sdHeart( in vec2 p )
{
p.x = abs(p.x);
if( p.y+p.x>1.0 )
return sqrt(dot2(p-vec2(0.25,0.75))) - sqrt(2.0)/4.0;
return sqrt(min(dot2(p-vec2(0.00,1.00)),
dot2(p-0.5*max(p.x+p.y,0.0)))) * sign(p.x-p.y);
}
// 楕円 p:中心 ab:半径(縦, 横)
float sdEllipse( in vec2 p, in vec2 ab )
{
p = abs(p); if( p.x > p.y ) {p=p.yx;ab=ab.yx;}
float l = ab.y*ab.y - ab.x*ab.x;
float m = ab.x*p.x/l; float m2 = m*m;
float n = ab.y*p.y/l; float n2 = n*n;
float c = (m2+n2-1.0)/3.0; float c3 = c*c*c;
float q = c3 + m2*n2*2.0;
float d = c3 + m2*n2;
float g = m + m*n2;
float co;
if( d<0.0 )
{
float h = acos(q/c3)/3.0;
float s = cos(h);
float t = sin(h)*sqrt(3.0);
float rx = sqrt( -c*(s + t + 2.0) + m2 );
float ry = sqrt( -c*(s - t + 2.0) + m2 );
co = (ry+sign(l)*rx+abs(g)/(rx*ry)- m)/2.0;
}
else
{
float h = 2.0*m*n*sqrt( d );
float s = sign(q+h)*pow(abs(q+h), 1.0/3.0);
float u = sign(q-h)*pow(abs(q-h), 1.0/3.0);
float rx = -s - u - c*4.0 + 2.0*m2;
float ry = (s - u)*sqrt(3.0);
float rm = sqrt( rx*rx + ry*ry );
co = (ry/sqrt(rm-rx)+2.0*g/rm-m)/2.0;
}
vec2 r = ab * vec2(co, sqrt(1.0-co*co));
return length(r-p) * sign(p.y-r.y);
}
float sdEgg( in vec2 p, in float ra, in float rb )
{
const float k = sqrt(3.0);
p.x = abs(p.x);
float r = ra - rb;
return ((p.y<0.0) ? length(vec2(p.x, p.y )) - r :
(k*(p.x+r)<p.y) ? length(vec2(p.x, p.y-k*r)) :
length(vec2(p.x+r,p.y )) - 2.0*r) - rb;
}
float sdTriangle( in vec2 p, in vec2 p0, in vec2 p1, in vec2 p2 )
{
vec2 e0 = p1-p0, e1 = p2-p1, e2 = p0-p2;
vec2 v0 = p -p0, v1 = p -p1, v2 = p -p2;
vec2 pq0 = v0 - e0*clamp( dot(v0,e0)/dot(e0,e0), 0.0, 1.0 );
vec2 pq1 = v1 - e1*clamp( dot(v1,e1)/dot(e1,e1), 0.0, 1.0 );
vec2 pq2 = v2 - e2*clamp( dot(v2,e2)/dot(e2,e2), 0.0, 1.0 );
float s = sign( e0.x*e2.y - e0.y*e2.x );
vec2 d = min(min(vec2(dot(pq0,pq0), s*(v0.x*e0.y-v0.y*e0.x)),
vec2(dot(pq1,pq1), s*(v1.x*e1.y-v1.y*e1.x))),
vec2(dot(pq2,pq2), s*(v2.x*e2.y-v2.y*e2.x)));
return -sqrt(d.x)*sign(d.y);
}
float sdBox( in vec2 p, in vec2 b )
{
vec2 d = abs(p)-b;
return length(max(d,0.0)) + min(max(d.x,d.y),0.0);
}
mat2 rot2d(float a){
float c = cos(a);
float s = sin(a);
return mat2(c, -s, s, c);
}
// Simplex Noise 2D
vec3 mod289(vec3 x) {
return x - floor(x * (1.0 / 289.0)) * 289.0;
}
vec2 mod289(vec2 x) {
return x - floor(x * (1.0 / 289.0)) * 289.0;
}
vec3 permute(vec3 x) {
return mod289(((x*34.0)+10.0)*x);
}
float snoise(vec2 v)
{
const vec4 C = vec4(0.211324865405187, // (3.0-sqrt(3.0))/6.0
0.366025403784439, // 0.5*(sqrt(3.0)-1.0)
-0.577350269189626, // -1.0 + 2.0 * C.x
0.024390243902439); // 1.0 / 41.0
// First corner
vec2 i = floor(v + dot(v, C.yy) );
vec2 x0 = v - i + dot(i, C.xx);
// Other corners
vec2 i1;
//i1.x = step( x0.y, x0.x ); // x0.x > x0.y ? 1.0 : 0.0
//i1.y = 1.0 - i1.x;
i1 = (x0.x > x0.y) ? vec2(1.0, 0.0) : vec2(0.0, 1.0);
// x0 = x0 - 0.0 + 0.0 * C.xx ;
// x1 = x0 - i1 + 1.0 * C.xx ;
// x2 = x0 - 1.0 + 2.0 * C.xx ;
vec4 x12 = x0.xyxy + C.xxzz;
x12.xy -= i1;
// Permutations
i = mod289(i); // Avoid truncation effects in permutation
vec3 p = permute( permute( i.y + vec3(0.0, i1.y, 1.0 ))
+ i.x + vec3(0.0, i1.x, 1.0 ));
vec3 m = max(0.5 - vec3(dot(x0,x0), dot(x12.xy,x12.xy), dot(x12.zw,x12.zw)), 0.0);
m = m*m ;
m = m*m ;
// Gradients: 41 points uniformly over a line, mapped onto a diamond.
// The ring size 17*17 = 289 is close to a multiple of 41 (41*7 = 287)
vec3 x = 2.0 * fract(p * C.www) - 1.0;
vec3 h = abs(x) - 0.5;
vec3 ox = floor(x + 0.5);
vec3 a0 = x - ox;
// Normalise gradients implicitly by scaling m
// Approximation of: m *= inversesqrt( a0*a0 + h*h );
m *= 1.79284291400159 - 0.85373472095314 * ( a0*a0 + h*h );
// Compute final noise value at P
vec3 g;
g.x = a0.x * x0.x + h.x * x0.y;
g.yz = a0.yz * x12.xz + h.yz * x12.yw;
return 130.0 * dot(m, g);
}
void main() {
float pi = 3.14159265359;
vec2 st = gl_FragCoord.xy/u_resolution.xy;
float mask = 1.0;
// 目
if (0. > sdEllipse(st.xy - vec2(0.25,0.55), vec2(0.04 ,0.05))){
if (0. > sdEllipse(st.xy - vec2(0.23,0.535) - (u_mouse * 0.0002 ), vec2(0.023 ,0.03))){
colour_out = black;
return;
}
colour_out = white;
return;
}
// 胴
if (0. > sdBox((st.xy - vec2(0.5,0.5))*rot2d(-pi/28.), vec2(0.2,0.11))){
colour_out =Scales(st);
return;
}
// 頭
if (0. > sdEllipse(st.xy - vec2(0.38,0.51), vec2(0.25 ,0.135))){
colour_out =Scales(st);
return;
}
// 尾びれ
float tail = sdHeart((st.xy - vec2(1.,0.5))*rot2d(-pi/2.) /.3);
if (tail < 0.)
{
if (tail > -.02) {
colour_out = black;
return;
}
vec2 center =
vec2(.68,0.5);
//u_mouse * 0.005;
vec2 pos = st.xy - center;
// 極座標に変換
float r = length(pos);
float a = atan(pos.y, pos.x);
float curveStrength =8.;
a = a + (st.y - 0.5) * curveStrength;
vec2 noiseUV = vec2(r * .2, a * 6.0);
float sn = snoise(noiseUV);
float pattern = step(0.3, sn);
colour_out = mix(blue, black, pattern);
return;
}
// 背びれ
if (0. >
sdEgg(
(st.xy - vec2(0.565,0.7))
*rot2d(pi/4.),
0.1, 0.02
)
)
{
vec2 noiseUV = st; noiseUV.x *= 0.4;
float scale = 30.;
float sn = snoise(noiseUV * scale);
float pattern = step(0.3, sn);
colour_out = mix(blue, black, pattern);
return;
}
// 腹びれ
if (0. >
sdEgg(
(st.xy - vec2(0.55,0.3))
* rot2d(-pi/4.),
0.1, 0.02
)
) {
vec2 noiseUV = st; noiseUV.y *= 0.01;
float scale = 80.;
float sn = snoise(noiseUV * scale);
float pattern = step(0.18, sn);
colour_out = mix(blue, black, pattern);
return;
}
// 胸鰭
if (0. >
sdTriangle(
st.xy,
vec2(0.37,0.37),
vec2(0.4,0.1),
vec2(0.3,0.4)
)
) {
vec2 noiseUV = st; noiseUV.y *= 0.5;
float scale = 7.;
float sn = snoise(noiseUV * scale);
float pattern = step(0.5, sn);
colour_out = mix(black, blue, pattern);
return;
}
if (0. >
sdTriangle(
st.xy,
vec2(0.37,0.37),
vec2(0.3,0.1),
vec2(0.3,0.4))
){
colour_out = black;
return;
}
colour_out = bg;
}
gyorogyoro by triw
uniform vec2 u_resolution;
uniform float u_time;
uniform vec2 u_mouse;
// ============================================================
// Noise Functions
// ============================================================
//
// Description : Array and textureless GLSL 2D simplex noise function.
// Author : Ian McEwan, Ashima Arts.
// Maintainer : stegu
// Lastmod : 20110822 (ijm)
// License : Copyright (C) 2011 Ashima Arts. All rights reserved.
// Distributed under the MIT License. See LICENSE file.
// https://github.com/ashima/webgl-noise
// https://github.com/stegu/webgl-noise
//
vec3 mod289(vec3 x) {
return x - floor(x * (1.0 / 289.0)) * 289.0;
}
vec2 mod289(vec2 x) {
return x - floor(x * (1.0 / 289.0)) * 289.0;
}
vec3 permute(vec3 x) {
return mod289(((x*34.0)+10.0)*x);
}
float snoise(vec2 v)
{
const vec4 C = vec4(0.211324865405187, // (3.0-sqrt(3.0))/6.0
0.366025403784439, // 0.5*(sqrt(3.0)-1.0)
-0.577350269189626, // -1.0 + 2.0 * C.x
0.024390243902439); // 1.0 / 41.0
// First corner
vec2 i = floor(v + dot(v, C.yy) );
vec2 x0 = v - i + dot(i, C.xx);
// Other corners
vec2 i1;
//i1.x = step( x0.y, x0.x ); // x0.x > x0.y ? 1.0 : 0.0
//i1.y = 1.0 - i1.x;
i1 = (x0.x > x0.y) ? vec2(1.0, 0.0) : vec2(0.0, 1.0);
// x0 = x0 - 0.0 + 0.0 * C.xx ;
// x1 = x0 - i1 + 1.0 * C.xx ;
// x2 = x0 - 1.0 + 2.0 * C.xx ;
vec4 x12 = x0.xyxy + C.xxzz;
x12.xy -= i1;
// Permutations
i = mod289(i); // Avoid truncation effects in permutation
vec3 p = permute( permute( i.y + vec3(0.0, i1.y, 1.0 ))
+ i.x + vec3(0.0, i1.x, 1.0 ));
vec3 m = max(0.5 - vec3(dot(x0,x0), dot(x12.xy,x12.xy), dot(x12.zw,x12.zw)), 0.0);
m = m*m ;
m = m*m ;
// Gradients: 41 points uniformly over a line, mapped onto a diamond.
// The ring size 17*17 = 289 is close to a multiple of 41 (41*7 = 287)
vec3 x = 2.0 * fract(p * C.www) - 1.0;
vec3 h = abs(x) - 0.5;
vec3 ox = floor(x + 0.5);
vec3 a0 = x - ox;
// Normalise gradients implicitly by scaling m
// Approximation of: m *= inversesqrt( a0*a0 + h*h );
m *= 1.79284291400159 - 0.85373472095314 * ( a0*a0 + h*h );
// Compute final noise value at P
vec3 g;
g.x = a0.x * x0.x + h.x * x0.y;
g.yz = a0.yz * x12.xz + h.yz * x12.yw;
return 130.0 * dot(m, g);
}
// ============================================================
// Noise Functions End
// ============================================================
// ============================================================
// Pattern Functions (新しく追加した模様生成関数)
// ============================================================
// 規則正しい鱗(ウロコ)模様を作る関数
float getScalePattern(vec2 p) {
// 1. スケーリング:模様のサイズ調整
vec2 uv = p * vec2(25.0, 20.0);
// 2. 交互配置:行(y)ごとに、列(x)を0.5個分ずらす
float shift = mod(floor(uv.x), 2.0) * 0.5;
uv.y += shift;
// 3. ローカル座標:各タイルの中の座標(0.0~1.0)を取得
vec2 cell = fract(uv);
// 4. 形状作成:タイルの下辺中央からの距離を測る
float d = distance(cell, vec2(0.5, 0.4));
// 5. 描画:距離に応じて白黒をつける(半円のような形)
// 0.5(半径)より内側なら白っぽく、外側なら黒っぽく
// smoothstepを使って境界を少しぼかし、滑らかに
float scale = smoothstep(0.6, 0.4, d);
// 輪郭を少し強調するために、値を調整
return 0.5 + 0.5 * scale;
}
// ▼ ヒレ用の放射状模様▼
// 引数center:放射の中心点
float getFinPattern(vec2 p, vec2 center) {
// 中心点からのベクトルで角度を計算
vec2 dir = p - center;
float angle = atan(dir.y, dir.x);
// ノイズと縞模様の生成
float n = snoise(vec2(angle * 8.0, 0.0));
float stripe = sin(n * 5.0);
stripe = stripe * 0.5 + 0.5;
return mix(0.5, 1.0, stripe);
}
// ============================================================
// Pattern Functions End
// ============================================================
// 回転行列
vec2 rotate(vec2 p, float angle) {
float s = sin(angle);
float c = cos(angle);
return mat2(c, -s, s, c) * p;
}
float sdCircle(vec2 p,float r){
return length(p)-r;
}
// 基本の楕円
float sdEllipse(vec2 p, vec2 r) {
return length(p / r) - 1.0;
}
// 半分の楕円(下半分をカットして、上半分を残す形状)
// 腹ひれとして使う際、平らな面を胴体に接合させるため
float sdHalfEllipse(vec2 p, vec2 r) {
float d = sdEllipse(p, r);
// -p.y で「yが0より小さい部分(下半分)」を切り落とす
return max(d, -p.y);
}
void main() {
// 座標の正規化
vec2 st = (gl_FragCoord.xy * 2.0 - u_resolution.xy) / min(u_resolution.x, u_resolution.y);
float wave = sin(st.x * 2.0 - u_time * 3.0);
// 魚らしくするため、頭(右側)はあまり揺れず、尾(左側)ほど大きく揺れるように調整
float waveAmp = 0.05 * smoothstep(-0.5, 1.0, 0.4 - st.x);
// Y座標に波の値を足して、空間を歪める
st.y += wave * waveAmp;
// ------------------------------------
// 1. 胴体(Body)
// ------------------------------------
float d_body = sdEllipse(st, vec2(0.5, 0.14));
// ------------------------------------
// 2. 背びれ(Dorsal Fin): 通常の楕円
// ------------------------------------
vec2 p_top = st;
// 位置調整: 胴体の上へ
p_top += vec2(0.15, -0.1);
// 回転: 後ろへ流す
p_top = rotate(p_top, -0.1);
// 描画
float d_top = sdEllipse(p_top, vec2(0.35, 0.15));
// ------------------------------------
// 3. 腹ひれ(Ventral Fin): 半分の楕円
// ------------------------------------
vec2 p_btm = st;
// 位置調整: 胴体の下へ
p_btm += vec2(0.25, -0.05);
// 回転: 後ろへ流す
p_btm = rotate(p_btm, 2.7);
// 描画: 半円関数を使用
// ここでは「上半分」が残る形なので、回転させて平らな面を胴体に向けます
float d_btm = sdHalfEllipse(p_btm, vec2(0.35, 0.3));
// 4. Eye
// 胴体と同じ座標系 st を使い、位置をずらす
vec2 p_eye = st + vec2(-0.35, -0.05); // 右(顔の前の方)へ、少し下へ
float d_eye = sdCircle(p_eye, 0.03); // 半径0.03の小さな円
// ------------------------------------
// 結合 (Union)
// ------------------------------------
// min(A, B) で図形を足し合わせる
float d = min(d_top, d_btm);
d = min(d_body, d);
d = min(d,d_eye);
// 描画
float mask = 1.0 - step(0.0, d);
// 基本の色(元の青系)
vec3 baseColor = vec3(mask * 0.2, mask * 0.4, mask * 1.0);
// ============================================================
// 模様の適用処理
// ============================================================
vec3 finalColor = baseColor;
float pattern = 1.0;
if (mask > 0.5) {
// 「胴体の中にいるか?」を最優先でチェック
if (d_eye <= 0.001) {
finalColor = vec3(0.0); // 黒色
pattern = 0.0; // 模様も適用しない
}
// 胴体の内部(境界線 0.0 より小さい)なら胴体優先
else if (d_body <= 0.001) {
pattern = getScalePattern(st);
pattern *= 0.8;
}
// 胴体じゃなくて、背びれの内部なら背びれ
else if (d_top <= 0.001) {
pattern = getFinPattern(p_top,vec2(0.2, 0.0));
}
// 胴体じゃなくて、腹ひれの内部なら腹ひれ
else if (d_btm <= 0.001) {
pattern = getFinPattern(p_btm,vec2(0.0, 0.0));
}
// 目以外のパーツには模様を適用(目が選ばれたときはpatternが0なので影響なし)
if (d_eye > 0.001) {
finalColor *= pattern;
}
}else{
finalColor = vec3(0.750,0.990,1.000);
}
// ============================================================
colour_out = vec4(finalColor, 1.0);
}
seasky by Ayutaya
uniform vec2 u_resolution;
uniform float u_time;
vec2 rotate2D(vec2 p, float angle){
float c = cos(angle);
float s = sin(angle);
mat2 rot = mat2(c, s, -s, c);
return rot*p;
}
float sdCircle( vec2 p, float r ) {
return length(p) - r;
}
float sdUnion( float d1, float d2 ) {
return min( d1, d2 );
}
float sdSector(vec2 p, float r, float angle){
float l = length(p);
float a = atan(p.y, p.x);
float dist_angle = abs(a) - angle;
float dist_edge = l* sin(dist_angle);
float d_circle = l-r;
if (dist_angle <=0.0){
return d_circle;
}
float dist_rad = l * cos(dist_angle) - r;
return length(max(vec2(dist_rad, dist_edge), 0.0)) + min( max(dist_rad, dist_edge), 0.0);
}
float betta_body_sdf( vec2 p ) {
float angle = -0.35;
vec2 p_body =rotate2D(p, angle);
float s = 1.0 + 0.01 * sin(u_time*3.0 + p_body.x);
p_body.y *= 2.5+ 0.1 * sin(u_time*3.0 + p_body.y);
return sdCircle( p_body + vec2(0.35, -0.25), 0.6*s) ;
}
float betta_fin_sdf1(vec2 p){
vec2 fin_center = vec2(-0.05, 0.21);
vec2 p_fin = p- fin_center;
float wave_distort1 = sin(p.y * 10.0 + u_time * 2.0)* 0.03* length(p_fin);
p_fin.x += wave_distort1;
float fin_radius = 1.0;
float fin_half_angle = 3.14 * 0.3;
p_fin = rotate2D(p_fin,0.2);
return sdSector(p_fin, fin_radius, fin_half_angle);
}
float betta_fin_sdf2(vec2 p){
vec2 fin_up = vec2(0.29, 0.2);
vec2 p_fin3 = p - fin_up;
float wave_distort3 = sin(p.y * 6.0 + u_time * 3.0)* 0.0 * length(p_fin3);
p_fin3.x += wave_distort3;
p_fin3= rotate2D(p_fin3, -2.8);
float fin_half_angle3 = 3.14 * 0.5;
float fin_radius3 = 0.54;
return sdSector(p_fin3, fin_radius3, fin_half_angle3);
}
float betta_fin_sdf(vec2 p){
vec2 fin_down = vec2(-0.09, 0.02);
vec2 p_fin2 = p - fin_down;
float wave_distort2 = cos(p.x * 8.0 + u_time * 3.0) * 0.04 * length(p_fin2);
p_fin2.y += wave_distort2;
p_fin2= rotate2D(p_fin2, 0.9);
float fin_half_angle2 = 3.14 * 0.3;
float fin_radius2 = 0.7;
float d2_fin = sdSector(p_fin2, fin_radius2, fin_half_angle2);
vec2 fin_down2 = vec2(-0.3, -0.2);
vec2 p_fin4 = p - fin_down2;
float wave_distort4 = sin(p.y * 3.0 + u_time * 2.0)*0.03 * length(p_fin4);
p_fin4 += wave_distort4;
p_fin4= rotate2D(p_fin4, 1.2);
float fin_half_angle4 = 3.14 * 0.3;
float fin_radius4 = 0.25;
float d4_fin = sdSector(p_fin4, fin_radius4, fin_half_angle4);
return sdUnion(d4_fin, d2_fin);
}
float small_fin_sdf(vec2 p){
vec2 small_fin = p - vec2(-0.6, -0.25);
float wave_distort_s= sin(p.y * 3.0 + u_time * 9.0)*0.05 * length(small_fin);
small_fin += wave_distort_s;
return sdSector(small_fin, 0.2, 3.14*0.3);
}
float eye_sdf(vec2 p){
return sdCircle(p - vec2(-0.75, -0.2), 0.02);
}
//
// Description : Array and textureless GLSL 2D simplex noise function.
// Author : Ian McEwan, Ashima Arts.
// Maintainer : stegu
// Lastmod : 20110822 (ijm)
// License : Copyright (C) 2011 Ashima Arts. All rights reserved.
// Distributed under the MIT License. See LICENSE file.
// https://github.com/ashima/webgl-noise
// https://github.com/stegu/webgl-noise
//
vec3 mod289(vec3 x) {
return x - floor(x * (1.0 / 289.0)) * 289.0;
}
vec2 mod289(vec2 x) {
return x - floor(x * (1.0 / 289.0)) * 289.0;
}
vec3 permute(vec3 x) {
return mod289(((x*34.0)+10.0)*x);
}
float snoise(vec2 v)
{
const vec4 C = vec4(0.211324865405187, // (3.0-sqrt(3.0))/6.0
0.366025403784439, // 0.5*(sqrt(3.0)-1.0)
-0.577350269189626, // -1.0 + 2.0 * C.x
0.024390243902439); // 1.0 / 41.0
// First corner
vec2 i = floor(v + dot(v, C.yy) );
vec2 x0 = v - i + dot(i, C.xx);
// Other corners
vec2 i1;
//i1.x = step( x0.y, x0.x ); // x0.x > x0.y ? 1.0 : 0.0
//i1.y = 1.0 - i1.x;
i1 = (x0.x > x0.y) ? vec2(1.0, 0.0) : vec2(0.0, 1.0);
// x0 = x0 - 0.0 + 0.0 * C.xx ;
// x1 = x0 - i1 + 1.0 * C.xx ;
// x2 = x0 - 1.0 + 2.0 * C.xx ;
vec4 x12 = x0.xyxy + C.xxzz;
x12.xy -= i1;
// Permutations
i = mod289(i); // Avoid truncation effects in permutation
vec3 p = permute( permute( i.y + vec3(0.0, i1.y, 1.0 ))
+ i.x + vec3(0.0, i1.x, 1.0 ));
vec3 m = max(0.5 - vec3(dot(x0,x0), dot(x12.xy,x12.xy), dot(x12.zw,x12.zw)), 0.0);
m = m*m ;
m = m*m ;
// Gradients: 41 points uniformly over a line, mapped onto a diamond.
// The ring size 17*17 = 289 is close to a multiple of 41 (41*7 = 287)
vec3 x = 2.0 * fract(p * C.www) - 1.0;
vec3 h = abs(x) - 0.5;
vec3 ox = floor(x + 0.5);
vec3 a0 = x - ox;
// Normalise gradients implicitly by scaling m
// Approximation of: m *= inversesqrt( a0*a0 + h*h );
m *= 1.79284291400159 - 0.85373472095314 * ( a0*a0 + h*h );
// Compute final noise value at P
vec3 g;
g.x = a0.x * x0.x + h.x * x0.y;
g.yz = a0.yz * x12.xz + h.yz * x12.yw;
return 130.0 * dot(m, g);
}
//
// Description : Array and textureless GLSL 2D/3D/4D simplex
// noise functions.
// Author : Ian McEwan, Ashima Arts.
// Maintainer : stegu
// Lastmod : 20201014 (stegu)
// License : Copyright (C) 2011 Ashima Arts. All rights reserved.
// Distributed under the MIT License. See LICENSE file.
// https://github.com/ashima/webgl-noise
// https://github.com/stegu/webgl-noise
//
vec4 mod289(vec4 x) {
return x - floor(x * (1.0 / 289.0)) * 289.0;
}
vec4 permute(vec4 x) {
return mod289(((x*34.0)+10.0)*x);
}
vec4 taylorInvSqrt(vec4 r)
{
return 1.79284291400159 - 0.85373472095314 * r;
}
float snoise(vec3 v)
{
const vec2 C = vec2(1.0/6.0, 1.0/3.0) ;
const vec4 D = vec4(0.0, 0.5, 1.0, 2.0);
// First corner
vec3 i = floor(v + dot(v, C.yyy) );
vec3 x0 = v - i + dot(i, C.xxx) ;
// Other corners
vec3 g = step(x0.yzx, x0.xyz);
vec3 l = 1.0 - g;
vec3 i1 = min( g.xyz, l.zxy );
vec3 i2 = max( g.xyz, l.zxy );
// x0 = x0 - 0.0 + 0.0 * C.xxx;
// x1 = x0 - i1 + 1.0 * C.xxx;
// x2 = x0 - i2 + 2.0 * C.xxx;
// x3 = x0 - 1.0 + 3.0 * C.xxx;
vec3 x1 = x0 - i1 + C.xxx;
vec3 x2 = x0 - i2 + C.yyy; // 2.0*C.x = 1/3 = C.y
vec3 x3 = x0 - D.yyy; // -1.0+3.0*C.x = -0.5 = -D.y
// Permutations
i = mod289(i);
vec4 p = permute( permute( permute(
i.z + vec4(0.0, i1.z, i2.z, 1.0 ))
+ i.y + vec4(0.0, i1.y, i2.y, 1.0 ))
+ i.x + vec4(0.0, i1.x, i2.x, 1.0 ));
// Gradients: 7x7 points over a square, mapped onto an octahedron.
// The ring size 17*17 = 289 is close to a multiple of 49 (49*6 = 294)
float n_ = 0.142857142857; // 1.0/7.0
vec3 ns = n_ * D.wyz - D.xzx;
vec4 j = p - 49.0 * floor(p * ns.z * ns.z); // mod(p,7*7)
vec4 x_ = floor(j * ns.z);
vec4 y_ = floor(j - 7.0 * x_ ); // mod(j,N)
vec4 x = x_ *ns.x + ns.yyyy;
vec4 y = y_ *ns.x + ns.yyyy;
vec4 h = 1.0 - abs(x) - abs(y);
vec4 b0 = vec4( x.xy, y.xy );
vec4 b1 = vec4( x.zw, y.zw );
//vec4 s0 = vec4(lessThan(b0,0.0))*2.0 - 1.0;
//vec4 s1 = vec4(lessThan(b1,0.0))*2.0 - 1.0;
vec4 s0 = floor(b0)*2.0 + 1.0;
vec4 s1 = floor(b1)*2.0 + 1.0;
vec4 sh = -step(h, vec4(0.0));
vec4 a0 = b0.xzyw + s0.xzyw*sh.xxyy ;
vec4 a1 = b1.xzyw + s1.xzyw*sh.zzww ;
vec3 p0 = vec3(a0.xy,h.x);
vec3 p1 = vec3(a0.zw,h.y);
vec3 p2 = vec3(a1.xy,h.z);
vec3 p3 = vec3(a1.zw,h.w);
//Normalise gradients
vec4 norm = taylorInvSqrt(vec4(dot(p0,p0), dot(p1,p1), dot(p2, p2), dot(p3,p3)));
p0 *= norm.x;
p1 *= norm.y;
p2 *= norm.z;
p3 *= norm.w;
// Mix final noise value
vec4 m = max(0.5 - vec4(dot(x0,x0), dot(x1,x1), dot(x2,x2), dot(x3,x3)), 0.0);
m = m * m;
return 105.0 * dot( m*m, vec4( dot(p0,x0), dot(p1,x1),
dot(p2,x2), dot(p3,x3) ) );
}
// Cellular noise ("Worley noise") in 2D in GLSL.
// Copyright (c) Stefan Gustavson 2011-04-19. All rights reserved.
// This code is released under the conditions of the MIT license.
// See LICENSE file for details.
// https://github.com/stegu/webgl-noise
// Modulo 289 without a division (only multiplications)
// Modulo 7 without a division
vec3 mod7(vec3 x) {
return x - floor(x * (1.0 / 7.0)) * 7.0;
}
// Cellular noise, returning F1 and F2 in a vec2.
// Standard 3x3 search window for good F1 and F2 values
vec2 cellular(vec2 P) {
#define K 0.142857142857 // 1/7
#define Ko 0.428571428571 // 3/7
#define jitter 1.0 // Less gives more regular pattern
vec2 Pi = mod289(floor(P));
vec2 Pf = fract(P);
vec3 oi = vec3(-1.0, 0.0, 1.0);
vec3 of = vec3(-0.5, 0.5, 1.5);
vec3 px = permute(Pi.x + oi);
vec3 p = permute(px.x + Pi.y + oi); // p11, p12, p13
vec3 ox = fract(p*K) - Ko;
vec3 oy = mod7(floor(p*K))*K - Ko;
vec3 dx = Pf.x + 0.5 + jitter*ox;
vec3 dy = Pf.y - of + jitter*oy;
vec3 d1 = dx * dx + dy * dy; // d11, d12 and d13, squared
p = permute(px.y + Pi.y + oi); // p21, p22, p23
ox = fract(p*K) - Ko;
oy = mod7(floor(p*K))*K - Ko;
dx = Pf.x - 0.5 + jitter*ox;
dy = Pf.y - of + jitter*oy;
vec3 d2 = dx * dx + dy * dy; // d21, d22 and d23, squared
p = permute(px.z + Pi.y + oi); // p31, p32, p33
ox = fract(p*K) - Ko;
oy = mod7(floor(p*K))*K - Ko;
dx = Pf.x - 1.5 + jitter*ox;
dy = Pf.y - of + jitter*oy;
vec3 d3 = dx * dx + dy * dy; // d31, d32 and d33, squared
// Sort out the two smallest distances (F1, F2)
vec3 d1a = min(d1, d2);
d2 = max(d1, d2); // Swap to keep candidates for F2
d2 = min(d2, d3); // neither F1 nor F2 are now in d3
d1 = min(d1a, d2); // F1 is now in d1
d2 = max(d1a, d2); // Swap to keep candidates for F2
d1.xy = (d1.x < d1.y) ? d1.xy : d1.yx; // Swap if smaller
d1.xz = (d1.x < d1.z) ? d1.xz : d1.zx; // F1 is in d1.x
d1.yz = min(d1.yz, d2.yz); // F2 is now not in d2.yz
d1.y = min(d1.y, d1.z); // nor in d1.z
d1.y = min(d1.y, d2.x); // F2 is in d1.y, we're done.
return sqrt(d1.xy);
}
float fbm( vec3 p ) {
float f = 0.0, scale;
for (int i=0; i<4; i++) {
scale = pow( pow(2.0, 4.0/3.0), float(i) );
f += snoise( p * scale ) / scale;
}
return f; }
float noise(vec3 v) {
v.xy += vec2( fbm(v), fbm(vec3(v.xy, v.z + 10.)));
return fbm(v) * 0.35 + 0.45;
}
void main() {
vec2 uv = gl_FragCoord.xy/u_resolution.xy;
vec2 p = uv * 2.0 - 1.0;
vec2 noise_coord = gl_FragCoord.xy / 200.0 - vec2(u_time * 0.3, 0.0);
float noise_val = snoise(noise_coord) * 0.5 + 0.5;
vec4 background_color = vec4(vec3(0.4*noise_val, 0.4*noise_val, 0.25)* (1.0 + noise_val * 2.5),1.0);
vec4 color = background_color;
float dist = betta_body_sdf(p);
float dist2 = betta_fin_sdf(p);
float dist2_1 = betta_fin_sdf1(p);
float dist2_2 = betta_fin_sdf2(p);
float dist3 = small_fin_sdf(p);
float dist4 = eye_sdf(p);
if (dist2_2 <= 0.0) {
vec3 uv1 = vec3(0., 0.01*u_time ,length(p)-uv.y*0.8-uv.x*1.);
float noise = noise( uv1 );
vec3 color1 = vec3(cos(length(p))*1.1, abs(sin((u_time*0.8)*0.5+0.5)), sin(length(p))*1.5);
noise = pow( noise, 2. )*7.5;
color = vec4( color1,noise);
}else {
float edge_smooth = fwidth(dist2_2);
float alpha = smoothstep(edge_smooth, 0.0, dist2_2);
vec4 edge_color = vec4(vec3(0.95,0.95,0.95), 1.0);
color = mix(color, edge_color, alpha);
}
if (dist2 <= 0.0) {
vec3 uv1 = vec3(0., 0.01*u_time ,length(p)-uv.y*0.8-uv.x*1.);
float noise = noise( uv1 );
vec3 color1 = vec3(cos(length(p))*1.1, abs(sin((u_time*0.8)*0.5+0.5)), sin(length(p))*1.5);
noise = pow( noise, 2. )*7.5;
color = vec4( color1,noise);
}else {
float edge_smooth = fwidth(dist2);
float alpha = smoothstep(edge_smooth, 0.0, dist2);
vec4 edge_color = vec4(vec3(0.95,0.95, 0.95), 1.0);
color = mix(color, edge_color, alpha);
}
if (dist2_1 <= 0.0) {
vec3 uv1 = vec3(0., 0.01*u_time ,length(p)-uv.y*0.8-uv.x*1.);
float noise = noise( uv1 );
vec3 color1 = vec3(cos(length(p))*1.1, abs(sin((u_time*0.8)*0.5+0.5)), sin(length(p))*1.5);
noise = pow( noise, 2. )*7.5;
color = vec4( color1,noise);
}else {
float edge_smooth = fwidth(dist2_1);
float alpha = smoothstep(edge_smooth, 0.0, dist2_1);
vec4 edge_color = vec4(vec3(0.,0., 0.), 1.0);
color = mix(color, edge_color, alpha);
}
if(dist <= 0.0){
vec2 f = cellular(gl_FragCoord.xy/18.0);
vec2 f1=1.0 - f;
float n =f1.x;
float n2 =f.y;
vec4 color1 = vec4(mix(vec3(1.000,0.133,0.375), vec3(0.175,0.300,1.000), sin(u_time*1.05)*0.5 +0.5),n);
vec4 color2 = vec4(mix(vec3(1.000,0.133,0.375), vec3(0.175,0.300,1.000), sin(u_time*1.05)*0.5 +0.5),n2);
color = mix(color1, color2, sin(u_time*1.4)*0.5+0.5);
}else {
float edge_smooth = fwidth(dist);
float alpha = smoothstep(edge_smooth, 0.0, dist);
vec4 edge_color = vec4(vec3(0.95,0.95,0.95), 1.0);
color = mix(color, edge_color, alpha);
}
if(dist3<=0.0){
float n = snoise( gl_FragCoord.xy / 100.0 );
n = n*0.5 + 0.5;
color = mix(vec4(n, n*0.3, n*0.3,1.0), vec4(n*0.4, n*0.4,n, 1.0), sin(u_time*1.05)*0.5 + 0.5);
}else {
float edge_smooth = fwidth(dist3);
float alpha = smoothstep(edge_smooth, 0.0, dist3);
vec4 edge_color = vec4(vec3(0.95,0.95,0.95), 1.0);
color = mix(color, edge_color, alpha);
}
if(dist4<=0.0){
color= vec4(0.1,0.1,0.1,0.8);
}else {
float edge_smooth = fwidth(dist4);
float alpha = smoothstep(edge_smooth, 0.0, dist4);
vec4 edge_color = vec4(vec3(0.95,0.95,0.95), 1.0);
color = mix(color, edge_color, alpha);
}
colour_out = color;
}
Unknown by Ocean
uniform vec2 u_resolution;
uniform vec2 u_mouse;
uniform float u_time;
float random1( vec2 p ) {
return fract(sin(dot(p,vec2(127.1,311.7)))*43758.5453);
}
float finRayPattern(vec2 uv, float angle) {
float rays = sin(angle * 120.0 + sin(uv.x * 20.0));
float rings = sin(length(uv) * 40.0);
float pattern = smoothstep(0.0, 0.7, rays + rings * 0.2);
return pattern;
}
float sectorDist(vec2 uv) {
vec2 p = uv - vec2(0.0, 0.5);
float d = length(p);
d += (1.0 - step(0.0, p.x)) * 10.0;
return d;
}
void main() {
vec2 st = (gl_FragCoord.xy * 2.0 - u_resolution.xy) / min(u_resolution.x, u_resolution.y);
vec2 pos = st + vec2(0.2, 0.0);
vec2 animPos = pos;
float swim = sin(pos.y * 5.0 - u_time * 3.0) * 0.03;
animPos.x += swim * step(-1.0, pos.x);
// body
float headThinning = smoothstep(-1.3, 0.0, animPos.x);
float bodyThickness = 2.0 + (1.0 - headThinning) * 1.2;
float bodyLen = length((animPos * vec2(0.9, bodyThickness)) + vec2(0.0, -0.080 + 0.1 * sin(animPos.x * 3.0)));
float bodyShape = smoothstep(0.48, 0.45, bodyLen);
// fin
vec2 tailOrigin = animPos - vec2(0.45, 0.0);
float tAngle = atan(tailOrigin.y, tailOrigin.x);
float tDist = length(tailOrigin);
float chestnutCurve = cos(tAngle * 0.8);
float maxTailLen = 0.85 * chestnutCurve;
float tailShape = smoothstep(maxTailLen, maxTailLen - 0.05, tDist);
float fanWidth = 1.2;
tailShape *= smoothstep(fanWidth, fanWidth - 0.2, abs(tAngle));
tailShape *= smoothstep(0.0, 0.2, tDist);
vec2 dorsalPos = animPos - vec2(0.110, 0.190);
dorsalPos.x += dorsalPos.y * 0.5;
float dorsalShape = smoothstep(0.4, 0.35, length(dorsalPos * vec2(1.0, 1.8)));
dorsalShape *= smoothstep(-0.264, 0.1, -dorsalPos.y + 0.1);
dorsalShape *= smoothstep(-0.4, 0.2, dorsalPos.x);
vec2 ventralPos = animPos - vec2(0.130, -0.370);
ventralPos.x -= ventralPos.y * 1.096;
float ventralShape = smoothstep(0.458, 0.024, length(ventralPos * vec2(0.690, -0.600)));
ventralShape *= smoothstep(-0.040, 0.116, ventralPos.y + 0.100);
ventralShape *= smoothstep(0.440, -0.264, ventralPos.x);
float allFins = max(tailShape, max(dorsalShape, ventralShape));
vec3 cBlack = vec3(0.0, 0.0, 0.05);
vec3 cCyan = vec3(0.0, 1.0, 1.0);
vec3 bgColor = vec3(0.02, 0.05, 0.08);
vec2 scaleScale = vec2(18.0, 14.0);
vec2 scaleUV = animPos * scaleScale;
vec2 uv1 = fract(scaleUV);
vec2 id1 = floor(scaleUV);
float dist1 = sectorDist(uv1);
vec2 uv2 = fract(scaleUV + vec2(0.5, 0.5));
vec2 id2 = floor(scaleUV + vec2(0.5, 0.5));
float dist2 = sectorDist(uv2);
float dist = min(dist1, dist2);
float layerMask = step(dist1, dist2);
float rnd = mix(random1(id2), random1(id1), layerMask);
float scaleMask = smoothstep(0.62, 0.58, dist);
float innerMask = smoothstep(0.58, 0.54, dist);
vec3 bodyColor = cBlack;
vec3 scaleColor = mix(cBlack, cCyan, innerMask);
float shimmer = 0.5 + 0.5 * sin(u_time * 4.0 + rnd * 6.28);
scaleColor += cCyan * innerMask * shimmer * 0.7 * smoothstep(0.3, 0.0, dist);
bodyColor = mix(bodyColor, scaleColor, scaleMask);
float tailRays = finRayPattern(tailOrigin, tAngle);
float otherRays = sin(animPos.y * 99.112 + animPos.x * 18.168);
float finalFinTex = mix(otherRays, tailRays, step(-0.99, animPos.x));
vec3 finColor = mix(cBlack, cCyan, finalFinTex * 0.8);
float finAlpha = 0.8 + 0.376 * finalFinTex;
vec3 color = bgColor;
color = mix(color, finColor, allFins * finAlpha);
color = mix(color, bodyColor, bodyShape);
colour_out = vec4(color, 1.0);
}
Unknown by RM
uniform vec2 u_resolution;
uniform vec2 u_mouse;
uniform float u_time;
// worley noise
vec2 cellular(vec2 P)
{
float K = 0.142857142857;
float Ko = 0.428571428571;
vec2 Pi = floor(P);
vec2 Pf = fract(P);
float minDist1 = 8.0;
float minDist2 = 8.0;
for(int j = -1; j <= 1; j++)
for(int i = -1; i <= 1; i++) {
vec2 neighbor = vec2(float(i),float(j));
vec2 point = vec2(
fract(sin(dot(Pi + neighbor, vec2(127.1,311.7))) * 43758.5453),
fract(sin(dot(Pi + neighbor, vec2(269.5,183.3))) * 43758.5453)
);
vec2 diff = neighbor + point - Pf;
float dist = dot(diff, diff);
if (dist < minDist1) {
minDist2 = minDist1;
minDist1 = dist;
} else if (dist < minDist2) {
minDist2 = dist;
}
}
return vec2(minDist1, minDist2);
}
// noise1
vec3 mod289(vec3 x) {
return x - floor(x * (1.0 / 289.0)) * 289.0;
}
vec2 mod289(vec2 x) {
return x - floor(x * (1.0 / 289.0)) * 289.0;
}
vec3 permute(vec3 x) {
return mod289(((x*34.0)+1.0)*x);
}
float snoise(vec2 v){
const vec4 C = vec4(0.211324865405187, 0.366025403784439, -0.577350269189626, 0.024390243902439);
vec2 i = floor(v + dot(v, C.yy) );
vec2 x0 = v - i + dot(i, C.xx);
vec2 i1 = (x0.x > x0.y) ? vec2(1.0, 0.0) : vec2(0.0, 1.0);
vec4 x12 = x0.xyxy + C.xxzz;
x12.xy -= i1;
i = mod289(i);
vec3 p = permute( permute(i.y + vec3(0.0, i1.y, 1.0))
+ i.x + vec3(0.0, i1.x, 1.0) );
float n_ = 0.142857142857;
vec3 ns = n_ * vec3(1.0, 2.0, 3.0) - vec3(0.0, 1.0, 2.0);
vec3 x_ = fract(p * ns.z) * 2.0 - 1.0;
vec3 y_ = abs(x_) - 0.5;
vec3 s_ = floor(x_ + 0.5);
vec3 gx = x_ - s_;
vec3 g0 = vec3(gx.x, y_.x, 0.0);
vec3 g1 = vec3(gx.y, y_.y, 0.0);
vec3 g2 = vec3(gx.z, y_.z, 0.0);
vec3 m = max(0.5 - vec3(dot(x0,x0), dot(x12.xy,x12.xy), dot(x12.zw,x12.zw)), 0.0);
m = m * m;
m = m * m;
vec3 gradDot = vec3(dot(g0, vec3(x0, 0.0)),
dot(g1, vec3(x12.xy, 0.0)),
dot(g2, vec3(x12.zw, 0.0)));
return 150.0 * dot(m, gradDot);
}
// wood fbm noise
float woodfbm(vec2 p) {
float f = 0.0;
float scale;
for(int i = 0; i < 5; i++) {
scale = pow(pow(2.0, 4.0/3.0), float(i));
f += snoise(p * scale) / scale;
}
return f;
}
// fbm noise for fish body
float hash(vec2 p) {
return fract(sin(dot(p, vec2(127.1, 311.7))) * 43758.5453123);
}
float noise(vec2 st) {
vec2 i = floor(st);
vec2 f = fract(st);
float a = hash(i);
float b = hash(i + vec2(1.0, 0.0));
float c = hash(i + vec2(0.0, 1.0));
float d = hash(i + vec2(1.0, 1.0));
vec2 u = f * f * (3.0 - 2.0 * f);
return mix(a, b, u.x) +
(c - a) * u.y * (1.0 - u.x) +
(d - b) * u.x * u.y;
}
float fbm(vec2 st) {
float value = 0.0;
float amplitude = 0.5;
float frequency = 1.0;
for(int i = 0; i < 3; i++) {
value += amplitude * noise(st * frequency);
frequency *= 2.0;
amplitude *= 0.5;
}
return value;
}
float fbmception(vec2 st, float t) {
float n1 = fbm(st + t * 0.05);
float n2 = fbm(st * 3.0 + n1 * 2.0 + t * 0.1);
return (n1 + n2 * 0.75) * 0.7;
}
// sdf for fish body
float ellipseSDF(vec2 p, vec2 center, vec2 radius) {
vec2 diff = (p - center) / radius;
return length(diff) - 1.0;
}
// sdf for tail
float triangleSDF(vec2 p, vec2 p0, vec2 p1, vec2 p2) {
vec2 e0 = p1 - p0;
vec2 e1 = p2 - p1;
vec2 e2 = p0 - p2;
vec2 v0 = p - p0;
vec2 v1 = p - p1;
vec2 v2 = p - p2;
vec2 n0 = vec2(e0.y, -e0.x);
vec2 n1 = vec2(e1.y, -e1.x);
vec2 n2 = vec2(e2.y, -e2.x);
float d0 = dot(n0, v0);
float d1 = dot(n1, v1);
float d2 = dot(n2, v2);
if (d0 >= 0.0 && d1 >= 0.0 && d2 >= 0.0) return -min(min(d0, d1), d2);
float dist0 = length(v0 - e0 * clamp(dot(v0, e0) / dot(e0, e0), 0.0, 1.0));
float dist1 = length(v1 - e1 * clamp(dot(v1, e1) / dot(e1, e1), 0.0, 1.0));
float dist2 = length(v2 - e2 * clamp(dot(v2, e2) / dot(e2, e2), 0.0, 1.0));
return min(min(dist0, dist1), dist2);
}
void main() {
vec2 uv = gl_FragCoord.xy / u_resolution.xy;
vec2 st = (2.0 * gl_FragCoord.xy - u_resolution.xy) / u_resolution.y;
// animate bg
vec2 move = vec2(u_time * -0.02, u_time * -0.04);
vec2 cellPos = (gl_FragCoord.xy + move * 150.0) / 100.0;
vec2 f = cellular(cellPos);
float nCell = f.x;
vec3 lightBlue = vec3(0.6, 0.8, 1.0);
vec3 darkBlue = vec3(0.764,0.904,0.970);
float brightness = 1.0 - smoothstep(0.35, 0.06, sqrt(nCell));
vec3 bgColor = mix(darkBlue, lightBlue, brightness);
float body = ellipseSDF(uv, vec2(0.58,0.500), vec2(0.3, 0.08));
float headCut = length(uv - vec2(0.65, 0.5)) - 0.26;
float bodyWithHead = max(body, headCut);
float bodyMask = smoothstep(0.15, -0.01, bodyWithHead);
// fbm noise pattern for fish body
float fbmNoise = fbmception(st * 8.0, u_time);
vec3 pink = vec3(1.000,0.767,0.738);
vec3 yellow = vec3(1.000,0.989,0.900);
vec3 white = vec3(1.0);
vec3 tint = mix(pink, yellow, fbmNoise);
vec3 bodyColor = mix(white, tint, 0.55 + fbmNoise * 0.3);
float topFin = ellipseSDF(uv, vec2(0.46, 0.6), vec2(0.15, 0.08));
float bottomFin = ellipseSDF(uv, vec2(0.54, 0.4), vec2(0.17, 0.15));
float tail = ellipseSDF(uv, vec2(0.28, 0.48), vec2(0.20, 0.23));
float finsTail = min(min(topFin, bottomFin), tail);
float finsTailMask = smoothstep(0.1, -0.01, finsTail);
// wood fbm noise
vec2 ringCenter = vec2(-0.010,0.000);
float nWood = woodfbm(st + u_time * 0.045);
float radiusFromCenter = length(st - ringCenter) * 150.0;
float woodRings = sin(radiusFromCenter + 12.0 * nWood);
woodRings = smoothstep(0.4, 0.2, woodRings);
vec3 pinkPurple = vec3(0.975,0.857,0.848);
vec3 whiteColor = vec3(1.0);
vec3 finsTailColor = pinkPurple * (1.0 - woodRings) + white * woodRings;
vec3 fishColor = mix(finsTailColor, bodyColor, bodyMask);
float fishShape = min(bodyWithHead, finsTail);
float mask = smoothstep(0.1, 0.0, fishShape);
vec3 color = mix(bgColor, fishColor, mask);
// eye
vec2 eyePos = vec2(0.82, 0.52);
float eyeRadius = 0.02;
float distToEye = distance(uv, eyePos);
float eyeMask = smoothstep(eyeRadius, eyeRadius - 0.02, distToEye);
color = mix(color, vec3(0.255,0.154,0.076), eyeMask * mask);
colour_out = vec4(color, 1.0);
}
Unknown by nn
uniform vec2 u_resolution;
uniform vec2 u_mouse;
uniform float u_time;
mat2 rotate2d(float _angle){
return mat2(cos(_angle),-sin(_angle),
sin(_angle),cos(_angle));
}
float sdBody(vec2 p) {
p *= rotate2d(0.1);
float taper = smoothstep(-0.6, 0.6, p.x);
return length(vec2(p.x * 0.5 + 0.1, p.y / (0.45 + 0.25*taper))) - 0.25;
}
float sdTail(vec2 p) {
vec2 q = p - vec2(-0.25, 0.0);
q *= rotate2d(0.1);
float waveY = sin(q.y * 6.0 - u_time * 3.0) * 0.05;
float waveX = cos(q.y * 5.0 - u_time * 2.0) * 0.02;
q.x -= waveY; q.y -= waveX;
float spread = 1.0 - q.x * 0.4;
float d = length(vec2(q.x * 0.6, q.y * spread)) - 0.5;
float dClip = max(-(q.x + 0.25), q.x - 0.9);
d = max(d, dClip);
d += sin(q.y * 20.0) * 0.005;
return d;
}
float getFinPattern(vec2 p) {
vec2 q = p - vec2(-0.25, 0.0);
q *= rotate2d(0.1);
float angle = atan(q.y, q.x);
float rays = abs(sin(angle * 40.0 + sin(q.x * 10.0)));
return smoothstep(0.0, 0.8, rays);
}
void main() {
vec2 st = (gl_FragCoord.xy * 2.0 - u_resolution.xy) / min(u_resolution.x, u_resolution.y);
st.x *= -1.0;
vec3 bgColor = vec3(0.0, 0.1, 0.15);
vec3 finalColor = bgColor;
vec3 commonRed = vec3(0.8, 0.05, 0.1);
vec3 headWhite = vec3(0.95, 0.92, 0.92);
vec3 bodyBlue = vec3(0.05, 0.2, 0.6);
vec3 finTipBlue = vec3(0.0, 0.3, 0.8);
float dTail = sdTail(st);
vec3 tailCol = vec3(0.0);
float tFinHorizontal = smoothstep(-0.35, -0.05, st.x);
tailCol = mix(commonRed, finTipBlue, tFinHorizontal);
float pattern = getFinPattern(st);
tailCol += pattern * 0.1;
tailCol += vec3(0.0, 0.2, 0.6) * tFinHorizontal * 0.4;
float tailAlpha = 1.0 - smoothstep(-0.01, 0.01, dTail);
finalColor = mix(finalColor, tailCol, tailAlpha);
float dBody = sdBody(st);
vec3 bodyCol = vec3(0.0);
float tHead = smoothstep(0.3, 0.6, st.x);
float tBody = smoothstep(-0.05, -0.35, st.x);
bodyCol = mix(bodyBlue, commonRed, tBody);
bodyCol = mix(bodyCol, headWhite, tHead);
bodyCol += (sin(st.x * 60.0)*sin(st.y * 60.0)) * 0.03;
float light = smoothstep(-0.5, 0.5, st.y + 0.2);
bodyCol *= (0.6 + 0.4 * light);
float highlight = smoothstep(0.95, 1.0, sin(st.y * 20.0 - st.x * 5.0 + u_time));
bodyCol += highlight * 0.1;
float bodyAlpha = 1.0 - smoothstep(-0.01, 0.01, dBody);
finalColor = mix(finalColor, bodyCol, bodyAlpha);
colour_out = vec4(finalColor, 1.0);
}
Unknown by MT
uniform vec2 u_resolution;
uniform vec2 u_mouse;
uniform float u_time;
// ノイズ
float hash(vec2 p) { return fract(1e4 * sin(17.0 * p.x + p.y * 0.1) * (0.1 + abs(sin(p.y * 13.0 + p.x)))); }
float noise(vec2 x) {
vec2 i = floor(x); vec2 f = fract(x);
float a = hash(i); float b = hash(i + vec2(1.0, 0.0));
float c = hash(i + vec2(0.0, 1.0)); float d = hash(i + vec2(1.0, 1.0));
vec2 u = f * f * (3.0 - 2.0 * f);
return mix(a, b, u.x) + (c - a) * u.y * (1.0 - u.x) + (d - b) * u.x * u.y;
}
float fbm(vec2 x) {
float v = 0.0; float a = 0.5; mat2 rot = mat2(cos(0.5), sin(0.5), -sin(0.5), cos(0.50));
for (int i = 0; i < 4; ++i) { v += a * noise(x); x = rot * x * 2.0; a *= 0.5; }
return v;
}
mat2 rotate2d(float angle){ return mat2(cos(angle),-sin(angle), sin(angle),cos(angle)); }
//SDF関数
// 紡錘形
float sdVesica(vec2 p, float r, float d) {
p = abs(p);
float b = sqrt(r*r-d*d);
if ((p.y-b)*d > p.x*b) {
return length(p - vec2(0.0, b));
} else {
return length(p - vec2(-d, 0.0)) - r;
}
}
// 楕円
float sdEllipse(vec2 p, vec2 r) {
float k0 = length(p/r); float k1 = length(p/(r*r)); return k0*(k0-1.0)/k1;
}
// 鱗パターン生成
float scalePattern(vec2 p) {
p.x -= p.y * p.y * 0.8;
vec2 grid = fract(p * vec2(25.0, 18.0)) - 0.5;
return smoothstep(0.4, 0.15, length(grid));
}
void main() {
vec2 st = (gl_FragCoord.xy * 2.0 - u_resolution.xy) / u_resolution.y;
float t = u_time * 0.5;
st.x += 0.1; // 位置調整
// 流線型
vec2 bodyPos = st;
bodyPos *= rotate2d(0.1);
// 紡錘形 SDF
float bodyDist = sdVesica(bodyPos - vec2(-0.15, 0.0), 0.55, 0.22);
float bodyMask = 1.0 - smoothstep(0.0, 0.015, bodyDist);
// 口元の微調整
float mouthMap = smoothstep(0.3, 0.4, bodyPos.x) * smoothstep(0.1, 0.0, abs(bodyPos.y));
bodyMask *= (1.0 - mouthMap * 0.5);
//ヒレの定義
float combinedFinMask = 0.0;
vec2 wavePos = st + vec2(t*0.3, 0.0);
float globalWave = fbm(wavePos*2.0);
//尾びれ
vec2 tailCenter = vec2(0.3, 0.0);
vec2 tailPos = st - tailCenter;
float tailA = atan(tailPos.y, tailPos.x);
float tailLen = length(tailPos);
float tailShape = smoothstep(1.0, 0.0, abs(tailA)) * smoothstep(0.0, 0.8, tailPos.x);
float currentTailLen = 0.6 + globalWave*0.2 + noise(tailPos*8.0)*0.05;
float tailMask = tailShape * (1.0 - smoothstep(currentTailLen, currentTailLen+0.05, tailLen));
combinedFinMask = max(combinedFinMask, tailMask);
//背びれ
vec2 dorsalPos = st - vec2(0.0, 0.15);
dorsalPos *= rotate2d(-0.6);
float dorsalShape = smoothstep(0.0, 0.4, dorsalPos.x) * smoothstep(0.4, 0.0, abs(dorsalPos.y));
float dorsalMask = dorsalShape * smoothstep(0.5 + globalWave*0.2, 0.0, dorsalPos.x);
combinedFinMask = max(combinedFinMask, dorsalMask);
//尻びれ
vec2 analPos = st - vec2(0.1, -0.15);
analPos *= rotate2d(0.3);
float analShape = smoothstep(-0.1, 0.6, analPos.x) * smoothstep(0.5, 0.0, abs(analPos.y + analPos.x*0.3));
float analMask = analShape * smoothstep(0.7 + globalWave*0.3, 0.0, analPos.x);
combinedFinMask = max(combinedFinMask, analMask);
//腹びれ
vec2 pelvicPos = st - vec2(-0.2, -0.1);
pelvicPos *= rotate2d(0.8);
float pelvicMask = (1.0-smoothstep(0.02, 0.05, abs(pelvicPos.y))) * smoothstep(0.0, 0.4, pelvicPos.x) * smoothstep(0.5, 0.4, pelvicPos.x);
combinedFinMask = max(combinedFinMask, pelvicMask);
combinedFinMask *= smoothstep(-0.05, 0.1, bodyDist);
//カラーリングと質
vec3 bgColor = vec3(0.05, 0.06, 0.08);
// ヒレの色
vec3 finColorRed = vec3(0.8, 0.05, 0.1);
vec3 finColorDark = vec3(0.2, 0.0, 0.05);
float rayAngle = atan(st.y, st.x - 0.5);
float rays = sin(rayAngle * 150.0 + fbm(st*10.0)*5.0);
rays = smoothstep(0.2, 0.8, rays);
float finGrad = smoothstep(-0.2, 0.6, st.x);
vec3 finFill = mix(finColorDark, finColorRed, finGrad);
finFill += vec3(1.0, 0.5, 0.3) * rays * 0.5 * finGrad;
// 体の色
vec3 bodyColorBlue = vec3(0.05, 0.3, 0.9);
vec3 bodyColorDark = vec3(0.02, 0.05, 0.2);
float scales = scalePattern(bodyPos * 1.5);
float lighting = smoothstep(0.5, -0.5, bodyPos.y - bodyPos.x*0.3);
vec3 bodyFill = mix(bodyColorDark, bodyColorBlue, lighting);
bodyFill += vec3(0.2, 0.6, 1.0) * scales * lighting;
// 目
vec2 eyePos = bodyPos - vec2(-0.35, 0.03);
float eyeMask = 1.0 - smoothstep(0.03, 0.035, length(eyePos));
float pupilMask = 1.0 - smoothstep(0.012, 0.015, length(eyePos));
vec3 eyeColor = mix(vec3(0.9, 0.7, 0.1), vec3(0.0), pupilMask);
eyeColor += smoothstep(0.01, 0.005, length(eyePos+vec2(-0.005,0.005)));
vec3 color = bgColor;
color = mix(color, finFill, combinedFinMask);
color = mix(color, bodyFill, bodyMask);
color = mix(color, eyeColor, eyeMask*bodyMask);
colour_out = vec4(color, 1.0);
}
Unknown by Earl
uniform vec2 u_resolution;
uniform float u_time;
vec3 mod289(vec3 x) {
return x - floor(x * (1.0 / 289.0)) * 289.0;
}
vec4 mod289(vec4 x) {
return x - floor(x * (1.0 / 289.0)) * 289.0;
}
vec4 permute(vec4 x) {
return mod289(((x*34.0)+10.0)*x);
}
vec4 taylorInvSqrt(vec4 r)
{
return 1.79284291400159 - 0.85373472095314 * r;
}
float snoise(vec3 v)
{
const vec2 C = vec2(1.0/6.0, 1.0/3.0) ;
const vec4 D = vec4(0.0, 0.5, 1.0, 2.0);
// First corner
vec3 i = floor(v + dot(v, C.yyy) );
vec3 x0 = v - i + dot(i, C.xxx) ;
// Other corners
vec3 g = step(x0.yzx, x0.xyz);
vec3 l = 1.0 - g;
vec3 i1 = min( g.xyz, l.zxy );
vec3 i2 = max( g.xyz, l.zxy );
// x0 = x0 - 0.0 + 0.0 * C.xxx;
// x1 = x0 - i1 + 1.0 * C.xxx;
// x2 = x0 - i2 + 2.0 * C.xxx;
// x3 = x0 - 1.0 + 3.0 * C.xxx;
vec3 x1 = x0 - i1 + C.xxx;
vec3 x2 = x0 - i2 + C.yyy; // 2.0*C.x = 1/3 = C.y
vec3 x3 = x0 - D.yyy; // -1.0+3.0*C.x = -0.5 = -D.y
// Permutations
i = mod289(i);
vec4 p = permute( permute( permute(
i.z + vec4(0.0, i1.z, i2.z, 1.0 ))
+ i.y + vec4(0.0, i1.y, i2.y, 1.0 ))
+ i.x + vec4(0.0, i1.x, i2.x, 1.0 ));
// Gradients: 7x7 points over a square, mapped onto an octahedron.
// The ring size 17*17 = 289 is close to a multiple of 49 (49*6 = 294)
float n_ = 0.142857142857; // 1.0/7.0
vec3 ns = n_ * D.wyz - D.xzx;
vec4 j = p - 49.0 * floor(p * ns.z * ns.z); // mod(p,7*7)
vec4 x_ = floor(j * ns.z);
vec4 y_ = floor(j - 7.0 * x_ ); // mod(j,N)
vec4 x = x_ *ns.x + ns.yyyy;
vec4 y = y_ *ns.x + ns.yyyy;
vec4 h = 1.0 - abs(x) - abs(y);
vec4 b0 = vec4( x.xy, y.xy );
vec4 b1 = vec4( x.zw, y.zw );
//vec4 s0 = vec4(lessThan(b0,0.0))*2.0 - 1.0;
//vec4 s1 = vec4(lessThan(b1,0.0))*2.0 - 1.0;
vec4 s0 = floor(b0)*2.0 + 1.0;
vec4 s1 = floor(b1)*2.0 + 1.0;
vec4 sh = -step(h, vec4(0.0));
vec4 a0 = b0.xzyw + s0.xzyw*sh.xxyy ;
vec4 a1 = b1.xzyw + s1.xzyw*sh.zzww ;
vec3 p0 = vec3(a0.xy,h.x);
vec3 p1 = vec3(a0.zw,h.y);
vec3 p2 = vec3(a1.xy,h.z);
vec3 p3 = vec3(a1.zw,h.w);
//Normalise gradients
vec4 norm = taylorInvSqrt(vec4(dot(p0,p0), dot(p1,p1), dot(p2, p2), dot(p3,p3)));
p0 *= norm.x;
p1 *= norm.y;
p2 *= norm.z;
p3 *= norm.w;
// Mix final noise value
vec4 m = max(0.5 - vec4(dot(x0,x0), dot(x1,x1), dot(x2,x2), dot(x3,x3)), 0.0);
m = m * m;
return 105.0 * dot( m*m, vec4( dot(p0,x0), dot(p1,x1),
dot(p2,x2), dot(p3,x3) ) );
}
// FBM: fractal brownian motion
float fbm(vec3 p) {
float f = 0.0;
f += 0.5000 * snoise(p); p *= 2.01;
f += 0.2500 * snoise(p); p *= 2.02;
f += 0.1250 * snoise(p); p *= 2.03;
f += 0.0625 * snoise(p);
return f;
}
float ridge(float h) {
h = abs(h);
return 1.0 - h;
}
// Turbulence noise
float turbulence(vec3 p) {
float t = 0.0;
t += abs(snoise(p));
t += abs(snoise(p * 2.0));
t += abs(snoise(p * 4.0));
t += abs(snoise(p * 8.0));
return t;
}
float circleMask(vec2 p, float r, float blur) {
float d = length(p);
return 1.0 - smoothstep(r - blur, r + blur, d);
}
float ellipseMask(vec2 p, vec2 r, float blur) {
vec2 q = p / r;
float d = length(q);
return 1.0 - smoothstep(1.0 - blur, 1.0 + blur, d);
}
vec3 bettaDesign(vec2 st) {
vec2 bodyP = st - vec2(0.63, 0.50);
float body = ellipseMask(bodyP, vec2(0.33, 0.16), 0.015);
float sway = 0.03 * sin(u_time * 1.2);
vec2 tailCenter = vec2(0.42 + sway, 0.50);
vec2 tailP = st - tailCenter;
float angle = atan(tailP.y, tailP.x);
float radius = length(tailP);
float baseR = 0.45;
float wave1 = sin(angle * 9.0 + u_time * 2.0);
float wave2 = sin(angle * 21.0 + u_time * 3.3);
float wave3 = sin(angle * 37.0 - radius * 12.0 + u_time * 1.7);
float waves = 0.12 * wave1 + 0.10 * wave2 + 0.07 * wave3;
float tailR = baseR + waves;
float tail = 1.0 - smoothstep(tailR - 0.008, tailR + 0.008, radius);
float fishMask = max(tail, body);
float tNoise =
fbm(vec3(tailP * 12.0, u_time * 0.2)) * 0.65 +
ridge(snoise(vec3(tailP * 6.0, u_time * 0.3))) * 0.35;
float dir = abs(sin(angle * 10.0));
float radialMask = smoothstep(0.1, 0.38, radius);
float tailStripe = mix(1.0,
1.0 + tNoise * dir,
radialMask);
float xGrad = smoothstep(0.30, 0.95, st.x);
vec3 baseRed = mix(vec3(1.0, 0.45, 0.25),
vec3(0.7, 0.05, 0.02),
xGrad);
vec3 tailColor = baseRed * tailStripe;
vec3 fishColor = tailColor;
float scaleBand = body * smoothstep(0.52, 0.70, st.x);
float grid = step(0.5,
fract(st.x * 32.0) +
fract(st.y * 24.0));
vec3 scaleColor = mix(vec3(0.95, 0.6, 0.45),
vec3(0.35, 0.55, 1.0),
grid);
fishColor = mix(fishColor, scaleColor, scaleBand * 0.8);
float shade = 0.4 + 0.6 * st.y;
fishColor *= shade;
vec2 eyeP = st - vec2(0.84, 0.55);
float eye = circleMask(eyeP, 0.018, 0.005);
vec3 eyeCol = vec3(0.02, 0.02, 0.05);
fishColor = mix(fishColor, eyeCol, eye);
vec3 finalColor = mix(vec3(0.0), fishColor, fishMask);
return finalColor;
}
void main() {
vec2 st = gl_FragCoord.xy / u_resolution.xy;
vec3 col = bettaDesign(st);
colour_out = vec4(col, 1.0);
}
Betta splendens by bbbleu
uniform vec2 u_resolution;
uniform float u_time;
// === noise2D from stegu ===
vec4 permute(vec4 x){
return mod(((x*34.0)+1.0)*x, 289.0);
}
vec2 fade(vec2 t){
return t*t*t*(t*(t*6.0-15.0)+10.0);
}
float noise(vec2 P){
vec4 Pi = floor(P.xyxy) + vec4(0.0, 0.0, 1.0, 1.0);
vec4 Pf = fract(P.xyxy) - vec4(0.0, 0.0, 1.0, 1.0);
Pi = mod(Pi, 289.0);
vec4 ix = Pi.xzxz;
vec4 iy = Pi.yyww;
vec4 fx = Pf.xzxz;
vec4 fy = Pf.yyww;
vec4 i = permute(permute(ix) + iy);
vec4 gx = fract(i * (1.0 / 41.0)) * 2.0 - 1.0;
vec4 gy = abs(gx) - 0.5;
vec4 tx = floor(gx + 0.5);
gx = gx - tx;
vec2 g00 = vec2(gx.x,gy.x);
vec2 g10 = vec2(gx.y,gy.y);
vec2 g01 = vec2(gx.z,gy.z);
vec2 g11 = vec2(gx.w,gy.w);
vec4 norm = 1.7928429 - 0.8537347 *
vec4(dot(g00,g00), dot(g10,g10),
dot(g01,g01), dot(g11,g11));
g00 *= norm.x;
g10 *= norm.y;
g01 *= norm.z;
g11 *= norm.w;
float n00 = dot(g00, vec2(fx.x,fy.x));
float n10 = dot(g10, vec2(fx.y,fy.y));
float n01 = dot(g01, vec2(fx.z,fy.x));
float n11 = dot(g11, vec2(fx.w,fy.w));
vec2 fade_xy = fade(Pf.xy);
vec2 n_x = mix(vec2(n00,n01), vec2(n10,n11), fade_xy.x);
return mix(n_x.x, n_x.y, fade_xy.y);
}
// Signed distance shapes
float fishSideFin(vec2 p) {
p -= vec2(0.020,-0.120);
float outer = length(p / vec2(0.38, 0.58)) - 0.85;
float indent = length((p - vec2(-0.010,0.090)) / vec2(0.32, 0.50)) - 0.188;
float lobe = length((p - vec2(0.010,0.030)) / vec2(0.42, 0.62)) - 0.126;
return max(outer, min(indent, lobe));
}
float fishBottomFin(vec2 p) {
p -= vec2(-0.020,-0.060);
p.x /= 0.646;
p.y /= 0.328;
float a = atan(p.y, p.x);
float ripple = 0.015 * sin(a * 28.0)
+ 0.008 * sin(a * 52.0);
float upCut = p.y + ripple;
float feather = abs(p.x) - (0.272 + 0.068 * -p.y);
float arc = length(p + vec2(0.180,0.320)) - (0.498 + ripple);
return max(max(upCut, feather), arc);
}
float fishBody(vec2 p) {
float taper = mix(1.0, 0.4, smoothstep(0.4, 0.9, p.x));
vec2 r = vec2(0.22 * taper, 0.10);
float body = length(p / r) - 1.0;
float nose = (p.x - 0.78) * 4.0;
return max(body, nose);
}
float fishTail(vec2 p) {
p -= vec2(-0.200, 0.000);
float a = atan(p.y, p.x);
float ripple = 0.006 * sin(a * 32.0)
+ 0.003 * sin(a * 64.0);
float circle = length(p) - (0.268 + ripple);
float halfMask = p.x;
return max(circle, halfMask);
}
float fishTailInner(vec2 p) {
p -= vec2(-0.200, 0.000);
float a = atan(p.y, p.x);
float ripple = 0.005 * sin(a * 32.0)
+ 0.002 * sin(a * 64.0);
float circle = length(p) - (0.180 + ripple);
float halfMask = p.x;
return max(circle, halfMask);
}
float fishFin(vec2 p) {
p -= vec2(-0.060, 0.060);
float a = radians(20.0);
p = mat2(cos(a),-sin(a), sin(a),cos(a)) * p;
float arc = length(p / vec2(-0.370, -0.980)) - 0.320;
float up = -p.y;
float baseTaper = abs(p.x) - (0.05 + 0.35 * p.y);
float roundTop = length(p - vec2(0.0, 0.15)) - 0.25;
return max(max(arc, up), max(baseTaper, roundTop));
}
float fishEye(vec2 p) {
p -= vec2(0.140,0.010);
return length(p) - 0.030;
}
void main() {
vec2 st = gl_FragCoord.xy / u_resolution.xy;
vec2 p = st - vec2(0.55, 0.5);
// Underwater background
vec3 waterDeep = vec3(0.0, 0.25, 0.55);
vec3 waterShallow = vec3(0.10, 0.52, 0.85);
vec3 base = mix(waterDeep, waterShallow, st.y);
float ripple = noise(st * 6.0 + u_time * 0.15);
ripple += 0.5 * noise(st * 12.0 - u_time * 0.25);
ripple = smoothstep(0.4, 1.0, ripple);
base += ripple * 0.10;
// Aquatic plants
vec3 weedColor = vec3(0.07, 0.45, 0.22);
float plantMaskTotal = 0.0;
for(int i = 0; i < 7; i++){
float anchorX = fract(st.x + float(i) * 0.21);
float sway = sin(u_time * 0.7 + anchorX * 10.0) * 0.03;
float xOff = st.x - anchorX - sway;
float h = 0.55 + 0.1 * sin(u_time + float(i));
float width = 0.04 + 0.02 * sin(st.y * 30.0 + u_time * 0.4);
float leaf = smoothstep(width, 0.0, abs(xOff));
leaf *= smoothstep(0.0, h, st.y);
float branch = smoothstep(0.28, 0.29, st.y + 0.05 * sin(st.y * 40.0 + float(i)*1.7)) * 0.5;
float weedMask = leaf * (0.8 + branch);
plantMaskTotal = max(plantMaskTotal, weedMask);
}
base = mix(base, weedColor, plantMaskTotal);
base = mix(vec3(0.02,0.10,0.18), base, smoothstep(0.05, 0.5, st.y));
// Distance fields
float dBody = fishBody(p);
float dFin = fishFin(p);
float dBottom = fishBottomFin(p);
float dSide = fishSideFin(p);
float dTailOuter = fishTail(p);
float dTailInner = fishTailInner(p);
float dEye = fishEye(p);
float dMain = min(min(min(dBody, dFin), dBottom), dSide);
float fishMask = 1.0 - step(0.0, min(dMain, dTailInner));
float tailMaskOuter = 1.0 - step(0.0, dTailOuter);
float tailMaskInner = 1.0 - step(0.0, dTailInner);
float eyeMask = 1.0 - step(0.0, dEye);
float finMask = 1.0 - step(0.0, dFin);
float bottomMask = 1.0 - step(0.0, dBottom);
float sideMask = 1.0 - step(0.0, dSide);
vec3 fishCol = vec3(1.0, 0.05, 0.05);
vec3 whiteCol = vec3(1.0);
vec3 blackCol = vec3(0.0);
base = mix(base, fishCol, fishMask);
base = mix(base, whiteCol, tailMaskOuter * (1.0 - tailMaskInner));
float a = atan(p.y, p.x);
float rippleMask = 0.015 * sin(a * 28.0)
+ 0.008 * sin(a * 52.0);
float bottomWhiteMask = bottomMask * step(p.y + rippleMask, -0.188);
base = mix(base, whiteCol, bottomWhiteMask);
vec2 pf = p - vec2(-0.060, 0.060);
float ang = radians(20.0);
pf = mat2(cos(ang),-sin(ang), sin(ang),cos(ang)) * pf;
float af = atan(pf.y, pf.x);
float topRipple = 0.025 * sin(af * 21.168)
+ 0.012 * sin(af * 35.720);
float finUpper = step(0.192, pf.y + topRipple);
base = mix(base, whiteCol, finMask * finUpper);
base = mix(base, whiteCol, sideMask);
float bodyMask = 1.0 - step(0.0, dBody);
if (bodyMask > 0.0 && sideMask == 0.0) {
float n1 = noise(p * 65.0 + u_time * 0.4);
float n2 = noise(p * 130.0 + u_time * 0.6);
float ns = (n1 * 0.6 + n2 * 0.4);
ns = clamp(ns * 2.5, -1.0, 1.0);
base.rgb += ns * 0.28;
}
{
float finRayMask = finMask + bottomMask + sideMask + tailMaskOuter;
finRayMask = clamp(finRayMask, 0.0, 1.0);
float angFin = atan(p.y, p.x);
float rays = sin(angFin * 80.0 + u_time * 0.2);
float distFade = smoothstep(0.03, 0.22, length(p));
base.rgb += rays * 0.09 * distFade * finRayMask;
}
base = mix(base, blackCol, eyeMask * fishMask);
colour_out = vec4(base, 1.0);
}
Siamese Fighting Fish by lls
uniform vec2 u_resolution;
uniform vec2 u_mouse;
uniform float u_time;
// random generator
float hash(vec2 p) {
return fract(sin( dot(p, vec2(99.0, 358.0))) * 500.0);
}
//used as noise
float noise(vec2 p) {
vec2 i = floor(p);
vec2 f = fract(p);
float a = hash(i );
float b = hash(i + vec2(1.0, 0.0 ));
float c = hash(i + vec2(0.0, 1.0 ));
float d = hash(i + vec2(1.0, 1.0 ));
vec2 u = f * f *(3.0 - 2.0 * f);
return mix(mix(a, b, u.x), mix(c, d, u.x), u.y);
}
float sdCircle(vec2 p, vec2 c, float r) {
return length(p - c) - r;
}
float sdEllipse(vec2 p, vec2 c, vec2 r) {
vec2 q = (p - c) / r;
return length(q) - 1.0;
}
void main() {
vec2 st = gl_FragCoord.xy / u_resolution.xy;
//aspect ratio
float aspect = u_resolution.x / u_resolution.y;
st.x *= aspect;
//center the fish(added laterr)
float fishCenterCurrent = 0.45;
float fishCenterTarget = 0.5 * aspect;
float fishOffsetX = fishCenterTarget - fishCenterCurrent;
vec2 bodyCenter =vec2(0.45 + fishOffsetX, 0.5);
vec2 headCenter =vec2(0.63 + fishOffsetX, 0.5);
vec2 tailCenter =vec2(0.25 + fishOffsetX, 0.5);
vec2 dorsalCenter =vec2(0.45 + fishOffsetX, 0.62);
vec2 ventralCenter=vec2(0.45 + fishOffsetX, 0.38);
vec2 finRoot =vec2(0.38 + fishOffsetX, 0.5);
vec2 eyeCenter =vec2(0.64 + fishOffsetX, 0.515);
vec2 eyeHighlight =vec2(0.647 + fishOffsetX, 0.522);
//blue background
float waterDepth = st.y;
vec3 bgTop = vec3(0.10, 0.55, 0.95); //light sea blue
vec3 bgBottom = vec3(0.00, 0.05, 0.25); //deep ocean blue
float shaft = noise(vec2(st.x * 4.0, st.y * 3.0 + u_time * 0.1));
shaft = smoothstep(0.7, 1.0, shaft );
vec3 background = mix(bgBottom, bgTop, waterDepth);
background += shaft * vec3(0.10, 0.25, 0.45) * 0.4;
//fish body shape
float body = sdEllipse(st, bodyCenter, vec2(0.23, 0.12));
float head = sdCircle(st, headCenter, 0.06);
float tailWarp = 0.04 * sin(st.y * 10.0 + u_time * 2.0);
float tail = sdEllipse(st + vec2(tailWarp, 0.0), tailCenter, vec2(0.24, 0.18));
float dorsal = sdEllipse(st, dorsalCenter, vec2(0.23, 0.16 ));
float ventral= sdEllipse(st, ventralCenter, vec2(0.23, 0.18));
float bodyOnly= min(body, head);
float finsOnly= min(min(tail,dorsal), ventral);
float fishDist= min(bodyOnly, finsOnly);
float fishMask= smoothstep( 0.02, 0.0, fishDist);
float bodyMask= smoothstep( 0.015, 0.0, bodyOnly);
float finsMask= smoothstep( 0.02, 0.0, finsOnly) * (1.0 - bodyMask);
//local coords
vec2 local = st - bodyCenter;
float r = length(local);
//0= tail side, 1= head side along body
float headToTail = clamp((local.x + 0.22) / 0.44,0.0, 1.0);
//0= belly, 1= dorsal line
float dorsalFactor = clamp((local.y + 0.12) / 0.24,0.0,1.0);
//base gradient: dark blue head/dorsal, red tail/belly
vec3 deepBlue =vec3(0.02, 0.12, 0.60);
vec3 pureRed =vec3(0.95, 0.15, 0.15);
//more blue near head, more red near tail
vec3 baseBody = mix(pureRed, deepBlue, headToTail);
// shift toward red at lower part, blue at upper part
baseBody = mix(baseBody, pureRed, (1.0 - dorsalFactor) * 0.5);
baseBody = mix(baseBody, deepBlue, dorsalFactor * 0.6);
// narrow bright lateral line along body
float lateral = smoothstep(0.02, 0.0, abs(local.y) - 0.01);
lateral *= smoothstep(-0.2, 0.15, local.x); // mostly middle of body
// vetical liness
float verticalBands = 0.5 + 0.5 * cos(local.x * 28.0 + noise(local * 5.0) * 3.0);
verticalBands = smoothstep(0.5, 0.95, verticalBands);
//texture
float longitudinal = 0.5 + 0.5 * sin(local.y * 30.0 + u_time * 1.2);
float bands = mix(verticalBands, longitudinal, 0.35);
// irregular pattern
float nBody = noise(local * 10.0 + vec2(u_time * 0.3, 0.0));
float marble = smoothstep(0.4, 0.9, nBody);
//micro pattern added
float scaleGrid = sin(local.x * 38.0) * sin(local.y * 32.0);
float scales = smoothstep(0.65, 0.95, 0.5 + 0.5 * scaleGrid);
// time-based shimmer along body
float shimmer = 0.5 + 0.5 * sin(r * 16.0 + u_time * 3.0 + local.x * 5.0);
shimmer *= smoothstep(0.01, 0.18, r);
// body color:
vec3 bodyColor = baseBody;
// deepen red/blue contrast via bands and marble
bodyColor = mix(bodyColor, pureRed, bands * 0.3);
bodyColor = mix(bodyColor, deepBlue, (1.0 - bands) * 0.25 * headToTail);
// lateral line highlight
bodyColor += lateral * vec3(0.7, 0.8, 1.0) *0.5;
bodyColor += scales * vec3(0.5, 0.6, 1.0) *0.45;
bodyColor += shimmer * vec3(0.3, 0.3, 0.7) *0.45;
// darken head slightly (many bettas have darker mask)
float headMask = smoothstep(0.05, -0.06, sdCircle(st, headCenter, 0.08));
bodyColor = mix(bodyColor * 0.6, bodyColor, 1.0 - headMask);
//fins
vec2 finLocal = st - finRoot;
float finR = length(finLocal);
//rays radial
float angle = atan(finLocal.y, finLocal.x);
float rays = 0.5 + 0.5 * cos(angle * 16.0 + finR * 14.0 - u_time * 1.5);
float finWaves = 0.5 + 0.5 * sin(finR * 20.0 - u_time * 2.2);
float nFins = noise(finLocal * 8.0 + vec2(u_time * 0.4, -u_time * 0.25));
float finPattern = mix(rays, finWaves, 0.5);
finPattern = mix(finPattern, nFins, 0.35);
finPattern = smoothstep(0.25, 0.95, finPattern);
vec3 finRed = vec3(0.97, 0.20, 0.16);
vec3 finDarkBlue = vec3(0.05, 0.25, 0.80); // strong blue
vec3 finColor = mix(finRed, finDarkBlue, finPattern * 0.6);
float finEdge = smoothstep(0.03, 0.15, abs(fishDist));
finColor += finEdge * 0.22;
//eye
float eye = sdCircle(st, eyeCenter, 0.015 );
float eyeMask = smoothstep(0.01, 0.0, eye);
vec3 eyeColor = vec3( 0.02, 0.02, 0.02);
float highlight = smoothstep(0.008, 0.0, sdCircle(st, eyeHighlight, 0.006));
eyeColor += highlight * vec3(0.9, 0.9, 0.9);
// final colors
vec3 color = background;
vec3 fishColor =bodyColor * bodyMask + finColor * finsMask;
color = mix(color, fishColor, fishMask);
color = mix(color, eyeColor, eyeMask * bodyMask);
vec2 cent = st -vec2(0.5* aspect, 0.5);
float vign = smoothstep(0.9, 0.2, length(cent));
color *= vign;
colour_out= vec4(color,1.0);
}
red and blue betta by anonymous
uniform vec2 u_resolution;
uniform vec2 u_mouse;
uniform float u_time;
// Your environment provides: vec4 colour_out;
// ===== Helpers =====
mat2 rot(float a){
float c = cos(a), s = sin(a);
return mat2(c,-s, s,c);
}
// Smooth-min for blending SDFs
float smin(float a, float b, float k){
float h = clamp(0.5 + 0.5*(b - a)/k, 0.0, 1.0);
return mix(b, a, h) - k*h*(1.0 - h);
}
// ---- Basic shapes ----
// Body ellipse
float sdEllipse(vec2 p, vec2 r){
vec2 k = abs(p);
return (length(k / r) - 1.0) * min(r.x, r.y);
}
// Fin triangle
float sdTri(vec2 p, float h){
p.x = abs(p.x);
vec2 a = vec2(0.0,-h);
vec2 b = vec2(-h,h);
vec2 ba = b-a;
float t = clamp(dot(p-a, ba)/dot(ba,ba), 0.0, 1.0);
vec2 proj = a + t*ba;
return length(p - proj) - 0.05;
}
// Tail fan
float sdTail(vec2 p){
p.x += 0.55;
float r = length(p);
float ang = abs(atan(p.y, p.x));
float fan = radians(70.0);
float cutoff = smoothstep(fan, fan - 0.2, ang);
return r - 0.8 + cutoff*0.1;
}
// ===== Fish shape =====
float mapFish(vec2 p){
vec2 q = p;
// swim motion
q.x += sin(u_time * 2.0 + q.y * 4.0) * 0.04;
float body = sdEllipse((q - vec2(0.05,0.0)) * rot(0.1), vec2(0.55, 0.22));
float tail = sdTail(q);
float topFin = sdTri((q + vec2(-0.1, -0.25))*vec2(1.4,1.0), 0.5);
float botFin = sdTri((q + vec2(-0.1, 0.25))*vec2(1.4,1.0), 0.5);
float fish = body;
fish = smin(fish, tail, 0.25);
fish = smin(fish, topFin, 0.15);
fish = smin(fish, botFin, 0.15);
return fish; // negative = inside fish
}
// ===== Colors =====
// Light blue body + dark edge
vec3 fishColor(vec2 p, float dist){
// Light blue base
vec3 base = vec3(0.3, 0.7, 1.0);
// Dist is negative inside, fade to black near edge
float edge = smoothstep(0.0, 0.08, abs(dist));
// Darken toward edges
vec3 shaded = mix(base, vec3(0.0), edge * 0.8);
// Slight head darkening
float head = smoothstep(-0.5, -0.25, p.x);
shaded *= mix(0.5, 1.0, head);
return shaded;
}
// Water background
vec3 background(vec2 uv){
vec3 top = vec3(0.95, 0.98, 1.0);
vec3 bottom = vec3(0.85, 0.92, 0.98);
return mix(bottom, top, uv.y);
}
void main(){
vec2 uv = gl_FragCoord.xy / u_resolution.xy;
float aspect = u_resolution.x / u_resolution.y;
// Normalized, centered
vec2 p = (gl_FragCoord.xy - 0.5*u_resolution.xy) / u_resolution.y;
p.x *= aspect;
p.y -= 0.05;
p *= 0.8; // fit in frame
float d = mapFish(p); // signed distance
vec3 col = background(uv);
// Inside mask (1 = inside fish)
float inside = 1.0 - smoothstep(0.0, 0.02, d);
// Blend fish into background
col = mix(col, fishColor(p, d), inside);
colour_out = vec4(col, 1.0);
}
unknown by anonymous
uniform vec2 u_resolution;
uniform vec2 u_mouse;
uniform float u_time;
// 2D and 3D noise functions from https://github.com/stegu/webgl-noise
vec2 mod289(vec2 x) {
return x - floor(x * (1.0 / 289.0)) * 289.0;
}
vec3 mod289(vec3 x) {
return x - floor(x * (1.0 / 289.0)) * 289.0;
}
vec4 mod289(vec4 x) {
return x - floor(x * (1.0 / 289.0)) * 289.0;
}
vec3 permute(vec3 x) {
return mod289(((x*34.0)+10.0)*x);
}
vec4 permute(vec4 x) {
return mod289(((x*34.0)+10.0)*x);
}
vec4 taylorInvSqrt(vec4 r)
{
return 1.79284291400159 - 0.85373472095314 * r;
}
float snoise(vec2 v)
{
const vec4 C = vec4(0.211324865405187, // (3.0-sqrt(3.0))/6.0
0.366025403784439, // 0.5*(sqrt(3.0)-1.0)
-0.577350269189626, // -1.0 + 2.0 * C.x
0.024390243902439); // 1.0 / 41.0
// First corner
vec2 i = floor(v + dot(v, C.yy) );
vec2 x0 = v - i + dot(i, C.xx);
// Other corners
vec2 i1;
//i1.x = step( x0.y, x0.x ); // x0.x > x0.y ? 1.0 : 0.0
//i1.y = 1.0 - i1.x;
i1 = (x0.x > x0.y) ? vec2(1.0, 0.0) : vec2(0.0, 1.0);
// x0 = x0 - 0.0 + 0.0 * C.xx ;
// x1 = x0 - i1 + 1.0 * C.xx ;
// x2 = x0 - 1.0 + 2.0 * C.xx ;
vec4 x12 = x0.xyxy + C.xxzz;
x12.xy -= i1;
// Permutations
i = mod289(i); // Avoid truncation effects in permutation
vec3 p = permute( permute( i.y + vec3(0.0, i1.y, 1.0 ))
+ i.x + vec3(0.0, i1.x, 1.0 ));
vec3 m = max(0.5 - vec3(dot(x0,x0), dot(x12.xy,x12.xy), dot(x12.zw,x12.zw)), 0.0);
m = m*m ;
m = m*m ;
// Gradients: 41 points uniformly over a line, mapped onto a diamond.
// The ring size 17*17 = 289 is close to a multiple of 41 (41*7 = 287)
vec3 x = 2.0 * fract(p * C.www) - 1.0;
vec3 h = abs(x) - 0.5;
vec3 ox = floor(x + 0.5);
vec3 a0 = x - ox;
// Normalise gradients implicitly by scaling m
// Approximation of: m *= inversesqrt( a0*a0 + h*h );
m *= 1.79284291400159 - 0.85373472095314 * ( a0*a0 + h*h );
// Compute final noise value at P
vec3 g;
g.x = a0.x * x0.x + h.x * x0.y;
g.yz = a0.yz * x12.xz + h.yz * x12.yw;
return 130.0 * dot(m, g);
}
float snoise(vec3 v)
{
const vec2 C = vec2(1.0/6.0, 1.0/3.0) ;
const vec4 D = vec4(0.0, 0.5, 1.0, 2.0);
// First corner
vec3 i = floor(v + dot(v, C.yyy) );
vec3 x0 = v - i + dot(i, C.xxx) ;
// Other corners
vec3 g = step(x0.yzx, x0.xyz);
vec3 l = 1.0 - g;
vec3 i1 = min( g.xyz, l.zxy );
vec3 i2 = max( g.xyz, l.zxy );
// x0 = x0 - 0.0 + 0.0 * C.xxx;
// x1 = x0 - i1 + 1.0 * C.xxx;
// x2 = x0 - i2 + 2.0 * C.xxx;
// x3 = x0 - 1.0 + 3.0 * C.xxx;
vec3 x1 = x0 - i1 + C.xxx;
vec3 x2 = x0 - i2 + C.yyy; // 2.0*C.x = 1/3 = C.y
vec3 x3 = x0 - D.yyy; // -1.0+3.0*C.x = -0.5 = -D.y
// Permutations
i = mod289(i);
vec4 p = permute( permute( permute(
i.z + vec4(0.0, i1.z, i2.z, 1.0 ))
+ i.y + vec4(0.0, i1.y, i2.y, 1.0 ))
+ i.x + vec4(0.0, i1.x, i2.x, 1.0 ));
// Gradients: 7x7 points over a square, mapped onto an octahedron.
// The ring size 17*17 = 289 is close to a multiple of 49 (49*6 = 294)
float n_ = 0.142857142857; // 1.0/7.0
vec3 ns = n_ * D.wyz - D.xzx;
vec4 j = p - 49.0 * floor(p * ns.z * ns.z); // mod(p,7*7)
vec4 x_ = floor(j * ns.z);
vec4 y_ = floor(j - 7.0 * x_ ); // mod(j,N)
vec4 x = x_ *ns.x + ns.yyyy;
vec4 y = y_ *ns.x + ns.yyyy;
vec4 h = 1.0 - abs(x) - abs(y);
vec4 b0 = vec4( x.xy, y.xy );
vec4 b1 = vec4( x.zw, y.zw );
//vec4 s0 = vec4(lessThan(b0,0.0))*2.0 - 1.0;
//vec4 s1 = vec4(lessThan(b1,0.0))*2.0 - 1.0;
vec4 s0 = floor(b0)*2.0 + 1.0;
vec4 s1 = floor(b1)*2.0 + 1.0;
vec4 sh = -step(h, vec4(0.0));
vec4 a0 = b0.xzyw + s0.xzyw*sh.xxyy ;
vec4 a1 = b1.xzyw + s1.xzyw*sh.zzww ;
vec3 p0 = vec3(a0.xy,h.x);
vec3 p1 = vec3(a0.zw,h.y);
vec3 p2 = vec3(a1.xy,h.z);
vec3 p3 = vec3(a1.zw,h.w);
// Normalise gradients
vec4 norm = taylorInvSqrt(vec4(dot(p0,p0), dot(p1,p1), dot(p2, p2), dot(p3,p3)));
p0 *= norm.x;
p1 *= norm.y;
p2 *= norm.z;
p3 *= norm.w;
// Mix final noise value
vec4 m = max(0.5 - vec4(dot(x0,x0), dot(x1,x1), dot(x2,x2), dot(x3,x3)), 0.0);
m = m * m;
return 105.0 * dot( m*m, vec4( dot(p0,x0), dot(p1,x1),
dot(p2,x2), dot(p3,x3) ) );
}
void main() {
vec2 st = gl_FragCoord.xy/u_resolution.xy;
vec3 color = vec3(0.0);
float mask = 0.0;
// Position of main body
vec2 bodyCenter = vec2(0.65, 0.5);
vec2 toBody = st - bodyCenter;
// Construct oval for main body
float bodyDist = length(toBody / vec2(2.0, 0.85));
float body = smoothstep(0.13, 0.12, bodyDist);
// Color and noise of main body
vec3 bodyColor = vec3(0.96, 0.8, 0.77);
float bodyNoise = snoise(st * 15.0 + u_time * 0.1) * 0.05;
bodyColor += bodyNoise;
// Add scale pattern using noise
float scales = snoise(st * 40.0) * 0.5 + 0.5;
scales = pow(scales, 3.0) * 0.15;
bodyColor -= scales * body;
vec2 eyePos = bodyCenter + vec2(0.19, 0.02); // Center of eye
float eyeOuter = smoothstep(0.025, 0.023, distance(st, eyePos)); // Outer circle of eye
float eyeInner = smoothstep(0.012, 0.010, distance(st, eyePos)); // Inner circle of eye
vec3 eyeSilver = vec3(0.85, 0.88, 0.90); // Outer color: silver
vec3 eyeBlack = vec3(0.0); // Inner color: black
vec3 eyeColor = mix(eyeSilver, eyeBlack, eyeInner);
// Position of jaw fin
vec2 jawFinCenter = eyePos + vec2(-0.05, -0.03);
vec2 toJawFin = st - jawFinCenter;
// Construct circular sector (-180 to -90 degrees) for jaw fin
float jawFinAngle = atan(toJawFin.y, toJawFin.x);
float jawFinDist = length(toJawFin);
float jawFin = smoothstep(0.12, 0.10, jawFinDist);
jawFin *= step(-3.14159, jawFinAngle) * step(jawFinAngle, -1.5708);
// Add ray texture to jaw fin
float jawFinRays = abs(sin(jawFinAngle * 10.0)) * 0.5 + 0.5;
jawFin *= jawFinRays;
// Color of jaw fin
vec3 jawFinColor = vec3(0.98, 0.92, 0.94); // Lighter pink-white color for fins
// Position of tail fin (large circular spread from left side of body)
vec2 tailCenter = bodyCenter - vec2(0.15, 0.0);
vec2 toTail = st - tailCenter;
// Construct flowing tail with multiple lobes
float tailAngle = atan(toTail.y, toTail.x);
float tailDist = length(toTail);
float tailNoise = snoise(vec3(tailAngle * 3.0, tailDist * 5.0, u_time * 0.1));
float tailRadius = 0.35 + tailNoise * 0.08;
// By default only show to the left of the eye position
float tailMask = step(toTail.x, eyePos.x - tailCenter.x);
// For upper half of tail, stop at a line tilted upper-right at 60 degrees
if (toTail.y > 0.0) {
// tan(60 degrees) = 1.732
tailMask = step(toTail.x - toTail.y / 1.732, 0.1);
}
float tail = smoothstep(tailRadius + 0.02, tailRadius - 0.05, tailDist) * tailMask;
// Add radial pattern to tail fin
float radialPattern = abs(sin(tailAngle * 12.0)) * 0.3 + 0.7;
tail *= radialPattern;
// Color and noise of tail fin
vec3 tailColor = vec3(0.98, 0.92, 0.94); // Lighter pink-white color for fins
float tailFlow = snoise(vec2(tailAngle * 2.0, tailDist * 8.0 - u_time * 0.1)); // Flowing pattern
tailColor += tailFlow * 0.1;
// Add ray texture to tail fin
float finRays = smoothstep(0.6, 0.8, abs(sin(tailAngle * 25.0 + tailDist * 10.0)));
tailColor -= finRays * 0.1 * tail;
// Combine all elements
color = mix(color, tailColor, tail);
color = mix(color, bodyColor, body);
color = mix(color, eyeColor, eyeOuter);
color = mix(color, jawFinColor, jawFin);
// Add subtle shimmer
float shimmer = snoise(st * 20.0 + u_time) * 0.03;
color += shimmer * (body + tail);
colour_out = vec4(color, 1.0);
}
Pink-White Betta by nakanokumin
// ===== NOISE FUNCTIONS =====
vec3 mod289(vec3 x) {
return x - floor(x * (1.0 / 289.0)) * 289.0;
}
vec4 mod289(vec4 x) {
return x - floor(x * (1.0 / 289.0)) * 289.0;
}
vec4 permute(vec4 x) {
return mod289(((x*34.0)+10.0)*x);
}
vec4 taylorInvSqrt(vec4 r)
{
return 1.79284291400159 - 0.85373472095314 * r;
}
float snoise(vec3 v)
{
const vec2 C = vec2(1.0/6.0, 1.0/3.0) ;
const vec4 D = vec4(0.0, 0.5, 1.0, 2.0);
// First corner
vec3 i = floor(v + dot(v, C.yyy) );
vec3 x0 = v - i + dot(i, C.xxx) ;
// Other corners
vec3 g = step(x0.yzx, x0.xyz);
vec3 l = 1.0 - g;
vec3 i1 = min( g.xyz, l.zxy );
vec3 i2 = max( g.xyz, l.zxy );
// x0 = x0 - 0.0 + 0.0 * C.xxx;
// x1 = x0 - i1 + 1.0 * C.xxx;
// x2 = x0 - i2 + 2.0 * C.xxx;
// x3 = x0 - 1.0 + 3.0 * C.xxx;
vec3 x1 = x0 - i1 + C.xxx;
vec3 x2 = x0 - i2 + C.yyy; // 2.0*C.x = 1/3 = C.y
vec3 x3 = x0 - D.yyy; // -1.0+3.0*C.x = -0.5 = -D.y
// Permutations
i = mod289(i);
vec4 p = permute( permute( permute(
i.z + vec4(0.0, i1.z, i2.z, 1.0 ))
+ i.y + vec4(0.0, i1.y, i2.y, 1.0 ))
+ i.x + vec4(0.0, i1.x, i2.x, 1.0 ));
// Gradients: 7x7 points over a square, mapped onto an octahedron.
// The ring size 17*17 = 289 is close to a multiple of 49 (49*6 = 294)
float n_ = 0.142857142857; // 1.0/7.0
vec3 ns = n_ * D.wyz - D.xzx;
vec4 j = p - 49.0 * floor(p * ns.z * ns.z); // mod(p,7*7)
vec4 x_ = floor(j * ns.z);
vec4 y_ = floor(j - 7.0 * x_ ); // mod(j,N)
vec4 x = x_ *ns.x + ns.yyyy;
vec4 y = y_ *ns.x + ns.yyyy;
vec4 h = 1.0 - abs(x) - abs(y);
vec4 b0 = vec4( x.xy, y.xy );
vec4 b1 = vec4( x.zw, y.zw );
//vec4 s0 = vec4(lessThan(b0,0.0))*2.0 - 1.0;
//vec4 s1 = vec4(lessThan(b1,0.0))*2.0 - 1.0;
vec4 s0 = floor(b0)*2.0 + 1.0;
vec4 s1 = floor(b1)*2.0 + 1.0;
vec4 sh = -step(h, vec4(0.0));
vec4 a0 = b0.xzyw + s0.xzyw*sh.xxyy ;
vec4 a1 = b1.xzyw + s1.xzyw*sh.zzww ;
vec3 p0 = vec3(a0.xy,h.x);
vec3 p1 = vec3(a0.zw,h.y);
vec3 p2 = vec3(a1.xy,h.z);
vec3 p3 = vec3(a1.zw,h.w);
//Normalise gradients
vec4 norm = taylorInvSqrt(vec4(dot(p0,p0), dot(p1,p1), dot(p2, p2), dot(p3,p3)));
p0 *= norm.x;
p1 *= norm.y;
p2 *= norm.z;
p3 *= norm.w;
// Mix final noise value
vec4 m = max(0.5 - vec4(dot(x0,x0), dot(x1,x1), dot(x2,x2), dot(x3,x3)), 0.0);
m = m * m;
return 105.0 * dot( m*m, vec4( dot(p0,x0), dot(p1,x1),
dot(p2,x2), dot(p3,x3) ) );
}
// fractal brownian motion
float fbm(vec3 p) {
float value = 0.0;
float amplitude = 0.5;
for(int i = 0; i < 6; i++) {
value += amplitude * snoise(p);
p *= 2.1;
amplitude *= 0.5;
}
return value;
}
// ===== MAIN SHADER =====
uniform vec2 u_resolution;
uniform float u_time;
void main() {
vec2 st = gl_FragCoord.xy / u_resolution.xy;
vec2 center = vec2(0.5, 0.5);
//calculate the distance to the edge to put the red tail
float distFromCenter = length(st - center);
float edgeMask = smoothstep(0.2, 0.5, distFromCenter); // 边缘是1,中心是0
//to put the noise to mimic the movement and color changing
float noise1 = fbm(vec3(st * 2.5, u_time * 0.3)) * 0.5 + 0.5;
float noise2 = fbm(vec3(st * 4.0, u_time * 0.4)) * 0.5 + 0.5;
float noise3 = fbm(vec3(st * 6.0, u_time * 0.5)) * 0.5 + 0.5;
// the more detailed noise
float scales = snoise(vec3(st * 30.0, u_time * 0.2)) * 0.5 + 0.5;
// To define the basic blue color
vec3 deepColor = vec3(0.05, 0.03, 0.12); // dark purple
vec3 darkBlue = vec3(0.12, 0.10, 0.28); // dark blue
vec3 midBlue = vec3(0.20, 0.20, 0.45); // blue
vec3 brightBlue = vec3(0.30, 0.35, 0.60); // bright blue
// To define the basic red color
vec3 darkRed = vec3(0.25, 0.02, 0.04); // dark red
vec3 brightRed = vec3(0.75, 0.10, 0.15); // red
vec3 vividRed = vec3(0.95, 0.20, 0.25); // bright red
// achieve the transition of blue part
vec3 baseBlue = mix(deepColor, darkBlue, noise1);
baseBlue = mix(baseBlue, midBlue, noise2 * 0.6);
baseBlue = mix(baseBlue, brightBlue, noise3 * 0.3);
// add the detail scale
baseBlue += (scales - 0.5) * 0.08;
// achieve the transition of red part
vec3 redColor = mix(darkRed, brightRed, noise1 * 0.7 + 0.3);
redColor = mix(redColor, vividRed, noise2 * 0.5);
// to achieve the movement
float redNoise = fbm(vec3(st * 3.0, u_time * 0.35)) * 0.5 + 0.5;
float redEdge = edgeMask * smoothstep(0.3, 0.7, redNoise);
// mix blue and red to do the fish color
vec3 color = mix(baseBlue, redColor, redEdge * 0.8);
// basic of highlight
float gloss1 = pow(noise1, 3.0) * 0.15;
float gloss2 = pow(noise2, 4.0) * 0.12;
float gloss3 = pow(noise3, 5.0) * 0.10;
// blue gloss
vec3 blueGloss = vec3(0.3, 0.35, 0.5) * (gloss1 + gloss2);
// red gloss
vec3 redGloss = vec3(0.4, 0.15, 0.2) * gloss2;
color += mix(blueGloss, redGloss, redEdge);
color += vec3(gloss3);
// Iridescent effect
float iridescence = snoise(vec3(st * 15.0, u_time * 0.3)) * 0.05;
color += vec3(iridescence * 0.8, iridescence, iridescence * 1.2);
// breathing
float pulse = sin(u_time * 1.2) * 0.03 + 0.97;
color *= pulse;
colour_out = vec4(color, 1.0);
}
redtail by WANG
SHAN
uniform float u_time;
uniform vec2 u_resolution;
// =====================
// webgl-noise style helpers (2D simplex noise)
// =====================
vec3 mod289(vec3 x) { return x - floor(x * (1.0 / 289.0)) * 289.0; }
vec2 mod289(vec2 x) { return x - floor(x * (1.0 / 289.0)) * 289.0; }
vec3 permute(vec3 x) {
return mod289((x * 34.0 + 1.0) * x);
}
// 2D simplex noise, roughly in [-1,1]
// based on Stefan Gustavson's webgl-noise implementation
float snoise(vec2 v) {
const vec4 C = vec4(
0.211324865405187, // (3.0 - sqrt(3.0)) / 6.0
0.366025403784439, // 0.5 * (sqrt(3.0) - 1.0)
-0.577350269189626, // -1.0 + 2.0 * C.x
0.024390243902439 // 1.0 / 41.0
);
// First corner
vec2 i = floor(v + dot(v, C.yy));
vec2 x0 = v - i + dot(i, C.xx);
// Other corners
vec2 i1 = (x0.x > x0.y) ? vec2(1.0, 0.0) : vec2(0.0, 1.0);
vec4 x12 = x0.xyxy + C.xxzz;
x12.xy -= i1;
// Permutations
i = mod289(i);
vec3 p = permute(
permute(i.y + vec3(0.0, i1.y, 1.0)) +
i.x + vec3(0.0, i1.x, 1.0)
);
// Gradients: 41 points uniformly over a line, mapped onto a diamond.
vec3 m = max(
0.5 - vec3(
dot(x0, x0),
dot(x12.xy, x12.xy),
dot(x12.zw, x12.zw)
),
0.0
);
m = m * m;
m = m * m;
vec3 x = 2.0 * fract(p * C.www) - 1.0;
vec3 h = abs(x) - 0.5;
vec3 ox = floor(x + 0.5);
vec3 a0 = x - ox;
// Normalize gradients implicitly by scaling m
m *= 1.79284291400159 - 0.85373472095314 * (a0 * a0 + h * h);
// Compute final noise value at P
vec3 g;
g.x = a0.x * x0.x + h.x * x0.y;
g.y = a0.y * x12.x + h.y * x12.y;
g.z = a0.z * x12.z + h.z * x12.w;
return 130.0 * dot(m, g);
}
// =====================
// SDF helpers & fan shapes
// =====================
float sdCircle(vec2 p, float r) {
return length(p) - r;
}
float sdEllipse(vec2 p, vec2 r) {
vec2 q = p / r;
return length(q) - 1.0;
}
float smin(float a, float b, float k) {
float h = clamp(0.5 + 0.5 * (b - a) / k, 0.0, 1.0);
return mix(b, a, h) - k * h * (1.0 - h);
}
vec2 rotate2D(vec2 p, float a) {
float c = cos(a), s = sin(a);
return mat2(c, -s, s, c) * p;
}
// Generic fan shape (used for tail and fins)
// flipX = -1.0 for fan opening to the left, +1.0 for right
float fanShape(vec2 p, vec2 base, float flipX,
float maxAng, float maxR,
float time, float rippleAmp) {
vec2 q = p - base;
q.x *= flipX;
float ang = atan(q.y, q.x);
float r = length(q);
float dAngle = abs(ang) - maxAng;
float dRadius = r - maxR;
float d = max(dAngle, dRadius);
float edge = maxR - r;
float ruf = rippleAmp *
sin(ang * 4.0 + time * 1.2) *
smoothstep(0.0, -0.25, edge);
d += ruf;
return d;
}
void main() {
vec2 vUV = gl_FragCoord.xy / u_resolution;
float t = u_time;
// Normalized coords with aspect
vec2 uv = vUV - 0.5;
float aspect = u_resolution.x / u_resolution.y;
uv.x *= aspect;
// ===== Background gradient =====
float gy = uv.y * 0.5 + 0.5;
vec3 colTop = vec3(0.94, 0.97, 1.0);
vec3 colBottom = vec3(0.65, 0.72, 0.88);
vec3 col = mix(colBottom, colTop, gy);
// ===== Ray-marched blue water with noise =====
vec2 scr = (vUV - 0.5) * 2.0;
scr.x *= aspect;
vec3 ro = vec3(0.0, 0.0, 2.0);
vec3 rd = normalize(vec3(scr, -1.5));
float s = 0.0;
float maxDist = 4.0;
float radius = 1.8;
vec3 waterAccum = vec3(0.0);
float waterAlpha = 0.0;
for (int i = 0; i < 72; ++i) {
if (s > maxDist || waterAlpha > 0.99) break;
vec3 pos = ro + rd * s;
float r = length(pos);
if (r < radius) {
float vert = exp(-1.2 * abs(pos.y));
float wave1 = sin(pos.x * 5.0 + t * 0.7);
float wave2 = cos(pos.z * 4.0 - t * 0.5);
float wave = 0.5 * (wave1 + wave2);
// simplex noise over xz plane for extra turbulence
float n = snoise(pos.xz * 1.2 + vec2(t * 0.15, -t * 0.1));
float baseDensity = max(wave, 0.0) * 0.06 * vert;
float density = baseDensity * (0.8 + 0.4 * n); // noise-modulated
vec3 sampleCol = vec3(0.20, 0.40, 0.85);
waterAccum += (1.0 - waterAlpha) * density * sampleCol;
waterAlpha += (1.0 - waterAlpha) * density;
}
s += 0.06;
}
vec3 baseWater = vec3(0.30, 0.50, 0.90);
float wMix = clamp(waterAlpha * 1.8, 0.0, 1.0);
col = mix(col, baseWater + waterAccum, wMix);
// ===== Betta coordinates =====
vec2 p = uv;
p.y += 0.02 * sin(t * 0.7);
p.x += 0.02 * sin(t * 0.4);
// ===== Body & head =====
float dBody = sdEllipse(p - vec2(0.15, 0.0), vec2(0.25, 0.12));
float dHead = sdCircle (p - vec2(0.37, 0.02), 0.07);
float dCore = smin(dBody, dHead, 0.08);
float bodyMask = smoothstep(0.03, 0.0, dCore);
// ===== Tail fan (left) =====
float tailMaxR = 0.55;
float tailMaxAng = 1.05;
vec2 tailBase = vec2(0.00, 0.0);
float dTail = fanShape(p, tailBase, -1.0,
tailMaxAng, tailMaxR,
t, 0.03);
float tailMask = smoothstep(0.03, 0.0, dTail);
// ===== Dorsal & ventral fins (fans, closer to head) =====
float dFinTop = fanShape(p, vec2(0.28, 0.11), -1.0,
0.40, 0.22, t, 0.015);
float dFinBot = fanShape(p, vec2(0.28, -0.11), -1.0,
0.40, 0.22, t, 0.015);
float finTopMask = smoothstep(0.03, 0.0, dFinTop);
float finBotMask = smoothstep(0.03, 0.0, dFinBot);
// ===== Colors =====
vec3 pastelBlue = vec3(0.70, 0.80, 1.00);
vec3 pastelPink = vec3(1.00, 0.70, 0.90);
vec3 pastelLilac = vec3(0.90, 0.80, 1.00);
// Tail color with simplex noise veins
vec2 qTail = p - tailBase;
qTail.x = -qTail.x;
float tailR = clamp(length(qTail) / tailMaxR, 0.0, 1.0);
float tailAng = atan(qTail.y, qTail.x);
vec3 tailColor = mix(pastelPink, pastelBlue, tailR * 0.7 + 0.1);
float vein = 0.5 + 0.5 * sin(tailAng * 10.0 - t * 0.8);
float veinNoise = snoise(qTail * 4.0 + vec2(0.0, t * 0.3));
float veinMask = tailMask * smoothstep(0.3, 1.0, tailR);
tailColor *= 1.0 + 0.25 * (vein * 0.6 + veinNoise * 0.4) * veinMask;
// Body gradient – light pink overall
float gx = clamp((p.x - 0.10) * 2.0, 0.0, 1.0);
vec3 bodyTail = mix(pastelPink, pastelBlue, 0.25);
vec3 bodyHead = pastelPink;
vec3 bodyColor = mix(bodyTail, bodyHead, gx);
// Body simplex noise for subtle scales
float bodyTex = snoise(p * 6.0 + vec2(t * 0.3, -t * 0.2));
float bodyTexN = 0.5 + 0.5 * bodyTex; // [0,1]
bodyColor *= 0.9 + 0.1 * bodyTexN;
// Body stripes (kept, but now modulated)
float stripeScale = 16.0;
float stripe = 0.5 + 0.5 * sin(p.x * stripeScale + p.y * 4.0);
vec3 stripeTint = vec3(1.05, 1.02, 1.10);
bodyColor = mix(bodyColor, bodyColor * stripeTint,
bodyMask * 0.35 * stripe);
// Fin colors
vec3 finTopColor = mix(pastelLilac, pastelBlue, 0.5);
vec3 finBotColor = mix(pastelPink, pastelBlue, 0.4);
// ===== Draw order: tail → fins → body → eye =====
// Tail
col = mix(col, tailColor, tailMask);
// Fins
col = mix(col, finTopColor, finTopMask);
col = mix(col, finBotColor, finBotMask);
// Rim light for body then draw body
float dFish = smin(dCore, dTail, 0.10);
float rim = smoothstep(0.10, 0.02, dFish + p.y * 0.35);
bodyColor += rim * 0.25;
col = mix(col, bodyColor, bodyMask);
// Eye
float dEyeWhite = sdCircle(p - vec2(0.39, 0.03), 0.018);
float eyeWhiteMask = smoothstep(0.014, 0.0, dEyeWhite);
col = mix(col, vec3(0.98, 0.98, 1.00), eyeWhiteMask);
float dEye = sdCircle(p - vec2(0.39, 0.03), 0.010);
float eyeMask = smoothstep(0.007, 0.0, dEye);
col = mix(col, vec3(0.02, 0.02, 0.06), eyeMask);
colour_out = vec4(col, 1.0);
}
unknown by anonymous
uniform vec2 u_resolution;
uniform vec2 u_mouse;
uniform float u_time;
vec3 mod289(vec3 x) { return x - floor(x / 289.0) * 289.0; }
vec2 mod289(vec2 x) { return x - floor(x / 289.0) * 289.0; }
vec3 permute(vec3 x) { return mod289(((x * 34.0) + 1.0) * x); }
float snoise(vec2 v) {
const vec4 C = vec4(
0.211324865405187,
0.366025403784439,
-0.577350269189626,
0.024390243902439
);
vec2 i = floor(v + dot(v, C.yy));
vec2 x0 = v - i + dot(i, C.xx);
vec2 i1 = (x0.x > x0.y) ? vec2(1.0, 0.0) : vec2(0.0, 1.0);
vec4 x12 = x0.xyxy + C.xxzz;
x12.xy -= i1;
i = mod289(i);
vec3 p = permute(
permute(i.y + vec3(0.0, i1.y, 1.0))
+ i.x + vec3(0.0, i1.x, 1.0)
);
vec3 m = max(
0.5 - vec3(
dot(x0, x0),
dot(x12.xy, x12.xy),
dot(x12.zw, x12.zw)
),
0.0
);
m = m * m;
m = m * m;
vec3 x = 2.0 * fract(p * C.www) - 1.0;
vec3 h = abs(x) - 0.5;
vec3 ox = floor(x + 0.5);
vec3 a0 = x - ox;
m *= 1.79284291400159 - 0.85373472095314 * (a0 * a0 + h * h);
vec3 g;
g.x = a0.x * x0.x + h.x * x0.y;
g.yz = a0.yz * x12.xz + h.yz * x12.yw;
return 130.0 * dot(m, g);
}
/*========= Tail Swing ==========*/
float tailSwing(vec2 p, float t) {
// p.x
return sin(t * 2.0 + p.x * 6.0) * 0.04;
}
/* ========= Fish body ========= */
float fishBody(vec2 p) {
float body = length(vec2(p.x , p.y * 3.0));
return smoothstep(0.28, 0.24, body);
}
/* ========= tail ========= */
float fanTail(vec2 p, float t) {
p.x -= 0.05;
float r = length(p);
float swing = tailSwing(p, u_time);
float a = atan(p.y, p.x + swing);
float alpha = smoothstep(0.2, 0.5, r);
float fan = smoothstep(1.4, 0.6, abs(a));
float radius = 0.4 + 0.15 * cos(a * 2.0);
float ripple = sin(a * 20.0 + t * 3.0) * 0.03;
float tail = smoothstep(radius, radius - 0.02, r + ripple);
return tail * fan * (1.0 - alpha);
}
/*=============== Rays ===================*/
float finRays(vec2 p) {
p.x -= 0.05;
float r = length(p);
float swing = tailSwing(p, u_time);
float a = atan(p.y, p.x + swing);
float fanMask = smoothstep(1.4, 0.6, abs(a));
float rayCount = 28.0;
float rayID = fract((a / 3.1415926 + 0.5) * rayCount);
float angleNorm = abs(a) / 1.4;
float maxR = mix(0.45, 0.25, angleNorm);
float t = clamp(r / maxR, 0.0, 1.0);
float width = mix(2.0, 0.2, t * t);
float ray = smoothstep(width, 0.0, abs(rayID - 0.5));
float lengthMask = smoothstep(0.02, maxR, r);
return ray * fanMask * lengthMask;
}
/*================背鳍腹鳍&眼睛=====================*/
float dorsalFin(vec2 p, float t) {
vec2 q = p;
q.y -= 0.02; //up
q.x += 0.25;
return fanTail(q, t) * 0.2;
}
float analFin(vec2 p, float t) {
vec2 q = p;
q.y += 0.02; //down
q.x += 0.25;
return fanTail(q, t) * 0.8;
}
float eye(vec2 p, vec2 offset) {
return smoothstep(0.023, 0.002, length(p - offset));
}
/*==============鱼须子===========*/
float whisker(vec2 p, vec2 offset, float t, float phase) {
vec2 pos = p - offset;
float sway = sin(length(pos)*15.0 + t*2.0 + phase)*0.02;
float d = abs(pos.x + sway) + abs(pos.y*0.5);
return smoothstep(0.01,0.0,d);
}
/* ========= Vegetation ========= */
float vegetation(vec2 st) {
float n = snoise(st * vec2(3.0, 8.0) + vec2(0.0, u_time * 0.3));
return smoothstep(0.2, 0.6, n + st.y);
}
void main() {
vec2 st = gl_FragCoord.xy / u_resolution.xy;
st.x *= u_resolution.x / u_resolution.y;
vec2 p = st - vec2(0.5, 0.5);
p.y += sin(u_time + p.x * 3.0) * 0.03;
float body = fishBody(p);
float fin = fanTail(p, u_time);
float rays = finRays(p);
float fins = max(fin, max(dorsalFin(p,u_time), analFin(p,u_time)));
float fish = max(body, fins);
float eyes = eye(p, vec2(-0.210,-0.000));
// float headMask = smoothstep(0.18, 0.05, length(p + vec2(0.15, 0.0)));
//float spots = snoise(p * 20.0) * 0.5 + 0.5;
//spots *= headMask;
// float yBody = st.y/3.0- length(vec2(p.x , p.y *3.0));
// float bodyH=0.0;
float bodyLen= 0.28 * 2.0;
// Red zone
//float bodyMask = smoothstep(0.28, 0.24, length(vec2(p.x , p.y * 3.0)));
float redWeight = smoothstep(-bodyLen/3.0, -bodyLen*5.0/24.0, p.x) * (1.0 - smoothstep(-bodyLen*5.0/24.0, -bodyLen/12.0, p.x));
//vec3 bodyColor = mix(vec3(0.8,0.8,0.9), vec3(0.8,0.1,0.1), redWeight);
vec3 redColor = vec3(0.885,0.218,0.117);
// Fish color (iridescent)
vec3 fishColor = vec3(
0.99 + 0.03 * sin(u_time + p.y * 6.0),
0.881 + 0.04 * body,
0.881 + 0.02 * fin
);
// Vegetation background
float veg = vegetation(st);
vec3 vegColor = mix(
vec3(0.0, 0.1, 0.05),
vec3(0.0, 0.3, 0.15),
veg
);
// Water gradient
vec3 water = mix(
vec3(0.072,0.143,0.430),
vec3(0.076,0.535,0.256),
st.y
);
// little shadow
float bodyGrad = smoothstep(-bodyLen/2.0, bodyLen/2.0, p.x);
// little light
float frontBack = mix(1.08, 0.92, bodyGrad);
vec3 color = water + vegColor;
//vec3 color = vec3(0.05,0.1,0.3);
vec3 rayColor = vec3(0.090,0.080,0.100); //
vec3 fishDetail = mix(fishColor, rayColor, rays * 0.7);
color = mix(color, fishDetail, fish);
color = mix(color, vec3(0.05), eyes);
//color = mix(color, vec3(0.8, 0.1, 0.1), spots * 0.6);
//color = mix(color, bodyColor, body);
color = mix(color, redColor, redWeight * body);
/* ===== 鳞片噪声 =====*/
vec2 scaleUV = vec2(p.x * 18.0, p.y * 70.0);
vec2 gv = fract(scaleUV) - 0.5;
float d = length(gv);
float scalePattern = smoothstep(0.25, 0.15, d);
float jitter = sin(p.x * 12.0 + p.y * 7.0) * 0.15;
scalePattern *= (1.0 + jitter);
float scaleRange = smoothstep(-bodyLen * 0.35, -bodyLen * 0.15, p.x)* (1.0 - smoothstep(bodyLen * 0.15, bodyLen * 0.35, p.x));
float scaleLight = 1.0 + scalePattern * 0.9;
color = mix(color, color * scaleLight, body*scaleRange);
color = mix(color, color * frontBack, body);
colour_out = vec4(color, 1.0);
}
Red Tancho Betta Fish by Allyssa