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 dann occupied und zeigt an, ob hier ein Teilchen ist. Der zweite heißt occupied_lastround und zeigt an, ob hier in der letzten Runde ein Teilchen war. Der dritte heißt occupied_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ür occupied_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 in occupied_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 in occupied_lastframe gesetzt ist, aber nicht mehr in occupied . 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 in occupied_last_but_oneround gesetzt. Der war vorher in occupied_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 in occupied_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.


Log in to reply