[SOLVED] Canvas and shader issue

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.
Thorkal
Prole
Posts: 11
Joined: Tue Aug 06, 2019 4:42 pm

[SOLVED] Canvas and shader issue

Post by Thorkal »

Hi everybody,

I have a hard time understanding how to use canvases and shaders together. In my example, I have this code for a class :

Code: Select all

local Class = require 'libs.hump.class'
local Entity = require 'entities.Entity'

local DeathLine = Class{
  __includes = Entity -- DeathLine class inherits our Entity class
}

function DeathLine:init(world, x, y, w, h)

  Entity.init(self, x, y, w, h)

  self.world = world
  self.time = 0

  self.body = love.physics.newBody(self.world, x+w/2, y+h/2, 'kinematic')
  self.shape = love.physics.newRectangleShape(w, h)
  self.fixture = love.physics.newFixture(self.body, self.shape)
  self.fixture:setCategory(8)
  self.fixture:setSensor(true)

  plasLine = love.graphics.newShader[[
  uniform vec2 iResolution;
  uniform float iTime;

  float random( vec2 p ) {
    vec2 K1 = vec2(
        23.14069263277926, // e^pi (Gelfond's constant)
         2.665144142690225 // 2^sqrt(2) (Gelfond–Schneider constant)
    );
    return fract( cos( dot(p,K1) ) * 12345.6789 );
  }

  vec4 effect( vec4 color, Image texture, vec2 texture_coords, vec2 screen_coords ){
    vec2 uv = screen_coords.xy / iResolution.xy;

    vec2 p = uv*2.0 - 1.0;
    p *= 15.0;
        vec2 limit = vec2(1., 7.);
    vec2 sfunc = vec2(p.x, p.y + 2.0*sin(uv.x*10.0-iTime*10 + cos(iTime*random(limit)) )+2.0*cos(uv.x*25.+iTime*random(limit)));
    sfunc.y *= uv.x*2.0+0.05;
    sfunc.y *= 2.0 - uv.x*2.0+0.05;
    sfunc.y /= 0.1; // Thickness fix

    vec3 c = vec3(abs(sfunc.y));
    c = pow(c, vec3(-0.5));
    c *= vec3(1,0.,0.);

    return vec4(c,1.0);
  }
  ]]

  plasLine:send('iResolution', {love.graphics.getWidth(), love.graphics.getHeight()})
  --plasLine:send('iTime', self.time)
  self.can = love.graphics.newCanvas(love.graphics.getWidth(), 100)

  love.graphics.setCanvas(self.can)

  love.graphics.clear()
  love.graphics.setBlendMode("alpha")
  love.graphics.setShader(plasLine)
  love.graphics.rectangle('line', 0, 0, love.graphics.getWidth(), love.graphics.getHeight())
  love.graphics.setShader()

  love.graphics.setCanvas()
end

function DeathLine:update(dt)
  self.time = self.time+dt
  plasLine:send('iTime', os.clock())
end

function DeathLine:draw(dt)
  local x, y = self.body:getWorldPoints(self.shape:getPoints())
  love.graphics.draw(self.can, x, y)
end

return DeathLine

This code doesn't work as I want. I want the canvas to take only 100px in height and the shader inside it to... work, but with this code, I have that :
Image

the only important thing in the picture is the red line, it's my not working shader.

The shader in itself works, because if I comment the line "love.graphics.setShader()", it works fine and it's animated as intended :
Image

The problem here is it take the whole screen and not just the size of my canvas.

How can I make this work as intended ?

Thank you
Last edited by Thorkal on Sun Nov 17, 2019 8:41 am, edited 1 time in total.
User avatar
raidho36
Party member
Posts: 2063
Joined: Mon Jun 17, 2013 12:00 pm

Re: Canvas and shader issue

Post by raidho36 »

You pass the size of the entire screen into shader, that's why it works across entire screen. You should pass the size of the canvas into it.

If your shader generates the image completely programmatically, you don't need this to be a canvas - a blank rectangle will do.
Thorkal
Prole
Posts: 11
Joined: Tue Aug 06, 2019 4:42 pm

Re: Canvas and shader issue

Post by Thorkal »

Thanks raidho36, indeed the value of iResolution wasn't correct. But setting the right size doesn't fix the issue, I don't understand why
User avatar
raidho36
Party member
Posts: 2063
Joined: Mon Jun 17, 2013 12:00 pm

Re: Canvas and shader issue

Post by raidho36 »

You're drawing the rectangle in "line" mode, so it only draws the outline. You should draw it in "fill" mode.
Thorkal
Prole
Posts: 11
Joined: Tue Aug 06, 2019 4:42 pm

Re: Canvas and shader issue

Post by Thorkal »

Unfortunately, using fill doesn't fix the issue :
Image
User avatar
pgimeno
Party member
Posts: 3548
Joined: Sun Oct 18, 2015 2:58 pm

Re: Canvas and shader issue

Post by pgimeno »

If you post something we can run, we can do some debugging; otherwise all we can do is guessing. Help us help you.
Thorkal
Prole
Posts: 11
Joined: Tue Aug 06, 2019 4:42 pm

Re: Canvas and shader issue

Post by Thorkal »

By making a simple main.lua file to give you a runnable example, I figured out that the shader worked normally, except that the line is outside the viewable scope.

With the canvas (both rectangle and canvas options are in the runnable example) I don't have the issue, but my line doesn't move, as if iTime wasn't taken into account.
Attachments
shader.love
(852 Bytes) Downloaded 161 times
User avatar
pgimeno
Party member
Posts: 3548
Joined: Sun Oct 18, 2015 2:58 pm

Re: Canvas and shader issue

Post by pgimeno »

You don't need the canvas, unless you want to draw the effect to a canvas and then draw the canvas to the screen. But then it should be updated in love.update, not in love.load, because otherwise the canvas won't change. You can then draw it in any position you want. Note that when you draw the rectangle to the canvas, you draw it with the shader active, but when you draw the canvas to the screen, you don't: the canvas already has the correct image.

If you don't use a canvas, you also need to let the shader know the coordinates where you're going to draw, and subtract that from screen_coords, because otherwise it's drawn on the top of the screen. For example:

Code: Select all

  plasLine = love.graphics.newShader[[
  extern vec2 iResolution;
  extern float iTime;
  extern vec2 position;  // <---- added
  [...]
  vec4 effect( vec4 color, Image texture, vec2 texture_coords, vec2 screen_coords ){
    vec2 uv = (screen_coords - position) / iResolution;  // <---- changed
  [...]
local coords = {}
function love.draw()
  -- Simple rectangle version
  coords[1] = 0
  coords[2] = love.graphics.getHeight() / 2
  plasLine:send('position', coords)
  love.graphics.setShader(plasLine)
  love.graphics.rectangle('fill', coords[1], coords[2], love.graphics.getWidth(), 100)
  love.graphics.setShader()
On a different matter, in the shader you forgot a couple places where you use an integer where a float should be used. Some graphics drivers are very picky with that. One is the number 100 multiplying iTime and the other is the number 1 as the argument of a vec3, near the end.

Finally, since you're using only the first component of c, you can spare some work from the GPU (if it can't optimize it by itself, which some drivers might not) by using a float instead of a vec3:

Code: Select all

number c = abs(sfunc.y);
c = pow(c, -0.5);
return vec4(c, 0., 0., 1.);
Thorkal
Prole
Posts: 11
Joined: Tue Aug 06, 2019 4:42 pm

Re: Canvas and shader issue

Post by Thorkal »

Okay thank you very much, for your help, it works now.

I have a subsidiary question : for now my shader has a black background. Is it for a shader to have a transparent background ?
User avatar
zorg
Party member
Posts: 3441
Joined: Thu Dec 13, 2012 2:55 pm
Location: Absurdistan, Hungary
Contact:

Re: Canvas and shader issue

Post by zorg »

As far as i know: you can have a transparent background as far as the shaders, canvases and the GPU is concerned; Löve doesn't support window transparency that way though, so whatever's below the window can't show through just because you set some pixels' alpha to 0, hence why you get black (by default anyway).
Me and my stuff :3True Neutral Aspirant. Why, yes, i do indeed enjoy sarcastically correcting others when they make the most blatant of spelling mistakes. No bullying or trolling the innocent tho.
Post Reply

Who is online

Users browsing this forum: No registered users and 140 guests