Я пытаюсь реализовать сбор луч с помощью инструкций от этот сайт.
Прямо сейчас я просто хочу иметь возможность щелкать по земле, чтобы приказать моей маленькой фигуре идти к этой точке.
Поскольку моя земная плоскость плоская, не повернута и не переведена, мне нужно будет найти координаты x и z моего луча, когда y достигает 0.
Пока все хорошо, вот что я придумал:
//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
где-то запутался, и мой луч в конечном итоге указывал в неправильном направлении.
Разрыв между рассчитанным положением и фактическим положением увеличивает расстояние, на которое я отошел от центра экрана.
Я узнал, что:
glm::perspective(45.f, 1.38f, 0.1f, 500.f)
(fovy, соотношение сторон, fNear, fFar соответственно).Так вот, где я потерялся. Что мне нужно сделать, чтобы получить точный луч?
PS: я знаю, что есть функции и библиотеки, которые это реализовали, но я стараюсь держаться подальше от этих вещей в учебных целях.
Вот рабочий код, который делает курсор в 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:
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;
Я нахожу это несколько отличным от того, что вы делаете, но, возможно, это будет иметь больше смысла.
Других решений пока нет …