In den letzten Monaten hat Facebook
3D-Fotos überflutet. Wenn Sie sie nicht sehen konnten, erkläre ich Folgendes: 3D-Fotos sind Bilder innerhalb des Beitrags, die den Winkel beim Scrollen der Seite oder beim Bewegen der Maus über sie reibungslos ändern.
Einige Monate vor Erscheinen dieser Funktion testete Facebook eine ähnliche Funktion mit 3D-Modellen. Obwohl Sie leicht verstehen können, wie Facebook 3D-Modelle rendern und entsprechend der Position der Maus drehen kann, ist die Situation bei 3D-Fotos möglicherweise nicht so intuitiv.
Die Technik, mit der Facebook zweidimensionale Bilder dreidimensional erzeugt, wird manchmal als
Höhenkartenversatz bezeichnet . Es verwendet ein optisches Phänomen namens
Parallaxe .
Beispiel eines 3D-Facebook-Fotos (GIF) Was ist Parallaxe?
Wenn Sie Super Mario gespielt haben, wissen Sie genau, was Parallaxe ist. Obwohl Mario mit der gleichen Geschwindigkeit läuft, scheinen sich entfernte Objekte im Hintergrund langsamer zu bewegen (siehe unten).
Dieser Effekt erzeugt die Illusion, dass einige Elemente wie Berge und Wolken weiter entfernt sind. Es ist effektiv, weil unser Gehirn Parallaxe (zusammen mit anderen visuellen Hinweisen) verwendet, um die Entfernung zu entfernten Objekten abzuschätzen.
Wie bewertet das Gehirn die Entfernung?Es wird angenommen, dass das menschliche Gehirn verschiedene Mechanismen verwendet, um die Entfernung abzuschätzen. Bei kurzen und mittleren Entfernungen werden Entfernungen berechnet, indem Unterschiede in der Position des sichtbaren Objekts mit dem rechten und linken Auge verglichen werden. Dies wird als
stereoskopisches Sehen bezeichnet und ist in der Natur weit verbreitet.
Für ausreichend entfernte Objekte reicht jedoch eine stereoskopische Sicht nicht aus. Berge, Wolken und Sterne unterscheiden sich zu wenig, als dass verschiedene Augen einen signifikanten Unterschied bemerken könnten. Daher kommt die relative Parallaxe ins Spiel. Objekte im Hintergrund bewegen sich weniger als Objekte im Vordergrund. Es ist ihre relative Bewegung, mit der Sie den relativen Abstand einstellen können.
Bei der Wahrnehmung von Distanz werden viele andere Mechanismen verwendet. Der bekannteste von ihnen ist der atmosphärische Dunst, der entfernten Objekten einen blauen Farbton verleiht. In anderen Welten existieren die meisten dieser atmosphärischen Hinweise nicht, so dass es so schwierig ist, die Größe von Objekten auf anderen Planeten und dem Mond zu beurteilen. Der YouTube-Nutzer Alex McCulgan erklärt dies auf seinem
Astrum- Kanal und zeigt, wie schwierig es ist, die Größe der im Video gezeigten Mondobjekte zu bestimmen.
Parallaxe als Verschiebung
Wenn Sie mit linearer Algebra vertraut sind, wissen Sie wahrscheinlich, wie kompliziert und nicht trivial die Mathematik von 3D-Rotationen sein kann. Daher gibt es eine viel einfachere Möglichkeit, die Parallaxe zu verstehen, die nur Verschiebungen erfordert.
Stellen wir uns vor, wir betrachten einen Würfel (siehe unten). Wenn wir genau auf die Mitte ausgerichtet sind, sehen die Vorder- und Rückseite für unsere Augen wie zwei Quadrate unterschiedlicher Größe aus. Das ist die
Aussicht .
Was passiert jedoch, wenn wir die Kamera nach unten bewegen oder den Würfel nach oben heben? Bei Anwendung der gleichen Prinzipien können wir sehen, dass sich die Vorder- und Rückseite relativ zu ihrer vorherigen Position verschoben haben. Noch interessanter ist, dass sie sich relativ zueinander bewegt haben. Die Rückseite, die weiter von uns entfernt ist, als wäre sie weniger bewegt.
Wenn wir die wahren Positionen dieser Eckpunkte des Würfels in unserem projizierten Bereich berechnen möchten, müssen wir die Trigonometrie ernsthaft in Angriff nehmen. Dies ist jedoch nicht wirklich notwendig. Wenn die Bewegung der Kamera klein genug ist, können wir die Verschiebung der Scheitelpunkte approximieren und sie proportional zu ihrer Entfernung bewegen.
Das einzige, was wir bestimmen müssen, ist die Skalierung. Wenn wir X Meter nach rechts bewegen, sollte das Objekt Y Meter entfernt Z Meter verschoben haben. Wenn X klein bleibt, wird die Parallaxe eher zur Aufgabe der
linearen Interpolation als der Trigonometrie. Im Wesentlichen bedeutet dies, dass wir kleine 3D-Rotationen simulieren können, indem wir Pixel in Abhängigkeit von ihrem Abstand von der Kamera verschieben.
Erstellen Sie Tiefenkarten
Im Prinzip unterscheidet sich Facebook nicht allzu sehr von dem, was in Super Mario passiert. Für ein bestimmtes Bild werden bestimmte Pixel basierend auf dem Abstand zur Kamera in Bewegungsrichtung verschoben. Um ein 3D-Foto von Facebook zu erstellen, benötigen Sie nur das Foto selbst und eine Karte, auf der angegeben ist, wie weit jedes Pixel von der Kamera entfernt ist. Eine solche Karte hat den erwarteten Namen
"Tiefenkarte" . Je nach Kontext wird es auch als
Höhenkarte bezeichnet .
Das Fotografieren ist ziemlich einfach, aber das Erstellen der richtigen Tiefenkarte ist eine viel schwierigere Aufgabe. Moderne Geräte verwenden verschiedene Techniken. Verwenden Sie meistens zwei Kameras. Jedes macht ein Bild des gleichen Motivs, jedoch mit einer etwas anderen Perspektive. Das gleiche Prinzip wird beim
stereoskopischen Sehen angewendet, mit dem Menschen die Tiefe bei kurzen und mittleren Entfernungen bewerten. Das folgende Bild zeigt, wie das iPhone 7 Tiefenkarten aus zwei sehr nahen Bildern erstellen kann.
Details zur Implementierung einer solchen Rekonstruktion sind in dem Artikel
Instant 3D Photography beschrieben , der von
Peter Hedman und
Johannes Kopf auf der SIGGRAPH2018 vorgestellt wird.
Nach dem Erstellen einer hochwertigen Tiefenkarte wird die Simulation der Dreidimensionalität zu einer fast trivialen Aufgabe. Die eigentliche Einschränkung dieser Technik besteht darin, dass selbst wenn Sie ein grobes 3D-Modell neu erstellen können, Informationen zum Rendern von Teilen fehlen, die auf dem Originalfoto nicht sichtbar sind. Im Moment kann dieses Problem nicht gelöst werden, und daher sind alle Bewegungen, die in 3D-Fotografien sichtbar sind, eher unbedeutend.
Wir haben uns mit dem Konzept von 3D-Fotos vertraut gemacht und kurz darüber gesprochen, wie moderne Smartphones sie erstellen können. Im zweiten Teil lernen wir, wie dieselben Techniken verwendet werden können, um 3D-Fotos in Unity mithilfe von Shadern zu implementieren.
Teil 2. Parallaxen-Shader und Tiefenkarten
Shader-Vorlage
Wenn wir 3D-Fotos von Facebook mit einem Shader neu erstellen möchten, müssen wir zuerst entscheiden, was genau wir tun. Da dieser Effekt am besten mit 2D-Bildern funktioniert, wäre es logisch, eine mit Unity-Sprites kompatible Lösung zu implementieren. Wir werden einen Shader erstellen, der mit
Sprite Renderer verwendet werden kann .
Obwohl ein solcher Shader von Grund auf neu erstellt werden kann, ist es oft vorzuziehen, mit einer vorgefertigten Vorlage zu beginnen. Beginnen Sie am besten, indem Sie den vorhandenen diffusen Shader von Sprites kopieren, den Unity standardmäßig für alle Sprites verwendet. Leider enthält die Engine keine
Shader- Datei, die Sie selbst bearbeiten können.
Um es zu erhalten, müssen Sie zum
Unity-Download-Archiv gehen und das integrierte
Shader- Paket (siehe unten) für die Version der von Ihnen verwendeten Engine herunterladen.
Nach dem Extrahieren des Pakets können Sie den Quellcode aller mit Unity gelieferten Shader anzeigen. Wir interessieren uns für die Datei
Sprites-Diffuse.shader , die standardmäßig für alle erstellten Sprites verwendet wird.
Bilder
Der zweite Aspekt, der formalisiert werden muss, sind die Daten, die wir haben. Stellen Sie sich vor, wir haben sowohl das Bild, das wir animieren möchten, als auch seine Tiefenkarte. Letzteres ist ein Schwarzweißbild, in dem Schwarzweißpixel angeben, wie weit oder nah sie von der Kamera entfernt sind.
Die in diesem Tutorial verwendeten Bilder stammen aus
Dennis Hotsons Pickle-Katzenprojekt , und dies ist ohne Zweifel das Beste, das Sie heute sehen werden.
Die diesem Bild zugeordnete Höhenkarte gibt die Entfernung des Katzengesichts von der Kamera wieder.
Es ist leicht zu erkennen, wie gute Ergebnisse mit einer so einfachen Tiefenkarte erzielt werden können. Dies bedeutet, dass es einfach ist, eigene Tiefenkarten für vorhandene Bilder zu erstellen.
Die Eigenschaften
Nachdem wir alle Ressourcen haben, können wir mit dem Schreiben des Parallax-Shader-Codes beginnen. Wenn wir das Hauptbild als Sprite importieren,
_MainTex
Unity es automatisch über die Eigenschaft
_MainTex
an den Shader. Wir müssen jedoch die Tiefenkarte dem Shader zur Verfügung stellen. Dies kann mithilfe einer neuen
Shader-Eigenschaft namens
_HeightTex
. Ich habe absichtlich beschlossen, es nicht
_DepthTex
zu nennen, um es nicht mit der
Tiefenstruktur zu verwechseln (dies ist ein ähnliches Unity-Konzept, das zum Rendern der Szenentiefenkarte verwendet wird).
Um die Stärke des Effekts zu ändern, fügen wir auch die Eigenschaft
_Scale
.
Properties { ... _HeightTex ("Heightmap (R)", 2D) = "gray" {} _Scale ("Scale", Vector) = (0,0,0,0) }
Diese beiden neuen Eigenschaften sollten auch zwei Variablen mit demselben Namen entsprechen, die dem
ENDCG
CGPROGRAM
/
ENDCG
hinzugefügt werden
ENDCG
:
sampler2D _HeightTex; fixed2 _Scale;
Jetzt ist alles fertig und wir können mit dem Schreiben von Code beginnen, der den Offset ausführt.
Der erste Schritt besteht darin, den Wert aus der Tiefenkarte
tex2D
, was mit der Funktion
tex2D
kann. Da
_HeightTex
eine Schwarz-Weiß-Textur ist, können wir einfach den roten Kanal nehmen und den Rest verwerfen. Der resultierende Wert misst den Abstand in einigen willkürlichen Einheiten vom aktuellen Pixel zur Kamera.
Der Tiefenwert liegt zwischen
vorher
aber wir werden es auf das Intervall von strecken
vorher
. Auf diese Weise können Sie sowohl positive (weiße Farbe) als auch negative (schwarze Farbe) Parallaxe bereitstellen.
Theorie
Um den Parallaxeneffekt in diesem Stadium zu simulieren, müssen wir die Tiefeninformationen verwenden, um die Pixel des Bildes zu verschieben. Je näher das Pixel ist, desto stärker muss es verschoben werden. Dieser Vorgang wird in der folgenden Abbildung erläutert. Das rote Pixel aus dem
Originalbild sollte gemäß den Informationen aus der Tiefenkarte zwei Pixel nach links verschieben. Ebenso sollte das blaue Pixel zwei Pixel nach rechts verschieben.
Obwohl dies
theoretisch funktionieren sollte, gibt es keine einfachen Möglichkeiten, dies im Shader zu implementieren. Die Sache ist, dass ein Shader nach seinem Prinzip nur die Farbe des
aktuellen Pixels ändern kann. Bei der Ausführung des Shader-Codes muss ein bestimmtes Pixel auf dem Bildschirm gezeichnet werden. Wir können dieses Pixel nicht einfach an einen anderen Ort verschieben oder die Farbe des benachbarten Pixels ändern. Diese
Einschränkung der Lokalität bietet einen sehr effizienten Parallelbetrieb von Shadern, ermöglicht es uns jedoch nicht, alle Arten von Effekten zu implementieren, die trivial wären, vorausgesetzt, es gibt einen
zufälligen Zugriff für die Aufzeichnung auf jedes Pixel im Bild.
Wenn wir genau sein wollen, müssen wir die Tiefenkarte aller benachbarten Pixel abtasten, um herauszufinden, welche an die aktuelle Position verschoben werden soll (falls sollte). Wenn sich mehrere Pixel an derselben Stelle befinden sollten, können wir ihren Einfluss mitteln. Obwohl ein solches System funktioniert und das bestmögliche Ergebnis liefert, ist es äußerst ineffizient und möglicherweise hunderte Male langsamer als der ursprüngliche diffuse Shader, mit dem wir begonnen haben.
Die beste Alternative wäre die folgende Lösung: Wir erhalten die Tiefe des aktuellen Pixels aus der Tiefenkarte; Wenn wir es dann
nach rechts verschieben müssen , ersetzen Sie die aktuelle Farbe durch das Pixel
auf der linken Seite (siehe Abbildung unten). Hier nehmen wir an, dass, wenn Sie das Pixel nach rechts verschieben möchten, die benachbarten Pixel auf der linken Seite ebenfalls auf die gleiche Weise verschoben werden sollen.
Es ist leicht zu erkennen, dass dies nur eine kostengünstige Annäherung an das ist, was wir wirklich erreichen wollten. Es ist jedoch sehr effektiv, da sich Tiefenkarten normalerweise als glatt herausstellen.
Code
Nach dem im vorherigen Abschnitt beschriebenen Algorithmus können wir den Parallax-Shader mit einem einfachen
Versatz der UV-Koordinaten implementieren.
Dies führt zu folgendem Code:
void surf (Input IN, inout SurfaceOutput o) {
Diese Technik funktioniert gut mit fast flachen Objekten, wie in der folgenden Animation gezeigt.
Aber es funktioniert wirklich sehr gut mit 3D-Modellen, da es sehr einfach ist, die Tiefenstruktur für eine 3D-Szene zu rendern. Unten sehen Sie ein 3D-gerendertes Bild und seine Tiefenkarte.
Die fertigen Ergebnisse werden hier angezeigt: