2013-07-14 4 views
6

Я хотел бы сохранить два значения float в одной 32-битовой переменной float. Кодировка произойдет в C#, в то время как декодирование должно выполняться в шейдере HLSL.Сохранение двух значений float в одной переменной float

Лучшее решение, которое я нашел до сих пор трудно проводкой смещение в дробной кодированных значений и сохранение их в виде целого числа и десятичной части «носителя» поплавка:

123.456 -> 12.3 and 45.6 

Это может» t обрабатывает отрицательные значения, но это нормально.

Однако мне было интересно, есть ли лучший способ сделать это.

EDIT: Несколько более подробно о задаче:

я работаю с фиксированной структурой данных в Unity, где данные вершин хранятся как поплавки. (Float2 для UV, float3 - нормальный и т. Д.). По-видимому, нет возможности правильно добавлять дополнительные данные, поэтому я должен работать в этих пределах, поэтому я решил, что все это связано с более общей проблемой кодирования данных , Например, я могу пожертвовать вторичными УФ-данными для передачи 2х2 дополнительных каналов данных.

Цель - шейдерная модель 3.0, но я бы не прочь, если бы декодирование работало разумно и на SM2.0.

Потеря данных в порядке, если она «разумна». Ожидаемый диапазон значений равен 0..64, но, как я думаю, 0..1 тоже будет хорошо, так как это дешево для переназначения в любой диапазон внутри шейдера. Важно держать точность как можно выше. Отрицательные значения не важны.

+0

@ZoltanE: Вы знаете диапазоны переменных, которые вы пытаетесь кодировать? –

+0

@ZoltanE: Какую модель шейдеров и версию DirectX вы нацеливаете? Можете ли вы дать более подробную информацию о том, почему вы кодируете это? Наконец: рассмотрите вопрос о сайте [gamedev] (http://gamedev.stackexchange.com/), вы можете получить более качественные ответы. –

+3

Возможно, вам может помочь вопрос http://stackoverflow.com/questions/4811219/pack-four-bytes-in-a-float. – Gnietschow

ответ

2

Следуя рекомендации Гнейцхова, я адаптировал алгоритм YellPika. (. Это C# для Unity 3d)

float Pack(Vector2 input, int precision) 
{ 
    Vector2 output = input; 
    output.x = Mathf.Floor(output.x * (precision - 1)); 
    output.y = Mathf.Floor(output.y * (precision - 1)); 

    return (output.x * precision) + output.y; 
} 

Vector2 Unpack(float input, int precision) 
{ 
    Vector2 output = Vector2.zero; 

    output.y = input % precision; 
    output.x = Mathf.Floor(input/precision); 

    return output/(precision - 1); 
} 

Быстрых и грязные испытания было получены следующей статистика (1 миллиона пара случайного значения в диапазоне 0-1):

Precision: 2048 | Avg error: 0.00024424 | Max error: 0.00048852 
Precision: 4096 | Avg error: 0.00012208 | Max error: 0.00024417 
Precision: 8192 | Avg error: 0.00011035 | Max error: 0.99999940 

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

В любом случае, я не знаю, является ли это лучшим алгоритмом, но он кажется достаточно хорошим для моего дела.

+0

Не могли бы вы объяснить, почему работает функция 'input% precision'? Вы выполняете модуль на float и int, но модуль определяется только целыми числами. – mrueg

+0

Определение не ограничивается целыми числами: «Операция по модулю находит остальную часть деления одного числа на другое». Имеет смысл спросить: «Что такое остаток, когда я делю 7,7 на 3,3?» – ZoltanE

+0

Примечание: это прерывается, если 'input.y' превышает' 1'. В этом случае '+ output.y' просто добавляет еще один прирост значения' precision' - тогда 'input% precision' может только распаковать дробную часть' input.y', а ее целая часть (деленная на ' precision') добавляется в 'output.x'. – meetar

Смежные вопросы