Share 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
User avatar
kraftman
Party member
Posts: 275
Joined: Sat May 14, 2011 10:18 am

Share a Shader!

Post by kraftman » Mon Oct 10, 2011 5:46 pm

Seeing as shaders are pretty new and there aren't many examples yet, I thought It'd be nice to pool together what we've come up with so far, so that people who're interested in checking them out before 0.8 is properly out can see what they can do and have a play.
I'll dump a few I've come up with so far in here, and if other people have some to share too, that'd be great!

This performs a simple box blur on boxes created by the mouse:
shader mouse test1.love
0.8
(942 Bytes) Downloaded 2940 times
This is an experiment attempting to simulate sand stacking purely using shaders, may appear upside down depending on which version of 0.8 you have:
sand shader.love
0.8
(1.28 KiB) Downloaded 2245 times

From a previous thread by GijsB about images effects, this shows a few basic effects that shaders can perform on an image:
Images Effects -shader.love
(1.02 MiB) Downloaded 2460 times
Last edited by kraftman on Fri Apr 06, 2012 12:30 am, edited 3 times in total.

User avatar
kraftman
Party member
Posts: 275
Joined: Sat May 14, 2011 10:18 am

Re: Share a Shader!

Post by kraftman » Mon Oct 10, 2011 5:57 pm

Sorry for the double post, but it seems i cant attach more than 3 files.

This is a test drawing application with layers. It uses a shader to display the color picker (middle mouse button) since chosing colors needs to be fairly responsive:
Drawing test1.love
0.8
(9.23 KiB) Downloaded 1992 times
This is an accident while making the sand shader, dragging the mouse across the window creates a strange fractal effect:
fractal shader.love
0.8
(1.46 KiB) Downloaded 1453 times
This is another side project for a gallery I'm making, I wanted the icons to have a mirrored effect. WASD controls the left image, arrows for the right:
gallery mirror.love
0.8
(85.49 KiB) Downloaded 1210 times

Hopefully these will be of use to someone.
Last edited by kraftman on Fri Apr 06, 2012 12:35 am, edited 1 time in total.

User avatar
slime
Solid Snayke
Posts: 2673
Joined: Mon Aug 23, 2010 6:45 am
Location: Nova Scotia, Canada
Contact:

Re: Share a Shader!

Post by slime » Mon Oct 10, 2011 5:58 pm

Instead of posting a .love file, I'll post code. ;)

This is a very highly unoptimized version of my dynamic lighting + normal mapping shader as seen here. I also just changed it from the ugly to the unoptimized version right now, so it may not work correctly. :P

Also it's the version without the matrix multiplication for proper lighting when the image is rotated. Maybe I'll add that to this later.

Code: Select all

const int numlights = 3;

const vec3 gamma = vec3(2.2);
const vec3 invgamma = 1.0/gamma;

const vec3 viewdir = vec3(0.0, 0.0, 1.0);


extern vec4 Lights[6]; // max n / 2 lights

extern Image normaltexture;

extern number specpower = 25.0;
		
extern number yres;
extern number z = 0.0;
		
vec4 pow(vec4 color, vec3 exp)
{
	color.rgb = pow(color.rgb, exp);
	return color;
}

vec4 effect(vec4 color, Image texture, vec2 texture_coords, vec2 pixel_coords)
{
	vec3 coords = vec3(pixel_coords.x, yres - pixel_coords.y, z);
				
	vec4 texcolor = pow(Texel(texture, texture_coords), gamma);

	vec4 finalcolor = pow(vec4(0.0), gamma) * texcolor;
	
	vec4 normal = Texel(normaltexture, texture_coords);
	vec3 N = normalize(normal.xyz * 2.0 - 1.0);
	
	float specvalue = normal.a; // the normal texture has the specular texture inside its alpha component
	
	for (int i = 0; i < numlights * 2; i += 2)
	{
		vec3 L = normalize(Lights[i].xyz - coords);
		number NdotL = max(dot(N, L), 0.0);
	
		vec3 R = normalize(reflect(-L, N));
		number specular = pow(max(dot(viewdir, R), 0.0), specpower);
	
		finalcolor += (texcolor * Lights[i+1] * NdotL) + (Lights[i+1] * specvalue * specular);
	}

	finalcolor.a = texcolor.a;
	
	return pow(finalcolor, invgamma);
}
EDIT: For reference, here is the optimized file I made for my space game. It's not very easy to understand.

Code: Select all

Effects.Lighting = class("Effects.Lighting")
	
function Effects.Lighting:initialize()
	local mt = {
		__index = function(self, k)
			if type(k) == "number" then self[k] = {} return self[k] end
		end,
	}
	
	self.shaders = setmetatable({numdirlights=0, numpointlights=0}, mt)
	
	self.lights = {
		point = {num=0},
		dir = {num=0},
	}
	
	self.ambientcolor = {0, 0, 0, 0}
	
	self.current = {
		angle = 0,
		specpower = 25,
		rotationmatrix = RotationMatrix(0),
		z = 0,
	}
end

function Effects.Lighting:GetCurrentShaders()
	return self.shaders[self.lights.point.num][self.lights.dir.num]
end

function Effects.Lighting:AddLight(lighttype, pos, color, radius)
	local light = {
		pos = pos or {0, 0, 0, radius or 0},
		radius = radius or pos[4] or 0,
		color = color or {1, 1, 1, 1},
		type = lighttype,
	}
	pos[4] = radius or pos[4]
	
	self.lights[lighttype][light] = true
	self.lights[lighttype].num = self.lights[lighttype].num + 1
	
	self:UpdateInfo()
	
	return light
end

function Effects.Lighting:UpdateInfo()
	local curshaders = self:GetCurrentShaders() or {}
	local numpointlights = self.lights.point.num
	local numdirlights = self.lights.dir.num
	-- debug.debug()
	if not next(curshaders) then
		curshaders = {
			standard = self:GenerateShader(false, false, numpointlights, numdirlights),
			normals = self:GenerateShader(true, false, numpointlights, numdirlights),
			specular = self:GenerateShader(true, true, numpointlights, numdirlights),
		}
		--print(curshaders, numpointlights, numdirlights)
	else
		--print("no new light", numpointlights, numdirlights)
	end
	
	for k,v in pairs(curshaders) do
		v:send("rotationmatrix", self.current.rotationmatrix)
		if k == "normals" or k == "specular" then
			if k == "specular" then
				v:send("specpower", self.current.specpower)
			end
			if self.current.normaltexture then v:send("normaltexture", self.current.normaltexture) end
		end
	end
	self.shaders[numpointlights][numdirlights] = curshaders
end

function Effects.Lighting:RemoveLight(light)
	if not self.lights[light.type][light] then return end
	
	self.lights[light.type][light] = nil
	self.lights[light.type].num = self.lights[light.type].num - 1
	
	self:UpdateInfo()
end

function Effects.Lighting:SetMaterialInfo(angle, z, normaltexture, specpower)
	local rotationmatrix = RotationMatrix(angle or 0)
	for k,v in pairs(self:GetCurrentShaders() or {}) do
		if self.current.angle ~= angle then
			v:send("rotationmatrix", rotationmatrix)
		end
		if self.current.z ~= z then
			v:send("z", z)
		end
		if (k == "normals" or k == "specular") and normaltexture then
			v:send("normaltexture", normaltexture)
		end
		if k == "specular" and specpower and self.current.specpower ~= specpower then
			v:send("specpower", specpower)
		end
	end
	
	self.current.angle = angle
	self.current.z = z
	self.current.rotationmatrix = rotationmatrix
	self.current.specpower = specpower
	self.current.normaltexture = normaltexture
end

function Effects.Lighting:SetEffectType(etype)
	local cureffects = self:GetCurrentShaders()
	love.graphics.setPixelEffect(cureffects and cureffects[etype] or nil)
end

function Effects.Lighting:update(dt)
	local dirlights, pointlights = {}, {}
	for k in pairs(self.lights.dir) do
		if k ~= "num" then
			dirlights[#dirlights+1] = k.pos
			dirlights[#dirlights+1] = k.color
		end 
	end
	for k in pairs(self.lights.point) do
		if k ~= "num" then
			pointlights[#pointlights+1] = k.pos
			pointlights[#pointlights+1] = k.color
		end
	end
	
	local cureffects = self:GetCurrentShaders() or {}
	for k, v in pairs(cureffects) do
		-- send lights
		if #pointlights > 0 then v:send("pointlights", unpack(pointlights)) end
		if #dirlights > 0 then v:send("dirlights", unpack(dirlights)) end
	end
end

function Effects.Lighting:SetAmbientColor(r, g, b, a)
	self.ambientcolor = {r, g, b, a}
	for k,v in pairs(self:GetCurrentShaders()) do
		v:send("ambientcolor", self.ambientcolor)
	end
end

local phong_base = [[
const vec3 gamma = vec3(2.2);
const vec3 invgamma = 1.0/gamma;

vec3 viewdir = vec3(0.0, 0.0, 1.0);

/* pointlight_array */
/* dirlight_array */
/* normal_texture */
		
extern number yres = 720;
extern number z = 0.0;
extern number specpower = 25.0;

extern mat2 rotationmatrix;

extern vec4 ambientcolor = vec4(0.5);
		
vec4 pow(vec4 color, vec3 exp)
{
	color.rgb = pow(color.rgb, exp);
	return color;
}

vec4 effect(vec4 color, Image texture, vec2 texture_coords, vec2 pixel_coords)
{
	viewdir.xy = rotationmatrix * viewdir.xy; // TODO: only do this if specular is on
	
	vec3 coords = vec3(pixel_coords.x, yres - pixel_coords.y, z);
				
	vec4 texcolor = pow(Texel(texture, texture_coords), gamma);

	vec4 finalcolor = pow(ambientcolor, gamma) * texcolor;
	
	// get normal
	/* normal_texture_access */
	
	// calculate point lights (if any)
	/* pointlight_calc */
	
	// calculate directional lights (if any)
	/* dirlight_calc */
	
	finalcolor.a = texcolor.a;
	return pow(finalcolor, invgamma) * color;
}
]]

function Effects.Lighting:GenerateShader(use_normalmap, use_specularmap, num_pointlights, num_dirlights)
	local str = string.format("%d;%d;%d;%d", use_normalmap and 1 or 0, use_specularmap and 1 or 0, num_pointlights, num_dirlights)
	if self.shaders[str] then return self.shaders[str] end
	
	use_specularmap = use_normalmap and use_specularmap or false
	
	local shadertext = phong_base
	
	local formats = {
		pointlight_array = "extern vec4 pointlights[%d];",
		dirlight_array = "extern vec4 dirlights[%d];",
		normal_texture = "extern Image normaltexture;",
		normal_texture_access = [[
		vec4 normal = Texel(normaltexture, texture_coords);
		vec3 N = normalize(normal.xyz * 2.0 - 1.0);
		%s
		]],
		pointlight_calc = [[
		number radius_PN_ = pointlights[_PN2_].w;
		
		vec3 lightdir_PN_ = (pointlights[_PN2_].xyz - coords) / radius_PN_;
		lightdir_PN_.xy = rotationmatrix * lightdir_PN_.xy;
		vec3 L_PN_ = normalize(lightdir_PN_);
		
		number atten_PN_ = clamp(1.0 - dot(lightdir_PN_, lightdir_PN_), 0.0, 1.0);
		
		number NdotL_PN_ = max(dot(N, L_PN_), 0.0);
		%s
		finalcolor += (texcolor * pointlights[_PN3_] * NdotL_PN_ * atten_PN_)%s;
		
		]],
	}
	local pointlight_specular = {[[
		vec3 R_PN_ = normalize(reflect(-L_PN_, N));
		number specular_PN_ = pow(max(dot(viewdir, R_PN_), 0.0), specpower);]],
	[[ + (pointlights[_PN3_] * specvalue * specular_PN_ * atten_PN_)]],
	}
	
	formats.dirlight_calc = [[
		vec3 lightdir_DN_ = dirlights[_DN2_].xyz;
		lightdir_DN_.xy = rotationmatrix * lightdir_DN_.xy;
		vec3 L_DN_ = normalize(lightdir_DN_);
		
		number NdotL_DN_ = max(dot(N, L_DN_), 0.0);
		%s
		finalcolor += (texcolor * dirlights[_DN3_] * NdotL_DN_)%s;
		
	]]
	
	local dirlight_specular = {}
	for i,v in ipairs(pointlight_specular) do
		v = v:gsub("pointlights", "dirlights")
		v = v:gsub("_PN", "_DN")
		dirlight_specular[i] = v:gsub(" %* atten_DN_", "")
	end
	
	local t = {}
	for k,v in pairs(formats) do t[k] = "" end
	
	if use_normalmap then
		t.normal_texture = formats.normal_texture
		if use_specularmap then
			t.normal_texture_access = formats.normal_texture_access:format("number specvalue = normal.a;")
			t.pointlight_calc = formats.pointlight_calc:format(unpack(pointlight_specular))
			t.dirlight_calc = formats.dirlight_calc:format(unpack(dirlight_specular))
		else
			t.normal_texture_access = formats.normal_texture_access:format("")
			t.pointlight_calc = formats.pointlight_calc:format("", "")
			t.dirlight_calc = formats.dirlight_calc:format("", "")
		end
	else
		t.normal_texture_access = "vec3 N = vec3(0.0, 0.0, 1.0);"
		t.pointlight_calc = formats.pointlight_calc:format("", "")
		t.dirlight_calc = formats.dirlight_calc:format("", "")
	end
	
	local function add_lights(basestr, numlights)
		local str = ""
		for i=1, numlights do
			local f = basestr
			f = f:gsub("_(%a)N_", "_%1"..i.."_")
			f = f:gsub("_(%a)N2_", (i-1)*2)
			f = f:gsub("_(%a)N3_", (i-1)*2 + 1)
			str = str..f
		end
		return str
	end
	
	t.pointlight_array = num_pointlights > 0 and formats.pointlight_array:format(num_pointlights * 2) or ""
	t.pointlight_calc = add_lights(t.pointlight_calc, num_pointlights)
	
	num_dlights = num_dirlights
	-- debug.debug()
	
	t.dirlight_array = num_dirlights > 0 and formats.dirlight_array:format(num_dirlights * 2) or ""
	t.dirlight_calc = add_lights(t.dirlight_calc, num_dirlights)
	
	shadertext = shadertext:gsub("\t*/%* (.-) %*/", t)
	shadertext = shadertext:gsub("\n\t\t", "\n")
	
	-- print(shadertext)
	
	self.shaders[str] = love.graphics.newPixelEffect(shadertext)
	return self.shaders[str]
end


This is a passthrough shader for graphics primitives (untextured things)

Code: Select all

vec4 effect(vec4 color, Image texture, vec2 texture_coords, vec2 pixel_coords)
{
	return color;
}
This is a passthrough shader for images

Code: Select all

vec4 effect(vec4 color, Image texture, vec2 texture_coords, vec2 pixel_coords)
{
	return color * Texel(texture, texture_coords);
}

This is one way to turn a square image into a rotating sphere (both ways are shown here)

Code: Select all

extern number time; // time in seconds
		
vec4 effect(vec4 color, Image texture, vec2 tc, vec2 pixel_coords)
{
	vec2 p = -1.0 + 2.0 * tc;
	number r = dot(p, p);
	
	if (r > 1.0) discard;
	
	number f = (1.0 - sqrt(1.0 - r)) / (r);
	vec2 uv;
	uv.x = 1.0*p.x*f + time;
	uv.y = 1.0*p.y*f;
	
	return vec4(Texel(texture, uv).xyz, 1.0) * color;
}
Here is the other way shown in the video

Code: Select all

const number pi = 3.14159265;
const number pi2 = 2.0 * pi;

extern number time;
		
vec4 effect(vec4 color, Image texture, vec2 tc, vec2 pixel_coords)
{
	vec2 p = 2.0 * (tc - 0.5);
	
	number r = sqrt(p.x*p.x + p.y*p.y);

	if (r > 1.0) discard;
	
	number d = r != 0.0 ? asin(r) / r : 0.0;
				
	vec2 p2 = d * p;
	
	number x3 = mod(p2.x / (pi2) + 0.5 + time, 1.0);
	number y3 = p2.y / (pi2) + 0.5;
	
	vec2 newCoord = vec2(x3, y3);
	
	vec4 sphereColor = color * Texel(texture, newCoord);
				
	return sphereColor;
}

There are a bunch of post-processing GLSL shaders available here which can be ported to the LÖVE pixeleffect syntax pretty easily.

User avatar
vrld
Party member
Posts: 909
Joined: Sun Apr 04, 2010 9:14 pm
Location: Germany
Contact:

Re: Share a Shader!

Post by vrld » Tue Oct 11, 2011 9:48 am

The effects used to make this thing:

Distortion:

Code: Select all

extern number time;
extern number distortion;

vec4 effect(vec4 color, Image tex, vec2 tc, vec2 pc)
{
	// play with parameters here:
	tc.x += sin(tc.y * 100 + time * 10.0) * .03  * distortion;
	return Texel(tex,tc);
}
Chroma shift (splitting the RGB channels):

Code: Select all

extern vec2 chroma;
extern vec2 imageSize;

vec4 effect(vec4 color, Image tex, vec2 tc, vec2 pc)
{
	vec2 shift = chroma / imageSize;
	return vec4(Texel(tex, tc+shift).r, Texel(tex,tc).g, Texel(tex,tc-shift).b, 1.0);
}
Caleidoscope:

Code: Select all

vec4 effect(vec4 color, Image tex, vec2 tc, vec2 pc)
{
	tc = vec2(1.0,1.0) - abs(2.0 * tc - vec2(1.0,1.0)); // tc.x = 0 ... 1 ... 0
	return vec4(Texel(tex, tc));
}
Combine them to get this thing:
caleidoscope.love
(888 Bytes) Downloaded 1133 times
I have come here to chew bubblegum and kick ass... and I'm all out of bubblegum.

hump | HC | SUIT | shine

User avatar
adrix89
Party member
Posts: 131
Joined: Fri Oct 15, 2010 10:58 am

Re: Share a Shader!

Post by adrix89 » Tue Oct 11, 2011 3:36 pm

caleidoscope.love
:cry: Image buffer does not compile
Workflowy. I have my free space quota but you can check it out.

User avatar
kraftman
Party member
Posts: 275
Joined: Sat May 14, 2011 10:18 am

Re: Share a Shader!

Post by kraftman » Tue Oct 11, 2011 7:38 pm

adrix89 wrote:
caleidoscope.love
:cry: Image buffer does not compile

It doesn't like the word buffer, here's a quick fix:
caleidoscope-fixed.love
(864 Bytes) Downloaded 884 times

User avatar
adrix89
Party member
Posts: 131
Joined: Fri Oct 15, 2010 10:58 am

Re: Share a Shader!

Post by adrix89 » Tue Oct 11, 2011 7:49 pm

kraftman wrote:
adrix89 wrote:
caleidoscope.love
:cry: Image buffer does not compile

It doesn't like the word buffer, here's a quick fix:
caleidoscope-fixed.love
Awesome!
Workflowy. I have my free space quota but you can check it out.

User avatar
kraftman
Party member
Posts: 275
Joined: Sat May 14, 2011 10:18 am

Re: Share a Shader!

Post by kraftman » Tue Oct 11, 2011 8:42 pm

I like the chroma thing a lot, had a play around with adding it to particle systems:
ringfire shader.love
0.8
(2.07 KiB) Downloaded 1218 times
Last edited by kraftman on Fri Apr 06, 2012 12:38 am, edited 1 time in total.

User avatar
vrld
Party member
Posts: 909
Joined: Sun Apr 04, 2010 9:14 pm
Location: Germany
Contact:

Re: Share a Shader!

Post by vrld » Tue Oct 11, 2011 11:00 pm

Metaball shader:

Code: Select all

extern vec2[3] balls;

float metaball(vec2 x, vec2 c)
{
	return 1.0 / dot(x-c, x-c);
}

vec4 effect(vec4 color, Image tex, vec2 tc, vec2 pc)
{
	float d = metaball(pc, balls[0]) + metaball(pc, balls[1]) + metaball(pc, balls[2]);
	if (d <= .0007)
		return vec4(1.0);
	return vec4(0.0);
}
Attachments
metaballs.love
(581 Bytes) Downloaded 3705 times
I have come here to chew bubblegum and kick ass... and I'm all out of bubblegum.

hump | HC | SUIT | shine

User avatar
TechnoCat
Inner party member
Posts: 1610
Joined: Thu Jul 30, 2009 12:31 am
Location: Chicago, IL
Contact:

Re: Share a Shader!

Post by TechnoCat » Tue Oct 11, 2011 11:52 pm

vrld wrote:Metaball shader:

Code: Select all

extern vec2[3] balls;

float metaball(vec2 x, vec2 c)
{
	return 1.0 / dot(x-c, x-c);
}

vec4 effect(vec4 color, Image tex, vec2 tc, vec2 pc)
{
	float d = metaball(pc, balls[0]) + metaball(pc, balls[1]) + metaball(pc, balls[2]);
	if (d <= .0007)
		return vec4(1.0);
	return vec4(0.0);
}
How would I go about parameterizing this? I can't seem to get the balls to stretch further.

Post Reply

Who is online

Users browsing this forum: No registered users and 8 guests