Add Shade shader to 2D circle object?

I suppose to make it work, I would need a circular mesh, correct? That sounds like a bunch of triangles just to get one smooth-looking circle. And if I want many of these small, shader-textured circles?

Once I have the right mesh, then just apply a shader I’ve created in Shade, right?

Anyone have a better way, or can provide sample code? Thanks!

@brianolive Not sure what you would do with this, but here’s a circle that’s created from a bunch of triangular meshes.

displayMode(FULLSCREEN)

function setup()
    tab={}
    m=mesh()
    a=100
    b=100
    for x=-a,a do
        y=math.sqrt((1-(x^2)/a^2)*b^2)
        table.insert(tab,vec2(x+WIDTH/2,y+HEIGHT/2))
    end  
    for x=a,-a,-1 do
        y=math.sqrt((1-(x^2)/a^2)*b^2)
        table.insert(tab,vec2(x+WIDTH/2,-y+HEIGHT/2))
    end 
    m.vertices=triangulate(tab)
    m:setColors(255,0,0)
end

function draw()
    background(40, 40, 50)
    m.draw(m)
end

I’d use angles to make the mesh as it makes the vertices go round the circle evenly which is easier to work with for colours and textures. You can also reuse meshes so depending on what you intend to do, it might be enough to create one mesh and draw it many times.

-- CircleMesh

function setup()
    
    local v = {}
    local c = {}
    local n = 30
    
    local x,y = 1,0

    for k=1,n do
        table.insert(v,vec2(0,0))
        table.insert(v,vec2(x,y))
        table.insert(c,color(255,0))
        table.insert(c,hsl((k-1)/n,1,.5))
        x,y = math.cos(2*math.pi/n*k),math.sin(2*math.pi/n*k)
        table.insert(v,vec2(x,y))
        table.insert(c,hsl(k/n,1,.5))
    end

    m = mesh()
    m.vertices = v
    m.colors = c
    p = {}
    for k=1,100 do
        table.insert(p,{
            math.random(0,WIDTH), 
            math.random(0,HEIGHT), 
            math.random(20,60),
            math.random(20,60),
            math.random(1,360)
        })
    end
end

function draw()
    background(40,40,50)
    for k,v in ipairs(p) do
        pushMatrix()
        translate(v[1],v[2])

        rotate(v[5]+ElapsedTime*60)
        scale(v[3],v[4])
        rotate(-ElapsedTime*30)
        m:draw()

        popMatrix()
    end

end


function hsl(h,s,l,a)
        h = (h-math.floor(h))*6
        s = s or 1
        l = l or 1
        a = a or 255
        local c = (1-math.abs(2*l-1))*s*255
        local m = l*255 - c/2
        local x = c*(1 - math.abs(h%2 - 1))
        local r,g,b
        if h < 1 then
            r,g,b = c+m,x+m,m
        elseif h < 2 then
            r,g,b = x+m,c+m,m
        elseif h < 3 then
            r,g,b = m,c+m,x+m
        elseif h < 4 then
            r,g,b = m,x+m,c+m
        elseif h < 5 then
            r,g,b = x+m,m,c+m
        else
            r,g,b = c+m,m,x+m
        end
        return color(r,g,b,a)
    end

Thanks guys. I appreciate the sample code! From there, I believe assigning a shader from Shade is a one-line call, so I should be good to go.

@brianolive when you get this working, please post an example, might come in handy! thanks!

Here’s an example to put an image on a circle. You can change the step size to create different shapes, 90 for a square, 120 for a triangle, etc. Change the radius for different sizes. You can change the offset if you create a square or other shape to alter the starting orientation.

@Simeon When this program starts, there is a slight delay before the program covers the full screen. The screen goes about 3/4 of the way to the left, pauses, and then goes all the way.

displayMode(FULLSCREEN)

function setup()  
    assert(OrbitViewer, "Please include Cameras (not Camera) as a dependency")        
    scene = craft.scene()
    v=scene.camera:add(OrbitViewer,vec3(0,0,0), 20, 0, 1000)
    img=readImage(asset.builtin.Cargo_Bot.Startup_Screen)
    v.rx,v.ry=-180,180
    offset=0
    step=1
    radius=5
    p1=vec3(0,0,0)
    s1=vec2(.5,.5)
    for a=0+offset,359+offset,step do
        x=math.cos(math.rad(a))
        y=math.sin(math.rad(a))
        p2=vec3(x*radius,0,y*radius)
        s2=vec2(x*.5+.5,y*.5+.5)
        x=math.cos(math.rad(a+step))
        y=math.sin(math.rad(a+step))
        p3=vec3(x*radius,0,y*radius)
        s3=vec2(x*.5+.5,y*.5+.5)
        createSlice(p1,p2,p3,s1,s2,s3) 
    end
end

function draw()
    update(DeltaTime)
    scene:draw() 
end

function update(dt)
    scene:update(dt)
end

function createSlice(p1,p2,p3,s1,s2,s3)
    local c=color(255, 255, 255, 255)
    local r=scene:entity()
    r.model = craft.model()
    r.model.positions={p1,p2,p3}
    r.model.indices={1,2,3,3,2,1}
    r.model.colors={c,c,c}
    r.model.uvs={s1,s2,s3}
    r.material = craft.material(asset.builtin.Materials.Basic)  
    r.material.map=img
end

@Simeon I made a copy of my above program and tried to modify it to use one model like you did in my other program. I couldn’t get it to work. The difference between the two programs is the one you modified was made up of squares (two triangles) per model. This one uses one triangle per model. I’ll keep trying, but I wonder if one model doesn’t work here.

@dave1707 this appears to work for me

displayMode(FULLSCREEN)

function setup()  
    assert(OrbitViewer, "Please include Cameras (not Camera) as a dependency")        
    scene = craft.scene()
    v=scene.camera:add(OrbitViewer,vec3(0,0,0), 20, 0, 1000)
    img=readImage(asset.builtin.Cargo_Bot.Startup_Screen)
    v.rx,v.ry=-180,180
    offset=0
    step=1
    radius=5
    p1=vec3(0,0,0)
    s1=vec2(.5,.5)
    
    local vert = {}
    local ind = {}
    local col = {}
    local uv = {}
    
    for a=0+offset,359+offset,step do
        x=math.cos(math.rad(a))
        y=math.sin(math.rad(a))
        p2=vec3(x*radius,0,y*radius)
        s2=vec2(x*.5+.5,y*.5+.5)
        x=math.cos(math.rad(a+step))
        y=math.sin(math.rad(a+step))
        p3=vec3(x*radius,0,y*radius)
        s3=vec2(x*.5+.5,y*.5+.5)
        createSlice(p1,p2,p3,s1,s2,s3,vert,ind,col,uv) 
    end
    
    local r=scene:entity()
    r.model = craft.model()
    r.model.positions=vert
    r.model.indices=ind
    r.model.colors=col
    r.model.uvs=uv
    r.material = craft.material(asset.builtin.Materials.Basic)  
    r.material.map=img
end

function draw()
    update(DeltaTime)
    scene:draw() 
end

function update(dt)
    scene:update(dt)
end

function createSlice(p1,p2,p3,s1,s2,s3,vert,ind,col,uv)
    local c=color(255, 255, 255, 255)    
    
    local s = #vert
    vert[s+1] = p1
    vert[s+2] = p2
    vert[s+3] = p3
    
    col[s+1] = c
    col[s+2] = c
    col[s+3] = c
    
    local si = #ind
    ind[si+1] = s+1
    ind[si+2] = s+2
    ind[si+3] = s+3
    ind[si+4] = s+3
    ind[si+5] = s+2
    ind[si+6] = s+1
    
    uv[s+1] = s1
    uv[s+2] = s2
    uv[s+3] = s3
end

@Simeon I’ll have to compare your code to mine and see why yours works and mine doesn’t. The one difference is you pass the different tables to createSlice whereas my tables are global and I just update them in createSlice. I’ll have to cut the step size to 90 and print out your tables and compare my tables to yours to see if they’re different. Everything else is about the same.

PS. I made your tables global and didn’t pass them to createSlice and yours still worked. I’ll have to compare dumps of the tables and see where I’m messing up.

@Simeon I found my problem. When I compared the tables from your change and my change, I saw that I was creating the indices table wrong. The code looked like how I wanted the table to be, but when I looked at the table, what I got wasn’t what I wanted. Thanks for your code.