【Unity】[保存版] 自作Shader向けの便利な変数変換やノイズ関数集

---
更新履歴
- 2021年10月19日 記事投稿
- 2022年8月10日 『色の合成系』の章を追加
- 2023年8月9日 球面座標変換、円柱座標変換、正規分布を追加
---


こんにちはっ🌟八ツ橋まろんです！

最近UnityのShaderを作ることが多く、その中で座標変換やノイズ生成などをするので、汎用的なこれらの関数について書き出してみました。Shader制作での一助となれば幸いです。
(というか主に未来の自分のShader制作の一助となる予定です💦)

# 座標変換系

座標変換の関数はuvに対してよく適用します。
θだけ回転させたり、中心からの距離rに依存した操作をしたい場合などに使います。

・2次元　直角座標 ↔ 極座標
・3次元　直角座標 ↔ 球面座標
・3次元　直角座標 ↔ 円柱座標
・2次元　原点を中心に回転

```hlsl:
//****************************************
//・2次元　直角座標 ↔ 極座標
//****************************************
float2 XyToPolar(float2 xy)
{
	float r = length(xy);
	float phi = atan2(xy.y, xy.x);
	return float2(r, phi);
}

float2 PolarToXy(float2 rphi)
{
	float r = rphi.x;
	float phi = rphi.y;
	return float2(r * cos(phi), r * sin(phi));
}

//****************************************
//・3次元　直角座標 ↔ 球面座標
//****************************************
float3 XyzToSphere(float3 xyz)
{
    float x = xyz.x; float y = xyz.y; float z = xyz.z;
    float r = length(xyz);
    float phi = atan2(sqrt(r * r - z * z), z); 
    float theta = atan2(y, x);
    return float3(r, phi, theta);
}

float3 SphereToXyz(float3 rpt)
{
    float r = rpt.x; float phi = rpt.y; float theta = rpt.z;
    float x = r * sin(phi) * cos(theta);
    float y = r * sin(phi) * sin(theta);
    float z = r * cos(phi);
    return float3(x, y, z);
}
//****************************************
//・3次元　直角座標 ↔ 円柱座標
//****************************************
float3 XyzToCylinder(float3 xyz)
{
    float x = xyz.x; float y = xyz.y; float z = xyz.z;
    float r = length(xyz.xy);
    float theta = atan2(y, x);
    return float3(r, theta, z);
}

float3 CylinderToXyz(float3 rtz)
{
    float r = rtz.x; float theta = rtz.y; float z = rtz.z;
    r = 0.3;
    float x = r * cos(theta);
    float y = r * sin(theta);
    return float3(x, y, z);
}
		
//****************************************
//・2次元　原点を中心に回転
//****************************************
float2 rotation(float2 p, float theta)
{
	return float2((p.x) * cos(theta) - p.y * sin(theta), p.x * sin(theta) +  p.y * cos(theta));
}
//****************************************
```
 
# 色変換系

Shaderで扱う色はRGB表記ですが、HSVで明度や彩度・コントラストを調整したい場合はこれが便利です。グレースケール化は単純なので、RGBの状態からでも変換できます。
・RGB → HSV変換
・HSV → RGB変換
・グレースケール化

```hlsl:
//****************************************
//RGB → HSV変換
//****************************************
float3 RGBtoHSV(float3 rgb)
{
	float r = rgb.r;
	float g = rgb.g;
	float b = rgb.b;
	
	float max = r > g ? r : g;
	max = max > b ? max : b;
	float min = r < g ? r : g;
	min = min < b ? min : b;
	float h = max - min;

	float h_r = (g - b) / h;
	h_r += (h_r < 0.0) ? 6.0 : 0.0;
	float h_g = 2.0 + (b - r) / h;
	float h_b = 4.0 + (r - g) / h;

	h = (h > 0.0) ? ((max == r) ? h_r : ((max == g) ? h_g : h_b)) : h;

	h /= 6.0;
	float s = (max - min);
	s = (max != 0.0) ? s /= max : s;
	float v = max;

	float3 hsv;
	hsv.x = h;
	hsv.y = s;
	hsv.z = v;
	return hsv;
}

//****************************************
//HSV → RGB変換
//****************************************
float3 HSVtoRGB(float3 hsv)
{
	float h = hsv.x;
	float s = hsv.y;
	float v = hsv.z;

	float r = v;
	float g = v;
	float b = v;

    h *= 6.0;
    float f = frac(h);
    switch (floor(h)) {
        default:
        case 0:
            g *= 1 - s * (1 - f);
            b *= 1 - s;
            break;
        case 1:
            r *= 1 - s * f;
            b *= 1 - s;
            break;
        case 2:
            r *= 1 - s;
            b *= 1 - s * (1 - f);
            break;
        case 3:
            r *= 1 - s;
            g *= 1 - s * f;
            break;
        case 4:
            r *= 1 - s * (1 - f);
            g *= 1 - s;
            break;
        case 5:
            g *= 1 - s;
            b *= 1 - s * f;
            break;
    }

	r = (s > 0.0) ? r : v;
	g = (s > 0.0) ? g : v;
	b = (s > 0.0) ? b : v;

	float3 rgb;
	rgb.r = r;
	rgb.g = g;
	rgb.b = b;
	return rgb;
}
//****************************************
//グレースケール化
//****************************************
float GrayScale(float3 rgb)
{
	return (rgb.r + rgb.g + rgb.b)*0.333333;
}
//****************************************
```
# 色の合成系

ペイントソフトのレイヤーのように、乗算・加算・オーバーレイなどの色処理をしたい時に使えます。ここではAを下絵、Bを上から重ねる絵として、各種合成モードを数式で表します。
・乗算
・加算
・除算
・スクリーン
・オーバーレイ
・ハードライト
・ソフトライト
・覆い焼きカラー
・焼き込みリニア
・差の絶対値
・比較暗
・比較明
・色相
・彩度
・カラー
・輝度

```hlsl:
//****************************************
// 乗算
float3 mul = a * b;

// 加算
float3 add = a + b;

// 除算
float3 div = a / b;

// スクリーン
float3 scr = 1 - (1 - a) * (1 - b);

// オーバーレイ&ハードライト計算用
float3 ovly_mul = 2 * a * b;
float3 ovly_scr = 1 - 2 * (1 - a) * (1 - b);

// オーバーレイ
float3 ovly = step(a, 0.5) * ovly_mul + (1 - step(a, 0.5)) * ovly_scr;

// ハードライト (オーバーレイはstepの判定にaを使ったが、ハードライトではbを使っている)
float3 hdlgt = step(b, 0.5) * ovly_mul + (1 - step(b, 0.5)) * ovly_scr;

// ソフトライト (実装方法はいくつかあるが、そのうちの一つ)
float3 sflgt = (1 - 2 * b) * a * a + 2 * a * b;

// 覆い焼きカラー
float3 dodge = a / (1 - b);

// 焼き込み(リニア)
float3 burn = a + b - 1;

// 差の絶対値
float3 diff = abs(a - b);

// 比較(暗)
float3 cpdark = min(a, b);

// 比較(明)
float3 cpbrgt = max(a, b);

//(以下は、上に書いてあるRGB変換を使います)
// 色相
float3 hue = HSVtoRGB(float3(RGBtoHSV(b).x, RGBtoHSV(a).y, RGBtoHSV(a).z));

// 彩度
float3 sat = HSVtoRGB(float3(RGBtoHSV(a).x, RGBtoHSV(b).y, RGBtoHSV(a).z));

// カラー
float3 col = HSVtoRGB(float3(RGBtoHSV(b).x, RGBtoHSV(b).y, RGBtoHSV(a).z));

// 輝度
float3 lumi = HSVtoRGB(float3(RGBtoHSV(a).x, RGBtoHSV(a).y, RGBtoHSV(b).z));

//****************************************
```

# 正規分布/疑似ランダム/ノイズ

有名な数学の関数や、古くから知られている疑似ランダムの関数、パーリンノイズなどです。
疑似ランダムはuv座標などを入力すると0～1のランダムぽい数字を出力します。
疑似ランダムなので同じ入力に対してはいつも同じ数値が返ってきます。

・(改変)正規分布
・1次元 疑似ランダム
・2次元 疑似ランダム
・パーリンノイズ

```hlsl:
//****************************************
//・(改変)正規分布
// 0を中心としたσ = 0.3の正規分布の係数を改変したもの。
// x = 0, σ, 2σ, 3σでそれぞれGD = 1, 0.6, 0.13, 0.01になる。
// 中心から離れるほど効果を小さくしたい時に使う。
//****************************************
float GaussianDistribution(float x, float sigma = 0.3)
{
    float GD = exp(-(x * x) / (2 * sigma * sigma));
    return GD;
}
//****************************************
//・1次元 疑似ランダム(入力は2次元です)
//****************************************
float random(fixed2 st)
{
	return frac(sin(dot(st.xy, fixed2(12.9898, 78.233))) * 43758.5453);
}
//****************************************
//・2次元 疑似ランダム(入力は2次元です)
//****************************************
float2 random2(fixed2 st) {
	st = fixed2(dot(st, float2(127.1, 311.7)),
		dot(st, fixed2(269.5, 183.3)));
	return -1.0 + 2.0 * frac(sin(st) * 43758.5453123);
}
//****************************************
//・パーリンノイズ (上記のrandom2を使います)
//****************************************
float perlinNoise(fixed2 st)
{
	fixed2 p = floor(st);
	fixed2 f = frac(st);
	fixed2 u = f * f * (3.0 - 2.0 * f);

	float v00 = random2(p + fixed2(0, 0));
	float v10 = random2(p + fixed2(1, 0));
	float v01 = random2(p + fixed2(0, 1));
	float v11 = random2(p + fixed2(1, 1));

	return lerp(lerp(dot(v00, f - fixed2(0, 0)), dot(v10, f - fixed2(1, 0)), u.x),
		lerp(dot(v01, f - fixed2(0, 1)), dot(v11, f - fixed2(1, 1)), u.x),
		u.y) + 0.5f;
}
//****************************************

```

# 2次元 距離関数系

図形描画のときに距離関数というのを使います。
距離関数を増やすことは手札を増やすことに他ならないのでとても重要です。

・距離関数(円)
・距離関数(直線)
・距離関数(四角形)
・距離関数(ひし形)
・距離関数(正多角形)
・距離関数(星形)
・距離関数(楕円)
・距離関数(ハート形)
・距離関数(円環)

```hlsl:
//****************************************
//距離関数 (円)
//****************************************
float circle(float2 p, float radius)
{
	return length(p) - radius;
}
//****************************************
//距離関数 (直線)
//****************************************
float lines(float2 p, int n,  float2 size, float thickness)
{
	float PI = 3.14159265358;
	float o = 1;
	for(int i = 1; i < n+1; i++){				
		p = rotation(p, 2*PI/n);
		if(p.x<0)
			o = min(o, 1);					
		else
			o = min(o, max(abs(p.x) - size.x, abs(p.y)-thickness*0.2));
	}
	return o;
}
//****************************************
//距離関数 (四角形)
//****************************************
float rectangle(float2 p, float2 size)
{
	return max(abs(p.x) - size.x, abs(p.y) - size.y);
}
//****************************************
//距離関数 (ひし形)
//****************************************
float rhombus(float2 p, float2 r, float size)
{
	return r.y*abs(p.x) + r.x*abs(p.y) - size;
}
//****************************************
//距離関数 (正多角形)
//****************************************
float polygon(float2 p, int n, float size)
{
	float PI = 3.14159265358;
	float a = atan2(p.x, p.y) + PI;
	float r = 2 * PI / n;
	return cos(floor(0.5 + a / r) * r - a) * length(p) - size * 0.35 * log(n); // * 0.35 * log(n)は大きさ調整用
}
//****************************************
//距離関数 (星形)
//****************************************
float star(float2 p, int n, float t, float size)
{
	float PI = 3.14159265358;
	float a = 2 * PI / float(n) / 2;
	float c = cos(a);
	float s = sin(a);
	float2 r = mul(p, float2x2(c, -s, s, c));
	return (polygon(p, n, size) - polygon(r, n, size) * t) / (1 - t);
}			
//****************************************
//距離関数 (楕円)
//****************************************
float ellipse(float2 p, float2 r, float size)
{
	return length(p / r) - size * 0.5; // * 0.5は大きさ調整用
}
//****************************************
//距離関数 (ハート)
//****************************************
float heart(float2 p, float size)
{
	float PI = 3.14159265358;
	float angle = PI / 180 * - 48;
	p.x = abs(p.x);
	p = float2(p.x * cos(angle) - p.y * sin(angle), p.x * sin(angle) +  p.y * cos(angle));
	return ellipse(p, float2(0.9, 0.5), size*2.2);//*2.2は大きさ調整用
}
//****************************************
//距離関数 (円環)
//****************************************
float ring(float2 p, float size, float w)
{
	return abs(length(p) - size) + w;
}
//****************************************
```


以上、自作Shaderで便利な関数集でしたっ🌟

これらの関数は随時更新・追加して、困ったらいつでもここに立ち返って見れるようにしていく予定です。それではっ！

八ツ橋まろん