はじめに
画像処理100本ノックの続き、やっていきましょう。
前回 -> 【Unity】ComputeShaderでHSV変換(HSV->RGB,RGB->HSV)をしてみる【Q5】 - はなちるのマイノート
次回 -> 【Unity】ComputeShaderで平均プーリングをする【Q7】 - はなちるのマイノート
今回は減色処理(ポスタライズ)について取り組んでいきましょう。
減色処理とは
減色処理とは色の要素の階調数を減らす処理のことです。
例えばRGBでは赤を0~255
の256階調ですが、それを4階調なんかに減らすということですね。
今回の減らす方法は次の式です。
if(val < 63){ val = 32; }else if(val < 127){ val = 96; }else if(val < 191){ val = 160; }else if(val < 255){ val = 224; }
0~62
,63~126
,128~190
,191~255
の4つに分けて、その平均値を値とします。(平均値と少しずれていますが、100本ノックのお題の方を表記しています)
これをR,G,Bそれぞれに適応するというわけですね。
コード
一応R,G,Bそれぞれを自由に何階調に減らすかを決められるようにしてみました。
100本ノックの解答例はRGBが0~255
で表されますが、Unityでは0~1
になっているところとStepが固定であるところに注意してください。
#pragma kernel Posterize RWTexture2D<float4> Result; Texture2D<float4> Texture; int3 Step; [numthreads(8,8,1)] void Posterize (uint3 id : SV_DispatchThreadID) { float r = clamp(floor(Texture[id.xy].x * Step.x) / Step.x + (1.0 / (Step.x * 2)), 0, 1); float g = clamp(floor(Texture[id.xy].y * Step.y) / Step.y + (1.0 / (Step.y * 2)), 0, 1); float b = clamp(floor(Texture[id.xy].z * Step.z) / Step.z + (1.0 / (Step.z * 2)), 0, 1); Result[id.xy] = float4(r, g, b, 1); }
C#側のコードはこちら。
using UnityEngine; using UnityEngine.UI; public class Posterize : MonoBehaviour { [SerializeField] private ComputeShader _computeShader; [SerializeField] private Texture2D _tex; [SerializeField] private RawImage _renderer; 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の初期化 var result = new RenderTexture(_tex.width, _tex.height, 0, RenderTextureFormat.ARGB32); result.enableRandomWrite = true; result.Create(); // Posterizeのカーネルインデックス(0)を取得 var kernelIndex = _computeShader.FindKernel("Posterize"); // 一つのグループの中に何個のスレッドがあるか 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); // RGBそれぞれを何階調にするか int[] Step = new int[] { 4, 4, 4 }; _computeShader.SetInts("Step", Step); // GPUの処理を実行する _computeShader.Dispatch(kernelIndex, _tex.width / (int)threadSize.x, _tex.height / (int)threadSize.y, (int)threadSize.z); // テクスチャを適応 _renderer.texture = result; } }
さいごに
少しずつ飽きてきたような飽きてきてないような感じですね。
また今回HLSLの組み込み関数を2つ使いましたが、こちらのまとめサイトがとても参考になるので良かったらみてみてください。
ではまた。