Quads and 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
User avatar
Ref
Party member
Posts: 702
Joined: Wed May 02, 2012 11:05 pm

Quads and Shader

Post by Ref »

A shader applied to a fragment of an image defined by a quad ends up being being applied to the original image, not the fragment.
Is there a way to apply the shader directly to the quad fragment and not to the source image?
Right now, I'm forced to create a canvas from the quad and then apply the shader to the canvas.

In love.update

Code: Select all

num=num<#quad and num+1 or 1
love.graphics.setCanvas(canvas)
love.graphics.clear()
love.graphics.draw(image,quad[num])
love.graphics.setCanvas()
and in love.draw

Code: Select all

love.graphics.setShader( shade )
love.graphics.draw(canvas,screen.w/2,screen.h/2,0,1,1,canvas:getWigth()/2,canvas:getHeight())
love.graphics.setShader()
This seems very inefficient???
User avatar
slime
Solid Snayke
Posts: 3162
Joined: Mon Aug 23, 2010 6:45 am
Location: Nova Scotia, Canada
Contact:

Re: Quads and Shader

Post by slime »

Ref wrote:A shader applied to a fragment of an image defined by a quad ends up being being applied to the original image, not the fragment.
I'm not sure what you mean – pixel shaders are applied to every pixel on the screen that is touched by anything you draw with the shader active. So if you draw a Quad, the shader will only be applied to pixels on the screen that the Quad's rectangle touches.

Also, keep in mind that Quads work by having texture coordinates that are in a smaller range than [0, 1], so be sure your shader makes use of the texture coordinate argument of effect() when calling the Texel/texture2D function, otherwise there's no point to using a Quad.
User avatar
Ref
Party member
Posts: 702
Joined: Wed May 02, 2012 11:05 pm

Re: Quads and Shader

Post by Ref »

Thanks for the reply.

What I see is that if I load a spritesheet
sheet = love.graphics.newImage('spritesheet.png')

and then create a quad for a sprite on the sheet
quad = love.graphics.newQuad(x,y,w,h,sheet:getWidth(),sheet:getHeight())

and then apply a gradient shader
love.graphics.setShader(shader)
love.graphics.draw(sheet,quad,nx,ny,0,1,1)
love.graphics.setShader()

The gradient shader is applied to the spritesheet and not just to the area on the spritesheet defined by the quad.
If I define multiple quads for different parts of the spritesheet, the gradient is different for each quad dependent where on the sheet the sprite is.

That is why I ended up drawing each quad to a canvas and applying the gradient shader to each canvas.

It just seems very inefficient and was wondering if there was a better approach.
User avatar
pgimeno
Party member
Posts: 3656
Joined: Sun Oct 18, 2015 2:58 pm

Re: Quads and Shader

Post by pgimeno »

If you mean that you expected the quad corners' coordinates to be 0 and 1 when they get to the shader, no, it doesn't work that way. LÖVE doesn't create a separate image for the quad, that would be too costly, as is your solution. It sends the full image to the shader, and then limits the range of the texture coordinates to the quad.

If you know the corners of the quad, you can pass them to the shader and do some math to adjust them to the range 0..1. It should be simple.

Say qx, qy, qw, qh are the quad's top-left corner and width/height, and iw and ih are the image's width and height. Then in the shader you can calculate the proper range with:

Code: Select all

extern number qx, qy, qw, qh, iw, ih;

vec4 effect(vec4 color, Image texture, vec2 texture_coords, vec2 screen_coords)
{
    vec2 quad_coords = vec2((texture_coords.x * iw - qx) / qw, (texture_coords.y * ih - qy) / qh);
    ...
In your code, you have to pass the parameters of the quad you're drawing to the shader:

Code: Select all

-- These depend on each quad and have to be sent with the parameters of the quad you're drawing
shader:send("qx", myquad.x)
shader:send("qy", myquad.y)
-- These may depend on each quad, but perhaps you use a fixed width and height for all quads
-- and then you can send them just once.
shader:send("qw", myquad.w)
shader:send("qh", myquad.h)
-- These can be sent only once if you always use the same spritesheet with that shader:
shader:send("iw", myimage.getWidth())
shader:send("ih", myimage.getHeight())
Hope this helps.
User avatar
Ref
Party member
Posts: 702
Joined: Wed May 02, 2012 11:05 pm

Re: Quads and Shader

Post by Ref »

Thanks pgimeno!
Works like a charm.
fps when from 220 to 240.
raz87
Prole
Posts: 7
Joined: Fri Jul 08, 2016 8:22 am

Re: Quads and Shader

Post by raz87 »

Hey guys, I've encountered the exact same as issue as the OP but the solution hasn't worked for me. Its probably because my setup is slightly different and I haven't understood the way the texture_coords work. The result i'm getting is what looks like slices of several sprites grouped together, the result is very weird.

Some background:
I have a 832x64 spritesheet, with 13 sprites in total. Each sprite is 64x64. Therefore my quads are like
0,0 64,0 128,0 192,0 and so on.

I am a bit confused how things are actually working in the code snippet provided by pgimeno.

My question:
If my understanding is correct the following line is executed for every pixel in the Texture, which is going to be the entire sprite sheet in this case because as pgimeno explained love2d sends the entire sprite sheet to the shader not just the quad.

Code: Select all

    vec2 quad_coords = vec2((texture_coords.x * iw - qx) / qw, (texture_coords.y * ih - qy) / qh);
Assuming the love.draw function has been called with the first quad, we will have the following values for the extern variables:
qx = 0, qy = 0, qw = 64, qh = 64, iw = 832, ih = 64;

So now for the first iteration of the above line of code, i'm assuming the shader will start in the top left corner of the texture, so texture_coords.x will be 0.

Code: Select all

x is = (0 * 832 - 0) / 64  = 0
y is = (0 * 64 -0) / 64 = 0
that looks fine, but say next pixel being operated on is x = 0.1,y=0 (to be honest I'm not sure if this will infact be the 2nd pixel), so now

Code: Select all

x is = (0.1 * 832 - 0) / 64  = 1.3
y is = (0 * 64 - 0) / 64 = 0
So now looks like x has exceeded 1? And in a lot of threads what I've read is that texture_coords are between 0 and 1. Not sure whats going on. And I'm sure pgimeno's solution worked for the OP, which means I'm doing something silly.

Any help would be appreciated. Very confused.

Thanks
Last edited by raz87 on Fri Sep 09, 2016 12:20 pm, edited 1 time in total.
User avatar
pgimeno
Party member
Posts: 3656
Joined: Sun Oct 18, 2015 2:58 pm

Re: Quads and Shader

Post by pgimeno »

I think that you're misunderstanding myquad.x and myquad.y (which are qx, qy in the shader code). (Edit: on a second read, I think that what you're misunderstanding may be texture coordinates. See below)

myquad.x, myquad.y, myquad.w, myquad.h are the coordinates and sizes passed to newQuad.

The first quad would have myquad.x, myquad.y, myquad.w, myquad.h = 0, 0, 64, 64; the second quad, 64, 0, 64, 64; the third quad, 128, 0, 64, 64 and so on.

So the calculation for point 0.1 in the second quad should really be:

Code: Select all

(0.1 * 832 - 64) / 64  = 0.3
which means that the texture coordinate 0.1 in the sprite sheet, is 30% into the quad starting from the left.

Edit: To elaborate on how the coordinates work in the shader: For a 13x1 sprite sheet that is perfectly packed like yours (i.e. without extra pixels on the sides or anywhere else), the first quad would have X texture coordinates between 0 and 1/13 ~= 0.0769, the second quad between 1/13 and 2/13 ~= 0.1538 and so on., until the 13th quad which would have coordinates between 12/13 ~= 0.923 and 13/13 = 1. If you get an x coordinate of 0.1, that's because the second quad is being drawn. In fact, the quad number being drawn can also be extracted from the texture coordinate if you know the number of sprites: quad_num = floor(x * 13) where quad_num = 0 would be the first quad.

Edit2: By the way, my real name is Pedro Gimeno (P. Gimeno) thus my nick. There's no "pi" there ;)
raz87
Prole
Posts: 7
Joined: Fri Jul 08, 2016 8:22 am

Re: Quads and Shader

Post by raz87 »

Apologies! I think that was some weird autocorrect on my end. Fixed :)
User avatar
pgimeno
Party member
Posts: 3656
Joined: Sun Oct 18, 2015 2:58 pm

Re: Quads and Shader

Post by pgimeno »

No problem. I'm not sure if you understand the idea, though. If you're drawing the first quad, you should never get a texture X coordinate of 0.1; the X coordinate would always be between 0 and 0.0769 approx. which is 1/13th. If you get 0.1, it's because the second quad is being drawn.

If you still get a mess, may it be because you're not passing to the shader the right qx that corresponds to the actual quad being drawn? Or maybe you're not sending qx to the shader every time you change the quad?
raz87
Prole
Posts: 7
Joined: Fri Jul 08, 2016 8:22 am

Re: Quads and Shader

Post by raz87 »

The 0.1 was just as an example. What you're saying makes complete sense and it appears to be working that way. However I'm still not sure why its drawing what appears to be snippets of various sprites.

Is there a good way to debug a shader? I wish I could log stuff at various points to see whats going on in there
Post Reply

Who is online

Users browsing this forum: Bing [Bot] and 3 guests