Преобразование лучей для пересечения лучей и треугольников

В openGL у меня есть 3D-модель, на которой я выполняю пересечение лучей и треугольников, используя код, описанный в статье «Быстрое пересечение лучей / треугольников с минимальным объемом памяти» (http://jgt.akpeters.com/paper/MollerTrumbore97/).

Моя позиция курсора не проецируется в мировое пространство с использованием следующего кода:

bool SCamera::unproject(Vector3 input, Vector3 & output){

  GLint viewport[4]; 
  glGetIntegerv(GL_VIEWPORT,viewport); //Grab screen info

  float x = input.mX; 
  float y = input.mY;
  float z = input.mZ;

  Matrix4 P, Mv, res;

  P = getProjection();
  Mv = getCameraTransform();

  Vector3 N; //Cursor point translated to having 0,0 at screen center
  N.mX = ((x-viewport[0]) / viewport[2])*2 - 1;
  N.mY = ((y-viewport[1]) / viewport[3])*2 - 1;
  N.mZ = z*2-1;

  res = P * Mv; //Multiply P * Mv to get transform
  Vector3 w = res.inverse() * N; //Apply transform to N.

  output.mX = w[0];
  output.mY = w[1];
  output.mZ = w[2];
  return true;
}

После этого я формирую луч следующим образом:

unproject(Vector3(xClick, yClick,0),resultUnproject)
ray.origin = cameraPosition;
ray.direction = resultUnproject - ray.origin;
ray.direction.normalize();

Теперь, наконец, я пытаюсь пропустить этот луч через код треугольника (ссылка выше), но мне кажется, что я не могу правильно его преобразовать. Моя текущая попытка выглядит следующим образом:

Matrix4 mview, T;
mview = getModelview();
T = mview.inverse();
ray.origin = T*ray.origin;
ray.direction = T*ray.direction;
ray.direction.normalize();

По какой-то причине это не работает. Я неправильно формирую свой луч? Или это неправильно трансформирует?


person Ian Nafiri    schedule 18.10.2010    source источник


Ответы (4)


Один из способов может быть:

Возьмите исходную точку луча и определите точку на единичном расстоянии в направлении луча в мировом пространстве. Затем выполните преобразование в обеих точках, то есть умножьте на обратную матрицу преобразования. Затем вы можете определить новое направление луча по разнице между двумя перенесенными точками. Если вы проверите, отличается ли это направление от исходного, вы быстро поймете, не пропустили ли вы шаг.

person sje397    schedule 18.10.2010
comment
Потрясающий! Это очень хороший способ отладки вещей, о которых я не думал. Спасибо. - person Ian Nafiri; 19.10.2010
comment
К сожалению, проблемы все еще возникают. Не могли бы вы просмотреть отрывки из сокращенного кода, перечисленные выше? - person Ian Nafiri; 20.10.2010
comment
@Ian - Я не думаю, что смогу так сильно помочь, так как он немного удален от вещей OGL, к которым я привык, а я парень кватернион;) Формирование луча кажется немного подозрительным для меня - обычно с помощью glFrustum вы можете создать «пирамиду» с пользователем в точке (0,0) и экраном внизу - база зависит от как размера окна просмотра, так и диапазонов координат . - person sje397; 20.10.2010
comment
Спасибо. :) Я уже полнели ломаю голову над этим, так что любая новая область исследований полезна. Вы очень помогли, просто указав области, на которые нужно смотреть. Я просто буду продолжать, пока не получу все правильно. - person Ian Nafiri; 20.10.2010

Моим решением было сделать то, что предлагал sje397, но просто использовать результат как новый луч.

Итак, в моем проекте (который является XNA), учитывая, что ray не проецируется курсором:

rayTransform = Matrix.Invert(model_transform);

//Transform ray (or more accurately its defining components into a new ray)
Vector3 v1 = Vector3.Transform(ray.Position, rayTransform);
Vector3 v2 = Vector3.Transform((ray.Position + ray.Direction), rayTransform);
transformedray = new Ray(v1, v2 - v1);

model_transform здесь генерируется так же, как преобразование для шейдера (то есть без замены мирового / локального умножения или чего-то еще)

Это отлично работает в моем проекте с целью вращения, преобразования и масштабирования пикировки всевозможными способами.

Кроме того, вы можете найти это немного неуместным, но если у вас все еще есть проблемы, уверены ли вы, что код для отмены проецирования курсора и создания луча выбора работает правильно? Я предполагаю, что это должно быть, если ваш код выбора треугольника работает, мне просто кажется, что он получает луч «круговым» способом.

В моем проекте я беру две точки, одну "на" плоскости "окна просмотра, так что Vector3(cursorx,cursory,0) это снова, но продвинулось по оси Z Vector3(cursorx,cursory,1). Отмените проецирование обеих этих точек, результат первой - ваше начало, а разница - ваше направление.

Удачи!

person sebf    schedule 12.02.2011
comment
Наглость - это хорошо, особенно когда помогает. Я давно не просматривал свой фрагмент кода для этого (он работает, поэтому я подумал об оптимизации позже), но похоже, что ваши предложения будут большим подспорьем. Спасибо. - person Ian Nafiri; 14.02.2011

Итак, я наконец получил это работающее, хотя я не удовлетворен тем, как. В моем случае поворот отдельной модели применяется как преобразование перед рендерингом, поэтому мне пришлось преобразовать целевой треугольник с помощью матрицы просмотра модели.

То, что я пытался сделать выше, - это вычислить луч, который служил бы той же цели, перемещая начало луча вокруг объекта так, как если бы объект был повернут. К сожалению, я не мог правильно понять математику.

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

person Ian Nafiri    schedule 22.10.2010

Если у вас есть луч (уже преобразованный в мировое пространство) и вы хотите проверить, пересекает ли он объект / треугольник, вам необходимо умножить его на инвертированные матрицы объекта. Вот какой-то псевдокод:

NewRayPosition = Vector3.Transform(OldRay.Position, Matrix.Invert(Transformation);
NewRayDirection = Vector3.Transform(OldRay.Direction, Matrix.Invert(Rotation*Scale));
NewRayDirection.Normalize();

Умножьте положение луча на Перенос, Масштаб и Вращение, но направление только на Масштаб и Вращение. Делать так, как вы это делали, неприемлемо по причинам производительности. Подробнее здесь: http://www.cl.cam.ac.uk/teaching/1999/AGraphHCI/SMAG/node2.html#SECTION00024100000000000000

person Tarec    schedule 14.06.2013