夜風のMixedReality

xRと出会って変わった人生と出会った技術を書き残すためのGeekなHoloRangerの居場所

Unity ComputeShaderでメッシュの頂点カラーを設定する

本日はShader枠です。

 少し前にUnityのComputeShaderを使用して頂点カラーを設定していきます。

redhologerbera.hatenablog.com

redhologerbera.hatenablog.com

redhologerbera.hatenablog.com

 というのも以前頂点カラーを実装した際にfor文で処理を行っていたため頂点数に応じて処理が重たくなるという問題がありました。

redhologerbera.hatenablog.com

 これをコンピュートシェーダーに置き換えることでパフォーマンスの向上が期待できます。

〇環境

・Unity6000.0.2f1

・Windows11PC

〇ComputeShaderで頂点カラーの取得を行う

 今回はComputeShaderを用いてメッシュの頂点カラーを取得します。

C#側のスクリプトは次のようになります。

using UnityEngine;

[RequireComponent(typeof(MeshFilter), typeof(MeshRenderer))]
public class VertexColorCompute : MonoBehaviour
{
    public ComputeShader vertexColorComputeShader;

    private Mesh mesh;
    private Vector3[] vertices;
    private int[] triangles;
    private Color[] colors;

    private ComputeBuffer vertexBuffer;
    private ComputeBuffer triangleBuffer;
    private ComputeBuffer colorBuffer;

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

        // 頂点バッファの作成
        vertexBuffer = new ComputeBuffer(vertices.Length, sizeof(float) * 3);
        vertexBuffer.SetData(vertices);

        // インデックスバッファの作成
        triangleBuffer = new ComputeBuffer(triangles.Length, sizeof(int));
        triangleBuffer.SetData(triangles);

        // カラーバッファの作成
        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();
        if (triangleBuffer != null)
            triangleBuffer.Release();
        if (colorBuffer != null)
            colorBuffer.Release();
    }
}

 ここではコンピュートシェーダーの基本的な使い方に応じて、Start関数でバッファを作成、Update関数で毎フレームコンピュートシェーダーからデータを受け取っています。

 具体的な処理はコンピュートシェーダー側に記載されているため非常に単調な実装になっています。

〇コンピュートシェーダー側の処理

コンピュートシェーダー側は以下のように実装します。

コンピュートシェーダーはHLSL分で記載します。

// VertexColorCompute.compute
#pragma kernel CSMain

// 頂点構造体
struct Vertex {
    float3 position;
};

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

// ワールド変換行列
float4x4 localToWorldMatrix;

// 各スレッドの処理
[numthreads(256, 1, 1)]
void CSMain(uint3 id : SV_DispatchThreadID)
{
    uint index = id.x;

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

    // ローカル座標をワールド座標に変換
    float4 worldPos = mul(localToWorldMatrix, float4(vertexBuffer[index], 1.0));
    float y = worldPos.y;

    // Y座標に基づいてカラーを設定
    if (y >= 0.0)
    {
        // Y >= 0 の場合は赤色
        colorBuffer[index] = float4(1.0, 0.0, 0.0, 1.0);
    }
    else
    {
        // Y < 0 の場合は青色
        colorBuffer[index] = float4(0.0, 0.0, 1.0, 1.0);
    }
}

 これはコメント通り頂点位置に応じて頂点カラーを変えています。

 これを実行すると以下のようにY座標に応じて頂点カラーが変わる処理になります。

本日は以上です。