使用MetalView和SwiftUI?我如何在其中显示内容?

8wtpewkr  于 5个月前  发布在  Swift
关注(0)|答案(4)|浏览(87)

我被SwiftUI和Metal卡住了,几乎要给予了。
我从https://developer.apple.com/forums/thread/119112?answerId=654964022#654964022得到这个例子:

import MetalKit
struct MetalView: NSViewRepresentable {
    func makeCoordinator() -> Coordinator {
        Coordinator(self)
    }
    func makeNSView(context: NSViewRepresentableContext<MetalView>) -> MTKView {
        let mtkView = MTKView()
        mtkView.delegate = context.coordinator
        mtkView.preferredFramesPerSecond = 60
        mtkView.enableSetNeedsDisplay = true
        if let metalDevice = MTLCreateSystemDefaultDevice() {
            mtkView.device = metalDevice
        }
        mtkView.framebufferOnly = false
        mtkView.clearColor = MTLClearColor(red: 0, green: 0, blue: 0, alpha: 0)
        mtkView.drawableSize = mtkView.frame.size
        mtkView.enableSetNeedsDisplay = true
        return mtkView
    }
    func updateNSView(_ nsView: MTKView, context: NSViewRepresentableContext<MetalView>) {
    }
    class Coordinator : NSObject, MTKViewDelegate {
        var parent: MetalView
        var metalDevice: MTLDevice!
        var metalCommandQueue: MTLCommandQueue!
        
        init(_ parent: MetalView) {
            self.parent = parent
            if let metalDevice = MTLCreateSystemDefaultDevice() {
                self.metalDevice = metalDevice
            }
            self.metalCommandQueue = metalDevice.makeCommandQueue()!
            super.init()
        }
        func mtkView(_ view: MTKView, drawableSizeWillChange size: CGSize) {
        }
        func draw(in view: MTKView) {
            guard let drawable = view.currentDrawable else {
                return
            }
            let commandBuffer = metalCommandQueue.makeCommandBuffer()
            let rpd = view.currentRenderPassDescriptor
            rpd?.colorAttachments[0].clearColor = MTLClearColorMake(0, 1, 0, 1)
            rpd?.colorAttachments[0].loadAction = .clear
            rpd?.colorAttachments[0].storeAction = .store
            let re = commandBuffer?.makeRenderCommandEncoder(descriptor: rpd!)
            re?.endEncoding()
            commandBuffer?.present(drawable)
            commandBuffer?.commit()
        }
    }
}

字符串
.但是我不知道如何使用MetalView()来显示数据,当我从SwiftUI视图调用它时,它似乎确实可以工作。我想用它来显示一个CIImage,它将被CIFilters过滤和操作.
有人可以请指出我在正确的方向上如何告诉这个视图如何显示的东西?我想我需要它来显示纹理的内容,但尝试了无数个小时,最终从零开始更无数次...
这就是我现在运行我的图像过滤器的方式,但它导致滑块非常慢,这就是为什么我决定尝试学习Metal..

func ciExposure (inputImage: CIImage, inputEV: Double) -> CIImage {
    let filter = CIFilter(name: "CIExposureAdjust")!
    filter.setValue(inputImage, forKey: kCIInputImageKey)
    filter.setValue(inputEV, forKey: kCIInputEVKey)
    return filter.outputImage!
}


我想我需要把那个filter.outputImage以某种方式传递给MetalView?
任何帮助都是非常,非常感谢的...

tyu7yeag

tyu7yeag1#

Apple的WWDC 2022包含了一个名为“使用Core Image、Metal和SwiftUI显示EDR内容”的教程/视频,其中描述了如何将Core Image与Metal和SwiftUI混合。它指向了一些名为“使用Core Image渲染目标生成动画”(here)的新示例代码。
这个示例项目非常以CoreImage为中心(这应该很适合你的目的),但我希望Apple能发布更多的示例代码示例,展示Metal与SwiftUI的集成。

gr8qqesn

gr8qqesn2#

我有一个小的Core Image + SwiftUI示例项目on Github,它可能是一个很好的起点。它还没有涵盖很多内容,但它已经演示了如何显示过滤的相机帧。
特别是查看视图的draw函数。它用于将CIImage渲染为MTKView(您可以在委托的draw函数中执行相同的操作)。

m1m5dgzv

m1m5dgzv3#

好吧,这对我来说是个小把戏:

func draw(in view: MTKView) {
            guard let drawable = view.currentDrawable else {
                return
            }
            
            let colorSpace = CGColorSpaceCreateDeviceRGB()

            let commandBuffer = metalCommandQueue.makeCommandBuffer()
            
            let rpd = view.currentRenderPassDescriptor
            rpd?.colorAttachments[0].clearColor = MTLClearColorMake(0, 1, 0, 1)
            rpd?.colorAttachments[0].loadAction = .clear
            rpd?.colorAttachments[0].storeAction = .store
            
            let re = commandBuffer?.makeRenderCommandEncoder(descriptor: rpd!)
            re?.endEncoding()
                
            context.render((AppState.shared.rawImage ?? AppState.shared.rawImageOriginal)!,
                to: drawable.texture,
                commandBuffer: commandBuffer,
                bounds: AppState.shared.rawImageOriginal!.extent,
                colorSpace: colorSpace)
            
            commandBuffer?.present(drawable)
            commandBuffer?.commit()
        }

字符串
rawImage是我的CIImage纹理,我从我的过滤功能。
上下文是在其他地方创建的,但应该是:

context = CIContext(mtlDevice: metalDevice)


接下来是添加Frank Schlegel提供的代码的居中部分。

fslejnso

fslejnso4#

从macOS 14.0 / Xcode 15开始,您可以使用Metal代码作为视图修改器。
因此,您可以创建Rectangle()并应用金属视觉效果(或扭曲效果)。
这里有一个很长的例子,有很多参数。
第一个月

import SwiftUI

struct ContentView: View {
    @State private var startTime: Date = .now
    @State private var touch: CGPoint = .zero
    @State private var schlorp: CGFloat = 5.0
    @State private var crisp: CGFloat = 0.005
    @State private var shapeCount: CGFloat = 5.0
    var body: some View {
        TimelineView(.animation) { timeline in
            let time = startTime.distance(to: timeline.date)
            
            ZStack {
                Image("sf-ferry-bldg")
                    .resizable()
                    .aspectRatio(contentMode: .fill)
                    .visualEffect { content, proxy in
                        content
                            .colorEffect(ShaderLibrary.wavy(
                                .float2(proxy.size),
                                .float2(touch),
                                .float(time),
                                .float(schlorp),
                                .float(crisp),
                                .float(shapeCount)
                            )
                            )
                    }
                    .gesture( DragGesture(minimumDistance: .zero)
                        .onChanged { value in
                            touch = value.location
                        }
                    )
            }
        }
        .toolbar {
            ToolbarItem {
                HStack {
                    Button(action: {
                        shapeCount -= 1.0
                    }, label: {
                        Image(systemName: "minus")
                    })
                    
                    Text("Shapes: \(shapeCount.rounded())")
                    
                    Button(action: {
                        shapeCount += 1.0
                    }, label: {
                        Image(systemName: "plus")
                    })
                }
            }
            ToolbarItem {
                Text("Crisp \((crisp * 1_000).formatted())")
            }
            ToolbarItem {
                Slider(value: $crisp, in: 0.0001...0.01) {
                    Text("Crisp")
                } minimumValueLabel: {
                    Text("1")
                } maximumValueLabel: {
                    Text("100")
                }
                .frame(minWidth: 200)
            }

            ToolbarItem {
                Text("Schlorp \(schlorp.formatted())")
            }
            ToolbarItem {
                Slider(value: $schlorp, in: 2.0...100.0) {
                    Text("Schlorp")
                } minimumValueLabel: {
                    Text("1.5")
                } maximumValueLabel: {
                    Text("50")
                }
                .frame(minWidth: 200)
            }
        }
    }
}

字符串
你需要一个这样的金属锉

[[ stitchable ]] half4 wavy(float2 position, half4 color, float2 size, float2 touch, float time, float schlorp = 5.0, float crisp = 10.0, float inShapeCount = 5) {
    // Calculate the UV coordinates
    float2 uv = position / size.xy;
    // Calculate the sine wave displacement based on time and UV coordinates
    float displacement = cos(uv.x * schlep + time) * sin(uv.y * crisp + time) * .1; // * 10.0 + time) * 0.1;
    // Apply the displacement to the X coordinate to create the wavy effect
    float x = uv.x + displacement;
    // Create a grayscale color based on the X coordinate
    float3 wavyColor = float3(x, -x, 1.0);
    // Output the final color
    return half4(half3(color), 1.0);
}

相关问题