Где я ошибся, пытаясь реализовать комплектацию с помощью raycasting в OpenGL ES 2.0 для iOS?
Я пытаюсь реализовать комплектацию с помощью raycasting в OpenGL ES 2.0 для приложения для iPad. В частности, я хочу знать, какая ячейка карты, похожей на майнкрафт, состоит из квадратных ячеек с разной высотой, на которые нажал пользователь. Однако мой код не находит пересечения ни с одной ячейкой.
В моем методе распознавания жестов я сначала получаю размеры точки и области просмотра
UITapGestureRecognizer* tapRecognizer = (UITapGestureRecognizer*) recognizer;
CGPoint tapLoc = [tapRecognizer locationInView: self.view];
GLint viewport[4];
glGetIntegerv(GL_VIEWPORT, viewport);
Затем я переворачиваю ось X, потому что мое приложение находится в ландшафтном режиме, и координаты openGL начинаются с другого угла, чем координаты окна.
tapLoc.x = viewport[2] - tapLoc.x;
И выполните unprojection с GLKitMathUnproject для значений z = 0 и z = 1, затем передайте результирующие векторы в функцию, которая будет возвращать координаты ячейки, которую пользователь нажал в виде struct BPPoint (всего два целых числа, x и y). В настоящее время на карте нет преобразований, поэтому я предполагаю, что мне нужно использовать единичную матрицу для ее преобразования, а матрица проекции была создана с помощью glkmatrix4makeperspective.
GLKVector3 nearPt = GLKMathUnproject(GLKVector3Make(tapLoc.x, tapLoc.y, 0.0), GLKMatrix4Identity, [BPGlobalEnv globalVars].cameraMatrix, &viewport[0] , &testResult);
GLKVector3 farPt = GLKMathUnproject(GLKVector3Make(tapLoc.x, tapLoc.y, 1.0), GLKMatrix4Identity, [BPGlobalEnv globalVars].cameraMatrix, &viewport[0] , &testResult);
BPPoint destination = [gameEngine.currMap gridForPoint: farPt fromPoint: nearPt];
Это тело этого метода. Я в основном просто перебираю и заново генерирую геометрию для вершины каждого квадрата моей карты (я знаю, что это неэффективно) тем же методом, который я использовал, чтобы заставить геометрию перейти в VBO, чтобы нарисовать ее в первую очередь.
-(BPPoint) gridForPoint: (GLKVector3) ray fromPoint: (GLKVector3) cameraCoords
{
size_t i = 0;
size_t j;
GLfloat* buffer = malloc(sizeof(GLfloat) * squareSize);
for (NSArray* row in self.mapCells) {
j = 0;
for (BPGridCell* cell in row) {
GLfloat a[3];
a[0] = i * tileSize;
a[1] = cell.height * step;
a[2] = j * tileSize;
GLfloat b[3];
b[0] = (i + 1) * tileSize;
b[1] = cell.height * step;
b[2] = (j + 1) * tileSize;
[self squareForPoint:a point:b pointer: buffer];
GLKVector3 v1 = GLKVector3Make(buffer[0], buffer[1], buffer[2]);
GLKVector3 v2 = GLKVector3Make(buffer[3], buffer[4], buffer[5]);
GLKVector3 v3 = GLKVector3Make(buffer[6], buffer[7], buffer[8]);
GLKVector3 v21 = GLKVector3Make(buffer[9], buffer[10], buffer[11]);
GLKVector3 v22 = GLKVector3Make(buffer[12], buffer[13], buffer[14]);
GLKVector3 v23 = GLKVector3Make(buffer[15], buffer[16], buffer[17]);
if ([self ray: ray fromCamera: cameraCoords intersectsTriangleWithV1: v1 V2: v2 V3: v3] ||
[self ray: ray fromCamera: cameraCoords intersectsTriangleWithV1: v21 V2: v22 V3: v23]) {
NSLog(@"found intersection");
free(buffer);
BPPoint toReturn;
toReturn.x = i;
toReturn.y = j;
NSLog(@"%ld, %ld", i, j);
return toReturn;
}
j += 1;
}
i += 1;
}
free(buffer);
BPPoint toReturn;
toReturn.x = i;
toReturn.y = j;
NSLog(@"%ld, %ld", i, j);
return toReturn;
}
И код для проверки треугольника на пересечение, который я конвертировал в objc отсюда ( http://www.cs.virginia.edu/~gfx/Courses/2003/ImageSynthesis/papers/Acceleration/Fast%20MinimumStorage%20RayTriangle%20Intersection.pdf) следует:
-(BOOL) ray: (GLKVector3) ray fromCamera: (GLKVector3) camera intersectsTriangleWithV1: (GLKVector3) v1 V2: (GLKVector3) v2 V3: (GLKVector3) v3
{
GLKVector3 e1 = GLKVector3Subtract(v2, v1);
GLKVector3 e2 = GLKVector3Subtract(v3, v1);
GLKVector3 h = GLKVector3CrossProduct(ray, e2);
float det = GLKVector3DotProduct(e1, h);
if (det > -0.00001 && det < 0.00001) {
return NO;
}
float invDet = 1.0/det;
GLKVector3 s = GLKVector3Subtract(camera, v1);
float u = GLKVector3DotProduct(s, h) * invDet;
if (u < 0.0 || u > 1.0) {
return NO;
}
GLKVector3 q = GLKVector3CrossProduct(s, e1);
float v = invDet * GLKVector3DotProduct(ray, q);
if (v < 0.0 || u + v > 1.0) {
return NO;
}
return YES;
}
Когда я запускаю это с картой 10х10, она печатает 10,10 для координат, обычно не находя пересечения. Где я облажался концептуально или в реализации этого или обоих?
1 ответ
Я нашел решение: вызов glgetintegerv вернул перевернутые значения, предположительно потому, что устройство находилось в ландшафтном режиме. Мне также пришлось вычесть ближнюю точку из дальней, чтобы получить правильный вектор направления луча.