Way to define and use shader programs from scripts

AFAIK not currently possible?

See this example from Amulet


local win = am.window{width = 400, height = 400}

-- create the shader program for rendering kaleidoscope
local vshader1 = [[
    precision mediump float;
    attribute vec2 vert;
    attribute vec2 uv;
    uniform float t;
    uniform mat4 MVP;
    varying vec2 v_uv;
    void main() {
        v_uv = (MVP * vec4(uv, 0.0, 1.0)).xy + vec2(sin(t*0.7), cos(t*0.3));
        gl_Position = MVP * vec4(vert, 0, 1);
    }
]]
local fshader1 = [[
    precision mediump float;
    varying vec2 v_uv;
    uniform sampler2D tex;
    void main() {
        gl_FragColor = texture2D(tex, v_uv);
    }
]]
local prog1 = am.program(vshader1, fshader1)

-- create the shader program for adding blur
local vshader2 = [[
    precision mediump float;
    attribute vec2 vert;
    attribute vec2 uv;
    uniform float t;
    uniform mat4 MVP;
    varying vec2 v_uv;
    varying vec2 v_pos;
    void main() {
        v_pos = vert;
        v_uv = uv;
        gl_Position = MVP * vec4(vert, 0, 1);
    }
]]
local fshader2 = [[
    #define n 2
    precision mediump float;
    varying vec2 v_uv;
    varying vec2 v_pos;
    uniform sampler2D tex;
    void main() {
        float e = pow(length(v_pos), 2.0) / 100.0;
        vec4 sum = vec4(0.0);
        for (int i = -n; i <= n; i++) {
            for (int j = -n; j <= n; j++) {
                vec2 d = vec2(float(i) * e, float(j) * e);
                vec4 samp = texture2D(tex, v_uv + d);
                sum += samp;
            }
        }
        gl_FragColor = sum / pow(float(n) * 2.0 + 1.0, 2.0)
            + vec4(e * 50.0, 0.0, 0.0, 0.0);
    }
]]
local prog2 = am.program(vshader2, fshader2)

-- setup vertices and texture coords
local num_segments = 9
local vbuf = am.buffer(16 * 3 * num_segments)
local verts = vbuf:view("vec2", 0, 16)
local uvs = vbuf:view("vec2", 8, 16)
local uv_scale = 5
for i = 0, num_segments-1 do
    local angle1 = (i / num_segments) * math.pi * 2
    local angle2 = ((i + 1) / num_segments) * math.pi * 2
    verts[i*3+1] = vec2(0, 0)
    verts[i*3+2] = vec2(math.cos(angle1)*2, math.sin(angle1)*2)
    verts[i*3+3] = vec2(math.cos(angle2)*2, math.sin(angle2)*2)
    uvs[i*3+1] = vec2(0, 0)
    uvs[i*3+2] = vec2(0, uv_scale)
    uvs[i*3+3] = vec2(uv_scale, 0)
end

-- create pattern texture
local pattern_tex_size = 8
local pattern_tex_buf = am.buffer(pattern_tex_size^2*4)
local pattern_tex_view = pattern_tex_buf:view("uint", 0, 4)
for i = 1, pattern_tex_size^2 do
    pattern_tex_view[i] = 255 * 2^24 + math.random(2^24)
end
local pattern_img_buf = am.image_buffer(pattern_tex_buf, pattern_tex_size, pattern_tex_size)
local pattern_texture = am.texture2d(pattern_img_buf)
pattern_texture.wrap = "mirrored_repeat"

-- create node that will render the kaleidoscope vertices using
-- the program and texture we created earlier
local t_node = 
    am.bind{
        vert = verts,
        uv = uvs,
        tex = pattern_texture,
        t = 0,
    }
    ^am.draw("triangles")
local rotation_node = am.rotate("MVP", quat(0)) ^ t_node
local node1 = 
    am.use_program(prog1)
    ^am.bind{MVP = mat4(1)}
    ^rotation_node

-- create texture for post-processing (applying blur)
local pptexture = am.texture2d(512)
pptexture.filter = "linear"

-- create framebuffer so we can draw into pptexture
local ppfb = am.framebuffer(pptexture)

-- create node to apply post-processing
local buf2 = am.buffer(4 * 4 * 6)
local verts2 = buf2:view("vec2", 0, 16)
local uvs2 = buf2:view("vec2", 8, 16)
verts2[1] = vec2(-1, -1)
verts2[2] = vec2(-1, 1)
verts2[3] = vec2(1, 1)
verts2[4] = vec2(-1, -1)
verts2[5] = vec2(1, 1)
verts2[6] = vec2(1, -1)
uvs2[1] = vec2(0, 0)
uvs2[2] = vec2(0, 1)
uvs2[3] = vec2(1, 1)
uvs2[4] = vec2(0, 0)
uvs2[5] = vec2(1, 1)
uvs2[6] = vec2(1, 0)
local node2 = 
    am.use_program(prog2)
    ^am.bind{
        vert = verts2,
        uv = uvs2,
        tex = pptexture,
        MVP = mat4(1),
    }
    ^am.draw("triangles")

-- set the post processing node as the scene
win.scene = node2

-- create an action on the scene that updates the various uniforms
-- and then renders the kaleidoscope into the post-processing framebuffer
-- which then gets automatically drawn to the window using the
-- blur shader (since it's the scene).
win.scene:action(function()
    pattern_tex_view[math.random(pattern_tex_size^2)] = 255 * 2^24 + math.random(2^24)
    rotation_node.rotation = quat(am.frame_time)
    t_node.t = am.frame_time
    ppfb:render(node1)
    if win:key_pressed("escape") then
        win:close()
    end
end)

Could you give examples of how this would be useful?

Only as a convenience.

I’m asking because without seeing the practical application of a requested feature, it is hard for us to prioritize said feature.
I personally have never came across dynamic generation of shaders (if that is what you are requesting that is… I could be misunderstanding you) so it’s hard for me to see what cases this would be useful.

On a side note though (and maybe totally irrelevant to what you are requesting here) I would like to see dynamic textures and/or materials being implemented - so that you with the use of code can change the shader of an object. I know this is in the backlog at least but am unsure when it will be implemented. Do you think that dynamic switching of textures/materials/shaders would be comparable to what you are requesting?

Dynamic switching of textures/materials/shaders would be useful. More of those features down the road would be welcome. Defold has very good strengths. It has some weaknesses compared to other tools, but they don’t really hurt the main use cases Defold seems best at. I’d ultimately rather Defold team focus on making it the best tool for what it is intended to do than try to make it do everything.

I didn’t mean dynamic creation exactly but being able to create them all in one place like is possible on https://www.shadertoy.com , and maybe that site is an example for you to dynamic generation. I should note that I am not an expert and this topic is possibly a naive request.

I know this isn’t really what you asked for, but it’s possible to “change” and use different materials/textures in runtime on a render_script-level. Have a look at http://www.defold.com/ref/render/#render.enable_material

Another good thing to be aware of is that you can hot-reload shaders, just like most other resources, in runtime. This means you should be able to edit and fiddle with your shaders, save, then reload the resource and the changes should appear in the running game.

1 Like