Как восстановить нормаль из RG текстуры доказав теорему Пифагора
Статья описывает поведение нормали в TangentSpace!
Возможно, вы сталкивались с текстурами нормали представленными в двухканальном виде RG(XY). Она же BC5/Компрессия нормали. Что же стало с B(Z)?
Z компоненту действительно можно точно восстановить, если изначально текстура была составлена корректно. Основное правило текстуры нормали, это нормализация, длина вектора в каждой точке должна составлять единицу.
В отличие от X, Y компонент, которые, условно, представляют значения от -1 до 1, Z компонента всегда находится в диапазоне от 0 до 1. Область значений нормали в данном случае можно представить в виде полусферы, где все возможные значения являются ее поверхностью.
Почему это важно? Расположим XY вектор на его плоскости и проведем перпендикуляр по оси Z.
В данном случае, пересечение возможно в единственной точке на поверхности полусферы, осталось ее вычислить. На деле нам нужно найти ту самую высоту перпендикуляра и здесь мы заметим кое-что интересное, треугольники, получаемые при добавлении значения в новом пространстве, являются прямоугольными, т.к. перпендикулярны собственной оси.
Рассмотрим первый треугольник ABC, который мы получаем из XY компонент, где BC = X, а AB = Y. В нем образуется неизвестная гипотенуза AC, которую мы попытаемся вычислить.
- - - - -
Возможно вы помните со школьной программы теорему Пифагора "Квадрат гипотенузы равен сумме квадратов катетов", и будете правы, мы действительно можем найти результат по формуле:
Из чего следует, что:
Но почему? Есть множество визуальных примеров, демонстрирующих доказательство теоремы, мое любимое, это доказательство "Методом площадей".
Впишем в квадрат со сторонами a+b еще один квадрат со сторонами c, являющимися гипотенузой получившихся правильных треугольников abc.
Все точки вписанного квадрата равноудалены, нам так же известна сумма углов в точках касания т.к. треугольник abc прямоугольный, то сумма острых углов равна 90 градусам, а значит и углы внутри вписанной фигуры равны 180 - 90 = 90 градусам, из чего следует, что вписанная фигура является квадратом.
Если мысленно достроить прямоугольники со сторонами ab, то гипотенуза c аккурат делит их напополам, а значит, площадь треугольников равна ab/2, сложив имеющееся, получим площадь фигуры: 4(ab/2) + (cc) = 2ab + (cc).
Пересоберём имеющиеся куски внутри той же фигуры сохранив размер сторон a+b.
Заметим, что его площадь равна 2ab + (aa+bb), из чего следует, что (сс) = (aa+bb), теорема доказана.
- - - - -
Возвращаемся к нашему примеру, допустим мы нашли гипотенузу AC на плоскости XY, теперь нам нужно найти катет CD представляющий ту самую Z компоненту в треугольнике ACD.
Очень кстати, что нам известна длина гипотенузы AD, т.к. мы работаем с нормализованной величиной она всегда будет равна 1.
Соберем уравнение где:
соответственно:
Единица в квадрате равна единице, и так же известные нам X и Y значения перенесем к единице.
Осталось взять обе части в корень и в итоге получить Z компоненту.
Учитывая то, что берется корень, мы можем быть уверены, что полученное значение будет положительным и дополнительно брать модуль Z не нужно, однако, принято вписывать функцию в saturate() для контроля над возможными артефактами из-за неточности вычислений машиной.
- - - - -
Дополнительно воспользуемся трюком со скалярным произведением. Возьмем из нашего уравнения "xx - yy" в скобки и получим:
Интересно тут то, что xx + yy можно представить более производительной функцией dot(xy,xy). Так как результат скалярного произведения, равен сумме произведений компонент передаваемых векторов, мы можем дважды передать текущий вектор и получить V1xV1x+V1yV1y, где V1 это наш вектор XY.
Итого, функцией восстановления Z вектора станет:
Z = sqrt(saturate(1 - dot(XY,XY)))
- - - - -
P.S.
1.Почему бы просто не нормализовать вектор XY?
В таком случае изменится только длина вектора, но не его направление и нормаль продолжит находиться в плоскости XY, что некорректно.
2.Почему бы просто не нормализовать вектор XY где Z равно единице?
В этом случае вектор так же не будет корректен и возникнет смещение. Чем ближе оригинальное значение Z будет ближе к 0, тем сильнее будет смещение.
gamedev
3d
shaders
textures
compression
math