Question about Shader:hasUniform and Shader:getExternVariable

Questions about the LÖVE API, installing LÖVE and other support related questions go here.
Forum rules
Before you make a thread asking for help, read this.
Post Reply
User avatar
Bigfoot71
Party member
Posts: 287
Joined: Fri Mar 11, 2022 11:07 am

Question about Shader:hasUniform and Shader:getExternVariable

Post by Bigfoot71 »

Hi :)

I'm writing a small script to manage transitions between different game screens and I wanted to add a system to send values ​​to shader uniforms from the parameters of a Lua function.

So I need to differentiate if the uniform is a vector or an array, because `:send` takes as parameter an array for vectors and a sequence of values ​​for an array. Here is what I would have liked to do roughly:

Code: Select all

function Transition:start(a, b, c, shader_values, ...)

    --[[ ... ]]--

    if type(shader_values) == "table" then

        for k, v in pairs(shader_values) do
            local u_type = self.shaderTrans:getExternVariable(k)
            self.shaderTrans:send(k, (u_type == "array") and unpack(v) or v)
        end

    end

    --[[ ... ]]--

end
However Shader:getExternVariable is deprecated and has been replaced by Shader:hasUniform which now only returns true or false, how could I do it then, and why was it removed?
My avatar code for the curious :D V1, V2, V3.
User avatar
slime
Solid Snayke
Posts: 3131
Joined: Mon Aug 23, 2010 6:45 am
Location: Nova Scotia, Canada
Contact:

Re: Question about Shader:hasUniform and Shader:getExternVariable

Post by slime »

I think I'd either structure the input data such that it doesn't need to branch depending on the shader's declaration (e.g. by having the function implementation always use unpack no matter what), or make multiple versions of the function where the caller can specify what they intend via the function name.

With the branch approach you want, how would you support arrays of vectors?
User avatar
Bigfoot71
Party member
Posts: 287
Joined: Fri Mar 11, 2022 11:07 am

Re: Question about Shader:hasUniform and Shader:getExternVariable

Post by Bigfoot71 »

slime wrote: Mon Mar 27, 2023 9:35 pm I think I'd either structure the input data such that it doesn't need to branch depending on the shader's declaration (e.g. by having the function implementation always use unpack no matter what), or make multiple versions of the function where the caller can specify what they intend via the function name.

With the branch approach you want, how would you support arrays of vectors?
This shouldn't be a problem for a vector array (unless I missed a case) however another major problem lies elsewhere, thanks to your advice I could see the problem differently.

For the example here is a test shader:

Code: Select all

local shader = lg.newShader[[

    extern vec4 col1;
    extern vec4 col2;
    extern vec4 col3;

    extern vec4 cols[3];

    vec4 effect(vec4 c, Image t, vec2 tc, vec2 sc) {

        int v = int(mod(sc.x, 6.0));

        if (v==0) return col1;
        if (v==1) return col2;
        if (v==2) return col3;

        return cols[v-3];

    }

]]
With this shader, to always use unpack would require having a table like this and it's not really desirable:

Code: Select all

local values = {

    col1 = { {0,1,1,1} };
    col2 = { {1,0,1,1} };
    col3 = { {1,1,0,1} };

    cols = {
        {0,0,1,1},
        {0,1,0,1},
        {1,0,0,1}
    }

}
So I opted for the option to get all the values ​​that follow a character string in order to send them to the shader (since I'm not using a table I also did a recursive `select` function to get the parameters from N1 to N2):

Code: Select all

local function get_elements(n1, n2, ...)
    if n1 > n2 then
        return
    else
        return select(n1, ...), get_elements(n1 + 1, n2, ...)
    end
end

local function send(shader, ...)

    local n = select("#", ...)

    local key;
    local i = 1

    while i <= n do

        local v = select(i, ...)

        if type(v) == "string" then
            key = v
        else

            local from, to = i, nil
            while i <= n and type(v) ~= "string" do
                to, i = i, i + 1
                v = select(i, ...)
            end

            shader:send(key, get_elements(from, to, ...))

            goto continue

        end

        i = i + 1

        ::continue::

    end

end
The function can be called like this:

Code: Select all

send(shader,

    "col1", {0,1,1,1},
    "col2", {1,0,1,1},
    "col3", {1,1,0,1},

    "cols", {0,0,1,1},
            {0,1,0,1},
            {1,0,0,1}

)
The exercise is actually much less obvious if we send it a table in this form:

Code: Select all

values = {

    col1 = {0,1,1,1};
    col2 = {1,0,1,1};
    col3 = {1,1,0,1};

    cols = {
        {0,0,1,1},
        {0,1,0,1},
        {1,0,0,1}
    }

}
Because it is impossible to guess intuitively if the `col1` corresponds to an array or to a vector without adding other values ​​which will make the table heavier, which in any case will end up being discarded almost immediately.

But it would have been much easier to do if the Shader:getExternVariable method was still available, hence my despair :cry:

So good, I was finally able to do what I wanted but if you have the answer to why this method is no longer available it will relieve my disappointment :?

Thank you anyway for your opinion, I might not have done this tonight otherwise! :awesome:

Here is my complete code if you want to try it on your side:

Code: Select all

local lg = love.graphics

local mask = lg.newCanvas()

local shader = lg.newShader[[

    extern vec4 col1;
    extern vec4 col2;
    extern vec4 col3;

    extern vec4 cols[3];

    vec4 effect(vec4 c, Image t, vec2 tc, vec2 sc) {

        int v = int(mod(sc.x, 48.0))/8;

        if (v==0) return col1;
        if (v==1) return col2;
        if (v==2) return col3;

        return cols[v-3];

    }

]]

local function get_elements(n1, n2, ...)
    if n1 > n2 then
        return
    else
        return select(n1, ...), get_elements(n1 + 1, n2, ...)
    end
end

local function send(shader, ...)

    local n = select("#", ...)

    local key;
    local i = 1

    while i <= n do

        local v = select(i, ...)

        if type(v) == "string" then
            key = v
        else

            local from, to = i, nil
            while i <= n and type(v) ~= "string" do
                to, i = i, i + 1
                v = select(i, ...)
            end

            shader:send(key, get_elements(from, to, ...))

            goto continue

        end

        i = i + 1

        ::continue::

    end

end

send(shader,

    "col1", {0,1,1,1},
    "col2", {1,0,1,1},
    "col3", {1,1,0,1},

    "cols", {0,0,1,1},
            {0,1,0,1},
            {1,0,0,1}

)

function love.draw()
    lg.setShader(shader)
    lg.draw(mask)
    lg.setShader()
end
My avatar code for the curious :D V1, V2, V3.
Post Reply

Who is online

Users browsing this forum: Ahrefs [Bot] and 43 guests