3d Flow
Flowing 3d lattice
Source
common.glsl
//vec3 lat1 = vec3(1.,1.618033988749895,-1.618033988749895);
//vec3 lat2 = vec3(1.618033988749895,1.,-1.618033988749895);
//vec3 lat3 = vec3(1.618033988749895,1.618033988749895,1.);
vec3 lat1 = vec3(1.,0.,0.);
vec3 lat2 = vec3(-.5,0.866,.9);
vec3 lat3 = vec3(-.5,-0.8659,.99);
//vec3 lat1 = vec3(.25,0.,0.);
//vec3 lat2 = vec3(0.,2.,0.);
//vec3 lat3 = vec3(0.,0.,2.);
vec4 dotCol = vec4(.8,.05,.04,1.);
vec4 backCol = vec4(1.,1.,1.,0);
vec4 basisCol = vec4(.651,.669,.95,1.);
mat3 flow3g(in float dTime, float a, float s)
{
float s1 = exp(dTime*s);
float s2 = exp(dTime*(1.-s));
return mat3(
s1*cos(a*dTime),s2*sin(a*dTime),0.,
-s1*sin(a*dTime),s2*cos(a*dTime),0.,
0.,0.,exp(-dTime)
);
}
mat3 flow3h(in float dTime, float a, float s)
{
return mat3(
1.,dTime,dTime*dTime/2.,
0.,1.,dTime,
0.,0.,1.
);
}
float sdSphere(vec3 p, float r) { return length(p) - r; }
// Distance to sphere centered at closest lattice point.
float sdLatticeBalls(mat3 L, vec3 p, float r, out vec3 lat)
{
mat3 Linv = inverse(L);
vec3 q = Linv * p; // lattice coordinates
vec3 n = floor(q + 0.5); // nearest integer lattice point
float d = 1e9;
lat = L*n;
return sdSphere(p - lat, r);
}
vec3 getRayDir(vec2 uv, float fov)
{
// uv is on shader plane; camera looks down +z
return normalize(vec3(uv, fov));
}
void reducePair(inout vec3 bi, vec3 bj)
{
float m = round(dot(bi, bj) / dot(bj, bj));
bi -= m * bj;
}
void reduceLattice(inout vec3 b0,
inout vec3 b1,
inout vec3 b2)
{
// fixed number of passes is sufficient in practice
for (int k = 0; k < 100; ++k)
{
reducePair(b0, b1);
reducePair(b0, b2);
reducePair(b1, b0);
reducePair(b1, b2);
reducePair(b2, b0);
reducePair(b2, b1);
}
} image.glsl
void mainImage(out vec4 fragColor, in vec2 fragCoord)
{
float r = .16;
vec2 uv = (fragCoord - 0.5*iResolution.xy) / iResolution.y;
vec3 M1 = texelFetch(notshaders, ivec2(1,1), 0).xyz;
vec3 M2 = texelFetch(notshaders, ivec2(2,1), 0).xyz;
vec3 M3 = texelFetch(notshaders, ivec2(3,1), 0).xyz;
mat3 L = mat3(M1,M2,M3);
// Simple camera
float camdist = 12.;
vec3 ro = vec3(0.0, 0.0, -camdist);
vec3 rd = getRayDir(uv, 1.);
// Raymarch
float startd = camdist/rd.z;
//startd = 0.;
float t = 0.;
float hit = 0.0;
vec3 p;
vec3 lat;
int draw = 100;
for (int it = 0; it < draw*2; it++)
{
p = ro + (t+startd)*rd;
float d = sdLatticeBalls(L, p, r, lat);
if (d < 0.) { d = -3.*r; }
else if (d < .005) { hit = 1.0; break; }
t += d * 0.95;
if (t+startd > float(draw)) break;
}
vec3 col = vec3(1.0);
if (hit > 0.5)
{
vec3 n = normalize(p-lat);
vec3 l = vec3(0.,0.,-100.);
l = normalize(l-p);
float light = dot(n,l)*0.9 + 0.1;
col = vec3(.1+light*.7,0.05,0.04);
float B = min(min(length(lat), length(lat-M1)), min(length(lat-M2), length(lat-M3)));
B = min(min(B, length(lat+M1)), min(length(lat+M2), length(lat+M3)));
if( B < .1 ) { col = vec3(0.04, .05,.1+light*.7); }
col = mix(col, backCol.xyz, t/float(draw));
}
fragColor = vec4(col, 1.0);
} notshaders.glsl
// Stored values
void mainImage( out vec4 fragColor, in vec2 fragCoord )
{
vec4 col = vec4(1.,.5,.5,1.);
vec2 mp = iMouse.xy/iResolution.xy;
// Update the lattice
if(fragCoord.y == 1.5 && fragCoord.x < 4.)
{
vec3 M1 = lat1;
vec3 M2 = lat2;
vec3 M3 = lat3;
if(iFrame > 0)
{
M1 = texelFetch(notshaders, ivec2(1,1), 0).xyz;
M2 = texelFetch(notshaders, ivec2(2,1), 0).xyz;
M3 = texelFetch(notshaders, ivec2(3,1), 0).xyz;
mat3 m = flow3g(.001, 5.*(mp.x-.5),mp.y);
M1 = m*M1;
M2 = m*M2;
M3 = m*M3;
}
reduceLattice(M1,M2,M3);
if(fragCoord.x == 1.5) { col = vec4(M1,0); }
if(fragCoord.x == 2.5) { col = vec4(M2,0); }
if(fragCoord.x == 3.5) { col = vec4(M3,0); }
}
fragColor = col;
} 3dlattice/script.js
// Particle state - positions and velocities
const particles = [];
const NUM_PARTICLES = 50;
export function setup(engine) {
// Initialize particles with random positions and velocities
for (let i = 0; i < NUM_PARTICLES; i++) {
particles.push({
x: Math.random(),
y: Math.random(),
vx: (Math.random() - 0.5) * 0.3,
vy: (Math.random() - 0.5) * 0.3,
});
}
}
export function onFrame(engine, time, deltaTime, frame) {
// Update particle positions
for (const p of particles) {
p.x += p.vx * deltaTime;
p.y += p.vy * deltaTime;
// Bounce off walls
if (p.x < 0 || p.x > 1) {
p.vx *= -1;
p.x = Math.max(0, Math.min(1, p.x));
}
if (p.y < 0 || p.y > 1) {
p.vy *= -1;
p.y = Math.max(0, Math.min(1, p.y));
}
}
// Pack particle data into Float32Array for the shader
// Each particle is a vec4: (x, y, vx, vy)
const data = new Float32Array(NUM_PARTICLES * 4);
for (let i = 0; i < NUM_PARTICLES; i++) {
const p = particles[i];
data[i * 4 + 0] = p.x;
data[i * 4 + 1] = p.y;
data[i * 4 + 2] = p.vx;
data[i * 4 + 3] = p.vy;
}
engine.setUniformValue('uParticles', data);
}