さやちゃんぐbotブログ

シェーダーで遊んだりするブログです。

Unityで画像にエフェクト加工するかんたんシェーダーを書く。

はいどーもー、さやちゃんぐbotです!

今日はですね!Unityで使えて、お手軽に画像を加工するシェーダーを紹介してみたいと思います!

gyazo.com

Scrapboxに書き溜めていたものにちょっぴり説明をつけていきます。

scrapbox.io

入門者、サンプルコードが欲しい人とか解説が欲しいという人にオススメでございます!

さっそくいってみましょう!!

 

いつものようにバージョンなどについて。

  • Unity : お好きなバージョンで
  • 登場するモデルデータ : かふじさん、ねこますさんのモデルを使わせてもらっております
  • 参考コード : 多くのサンプルシェーダーでkaiwareさんのコードをパク……インスパイアしました!

UnityのUnlitなフラグメントシェーダーについて書いています。バージョンはなんでも良いですが、HDRPでSRPな環境ではアカンので、Visual Effect Graphで遊んでいる人とかはお気をつけください。

この記事を書くにあたっての作業環境は、Unity2019.2.0a4です。

 

とりあえずシェーダーを使ってみたいだけであれば新規でUnlitシェーダーを作成し、以下に貼られているスニペットでUnlitシェーダーのfrag()関数をそっくり置き換えれば良いです。

 

色反転

gyazo.comシェーダーでは色をfixed4型でRGBの各色、Aでアルファ値を扱います。

で、RGBは値の範囲が0.0から1.0です。0で黒に、1で白に近づきます。

なので、「RGBの値を1から引いた数字にかえる」ことでネガポジ反転になります。

 

fixed4 frag (v2f i) : SV_Target
{
  fixed4 col = tex2D(_MainTex, i.uv);
  return 1. - col;
}

 

繰り返し

gyazo.com座標は横をu、縦をvとして扱い、色と同じく範囲は0.0から1.0です。

フラグメントシェーダーではi.uvという構造体の変数で使いますが、四角の範囲の左下が原点(0.0,0.0)、右上が(1.0,1.0)です。

このuvの数字に掛け算をしてから、その値を元にテクスチャを取り出すと繰り返し表示にできるんですね。

 

fixed4 frag (v2f i) : SV_Target
{
  float2 uv = i.uv;
  uv *= 3;
  fixed4 col = tex2D(_MainTex, uv);
  return col;
}

 

移動(UVスクロール)

gyazo.com画像が流れるように動く、通称UVスクロールです。

流れる水面や溶岩などに使われる手法ですが、実装はシンプルで座標に時間を足すだけです。

下のサンプルではfrac関数を使って、経過時間の小数点以下の値を利用しています。

 

fixed4 frag (v2f i) : SV_Target
{
  float2 uv = i.uv;
  uv += frac(_Time.y);
  fixed4 col = tex2D(_MainTex, uv);
  return col;
}

 

歪み

gyazo.comx座標にy座標の値をなんらか加工したものを足すと、画像が歪みます。

掛けたり足したりしたり、三角関数を使ったりして歪ませ方をアレンジすることもできます。また、時間_Timeを使うと動きをつけることもできます。

 

fixed4 frag (v2f i) : SV_Target
{
  float2 uv = i.uv;
  uv.x += frac(uv.y * 2.);
  fixed4 col = tex2D(_MainTex, uv);
  return col;
}

 

モザイク

gyazo.comモザイク状になるエフェクトコードは、初見だと理解するのに時間がかかるかもしれません。

以下のサンプルでは、uv座標を割り算してからfloorの値を取り出しています。これで小数点以下の数字が切り捨てられました。割った数で領域が分けられ、それぞれの範囲では「同じ値」になっています。

ここに割ったのと同じ数字で掛け算すると、全体のサイズが元のサイズに戻ります。しかし、分割した範囲ではそれぞれ「左下のuv座標に相当する値」に変わっているため、四角形の領域に分割された状態になります。

ううーん、説明がわかりづらい!とにかくサンプルで「20」となっている数字を大きくしたり小さくしたりして試してみましょう!モザイクの粗さが変わります。

 

fixed4 frag (v2f i) : SV_Target
{
  float2 uv = i.uv;
  uv = floor(uv / 20.) * 20.;
  fixed4 col = tex2D(_MainTex, uv);
  return col;
}

 

集中線

gyazo.com集中線を描くには極座標を使います……これはモザイク以上に説明が悩ましい!!

極座標では直交するxy座標のかわりに、半径rと角度aを使います…よくわからなければ調べてみて欲しい!!とにかく、原点を四角の中心に持ってきて、それを元に円を扱うみたいなイメージです。

あとは三角関数でaに掛けるパラメータや、smoothstepなどで集中線の密度とか長さとかをうまいこと調整します。これはもう、色々数字を変えてルックがどう変わるのかを見て確認した方が早いです。レッツトライ!

 

fixed4 frag (v2f i) : SV_Target
{
  float2 uv = 2. * i.uv - 1.;

  float r = length(uv);
  r = 0.7 * r - 0.7;

  float a = atan2(uv.y, uv.x);
  a = abs(cos(50. * a) + sin(20. * a));

  float d = a - r;
  float n = smoothstep(0.1, 0.4, saturate(d));

  fixed4 col = tex2D(_MainTex, i.uv);
  return n * col;
}

 

短冊切り

gyazo.com横に切ってズラすようなエフェクトです。先に出てきた歪みの応用編です。

画像のようなルックは、x座標にy座標をflootと三角関数で加工したものを足して実現しています。

下のサンプルコードのコメントアウトを外すと、縦に切ります。

 

fixed4 frag (v2f i) : SV_Target
{
  float2 uv = i.uv;
  uv.x += sin(floor(4. * uv.y) - _Time.y);
  //uv.x += cos(floor(4. * uv.x) + _Time.y);

  fixed4 col = tex2D(_MainTex, uv);
  return col;
}

 

RGBシフト

gyazo.comRGBのうち特定成分を隣のピクセルにズラしてあげる、RGBシフトです。

ちょっと座標をズラしてテクスチャを色別に取得すると実現できます。

関数の後ろについている「.r」みたいな書き方については、シェーダーのスウィズル(Swizzle)というキーワードでググってみてください。

 

fixed4 frag (v2f i) : SV_Target
{
  float2 uv = i.uv;

  float r = tex2D(_MainTex, uv + 0.1 * sin(_Time.y)).r;
  float b = tex2D(_MainTex, uv - 0.1 * sin(_Time.y)).b;
  float2 ga = tex2D(_MainTex, uv).ga;
  return fixed4(r, ga.x, b, ga.y);
}

 

反転

gyazo.com座標は0.0から1.0の範囲であらわすので、x座標やy座標を1から引いた値で置き換えると左右反転、上下反転になります。

以下のサンプルでは動きをつけるためにstep関数を使ってもう少しこみいったことをしていますが、反転を実装する考え方はシンプルです。

 

fixed4 frag (v2f i) : SV_Target
{
  float2 uv = i.uv;

  uv.x += step(sin(_Time.y), 0) * (1. - 2. * uv.x);
  uv.y += step(sin(_Time.y), 0) * (1. - 2. * uv.y);

  fixed4 col = tex2D(_MainTex, uv);
  return col;
}

 

ブラー

gyazo.comぼやけた効果を出すブラーです。

ループでちょっとずつズラしたテクスチャ座標の色を取りだして、色の足し算を行っています。

 

float4 _MainTex_TexelSize;
fixed4 frag (v2f i) : SV_Target
{
  float2 uv = i.uv;
  float2 dir = -uv;

  float d = length(dir);
  dir = normalize(dir) * _MainTex_TexelSize.xy;
  dir *= 1.2 * d;

  fixed4 col = tex2D(_MainTex, uv) * 0.19;
  for (int j = 1; j < 10; j++) {
    col += tex2D(_MainTex, uv + dir * j) * (0.19 - j * 0.02);
  }
  return col;
}

 

ヴィネット(Vignette)

gyazo.com円状に周囲の色あいを変えるヴィネットです。

座標の原点を中心に持ってきて、内積dot(uv, uv)を利用します。この内積の値は、中心が0.0で、中心から離れるほど大きな数字になっていきます。

このサンプルでは1.0から内積の値を引いたものを色に掛け算しているので、外側の色の値が0.0(か、またはマイナス)になり黒く見えます。

 

fixed4 frag (v2f i) : SV_Target
{
  float2 uv = i.uv;
  fixed4 col = tex2D(_MainTex, i.uv);

  uv = 2. * uv - 1.;
  col *= 1.0 - dot(uv, uv);
  return col;
}

 

グレースケール

gyazo.comカラー画像をグレースケールに変えるには、RGBすべての値を足して割るような計算をすれば良いです。

が、このやり方はあるマジックナンバーを使うと、いいかんじの見栄えのグレースケールになるという手法があるらしいです。

パラメータは以下のサンプルを読んでみてください。

 

fixed4 frag (v2f i) : SV_Target
{
  fixed4 c = tex2D(_MainTex, i.uv);
  return 0.21 * c.r + 0.72 * c.g + 0.07 * c.b;
}

*1

 

デュオトーン

gyazo.com

gyazo.comデザイン界隈には2色を使ったオシャレ画像を作るやり方でデュオトーンという手法があるらしいです。

シェーダーの実装では、2色のパレットを定義し、グレースケールの値を利用して2色をlerp関数で線形補完してあげれば良いです。

 

fixed4 frag (v2f i) : SV_Target
{
  fixed4 c = tex2D(_MainTex, i.uv);
  float gray = 0.21 * c.x + 0.72 * c.y + 0.07 * c.z;
  float4 d1 = float4(195., 201., 13., 255.) / 255.;
  float4 d2 = float4(136., 241., 48., 255.) / 255.;
  return lerp(d1, d2, gray);
}

 

トンネル

gyazo.com画像を極座標で円状に歪ませ、真ん中を暗くしたトンネルのようなエフェクト。

1枚絵の加工というよりは、岩壁のマテリアルに使うようなテクスチャで奥行きがあるっぽく見えるビルボードを簡易的に作るような使い方ができるかもしれません。

 

fixed4 frag (v2f_img i) : SV_Target
{
  float2 uv = 2. * i.uv - 1.;
  float d = 1. / length(uv);

  uv = float2(d, atan2(uv.y, uv.x) / UNITY_TWO_PI);
  fixed4 col = tex2D(_MainTex, uv);
  fixed4 c = (2. - d) * col;

  return c;
}

 

グリッチ

エッジ抽出

このあたり、ここに載せられればかっこいい絵が見れる可能性があったのですが、未だ研究中なのでキーワードだけ載せて割愛!割愛です!*2

 

カメラの水滴

gyazo.com水滴とかすげーかっこいいじゃないですか!カメラにアタッチしたりするの、ちょうやりたい!!

が、あまりにもコードが長すぎるので、以下のリンクとかを見て頂ければ。カメラ用のc#スクリプトとシェーダーコードを載せてます。VRのカメラにアタッチすると面白かったりもします。

scrapbox.ioシェーダーの元ネタはshadertoyです。

www.shadertoy.com

ということで、Unityですぐに使える画像加工用シェーダーでした。

静止画だけでなく、VJ、VR、レイマーチングなどでも使える手法がたくさんありますので、いろいろとやってみると楽しいかと思います!

 

ここに載せたシェーダーの大半はkaiwareさんのコードを参考にしていますので、GitHubも覗きに行くと良いでしょう!!

github.com

 

ではでは。

*1:このRGBに対応するマジックナンバーは、輝度の計算で使われるものです。また、dotを使ってdot(c.rgb, fixed3(0.21, 0.72, 0.07)と実装することもできます。

*2:実装としては、グリッチは短冊やRGBシフトなどを組み合わせて時間が動かしたもので、エッジは「微分」…隣接したピクセルの色の差を元に計算すれば良いです。