Wie schreibe ich einen Shader, der aufleuchtet, wenn sich Objekte in der Nähe einer Oberfläche befinden?

15

In diesem Overwatch- Gameplay-Video leuchtet der Schild des Charakters in Bereichen in der Nähe der Geometrie anderer Objekte weiß auf.

Reinharts Schild schneidet eine ebene Geometrie ab

Beachten Sie die weißen Ränder des blauen Schilds in der Nähe des Bodens, der Wände und der Säule.

Ich glaube, der Shield hat ein eigenes Modell und der Effekt wird mit einem Shader erzielt, aber ich habe den Versuch verloren, herauszufinden, wie das Konzept der "Nähe" zur Shader-Programmierung übersetzt werden kann.

Blaue Wanze
quelle
2
Wenn Sie Unity verwenden, ist dieser Vortrag möglicherweise von Interesse . Überprüfen Sie den Abschnitt "Höhepunkte der Kreuzung" ab Folie 26.
DMGregory
Die Antwort wurde bereits weiter unten behandelt, aber hier ist ein Video, das dies erklärt: youtube.com/watch?v=C6lGEgcHbWc
CobaltHex

Antworten:

28

Ein allgemeiner Überblick:

  1. Erstellen Sie eine Tiefenkarte Ihrer Szene ohne Schild. Sie können dies effektiv kostenlos erhalten, da transparente Objekte ohnehin oft in einem späteren Durchgang gerendert werden. Andernfalls können Sie die Tiefenkarte erstellen, indem Sie die Szene ohne Abschirmung auf einem RTT mit einem Tiefen-Shader rendern .

  2. Rendern Sie Ihre Szene normal, übergeben Sie die Tiefenkarte an Ihren Shader.

  3. Berechnen Sie im Shader den Unterschied in der Szenentiefe von der Tiefe des Schildfragments und verwenden Sie diesen Unterschied, um die Fragmentfarbe zu ändern.

Demo

Ich habe eine einfache WebGL-Demo dazu geschrieben.

Screenshot der Demo

Zeile für Zeile

Lassen Sie uns den Fragment-Shader-Code im Detail durchgehen:

float solidsDepth = texture2D(depthMap, gl_FragCoord.xy / dims).r;

Probieren Sie die Tiefenkarte bei jedem Fragment. Denken Sie daran, durch die Abmessungen Ihres Ansichtsfensters zu dividieren, um das Fragment vom Bildschirmbereich [0, Breite / Höhe] in normalisierte [0.0, 1.0] Koordinaten zu konvertieren. Wenn Sie zu diesem Zeitpunkt die Fragmentfarbe einfach auf das Pixel der abgetasteten Tiefenkarte einstellen, sieht dies folgendermaßen aus:

Screenshot der Tiefenkarte

Die Tiefenkarte ist Graustufen, sodass Sie den Wert von jedem Kanal abrufen können (den ich rhier verwendet habe).

float solidsDiff = 1.0 - smoothstep(
    zNear,
    zFar,
    gl_FragCoord.z / gl_FragCoord.w
  ) - solidsDepth;

Mit diesem Tiefenmuster können Sie dann den Unterschied zwischen der Szenentiefe und der Tiefe des Schildfragments ermitteln. Denken Sie daran, auch Ihre Tiefe zu normalisieren, um sie von [zNear, zFar] (der nahen und der fernen Ebene Ihrer Kamera) auf [0.0, 1.0] zu bringen. smoothstepmacht das schön. Das 1.0 -ist, um den Wert so zu invertieren, dass er solidsDiff1,0 ist, wenn die Differenz das Maximum (zFar - zNear) und 0,0 das Minimum (0,0) ist.

Beachten Sie, dass ich davon ausgegangen bin, dass solidsDepthder Tiefen-Shader, der die Tiefen-Map erstellt hat, bereits normalisiert wurde.

float alpha = 0.3 + max(0.0, 1.0 - log(100.0 * (solidsDiff - 0.005) + 1.0));

Sie können dann den Alphakanal Ihres Schilds in Abhängigkeit von der Tiefenunterschiede ändern. Hier beginnen wir mit einem Alpha-Minimum von 0.3, und erzeugen dann einen schönen steilen Alpha- Anstieg, wenn wir uns der 0.0Differenz nähern.

Der - 0.005Versatz fügt nur einen weißen Rand hinzu, um den "Schnitt" dicker zu machen. Versuchen Sie es zu ändern!

gl_FragColor = vec4(vec3(1.0), alpha);

Und schließlich wenden Sie dieses Alpha auf Ihre Fragmentfarbe an.


Verbesserungen

Sie können einen gekrümmten Schild erstellen, Plasma hinzufügen, um einen "Energieschild" -Look zu erhalten (Demo), oder Effekte mit nur den angezeigten Schnittpunkten untersuchen (Demo) .

Der Himmel Ihre Grafikkarte ist das Limit!

Yousef Amar
quelle
Oh. Meine. Gute Nachrichten. Ty für das tolle Tutorial.
Blue Bug
5

Es wird nur die Tiefenkarte verwendet. Die Welt wird gerendert, dann wird der Schild gerendert und es wird eine Differenz zwischen dem gerenderten Z-Wert des Schilds und dem Tiefenpuffer-Z-Wert verwendet, um das Pixel weißer zu färben.

Sirisian
quelle
3

Ich weiß nicht, welche Engine Sie verwenden. Oder mit welcher Sprache Sie arbeiten. Dennoch ist es nicht schwierig, das meiste, was Sie online finden, von einer Umgebung in eine andere zu portieren, um das zu erreichen, wonach Sie suchen.

Und es gibt sicherlich Material online, das Ihnen helfen kann. Siehe diese Diskussion zu Unity: http://www.superspacetrooper.com/2012/06/tutorial-force-field-weapon-impact-energy-dispersion/ und diese Frage zu UE4: https: //answers.unrealengine. com / questions / 74858 / dynamic-forcefield-shader.html . Für ein komplettes Shader Beispiel, dass die Abschirmwirkung, aus einer ganz jüngsten Debatte in Reddit Implementierung können Sie sehen: https://www.reddit.com/r/Unity3D/comments/3edi0n/does_any_one_knows_how_to_make_this_shield_effect/ Und für ein Tutorial , das ist nicht Genau darauf bezogen ist aber genug von Interesse: http://www.nightbox-studios.com/2015/09/05/assets-shield-effect-scripts-for-texture3d-perlin-noise-shader/

Außerdem finden Sie hier die Links zu drei verwandten Fragen, die zuvor auf dieser Website gestellt wurden. Sie sind wahrscheinlich eine große Hilfe, um die Konzepte zu verstehen, die hinter dem stehen, was Sie erreichen möchten:

Raumschiff Shield Flare

So implementierst du einen Starwar-Energieschild im Spiel

XNA-Schildeffekt mit einem primativen Kugelproblem

Schließlich gibt es einige nette Implementierungen von Shield-Shadern sowohl in Unity als auch in Unreal Engine in ihrem jeweiligen virtuellen Geschäft, falls Sie zufällig eine dieser Engines verwenden. Sie werden in der Regel natürlich bezahlt, sind aber nach dem Kauf fast immer Open Source - und häufig billig. Auch wenn Sie diese Engines nicht verwenden, können diese Assets hilfreich sein, um herumzuspielen und zu lernen.

Ich hoffe es hilft.

MAnd
quelle
Obwohl diese Links hilfreich sind, handelt es sich bei dieser Antwort im Grunde nur um Links, die die Frage nicht direkt beantworten. Es wäre besser, den relevanten Inhalt der Links in eine tatsächliche Erklärung zu extrahieren.
Anko
Während dieser Link die Frage beantworten kann, ist es besser, die wesentlichen Teile der Antwort hier einzuschließen und den Link als Referenz bereitzustellen. Nur-Link-Antworten können ungültig werden, wenn sich die verlinkte Seite ändert. - Aus der Bewertung
Vaillancourt
@Anko Sicher, meine schlechte. Normalerweise füge ich eine Erklärung und eine Sammlung von Links hinzu, aber diesmal haben Sie recht, es fehlte die wirkliche Erklärung. Ich werde dann wohl die Antwort löschen.
20.
@AlexandreVaillancourt Mir gefällt die Idee nicht, einfach von einem anderen Link zu kopieren und einzufügen, anstatt das OP auf die ursprüngliche Quelle umzuleiten. Was ich dann am besten finde, ist, Links in den Kommentaren zu der Frage anzugeben. Das Problem mit solchen ist, wenn Sie viel Material kennen, das hilfreich sein kann, aber für Kommentare zu groß ist. Da hier jedoch bereits eine gute Antwort auf die Erklärung gepostet wurde, werde ich diese
Frage