WARNING
Obwohl Minecraft mit OpenGL erstellt wurde, kannst du ab Version 1.17 keine älteren OpenGL-Methoden mehr verwenden, um eigene Inhalte zu rendern. Stattdessen musst du das neue BufferBuilder-System verwenden, das die Rendering-Daten formatiert und zum Zeichnen an OpenGL hochlädt.
Zusammenfassend kann man sagen, dass man das Rendering-System von Minecraft benutzen muss, oder ein eigenes, das GL.glDrawElements() benutzt.
WICHTIGES UPDATE
Ab Version 1.21.6 werden umfangreiche Änderungen an der Rendering-Pipeline vorgenommen, darunter die Umstellung auf RenderTypes und RenderPipelines und vor allem auf RenderStates. Das ultimative Ziel besteht darin, das nächste Bild vorbereiten zu können, während das aktuelle Bild gezeichnet wird. In der "Vorbereitungsphase" werden alle für die Darstellung verwendeten Spieldaten in RenderStates extrahiert, sodass ein anderer Thread an der Zeichnung dieses Bilds arbeiten kann, während das nächste Bild extrahiert wird.
Beispielsweise wurde dieses Modell in 1.21.8 für das GUI-Rendering übernommen, und die Methoden von GuiGraphics fügen einfach zum Renderstatus hinzu. Das eigentliche Hochladen in den BufferBuilder erfolgt am Ende der Vorbereitungsphase, nachdem alle Elemente zum RenderState hinzugefügt wurden. Siehe GuiRenderer#prepare.
Dieser Artikel behandelt die Grundlagen des Renderns und ist zwar nach wie vor relevant, doch in den meisten Fällen gibt es höhere Abstraktionsebenen für eine bessere Leistung und Kompatibilität. Für weitere Informationen siehe Rendering in der Welt.
Auf dieser Seite werden die Grundlagen des Renderings mit dem neuen System behandelt, wobei die wichtigsten Begriffe und Konzepte erläutert werden.
Obwohl ein Großteil des Renderings in Minecraft durch die verschiedenen Methoden von GuiGraphics abstrahiert wird und du wahrscheinlich nichts von dem, was hier erwähnt wird, anfassen musst, ist es trotzdem wichtig, die Grundlagen zu verstehen, wie Rendering funktioniert.
Der Tesselator
Der Tesselator ist die Hauptklasse, die zum Rendern von Dingen in Minecraft verwendet wird. Es ist ein Singleton, das heißt es gibt nur eine Instanz davon im Spiel. Du kannst die Instanz mit Tesselator.getInstance() erhalten.
Der BufferBuilder
Der BufferBuilder ist die Klasse, die zum Formatieren und Hochladen von Rendering-Daten in OpenGL verwendet wird. Sie wird verwendet, um einen Puffer zu erstellen, der dann zum Zeichnen in OpenGL hochgeladen wird.
Der Tesselator wird verwendet, um einen BufferBuilder zu erstellen, der zum Formatieren und Hochladen von Rendering-Daten in OpenGL verwendet wird.
Den BufferBuilder initialisieren
Bevor du etwas in den BufferBuilder schreiben kannst, musst du ihn initialisieren. Dies geschieht mit der Methode Tesselator#begin(...), die ein VertexFormat und einen Zeichenmodus entgegennimmt und einen BufferBuilder zurückgibt.
Vertex Formate
Das VertexFormat definiert die Elemente, die wir in unseren Datenpuffer aufnehmen und umreißt, wie diese Elemente an OpenGL übertragen werden sollen.
Die folgenden Standard VertexFormat Elemente stehen in DefaultVertexFormat zur Verfügung:
| Element | Format |
|---|---|
EMPTY | { } |
BLOCK | { position, color, texture uv, texture light (2 shorts), texture normal (3 sbytes) } |
NEW_ENTITY | { position, color, texture uv, overlay (2 shorts), texture light, normal (3 sbytes) } |
PARTICLE | { position, texture uv, color, texture light } |
POSITION | { position } |
POSITION_COLOR | { position, color } |
POSITION_COLOR_NORMAL | { position, color, normal } |
POSITION_COLOR_LIGHTMAP | { position, color, light } |
POSITION_TEX | { position, uv } |
POSITION_TEX_COLOR | { position, uv, color } |
POSITION_COLOR_TEX_LIGHTMAP | { position, color, uv, light } |
POSITION_TEX_LIGHTMAP_COLOR | { position, uv, light, color } |
POSITION_TEX_COLOR_NORMAL | { position, uv, color, normal } |
Zeichenmodi
Der Zeichenmodus legt fest, wie die Daten gezeichnet werden. Die folgenden Zeichenmodi sind in VertexFormat.Mode verfügbar:
| Zeichenmodus | Beschreibung |
|---|---|
LINES | Jedes Element besteht aus 2 Eckpunkten und wird als eine einzige Linie dargestellt. |
LINE_STRIP | Das erste Element benötigt 2 Eckpunkte. Zusätzliche Elemente werden nur mit einem neuen Eckpunkt gezeichnet, wodurch eine durchgehende Linie entsteht. |
DEBUG_LINES | Ähnlich wie Mode.LINES, aber die Linie ist immer genau ein Pixel breit auf dem Bildschirm. |
DEBUG_LINE_STRIP | Wie Mode.LINE_STRIP, aber die Linien sind immer ein Pixel breit. |
TRIANGLES | Jedes Element besteht aus 3 Eckpunkten, die ein Dreieck bilden. |
TRIANGLE_STRIP | Beginnt mit 3 Eckpunkten für das erste Dreieck. Jeder weitere Eckpunkt bildet ein neues Dreieck mit den letzten beiden Eckpunkten. |
TRIANGLE_FAN | Beginnt mit 3 Eckpunkten für das erste Dreieck. Jeder weitere Scheitelpunkt bildet ein neues Dreieck mit dem ersten und dem letzten Scheitelpunkt. |
QUADS | Jedes Element besteht aus 4 Scheitelpunkten, die ein Viereck bilden. |
In den BufferBuilder schreiben
Sobald der BufferBuilder initialisiert ist, kannst du Daten in ihn schreiben.
Der BufferBuilder erlaubt uns, unseren Puffer Punkt für Punkt zu konstruieren. Um einen Vertex hinzuzufügen, verwenden wir die Methode buffer.addVertex(Matrix4f, float, float, float). Der Parameter Matrix4f ist die Transformationsmatrix, auf die wir später noch näher eingehen werden. Die drei Float-Parameter stellen die (x, y, z) Koordinaten der Eckpunktposition dar.
Diese Methode gibt einen Eckpunkt-Builder zurück, den wir verwenden können, um zusätzliche Informationen für den Eckpunkt anzugeben. Es ist wichtig, dass die Reihenfolge der von uns definierten VertexFormat beim Hinzufügen dieser Informationen eingehalten wird. Andernfalls könnte OpenGL unsere Daten nicht richtig interpretieren. Nachdem wir mit der Erstellung eines Scheitelpunktes fertig sind, füge einfach weitere Scheitelpunkte und Daten in den Puffer ein, bis du fertig bist.
Es lohnt sich auch, das Konzept des Culling zu verstehen. Culling ist der Prozess, bei dem Flächen einer 3D-Form entfernt werden, die aus der Perspektive des Betrachters nicht sichtbar sind. Wenn die Eckpunkte für eine Fläche in der falschen Reihenfolge angegeben werden, wird die Fläche aufgrund von Culling möglicherweise nicht korrekt dargestellt.
Was ist eine Transformationsmatrix?
Eine Transformationsmatrix ist eine 4x4-Matrix, die zur Transformation eines Vektors verwendet wird. In Minecraft transformiert die Transformationsmatrix lediglich die Koordinaten, die wir in den Aufruf von addVertex hineingeben. Mit den Transformationen kann unser Modell skaliert, verschoben und gedreht werden.
Sie wird manchmal auch als Positionsmatrix oder als Modellmatrix bezeichnet.
Es wird normalerweise über die Klasse Matrix3x2fStack bezogen, die über das Objekt von GuiGraphics über einen Aufruf von GuiGraphics#pose() bezogen werden kann.
Ein praktisches Beispiel: Rendering eines Dreiecksstreifens
Es ist einfacher, anhand eines praktischen Beispiels zu erklären, wie man in den BufferBuilder schreibt. Nehmen wir an, wir wollen etwas mit dem Zeichenmodus VertexFormat.Mode.TRIANGLE_STRIP und dem Vertexformat POSITION_COLOR rendern.
Wir werden Eckpinkt an den folgenden Punkten auf dem HUD zeichnen (in dieser Reihenfolge):
text
(20, 20)
(5, 40)
(35, 40)
(20, 60)Dies sollte einen schönen Diamanten ergeben - da wir den Zeichenmodus TRIANGLE_STRIP verwenden, wird der Renderer die folgenden Schritte durchführen:

Da wir in diesem Beispiel auf dem HUD zeichnen, werden wir die HudElementRegistry verwenden:
WICHTIGES UPDATE
Ab Version 1.21.8 wurde der für die HUD-Rendering übergebene Matrixstapel von PoseStack zu Matrix3x2fStack geändert. Die meisten Methoden unterscheiden sich geringfügig und verwenden keinen Parameter z mehr, aber die Konzepte sind dieselben.
Außerdem stimmt der folgende Code nicht vollständig mit der obigen Erklärung überein: Du musst nicht manuell in den BufferBuilder schreiben, da die Methoden von GuiGraphics während der Vorbereitung automatisch in den BufferBuilder des HUD schreiben.
Lies für weitere Informationen die wichtige Aktualisierung oben.
Registrierung des Elements:
java
HudElementRegistry.addLast(Identifier.fromNamespaceAndPath(ExampleMod.MOD_ID, "last_element"), hudLayer());1
Implementation von hudLayer():
java
private HudElement hudLayer() {
return (graphics, deltaTracker) -> {
// :::2
Matrix3x2fStack matrices = graphics.pose();
// Store the total tick delta in a field, so we can use it later.
totalTickProgress += deltaTracker.getGameTimeDeltaPartialTick(true);
// Push a new matrix onto the stack.
matrices.pushMatrix();
// :::2
// :::2
// Scale the matrix by 0.5 to make the triangle smaller and larger over time.
float scaleAmount = Mth.sin(totalTickProgress / 10F) / 2F + 1.5F;
// Apply the scaling amount to the matrix.
// We don't need to scale the Z axis since it's on the HUD and 2D.
matrices.scale(scaleAmount, scaleAmount);
// :::2
matrices.scale(1 / scaleAmount, 1 / scaleAmount);
matrices.translate(60f, 60f);
// :::3
// Lerp between 0 and 360 degrees over time.
float rotationAmount = totalTickProgress / 50F % 360;
matrices.rotate(rotationAmount);
// Shift entire square so that it rotates in its center.
matrices.translate(-20f, -40f);
// :::3
// :::2
// We do not need to manually write to the buffer. GuiGraphics methods write to GUI buffer in `GuiRenderer` at the end of preparation.
// Pop our matrix from the stack.
matrices.popMatrix();
// :::2
};
}1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
Dies führt dazu, dass auf dem HUD folgendes gezeichnet wird:

TIP
Versuche, mit den Farben und Positionen der Eckpunkte herumzuspielen, um zu sehen, was passiert! Du kannst auch verschiedene Zeichenmodi und Vertex-Formate ausprobieren.
Der PoseStack
WARNING
Der Code und der Text in diesem Abschnitt behandeln unterschiedliche Themen!
Der Code zeigt Matrix3x2fStack, das seit 1.21.8 für das HUD-Rendering verwendet wird, während der Text PoseStack beschreibt, das leicht abweichende Methoden hat.
Lies für weitere Informationen die wichtige Aktualisierung oben.
Nachdem du gelernt hast, wie man in den BufferBuilder schreibt, fragst du dich vielleicht, wie du dein Modell transformieren oder sogar animieren kannst. Hier kommt die Klasse PoseStack ins Spiel.
Die Klasse PoseStack hat folgende Methoden:
pushPose()- Schiebt eine neue Matrix auf den Stack.popPose()- Nimmt die oberste Matrix vom Stapel.popPose()- Gibt die oberste Matrix des Stapels zurück.translate(x, y, z)- Verschiebt die oberste Matrix auf dem Stapel.translate(vec3)scale(x, y, z)- Skaliert die oberste Matrix auf dem Stapel.
Du kannst auch die oberste Matrix auf dem Stapel mit Quaternionen multiplizieren, was wir im nächsten Abschnitt behandeln werden.
Ausgehend von unserem obigen Beispiel können wir unseren Diamanten nach oben und unten skalieren, indem wir den PoseStack und tickDelta verwenden - was der "Fortschritt" zwischen dem letztem Spieltick und dem nächsten Spieltick ist. Wir werden dies später auf der Seite Rendering im HUD erläutern.
WARNING
Du musst zuerst den Matrixstapel schieben und ihn dann wieder herausnehmen, wenn du damit fertig bist. Wenn du dies nicht tust, erhältst du einen beschädigten Matrixstapel, was zu Darstellungsproblemen führt.
Stelle sicher, dass du den Matrixstapel verschiebst, bevor du eine Transformationsmatrix erhältst!
java
Matrix3x2fStack matrices = graphics.pose();
// Store the total tick delta in a field, so we can use it later.
totalTickProgress += deltaTracker.getGameTimeDeltaPartialTick(true);
// Push a new matrix onto the stack.
matrices.pushMatrix();
// Scale the matrix by 0.5 to make the triangle smaller and larger over time.
float scaleAmount = Mth.sin(totalTickProgress / 10F) / 2F + 1.5F;
// Apply the scaling amount to the matrix.
// We don't need to scale the Z axis since it's on the HUD and 2D.
matrices.scale(scaleAmount, scaleAmount);
// We do not need to manually write to the buffer. GuiGraphics methods write to GUI buffer in `GuiRenderer` at the end of preparation.
// Pop our matrix from the stack.
matrices.popMatrix();1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17

Quaternionen (rotierende Dinge)
WARNING
Der Code und der Text in diesem Abschnitt behandeln unterschiedliche Themen!
Der Code zeigt das Rendering auf dem HUD, während der Text das Rendern des 3D-Weltraums beschreibt.
Lies für weitere Informationen die wichtige Aktualisierung oben.
Quaternionen sind eine Methode zur Darstellung von Drehungen im 3D-Raum. Sie werden verwendet, um die oberste Matrix auf dem PoseStack über die Methode rotateAround(quaternionfc, x, y, z) zu rotieren.
Es ist sehr unwahrscheinlich, dass du jemals eine Quaternion-Klasse direkt verwenden musst, da Minecraft verschiedene vorgefertigte Quaternion-Instanzen in seiner Axis Hilfsklasse bereitstellt.
Nehmen wir an, wir wollen unseren Quader um die Z-Achse drehen. Wir können dies tun, indem wir den PoseStack und die Methode rotateAround(quaternionfc, x, y, z) verwenden.
java
// Lerp between 0 and 360 degrees over time.
float rotationAmount = totalTickProgress / 50F % 360;
matrices.rotate(rotationAmount);
// Shift entire square so that it rotates in its center.
matrices.translate(-20f, -40f);1
2
3
4
5
2
3
4
5
Daraus ergibt sich Folgendes:




