--- title: Three.jsのGPGPUのサンプルが難しすぎるから解体して勉強してみる tags: JavaScript WebGL GLSL three.js author: uma6661 slide: false --- ## 概要 本エントリーはGPUパーティクルを飛ばすために筆者が勉強した軌跡です。 ## 本エントリーの対象者 * シェーダーが何なのかは知ってる * ちょっとくらいならGLSL書いたことある * だけどGPUに演算をさせるのはやったことない * サンプルのスクリプトがわけわからん こんな人を想定して書いてます。 GLSLについての基本中の基本については解説しません。 そこらへんがまだわからない方は本エントリーについてはさらっと目をとおして、 初心者向けの学習資料を読み漁ってください! ## GPGPUでパーティクルたくさん飛ばしたいからサンプルを覗いてみる ### three.jsの素晴らしいサンプル達 ![screencapture-threejs-org-examples-webgl_gpgpu_birds-html-1481813036938.png](https://qiita-image-store.s3.amazonaws.com/0/54225/b775b6b8-35aa-bb04-b4d8-1fe542d87806.png "screencapture-threejs-org-examples-webgl_gpgpu_birds-html-1481813036938.png") https://threejs.org/examples/webgl_gpgpu_birds.html ![screencapture-threejs-org-examples-webgl_gpgpu_protoplanet-html-1481813042713.png](https://qiita-image-store.s3.amazonaws.com/0/54225/183e9f17-5df7-9711-51d1-ab986281ddb2.png "screencapture-threejs-org-examples-webgl_gpgpu_protoplanet-html-1481813042713.png") https://threejs.org/examples/webgl_gpgpu_protoplanet.html ![screencapture-threejs-org-examples-webgl_gpgpu_water-html-1481813044941.png](https://qiita-image-store.s3.amazonaws.com/0/54225/6ad72ff4-4b49-87b9-e695-b187f70c7829.png "screencapture-threejs-org-examples-webgl_gpgpu_water-html-1481813044941.png") https://threejs.org/examples/webgl_gpgpu_water.html ### サンプルがむずかしい * 複雑な数学的要素がコードの読解の邪魔 * パーティクル飛ばしたいだけだっつの 結局このなかでも比較的簡単そうな重力とかを計算してるこいつを解読してみることに。 https://threejs.org/examples/webgl_gpgpu_protoplanet.html
そもそもパーティクル専用のオブジェクトがあるからそれ使えば?っていうツッコミが来そうですけど、私は当時、一から作りたかったのです。勉強も兼ねて。 ## 削りに削ってランダムに飛ぶだけのパーティクルにしてみた! ![gpuparticle_forQiita03.gif](https://qiita-image-store.s3.amazonaws.com/0/54225/293905ac-7b2e-67dc-a313-7d7172690824.gif "gpuparticle_forQiita03.gif") 今回はこれを目指します。 なんと25万つぶつぶです! ## シェーダーで演算するというのはどういうことか ![Group 3.jpg](https://qiita-image-store.s3.amazonaws.com/0/54225/7f35cfb5-0dbf-03d1-bb3a-465e757694c6.jpeg "Group 3.jpg") すごいざっくりした解説。ここまではまあわかる。 しかしシェーダーをちょこちょこいじったことが有る人ならわかるが、配列なんかを用意してさくっと情報を連番で保存することが難しい…。 ここでテクスチャの登場である。テクスチャはrgbaの情報を格納するためのものだが、今回はむりやりxyzwと言った感じで座標を保存するために利用させて頂きます。 そしてこのテクスチャが複数あれば… ![Group 2.jpg](https://qiita-image-store.s3.amazonaws.com/0/54225/a60d18ff-85fa-33be-a745-70f6bae850b8.jpeg "Group 2.jpg") こんな感じにでワンフレームごとにピンポンを繰り返すことで、自由な演算ができます。 JavaScriptだけだと、何万という頂点情報が格納された配列をfor文で回して配列の中を書き換えていくわけですが、 これをGPU側で並列にドカンと処理してしまおうというもの。 ## 肝心のソースコード ```html three.js webgl - gpgpu - protoplanet ``` サンプルに目を通した方ならわかると思いますが、けっこうダイエットに成功したソースコードになっております。 適宜コメントアウトをいれておりますが、GPGPUあたりを細かく解説していくと文字量がとんでもなくなるので、 このソースコードになるように https://threejs.org/examples/webgl_gpgpu_protoplanet.html このサンプルのソースコードを削ってみてください!そうすればなんとなくわかってきます。 この領域までくるとソースコードを部分ごとに破壊して表示結果がどう変化するかを体験したほうが理解が速いと思います。 あとdoxasさんの資料もおすすめです。 [GPGPU でパーティクルを大量に描く](https://wgld.org/d/webgl/w083.html) ## とはいえ、一応ソースコードをざっくり解説 今回用意したシェーダーは3つです * 頂点情報のシェーダー(computeShaderPosition : fragmentのみ) * 移動方向を決定するシェーダー(computeShaderVelocity : fragmentのみ) * パーティクルを描写するためのシェーダー(particleVertexShaderとparticleFragmentShader) 重要なのは上の2つで、一番上が頂点情報をワンフレームごとに記録するメモリの役割をします。 2番め(computeShaderVelocity)で移動方向を決定します。パーティクルの動きはここで制御できるので、ためにしcomputeShaderVelocityに ```glsl vel.y = 0.0; vel.z = 0.0; ``` 以上のコードをvelが宣言された後に追記してみましょう。そうすると… ![gpuparticle_forQiita04.gif](https://qiita-image-store.s3.amazonaws.com/0/54225/d144e687-5cc6-c5bd-1fcc-0737edc4fab4.gif "gpuparticle_forQiita04.gif") x方向の動きだけになりました。なんとなくわかってきましたか? ## 発展系 ![simpleGPGPU_study11.gif](https://qiita-image-store.s3.amazonaws.com/0/54225/05699477-148d-74a7-d9b8-5f6f2227a82a.gif "simpleGPGPU_study11.gif") ![simpleGPGPU_study12.gif](https://qiita-image-store.s3.amazonaws.com/0/54225/c476cf61-566b-d960-5d03-adb914af781c.gif "simpleGPGPU_study12.gif") ![gpuparticle_wave_test05.gif](https://qiita-image-store.s3.amazonaws.com/0/54225/24daea3a-b432-25b8-bf31-cfbea7910bcf.gif "gpuparticle_wave_test05.gif") https://murasaki-uma.github.io/webgl_vj/frame.html サンプルページでは "<"キーでアニメーションスタート ">"キーを押してる間はスピードダウン という設定になっているので、少し遊んでください。MBP2015程度なら普通に動くはずです。 解説は以上です!情報がまだ足りないという方は遠慮なくコメント欄やTwitterの方から質問を飛ばしてください。 また、シェーダーに関しては私もまだまだ素人なので、気になる部分を見つけた先生方も、遠慮なくコメント欄からご指摘いただければ幸いです!