Boost.QVMは行列計算のためのライブラリである. QVMはQuaternion, Vector, Matrixの略である. この記事では同ライブラリの使用方法を解説する入門記事である.
本記事では最初に主要なヘッダの概要を述べた上で, 具体的な使用例を例示する形で説明する, 所謂逆引きの形式を採る. これは, 利用したい機能がどのヘッダに定義されているかをファイル名から察する上で, 数学やコンピュータグラフィックスの知識, 文脈に対する理解が必要であり, 初心者に優しくないためである.
ヘッダのインクルードや関数の定義を省いた疑似コードがあるので注意.
主要なヘッダ
boost/qvm/
は省略しているので注意.
ヘッダ | 概要 |
---|---|
vec | ベクトル型 |
vec_access | ベクトルの要素へのアクセス |
vector_operations | ベクトル演算 |
mat | 行列型の定義 |
mat_access | 行列の要素へのアクセス |
matrix_operations | 行列演算 |
quat | クォータニオンの定義 |
quat_access | クォータニオンの要素へのアクセス |
quaternion_operations | クォータニオン演算 |
swizzling | ベクトルや行列の拡張 |
Vector-to-Matrix View Proxies | ベクトルからの行列の作成 |
Matrix-to-Matrix View Proxies | 行列からの行列の作成 |
Matrix-to-Vector View Proxies | 行列からのベクトルの生成 |
Matrix-Vector Operations | ベクトルと行列との演算 |
Quaternion-Vector Operations | クォータニオンとベクトルとの演算 |
各種算術演算, ベクトルのクロス積, ドット積等はoperations
と名前の付くヘッダに定義されている.
ベクトルの定義と要素アクセス
#include <boost/qvm/vec.hpp>
#include <boost/qvm/vec_access.hpp>
#include <iostream>
int main()
{
boost::qvm::vec<float, 2> v;
boost::qvm::X(v) = 1.f;
boost::qvm::Y(v) = 2.f;
std::cout << "("
<< boost::qvm::X(v) << ", "
<< boost::qvm::Y(v) << ")" << std::endl;
}
クラステンプレートvec
はvec.hpp
に, 要素にアクセスする関数X
やY
はvec_access.hpp
に定義されている.
X
, Y
はA0
, A1
で各々代用する事が出来る. これらの関数はA9
まで用意されている. また, n次元のベクトルにアクセスしたり, コンパイル時定数によってアクセスする次元を決定したい場合は関数テンプレートA
の第1引数に対して次元を表す値を渡す.
これらの関数はオーバーロードされており, 基本的に引数がconst
修飾されていれば戻り値は値型に, されていなければ参照型となる.
ベクトルの合成, スカラー積
#include <boost/qvm/vec.hpp>
#include <boost/qvm/vec_access.hpp>
#include <boost/qvm/vec_operations.hpp>
#include <iostream>
int main()
{
using vec = boost::qvm::vec<float, 2>;
auto out = [](vec const& v)
{
std::cout << "("
<< boost::qvm::X(v) << ", "
<< boost::qvm::Y(v) << ")" << std::endl;
};
vec a, b;
boost::qvm::X(a) = 1;
boost::qvm::Y(a) = 2;
boost::qvm::X(b) = 3;
boost::qvm::Y(b) = 4;
out(a);
out(b);
out(-a);
out(a + b);
out(a - b);
out(a * 2);
out(2 * a);
out(a / 2);
}
各種ベクトル演算はvec_operations.hpp
に定義されている. この他にもドット積, クロス積, 標準化, 大きさの計算等が定義されている. 算術演算子だけでなく, 算術代入演算子も勿論定義されている.
行列
ベクトルと同様に, mat.hpp
, mat_access.hpp
, mat_operations.hpp
を使用する. ベクトルとは異なり, 要素アクセスを行う関数はA00
, A01
, ... A09
, A10
, A11
, ... A99
となる. 各値が行と列の次数に対応する. 関数テンプレートA
を使用する場合, 次数の指定を第1テンプレート引数と第2テンプレート引数で行う.
#include <boost/qvm/mat.hpp>
#include <boost/qvm/mat_access.hpp>
#include <boost/qvm/mat_operations.hpp>
#include <iostream>
int main()
{
using mat = boost::qvm::mat<float, 2, 2>;
auto out = [](mat const& m)
{
std::cout << "(("
<< boost::qvm::A00(m) << ", "
<< boost::qvm::A01(m) << "), ("
<< boost::qvm::A10(m) << ", "
<< boost::qvm::A11(m) << "))" << std::endl;
};
mat a, b;
boost::qvm::A00(a) = 1;
boost::qvm::A01(a) = 2;
boost::qvm::A10(a) = 3;
boost::qvm::A11(a) = 4;
out(a);
b = 2 * a + a * 3 - a / 4;
out(b);
b += a;
out(b);
b -= a;
out(b);
b *= 2;
out(b);
b /= 3;
out(b);
}
行列演算によるベクトルの移動
#include <boost/qvm/map_vec_mat.hpp>
#include <boost/qvm/swizzle.hpp>
#include <boost/qvm/vec.hpp>
#include <boost/qvm/vec_access.hpp>
#include <boost/qvm/vec_mat_operations.hpp>
int main()
{
using vec = boost::qvm::vec<float, 2>;
vec a, b;
boost::qvm::X(a) = 1;
boost::qvm::Y(a) = 2;
boost::qvm::X(b) = 3;
boost::qvm::Y(b) = 4;
b = boost::qvm::translation_mat(a) * boost::qvm::XY1(b);
}
移動を行う行列の生成にはtranslation_mat
を使用する. このようなベクトルから行列へのマッピングを行う関数テンプレートはmap_vec_mat.hpp
に定義されている. 同様に各種マッピングを行う関数テンプレートはmap_xxx.hpp
やmap_xxx_yyy.hpp
に定義されている.
移動行列はn次元のベクトルに対して, n+1行n+1列の行列となるので, 実際に行列と演算するためには行列側を拡張する必要がある. ここでは2次元から3次元への拡張となるのでXY1
を使用する. この関数テンプレートはswizzle.hpp
に定義されている. 3次元から4次元への拡張の場合XYZ1
を使用する. このヘッダには他にもXY
, X0
, YX
, XX
等, 次元の縮小や, 要素の位置の変更, 0や1を代入したベクトルを得るための関数テンプレートが定義されている.
最終的にベクトルと行列との演算を行うにはvec_mat_operations.hpp
を使用する.
ベクトルのスケール
#include <boost/qvm/map_vec_mat.hpp>
#include <boost/qvm/vec.hpp>
#include <boost/qvm/vec_access.hpp>
#include <boost/qvm/vec_mat_operations.hpp>
int main()
{
using vec = boost::qvm::vec<float, 2>;
vec a, b;
boost::qvm::X(a) = 1;
boost::qvm::Y(a) = 2;
boost::qvm::X(b) = 3;
boost::qvm::Y(b) = 4;
a = boost::qvm::diag_mat(a) * b;
}
ベクトルの各要素に対する乗算を行うには対角行列を使用する. 対角行列の生成にはdiag_mat
を使用する. 移動とは異なり, n次元のベクトルに対してn行n列となるため, swizzleは不要. 移動とスケールの両方を行う行列を作成する場合は事前にスケールを拡張してからdiag_mat
を使用することになるだろう.
2次元のベクトルの回転
#include <boost/qvm/mat_operations.hpp>
#include <boost/qvm/vec.hpp>
#include <boost/qvm/vec_access.hpp>
#include <boost/qvm/vec_mat_operations.hpp>
int main()
{
boost::qvm::vec<float, 2> a;
boost::qvm::X(a) = 1;
boost::qvm::Y(a) = 2;
a = boost::qvm::rotz_mat<2>(1) * a;
}
クォータニオンによる3次元の回転
qvm/quat_operations.hpp
をインクルードし, クォータニオンの生成, 操作, 変換を行う.
#include <boost/math/constants/constants.hpp>
#include <boost/qvm/mat.hpp>
#include <boost/qvm/mat_operations.hpp>
#include <boost/qvm/quat.hpp>
#include <boost/qvm/quat_operations.hpp>
#include <boost/qvm/to_string.hpp>
#include <boost/qvm/vec.hpp>
#include <boost/qvm/vec_access.hpp>
#include <boost/qvm/vec_mat_operations.hpp>
#include <boost/qvm/vec_operations.hpp>
#include <boost/range/algorithm/for_each.hpp>
#include <boost/range/irange.hpp>
#include <iostream>
int main()
{
using vector_3 = boost::qvm::vec<float, 3>;
using quaternion = boost::qvm::quat<float>;
using matrix_3_x_3 = boost::qvm::mat<float, 3, 3>;
using matrix_4_x_4 = boost::qvm::mat<float, 4, 4>;
boost::for_each(boost::irange(0,25), [](int const i) {
using boost::qvm::convert_to;
auto const two_pi = boost::math::constants::two_pi<float>();
vector_3 axis;
X(axis) = 3;
Y(axis) = 4;
Z(axis) = 5;
auto const rot_quat = boost::qvm::rot_quat(axis, two_pi * i / 24);
auto const rot_mat_3_x_3 = convert_to<matrix_3_x_3>(rot_quat);
auto const rot_mat_4_x_4 = convert_to<matrix_4_x_4 >(rot_quat);
vector_3 position;
X(position) = 1;
Y(position) = 2;
Z(position) = 3;
std::cout
<< to_string(rot_quat) << '\n'
<< to_string(rot_mat_3_x_3) << '\n'
<< to_string(rot_mat_4_x_4) << '\n'
<< to_string(rot_mat_3_x_3 * position) << '\n'
<< std::endl;
} );
}
rot_quat
を使用して生成したクォータニオンをconvert_to
を用いて行列に変換し, operator *
によって座標計算を行う. vec_mat_operations.hpp
, quat_operations.hpp
等のインクルード漏れに注意.
なんと, 2017-12-03現在, クォータニオンから行列への変換を行う関数テンプレートはドキュメントのバグにより公式のリファレンスに記載されていない.
その他のクォータニオンに対する操作はリファレンスを参照されたし.
その他
access, operations, swizzle, mapから概ねの要素が成り立っている事を理解すれば, あとはリファレンスを読めばBoost.QVMはほぼ使いこなせる.