ARKit自定义SCNMaterial的渲染

本文转自: XJ的博客

本文主要介绍了ARKit如何将照相机捕获到的数据传入SceneKit自定义着色器进行渲染,本文内容假定读者有一定的ARKit、SceneKit及Metal的基础。

ARKit

今年的 WWDC 开发者大会上,苹果为我们带来了 ARKit。它做到了将优秀的 AR 体验带给无数的消费级设备,其重要性我们这段时间来早就了解了。不过苹果一个巴掌毕竟拍不响,这些还都需要开发者们来支持。更多关于ARKit的介绍可以访问苹果的开发者网站

SceneKit

在 WWDC 2012 (那时 OS X 系统还在用喵系命名),Apple 向 OS X 开发者们介绍了 SceneKit,这个 Cocoa 下的 3D 渲染框架。在第一版通用 3D 渲染器发布后,一年内又陆续增加了像 shader (着色器) 修改器、节点约束、骨骼动画等几个强大的特性 (随 Mavericks 发布)。今年,SceneKit 变的更加强大,支持了粒子效果、物理引擎、脚本事件以及多通道分层渲染等多种技术,而且,对于很多人来说更关键的是,它终于可以在 iOS 中使用了。

一上手,我发现 SceneKit 最强大和脱颖而出的地方,就是可以与 CoreImageCoreAnimationSpriteKit 等已有的图形框架相互整合及协作,这在其他游戏引擎中可不常见,但如果你本身就是个 Cocoa 或 Cocoa Touch 框架下的的开发者的话,就会感到相当亲切了。 关于SceneKit介绍可以到这里了解。

创建ARKit的三个模板之一就是SceneKit,并且SceneKit在开发3D游戏优势十分明显。

开始

效果效果

由于使用了SceneKit的ARKit,不支持OpenGLESrenderAPI,所以以下内容的介绍为Metal。

效果如上图,在渲染自定义物体时我们可能需要传入背景去渲染。那么可以通过下面的方法去从CVPixelBufferRef获取YUV数据。

获取YUV数据

- (id<MTLTexture>)_createTextureFromPixelBuffer:(CVPixelBufferRef)pixelBuffer
pixelFormat:(MTLPixelFormat)pixelFormat
planeIndex:(NSInteger)planeIndex {
id<MTLTexture> mtlTexture = nil;
const size_t width = CVPixelBufferGetWidthOfPlane(pixelBuffer, planeIndex);
const size_t height = CVPixelBufferGetHeightOfPlane(pixelBuffer, planeIndex);
CVMetalTextureRef texture = NULL;
CVReturn status = CVMetalTextureCacheCreateTextureFromImage(NULL, _capturedImageTextureCache, pixelBuffer, NULL, pixelFormat, width, height, planeIndex, &texture);
if(status == kCVReturnSuccess) {
mtlTexture = CVMetalTextureGetTexture(texture);
CFRelease(texture);
}
return mtlTexture;
}

调用并且传递数据

SCNMaterial *material;
/*
material = 某个SCNGeometry的material
*/
// 获取YUV数据
id<MTLTexture> capturedImageTextureY = [self _createTextureFromPixelBuffer:pixelBuffer pixelFormat:MTLPixelFormatR8Unorm planeIndex:0];
id<MTLTexture> capturedImageTextureCbCr = [self _createTextureFromPixelBuffer:pixelBuffer pixelFormat:MTLPixelFormatRG8Unorm planeIndex:1];
// 使用SCNMaterialProperty来传递数据,不支持直接设置给SCNMaterial
SCNMaterialProperty *capturedImageTextureYMaterial = [SCNMaterialProperty materialPropertyWithContents:capturedImageTextureY];
SCNMaterialProperty *capturedImageTextureCbCrMaterial = [SCNMaterialProperty materialPropertyWithContents:capturedImageTextureCbCr];
// SCNMaterial设置KVC
[material setValue:capturedImageTextureYMaterial forKey:@"capturedImageTextureY"];
[material setValue:capturedImageTextureCbCrMaterial forKey:@"capturedImageTextureCbCr"];
SCNProgram *program;
/*
初始化着色器……
*/
// 为SCNMaterial设置着色器
material.program = program;

VSH和FSH

VSH
#include <metal_stdlib>
using namespace metal;
#include <SceneKit/scn_metal>
#include "ShaderDatas.h"
struct MyNodeBuffer {
float4x4 modelTransform;
float4x4 modelViewTransform;
float4x4 normalTransform;
float4x4 modelViewProjectionTransform;
};
typedef struct {
float4 position [[ attribute(SCNVertexSemanticPosition) ]];
float2 texCoord [[ attribute(SCNVertexSemanticTexcoord0) ]];
float4 normal [[ attribute(SCNVertexSemanticNormal) ]];
} MyVertexInput;
struct SimpleVertex
{
float4 position [[position]];
float2 texCoord;
float3 normal;
float4 ambient;
float3 light;
float3 eye;
float sinTime;
float cosTime;
float random01;
};
vertex SimpleVertex myVertex(MyVertexInput in [[ stage_in ]],
constant SCNSceneBuffer& scn_frame [[buffer(0)]],
constant MyNodeBuffer& scn_node [[buffer(1)]])
{
/*
顶点处理
*/
}
FSH
fragment float4 glassFragment(SimpleVertex in [[stage_in]],
texture2d<float, access::sample> capturedImageTextureY [[texture(0)]],
texture2d<float, access::sample> capturedImageTextureCbCr [[texture(1)]])
{
/*
处理图片效果
*/
}

接下去就是编写脚本,与正常的Metal脚本没有什么大的区别。只是需要传入SceneKit的一些诸如灯光位置、颜色、顶点、纹理坐标等等数据。更详细的请查阅SCNProgram文档

小结

在一些需要结合背景的渲染功能中(例如:放大镜、彩色玻璃),我们可以使用SCNProgram进行单个几何体的渲染。

猜你喜欢

转载自blog.csdn.net/sh15285118586/article/details/80493812