見出し画像

シェーダー勉強内容(4月、5月)

はじめに

今年の1月から勉強して6月になったので勉強したことをまとめておきます。

最初の3ヶ月分はこちらです。

ほぼ毎日シェーダーを勉強してるのでやったことの時系列がぐちゃぐちゃだったりします。

4月

NEORT
NEORTというデジタルアートを掲載できるサイトでいろんな人がシェーダーで絵を描いていたりして、リアルタイムで絵が変わる作品を見ていて自分も書きたいと思ったので主にNEORTに投稿できそうなシェーダーを作成することを意識して勉強しました。

よって4月はGLSLのフラグメントシェーダーをメインで勉強しました。

目標を自分で作ってそれを達成するようなシェーダーを書きました。

・複数のオブジェクトを別々に動かす
・緩急をつけた移動
・座標を色に変換

の三つです。座標を色に変換するのは簡単で、

 // raymarch
vec3 col = vec3(0.0);

for(int i = 0; i < 128; i++)
{
   float d = map(cur);
   if(d < 0.0001)
   {
       vec3 normal = calcNormal(cur);
       float diff = dot(normal, lightDir);
       float amb = 0.5 + 0.5 * dot(normal, vec3(0.0,1.0,0.0));
       col = vec3(diff) * vec3(pos, gl_FragCoord.z) * amb + vec3(0.3);
       break;
   }
   cur += ray * d;
}

gl_FragColor = vec4(vec3(col), 1.0);

座標を色に入れればできるので問題なかったです。

しかし緩急とオブジェクトを別に動かすのが厄介でこの計算式を作るのに2日ほどかかったと思います。Twitterでシェーダーやってる人に助けてもらったり学校の先生にアイデアをもらったりして何とか出来ました。

{
   	vec3 q = pos - vec3(0.0, -0.4, 0.0);
   q.xz *= rot((sqrt(1.0 - pow(fract(time / 2.0 * PI) - 1.0, 2.0)) * 2.0 * PI) * t);

   /*
   新しい関数(Graphtoy)でチェック
   sqrt(1.0 - (fract(x / (2.0 * PI)) - 1) ^ 2.0) * 2.0 * PI
   */
   
   float d1 = box(q - vec3(0.0, -0.75, 0.0), vec3(1.0));
   float d2 = octahedron(q, 1.0);
   float dt1 = subtraction(d1, d2);
   d = min(d, dt1);
}
q.xz *= rot((sqrt(1.0 - pow(fract(time / 2.0 * PI) - 1.0, 2.0)) * 2.0 * PI) * t);

ここが2日かかった計算式です。

愛用している計算からグラフを描けるサイトでどんな計算式を作ればいいのか考えました。上の2日かかった計算式はこんなグラフになります。

画像3

動きのあるものは作れましたが同じことを繰り返しているだけなのでつまらないと思い、毎秒数字を出すシェーダーを作りました。

完成自体はしましたがコードが最悪になっていて、

float map1(vec2 pos)
{
   float d = 1e10;
   float offset = 0.05;
   
   float d3 = ve(vec2(pos.x - 0.3, pos.y - 0.3));
   float d6 = ve(vec2(pos.x - 0.3, pos.y + 0.3));
   
   d = d3 + d6;
   return d;
}

float map2(vec2 pos)
{
   float d = 1e10;
   float offset = 0.05;
   
   float d1 = ho(vec2(pos.x, pos.y - 0.6));
   float d3 = ve(vec2(pos.x - 0.3, pos.y - 0.3));
   float d4 = ho(pos);
   float d5 = ve(vec2(pos.x + 0.3, pos.y + 0.3));
   float d7 = ho(vec2(pos.x, pos.y + 0.6));
   
   d = d1 + d3 + d4 + d5 + d7;
   return d;
}

float map3(vec2 pos)
{
   float d = 1e10;
   float offset = 0.05;
   
   float d1 = ho(vec2(pos.x, pos.y - 0.6));
   float d3 = ve(vec2(pos.x - 0.3, pos.y - 0.3));
   float d4 = ho(pos);
   float d6 = ve(vec2(pos.x - 0.3, pos.y + 0.3));
   float d7 = ho(vec2(pos.x, pos.y + 0.6));
   
   d = d1 + d3 + d4 + d6 + d7;
   return d;
}

float map4(vec2 pos)
{
   float d = 1e10;
   float offset = 0.05;
   
   float d2 = ve(vec2(pos.x + 0.3, pos.y - 0.3));
   float d3 = ve(vec2(pos.x - 0.3, pos.y - 0.3));
   float d4 = ho(pos);
   float d6 = ve(vec2(pos.x - 0.3, pos.y + 0.3));
   
   d = d2 + d3 + d4 + d6;
   return d;
}

こうやってすべての数字をあらかじめ作っておいて、それを

void main(void)
{
   vec2 pos = (gl_FragCoord.xy * 2.0 - resolution.xy) / min(resolution.x, resolution.y);
   
   float t = time;
   t = mod(t,10.0);
   
   vec3 c0 = vec3(map0(pos)) * step(0.0, t) * (1.0 - step(1.0, t));
   vec3 c1 = vec3(map1(pos)) * step(1.0, t) * (1.0 - step(2.0, t));
   vec3 c2 = vec3(map2(pos)) * step(2.0, t) * (1.0 - step(3.0, t));
   vec3 c3 = vec3(map3(pos)) * step(3.0, t) * (1.0 - step(4.0, t));
   vec3 c4 = vec3(map4(pos)) * step(4.0, t) * (1.0 - step(5.0, t));
   vec3 c5 = vec3(map5(pos)) * step(5.0, t) * (1.0 - step(6.0, t));
   vec3 c6 = vec3(map6(pos)) * step(6.0, t) * (1.0 - step(7.0, t));
   vec3 c7 = vec3(map7(pos)) * step(7.0, t) * (1.0 - step(8.0, t));
   vec3 c8 = vec3(map8(pos)) * step(8.0, t) * (1.0 - step(9.0, t));
   vec3 c9 = vec3(map9(pos)) * step(9.0, t) * (1.0 - step(10.0, t));
   
   vec3 col = vec3(c0) + vec3(c1) + vec3(c2) + vec3(c3) + vec3(c4) + vec3(c5) + vec3(c6) + vec3(c7) + vec3(c8) + vec3(c9);
   
   gl_FragColor = vec4(col , 1.0);
}

正しいかわからないstep関数で計算して出力しています。今でも思いますがstep間違ってる気がします。まぁ学校の課題でもないのでとりあえず完成を目標にしました。

一秒ごとに変わって面白いです。今のところ自分の中で一番気に入ってるシェーダーです。

VJ
後はVJが気になっていろいろ調べました。DJの後ろできれいな模様が音に合わせて動いてるアレをやってるのがVJです(たぶん)

VJするにはそれ用のソフトがあってそれを使うらしいのですが自分がやりたかったのは

・コードベースで素材を作りたい。ノードベースはやりたくない。
・本気でやるわけじゃないから無料がいい
・MIDIキーボードとかつなげてパラメータを調整したい
・音に合わせてシェーダーが動くようにしたい

があり、TouchDesignerというソフトが要件を満たしている気がしたのですがノードベース(コードベースできるのかもしれないが)だったのでインストールして終わりました。

でもVJ的な何かしたいという夢?は冷めなかったのでUnityを使ってそれらしい何かを作ることを試みました。なければ作るって方法ですね。

Unityを使えば

・Cg/HLSLを使ってコードベースで素材を作れる
・Unityは無料!
・頑張ればMIDIキーボード繋げれる
・音の解析たぶんできる

要件を満たすので頑張ったのですが…

UnityのC#からシェーダーのパラメータは参照できますがシェーダーに書いてある内部の計算式を変更することはできないので(+,-変えれる計算式を組み込めばいいけど)作るのをやめました。

自分の作りたかったようなものをUnityで作ってる人がTwitterで見つけたのですごくわくわくして見てます。

現状シェーダー:C#を9:1で勉強してるのでまずいです。シェーダーの勉強も程々に…

後は友達にカメラの画像を合成するシェーダーが作れないかと聞かれて爆速で作りました。

fixed4 frag (v2f i) : SV_Target
{
   fixed4 mask = step(_Mix, i.uv.x);

   fixed4 col1 = tex2D(_MainTex, i.uv) *(1.0 - mask) ;
   fixed4 col2 = tex2D(_SubTex, i.uv) * mask;

   fixed4 colMix = fixed4(0.0, 0.0, 0.0, 0.0);

   colMix = col1 + col2;
   return colMix;
}

単純だと思います。頼まれた瞬間にコードが頭の中に出てきて『オレ天才じゃん』って思いました。

計算式とか

画像2

acos(-1)するとπが出てくることを知りました。偉い人のシェーダーを眺めてたら見つけました。

#define PI 3.14159

って書くのとどっちが処理がいいのかわからないですが数学的に面白いなと感じました。

5月

日本ゲーム大賞が迫っていてシェーダーどころじゃないのにシェーダー触ってました。(以後気を付けます)

NEORT

NHKで龍の歯医者を見れる機会があったので見ました。そしたらOPとEDで網掛け模様が動いている背景映像があってシェーダーで作れそうだったので挑戦しました。

頑張った!って思ったポイントは

float lineCross(vec2 pos)
{
   // horizontal
	float h = step(0.0, pos.x) * step(0.95, 1.0 - pos.x);
   
   // Vertical
   float v = step(0.0, pos.y) * step(0.95, 1.0 - pos.y);
   return clamp(h + v, 0.0, 1.0);
}

の最後のreturn分です。ここで白色の網掛け模様を作ってますがclampしないと縦と横の線が交わった部分が1.0を超えた値になってしまうので1.0に無理やり調整しました。

あとはマスクを画像としてメタボールを使いました。メタボールは始めてだったでの調べて作りました。

ほかの言語に手を出す

いつもならNEORTでGLSLでフラグメントシェーダーを描くかUnityで頂点とフラグメントシェーダーを描くかしかしてないのですがNEORTだったりShadertoyで書いているともしそのサイトがなくなった時に自分の書いた絵が消えてしまうと困るなと思い、何を思ったのかWebGLを触り始めます。

ですがポリゴンを生成するのに200行ほどプログラムを書き、さらにJavaScriptは習ってなくてよくわからなかったのでポリゴンを出すだけで終わりました。

もっとほかのに手を出す…
NEORTで作品を見ているとGLSLで書いてある作品もあればHTML&CSS&JavaScriptが多い気がしたのでいろいろ調べる旅に出ました。

自分の目的はプログラムでジェネラティブアートを描きたいのでNEORTに投稿されているような細い曲線がいっぱい書いてあるような絵を描けるようになりたいです。

画像3

こんな感じの絵です。

調べたところJavaScriptとThreejsというのを使うとコードベースで図形を表示させたりできるようなので手を出しました。Threejsはなんだかウェブぺージの背景のいい感じな図形のところに使われてたりするみたいです。

使ってみたのですが今までキューブを描画するにはレイマーチングで長々と書いていたのに一行でキューブが出てしまって不思議な感じです。具体的には

・決められた形しか描画できないのか?SDF使えば何でも作れるのかもしれないが…
・シェーダーも決められてて一行書けば割り当てられる
・上記二つに自由度があるのかわからない

のでキューブを出して終わりました。

シェーダーに関しては自分で作ったシェーダーを割り当てれるのは知ってるのですがHTML&CSS&JavaScriptでどうやってそれを組み込めばいいのかわからなくて詰みました(HTML&CSS&JavaScript全部わからない)

あとNEORTに投稿したいのですがHTML&CSS&JavaScriptは投稿できてもThreejsを使ってる場合投稿できるのかわからなくてやってないです。

WebGL、Threejsさらに…

ほかに似たのにprocessingというのがあります。気になってたので調べてみようと思いとりあえず円だけ描画しました。

processingはVSCodeでうまく出力できなくてやるのを中断しています。わかる人にわかるように書くと最後の出力だけがうまく行ってないです。コーディングだけVSCodeで書いて最後のコンパイル?を専用のを使ってやろうと思考中です。

WebGL、Threejs、processingさらに…

6月締め切りの日本ゲーム大賞があるのにまだシェーダー触ってました。気をつけてください。

チーム制作でUnityのURPを使うことになりました。浅い知識ですがURPになると今までのシェーダーは全部使えないと聞いたので勉強を開始しました。

URP、かなり難しかったです。結論から言うと触りたくないなと思いましたし、今までのシェーダーでいいかなと思ってます。

画像4

手始めにアウトラインを作りましたが今までのシェーダーと違う点が山ほどあります。

#include "Packages/com.unity.render-pipelines.universal/ShaderLibrary/Core.hlsl"
Tags
{
   "LightMode" = "UniversalForward"
}
CBUFFER_START(UnityPerMaterial)
CBUFFER_END
VertexPositionInputs vertex = GetVertexPositionInputs(v.positionOS.xyz);

これが今までになかったり名前が変わった部分です。具体的に説明はしないですがこれらについてちゃんと説明している日本語記事が全くなく、海外のサイトをもとにして作りました。

名前が変わったりコード量が増えたりするのは構わないのですが

画像5

このシールドなシェーダーを書こうと思ったときに外側の線を描くだけなら簡単なのですが赤い箱と接触した部分にも線を引く処理をURPのコードベースで作れなくURPをやめて今までのシェーダーで描いたらうまく行きました。

URPを使うとノードベースのシェーダーグラフを有効活用することができ、URPの利点はそこなんじゃないかなと感じてますが(それ以外にもいいことはある)でも私はノードベースのシェーダーは書きたくないので今までのシェーダーの勉強に戻ることにしました。

計算式とか

フレネルの式を勉強しました。これは上のシールドシェーダーを描くために必要な知識だったので気になったので調べました。

画像6

Wikipediaのここが私に必要な知識でした。ここ以外はわからなくはないですが謎です。でもこれが成り立つためにスネルの法則ってのがあるんだなと知りました。

見た記事

たぶんUTSを開発した人だと思います。この記事にあるNPRシェーダーを描いてみたいなと感じたり

ここにある法線情報をもとにしたいい感じの色になるシェーダーを書いてみたいなと感じました。

今後自分のゲームに使えるような汎用性のあるような?シェーダーを製作するつもりです。

まとめ

4月はシェーダーいっぱい書いて5月はいろいろなものに手を出しました。

シェーダーやプログラムで何したいかというとやっぱりジェネラティブアートを描きたいってのが大きいです。

でもゲーム作る学校にいて層がメインなのでゲームに使えるシェーダーを書けるように勉強します。

今は頂点カラーを使って色を出すシェーダーを書く予定です。単純な色で構成された3Dゲームを作りたく、オブジェクトもアセットストアからではなくモデリングして使いたい、しかしUV展開するほどのものではないので頂点カラーでまとめて単色的なゲーム画面が作れないかと思考中です。

パズルゲーム的な何かを作ってタイトル画面でレイマーチングで不思議な形が動いていたら見栄えが面白いんじゃないかなと考えてもいます。

ジェネラティブアートを描きたいという夢は消えませんね。シェーダーを3Dで表示したら面白いんじゃないかと思いLooking Glassも買いましたし…

いいなと思ったら応援しよう!