Transcript
Page 1: シェーダだけで世界を創る!three.jsによるレイマーチング

シェーダだけで世界を創る!three.jsによるレイマーチング

2016/02/14

GPU の熱でチョコも溶けちゃう!?

GLSL シェーダテクニック勉強会

@gam0022

Page 2: シェーダだけで世界を創る!three.jsによるレイマーチング

自己紹介

細田 翔

去年まで筑波大学のCGの研究室

KLab 新卒 1年目

three.js 歴 3年目

2

@gam0022(がむ)

Page 3: シェーダだけで世界を創る!three.jsによるレイマーチング

最近、取り組んでいること

レイトレーシングをGLSLのフラグメントシェーダで実装

http://gam0022.net/webgl/

3

Page 4: シェーダだけで世界を創る!three.jsによるレイマーチング

最近、取り組んでいること

レイトレーシングをGLSLのフラグメントシェーダで実装

http://gam0022.net/webgl/

4

Page 5: シェーダだけで世界を創る!three.jsによるレイマーチング

自己紹介

細田 翔

去年まで筑波大学のCGの研究室

KLab 新卒 1年目

three.js 歴 3年目

5

@gam0022(がむ)レイマーチングのおかげで、three.js に PRがマージされた

http://threejs.org/examples/#webgl_raymarching_reflect

Page 6: シェーダだけで世界を創る!three.jsによるレイマーチング

レイマーチング?

レイマーチング

||

レイトレーシングの1種

6

Page 7: シェーダだけで世界を創る!three.jsによるレイマーチング

レイマーチングは超すごい

レイマーチングによって、コードから生成された映像

メガデモの神 iq(@iquilezles) 氏の作品

https://www.shadertoy.com/view/MdX3Rr

7

Page 8: シェーダだけで世界を創る!three.jsによるレイマーチング

レイマーチングは超すごいレイマーチングによって、コードから生成された映像

otaviogood 氏の作品 https://www.shadertoy.com/view/XljGDz

Kali 氏の作品 https://www.shadertoy.com/view/Xtf3Rn

8

Page 9: シェーダだけで世界を創る!three.jsによるレイマーチング

レイマーチングは超すごいShadertoy で raymarching タグを検索https://www.shadertoy.com/results?query=tag%3Draymarching

9

Page 10: シェーダだけで世界を創る!three.jsによるレイマーチング

レイマーチングはやばい

マインクラフトの作者もレイマーチングに注目!

http://japanese.engadget.com/2016/01/28/notch-oculus-vr-3-5kb/

10

Page 11: シェーダだけで世界を創る!three.jsによるレイマーチング

レイマーチングは面白い

超すごい映像をコードだけで動的生成できる!

マインクラフトの作者もレイマーチングに注目!

11

レイマーチングすごく面白そう!!

Page 12: シェーダだけで世界を創る!three.jsによるレイマーチング

話の流れ

1. レイマーチングのための基礎知識

2. レイマーチングのアルゴリズム解説

3. レイマーチング発展編

4. three.js の話

12

Page 13: シェーダだけで世界を創る!three.jsによるレイマーチング

話の流れ

1. レイマーチングのための基礎知識

2. レイマーチングのアルゴリズム解説

3. レイマーチング発展編

4. three.js の話

13

Page 14: シェーダだけで世界を創る!three.jsによるレイマーチング

3Dの描画方法

3Dの描画方法は大きく2種類

1. ラスタライズ法

2. レイトレーシング法

14

Page 15: シェーダだけで世界を創る!three.jsによるレイマーチング

ラスタライズ法形状を三角形(ポリゴン)の集合で表現

3Dの頂点を2Dに投影して描画

処理が軽い15

Page 16: シェーダだけで世界を創る!three.jsによるレイマーチング

ラスタライズ法形状を三角形(ポリゴン)の集合で表現

3Dの頂点を2Dに投影して描画

処理が軽い16

Page 17: シェーダだけで世界を創る!three.jsによるレイマーチング

レイトレーシング法

カメラに届く光線(レイ)を全ピクセル数シュミレーション

17

【画像出典】 http://www.vcl.jp/~kanazawa/raytracing/?page_id=1154

Page 18: シェーダだけで世界を創る!three.jsによるレイマーチング

レイトレーシング法全ピクセル数シミュレート

- 1024x768 = 786432(78万)回

処理は重い

無限に滑らか・物理現象の再現が可能

18

【画像出典】 https://sites.google.com/site/

rendering1h/

Page 19: シェーダだけで世界を創る!three.jsによるレイマーチング

レイトレーシング法全ピクセル数シミュレート

- 1024x768 = 786432(78万)回

処理は重い

無限に滑らか・物理現象の再現が可能

19

【画像出典】 https://sites.google.com/site/

rendering1h/

詳しくは レイトレーシングの専門家 hole 氏に聞いてください

edupt(パストレーシングベースの

物理ベースレンダラ)の作者

Page 20: シェーダだけで世界を創る!three.jsによるレイマーチング

2種類のシェーダ

WebGLのシェーダは2種類

1.頂点シェーダ(vertex shader)

2.フラグメントシェーダ(fragment shader)

20

Page 21: シェーダだけで世界を創る!three.jsによるレイマーチング

頂点シェーダ頂点の集合をカメラの世界を基準に座標変換

頂点ごとの処理

21

座標変換

Page 22: シェーダだけで世界を創る!three.jsによるレイマーチング

フラグメントシェーダ

ポリゴンの塗り方を決める役割

- どう陰影をつけるか?

- テクスチャを貼るか?

ピクセルごとの処理

別名: ピクセルシェーダ

22

Page 23: シェーダだけで世界を創る!three.jsによるレイマーチング

レイトレのフラグメントシェーダ実装

3Dシーンの中に1枚の板を配置

1枚の板の塗り方を決める処理として、レイトレーシングをフラグメントシェーダをつかって実装

23

Page 24: シェーダだけで世界を創る!three.jsによるレイマーチング

レイトレのフラグメントシェーダ実装

フラグメントシェーダはGPUで並列処理される

とても高速に処理できる

リアルタイム描画が可能

24

Page 25: シェーダだけで世界を創る!three.jsによるレイマーチング

レイトレのフラグメントシェーダ実装

GPUで並列処理? 難しそう…

レイトレーシングは並列性が高い手法

並列の実装は簡単

25

Page 26: シェーダだけで世界を創る!three.jsによるレイマーチング

話の流れ

1. レイマーチングのための基礎知識

2. レイマーチングのアルゴリズム解説

3. レイマーチング発展編

4. three.js の話

26

Page 27: シェーダだけで世界を創る!three.jsによるレイマーチング

ここから レイマーチングの話

Page 28: シェーダだけで世界を創る!three.jsによるレイマーチング

レイマーチングレイマーチング ∈ レイトレーシング

レイマーチングの大きな特徴は、衝突判定

- レイトレーシング

• 形状(球体・平面・ポリゴンなど)によって、衝突判定のアルゴリズムを使い分け

- レイマーチング

• 距離関数でシーンを定義し、レイを少しずつ進めながら衝突判定

28

Page 29: シェーダだけで世界を創る!three.jsによるレイマーチング

距離関数とは

距離関数

入力:3Dの座標 p (vec3)

出力:点 p からシーンの物体への 最短距離(float)

29

Page 30: シェーダだけで世界を創る!three.jsによるレイマーチング

距離関数とは

距離関数 = 最短距離

イメージしずらいので、 実例を挙げていきます

30

Page 31: シェーダだけで世界を創る!three.jsによるレイマーチング

距離関数とは

距離関数 = 点 p から、シーン中の物体への最短距離を返す関数

31

距離関数(p) = 最短距離の長さ

Page 32: シェーダだけで世界を創る!three.jsによるレイマーチング

距離関数とは

距離関数 = 点 p から、シーン中の物体への最短距離を返す関数

32

Page 33: シェーダだけで世界を創る!three.jsによるレイマーチング

距離関数とは

距離関数 = 点 p から、シーン中の物体への最短距離を返す関数

33

Page 34: シェーダだけで世界を創る!three.jsによるレイマーチング

レイマーチングのアルゴリズム

距離関数は分かったけど、どうやって衝突判定するの?

次のシーンを使って実例で説明!

34

Page 35: シェーダだけで世界を創る!three.jsによるレイマーチング

レイマーチング

レイを少しずつ進める ||

レイマーチング

35

Page 36: シェーダだけで世界を創る!three.jsによるレイマーチング

レイマーチングのアルゴリズム

レイの先端はカメラの位置からスタート

がレイの先端

36

Page 37: シェーダだけで世界を創る!three.jsによるレイマーチング

レイマーチングのアルゴリズム

レイの向きは常に真っ直ぐ

37

Page 38: シェーダだけで世界を創る!three.jsによるレイマーチング

レイマーチングのアルゴリズム

赤い線 = 距離関数の値 = レイの先端からの最短距離

一番近い位置にあるのは黄色い球体

38

Page 39: シェーダだけで世界を創る!three.jsによるレイマーチング

レイマーチングのアルゴリズム

距離関数の値だけ、レイの先端 を進める

39

Page 40: シェーダだけで世界を創る!three.jsによるレイマーチング

レイマーチングのアルゴリズム

2週目の先端 から、さらに最短距離を求める

2週目でも黄色い球体が最短

40

Page 41: シェーダだけで世界を創る!three.jsによるレイマーチング

レイマーチングのアルゴリズム

最短距離だけレイを進めて、3週目のレイの先端が求まる

41

Page 42: シェーダだけで世界を創る!three.jsによるレイマーチング

レイマーチングのアルゴリズム

3週目でも黄色い球体が最短

42

Page 43: シェーダだけで世界を創る!three.jsによるレイマーチング

レイマーチングのアルゴリズム

4週目のレイの先端が求まる

43

Page 44: シェーダだけで世界を創る!three.jsによるレイマーチング

レイマーチングのアルゴリズム

4週目で、ついに緑の箱が最短!!

44

Page 45: シェーダだけで世界を創る!three.jsによるレイマーチング

レイマーチングのアルゴリズム

レイの先端が無事に緑の箱の位置となった!!

45

Page 46: シェーダだけで世界を創る!three.jsによるレイマーチング

レイマーチングのアルゴリズム

衝突するまで(最短距離が0)になるまで、ひたすらレイを進めるだけ

最短距離だけ進めるので、進みすぎて通り抜けることはない

単純!!シンプル!!

46

Page 47: シェーダだけで世界を創る!three.jsによるレイマーチング

レイマーチングのアルゴリズム

レイマーチングのアルゴリズムの繰り返し処理

||

マーチングループ

47

Page 48: シェーダだけで世界を創る!three.jsによるレイマーチング

レイマーチングのアルゴリズム

マーチングループの実装例

48

float dist, depth = 0.0; p = origin;

for ( int i = 0; i < 64; i++ ){ dist = sceneDist( p ); depth += dist; p = origin + depth * ray;

if ( abs(dist) < EPS ) break; }

Page 49: シェーダだけで世界を創る!three.jsによるレイマーチング

レイマーチングのアニメーション

https://twitter.com/motions_work/status/694898149622550528

49

Page 50: シェーダだけで世界を創る!three.jsによるレイマーチング

スフィアートレーシング

今まで説明したのはレイマーチングの1種のスフィアートレーシング

衝突判定の過程で球(スフィアー)がたくさん出てくる

50

Page 51: シェーダだけで世界を創る!three.jsによるレイマーチング

3D描画手法の整理

ラスタライザ

レイトレーシング

-レイマーチング

•スフィアトレーシング

51

Page 52: シェーダだけで世界を創る!three.jsによるレイマーチング

距離関数の実装

Page 53: シェーダだけで世界を創る!three.jsによるレイマーチング

球体の距離関数

原点が中心の半径rの球体の距離関数

原点からの距離 - 半径r

超シンプル

53

float sphereDist(vec3 p, float r) { return length(p) - r; }

【画像出典】 http://resources.esri.com/help/9.3/arcgisengine/java/api/arcobjects/

com/esri/arcgis/geometry/ISphere.html

Page 54: シェーダだけで世界を創る!three.jsによるレイマーチング

箱の距離関数

原点にある大きさbの箱の距離関数

これも超シンプル

動作原理はややこしいので割愛><

54

float boxDist(vec3 p, vec3 b){ return length(max(abs(p) - b, 0.0)); }

Page 55: シェーダだけで世界を創る!three.jsによるレイマーチング

様々な距離関数http://iquilezles.org/www/articles/distfunctions/distfunctions.htm

最初のメガデモの作者 iq 氏のページにまとめらている

55

Page 56: シェーダだけで世界を創る!three.jsによるレイマーチング

シェーディング衝突「した・しない」の情報からはシルエットしか出せない

56

Page 57: シェーダだけで世界を創る!three.jsによるレイマーチング

シェーディング光源の向きと法線が分かれば、シェーディング(陰影効果)できるfloat diffuse = clamp(dot(light, normal), 0.1, 1.0);

57

Page 58: シェーダだけで世界を創る!three.jsによるレイマーチング

法線を求める

レイトレーシングでは、形状に応じた法線の計算が必要

- 球体の法線:交点から球体の中心を引く

- ポリゴンの法線:エッジの外積を計算

58

Page 59: シェーダだけで世界を創る!three.jsによるレイマーチング

法線を求める

レイトレーシングでは、形状に応じた法線の計算が必要

- 球体の法線:交点から球体の中心を引く

- ポリゴンの法線:エッジの外積を計算

59

Page 60: シェーダだけで世界を創る!three.jsによるレイマーチング

法線を求める

レイマーチングでは、どんな形状でも同じ計算で法線が求まる

- 球体の法線:距離関数の勾配

- 箱の法線: 距離関数の勾配

60

Page 61: シェーダだけで世界を創る!three.jsによるレイマーチング

法線を求める

レイマーチングでは、どんな形状でも同じ計算で法線が求まる

- 球体の法線:距離関数の勾配

- 箱の法線: 距離関数の勾配

61

Page 62: シェーダだけで世界を創る!three.jsによるレイマーチング

法線を求める

なるほど! 勾配!

って何だっけ?62

Page 63: シェーダだけで世界を創る!three.jsによるレイマーチング

法線を求める

63

const float EPS = 0.01; vec3 getNormal( vec3 p ) { return normalize(vec3( sceneDist(p + vec3( EPS, 0.0, 0.0 ) ) - sceneDist(p + vec3( -EPS, 0.0, 0.0 ) ), sceneDist(p + vec3( 0.0, EPS, 0.0 ) ) - sceneDist(p + vec3( 0.0, -EPS, 0.0 ) ), sceneDist(p + vec3( 0.0, 0.0, EPS ) ) - sceneDist(p + vec3( 0.0, 0.0, -EPS ) ) )); }

勾配の計算 = xyzそれぞれ少しずらして、 距離関数の差分を計算

Page 64: シェーダだけで世界を創る!three.jsによるレイマーチング

法線を求める

数学では、f(x,y,z) = 0 で表せる曲面の法線ベクトル n はn = (f’x, f’y, f’z) と fの偏微分(勾配)となると知られている

距離関数は f(x,y,z) = 0 で表せる曲面に等しい

法線n = 距離関数の勾配 として計算可能

64

Page 65: シェーダだけで世界を創る!three.jsによるレイマーチング

法線を求める

「学校で微分は傾きと習ったんだけど!?」

y = 2x のような式だと、xの微分は傾き

f(x,y) = 2x - y = 0 に変形すれば、2次元でも微分は法線

65

Page 66: シェーダだけで世界を創る!three.jsによるレイマーチング

距離関数は万能

Page 67: シェーダだけで世界を創る!three.jsによるレイマーチング

距離関数の発展編

レイマーチングでは、距離関数でシーンを定義

距離関数を制した者がレイマーチングを制す

67

Page 68: シェーダだけで世界を創る!three.jsによるレイマーチング

話の流れ

1. レイマーチングのための基礎知識

2. レイマーチングのアルゴリズム解説

3. レイマーチング発展編

4. three.js の話

68

Page 69: シェーダだけで世界を創る!three.jsによるレイマーチング

距離関数の合成

箱と球体を和集合で組み合わせた距離関数

min で各距離関数を合成

69

// 箱と球体を和集合で組み合わせた距離関数 float sceneDist(vec3 p) { return min( boxDist(p, vec3(0.9, 0.45, 0.9), 0.05), sphereDist(p, vec3(0.0, 0.45, 0.0), 0.9) ); }

Page 70: シェーダだけで世界を創る!three.jsによるレイマーチング

距離関数の合成

距離関数を min や max で合成するだけで、CSG表現が可能

70

Page 71: シェーダだけで世界を創る!three.jsによるレイマーチング

距離関数の無限の繰り返し

mod 演算 で p をループさせることで、無限に繰り返し表示できる

71

float distSphere(vec3 p, float r) { return length(p) - r; }

vec3 onRep(vec3 p, float interval) { return mod(p, interval) - interval * 0.5; }

float distScene(vec3 p) { return distSphere(onRep(p, 4.0), 1.0); }

Page 72: シェーダだけで世界を創る!three.jsによるレイマーチング

距離関数の実践編

最初の鉄骨の例

CSG表現+

無限の繰り返し

で実現可能!

72

Page 73: シェーダだけで世界を創る!three.jsによるレイマーチング

距離関数の実践編

幅widthの四角柱の距離関数

73

float barDist(vec2 p, float width) { return length(max(abs(p) - width, 0.0)); }

float sceneDist(vec3 p) { float bar_x = barDist(p.yz, 0.1); return bar_x; }

Page 74: シェーダだけで世界を創る!three.jsによるレイマーチング

距離関数の実践編

y軸の四角柱を和集合

74

float sceneDist(vec3 p) { float bar_x = barDist(p.yz, 0.1); float bar_y = barDist(p.xz, 0.1);

return min(bar_x, bar_y); }

Page 75: シェーダだけで世界を創る!three.jsによるレイマーチング

距離関数の実践編

z軸の四角柱を和集合

75

float sceneDist(vec3 p) { float bar_x = barDist(p.yz, 0.1); float bar_y = barDist(p.xz, 0.1); float bar_z = barDist(p.xy, 0.1);

return min( min(bar_x, bar_y), bar_z); }

Page 76: シェーダだけで世界を創る!three.jsによるレイマーチング

距離関数の実践編

無限の繰り返し

76

vec2 onRep(vec2 p, float interval) { return mod(p, interval) - interval * 0.5; }

float barDist(vec2 p, float interval, float width) { return length( max(abs(onRep(p, interval)) - width, 0.0)); }

p ➡ onRep(p, interval)

Page 77: シェーダだけで世界を創る!three.jsによるレイマーチング

距離関数の実践編

同様に円柱を無限に繰り返し

77

float tubeDist(vec2 p, float interval, float width) { return length(onRep(p, interval)) - width; }

float sceneDist(vec3 p) { float tube_x = tubeDist(p.yz, 0.5, 0.025); float tube_y = tubeDist(p.xz, 0.5, 0.025); float tube_z = tubeDist(p.xy, 0.5, 0.025);

return min(min(tube_x, tube_y),tube_z); }

Page 78: シェーダだけで世界を創る!three.jsによるレイマーチング

距離関数の実践編

角柱 から 円柱 を 差集合 で刳り貫くと…

78

- =

Page 79: シェーダだけで世界を創る!three.jsによるレイマーチング

距離関数の実践編

カメラの向きを変えると…

79

Page 80: シェーダだけで世界を創る!three.jsによるレイマーチング

距離関数の実践編

sceneDist全体

80

float sceneDist(vec3 p) { float bar_x = barDist(p.yz, 1.0, 0.1); float bar_y = barDist(p.xz, 1.0, 0.1); float bar_z = barDist(p.xy, 1.0, 0.1);

float tube_x = tubeDist(p.yz, 0.1, 0.025); float tube_y = tubeDist(p.xz, 0.1, 0.025); float tube_z = tubeDist(p.xy, 0.1, 0.025);

return max(max(max(min(min( bar_x, bar_y),bar_z), -tube_x), -tube_y), -tube_z); }

Page 81: シェーダだけで世界を創る!three.jsによるレイマーチング

距離関数の実践編

赤い色をつけて、遠くを明るくすると…

81

Page 82: シェーダだけで世界を創る!three.jsによるレイマーチング

距離関数まとめ

距離関数: 物体までの最短距離

以下を簡単に実現できる

- CSG表現(図形の合成)

- 無限の繰り返し

少ないコードで複雑な形状をできる82

Page 83: シェーダだけで世界を創る!three.jsによるレイマーチング

話の流れ

1. レイマーチングのための基礎知識

2. レイマーチングのアルゴリズム解説

3. レイマーチング発展編

4. three.js の話

83

Page 84: シェーダだけで世界を創る!three.jsによるレイマーチング

three.js とは

WebGLのラッパーライブラリ

http://threejs.org/

84

Page 85: シェーダだけで世界を創る!three.jsによるレイマーチング

three.js を使う理由

WebGLの初期化処理をたった数行のコードで書ける

カメラのコントロールのための ライブラリを最初から提供

85

大量のコードを書きたくない

Page 86: シェーダだけで世界を創る!three.jsによるレイマーチング

three.jsの用語

画像出典: https://html5experts.jp/yomotsu/5225/

86

Page 87: シェーダだけで世界を創る!three.jsによるレイマーチング

フラグメントシェーダ実装

シーンの中に1枚の板を配置

1枚の板の塗り方を決める処理として、レイマーチングをフラグメントシェーダをつかって実装

87

Page 88: シェーダだけで世界を創る!three.jsによるレイマーチング

three.js からシェーダを使う

1枚の板のジオメトリを作り、頂点シェーダとフラグメントシェーダを設定したマテリアルを適用するコード

88

geometry = new THREE.PlaneBufferGeometry(2.0, 2.0); material = new THREE.ShaderMaterial({ uniforms: { resolution: { type: "v2", value: new THREE.Vector2(512.0, 512.0) }, }, vertexShader: "頂点シェーダ", fragmentShader: "フラグメントシェーダ", }); mesh = new THREE.Mesh(geometry, material);

Page 89: シェーダだけで世界を創る!three.jsによるレイマーチング

three.js からシェーダを使う

Material の初期化の時に、シェーダのコードを渡すだけ

89

geometry = new THREE.PlaneBufferGeometry(2.0, 2.0); material = new THREE.ShaderMaterial({ uniforms: { resolution: { type: "v2", value: new THREE.Vector2(512.0, 512.0) }, }, vertexShader: "頂点シェーダ", fragmentShader: "フラグメントシェーダ", }); mesh = new THREE.Mesh(geometry, material);

Page 90: シェーダだけで世界を創る!three.jsによるレイマーチング

three.js vs 生WebGL

スクロールバーに注目▼

90

three.js 49行 生WebGL 215行

Page 91: シェーダだけで世界を創る!three.jsによるレイマーチング

常にthree.jsが最適か?

生WebGLを書くことで勉強になる

高度なことをしたいなら、生WebGLを使うしか無い

91

Page 92: シェーダだけで世界を創る!three.jsによるレイマーチング

常にthree.jsが最適か?

three.js勢 も 生WebGL勢 も 仲良くしよう!

92

Page 93: シェーダだけで世界を創る!three.jsによるレイマーチング

まとめ

レイトレーシングはフラグメントシェーダで高速にGPU実装できる

レイマーチングでは距離関数で衝突判定をする

- 短いコードで複雑な形状を表現できる

- CSGや繰り返しが簡単にできる

three.jsでWebGLを少ないコードで扱える

93

Page 94: シェーダだけで世界を創る!three.jsによるレイマーチング

参考資料

我らが @h_doxas 先生の資料

- https://wgld.org/d/glsl/

メガデモの神 iq(@iquilezles) 氏のページ

- http://iquilezles.org/www/index.htm

のWebフロントエンド本 (私の著書)

- https://techbooster.github.io/c89/#scriptoon2

94

Page 95: シェーダだけで世界を創る!three.jsによるレイマーチング

参考資料

これがGPUの力!Three.jsによる“リアルタイム”なレイトレーシング

- http://qiita.com/gam0022/items/03699a07e4a4b5f2d41f

これがGPUの力!three.jsによる“リアルタイム”なレイトレーシング ~宝石編~

- http://qiita.com/gam0022/items/9875480d33e03fe2113c

95

Page 96: シェーダだけで世界を創る!three.jsによるレイマーチング

資料について

資料は後日 SlideShare で公開予定

http://www.slideshare.net/shohosoda9

96

Page 97: シェーダだけで世界を創る!three.jsによるレイマーチング

END

http://gam0022.net/webgl/

97


Top Related