AI GENERATED

暗闇の中の桜吹雪

by touyou

PROMPT

桜の花びらが舞う春の嵐

DATE
2026/6/25
SOURCE
AI GENERATED
MODEL
gemini-2.5-flash
LIKES
0
VIEW SHADER CODE
mat2 rotate2D(float angle) {
    float c = cos(angle);
    float s = sin(angle);
    return mat2(c, -s, s, c);
}

// 1D hash from https://www.shadertoy.com/view/4djSRW
float hash11(float p) {
    p = fract(p * 0.1031);
    p *= p + 33.33;
    p = fract(p * p);
    return p;
}

// 2D hash (vec2 input, vec2 output) for grid cells
vec2 hash22(vec2 p) {
    vec3 p3 = fract(vec3(p.x, p.y, p.x + p.y) * vec3(0.1031, 0.1030, 0.0973));
    p3 += dot(p3, p3.yzx + 33.33);
    return fract((p3.xx + p3.yz) * p3.zy);
}

// Value noise from https://www.shadertoy.com/view/XdXBRH
float noise(vec2 p) {
    vec2 ip = floor(p);
    vec2 fp = fract(p);
    fp = fp * fp * (3.0 - 2.0 * fp); // Smoothstep interpolation

    float a = hash11(ip.x + ip.y * 13.0);
    float b = hash11(ip.x + 1.0 + ip.y * 13.0);
    float c = hash11(ip.x + (ip.y + 1.0) * 13.0);
    float d = hash11(ip.x + 1.0 + (ip.y + 1.0) * 13.0);

    return mix(mix(a, b, fp.x), mix(c, d, fp.x), fp.y);
}

// FBM (Fractal Brownian Motion)
float fbm(vec2 p) {
    float total = 0.0;
    float amplitude = 0.5;
    float frequency = 2.0;
    for (int i = 0; i < 5; i++) { // 5 octaves
        total += amplitude * noise(p * frequency);
        frequency *= 2.0;
        amplitude *= 0.5;
    }
    return total;
}

// Signed Distance Function for a simple petal shape
float sdPetal(vec2 p) {
    p.x = abs(p.x); // Make the petal symmetric about the y-axis
    p.y -= 0.1; // Shift origin so the tip is roughly at (0, 0.1) and base at (0, -0.1) relative to its internal coordinates
    p.x *= 0.6; // Make it thinner

    // Main body: large circle
    float d = length(p) - 0.15; 
    
    // Create a point at the top by blending with a smaller circle
    d = min(d, length(p - vec2(0.0, 0.1)) - 0.08); 
    
    // Flatten the base
    d = max(d, -p.y - 0.08); 
    
    return d;
}

void mainImage(out vec4 fragColor, in vec2 fragCoord) {
    vec2 uv = (fragCoord * 2.0 - iResolution.xy) / iResolution.y;
    vec2 aspect = iResolution.xy / iResolution.y;

    vec3 finalColor = vec3(0.0);

    // --- Background: Stormy atmosphere with FBM ---
    vec2 bg_uv = uv * 0.5 + iTime * 0.05;
    float bg_noise = fbm(bg_uv * 3.0 + fbm(bg_uv * 1.5 + iTime * 0.1) * 0.5);
    bg_noise = pow(bg_noise, 1.5) * 0.6 + 0.1; // Darken and add contrast

    vec3 skyColor1 = vec3(0.05, 0.1, 0.15) * (0.8 + 0.2 * sin(iTime * 0.3)); // Dark blue-grey, subtle time shift
    vec3 skyColor2 = vec3(0.15, 0.25, 0.3) * (0.8 + 0.2 * cos(iTime * 0.2)); // Lighter blue-grey, subtle time shift
    vec3 stormyColor = mix(skyColor1, skyColor2, bg_noise);

    finalColor += stormyColor;

    // --- Petals ---
    vec2 grid_scale = vec2(10.0, 15.0); // Denser grid for more petals, especially vertically
    vec2 cell_id = floor(uv * grid_scale);
    vec2 local_uv = fract(uv * grid_scale) - 0.5; // Local coordinates within cell, centered [-0.5, 0.5]

    // Iterate over neighboring cells to ensure petals crossing boundaries are rendered
    for (int y = -1; y <= 1; y++) {
        for (int x = -1; x <= 1; x++) {
            vec2 current_cell_id = cell_id + vec2(float(x), float(y));
            
            // Hash for unique petal properties per cell
            vec2 h = hash22(current_cell_id);

            // Petal's relative position within its cell, animated for a falling loop
            // fract() creates the looping falling effect
            vec2 petal_pos_in_cell = vec2(h.x * 2.0 - 1.0, fract(h.y * 10.0 + iTime * 0.2 + h.x * 0.5) * 2.0 - 1.0); // Range [-1, 1]
            
            // Add horizontal drift based on noise (wind-like)
            petal_pos_in_cell.x += (noise(current_cell_id * 0.1 + iTime * 0.1) - 0.5) * 0.5;
            
            // Position relative to petal's actual center
            vec2 p = local_uv - petal_pos_in_cell * 0.5; 
            
            // Petal rotation
            float angle = iTime * 0.3 + h.x * 10.0; // Base rotation speed and offset
            angle += (noise(current_cell_id * 0.2 + iTime * 0.5) - 0.5) * 2.0; // Wobble rotation
            mat2 rot = rotate2D(angle);

            // Petal scale, varied by hash for depth effect
            float scale = 0.05 + h.y * 0.03; 

            vec2 transformed_p = p * rot / scale;

            float d = sdPetal(transformed_p);

            float petal_smoothness = 0.01;
            float petal_alpha = smoothstep(petal_smoothness, -petal_smoothness, d);
            
            // Add a subtle glow/fringe effect around the petal
            petal_alpha += smoothstep(petal_smoothness * 2.0, petal_smoothness * 1.0, d) * 0.1 * (1.0 - petal_alpha);

            // Petal color, varied by hash and subtle pulsation
            vec3 petal_color = mix(vec3(1.0, 0.7, 0.8), vec3(1.0, 0.85, 0.95), h.x); // Pinkish white
            petal_color *= (0.8 + 0.2 * sin(iTime * 2.0 + h.y * 10.0)); // Subtle pulsating light

            finalColor = mix(finalColor, petal_color, petal_alpha);
        }
    }

    // --- Vignette ---
    vec2 v = (fragCoord - 0.5 * iResolution.xy) / iResolution.y;
    float vignette = smoothstep(0.8, 0.3, length(v * vec2(aspect.x / aspect.y, 1.0)));
    finalColor *= vignette;

    fragColor = vec4(finalColor, 1.0);
}

ZIP を展開して .playground を Xcode でダブルクリックし、Live View を表示して ▶ を押すとシェーダーがアニメーションします。Metal ソースは Resources/Shader.metal に独立ファイルとして入っているので、Xcode 上で直接編集できます。