はじめに
今回はComputeShaderでチャンネル変換してみようという記事になります!
ComputeShader
とはGPUを計算のために使ってみようというもので、計算を爆速化できることが多いです。
最近画像処理に少しハマっていて、この100本ノックをComputeShaderで実装してみようかなと密かに計画しています。
次回 -> 【Unity】ComputeShaderでグレースケールをしてみた【Q2】 - はなちるのマイノート
今回はその第一問であるチャンネル変換というものをしてみようと思います。
チャンネル変換
これは単純にRGB
をBGR
の順番になるように入れ替えています。
ちなみにRは赤,Gは緑,Bは青のことです。
OpenCVではこのBGR
がよく使われるみたいですね。
コード
ComputeShader
はこんな感じ。
#pragma kernel RGB2BGR RWTexture2D<float4> Result; Texture2D<float4> Texture; [numthreads(8,8,1)] void RGB2BGR (uint3 id : SV_DispatchThreadID) { Result[id.xy] = float4(Texture[id.xy].z, Texture[id.xy].y, Texture[id.xy].x, Texture[id.xy].w); }
C#でのCPU側のコードはこちら。
using UnityEngine; using UnityEngine.UI; public class RGB2BGR : MonoBehaviour { [SerializeField] private ComputeShader _computeShader; [SerializeField] private Texture2D _tex; [SerializeField] private RawImage _renderer; private RenderTexture _result; struct ThreadSize { public uint x; public uint y; public uint z; public ThreadSize(uint x, uint y, uint z) { this.x = x; this.y = y; this.z = z; } } private void Start() { if (!SystemInfo.supportsComputeShaders) { Debug.LogError("Comppute Shader is not support."); return; } // RenderTextueの初期化 _result = new RenderTexture(_tex.width, _tex.height, 0, RenderTextureFormat.ARGB32); _result.enableRandomWrite = true; _result.Create(); // RGB2BGRのカーネルインデックス(0)を取得 var kernelIndex = _computeShader.FindKernel("RGB2BGR"); // 一つのグループの中に何個のスレッドがあるか ThreadSize threadSize = new ThreadSize(); _computeShader.GetKernelThreadGroupSizes(kernelIndex, out threadSize.x, out threadSize.y, out threadSize.z); // GPUにデータをコピーする _computeShader.SetTexture(kernelIndex, "Texture", _tex); _computeShader.SetTexture(kernelIndex, "Result", _result); // GPUの処理を実行する _computeShader.Dispatch(kernelIndex, _tex.width / (int)threadSize.x, _tex.height / (int)threadSize.y, (int)threadSize.z); // テクスチャを適応する _renderer.texture = _result; } private void OnDestroy() { _result = null; } }
解説
初回というわけで少しComputeShaderについて説明を加えます。
GPUでどんなように処理されているかを簡単にするために、入力画像を512*512
と仮定しましょう。
つまりピクセル(RGBA:float[4])が512*512
個あるということです。
GPUは並列処理が得意なので、今回も並列処理を活用して高速化をはかっています。
この並列処理にはスレッド,スレッドグループの二つがとても重要になっています。
スレッドを決まった単位でまとめたものをスレッドグループと呼び,以下の画像のような関係にあります。
numthreads - Win32 apps | Microsoft Docs
今回の場合は、一つのピクセルの色の計算に一つのスレッドを用いていて、合計512*512
個のスレッドを利用して並列で計算させています。
それを一つのスレッドグループに8*8*1
個のスレッド,64(=512/8)*64*1
個のスレッドグループを用意して実現させているとうわけですね。
また実際の書き方は是非参考に貼ったリンクをみていただけると分かりやすいと思います。
さいごに
おそらく100問目まではやらないと思いますが、飽きるまではこれ関係の投稿が続くかもしれません。
もし興味がある方は是非お付き合いください。
ではまた。