Page 1 of 1

Shader questions

Posted: Sun Apr 02, 2023 4:41 pm
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)

Re: Shader questions

Posted: Sun Apr 02, 2023 4:43 pm
by slime
The code in the link doesn't use love.graphics.rectangle. Primitive shapes (like rectangles) don't have texture coordinates.

Re: Shader questions

Posted: Sun Apr 02, 2023 5:00 pm
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

Re: Shader questions

Posted: Mon Apr 03, 2023 3:07 pm
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;
}

]])

Re: Shader questions

Posted: Mon Apr 03, 2023 3:26 pm
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;

Re: Shader questions

Posted: Mon Apr 03, 2023 5:57 pm
by pgimeno
As for your first issue, look here: https://love2d.org/forums/viewtopic.php ... 31#p253931

Re: Shader questions

Posted: Tue Apr 04, 2023 2:23 pm
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

Re: Shader questions

Posted: Tue Apr 04, 2023 5:38 pm
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

Re: Shader questions

Posted: Fri Sep 08, 2023 7:51 pm
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