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.

Share a Shader!

Postby kraftman on 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 1896 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 1585 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 1906 times
Last edited by kraftman on Fri Apr 06, 2012 12:30 am, edited 3 times in total.
User avatar
kraftman
Party member
 
Posts: 270
Joined: Sat May 14, 2011 10:18 am

Re: Share a Shader!

Postby kraftman on 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 1685 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 1099 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 954 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
kraftman
Party member
 
Posts: 270
Joined: Sat May 14, 2011 10:18 am

Re: Share a Shader!

Postby slime on 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
slime
Solid Snayke
 
Posts: 1824
Joined: Mon Aug 23, 2010 6:45 am
Location: Canada

Re: Share a Shader!

Postby vrld on 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 896 times
When I was a kid I used to pray every night for a new bicycle. Then I realised God doesn’t work that way, so I stole one and prayed for forgiveness.
hump Helper Utilities for Massive Productivity | HardonCollider Collision detection | Quickie Easy GUI Library
User avatar
vrld
Party member
 
Posts: 835
Joined: Sun Apr 04, 2010 9:14 pm
Location: Germany

Re: Share a Shader!

Postby adrix89 on Tue Oct 11, 2011 3:36 pm

caleidoscope.love

:cry: Image buffer does not compile
adrix89
Citizen
 
Posts: 87
Joined: Fri Oct 15, 2010 10:58 am

Re: Share a Shader!

Postby kraftman on 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 676 times
User avatar
kraftman
Party member
 
Posts: 270
Joined: Sat May 14, 2011 10:18 am

Re: Share a Shader!

Postby adrix89 on 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!
adrix89
Citizen
 
Posts: 87
Joined: Fri Oct 15, 2010 10:58 am

Re: Share a Shader!

Postby kraftman on 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 951 times
Last edited by kraftman on Fri Apr 06, 2012 12:38 am, edited 1 time in total.
User avatar
kraftman
Party member
 
Posts: 270
Joined: Sat May 14, 2011 10:18 am

Re: Share a Shader!

Postby vrld on 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 3466 times
When I was a kid I used to pray every night for a new bicycle. Then I realised God doesn’t work that way, so I stole one and prayed for forgiveness.
hump Helper Utilities for Massive Productivity | HardonCollider Collision detection | Quickie Easy GUI Library
User avatar
vrld
Party member
 
Posts: 835
Joined: Sun Apr 04, 2010 9:14 pm
Location: Germany

Re: Share a Shader!

Postby TechnoCat on 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.
User avatar
TechnoCat
Inner party member
 
Posts: 1607
Joined: Thu Jul 30, 2009 12:31 am
Location: Chicago, IL

Next

Return to Support and Development

Who is online

Users browsing this forum: No registered users and 3 guests