#version 330 in vec2 fragWorldPos; out vec4 finalColor; uniform float time; uniform float cloudScale = 0.018; uniform vec2 driftDirection = vec2(1.0, 0.35); uniform float driftSpeed = 4.0; uniform float coverage = 0.99; uniform float softness = 0.12; uniform vec4 cloudColor = vec4(1.0, 1.0, 1.0, 0.8); uniform bool isShadowPass = false; uniform vec2 shadowWorldOffset = vec2(48.0, 72.0); uniform vec4 shadowColor = vec4(0.0, 0.05, 0.1, 0.35); vec2 pixelizeCoordinates(vec2 coordinates) { return floor(coordinates); } float hash(vec2 p) { p = fract(p * vec2(123.34, 456.21)); p += dot(p, p + 45.32); return fract(p.x * p.y); } float valueNoise(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(a, b, u.x) + (c - a) * u.y * (1.0 - u.x) + (d - b) * u.x * u.y; } float cloudField(vec2 p) { float value = 0.0; float amplitude = 0.5; for (int i = 0; i < 4; i++) { value += amplitude * valueNoise(p); p *= 2.0; amplitude *= 0.5; } return value; } void main() { vec2 worldPos = fragWorldPos; if (isShadowPass) { worldPos -= shadowWorldOffset; } vec2 pixelized = pixelizeCoordinates(worldPos); vec2 drift = normalize(driftDirection) * time * driftSpeed; float density = cloudField((pixelized + drift) * cloudScale); float mask = smoothstep(coverage - softness, coverage + softness, density); vec4 layerColor = isShadowPass ? shadowColor : cloudColor; finalColor = vec4(layerColor.rgb, layerColor.a * mask); }