MTKView 透明度

MTKView Transparency

我无法让我的 MTKView 清除它的背景。我已将视图及其层的 isOpaque 设置为 false,将背景颜色设置为清除并尝试了 google/Whosebug 上的多个解决方案(下面的代码中的大多数解决方案,如颜色附件的 loadAction 和 clearColor),但没有任何效果。

似乎忽略了所有背景颜色设置。设置 MTLRenderPassColorAttachmentDescriptor 的 loadAction 和 clearColor 什么都不做。

我想在 MTKView 下绘制我的常规 UIView。我错过了什么?

            // initialization
            let metal = MTKView(frame: self.view.bounds)
            metal.device = MTLCreateSystemDefaultDevice()
            self.renderer = try! MetalRenderer(mtkView: metal)
            metal.delegate = self.renderer
            self.view.addSubview(metal);
import Foundation
import MetalKit
import simd

public enum MetalError: Error {
    case mtkViewError
    case renderError
}

internal class MetalRenderer: NSObject, MTKViewDelegate {
    private let commandQueue: MTLCommandQueue;
    private let pipelineState: MTLRenderPipelineState
    private var viewportSize: SIMD2<UInt32> = SIMD2(x: 10, y: 10);
    private weak var mtkView: MTKView?
        

    
    init(mtkView: MTKView) throws {

        guard let device = mtkView.device else { 
            print("device not found error")
            throw MetalError.mtkViewError 
        }
        self.mtkView = mtkView

        // Load all the shader files with a .metal file extension in the project.
        guard let defaultLibrary = device.makeDefaultLibrary() else {
            print("Could not find library")
            throw MetalError.mtkViewError
        }
        let vertexFunction = defaultLibrary.makeFunction(name: "vertexShader")
        let fragmentFunction = defaultLibrary.makeFunction(name: "fragmentShader")

        mtkView.layer.isOpaque = false;
        mtkView.layer.backgroundColor = UIColor.clear.cgColor
        mtkView.isOpaque = false;
        mtkView.backgroundColor = .clear

        let pipelineStateDescriptor = MTLRenderPipelineDescriptor();
        pipelineStateDescriptor.label = "Pipeline";
        pipelineStateDescriptor.vertexFunction = vertexFunction;
        pipelineStateDescriptor.fragmentFunction = fragmentFunction;
        pipelineStateDescriptor.isAlphaToCoverageEnabled = true
        pipelineStateDescriptor.colorAttachments[0].pixelFormat = .bgra8Unorm;
        pipelineStateDescriptor.colorAttachments[0].isBlendingEnabled = true;
        pipelineStateDescriptor.colorAttachments[0].destinationRGBBlendFactor = .oneMinusSourceAlpha;
        pipelineStateDescriptor.colorAttachments[0].destinationAlphaBlendFactor = .oneMinusSourceAlpha;

        pipelineState = try! device.makeRenderPipelineState(descriptor: pipelineStateDescriptor);
        
        guard let queue = device.makeCommandQueue() else { 
            print("make command queue error")
            throw MetalError.mtkViewError 
        }
        commandQueue = queue
    }


    func mtkView(_ view: MTKView, drawableSizeWillChange size: CGSize) {
        viewportSize.x = UInt32(size.width)
        viewportSize.y = UInt32(size.height)
    }

    func draw(in view: MTKView) {

        let vertices: [Vertex] = [
            Vertex(position: SIMD3<Float>(x: 250.0, y: -250.0, z: 0)),
            Vertex(position: SIMD3<Float>(x: -250.0, y: -250.0, z: 0)),
            Vertex(position: SIMD3<Float>(x: 0.0, y: 250.0, z: 0)),
        ]
        
        guard let commandBuffer = commandQueue.makeCommandBuffer() else { 
            print("Couldn't create command buffer")
            return 
        }
        // Create a new command buffer for each render pass to the current drawable.
        commandBuffer.label = "MyCommand";

        // Obtain a renderPassDescriptor generated from the view's drawable textures.
        guard let renderPassDescriptor = view.currentRenderPassDescriptor else {
            print("Couldn't create render pass descriptor")
            return
        }
        
        guard let renderEncoder = commandBuffer.makeRenderCommandEncoder(descriptor: renderPassDescriptor) else {
            print("Couldn't create render encoder")
            return
        }

        renderPassDescriptor.colorAttachments[0].loadAction = .clear
        renderPassDescriptor.colorAttachments[0].clearColor = MTLClearColorMake(1.0, 0.0, 0.0, 0.5)

        renderEncoder.label = "MyRenderEncoder";
        // Set the region of the drawable to draw into.
        renderEncoder.setViewport(MTLViewport(originX: 0.0, originY: 0.0, width: Double(viewportSize.x), height: Double(viewportSize.y), znear: 0.0, zfar: 1.0))
        renderEncoder.setRenderPipelineState(pipelineState)

        // Pass in the parameter data.
        renderEncoder.setVertexBytes(vertices, length: MemoryLayout<Vertex>.size * vertices.count, index: Int(VertexInputIndexVertices.rawValue))
        renderEncoder.setVertexBytes(&viewportSize, length: MemoryLayout<SIMD2<UInt32>>.size, index: Int(VertexInputIndexViewportSize.rawValue))
        renderEncoder.drawPrimitives(type: MTLPrimitiveType.triangle, vertexStart: 0, vertexCount: 3)
        renderEncoder.endEncoding()

        // Schedule a present once the framebuffer is complete using the current drawable.
        guard let drawable = view.currentDrawable else {
            print("Couldn't get current drawable")
            return
        }

        commandBuffer.present(drawable)

        // Finalize rendering here & push the command buffer to the GPU.
        commandBuffer.commit()
    }
}

感谢 Frank,答案是只设置视图本身的 clearColor 属性,我错过了。我还删除了 MTLRenderPipelineDescriptor 中的大部分调整,现在的代码是:

    let pipelineStateDescriptor = MTLRenderPipelineDescriptor();
    pipelineStateDescriptor.label = "Pipeline";
    pipelineStateDescriptor.vertexFunction = vertexFunction;
    pipelineStateDescriptor.fragmentFunction = fragmentFunction;
    pipelineStateDescriptor.colorAttachments[0].pixelFormat = 
    mtkView.colorPixelFormat;

也不需要从 currentRenderPassDescriptor 到 MTLRenderPassDescriptor 的更改。

编辑:还要确保将 MTKView 的 isOpaque 属性 也设置为 false。