Für den Zweck dieser Diskussion wird ein Bild definiert als eine einzelne Anordnung von Pixeln einer bestimmten Dimensionalität (1D, 2D oder 3D), mit einer bestimmten Größe und einem bestimmten Format.
Eine Textur ist ein Container mit einem oder mehreren Bildern. Aber Texturen speichern keine beliebigen Bilder. Eine Textur hat spezifische Einschränkungen für die Bilder, die sie enthalten kann. Es gibt drei definierende Merkmale einer Textur, von denen jede einen Teil dieser Einschränkungen definiert: den Texturtyp, die Texturgröße und das Bildformat, das für Bilder in der Textur verwendet wird. Der Texturtyp definiert die Anordnung der Bilder innerhalb der Textur. Die Größe definiert die Größe der Bilder in der Textur und das Bildformat definiert das Format, das alle diese Bilder gemeinsam haben.
Es gibt eine Reihe von verschiedenen Textur-Typen, die wir im Folgenden kurz vorstellen möchten:
Texturgrößen haben ein Limit, das auf der GL-Implementierung basiert. Für 1D- und 2D-Texturen (und alle Texturtypen, die eine ähnliche Dimensionalität verwenden, wie Cubemaps) ist die maximale Größe beider Dimensionen GL_MAX_TEXTURE_SIZE. Für Array-Texturen ist die maximale Array-Länge GL_MAX_ARRAY_TEXTURE_LAYERS. Bei 3D Texturen darf keine Bemaßung größer sein als GL_MAX_3D_TEXTURE_SIZE in der Größe.
Innerhalb dieser Grenzen kann die Größe einer Textur ein beliebiger Wert sein. Es wird jedoch empfohlen, dass Sie sich bei Texturgrößen an die Potenz von zwei halten, es sei denn, Sie haben einen erheblichen Bedarf, beliebige Größen zu verwenden.
Mip Maps.
Wenn eine Textur direkt auf eine Oberfläche angewendet wird, hängt es vom Winkel ab, in dem diese Oberfläche gerendert wird, wie viele Pixel dieser Textur (allgemein als „Texte“ bezeichnet) verwendet werden. Eine Textur, die einer Ebene zugeordnet ist, die mit der Kamera fast kantengenau ist, verwendet nur einen Bruchteil der Pixel der Textur. Ebenso wird der direkte Blick von weitem auf die Textur weniger Texte zeigen als eine Nahaufnahme.
Das Problem ist die Animation. Wenn Sie langsam aus einer Textur herauszoomen, beginnen Sie zu sehen, wie Aliasing-Artefakte erscheinen. Diese werden durch das Abtasten von weniger als allen Texten verursacht. Die Wahl, welche Texte abgetastet werden, wechselt zwischen verschiedenen Frames der Animation. Auch bei linearer Filterung (siehe unten) werden beim Herauszoomen der Kamera Artefakte angezeigt.
Um dieses Problem zu lösen, verwenden wir Mip Maps. Dies sind vorverkleinerte Versionen des Bildes in voller Größe. Jedes Mipmap ist halb so groß wie das vorherige in der Kette und verwendet die größte Dimension des Bildes. Eine 64×16 2D-Textur kann also 6 Mip-Maps haben: 32×8. 16X4, 8×2, 4×1, 2×1 und 1×1. OpenGL erfordert nicht, dass die gesamte Mipmap-Kette vollständig ist. Sie können angeben, welcher Bereich von Mipmaps in einer Textur verfügbar ist.
Einige Texturtypen haben mehrere unabhängige Sätze von Mipmaps. Jede Fläche einer Cubemap hat Ihren Satz von Mipmaps, ebenso wie jeder Eintrag in einer Array-Textur. Die Textur als Ganzes hat jedoch nur eine Einstellung, für die Mipmaps vorhanden sind. Wenn die Textur also so eingerichtet ist, dass nur die obersten 4 Ebenen der Mipmaps vorhanden sind, müssen Sie sie für alle Mipmap-Ketten in der Textur haben.
Beim Sampling einer Textur (siehe unten) wählt die Implementierung automatisch die zu verwendete Mipmap aus, basierend auf dem Blickwinkel, der Größe der Textur und verschiedenen anderen Faktoren.
Bei der Verwendung von Texturgrößen, die keine Zweierpotenzen sind, wird die halbe Größe der unteren Mipmaps abgerundet. Eine 63×63 Textur hat also als nächstes Mipmap-Level 31×31 und so weiter.
Die Basisebene einer Mipmap-Kette ist die größte. Es ist auch dasjenige, das die volle Größe der Textur definiert. OpenGL nummeriert diese Mipmap-Ebene mit 0, die nächste Mipmap-Ebene ist 1 und so weiter.
Die Basisebene einer Textur muss nicht geladen werden. Solange Sie den Bereich der Mipmaps korrekt angeben, können Sie alle gewünschten Mipmap-Ebenen weglassen.
Textur-Objekte.
Texturen in OpenGL sind OpenGL-Objekte, und sie folgen den Standardkonventionen dieser Objekte. So haben sie die Standard glGenTextures, glBindTexture, wie Sie es erwarten würden.
Der Zielparameter von glBindTexture entspricht dem Typ der Textur. Wenn Sie also einen frisch generierten Texturnamen verwenden, hilft Ihnen der erste Bind, den Typ der Textur zu definieren. Es ist nicht erlaubt, ein Objekt an ein anderes Ziel zu binden als das, mit dem es zuvor verbunden war. Wenn Sie also eine Textur erzeugen und als GL_TEXTURE_1D binden, dann müssen Sie sie weiterhin als solche binden.
Wie bei jeder anderen Art von OpenGL-Objekt ist es legal, mehrere Objekte an verschiedene Ziele zu binden. So können Sie eine GL_TEXTURE_1D gebunden haben, während eine GL_TEXTURE_2D_ARRAY gebunden ist.
Vollständigkeit der Textur.
Ein Texturobjekt hat den Begriff „Vollständigkeit“. Ein komplettes Texturobjekt ist eines, das sich in einem logischen Zustand befindet, der für viele Operationen verwendet werden kann. Bis eine Textur vollständig ist, kann sie „nicht“ für Shader-Sampling oder Image Load Store Operationen verwendet werden. Und das Anhängen eines Bildes aus einer Textur an ein Framebuffer-Objekt erfordert bestimmte Formen der Vollständigkeit.
Ein Texturobjekt ist vollständig, wenn es die folgenden 3 Arten von Vollständigkeitsanforderungen erfüllt.
Vollständigkeit der Mipmap.
Die Vollständigkeit der Mipmap erfordert im Wesentlichen die Konsistenz zwischen den Bildformaten der zugeordneten Mipmap-Ebenen, den Texturparametern und den Sampling-Parametern der Textur.
Texturen, die Speicher verwenden, der mit unveränderlichen Speicherfunktionen (und Puffertexten) zugewiesen wurde, sind immer mipmap-komplett. Für andere Texturen gelten die folgenden Regeln.
Wenn der Sampling-Parameter GL_TEXTURE_MIN_FILTER der Textur angibt, dass sie Mipmaps verwendet, dann muss folgendes wahr sein:
Die Vervollständigung von Mipmaps gilt nur, wenn die Parameter der Minifikationsfilterung Mipmaps verwenden. Wenn dies nicht der Fall ist, dann ist die Textur immer „mipmap complete“.
Vollständigkeit der Cubemap.
Cubemap-Texturen haben zusätzliche Anforderungen, die sich aus der Art und Weise ihrer Zuordnung (mit jährlichen Speicherfunktionen) ergeben. Texturen, die Speicherplatz verwenden, der mit unveränderlichen Speicherfunktionen belegt ist, sind immer „cubemap complete“. Für andere Texturen gelten die folgenden Regeln:
Vollständigkeit des Bildformats.
Das interne Format, das eine Textur verwendet, kann auch ihre Vollständigkeit beeinflussen, abhängig von den Sampling-Parametern.
Ganze Farbformate und Stencil Index Formate (ob GL_STENCIL_INDEX direkt oder über Stencil Texturing) unterstützen keine lineare Filterung. Daher muss der Stichprobenparameter GL_TEXTURE_MAG_FILTER GL_NEAREST und GL_TEXTURE_MIN_FILTER entweder GL_NEAREST oder GL_NEAREST_MIPMAP_NEAREST sein.
Sampler-Objekte und Vollständigkeit.
Wenn Sie eine Textur mit einem Sampler-Objekt verwenden, basiert die Vollständigkeit dieser Textur auf den Sampling-Parametern des Sampler-Objekts und nicht auf den internen Sampling-Parametern der Textur.
Wenn beispielsweise ein Texturobjekt nur die Basis-Mipmap hat und die Parameter des Mipmap-Bereichs den Zugriff über die Basisebene hinaus erlauben, ist dieses Texturobjekt unvollständig, wenn die Parameter GL_TEXTURE_MIN_FILTER den Zugriff auf andere Mipmaps als die Basisebene erfordern. Wenn Sie dieses Objekt jedoch mit einem Sampler koppeln, dessen Min-Filter GL_LINEAR oder GL_NEAREST ist, dann ist diese Textur-Bildeinheit mipmap vollständig.
Ähnlich verhält es sich, wenn eine Textur ein ganzzahliges Bildformat verwendet und der gepaarte Sampler nur die nächstgelegene Filterung verwendet, wird die Textur das Bildformat vollständig sein.
Eine Textur kann also als vollständig oder nicht vollständig angesehen werden, je nachdem, wo sie verwendet wird.
Image Load Store verwendet keine Sampler-Objekte. Aber es führt immer noch Texturvollständigkeitsprüfungen durch. Daher werden die Vollständigkeitsregeln basierend auf den internen Sampling-Parametern der Textur angewendet, nicht die eines Sampler-Objekts.
Speicherung.
Textur-Objekte bestehen aus drei Teilen: Speicherung, Sampling-Parameter und Textur-Parameter. Es gibt zahlreiche Funktionen, um den Speicher einer Textur zu erstellen. Und zwar so viele, dass der Artikel eine eigene Seite benötigt, um sie alle zu beschreiben.
Parameter.
Exture-Objekte haben Parameter. Diese Parameter steuern viele Aspekte der Funktionsweise der Textur.
Textur-Parameter werden mit den folgenden Funktionen eingestellt:
Diese Funktion setzt die Parameterwerte param oder params für den jeweiligen Parameter pname in der an das Ziel gebundene Textur.
In der Anatomie eines obigen Bildes eines Texturobjekts zeigt es drei Datenstücke: Texturspeicher, Texturparameter und Samplingparameter. Es ist wichtig zu verstehen, dass die beiden letzten beiden Arten von Daten durch die gleichen Funktionen für Texturen gesetzt werden. Bestimmte Parameter beziehen sich auf die Textur selbst, und einige auf das Sampling von ihnen.
In diesem Abschnitt werden nur die Texturparameter beschrieben.
Mipmap-Bereich.
Die Parameter GL_TEXTURE_BASE_LEVEL und GL_TEXTURE_MAX_LEVEL (Integer-Werte) definieren den geschlossenen Bereich der Mipmaps, die mit dieser Textur als verfügbar betrachtet werden sollen. Nichts kann das Sampling von Mipmaps kleiner als GL_TEXTURE_BASE_LEVEL verursachen und nichts kann das Sampling von Mipmaps größer als GL_TEXTURE_MAX_LEVEL verursachen. Dies filtert sogar in GLSL. Die Texturgrößenfunktionen liefern die Größe von GL_TEXTURE_BASE_LEVEL und nicht die Größe von mipmap level 0.
Beachten Sie, dass bei unveränderlichen Speichertexturen diese Werte bereits beim Erstellen auf den Mipmap-Bereich des Speichers gesetzt werden. Sie können sie als einen Teilbereich davon festlegen, aber es ist ein Fehler, den Basis- und Maximalpegel außerhalb des verfügbaren Mipmap-Bereichs für den unveränderlichen Speicher einzustellen.
Swizzle Mask.
Während GLSL-Shader durchaus in der Lage sind, den von einer Texturfunktion zurückgegebenen vec4-Werte neu zu ordnen, ist es oft bequemer, die Reihenfolge der von einer Textur aus dem Code geholten Daten zu steuern. Dies geschieht durch sogenannte „Swizzle Parameter“.
Texturobjekte können schwindelerregende Parameter haben. Dies funktioniert nur bei Texturen mit Farbbildformaten. Jede der vier Ausgangskomponenten, RGBA, kann so eingestellt werden, dass sie von einem bestimmten Farbkanal kommt. Die Swizzle-Maske wird von allen Shader-Lesezugriffen beachtet, sei es über Textursampler oder Image Load Store.
Hinweis: Diese Funktion gilt nur für Lesezugriffe aus dem Shader. Schreibvorgänge über den Image Load Store werden von der Swizzle Mask nicht beeinflusst. Auch sind Lese- oder Schreibvorgänge aus anderen Quellen, wie z.B. Blending-Operationen, die die Masken nicht unterstützen.
Um den Output für eine Komponente einzustellen, setzen Sie den Texturparameter GL_TEXTURE_SWIZZLE_C, wobei C gleich R, G, B oder A ist. Diese Parameter können auf die folgenden Werte gesetzt werden:
Sie können auch den Parameter GL_TEXTURE_SWIZZLE_RGBA verwenden, um alle vier auf einmal zu setzen. Dieser nimmt ein Array mit vier Werten an. Zum Beispiel:
Dadurch wird der rote Kanal im Bild effektiv dem Alphakanal zugeordnet, wenn der Shader auf ihn zugreift.
Stencil Texturierung.
Eine Textur mit einem Depth-Image-Format wird normalerweise als Depth-Komponenten-Textur betrachtet. Das bedeutet, dass der Non-Depth Comparison Access einen einzelnen Gleitkommawert zurückgibt (da Depth-Komponenten entweder normierte ganze Zahlen oder Gleitkommazahlen sind). Diese Depth-Texturen können als eine Sonderform der einkanaligen Gleitkomma-Farbtexturen angesehen werden.
Wenn die Textur jedoch ein gepacktes Depth/Stencil Image Format verwendet, ist es möglich, anstelle der Depth-Komponente auf die Stencil Komponente zuzugreifen. Dies wird durch den Parameter GL_DEPTH_STENCIL_TEXTURE_MODE gesteuert.
Wenn der Parameter auf GL_DEPTH_COMPONENT gesetzt ist, greift der Zugriff über den Shader wie gewohnt auf die Depth-Komponente als einzelner Float zu. Wenn der Parameter jedoch auf GL_STENCIL_INDEX gesetzt ist, kann der Shader auf die Stencil Komponente zugreifen.
Dieser Parameter ändert die Art des Texturzugriffs. Die Stencil-Komponente ist ein vorzeichenloser ganzzahliger Wert, daher müssen Sie beim Zugriff auf sie einen vorzeichenlosen ganzzahligen Sampler verwenden. Beim Zugriff auf die Stencil-Komponente einer 2D-Depth-/Stencil-Textur müssen Sie also usampler2D verwenden.
Hinweis: Obwohl dieser Parameter das Sampling beeinflusst, ist er kein Sampling-Parameter. Daher können Sie nicht dasselbe Texturobjekt an zwei Bildeinheiten binden und zwei verschiedene Sampler-Objekte verwenden, um die Depth- und Stencil-Komponenten zu erhalten. Sie können jedoch eine Ansicht der Textur erstellen (sowohl Ansichtstexturen als auch Stencil-Texturen sind Funktionen von GL 4.3) und verschiedene Texturparameter in den verschiedenen Ansichten festlegen. Eine Ansicht für die Tiefe, eine Ansicht für die Schablone.
Sampling Parameter.
Sampling ist der Prozess des Holens eines Wertes aus einer Textur an einer bestimmten Position. GLSL kontrolliert einen Großteil des Probenahmeprozesses, aber es gibt viele Parameter, die dies ebenfalls beeinflussen.
Diese Parameter werden mit Sampler Objects geteilt, da sowohl Texturobjekte als auch Samplerobjekte diese haben.
Textur Image Units.
Verbindliche Texturen für die Verwendung in OpenGL sind etwas seltsam. Es gibt zwei Gründe, ein Texturobjekt an den Kontext zu binden: das Objekt zu ändern (z.B. seinen Speicher oder seine Parameter zu ändern) oder etwas damit zu rendern.
Das Ändern des gespeicherten Zustands der Textur kann mit dem oben genannten einfachen glBindTexture-Call durchgeführt werden. Allerdings ist das Rendern mit einer Textur etwas komplizierter.
Eine Textur kann an eine oder mehrere Stellen gebunden werden. Diese Positionen werden als Textur Image Units bezeichnet. OpenGL-Kontexte haben eine maximale Anzahl von Textur Image Units, die aus der Konstanten GL_MAX_COMBIND_TEXTURE_IMAGE_UNITS abgefragt werden können.
Welche Image Unit einen glBindTexture-Call bindet, hängt von der aktuell aktiven Textur Image Unit ab. Dieser Wert wird durch den Aufruf gesetzt:
Der Wert der Textur ist GL_TEXTURE0 + i, wobei i eine Zahl im halboffenen Bereich [0, GL_MAX_COMBINED_TEXTURE_IMAGE_UNITS] ist. Dies führt dazu, dass die Textur Image Unit i die aktuell aktive Image Unit ist.
Jede Texture Image Unit unterstützt Bindungen an alle Ziele. So können eine 2D-Textur und eine Array-Textur an dieselbe Image Unit gebunden werden oder verschiedene 2D-Texturen können in zwei verschiedenen Image Units gebunden werden, ohne sich gegenseitig zu beeinflussen. Welche Textur wird also beim Rendern verwendet? In GLSL hängt dies von der Art des Samplers ab, der diese Texture Image Unit verwendet.
Hinweis: Das kling verdächtig, als ob man die gleiche Textur Image Unit für verschiedene Sampler verwenden könnte, solange sie unterschiedliche Textur-Typen haben. Diese Spezifikation verbietet es explizit, wenn zwei verschiedene GLSL-Sampler unterschiedliche Textur-Typen haben, aber derselben Textur Image Unit zugeordnet sind, schlägt das Rendern fehl. Geben Sie jedem Sampler eine andere Textur Image Unit.
Die glActiveTexture-Funktion definiert die Textur Image Unit, die jede Funktion verwendet, die ein Texturziel als Parameter verwendet.
GLSL Bindungen.
Shader-Programme sind eine von zwei Anwendungen von Texturen. Um Texturen mit einem Programm verwenden zu können, muss das Programm selbst eine bestimmte Syntax verwenden, um Texture Binding Points freizulegen.
Sampler.
Ein Sampler in GLSL ist eine einheitliche Variable, die eine zugängliche Textur darstellt. Es kann nicht innerhalb eines Programms eingestellt werden, es kann nur vom Benutzer des Programms eingestellt werden. Sampler-Typen entsprechen den OpenGL-Texturtypen.
Sampler werden mit GLSL Texture Access Funktionen verwendet.
Der Prozess der Verwendung von Texturen mit Programm-Samplern umfasst 2 Hälften. Texturobjekte sind nicht direkt mit Programmobjekten verknüpft oder an diese angehängt. Stattdessen referenzieren Programm-Sampler auf der Textur-Image-Unit-Indizes. Welche Texturen zum Zeitpunkt des Renderings an diese Image Units gebunden sind, verwendet das Programm.
Der erste Schritt besteht also darin, den einheitlichen Wert für die Programm-Sampler einzustellen. Setzen Sie für jede Sampler-Uniform ihren einheitlichen Wert auf eine ganze Zahl im Bereich [0, GL_MAX_COMBINED_TEXTURE_IMAGE_UNITS]. Wenn es an der Zeit ist, das Programm direkt zu nutzen, verwenden Sie einfach glActiveTexture und glBindTexture, um die gewünschten Texturen an diese Image-Units zu binden.
Die Texturen, die an die in den Sampler-Uniformen festgelegte Image Unit gebunden sind, müssen dem Typ des Samplers entsprechen. Ein Sampler1D wird also auf die GL_TEXTURE_1D-Bindung in der Image Unit achten, in der er eingestellt ist.
Wenn ein Sampler-Objekt an die gleiche Textur Image Unite gebunden ist wie eine Textur, dann ersetzen die Parameter des Sampler-Objekts die Sampling-Parameter dieses Texturobjekts.
Bilder.
Bilder innerhalb einer Textur können für beliebige Lade-/Speicheroperationen verwendet werden. Dies geschieht über Bildvariablen, die als Uniforms deklariert werden. Image Uniforms sind einer Image Unit zugeordnet (anders als eine Textur Image Unit). Der Zusammenschluss funktioniert ähnlich wie bei Sampler-Uniforms, nur die Anzahl der Image Units pro Shaderstufe unterscheidet sich von der Anzahl der Textur Image Units pro Shaderstufe.
Bilder werden mit glBindImageTexture an Image Units gebunden.
Render-Ziele.
Durch die Verwendung eines Framebuffer-Objekts können einzelne Bilder innerhalb einer textur das Ziel für das Rendern sein.
Wir hoffen, dass wir Ihnen einen ersten Überblick über die Thematik bieten konnten, wenn Sie noch Fragen oder Anregungen haben sollten, hinterlassen Sie uns unten einen Kommentar.
Vielen Dank für Ihren Besuch.