diff --git a/app/components/grainy-liquid/Blob.vue b/app/components/grainy-liquid/Blob.vue new file mode 100644 index 00000000..53e7ea8c --- /dev/null +++ b/app/components/grainy-liquid/Blob.vue @@ -0,0 +1,133 @@ + + + + + + + + \ No newline at end of file diff --git a/app/components/grainy-liquid/Experience.vue b/app/components/grainy-liquid/Experience.vue new file mode 100644 index 00000000..b83f0b63 --- /dev/null +++ b/app/components/grainy-liquid/Experience.vue @@ -0,0 +1,23 @@ + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/app/components/grainy-liquid/MultiBlob.vue b/app/components/grainy-liquid/MultiBlob.vue new file mode 100644 index 00000000..3581dd3b --- /dev/null +++ b/app/components/grainy-liquid/MultiBlob.vue @@ -0,0 +1,62 @@ + + + + + + + + + \ No newline at end of file diff --git a/app/components/grainy-liquid/WebsiteLayout.vue b/app/components/grainy-liquid/WebsiteLayout.vue new file mode 100644 index 00000000..9b873ae4 --- /dev/null +++ b/app/components/grainy-liquid/WebsiteLayout.vue @@ -0,0 +1,89 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + Perfect for your next web, social or branding project + + + + + + + + + + + + High res + grainy + abstract + shapes. + + + + Grainy + Shapes + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/app/components/grainy-liquid/index.global.vue b/app/components/grainy-liquid/index.global.vue new file mode 100644 index 00000000..5f98fd9f --- /dev/null +++ b/app/components/grainy-liquid/index.global.vue @@ -0,0 +1,7 @@ + + + + + \ No newline at end of file diff --git a/app/components/grainy-liquid/shaders/fragment.glsl b/app/components/grainy-liquid/shaders/fragment.glsl new file mode 100644 index 00000000..37cd2011 --- /dev/null +++ b/app/components/grainy-liquid/shaders/fragment.glsl @@ -0,0 +1,76 @@ +uniform float u_time; +uniform vec3 u_colorA; +uniform vec3 u_colorB; +uniform vec3 u_colorC; +uniform float u_noiseScale; +uniform float u_grainIntensity; +uniform float u_fresnelPower; + +varying vec3 v_position; +varying vec3 v_normal; +varying vec2 v_uv; + +// Random function for grain effect +float random(vec2 st) { + return fract(sin(dot(st.xy, vec2(12.9898,78.233))) * 43758.5453123); +} + +// Noise function for color mixing +float noise(vec2 st) { + vec2 i = floor(st); + vec2 f = fract(st); + + float a = random(i); + float b = random(i + vec2(1.0, 0.0)); + float c = random(i + vec2(0.0, 1.0)); + float d = random(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; +} + +// Fractal Brownian Motion for more complex noise +float fbm(vec2 st) { + float value = 0.0; + float amplitude = 0.5; + float frequency = 1.0; + + for (int i = 0; i < 5; i++) { + value += amplitude * noise(st * frequency); + frequency *= 2.0; + amplitude *= 0.5; + } + + return value; +} + +void main() { + vec3 normal = normalize(v_normal); + vec3 viewDirection = normalize(cameraPosition - v_position); + + // Fresnel effect for liquid-like rim lighting + float fresnel = 1.0 - dot(normal, viewDirection); + fresnel = pow(fresnel, u_fresnelPower); + + // Create flowing noise pattern for color mixing + vec2 flowUv = v_uv + u_time * 0.1; + float colorNoise = fbm(flowUv * u_noiseScale); + + // Mix colors based on noise and fresnel + vec3 baseColor = mix(u_colorA, u_colorB, colorNoise); + vec3 finalColor = mix(baseColor, u_colorC, fresnel); + + // Enhanced grain effect for more texture + vec2 grainUv = v_uv * 100.0 + u_time * 0.02; + float grain1 = random(grainUv) * u_grainIntensity; + float grain2 = random(grainUv * 2.0 + 0.5) * u_grainIntensity * 0.5; + float totalGrain = grain1 + grain2; + finalColor += vec3(totalGrain); + + // Add some iridescence based on viewing angle + float iridescence = sin(fresnel * 3.14159 + u_time) * 0.1; + finalColor += vec3(iridescence * 0.5, iridescence * 0.8, iridescence); + + gl_FragColor = vec4(finalColor, 1.0); +} \ No newline at end of file diff --git a/app/components/grainy-liquid/shaders/fragment.glsl.d.ts b/app/components/grainy-liquid/shaders/fragment.glsl.d.ts new file mode 100644 index 00000000..f93db699 --- /dev/null +++ b/app/components/grainy-liquid/shaders/fragment.glsl.d.ts @@ -0,0 +1,2 @@ +declare const content: string +export default content \ No newline at end of file diff --git a/app/components/grainy-liquid/shaders/vertex.glsl b/app/components/grainy-liquid/shaders/vertex.glsl new file mode 100644 index 00000000..81fbb17d --- /dev/null +++ b/app/components/grainy-liquid/shaders/vertex.glsl @@ -0,0 +1,109 @@ +uniform float u_time; +uniform float u_amplitude; +uniform float u_frequency; +uniform float u_speed; + +varying vec3 v_position; +varying vec3 v_normal; +varying vec2 v_uv; + +// Simplex 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)+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) ) ); +} + +void main() { + v_uv = uv; + v_normal = normal; + + vec3 pos = position; + float time = u_time * u_speed; + + // Create multiple layers of noise for liquid-like movement + float noise1 = snoise(pos * u_frequency + time * 0.5); + float noise2 = snoise(pos * u_frequency * 2.0 + time * 0.3); + float noise3 = snoise(pos * u_frequency * 4.0 + time * 0.7); + + // Combine noise layers with different amplitudes + float displacement = noise1 * 0.6 + noise2 * 0.3 + noise3 * 0.1; + + // Apply displacement along normal direction with smoother transition + vec3 displacedPosition = pos + normal * displacement * u_amplitude; + + v_position = displacedPosition; + + gl_Position = projectionMatrix * modelViewMatrix * vec4(displacedPosition, 1.0); +} \ No newline at end of file diff --git a/app/components/grainy-liquid/shaders/vertex.glsl.d.ts b/app/components/grainy-liquid/shaders/vertex.glsl.d.ts new file mode 100644 index 00000000..f93db699 --- /dev/null +++ b/app/components/grainy-liquid/shaders/vertex.glsl.d.ts @@ -0,0 +1,2 @@ +declare const content: string +export default content \ No newline at end of file diff --git a/content/experiments/grainy-liquid.md b/content/experiments/grainy-liquid.md new file mode 100644 index 00000000..7f6ea923 --- /dev/null +++ b/content/experiments/grainy-liquid.md @@ -0,0 +1,214 @@ +--- +title: Grainy Liquid Blobs +author: alvarosabu +description: Animated liquid blobs with GLSL shaders, noise effects, and organic deformation inspired by oil drops in water +tags: ['shaders', 'glsl', 'liquid', 'noise', 'animation'] +date: 2025-08-13 +--- + + +A dynamic website design featuring animated liquid blobs created with custom GLSL shaders, demonstrating organic deformation and fluid-like behavior. + +## Technical Implementation + +### Architecture Overview + +The experiment consists of several key components working together to create the liquid blob effect: + +``` +app/components/grainy-liquid/ +├── index.global.vue # Main entry point +├── WebsiteLayout.vue # Full website layout with text overlay +├── Experience.vue # 3D scene setup +├── MultiBlob.vue # Multiple blob instances +├── Blob.vue # Individual blob component +└── shaders/ + ├── vertex.glsl # Vertex displacement shader + ├── fragment.glsl # Color and surface effects + ├── vertex.glsl.d.ts # TypeScript declarations + └── fragment.glsl.d.ts +``` + +### Shader System + +#### Vertex Shader (Displacement) + +The vertex shader creates organic liquid-like deformation using layered Simplex noise: + +```glsl +// Multiple noise layers for complex deformation +float noise1 = snoise(pos * u_frequency + time * 0.5); +float noise2 = snoise(pos * u_frequency * 2.0 + time * 0.3); +float noise3 = snoise(pos * u_frequency * 4.0 + time * 0.7); + +// Combine with different amplitudes for organic movement +float displacement = noise1 * 0.6 + noise2 * 0.3 + noise3 * 0.1; +vec3 displacedPosition = pos + normal * displacement * u_amplitude; +``` + +**Key Features:** +- **Simplex noise implementation** for smooth, organic deformation +- **Multi-layered noise** with different frequencies and speeds +- **Normal-based displacement** to maintain blob volume +- **Time-based animation** for continuous fluid movement + +#### Fragment Shader (Surface Effects) + +The fragment shader handles color mixing, surface effects, and visual texture: + +```glsl +// Fresnel effect for liquid-like rim lighting +float fresnel = 1.0 - dot(normal, viewDirection); +fresnel = pow(fresnel, u_fresnelPower); + +// Flowing color mixing with noise +vec2 flowUv = v_uv + u_time * 0.1; +float colorNoise = fbm(flowUv * u_noiseScale); +vec3 baseColor = mix(u_colorA, u_colorB, colorNoise); +vec3 finalColor = mix(baseColor, u_colorC, fresnel); + +// Grain texture for surface detail +float grain = random(v_uv * 100.0 + u_time * 0.02) * u_grainIntensity; +finalColor += vec3(grain); +``` + +**Key Features:** +- **Fresnel effect** for realistic edge lighting +- **Fractal Brownian Motion (FBM)** for organic color patterns +- **Multi-layer grain** for surface texture +- **Flowing color animation** synchronized with deformation +- **Iridescent highlights** for liquid shimmer + +### Component Architecture + +#### Blob Component (`Blob.vue`) + +Individual blob with customizable properties: + +```typescript +interface Props { + colorA?: string // Primary color + colorB?: string // Secondary color + colorC?: string // Highlight color + speed?: number // Animation speed + amplitude?: number // Deformation intensity +} +``` + +**Features:** +- **Interactive controls** via `useControls()` for real-time adjustment +- **Prop-based customization** for different blob variations +- **Shader uniform management** with reactive updates +- **Animation loop** using `useLoop()` composable + +#### MultiBlob Component (`MultiBlob.vue`) + +Manages multiple blob instances with varied configurations: + +```typescript +const blobs: BlobConfig[] = [ + { + position: [-4, 2, 0], + scale: [1.8, 1.8, 1.8], + colorA: '#6366f1', // Purple blob + colorB: '#8b5cf6', + colorC: '#ddd6fe', + speed: 0.6, + amplitude: 0.15 + }, + // Pink and gray blobs with different properties... +] +``` + +**Features:** +- **Varied positioning** for layered composition +- **Different scales** to create depth +- **Unique color palettes** per blob +- **Staggered animation speeds** for organic movement + +#### Website Layout (`WebsiteLayout.vue`) + +Complete website design with 3D background and text overlay: + +```vue + + + + + + + + + + + + + + + +``` + +**Features:** +- **Layered design** with 3D background and HTML overlay +- **Responsive typography** using TailwindCSS +- **Google Fonts integration** (Playfair Display serif) +- **Interactive controls** disabled for presentation mode + +### Technical Highlights + +#### Noise-Based Animation + +The liquid effect relies on sophisticated noise techniques: + +- **Simplex noise** for smooth, organic deformation patterns +- **Multiple octaves** combined for complex surface detail +- **Time-based offsets** creating flowing animation +- **Frequency variation** for different scales of movement + +#### Color System + +Realistic liquid color mixing: + +- **Fresnel-based rim lighting** mimicking surface tension +- **Flowing color patterns** using FBM noise +- **Multi-color blending** for realistic liquid appearance +- **Grain texture overlay** for surface detail + +#### Performance Optimization + +- **Efficient shader implementation** with clamped displacement +- **Instanced blob rendering** for multiple objects +- **Optimized geometry** using icosahedron base mesh +- **Controlled animation speeds** for smooth performance + +### Shader Mathematics + +The core deformation uses mathematical functions to simulate liquid behavior: + +```glsl +// Simplex noise for organic patterns +float snoise(vec3 v) { /* Implementation */ } + +// Fractal Brownian Motion for color variation +float fbm(vec2 st) { + float value = 0.0; + float amplitude = 0.5; + for (int i = 0; i < 5; i++) { + value += amplitude * noise(st * frequency); + frequency *= 2.0; + amplitude *= 0.5; + } + return value; +} +``` + +### Design Inspiration + +The visual style draws from modern liquid design trends: + +- **Organic blob shapes** popular in contemporary web design +- **Grainy texture effects** for tactile visual appeal +- **Gradient color schemes** with purple/pink palettes +- **Minimalist layout** focusing on typography and shapes + +This experiment demonstrates how GLSL shaders can create sophisticated visual effects in web applications, combining mathematical precision with organic, fluid aesthetics. \ No newline at end of file diff --git a/public/experiments/grainy-liquid.png b/public/experiments/grainy-liquid.png new file mode 100644 index 00000000..4192ef55 Binary files /dev/null and b/public/experiments/grainy-liquid.png differ
High res
grainy
abstract
shapes.