您当前在:首页 > 开发者 >

浏览量

ARKit开发者教程(四):如何使用Metal语言开发AR体验

ARKit开发者教程(四):如何使用Metal语言开发AR体验

发布时间:2018-04-10

[ARKit系列] 四、使用Meta语言开发一个AR体验
  .

​ 最近,ARKit可是火得炙手可热o-O!在 iOS 11 中苹果终于公开了他们在增强现实方面的野心,推出 ARKit 开发平台,支持开发者在他们的应用和游戏中快速简便地整合增强现实体验。本月早些时候,苹果提供的 ARKit 演示应用已经让我们一睹 ARKit 的功能。接下来我们将让大家进一步认识ARKit,本系列分为四部分,希望能给大家带来帮助~

使用Metal语言开发一个AR体验

通过渲染摄影图像以及运用显示图层内容的位置追踪信息来建立AR自定义视图

综述

ARKit包括可以使用SceneKit或SpriteKit来方便地开发AR体验的视图。但是,如果你想建立你自己的渲染引擎(或与第三方引擎相结合),ARKit也提供以自定义视图显示AR体验所必需的一切支持。

[ARKit系列] 四、使用Meta语言开发一个AR体验
 

在任何AR体验中,第一步就是设置一个ARSession对象来管理摄影捕捉和运动处理。Session定义并维持着设备所处的现实世界空间和你所模拟的AR内容虚拟空间之间的对应。要自定义视图中显示你的AR体验,你将需要:

1. 检索视频帧,追踪来自session的信息。

2. 渲染这些帧图像作为你的视图的背景。

3. 用追踪信息定位,在摄像图像顶上画上AR内容。

注意

本篇代码建立在Xcode project模板。为了完整的代码示例,请创建配有AR模板的新款iOS,应用软件,并从Content Technology弹出菜单上选择Metal语言。

从Session中得到视频帧和追踪数据

创建和维持你自己的ARSession实例,根据你想要支持哪种AR体验,选择适用的会话设置来运行该ARSession实例。(方法请见“建立一个基本的AR示例”)该session从摄像中捕捉视频,追踪设备在三维空间建模里的位置和方向,提供ARFrame对象。每个这样的对象既包含了个体的视频帧图像,又包含了真被捕捉瞬间的位置追踪信息。 有两种方法获取AR session生成的ARFrame对象,这取决于你的APP支持的是推数据(push)还是拉数据(pull)的设计模式。 如果你倾向于控制帧像周期(the pull design pattern),请用session的currentFrame性能来得到当前的帧图像和你重画视图内容的每一刻的追踪信息。ARKit Xcode模板使用以下途径:

[代码]:

01 // in Renderer class, called from MTKViewDelegate.draw(in:) via Renderer.update()
02 func updateGameState() {       
03     guard let currentFrame = session.currentFrame else {
04         return
05     }
06     
07     updateSharedUniforms(frame: currentFrame)
08     updateAnchors(frame: currentFrame)
09     updateCapturedImageTextures(frame: currentFrame)
10     
11     if viewportSizeDidChange {
12         viewportSizeDidChange = false
13         
14         updateImagePlane(frame: currentFrame)
15     }
16 }

或者,如果你的APP设计支持的是推模式(push pattern),执行session(_:didUpdate:)委托函数,该session将调用一次给每一个捕捉到的视频帧(默认为每秒60帧)。

当你获得一帧,你将需要绘制摄影图像,更新渲染你的AR体验包括的所有图层内容。

绘制摄影图像

每个ARFrame对象的capturedImage性能包含了从设备摄像头捕捉到的像素缓冲区。绘制这个图像在位你的自定义视图的背景,你将需要创建来自图像内容的纹理,向GPU提交使用这些纹理的渲染指令。

像素缓冲区的内容被编码进一个二平面的色差配置(也叫亮度和色差信号)数据格式;渲染你需要转换成可绘制的三原色格式的像素数据的图像。为了用Metal语言进行渲染,你可以在GPU着色器代码里最有效率地执行这个转换。

使用CVMetalTextureCache 应用程序界面创建像素缓冲区的两个Metal纹理——其中一个是缓冲区亮度的(Y),另一个是色度的(CbCr):

[代码]:

01 func updateCapturedImageTextures(frame: ARFrame) {
02     // Create two textures (Y and CbCr) from the provided frame's captured image
03     let pixelBuffer = frame.capturedImage
04     if (CVPixelBufferGetPlaneCount(pixelBuffer) < 2) {
05         return
06     }
07     capturedImageTextureY = createTexture(fromPixelBuffer: pixelBuffer, pixelFormat:.r8Unorm, planeIndex:0)!
08     capturedImageTextureCbCr = createTexture(fromPixelBuffer: pixelBuffer, pixelFormat:.rg8Unorm, planeIndex:1)!
09 }
10  
11 func createTexture(fromPixelBuffer pixelBuffer: CVPixelBuffer, pixelFormat: MTLPixelFormat, planeIndex: Int) -> MTLTexture? {
12     var mtlTexture: MTLTexture? = nil
13     let width = CVPixelBufferGetWidthOfPlane(pixelBuffer, planeIndex)
14     let height = CVPixelBufferGetHeightOfPlane(pixelBuffer, planeIndex)
15     
16     var texture: CVMetalTexture? = nil
17     let status = CVMetalTextureCacheCreateTextureFromImage(nil, capturedImageTextureCache, pixelBuffer, nil, pixelFormat, width, height, planeIndex, &texture)
18     if status == kCVReturnSuccess {
19         mtlTexture = CVMetalTextureGetTexture(texture!)
20     }
21     
22     return mtlTexture
23 }

然后,将渲染指令编码,使用分段函数通过色彩变换矩阵完成YCbCr到RGB的转换,从而绘制这两个纹理:

[代码]:

01 fragment float4 capturedImageFragmentShader(ImageColorInOut in [[stage_in]],
02                                             texture2d capturedImageTextureY [[ texture(kTextureIndexY) ]],
03                                             texture2d capturedImageTextureCbCr [[ texture(kTextureIndexCbCr) ]]) {
04     
05     constexpr sampler colorSampler(mip_filter::linear,
06                                    mag_filter::linear,
07                                    min_filter::linear);
08     
09     const float4x4 ycbcrToRGBTransform = float4x4(
10         float4(+1.164380f, +1.164380f, +1.164380f, +0.000000f),
11         float4(+0.000000f, -0.391762f, +2.017230f, +0.000000f),
12         float4(+1.596030f, -0.812968f, +0.000000f, +0.000000f),
13         float4(-0.874202f, +0.531668f, -1.085630f, +1.000000f)
14     );
15     
16     // Sample Y and CbCr textures to get the YCbCr color at the given texture coordinate
17     float4 ycbcr = float4(capturedImageTextureY.sample(colorSampler, in.texCoord).r,
18                           capturedImageTextureCbCr.sample(colorSampler, in.texCoord).rg, 1.0);
19     
20     // Return converted RGB color
21     return ycbcrToRGBTransform * ycbcr;
22 }<br>

注意

使用displayTransform(withViewportSize:orientation:)函数,确保摄影图像覆盖整个视图。例如,使用这个类函数,就像完整的Metal管线设置码,请见完整的Xcode模板。(请创建配有AR模板的新款iOS应用软件,并从Content Technology弹出菜单上选择Metal语言。)追踪、渲染图层内容。

AR体验通常集中在渲染3D图层内容,从而让该内容在摄影图像中看起来仿佛是现实世界的一部分。为了能实现这种错觉,请使用ARAnchor来给你自己的与现实世界空间有关的3D内容的位置和方向建模。锚点会提供渲染时你能参照的变换。

例如,Xcode模板穿件一个距离设备前方20cm的锚点,无论使用者什么时候轻击屏幕:

[代码]:

01 func handleTap(gestureRecognize: UITapGestureRecognizer) {
02     // Create anchor using the camera's current position
03     if let currentFrame = session.currentFrame {
04         
05         // Create a transform with a translation of 0.2 meters in front of the camera
06         var translation = matrix_identity_float4x4
07         translation.columns.3.z = -0.2
08         let transform = simd_mul(currentFrame.camera.transform, translation)
09         
10         // Add a new anchor to the session
11         let anchor = ARAnchor(transform: transform)
12         session.add(anchor: anchor)
13     }

​ 在你的渲染引擎中,对每个ARAnchor对象使用transform性能,放置虚拟内容。Xcode模板使用每一个添加到在handleTap里的session的锚点,来放置一个简单的立方体网格:

[代码]:

01 func updateAnchors(frame: ARFrame) {
02     // Update the anchor uniform buffer with transforms of the current frame's anchors
03     anchorInstanceCount = min(frame.anchors.count, kMaxAnchorInstanceCount)
04     
05     var anchorOffset: Int = 0
06     if anchorInstanceCount == kMaxAnchorInstanceCount {
07         anchorOffset = max(frame.anchors.count - kMaxAnchorInstanceCount, 0)
08     }
09     
10     for index in 0..

注意

在更复杂的AR体验中,你可以用撞击实验和平面探测来找现实世界曲面的各个位置。细节方面,请看planeDetection性能和hitTest(_:types:)类函数。在这两种情况下,ARKit提供结果作为ARAnchor对象,所以你仍使用锚点转换来放置虚拟内容。

用真实感光照计算来渲染

当你设置着色器,绘制场景中的3D内容,使用每个ARFrame对象的预估光照信息,以此生成更真实的阴影:

[代码]:

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

注意

对于全套Metal设置和本例中的渲染指令,请见完整的Xcode模板。(请创建配有AR模板的新款iOS应用软件,并从Content Technology弹出菜单上选择Metal语言。)
 

0

刚刚发布

深度VR观察

友情链接

Top ->