露滴アニメーション効果を実装するための Three.js サンプル コード

露滴アニメーション効果を実装するための Three.js サンプル コード

序文

みなさんこんにちは。CSS ウィザードの alphardex です。

この記事では、three.js を使用して、落ちる露滴というクールな光学効果を実装します。物体の表面から露が落ちると、粘着性が生じることはよく知られています。 2D 平面では、この接着効果は CSS フィルターを使用して実際に簡単に実現できます。しかし、3D の世界では、それはそれほど単純ではありません。これを実現するには、レイ マーチングという重要なアルゴリズムを伴う照明に頼る必要があります。以下は最終的な効果図です

撃て、拝島ルート!

準備

私の three.js テンプレート: 右下のフォークをクリックしてコピーします

ポジティブ

フルスクリーンカメラ

まず、カメラを正投影カメラに変更し、平面の長さを 2 に調整して画面いっぱいに表示します。

クラスRayMarchingはBaseを拡張します{
 コンストラクタ(sel: 文字列、debug: ブール値) {
 スーパー(sel、デバッグ);
 this.clock = 新しい THREE.Clock();
 this.cameraPosition = 新しい THREE.Vector3(0, 0, 0);
 this.orthographicCameraParams = {
  左: -1、
  右: 1、
  トップ: 1,
  下: -1、
  近く: 0,
  遠い: 1,
  ズーム: 1
 };
 }
 // 初期化 init() {
 シーンを作成します。
 this.createOrthographicCamera();
 レンダラーを作成します。
 this.createRayMarchingMaterial();
 this.createPlane();
 this.createLight();
 this.trackMousePos();
 リスナーを追加します。
 ループを設定します。
 }
 // 平面を作成する createPlane() {
 const ジオメトリ = 新しい THREE.PlaneBufferGeometry(2, 2, 100, 100);
 const マテリアル = this.rayMarchingMaterial;
 this.createMesh({
  幾何学、
  材料
 });
 }
} 

マテリアルの作成

シェーダに渡されるすべてのパラメータを定義するシェーダマテリアルを作成します。

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

クラスRayMarchingはBaseを拡張します{
 // レイトレーシングマテリアルを作成する createRayMarchingMaterial() {
 const ローダー = 新しい THREE.TextureLoader();
 const テクスチャ = loader.load(matcapTextureUrl);
 const rayMarchingMaterial = 新しい THREE.ShaderMaterial({
  頂点シェーダー: rayMarchingVertexShader、
  フラグメントシェーダー: rayMarchingFragmentShader、
  サイド: THREE.DoubleSide、
  制服:
  u時間: {
   値: 0
  },
  uマウス: {
   値: 新しい THREE.Vector2(0, 0)
  },
  u解像度:
   値: 新しい THREE.Vector2(window.innerWidth, window.innerHeight)
  },
  uテクスチャ: {
   値: テクスチャ
  },
  進捗状況:
   値: 1
  },
  uVelocityBox: {
   値: 0.25
  },
  uVelocitySphere: {
   値: 0.5
  },
  u角度:
   値: 1.5
  },
  u距離:
   値: 1.2
  }
  }
 });
 this.rayMarchingMaterial = rayMarchingMaterial;
 }
}

頂点シェーダrayMarchingVertexShader 、既製のテンプレートを使用するだけです

焦点はフラグメントシェーダrayMarchingFragmentShaderにあります

フラグメントシェーダー

背景

ウォーミングアップとして、光り輝く背景を作成しましょう。

変化するvec2 vUv;

vec3背景(vec2 uv){
 浮動小数点数 = 長さ(uv-vec2(.5));
 vec3 bg = mix(vec3(.3),vec3(.0),dist);
 bg を返します。
}

void main(){
 vec3 bg = 背景(vUv);
 vec3 色=bg;
 gl_FragColor = vec4(色,1.);
} 

自衛隊

照明モデルでオブジェクトを作成するにはどうすればいいですか?自衛隊が必要だ。

SDF は符号付き距離関数を意味します。関数空間内の座標が渡されると、その点とある平面との間の最短距離を返します。戻り値の符号は、点が平面の内側にあるか外側にあるかを示すため、符号付き距離関数と呼ばれます。

ボールを作成したい場合は、ボールの SDF を使用して作成する必要があります。球面方程式は次のGLSLコードで表現できる。

フロート sdSphere(vec3 p,フロート r)
{
 長さ(p)-rを返します。
}

ブロックのコードは次のとおりです

浮動小数点ボックス(vec3 p,vec3 b)
{
 vec3 q=abs(p)-b;
 長さ(max(q,0.))+最小(max(qx,max(qy,qz)),0.)を返します。
}

わからない場合はどうすればいいですか?問題ではない、海外ではすでに、一般的に使用されている自衛隊の公式を整理した専門家がいる。

まずSDFにブロックを作成する

浮動小数点数sdf(vec3 p){
 フロートボックス=sdBox(p,vec3(.3));
 返品ボックス;
}

ゲストのライトさんがまだ入室していないため、画面はまだ空白です。

軽い足取り

次は、この記事の目玉であるレイ ステッパーです。彼女を紹介する前に、彼女の良き友人であるレイ トレーシングについて見てみましょう。

まず、レイ トレーシングの仕組みを理解する必要があります。カメラに位置のeyeを与え、その前にグリッドを配置し、カメラの位置からrayを放射し、グリッドを通過してオブジェクトに当たると、画像の各ピクセルがグリッド上の各ポイントに対応します。

レイマーチングでは、シーン全体が一連の SDF 角度によって定義されます。シーンと視線の境界を見つけるために、カメラの位置から始めて、光線に沿って各ポイントを少しずつ移動します。各ステップで、ポイントがシーンの表面内にあるかどうかを判断します。そうであれば、光線が何かに当たったことを示し、これで完了です。そうでない場合、光線は前進し続けます。

上図では、p0 はカメラの位置であり、青い線は光線を表しています。光の最初のステップ p0p1 が非常に大きく、これがこの時点で光から表面までの最短距離になっていることがわかります。表面上の点は最短距離ですが、視線に沿っていないため、点 p4 の検出を継続する必要があります。

shadertoyにインタラクティブな例があります

以下はレイマーチングのGLSLコード実装です。

定数フロートEPSILON=.0001;

float rayMarch(vec3 eye,vec3 ray,float end,int maxIter){
 浮動小数点の深さ=0。;
 for(int i=0;i<maxIter;i++){
  vec3 pos=目+深さ*光線;
  浮動小数点数=sdf(pos);
  深さ+=距離;
  if(dist<EPSILON||dist>=end){
   壊す;
  }
 }
 深さを返します。
}

メイン関数で光線を作成し、それを光線ステッピング アルゴリズムに渡して、光線から表面までの最短距離を取得します。

void main(){
 ...
 vec3目 = vec3(0.,0.,2.5);
 vec3 ray = normalize(vec3(vUv,-eye.z));
 フロート終了=5。;
 整数最大反復回数 = 256;
 float depth=rayMarch(eye,ray,end,maxIter);
 if(深さ<終了){
  vec3 pos=目+深さ*光線;
  色=位置;
 }
 ...
} 

軽快なステップに誘われて、ワイルドブロックが登場!

センター素材

現在のブロックには2つの問題があります。1. 中央に配置されていない 2. x軸方向に伸びている

センタリング+ストレッチ品質2段階

vec2 中心紫外線(vec2 紫外線){
 uv=2.*uv-1.;
 フロートアスペクト=uResolution.x/uResolution.y;
 uv.x*=アスペクト;
 uv を返します。
}

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

立方体は瞬時に画面の中央に浮かび上がったが、この時点では彼女には色はなかった。

表面法線の計算

照明モデルでは、マテリアルの色を与えるために表面法線を計算する必要があります。

vec3 calcNormal(vec3 p 内)
{
 定数float eps = .0001;
 定数vec2 h = vec2(eps,0);
 正規化を返す(vec3(sdf(p+h.xyy)-sdf(ph.xyy),
 sdf(p+h.yxy)-sdf(ph.yxy)、
 sdf(p+h.yyx)-sdf(ph.yyx)));
}

void main(){
 ...
 if(深さ<終了){
  vec3 pos=目+深さ*光線;
  vec3 正規分布 = calcNormal(pos);
  色=通常;
 }
 ...
} 

この時点では、立方体に青色が与えられていますが、まだ立体的な図形であることは分かりません。

動き出そう

ブロックを360度回転させてみましょう。3D回転機能はgistで検索すると見つかります。

均一な float uVelocityBox;

mat4回転行列(vec3軸、float角度){
 軸を正規化します。
 s = sin(角度)を浮動小数点数化します。
 フロート c=cos(角度);
 フロート oc=1.-c;
 
 mat4(oc*axis.x*axis.x+c,oc*axis.x*axis.y-axis.z*s,oc*axis.z*axis.x+axis.y*s,0., を返します。
  oc*axis.x*axis.y+axis.z*s、oc*axis.y*axis.y+c、oc*axis.y*axis.z-axis.x*s、0.、
  oc*axis.z*axis.x-axis.y*s、oc*axis.y*axis.z+axis.x*s、oc*axis.z*axis.z+c、0.、
 0.,0.,0.,1.);
}

vec3 回転(vec3 v,vec3 軸,float 角度){
 mat4 m=回転行列(軸、角度);
 戻り値: m*vec4(v,1.)).xyz;
}

浮動小数点数sdf(vec3 p){
 vec3 p1 = rotate(p、vec3(1.)、uTime*uVelocityBox);
 フロートボックス=sdBox(p1,vec3(.3));
 返品ボックス;
} 

融合効果

キューブ1つだけでは寂しすぎるので、一緒にいられるボールを作りましょう

ボールとブロックをくっつけるにはどうすればいいでしょうか?smin関数が必要です

ユニフォームフロート uProgress;

フロートsmin(フロートa、フロートb、フロートk)
{
 フロートh=クランプ(.5+.5*(ba)/k,0.,1.);
 mix(b,a,h)-k*h*(1.-h) を返します。
}

浮動小数点数sdf(vec3 p){
 vec3 p1 = rotate(p、vec3(1.)、uTime*uVelocityBox);
 フロートボックス=sdBox(p1,vec3(.3));
 浮動小数点球=sdSphere(p,.3);
 float sBox=smin(ボックス、球、.3);
 float mixedBox=mix(sBox,box,uProgress);
 混合ボックスを返します。
}

uProgressの値を 0 に設定すると、正常に接続されます。

uProgressの値を 1 に戻すと、再び分離します。

ダイナミックフュージョン

次に、結露が落ちるアニメーションを実現します。これは、実際には融合された形状に変位変換を適用します。

均一な float uAngle;
均一な float uDistance;
均一な float uVelocitySphere;

定数浮動小数点数 PI=3.14159265359;

float movingSphere(vec3 p, float shape){
 浮動小数点rad=uAngle*PI;
 vec3 pos=vec3(cos(rad),sin(rad),0.)*u距離;
 vec3 変位 = pos*fract(uTime*uVelocitySphere);
 フロート gotoCenter=sdSphere(p-変位,.1);
 smin(shape,gotoCenter,.3) を返します。
}

浮動小数点数sdf(vec3 p){
 vec3 p1 = rotate(p、vec3(1.)、uTime*uVelocityBox);
 フロートボックス=sdBox(p1,vec3(.3));
 浮動小数点球=sdSphere(p,.3);
 float sBox=smin(ボックス、球、.3);
 float mixedBox=mix(sBox,box,uProgress);
 混合ボックス = movingSphere(p, 混合ボックス);
 混合ボックスを返します。
} 

マットキャップマップ

デフォルトのテクスチャは土っぽすぎますか?役に立つクールなマットキャップマップがあります

均一サンプラー2D uTexture;

vec2 matcap(vec3 eye,vec3 normal){
 vec3 反射 = reflect(eye,normal);
 浮動小数点数 m=2.8284271247461903*sqrt(reflected.z+1.);
 reflected.xy/m+.5 を返します。
}

float fresnel(float バイアス、float スケール、float パワー、vec3 I、vec3 N)
{
 バイアス+スケール*pow(1.+dot(I,N),power)を返します。
}

void main(){
 ...
 if(深さ<終了){
  vec3 pos=目+深さ*光線;
  vec3 正規分布 = calcNormal(pos);
  vec2 matcapUv = matcap(ray,normal);
  色=texture2D(uTexture,matcapUv).rgb;
  float F=fresnel(0.,.4,3.2,ray,normal);
  色=ミックス(色、背景、F);
 }
 ...
} 

マットキャップとフレネル数式を並べると一気にかっこよくなりますよね? !

プロジェクトギャラリー

レイ・マーチング・グーイ・エフェクト

three.js で露滴アニメーション効果を実装するためのサンプルコードに関するこの記事はこれで終わりです。three.js で露滴アニメーションを実装するための関連コンテンツの詳細については、123WORDPRESS.COM の以前の記事を検索するか、以下の関連記事を引き続き参照してください。今後も 123WORDPRESS.COM を応援してください。

以下もご興味があるかもしれません:
  • Three.js が Facebook Metaverse 3D ダイナミック ロゴ効果を実現
  • three.js を使って立体的な矢印線を描く詳細な手順
  • three.js を使用してクールなアシッドスタイルの 3D ページ効果を実現します
  • three.js で 3D ダイナミック テキスト効果を実現する方法
  • three.js でのマルチスレッドの使用とパフォーマンステストの詳細な説明
  • JavaScript Three.js でテキストを作成する最初の経験

<<:  Linux のファイル圧縮とパッケージ化の概要

>>:  Windows プラットフォームでの MySQL のインストールと設定方法と注意事項

推薦する

MySQL 5.7.19 のインストールと設定方法のグラフィック チュートリアル (win10)

以下に記録されているように、WIN10システムにMYSQLをダウンロードしてインストールするための詳...

Google Web Fonts でウェブサイトに無制限のフォントを追加

長い間、リソースの制約により、使用できるフォントが限られていたため、Web サイトの開発は妨げられて...

MySQL インデックスの知識の要約

MySQL インデックスの確立は、MySQL の効率的な操作にとって非常に重要です。インデックスによ...

TypeScript におけるインターフェースと型メソッドの正しい使用例

目次序文インタフェースタイプ付録: インターフェースとタイプの違い要約する序文インターフェースとタイ...

Dockerのオンラインおよびオフラインインストールと一般的なコマンド操作

1. テスト環境名前バージョンセント7.6ドッカー18.09.06 2. オンラインインストールここ...

VMware 仮想マシンのインストール CentOS 8 (1905) システム チュートリアル ダイアグラム

世界的に有名な仮想マシン ソフトウェア VMware-workstation-full-15.5.0...

MySQL の効率的なクエリの左結合とグループ化 (プラス インデックス)

mysql 効率的なクエリMySQL は、左結合の速度を上げるために group by を犠牲にし...

Docker コンテナを他のサーバーに移行する 5 つの方法

多くの場合、移行は避けられません。ハードウェアのアップグレード、データ センターの変更、古いオペレー...

Nginx プロキシを使用してインターネットを閲覧する方法

私は通常、Tomcatや他のアプリケーションのリバースプロキシとしてnginxを使用しています。実際...

Vueはechart円グラフの凡例のパーセンテージを表示するメソッドを実装します

この記事では主に、echart を使用してパーセンテージを表示する Vue の円グラフデータ部分を紹...

メタビューポートタグ(モバイルブラウジングズームコントロール)の使用方法

OP が現在のファームウェアで Web ページを開くと、常に 50% にズームアウトされてから表示さ...

JavaScript の新しい要素トラバーサルプロパティを使用して子要素をトラバースする方法を学びます

目次1. ChildNodes属性のトラバーサル2. 要素シリーズ属性のトラバーサル以前は、chil...

クロスブラウザの問題に対する 5 つの解決策 (要約)

簡単なレビュー: ブラウザの互換性の問題は、しばしば頭痛の種となります。ここでは、これらの問題を回避...

jQueryは検証コード送信のコントロールボタンを無効にする機能を実装します

必要な効果: 確認コードを送信するためにクリックした後、ボタンは無効になり、5 秒後に無効解除されま...

仮想マシンの複製に関するVirtual Boxチュートリアル図

VMに慣れた後、BOXに切り替えるのは少し異なります。たとえば、コピーネットワークカードを2枚使って...