Shadertoy to Defold Help

Hi, so I’ve been wanting to make a bit of a 2d lighting system with normal mapping. I’ve read a lot of the forum posts and existing workarounds and that we need multi texture support for sprites, which is being worked on.

I also have been learning more about shaders. I found this shadertoy example that seems really nice: https://www.shadertoy.com/view/lstyDr. It seems to do the normalization as part of its code, so only one sampler is needed still, so I thought I’d see if I could make it work as the sprite shader.

So I copied the sprite vertex/fragment shader programs and made a new material and tried to translate it. It just kind of renders a single color for the most part though. Here is what I have:

varying mediump vec2 var_texcoord0;
uniform lowp vec4 mousepos;

uniform lowp sampler2D texture_sampler;
uniform lowp vec4 tint;

#define fac 1.
const int numLights = 2;

vec3 esnm2(in vec2 uv, in vec2 t)
{
    return normalize(vec3(
        texture(texture_sampler, vec2(uv.x + t.x*fac, uv.y)).x - texture(texture_sampler, vec2(uv.x - t.x*fac, uv.y)).x,
        texture(texture_sampler, vec2(uv.x, uv.y + t.y*fac)).x - texture(texture_sampler, vec2(uv.x, uv.y - t.y*fac)).x,
        1
    ));
}

struct material {
    vec3 ambient, diffuse, specular;
    float shininess;
};

struct light {
    vec3 o, ambient, diffuse;
    float power;
};

vec3 shade(in vec3 p, in material m0, in vec3 N, in light[numLights] l) {
    vec3 ambient = m0.ambient;
    vec3 diffuse = m0.diffuse;
    for(int i=0; i<numLights; ++i)
    {
        ambient += l[i].ambient;
        float d = distance(l[i].o, p);
        vec3 ldir = p-l[i].o;
        vec3 L = normalize(ldir);
        float NdotL = dot(N,L);
        diffuse += NdotL*l[i].diffuse*l[i].power/d;
    }
    return (ambient+diffuse)/float(numLights);
}

void main()
{
    vec2 res = vec2(1.92, 1.08); // <3>
    vec2 uv = var_texcoord0.xy/res.xy;
    float aspect = res.y/res.x;

    vec2 p = uv*2.-1.;
    uv.y*=aspect;
    p.y*=aspect;

    vec2 mouse = mousepos.xy/res.xy;
    vec2 mousep = mouse*2.-1.;
    mousep.y *= aspect;

    light[numLights] l;
    l[0] = light(vec3(mousep, -.02), vec3(.075, .056, 0), vec3(.5, .34, 0), .2);
    l[1] = light(vec3(mousep+0.3, -.02), vec3(.075, .056, 0), vec3(.3, .34, 0), .6);

    material m0 = material(vec3(.056), vec3(.1, .089, .08), vec3(.56, .54, .34), 10.0);

    vec3 N = esnm2(uv, 1./res.xy);
    vec3 col = shade(vec3(p,0), m0, N, l);

    gl_FragColor = vec4(col,1);
}

I added a vec4 variable to the fragment shader in the material for mousepos and update it with a script which does not seem to work at all. (the script works, I can put a print there to see mouse pos move around but it does not change the shader at all.)

function init(self)
	msg.post(".", "acquire_input_focus")
end


function on_input(self, action_id, action)
	if action.x and action.y then
		sprite.set_constant("#sprite", "mousepos", vmath.vector4(action.x, action.y, 1, 1))
	end
end

I am pretty new to all this. Any help converting this shadertoy to defold would be awesome! Thanks.

Does the color change when the mouse moves?

Note: There’s also this tutorial on Shadertoy to Defold conversions:

1 Like

The color did not change with mouse movement. I followed that tutorial to get as far as I did. Without it I was getting lots of errors haha.

I added a brick pattern image that is mostly brown to the sprite as the texture. With the standard shader the image looks fine. With this shader it is basically solid brown, single color. It loses the image to the one color. And moving the mouse around doesn’t do anything.

I’ll probably try to play more with it but thought I’d post here to see if someone else could see something I’m obviously doing wrong.

I think you need to back away and start from the beginning. First make sure you can get mouse input to the shader. Start by sending a normalized mouse position (ie where all values are between 0 and 1).

-- width and height of your project as set in game.project
local width = tonumber(sys.get_config("display.width"))
local height = tonumber(sys.get_config("display.height"))

-- make sure values are within 0.0 and 1.0
sprite.set_constant("#sprite", "mousepos", vmath.vector4(action.x / width, action.y / height, 1, 1))

And in your shader:

uniform lowp vec4 mousepos;

void main()
{
    gl_FragColor = mousepos;
}

This should draw the whole mesh in a color based on your mouse position. If that works then add more piece by piece until you have something that is working.

3 Likes

Normalizing the mouse position helped for sure, thanks!

1 Like