A tool for writing shaders in P5js, focused on SDF with ray marching 😎
To try it out clone this repo and create your own scenes in
src/scenes
lambert-lightning-with-shadows
function setup() {
createCanvas(windowWidth, windowHeight)
tina = new Tina(width, height)
postProc = new Tina(width, height)
tina.build(/* glsl */ `
struct PLight {
vec3 pos;
vec3 color;
float r;
float dist;
} pl;
float sdTerrain(vec3 p) {
float height = cos(p.x) + cos(p.y) + cos(p.z);
return sin(p.y) - height;
}
float sdScene(vec3 p) {
pl.dist = sdSphere(p - pl.pos, pl.r);
p = rotateY(1.2) * p;
p.x -= 2.5;
p.y += 2.5;
p.z += 1.5;
float terrainD = sdTerrain(p);
return min(terrainD, pl.dist);
}
vec3 calcNormal(vec3 p) {
const float eps = 1e-3;
vec3 h = vec3(eps, 0.0, 0.0);
return normalize(vec3(
sdScene(p + h.xyy) - sdScene(p - h.xyy),
sdScene(p + h.yxy) - sdScene(p - h.yxy),
sdScene(p + h.yyx) - sdScene(p - h.yyx)
));
}
vec3 applyPointLight(vec3 p, vec3 normal, vec3 lightPos, vec3 lightColor, vec3 baseColor) {
vec3 lightDir = normalize(pl.pos - p);
float diff = max(dot(normal, lightDir), 0.);
return lightColor * baseColor * diff;
}
vec3 calcPos(vec3 ro, vec3 rd) {
float z = 0.;
for(int i = 0; i < 256; i++) {
vec3 p = ro + z * rd;
float d = sdScene(p) / 2.;
if(d < 1e-3) {
return p;
}
z += d;
if(z > 1e3) break;
}
return vec3(0.);
}
---
uv = uv * 2. - 1.;
uv.x *= width/height;
vec3 ro = vec3(0., 0., 1.);
vec3 rd = normalize(vec3(uv, -.8));
pl = PLight(
vec3(
2. * sin(time / 2.) - 0.3,
0.,
2. * cos(time / 2.) - 3.
),
vec3(1.),
0.1,
0.
);
vec3 p = calcPos(ro, rd);
float isSurface = step(1e-4, length(p));
vec3 lightDir = normalize(p - pl.pos);
vec3 lightRayP = calcPos(
pl.pos + lightDir * (pl.r + 0.01), lightDir
);
float visibility = max(1. - length(lightRayP - p), .2);
vec3 normal = calcNormal(p);
vec3 lighting = applyPointLight(
p, normal, pl.pos, pl.color, vec3(1.)
) * visibility * isSurface;
vec3 color = max(
step(pl.dist, 0.01) * normal,
lighting
);
fragColor = vec4(color, 1.);
`)
postProc.build(/* glsl */ `
uniform sampler2D graphics;
---
uv.y = 1. - uv.y;
fragColor = texture(graphics, uv.xy) +
snoise(vec3(uv * 1e3, time)) * 0.1;
`)
}
function draw() {
let graphics = tina.update()
graphics = postProc.update({ graphics })
image(graphics, 0, 0, width, height)
}