Войти
ПрограммированиеФорумГрафика

Правильная трансформация нормалей.

Страницы: 1 2 3 Следующая »
#0
19:49, 19 мар. 2017

Есть модель, состоящая из вертексов, и есть матрица преобразования этой модели. Необходимо преобразовать и зашить обратно в модель позиции и нормали.
Подход, обратный тому, который применяется при расчёте света, не годится - при расчёте света мы трансформируем не нормали, а позицию источника света. Когда работаем с направленным светом - это уже нормаль, но без позиции, с ней тоже всё просто.
Vec3TransformNormal не подходит, если матрица включает не только переносы и повороты, но и скейл. Нормализация нормалей после Vec3TransformNormal тоже не панацея, даже в результате чистого скейла нормали меняют направление (и должны менять, но не так, как меняют).
Я, вроде бы, решил этот вопрос, выглядит похоже на то, что нужно, но уж больно навороченно:

Sub TransformNorm(Norm As D3DVECTOR, M As D3DMATRIX)
  Dim MN As D3DMATRIX
  Dim M1 As D3DMATRIX

  D3DXMatrixInverse M1, 0, M
  MN.m11 = M1.m11
  MN.m22 = M1.m22
  MN.m33 = M1.m33
  MN.m44 = M1.m44
  D3DXMatrixMultiply MN, M, MN
  D3DXVec3TransformNormal Norm, Norm, MN
  D3DXVec3Normalize Norm, Norm
End Sub

Тут есть довольно тяжёлая штука - D3DXMatrixInverse, она включает среди прочего D3DXVec4Cross. Всё это довольно громоздко, особенно если перекладывать на шейдер.

Правка:
Полное решение в п.16

#1
20:07, 19 мар. 2017

Возможно, я не правильно понял вопрос, но старый добрый gl_NormalMatrix разве не то что нужно? - transpose(inverse(M))
А насчет производительности - не нужно считать матрицу в цикле для каждой вершины, она ведь одинаковая.

#2
7:37, 20 мар. 2017

Тоже интересует этот вопрос.
Ситуация 1 в 1

#3
9:52, 20 мар. 2017

Проверил, вариант в п.0 тоже неправильный.
Задачу можно упростить:
Предположим, что трансформация, задаваемая матрицей - это комбинация поворотов, скейлов и переносов, без перспективных искажений, то есть m14, m24 и m34 равны нулю.
Переносы от других преобразований отделяются легко, это m41, m42 и m43.
Фактически D3DXVec3TransformNormal, умножая вектор на матрицу 3*3, уже игнорирует переносы, но эта матрица 3*3 содержит повороты и скейлы в смешанном виде, элементы m11, m22 и m33 зависят и от скейлов, и от поворотов.
Вопрос: как отделить повороты от скейлов?

#4
10:41, 20 мар. 2017

Нашёл!

Public Sub Transform(Norm As D3DVECTOR, M As D3DMATRIX)
  Dim V0 As D3DVECTOR
  Dim V1 As D3DVECTOR
  Dim V2 As D3DVECTOR

  V1 = Vec3(Rnd, Rnd, Rnd)
  D3DXVec3Cross V1, V1, Norm
  D3DXVec3Cross V2, V1, Norm
  D3DXVec3TransformCoord V0, Vec3(0, 0, 0), M
  D3DXVec3TransformCoord V1, V1, M
  D3DXVec3TransformCoord V2, V2, M
  D3DXVec3Subtract V1, V1, V0
  D3DXVec3Subtract V2, V2, V0
  D3DXVec3Cross Norm, V2, V1
  D3DXVec3Normalize Norm, Norm
End Sub

Длинновато, но работает правильно, единственное - есть опасность, что случайный вектор Vec3(Rand, Rand, Rand) окажется сонаправленным с исходным. Вероятность мала, но есть.
Как проще (без if-оф) получить случайный вектор, гарантированно не сонаправленный данному? Направленный в противоположную стороны тоже считается сонаправленным.

#5
11:43, 20 мар. 2017

Я заменил:

V1 = Vec3(Rnd, Rnd, Rnd)
на:
If Abs(Norm.x) < Abs(Norm.y) Then
  V1 = Vec3(1, 0, 0)
Else
  V1 = Vec3(0, 1, 0)
End If
Наверное, это самый простой вариант.
Но остаётся в силе вопрос - в целом я задачу решил оптимально, или можно проще?

#6
12:26, 20 мар. 2017

не понятно зачем ты хранишь трансформацию объекта в виде матрицы 4x4
создай структуру в которой будут храниться  вектор перемещения, вектор масштабирования, и всегда нормированная матрица 3x3 для поворота (или кватернион)
многие операции упростятся в такой форме, например существенно упростится нахождение обратной трансформации
матрицу 4x4 создавай вы последний момент при передаче в шейдер, можешь создавать и другие вспомогательные матрицы, содержащие только поворот без масштаба

#7
12:55, 20 мар. 2017

Mikle
> Вопрос: как отделить повороты от скейлов?
Вот недавно как раз в ассимпе видел функцию

template <typename TReal>
inline void aiMatrix4x4t<TReal>::Decompose (aiVector3t<TReal>& scaling, aiQuaterniont<TReal>& rotation,
    aiVector3t<TReal>& position) const
{
    const aiMatrix4x4t<TReal>& _this = *this;

    // extract translation
    position.x = _this[0][3];
    position.y = _this[1][3];
    position.z = _this[2][3];

    // extract the rows of the matrix
    aiVector3t<TReal> vRows[3] = {
        aiVector3t<TReal>(_this[0][0],_this[1][0],_this[2][0]),
        aiVector3t<TReal>(_this[0][1],_this[1][1],_this[2][1]),
        aiVector3t<TReal>(_this[0][2],_this[1][2],_this[2][2])
    };

    // extract the scaling factors
    scaling.x = vRows[0].Length();
    scaling.y = vRows[1].Length();
    scaling.z = vRows[2].Length();

    // and the sign of the scaling
    if (Determinant() < 0) {
        scaling.x = -scaling.x;
        scaling.y = -scaling.y;
        scaling.z = -scaling.z;
    }

    // and remove all scaling from the matrix
    if(scaling.x)
    {
        vRows[0] /= scaling.x;
    }
    if(scaling.y)
    {
        vRows[1] /= scaling.y;
    }
    if(scaling.z)
    {
        vRows[2] /= scaling.z;
    }

    // build a 3x3 rotation matrix
    aiMatrix3x3t<TReal> m(vRows[0].x,vRows[1].x,vRows[2].x,
        vRows[0].y,vRows[1].y,vRows[2].y,
        vRows[0].z,vRows[1].z,vRows[2].z);

    // and generate the rotation quaternion from it
    rotation = aiQuaterniont<TReal>(m);
}
#8
13:32, 20 мар. 2017

Mikle
Хардкорно както.
Я кстати пробовал как писали инверсную транспонированную, чето не вышло.

#9
13:45, 20 мар. 2017

Mikle
> Вопрос: как отделить повороты от скейлов?
Сингулярное разложение. Все остальные алгоритмы работают только в частных случаях.

Но для трансформации нормалей это все нафиг нужно, тебе же уже написали, что нормали преобразуются транспонированной инверсной матрицей.
Не обязательно инвертировать финальную матрицу, можно отдельно инвертировать каждый множитель.

#10
13:46, 20 мар. 2017

Mira
> Хардкорно както.
Есть такое, но работает правильно. А то, что дал AMM1AK, похоже на мой первый вариант (на задумку), только я там где-то налажал, и оно не менее хардкорно, а ведь это ещё не трансформация нормали, а разложение трансформации на составляющие.
ashujon
> не понятно зачем ты хранишь трансформацию объекта в виде матрицы 4x4
Унификация. Всего одна сущность (матрица) задаёт все преобразования.

#11
14:00, 20 мар. 2017

}:+()___ [Smile]
> Не обязательно инвертировать финальную матрицу, можно отдельно инвертировать
> каждый множитель.
У меня есть как раз финальная матрица, будь скейл, вращение и перемещение отдельно, это бы решил.
}:+()___ [Smile]
> нормали преобразуются транспонированной инверсной матрицей.
Сейчас попробую.

#12
14:01, 20 мар. 2017

Mikle
Надо попробовать.  По сути главное для нормалей разово посчитать нужную матрицу и потом множить позиции на одну мптрицу а нормали на другую.  Тоесть издержки незаметны

#13
14:13, 20 мар. 2017

}:+()___ [Smile]
Сработало. Надо будет ещё проверить на быстродействие, а то MatrixInverse - штука тяжёлая.

#14
18:48, 20 мар. 2017

Mira
> Я кстати пробовал как писали инверсную транспонированную, чето не вышло.
Это работает:

Public Sub Transform(Norm As D3DVECTOR, M As D3DMATRIX)
  Dim M1 As D3DMATRIX
 
  D3DXMatrixInverse M1, M
  D3DXMatrixTranspose M1, M1
  D3DXVec3TransformNormal Norm, Norm, M
  D3DXVec3Normalize Norm, Norm
End Sub
}:+()___ [Smile]
> тебе же уже написали, что нормали преобразуются транспонированной инверсной матрицей.
Когда так пишут: "чето не вышло", как-то и мысли не возникло попробовать.
CapSopener
Теперь понял, что и ты всё верно посоветовал, я в GL не разбираюсь, сразу не въехал.
Страницы: 1 2 3 Следующая »
ПрограммированиеФорумГрафика

Тема в архиве.