目次
はじめに
Compute Shaderの勉強中なので、簡単なサンプルとしてキューブを100万個動かしてみました。主にPositionを再計算する処理をCompute Shader で実装しております。比較用としてスクリプト(CPU)でのPositionを再計算する処理とのパフォーマンスの違いも見ます。
Unityを知らない方は、ぜひ こちらの記事 をご参照ください。
Link
- Github:unity-compute-shader
- 関連記事:Unity:Geometry Shader を使って100万キューブ/立方体/Cubeを描画する!
- 関連記事:Unity:1万個のキューブ/Cube/立方体を頂点移動だけで動かす
環境
私のPC環境は以下となります。
- CPU:Intel Core i7-6700
- MEM:32GB
- GPU:Geforce GTX1060
- ディスク:SSD
ソースコード
Compute Shaderと描画用Shader の管理スクリプト
Compute Shaderと描画用Shaderを生成して、バッファの共有を行うソースコードになります。
- OnEnableでComputeBufferを生成して共有
- 描画用Shaderにバッファとして設定
- Compute Shaderにバッファとして設定
- Update で Compute Shader で位置の再計算
- ここでバッファを更新すると、共有されている描画用Shader側にも反映されます。
- OnRenderObject で 描画用Shader にて画面に描画
using System.Collections;
using System.Collections.Generic;
using System.Runtime.InteropServices;
using UnityEngine;
public class ComputeShaderCube : MonoBehaviour
{
public Shader drawShader;
public ComputeShader computeShader;
ComputeBuffer buffer;
Material material;
int CSMain;
void OnEnable()
{
// 頂点を生成
var points = new List<Vector3>();
for (int x = Gui.start; x < Gui.end; x++)
for (int z = Gui.start; z < Gui.end; z++)
points.Add(new Vector3(x, 0, z));
// バッファを生成
buffer = new ComputeBuffer(points.Count, Marshal.SizeOf(typeof(Vector3)), ComputeBufferType.Default);
buffer.SetData(points);
// 描画シェーダーにバッファを設定
material = new Material(drawShader);
material.SetBuffer("points", buffer);
// コンピュータシェーダにバッファを設定
computeShader.SetBuffer(0/*CSMain*/, "bufferXYZ", buffer);
}
void Update()
{
// コンピュータシェーダで再計算
this.computeShader.SetFloat("realtimeSinceStartup", Time.realtimeSinceStartup);
this.computeShader.Dispatch(0/*CSMain*/, 1, 1, 1);
}
void OnDisable()
{
buffer.Release();
}
/// <summary>
/// 描画シェーダーの実行
/// </summary>
void OnRenderObject()
{
material.SetPass(0);
Graphics.DrawProcedural(MeshTopology.Points, buffer.count);
}
}
Compute Shader
- bufferXYZ が 描画用Shaderと共有のメモリ
- realtimeSinceStartup が立ち上げ後の経過時間
- CSMain 関数が並列に実行されます
#pragma kernel CSMain
RWStructuredBuffer<float3> bufferXYZ;
float realtimeSinceStartup;
[numthreads(1000, 1, 1)] // x方向をスレッド数で定義
void CSMain (uint3 groupThreadID : SV_GroupThreadID)
{
int x = groupThreadID.x - 500;
for (int z = -500; z < 500; z++) { // z方向をforで定義
float y = sin(sqrt((x * x) + (z * z)) / 10 + realtimeSinceStartup) * 3;
bufferXYZ[(z + 500) + (x + 500) * 1000].y = y;
}
}
描画用 Shader
Geometry Shaderはこちらの記事を参照してください。
実行結果
CPUで位置計算、Compute Shader で位置計算した場合のFPSが左上に表示されていますが、これを見ると明らかにパフォーマンスに違いが出ているのがわかると思います。
最後に
体感が変わるから Unity の Shader は良いぞ!