はじめに
iPad Pro と Apple Pencil が発表されてから、何かお絵描きのようなプロダクティビティのアプリを開発したいとおもい、Metal に目をつけて勉強してきました。やはり最高のパフォーマンスを出すには METAL かなと勝手に思い WWDC を見たりと秋頃から勉強を続けてまいりましたが、ずっと陸サーファーならぬ陸METALの状態が続いていたので、2015年の年末頃から練習がてらに、2D METAL のボイラープレートになるようなサンプルを書いてみましたので、その解説をしてみたいと思います。
Basic Metal 2D の入手
より入手してください。ライセンスは MIT になっています。Xcodeでプロジェクトを開くと Mac用 と iOS用のターゲットが入っています。iOS版 はシミュレーターでは動かないのでご了承ください。ホスト側のコードは Swift になっています。Mac も iOS も世界地図が表示され、パンやズームができるようになっています。
Basic Metal 2D の狙い
世の中に Metal Base のサンプルコードは Open GL のそれと比べると圧倒的に少ないです。お絵描きアプリなどを想定してサンプルコードの検索の旅に出かけても、それなりに幾つか出てきても、スクロールやズームといった操作を前提にしていないものが多く。あくまで、METAL やその他の目的の為の検証の為のもので、ボイラープレート化するのに適さないものが多いと感じたからです。
また、個人的には Core Graphics のモデルが結構に気に入っていて、CGContextRef
に Pen や Brush のステートを与えて、Stroke や Fill すると言ったモデルに近い所を狙ってみました。
今回のサンプルは、2D でパン、ズームが可能な、しかも独自のシェーダーを追加しやすいモデルを目標にサンプルを書いてみました。
METAL
METAL の作法はやっぱりめんどく臭いです。こんなモデルを頭に描きながらプログラミングをしていると、本来のクライアントコードの目的を忘れていっぱいいっぱいになってしまいそうです。
そこで、最も面倒な Render Pipeline Descriptor
や その State
をうまく抽象化してクライアントコードから分離できないかと考えました。そこで、以下のようなモデルを考案しました。
- Shader
- Renderer
- Renderable
- RenderContext
Shader
Metal の GPU 側で動作する Vertex Shader または Fragment Shader のコード。Renderer と頂点情報など密接に設計されている必要がある。
Renderer
RenderPipelineDescriptor
及び RenderPipelineState
を抽象化したものです。そのサブクラスは実際にRenderPipelineDescriptor
を生成し、そのクライアントコードはその細かい作法を知らなくて良いものとし、APIを介して利用するものとします。サンプル内の ImageRenderer
は Texture を表示する実装になっています。
Renderable
Renderer
のサブクラスと対をなし、表示されるオブジェクトモデルを表現します。Renderable
オブジェクトは自分がどの Renderer
で表示されるかを知っているものとし、その為に必要なモデルの情報を管理します。
RenderContext
実際の表示までには頂点情報がシェーダーに渡されるまでの間には、様々な中間的な情報をおまじないのように必要になります。RenderContext
は、こうした二次、三次的な情報を集約し、かつ親モデル→子モデルの中間的な変換行列の保存復帰などを管理します。
独自シェーダーを追加したい場合
いろいろ改善が必要な点も認識していますが、いろいろなシェーダーを追加したい場合は、Image Shader
に習い、
Shader, Renderer
, Renderable
を設計してください。その際、shader には、必ず modelViewProjectionMatrix
を渡してシェーダー全体で行列の変換を行ってください。これをしないと、パン、スクロールしても何も起きない表示物が生まれてしまいますが、HUD
などそれを狙ったものであれば、その限りではありません。
今後の改善点
- Touch の軌跡を表示する、 Stroke Renderer などシェーダーなどの追加
- 構造像的なオブジェクト表示時の Uniform情報 の改善
- ScrollView っぽいスクロール
- ネーミング?