LoginSignup
4
5

More than 3 years have passed since last update.

Unityで行うGPU instancingについて

Last updated at Posted at 2020-08-14

はじめに

UnityではInstancingの方法が何パターンかあります.Instancing自体はそこまでハードルが高いものではありませんが,毎回1から書くのはめんどくさいと思うので,簡単なインスタンシングを実装したプロジェクトを作りました.よかったらGitHubからcloneしてみてください.(Instancing_Surfの中身を見てみてください)また今回はComputeShader, ComputeBufferを平行して使用することを前提としております.

コード解説(C#)

Graphics.DrawMeshInstancedIndirect(Mesh instanceMesh, int subMeshIndex, Material instanceMaterial, Vector3 bounds, ComputeBuffer argsBuffer);

このメソッドを用いることでInstancingを行うDrawCallを発行するすることが出来ます.これからひとつずつ必要な引数のデータについて見て行きましょう.

  • Mesh instanceMesh : InstansingしたいMesh
  • int subMeshIndex : 何番目のsubMeshかを指定する.
  • Material instanceMaterial : Instansingするmeshに適応させるShader
  • ComputeBuffer argsBuffer : InstancingするためのMesh情報を詰め込んだ配列情報.(↓を参考にしてみてください)
argsBuffer = new ComputeBuffer (int count, int stride, ComputeBufferType type);
args[0] = (uint)instanceMesh.GetIndexCount(subMeshIndex);
args[1] = (uint)instanceCount;
args[2] = (uint)instanceMesh.GetIndexStart(subMeshIndex);
args[3] = (uint)instanceMesh.GetBaseVertex(subMeshIndex);
argsBuffer.SetData(args);

コード解説(Shader)

Shader側ではVertexShader+FragmentShaderVertexShader+Surface Shaderの2つの型があります.

VertexShaderとFragmentShaderを用いる方法

実行画像

Unity 2019.1.1f1 Personal - [PREVIEW PACKAGES IN USE] - Instancing_Frag.unity - DrawMeshInstancedIndirect-Example - PC, Mac & Linux Standalone _DX11_ 2020_08_14 22_22_03 (2).png

Target

Shaderは使用するバージョンによってGeometryやComputeShaderが使える使えないなど,使用出来る機能に差があります.Targetを指定することで,どのバージョンのShaderを使用するかを決定することが出来ます.
Targetについてはここに詳しく書かれていますが,#pragma target4.5以上にすることでComputeShader,つまりComputeBufferを使用することが可能です.(今回はComputeShaderを使用するのでこの設定で)

VertexShader

変換について

VertexShaderではComputeBufferに詰められた情報をもとに座標変換を行います.またここでの変換は通常の変換通り,Projection座標系までの変換を行います.

InstanceIDの取得

Instanceをした際には個々のユニークなIDを取得することが出来ます.例えば,1万個MeshをInstanceした際には座標などを格納するComputeBufferも同じ数だけ用意し,このIDを頼りにアクセスしていけば個々の座標を動かしたり..みたいなことが出来ます.
以下の例ではVertexShaderの引数にuint instanceID : SV_InstanceIDを渡すことで参照可能となります.

instancing_frag.shader
#if SHADER_TARGET >= 45
    StructuredBuffer<float4> positionBuffer;
#endif

//====================================================
//省略
//====================================================
v2f vert(appdata_full v, uint instanceID : SV_InstanceID)
{
#if SHADER_TARGET >= 45
    float4 data = positionBuffer[instanceID];
#else
    float4 data = 0;
#endif
    v2f o;
    float3 localPosition = v.vertex.xyz * data.w;
    float3 worldPosition = data.xyz + localPosition;
    o.vertex = mul(UNITY_MATRIX_VP, float4(worldPosition, 1.0f));
    return o;
}

FragmentShader

instancing_frag.shader
fixed4 frag(v2f i) : SV_Target
{
    return _Color;
}
4
5
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
4
5