Как решить проблему с матрицей lookat на OpenGL / GLSL - PullRequest
0 голосов
/ 13 февраля 2019

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

template<typename Type>
void setLookAt(Matrix4x4<Type>& matrix, const Vector3<Type> eye, const Vector3<Type> center, const Vector3<Type> up) noexcept
{
    Math::Vector3f right = Math::cross(center, up).normalize();
    Matrix4x4f lookAt({
        right.getX(), right.getY(), right.getZ(), 0.0,
        up.getX(), up.getY(), up.getZ(), 0.0,
        center.getX(), center.getY(), center.getZ(), 0.0,
        0.0, 0.0, 0.0, 1.0
    });
    Matrix4x4f additionalMatrix({
        0.0, 0.0, 0.0, -(eye.getX()),
        0.0, 0.0, 0.0, -(eye.getY()),
        0.0, 0.0, 0.0, -(eye.getZ()),
        0.0, 0.0, 0.0, 1.0
    });
    lookAt.mul(additionalMatrix);
    matrix = lookAt;
}

template<typename Type>
void setPerspectiveMatrix(Matrix4x4<Type>& matrix, Type fov, Type aspect, Type znear, Type zfar) noexcept
{
    const Type yScale = static_cast<Type>(1.0 / tan(RADIANS_PER_DEGREE * fov / 2));
    const Type xScale = yScale / aspect;
    const Type difference = znear - zfar;
    matrix  = {
        xScale, 0, 0, 0,
        0, yScale, 0, 0,
        0, 0, (zfar + znear) / difference, 2 * zfar * znear / difference,
        0, 0, -1, 0
    };
}

Реализация умножения матриц:

// static const std::uint8_t ROW_SIZE = 4;
// static const std::uint8_t MATRIX_SIZE = ROW_SIZE * ROW_SIZE;
// static const std::uint8_t FIRST_ROW = 0;
// static const std::uint8_t SECOND_ROW = ROW_SIZE;
// static const std::uint8_t THIRD_ROW = ROW_SIZE + ROW_SIZE;
// static const std::uint8_t FOURTH_ROW = ROW_SIZE + ROW_SIZE + ROW_SIZE;

template<class Type>
void Matrix4x4<Type>::mul(const Matrix4x4& anotherMatrix) noexcept
{
    Type currentElements[MATRIX_SIZE];
    std::copy(std::begin(mElements), std::end(mElements), currentElements);
    const Type* otherElements = anotherMatrix.mElements;
    for (std::uint8_t i = 0; i < MATRIX_SIZE; i += ROW_SIZE) 
    {
        mElements[i] = currentElements[i] * otherElements[FIRST_ROW] +
            currentElements[i + 1] * otherElements[SECOND_ROW] +
            currentElements[i + 2] * otherElements[THIRD_ROW] +
            currentElements[i + 3] * otherElements[FOURTH_ROW];
        mElements[i + 1] = currentElements[i] * otherElements[FIRST_ROW + 1] +
            currentElements[i + 1] * otherElements[SECOND_ROW + 1] +
            currentElements[i + 2] * otherElements[THIRD_ROW + 1] +
            currentElements[i + 3] * otherElements[FOURTH_ROW + 1];
        mElements[i + 2] = currentElements[i] * otherElements[FIRST_ROW + 2] +
            currentElements[i + 1] * otherElements[SECOND_ROW + 2] +
            currentElements[i + 2] * otherElements[THIRD_ROW + 2] +
            currentElements[i + 3] * otherElements[FOURTH_ROW + 2];
        mElements[i + 3] = currentElements[i] * otherElements[FIRST_ROW + 3] +
            currentElements[i + 1] * otherElements[SECOND_ROW + 3] +
            currentElements[i + 2] * otherElements[THIRD_ROW + 3] +
            currentElements[i + 3] * otherElements[FOURTH_ROW + 3];
    }
}

Реализация кросс-продукта:

template<typename Type>
Math::Vector3<Type> cross(Vector3<Type> vector, Vector3<Type> anotherVector) noexcept
{
    const Type x = vector.getY()*anotherVector.getZ() - vector.getZ()*anotherVector.getY();
    const Type y = -(vector.getX()*anotherVector.getZ() - vector.getZ()*anotherVector.getX());
    const Type z = vector.getX()*anotherVector.getY() - vector.getY()*anotherVector.getX();
    return { x, y, z };
}

Использование:

// OpenGL

glUseProgram(mProgramID);
Matrix4x4f lookAt;
setLookAt(lookAt, { 0.0f, 0.0f, 3.0f }, { 0.0f, 0.0f, -1.0f }, { 0.0f, 1.0f, 0.0f });
glUniformMatrix4fv(glGetAttribLocation(mProgramID, "viewMatrix"), 1, GL_TRUE, lookAt);
Matrix4x4f projection;
setPerspectiveMatrix(projection, 45.0f, width / height, -0.1, 100.0f);
glUniformMatrix4fv(glGetAttribLocation(mProgramID, "projectionMatrix "), 1, GL_TRUE, projection);
// GLSL

layout (location = 0) in vec3 position;

uniform mat4 viewMatrix;
uniform mat4 projectionMatrix;

void main()
{
    gl_Position = projectionMatrix * viewMatrix * vec4(position, 1.0f);
}

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

1 Ответ

0 голосов
/ 22 февраля 2019

"projectionMatrix" и "viewMatrix" - однородные переменные.Унифицированное местоположение можно получить по glGetUniformLocation, а не glGetAttribLocation, что вернет индекс атрибута активного атрибута:

GLint projLoc = glGetUniformLocation( mProgramID, "projectionMatrix" );
GLint viewLoc = glGetUniformLocation( mProgramID, "viewMatrix" );

В перспективеПроекция Матрица проекции описывает отображение из трехмерных точек в мире, как они видны из камеры-обскуры, в двумерные точки видового экрана.
Координаты глазного пространства в усечении камеры (усеченная пирамида) сопоставляются скуб (нормализованные координаты устройства).
При перспективной проекции пространство вида (объем) определяется усеченным конусом (усеченной пирамидой), где вершина пирамиды является положением зрителя.Направление обзора (прямая видимости), а также ближнее и дальнее расстояние определяют плоскости, которые обрезали пирамиду до усеченного конуса (направление обзора - это вектор нормали этих плоскостей).
Это означает оба значения, расстояниедо ближней плоскости и расстояние до дальней плоскости должны быть положительными значениями:

Matrix4x4f lookAt;
setLookAt(lookAt, { 0.0f, 0.0f, 3.0f }, { 0.0f, 0.0f, -1.0f }, { 0.0f, 1.0f, 0.0f });
glUniformMatrix4fv(viewLoc, 1, GL_TRUE, lookAt);

Matrix4x4f projection;
setPerspectiveMatrix(projection, 45.0f, width / height, 0.1f, 100.0f); // 0.1f instead of -0.1f
glUniformMatrix4fv(projLoc, 1, GL_TRUE, projection);

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

Линия визирования оси Zявляется вектором с точки зрения eye до трагета center:

template<typename Type>
void setLookAt(Matrix4x4<Type>& matrix, const Vector3<Type> eye, const Vector3<Type> center, const Vector3<Type> up) noexcept
{
    Vector3f mz( { eye.getX()-center.getX(), eye.getY()-center.getY(), eye.getZ()-center.getZ() } );
    mz = mz.normalize();
    Vector3f my = up.normalize();
    Vector3f mx = cross(my, mz).normalize();

    Type tx = dot( mx, eye );
    Type ty = dot( my, eye );
    Type tz = -dot( mz, eye );

    matrix = {
        mx.getX(), mx.getY(), mx.getZ(), tx,
        my.getX(), my.getY(), my.getZ(), ty,
        mz.getX(), mz.getY(), mz.getZ(), tz,
        0.0,       0.0,       0.0,       1.0
    };
}

template<typename Type>
Vector3<Type> cross(Vector3<Type> vector, Vector3<Type> anotherVector) noexcept
{
    const Type x = vector.getY()*anotherVector.getZ() - vector.getZ()*anotherVector.getY();
    const Type y = -(vector.getX()*anotherVector.getZ() - vector.getZ()*anotherVector.getX());
    const Type z = vector.getX()*anotherVector.getY() - vector.getY()*anotherVector.getX();
    return { x, y, z };
}

template<typename Type>
Vector3<Type> Vector3<Type>::normalize(void) const
{ 
  Type len = std::sqrt(mV[0]*mV[0] + mV[1]*mV[1] + mV[2]*mV[2]);
  return { mV[0] / len, mV[1] / len, mV[2] / len }; 
}

template<typename Type>
Type dot(Vector3<Type> vector, Vector3<Type> anotherVector) noexcept
{
    Type ax = vector.getX(), ay = vector.getY(), az = vector.getZ();
    Type bx = anotherVector.getX(), by = anotherVector.getY(), bz = anotherVector.getZ();
    return ax*bx + ay*by + az*bz;
}

Матрица перспективной проекции может быть определена как frustum .
Расстояния left, right, bottom и top - это расстояния от центра вида до боковых граней усеченного конуса в ближней плоскости.near и far определяют расстояния до ближней и дальней плоскостей в усеченном контуре.

r = right, l = left, b = bottom, t = top, n = near, f = far

x              y              z                t
2*n/(r-l)      0              (r+l)/(r-l)      0
0              2*n/(t-b)      (t+b)/(t-b)      0
0              0             -(f+n)/(f-n)     -2*f*n/(f-n)
0              0             -1                0

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

x              y              z                t
1/(ta*a)       0              0                0
0              1/ta           0                0
0              0             -(f+n)/(f-n)     -2*f*n/(f-n)
0              0             -1                0

где:

a = w / h
ta = tan( fov_y / 2 );

2 * n / (r-l) = 1 / (ta * a)
2 * n / (t-b) = 1 / ta

Далее матрица проецирования переключается с правосторонней системы на левостороннюю, поскольку ось z повернута.

template<typename Type>
void setPerspectiveMatrix(Matrix4x4<Type>& matrix, Type fov, Type aspect, Type znear, Type zfar) noexcept
{
    const Type yScale = static_cast<Type>(1.0 / tan(RADIANS_PER_DEGREE * fov / 2));
    const Type xScale = yScale / aspect;
    const Type difference = zfar - znear;
    matrix  = {
        xScale, 0,       0,                            0,
        0,      yScale,  0,                            0,
        0,      0,      -(zfar + znear) / difference, -2 * zfar * znear / difference,
        0,      0,      -1,                            0
    };
}
...