レイマーチングで
半歩差のつくシェーダー⼩技テクニック集
GLSL Tech Night 2018
召⽥ 敬(keim_at_si)
⼩学⽣時代︔FM7 (ベーマガ)
中〜⾼校⽣時代︔ポケコン (ポケコンジャーナル)
⼤学⽣時代︔DTM(M3・コミケ)
20代︔フリーゲーム (2ch ゲ作板/STG板)
30代︔Flasher (wonderfl)
40代︔シェーダアート (Shadertoy/GLSLfan)
keim_at_si
(⽇曜プログラマ―)
Nomltest Sky Swimmers Heaven
Quantized Blaze TSS Clipboard Player
MMLTalks / SiON Wonderfl Experiments
Who am I ?
レイマーチングでいつもよりちょっと綺麗な絵を描こう
Motivation
From https://www.shadertoy.com/user/keim
レイマーチング(スフィアトレーシング)とは︖
Background
レイトレーシングの⼀種
⽐較的計算が単純
計算過程・結果の応⽤範囲が広い
フラグメントシェーダでの実装が容易
【参考】#GLSLTech 2016
@gam0022 シェーダだけで世界を創る︕three.jsによるレイマーチング
https://www.slideshare.net/shohosoda9/threejs-58238484
Background
GPUで本当のリアルタイム・レイトレーシングが可能
【参考】#GLSLTech 2016
@gam0022 シェーダだけで世界を創る︕three.jsによるレイマーチング
https://www.slideshare.net/shohosoda9/threejs-58238484
※今年ゲーム業界でバズワード化してるリアルタイム・レイトレーシングとは別物
近年、ラスタライズとレイトレースの境界が曖昧になってるとは思うものの、
正直アレをレイトレと呼ぶのは抵抗があります...(※個⼈の意⾒です)
レイマーチング(スフィアトレーシング)とは︖
Background
【参考】#GLSLTech 2016
@gam0022 シェーダだけで世界を創る︕three.jsによるレイマーチング
https://www.slideshare.net/shohosoda9/threejs-58238484
レイマーチング(スフィアトレーシング)とは︖
- 距離関数(形状/繰り返し/変形など)の実装
- レイマーチングによるレイトレース(反射/屈折)
- 基本的なシェーディング(拡散⾯/影/AO)
【今⽇のお題】
の話はしません。
Background
⾔ったよね︖
(※お誘いありがとうございます)
モーション関数
光の表現
影と陰
アフターエフェクト
コード・リーディング
Agenda
Agenda
モーション関数
Topic: Motion
【お題】指数関数を使う︓定義
特に a がネイピア数(e=2.718..)の時、
指数関数とは
GLSL では power(a, x) 関数とexp(x) 関数がある
Topic: Motion
【お題】指数関数を使う︓定義
GLSL では power(a, x) 関数とexp(x) 関数がある、けど
power(a,x) = exp(k*x); // k = ln(a)
なので、数学的な意味を考慮しないなら、exp(x)で⼗分。
...という定義はどうでも良い
Topic: Motion
【お題】指数関数を使う︓雰囲気
モーションで関数を使う場合、意味はどうでも良い
重要なのは数字変化の「雰囲気」
指数関数の雰囲気
x<0 では
「きゅーっ」と減速・収縮
0<x では
「ぐわっ」と加速・爆発
※感じ⽅には個⼈差があります。
Topic: Motion
【お題】指数関数を使う︓特徴
モーションで関数を使う場合、意味はどうでも良い
重要なのは数字変化の「雰囲気」
y=exp(x)
- x<0 では変化が緩やか
- 0<x では変化が激しい
- -∞<0<+∞ が 0<1<+∞に変換
- 実数→正数への変換
Topic: Motion
【お題】指数関数を使う︓使い⽅
y=exp(x)y=sin(x) y=exp(sin(x))
X =
-1と+1の間を往復するsin()をexp()で変形させる
負値でゆっくり、正値で早く変化する動きに
Topic: Motion
【お題】指数関数を使う︓使い⽅
-1と+1の間を往復するsin()をexp()で変形させる
0付近で張り付いて粘るような数値変化が可能
https://www.shadertoy.com/view/MtGSWc のカメラモーション
Topic: Motion
【お題】指数関数を使う︓応⽤
モーショングラフィックスでよくある
「ヒュッと⼊って、ヒュッと出てく」
感じの動きとか再現できる(語彙⼒)
指数イージング関数
m=18
https://glslfan.com/?channel=-L0I4J2AzJUyHagNW8nW のキューブの動き
Topic: Motion
【お題】指数関数を使う︓まとめ
指数関数を使うと緩急をつけることができる
y=sin(x)
>
数字変化の「雰囲気」⼤切
y=exp(k sin(x))/exp(k)
Topic: Motion
ツール紹介
https://www.desmos.com/
ちょっとした数式の雰囲気を確認したいとき、desoms が超便利
Agenda
光の表現
Topic: Light Emission
【お題】発光体の表現
① グロウ(ブルーム)
②シャドウ(シェード)
ダイナミックレンジ以上の光を表現するために、
様々な疑似的⼿法が考案されてる(HDRレンダリング)。
グロウとシャドウがあれば、⼤体ソレっぽい。
Topic: Light Emission
【お題】発光体の表現
グロウの計算(点光源)
レイトレーシングの場合は、
視線ベクトルと光源の距離に応じて、
発光⾊を加算するだけ
k=1
k=2
k=4
distance
Emission
k値でグローの広がりを操作
Topic: Light Emission
【お題】発光体の表現
グロウの遮蔽
遮蔽体までの距離は、レイトレーシングの
過程で算出される
遮蔽体までの距離<発光体までの距離
発光⾊を加算しない
遮蔽体までの距離>発光体までの距離
発光⾊を加算する
if (hit.length > length(light.pos - pos)) {
out += emit(hit, light.pos);
}
Topic: Light Emission
【お題】発光体の表現
形状を持った物体のグロウ
レイマーチングの場合、光線追跡の時点で
既に物体までの距離を計算している。
(”d+2.0”は、物体表⾯で0除算が発⽣するための調整)
ライティング計算時に、発光物体までの距離
からグローを計算して加算
Surface emitSurface = NO_HIT;
Surface map(in vec3 p) {
Surface s = Surface(dfBox(p, ...), ...);
emitSurface = near(s, emitSurface);
return near(s, Surface(dfPln(p, ...), ...);
}
vec3 lighting(in Hit hit, in Light lit) {
emit = pow(emitSurface.d+2.0, -1.5)*emitColor;
...
光線追跡中に⼀番近づいた表⾯情報を保持しておく
保持しておいた表⾯情報から発光⾊を計算
Topic: Light Emission
【お題】発光体の表現
グロー計算でよくある失敗
×
RGB=(1, 0, 0)
〇
RGB=(1, 0.1, 0.1)
- 0は何倍しても何乗しても0
- まぶしい発光の表現=「⽩」
RGB全要素を少し⼊れないと光っぽくならない。
Topic: Light Emission
【お題】発光体の表現︓まとめ
グロウとシャドウがあれば、⼤体光る。
Topic: Light Emission
おまけ
GLSLfan のデフォルトシェーダは、Sin波線までの距離に対するグロウ計算
line += 0.0025/abs(p.y + sin(p.x*1.0+timer+offset)*0.75) * color;
Agenda
影と陰
Topic: Shading
【お題】⼤域照明︕
http://graphics.ucsd.edu/~henrik/
Cornell box images by Henrik Wann Jensen
Ray Tracing Path Tracing
もはや⼤域照明はCGにおける必修科⽬
Topic: Shading
【お題】⼤域照明︕
レイマーチングの場合、
- Soft Shadow
- Ambient Occlusion
による近似が⼀般的
リアルタイムレンダリングに向けた様々な⼿法が考案されている
ss=100/ao=0 ss=1/ao=0
ss=off/ao=0.35 ss=1/ao=0.35
無し Soft Shadowのみ
A.O. のみ Soft Shadow+A.O.
⼤域照明=無数の光源計算=無理︕
Topic: Shading
【お題】⼤域照明︕
Soft Shadow
Ambient Occlusion
綺麗...だけど、ShaderToy界隈では普遍的⼿法
Topic: Shading (Future Work)
【お題】より⾼品質な⼤域照明へ︕
Ambient Occlusion 疑似拡散反射光GI
AO計算を応⽤して拡散反射光GIを実装してみた【実装】
⼤域照明特有の「⾊にじみ」に挑戦
※⾊にじみ
近傍⾯からの拡散反射光が光源となって
うっすらと近傍⾯⾊が反映される現象
Topic: Shading (Future Work)
【お題】より⾼品質な⼤域照明へ︕
AO計算を応⽤して拡散反射光GIを実装してみた【結果】
Ambient Occlusion 疑似拡散反射光GI
めっちゃ地味
Topic: Shading (Future Work)
【お題】より⾼品質な⼤域照明へ︕
AO計算を応⽤して拡散反射光GIを実装してみた【失敗】
失敗例(光らせすぎ)
物理的に正しくないので最終的には⼿調整
疑似拡散反射光GI
Topic: Shading (Future Work)
【お題】より⾼品質な⼤域照明へ︕
AO計算を応⽤して拡散反射光GIを実装してみた【応⽤】
反射⾯なら割と⾃然 集光模様︕ (in progress)
拡散反射⾯レンダリングでの結果は地味だったが、
応⽤範囲は意外に広い、かもしれない。
Agenda
アフターエフェクト
Topic: After Effect
【お題】フォーカルブラー(マルチパス)
マルチパス・レンダリングであれば、簡単・軽量・効果的
フォーカルブラー無 フォーカルブラー有
Topic: After Effect
【お題】フォーカルブラー(マルチパス)
fragColor = vec4(render(ray), min(abs(hit.distance-focus)/100., 1.));
レンダリング結果
vec3(R,G,B)
焦点距離を0とした衝突表⾯までの距離を
0~1の範囲に正規化
1枚⽬︔レンダリング結果(RGB値)と、w値にレイトレースした距離を⼊れておく
2枚⽬︔1枚⽬から受け取ったw値に応じてサンプルポイントをずらした4点で平均化
void mainImage(out vec4 fragColor, in vec2 fragCoord) {
vec4 col = getRenderdTexture(fragCoord);
vec2 b = vec2(col.w * BLUR * iResolution.x, 0);
fragColor = gamma((col + img(fragCoord+b.xy)
+ img(fragCoord-b.xy)
+ img(fragCoord+b.yx)
+ img(fragCoord-b.yx))/5.);
}
Topic: After Effect
【お題】フォーカルブラー(シングルパス)
シングルパス・レンダリングでも可能
ただし、簡単だけど軽量じゃない
フォーカルブラー無 フォーカルブラー有
Topic: After Effect
【お題】フォーカルブラー(シングルパス)
void mainImage(out vec4 fragColor, in vec2 fragCoord) {
vec4 col = render(ray);
col += render(blur_ray(ray, camera, LENS_BLUR)));
col += render(blur_ray(ray, camera, -LENS_BLUR)));
fragColor = gamma(col/3.);
}
// blur_ray() ... ターゲットを中⼼にLENS_BLUR分回転
焦点を合わせるターゲットを中⼼に
カメラを少し移動してレンダリングした
結果を平均化する。
⇒ 平均化分だけ全画⾯のレイトレが必要
Topic: After Effect
【お題】錯視⽴体
Shadertoy でもまだまだ未開の地
アイディア勝負なのでワンチャンあるかも
Topic: After Effect
【お題】錯視⽴体
近い物体の⼿前に遠い物体を描画したい
→遠い物体が描かれる場所で近い物体を描かなければよい
trickflag = 0
trickflag = 1
trickflag = 0
最初に、
⼿前に表⽰したい物体のみをレイトレースし、
描画される範囲でフラグを⽴てる
レンダリングの際、
フラグが⽴っている範囲のレイトレースでは、
近い物体は衝突判定しない(無いものとして扱う)。
trickflag = 1
①
②
③
Topic: After Effect
まとめ
フォーカルブラー 錯視⽴体
ブルーム+モーションブラー ⾛査線エフェクト
今回紹介できなかったが、ほかにも多種多様なAfter Effectがある。
After Effect はアイディア勝負
Agenda
コード・リーディング
Topic: Code Reading
【お題】神々の⾔葉を読む
Shadertoy/GLSLfan は宝の⼭
でも
GLSLのコードは、ものすごく読みにくい
1シェーダをしっかり読み解くだけで、割とガチWizard級の知識が得られる。
(※個⼈の感想です)
Topic: Code Reading
【お題】神々の⾔葉を読む
- 後付けで関数定義ができない
- 1ページで1シェーダが完結
- 命令がプリミティブ
- パラメータをハードコーディング
- 変数名に略称を⽤いることが多い
- こなれた英語コメントが多い
- そもそもコメントが少ない
- 書き捨てコードが多い
- コーダーのクセがすごい
GLSLのコードは、ものすごく読みにくい
...LL⾔語脳には⾟い
Topic: Code Reading
【お題】コードを読む①︔流れと位置
〇 後付けで関数定義ができない
⇒ 定義は必ず呼び出し前
- 処理順は下から上になる
- 汎⽤的に使う定数・関数は上⽅
- 結果、距離関数は真中付近
の事が多い
(※ ただし関数定義をせずに、
インラインで記述してる事も多々)
エントリポイント⇒
レンダリング⇒
ライティング⇒
光線追跡⇒
距離関数⇒
汎⽤関数⇒
構造体定義⇒
定数定義⇒
関数の処理順は
下から上
Topic: Code Reading
レイマーチングの場合、
処理の⼤まかな構造は変わらない
それぞれの処理ブロックを意識する
【お題】コードを読む②︔全体構造把握
エントリポイント⇒
レンダリング⇒
ライティング⇒
光線追跡⇒
距離関数⇒
汎⽤関数⇒
構造体定義⇒
定数定義⇒
レンダリング(⾊付け)
(ライティング・シェーディング)
↓
光線追跡
↓
距離関数
Topic: Code Reading
【お題】コードを読む③︔クセがすごい
エントリポイント⇒
レンダリング⇒
ライティング⇒
光線追跡⇒
距離関数⇒
汎⽤関数⇒
構造体定義⇒
定数定義⇒
- 変数名に略称を⽤いることが多い
- こなれた英語コメントが多い
- そもそもコメントが少ない
- 書き捨てコードが多い
- コーダーのクセがすごい
⇒ ⼀神教に⼊信する
(リーディングする対象を1~数名に絞る)
基本的には、好みのシェーダを書く⼈で良いが、
略称が多くコメントが少ない書き捨てをする⼈は
あんまり適切ではない。 (例︔keim_at_si)
Topic: Code Reading
【お題】コードを読む④︔英語
エントリポイント⇒
レンダリング⇒
ライティング⇒
光線追跡⇒
距離関数⇒
汎⽤関数⇒
構造体定義⇒
定数定義⇒
- 専⾨⽤語のボキャブラリが増える
- 関数の機能がシンプルになる
- コメント特有の⾔い回しに慣れる
- 割とフランクにアドバイスがもらえる
⾃分のコードのコメントを英語で書く
「コンパイルクラッシュするんだけど」みたいな事書いて投稿したら、
iq⽒からOpenGLのマニアックなバグについて超丁寧なアドバイスをもらった
Topic: Code Reading
まとめ
Shadertoy/GLSLfan は宝の⼭
1シェーダをしっかり読み解くだけで、割とガチWizard級の知識が得られる。
レイマーチングで
半歩差のつくシェーダー⼩技テクニック集
GLSL Tech Night 2018
https://twitter.com/mike_at_cg
https://twitter.com/keim_at_si
https://www.shadertoy.com/user/keim

GLSLtech2018 レイマーチングで半歩差のつく小技集