Использование gluUnProject() для рисования четырехугольника в позиции курсора

Я использую SDL с OpenGL и до сих пор все делал в 2D (используя glOrtho())

Теперь я хочу попробовать рисовать вещи в 3D, но, конечно, поскольку я теперь добавил в смесь 3-е измерение, все становится сложнее.

Что я хочу сделать, так это взять координаты экрана курсора, перевести их в (?) мировые координаты (координаты, которые я могу использовать в OpenGL, так как теперь у меня есть перспектива и т. д.), и нарисовать четырехугольник в эта позиция (курсор мыши находится в центре четырехугольника)

Я прочитал несколько руководств по gluUnProject() и примерно понимаю, что я должен делать, но, поскольку я учусь сам, очень легко запутаться и не понять, что я делаю.

Таким образом, я в основном скопировал примеры, которые нашел.

Ниже приведен мой код (я убрал проверку ошибок и т. д., пытаясь немного его сократить)

Как видите, у меня есть переменные mouseX, mouseY и mouseZ, значения которых mouseX и mouseY получают из SDL.

Я пытался использовать glReadPixel() для получения mouseZ, но я не уверен, что делаю это правильно.

Проблема в том, что когда я нажимаю, четырехугольник не рисуется в правильном положении (я думаю, отчасти из-за того, что я не знаю, как правильно получить mouseZ, поэтому я просто заменил его в gluUnProject() на 1.0?)

Я использую новые координаты (wx, wy, wz) в функции glTranslatef(), но еще раз, потому что я не знаю, как правильно получить mouseZ, я предполагаю, что это причина, по которой она не работает должным образом. Если я заменю wz на -499, все будет работать нормально, но мне нужно знать, как получить точные результаты без ручного поиска правильных (или почти правильных) чисел.

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

#include <SDL/SDL.h>
#include <SDL/SDL_opengl.h>

SDL_Surface* screen = 0;
SDL_Event event;
bool isRunning = true;

int mouseX, mouseY, mouseZ = 0;

GLint viewport[4];
GLdouble mvmatrix[16], projmatrix[16];
GLint realY; /* OpenGL y coordinate position */
GLdouble wx, wy, wz; /* returned world x, y, z coords */

int main(int argc, char **argv) {
    SDL_Init(SDL_INIT_EVERYTHING);

    screen = SDL_SetVideoMode(800, 600, 32, SDL_OPENGL);

    glEnable(GL_DEPTH_TEST);
    glDepthMask(GL_TRUE);

    glViewport(0, 0, 800, 600);

    glClearDepth(1.f);
    glClearColor(0, 0, 0, 0);

    glMatrixMode(GL_PROJECTION);
    glLoadIdentity();

    gluPerspective(90.f, 1.f, 1.f, 500.f);

    glMatrixMode(GL_MODELVIEW);
    glLoadIdentity();

    while(isRunning) {
        while(SDL_PollEvent(&event)) {
            if(event.type == SDL_QUIT) {
                isRunning = false;
            }

            if(event.type == SDL_MOUSEBUTTONDOWN) {
                if(event.button.button == SDL_BUTTON_LEFT) {
                    SDL_GetMouseState(&mouseX, &mouseY);

                    glGetIntegerv(GL_VIEWPORT, viewport);
                    glGetDoublev(GL_MODELVIEW_MATRIX, mvmatrix);
                    glGetDoublev(GL_PROJECTION_MATRIX, projmatrix);
                    realY = viewport[3] - (GLint) mouseY - 1;
                    glReadPixels(mouseX, realY, 1, 1, GL_DEPTH_COMPONENT, GL_FLOAT, &mouseZ);

                    gluUnProject((GLdouble)mouseX, (GLdouble)realY, 1.0, mvmatrix, projmatrix, viewport, &wx, &wy, &wz);
                }
            }
        }

        glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);

        glPushMatrix();

        glTranslatef(wx, wy, wz);

        glBegin(GL_QUADS);
            glColor4f(1.f, 0.f, 0.f, 1.f);
            glVertex3f(-20.f, -20.f, 0.f);

            glColor4f(0.f, 1.f, 0.f, 1.f);
            glVertex3f(-20.f, 20.f, 0.f);

            glColor4f(0.f, 0.f, 1.f, 1.f);
            glVertex3f(20.f, 20.f, 0.f);

            glColor4f(1.f, 0.f, 1.f, 1.f);
            glVertex3f(20.f, -20.f, 0.f);
        glEnd();

        glPopMatrix();

        SDL_GL_SwapBuffers();
    }

    SDL_FreeSurface(screen);

    return 0;
}

person Lucas    schedule 09.11.2010    source источник


Ответы (2)


Я обнаружил, что если добавить следующее:

GLfloat depth[2];

затем изменил glReadPixels() с:

glReadPixels(mouseX, realY, 1, 1, GL_DEPTH_COMPONENT, GL_FLOAT, &mouseZ);

to:

glReadPixels(mouseX, realY, 1, 1, GL_DEPTH_COMPONENT, GL_FLOAT, depth);

затем я просто изменил gluUnProject() с:

gluUnProject((GLdouble)mouseX, (GLdouble)realY, mouseZ, mvmatrix, projmatrix, viewport, &wx, &wy, &wz);

to:

gluUnProject((GLdouble) mouseX, (GLdouble) realY, depth[0], mvmatrix, projmatrix, viewport, &wx, &wy, &wz);

тогда я мог бы получить значение Z, которое хотел.

Затем я просто добавил небольшое значение к wz, например: wz += 1;, чтобы убедиться, что четырехугольник появился над тем местом, где я щелкнул, и теперь он работает так, как я предполагал.

person Lucas    schedule 10.11.2010

Чтобы угадать Z, вам нужно иметь значение для этого z :

Обычно это означает, что вы хотите пересечь линию, соответствующую (началу камеры, пикселю), с отображаемым 3D-объектом. Для этого можно использовать Z-буфер.

Если нет (вы хотите прикрепить к курсору многоугольник без 3D-отношения), просто используйте 2D-функции, вы можете просто переключаться между 3D- и 2D-проекциями при рисовании в одном и том же кадре (либо с матрицей проекции, либо со специфическими функциями 2D). ), поэтому вам не нужен z.

Установка значения по умолчанию для Z — плохая идея, если в вашем дизайне это не обосновано.

Существует только одно значение Z, которое даст вам правильные координаты пикселей.

Связь между 2D- и 3D-координатами в openGL заключается в том, что OpenGl использует плоскость проекции при Z = 1. А затем масштабирует координаты пикселей от [-1,1] до координат пикселей.

person fa.    schedule 09.11.2010
comment
Итак, просто чтобы проверить мое понимание, если бы я хотел найти правильное значение Z, мне сначала нужна была бы какая-то поверхность, на которую я хочу поместить четырехугольник? - person Lucas; 09.11.2010
comment
для первого варианта да, если вы хотите, чтобы этот квадроцикл жил в трехмерном пространстве. если у вас нет объекта, как кажется, вы можете использовать плоскость проекции, ортогональную направлению камеры. Это то, что вы делаете при использовании фиксированного Z, как вы сказали. Но вы должны найти правильный Z для соответствия координатам пикселей. Это зависит от вашей проекционной матрицы. Поэтому я думаю, что проще просто переключить матрицу проекции на 2D, чтобы нарисовать свой квад. - person fa.; 09.11.2010