Shaders: having trouble porting existing shaders

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
unixfreak
Citizen
Posts: 82
Joined: Thu Oct 15, 2015 6:25 am
Location: Bristol, UK
Contact:

Shaders: having trouble porting existing shaders

Post by unixfreak »

I've been digging through the net looking at simple shaders on websites such as shadertoy and others. Trying to implement those examples into a love project to understand things; however they never seem to work and i get all kinds of errors, or broken/visual changes when applied. Even with shaders that are doing very simple things.

I'm fiddling about with shaders, for the fact i want to add a "dome" effect to my background layer of a game i'm working on.

See this example; (how things look now in my project)
https://love2d.org/imgmirrur/qna16BC.png

And how i wish to transform the background layers with a shader: (edited with gimp quickly for illustration, just ignore the platform/enemies being warped, the background is the important part)
https://love2d.org/imgmirrur/RkLoSZd.png

I'm wanting to add some depth to the scene by adding what i think is called a reverse fish eye lense.

What i need to know, is how do i port a shader such as this; http://www.geeks3d.com/20140213/glsl-sh ... filters/3/ into love2d?
User avatar
pgimeno
Party member
Posts: 3544
Joined: Sun Oct 18, 2015 2:58 pm

Re: Shaders: having trouble porting existing shaders

Post by pgimeno »

What you're asking isn't trivial. In LÖVE you don't define the complete shader code directly. Shader code needs a main(), but in LÖVE you never write a main(), because the engine takes care of that part. You define a position() or effect() function instead.

Pure GLSL shaders typically return the results by setting the values of variables. For the vertex shader, that's usually gl_Position. The fragment shader (aka pixel shader) typically uses gl_FragColor, though that has disappeared in more modern versions of OpenGL. If a shader sets any other gl_* variable, chances are that it will not be portable to LÖVE.

Also, depending on the OpenGL version the shader uses, LÖVE might not support it.

By contrast, shaders in LÖVE return values using the return statement of the position() or effect() function. So, that's one thing to change.

The #version line should go, because that must be the first line yet LÖVE wraps your code in a bunch of stuff for cross-platform compatibility. In LÖVE you can use #pragma language glsl3 for 330 (3.30) or #pragma language glsl1 for 120 (1.20). GLES uses other version codes.

Now, from what remains, you need to identify what is specific to the application the shader is using, and what is the actual code that performs the task. And that's when we arrive to the example in the link you've provided.

That example performs the action through a vertex shader. That means that it only modifies the vertices of the geometry in order to perform the effect. You don't want that, unless you're rendering high-poly geometry. I doubt that's the case. If you're rendering the background as a single image, it will have just 4 vertices, so the result of altering them will be another quad, probably just another rectangle, and there will be no visible effect. You could, however, subdivide the image via quads into many small pieces and render them all with a spritebatch, which would make a vertex shader more applicable. But even then, I would expect some per-quad distortion, that can only be minimized if the quads are small. I don't really recommend this approach.

I've looked for other examples and found this: https://gamedev.stackexchange.com/a/29597 - That one does look simple enough, and it has (commented out) code for the inverse distortion that you're looking for. Let's see how to port it.

First the precision statement is not necessary in LÖVE. You can delete it together with the #ifdef and #endif.

Next comes a bit of interpretation of what the variables mean. A varying variable is typically used for communication between the vertex and the fragment shader; the name gives a hint on what it is: the texture coordinate. LÖVE gives you that as a parameter to the effect() function, so instead of a varying, it needs to appear in the parameter list of the effect() function.

Next is a uniform. Uniform variables typically come from the application itself, sent to the shader via shader:send(). In this particular case, however, it's the texture to draw, and LÖVE passes that to us in another parameter of the effect() function. So that will also go there.

Next is fovTheta. That's one you will probably want to define. While not mandatory, you can replace uniform with extern in LÖVE, to more accurately represent how it's used. Take a look at love.graphics.newShader#Shader_Language for other things you can replace to improve readability and possibly even portability.

Then comes the function main(). You don't define a main(), but an effect(), as discussed above.

Note that main() returns its value by setting the gl_FragColor variable; instead, you need to return it.

And that would be all. Putting everything together:

Code: Select all

#pragma language glsl1

extern number fovTheta; // FOV's theta

// fisheye
vec4 effect(vec4 colour, Image u_texture, vec2 v_texCoord vec2 screen_coords)
{   
    vec2 uv = v_texCoord - 0.5;
    number z = sqrt(1.0 - uv.x * uv.x - uv.y * uv.y);
//  number a = 1.0 / (z * tan(fovTheta * 0.5)); // forward lens - not what you want
    number a = (z * tan(fovTheta * 0.5)) / 1.0; // reverse lens
    return Texel(u_texture, (uv* a) + 0.5);
}
Disclaimer: I haven't tested it. I'm not convinced the inverse distortion code is correct, by the way. This code will not work for quads or spritebatches, only for whole images.

One final note: in LÖVE, the pixel shader is the one that applies the setColor setting to the image. The above shader does not take the colour into account, because I have omitted it for clarity, but if you want to be able to tint via setColor what you draw while the shader is active, you can replace the return statement with this:

Code: Select all

    return Texel(u_texture, (uv* a) + 0.5) * colour;
And remember to use shader:send('fovTheta', some_value) in your code.
User avatar
unixfreak
Citizen
Posts: 82
Joined: Thu Oct 15, 2015 6:25 am
Location: Bristol, UK
Contact:

Re: Shaders: having trouble porting existing shaders

Post by unixfreak »

Many thanks for all of that information, sorry for the late reply. This will be very useful to me, although, i think i'm going to go and look at GLSL shaders to try and understand things more about how shaders are generally put together.

Your shader example does work, although i'm not sure how to implement it properly (as for scrolling textures such as the sky, weird things happen when moving the game camera)
Post Reply

Who is online

Users browsing this forum: Google [Bot] and 68 guests