Advertisement
Not a member of Pastebin yet?
Sign Up,
it unlocks many cool features!
- // === Orb Moving ===
- //join r/keisen for more source code, designs, other cool stuff
- // How to Use:
- // 1. Create a new SwiftUI project in Xcode.
- // 2. Replace the default ContentView.swift with the ContentView code below.
- // 3. Create a new Swift file named ShapeUniforms.swift and paste its code below.
- // 4. Create a new Swift file named UIEffectRenderer.swift and paste its code below.
- // 5. Create a new Swift file named UIEffectView.swift and paste its code below.
- // 6. Create a new Metal file named UIEffectShaders.metal and paste its code below.
- // 7. Add an image named exactly "trippywave_texture" to your Assets.xcassets (or change the name in UIEffectRenderer.swift).
- // 8. Build and Run! Drag up from the bottom to show sliders.
- //CONTENT VIEW, DO NOT COPY AND PASTE EVERYTHING IN ONE FILE! FOLLOW INSTRUCTIONS ABOVE
- import SwiftUI
- import simd
- struct ContentView: View {
- @State private var warp: Float = 1.8
- @State private var speed: Float = 0.1
- @State private var blend: Float = 0.3
- @State private var warm: Float = 0.6
- @State private var noise: Float = 4.0
- @State private var contrast: Float = 1.8
- @State private var radius: Float = 0.3
- @State private var panelOffset: CGFloat = UIScreen.main.bounds.height
- @State private var startDragOffset: CGFloat = UIScreen.main.bounds.height
- @State private var panelHeight: CGFloat = 400
- private var currentShapeUniforms: ShapeUniforms {
- ShapeUniforms(
- positionAndRadius: SIMD4<Float>(0, 0, 0, radius),
- params1: SIMD4<Float>(blend, warp, speed, warm),
- params2: SIMD4<Float>(noise, contrast, 0, 0)
- )
- }
- private func formatValue(_ value: Float) -> String {
- String(format: "%.2f", value)
- }
- var body: some View {
- GeometryReader { screenGeometry in
- ZStack(alignment: .bottom) {
- UIEffectView(shapeUniforms: currentShapeUniforms)
- .edgesIgnoringSafeArea(.all)
- .gesture(dragGesture(screenGeometry: screenGeometry))
- parameterPanel(screenGeometry: screenGeometry)
- .offset(y: panelOffset)
- .background(GeometryReader { panelGeo in
- Color.clear.onAppear {
- self.panelHeight = panelGeo.size.height
- let peekAmount: CGFloat = 50
- let maxOffset = max(0, self.panelHeight - peekAmount)
- self.panelOffset = maxOffset
- self.startDragOffset = self.panelOffset
- print("Panel Height: \(self.panelHeight), Initial Offset: \(self.panelOffset)")
- }
- })
- }
- .edgesIgnoringSafeArea(.bottom)
- }
- // .statusBar(hidden: true)
- }
- func dragGesture(screenGeometry: GeometryProxy) -> some Gesture {
- DragGesture(minimumDistance: 1, coordinateSpace: .global)
- .onChanged { value in
- let dragAmount = value.translation.height
- let targetOffset = startDragOffset + dragAmount
- let peekAmount: CGFloat = 50
- let maxOffset = max(0, panelHeight - peekAmount)
- panelOffset = max(0, min(maxOffset, targetOffset))
- }
- .onEnded { value in
- startDragOffset = panelOffset
- let predictedEndOffset = panelOffset + value.predictedEndTranslation.height
- let peekAmount: CGFloat = 50
- let maxOffset = max(0, panelHeight - peekAmount)
- let halfway = maxOffset / 2
- withAnimation(.interpolatingSpring(stiffness: 250, damping: 25)) {
- if predictedEndOffset > halfway {
- panelOffset = maxOffset
- } else {
- panelOffset = 0
- }
- }
- startDragOffset = panelOffset
- }
- }
- func parameterPanel(screenGeometry: GeometryProxy) -> some View {
- VStack(alignment: .leading, spacing: 10) {
- HStack {
- Text("Radius:")
- .frame(width: 60, alignment: .leading)
- Slider(value: $radius, in: 0.1...1.0)
- Text(formatValue(radius))
- .frame(width: 40, alignment: .trailing)
- }
- HStack {
- Text("Warp:")
- .frame(width: 60, alignment: .leading)
- Slider(value: $warp, in: 0.0...5.0)
- Text(formatValue(warp))
- .frame(width: 40, alignment: .trailing)
- }
- HStack {
- Text("Speed:")
- .frame(width: 60, alignment: .leading)
- Slider(value: $speed, in: 0.0...1.0)
- Text(formatValue(speed))
- .frame(width: 40, alignment: .trailing)
- }
- HStack {
- Text("Blend:")
- .frame(width: 60, alignment: .leading)
- Slider(value: $blend, in: 0.0...1.0)
- Text(formatValue(blend))
- .frame(width: 40, alignment: .trailing)
- }
- HStack {
- Text("Warm:")
- .frame(width: 60, alignment: .leading)
- Slider(value: $warm, in: 0.0...2.0)
- Text(formatValue(warm))
- .frame(width: 40, alignment: .trailing)
- }
- HStack {
- Text("Noise:")
- .frame(width: 60, alignment: .leading)
- Slider(value: $noise, in: 0.1...10.0)
- Text(formatValue(noise))
- .frame(width: 40, alignment: .trailing)
- }
- HStack {
- Text("Contrast:")
- .frame(width: 60, alignment: .leading)
- Slider(value: $contrast, in: 0.5...5.0)
- Text(formatValue(contrast))
- .frame(width: 40, alignment: .trailing)
- }
- RoundedRectangle(cornerRadius: 3)
- .fill(Color.gray.opacity(0.5))
- .frame(width: 40, height: 6)
- .padding(.vertical, 5)
- .frame(maxWidth: .infinity, alignment: .center)
- }
- .padding(.horizontal)
- .padding(.top, 15)
- .padding(.bottom, screenGeometry.safeAreaInsets.bottom + 10)
- .background(.regularMaterial)
- .cornerRadius(20, corners: [.topLeft, .topRight])
- .foregroundColor(.primary)
- .font(.system(size: 12))
- }
- }
- extension View {
- func cornerRadius(_ radius: CGFloat, corners: UIRectCorner) -> some View {
- clipShape( RoundedCorner(radius: radius, corners: corners) )
- }
- }
- struct RoundedCorner: Shape {
- var radius: CGFloat = .infinity
- var corners: UIRectCorner = .allCorners
- func path(in rect: CGRect) -> Path {
- let path = UIBezierPath(roundedRect: rect, byRoundingCorners: corners, cornerRadii: CGSize(width: radius, height: radius))
- return Path(path.cgPath)
- }
- }
- struct ContentView_Previews: PreviewProvider {
- static var previews: some View {
- ContentView()
- .preferredColorScheme(.dark)
- }
- }
- //WARNING//
- // SEPARATE FILE Shaders.metal
- //WARNING//
- #include <metal_stdlib>
- using namespace metal;
- #define MAX_STEPS 100
- #define MAX_DIST 100.0f
- #define HIT_THRESHOLD 0.001f
- #define NORMAL_EPSILON 0.001f
- #define M_PI_F 3.141592653589793f
- struct VertexOut { float4 position [[position]]; float2 uv; };
- struct Uniforms { float time; float2 resolution; };
- struct ShapeUniforms { float4 positionAndRadius; float4 params1; float4 params2; };
- float3 mod289(float3 x) { return x - floor(x * (1.0 / 289.0)) * 289.0; }
- float2 mod289(float2 x) { return x - floor(x * (1.0 / 289.0)) * 289.0; }
- float3 permute(float3 x) { return mod289(((x*34.0)+1.0)*x); }
- float snoise(float2 v) { const float4 C = float4(0.211324865405187, 0.366025403784439, -0.577350269189626, 0.024390243902439); float2 i = floor(v + dot(v, C.yy) ); float2 x0 = v - i + dot(i, C.xx); float2 i1 = (x0.x > x0.y) ? float2(1.0f, 0.0f) : float2(0.0f, 1.0f); float2 x1 = x0.xy + C.xx - i1; float2 x2 = x0.xy + C.zz; i = mod289(i); float3 p = permute( permute( i.y + float3(0.0f, i1.y, 1.0f )) + i.x + float3(0.0f, i1.x, 1.0f )); float3 m = max(0.5f - float3(dot(x0,x0), dot(x1,x1), dot(x2,x2)), 0.0f); m = m*m; m = m*m; float3 x = 2.0f * fract(p * C.www) - 1.0f; float3 h = abs(x) - 0.5f; float3 ox = floor(x + 0.5f); float3 a0 = x - ox; float3 g; g.x=a0.x*x0.x+h.x*x0.y; g.y=a0.y*x1.x+h.y*x1.y; g.z=a0.z*x2.x+h.z*x2.y; return 130.0f * dot(m, g); }
- float fbm_snoise(float2 p, int octaves) { float value = 0.0f; float amplitude = 0.5f; float frequency = 1.0f; for (int i = 0; i < octaves; ++i) { value += amplitude * (snoise(p * frequency) * 0.5f + 0.5f); frequency *= 2.0f; amplitude *= 0.5f; } return clamp(value, 0.0f, 1.0f); }
- float3 simpleDomainWarp(float2 uv, float scaled_time, float warpIntensity, int octaves) { float time1 = scaled_time * 1.0f; float time2 = scaled_time * 1.3f; float2 q = float2(fbm_snoise(uv + float2(0.0f, 0.0f) + time1, octaves), fbm_snoise(uv + float2(5.2f, 1.3f) + time2, octaves)); q = q * 2.0f - 1.0f; float2 warped_uv = uv + warpIntensity * q; float final_fbm = fbm_snoise(warped_uv + float2(1.7f, 9.2f) + scaled_time * 0.7f, octaves); return float3(final_fbm, warped_uv.x, warped_uv.y); }
- float sdSphere(float3 p, float3 center, float radius) { return length(p - center) - radius; }
- float mapScene(float3 p, constant ShapeUniforms& shapeUniforms) { float3 spherePos = shapeUniforms.positionAndRadius.xyz; float sphereRadius = shapeUniforms.positionAndRadius.w; float sphereDist = sdSphere(p, spherePos, sphereRadius); return sphereDist; }
- float3 calcNormal(float3 p, constant ShapeUniforms& shapeUniforms) { float2 e = float2(NORMAL_EPSILON, 0.0); return normalize(float3(mapScene(p + e.xyy, shapeUniforms) - mapScene(p - e.xyy, shapeUniforms), mapScene(p + e.yxy, shapeUniforms) - mapScene(p - e.yxy, shapeUniforms), mapScene(p + e.yyx, shapeUniforms) - mapScene(p - e.yyx, shapeUniforms))); }
- float calcLighting(float3 pos, float3 normal, float3 lightDir) { float diffuse = max(0.0f, dot(normal, lightDir)); float ambient = 0.2f; return ambient + diffuse * 0.8f; }
- float3 calcBackground(float2 screen_uv, constant Uniforms& uniforms, constant ShapeUniforms& shapeUniforms, texture2d<half, access::sample> tex, sampler samp) {
- float aspect = uniforms.resolution.x / uniforms.resolution.y; float textureBlendFactor = shapeUniforms.params1.x; float warpIntensity = 3.0f; float animationSpeed = shapeUniforms.params1.z; float warmColorIntensity = shapeUniforms.params1.w; float noiseScale = shapeUniforms.params2.x; float contrastExponent = shapeUniforms.params2.y; float zoom = noiseScale * 0.8f; float time = (uniforms.time * animationSpeed); float warpTime = time * 0.06f; int octaves = 6; float2 uv_start_raw = (screen_uv - 0.5f) * float2(aspect, 1.0f) * zoom; float driftSpeedX = 0.03f; float driftSpeedY = 0.015f; float2 driftOffset = float2(time * driftSpeedX, time * driftSpeedY); float2 uv_start = uv_start_raw + driftOffset; float3 warp_result = simpleDomainWarp(uv_start, warpTime, warpIntensity, octaves); float f_raw = warp_result.x; float2 warped_uv = warp_result.yz; float f = pow(f_raw, contrastExponent); float3 color_deep_bg=float3(0.01f,0.02f,0.04f); float3 color_mid_cool=float3(0.1f,0.35f,0.5f); float3 color_warm_tint=float3(0.5f,0.3f,0.15f); float3 color_bright_cool=float3(0.7f,0.9f,1.0f); float3 color_highlight=float3(1.0f,1.0f,1.0f); float3 procedural_color; procedural_color=mix(color_deep_bg,color_mid_cool,smoothstep(0.05f,0.55f,f)); float warm_strength = warmColorIntensity; procedural_color=mix(procedural_color,color_warm_tint,smoothstep(0.25f,0.5f,f)*(1.0f-smoothstep(0.45f,0.65f,f))*warm_strength); float bright_cool_strength=0.9f; procedural_color=mix(procedural_color,color_bright_cool,smoothstep(0.55f,0.8f,f)*bright_cool_strength); float highlight_strength=1.0f; procedural_color=mix(procedural_color,color_highlight,smoothstep(0.8f,0.9f,f)*highlight_strength); float texture_scale = 1.0f; float2 warped_uv_normalized = warped_uv / zoom / float2(aspect, 1.0) + 0.5; float2 texture_uv = fract(warped_uv_normalized * texture_scale + screen_uv * texture_scale * 0.3f); half4 texColor = tex.sample(samp, texture_uv); float texture_mix_factor = textureBlendFactor; float3 combined_color = mix(procedural_color, float3(texColor.rgb), texture_mix_factor); combined_color *= (0.95f + f * 0.25f); float vignette_radius=0.7f; float vignette_smoothness=0.6f; float dist_from_center=distance(screen_uv,float2(0.5f)); float vignette=smoothstep(vignette_radius,vignette_radius - vignette_smoothness,dist_from_center); combined_color *= vignette; return combined_color;
- }
- vertex VertexOut ui_effect_vertex( uint vertexID [[vertex_id]], constant float4* positions [[buffer(0)]] ) {
- VertexOut out; out.position = positions[vertexID];
- out.uv = float2(out.position.x * 0.5f + 0.5f, out.position.y * -0.5f + 0.5f); return out;
- }
- // This is the main GPU function that runs for every pixel!
- fragment half4 ui_effect_fragment(
- VertexOut in [[stage_in]],
- constant Uniforms& uniforms [[buffer(1)]],
- constant ShapeUniforms& shapeUniforms [[buffer(2)]],
- texture2d<half, access::sample> bgTexture [[texture(0)]],
- sampler bgSampler [[sampler(0)]]
- ) {
- float2 uv = in.uv; float aspect = uniforms.resolution.x / uniforms.resolution.y;
- float2 screenPos = (uv * 2.0f - 1.0f) * float2(aspect, 1.0f);
- float3 ro = float3(screenPos.x, screenPos.y, 2.0f); float3 rd = float3(0.0f, 0.0f, -1.0f);
- float total_dist = 0.0f; float3 p = ro; bool hit = false;
- for (int i = 0; i < MAX_STEPS; ++i) { float d = mapScene(p, shapeUniforms); if (d < HIT_THRESHOLD) { hit = true; p += rd * d; break; } total_dist += d; p += rd * d; if (total_dist > MAX_DIST) { break; } }
- // Grab all the parameters sent from the Swift code (sliders etc).
- float textureBlendFactor = shapeUniforms.params1.x; float warpIntensity = shapeUniforms.params1.y; float animationSpeed = shapeUniforms.params1.z; float warmColorIntensity = shapeUniforms.params1.w; float noiseScale = shapeUniforms.params2.x; float contrastExponent = shapeUniforms.params2.y;
- float scaledTime = uniforms.time * animationSpeed;
- float3 final_color;
- // Did our ray marching hit the sphere or just the background?
- if (hit) {
- float3 normal = calcNormal(p, shapeUniforms);
- float3 n = normal; float phi = acos(clamp(n.y, -1.0f, 1.0f)); float theta = atan2(n.z, n.x);
- float2 sphereUV_raw; sphereUV_raw.x = theta / (2.0f * M_PI_F) + 0.5f; sphereUV_raw.y = phi / M_PI_F;
- float rotationSpeed = -0.05;
- float rotationOffset = scaledTime * rotationSpeed;
- float2 sphereUV = sphereUV_raw;
- sphereUV.x = sphereUV.x + rotationOffset;
- float sphereTime = scaledTime * 0.07f; float sphereNoiseScale = noiseScale; float sphereWarpIntensity = warpIntensity; int sphereOctaves = 6;
- float3 sphereWarpResult = simpleDomainWarp(sphereUV * sphereNoiseScale, sphereTime, sphereWarpIntensity, sphereOctaves);
- float baseSphereNoiseRaw = sphereWarpResult.x; float2 warpedSphereUV_raw = sphereWarpResult.yz;
- float f = pow(baseSphereNoiseRaw, contrastExponent);
- float3 color_deep_bg=float3(0.01f,0.02f,0.04f); float3 color_mid_cool=float3(0.1f,0.35f,0.5f); float3 color_warm_tint=float3(0.5f,0.3f,0.15f); float3 color_bright_cool=float3(0.7f,0.9f,1.0f); float3 color_highlight=float3(1.0f,1.0f,1.0f);
- float3 proceduralSphereColor; proceduralSphereColor=mix(color_deep_bg,color_mid_cool,smoothstep(0.05f,0.55f,f)); float warm_strength = warmColorIntensity; proceduralSphereColor=mix(proceduralSphereColor,color_warm_tint,smoothstep(0.25f,0.5f,f)*(1.0f-smoothstep(0.45f,0.65f,f))*warm_strength); float bright_cool_strength=0.9f; proceduralSphereColor=mix(proceduralSphereColor,color_bright_cool,smoothstep(0.55f,0.8f,f)*bright_cool_strength); float highlight_strength=1.0f; proceduralSphereColor=mix(proceduralSphereColor,color_highlight,smoothstep(0.8f,0.9f,f)*highlight_strength);
- float texture_scale = 1.2f; float2 warpedSphereUV_normalized = warpedSphereUV_raw / sphereNoiseScale;
- float2 sphereTextureUV = fract(warpedSphereUV_normalized * texture_scale + sphereUV * texture_scale * 0.3f);
- half4 texColor = bgTexture.sample(bgSampler, sphereTextureUV);
- float texture_mix_factor = textureBlendFactor; float3 combinedSphereColor = mix(proceduralSphereColor, float3(texColor.rgb), texture_mix_factor);
- float3 sphereBaseColor = combinedSphereColor * (1.1f + f * 0.5f);
- float3 viewDir = -rd; float dotNV = dot(normal, viewDir); float rimPower = 3.5f; float fresnel = pow(1.0f - max(dotNV, 0.0f), rimPower); float3 rimColor = float3(0.9f, 1.0f, 1.1f); float rimIntensity = 0.6f; float3 rimContribution = rimColor * fresnel * rimIntensity;
- final_color = sphereBaseColor + rimContribution;
- } else {
- final_color = calcBackground(in.uv, uniforms, shapeUniforms, bgTexture, bgSampler);
- }
- return half4(half3(clamp(final_color, 0.0f, 1.0f)), 1.0h);
- }
- //WARNING//
- // SEPERATE FILE ShapeUniforms.swift
- //WARNING//
- import simd
- struct ShapeUniforms: Equatable {
- var positionAndRadius: SIMD4<Float>
- var params1: SIMD4<Float>
- var params2: SIMD4<Float>
- // adjust default values as needed
- init(positionAndRadius: SIMD4<Float> = SIMD4<Float>(0, 0, 0, 0.5),
- params1: SIMD4<Float> = SIMD4<Float>(0.5, 1.8, 0.1, 0.6),
- params2: SIMD4<Float> = SIMD4<Float>(4.0, 1.8, 0.0, 0.0)) {
- self.positionAndRadius = positionAndRadius
- self.params1 = params1
- self.params2 = params2
- }
- }
- //WARNING//
- // SEPERATE FILE UIEffectRenderer.swift
- //WARNING//
- import MetalKit
- import simd
- struct UIEffectUniforms {
- var time: Float
- var resolution: SIMD2<Float>
- }
- class UIEffectRenderer: NSObject, MTKViewDelegate {
- let device: MTLDevice
- let commandQueue: MTLCommandQueue
- let pipelineState: MTLRenderPipelineState
- let vertexBuffer: MTLBuffer
- var uniforms: UIEffectUniforms
- let uniformsBuffer: MTLBuffer
- private(set) var currentShapeUniforms: ShapeUniforms = ShapeUniforms()
- private let shapeUniformsBuffer: MTLBuffer
- let texture: MTLTexture
- let samplerState: MTLSamplerState
- let startTime: Date
- init?(metalKitView: MTKView) {
- guard let device = MTLCreateSystemDefaultDevice(),
- let commandQueue = device.makeCommandQueue() else {
- print("UIEffectRenderer Error: Metal is not supported.")
- return nil //gotta bail
- }
- self.device = device
- self.commandQueue = commandQueue
- self.startTime = Date()
- metalKitView.device = device
- metalKitView.colorPixelFormat = .bgra8Unorm
- metalKitView.clearColor = MTLClearColor(red: 0, green: 0, blue: 0, alpha: 1)
- metalKitView.isPaused = false
- metalKitView.enableSetNeedsDisplay = false
- do {
- let library = try device.makeDefaultLibrary(bundle: .main)
- guard let vertexFunction = library.makeFunction(name: "ui_effect_vertex"),
- let fragmentFunction = library.makeFunction(name: "ui_effect_fragment") else {
- print("UIEffectRenderer Error: Shader function 'ui_effect_vertex' or 'ui_effect_fragment' not found in default library. Check .metal file and function names.")
- return nil
- }
- let pipelineDescriptor = MTLRenderPipelineDescriptor()
- pipelineDescriptor.label = "SDF Effect Pipeline"
- pipelineDescriptor.vertexFunction = vertexFunction
- pipelineDescriptor.fragmentFunction = fragmentFunction
- pipelineDescriptor.colorAttachments[0].pixelFormat = metalKitView.colorPixelFormat
- self.pipelineState = try device.makeRenderPipelineState(descriptor: pipelineDescriptor)
- } catch {
- print("UIEffectRenderer Error: Failed to create Metal pipeline state: \(error). Check shader compilation.")
- return nil
- }
- let quadVertices: [SIMD4<Float>] = [ .init(-1,-1,0,1), .init(1,-1,0,1), .init(-1,1,0,1), .init(1,-1,0,1), .init(1,1,0,1), .init(-1,1,0,1) ]
- guard let createdVertexBuffer = device.makeBuffer(bytes: quadVertices, length: quadVertices.count * MemoryLayout<SIMD4<Float>>.stride, options: .storageModeShared) else { print("UIEffectRenderer Error: Could not create vertex buffer."); return nil }
- self.vertexBuffer = createdVertexBuffer
- self.vertexBuffer.label = "Quad Vertices"
- let textureLoader = MTKTextureLoader(device: device)
- let textureLoaderOptions: [MTKTextureLoader.Option: Any] = [.origin: MTKTextureLoader.Origin.topLeft, .generateMipmaps: true, .SRGB: false ]
- do {
- let textureName = "trippywave_texture"
- self.texture = try textureLoader.newTexture(name: textureName, scaleFactor: metalKitView.contentScaleFactor, bundle: nil, options: textureLoaderOptions)
- self.texture.label = "\(textureName) Texture"
- } catch {
- print("UIEffectRenderer Error: Failed loading texture '\("trippywave_texture")': \(error). Check Assets.xcassets.")
- return nil
- }
- let samplerDescriptor = MTLSamplerDescriptor()
- samplerDescriptor.label = "BG Sampler"; samplerDescriptor.minFilter = .linear; samplerDescriptor.magFilter = .linear; samplerDescriptor.mipFilter = .linear; samplerDescriptor.sAddressMode = .repeat; samplerDescriptor.tAddressMode = .repeat
- guard let createdSamplerState = device.makeSamplerState(descriptor: samplerDescriptor) else { print("UIEffectRenderer Error: Failed to create sampler state."); return nil }
- self.samplerState = createdSamplerState
- self.uniforms = UIEffectUniforms(time: 0.0, resolution: .zero)
- let uniformBufferSize = MemoryLayout<UIEffectUniforms>.stride
- guard let createdUniformsBuffer = device.makeBuffer(length: uniformBufferSize, options: .storageModeShared) else { print("UIEffectRenderer Error: Could not create uniforms buffer."); return nil }
- self.uniformsBuffer = createdUniformsBuffer
- self.uniformsBuffer.label = "Uniforms Buffer (Time/Res)"
- let shapeUniformsSize = MemoryLayout<ShapeUniforms>.stride
- guard let createdShapeBuffer = device.makeBuffer(length: shapeUniformsSize, options: .storageModeShared) else { print("UIEffectRenderer Error: Could not create shape uniforms buffer."); return nil }
- self.shapeUniformsBuffer = createdShapeBuffer
- self.shapeUniformsBuffer.label = "Shape Uniforms Buffer"
- memset(self.shapeUniformsBuffer.contents(), 0, shapeUniformsSize)
- super.init()
- print("UIEffectRenderer initialized successfully.")
- }
- func updateShapeUniforms(_ newUniforms: ShapeUniforms) {
- self.currentShapeUniforms = newUniforms
- }
- func mtkView(_ view: MTKView, drawableSizeWillChange size: CGSize) {
- uniforms.resolution = SIMD2<Float>(Float(size.width), Float(size.height))
- print("UIEffectRenderer: Drawable size changed to \(size.width)x\(size.height)")
- }
- func draw(in view: MTKView) {
- guard let commandBuffer = commandQueue.makeCommandBuffer(),
- let currentDrawable = view.currentDrawable,
- let renderPassDescriptor = view.currentRenderPassDescriptor else {
- return
- }
- commandBuffer.label = "Effect Command Buffer"
- guard let renderEncoder = commandBuffer.makeRenderCommandEncoder(descriptor: renderPassDescriptor) else {
- print("UIEffectRenderer Warning: Could not create render command encoder.")
- return
- }
- renderEncoder.label = "Effect Render Encoder"
- uniforms.time = Float(Date().timeIntervalSince(startTime))
- uniformsBuffer.contents().copyMemory(from: &uniforms, byteCount: MemoryLayout<UIEffectUniforms>.stride)
- var animatedShapeUniforms = currentShapeUniforms
- let floatSpeed: Float = 0.25
- let floatAmplitude: Float = 0.04
- let yOffset = sin(uniforms.time * floatSpeed * 2.0 * Float.pi) * floatAmplitude
- animatedShapeUniforms.positionAndRadius.y += yOffset
- // Slap the latest shape data (with the floaty animation) into the GPU buffer
- let shapeDataPtr = shapeUniformsBuffer.contents().bindMemory(to: ShapeUniforms.self, capacity: 1)
- shapeDataPtr[0] = animatedShapeUniforms
- renderEncoder.setRenderPipelineState(pipelineState)
- renderEncoder.setVertexBuffer(vertexBuffer, offset: 0, index: 0)
- renderEncoder.setFragmentBuffer(uniformsBuffer, offset: 0, index: 1)
- renderEncoder.setFragmentBuffer(shapeUniformsBuffer, offset: 0, index: 2) // This sends our shape struct data!
- renderEncoder.setFragmentTexture(texture, index: 0)
- renderEncoder.setFragmentSamplerState(samplerState, index: 0)
- renderEncoder.drawPrimitives(type: .triangle, vertexStart: 0, vertexCount: 6) // Draw the actual fullscreen quad.
- renderEncoder.endEncoding()
- commandBuffer.present(currentDrawable)
- commandBuffer.commit()
- }
- }
- //WARNING//
- // SEPERATE FILE UIEffectView.swift
- //WARNING//
- import SwiftUI
- import MetalKit
- struct UIEffectView: UIViewRepresentable {
- // holds all the parameters we get from ContentView for the shader
- var shapeUniforms: ShapeUniforms
- @StateObject private var coordinator = Coordinator()
- func makeUIView(context: Context) -> MTKView {
- print("UIEffectView: makeUIView")
- let mtkView = MTKView()
- mtkView.delegate = context.coordinator
- mtkView.enableSetNeedsDisplay = false
- mtkView.isPaused = false
- context.coordinator.setupRenderer(metalKitView: mtkView)
- guard let renderer = context.coordinator.renderer else {
- print("UIEffectView Error: Renderer is nil after setup attempt in makeUIView.")
- return mtkView
- }
- mtkView.device = renderer.device
- print("UIEffectView: MTKView device configured.")
- renderer.updateShapeUniforms(shapeUniforms)
- return mtkView
- }
- func updateUIView(_ uiView: MTKView, context: Context) {
- print("UIEffectView: updateUIView")
- context.coordinator.renderer?.updateShapeUniforms(shapeUniforms)
- }
- func makeCoordinator() -> Coordinator {
- print("UIEffectView: makeCoordinator")
- return coordinator
- }
- class Coordinator: NSObject, MTKViewDelegate, ObservableObject {
- var renderer: UIEffectRenderer?
- override init() {
- print("Coordinator: init")
- super.init()
- }
- func setupRenderer(metalKitView: MTKView) {
- print("Coordinator: Setting up renderer...")
- self.renderer = UIEffectRenderer(metalKitView: metalKitView)
- if self.renderer == nil {
- print("Coordinator: Renderer initialization FAILED.")
- } else {
- print("Coordinator: Renderer initialized successfully.")
- }
- }
- func mtkView(_ view: MTKView, drawableSizeWillChange size: CGSize) {
- renderer?.mtkView(view, drawableSizeWillChange: size)
- }
- func draw(in view: MTKView) {
- renderer?.draw(in: view)
- }
- }
- }
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement