使用 Metal 对 SceneKit 渲染进行抗锯齿处理
Antialiasing a SceneKit rendering with Metal
我是 Metal 的新手。我正在使用 this Apple sample code 使用 Metal 渲染 SceneKit 场景。 TLDR;它调用 SCNRenderer's
render
函数并传入命令缓冲区。我正在为大苏尔编译。
它可以工作,但没有抗锯齿功能。我已经尝试了几种方法来实现它,您可以在下面的更新中看到。
如果没有 Metal,我只需在 SCNRenderer 上将 isJitteringEnabled
设置为 true
,我就得到了漂亮(且缓慢)的 96-ish-pass 渲染。如果我尝试使用 Metal 执行此操作,我会遇到奇怪的像素格式不匹配问题,因此我怀疑两者不兼容。
据我所知,使用 Metal 实现抗锯齿的最简单方法是在渲染管线中启用多重采样(我知道该怎么做)— 和 使用多采样纹理 (MTLTextureType.type2DMultisample
)。 This partial answer 支持我的假设。
这就是问题所在。当我从 CVMetalTextureCache
和 CVMetalTextureCacheCreateTextureFromImage
获取纹理时,我不知道如何更改纹理类型。这似乎是 Core Video 对 Metal 支持的限制?
就是这样。 post 的其余部分是关于我尝试过的内容的更多详细信息。
(我认为这可能可以使用着色器。我也对这个解决方案持开放态度,但我不知道从哪里开始。This example doesn't compile, and this example 适用于 GSLS)
我的像素缓冲区 atts 看起来像这样
let pixelbufferAttributes = [
kCVPixelBufferPixelFormatTypeKey : kCVPixelFormatType_32BGRA,
kCVPixelBufferWidthKey: exportSettings.width,
kCVPixelBufferHeightKey : exportSettings.height,
kCVPixelBufferMetalCompatibilityKey: true] as [String: Any]
对于每一帧,它从池中创建一个新的像素缓冲区,将其包装在缓存中的金属纹理中,就像这样
let pixelFormat = MTLPixelFormat.bgra8Unorm_srgb
var optionalMetalTexture: CVMetalTexture?
err = CVMetalTextureCacheCreateTextureFromImage(
kCFAllocatorDefault,
metalTextureCache, // object prop
pixelBuffer,
nil, // texture attributes
pixelFormat,
exportSettings.width,
exportSettings.height,
0, // planeIndex
&optionalMetalTexture)
guard err == noErr, let metalTexture = optionalMetalTexture else {
fatalError("Failed to create metal texture wrapper from pixel bufffer \(err)")
}
尝试:更改纹理描述符
由于我使用 CVMetalTextureCacheCreateTextureFromImage
从 CVPixelbuffer
创建我的金属纹理,我不知道如何设置它的属性并使其成为多样本。
尝试:尝试H264
没有改变任何东西。还尝试仅更改 alpha 质量,使用 HEVC 和 alpha,但没有改变。
尝试:启用多重采样
我能够让我的管道接收到我想要的多重采样,但由于没有为多重采样设置纹理(更准确地说是 .2DMultisample
类型的 MTLTexture(docs)
尝试:复制Core Video
创建的MTLTexture
我尝试使用 MTLBlitCommandEncoder
将 Core Video 提供的纹理复制到我设置的具有正确属性的纹理中。但是它崩溃了,告诉我属性不匹配。
我开始觉得没有办法解决这个问题?
启用多重采样是正确的想法。
以下补丁显示了如何启用它。
--- a/HEVC-Videos-With-Alpha-AssetWriting/HEVC-Videos-With-Alpha-AssetWriting/AppDelegate.swift
+++ b/HEVC-Videos-With-Alpha-AssetWriting/HEVC-Videos-With-Alpha-AssetWriting/AppDelegate.swift
@@ -32,6 +32,8 @@ class AppDelegate: NSObject, NSApplicationDelegate, SCNSceneRendererDelegate {
let renderer = SCNRenderer(device: nil, options: nil)
var lampMaterials: SCNNode!
var metalTextureCache: CVMetalTextureCache!
+ let msaaSampleCount = 1
+ var metalMultisampledTexture: MTLTexture!
// Export
var frameCounter = 0
@@ -61,6 +63,18 @@ class AppDelegate: NSObject, NSApplicationDelegate, SCNSceneRendererDelegate {
fatalError("Cannot create metal texture cache: \(err)")
}
metalTextureCache = optionalMetalTextureCache
+
+ if (msaaSampleCount > 1) {
+ let textureDescriptor = MTLTextureDescriptor.texture2DDescriptor(pixelFormat: MTLPixelFormat.bgra8Unorm_srgb,
+ width: ExportSettings.width,
+ height: ExportSettings.height,
+ mipmapped: false)
+ textureDescriptor.usage = .renderTarget
+ textureDescriptor.storageMode = .private
+ textureDescriptor.textureType = .type2DMultisample
+ textureDescriptor.sampleCount = msaaSampleCount
+ metalMultisampledTexture = renderer.device!.makeTexture(descriptor: textureDescriptor)
+ }
}
/// Render next frame and call the frame completion handler
@@ -106,7 +120,14 @@ class AppDelegate: NSObject, NSApplicationDelegate, SCNSceneRendererDelegate {
let renderPassDescriptor = MTLRenderPassDescriptor()
renderPassDescriptor.colorAttachments[0].loadAction = .clear
renderPassDescriptor.colorAttachments[0].clearColor = clearColor
- renderPassDescriptor.colorAttachments[0].texture = CVMetalTextureGetTexture(metalTexture)
+ if (msaaSampleCount > 1) {
+ renderPassDescriptor.colorAttachments[0].texture = metalMultisampledTexture
+ renderPassDescriptor.colorAttachments[0].resolveTexture = CVMetalTextureGetTexture(metalTexture)
+ renderPassDescriptor.colorAttachments[0].storeAction = .multisampleResolve
+ }
+ else {
+ renderPassDescriptor.colorAttachments[0].texture = CVMetalTextureGetTexture(metalTexture)
+ }
renderer.render(atTime: currentPresentationTime.seconds,
viewport: ExportSettings.viewport,
commandBuffer: commandBuffer,
我是 Metal 的新手。我正在使用 this Apple sample code 使用 Metal 渲染 SceneKit 场景。 TLDR;它调用 SCNRenderer's
render
函数并传入命令缓冲区。我正在为大苏尔编译。
它可以工作,但没有抗锯齿功能。我已经尝试了几种方法来实现它,您可以在下面的更新中看到。
如果没有 Metal,我只需在 SCNRenderer 上将 isJitteringEnabled
设置为 true
,我就得到了漂亮(且缓慢)的 96-ish-pass 渲染。如果我尝试使用 Metal 执行此操作,我会遇到奇怪的像素格式不匹配问题,因此我怀疑两者不兼容。
据我所知,使用 Metal 实现抗锯齿的最简单方法是在渲染管线中启用多重采样(我知道该怎么做)— 和 使用多采样纹理 (MTLTextureType.type2DMultisample
)。 This partial answer 支持我的假设。
这就是问题所在。当我从 CVMetalTextureCache
和 CVMetalTextureCacheCreateTextureFromImage
获取纹理时,我不知道如何更改纹理类型。这似乎是 Core Video 对 Metal 支持的限制?
就是这样。 post 的其余部分是关于我尝试过的内容的更多详细信息。
(我认为这可能可以使用着色器。我也对这个解决方案持开放态度,但我不知道从哪里开始。This example doesn't compile, and this example 适用于 GSLS)
我的像素缓冲区 atts 看起来像这样
let pixelbufferAttributes = [
kCVPixelBufferPixelFormatTypeKey : kCVPixelFormatType_32BGRA,
kCVPixelBufferWidthKey: exportSettings.width,
kCVPixelBufferHeightKey : exportSettings.height,
kCVPixelBufferMetalCompatibilityKey: true] as [String: Any]
对于每一帧,它从池中创建一个新的像素缓冲区,将其包装在缓存中的金属纹理中,就像这样
let pixelFormat = MTLPixelFormat.bgra8Unorm_srgb
var optionalMetalTexture: CVMetalTexture?
err = CVMetalTextureCacheCreateTextureFromImage(
kCFAllocatorDefault,
metalTextureCache, // object prop
pixelBuffer,
nil, // texture attributes
pixelFormat,
exportSettings.width,
exportSettings.height,
0, // planeIndex
&optionalMetalTexture)
guard err == noErr, let metalTexture = optionalMetalTexture else {
fatalError("Failed to create metal texture wrapper from pixel bufffer \(err)")
}
尝试:更改纹理描述符
由于我使用 CVMetalTextureCacheCreateTextureFromImage
从 CVPixelbuffer
创建我的金属纹理,我不知道如何设置它的属性并使其成为多样本。
尝试:尝试H264
没有改变任何东西。还尝试仅更改 alpha 质量,使用 HEVC 和 alpha,但没有改变。
尝试:启用多重采样
我能够让我的管道接收到我想要的多重采样,但由于没有为多重采样设置纹理(更准确地说是 .2DMultisample
类型的 MTLTexture(docs)
尝试:复制Core Video
创建的MTLTexture
我尝试使用 MTLBlitCommandEncoder
将 Core Video 提供的纹理复制到我设置的具有正确属性的纹理中。但是它崩溃了,告诉我属性不匹配。
我开始觉得没有办法解决这个问题?
启用多重采样是正确的想法。 以下补丁显示了如何启用它。
--- a/HEVC-Videos-With-Alpha-AssetWriting/HEVC-Videos-With-Alpha-AssetWriting/AppDelegate.swift
+++ b/HEVC-Videos-With-Alpha-AssetWriting/HEVC-Videos-With-Alpha-AssetWriting/AppDelegate.swift
@@ -32,6 +32,8 @@ class AppDelegate: NSObject, NSApplicationDelegate, SCNSceneRendererDelegate {
let renderer = SCNRenderer(device: nil, options: nil)
var lampMaterials: SCNNode!
var metalTextureCache: CVMetalTextureCache!
+ let msaaSampleCount = 1
+ var metalMultisampledTexture: MTLTexture!
// Export
var frameCounter = 0
@@ -61,6 +63,18 @@ class AppDelegate: NSObject, NSApplicationDelegate, SCNSceneRendererDelegate {
fatalError("Cannot create metal texture cache: \(err)")
}
metalTextureCache = optionalMetalTextureCache
+
+ if (msaaSampleCount > 1) {
+ let textureDescriptor = MTLTextureDescriptor.texture2DDescriptor(pixelFormat: MTLPixelFormat.bgra8Unorm_srgb,
+ width: ExportSettings.width,
+ height: ExportSettings.height,
+ mipmapped: false)
+ textureDescriptor.usage = .renderTarget
+ textureDescriptor.storageMode = .private
+ textureDescriptor.textureType = .type2DMultisample
+ textureDescriptor.sampleCount = msaaSampleCount
+ metalMultisampledTexture = renderer.device!.makeTexture(descriptor: textureDescriptor)
+ }
}
/// Render next frame and call the frame completion handler
@@ -106,7 +120,14 @@ class AppDelegate: NSObject, NSApplicationDelegate, SCNSceneRendererDelegate {
let renderPassDescriptor = MTLRenderPassDescriptor()
renderPassDescriptor.colorAttachments[0].loadAction = .clear
renderPassDescriptor.colorAttachments[0].clearColor = clearColor
- renderPassDescriptor.colorAttachments[0].texture = CVMetalTextureGetTexture(metalTexture)
+ if (msaaSampleCount > 1) {
+ renderPassDescriptor.colorAttachments[0].texture = metalMultisampledTexture
+ renderPassDescriptor.colorAttachments[0].resolveTexture = CVMetalTextureGetTexture(metalTexture)
+ renderPassDescriptor.colorAttachments[0].storeAction = .multisampleResolve
+ }
+ else {
+ renderPassDescriptor.colorAttachments[0].texture = CVMetalTextureGetTexture(metalTexture)
+ }
renderer.render(atTime: currentPresentationTime.seconds,
viewport: ExportSettings.viewport,
commandBuffer: commandBuffer,