2013-07-14 13 views
6

Chciałbym przechowywać dwie wartości zmiennoprzecinkowe w pojedynczej 32-bitowej zmiennej zmiennoprzecinkowej. Kodowanie będzie miało miejsce w języku C#, podczas gdy dekodowanie będzie wykonywane w module cieniującym HLSL.Przechowywanie dwóch wartości zmiennoprzecinkowych w jednej zmiennej zmiennoprzecinkowej

Najlepszym rozwiązaniem znalazłem tak daleko jest hard-okablowanie offset po przecinku w kodowanym wartości i przechowywanie ich jako liczba całkowita i dziesiętną „nośnik” float:

123.456 -> 12.3 and 45.6 

Może” t obsługuje ujemne wartości, ale to jest w porządku.

Jednak zastanawiałem się, czy jest lepszy sposób na zrobienie tego.

EDIT: Jeszcze kilka szczegółów na temat zadania:

pracuję z ustaloną strukturą danych w jedności, w której dane są przechowywane jako wierzchołek pływaków. (Float2 dla UV, float3 normalny, i tak dalej.) Najwyraźniej nie ma sposobu na poprawne dodawanie dodatkowych danych, więc muszę pracować w tych granicach, dlatego pomyślałem, że wszystko sprowadza się do bardziej ogólnego problemu kodowania danych . Na przykład mogłem poświęcić dodatkowe dane UV, aby przenieść dodatkowe kanały danych 2x2.

Celem jest model cieniowania 3.0, ale nie miałbym nic przeciwko, jeśli dekodowanie działa również na SM2.0.

Utrata danych jest w porządku, o ile jest "uzasadniona". Oczekiwany zakres wartości to 0..64, ale gdy przyjdę do tego wniosku, 0..1 również będzie w porządku, ponieważ jest on tani, aby zamienić go na dowolny zakres wewnątrz modułu cieniującego. Ważne jest, aby precyzja była jak najwyższa. Wartości ujemne nie są ważne.

+0

@ZoltanE: Czy znasz zakresy zmiennych, które próbujesz kodować? –

+0

@ZoltanE: Na który model shadera i wersja DirectX celujesz? Czy możesz podać więcej szczegółów o tym, dlaczego kodujesz w ten sposób? Wreszcie: Zastanów się nad pytaniem na stronie [gamedev] (http://gamedev.stackexchange.com/), możesz uzyskać lepsze odpowiedzi. –

+3

Może może Ci pomóc pytanie http://stackoverflow.com/questions/4811219/pack-four-bytes-in-a-float. – Gnietschow

Odpowiedz

2

Zgodnie z zaleceniem Gnietschowa zaadaptowałem algo z YellPika. (. Jest to C# dla 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); 
} 

Szybkie i brudne testowanie produkowane następujące statystyk (1 mln par losową wartość w przedziale 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 

Precyzja 4096 wydaje być słodkim miejscem. Zauważ, że zarówno pakowanie, jak i rozpakowywanie w tych testach odbywało się na procesorze, więc wyniki mogłyby być gorsze na GPU, gdyby przecinało rogi z precyzją pływaka.

W każdym razie, nie wiem, czy jest to najlepszy algorytm, ale wydaje się wystarczająco dobry dla mojej sprawy.

+0

Czy mógłbyś wyjaśnić, dlaczego część "input% precision" działa? Robisz moduł na float i int, ale modulus jest zdefiniowany tylko na liczbach całkowitych. – mrueg

+0

Definicja nie jest ograniczona do liczb całkowitych: "Operacja modulo znajduje pozostałą część podziału jednej liczby przez drugą". Warto zapytać "Jaka jest pozostałość, gdy dzielę 7,7 na 3,3?" – ZoltanE

+0

Uwaga: to zepsuje się, jeśli 'input.y' skończył się' 1'. W takim wypadku '+ output.y' dodaje tylko kolejny przyrost wartości' precyzji' - wtedy 'input% precision' jest w stanie rozpakować ułamkową część' input.y', a jej część całkowitą (podzieloną przez '' precyzja') jest dodawany do 'output.x'. – meetar

Powiązane problemy