diff --git a/Game-of-Life-3D/README.md b/Game-of-Life-3D/README.md new file mode 100644 index 0000000..83d3e4f --- /dev/null +++ b/Game-of-Life-3D/README.md @@ -0,0 +1,9 @@ +# John Conway's Game of Life: 3D +Multipass GPU implementation of the famous Game of Life. But in 3D! +The rules of this 3D version are as follows: +A cell lives if: + - it is alive and either 5 or 6 neighboors are alive + - or it has 4 alive neighboors +otherwise the cell dies. + +![overview.png](https://git.teridax.de/teridax/Shadertoy-Shaders/raw/branch/main/John-Conway's-Game-of-Life:2D/overview.png) diff --git a/Game-of-Life-3D/overview.webm b/Game-of-Life-3D/overview.webm new file mode 100644 index 0000000..b17b18f Binary files /dev/null and b/Game-of-Life-3D/overview.webm differ diff --git a/Game-of-Life-3D/src/Buffer A.glsl b/Game-of-Life-3D/src/Buffer A.glsl new file mode 100644 index 0000000..218019f --- /dev/null +++ b/Game-of-Life-3D/src/Buffer A.glsl @@ -0,0 +1,41 @@ +void mainImage(out vec4 fragColor, in vec2 fragCoord) +{ + ivec3 xyz = to3d(ivec2(fragCoord)); + + if (xyz.x >= int(Size) || xyz.y >= int(Size) || xyz.z >= int(Size)) + { + fragColor = vec4(0); + return; + } + + if (iFrame == 0) + { + fragColor = vec4( step(0.99, sin(fragCoord.x * 4.0 * fragCoord.y)) ); + return; + } + + if (iFrame % 32 != 0) + { + fragColor = texture(iChannel0, fragCoord/iResolution.xy); + return; + } + + int c = 0; + for (int x = -1; x < 2; x++) + for (int y = -1; y < 2; y++) + for (int z = -1; z < 2; z++) + { + if (x == 0 && y == 0 && z == 0) + continue; + + c += int(texelFetch(iChannel0, to2d(to3d(ivec2(fragCoord)) + ivec3(x,y,z)), 0).r); + } + + int m = int(texelFetch(iChannel0, to2d(to3d(ivec2(fragCoord))), 0).r); + + vec3 live = vec3(0); + if (m == 1 && (c == 5 || c == 6) || c == 4) + live = vec3(1); + + fragColor = vec4(live, 1); +} diff --git a/Game-of-Life-3D/src/Common.glsl b/Game-of-Life-3D/src/Common.glsl new file mode 100644 index 0000000..2512c24 --- /dev/null +++ b/Game-of-Life-3D/src/Common.glsl @@ -0,0 +1,19 @@ + +const float Size = 16.; + +vec2 pixelize(in vec2 xy) +{ + return floor(xy*Size)/Size; +} + +ivec3 to3d(in ivec2 xy) +{ + return ivec3(xy.x, xy.y/int(Size), xy.y % int(Size)) + ivec3(4); +} + +ivec2 to2d(in ivec3 xyz) +{ + ivec3 stw = xyz - ivec3(4); + return ivec2(stw.x, stw.y*int(Size) + stw.z); +} + diff --git a/Game-of-Life-3D/src/Image.glsl b/Game-of-Life-3D/src/Image.glsl new file mode 100644 index 0000000..5669064 --- /dev/null +++ b/Game-of-Life-3D/src/Image.glsl @@ -0,0 +1,104 @@ + + +#define AA 2. + +vec2 boxIntersection(in vec3 ro, in vec3 rd, in vec3 size) +{ + vec3 m = 1.0/rd; + vec3 n = m*ro; + vec3 k = abs(m)*size; + vec3 t1 = -n - k; + vec3 t2 = -n + k; + float tN = max( max( t1.x, t1.y ), t1.z ); + float tF = min( min( t2.x, t2.y ), t2.z ); + if(tN > tF) + return vec2(-1.0); + return vec2( tN, tF ); +} + +mat2 rot(in float t) +{ + float c = cos(t); + float s = sin(t); + return mat2(c, -s, s, c); +} + +vec3 wavelengthToRGB(float wavelength) { + vec3 rgb; + if (wavelength < 380.0) { + rgb = vec3(0.0, 0.0, 0.0); + } else if (wavelength < 440.0) { + rgb = vec3(0.0, 0.0, 0.4 + 0.6 * (wavelength - 380.0) / (440.0 - 380.0)); + } else if (wavelength < 490.0) { + rgb = vec3(0.0, (wavelength - 440.0) / (490.0 - 440.0), 1.0); + } else if (wavelength < 510.0) { + rgb = vec3((wavelength - 490.0) / (510.0 - 490.0), 1.0, 1.0 - (wavelength - 490.0) / (510.0 - 490.0)); + } else if (wavelength < 580.0) { + rgb = vec3(1.0, 1.0 - (wavelength - 510.0) / (580.0 - 510.0), 0.0); + } else if (wavelength < 645.0) { + rgb = vec3(1.0 - (wavelength - 580.0) / (645.0 - 580.0), 0.0, (wavelength - 580.0) / (645.0 - 580.0)); + } else if (wavelength < 781.0) { + rgb = vec3(0.0, 0.0, 0.4 + 0.6 * (780.0 - wavelength) / (780.0 - 645.0)); + } else { + rgb = vec3(0.0, 0.0, 0.0); + } + return rgb; +} + +void mainImage(out vec4 fragColor, in vec2 fragCoord) +{ + vec3 sum = vec3(0); + for (float x = 0.; x < AA; x++) + for (float y = 0.; y < AA; y++) + { + vec2 uv = (fragCoord+vec2(x,y)/AA-.5*iResolution.xy)/iResolution.y; + + vec3 ro = vec3(0,0,-5); + vec3 rd = normalize(vec3(uv, 1)); + + mat2 r0 = rot( iTime *.1 ); + mat2 r1 = rot(0.4); + + ro.yz *= r1; + rd.yz *= r1; + ro.xz *= r0; + rd.xz *= r0; + + vec2 dist = boxIntersection(ro, rd, vec3(1)) + vec2(1e-4,-1e-4); + + vec3 col = vec3(0); + + vec3 p = ro + rd * dist.x; + float S = 128.; + float d = (dist.y-dist.x)/S; + + for (float i = 0.; dist.x > 0. && i < S; i++) + { + vec3 k = floor(p*Size)/Size; + ivec3 v = ivec3((k*.5+.5)*Size); + if (texelFetch(iChannel0, to2d(v), 0).r > 0.5) + { + float c = 0.; + for (int x = -1; x < 2; x++) + for (int y = -1; y < 2; y++) + for (int z = -1; z < 2; z++) + c += texelFetch(iChannel0, to2d(v + ivec3(x,y,z)), 0).r; + + float b = 1.; + for (int y = 1; y < 9; y++) + b += texelFetch(iChannel0, to2d(v + ivec3(0,y,0)), 0).r; + + col = wavelengthToRGB( c / 27.0 * (780.-440.) + 440. ) / b; + break; + } + + p += rd * d; + } + + sum += col; + } + + sum /= AA * AA; + + fragColor = vec4(sum, 1.0); +}