2013-07-14 15 views
6

Vorrei memorizzare due valori float in una singola variabile float a 32 bit. La codifica avverrà in C# mentre la decodifica deve essere eseguita in uno shader HLSL.Memorizzazione di due valori float in una variabile float singola

La soluzione migliore che ho trovato finora è hard-cablaggio l'offset del decimale nei valori codificati e la loro memorizzazione come intero e decimale del "carrier" float:

123.456 -> 12.3 and 45.6 

Può' t gestire valori negativi ma va bene.

Tuttavia mi stavo chiedendo se c'è un modo migliore per farlo.

EDIT: qualche dettaglio in più circa l'operazione:

sto lavorando con una struttura di dati fissa in Unity in cui i dati vengono memorizzati come vertici galleggianti. (Float2 per un UV, float3 il normale e così via.) Apparentemente non c'è modo di aggiungere dati extra in modo appropriato, quindi devo lavorare entro questi limiti, ecco perché ho pensato che fosse tutto un problema più generale dei dati di codifica . Per esempio potrei sacrificare i dati UV secondari per trasferire i canali dati extra 2x2.

L'obiettivo è shader modello 3.0 ma non mi dispiacerebbe se la decodifica funzionasse anche su SM2.0.

La perdita di dati va bene fintanto che è "ragionevole". L'intervallo di valori atteso è 0.60, ma, a mio avviso, lo 0..1 potrebbe anche andare bene dal momento che è economico rimappare in qualsiasi intervallo all'interno dello shader. L'importante è mantenere la precisione il più in alto possibile. I valori negativi non sono importanti.

+0

@ZoltanE: conosci gli intervalli delle variabili che stai tentando di codificare? –

+0

@ZoltanE: anche quale modello di shader e versione di DirectX stai prendendo di mira? Puoi dare maggiori dettagli sul perché stai codificando in questo modo? Infine: considera la possibilità di chiedere informazioni sul sito [gamedev] (http://gamedev.stackexchange.com/), potresti ottenere risposte migliori. –

+3

Forse la domanda http://stackoverflow.com/questions/4811219/pack-four-bytes-in-a-float potrebbe aiutarti. – Gnietschow

risposta

2

Seguendo la raccomandazione di Gnietschow ho adattato l'algo di YellPika. (. E 'C# per l'Unità 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); 
} 

Il test rapido e sporco ha prodotto i seguenti statistiche (1 milione di coppie di valori casuali compresi nell'intervallo tra 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 

precisione del 4096 sembra essere il punto debole. Si noti che sia l'imballaggio che lo spacchettamento in questi test hanno funzionato sulla CPU, quindi i risultati potrebbero essere peggiori su una GPU se tagliano gli angoli con precisione flottante.

In ogni caso, non so se questo è il miglior algoritmo ma sembra abbastanza buono per il mio caso.

+0

Ti dispiacerebbe spiegare perché funziona la parte 'input% precision'? Stai facendo un modulo su un float e un int, ma il modulo è definito solo sui numeri interi. – mrueg

+0

La definizione non è limitata ai numeri interi: "L'operazione modulo trova il resto della divisione di un numero con un altro". Ha senso chiedere "Qual è il resto quando divido 7.7 per 3.3?" – ZoltanE

+0

Nota: questo si interrompe se 'input.y' è sopra' 1'. In questo caso '+ output.y' aggiunge solo un altro incremento del valore di' precision' - quindi 'input% precision' è solo in grado di decomprimere la parte frazionaria di' input.y', e la sua parte intera (divisa per ' precision') viene aggiunto a 'output.x'. – meetar