0

このウェブサイトの指示に従って、ピッキング レイを実装しようとしています。

今のところ、私は基本的に地面をクリックして、私の小さなフィギュアがこのポイントに向かって歩くように命令できるようにしたいだけです. 私の地面は平らで、回転も平行移動もしていないので、y が 0 に達したときにピッキング レイの x 座標と z 座標を見つける必要があります。

これまでのところ、これは私が思いついたものです:

//some constants
float HEIGHT = 768.f;
float LENGTH = 1024.f;
float fovy = 45.f;
float nearClip = 0.1f;

//mouse position on screen
float x = MouseX;
float y = HEIGHT - MouseY;

//GetView() returns the viewing direction, not the lookAt point.
glm::vec3 view = cam->GetView();
glm::normalize(view);

glm::vec3 h = glm::cross(view, glm::vec3(0,1,0) ); //cameraUp
glm::normalize(h);

glm::vec3 v = glm::cross(h, view);
glm::normalize(v);

// convert fovy to radians 
float rad = fovy * 3.14 / 180.f; 
float vLength = tan(rad/2) * nearClip; //nearClippingPlaneDistance
float hLength = vLength * (LENGTH/HEIGHT);

v *= vLength;
h *= hLength;

// translate mouse coordinates so that the origin lies in the center
// of the view port
x -= LENGTH / 2.f;
y -= HEIGHT / 2.f;

// scale mouse coordinates so that half the view port width and height
// becomes 1
x /= (LENGTH/2.f);
y /= (HEIGHT/2.f);

glm::vec3 cameraPos = cam->GetPosition();

// linear combination to compute intersection of picking ray with
// view port plane
glm::vec3 pos = cameraPos + (view*nearClip) + (h*x) + (v*y);

// compute direction of picking ray by subtracting intersection point
// with camera position
glm::vec3 dir = pos - cameraPos;

//Get intersection between ray and the ground plane
pos -= (dir * (pos.y/dir.y));

このpos時点で、ピッキング レイが地面に当たるポイントになると予想されます。しかし、試してみると、次のような結果が得られます: エラー (マウス カーソルは記録されませんでした) 地面にはテクスチャがないため見にくいですが、ほとんどの RTS ゲームのようにカメラが傾いています。Blender で遠く離れた人間に見える存在をモデル化するという哀れな試みは、私の計算によると交差が発生したポイントを示しています。

viewそのため、との間の変換がdir台無しになり、私の光線が間違った方向を向いてしまったようです。計算された位置と実際の位置の間のギャップは、画面の中心からマウスを離すほど大きくなります。

私はそれを発見しました:

  • HEIGHT と LENGTH は正確ではありません。Windows は境界線のために数ピクセルを切り取るので、ウィンドウの解像度として 1006,728 を使用する方が正確です。多少のズレはあると思います。
  • fovy を 45 から約 78 に上げると、かなり正確な光線が得られます。だから、私がフォービーとして使っているものに何か問題があるのか​​もしれません。私は明示的に呼び出してglm::perspective(45.f, 1.38f, 0.1f, 500.f)います(それぞれ fovy、アスペクト比、fNear、fFar)。

だからここで私は迷っています。正確な光線を取得するにはどうすればよいですか?

PS: これを実装した関数やライブラリがあることは知っていますが、学習目的でこれらのものから離れようとしています。

4

1 に答える 1

0

深度バッファ情報を使用してカーソルから 3D への変換を行う作業コードを次に示します。

  glGetIntegerv(GL_VIEWPORT, @fViewport);
  glGetDoublev(GL_PROJECTION_MATRIX, @fProjection);
  glGetDoublev(GL_MODELVIEW_MATRIX, @fModelview);

  //fViewport already contains viewport offsets
  PosX := X;
  PosY := ScreenY - Y; //In OpenGL Y axis is inverted and starts from bottom

  glReadPixels(PosX, PosY, 1, 1, GL_DEPTH_COMPONENT, GL_FLOAT, @vz);

  gluUnProject(PosX, PosY, vz, fModelview, fProjection, fViewport, @wx, @wy, @wz);
  XYZ.X := wx;
  XYZ.Y := wy;
  XYZ.Z := wz;

レイ/プレーンの交差のみをテストする場合、これは DepthBuffer のない 2 番目の部分です。

  gluUnProject(PosX, PosY, 0, fModelview, fProjection, fViewport, @x1, @y1, @z1); //Near
  gluUnProject(PosX, PosY, 1, fModelview, fProjection, fViewport, @x2, @y2, @z2); //Far

  //No intersection
  Result := False;
  XYZ.X := 0;
  XYZ.Y := 0;
  XYZ.Z := aZ;

  if z2 < z1 then
    SwapFloat(z1, z2);

  if (z1 <> z2) and InRange(aZ, z1, z2) then
  begin
    D := 1 - (aZ - z1) / (z2 - z1);
    XYZ.X := Lerp(x1, x2, D);
    XYZ.Y := Lerp(y1, y2, D);
    Result := True;
  end;

あなたがやっていることとはかなり違うと思いますが、多分それはもっと理にかなっています。

于 2012-11-21T13:25:35.253 に答える