2e1fa33b431b — Leonard Ritter a month ago
* world volume
2 files changed, 354 insertions(+), 411 deletions(-)

M lib/tukan/gl.sc
M testing/test_subjective_mt.sc
M lib/tukan/gl.sc +1 -0
@@ 676,6 676,7 @@ typedef+ GLtexture
                 if lod
                     GL_NEAREST_MIPMAP_NEAREST
                 else GL_NEAREST
+            else min-filter
         let defaultlevels =
             if (lod | pyramid)
                 let s =

          
M testing/test_subjective_mt.sc +353 -411
@@ 36,6 36,7 @@ using import tukan.sdl
 using import tukan.rotation
 using import tukan.brdf
 using import tukan.normal
+using import tukan.noise
 using import tukan.projection
 using import tukan.derivative
 using import tukan.isosurface

          
@@ 46,25 47,36 @@ using import .testfragment
 let RG = (ResourceGroup "RG")
 from (import tukan.math) let expmix
 
-# reserve 10MB for each voxel buffer
-    at 4 bytes per voxel
-let MAX_VOXELS = ((10 * (1 << 20)) // 4)
-let POINTLIMIT = (1600000 * 6)
+SAMPLE_CAMERA_OFFSET := true
+PROJECT_FINAL_VERTEX := true
+VISUALIZE_IDS := false
+POST_TRANSFORM := false # true is worse
+OCCLUSION_CULLING := true
+FOG := true
+
+# to reach fog density D at depth Z, FOG_RATE = -log2(1 - D)/Z
+FOG_RATE := 0.01 # 50% at 100 units
 
-let BINDING_BUF_CELLS_IN = 1
-let BINDING_BUF_CELLS_OUT = 2
+let MAX_VERTICES = (20 * (1 << 20))
+
+let WORLD_SIZE = (uvec3 256)
+let WORLD_SCALE = (vec3 310.0)
+
+let CUBE_SIZE = (uvec3 128)
+let GROUP_SIZE = 4
+
+let BINDING_BUF_VERTEX_IN = 1
+let BINDING_BUF_VERTEX_OUT = 2
 let BINDING_BUF_DRAW_VOXELS_CMD = 3
 let BINDING_BUF_DISPATCH_CMD = 4
-
-let IMAGE_TARGET_RGBA32F = 1
+let BINDING_IMG_ZBUFFER = 5
+let BINDING_IMG_WORLD_IN = 6
+let BINDING_IMG_WORLD_OUT = 7
 
 let UNIFORM_LEVEL = 1
 let UNIFORM_PROGRAM = 2
 let UNIFORM_SCREEN_SAMPLER = 3
-
-let ProgramVoxelizeInit = 0
-let ProgramVoxelize = 1
-let ProgramSimplify = 2
+let UNIFORM_WORLD_SAMPLER = 7
 
 let LEVELS = 8
 

          
@@ 75,38 87,22 @@ let sqrt3 = (sqrt 3.0)
 
 run-stage;
 
-struct CellVals plain
+struct Vertices plain
     count : u32
-    # each entry holds a key
-    entries : (array u32)
+    # each entry holds a vertex
+    entries : (array vec4)
 
-buffer buf-cells-in : CellVals
-    binding = BINDING_BUF_CELLS_IN
+buffer vertex-in : Vertices
+    binding = BINDING_BUF_VERTEX_IN
     \ readonly coherent
 
-buffer buf-cells-out : CellVals
-    binding = BINDING_BUF_CELLS_OUT
+buffer vertex-out : Vertices
+    binding = BINDING_BUF_VERTEX_OUT
     \ coherent
 
-uniform u-program : i32
-    location = UNIFORM_PROGRAM
-
-uniform u-level : i32
-    location = UNIFORM_LEVEL
-
 uniform smp-screen : sampler2D
     location = UNIFORM_SCREEN_SAMPLER
 
-struct DrawElementsIndirectCommand plain
-    count : u32 = 0
-    instanceCount : u32 = 0
-    firstIndex : u32 = 0
-    baseVertex : u32 = 0
-    baseInstance : u32 = 0
-
-buffer buf-draw-voxels-cmd : DrawElementsIndirectCommand
-    binding = BINDING_BUF_DRAW_VOXELS_CMD
-
 fn simple-sphere (p)
     (length p) - 0.5
 

          
@@ 233,15 229,27 @@ fn one-box (p)
     sdBox p (vec3 0.33)
 
 fn matmapf (p)
-    p := p + 1.0
-    p := p + (vec3 1 0 0) * shglobals.time
-    p := (sdDomainRep p 2.0)
+    #p := p + 1.0
+    #p := p + (vec3 1 0 0) * shglobals.time
+    #p := (sdDomainRep p 2.0)
     #do
         p := p.yzx * 2.0
         (two-boxes-merge p) * 0.5
     #doubletori p
+    #sdmDist
+        simple-sphere p
+        sdMaterial
+            vec4 0.5 0.3 1.0 1.0
     sdmDist
-        simple-sphere p
+        do
+            S := ((length p) - 300.0)
+            p := p * 0.01
+            local d = 0.0
+            for i in (range 8)
+                s := (exp2 (i as f32))
+                d += ((triquad-noise3 (p * s)) * 2.0 - 1.0) / s
+            max S
+                deref d
         sdMaterial
             vec4 0.5 0.3 1.0 1.0
 

          
@@ 272,19 280,28 @@ fn normalmapf (p r)
     - (sdNormalFast mapf p r)
 
 let ONION_NEAR = 0.6
-let ONION_FAR = 20.0
+let ONION_FAR = 100.0
 let ONION_LAYERS = 32.0
 
 fn map_onion_radius (p)
     #r := (clamp ((p.z * 0.5 + 0.5) * 0.5 + 0.5) 0.0 1.0)
     r := (clamp (p.z * 0.5 + 0.5) 0.0 1.0)
     #r := (p.z * 0.5 + 0.5) * ONION_LAYERS
-    #r := (1 + 2 * (sqrt pi) / (ONION_LAYERS * 0.5)) ** r
-    #r := (exp2 (mix (log2 ONION_NEAR) (log2 ONION_FAR) r))
-    r := (expmix ONION_NEAR ONION_FAR r 1.0)
+
+    # roughly square layers
+    embed
+        Z := CUBE_SIZE.z as f32
+        r := ONION_NEAR * ((1 + 2 * (sqrt pi) / (Z * 0.5)) ** (r * Z))
 
-    #r := r * 0.9999
-    #r := r / (1.0 * (1.0 - r))
+    # exponential interpolation
+    #embed
+        r := (expmix ONION_NEAR ONION_FAR r 0.5)
+
+    # infinite perspective projection
+    #embed
+        r := r * 0.9999
+        k := 0.01
+        r := r / (k * (1.0 - r))
 
     _ ((unpack_normal_snorm p.xy) * r) (r * 2.5)
 

          
@@ 297,177 314,12 @@ fn map_identity_radius (p) (_ p 1.0)
 
 let map_vertex map_vertex_rlimit = map_onion map_onion_radius
 #let map_vertex map_vertex_rlimit = map_identity map_identity_radius
-PROJECT_FINAL_VERTEX := false
-VISUALIZE_IDS := false
-
-fn subdivide-cell (key)
-    let level = ((deref u-level) as u32)
-    let r = (/ (f32 (1:u32 << level)))
-    let d = (2.0 * r)
-    #let rlimit =
-        if (level == 8:u32) r
-        else (sqrt3 * r)
-
-    key := (key << 3:u32)
-
-    ucoord := (unpack-morton3x10 key)
-    coord := (vec3 ucoord) * d - 1.0 + r
-
-    global cells : (array u32 8)
-    global written = 0:u32
-
-    fn test-cell (i ofs key coord r)
-        let rlimit = (sqrt3 * r)
-        key := key | i
-        pos := coord + ofs
-        let vx vr = (map_onion_radius pos)
-        let dist = (mapf vx)
-        let hit = ((abs dist) < (rlimit * vr))
-        if hit
-            cells @ (deref written) = key
-            written += 1:u32
-
-    inline test-cell (i ofs)
-        test-cell i ofs key coord r
-
-    test-cell 0:u32 (vec3 0 0 0)
-    test-cell 1:u32 (vec3 d 0 0)
-    test-cell 2:u32 (vec3 0 d 0)
-    test-cell 3:u32 (vec3 d d 0)
-    test-cell 4:u32 (vec3 0 0 d)
-    test-cell 5:u32 (vec3 d 0 d)
-    test-cell 6:u32 (vec3 0 d d)
-    test-cell 7:u32 (vec3 d d d)
-
-    # commit
-    if (written != 0:u32)
-        let id = (atomicAdd buf-cells-out.count (deref written))
-        for i in (range (deref written))
-            buf-cells-out.entries @ (id + i) = (cells @ i)
-
-fn simplify-cell (key)
-    let level = (((deref u-level) as u32) - 1)
-    let r = (/ (f32 (1:u32 << level)))
-    let d = (2.0 * r)
-
-    ucoord := (unpack-morton3x10 key)
-    coord := (vec3 ucoord) * d - 1.0
-    key := (key << 3:u32)
-
-    global mask = 0:u32
-
-    fn check-cell (i ofs coord)
-        let dist = (mapf (map_vertex (coord + ofs)))
-        let bit = (? (dist < 0.0) 1:u32 0:u32)
-        mask |= (bit << i)
-
-    inline check-cell (i ofs)
-        check-cell i ofs coord
-
-    #do
-        let coord = (coord + r)
-        let n =
-            normalmapf coord r
-        embed
-            let a = ((deref shglobals.time) * 0.2)
-            let c s = (cos a) (sin a)
-
-            n :=
-                vec3
-                    c * n.x - s * n.z
-                    n.y
-                    s * n.x + c * n.z
 
-            coord :=
-                vec3
-                    c * coord.x - s * coord.z
-                    coord.y
-                    s * coord.x + c * coord.z
-
-        coord :=
-            coord + (vec3 0 0 1)
-
-        if ((dot n (normalize coord)) < -0.3)
-            return;
-
-    check-cell 0:u32 (vec3 0 0 0)
-    check-cell 1:u32 (vec3 d 0 0)
-    check-cell 2:u32 (vec3 0 d 0)
-    check-cell 3:u32 (vec3 d d 0)
-    check-cell 4:u32 (vec3 0 0 d)
-    check-cell 5:u32 (vec3 d 0 d)
-    check-cell 6:u32 (vec3 0 d d)
-    check-cell 7:u32 (vec3 d d d)
-    let mask = (deref mask)
-
-    if ((mask != 0:u32) & (mask != 255:u32))
-        global cells : (array u32 8)
-        global written = 0:u32
-
-        fn test-cell (i k1 k3 checkmask mask key packedmask)
-            let hit = ((checkmask != 0:u32) & (checkmask != mask))
-            if hit
-                cells @ (deref written) = key | i | (packedmask << 28:u32)
-                written += 1:u32
+inline map-translation (tpos)
+    static-if SAMPLE_CAMERA_OFFSET
+        tpos + (shglobals.view-inverse @ 3) . xyz
+    else tpos
 
-        inline test-cell (i)
-            let tetverts = 0x6cc99:u32
-            k := i * 3:u32
-            k1 := (tetverts >> k) & 7:u32
-            k3 := (tetverts >> (k + 3:u32)) & 7:u32
-            packedmask :=
-                |
-                    mask & 1:u32
-                    ((mask >> k1) & 1:u32) << 1:u32
-                    ((mask >> 7:u32) & 1:u32) << 2:u32
-                    ((mask >> k3) & 1:u32) << 3:u32
-            checkmask :=
-                |
-                    (1:u32 << 0:u32) | (1:u32 << 7:u32)
-                    1:u32 << k1
-                    1:u32 << k3
-            static-assert (constant? checkmask)
-            test-cell i k1 k3 checkmask mask key packedmask
-
-        test-cell 0:u32
-        test-cell 1:u32
-        test-cell 2:u32
-        test-cell 3:u32
-        test-cell 4:u32
-        test-cell 5:u32
-
-        # commit
-        if (written != 0:u32)
-            let id = (atomicAdd buf-cells-out.count (deref written))
-            for i in (range (deref written))
-                buf-cells-out.entries @ (id + i) = (cells @ i)
-
-fn voxelize-init ()
-    let index = (deref gl_GlobalInvocationID.x)
-    subdivide-cell index
-
-fn voxelize ()
-    let index = (deref gl_GlobalInvocationID.x)
-    if (index < buf-cells-in.count)
-        subdivide-cell (deref (buf-cells-in.entries @ index))
-
-fn simplify ()
-    let index = (deref gl_GlobalInvocationID.x)
-    if (index < buf-cells-in.count)
-        simplify-cell (deref (buf-cells-in.entries @ index))
-
-fn supershader ()
-    local_size 64 1 1
-    let mode = (deref u-program)
-    switch mode
-    case ProgramVoxelizeInit
-        voxelize-init;
-    case ProgramVoxelize
-        voxelize;
-    case ProgramSimplify
-        simplify;
-    default
-        ;;
 
 fn calc-projection ()
     let aspect = (vec2 (/ (deref shglobals.aspect)) 1.0)

          
@@ 487,117 339,212 @@ inline transform-pos (p)
     v := (deref shglobals.view) * (vec4 p 1)
     v.xyz
 
-inout normal : vec3
-inout depthval : f32
-inout albedo : vec4
-inout matdata : vec4
-fn rasterize-vert ()
-    let index = ((deref gl_InstanceID) as u32)
-    if (index < buf-cells-in.count)
-        let vertex-index = ((deref gl_VertexID) as u32)
-        let key = (deref (buf-cells-in.entries @ index))
-        tetidx := (key & 7:u32)
-        signs := (key >> 28:u32)
-        key := (key >> 3:u32) & 0x1ffffff
+uniform world-out : (image3D r32f)
+    binding = BINDING_IMG_WORLD_OUT
+    \ coherent readonly restrict
+
+uniform smp-world : sampler3D
+    location = UNIFORM_WORLD_SAMPLER
+
+uniform zbuffer-inout : (image2D r32f)
+    binding = BINDING_IMG_ZBUFFER
+    \ coherent restrict
+
+fn generate-world ()
+    local_size GROUP_SIZE GROUP_SIZE GROUP_SIZE
+    ipos := (uvec3 gl_GlobalInvocationID.xyz)
+    #if (any? (ipos >= WORLD_SIZE))
+        return;
+    rd := (2.0 / (vec3 WORLD_SIZE))
+    fpos := (vec3 ipos) * rd - 1.0
+    let density =
+        static-if false
+            pos := fpos * WORLD_SCALE
+            mapf pos
+        else
+            local bits = 0
+            for z in (range 4:u32)
+                for y in (range 4:u32)
+                    for x in (range 4:u32)
+                        pos := (fpos + ((vec3 x y z) / 4) * rd) * WORLD_SCALE
+                        if ((mapf pos) <= 0.0)
+                            bits += 1
+            bits as f32 / 64.0
+    imageStore world-out ipos (vec4 density 0 0 0)
+    ;
+
+inline mapf (p)
+    d := (textureLod smp-world ((p / WORLD_SCALE) * 0.5 + 0.5) 0) . r
+    1.0 - d * 2.0
+    #d
+inline matmapf (p)
+    sdmDist (mapf p)
+        sdMaterial
+            vec4 0.5 0.3 1.0 1.0
+fn normalmapf (p r)
+    r := (0.25 / WORLD_SIZE.x) * WORLD_SCALE
+    - (sdNormalFast mapf p r)
 
-        let level = (((deref u-level) as u32) - 1)
-        let r = (/ (f32 (1:u32 << level)))
-        let d = (2.0 * r)
-        ucoord := (unpack-morton3x10 key)
-        coord := (vec3 ucoord) * d - 1.0
+fn generate-verts ()
+    local_size GROUP_SIZE GROUP_SIZE GROUP_SIZE
+    ipos := (uvec3 gl_GlobalInvocationID.xyz)
+    if (any? (ipos >= CUBE_SIZE))
+        return;
+
+    z := ipos.z / CUBE_SIZE.z
+    zbpos := (ivec2 ipos.xy)
+    static-if OCCLUSION_CULLING
+        let imgz = (deref ((imageLoad zbuffer-inout zbpos) . r))
+        if (z > imgz)
+            return;
+
+    d := (2 / (vec3 CUBE_SIZE))
+    coord := (vec3 ipos) * d - 1
+
+    center := (coord.xy + d.xy / 2)
+    flipped? := ((center.x * center.y) >= 0.0)
+    #if (not flipped?)
+        return;
+    flipmask := (? flipped? 1 0)
+    flipsign := (? flipped? -1.0 1.0)
 
+    local cp : (array vec3 8)
+    local cd : (array f32 8)
+    local mind = inf
+    local maxd = -inf
+    for i in (range 8)
+        k := i ^ flipmask
+        pos := (coord + ((vec3 ((uvec3 k (k >> 1:u32) (k >> 2:u32)) & 1:u32)) * d))
+        tpos := (map_vertex pos)
+        d := (mapf (map-translation tpos))
+        cp @ i =
+            do
+                static-if PROJECT_FINAL_VERTEX
+                    static-if POST_TRANSFORM pos
+                    else tpos
+                else pos
+        cd @ i = d * flipsign
+        mind = (min mind d)
+        maxd = (max maxd d)
+        ;
+    if ((mind * maxd) >= 0.0) # all values have same sign, no isosurface inbetween
+        static-if OCCLUSION_CULLING
+            if (maxd < 0.0) # full occlusion
+                imageStore zbuffer-inout (ivec2 zbpos) (vec4 z 0 0 0)
+        return;
+    for tetidx in (range 6:u32)
         let tetverts = 0x6cc99:u32
         k := tetidx * 3:u32
         k1 := (tetverts >> k) & 7:u32
         k3 := (tetverts >> (k + 3:u32)) & 7:u32
 
-        xycenter := (coord.xy + (vec2 (d * 0.5)))
+        let idxs = (ivec4 0 k1 7 k3)
 
         local p : (array vec3 4)
-        p @ 0 = coord
-        p @ 1 = coord + ((vec3 ((uvec3 k1 (k1 >> 1:u32) (k1 >> 2:u32)) & 1:u32)) * d)
-        p @ 2 = coord + (vec3 d d d)
-        p @ 3 = coord + ((vec3 ((uvec3 k3 (k3 >> 1:u32) (k3 >> 2:u32)) & 1:u32)) * d)
-
-        let d =
-            vec4
-                ? ((signs & 1:u32) == 1:u32) -1.0 1.0
-                ? ((signs & 2:u32) == 2:u32) -1.0 1.0
-                ? ((signs & 4:u32) == 4:u32) -1.0 1.0
-                ? ((signs & 8:u32) == 8:u32) -1.0 1.0
-
-        if ((xycenter.x * xycenter.y) >= 0.0)
-            # flip
-            return;
+        p @ 0 = cp @ 0
+        p @ 1 = cp @ k1
+        p @ 2 = cp @ 7
+        p @ 3 = cp @ k3
+        let d = (vec4 (cd @ 0) (cd @ k1) (cd @ 7) (cd @ k3))
 
         let c i = (tetfaces d)
-        let shift = ((((c - 1) << 2) | vertex-index) * 2)
-        let i0 i1 =
-            (0x5000 >> shift) & 3
-            (0xeef9 >> shift) & 3
-        let i0 i1 = (deref (i @ i0)) (deref (i @ i1))
-        let dist0 dist1 =
-            mapf (map_vertex (deref (p @ i0)))
-            mapf (map_vertex (deref (p @ i1)))
-        let d0 d1 = (dist0 as f32) (dist1 as f32)
-        let l = (tetlerp d0 d1)
-        let coord = (mix (deref (p @ i0)) (deref (p @ i1)) l)
-        #let coord = (deref (p @ vertex-index))
-        let dist = (matmapf (map_vertex coord))
-        let material =
-            dist.material
-        #let dist = dist0
-        #let material =
-            'mix dist0.material dist1.material l
-        let n =
-            normalmapf (map_vertex coord) (r * 0.5)
+        if (c == 0:u32)
+            continue;
+        let vc = (c * 3)
+        let ofs = (atomicAdd vertex-out.count vc)
+        if ((ofs + vc) > MAX_VERTICES)
+            return;
+        entries := vertex-out.entries
+
+        inline tf (i0 i1)
+            let p =
+                mix (p @ i0) (p @ i1) (tetlerp (d @ i0) (d @ i1))
+            static-if (PROJECT_FINAL_VERTEX & POST_TRANSFORM) (map_vertex p)
+            else p
+
 
-        # rotate it a little
-        #embed
-            let a = ((deref shglobals.time) * 0.2)
-            let c s = (cos a) (sin a)
+        if (c == 1:u32)
+            # generate triangle
+            entries @ (ofs + 0) . xyz = (tf i.x i.y)
+            entries @ (ofs + 1) . xyz = (tf i.x i.z)
+            entries @ (ofs + 2) . xyz = (tf i.x i.w)
+        elseif (c == 2:u32)
+            p0 := (tf i.x i.z)
+            p2 := (tf i.y i.w)
+            # generate quad
+            entries @ (ofs + 0) . xyz = p0
+            entries @ (ofs + 1) . xyz = (tf i.x i.w)
+            entries @ (ofs + 2) . xyz = p2
+            entries @ (ofs + 3) . xyz = p2
+            entries @ (ofs + 4) . xyz = (tf i.y i.z)
+            entries @ (ofs + 5) . xyz = p0
 
-            n :=
-                vec3
-                    c * n.x - s * n.z
-                    n.y
-                    s * n.x + c * n.z
+    ;
+
+inout normal : vec3
+inout depthval : f32
+inout albedo : vec4
+inout matdata : vec4
+fn rasterize-vert ()
+    let vertex-index = ((deref gl_VertexID) as u32)
+    let coord = (vec3 (vertex-in.entries @ vertex-index . xyz))
+
+    let tcoord =
+        static-if PROJECT_FINAL_VERTEX (map-translation coord)
+        else (map_vertex coord)
 
-            coord :=
-                vec3
-                    c * coord.x - s * coord.z
-                    coord.y
-                    s * coord.x + c * coord.z
+    let dist = (matmapf tcoord)
+    let material =
+        dist.material
+    #let dist = dist0
+    #let material =
+        'mix dist0.material dist1.material l
+    let n =
+        normalmapf tcoord 0.001 # (r * 0.5)
 
-        #coord :=
-            coord + (vec3 0 0 1)
-        #n := (transform-dist n)
+    # rotate it a little
+    #embed
+        let a = ((deref shglobals.time) * 0.2)
+        let c s = (cos a) (sin a)
 
-        let coord =
-            if PROJECT_FINAL_VERTEX
-                map_vertex coord
-            else coord
-        coord := (transform-pos coord)
+        n :=
+            vec3
+                c * n.x - s * n.z
+                n.y
+                s * n.x + c * n.z
+
+        coord :=
+            vec3
+                c * coord.x - s * coord.z
+                coord.y
+                s * coord.x + c * coord.z
 
-        let proj =
-            calc-projection;
+    #coord :=
+        coord + (vec3 0 0 1)
+    #n := (transform-dist n)
+
+    #if PROJECT_FINAL_VERTEX
 
-        let pcoord =
-            'project proj
-                vec4 coord 1.0
+    let coord =
+        static-if SAMPLE_CAMERA_OFFSET (transform-dist coord)
+        else (transform-pos coord)
+
+    let proj =
+        calc-projection;
 
-        normal.out =
-            do
-                static-if VISUALIZE_IDS (vec3hash (key as f32))
-                else n
-        depthval.out = coord.z
-        albedo.out = material.albedo
-        matdata.out =
-            vec4 material.roughness material.metallic 0 0
-        gl_Position = pcoord
-        return;
+    let pcoord =
+        'project proj
+            vec4 coord 1.0
 
-    gl_Position = (vec4 0 0 0 inf)
+    normal.out =
+        do
+            static-if VISUALIZE_IDS ((vec3hash (vertex-index as f32)) * 2.0 - 1.0)
+            else n
+    depthval.out = coord.z
+    albedo.out = material.albedo
+    matdata.out =
+        vec4 material.roughness material.metallic 0 0
+    gl_Position = pcoord
     ;
 
 fn pack-surfel-data (normal depth color matdata)

          
@@ 636,9 583,6 @@ fn rasterize-frag ()
     binding = IMAGE_TARGET_RGBA32F
     \ coherent writeonly restrict
 
-uniform u-level : i32
-    location = UNIFORM_LEVEL
-
 fn mixdown (uv)
     #let t = (deref shglobals.time)
     let size =

          
@@ 707,17 651,23 @@ fn visualize-buffer (uv)
     let col =
         texelFetch smp-screen uv 0
     let normal depth color matdata = (unpack-surfel-data col)
+    let fog-color = (vec4 0.3 0.5 1 1)
+
     if (depth == 0.0)
-        return
-            vec4 0 0 0 1
+        return fog-color
 
-    return
+    let col =
         vec4
             normal * 0.5 + 0.5
             #normhue depth
             #normhue (radius / 16.0)
             #color
-            1.0
+            0.0
+    return
+        static-if FOG
+            mix col fog-color
+                1.0 - (exp2 (-depth * FOG_RATE))
+        else col
 
 fn shader (uv)
     #mixdown uv

          
@@ 748,22 698,6 @@ inline main ()
         in: indirect draw call argument
         out: rasterized voxel cubes
 
-    let NUM_BUFFERS = 5
-
-    global cell_buffers =
-        arrayof GL.uint
-            GL.Buffer;
-            GL.Buffer;
-            GL.Buffer;
-            GL.Buffer;
-            GL.Buffer;
-    let cell_buffer_sz = ((sizeof u32) * (1 + MAX_VOXELS))
-    for i in (range (NUM_BUFFERS as u32))
-        let buf = (cell_buffers @ i)
-        GL.NamedBufferData buf (i32 cell_buffer_sz) null GL.STREAM_COPY
-        GL.BindBufferRange GL.SHADER_STORAGE_BUFFER (BINDING_BUF_CELLS_IN + i)
-            \ buf 0:i64 (i64 cell_buffer_sz)
-
     #global draw_voxels_cmd = (GL.CreateBuffer)
     #setup-ssbo draw_voxels_cmd buf-draw-voxels-cmd
     #let draw_voxels_cmd_sz = (sizeof DrawElementsIndirectCommand)

          
@@ 798,102 732,112 @@ inline main ()
             fragment = rasterize-frag
             #debug = true
 
-    global pg-supershader = (GL.Program)
-    call
-        attach-shaders (deref pg-supershader)
-            compute = supershader
-            #debug = true
-
     global rg : (Option RG)
 
     fn per-frame-setup (size pg-test frame)
         let rg =
             'force-unwrap rg
-        from (methodsof rg) let static program compute-program
+        from (methodsof rg) let static program compute-program indirect-draw-arrays-setup
 
         GL.BindTextureUnit 0 fb-scene-color
         GL.Uniform smp-screen 0
 
-        for i in (range NUM_BUFFERS)
-            let buf = (cell_buffers @ i)
-            let ptr =
-                GL.MapNamedBufferRange buf 0 (sizeof u32)
-                    | GL.MAP_WRITE_BIT
-                        GL.MAP_INVALIDATE_BUFFER_BIT
-                        #GL.MAP_UNSYNCHRONIZED_BIT
-            let ptr = (bitcast ptr (mutable pointer CellVals))
-            ptr.count = 0:u32
-            GL.UnmapNamedBuffer buf
-
-        #local cmd = (DrawElementsIndirectCommand)
-        #bind-ssbo draw_voxels_cmd buf-draw-voxels-cmd &cmd
-        #GL.NamedBufferSubData draw_voxels_cmd 0 draw_voxels_cmd_sz &cmd
+        let world =
+            static GL.Texture
+                inline ()
+                    let tex = (GL.Texture GL.TEXTURE_3D)
+                    'setup tex
+                        size = (ivec3 WORLD_SIZE)
+                        format = GL.R32F
+                        lod = true
+                        min-filter = GL.LINEAR_MIPMAP_LINEAR
+                        mag-filter = GL.LINEAR
+                    tex
 
-        inline bind-buffers (i0 i1)
-            GL.BindBufferRange GL.SHADER_STORAGE_BUFFER
-                BINDING_BUF_CELLS_IN
-                cell_buffers @ i0
-                \ 0:i64 (i64 cell_buffer_sz)
-            GL.BindBufferRange GL.SHADER_STORAGE_BUFFER
-                BINDING_BUF_CELLS_OUT
-                cell_buffers @ i1
-                \ 0:i64 (i64 cell_buffer_sz)
+        if (frame == 0)
+            # generate world
+            let pg-genworld = (compute-program generate-world)
+            GL.UseProgram pg-genworld
+            GL.BindImageTexture BINDING_IMG_WORLD_OUT world 0 GL.TRUE 0
+                GL.READ_ONLY
+                GL.R32F
+            GL.DispatchCompute (unpack ((WORLD_SIZE + GROUP_SIZE - 1) // GROUP_SIZE))
+            GL.MemoryBarrier GL.TEXTURE_FETCH_BARRIER_BIT
+            GL.GenerateTextureMipmap world
 
-        GL.UseProgram pg-supershader
+        let vertex_buffer_sz = ((sizeof Vertices) + (sizeof vec3) * MAX_VERTICES)
+        let vertex_buffer =
+            static GL.Buffer
+                inline ()
+                    let buf = (GL.Buffer)
+                    GL.NamedBufferData buf (i32 vertex_buffer_sz) null GL.STREAM_COPY
+                    buf
 
-        do
-            GL.Uniform u-program ProgramVoxelizeInit
-            GL.Uniform u-level 6
-            bind-buffers 0 1
-            GL.DispatchCompute ((8 ** 3) as u32) 1 1
-            GL.MemoryBarrier GL.SHADER_STORAGE_BARRIER_BIT
-            #GL.MemoryBarrier (GL.ALL_BARRIER_BITS as u32)
+        if true #(frame == 0)
+            do
+                # clear vertex buffer count
+                let ptr =
+                    GL.MapNamedBufferRange vertex_buffer 0 (sizeof u32)
+                        | GL.MAP_WRITE_BIT
+                            GL.MAP_INVALIDATE_BUFFER_BIT
+                            #GL.MAP_UNSYNCHRONIZED_BIT
+                let ptr = (bitcast ptr (mutable pointer Vertices))
+                ptr.count = 0:u32
+                GL.UnmapNamedBuffer vertex_buffer
 
-        #do
-            GL.UseProgram pg-voxelize
-            GL.Uniform u-level 7
-            bind-buffers 1 2
-            GL.DispatchCompute 84 1 1
-            GL.MemoryBarrier GL.SHADER_STORAGE_BARRIER_BIT
-            #GL.MemoryBarrier (GL.ALL_BARRIER_BITS as u32)
+            static-if OCCLUSION_CULLING
+                let zbuffer =
+                    static GL.Texture
+                        inline ()
+                            let tex = (GL.Texture GL.TEXTURE_2D)
+                            'setup tex
+                                size = (ivec2 CUBE_SIZE.xy)
+                                format = GL.R32F
+                            tex
+                do
+                    local clearval = 1.0
+                    GL.ClearTexImage zbuffer 0 GL.RED GL.FLOAT &clearval
 
-        GL.Uniform u-program ProgramVoxelize
-
-        #do
-            GL.Uniform u-level 6
-            bind-buffers 1 2
-            #GL.DispatchCompute 21 1 1
-            #GL.DispatchCompute 346 1 1
-            GL.DispatchCompute 594 1 1
-            GL.MemoryBarrier GL.SHADER_STORAGE_BARRIER_BIT
-            #GL.MemoryBarrier (GL.ALL_BARRIER_BITS as u32)
+                GL.BindImageTexture BINDING_IMG_ZBUFFER zbuffer 0 GL.FALSE 0
+                    GL.READ_WRITE
+                    GL.R32F
+            GL.BindBufferRange GL.SHADER_STORAGE_BUFFER
+                BINDING_BUF_VERTEX_OUT
+                vertex_buffer
+                \ 0:i64 (i64 vertex_buffer_sz)
 
-        do
-            GL.Uniform u-level 7
-            bind-buffers 1 2
-            #GL.DispatchCompute 84 1 1
-            #GL.DispatchCompute 1395 1 1
-            GL.DispatchCompute ((POINTLIMIT // 15) // 64) 1 1
+            let pg-gen = (compute-program generate-verts)
+            GL.UseProgram pg-gen
+            GL.BindTextureUnit 1 world
+            GL.Uniform smp-world 1
+            GL.DispatchCompute (unpack ((CUBE_SIZE + GROUP_SIZE - 1) // GROUP_SIZE))
             GL.MemoryBarrier GL.SHADER_STORAGE_BARRIER_BIT
-            #GL.MemoryBarrier (GL.ALL_BARRIER_BITS as u32)
+
+        inline print-in-count ()
+            let ptr =
+                GL.MapNamedBufferRange vertex_buffer 0 (sizeof u32)
+                    GL.MAP_READ_BIT
+            let ptr = (bitcast ptr (pointer Vertices))
+            print (ptr.count / 3) "triangles"
+            GL.UnmapNamedBuffer vertex_buffer
 
-        do
-            GL.Uniform u-program ProgramSimplify
-            GL.Uniform u-level 8
-            bind-buffers 2 3
-            GL.DispatchCompute ((POINTLIMIT // 3) // 64) 1 1
-            GL.MemoryBarrier GL.SHADER_STORAGE_BARRIER_BIT
-            #GL.MemoryBarrier (GL.ALL_BARRIER_BITS as u32)
+        if ((frame % 60) == 0)
+            print-in-count;
 
-        #do
-            for i in (range NUM_BUFFERS)
-                let buf = (cell_buffers @ i)
-                let ptr =
-                    GL.MapNamedBufferRange buf 0 (sizeof u32)
-                        GL.MAP_READ_BIT
-                let ptr = (bitcast ptr (pointer CellVals))
-                print i "=" ptr.count "/" ((ptr.count + 63:u32) // 64:u32)
-                GL.UnmapNamedBuffer buf
+        vvv bind setup-draw-arrays exec-draw-arrays
+        indirect-draw-arrays-setup
+            inline ()
+                _
+                    deref vertex-in.count
+                    1
+                    0
+                    0
+
+        GL.BindBufferRange GL.SHADER_STORAGE_BUFFER
+            BINDING_BUF_VERTEX_IN
+            vertex_buffer
+            \ 0:i64 (i64 vertex_buffer_sz)
+        setup-draw-arrays;
 
         do
             GL.BindFramebuffer GL.FRAMEBUFFER fb-scene

          
@@ 912,12 856,10 @@ inline main ()
                     GL.STENCIL_BUFFER_BIT
 
             GL.UseProgram pg-rasterize
-            bind-buffers 3 0
-            GL.Uniform u-level 8
+            GL.BindTextureUnit 1 world
+            GL.Uniform smp-world 1
             GL.BindVertexArray vao-empty
-            #GL.DrawArrays GL.POINTS 0 POINTLIMIT
-            #GL.DrawArraysInstanced GL.POINTS 0 1 POINTLIMIT
-            GL.DrawArraysInstanced GL.TRIANGLE_STRIP 0 4 POINTLIMIT
+            exec-draw-arrays GL.TRIANGLES
 
             GL.Disable GL.DEPTH_TEST
             GL.Disable GL.CULL_FACE