
Assembler ist meine Lieblingssprache ... aber das Leben ist so kurz.
Ich setze den Forschungszyklus zum Thema geeignete Schatten für einige Bagels fort. Nach der Veröffentlichung habe
ich mich für dieses Thema
ein- und
zweimal abgekühlt, aber der Effekt unvollständiger Aktionen veranlasst mich, zu den Trümmern der Pixel zurückzukehren und die
Gestalt abzuschließen.
Wenn ich mich selbst kenne, bin ich mir sicher, dass das Spiel kaum seine Verkörperung bekommen wird, aber vielleicht wird ein Teil der Öffentlichkeit an meinen Erfolgen auf diesem heiklen Weg interessiert sein. Und so fangen wir an.
Am Ende des letzten Zyklus kam ich zu dem Schluss, dass die Berechnung von Grafiken auf einer CPU bereits das letzte Jahrhundert war, aber die natürliche Hartnäckigkeit bestand darauf: Nicht alle Möglichkeiten wurden genutzt, es gab immer noch Optionen für interessante Lösungen.
Die Strahlverfolgung blieb nicht implementiert. Genauer gesagt, seine Art, bei der für jedes Pixel des Bildes (Pixelblock) ein Strahl geworfen wird und der Beleuchtungsgrad des aktuellen Punktes bestimmt wird. Der Algorithmus selbst wurde in einem früheren Artikel beschrieben, und es macht keinen Sinn, darauf zurückzukommen. Für die Rückstrahlverfolgung wurde der Code noch vereinfacht, die gesamte Trigonometrie wurde vollständig entfernt, was in Zukunft zu einem akzeptablen Ergebnis führen könnte.
Leider war das Ergebnis viel schlechter als erwartet, es hat sich gelohnt, das Bild im Vollbildmodus bereitzustellen, FPS suchte nach Einheiten.

Das Gruppieren von Pixeln in Makroblöcke, um Berechnungen zu reduzieren und das anschließende Glätten anzuwenden, verbesserte die Leistung nicht wesentlich. Der Effekt gefiel das Wort offen gesagt überhaupt nicht.

Der Algorithmus war perfekt parallel, aber es war nicht sinnvoll, viele Streams zu verwenden. Der Effekt schien viel schlechter zu sein als im vorherigen Artikel, selbst bei besserer Bildqualität.
Es stellte sich als Sackgasse heraus. Ich musste zugeben, die CPU bei der Berechnung von Grafiken in meinen Augen hat sich erschöpft. Der Vorhang.
Exkurs 1In den letzten zehn Jahren wurden bei der Entwicklung von Allzweckprozessoren praktisch keine Fortschritte erzielt. Wenn sich der Benutzer nähert, beträgt die maximal wahrnehmbare Leistungssteigerung nicht mehr als 30% pro Kern. Fortschritte sind, gelinde gesagt, unbedeutend. Wenn wir die Verlängerung der Länge der Vektorbefehle und eine gewisse Beschleunigung der Förderblöcke weglassen, ist dies eine Zunahme der Anzahl der Arbeitskerne. Das sichere Arbeiten mit Threads ist immer noch ein Vergnügen, und nicht alle Aufgaben können erfolgreich parallelisiert werden. Ich hätte gerne einen funktionierenden Kern, wenn auch einen, aber wenn ja, ist er 5-10 schneller, aber leider und oh, wie sie sagen.
Hier auf Habré gibt es eine ausgezeichnete Artikelserie
"Leben in der Ära des" dunklen "Siliziums", die einige der Voraussetzungen für den aktuellen Stand der Dinge erklärt, aber auch vom Himmel auf die Erde zurückkehrt. In den nächsten zehn Jahren können Sie keinen signifikanten Anstieg der Datenverarbeitung pro Kern erwarten. Wir können jedoch eine weitere Entwicklung der Anzahl der GPU-Kerne und ihrer Gesamtbeschleunigung erwarten. Selbst auf meinem alten Laptop ist die geschätzte Gesamtleistung der GPU 20-mal höher als bei einem einzelnen CPU-Thread. Selbst wenn Sie alle 4 Prozessorkerne effektiv laden, ist dies viel weniger, als wir möchten.
Ich würdige die Entwickler der Grafiken der Vergangenheit, die ihre Meisterwerke ohne Hardwarebeschleuniger zu echten Meistern gemacht haben.
Wir beschäftigen uns also mit der GPU. Es stellte sich für mich als etwas unerwartet heraus, dass in der Praxis nur wenige Menschen Polygone einfach in Form streuen. Alle interessanten Dinge werden mit
Shadern erstellt . Nachdem ich die fertigen 3D-Engines verworfen hatte, versuchte ich, die Innereien der Technologie zu untersuchen, da sie sich auf einer tiefen Ebene befindet. Dieselben Prozessoren sind dieselben Assembler, nur wenige abgeschnittene Anweisungen und ihre eigenen Arbeitsspezifikationen. Für den Test habe ich bei
GLSL angehalten, einer C-ähnlichen Syntax, Einfachheit, vielen Trainingsstunden und Beispielen, einschließlich des Habr.
Da ich es meistens gewohnt war, in
Pascal zu schreiben, bestand die Aufgabe darin, OpenGL zu verbinden
zum Projekt. Es gelang mir, zwei Verbindungsmöglichkeiten zu finden: die
GLFW- Bibliothek und die
dglOpenGL- Headerdatei. Das einzige, was ich im ersten Moment nicht verbinden konnte, aber anscheinend liegt das an der Krümmung meiner Hände.
Exkurs 2Viele Freunde fragen mich, warum ich in Pascal schreibe? Offensichtlich ist dies eine gefährdete Sprache, ihre Gemeinschaft nimmt stetig ab, es gibt fast keine Entwicklung. Low-Level-Systemingenieure bevorzugen C und Java, Python, Ruby oder was auch immer gerade auf dem Höhepunkt ist.
Für mich ist Pascal mit der ersten Liebe verwandt. Vor zwei Jahrzehnten, in den Tagen von
Turbo Pascal 5.5 , versank es in meiner Seele und ist seitdem mit mir gegangen, sei es Delphi oder in den letzten Jahren
Lazarus . Ich mag die Vorhersagbarkeit der Sprache, die relativ niedrige Ebene (Assembler-Einfügungen und Anzeigen der Prozessoranweisung), die Kompatibilität mit C. Die Hauptsache ist, dass der Code ohne Probleme zusammengestellt und ausgeführt wird, aber nicht in Mode ist, veraltet ist und einige Funktionen vorhanden sind, das ist Unsinn. Gerüchten zufolge gibt es Leute, die immer noch über
LISP schreiben, aber für ihn im Allgemeinen seit einem halben Jahrhundert.
Tauchen wir also in die Entwicklung ein. Für einen Testschritt werden wir keine genauen realistischen Schattierungsmodelle verwenden, sondern versuchen, das zu implementieren, was wir bereits zuvor versucht haben, aber sozusagen mit GPU-Leistung für einen klaren Vergleich.
Anfangs dachte ich daran, einen Schatten von ungefähr dieser Form zu erhalten, indem ich Dreiecke für ein Objekt verwendete.

Um den Effekt eines glatten Kreises zu erzielen, benötigen Sie viele Polygone. Was aber, wenn Sie nur Dreiecke verwenden und einen Pixel-Shader verwenden, um ein Loch in der Form zu erstellen? Die Idee kam mir, nachdem ich einen
Artikel eines angesehenen Meisters gelesen hatte, in dem die Gelegenheit eröffnet wurde, eine Kugel mit einem Shader zu erstellen.

Wenn Sie das Dreieck über die Bildschirmgrenzen hinaus erweitern, lautet das Ergebnis wie folgt:

Die Ränder des Schattens erwiesen sich als sehr starr und auch abgestuft. Es gibt jedoch eine Möglichkeit, ein akzeptables Ergebnis zu
erzielen , ohne
Supersampling zu verwenden . Dabei werden geglättete Ränder verwendet. Ändern Sie dazu das Schema leicht. Die Ecken der Polygone am Schnittpunkt der Tangente an den Kreis werden transparent.

Das Ergebnis ist besser, aber es sieht immer noch unnatürlich aus.

Fügen Sie eine kleine Glättung des Kreises hinzu, um Weichheit zu erzielen, und ändern Sie auch die Form des Verlaufs von linear zu Leistung.

Es ist ein akzeptables Ergebnis.
Fügen Sie am Ende der Form Objekte hinzu, die Hindernisse imitieren.

Shader-Code//
#version 330 core
layout (location = 0) in vec2 aVertexPosition;
void main(void) {
gl_Position = vec4(aVertexPosition.xy, 0, 1.0);
}
//
#version 330 core
layout (points) in;
layout (triangle_strip, max_vertices = 5) out;
uniform mat4 uModelViewMatrix;
uniform float uRadius;
uniform vec2 uHeroPoint;
out float fTransparency;
out vec2 vCenter;
void main(){
vCenter = gl_in[0].gl_Position.xy;
vec2 d = uHeroPoint - vCenter;
float l = length(d);
float i = uRadius / l;
float ii = i*i;
float ij = i * sqrt(1 - ii);
vec2 p1 = vec2(vCenter.x + dx*ii - dy*ij , vCenter.y + dx*ij + dy*ii);
vec2 p2 = vec2(vCenter.x + dx*ii + dy*ij , vCenter.y - dx*ij + dy*ii);
d = uHeroPoint - p1;
vec2 p3 = vec2(p1 - d/length(d)*1000000);
d = uHeroPoint - p2;
vec2 p4 = vec2(p2 - d/length(d)*1000000);
fTransparency = 0;
gl_Position = uModelViewMatrix * vec4(p1, 0, 1);
EmitVertex();
fTransparency = 1;
gl_Position = uModelViewMatrix * vec4(p3, 0, 1);
EmitVertex();
gl_Position = uModelViewMatrix * vec4(vCenter, 0, 1);
EmitVertex();
gl_Position = uModelViewMatrix * vec4(p4, 0, 1);
EmitVertex();
fTransparency = 0;
gl_Position = uModelViewMatrix * vec4(p2, 0, 1);
EmitVertex();
EndPrimitive();
}
//
#version 330 core
precision mediump float;
varying float fTransparency;
varying vec2 vCenter;
uniform float uRadius;
uniform vec2 uScreenHalfSize;
uniform float uShadowTransparency;
uniform float uShadowSmoothness;
out vec4 FragColor;
void main(){
float l = distance(vec2((gl_FragCoord.xy - uScreenHalfSize.xy)/uScreenHalfSize.y), vCenter.xy);
if (l<uRadius) {discard;}
else {FragColor = vec4(0, 0, 0, min(pow(fTransparency, uShadowSmoothness), (l-uRadius)/uRadius*10)*uShadowTransparency);}
}
Ich hoffe es war informativ
Dein bescheidener Diener, Pixelquäler, Wiederaufbauer.
Ich lege eine kleine
Demo bei . (EXE Windows)
PS Der Titel des Artikels enthält ein
Osterei , ein Verweis auf die
Siala Chronicle- Trilogie. Eine exzellente Arbeit im Stil der Fantasie über das Unglück der Hörner von Alexei Pekhov.