【ゲーム制作】No.18 ビルボード(Direct3D11)

 

今回は ビルボードになります。
ビルボードとは何なのか、まったく知識が無い状態で始めたので
このテストもゼロからの構築となりました。
ビルボードは比較的Web上に多く情報があった為参考になりました。

今回参考になったサイトは以下です。

①「指定の形状が視線方向を向くようにしたい (スクリプト)
Shade3Dというプラットフォームでの実装ですが、
自分が最も望んでいる内容でした。途中で切り上げてしまった為
ちゃんと動作は確認していません。いずれしたいと思っています。

 

// ビルボードの表示テスト
HOUZMETHOD_VD text_out_effect_t::billboard_main_test( const ID3D11DeviceContextPtr&    device_context)
{
    try
    {
        // テストの為にビルボード化+回転されたボックスを表示させます。


        // 回転させる為の角度の変数です。

        static float_t    s_angle    = -1.0;    // 回転用の角度
        s_angle    = s_angle > 360 ? 0 : s_angle + 1;    // 0 ~ 360


        // カメラの位置を取得します。
        // これはGPUのエフェクト変数として別途用意しており、それを流用しています。

        cvector4_t    view_position_W    = zero();
        if( m_h_view_position_W)
            m_h_view_position_W->GetFloatVector( (float32_t*)view_position_W.get_block_ptr(0));    // GPU制限の為、GPUのfloat精度をfloat32固定にします。


        // カメラの位置を示すベクトルを求めます。
        // 「カメラの位置 - ボックスの位置」で求めることが出来ます。

        const cvector4_t    v_object_center_pos_inW    = initv( 0, 5, 0, 0);    // ボックスの位置
        const cvector3_t    v_eye_dir    = normalize( view_position_W - v_object_center_pos_inW);


        // カメラの位置を示すベクトルが上を向きすぎて
        // 上ベクトルと近づくと、ベクトルの誤差が増え映像が破綻します。
        // そうならないように処置を加えます。(ただし完璧ではありません。ちょっとマシになる程度・・)
        cvector3_t        v_up    = initv( 0, 1, 0);
        const float_t    dot_v    = abs( dot( v_eye_dir, v_up));
        if( dot_v >= 0.98)
            v_up    = initv( 0, 0, 1);


        // カメラの位置を示すベクトルから正規直交行列を作ります。
        // これは、X軸はY軸とZ軸の外積、Y軸はUP方向、Z軸はカメラの位置を示すベクトル(v_eye_dir)の
        // 回転行列になります。
        // ボックスにこの行列を掛けると、ボックスの正面がカメラの位置を示すベクトル(v_eye_dir)になるよう
        // 回転されます。
        const rmatrix4x4_t    m_diff    = to_orthonormal_basis( v_eye_dir,  v_up);


        // ワールド行列を作ります。
        rmatrix4x4_t    world    = identity();

        world    = scaling< 4 >(cvector3_t(initv( 1.0, 1.0, 0.1)) );    // ボックスの半径

        // X軸で回転するケースです。(可能な限り表示しています。すぐに破綻してしまいます。。)
        //world    = transpose( mul( world, transpose( rmatrix4x4_t( rotate_x( to_radian(s_angle)) ) ) ) );
        //world    = mul( transpose( m_diff ), world);

        // Y軸で回転するケースです。(可能な限り表示しています。すぐに破綻してしまいます。。)
        //world    = transpose( mul( world, transpose( rmatrix4x4_t( rotate_y( to_radian(s_angle)) ) ) ) );
        //world    = mul( transpose( m_diff ), world);

        // Z軸で回転するケースです。これは正常に表示できます。これがデフォルトになります。
        world    = mul( transpose( m_diff ), world);
        world    = mul( world, rotate_z( to_radian(s_angle)) );

        // ボックスの位置を適用します。
        cvector3_t    v_translation    = initv(0,+5,0);    // 上のボックスの位置と同じ値であること。
        rmatrix4x4_t    translation    = translate< 3 >( v_translation);
        world    = mul( translation, world );
        

        // ボックスを表示します。
        // 今回のテストでは独自のライブラリを使って表示していますが、
        // 普通に表示させて問題はありません。

        // 視錐台の頂点データをアップする為にバーテックスバッファをマップ(Open)します。
        get_D3D11_geometric_shape_effect_ptr()->map_vb_shape_orders(device_context);    // geometric_shape_effect_tオブジェクトを静的操作する拡張。(2024/2/25)

        // ボックスを表示させる関数。引数は左から、形状の種類(球やボックスなどを選択)、ワールド行列、色、ワイヤーフレームの有無になります。
        get_D3D11_geometric_shape_effect_ptr()->push_draw_shape_order( shape_order_type_box, world, color_t(1,0,0,1), false);

        // バーテックスバッファをアンマップ(Close)します。
        get_D3D11_geometric_shape_effect_ptr()->unmap_vb_shape_orders(device_context);    // geometric_shape_effect_tオブジェクトを静的操作する拡張。(2024/2/25)

    }
    catch( const general_exception&)
    {
        throw;
    }
}

 

// 以降はto_orthonormal_basis()のコードです。

// 見づらいですので参考程度でお願い致します。

template< uint_t row, typename traits_type, typename storage_type >
HOUZ_FORCE_INLINE packed_matrix< 4, 4, traits_type, matrix_row_major, matrix_storage_value > HOUZCALL    to_orthonormal_basis    (    /*[in]*/const packed_vector< row, traits_type, matrix_col_major, storage_type >&    front)
{
    typedef    packed_matrix< 4, 4, traits_type, matrix_row_major, matrix_storage_value >    matrix_type;
    typedef    matrix_type::rvector_type    rvector_type;
    typedef    matrix_type::element_type    element_type;

    rvector_type    tangent, binormal, normal;

    normal    = normalize( transpose(front));

    const rvector_type    abs_normal    = abs( normal);
    const element_type    normal_min    = min( abs_normal);

    if( abs_normal(0) == normal_min)
        binormal    = initv(1,0,0);
    else
    if( abs_normal(1) == normal_min)
        binormal    = initv(0,1,0);
    else
        binormal    = initv(0,0,1);
    tangent        = normalize( cross( binormal, normal));
    binormal    = normalize( cross( normal, tangent));
    assert( abs( dot( tangent, binormal)) < 0.1f);
    assert( abs( dot( binormal, normal)) < 0.1f);
    assert( abs( dot( normal, tangent)) < 0.1f);

    return initv( tangent, binormal, normal, last(1));
}

template< uint_t row, typename traits_type, typename storage_type0, typename storage_type1 >
HOUZ_FORCE_INLINE packed_matrix< 4, 4, traits_type, matrix_row_major, matrix_storage_value > HOUZCALL    to_orthonormal_basis    (    /*[in]*/const packed_vector< row, traits_type, matrix_col_major, storage_type0 >&    front,
                                                                                                                                    /*[in]*/const packed_vector< row, traits_type, matrix_col_major, storage_type1 >&    up)
{
    typedef    packed_matrix< 4, 4, traits_type, matrix_row_major, matrix_storage_value >    matrix_type;
    typedef    matrix_type::rvector_type    rvector_type;
    typedef    matrix_type::element_type    element_type;

    static const rvector_type zero_        = zero();
    static const element_type threshold    = 0.7f; // 0.7 ≒ cos(π/4)

    const rvector_type binormal    = normalize( transpose(up));
    const rvector_type normal    = normalize( transpose(front));

    if( abs( dot( binormal, normal)) < threshold)
    {
        const rvector_type    tangent    = normalize( cross( binormal, normal));
        return initv( tangent, cross( normal, tangent), normal, last(1));
    }
    else
        return to_orthonormal_basis( front);
}