Textur-Synthese: Lineare und radiale Gradienten

Update: Freitag, 27. September

Home / TECHNIK / Programmierung

Auf dieser Seite findest du eine Sammlung verschiedener Beispiele zur Texturerzeugung für lineare und radiale Gradienten, meist mit nur wenigen Zeilen Quellcode.

Die Algorithmen sind in C/C++ bzw. ANSI-C geschrieben und sollen demonstrieren, wie Texturen durch Programmcode generiert werden können.
Angeregt wurde ich durch die Khronos OpenVG 1.1 Spezifikation - mit dieser 2D Vektor-API ist es nämlich möglich, flexible Gradienten zu zeichnen.

 

Gradienten Texturen

Die folgenden Gradienten-Texturen sind aus mathematischer Sicht in zwei Teile gegliedert: Zum einen wird eine skalare Gradientenfunktion verwendet, die für jeden Bildpunkt im zweidimensionalen Koordinatensystem einen Wert im Bereich [0, 1] definiert. Zum anderen wird dann über eine Color-Ramp-Funktion ein Farbwert für diesen Gradienten interpoliert.

Color Ramp Funktion

Die Color Ramp Funktion soll aus einem Eingabewert im Bereich [0, 1] einen entsprechenden Farbwert (32-bit, RGBA8888 Format) zurückliefern. Dazu werden im Bereich [0, 1] fünf Punkte festgelegt (=Stops), die einen konkreten Farbwert zugewiesen bekommen. Es wird vorausgesetzt, dass die beiden Grenzwerte 0 und 1 immer enthalten sind und die anderen drei Punkte in aufsteigender Reihenfolge dazwischen liegen (Abstand frei wählbar). An jedem dieser 5 Punkte ist der Farbwert bekannt, an den Werten dazwischen muss die Farbe aus den beiden Nachbar-Stops linear interpoliert werden. Im Quellcode sind die Stops mit Farbwerten in einem Array abgelegt.

Beispiel für die fünf Stops, mit unterschiedlichen Abständen:

Beispiel Color Ramp

Die lineare Interpolation kann mit Hilfe des Strahlensatzes aufgesetzt werden:

Lineare Interpolation

Formel Strahlensatz

Quelltext der Color Ramp Funktion:

// type for color ramp stops
typedef struct {
int r, g, b, a;
float stop;
} stops_t;
// create an RGBA8888 color value based on an input gradient of range [0..1] static unsigned int colorRamp(float grad) { int i, j, r, g, b, a; float alpha; stops_t stops[] = { // red, green, blue, alpha, gradient { 10, 10, 10, 255, 0.0f }, // black { 230, 10, 10, 255, 0.1f }, // red { 10, 230, 10, 255, 0.4f }, // green { 10, 10, 230, 255, 0.6f }, // blue { 230, 230, 230, 255, 1.0f }, // white }; if (grad < 0.0f) grad = 0.0f; if (grad > 1.0f) grad = 1.0f; // find the two nearest stops for (i = 0; i < 4; ++i) { if (grad < stops[i+1].stop) { break; } } j = i + 1; // linear interpolation of two stop colors alpha = (grad - stops[i].stop) / (stops[j].stop - stops[i].stop); r = (int)(stops[i].r + (stops[j].r - stops[i].r) * alpha); g = (int)(stops[i].g + (stops[j].g - stops[i].g) * alpha); b = (int)(stops[i].b + (stops[j].b - stops[i].b) * alpha); a = (int)(stops[i].a + (stops[j].a - stops[i].a) * alpha); return (r << 24) | (g << 16) | (b << 8) | a; }

 

Lineare Gradienten mit Color Ramp

Ein linearer Gradient wird durch zwei Punkte (x0, y0) und (x1, y1) eindeutig festgelegt. Für die Gradientenfunktion sollen folgende Eigenschaften gelten:

  • Der Gradient ist 0 am Punkt (x0, y0).
  • Der Gradient ist 1 am Punkt (x1, y1).
  • Der Wert soll stetig entlang der Linie von (x0, y0) nach (x1, y1) ansteigen.
  • Der Wert ist konstant auf allen Linien, die zur Linie (x0, y0) nach (x1, y1) parallel verlaufen.

Skizze Linearer Gradient

Mathematisch lässt sich der Gradient über zwei Vektoren mit dem Skalarprodukt  bestimmen. Durch orthogonale Projektion des Vektors b' auf die durch a' bestimmte Richtung. Der Gradient ist normiert auf die Länge des Vektors a', zusammengefasst ergibt sich:

Vektor Projektion

Linearer Gradient Formel

Beispielbild für einen linearen Gradienten:

Textur mit linearem Gradienten

Quelltext zum Erzeugen des linearen Gradienten:

// see color ramp code above
static unsigned int colorRamp(float grad);
// ptr points to a memory buffer of size width * height * sizeof(unsigned int)
static void genLinearGradient(unsigned int *ptr, int width, int height, 
                              float x0, float y0, float x1, float y1) {
  int x,y;
  float deltaX, deltaY, grad, denom;
  deltaX = x1 - x0;
  deltaY = y1 - y0;
  denom = 1.0f / ((deltaX * deltaX) + (deltaY * deltaY));
  for (y = 0; y < height; ++y) {
    for (x = 0; x < width; ++x) {
      // gradient value in the range [0..1]
      grad = (deltaX * (x - x0) + deltaY * (y - y0)) * denom;
      *ptr++ = colorRamp(grad);
    }
  }
}
...
// example call
genLinearGradient(buf, width, height, 10.0f, 20.0f, 700.0f, 200.0f);

 

Radiale Gradienten mit Color Ramp

Eine weitere Abwandlung ermöglicht es Texturen mit radialen Gradienten zu erzeugen. Dabei wird ein Gradienten-Kreis mit dem Mittelpunkt (cx, cy) und dem Radius r definiert. Zusätzlich wird ein Fokuspunkt (fx, fy) innerhalb des Kreises festgelegt. Der Wert der Gradientenfunktion soll 0 am Fokuspunkt sein und 1 am Umfang des Kreises. Die folgende Skizze veranschaulicht die Situation (Quelle der Abb. Khronos OpenVG 1.1 Spezifikation):

Skizze Radialer Gradient

Über einige Umformungen kommt man zu folgender Gradientenformel (mehr Details sind in der OpenVG 1.1 Spezifikation zu finden):

Formel Radialer Gradient

Beispielbild - Radiale Gradienten Textur:

Textur mit radialem Gradienten

 

Quelltext zum Erzeugen des radialen Gradienten:

// see code above
static unsigned int colorRamp(float grad);
// ptr points to a memory buffer of size width * height * sizeof(unsigned int)
static void genRadialGradient(unsigned int *ptr, int width, int height, 
                              float cx, float cy, float radius, float fx, float fy) {
  int x,y;
  float grad, fx_, fy_, dx, dy, denom, radius2, dx2, dy2;
  fx_ = fx - cx;
  fy_ = fy - cy;
  radius2 = radius * radius;
  denom = 1.0f / (radius2 - ((fx_ * fx_) + (fy_ * fy_)));
  for (y = 0; y < height; ++y) {
    for (x = 0; x < width; ++x)  {
      dx = x - fx;
      dy = y - fy;
      dx2 = dx * dx;
      dy2 = dy * dy;
      grad = (radius2 * (dx2 + dy2)) - ((dx * fy_ - dy * fx_) * (dx * fy_ - dy * fx_));
      grad = ((dx * fx_ + dy * fy_) + sqrtf(grad));
      grad *= denom;
      *ptr++ = colorRamp(grad);
    }
  }
}

 

Gefällt dir der Artikel zur Texturerzeugung mit linearen und radialen Gradienten? Hast du Anregungen, Ergänzungen oder einen Fehler gefunden? Schreibe doch einen Kommentar...

Kommentare 0

 

Neuen Kommentar schreiben: