Free-to-Use Shaders

Here I’ll post simple or complex shaders for those who want to learn from them or those who want to use them. Do what you like with them. Feedback is welcome. Each shader will be accompanied with an example project that uses it.

Sky Gradient

This is a shader that produces a gradient that looks very similar to the sky and can be used for less boring backgrounds.
Difficulty: This shader may be confusing for beginners
Shader

shader([[ --vertex shader
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;
}]],
[[ --fragment shader
precision highp float;
uniform lowp sampler2D texture;
varying lowp vec4 vColor;
varying highp vec2 vTexCoord;
void main()
{
    lowp vec4 col;
    lowp vec4 highCol = vec4(0.352,0.745,1,1); //90,190,255
    lowp vec4 lowCol = vec4(0.705,0.862,1,1); //180, 220, 255
        //lowCol = vec4(0.1,0.1,0.1,1); highCol = vec4(0.95,0.95,0.95,1);
    highp float size = 1.9;
    highp float dis = distance(vTexCoord,vec2(0.5,size * -1.0 + 0.40));
    highp float blur = 0.3;
    if (dis < size + blur) {
        lowp float min = size;
        if (dis > min) {
            lowp float bri = (dis - min) / (blur);
            lowp vec4 final = lowCol * (1. - bri) + highCol * bri;
            final.a = 1.;
            col = final;
        }
        else col = lowCol;
    }
    else {col = highCol;}
    gl_FragColor = col;
}]])

Example Project

--# Main
displayMode(FULLSCREEN) -- This function sets it to Fullscreen
function setup()
    BG = mesh()
    BG.shader = shader([[
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;
}]],[[
precision highp float;
uniform lowp sampler2D texture;
varying lowp vec4 vColor;
varying highp vec2 vTexCoord;
void main()
{
    lowp vec4 col;
    lowp vec4 highCol = vec4(0.352,0.745,1,1); //90,190,255
    lowp vec4 lowCol = vec4(0.705,0.862,1,1); //180, 220, 255
        //lowCol = vec4(0.1,0.1,0.1,1); highCol = vec4(0.95,0.95,0.95,1);
    highp float size = 1.9;
    highp float dis = distance(vTexCoord,vec2(0.5,size * -1.0 + 0.40));
    highp float blur = 0.3;
    if (dis < size + blur) {
        lowp float min = size;
        if (dis > min) {
            lowp float bri = (dis - min) / (blur);
            lowp vec4 final = lowCol * (1. - bri) + highCol * bri;
            final.a = 1.;
            col = final;
        }
        else col = lowCol;
    }
    else {col = highCol;}
    gl_FragColor = col;
}]])
    BG:addRect(WIDTH/2,HEIGHT/2,WIDTH,HEIGHT)
end

-- This function gets called once every frame
function draw()
    BG:draw()
end

Rainbow Circle

This shader produces a circle with a gradient that goes from red - yellow - green - turquoise - blue - purple and is very easy to use.
Difficulty: This shader should only be slightly difficult for beginners to understand
Shader

shader([[ -- vertex shader (nothing changed)
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;
}
]],
[[ -- fragment shader
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;
}
]])

Example Project

--# 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;
}
]]
}

Green Screen

This shader can be used to create a very simple green screen that compares a given colour to each pixel in a photo, and replaces the ones that are too similar
Difficulty: This shader should be fairly easy for beginners
Shader Inputs
sens: float (number), determines the sensitivity of the green screen
texture: sampler2D (image), the main photo (normally the CAMERA)
overTex: sampler2D (image), the photo in the background
sample: vec4 (color), the colour of the pixels that will be replaced
inactive: bool (boolean), while true, the pixels of texture are not changed
Shader

shader([[uniform mat4 modelViewProjection; -- vertex shader
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; }]],
[[ -- fragment shader
precision highp float; -- This sets the default precision of all float variables

uniform lowp float sens;
uniform lowp sampler2D texture;
uniform lowp sampler2D overTex;
uniform lowp vec4 sample;
uniform bool inactive;

varying lowp vec4 vColor;
varying highp vec2 vTexCoord;

void main()
{
    lowp vec4 col = texture2D( texture, vTexCoord ); -- get the pixel of texture at vTexCoord
    if (inactive == false && col.r > sample.r - sens && col.r < sample.r + sens && col.g > sample.g - sens && col.g < sample.g + sens && col.b < sample.b + sens && col.b > sample.b - sens) {
        col = texture2D( overTex, vTexCoord ); -- get the pixel colour of overTex at vTexCoord
    }
    gl_FragColor = col;
}
]])

Example Project

--# Main

function setup()
    info = ""
    infoTimer = 0
    sampling = true
    cameraSource(CAMERA_FRONT)
    m = mesh()
    m:addRect(WIDTH/2,HEIGHT/2,0,0)
    m.texture = CAMERA -- 1920 x 1080
    m.shader = shader(Ghost.v,Ghost.f)
    m.shader.inactive = true
    parameter.boolean("Camera_Front",false,function(b)
        cameraSource((b and CAMERA_FRONT) or CAMERA_BACK)
    end)

    -- Hooray! Variables for playing with! : )
    replaceTexture = readImage("SpaceCute:Background") -- The texture that will replace certain pixels. (Will be stretched to fit the screen)
    sensitivity = 0.2 -- How different a pixels colour can be from the sample colour before being replaced
end

function draw()
    background(40, 40, 50)
    camW,camH = spriteSize(CAMERA)
    if CurrentOrientation == LANDSCAPE_LEFT or CurrentOrientation == LANDSCAPE_RIGHT then
        m:setRect(1,0,0,camW*(WIDTH/camW),camH*(WIDTH/camW))
        else
        m:setRect(1,0,0,camW*(HEIGHT/camH),camH*(HEIGHT/camH))
    end
    translate(WIDTH/2,HEIGHT/2)
    noSmooth()
    m:draw()
    if sampling then
        stroke(255, 255, 255, 255)
        strokeWidth(5)
        if sampleC then -- sampleC is the variable that holds the sampled colour
            fill(sampleC)
            else
            noFill()
        end
        ellipse(0,0,30)
        line(-110,0,-10,0)
        line(10,0,110,0)
        line(0,-110,0,-10)
        line(0,10,0,110)
        fill(255)
        fontSize(20)
        smooth()
        text("Tap the center of the screen to sample the colour",0,HEIGHT/2 - 30)
        text("Tap outside of the center to activate the shader",0,HEIGHT/-2 + 30)
        else
        smooth()
        text("Tap anywhere to return to the colour sampler",0,HEIGHT/-2 + 30)
    end
    if infoTimer > 0 then -- This is responsible for the blue text that appears in the center of the screen.
        infoTimer = infoTimer - DeltaTime
        fill(0,200,255)
        fontSize(30)
        text(info,0,-200)
    end
end

function touched(t)
    if t.state == 0 then
        if sampling and ((t.x - WIDTH/2)^2 + (t.y - HEIGHT/2)^2)^0.5 < 180 then
            if not sampleC then
                info = "This colour will now be used in the shader"
                infoTimer = 4
            end

            local samCam = image(WIDTH,HEIGHT)
            pushStyle()
            smooth()
            setContext(samCam)
            m:draw()
            setContext()
            popStyle()
            local all = {r=0,g=0,b=0,c=0}
            for x = WIDTH//2 - 10, WIDTH//2 + 10 do
                for y = HEIGHT//2 - 10, HEIGHT//2 + 10 do
                    if ((x - WIDTH//2)^2 + (y - HEIGHT//2)^2)^0.5 < 10 then--if this pixel is in a 10 pixel radius
                        local r,g,b = samCam:get(x,y)
                        all.r = all.r + r
                        all.g = all.g + g
                        all.b = all.b + b
                        all.c = all.c + 1 
                    end
                end
            end
            all.r,all.g,all.b = all.r/all.c,all.g/all.c,all.b/all.c
            sampleC = color(all.r,all.g,all.b,255)

            samCam = nil
            all = nil
            collectgarbage()
            elseif sampleC then
            sampling = not sampling
            if sampling then
                m.shader.inactive = true
                else
                m.shader.sens = sensitivity
                m.shader.inactive = false
                m.shader.overTex = replaceTexture
                m.shader.sample = sampleC
            end
            else
            info = "You need to sample a colour for the shader, first"
            infoTimer = 4
        end
    end
end

Ghost = {
v = [[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; }]],
f = [[
precision highp float;

uniform lowp float sens;
uniform lowp sampler2D texture;
uniform lowp sampler2D overTex;
uniform lowp vec4 sample;
uniform bool inactive;

varying lowp vec4 vColor;
varying highp vec2 vTexCoord;

void main()
{
    lowp vec4 col = texture2D( texture, vTexCoord );
    if (inactive == false && col.r > sample.r - sens && col.r < sample.r + sens && col.g > sample.g - sens && col.g < sample.g + sens && col.b < sample.b + sens && col.b > sample.b - sens) {
        col = texture2D( overTex, vTexCoord );
    }
    gl_FragColor = col;
}
]]
}

Circular Textures

This shader takes any texture and draws only pixels that are in a circle, creating a circular texture
Difficulty: This shader is very easy to follow and is recommended for beginners
Shader inputs
texture: sampler2D (image), the texture to make circular
circleSize: float (number), the distance a pixel must be from the center before being removed
Shader

CircleS = {
shader([[ -- vertex shader
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;
}
]],
[[ -- fragment shader
precision highp float;

uniform lowp sampler2D texture;
uniform lowp float circleSize;

varying lowp vec4 vColor;
varying highp vec2 vTexCoord;

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 < circleSize) { //circleSize is the size of the circle.
        //Turning a rectangular texture into a circle won't actually be that hard. All we have to do is remove
        //the pixels that are distant from the center. It's really that simple!
        col = texture2D(texture, vTexCoord); //sample the pixel in texture at the position vTexCoord
    }
    else {col = vec4(0,0,0,0);} //remove this pixel
    gl_FragColor = col;
}
]])

Example Project

--# Main
function setup()
    width,height = HEIGHT*0.9,HEIGHT*0.9 -- the initial width and height of the rect
    textures = {
    readImage("Cargo Bot:Codea Icon"),
    readImage("Platformer Art:Block Grass"),
    readImage("Cargo Bot:Background Fade"),
    readImage("Tyrian Remastered:Brown Capsule 1")
    }

    circleM = mesh()
    circleM:addRect(WIDTH/2,HEIGHT/2,width,height) -- add a square to the mesh
    circleM.texture = textures[1]
    circleM.shader = shader(CircleS.vertexShader,CircleS.fragmentShader)
    circleM.shader.circleSize = 1
    parameter.number("Circle_Size",0,1,0.5)
    parameter.integer("Texture",1,#textures)
end

function draw()
    background(255, 130, 0, 255)
    circleM.texture = textures[Texture] -- update the meshes texture
    circleM.shader.circleSize = Circle_Size -- update the shader's uniform, circleSize
    if circleM.texture.width*circleM.texture.height < 6400 then
        noSmooth()
        else
        smooth()
    end
    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

-- shaders below
CircleS = {
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;

uniform lowp sampler2D texture;
uniform lowp float circleSize;

varying lowp vec4 vColor;
varying highp vec2 vTexCoord;

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 < circleSize) { //circleSize is the size of the circle.
        //Turning a rectangular texture into a circle won't actually be that hard. All we have to do is remove
        //the pixels that are distant from the center. It's really that simple!
        col = texture2D(texture, vTexCoord); //sample the pixel in texture at the position vTexCoord
    }
    else {col = vec4(0,0,0,0);} //remove this pixel
    gl_FragColor = col;
}
]]
}

@Kolosso, this is great!
I like that you also include working examples that are easy to “Paste into new project” - very nice touch.

Thanks, @juce! :smiley:

This thread is a little old, but I just wanted to post a few more of these.

Voronoi

This shader creates a voronoi pattern that can have many uses. The example project uses a modified version of the shader to use the pattern as a displacement map, for example.
_Difficulty: This shader may be more difficult for beginners.
Shader Inputs
aspect: float (number), This is the aspect ration of the rectangle.
scale: float (number), This scales the pattern. 1.0 is one cell, so a scale of 20.0 will show 20.0 cells.
panX: float (number), This pans the pattern left or right.
panY: float (number), This pans the pattern up or down.
Shader

shader([[uniform mat4 modelViewProjection; // vertex shader
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; }]],
[[ // fragment shader
//Default precision qualifier
precision highp float;

// aspect ratio of rectangle (width/height)
uniform highp float aspect;

// scale the pattern
uniform highp float scale;

// pan the pattern
uniform highp float panX;
uniform highp float panY;

//The interpolated vertex color for this fragment
varying lowp vec4 vColor;

//The interpolated texture coordinate for this fragment
varying highp vec2 vTexCoord;

// function R22: Returns random vec2 in range 0-1 with vec2 seed.
vec2 R22(vec2 n) {
    highp vec3 a = fract(3.3 + n.xyx * vec3(123.34, 234.34, 345.65));
    a += dot(a, a + 34.45);
    return fract(vec2(a.x * a.y, a.y * a.z));
}
// function voronoi: Returns the brightness of the given point (uv) on a voronoi pattern.
//     Requires:
//         -function R22
float voronoi(highp vec2 uv) {
    lowp vec2 lv = fract(uv); // local x,y coord in cell
    mediump float md = 3.0; // distance to nearest point will be stored in md
    for (float x = -1.0; x <= 1.0; x++) { // loop from -1 to +1
        for (float y = -1.0; y <= 1.0; y++) { // loop from -1 to +1
            lowp vec2 o = vec2(x,y); // cell offset
            highp vec2 id = floor(uv) + o; // id for this cell
            mediump vec2 p = R22(id) + o; // local coord of point in cell
            mediump float d = length(lv - p);
            if (d < md) { // update nearest distance
                md = d;
            }
        }
    }
    return md;
}
void main()
{  highp vec2 uv = vTexCoord * scale;
    uv.x += panX;
    uv.y += panY;
    uv.x *= aspect; // correct for aspect ratio
    gl_FragColor = vec4(vColor.rgb * voronoi(uv),1.0); // multiply voronoi with vColor
}]])

Example Project

-- Shaders - Voronoi

function setup()
    print("Hello Voronoi!")
    
    -- Create a new mesh that will cover the screen
    m = mesh()
    m:addRect(WIDTH/2,HEIGHT/2,WIDTH*0.7,HEIGHT*0.7)
    m:setColors(255,255,255)
    
    parameter.boolean("Use_As_Displacement_Map")
    m.texture = readImage("Environments:Night Up") -- image to displace
    
    parameter.number("Displacement_Amount", 0, 1, 0.1) -- severity of the displacement
    
    m.shader = shader(voronoi.vert,voronoi.frag)
    m.shader.aspect = WIDTH/HEIGHT -- input the aspect ratio of the rect
    m.shader.scale = 15.0 -- scale the pattern
end

function draw()
    background(40, 40, 50)
    
    m.shader.panX = ElapsedTime * 0.6
    m.shader.panY = ElapsedTime * 0.1
    m.shader.displace = Use_As_Displacement_Map
    m.shader.displaceAmount = Displacement_Amount
    
    m:draw()
end

voronoi = {
vert = [[
//This is the current model * view * projection matrix
// Codea sets it automatically
uniform mat4 modelViewProjection;

//This is the current mesh vertex position, color and tex coord
// Set automatically
attribute vec4 position;
attribute vec4 color;
attribute vec2 texCoord;

//This is an output variable that will be passed to the fragment shader
varying lowp vec4 vColor;
varying highp vec2 vTexCoord;

void main()
{
    //Pass the mesh color to the fragment shader
    vColor = color;
    vTexCoord = texCoord;
    
    //Multiply the vertex position by our combined transform
    gl_Position = modelViewProjection * position;
}
]],
frag = [[
//Default precision qualifier
precision highp float;

// texture to displace
uniform lowp sampler2D texture;

// aspect ratio of rectangle (width/height)
uniform highp float aspect;

// scale the pattern
uniform highp float scale;

// pan the pattern
uniform highp float panX;
uniform highp float panY;

// displace texture boolean
uniform bool displace;

// displace amount
uniform mediump float displaceAmount;

//The interpolated vertex color for this fragment
varying lowp vec4 vColor;

//The interpolated texture coordinate for this fragment
varying highp vec2 vTexCoord;

// function R22: Returns random vec2 in range 0-1 with vec2 seed.
vec2 R22(vec2 n) {
    highp vec3 a = fract(3.3 + n.xyx * vec3(123.34, 234.34, 345.65));
    a += dot(a, a + 34.45);
    return fract(vec2(a.x * a.y, a.y * a.z));
}

// function voronoi: Returns the brightness of the given point (uv) on a voronoi pattern.
//     Requires:
//         -function R22
float voronoi(highp vec2 uv) {
    lowp vec2 lv = fract(uv); // local x,y coord in cell
    mediump float md = 3.0; // distance to nearest point will be stored in md
    for (float x = -1.0; x <= 1.0; x++) { // loop from -1 to +1
        for (float y = -1.0; y <= 1.0; y++) { // loop from -1 to +1
            lowp vec2 o = vec2(x,y); // cell offset
            highp vec2 id = floor(uv) + o; // id for this cell
            mediump vec2 p = R22(id) + o; // local coord of point in cell
            mediump float d = length(lv - p);
            if (d < md) { // update nearest distance
                md = d;
            }
        }
    }
    return md;
}

void main()
{
    highp vec2 uv = vTexCoord * scale;
    uv.x += panX;
    uv.y += panY;
    uv.x *= aspect; // correct for aspect ratio
    
    mediump float v = voronoi(uv); // get voronoi value for this fragment
    
    lowp vec3 col;
    if (displace)
        col = vColor.rgb * texture2D(texture, fract(vTexCoord + v * displaceAmount)).rgb;
    else
        col = vec3(v * vColor.rgb);
    gl_FragColor = vec4(col,1.0); //pass final color
}
]]
}

3D Sky Dome (but not really a dome)

I was getting tired of the fact that Craft was the only answer for getting an easy 3D sky. That’s why I made this shader. It works similarly to the Craft sky, except with one extra color input to control the gradient easier. :wink:
The shader uses raytracing to draw a 3D sky dome. (onto a 2D rectangle) The example below uses it in a 3D scene.
Difficulty: You might want to learn the basics of raytracing before trying to understand this shader.
Shader Inputs
aspect: float (number), This is the aspect ratio of the screen
zoom: float (number), This is the fov of the camera. The lower the value, the closer the FOV gets to 180 degrees. The higher the value, the fewer degrees. 0.5 is 90 degrees of FOV. 1.0 is 45 degrees of FOV. Note that this is on a logarithmic scale.
lookAt: vec2 (vec2), This is the direction the camera is facing. Since the sky looks the same, no matter if you look left or right; I’ve only given it 2 dimensions. X is forward and Y is up. Note that this vec2 needs to be normalized.
horizon: vec4 (color), This is the color of the horizon
sky: vec4 (color), This is the color of the top of the sky
midSky: vec4 (color)
, This is the color between the top of the sky and the horizon.
ground: vec4 (color)_, This is the color of the bottom of the sky.
Shader

shader([[uniform mat4 modelViewProjection; // vertex shader
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; }]],
[[ // fragment shader
precision highp float;
// camera zoom
uniform mediump float zoom;
// aspect ratio of the screen (WIDTH/HEIGHT)
uniform mediump float aspect;
// normalized look at vector
uniform lowp vec2 lookAt;

// 4 sky colors
uniform highp vec4 sky;
uniform highp vec4 midSky;
uniform highp vec4 horizon;
uniform highp vec4 ground;

varying highp vec2 vTexCoord;
void main()
{  mediump vec2 uv = vec2((vTexCoord.x - 0.5)*aspect,(vTexCoord.y - 0.5));
    lowp vec3 up = cross(vec3(0.,lookAt.y,lookAt.x),vec3(1.,0.,0.));
    highp vec3 c = vec3(0.,lookAt.y,lookAt.x)*zoom;
    mediump float t = normalize(c + vec3(uv.x,0.,0.) + uv.y * up).y;
    
    lowp vec3 col;
    if (t < 0.) col = mix(horizon.rgb,ground.rgb,abs(t));
    else {
        lowp float h = 2.*t-1.;
        if (h>0.) h = 0.;
        h*=h;
        lowp float m = 2.*t-1.;
        m*=m;
        m=1.-m;
        lowp float s = 2.*t-1.;
        if (s<0.) s = 0.;
        s*=s;
        col = horizon.rgb*h + midSky.rgb*m + sky.rgb*s;
    } gl_FragColor = vec4(col,1.); }]])

Example Project

--# Main
displayMode(FULLSCREEN)
function setup()
    rotX,rotY = 0,0 -- camera rotation
    camX,camY,camZ = 20,20,20
    
    m = mesh() -- create a mesh that will cover the screen
    m:addRect(WIDTH/2,HEIGHT/2,WIDTH,HEIGHT)
    m:setColors(255,255,255,255)
    m.shader = shader(sky.vert,sky.frag)
    m.shader.zoom = 0.5 -- fov of the camera (0.5 is 90 degrees. 1.0 is 45 degrees)
    m.shader.aspect = WIDTH/HEIGHT -- aspect ration of the screen
    m.shader.sky = vec4(0.15,0.4,0.6,1.0) -- top color of the sky
    m.shader.midSky = vec4(0.3,0.5,0.7,1) -- color between the top and horizon
    m.shader.horizon = vec4(0.7,0.8,0.9) -- horizon color
    m.shader.ground = vec4(0.05,0.15,0.3) -- bottom color
    
    axis = mesh() -- x,y,z axis mesh
    axis.vertices = {
        vec3(0,1,0),vec3(6,0,0),vec3(0,0,0), -- x
        vec3(0.5,0.9166,0),vec3(0,6,0),vec3(0,1,0), -- y
        vec3(0,1,0),vec3(0,6,0),vec3(0,0.9166,0.5),
        vec3(0,1,0),vec3(0,0,6),vec3(0,0,0) -- z
    }
    local r,g,b = color(255,0,0),color(0,255,0),color(0,0,255)
    axis.colors = {
        r,r,r,
        g,g,g,
        g,g,g,
        b,b,b
    }
    
    -- create the ground
    ground = mesh()
    local size = 2000 -- size of the ground
    ground.vertices = {
    vec3(-size,0,-size),vec3(-size,0,size),vec3(size,0,size),
    vec3(size,0,size),vec3(size,0,-size),vec3(-size,0,-size)
    }
    local tiles = 160 -- number of tiles on the ground
    ground.texCoords = {
    vec2(0,0),vec2(0,tiles),vec2(tiles,tiles),
    vec2(tiles,tiles),vec2(tiles,0),vec2(0,0)
    }
    ground:setColors(255,255,255,255)
    ground.texture = readImage("Blocks:Grass Top")
    ground.shader = shader(basicTiles.vert,basicTiles.frag)
end

-- This function gets called once every frame
function draw()
    -- This sets a dark background color 
    background(40, 40, 50)
    
    local lookAtX = math.sin(rotY) * math.cos(rotX)
    local lookAtY = math.sin(rotX)
    local lookAtZ = -math.cos(rotY) * math.cos(rotX)
    perspective(90,WIDTH/HEIGHT,0.1,1000)
    camera(camX,camY,camZ,
    camX + lookAtX,
    camY + lookAtY,
    camZ + lookAtZ)
    -- ground:draw() -- uncomment this to draw the ground
    axis:draw()
    
    ortho()
    viewMatrix(matrix())
    
    resetMatrix()
    zLevel(-10)
    m.shader.lookAt = vec2(math.cos(rotX),math.sin(rotX))
    m:draw()
end

function touched(t)
    rotY = rotY + t.deltaX * 0.01
    rotX = rotX + t.deltaY * 0.01
    if rotX < -1.5 then
        rotX = -1.5
    elseif rotX > 1.5 then
        rotX = 1.5
    end
end

--# Shaders
sky = {
vert = [[
uniform mat4 modelViewProjection; // vertex shader
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; }
]],
frag = [[
precision highp float;

// camera zoom
uniform mediump float zoom;
// aspect ratio of the screen (WIDTH/HEIGHT)
uniform mediump float aspect;
// normalized look at vector
uniform lowp vec2 lookAt;

// 4 sky colors
uniform highp vec4 sky;
uniform highp vec4 midSky;
uniform highp vec4 horizon;
uniform highp vec4 ground;

//The interpolated texture coordinate for this fragment
varying highp vec2 vTexCoord;

void main()
{
    mediump vec2 uv = vec2((vTexCoord.x - 0.5)*aspect,(vTexCoord.y - 0.5));
    lowp vec3 up = cross(vec3(0.,lookAt.y,lookAt.x),vec3(1.,0.,0.));
    highp vec3 c = vec3(0.,lookAt.y,lookAt.x)*zoom;
    mediump float t = normalize(c + vec3(uv.x,0.,0.) + uv.y * up).y;
    
    lowp vec3 col;
    if (t < 0.) col = mix(horizon.rgb,ground.rgb,abs(t));
    else {
        lowp float h = 2.*t-1.;
        if (h>0.) h = 0.;
        h*=h;
        lowp float m = 2.*t-1.;
        m*=m;
        m=1.-m;
        lowp float s = 2.*t-1.;
        if (s<0.) s = 0.;
        s*=s;
        col = horizon.rgb*h + midSky.rgb*m + sky.rgb*s;
    }
    gl_FragColor = vec4(col,1.);
}
]]
}
basicTiles = {
vert = sky.vert, -- make the vertex shader the same as sky.vert because sky.vert is just the default vertex shader
frag = [[
//Default precision qualifier
precision highp float;

//This represents the current texture on the mesh
uniform lowp sampler2D texture;

//The interpolated vertex color for this fragment
varying lowp vec4 vColor;

//The interpolated texture coordinate for this fragment
varying highp vec2 vTexCoord;

void main()
{
    //Sample the texture at the interpolated coordinate and loop the coordinates with fract()
    lowp vec4 col = texture2D( texture, fract(vTexCoord) ) * vColor;
    gl_FragColor = col;
}
]]
}

@Kolosso - absolutely magic, I will learn a lot from these. One thing, error in your SkyDome where the code is trying to load a “Basic Tiles” shader and I have no file of that type. Thanks.

@Bri_G Thanks, I didn’t see that. I’ve updated the code, so it should work now!