2018/08最新追記
現在、GLBoostは以下文章で挙げたコード管理上の問題の多くを解決しています。
また、コンポーネントシステム搭載を含む、大幅な設計刷新の作業が進行中です。今後1〜2年をかけて、本ライブラリは大きく状況が変わるでしょう。その頃にはライブラリとしての名称変更も予定しています。
商業採用例もさらに増えました。今年末に再び、状況の報告記事を書ければと思います(→書きました)。
この記事ってなんなの?
趣味で作り始めたWebGLライブラリが、いつの間にやら商業案件に採用され、気を引き締め直した作者がなんとか今までやってきた苦労と反省と、得られた知見について長々と語ります。本当にTL;DR(この言葉の意味を知らない人はググろう)です。
今回の記事は、得られた経験・苦労・反省点を振り返るための記事であり、もし皆様の糧にしていただけたら・・・という意味合いが強いので、あえて自分でネガティブなことを多めに書いています。でも、本当はこのライブラリ、ちゃんと良いところもたくさんあるのよ?(笑)
(公開まで時間がなくて図を全く入れられませんでした。後ほど入れます。文章ばかりで分かりづらくてすみません!)
この記事の対象読者
- WebGLまたはThree.jsなどで、ちょっとした3Dプログラミングが楽しくなってきた方
- 「ん?俺ってもしかして3Dプログラムの才能ある?」って思えてきて次何しようと考えている方
- 実力があるのに、「自分なんかがライブラリ開発なんてまだまだ・・・」って遠慮している方
はじめに
こんにちは、@emadurandalです。2015年中頃だったでしょうか。WebGLライブラリを作ってみたいと思いたち、Github上でリポジトリを作りました。
(ちなみに、Gitで初回コミットは git log -p --reverse
で確認することができますよ)
当時すでにUnityといったゲームエンジンが台頭しており、しかもWebGLへの出力対応も進みつつあるという状況でした。おそらく、そうなれば「とりあえずなんでもUnityで作ればいいんじゃね?」のような時代が来て、一からコードを書く人はいなくなるのではという危機感があり、あえてそれに逆らってみよう、という思いがありました。
作り始めたライブラリは、最初はobjファイルを読んでそれを表示したり、何故かブレンドシェイプだけ先に実装したりと、思うままに作っていたのですが、徐々にシーングラフの仕組みやらに手を出し、少しずつライブラリっぽい方向性に向かって行きました。
WebGLライブラリ比較記事で知られる @cx20 さんが当時早々に興味を持ってくれて、使って意見をくれたのも大きかったと思います(開発序盤は、こういう数少ないユーザーさんの反応が嬉しいんです)。
LambertやPhongシェーダーといったおなじみのものから、シャドウマップ、スケルタルアニメーション(これは実装苦労しました…)までサポートし始めたあたりから、ライブラリとして今後長期に育てていこうという気持ちになっていきました。
それがある日、フリーランスとしてお仕事させていただいたCG会社さんが受けたWebGL案件で、Three.jsをglTFを使ってキャラアニメーションを試みたところ、当時ちょっとうまくいかず、GLBoostでうまくいった、ということがあったんですね。
そこで、「本来ならThree.jsを案件で使うのが鉄板なのだろうが・・・。こういう不具合があった時に、手を加えてメンテしていって、公式の更新を取り込む、なんてやっていけるのだろうか。自分の肌に合うんだろうか」
「自分のライブラリなら、完全になんでも制御できるじゃないか!」
と考えて、自作WebGLライブラリである「GLBoost」を提案してみたところ・・・なんと、提案が通ったのです!
なんとまぁ、趣味で作り始めたオレオレライブラリが、商業案件に採用・・・。
「自分で言い出したことではあるけど、これは気合いを入れねば・・・(汗)」
そこから、怒涛の開発が始まっていくことになったのです・・・。
GLBoostとは
GLBoostとは、私 @emadurandal が開発している国産WebGLライブラリです。
元々はPhina.js(アドベントカレンダーもあるよ)という国産2Dゲームライブラリの3D機能の開発としてスタートした・・・はずなのですが、いつの間にか単体の3D特化ライブラリに変貌を遂げて行ったという経緯があります。
元々はCG研究者がCGのいろいろな実験をするのに、WebGLを直接触るのでは面倒くさいから、その手間をなくすためという思想で作ったのですが、いつの間にかThree.jsみたいなノリのライブラリになって行きました(それでも、割とローレベルのことがやりやすい感じではあると思います)。
採用されてから
商業案件で採用されてからというもの(一体、どの製品・サービスに採用されたかは……ここではあえて秘密にしておきますが、TwitterでWebGL関係の情報を漁っている人ならわかるかもしれません)、GLBoostに足らない機能を、ひたすら開発していく時期が続きました。
この記事は、長々と書きますが、採用されて行った結果、主にライブラリの設計面と、開発の管理面の両方で感じた課題を明らかにするものです。
設計面の反省
趣味から始まり、何気なく始めたということもあり、またライブラリ開発を(それまで仕事で一部に携わったことはあるんですが)、丸ごと自分で全部やる、というのは初めての経験でした。
本当に多くの課題点・反省点があります。
だんだんシェーダーが重くなっていった
おおっ、いきなり重い話だ(笑)
その採用された商業案件は、3Dモデルを様々評価するための、凝った表示機能が多くあります。スケルタルアニメーションだったり、法線表示モードだったり、ワイヤーフレーム表示だったりと。
そういう機能は、あらゆる描画でも機能するように、最も基本的な共通シェーダー箇所に適用することになります。
そうすると、ほぼ全てのシェーダー構成で、その機能が有効になってくれるわけですね。
しかし、当然・・・こういうことをやっていくと「ちりつも」(塵も積もれば山となる)で処理が重くなっていくわけです。
採用された案件は、高速性よりもどちらかというとDCCツールの表示をできるだけ再現する「ビューアー」としての機能性が重視されるため、シェーダーが軽量であることは最優先ではありません。むしろ、様々な表示モードをサポートするために、自然と「ヘビー」なシェーダーになります。
それは、当然Github公開版にも多少のしわ寄せが行くようになります。
※GLBoostは必ずしも全体の動作が重いライブラリ、というわけではなく、日々最適化は進めています。特にここ数ヶ月ではシャドウマッピング時の動作やスケルタルアニメーションの計算など、かなり最適化が進み始め、全体としては高速に動作するようになっています(商業版Onlyですが…)。ただ、シェーダーは少しヘビーですね。とはいえ今のGPUはシェーダー演算モンスターであり、WebGLの場合は相対的にCPU側がボトルネックになりがちなので、多くのケースではそれほど問題はないのかもしれません。
「コンポーネントシステム」はやはり入れるべきだった。
Unity然り、Unreal Engine然り、最近の大規模なライブラリやエンジンではコンポーネントシステムという機能を導入する例が増えています。
コンポーネントシステムとは、詳しくはこちらをご覧ください。手短に説明すると、特定の機能のまとまりを「コンポーネント」という仕様単位にし、ゲームクラスの各インスタンスに対して、そのコンポーネント群を好きにアタッチして行くことで、ゲームインスタンスに機能を臨機応変に追加して行くことができるという機能仕様です。
WebGLライブラリでも、私の友人 @kyasbal_1994 さんたちが開発されている国産WebGLフレームワーク「Grimoire.js」(アドベントカレンダーもあります)では大変優れたコンポーネントシステムを搭載しています。
しかし、私ちょい前まで、コンポーネントシステムなんて便利なアイデア、知らなかったんです・・・。
今思えば、搭載すべきでした。全部継承とちょっとしたクラスのコンポジションでどうにかしようとしていた…。自分の設計力の甘さを痛感します。
そのうち、GLBoostでもなんとか搭載することになるかもしれません。大工事になりそうですが。
こういうことを受けてしみじみ感じることは、先人のライブラリやエンジン、そしてライブラリ開発についての書籍など、きちんとリサーチせよ、ということですね。経験が少ない状況で、自力の試行錯誤でできることと、先人の知恵に学ぶこと、できるクオリティがやはり最初から違ってくるでしょう。近道があるなら通らない手はありません。
ライトなど、動的に数が変化しうる物に関わる計算は、頂点シェーダ->フラグメントシェーダーの間を跨(またが)らせない方がいい
どういうことかと言いますと。例えば法線(ノーマル)マッピングっていう、法線をテクスチャで摂動させて、凹凸を擬似的にライティングの変化で表現する手法ありますよね。あれを例にとりましょう(以下、法線マッピングの概要を知っている人向けに話します。知らない人はごめんなさい。ここで調べてね)。
ノーマルテクスチャの法線って、大抵は接空間を前提として値が入っています(常にそうとは限りません。オブジェクト空間だったりすることもあります。ケースバイケースではあるんですが、まぁ大体は)。
ライティングの計算においては、計算に関わる全てのベクトルの座標空間は統一しないと結果が正しくなりません(当然ですね)。
そこで、よく(GPUが貧弱だった昔は特に)取られるのが、「ワールド空間における視線ベクトルや光源ベクトルを、頂点シェーダー内で接空間に写像して、フラグメントシェーダーに渡す」という方法です。そうすることで、フラグメントシェーダーでフェッチするノーマルマップの法線は接空間ですから、座標空間として一致するわけですね。
別の方法もあります。視線ベクトルや光源ベクトルはワールド空間とし、フラグメントシェーダーでフェッチしたノーマルマップの法線(説空間)を、ワールド空間に写像して、ワールド空間でライティング計算する、というものです。
ワールド空間<->接空間の写像は、接空間基底行列という行列をベクトルにかけることで行います(ちなみに、この行列は直交行列なので、転置行列が逆行列となります、つまり、転置することで、写像の向きを反転させることが可能です)。
で、前者の方法の方が、比較的重たい処理である行列とベクトルの掛け算を頂点シェーダー(頂点単位)という荒い単位で行えばいいため、処理は軽くて済みます。
そのため、前者の方法がとられることが多く、GLBoostでもそうしていたのですが、だんだんこれが問題をはらむようになってきました。
写像した光源ベクトルはフラグメントシェーダーに渡さなければなりません。つまり、varying変数としてラスタライザを通過させるわけですね。しかしこのvarying変数、モバイル機器だと許される個数が極端に限られているのです。割と最近のiOS機器でもvec4で8個分くらいしかありません(おそらく、iOSのGPUがTBDR:タイルベース遅延レンダリングの設計であることが起因しているのではないかと思います)。varying変数は様々な用途に使いますから、これだと光源の数は無理してもせいぜい5〜6個分しか使えないでしょう。
大体、これだと光源の数が動的に増えるたびに、シェーダーコードのvarying変数の記述が変わるため、シェーダーをコンパイルし直さなければなりません。面倒だし、瞬間的な処理落ちの原因にもなりますね。光源の使用数の最大値を決めうって、常にその最大数を占有するという方法もないわけではないですが、オススメはできません。varying変数はその使用量が増えるだけで、パフォーマンスに影響するからです。
しかし、後者の方法では、行列とベクトルの掛け算がフラグメントシェーダーに回り、計算回数は増えてしまいますが、こうした問題は起きなくなります。varying変数だけでなく、光源位置などのunifom変数もフラグメントシェーダーからアクセスするだけで済みますね。
(さらには、光源位置などのデータをuniform変数ではなく、テクスチャに格納するなどすると、より汎用性が高まります。ライト数が増えても、シェーダーコードは変化させなくてもよく、シェーダーの再コンパイルは不要となることでしょう)
と、いうようなことも、ライブラリを自作していると徐々に知見が溜まってくるのです。ライブラリ自作、オススメです。
今、GLBoostをまさにこの実装方式に書き直しているところです。
※前項のシェーダーの肥大化や、シェーダー内での動的要素の増減への対応については、条件が変化したタイミングでシェーダコードを再構築(使わない機能を削ぎ落とす、動的要素の個数を変動させるなど)した上で、シェーダーのリコンパイル・再リンクを行うことでも対応は可能です。ただ、そう言う実装を作るのは若干手間がかかるのと、シェーダーの再構築時の性能ボトルネックが嫌な感じです。できれば、シェーダーの再構築をしない方法で対応したいというのが私の考えです。
TypeScriptで作っておけば・・・。
型がないって本当に辛いです! コンパイル時のチェックがどれだけありがたいか。
そもそも、細かいミス(実にくだらない些細なスペルミスも含む)に、ランタイムで動かしてようやく気づく、という時点で無駄なのです。この無駄なランタイム実行時のミスで累計どれだけの時間をドブに捨てたかわかりません。
ESLintなどでチェックできる部分はあるにせよ、Lintも完全ではありませんからね。
TypeScriptに移行したい、と思いつつもやはりこれまでのコードを全て置き換えるのは大変なのことで、TypeScriptへは断念しました。代わりに、最近、段階的に移行できるFlowtypeを導入しました。型っていいな。
CG(ガーベージコレクション)の誘発を防ぐべし
JavaScriptにはCG(ガーベージコレクション)という、メモリ自動解放のための仕組みがありますが、例によってこのGCはいつ起こるかわからず、起こると全体の動作がガクッと瞬間的に止まります。これが、数フレームに一度起きたりすると、FPSの不安定化につながるのです。
現行のGLBoostでも起こります。原因は、レンダリングループの中で、ライブラリ中でnewしているオブジェクト(行列クラスやら何やら)が結構ある、ということが一番多いでしょう。あとは、文字列操作をしているところもあり、そういうのも多少影響していると思います。
行列クラスについては、値が上書きを避けるため、独自のclone()メソッドで行列を複製しないといけないこともあり、なかなか一筋縄ではいかないのですが、1番の解決策は、あらかじめ必要となる行列はレンダリングループの前にnewしてしまい、それを使いまわすことでしょう。この作業を、今後のGLBoostでは辛抱強くやっていく予定です。やはりFPSは安定させたいので。
Github版の悲しみ
だんだんシェーダーが重くなっていった -> Github版も重くなった
前述のとおり、商業案件向けに表示機能を多機能にすればするほど、多くの機能を共通シェーダーにも入れ込むことになり、……GLBoostのGithub公開版(汎用目的で使われます)のシェーダー処理まで重たくなってしまいました。
もちろん、コアシェーダー部分も、商業案件版とGithub版で実装を分ける、という手もあります。実際そうしている部分はあるのですが、しかしその両方の機能差異があまりに大きくなると、今度はそこと連携するJavaScript側のコードも分岐が必要にもなり、やはりある程度の機能をGithub版にも(権利関係上使えないようにはしつつも、コード的には)存在させないといけない場面もあったのです。いやー、きついですね。
プロジェクト管理面
バージョン番号どうする!?
私、バージョン番号つけるのすごい苦手なんです。
どういう基準でつけたらいいの? いや、世の中にセマンティック・バージョニングなんて考えがあることは流石に知っています。しかし、Deprecated(非推奨)な関数をきちんと明示したり、このマイナーバージョンの間は勝手に機能を削除してはならぬとか、セマンティックにするために意識しなければならないことが増えて、大変だなーと思ったりしました。
結果、どうなったかというと、Three.jsのようなリビジョン番号制になりました。「r2とかr3」って感じです。
これが本当にいいのかどうかはわかりませんが、あーでも、やっぱりいずれはセマンティック・バージョニングにしたいですね。
商用案件用のブランチとGithub一般公開用ブランチに分けた
契約の縛りで、お金をいただいた時間で開発した機能部分に関してはGithub一般公開版に晒すわけにはいかないので、商業案件用のバージョンは会社のGitlabで管理するような、リポジトリの二重管理になりました。
商用案件用/Github一般公開用のコード切り分けは結構むずい(というか意外とコスト高)!?
しかし実際は、二重管理どころか、三重管理になってしまいました。
というのは、Github版として公開したいけど商業案件用のコードで切り分けができていない曖昧なコードが出てきたからです。具体的には、商業案件の機能要件に関連のあるコードなんだけれども、実装の都合上、共通部分の処理に入れないと都合が悪い、といったようなケースです。
そういった部分、まぁ実際にはGithub版に含めても実害が出るケースはないと思うのですが、念のために3つ目の「保留リポジトリ」で「コード切り分けの沙汰」があるまで待機させておく、という感じになりました。
いや、スケジュールに追われて機能実装に明け暮れていると、なかなかそういうコード切り分けの作業をやっている時間なんて取れないのですよ。ブランチを切り替えてそれぞれにコミットするのさえ億劫になりそうなくらいなのです。
慣れなかった頃は、そうした管理の手間で全開発時間の3割以上の時間を溶かしてしまうこともありました。今は多少慣れましたが、コードの切り分けの徹底をどうしよう、と思うとちょっと気が重くなります。
どの粒度でコードを切り分けるのか
GLBoostでは、クラス単位で分けています。JavaScriptではマクロは使えませんし、案件ごとの違いが徐々に増えていくであろうことを考えると、多少粒度としてはやや大きくても、クラス(ファイル)としてはっきり分けてしまうことで、Git管理上もとてもシンプルになります。
例えば、DecalShaderクラスであれば、Github一般公開用はDecalShader
クラス、商用案件用はXXXDecalShader
クラス、といった感じです。もちろん、多くの場合は親クラスとして共通処理を汎化させたりもしていますが、中には汎化なしで、双方でコードの少なからずが重複してしまっているものもあります。ここら辺は課題ですね…。
挙げ句の果てに、切り分け作業が完全にやりきれなくなった -> Github一般公開版の更新が大幅遅延
見出しの通りです。最初はちゃんとやっていたのですが、機能実装に追われ、だんだんと切り分け作業なんてやっていられなくなりました。
作業として、やりやすければ問題ないのです。次第にGit上でのブランチ機能を使った切り分け作業も慣れてきたり、ちょっとした工夫を入れたりもしていたのです。しかし、問題なのは前述の通り、設計の都合上、案件で発生した機能要件ではあるものの、一部はコア機能に入れないと動作させるのが難しいコード実装部分が出てくる場合です。それが、本当に純粋な汎用機能であればいいのですが、やはり案件独自の特色や事情が強い実装部分となると、コア機能ではなく、時間をかけてリファクタリングしてでも、案件用ブランチに切り分けたくなります。しかし、機能実装のスケジュール・マイルストーンは待ってくれません。
開発スケジュールに間に合わせるため、選ばざるをえなかった苦肉の策……それは、切り分けがしきれていないコア部分に入れ込んだ案件との関連性が高いコード実装を、Github公開版で公開してしまわないようにするため、Github一般公開版の更新を見送ることでした……。
はっきりいって、これは辛い。GLBoostはまだ当時利用者が少なかったからよかったものの、割と利用者が増えてきた段階で、こうした事態になったらシャレになりません。
こんなこともあり得るので、自作ライブラリがもしかしたら商業案件に採用されるかも…という状況に恵まれた、そこのあなた。私と同じ轍をどうか踏みませぬよう……。
※できれば、契約の段階で、コードが案件に縛られないようにクライアントに理解をいただくのがいいですね。ケースにもよると思うので、なかなか難しいと思いますが。
では、どう折り合いをつけるのが理想なのか?
これは、私の知り合いの某国産オープンソースSNSシステムを作られている会社の社長さんもおっしゃっていたことですが(うろ覚えです。記憶違いならごめんなさい)、今身をもって私も実感していることが以下です。
- 案件ごとのカスタマイズ開発は、やるにやまれず、なケースもあるが、やはり状況が許すならやらない方がいい。
- 自社は基本コード版のみメンテナンスし、各案件のクライアントにコードは開示して、必要ならば彼らにカスタマイズさせた方がいい(そういう風にいかに持っていくか、という別の問題もあるけれども)。
- どうしても、という場合は、「プラグイン開発」という形で対応すること。
- その「プラグイン開発」さえも、案件ごとに自社で作ってメンテナンスし続けるよりは、自社メンテ版プラグインを、案件先にフォークさせて、彼らの方でカスタマイズ版のプラグインを作らせる方がいい。
「彼ら」には、クライアント企業さんだけでなく、パートナー企業さんなども含まれます(自社製品の派生物を作ってもらえるパートナー企業さんなんてできれば、本当その製品は成功しているってことですけどね)。
カスタマイズ開発を避けるべき理由は、主に以下の三つです。
- コードのメンテナンスが大変
- 貴重な自社の開発リソースが分散してしまう
- いずれ本体ブランチにまでカスタマイズ開発の影響が出てきて、非本質的なコードをマージせざるをえなくなったり、それらの互換性を維持するために、新機能の追加や設計変更が難しくなる -> 製品寿命が縮まる!
とはいえ、これは一般論です。
私のGLBoostの場合は、お相手が今の所一社さんだけですので、これらの悪影響はそれほど出ていません(Github一般公開版との切り分け問題で苦しんだくらい)。
むしろ、その会社さんのご要望に応える過程でライブラリが鍛えられてきたこともあったので、単なる個人ライブラリの域から脱却できたという点で、今回の商用案件のお話には本当に感謝しています。
総じて設計面を振り返ると…。
機能実装を優先したため、というのもあったのですが、やはり設計面の洗練さがまだまだ足りていないな、という課題点ですね。ここをもう少しスマートにできれば、案件ごとのコードの切り分けももう少しやりやすかったかもしれません。
そして、今回ありがたくもある商用案件で採用していただいたわけですが……。結構大変でした。特にGithub一般公開の方に商用案件の実装を(完璧には難しいですが、実質問題ないレベルまでに)入れないように、コードの切り分けをすることも含めた、コードの保守…。何よりこれが大変です。そして、よりマクロなレベルで言えば、機能実装の長期的な方向性としても、商用案件とGithub一般公開用とで乖離を起こさずにどこまで折り合いをつけられるか、も重要な問題です。
方向性、ということで言えば、GLBoostはまだ当時Github一般公開版は利用者も(っていうか今もそんなにいませんが笑)少なく、私個人としても当時決めていた開発方向性にそれほど強烈なこだわりもありませんでした。その結果、自然と商用案件での開発の方向性にずっと寄っていった感じになりました。
その結果、基本シェーダーは非常にリッチな反面、コードサイズは大きく重たいシェーダーになりました。この案件では目的にかなっていて良いのですが、一般用途ではオーバースペックで無駄な面が多いとも言えます(商用案件用とは別に、Github一般公開用の基本シェーダーはずっと軽いのですが、それでも都合上、商用案件用の思想に引っ張られて、やや大きめなのです)。
おそらく、よほど工夫をしない限り、別案件用にコードの派生バージョンを個人プロジェクトで管理していくのであれば、基本版の他に、別案件版は1つ程度に留めるのが安全です。よほど工夫しない限り、2つだと多分死にます。死なないまでも、コードのメンテナンスに多大な労力が割かれ、それぞれの開発効率が大幅にダウンしていくでしょう。
本当にエンジン開発のプロ達がいる、大手ゲームメーカーですら、自社エンジンのコード資産メンテナンス、派生バージョンをいかに管理していくか、は至難の技です。派生バージョンが本家チームの手を離れて、いつの間にやら他部署で10年近く拡張され使われ続けることだってありえます。最新のAAAゲームタイトルが、蓋を開けてみるとそのメーカーの旧世代ゲームエンジンの派生バージョンで未だに作られていた、なんて割と聞く話です。そういうことを考えると、Unreal EngineとかFrostbiteはすごいですね。
テストの仕組み・・・。
ほぼ、できてないですね。手動確認です。レガシーです。サバンナでライオンに食われそうです。
一応、商業案件のGLBoostベースの応用アプリの方にはテストの仕組みはあるのですが、GLBoost側は野ざらしと言っていいでしょう。まぁ、CG系ってテストはそもそも面倒臭いのですが、しかしGrimoire.jsの方を見てみると、「ビルドおじさん」を自称するkyasbal氏(「おじさん」ってあなたまだ学生でしょw)が完璧に近いE2Eテストシステムを作ったりしているものだから、もう言い逃れできません。っていうか君たち天才か・・・。
まぁ、自分もどうにかしないと・・・いけませんね。仕組みを作れば、その構築時間を超える恩恵は受けれるはずですから。
ドキュメント・・・。
こちらもなー。いや、ほら。言い訳かもしれませんが。わたしゃほぼ一人でやっているわけですよ。手が回りませんって!
しかし、ここをおろそかにすると使ってくれる人が増えませんので、なんとかしなきゃならないところです。うー。
平たくいうとだな・・・最大の反省点は・・・
コード管理です・・・先生。テストです・・・先生。ドキュメントです・・・先生。
体制面が、技術的にはほぼ私一人でやっているところも影響しているのですが、こうした管理面・開発フロー面の自分の弱さ・適当さがかなりの現実的な辛さになって跳ね返ってきているな・・・と痛感しています。
ただ、CGのコード書いているだけでは、こういう部分は強化できないので、皆さんも、こうした部分のリテラシーは意識的に強化しましょう。
個人的に、ここら周りは私の最大の課題点ですね。来年はがんばる。
個人ライブラリから始まったものを、商用案件で採用いただくということ
はっきりいって、確かに簡単な話ではありません。採用いただくこともそうですが、それよりもずっと大変なのが、お客様の要望に応え続けられるか、です。
「結局、業界で一番メジャーなライブラリをそのまま使った方が、お客様もこっちも幸せだったんじゃないか…」
「未熟な自分のライブラリで要件を満たすのは100年早かった……」
ということが往々にして起こりえるのが、この世界です。「安易に自分のライブラリを商用案件に使うことを提案するもんじゃない」とよく言われますが、確かに一般論としてはそうだと思います。
ただ、私の今回のケースについては、世界で最も使われているWebGLライブラリである「Three.js」を採用するより、GLBoostという個人ライブラリを採用していただいて、「結果、おそらく良かったであろう」という結果にまで、まずまず漕ぎ着けることができました。これは、本当に幸いでした。
(実際、モバイル対応については、Three.jsはiOSデバイスではキャラクターアニメーションで、現状ボーン数がせいぜい25個くらいのモデルしか扱えないのに対し、GLBoostでは最適化を施したことで90個程度まで扱える、などの優位点が一部あります)
成功要因としては、幾つ考えられますが
- 私のCG技術者&ライブラリ作者としての力は、実力的に(初めてライブラリらしいライブラリを作った割には)まぁ無能というわけでもなかった(それなりにやれる実力や伸び代は一応あった)
- 今回の商用案件の要件の方向性が、かなり限定されたものであった。
- 優れたマネジメント(コード管理以外^^;)に恵まれた。
1つ目よりも、2つ目の要因の方が大きいかもしれません。もちろん、機能実装に当たって自分で再調査したり試行錯誤を重ねたことは数知れず、概念として知っていても、実際に実装するとなったら「あ、これじゃダメだったんだ・・・理解が足りてなかったんだ・・・」ということもよくありました。それ以上に、 今回の案件の要件が限られていたからこそ、これら程度の努力や自分の経験値でもなんとかなった 、という言い方もできるのです。
もちろん、決してつまらないものを採用いただいたわけではありません。今Githubに公開されている一般公開版はかなり古いもので、会社のGitlabで管理している商用案件版は、随分とまともになっています。今回の案件の目的を果たせるだけのものです。
私が言いたいのは、いわゆるUnreal EngineやUnityのような「汎用エンジン」を作ることは、目的特化したライブラリを作るよりもはるかに大変だということです。
逆にいうと、「多くの人・企業・案件の需要に応える、そんな汎用要件を満たす仕事を個人ライブラリでやろう」と思うのは確かに無謀ですが、「特定分野・特定顧客に限った用途」であれば、個人の力でもなんとかなる場合・チャンスがあるということです。むしろ、私のケースのように「巨大な汎用エンジン」をかんばってカスタマイズするよりも、全てを知り尽くしている個人ライブラリを拡張していった方が、顧客のニーズに素早く対応できる……そういうことだってありうる、ということです。
そして3つ目。私の場合、優れたマネジメントに恵まれた、ということも大きかったでしょう。私の今回の商用案件は、ある会社さんが受けたプロジェクトにメイン開発者として入る形で関わったのですが、その会社の社長さんは、大手CG制作会社でキャリアを積まれた方で、CGのテクニカルとCG制作現場のノウハウを知り尽くしている方です。その社長さんがマネジメントとクライアント様との仕様調整などをしてくれたおかげで、だいぶ助けられました(まぁ「この機能、入れられるといいよね(チラチラッ」っという圧(笑)も割とあったけどw)。
そもそも、この人がいないと、この案件に会うこともありませんでしたしね。
総じて、私は運にも恵まれたのかも知れません。
## 最後に
ライブラリ開発は、やる過程で非常に多くの学びを得ることができます(この「経験を積める」ということだけを主目的に、お客様に個人ライブラリをむやみに提案してはいけませんが)。
私の場合は、大げさかも知れませんが、人間的にも少し学べたことが多かった気がします。1〜2年前のGLBoostのQiita記事をご覧になった方はなんとなくわかるかも知れませんが、なんともいい加減な気持ちでやっていたり、ライブラリ作ってるというだけで安易にドヤったりしたものです(作るだけなら誰でもできるっちゅうの!)。痛いですねw
今、私が感じているのは……お客様やGithub版のユーザーさんに使っていただいていることへの感謝と、そして、自分なりの責任です。いかにスマートに開発を継続・改善していくべきかについてはもちろんのこと。最近では、製品に採用いただいているライブラリ作者がどんなやつなのか、「あ、こんな奴なのか・・・大丈夫かよ」なんて思われないように、SNSでの発言も気をつけるようになりました(その戒めが、ようやくちゃんと機能し始めたのは、ほんとここ1、2ヶ月ですが笑)。
人間、責任を引き受けると……自然と謙虚に、慎重になっていく……いや、ならざるを得ないのかも知れません。
何れにしても、皆さんに言いたいことーー「ライブラリ開発」はやっぱりやり甲斐がハンパないです。商用案件に採用されたりしたら、もしかしたら「楽しい -> 地獄」な展開ももしかたらあるかもだけど、でも、多くのことを学べます。
そして、「ライブラリ開発を一人で始め、やがて人や企業に使われるようになり、バージョン管理やらロードマップ計画までもできるようになり、ライブラリのコミュニティを徐々に育てていける」まで、こなせるようになった人は、どこに行ってもおそらく引っ張りだこのエンジニアになれる、ということも言えます。私はまだまだその道の序盤をのろのろ歩いているのにすぎませんが、踏破できるといいな、と思ってます。
また、単にそんな大それた話でなく、「技術を素早く習得したい」という目的のためだけでも、個人ライブラリ開発は非常に効果があります。
それまで概念的に知っているつもりだったCG技法も、実装してみる段階になって、実際には自分にまだ欠けていた(実装するにはさらに調べないといけない)知見があった、なんてことはザラです。しかも、それを他の人に利用しやすい形で提供しないといけないわけですから、あらゆる方面で検討や調査、試行錯誤を重ねることになります。人に教えるのと同じか、もしかしたらそれ以上に技術習得の近道と言えるのが、ライブラリ開発かもしれません。
というわけでーー皆さんも、ライブラリ開発、してみませんか?
PS
「今の所、商業案件のお客様は一社だけ」と前述しましたが・・・。それはその部分の文章を書いていた頃の話で、つい最近、なんと2社目が来てしまいました。
採用2例目、ありがたいお話ですね。なにこの超展開・・・。