I have been working on a project where one could calculate shadows for small pointlights. This project shows the result with a bigger pointlight in the scene. I hope you like it!
--# Main
function setup()
pi,ti,sin,cos,rad=math.pi,table.insert,math.sin,math.cos,math.rad
scene={Create:Box(vec3(0,-5,0),vec3(100,5,100)),
Create:Box(vec3(10,0,40),vec3(15,15,15)),
Create:Sphere(vec3(60,8,30),8,25),
Create:Cyl(vec3(20,0,20),7,20,40,0)
}
for i=1,#scene do scene[i].shader=shader(BufferVertex,BufferFrag) scene[i].shader.lr=300 end
--Screen
M=mesh()
M.vertices={vec2(0,0),vec2(WIDTH,0),vec2(WIDTH,HEIGHT),vec2(WIDTH,HEIGHT),vec2(0,HEIGHT),vec2(0,0)}
M.texCoords={vec2(0,0),vec2(1,0),vec2(1,1),vec2(1,1),vec2(0,1),vec2(0,0)}
M.shader=shader(LightVertex,LightFrag)
M.shader.lr=300
M.shader.FA=vec2(math.tan(math.rad(25)),WIDTH/HEIGHT)
--Variabler
parameter.watch("1/DeltaTime")
parameter.number("lx",0,100,85)
parameter.number("px",-100,200,-50)
parameter.number("pz",-100,200,-50)
Pos=vec3(-50,75,-50)
Eye=vec3(25,0,25)
Ljus=vec3(0,25,30)
Depth1=image(WIDTH,HEIGHT)
Depth2=image(WIDTH,HEIGHT)
Normals=image(WIDTH,HEIGHT)
end
function draw()
Ljus.x=lx
Pos.x=px
Pos.z=pz
for j=1,3 do
if j==1 then setContext(Depth1,true) elseif j==2 then setContext(Depth2,true) else setContext(Normals,true) end
background(255, 255, 255, 255)
perspective(50,WIDTH/HEIGHT,1,300)
camera(Pos.x,Pos.y,Pos.z,Eye.x,Eye.y,Eye.z)
shV=viewMatrix()
shP=projectionMatrix()
for i=1,#scene do
scene[i].shader.loop=j
scene[i].shader.View=viewMatrix()
scene[i].shader.imodmat=modelMatrix():inverse()
scene[i]:draw()
end
setContext()
end
ortho()
viewMatrix(matrix())
--Screen
background(0, 255, 255, 255)
M.shader.Depth1=Depth1
M.shader.Depth2=Depth2
M.shader.Normals=Normals
M.shader.lp=Ljus
M.shader.iView=shV:inverse()
M.shader.ViewProj=shV*shP
M:draw()
end
function normals(m)
local normal={}
local v=m.vertices
for i=1,#v,3 do
local n=(v[i+1]-v[i]):cross(v[i+2]-v[i]):normalize()
normal[i]=n normal[i+1]=n normal[i+2]=n
end
return normal
end
BufferVertex=[[
uniform highp mat4 modelViewProjection;
attribute vec4 position;
attribute vec3 normal;
varying vec4 Pos;
varying vec3 Norm;
uniform mat4 imodmat;
uniform mat4 View;
void main() {
Pos=View*position;
Norm=(imodmat*vec4(normal,1.)).xyz;
gl_Position=modelViewProjection*position;
}
]]
BufferFrag=[[
precision highp float;
varying highp vec4 Pos;
varying vec3 Norm;
uniform float lr;
uniform int loop;
vec3 encodeDepth(float d) {
vec3 enc=vec3(1.,255.,65025.)*d;
enc=fract(enc);
enc-=vec3(enc.y,enc.z,enc.z)*vec3(1./255.,1./255.,1./255.);
return enc;
}
void main() {
if (loop==1) {
if (!gl_FrontFacing) discard;
highp vec3 co=encodeDepth(-Pos.z/Pos.w/lr);
gl_FragColor=vec4(co,1.);
} else if (loop==2) {
if (gl_FrontFacing) discard;
highp vec3 co=encodeDepth(-Pos.z/Pos.w/lr);
gl_FragColor=vec4(co,1.);
} else {
vec3 N=((Norm+1.)*0.5);
gl_FragColor=vec4(N,1.);
}
}
]]
LightVertex=[[
uniform mat4 modelViewProjection;
attribute vec4 position;
attribute vec2 texCoord;
varying highp vec2 vT;
void main() {
vT=texCoord;
gl_Position=modelViewProjection*position;
}
]]
LightFrag=[[
precision highp float;
uniform sampler2D Depth1;
uniform sampler2D Depth2;
uniform sampler2D Normals;
uniform float lr;
uniform vec3 lp;
uniform vec2 FA;
uniform mat4 iView;
uniform mat4 ViewProj;
varying highp vec2 vT;
const highp vec2 Values=vec2(1./255.,1./65025.);
//This is the number of steps where we check for shadows
#define Steps 25.
float decodeDepth(vec3 c) { return dot(c,vec3(1.,Values.x,Values.y)); }
vec3 ToPos(vec2 vt,float f) {
vec2 uv=vt*2.-1.;
highp float Z=f*lr;
highp float X=uv.x*FA.x*FA.y*Z;
highp float Y=uv.y*FA.x*Z;
highp vec4 fPos=iView*vec4(X,Y,-Z,1.);
return fPos.xyz/fPos.w;
}
void main() {
highp vec3 pd=texture2D(Depth1,vT).xyz;
float D=decodeDepth(pd);
if (D>0.99) discard;
//Var
vec4 Col=vec4(0.3,0.,0.,1.);
vec3 Normal=texture2D(Normals,vT).xyz; Normal=Normal*2.-1.;
vec3 Pos=ToPos(vT,D);
//Light
vec3 ltp=lp-Pos;
float ll=max(1.-length(ltp)/60.,0.);
float Ljus=max(0.,dot(normalize(ltp),Normal)*ll);
if (Ljus!=0.) {
Col+=vec4(Ljus);
vec4 lpos=ViewProj*vec4(lp.xyz,1.); lpos.xy=lpos.xy/lpos.w;
float dz=(lpos.z/lr-D)/Steps; vec2 dir=((lpos.xy*0.5+0.5)-vT)/Steps;
float cz=D; vec2 cpos=vT;
for (int i=0;i<int(Steps);i++) {
cz=cz+dz; cpos=cpos+dir;
if (decodeDepth(texture2D(Depth1,cpos).xyz)<cz && decodeDepth(texture2D(Depth2,cpos).xyz)>cz)
{ Col=vec4(0.3,0.,0.,1.); break; }
}
}
gl_FragColor=vec4(Col.xyz,1.);
}
]]
--# Create
Create=class()
function Create:Box(p,pp)
v={
vec3(p.x,p.y+pp.y,p.z),vec3(p.x,p.y+pp.y,p.z+pp.z),vec3(p.x+pp.x,p.y+pp.y,p.z+pp.z),
vec3(p.x+pp.x,p.y+pp.y,p.z+pp.z),vec3(p.x+pp.x,p.y+pp.y,p.z),vec3(p.x,p.y+pp.y,p.z),
vec3(p.x,p.y,p.z),vec3(p.x+pp.x,p.y,p.z+pp.z),vec3(p.x,p.y,p.z+pp.z),
vec3(p.x+pp.x,p.y,p.z+pp.z),vec3(p.x,p.y,p.z),vec3(p.x+pp.x,p.y,p.z),
vec3(p.x,p.y,p.z),vec3(p.x,p.y+pp.y,p.z),vec3(p.x+pp.x,p.y+pp.y,p.z),
vec3(p.x+pp.x,p.y+pp.y,p.z),vec3(p.x+pp.x,p.y,p.z),vec3(p.x,p.y,p.z),
vec3(p.x,p.y,p.z+pp.z),vec3(p.x+pp.x,p.y,p.z+pp.z),vec3(p.x+pp.x,p.y+pp.y,p.z+pp.z),
vec3(p.x+pp.x,p.y+pp.y,p.z+pp.z),vec3(p.x,p.y+pp.y,p.z+pp.z),vec3(p.x,p.y,p.z+pp.z),
vec3(p.x,p.y,p.z),vec3(p.x,p.y,p.z+pp.z),vec3(p.x,p.y+pp.y,p.z+pp.z),
vec3(p.x,p.y+pp.y,p.z+pp.z),vec3(p.x,p.y+pp.y,p.z),vec3(p.x,p.y,p.z),
vec3(p.x+pp.x,p.y,p.z),vec3(p.x+pp.x,p.y+pp.y,p.z),vec3(p.x+pp.x,p.y+pp.y,p.z+pp.z),
vec3(p.x+pp.x,p.y+pp.y,p.z+pp.z),vec3(p.x+pp.x,p.y,p.z+pp.z),vec3(p.x+pp.x,p.y,p.z)}
m=mesh()
m.vertices=v
m.normals=normals(m)
return m
end
function Create:Cyl(pos,propx,propy,poly,open)
local cyl=mesh()
local fc={}
local ac={}
local cs=cs or 360
local angle=cs/poly
for i=1,poly+1 do
ti(fc,vec3(pos.x+propx*cos(rad(angle*i)),pos.y,pos.z+propx*sin(rad(angle*i))))
ti(ac,vec3(pos.x+open*(propx*cos(rad(angle*(i)))),pos.y+propy,pos.z+open*(propx*sin(rad(angle*(i))))))
end
local v={}
for k=1,poly do
ti(v,fc[k]) ti(v,ac[k]) ti(v,ac[k+1])
ti(v,ac[k+1]) ti(v,fc[k+1]) ti(v,fc[k])
end
cyl.vertices=v
cyl.normals=normals(cyl)
return cyl
end
function Create:Sphere(pos,r,N)
local tab={}
local snorm={}
for n=0,N-1 do
for m=0,N-1 do
x=pos.x+r*sin(pi*m/N)*cos(2*pi*n/N)
y=pos.y+r*sin(pi*m/N)*sin(2*pi*n/N)
z=pos.z+r*cos(pi*m/N)
x1=pos.x+r*sin(pi*m/N)*cos(2*pi*(n+1)/N)
y1=pos.y+r*sin(pi*m/N)*sin(2*pi*(n+1)/N)
z1=pos.z+r*cos(pi*m/N)
x2=pos.x+r*sin(pi*(m+1)/N)*cos(2*pi*(n+1)/N)
y2=pos.y+r*sin(pi*(m+1)/N)*sin(2*pi*(n+1)/N)
z2=pos.z+r*cos(pi*(m+1)/N)
x3=pos.x+r*sin(pi*(m+1)/N)*cos(2*pi*n/N)
y3=pos.y+r*sin(pi*(m+1)/N)*sin(2*pi*n/N)
z3=pos.z+r*cos(pi*(m+1)/N)
ti(tab,vec3(x1,y1,z1)) ti(tab,vec3(x,y,z)) ti(tab,vec3(x2,y2,z2))
ti(tab,vec3(x2,y2,z2)) ti(tab,vec3(x,y,z)) ti(tab,vec3(x3,y3,z3))
ti(snorm,(vec3(x1,y1,z1)-pos):normalize()) ti(snorm,(vec3(x,y,z)-pos):normalize())
ti(snorm,(vec3(x2,y2,z2)-pos):normalize()) ti(snorm,(vec3(x2,y2,z2)-pos):normalize())
ti(snorm,(vec3(x,y,z)-pos):normalize()) ti(snorm,(vec3(x3,y3,z3)-pos):normalize())
end
end
local sph=mesh()
sph.vertices=tab
sph.normals=snorm
return sph
end