3DCG

Metal と OpenGL

OpenGL はネット上で独学できるリソースが転がっているのがいいなあ。

独学で一ヶ月間 OpenGL を学んで得た基礎知識のまとめ

このシリーズは、初学者向け。軽快な口調で読みやすい。

Metal はなあ・・・(遠い目)。

メモ

某プロジェクトでキモになるのはシェーダーへの頂点データの渡し方。

具体的には、以下の for in ループで以下のように渡している。

      for mesh in model.meshes {
        let vertexBuffer = mesh.mtkMesh.vertexBuffers[0].buffer
        renderEncoder.setVertexBuffer(vertexBuffer, offset: 0,
                                      index: 0)

        for submesh in mesh.submeshes {
          let mtkSubmesh = submesh.mtkSubmesh
          renderEncoder.drawIndexedPrimitives(type: .triangle,
                                              indexCount: mtkSubmesh.indexCount,
                                              indexType: mtkSubmesh.indexType,
                                              indexBuffer: mtkSubmesh.indexBuffer.buffer,
                                              indexBufferOffset: mtkSubmesh.indexBuffer.offset)
        }
      }

mtkMesh が良い感じに効いていると思うのだが、なんらかの事情でこれが使えない場合はどうするのか???

たまにはリアル勉強会にも参加しよう

・・・などと Metal 関係については、たいそう歯切れ悪く書いていたが、現在は、いくらか改善された。

というのはたまたま参加した iOS/MacOS の勉強会で、軽くレクチャーを受けたらこの分野の理解がかなり深まったから。

それまでは、これまで同様、ある程度理解した後に、サンプル作成して理解を深めようと思っていたのだが、こと、この分野に関してはそれでは効率的でないようだ。

画像処理、特に 3DCG の領域では、プログラミング的な能力より、数理的な理論の理解の方が重要なので、理解度が足踏みしていると感じたら、分かっている人に聞くのも一つの手でしょう。

 

3DCG 変換行列

3DCG で頻出の各種行列について、メモ。

・・・するつもりだったのだが、イマドキの API は一度各種行列の形を決めておけば、行列の中身を知らなくても、どうにかこうにかコーディング自体はできそう。

例えば、Metal であれば、以下のコードの vertxIn.position で示されたモデルの各点は(一度、projectionMatrix などを書いておけば)

float4 position = uniforms.projectionMatrix * uniforms.viewMatrix
  * uniforms.modelMatrix * vertexIn.position;

の変換で float4 position に移される。

だから、これをシェーダーに書いておけばいい、という寸法だ。

この時、ややこしいとされている uniforms.projectionMatrix の要素を具体的に知らなくても必須パラメータの設定ができていれば、正しく動作する。

まず、最低限、理解すべきは

・M → V → P の流れ

・M に関しては Translation・Rotatio・Scale を意味する行列の積で表現できる

ことなのだろう。

Swift における行列の取り扱い

というわけで、これ

let x = simd_double4(x: 10, y: 20, z: 30, w: 40)
let y = simd_double4(x: 1, y: 2, z: 3, w: 4)

/*
 A matrix of two columns and four rows:
 
     10  1
     20  2
     30  3
     40  4
 */
let a = simd_double2x4([x, y]) // columns

/*
 A matrix of four columns and two rows:

    10  20  30  40
    1   2   3   4
*/
let b = simd_double4x2(rows: [x, y])

projectionMatrix

これを踏まえて、projectionMatrix p を考える。

let y = 1 / tan(fov * 0.5)
let x = y / aspect
let z = lhs ? far / (far - near) : far / (near - far)
let X = float4( x, 0, 0, 0)
let Y = float4( 0, y, 0, 0)
let Z = lhs ? float4( 0, 0, z, 1) : float4( 0, 0, z, -1)
let W = lhs ? float4( 0, 0, z * -near, 0) : float4( 0, 0, z * near, 0)

let p = simd_float4x4([X, Y, Z, W])//columns

でしょうか。

viewMatrix

これも小理屈がある。
移動・拡大・回転の行列を translateMatrix, scaleMatrix, rotateMatrix としたとき、viewMatrix は

(translateMatrix * scaleMatrix * rotateMatrix).inverse//inverse 逆行列

でいいらしい。

(続く)

 

cocoa Core Graphics 関係

MacOS の新しい画像ライブラリは Metal だが、2次元に限定されてしまうが、使いやすいのは Core Graphics だろう。

なのだが、情報が乏しいのなんのって。

iOS 系のサンプルや公式ドキュメントを参考に MacOS 向けの簡単なサンプルコードを書いてみた。

簡単な描画サンプル


- (void)drawRect:(CGRect)dirtyRect {
    
    //[super drawRect:dirtyRect];//不要のようだ
     NSGraphicsContext* nsgc = [NSGraphicsContext currentContext];
     CGContextRef context = [nsgc CGContext];
    
     CGColorSpaceRef space = CGColorSpaceCreateDeviceRGB();
    double col[4],col2[4];
     col[0] = 1.0;col[1] = 1.0;col[2] = 0.5;col[3] = 1.0;
    col2[0] = 1.0;col2[1] = 0.0;col2[2] = 0.0;col2[3] = 0.8;
     CGColorRef color = CGColorCreate(space,col);
    CGColorRef color2 = CGColorCreate(space,col2);
     //透明レイヤー開始
     //CGContextBeginTransparencyLayer(context, nil);

     //パスの描画を開始
     CGContextBeginPath(context);
     float startAngle = -M_PI/2;
     float endAngle = startAngle + (M_PI * 2.0) * 0.5;
     CGContextMoveToPoint(context, 100, 100);
     CGContextAddArc(context, 100, 100, 50.0f, startAngle, endAngle, clockwise);
     CGContextClosePath(context);
     CGContextSetFillColorWithColor(context, color);
     CGContextDrawPath(context, kCGPathFill);
    
    CGContextBeginPath(context);
    CGContextMoveToPoint(context, 0, 0);
    //CGContextSetStrokeColor(context, col2);
    CGContextSetLineWidth(context,2.0);
    CGContextAddLineToPoint(context, 100, 100);
    CGContextAddLineToPoint(context, 150, 50);
    CGContextDrawPath(context, kCGPathStroke);

     //透明レイヤー終了
     //CGContextEndTransparencyLayer(context);
    
    CGColorRelease(color);

}

Metal に比べると実に簡単。
点を打ってラインで繋ぎ・・・というふうに本当に直感的。
これでアイキャッチのような図形が描画できる。

なお、cocoa 系の Core Graphics の座標系は以下の通り。

clockwise は 1 と定義してます。

Core Graphics が Quartz???

ところで、Core Graphics は Quartz の枠組みを踏襲しているようだが、その実装は CPU レンダリング主体、少なくとも OpenGL 絡みは使っていないはずで、今後も安心して使えると思うのだが、そこらへん、曖昧に説明しているサイトがあったりして「は?」となっている。

追記:ソースコードはこちらの CoreGraphics 特集?に収載されてます。

Core Graphics + Metal

Core Graphics のお手軽さと Metal の速さを組み合わせて両方を活かして使いたいという欲求はあるようで、以下のような記事があった。

Combine the power of CoreGraphics and Metal by sharing resource memory 

サンプルが動画になっている。

見事。

 

Metal 関係リソース物色中

あまり勉強したことがない Metal 関係のリソースを物色中。

公式サンプル

おお!と思ったのは公式のサンプル紹介記事にあったこのプロジェクト(群)。

C++ と書いてあるが、「どーせ、これ Objective-C++ でしょ?」と思っていたが、れっきとした C++ だった。
例えば、Xcode で GUI アプリを作成した際、自動で生成される main.m に相当する部分は

int main( int argc, char* argv[] )
{
    NS::AutoreleasePool* pAutoreleasePool = NS::AutoreleasePool::alloc()->init();

    MyAppDelegate del;

    NS::Application* pSharedApplication = NS::Application::sharedApplication();
    pSharedApplication->setDelegate( &del );
    pSharedApplication->run();

    pAutoreleasePool->release();

    return 0;
}

となっている。
言われてみれば Objective-C++  と C++ との対応はこうですね、って感じ。

三角形ばかり書いていてもつまらん

そんな方はこちらなんかも。

ただし、これは Swift の iOS プロジェクトなので、MacOS に移植する際にはいくらか(↓)修正が必要。

cocoa アプリでの Metal Viewer の取り扱い

手動で追加する。

NSObect のサブクラスとして適当な名前のクラスを作成した後に、MTKViewDelegate を書き加える。

また、以下の二つのメソッドは MTKViewDelegate を継承するには必須のようなので、付け加える。

 

参考(追記)

MacでObjective-cを使ってMetalを初期化

のシリーズを使って勉強しよう・・・と思っていたのだが、これは Objective-C で Metal を扱う際にはイマイチのやり方のようだ。

もともとアップル公式のサンプルは Objective-C で書かれており、これを Swift に移植したものが、上記シリーズ(というかネットに落ちている情報全般)っぽい。

この記事にならって丹念にアップル公式のサンプル眺めていくしかないかな。

 

 

MoltenVK を試してみる

MacOS では将来的には OpenGL は廃止になる。

アップル公式的には、Metal を使えということになっているのだが、いやー、これがとっつきにくい。

Metal に被せる形にはなるのだが、Vulkan という仕様がある。

Vulkan の MacOS 向けの実装が MoltenVK で、GitHub にも公式リポジトリがあり、誰でも利用可能だ。

自分でビルドする必要があるが、大して難しくない。

MoltenVK のビルド

必要なのは cmake と python3 。

リポジトリからソースを取ってきた後、フォルダに移動して

./fetchDependencies --macos

を実行するのみ(iOS で使いたい人は –ios などとする)。

これで、Package/Release/MoltenVK に MoltenVK.xcframework ができている。

不幸にも

tool ‘xcodebuild’ requires Xcode, but active developer directory ‘/Library/Developer/CommandLineTools’ is a command line tools instance

のエラーが出た人は、ここらあたりを参照して直しておきましょう。

デモの実行

デモも用意されている。

実行すると

というウィンドウが表示されます。

ライブラリなどの構築

フレームワークがあるので、必ずしも必要ではないが、静的・動的ライブラリをアプリに組み込んで使いたい人は、.dylib や .a ファイルを作成する手段がいくつか提供されている。

わかりやすいのは、Xcode を使う方法でしょうか。

落としたてきたフォルダの中にMoltenPackaging.xcodeproj があるので、これを Xcode で開く。

プラットフォーム毎にターゲットが用意されているので、欲しいプラットフォームのターゲットを選び、ビルドする。

そんなに時間もかからず、ビルドは完了する。

まあ、現時点ではそんなに使う機会もないかな。

なお、公式のプログラミングガイダンスはこちら

けっこう、えぐい。