.heightMap II

Kurzes Intro
.zurück zu heightMap I .weiter zu heightMap III

Nachdem ich in meinem HeightMap Versuchsprojekt soweit schon ordentliche Ergebnisse in der Terraingeneration und der Visualisierung erreicht habe, bin ich dazu übergegangen auch die passenden Texturen prozedural zu erzeugen. Die Ergebnisse habe ich nun auf dieser Seite zusammengefasst...
 

 
Prozedurale erzeugte HM-Texturen
Die Idee hinter den prozedural erzeugten Heightmaptexturen ist eigentlich simpel:
Wir haben eine HeightMap mit Höhenangaben für jeden Punkt (x/y) und n Texturen. Wir diskretisieren jetzt jeden Punkt auf eine Ganzzahl im Intervall [0..n] und ordnen so jedem Punkt (x,y,(z)) die zwei benachbarten Texturen zu also z.B. texLower und TexHigher. Dann wird der jeweilige Farbwert aus den beiden Texturen ausgelesen (z.B. Pixel p1 = texLower.getPixel(x,y), p2 = texHigher.getPixel(x,y)) und ein normierter Abstand des echten z wertes zu den Texturen errechnet - um dann die Farbwerte linear zu interpolieren.

Liegt beispielsweise eine Map mit Höhenwerten von min bis max vor, wir haben n Texturen und wir wollen nach oben beschriebener Vorgehensweise den Farbwert für den Punkt P = (x, y, z) berechnen, dann ergibt sich als der Abstand dz_lower = floor((z / (max-min)) * n) bzw. enstprechend dz_higher = 1 - dz_lower. Danach kann linear interpoliert werden.

Graphisch kann man sich den Ablauf in etwa wie folgt vorstellen:

Der betreffende Codeschnipsel aus meinem Projekt sieht in etwa wie folgt aus:

       

  GLfloat minVal = this->getMinHeightValue();
  GLfloat maxVal = this->getMaxHeightValue();
  GLfloat range  = maxVal - minVal;
  
  Bitmap* result = new Bitmap(pixelSize, pixelSize);
  for (int y = 0; y < pixelSize; y++) {
    for (int x = 0; x < pixelSize; x++) {
      GLfloat height = this->getHeightAt(x, y);
      GLfloat norm_h = (height - minVal) / range;
      GLfloat index  = norm_h*(numTextures-1);
      int lo_index   = (int) floor(index);
      int hi_index   = lo_index+1;

      GLfloat p = (index - lo_index);
      GLfloat q = 1-p;

      Bitmap* lo_bmp = *(bmpArray+lo_index);
      Bitmap* hi_bmp = *(bmpArray+hi_index);
      Pixel* loPix = lo_bmp->getPixel(x, y);
      Pixel* hiPix = hi_bmp->getPixel(x, y);

      unsigned char r = q*loPix->r + p*hiPix->r;
      unsigned char g = q*loPix->g + p*hiPix->g;
      unsigned char b = q*loPix->b + p*hiPix->b;

      result->setPixel(x, y, r, g, b);
    }
  }       

 

 
Skizzen und Demo Screenshots
Ich denke der Ablauf ist klar geworden, ich möchte das ganze jetzt mit den ein paar Bildern skizzieren und meine Ergebnissen vorstellen:

Zunächst habe ich das Ganze für eine einzelne Textur entwickelt; diese wurde aus einer Datei geladen und auf das Polygonnetz angewandt.

Jetzt folgte der "spannende Teil": Ich habe zunächst konstant mit zwei zur Laufzeit interpolierten Texturen gearbeitet um die Routine grundlegend zu implementieren und die Ausgabe zu überprüfen:

Danach folgte logischerweise die Verallgemeinerung auf n Texturen und die Ergebnisse sind meiner Meinung nach schon durchaus überzeugend:


 

 
Vorläufiges Ergebnis
Um das ganze dann auch noch ein wenig zu verschönern habe ich ein Alphageblendetes "Wasser"-Polygon eingefügt um ein schönes Endergebnis zu erhalten. Der nächste Schritt wird wohl entweder eine weitere Verallgemeinerung auf nicht äquidistante Interpolationsgrenzen oder der direkte Einstieg in die Shadowmap sein... in diesem Sinne: Ich hoffe ich konnte ein wenig den Gedanken klarmachen und eventuell zum Nachmachen inspirieren... - Fortsetzung folgt!


.zurück zu heightMap I .weiter zu heightMap III zurück