2017-04-02 5 views
0

I habe 27 Kombinationen von 3 Werten von -1 bis 1 vom Typ:wie 27 vector3 in einen 0-256 Wert zu kodieren?

Vector3(0,0,0); 
Vector3(-1,0,0); 
Vector3(0,-1,0); 
Vector3(0,0,-1); 
Vector3(-1,-1,0); 
... up to 
Vector3(0,1,1); 
Vector3(1,1,1); 

Ich brauche sie zu und von einer 8-Bit-sbyte/Byte-Array zu konvertieren.

Eine Lösung ist die erste Ziffer zu sagen, von der 256 = X die zweite Ziffer ist Y und die dritte ist Z ...

so

Vector3(-1,1,1) becomes 022, 
    Vector3(1,-1,-1) becomes 200, 
    Vector3(1,0,1) becomes 212... 

Ich würde es vorziehen, es zu codieren in einer kompakteren Weise, vielleicht mit Bytes (worüber ich keine Ahnung habe), weil die obige Lösung viele Multiplikationen und runde Funktionen zum Decodieren verwendet, haben Sie bitte einige Vorschläge? Die andere Möglichkeit ist 27 zu schreiben, wenn die Bedingungen zum Schreiben der Vector3-Kombination in ein Array ineffizient erscheinen.

Dank Böse Tak für die Führung, änderte ich den Code ein bisschen 0-1 Werte auf das erste Bit hinzuzufügen, und es für unity3d anzupassen:

function Pack4(x:int,y:int,z:int,w:int):sbyte { 
var b: sbyte = 0; 

b |= (x + 1) << 6; 
b |= (y + 1) << 4; 
b |= (z + 1) << 2; 
b |= (w + 1); 
return b; 
} 

function unPack4(b:sbyte):Vector4 { 
var v : Vector4; 
v.x = ((b & 0xC0) >> 6) - 1;  //0xC0 == 1100 0000 
v.y = ((b & 0x30) >> 4) - 1; // 0x30 == 0011 0000 
v.z = ((b & 0xC) >> 2) - 1;  // 0xC == 0000 1100 
v.w = (b & 0x3) - 1;   // 0x3 == 0000 0011 
return v; 
} 

Antwort

1

Eine Möglichkeit ist, um die Komponente zu speichern, jedes Vektors in je 2 Bits eines Bytes.

Das Konvertieren eines Vektorkomponentenwerts in und aus der gespeicherten 2-Bit-Form ist so einfach wie das Addieren und Subtrahieren von eins.

-1 (1111 1111 as a signed byte) <-> 00 (in binary) 
0 (0000 0000 in binary)  <-> 01 (in binary) 
1 (0000 0001 in binary)  <-> 10 (in binary) 

Die gepackten 2-Bit-Werte können in einem Byte in beliebiger Reihenfolge Ihrer Präferenz gespeichert werden. Ich werde das folgende Format verwenden: 00XXYYZZ Dabei ist XX der konvertierte (gepackte) Wert der X-Komponente und so weiter. Die 0 am Start werden nicht verwendet.

ein Vektor dann in einem Byte gepackt werden, wie folgt: Form wie folgt wird

byte Pack(Vector3<int> vector) { 
    byte b = 0; 
    b |= (vector.x + 1) << 4; 
    b |= (vector.y + 1) << 2; 
    b |= (vector.z + 1); 
    return b; 
} 

Auspacken einen Vektors von seinem Byte:

Vector3<int> Unpack(byte b) { 
    Vector3<int> v = new Vector<int>(); 
    v.x = ((b & 0x30) >> 4) - 1; // 0x30 == 0011 0000 
    v.y = ((b & 0xC) >> 2) - 1;  // 0xC == 0000 1100 
    v.z = (b & 0x3) - 1;  // 0x3 == 0000 0011 
    return v; 
} 

Sowohl die obigen Verfahren setzen voraus, dass der Eingang ist gültig, dh Alle Komponenten von vector in Pack sind entweder -1, 0 oder 1 und alle 2-Bit-Abschnitte von b in Unpack haben eine (binärer Wert von entweder 00, 01 oder 10.

Da diese Methode bitweise Operatoren verwendet, ist sie schnell und effizient. Wenn Sie die Daten weiter komprimieren möchten, können Sie auch versuchen, die 2 unbenutzten Bits zu verwenden und alle 3 zu einem Vektor verarbeiteten 2-Bit-Elemente zu konvertieren.

+0

sehr cool, danke, es ist sehr schwierig ohne fundierte Kenntnisse des Themas. Ich habe den Code ein wenig für vector4 mit xyzw geändert. –

+0

ok ich hatte einen fehler um die vec4-version zu konvertieren und ich reparierte es, prost. –

+0

@komprehensible: etwas schneller mit '(v.x << 4) + (v.y << 2) + v.z + 21'. –

1

Die kompakteste Weg ist durch eine 27 Ziffern Zahl zur Basis Schreiben 3 (unter Verwendung eines Schiebe -1 -> 0, 0 -> 1, 1 -> 2).

Der Wert dieser Zahl wird aus 0 bis 3^27-1 = 7625597484987, Bereich, die 43 Bits codiert zu werden braucht, d.h. 6 Bytes (und 5 Ersatz Bits).

Dies ist ein wenig Ersparnis im Vergleich zu einer gepackten Darstellung mit 4 Zwei-Bit-Zahlen in einem Byte gepackt (daher 7 Bytes/56 Bits insgesamt).

Eine interessante Variante besteht darin, die Basis 3 Ziffern fünf mal fünf in Bytes zu gruppieren (daher Zahlen 0 bis 242). Sie benötigen immer noch 6 Bytes (und keine Ersatzbits), aber die Decodierung der Bytes kann leicht als eine Tabelle von 243 Einträgen hart-codiert werden.

+0

Da ich 1/2 Milliarden Voxel verarbeiten muss, benötigt das 8 Bit Array ungefähr 2 Gigs, während ein vector3 Int 8 Gigs benötigt. Das größte Problem ist die Lese- und Schreibgeschwindigkeit. Es ist ziemlich schnell, durch 1 Milliarde 8-Bit-Array auf einem i7, 1-2 Sekunden zu lesen, und dann 2-3 Anweisungen für jede Zahl zu tun dauert 2-3 mal länger. Jedes Mal, wenn ein Befehl zu einem 1 Milliarde Datenlesevorgang hinzugefügt wird, dauert dies etwa 1-2 Sekunden. Ich würde Ihren Vorschlag verwenden, um Arrays auf die Festplatte zu schreiben, es hört sich ziemlich schwierig an, hehe. Danke, Base-3-Konvertierung ist cool. –

+0

@comprehensible: "Es klingt ziemlich schwierig": Was? –

+0

Ich war nicht sehr versiert auf Basis 3 ... Ich recherchierte es und anscheinend ist es gleichmütig, Basis 10 mit 3x3x3 zu verwenden. difficile .. mein Französisch ist Panorama. Ich spreche es. –

2
  1. Ich nehme an, Ihre Werte sind float integer nicht

    so Operationen Bit werden auf ganzzahlige Typ nicht Geschwindigkeit zu viel im Vergleich zur Umkehr verbessern. Also wird meine Wette mit voller Reichweite besser sein. Ich würde dies für 3D-Fall:

    8 bit -> 256 values 
    3D -> pow(256,1/3) = ~ 6.349 values per dimension 
    6^3 = 216 < 256 
    

    So von (x,y,z) Verpackung wie folgt aussieht:

    BYTE p; 
    p =floor((x+1.0)*3.0); 
    p+=floor((y+1.0)*3.0*6.0); 
    p+=floor((y+1.0)*3.0*6.0*6.0); 
    

    Die Idee <-1,+1> ist konvertieren <0,1> daher die +1.0 und *3.0 statt *6.0 zu reichen und dann multiplizieren Sie einfach an der richtigen Stelle im endgültigen BYTE.

    und p Auspacken sieht wie folgt aus:

    x=p%6; x=(x/3.0)-1.0; p/=6; 
    y=p%6; y=(y/3.0)-1.0; p/=6; 
    z=p%6; z=(z/3.0)-1.0; 
    

    Auf diese Weise können 216 von 256 Werte verwendet werden, die dann nur 2 Bits viel besser ist (4 Werte). Ihr 4D Fall würde ähnlich aussehen, verwenden Sie einfach 3.0,6.0 andere Konstante floor(pow(256,1/4))=4 so verwenden Sie 2.0,4.0 aber Vorsicht Fall, wenn p=256 oder verwenden Sie 2 Bits pro Dimension und Bit-Ansatz wie die angenommene Antwort tut.

    Wenn Sie echte Geschwindigkeit benötigen, können Sie diese optimieren, um Schwimmer Darstellung Halte Ergebnis BYTE Paket zu erzwingen bestimmten Exponenten und extrahieren Mantissenbits als gepackte BYTE direkt. Als Ergebnis wird <0,216> Sie können eine größere Zahl hinzufügen. siehe IEEE 754-1985 für Details, aber Sie wollen die Mantisse mit Ihrem BYTE so ausrichten, wenn Sie Nummer wie 2^23 hinzufügen, dann sollte die niedrigste 8 Bit float Ihr gepackter Wert direkt sein (wie MSB 1 ist nicht in Mantisse vorhanden) so keine teure Konvertierung wird gebraucht.

  2. Im Fall bekam man nur {-1,0,+1} statt <-1,+1>

    dann von groben ganzzahligen Ansatz wie Bit mit 2 Bits pro Dimension Verpackung oder verwenden LUT Tabelle aller 3^3 = 27 Möglichkeiten und ganze Vektor-Pack verwenden sollten in 5 Bits.

    Die Codierung würde wie folgt aussehen:

    int enc[3][3][3] = { 0,1,2, ... 24,25,26 }; 
    p=enc[x+1][y+1][z+1]; 
    

    und Decodierung:

    int dec[27][3] = { {-1,-1,-1},.....,{+1,+1,+1} }; 
    x=dec[p][0]; 
    y=dec[p][1]; 
    z=dec[p][2]; 
    

    die schnell genug sein sollte, und wenn man viele Vektoren bekam können Sie die p in jeder Packung 5 Bits ... um noch mehr Speicherplatz zu sparen

+0

Hallo, das ist ein sehr interessanter Programmiertrick. Danke vielmals. Die ursprüngliche Frage wäre sehr kompliziert, klar zu sagen, sie beinhaltet einen Voxelraum, wo die Normalen von den freien Räumen um die Voxel abgeleitet sind, und weil es so viele Abtastwerte gibt, braucht das Programm nur 0/1 2-Bit-Normale zu sagen das, wo die Cubes/Samples stehen, die durch Poisson Disk Vertex Mapping später zusammengefügt werden. Ich muss danach auf 8-Bit-Präzision hochskalieren, weil ich die Voxel antialiasieren kann, indem ich nur die gezackten Ecken behalte und die Samples um sie herum aus flachen Zonen gemittelte. –

+0

@komprehensible in diesem Kontext BYTE-Darstellung machen viel mehr Sinn, wie Sie es für maskierte Glättung und Sachen nicht nur für Anti-Aliasing benötigt. Ich habe es für dieses [Wie procedurllly erzeugen ein Zelda wie machen in Java] (http://stackoverflow.com/a/36263999/2521214) für die Glättung des gekachelten Voxel 3D-Geländes durch das Maskieren der Kombinationen der benachbarten voxels verwendet. im Grunde konvertiere ich 27 Voxelwürfel in eine Zeichenkette und maskiere eine Zeichenkette, vergleiche mit Regeln für eine Aufgabe, die ich wie eine bestimmte Ecke aufspüre, um eine bestimmte Kachel usw. zu platzieren ... es erleichtert den Code, stattdessen hunderte von 'if else' habe ich wenige. – Spektre