Science with OpenGL | #1 | Superposition of two waves
If you have a good background in physics, you will be familiar with the Principle of Superposition of Waves.
The Principle of Superposition of Waves states that, when two or more waves meet at a point, the resultant wave has a displacement which is the algebraic sum of the displacements of each wave.Here is a demonstration of the program we are going to create in this lesson.
Prerequisites
You need to be familiar with the following concepts before you begin this tutorial.
- Sine waves ( expressed as a function of time and position ).
- Various parameters that define a wave ( Amplitude, angular frequency, angular wave number and phase).
- The superposition principle.
Now, we will be working with only very basic features of OpenGL and GLUT (I always use GLUT in my tutorials so if you don't have it yet, its highly recommended that you get it) so even if you are just a beginner, you will be able to follow this tutorial without any problem. Still, I'd like to highlight the the things you need to know about the API.
- Working in the double buffered mode ( GLUT_DOUBLE )
- Setting up an orthographic projection using gluOrtho2d() or glOrtho()
- Translation
- Drawing lines
If you know all this,then you're good to begin.
Initial code
We are going to start off with an empty GLUT project. You'll need to have the following code written and understood beforehand. If you are not able to understand any part of this code, it is highly recommended that you learn some basics about OpenGL fisrt. Comments have been provided wherever necessary. If you have doubts regarding the glutTimerFunc(), you can refer to some documentation or watch this video for detailed explanations regarding the frame rate.
#include <GL/glut.h> #include <iostream>
#include <math.h> void reshape(int,int); void display(); void timer(int); /*initialization*/ void init() { glClearColor(1.0,1.0,1.0,0.0); } int main(int argc,char **argv) { glutInit(&argc,argv); glutInitDisplayMode( GLUT_DOUBLE | GLUT_RGB); glutInitWindowSize(16*30,9*30); glutCreateWindow("Wave Superposition "); init(); glutReshapeFunc(reshape); glutDisplayFunc(display); glutTimerFunc(10,timer,0); /*the registered callback (timer) will be called after 10 milliseconds*/ glutMainLoop(); return 0; } void reshape(int w,int h) { glViewport(0,0,16*30,9*30); glMatrixMode(GL_PROJECTION); glLoadIdentity(); gluOrtho2D(0,160,0,90); //2D orthographic projection glMatrixMode(GL_MODELVIEW); } void display() { glClear(GL_COLOR_BUFFER_BIT); glLoadIdentity(); glutSwapBuffers(); } /* After the timer() function is first called, it first calls glutPostRedisplay() which causes the display function to be called again, and then it again registers itself as the timer callback and is called again after 1000/60 milliseconds ( 1/60th of a second ). This loop continues and gives 60FPS. */ void timer(int) { glutPostRedisplay(); glutTimerFunc(1000.0/60.0,timer,0); }
The orthographic projection has the origin at the bottom left corner so that our screen lies in the first quadrant and the top right corner has the coordinates (160,90).
We will need the math.h header to use the sin() function.
Also note that I have included the iostream header file. We will need this for console input and output.
Getting the data from the user
Harmonic waves can be distinguished by their angular wave number, angular frequency, phase and amplitude. These parameters for both the waves that will be superimposed to produce a resultant wave, will be selected by the user. To keep things simple we will use the console for the input. The program when executed, will ask the user to first input these values, which will be stored in the respective structures of the two waves.
struct wave { float k,w,A,phi; }w1,w2;
Here w1 and w2 will be our two waves. The variables k, w, A and phi are the angular wave number, angular frequency, amplitude and phase respectively. We need to ask the user for the input before creating the OprnGL window, so the input code will go inside the main() function.
Add the following code in the beginning of the main().
std::cout<<"\t[*] Enter the parameters 'k', 'w', 'A' and phase(phi) for two waves:\n"; //wave 1 std::cout<<"\t[*]Wave 1 :\n\t\tk = "; std::cin>>w1.k; std::cout<<"\t\tw = "; std::cin>>w1.w; std::cout<<"\t\tA = "; std::cin>>w1.A; std::cout<<"\t\tphi = "; std::cin>>w1.phi; std::cout<<"\n\t[*] w1 = "<<w1.A<<"sin( "<<w1.k<<"x - "<<w1.w<<"t + "<<w1.phi<<" )\n\n"; //wave 2 std::cout<<"\t[*]Wave 2 :\n\t\tk = "; std::cin>>w2.k; std::cout<<"\t\tw = "; std::cin>>w2.w; std::cout<<"\t\tA = "; std::cin>>w2.A; std::cout<<"\t\tphi = "; std::cin>>w2.phi; std::cout<<"\n\t[*] w2 = "<<w2.A<<"sin( "<<w2.k<<"x - "<<w2.w<<"t + "<<w2.phi<<" )\n\n"; std::cout<<"\t=>The third wave on your screen\n\t will be the superimposed wave";
Note that after the user inputs all the paramenters, the wave equation is displayed in the form
f(x,t) = A sin ( kx - wt + Φ)This is an equation to a harmonic wave.
Plotting the wave
The wave will be a function of time and position. So, we need a global variable which increases with time.
float time=0.0;
it is fairly obvious that the best place to update this variable is the timer() function.
void timer(int) { glutPostRedisplay(); time+=0.5; //update time glutTimerFunc(1000.0/60.0,timer,0); }
We will now define a function to return the ordinate ( y-coordinate ) of a sine wave for a given abcissa ( x-coordinate ) and a particular value of the variable time. This function works according to the equation mentioned above. The parameters of the wave should be provided as the arguments to this function. Do not forget to add a declaration for this function at the top of your program.
float wave_ordinate(float k,float w,float A,float phi,float x) { return A * sin( (k*x) - (w*time) + phi); }
We need to plot three waves, so we will divide our drawing area into three horizontal segments of height 30 each. The code used for drawing the wave is pretty much self explanatory. We use the for loop and call the wave_ordinate() function to find y for various values of x ranging between 0 and 160, and then we plot the point (x,y). All the points are joined in a continuous strip ( GL_LINE_STRIP ). For plotting the resultant wave, we just add the respective displacements ( y-coordinates ) of the two constituent waves. The display function, after adding the code for drawing, should look something like this.
void display() { glClear(GL_COLOR_BUFFER_BIT); glLoadIdentity(); glColor3f(0.0,0.0,1.0); //First wave glTranslatef(0.0,75.0,0.0); glBegin(GL_LINE_STRIP); for(float x = 0 ; x < 160 ; x+=0.5) { float y = wave_ordinate(w1.k,w1.w,w1.A,w1.phi,x); glVertex2d(x,y); } glEnd(); //Second wave glTranslatef(0.0,-30.0,0.0); glBegin(GL_LINE_STRIP); for(float x = 0 ; x < 160 ; x+=0.5) { float y = wave_ordinate(w2.k,w2.w,w2.A,w2.phi,x); glVertex2d(x,y); } glEnd(); //Resultant wave glColor3f(1.0,0.0,0.0); glTranslatef(0.0,-30.0,0.0); glBegin(GL_LINE_STRIP); for(float x = 0 ; x < 160 ; x+=0.5) { /* the resultant displacement is the algebraic sum of the displacements of the two individual waves*/ float y = wave_ordinate(w1.k,w1.w,w1.A,w1.phi,x) + wave_ordinate(w2.k,w2.w,w2.A,w2.phi,x); glVertex2d(x,y); } glEnd(); glutSwapBuffers(); }If you want to make the lines thicker you can add the following call to your init() function.
glLineWidth(3.0);
That's it!
Here is the full source code.
#include <GL/glut.h> #include <iostream> #include <math.h> void reshape(int,int); void display(); void timer(int); float wave_ordinate(float,float,float,float,float); float time=0.0; struct wave { float k,w,A,phi; }w1,w2; /*initialization*/ void init() { glLineWidth(3.0); glClearColor(1.0,1.0,1.0,0.0); } int main(int argc,char **argv) { std::cout<<"\t[*] Enter the parameters 'k', 'w', 'A' and phase(phi) for two waves:\n"; //wave 1 std::cout<<"\t[*]Wave 1 :\n\t\tk = "; std::cin>>w1.k; std::cout<<"\t\tw = "; std::cin>>w1.w; std::cout<<"\t\tA = "; std::cin>>w1.A; std::cout<<"\t\tphi = "; std::cin>>w1.phi; std::cout<<"\n\t[*] w1 = "<<w1.A<<"sin( "<<w1.k<<"x - "<<w1.w<<"t + "<<w1.phi<<" )\n\n"; //wave 2 std::cout<<"\t[*]Wave 2 :\n\t\tk = "; std::cin>>w2.k; std::cout<<"\t\tw = "; std::cin>>w2.w; std::cout<<"\t\tA = "; std::cin>>w2.A; std::cout<<"\t\tphi = "; std::cin>>w2.phi; std::cout<<"\n\t[*] w2 = "<<w2.A<<"sin( "<<w2.k<<"x - "<<w2.w<<"t + "<<w2.phi<<" )\n\n"; std::cout<<"\t=>The third wave on your screen\n\t will be the superimposed wave"; glutInit(&argc,argv); glutInitDisplayMode( GLUT_DOUBLE | GLUT_RGB); glutInitWindowSize(16*30,9*30); glutCreateWindow("Wave Superposition "); init(); glutReshapeFunc(reshape); glutDisplayFunc(display); glutTimerFunc(10,timer,0); /*the registered callback (timer) will be called after 10 milliseconds*/ glutMainLoop(); return 0; } void reshape(int w,int h) { glViewport(0,0,16*30,9*30); glMatrixMode(GL_PROJECTION); glLoadIdentity(); gluOrtho2D(0,160,0,90); //2D orthographic projection glMatrixMode(GL_MODELVIEW); } void display() { glClear(GL_COLOR_BUFFER_BIT); glLoadIdentity(); glColor3f(0.0,0.0,1.0); //First wave glTranslatef(0.0,75.0,0.0); glBegin(GL_LINE_STRIP); for(float x = 0 ; x < 160 ; x+=0.5) { float y = wave_ordinate(w1.k,w1.w,w1.A,w1.phi,x); glVertex2d(x,y); } glEnd(); //Second wave glTranslatef(0.0,-30.0,0.0); glBegin(GL_LINE_STRIP); for(float x = 0 ; x < 160 ; x+=0.5) { float y = wave_ordinate(w2.k,w2.w,w2.A,w2.phi,x); glVertex2d(x,y); } glEnd(); //Resultant wave glColor3f(1.0,0.0,0.0); glTranslatef(0.0,-30.0,0.0); glBegin(GL_LINE_STRIP); for(float x = 0 ; x < 160 ; x+=0.5) { /* the resultant displacement is the algebraic sum of the displacements of the two individual waves*/ float y = wave_ordinate(w1.k,w1.w,w1.A,w1.phi,x) + wave_ordinate(w2.k,w2.w,w2.A,w2.phi,x); glVertex2d(x,y); } glEnd(); glutSwapBuffers(); } /* After the timer() function is first called, it first calls glutPostRedisplay() which causes the display function to be called again, and then it again registers itself as the timer callback and is called again after 1000/60 milliseconds ( 1/60th of a second ). This loop continues and gives 60FPS. */ void timer(int) { glutPostRedisplay(); time+=0.5; //update time glutTimerFunc(1000.0/60.0,timer,0); } float wave_ordinate(float k,float w,float A,float phi,float x) { return A * sin( (k*x) - (w*time) + phi); }
Running the program
Here is a list of suitaible values for the parameters.
- A - between 0 to 10
- k - between 0.05 to 2
- w - between 0.05 to 0.5
These parameters can be both, positive or negative. For a live demonstration of this, watch this video.
Comments
Post a Comment