hibitの技術系メモ

数学とか3Dとか翻訳とか

PythonとHerokuを使ってTwitterBotを作りました(ソースコード以外編)

 突然ですが先日、以下のようなBotを作成しました。

twitter.com

 Beat Saberという音ゲーについての情報提供を目的にしたもので、タイムラインの情報から以下の内容を自動的につぶやきます。

  • 現在配信中と思われるtwitchチャンネルを複窓で表示するためのURL
  • 一定時間ごとに投稿されたビーセイのハッシュタグがついたツイート(だいたい動画)をまとめてリツイート

 Botを作るにあたって手順や参考にしたページ、躓いた点などをまとめておきたいと思います。同じ内容を扱った記事は複数ありますが、(作成中の私と同じように)初めて作業する人だと既存の記事で想定していないようなポイントに躓いたりするものなので、何らかの形でそういった方々の助けになればと思います。

 一応備忘録がわりに説明文を加えていますが、もしプログラムを触ったことがない*1人が何もインストールされていない状態から始めるとしたら最低限の手順書になるように書きました。OSはWindows 10 64bitを前提としていますが、もし手順を参考にされる方がいればOS等は適宜読み替えてください。

目次

Botアカウントの作成

f:id:hibit_at:20190728214538p:plain

やること:TwitterBot用の新アカウント作成

 何はともあれアカウント登録。

APIキーの取得(Twitter開発者登録)

f:id:hibit_at:20190728214616p:plain

やること:ここで登録&作文

 ここから開発者っぽい領域に入っていきます。Twitterのアカウントとは別に、Twitterのdeveloper(開発者)としての登録をします。開発者になるとAPIキーというものをもらえます。

 APIキーについての詳しい説明は省きますが(というか私も詳細をわかっていない)、普段はTwitterのアプリやWebブラウザを通してしか見られないツイート内容やフォロワー数といったデータを、直接もらったり操作したりするためのパスワードのようなものです。これによってBotに色々させることができます。もちろん、誰にでもそんなパスワードを渡したら悪用されて危険なので、信頼できる作文を書ける開発者にのみ渡されます。

 そう、APIキーをもらうためには作文poemが必要なのです。それも英語の!(一応日本語でもできるらしいです)

参考記事

Twitter API 登録 (アカウント申請方法) から承認されるまでの手順まとめ ※2018年9月時点の情報 - Qiita

 なお注意点として、APIキーで操作できるアカウントはログインしているアカウントのみです。つまりBotを操作するためには、Botのアカウントを作成して、そのアカウントでDevelopersサイトにログインしてDeveloperの申請を行わなければなりません。

 私は最初これに気づかず(気づけよ)作文を2回やるハメになりました。なお、2回とも認証は一瞬(本当に数秒レベル)で降りました。審査に一週間かかるという噂はなんだったのか。

Pythonのインストール

f:id:hibit_at:20190728214820p:plain

やること:ここでインストール

 無事にAPIキーが取得できたら、いよいよそれを用いて実際にTwitterを内部的にアレコレするプログラムを作っていきます。その気になればどの言語でもできるんでしょうけど、Pythonが色々ライブラリも揃っていて情報も豊富なのでそれを使っていきます。上のURLには最新のPythonが公開されているはずなので、クリックした先にある「Windows x86-64 web-based installer」でインストーラがダウンロードできます。

f:id:hibit_at:20190728215011p:plain

 なお注意事項として、Pythonをインストールする時の注意点として、環境変数チェックボックスを入れましょう。これがないとコマンドプロンプトpythonpipといったコマンドが使えません。特にpipが使えないのは致命的です。

参考記事

Pythonインストール(Win10)編 - Qiita

Pathを通すとは、環境変数とは - Qiita

Pythonモジュールのインストール

やること:コマンドプロンプトで以下のコマンド

pip install requests_oauthlib

 Pythonは色々なことができるプログラム言語ですが(わかったようなことを言う)、Python自体にそのすべての機能が備わっている訳ではありません。色々な機能を持ったモジュール(部品)として公開されており、プログラムごとに必要な機能を応じてインポートする必要があります。また、標準ライブラリに含まれていない機能は必要に応じてダウンロードしインストールする必要があります。

 例えば今回のBotでは、APITwitterに投げてデータを受け取る……という操作を行いますが、このような機能はPythonに備え付けられていませんし、まさかフルスクラッチでかける訳がありません。なので、外部プログラム(モジュール)を利用してそれを簡単にできるようにします。

 で、このモジュール、結構いっぱいあります。今回のBotだけでも以下のモジュールが必要になりました。

  • json --- JSON エンコーダおよびデコーダAPIを通したやりとりは基本的にjsonというフォーマットのデータになるため、これが必要です。
  • sys --- システムパラメータと関数。絵文字のエンコードに使います。
  • codecs --- codec レジストリと基底クラス。絵文字のエンコードに使います。
  • datetime --- 基本的な日付型および時間型。完成したプログラムには使いませんが、デバッグの時に使いました。
  • requests_oauthlib --- OAuth認証のためのライブラリ。これがないとTwitterとのデータのやりとりができません。これだけは別途インストールが必要。

Pythonプログラムの作成

やること:ソースコードをかく

ソースコードについては近日中にQiitaに公開し解説も加えるつもりです。目下、機能改良&コード整理中なので……。

 いよいよプログラムを書いていきます。プログラムを書くことはそれこそメモ帳でもできますが、エディタを使った方がインデント(プログラムの入れ子関係を示すための文頭開始位置)を自動で揃えてくれたり、シンタックスハイライト(要素ごとに色を変えてプログラムを読みやすくする)をしてくれたりするので、その方が書きやすいでしょう。私はPythonとセットになっている純正のエディタで特に不満がなかったのでそれで書いていました。

ローカル環境での自動化

f:id:hibit_at:20190728221341p:plain

やること:Windowsタスクスケジューラーでタスクの設定

 無事に、BotにツイートをさせるPythonプログラムがかけました。原理上は人間がこれを注意深く時計を見ながら10分に1回ダブルクリックをすれば定期ツイートをしてくれるBotBotじゃねえ)が実現しますが、とてもそんなことはやっていられないのでプログラムにやらせましょう。

 幸い、Windowsにはタスクスケジューラーという備え付けのプログラムがあり、これは定期的に指定したプログラムを実行してくれるというまさにうってつけのものです。

参考記事

タスクスケジューラにpythonを登録する方法(引数付実行) - Qiita

 これで自動化完了! 10分に1回、PC上でPythonファイルが走るようになります……が、毎回Pythonが立ち上がるのは正直目障りです。そして何よりPCを起動し続けていなければなりません。真の自動化のためには、自分のPCとは無縁なところでTwitterAPIを送り続けるような仕組みが必要になりそうです。

 そのための仮想環境を提供するサービスが、次に説明するHerokuになります。

Herokuのアカウント作成

f:id:hibit_at:20190728221506p:plain

やること:ここでアカウント登録した後、ここCLIをインストール

 Twitterとかと違ってややとっつきづらいサイトですが、まあ登録していきましょう。このサービスの中にさっきのパイソンファイルを置いて実行してもらう、ということになるのですが、ドラッグ&ドロップとかをするわけではなく操作は基本的にコマンドで行います。開発者はコマンドを使うものなのです。慣れない人は頑張りましょう。

f:id:hibit_at:20190728222156p:plain

 また、先程のpipと同じく、Windowsコマンドプロンプトにherokuを扱うための命令一式をインストールする必要があります。それがCLIです。インストールしていきます。

Gitのインストール

f:id:hibit_at:20190728222034p:plain

やること:ここでインストール

 更にややこしいのですが、Herokuのファイルを管理するためにはGitというシステムを使う必要があるので、それも別途コマンドラインにインストールする必要があります。さっきからインストールするものばかりですね。

参考記事:

自分用 Git For Windowsのインストール手順 - Qiita

Herokuへのデプロイ

やること:専用のフォルダを作成し、コマンドプロンプトで以下のコマンド

mkdir application_folder

でフォルダを作成し、Pythonファイルやその他設定ファイルを格納

cd application_folder
git init
git add.
git commit -m "commit"
heroku login
heroku create application_name
heroku buildpacks:set heroku/python
git push heroku master

※もし実際に上の手順をされる方がいたら、application_folderapplication_nameは自分のアプリの名前に変えてください。

 これでようやくHeroku上にPythonファイル(やその他設定ファイル)を送るための準備が整いました。ローカルで作業用のフォルダを用意して、いろいろファイルを用意して(このあたりの詳細は参考記事を見てください)Herokuにpush(ローカルの作業内容をインターネット上の大本に反映させること)します。Heroku上でプログラムが動く状態にすることをデプロイと言いますが、これでデプロイ完了です。

 なお注意点として、Pythonをデプロイする際には、Python用のビルドパックというものを設定する必要があります。ブラウザから設定することもできますが、コマンドでも設定できるので、上のコマンドではそれも一括してやっています。

 私はこのデプロイを通すときに何回やってもエラーが出てデプロイできなかったので困り果てていましたが、知人に相談したところ 「requirements.txt」の名前を「requirement.txt」にしていたということが発覚しました。お恥ずかしい……。これも誰かの参考になるかもしれないので言いますが。

参考記事

簡単!Herokuで動くTwitter botをPythonで実装する - Qiita

Herokuスケジューラの設定

f:id:hibit_at:20190728222521p:plain

やること:HerokuスケジューラでPythonファイルを定期的に実行するようにする

 長かった戦いにも終わりが訪れようとしています。後はHeroku上のスケジューラで、デプロイしたプログラムを定期的に走らせるようにすれば自動Botの完成です。Herokuにはスケジューラというアドオンがあるので、それを設定します。Herokuのブラウザでも設定できますが、以下のコマンドでも開くことができます。

 なお、Herokuスケジューラは無料のアドオンですが一応クレジットカードを登録する必要があります。

f:id:hibit_at:20190728222625p:plain

 これで定期的に自動ツイートするBotが完成しました!

*1:コマンドプロンプトの説明はないですが、もし知らなければググってください

数学が先かプログラムが先か、あるいはよりよい回文数式の可能性

 今日も元気にtwitterをしていたら、以下のようなツイートが目に入ってきました。

 これを見た時に

  • 他にもこの形で成り立つ回文数式はないか
  • なぜ1〜9を1つずつ使っていないのか、もしかして使えないのか

 と感じるのは、健康的な成人男子なら当然の衝動*1かと思われます。検証していきましょう。

他にもこの形で成り立つ回文数式はないか

 もし私に無限の時間と体力があるなら、紙とペンを持ち出して一つずつ数を書き出して検証していくでしょうが、それを行おうとすると9^{9}=387,420,489回の試行が必要になります。1試行10秒で終わるとしても約1,076,168時間もかかっています。プログラムにやってもらった方が良さそうです。

 私はこういう、数を並べる系のプログラムはVBAでささっと書いてしまいます。何も考えずに実装したら以下のようになるでしょうか。

Sub zatsu()

col = 1

For a = 1 To 9
For b = 1 To 9
For c = 1 To 9
For d = 1 To 9
For e = 1 To 9
For f = 1 To 9
For g = 1 To 9
For h = 1 To 9
For i = 1 To 9

    If (100 * a + 10 * b + c) + (100 * d + 10 * e + f) * (100 * g + 10 * h + i) = (100 * i + 10 * h + g) * (100 * f + 10 * e + d) + (100 * c + 10 * b + a) Then
        col = col + 1
        Cells(col, 1) = a
        Cells(col, 2) = b
        Cells(col, 3) = c
        Cells(col, 4) = d
        Cells(col, 5) = e
        Cells(col, 6) = f
        Cells(col, 7) = g
        Cells(col, 8) = h
        Cells(col, 9) = i
        Cells(col, 10) = (100 * a + 10 * b + c) + (100 * d + 10 * e + f) * (100 * g + 10 * h + i)
        Cells(col, 11) = (100 * i + 10 * h + g) * (100 * f + 10 * e + d) + (100 * c + 10 * b + a)
    End If

Next i
Next h
Next g
Next f
Next e
Next d
Next c
Next b
Next a

End Sub

 ……ネスト深すぎるわ!

 パソコンは人間に比べたら作業が早く忍耐強いですが、それでもこれをマトモに計算させたら(私のPCでは)5分ぐらい固まります。このような単純労働を強いるのは基本的PC権の侵害であると言えるでしょうし、何より桁数が増えた時に計算量が爆発してしまいます*2

 雑にプログラムを組むよりも、人間側が工夫して無駄な計算をさせないようにした方がよさそうです。この段階になって初めて人間が紙とペンで数学する必要が出てきます。

 さて、件の回文をabc+def \times ghi = ihg \times fed+cbaという形で変数を当てはめると、以下のように整理できます。

f:id:hibit_at:20190708220734j:plain

 これがa〜iが満たすべき条件です。各項の成分が独立であればすべての係数が0がその条件になりますが、今回はそうではないので各項同士が相殺するという状態も考えなければいけません。(後日追記)ここら辺、大きな勘違いがあり、以下の場合は限られた一部ということがわかりました。訂正いたします。

 つまり、以下のようになります。

f:id:hibit_at:20190708220746j:plain

 これをプログラムにも反映させていきましょう。それぞれの条件でプロシージャを分けて、条件を満たさなければ途中で計算を打ち切るようにするだけでもだいぶ計算量を省くことができます。

Sub step1()

col = 1

For d = 1 To 9
For g = 1 To 9
For f = 1 To 9
For i = 1 To 9
    If Abs(d * g - f * i) = 1 Then
       step2
    End If
Next i
Next f
Next g
Next d

End Sub
Sub step2()

For e = 1 To 9
For h = 1 To 9
    If e * (g - i) + h * (d - f) + 10 * (d * g - f * i) = 0 Then
        step3
    End If
Next h
Next e

End Sub
Sub step3()

For a = 1 To 9
For c = 1 To 9
    If d * g - f * i + a - c = 0 Then
        step4
    End If
Next c
Next a

End Sub
Sub step4()

For b = 1 To 9
    col = col + 1
    Cells(col, 1) = a
    Cells(col, 2) = b
    Cells(col, 3) = c
    Cells(col, 4) = d
    Cells(col, 5) = e
    Cells(col, 6) = f
    Cells(col, 7) = g
    Cells(col, 8) = h
    Cells(col, 9) = i
    
    Cells(col, 10) = (100 * a + 10 * b + c) + (100 * d + 10 * e + f) * (100 * g + 10 * h + i)
    Cells(col, 11) = (100 * i + 10 * h + g) * (100 * f + 10 * e + d) + (100 * c + 10 * b + a)
Next b

End Sub

 実際に回した結果がこちら。10秒ぐらいで計算できるようになりました。

f:id:hibit_at:20190708213138p:plain

 あまりにも多すぎて載せきれませんが、なんと11,520617,598通りもあります。まあ、以下のような4つの組み合わせは、文の形は違えど実質的には同じものと言えるかもしれませんが。

f:id:hibit_at:20190708215142p:plain

 それでも2,880154,400以上の*3通りあるので、この形の回文数式にあまりありがたみはなさそうです。

f:id:hibit_at:20190708213001p:plain

 冒頭のツイートにあるやつも出てきました。

なぜ1〜9を1つずつ使っていないのか

 計算の過程で、1〜9を使っている数式も普通に出てきました。例えば以下のようなやつ。

152+483 \times 769 = 967 \times 384 + 251

※両辺はともに371,579

 なお、このようなタイプ(1〜9を満遍なく使っているタイプ)は全部で4452通りあるようです。上で述べた補正をしても1113通りなので、わりとありますね。回文数式の例としてはこういったものの方がベターではないでしょうか?

あとがきポエム

 個人的には、ある計算を行いたい場合、数学(紙とペン的な意味で)とプログラムのどちらを使うかという問いかけにあまり意味はないと思います。手計算で大変そうだったらプログラムを組む、プログラムの最適化が必要そうだったら人間が考える、という形で相補的に働くものだと思っています。今回のような例はごくごく簡単な例ですが、いつかはもっと難しい問題で圧倒的効率化をした上で圧倒的計算ができればよいなと思いました(小学生か)。

 皆さんもよき数学orプログラミングライフを。

*1:つい筆が滑りましたが、もちろん成人女子いや老若男女においても当然の衝動かと思われます

*2:O(9^{n})で合ってるんでしょうか(自信がない)

*3:例えばa~iがすべてが1であるような場合、バリエーションはないので見かけが4倍、ということにはなりません

VRChatのアバター改変でテクスチャにロゴ等を貼る方法

 VRChatをプレイしているとアバターの服にロゴ(または肌にタトゥー)等のワンポイントつけたいとかそういうことがあると思います。基本的にはテクスチャにロゴを貼り付けるだけなのですが、一手間必要な場合が多いのでその手順を解説していこうと思います。

基本…UVマップとその問題点

f:id:hibit_at:20190704154453p:plain

 以降、ミーシェちゃんのモデル(かわいい)(BOOTHで販売中だよ!)を基準に解説していきたいと思います。

 3Dモデルはメッシュ(ポリゴン)の表面を1枚の平面の「地図」にして、そこに絵を描き込んでいく、という方法でテクスチャ(表面の質感)を表現しています。この地図をUVマップと言います。3次元(X軸、Y軸、Z軸)からなる物体の表面を新たに2次元(U軸、V軸)に転写することからUVマップと呼ばれます。

 しかし、サイコロのような物体でない限り、立体は必ず曲面を持っています。それを平面の地図に「まっすぐ」転写するのは不可能であり、必ずどこかに歪みが現れます。これはUVマップの仕組み上避けられないことです。

f:id:hibit_at:20190704154709p:plain

 これは実際のミーシェちゃんの服のテクスチャです。赤い線がUVマップになります。普段は表示されていませんが、psd(フォトショップデータ)にはレイヤーとして保持されているので、今回はあえて表示しています。この赤い線が実際のポリゴンの1枚1枚*1と対応しています。

 私は以前psdデータがあると色変えがしやすいという記事を書いたことがありますが、本記事のような場合にもpsdデータがあると役立つので、販売モデルはなるべくpsdデータ付のものがいいと思います。やや余談ですが。

f:id:hibit_at:20190704154901p:plain

 ちなみに、あえてUVマップの線を3Dモデルに写してみた図。時々非表示にするのを忘れて素でやってしまうことがある。

 3Dモデル上では自然な線ですが、UVマップ上ではナナメになっていました。

 このテクスチャにそのままロゴをはりつけてしまうとどうなるか。

f:id:hibit_at:20190704155547p:plain

f:id:hibit_at:20190704155630p:plain

 傾いています。というか左右反転しています。UVマップが左に傾いていれば逆に右に傾き、縮んでいれば逆に拡大して表示されてしまいます(数学的に表現すれば、UVマップの逆変換を受けてしまいます)。更に今回は、UVマップを「裏から見たような」形で展開しているので、それの影響も受けています。このようなことはままあります。というか、そういう方が多いと考えてよいでしょう。

 そこでどうするか。

方法1…ロゴを変形させる

 力技かつわかりやすい方法です。UVマップが歪んでいるなら、ロゴもそれに合わせて歪ませればいいじゃない。幸いgimp(無料ツール)は自由変形があるのでそれほど苦ではないです。Shift + Tで出来ます(鏡像反転はShift + F)。ビーくん*2をこう……

f:id:hibit_at:20190704155810p:plain

 変形!

f:id:hibit_at:20190704160138p:plain

 解決!

 ただこれには限界があって、関わるポリゴンが少ないかつ曲率が小さければ大丈夫なのですが、どちらかが複雑になってくるとUVマップの歪みが大きくなってきます。直線的な自由変形では対処しきれなくなる……というのもありますが、例えばある箇所と別の箇所で対応するポリゴンのサイズが数倍、という状態になると、結果として表示される解像度にもその差が現れてきます。見栄えが少し悪くなるかもしれません(ロゴの一端がぼやけるなど)。

 場合によっては、UVマップ自体を直す、ということができるので、次の項目で説明します。

方法2…UVマップを修正する。

 こちらの方がアプローチとしては根本的ですね。社会が間違っているなら、社会に合わせて自分を殺すのではなくむしろ社会を変えていく、そんな男気を感じます。ただ、後述するデメリットのため、いつでも使える訳でもないので注意が必要です。

 この方法では、みんな大好きBlenderを使う必要が出てきますが、がんばりましょう。

 さて、Blenderにミーシェちゃんのfbxを読み込ませていきます。

f:id:hibit_at:20190704160526p:plain

 初期状態ではよくわからないタイムラインみたいのが表示されていると思うので、ここにUVマップを表示するようにします。

 ミーシェちゃんは既にUVマップが展開済なので、服のオブジェクトを選択して編集モードにすればすぐにUVマップが出てくるはずです。

f:id:hibit_at:20190704160735p:plain

 この内4枚を正方形にしたいと思います。

f:id:hibit_at:20190704160934p:plain

 服のUVマップの内、真っ直ぐにしたい辺を選択して、縦にしたいならば「X軸揃え」、横にしたいならば「Y軸揃え」をします(ショートカットキーはW)。お前さっきU軸とV軸ゆーたやん。なんでX軸とY軸になってるの? 私がBlenderに聞きたいよ……。

f:id:hibit_at:20190704161147p:plain

 後は細かい調整をすれば(画像を貼り付けるという意味では)綺麗なUVマップが完成します。辺を動かす時に周りも動いてしまうようであれば「プロポーショナル編集モード」になってますので注意。

 もちろんこの方法にはデメリットもあって、もともと完成されていたテクスチャ(今回の例であれば服のシワなど)の方が歪んで表示されてしまいます。影響する範囲が小さければほとんど気にならないですが、大きければ無視できない範囲になるかもです。服のつなぎ目等のテクスチャを巻き込んでしまったらその時点でアウトでしょうね。UVマップ、ムツカシイネー。

 皆様もUVマップの性質を理解しながら良き3Dライフを。

*1:正確には1ポリゴンは三角形ですが、2ポリゴンの四角形を基準にした方がわかりやすいので、以後「1枚」は「四角形」と考えてください

*2:このロゴのキャラの名前。著作権はかろうじて私にあります。

(主にBeat Saberを想定した)バーチャルモーションキャプチャーを調整するための方法

www.youtube.com

 バーチャルモーションキャプチャー(以下:VMC)を用いると、上に貼った動画のように、あたかもアバターがBeat Saber(以下:ビートセイバー)をプレイしているかのような動画が撮れます。詳しくは以下の手順を参照。

sh-akira.github.io

 しかし何も調整を加えないと、

  • 目を見開きっぱなし
  • 手の平ひらきっぱなし
  • 手が捻じれる

 という問題が発生するのでそれぞれ対策を解説していきたいと思います。

目を見開きっぱなし

 fbxから変換したままのVRMでは、VMCで表示されるアバターはまばたきをしません。目を見開きっぱなしです。別に気にならないと言えば気にならないレベルですが、まばたきがあると一気に「生きてる感」が出るので設定していきましょう。

 正確には、VMCにはデフォルトでまばたきの設定が入っているのですが、素のVRMデータにはそれに対応する「BLINK」というブレンドシェイプが設定されていないので、まばたきの命令は出していてもその動かし方がわからないという状態です。

 では、「BLINK」の設定をしていきましょう。VRMを出力したということはUnityに以下のようなアセットが設定されているはずです。(既存のVRMをいじる場合でも、UniVRMを読み込んだUnityにVRMファイルを読み込んだ時点で生成されます)

f:id:hibit_at:20190321215519p:plain

 その中の「VRM名.BlendShapes」フォルダにある「BlendShapes」を選択すると、インスペクタに色々な表情に対応するブレンドシェイプの一覧が表示されます。

f:id:hibit_at:20190321215827p:plain

 その中の「BLINK」を選択します。

 売り物のアバターだったら普通、対応するシェイプキーを作っているので、その値を「100」にします。

f:id:hibit_at:20190321215937p:plain

 シェイプキーは対応するメッシュに格納されています。三角を押して展開しないとシェイプキーの一覧が表示されないので注意。大体のモデルは「face」とか「body」とかそんな名前になっているはず。

f:id:hibit_at:20190321220042p:plain

 これで設定完了。もちろん、シェイプキー(頂点の変形)が作られていることが前提で、何もシェイプキーを設定していないモデルが魔法のようにいきなり瞬き出したりはしないですからそこは注意です。

f:id:hibit_at:20190321230415p:plain
図にするとこんな感じ

 …という流れですね。この流れは瞬きに限らずあらゆる表情付けに共通なので覚えておくとよいかもしれません。また1つのブレンドシェイプに複数のシェイプキーを組み合わせることもできます(なので「ブレンド」シェイプな訳ですね)。

手の平ひらきっぱなし

 VMCで読み込んだモデルは、手を開いた状態で持ち込まれます。コントローラーのタッチパッドを触ると色々手の形が変わりますが(VRChatと同じ制御です)

f:id:hibit_at:20181220024849p:plain
VRChatの表情制御の図

 まあ普通にビートセイバーをやっていればここはまず触らないので、多くの場合は手の平を開きっぱなしにしながらセイバーを振り回す訳です。

f:id:hibit_at:20190321220412p:plain
手が開いている様子

 これはこれで超能力者みたいな趣がありますが、やはりセイバーを握りしめていた方が自然でしょう。

 なのでVMC側で握りっぱなしにするようにします。

f:id:hibit_at:20190321220551p:plain

 ここの「ショートカットキー」をクリック

f:id:hibit_at:20190321220706p:plain

 色々と手の形を動かす命令があります。もったいないですが、すべて削除しましょう。

f:id:hibit_at:20190321220835p:plain

 そして新たに命令を作りましょう。上の図の「カスタム」が新しく作った命令になります。「ハンドジェスチェー追加」を押して、以下のようなセッティング。初期設定からいじった部分をすべて赤丸つけています。

f:id:hibit_at:20190321221055p:plain

 発動するキーは何でもいいのですが、私はキーボードの「S」にしてます。一度Sを押したら最後、この手は二度と開かれませんがまあビートセイバーをする上でならそれぐらいがちょうどいいです。この設定を一回してしまえば、「ショートカットキー」の右下にある「カスタム名称(保存でプリセットへ追加)」という所でこのセットをまるごと保存・呼び出しできるので、以降はワンクリックで出来るようになります。

手が捻じれる

 これは一部の人向けの問題になります。アバターの手の向きについては、VMC側である程度補正が入るっぽく、コントローラーを自然に持っている限りはいいのですが、それ以外の角度で持つ場合、手首が不自然に捻じれてしまいます(あらかじめコントローラを通常じゃない角度で持ってキャリブレーションしてもこの現象は発生します)。

f:id:hibit_at:20190321231142p:plain

 これは私が逆手で構えた時のポーズ。

f:id:hibit_at:20190321231436p:plain

 現実はこんな感じになっています。VMCの機能や(もしくはポリゴン自体をいじることにより)「手の角度」自体は補正できますが、問題はこれ。IK(インバースキネマティクス、手の角度から肘の動きを計算すること)が狂ってしまうため、ポーズが狂ってしまうのです。

 え? 逆手でプレイするのなんてお前だけだろって?

 確かに逆手でプレイするのは世界で私含めごく一部(最近は片手逆手の人が増えてきました)ですが、実はランカー勢は通常プレイにおいても、コントローラーを特殊な方法で持っているのです。

 それがこれだ!

f:id:hibit_at:20190321221307p:plain (元画像はBeat Saber界隈のDiscordに出回っているものです。作者を存じ上げない、というか特定不可能なため無断転載させていただきますが、問題があることが発覚した時点ですぐに対応させていただきます)

 名付けてB-Grip!

 実は、VIVEコントローラーを普通に持つと先端が大きすぎてアンバランスであるため、とても振り回しづらいです。これは難曲を攻略する上では死活問題です*1。そのためランカー勢(のうちVIVE勢)はほぼこのグリップでプレイしています。しかしこれだとVMCでは手首が裏返ってしまいます。これは全世界共通の問題なのです!

 ではどう直すかについて。

 トラッカーを追加して手の甲にはめましょう。

 今のところこの問題はマネーでしか解決できないのだ。いや、なんかIKをいじれるすごい技術があれば解決できるかもしれんけど……。

f:id:hibit_at:20190321221928p:plain

 ちなみにトラッカーを手の甲にわりあてる場合はVMCのここで割当設定を決める必要があります。自動割当だとコントローラを手だと認識してしまうので……。まあ、これは動画の見栄えにどうしてもこだわるなら、というレベルですね。

 皆様もよき動画撮影ライフを。

*1:ちなみにこれは冗談ではなく、世界のトップランカーの大半はOculusです

2018年+αにやったこと(主に3D)

 4月から3DCGを初めて、いろいろと知見が溜まってきました。まだまだ勉強中で、走りながら考えるといった感じですが、それらをまとめて書き溜めておかないと、自分がどういうことをやってきたのか、またその過程でどのようなサイトにお世話になってきたのかということを忘れてしまい、技術の基礎が疎かになってしまう感じがあるので、書き記しておきたいと考えております。

 本当は、半年に1回ぐらいのペースでやるべきだったのかもしれませんが(半年前の記憶でさえすでにあやふや……)、もうすでにそのタイミングを逃してしまったので、やや中途半端な時期ではありますが、2018年+αにやったこと、という形でまとめておきたいと思います。

(以下、長いので敬体ではなく常体で記す)

VRChatへのアバターアップロード

 4月。HTC Viveを買ってVRChatに登録する。

 すべての始まり。この頃はトラストシステムがなく、登録したらすぐにアバターアップロードができた。アップロードにあたっては、サンオ (id:san_o)さんの以下のページに大変お世話になった。残念ながらこの記事だけで更新が止まってしまったようだが、この記事だけで千金に値するブログだと思う。

vr-san-o.hatenablog.com

f:id:hibit_at:20190128013636p:plain

 一応こんなものだが。この時期はまだ販売アバターが少なかったので、Unity Asset StoreにあるSuriyunさんのアバターを改変して遊んでいた。VTuberでこの子を使っている方もいたようだ(詳しくない)。下はそのボクセルバージョン。

f:id:hibit_at:20190128015714p:plain

 なお、ボクセルの作成方法についてはサンフィッシュ熊野さんの3DCG出来ない!でもVRChatでオリジナルアバターを作りたい人向けチュートリアルが詳しい。

フルスクラッチアバターの作成

f:id:hibit_at:20190211004556p:plain

 今なお現役のパンダもどき。二面図をモデル作っていくという作り方を日本VTRさんの以下のページから学んだ。

nvtrlab.jp

 元の二面図のパンダからは似ても似つかない謎の生物になったが、これはこれで愛嬌があってよいのではと思っている。

f:id:hibit_at:20190211004235p:plain

 私の外向けのメインアバターであるダークエルフちゃん(一応そういう名前)。見てわかる通り、服装はJamiroquaiVirtual Insanityのオマージュ。この子はJamiroquaiを聴きすぎてしまった結果、自分をアボリジニだと思い込んでしまった悲しい子である。

 特にそのために参考にしたという訳ではないが、高知工科大学が公開している以下のページは基礎的ながらもかなり内容が濃く、これからBlenderを始める人におすすめである。

krlab.info.kochi-tech.ac.jp

 この頃はVRChatで知り合った人達に、とりあえずその日できたアバターを見せては反応をもらったりアドバイスをもらったりという感じで、非常に恵まれた期間だった。あるフレンドのオリジナルアバターを見て「アマチュアでもここまで出来るのか……」という衝撃を受けたのが、アバター作りにチャレンジした一番のきっかけである。部屋にこもってBlenderをいじっていても、そういう気持ちは起きなかっただろう。

改変アバターの作成

f:id:hibit_at:20190211004704p:plain

 6月に作ったメインアバター。お気に入りではあるが、この子は露出度が高いしweebな感じがあるので、一応内向けアバターという位置づけ。この頃になると、フルスクラッチではなく販売素材や配布素材を活かして効率的に作っていく術を覚えるようになる。その時はタイミングが良いことにあるモデラーさんがVRChat向けに素体を配布してくれていたので、ありがたく改変させていただいた(今は広く公開はしていない)。服とか髪とか含めて1週間ぐらいで出来た気がする。

 今(2019年現在)では数百体のモデルがBOOTHを中心に売られている。もしVRChatを始めるのがあと半年遅かったら、自分はアバターを自作までする気になっただろうか? 今のコミュニティの人間と知り合えただろうか? 仮に知り合ったとして、同じような関係になれただろうか? そういうことを考えると不思議な気分になる。

数学の勉強を再開

 思うところがあり数学の勉強を再開した。図形の一般概念であるところの多様体のことを勉強したかったので、入門書として有名な「多様体の基礎」(通称:松本多様体)を購入し、一応一通り目を通した。

www.amazon.co.jp

 ラノベのように読めるということで有名だが、全然そんなことはなかった。位相と集合とか解析学とかあたりの基礎が抜けているっぽいので、そのあたりを補完してからリトライしようと思う。学部時代に数学を真面目に勉強しておけばよかったと後悔しているが、こういうことは思い立った時に始めるしかない。

 他に参考にしたものとして、東工大のサイトに上がっている川平准教授が公開されている多様体の基礎のキソというpdfは非常にわかりやすかった……が、もっと噛み砕いた資料、というか具体的な計算例や可視化例があってもいいかなと思う(というか私が欲しい)。私が多様体の気持ちをよく理解できるようになったら(cf. 多様体愛護協会)「多様体の基礎のキソのきそ」みたいな文を書こうと思う。

 全体的な理解とは別に、特に射影平面については面白い概念だと思ったのでUnityでビジュアライズできないかということに興味が移るが、これは後日レイマーチングを用いて一部成功した。

5次元立方体のバズ

 およそ1年前に投稿した5次元立方体のビジュアライズがなぜか1年経った頃になってバズる。phiさんがテセラクト(4次元立方体)シェーダを販売して話題になった頃だったから、皆様に耐性(?)のようなものがついたのかもしれない。ようやく時代が俺に追いついてきたということか……ということはなく、バズりは時の運の産物である。ただ、コーディングでこの動画を実装できたのはそれなりに面白いことだと思うので、それがある程度認められたようで嬉しかった。せっかくなのでQiitaに解説記事を書いた。

 5次元立方体についての動画はあまりなく、そこにある程度の私の新規性はあると思うが、YouTubeではJos Leysさんが超次元立体について非常に興味深い動画を多数投稿しているので、こういったレベルと比べるとまだ改善の余地がある。今後、UnityとVR/ARデバイスを用いてよりインタラクティブかつ、より体感的に幾何をビジュアライズすることにより新規性を出していきたい。

販売モデルリス

 6月頃から盛んになったアバター販売だが、すごい勢いで増え続けているので誰も整理できていないのが現状である。こりゃあ誰かがリスト化した方がいいぞ、ということでとりあえず作ってみた。最初に20体ぐらいのリストを作って(まだその頃は平和だった)Googleスプレッドシートで共同編集できるようにしたが、結論的にいうとこれは失敗した、というか更新停止をせざるを得なくなった。

f:id:hibit_at:20190129022704p:plain

 主に私とあるフレンドさんとで記入をしていたが、100体を超えたあたりで手が回らなくなってしまった。元から少数が人力管理するには無理のある量だったのだ。

 人力でダメなら機械じゃい、ということで正規表現Google Apps Scriptを勉強してBoothをWebスクレイピングすることを考えた。GASの扱いについてはこのページにお世話になった。ここら辺の経過はQiitaのここにまとめてある。

 しかしこれも結果的には失敗した。「3Dモデル」を機械的に抽出することはできても「どれがアバターで、どんな特徴を持つか」という情報を機械的に抽出するのが難しく、結局人力勝負になってしまうのだ。「3Dモデル」タグを持つ商品は2,000以上にのぼるが、人力で選別処理をするには馬鹿げた量だ。

 「大量のモデル群の整理がついていない」という懸念はたぶん誰もが感じている。例えば、ねこますさんは、作者個人が申請するWebプラットフォームのような構想が良いのではと言っている。これをやってみるのもアリかと思ったが、私がWeb周りに詳しくないのと、構築したところで周知浸透できるのかという点に不安があるので、今はまだチャレンジしていない。

 という訳でこれは未解決問題である。他に個人努力として考えられるアプローチとしては、キカイガクシューを頑張って、Boothの個別ページからアバターか否か・性別・外見をまとめる判別システムを作るとか(できるのか)?

シェーダ

 9月ぐらいからシェーダそのものをいじってみたくなる。最初は簡単な例から……ということで、いくつかのワンライナー改変を考えてQiitaに投稿したら、少し反響があって嬉しかった。その後、マンデルブロ集合固有ベクトルの図示などいろいろなシェーダを自作する。これらの勉強にあたってはちくわ (id:nn_hokuson)氏のおもちゃラボに非常にお世話になった。このサイトなくてはここまでスムーズにシェーダを勉強できなかったであろう。紛うことなき神サイトである。

 11月には、簡単なサーフェスシェーダだが、フルスクラッチで自作シェーダを作った。1行残らずすべてのコードの意味(少なくとも「変更したらどういう挙動を示すか」)を理解しているシェーダである。GitHubリポジトリここ、Qiitaの解説はここ

 また時期は前後するが、VRChatで見かけたFractal Explorerというワールドに感銘を受けて、ああいうのを再現できないかと思った。二次元の座標をもとに組むシェーダだと限界がありそうなので、レイマーチングに興味を持つようになる。その内GLSLとか読み書きできるようになりたい。

セル結合

 12月に書いた、セル結合について積年の恨みを書いた文章がなぜか異様にバズった。

deux-hibi.hatenablog.com

 これはまさに異様で、1,700ブクマ弱を獲得してその日のはてブロ総合トップになるとともに、Twitterのトレンドに「セル結合」が並ぶことになった。まさかここまで広がると思わず極端なキレ芸的文章を書いてしまったので、さすがにフォローした方が良いなと思って後日釈明記事を書いた。これで一躍人気ブログに……ということはなく、その後は細々とした感じに戻ったが、まあこれぐらいがちょうど良いんじゃなかろうか。それはともかく人類は(無意味な)セル結合をやめ……やめてくれると嬉しいな(遠慮)。

ビートセイバー(環境構築方面)

 12月にビートセイバー(Beat Saber)を始めた。単なる娯楽用のゲームを「始めた」と言うのも変だけど。ある晩、もののためしにコントローラーを逆手に持って数曲やってみたら「これは……”来る”……!」と謎の確信を感じ、その日から逆手でしかプレイをしなくなる(過言)。

 その思いを抑えきれず下のようなイラストツイートをしたところ、それを見かけたガチ勢の人から声をかけてもらった。これがきっかけとなってビートセイバーのディープな世界に突っ込んでいくことになる。

 このイラストは今年の初めに購入したiPad Proがなかったら描かなかったかもしれない。思いついた事柄をそのままApple Pencilで表現して数秒でtwitterに投稿できるのはすごいことだ。そう考えると今ビートセイバーを楽しめているのはiPad Proのおかげと言えるのか……?

 やったこととしては、LIVバーチャルモーションキャプチャー(VMC)の導入。このあたりは猫井ゆうなさんの解説ページがわかりやすくとても助かった。MODを用いたカスタムアバターの導入にあたってはのしろぐさんのサイトさんには非常にお世話になった。ビートセイバーに関する情報では、日本で一番まとまっているサイトではなかろうか。神サイトである。MODを使うかVMCを使うかどちらがいいのかというのは非常に難しい問題で、これについては別途記事を書くつもりである。

 なお動画撮影のためだけにトラッカーを買った。VR機器をフル活用するようになってからの方が、肉体の限界とか部屋の狭さとかが気になるようになった気がする。物理的制約から人を自由にするためのVRなのに、本末転倒では?

ビートセイバー(動画投稿方面)

 とりあえず逆手セイバーの可能性を示すために以下のようなマップ(譜面)を作成&撮影した。マップデータはここ

 あと、合同作譜というのもやった。サーバ上にあるマップを協同で編集するというやつで、わりとたのしい。こういうツール・MOD類がユーザー主導でどんどん公開されていくのもビートセイバーの良い文化である。

 せっかくみんなで作っているから、動画もリレー形式でプレイするものを作ってみてはどうかと呼びかけた。タイムシートを決めて、皆から素材動画をもらって動画編集をするということをやった。編集にはAVIUtlを用いた。非常に便利なフリーソフトだが、各種プラグインの導入やメモリ拡張が(ほぼ)必須なのでその点は注意。完成品は以下。

発信し続けること

 やりたいことや面白いと思ったことを発信し続けるのは大事だと思う。ふとしたことがきっかけで……というのはよくある話だが、結局そういうめぐり合わせがないと効率的に成長できないのが普通の人間だと思う。同時に、それと同じぐらい、頑張ったはずなのに上手くいかなかったことや自然消滅したこともあるというのが、普通の人間だと思う。こういうのは時の運が絡むので、こうすれば絶対に上手くいくということはない。

 だからうまい縁を引き当てるまではどんな形であれ、twitterの呟きでもブログでも飲み会の雑談でも何でもいいけど、可能な限りわかりやすく具体的にやりたいことのイメージや希望を発信し続けることが大事かなと思う。

2019年のこれから

 「最終的に何がやりたいの?」ということを簡潔に記すのは難しい(長期的には変わっていくものでもあるし)。現在考えていることを要約すると、世の中の美しいこと、複雑なことは突き詰めれば線形的な要素群に分解できて、いったん線形的に扱えるようにすればコンピュータに一括計算させられるので、プログラムを活かして上手くそれら(美しいことや複雑なこと)をビジュアライズしたい、ということになる。数学もプログラミングもそのためのツールに過ぎないと思っているが、まだまだ両者とも未熟である。

 それとは別に、3DとVR/AR(将来的にはXRの方がより総括的な名称になるのかな?)はまだまだ遊びの余地がたくさんあるので、そういう娯楽的な楽しみも追及していきたい。また、それらが一般化していく中で、編集や作業を効率化していきたいという需要は絶対出てくると思うので、そういった面でもプログラミング能力を活かせたらと思う。ニッチかつマイクロなOSS開発とかあったら参加してみたい(インターネットの詳しい人達おしえて!)。後は十分な食事と睡眠をとって健康に気を付けるぐらいだろうか。

Unityとレイマーチングを用いた射影平面上の二次曲線の可視化

Unityとレイマーチングを用いた射影平面上の二次曲線の可視化
Visualization of conic curve on projective plane using Unity and Raymarching
hibit

f:id:hibit_at:20190130081525p:plain

概要

 二次曲線(楕円,放物線,双曲線)は3次元空間上の円錐に対して投影方向を変えた断面であると解釈できる.筆者は,ゲーム用ミドルウェアであるUnity上で,レイマーチングと呼ばれる技術を用いることにより,3次元空間上の円錐断面がある平面上で二次曲線になる様子,そしてその角度が変化する様子をリアルタイムに観察できるVR空間を作成し,それをVRゲーム「VRChat」上のワールドとして公開した.これにより,それぞれ別の形をしているように見える二次曲線が,射影平面上では無限遠を介して1つの円に対応していることを可視化した.

緒言

 以下,射影平面と二次曲線についての概説を記す.

射影平面

 ユークリッド空間\mathbb{R}^{n}上において,原点を通る直線全体の集合(ただし原点を除く)は射影空間(Projective Space)と呼ばれ,\mathbb{P}^{n-1}と表現される[1].これは直感的には原点を通る直線の「傾き」だけを抽出した世界である.また,これら直線は原点を中心として球に対して対蹠点(反対側の点)を同一視した上で球面上の点に対応しており,それを平面に写したものは,ただの平面に加えて「無限遠で反対側にループする」性質を持つ平面であることがわかる[2].

f:id:hibit_at:20190130082656p:plain 射影平面の構造をシンプルに表現したもの.トポロジーへの招待 2 より引用)

 なお,\mathbb{P}^{2}\mathbb{R}^{3}に埋め込めないことが知られているが[1],自己交差した形では描画できる.そのような例として,例えば以下のようなCross-capと呼ばれる図形がある.

f:id:hibit_at:20190131234825p:plain
Cross-cap(Cross-Cap -- from Wolfram MathWorldより引用)

二次曲線

 楕円,放物線,双曲線はそれぞれ以下のような曲線である.

f:id:hibit_at:20190130081740p:plain (描画にはWolfram|Alpha: Computational Intelligenceを用いた)

 これらは,適当な定数a,b,c,dを用意することにより,xy空間上において, $$ ax^{2}+bxy+cy^{2}+d=0 $$  なる一般式により記述され,二次曲線(または円錐曲線,conic curve)と呼ばれる.

 これらは円錐の断面である正円を平面に写したものの,その角度を変えたものであるといえる.また,これらはすべてある円を異なる射影平面に投影したものであると言える.言い方を変えると,

  • 楕円は傾いた「正円の影」
  • 放物線は無限遠に伸びた「正円の影」
  • 双曲線は無限遠を介して反対側にループする「正円の影」

 であると言える.

 上の事実は円錐を任意の平面で切った断面を計算することにより計算できる.つまり,原理的には,適当な定数p,q,r,sを用意することにより,xyz空間上において,

$$x^{2}+y^{2}-z^{2}=0$$ $$px+qy+rz+s=0$$

 を満たす(x,y,z)を解析的に求めることにより,任意の二次曲線を生み出すことができる.

 しかし,実際にこれらの計算を解くことはかなり煩雑な作業であり,またそうして得られた二次方程式から平面と二次曲線との関係を直観することはやや困難である.そこで,ゲーム用ミドルウェアであるUnityを用いて「円錐が回転する様子」「円錐の延長と,ある平面の断面」を実際にゲーム画面上に描画することで,「任意の二次曲線が正円の影である」ことを可視化した.

 なお,楕円が円の影であることはほぼ自明であるが,放物線と双曲線が,無限遠を概念を加えた射影平面上においては円の影であると言えることについては,後の考察で説明する.

方法

 手法はレイマーチングと呼ばれる技術を用いた.これは,カメラポジションからレイ(探査線)を飛ばすことにより,レイがある条件を満たす時に座標を着色することにより,あたかも空間上にオブジェクトがあるかのような表現をするシェーダ技術である.

 本研究では,やぎり氏が公開しているコード[3]を一部改変し,以下の変更を加えた.

  • 形状関数を球ではなく円錐にした
  • 円錐を回転するようにした
  • 角度によって色に変化を出すようにした
  • 一定の距離を超えるとオブジェクトを描画しないようにした

 なお末尾の「付録」にソースコードを載せている.

結果

 実際に,VR空間上で円錐が回転し,それが平面に投影される様子を一人称視点で撮った動画が以下である.

 中心の球の中では赤・緑・青で着色された円錐が回転している.中心からの角度が同じであれば色相は同じになり,距離は問題にならない.つまり色相は空間上における線に対応する.これがある平面に投影される時,これは二次曲線となり色相は点に対応する.

 なお,見た目の都合上曲線だけではなくその内部も着色している.つまり元の正円における「円周」ではなく「円板」を描画しており,円板の中心に近ければ近いほど発光するような処理を加えている.

 このワールドは「Virtual Projective Plane」として,既にVRChat上にアップロードされているが,パフォーマンスの問題上パブリックワールドにはなっていない.いかに計算負荷を下げてパブリック申請を通すかというのもまた興味深いテーマであるが,それは今後の課題としたい.

考察

 上の現象を,射影平面の展開図上で考えてみる.射影平面は以下のようなトポロジーを持つ展開図を考えることができる[2].

f:id:hibit_at:20190203132331p:plain トポロジーへの招待 2 より引用)

 これらのつなぎ目を介して平面がループが連続する「大きな」平面を考える.家庭用RPG(つまりドラクエやその類)の世界であればこれは同一の平面が繰り返し続くだけだが(このような世界地図はトーラスと呼ばれるドーナツ型によって現実に再現できる),射影平面においてはループをする度に反対側に行く「ねじれ」が生じる.以下,射影平面の展開図にある同一の円が位置を変えながら投影される様子を示す.

f:id:hibit_at:20190203211808p:plain

 展開図の黄色の部分が観測者のいる世界であり,Unityの画面ではグリッドの刻まれた黒い床がそれにあたる.無限遠を越した後の世界では「ねじれ」が生じ,展開図ではそれを灰色の部分として示している(円のR→G→Bの方向が時計回りから反時計回りになっていることに注目されたい).射影平面の展開図はこのように「ねじれ」を繰り返しながら永遠にループするものであり,1方向に限ればトポロジーとしてはメビウスの輪と同一となる.

 さて,床に投影された影に着目したい.円錐の直下に投影される円は当然ながら円となる.

 円錐の回転が進んだ時の様子を見る.

f:id:hibit_at:20190203212300p:plain

 この時,影は放物線となり,展開図では円が無限遠で接している(乱暴な定義で恐縮である)ことがわかり,放物線は無限遠に伸びた円であると言える.ただしこれは何も数十万kmも移動する,というものではなく,歪んだレンズを通して平面に写した結果,あたかも無限遠まで伸びるように「見えてしまう」,という方が本質に近い.円錐はただマイペースに回転しているだけである.

 更に円錐が回転する様子を見る.

f:id:hibit_at:20190203212929p:plain

 この時,影は双曲線となる.双曲線は2つに分かれた曲線ではなく,無限遠を介してつながった1つの円が写されたものである(と解釈できる)ことがわかる.青と緑の部分は,それぞれ無限遠を介して「反対側」にループする.

f:id:hibit_at:20190203213605p:plain

 更に回転が進むとまた放物線が現れる.

 この後はまた円→放物線→双曲線→放物線→…という変化が続くが,周期が180°ずれると映される円の色相が違う(時計回りと反時計回りとが逆転する).この違いは単なる周期の違いでしかなく,メビウスの輪に表と裏がないようにどちらが正と言えるものではない.展開図においても,便宜上黄色を「表」,灰色を「裏」としているが,灰色側から見れば黄色こそが裏である.このように射影平面は向きつけ不可能なものである.

補足

 本記事はあたかも論文のような体裁を取ってはいるが,何の指導も査読も受けていないものである.記述内容に誤りがないか注意を払ってはいるが,内容(特にシェーダ,数学にかかわる部分)についてもし誤りがあれば,謹んで訂正したいので,筆者までお知らせ願いたい.このブログにはコメント欄がないので,お手数ながらtwitterのリプライかDMかで知らせていただきたい.

謝辞

 「方法」でも述べた通り,内容にクリティカルに関わる部分以外をやぎり氏のコードから流用させていただいている.また,レイマーチングを勉強し始める段階では,がとーしょこら氏には参考になるサイトをいくつも紹介いただいた.また,phi氏にはコード簡潔化のためのアドバイスをいただいた.この場を借りて感謝を申し上げたい.

参考文献

 著者名がハンドルネームしかわからない場合,はてなidを記す.

1, 松本幸夫(1988)「多様体の基礎」 東京大学出版会

2, 佐野岳人(2017)「トポロジーへの招待 〜 2. 切り貼りで作る色々な曲面」http://taketo1024.hateblo.jp/entry/topology/2

3 やぎり (id:yagiri000)(2018),「固定長進行レイマーチングやってみたので簡単なサンプルと解説」http://yagiri000.hatenablog.com/entry/2018/09/13/190006

付録

 以下がシェーダのコードである.ベースは[2]より引用したコードであるが,いくつか改変を加えた.

Shader "Custom/ProjectivePlane"
{
    Properties
    {
        _Threshold("Threshold", Range(-0.1,0.1)) = 0 // sliders
        _BaseColor("BaseColor", Color) = (.5,.5,.5,1)
   }
    SubShader
    {
        Tags{ "Queue" = "Transparent" }
        LOD 100

        Pass
        {
            ZWrite On
            Blend SrcAlpha OneMinusSrcAlpha

            CGPROGRAM
            #pragma vertex vert
            #pragma fragment frag
            // make fog work
            #pragma multi_compile_fog

            #include "UnityCG.cginc"

            struct appdata
            {
                float4 vertex : POSITION;
                float2 uv : TEXCOORD0;
            };

            struct v2f
            {
                float2 uv : TEXCOORD0;
                float4 pos : POSITION1;
                float4 vertex : SV_POSITION;
            };

            v2f vert(appdata v)
            {
                v2f o;
                o.vertex = UnityObjectToClipPos(v.vertex);
                o.pos = mul(unity_ObjectToWorld, v.vertex);
                o.uv = v.uv;
                return o;
            }

            float _Threshold;
            float4 _BaseColor;
            float speed1;
            float speed2;

            float2x2 rotmat(float x){
                return float2x2(cos(x),-sin(x),sin(x),cos(x));
            }

            // 座標がオブジェクト内か?を返し,形状を定義する形状関数
            // 形状は原点を中心とした円錐.
            bool isInObject(float3 pos) {
                speed1 = _Time.x*3;
                speed2 = _Time.x*1.1;
                pos.xy = mul(rotmat(speed1),pos.xy);
                pos.zy = mul(rotmat(speed2),pos.zy);
                float keijou = pow(pos.x,2)+pow(pos.z,2)-pow(pos.y,2);
                return keijou < _Threshold;
            }

            fixed4 frag(v2f i) : SV_Target
            {
                fixed4 col;

                // 初期の色(黒)を設定
                col.xyz = _BaseColor;
                col.w = 1.0;

                // レイの初期位置
                float3 pos = i.pos.xyz; 

                // レイの進行方向
                float3 forward = normalize(pos.xyz - _WorldSpaceCameraPos); 

                // レイが進むことを繰り返す.
                // オブジェクト内に到達したら進行距離に応じて色決定
                // 当たらなかったらそのまま(今回は黒)
                const int StepNum = 1000;
                const float MarchingDist = 0.01;
                for (int i = 0; i < StepNum; i++) {
                    if (isInObject(pos)) {
                float3 original = pos.xyz;
                speed1 = _Time.x*3;
                speed2 = _Time.x*1.1;
                pos.xy = mul(rotmat(speed1),pos.xy);
                pos.zy = mul(rotmat(speed2),pos.zy);
                float light = saturate(1-(pow(pos.x,2)+pow(pos.z,2))*5);
                        if (pos.y>0){
                        col.xyz = (cos(atan2(pos.z,pos.x)+UNITY_PI*float3(0,2,4)/3)/2+light);
                        }
                        else{
                        col.xyz = (cos(atan2(pos.z,pos.x)+UNITY_PI*float3(3,5,7)/3)/2+light);
                        }
                        if (original.y < -.51){
                            col.xyz = _BaseColor;
                        }
                        if (pow(original.x,2) > pow(50.01,2)){
                            col.xyz = _BaseColor;
                        }
                        if (pow(original.z,2) > pow(50.01,2)){
                            col.xyz = _BaseColor;
                        }
                        break;
                    }
                    pos.xyz += MarchingDist * forward.xyz;
                }

                return col;
            }
            ENDCG
        }
    }
}

VRChatを目的にする人、VRChatを手段にする人

(どちらが優れているとかどちらが劣っているとか、そういう話ではないですよ。念の為。)

 どうも、hibitです。去年の12月~今年の1月にかけて今更ながらBeat Saberにハマっていました。と言っても、難譜面攻略や高ランクを目指す訳ではなく、人類がまだ経験していない芸術アートである(と勝手に思っている)逆手セイバーを確立・普及させるという使命(であると勝手に思っている)を果たすべく、

 などの環境構築・技術習得を主に行っておりました。という訳で動画観て。

 これらの活動をできたのは、えんぢょ氏がdiscord鯖に誘ってくれたことが大きくて、この場を借りて感謝の念を申し上げます。また、技術面においてはチャイロ氏(100万再生おめでとう!)にはいつも素早いお返事をいただきました。ほか個別に名前をあげていくとキリがないので割愛させていただきますが、皆様にはお世話になりました。ありがとうございました。

 そうそう、チャイロ氏の動画、100万再生超えたんですよね。すごい!

 先日、それをお祝いしてVRChat(VRC)上でバーチャルオフ会的なのが開催されました。

f:id:hibit_at:20190114013429p:plain ※バーチャルプライバシーに配慮してバーチャル目線を入れています(意味あるのか?)

 一瞬で集まれて、離席も気兼ねなくできて、それでいて空気はオフ会そのもの。いやあ実に便利な世の中になったものだ。

 実は上に書いたことはすべて報告・宣伝・前置きで、その時に感じたことが本記事の内容になります。VRCには、それを目的にする人と、それを手段にする人とがいるということです。

VRCを目的とする人

 私のVRキャリア(?)はVRChatから始まっていまして、2018年の4月からのものになります。その頃は、まだ販売アバターも豊富ではなく、アバターといえば改変(というかミコちゃん)か自作かという感じでした。当然、アバターの自作は、

  • ポリゴン編集
  • リグ付け
  • UV展開
  • テクスチャ付け
  • Unityでの調整

 といった長大な手間を要するものであり、その過程で自然にその人のこだわり・創造性、言ってしまえば「魂」が宿るものでした。

 当然、そのような状況では、VRCで出会った人との会話もアバターに関するものが多くなり、「アバターが自作かどうか」「シェーダは何を使っているか」というものはとっかかりとして定番でした。また、アバター以外の話題であっても、ワールド作成であるとか、どのようなVRバイスを使っているか、というものになりやすかったと記憶しています。

 このような状況は、VRCにいるだけでどんどん情報共有がなされ進化が起こりやすく、それがまたさらなる進化を……という好循環を生み出しやすく、そのおかげでVRCには活発なコミュニティ群がいくつも出来ていました(今も出来ているのかもしれませんが、あまり把握できていない……)。つまりVRCそのものがVRCにいる目的になっていた訳です。今のVRCにある豊富なアバターとワールドは、こういった空気の中で色々な人達が切磋琢磨してきたからこそ成り立ってきたものなのだと思います。私も、あの時期にVRCを初めていなかったら、自分でフルスクラッチアバターを作ったりすることはなかったかもな、と今でも思います。

 ただその一方で、「VRCで話すことが、本質的にはVRCに関わること(あとそれにまつわる人間関係とか)以外あまりない」という状況が長く続くことに、少し息苦しさを覚えていたのかもしれません。

VRCを手段とする人

 ここで話を冒頭で話したバーチャルオフ会に戻しますと、そこではVRCに慣れていない人がほとんどでした。joinてなに、フレンド申請ってどうやるの、というそういうレベル。当然、私含むVRC慣れてる勢が先導してアテンド的な働きをやっていました。と言っても、全然嫌ではなかったというか、むしろ新鮮な感じでした。

 アバターも、アバターワールドで借りてきたもので通して、特にそれで十分。VRCに入るのに、別にBlenderやUnityと格闘することは必要条件でも何でもない、という当たり前の事実を改めて感じることになりました(VRCのヘビーユーザーのグループにいると時々忘れそうになります)。こういう空気に触れると、VRC界隈で論争になっていたこと(自作アバター至上主義とかね)がばからしく思えてくる程ですが、こういう気軽な楽しみ方こそ、技術が本来目指すべきものではないのかな、とも思います。

 今後、このようなツールとしてのVRC(もしくは他のVR/ARプラットフォーム)の活用はどんどん進んでいくでしょう。そこでは、アバターもワールドも単なる手段であり、その人の魂を顕現させるものではありません。

それは良いことなのか悪いことなのか

 長期的には良いことだと思います。というのも、自己目的化した同質的な集団は、高い付加価値を生み出しやすい反面、過激化しやすい・排他的になりやすいという側面があります。ライト層の参入は、そういったタコツボ性を壊し、文化を一般化・長寿命化させるという働きがあります。

 そりゃ、いわゆる「古参」「懐古」側の気持ちはわからんでもないですよ。大衆化するとノイズが増えます。事実、インターネットは大衆化してゴミだらけの腐海になりました。パソコン通信の時代や、5ちゃんねる(旧2ch)の時代で時が止まって欲しいという人もいるでしょうが、やはり今のインターネットの利便性があるのは、色々なユーザーを取り込んで拡大してきたからです。それに、腐海の中でも情報を真摯に発信している人はいます。このブログもそうでありたいと願っています。全然関係ないけど、クソみたいなまとめサイトとかコタツ記事とか早く絶滅しないかな。

 いまだ発展途上にあるVR/AR界隈においても、ギーク層もライト層もお互いを尊重しつつ長く共存していけるといいですね、という超無難なまとめで本記事を締めたいと思います。

 そう言えば、Beat SaberもVRゲームのはずだけどあまりVR文化という感じがないですね。根本的に音ゲー文化だから?