以下の内容はhttps://redhologerbera.hatenablog.com/entry/2025/01/05/163605より取得しました。


UnityComputeShaderで頂点一つ一つに対して独自の処理を実装する

本日はUnity Shader枠です。

昨年よりComputeShaderに力を入れ始めましたが、筆者の中でコンピュートシェーダーの挙動に関してまだ慣れない点があり、今回は検証も兼ねて実装します。

〇環境

・Windows11PC

・Unity2022.3.6f1

〇頂点ごとに違う出力を与える

今回は頂点カラーを例に頂点ごとに違う出力を行っていきます。

頂点は配列としてIDで管理されMeshクラスに格納されています。

このID順に色を分けて与えていきます。

ComputeShaderを用いた頂点カラーの設定法は過去の記事を参考にしてください。

redhologerbera.hatenablog.com

redhologerbera.hatenablog.com

redhologerbera.hatenablog.com

HLSLを用いたシェーダープログラムの場合一般的にVertexShaderステージ、FragmentShaderステージが分かれており、頂点ごとに行う処理はVertexShaderステージで行われます。その後ラフタライズされ、ピクセルごとにFragmentShaderステージでの処理が行われます。

ComputeShaderの場合は、上記のようなVertexShaderステージ、FragmentShaderステージといったステージで処理されるのではなく、グラフィックスAPI上の特定のシェーダーステージに属さず、独立したステージで実行されます。

 このために[numthreads(256, 1, 1)]でスレッドを作成しており、メモリーがバッファとして確保されます。

 今回のように頂点1つ1つに対して処理を行いたい場合は実際のところは頂点シェーダー同様頂点ごとに並列処理が実行されます。そのためfor文などは記載する必要はありません。

下記は前回のコンピュートシェーダー回収した例です。

#pragma kernel CSMain

// バッファの宣言
StructuredBuffer<float3> vertexBuffer;
RWStructuredBuffer<float4> colorBuffer;

[numthreads(256, 1, 1)]
void CSMain(uint3 id : SV_DispatchThreadID)
{
    uint index = id.x;

    // バッファの範囲をチェック
    if (index >= vertexBuffer.Length)
        return;

    // 色の明るさを計算
    float brightness = (float)index / (float)(vertexBuffer.Length - 1);

    // 黒から白へのグラデーションを設定
    colorBuffer[index] = float4(brightness, brightness, brightness, 1.0);
}

この時vertexBufferには頂点のIDが格納されます。各頂点IDをバッファの長さで正規化することで、brightness を 0.0 ~ 1.0 の範囲で算出します。 index / (Length - 1)とすることで、最初のIDが 0(黒)、最後のIDが 1(白)となります。

C#側は不要なバッファの宣言等を省いたにすぎません

using UnityEngine;

[RequireComponent(typeof(MeshFilter), typeof(MeshRenderer))]
public class VertexColorCompute : MonoBehaviour
{
    public ComputeShader vertexColorComputeShader; 
    [SerializeField] private Color _paintColor;
    
    private Mesh mesh;
    private Vector3[] vertices;
    private Color[] colors;

    private ComputeBuffer vertexBuffer;
    private ComputeBuffer colorBuffer;
    void Start()
    {
        // メッシュの取得
        mesh = GetComponent<MeshFilter>().mesh;
        vertices = mesh.vertices;
        colors = new Color[vertices.Length];

        // 頂点バッファの作成
        vertexBuffer = new ComputeBuffer(vertices.Length, sizeof(float) * 3);
        vertexBuffer.SetData(vertices);
        
        // カラーバッファの作成
        colorBuffer = new ComputeBuffer(colors.Length, sizeof(float) * 4);
        colorBuffer.SetData(colors);
        
        // メッシュに初期カラーを設定
        mesh.colors = colors;
    }

    void Update()
    {
        // コンピュートシェーダーのセットアップ
        int kernelHandle = vertexColorComputeShader.FindKernel("CSMain");

        // シェーダーにバッファをセット
        vertexColorComputeShader.SetBuffer(kernelHandle, "vertexBuffer", vertexBuffer);
        vertexColorComputeShader.SetBuffer(kernelHandle, "colorBuffer", colorBuffer);
        // ワールド変換行列をセット(必要に応じて)
        vertexColorComputeShader.SetMatrix("localToWorldMatrix", transform.localToWorldMatrix);

        // シェーダーをディスパッチ
        int threadGroups = Mathf.CeilToInt(vertices.Length / 256.0f);
        vertexColorComputeShader.Dispatch(kernelHandle, threadGroups, 1, 1);
        // カラーバッファからデータを取得
        colorBuffer.GetData(colors);

        // メッシュにカラーを適用
        mesh.colors = colors;
    }

    void OnDestroy()
    {
        // バッファの解放
        if (vertexBuffer != null)
            vertexBuffer.Release();
    }
}

〇頂点カラーの可視化

デフォルトのマテリアルに設定されているシェーダーでは頂点カラーの可視化に対応していません。

今回はMixed RealityToolkit GraphicsToolsのStandardShadferからVertex Colorsの機能を使用します。

ここでは影の影響を無視したいのでUnlitの設定を使用しています。

〇挙動確認

最後に挙動を確認します。

実行すると次のような結果を得ることができました。

白黒ですが、頂点ごとにIDに基づいた色分けの実装ができました。

本日は以上です。




以上の内容はhttps://redhologerbera.hatenablog.com/entry/2025/01/05/163605より取得しました。
このページはhttp://font.textar.tv/のウェブフォントを使用してます

不具合報告/要望等はこちらへお願いします。
モバイルやる夫Viewer Ver0.14