Someone asked a similar question a while ago, and I tweaked my WIP godray shader for it to work. How’s this?
--# Main
-- Skyrays Lighting
displayMode(OVERLAY)
function setup()
print("----\
Drag to move the image\
Double-tap to zoom in/out\
Zoom is kinda broken, sorry\
----")
sidebarTween = tween.delay(1.5, function()
displayMode(FULLSCREEN)
end)
shouldSidebar = 0
parameter.watch("FPS")
parameter.number("Samples", 0.5, 2.0, 0.5, callback)-- The shader can have the samples set to any amount, but 5 is the most I would recommend. 2 is a good mumber IMO, not too little that it looks really weird, but not too many that it's super laggy
parameter.boolean("Realistic Curve", true, callback) -- Whether or not the light effect should amplified realistically
parameter.boolean("Visible Walls", true, callback) -- Whether the walls should be colored or solid black
parameter.boolean("Overlay", false, callback) -- Sprite the image over where it would be so you can see it in the dark
mult = 1.0 -- Multiplier for the resolution of the screen (you can crank it up to 2k resolution on retinas, set the mult to 2)
screen = image(WIDTH * mult, HEIGHT * mult)
objects = image(WIDTH * mult, HEIGHT * mult)
m = mesh()
rIdx = m:addRect(WIDTH / 2 * mult, HEIGHT / 2 * mult, WIDTH * mult, HEIGHT * mult)
m.texture = screen
m.shader = shader(Shaders.Skyrays.vS, Shaders.Skyrays.fS)
m.shader.point = vec2(0.5, 0.5)
m.shader.samples = 100
pos = {x = WIDTH / 8, y = HEIGHT / 2, s = 1}
img = readImage("Small World:Mine Large") -- The image that is put on the screen to block godrays
bg = image(WIDTH, HEIGHT)
setContext(bg, false)
local xSize = WIDTH / 7
local ySize = WIDTH / 7
for x = WIDTH / 2 - (xSize + 100), WIDTH / 2 + (xSize + 100), 100 do
for y = HEIGHT / 2 - (ySize + 100), HEIGHT / 2 + (ySize + 100), 100 do
sprite("Platformer Art:Block Brick", x, y, 100)
end
end
setContext()
math.randomseed(0)
obj = {}
for i = 1, 25 do
table.insert(obj, {math.random(0, WIDTH), math.random(0, HEIGHT)})
end
end
function callback()
if FPS ~= 0 then
tween.stop(sidebarTween)
shouldSidebar = 10
end
end
function draw()
--noSmooth()
local prev = shouldSidebar
shouldSidebar = math.max(0, shouldSidebar - DeltaTime)
if prev ~= 0 and shouldSidebar == 0 then
displayMode(FULLSCREEN)
end
m.shader.samples = Samples
m.shader.realisticCurve = Realistic_Curve
m.shader.visibleWalls = Visible_Walls
--m.shader.objects = objects
background(119, 182, 200, 255)
strokeWidth(5)
setContext(screen, false)
background(0, 0)
pushMatrix()
translate(pos.x, pos.y)
scale(pos.s)
translate(-(pos.x), -(pos.y))
sprite(img, pos.x, pos.y, WIDTH, HEIGHT)
popMatrix()
setContext()
translate(WIDTH / 2, HEIGHT / 2)
scale(pos.s)
translate(WIDTH / -2, HEIGHT / -2)
local xOff = (pos.x / 100 - math.floor(pos.x / 100)) * 100
local yOff = (pos.y / 100 - math.floor(pos.y / 100)) * 100
sprite(bg, WIDTH / 2 + xOff, HEIGHT / 2 + yOff)
for k, v in ipairs(obj) do
sprite("Cargo Bot:Title Large Crate 1", v[1] + pos.x, v[2] + pos.y, 50)
end
sprite(objects, WIDTH / 2, HEIGHT / 2, WIDTH, HEIGHT)
scale(1 / mult)
m:draw()
resetMatrix()
if Overlay then
tint(255, 31)
sprite(img, pos.x, pos.y, WIDTH, HEIGHT)
noTint()
end
resetMatrix()
fill(255)
font("HelveticaNeue-UltraLight")
fontSize(24)
textMode(CORNER)
local str = "FPS: " .. FPS
local w, h = textSize(str)
text(str, 5, HEIGHT - h - 5)
end
function touched(touch)
if touch.state ~= ENDED and touch.state ~= CANCELLED then
pos.x = pos.x + touch.deltaX
pos.y = pos.y + touch.deltaY
displayMode(FULLSCREEN)
elseif touch.tapCount == 2 then
tween(1, pos, {s = 3 - pos.s}, tween.easing.bounceOut)
if shouldSidebar > 0 then
displayMode(OVERLAY)
end
else
if shouldSidebar > 0 then
displayMode(OVERLAY)
end
end
end
-- FPS counter --
FPS = 0
local frames = 0
local time = 0
tween.delay(0, function()
local d = draw
draw = function()
frames = frames + 1
if math.floor(ElapsedTime) ~= math.floor(time) then
FPS = frames - 1
frames = 1
end
time = ElapsedTime
d()
end
end)
--# Shaders
Shaders = {
Skyrays = {
vS = [[
//
// A basic vertex shader
//
//This is the current model * view * projection matrix
// Codea sets it automatically
uniform mat4 modelViewProjection;
//This is the current mesh vertex position, color and tex coord
// Set automatically
attribute vec4 position;
attribute vec4 color;
attribute vec2 texCoord;
//This is an output variable that will be passed to the fragment shader
varying lowp vec4 vColor;
varying highp vec2 vTexCoord;
void main()
{
//Pass the mesh color to the fragment shader
vColor = color;
vTexCoord = texCoord;
//Multiply the vertex position by our combined transform
gl_Position = modelViewProjection * position;
}
]],
fS = [[
//
// A basic fragment shader
//
//Default precision qualifier
precision highp float;
//This represents the current texture on the mesh
uniform lowp sampler2D texture;
//uniform lowp sampler2D objects;
uniform vec2 point;
//The interpolated vertex color for this fragment
varying lowp vec4 vColor;
//The interpolated texture coordinate for this fragment
varying highp vec2 vTexCoord;
uniform bool realisticCurve;
uniform float samples;
uniform bool visibleWalls;
const float distx = 1.0 / 3.0;
const float texMult = 1.0 / distx;
void main()
{
//Sample the texture at the interpolated coordinate
lowp vec4 col = texture2D( texture, vTexCoord ) * vColor;
if (col.a <= 0.5) {
col = vec4(0.0);
}
float targetDist = distance(point, vTexCoord);
lowp vec4 pointSample = texture2D(texture, point);
bool startVisible = pointSample.a <= 0.5;
float light = 0.0;
float add = max(0.0, 1.0 - targetDist * texMult);
float objLight = add * col.a;
if (targetDist <= distx) {
vec2 dir = normalize(point - vTexCoord);
bool visible = startVisible || col.a <= 0.5;
float visibleNum = 0.0;
float mult;
int c = 0;
for (float dist = 0.0; dist <= targetDist; dist += targetDist / (samples * 10.0)) {
lowp vec4 sample = texture2D(texture, vTexCoord + dir * dist);// + texture2D(objects, vTexCoord + dir * dist);
mult = min(1.0, (1.0 - sample.a) + 0.2);
float objMult;
if (visibleWalls) objMult = min(1.0, (1.0 - sample.a) + 0.85);
if (sample.a != 0.0) {
if (sample.a <= 0.5) {
visibleNum += 1.0;
visible = true;
}
if ((visible && startVisible) || (!visible) || sample.a > 0.5) {
add *= mult;
if (visibleWalls) objLight *= objMult;
if (col.a > 0.5) {
add *= mult;
if (visibleWalls) objLight *= objMult;
c++;
}
if (c > 2) break;
}
}
}
if (visible) {
if (realisticCurve) add = pow(add * 1.5, 2.0) / 1.5;
light += add;
}
}
//Set the output color to the texture color
if (visibleWalls && col.a > 0.5) {
//light = (light + 0.1) * 2.0 - 0.1;
gl_FragColor = mix(vec4(vec3(0.0), 1.0), col, max(0.0, min(1.0, objLight)));
} else {
gl_FragColor = mix(vec4(vec3(0.0), 1.0), col, max(0.0, min(1.0, light)));
}
}
]]
}
}