ARマーカーの上にUnityChanを表示させてみた
OpenCVのChARUcoを使うと,簡単にマーカーの位置姿勢を推定することができます.
今回はこれを使って,位置が推定されたマーカーの上にUnityChanを表示させるプログラムを作ってみました.
テクスチャを張り付けたメッシュを画面に表示させるため、自分でOpenGLのシェーダーを書いて、表示させました。ライティングは特に考えていません。単にテクスチャを貼っただけです。
この記事ではOpenCVとOpenGLの連携にかかわることに絞って書いていきます。連携さえできればあとはOpenGLのプログラミングになるので、その辺はlearn openglなどを見ていただければいいんじゃないかなと思います。
なお、Mesh fileの読み込みなどは、Assimpを使用しました。クロスプラットフォームで使用でき、いろいろな形式の3Dモデルを読み込むことができます。
ソースコード
cv::MatをOpenGLのtextureにする
こちらの記事を参考にさせていただきました。
以下のコードでは、cv::Matの画像からOpenGLのテクスチャを作成しています。"texture_data"が作られたやつです。
cv::Mat src_img = src_img_data.clone();
cv::flip(src_img, src_img, 0);
cv::cvtColor(src_img, src_img, cv::COLOR_BGR2RGB);
glGenTextures(1, &texture_data);
glActiveTexture(GL_TEXTURE0);
glBindTexture(GL_TEXTURE_2D, texture_data);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
width = src_img.cols;
height = src_img.rows;
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, src_img.cols, src_img.rows, 0, GL_RGB, GL_UNSIGNED_BYTE, src_img.data);
// Generates MipMaps
glGenerateMipmap(GL_TEXTURE_2D);
glBindTexture(texture_data, 0); // unbind
カメラの内部パラメータを踏まえた投影行列
こちらの記事を参考にさせていただきました。
まず、何らかの方法で使用するカメラの内部パラメータを取得します。ここではcv::calibrateCamera()を使用しました。そのパラメータを以下の変数に格納します。
cv::Mat cameraMat = (cv::Mat_<double>(3,3) << 852.8839555848483, 0, 639.5, 0, 852.215118770151, 359.5, 0, 0, 1);
cv::Mat distCoeffs = (cv::Mat_<double>(1,5) << 0.08005910250868746, -0.3113160415419927, 0, 0, 0.196456176823346);
投影行列は次のようになります。上のQiitaの記事で書かれていた行列をそのままプログラムに落とし込んだ形になります。
glm::vec2 screenSize(848, 480);
const double nearP = 0.1f;
const double farP = 200;
glm::mat4 camProj = glm::mat4(
2*cameraMat.at<double>(0, 0)/screenSize.x, 0.0, 0.0, 0.0,
0.0, 2.0*cameraMat.at<double>(1, 1)/screenSize.y, 0.0, 0.0,
-2*cameraMat.at<double>(0, 2)/screenSize.x + 1, 2.0*cameraMat.at<double>(1, 2)/screenSize.y - 1, -(farP + nearP)/(farP - nearP), -1.0,
0.0, 0.0, -2*farP*nearP/(farP - nearP), 0.0
);
OpenCVでは、カメラのz軸が撮影方向、y軸が下、x軸が撮影方向に対して右向きとなっているので、View行列は次のようになります。
glm::mat4 camView = glm::lookAt(glm::vec3(0.f), glm::vec3(0.f, 0.f, 1.f), glm::vec3(0.f, -1.f, 0.f));
+z方向を見て、-yが上ベクトルとなっています。
なので、頂点シェーダーでは次のような雰囲気のコードになります。
gl_Position = camProj * camView * ModelTransform * vec4(Position, 1.0);
参考文献
OGLdev https://ogldev.org/
UnityChan https://unity-chan.com/