DirectXとOpenGLを使用するレンダラーと3Dシーンがあります。ビューポートとウィンドウは同じサイズです。
プラットフォームに依存しない方法で、指定されたマウス座標xおよびyのピッキングを実装するにはどうすればよいですか?
可能であれば、マウスポインターを介して目から光線を計算し、モデルと交差させることにより、CPUでピッキングを実行します。
これがオプションでない場合は、ある種のIDレンダリングを使用します。一意の色を選択する各オブジェクトを割り当て、これらの色でオブジェクトをレンダリングし、最後にマウスポインタの下のフレームバッファから色を読み取ります。
編集:問題がマウス座標から光線を構築する方法である場合は、次のものが必要です:投影行列Pとカメラ変換C。マウスポインタの座標が(x、y)で、ビューポートのサイズが(幅、高さ)の場合、光線に沿ったクリップスペースの1つの位置は次のようになります。
mouse_clip = [
float(x) * 2 / float(width) - 1,
1 - float(y) * 2 / float(height),
0,
1]
(マウス座標の原点が左上隅にあることが多いため、y軸を反転したことに注意してください)
以下も当てはまります。
mouse_clip = P * C * mouse_worldspace
これは次のようになります。
mouse_worldspace = inverse(C) * inverse(P) * mouse_clip
私たちは今持っています:
p = C.position(); //origin of camera in worldspace
n = normalize(mouse_worldspace - p); //unit vector from p through mouse pos in worldspace
これが視聴錐台です:
まず、ニアプレーンのどこでマウスクリックが発生したかを判断する必要があります。
unview = (P * M).inverse() = M.inverse() * P.inverse()
ここM
で、はModelView行列で、P
は投影行列です。次に、カメラがワールドスペースのどこにあるかを判断し、カメラから開始して、ニアプレーンで見つけたポイントを通過する光線を描画します。
カメラはM.inverse().col(4)
、つまり、逆ModelView行列の最後の列にあります。
最終的な擬似コード:
normalised_x = 2 * mouse_x / win_width - 1
normalised_y = 1 - 2 * mouse_y / win_height
// note the y pos is inverted, so +y is at the top of the screen
unviewMat = (projectionMat * modelViewMat).inverse()
near_point = unviewMat * Vec(normalised_x, normalised_y, 0, 1)
camera_pos = ray_origin = modelViewMat.inverse().col(4)
ray_dir = near_point - camera_pos
まあ、かなり単純です、これの背後にある理論は常に同じです
1)2D座標の2倍を3D空間に投影解除します。(各APIには独自の機能がありますが、必要に応じて独自の機能を実装できます)。1つは最小Zに、もう1つは最大Zにあります。
2)これらの2つの値を使用して、最小Zから最大Zを指すベクトルを計算します。
3)ベクトルと点を使用して、最小Zから最大Zに向かう光線を計算します。
4)これで光線ができました。これにより、光線-三角形/光線-平面/光線-何かの交差を実行して、結果を得ることができます...
DirectXの経験はほとんどありませんが、OpenGLに似ていると確信しています。必要なのはgluUnproject呼び出しです。
有効なZバッファがあると仮定すると、マウスの位置にあるZバッファの内容を次のようにクエリできます。
// obtain the viewport, modelview matrix and projection matrix
// you may keep the viewport and projection matrices throughout the program if you don't change them
GLint viewport[4];
GLdouble modelview[16];
GLdouble projection[16];
glGetIntegerv(GL_VIEWPORT, viewport);
glGetDoublev(GL_MODELVIEW_MATRIX, modelview);
glGetDoublev(GL_PROJECTION_MATRIX, projection);
// obtain the Z position (not world coordinates but in range 0 - 1)
GLfloat z_cursor;
glReadPixels(x_cursor, y_cursor, 1, 1, GL_DEPTH_COMPONENT, GL_FLOAT, &z_cursor);
// obtain the world coordinates
GLdouble x, y, z;
gluUnProject(x_cursor, y_cursor, z_cursor, modelview, projection, viewport, &x, &y, &z);
gluを使用したくない場合は、gluUnProjectを実装することもできます。自分で実装することもできます。その機能は比較的単純で、opengl.orgで説明されています。
わかりました、このトピックは古いですが、このトピックで見つけた最高のものであり、少し役に立ちました。フォローしている人のためにここに投稿します;-)
これは、射影行列の逆行列を計算せずに機能させる方法です。
void Application::leftButtonPress(u32 x, u32 y){
GL::Viewport vp = GL::getViewport(); // just a call to glGet GL_VIEWPORT
vec3f p = vec3f::from(
((float)(vp.width - x) / (float)vp.width),
((float)y / (float)vp.height),
1.);
// alternatively vec3f p = vec3f::from(
// ((float)x / (float)vp.width),
// ((float)(vp.height - y) / (float)vp.height),
// 1.);
p *= vec3f::from(APP_FRUSTUM_WIDTH, APP_FRUSTUM_HEIGHT, 1.);
p += vec3f::from(APP_FRUSTUM_LEFT, APP_FRUSTUM_BOTTOM, 0.);
// now p elements are in (-1, 1)
vec3f near = p * vec3f::from(APP_FRUSTUM_NEAR);
vec3f far = p * vec3f::from(APP_FRUSTUM_FAR);
// ray in world coordinates
Ray ray = { _camera->getPos(), -(_camera->getBasis() * (far - near).normalize()) };
_ray->set(ray.origin, ray.dir, 10000.); // this is a debugging vertex array to see the Ray on screen
Node* node = _scene->collide(ray, Transform());
cout << "node is : " << node << endl;
}
これは透視投影を想定していますが、そもそも正投影では問題は発生しません。
通常のレイピッキングでも同じ状況ですが、何かがおかしいです。プロジェクト解除操作を適切な方法で実行しましたが、機能しません。私はいくつかの間違いを犯したと思いますが、どこにあるのかわかりません。私のmatix乗算、逆数、およびmatix乗算によるベクトルはすべて正常に機能するように見え、それらをテストしました。私のコードでは、WM_LBUTTONDOWNに反応しています。したがって、lParamは[Y][X]座標をdwordの2ワードとして返します。それらを抽出してから、正規化されたスペースに変換します。この部分も正常に機能することを確認しました。左下隅をクリックすると、-1 -1に近い値が得られ、他の3つの隅すべてに適切な値が表示されます。次に、デバッグにlinepoins.vtx配列を使用していますが、現実に近いものではありません。
unsigned int x_coord=lParam&0x0000ffff; //X RAW COORD
unsigned int y_coord=client_area.bottom-(lParam>>16); //Y RAW COORD
double xn=((double)x_coord/client_area.right)*2-1; //X [-1 +1]
double yn=1-((double)y_coord/client_area.bottom)*2;//Y [-1 +1]
_declspec(align(16))gl_vec4 pt_eye(xn,yn,0.0,1.0);
gl_mat4 view_matrix_inversed;
gl_mat4 projection_matrix_inversed;
cam.matrixProjection.inverse(&projection_matrix_inversed);
cam.matrixView.inverse(&view_matrix_inversed);
gl_mat4::vec4_multiply_by_matrix4(&pt_eye,&projection_matrix_inversed);
gl_mat4::vec4_multiply_by_matrix4(&pt_eye,&view_matrix_inversed);
line_points.vtx[line_points.count*4]=pt_eye.x-cam.pos.x;
line_points.vtx[line_points.count*4+1]=pt_eye.y-cam.pos.y;
line_points.vtx[line_points.count*4+2]=pt_eye.z-cam.pos.z;
line_points.vtx[line_points.count*4+3]=1.0;