Can you apply shaders to things drawn with the 2D tools?

Shaders are only for 3D objects, right?

If I apply a shader when I’m drawing some things using ellipse() for example, the shaders not gonna affect those ellipses, right?

In order to apply a shader to 2D art, I’d have to draw that art to a texture that was on a mesh, is that correct?

Found something here by the esteemed @Ignatz, praise be his name!

Below is my effort to generalize it into code that can be used as a dependency by anyone.

I can’t say it’s robustly tested, but I can say it’s currently working as a dependency in my own projects.

I hope it’s of help to the next traveler this way…


--# Main
--Shader2D
--A way to apply (possibly any) 3D shaders to 2D drawing environments
--can be used in (possibly any) other projects as a dependency, by following the instructions below

function setup()
    --your setup function needs two things
    --1: call Shader2D.setup() with the shader, your 2Ddrawing function, and a shader updater
    --2: setup up any initial values your shader needs *directly on the Shader2D*
    Shader2D.setup(shader("Effects:Ripple"), draw2DsStuff, shaderUpdates)
    Shader2D.mesh.shader.freq = 2
end

function draw()
    -- you already passed your 2D drawing function to Shader2D, so in this function just call:
    Shader2D.draw()
end

function draw2DsStuff()
    --an example of a 2D drawing function
    background(40, 40, 50)
    strokeWidth(5)
    fill(214, 226, 116, 255)
    spriteMode(CORNER)
    sprite("Cargo Bot:Game Area", 0, 0, WIDTH, HEIGHT)
    ellipse(WIDTH/2,HEIGHT/2,HEIGHT/3)
end

function shaderUpdates()
    --a sample updater: this is what the ripple shader needs every frame
    Shader2D.mesh.shader.time = ElapsedTime
end


--# CustomFunctions

Shader2D = {}

function Shader2D.setup(thisShader, this2DDrawer, thisShaderUpdater)
    --creates the mesh and its texture and defines the drawing functions
    Shader2D.screen = image(WIDTH,HEIGHT)
    Shader2D.mesh = mesh()
    Shader2D.mesh:addRect(0,0,WIDTH,HEIGHT)
    Shader2D.mesh.shader = thisShader
    Shader2D.mesh.texture = Shader2D.screen
    Shader2D.draw2D = this2DDrawer
    Shader2D.updateShader = thisShaderUpdater
end

function Shader2D.draw()
    --draws the stored 2D drawing function to the mesh texture
    setContext(Shader2D.screen)
    Shader2D.draw2D()
    setContext()
    translate(WIDTH/2,HEIGHT/2)
    --updates the shader and draws the mesh
    if Shader2D.updateShader ~= nil then
        Shader2D.updateShader()
    end
    Shader2D.mesh:draw()
end

Shaders are applied to meshes. Meshes can be displayed in 2D or 3D. 2D is orthogonal projection, with all the objects as planes perpendicular to the camera. Codea’s addRect/ setRect API is the best way to display meshes in 2D.

You can’t as far as I know, apply a shader to the 2D primitive drawing commands (ellipse etc). Partly, this is because I believe the ellipse itself is drawn with a shader.

@UberGoober Looking over your posted code, am I right in thinking that it’s setting up a full screen mesh, drawing the 2D operations onto the mesh, and then applying the shader to that mesh?

@mindless that’s correct, I think. It’s not my code originally but that’s what it looks like to me too.

@yojimbo2000 I don’t know anything about addRect/setRect, I’ll have to look into that. As far as applying shader to an ellipse() command, I believe that’s what this code does. If you run it, it draws an ellipse and then puts a ripple on it.

Anything that’s drawn on the mesh texture is modified by the shader. Shader2D.screen is an image the size of the screen. The sprite Cargo Bot:Game area is drawn on it along with an ellipse.

Yes that’s the point, this is a modular pre-rolled solution that lets you keep all your existing 2D drawing and easily lay a shader on top of it. It handles the 3D part for you, almost all you need to do is pick a shader and rename your existing draw function.