本日はUnity Shader枠です。
昨年よりComputeShaderに力を入れ始めましたが、筆者の中でコンピュートシェーダーの挙動に関してまだ慣れない点があり、今回は検証も兼ねて実装します。
〇環境
・Windows11PC
・Unity2022.3.6f1
〇頂点ごとに違う出力を与える
今回は頂点カラーを例に頂点ごとに違う出力を行っていきます。
頂点は配列としてIDで管理されMeshクラスに格納されています。
このID順に色を分けて与えていきます。
ComputeShaderを用いた頂点カラーの設定法は過去の記事を参考にしてください。
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に基づいた色分けの実装ができました。
本日は以上です。