Yesterday I decided to try to make a small project of a green screen for the fun of it and it reminded me a bit about (this project)[https://codea.io/talk/discussion/7634/magic-mirror-green-screen-type-effect-with-video] @West made a year ago. To use it, give it a color
(the colour to detect) and two images
(the camera and the replace texture). It will then draw the camera while replacing any pixels of similar colours to the supplied colour with the replace texture. You can also adjust the sensitivity of the shader by changing m.shader.sens
. setting it to 0 will not replace anything and 1 will replace all pixels. I like it at 0.2 but you can change it at anytime. The shader isn’t as good as @West’s, but it’s better than I thought it would be.
Below is an example project that uses the shader. You can modify it, learn from it, or add things to it.
--# Main
function setup()
info = ""
infoTimer = 0
sampling = true
cameraSource(CAMERA_FRONT)
m = mesh()
m:addRect(WIDTH/2,HEIGHT/2,0,0)
m.texture = CAMERA -- 1920 x 1080
m.shader = shader(Ghost.v,Ghost.f)
m.shader.inactive = true
parameter.boolean("Camera_Front",false,function(b)
cameraSource((b and CAMERA_FRONT) or CAMERA_BACK)
end)
-- Hooray! Variables for playing with! : )
replaceTexture = readImage("SpaceCute:Background") -- The texture that will replace certain pixels. (Will be stretched to fit the screen)
sensitivity = 0.2 -- How different a pixels colour can be from the sample colour before being replaced
end
function draw()
background(40, 40, 50)
camW,camH = spriteSize(CAMERA)
if CurrentOrientation == LANDSCAPE_LEFT or CurrentOrientation == LANDSCAPE_RIGHT then
m:setRect(1,0,0,camW*(WIDTH/camW),camH*(WIDTH/camW))
else
m:setRect(1,0,0,camW*(HEIGHT/camH),camH*(HEIGHT/camH))
end
translate(WIDTH/2,HEIGHT/2)
noSmooth()
m:draw()
if sampling then
stroke(255, 255, 255, 255)
strokeWidth(5)
if sampleC then -- sampleC is the variable that holds the sampled colour
fill(sampleC)
else
noFill()
end
ellipse(0,0,30)
line(-110,0,-10,0)
line(10,0,110,0)
line(0,-110,0,-10)
line(0,10,0,110)
fill(255)
fontSize(20)
smooth()
text("Tap the center of the screen to sample the colour",0,HEIGHT/2 - 30)
text("Tap outside of the center to activate the shader",0,HEIGHT/-2 + 30)
else
smooth()
text("Tap anywhere to return to the colour sampler",0,HEIGHT/-2 + 30)
end
if infoTimer > 0 then -- This is responsible for the blue text that appears in the center of the screen.
infoTimer = infoTimer - DeltaTime
fill(0,200,255)
fontSize(30)
text(info,0,-200)
end
end
function touched(t)
if t.state == 0 then
if sampling and ((t.x - WIDTH/2)^2 + (t.y - HEIGHT/2)^2)^0.5 < 180 then
if not sampleC then
info = "This colour will now be used in the shader"
infoTimer = 4
end
local samCam = image(WIDTH,HEIGHT)
pushStyle()
smooth()
setContext(samCam)
m:draw()
setContext()
popStyle()
local all = {r=0,g=0,b=0,c=0}
for x = WIDTH//2 - 10, WIDTH//2 + 10 do
for y = HEIGHT//2 - 10, HEIGHT//2 + 10 do
if ((x - WIDTH//2)^2 + (y - HEIGHT//2)^2)^0.5 < 10 then--if this pixel is in a 10 pixel radius
local r,g,b = samCam:get(x,y)
all.r = all.r + r
all.g = all.g + g
all.b = all.b + b
all.c = all.c + 1
end
end
end
all.r,all.g,all.b = all.r/all.c,all.g/all.c,all.b/all.c
sampleC = color(all.r,all.g,all.b,255)
samCam = nil
all = nil
collectgarbage()
elseif sampleC then
sampling = not sampling
if sampling then
m.shader.inactive = true
else
m.shader.sens = sensitivity
m.shader.inactive = false
m.shader.overTex = replaceTexture
m.shader.sample = sampleC
end
else
info = "You need to sample a colour for the shader, first"
infoTimer = 4
end
end
end
Ghost = {
v = [[uniform mat4 modelViewProjection;
attribute vec4 position;
attribute vec4 color;
attribute vec2 texCoord;
varying lowp vec4 vColor;
varying highp vec2 vTexCoord;
void main() { vColor = color; vTexCoord = texCoord; gl_Position = modelViewProjection * position; }]],
f = [[
precision highp float;
uniform lowp float sens;
uniform lowp sampler2D texture;
uniform lowp sampler2D overTex;
uniform lowp vec4 sample;
uniform bool inactive;
varying lowp vec4 vColor;
varying highp vec2 vTexCoord;
void main()
{
lowp vec4 col = texture2D( texture, vTexCoord );
if (inactive == false && col.r > sample.r - sens && col.r < sample.r + sens && col.g > sample.g - sens && col.g < sample.g + sens && col.b < sample.b + sens && col.b > sample.b - sens) {
col = texture2D( overTex, vTexCoord );
}
gl_FragColor = col;
}
]]
}