【ゲーム制作】No.25 摩擦ジョイント(物理エンジン)

立方体(Box)

 

今回も物理エンジンの機能追加になります。
物理エンジンの中の拘束オブジェクトの

ひとつである摩擦ジョイントの追加になります。

 

摩擦はソースをみるとひととおり追加されていて、
ずっと実装済みと思っていたのですが
実際動作確認をしてみると、それっぽい動作をしてくれない・・


「最初はスゥーと滑って次第に止まる」の様な摩擦の動作は無く
「滑るか停止するか」の二択になっている・・

ちょっと修正するか・・から始まり
思いのほか手こずり、完了まで1か月以上かかってしまいました。

 

摩擦は確認する形状に立方体(Box)と球と楕円体の3種類にしたため
その分手続きも増えました。

 

①立方体(Box)
Boxは接触点が2つ以上の複数になることが多く
その為か比較的安定していました。
実際、Box個別に対する修正はありませんでした。

 

②球
球では、「摩擦で永遠に滑る」問題が起きました。
球がずっと滑って永遠に止まらないんです。

球は、Boxとは違い接触点は必ず1つになります。
接触した時に起動される接触ジョイントは
速度のみ拘束して角速度は拘束されずフリーのままです。
だからずっと滑り続けるんです。
(球の1点を接触点として速度をゼロに拘束しても
任意の角度に回転は出来ます)

現実世界で接触点が1点というのはあり得ないので
シミュレーションとみれば正しい動作なんでしょう(多分)

 

修正案としては以下2点を出しました。

 

 ①接触点を増やす
  仮として球の接触点を4つまたは3つに増やすという案です。
  行うとちゃんと停止します。
  ただ、例えばBoxが4接触点だったりすると、接触点数は16点になり
  処理が重くなりそうなので、球限定にする必要はありそうです。

 

 ②角速度の拘束を追加する
  球など接触点1の形状に限り、接触ジョイントに角速度の拘束を追加する案です。
  こちらもいい感じで停止してくれます。
  問題点としては誤差修正の為の角速度を出すのが難しい

  (というか正しい式がわからない)
  なので現状「おおよそ1」な式で胡麻化している。

 

これは見た目が比較的リアルだったので

「②角速度の拘束を追加する」を採用しました。

 

修正前:摩擦では止まらずずっと滑り続ける。※後半もうひとつ球が落ちてきますがこれはバグです(笑)

 

修正後:ちゃんと止まるようになりました。

 

③楕円体

楕円体は摩擦以前に挙動が全然安定せずそれを制御するのに苦労しました。

 

楕円体で挙動がおかしい → 摩擦の処理に問題がある → いろいろ修正する → 余計悪くなる → 無限ループ

 

こんな感じです。

 

あるとき、楕円体は摩擦云々の前に、まず不安定な状態がおかしいのでは?と気付き
楕円体のパラメータを見ていくように変更しました。

その結果、楕円体の直径が細すぎて不安定だったようで、
直径を太めにしたところ安定するようになりました。

こういう、一見当たり前に見えることでも、

ひとりでやっていると気付くのが難しいです。

楕円体

 

資料は以下になります。

 

Gauss-Seidel 法の拡張による剛体関節機構の接触/拘束力計算

 

追加したコードは少なく、資料をそのまま実装するだけでした。

 

{ // T軸で以下を実行します。

// TBN軸のインデックスは安定化の為にランダムにソートされています。
// 以下の処理で正しいインデックスを取得しています。
const uint_t    Ti = i;
const uint_t    Bi = org2cur_index[cur2org_index[i] + 1];
const uint_t    Ni = org2cur_index[cur2org_index[i] + 2];

const cvector2_t    TB = initv(next_f, f(Bi));
const float_t    TB_Len = TB.length;
const float_t    N = f(Ni);
const float_t    N_Len = N;
const float_t    friction_limit = N_Len * parm[Ni].static_friction_factor;
if (TB_Len > friction_limit)
{
const cvector2_t    TB_New = friction_limit / TB_Len * TB;
next_f    = TB_New.x;
insert(f, Bi, TB_New.y);
}
}

 

{ // B軸で以下を実行します。

// TBN軸のインデックスは安定化の為にランダムにソートされています。
// 以下の処理で正しいインデックスを取得しています。
const uint_t    Ti = org2cur_index[cur2org_index[i] - 1];
const uint_t    Bi = i;
const uint_t    Ni = org2cur_index[cur2org_index[i] + 1];

const cvector2_t    TB = initv(f(Ti), next_f);
const float_t    TB_Len = TB.length;
const float_t    N = f(Ni);
const float_t    N_Len = N;

const float_t    friction_limit = N_Len * parm[Ni].static_friction_factor;

if (TB_Len > friction_limit)
{
const cvector2_t    TB_New = friction_limit / TB_Len * TB;

insert(f, Ti, TB_New.x);
next_f    = TB_New.y;
}
}

【ゲーム制作】No.24 ヒンジジョイント (物理エンジン)

ヒンジジョイント

ー90度~+90度の角度制限がついています。(わかりずらいですが)

 

今回は物理エンジンの機能追加になります。
いきなり物理エンジンなんで唐突ですよね?
自分も困惑してます。

 

この11月から12月で、2点機能追加出来たので
今回それを載せることにしました。

 

内容は拘束オブジェクトのひとつであるヒンジジョイントの更新になります。
(もう一つは拘束オブジェクトのひとつである摩擦ジョイントです)

 

ヒンジドアのジョイントは以前からずっと作成していて
ドアを開け閉めは出来るのですが、どこまで開くか、どこまで閉じるか
という角度制限(UnityにおけるLimits_MinとLimits_Max)が実装出来ていませんでした。

 

どうすれば角度制限が実装出来るのか、いろいろ考えたのですが
わからないまま時間だけ過ぎていく感じでした。(ここいらが高卒の限界ですね笑)

 

ですが最近幸運にもネットでやり方を示す資料が見つかったので
今回それを元に実装してみました。

 

資料は以下になります。

ながむしメモ」というブログに載っていた
ConstraintsDerivationRigidBody3D.pdf」という資料です。

(ブログで教えてくれた方ありがとうございます!!)

 

この資料は、実装者メインというか、余計な説明は最小限にとどめ、
コード製作者に必要なことだけを羅列している形で
わかりやすく作られており、コストがかからない良い資料でした。

 

実際の制作の方は、変更は1点のみ。
それ以外は資料の内容そのままを適用することで実装出来ました。

 

変更点は、資料の20ページ
「2.4.1 Position constraint」で「Ctrans(s) = x2 + r2 + r2 − x1 − r1」とありますが
正しくは「Ctrans(s) = x2 + r2 − x1 − r1」です。(ボールジョイントで使われている)
関連する式はヒンジジョイントでは無く、ボールジョイントで表記されている方を使う必要がありました。

 

次回も物理エンジンの追加点にする予定です。

【ゲーム制作】No.23 両眼立体視②(Direct3D11)

 

今回のテーマは「両眼立体視」で前回の続きとなります。
Direct3D立体視を行う為のソース作成がメインとなります。

立体視を行う為には、大きく分けて2つのフェーズがあります。

 

(1).マルチビューポート機能により右目用・左目用の複数ウインドウを作成する。
(2).右目用・左目用のView行列と射影行列を作る。

 

以下、それぞれの作成について述べていきます。


(1).マルチビューポート機能により右目用・左目用の複数ウインドウを作成する。

 

これは以前のブログで紹介したサイト「MaverickProjectの147.ステレオグラム」
参照頂けれは良いかと思います。
私もフルコピペで作成出来ました。

 

発生した問題としては、「SV_ViewportArrayIndexを使うとポリゴン欠けが起こる」です。
普通、SV_ViewportArrayIndex=0なら左目用、SV_ViewportArrayIndex=1なら右目用
と表示するウインドウを変えることが出来るのですが、
SV_ViewportArrayIndex=0の場合のみポリゴンの欠けが起こったり
モデル一式がまるまる表示されなくなるといった症状が起きました。
調査しましたが修正方法を見つけることが出来ず、仕方なく複数パスに分けて
描画することで回避することにしました。


(2).右目用・左目用のView行列と射影行列を作る。

これはサンプルサイトが複数見つかりましたので
これから情報を得て作り上げてみました。

 

①MaverickProjectの147.ステレオグラム

おなじみのこちらのサイトです。
このサイトにもサンプルコードがありましたので
動作してみました。

その結果ですが・・
残念ながらサンプルコードのビルドが出来ず
動作の確認は出来ませんでした。
"HalfLambert2_HalfLambert2_VS_Main.h"
"HalfLambert2_HalfLambert2_GS_Main.h"
"HalfLambert2_HalfLambert2_PS_Main.h"
の3本のファイルがアップされていない為です。
ファイルが無いのではコンパイルしようがない・・

まあこのサンプルである立体視部分のコードは、
View行列に適当な値を加えるという形で
汎用性は無く、私が求める情報は無いなという結論です。

 

②月刊誌、CQ出版社<インターフェース>2011年1月号

こちらも以前紹介した雑誌。
この雑誌の「OpenGLを使って立体視の絵を作ろう!」という記事があり
こちらにもサンプルコードがあるので動かしてみました。
このサンプルコードはOpenGLなのでGLUTを使ってコンパイルします。
(ちなみに記事はネット上にはありませんがサンプルコードなら
こちらにアップしてあります。)

そして、その結果ですが・・
サンプルはoff-axis法でView行列は両目平行です。
ソースはシンプルで扱いやすいです。
OpenGLなので私は今回はキャンセルしましたが
使える方ならばこれをメインにしても良いかもしれません。
ただ、表示された立体視のサンプルは、立体度が弱く
効いているのかわかりにくいなぁという印象でした。
また、ソースには誤りがあります。
誤:glTranslated(eye * distance, 0, 0);
正:glTranslated(eye, 0, -distance);

これはサンプルコードを動かしたものです。立体的に見えるでしょうか・・?

 

③GitHubのd3d-stereo-sample
Direct3Dのサンプルということで、こちらも動かしてみました。
ビルドするには、Visual StudioC++/WinRTVisualStudio拡張機能(VSIX)をインストール
必要がありました。あと細かい修正がいくつかありました。

そして、その結果ですが・・
どうやらこのサンプルは3Dステレオ機能を持つディスプレイが必要とのことで
私の環境では有効にはならず立体視を確認することは出来ませんでした。
一応その後、私の立体視サンプルに移植して立体視を確認しましたが
立体になってるかなぁ程度で特に得られる情報はありませんでした。

 

④off-axis法を用いたステレオグラフィックスの奥行き感に関する研究
PDFです。立体視に対する情報をいろいろと調べたんですが、
立体視を目的としたView行列と射影行列の作り方という点では、
この資料が一番だと思いました。

そして、その結果ですが・・
サンプルコードを移植して立体視を確認してみましたが
まずまずの結果に見えました。サンプルはOpenGLを使用しており
Diret3Dに変更する必要があり苦労しました。
(というかDirect3Dへの移植はまだ完成では無くバグが残ってそうな感じです)

 

以下ソースコードです。

// 「off-axis 法を用いたステレオグラフィックスの奥行き感に関する研究」の移植(off-axis法)

HOUZMETHOD_VD    perspective_view_t::get_stereogram_matrix(    rmatrix4x4_t&    m_左目_ビュー行列,
                                                            rmatrix4x4_t&    m_右目_ビュー行列,
                                                            rmatrix4x4_t&    m_左目_射影行列,
                                                            rmatrix4x4_t&    m_右目_射影行列) const
{
    //p_camera->set_position(initv(0,0,-50));        // 参考:カメラの位置
    //const cvector3_t    pos        = initv(0, 0, 0);    // 参考:モデルの位置

    extern int    gui_i_pupillary_distance;    // 視差量(左目と右目の距離) 調節用のパラメータ変数
    const float_t    gui_f_pupillary_distance    = gui_i_pupillary_distance * 0.01f;


    // View Matrix

    const cvector3_t    side    = transpose(get_orthonormal_basis()(0));    // カメラの向き。横方向。X軸。
    const cvector3_t    up        = transpose(get_orthonormal_basis()(1));    // カメラの向き。縦方向。Y軸。
    const cvector3_t    front    = transpose(get_orthonormal_basis()(2));    // カメラの向き。奥方向。Z軸。

    cvector3_t    v_両目_カメラの位置        = get_center_position();
    cvector3_t    v_両目_視点                = front;
    cvector3_t    v_両目_上視点            = initv(0,+1,0,0);    // 変数upを使用しても良い。

     // to_view()はD3DXMatrixLookAtLH()相当
    m_左目_ビュー行列    = to_view( v_両目_カメラの位置, to_orthonormal_basis( front, v_両目_上視点));    // 左目用のビュー行列。カメラの向きに平行。

    m_右目_ビュー行列    = to_view( v_両目_カメラの位置, to_orthonormal_basis( front, v_両目_上視点));    // 右目用のビュー行列。カメラの向きに平行。

    // ビュー行列使用時はtranspose()で転置すること。

    // Projection Matrix

    float_t    f_距離N        = 1.0;        // N:(視点から前方クリッピング面までの距離) 値はPDFの実験の項から借用した。
    float_t    f_距離L        = 50.0;        // L:(視点からスクリーン面(ディスプレイ面)までの距離) 値はPDFの実験の項から借用した。
    float_t    f_距離F        = 150.0;    // F:(視点から後方クリッピング面までの距離) 値はPDFの実験の項から借用した。
    float_t    f_左右眼の区別    = 0.0;    // E:(左右眼の区別(右眼= 1.0, 左眼=-1.0)
    float_t    f_視点間距離    = 6.5 * 0.5 + gui_f_pupillary_distance;    // D:(視点間距離の1/2の値) 値はPDFの実験の項から借用した。
    float_t    f_スクリーンの幅    = 30.4;    // W:(スクリーン(ディスプレイ)の横の大きさ) 値はPDFの実験の項から借用した。
    float_t    f_スクリーンの高さ    = 27.2;    // H:(スクリーン(ディスプレイ)の縦の大きさ) 値はPDFの実験の項から借用した。

    {    // 左目
        f_左右眼の区別    = -1.0;    //(右眼= 1.0, 左眼=-1.0)
        float_t    射影行列P_パラメータ_A    = 2.0 * f_距離L / f_スクリーンの幅;
        float_t    射影行列P_パラメータ_B    = 0;
        float_t    射影行列P_パラメータ_C    = 2.0 * f_距離L / f_スクリーンの高さ;
        float_t    射影行列P_パラメータ_D    = 0;
        float_t    射影行列P_パラメータ_E    = -( f_距離F + f_距離N) / ( f_距離F - f_距離N);
        float_t    射影行列P_パラメータ_F    = -2.0 * f_距離F * f_距離N / ( f_距離F - f_距離N);
        float_t    射影行列P_パラメータ_EDx    = f_距離F / ( f_距離F - f_距離N);
        float_t    射影行列P_パラメータ_FDx    = -f_距離N * f_距離F / ( f_距離F - f_距離N);
        float_t    回転行列R_パラメータ_sinθ    = f_左右眼の区別 * f_視点間距離 / sqrt( pow( f_左右眼の区別 * f_視点間距離, 2.0) + f_距離L * f_距離L);
        float_t    回転行列R_パラメータ_cosθ    = f_距離L / sqrt( pow( f_左右眼の区別 * f_視点間距離, 2.0) + f_距離L * f_距離L);

        // 元のコード
        //rmatrix4x4_t    射影行列P    = initm(    射影行列P_パラメータ_A,    0,                        射影行列P_パラメータ_B,    0,
        //                                        0,                        射影行列P_パラメータ_C,    射影行列P_パラメータ_D,    0,
        //                                        0,                        0,                        射影行列P_パラメータ_E,    射影行列P_パラメータ_F,
        //                                        0,                        0,                        -1,                        0);
        
        // Direct3D用に変更
        rmatrix4x4_t    射影行列P    = initm(    射影行列P_パラメータ_A,    0,                        射影行列P_パラメータ_B,    0,
                                                0,                        射影行列P_パラメータ_C,    射影行列P_パラメータ_D,    0,
                                                0,                        0,                        射影行列P_パラメータ_EDx,    射影行列P_パラメータ_FDx,
                                                0,                        0,                        1,                        0);

        rmatrix4x4_t    回転行列R    = initm(    回転行列R_パラメータ_cosθ,    0,    -回転行列R_パラメータ_sinθ,    0,
                                                0,                            1,    0,                                0,
                                                回転行列R_パラメータ_sinθ,    0,    回転行列R_パラメータ_cosθ,        0,
                                                0,                            0,    0,                                1);

        rmatrix4x4_t    移動行列T    = initm(    1,    0,    0,    f_左右眼の区別 * f_視点間距離,    // 元のコードでは「-f_左右眼の区別」。変更した。
                                                0,    1,    0,    0,
                                                0,    0,    1,    -f_距離L,
                                                0,    0,    0,    1);

        m_左目_射影行列    = mul( 射影行列P, mul( 回転行列R, 移動行列T));

        // 射影行列使用時はtranspose()で転置すること。
    }

    {    // 右目
        f_左右眼の区別    = 1.0;    //(右眼= 1.0, 左眼=-1.0)
        float_t    射影行列P_パラメータ_A    = 2.0 * f_距離L / f_スクリーンの幅;
        float_t    射影行列P_パラメータ_B    = 0;
        float_t    射影行列P_パラメータ_C    = 2.0 * f_距離L / f_スクリーンの高さ;
        float_t    射影行列P_パラメータ_D    = 0;
        float_t    射影行列P_パラメータ_E    = -( f_距離F + f_距離N) / ( f_距離F - f_距離N);
        float_t    射影行列P_パラメータ_F    = -2.0 * f_距離F * f_距離N / ( f_距離F - f_距離N);
        float_t    射影行列P_パラメータ_EDx    = f_距離F / ( f_距離F - f_距離N);
        float_t    射影行列P_パラメータ_FDx    = -f_距離N * f_距離F / ( f_距離F - f_距離N);
        float_t    回転行列R_パラメータ_sinθ    = f_左右眼の区別 * f_視点間距離 / sqrt( pow( f_左右眼の区別 * f_視点間距離, 2.0) + f_距離L * f_距離L);
        float_t    回転行列R_パラメータ_cosθ    = f_距離L / sqrt( pow( f_左右眼の区別 * f_視点間距離, 2.0) + f_距離L * f_距離L);

        // 元のコード
        //rmatrix4x4_t    射影行列P    = initm(    射影行列P_パラメータ_A,    0,                        射影行列P_パラメータ_B,    0,
        //                                        0,                        射影行列P_パラメータ_C,    射影行列P_パラメータ_D,    0,
        //                                        0,                        0,                        射影行列P_パラメータ_E,    射影行列P_パラメータ_F,
        //                                        0,                        0,                        -1,                        0);
        
        // Direct3D用に変更
        rmatrix4x4_t    射影行列P    = initm(    射影行列P_パラメータ_A,    0,                        射影行列P_パラメータ_B,    0,
                                                0,                        射影行列P_パラメータ_C,    射影行列P_パラメータ_D,    0,
                                                0,                        0,                        射影行列P_パラメータ_EDx,    射影行列P_パラメータ_FDx,
                                                0,                        0,                        1,                        0);

        rmatrix4x4_t    回転行列R    = initm(    回転行列R_パラメータ_cosθ,    0,    -回転行列R_パラメータ_sinθ,    0,
                                                0,                            1,    0,                                0,
                                                回転行列R_パラメータ_sinθ,    0,    回転行列R_パラメータ_cosθ,        0,
                                                0,                            0,    0,                                1);

        rmatrix4x4_t    移動行列T    = initm(    1,    0,    0,    f_左右眼の区別 * f_視点間距離,    // 元のコードでは「-f_左右眼の区別」。変更した。
                                                0,    1,    0,    0,
                                                0,    0,    1,    -f_距離L,
                                                0,    0,    0,    1);

        m_右目_射影行列    = mul( 射影行列P, mul( 回転行列R, 移動行列T));

        // 射影行列使用時はtranspose()で転置すること。
    }
}

 

⑤オリジナルコード
いろいろな資料を得て私が作ったオリジナルのコードです。
立体視を目的として、右目用と左目用のView行列と射影行列を作ります。
資料としたサイトは以下の2つです。
3D ベーシック講座 第2回 AG-3DA1での立体映像の記録
液晶シャッタメガネ(時分割方式)を用いた立体視の実現

 

この画像は上のリンク先に貼っているものです。
小さい画像ですが、ちゃんと立体に見えると思います。
この画像を参考にして、球の位置から
球の傾きを求めれば良い訳です。
それで立体視した画像が表示出来ます。

 

以下ソースコードです。

HOUZMETHOD_VD    perspective_view_t::get_stereogram_matrix(    rmatrix4x4_t&    m_左目_ビュー行列,
                                                            rmatrix4x4_t&    m_右目_ビュー行列,
                                                            rmatrix4x4_t&    m_左目_射影行列,
                                                            rmatrix4x4_t&    m_右目_射影行列) const
{
    // 各種パラメータ変数
    extern int    gui_i_pupillary_distance;    // 視差量(左目と右目の距離)
    extern int    gui_i_pupillary_distance2;    // 基準面距離(カメラから基準点までの距離)
    const float_t    gui_f_pupillary_distance    = gui_i_pupillary_distance * 0.01f;
    const float_t    gui_f_pupillary_distance2    = gui_i_pupillary_distance2 * 0.01f;


    // View Matrix

    const cvector3_t    side    = transpose(get_orthonormal_basis()(0));    // カメラの向き。横方向。X軸。
    const cvector3_t    up        = transpose(get_orthonormal_basis()(1));    // カメラの向き。縦方向。Y軸。
    const cvector3_t    front    = transpose(get_orthonormal_basis()(2));    // カメラの向き。奥方向。Z軸。

    cvector3_t    v_両目_カメラの位置        = get_center_position();
    cvector3_t    v_左目_位置        = get_center_position() + side * gui_f_pupillary_distance;
    cvector3_t    v_右目_位置        = get_center_position() + -side * gui_f_pupillary_distance;
    cvector3_t    v_両目_遠視点    = get_center_position() + front * gui_f_pupillary_distance2;
    cvector3_t    v_左目_視点        = normalize( v_両目_遠視点 - v_左目_位置);
    cvector3_t    v_右目_視点        = normalize( v_両目_遠視点 - v_右目_位置);
    cvector3_t    v_両目_上視点    = initv(0,+1,0,0);    // 変数upを使用しても良い。

    // to_view()はD3DXMatrixLookAtLH()相当
    const rmatrix4x4_t    m_左目_新ビュー行列    = to_view( v_左目_位置, to_orthonormal_basis( v_左目_視点, v_両目_上視点));    // 左目用のビュー行列。遠視点へ視点。
    const rmatrix4x4_t    m_右目_新ビュー行列    = to_view( v_右目_位置, to_orthonormal_basis( v_右目_視点, v_両目_上視点));    // 右目用のビュー行列。遠視点へ視点。

    m_左目_ビュー行列    = m_左目_新ビュー行列;
    m_右目_ビュー行列    = m_右目_新ビュー行列;

    // ビュー行列使用時はtranspose()で転置すること。


    // Projection Matrix

    // pers_proj_LH()の実装
    //const element_type0 cot            = rcp(tan( fov_y * 0.5f));
    //const element_type0 z_factor    = far_z / ( far_z - near_z);
    //return initm(    cot / aspect,    0,        0,            0,
    //                0,                cot,    0,            0,
    //                0,                0,        z_factor,    -near_z * z_factor,
    //                0,                0,        +1,            0);

    m_左目_射影行列    = pers_proj_LH(
                    get_fov_y(),                    
                    2.0 / 3.0,    //get_aspect(),ビューポートが横幅の半分のサイズになるのでアスペクト比を変更する
                    get_near_range(),
                    get_far_range());

    m_右目_射影行列    =     m_左目_射影行列;

    // 射影行列使用時はtranspose()で転置すること。
}

 

今回の目標について

前回目標として以下の3点を挙げていました。

 

①「3DS並の立体感を実現したい」
⓶「ビュー射影行列を得る関数を作りたい」
③「飛び出す立体視を実現したい」

 

今回で①と②は実現出来たと思いますが
③はちょっと実現出来ませんでした。
「飛び出す立体視」という技術が
殆ど見当たらないんですよね。。
何か情報があったら教えて欲しいです。
今回は以上です。

【ゲーム制作】No.22 両眼立体視①(Direct3D11)

tiny

 

ティーポット

 

ボックスx3

 

今回は立体視表示を行います。マルチビューポートの複数ウインドウによって
右目用モニタと右目用モニタを表示し立体感を持つ画像を作ります。

 

参考にしたサイトは、

 

1、MaverickProjectの147.ステレオグラム

毎回参考にさせて頂いているサイトです。
今回立体視をやろうとした理由も、
このページがあったのを発見したからでした。
いろいろと探しましたが
Direct3D立体視」を挙げているサイトは
ここぐらいでとても参考になりました。

 

2、月刊誌、CQ出版社<インターフェース>2011年1月号

13年以上前の技術雑誌ですが、記事として「OpenGLを使って立体視の絵を作ろう!」とありこれを参考にしました。
Direct3DOpenGL立体視は殆ど無く、結局買うことになってしまいました。
送料込みで¥900かかりました。
ちなみにAmazonだと¥30,080かかるそうです。ちょっと高すぎやしませんか?

転売屋ってやつですか?

 

次に作成の方ですが、
今回は目標をあげてみました。

①「3DS並の立体感を実現したい」
⓶「ビュー射影行列を得る関数を作りたい」
③「飛び出す立体視を実現したい」

 

実装の方は書くのに時間がかかるので

次回にさせてください。(9月には出したい・・)

 

【DirectXTK】SpriteFont/SpriteBatchでShader Linkage error

※2024/7/28追記あり

 

DirectX11による分割画面を実装する為に、マルチビューポート+ジオメトリシェーダの

コードを実装したところ、アプリ起動後に以下の様なデバッグメッセージが出て

ジオメトリシェーダが起動しなくなってしまいました。

 

D3D11 ERROR: ID3D11DeviceContext::DrawIndexed: Vertex Shader - Geometry Shader linkage error: Signatures between stages are incompatible. Semantic 'SV_POSITION' is defined for mismatched hardware registers between the output stage and input stage. [ EXECUTION ERROR #343: DEVICE_SHADER_LINKAGE_REGISTERINDEX]

 

D3D11 ERROR: ID3D11DeviceContext::DrawIndexed: Vertex Shader - Geometry Shader linkage error: Signatures between stages are incompatible. Semantic 'TEXCOORD' of the input stage has a hardware register component mask that is not a subset of the output of the previous stage. [ EXECUTION ERROR #345: DEVICE_SHADER_LINKAGE_REGISTERMASK]

 

D3D11 ERROR: ID3D11DeviceContext::DrawIndexed: Geometry Shader - Pixel Shader linkage error: Signatures between stages are incompatible. The input stage requires Semantic/Index (COLOR,0) as input, but it is not provided by the output stage. [ EXECUTION ERROR #342: DEVICE_SHADER_LINKAGE_SEMANTICNAME_NOT_FOUND]

 

実装したのがスカイボックスのシェーダでしたので、そこを中心に調べたのですが

間違いのような箇所は見つかりません。というかそれ以前にメッセージの内容が意味不明です。

その後調査を続けたところ、問題が発生している箇所はスカイボックスでは無く

DirectXTKのSpriteFont/SpriteBatch描画だとわかりました。

検索をかけると、他の方も多くの人が同じような問題に対応していたようです。

私もそこから修正方法を得ることが出来ました。

 

修正の為に、以下の1行を追加してください。私はこれで修正出来ました。

 

device_context->GSSetShader(NULL, NULL, 0);  // この行を追加する。
m_p_SpriteBatch->Begin();
m_p_SpriteFont->DrawString(m_p_SpriteBatch.get(), L"Hello, world!", DirectX::XMFLOAT2( 0, 0));
m_p_SpriteBatch->End();

 

実際のところ、これが真に正しい方法では無いのかもしれません。

SpriteBatchの「State management」の項にあるように

描画の際には、ステートの保存と復元を私たちがしなくてはならないようです。

 

~2024/7/28追記~

よくよく読んでみたら、上記のサイトにこの問題と処置が載っていました。

これが正解のようですね。

Be sure that if you set any of the following shaders prior to using SpriteBatch that you clear them: Geometry Shader, Hull Shader, Domain Shader, Compute Shader.

 

ちなみに5年前のimguiのバグでもまったく同じ修正をしていました。

不思議な縁?・・

【ゲーム制作】No.21 DirectXTK_SpriteFont③(Direct3D11)

 

今回も前回に引き続きDirectXTK::SpriteFontとDirectXTK::SpriteBatchによる
テキストの描画処理になります。

 

主に今回は、長文によるページ表示とメッセージボックスによる選択回答を実装しています。

 

今回もブログのアップが遅くなり申し訳ありません。
実は、テキスト処理の実装にあたり、4回ぐらい作り直ししています。
単純なswitch文による実装、クラスによる静的な実装、クラスによる動的な実装、その拡張・・など。

 

結局今は、疑似コードによってスクリプト言語を組み立て実行する形になりました。

 

このせいで遅くなってしまいました・・と言えれば良いんですがそうでは無くて、
単純にC++のstd::stringの処理にやる気が出ませんでした。触っていて面白くはあるんですけど・・

 

今回の実装において、伝える点は特にありません。std::stringで作っていくだけです。

 

発生した問題としては、SpriteFont::MeasureDrawBounds()にエスケープシーケンスの改ページ(\f)を
渡すと例外になるというもの。この関数は\rと\nしか実装されていない為です。
\rをコピペして\fの処理を追加しても良かったのですが、\rを\fの代わりに使う方法にしました。

 

今回でDirectXTK_SpriteFontは終わりです。
次回はマルチモニタ云々をやる予定です。

【PCゲーム】No.1 Ultima IXで日本語表示(Windows11)

 

※6/16 更新あり

今回は、ゲーム制作では無く、PCゲーム関連のブログになります。
ゲーム関連としては記念すべき1回目になりますね。

 

ブログは、今後もゲーム制作をメインに続ていきますが
意味のある情報が得たら、別カテゴリとして出して行こうと考えています。

 

・今回はPCゲームの「Ultima IX: Ascension」(ウルティマ 9 アセンション)の日本語化に
成功したので、その方法になります。

 

・私は、「Ultima IX: Ascension」の日本語版を既に持っており
PCがWindows7だった頃、これで遊んでいました。(クリアまでは出来ませんでした)

・最近になって引き続きこのゲームをやってみたいと思い、
再インストールしてみましたがゲームは起動出来ませんでした。
(実際にはインストール出来るし、起動も出来るし、日本語音声も出るが
文字化けになる)

OSがWindows10やWindows11に変わった為、それが原因だと思います。

 

・その何もない状態から、ゲームの情報を集めまくって
日本語版で起動できるまでこぎつけました。

 

・でも、翌々調べてみると、「ウルティマ9 日本語化」でGoogle検索をかけると
普通にヒットしました。(ブリッツという方です。素晴らしい!)
私はキーワードとして「Ultima 9」や「Ultima IX」としか使わなかった為
ヒットしなかっただけらしい・・(哀)

 

・この方法ではVB.NETVisual Basic)のビルドが必要になります。
私はVisualStudio2019があった為これで出来ましたが、
無い方はビルド環境構築も必要になります。
フリーなソフトですがこの程度の為に構築は正直痛い・・
代替案はないものか・・

 

以下、日本語化の手順になります。

 

1.(持っていないなら)PCゲームの「Ultima IX: Ascension」を購入します。

 

当たり前ですがソフトが無いなら手に入れておきます。ソフトは何でも良いです。「Ultima IX: Ascension」の日本語版CDを買っても良いですし
エレクトロニック・アーツ(EA)から英語版のダウンロード版を買っても良いです。
個人的にはEA版から買う方をお勧めします。
価格も¥500と安いですし、CDから改良されており、FPSも滑らかで安定しています。
※私が買った時は¥100だった気がしますが・・値上げ?

2.購入したら正しく英語版または日本語版(文字化け)で起動することを確認します。

 

他の方の調査では、「UltimaIXが起動するのはWindos7までで
Windows10や11では起動しない。SafeDiscというカードプロテクトが理由。」
とあるんですが、私の環境では普通にインストール出来るし起動も出来ます。

↓に訂正追加しました。

ただ日本語が文字化けになってしまう感じでした。何か違いがあるんですかね・・?

 

※6/16 更新

Ultima IX: Ascension」の日本語版CDでのインストールを確認したところ

処置を加えないと進まないケースがあったので追記致します。

~「Ultima IX: Ascension」の日本語版CDでのインストール~

①インストールCDの「AUTORUN.EXE」ファイルではインストールに失敗します。

代わりに「SETUP.EXE」ファイルで実行してください。

グラフィックデバイスは「Glide」を選択してください。

②インストールが終わったらnGlideという3Dfx Voodoo Glide ラッパーを

インストールします。

Ultima IX用パッチ「u9patch-1.19f」をインストールします。

 

以上でUltima IXが起動出来るはずです。

上手くいかない場合、EAのダウンロード版を使ってみてください。

ダウンロード版はこういった処置が不要で、英語版が起動出来ると思います。

 

3.「Ultima Patcher」をダウンロードします。

 

Ultima I~IXのパッチや有用なファイルをダウンロード出来るサイトです。
ここのUltima IXの欄から各国の言語パックをダウンロードし適用します。


①以下のサイトに移動します。

 

②中央にある「<> Code」ボタンをクリックし、
「Download ZIP」をクリックします。
ZIPファイルをダウンロードします。

 

③任意の場所にZIPファイルを展開します。

 

④フォルダ内に「UltimaPatcher.sln」ファイルがあるのでダブルクリックします。
VisualStudioがインストールされていれば、
そのまま起動しプロジェクトが読み込まれます。
互換性のダイアログが表示されたら、全てOKで進めてかまいません。

 

⑤メニュー「ビルド」-「ソリューションのビルド」をクリックします。
ビルドが開始されます。
ビルドが正常完了することを確認します。

 

4.「Ultima Patcher」を起動させます。

 

①ビルドが正常完了すると「・・任意のファイル\UltimaPatcher-master\UltimaPatcher\bin\Release」以下に
「UltimaPatcher.exe」ファイルが出来ているので、
これをクリックし起動させます。

 

②「Ultima 9」ボタンをクリックします。

 

Ultima 9がインストールされているフォルダを選択します。

 

④「Language」タブの「Jp」を選択し「Download」ボタンをクリックします。
音声データのダウンロードが始まります。

 

⑤ダウンロードが終わったらText:「Installed」ボタンと
(押せるなら)Speech:「Installed」ボタンをクリックします。

 

⑥最終的に「Current Text:」表示と「Current Speech:」表示が
共に「Jp」と表示されていれば設定完了です。

 

ツールのこのページには他にもパッチ適用や画像改善などが出来るようですので
合わせて入れていっても良いと思います。

 

5.ゲームを起動します。

 

①ゲームのフォルダに「u9.jp.exe」ファイルが出来ているはずです。
このファイルで起動すると日本語状態で表示されます。
お疲れ様でした。
(クリックするのはu9.exeではありません。注意!)

 

日本語が無く遊べないゲームは他にも沢山あります。
例えば「Wizardry8」や「The Elder Scrolls II: Daggerfall」などです。
これらもいつか遊べるようになると良いですね!