Metal(iOS)中的多重采样/锯齿状边缘

Posted

技术标签:

【中文标题】Metal(iOS)中的多重采样/锯齿状边缘【英文标题】:Multisampling/jagged edges in Metal (iOS) 【发布时间】:2016-07-13 15:19:34 【问题描述】:

我目前正在尝试绘制一个将在 Swift 中使用 Metal 进行动画处理的图形。我已经成功地绘制了我的图形的单帧。图形很简单,正如您从这张图片中看到的那样。我不知道如何对绘图进行多重采样。总体而言,似乎很少有关于 Metal 的参考资料,尤其是在 Swift 语法方面。

self.metalLayer = CAMetalLayer()
self.metalLayer.device = self.device
self.metalLayer.pixelFormat = .BGRA8Unorm
self.metalLayer.framebufferOnly = true
self.metalLayer.frame = self.view.frame
self.view.layer.addSublayer(self.metalLayer)

self.renderer = SunRenderer(device: self.device, frame: self.view.frame)

let defaultLibrary = self.device.newDefaultLibrary()
let fragmentProgram = defaultLibrary!.newFunctionWithName("basic_fragment")
let vertexProgram = defaultLibrary!.newFunctionWithName("basic_vertex")

let pipelineStateDescriptor = MTLRenderPipelineDescriptor()
pipelineStateDescriptor.vertexFunction = vertexProgram
pipelineStateDescriptor.fragmentFunction = fragmentProgram
pipelineStateDescriptor.colorAttachments[0].pixelFormat = .BGRA8Unorm
pipelineStateDescriptor.colorAttachments[0].blendingEnabled = true

pipelineStateDescriptor.colorAttachments[0].rgbBlendOperation = MTLBlendOperation.Add
pipelineStateDescriptor.colorAttachments[0].alphaBlendOperation = MTLBlendOperation.Add
pipelineStateDescriptor.colorAttachments[0].sourceRGBBlendFactor = MTLBlendFactor.SourceAlpha
pipelineStateDescriptor.colorAttachments[0].sourceAlphaBlendFactor = MTLBlendFactor.SourceAlpha
pipelineStateDescriptor.colorAttachments[0].destinationRGBBlendFactor = MTLBlendFactor.OneMinusSourceAlpha
pipelineStateDescriptor.colorAttachments[0].destinationAlphaBlendFactor = MTLBlendFactor.OneMinusSourceAlpha

问题是,如何平滑这些边缘?

更新:

所以我实现了一个 MultiSample 纹理并将 sampleCount 设置为 4。我没有发现任何区别,所以我怀疑我做错了什么。

最终:

因此,最终,多重采样确实有效。最初,我用 0 alpha 包裹这些“射线”的顶点。这是使边缘更平滑的技巧。有了这些顶点,多重采样似乎并没有改善边缘。当我恢复到每条光线有 4 个顶点时,多重采样改善了它们的边缘。

let defaultLibrary = self.device.newDefaultLibrary()
let fragmentProgram = defaultLibrary!.newFunctionWithName("basic_fragment")
let vertexProgram = defaultLibrary!.newFunctionWithName("basic_vertex")
        
let pipelineStateDescriptor = MTLRenderPipelineDescriptor()
pipelineStateDescriptor.vertexFunction = vertexProgram
pipelineStateDescriptor.fragmentFunction = fragmentProgram
pipelineStateDescriptor.colorAttachments[0].pixelFormat = .BGRA8Unorm
pipelineStateDescriptor.colorAttachments[0].blendingEnabled = true
pipelineStateDescriptor.sampleCount = 4
        
pipelineStateDescriptor.colorAttachments[0].rgbBlendOperation =    MTLBlendOperation.Add
pipelineStateDescriptor.colorAttachments[0].alphaBlendOperation = MTLBlendOperation.Add
pipelineStateDescriptor.colorAttachments[0].sourceRGBBlendFactor = MTLBlendFactor.SourceAlpha
pipelineStateDescriptor.colorAttachments[0].sourceAlphaBlendFactor = MTLBlendFactor.SourceAlpha

pipelineStateDescriptor.colorAttachments[0].destinationRGBBlendFactor = MTLBlendFactor.OneMinusSourceAlpha
pipelineStateDescriptor.colorAttachments[0].destinationAlphaBlendFactor = MTLBlendFactor.OneMinusSourceAlpha

let desc = MTLTextureDescriptor()
desc.textureType = MTLTextureType.Type2DMultisample
desc.width = Int(self.view.frame.width)
desc.height = Int(self.view.frame.height)
desc.sampleCount = 4
desc.pixelFormat = .BGRA8Unorm
        
self.sampletex = self.device.newTextureWithDescriptor(desc)


// When rendering
let renderPassDescriptor = MTLRenderPassDescriptor()
renderPassDescriptor.colorAttachments[0].texture = sampletex
renderPassDescriptor.colorAttachments[0].resolveTexture = drawable.texture
renderPassDescriptor.colorAttachments[0].loadAction = .Clear
renderPassDescriptor.colorAttachments[0].clearColor = MTLClearColor(red: 23/255.0, green: 26/255.0, blue: 31/255.0, alpha: 0.0)
renderPassDescriptor.colorAttachments[0].storeAction = .MultisampleResolve
    
    
let commandBuffer = commandQueue.commandBuffer()
    
let renderEncoder = commandBuffer.renderCommandEncoderWithDescriptor(renderPassDescriptor)
renderEncoder.setRenderPipelineState(pipelineState)

【问题讨论】:

【参考方案1】:

使用 MTKView(只需将 sampleCount 设置为视图和管道描述符上所需的 MSAA 样本数),这要简单得多,但以下是滚动您自己的步骤。

    创建渲染管道状态时,将渲染管道状态描述符的sampleCount 设置为多重采样计数。

    在启动时,以及每当层调整大小时,通过创建一个纹理描述符,其textureTypeMTLTextureType2DMultisample 并且其sampleCount 是您的多重采样计数,从而创建一个尺寸等于图层可绘制大小的多重采样纹理。如果您正在使用深度和/或模板缓冲区,请在其描述符上设置这些属性。

    渲染时,将MSAA纹理设置为render pass描述符的原色附件texture,将当前drawable的纹理设置为resolveTexture

    将颜色附件的storeAction 设置为MTLStoreActionMultisampleResolve,以便在通道结束时将MSAA 纹理解析到渲染缓冲区中。

    像往常一样绘制和呈现。

【讨论】:

请注意,在第2步中,您还必须将MSAA纹理的storageMode设置为MTLStorageModePrivate。理想情况下,您还应该将usage 设置为MTLTextureUsageRenderTarget 使用 MTKView sampleCount 工作得很好。只需确保使用 device.supportsTextureSampleCount(count) 确定设备可以使用多少个样本,如果使用超过 1 个样本,请确保设置 renderPassDescriptor!.colorAttachments[0].storeAction = MTLStoreAction.storeAndMultisampleResolve 如果您不需要 MSAA 缓冲区的全部内容进行进一步处理,则无需使用 .storeAndMultisampleResolve 存储操作;只需使用 .multisampleResolve 操作即可满足 99% 的情况,并且可以节省大量内存。

以上是关于Metal(iOS)中的多重采样/锯齿状边缘的主要内容,如果未能解决你的问题,请参考以下文章

Metal2剖析:抗锯齿之基于Imageblock特性的增强MSAA

OpenGL4 多重采样抗锯齿和渲染到纹理

在 GLFW 窗口中启用多重采样不会提高抗锯齿的质量

WebGPU学习:MSAA

对特定绘制调用应用多重采样/消除锯齿,而不是全部

在 iOS 设备上将多个渲染目标 (MRT) 与多重采样组合失败,而不是在模拟器上