HP Reverb G2 Omnicept Editionでできること

2022年7月
瞳孔・視線・心拍数・口の動きを検出できるVR用ヘッドマウントディスプレイを使ったアプリケーション開発

HP社から発売されたHP Reverb G2 Omnicept Editionが届きましたので、このVRヘッドセットで特に特徴的な瞳孔・視線・心拍数・口の動きの検出等の動作確認をしてみた。

VRヘッドセット(HMD)を用いたシステム開発が進化する過程で、HMDを使用するユーザの情報取得ニーズが増えてきている。ウェブサイトを作った後に、そのウェブに訪問してくるユーザ解析をGoogle Analytics等の解析ツールを用いてするように、HMDを通してアプリケーションを体験するユーザがどういう感想・感情を抱きながらコンテンツを楽しんでいるかを知りたいのだ。たとえば以下のような具体的な使用例が考えられる。

1、エンターテイメント系コンテンツを体験しているときに、どの時間、どの場所でユーザは楽しんでくれているのか、つまらないと思っているのか?
2、研修系VRアプリを使っているときに、研修者は集中したり緊張しているのか?見るべきところをしっかり見てくれているのだろうか?
3、医療系アプリケーションを使用しているときに、医師が患者の生体情報を取れないのだろうか?

従来のVR HMDではユーザの頭部や手の動きはトラッキングできたものの、それ以上の生体情報の取得はできなかった。しかし2021年末から日本でも販売開始されているHP Reverb G2 Omnicept Editionを持ちいれば、瞳孔・視線・心拍数・口の動きを検出でき、それらデータからユーザの感情、集中度、心身の状況などが推定できるようになりつつある。

この記事では、HP Reverb G2 Omnicept Editionを使用した初期実験として、取れるとされている各種生体情報の取得を試した記録を記載する。

Heart Rate (BPM: Beats Per Minute):心拍センサーによる心拍数の取得

このHMDのおでこに当たる部分に、心拍センサーが付いている。この心拍センサーにより、リアルタイムにユーザの心拍数を取得できる。HMDを被りながら、腕を動かしたり、スクワットをすることで、実際に心拍数が上がったことが確認できた。

Face Camera & Mouth Tracking:フェイスカメラを用いた口の動きのセンシング

このHMDの鼻の下・口の前の部分に、フェイスカメラが付いている。このフェイスカメラは赤外線カメラのため、RGBのうちR(赤)一色での動画出力になっており口周辺の肌の色の解析には使えず、主に口の動きに使う想定になっている。たとえばアバターの口の動きを、実際の演者(HMDをかぶったユーザ)に合わせてリップシンクさせることが可能だ。

Eye Opening / Closing & Eye Direction:アイトラッキングカメラによる瞬きや視線検出

両眼のレンズの周りにアイトラッキングセンサーがついており、それらによりユーザの瞬きや視線(上下左右どちらを見ているか)を検出できる。このアイトラッキングと次の瞳孔測定センサーはTobii社の技術を使っているそうで、Tobii社のウェブサイトには以下のような説明があった。

Tobii社ウェブサイトより引用)角膜上に光の反射点を生じさせ、その画像をカメラで撮影します。撮影された眼球の画像から、角膜上の光の反射点と瞳孔を識別します。光の反射点やその他の幾何学的特徴を基に眼球の方向が算出されます。

Eye Pupil:瞳孔測定センサーによる瞳孔の大きさ測定

アイトラッキングセンサー・瞳孔測定センサーを用いて、瞳孔の大きさの測定が可能だ。瞳孔とは目の中心部の最も黒くなっているところであり、瞳孔の大きさの変化は網膜に当社する光量を調整するためにある。(Wikipediaによると)瞳孔径は2mmから8mm程度の間で変化する。明所では縮瞳が生じ、瞳孔径は小さくなる。暗所では散瞳が生じ、瞳孔径は大きくなる。これはカメラの絞りに似た動きである。実際にサンプルアプリでも明るい空間と暗い空間を作ることで、瞳孔の大きさが変化したことが確認できた。

Eye Exam:視力検査

これはおまけ的な機能だとは思うが、視力検査もできた。片目ずつ真っ暗になり、視力検査によくあるCの文字が上下左右に向きを変えて、その開いてる方を応えるアプリだ。一説によると、HMDを使うことで視力が上がる?という説もあるようなので、HMD使用前後でこの機能で視力を測ってみたら面白い結果が得られるかもしれない。

Eye Vignette (Field of View):視野角制限

VRの根源的な問題の一つにHMDを使うことでVR酔いすることがある。しかしこのVR酔いは視野角を制限することである程度予防できることも研究によりわかってきている。アプリケーションを開発する過程や提供する過程で、VR酔いをしやすいユーザにはこのような視野角制限機能を提供するのもありだろう。

以上の通り、HP Reverb G2 Omnicept Editionの各種生体情報取得を試しにやってみた。今後は、当社が関係している、エンタメ、研修、医療系の各種アプリケーションの上記のような機能を追加したり、またはこれら生体情報取得により可能になる新たなアプリケーション創出を考えていきたいと思う。

Pixotope運用メモ

2022年2月
Pixotope運用について

起動手順

画面中心にあるアイコンからPixotopeを起動

起動画面(START)にてプロジェクトを選択。Pixotopeにプロジェクトを読み込んでいない、もしくは新規作成する場合は、画面右上にある「Create new Show」を選択。

プロジェクトは、テンプレートから新規生成が可能。既存のプロジェクトが存在する場合は、そのファイルパス(.uproject)を指定する。このプロジェクトはUE4で生成も可能で、それをPixotopeで読み込めば自動的に対応する。

プロジェクトを選択したら、SETUP画面に移動する。ここにはプロジェクト内に存在するLevelがリストとして表示されているので、起動したいものを選択する。モードは「Editor モード」と「LIVEモード」が存在する。Editorモードは主に編集やテスト用途で起動する。本番撮影の場合は、負荷の軽いLIVEモードで起動する。

プロジェクトファイル管理

プロジェクトファイル自体は通常のUE4と同様の構成なので、UE4の運用に準じた方法でよい。

Git管理

ファイルの差分情報を更新して管理する方法。ただしLevelやBlueprintはハッシュ管理されるため、ファイルの部分的な差分更新ができない。そのため、複数人で同じファイルを更新してしまうとコンフリクトが発生し、その場合はどちらか一方の変更で上書きするしかできないので注意。

フルバックアップ

プロジェクトフォルダをそのままコピーしておけば、フルバックアップとして保存できる。大きなデメリットはないが、内部のアセットそのものを複製するのでファイル容量には注意。

カメラ設定

センサーサイズ(Filmback)

FX6に積んであるセンサーは、いわゆる”35mmフルサイズ”と呼ばれる36mm x 24mmのセンサーで、比率は3:2となる。
SETUPのConfigureセクションにあるCamera trackingを開き、その中にある[Camera and lens]を開く。その中に[Filmback]セク/ションがあるので、ここでセンサーサイズを設定する。

要注意事項となるのが、WidthとHeightの設定値。ここに入力する値は物理的なセンサーサイズではなく、映像の比率に応じて使用するセンサー領域を入力する(要追加検証)。例えば、16:9の比率で動画を撮影する場合、使用されるセンサー領域は16:9に合わせて36mm x 20.25mmとなるので、その値をWidthとHeightに設定する。なお感光された情報を解像度に応じて分割すると思われるので、ここで気にするべきは比率のみでよい。

解像度(Resolution)

基本的には1080p (1920×1080) をベースに使用する。SDI出力が対応しているのであれば、2160p (3840×2160) も使用可能ではある。

フレームレート(FPS)

コンポジット映像のフレームレートにあわせて、カメラ側も設定する。基本的には30fpsか60fpsを使用する。ただしカメラ側は表記上と実際のfpsが異なり、30fps表記の場合は29.97fps, 60fps表記の場合は59.94fpsとなるので注意。

Pixotope上の入出力設定は、SETUP画面のVideo I/Oにて設定。

映像の同期

RedSpyと物理カメラの映像フレームは、それぞれ異なるタイミングで更新される。PIXOTOPEにてそれぞれに遅延を設定することで更新タイミングを合わせ、コンポジット(最終出力)映像の整合性を調整することができる。
ただし、それぞれの遅延は一定ではなく、また処理負荷の影響で更新タイミングが動的にズレてしまう。特に、例えばLevelやアセットのロード等によってPIXOTOPEにて一時的な高負荷が発生、それにフリーズが生じてしまうと、そのタイミングで実写とバーチャル映像のズレが変化してしまう。そのため、描画負荷は可能な限り一定に保ち、PIXOTOPEの描画FPSが実写とRedSpyのFPSを常に下回るように構築、設定する必要がある。

TCの活用

FX6ではTCを生成し、映像と一緒にキャプチャボードの映像端子経由でTCをPixotopeに参照させられる。TCをベースに同期させることで、カメラとCG映像のズレは概ね抑えることができる。カメラ側では、DF(Drop Frame)とNDF(Non-Drop Frame)の2つのTCモードを選択できる。通常使用するのは、後者のNDFとなる。
DFにした場合、TCをリセットしたタイミングではフレームが完全に同期しているように見えたが、時間経過に比例してズレが大きく様子が見られた。おそらくこれは、カメラ側のが29.97fpsもしくは59.94fpsであるのに対し、Pixotope側は30.0fpsもしくは60.0fpsという差異があり、カメラ側で意図的にフレームを削ってしまうことでカメラとPixotopeのフレームのズレがどんどん大きくなっていることが原因かと思われる。NDFの場合、経過時間に関係なくズレが変化する様子はなかった。

なおNDFでも発生するわずかなズレは、Genlockをかけていないことが原因と推測。TC + GenLockによって完全に一致させることが可能になると思われる。これを実現したい場合は両方の端子をもつ物理カメラが必要で、FX6にはGenLock端子がないことから、FX9が必要となる。
細かなフレームのズレは、Pixotope上で調整可能。これでほぼ一致した状態で撮影は可能となる。

Sync Generatorの活用

SyncGeneratorを使用してGenLockすると、より正確にフレーム同期が行なわれる。RedSpyには入力端子が用意されているが、物理カメラの場合は機種によっては対応していないことがある。Sony FX6には入力端子なし。
PIXOTOPEの場合、キャプチャボードのReference端子に入力する。その上で、DashBoardにて設定を行なえばフレーム更新タイミングを同期させられる。

Maskとシェーダの干渉

透過を含むシェーダがマスクの前に来ると、マスク越しの実写映像が描画されないことがある。

映像のノイズ対策

Liveモードの使用

処理負荷はEditモードよりもLiveモードの方が軽く、フレーム落ちのようなノイズも発生しにくい。映像を確認しながら調整を行なう場合はEditモード、本撮影ではLiveモードを使用すること。

キャプチャデバイスの選定

PIXOTOPEを動かすPCは極力負荷を落とすため、映像キャプチャと配信は別のデバイスで行なう。映像キャプチャはBlackmagic Video Assistのようなレコーダ搭載のモニター、OBSを積んだ別PC等に映像を伝送する。
Blackmagic Video Assist

コーデックの選定

コーデックによって記録処理の負荷が異なるため、キャプチャデバイスが耐えられるものを使用すること。一方で画質にも影響は出てくるので、コーデックの指定がある場合は性能の高いキャプチャデバイスを使用すること。

カラーマネジメント

ディスプレイ

PIXOTOPEでカラーコレクションを行なう場合、ディスプレイの色特性の影響を受けてしまうため、カラーマネジメントディスプレイを使用して設定すること。カラーフォーマットは後述に従う。

カラーフォーマット

基本的には成果物を見るであろうユーザのデバイスに合わせ、汎用的なディスプレイでも対応しているRec.709をカラーマネジメントディスプレイの色空間として設定する。一方で、例えば映画館のような鑑賞用の空間で成果物を投影するような場合、使用するディスプレイの特性が明確になっているのであれば、**HDR(Rec.2020など)**を設定してカラーコレクションを行なうことも可能。ただし、フォーマットが変更になった場合の影響が大きいので、カラーコレクションの実施前に確認しておくこと。
よくわかる、HDR徹底解説! ガンマカーブの違い | EIZO株式会社

キーイング

Initialize Key

内部処理は詳しくわからないが、カメラ映像をベースにキーイングのベース設定を自動的に行う機能。
Setting Up A Chroma Key Material in UE4

その他のパラメータ

基本的には、一般的なクロマキー合成処理における調整パラメータと同じ。

キーワード

【映像関係】
TC: TimeCode
GenLock
SyncGenerator
カラーフォーマット
カラーコレクション / カラーグレーディング
ポストプロセス
SDI
カラーモニター
シネマカメラ
FPS: Frame Per Second
センサーサイズ

【エンジニアリング / VFX関係】
In-Camera VFX
TA: Technical Artist
LEDウォール
インナーフラスタム / アウターフラスタム
Pixotope
UE4: Unreal Engine 4
Unity
Virtual Production
RedSpy
キーイング
プリレンダリング / リアルタイムレンダリング
TouchDesigner
HTC VIVE Tracker
Volumetric Capture
Reality
Photogrammetry
Houdini

【Unreal Engine関係】
Pixotope
Megascans
Live Link
Niagara
Blueprint
nDisplay

【照明関係】
DMX
Art-Net
sACN
ムービングライト

パノラマカメラ:Teche 360Anywhereの検証

2021年11月
Teche 360AnyWhereのテスト利用

中国のTeche社のパノラマカメラである360Anywhereをお借りでき、短期間ではありますがいろいろと撮影してみれたのでその結果を書き残しておきたいと思います。

当社の360度動画の撮影やライブストリーミングの案件では、Insta360社のInsta360 Pro2Insta360 Titanを使用することが多いです。360Anywhereの話を初めて聞いたときは、Insta360の製品に大きな不満はなく、いまから他社製品を試す必要はないのではと思ったのですが、良い意味でその予想を裏切ってくれた製品でした。Insta360 Pro2は発売日が2018年8月と、すでに3年前の発売の製品なので比較するのがアンフェアな気もしますが、その後の新製品(プロ向け製品)は発売されておらず、いまだに現役で使われている製品ということもあり、価格帯も360Anywhereに近いので、以下の比較対象としています。

Teche 360AnywhereとInsta360 Pro2の機能・性能比較

カタログスペック上の機能や性能の比較は以下のとおりです。

ここで特筆すべきなのは以下のような事項でしょうか。撮影できる動画の解像度はそれぞれ8K以上であるのに対して、360Anywhereやサイズや取り回しの良さが際立っているかと思います。
・重さ:Insta360 Pro2が1550gであるのに対して、360Anywhereは600gで、軽い
・大きさ:Insta360 Pro2が高さであるのに対して、360Anywhereは高さで、小さい
・ストレージ:Insta360 Pro2が7枚のSDカードが必要であるのに対して、360Anywhereは本体内蔵ストレージ
・熱対策用ファン:Insta360 Pro2がファン内蔵であるのに対して、360Anywhereはファンレス

所感

一方で、まだ発売直後で、かつ日本展開が始まったばかりのせいなのか
・日本語でのマニュアルがなかったり、情報も少なくすべての機能が使いこなしにくい
・スマホアプリもPCアプリもやや不安定で、期待通りに動かないことも多い
・ファンレスのため、長時間使用時には不安が残る
という不安・懸念事項もあったことは付け加えておきたいと思います。

いずれにしても、ハードウェアとしてのスペックは高く、そのコンパクトさはとても魅力的でした。今後ファームウェアやソフトウェアのアップデートにより性能や価値は上がりそうなところもあるので、今後にも期待したいと思います。

VRとARとMRの違いについて

2021年9月
Virtual Reality (VR)、Augmented Reality (AR)、Mixed Reality (MR)、Extended Reality (XR)の特徴や違いについて解説します

Virtual Reality (VR)、Augmented Reality (AR)、Mixed Reality (MR)、Extended Reality (XR)について

最近、TVや各種メディアでもよく取り上げられるVR(Virtual Reality)ですが、このVRと似た様な技術にAR(Augmented Reality)とMR(Mixed Reality)があります。これらの技術はどのように違うのでしょうか。各技術の概要と代表製品を取り上げその可能性について紹介いたします。またこれらの技術は総称して、XR (Cross Reality、eXtended Reality)と呼ばれます。

VR(Virtual Reality : バーチャルリアリティ)とは

VRとはコンピュータ上に人工的な環境を作り出し、時間や空間を超えてそこにいるかのような感覚を体験できる技術で、日本語では「人工現実感」あるいは「仮想現実」と呼ばれます。VRを実現する為に、VR用ヘッドマウントディスプレイ(HMD)を用いることが多く、HMDを被ると実世界は見えなくなるのが、ARやMRとの違いです。バーチャル(仮想)という言葉が、CGや「架空の世界」を連想させますが、空想の世界を作り上げ体験することだけが、「バーチャルリアリティ」ではありません。「バーチャルリアリティ」という言葉は、「バーチャル(仮想)」も含めたあらゆる空間表現を、「まるで現実(リアリティ)であるかのように」体験するための技術や取り組みの総称だと、私たちは捉えています。作り込まれた「バーチャルリアリティ」がなんであれ、ユーザーが「まるで本物の世界」のように、体験し、行動することができるのが、「バーチャルリアリティ」の本質です。

HMDの操作は、手で持つコントローラもしくは手のみの動きを通して、HMD内に表示されるメニュー選択等を行います。HMDには3DoF(Degree of Freedom)と6DoFの2種類があり、3DoFではユーザは歩くことができずにその場で上下左右を見回したり、頭を傾けられます。6DoFでは、3DoFに加えて、前後左右に歩け、しゃがんだりジャンプもできます。

当社における事例:「VRデビルマン展」基盤機能開発VR技術を用いたがん患者さん向けピアサポート
市販品の例:Oculus Quest2HTC VivePlaystation VR

AR(Augmented Reality : 拡張現実)とは

ARとは、現実空間に付加情報を表示させ、現実世界を拡張する技術のことをいいます。VRは現実世界とは切り離された仮想世界に入り込みますが、ARはあくまで現実世界が主体です。後述するMRも包含する概念になりますが、現実世界と仮想世界を両方活用することで、現実世界に新たな価値を付加できるようになります。ARではMRとは違って現実世界の形状情報、3次元情報等は認識せずに、カメラ画像にデジタルコンテンツを重畳して表示する手法が主流になっています。最近では、スマホでもヘッドマウントディスプレイでも現実空間の平面認識や3次元スキャンをできるようになってきたことで、単純なARは減ってきているかもしれません。

当社における事例:歌舞伎AR/MRアプリ Reverse Realityの開発zSpaceの販売代理店
市販品の例:Google 「Glass Enterprise Edition 2」

MR(Mixed Reality : 複合現実)とは

MRは複合現実と呼ばれ、現実世界にCGなどで作られた人工的な仮想世界の情報を取り込み、現実世界と仮想世界を融合させた世界をつくる技術です。MRの世界内では、仮想世界のモノと現実世界のモノが相互に影響します。ARとの大きな違いは、MRでは、現実空間の3次元形状を認識することで、デジタルなオブジェクトを現実空間の地面や机上に置いたりと、空間を認識していることが挙げられます。また物理的なコントローラではなく、手のジェスチャー操作を実現していることもあります。

市販品の例:Microsoft Hololens2Magic Leap Oneなどのヘッドマウントディスプレイが代表的な市販品MRデバイスですが、最近ではスマホにもLiDAR等の奥行き検出センサーが搭載されることで、スマホでもMR的な機能が実現されつつあります。

VR・AR・MRの可能性

VRやMRは2025年までに現在のデスクトップPCと並ぶ巨大市場へと成長する可能性を秘めているそうです。ゴールドマンサックス社の2025年におけるVRユースケース予測によるとVRの活用市場は多岐にわたり、現在盛り上がりつつあるゲーム市場・エンターテイメント分野を始め医療系ヘルスケア分野や産業分野、小売市場など様々な業界で目にすることになりそうです。

VRは脳をどう変えるか?仮想現実の心理学」という書籍では、以下のようなことが述べられていました。市場が大きく成長しているだけでなく、人類にとっても新しい技術、新しい道具ができつつあり、当社でも各種システム開発を進めています。
・VR内での体験を、脳は現実の出来事として扱ってしまう
・VR内で第三の腕を生やしたり、動物の身体に〝移転〟しても、脳はすぐさまその変化に適応し、新たな身体を使いこなす
・イラク戦争後、〝バーチャル・イラク〟を体験するVR療法により、PTSDに苦しんでいた二〇〇〇人以上の元兵士が回復した
・VRで一人称視点の暴力ゲームをプレイすると、相手が仮想人間だとわかっていても生々しい罪悪感を覚える
・仮想世界で一日過ごすと現実と非現実の違いがわからなくなる
・VRユーザーの身体や視線の細かな動きは、正確にデータ化できる
・そこからその人の精神状態、感情、自己認識がダイレクトに読み取れる

メタバースやARクラウドへの拡張

2016年あたりからVRのブームが到来していますが、最近になるまではハードウェアとしてのヘッドマウントディスプレイと、そこで動作する主にスタンドアロンのアプリケーションの開発が主流だったように感じています。それは、まずは手元で動作するデバイスがなければ始まらないということかと思いますが、2020年頃より各製品が必要最低限の技術的仕様を満たすようになり、複数人ユーザが同時に仮想空間内に入り、そこでコミュニケーションを行うVRメタバースや、MRヘッドマウントディスプレイを屋外で使う際に必要になってくる技術であるARクラウドなどの研究開発も進んできました。ハードウェアがクラウドと連携することで、ますますVR、AR、MRの可能性が広がっています。

Sony ELF-SR1の環境構築と実機でサンプルを動かすまで

2021年1月
Unityを使って、Spatial Reality Display用のサンプルアプリを作ってみました
ELF-SR1一式

この記事では Unity でELF-SR1の開発を行う際の 環境構築とサンプルシーンのビルドまでを行います。

目次

  1. 初めに
  2. 動作環境の紹介
  3. PC上のセットアップ
  4. unity上の操作(ビルドまで)
  5. 自作プロジェクトの紹介
  6. 終わりに

1. 初めに

筆者は人生で一度も記事を書いたことがないど素人なので拙い部分が存在するかと思いますが暖かい目で見守って頂けると幸いです。

2. 動作環境の紹介

推奨PCスペック

・OS:Windows 10 (64-bit)

・CPU:AMD Ryzen 5 3600X以上 (6 Core以上)

・GPU:GeForce RTX2060以上

・Memory:8GB以上

・Storage:SSDを推奨

対応環境

・Unity 2018.4, 2019.4, 2020.1

・Unreal Engine 4.24, 4.25

今回は以下のPCスペックと環境でビルドを行いました。

PCスペック

・OS:Windows 10 (64-bit)

・CPU:Intel Core-i7 10750H

・GPU:GeForce RTX 2070 super

・Memory:16GB

・Storage:SSD

動作環境

・Unity2019.4.15f1

・Visual Studio 2019

ちなみにWindows上でしか動かないです(泣)Bootcampを使用したMacで動くかどうかはまだ不明です。そのうちやります。スペック次第ですが望み薄です。

3. PC上のセットアップ

やることをすごくざっくり説明すると

  1. 画面の設定
  2. 必要な物をダウンロード&インストール
  3. ちゃんとELF-SR1が機能しているかの確認

です。

以下詳しい説明と画像です。

画像1
画像2

①ELF-SR1をWindows PCと接続した状態でディスプレイの設定を行います。画像2のようにELF-SR1の画面サイズを100%、解像度を3840x2160、画面の向きを横に変更します。その後マルチディスプレイの設定を「表示画面を拡張する」に変更します。

※ 今回はELF-SR1を横にして使用しますが縦でコンテンツを制作することも可能です。縦て展示などする場合は画面の向きを縦に変更してください。

画像3

https://www.sony.net/Products/Developer-Spatial-Reality-display/jp/develop/Setup/DownloadInfo.html から開発に必要なプラグインとドライバーをダウンロードします。

以下内容物の説明です。読まなくても大丈夫です。
SR Display Runtime Installer には以下の2つのソフトウェアが含まれています。
・SR Display Runtime libraries & device drivers
・SR Display Settings application
SR Display Runtime libraries & device driversはunity上でビルドする際に必要なライブラリとELF-SR1用のドライバーです。インストールすることでELF-SR1で開発を行うことができるようになります。
SR Display Settings applicationはSR Displayの設定値の調整や高速ビジョンセンサーがユーザの顔を認識できているか確認することができるアプリです。
SR Display Plugin for Unity は Unityで開発を行う際に必要なものを詰め込んだzipファイルのようなものです。4. Unity上の操作で使用します
画像4 ELF-SR1のビジョンセンサーのステータスバー
画像5 カメラのキャプチャ画像ウィンドウ

③ WindowsボタンからSR Display Settingsを起動し、動作確認を行います。自分の顔に赤枠が表示されて入れば確認完了です。

以下各ボタンなどの説明です。
ステータスバーのCamera Gain settingではカメラ光量を調整することが可能です。 自分の顔がきちんと見える光量に調整してみましょう。Auto Exposureにチェックをいれると自動で調節してくれます。
Crosstalk Correctionはノイズなどを修正してくれるシステムです。Application Cotrolledを選択するとunity側の設定がCrosstalk Correctionに反映されます。 offはCrosstalk Correctionがない状態になります。

4. Unity上の操作

新規プロジェクトを作る→プロジェクトの設定をする→サンプルシーンをビルド の3ステップでやっていきます。

ステップ1 新規プロジェクトの作成

画像6

プロジェクト名は何でも大丈夫です。全角文字やスペースはよく悪さをするのでできるだけ半角文字を使用しましょう。保存先も自由に決めて大丈夫です。決めたら作成ボタンをクリックします。

画像7

プロジェクトの作成に成功し、シーンが展開されたらとりあえずツールバーのFile→Saveで保存しましょう。

ステップ2 プロジェクトの設定をする

画像8

Saveができたら3.PC上のセットアップの際にダウンロードしたSR Display Plugin for Unity を作成したシーンにインポートします。

画像9

SR Display UnityPluginはWindows x86_64アーキテクチャのみ対応しているので設定が違うとインポートの際に画像9のように警告がでます。okを押すと自動で設定を変更してくれます。

画像10

ELF-SR1用にプロジェクトの設定を変える必要があるのでツールバーのEdit→Project Settingを開きましょう。

画像11

Qualityを選び、その後画面下部のVSync CountがEvery V Blankになっていると思うので赤枠のようにDon’t Syncに変更しましょう。

これでunityプロジェクトの設定は完了です。 次は実際にシーンをビルドしてディスプレイ上で動いているところを見 てみましょう。

ステップ3 サンプルシーンをビルド

画像12

画像11のようにProjectウィンドウからAsset欄を開きSRDisplayUnityPlugin →Samplesy→0_SRDSampleHome→Scenes→SRDisplaySampleHomeを開きましょう。

画像13

ツールバーのFileBuild settingを開きましょう。Scenes In Buildに6種類のシーンが追加されていること、PlatformがPC Mac & Linux Standaloneになっていることを確認してたらBuild and Go をクリックしましょう。

画像14 人間の目に合わせているのでカメラをむけると高確率で歪みます(泣) 写真上手く撮れる用に精進します。

Buildすると画像14のような画面が出てきます。数字の1〜5を押すことで色々なサンプルが体験できます。

5. 自作プロジェクトの紹介

画像15 ELF-SR1を用いた地球しか見えないプラネタリウム
画像16 hierarchyとInspector

SRDisplayBoxの壁に合わせて内側にquadを配置し、quadのテクスチャーを変更することことで画像16のように背景を作ることができます。またleap motion などのガジェットと合わせることで活用の幅が広がるのでぜひ色々試してみてください。

6.終わりに

お疲れさまでした!
最後まで読んでいただきありがとうございます。 今回は公式が出しているドキュメントを見ながら制作しました。 少しでも参考なれば嬉しいです。

Sony Global - SPATIAL REALITY DISPLAY | Overview


Sony ELF-SR1の環境構築と実機でサンプルを動かすまで was originally published in Kadinche Engineering on Medium, where people are continuing the conversation by highlighting and responding to this story.

[Tips] React で highlight.js を適用する方法

2020年12月

はじめに

Reacthighlight.js を組み込もうとしたのですが、若干躓いてしまったので対処法についてメモっておきます。

また、React は既にプロジェクトにインストール済みと仮定します。

# 一応 React をインストールするためのコマンドは ↓
npm i --save react react-dom

React に highlight.js を組み込む

まずは highlight.js を NPM or Yarn でインストールします。

# NPM で highlight.js をインストールする
npm i --save highlight.js
yarn add highlight.js

その後、React ソースコードに highlight.js を組み込みます。
ソースコードの該当部分のみを載せると下記の感じになります。

import Head from 'next/head'
import styles from '../styles/Home.module.css'
import React, { useState, useEffect } from 'react';
/**
highlight.js を import する
*/
import hljs from 'highlight.js/lib/core';
/**
シンタックスハイライトしたい言語のみ import として登録する
今回は html をハイライトしたかったので xml を import した
デザインは highlight.js/styles/~ を変更することで調整可能
https://highlightjs.org/ のトップページから各種デザインについては確認可能
(コード右下にある style の右側リンククリックで各種デザインのプレビューが可能)
*/
import xml from 'highlight.js/lib/languages/xml';
import 'highlight.js/styles/github.css';
hljs.registerLanguage('xml', xml);
let inputChecker = null;
export default function Home() {
  const [user, setUser] = useState('nikaera');
  const [previewUser, setPreviewUser] = useState('nikaera');
  const [badgeCode, setBadgeCode] = useState('nikaera');
  const [style, setStyle] = useState('plastic')
  /**
  useEffect のタイミングで hightlight.js の初期化を行う。
  called プロパティを false にすることで highlight.js で、
  コードが変更された場合でも常にシンタックスハイライトすることが可能
  */
  useEffect(() => {
    hljs.initHighlighting();
    hljs.initHighlighting.called = false;
  });
  useEffect(() => {
    /**
    シンタックスハイライトしたいコード input フォームへの入力内容に応じて動的に変わる
    */
    setBadgeCode(`  <!-- highlight.js でハイライトする -->
  <div>Hello ${user}!</div>
  }`, [user, style]);
  });
  const handleChange = (event) => {
    if (inputChecker)
      clearTimeout(inputChecker);
    inputChecker = setTimeout(() => {
      clearTimeout(inputChecker);
      inputChecker = null;
      setPreviewUser(event.target.value);
    }, 1 * 1000); // 1 seconds
    setUser(event.target.value);
  };
  return (
    <div className={styles.container}>
      <Head>
        <title>Highlight</title>
      </Head>

      <main className={styles.main}>
        <h1 className={styles.title}>
          Highlight sample
        </h1>

        <input type="text" value={user} onChange={handleChange} />

        <div>
          { /* pre -> code タグ内に highlight.js で
            シンタックスハイライトしたい内容を出力する */ }
          <pre style={{ width: '80vw' }}>
            <code className="xml">
              {badgeCode}
            </code>
          </pre>
        </div>
      </main>

      <footer className={styles.footer}>
      </footer>
    </div>
  )
}

おわりに

React に highlight.js を組み込むやり方は多々見つけたものの、中々上手くできなかったのでメモっておきました。同様のケースで悩んでいる方の助けになれれば幸いです:pray:

参考リンク

[Tips] Hugo で外部リンクを target=”_blank” で開く方法

2020年12月

はじめに

Hugo で設定した外部リンクを開くときは別ウィンドウで開けるようにしたかったので、Hugo のテーマファイルをオーバーライドして対応しました。外部リンクが設定されているときのみ a タグに target="_blank" rel="noopener noreferrer" が追加されるようにしました。

テンプレートファイルは GO の HTML テンプレートで書かれているので、その書式にしたがって a タグの属性を書き換えることで、外部リンクの場合は target="_blank" rel="noopener noreferrer" を追加します。

a タグに外部リンクが設定されていた場合は target="_blank" を付与する

下記は hugo-PaperMod で、メインメニューの a タグに target="_blank" rel="noopener noreferrer" を追加するときのサンプルになります。

layouts/partials/header.html
<ul class="menu" id="menu" onscroll="menu_on_scroll()">
    <!-- `.Site.Menues.main` の要素数 (メニュー数) ループします -->
    {{- range .Site.Menus.main }}
    <!--
        要素内の .URL にアクセスすることで設定されているリンクにアクセスする。
        設定されている URL のプレフィクスが https:// or http:// であれば、
        絶対リンクが設定されているはずなため、外部リンクが設定されているとみなす。
    -->
    {{- $is_abs_url := or (strings.HasPrefix .URL "https://") (strings.HasPrefix .URL "http://") }}
    <!--
        もし外部リンクが設定されていれば、そのまま .URL の内容を出力する。
        そうでなければ、内部リンクを language プレフィクスを付与した形で出力する。
    -->
    {{- $menu_item_url := (cond $is_abs_url .URL (printf "%s/" .URL) ) | absLangURL }}
    <li>
        <!--
            外部リンクが設定されていたら ($is_abs_url が true なら)
            a タグに target="_blank" rel="noopener noreferrer" を設定する
        -->
        <a href="{{ $menu_item_url }}" {{- if $is_abs_url }} target="_blank" rel="noopener noreferrer" {{- end}}>
            <span {{- if eq $menu_item_url $page_url }} class="active" {{- end }}>
                {{ .Name }}
            </span>
        </a>
    </li>
    {{- end -}}
</ul>

おわりに

Hugo でページを自分の思い通りカスタマイズするためには、ある程度は Go の HTML テンプレートの知識も必要になりそうです... :boy: :writing_hand:

参考リンク

MediaPackage 用の CloudFront ディストリビューションを AWS SDK で作成する

2020年12月

はじめに

とある事情で MediaPackage のエンドポイント用の CloudFront ディストリビューションを AWS SDK で作成する機会がありました。その際得た知見をソースコードを交えながら備忘録として記事に残しておきます。

本記事内容で紹介しているソースコードは Gist にも同じ内容でアップしてあります。

ちなみに MediaLive + MediaPackage + CloudFront の構成でインフラ構築したい場合は、CloudFormation が MediaPackage にも対応したので CloudFormation の利用を推奨します。

本記事内容はあくまでも何らかの事情で、後から CloudFront ディストリビューションを MediaPackage エンドポイントに紐づけたいケース等で参考になると思われます。

実装内容

作成したソースコードの内容は下記になります。
最下部の createDistributionForMediaPackage が本記事タイトルに該当する関数です。

CloudFrontClientForMediaPackage.ts
import { CloudFront } from "aws-sdk";
import * as url from "url";
import {
    CreateDistributionWithTagsResult,
    GetDistributionResult,
    UpdateDistributionResult
} from "aws-sdk/clients/cloudfront";
export class CloudFrontClientForMediaPackage {
    private cloudFront: CloudFront;
  constructor() {
    this.cloudFront = new CloudFront({
        region: "ap-northeast-1",
        apiVersion: '2020-05-31',
    });
}
/**
 * CloudFront ディストリビューションの情報を取得するために利用する
 * @param id CloudFront ディストリビューションの ID
 * @return ディストリビューションの情報を取得する
 */
  async getDistribution(id: string): Promise<GetDistributionResult> {
      const distribution = await this.cloudFront.getDistribution({
          Id: id
      }).promise()
      return distribution;
  }
  /**
   * CloudFront ディストリビューションの設定内容を取得するために利用する
   * @param id CloudFront ディストリビューションの ID
   * @return ディストリビューションの設定内容を取得する
   */
  async getDistributionConfig(id: string): Promise<CloudFront.DistributionConfig> {
      const config = await this.cloudFront.getDistributionConfig({
          Id: id
      }).promise()
      return config.DistributionConfig;
  }
  /**
   * CloudFront ディストリビューションを削除する
   * @param id 削除したい CloudFront ディストリビューションの ID
   */
  async deleteDistribution(id: string) {
    const distribution = await this.getDistribution(id);
    await this.cloudFront.deleteDistribution({
        Id: id, IfMatch: distribution.ETag
    }).promise()
  }
  /**
   * CloudFront ディストリビューションを無効化する
   * @param id 無効化したい CloudFront ディストリビューションの ID
   * @return 無効化した CloudFront ディストリビューションの情報
   */
  async disableDistribution(id: string): Promise<UpdateDistributionResult> {
      const distribution = await this.getDistribution(id);
      const config = distribution.Distribution.DistributionConfig;
      config.Enabled = false;
      return await this.cloudFront.updateDistribution({
        Id: id,
        IfMatch: distribution.ETag,
        DistributionConfig: config
      }).promise();
  }
  /**
   * MediaPackage のエンドポイント用の CloudFront ディストリビューションを作成する
   * @param id CloudFront ディストリビューションを判別するための ID
   * @param mediaPackageArn MediaPackage チャンネルの ARN
   * @param mediaPackageUrl MediaPackage エンドポイントの URL
   */
  async createDistributionForMediaPackage(
      id: string,
      mediaPackageArn: string,
      mediaPackageUrl: string
    ): Promise<CreateDistributionWithTagsResult> {
    // 1. url モジュールを用いて URL 文字列をパースする
    const mediaPackageEndpoint = url.parse(mediaPackageUrl);
    /**
    2. MediaPackage のエンドポイント URL から FQDN を取得する。
    後述する CloudFront ディストリビューションのオリジンのドメイン名としても利用する
    */
    const mediaPackageHostname = mediaPackageEndpoint.hostname;
    /**
    3. MediaPackage のエンドポイント URL のフォーマットは
    https://<AccountID>.mediapackage.<Region>.amazonaws.com/**** となっているので、
    FQDN の先頭部分を文字列分割で取り出すとアカウント ID が取得できる
    */
    const accountId = mediaPackageHostname.split('.')[0];
    // 4. 後述する CloudFront ディストリビューションのオリジン ID として、アカウント ID を利用する
    const targetOriginId = `MP-${accountId}`
    /**
    5. createDistribution ではなく、createDistributionWithTags 関数で、
    CloudFront ディストリビューションを作成する。MediaPackage との紐付けにタグを利用するため。
    */
    return await this.cloudFront.createDistributionWithTags({
        DistributionConfigWithTags: {
            Tags: {
                Items: [
                    /**
                    !!!!!重要!!!!!
                    6. CloudFront ディストリビューションに紐付けたい
                    MediaPackage エンドポイントのチャンネル ARN を
                    mediapackage:cloudfront_assoc で定義する。
                    mediapackage:cloudfront_assoc を定義することで、
                    CloudFront ディストリビューションと
                    MediaPackage チャンネルを紐付けることが可能となる。
                    */
                    {
                        Key: 'mediapackage:cloudfront_assoc',
                        Value: mediaPackageArn
                    },
                    {
                        Key: 'Id',
                        Value: id
                    },
                    {
                        Key: 'Product',
                        Value: 'product'
                    },
                    {
                        Key: 'Stage',
                        Value: 'dev'
                    }
                ]
            },
            DistributionConfig: {
                CallerReference: new Date().toISOString(),
                Comment: `Managed by MediaPackage - ${id}`,
                Enabled: true,
                /**
                7. CloudFront ディストリビューションのオリジンには 2つ設定します。
                1つが MediaPackage のエンドポイントに対するものと、
                もう 1つが MediaPacakge サービスに対するものです。
                基本的には MediaPackage のエンドポイントに対するオリジンを利用します。
                例外時に向けるオリジンが MediaPacakge サービスに対するものになります。
                */
                Origins: {
                    Quantity: 2,
                    Items: [
                        {
                            DomainName: mediaPackageHostname,
                            Id: targetOriginId,
                            CustomOriginConfig: {
                                HTTPPort: 80,
                                HTTPSPort: 443,
                                OriginProtocolPolicy: 'match-viewer'
                            }
                        },
                        {
                            DomainName: 'mediapackage.amazonaws.com',
                            Id: "TEMP_ORIGIN_ID/channel",
                            CustomOriginConfig: {
                                HTTPPort: 80,
                                HTTPSPort: 443,
                                OriginProtocolPolicy: 'match-viewer'
                            }
                        }
                    ]
                },
                /**
                8. CacheBehaviors のいずれにも当てはまらなかった場合の
                キャッシュの振る舞いを定義します。
                MediaPackage は タイムシフト表示機能を使用する際等で、クエリ文字列に start, m, end を利用しています。
                そのため、それらの文字列は WhitelistedNames に含め QueryString には true を指定しておきます。
                DefaultCacheBehavior に引っかかる挙動は例外的扱いなので、
                使用するオリジンは MediaPackage サービスのものを設定します。
                */
                DefaultCacheBehavior: {
                    ForwardedValues: {
                        Cookies: {
                            Forward: 'whitelist',
                            WhitelistedNames: {
                                Quantity: 3,
                                Items: [
                                    'end', 'm', 'start'
                                ]
                            }
                        },
                        QueryString: true,
                        Headers: {
                            Quantity: 0
                        },
                        QueryStringCacheKeys: {
                            Quantity: 0
                        }
                    },
                    MinTTL: 6,
                    TargetOriginId: "TEMP_ORIGIN_ID/channel",
                    TrustedSigners: {
                        Enabled: false,
                        Quantity: 0
                    },
                    ViewerProtocolPolicy: 'redirect-to-https',
                    AllowedMethods: {
                        Items: [
                            'GET', 'HEAD'
                        ],
                        Quantity: 2,
                    },
                    MaxTTL: 60
                },
                /**
                9. CloudFront のエラーコード全ての TTL に 1sec を設定します。
                MediaPackage のエラーのキャッシュが長時間持続してしまうと、
                その間は MediaPackage で正常に配信できているとしても、
                復旧できない状態となるからです。
                */
                CustomErrorResponses: {
                    Quantity: 10,
                    Items: [
                    {
                        ErrorCode: 400,
                        ErrorCachingMinTTL: 1
                    }, {
                        ErrorCode: 403,
                        ErrorCachingMinTTL: 1
                    }, {
                        ErrorCode: 404,
                        ErrorCachingMinTTL: 1
                    }, {
                        ErrorCode: 405,
                        ErrorCachingMinTTL: 1
                    }, {
                        ErrorCode: 414,
                        ErrorCachingMinTTL: 1
                    }, {
                        ErrorCode: 416,
                        ErrorCachingMinTTL: 1
                    }, {
                        ErrorCode: 500,
                        ErrorCachingMinTTL: 1
                    }, {
                        ErrorCode: 501,
                        ErrorCachingMinTTL: 1
                    }, {
                        ErrorCode: 502,
                        ErrorCachingMinTTL: 1
                    }, {
                        ErrorCode: 503,
                        ErrorCachingMinTTL: 1
                    }
                    ]
                },
                /**
                10. CloudFront ディストリビューションのキャッシュの振る舞いを 2つ定義します。
                それぞれの設定内容は基本的に DefaultCacheBehavior で定義したものと同様です。
                しかし、利用するオリジンは MediaPackage エンドポイントに向けたものを利用します。
                1つは Microsoft Smooth Streaming での配信時に利用する
                index.ism に対するもので Smooth Streaming を true に設定しています。
                もう 1つは上記 Microsoft Smooth Streaming 以外の
                全てに当てはまるストリーミングに適用されるものになります。
                */
                CacheBehaviors: {
                    Quantity: 2,
                    Items: [{
                        MinTTL: 6,
                        PathPattern: 'index.ism/*',
                        TargetOriginId: targetOriginId,
                        ViewerProtocolPolicy: 'redirect-to-https',
                        AllowedMethods: {
                            Items: [
                                'GET', 'HEAD'
                            ],
                            Quantity: 2,
                        },
                        ForwardedValues: {
                            Cookies: {
                                Forward: 'whitelist',
                                WhitelistedNames: {
                                    Quantity: 3,
                                    Items: [
                                        'end', 'm', 'start'
                                    ]
                                }
                            },
                            QueryString: true,
                            Headers: {
                                Quantity: 0
                            },
                            QueryStringCacheKeys: {
                                Quantity: 0
                            },
                        },
                        SmoothStreaming: true
                    }, {
                        MinTTL: 6,
                        PathPattern: '*',
                        TargetOriginId: targetOriginId,
                        ViewerProtocolPolicy: 'redirect-to-https',
                        AllowedMethods: {
                            Items: [
                                'GET', 'HEAD'
                            ],
                            Quantity: 2,
                        },
                        ForwardedValues: {
                            Cookies: {
                                Forward: 'whitelist',
                                WhitelistedNames: {
                                Quantity: 3,
                                Items: [
                                    'end', 'm', 'start'
                                ]
                                }
                            },
                            QueryString: true,
                            Headers: {
                                Quantity: 0
                            },
                            QueryStringCacheKeys: {
                                Quantity: 0
                            },
                        }
                    }]
                },
                PriceClass: 'PriceClass_All'
            }
        }
    }).promise()
  }
}

createDistributionForMediaPackage で作成したディストリビューションは、公式ページに記載された手順 で作成した CloudFront ディストリビューションと同等のものになります。

詳細な説明はインラインコメントにて書きましたが、一応補足説明を少し付け加えておきます。

随所に出てくる Quantity について

Quantity には Items で指定する項目の数を入力します。 例えば HeadersQueryStringCacheKeys には Items に何も指定していないため、Quantity0 を指定します。

しかし、AllowedMethodsWhitelistedNames には Items に指定した項目数である 23Quantity に入力しています。Quantity の数と Items の項目数が合わないと、エラーが発生するため、注意が必要です。

mediapackage:cloudfront_assoc を定義する意味

CloudFront ディストリビューションのタグに mediapackage:cloudfront_assoc で紐付ける MediaPackage のチャンネル ARN を指定することで、MediaPackage コンソールから紐付けられた CloudFront ディストリビューション情報を参照できるようになります。

試しに紐づけられた MediaPackage のチャンネルのエンドポイント詳細ページに遷移すると、
下記のような画面が確認できるはずです。

mediapackage:cloudfront_assoc で紐付いた CloudFront ディストリビューションが確認できる
mediapackage:cloudfront_assoc で紐付いた CloudFront ディストリビューションが確認できる

なお、本記事内のソースコードでは他にも Id, Product, Stage といったタグを定義していますが、MediaPackage とは関係無いものなので削除して問題ありません。

updateDistribution を実行する際の注意点

これは今回の記事内容とは直接関係ないのですが、地味にハマったので載せておきます。

CloudFront では createDistribution の時に要求されるパラメータよりも updateDistribution で要求されるパラメータのほうが多いです。 AWS 公式ページの比較表にある通りです。

そのため、updateDistribution で設定を一部更新したいだけなのに、とても多くのパラメータを指定する必要があり非常に面倒です。例えば CloudFront ディストリビューションの Enable/Disable を切り替えるだけでも 30 個近いパラメータを指定する必要あります。

上記の入力の手間を省くのには getDistribution で取得した既存のディストリビューション情報を改変する形で updateDistribution のパラメータを作成すると楽でした。

今回のソースコードの内容を参照すると disableDistribution が該当します。

// 1. getDistribution を実行して CloudFront ディストリビューションの情報を取得する
const distribution = await this.getDistribution(id);
// 2. CloudFront ディストリビューションの設定内容を取得する
const config = distribution.Distribution.DistributionConfig;
// 3. CloudFront ディストリビューションの Enabled/Disabled を切り替えるオプションを改変する
config.Enabled = false;
// 4. 3. で改変した内容を updateDistribution で CloudFront ディストリビューションに反映する
return await this.cloudFront.updateDistribution({
    Id: id,
    IfMatch: distribution.ETag,
    DistributionConfig: config
}).promise();

おわりに

ニッチな内容なので、本記事内容を今後利用するかどうかは分かりませんが、一応得た知見を記事として残しておきました。同様のことを行う必要が出てきた方の参考になれれば幸いです。

参考リンク

管理者以外も Chocolatey でインストールできるようにする

2020年11月

はじめに

Chocolatey 自体はとても便利で気に入っているのですが、管理者権限で PowerShell を実行してから都度 choco install ~ するのが面倒すぎて、何か良い解決策は無いかと調べていました。:mag:

一応公式サイトにも 管理者以外でインストール可能にする手順 は掲載されているのですが、私の手元の環境だとエラーが発生してしまいセットアップできませんでした。。 :persevere:

しかし、お手軽に管理者以外でもインストール可能にする方法を他に見つけましたので、備忘録も兼ねて記事として残しておくことにしました :writing_hand:

対処法

  1. PowerShell を起動して powershell start-process cmd -verb runas を実行する
  2. 別ウインドウでコマンドプロンプトが起動したら choco install gsudo を実行する
  3. 先ほどの PowerShell で gsudo choco install ~ でインストールが成功するか確認する

powershell start-process cmd -verb runas コマンドを実行すると、別ウインドウで管理者として実行したコマンドプロンプトが起動します。そこで choco install gsudo を実行することで、Windows 版の sudo である gsudo をインストールします。

これで gsudo choco install ~ のような形で PowerShell からコマンドを打ち込むことで、管理者として choco install ~ が実行できるようになりました。今後は管理者として PowerShell を起動せずとも各種ソフトウェアのインストールが Chocolatey で可能になります! :relaxed:

おわりに

PowerShell については最近使い始めたせいもあって不慣れなため、本記事タイトルの内容をどう実現すればよいか良く分かりませんでした。。

ただ sudo のような仕組みが Windows にもあれば、それだけでサクッと解決できそうだなーと思いググっていたら、非常に楽な解決策を見つけることが出来て満足です :laughing:

参考リンク

GitHub 接続時の ~/.ssh/config の書き方

2020年11月
本記事ではタイトルの内容に加えて ~/.ssh/config に対する基本的な理解を深めるため、各種設定項目に関する説明も付随して行います。GitHub を例に挙げてはいるものの、Bitbucket や GitLab、Git サーバへ接続する際にも利用可能な ~/.ssh/config の設定について記載しております。

~/.ssh/config の書き方

SSH キーが未登録の場合は 公式サイトの手順 に従って鍵の生成・登録までを事前に行っておきます。

まず GitHub へ接続する ~/.ssh/config の設定を見ていきます。GitHub で認証に使用する SSH キーは登録済みで秘密鍵は ~/.ssh/github へ配置している想定です。

~/.ssh/config
Host github.com
IdentityFile ~/.ssh/github
User git

~/.ssh/config の各種設定項目は下記になります。

項目 説明
Host ホスト名を指定する
IdentityFile 接続時に使用する秘密鍵を指定する
User 接続時のユーザ名を指定する

設定を行うことで github.com に SSH 接続する際、ユーザ名に git が指定され、~/.ssh/github に存在する秘密鍵を用いて SSH 接続を試みるようになります。
早速 github.com 接続時に正しく認証が通っているか確認するため、適当なプライベートリポジトリを git clone してみます。

プライベートリポジトリを git clone した実行結果 (成功)。無事に git clone できることが確認できたら成功です。

接続先に応じて秘密鍵を使い分けたい

GitHub で常に同一の秘密鍵を用いて認証を行う際は問題ないのですが、例えば GitHub アカウントをプライベート用と仕事用で使い分けていて、リポジトリ先に応じて秘密鍵を使い分けたいというケースはあると思います。

その場合は ~/.ssh/config の設定と git のリモートリポジトリへの接続情報を変更することで対応可能です。
例えばプライベート用の GitHub アカウント A と仕事用の GitHub アカウント B が存在するとします。その際 github-A がホストの時は A の秘密鍵を、github-B がホストの時は B の秘密鍵を利用するための設定は下記になります。

~/.ssh/config
# プライベート用の GitHub アカウントで利用する接続設定
Host github-A
HostName github.com
IdentityFile ~/.ssh/github-A
User git
# 仕事用の GitHub アカウントで利用する接続設定
Host github-B
HostName github.com
IdentityFile ~/.ssh/github-B
User git

新たに HostName という項目を設定しました。HostName には接続先を指定します。HostName は何も指定しない場合 Host と同じ値が設定されます。
今回は同一接続先に対してアカウントを切り替えたいので、Host にはアカウント識別可能な値 (ex: github-A, github-B, etc.) を、HostName に接続先である github.com を明示的に指定してアカウント毎に設定をわけました。
次に git のリモートリポジトリへの接続情報を変更します。

~/.ssh/configUser を指定している場合は該当 Host へ接続時に自動的にユーザが設定されるため、接続 URL からユーザ名である git は取り除いても問題ありません。

# 現在のリモートリポジトリ URL
git remote -v
origin  git@github.com:nikaera/private-repository.git (fetch)
origin  git@github.com:nikaera/private-repository.git (push)
# リモートリポジトリ URL を
# git@github.com:nikaera/private-repository.git から
# github-A:nikaera/private-repository.git に変更する
# (ユーザ名の git は変更先から取り除いている)
git remote set-url origin github-A:nikaera/private-repository.git
# 新たに設定したリモートリポジトリ URL
git remote -v
origin  github-A:nikaera/private-repository.git (fetch)
origin  github-A:nikaera/private-repository.git (push)

この状態で先ほど git clone してきたプライベートリポジトリ内で git ls-remote origin コマンドを実行してみて正常に結果が取得できれば正しく設定できています。

次に B のアカウント情報で接続を試みます。

# 現在のリモートリポジトリ URL
git remote -v
origin  github-A:nikaera/private-repository.git (fetch)
origin  github-A:nikaera/private-repository.git (push)
# リモートリポジトリ URL を github-A:nikaera/private-repository.git から
# github-B:nikaera/private-repository.git に変更する
git remote set-url origin github-B:nikaera/private-repository.git
# 新たに設定したリモートリポジトリ URL
git remote -v
origin  github-B:nikaera/private-repository.git (fetch)
origin  github-B:nikaera/private-repository.git (push)

B には先ほどのプライベートリポジトリの読み取り権限が無い状態で、git ls-remote origin コマンドを実行してみると失敗するはずです。

これで今後は git のリモートリポジトリ URL を一度書き換えておくだけで、それぞれ適切な秘密鍵で GitHub 認証する設定ができました。
単に Git サーバに SSH 接続する際の設定情報としては、上記項目を抑えておけば問題無いです。しかし、他にも設定したほうが良い項目や、場合によっては設定が必要な項目も存在します。

基本的に設定しておいた方が良い項目

他に設定しておいたほうが良い項目には下記があります。

~/.ssh/config
Host github.com
IdentityFile ~/.ssh/github
User git
IdentitiesOnly yes # IdentityFile で指定した秘密鍵でのみ認証を試みる
Compression yes # Git でのファイル転送時に圧縮する
項目 説明
IdentitiesOnly IdentityFile で指定した鍵ファイルでのみ認証を行うかどうか指定する boolean (yes or no)
Compression 圧縮転送を行うかどうか指定する boolean (yes or no)

IdentityFile で指定したファイル以外で認証が通ってしまう状況だと、意図したアカウントで適切に認証が通せていない可能性が出てきてしまいます。

そのため、特別な理由がなければ IdentitiesOnly には yes を設定しておいたほうが良いです。

IdentitiesOnlyno に設定しておくと ssh-add で登録したキーの中からも認証を試みるようになります。

また、Compression についても yes に設定しておくことをオススメします。プログラミングを行っているプロジェクトでは、テキストファイルの数が大半を占めるはずなので、圧縮転送を有効にしておいたほうがファイル転送の速度向上を見込めるからです。

ただし Compression は大容量の単一のファイルがアップされているなどして、圧縮効率の悪いファイルを含んでいるプロジェクトの場合は逆に転送速度が低下する恐れもあるので要注意です。

接続先によっては設定が必要な項目

接続先に応じて設定する必要がありそうな項目には下記があります。

~/.ssh/config
Host github.com
IdentityFile ~/.ssh/github
User git
Port 12345 # 接続先のポートが 22 以外の場合指定する
項目 説明
Port SSH 接続時のポート番号を設定する number (ex: 22, 22222, etc.)

例えば自前で用意した Git リポジトリに対して接続する場合、SSH のポートがセキュリティ上の理由等でウェルノウンポートの 22 ではない場合がありえます。その場合は Port を明示的に指定することで接続時のポート番号を適切な値に設定しておく必要が出てくるでしょう。

おわりに

今回は GitHub へ SSH 接続する際の例を元に ~/.ssh/config の設定方法について書きました。~/.ssh/configHost の設定は行っているものの、それに応じて git のリモートリポジトリ URL を変更すべき場合もあることを明示している記事が少なかったので書きました。
また、本記事では Git 接続時の ~/.ssh/config の設定にフォーカスを当てましたが、~/.ssh/config についてはサーバに SSH 接続して作業する際に設定しておくと良い項目等もあります。
~/.ssh/config の各種設定項目について理解していると頻繁に実行するコマンドのコスト削減ができて便利です。特に @oohira さんの こちらの記事 はとても参考になりました。

参考リンク