Skip to content

Instantly share code, notes, and snippets.

@vedantgurav
Last active May 28, 2024 08:38
Show Gist options
  • Save vedantgurav/fff05d316d620a34d70361983c3b2bd3 to your computer and use it in GitHub Desktop.
Save vedantgurav/fff05d316d620a34d70361983c3b2bd3 to your computer and use it in GitHub Desktop.
Apple Sports effect using Metal Shaders
//
// FlagShader.metal
// VideoCamera
//
// Created by Vedant Gurav on 21/02/2024.
//
#include <metal_stdlib>
#include <SwiftUI/SwiftUI_Metal.h>
using namespace metal;
[[ stitchable ]] float2 ripple(float2 position, float time, float2 size, float speed, float strength, float frequency, float twist, float offset) {
float2 normalizedPosition = position / size;
float wave = sin(
(time + offset) * speed + position.x * frequency - position.y * frequency * twist
);
position.x += normalizedPosition.x + wave * strength;
return position;
}
[[ stitchable ]] half4 rippleColor(float2 position, half4 currentColor, float time, float2 size, float speed, float strength, float frequency, float twist) {
float wave = sin(
time * speed + position.x * frequency - position.y * frequency * twist
);
float brightnessFactor = strength * wave + 1;
half4 adjustedColor = half4(currentColor.rgb * brightnessFactor, currentColor.a);
return adjustedColor;
}
//
// FlagView.swift
// VideoCamera
//
// Created by Vedant Gurav on 21/02/2024.
//
import SwiftUI
struct FlagView: View {
let leftColor = Color(hue: 1, saturation: 0.8, brightness: 0.7)
let rightColor = Color(hue: 0.6, saturation: 0.9, brightness: 0.8)
let startDate = Date()
var body: some View {
TimelineView(.animation) { _ in
ZStack {
LinearGradient(stops: [
.init(color: leftColor, location: 0),
.init(color: leftColor, location: 0.35),
.init(color: leftColor.opacity(0.4), location: 0.6),
.init(color: .clear, location: 0.6)
], startPoint: .leading, endPoint: .trailing)
LinearGradient(stops: [
.init(color: .clear, location: 0.3),
.init(color: rightColor.opacity(0.2), location: 0.4),
.init(color: rightColor, location: 0.65),
.init(color: rightColor, location: 1),
], startPoint: .leading, endPoint: .trailing)
}
.background(.black)
.blur(radius: 20)
.overlay {
Rectangle()
.fill(ImagePaint(image: Image("cloth"), scale: 1.5))
.tint(.yellow)
.blendMode(.softLight)
.padding(-100)
.rotationEffect(.degrees(-20))
.blur(radius: 0.4)
}
.scaleEffect(1.2)
.visualEffect { content, proxy in
content
.colorEffect(
ShaderLibrary.rippleColor(
.float(startDate.timeIntervalSinceNow),
.float2(proxy.size),
.float(2),
.float(0.2),
.float(0.025),
.float(0.25)
)
)
}
.drawingGroup()
.visualEffect { content, proxy in
content
.distortionEffect(
ShaderLibrary.ripple(
.float(startDate.timeIntervalSinceNow),
.float2(proxy.size),
.float(2),
.float(8),
.float(0.025),
.float(0.25),
.float(0.75)
),
maxSampleOffset: .zero
)
}
.ignoresSafeArea(edges: .all)
.statusBar(hidden: true)
.persistentSystemOverlays(.hidden)
}
}
}
#Preview {
FlagView()
}
@vedantgurav
Copy link
Author

vedantgurav commented Feb 21, 2024

Inspired by the Apple Sports app

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment