I’m trying to write a cellular automaton shader, and I started off by making one to run the basic Conway rules. Here’s my code:
--# Main
-- LifeShader
-- Use this function to perform your initial setup
function setup()
displayMode(FULLSCREEN)
noSmooth()
life = {}
life.s = vec2(WIDTH, HEIGHT)
life.m = mesh()
life.m.vertices = triangulate{vec2(0,0),vec2(0,HEIGHT),vec2(WIDTH,HEIGHT),vec2(WIDTH,0)}
life.m.texCoords = triangulate{vec2(0,0),vec2(0,1),vec2(1,1),vec2(1,0)}
life.m.shader = shader(vertex, fragment)
life.m.shader.size = life.s
life.m.shader.scale = ContentScaleFactor
life.source = image(life.s.x, life.s.y)
life.target = image(life.s.x, life.s.y)
spriteMode(CORNER)
setContext(life.target)
background(0)
setContext()
--setContext(life.source)
--noStroke()
--noSmooth()
--[[
local nf = 50 + 5*math.random()
local xd = math.random()
local yd = math.random()
for x = 1,WIDTH do
for y = 1,HEIGHT do
if noise(nf*x/WIDTH + xd,nf*y/HEIGHT + yd) > .2 then
life.source:set(x, y, color(255))
end
--rect(x, y, 1, 1)
end
end
--]]
--setContext()
--[[
local pattern = readImage("Project:glidergun")
local p2 = image(math.ceil(pattern.width / 2) * 2, math.ceil(pattern.height / 2) * 2)
setContext(p2)
background(0)
for x = 1, pattern.width do
for y = 1, pattern.height do
p2:set(x, y, color(255 - pattern:get(x, y)))
end
end
saveImage("Project:ggun", p2)
setContext()
--[=[
--]]
local pattern = readImage("Project:ggun")
--]=]
setContext(life.source)
sprite(pattern, math.floor(WIDTH / 2), math.floor(HEIGHT / 2), pattern.width/2, pattern.height/2)
setContext()
life.m:setColors(color(255))
life.m.texture = life.source
--local i = life.m:addRect(WIDTH/2, HEIGHT/2, WIDTH, HEIGHT)
--life.m:setRectTex(i, WIDTH/2, HEIGHT/2, WIDTH, HEIGHT)
end
-- This function gets called once every frame
function draw()
-- This sets a dark background color
background(255, 255, 255, 255)
-- This sets the line thickness
strokeWidth(0)
noSmooth()
--fill(255)
-- Do your drawing here
spriteMode(CORNER)
life.m.texture = life.source
setContext(life.target)
--background(255)
life.m:draw()
setContext()
spriteMode(CORNER)
sprite(life.target, 0,0)
life.source = life.target
end
function touched(t)
setContext(life.source)
stroke(255)
strokeWidth(1)
line(t.prevX, t.prevY, t.x, t.y)
setContext()
end
--# Shader
vertex = [[
//
// 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 highp 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;
}
]]
fragment = [[
//
// A basic fragment shader
//
//Default precision qualifier
precision highp float;
//This represents the current texture on the mesh
uniform highp sampler2D texture;
uniform highp vec2 size;
uniform highp float scale;
//The interpolated vertex color for this fragment
varying highp vec4 vColor;
//The interpolated texture coordinate for this fragment
varying highp vec2 vTexCoord;
void main()
{
//Sample the texture at the interpolated coordinate
highp vec4 col = texture2D( texture, vTexCoord ) * vColor;
//*
int neighbors = 0;
highp float w = 1. / size.x / scale;
highp float h = 1. / size.y / scale;
neighbors += int(texture2D(texture, vTexCoord + vec2(-w, -h)).r+.5);
neighbors += int(texture2D(texture, vTexCoord + vec2(0., -h)).r+.5);
neighbors += int(texture2D(texture, vTexCoord + vec2(w, -h)).r+.5);
neighbors += int(texture2D(texture, vTexCoord + vec2(-w, 0.)).r+.5);
neighbors += int(texture2D(texture, vTexCoord + vec2(w, 0.)).r+.5);
neighbors += int(texture2D(texture, vTexCoord + vec2(-w, h)).r+.5);
neighbors += int(texture2D(texture, vTexCoord + vec2(0., h)).r+.5);
neighbors += int(texture2D(texture, vTexCoord + vec2(w, h)).r+.5);
if (neighbors < 2) col = vec4(0,0,0,1);
else if (neighbors > 3) col = vec4(0,0,0,1);
else if (neighbors == 3) col = vec4(1,1,1,1);
else col = vec4(int(col.r+.5),int(col.r+.5),int(col.r+.5),1);
//*/
/*
col = vec4(1, 0, 0, 1);
//*/
//Set the output color to the texture color
gl_FragColor = col;
}
]]
For some reason, it doesn’t work, and what’s really weird is that the results are different every time I run it, even though the pattern is always the same. It runs perfectly for a little while, and then somewhere some cell is processed wrong, and the errors don’t seem to follow any particular pattern. Any idea how I could fix this?