Apple Engine

Apple, iPhone, iOS, その周辺のことについて

Xcode 9 の Scene Editor、iOS 11 の Scene File の Procedual Sky のバグについて

Xcode 10 を触っていて気づいたのだが、Xcode 9 の Scene Editor、iOS 11 の Scene File では Procedual Sky で正しい表示がされないバグがある。

ちなみに Scene Editor での Procedual Sky の適応方法は、何らかのノードを選択後、Scene Inspector (Command + Option + 7) を押して、Background や Environment で Procedual Sky を選択する。

f:id:x67x6fx74x6f:20180703194346p:plain

 

問題点

問題点が2点ある

  • Xcode 9 の Scene Editor では Adjustments のパラメーターが適応されない
  • Xcode 9 の作成する Scene File の Adjustments のパラメーターが保存されるが、実行時適応されない

 

Xcode の Scene Editor 自体、SceneKit で作成されているため、Scene Editor で出ているバグはビルドした実際のアプリでも同様のバグが発生する。

ちなみに、Adjustments のパラメーターはトーンマップ用のパラメーター。

  

原因

Scene Editor で設定する Procedual Sky は Model I/O の MDLSkyCubeTexture を使用しており、MDLTexture のサブクラスとなっている。

テクスチャとして空をキューブマップとして描画するのだが、中身は MTLTexture なのでパラメーターを与えただけではテクスチャ内容の変更ができない模様。

update() 関数があり、これが呼ばれるとテクスチャの再生成し MDLSkyCubeTexture として生成するのだと思われる。

 

このような流れで MDLSkyCubeTexture を使用するのだが、
どうやら Scene Editor や SceneKit では update() 関数が呼ばれていない可能性がある。

 

MDLSkyCubeTexture について

6面のキューブマップテクスチャが生成され、背景画像が作成される。
キューブマップの背景に関しては過去記事を参照。

appleengine.hatenablog.com

 

内部的には幅 64、高さ 384 の画像のテクスチャとなっている模様。
(もしかしたら Mipmap で1番小さい画像を取得している可能性あり)

 

パラメーター

Scene Editor で設定できるのものが全てのパラメーターとなっている

 

シミュレーションパラメーター

パラメーター名 説明
turbidity Float 空のかすみや曇り具合
sunElevation Float 太陽の位置
upperAtmosphereScattering Float 空の色に影響を与える値
groundAlbedo Float 空の透明度に影響を与える値
groundColor CGColor? 擬似的な地面の色
horizonElevation Float 地面の色をレンダリングする際に下の中心を基準にした角度 (単位はラジアン)

 

トーンマップパラメーター

パラメーター名 説明
gamma Float 適用するガンマ補正値
exposure Float 適用する露出補正値
brightness Float 明るさを強調させるための値
contrast Float コントラストを強調させるための値
saturation Float 彩度を強調させるための値
highDynamicRangeCompression vector_float2 テクスチャの色の輝度の圧縮曲線を決める2つのパラメータ(Photoshop のトーンカーブのようなもの)

 

Xcode 10 のシーンエディターの値を使い、Xcode 9 で MDLSkyCubeTexture を表示してみる

以下、Xcode 10 でのパラメーター。
Unity のデフォルトのような背景になる。

パラメーター名
turbidity 1.0
sunElevation 0.778
upperAtmosphereScattering 0.4
groundAlbedo 0.33
groundColor #383838 (0.22,0.22,0.22,1.0)
horizonElevation 0
gamma 0.05
saturation -2

 

コード

コードで書いてみる。

SCNScene の background に設定するため、SCNScene の初期化の後に以下を書く。

// MDLSkyCubeTexture の初期化
let skyTexture = MDLSkyCubeTexture(
    name: "sky", 
    channelEncoding: .uInt8,
    textureDimensions: vector_int2(1024, 1024),
    turbidity: 1.0,
    sunElevation: 0.778,
    sunAzimuth: 0,
    upperAtmosphereScattering: 0.4,
    groundAlbedo: 0.33
)

let groundColor = UIColor(red: 0.22, green: 0.22, blue: 0.22, alpha: 1.0)
skyTexture.groundColor = groundColor.cgColor

skyTexture.gamma = 0.05
skyTexture.exposure = 0
skyTexture.brightness = 0
skyTexture.contrast = 0
skyTexture.saturation = -2

skyTexture.update()

scene.background.contents = skyTexture
scene.lightingEnvironment.contents = skyTexture

 

MDLSkyCubeTexture の初期化には名前と channelEncoding、textureDimensions の値がある。

channelEncoding はピクセルフォーマット。
SceneKit での Metal は bgra8Unorm なので uInt8 を設定している。

textureDimensions はキューブマップ各1面の大きさ。
基本的には正方形を指定する形になると思われる。

 

表示結果

このような感じになる。

f:id:x67x6fx74x6f:20180703194758p:plain

 

update() 関数を呼ばないとこんな感じ。

f:id:x67x6fx74x6f:20180703194838p:plain

ARKit 2 の環境マッピング AREnvironmentProbeAnchor を試してみる

Beta 版から記事を起こしているため今後変更される可能性あり。

ARKit 2 で環境マップテクスチャの作成ができるようになった。

通常は AREnvironmentProbeAnchor から環境マッピングのテクスチャを作成するのだが、SceneKit の場合は設定をするとデフォルトでオンになるため、1行書くだけで実装できる。

 

Xcode 10 Beta で Xcode 9 と同様に「Augmented Reality App」でプロジェクトを作成。

viewWillAppear の ARWorldTrackingConfiguration に設定を追加するだけ。

let configuration = ARWorldTrackingConfiguration()
configuration.environmentTexturing = .automatic

 

あとは、Physically Based のマテリアルに Metalness の Float Value を 1、Roughness を Float Value を 0 に適応したアンカーやジオメトリを置くと環境マッピングされたマテリアルの反射を試すことができる。

 

automatic の AREnvironmentProbeAnchor 振る舞い

environmentTexturing を automatic で設置される AREnvironmentProbeAnchor は、 ドキュメントには書かれていないため詳細は不明だが、試したところ、 AREnvironmentProbeAnchor の変化があった時の端末の位置と向きを取る。

起動し端末を立てたままそのままでしばらくすると、位置は XYZ 軸で約 0 の、回転は Y軸が約 -90、extent は XYZ 軸で無限大を取る。

また、6面のキューブマップの1面の画像は 256 x 256 で構成されている模様。
(iPhone 6s で試しているため他の端末では解像度が異なるかも?)

負荷的には iPhone 6s で 60fps 切ったり切らなかったり。

 

AREnvironmentProbeAnchor を取得する

ARSessionDelegate の func session(_ session: ARSession, didUpdate anchors: [ARAnchor]) を使用し、 ARAnchor の配列から AREnvironmentProbeAnchor を調べて、そのアンカーの environmentTexture から MTLTexture を取得する。

environmentTexture はすぐに生成されないのであるか無いか確認する必要があり。
また、MTLTextureType が CubeMap なので、そのまま CIImage などに変更できないので注意。

 

サンプル

environmentTexturing を automatic にして Metalic な球体を 0.5m 上に置いたサンプルを作成してみた。

github.com

 

まとめ

かなり簡単に現実空間のものを反射するマテリアルができることがわかると思う。
より精度を高めたい場合は、environmentTexturing を manual にして、自前で AREnvironmentProbeAnchor をいくつか置くと良いと思われる。

ARKit 2.0 の更新内容 (iOS 12 Bata 2)

NDA の問題でスクリーンショットなどセッション資料のものを使用している。
また、Beta 版から記事を起こしているため今後変更される可能性あり。

遅くなってしまったが iOS 12 Bata の ARKit 2.0 更新内容を見てゆく。

Bata 2 の内容だが 1.0 の Beta の際は Configuration 関連の名称が変更したぐらいなので、 今回も機能的には GM までほぼ変更はないと思われる。

 

増えた機能

  • 環境マップテクスチャの作成
  • 物体認識と物体のトラッキング
  • 物体認識用の物体のスキャニング
  • 画像認識と画像のトラッキング
  • 現実世界から得た特徴点からのワールドマップの作成と復元(他の端末との空間共有などに使用)
  • 顔認識での舌の動きの取得

 

f:id:x67x6fx74x6f:20180622194719p:plain

f:id:x67x6fx74x6f:20180622194733p:plain

 

ワールドマップによるワールドトラッキングについて

WWWDC のセッションやドキュメントでは以下の2つを想定している。

  • Multiuser AR experiences
  • Persistent AR experiences

Multiuser は空間と仮想オブジェクトであるアンカーを他の端末に渡し仮想空間を共有、 Persistent は空間と仮想オブジェクトであるアンカーを保存し、再開した際に復元する。

 

物体認識用の物体のスキャニング

ARKit 自体には簡単にスキャンする UI 等がないため、地道に実装することになる。

特徴点のスキャンが完了したら .arobject のファイルとして保存し、
物体認識時に参照用のオブジェクトとして使う。

Apple のサンプルファイルでスキャンできるため、こちらを使用して振る舞いを確かめるとよいかと。

Scanning and Detecting 3D Objects | Apple Developer Documentation

 

ちなみに、スキャンはマシンパワーを使いまくるので基本的には物体のスキャニングは開発者側が事前に行なっておいた方がよいと思われる。
また、ある程度正確な特徴点のスキャンが行わないと物体認識時に上手く行かない場合があるので注意。

画像認識同様に真っ白で物体のコントラスト差がなく特徴点が認識しづらいものや、透明なもの、鏡面の物質などは物体のスキャンに向いていない。

 

既存機能の変更点

機能的な変更は特にないが、移動や回転などで使用している vector_float3 や matrix_float4x4 などが Accelerate Framework の simd で設定するものに変更されている。

 

増えたクラス

  • AREnvironmentProbeAnchor
  • ARWorldMap
  • ARReferenceObject
  • ARObjectAnchor
  • ARObjectScanningConfiguration
  • ARImageTrackingConfiguration

 

以下、変更を見てゆく。
今回あまりコードに触れていないので、翻訳が間違っていたらごめんなさい。

 

ARSession

ARSession

追加

func getCurrentWorldMap(completionHandler: @escaping (ARWorldMap?, Error?) -> Void)

ARWorldMap でワールドトラッキングの際、セッションの空間のマッピング状態とアンカーのセットをカプセル化したオブジェクトとして返す。

このメソッドを使用してセッションのワールドマップを保存した後、 initialWorldMap プロパティに割り当て、run(_: options:) を使用すると同じ空間認識とアンカーを別のセッションで開始する。

ARWorldTrackingConfiguration で Configuration を設定しないと即 completionHandler が返るので注意。

 

createReferenceObject(transform:center:extent:completionHandler:)

セッションでワールド空間の指定した領域から参照オブジェクト (3D オブジェクトの物体認識検出用オブジェクト) を作成する。

抽出する領域のローカル座標の原点と方向を設定する transform、
抽出する領域のバウンディングボックスの中心を定義する center、
抽出する領域の幅、高さ、深さを中心点としたローカル座標の範囲 extent を設定する。

completionHandler では、参照オブジェクトとなるワールドマップの指定された領域を表す ARReferenceObject を渡す referenceObject、エラー時の説明する ARError を渡す error がある。

 

注意点

このメソッドは参照オブジェクトのスキャンを可能にする ARObjectScanningConfiguration が設定されているセッション内で動作し、設定されていない場合は即 completionHandler が返る。

参照オブジェクトはスキャンした状態を元に原点が設定される。参照オブジェクトの原点を抽出した後で調整するには、applyingTransform メソッドを使用する。
例えば、スキャンした現実のフィギュアの頭の上に吹き出しを出したい時など、原点を頭の上に設定しておくと吹き出しを出す高さを考慮ても済む。

 

変更

setWorldOrigin(relativeTransform:)

引数が matrix_float4x4 から simd_float4x4 に変更。

 

ARSessionObserver

ARError.Code

追加

エラー用 enunm

名前 コード
fileIOFailed 500
insufficientFeatures 400
invalidConfiguration 303
invalidReferenceObject 301
invalidWorldMap 302
microphoneUnauthorized 104

 

ARError

追加

エラードメインと上記のエラーコード取得用

  • errorDomain: String
  • fileIOFailed: ARError.Code
  • insufficientFeatures: ARError.Code
  • invalidConfiguration: ARError.Code
  • invalidReferenceObject: ARError.Code
  • invalidWorldMap: ARError.Code
  • microphoneUnauthorized: ARError.Code

 

ARSCNView

追加

func unprojectPoint(_ point: CGPoint, ontoPlane planeTransform: simd_float4x4) -> simd_float3?

ARKit によって指定した 2D ビューの点から投影されたレイが、
指定した平面と交差するワールド空間の 3D ポイントを返し、レイが平面と交差しない場合 nil を返す。

point は平面上に投影する 2D ビュー空間の点。 planeTransform は3D ワールド空間での平面の位置と方向を指定する変換行列で、 この変換が定義するローカル座標空間の XZ 平面となる。

 

ARWorldTrackingConfiguration

追加

var initialWorldMap: ARWorldMap?

getCurrentWorldMap を使用してセッションのワールドマップを保存した後、その構成のワールドマップを復元する際に使用する。

ARKit が記録されたワールドマップを現在の環境と合わない場合、
セッションは初期状態の ARCamera.TrackingState.Reason.relocalizing 状態になる。
たとえば、ワールドマップが記録された場所とはまったく異なる場所にいる場合などで合わなくなる。

 

var maximumNumberOfTrackedImages: Int

同時にトラッキングできる検出画像の最大数。
0 より大きい値に設定すると、画像トラッキングが可能になる。
以前のものとは異なり、空間を移動しても連続してトラッキングされる。

画像検出は、ARKit が最初にカメラビューで検出したときの画像の位置と向きを報告し、その後はまれな更新しか行われない。

ARWorldTrackingConfiguration では少数の画像しか同時にトラッキングできない。
さらに多くの画像をトラッキングしたい場合は ARImageTrackingConfiguration の方を使用する。

 

class var isObjectDetectionSupported: Bool { get }

3D オブジェクトの検出をサポートしているか示すフラグを取得する。
オブジェクト検出を有効にするには、detectObjects プロパティに ARReferenceObject インスタンスを指定する。

 

var detectionObjects: Set { get set }

現実環境で物体認識から検出する 3D オブジェクトのセット。
このプロパティを使用して、設定している 3D オブジェクトを選択、 ユーザーの現実環境で指定したオブジェクト検索し、ARObjectAnchor として AR で使用できるようにする。

画像認識と同じように Xcode のアセットカタログで参照オブジェクトの .arobject ファイルを設定してこの命令で設定する。

 

var environmentTexturing: ARWorldTrackingConfiguration.EnvironmentTexturing { get set }

環境マップテクスチャを生成するために使用する。

シーン内の特定のポイントからのすべての方向のビューを示すキューブマップテクスチャを生成し、Physically Based Rendering を使用したリアルっぽいマテリアルの反射や照明を実現する。

ARWorldTrackingConfiguration.EnvironmentTexturing のプロパティを none から変更すると使用でき、
manual を設定すると AREnvironmentProbeAnchor を作成してセッションに追加することで任意の場所から環境マップテクスチャを作成、
automatic では ARKitはAREnvironmentProbeAnchor オブジェクトを自動的に作成し配置する。

いずれの場合も、セッションがカメラの画像を収集すると自動的に環境マップテクスチャを生成する。

session(_:didUpdate :) などのデリゲートメソッドを使用して、テクスチャがいつ利用可能かを調べ、アンカーのenvironmentTexture プロパティからアクセスをする。

ARSCNView と automaticUpdatesLighting オプションを使用してARコンテンツを表示すると、SceneKit はAREnvironmentProbeAnchor テクスチャマップを自動的に取得しシーンを照らす。

 

ARWorldTrackingConfiguration.EnvironmentTexturing

ワールドトラッキングARセッションで環境テクスチャを生成するオプション。

 

追加

オプション名 説明
none 環境マップテクスチャを生成しない
manual セッションで追加するプローブアンカーに対してのみ環境マップテクスチャを生成する
automatic 環境マップテクスチャをいつ、どこで生成するか自動的に決定する

 

AREnvironmentProbeAnchor

ワールドトラッキングで環境照明情報である環境プローブアンカーのオブジェクト。 これを元に環境マップテクスチャが生成される。

セッションでこのテクスチャマップ生成を有効にするには、上記の environmentTexturing プロパティを設定する必要がある。

  

追加

init(transform: simd_float4x4, extent: simd_float3)
init(name: String, transform: simd_float4x4, extent: simd_float3)

新しい環境プローブアンカーを作成する

name はアンカーの識別用、
transform はこのアンカーを空間に配置するためのワールド座標、
extent は視差補正が考慮されたリフレクションのテクスチャ(Parallax-Corrected Cubemap)を投影するときに使用するアンカーの位置周辺の領域。

 

var environmentTexture: MTLTexture?

プローブアンカーの位置からすべての方向のビューを表すキューブマップテクスチャ。

 

var extent: simd_float3

init で設定しているものと同じもので、
視差補正が考慮されたリフレクションのテクスチャ(Parallax-Corrected Cubemap)を投影するときに使用するアンカーの位置周辺の領域.

マテリアルが反射するオブジェクトをレンダリングするには、アンカーの位置を中心とするプロキシジオメトリに環境マップテクスチャを投影し、投影テクスチャからサンプリングする必要がある。 テクスチャがグローバルライティングとして使用されることを示す無限の範囲、またはテクスチャがシーンの特定の領域におけるローカルライティングの条件を示す有限の範囲を設定する。

 

ARWorldMap

空間マッピングをしたワールドマップのセッション状態は、ユーザがデバイスを移動する物理的な空間と、 セッションに追加された ARAnchor オブジェクトのアンカーの集まりを持つ。

ワールドマップを保存し、それらを使って新しいセッションを開始することで、複数ユーザーで空間を共有する Multiuser AR experiences と保存した空間を復元する Persistent AR experiences を使用することができる。

 

追加

var anchors: [ARAnchor]

ワールドマップに記録されたアンカーの集まり。

この配列は、ワールドマップキャプチャ時のセッション内のサブクラスを含む ARAnchor オブジェクトのスナップショット。
ワールドマップを作成すると、その環境で静的であると見なされるすべてのアンカーが自動的に格納される。 そのため、ARTrackable に適合しているものは省かれてしまうので注意。

ワールドマップを作成後、マップを保存または共有する前にこの配列からアンカーを追加・削除したりすることが可能。 ワールドマップの共有や復元時に不必要なアンカーの削除などができる。

 

var center: simd_float3

ワールドマップがセッションで記録された際のワールド座標の原点に対するマッピングデータの中心点。
extent のプロパティとともにワールドマップに記録されたデータの bounding box を定義する。

 

var extent: simd_float3

ワールドマップがセッションで記録された際のワールド座標原点に対する空間マッピングデータの大きさ(広さ)。
center のプロパティとともにワールドマップに記録されたデータの bounding box を定義する。

 

var rawFeaturePoints: ARPointCloud

ARFrame の rawFeaturePoints と同様に、ワールドマップに記録された空間マッピングデータの特徴点を表す。

セッション中にカメラの視野にあった実世界のオブジェクトの大まかな輪郭をポイントクラウドで視覚化し表示するため、 ワールドマップの記録をデバッグや、サイズを調べて空間取得の品質を調べることができる。

ソフトウェアリリース間で未処理の特徴点の数や配置などの安定は保証されないらしいので注意。 多分、OS やアプリが同じバーションで使用することが望まれるということだと思われる。

 

ARReferenceObject

現実世界で認識させる 3D のオブジェクト

物体検出により、セッションが設定されている 3D オブジェクトを認識したときに、トリガーとして任意の AR コンテンツのインタラクションを起こすことができる。 たとえば、美術館の彫刻を検出して仮想キュレーターを提供したり、ボードゲームの人物を検出したり、ゲームの視覚効果をつくり出すことができる。

 

追加

init(archiveURL url: URL) throws

指定されたファイルの URL から参照オブジェクトをロードする。

url はロードする参照オブジェクトを含むローカルファイルの URL。
throws から NSError オブジェクトへのポインタである error が返され、nil の場合エラーをチェックする。

参照オブジェクトを返し、ワールドトラッキングでの検出で使用するには、detectObjects に追加する。

 

class func referenceObjects(inGroupNamed: String, bundle: Bundle?) -> Set?

Xcodeプロジェクトのアセットカタログにあるすべての参照オブジェクトを指定した AR リソースグループへ読み込む。

name は Xcode プロジェクトのアセットカタログにある AR リソースグループ名、
bundle アセットカタログのリソースを読み込むバンドル。アプリのメインバンドルを使用する場合は nil。

init と同様にワールドトラッキングで使用するには detectObjects に追加する。

 

var name: String?

参照オブジェクト用の名前。

Xcodeアセットカタログから参照オブジェクトをロードする際に必要な名前です。 extractReferenceObject を使用してセッションで記録したオブジェクトに名前を割り当てることもできる。

 

var center: simd_float3
var extent: simd_float3

参照オブジェクトの空間マッピングデータの中心点と大きさ

extent と center プロパティは、ローカル座標で参照オブジェクトに記録されたデータのバウンディングボックスを定義する。 extractReferenceObject(transform:center:extent :)を呼び出すときに、transform パラメータを使用してその座標系を定義し、 applyTransform(_ :)を使用して別の参照オブジェクトを作成して座標を変更することもできる。

 

var scale: simd_float3

参照オブジェクトが定義するローカル座標空間での倍率。

extent にこの scale を掛ければ、オブジェクトの物理サイズがメートル単位になる。(らしい? 翻訳あっているか不安)

 

func export(to: URL, previewImage: UIImage?)

指定されたファイル URL でオブジェクトをバイナリファイルとして書き出す。

url は参照オブジェクトデータを書き込む URL、
previewImage は参照オブジェクトのファイル(.arobject)に埋め込まれるサムネイルイメージ。

ARKit 自体はファイルのサムネールを使用することはないが、Xcode、Finder、QuickLook などで視覚的にわかりやすいようにするため設定する。

通常のファイル操作と同様に、成功した場合は true。false の場合は、error パラメータがくる。

 

class let archiveExtension: String

書き出す ARReferenceObject インスタンスのファイルの拡張子。
export(to:previewImage :)メソッドで書き出す際、この拡張子を使用する。

 

func applyingTransform(simd_float4x4) -> ARReferenceObject

参照オブジェクトにここで指定した座標変換を適用すると新しい参照オブジェクトを返す。
transform は参照オブジェクトのローカル座標空間の変換行列で変換された参照オブジェクトが返る。

 

var rawFeaturePoints: ARPointCloud

ARFrame や ARWorldMap と同様なため割愛。

 

ARObjectAnchor

追加

var referenceObject: ARReferenceObject

オブジェクトアンカーによって参照され検出されたオブジェクト。
セッションの構成時に detectObjects 配列で指定した ARReferenceObject オブジェクト。

 

ARObjectScanningConfiguration

背面カメラから現実空間の物体を 3D オブジェクトとしてスキャンするための構成の設定。

こちらを使用してセッションを実行すると、オブジェクトスキャンに必要なデータの収集が可能になる。
この構成のセッションでオブジェクトをスキャンした後、createReferenceObject を呼び出し、セッションの内部空間マッピングデータの領域をスキャン。それを参照オブジェクトを使用することができる。

 

Apple 先生からのありがたき警告文

ARObjectScanningConfiguration は開発のシナリオでのみ使用される。
再現性の高い空間マッピングはパフォーマンスとエネルギーコストが高く、参照オブジェクトのスキャンに必要のない ARKit 機能を無効する必要があるため。
エンドユーザー側での AR は、ARWorldTrackingConfiguration を使用すべし。

 

追加

init()

新しいオブジェクトスキャン構成(コンフィグレーション)の初期化。
AR セッションでこの構成を使用するには、ARSession の run(_: options:) メソッドに渡す。

 

var planeDetection: ARWorldTrackingConfiguration.PlaneDetection { get set }

カメラでキャプチャされたイメージの平面をセッションが自動的に検出するかどうかとその方法を指定する。

デフォルトでは平面検出はオフ。 水平または垂直の平面検出を有効にすると、セッションでは ARPlaneAnchor オブジェクトが追加され、キャプチャされたビデオ画像の解析によって平面に見える領域が検出されると、ARSessionDelegate、ARSCNViewDelegate、または ARSKViewDelegateオブジェクトに通知される。
オブジェクトスキャンセッションでは、検出された平面を使用して、スキャンされたオブジェクトの原点(アンカーポイント)がその範囲に比例する場所を特定するのに役立つ。

 

var isAutoFocusEnabled: Bool { get set }

デバイスカメラのオートフォーカス有効にするか決める。
オートフォーカスはデフォルトで有効。

 

ARHitTestResult

変更

var worldTransform: simd_float4x4 { get }
var localTransform: simd_float4x4 { get }

共に、返り値を matrix_float4x4 から simd_float4x4 に変更

 

ARAnchor

変更

  • init(transform: simd_float4x4)
  • var transform: simd_float4x4 { get }

引数を matrix_float4x4 から simd_float4x4 に変更

 

追加

init(name: String, transform: simd_float4x4)

新規のアンカーオブジェクトで名前をつけて初期化できるようになったもの

 

var name: String? { get }

アンカー識別用の名前。

 

ARFrame

追加

var worldMappingStatus: ARFrame.WorldMappingStatus

このフレームのワールドマップを生成状態、または再生成などのステータスを提示する。

すべてのワールドトラッキングセッションは、ユーザー環境でデバイスの位置を決定するために使用する内部のワールドマップを構築する。   その内部状態のスナップショットを ARWorldMap オブジェクトとして保存し、後で保存したセッションを再開したり、デバイス間で共有してすることができる。

セッション中にいつでも getCurrentWorldMap (completionHandler:) を呼び出すことができるが、
このプロパティを使用して、セッションが現在有用な ARWorldMap を生成するのに十分なデータを持っているかどうかを判断したり、
ユーザーへ空間のマッピングの作業を促す。

 

ARFrame.WorldMappingStatus

指定したフレームで表示されている領域のマッピング状態を表す値。

名称 状態
notAvailable ワールドマップがない状態
limited 周辺の領域はまだ完全にマッピングされていない
extending 訪れた区域をマップしたが、周囲でマッピングさせていない場所がある
mapped 可視領域を適切にマッピングしている

 

追加

ARFrame.WorldMappingStatus.notAvailable

セッションの currentFrame の worldMappingStatus が notAvailable である場合、 デバイス周囲の現実世界の空間の内部マップにもカメラにも見えるシーンがない。 この時点で getCurrentWorldMap(completionHandler :)を呼び出すとエラーが発生する。

このステータスは新しいセッションの開始直後に発生する。 ワールドマップを保存または共有するにはユーザーが周囲の探索し、 アプリ側でセッションの状態を調べて mapped や extending に変更されるまで待つ。

 

ARFrame.WorldMappingStatus.limited

セッションの currentFrame の worldMappingStatus が limited である場合、 デバイス周囲の現実空間もカメラから見えるシーンもまだ完全にマッピングされていない。

現時点で、getCurrentWorldMap(completionHandler :)を呼び出してワールドマップを保存することは可能だが、 得られる ARWorldMap は、デバイスの位置付近の現実空間での再現にはあまり役に立たない。

より高品質の世ワールドマップを作成するには、ユーザーが周囲の探索し、セッションのステータスが mapped や ARFrame.WorldMappingStatus.extendingに変更するまで待つ。

 

ARFrame.WorldMappingStatus.extending

セッションの currentFrame の worldMappingStatus が extending である場合、 デバイスが最近通過した現実空間の高精度な内部マップを生成したが、 デバイスの周囲の領域をマップするためのデータを収集している。

この状態は保存されたワールドマップの再生成の質は中から高。

  • currentFrameのステータスがARFrame.WorldMappingStatus.extendingである間にgetCurrentWorldMap(completionHandler :)を呼び出してワールドマップを保存します。
  • ARWorldMapから新しいセッション(後でまたは別のデバイス)を実行すると、新しいセッションを実行するデバイスは、セッションを保存したデバイスが訪問した位置と向きを通過します。

現時点でワールドマップの保存または共有は適切な結果が得られる可能性があるが、 ユーザーがより多くの環境を探索して mapped のセッションにステータスが変わるまで待つと、 より高品質なワールドマップが再現できる可能性がある。

 

ARFrame.WorldMappingStatus.mapped

セッションの currentFrame の worldMappingStatus が mapped の場合、 デバイスの現在の位置やカメラから見えるシーンの現実空間で高精度な内部マップを生成されたことを表す。 この状態は、保存されたワールドマップで再生成する際に最高の信頼性を表す。

  • currentFrame のステータスが mapped である間に getCurrentWorldMap を呼び出してワールドマップを保存する。
  • その ARWorldMap から新しいセッションや別のデバイスで実行すると、新しいセッションを実行するデバイスは、ワールドマップが保存されたときと同じような現実の位置や向きを復元する。

 

ARCamera

追加

func unprojectPoint(_ point: CGPoint, ontoPlane planeTransform: simd_float4x4, orientation: UIInterfaceOrientation, viewportSize: CGSize) -> simd_float3?

ドキュメント上での説明なし。

ARSCNView の func unprojectPoint(_ point: CGPoint, ontoPlane planeTransform: simd_float4x4) -> simd_float3? に端末の傾きと画面の大きさをつけたものだと思われる。

 

変更

  • var eulerAngles: simd_float3 { get }

返り値を vector_float3 から simd_float3 に変更

 

  • var intrinsics: simd_float3x3 { get }

返り値を matrix_float3x3 から simd_float3x3 に変更

 

  • var transform: simd_float4x4 { get }
  • var projectionMatrix: simd_float4x4 { get }
  • func projectionMatrix(for orientation: UIInterfaceOrientation, viewportSize: CGSize, zNear: CGFloat, zFar: CGFloat) -> simd_float4x4
  • func viewMatrix(for: UIInterfaceOrientation) -> simd_float4x4

返り値を matrix_float4x4 から simd_float4x4 に変更

 

  • func projectPoint(_ point: simd_float3, orientation: UIInterfaceOrientation, viewportSize: CGSize) -> CGPoint

引数を vector_float3 から simd_float3 に変更

 

ARFaceAnchor

TrueDepth カメラを使用した顔認識

 

追加

leftEyeTransform: simd_float4x4

顔の左目の位置と向きを示す変換行列。

顔のアンカーに対しての眼球の中心の位置を示す。 +Z 軸は、眼球の中心から瞳孔の方向に向いている。 回転は眼球の向きを示し、X 軸の回転は瞳孔の上下で、Z軸は回転しない。

 

var rightEyeTransform: simd_float4x4

顔の右目の位置と向きを示す変換行列。
左目と同じなので割愛。

 

var lookAtPoint: simd_float3

視線の方向を推定する。

このベクトルは leftEyeTransform と rightEyeTransform 行列から抽象化して、 ユーザの目がどこに集中しているかを推定する。

  • ユーザーが左を見ている場合、ベクトルは正の X 軸成分を持ちます。
  • ユーザーが近くのオブジェクトに集中している場合はベクトルの長さは短くなる。
  • ユーザーが離れたオブジェクトに集中している場合はベクトルの長さは長くなる。

 

BlendShapeLocation

追加

static let tongueOut: ARFaceAnchor.BlendShapeLocation

舌の伸展を表す係数

0.0 の値は舌が完全に口の中にあることを示し、
1.0 の値は舌が口から遠くにあることを示す。

ちなみに、今回は使用できないが iOS 11 の復元ファイルのアニ文字に、上下左右など舌の動きのブレンドシェイプが用意されている。

 

ARDirectionalLightEstimate

変更

var primaryLightDirection: simd_float3 { get }

返り値を vector_float3 から simd_float3 に変更

 

ARImageTrackingConfiguration

追加

init()

イメージトラッキング設定の初期化をする。
セッションでこの設定を使用するには、ARSession の run(_:options :) メソッドに渡す。

 

var trackingImages: Set { get set }

現実世界で検出やトラッキングするイメージのセット

Xcode のアセットカタログや ARReferenceImageクラスを使用して設定した 2D 画像を現実世界で検索し、ARImageAnchor として AR で使用する。

 

var maximumNumberOfTrackedImages: Int { get set }

ARWorldTrackingConfiguration のものと同様にトラッキングできる画像の最大数。

 

var isAutoFocusEnabled: Bool { get set }

他の isAutoFocusEnabled 同様、オートフォーカスの使用するかの設定。

 

 

まとめ

ワールドマップの保存、共有や画像やオブジェクトのトラッキングができるようになり、AR として必要な機能はある程度揃った。
もうそろそろ AR グラスなどのハードを出してくれる頃合いだと思っている。

 

 

変更不明なやつ。

  • ARSession > init(rawValue:)
  • ARSession > ARSession > ARSessionObserver > ARError.Code > unsupportedConfiguration
  • ARSession > ARSession > ARSessionObserver > ARError.Code > sensorUnavailable
  • ARSession > ARSession > ARSessionObserver > ARError.Code > sensorFailed
  • ARSession > ARSession > ARSessionObserver > ARError.Code > worldTrackingFailed
  • ARSession > ARSession > ARSessionObserver > ARError.Code > cameraUnauthorized
  • ARSession > ARSession > ARSessionObserver > ARError.Code > invalidReferenceImage
  • ARSession > ARSession > ARSessionObserver > ARError > var errorCode: Int
  • ARSession > ARSession > ARSessionObserver > ARError > var errorUserInfo: [String : Any]
  • ARSession > ARSession > ARSessionObserver > ARError > var localizedDescription: String
  • ARSession > ARSession > ARSessionObserver > ARError > static var cameraUnauthorized: ARError.Code
  • ARSession > ARSession > ARSessionObserver > ARError > static var errorDomain: String
  • ARSession > ARSession > ARSessionObserver > ARError > static var invalidReferenceImage: ARError.Code
  • ARSession > ARSession > ARSessionObserver > ARError > static var sensorFailed: ARError.Code
  • ARSession > ARSession > ARSessionObserver > ARError > static var sensorUnavailable: ARError.Code
  • ARSession > ARSession > ARSessionObserver > ARError > static var unsupportedConfiguration: ARError.Code
  • ARSession > ARSession > ARSessionObserver > ARError > static var worldTrackingFailed: ARError.Code
  • ARSession > ARSession > ARSessionObserver > ARError > static func != (lhs: ARError, rhs: ARError) -> Bool
  • ARConfiguration.WorldAlignment.gravity
  • ARConfiguration.WorldAlignment.gravityAndHeading
  • ARConfiguration.WorldAlignment.camera
  • ARSCNView > ARSCNDebugOptions > init()
  • ARPlaneGeometry > var vertices: [vector_float3]
  • ARPlaneGeometry > var textureCoordinates: [vector_float2]
  • ARPlaneGeometry > var triangleCount: Int
  • ARPlaneGeometry > var triangleIndices: [Int16]
  • ARPlaneGeometry > var boundaryVertices: [vector_float3]
  • ARHitTestResult > ARHitTestResult.ResultType > init(rawValue: UInt)
  • ARPointCloud > var points: [vector_float3] { get }
  • ARPointCloud > var identifiers: [UInt64] { get }
  • ARCamera > TrackingState > notAvailable
  • ARCamera > TrackingState > limited(ARCamera.TrackingState.Reason)
  • ARCamera > TrackingState > ARCamera.TrackingState.Reason
  • ARCamera > TrackingState > normal
  • ARFaceAnchor > ARFaceGeometry > var vertices: [vector_float3]
  • ARFaceAnchor > ARFaceGeometry > var textureCoordinates: [vector_float2]
  • ARFaceAnchor > ARFaceGeometry > var triangleIndices: [Int16]
  • ARFaceAnchor > ARFaceGeometry > BlendShapeLocation > init(rawValue: String)

Blender から USDZ をつくる

Blender から USDZ をつくみようと思う。
USD のプラグイン的なものがないので、OBJ に吐き出し、Xcode 10 の Command line tools から USDZ に変換する。

 

Xcode 10 の Command line tools については前回の記事を参照。
(現状、Xcode 10 が Beta のため、今回の方法で USDZ に変換する場合は Apple Developer Program 加入が必要)
appleengine.hatenablog.com

Xcode 11 から usdz_converter が Command line tools から外れた。
Apple の Augmented Reality ページからダウンロードする必要がある。
(Terminal からダウンロードしたファイル等の PATH を設定する必要あり)
 

内容的には Blender で OBJ ファイルを書き出し、ターミナルでコマンドを打つだけ。
Blender に関してはどのバージョンでもよいのだが、今回は 2.79 を使用した。

 

Blender でジオメトリを作成する

おさるさんの SUZANNE にテクスチャを設定した OBJ ファイルを作成する。

 

Blender を起動して、立方体を削除する

起動するとスプラッシュが出るのでクリック。
選択された立方体が表示されるので、X キーを押おす。
選択したものを消すか聞かれるので delete を押して消す。

f:id:x67x6fx74x6f:20180619194639p:plain

 

そして Shift + A を押すとメニューが出るので、Mesh > Monky を選ぶ。

f:id:x67x6fx74x6f:20180619195031p:plain

そうするとおさるの顔が表示される。

 

マテリアルを設定する

テクスチャを設定する際にジオメトリの質感であるマテリアルが必要なので、 右側に球体のタブがあるのでクリックして、下の「New」を押す。

f:id:x67x6fx74x6f:20180619195101p:plain

 

おさるさんに質感であるマテリアルが設定される。

f:id:x67x6fx74x6f:20180619195119p:plain

 

テクスチャを設定する

マテリアルのタブの横にチェック模様のタブがあるのでそちらをクリック。
マテリアル同様に下の「New」を押し、
下の「open」ボタンから適当な画像を選択する。

f:id:x67x6fx74x6f:20180619195146p:plain

 

現在のおさるさんにテクスチャを表示したい場合は、画像のピンクの枠をクリックして Material に変更すると表示される。

f:id:x67x6fx74x6f:20180619195201p:plain

f:id:x67x6fx74x6f:20180619195234p:plain

 

変更するのが面倒であれば Shift + Z キーでレンダリング画像になり、戻す場合はもう一度 Shift + Z キーを押す。

 

位置を調整する

N キーを押して Transform の Position の Z 軸を 1 にすると上に上がる。
(Bender は Z 軸が上方向になっている)

f:id:x67x6fx74x6f:20180619195246p:plain

 

必要であれば UV を変更する

おさるが選択された状態で TAB キーを押すと頂点編集のモードになる。
初期状態では全選択になっているので、以下のボタンで球体でマッピングする。

Mesh > UV Unwrap > Sphere Projection

f:id:x67x6fx74x6f:20180619195306p:plain

f:id:x67x6fx74x6f:20180619195330p:plain

 

OBJ に書き出す

File > Export > Wavefront (.obj) を選択して保存。

f:id:x67x6fx74x6f:20180619195339p:plain

デフォルトで obj とテクスチャが設定された mtl ファイルが書き出される。

 

USDZ に変換する

以下の文字をターミナルで入力してコンバート USDZ が作成される。
テクスチャ画像のファイルは obj や mtl と同じ階層に必要なので注意。

xcrun usdz_converter ファイル名.obj ファイル名.usdz

 

本来は以下のように画像とともに設定して変換するが、今回は mtl で参照設定されるため必要がない。

xcrun usdz_converter RetroTV.obj RetroTV.usdz
-g RetroTVMesh
-color_map RetroTV_Albedo.png
-metallic_map RetroTV_Metallic.png
-roughness_map RetroTV_Roughness.png
-normal_map RetroTV_Normal.png
-ao_map RetroTV_AmbientOcclusion.png
-emissive_map RetroTV_Emissive.png

ARKit で使用されている USDZ とは何か

NDA の問題でスクリーンショットなどセッション資料のものを使用している。
また、Beta 版から記事を起こしているため今後変更される可能性あり。

Pixar Animation Studios が SIGGRAPH 2013 でプレゼンし発表しのちに公開されたシーン・アセット管理システム USD を改良版で、圧縮された USD ファイルとテクスチャとして使用する画像ファイルを無圧縮で zip にしたファイルとなっている。
iOS 12 ではメールやファイルアプリなどから専用の ViewController が開き閲覧ができ、Safari でも実装されているため HTML からも閲覧可能。

閲覧環境は ARKit 動作端末となるが、メモリーが多く搭載している以下の端末を推奨している。

  • iPhone 7 Plus
  • iPhone 8 Plus
  • iPhone X
  • iPad Pro 12.9 inch

 

Pixar USD とは何か

Pixar とは言え1つのアプリケーションでは CG を作成しておらず、複数のアプリケーションを使用しており、 2016年のプレゼン資料では Maya、Modo、SketchUp、Houdini、Katana、Nuke や Pixar が制作した Presto、Flow、Paint 3D など多岐にわたる。
これらの異なるアプリのファイルやを管理するためのパイプラインで使用するファイルとして USD を開発した。

f:id:x67x6fx74x6f:20180618154632p:plain

画像参照先
USD-Based Pipeline (2016) on Vimeo

 

USD は Photoshop のレイヤーような仕組みで非破壊ので編集を実現できるよう USD ファイルを他の USD で参照できるようになっている。

USD は Maya と同様にバイナリと文字列だけで構成されたアスキーのファイルフォーマットがあり、Apple のプラットフォームでは WWDC 2016 から Model I/O や SceneKit で使用できるようになっている。

また、macOS の QuickLook でも対応しているためファイルを選択してスペースキーでプレビューできる。

 

USD ファイルをつくってみる

本来は Alenbic ファイルや Maya、Houdini などから変換するプラグインを使用するのだが、自分の環境では Python 関連のツール類が上手くビルドできなかったので、アスキーの USD を作成してみる。

お好みのテキストエディタで以下を書いて保存。今回は cube.usda として保存した。

#usda 1.0

def Cube "cube"
{
}

Mac で保存した cube.usda を選択し、スペースキーを押すと立方体が描画できる。

 

他の USD ファイルを参照する

USD は他の USD ファイルを参照できるため、cube.usda のあるフォルダに、以下のように書いたファイルを作ると参照することができる。
ここでは ref.usda として保存した。

ちなみに、リファレンスとして USD ファイルを読み込んだ USD ファイルは、High Sierra の QuickLook などで見れないため Xcode 10 で閲覧するべし。
Xcode 9 で開くとクラッシュする。

#usda 1.0

over "refCube" (
    prepend references = @./cube.usda@
)
{
}

 

移動させてみる

先ほどの参照した USD ファイルを修正して立方体を移動させてみる。
ただ、移動させるだけではわかりずらいのでリファレンスのジオメトリをもう一つ作成し、translate を xform オプションとして設定し移動させている。

#usda 1.0

over "refCube" (
    prepend references = @./cube.usda@
)
{
    double3 xformOp:translate = (0, 1, 0)
    uniform token[] xformOpOrder = ["xformOp:translate"]
}

over "refCube2" (
    prepend references = @./cube.usda@
)
{
    double3 xformOp:translate = (0, 1, -4)
    uniform token[] xformOpOrder = ["xformOp:translate"]
}

 

USDZ ファイルとは

冒頭で書いているように、非圧縮された USD ファイル .usdc とテクスチャファイル albedo、metallic、roughness、normal、ambient occlusion、emission で構成された zip で、現状では1つの USD しか使えない。

また、それ以外の制限もある。

  • 100K ポリゴン
  • 2048 x 2048 までの正方形のテクスチャ
  • 10秒までのアニメーション
  • パーティクルや物理アニメーションは使用できない
  • ジオメトリやアニメーションの変更ができず、Metal シェーダーを使用することはできない

 

ちなみに非圧縮の zip と書いたが、USDZ のドキュメントを見ると各ファイルのアーカイブが先頭64Bitの倍数で始めなければならないと書かれており、ただ無圧縮の zip にしただけでは動かない。

 

USDZ で使用できる Physically Based Rendering テクスチャ

  • Diffuse (Albedo)
  • Metallic
  • Roughness
  • Normal
  • Ambient Occlusion
  • Emission

 

USDZ のつくりかた

現状、簡単に作成するものがないので Xcode 10 の Command line tools の xcrun usdz_converter で変換できる。

Xcode 11 から usdz_converter が Command line tools から外れた。
Apple の Augmented Reality ページからダウンロードする必要がある。
(Terminal からダウンロードしたファイル等の PATH を設定する必要あり)

 

変換できるファイル

  • OBJ
  • USD (usd, usda, usdc)
  • Alembic (アニメーション無し)

 

作成する前に USDZ での注意点

XYZ 座標 0, 0, 0 で配置されるため、作成するジオメトリの原点を 0 で置いた時見栄えが良いようにする。

SceneKit の空間同様に +Y 軸が上になる右手座標系のため、+Z 軸方向が正面となる。+Z 軸方向で置いた時見栄えが良いようにする。

f:id:x67x6fx74x6f:20180618155055p:plain

  

ジオメトリの接地面で自動的に影が生成され、アニメーションの場合は最初のフレームで生成される。
生成された影はアニメーションされないので、アニメーションを設定している場合は最初のフレームでのジオメトリの位置を工夫する。

f:id:x67x6fx74x6f:20180618155139p:plain

 

透過用のマスクを設定できないため、Diffuse で透過画像を作成する。

 

USDZ をアスキーの USD から作成してみる

アスキーの USD を作成する。立方体のプリミティブを配置するのだが、そのままだと原点がジオメトリの中央になるため、底面が Y 軸 0 になるように移動させる。 そしてここでは cube.usda として保存。

#usda 1.0

def Cube "cube"
{
    double3 xformOp:translate = (0, 1, 0)
    uniform token[] xformOpOrder = ["xformOp:translate"]
}

Xcode 10 をインストールするか、Xcode 10 の Command line tools をインストール。 コマンドラインで cube.usda を保存したパスまで移動し以下のものを入力する。

 

xcrun usdz_converter cube.usda cube.usdz

実行すると cube.usdz が吐き出されるので、AirDrop や Mail などでファイルアプリ保存すると閲覧することができる。

もし、"xcrun: error: unable to find utility "usdz_converter", not a developer tool or in PATH" とエラーが出る場合は、
Xcode 10 の Command line tools がインストールされていないか、Xcode で 10 のコマンドラインツールが設定されていない可能性がある。
Xcode 10 の Preferences > Locations > Command line tools で 10 以降に変更する。

 

HTML からの USDZ ファイル閲覧

HTML から USDZ ファイルを閲覧することができる。
家具など購入する商品の実物を AR で配置できるため、ユーザーが部屋の寸法を測るなどを行わなくても済み、ユーザー体験の向上をが期待される。

アンカータグの rel 属性を ar を設定して usdz をリンク。
img か picture のタグを設定すると 右上に AR 的なアイコンが自動的に表示され閲覧できるようになる。

f:id:x67x6fx74x6f:20180618160444p:plain

 

<a rel="ar" href="sample.usdz">
    <img src="thumbnail.png">
</a>
<a rel="ar" href="sample.usdz">
    <picture>
        <source srcset="thumbnail_big.png" media="(min-width: 500px)">
        <img src="thumbnail.png">
    </picture>
</a>

 

注意点

  • a タグの中に img もしくは picture タグ以外があると普通のファイルのリンクになる
  • 現状 picture タグの場合 AR 的なアイコンが表示されない
  • サーバー設定によってファイルが読み込めないため MIME type を以下のものにする

 

AddType model/vnd.pixar.usd .usdz
AddType model/usd .usdz

 

 

まとめ

割と簡単に USDZ ファイルの作成ができ、AR を体験してもらう切っ掛けにはなると思われる。

個人的には、DAE やシーンファイルからも USDZ に変更できるとより楽なのになとは思っている。

 

技術書典 3 で配布いたしました「SceneKit Book fot Swift Playgrounds」を公開しました

明日の WWDC 2018 で情報が古くなる可能性があるため、
昨年の技術書典 3 で配布いたしました「SceneKit Book fot Swift Playgrounds」を公開しました。
このページの SceneKit の情報を元に iOS 11 で追加された機能をまとめたものです。

GitHub のリポジトリからダウンロードしてください。

github.com

 

ちなみに誤字脱字、その他修正していません。
また、Swift Playgrounds(for iPad)で書いていますが 修正すると macOS や iOS でも動きます。

 

書き忘れていたか覚えがないのですが、Metal を使用している部分に関しては現行の Xcode の iOS の Swift Playgrounds では動作しません。
Xcode の iOS の Swift Playground はシミュレータで動いているため OpenGL ES での動作しているため。

WWDC 2017 の SceneKit サンプル Fox 2 を調べる まとめ

概要

appleengine.hatenablog.com

 

リソース解説

リソース:概要

appleengine.hatenablog.com

 

リソース:プレイキャラクター

appleengine.hatenablog.com

 

リソース:敵キャラクター

appleengine.hatenablog.com

 

リソース:パーティクル

appleengine.hatenablog.com

 

リソース:背景、その他

appleengine.hatenablog.com

appleengine.hatenablog.com

 

概要のおさらい

appleengine.hatenablog.com

 

GameplayKit の概要

appleengine.hatenablog.com

GameplayKit : GKComponent

appleengine.hatenablog.com

GameplayKit : GKAgent

appleengine.hatenablog.com

  

Swift ファイル

SimdExtensions.swift

appleengine.hatenablog.com

 

Character.swift

定数 / 変数 と グローバル関数

appleengine.hatenablog.com

イニシャライズ関数

appleengine.hatenablog.com

その他の関数とプロパティ その1

appleengine.hatenablog.com

update 関数

appleengine.hatenablog.com

その他の関数とプロパティ その1

appleengine.hatenablog.com

 

BaseComponent.swift

appleengine.hatenablog.com

PlayerComponent.swift

appleengine.hatenablog.com

ChaserComponent.swift

appleengine.hatenablog.com

ScaredComponent.swift

appleengine.hatenablog.com

 

GameController.swift

appleengine.hatenablog.com appleengine.hatenablog.com appleengine.hatenablog.com appleengine.hatenablog.com appleengine.hatenablog.com appleengine.hatenablog.com appleengine.hatenablog.com appleengine.hatenablog.com appleengine.hatenablog.com appleengine.hatenablog.com appleengine.hatenablog.com appleengine.hatenablog.com appleengine.hatenablog.com appleengine.hatenablog.com

 

Overlay.swift

appleengine.hatenablog.com

 

iOS のバーチャルキーボード

ButtonOverlay.swift, PadOverlay.swift, ControlOverlay.swift

appleengine.hatenablog.com

 

シェーダー

いつか書く