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.
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.
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:
Die lineare Interpolation kann mit Hilfe des Strahlensatzes aufgesetzt werden:
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; }
Ein linearer Gradient wird durch zwei Punkte (x0, y0) und (x1, y1) eindeutig festgelegt. Für die Gradientenfunktion sollen folgende Eigenschaften gelten:
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:
Beispielbild für einen linearen 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);
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):
Über einige Umformungen kommt man zu folgender Gradientenformel (mehr Details sind in der OpenVG 1.1 Spezifikation zu finden):
Beispielbild - Radiale Gradienten Textur:
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...
Diese Website benutzt Cookies. 🍪 Wenn Sie die Website weiter nutzen, stimmen Sie der Verwendung von Cookies zu. Mehr Infos