Three.js-Beispielcode zur Implementierung des Tautropfen-Animationseffekts

Three.js-Beispielcode zur Implementierung des Tautropfen-Animationseffekts

Vorwort

Hallo zusammen, hier ist der CSS-Assistent – ​​alphardex.

In diesem Artikel verwenden wir three.js, um einen coolen optischen Effekt zu implementieren – fallende Tautropfen. Wir wissen, dass ein klebriger Effekt entsteht, wenn Tautropfen von der Oberfläche eines Objekts fallen. In einer 2D-Ebene kann dieser Adhäsionseffekt tatsächlich leicht durch die Verwendung von CSS-Filtern erzielt werden. In der 3D-Welt ist es jedoch nicht so einfach. Um dies zu erreichen, müssen wir uns auf die Beleuchtung verlassen, die einen wichtigen Algorithmus beinhaltet - Ray Marching. Nachfolgend sehen Sie das endgültige Effektdiagramm

Verdammt, Hajima-Route!

Vorbereitung

Meine three.js-Vorlage: Klicken Sie unten rechts auf „Fork“, um eine Kopie zu erstellen.

positiv

Vollbildkamera

Ändern Sie zunächst die Kamera in eine orthographische Kamera und passen Sie dann die Länge der Ebene auf 2 an, sodass sie den Bildschirm ausfüllt.

Klasse RayMarching erweitert Base {
 Konstruktor(sel: string, debug: boolean) {
 super(sel, debug);
 diese.Uhr = neue DREI.Uhr();
 diese.Kameraposition = neuer THREE.Vector3(0, 0, 0);
 diese.orthographicCameraParams = {
  links: -1,
  rechts: 1,
  oben: 1,
  unten: -1,
  in der Nähe von: 0,
  weit: 1,
  Zoom: 1
 };
 }
 // Initialisierung init() {
 dies.createScene();
 dies.createOrthographicCamera();
 dies.createRenderer();
 dies.createRayMarchingMaterial();
 dies.createPlane();
 dies.createLight();
 dies.trackMousePos();
 dies.addListeners();
 dies.setLoop();
 }
 // Erstelle ein Flugzeug createPlane() {
 const Geometrie = neue THREE.PlaneBufferGeometry(2, 2, 100, 100);
 const material = this.rayMarchingMaterial;
 dies.createMesh({
  Geometrie,
  Material
 });
 }
} 

Materialien erstellen

Erstellen Sie ein Shadermaterial, das alle Parameter definiert, die an den Shader übergeben werden sollen

const matcapTextureUrl = "https://i.loli.net/2021/02/27/7zhBySIYxEqUFW3.png";

Klasse RayMarching erweitert Base {
 // Erstelle ein Raytracing-Material createRayMarchingMaterial() {
 const loader = neues THREE.TextureLoader();
 const texture = loader.load(matcapTextureUrl);
 const rayMarchingMaterial = neues THREE.ShaderMaterial({
  vertexShader: rayMarchingVertexShader,
  fragmentShader: rayMarchingFragmentShader,
  Seite: DREI.Doppelseite,
  Uniformen:
  uZeit: {
   Wert: 0
  },
  uMaus: {
   Wert: neuer THREE.Vector2(0, 0)
  },
  uAuflösung:
   Wert: neuer THREE.Vector2(window.innerWidth, window.innerHeight)
  },
  uTextur: {
   Wert: Textur
  },
  uFortschritt:
   Wert: 1
  },
  uVelocityBox: {
   Wert: 0,25
  },
  uVelocitySphere: {
   Wert: 0,5
  },
  uWinkel:
   Wert: 1,5
  },
  uEntfernung:
   Wert: 1,2
  }
  }
 });
 dies.rayMarchingMaterial = rayMarchingMaterial;
 }
}

Vertex-Shader rayMarchingVertexShader , verwenden Sie einfach die vorgefertigte Vorlage

Der Fokus liegt auf dem Fragment-Shader rayMarchingFragmentShader

Fragment-Shader

Hintergrund

Lassen Sie uns als Aufwärmübung einen strahlenden Hintergrund erstellen.

variierender Veränderlicher Vektor 2 vUv;

vec3 Hintergrund(vec2 uv){
 float dist = Länge(uv-vec2(.5));
 vec3 bg = mix(vec3(.3),vec3(.0),verteilt);
 Rückkehr bg;
}

void main(){
 vec3 bg = Hintergrund(vUv);
 vec3 Farbe=Hintergrund;
 gl_FragColor = vec4(Farbe,1.);
} 

sdf

Wie erstelle ich Objekte im Beleuchtungsmodell? Wir brauchen SDF.

SDF steht für vorzeichenbehaftete Distanzfunktion: Wenn eine Koordinate im Funktionsraum übergeben wird, gibt sie die kürzeste Distanz zwischen diesem Punkt und einer Ebene zurück. Das Vorzeichen des Rückgabewerts gibt an, ob der Punkt innerhalb oder außerhalb der Ebene liegt, daher wird sie als vorzeichenbehaftete Distanzfunktion bezeichnet.

Wenn wir einen Ball erstellen möchten, müssen wir zur Erstellung die SDF des Balls verwenden. Die Kugelgleichung kann mit dem folgenden GLSl-Code ausgedrückt werden

float sdSphere(vec3 p,float r)
{
 Rückgabelänge (p)-r;
}

Der Code des Blocks lautet wie folgt

Float sdBox(vec3 p, vec3 b)
{
 vec3 q=abs(p)-b;
 Rückgabelänge (max(q,0.))+min(max(qx,max(qy,qz)),0.);
}

Was soll ich tun, wenn ich etwas nicht verstehe? Es spielt keine Rolle, es gibt bereits Experten im Ausland, die die gängigen SDF-Formeln aussortiert haben

Erstellen Sie zuerst einen Block in SDF

Gleitkommazahl sdf(vec3 p){
 float-box=sdBox(p,vec3(.3));
 Rücksendekarton;
}

Der Bildschirm ist noch leer, da unser Gast – Licht – noch nicht hereingekommen ist.

Leichtes Treten

Als nächstes kommt die Schlagzeile dieses Artikels – der Ray Stepper. Bevor wir sie vorstellen, werfen wir einen Blick auf ihren guten Freund, das Raytracing.

Zunächst müssen wir wissen, wie Raytracing funktioniert: Man gibt der Kamera ein eye , platziert ein Gitter davor, sendet einen ray von der Kameraposition aus, durchquert das Gitter und trifft auf das Objekt, und jeder Pixel des Bildes entspricht jedem Punkt auf dem Gitter.

Beim Ray Marching wird die gesamte Szene durch eine Reihe von SDF-Winkeln definiert. Um die Grenze zwischen der Szene und der Sichtlinie zu finden, beginnen wir bei der Position der Kamera und bewegen jeden Punkt Stück für Stück entlang des Strahls. Bei jedem Schritt bestimmen wir, ob der Punkt innerhalb einer Oberfläche der Szene liegt. Wenn dies der Fall ist, sind wir fertig, was bedeutet, dass der Strahl auf etwas trifft. Wenn nicht, bewegt sich der Strahl weiter vorwärts.

In der obigen Abbildung ist p0 die Kameraposition und die blaue Linie stellt den Strahl dar. Es ist ersichtlich, dass der erste Schritt p0p1 des Lichts sehr groß ist. Dies ist zufällig die kürzeste Entfernung vom Licht zur Oberfläche zu diesem Zeitpunkt. Obwohl der Punkt auf der Oberfläche die kürzeste Entfernung aufweist, liegt er nicht entlang der Sichtlinie. Daher müssen wir mit der Erkennung von Punkt p4 fortfahren.

Es gibt ein interaktives Beispiel auf Shadertoy

Nachfolgend sehen Sie die GLS-Codeimplementierung von Ray Marching

Konstante Float EPSILON = .0001;

float rayMarch(vec3 Auge,vec3 Strahl,float Ende,int maxIter){
 Schwimmertiefe=0.;
 für(int i=0;i<maxIter;i++){
  vec3 pos=Auge+Tiefe*Strahl;
  Float dist = sdf (pos);
  Tiefe+=Abstand;
  wenn(dist<EPSILON||dist>=Ende){
   brechen;
  }
 }
 Rücklauftiefe;
}

Erstellen Sie in der Hauptfunktion einen Strahl und geben Sie ihn in den Strahlschrittalgorithmus ein, um die kürzeste Entfernung vom Strahl zur Oberfläche zu erhalten.

void main(){
 ...
 vec3 Auge = vec3(0.,0.,2.5);
 vec3 Strahl = normalisieren(vec3(vUv,-eye.z));
 Gleitkommazahl Ende=5.;
 int maxIter=256;
 Float-Tiefe = RayMarch (Auge, Strahl, Ende, MaxIter);
 wenn(Tiefe<Ende){
  vec3 pos=Auge+Tiefe*Strahl;
  Farbe=pos;
 }
 ...
} 

Angelockt durch die leichten Schritte, erscheinen wilde Blöcke!

Mittelmaterial

Der aktuelle Block hat zwei Probleme: 1. Er ist nicht zentriert. 2. Er ist in der x-Achsenrichtung gestreckt.

Zentrierung + Dehnungsqualität 2 Schritte

vec2 centerUv(vec2 uv){
 uv=2.*uv-1.;
 Float-Aspekt = uResolution.x/uResolution.y;
 uv.x*=Aspekt;
 UV zurückgeben;
}

void main(){
 ...
 vec2 cUv=centerUv(vUv);
 vec3 Strahl = normalisieren(vec3(cUv,-eye.z));
 ...
} 

Der Würfel schwebte sofort in die Mitte des Bildschirms, aber zu diesem Zeitpunkt hatte sie keine Farbe

Berechnen von Oberflächennormalen

Im Beleuchtungsmodell müssen wir die Oberflächennormale berechnen, um dem Material Farbe zu geben

vec3 calcNormal(in vec3 p)
{
 Konstanten-Float eps = .0001;
 const vec2 h = vec2(eps,0);
 returniere normalisieren(vec3(sdf(p+h.xyy)-sdf(ph.xyy),
 sdf(p+h.yxy)-sdf(ph.yxy),
 sdf(p+h.yyx)-sdf(ph.yyx)));
}

void main(){
 ...
 wenn(Tiefe<Ende){
  vec3 pos=Auge+Tiefe*Strahl;
  vec3 normal = calcNormal(pos);
  Farbe=normal;
 }
 ...
} 

Zu diesem Zeitpunkt erhält der Würfel eine blaue Farbe, wir können jedoch noch nicht erkennen, dass es sich um eine dreidimensionale Figur handelt.

Bewegen Sie sich

Lassen Sie uns den Block um 360 Grad drehen. Sie finden die 3D-Rotationsfunktion, indem Sie nach Gist suchen.

einheitlicher Float uVelocityBox;

mat4 rotationMatrix(vec3 Achse,Float Winkel){
 Achse=normalisieren(Achse);
 float s=sin(Winkel);
 float c=cos(Winkel);
 Gleitkommazahl oc=1.-c;
 
 returniere mat4(oc*Achse.x*Achse.x+c,oc*Achse.x*Achse.y-Achse.z*s,oc*Achse.z*Achse.x+Achse.y*s,0.,
  oc*Achse.x*Achse.y+Achse.z*s,oc*Achse.y*Achse.y+c,oc*Achse.y*Achse.z-Achse.x*s,0.,
  oc*Achse.z*Achse.x-Achse.y*s,oc*Achse.y*Achse.z+Achse.x*s,oc*Achse.z*Achse.z+c,0.,
 0.,0.,0.,1.);
}

vec3 drehen(vec3 v,vec3 Achse,Float-Winkel){
 mat4 m=RotationsMatrix(Achse,Winkel);
 Rückgabewert(m*vec4(v,1.)).xyz;
}

Gleitkommazahl sdf(vec3 p){
 vec3 p1=rotieren(p,vec3(1.),uTime*uVelocityBox);
 float-Box=sdBox(p1,vec3(.3));
 Rücksendebox;
} 

Fusion-Effekt

Ein einzelner Würfel ist zu einsam. Erstelle einen Ball, der ihr Gesellschaft leistet.

Wie kann man den Ball und den Block zusammenkleben? Dazu braucht man die Smin-Funktion

einheitlicher Float uProgress;

Gleitkommazahl smin(Gleitkommazahl a,Gleitkommazahl b,Gleitkommazahl k)
{
 Gleitkommazahl h=Klemme(.5+.5*(ba)/k,0.,1.);
 Rückgabemix(b,a,h)-k*h*(1.-h);
}

Gleitkommazahl sdf(vec3 p){
 vec3 p1=rotieren(p,vec3(1.),uTime*uVelocityBox);
 float-Box=sdBox(p1,vec3(.3));
 Gleitkomma-Kugel = sdKugel (p,.3);
 Gleitkomma sBox = smin (Box, Kugel, .3);
 float mixedBox=mix(sBox,box,uProgress);
 gebe gemischteBox zurück;
}

Setzen Sie den Wert von uProgress auf 0 und die Verbindungen werden erfolgreich hergestellt.

Setzen Sie den Wert von uProgress zurück auf 1, und sie trennen sich wieder.

Dynamische Fusion

Als nächstes wird die Animation der fallenden Tautropfen realisiert, wobei eigentlich eine Verschiebungstransformation auf die verschmolzene Form angewendet wird.

gleichmäßiger Gleitkommawert uWinkel;
gleichmäßiger Gleitkommawert uDistance;
gleichmäßiger Gleitkommawert uVelocitySphere;

Konstante Gleitkommazahl PI = 3,14159265359;

float movingSphere(vec3 p, float Form){
 float rad=uWinkel*PI;
 vec3 pos=vec3(cos(rad),sin(rad),0.)*uAbstand;
 vec3 Verschiebung = Position * Bruch (uZeit * uVelocitySphere);
 float gotoCenter=sdSphere(p-Verschiebung,.1);
 gibt smin zurück (Form, gehe zu Mittelpunkt, .3);
}

Gleitkommazahl sdf(vec3 p){
 vec3 p1=rotieren(p,vec3(1.),uTime*uVelocityBox);
 float-Box=sdBox(p1,vec3(.3));
 Gleitkomma-Kugel = sdKugel (p,.3);
 Gleitkomma sBox = smin (Box, Kugel, .3);
 float mixedBox=mix(sBox,box,uProgress);
 gemischteBox = beweglicheKugel(p, gemischteBox);
 gebe mixedBox zurück;
} 

Matcap-Karte

Die Standardtextur ist zu erdig? Wir haben coole Matcap-Karten, die Ihnen helfen

einheitlicher Sampler2D uTexture;

vec2 matcap(vec3 Auge,vec3 normal){
 vec3 reflektiert = reflektieren (Auge, normal);
 float m=2.8284271247461903*sqrt(reflektiert.z+1.);
 gebe reflektiertes xy/m+.5 zurück;
}

Float Fresnel (Float-Bias, Float-Skala, Float-Leistung, vec3 I, vec3 N)
{
 Rendite-Bias + Maßstab * pow (1. + Punkt (I, N), Leistung);
}

void main(){
 ...
 wenn(Tiefe<Ende){
  vec3 pos=Auge+Tiefe*Strahl;
  vec3 normal = calcNormal(pos);
  vec2 matcapUv=matcap(Strahl,normal);
  Farbe=Textur2D(uTexture,matcapUv).rgb;
  Gleitkomma F=fresnel(0.,.4,3.2,Strahl,normal);
  Farbe=Mix(Farbe,Hintergrund,F);
 }
 ...
} 

Nach dem Anordnen von Matcap und Fresnel-Formel wird es sofort cool, oder? !

Projektgalerie

Ray Marching Gooey-Effekt

Dies ist das Ende dieses Artikels über den Beispielcode zur Implementierung des Tautropfen-Animationseffekts mit three.js. Weitere relevante Inhalte zur Implementierung der Tautropfen-Animation mit three.js finden Sie in früheren Artikeln auf 123WORDPRESS.COM oder in den verwandten Artikeln weiter unten. Ich hoffe, Sie werden 123WORDPRESS.COM auch in Zukunft unterstützen!

Das könnte Sie auch interessieren:
  • Three.js realisiert den dynamischen 3D-Logoeffekt von Facebook Metaverse
  • Detaillierter Prozess zum Zeichnen dreidimensionaler Pfeillinien mit three.js
  • Verwenden Sie three.js, um coole 3D-Seiteneffekte im Acid-Stil zu erzielen
  • So erzielen Sie mit three.js einen dynamischen 3D-Texteffekt
  • Detaillierte Erläuterung der Verwendung und Leistungstests von Multithreading in three.js
  • Erste Erfahrungen mit der Texterstellung mit Javascript Three.js

<<:  Einführung in die Linux-Dateikomprimierung und -Verpackung

>>:  MySQL-Installations- und Konfigurationsmethoden und Vorsichtsmaßnahmen unter der Windows-Plattform

Artikel empfehlen

Analyse der vier Transaktionsisolationsstufen in MySQL anhand von Beispielen

Vorwort Um bei Datenbankoperationen die Richtigke...

So schreiben Sie DROP TABLE in verschiedene Datenbanken

So schreiben Sie DROP TABLE in verschiedene Daten...

Verwendung des HTML-H-Titel-Tags

Die Verwendung von H-Tags, insbesondere h1, war sc...

js zur Realisierung der automatischen Sperrbildschirmfunktion

1. Anwendungsszenarien Es gibt eine solche Anford...

26 häufig vergessene CSS-Tipps

Dies ist eine Sammlung häufig verwendeter, aber l...

Tutorial zur Installation und Konfiguration von Hbase unter Linux

Inhaltsverzeichnis Hbase-Installation und -Konfig...

Drei Möglichkeiten zur Implementierung von Animationen in CSS3

Hiermit werden die Grundkenntnisse des Interviewt...

Vue definiert private Filter und grundlegende Nutzung

Die Methoden und Konzepte privater und globaler F...

Verwenden Sie nginx, um virtuelle Hosts auf Domänennamenbasis zu konfigurieren

1. Was ist ein virtueller Host? Virtuelle Hosts v...

Detaillierte Erklärung, wann Javascript-Skripte ausgeführt werden

JavaScript-Skripte können überall in HTML eingebe...

MySQL-Datentabellenpartitionierungsstrategie und Vor- und Nachteileanalyse

Inhaltsverzeichnis Warum brauchen wir Partitionen...