SIEM on Amazon OpenSearch Serviceによるセキュリティログの可視化について

はじめに

株式会社セガ ゲームコンテンツ&サービス事業本部技術本部開発IT支援部の長谷川と申します。今回はセキュリティログの活用法の一例としてSIEMを用いた可視化方法を紹介します。

目次

背景

昨今セキュリティ対策不足によるデータの抽出やサービス操作される被害が発生しており、ログからユーザの行動を抽出し、可視化するまでを一括管理できるものが求められていました。さまざまなサービスの中でSIEM on Amazon OpenSearch Serviceにてログ情報からユーザの行動を可視化・検索により原因を改善するができるため、利用し始めました。 セキュリティログをgrepやAthena利用して調査を行ってきましたが、可視化を行うまでは進んでおらず全体的なセキュリティを確認するまで相当な時間がかかっていました。上記のサービスを利用することで、原因特定までの時間が軽減されました。

Opensearch(Elasticsearch)とは

  • 大容量データを処理することを想定した全文検索エンジン
  • 複数のノードにデータ分散して保存する分散設計
  • システムログの可視化・検知やユーザへのレコメンドや分析など多種多様な範囲で利用可能

SIEMとは

  • SIEM は Security Information and Event Management の略で、セキュリティ機器、ネットワーク機器、その他のあらゆる機器のデータを収集及び一元管理をして、相関分析によって脅威検出とインシデントレスポンスをサポートするためのソリューションです。Amazon OpenSearch Service は、OpenSearch と OpenSearch Dashboards を大規模かつ簡単でコスト効率の良い方法を使用してデプロイ、保護、実行する完全マネージド型サービスです。Amazon OpenSearch Service の環境に SIEM として必要な機能を実装したのが SIEM on Amazon ES です。

github.com

Cognitoとは

大きくユーザプールとIDプールに分けて認証・認可を行うシステムです

ユーザプールとは

  • ユーザの作成、管理及び認証を行うユーザディレクトリです
  • アプリからの多数の承認フローが利用可能です
  • サインイン後に発行されるトークンを用いて、認証されたサービスへアクセスを行います

IDプールとは

  • ユーザプールにて認証されたユーザがどのリソースへのアクセスに対してユーザの権限を管理します
  • 未承認のユーザに対して、一時的なリソースへのアクセス権を付与することができます
  • ユーザのアクセス権をルールベースおよびアクセスベースでの制御が可能です

アーキテクチャ

設定方法

※今回はセキュリティ面を考慮しまして、VPC内でOpenSearchを構築いたします

  1. SIEMの設定方法は下記より構築しています SIEM構築
  2. ECS on Fargate,SecretsManager,Cognito,ELB,Route53サービスの作成します
    1. terraformにより作成(作成方法については割愛します)
  3. Cognito設定を一部抜粋して紹介します
    1. ユーザプールの作成

      1. 該当のユーザプールのサインインエクスペリエンス欄より、多機能認証を必須設定する
      2. アプリケーション総合よりアプリケーションクライアントの作成をします
        1. 認証フローについては該当の項目を選択します

          Note If you don't specify a value for ExplicitAuthFlows, your user client supports ALLOW_REFRESH_TOKEN_AUTH, ALLOW_USER_SRP_AUTH, and ALLOW_CUSTOM_AUTH. docs.aws.amazon.com

        2. コールバックURL,サインアウトURLを記入します
    2. IDプールを作成します

      1. ユーザアクセス欄より認証されたユーザに対するロールを作成します
  4. nginxプロキシによるCognitio認証設定します
    1. https://repost.aws/ja/knowledge-center/opensearch-outside-vpc-nginx
    2. セキュリティの観点より環境情報などをSecretsManagerより値を取得し、nginxに取得した環境変数を渡します ※下記はタスク定義の設定を抜粋して記載しています
{
    "containerDefinitions": [
    { ------ 中略 ------
        "secrets": [
            {
                "name": "opensearch_domain",
                "valueFrom": <secretsmanager_arn>:<opensearch_domain_key>::
            },
            {
                "name": "cognito_domain",
                "valueFrom": <secretsmanager_arn>:<cognito_domain_key>::
            }
        ]
    }
}

Cognitoによるログイン

  • SIEMのURLにアクセスすることでCognito認証によるログインを行います
  • MFA設定されている場合は次にTOTPよりコードを入力します

セキュリティログの可視化

  • Discover画面にて特定のインデックスで取り込んだログを表示が可能になります

    • フィルターやSearch欄でクエリで絞り込むことで特定ログ・項目の絞り込みが可能になります
  • DashBoardにおいても特定フィルター、特定アカウント、特定ACLの絞り込みにより必要なデータの可視化が可能になります


    クロスアカウントのCloudTrailログの可視化


WAFログの可視化

補足

※今回はアカウント間のデータを一括収集を行い、データの可視化を行う例となっております。

  • 特定の条件(特定のIP、国、UAからDDosアタック)が発生した場合にOpensearchのアラート通知を送るなどでシステム改善の気づきを与えます
  • セキュリティログに限らずアクセスログも収集しているため、Athenaに頼らずに検索や可視化までが一括して可能になるため汎用的な調査が可能になります
  • アクセスログ(CloudFront,ELB,WAF,VPCFlowログなど)の可視化により不要に開放しているセキュリティグループやパスを改善する手がかりを得られ、システム改善にも貢献します
  • 他のメンバーにも確認しやすいDashBoardになっているため、インフラ面の改善に限らずアプリケーション側の改善やBotなどの防ぐべき対象を可視化することができます

まとめ

  • セキュリティログの一元管理および可視化としてSIEMを利用しました
  • 一括管理することで、情報が整理されて必要な条件下でのセキュリティの対策を包括的に行うことが可能になります
  • S3のトリガー経由でログの取り込みが行われるため、同一アカウント・クロスアカウントに限らず様々なサービスログを取り込むことも可能になります

※まずは、セキュリティに不安がある方や様々なサービスを用いて通知疲れになっている方はSIEMを用いて一元管理して可視化してみると効率的な監視の手がかりがつかめると思います。

セガで働くことにご興味を持たれましたら下記サイトにアクセスお願いします。 www.sega.co.jp (C)SEGA

参考

『PSO2 ニュージェネシス』におけるトゥーン表示対応について

1.はじめに

株式会社セガ 技術本部 技術統括室 ソフトシステムセクションの西濱 高志と申します。 今回の記事では『ファンタシースターオンライン2 ニュージェネシス』におけるトゥーン表示対応について紹介いたします。

2.目次

3.前置き

記事内の画像は開発時のゲーム画面となります。実際にリリースされているゲーム画面とは異なるところもありますので、あらかじめご了承下さい。

また、表記として『ファンタシースターオンライン2 ニュージェネシス』のことを『NGS』、『ファンタシースターオンライン2』のことを『PSO2』と呼称することにします。

トゥーンレンダリングに関する最新技術の話はありませんが、何かしら参考になれば幸いです。

4.トゥーンレンダリングとは?

トゥーンレンダリングとは、コンピュータグラフィックスにおいてアニメ風、広くは漫画やイラスト風の作画でレンダリングする手法のことを言います。 セルシェーディングとも呼ばれ、ゲームの表現において個人、企業問わず採用する例が増えています。

トゥーンレンダリングを実現する上で、重要な要素となるのが「陰影のマルチトーン化」と「アウトライン表現」の2点になります。

4.1.陰影のマルチトーン化

トゥーンレンダリングにおける陰影は、物理的な計算を解いて表現するフォトリアルな陰影とは違い、デザイナー(アーティスト)が指定、調整した色で陰影も表現されることが多いです。

一般的な実装としては色の段階を設定したテクスチャ(ランプテクスチャ)を用意し、陰影の表現に使用する形が多いです。

しかし陰影を段階的にする都合上、人間の顔など複雑な凹凸がある物体では陰影が綺麗にならないことが多いです。 このため、トゥーンレンダリングでは物体の法線を調整して陰影を綺麗にし、アニメのような表現に近づけるといった工夫がされています。

この記事では、このようなトゥーンレンダリングにおける陰影の段階化を「陰影のマルチトーン化」と呼称することにします。

4.2.アウトライン表現

イラストやアニメにおける、「線画」を表現するため、アウトラインの表示もトゥーンレンダリングでは重要な要素になります。

実装方法としては、「背面法」と「輪郭線の抽出」の二つがあります。 「背面法」とは対象のモデルについて頂点を法線方向に拡大して裏面を描画した後、通常の描画を上塗りすることでアウトラインを表現する手法になります。1

「輪郭線の抽出」とは、レンダリング途中で生成した深度情報等をもとにフィルタ処理をすることで輪郭線の抽出を行い、アウトラインを描画する手法になります。 下記図は、出力された画像に対してSobelフィルタを用いてアウトラインを描画する例になります。

それぞれ特徴があり、まとめると下記のようになります。

手法 クォリティ 処理負荷 モデルデータの改良
背面法 描画する物体が多くなるほど高くなる 必要
輪郭線の抽出 描画する物体に関わらずほぼ一定 不要

このため、プレイヤー等、描画する物体が少ない場合はクォリティが高い「背面法」、背景や小物など描画する物体が多い場合は処理負荷を抑えられる「輪郭線の抽出」と使い分けをすることが、最近のゲームでは多いです。

5.『NGS』におけるトゥーン表示対応について

『NGS』では、元々トゥーンレンダリングを想定してモデルデータを作成していません。 このため、いくつか異なるアプローチで実装する必要がありました。 ここでは具体的にどのような実装をしたのかについて紹介いたします。

5.1.『NGS』のレンダリングシステムについて

『NGS』では、不透明の描画について「ディファードレンダリング」を行い、半透明、カットイン画面で「フォワードレンダリング」を行うという実装になっています。 通常のレンダリングパスを図にしたものが下記になります。

また、描画したGBufferの内容が下記になります。

前述したように、モデルデータがトゥーン表示用のものではないので必要な情報を適宜GBuffer等から取得して、活用していくことで実装しました。 最終的なトゥーン表示におけるレンダリングパスが下記になります。

5.2.拡散反射光(陰影)のマルチトーン表現

最初に拡散反射光(陰影)のマルチトーン表現について説明します。 前述したように、拡散反射光(陰影)のマルチトーン表現を行う際はランプテクスチャを用意して、陰影付けを行うのが一般的です。

しかし、『NGS』の既存のモデルデータにランプテクスチャを追加するのは難しく、GBufferにモデル毎のランプテクスチャの値を入れるための容量もありません。 なので、一律のパラメータを用意し、計算で陰影付けを行いました。

具体的なパラメータは下記になります。

struct ToonDiffuseParameter
{
    float brightness;     //!< 陰影の明るさ
    float subsurface;     //!< どの程度表面下散乱の色(血の色)を使用するか
    float threshold;      //!< どの段階から暗くするか
    float gradation;      //!< どの程度陰影をくっきりさせるか
}

ToonDiffuseParameter m_diffuse_bright; //!< 明るい部分のパラメータ
ToonDiffuseParameter m_diffuse_middle; //!< 中間部分のパラメータ
ToonDiffuseParameter m_diffuse_dark;   //!< 暗い部分のパラメータ

これらのパラメータを用いて、陰影を3段階に分けています。

『NGS』の拡散反射光は「Lambert BRDF」を使用しています。 このため、トゥーンレンダリングにおいてもモデルの法線とライトの内積値を元に、陰影を3段階に分けて陰影付けに使用しています。 実際にパラメータを調整すると下記のように陰影の調整ができます。

また、GBufferにどのシェーダを使用しているかの情報(ShaderID)を格納しているため、陰影をつけている物体の判別ができます。 これを用いて『NGS』では「毛髪用のパラメータ」と、「それ以外のパラメータ」の二つを用意して別々で調整ができるようにしています。2

5.3.鏡面反射光(スぺキュラ)のマルチトーン表現

次にスぺキュラのマルチトーン表現について説明します。 トゥーンレンダリングのスぺキュラ表現では、ワンポイントで表現することが多いかと思います。 実装方法としては、「スペキュラ自体をテクスチャに描きこむ」、或いは「スぺキュラマスクを用意して指定した箇所のみスペキュラを描く」等があります。

しかし、『NGS』ではゲーム中に登場するNPCの「グレン」のように、顔の色がほぼ黒色のキャラクターを作ることができます。 このようなキャラクターは前述した、「拡散反射光(陰影)のマルチトーン化」をしても陰影ができず、真っ黒になってしまうといったことが発生しました。

このため、『NGS』でも「陰影のマルチトーン化」と同様に「スぺキュラのマルチトーン化」を行うことで、黒色のキャラクターでもアニメ調のような陰影が見えるように実装を行いました。 下記が調整するパラメータになります。

struct ToonSpecularParameter
{
    float brightness;     //!< スペキュラの明るさ
    float exponent;       //!< スペキュラの広がり
    float gradation;      //!< どの程度くっきりさせるか
}

ToonSpecularParameter m_specular_bright; //!< 明るい部分のパラメータ
ToonSpecularParameter m_specular_middle; //!< 中間色のパラメータ
ToonSpecularParameter m_specular_dark;   //!< 暗い部分のパラメータ

基本的には「拡散反射光(陰影)のマルチトーン化」とほぼ同じような処理でスぺキュラを3段階に分けて描画しています。

実際にパラメータを調整すると下記のような表示になります。

特にパラメータの調整では黒色のキャラクターでも真っ黒な顔にならないようにしつつ、キャスト(ロボット)等の金属の光沢がカッコよく表現できるように調整しました。

また、スペキュラのマルチトーン化は「肌」、「毛髪」、「その他」で別のパラメータを調整しています。

5.4.アウトライン表現

次に、「アウトライン表現」について説明します。 前述したように、トゥーンレンダリングのアウトライン表現では「背面法」と「輪郭線の抽出」の二つが手法としてあります。 『NGS』では、下記のような制約があり「背面法」を採用することができませんでした。

  • モデルについて、片面しかないモデルやハードエッジを使用しているモデルがあり、法線方向に拡大した時にアウトラインがひび割れてしまう等、不自然な描画になってしまうことがある。
  • 多数のプレイヤー、NPCをトゥーン表示にする必要があり、「背面法」では処理負荷が高くなってしまう。
  • 半透明のアウトラインについて対応することが難しい。

このため、「輪郭線の抽出」を用いてアウトライン表現を実装しました。

5.4.3.輪郭線の抽出

『NGS』ではGBufferのうち「深度」、「法線」、「マテリアル」情報を用いて輪郭線の抽出を行いました。 処理としては、近傍の値と比較して一定以上の差異がある場合、アウトラインを描画するという内容になります。

また、ShaderIDを用いて、「顔と毛髪以外は法線によるアウトラインを使用する」、「毛髪のみ、マテリアルによるアウトラインを使用する」と、カテゴリ毎に分けて使用することで、アウトラインを描画するようにしました。 下記が、それぞれの情報を用いて輪郭線の抽出を行った結果になります。

パラメータとしては、アウトラインの太さや色を調整できるようにしました。 特にアウトラインの色については、近傍の色を元に明度と彩度の調整をすることで色トレスができるようにしています。 また、これらのパラメータは「肌」、「毛髪」、「その他」で調整ができるように実装しています。

struct ToonOutlineParameter
{
    float brightness;   // 明度
    float saturation;   // 彩度
}

ToonOutlineParameter skin_outline_param;    // 肌のアウトライン
ToonOutlineParameter hair_outline_param;    // 毛髪のアウトライン
ToonOutlineParameter other_outline_param;   // その他のアウトライン
float outline_weight;                       // アウトラインの太さ

6.トゥーン用の顔データの作成について

ここまでの処理で、プログラム上の処理が一通り完了しました。 しかし、既存のモデルではやはり顔の陰影が綺麗になりにくいといった問題が発生しました。

これに対処するには、顔モデルの法線を調整する等、データに対して調整を行う必要があります。 『NGS』では、トゥーン表示用に法線等を調整したモデルを新たに用意し、トゥーン表示に切り替えた際に顔モデルを切り替えることで対処するようにしました。3

元々、『PSO2』から『NGS』にアップデートする際、顔のデータは『PSO2』で体のデータは『NGS』といったように、顔と体で別のデータを切り替えることができるシステムとデータを作っていました。 このため顔の切り替え処理については実装をスムーズに行うことができました。

詳細な情報については、CEDEC2022の資料「『PSO2 ニュージェネシス』 長期運営タイトルにおける、大規模バージョンアップの開発事例の紹介」を参照してください。4

トゥーン用の顔データの作成では主に「法線の調整」、「目の陰影を追加」、「ラフネス値の調整」をデザイナー(アーティスト)の方に行っていただきました。 行った調整について簡単にご紹介させていただきます。

6.1 法線の調整について

顔の法線については、モデルの頂点法線について下記のような調整を行っています。

  • 頬の陰影が綺麗になるように調整
  • 口元や、鼻の陰影がくっきりでるように調整

下図が通常時との比較になります。

6.2 ラフネス値の調整について

一部の顔モデルについては、頬、鼻先、唇にスペキュラが出やすくするためにラフネスの調整をしています。 下記が通常時との比較になります。

6.3 目の影について

イラストの表現では、目に対してまぶたの影をつけることで立体感を表現することがあります。 『NGS』のトゥーン表示でも同様に目に影をつけることでイラスト特有の立体感のある目を表現しています。

7.その他の対応

以上がおおまかな、対応内容になります。 しかし、後付けの処理であるため様々な問題が発生しました。 ここでは、発生した問題や対処した方法について紹介したいと思います。

7.1.カットイン画面のトゥーン表示対応

『NGS』では、キャラクターの顔が小さいウインドウに表示される「カットイン描画」と呼ばれるものがあります。この機能は主に、チャット画面やショップでのプレビュー画面で使用されます。

このカットイン画面は、全てフォワードレンダリングで描画しています。 既存の処理ではGBufferの情報を出力しない設計になっていたので、新しくGBufferの情報を一部追加で出力するように改良する必要がありました。 最終的には下記のように、フォワードレンダリングの結果だけでなく法線、マテリアル情報も出力するように変更しました。

後は、前述した処理と同様に「陰影のマルチトーン化」や「アウトラインの表示」を行っています。 下記がトゥーン表示時における、カットイン描画のパスになります。

7.2.半透明のアウトライン

『PSO2』では半透明を使用しているものが多くあります。例として、プレイヤーが身に着けられるアクセサリーである「フォトンマフラー」は、不透明と半透明の組み合わせで作られています。5

このデータでアウトラインを表示した場合、不透明部分だけ考慮されるため、半透明部分で不自然なアウトラインが描画される問題が発生しました。

この問題に対処するため、新しく半透明部分を表すアルファマスク作成し、半透明部分にアウトラインが描画されない対応を行いました。 下図が対応後の結果になります。

8.調整するパラメータについて

内部で調整できるパラメータの合計は最終的に60個になりました。 実装当初、用意したパラメータは固定値とする予定でした。

しかし、『NGS』では人間、キャスト(ロボット)、或いはその枠を超えた様々なキャラクターを作成することができます。

システムの都合で個別にパラメータを設定することが難しいですがそれでも調整できる方が良いと判断し、上記のパラメータをシンプルな形で調整できるようにしました。

製品では「影の濃度」と「輪郭の強調」の二つのパラメータを調整することで、見た目が変わるようにしています。

9.まとめ

これらの対応を行うことで事前にトゥーンレンダリングを想定していなくとも、ある程度のクォリティでキャラクターのトゥーン化を行うことができました。

リリース前はプレイヤーの皆様に受け入れてもらえるか不安でしたが、「キャラクタークリエイトの幅が広がった」、「過去のコスチュームの見た目がよくなり使用する機会が増えた」といった好意的な意見があり、実装してよかったと感じました。

10.最後に

汎用ゲームエンジンが流行りつつありますが、自社でゲームエンジンを開発することで、どういった処理が最適なのかを基礎から学び、ひいては独自の実装で特徴ある表現を可能にすることができます。特に今回のような「ゲームの表現を途中で変えるような大きな変更対応」は自社のゲームエンジンでしか実現ができないものだと思います。自社でゲームエンジンを「使う」だけでなく、「作る」ことができる環境。 セガで働くことにご興味を持たれましたら下記サイトにアクセスお願いします。

(C) SEGA www.sega.co.jp


  1. 描画負荷としては裏面を後で描画する方が良いですが、わかりやすさのために上記の記載になっています。
  2. キャラクターの肌について、肌ではないけど肌と陰影を合わせたいアクセサリーがあり、パラメータを分けることはしませんでした。
  3. トゥーン用に用意してあるのは、『NGS』で新しく作成した顔モデルのみになります。
  4. https://cedil.cesa.or.jp/cedil_sessions/view/2614
  5. 『PSO2』における、髪型も半透明と不透明の組み合わせで作られています。

CEDEC 2023 セガサミーグループによるセッション紹介!

今年も夏がはじまり、CEDECが近くなってまいりました。

今回はハイブリッド開催となったゲーム業界最大のカンファレンスに、

セガサミーグループからも登壇します!

cedec.cesa.or.jp

会期:2023年8月23日(水)~8月25日(金)

今年で8回目となる、セッションと登壇者紹介に加え、ここでしか見られない当日の資料の抜粋チラ見せ付き(一部公募のみ)でブログ本文は株式会社セガ、第3事業部の麓がご紹介致します。

続きを読む

『PSO2 ニュージェネシス』におけるハイダイナミックレンジ対応について

1 はじめに

株式会社セガ ゲームコンテンツ&サービス事業本部 技術本部 技術統括室 ソフトシステムセクションの西濱 高志といいます。 現在グラフィックスプログラマとして株式会社セガが開発運営する『PSO2 ニュージェネシス(以下『NGS』)』に携わっています。

今回の記事では『NGS』におけるハイダイナミックレンジ(HDR)ディスプレイ対応について紹介いたします。

2 目次

3 注意事項

この記事において、2点注意することがあります。

3.1 各ゲームにおけるHDRディスプレイ対応についての注意事項

現在、様々なゲームにおいてHDRディスプレイ対応がされています。 しかし、対応方法については各ゲームで異なっている場合や、ユーザーが調整できるパラメータに違いがある場合があります。 この記事ではあくまで『NGS』におけるHDR対応の話になりますのでご注意ください。

3.2 掲載している画像についての注意事項

この記事では随所にSDRディスプレイへの出力結果とHDRディスプレイへの出力結果の比較画像を掲載しています。 しかし、HDRで出力した画像はHDR出力に対応をしているディスプレイやデバイスでないと正しく表示することができません。

そこで、SDRとHDRの違いがわかりやすくなるように加工したSDR画像を掲載しています。 実際の数値や実機上での見た目とは異なりますのでご注意ください。

4 ハイダイナミックレンジ(HDR)とは?

それでは、本題に入っていきたいと思います。 HDRとは一言でいってしまうと「従来の標準ダイナミックレンジ(SDR)と比べて、より広い範囲の明るさを表現できる技術、或いはビデオ映像」のことを言います。

SDRとの違いは表現できる「明るさの幅」、「色の幅」の2点になります。 この2つの幅についてHDRの方が圧倒的に広いため、今まで表現できなかった明るさや色を表現できるようになります。

実際に、同じシーンでHDRとSDRを出力した結果を掲載します。 今回、比較に用いたのはゲームの拠点となる「セントラルシティ」と、ゲームプレイ時に発生する「ナーデレフのライブイベント」になります。 違いとしては、主にハイライト部分の箇所と全体的な色合いになります。

セントラルシティにおける比較(左:SDR出力、右:HDR出力)
ライブイベントにおける比較(左:SDR出力、右:HDR出力)

セントラルシティにおいては、太陽の部分についてHDRの方が白飛びしておらず、色合いについても植物の緑色などが色濃くでているのがわかるかと思います。 ライブイベントにおいては、後ろにあるライトについてHDRでは白飛びせずライトの色が識別できるようになっているのがわかるかと思います。

4.1 明るさの違い

ディスプレイにおける明るさはnit(ニット)という単位面積あたりの明るさ(カンデラ毎平方メートル)で表されます。 出力することが可能な明るさの範囲はディスプレイのスペックによって違いますが、SDRとHDRでおおよそ下記の範囲になります。

  • SDR:0.1nit以下~数百nit
  • HDR:0.1nit以下~数千nit

あくまでイメージとなりますが表現できる白色について見た時、下記画像のような違いがあります。

4.2 色の違い

HDRとSDRでは利用されている色空間が違うため、表現できる色の幅についても違いがあります。 具体的にはSDRではRec.709が利用され、HDRではRec.2020が利用されています。

それぞれの色空間についてのグラフは下記になります。

あくまでイメージとなりますが、実際の見た目としては赤色(R)、緑色(G)、青色(B)で下記画像のような違いがあります。

このようにHDRディスプレイ対応を行うことで、明るさの幅と色の幅が広がり、今まで表現することができなかった色や、白飛びしてしまっていたライトの光等を精密に表示することができるようになります。

5 HDRディスプレイ対応について

ここまでの内容でSDRとHDRでどういった所が違うのかについて、説明いたしました。 ここからは『NGS』における対応方法について説明いたします。

5.1 SDR時におけるレンダリングパスについて

レンダリングパスとはグラフィックス処理の順序のことを言います。 『NGS』ではHDR時とSDR時でレンダリングパスは、ほぼ同じ構成になっており、SDR時の出力を基準としてパラメータでHDRへ拡張をするといった対応をしています。 このためSDR時の処理について先に説明します。

下記がSDR時におけるレンダリングパスになります。 注目する点としては、それぞれの処理における「輝度の範囲」、「色空間」、「テクスチャフォーマット」になります。 またオレンジ色はHDRの区間、青色はSDRの区間を表しています。

SDR時のレンダリングパス

今回はHDRディスプレイ対応と関連性がある、「Gbuffer描画からHDRポストプロセスにかけて行うHDRレンダリング処理」、「トーンマッピングとガンマ補正処理」、「カラーグレーディング処理」について簡単に説明いたします。1

5.1.1 HDRレンダリング処理について

HDRレンダリングとは十分な計算精度を保ったまま幅広い諧調を扱うレンダリング方法のことを言います。 SDRで出力する場合でもHDRの範囲で一旦描画を行い、この後の「トーンマッピングとガンマ補正処理」においてSDRに変換することで、幅広い諧調を持った画像を出力できるようになります。 HDRレンダリングの結果が下の画像となります。

HDRレンダリングの結果

5.1.2 トーンマッピングとガンマ補正処理について

トーンマッピングとガンマ補正処理においては、先ほどのHDRレンダリングの結果をSDRディスプレイにおいて適切な見た目となるように変換する処理を行っています。2

5.1.2.1 トーンマッピング

ゲームでは生成したHDRレンダリング画像に対して、トーンカーブを用いることで出力するディスプレイで正確な表現ができるように変換します。 SDRディスプレイに出力する場合、0~1の範囲に変換します。 トーンカーブは様々な種類があるのですが、『NGS』ではACES Filmicを使用しています。

// 使用している係数
// a = 3.0
// b = 0.03
// c = 2.43
// d = 0.59
// e = 0.14
float3 ACESFilmic(float3 x, float a, float b, float c, float d, float e){
    return (x*(a*x+b))/(x*(c*x+d)+e);
}

『NGS』において設定しているACES Filimicの係数を当てはめた結果が下記の画像となります。横軸が変換前の値で、縦軸が変換後の値です。

SDR時のトーンカーブ

輝度値が低いところでは変化が大きくなり輝度値が高いところでは変化が緩やかになり、1.0に収束していることがわかるかと思います。 トーンマッピングを行った結果が下記になります。

トーンマッピング後の画像

5.1.2.2 ガンマ補正について

ガンマ補正の説明をする前に、まず電気光伝達関数(Electro-Optical Transfer Function)と光電伝達関数(Opto-Electronic Transfer Function)について説明します。 (以降、電気光伝達関数はEOTF、光電伝達関数はOETFとします。) EOTFは映像信号をディスプレイの光に変換するための関数、OETFはシーンの光を映像信号に変換するための関数を表しています。

現実の世界でカメラを用いて説明しますと、被写体をカメラで撮影した時、カメラの内部でシーンの光を映像信号に変換するOETF処理が行われます。 次にこのカメラ内の映像信号はカメラに内蔵されているディスプレイ等に送られ、ディスプレイ側で映像信号をディスプレイの光に変換するEOTF処理が行われます。 カメラで撮られた画像はこのようにして、私たちの目に届くといった仕組みになっています。

カメラにおけるEOTFとOETF

ゲーム等のCGでは、カメラにあたる部分がないため生成した画像をそのままディスプレイに渡してしまうと、ディスプレイ側でEOTF処理が行われてしまうため、想定と違う見た目になってしまいます。 このため、あらかじめOETF処理を行うことでディスプレイ側で行われるEOTFの効果を相殺します。

ゲームにおけるEOTFとOETF

SDRディスプレイにおけるEOTFは様々有り、ガンマ1.9、ガンマ2.2、ガンマ2.4等と呼ばれます。 これをOETFで相殺する処理を(特にSDRでは一般に)ガンマ補正と呼びます。 『NGS』ではsRGBのガンマ2.2として扱い、ガンマ補正として画素に対して1/2.2を累乗を行うことでSDRディスプレイにおいて適切な見た目で表示できるようにしています。

ガンマ補正後の画像

5.1.3 カラーグレーディング処理について

次にカラーグレーディング処理について説明いたします。 カラーグレーディング処理とは出来上がった画像に対して色味を変える処理のことを言います。 広義としては色調補正、カラーコレクション等と呼ばれたりします。

ゲームではシチュエーションによっては赤味を強くしたいといった要望や、過去回想などにおいてセピア調にしたいといった要望があると思います。 これらの処理を行うのがカラーグレーディングの役目となります。 具体的にはPhotoshop等を用いて作成したルックアップテーブル(LUT)テクスチャを用いて色の調整を行います。

下記画像がカラーグレーディングの比較画像になります。

カラーグレーディングの比較画像(左:なし、右:あり)

見比べてみるとかなり色味が変わってることがわかるかと思います。 『NGS』では、地域、天候等によってLUTを複数用意しており、ライブイベントの場合4つのLUTを使用して、時間変化で切り替えています。

長くなりましたが、以上がSDR時のレンダリングパスの説明となります。

5.2 HDR時におけるレンダリングパスについて

次に、HDR時におけるレンダリングパスについて説明いたします。 基本的には先ほどのSDR時のレンダリングパスからの追加対応について説明していく形になります。 下記が初期に実装したHDR時のレンダリングパスになります。

初期に実装したHDR時のレンダリングパス

違いとしては、SDR時にトーンマッピングとガンマ補正を掛けていた箇所がHDR用のトーンマッピングになり、出力の手前でOETF処理をしているところになります。 また、オレンジはHDR、赤色は最大輝度で変換したHDR、紫色はOETF処理をしたHDRを表しています。

5.2.1 HDR用のトーンマッピングについて

HDR用のトーンマッピングの説明をする前に、まずHDRで表現できる数値について説明いたします。 SDRでは0~1の範囲に変換をしたと思うのですが、HDRでは1.0以上の値で表示が可能となります。

ディスプレイのスペックによって変わってくるのですが例えば最大輝度(ピーク輝度)が350nitの場合、単純なケースでは3.5までの値が描画可能となります。3 これは、HDRの規格上100nitが標準白レベル(1.0 = 100nit)として取り扱っているためです。 このため、1.0以上の範囲で変換できれば対応することができそうです。

まず、最も簡単な方法としてACES Filimicの係数を変更してみることにします。 ACES Filimicの処理について、「x」が無限大に近づくときの値は「a / c」になります。 このため、「a / c」が最大輝度となるように係数を導出すれば表現できそうですね。 「a = 3.0」とすると、「c = 3.0 / 最大輝度」となるため、これを式に当てはめてみます。

// a = 3.0
// b = 0.03
// c = a / L
// L = 最大輝度
// d = 1.0
// e = 0.14
float3 ACESFilmic(float3 x, float a, float b, float c, float d, float e){
    return (x*(a*x+b))/(x*(c*x+d)+e);
}

係数を調整し、1.5に収束するようにした結果が下記になります。

HDR時のトーンカーブ(青:HDR、緑:SDR)

また、この「最大輝度」はパラメータ「HDR設定:最大輝度レベル」としてユーザーが調整できるようにしています。 このカーブを用いれば、ディスプレイの最大輝度に合わせた画像を出力することができます。

しかし、ここで問題が起きました。

5.2.1.1 HDR用のトーンマッピングの問題点

元々『NGS』ではトーンマッピング後はSDR前提で処理が組まれています。 このため、カラーグレーディングやSDRポストプロセス処理で問題が発生しました。

特にカラーグレーディング処理では0.0~1.0の値を前提として作られているため工夫が必要でした。 対策として、最大輝度で除算を行いカラーグレーディング処理を行った後、元に戻すと対応を試してみましたが色合いが不自然になってしまう問題が発生しました。

下の画像がSDR時の画像とHDR時の画像の比較になります。

左:SDR出力、右:HDR出力

今回の対応では「明るさの幅」を拡張したいのですが全体的に青みが強く出てしまっています。 この問題に対処するため、レンダリングパスについて再度見直す必要がありました。

5.2.1.2 HDR時におけるレンダリングパスの改良

先ほどのレンダリングパスについて、改良を加えたものがこちらになります。

改良したHDR時のレンダリングパス

違いとしては、UI描画の手前までSDRで処理を通し、その後HDR用のトーンマッピングをかけているところです。 この対応を行うことでUI描画の手前までの区間はSDR時と同じ処理になるため、色味に不具合が発生しないようにしました。

また、元々のSDR時のレンダリングパスでは8bit精度で処理を通していたのですが16bit精度にしています。 これはSDRに範囲を狭くして、後から引き延ばす都合上精度を落とさないようにするために行っています。

5.2.1.3 HDR用のトーンマッピングの改良

それでは新しくUI描画の手前に移したHDR用のトーンマッピング処理について説明いたします。

HDR用のトーンマッピングではこちらの3つを満たすものを独自実装しました。

  • SDR用のトーンマッピング後の値を使用すること
  • 輝度の値が低いところについてはSDRと同じにすること
  • 輝度の値が高くなるにつれて既存のHDR用のトーンカーブの変化率に近づくようにすること

これらを満たすトーンマッピングを実装したコードが下記になります。

// ガンマ補正の効果を相殺するため、ガンマ2.2を処理
color.rgb = pow(color.rgb, 2.2f);

// 係数を算出
float3 rate = color.rgb;
rate = pow(rate, 10.0f);

// HDR用のトーンマッピングをかける
// max_nit_level:最大輝度
color.rgb = lerp(color.rgb, color.rgb * max_nit_level, saturate(rate));

// UI素材と色空間を揃えるため、再度ガンマ補正を処理
color.rgb = pow(color.rgb, 1.0f/2.2f);

下記がトーンマッピング適用時のグラフになります。

青:既存のHDR用トーンカーブ、緑:SDR用トーンカーブ、灰:改良したHDR用トーンカーブ

グラフを見ると、輝度の値が低いところはSDRと同じで、輝度が高くなるにつれてHDRに近づいているのがわかるかと思います。

この改良したトーンマッピングを行った結果とSDR時の画像を比較したものが下記になります。

左:SDR出力、右:改良したトーンマッピングによるHDR出力

改良前と比べ全体的な色見は変わらず、ライトの白飛びが改善していることがわかるかと思います。

5.2.2 HDRにおけるOETFについて

次にHDRにおけるOETF処理について説明します。 HDRの場合はOETF処理と合わせて下記の処理を行っています。

  • ガンマ2.2を処理
  • 色空間の変換処理
  • 輝度の補正
  • PQ補正

一番最初にガンマ2.2を処理しているのは、ここまでガンマ補正後の画像で処理されているためです。 SDRディスプレイとHDRディスプレイで使用されているEOTFは違うので、ここでガンマ補正前の画像にします。

5.2.2.1 色空間の変換について

『NGS』ではRec.709で描画処理を行っているため、HDRディスプレイにそのまま値を出力すると下記画像のようにSDR時の画像と比べて鮮やかすぎる見た目になってしまいます。

色変換せずに出力した画像

HDRディスプレイではRec.2020で処理するため、Rec.709の値をそのまま渡してしまうと下記の画像のように座標変換で引き延ばされてしまい、想定と違う色味になってしまうのが原因です。

座標変換による引き延ばし

そこで、Rec.709からRec.2020への変換処理を行うことで元の画像と同じ色合いになるようにします。

// Rec.709からRec.2020への変換
float3 ConvertRec709To2020(float3 value){
   float3x3 matrix709to2020 =
   {
      { 0.627404f, 0.329282f, 0.043314f },
      { 0.069097f, 0.919541f, 0.011362f },
      { 0.016392f, 0.088013f, 0.895595f }
   };
   return mul(matrix709to2020, value);
}

これで、SDRディスプレイの時と同じ色合いの画像をHDRディスプレイで出力できるようになります。

5.2.2.2 色空間の変換における改善

上述のように色空間の変換を行うことで元の画像と同じ色合いにしましたが、これはHDRなのにも関わらずRec.709の範囲の色しか扱えていないとも言えます。 本来であればゲームの素材をすべてHDR用に調整して、表示すればいいのですが運営しているタイトルですべての素材をHDR用に調整し直すのは現実的ではありません。 そこで、色の範囲を疑似的に拡張しました。

先ほどRec.709の値をそのまま渡すと、色空間がRec.2020に引き伸ばされると言いました。 これはRec.2020の色の範囲を扱えているとも言えます。 なので、色空間の変更後と変更前で線形補間を行い調整できるようにしました。

この「線形補間の係数」はパラメータ「HDR設定:彩度調整」としてユーザーが調整できるようにしています。

float3 rec709 = color.rgb
 
// rec709の数値をrec2020に変換
float3 rec2020 = ConvertRec709To2020(rec709);
 
//rateはユーザーが調整できるようにする
color.rgb = lerp(rec2020, rec709, rate);

これで、ユーザーの好みに合わせて色域の拡張を行い鮮やかさを調整できるようにしました。 「HDR設定:彩度調整」の値を50(計算上は0.5)にした結果が下記になります。

係数を0.5にした時の画像

5.2.2.3 輝度の補正

次に輝度の補正について説明します。 HDRでよく問題になるのが、SDRと比べて画像が暗くなるという問題です。

これは、SDRとHDRで標準白レベルの明るさに相違があるため発生します。

一般的なSDRディスプレイでは、1.0を100nitで表示しておらず、おおよそ200~300nitで表示されています。 このため、HDRで1.0を100nitとして表示を行うと暗くなるという問題が起きます。

この問題については輝度の補正をすることで対処します。 具体的には標準白レベルをパラメータで制御することで、明るさを調整できるようにします。 HDRは規格上10000nitまで表現が可能ですので、「標準白レベル ÷ 10000」乗算することで補正します。

この「標準白レベル」はパラメータ「HDR設定:標準白レベル」としてユーザーが調整できるようにしています。

// 指定した輝度に補正する
float3 NormalizeBaseNit(float3 value, float base_nit){
    base_nit = max(base_nit, 1.0f);
    return value * base_nit / 10000.0f;
}

5.2.2.4 PQ補正

次にPQ補正について説明します。

HDRディスプレイのEOTFではPQ方式が使用されます。 これは、人間の視覚特性に基づいて新しく開発されたガンマカーブになります。 OETFではこの逆関数であるPQ補正を行います。

// PQ補正
float3 LinearToPQ(float3 value){
    float m1 = 2610.0f / 16384.f;
    float m2 = 2523.0f / 4096.0f * 128.0f;
    float c1 = 3424.0f / 4096.0f;
    float c2 = 2413.0f / 4096.0f * 32.0f;
    float c3 = 2392.0f / 4096.0f * 32.0f;
    
    float3 cp = pow(value, m1);
    float3 n = (c1 + c2 * cp);
    float3 d = (1 + c3 * cp);
    return pow(n / d, m2);
}

以上の処理を行い、描画した結果をSDRディスプレイに表示した結果がこちらになります。

OETF後の画像

SDRとHDRで使用しているEOTFが違うため、白っぽい見た目になりますがこちらの画像をHDRディスプレイで表示することで下の画像のように適切な見た目となります。

ディスプレイに表示される画像

以上がHDR時におけるレンダリングパスの説明になります。

6 調整するパラメータについて

『NGS』で調整できるパラメータは下記3つになります。

  • HDR設定:標準白レベル(輝度の補正で使用)
  • HDR設定:最大輝度レベル(HDR用のトーンマッピングで使用)
  • HDR設定:彩度調整(色域の拡張で使用)

これらのパラメータを用意した理由はディスプレイによって、明るさや色味が違うためです。 『NGS』では調整しやすいように下記のような調整用の画像を用意しました。

調整用の画像

標準白レベルでは250nitが基準となるようにし、最大輝度レベルではそのディスプレイが表現できる明るさに調整できるようになっています。 しかし、ディスプレイの設定やスペックによっては眩しすぎたり、暗くなったりすることがあるため好みに合わせて調整してただくのが適切かと思います。

7 まとめ

長くなってしまいましたが、『NGS』におけるHDR対応についてまとめに入ります。 HDR対応では下の3つのことを行い、SDRの見た目を維持しつつ明るさと色の拡張を行うことができるようにしました。

  • HDR用のトーンマッピング処理
  • 色の範囲の拡張
  • HDRにおけるOETF処理

最初に記述したようにこれは『NGS』におけるHDRの対応方法になります。 ゲームによっては「SDRとは違った絵を出したい」といった要望や「HDR用に素材を調整したい」等の要望によって、対応方法に違いが出てくるかと思います。 この記事にかかれていることについては、あくまで参考として活用いただけると幸いです。

8 最後に

汎用ゲームエンジンが流行りつつありますが、自社でゲームエンジンを開発することで、どういった処理が最適なのかを基礎から学び、ひいては独自の実装で特徴ある表現を可能にすることができます。 自社でゲームエンジンを「使う」だけでなく、「作る」ことができる環境。 セガで働くことにご興味を持たれましたら下記サイトにアクセスお願いします。

(C) SEGA

www.sega.co.jp


  1. Gbuffer描画からHDRポストプロセスの区間については処理を省略して掲載していますのでご注意ください。
  2. 実際の処理としては、トーンマッピング前に露出制御を行っていますが詳細説明は省略いたします。
  3. 表現できる最大値はディスプレイの表示モードによって変わることがあります。

Inverse Kinematics(IK)について

はじめに

株式会社セガ 第3事業部 オンライン研究開発プログラム2部の松本と申します。

今回はInverse Kinematics(IK)に関する基本的な考え方や手法についての記事となります。

ゲーム制作の仕事に携わっているとIKの名前くらいは聞いたことがあるのではないかと思いますが、実際の中身については何をやってるのか良く分からない謎の技術だと感じている人も多いのではないでしょうか。この記事を読んでInverse Kinematicsについての理解を深めたり、あるいは改めて学び直すための良い機会になればと思います。

特にゲーム制作者ではない方にとっては、前提知識が不足していて分かり辛い所もあるかも知れませんが、本題であるIKそのものの概念や処理内容については、高校数学課程の知識 *1 があれば十分に理解できそうな内容になっているので、そうした視点で数学の復習がてら読んでみるのも良いかと思います。SNS等で度々話題になるベクトルや三角関数と言った高校数学で習う項目の有用性を感じてもらうだけでも構いません。

目次

IKの正体

まずはIKというのがどういう物なのかを説明したいのですが、いくつか前提となる知識も必要となるので、そちらから順番に説明します。

FKについて

人体のように複数の関節が連なって構成される骨格構造の姿勢を決定していく際に、構造の開始位置となるルート(=根っこ)関節から始めて、構造の終端側にある関節に向かって順番に平行移動や回転変換による姿勢計算を繰り返すことで、全身の姿勢を決定していく手法が考えられます。

この様な逐次的な手順で多関節連鎖構造の姿勢を解決する手法を、Forward Kinematics(フォワード・キネマティクス=順運動学)と呼びます。長いので今後はFK(エフ・ケー)と省略します。

FKによる平行移動と回転のイメージ図

FKの役割

ゲームに登場する3Dキャラクターの動きは、あらかじめ用意されているモーションデータを再生することで表現されていることがほとんどですが、この処理はFKによって計算されています。つまりFKはモーション再生処理に欠かせない基盤技術という位置付けです。

実際のFKモーションデータの大半は回転データの塊ですが、適切な全身のポーズや動きを作るために、どの関節にどのくらいの回転量を与えるのがいいのかというのは、FKでは決めることができません。計算で決めることができないのはもちろんのこと、人の手を使ったとしてもFK的な手続きによるデータ作成は、極めて非効率的な作業となります。ここで言うFK的手続きというのは、関節の回転角を1つずつ編集することで目的の全身ポーズや動作をつくることを指します。例えば関節が動くフィギュアやプラモで全身のポーズを付ける作業手順が、おおよそこれに該当するので、経験したことのある方は、あの作業で複雑で細かい動きを作るのがどれだけ大変か想像がつくのではないでしょうか?

まとめると、FKは既に決まっている回転データを順番に処理して姿勢を作り上げるという基礎的な役割を担いますが、その回転量そのものを決めることができないため、モーションデータを作成するだけでなく、ゲーム中にモーションの再生結果を加工して別の動きを作り出すのも難しい、ということになります。

ようやくIKの登場

そこで、制御し易い情報を与えることで、FK処理に必要な回転量を決めるための機能が求められます。

特定の部位の位置や姿勢を指定することで、そこに到達するまでの中間の関節の姿勢(=回転量)を全て計算で決める手法、すなわちInverse Kinematics(インバース・キネマティクス=逆運動学)と呼ばれる手法が、その役割を担います。こちらも今後はIK(アイ・ケー)と略します。

IKには様々な手法があり、その手法や適用範囲にも寄りますが、例えば手先の位置を決めたら、そこに連結されている前腕や上腕、場合によっては肩や背骨、そこに連結された反対側の腕の姿勢までも一度に決めるようなことも可能です。これはモーションデータを作る際にも非常に効率的なので、モーションデザイナーが扱うツールには必須の機能となっていますし、ゲーム中で再生されたモーションの結果をゲーム側の都合に合わせて修正・加工したりするのにも使われています。

IKは難しい?

計算でどのように回転量を決めるのかはパッとは思いつかないかも知れませんが、具体的なIKの手法そのものは、知ってしまえば超簡単な物から、理論背景を理解するだけでも難しい物まで様々です。今回紹介するのは基本的なアルゴリズムで、仕組みや理論は比較的簡単な部類に入るものですが、手法そのものの実用性や重要性は非常に高く、今でも多くのゲームで使われているので、学びのコストパフォーマンスはかなり良いはずです。

1ボーンIK

概要

多関節の連鎖で構成される骨格構造を1つ1つ分解すると、1つの関節とそれに対応する骨(ボーン)に相当する部位に分解できます。この単体骨に適用するIKが1ボーンIKです。1ボーンIKを組み合わせて多関節のNボーンIKとすることはできませんが、2ボーン以上のIK処理の多くが内部処理に利用している重要なIKです。

下の図は1ボーンIKの動作概要を示した図です。このような図は今後も出てくるので、その見方と合わせて動作概要を説明します。

1ボーンIKの概念図

図には、ちょっと太めの針のようなモデル表示が灰色と水色で2本分描かれています。これが制御対象の骨の初期配置(灰色)と、1ボーンIK適用後の配置(水色)を表しています。針の尖った方向がこの骨の進行方向になり、針の根本から先端までの長さはそのまま骨の長さを表します。水色の針モデルの延長線上にある白と青の四角いチェッカーマークがありますが、その中心がIKの目標位置を表しています。この図は1ボーンIKを適用したことで、骨の進行方向が目標位置の方向を向いているということを表したものです。

このように、1ボーンIKが達成する処理内容は、ある目標位置が1点与えられたら、骨の進行方向をその点の方向に向けるというものになります。IKは一般的には、与えられた目標位置に骨の先端位置が到達するような姿勢を解として導き出しますが、1ボーンの場合はそれを達成できるのが、目標位置が骨の長さと同じ距離にある場合だけなので、進行方向が合えばOKとしています。

骨の進行方向を目標位置に向けるというのは、単純に関節の位置から目標位置に向けたベクトルを計算して、骨の長さに合わせれば完了という訳にはいきません。骨の進行方向に関してはそれで解決するのですが、関節の姿勢がどのように変化したかは、それだけでは不十分だからです。

具体的な手法の説明に移る前に、少しだけ関節の姿勢について説明します。

前提知識1:X軸Y軸Z軸による姿勢表現について

各関節の姿勢は、それぞれのX軸、Y軸、Z軸の方向で与えることができます。CGなどの分野では、関節の進行方向をX軸に割り当てるのが主流となっているので、ここでもそれを前提に説明します。
先ほどのベクトル計算では、このX軸の方向を向けることはできていますが、Y軸、Z軸に関しては未解決です。X軸を回転させたのと同じ分だけY軸やZ軸も回転させることで、初めて関節の方向が変わったことになります。

3次元の場合はXYZ全ての軸の方向を決める必要がありますが、XY座標だけを取り扱う2次元の場合は、Z軸に相当する軸の方向は常に手前側に固定されていると考えることができるので、X軸とY軸の方向だけを解決すればOKです。

XYZ軸の回転イメージ(X→X'、Y→Y'、Z→Z')

手法

1ボーンIKにはいくつかの手法がありますが、ここでは三角比を使ってヨー回転量とピッチ回転量を計算する手法を紹介します。

前提知識2:ヨー回転・ピッチ回転について

その前に、ヨー回転、ピッチ回転について簡単に説明しておきます。

  • ヨー回転というのは進行方向に対して垂直上方向を回転軸とする回転です。
    進行方向基準で姿勢の左右方向を規定します。
  • ピッチ回転というのは進行方向に対して水平左方向を回転軸とする回転です。
    進行方向基準で姿勢の上下方向を規定します。

この他にロール回転というのもあって、これは進行方向軸を回転軸とし、進行方向基準で姿勢の捻じれ具合を規定しますが、ここでは一旦無視します。※あとでまた登場します。

ここでは、ヨー回転軸をZ軸、ピッチ回転軸をY軸にとり、ヨー回転角をθz、ピッチ回転角をθyとして説明します。*2

ヨー回転とピッチ回転

ヨー回転軸がZ軸なのでヨー回転ではX軸とY軸だけを回転し、ピッチ回転軸がY軸なのでピッチ回転ではX軸とZ軸だけを回転します。必然的にロール回転軸はX軸となり、ロール回転ではY軸とZ軸だけを回転します。

1ボーンIK手順
  1. IKの目標位置を関節のローカル座標に変換する
  2. ローカル座標から三角比を用いてcosθz, sinθz, cosθy, sinθyを計算する
    • \displaystyle{ \cos{θ_z} = x / \sqrt{x^2 + y^2} }
    • \displaystyle{ \sin{θ_z} = y / \sqrt{x^2 + y^2} }
    • \displaystyle{ \cos{θ_y} = \sqrt{x^2 + y^2} / \sqrt{x^2 + y^2 + z^2} }
    • \displaystyle{ \sin{θ_y} = z / \sqrt{x^2 + y^2 + z^2} }
  3. 関節の姿勢をcosθz, sinθzでヨー軸回転、cosθy, sinθyでピッチ軸回転する *3

ヨー回転(Z軸)+ピッチ回転(Y軸)時の三角比

赤いライン(X軸)に沿っている灰色表示の骨がIK適用前の初期配置です。
そこからIK目標位置(白青のチェッカーマーク中心)の方向に向けてヨー軸(Z軸)回転させた状態が中央の灰色表示の骨となり、更にそこから目標位置の方向にピッチ軸(Y軸)回転させた最終状態が水色表示の骨となります。

三角比の計算は、上の図を参考にしてください。
いきなり三角関数が出てきましたが、cos=底辺 / 斜辺、sin=高さ / 斜辺なので、手順2.のような式になります。
斜辺は三平方の定理 *4 で求めます。

この手法は3次元での1ボーンIKの一例ですが、ピッチ軸回転を無視することで、そのまま2次元でのIKにも使えるのが利点です。

その他の手法もざっくり紹介

1ボーンIKは他にも外積を用いる手法や、クォータニオンを用いる手法があるので、簡単に概要を紹介しておきます。

外積を用いる手法は回転を用いずにXYZ各軸を生成する手法ですが、今回説明したヨー回転+ピッチ回転による手法と実質的に同じ結果が得られます。

クォータニオンを用いる手法は、元の進行方向ベクトルと目標位置までの方向ベクトルから計算できる内積と外積の結果を利用してクォータニオンを生成し、このクォータニオンで姿勢を回転させる手法です。ヨー+ピッチ回転方式とは、進行方向となるX軸の方向は一致しますが、Y軸とZ軸の方向が異なる結果となります。

Up Vector Constraint

ヨー回転とピッチ回転の組み合わせで、目標位置の方向に関節の進行方向を向ける1ボーンIK手法を紹介しましたが、このままではロール回転については全く制御されていない状態です。*5

Up Vector Constraint(アップ・ベクター・コンストレイント=上方ベクトル拘束)は、関節のロール回転軸に対して垂直な任意の軸を上方ベクトルとして設定し、この上方ベクトルが拘束用の目標位置を向くようにロール回転軸の回転量を拘束する手法です。これはロール回転を制御する1ボーンIKだとも言えます。

Z軸に対するUp Vector Constraint(赤い矢印がロール回転、白赤チェッカーの中心が拘束用目標位置)

上方ベクトルはロール回転軸=X軸に垂直ならばどのような方向でも構いませんが、一般的にはY軸やZ軸、あるいはそれらの負方向を設定することが多いと思うので、ここではそれを前提に手順を紹介します。
以下の手順は、1ボーンIKを適用した後の追加処理となります。

Up Vector Constraintの手順
  1. 拘束用の目標位置 *6 を関節のローカル座標に変換する
  2. ローカル座標から三角比を用いてcosX, sinXを計算する
    • \displaystyle{ \cos{X} = y / \sqrt{y^2 + z^2} }
    • \displaystyle{ \sin{X} = z / \sqrt{y^2 + z^2} }
  3. Up Vectorとして選択する軸の種類によってcosX, sinXを変換する
    • Y軸:変更しない *7
    • Z軸:cosX = sinX, sinX = - cosX
    • 各軸の負方向合わせの場合は、上記手順で求めたcosX, sinX両方を符号反転する
  4. cosX, sinXで関節をロール回転する

1ボーンIK本体の方で三角比を用いたヨー回転+ピッチ回転の手法で説明したので、こちらも三角比で計算する手法を紹介しましたが、クォータニオンを用いて計算する方法もあります。

2ボーンIK

概要

2ボーンIKとは、連結された2本の骨の先端位置が与えられた目標位置に到達するように、2本それぞれの骨の姿勢を同時に決定するIK処理です。
人間の手足の構造に適用するのに非常に適しているため、Limb IK(リム=肢)と呼ばれることも多いです。

2ボーンIKはその基本アルゴリズムだけで、2本目の骨の関節の回転軸が1軸に限定されることや、その回転方向を1方向に限定できることがメリットです。 *8

手法

こちらもいくつかの手法が知られていますが、余弦定理を用いる手法が一般的です。骨が増えた分だけ少し複雑になるので理論背景を交えつつ順番に説明します。

理論背景を交えた手順説明

まずは前項で説明した1ボーンIKを用いて第1関節を目標位置の方向に向けます。*9

第1関節の方向を1ボーンIKを用いて目標位置の方向に向ける

目標位置に向けた第1関節の進行方向軸を基準として、第2関節の回転軸を法線とする平面を想定します。この図では手前側に法線が向いた状態となっています。
第1関節も第2関節もこの法線方向を回転軸とした1軸回転を行うため、ここからは2次元での問題として扱います。
この平面上で、第2関節の先端が目標位置に到達するように、2本の関節を回転させて以下のような三角形を形成した状態を想定します。

平面上に2本の骨が構成する三角形を想定した図

辺a、辺bの長さは、それぞれ1本目と2本目の骨の長さに相当します。これは事前に分かっている既知の値です。また、辺cの長さは1本目の関節の位置と目標位置との距離に相当しており、これは2点間の距離を計算することで求まります。このように三角形の3辺の長さが既知なので、余弦定理を用いてcosB, cosCを求めることができます。

  • \displaystyle{ \cos{B} = \bigl(c^2 + a^2 - b^2\bigr) / 2ca }
  • \displaystyle{ \cos{C} = \bigl(a^2 + b^2 - c^2\bigr) / 2ab }

cosが決まれば \displaystyle{ \cos^2{θ} + \sin^2{θ} = 1 } の関係からsinも計算できます。

  • \displaystyle{ \sin{B} = ±\sqrt{1 - \cos^2{B}} }
  • \displaystyle{ \sin{C} = ±\sqrt{1 - \cos^2{C}} }

sinの符号は回転方向の符号と一致するため、ここでの決定は一旦保留して、回転方向がどうなっているのかを確認する必要があります。各関節の回転方向を表した図を以下に示してみました。

各関節の回転方向を示した図

回転方向について考える前に、第2関節の回転角が余弦定理で求めた三角形abcの内角Cではなく、実際には外角のπ-Cとなっている点について考慮しなければなりません。
といっても、ここは以下の公式にしたがってcosCの符号を反転するだけで構いません。sinCの値はそのままです。

  • \displaystyle{ \cos{(π-C)} = -\cos{C} }
  • \displaystyle{ \sin{(π-C)} = \sin{C} }

改めて回転方向について話を戻します。
回転方向が反時計回りの場合は正方向の回転で、時計回りの場合は負方向の回転となります。この図によると、第1関節の回転Bは負方向への回転で、第2関節の回転π-Cは正方向の回転ということになります。
回転方向の正負はsinθの正負だけで表現できるので、cosの符号は変化しません。
回転方向を考慮した最終的なcosとsinの組み合わせは以下のようになります。

  • 第1関節:\displaystyle{ \cos{B} }, \displaystyle{ -\sin{B} }
  • 第2関節:\displaystyle{ -\cos{C} }, \displaystyle{ \sin{C} }

それぞれの関節についてcosとsinが決まったので、これらを使った回転変換で姿勢を作ることができるようになりましたが、2ボーンIKとしてはまだ考慮しなければならないことがあります。

2ボーンIKの条件を満たす三角形は、辺cの反対側にも想定することができるからです。

回転方向が反対となる逆側の三角形

もう1つの三角形はBとCの回転方向を正負逆にしたものなので、既に求めているsin値の符号変換だけで対応可能です。

  • 第1関節:\displaystyle{ \cos{B} }, \displaystyle{ \sin{B} }
  • 第2関節:\displaystyle{ -\cos{C} }, \displaystyle{ -\sin{C} }

2ボーンIKの計算をする場合に、どちらの三角形による結果を選択するのかは、第2関節の回転方向が正負どちらであるかという基準で決定することが一般的です。これらはあらかじめどちらか一方に決めたものを選択することもあれば、両方に回転可能な場合も考慮して、IKを適用する前にどちら側に関節が曲がっていたかを調べて、同じ方向に曲がるような選択をする場合もあります。*10 いずれにせよ、第2関節の回転方向が正なら下の三角形、負なら上の三角形を選択します。余弦定理で求めるcosの結果は一緒なので、単純にsinの符号の組み合わせの選択を行います。

回転方向の設定について

第2関節の回転軸がどの軸になるかや、回転方向が正負どちらになるのかについては、2ボーンIKを適用する対象の構造に依存します。したがって、回転軸や回転方向を規定する設定項目を用意しておくのが良いでしょう。

人体モデルに適用する場合、一般的な作り方だと腕に設定する場合と足に設定する場合では、回転方向は逆になることが多いようです。当てはめてみて逆に曲がってしまったらもう一方の設定にすれば良いだけなので、そんなに難しく考える必要はありません。

2ボーンIKの手順(まとめ)
  1. 1ボーンIKを用いて目標位置の方向に第1関節を向ける
  2. 余弦定理でcosB, cosCを求める(cosCは符号反転する)
  3. sinB, sinCを求める(符号は正とする)
  4. 第2関節の回転方向設定によりsinB, sinCいずれかの符号反転を行う
    • 正回転:-sinB
    • 負回転:-sinC
  5. 得られたcos, sinの組み合わせで、各関節を回転させる

 

次項以降では、2ボーンIKに対するいくつかの基本的な機能拡張について説明します。

Up Vector Constraint

2ボーンIKは、1ボーンIK適用後に第2関節の回転軸を法線とする平面上での三角形を想定して計算するという基本アルゴリズムでした。2次元の場合はこれ以上のIK解は存在しませんが、3次元の場合はロール軸回転が存在するため、無限の解が存在することになります。これらの中から任意の平面上の三角形を選択したい場合は、1ボーンIKの計算後にUp Vector Constraintをかけることで対応します。 *11

2ボーンIKへのUp Vector Constraint適用イメージ

キャラクターモーションの補正目的で2ボーンIKを使う場合は、元のモーション姿勢から1ボーンIKを適用した状態での平面をそのまま使い、Up Vector Constraintをかけないことも多いですが、そこから手足の開き具合の調節が必要な場合などには、この機能に頼らなければなりません。

第2関節の回転角制限

2ボーンIKでは第2関節の曲がる方向を選択することで、回転角の範囲が自動的に0~180°か-180~0°のいずれかに制限されますが、この曲がり具合の範囲を更に制限することができるので、その手法について説明します。

理論背景を交えた手順説明

通常の2ボーンIK処理の工程で、cの長さを目標位置と第1関節の位置との距離として求めますが、ここでも余弦定理を使うことで角Cの角度制限の問題を、辺cの長さの範囲制限の問題に落とし込み、第2関節の回転角制限を実現します。

ここでは実際の回転計算で考慮する三角形の外角(π-C)ではなく、余弦定理で求める対象となる三角形の内角Cの方で考えます。

角Cの最小回転角をθmin、最大回転角をθmaxとします。(0 ≦ θmin ≦ θmax ≦180°)
設定は角度で与えておいたとしても、実際には事前処理でcosθmin, cosθmaxを求めておくのが効率的です。
辺a、辺bの長さと、それらがなす角Cに対するcosθmin, cosθmaxが既知なので、余弦定理で辺cの長さの最小値と最大値が決まります。 *12

  • \displaystyle{ c_{min} = \sqrt{ a^2 + b^2 - 2ab \cos{θ_{min}}} }
  • \displaystyle{ c_{max} = \sqrt{ a^2 + b^2 - 2ab \cos{θ_{max}}} }

実際の距離から求めた辺cの長さを、この\displaystyle{ c_{min} }\displaystyle{ c_{max} }で範囲制限した結果を新たなcとして2ボーンIKを計算することで、第2関節の角度が範囲制限された状態で、対応する第1関節の角度と共に求まります。

第2関節の角度制限適用例(最小角度:30°、最大角度:170°)

最初からcの長さを規定するパラメーター(距離に対する比率など)で範囲制限することもありますが、その方法だとaとbの長さの比率がキャラによって異なる場合に、制限する角度を一定に保つことができないという問題があります。aとbの長さの比率が固定なら\displaystyle{ c_{min} }\displaystyle{ c_{max} }は完全に事前計算可能なので、この方式を選択することにより2ボーンIKの処理負荷が余計に増えることはありません。

回転角制限の副作用

回転制限がかかると目標位置への到達という、IKが達成すべき本来の目的が達成できなくなってしまいます。しかしながら、目標位置への到達が多少阻害されたとしても、挙動の乱れや関節間の回転限界を超えた不自然な姿勢を見せないことの方が重視されるケースも多々あるため、そうしたメリット・デメリットを天秤にかけて、必要な場合には回転角制限をかけることを選択しなければなりません。

回転角制限が必要となる事例

2ボーンIKを適用する事例では、実はそこまで回転角制限が必要になるケースはありません。これは2ボーンIK自体に最初から関節が逆側に曲がらないような回転制限が自動的にかかっているためで、これは2ボーンIKの大きなメリットと言えます。
ただ、目標位置の配置によっては2本の関節が伸びきったり曲がったりするような状態、すなわち第2関節の最大角度が180°まで振り切ったり少し戻ったりするようなギリギリの状態になることがあり、そのような状態では目標位置の僅かな移動量に対し角度の変化量が極端に大きくなることから、その急激な挙動変化が不自然な動きとして目立つようになります。このような急激な角度変化の影響を緩和し目立たなくする目的で、最大角度を180°よりも少し小さい値 *13 に設定することで関節が完全に伸びきらないように設定しておくことがあります。手足を限界まで伸ばさなくても支障がない、あるいは限界まで伸び切って欲しくない場面ではメリットしかないので試してみてください。

2ボーンIKをキャラクターに適用する

エンドエフェクターについて

2ボーンIKを人型キャラクターなどに適用する場合は、腕や足に設定することが多いですが、実際に当てはめてみると腕部だと上腕+前腕、脚部だと腿+脛という割り当てになり、手足や足先の関節が対象から外れてしまうので、1ボーン分足りないと感じるかもしれません。実際のゲーム中で位置や姿勢を合わせたいのは手の指先や足先だったり足の裏面だったりするのに、このままではIK処理が合わせるのは手首、足首の位置になってしまうからです。

2ボーンIKの人型キャラクターへの適用例

IKの位置や姿勢を目標に合わせたい部位のことをエンドエフェクター、あるいは単にエフェクターと呼びます。これは末端効果器という意味で、IKを使って動作させるロボットアームなどの先端に接続されて実際に作業を行う装置を想定していることから、このような呼び方をしています。

先程の人型キャラクターの例に話を戻すと、手先や足先の部位がエフェクターに相当することになりますが、このエフェクター部位はIKの計算対象には含まなくて良いのです。
あらかじめ何らかの手法で求められているエフェクターの姿勢や位置から逆算して、途中の関節の姿勢や位置を計算するのがIKなので、エフェクターの位置を目標位置として渡してしまえば、IK内ではエフェクターそのものの制御をする必要がないからです。*14

エフェクターの目標位置の置き換え

もう少し具体的に説明すると、もしエフェクターの姿勢があらかじめ固定されているのなら、実際にエフェクターが効果を作用させたい位置とエフェクターの関節位置との相対的な位置関係も固定されていることになります。であれば本来の目標位置から、この相対的な位置差分を引いた位置をIKの目標位置にすれば、エフェクターが作用する位置にも自動的に合うことになります

人体で例えれば、手先エフェクターの実際の作用点が指先だった場合は、その時点での指先の位置と手先エフェクターの関節位置との相対的な位置関係を求めておけば良いのです。

エフェクターの作用位置を考慮した目標位置の平行移動

図で説明すると、エフェクターの先端部を本来の目標位置である白のチェッカーマーク位置に合わせたい場合に、エフェクターの位置差分を考慮して得られたピンクのチェッカーマーク位置を2ボーンIKの目標位置として計算することで、エフェクターの先端位置は自動的に白のチェッカーマークに到達するという仕掛けです。

エフェクターの関節位置が目標に到達すると実際の作用位置も自動的に目標に到達する

この手法は多くのIKに応用可能で、IK制御が必要な連鎖数を1つ減らすことが可能となります。*15

2ボーンIK+エフェクター部位の制御イメージ

疑似3ボーンIK

エフェクターの姿勢が固定されたままだと、IKによる姿勢変化が大きくなった時に、親となる第2関節とエフェクターとの間の回転角が極端な状態になることがあります。

エフェクター部位の関節の角度が極端になった例(左手)

エフェクターの固定姿勢を決める際に、目標位置や対象の状態を見てある程度の変化を付けておいたり、目標位置への到達を多少犠牲にしてでも回転制限の範囲内になるようにエフェクターの姿勢を後から変更する方法も考えられますが、どうせならエフェクター部位もIKに計算させてしまいたいと考えるはずです。

エフェクターもIKに含めて解決した場合

人間の手足のケースならば3ボーンに対応したIKを適用することで解決できそうですが、ここでは2ボーンIKを使って疑似的に3ボーン分のIK処理を実現する手法について紹介します。

疑似3ボーンIKの手順
  1. IK適用前の第2関節と第3関節間のローカル回転差分を計算する
  2. 第3関節の関節位置と先端位置の移動差分を計算する
  3. 手順2.で求めた移動差分を目標位置から差し引いた位置を新たな目標位置とする
  4. 第1関節と第2関節に2ボーンIKを適用する
  5. 手順1.で求めたローカル回転差分を用いて、第3関節の姿勢を再構成

  6. 手順5.で求めた第3関節の姿勢と、第3関節の初期姿勢の中間姿勢を計算し、これを第3関節の姿勢とする
    • 中間姿勢のブレンド率は追加パラメーターとして別途与える必要がある
    • 0%だとブレンドターゲット側、100%だと初期姿勢側としてブレンドする
  7. 手順6.のブレンド率が100%以外(未満)の場合は、手順2.~4.を再び実行する *16

 

細かく手順を書きましたが、ざっくり言うと、2ボーンIK計算後に第3関節の姿勢を初期姿勢を使って加工し、目標位置からずれた分を再度2ボーンIKを計算することで解消しています。手順2.~4.は、2ボーンIKで必要となる手順そのものなので、そのまま機能を呼び出せばOKです。

ブレンド率の調整で出る結果の幅のサンプルが以下の画像となりますが、この幅は第3関節の初期姿勢だけでなく目標位置の場所によっても広がったり狭まったりします。

ブレンド率による結果の幅

人型キャラクターに適用する時の様にエフェクター部位を第3関節とする場合には、必要なら姿勢を外部から与えて拘束してもいいのですが、通常は元のモーションで得られるFK姿勢をそのまま使えば十分です。下の適用例は疑似3ボーンIKとUp Vector Constraintのみで、Tポーズの状態から1分くらいで適当にポーズ付けしたものです。

疑似3ボーンIK(+Up Vector Constraint)の人型キャラクターへの適用例
別バージョン

上の説明では回転差分を用いることで元のFK姿勢とのブレンドを行う方式について説明しましたが、回転差分を用いずに第2関節と同じ姿勢をそのままブレンド対象とするやり方もあるので、設定によって使い分けられるようにしておくと便利です。

他にも2ボーンIKの適用後にエフェクター部位の回転制限をかけて目標位置からずれてしまった結果に対して、追加処理で手順2.~4.を適用することで、回転制限もある程度考慮しつつ目標位置に到達するといった簡易方式も考えられます。 *17

特徴

追加で第3関節のブレンド率の設定が必要となることや、2ボーンIKを2回計算することによる処理負荷増の問題はありますが、2ボーンIKの仕組みやメリットを生かしたまま3ボーン分のIK処理が実現できるという点で、導入し易いのではないかと思います。特にエフェクターの先端は目標位置に到達して欲しいけど、姿勢拘束はボチボチで良いという場合に最適です。

また、一般的に四足歩行動物の四肢の制御は人間の手足よりも1ボーン多い関節制御が必要となるため、3ボーン以上に対応したIKアルゴリズム+エフェクター部位の制御を行う必要がありますが、この手法を用いることで2ボーンIKの機能しかなくても四足歩行動物の四肢の制御への対応が可能となります。以下は動作サンプルです。

疑似3ボーンIK+エフェクター制御の動作サンプル(前足→後ろ足→ブレンド率制御の様子)

まとめ

IKの基本的な概念、基本処理となる1ボーンIKと今でも広範囲で使われている2ボーンIKの具体的な処理内容について説明しました。数学の授業で習った時には何に使うのか今ひとつピンと来なかった余弦定理も、ここではしっかり役に立っています。

IKアルゴリズムには他にも様々な手法があり、それぞれ適用できる範囲や得意分野が異なります。また今でも新しいIKアルゴリズムや既存のアルゴリズムの改善の提案が各所で発表されているので、そうした手法についても学ぶきっかけになればと思います。

特に昨今は商用ゲームエンジンに標準機能として様々なIK処理が実装されるようになったので、自分でIK処理を新たに作成する機会は減っているかも知れません。それでも自社エンジンやタイトルにIK処理を組み込む事例はまだまだありますし、仮に既に用意されているIKの機能を使うだけであっても、その仕組みや特徴を理解しているのとそうでないのとでは、同じIK機能を使ってるにも関わらずゲーム上での挙動が大きく変わることがあったりするので、IK処理の知識は無駄になりません。

今回はIK本体のアルゴリズムを中心に解説しましたが、実際のゲーム制作においては、IKに与える目標位置の取得方法や加工の仕方、各種パラメーターの設定にも工夫が必要となり、最終的な挙動に与える影響度としてはむしろそちらの方が重要だったりもします。今後機会があれば、そういった内容にも触れられれば良いなと思っています。

終わりに

セガでは商用ゲームエンジンだけでなく自社開発エンジンによるゲーム開発も行われており、開発メンバーとして一緒に働ける仲間を幅広く募集しています。セガでのゲーム開発に興味がある方は、下記サイトにアクセスしてみてください。

recruit.sega.jp

©SEGA

 

*1:+α

*2:この場合は必然的にX軸がロール回転軸となります。

*3:回転の処理はX軸、Y軸、Z軸それぞれに対して計算する必要があります。1つずつ計算しても良いのですが、通常は行列などを使ってまとめて処理します。

*4:中学3年で習う「底辺の2乗+高さの2乗=斜辺の2乗」です。

*5:これは外積版やクォータニオン版も同様です

*6:1ボーンIK用の目標位置とは別に設定される

*7:手順2.で求めたcosX, sinXの式はY軸合せの場合の式なので

*8:これは他のIKだと非常に面倒な追加処理を要求されます

*9:この図では第2関節も一緒に追従させていますが、計算上は第2関節は放置していて問題ありません。

*10:適用前の角度が180°の場合は方向が決められないので、その場合に正負どちらを優先するかは決めておく必要があります。

*11:この処理自体は1ボーンIKにかかるものなので計算手順も同じです。

*12:余弦定理の式が先程とは違いますが、cについて展開し直しただけです

*13:例えば170~175°程度

*14: IKで決定した最終位置にエフェクター部位を接続する処理も、IK処理全体の枠組みの中には含まれますが、IKアルゴリズムそのものが解決する内容ではないという意味です。

*15:エフェクター部位の情報もIK計算に必要な場合は残す必要があります

*16:100%の場合は、そのままIK処理を終了する

*17:その場合、結果として回転制限を突破する可能性もあります

Powered by はてなブログ