#version 440 core in vec3 pos; in vec2 uv; in vec3 normal; out vec4 color; uniform sampler2D textureSampler; uniform usampler3D octreeSampler; layout(location = $width$) uniform int width; layout(location = $height$) uniform int height; layout(location = $angle$) uniform float angle; layout(location = $aspect$) uniform float aspect; layout(location = $lightDirection$) uniform vec3 lightDirection; layout(location = $cameraPosition$) uniform vec3 cameraPosition; //************************************ // Intersection test between ray and cube, also gives intersection points as p0 + tmin * ray and p0 + tmax * ray //************************************ bool rayCube(vec3 p0, vec3 ray, vec3 center, float extent, out float tmin, out float tmax, out uint minFace, out uint maxFace) { // Translate ray origin based on cube center p0 -= center; // Get t from ray and cube's plane equations and use it to get the intersection coordinates float t1 = -(dot(p0, vec3(1., 0., 0.)) - extent) / dot(ray, vec3(1., 0., 0.)); vec3 test1 = p0 + t1*ray; float t2 = -(dot(p0, vec3(-1., 0., 0.)) - extent) / dot(ray, vec3(-1., 0., 0.)); vec3 test2 = p0 + t2*ray; float t3 = -(dot(p0, vec3(0., 1., 0.)) - extent) / dot(ray, vec3(0., 1., 0.)); vec3 test3 = p0 + t3*ray; float t4 = -(dot(p0, vec3(0., -1., 0.)) - extent) / dot(ray, vec3(0., -1., 0.)); vec3 test4 = p0 + t4*ray; float t5 = -(dot(p0, vec3(0., 0., 1.)) - extent) / dot(ray, vec3(0., 0., 1.)); vec3 test5 = p0 + t5*ray; float t6 = -(dot(p0, vec3(0., 0., -1.)) - extent) / dot(ray, vec3(0., 0., -1.)); vec3 test6 = p0 + t6*ray; // Check if t was not negative and that the ray-plane intersection falls within the cube face if (t1 < 0. || any(greaterThanEqual(test1.yz, vec2(extent))) || any(lessThanEqual(test1.yz, vec2(-extent)))) t1 = 0.; if (t2 < 0. || any(greaterThanEqual(test2.yz, vec2(extent))) || any(lessThanEqual(test2.yz, vec2(-extent)))) t2 = 0.; if (t3 < 0. || any(greaterThanEqual(test3.xz, vec2(extent))) || any(lessThanEqual(test3.xz, vec2(-extent)))) t3 = 0.; if (t4 < 0. || any(greaterThanEqual(test4.xz, vec2(extent))) || any(lessThanEqual(test4.xz, vec2(-extent)))) t4 = 0.; if (t5 < 0. || any(greaterThanEqual(test5.xy, vec2(extent))) || any(lessThanEqual(test5.xy, vec2(-extent)))) t5 = 0.; if (t6 < 0. || any(greaterThanEqual(test6.xy, vec2(extent))) || any(lessThanEqual(test6.xy, vec2(-extent)))) t6 = 0.; // Initialize tmin and tmax values that define the two intersection points tmin = 9999999999.; tmax = 0.; // Use the lowest value of t that is not 0 for tmin if (t1 > 0. && t1 <= tmin) tmin = t1; if (t2 > 0. && t2 <= tmin) tmin = t2; if (t3 > 0. && t3 <= tmin) tmin = t3; if (t4 > 0. && t4 <= tmin) tmin = t4; if (t5 > 0. && t5 <= tmin) tmin = t5; if (t6 > 0. && t6 <= tmin) tmin = t6; // Use the highest value of t that is not 0 for tmax if (t1 > 0. && t1 >= tmax) tmax = t1; if (t2 > 0. && t2 >= tmax) tmax = t2; if (t3 > 0. && t3 >= tmax) tmax = t3; if (t4 > 0. && t4 >= tmax) tmax = t4; if (t5 > 0. && t5 >= tmax) tmax = t5; if (t6 > 0. && t6 >= tmax) tmax = t6; // If tmin = tmax, the ray origin is within the cube, so set tmin to 0 if (tmin == tmax) tmin = 0.; // Figure out which face the ray exits if (t1 == tmin) minFace = 1; if (t2 == tmin) minFace = 2; if (t3 == tmin) minFace = 3; if (t4 == tmin) minFace = 4; if (t5 == tmin) minFace = 5; if (t6 == tmin) minFace = 6; if (t1 == tmax) maxFace = 1; if (t2 == tmax) maxFace = 2; if (t3 == tmax) maxFace = 3; if (t4 == tmax) maxFace = 4; if (t5 == tmax) maxFace = 5; if (t6 == tmax) maxFace = 6; // If tmax is not 0, an intersection was found return tmax > 0.; } //************************************ // Sample the octree at a location, return if geometry is present here and return deepest level reached when traversing //************************************ uvec4 sampleOctree(vec3 loc) { ivec3 texSize = textureSize(octreeSampler, 0); // Set location in range (0,2^$max_level$) uint range = 1 << $max_level$; loc = range + range * loc / $extent$; uvec3 uLoc = uvec3(loc.x, loc.y, loc.z); uvec3 result = uvec3(0); // Traverse octree uint i = 0; for (; i <= $max_level$; ++i) { if (i == $max_level$) { // Leaf node was reached, fetch the full color: return uvec4(texelFetch(octreeSampler, ivec3(result), 0).rgb, i); } // Read the childmask (stored in "blue"): uint childMask = texelFetch(octreeSampler, ivec3(result), 0).b; // If the mask is empty, we got a child node if (childMask == 0) break; // Find the index of the child in which the sampleposition is located range = 1 << ($max_level$ - i); uint childIndex = ((uLoc.x & range) == range ? 1 : 0) + ((uLoc.y & range) == range ? 2 : 0) + ((uLoc.z & range) == range ? 4 : 0); uint mask = 1 << childIndex; if ((childMask & mask) == 0) // The child doesn't exist, even though this is not a leaf node break;//return vec4(255, 255, 0, float($max_level$ + 1)); // Based on this index and the childmask, find out what the index of the childpointer is uint childPointerIndex = 0; for(int j = 0; j < 8; j++) { uint mask = 1 << j; if ((childMask & mask) == mask) childPointerIndex++; if (j == childIndex) break; } // Find sample position in 3D texture corresponding to current node and location uvec3 samplePos = result; samplePos.x += childPointerIndex; if (samplePos.x >= texSize.x) { samplePos.x = samplePos.x % texSize.x; samplePos.y++; if (samplePos.y >= texSize.y) { samplePos.y = samplePos.y % texSize.y; samplePos.z++; } //return vec4(255, 255, 0, float($max_level$ + 1)); } result = texelFetch(octreeSampler, ivec3(samplePos), 0).rgb; // If not, this is not a leaf node, so result actually contains a pointer to the 3D location of the child // Multiply the location by two to go to next level //loc *= 2.; } // Return level reached, and the pointer found there? return uvec4(result, i); } void main() { uint maxLoop = 4096; // Set color using textures color = texture(textureSampler, uv); // Get ray and sample point vec3 ray = normalize(pos - cameraPosition); vec3 samplePoint = cameraPosition; float extent = $extent$; float tmin, tmax; uint minFace = 0; uint maxFace; uint crap; // Calculate the rayCube intersection with the root: float tmaxRoot, tminRoot; if (!rayCube(cameraPosition, ray, vec3(0.), extent, tminRoot, tmaxRoot, crap, crap)) return; float t = tminRoot; // Keep testing ray box intersection, but prevent endless looping int loop = 0; while (t < tmaxRoot && loop < maxLoop) { samplePoint = cameraPosition + t * ray + 0.001 * ray; // Sample octree at sample point and store reached level uvec4 result = sampleOctree(samplePoint); uint reached_level = result.w; // If the reached level is max_level + 1, we have reached geometry, so color the pixel and break from loop if (reached_level == $max_level$) { color.rgb = /*(loop / 120.) * (0. + (float(minFace) * 0.16666666)) **/ (vec3(result) / 255.); //color.rgb = samplePoint / $extent$; break; } // Set cube center to 0 and current extent to overall extent vec3 cubeCenter = vec3(0.); float currentExtent = extent; // Convert relative sample point to (-1,1) range for easier computations vec3 relativeSamplePoint = samplePoint / extent; // Loop through all reached levels for (int j = 0; j <= reached_level; ++j) { // At every level, compute the cube center of the node that was sampled at this level and the sample point relative to this node cubeCenter += sign(relativeSamplePoint) * .5 * currentExtent; relativeSamplePoint = sign(relativeSamplePoint) * (-1. + 2. * abs(relativeSamplePoint)); currentExtent *= .5; } // Use the obtained cube center to do another ray-box intersection test rayCube(cameraPosition, ray, cubeCenter, currentExtent, tmin, tmax, minFace, maxFace); t = tmax; ++loop; } if (loop == maxLoop) color.rgb = vec3(1, 0,0); // Discard pixels that are transparent if (color.a < 0.5) discard; }