Асимметричный усеченный конус OpenGL для настольной виртуальной реальности

Я делаю приложение OpenGL С++, которое отслеживает местоположение пользователей по отношению к экрану, а затем обновляет отображаемую сцену с точки зрения пользователя. Это известно как «настольная виртуальная реальность», или вы можете думать об экране как о диораме или аквариуме. Я новичок в OpenGL и до сих пор определил только очень простую сцену, просто куб, и изначально он отображается правильно.

Проблема в том, что когда я начинаю двигаться и хочу перерендерить кубическую сцену, плоскость проекции кажется переведенной, и я не вижу того, что, по моему мнению, должен. Я хочу, чтобы этот самолет починили. Если бы я писал трассировщик лучей, мое окно всегда было бы фиксированным, но мой глаз мог блуждать. Может кто-нибудь объяснить мне, как я могу добиться желаемого эффекта (закрепить окно просмотра), когда моя камера/глаз блуждают по координате, отличной от исходной?

Все примеры, которые я нахожу, требуют, чтобы камера/глаз находились в начале координат, но это концептуально неудобно для меня. Кроме того, поскольку это «аквариум», я устанавливаю d_near на плоскость xy, где z = 0.

В пространстве экрана/мира я назначаю центр экрана на (0,0,0) и его 4 угла на: TL(-44.25, 25, 0) TR( 44.25, 25, 0) BR( 44.25,-25 , 0) BL(-44,25,-25, 0) Эти значения указаны в сантиметрах для дисплея 16x9.

Затем я рассчитываю глаз пользователя (фактически веб-камеру на моем лице), используя POSIT, чтобы он обычно находился где-то в диапазоне (+/-40, +/-40, 40-250). Мой метод POSIT точен.

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

Я инициализирую следующим образом:

float right = 44.25;
float left = -44.25;
float top = 25.00;
float bottom = -25.00; 

vec3 eye = vec3(0.0, 0.0, 100.0);
vec3 view_dir = vec3(0.0, 0.0, -1.0);
vec3 up = vec3(0.0, 1.0, 0.0);
vec3 n = normalize(-view_dir);
vec3 u = normalize(cross(up, n)); 
vec3 v = normalize(cross(n, u));

float d_x = -(dot(eye, u));
float d_y = -(dot(eye, v));
float d_z = -(dot(eye, n));

float d_near = eye.z;
float d_far = d_near + 50;

// perspective transform matrix
mat4 P = mat4((2.0*d_near)/(right-left ), 0, (right+left)/(right-left), 0, 
            0, (2.0*d_near)/(top-bottom), (top+bottom)/(top-bottom), 0,
            0, 0, -(d_far+d_near)/(d_far-d_near), -(2.0*d_far*d_near)/(d_far-d_near),
            0, 0, -1.0, 0);

// viewing transform matrix
mat4 V = mat4(u.x, u.y, u.z, d_x,
              v.x, v.y, v.z, d_y,
              n.x, n.y, n.z, d_z,
              0.0, 0.0, 0.0, 1.0);

mat4 MV = C * V;
//MV = V;

Судя по тому, что я нашел в Интернете, мой view_dir и выше должны оставаться фиксированными. Это означает, что мне нужно обновить только d_near и d_far, а также d_x, d_y и d_y? Я делаю это в своем glutIdleFunc(idle);

void idle (void) {  

    hBuffer->getFrame(hFrame);
    if (hFrame->goodH && hFrame->timeStamp != timeStamp) {
        timeStamp = hFrame->timeStamp;
        std::cout << "(" << hFrame->eye.x << ", " <<
                    hFrame->eye.y << ", " <<
                    hFrame->eye.z << ") \n";

        eye = vec3(hFrame->eye.x, hFrame->eye.y, hFrame->eye.z);

        d_near = eye.z;
        d_far = eye.z + 50;

        P = mat4((2.0*d_near)/(right-left), 0, (right+left)/(right-left), 0, 
                 0, (2.0*d_near)/(top-bottom), (top+bottom)/(top-bottom), 0,
                 0, 0, -(d_far+d_near)/(d_far-d_near), -(2.0*d_far*d_near)/(d_far-d_near),
                 0, 0, -1.0, 0);

        d_x = -(dot(eye, u));
        d_y = -(dot(eye, v));
        d_z = -(dot(eye, n));

        C = mat4(1.0, 0.0, 0.0, eye.x,
                 0.0, 1.0, 0.0, eye.y,
                 0.0, 0.0, 1.0, 0.0,
                 0.0, 0.0, 0.0, 1.0);

        V = mat4(u.x, u.y, u.z, d_x,
                 v.x, v.y, v.z, d_y,
                 n.x, n.y, n.z, d_z,
                 0.0, 0.0, 0.0, 1.0);

        MV = C * V;
        //MV = V;

        glutPostRedisplay();
    }
}

Вот мой код шейдера:

#version 150

uniform mat4 MV;
uniform mat4 P;
in vec4 vPosition;
in vec4 vColor;
out vec4 color;

void 
main() 
{ 
    gl_Position = P * MV * vPosition;
    color = vColor;
}

Хорошо, я внес некоторые изменения в свой код, но безуспешно. Когда я использую V вместо MV в вершинном шейдере, все выглядит так, как я хочу, перспектива правильная и объекты имеют нужный размер, однако сцена транслируется смещением камеры.

При использовании C и V для получения MV моя сцена визуализируется с точки зрения наблюдателя прямо, и визуализированная сцена заполняет окно, как и должно, но перспектива глаза/камеры теряется.

На самом деле, я хочу перевести 2D-пиксели, плоскость проекции, на соответствующие значения x и y глаза/камеры, чтобы сохранить центр объекта (чей центр xy равен (0,0)) в центр визуализируемого изображения. Я руководствуюсь примерами из учебника "Интерактивная компьютерная графика: нисходящий подход с использованием шейдеров OpenGL (6-е издание)". Используя файлы в паре с книгой, свободно доступные в Интернете, я продолжаю использовать подход «основной ряд».

Следующие изображения сделаны, когда не используется матрица C для создания MV. Когда я использую C для создания MV, все сцены выглядят как на первом изображении ниже. Я не хочу перевода в z, поэтому я оставляю это значение равным 0.

Поскольку плоскость проекции и плоскость моей камеры параллельны, преобразование из одной системы координат в другую представляет собой просто сдвиг и inv(T) ~ -T.

Вот мое изображение для глаза в (0,0,50): Вот мое изображение для глаза в (0  ,0,50):

Вот мое изображение для глаза в (56,-16,50): Вот мое изображение для глаза в (  56,-16,50):


person Der Luftmensch    schedule 17.04.2013    source источник


Ответы (2)


Решение состоит в том, чтобы обновить d_x, d_y и d_z с учетом нового положения глаза, но никогда не изменять u, v или n. Кроме того, необходимо обновить матрицу P новыми значениями для левого, правого, верхнего и нижнего, поскольку они относятся к новому положению камеры/глаза.

Я инициализирую это:

float screen_right = 44.25;
float screen_left = -44.25;
float screen_top = 25.00;
float screen_bottom = -25.00; 
float screen_front = 0.0;
float screen_back = -150;

И теперь моя функция ожидания выглядит так, обратите внимание на вычисления для верхнего, нижнего, правого и левого:

void idle (void) {  

hBuffer->getFrame(&hFrame);
if (hFrame.goodH && hFrame.timeStamp != timeStamp) {
    timeStamp = hFrame.timeStamp;
    //std::cout << "(" << hFrame.eye.x << ", " <<
    //                  hFrame.eye.y << ", " <<
    //                  hFrame.eye.z << ") \n";

    eye = vec3(hFrame.eye.x, hFrame.eye.y, hFrame.eye.z);

    d_near = eye.z;
    d_far = eye.z + abs(screen_back) + 1;

    float top = screen_top - eye.y;
    float bottom = top - 2*screen_top;
    float right = screen_right - eye.x;
    float left = right - 2*screen_right;

    P = mat4((2.0 * d_near)/(right - left ), 0.0, (right + left)/(right - left), 0.0, 
             0.0, (2.0 * d_near)/(top - bottom), (top + bottom)/(top - bottom), 0.0,
             0.0, 0.0, -(d_far + d_near)/(d_far - d_near), -(2.0 * d_far * d_near)/(d_far - d_near),
             0.0, 0.0, -1.0, 0.0);

    d_x = -(dot(eye, u));
    d_y = -(dot(eye, v));
    d_z = -(dot(eye, n));

    V = mat4(u.x, u.y, u.z, d_x,
             v.x, v.y, v.z, d_y,
             n.x, n.y, n.z, d_z,
             0.0, 0.0, 0.0, 1.0);

    glutPostRedisplay();
}

}

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

введите здесь описание изображения

person Der Luftmensch    schedule 21.04.2013
comment
Несколько видео-ссылок на проект: фон и результат - person Der Luftmensch; 17.01.2015

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

Это не требование. OpenGL не определяет камеру, все сводится к преобразованиям. Их обычно называют проекцией и представлением модели (объединение преобразования модели и представления). Камера, т.е. преобразование вида, является просто обратным положением камеры в мировом пространстве. Итак, скажем, вы построили матрицу для своей камеры C, а затем предварительно умножили бы ее на обратную в представлении модели.

mat4 MV = inv(C) * V

С другой стороны, ваш код шейдера неверен:

gl_Position = P * ( (V * vPosition ) / vPosition.w );
                                    ^^^^^^^^^^^^^^

Однородное деление не должно выполняться в шейдере, так как оно встроено в конвейер рендеринга.

person datenwolf    schedule 18.04.2013
comment
@DerLuftmensch: В своем обновлении вы написали MV = C * V; Я предложил inv(C) * MV. В любом случае, не могли бы вы опубликовать несколько скриншотов? Также укажите, какую математическую библиотеку вы используете. Ваши матрицы, кажется, написаны в основном порядке строк, что немного необычно. - person datenwolf; 19.04.2013