Shader questions

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
darkfrei
Party member
Posts: 1186
Joined: Sat Feb 08, 2020 11:09 pm

Shader questions

Post by darkfrei »

Hi all!

Why this code makes just blue, nothing red?

Code: Select all

 -- main.lua
shader2 = love.graphics.newShader([[
vec4 effect( vec4 color, Image texture, vec2 texture_coords, vec2 screen_coords ){
  if(texture_coords.x > 0.5){
    return vec4(1.0,0.0,0.0,1.0);//red
  }
  else
  {
    return vec4(0.0,0.0,1.0,1.0);//blue
  }
}
]])
function love.draw()
    love.graphics.setShader(shader2)
    love.graphics.rectangle("fill", 0, 0, love.graphics.getWidth(), love.graphics.getHeight())
    love.graphics.setShader()
end
(the code from https://blogs.love2d.org/content/beginn ... de-shaders)
:awesome: in Lua we Löve
:awesome: Platformer Guide
:awesome: freebies
User avatar
slime
Solid Snayke
Posts: 3144
Joined: Mon Aug 23, 2010 6:45 am
Location: Nova Scotia, Canada
Contact:

Re: Shader questions

Post by slime »

The code in the link doesn't use love.graphics.rectangle. Primitive shapes (like rectangles) don't have texture coordinates.
User avatar
darkfrei
Party member
Posts: 1186
Joined: Sat Feb 08, 2020 11:09 pm

Re: Shader questions

Post by darkfrei »

Thanks! Now it works:

Code: Select all

shader2 = love.graphics.newShader([[
extern number screenWidth;

vec4 effect( vec4 color, Image texture, vec2 texture_coords, vec2 screen_coords ){
  if(screen_coords.x/screenWidth > 0.5){
    return vec4(1.0,0.0,0.0,1.0);//red
  }
  else
  {
    return vec4(0.0,0.0,1.0,1.0);//blue
  }
}
]])


shader2:send("screenWidth", love.graphics.getWidth())

function love.draw()
    love.graphics.setShader(shader2)
    love.graphics.rectangle("fill", 0, 0, love.graphics.getWidth(), love.graphics.getHeight())
    love.graphics.setShader()
end
:awesome: in Lua we Löve
:awesome: Platformer Guide
:awesome: freebies
User avatar
darkfrei
Party member
Posts: 1186
Joined: Sat Feb 08, 2020 11:09 pm

Re: Shader questions

Post by darkfrei »

Is here a way to take neighbor pixels easier?

Code: Select all

shader = love.graphics.newShader([[

vec4 Laplacian(Image texture, vec2 texture_coords) {
  vec4 left = Texel(texture, texture_coords + vec2(-1.0 / love_ScreenSize.x, 0.0));
  vec4 right = Texel(texture, texture_coords + vec2(1.0 / love_ScreenSize.x, 0.0));
  vec4 top = Texel(texture, texture_coords + vec2(0.0, -1.0 / love_ScreenSize.y));
  vec4 bottom = Texel(texture, texture_coords + vec2(0.0, 1.0 / love_ScreenSize.y));
  vec4 center = Texel(texture, texture_coords);
  center = center - (left + right + top + bottom)/4.0;
  return 100.0*center;
}

vec4 effect(vec4 color, Image texture, vec2 texture_coords, vec2 screen_coords) {
  vec4 laplacian = Laplacian(texture, texture_coords);
  return laplacian;
}

]])
Attachments
Screenshot 2023-04-03 170844.png
Screenshot 2023-04-03 170844.png (3.52 KiB) Viewed 1912 times
:awesome: in Lua we Löve
:awesome: Platformer Guide
:awesome: freebies
User avatar
marclurr
Party member
Posts: 111
Joined: Fri Apr 22, 2022 9:25 am

Re: Shader questions

Post by marclurr »

How you've done it is pretty much how I'd have done it. That said, according to the GLSL documentation you can do a component-wise division on two vectors, so you can make it very slightly neater with a wrapper function, perhaps something like this (untested):

Code: Select all

vec2 toUV(float x, float y) {
    return vec2(x, y) / love_ScreenSize;
}
....
vec4 left = Texel(texture, texture_coords + toUV(-1.0,0));
Or just forgo the wrapper and inline the division whenever you need to.

Edit: Just realised love_ScreenSize is a vec4, so that would need modifying slightly. Something like:

Code: Select all

vec2(x, y) / love_ScreenSize.xy;
User avatar
pgimeno
Party member
Posts: 3593
Joined: Sun Oct 18, 2015 2:58 pm

Re: Shader questions

Post by pgimeno »

As for your first issue, look here: https://love2d.org/forums/viewtopic.php ... 31#p253931
User avatar
darkfrei
Party member
Posts: 1186
Joined: Sat Feb 08, 2020 11:09 pm

Re: Shader questions

Post by darkfrei »

How to convert grayscale picture to the gradient palette?

For example

Code: Select all

function getColor (t) -- from 0 to 1
  local delta = 2*math.pi/6 -- 60 degrees 
  local angle = t * 5*delta -- 1 is 300 degrees
  local r = 0.5 + math.cos (angle + 1*delta)
  local g = 0.5 + math.cos (angle + 3*delta)
  local b = 0.5 + math.cos (angle + 5*delta)
  r = math.min(1, math.max(0, r))
  g = math.min(1, math.max(0, g))
  b = math.min(1, math.max(0, b))
  return 0.45+r*0.5, 0.45+g*0.5, 0.45+b*0.5
end
And the color palette with 12 colors:

Code: Select all

palette = {}
for i = 0, 11 do
  table.insert(palette,  {getColor(i/11)})
end
:awesome: in Lua we Löve
:awesome: Platformer Guide
:awesome: freebies
User avatar
pgimeno
Party member
Posts: 3593
Joined: Sun Oct 18, 2015 2:58 pm

Re: Shader questions

Post by pgimeno »

Shaders work with one pixel at a time (conceptually). You can sample the texture with Texel, read one of the components (for example R, which is compatible with most texture formats) as grayscale value, apply the formula you want and then return an RGBA vector with the values.

For example, your code would be:

Code: Select all

local shader = love.graphics.newShader[[
vec4 effect(vec4 tint, Image tex, vec2 texpos, vec2 scrpos)
{
    vec2 grey_and_alpha = Texel(tex, texpos).ra;
    float delta = 2.0*3.14159265/6.0;
    float angle = grey_and_alpha.x * 5.0 * delta;
    vec3 result;
    result.r = 0.5 + cos(angle + delta);
    result.g = 0.5 + cos(angle + 3.0*delta);
    result.b = 0.5 + cos(angle + 5.0*delta);
    result = max(result, vec3(0.0, 0.0, 0.0));
    result = min(result, vec3(1.0, 1.0, 1.0));
    return vec4(result * 0.5 + 0.45, grey_and_alpha.y) * tint;
}
]]

local img = love.graphics.newImage('grey.png')

function love.draw()
  love.graphics.setShader(shader)
  love.graphics.draw(img)
  love.graphics.setShader()
end
Or make an image with the palette and use the R component as the coordinate:

Code: Select all

local shader = love.graphics.newShader[[
extern Image palette;
const float width = 3.0; // width of the palette texture; can be extern

const float multiplier = (width - 1.0);
const float divisor = 1.0 / width;

vec4 effect(vec4 tint, Image tex, vec2 texpos, vec2 scrpos)
{
    float grey = Texel(tex, texpos).r;
    vec4 result = Texel(palette, vec2((grey*multiplier+0.5)*divisor, 0.5));
    return result * tint;
}
]]

local img = love.graphics.newImage('grey.png')
local palette = love.graphics.newImage(
  love.image.newImageData(3, 1, 'rgba8', '\255\0\0\255\0\255\0\255\0\0\255\255')
)
shader:send('palette', palette)

function love.draw()
  love.graphics.setShader(shader)
  love.graphics.draw(img)
  love.graphics.setShader()
end
User avatar
darkfrei
Party member
Posts: 1186
Joined: Sat Feb 08, 2020 11:09 pm

Re: Shader questions

Post by darkfrei »

Hi all!

I really need your help! I am making the shader for System Reaction-Diffusion, but something goes wrong:

Code: Select all

local canvasWidth, canvasHeight = 600, 600

local shaderCode = [[
float feed_rate = 0.162; // Feed rate (F)
float kill_rate = 0.155; // Kill rate (K)
float diffusion_rate_red = 0.06; // Diffusion rate for U
float diffusion_rate_green = 0.025; // Diffusion rate for V
float dt = 0.05; // Time step (dt)

vec4 calculateLaplacian(Image texture, vec2 texture_coords) {
	vec2 offsetH = vec2(1.0 / love_ScreenSize.x, 0.0);
	vec2 offsetV = vec2(0.0, 1.0 / love_ScreenSize.y);

  vec4 colorCenter = Texel(texture, texture_coords);
	vec4 colorLeft = Texel(texture, texture_coords - offsetH);
	vec4 colorRight = Texel(texture, texture_coords + offsetH);
	vec4 colorUp = Texel(texture, texture_coords - offsetV);
	vec4 colorDown = Texel(texture, texture_coords + offsetV);
	vec4 colorUpLeft = Texel(texture, texture_coords - offsetV - offsetH);
	vec4 colorUpRight = Texel(texture, texture_coords - offsetV + offsetH);
	vec4 colorDownLeft = Texel(texture, texture_coords + offsetV - offsetH);
	vec4 colorDownRight = Texel(texture, texture_coords + offsetV + offsetH);

	vec4 laplacian = - 1.0 * colorCenter + 
//	0.14645 * (colorLeft + colorRight + colorUp + colorDown) +
//	0.10355 * (colorUpLeft + colorUpRight + colorDownLeft + colorDownRight);
	0.2 * (colorLeft + colorRight + colorUp + colorDown) +
	0.05 * (colorUpLeft + colorUpRight + colorDownLeft + colorDownRight);
	return laplacian;
}


vec4 effect(vec4 color, Image texture, vec2 texture_coords, vec2 screen_coords) {
    vec4 current = Texel(texture, texture_coords);
    vec4 laplacian = calculateLaplacian(texture, texture_coords);
		
		float triangleR = laplacian.r;
		float triangleG = laplacian.g;
		
		float cR = current.r; // current U
		float cG = current.g;
		
    float newU = cR + (diffusion_rate_red * triangleR - cR * cG * cG + feed_rate * (1.0 - cR)) * dt;
    float newV = cG + (diffusion_rate_green * triangleG + cR * cG * cG - (kill_rate + feed_rate) * cG) * dt;

    vec4 new = vec4 (newU, newV, 0, 1);

    return new;
}
]]

shader = love.graphics.newShader(shaderCode)


local canvases = {
	love.graphics.newCanvas(canvasWidth, canvasHeight),
	love.graphics.newCanvas(canvasWidth, canvasHeight)
}

local iCanvas = 1

function love.load()
	love.window.setTitle("Reaction Diffusion Simulation")
	love.window.setMode(canvasWidth, canvasHeight)

	love.graphics.setCanvas(canvases[1])
	love.graphics.setColor (1,0,0)
	for i = 1, 10 do
		love.graphics.rectangle ('fill', math.random (canvasWidth-50), math.random (canvasHeight-50), 50, 50)
	end
--	love.graphics.setCanvas(canvases[2])
	love.graphics.setColor (0,1,0)
	for i = 1, 10 do
		love.graphics.rectangle ('fill', math.random (canvasWidth-50), math.random (canvasHeight-50), 50, 50)
	end
	love.graphics.setColor (1,1,0)
	for i = 1, 10 do
		love.graphics.rectangle ('fill', math.random (canvasWidth-40), math.random (canvasHeight-40), 40, 40)
	end
	love.graphics.setCanvas()
end


function love.update(dt)
	local iPrevCanvas = iCanvas
	iCanvas = iCanvas %2 +1
	love.window.setTitle (iCanvas)
	love.graphics.setCanvas(canvases[iCanvas])
	love.graphics.setShader(shader)
	
	love.graphics.setColor (1,1,1)
		love.graphics.draw (canvases[iPrevCanvas])
	love.graphics.setShader()
	love.graphics.setCanvas()
end


function love.draw()
	love.graphics.setColor (1,1,1)
	love.graphics.draw (canvases[iCanvas])
end
:awesome: in Lua we Löve
:awesome: Platformer Guide
:awesome: freebies
Post Reply

Who is online

Users browsing this forum: Google [Bot], Semrush [Bot] and 1 guest