#version 460 core #include out vec4 fragColor; uniform vec2 uSize; uniform vec2 uTilt; uniform float uPrismatic; uniform float uSparkle; uniform float uSpecular; uniform float uDiffraction; uniform float uOpacity; #define TWO_PI 6.28318530718 #define ROT_0_78 mat2(0.71091, 0.70328, -0.70328, 0.71091) #define ROT_M0_5 mat2(0.87758, -0.47943, 0.47943, 0.87758) #define ROT_1_2 mat2(0.36236, 0.93204, -0.93204, 0.36236) float hash(vec2 p) { vec3 p3 = fract(vec3(p.xyx) * 0.1031); p3 += dot(p3, p3.yzx + 33.33); return fract((p3.x + p3.y) * p3.z); } float noise(vec2 p) { vec2 i = floor(p); vec2 f = fract(p); vec2 u = 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, u.x), mix(c, d, u.x), u.y); } vec3 hsv2rgb(float h, float s, float v) { vec3 k = vec3(1.0, 2.0 / 3.0, 1.0 / 3.0); vec3 p = abs(fract(vec3(h) + k) * 6.0 - 3.0); return v * mix(vec3(1.0), clamp(p - 1.0, 0.0, 1.0), s); } vec3 rainbow(float phase, float saturation, float value) { return hsv2rgb(fract(phase), saturation, value); } vec2 safeNormalize(vec2 p) { return p * inversesqrt(max(dot(p, p), 0.000001)); } float sdRegularPolygon(vec2 p, float sides, float radius) { float angle = atan(p.y, p.x); float sector = TWO_PI / sides; return cos(floor(0.5 + angle / sector) * sector - angle) * length(p) - radius; } float sparklePolygonMask(vec2 p, float sides, float aspectRatio, float rotation) { float c = cos(rotation); float s = sin(rotation); vec2 q = vec2(c * p.x - s * p.y, s * p.x + c * p.y); q.x /= aspectRatio; float poly = sdRegularPolygon(q, sides, 0.26); return smoothstep(0.03, -0.03, poly); } vec3 styleBase(vec2 uv, vec2 tilt) { float maxZ = -999.0; vec2 bestSlope = vec2(0.0); float bestHash = 0.0; float bestEdge = 1.0; vec2 warp = vec2(sin(uv.y * 10.0), cos(uv.x * 10.0)) * 0.05; vec2 baseUV = uv + warp; vec2 p1 = baseUV * 12.0; vec2 id1 = floor(p1); vec2 f1 = fract(p1) - 0.5; vec2 slope1 = vec2(hash(id1 + 1.2), hash(id1 + 1.3)) * 2.0 - 1.0; float z1 = dot(f1, slope1) * 3.5; float edge1 = min(0.5 - abs(f1.x), 0.5 - abs(f1.y)); float hash1 = hash(id1 + 1.1); float use1 = step(maxZ, z1); maxZ = mix(maxZ, z1, use1); bestSlope = mix(bestSlope, slope1, use1); bestHash = mix(bestHash, hash1, use1); bestEdge = mix(bestEdge, edge1, use1); vec2 p2 = (ROT_0_78 * baseUV) * 15.0; vec2 id2 = floor(p2); vec2 f2 = fract(p2) - 0.5; vec2 slope2 = vec2(hash(id2 + 2.2), hash(id2 + 2.3)) * 2.0 - 1.0; float z2 = 0.8 + dot(f2, slope2) * 3.5; float edge2 = min(0.5 - abs(f2.x), 0.5 - abs(f2.y)); float hash2 = hash(id2 + 2.1); float use2 = step(maxZ, z2); maxZ = mix(maxZ, z2, use2); bestSlope = mix(bestSlope, slope2, use2); bestHash = mix(bestHash, hash2, use2); bestEdge = mix(bestEdge, edge2, use2); vec2 p3 = (ROT_M0_5 * baseUV) * 18.0; vec2 id3 = floor(p3); vec2 f3 = fract(p3) - 0.5; vec2 slope3 = vec2(hash(id3 + 3.2), hash(id3 + 3.3)) * 2.0 - 1.0; float z3 = 1.6 + dot(f3, slope3) * 3.5; float edge3 = min(0.5 - abs(f3.x), 0.5 - abs(f3.y)); float hash3 = hash(id3 + 3.1); float use3 = step(maxZ, z3); maxZ = mix(maxZ, z3, use3); bestSlope = mix(bestSlope, slope3, use3); bestHash = mix(bestHash, hash3, use3); bestEdge = mix(bestEdge, edge3, use3); vec2 p4 = (ROT_1_2 * baseUV) * 21.0; vec2 id4 = floor(p4); vec2 f4 = fract(p4) - 0.5; vec2 slope4 = vec2(hash(id4 + 4.2), hash(id4 + 4.3)) * 2.0 - 1.0; float z4 = 2.4 + dot(f4, slope4) * 3.5; float edge4 = min(0.5 - abs(f4.x), 0.5 - abs(f4.y)); float hash4 = hash(id4 + 4.1); float use4 = step(maxZ, z4); maxZ = mix(maxZ, z4, use4); bestSlope = mix(bestSlope, slope4, use4); bestHash = mix(bestHash, hash4, use4); bestEdge = mix(bestEdge, edge4, use4); float alignment = dot(tilt, safeNormalize(bestSlope + vec2(0.001))); float phase = dot(uv, vec2(0.6, -0.4)) + bestHash * 0.6 + alignment * 0.5; float saturation = 0.7 + 0.3 * hash(vec2(bestHash, 6.1)); vec3 baseColor = rainbow(phase, saturation, 1.0); float lightCycle = sin(alignment * 4.0 + bestHash * TWO_PI + dot(uv, vec2(2.1, -1.7))); float ambient = 0.15 + 0.25 * max(0.0, lightCycle); float flash = pow(max(0.0, lightCycle), 5.0); vec3 finalColor = baseColor * ambient; finalColor += mix(baseColor, vec3(1.0, 0.95, 0.85), 0.6) * flash * 0.55; finalColor += baseColor * smoothstep(0.05, 0.0, bestEdge) * flash * 0.4; return finalColor; } vec3 sparkleLayer(vec2 aspectUV, vec2 tilt, vec2 tiltDir) { vec2 sparkleGrid = aspectUV * 7.0; vec2 grid = floor(sparkleGrid); vec2 cell = fract(sparkleGrid) - 0.5; float sparkleHash = hash(grid); vec2 sparkleNormal = safeNormalize(vec2(sparkleHash * 2.0 - 1.0, hash(grid + 19.7) * 2.0 - 1.0)); float rarity = step(0.965, hash(grid + 0.31)); float alignment = max(0.0, dot(tiltDir, sparkleNormal)); float twinkle = 0.45 + 0.55 * sin(dot(cell, vec2(21.0, -17.0)) + dot(tilt, vec2(5.3, -4.1)) + sparkleHash * TWO_PI); float sides = 5.0 + floor(hash(grid + 9.4) * 4.0); float aspect = 0.75 + hash(grid + 13.1) * 0.55; float shapeMask = sparklePolygonMask(cell, sides, aspect, hash(grid + 5.1) * TWO_PI); float sparkleMask = pow(alignment, 4.8) * twinkle * rarity * shapeMask; vec3 sparkleColor = mix(vec3(1.0), rainbow(sparkleHash + dot(tilt, vec2(0.2, -0.15)), 0.55, 1.0), 0.20); return sparkleColor * sparkleMask * uSparkle * 0.25 * 0.70; } void main() { vec2 frag = FlutterFragCoord().xy; vec2 uv = frag / uSize; vec2 fromCenter = uv - vec2(0.5); float maxDim = max(uSize.x, uSize.y); vec2 aspectUV = frag / maxDim; float tiltMag = length(uTilt); vec2 tiltDir = safeNormalize(uTilt + vec2(0.001)); float highlightDistance = length(uv - (vec2(0.5) + (uTilt * 0.35))); float specularMask = pow(max(0.0, 1.0 - highlightDistance * 2.6), 4.0); vec3 base = styleBase(aspectUV, uTilt); vec3 sparkle = sparkleLayer(aspectUV, uTilt, tiltDir); float baseLuma = dot(base, vec3(0.299, 0.587, 0.114)); vec3 chromaAdjusted = mix(vec3(baseLuma), base, 0.2 + 0.8 * uPrismatic); vec2 normal2d = safeNormalize(fromCenter + vec2(0.001)); float directional = 0.5 + 0.5 * dot(normal2d, tiltDir); float tiltLighting = mix(0.86, 1.20, pow(directional, 1.2)); chromaAdjusted *= tiltLighting; float edgeSpec = pow(1.0 - directional, 2.2); vec3 specular = vec3(specularMask) * (0.3 + 0.7 * edgeSpec) * uSpecular * (0.35 + 0.65 * tiltMag) * 1.65; float prismaticPhase = dot(aspectUV, vec2(3.6, -2.1)) + dot(uTilt, vec2(0.9, -0.7)); vec3 prismaticTint = rainbow(prismaticPhase, 0.72, 1.0) * uPrismatic * 0.90 * (0.08 + 0.14 * edgeSpec); float microMask = 0.5 + 0.5 * sin(dot(aspectUV, vec2(137.0, 57.0)) + noise(aspectUV * 14.0) * 4.0 + dot(uTilt, vec2(9.0, 4.0)) * 2.0); vec3 microShimmer = rainbow(noise(aspectUV * 10.0) + dot(uTilt, vec2(0.4, -0.3)) * 0.3, 0.65, 1.0) * microMask * uDiffraction * 0.08; vec3 styleColor = chromaAdjusted + microShimmer + prismaticTint + (specular * 0.22) + sparkle; vec3 mappedColor = styleColor / (1.0 + styleColor * 0.3); float luma = dot(mappedColor, vec3(0.299, 0.587, 0.114)); vec3 vibrantColor = mix(vec3(luma), mappedColor, 1.4); float foilBrightness = dot(vibrantColor, vec3(0.333)); float alpha = clamp(foilBrightness * 0.5, 0.0, 0.35) * clamp(uOpacity, 0.0, 1.0); vec3 finalColor = clamp(vibrantColor, 0.0, 1.0); fragColor = vec4(finalColor * alpha, alpha); }