すべては、 AIMPプレーヤーで見られるフタロのコロナの視覚化から始まりました。 私は長い間それがどのように機能するかを考え、ついに何かを思いついた。
最初のプロトタイプはC ++で作成されました。
結果として、私はこの視覚化のように粒子の動きのプロトタイプを得ました。 これで私は立ち止まり、半年後に再び音で働きたいと思いました。 2日後、そのようなフラッシュドライブの準備が整いました。
リンク
マウスクリック-ぼかしモードを変更します。
正しく機能させるには、ブラウザで何らかの種類の音楽プレーヤー(VKontakteなど)を開き、オーディオデータが含まれるすべての余分なタブを閉じる必要があります。スペクトルデータを受信すると、SoundMixer.computeSpectrumフラッシュドライブを通じて他のアプリケーションでは音声データの取得が許可されていないという事実。 落ちない場合は、何も閉じることができません。 指摘したように、私のクロムでは他のタブからの音はキャッチされません。
それでは、これらすべての仕組みの概要に移りましょう。
物理学
粒子はその領域にあります(この場合は650)。 600個のアクティブパーティクルと50個のパーティクルが領域内のランダムなポイントに出現して落下し、そのようなパーティクルが床に触れると、領域内で再リスポーンします。 もちろん、重力はすべての粒子に作用します。
重力に加えて、粒子はアトラクタとリパルサの影響を受ける場合があります。 次に、どのようなものがとても魔法なのかを説明します。 実際、彼らはまったく魔法ではなく、彼らの名前は、「教授」が自分自身を表現したいのと同じくらい難解です。
アトラクタは、それ自体に可能なすべてのものを引き付けるようなものです。 この場合、パーティクル、およびパーティクルがアトラクタから遠くなるほど、パーティクルはより弱く引き付けられます。
リパルサーはアトラクタと同じですが、逆にそれ自体からすべてをはじきます。
そのため、特定の条件下(音響スペクトルに基づく)で、アトラクタとリパルサが粒子に作用します。 このため、彼らはその地域でとてもジャンプしています。
描画
ビットマップデータはパーティクル用に作成され、1つはすべてに対応します(これは論理的です)。 これは小さな白い円で、なぜ白が下で説明されるのか。
var radius: Number = PARTICLE_SIZE * 0.5; var particleShape: Shape = new Shape(); particleShape.graphics.beginFill(0xFFFFFF); particleShape.graphics.drawCircle(PARTICLE_SIZE * 0.5, PARTICLE_SIZE * 0.5, radius); m_ParticleBmp = new BitmapData(PARTICLE_SIZE, PARTICLE_SIZE, true, 0x0); m_ParticleBmp.draw(particleShape);
粒子は次のように描かれます。
var velDir: Number = Math.atan2(part.vel.y, part.vel.x); var speed: Number = Math.max(Math.abs(part.vel.y), Math.abs(part.vel.x)); if (speed > 20) speed = 20; matr.identity(); matr.scale(1.3 + speed / 20.0 * 4.0, 0.5); matr.rotate(velDir); matr.translate(part.pos.x, part.pos.y); m_BackBuffer.draw(m_ParticleBmp, matr);
マトリックスは、粒子が移動すると伸びるように粒子を変換します。 複雑なことは何もありません。
効果
パレット
視覚化を開始すると、最終画像のレンダリング時に色がサンプリングされるパレットが作成されます(デモコーダーの標準フィクスチャー:)
それらはプロセスでランダムに選択されます。
パレットを使用するには、256値のサイズの3つの配列を準備する必要があります。 これらは、赤、緑、青の配列になります。
それらを使用するには、 BitmapDataクラスのpaletteMap関数を使用する必要があります 。 これらの配列と元の画像をパラメーターとして受け取ります。
次のように機能します。
- ピクセルの色は元の画像の整数から取得されます。
- この色はrgb(バイト)コンポーネントに分割されます。
- これらのコンポーネントの場合、対応する配列から値が取得され、これらは新しいrgb値になります。
- 新しいrgb値は整数に収集されます。
次に、粒子を白に、背景を黒に塗る必要がある理由を説明します。
フィルターブルームを適用すると、すべてのピクセルがぼやけ、その結果、何も描画しないと黒になります。 パレットを適用すると、黒は任意の色に置き換えられるため、ぼかしをかけると、必要な色にスムーズに移行します(粒子が描画されるバッファーでは、背景は黒のままです)。
パレットは、上側のインデックス(255-白)のrgb配列で粒子の色が記録され、下側(0-黒)で背景色が記録されるように構成されています。 他のすべてのインデックス[1..254]では、背景色から粒子色へのグラデーション色が記録されます。 もちろん、パレットに2色以上を追加し、それらの間にグラデーションを作成することを気にする人はいません。
歪みマップ
元の視覚化では、粒子軌跡の非常に興味深いひねりが加えられています。 私はそれがどのように機能するかを理解するために長い間彼を見つめました。
結局、これは双曲線スパイラルであるという結論に達しました。
双曲線スパイラルの方程式(極座標):
r * phi = a、ここでaはらせん発散率
デカルト座標で:
x = a * cos(phi)/ phi、
y = a * sin(phi)/ phi、ここでa / phi = r
このO_Oをどのように行うかという疑問が生じました。 まず、何らかの形でそのような歪みの式を設定する必要があり、次にフラッシュでそれを行う方法が必要です。
もちろん、最初はすべてを昔ながらの方法で実行しようとしました。画像のすべてのピクセルを通過し、ピクセルを特定の方向にシフトしました。さらに、これによりすぐにぼかしが発生しました。 しかし実際には、すべてが非常に異なっていることが判明しました。 画像のサイクルがfpsを殺しました。 別の解決策を探す必要がありました。
このアイデアは、回転効果を作成するために思いついたもので、すぐにDisplacementMapFilterでコードを見つけました。 しかし、これはオリジナルのものではありませんでした。
双曲線スパイラルのレビューに戻る必要がありました。 全体の難しさは、スパイラルが極座標の角度と半径によって明確に定義されることであり、厚さという別のパラメーターを追加する必要がありました。 その結果、いくつかの式を導出した後、ディスプレイスメントマップを生成するアルゴリズムを作成しました。
ディスプレイスメントマップは、次のアルゴリズムを使用して構築されます。
画像のすべてのピクセルを調べて、各ピクセルのスパイラルを確認します(厚さを指定)。 ピクセルがスパイラルに属する場合、この時点でオフセットを計算する必要があります。
変位の方向を見つけるために、特定の点で接線を取ることができます。 計算を簡単にするために、特定の点で円の接線を見つけることにしました。 このアプローチでは、計算誤差はスパイラルの尾部でのみ顕著になります。 なぜなら 半径(または角度)yが増加すると、座標はaになる傾向があります。 行に。
f '(x、y)= ArcTan(y / x)-Pi / 2
ここで、取得した角度でベクトルを見つけ、それにオフセット係数を掛けます。 結果は、このようなディスプレイスメントマップです(緑と青のチャネルを使用)
2番目のモードでは、Perlinノイズによって生成される別の変位マップが重ねられます。
m_NoiseBuffer.perlinNoise(SCREEN_WIDTH, SCREEN_HEIGHT - FLOOR_POS, 2, 1, false, true, BitmapDataChannel.RED | BitmapDataChannel.BLUE, false, [m_OctaveOffset, m_OctaveOffset]);
ここに出てくる素敵な画像があります:
生成中にさらにオクターブを追加することも可能ですが、更新のたびにノイズが生成されます(このため、移動の効果のためにオクターブシフトが使用されます)。
その結果、レンダリング手順は次のとおりです。
- オフセット付きのDisplacementMapFilterフィルターがbackBufferに適用されます。
- ぼかしフィルター(x:8、y:8、quality:1)がbackBufferに適用されます。
- ColorTransformはbackBufferに適用されるため、画像のフェードレートを調整します。
- backBufferにパーティクルを描画します。
- paletteMapをscreeBufferに適用し、backBufferをソースイメージとして使用します。
- さて、最後に倒立した床を描きます
ソース
ご覧いただきありがとうございます!