2016-04-27 16 views
1

私は非常に簡単な2つの平面波の干渉をシミュレートする非常に簡単なスケッチを書いた。処理3集中的な計算の計算

問題は、CPUのための集中はるかに少ないと思われる(更に処理のみつのコアを使用する)と私は1 O 2 FPSを得ます。

このスケッチを改善する方法はありますか?

float x0; 
float y0; 
float x1; 
float y1; 
float x2; 
float y2; 
int t = 0; 

void setup() { 
    //noLoop(); 
    frameRate(30); 
    size(400, 400, P2D); 
    x0 = width/2; 
    y0 = height/2; 
    x1 = width/4; 
    y1 = height/2; 
    x2 = width * 3/4; 
    y2 = height/2; 
} 

    void draw() { 
    background(0); 

    for (int x = 0; x <= width; x++) { 
    for (int y = 0; y <= height; y++) { 

     float d1 = dist(x1, y1, x, y); 
     float d2 = dist(x2, y2, x, y); 
     float factorA = 20; 
     float factorB = 80; 
     float wave1 = (1 + (sin(TWO_PI * d1/factorA + t)))/2 * exp(-d1/factorB); 
     float wave2 = (1 + (sin(TWO_PI * d2/factorA + t)))/2 * exp(-d2/factorB); 
     stroke((wave1 + wave2) *255); 
     point(x, y); 
    } 
    } 

    t--; //Wave propagation 
    //saveFrame("wave-##.png"); 
} 
+0

どのAPIですか?私は減速の大部分がポイント(x、y)法の内側にあると考えます。基本的なJavaのBufferedImageでピクセルを直接設定し、それを1秒間に30回スクリーンに表示すると、十分速く動作するはずです。 –

答えて

4

Kevinが示唆したように、point()を使用すると、beginShape();vertex() and endShape();が呼び出されて以来、最も効率的な方法ではありません。ピクセルを使用する方が良いかもしれません。

さらに、入れ子になったループは単一のループとして記述することができ、背後の平方根を使用するdist()は避けることができます(より高い値で平方距離を使用できます)。これは、このようなsin()exp()などの機能を消費するより多くの時間のためのルックアップテーブルを用いてさらにスピードアップすることができます

float x1; 
float y1; 
float x2; 
float y2; 
int t = 0; 
//using larger factors to use squared distance bellow instead of dist(),sqrt() 
float factorA = 20*200; 
float factorB = 80*200; 

void setup() { 
    //noLoop(); 
    frameRate(30); 
    size(400, 400); 
    x1 = width/4; 
    y1 = height/2; 
    x2 = width * 3/4; 
    y2 = height/2; 
    //use pixels, not points() 
    loadPixels(); 
} 

void draw() { 
    for (int i = 0; i < pixels.length; i++) { 
    int x = i % width; 
    int y = i/height; 

    float dx1 = x1-x; 
    float dy1 = y1-y; 
    float dx2 = x2-x; 
    float dy2 = y2-y; 

    //squared distance 
    float d1 = dx1*dx1+dy1*dy1;//dist(x1, y1, x, y); 
    float d2 = dx2*dx2+dy2*dy2;//dist(x2, y2, x, y); 

    float wave1 = (1 + (sin(TWO_PI * d1/factorA + t))) * 0.5 * exp(-d1/factorB); 
    float wave2 = (1 + (sin(TWO_PI * d2/factorA + t))) * 0.5 * exp(-d2/factorB); 

    pixels[i] = color((wave1 + wave2) *255); 
    } 
    updatePixels(); 
    text((int)frameRate+"fps",10,15); 
    // endShape(); 
    t--; //Wave propagation 
    //saveFrame("wave-##.png"); 
} 

ここではこれらを使用したバージョンです。

あなたがラフ(数字は微調整する必要がある)見ることができても、JavaScriptで実行されているプレビュー:

var x1; 
 
var y1; 
 
var x2; 
 
var y2; 
 
var t = 0; 
 

 
var factorA = 20*200; 
 
var factorB = 80*200; 
 

 
function setup() { 
 
    createCanvas(400, 400); 
 
    frameRate(30); 
 
    x1 = width/4; 
 
    y1 = height/2; 
 
    x2 = width * 3/4; 
 
    y2 = height/2; 
 
    loadPixels(); 
 
} 
 

 
function draw() { 
 
    for (var i = 0; i < pixels.length; i+= 4) { 
 
    var x = i % width; 
 
    var y = i/height; 
 
    
 
    var dx1 = x1-x; 
 
    var dy1 = y1-y; 
 
    var dx2 = x2-x; 
 
    var dy2 = y2-y; 
 
    
 
    var d1 = dx1*dx1+dy1*dy1;//dist(x1, y1, x, y); 
 
    var d2 = dx2*dx2+dy2*dy2;//dist(x2, y2, x, y); 
 
    
 
    var wave1 = (1 + (sin(TWO_PI * d1/factorA + t))) * 0.5 * exp(-d1/factorB); 
 
    var wave2 = (1 + (sin(TWO_PI * d2/factorA + t))) * 0.5 * exp(-d2/factorB); 
 

 
    pixels[i] = pixels[i+1] = pixels[i+2] = (wave1 + wave2) * 255; 
 
    pixels[i+3] = 255; 
 
    } 
 
    updatePixels(); 
 
    text(frameRate+"fps",10,15); 
 
    // endShape(); 
 
    t--; //Wave propagation 
 
    //saveFrame("wave-##.png"); 
 
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/p5.js/0.4.24/p5.min.js"></script>

あなたが画像を合成するために数学を使用しているので、それはより多くを行うことができますこれをGLSLシェーダとして記述します。詳細についてはPShader tutorialを必ずチェックアウトしてください。

アップデート:ここで

はGLSLのバージョンがあります:コードが少ないハックされ、より多くの読み:

float t = 0; 
float factorA = 0.20; 
float factorB = 0.80; 

PShader waves; 

void setup() { 
    size(400, 400, P2D); 
    noStroke(); 

    waves = loadShader("waves.glsl"); 
    waves.set("resolution", float(width), float(height)); 
    waves.set("factorA",factorA); 
    waves.set("factorB",factorB); 
    waves.set("pt1",-0.5,0.0); 
    waves.set("pt2",0.75,0.0); 
} 

void draw() { 
    t++; 
    waves.set("t",t); 

    shader(waves); 
    rect(0, 0, width, height); 
} 
void mouseDragged(){ 
    float x = map(mouseX,0,width,-1.0,1.0); 
    float y = map(mouseY,0,height,1.0,-1.0); 
    println(x,y); 
    if(keyPressed) waves.set("pt2",x,y); 
    else   waves.set("pt1",x,y); 
} 
void keyPressed(){ 
    float amount = 0.05; 
    if(keyCode == UP)  factorA += amount; 
    if(keyCode == DOWN) factorA -= amount; 
    if(keyCode == LEFT) factorB -= amount; 
    if(keyCode == RIGHT) factorB += amount; 
    waves.set("factorA",factorA); 
    waves.set("factorB",factorB); 
    println(factorA,factorB); 
} 

そしてwaves.glsl:

#define PROCESSING_COLOR_SHADER 

uniform vec2 pt1; 
uniform vec2 pt2; 
uniform float t; 

uniform float factorA; 
uniform float factorB; 

const float TWO_PI = 6.283185307179586; 

uniform vec2 resolution; 
uniform float time; 


void main(void) { 
    vec2 p = -1.0 + 2.0 * gl_FragCoord.xy/resolution.xy; 

    float d1 = distance(pt1,p); 
    float d2 = distance(pt2,p); 

    float wave1 = (1.0 + (sin(TWO_PI * d1/factorA + t))) * 0.5 * exp(-d1/factorB); 
    float wave2 = (1.0 + (sin(TWO_PI * d2/factorA + t))) * 0.5 * exp(-d2/factorB); 

    float gray = wave1 + wave2; 

    gl_FragColor=vec4(gray,gray,gray,1.0); 
} 

あなたが使用することができます最初の点をドラッグし、キーを押しながら2番目の点をドラッグします。 さらにfactorAfactorBを変更するには、UP/DOWN,LEFT/RIGHTのキーを使用します。結果は面白そうに見える:

GLSL Waves 1

GLSL Waves 2

GLSL Waves 3

また、あなたは(私は非圧縮保存をお勧めします)スレッドを使用してフレームを保存するためにthis answerからコードのビットをつかむことができます。

+0

うわー!あなたのスケッチは私のラップトップで約16/17 fpsで走ります。これは私の実験にはかなり良いです!より大きな解像度ではsaveFrame()を使用しますが、ピクセルを使用するとすばらしいです!ありがとう、私はPShaderを勉強するつもりです! – johnnyparafango

+0

PShaderバージョンも含めて回答を更新しました。あなたはそれを微調整して楽しむことができ、さらに高解像度で簡単にレンダリングすることができます。シェーダのパラメータは浮動小数点であり、非正方形のアスペクト比を使用し、楕円の代わりに円を描画するには、シェーダを調整する必要があることに注意してください。やるべき楽しみがいっぱい:)お楽しみください! –

1

オプション1:プリレンダリングスケッチ。

これは、静的な反復パターンのようですので、あなたは、事前にアニメーションを実行し、画像に各フレームを保存することにより、それをプリレンダリングすることができます。あなたは既にsaveFrame()への電話があったことがわかります。保存した画像は、新しいスケッチに読み込んで一度に1フレームずつ再生できます。それは非常に迅速にそれ自身を繰り返すように見えるので、それは非常に多くの画像を必要とすべきではありません。永遠にループするアニメーションGIFを考えてみましょう。

オプション2:あなたのスケッチの解像度を下げます。

ピクセル完全な400x400解像度が本当に必要ですか? 100x100の画像に拡大してスケールアップできますか?

それとも以上1ずつ増加して、forループの解像度を減少させることができた:

for (int x = 0; x <= width; x+=2) { 
    for (int y = 0; y <= height; y+=2) { 

あなたが増加して、より大きなピクセルを描画するstrokeWeight()またはrect()機能を使用してどのくらいで遊ぶことができます。

オプション3:スケッチの時間分解能を下げます。

1フレームごとに1ピクセルずつ移動するのではなく、5フレームごとに5ピクセルずつ移動するとどうなりますか?アニメーションをスピードアップしますが、Xフレームごとに移動するだけで、全体のスピードは同じように見えます。 modulo operatorframeCount変数を使用して、Xフレームごとに何かを実行することができます。スケッチ全体のフレームレートを30または60に保ちたいのですが、Xフレームごとにアニメーションを変更するだけです。

オプション4:アニメーションを簡素化します。

実際にすべてのピクセルを計算する必要はありますか?あなたが示したいものが、サイズが増えていく一連のサークルであれば、これを行う方法はずっと簡単です。 ellipse()関数を呼び出すのは、point()関数を複数回呼び出すよりもはるかに高速です。 point()を毎秒50万回呼び出すことなく、他の関数を使用してぼかし効果を作成することができます(呼び出す頻度です)。

オプション5:はあなたのコードをリファクタリング。

他のすべてが失敗した場合は、コードをリファクタリングする必要があります。あなたがdraw()関数の最後にmouseX, mouseYで楕円を描画し、その性能を比較することによって、これを証明することができます機能 - あなたはループのネストされた内部のpoint()への呼び出しをコメントアウトするときあなたのプログラムの時間のほとんどはpoint()に費やされています。

コンピュータは魔法ではないため、point()関数を毎秒50万回呼び出すことは無料ではありません。上記のオプションの1つ(または複数)を取るか、あるいは他の方法でコードをリファクタリングすることによって、何とかその数を減らさなければなりません。

どのようにそうするのかは、実際にあなたが述べていない実際の目標に依存します。このアニメーションをレンダリングしようとしているだけの場合は、事前レンダリングで問題なく動作します。ユーザーとのやりとりが必要な場合は、解像度を下げるなどの機能が有効になります。あなたは何かを犠牲にしなければならないでしょう、そして、それは本当にあなた次第です。

+0

非常に徹底的な返信をありがとうございます。 **オプション1 **:はい、私はすでにこの方法で試してもうまくいきますが、最終的な結果ではないかもしれません。 **オプション2/3 **:実験の解像度/ステップを減らすことができますが、私の目標はフルHDビデオを生成することです。最終レンダリングでは最初のオプションを使用します。 **オプション4/5 **:ellipse()ははるかに効率的ですが、2つの波の振幅を合計または減算する可能性なしに、全周に同じ色を保ち、次に色を変更しますオーバーラップ – johnnyparafango

+0

@ johnnyparafango「合計/減算」効果を達成するために不透明度を変えた一連の円を使用することができます。または、エフェクトを作成するためにぼかしフィルタや他の方法を使用することもできます。単純な円だけを描くことに限定されるものではありません。しかし、あなたの目標がHDビデオを作成することであれば、あなたのものをフレームに出力し、それらを一緒にステッチすることがおそらく最良の賭けです。 –