How to achieve this type of water effect through a shader?

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
KrimsonHorseman
Prole
Posts: 2
Joined: Wed Jan 02, 2019 7:44 am

How to achieve this type of water effect through a shader?

Post by KrimsonHorseman »

Hi, this is a gif from my game.
I have this green water with a subtle glowing/moving effect. It might be hard to see but some of the pixels gradually become brighter/darker and move a little bit to the left/right over time. Right now I have 4 different water tiles with each one having 4 frames of animation to get this effect. Is it possible to achieve something like this using a shader?
User avatar
Lirija
Prole
Posts: 28
Joined: Tue Mar 14, 2017 11:42 am
Contact:

Re: How to achieve this type of water effect through a shader?

Post by Lirija »

I totally think it is.
Actually i'm not totally familiar with shaders and canvases in love but my wild guess is to draw the water tiles in a separate canvas (you might need to add a stencyl) and apply a shader that get a vector of points contained in the water tiles as an extern input and that change their color using sin(u_time)
User avatar
pgimeno
Party member
Posts: 3548
Joined: Sun Oct 18, 2015 2:58 pm

Re: How to achieve this type of water effect through a shader?

Post by pgimeno »

The closest I can think of is to use an image for the spots and use a shader for animation only.

It would consist of three B/W images with black background and the spots that flash in white, and combine them as one single image, with the R channel being one of the B/W images, the G channel being another, and the B channel being another. Maybe you can use the A channel for the same purpose as well.

All the spots in the R channel would flash at the same time. So would all the spots in the G channel and all the spots in the B channel.

In each frame, you would send the shader a time parameter, and the shader would draw the image with the colours that it has built in, using the image for its data only, not directly as colour information. A sine function, multiplied by the corresponding pixel of the image, would add or remove some intensity for the colour in the respective channel. The R, G, and B channel's sine waves would be separated by 120° (or by 90° if you're using R, G, B, A).

The movement could perhaps also be implemented in the shader, but maybe it's best to do it by moving the image a bit when drawing.

In pseudocode:

Code: Select all

   c = pixel from the image
   b = base colour (the background green)
   m = colour when the spot is at its maximum bright
   t = time

   i = max(0, sin(t)) * c.r             // contribution of the R channel
   i = i + max(0, sin(t + 120°)) * c.g  // contribution of the G channel
   i = i + max(0, sin(t + 240°)) * c.b  // contribution of the B channel
   return colour * mix(b, m, i)
Remember that the shader works in radians, though.
User avatar
pgimeno
Party member
Posts: 3548
Joined: Sun Oct 18, 2015 2:58 pm

Re: How to achieve this type of water effect through a shader?

Post by pgimeno »

Here's an implementation of the above idea:

Code: Select all

local lg, lt, le = love.graphics, love.timer, love.event

local shader = lg.newShader[[

extern vec4 base;
extern vec4 maxc;
extern number t;

vec4 effect(vec4 col, Image tex, vec2 imgpos, vec2 scrpos)
{
    vec4 c = Texel(tex, imgpos);
    float i = (sin(t) + 1.) / 2. * c.r;
    i += (sin(t + 2.0943951) + 1.) / 2. * c.g;
    i += (sin(t - 2.0943951) + 1.) / 2. * c.b;
    i = clamp(i, 0., 1.);
    return col * mix(base, maxc, sqrt(i));
}

]]

local rgb = {0,0,1,1}
shader:send('base', rgb)
rgb[1] = 1
rgb[2] = 1
shader:send('maxc', rgb)

local img = lg.newImage('lake-effect.png')

function love.draw()
  shader:send('t', (lt.getTime() % (2* math.pi)) * 7)
  lg.setShader(shader)
  lg.draw(img)
  lg.setShader()
end

function love.keypressed(k)
  return k == "escape" and le.quit()
end
I tweaked some parameters until they worked reasonably, namely the range of the sine wave and the square root.

The image I used uses only three channels. I made it with gimp, using filters>noise>hurl, tools>color tools>threshold, filters>blur>gaussian blur, colors>auto>equalize, colors>auto>normalize (all these three times with different hurl seeds) and colors>components>compose (to join the RGBs). It's included in the attached love file.

And here's the result:

Image

I may use this for T2R :)
Attachments
lake-effect.love
(10.26 KiB) Downloaded 191 times
Post Reply

Who is online

Users browsing this forum: No registered users and 143 guests