Free-to-Use Rainbow Circle Shader

Today I saw this post from 3 months ago by @xThomas. He was asking how to make a rainbow circle shader, and I wanted to give it a shot. I wasn’t sure if I should comment on his old post or if I should make a new one. The shader is free for use and easy to use. Here is an example project using the shader.

--# Main
function setup()
    width,height = HEIGHT,HEIGHT
    
    circleM = mesh()
    circleM:addRect(WIDTH/2,HEIGHT/2,width,height) -- add a square to the mesh
    circleM.shader = shader(RBCircle.vertexShader,RBCircle.fragmentShader)
end

function draw()
    background(0)
    circleM:draw()
end

function touched(t)
    if t.state ~= ENDED then
        width,height = width + t.deltaX*2, height + t.deltaY*2
        circleM:setRect(1,WIDTH/2,HEIGHT/2,width,height)
    end
end

RBCircle = {
vertexShader = [[
uniform mat4 modelViewProjection;
    
attribute vec4 position;
attribute vec4 color;
attribute vec2 texCoord;

varying lowp vec4 vColor;
varying highp vec2 vTexCoord;
void main()
{
 vColor = color;
 vTexCoord = texCoord;
 
 gl_Position = modelViewProjection * position;
}
]],
fragmentShader = [[
precision highp float;
varying lowp vec4 vColor;
varying highp vec2 vTexCoord;

vec4 mixCol(vec4 C1, vec4 C2, float i) //To make the mixing easier, We'll have this function
{ // C1 is the 1st colour, C2 is the 2nd colour, i is a float for interpolation
    return C1 * (1.0 - i) + C2 * i;
}

void main()
{
    highp vec4 col;
    lowp float dis = distance(vTexCoord,vec2(0.5,0.5)); //get the distance from the texCoord to the center
    if (dis < 0.5) { //0.5 is the size of the circle. It's set to take up the whole rectangle
        //To make a gradient that transitions through each colour of the rainbow, we'll actually need to make
        //5 gradients:
        //red -> yellow, yellow -> green, green -> turqoise, turquoise -> blue, blue -> purple
        if (vTexCoord.y < 0.2) col = mixCol(vec4(1,0,0,1),vec4(1,1,0,1),vTexCoord.y/0.2); //red to yellow
        else if (vTexCoord.y < 0.4) col = mixCol(vec4(1,1,0,1),vec4(0,1,0,1),(vTexCoord.y - 0.2)/0.2);//yel to gre
        else if (vTexCoord.y < 0.6) col = mixCol(vec4(0,1,0,1),vec4(0,1,1,1),(vTexCoord.y - 0.4)/0.2);//gre to tur
        else if (vTexCoord.y < 0.8) col = mixCol(vec4(0,1,1,1),vec4(0,0,1,1),(vTexCoord.y - 0.6)/0.2);//tur to blu
        else col = mixCol(vec4(0,0,1,1),vec4(1,0,1,1),(vTexCoord.y - 0.8)/0.2); // blue to purple
    }
    else {col = vec4(0,0,0,0);}
    gl_FragColor = col;
}
]]
}

@Kolosso Nice job. I like it when people use shaders because I can learn from them. If I run your program in fullscreen landscape mode and move my finger in a large circle, the image gives me an uneasy feeling. Your rainbow circle is similar to what I have in the link below. Below that is another version based on my original code.

https://codea.io/talk/discussion/8375/rainbow-rectangle
supportedOrientations(LANDSCAPE_ANY)

function setup()
    parameter.integer("size",100,2000,750)
    print("Please wait while I finish my calculations, approx 10 seconds.")
    print("Use the parameter to change the size, and use your finger to move the circle around the screen.")
    dx,dy=0,0
    tab1={}
    r,g,b=0,255,255
    y=WIDTH
    while y>=0 do
        table.insert(tab1,vec3(r,g,b))
        y=y-WIDTH/(256*6)
        if r==255 and g==0 and b==0 then
            rv,gv,bv=0,1,0
        elseif r==255 and g==255 and b==0 then
            rv,gv,bv=-1,0,0
        elseif r==0 and g==255 and b==0 then
            rv,gv,bv=0,0,1
        elseif r==0 and g==255 and b==255 then
            rv,gv,bv=0,-1,0
        elseif r==0 and g==0 and b==255 then
            rv,gv,bv=1,0,0
        elseif r==255 and g==0 and b==255 then
            rv,gv,bv=0,0,-1
        end 
        r=r+rv
        g=g+gv
        b=b+bv       
    end
    w=#tab1//2
    img=image(#tab1,#tab1)
    setContext(img)
    background(0)
    noFill()
    strokeWidth(1)
    for z=1,#tab1 do
        stroke(tab1[z].x,tab1[z].y,tab1[z].z)
        ellipse(w,w,z)        
    end
    setContext()
end

function draw()
    background(0)
    sprite(img,WIDTH/2+dx,HEIGHT/2+dy,size)
end

function touched(t)
    if t.state==MOVING then
        dx=dx+t.deltaX
        dy=dy+t.deltaY
    end
end

@dave1707 That’s a cool effect! I’m pretty new to shaders but I think my little experience with C# helps me understand it better. I’m also very surprised you would learn something from me. As a beginner I had always looked up to you and try to learn from your code. The last thing I would have ever expected was for someone as experienced as you to learn something from me.

And thank-you for inspiring me throughout my whole journey with all of your examples. :slight_smile:

@Kolosso Thanks for the kind words. I don’t do a lot of shader coding, so I do learn something when I see them. And I also learn things from some Codea code. I tend to use the same variable names and code things the same way, so when someone codes something in a different way, I find that interesting. Looking at small examples was one way I learned things. I would take that code and play with it until I understood what was happening, then I would start to modify it just to see what would happen. I’ve been using Codea for over 5 years and there’s still things I don’t know which keeps me interested.