Сборка пересечения Луч-Треугольник не работает - PullRequest
2 голосов
/ 21 января 2011

Я работал над этим последние несколько дней и застрял. Мне нужно иметь возможность касаться экрана и возвращать координаты x, y, z точки на моей модели, ближайшей к ближней плоскости, которая пересекает луч, сгенерированный в точке выбора. Я думаю, что часть моей проблемы заключается в том, что я выполняю кучу матричных преобразований и поворотов по всему коду рендеринга для моей модели, хотя интересующая меня геометрия все визуализируется в определенном состоянии преобразования. Мой код, который я использую, ниже. Если кто-нибудь может помочь мне понять, как заставить это работать, это было бы здорово. checkCollision () получает точку, на которую нажимает пользователь, и gluUnProject () должен преобразовать мою 2-мерную точку выбора в трехмерные координаты на ближней и дальней плоскостях, 0 - ближняя и 1 - дальняя. Мое использование здесь и вызывается прямо перед рендерингом геометрии, поэтому все преобразования уже применены:

[self checkCollision:touchPoint panVector:panVec];

Этот код ниже является кодом проверки столкновения:

-(Boolean) checkCollision:(CGPoint)winPos panVector:(Vector3f*)panVec
{   
glGetIntegerv(GL_VIEWPORT, viewport);

winPos.y = (float)viewport[3] - winPos.y;

Vector3f nearPoint;
Vector3f farPoint;

glGetFloatv(GL_PROJECTION_MATRIX, projection);
glGetFloatv(GL_MODELVIEW_MATRIX, modelview);

//Retreiving position projected on near plane
gluUnProject(winPos.x, winPos.y , 0, modelview, projection, viewport, &nearPoint.x, &nearPoint.y, &nearPoint.z);

//Retreiving position projected on far plane
gluUnProject(winPos.x, winPos.y,  1, modelview, projection, viewport, &farPoint.x, &farPoint.y, &farPoint.z);

Vector3f *near = [[Vector3f alloc] initWithFloatsX:nearPoint.x Y:nearPoint.y Z:nearPoint.z];
Vector3f *far = [[Vector3f alloc] initWithFloatsX:farPoint.x Y:farPoint.y Z:farPoint.z];
Vector3f *d = [Vector3f subtractV1:far minusV2:near];

Vector3f *v0 = [[Vector3f alloc] init];
Vector3f *v1 = [[Vector3f alloc] init];
Vector3f *v2 = [[Vector3f alloc] init];
Vector3f *e1; // = [[Vector3f alloc] init];
Vector3f *e2; // = [[Vector3f alloc] init];

for (int i = 0; i < assemblyObj->numObjects; i++) {
    for (int j = 0; j < assemblyObj->partList[i].numVertices; j+=18) {
        v0.x = assemblyObj->partList[i].vertices[j+0];
        v0.y = assemblyObj->partList[i].vertices[j+1];
        v0.z = assemblyObj->partList[i].vertices[j+2];

        v1.x = assemblyObj->partList[i].vertices[j+6];
        v1.y = assemblyObj->partList[i].vertices[j+7];
        v1.z = assemblyObj->partList[i].vertices[j+8];

        v2.x = assemblyObj->partList[i].vertices[j+12];
        v2.y = assemblyObj->partList[i].vertices[j+13];
        v2.z = assemblyObj->partList[i].vertices[j+14];

        e1 = [Vector3f subtractV1:v1 minusV2:v0];
        e2 = [Vector3f subtractV1:v2 minusV2:v0];

        Vector3f *p = [[Vector3f alloc] init];
        [Vector3f cross:p V1:d V2:e2];
        float a = [Vector3f dot:e1 V2:p];
        if (a > -.000001 && a < .000001) {
            continue;
        }

        float f = 1/a;
        Vector3f *s = [Vector3f subtractV1:near minusV2:v0];
        float u = f*([Vector3f dot:s V2:p]);
        if (u<0 || u>1) {
            continue;
        }
        Vector3f *q = [[Vector3f alloc] init];
        [Vector3f cross:q V1:s V2:e1];
        float v = f*([Vector3f dot:d V2:q]);
        if (v<0 || (u+v)>1) {
            continue;
        }
        //NSLog(@"hit polygon");
        return true;
    }
}

//NSLog(@"didn't hit polygon");
return FALSE;   
}  


GLint gluUnProject(GLfloat winx, GLfloat winy, GLfloat winz,
         const GLfloat model[16], const GLfloat proj[16],
         const GLint viewport[4],
         GLfloat * objx, GLfloat * objy, GLfloat * objz)
{
/* matrice de transformation */
GLfloat m[16], A[16];
GLfloat in[4], out[4];

/* transformation coordonnees normalisees entre -1 et 1 */
in[0] = (winx - viewport[0]) * 2 / viewport[2] - 1.f;
in[1] = (winy - viewport[1]) * 2 / viewport[3] - 1.f;
in[2] = 2 * winz - 1.f;
in[3] = 1.f;

/* calcul transformation inverse */
matmul(A, proj, model);
invert_matrix(A, m);

/* d'ou les coordonnees objets */
transform_point(out, m, in);
if (out[3] == 0.f)
    return GL_FALSE;
*objx = out[0] / out[3];
*objy = out[1] / out[3];
*objz = out[2] / out[3];
return GL_TRUE;
}


void transform_point(GLfloat out[4], const GLfloat m[16], const GLfloat in[4])
{
#define M(row,col)  m[col*4+row]
out[0] =
M(0, 0) * in[0] + M(0, 1) * in[1] + M(0, 2) * in[2] + M(0, 3) * in[3];
out[1] =
M(1, 0) * in[0] + M(1, 1) * in[1] + M(1, 2) * in[2] + M(1, 3) * in[3];
out[2] =
M(2, 0) * in[0] + M(2, 1) * in[1] + M(2, 2) * in[2] + M(2, 3) * in[3];
out[3] =
M(3, 0) * in[0] + M(3, 1) * in[1] + M(3, 2) * in[2] + M(3, 3) * in[3];
#undef M
}

void matmul(GLfloat * product, const GLfloat * a, const GLfloat * b)
{
/* This matmul was contributed by Thomas Malik */
GLfloat temp[16];
GLint i;

#define A(row,col)  a[(col<<2)+row]
#define B(row,col)  b[(col<<2)+row]
#define T(row,col)  temp[(col<<2)+row]

/* i-te Zeile */
for (i = 0; i < 4; i++) {
    T(i, 0) =
    A(i, 0) * B(0, 0) + A(i, 1) * B(1, 0) + A(i, 2) * B(2, 0) + A(i,
                                                                  3) *
    B(3, 0);
    T(i, 1) =
    A(i, 0) * B(0, 1) + A(i, 1) * B(1, 1) + A(i, 2) * B(2, 1) + A(i,
                                                                  3) *
    B(3, 1);
    T(i, 2) =
    A(i, 0) * B(0, 2) + A(i, 1) * B(1, 2) + A(i, 2) * B(2, 2) + A(i,
                                                                  3) *
    B(3, 2);
    T(i, 3) =
    A(i, 0) * B(0, 3) + A(i, 1) * B(1, 3) + A(i, 2) * B(2, 3) + A(i,
                                                                  3) *
    B(3, 3);
}

#undef A
#undef B
#undef T
memcpy(product, temp, 16 * sizeof(GLfloat));
}

int invert_matrix(const GLfloat * m, GLfloat * out)
{
/* NB. OpenGL Matrices are COLUMN major. */
#define SWAP_ROWS(a, b) { GLfloat *_tmp = a; (a)=(b); (b)=_tmp; }
#define MAT(m,r,c) (m)[(c)*4+(r)]

GLfloat wtmp[4][8];
GLfloat m0, m1, m2, m3, s;
GLfloat *r0, *r1, *r2, *r3;

r0 = wtmp[0], r1 = wtmp[1], r2 = wtmp[2], r3 = wtmp[3];

r0[0] = MAT(m, 0, 0), r0[1] = MAT(m, 0, 1),
r0[2] = MAT(m, 0, 2), r0[3] = MAT(m, 0, 3),
r0[4] = 1.f, r0[5] = r0[6] = r0[7] = 0.f,
r1[0] = MAT(m, 1, 0), r1[1] = MAT(m, 1, 1),
r1[2] = MAT(m, 1, 2), r1[3] = MAT(m, 1, 3),
r1[5] = 1.f, r1[4] = r1[6] = r1[7] = 0.f,
r2[0] = MAT(m, 2, 0), r2[1] = MAT(m, 2, 1),
r2[2] = MAT(m, 2, 2), r2[3] = MAT(m, 2, 3),
r2[6] = 1.f, r2[4] = r2[5] = r2[7] = 0.f,
r3[0] = MAT(m, 3, 0), r3[1] = MAT(m, 3, 1),
r3[2] = MAT(m, 3, 2), r3[3] = MAT(m, 3, 3),
r3[7] = 1.f, r3[4] = r3[5] = r3[6] = 0.f;

/* choose pivot - or die */
if (fabsf(r3[0]) > fabsf(r2[0]))
    SWAP_ROWS(r3, r2);
if (fabsf(r2[0]) > fabsf(r1[0]))
    SWAP_ROWS(r2, r1);
if (fabsf(r1[0]) > fabsf(r0[0]))
    SWAP_ROWS(r1, r0);
if (0.f == r0[0])
    return GL_FALSE;

/* eliminate first variable     */
m1 = r1[0] / r0[0];
m2 = r2[0] / r0[0];
m3 = r3[0] / r0[0];
s = r0[1];
r1[1] -= m1 * s;
r2[1] -= m2 * s;
r3[1] -= m3 * s;
s = r0[2];
r1[2] -= m1 * s;
r2[2] -= m2 * s;
r3[2] -= m3 * s;
s = r0[3];
r1[3] -= m1 * s;
r2[3] -= m2 * s;
r3[3] -= m3 * s;
s = r0[4];
if (s != 0.f) {
    r1[4] -= m1 * s;
    r2[4] -= m2 * s;
    r3[4] -= m3 * s;
}
s = r0[5];
if (s != 0.f) {
    r1[5] -= m1 * s;
    r2[5] -= m2 * s;
    r3[5] -= m3 * s;
}
s = r0[6];
if (s != 0.f) {
    r1[6] -= m1 * s;
    r2[6] -= m2 * s;
    r3[6] -= m3 * s;
}
s = r0[7];
if (s != 0.f) {
    r1[7] -= m1 * s;
    r2[7] -= m2 * s;
    r3[7] -= m3 * s;
}

/* choose pivot - or die */
if (fabsf(r3[1]) > fabsf(r2[1]))
    SWAP_ROWS(r3, r2);
if (fabsf(r2[1]) > fabsf(r1[1]))
    SWAP_ROWS(r2, r1);
if (0.f == r1[1])
    return GL_FALSE;

/* eliminate second variable */
m2 = r2[1] / r1[1];
m3 = r3[1] / r1[1];
r2[2] -= m2 * r1[2];
r3[2] -= m3 * r1[2];
r2[3] -= m2 * r1[3];
r3[3] -= m3 * r1[3];
s = r1[4];
if (0.f != s) {
    r2[4] -= m2 * s;
    r3[4] -= m3 * s;
}
s = r1[5];
if (0.f != s) {
    r2[5] -= m2 * s;
    r3[5] -= m3 * s;
}
s = r1[6];
if (0.f != s) {
    r2[6] -= m2 * s;
    r3[6] -= m3 * s;
}
s = r1[7];
if (0.f != s) {
    r2[7] -= m2 * s;
    r3[7] -= m3 * s;
}

/* choose pivot - or die */
if (fabs(r3[2]) > fabs(r2[2]))
    SWAP_ROWS(r3, r2);
if (0.f == r2[2])
    return GL_FALSE;

/* eliminate third variable */
m3 = r3[2] / r2[2];
r3[3] -= m3 * r2[3], r3[4] -= m3 * r2[4],
r3[5] -= m3 * r2[5], r3[6] -= m3 * r2[6], r3[7] -= m3 * r2[7];

/* last check */
if (0.f == r3[3])
    return GL_FALSE;

s = 1.f / r3[3];        /* now back substitute row 3 */
r3[4] *= s;
r3[5] *= s;
r3[6] *= s;
r3[7] *= s;

m2 = r2[3];         /* now back substitute row 2 */
s = 1.f / r2[2];
r2[4] = s * (r2[4] - r3[4] * m2), r2[5] = s * (r2[5] - r3[5] * m2),
r2[6] = s * (r2[6] - r3[6] * m2), r2[7] = s * (r2[7] - r3[7] * m2);
m1 = r1[3];
r1[4] -= r3[4] * m1, r1[5] -= r3[5] * m1,
r1[6] -= r3[6] * m1, r1[7] -= r3[7] * m1;
m0 = r0[3];
r0[4] -= r3[4] * m0, r0[5] -= r3[5] * m0,
r0[6] -= r3[6] * m0, r0[7] -= r3[7] * m0;

m1 = r1[2];         /* now back substitute row 1 */
s = 1.f / r1[1];
r1[4] = s * (r1[4] - r2[4] * m1), r1[5] = s * (r1[5] - r2[5] * m1),
r1[6] = s * (r1[6] - r2[6] * m1), r1[7] = s * (r1[7] - r2[7] * m1);
m0 = r0[2];
r0[4] -= r2[4] * m0, r0[5] -= r2[5] * m0,
r0[6] -= r2[6] * m0, r0[7] -= r2[7] * m0;

m0 = r0[1];         /* now back substitute row 0 */
s = 1.f / r0[0];
r0[4] = s * (r0[4] - r1[4] * m0), r0[5] = s * (r0[5] - r1[5] * m0),
r0[6] = s * (r0[6] - r1[6] * m0), r0[7] = s * (r0[7] - r1[7] * m0);

MAT(out, 0, 0) = r0[4];
MAT(out, 0, 1) = r0[5], MAT(out, 0, 2) = r0[6];
MAT(out, 0, 3) = r0[7], MAT(out, 1, 0) = r1[4];
MAT(out, 1, 1) = r1[5], MAT(out, 1, 2) = r1[6];
MAT(out, 1, 3) = r1[7], MAT(out, 2, 0) = r2[4];
MAT(out, 2, 1) = r2[5], MAT(out, 2, 2) = r2[6];
MAT(out, 2, 3) = r2[7], MAT(out, 3, 0) = r3[4];
MAT(out, 3, 1) = r3[5], MAT(out, 3, 2) = r3[6];
MAT(out, 3, 3) = r3[7];

return GL_TRUE;

#undef MAT
#undef SWAP_ROWS
}

Edit:

Я последовал предложению Джастина Майнерса о точках рендеринга, чтобы показать, где генерируется мой пик-луч, и я могу видеть, что происходит сейчас, но не знаю почему. Моя сцена реализует вращение аркбола, масштабирование и панорамирование кватернионов. Я примерно изложу, что делает моя сцена, а затем то, что происходит с моим медиатором.

Сначала настройте мой видовой экран:

glViewport(0, 0, scene.width, scene.height);
glOrthof(-11.25, 11.25, -14.355, 14.355, -1000, 1000);

Затем я беру 16-элементную матрицу, которую я использую как часть моего метода arcball, чтобы перемещаться по моей сцене и умножаю на нее матрицу вида модели:

float mat[16];
[arcball get_Renamed:mat];
glMultMatrixf(mat);

Теперь я выбираю свой луч:

glGetIntegerv(GL_VIEWPORT, viewport);
glGetFloatv(GL_PROJECTION_MATRIX, projection);
glGetFloatv(GL_MODELVIEW_MATRIX, modelview);

touchPoint.y = (float)viewport[3] - touchPoint.y;

Vector3f nearPoint, farPoint;

//Retreiving position projected on near plane
gluUnProject(touchPoint.x, touchPoint.y , 0, modelview, projection, viewport, &nearPoint.x, &nearPoint.y, &nearPoint.z);

//Retreiving position projected on far plane
gluUnProject(touchPoint.x, touchPoint.y,  1, modelview, projection, viewport, &farPoint.x, &farPoint.y, &farPoint.z);

float coords[3] = {nearPoint.x, nearPoint.y, nearPoint.z};
float coords2[3] = {farPoint.x, farPoint.y, farPoint.z};

glPointSize(100);
glColor4f(1, 0, 0, 1);
glEnableClientState(GL_VERTEX_ARRAY);
glVertexPointer(3, GL_FLOAT, sizeof(coords[0])*3, coords);
glDrawArrays(GL_POINTS, 0, 1);

glPointSize(150);
glColor4f(0, 0, 1, 1);
glEnableClientState(GL_VERTEX_ARRAY);
glVertexPointer(3, GL_FLOAT, sizeof(coords2[0])*3, coords2);
glDrawArrays(GL_POINTS, 0, 1);
glDisableClientState(GL_VERTEX_ARRAY);

Я делаю это, и это прекрасно работает, прежде чем я поворачиваю свою сцену, но как только я начинаю вращать свою сцену, дальняя точка начинает двигаться. Если я поверну сцену примерно на 180 градусов, дальняя точка вернется в соответствие с ближней точкой. Есть идеи, что происходит? Аркбол просто основан на алгоритме Кена Шумейка.

Ответы [ 2 ]

3 голосов
/ 21 января 2011

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

glPushMatrix();

// Rotate world
glRotate(...)

// Mouse glGetIntv, and glGetFloatV here

drawObject()

Образец кода для рисования (Пусть Vector является просто структурой с компонентами float x, y и z.

glEnableClientState(GL_VERTEX_ARRAY);
    glVertexPointer(3, GL_FLOAT, sizeof(Vector), points);

    glDrawArrays(GL_POINTS, 0, 2);

    glDisableClientState(GL_VERTEX_ARRAY); 
1 голос
/ 26 января 2011

Я наконец понял, что я делаю не так. Вы должны получить состояние GL_VIEWPORT и GL_PROJECTION_MATRIX сразу после их создания, то есть сразу после

glMatrixMode(GL_PROJECTION);
glLoadIdentity();

звонки. Если вы сделаете это позже, то ваши преобразования матриц влияют на ваш видовой экран и матрицы проекций. Не похоже, что они должны, но они делают. Единственная матрица, в которую вы хотите получить состояние, когда вы выбираете, - это матрица вида модели, и вы хотите сделать это, когда ваш вид модели имеет те же преобразования, что и геометрия, над которой вы пытаетесь выполнить gluUnProject. Как только я понял это, алгоритм выбора лучей работал отлично. На iPad он может выполнять пересечение треугольников с лучами на 15 000 треугольников за 19 мс, что соответствует примерно 800 000 пересечений в секунду. Не так уж плохо для iPad, хотя я уверен, что производительность можно улучшить, предварительно вычислив уравнения треугольной плоскости. Спасибо за предложения, они помогли мне понять, что происходит, и я смог это исправить.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...