Here’s another bit of fun with shaders. In this one all the interesting stuff happens in the vertex shader. The mesh is divided into squares which “explode”: they zoom off in different directions slowing rotating as they do. The catch is that you have to program in the full path of each square so you can’t do a “step by step” solution of the ODE. This means that it is only practical to use this for ODEs that can be solved with closed form.
It also demonstrates how buffers work.
Second time through in the video then I didn’t call the background
function. Rather firework-y in effect!
http://www.youtube.com/watch?v=-NXFkp5iKh0
displayMode(FULLSCREEN)
function setup()
backingMode(RETAINED)
explosion = mesh()
local s = shader()
s.vertexProgram, s.fragmentProgram = expshader()
explosion.shader = s
explosion.texture = "Cargo Bot:Codea Icon"
explosion.shader.friction = .1
explosion.shader.factor = 10
local vels = explosion:buffer("velocity")
local origin = explosion:buffer("origin")
local angv = explosion:buffer("angvel")
local m,n = 20,20
vels:resize(m*n*6)
origin:resize(m*n*6)
angv:resize(m*n*6)
local w,h = 20,20
local xx,y = (WIDTH - n*w)/2, (HEIGHT - h*m)/2
explosion.shader.size = vec2(n*w,m*h)
explosion.shader.lr = vec2(xx - w/2,y - h/2)
local c = vec2(xx+n*w/2,y+m*h/2)
local cl = vec2(n*w,m*h):len()/2
local r,th,sf,x,df
sf = .3
df = math.random()
for i=1,m do
x = xx
for j = 1,n do
r = explosion:addRect(x,y,w,h)
explosion:setRectTex(r,(j-1)/n,(i-1)/m,1/n,1/m)
th = 2*noise(i*sf+df,j*sf+df)*math.pi
for k=1,6 do
vels[6*r-k+1] = 20*(2-(c:dist(vec2(x,y))/cl))^2
*vec4(math.cos(th),math.sin(th),0,0)
origin[6*r-k+1] = vec2(x,y)
angv[6*r-k+1] = vec2(th,0)
end
x = x + w
end
y = y + h
end
--startRecording()
end
function draw()
if ElapsedTime < 10 then
background(91, 90, 71, 255)
explosion.shader.time = math.max(0,ElapsedTime-2)
elseif ElapsedTime < 11 then
background(0, 0, 0, 255)
explosion.shader.time = 0
else
explosion.shader.time = math.max(0,ElapsedTime-12)
end
explosion:draw()
end
function touched(touch)
end
function expshader()
return [[
//
// The explosion vertex shader
//
//This is the current model * view * projection matrix
// Codea sets it automatically
uniform mat4 modelViewProjection;
uniform float time;
uniform vec2 size;
uniform vec2 lr;
uniform float friction;
uniform float factor;
lowp vec4 gravity = vec4(0.,-1.,0.,0.);
//This is the current mesh vertex position, color and tex coord
// Set automatically
attribute vec4 position;
attribute vec4 color;
attribute vec2 texCoord;
// These are vertex buffers: initial velocity of the square,
// angular velocity,
// centre of square
attribute vec4 velocity;
attribute vec2 angvel;
attribute vec2 origin;
//This is an output variable that will be passed to the fragment shader
varying lowp vec4 vColor;
varying highp vec2 vTexCoord;
// ODE: x'' = -friction x' + gravity
// Solution: A exp(- friction * time) + B + time*gravity/friction
// Initial conditions:
// A = gravity/(friction*friction) - x'(0)/friction
// B = x(0) -A
void main()
{
//Pass the mesh color to the fragment shader
vColor = color;
vTexCoord = texCoord;
lowp vec4 pos;
lowp float angle = time*angvel.x;
highp vec4 A = gravity/(friction*friction) - velocity/friction;
highp vec4 B = vec4(origin,0.,0.) - A;
lowp mat2 rot = mat2(cos(angle), sin(angle), -sin(angle), cos(angle));
pos = (position - vec4(origin,0.,0.));
pos.xy = rot * pos.xy;
pos += exp(-factor*time*friction)*A + B + factor*time * gravity/friction;
//Multiply the vertex position by our combined transform
gl_Position = modelViewProjection * pos;
}
]],[[
//
// A basic fragment shader
//
//This represents the current texture on the mesh
uniform lowp sampler2D texture;
//The interpolated vertex color for this fragment
varying lowp vec4 vColor;
//The interpolated texture coordinate for this fragment
varying highp vec2 vTexCoord;
void main()
{
//Sample the texture at the interpolated coordinate
lowp vec4 col = texture2D( texture, vTexCoord );
//Set the output color to the texture color
gl_FragColor = col;
}
]]
end