Here’s a fairly pointless curiosity. I can’t claim anything in it as mine, it’s a webgl one I found somewhere and ported into Codea.
-- RayTracer 3Balls
-- Use this function to perform your initial setup
function setup()
displayMode(FULLSCREEN)
myMesh = mesh()
--myMesh.shader = shader("Documents:ray")
myMesh.shader = shader(rayShader.vertexShader, rayShader.fragmentShader)
aVPosBuffer = myMesh:buffer("aVertexPosition")
aPPosBuffer = myMesh:buffer("aPlotPosition")
aVPosBuffer:resize(6)
aPPosBuffer:resize(6)
size = 0.8 -- 1.0 is fullscreen
aVPosBuffer[1] = vec2(size,size)
aVPosBuffer[2] = vec2(-size,size)
aVPosBuffer[3] = vec2(size,-size)
aVPosBuffer[4] = vec2(size,-size)
aVPosBuffer[5] = vec2(-size,size)
aVPosBuffer[6] = vec2(-size,-size)
t=0
s1 = vec3(0,0,0)
s2 = vec3(0,0,0)
s3 = vec3(0,0,0)
end
function calcCamera()
cameraFrom = vec3(math.sin(t*0.4)*28, math.sin(t*0.13)*5+5, math.cos(t*0.4)*28)
cameraTo = vec3(0,0,0)
cameraPersp = 6
up = vec3(0,1,0)
cameraDir = (cameraTo - cameraFrom):normalize()
cameraLeft = cameraDir:cross(up):normalize()
cameraUp = cameraLeft:cross(cameraDir):normalize()
cameraCenter = cameraFrom + cameraDir * cameraPersp
ratio = WIDTH/HEIGHT
cameraTopLeft = cameraCenter + cameraUp + cameraLeft * ratio
cameraBotLeft = cameraCenter - cameraUp + cameraLeft * ratio
cameraTopRight = cameraCenter + cameraUp - cameraLeft * ratio
cameraBotRight = cameraCenter - cameraUp - cameraLeft * ratio
aPPosBuffer[1] = cameraTopRight
aPPosBuffer[2] = cameraTopLeft
aPPosBuffer[3] = cameraBotRight
aPPosBuffer[4] = cameraBotRight
aPPosBuffer[5] = cameraTopLeft
aPPosBuffer[6] = cameraBotLeft
end
function calcBalls()
s1.x = math.sin(t * 1.1) * 1.5
s1.y = math.cos(t * 1.3) * 1.5
s1.z = math.sin(t + math.pi/3) * 1.5
s2.x = math.cos(t * 1.2) * 1.5
s2.y = math.sin(t * 1.4) * 1.5
s2.z = math.sin(t*1.25 - math.pi/3) * 1.5
s3.x = math.cos(t * 1.15) * 1.5
s3.y = math.sin(t * 1.37) * 1.5
s3.z = math.sin(t*1.27) * 1.5
end
-- This function gets called once every frame
function draw()
-- This sets a dark background color
background(0, 0, 0)
calcBalls()
calcCamera()
myMesh.shader.cameraPos = cameraFrom
myMesh.shader.sphere1Center = s1
myMesh.shader.sphere2Center = s2
myMesh.shader.sphere3Center = s3
myMesh:draw()
t = t + 0.03
if t> math.pi * 200 then
t = t - math.pi * 200
end
end
rayShader = {
vertexShader = [[
attribute vec2 aVertexPosition;
attribute vec3 aPlotPosition;
varying vec3 vPosition;
void main(void)
{
gl_Position = vec4(aVertexPosition, 1.0, 1.0);
vPosition = aPlotPosition;
}
]],
fragmentShader = [[
precision highp float;
const vec3 lightDir = vec3(0.577350269, 0.577350269, -0.577350269);
varying vec3 vPosition;
uniform vec3 cameraPos;
uniform vec3 sphere1Center;
uniform vec3 sphere2Center;
uniform vec3 sphere3Center;
bool intersectSphere(vec3 center, vec3 lStart, vec3 lDir,
out float dist) {
vec3 c = center - lStart;
float b = dot(lDir, c);
float d = b*b - dot(c, c) + 1.0;
if (d < 0.0) {
dist = 10000.0;
return false;
}
dist = b - sqrt(d);
if (dist < 0.0) {
dist = 10000.0;
return false;
}
return true;
}
vec3 lightAt(vec3 N, vec3 V, vec3 color) {
vec3 L = lightDir;
vec3 R = reflect(-L, N);
float c = 0.3 + 0.4 * pow(max(dot(R, V), 0.0), 30.0) + 0.7 * dot(L, N);
if (c > 1.0) {
return mix(color, vec3(1.6, 1.6, 1.6), c - 1.0);
}
return c * color;
}
bool intersectWorld(vec3 lStart, vec3 lDir, out vec3 pos,
out vec3 normal, out vec3 color) {
float d1, d2, d3;
bool h1, h2, h3;
h1 = intersectSphere(sphere1Center, lStart, lDir, d1);
h2 = intersectSphere(sphere2Center, lStart, lDir, d2);
h3 = intersectSphere(sphere3Center, lStart, lDir, d3);
if (h1 && d1 < d2 && d1 < d3) {
pos = lStart + d1 * lDir;
normal = pos - sphere1Center;
color = vec3(0.0, 0.0, 0.9);
}
else if (h2 && d2 < d3) {
pos = lStart + d2 * lDir;
normal = pos - sphere2Center;
color = vec3(0.9, 0.0, 0.0);
}
else if (h3) {
pos = lStart + d3 * lDir;
normal = pos - sphere3Center;
color = vec3(0.0, 0.9, 0.0);
}
else if (lDir.y < -0.01) {
pos = lStart + ((lStart.y + 2.7) / -lDir.y) * lDir;
if (pos.x*pos.x + pos.z*pos.z > 30.0) {
return false;
}
normal = vec3(0.0, 1.0, 0.0);
if (fract(pos.x / 5.0) > 0.5 == fract(pos.z / 5.0) > 0.5) {
color = vec3(1.0);
}
else {
color = vec3(0.0);
}
}
else {
return false;
}
return true;
}
void main(void)
{
vec3 cameraDir = normalize(vPosition - cameraPos);
vec3 p1, norm, p2;
vec3 col, colT, colM, col3;
if (intersectWorld(cameraPos, cameraDir, p1,
norm, colT)) {
col = lightAt(norm, -cameraDir, colT);
colM = (colT + vec3(0.7)) / 1.7;
cameraDir = reflect(cameraDir, norm);
if (intersectWorld(p1, cameraDir, p2, norm, colT)) {
col += lightAt(norm, -cameraDir, colT) * colM;
colM *= (colT + vec3(0.7)) / 1.7;
cameraDir = reflect(cameraDir, norm);
if (intersectWorld(p2, cameraDir, p1, norm, colT)) {
col += lightAt(norm, -cameraDir, colT) * colM;
}
}
gl_FragColor = vec4(col, 1.0);
}
else {
discard;
//gl_FragColor = vec4(0.0, 0.0, 0.0, 1.0);
}
}]]
}