2012-04-02 3 views
3

Ich schreibe eine App, die ein dynamisches 640 * 480 Terrain erstellt (ändert jeden Frame). Jedes Gelände wird in einer Rohdatendatei gespeichert, die eine Folge von (Fließ-) Höhenwerten darstellt. Bis jetzt kann ich die Dateien nacheinander in den Speicher lesen und dynamisch ein Mesh basierend auf den Höhenwerten für jeden Frame erstellen (und die Framerate ist eigentlich vernünftig), aber ich maximiere 20 Frames, bevor meine App ohne Fehler oder Stack beendet wird Spur.OpenGLES2 iOS: Wie kann man die Vertex-Animation für dynamisches Terrain am besten streamen?

Ich vermute, dass ich das falsch annähere. Wie würde ich diese Daten streamen, so dass ich nicht jeden Frame im Speicher halten muss? Hier

ist ein Ausschnitt aus meiner Datenklasse, die eine Sammlung von Terrains hält:

- (void)addModelWithID:(int)modelID; 
{ 
    NSString* resourcePath = [[NSBundle mainBundle] resourcePath]; 
    NSString* fileName  = [NSString stringWithFormat:@"depth_%i.raw", modelID]; 
    NSString* fullPath = [resourcePath stringByAppendingPathComponent:fileName]; 

    NSData *myData = [NSData dataWithContentsOfFile:fullPath]; 
    if (!myData) 
     return; 

    const float *data = [myData bytes]; 

    int meshWidth    = 640; 
    int meshHeight    = 480; 

    Model *kModel = [[Model alloc] init]; 

    int indicesPtr = 0; 
    int depthPtr = 2; 

    for (int y=0;y<meshHeight;y++) // Loop through y pixels 
     { 
      for (int x=0;x<meshWidth;x++) // Loop through x pixels 
      { 
       // Set up vertex positions 
       int index = y*meshWidth+x; 
       float xpos = ((float)x/(float)(meshWidth-1)) - 0.5f; 
       float ypos = ((float)y/(float)(meshHeight-1)) - 0.5f; 
       float zpos = (float)data[index]; 

       kModel.vertices1[index*3+0] = xpos; 
       kModel.vertices1[index*3+1] = ypos; 
       kModel.vertices1[index*3+2] = zpos; 

       // Create a new index based on whether the current line is even or odd (flipped horizontally if odd) 
       int _index = (y%2==0) ? index : (y*meshWidth) + ((meshWidth-1)-x); 

       //Create the first index 
       kModel.indices[indicesPtr++] = _index; 

       // Create the second index 
       if ((x<meshWidth-1) || (x==meshWidth-1 && y==meshHeight-2)) 
        kModel.indices[indicesPtr++] = _index+meshWidth; 
      } 
     } 
    } 

    // Add the model to the data object 
    [Models addObject:kModel]; 
    [kModel release]; 
} 

Und meinen Draw-Code (jeden Frame ich ein anderes Terrain nennen, hoffentlich bis zu 500 oder so, aber ich max out bei ~ 20:

{ 
    ... 
     Model *kModel = [kData.kinectModels objectAtIndex:sequenceCurrentFrame]; 

     glVertexAttribPointer(vertexHandle, 3, GL_FLOAT, GL_FALSE, 0, (const GLvoid*)kModel.vertices1); 

     glEnableVertexAttribArray(vertexHandle); 

     glUniformMatrix4fv(mvpMatrixHandle, 1, GL_FALSE, (const GLfloat*)&modelViewProjection.data[0]); 
     glDrawElements(GL_TRIANGLE_STRIP, kModel.numIndices, GL_UNSIGNED_SHORT, (const GLvoid*)kModel.indices); 
    ... 
} 

Dank einer Million für Ihre Hilfe,

Josh

+0

Sie sollten definitiv einen Vertex-Shader schreiben, um das Terrain für Sie zu verändern. Abgesehen davon, wie lässt man die Erinnerung an jeden Geländeabschnitt frei? – arul

+0

Soweit ich es verstehe, kann ich die Erinnerung an jeden Geländebrocken nicht freigeben, da ich sie mit ~ 30 Bildern pro Sekunde durchlaufen muss. Wenn ich es also loslasse, müsste ich die Terraindatei wieder in jeden Frame laden, was sehr langsam wäre, oder? Ist es möglich, einen Vertex-Shader für die Tiefe des Geländes zu schreiben? Es scheint albern, die X- und Y-Werte jedes Mal zu erstellen, da sie sich nicht ändern, aber ich weiß nicht wirklich, wie ich anfangen soll, die Z-Position an den Shader zu senden. Irgendwelche Tipps? – Josh

+0

Eine Nebenbemerkung: Verwenden Sie 'NSData's' NSDataReadingMappedAlways' Option (mit 'dataWithContentsOfFile: Optionen: Fehler:' als Drop-In-Substitution für Ihre aktuelle 'dataWithContentsOfFile:'); Dies führt dazu, dass die Datei im Speicher abgelegt wird und nicht geladen wird. Sie erhalten also einen Zeiger, den Sie genau so verwenden können, als ob die gesamte Datei geladen wäre, aber das Betriebssystem behandelt, welcher Teil der Datei tatsächlich im Speicher ist und was bleibt Festplatte als Caching-Aufgabe, die automatisch den Gesamtspeicherbedarf verarbeitet. – Tommy

Antwort

3

Sie können Ihre Höhendaten in einer Textur speichern und dann im Vertex-Shader die Funktion texture2D verwenden, um die aktuelle Höhe des Eckpunkts zu ändern.

Mit dieser Konfiguration können Sie ein einzelnes Netz haben und die Höhe jedes Eckpunkts einfach um den in der Textur gespeicherten Wert anpassen.

Wenn Sie sicher sind, dass Sie GPU-gebunden sind, kann das Texturformat, das zum Nachschlagen der Höhendaten verwendet wird, weiter optimiert werden. Nehmen wir an, Ihre Heightmap reicht von 0-255, beim Format GL_RGBA können Sie die gleiche Textur für das Rendern von bis zu 4 Frames verwenden (erstes Frame liest die Höhe von der roten Komponente, nächstes Frame von der blauen Komponente usw.). Durch die doppelt so große Hintergrundtextur (1280x960) können Sie bis zu 16 Bilder in einer einzigen Textur speichern. Eine weitere Sache, die in Bezug auf Texturen erwähnenswert ist, ist, dass die Verwendung des nativen Formats für Ihre GPU auch die Leistung in beiden GPU/IO-gebundenen Szenarien verbessern kann. Für iDevices ist dieses Format PVRTC.

Ich glaube wirklich, dass das Problem ist nur die Zuordnung von 300k + Ecken jeder Rahmen allein ..., die jeden Rahmen zugeordnet 640 * 480 * 3 * 4 = 3 686 400 Bytes addiert und Sie nicht erwähnt Nach wie vielen Frames hören Sie auf, den Speicher zuzuweisen (warten Sie ... tun Sie?). Das ist viel zu viel für eine solche ressourcenbeschränkte Umgebung.

+0

Danke arul, das ist aufschlussreichste. Ich werde sicherlich die texture2D-Idee umsetzen. Im Moment behalte ich alle Daten im Speicher, nur weil ich nur einmal von der Platte lesen will. Das funktioniert eindeutig nicht. Letztendlich möchte ich eine Sequenz beliebiger Länge spielen. Meine Idee war, alle Objektdateien zu einer zu verketten, aber wie würde ich eine solche Datei von der Festplatte streamen, wie Sie es zum Beispiel mit einer 3Gb-Filmdatei tun würden? Jede Datei enthält nur eine Liste von 640 * 480 Floats. Abbildung 3 Minuten der Animation bei 24 Bildern pro Sekunde und Sie sind in etwa so groß. Ist das Streaming nicht die einzige Option? – Josh

+0

Ja ist es, aber Sie müssen nur die wichtigen Daten streamen, die in diesem Fall die Heightfield-Map ist. Der theoretische IO-Durchsatz, der benötigt wird, beträgt 29,4912 MB/s (ist das nicht zu viel für iDevices?). Ich denke, dass das Speichern der Höheninformationen als Floats übertrieben ist. Angenommen, Sie arbeiten mit Kinect zusammen, erhalten Sie die Tiefeninformationen mit einer Genauigkeit von 13 Bits, und die verbleibenden 19 Bits werden verschwendet. Versuchen Sie, die texture2D-Lookup-Methode zu implementieren und dann den Code zu profilieren, um herauszufinden, wo der Engpass liegt (ich wette auf IO-Durchsatz). – arul

+0

Großartig, vielen Dank für Ihre Hilfe. Um das klarzustellen: a) Schreiben Sie die Kinect - Rohdateien neu, um nur viel kleinere Datentypen zu verwenden (vielleicht nicht signiert?) B) Streuen Sie die Daten mit der von Tommy vorgeschlagenen NSDataReadingMappedAlways - Option und c) verwenden Sie die texture2D - Methode Höhendaten für den Vertex-Shader. Klingt das richtig? Kann ich den Inhalt von NSDataReadingMappedAlways direkt hierhin ausgeben :? 'glVertexAttribPointer (VertexHandle, 3, GL_FLOAT, GL_FALSE, 0, (const GLvoid *) Scheitelpunkte);' – Josh