Unity Hintergrundbild Unschärfe



Eine der Aufgaben eines Unity-Anwendungsentwicklers besteht darin, das aktuelle Bild in den Hintergrund zu stellen, um die Aufmerksamkeit des Benutzers auf etwas Neues zu lenken, z. B. ein Menü oder eine Meldung, die angezeigt wird. Der Artikel berichtet über die Erfahrungen mit der Lösung dieses Problems durch einen Entwickler, der über Grundkenntnisse in Unity verfügt, ohne externe Ressourcen oder eine zusätzliche Unity-Lizenz zu verwenden. Ich hoffe, dass dieses Material für diejenigen nützlich sein wird, die mit einem ähnlichen Problem konfrontiert waren und in verschiedenen Phasen keine wirksamen Lösungen gefunden haben.

Das Hervorheben wichtiger Oberflächenelemente (und das Entfernen unnötiger Elemente) ist eines der Hauptwerkzeuge zur Verbesserung der Benutzererfahrung. Dies ist besonders ausgeprägt bei der Arbeit mit mobilen Anwendungen. Dieses Umschalten der Aufmerksamkeit kann auf verschiedene Arten erfolgen - durch reibungsloses Ausführen von Schaltflächen am Bildschirmrand, Dimmen des Hintergrunds, dynamisches Verhalten usw. Dazu gehört das Verwischen des Bildes. Dieser Effekt verringert einerseits den Kontrast von Hintergrundelementen und es wird für das Auge schwieriger, ihn zu erfassen. Auf der anderen Seite hat Unschärfe eine unbewusste Wirkung, wenn das Bild unscharf wird und wir es anders wahrnehmen. Dieser Ansatz wird häufig in Spielen verwendet, und bekannte Beispiele für das Unity-Spiel können als Menüauswahl für Momente-Quests Hearthstone oder Hearthstone bezeichnet werdenDuell-Abschlussbildschirm .

Stellen Sie sich nun vor, wir möchten in unserer Lieblingsanwendung dasselbe oder ähnliches tun. Wir sind auch nicht ohne Budget und haben wenig Erfahrung mit Unity.

Teil Eins - Shader


Der erste Gedanke war, dass alles schon realisiert sein sollte. Sicherlich mit einem Shader. Und es sollte auf jeden Fall in der Unity-Box sein. Ein schneller Such- und Installationsversuch hat gezeigt, dass es in Unity einen solchen Shader gibt, der sich jedoch als Teil von Standard Assets herausstellt und nicht für eine kostenlose Lizenz verfügbar ist.

Aber das ist Unschärfe! Ein grundlegender Effekt, der mathematisch einfach ist und auch leicht implementiert und in das Projekt integriert werden sollte. Auch wenn wir nichts über Shader wissen. Gehen Sie als Nächstes ins Internet, und die Suche ergab schnell, dass es Quellcodes von vorgefertigten Shadern für den Effekt gibt, und sogar verschiedene.

Für die erste Implementierung wurde dieser Shader verwendet . Um es zu testen, fügen Sie einfach den Shader als Quelldatei zum Projekt hinzu, ordnen Sie ihn dem Material zu und binden Sie das Material an Image.

Teil Zwei - Benutzeroberfläche


Wenn wir uns um den Benutzer der Anwendung kümmern, ist es notwendig, die Benutzeroberfläche genau zu beachten. Wenn Sie dem Hintergrund nur einen Unschärfeeffekt hinzufügen und ihn vergessen, entspricht dies nicht den internen Prinzipien für die Erstellung eines Produkts.

Daher müssen Sie für weitere Aktionen das Geschehene berühren und dann so denken und anpassen, dass der Benutzer die erforderliche Wahrnehmung erhält. Dieser Punkt ist sehr kontextsensitiv. Abhängig vom Bildkontrast, der Farbvielfalt und anderen Faktoren können verschiedene Werte und zusätzliche Werkzeuge ausgewählt werden. In unserem Fall wurde zu diesem Zeitpunkt ein gutes Ergebnis mit einem Unschärferadius von 25 (Shader-Parameter) und einem zusätzlichen Transparent von 70% (außerhalb des Shaders durch ein separates Bild) erzielt. Dies ist jedoch nur das endgültige Hintergrundbild. Der Übergang selbst war nach den Gefühlen am Telefon zu scharf.

Der im Bild installierte Shader wird für jeden Frame berechnet. Um ein reibungsloses Umschalten zu gewährleisten, reicht es daher aus, den Parameter und die Transparenz des Unschärferadius dynamisch zu ändern. Sie können dies auf verschiedene Arten organisieren, aber im Wesentlichen handelt es sich bei der Verarbeitung je nach Zeit um ein Update in den Update-Handlern. Der Übergang selbst in der endgültigen Version der Anwendung beträgt 0,2 Sekunden, aber wie sich herausstellt, ist er für die Wahrnehmung des Benutzers wichtig.

Dritter Teil - Produktivität


In den letzten Monaten habe ich Beschwerden von mehreren verschiedenen Benutzern (nicht einmal Programmierern) gehört, dass Spielprogramme häufig alle verfügbaren Computerressourcen im Leerlauf und ohne Bremsen nutzen. Dies kann zum Beispiel in der maximalen Nutzung der Grafikkarte im Fall eines auf das Fach minimierten Programms oder unabhängig von allem und dem konstanten Laden eines Prozessorthreads ausgedrückt werden. Die Gründe dafür sind intuitiv klar: Das Einkommen des Entwicklers oder Herausgebers hängt nicht von der Optimierung ab (die Leute achten beim Kauf nicht auf solche Dinge), und normale Entwickler sind höchstwahrscheinlich eher besorgt über lokale KPIs und die Notwendigkeit, Fristen zu verschieben. In der Praxis möchte ich jedoch nicht in diese Kategorie fallen.

Nachdem Sie die vorherige Phase beim nächsten Start mit einem Hintergrundunschärfeeffekt abgeschlossen hatten und das Telefon einige Zeit in der Hand gehalten hatten, trat ein Gefühl der Erwärmung auf. Wenn Sie mehr Zeit auf der Speisekarte verbringen, wird alles viel schlimmer. Und selbst wenn der Benutzer im Menü vermutlich und höchstwahrscheinlich nicht viel Zeit verbringt, möchte ich den Akku überhaupt nicht fallen lassen.

Die Analyse zeigte, dass der oben ausgewählte Shader abhängig vom ausgewählten Radius eine asymptotische Komplexität O (r 2 ) aufweist. Mit anderen Worten, die Anzahl der Berechnungen pro Punkt wird 625-mal größer, wenn Sie den Unschärferadius von 1 auf 25 erhöhen. In diesem Fall werden die Berechnungen für jeden Frame ausgeführt.

Der erste Schritt in der Lösung war die Idee, dass nicht alle Shader gleichermaßen nützlich sind und der Unschärfeeffekt auf verschiedene Arten implementiert werden kann. Zu diesem Zweck können Sie eine separate Unschärfe verwenden, deren Kern darin besteht, zuerst nur die Linien horizontal und dann nur die Linien vertikal zu verwischen. Als Ergebnis erhalten wir O (r) und ceteris paribus sinkt die Komplexität um eine Größenordnung. Ein zusätzlicher Weg wäre, eine kleinere Mipmap zu erstellen, die bereits einen Teil der Unschärfe übernimmt. Dieser Shader

wurde als Grundlage genommen. Der Unschärfeeffekt erwies sich jedoch als unzureichend, und bei hohem Kontrast der Grafik wurde das Bild „abgebrochen“. Um bessere Effekte zu erzielen, wurde die Verteilung geändert (GRABPIXEL-Elemente). Wenn im Shader der Link zwischen 0,18 und 0,05 des Radius 4 liegt, dann in unserer Version zwischen 0,14 und 0,03 des Radius 6 (ess, aber die Summe aller sollte 1 sein).

Somit wurde die Verarbeitungskomplexität um 1-2 Größenordnungen reduziert. Dies ist jedoch nicht notwendig, um aufzuhören.

Vierter Teil - Statischer Hintergrund


Wenn wir den Shader jedoch auf dem vorhandenen Bild belassen, ist er ständig in Arbeit und führt für jeden Frame die gleichen Berechnungen durch. Wenn etwas im Hintergrund passiert, müssen wir es tun. Dies ist jedoch völlig optional, wenn der Hintergrund statisch ist. So können Sie sich nach einer dynamischen Unschärfe an das Ergebnis erinnern, es in den Hintergrund stellen und den Shader ausschalten.

Als nächstes versuchen wir es mit Codefragmenten. Das Vorbereiten der Zieltextur für den gesamten Bildschirm kann folgendermaßen aussehen:

_width = Screen.width / 2;
_height = Screen.height / 2;
_texture = new Texture2D(_width, _height);

var fillColor = Color.white;
var fillColorArray = _texture.GetPixels();
for (var i = 0; i < fillColorArray.Length; ++i)
{
	fillColorArray[i] = fillColor;
}
_texture.SetPixels(fillColorArray);
_texture.Apply();

_from.GetComponent<Image>().sprite = Sprite.Create(_texture, new Rect(0, 0, _texture.width, _texture.height), new Vector2(0.5f, 0.5f));

Das heißt, hier wird eine Textur vorbereitet, die horizontal und vertikal vom Bildschirm zweimal kleiner ist. Dies ist fast immer möglich, da wir wissen, dass die Bildschirmgrößen von Geräten ein Vielfaches von 2 sind. Wenn dies durchgeführt wird, wird die Größe der Textur verringert und das Verwischen erleichtert.

RenderTexture temp = RenderTexture.GetTemporary(_width, _height);
Graphics.Blit(_from_image.mainTexture, temp, _material);
RenderTexture.active = temp;

_texture.ReadPixels(new Rect(0, 0, temp.width, temp.height), 0, 0, false);
_texture.Apply();

_to_image.sprite = Sprite.Create(_texture, new Rect(0, 0, _texture.width, _texture.height), new Vector2(0.5f, 0.5f));
RenderTexture.ReleaseTemporary(temp);

Um ein Bild mit mainTexture mit einem Shader (auf _material) aufzunehmen, wird Graphics.Blit verwendet. Als nächstes merken wir uns das Ergebnis in der vorbereiteten Textur und platzieren es im Zielbild.

Alles wäre in Ordnung, in der Praxis war er mit einem solchen Effekt konfrontiert, dass das Hintergrundbild je nach Gerät auf den Kopf gestellt wird. Der Grund nach einigen Untersuchungen des Problems und dem Debuggen wird klar - der Unterschied in den KoordinatensystemenDirect3D und OpenGL, die Unity nicht verbergen kann. Gleichzeitig erkennt unser Shader UV-Strahlen und verarbeitet sie korrekt. Die Inversion erfolgt bereits in der Unity-Umgebung (ReadPixels). Es gibt viele Tipps im Netz, z. B. "Wenn Sie auf den Kopf gestellt haben, drehen Sie es selbst um" oder "Ändern Sie das UV-Zeichen". Die Verwendung des Makros UNITY_UV_STARTS_AT_TOP hat nicht dazu beigetragen, eine gute Verallgemeinerung für alle getesteten Geräte zu erhalten. Darüber hinaus ist beispielsweise ein solcher Fall aufgetreten, dass Sie, wenn Sie die Assembly für die Emulation in Xcode auf dem iPhone vorbereiten und Unity nicht Metal, sondern nur OpenGLES verwendet, Fälle wie die Emulation eines Geräts abfangen müssen, das auf einer anderen Software ausgeführt wird.

Nachdem ich einige Optionen ausprobiert hatte, entschied ich mich für zwei Tablets. Der erste ist das Erzwingen des Renderns der Kamera(Festlegen von forceIntoRenderTexture zum Zeitpunkt der Bildaufnahme). Die zweite ist die Bestimmung des Typs des Grafiksystems im laufenden Betrieb über SystemInfo.graphicsDeviceType, wodurch OpenGL-ähnlich oder Direct3D-ähnlich definiert werden kann (die erste Gruppe ist OpenGLES2, OpenGLES3, OpenGLCore und Vulkan). Außerdem müssen wir in unserer Implementierung für Direct3D-like umdrehen, was prozedural erfolgt (zum Beispiel so ).

Fazit


Ich hoffe, dieser Artikel wird jemandem helfen, einen unbekannten Rechen zu überwinden, das Leben der Benutzer zu verbessern und Unity zu verstehen. Wenn man sich die Wirkung im Kampf Gebrauch sucht, dann in Unity sieht es aus wie diese . In der Anwendung sind die Empfindungen etwas unterschiedlich, aber diese Animation kann eine ausreichende Vorstellung geben.

All Articles