夜風のMixedReality

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

ComputeShaderでオブジェクトを拡大縮小させる

本日はUnity枠です。

前回はComputeShaderでメッシュの回転を行っていきました。

redhologerbera.hatenablog.com

ここでは回転行列を使用することでメッシュを回転させるアニメーションを行いました。

今回はスケールをアニメーションさせていきます。

〇環境

・Unity2022.3.26f1

・Windows11PC

〇スケール

回転は回転行列を使用しましたが、スケールはスケーリング行列を使用します。

redhologerbera.hatenablog.com

スケーリング行列をHLSL文であらわすと次のようになります。

// スケール行列
float4x4 GetScaleMatrix(float scale)
{
    return float4x4(
        float4(scale, 0,     0,     0),
        float4(0,     scale, 0,     0),
        float4(0,     0,     scale, 0),
        float4(0,     0,     0,     1)
    );
}

行列を使用したコンピュートシェーダーは以下のようになります。

// スケール行列
float4x4 GetScaleMatrix(float scale)
{
    return float4x4(
        float4(scale, 0,     0,     0),
        float4(0,     scale, 0,     0),
        float4(0,     0,     scale, 0),
        float4(0,     0,     0,     1)
    );
}

[numthreads(256, 1, 1)] // スレッドの設定
void CSMain(uint3 id : SV_DispatchThreadID)
{
    if (id.x >= inputVertices.Length) return;

    // 元の頂点を取得
    float3 vertex = inputVertices[id.x];

    // 回転行列を計算
    float4x4 rotationMatrix = GetRotationMatrixY(time);

    // スケール行列を計算
    float4x4 scaleMatrix = GetScaleMatrix(scale);

    // スケールと回転を適用
    float4 transformedVertex = mul(rotationMatrix, float4(vertex, 1.0));
    transformedVertex = mul(scaleMatrix, transformedVertex);

    // 結果を出力バッファに保存
    outputVertices[id.x] = transformedVertex.xyz;
}

スケーリング行列を使用することで非常にシンプルな処理になっています。

C#側の処理

using UnityEngine;

public class ScaleMesh : MonoBehaviour
{
    public ComputeShader computeShader; // コンピュートシェーダー
    private Mesh mesh;
    private ComputeBuffer inputBuffer;
    private ComputeBuffer outputBuffer;

    private Vector3[] vertices;
    private int kernel;

    void Start()
    {
        // メッシュの取得
        mesh = GetComponent<MeshFilter>().mesh;
        vertices = mesh.vertices;

        // コンピュートバッファの作成
        inputBuffer = new ComputeBuffer(vertices.Length, sizeof(float) * 3);
        outputBuffer = new ComputeBuffer(vertices.Length, sizeof(float) * 3);

        // 元の頂点データをバッファにセット
        inputBuffer.SetData(vertices);

        // カーネルを取得
        kernel = computeShader.FindKernel("CSMain");
        computeShader.SetBuffer(kernel, "inputVertices", inputBuffer);
        computeShader.SetBuffer(kernel, "outputVertices", outputBuffer);
    }

    void Update()
    {
        // 時間を送る
        computeShader.SetFloat("time", Time.time);

        // スケールを送る
        float scale = Mathf.PingPong(Time.time, 2.0f) + 0.5f; // 0.5~2.5の範囲でスケール変化
        computeShader.SetFloat("scale", scale);

        // ディスパッチを実行
        computeShader.Dispatch(kernel, Mathf.CeilToInt(vertices.Length / 256.0f), 1, 1);

        // 結果を取得してメッシュに反映
        Vector3[] outputVertices = new Vector3[vertices.Length];
        outputBuffer.GetData(outputVertices);
        mesh.vertices = outputVertices;
        mesh.RecalculateNormals();
    }

    void OnDestroy()
    {
        // バッファの解放
        inputBuffer.Release();
        outputBuffer.Release();
    }
}

C#側の処理はComputeShaderのfloat型変数Scaleに値を送る処理を加えたのみです。

これによってスケーリングと回転が実装できました。

本日は以上です。