頭と尻尾はくれてやる!

パソコンおやじのiPhoneアプリ・サイト作成・運営日記


iOSでUIViewをpdfファイルにする

How to Convert UIView to PDF within iOS? - Stack Overflow
↑こちらを参考に下のようなクラスメソッドを作成した。
+(BOOL)saveAsPDFForView:(UIView *)targetView filePath:(NSString *)filePath
{
    NSMutableData *pdfData = [NSMutableData data];
    
    UIGraphicsBeginPDFContextToData(pdfData, targetView.bounds, nil);
    UIGraphicsBeginPDFPage();
    CGContextRef pdfContext = UIGraphicsGetCurrentContext();
    
    [targetView.layer renderInContext:pdfContext];
    
    UIGraphicsEndPDFContext();

    BOOL isOk = [pdfData writeToFile:filePath atomically:YES];
    
    return isOk;
 }
ただリンク先にあるようにこの手法だとベクター形式ではなくラスター形式になるとのこと。
出力したpdfファイルを拡大すると確かに最後は粗くなる。
なんとなくpdfというとベクターなのかと漠然と思ってた。
スポンサーサイト



GPUで何やってるのかを確認するXcodeの機能

ただでさえややこしいMetalで凝ったことをしようとすると、うまくいかない時にどこが悪いのかを探すのに苦労する。ところが、、、

Metalのデバッグまとめ(随時更新) - Qiita

↑このページを発見。そんなのあったんだー、と思いさっそく試してみた。

Xcodeのカメラボタン

実行中にXcodeに表示される↑このカメラボタン(※1)を押してみる。
いろいろわかるのだが、特に感動したのが処理途中のMTLTextureも画像として確認できるのだ!

MTLTextureDescriptorのusageを複数指定する

↑例えばこの記事では、

処理の流れの図
↑このような処理をした(同記事中の画像)。
当然途中のMTLTextureを知ることはできないのだけど、Xcodeのこの機能を使うと、、、

MTLTextureの内容

↑最初のLenaのテクスチャとする立方体をMTLTextureに書き込んだ内容を見ることができる(※2)。



別なプロジェクトだけど、メッシュを表示するところでも

メッシュ情報

↑こんな感じで三角形や頂点の一つ一つの情報を確認することができる。


なんかすごくね!?
これを使いこなせるといろいろと捗りそうな気がする!




※1 MetalKitやSceneKitを使う場合には表示されてたけど、そうでない場合は表示されなかった。

MTKViewに表示する内容

※2 ↑MTKViewへの出力画像。最終的にはMTLTextureの内容をコンピュートシェーダでぼかし、それをテクスチャとする立方体と背景画像とあわせて画面にレンダリングしてる。



MetalでMTLTextureを画面全体に表示する

Metalでid型のオブジェクトを画面いっぱいに表示したい、という時。
以前は画面からはみ出すような平面を用意し、フラグメントシェーダ内で画面上の座標を計算しそこからテクスチャ座標を得て描画していたのだが、、、
なるほど、こんなやり方があるんだな、と感心したのがAppleのサンプルコード(※1)にあった。
static const float kImagePlaneVertexData[16] = {
    -1.0, -1.0,  0.0, 1.0,
    1.0, -1.0,  1.0, 1.0,
    -1.0,  1.0,  0.0, 0.0,
    1.0,  1.0,  1.0, 0.0,
};
↑平面を構成する4つの頂点。x,y, u,vのデータが4つある。
これをMTLPrimitiveTypeTriangleStripで描く。
{
    [renderEncoder drawPrimitives:MTLPrimitiveTypeTriangleStrip vertexStart:0 vertexCount:4];
}
MTLPrimitiveTypeTriangleではないのでどの頂点を使うか、の情報も不要。なるほど!
vertex ImageColorInOut vertexFunction(ImageVertex in [[stage_in]])
{
    ImageColorInOut out;
    
    out.position = float4(in.position, 0.0, 1.0);
    
    out.texCoord = in.texCoord;
    
    return out;
}


fragment float4 fragmentFunction(ImageColorInOut in [[stage_in]],
                                            texture2d texture [[ texture(kTextureIndexColor) ]])

{
    constexpr sampler colorSampler(mip_filter::linear,
                                   mag_filter::linear,
                                   min_filter::linear);

    float4 color = float4(texture.sample(colorSampler, in.texCoord).rgb , 1.0);
    
    return  color;
}
↑シェーダではこの程度の記述で画面いっぱいにテクスチャが表示されることになる。すごい!

実行結果のスクショ

↑正方形のテクスチャを表示させた結果。

人様のコードを読むのって勉強になるよね。



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


MTLTextureDescriptorのusageを複数指定する

Metalで画面ではなくMTLTextureに描く
↑この続き。

処理の流れの図
↑Metalでこういう処理をやろうとした。

Render functionsというのは頂点シェーダとフラグメントシェーダで、
Compute functionsというのはいわゆるコンピュートシェーダを意味してる。

Render functions1はオブジェクトを描くが出力先は画面ではなくid <MTLTexture>型のバッファ。ここではtexture1としてる。

Compute functionsはこのtexture1を読み込んで画像処理した結果を別のtexture2に出力する。

Render functions2はこのtexture2を読み込んでオブジェクトのテクスチャとして使い、画面にレンダリングする。


ここで、texture1,2のusage (MTLTextureDescriptorのプロパティ)はどうするの?というお話。

結論から言うと、
{
    textureDescriptor1.usage = MTLTextureUsageRenderTarget | MTLTextureUsageShaderRead;
}
↑最初のtexture1はレンダリングの出力先であり、かつコンピュートシェーダで読み込まれるのでその両方を指定する。
{
    textureDescriptor2.usage = MTLTextureUsageShaderWrite | MTLTextureUsageShaderRead;
}
↑もう一つのtexture2はコンピュートシェーダの出力先なので’ShaderWrite’、Render functions2で読み込まれるので’ShaderRead’を指定する。


正しく指定しないとXcodeから実機デバイスにビルド・実行すると実行時に
Texture at colorAttachment[0] has usage (0x01) which doesn't specify MTLTextureUsageRenderTarget (0x04)
↑例えばこのようなログを吐いて落ちる。

ところが、その直後にデバイスのアプリを起動すると意図通りに動く。
Xcodeは(特にアップデート後など)意味不明の警告を出したりすることがあるので、あまり気にしないでいいかと思ってしまいそうだが、今回はXcodeは間違っていなかった。

実行結果のスクショ

↑こちらが今回の処理の結果。
Render functions1で立方体を描画(テクスチャはLena)。
それをCompute functionsでぼかし処理。
Render functions2でぼかした画像をテクスチャとして再度立方体を画面に描画してる。
Lenaの画像が前記事のよりぼけている。


Metalで画面ではなくMTLTextureに描く

MetalでMTKViewオブジェクトなどの画面ではなくMTLTextureに描くことを試してみた。
だいぶ昔にOpenGL ESでやった時はオフスクリーンレンダリング(※1)と言ってたかな。
以前Metalで影を表現するために光源位置から見たシーンの深度情報をMTLTextureに書き込む、というのはやったことあったのだが、その場合と異なったところなどをメモ。
{
    id <MTLTexture> outTexture;//インスタンス変数
}
-(void)setupOutTexture
{
    MTLTextureDescriptor *textureDescriptor = [MTLTextureDescriptor texture2DDescriptorWithPixelFormat:MTLPixelFormatBGRA8Unorm width:WIDTH height:HEIGHT mipmapped:NO];//—(1)
    textureDescriptor.usage = MTLTextureUsageRenderTarget;//—(2)
    outTexture = [device newTextureWithDescriptor:textureDescriptor];//—(3)
}
(1)で指定するピクセルフォーマットをカラー画像用に。深度情報を書き込んだ時はMTLPixelFormatDepth32Floatを使っていたが、今回はカラー画像なのでMTLPixelFormatBGRA8Unormで。
(2)こういうのを書け、と実行時にエラーが出たので追加。
(3)textureDescriptorからテクスチャ用バッファを作成。


MTLRenderPipelineDescriptorオブジェクト自体は画面に描こうがMTLTextureに描こうが特に変わらないが、MTLRenderPassDescriptorは出力先を画面じゃなくてMTLTextureと記述する必要がある。
{
    MTLRenderPassDescriptor *outTextureRenderPassDescriptor;//インスタンス変数
}
-(void)setupOutTextureRenderPassDescriptor
{
    outTextureRenderPassDescriptor = [MTLRenderPassDescriptor renderPassDescriptor];

    MTLRenderPassColorAttachmentDescriptor *attachment = outTextureRenderPassDescriptor.colorAttachments[0];
    attachment.texture = outTexture;
    attachment.loadAction = MTLLoadActionClear;
    attachment.storeAction = MTLStoreActionStore;
    attachment.clearColor = MTLClearColorMake(0.0f, 104.0/255.0, 5.0/255.0, 1.0f);//green
}
↑描画時にはここで作成したoutTextureRenderPassDescriptorからid <MTLRenderCommandEncoder>を作成してレンダリングする。

実行結果のスクショ

↑実行結果。テクスチャ画像におなじみのLenaを使いMTLTextureに立方体を描き、それをテクスチャとする立方体を描いている(ARKitのARFaceAnchorから立方体の位置姿勢を決めているので後方におっさんがいるけどここでは関係ない)。
なんのこっちゃ、という感じだが処理の流れは意図通りにできていることを確認した。



※1 ↓オフスクリーンレンダリング。な、なつかしい。このサイトにはホントお世話になった。
床井研究室 - 第26回 レンダリング画像をテクスチャに使う




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

FC2Ad