На главную
ПРАВИЛА FAQ Помощь Участники Календарь Избранное DigiMania RSS
msm.ru
! Правила раздела "Программирование графики"
1) Данный раздел предназначен для обсуждения проблем, возникающих при программировании задач, связанных с чтением, сохранением, обработкой, созданием, отрисовкой графической информации (в том числе - 3D [OpenGL, Direct3D] и анимации [в т.ч. VFW, DirectShow, OpenDML]).
Флэш обсуждают здесь!.

2) Если вы хотите получить совет для конкретной платформы/языка программирования, обязательно укажите их в вопросе.

3) Уважаемые новички! Мы приветствуем Ваше желание научить всех посетителей раздела правильному программированию. Но огромная просьба, перед тем, как писать поучения в старых (последний ответ - "старее" месяца, а особенно, если вопрошавший не появляется на форуме уже не первый месяц, в чем можно убедиться в его профиле) темах, хорошо подумать, будет ли кому-нибудь, кроме Вас cамих, это интересно.



Ваше мнение о модераторах: user posted imageBarazuk, user posted imageOpenGL, user posted imageMikle
Модераторы: OpenGL, barazuk, Mikle
  
> Правильная трансформация нормалей.
    Есть модель, состоящая из вертексов, и есть матрица преобразования этой модели. Необходимо преобразовать и зашить обратно в модель позиции и нормали.
    Задачу можно упростить:
    Предположим, что трансформация, задаваемая матрицей - это комбинация поворотов, скейлов и переносов, без перспективных искажений, то есть m14, m24 и m34 равны нулю.

    D3DXVec3TransformNormal не подходит, если матрица включает не только переносы и повороты, но и скейл. Нормализация нормалей после D3DXVec3TransformNormal - тоже не панацея, даже в результате чистого скейла нормали меняют направление (и должны менять, но не так, как меняют тут).
    Переносы от других преобразований отделяются легко, это m41, m42 и m43.
    Фактически D3DXVec3TransformNormal, умножая вектор на матрицу 3*3 (входящую в состав матрицы 4*4), уже игнорирует переносы, но эта матрица 3*3 содержит повороты и скейлы в смешанном виде, элементы m11, m22 и m33 зависят и от скейлов, и от поворотов.

    Я решил эту задачу, отказавшись от D3DXVec3TransformNormal:
    ExpandedWrap disabled
      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(Rnd, Rnd, Rnd) окажется сонаправленным с исходным. Вероятность мала, но есть.
    Как проще (без if-оф) получить случайный вектор, гарантированно не сонаправленный данному? Направленный в противоположную стороны тоже считается сонаправленным.
    Может есть вариант по-проще?
      Есть такой подход для генерации перпендикулярного вектора в 3D:

      Выбрать максимальный по амплитуде компонент из (dx, dy, dz). Пусть это будет dy. Записать на его место следующий в циклическом порядке, а сам компонент - на следующее место, изменив знак. Третий компонент обнулить.

      Т.е. результат будет (0, dz, -dy)
        Цитата MBo @
        Есть такой подход для генерации перпендикулярного вектора в 3D

        Длинно, два if-а, потом ещё определять понятие "следующий", что тоже без if-ов трудно представить.
        Я заменил:
        ExpandedWrap disabled
          V1 = Vec3(Rnd, Rnd, Rnd)

        на:
        ExpandedWrap disabled
          If Abs(Norm.x) < Abs(Norm.y) Then
            V1 = Vec3(1, 0, 0)
          Else
            V1 = Vec3(0, 1, 0)
          End If

        Наверное, это самый простой вариант.
        Но остаётся в силе вопрос - в целом я задачу решил оптимально, или можно проще?
        Сообщение отредактировано: Mikle -
          Цитата
          Нормали трансформируют транспонированной инверсной матрицей.

          Всего-то нужно было сделать так:
          ExpandedWrap disabled
            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
          Сообщение отредактировано: Mikle -
            Цитата MBo @
            Есть такой подход для генерации перпендикулярного вектора в 3D:
            Что-то не то. Вектор получается не нормальный к поверхности.
            Цитата Mikle @
            Всего-то нужно было сделать так:
            Похоже не так просто подсказку найти оказалось (4 часа прошло) Вообще-то матрица преобразования нормалей получается умножением почти тех же матриц, как и матрица преобразования координат, но выбрасываются сдвиги, и вместо масштабных используются их обратные (что насчёт перспективного преобразования, не помню).
            Так что на всякий случай проверь, всегда ли правильная нормаль получается.
            Всё написанное выше это всего лишь моё мнение, возможно ошибочное.
              >Что-то не то. Вектор получается не нормальный к поверхности.
              Это вектор, перпендикулярный данному
                Цитата amk @
                Вообще-то матрица преобразования нормалей получается умножением почти тех же матриц, как и матрица преобразования координат, но выбрасываются сдвиги, и вместо масштабных используются их обратные (что насчёт перспективного преобразования, не помню)

                Но для этого нужно иметь эти исходные трансформации по отдельности.
                Цитата amk @
                Похоже не так просто подсказку найти оказалось (4 часа прошло)

                Я не привык долго искать. То, что нашёл по первым трём ссылкам, касалось преобразования параметров источника света вместо нормалей модели, это всё правильно, но этим я пользовался и раньше, а сейчас не тот случай.
                Поэтому я по привычке сел и написал свой велосипед, и он таки оказался правильным, но не оптимальным.
                Цитата amk @
                Так что на всякий случай проверь, всегда ли правильная нормаль получается.

                В моём первом решении - всегда. В последнем результаты для всех видов трансформаций не проверял, но это общепринятый метод, который я в своё время просто умудрился пропустить.
                Сообщение отредактировано: Mikle -
                  Хм.. а как быть, если матрица содержит и перспективные искажения?
                  И преобразования транспонированной инверсной матрицей, и мой вариант дают одинаковый неверный результат.
                    С перспективными искажениями всё очень плохо. Лично я даже не знаю, куда там должны быть правильные нормали направлены (если те, что для расчёта освещения).
                    Всё написанное выше это всего лишь моё мнение, возможно ошибочное.
                      Цитата amk @
                      Лично я даже не знаю, куда там должны быть правильные нормали направлены (если те, что для расчёта освещения).

                      Тут, в общем, понятно. Любая нормаль подразумевает плоскость, к которой она и является нормалью. Ищем эту плоскость, трансформируем её нашей матрицей (в данном случае не аффинной), а к полученной плоскости снова ищем нормаль - это и есть искомая величина. Мой метод так и сделан, и я уже догадываюсь, почему он не работает, попробую исправить.

                      Добавлено
                      Готово!
                      Это уже не аффинное преобразование, трансформировать нормаль в отрыве от позиции было нельзя, вот так работает:
                      ExpandedWrap disabled
                        Public Sub TransformPosNorm(Pos As D3DVECTOR, Norm As D3DVECTOR, M As D3DMATRIX)
                          Dim V1 As D3DVECTOR
                          Dim V2 As D3DVECTOR
                         
                          If Abs(Norm.x) < Abs(Norm.y) Then
                            V1 = Vec3(1, 0, 0)
                          Else
                            V1 = Vec3(0, 1, 0)
                          End If
                          D3DXVec3Cross V1, V1, Norm
                          D3DXVec3Cross V2, V1, Norm
                          D3DXVec3Add V1, V1, Pos
                          D3DXVec3Add V2, V2, Pos
                          D3DXVec3TransformCoord Pos, Pos, M
                          D3DXVec3TransformCoord V1, V1, M
                          D3DXVec3TransformCoord V2, V2, M
                          D3DXVec3Subtract V1, V1, Pos
                          D3DXVec3Subtract V2, V2, Pos
                          D3DXVec3Cross Norm, V2, V1
                          D3DXVec3Normalize Norm, Norm
                        End Sub
                      1 пользователей читают эту тему (1 гостей и 0 скрытых пользователей)
                      0 пользователей:


                      Рейтинг@Mail.ru
                      [ Script Execution time: 0,1152 ]   [ 16 queries used ]   [ Generated: 23.07.17, 06:36 GMT ]