在 swift 中创建透明纹理

create transparent texture in swift

我只需要创建一个透明纹理。(alpha 0 的像素)。

func layerTexture()-> MTLTexture {

        let width = Int(self.drawableSize.width  )
        let height = Int(self.drawableSize.height )


        let texDescriptor = MTLTextureDescriptor.texture2DDescriptor(pixelFormat: .bgra8Unorm, width: width , height: height, mipmapped: false)
        let temparyTexture = self.device?.makeTexture(descriptor: texDescriptor)

        return temparyTexture!
    }

当我使用预览打开临时纹理时,它看起来是黑色的。这里缺少什么?

更新

我刚刚尝试使用透明图像创建纹理。 代码。

 func layerTexture(imageData:Data)-> MTLTexture {

        let width = Int(self.drawableSize.width  )
        let height = Int(self.drawableSize.height )
        let bytesPerRow = width * 4


        let texDescriptor = MTLTextureDescriptor.texture2DDescriptor(pixelFormat: .rgba8Unorm, width: width , height: height, mipmapped: false)

        let temparyTexture = self.device?.makeTexture(descriptor: texDescriptor)

         let region = MTLRegionMake2D(0, 0, width, height)

        imageData.withUnsafeBytes { (u8Ptr: UnsafePointer<UInt8>) in
            let rawPtr = UnsafeRawPointer(u8Ptr)

            temparyTexture?.replace(region: region, mipmapLevel: 0, withBytes: rawPtr, bytesPerRow: bytesPerRow)
        }


        return temparyTexture!
    }

方法调用如下

let image = UIImage(named: "layer1.png")!

let imageData = UIImagePNGRepresentation(image)

self.layerTexture(imageData: imageData!)

其中 layer1.png 是透明的 png。但即使在我尝试替换纹理时它因消息 "Thread 1: EXC_BAD_ACCESS (code=1, address=0x107e8c000) " 而崩溃。我相信这是因为图像数据被压缩并且 rawpointer 应该指向未压缩的数据。我该如何解决?

我走的路是正确的还是完全走错了方向?有没有其他选择。我只需要创建透明纹理。

预编辑:快速查看透明纹理时,它会显示为黑色。我只是仔细检查了我在生产中稳定 运行 的一些代码 - 这是预期的结果。

Post-edit:你是对的,你不应该将 PNG 或 JPEG 数据直接复制到 MTLTexture 的内容中。我建议这样做:

    var pixelBuffer: CVPixelBuffer?

    let attrs = [kCVPixelBufferCGImageCompatibilityKey: kCFBooleanTrue,
                 kCVPixelBufferCGBitmapContextCompatibilityKey: kCFBooleanTrue,
                 kCVPixelBufferMetalCompatibilityKey: kCFBooleanTrue]

    var status = CVPixelBufferCreate(nil, Int(image.size.width), Int(image.size.height),
                                     kCVPixelFormatType_32BGRA, attrs as CFDictionary,
                                     &pixelBuffer)
    assert(status == noErr)

    let coreImage = CIImage(image: image)!
    let context = CIContext(mtlDevice: MTLCreateSystemDefaultDevice()!)
    context.render(coreImage, to: pixelBuffer!)

    var textureWrapper: CVMetalTexture?

    status = CVMetalTextureCacheCreateTextureFromImage(kCFAllocatorDefault,
                                                       GPUManager.shared.textureCache, pixelBuffer!, nil, .bgra8Unorm,
                                                       CVPixelBufferGetWidth(pixelBuffer!), CVPixelBufferGetHeight(pixelBuffer!), 0, &textureWrapper)

    let texture = CVMetalTextureGetTexture(textureWrapper!)!

    // use texture now for your Metal texture. the texture is now map-bound to the CVPixelBuffer's underlying memory.

您 运行 遇到的问题是,实际上很难完全掌握位图的工作原理以及如何以不同方式布置它们。图形是一个非常封闭的领域,有很多深奥的术语,其中一些指的是需要多年才能掌握的东西,其中一些指的是微不足道但人们只是用一个奇怪的词来称呼它们的东西。我的主要指示是:

  • 尽早在您的代码中摆脱 UIImage 领域。进入 Metal 领域时避免开销和延迟的最佳方法是尽快将图像转换为 GPU 兼容的表示形式。
  • 一旦您离开了 UIImage 领域,始终了解您的通道顺序(RGBA、BGRA)。在您正在编辑的代码中的任何一点,您都应该对每个 CVPixelBuffer / MTLTexture 具有的像素格式有一个心理模型。
  • 阅读有关预乘与非预乘 alpha 的内容,您可能不会 运行 解决这个问题,但在我第一次学习时它反复让我失望。
  • 一个 bitmap/pixelbuffer 的总字节大小 = bytesPerRow * height