コンテンツにスキップ

カーネル

書き方説明
float v = prev[id].Rローカル変数
v = prev[id].R代入
v += 0.25f複合代入
n++増減
return new Color4(...)戻り値のあるメソッド
if (v > 0.5f) { ... }条件による分岐
if (...) { } else { }二手の分岐
if (id.X == 0) { return ...; }途中での復帰
for (int i = 0; i < 4; i++)回数を決めた繰り返し
while (i < 4)条件による繰り返し
{ ... }ブロック
if (...) { if (...) { } }条件の入れ子
prev[id]自分のセルの読み出し
c.R c.G c.B c.A成分の読み出し
new Color4(r, g, b, a)セルの値の生成
Color4.White定数の色
Color4.Black定数の色の返却
prev[id] + Color4.Blackセルどうしの加算
prev[id] - Color4.Blackセルどうしの減算
prev[id] * 0.5fセルの定数倍
id.X id.Y担当セルの座標
prev[id.Offset(1, -1)]相対位置のセルの読み出し
new KernelId(1, 2)座標の生成
0.5f小数の値
id.X * 2 + 1整数の計算
a && b真偽の組み合わせ
!(id.X > 0)否定
v >= 0.25f && v <= 0.75f範囲の比較
cond ? a : b条件による値の選択
-v符号の反転
(float)n整数から小数への変換
(int)(v * 4f)小数から整数への変換
id.X % 4整数の剰余
Mathf.Repeat(v, 0.5f)小数の折り返し
new Vector2(x, y)2 成分の値の生成
v + w, v.magnitude2 成分の値の演算
private const float Gain = 0.5f;型の定数
const float gain = 0.5f;ローカル定数
float sample; float matrix;コンパイル先の予約語と同じ名前
Falloff(d)補助関数の呼び出し
Mix(a, b)引数を 2 つ取る補助関数
Gpu.Pack16x2(v)2 つの値の詰め込み
Gpu.Unpack16x2(c)詰めた値の取り出し
prev[id.Offset(-1000, -1000)]バッファの外の読み出し

エラーは出ません。書けますし、コンパイルもされます。ただし普通の C# と違う値になる条件があります。

書き方何が起きるか
Mathf.Log(f, g)底が実行時に 0 や 1 になったときは ±0・±無限になる(C# はどちらも NaN)。
Mathf.Pow(f, g)底が負のときは NaN になる(C# は指数が整数なら値を返す)
書き方説明エラー理由外では
do { } while (c)後判定の繰り返しTUKI0001未実装通る
switch値による分岐TUKI0001未実装通る
break繰り返しの脱出TUKI0001未実装通る
continue次の周への継続TUKI0001未実装通る
foreach並びの走査TUKI0001設計通る
a & bビットごとの論理積TUKI0001未実装通る
a << 1ビットをずらすTUKI0001未実装通る
double d倍精度の小数TUKI0001未定通る
Vector3 v3 成分の値TUKI0001未実装通る
string s文字列TUKI0001設計通る
float[] xs配列TUKI0001設計通る
struct Pair { }自分で作った値型TUKI0001未定通る
Debug.Log(x)Unity の API を呼ぶTUKI0001設計通る
Random.value乱数TUKI0001設計通る
Time.time時刻TUKI0001設計通る
v == w2 成分の値どうしの等値TUKI0001設計通る
0f / 0fNaN になる定数TUKI0001設計通る
1f / 0f無限大になる定数TUKI0001設計通る
goto任意の位置への移動TUKI0001設計断られる
try / catch例外を捕まえるTUKI0001環境断られる
out 引数引数で値を返すTUKI0001未実装断られる
自分を呼ぶ関数再帰TUKI0001設計断られる
Peek(GpuBuffer2D b, ...)補助関数にバッファを渡すTUKI0001設計
prev[id.Offset(1, 0)] = c他のセルに書くCS0200設計

カーネルの中から呼べるものの一覧で、この表に無いものは断られます

「C# との差」の列には、次の印が付きます。

  • 補正 / 同じ値になるよう、コンパイラが式を組み替えています
  • 差あり / 組み替えても残る差があります。上の「書けるが値が違う形」に出してあります
書き方説明C# との差
Mathf.Abs(f)絶対値
Mathf.Acos(f)逆余弦
Mathf.Asin(f)逆正弦
Mathf.Atan(f)逆正接
Mathf.Atan2(f, g)y,x から角度
Mathf.Ceil(f)切り上げ
Mathf.Clamp(f, g, h)下限と上限で挟む補正
Mathf.Clamp01(f)0..1 で挟む
Mathf.Cos(f)余弦
Mathf.Deg2Rad度→ラジアンの係数
Mathf.Exp(f)e の冪
Mathf.Floor(f)切り捨て
Mathf.InverseLerp(f, g, h)値が a..b のどこにあるか(0..1)補正
Mathf.Lerp(f, g, h)線形補間(t は 0..1 に丸める)補正
Mathf.LerpUnclamped(f, g, h)線形補間(t を丸めない)
Mathf.Log(f)自然対数
Mathf.Log(f, g)底を指定した対数差あり
Mathf.Log10(f)常用対数
Mathf.Max(f, g)大きいほう
Mathf.Min(f, g)小さいほう
Mathf.PI円周率
Mathf.Pow(f, g)冪乗差あり
Mathf.Rad2Degラジアン→度の係数
Mathf.Repeat(f, g)0..length で折り返す(剰余)補正
Mathf.Round(f)四捨五入(0.5 は偶数側へ)
Mathf.Sign(f)符号(0 は正として 1)補正
Mathf.Sin(f)正弦
Mathf.SmoothStep(f, g, h)なめらかな補間(両端で速度 0)補正
Mathf.Sqrt(f)平方根
Mathf.Tan(f)正接
書き方説明C# との差
Vector2.Distance(v, w)2 点の距離
Vector2.Dot(v, w)内積
Vector2.Lerp(v, w, f)線形補間(t は 0..1 に丸める)補正
Vector2.LerpUnclamped(v, w, f)線形補間(t を丸めない)
Vector2.Max(v, w)成分ごとに大きいほう
Vector2.Min(v, w)成分ごとに小さいほう
Vector2.one(1, 1)
Vector2.right(1, 0)
Vector2.up(0, 1)
Vector2.zero(0, 0)
書き方説明C# との差
v.magnitude長さ
v.normalized長さ 1 にした向き(長さ 0 なら (0,0))補正
v.sqrMagnitude長さの 2 乗(平方根を取らない)
書き方説明C# との差
Gpu.Pack16x2(v)2 つの値を 1 セルに詰める(各 65536 段階)
Gpu.Unpack16x2(c)Pack16x2 で詰めた値を取り出す

カーネルの中で書けるのは、この言語のうちの狭い一部です。上の表の「外では」の列がその差で、 18 形は普通のメソッドなら書けるのに、カーネルの中では書けません。

エラーの文言は、どちらの理由で断られても同じです (TUKI0001「Udon に落とせない C# 構文です」)。上の 18 形は Udon では現に書けるので、 文言どおりに読むと原因を取り違えます。断られた形が普通のメソッドでは通るなら、 原因はその形ではなく、カーネルの中に書いたことです。

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

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

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

using UnityEngine;
using Tsukimi;
public class ParLocalVariable : TsukimiBehaviour
{
private GpuBuffer2D current;
private GpuBuffer2D next;
void Start()
{
current = Gpu.Buffer(64, 64);
next = Gpu.Buffer(64, 64);
}
[TsukimiKernel]
static Color4 Step(KernelId id, GpuBuffer2D prev)
{
float v = prev[id].R;
return new Color4(v, 0f, 0f, 1f);
}
void Update()
{
Gpu.Run(nameof(Step), next, current);
Gpu.Swap(ref current, ref next);
}
}
using UnityEngine;
using Tsukimi;
public class ParAssignment : TsukimiBehaviour
{
private GpuBuffer2D current;
private GpuBuffer2D next;
void Start()
{
current = Gpu.Buffer(64, 64);
next = Gpu.Buffer(64, 64);
}
[TsukimiKernel]
static Color4 Step(KernelId id, GpuBuffer2D prev)
{
float v = 0f;
v = prev[id].R;
return new Color4(v, 0f, 0f, 1f);
}
void Update()
{
Gpu.Run(nameof(Step), next, current);
Gpu.Swap(ref current, ref next);
}
}
using UnityEngine;
using Tsukimi;
public class ParCompoundAssignment : TsukimiBehaviour
{
private GpuBuffer2D current;
private GpuBuffer2D next;
void Start()
{
current = Gpu.Buffer(64, 64);
next = Gpu.Buffer(64, 64);
}
[TsukimiKernel]
static Color4 Step(KernelId id, GpuBuffer2D prev)
{
float v = prev[id].R;
v += 0.25f;
return new Color4(v, 0f, 0f, 1f);
}
void Update()
{
Gpu.Run(nameof(Step), next, current);
Gpu.Swap(ref current, ref next);
}
}
using UnityEngine;
using Tsukimi;
public class ParIncrement : TsukimiBehaviour
{
private GpuBuffer2D current;
private GpuBuffer2D next;
void Start()
{
current = Gpu.Buffer(64, 64);
next = Gpu.Buffer(64, 64);
}
[TsukimiKernel]
static Color4 Step(KernelId id, GpuBuffer2D prev)
{
int n = id.X;
n++;
return new Color4(n * 0.01f, 0f, 0f, 1f);
}
void Update()
{
Gpu.Run(nameof(Step), next, current);
Gpu.Swap(ref current, ref next);
}
}

カーネルの戻り値が、そのセルに書かれます。書き込む手段はこれだけです。

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);
}
[TsukimiKernel]
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);
}
}
using UnityEngine;
using Tsukimi;
public class ParIf : TsukimiBehaviour
{
private GpuBuffer2D current;
private GpuBuffer2D next;
void Start()
{
current = Gpu.Buffer(64, 64);
next = Gpu.Buffer(64, 64);
}
[TsukimiKernel]
static Color4 Step(KernelId id, GpuBuffer2D prev)
{
float v = prev[id].R;
if (v > 0.5f) { v = 1f; }
return new Color4(v, 0f, 0f, 1f);
}
void Update()
{
Gpu.Run(nameof(Step), next, current);
Gpu.Swap(ref current, ref next);
}
}
using UnityEngine;
using Tsukimi;
public class ParIfElse : TsukimiBehaviour
{
private GpuBuffer2D current;
private GpuBuffer2D next;
void Start()
{
current = Gpu.Buffer(64, 64);
next = Gpu.Buffer(64, 64);
}
[TsukimiKernel]
static Color4 Step(KernelId id, GpuBuffer2D prev)
{
float v = prev[id].R;
if (v > 0.5f) { v = 1f; } else { v = 0f; }
return new Color4(v, 0f, 0f, 1f);
}
void Update()
{
Gpu.Run(nameof(Step), next, current);
Gpu.Swap(ref current, ref next);
}
}
using UnityEngine;
using Tsukimi;
public class ParEarlyReturn : TsukimiBehaviour
{
private GpuBuffer2D current;
private GpuBuffer2D next;
void Start()
{
current = Gpu.Buffer(64, 64);
next = Gpu.Buffer(64, 64);
}
[TsukimiKernel]
static Color4 Step(KernelId id, GpuBuffer2D prev)
{
if (id.X == 0) { return Color4.Black; }
return new Color4(prev[id].R, 0f, 0f, 1f);
}
void Update()
{
Gpu.Run(nameof(Step), next, current);
Gpu.Swap(ref current, ref next);
}
}
using UnityEngine;
using Tsukimi;
public class ParFor : TsukimiBehaviour
{
private GpuBuffer2D current;
private GpuBuffer2D next;
void Start()
{
current = Gpu.Buffer(64, 64);
next = Gpu.Buffer(64, 64);
}
[TsukimiKernel]
static Color4 Step(KernelId id, GpuBuffer2D prev)
{
float v = 0f;
for (int i = 0; i < 4; i++) { v = v + prev[id.Offset(i, 0)].R; }
return new Color4(v * 0.25f, 0f, 0f, 1f);
}
void Update()
{
Gpu.Run(nameof(Step), next, current);
Gpu.Swap(ref current, ref next);
}
}
using UnityEngine;
using Tsukimi;
public class ParWhile : TsukimiBehaviour
{
private GpuBuffer2D current;
private GpuBuffer2D next;
void Start()
{
current = Gpu.Buffer(64, 64);
next = Gpu.Buffer(64, 64);
}
[TsukimiKernel]
static Color4 Step(KernelId id, GpuBuffer2D prev)
{
float v = 0f;
int i = 0;
while (i < 4) { v = v + prev[id.Offset(i, 0)].R; i++; }
return new Color4(v * 0.25f, 0f, 0f, 1f);
}
void Update()
{
Gpu.Run(nameof(Step), next, current);
Gpu.Swap(ref current, ref next);
}
}
using UnityEngine;
using Tsukimi;
public class ParBlock : TsukimiBehaviour
{
private GpuBuffer2D current;
private GpuBuffer2D next;
void Start()
{
current = Gpu.Buffer(64, 64);
next = Gpu.Buffer(64, 64);
}
[TsukimiKernel]
static Color4 Step(KernelId id, GpuBuffer2D prev)
{
float v = prev[id].R;
{ v = v * 2f; }
return new Color4(Mathf.Clamp01(v), 0f, 0f, 1f);
}
void Update()
{
Gpu.Run(nameof(Step), next, current);
Gpu.Swap(ref current, ref next);
}
}
using UnityEngine;
using Tsukimi;
public class ParNestedIf : TsukimiBehaviour
{
private GpuBuffer2D current;
private GpuBuffer2D next;
void Start()
{
current = Gpu.Buffer(64, 64);
next = Gpu.Buffer(64, 64);
}
[TsukimiKernel]
static Color4 Step(KernelId id, GpuBuffer2D prev)
{
float v = prev[id].R;
if (v > 0.25f) { if (v > 0.75f) { v = 1f; } }
return new Color4(v, 0f, 0f, 1f);
}
void Update()
{
Gpu.Run(nameof(Step), next, current);
Gpu.Swap(ref current, ref next);
}
}

id は、そのカーネルが担当するセルの座標です。prev[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);
}
[TsukimiKernel]
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);
}
[TsukimiKernel]
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);
}
[TsukimiKernel]
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);
}
[TsukimiKernel]
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);
}
[TsukimiKernel]
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 ParCellAdd : TsukimiBehaviour
{
private GpuBuffer2D current;
private GpuBuffer2D next;
void Start()
{
current = Gpu.Buffer(64, 64);
next = Gpu.Buffer(64, 64);
}
[TsukimiKernel]
static Color4 Step(KernelId id, GpuBuffer2D prev)
{
Color4 c = prev[id] + Color4.Black;
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 ParCellSub : TsukimiBehaviour
{
private GpuBuffer2D current;
private GpuBuffer2D next;
void Start()
{
current = Gpu.Buffer(64, 64);
next = Gpu.Buffer(64, 64);
}
[TsukimiKernel]
static Color4 Step(KernelId id, GpuBuffer2D prev)
{
Color4 c = prev[id] - Color4.Black;
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 ParCellScale : TsukimiBehaviour
{
private GpuBuffer2D current;
private GpuBuffer2D next;
void Start()
{
current = Gpu.Buffer(64, 64);
next = Gpu.Buffer(64, 64);
}
[TsukimiKernel]
static Color4 Step(KernelId id, GpuBuffer2D prev)
{
Color4 c = prev[id] * 0.5f;
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 ParIdXy : TsukimiBehaviour
{
private GpuBuffer2D current;
private GpuBuffer2D next;
void Start()
{
current = Gpu.Buffer(64, 64);
next = Gpu.Buffer(64, 64);
}
[TsukimiKernel]
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);
}
[TsukimiKernel]
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);
}
[TsukimiKernel]
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);
}
}

末尾に f を付けて、小数の定数を書きます。

using UnityEngine;
using Tsukimi;
public class ParFloatLiteral : TsukimiBehaviour
{
private GpuBuffer2D current;
private GpuBuffer2D next;
void Start()
{
current = Gpu.Buffer(64, 64);
next = Gpu.Buffer(64, 64);
}
[TsukimiKernel]
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);
}
}
using UnityEngine;
using Tsukimi;
public class ParIntArith : TsukimiBehaviour
{
private GpuBuffer2D current;
private GpuBuffer2D next;
void Start()
{
current = Gpu.Buffer(64, 64);
next = Gpu.Buffer(64, 64);
}
[TsukimiKernel]
static Color4 Step(KernelId id, GpuBuffer2D prev)
{
int n = id.X * 2 + 1;
return new Color4(n * 0.001f, 0f, 0f, 1f);
}
void Update()
{
Gpu.Run(nameof(Step), next, current);
Gpu.Swap(ref current, ref next);
}
}
using UnityEngine;
using Tsukimi;
public class ParBoolLogic : TsukimiBehaviour
{
private GpuBuffer2D current;
private GpuBuffer2D next;
void Start()
{
current = Gpu.Buffer(64, 64);
next = Gpu.Buffer(64, 64);
}
[TsukimiKernel]
static Color4 Step(KernelId id, GpuBuffer2D prev)
{
bool b = id.X > 0 && id.Y > 0;
return new Color4(b ? 1f : 0f, 0f, 0f, 1f);
}
void Update()
{
Gpu.Run(nameof(Step), next, current);
Gpu.Swap(ref current, ref next);
}
}
using UnityEngine;
using Tsukimi;
public class ParNot : TsukimiBehaviour
{
private GpuBuffer2D current;
private GpuBuffer2D next;
void Start()
{
current = Gpu.Buffer(64, 64);
next = Gpu.Buffer(64, 64);
}
[TsukimiKernel]
static Color4 Step(KernelId id, GpuBuffer2D prev)
{
bool b = !(id.X > 0);
return new Color4(b ? 1f : 0f, 0f, 0f, 1f);
}
void Update()
{
Gpu.Run(nameof(Step), next, current);
Gpu.Swap(ref current, ref next);
}
}
using UnityEngine;
using Tsukimi;
public class ParComparisonChain : TsukimiBehaviour
{
private GpuBuffer2D current;
private GpuBuffer2D next;
void Start()
{
current = Gpu.Buffer(64, 64);
next = Gpu.Buffer(64, 64);
}
[TsukimiKernel]
static Color4 Step(KernelId id, GpuBuffer2D prev)
{
bool b = prev[id].R >= 0.25f && prev[id].R <= 0.75f;
return new Color4(b ? 1f : 0f, 0f, 0f, 1f);
}
void Update()
{
Gpu.Run(nameof(Step), next, current);
Gpu.Swap(ref current, ref next);
}
}
using UnityEngine;
using Tsukimi;
public class ParTernary : TsukimiBehaviour
{
private GpuBuffer2D current;
private GpuBuffer2D next;
void Start()
{
current = Gpu.Buffer(64, 64);
next = Gpu.Buffer(64, 64);
}
[TsukimiKernel]
static Color4 Step(KernelId id, GpuBuffer2D prev)
{
return new Color4(id.X > 32 ? 1f : 0f, 0f, 0f, 1f);
}
void Update()
{
Gpu.Run(nameof(Step), next, current);
Gpu.Swap(ref current, ref next);
}
}
using UnityEngine;
using Tsukimi;
public class ParNegate : TsukimiBehaviour
{
private GpuBuffer2D current;
private GpuBuffer2D next;
void Start()
{
current = Gpu.Buffer(64, 64);
next = Gpu.Buffer(64, 64);
}
[TsukimiKernel]
static Color4 Step(KernelId id, GpuBuffer2D prev)
{
return new Color4(-(-prev[id].R), 0f, 0f, 1f);
}
void Update()
{
Gpu.Run(nameof(Step), next, current);
Gpu.Swap(ref current, ref next);
}
}
using UnityEngine;
using Tsukimi;
public class ParCastIntToFloat : TsukimiBehaviour
{
private GpuBuffer2D current;
private GpuBuffer2D next;
void Start()
{
current = Gpu.Buffer(64, 64);
next = Gpu.Buffer(64, 64);
}
[TsukimiKernel]
static Color4 Step(KernelId id, GpuBuffer2D prev)
{
int n = id.X;
return new Color4((float)n * 0.001f, 0f, 0f, 1f);
}
void Update()
{
Gpu.Run(nameof(Step), next, current);
Gpu.Swap(ref current, ref next);
}
}
using UnityEngine;
using Tsukimi;
public class ParCastFloatToInt : TsukimiBehaviour
{
private GpuBuffer2D current;
private GpuBuffer2D next;
void Start()
{
current = Gpu.Buffer(64, 64);
next = Gpu.Buffer(64, 64);
}
[TsukimiKernel]
static Color4 Step(KernelId id, GpuBuffer2D prev)
{
int n = (int)(prev[id].R * 4f);
return new Color4(n * 0.25f, 0f, 0f, 1f);
}
void Update()
{
Gpu.Run(nameof(Step), next, current);
Gpu.Swap(ref current, ref next);
}
}

% で整数の剰余を計算できます。コンパイル先のシェーダコンパイラが、遅いと警告することがあります。

using UnityEngine;
using Tsukimi;
public class ParModuloInt : TsukimiBehaviour
{
private GpuBuffer2D current;
private GpuBuffer2D next;
void Start()
{
current = Gpu.Buffer(64, 64);
next = Gpu.Buffer(64, 64);
}
[TsukimiKernel]
static Color4 Step(KernelId id, GpuBuffer2D prev)
{
int n = id.X % 4;
return new Color4(n * 0.25f, 0f, 0f, 1f);
}
void Update()
{
Gpu.Run(nameof(Step), next, current);
Gpu.Swap(ref current, ref next);
}
}

小数の折り返しには Mathf.Repeat を使います。

using UnityEngine;
using Tsukimi;
public class ParModuloFloat : TsukimiBehaviour
{
private GpuBuffer2D current;
private GpuBuffer2D next;
void Start()
{
current = Gpu.Buffer(64, 64);
next = Gpu.Buffer(64, 64);
}
[TsukimiKernel]
static Color4 Step(KernelId id, GpuBuffer2D prev)
{
return new Color4(Mathf.Repeat(prev[id].R, 0.5f), 0f, 0f, 1f);
}
void Update()
{
Gpu.Run(nameof(Step), next, current);
Gpu.Swap(ref current, ref next);
}
}
using UnityEngine;
using Tsukimi;
public class ParVector2Construct : TsukimiBehaviour
{
private GpuBuffer2D current;
private GpuBuffer2D next;
void Start()
{
current = Gpu.Buffer(64, 64);
next = Gpu.Buffer(64, 64);
}
[TsukimiKernel]
static Color4 Step(KernelId id, GpuBuffer2D prev)
{
Vector2 v = new Vector2(0.25f, 0.5f);
return new Color4(v.x + v.y, 0f, 0f, 1f);
}
void Update()
{
Gpu.Run(nameof(Step), next, current);
Gpu.Swap(ref current, ref next);
}
}

Vector2 どうしの足し引きのほか、magnitude などのメンバも使えます。

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

定数は、コンパイルのときに値へ置き換わります。

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

コンパイル先の予約語と同じ名前

Section titled “コンパイル先の予約語と同じ名前”

シェーダ側の予約語と重なる名前は、コンパイルのときに改名されます。

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

カーネルから呼んだ static メソッドは、シェーダの関数として一緒に書き出されます。その補助関数に [TsukimiKernel] は要りません。

using UnityEngine;
using Tsukimi;
public class ParCallHelper : TsukimiBehaviour
{
private GpuBuffer2D current;
private GpuBuffer2D next;
void Start()
{
current = Gpu.Buffer(64, 64);
next = Gpu.Buffer(64, 64);
}
static float Falloff(float d) { return Mathf.Clamp01(1f - d * d); }
[TsukimiKernel]
static Color4 Step(KernelId id, GpuBuffer2D prev)
{
float d = (id.X - 32) * 0.05f;
return new Color4(Falloff(d), 0f, 0f, 1f);
}
void Update()
{
Gpu.Run(nameof(Step), next, current);
Gpu.Swap(ref current, ref next);
}
}
using UnityEngine;
using Tsukimi;
public class ParHelperTwoArgs : TsukimiBehaviour
{
private GpuBuffer2D current;
private GpuBuffer2D next;
void Start()
{
current = Gpu.Buffer(64, 64);
next = Gpu.Buffer(64, 64);
}
static float Mix(float a, float b) { return a * 0.5f + b * 0.5f; }
[TsukimiKernel]
static Color4 Step(KernelId id, GpuBuffer2D prev)
{
return new Color4(Mix(prev[id].R, prev[id].G), 0f, 0f, 1f);
}
void Update()
{
Gpu.Run(nameof(Step), next, current);
Gpu.Swap(ref current, ref next);
}
}

1 セルは 4 成分・各 8 ビット(256 段階)です。詰めると、各 65536 段階まで保たれます。

using UnityEngine;
using Tsukimi;
public class ParPackTwoValues : TsukimiBehaviour
{
private GpuBuffer2D current;
private GpuBuffer2D next;
void Start()
{
current = Gpu.Buffer(64, 64);
next = Gpu.Buffer(64, 64);
}
[TsukimiKernel]
static Color4 Step(KernelId id, GpuBuffer2D prev)
{
Vector2 fine = new Vector2(prev[id].R, prev[id].G);
return Gpu.Pack16x2(fine);
}
void Update()
{
Gpu.Run(nameof(Step), next, current);
Gpu.Swap(ref current, ref next);
}
}

詰めた値は、Gpu.Unpack16x2 で元の 2 つに戻せます。

using UnityEngine;
using Tsukimi;
public class ParPackRoundtrip : TsukimiBehaviour
{
private GpuBuffer2D current;
private GpuBuffer2D next;
void Start()
{
current = Gpu.Buffer(64, 64);
next = Gpu.Buffer(64, 64);
}
[TsukimiKernel]
static Color4 Step(KernelId id, GpuBuffer2D prev)
{
Vector2 fine = new Vector2(0.5f, 0.25f);
Color4 packed = Gpu.Pack16x2(fine);
Vector2 back = Gpu.Unpack16x2(packed);
return new Color4(back.x, back.y, 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);
}
[TsukimiKernel]
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);
}
}