SwiftUI Shader Simple Lens
With SwiftUI it is easy to add Metal shaders and with this example a simple lens effect is created. The simple lens shader is implemented in a `.metal` file. The function name is defined as `simpleLens`. To use it in SwiftUI, the modifier `.visualEffect` is used. `simpleLens` is automatically available as part of the `ShaderLibrary` type.
import SwiftUI
struct SampleShaderLensView: View {
@State private var loc = CGPoint.zero
var body: some View {
ZStack {
Image("mtfuji")
.resizable()
.scaledToFill()
Text("富士山\nMount Fuji")
.font(.largeTitle)
.bold()
}
.drawingGroup()
.visualEffect { content, proxy in
content
.layerEffect(
ShaderLibrary.simpleLens(
.float2(proxy.size.applying(CGAffineTransform(scaleX: 0.3, y: 0.3))),
.float2(loc),
.float(0.15),
.float(1.5)
),
maxSampleOffset: .zero
)
}
.gesture(
DragGesture(minimumDistance: 0)
.onChanged { loc = $0.location }
)
}
}
#Preview {
SampleShaderLensView()
}
#include
#include
using namespace metal;
[[ stitchable ]] half4 simpleLens(
float2 position,
SwiftUI::Layer layer,
float2 size,
float2 pos,
float maxDistance,
float zoomFactor
) {
half2 norm = half2(position / size);
half2 center = half2(pos / size);
half2 d = norm - center;
half ar = size.x / size.y;
half distance = (d.x * d.x) + (d.y * d.y) / ar / ar;
half zm = 1.0h;
if (distance < maxDistance) {
zm /= zoomFactor;
}
half2 p = d * zm + center;
return layer.sample(float2(p) * size);
}