Part of the Khronos Group
OpenGL.org

The Industry's Foundation for High Performance Graphics

from games to virtual reality, mobile phones to supercomputers

Page 1 of 2 12 LastLast
Results 1 to 10 of 14

Thread: How does one create animated rgb rainbow effect on objects(lines,quads,triangles)?

  1. #1
    Junior Member Newbie
    Join Date
    Feb 2018
    Posts
    20

    Question How does one create animated rgb rainbow effect on objects(lines,quads,triangles)?

    Well i got stuck for quite a, while trying to answer myself this question i tried everything from, do to for loops to change colors gradiently from red to red in rgb spectrum but my project would stop working or will not give any results at all.
    In reality i want to make object in my case line to change colors in the whole rgb spectrum gradiently like this http://csharphelper.com/blog/2017/08...inbow-colors-c
    Could anybody help me with this problem ?

    If link doesn"t work then. Click image for larger version. 

Name:	howto_animate_rainbow_colors.jpg 
Views:	39 
Size:	7.7 KB 
ID:	2668

    If anybody`s wondering im using C++
    Last edited by StabberKnight906; 02-12-2018 at 08:57 AM. Reason: Additional information and linking

  2. #2
    Senior Member OpenGL Guru Dark Photon's Avatar
    Join Date
    Oct 2004
    Location
    Druidia
    Posts
    4,571
    This is a perfect job for texture mapping. Upload your color gradient to a 1D texture. And then in the shader, take some value (with a min/max associated with those values), map it from (min..max) to (0..1), and then do a filtered texture lookup into your 1D texture. If you've enabled LINEAR filtering, the texture sampling will automatically interpolate the correct color from your gradient texture map even if its texture coordinate lies between 2 texel centers in the texture map.

    One other quick note: If the number of texels in your texture map is large, you may not care about this next point -- just use your 0..1 texture coordinate (which runs from the left edge of the left-most texel in the texture to the right edge of the right-most texel in the texture), and that'll be close enough for a decent result. However...

    When you're using a texture as a linear lookup function like this, it's actually more correct to have your texture coordinates run between the center of the left texel to the center of the right texel. The reason is because that in OpenGL, the "data" associated with each texel (a color, in your case) is conceptually located at the centers of the texels (cell-centered data). That way, you get smooth interpolation between your min and max colors over the entire 0..1 range.

    To get this modified texture coordinate from your 0..1 input value is easy. See this for details. But basically, take your 0..1 value (we'll call this tc), and run it through this expression:

    Code glsl:
    tc * (M-1)/float(N) + 0.5/N

    where M = number of "used" texels in your texture, and N = total number of texels in your texture. If you're using the whole width of the texture for data, then M == N. All this expression does is "squish" your 0..1 value into one that runs between the centers of the edge texels instead of the edges of the edge texels.

    Now you might be wondering if you don't do this, what happens when you sample your texture within a 1/2 texel of the edge of a texture (or more correctly, the edge of a texture MIP level). That's where the wrap mode you've set on the texture axis comes in. If you use GL_CLAMP_TO_EDGE, then you'll get the same color value all the way out from the edge texel's center out to the edge of the texture.
    Last edited by Dark Photon; 02-13-2018 at 07:18 AM.

  3. #3
    Junior Member Newbie
    Join Date
    Feb 2018
    Posts
    20

    Smile Thank You! But....

    Quote Originally Posted by Dark Photon View Post
    This is a perfect job for texture mapping. Upload your color gradient to a 1D texture. And then in the shader, take some value (with a min/max associated with those values), map it from (min..max) to (0..1), and then do a filtered texture lookup into your 1D texture. If you've enabled LINEAR filtering, the texture sampling will automatically interpolate the correct color from your gradient texture map even if its texture coordinate lies between 2 texel centers in the texture map.

    One other quick note: If the number of texels in your texture map is large, you may not care about this next point -- just use your 0..1 texture coordinate (which runs from the left edge of the left-most texel in the texture to the right edge of the right-most texel in the texture), and that'll be close enough for a decent result. However...

    When you're using a texture as a linear lookup function like this, it's actually more correct to have your texture coordinates run between the center of the left texel to the center of the right texel. The reason is because that in OpenGL, the "data" associated with each texel (a color, in your case) is conceptually located at the centers of the texels (cell-centered data). That way, you get smooth interpolation between your min and max colors over the entire 0..1 range.

    To get this modified texture coordinate from your 0..1 input value is easy. See this for details. But basically, take your 0..1 value (we'll call this tc), and run it through this expression:

    Code glsl:
    tc * (M-1)/float(N) + 0.5/N

    where M = number of "used" texels in your texture, and N = total number of texels in your texture. If you're using the whole width of the texture for data, then M == N. All this expression does is "squish" your 0..1 value into one that runs between the centers of the edge texels instead of the edges of the edge texels.

    Now you might be wondering if you don't do this, what happens when you sample your texture within a 1/2 texel of the edge of a texture (or more correctly, the edge of a texture MIP level). That's where the wrap mode you've set on the texture axis comes in. If you use GL_CLAMP_TO_EDGE, then you'll get the same color value all the way out from the edge texel's center out to the edge of the texture.
    After a whole month of training how to manipulate pictures(textures) in opengl thank you !
    That advice is helpful for me but the problem is that i do not want to use any pictures,I want to infinitely cycle through an rgb without texture.
    Im trying to use an HSV method(without black and white) but the main problem is that after testing my test code which transitions from red to yellow one time(while( r>=rgbmax){g++;if(g>=rgbmax){break;}),My display only shows yellow line without transition from red to yellow no matter which timer i put in test code,And im using glutPostRedisplay() in this code.

    How does one make in opengl,transition smoothly between colors ?
    Last edited by StabberKnight906; 03-20-2018 at 10:49 AM.

  4. #4
    Senior Member OpenGL Guru
    Join Date
    Jun 2013
    Posts
    3,008
    Quote Originally Posted by StabberKnight906 View Post
    How does one make in opengl,transition smoothly between colors ?
    You need to ensure that the display function is called repeatedly, e.g. by calling glutPostRedisplay() from within the display function. Also, be sure to call glutSwapBuffers() at the end of the display function.

  5. #5
    Junior Member Newbie
    Join Date
    Feb 2018
    Posts
    20
    Quote Originally Posted by GClements View Post
    You need to ensure that the display function is called repeatedly, e.g. by calling glutPostRedisplay() from within the display function. Also, be sure to call glutSwapBuffers() at the end of the display function.
    So this means that my code instead of looking like this:

    Code cpp:
    void rainboweffect(int r,int g,int b){
      r=255;
      g=0;
      b=0;
      while( r>=rgbmax)
      {g++;
        if(g>=rgbmax){break;}
      }
      glutPostRedisplay()
    }

    should look like this:

    Code cpp:
    void rainboweffect(int r,int g,int b){
      r=255;
      g=0;
      b=0;
      while( r>=rgbmax)
      {g++;glutPostRedisplay();
        if(g>=rgbmax){break;}
      }
      glutSwapBuffers();
    }
    ??

    If this isn't right could somebody net me a code of a simple smooth color transition to examine how it works?

    Edit:
    Well i still couldn't make my simple rgb cycle line work,if i do an endless loop it would stop responding or if you make one time transition from red to yellow it would show only yellow without transition.
    And these voids go into the void display();
    And thank you Dark Photon for highlighting my code.
    Last edited by StabberKnight906; 03-21-2018 at 08:11 AM.

  6. #6
    Intern Contributor
    Join Date
    Oct 2014
    Posts
    94
    Without trying your code and without being an absolute expert, I see some problems here:

    Code :
    void rainboweffect(int r,int g,int b){
    r=255;
    g=0;
    b=0;
    while( r>=rgbmax)
    {
        g++;  //<---  1.
        glutPostRedisplay();
        if(g>=rgbmax)
        {
            break;
        }
    }
    glutSwapBuffers();   //<---  2.

    1. You increase the color value g using the command g++ inside a loop. Thats fine for the compiler and the execution of your program, but not if you actually want to see something. On modern hardware the value g==255 is reached almost instantaneous. This means that the color change is to fast for your eyes to notice something. include the following headers:
    Code :
    #include <thread>
    #include <chrono>

    and then this line INSIDE your loop:

    Code :
    std::this_thread::sleep_for(20ms);

    This will cause a 20ms break for every loop iteration.


    2. If you are using a double buffered render context you have two buffers, as the name suggests. One that is shown on screen and one that is hidden. All your render commands write to the off screen buffer, so that you won't see anything until you call glutSwapBuffers(). This swaps both buffers, making the offscreen buffer visible and your previous screen image your new buffer for rendering. This is done to present always the fully rendered scene to you without items that pop up one after each other. In your code you call glutSwapBuffers() after the loop, which means only once! So you only get to see the image that was drawn during your last loop iteration. This means that you have to move glutSwapBuffers() also into the loop. I am not so sure what glutPostRedisplay() does, but as far as I remember it just tells your rendercontext (the output window), that it should refresh during the next screen refresh. This means it should redraw the current screen buffer. So you probably need both inside the loop. glutPostRedisplay() to put your drawn stuff into the onscreen buffer and glutPostRedisplay() for actually refreshing your Window. --- but as I said --- don't take anything I said about glutPostRedisplay() for the truth, its been a while since I used it.

  7. #7
    Junior Member Newbie
    Join Date
    Feb 2018
    Posts
    20
    Quote Originally Posted by ProgrammerX View Post
    Without trying your code and without being an absolute expert, I see some problems here:

    Code :
    void rainboweffect(int r,int g,int b){
    r=255;
    g=0;
    b=0;
    while( r>=rgbmax)
    {
        g++;  //<---  1.
        glutPostRedisplay();
        if(g>=rgbmax)
        {
            break;
        }
    }
    glutSwapBuffers();   //<---  2.

    1. You increase the color value g using the command g++ inside a loop. Thats fine for the compiler and the execution of your program, but not if you actually want to see something. On modern hardware the value g==255 is reached almost instantaneous. This means that the color change is to fast for your eyes to notice something. include the following headers:
    Code :
    #include <thread>
    #include <chrono>

    and then this line INSIDE your loop:

    Code :
    std::this_thread::sleep_for(20ms);

    This will cause a 20ms break for every loop iteration.


    2. If you are using a double buffered render context you have two buffers, as the name suggests. One that is shown on screen and one that is hidden. All your render commands write to the off screen buffer, so that you won't see anything until you call glutSwapBuffers(). This swaps both buffers, making the offscreen buffer visible and your previous screen image your new buffer for rendering. This is done to present always the fully rendered scene to you without items that pop up one after each other. In your code you call glutSwapBuffers() after the loop, which means only once! So you only get to see the image that was drawn during your last loop iteration. This means that you have to move glutSwapBuffers() also into the loop. I am not so sure what glutPostRedisplay() does, but as far as I remember it just tells your rendercontext (the output window), that it should refresh during the next screen refresh. This means it should redraw the current screen buffer. So you probably need both inside the loop. glutPostRedisplay() to put your drawn stuff into the onscreen buffer and glutPostRedisplay() for actually refreshing your Window. --- but as I said --- don't take anything I said about glutPostRedisplay() for the truth, its been a while since I used it.
    Tried it and screen is black without line if i put glutSwapBuffers() and glutPostRedisplay() in loop with 40 ms timer.
    Even so i tried to move glutSwapBuffers and glutPostRedisplay() out of the loop and it would not respond approximately 1 second and after 1 second i would get yellow line without transition from red to yellow.

    Edit:What is shocking is that rand()255% to all three rgb values and glutPostRedisplay at the end works,but i donīt want random color flashing every 40ms i want infinite smooth rgb color cycle without using pictures or textures and now im testing color transition from red to yellow.
    Last edited by StabberKnight906; 03-21-2018 at 02:39 PM. Reason: Additional Clarity

  8. #8
    Intern Contributor
    Join Date
    Oct 2014
    Posts
    94
    The black screen sounds like you messed up the order of the two commands. First swap the buffers, then update the window with glutPostRedisplay. Try this one here inside the while loop:

    Code :
     
        g++;
     
        glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); // Clear the offscreen buffer
     
       // Do some rendering here and don't forget to update your color values on the VBO, uniform or whatever you use to get the color to your shader program.
     
        glutSwapBuffers();    // swap the buffers
        glutPostRedisplay();   // refresh the render contest
     
       std::this_thread::sleep_for(20ms);

    This actually code that works in one of my samples.

  9. #9
    Junior Member Newbie
    Join Date
    Feb 2018
    Posts
    20
    Quote Originally Posted by ProgrammerX View Post
    The black screen sounds like you messed up the order of the two commands. First swap the buffers, then update the window with glutPostRedisplay. Try this one here inside the while loop:

    Code :
     
        g++;
     
        glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); // Clear the offscreen buffer
     
       // Do some rendering here and don't forget to update your color values on the VBO, uniform or whatever you use to get the color to your shader program.
     
        glutSwapBuffers();    // swap the buffers
        glutPostRedisplay();   // refresh the render contest
     
       std::this_thread::sleep_for(20ms);

    This actually code that works in one of my samples.
    Well for myself i got only a flashing yellow line each 3 seconds if i use Sleep function i have to wait 3 seconds to make window to respond and yellow line shows up each 9 seconds.
    Maybe i made some mistakes ?

    Code :
    /*
     * main.cpp
     *
     *  Created on: Mar 19, 2018
     *      Author: Daniel
     */
     
     
    #include <iostream>
    #include <cstdlib>
    #include <cmath>
    #include <string>
    #include <GL/GLUT.H>
    #include <windows.h>
    #include <thread>
    #include <chrono>
     
     
    using namespace std;
    void initgl()
    {
    	glClear(GL_COLOR_BUFFER_BIT|GL_COLOR_BUFFER_BIT);
    	glClearColor(0.0, 0.0, 0.0, 1.0);
    }
    void rainbowcolor(int & r,int & g,int & b){
    	int rgbmax=255;
    		int rgbmin=1;
    		//int rgb{255,0,0}
    		r=255;
    		g=0;
    		b=0;
     
    		while(r>=rgbmax){
    			g++;
    			glClear(GL_COLOR_BUFFER_BIT|GL_COLOR_BUFFER_BIT);
    			glutSwapBuffers();
    			glutPostRedisplay();
    			if(g>=rgbmax){
    				break;
    			}
    		}
     
     
     
    }
    void anarchy(int & r,int & g,int & b){
    	r=rand()%255;
    	g=rand()%255;
    	b=rand()%255;
    }
     
    void draw(GLfloat x1,GLfloat x2,GLfloat y1,GLfloat y2){
    x1=0;
    x2=0;
    y1=-1.0;
    y2=0;
    glBegin(GL_LINES);
    glVertex2f(x1,y1);
    glVertex2f(x2,y2);
    glEnd();
     
    }
     
    void display(){
    	float x1,x2,y1,y2;
    	int r,g,b;
    	glLineWidth(5.5);
    	rainbowcolor(r,g,b);
    	glColor3ub(r,g,b);
    	draw(x1,x2,y1,y2);
    	glutSwapBuffers();
    	glFlush();
     
    }
     
    int main(int argc, char** argv){
    	 glutInit(&argc, argv); // Initialize GLUT
    		   glutInitWindowSize(480,860);   // Set the window's initial width & height
    		   glutInitWindowPosition(100, 100); // Position the window's initial top-left corner
    		   glutCreateWindow("Test");  // Create window with the given title
    		   glutDisplayFunc(display);			// Register callback handler for window re-paint event
    		   initgl();                       // Our own OpenGL initialization
    		   glutMainLoop();                 // Enter the event-processing loop
    		return 0;
    }

  10. #10
    Intern Contributor
    Join Date
    Oct 2014
    Posts
    94
    Now that I got the full picture I was able to fix it. You messed up some things. When you call glutMainLoop(); ,your display function is called. There you set all your initial rgb values as TEMPORARY function variables. Then you call your rainbowcolor(r,g,b). Here you enter a while loop which increases g until it is 255 without sending your color value to the gpu or making a single openGL draw call. When your function is finished, you continue in your display function with
    Code :
    	glColor3ub(r,g,b);
    	draw(x1,x2,y1,y2);
    	glutSwapBuffers();
    	glFlush();

    At this point you actually send the color to your GPU, but here g is already 255. Then you draw your Line. Actually I checked your code and you did not set double buffering. So you don't need glutSwapBuffers(). Even if you did, glFlush does the same. Since you called glutPostRedisplay in your rainbowcolor function, the main loop from glut knows that it has to call the display function again in the next loop iteration. But now you repeat the same thing as before, since your r,g,b values are temporaries and are set back to the initial values. Anyways, you can achieve what you want by deleting your rainbow function and replacing your display function with the following one:

    Code :
    void display()
    {
        float x1, x2, y1, y2;
        static int r = 255, g = 0, b = 0;
        static int incdec = 1;
        g += incdec;
        if (g == 255)
            incdec = -1;
        if (g == 0)
            incdec = 1;
        glLineWidth(5.5);
        glColor3ub(r, g, b);
        draw(x1, x2, y1, y2);
        glFlush();
        std::this_thread::sleep_for(5ms);
        glutPostRedisplay();
    }

    What it does is the following:
    The variables r,g,b are now static. That means the values stay the same when the function is called a second time. If g was 42 at the end of the first call, then it is 42 when you call the function a second time and so on. Only the first call sets it to 0. Then you add incdec to your g. The incdec variable is just introduced to let g grow until 255 and fall back to 0 afterwards. Now you upload the color, draw your line and tell OpenGL to finish the drawing with glFlush. Then you wait 5ms and call glutPostRedisplay, which lets the main loop call your display function again. Since g is static, it has now a value of 1. You increase it to 2 and draw all your stuff again.... and again.

    EDIT: After re-reading some stuff, forget the thing I said about the side effects of glutPostRedisplay. It just tells gluts main loop, that the display needs to be updated during the next loop iteration, which means calling the display function. But that happens when your current display function call is finished. I have deleted the wrong statements to avoid confusing others who might read this.
    Last edited by ProgrammerX; 03-21-2018 at 06:28 PM.

Posting Permissions

  • You may not post new threads
  • You may not post replies
  • You may not post attachments
  • You may not edit your posts
  •