月 の 上

2020年読んだ本、良かった本

去年買った本のなかで良かった物を紹介する。

良かった本3選

SF映画タイポグラフィとデザイン

SF映画のタイポグラフィとデザイン

SF映画のタイポグラフィとデザイン

  • 良かった度: ☆☆☆☆☆
  • オススメ度: ☆☆☆☆☆

淀屋橋ジュンク堂でたまたま見かけて購入。去年読んだ本の中で一番面白かった。

SF作品における定番フォント "Eurostile Bold Extended" をはじめ、いわゆる「SF風デザイン」がどのようにして形作られてきたのか、歴代の名作SF映画を振り返りつつまとめた本。 SF映画とデザインというテーマで書かれた書籍では「SF映画で学ぶインターフェイスデザイン」が有名だが、あちらがコントローラー、音声、VR/ARといったインターフェイスの要素ごとにSF映画を分析しているのに対し、本作では2001年宇宙の旅スタートレック、エイリアンなど、SF映画史のマイルストーンとなる作品の1カット1カットにあらわれるフォントやデザインを異常な情熱で調べ上げることで、「SF風デザイン」の進化の歴史を辿っていく。

ライトな文体で書かれており図も多いので、文章はとても楽に読めるんだけど、いかんせん情報量が多く、ググりながら読むことになるので、ページ数の割になかなか読み終わらなかった。 例えば、『エイリアン』のパートでは、タイトルロゴが表示されるカットに5ページも費やして、エイリアンのロゴがその後のSF映画に及ぼした影響を解説してくれる。

f:id:amagitakayosi:20210126143806p:plain

扱うテーマも面白いけど、この本の一番の特徴は、著者の異常な知識量とオタク的情熱あふれる文章だ。 デザインやタイポグラフィに興味のない人でも楽しめると思う。 ただ、語り口があまりにも早口オタクすぎるので、それが肌に合わない人はいるかも……?

有害無罪玩具

有害無罪玩具 (ビームコミックス)

有害無罪玩具 (ビームコミックス)

  • 良かった度: ☆☆☆☆☆
  • オススメ度: ☆☆☆☆★

新都社で連載していたチラシの裏さんの短編集。出版されたのは一昨年?

この人の漫画は、不死者、山人、平行世界など、「向こう側」の存在を描いた物が多い。 どの作品でも、世界の設定や、その世界に住む人のモノローグが淡々と書かれているんだけど、独特のリズムがあり美しい。

語り口にクセがあるので、人によっては読むのが疲れると感じるかも。 Webで公開されている作品もあるので、まずは読んでみてほしい。

tirasimanga.web.fc2.com

愛国とノーサイド 松任谷家と頭山家

愛国とノーサイド 松任谷家と頭山家

愛国とノーサイド 松任谷家と頭山家

  • 作者:延江 浩
  • 発売日: 2017/03/08
  • メディア: 単行本

  • 良かった度: ☆☆☆☆★
  • オススメ度: ☆★★★★

図書館でたまたま見つけてKindleで購入。 戦前の右翼政治結社から松任谷由実に連なる、昭和の文化人の人脈をメチャクチャ駆け足で紹介する本。 カバー範囲が異常に広く、三島由紀夫楯の会の制服をセゾングループ堤清二が提供した経緯とか、重信房子が逮捕されたときに娘は頭山家の世話になったとか、そういう小話が無限に出てきて、Wikipediaのリンクを高速に辿っていくような体験ができる。

ただ、Amazonのレビューでも言われているけど、無反省なノスタルジーとでもいうか、昭和のブルジョワの価値観について無批判にかかれている箇所が多く、文章のテイストがかなり厳しい。 僕は国粋主義にトラウマがあるので所々吐きそうになりながら読んでたけど、それでも「昭和の価値観」をなんとなく追体験できた気にになれるので、耐性のある人にはオススメできる。

2020年に読んだ技術/デザイン関係の本

Clean Architecture

ボブおじさんの長年のキャリアから導き出された、設計上のベストプラクティスをまとめた物。 いわゆる「クリーンアーキテクチャ」(オニオンアーキテクチャ)はちょろっとしか出てこないので注意。

Amazonのレビューで言われてるほど昔話オジサンという感じではない。ぼくが昔話好きなだけかも知れないが……

ゼロから作るDeep Learning

ゼロから作るDeep Learning ―Pythonで学ぶディープラーニングの理論と実装

ゼロから作るDeep Learning ―Pythonで学ぶディープラーニングの理論と実装

  • 作者:斎藤 康毅
  • 発売日: 2016/09/24
  • メディア: 単行本(ソフトカバー)

名著。

オブジェクト指向UIデザイン

オブジェクト指向UIの紹介とケーススタディオブジェクト指向の歴史について書かれた本。

タスクベースのUIではなくオブジェクト指向UIを採用することで得られるメリットや、実際の設計プロセスを学べる。 帯に「銀の弾丸」とあるが当然そんなことはなく、エンジニア目線で見ると違和感を覚える箇所もあるが、新規にUIを設計するときには本書の内容を頭の片隅においておくと良さそう。

Webフロントエンドパフォーマンスチューニング

案件でパフォーマンス改善に詰まったので購入。 ちょっと古い本だけど、いわゆるWebフロントエンドにおけるパフォーマンス問題とその対策がまとまってて、読みやすいし実践にも生かしやすい。 ただ、こういうプラクティスは数年で新しい指標が出てきたりするので(最近だとWeb Vitalsとかあるんでしょ?よう知らんけど)、定期的に新しい情報をまとめた本が出てくれると嬉しいな。

あと、個人的にリリース前のパフォーマンス改善チェックリスト作りたい。

デザイナーの英語帳

デザイナーの英語帳

デザイナーの英語帳

去年からフリーランスになったので、コミュニケーションに役立つと考えて購入。 プロダクトデザイン/UXデザインで使われる英語表現をまとめたもの。 単語帳形式ではあるんだけど、用例と解説が参考になるので、辞書的に使うというよりは通読したほうが身につきそう。

駅をデザインする

駅をデザインする (ちくま新書)

駅をデザインする (ちくま新書)

ちくま新書Kindleセールで購入。 駅の空間デザインからサイン(ホーム番号や方向案内など)の作り方、ブランディングまで、駅がどのように作られるかを、国内だけでなく海外の事例も交えて解説している。 普段利用している駅がどういう意図でデザインされたか考えるきっかけになり、とても楽しめた。


2020年はほとんど自宅で仕事することになり、移動時間が減ってしまったので読書にあてる時間が少なくなってしまった。 今年も引き続き自宅作業メインになりそうだけど、ちゃんと勉強したりコンテンツを楽しむための時間を作れたらいいな~

VIRTUAL ART BOOK FAIR 2020のWebGL開発を担当した

f:id:amagitakayosi:20201127130147p:plain

2020-11-16〜2020-11-23 に開催された VIRTUAL ART BOOK FAIR の Web サイト開発を担当した。
また、イベントの一環として、開発の裏側についてYoutube Liveで話したりした。

https://2020.virtualartbookfair.com/

www.youtube.com

この記事では、プロジェクト参加の経緯や開発のすすめ方、僕が技術的に頑張ったことについて振り返る。

VIRTUAL ART BOOK FAIR とは

毎年開催されている TOKYO ART BOOK FAIR のバーチャル版。

TOKYO ART BOOK FAIR (以下 TABF) は、アートに関する本や ZINE 等、一風変わった書籍を扱う即売会である。 芸術系のコミケのようなものだ。 TABFは2009年から開催されており、去年は東京都現代美術館で2万人を超える来場者が訪れるなど、非常に大規模なイベントになっている。 今年も東京都現代美術館での開催を予定していたようだが、コロナ禍の影響でリアルでの開催を見送り、代わりにWebブラウザで閲覧できるバーチャル会場を作って開催することになった。

3D部分は当初Unityで作ることも検討されたが、Web部分との連携やモバイル対応の難しさなどからWebGLを採用することになり、WebGL活動をしている僕に依頼が来たのだった。 僕はすでに他の案件で週の半分以上が埋まっていたので週5で参加する訳にはいかず、10月中旬までは週1で、最後の一ヶ月は他の案件を調整して週3ペースで参加することになった。

世界観ができるまで

僕が参加した9月頭の時点では、「エントランスは東京都現代美術館」「メイン会場は果物か野菜の表面」という事は決まっていたけど、実際にどういう見せ方をするかは決まっておらず、zoomで皆から寄せられるアイデアに対して、実装の難しさを判断したり、逆にこういうアイデアもありますよね、といった実装者観点の提案をしていく形になった。

f:id:amagitakayosi:20201127153311p:plain
ブース毎にテーブルや壁のレイアウトは異なる

メイン会場は、当初は一人称視点でブースを歩き回る想定だったが、一覧性をあげたいということでクオータービューになった。 メイン会場ではいわゆる3D的な演出はほとんど無いが、ブースごとにテーブルや壁の配置が異なっていたり、出展者がテーブル画像を工夫してくれたことで、見て回るだけで楽しい空間ができた。 ただ、3D演出がない代わりに、テーブルの配置の仕組みや、画像を大量にロードするための仕組み作りに苦戦することになった。

エントランスの空間には、東京都現代美術館の外側にラファエル・ローゼンダールの作品が展示されている他、特設ページや外部サイトへのリンクとなるオジェクトが多数配置されている。 ラファエル・ローゼンダールの作品展示については、アーティストと直接メールでやり取りしつつ、オブジェクトの配置や見せ方を決めていった。 ユーザーに作品の大きさを感じさせるため、作品にフォーカスしたときには背後に他の作品や美術館が見えるようにした。 また、今回の展示作品はすべて白一色であり、重なったときに形が把握しづらいという問題があったため、サイト全体のテイストと衝突しない範囲でフォグを追加するなどの調整を行った。

f:id:amagitakayosi:20201127120041p:plain
フォグやライトの調整で視認性を確保している

制作の進め方については、ディレクター兼フロントエンド担当の萩原さんのインタビュー記事があるので、こちらもどうぞ。

sb-rs.com

開発の体制

f:id:amagitakayosi:20201126233622p:plain

開発は大きくWebチームとグラフィック、建築チーム、運営チームに分かれて作業を進めた。 3D空間のデザインや仕様については、主に建築チームと僕とで検討し、グラフィックデザイナーに監督してもらうという形で開発をすすめた。

Webサイトの開発に建築系の人が入るのはメチャクチャ珍しいし、僕も建築系の人と一緒に仕事をするのは初めてだった。 最近は建築の世界でもRhinocerosやHoudiniなどの3Dソフトを使うというのは伝え聞いていたが、実際に会話してみると、ライティングやマテリアルに関する話など、3Dの専門的な知識について特に注釈を入れなくても会話が通じることが多くて感動した。

Webチームは開発期間が短かったこともあり、担当分野でスッパリ分業する体制になった。 僕はReact-WebGL連携用のHooksを用意したり、たまにAWSのコンソールで設定をいじったりする以外は、ほとんどWebGL部分だけを開発することになった。

技術的な構成

f:id:amagitakayosi:20201127172038p:plain
構成

  • フロントエンド: Gatsby, Three.js
  • バックエンド: AWS (Amplify, S3, CloudFront, Lightsail)

アプリのホスティングにはAmplifyが採用された。GitHubにpushすると自動でstaging環境がデプロイされて便利。 環境構築は全てバックエンド担当の黒田さんがやってくれた。

出展者のデータはLightsail上のWordPressで管理し、フロントから取得するデータは全て事前にJSONに吐き出してもらった。 フロントとバックエンドは完全分業だったので、APIの事を考える必要がなくてやりやすかった。

技術的に頑張ったこと

文字表示

f:id:amagitakayosi:20201129220047p:plain
WebGL内で描画されているテキスト

今回はブース名やリンクの表示のため、テキストを3D空間上に描画する必要があった。 WebGL内にテキストを描画する方法はいくつかあるけど、今回は

  • OSに関わらずフォントを指定したかった(Univers + ヒラギノ
  • 使用する文字が限られていた

という理由から、MSDFを事前に生成して表示する方法を採用した。 この手法では、フォントのグリフ(字形)の形状を表す画像を事前に生成しておき、シェーダーで描画している。UnityのTextMeshProなどでも採用されている方式だ。 他の手法と比べると、フォントデータを1枚の画像に収められる、テキストを拡大しても誤差が出にくい、といったメリットがある。

f:id:amagitakayosi:20201129135517p:plain
生成されたMSDF(一部)

今回はこれに加え、日本語と英語でフォントを切り替える必要があったため、Fontforgeでフォントを合成している。 全体の流れは以下の通り。

WebGLで文字を表示するその他の方法については、以下の記事が詳しい(英語)。

css-tricks.com

パフォーマンス調整

今回はとにかくパフォーマンス調整が大変だった。

言い訳になってしまうけど、オープン前日までモデルの入れ替えなどで忙殺されており、本来やろうと思っていたパフォーマンス周りの調整まで手が届かなかったので、オープン後にも必死でパフォーマンス改善に取り組むことになったのだった……。 おかげで、現在ではスマートフォンでもある程度快適に閲覧できるようになったはず。

今回、パフォーマンス問題は大きく3種類に分けて考えた。

  • ロード時間
  • FPS維持
  • クラッシュ回避

ロード時間はRAILで言うところのLoadに, FPSは Animationに相当する。

FPS維持やクラッシュ回避については、不要なオブジェクトを無効化するとか、Squooshでテクスチャを軽量化するといった地味な改善で対処した。 また、PC版ではシーン遷移のアニメーションを実現するため、美術館のシーンとブース一覧のシーンを並行して動かしているのだけど、モバイルではメモリ使用量を減らすため、前のシーンを破棄した後に次のシーンを初期化するようにした。

クラッシュは、特にモバイルにおいて、ブース一覧でカメラを移動する際に発生することが多かった。 デバイスをUSBケーブルでPCに接続し、ChromeSafariでプロファイルをとるにしても、一度クラッシュしてしまうとプロファイルも消えてしまうので、原因を探るのに苦労した記憶がある。 結局、他のメモリ使用量対策に加え、モバイル版ではカメラをズームインすることで一度に表示されるブースの数を減らすという原始的な対策をとった所、クラッシュはほとんど発生しなくなった。

ロード時間短縮

並列ロード

初歩的な事だけど一応。

今回、WebGL部分はすべてTypeScriptで書いた。 ゲーム内のオブジェクトやステージはクラスとして表現したんだけど、WebGLのデータはモデルのロードなど、時間のかかる処理を行う必要があり、コンストラクタで初期化を完結することが難しかった。 例えば以下の例だと、プロパティを参照するたびにundefinedを考慮する必要があったり、コンストラクタを呼び出した側で初期化の完了を待つのが難しいという問題がある。

class StageA {
    model: Model | undefined; // 初期化が非同期なので、undefinedが入ってしまう

    constructor() {
        loadModel().then(model => {
            this.model = model;
        });
    }

    loop() {
        this.model?.update(); // undefinedを考慮する必要がある
    }
}

const a = new StageA();
console.log(a.model); // undefined

そこで、今回はオブジェクトの初期化はすべて static async init(): Promise<T> に統一し、初期化処理を async/await で書き下せるようにした。 これにより、 Promise.all で簡単に初期化処理を並列化でき、ワールド遷移のアニメーション処理も async/await で書けて便利だった。

class Game {
    private world: World;

    private constructor(private worlds: World[], worldId: number) {        
        this.world = worlds[worldId]; // 現在のワールドを決定
    }

    /** 初期化 */
    static async init(worldId: number): Promise<Game> {
        // ワールドを初期化
        const worlds = await Promise.all([
            WorldA.init(),
            WorldB.init(),
        ]);

        return new Game(worlds, worldId);
    }

    /** ワールド遷移の処理 (簡略化したもの) */
    async changeWorld(worldId: number) {
        // 現在のワールドの非表示アニメーション
        await this.world.leave();

        this.world = this.worlds[worldId]
        
        // 次のワールドの表示アニメーション
        await this.world.enter()
    }
}
画像を GLB にまとめる

f:id:amagitakayosi:20201130125813p:plain
テクスチャを格納したGLBファイル

ブース一覧画面では、出展者のアイコン、テーブル、壁の画像をすべてロードする必要があった。 全部で約1000枚にもなるため、これを愚直にロードするとリクエストが多すぎて困ったことになる。 開発当初は全て普通にロードしていたので、画像が全てロード完了するまで非常に時間がかかっていた。

最初に考えたのは、CSSスプライトのように画像を1枚にまとめてしまう方法だった。 アイコン画像ひとつの画像サイズを128x128とすると、2048x2048のテクスチャ1枚に収めることができる(2048 / 128 = 16なので、16 * 16 = 256枚までいける)。 node-canvasを使ってアイコン画像を1枚のテクスチャにまとめるスクリプトを書き、実際に試してみたんだけど、今度はFPSが激しく低下する現象にみまわれた。 詳しい原因はわからないが、おそらく1枚のテクスチャのフェッチ回数が多すぎてバスが詰まっていたのでは無いか……。

なんとかして1つのファイルに固められないかな~ということで思いついたのが、GLBファイルを画像アーカイブとして使う方法だった。

今回、WebGLで使う3DモデルはすべてGLB形式でロードしている。 GLBはモデルデータとマテリアル、テクスチャのデータをひとつのバイナリに固めたもので、最近のWebGLアプリでは大抵これが使われている。 GLBはテクスチャを含むことができるので、すべてのブースの画像をGLBファイルにまとめられるのでは?と考えたのだった。

というわけで、画像ファイルからOBJ/MTLファイルを経由してGLBファイルを生成するスクリプトを書いてみた。 OBJ/MTLファイルは3Dモデルを扱う形式の一つ。中身はただのテキストであり、テクスチャは画像ファイルへのパスを書く形式なので、自動生成するのがとても楽だった。。

処理の流れは以下の通り。

  • aws s3 sync で出展者のアイコン、テーブル、壁の画像を手元にダウンロード
  • ImageMagick (mogrify) で 2 のべき乗サイズにリサイズ
  • obj/mtl を生成
  • obj2gltf で GL

生成されたGLBファイルをロードしてテクスチャを利用してみたところ、特に問題なく表示できたので、今回はこれを採用した。 1000 リクエストが 3 リクエストに減ってめでたい。

……と、ブログを書いてる途中に見つけたんだけど、yomotsu/zipLoaderを使えばブラウザで普通にリソースをzipで固めてロードできるんですね。 次回こういう需要があったら使おうかな。

github.com

やってないこと

今回はやらなくても良いと判断したものや、やりたいけど出来なかった事などがあります。

GPU インスタンシング

GPU インスタンシングは、同じオブジェクトを大量に描画することができる実装テクニックである。 雪や炎といったパーティクル表現から、魚や鳥の群れなど、大量のオブジェクトを動かす場面で多く使われている。

今回はテーブル/壁を大量に描画する必要があり、GPU インスタンシングが有効である可能性があったが、

  • ブースの形が一定ではなく、管理が大変そう
  • 他の工夫によって十分な FPS が得られた

という事で採用を見送った。

Tree Shaking

Three.js は一般的な JS のライブラリと比べファイルサイズが大きく、bundlefobia によると GZIP 圧縮なしで 633KB にもなる。 (jQuery は 87KB) このようなファイルサイズの大きいライブラリを使う場合、ライブラリのうち不必要なモジュールを削る "Tree Shaking" というテクニックが用いられる。

Three.js で Tree Shaking を行う方法については以下の記事が詳しい。 この記事によると、Webpack等のプラグインを使うか、Three.js自体のカスタムビルドを行うのが簡単なようだ。

note.com

今回はJS以外のアセットのサイズが大きく、Tree Shakingで得られるメリットは労力に見合わないと判断し、採用を見送った。

感想

TABFはいつか参加してみたいと思っていたが、まさか開発側として参加する事になるとは思っていなかった。 「即売会のプラットフォーム」というWebコンテンツも、ここまで長い期間開催されるオンラインイベントもとても珍しい。 出展者の熱量も高く、簡素な仕組みの上に工夫をこらしたコンテンツが用意されており、個人的な思い出としても非常に貴重な体験となった。

一方、開発者として反省すべき点も多かった。 3D的な演出を提案する余裕があまりなかった事や、リリース前に十分なパフォーマンスを出せなかった事など色々あるけど、冷静に振り返ってみると、根本にあるのは情報共有の問題という気がしている。 スケジュールについては、リリース直前にデータの修正が出た場合に他のタスクにどれくらい影響が及ぶのか、早い時点で共有できていれば優先順位をつけるのがラクだったと思う。 パフォーマンスの問題については、プロジェクト初期に開発陣で最低限のラインや理想のラインなどを決めておき、例えば定期MTGでLighthouseスコアを見ながら議論するとか、皆で問題を共有しておけばより話がスムーズだっただろう。

ともあれ、大変だけど楽しい仕事だった。 参加者の皆様ならびに関係者の皆様、ありがとうございました。