露滴アニメーション効果を実装するための 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は、外部ネットワーク接続クライアントの低速問題を解決するためにskip-name-resolveを使用します。

Tencent Cloud上に構築されたMySQLは、開発用コンピュータでNavicatを使用して...

衝突検出を実装するためのjs

この記事の例では、衝突検出を実装するためのjsの具体的なコードを参考までに共有しています。具体的な内...

sshとは何ですか?使い方は?どのような誤解があるのでしょうか?

目次序文SSHとは何かssh は何に使用されますか? sshの使い方ssh 再修正要約する序文ssh...

MySQL でデータを削除してもテーブル ファイルのサイズが変更されないのはなぜですか?

長期間稼働しているデータベースの場合、テーブルがストレージ領域を占有しすぎるという問題がよく発生しま...

CentOS 7.3 で Nginx 仮想ホストを設定する方法

実験環境最小限にインストールされた CentOS 7.3 仮想マシン基本環境を構成する1. ngin...

JavaScriptのURLオブジェクトとは何かについて話しましょう

目次概要ハッシュプロパティホストプロパティホスト名属性Href属性起源のプロパティユーザー名とパスワ...

docker を使用して minio と java sdk を構築するプロセスの詳細な説明

目次1minioはシンプル2 Dockerビルド minio 2.1 単一ノード2.2 マルチノード...

mysql5.7.21 の異常起動を修正する方法

同僚から、停電のため MySQL インスタンスを起動できないという報告がありました。 innodb_...

ウェブサイトのフロントエンドパフォーマンスの最適化: JavaScript と CSS

Yahoo チームが書いた、ウェブサイトのパフォーマンス最適化に関する記事を読みました。この記事は...

Docker Docker の保存場所を変更する コンテナイメージのサイズ制限を変更する操作

これは新しいバージョンではもう不可能なようで、推奨されません。そうでない場合は、ソフト リンクを直接...

Ubuntu 18.04 システムでの Redis および phpredis 拡張機能のインストールと設定の詳細な説明

この記事では、Ubuntu 18.04 に Redis と phpredis 拡張機能をインストール...

VMwareでCentOSがインターネットにアクセスできない問題を素早く解決

昨日、VMware に CentOS7 をインストールしました。Tomcat パッケージを転送するた...

html+vue+element-ui のスムーズさを 1 分で体験

テクノロジーファンHTMLウェブページ、知っておくべきYouyou が開発した vue フロントエン...

Ubuntuのpython3でvenvを使用して仮想環境を作成する

1. 仮想環境はプロジェクトに従い、単一のプロジェクト用の仮想環境を作成します(Python 3.4...

Linux CentOS6.5 yum インストール mysql5.6

この記事では、Linux yumを使用してmysql5.6をインストールする簡単な手順を参考までに紹...