頭と尻尾はくれてやる!

iOSアプリなどの開発日記です


scn/daeファイルから3DオブジェクトをMetalで描画する

SCNGeometryからMDLMeshを作成する
↑ここで顔のメッシュをMetalで表示することに加え、簡単な形状を描くのはできてた。
なので今回はscnファイルのデータを表示させてみようとした。
Appleのサンプルコード(※1)にはscnファイルでいくつかの3Dオブジェクトがある。
これをプロジェクトに持ってきておき、読み込んで使う。

scnファイル中身

↑使うのはcoordinateOrigin.scnとする。このように三つの軸が表示するオブジェクト。
{
    NSString *filePath = [[NSBundle mainBundle] pathForResource:@"art.scnassets/coordinateOrigin" ofType:@"scn"];
    NSURL *fileURL =  [NSURL fileURLWithPath:filePath];
    SCNReferenceNode *node = [[SCNReferenceNode alloc] initWithURL:fileURL];—(1)
    [node load];

    SCNGeometry *scnGeo = node.geometry;//—(2)
    NSAssert(scnGeo, @"scnGeo is nil!”);//—(3)

    mdlMesh = [MDLMesh meshWithSCNGeometry:scnGeo
                                 bufferAllocator:metalAllocator];
}
(1)SCNReferenceNodeはSCNNodeのサブクラス。
よって、(2)のようにすればSCNGeometryオブジェクトを取得でき、MDLMeshオブジェクトを作成できる、と思ったのだが、、、
なぜか(3)のチェックで止まるのだ(いや、実際にはNSAssertを入れてなくて落ちるでもなく単に表示されなくてなんでだ?!ってはまっていたんだが)。

調べるとこのcoordinateOrigin.scnファイルには最初のスクショにあるようにx,y,z軸の直線(実際には直方体)のオブジェクトが3つある。
SceneKitだとrootNodeの子として3つの軸オブジェクトがある、という構成なのだろう。
{
    SCNNode *rootNode = [node.childNodes firstObject];
    SCNNode *childNode0 = [rootNode.childNodes objectAtIndex:0];

    SCNGeometry *scnGeo = childNode0.geometry;

}
↑ということで、nodeの子供の下にある3つの内の最初のSCNNodeオブジェクトからSCNGeometryオブジェクトを取得すれば、、、

オブジェクトを表示1

↑y軸に相当するオブジェクト(scnファイルの構成には最初にあったよね)が原点に表示されている。ローカル座標を持っているはずだが頂点データをシェーダに渡しただけなので当然無視されている。
つまり3軸分を表示させようとするとそれぞれの頂点データなどをからMDLMeshオブジェクトを3回作成しなければならないということか。しかもローカル座標を考慮して。
SceneKitだとそんなのはフレームワーク側がよきに計らってくれるわけだが、、、

なかなか面倒なので実際にやろうと思うとBlenderなどで1かたまりの頂点データを作成して、そのデータを使うのがいいのかもしれない。

Blenderで作成したオブジェクト

↑試しにBlenderで1かたまりのデータとしてこのようなオブジェクトを作成し、daeファイルで出力。それを同様に処理する。

オブジェクトを表示2

↑これなら表示は簡単に可能。
このあたりが現実的か。



※1 Appleのサンプルコード↓
Creating Face-Based AR Experiences | Apple Developer Documentation
スポンサーサイト






ARKitで顔を描くのにMetalのMTKViewだと白っぽくならなかった

以前からARKitで顔の頂点を動かす時に顔がどういうわけか白っぽくなってた。



↑こんな感じだ。
この顔の少し白くなっている部分はARSCNFaceGeometryの頂点データやフロントカメラの画像などをシェーダに渡して描かれている。

Displaying an AR Experience with Metal | Apple Developer Documentation
↑リファレンスにあるようにフロントカメラの画像はYとCbCrそれぞれに相当する2枚のidをシェーダに渡し、シェーダでYCbCr→RGBに変換している。

ところがどういうわけか周囲の画像より少し白っぽくなるのである。
自分でY,CbCrの値や変換マトリックスの値を変えたみたり、ライティング処理を適用させようとしたり、いっそ背景も同じシェーダで描画するようにしてみたりしたのだが、ことごとくうまくいかず。
ようやくたどり着いたのがMetalKitを使うこと。

Appleのサンプルコード(※1)ベースなのでARSCNView(SCNViewのサブクラス)に描画していた。
これをこれをMTKViewに描くようにした。
つまりMetalなのでpipelineやbuffers、render encoder、、、などなどやったことがある方ならわかると思うが書くべきコードの量が圧倒的に増えるのだ。
とは言ってもXcodeで新規プロジェクトを作成する時にARアプリで描画をMetalで指定すれば処理内容を勉強することができる。
これをベースにして作成したのがこちら↓



顔が白くならず周囲の画像と変わりない。区別がつかないので本当に意図するシェーダを通っているのか戸惑うほどだ。
フラグメントシェーダに渡しているものもシェーダ内での処理(RGBへの変換)も同じ。

ARSCNViewの不具合なのか仕様なのかもわからないが、とりあえずMTKViewだとなんとかなることはわかった。



※1 Appleのサンプルコード↓
Creating Face-Based AR Experiences | Apple Developer Documentation


iPhone 6のフロントパネルを自分で交換修理してみた

ガラスが破損したiPhone 6

↑iPhone 6を落としてフロントのガラスが割れてしまった。

iPhone 6の近接センサーを交換した

↑このiPhone 6は以前業者にバッテリー交換を依頼したら通話ができなくなってしまい、近接センサーを自分で交換したiPhone 6だ。



↑幸いAmazonでフロントパネルも手頃な値段で売っていたので、交換を自分でやってみようと思ったのである。

分解工房・iPhone 6/フロントパネル(ガラス)交換修理方法
↑参考にしたのはこちらのページ。ホント感謝、感謝だ。

フロントパネル取り外し直後

↑フロントパネル部分を完全に切り離した状態。ここまでは前にやったことがある。

ホームボタン取り外し直後

↑ホームボタンを外したところ。どこが分離するのかよくわからなかったがなんとか外れた。

フロントカメラ、スピーカ取り外し直後

↑フロントカメラ、スピーカーなどを外したところ。

フロントパネルのバックプレート止めネジ部分

↑液晶パネル裏のバックプレートを外そうとしたが、参考ページには6つのネジがあるらしいけど4つ(白い丸のところ)しかなかった。赤い丸の部分にはネジがなかったが、まあいいや。

バックプレート取り外し後

↑バックプレートを外したところ。
下がAmazonで買った新しいフロントパネル。

取り外した部品のまとめ方

↑ちなみに取り外したネジや部品は工程ごとに紙で作成した”ケース”に入れて置いておいた。


後は逆に組み立てていくだけだ。


ところがその時に気付いたのだが、、、

新パネルのフロントカメラ部分

↑購入したフロントパネルにはフロントカメラを位置決めするため(と勝手に思っている)の透明な部品がない。

旧パネルのフロントカメラ部分

↑元のフロントパネルには同じところに透明な部品がある。
この部品はパネルに強固に接着されてるようで取れそうにないし、取れたところで正しい位置をだせるとは思えない。

フロントカメラ取り付け部

↑とりあえず取り付けた後に横から見ると、、、すごく斜め向いてる。そりゃそうだろう。
これはフロントカメラは使えないな、と諦めた。


気持ちを切り替えて組み立てて行ったのだが、、、

コネクタの誤配置

↑フロントパネルとバッテリー側を接続するコネクタ部分の上下関係(重なり方)を間違えていた。
一番横が長いのが真ん中になってるけど、

コネクタの接続部分

↑反対側は違う。一番横長なのが端にある、、、orz
こんなの間違えてたら組みあがらないようになっているのだろう、と深く考えずに組み立てていたのだ。

そんな感じで組み立て直しもあったのだが、、、

修理完成後のiPhone 6

↑なんとか元の形にはなった。
今の所、普通に使えている。
ちゃんと会話もできるのでスピーカ、近接センサあたりも問題がなかったようだ。
問題のフロントカメラがどういうわけかちゃんと映る。斜め向いてるのかもしれないが、端っこが表示されない、とかの不具合はない。

液晶の影

↑ただ、ディスプレイやホームボタンを強く押すと液晶独特の影のような正しく表示できていない部分が現れる。前にはこんなのなかった。
また、ホームボタンが少し奥まってる気がする。
どちらも使用には問題ないだろう。

なお、前に工具を買っていたため、今回フロントパネルに付属していた工具は一切使わなかった。
その工具はこちら↓




ビット演算子のチルダ(~)って何やねん?

久々にMetalで描画するためにいろいろとコードを見てたりするのだが、Appleのコード(※1)に下のような面白いのがあった。
// The 256 byte aligned size of our uniform structures
static const size_t kAlignedSharedUniformsSize = (sizeof(SharedUniforms) & ~0xFF) + 0x100;
シェーダに渡すデータサイズを256バイトの倍数だといくらになるのかを計算している。
SharedUniformsという構造体(※2)のデータサイズを満たしつつ、256の倍数だといくらかを得たい。メモリの確保も必要になるので当然なるべく小さい方がいい。
構造体の中身がどんなのでもこの式でOKというものだけど、、、

なんて難解なんだ!

そんな難解な記述しなくてもええやん、と突っ込みたくなる。

なんだよ?0xFFの前の~(チルダ)は?!

調べるとビット演算でチルダはビット反転(補数を作る)なんだそうな(※3)。
0xFFは2進数だと全部1だから、その補数ってことは2進数では
…11 0000 0000
ってことか?
これとの&だから16進数で言うと下2桁をゼロにして、それより上のビットはそのままをキープできる。ここはすでに256の倍数。下2桁分は切り捨ててるようなものだから、あとで+0x100してる。
0x00との&だと上の桁までゼロにしちゃうから0xFFの補数を使ってたのか。



※1 ↓ここにも書いてるようにXcodeでプロジェクト新規作成時にARKitアプリで描画にMetal、言語にObjective-Cを選択した場合にできるコード。
SCNGeometryからMDLMeshを作成する

※2 ↓SharedUniforms はこんな構造体として定義されてる
typedef struct {
    // Camera Uniforms
    matrix_float4x4 projectionMatrix;
    matrix_float4x4 viewMatrix;
    matrix_float4x4 displayTransform;
    
    // Lighting Properties
    vector_float3 ambientLightColor;
    vector_float3 directionalLightDirection;
    vector_float3 directionalLightColor;
    float materialShininess;
} SharedUniforms;
※3 参考サイト
もう一度基礎からC言語 第19回 いろいろな演算子~ビット演算子 ビット演算子


SCNGeometryからMDLMeshを作成する

ARKit + TrueDepth Cameraでface trackingなどをするのに、描画にSceneKitではなくMetalを使おうと試している。

Xcodeで新プロジェクトを作成する時に、Augmented Reality Appを選択、Content TechnologyでMetalを選択。
すると作成されるコードはMetalで描画する方法などとても参考になる。
この基本アプリは画面をタップすると空間に立方体を”置く”。この立方体の形状はMDLMeshクラスの
newBoxWithDimensions:segments:geometryType:inwardNormals:allocator:
メソッドで作成している。

最終的には顔のメッシュを表示したいので、ARSCNFaceGeometry(これはSCNGeometryのサブクラス)からデータを持ってくる必要がある。
ありがたいことにMDLMeshクラスには直方体や球のような基本形状以外に、
+ (instancetype)meshWithSCNGeometry:(SCNGeometry *)scnGeometry bufferAllocator:(id<MDLMeshBufferAllocator>)bufferAllocator;
というSCNGeometryオブジェクトから作成できるメソッドがある。
これを使えば顔の形状をMetalで描画できるのでは?という考えだ。

なので、まずは簡単なSCNBox(これもSCNGeometryのサブクラス)で直方体オブジェクトを作ってそれをMetalで描画してみようとしたのだが、、、
{
    SCNBox *scnGeo = [SCNBox boxWithWidth:0.1 height:0.1 length:0.1 chamferRadius:0.01];
    
    MDLMesh *mesh = [MDLMesh meshWithSCNGeometry:scnGeo
                                 bufferAllocator:metalAllocator]; 
}
Xcodeのエラーメッセージ

↑なぜかエラーがでる。
No known class method for selector 'meshWithSCNGeometry:bufferAllocator:'
だとー?君は何を言ってる?
#import <ModelIO/ModelIO.h>
#import <MetalKit/MetalKit.h>
#import <SceneKit/SceneKit.h>

↑ちゃんとインポートしてるし、メソッドのところで⌘+クリックするとちゃんと定義のところへ飛んで行くじゃないか?

しばらく調べてようやく気付いたのが、このメソッドはカテゴリで定義されてて別ファイルに記述されてる、、、
#import <SceneKit/ModelIO.h>
↑これを追加したらいけた!そういうことだったのね。
↑とりあえず、face trackingして顔のanchorにSCNBoxから作成したメッシュを表示させることはできた。




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