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.
User avatar
Robin
The Omniscient
Posts: 6506
Joined: Fri Feb 20, 2009 4:29 pm
Location: The Netherlands
Contact:

Re: Share a Shader!

Post by Robin » Sun Oct 16, 2011 8:23 pm

RPG wrote:but not GLSLToeffectCode?
Well, you won't need that, would you?

LÖVE compiles effect code to GLSL, so if you want to use GLSL, you just have to make sure LÖVE doesn't do anything fancy with it (which is the solution slime posted).
Help us help you: attach a .love.

User avatar
thelinx
The Strongest
Posts: 847
Joined: Fri Sep 26, 2008 3:56 pm
Location: Sweden

Re: Share a Shader!

Post by thelinx » Sun Oct 16, 2011 9:25 pm

RPG wrote:effectCodeToGLSL

but not GLSLToeffectCode?
You seem to have misunderstood.

_effectCodeToGLSL is the pipe between newPixelEffect and the GLSL compiler.
By default it's defined to rename all the things from PixelEffect-language to GLSL.
However, if you rewrite it and just make it return the same code, you can input regular GLSL to newPixelEffect.

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

Re: Share a Shader!

Post by slime » Sun Oct 16, 2011 9:29 pm

thelinx wrote:
RPG wrote:effectCodeToGLSL

but not GLSLToeffectCode?
You seem to have misunderstood.

_effectCodeToGLSL is the pipe between newPixelEffect and the GLSL compiler.
By default it's defined to rename all the things from PixelEffect-language to GLSL.
However, if you rewrite it and just make it return the same code, you can input regular GLSL to newPixelEffect.
It renames things, makes void main() use a callback function which makes things easier to remember, and locks the GLSL version at 1.20, among other things.

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

Re: Share a Shader!

Post by vrld » Mon Oct 17, 2011 12:25 pm

RPG wrote:I don't think so. love is not the only engine in the world that supports shaders, and glsl shaders must be easily ported between engines.
But love doesn't support (GLSL) shaders. It supports pixel effects, which are totally different things. They just happen to be implemented using GLSL shaders.
RPG wrote:GLSL is standart and it was developed to be the standart.
Yeah, as was Cg/HLSL. Besides, as the others mentioned, effect code is basically a dumbed down version of GLSL, which you verify yourself by looking at the code conversion yourself.
RPG wrote:I have 1000's lines of glsl shaders in more than 20 fragment programs.
If those are functions (which I hope for your sake) you can just plug them in. No problem at all (unless you named something "effect", "number", "Image", "extern", "Texel" or "_tex0_").
I have come here to chew bubblegum and kick ass... and I'm all out of bubblegum.

hump | HC | SUIT | moonshine

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

Re: Share a Shader!

Post by slime » Fri Oct 21, 2011 2:37 am

Bloom shader! I'll post it once I get it cleaned up a bit.

Also, the blur shader I posted previously has a mistake in the code, I'll fix that soon too.

Image

Bloom intensity was increased for demonstration purposes
Last edited by slime on Tue Apr 10, 2012 1:46 am, edited 3 times in total.

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

Re: Share a Shader!

Post by slime » Mon Oct 24, 2011 3:32 pm

Bloom.

Code: Select all

local math, love = math, love

local function FindSmallestPO2(num)
	return 2 ^ math.ceil(math.log(num)/math.log(2))
end

local function ScaleToPO2(xsize, ysize)
	if love.graphics.isSupported("npot") then return xsize, ysize end
	return FindSmallestPO2(xsize), FindSmallestPO2(ysize)
end

-- i've found xsize and ysize of 1/2 - 1/4 the screen resolution to be nice
-- smaller values make the bloom more apparent and give much better performance, but can make it look bad if too small
function CreateBloomEffect(xsize, ysize) 
	if not love.graphics.newPixelEffect
	or not love.graphics.isSupported
	or not love.graphics.isSupported("pixeleffect")
	or not love.graphics.isSupported("canvas") then
		return
	end
	
	local function newPixelEffect(code)
		local success, result = pcall(love.graphics.newPixelEffect, code)
		if success then
			return result
		else
			print("Error compiling shader!\n"..result)
		end
	end
	
	local bloom = {}
	
	local shaders = {
		blur_vert = [[
			extern number canvas_h = 256.0;
			
			const number offset_1 = 1.3846153846;
			const number offset_2 = 3.2307692308;
			
			const number weight_0 = 0.2270270270;
			const number weight_1 = 0.3162162162;
			const number weight_2 = 0.0702702703;
			
			vec4 effect(vec4 color, Image texture, vec2 texture_coords, vec2 pixel_coords)
			{
				vec4 texcolor = Texel(texture, texture_coords);
				vec3 tc = texcolor.rgb * weight_0;
				
				tc += Texel(texture, texture_coords + vec2(0.0, offset_1)/canvas_h).rgb * weight_1;
				tc += Texel(texture, texture_coords - vec2(0.0, offset_1)/canvas_h).rgb * weight_1;
				
				tc += Texel(texture, texture_coords + vec2(0.0, offset_2)/canvas_h).rgb * weight_2;
				tc += Texel(texture, texture_coords - vec2(0.0, offset_2)/canvas_h).rgb * weight_2;
				
				return color * vec4(tc, 1.0);
			}
		]],
		blur_horiz = [[
			extern number canvas_w = 256.0;

			const number offset_1 = 1.3846153846;
			const number offset_2 = 3.2307692308;
			
			const number weight_0 = 0.2270270270;
			const number weight_1 = 0.3162162162;
			const number weight_2 = 0.0702702703;

			vec4 effect(vec4 color, Image texture, vec2 texture_coords, vec2 pixel_coords)
			{
				vec4 texcolor = Texel(texture, texture_coords);
				vec3 tc = texcolor.rgb * weight_0;
				
				tc += Texel(texture, texture_coords + vec2(offset_1, 0.0)/canvas_w).rgb * weight_1;
				tc += Texel(texture, texture_coords - vec2(offset_1, 0.0)/canvas_w).rgb * weight_1;
				
				tc += Texel(texture, texture_coords + vec2(offset_2, 0.0)/canvas_w).rgb * weight_2;
				tc += Texel(texture, texture_coords - vec2(offset_2, 0.0)/canvas_w).rgb * weight_2;
				
				return color * vec4(tc, 1.0);
			}
		]],
		bloom = [[
			extern number threshold = 1.0;
			
			float luminance(vec3 color)
			{
				// numbers make 'true grey' on most monitors, apparently
				return (0.212671 * color.r) + (0.715160 * color.g) + (0.072169 * color.b);
			}
			
			vec4 effect(vec4 color, Image texture, vec2 texture_coords, vec2 pixel_coords)
			{
				vec4 texcolor = Texel(texture, texture_coords);
				
				vec3 extract = smoothstep(threshold * 0.7, threshold, luminance(texcolor.rgb)) * texcolor.rgb;
				return vec4(extract, 1.0);
			}
		]],
		
		combine = [[
			extern Image bloomtex;
			
			extern number basesaturation = 1.0;
			extern number bloomsaturation = 1.0;
			
			extern number baseintensity = 1.0;
			extern number bloomintensity = 1.0;
			
			vec3 AdjustSaturation(vec3 color, number saturation)
			{
    			vec3 grey = vec3(dot(color, vec3(0.212671, 0.715160, 0.072169)));
    			return mix(grey, color, saturation);
			}
		
			vec4 effect(vec4 color, Image texture, vec2 texture_coords, vec2 pixel_coords)
			{
				vec4 basecolor = Texel(texture, texture_coords);
				vec4 bloomcolor = Texel(bloomtex, texture_coords);
				
				bloomcolor.rgb = AdjustSaturation(bloomcolor.rgb, bloomsaturation) * bloomintensity;
				basecolor.rgb = AdjustSaturation(basecolor.rgb, basesaturation) * baseintensity;
				
				basecolor.rgb *= (1.0 - clamp(bloomcolor.rgb, 0.0, 1.0));
				
				bloomcolor.a = 0.0;
				
				return clamp(basecolor + bloomcolor, 0.0, 1.0);
			}
		]]
	}
	
	for k,v in pairs(shaders) do
		local shader = newPixelEffect(v)
		if shader then
			shaders[k] = shader
		else
			return
		end
	end
	
	if not shaders.blur_vert or not shaders.blur_horiz or not shaders.bloom or not shaders.combine then
		return
	end

	
	local intensity_base, intensity_bloom = 1, 1
	local saturation_base, saturation_bloom = 1, 1
	local threshold_bloom = 0.5
	
	local debugdraw = false
	
	function bloom:refresh(xs, ys)
		xs, ys = math.floor(xs+0.5), math.floor(ys+0.5)
		
		local renderingtoscene = false

		self.xsize, self.ysize = xs, ys
		
		self.po2xsize, self.po2ysize = ScaleToPO2(self.xsize, self.ysize) -- scales x and y to next power of 2 if npot is false
		
		self.xres, self.yres = love.graphics.getWidth(), love.graphics.getHeight()
		self.po2xres, self.po2yres = ScaleToPO2(self.xres, self.yres)

	
		self.quad = love.graphics.newQuad(0, 0, self.xsize, self.ysize, self.po2xsize, self.po2ysize)
		self.scenequad = love.graphics.newQuad(0, 0, self.xres, self.yres, self.po2xres, self.po2yres)
	
		self.canvas = {
			bloom = love.graphics.newCanvas(self.po2xsize, self.po2ysize),
			blur_horiz = love.graphics.newCanvas(self.po2xsize, self.po2ysize),
			blur_vert = love.graphics.newCanvas(self.po2xsize, self.po2ysize),
			scene = love.graphics.newCanvas(self.po2xres, self.po2yres),
			bloomscene = love.graphics.newCanvas(self.po2xres, self.po2yres),
		}
				
		for k,v in pairs(self.canvas) do
			v:clear()
		end
		
		shaders.blur_horiz:send("canvas_w", self.po2xsize)
		shaders.blur_vert:send("canvas_h", self.po2ysize)
		self:setThreshold(self:getThreshold())
		self:setIntensity(self:getIntensity())
		self:setSaturation(self:getSaturation())
		
		if renderingtoscene then
			love.graphics.setCanvas(self.canvas.scene)
		end
		
		collectgarbage("collect")
	end
	
	function bloom:debugDraw(shoulddebugdraw)
		debugdraw = not not shoulddebugdraw
	end
	
	function bloom:setIntensity(ibase, ibloom)
		intensity_base = ibase
		intensity_bloom = ibloom
		
		shaders.combine:send("baseintensity", ibase)
		shaders.combine:send("bloomintensity", ibloom)
	end
	function bloom:getIntensity()
		return intensity_base, intensity_bloom
	end
	
	function bloom:setSaturation(sbase, sbloom)
		saturation_base = sbase
		saturation_bloom = sbloom
		
		shaders.combine:send("basesaturation", sbase)
		shaders.combine:send("bloomsaturation", sbloom)
	end
	function bloom:getSaturation()
		return saturation_base, saturation_bloom
	end
	
	function bloom:setThreshold(threshold)
		threshold_bloom = threshold
		
		shaders.bloom:send("threshold", threshold)
	end
	function bloom:getThreshold()
		return threshold_bloom
	end
	
	
	-- call right before drawing the stuff you want bloomed
	function bloom:predraw()
		for k,v in pairs(self.canvas) do
			v:clear()
		end
		love.graphics.setCanvas(self.canvas.scene)
		
		self.drawing = true
	end
	
	function bloom:enabledrawtobloom()
		love.graphics.setCanvas(self.canvas.bloomscene)
	end
	
	-- call after drawing the stuff you want bloomed
	function bloom:postdraw()			
		love.graphics.setColor(255, 255, 255)
		local blendmode = love.graphics.getBlendMode()
		love.graphics.setBlendMode("premultiplied")
		
		love.graphics.push()
		love.graphics.scale(self.po2xsize/self.po2xres, self.po2ysize/self.po2yres)
		
		-- apply bloom extract shader
		love.graphics.setCanvas(self.canvas.bloom)
		love.graphics.setPixelEffect(shaders.bloom)
		love.graphics.drawq(self.canvas.bloomscene, self.scenequad, 0, 0)
		
		love.graphics.pop()
					
		-- apply horizontal blur shader to extracted bloom
		love.graphics.setCanvas(self.canvas.blur_horiz)
		love.graphics.setPixelEffect(shaders.blur_horiz)
		love.graphics.drawq(self.canvas.bloom, self.quad, 0, 0)
		
		-- apply vertical blur shader to blurred bloom
		love.graphics.setCanvas(self.canvas.blur_vert)
		love.graphics.setPixelEffect(shaders.blur_vert)
		love.graphics.drawq(self.canvas.blur_horiz, self.quad, 0, 0)
		
		-- render final scene combined with bloom canvas
		love.graphics.setCanvas()
		shaders.combine:send("bloomtex", self.canvas.blur_vert)
		love.graphics.setPixelEffect(shaders.combine)
		love.graphics.drawq(self.canvas.scene, self.scenequad, 0, 0)
		
		love.graphics.setPixelEffect()
		
		if debugdraw then
			-- love.graphics.setColor(255, 255, 255, 128)
			love.graphics.draw(self.canvas.bloom, 0, 0)
			love.graphics.draw(self.canvas.blur_horiz, self.po2xsize+4, 0)
			love.graphics.draw(self.canvas.blur_vert, self.po2xsize*2+8, 0)
			-- love.graphics.draw(self.canvas.blur_vert, 0, 0, 0, self.po2xres/self.po2xsize, self.po2yres/self.po2ysize)
		end
		
		love.graphics.setBlendMode(blendmode)
		
		self.drawing = false
	end
	
	function bloom:isDrawing()
		return not not self.drawing
	end
	
	bloom:refresh(xsize, ysize)
	
	bloom:setIntensity(1, 1)
	bloom:setSaturation(1, 1)
	bloom:setThreshold(0.35)
	
	return bloom
end
Last edited by slime on Tue Apr 10, 2012 1:33 am, edited 5 times in total.

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

Re: Share a Shader!

Post by vrld » Mon Oct 24, 2011 5:11 pm

A simpler, but not necessarily faster bloom-like effect can be done by oversaturated blurring (code untested):

Code: Select all

bloom = love.graphics.newPixelEffect [[
vec2 image_size;

vec4 effect(vec4 color, Image tex, vec2 tc, vec2 pc)
{
    vec2 pixel_offset = vec2(1.0)/image_size;
    color = Texel(tex, tc); // maybe add a weight here?

    color += Texel(tex, tc + vec2(-offset.x, offset.y));
    color += Texel(tex, tc + vec2(0, offset.y));
    color += Texel(tex, tc + vec2(offset.x, offset.y));

    color += Texel(tex, tc + vec2(-offset.x, 0));
    color += Texel(tex, tc + vec2(0, 0));
    color += Texel(tex, tc + vec2(offset.x, 0));

    color += Texel(tex, tc + vec2(-offset.x, -offset.y));
    color += Texel(tex, tc + vec2(0, -offset.y));
    color += Texel(tex, tc + vec2(offset.x, -offset.y));

    return color / 9.0; // use 10.0 for regular blurring.
}
]]
Last edited by vrld on Mon Oct 24, 2011 5:26 pm, edited 1 time in total.
I have come here to chew bubblegum and kick ass... and I'm all out of bubblegum.

hump | HC | SUIT | moonshine

User avatar
GijsB
Party member
Posts: 380
Joined: Wed Jul 20, 2011 10:19 pm
Location: Netherlands

Re: Share a Shader!

Post by GijsB » Mon Oct 24, 2011 5:14 pm

Why didn't they make the pixeleffect coding in Lua?

User avatar
thelinx
The Strongest
Posts: 847
Joined: Fri Sep 26, 2008 3:56 pm
Location: Sweden

Re: Share a Shader!

Post by thelinx » Mon Oct 24, 2011 7:27 pm

Because Lua can't be hardware accelerated.

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

Re: Share a Shader!

Post by slime » Mon Oct 24, 2011 8:05 pm

The pixeleffects are programs that run on the GPU. OpenGL has its own special language (which pixeleffects use) that gets compiled and then sent to the GPU, it can't use Lua.

Post Reply

Who is online

Users browsing this forum: Google [Bot] and 10 guests