VRエンジン作成日記

ひたすらVRエンジンを作っていきます

パーティクルシステムを作り始めた

パーティクルシステムというのは、小さな点が次々に飛び出してくるような機能。

雪が降っているシーン、星が奥から飛び出してきて光速移動しているように見えるシーン、シューティングゲームの爆発効果とかで使うことができる。この機能があると、様々な用途で画面を豪華にすることができる。

f:id:hikipuro:20180901192930p:plain

今のところ、ほとんど設定を変えることができないから、もうちょっと柔軟に変えられるように変更しようと思う。

UnityではShurikenっていう名前のパーティクルシステムが入っていて、飛び出す時間の間隔、飛び出し元の形、飛び出してくる物体、色とか、ありとあらゆる要素が変更できるようになっている。

パーティクルに馴染みがない人は次のサイトを開いてもらえると、どんなものか分かると思う。2Dだけどパーティクルをブラウザ上で作ることができる。

particle2dx.com

技術的な点でもう1つ書いておくと、今のところCPUを使って1つ1つの点を毎フレーム一斉に移動させているけど、この仕組みでは、スペックにもよるけど出現個数が1万個くらいで処理落ちが発生するようになるのではないかと推測している。GPUを使って移動の処理もしてしまう方法があるようなので、そちらも調べて実装してみたい。

・関連するコミット

add particle system · hikipuro/tea.js@6f8d211 · GitHub

 

objフォーマットの対応

objファイルの読み込み機能を修正した。objファイルの読み込み機能は数週間前に作ってはいたんだけど、頂点データくらいしか読み込めていなかった。今回は三角形のインデックスと法線、UVとテクスチャを読み込むようにした。objファイルが正常に読み込めるようになると、3Dのモデリングソフトで作ったメッシュデータを画面に表示することができる。

f:id:hikipuro:20180831063832p:plain

今のところ、ひとまず読み込んで画面に表示できるというところまでを雑に実装した感じになっている。ティーポット以外のファイルは読み込めないかもしれない。またいずれ修正することにしよう。

・関連するコミット

fix obj file reader · hikipuro/tea.js@6718e82 · GitHub

 

立体視用の機能を追加

ようやくVRっぽい機能を1つ追加できた。

立体映像の表示方法にはいくつか種類がある。基本的にはどれも、左右の目で別々の画像を表示する構成になっている。例えば次のような種類がある。

 

・サイドバイサイド (side by side)

f:id:hikipuro:20180830004513p:plain

 

・トップアンドボトム (top and bottom)

f:id:hikipuro:20180830004759p:plain

 

・ラインバイライン (line by line)

f:id:hikipuro:20180830004826p:plain

 

今のところとりあえず、この3種類の表示ができるようにしておいた。Youtubeの3D映像では、PCで閲覧時はアナグリフという方式が使われる。アナグリフというのは、右目に青のフィルム、左目に赤のフィルムを張り付けたようなメガネをかけて、左右別々の映像を1つのモニタ上に表示するという方式。アナグリフであれば、立体視対応モニタでなくとも表示することができる。今のところアナグリフ用のメガネを持っていないので動作確認ができず、実装もできていない。

ちなみに上の3種類については、LG製の立体視対応PCモニタで動作チェックをした。このモニタは偏光板付きのメガネをかけて立体映像を見るような方式になっている。立体視で見ると、テキストが画面から飛び出して見えた。

あと、スマホ対応を考えるならGoogleカードボード方式に対応した方が良いのかもしれない。現状の実装では、映像が左右にひしゃげたようなレンダリングになっているため、カードボードで見ると映像が縦長になっているように見えるだろうと思う。カードボードに対応できれば、そのままオキュラスリフト対応にもなるのではないだろうか。

どのようにしてこの映像をレンダリングしているかというと、単純にステージ内にカメラを2つ追加したような構成になっていて、左右に10cmから20cmずらして (レンダラ内の単位1を1mとした場合、0.1から0.2ずらして) 1フレーム中に1回ずつ、合計2回レンダリングするという簡単な方法を使っている。プロジェクション変換の行列の数値を変えることまでは考慮していない。処理負荷でいうと、描画するピクセル数は通常の描画でも立体視でも同じピクセル数になる。Unityでいうところのドローコールの量が2倍になる。

・関連するコミット

add stereo camera settings · hikipuro/tea.js@cb31c27 · GitHub

 

シェーダ関連機能の追加

今日はシェーダに関連する機能を追加していた。昨日書いたように、シェーダ言語では設定しきれないけどレンダリングに必要な項目がいくつかある。UnityではShaderLabのPassブロックの中で、レンダリングステートとして設定できるようになっている。この辺りの機能はライブラリのユーザ側で設定できないと描画用の機能が減って不便なので、エンジン内に実装しておいた方が良い。

docs.unity3d.com

Unityではこの辺の機能はうまく隠蔽されていて、ShaderLab以外の場所、C#スクリプト等からは設定変更ができないようになっているらしい。なぜそうなっているかというと、DirectXOpenGL等の描画用APIバージョンアップごとに、その時代の一般的なハードウェア事情に合わせて調整されているらしく、どの部分でハードウェアアクセラレーションが有効になるか等が考慮された上でAPIが作られている。そう考えると、いつ基盤が変更されても後方互換性を持たせられるように (今の時代のUnityで作ったスクリプトが変更なしに数年後のUnityでビルドできるように)、変化する可能性があるものはできるだけユーザ側に公開しないようになっているんだろうと思う。シェーダを書き換える手間はかかるかもしれないけど、どこでどのように使われるか分からないC#スクリプト内で自由に操作できてしまうと、後方互換性を維持するコストが増すだろうと考えることができる。

今のところtea.jsでは、そこまで考慮するのは難しいため、全部公開するような感じで書いている。うまくAPIを隠蔽するためには、プログラミング言語の記述テクニック的な難しさもあるし、将来的に変更される可能性のある箇所についての予想を立てるために、描画APIについての深い知識も必要になる。予想した内容が的外れだった場合、ライブラリの使い心地の悪さが際立ってしまうことになる。

・関連するコミット

add shader settings · hikipuro/tea.js@0faf8af · GitHub

 

追記

検索してみると、C#スクリプトからレンダリングステートの変更はできるようだった。調べる前に書いていることもあるので、疑問に感じる箇所があった人は検索した方が正確に理解できると思う。

qiita.com

 

Material.renderQueueの追加

今日は細々と気になっていたことを修正した。落ち着いて衝突判定の処理を書き進めていきたいところだけど、積もってきた未処理の作業を減らしておかないと落ち着いて考えることができなさそうだった。

今日やったことは主にMaterial.renderQueueの部分。renderQueueはどういう機能かというと、例えばUnityではTextMeshは常に最前面に表示されるんだけど、最前面に表示するにはどうすれば良いかと自然に考えると、一番最後に描画する方が破綻が少ないということになる。そういう時にrenderQueueの値でレンダラオブジェクトをソートして描画順を決定するというような仕組みが用意されている。例えば球や立方体等のプリミティブオブジェクトを画面に追加するとrenderQueueの値は2000になっている。TextMeshを追加すると3000になる。小さいもの順にソートされるようだ。

きちんと調べられていないけど、おそらくこの値はUnityだとShaderLabのソースコード内で値を書き込むのではなかろうか。ShaderLabは便利に作られていて、HLSL/GLSL等のシェーダ言語で表現できる範囲外の項目だけど、シェーダに関連して書いておくべき値を書く欄が用意されている。その仕組みを使ってエディタ内で設定できる色やテクスチャ等の変数の項目をGUI部品として表示できるようになっている。

今日は他に、MeshFilterをとりあえず作ったのと、Object3D.sendMessage()等を分かる範囲で書いておいた。動作テストはほとんどできていない。MeshFilterを作っている最中に、sharedMeshやsharedMaterialを作る必要がありそうだと思った。今のところ作らなくても動作に支障がでるわけではないから、この実装は後回しにする予定。

mocha用のUnitTestフォルダも作ったは良いけど、Vector3の一部しかテストが書けていない。数学用の関数となると、正しくテストが書けるのかという疑問も湧いてくる。

・関連するコミット

fix some problems · hikipuro/tea.js@cea6436 · GitHub

 

FPSの表示機能を追加した

今日はFPS(画面描画回数の単位。フレーム毎秒)の表示機能を追加していた。画面の右上あたりでデバッグ情報とかを表示する窓みたいなものなんだけど。ゲーム開発者の間では一般的にStatsというらしい。以前、Flashでコンテンツを作っている時に、よく誰かが実装してくれたStatsライブラリを使わせてもらっていた。

今のところFPSしか表示されていなくて、しかも正確な値が出せていないから便利とは言えない。余裕がある時に修正しようと思う。

詳しく調べたわけではないけど、WebGLではGPUの処理負荷やメモリ消費量等の情報がブラウザ内のJavaScriptからは取れないようになっているらしい。そうすると、頑張っても便利なデバッグ画面は作れないかもしれない。ChromeFirefox拡張機能を使ったWebGLデバッグ画面が配布されてるみたいだから、いざとなればそれを使えば良さそう。

ある意味、自分で作らなくても色々便利なものが汎用的に作られて配布されてるから、巨人の肩の上に乗れるところでは乗っておいたほうが良いかもしれない。そもそも最初からUnityとかThree.jsを使えば良いじゃない?と考えると、そのとおりなんだけどね。

今のところ興味があるのはコンテンツ1つ1つを作り上げる手法ではなくて、3Dの描画エンジンそのものだからエンジンを作って覚えるほうが良い。

・関連するコミット

add stats class · hikipuro/tea.js@c5b941d · GitHub

 

親子関係にあるオブジェクトの座標

今日は画面に表示されるオブジェクトの親子関係の座標についての実装を変更していた。今まではObject3Dというクラスに座標を格納していて、positionというメンバ変数に保存していた。座標を変えたい場合は次のように書く。

obj.position.x = 10;

こうすると、X座標が10のところにオブジェクトが移動する。

ただ、親子関係がある場合は安直に座標を入れ込むことができなくなる。例えば戦車のオブジェクトがあるとすれば、本体と砲台は一緒に動いた方がいい。また、戦車の本体が動いたら砲台は動かさなくても自動的にくっついていた方が便利だろう。そうする場合、戦車本体を親オブジェクトとして、砲台を本体の子オブジェクトというように関係を作っておく。自分で描画エンジンを書く場合は、この親子関係についても実装する必要がある。

Unityでは自分自身の座標はlocalPositionというメンバ変数に保存するようになっていて、親子関係を考慮した座標はpositionというメンバ変数に保存されている。具体的な実装ではどうなっているかというと、おそらくposition側はgetter/setterを通してアクセスする仕組みになっているだろうと考えられる。getter/setterがあると、副次的な操作を変数の参照時に加えることができる。また、position.xのようなさらに細かい参照については変更が伝搬しないようになっている。positionを変える場合はposition = newPositionというように書かなければならない。これはgetter/setterの動きがpositionのみに設定されていて、より細かい操作は上位オブジェクトに伝搬しないように設計されているためだろう。より細かい操作をサポートする場合は、JavaScriptでいうところのEventEmitter等を使ってposition.x等の要素の操作があった場合は、そこでemitする必要がある。ただ、そんなに細かい操作でemitが入るような仕組みは簡単に処理が重くなってしまうだろう。この設計は便利ではないが、速度と便利さのトレードオフとしてあえてそうしていると考えるのが自然だ。それらを考慮すると、JavaScriptでも同じようにしていればUnityユーザには分かりやすいだろうと思う。今日書いたコードでは、なんとなくそうなりそうな感じに書いておいた。不都合があれば後で修正する。

あと、コーディングレベルで気を付けないといけないこととしては、いつ親から伝搬されたpositionの値を更新するかという問題がある。getter/setter内に都度更新するような処理を書いても良いけど、毎回、孫、ひ孫オブジェクトがルートまで登って行って座標を取得するというのはどう考えても処理時間がかかるだろうと予想が立つ。ただ、キャッシュするにしてもいつなのか、親が更新されたら子側全てを更新しておくというのが良いのか、ベストな解決方法というのはぱっと思い浮かばなかった。この辺も思いつき次第で修正しておきたいと思う。

・関連するコミット

fix some functions · hikipuro/tea.js@ef582ef · GitHub