コンテンツにスキップ

Unityシェーダースニペット集

シェーダー定義マクロ及び定数

ビルドインでよくつかうやつ

Name type 説明
_Time float4 ステージのロードからの時間 (t/20, t, t2, t3)。
unity_DeltaTime float4 デルタ時間: (dt, 1/dt, smoothDt, 1/smoothDt)
_ScreenParams float4 x はカメラのターゲットテクスチャの幅(単位:ピクセル)、y はカメラのターゲットテクスチャの高さ(単位:ピクセル)、 z は 1.0 + 1.0/幅、 w は 1.0 + 1.0/高さです。
_ProjectionParams float4 x は 1.0 または -1.0、反転した射影行列で現在レンダリングしている場合は負の値。y は カメラのNear Plane、z はカメラのFar Plane、w は 1/Far Plane。

よく使う定数

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
// 円周率
#define PI 3.14159265
static const PI = 3.14159265f;

// 角度→ラジアン
#define DEG2RAD PI / 180.0f
static const float DEG2RAD = PI / 180.0f;

// 使い方
float rad = degree * DEG2RAD;

全部入りStandardSurfaceShader

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
Properties
{
    // アルベド
    _Color ("Color", Color) = (1,1,1,1)
    _MainTex ("Albedo (RGB)", 2D) = "white" {}

    // 法線
    _BumpScale("Scale", Float) = 1.0
    _BumpMap("Normal", 2D) = "bump" {}

    // ラフネス
    _Glossiness("Roughness", Range(0.0, 1.0)) = 0.5 // Standard準拠だがRoughnessを_Glossinessという名前で受けてるのムカつく
    _SpecGlossMap("Roughness Map", 2D) = "white" {}

    // メタリック
    _Metallic("Metallic", Range(0.0, 1.0)) = 0.0
    _MetallicGlossMap("Metallic", 2D) = "white" {}

    // オクルージョン
    _OcclusionStrength("Strength", Range(0.0, 1.0)) = 1.0
    _OcclusionMap("Occlusion", 2D) = "white" {}
}
void surf (Input IN, inout SurfaceOutputStandard o)
{
    fixed4 c = tex2D(_MainTex, IN.uv_MainTex);
    o.Albedo = c.rgb;
    o.Alpha = c.a;

    o.Normal = UnpackScaleNormal(tex2D(_BumpMap, IN.uv_BumpMap), _BumpScale);

    o.Metallic = tex2D(_MetallicGlossMap, IN.uv_MainTex);
    o.Metallic *= _Metallic;

    o.Smoothness = 1.0f - tex2D(_SpecGlossMap, IN.uv_MainTex);
    o.Smoothness *= (1.0f - _Glossiness);
}

スクリーン座標を取得する

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
struct v2f
{
    float2 uv : TEXCOORD0;
}

v2f vert (
    float4 vertex : POSITION,
    float2 uv : TEXCOORD0,
    out float4 outpos : SV_POSITION // VPOSとSV_POSITIONを両方同時にフラグメントシェーダーに渡せないので、SV_POSITIONにout修飾子を付ける
)
{
    v2f o;
    o.uv = uv;
    outpos = UnityObjectToClipPos(vertex);
    return o;
}

fixed4 frag (v2f i, UNITY_VPOS_TYPE screenPos : VPOS) : SV_Target
{
    // screnPosは解像度の値(ex. 1920x1280)のような整数値なので0-1に変換する
    float2 screenPosNorm  = screenPos.xy / _ScreenParam.xy;
}

アルファブレンディング

1
2
3
4
5
6
7
float4 src; // 前景
float4 dst; // 背景

float outA = src.a + dst.a * (1 - src.a);
float3 outRGB = lerp(dst.rgb, src.rgb, src.a);

float4 out = float4(outRGB, outA);

コントラスト調整

1
2
float3 c = color;
c = 1.0f / (1.0f + exp(-_Contrast*(c-0.5f)));

軽いコントラスト

1
dst = src - (src - src * src) * -x;

Remap01

1
2
3
4
float remap01(float x, float min_value, float max_value)
{
    return (x - min_value) / (max_value - min_value);
}

Remap

1
2
3
4
float remap(float value, float inputMin, float inputMax, float outputMin, float outputMax)
{
    return (value - inputMin) * ((outputMax - outputMin) / (inputMax - inputMin)) + outputMin;
}

ガンマ補正

1
2
3
4
5
6
7
8
9
inline float3 srgbToLinear(float3 c)
{
    return lerp(c / 12.92, pow((c + 0.055) / 1.055, 2.4), step(0.04045, c));
}

inline float3 linearToSrgb(float3 c)
{
    return lerp(c * 12.92, 1.055 * pow(c, 1.0 / 2.4) - 0.055, step(0.0031308, c));
}

LinearStep

閾値aから閾値bで0~1で線形に遷移する関数

1
2
3
4
float linearstep(float a, float b, float x)
{
    return saturate((x - a) / (b - a));
}

SmoothStep

閾値aから閾値bで0~1で緩やかに遷移する関数

1
2
3
4
5
float smoothstep(float a, float b, float x)
{
  float t = saturate((x - a)/(b - a));
  return t * t * (3.0 - (2.0 * t));
}

グレースケールからノーマルを作る

事前に_TexelSizeを宣言しておく必要がある。

SurfaceShader

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
// テクスチャ宣言時に_HightMap_TexelSizeを宣言
sampler2D _HightMap;
float4 _HightMap_TexelSize;

// 【中略】

float center = tex2Dlod(_HightMap, float4(IN.uv_HightMap, 0, 0)).r;
float right  = tex2Dlod(_HightMap, float4(IN.uv_HightMap + float2(_HightMap_TexelSize.x , 0), 0, 0)).r;
float up     = tex2Dlod(_HightMap, float4(IN.uv_HightMap + float2(0, _HightMap_TexelSize.y), 0, 0)).r;

float sampleDeltaRight = right - center;
float sampleDeltaUp    = up    - center;

float3 grayNorm = cross(
    float3(1, 0, sampleDeltaRight * _NormalStrength),
    float3(0, 1, sampleDeltaUp    * _NormalStrength));

o.Normal = normalize(grayNorm);

Alpha to Coverage有効

1
2
3
4
5
6
7
8
9
SubShader
{

    // ここで指定
    AlphaToMask On

    Pass
    {}
}

ScreenPosの正規化

SurfaceShader

解説

xy: 0 ~ w を 0 ~ 1
z: -w ~ w を -1 ~ 1
に正規化する

1
2
float4 screenPos = float4(IN.screenPos.xyz, IN.screenPos.w + 0.00000000001);
float4 screenPosNorm = screenPos / screenPos.w;

プラットフォームによる差を吸収し、Zが必ず0から1になるようにする

1
screenPosNorm.z = (UNITY_NEAR_CLIP_VALUE >= 0) ? screenPosNorm.z : screenPosNorm.z * 0.5 + 0.5;

ScreenPosからGrabTextrueの位置、UV値を求める

SurfaceShader

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
inline float4 ComputeGrabScreenPos(float4 pos)
{
#if UNITY_UV_STARTS_AT_TOP
    float scale = -1.0;
#else
    float scale = 1.0;
#endif
    float4 o = pos;
    o.y = pos.w * 0.5f;
    o.y = (pos.y - o.y) * _ProjectionParams.x * scale + o.y;
    return o;
}

// 使用方法

float4 grabPos = ComputeGrabScreenPos(IN.screenPos);
float2 grabUV = grabPos.xy / grabPos.w;

fixed4 grabColor = tex2D(_GrabTextrue, grabUV);

デプステクスチャの取得

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
// 宣言が必要
sampler2D _CameraDepthTexture; // カメラからのデプステクスチャ

// 【中略】

// 正規化されたScreenPosを使用する必要がる

float4 screenPos = float4(IN.screenPos.xyz, IN.screenPos.w + 0.00000000001);
float4 screenPosNorm = screenPos / screenPos.w;
screenPosNorm.z = (UNITY_NEAR_CLIP_VALUE >= 0) ? screenPosNorm.z : screenPosNorm.z * 0.5 + 0.5;

// この段階では非線形
float depth = SAMPLE_DEPTH_TEXTURE(_CameraDepthTexture, screenPosNorm.xy);

// カメラからの距離 (ワールド座標系のスケール)
float linearEyeDepth = LinearEyeDepth(depth);

// カメラからの距離(Near - Far -> 0 - 1)
float linear01 = Linear01Depth(depth);

// 取得できたdepthと同じ単位系のモデルのデプスの求め方
float surfaceDepth = UNITY_Z_0_FAR_FROM_CLIPSPACE(IN.screenPos.z); 

HSV RGB変換

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
float3 hsv2rgb(float3 c)
{
    float4 K = float4(1.0, 2.0 / 3.0, 1.0 / 3.0, 3.0);
    float3 p = abs(frac(c.xxx + K.xyz) * 6.0 - K.www);
    return c.z * lerp(K.xxx, clamp(p - K.xxx, 0.0, 1.0), c.y);
}

float3 hsv2rgb(float3 c)
{
    return ((clamp(abs(frac(c.x + float3(0, 2, 1) / 3.)*6. - 3.) - 1., 0., 1.) - 1.)*c.y + 1.)*c.z;
}

float3 rgb2hsv(float3 c)
{
    float4 K = float4(0.0, -1.0 / 3.0, 2.0 / 3.0, -1.0);
    float4 p = lerp(float4(c.bg, K.wz), float4(c.gb, K.xy), step(c.b, c.g));
    float4 q = lerp(float4(p.xyw, c.r), float4(c.r, p.yzx), step(p.x, c.r));

    float d = q.x - min(q.w, q.y);
    float e = 1.0e-10;
    return float3(abs(q.z + (q.w - q.y) / (6.0 * d + e)), d / (q.x + e), q.x);
}

強制的にポイントサンプリングする

テクスチャの設定で変えられるので。正直使いどころはあまりない。 1つのデータを複数のサンプリング方法で取得するときに便利?

1
2
3
4
5
// 無理やりポイントサンプリングする
{
    float2 pixelPos = floor(i.uv * _MainTex_TexelSize.zw) + float2(0.5f,0.5f);
    i.uv = (float2)pixelPos * _MainTex_TexelSize.xy;
}

視界に張り付くシェーダー

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
// なるべく最後に描画されるように設定する。
Tags { "RenderType" = "Transparent" "Queue" = "Overlay+3000" "IgnoreProjector" = "True" }

v2f vert(appdata v)
{
    v2f o;
    // uv座標をポジションにする。
    o.pos = float4(2 * v.uv.x + 1 -  2, 1 - 2 * v.uv.y, 1, 1);
    o.uv = TRANSFORM_TEX(v.uv, _MainTex);
    return o;
}

ワールド座標

1
2
// 頂点シェーダー
o.worldPos = mul(unity_ObjectToWorld, v.vertex).xyz;

オブジェクトの原点座標

1
2
// モデル原点
float4 objectOrigin = mul(unity_ObjectToWorld, float4(0.0, 0.0, 0.0, 1.0));

ビュー方向

1
float3 viewDir = normalize(UnityWorldSpaceViewDir(worldPos));

ベクトル回転

軸回転

1
2
3
4
5
6
7
8
9
float sinX = sin(_Time.x * 0.1 * 2 * PI);
float cosX = cos(_Time.x * 0.1 * 2 * PI);

// X軸回転
float3x3 rotateX = float3x3(
        1, 0, 0,
        0, cosX, -sinX,
        0, sinX, cosX
    );

ノイズ系