次世代型 HorliX

現行の HorliX を Mac AppStore からリリースしたのは、2018 年のことだから、かれこれ 5 年前のことになる。

売れ行きも評判も上々で気を良くしていたんだが、すぐに今も続く懸案事項が持ち上がる。

それは当時のアップルが打ち出した

OpenGL の廃止・Metal への移行

という方針だった。

HorliX に限らず、画像系のアプリはほとんど OpenGL に依存していたから、それなりの騒ぎになったことは覚えている。

幸い?すぐに廃止ということにはならなかったが(ちなみに、今でも OpenGL は使えている)、Metal が普及するにつれ、そのパフォーマンスの良さに「移行はやむなし」という雰囲気に変わってきたように思う。

私も、この方針が打ち出された直後、次期バージョンのことを思い浮かべた。

当初は、まずプラットフォームを Qt に移そうかと考えていた。
Qt はかなり早い段階から、Metal 対応を進めていたので、構想としては悪くなかったと思う。実際、試験的にプロトタイプも作成したりした。

ただ、どうしても本気になれなかったのは、HorliX を構成する膨大なライブラリ群もまとめて Qt で面倒をみなくてはならなくなることだった。

何か本質的でない気がした。

Metal への移行が思っていたより緩やかなことがわかってくると、アプリの Metal 対応は次第に考えなくなっていった。

ここ数年はそんな感じだった。

ところが、別件で画像処理関係で Metal を触ってみると、意外に扱いやすいことがわかってきて、HorliX 云々は別にしていくつか試験的なコードを書いてみた。

例えば、2D ビューア。

3D ビューア。

機能を豊富に盛っているわけではないが、最低限のラインはできているんじゃないかと思う。

当初は何かに使うという予定はなかったが、Metal による 2D や 3D 画像の取り扱いに慣れてくると、「これらを使って、次期 HorliX (PHORLIX と呼んでいる)できるのでは?」と思うようになった。

ここからの話は長くなりそうなので、いったん切ります。

 

Metal 関係覚書

たまにしか使わないので基本概念が抜けがち。

シェーダー

具体的には ***.metal ファイルがそれ。
頂点データと色の処理を決めているファイル。これだけだと有り難みがピンとこないが、実際にはこれが(並列処理可能な) GPU で処理される、というのがミソ。
このおかげで、頂点が増えれば増えるほど、効率的な処理ができるようになる。

なお、この時、使用される言語が MSL (Matal Shading Language)。

Metal は OpenGL とは違い、***.metal ファイルはビルド時にプリコンパイルされれる(.metallib というファイルが生成される)。OpenGL では実行時にコンパイルされるので、この点も高速化に貢献してそう。

 

Metal 公式サンプル

アップルが公開している Metal の公式サンプルで気になったプロジェクトなど。

CAMetalLayer 関係

CA というのがわからなかったが、Core Animation のことらしい。
興味深くはあるが、私はあまり使わないかな。

Ray Tracing と InterSection を組み合わせたプロジェクト

綺麗だ。。。

が、ここら辺までくると文字ベースの解説は一切なし。
動画はあるけど。

2D texture 関係

特にサンプルは挙げられていないが、取り扱うデータによって何を採用したらいいのか?みたいなことがこの記事に書いてある。

具体的なサンプルとしては、とりあえず

https://developer.apple.com/documentation/metal/onscreen_presentation/reading_pixel_data_from_a_drawable_texture?language=objc

https://developer.apple.com/documentation/metal/textures/creating_and_sampling_textures?language=objc

の二つをチェック。

前者は、ビルドするとこんなウィンドウが表示される。

ドラッグすると、ドラッグした領域をデスクトップ上にファイルの形で切り出してくれる。
MTKView 上でマウスの位置に応じてちょっと工夫した処理を行いたい、なんてときに使えるかもしれない。

後者は、これ。

 

かなり以前からある基本的なプロジェクトでした。

が、今から思うとこれはこれで使い道ありそう。

(続く)

 

Metal がよくわからんという人は公式サンプルから入るといいと思う

以前に「Metal 入門」という記事を書いたことがあるのだが、Metal 関係の情報は乏しいせいかネット上ではかなり参照されているようだ。

放置するのもなんなので、先日、Metal を仕様する際に出てくる「コマンド」に関して追記した。

そこで提示したサンプルは Objective-C にするのか Swift にするのか迷ったのだが、結局、Swift にした。

じゃあ、Objective-C ではどうやるのってのが本記事。

Objective-C で書かれたサンプルは本当に少ないが、Objective-C から Metal を使いたい人は公式にあるサンプルあたりから入るといいと思う。

お馴染みの三角形を描くサンプルもある。

ただし、同一プロジェクトに iOS と tvOS のターゲットも同包されているので、MacOS だけ取り出しておいた方がわかりやすい。

こんな感じになる。

以下、このサンプルに関して注意点などメモ。

MTKView の設定

View は MTKView にする必要があるが、Xcode の操作性がイマイチで、手動で MTKView に設定する必要がある。

ViewPort

ところで、今回のサンプルでは、ウィンドウのサイズを変えても描画される三角形の大きさは変わらない。

これは ViewPort を使うことで以下のように実現している。

内部的には、ウィンドウに何らかの変化が起こると delegate の以下のメソッドが呼び出されている。

/// Called whenever view changes orientation or is resized
- (void)mtkView:(nonnull MTKView *)view drawableSizeWillChange:(CGSize)size
{
    // Save the size of the drawable to pass to the vertex shader.
    _viewportSize.x = size.width;
    _viewportSize.y = size.height;
}

要するに実際のウインドウのサイズを ViewPort のサイズとして引き渡しているわけですね。

この情報はシェーダーで以下のように処理されている。

    out.position.xy = pixelSpacePosition / (viewportSize / 2.0);

Metal の使用する座標系を実際のウィンドウの大きさで割ることで、描画する図形の大きさを一定に保っているという理屈です。

単純に三角形を描画させたいだけであれば、頂点のデータは

    static const PHVertex triangleVertices[] =
    {
        // 2D positions,    RGBA colors
        { {  1.0,  -1.0 }, { 1, 0, 0, 1 } },
        { { -1.0,  -1.0 }, { 0, 1, 0, 1 } },
        { {    0,   1.0 }, { 0, 0, 1, 1 } },
    };

などと Metal 本来の座標系に準じて設定。
シェーダーも

    //out.position.xy = pixelSpacePosition / (viewportSize / 2.0);
    out.position.xy = pixelSpacePosition;

としてしまえば、三角形はウィンドウの大きさに従って変化します。

描画色に変化をつけてみる

私は試してないが、『シェーダーにランダムな値を送って動きをつける』あたりを参照して、改変するといいと思う。

ソースコードはこちらで。

回転させる、遠近処理をつける

詳しい解説はしないが、それなりの改変を加えると回転や遠近処理もできる。

その入り口くらいまではこちらの記事で軽く説明しています。
よろしければご一読ください。