|
| 1 | +/* |
| 2 | + * Copyright LWJGL. All rights reserved. |
| 3 | + * License terms: http://lwjgl.org/license.php |
| 4 | + */ |
| 5 | +#version 430 core |
| 6 | +#if GL_NV_gpu_shader5 |
| 7 | +#extension GL_NV_gpu_shader5 : enable |
| 8 | +#elif GL_AMD_gpu_shader_int16 |
| 9 | +#extension GL_AMD_gpu_shader_int16 : enable |
| 10 | +#endif |
| 11 | + |
| 12 | +#define NO_AXIS uint(-1u) |
| 13 | +#define NO_NODE uint(-1u) |
| 14 | +#define MAX_DESCEND 200u |
| 15 | +#define MAX_ROPES 100u |
| 16 | +#define SKY_COLOR vec3(0.96, 0.98, 1.0) |
| 17 | + |
| 18 | +#define SIDE_Y_NEG 2u |
| 19 | +#define SIDE_Z_NEG 4u |
| 20 | + |
| 21 | +layout(binding = 0, rgba8) writeonly uniform image2D framebufferImage; |
| 22 | +uniform vec3 cam[5]; |
| 23 | +uniform ivec2 off = ivec2(0,0), cbwidth = ivec2(1,1); |
| 24 | +uniform int scale = 1; |
| 25 | +uniform uint startNodeIdx = 0u; |
| 26 | +layout(location = 0) uniform sampler2D tex; |
| 27 | + |
| 28 | +struct node { |
| 29 | + uvec4 rightOrLeaf_SplitAxis_SplitPos_GlobalOffset; |
| 30 | + uint ropes[6]; |
| 31 | +}; |
| 32 | +layout(std430, binding = 0) readonly restrict buffer Nodes { |
| 33 | + node[] nodes; |
| 34 | +}; |
| 35 | + |
| 36 | +struct nodegeom { |
| 37 | + u8vec3 min, max; |
| 38 | +}; |
| 39 | +layout(std430, binding = 1) readonly restrict buffer NodeGeoms { |
| 40 | + nodegeom[] nodegeoms; |
| 41 | +}; |
| 42 | + |
| 43 | +struct leafnode { |
| 44 | + uvec2 voxels; |
| 45 | +}; |
| 46 | +layout(std430, binding = 2) readonly restrict buffer LeafNodes { |
| 47 | + leafnode[] leafnodes; |
| 48 | +}; |
| 49 | + |
| 50 | +layout(std430, binding = 3) readonly restrict buffer Voxels { |
| 51 | + u8vec4[] voxels; |
| 52 | +}; |
| 53 | + |
| 54 | +const bool intersectGrass(const in vec3 origin, const in vec3 dir, const in vec3 bmin, const in vec3 bmax, |
| 55 | + inout float t, out vec2 uv) { |
| 56 | + vec3 p0l0 = (bmin + bmax) * 0.5 - origin; |
| 57 | + float tf = (p0l0.x + p0l0.z) / (dir.x + dir.z), tf2 = (p0l0.z - p0l0.x) / (dir.z - dir.x); |
| 58 | + vec3 p1 = origin + tf * dir, p2 = origin + tf2 * dir; |
| 59 | + tf = mix(t, tf, all(lessThanEqual(p1, bmax)) && all(greaterThanEqual(p1, bmin)) && tf >= 0.0 && tf < t); |
| 60 | + tf2 = mix(t, tf2, all(lessThanEqual(p2, bmax)) && all(greaterThanEqual(p2, bmin)) && tf2 >= 0.0 && tf2 < t); |
| 61 | + vec2 uv1 = vec2(p1.z - bmin.z, 1.0 - p1.y + bmin.y), uv2 = vec2(p2.z - bmin.z, 1.0 - p2.y + bmin.y); |
| 62 | + bool hit1 = tf < t && texture(tex, uv1).a > 0.8, hit2 = tf2 < t && texture(tex, uv2).a > 0.8; |
| 63 | + if (hit1 || hit2) { |
| 64 | + uv = mix(uv2, uv1, hit1); |
| 65 | + t = min(tf, tf2); |
| 66 | + return true; |
| 67 | + } |
| 68 | + return false; |
| 69 | +} |
| 70 | + |
| 71 | +const bool intersectVoxels(const in vec3 origin, const in vec3 dir, const uint firstVoxel, |
| 72 | + const uint numVoxels, inout float t, out uint vindex, out vec2 uv) { |
| 73 | + bool hit = false; |
| 74 | + for (uint i = 0; i < numVoxels; i++) { |
| 75 | + const u8vec4 v = voxels[i + firstVoxel]; |
| 76 | + const vec3 vx = vec3(v.xyz)*scale; |
| 77 | + if (intersectGrass(origin, dir, vx, vx + vec3(scale), t, uv)) { |
| 78 | + hit = true; |
| 79 | + vindex = i + firstVoxel; |
| 80 | + } |
| 81 | + } |
| 82 | + return hit; |
| 83 | +} |
| 84 | + |
| 85 | +const float exitSide(const in vec3 origin, const in vec3 invdir, |
| 86 | + const in uvec3 boxMin, const in uvec3 boxMax, |
| 87 | + out uint exitSide) { |
| 88 | + const bvec3 lt = lessThan(invdir, vec3(0.0)); |
| 89 | + const vec3 tmax = (mix(vec3(boxMax)+vec3(1.0), vec3(boxMin), lt)*scale - origin) * invdir; |
| 90 | + const ivec3 signs = ivec3(lt); |
| 91 | + vec2 vals; |
| 92 | + vals = mix(vec2(tmax.y, SIDE_Y_NEG + signs.y), vec2(tmax.x, signs.x), tmax.y > tmax.x); |
| 93 | + vals = mix(vec2(tmax.z, SIDE_Z_NEG + signs.z), vals, tmax.z > vals.x); |
| 94 | + exitSide = uint(vals.y); |
| 95 | + return vals.x; |
| 96 | +} |
| 97 | + |
| 98 | +const vec2 intersectBox(const in vec3 origin, const in vec3 invdir, const in u8vec3 bmin, const in u8vec3 bmax) { |
| 99 | + const bvec3 lt = lessThan(invdir, vec3(0.0)); |
| 100 | + const vec3 m1 = (vec3(bmin)*scale - origin) * invdir, m2 = ((vec3(bmax) + vec3(1.0))*scale - origin) * invdir; |
| 101 | + const vec3 tmin = mix(m1, m2, lt), tmax = mix(m2, m1, lt); |
| 102 | + return vec2(max(max(tmin.x, tmin.y), tmin.z), min(min(tmax.x, tmax.y), tmax.z)); |
| 103 | +} |
| 104 | + |
| 105 | +struct scenehitinfo { |
| 106 | + float t; |
| 107 | + uint vindex; |
| 108 | + uint descends, ropes; |
| 109 | + vec2 uv; |
| 110 | +}; |
| 111 | +const bool intersectScene(in uint nodeIdx, const in vec3 origin, const in vec3 dir, |
| 112 | + const in vec3 invdir, out scenehitinfo shinfo) { |
| 113 | + node n = nodes[nodeIdx]; |
| 114 | + vec3 o = origin; |
| 115 | + nodegeom ng = nodegeoms[nodeIdx]; |
| 116 | + vec2 lambda = intersectBox(o, invdir, ng.min, ng.max); |
| 117 | + if (lambda.y < 0.0 || lambda.x > lambda.y) |
| 118 | + return false; |
| 119 | + lambda.x = max(lambda.x, 0.0); |
| 120 | + shinfo.descends = 0u; |
| 121 | + shinfo.ropes = 0u; |
| 122 | + shinfo.t = 1.0/0.0; |
| 123 | + shinfo.uv = vec2(0.0); |
| 124 | + uvec3 nmin = ng.min, nmax = ng.max; |
| 125 | + while (true) { |
| 126 | + vec3 pEntry = dir * lambda.x + o; |
| 127 | + while (n.rightOrLeaf_SplitAxis_SplitPos_GlobalOffset.y != NO_AXIS) { |
| 128 | + if (float(n.rightOrLeaf_SplitAxis_SplitPos_GlobalOffset.z)*scale <= pEntry[n.rightOrLeaf_SplitAxis_SplitPos_GlobalOffset.y]) { |
| 129 | + nodeIdx = n.rightOrLeaf_SplitAxis_SplitPos_GlobalOffset.x; |
| 130 | + nmin[n.rightOrLeaf_SplitAxis_SplitPos_GlobalOffset.y] = n.rightOrLeaf_SplitAxis_SplitPos_GlobalOffset.z; |
| 131 | + } else { |
| 132 | + nodeIdx = nodeIdx + 1u; |
| 133 | + nmax[n.rightOrLeaf_SplitAxis_SplitPos_GlobalOffset.y] = n.rightOrLeaf_SplitAxis_SplitPos_GlobalOffset.z - 1u; |
| 134 | + } |
| 135 | + n = nodes[nodeIdx]; |
| 136 | + if (shinfo.descends++ > MAX_DESCEND) |
| 137 | + return false; |
| 138 | + } |
| 139 | + if (n.rightOrLeaf_SplitAxis_SplitPos_GlobalOffset.x != NO_NODE) { |
| 140 | + leafnode ln = leafnodes[n.rightOrLeaf_SplitAxis_SplitPos_GlobalOffset.x]; |
| 141 | + if (intersectVoxels(o, dir, ln.voxels.x, ln.voxels.y, shinfo.t, shinfo.vindex, shinfo.uv)) |
| 142 | + return true; |
| 143 | + } |
| 144 | + uint exit; |
| 145 | + lambda.x = exitSide(o, invdir, nmin, nmax, exit); |
| 146 | + nodeIdx = n.ropes[exit]; |
| 147 | + if (nodeIdx == NO_NODE) |
| 148 | + return false; |
| 149 | + n = nodes[nodeIdx]; |
| 150 | + ng = nodegeoms[nodeIdx]; |
| 151 | + nmin = ng.min; |
| 152 | + nmax = ng.max; |
| 153 | + if (shinfo.ropes++ > MAX_ROPES) |
| 154 | + return false; |
| 155 | + o = origin; |
| 156 | + } |
| 157 | + return false; |
| 158 | +} |
| 159 | + |
| 160 | +layout (local_size_x = 8, local_size_y = 8) in; |
| 161 | +void main(void) { |
| 162 | + const ivec2 pix = ivec2(gl_GlobalInvocationID.xy) * cbwidth + off; |
| 163 | + const ivec2 size = imageSize(framebufferImage); |
| 164 | + if (any(greaterThanEqual(pix, size))) |
| 165 | + return; |
| 166 | + const vec2 p = (vec2(pix) + vec2(0.5)) / vec2(size); |
| 167 | + const vec3 dir = normalize(mix(mix(cam[1], cam[2], p.y), mix(cam[3], cam[4], p.y), p.x)); |
| 168 | + vec3 acc = vec3(0.0); |
| 169 | + vec3 att = vec3(1.0); |
| 170 | + scenehitinfo shinfo; |
| 171 | + if (intersectScene(startNodeIdx, cam[0], dir, 1.0/dir, shinfo)) { |
| 172 | + vec3 col = texture(tex, shinfo.uv).rgb; |
| 173 | + acc += col;// * 4.6 * vec3(float(shinfo.ropes) * 1.4E-2, float(shinfo.descends) * 4E-3, 0.0); |
| 174 | + } else { |
| 175 | + acc += SKY_COLOR; |
| 176 | + } |
| 177 | + imageStore(framebufferImage, pix, vec4(acc, 1.0)); |
| 178 | +} |
0 commit comments