FC2ブログ

頭と尻尾はくれてやる!

パソコンおやじのiPhoneアプリ・サイト作成・運営日記


フロントカメラが得た画像を自分の顔のテクスチャ用にシェーダに渡したい(2)

フロントカメラが得た画像を自分の顔のテクスチャ用にシェーダに渡したい(1)
↑この続き。


【フラグメントシェーダにテクスチャとしてフロントカメラの画像を渡す】

png画像などをテクスチャとしてシェーダに渡すことはできたので、ようやくフロントカメラで得た画像をシェーダに渡す。

Displaying an AR Experience with Metal | Apple Developer Documentation
↑Appleのドキュメントにこのような記事がある。
これによるとARFrameオブジェクトからCVPixelBufferRefを得てさらにMTLTextureをシェーダに渡している。
よくわからないのが、CVPixelBufferRefがRGBではなくYCbCr形式で、YとCbCrをそれぞれ得て、2枚のMTLTextureをフラグメントシェーダに渡して、そこでRBGに変換せよ、と、、、なんだその処理は?と思うけど、ハードや低レイヤーのことがわかっている人には当たり前なのだろうか?
ともかくそういう流れなので、これを参考にObjective-Cで書いてみた。
{
    id<MTLDevice> device = MTLCreateSystemDefaultDevice();
    CVMetalTextureCacheCreate(kCFAllocatorDefault,
                              nil,
                              device,
                              nil,
                              &textureCache);
}
↑下準備でテクスチャのキャッシュを作っておく。YとCbCrの2枚あるけど、1つでいいっぽい。

シェーダの設定はたいして変わらないが、頂点シェーダで画像変換のために使う行列を一つ渡す設定がある。
{
    // CGAffineTransform affineTransform = [frame displayTransformForOrientation:UIInterfaceOrientationPortrait viewportSize: sceneView.bounds.size];
    CGAffineTransform affineTransform = [frame displayTransformForOrientation:UIInterfaceOrientationPortrait viewportSize:CGSizeMake(414.0, 862.0)];//—(1)

    
    CATransform3D caTransform = CATransform3DMakeAffineTransform(affineTransform);
    NSValue *value = [NSValue valueWithCATransform3D:CATransform3DInvert(caTransform)];
    [material setValue:value forKey:@"displayTransform"];


    faceMaterial = faceGeometry.firstMaterial;//—(2)
}
↑(1)数字をベタ打ちなんてやってはいけませんね。swiftで書かれたAppleのサンプルコード(※1)だと sceneView.bounds.size となっている部分だけど、同じように書くと

main thread onlyの警告画像

↑このように [UIView bounds] must be used from main thread only ってXcodeに怒られる(動くけど)。なのでiPhone XS Max用の数字がそのまま入ってるけどただの手抜きです。

(2)faceMaterialはSCNMaterialオブジェクトで後で使うのでこのあたりで得ておく。


次に、カメラ画像をシェーダに渡す設定をする。頻繁にコールされる処理。
-(void)renderer:(id<SCNSceneRenderer>)renderer didUpdateNode:(SCNNode *)node forAnchor:(ARAnchor *)anchor //—(3)
{
    ARSCNFaceGeometry *faceGeometry = (ARSCNFaceGeometry *)node.geometry;

    if ([anchor class] !=  [ARFaceAnchor class]) return;
    
    ARFaceAnchor *faceAnchor = (ARFaceAnchor *)anchor;
    [faceGeometry updateFromFaceGeometry:faceAnchor.geometry];//—(4)

    
    ARSCNView *sceneView = (ARSCNView *)renderer;
    ARFrame *frame = sceneView.session.currentFrame;
    [self updateTexturesForFrame:frame];
}
(3)↑ここではARSCNViewDelegateの上のメソッドに記述してるけど、ARSessionDelegateの
- (void)session:(ARSession *)session didUpdateFrame:(ARFrame *)frame;
などでも可。
(4)は目や口の頂点座標などをアップデートする部分なので、これを忘れると表情が変わらない。
-(void)updateTexturesForFrame:(ARFrame *)frame
{
    CVPixelBufferRef pixelBuffer = frame.capturedImage;
    if (CVPixelBufferGetPlaneCount(pixelBuffer) < 2) {
        return;
    }

    id<MTLTexture> textureY = [self createTextureFromPixelBuffer:pixelBuffer pixelFormat:MTLPixelFormatR8Unorm planeIndex:0];
    id<MTLTexture> textureCbCr = [self createTextureFromPixelBuffer:pixelBuffer pixelFormat:MTLPixelFormatRG8Unorm planeIndex:1];
    
    if ( (textureY)&&(textureCbCr) ) {
        
        SCNMaterialProperty *propertyY = [SCNMaterialProperty materialPropertyWithContents:textureY];
        SCNMaterialProperty *propertyCbCr = [SCNMaterialProperty materialPropertyWithContents:textureCbCr];
        
        [faceMaterial setValue:propertyY forKey:@“textureY”];
        [faceMaterial setValue:propertyCbCr forKey:@“textureCbCr”];
    }
}
↑2枚のMTLTextureを作りシェーダに教える部分。
-(id<MTLTexture>)createTextureFromPixelBuffer:(CVPixelBufferRef)pixelBuffer pixelFormat:(MTLPixelFormat)pixelFormat planeIndex:(NSUInteger)planeIndex
{
    id<MTLTexture> mtlTexture = nil;
     
    size_t width = CVPixelBufferGetWidthOfPlane(pixelBuffer, planeIndex);
    size_t height = CVPixelBufferGetHeightOfPlane(pixelBuffer, planeIndex);
    
    CVMetalTextureRef texture = nil;

    CVReturn status = CVMetalTextureCacheCreateTextureFromImage(nil, textureCache , pixelBuffer, nil, pixelFormat, width, height, planeIndex, &texture);
    
    if (status == kCVReturnSuccess) {
        mtlTexture = CVMetalTextureGetTexture(texture);
    }
    CFRelease(texture);
    
    return mtlTexture;
}
↑1つのMTLTextureを得るところ。解放を忘れると停止する。

次にシェーダ部分、、、だけど長くなったので次回に。


※1 Creating Face-Based AR Experiences | Apple Developer Documentation



<< フロントカメラが得た画像を自分の顔のテクスチャ用にシェーダに渡したい(3)  TopPage  フロントカメラが得た画像を自分の顔のテクスチャ用にシェーダに渡したい(1) >>

コメント


管理者にだけ表示を許可する
 

トラックバック

トラックバックURL
http://ringsbell.blog117.fc2.com/tb.php/1268-be30f11d




Copyright ©頭と尻尾はくれてやる!. Powered by FC2 Blog. Template by eriraha.

FC2Ad