freeglut - Pixel im Fenster wandern lassen
-
Hallo, ich habe eine einfache Simulation zu difusion limited aggregation geschrieben und es macht auch eigentlich das, was es soll
#include <GL/glut.h> #include <cmath> #include <vector> #include <cstdlib> #include <ctime> const int width = 600, height = 600; const double alpha = 2, beta=3; const int N_max=100000; double distance(const int &x1, const int &y1, const int &x2, const int &y2){ return sqrt(pow((x1-x2),2)+pow((y1-y2),2)); } void random_walk(std::vector<std::vector<bool> > &occupied, int &a, int &b, const int &origin_x, const int &origin_y, double &radius, double &d){ do{ int z = rand()%4; if((z==0 && a==1)||(z==1 && a==width-2)||(z==2 && b==1)||(z==3 && a==height-2)) continue; else if(z==0) --a; else if(z==1) ++a; else if(z==2) --b; else if(z==3) ++b; d = distance(a,b,origin_x,origin_y); } while(occupied[a+1][b]!=true && occupied[a-1][b]!=true && occupied[a][b+1]!=true && occupied[a][b-1]!=true && d<beta*radius); if(occupied[a+1][b]==true || occupied[a-1][b]==true || occupied[a][b+1]==true || occupied[a][b-1]==true){ occupied[a][b]=true; if(d>radius) radius = d; } } void random_set(const int &origin_x, const int &origin_y){ std::vector<std::vector<bool> > occupied(width, std::vector<bool>(height)); occupied[origin_x][origin_y] = true; glBegin(GL_POINTS); glColor3f(0.0,0.0,0.0); glVertex2f(origin_x,origin_y); glEnd(); double radius = 0.5; double min_set_radius = alpha * radius; int N = 0; while(N<N_max && min_set_radius<width-1){ int a, b; double d; do{ a = rand() % (width-2) +1; b = rand() % (height-2)+1; d=distance(a,b,origin_x,origin_y); } while(d <= min_set_radius || d > beta*radius); random_walk(occupied,a,b,origin_x,origin_y,radius,d); if(occupied[a][b]==true){ glBegin(GL_POINTS); glColor3f(1.0,0.0,0.0); glVertex2f(a,b); glEnd(); ++N; min_set_radius = alpha * radius; } } } void draw(){ int origin_x = width/2, origin_y = height/2; srand(time(0)); glClear(GL_COLOR_BUFFER_BIT); random_set(origin_x, origin_y); glFlush(); } int main(int argc, char **argv){ glutInit(&argc, argv); glutInitDisplayMode(GLUT_SINGLE | GLUT_RGB ); glutInitWindowPosition(400,50); glutInitWindowSize(width,height); glutCreateWindow("diffusion limited aggregation simulation"); glClearColor(1,1,1,1); glMatrixMode(GL_PROJECTION); glLoadIdentity(); glOrtho(0,width,height,0,-1.0,1.0); glutDisplayFunc(draw); glutMainLoop(); return 0; }
Ich möchte das aber jetzt in "Echtzeit" haben, d.h. dass man jedes Teilchen bzw. Pixel sieht, wie es den Randomwalk macht und sich anlagert bzw. sich für immer entfernt. Und deshalb meine Frage, wiel ich davon keine Ahnung habe. Wie lässt man ein Teilchen z.B. von Anfang des Fensters nach ganz rechts wandern?
Vielen Dank!
-
Meinst du, dass das Teilchen einfach von bspw. (0,10) auf (599,10) springt (bei einer Fenstergröße von 600x600? Also von der ersten Spalte in die letzte?
Wie bei die Asteroiden bei Asteroids http://www.youtube.com/watch?v=cZfsnA7dAHI ?Einfach prüfen, wann dein Teilchen den eigentlichen Viewport verlässt. D.h., wenn random_walk in occupied etwas an die Position -1 oder eben 600 setzen will, jeweils auf die andere Seite setzen. In Zeile 19 fragst du ja, ob die Koordinaten schon am Rand sind und eine Bewegung den Rand verlassen würde. Hier kein "continue" machen, sondern berechnen, in welche Richtung es geht und dann an die andere Seite des Bildes springen.
Nennt sich "wrap around".
-
Nein, ich mein dass man das Teilchen beim Wandern beobachten kann, also dass jedes Pixel, wo es war aufleuchtet und dann wieder erlischt, wenn es weiterwandert. Wie gesagt, ich hab keine große Ahnung von Grafikprogrammierung bzw. Programmierung an sich. Man braucht ja auch die Geschwindigkeit, mit der das Teilchen wandert und ich weiß nicht wie man das alles implementiert.
-
Achso. Dann mach doch mehrere std::vector<bool> (bei so einem kleinen Problem darf man ruhig großzügig sein, was den Speicher angeht).
Der erste heißt dannoccupied
und zeigt an, ob hier ein Teilchen ist. Der zweite heißtoccupied_lastround
und zeigt an, ob hier in der letzten Runde ein Teilchen war. Der dritte heißtoccupied_last_but_oneround
für die Stellen, wo in der vorletzten Runde ein Teilchen war... usw...Beim Zeichnen nimmst du dann 100% Farbe für
occupied
, 75% Farbe füroccupied_lastround
usw. an. Damit gibt es einen Effekt, der an Verblassen erinnert.Und beim Update der Teilchen setzest du dann der Reihe nach die Positionen in das jeweils nächste Feld, damit die schön weiter wandern.
-
Ok, bisher war ja aber doch, das ich am ende einen flush gemacht hab und er es dann gezeichnet hat. Wie lösche ich denn ein schon gezeichnetes Pixel?
-
Gar nicht. Darum geht es ja auch nicht.
Du zeichnest alles, was in den verschiedenen std::vector<> drin steht. Wenn dann ein neues Bild gezeichnet werden soll, schaust du nach, welche Positionen in
occupied
gesetzt sind, kopierst diese inoccupied_lastframe
und berechnest dann erst den random walk.Gelöscht wird auf dem Bildschirm eigentlich nie etwas (von glClear() abgesehen).
In folgendem Bild ist das mal illustriert: http://picload.org/view/lilaiol/grid.png.html
Ganz links ist das 1. Frame. Hierbei ist nur
occupied
gesetzt, alles andere ist false. Im 2. Frame ist das Teilchen dann gewandert. Gleichzeitig hast du aber die alte Position so gesetzt, dass sie inoccupied_lastframe
gesetzt ist, aber nicht mehr inoccupied
. Der aktuelle Platz wird dann in rot gezeichnet, der alte Platz in orange. Im 3. Frame gehts dann noch eins weiter, hier haben wir sogar noch einen Platz inoccupied_last_but_oneround
gesetzt. Der war vorher inoccupied_lastround
und ist nun eins weitergesetzt worden. Dieser Platz wird dann nochmal etwas heller gezeichnet.Wenn es dann wirklich ans zeichnen geht, zeichnest du alle Positionen in
occupied
in rot, dann alle, die inoccupied_lastround
gesetzt sind, in orange usw.Zeichnen musst du immer alles, das ist die Philosophie bei OpenGL (und DirectX). Wichtig ist, dass du dir selbst merkst, welche Zellen zu welcher Zeit besetzt waren.