美文网首页音视频
通过Metal展示AR体验

通过Metal展示AR体验

作者: loongod | 来源:发表于2021-03-03 09:50 被阅读0次

在相机信息流上控制你的app虚拟内容渲染。

一、概述

ARKit包含视图类,可轻松显示SceneKitSpriteKitAR体验。 但是,如果改为使用Metal来构建自己的渲染引擎,则ARKit也将提供所有必要的支持,以使用自定义视图显示AR体验。

arsession.png

在任何AR体验中,第一步都是配置一个ARSession对象以管理相机捕获和运动处理。 session定义并维持设备所居住的真实空间与您为AR内容建模的虚拟空间之间的对应关系。 要在自定义视图中显示您的AR体验,您需要:

  1. session中检索视频帧和跟踪信息。
  2. 渲染这些帧图像作为您的视图背景。
  3. 使用跟踪信息将AR内容定位并绘制在相机图像上。

注意
本文介绍了在Xcode项目模板中找到的代码。 有关完整的示例代码,请使用Augmented Reality模板创建一个新的iOS应用程序,然后从Content Technology弹出菜单中选择Metal

二、从Session中获取视频帧和跟踪数据

创建并维护您自己的ARSession实例,并使用适合您要支持的AR体验的session配置来运行它。 session从相机捕获视频,在建模的3D空间中跟踪设备的位置和方向,并提供ARFrame对象。 从捕获帧的那一刻起,每个这样的对象都包含一个单独的视频帧图像和位置跟踪信息。

有两种方法可以访问AR session生成的ARFrame对象,具体取决于您的应用是支持拉还是推设计模式。

如果您希望控制帧计时(拉动设计模式),则每次重绘视图内容时,都可以使用sessioncurrentFrame属性获取当前帧图像和跟踪信息。 ARKit Xcode模板使用以下方法:

// in Renderer class, called from MTKViewDelegate.draw(in:) via Renderer.update()
func updateGameState() {        
    guard let currentFrame = session.currentFrame else {
        return
    }
    
    updateSharedUniforms(frame: currentFrame)
    updateAnchors(frame: currentFrame)
    updateCapturedImageTextures(frame: currentFrame)
    
    if viewportSizeDidChange {
        viewportSizeDidChange = false
        
        updateImagePlane(frame: currentFrame)
    }
}

或者,如果您的应用程序设计偏爱推送模式,请实现 session(_:didUpdate :)委托方法,session将针对其捕获的每个视频帧对其调用一次(默认为每秒60帧)。

获取一帧后,您需要绘制相机图像,并更新和渲染您的AR体验中包括的任何叠加内容。

三、绘制相机图像

每个ARFrame对象的captureImage属性都包含一个从设备相机捕获的像素缓冲区。 要将图像绘制为自定义视图的背景,您需要根据图像内容创建纹理,并提交使用这些纹理的GPU渲染命令。

像素缓冲区的内容以双平面YCbCr(也称为Y'UV)数据格式进行编码; 要渲染图像,您需要将该像素数据转换为可绘制的RGB格式。 对于使用Metal渲染,您可以在GPU着色器代码中最高效地执行此转换。 使用CVMetalTextureCache API从像素缓冲区创建两个Metal纹理———缓冲区的亮度luma(Y)和色度chroma(CbCr)平面各一个:

func updateCapturedImageTextures(frame: ARFrame) {
    // Create two textures (Y and CbCr) from the provided frame's captured image
    let pixelBuffer = frame.capturedImage
    if (CVPixelBufferGetPlaneCount(pixelBuffer) < 2) {
        return
    }
    capturedImageTextureY = createTexture(fromPixelBuffer: pixelBuffer, pixelFormat:.r8Unorm, planeIndex:0)!
    capturedImageTextureCbCr = createTexture(fromPixelBuffer: pixelBuffer, pixelFormat:.rg8Unorm, planeIndex:1)!
}

func createTexture(fromPixelBuffer pixelBuffer: CVPixelBuffer, pixelFormat: MTLPixelFormat, planeIndex: Int) -> MTLTexture? {
    var mtlTexture: MTLTexture? = nil
    let width = CVPixelBufferGetWidthOfPlane(pixelBuffer, planeIndex)
    let height = CVPixelBufferGetHeightOfPlane(pixelBuffer, planeIndex)
    
    var texture: CVMetalTexture? = nil
    let status = CVMetalTextureCacheCreateTextureFromImage(nil, capturedImageTextureCache, pixelBuffer, nil, pixelFormat, width, height, planeIndex, &texture)
    if status == kCVReturnSuccess {
        mtlTexture = CVMetalTextureGetTexture(texture!)
    }
    
    return mtlTexture
}

接下来,使用片段函数对绘制这两个纹理的渲染命令进行编码,该片段函数通过颜色转换矩阵执行从YCbCrRGB的转换:

fragment float4 capturedImageFragmentShader(ImageColorInOut in [[stage_in]],
                                            texture2d<float, access::sample> capturedImageTextureY [[ texture(kTextureIndexY) ]],
                                            texture2d<float, access::sample> capturedImageTextureCbCr [[ texture(kTextureIndexCbCr) ]]) {
    
    constexpr sampler colorSampler(mip_filter::linear,
                                   mag_filter::linear,
                                   min_filter::linear);
    
    const float4x4 ycbcrToRGBTransform = float4x4(
        float4(+1.0000f, +1.0000f, +1.0000f, +0.0000f),
        float4(+0.0000f, -0.3441f, +1.7720f, +0.0000f),
        float4(+1.4020f, -0.7141f, +0.0000f, +0.0000f),
        float4(-0.7010f, +0.5291f, -0.8860f, +1.0000f)
    );
    
    // Sample Y and CbCr textures to get the YCbCr color at the given texture coordinate
    float4 ycbcr = float4(capturedImageTextureY.sample(colorSampler, in.texCoord).r,
                          capturedImageTextureCbCr.sample(colorSampler, in.texCoord).rg, 1.0);
    
    // Return converted RGB color
    return ycbcrToRGBTransform * ycbcr;
}

注意
使用displayTransform(for: viewportSize:)方法确保相机图像覆盖整个视图。 例如,使用此方法以及完整的Metal管道设置代码,请参阅完整的Xcode模板。 (使用增强现实模板创建一个新的iOS应用程序,然后从Content Technology弹出菜单中选择Metal。)

四、跟踪和渲染叠加内容

AR体验通常专注于渲染3D叠加内容,因此该内容似乎是在相机图像中看到的真实世界的一部分。 要实现这种错觉,请使用ARAnchor类对您自己的3D内容相对于真实空间的位置和方向进行建模。 Anchors提供您可以在渲染期间参考的转换。

例如,每当用户点击屏幕时,Xcode模板都会在设备前方约20 cm处创建一个锚点:

func handleTap(gestureRecognize: UITapGestureRecognizer) {
    // Create anchor using the camera's current position
    if let currentFrame = session.currentFrame {
        
        // Create a transform with a translation of 0.2 meters in front of the camera
        var translation = matrix_identity_float4x4
        translation.columns.3.z = -0.2
        let transform = simd_mul(currentFrame.camera.transform, translation)
        
        // Add a new anchor to the session
        let anchor = ARAnchor(transform: transform)
        session.add(anchor: anchor)
    }
}

在渲染引擎中,使用每个ARAnchor对象的transform属性放置可视化内容。 Xcode模板使用其handleTap方法中添加到session的每个锚点来定位简单的多维数据集网格:

func updateAnchors(frame: ARFrame) {
    // Update the anchor uniform buffer with transforms of the current frame's anchors
    anchorInstanceCount = min(frame.anchors.count, kMaxAnchorInstanceCount)
    
    var anchorOffset: Int = 0
    if anchorInstanceCount == kMaxAnchorInstanceCount {
        anchorOffset = max(frame.anchors.count - kMaxAnchorInstanceCount, 0)
    }
    
    for index in 0..<anchorInstanceCount {
        let anchor = frame.anchors[index + anchorOffset]
        
        // Flip Z axis to convert geometry from right handed to left handed
        var coordinateSpaceTransform = matrix_identity_float4x4
        coordinateSpaceTransform.columns.2.z = -1.0
        
        let modelMatrix = simd_mul(anchor.transform, coordinateSpaceTransform)
        
        let anchorUniforms = anchorUniformBufferAddress.assumingMemoryBound(to: InstanceUniforms.self).advanced(by: index)
        anchorUniforms.pointee.modelMatrix = modelMatrix
    }
}

注意
在更复杂的AR体验中,您可以使用命中测试或平面检测来找到真实表面的位置。 有关详细信息,请参见planeDetection属性和hitTest(_: types: )方法。 在这两种情况下,ARKit都将结果作为ARAnchor对象提供,因此您仍然可以使用锚点转换来放置可视化内容。

五、渲染逼真的灯光

当配置着色器以在场景中绘制3D内容时,请使用每个ARFrame对象中的估计照明信息来产生更逼真的阴影:

// in Renderer.updateSharedUniforms(frame:):
// Set up lighting for the scene using the ambient intensity if provided
var ambientIntensity: Float = 1.0
if let lightEstimate = frame.lightEstimate {
    ambientIntensity = Float(lightEstimate.ambientIntensity) / 1000.0
}
let ambientLightColor: vector_float3 = vector3(0.5, 0.5, 0.5)
uniforms.pointee.ambientLightColor = ambientLightColor * ambientIntensity

注意
有关此示例附带的完整的Metal设置和渲染命令集,请参见完整的Xcode模板。(使用增强现实模板创建一个新的iOS应用程序,然后从Content Technology弹出菜单中选择Metal。)

相关文章

  • 通过Metal展示AR体验

    在相机信息流上控制你的app虚拟内容渲染。 一、概述 ARKit包含视图类,可轻松显示SceneKit或Sprit...

  • ARKit尝试翻译五_Custom Views

    Displaying an AR Experience with Metal 通过渲染摄像机图像和使用位置跟踪信息...

  • AR增强现实技术在室内设计中的应用

    1. AR技术简介 AR技术通过捕捉现实场景,将设计好的三维画面与现实场景融合,体验者通过手机或者平板电脑可以看到...

  • Metal 1

    Metal 1 MetalKit MTKView Metal 渲染管道 1.VertexData 通过CPU也就是...

  • Metal基本认识

    Metal简介 在 WWDC 2014 上,Apple为游戏开发者推出了新的平台技术 Metal。通过Metal相...

  • Metal

    Metal Metal 是苹果在2018年推出的图形编程接口,通过Metal相关API可以直接操作GPU,能最大限...

  • 你绝对想不到,带减肥功能的AR美食眼镜已经诞生!

    具备下面这2个功能的AR眼镜,带给人们什么样的体验? 1、通过AR设备改变我们眼中看到的食物大小 2、通过眼镜上的...

  • [译] iOS设计规范:概述三

    iOS11系统中更新的内容: AR功能 通过AR功能,应用可以提供身临其境、引人入胜的体验,把虚拟世界与真实世界更...

  • 02 - Metal学习之渲染管线

    通过这篇文章你将了解Metal渲染管线的工作流程,这有助于你深入了解Metal工作流程,以及Metal是基于什么现...

  • Metal入门003-案例:渲染背景色

    Metal入门001-初识MetalMetal入门002-Metal相关API 本文通过渲染背景色这个小案例,展现...

网友评论

    本文标题:通过Metal展示AR体验

    本文链接:https://www.haomeiwen.com/subject/nhsxqltx.html