Shader Graphのremapノードが便利だった

最近Shader Graphを触る機会があるので、いろいろお試しで作っていたんですが、remapノードというのが便利だったのでShaderToyとかでも今後使っていこうと思ってます。

github.com

float remap(float val, float inMin, float inMax, float outMin, float outMax)
{
    return clamp(outMin + (val - inMin) * (outMax - outMin) / (inMax - inMin), outMin, outMax);
}

inMin ~ inMaxの入力値valをoutMin ~ outMaxにマッピングしてくれる。

Shader GraphのRemapノードは出力値をクランプしてくれないが、してくれた方が使い勝手が良いので変更した。

地味に使いどころがある気がする。

2019年3月に作ったシェーダーを紹介する

はじめに

先月からやっている月毎に制作したシェーダーの紹介第二回目です。

軽く解説やバックボーンを交えて紹介しようと思います。

2/27

レイマーチングでは、座標に対して繰り返し操作を行うことで似たような動きの大量のオブジェクトを描画するのが得意ですが、それぞれ独立した動きのオブジェクトを描画するのはそれほど得意ではありません。(レイのオーバーシュートやグリッド内の制限など)

今回はそのうちのオーバーシュートの問題をこちらの記事の手法で解決して、複雑な動きをする大量のオブジェクト描画をやりました。

qiita.com

動くものはこちらから見れます

www.shadertoy.com

3/18

私はいつもShaderToyという海外のシェーダー勢が集まるサイトでシェーダーを公開していますが、これはNEORTという最近リリースされた、日本生まれのデジタルアート全般の共有サイトで公開しました。

今後は2000文字程度のシェーダーや、軽量なシェーダーはこちらで公開しようと考えています。

動くものはこちらから見れます

https://neort.io/art/bi75kac3p9f8qviu3g00

f:id:kaneta1011:20190326235128p:plain
Glow

3/19

こちらは3月中旬ごろにTwitterでバズっていたツイートを見て似たようなことをやりたくて試しに土と泥を雑に作ったものです。

またノイズ構成等一部をこちらのチュートリアルを参考にして実装しています

80.lv

今は別のことをやっていますが、この表現もいずれブラッシュアップして一つの作品にしてみたいです。

このシェーダーは最適化不足(地形との衝突判定)でなかなか重いものになっているため、一般公開していませんが興味があればご覧ください。

動くものはこちらから見れます

www.shadertoy.com

3/20

f:id:kaneta1011:20190326235734p:plain
切稜立方体?
色んなゲームのアイテムボックス等で稀に利用されている、切稜立方体(ホントか?正しい名前を知りたい)の距離関数が必要だったので作成しました。

結構難しそうな形状だと思っていたんですが、八面体を各軸に引き延ばすという単純な操作で実現することが出来ました。

これを使って何か作ろうと思います。

公開していませんが動くものはこちらから見れます

www.shadertoy.com

さいごに

3月に作った4つのシェーダーを紹介しました。

来月も、もし何か作ったら紹介します。

2019年2月に作ったシェーダーを紹介する

f:id:kaneta1011:20190221184937p:plain
サムネ用

はじめに

去年の12月頃から、個人的にシェーダーのみで短い映像や画像を作ってtwitterで公開するという事をやっています。

今月から、シェーダーを作成した月の月末に公開しなかったシェーダーも含めて、軽く解説やバックボーンを交えて紹介しようと思います。

2/4(公開してない)

f:id:kaneta1011:20190222004709p:plain
PBR
物理ベースレンダリング(PBR)をテストする時に作成しました。

直接光のBRDFモデルは、拡散反射に正規化ランバートモデル、鏡面反射にCook-Torranceマイクロファセットモデルを使用しています。

両者ともに実装した経験があることや、モデル自体がよく知られているので文献や参考実装が豊富にあり、困ることはなかったです。(正しく実装できているかはあまり自信がない...)

間接光のBRDFモデルは、Unreal Engineで使用されているPre-filtered environment mapと言うIBL手法の、Environment BRDF(Ambient BRDFと呼ばれていたりもするもの)の近似を使用しています(プロシージャルに毎フレーム生成した環境マップをフィルタするのは困難なため)

また今回は、間接光のオクル―ジョンとしてよく知られるAmbient Occlusion(AO)のみでなく、EA のFrostbiteエンジンで使用されている、AO項からSpecular Occlusionを計算する処理も追加しました。(参照75ページあたりから)

そしてHDRで使用されるトーンマッピングですが、これは@gam0022氏のhanamaru-renderer実装をGLSLにそのまま実装し直しました。

PBRや簡単な物だけですが、ハイエンドエンジンの技術を取り込んだのでそこそこよい見た目になった気がします。

これは公開していませんが、動くものはこちらから見れます。

www.shadertoy.com

2/10(公開した)

f:id:kaneta1011:20190222004816p:plain
ミーチルシェーダー
これは、@KeiChan氏と@gam0022氏と私の三人で、とあるアニメのライブを見に行った際に、ライブ背景のスクリーンに表示されていたVJを家に帰ってから5時間ほどかけて、うろ覚え状態で思い出しながら作ったものです。

3次元的に幾何学な形状のトンネルを進んでいきますが、実はこれは3Dではなく2Dの作品です。

幾何学模様を複数のレイヤーで作成して、サイズを変えたり、奥のレイヤーほど黒くすることで、疑似的に3次元的な表現をしています。

この手法は、当時TLで流行っていたので乗っかりました。

我ながら、うろ覚えのため20%程の再現度でほとんど原型をとどめませんでした...(おそらく色は白色一色だったし、もっと密度も少なかった気がする...)

ですがわかる人にはわかるようで、一人気づいてくれた方がいました!(うれしい!!)

これはこちらのツイートで公開しました

実際に動くものはこちらから見れます

www.shadertoy.com

2/19(公開した)

f:id:kaneta1011:20190222004911p:plain
🤔
私が🤔を好きなこともあり、シェーダーで何か形にできればなぁと考えていました。

そこで、最初に紹介したPBRをそのまま活用して、一つのシェーダーに仕上げました。

空や環境光はかなり力技+二度と読まない気で作っているのでやばいコードになってます...

この映像をループ動画にして、twitterで公開したところ、シュールな映像もあいまってそこそこ受けました。

実際に動くものはこちらから見れます

www.shadertoy.com

また面白いことに、派生作品もいくつか出現しました。

こういったフィードバックがあるのが、twitterの面白いところだと思います。

派生作品1

こちらは、自作のシェーダーをサポートしているという、zx spectrumエミュレーターに私のシェーダーをポートして前面に表示した物のようです。

派生作品2

thinkingFaceの距離関数を、自作の距離関数に変更した物のようです。

PBRで描画されているので、色や形状や材質が変わっても説得力のある映像になっていますね!

派生作品3

こちらも公開していたコードを独自の距離関数に変更したものです。

メタリックな表現もかっこいいですね!

実はこの文字にも私が過去に作ったシェーダーが使われていたりします!(重いためブラクラ注意!)

www.shadertoy.com

派生作品4

こちらは変化球できましたね🤔

爆ぜるコインがシュールです😂

さいごに

2月に作った3作品を紹介しました。

来月も、もし何か作ったら紹介します。(三日坊主なので不安しかない)

二次元ベクトルに垂直なベクトル

必要だったのでメモ

二次元ベクトルであれば、90度回転させると垂直なベクトルになります(当たり前)

90度の回転であれば、行列計算の結果がコンポーネントと符号の入れ替えになるため、わざわざ回転行列を用意する必要はなく

vertical = vec2(-vec.y, vec.x);

とするだけで良いです

www.shadertoy.com

なんとあのShadertoyのShader Of the Weekに作品が掲載された!!

憧れのサイトのトップページを飾れて嬉しさしかないです!!

学生時代は「Shadertoy = 神が集まるサイト」で平民の自分は全く理解できなかったので、神シェーダーを眺めているだけでした

まさか自分が作品を投稿するようになって、ましてやトップページの一番大きいところに載せて頂けるとは当時は想像すらしていなかったです...

記念撮影

f:id:kaneta1011:20181228003431p:plain

f:id:kaneta1011:20181228003649p:plain

Tokyo Demo Fest 2018のGLSL Graphics Compoで優勝した!作品の解説等

2018年の12/1と12/2の2日間に渡って、Tokyo Demo Fest 2018という日本唯一のデモパーティが開催され、そこに初参加してきました。

僕はそのイベントの、GLSL Graphics CompoCombined Graphics Compoに作品をエントリーして1位と4位に選んで頂きました!ありがとうございます!

f:id:kaneta1011:20181212200638p:plain
GLSL Graphics Compo 優勝!

GLSL Graphics Compo作品の動画やソースは以下から見ることができます。

www.youtube.com

Graphics Compoの作品は当日会場に到着してから、友人と話しているときに思いついて、GLSL Graphics Compo用のネタでお蔵入りしていたシェーダーを流用して、iq氏の4kテンプレートで大急ぎで作りました。

僕がGLSL Graphics CompoにエントリーしたTraveler 2は、偉大な先人の方々の解説記事や作品に多大な影響を受けています。誰の作品にどんな影響を受けたかはこちらの記事に全てまとめているのでご参照ください。 qiita.com

この記事では、先人達への感謝の気持ちを込めて、さらに深堀して作品の解説をしたいと思います。

Traveler 2の解説

コンセプト

過去の入賞作品や、TDF2017のGLSL Graphics CompoのYoutube動画を何度も見て、以下の条件に当てはまるものを作ろうと考えました。

  • 過去の作品にはないような見た目
    • 単純に新しい体験の方が楽しめる
  • 過去の作品に比べて尺が長め
    • 上映時間が長い方が印象に残るかも?
    • ほかの作品が大体30秒ほどなので、差別化できるかもしれない
    • あまり長くすると退屈するかもしれないので2分を目安
  • 上映中に退屈になる時間が無い
    • GLSL作品は任意の音楽を再生できないので、常に画面に変化を加える
    • Traveler 2では大体8beat毎に新しい変化を加えた
  • 小気味のよい動き
    • 2017年優勝作品がぬるぬる動いていた

結果、「1パスGLSLだけで作られているとは思えないようなメガデモ風の作品」というコンセプトの元にTraveler 2が誕生しました。

「これが1パスGLSLだけで作られてるの!?」と一人にでも思っていただけていればうれしいです。

時系列

今年の6月からレイマーチングの勉強を初めると同時に、Traveler 2の元となるtraveler.という作品を2週間で作りました。

グラフィックの知識が多少あったので、割とすんなりレイマーチングを習得することができました。

Shadertoy BETA

f:id:kaneta1011:20181209161413p:plain
traveler.

その後10月の下旬までの4ヵ月間、シェーダー芸のポキャブラリーを増やすために、Shadertoyや既存の作品のコードを読んだり、小ネタを実装したりしていました。

10月下旬からTDF本番までは、できるようになったことをtraveler.に詰め込んでTraveler 2が完成しました。

traveler.からTraveler 2までの履歴はすべてこちらのPRに残っているので、興味がある方はご覧ください! github.com

ロード画面について

f:id:kaneta1011:20181209234852p:plain 当日の上映時は緊張で回りの音が聞こえてなかったんですけど、アーカイブを見ると、最初のロード画面に困惑されている方が多そうでした。

皆さんお察しの通りだと思いますが、GLSL Sandboxでは事前にロードする処理等は書けないので、実際にロードしているわけではありません

ではなぜ、30秒もロード画面を表示したのかといいますと、シェーダーのコンパイルに20秒近く掛かってしまうからなんです。

GLSL Sandboxはコンパイル中も時間が進み続けてしまい、20秒もコンパイルをしていると、作品が途中から再生されてしまうという可能性があったんです。

なので最初の30秒間に待機画面を用意して、必ず作品を最初から再生してもらう、というのが狙いでした。

しかし上映時はブラウザに、リハーサルした際のキャッシュが残っていたのか一瞬でコンパイルが完了していましたね...

皆さんには30秒待ってもらうことになってしまいましたが、逆にインパクトがあったのかなぁとも思います。

背景

f:id:kaneta1011:20181209234935p:plain

f:id:kaneta1011:20181209235143p:plain 背景の3Dオブジェクトは、すべてメンガーのスポンジを改造したものを2つ重ねて作りました。

序盤のシーンでは重ねたもののうち、一つをbeatに合わせてIFS中のイテレーション毎に回転させています

後半は、追加でz軸を中心に回転foldをして複雑な形状を作りました。

ポストエフェクト

f:id:kaneta1011:20181209234720p:plain 今回は描画結果に対して3種類のポストエフェクトを使用しました。

グレアエフェクト

中央の球体と進行方向にグレアエフェクトを使用しました。

といっても1パスなので、よく使われるような描画結果をボカして合成するといった手法は使えないので、レイの進行方向とオブジェクトの角度を光量とすることにしました。

float flare = pow(max(0.0, dot(vec3(0.0, 0.0, 1.0), ray)), stageFlareExp * 1.25);
float flare2 = pow(max(0.0, dot(vec3(0.0, 0.0, 1.0), ray)), stageFlareExp);
vec3 f = flare * stageFlareCol + flare2 * di * stageFlareCol * 0.05;
    
float sflare = pow(max(0.0, dot(normalize(sp - ro), ray)), travelerFlareExp * 1.25);
float sflare2 = pow(max(0.0, dot(normalize(sp - ro), ray)), travelerFlareExp);
vec3 s = sflare * travelerFlareCol + sflare2 * di * travelerFlareCol * 0.05;

かなりいい加減な方法ですが、割とよい雰囲気を出せたように思います。

レンズダート

レンズに埃のようなものが付いているときにでるあれです。

バトルフィールドか何かのポストエフェクトで見て、かっこよかったので入れてみました。

実装はとても単純で、画面をいくつかのセルに分けてそれぞれに、ダートエフェクトを表示します。

その時にセル毎の乱数でダートエフェクトの位置を変えることで、不規則に並べることができます

それだけでは、密度が足りないのでいくつかのレイヤーに分けてリアルタイムでダートマスクを生成しました。

vec3 dirt(vec2 uv, float n)
{
    vec2 p = fract(uv * n);
    vec2 st = (floor(uv * n) + 0.5) / n;
    vec2 rnd = hash(st);
    float c = Bokeh(p, vec2(0.5, 0.5) + vec2(0.3) * rnd, 0.2, abs(rnd.y * 0.4) + 0.3, 0.25 + rnd.x * rnd.y * 0.2);
    
    return vec3(c) * exp(rnd.x * 4.0);
}

vec3 di = dirt(uv, 3.5);
di += dirt(uv - vec2(0.17), 3.0);
di += dirt(uv- vec2(0.41), 2.75);
di += dirt(uv- vec2(0.3), 2.5);
di += dirt(uv - vec2(0.47), 3.5);
di += dirt(uv- vec2(0.21), 4.0);
di += dirt(uv- vec2(0.6), 4.5);

ダートマスクのみを切り出したものをこちらに用意しているのでご参照ください。

Shadertoy BETA

f:id:kaneta1011:20181212183932p:plain
ダートマス

周辺減光

本来はレンズに発生する光学現象ですが、今回はいい感じに画面端を暗くすることでそれっぽい雰囲気を出しました。

お手軽に画面のクオリティアップを狙えるのでオススメです。

vec2 uv = fragCoord.xy / iResolution.xy;
uv *=  1.0 - uv.yx;
float vig = uv.x*uv.y * 200.0;
vig = pow(vig, 0.1);
col = saturate(pow(col, vec3(1.0 / 2.2))) * vig;

疑似パーティクル

f:id:kaneta1011:20181209235104p:plain

途中のシーンから表示されるパーティクルのようなものは、ポストエフェクトで紹介したレンズダートとほぼ同じことをしています。

レンズダートは2Dでしたが、こちらは3D空間でmodして増やした球やボックスをセル毎に乱数で間引く + 中央から乱数で少し位置を変えるということをした上で、全体を真上にスクロールしました。

また球は、セルの中央を起点にランダムに回転させることで複雑な動きをしているように見せかけています

疑似パーティクルの部分のみを切り出したものをこちらに用意したのでご参照ください。

Shadertoy BETA

f:id:kaneta1011:20181212181806p:plain
疑似パーティクル

2DのIFSで模様

f:id:kaneta1011:20181209235524p:plain

球体や背景の模様はとても単純で、2DのIFSを使用してボックスの輪郭のみを描画しています

実際に使用しているコードがこちらにありますが、とても簡潔ですね。

vec3 tex(vec2 p, float z)
{
    vec2 q = (fract(p / 10.0) - 0.5) * 10.0;
    float d = 9999.0;
    for (int i = 0; i < 5; ++i) {
        q = abs(q) - 0.5;
        q *= rot(0.785398);
        q = abs(q) - 0.5;
        q *= rot(z * 0.5);
        float k = sdRect(q, vec2(1.0, 0.55 + q.x));
        d = min(d, k);
    }
    float f = 1.0 / (1.0 + abs(d));
    return vec3(smoothstep(0.8, 0.9, f));
}

ボックスを軸で折り畳みして平行移動・回転するという動作を繰り返すことで複雑な模様が生まれます。

この時引数のzを変えることでいろいろなバリエーションの模様ができるのですが、Traveler 2の模様を作るにあたって、パラメータをtimeにして眺めながら、好みの模様ができるtimeをメモしてコードに直打ちしました。

模様部分のみを切り出したものを以下に用意していますのでご参照ください。

Shadertoy BETA

f:id:kaneta1011:20181212155309p:plain
紋様

Shadertoy BETA

f:id:kaneta1011:20181212155501p:plain
球体にマッピングしたもの

モーションブラー

f:id:kaneta1011:20181209235346p:plain

モーションブラーを愚直に実装してしまうと、1フレーム内で過去のレンダリング結果をいくつか計算しなおしてブレンドするという方法になります。

Traveler 2も最初はそうしていたんですが、後半になるにつれて処理負荷が無視できないものになってきました...

そこで、最終的に採用したのはピクセル毎に時間をずらすという単純な手法です。

多少見た目がノイジーになってしまいましたが、フルHDで再生する分には許容範囲でした。

実装はこのようになっています。

beat = (t + hash(p).x * 0.0065 * (1.0 - saturate((orgBeat - 230.0) / 4.0)) * step(12., orgBeat)) * BPM / 60.0;

tはtimeです、 * (1.0 - saturate((orgBeat - 230.0) / 4.0)) * step(12., orgBeat)) こんなよく分からないものが付いていますが、これは、最初と最後にカメラが大きく揺れるシーンがあり、その二か所のみノイズが許容できなかったのでモーションブラーをしないようにする処理です。

カメラの手振れ

これはかなりオススメなんですが、カメラの手振れにfbm(fractal brownian motion)を使用しました。

最初はカメラの位置と視点を揺らすつもりだったんですが、元々結構ギリギリなカメラワークをしていたりしたので、一部のシーンでカメラがめり込んでしまいました...

Traveler 2ではカメラをめり込ませたくなかったので、レイを飛ばす前にスクリーン座標をfbmでオフセットすることで、手振れさせても絶対にめり込まないようにしました。

vec2 pp = p + (vec2(fbm(vec2(beat * 0.1), 1.0), fbm(vec2(beat * 0.1 + 114.514), 1.0)) * 2.0 - 1.0) * .65;
vec3 col =  scene(pp) * glitchColor;

カメラに手振れを追加すると無機質な映像が、一気に臨場感ある映像になって感動しました!

苦労話

コンパイルに時間がかかりすぎてChromeがクラッシュする...

最終提出時に手元のPC(i7 2.7GHz/GTX1050)ではコンパイル時間18秒ぐらいで、コンパイル時に6回に1回ぐらいクラッシュする状態でした...

最強のコンポマシンならきっと動く!と神に祈る勢いで提出したんですが、ちゃんと上映されてホッとしています。

コンパイル時間の問題はWindows限定で、おそらくAngleを経由してOpenGLを動かしているからだと推測していますが、正しいことはわかりません。

ちなみにChromeのuse-angleオプションを使用してOpenGLを直接動かすと、Windowsでもコンパイルが爆速になります。詳しい話は以下の記事を見てください。

nanka.hateblo.jp

シェーダーがクラッシュして、もう機能追加できない!という状態が制作中に3度ほどありましたが、品質を落としたり、ifを撲滅したりすることで何とか納得のいく状態まで作り終えることができました。

コンパイル時間改善にもっとも効果があったのがifの撲滅で、以下のPRを適用すると、20秒だったコンパイル時間が12秒になりました

github.com

まだまだTraveler 2に入れたいものがあった...

コンパイル時間の問題が無ければ追加したいものがまだまだありました...

事前に用意していて入らなかったものを2つ紹介します。

タイトルアニメーション

一番最初に球体が飛んでいくシーンの直前に入れようとこんなものを用意していました。

残念ながらコンパイル時間が長すぎて入れることができませんでした...

Shadertoy BETA

f:id:kaneta1011:20181212195115p:plain
タイトルアニメーション

TDFロゴ

Graphics Compoに提出したGLSL Compoでお蔵入りになったと言った奴です。

これは3Dの距離関数として作っていて、Traveler 2序盤の不自然なほど激しいカメラワークが2箇所あるのですが、そこで一瞬TDFの3Dロゴを表示しようと考えていました。

Shadertoy BETA

f:id:kaneta1011:20181212195736p:plain
TDFの3Dロゴ

作品の最終シーンがなかなか決まらない...

提出版最終シーンは、紆余曲折あって、画面に徐々にノイズが現れて、最後にシャットダウンするようなシーンになっています。

最初は別プランでやっていましたが、終わり感が出せずいまいちしっくりこなかったため、同僚の@amanatsu_nit氏に相談したところ、シャットダウン演出を提案してもらい、モックまで作ってもらいました!

f:id:kaneta1011:20181212205817g:plain
シャットダウンエフェクトのモック

ここにたどり着くまでになかなか迷走しましたが、本番の前日ぐらいまでモックを見ながら、良しなに調整できたので、盛り上がる最終シーンに仕上がったと思います。

さいごに

Tokyo Demo Festは、同僚のgam0022氏が2016年のGLSL Graphics Compoで3位入賞して、そこで初めて存在を知りました。

同僚の活躍を見て、僕も作品を出したい!と常々思っていたのですが、時間があまりとれずに、今年の開催が12月にずれたこともあって、ようやく初参加することができました!

僕はコミュニケーションが苦手なので知人以外とあまり喋ることができませんでしたが、後半はtwitterでいつも見かけるような有名人の方とも沢山お話しできてとても楽しかったです。

特に印象に残っているのは、2nd Stage Boss等の4k作品を手掛けたよっしんさんが「この量のコードなら4kに収まる」と仰っていたことで、驚きすぎて耳を疑いました...(25000charsもあるのに...)

個人的に話してみたいなぁと思っていた方も見かけたのですが、声を掛けることができなかったので、来年は勇気を出して声を掛けたいと思います!

今後は4kbに収まるようにコードを書く技術や、音楽を作る練習をして、次回作品をエントリーするときは4k作品をエントリーしたいです!(作品が間に合わなくても、来年もTDFを見に行きます!)

最後になりましたが、Tokyo Demo Fest 関係者の皆様、最高のイベントありがとうございました!!

おまけ

誰も見ていないだろうと、最初からパブリックなリポジトリでTraveler 2を作っていたんですが。

突然レイトレ若人にマサカリを投げられてしまいました...

github.com

心が折れたので、来年からはプライベートリポジトリで作業しようと思います!

windowsノートPC(XPS15)でイヤホンで聞いた時の音が急におかしくなった

設定をいじった記憶もないし、元に戻す設定を探しても無いしで困り果てたので

結局ドライバーを入れ直して解決した

f:id:kaneta1011:20181123011659p:plain

サウンドのドライバをアンインストール

f:id:kaneta1011:20181123011737p:plain

ハードウェア変更のスキャンで再インストール