|
Главная
Статьи
Ссылки
Скачать
Скриншоты
Юмор
Почитать
Tools
Проекты
Обо мне
Гостевая
Форум
|
Натолкнулся в ряде публикаций на использование интересного метода компактного хранения единичных векторов без их сильного искажения.
Пусть у нас есть единичные вектора, но без сильной корреляции между соседними. Например, это могут быть значения нормали в G-буфере, которые мы хотим хранить максимально компактно и без заметной потери точности. Наиболее очевидное решение - это сферические координаты, но у них есть две особых точки - полюса. Кроме того точность распределена сильно неравномерно - у экватора она явно оставляет желать лучшего.
Простой и красивый метод довольно часто встречается, причем в самых разных задачах, вплоть до расчета глобального освещения. Давайте вместо единичной сферы рассмотрим единичный октаэдр. Тогда каждой грани октаэдра будет соответствовать сегмент сферы. Спроектируем октаэдр на плоскость \(z=0\) - результат полностью ляжет в квадрат \([-1,1]^2\). Но при этом половине точек квадрата будет соответствовать две точки октаэдра, а половине - ни одной.
Возьмем четыре нижние грани октаэдра и отобразим их на свободные участки квадрата как показано на рисунке выше. В результате мы получаем взаимно-однозначное отображение октаэдра на квадрат, т.е. каждой точке квадрата соответствует ровно одна точка на октаэдре и, значит, ровно один единичный вектор.
При этом искажения равномерно распределены по всем направлениям и довольно невелики. Ниже приводится код на GLSL, реализующий данное отображение.
// Returns ±1
vec2 signNotZero ( in vec2 v )
{
return vec2 ( (v.x >= 0.0) ? +1.0 : -1.0, (v.y >= 0.0) ? +1.0 : -1.0 );
}
// Assume normalized input.
// Output is on [-1, 1] for each component.
vec2 toOct ( in vec3 v )
{
// Project the sphere onto the octahedron,
// and then onto the xy plane
vec2 p = v.xy * (1.0 / (abs(v.x) + abs(v.y) + abs(v.z)));
// Reflect the folds of the lower hemisphere
// over the diagonals
return (v.z <= 0.0) ? ((1.0 - abs(p.yx)) * signNotZero(p)) : p;
}
vec3 fromOct ( in vec2 e )
{
vec3 v = vec3 ( e.xy, 1.0 - abs ( e.x ) - abs ( e.y ) );
if (v.z < 0 )
v.xy = ( 1.0 - abs ( v.yx ) ) * signNotZero ( v.xy );
return normalize ( v );
}
Исходная статья с обзором ряда подобных методов - A Survey of Efficient Representations for Independent Unit Vectors.