Amethyst: как преобразовать координаты мыши в координаты сцены?


#1

Подскажите, пожалуйста, формулу или литературу где её взять.
Собственно есть:

  • координаты мыши с (0,0) в левом верхнем углу окна
  • ортогональная проекция камеры в виде матрицы 4x4
  • глобальное положение камеры в виде матрицы 4x4
  • ширина и высота окна

У камеры (0,0) в левом нижнем углу.
Как преобразовать координаты мыши в координаты камеры (сцены) с учётом положения и поворота камеры?


#2

Прямо так взять экранную точку и преобразовать в точку в мировом пространстве не выйдет, потому что одно это 2д, другое 3д. Так можно только луч получить, исходящий из наблюдателя. Потом уже можно искать с чем там этот луч сталкивается. Тебе луч нужен или?


#3

Нет, без луча, просто проекция. Что-то вроде Camera.ScreenToWorldPoint


#4

Ну так эта функция принимает 3 координаты, а у тебя только 2.
Точке на экране соответсвует бесконечное количество точек сцены (луч).

Вообще, в лоб, то, кажется, как-то так

struct Camera {
    view: Projective3<f32>,
    proj: Perspective3<f32>,
}

fn world_from_mouse(pos: Point2<f32>, camera: &Camera) -> Point3<f32> {
  let x = pos[0];
  let y = 1. 0 - pos[1]; // Переворачиваем потому что `У камеры (0,0) в левом нижнем углу` а `координаты мыши с (0,0) в левом верхнем углу окна`.
  // Хотя я б ожидал, что у камеры (0,0) в центре.
  let pos = Point3::new(x, y, 0.0).to_homogeneous();
  Point3::from_homogeneous(camera.view.to_homogeneous() * camera.projection.inverse() * pos)
}

На выходе будет точка на near-plane камеры.


#5

Так там в примере за z берётся некий cam.nearClipPlane, полагаю его можно заменить на 1.0

У камеры в аметисте у меня выходит в левом нижнем углу, если я что-то не путаю конечно.

Я что-то делаю не так

fn screen_to_2d_scene(
    position: (f64, f64),
    camera_transform: &GlobalTransform,
    camera: &Camera,
    screen_dim: &ScreenDimensions,
) -> (f32, f32) {
    let projection = Orthographic3::from_matrix_unchecked(camera.proj);
    let perspective = Perspective3::from_matrix_unchecked(camera_transform.0);

    let screen_x = position.0 as f32;
    let screen_y = position.1 as f32;

    let pos = Point3::new(screen_x, -screen_y, 1.0).to_homogeneous();

    let world = Point3::from_homogeneous(perspective.to_homogeneous() * projection.inverse() * pos)
        .unwrap();

    (world.x, world.y)
}

выдаёт далеко не то что хотелось бы.


#6

Не получается у меня что-то, размер сцены 409х230 а координаты мыши на выходе получаются
от (205, 115) до (409, 230)
То есть правый верхний угол попадает, а нижний левый нет.

fn screen_to_2d_scene(
    position: (f64, f64),
    camera_transform: &GlobalTransform,
    camera: &Camera,
    screen_dim: &ScreenDimensions,
) -> (f32, f32) {
    let screen_x = position.0 as f32 / screen_dim.width();
    let screen_y = 1.0 - position.1 as f32 / screen_dim.height();

    let screen_point = Point3::new(screen_x, screen_y, 0.0).to_homogeneous();

    let matrix = camera_transform.0 * camera.proj.try_inverse().unwrap();
    
    let world_point = matrix * screen_point;

    (world_point.x, world_point.y)
}

#7

Всё, разобрался, всем спасибо

fn screen_to_2d_scene(
    position: (f64, f64),
    camera_transform: &GlobalTransform,
    camera: &Camera,
    screen_dim: &ScreenDimensions,
) -> Point3<f32> {
    let screen_x = 2.0 * position.0 as f32 / screen_dim.width() - 1.0;
    let screen_y = 1.0 - 2.0 * position.1 as f32 / screen_dim.height();
    let screen_point = Point3::new(screen_x, screen_y, 0.0).to_homogeneous();

    let world_point = camera_transform.0 * camera.proj.try_inverse().unwrap() * screen_point;

    Point3::from_homogeneous(world_point).unwrap()
}