眠くなっちゃった

気持ちで書きます

【Unity】Compute Shaderで画像処理をやってみる

この記事はMCC Advent Calender2018 5日目の記事です。今日なの忘れてて遅刻しました。

はじめに

Unityでは、Compute Shaderを用いることでGPUを用いた並列計算を行うことができます。主にGPUパーティクルを実装したいときに使う感じっぽいです。

今回は、このCompute Shaderの入門として軽い画像処理をしてみます。FFTとかを扱うとFFTの書き方の解説が面倒になるので、今回はエッジを抽出するフィルタの一つである、ラプラシアンフィルタを実装してみます。

表示用UIの準備

Compute Shaderとは関係なく、今回の実験のためにいくつか準備をします。

まず、画像処理の入力と出力を表示するためのUIを作成します。HierarchyビューのCreateからUI>Raw Imageを選択してRawImageを作成できます(下図)。これを二つ作り、それぞれSrc、Dstと名付けます。

f:id:ykun33:20181205230047p:plain

Compute Shaderを用いるスクリプトの準備

Compute Shaderはスクリプトから呼び出して用います。というわけでスクリプトを作成しましょう。Projectビューで右クリックをし、Create>C#Scriptと選択して作成、ImageProcesserと名付けます。

そして、ImageProcesser.csを以下のように書きます。

Compute Shaderからの結果を受け取るためのにRenderTextureを作成(rTex)、設定します。このRenderTextureはARGBFloatにしておくと値が0から1の浮動小数点数になるため計算が楽です。 そして、ここで作成したRenderTextureを表示用UI(dstImg)に設定します。

RenderTextureについて:レンダーテクスチャ - Unity マニュアル Unity - スクリプティング API: RenderTexture

次に、SetTextureでShaderに入力用の画像、出力用の画像をShader側での変数名を指定してセットします。そして最後に、Dispatchで実行します。この辺の引数は後でまとめて説明します。

Compute Shaderを作成する

Compute Shaderのファイルは、Projectビューで右クリックをして、Create>Shader>Compute Shaderの順で選択すると作成できます。名前はLaplacianにします。

f:id:ykun33:20181205224507p:plain

中身を編集して、次のように書きます。

一行目に#pragma kernel CSMainとありますが、これはCSMain関数がこのCompute Shaderのエントリーポイントの一つだよと指定するものです。ちなみにCSMainという名前になっていますが、ここは他の名前に変えても大丈夫です。

numthreadsでは、1つのグループで並列に実行するスレッドの数を3次元で指定できます。スレッド数の合計は、ここでは32321個となります。先ほどImageProcesser.csの方でDispatchをしたときに、w/32、h/32で指定した値は、このグループの数を指定しています。つまり、ここでは、全グループでのスレッドの合計が画像の画素数になります。

続いて、CSMainを見ていきます。カーネルに指定した関数は引数にスレッド番号を受け取ることができます。今回はDispatchThreadIDという、グループに関係なく全体での番号を受け取ります。これで各スレッドが画像の各画素の座標を受け取れます。このidはid.xyとすることでxy座標の座標を取得できます。

CSMainの処理の内容はラプラシアンフィルタです。up,down,left,right,middleはそれぞれラプラシアンフィルタ(下記)で0以外になる値を出しています。

 \displaystyle
    \begin{bmatrix}
       0 &  1 &  0  \\
      1 &  -4 &  1  \\
       0 & 1 &  0 
    \end{bmatrix}

up,down,left,right,middleを足した結果をsumに格納、dstTexの対応する画素に格納します。これでラプラシアンフィルタの実装完了です。これだけです。

おわりに

Compute Shaderでラプラシアンフィルタを実装しました。こんだけでできるので、みなさんいろいろ活用してみてください。あと遅れてしまってすみません。