コンテンツにスキップ

GPU カーネル

[Kernel] は以前 [TsukimiKernel] という綴りでした。古い綴りもそのまま動きます。

書き方説明注意
return new Color4(...)戻り値のあるメソッド書き込む手段はこれだけです。
static Color4 Step(...) => prev[id] * 0.5f式だけのカーネル
int bands整数の引数
bool invert真偽値の引数
Vector2 shift2 成分の引数
Vector3 tint3 成分の引数
Vector4 weights4 成分の引数
prev[id]自分のセルの読み出しprev[id] はそのセルの値を読みます。
c.R c.G c.B c.A成分の読み出し
new Color4(r, g, b, a)セルの値の生成
Color4.White定数の色
Color4.Black定数の色の返却
id.X id.Y担当セルの座標
prev[id.Offset(1, -1)]相対位置のセルの読み出しバッファの外を指した添字は、端の値に丸められます(下の「数の性質」)。
new KernelId(1, 2)座標の生成
prev[id.Offset(-1000, -1000)]バッファの外の読み出し
prev.Wrap(id.Offset(1, 0))反対側へ回した読み出し端の値に丸める添字と違って、小さな板を敷き詰めるときに使えます。幅 64 のバッファで -1 を指すと 63 のセルが返ります。
prev.Smooth(position)まわりと混ぜた読み出し拡大しても境目が出ません。4 つのセルを読むので、1 つを読むより費用がかかります。
書き方説明エラー理由カーネルの外
foreach並びの走査TUKI0001設計対応
float v = (i++) + 1f値として使う位置の増減TUKI0001設計対応
float w = (v = 1f) + 1f値として使う位置の代入TUKI0001設計対応
double d倍精度の小数TUKI0001未定対応
string s文字列TUKI0001設計対応
float[] xs配列TUKI0001設計対応
struct Pair { }自分で作った値型TUKI0001未定対応
Debug.Log(x)Unity の API を呼ぶTUKI0001設計対応
Random.value乱数TUKI0001設計対応
Time.time時刻TUKI0001設計対応
0f / 0fNaN になる定数TUKI0001設計対応
1f / 0f無限大になる定数TUKI0001設計対応
out 引数引数で値を返すTUKI0001設計対応
goto任意の位置への移動TUKI0001設計非対応
try / catch例外を捕まえるTUKI0001環境非対応
自分を呼ぶ関数再帰TUKI0001設計非対応
Peek(GpuBuffer2D b, ...)補助関数にバッファを渡すTUKI0001設計—
prev[id.Offset(1, 0)] = c他のセルに書くCS0200, TUKI0001設計—
型中身例
GpuBuffer2Dセルの並び。カーネルの引数として受け取り、添字で読みます自分のセルの読み出し
KernelId担当するセルの座標。X と Y は int です担当セルの座標
Color41 セルの値。R G B A は 0 から 1 の float です成分の読み出し
float int boolスカラー。引数・ローカル変数・補助関数の引数と戻り値に使えます整数の引数
Vector2 Vector3 Vector42 から 4 成分の値。演算子は型ごとの演算子にあります2 成分の引数
Vector4[]表。[Capacity(N)] を付けた引数としてだけ受け取れます表を渡した実行
ページ中身
カーネルの文と式制御構文・演算子・型ごとの演算の一覧
カーネルから呼べる関数組み込みの表と補助関数
書ける範囲この言語のうちの狭い一部です。上の表の「カーネルの外」の列がその差で、13 種類の構文は通常のメソッドでは書けるのに、カーネルの中では書けません
エラーの文言どちらの理由でも同じです(TUKI0001「Udon に落とせない C# 構文です」)。上の 13 形は Udon では現に書けるので、文言どおりに読むと原因を取り違えます
判別方法エラーになった構文が通常のメソッドでは対応しているなら、原因はその構文ではなく、カーネルの中に書いたことです

書き込む手段はカーネルの戻り値だけで、その値が自分のセルに書かれます。他のセルへ代入すると、 TUKI 番号ではなく CS0200(読み取り専用への代入)のエラーになります。バッファの添字が 読み出し専用なので、この言語の検査に到達する前に、C# コンパイラの規則でエラーになります。

  • 1 セルは 4 成分で、成分は既定では 8 ビット(256 段階)です。0..1 の内側で細かい値が必要なら Gpu.Pack16x2 で 2 つ詰めます(各 65536 段階・1 セルに 2 値)。0..1 の外の値が必要なら、バッファを GpuFormat.Half で作ります
  • バッファの外を指す添字は端の値に丸められるので、範囲の検査を自分で書かなくて済みます
  • 辺の長さは 16 の倍数にしておくと、対応する環境が広くなります
  • Gpu.Pack16x2 はカーネルの中でしか使えません(外から詰めて渡すことはできません)

既定のバッファでは色は 8 ビットに丸まります。1 つの色に複数の値を詰めると、その段階数だけ精度が落ちます。

カーネルの戻り値が、そのセルに書かれます。

using UnityEngine;
using Tsukimi;
public class ParReturn : TsukimiBehaviour
{
private GpuBuffer2D current;
private GpuBuffer2D next;
void Start()
{
current = Gpu.Buffer(64, 64);
next = Gpu.Buffer(64, 64);
}
[Kernel]
static Color4 Step(KernelId id, GpuBuffer2D prev)
{
return new Color4(0.5f, 0f, 0f, 1f);
}
void Update()
{
Gpu.Run(nameof(Step), next, current);
Gpu.Swap(ref current, ref next);
}
}

本体が 1 つの式なら => で書けます。

using UnityEngine;
using Tsukimi;
public class ParExpressionBody : TsukimiBehaviour
{
private GpuBuffer2D current;
private GpuBuffer2D next;
void Start()
{
current = Gpu.Buffer(64, 64);
next = Gpu.Buffer(64, 64);
}
// 本体が 1 つの式なら、=> で書けます
[Kernel]
static Color4 Step(KernelId id, GpuBuffer2D prev) => prev[id] * 0.5f;
void Update()
{
Gpu.Run(nameof(Step), next, current);
Gpu.Swap(ref current, ref next);
}
}

Gpu.Run の同じ位置に、同じ型の値を渡します。

using UnityEngine;
using Tsukimi;
public class ParArgInt : TsukimiBehaviour
{
private GpuBuffer2D current;
private GpuBuffer2D next;
public int bands;
void Start()
{
current = Gpu.Buffer(64, 64);
next = Gpu.Buffer(64, 64);
}
[Kernel]
static Color4 Step(KernelId id, GpuBuffer2D prev, int bands)
{
// 整数の引数は、Gpu.Run の同じ位置に int を渡します
float v = prev[id].R * bands;
return new Color4(v, 0f, 0f, 1f);
}
void Update()
{
Gpu.Run(nameof(Step), next, current, bands);
Gpu.Swap(ref current, ref next);
}
}
using UnityEngine;
using Tsukimi;
public class ParArgBool : TsukimiBehaviour
{
private GpuBuffer2D current;
private GpuBuffer2D next;
public bool invert;
void Start()
{
current = Gpu.Buffer(64, 64);
next = Gpu.Buffer(64, 64);
}
[Kernel]
static Color4 Step(KernelId id, GpuBuffer2D prev, bool invert)
{
float v = prev[id].R;
// 真偽値の引数は、そのまま条件に使えます
return new Color4(invert ? 1f - v : v, 0f, 0f, 1f);
}
void Update()
{
Gpu.Run(nameof(Step), next, current, invert);
Gpu.Swap(ref current, ref next);
}
}
using UnityEngine;
using Tsukimi;
public class ParArgVector2 : TsukimiBehaviour
{
private GpuBuffer2D current;
private GpuBuffer2D next;
public Vector2 shift;
void Start()
{
current = Gpu.Buffer(64, 64);
next = Gpu.Buffer(64, 64);
}
[Kernel]
static Color4 Step(KernelId id, GpuBuffer2D prev, Vector2 shift)
{
// 2 成分の引数。ここでは読むセルをずらす量に使っています
return prev[id.Offset((int)shift.x, (int)shift.y)];
}
void Update()
{
Gpu.Run(nameof(Step), next, current, shift);
Gpu.Swap(ref current, ref next);
}
}
using UnityEngine;
using Tsukimi;
public class ParArgVector3 : TsukimiBehaviour
{
private GpuBuffer2D current;
private GpuBuffer2D next;
public Vector3 tint;
void Start()
{
current = Gpu.Buffer(64, 64);
next = Gpu.Buffer(64, 64);
}
[Kernel]
static Color4 Step(KernelId id, GpuBuffer2D prev, Vector3 tint)
{
Color4 c = prev[id];
// 3 成分の引数。ここでは色の係数に使っています
return new Color4(c.R * tint.x, c.G * tint.y, c.B * tint.z, 1f);
}
void Update()
{
Gpu.Run(nameof(Step), next, current, tint);
Gpu.Swap(ref current, ref next);
}
}
using UnityEngine;
using Tsukimi;
public class ParArgVector4 : TsukimiBehaviour
{
private GpuBuffer2D current;
private GpuBuffer2D next;
public Vector4 weights;
void Start()
{
current = Gpu.Buffer(64, 64);
next = Gpu.Buffer(64, 64);
}
[Kernel]
static Color4 Step(KernelId id, GpuBuffer2D prev, Vector4 weights)
{
Color4 c = prev[id];
// 4 成分の引数。ここでは成分ごとの重みに使っています
float v = c.R * weights.x + c.G * weights.y + c.B * weights.z + c.A * weights.w;
return new Color4(v, v, v, 1f);
}
void Update()
{
Gpu.Run(nameof(Step), next, current, weights);
Gpu.Swap(ref current, ref next);
}
}

id は、そのカーネルが担当するセルの座標です。

using UnityEngine;
using Tsukimi;
public class ParCellRead : TsukimiBehaviour
{
private GpuBuffer2D current;
private GpuBuffer2D next;
void Start()
{
current = Gpu.Buffer(64, 64);
next = Gpu.Buffer(64, 64);
}
[Kernel]
static Color4 Step(KernelId id, GpuBuffer2D prev)
{
Color4 c = prev[id];
return new Color4(c.R, 0f, 0f, 1f);
}
void Update()
{
Gpu.Run(nameof(Step), next, current);
Gpu.Swap(ref current, ref next);
}
}

各成分は 0 から 1 のあいだの値です。

using UnityEngine;
using Tsukimi;
public class ParCellComponents : TsukimiBehaviour
{
private GpuBuffer2D current;
private GpuBuffer2D next;
void Start()
{
current = Gpu.Buffer(64, 64);
next = Gpu.Buffer(64, 64);
}
[Kernel]
static Color4 Step(KernelId id, GpuBuffer2D prev)
{
Color4 c = prev[id];
return new Color4(c.R + c.G + c.B + c.A, 0f, 0f, 1f);
}
void Update()
{
Gpu.Run(nameof(Step), next, current);
Gpu.Swap(ref current, ref next);
}
}

4 つの成分から、そのセルへ書く値を作ります。

using UnityEngine;
using Tsukimi;
public class ParCellConstruct : TsukimiBehaviour
{
private GpuBuffer2D current;
private GpuBuffer2D next;
void Start()
{
current = Gpu.Buffer(64, 64);
next = Gpu.Buffer(64, 64);
}
[Kernel]
static Color4 Step(KernelId id, GpuBuffer2D prev)
{
Color4 c = new Color4(0.5f, 0f, 0f, 1f);
return new Color4(c.R, 0f, 0f, 1f);
}
void Update()
{
Gpu.Run(nameof(Step), next, current);
Gpu.Swap(ref current, ref next);
}
}

組み込みの定数を、そのまま値として使えます。

using UnityEngine;
using Tsukimi;
public class ParCellWhite : TsukimiBehaviour
{
private GpuBuffer2D current;
private GpuBuffer2D next;
void Start()
{
current = Gpu.Buffer(64, 64);
next = Gpu.Buffer(64, 64);
}
[Kernel]
static Color4 Step(KernelId id, GpuBuffer2D prev)
{
return new Color4(Color4.White.R, 0f, 0f, 1f);
}
void Update()
{
Gpu.Run(nameof(Step), next, current);
Gpu.Swap(ref current, ref next);
}
}

定数の色を、そのまま戻り値にできます。

using UnityEngine;
using Tsukimi;
public class ParCellBlack : TsukimiBehaviour
{
private GpuBuffer2D current;
private GpuBuffer2D next;
void Start()
{
current = Gpu.Buffer(64, 64);
next = Gpu.Buffer(64, 64);
}
[Kernel]
static Color4 Step(KernelId id, GpuBuffer2D prev)
{
return Color4.Black;
}
void Update()
{
Gpu.Run(nameof(Step), next, current);
Gpu.Swap(ref current, ref next);
}
}

担当するセルの位置を、整数で取り出せます。

using UnityEngine;
using Tsukimi;
public class ParIdXy : TsukimiBehaviour
{
private GpuBuffer2D current;
private GpuBuffer2D next;
void Start()
{
current = Gpu.Buffer(64, 64);
next = Gpu.Buffer(64, 64);
}
[Kernel]
static Color4 Step(KernelId id, GpuBuffer2D prev)
{
return new Color4((id.X + id.Y) * 0.001f, 0f, 0f, 1f);
}
void Update()
{
Gpu.Run(nameof(Step), next, current);
Gpu.Swap(ref current, ref next);
}
}

Offset で、相対位置のセルを読めます。

using UnityEngine;
using Tsukimi;
public class ParIdOffset : TsukimiBehaviour
{
private GpuBuffer2D current;
private GpuBuffer2D next;
void Start()
{
current = Gpu.Buffer(64, 64);
next = Gpu.Buffer(64, 64);
}
[Kernel]
static Color4 Step(KernelId id, GpuBuffer2D prev)
{
return new Color4(prev[id.Offset(1, -1)].R, 0f, 0f, 1f);
}
void Update()
{
Gpu.Run(nameof(Step), next, current);
Gpu.Swap(ref current, ref next);
}
}

座標を自分で組み立てて、任意のセルを指せます。

using UnityEngine;
using Tsukimi;
public class ParIdConstruct : TsukimiBehaviour
{
private GpuBuffer2D current;
private GpuBuffer2D next;
void Start()
{
current = Gpu.Buffer(64, 64);
next = Gpu.Buffer(64, 64);
}
[Kernel]
static Color4 Step(KernelId id, GpuBuffer2D prev)
{
KernelId k = new KernelId(1, 2);
return new Color4(prev[k].R, 0f, 0f, 1f);
}
void Update()
{
Gpu.Run(nameof(Step), next, current);
Gpu.Swap(ref current, ref next);
}
}

バッファの外を指した添字は端の値に丸められるので、範囲の検査を自分で書かなくて済みます。

using UnityEngine;
using Tsukimi;
public class ParEdgeRead : TsukimiBehaviour
{
private GpuBuffer2D current;
private GpuBuffer2D next;
void Start()
{
current = Gpu.Buffer(64, 64);
next = Gpu.Buffer(64, 64);
}
[Kernel]
static Color4 Step(KernelId id, GpuBuffer2D prev)
{
// バッファの外を指す添字は、端の値に丸められる
Color4 outside = prev[id.Offset(-1000, -1000)];
return new Color4(outside.R, 0f, 0f, 1f);
}
void Update()
{
Gpu.Run(nameof(Step), next, current);
Gpu.Swap(ref current, ref next);
}
}

Wrap は、バッファの外を指した添字を反対側へ回します。

using UnityEngine;
using Tsukimi;
public class ParCellWrap : TsukimiBehaviour
{
private GpuBuffer2D current;
private GpuBuffer2D next;
void Start()
{
current = Gpu.Buffer(64, 64);
next = Gpu.Buffer(64, 64);
}
[Kernel]
static Color4 Step(KernelId id, GpuBuffer2D prev)
{
// バッファの外を指す添字は、反対側へ回る
Color4 far = prev.Wrap(id.Offset(-1, 0));
return new Color4(far.R, 0f, 0f, 1f);
}
void Update()
{
Gpu.Run(nameof(Step), next, current);
Gpu.Swap(ref current, ref next);
}
}

Smooth は、位置(0 から 1)で指した値を、まわりのセルと混ぜて返します。

using UnityEngine;
using Tsukimi;
public class ParCellSmooth : TsukimiBehaviour
{
private GpuBuffer2D small;
private GpuBuffer2D big;
void Start()
{
small = Gpu.Buffer(16, 16);
big = Gpu.Buffer(256, 256);
}
[Kernel]
static Color4 Step(KernelId id, GpuBuffer2D board)
{
// 位置(0 から 1)で指して、まわりのセルと混ぜて読む
Vector2 position = new Vector2(id.X * 0.00390625f, id.Y * 0.00390625f);
return board.Smooth(position);
}
void Update()
{
Gpu.Run(nameof(Step), big, small);
}
}