TokyoDemoFest2015

GLSL Graphics Compoのレイマーチング向けのチュートリアル



Tokyo Demo Fest 2015 (http://tokyodemofest.jp/2015) の
GLSL Graphics Compoのチュートリアルです。
今回はレイマーチングに特化して解説したいと思います。
予備知識としてGLSLがある程度触れると嬉しいです。
よろしくお願いします。





コンポについて補足

GLSL Graphics CompoはTokyoDemoFestとしては2回目です。

今年は「7Line(行)」の制限はありません。
よって、自由に何行でもGLSLを書くことができます。
※もちろん7Lineの作品を投稿しても大丈夫です。

Compo作品の上映時間ですが、必要であれば指定することができます(30[s]など…)
投稿する際にコメントを書く欄があるので、
そちらに記載して上映時間を設定することができます。
尚、作品上映中は具合の良いBGMが流れます。
※不明点は会場で聞いてみてください。

またリモートで投稿したい方はオーガザイナーの方に
事前にメールだったりtwitter等でご連絡いただけると嬉しいです。
※制限時間内であれば飛び入りでコンポ登録しても大丈夫です。ぜひ。

GLSL Sandboxの使い方

前年度のTokyoDemoFest 2014のチュートリアルが参考になります。
綺麗にまとめられております。
http://tokyodemofest.jp/2014/7lines/index.html




去年の作品

さっそく、TokyoDemoFest2014 7Line GLSL Compoの作品をみてみましょう。
※順不同です。

fading-heart by mwilson




Flash! by gyabo(ocha)



Doki-doki heart by nikq(club)



My1stDemo by なか-chan@最愛のiMac



FakeAOBench by ao-kun



Glitch07 by muuuuuuuuuuuuusk(brainstorm)



munyu_munyu by 0x4015



non title by chibaf



輝きエフェクト by hamuha



raytrace tutorial in 7 lines by ando(none)



Seven circles in seven lines by shinh



stalking comet by maah_booh



stripe by akegure



Surfing on sine waves by Wkter



ぐにょぐにょ by hole by n.h.k



universe by ando(none)



caress by yolp



Lemonade by Pentan



TDF4 by mitsuman


このような素晴らしい作品が会場で上映されました。
改めて観てみるとすごいですね…
みんなリアルタイムできびきびと動きます。

海外からの作品投稿もありました。
全部GLSL + 四角ポリゴン一枚です(GLSL Sandboxはbackbufferが使えます)

上記の作品は去年の作品です。
したがって7Line(7行)の制限があります。

7行のGLSLでこれだけの表現ができます。すごいですね。







レイマーチングってなんぞ?


手前みそですが、2012年のTokyoDemoFestで発表したスライドを以下に記載します。


レイマーチングでDistanceFieldする為のひな形

さあ、スライドを観たので大体考え方はばっちりなのではないでしょうか。
普段ポリゴン出したりしてモデリングしている方は、今こそ2ポリで何か描画する面白いShaderの書き方を
覚えると面白かもしれません。


今回はレイマーチングがテーマです。
スライド観たけど、どこから手をつけたらいいのかわからない、
と、思われている方は多いんじゃないでしょうか。
FragmentShaderを書いたことがある方は、

・画面一枚に大きな板ポリがあって
・UVが0~1まで取れる
という制限さえ知っていればOKです。

以下にGLSL Compo向けに2D, 3D用のひな形を作ってみました。
※もちろん秘伝のソースでもOKです!
■2D用■
        #ifdef GL_ES
        precision mediump float;
        #endif

        uniform float time;
        uniform vec2 mouse;
        uniform vec2 resolution;

        void main( void ) {
        	vec2 pos = ( gl_FragCoord.xy / resolution.xy ) * 2.0 - 1.0;
        	gl_FragColor = vec4(pos.xy, 1.0, 1.0);
        }
        
■3D用(SphereTracingでのdisance fieldでの形状描画)■
ちょっと長いですが、説明を記載しました。
         #ifdef GL_ES
         precision mediump float;
         #endif

         uniform float time;
         uniform vec2 mouse;
         uniform vec2 resolution;

         //反復回数(constで書く方も多い)
         #define ITE_MAX      45

         //t更新時の適当な係数。通常1で大丈夫です。
         //複雑な形状だったりレイ突き抜けるような小さいオブジェクトは値を0.25位にすると良いです
         #define DIST_COEFF   1.00

         //打ち切り係数
         #define DIST_MIN     0.01

         //t最大
         #define DIST_MAX     1000.0

         //形状を記述していきます
         //参考 : http://www.iquilezles.org/www/articles/distfunctions/distfunctions.htm
         float map(vec3 p) {
         	//t初期値
         	float t = DIST_MAX;
         	float w = 0.0;

         	//球を出します(直径1.0)
         	w = length(p) - 0.5; 
         	t = min(t, w);

         	return t;
         }


         void main( void ) {
         	//-1 ~ +1のテクスチャUVを作る
         	vec2 uv = ( gl_FragCoord.xy / resolution.xy ) * 2.0 - 1.0;
         	
         	//視点ベクトルを作成する。
         	float aspect = resolution.x / resolution.y;
         	vec3  dir = normalize(vec3(uv * vec2(aspect, 1.0), 1.0));
         	
         	//eye座標
         	vec3 pos = vec3(0.0, 0.0, -1.0);
         	
         	//map関数で定義した形状を反復法で解きます。ここではt初期値は0にしとく
         	float t = 0.0;
         	
         	//SphereTracing。ここintersectって名前で別に作る人も多いです
         	for(int i = 0 ; i < ITE_MAX; i++) {

         		//形状表現した陰関数を反復しながら解く
         		//0(DIST_MIN)に近くなったら解に近いので打ち切り
         		float ttemp = map(t * dir + pos);
         		if(ttemp < DIST_MIN) break;
         		
         		//tを更新。DIST_COEFFは複雑な形状を描画する際に小さく為につけています。
         		//ちょっとずつレイを進める事ができます。
         		t += ttemp * DIST_COEFF;
         	}
         	
         	//option形状の近くの位置を出しておく
         	vec3 ip = pos + dir * t;
         	
         	//色を作ります。ここでは進めたtの位置(深度)をただ出力するだけ
         	vec3 color = vec3(t);
         	
         	//最後に色をつけておしまいです
         	gl_FragColor = vec4(color, 1.0);
         }

        
        
↑の表示例は以下です。



GLSL Sandboxにも他の方が書かれた同じような仕組みが一杯あります。
やっていることはmap関数に現在の視点ベクトルの先端(いわゆるレイ)を、
表示したい形を定義した陰関数(の集まりとの距離)を反復法で解いていきます。

レイの飛ばし方は作られている方々や表示したいオブジェクトによって工夫が見られます。
1段目レイ飛ばすのをざっくり飛ばして(いい加減な小さいオブジェクトは無視する)、2段、3段に分けて
レイを飛ばす回数をなるべく減らしたり、
多少離散的になって荒くなっても良いのでスピードを出すようにレイの突き出し方を工夫しているものもあります。
リッチなレイの飛ばし方は、ShaderToyの作品群が参考になると思います。
有名なdemoですと、receptor by TBCの最初のシーンです。
視点ベクトル位置を小さくランダムに決めているのか、四角いオブジェクトの端の部分が若干ノイジーですが、
遠くまでレイが飛んでいるのがすごいのと、スピードがあるdemoなのでノイズなんてそんなに気になりません。




map関数でよく使われるをまとめてみました

適当に目的別に並べてみました。

[plasmaなテクスチャが欲しい]

基本中の基本、以下から作ってみてください(GLSLSandboxは簡単なUV操作ができる状態からStartします)
http://glslsandbox.com/e





[無限の床を描きたい]

平面の方程式を考えます。ax + by + cy + d = 0
これをそのままmap関数に入れちゃいます。
※(t + dir + pos)のレイの位置を(x,y,z), 法線ベクトル(a,b,c))
参考 : Ray tracing primitives
        float map(float p)
        {
          return 1.0 - dot(p, vec3(0.0, 1.0, 0.0));
        }
        

ソース(GLSL Sandbox向け)
床ができました。map関数はシンプルですね。






[作った床に模様を描きたい]

お試しで、plasmaを出したときの関数を丸ごとtexture関数にします(GLSL Sandboxのデフォルトのを使ってみます)
uvは、今回はy方向の法線で定義した床なので、衝突した点のxzでuvを取ります。
http://glslsandbox.com/e
        //http://glslsandbox.com/e
        vec3 texmain(vec2 position) {

        	//vec2 position = ( gl_FragCoord.xy / resolution.xy ) + mouse / 4.0;
        	position += mouse / 4.0;

        	float color = 0.0;
        	color += sin( position.x * cos( time / 15.0 ) * 80.0 ) + cos( position.y * cos( time / 15.0 ) * 10.0 );
        	color += sin( position.y * sin( time / 10.0 ) * 40.0 ) + cos( position.x * sin( time / 25.0 ) * 40.0 );
        	color += sin( position.x * sin( time / 5.0 ) * 10.0 ) + sin( position.y * sin( time / 35.0 ) * 80.0 );
        	color *= sin( time / 10.0 ) * 0.5;

        	return vec3( color, color * 0.5, sin( color + time / 3.0 ) * 0.75 );
        }
        

ソース(GLSL Sandbox向け)
床に模様がつきました。






[柱を描きたい]

球の応用です。たとえばy方向にのびる柱を描きたい場合、
球のmap関数から、y方向の制限を外します。
※この場合は視点からみて上下(タテ)方向に無限遠にします。
        float map(float pos)
        {
          return lenght(pos.xz) - 0.5;
        }
        

ソース(GLSL Sandbox向け)
床と柱が出ました。

★ここで複数のオブジェクトを混ぜる場合、レイが最も近いオブジェクトに当たっていないとダメなので、
tの最小値をmapから返すようにします。
        float map(vec3 p) {
        	//t初期値
        	float t = DIST_MAX;
        	float w = 0.0;

        	//柱を出します
        	w = length(p.xz) - 0.5;
        	t = min(t, w);
        	
        	//床を出します
        	w = 0.1 + dot(p, vec3(0, 1, 0));
        	t = min(t, w);
        	
        	return t;
        }
        






[glslだけでperlinノイズを出してみたい]

以下に簡単なperlinノイズ関数があります。ありがたく使います。

引用 : Raymarching Beginners' Thread(pouet.net)
        #define pi 3.14159265
        float perlin(vec3 p) {
        	vec3 i = floor(p);
        	vec4 a = dot(i, vec3(1., 57., 21.)) + vec4(0., 57., 21., 78.);
        	vec3 f = cos((p-i)*pi)*(-.5)+.5;
        	a = mix(sin(cos(a)*a),sin(cos(1.+a)*(1.+a)), f.x);
        	a.xy = mix(a.xz, a.yw, f.y);
        	return mix(a.x, a.y, f.z);
        }
        
以下のように使ったりします(ここでは球にノイズを加えています)
        float map(vec3 p) {
        	//t初期値
        	float t = DIST_MAX;
        	float w = 0.0;

        	//球を出します(直径1.0)
        	w = length(p) - 0.5 + perlin(p * 7.0) * 0.1; 
        	t = min(t, w);

        	return t;
        }
        
結果は以下の通りです。

ソース(GLSL Sandbox向け)

perlinnoise系は応用範囲がとても広いです。






[四角い板を出してみたい]

球の応用です。ここではx=0.5, y=0.01, z=0.5で、ちょっとだけ角が丸い板を出してみます。
        float map(vec3 p) {
        	//箱を作ってみます
        	w = length(max(abs(p) - vec3(0.5, 0.01, 0.5) , 0.0)) - 0.01;
        	t = min(t, w);
        	return t;
        }
        

ソース(GLSL Sandbox向け)

球と床と混ぜて、視点のアングルをrotate関数で変えて動かしてみました。
glslsandboxはtimeというuniform変数があるので、
積極的にオブジェクトを動かしてみると良いかもしれません。







チュートリアルは終わりです


ここまで出てきたのをまとめると、

・球が表示できる
・板が表示できる
・perlin noiseで何かできる
・プロシージャルテクスチャを適当に貼れる
・床が作れる
・timeで動かせる


ムムッ、何となく、寿司が作れそうですね。



3D系は海外のShaderToy, GLSLSandboxをみるとワンサカ出てきます。
面白そうなShaderを組み合わてみると、色々発見があると思います。



終わりに


GLSL Sandboxで遊んでいて、変なのができた、すごいのできたぜ!などありましたら、
宜しければ是非、お気軽にCompo投稿してみてください。
会場のデカイスクリーンに自分の作品が映るのはとても楽しいです!






参考文献、サイトまとめ

http://glslsandbox.com/
https://www.shadertoy.com/
http://mathworld.wolfram.com/Plane.html
全能感UP! GLSLで進めレイマーチング
http://acko.net/files/fullfrontal/fullfrontal/wdcode/online.html
Qiita : ランダムな値を返す関数 on GLSL
ShaderToy : [2TC 15] Mystery Mou...
Ray tracing primitives



2015,@gyabo