Я делаю приложение OpenGL c ++, которое отслеживает местоположение пользователей по отношению к экрану, а затем обновляет визуализированную сцену с точки зрения пользователя. Это известно как «настольный VR» или вы можете думать об экране как о диораме или аквариуме. Я довольно новичок в 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)
Эти значения указаны в сантиметрах для дисплея 16×9.
Затем я вычисляю глаз пользователя (на самом деле веб-камеру на моем лице), используя 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 (в режиме ожидания);
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):
Вот мое изображение для глаза в (56, -16,50):
Решение состоит в том, чтобы обновить 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;
И теперь моя бездействующая функция выглядит следующим образом, обратите внимание на вычисления для top, bottom, right и left:
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();
}
}
Это переопределение матрицы перспективы удерживает отрендеренное изображение от перевода. У меня по-прежнему возникают проблемы с захватом камеры и захватом экрана, но я могу создавать изображения в режиме реального времени для пользователя:
Все примеры, которые я нахожу, требуют, чтобы камера / глаз были в начале координат.
Это не требование. OpenGL не определяет камеру, все сводится к преобразованиям. Их обычно называют проекцией и представлением модели (преобразование модели и вида вместе). Камера, то есть преобразование вида, является просто обратной позицией камеры в мировом пространстве. Допустим, вы создали матрицу для своей камеры. C
, тогда вы бы умножили обратное на вид модели.
mat4 MV = inv(C) * V
Кстати, ваш код шейдера неверен:
gl_Position = P * ( (V * vPosition ) / vPosition.w );
^^^^^^^^^^^^^^
Гомогенное разделение не быть сделано в шейдере, так как он встроен в конвейер рендеринга.