はなちるのマイノート

Unityをメインとした技術ブログ。自分らしくまったりやっていきたいと思いますー!

【Unity】ComputeShaderでMax-Minフィルタを実装してみる【Q13】

Max-Minフィルタとは

グレースケールに対してフィルタ内の画素の最大値と最小値の差を出力するフィルタです。

エッジフィルタの一つみたいですね。

実装

ComputeShaderはこちら。

#pragma kernel MaxMinFilter

RWTexture2D<float4> Result;
Texture2D<float4> Texture;

[numthreads(32,16,1)]
void MaxMinFilter (uint3 id : SV_DispatchThreadID)
{
    float4 rgb2gray = float4(0.2126, 0.7152, 0.0722, 0);

    float gray[9] = {
        dot(Texture[id.xy + int2(-1, 1)],rgb2gray),
        dot(Texture[id.xy + int2(0, 1)],rgb2gray),
        dot(Texture[id.xy + int2(1, 1)],rgb2gray),
        dot(Texture[id.xy + int2(-1, 0)],rgb2gray),
        dot(Texture[id.xy],rgb2gray),
        dot(Texture[id.xy + int2(1, 0)],rgb2gray),
        dot(Texture[id.xy + int2(-1, -1)],rgb2gray),
        dot(Texture[id.xy + int2(0, -1)],rgb2gray),
        dot(Texture[id.xy + int2(1, -1)],rgb2gray)
    };

    float maxValue = max(gray[0],max(gray[1], max(gray[2], max(gray[3], max(gray[4], max(gray[5], max(gray[6], max(gray[7], gray[8]))))))));
    float minValue = min(gray[0],min(gray[1], min(gray[2], min(gray[3], min(gray[4], min(gray[5], min(gray[6], min(gray[7], gray[8]))))))));

    float target = maxValue - minValue;

    Result[id.xy] = float4(target, target, target, 1);
}


C#側のコードはこちら。

using UnityEngine;
using UnityEngine.UI;

public class MaxMinFilter : 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();

        // カーネルインデックスを取得
        var kernelIndex = _computeShader.FindKernel("MaxMinFilter");

        // 一つのグループの中に何個のスレッドがあるか
        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;
    }
}

さいごに

最近知ったのですがiPhoneの多くではでは512個の並列処理ができるみたいらしく、今回は(32, 16, 1)にしてみました。

携帯端末でそれだけあるなら、おそらくパソコンならもっとできるのかもしれませんね。

どれくらいを目安にしていけば良いのかは少し調査してみようと思います。

ではまた。