#version 440 core #define $TreeType$ #define $PoolType$ #define $Material$ #define TEXTURE_BLOCK_SIZE $TextureBlockSize$ #define TEXTURE_SIZE 1024 #if defined(TT_UNIQUEINDEX) || defined(TT_UNIQUEINDEXSHIFT) || ($OctreeSamplerCount$ > 4) #extension GL_NV_gpu_shader5 : enable #define ENABLE_64_BIT #endif #if defined(TT_UNIQUEINDEX) || defined(TT_UNIQUEINDEXSHIFT) #define $TextureCompression$ #endif #if defined(TT_UNIQUEINDEX) || defined(TT_UNIQUEINDEXSHIFT) || defined(TT_HIERARCHICAL) || defined(TT_HIERARCHICALSHIFT) || defined(TT_ONLYMATERIAL) || defined(TT_BITTREES) #define MATERIAL_LIBRARY #endif #if defined(TT_UNIQUEINDEX) || defined(TT_UNIQUEINDEXSHIFT) || defined(TT_HIERARCHICAL) || defined(TT_HIERARCHICALSHIFT) || defined(TT_ONLYMATERIAL) || defined(TT_STANDARD) || defined(TT_BITTREES) #define EARLY_RAY_TERMINATION #endif #if defined(TT_COLORED) || defined(TT_HIERARCHICAL) || defined(TT_HIERARCHICALSHIFT) || defined(TT_BITTREES) || defined(TT_MULTIROOT) #define ADDITIONAL_NODE_INFO #endif #if defined(TT_UNIQUEINDEX) || defined(TT_UNIQUEINDEXSHIFT) #define ADDITIONAL_POINTER_INFO #endif #if defined(PT_ADAPTIVEDIRECT1) | defined(PT_ADAPTIVELOOKUP1) const uint pointerSizeMaskSize = 1; #define POINTERSIZEMASKSIZE 1 #elif defined(PT_ADAPTIVEDIRECT2) | defined(PT_ADAPTIVELOOKUP2) const uint pointerSizeMaskSize = 2; #define POINTERSIZEMASKSIZE 2 #endif #if defined(PT_ADAPTIVEDIRECT1) | defined(PT_ADAPTIVEDIRECT2) #define PT_ADAPTIVEDIRECT #elif defined(PT_ADAPTIVELOOKUP1) | defined(PT_ADAPTIVELOOKUP2) #define PT_ADAPTIVELOOKUP #endif #if defined(MT_NORMAL) | defined(MT_COLORNORMAL) | defined(MT_COLORNORMALREFLECTIVITY) #define NORMALSIZE $NormalSize$ #endif #ifdef TT_MULTIROOT #define TreesPerChannel (($BitsPerChannel$ / $BitsPerTree$) + ((($BitsPerChannel$ % $BitsPerTree$) == 0) ? 0 : 1)) #define RootPointersSize TreesPerChannel * 3 + 1 #endif #ifdef TT_BITTREES #define RootPointersSize $RootCount$ #endif #define AOEnabled $AOEnabled$ #define OFFSET_SIZES_PER_LEVEL 1 #define FIRST_OFFSET_STORED_IMPLICITLY 1 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 = $lightType$) uniform int lightType; layout(location = $cameraPosition$) uniform vec3 cameraPosition; layout(location = $reflections$) uniform bool reflections; uniform sampler2D textureSampler; uniform usampler3D octreeSamplers[$OctreeSamplerCount$]; #if ($OctreeSamplerCount$ * TEXTURE_BLOCK_SIZE <= 4096) #define ptype uint #define POINTER_BYTES 4 #else #define ptype uint64_t #define POINTER_BYTES 4 #endif // This should be a texture containing random directions as RGB values. uniform sampler2D randomSampler; #ifdef MATERIAL_LIBRARY uniform usampler2D materialTextureSampler; #endif #if defined(TT_UNIQUEINDEX) || defined(TT_UNIQUEINDEXSHIFT) // Samplers for the compressed textures that are used in Unique Index trees uniform usampler3D blockPointerSampler; uniform usampler3D blockSamplers[$BlockSamplerCount$]; #if ($BlockSamplerCount$ * TEXTURE_BLOCK_SIZE <= 4096) #define bptype uint #define BLOCK_POINTER_SIZE 4 #else #define bptype uint64_t #define BLOCK_POINTER_SIZE 8 #endif #endif #ifdef TT_ONLYMATERIAL // Sampler for the material texture uniform usampler3D materialOctreeSampler; #endif uniform uint renderDepth; const float invWidth = 1.0 / float(width); const float pi = 3.14159265359; const uint range = 1 << ($max_level$ + 1); const vec3 rootCenter = vec3(range) * 0.5; const float rootExtent = float(range) * 0.5; const float extent = $extent$; const float rangeF = float(range); const float scale = rangeF / (extent * 2.); const float offset = extent + 0.5 / scale; // Settings for AO: const float aoRayStepback = 2.0; const float aoMaxOcclusionDistance = 7.5 * scale; const float aoMaxSingleRayOcclusion = 1.0; const uint aoRaySteps = 200; const uint aoSampleCount = 10; const float aoCoeff = 3.0; // Settings for GI (Global Illumination/Color bleeding) const float giRaySkip = 2.0; const float giRayMaxDistance1 = 200.0 * scale; // Max distance to check if any surfaces are hit that could cause color bleeding, also used to determine the influence of the color bleed (first ray) const uint giRaySteps1 = 100; // Ray steps for first ray const uint giSampleCount = 10; // Number of samples to take for global illumination const uint giRayDepth = 9; const float giStrength = 4.0; const float environmentLight = 0.07; const float lightStrength = 2.0; //const float giCoeff = 5.0; // Samples on the unit sphere (useful for ambient occlusion) //const vec3 samples[20] = { // vec3(0.576112, -0.307923, 0.273497), vec3(-0.239062, 0.014342, -0.512609), // vec3(-0.578527, 0.692620, 0.009409), vec3(-0.496125, 0.306383, -0.508048), // vec3(-0.620389, 0.266880, 0.483354), vec3(-0.478377, 0.521577, 0.611179), // vec3(0.250791, 0.168587, -0.066709), vec3(0.936988, -0.118714, -0.235831), // vec3(0.310220, 0.289636, 0.046191), vec3(-0.604985, 0.514587, 0.180593), // vec3(0.253899, 0.730909, 0.587989), vec3(-0.097092, -0.047744, -0.110424), // vec3(-0.357070, 0.877361, 0.020189), vec3(0.550173, 0.220257, -0.754331), // vec3(0.574418, 0.637574, -0.445669), vec3(0.170666, -0.597387, -0.366459), // vec3(0.269777, 0.234300, -0.577843), vec3(-0.702647, -0.090661, -0.628000), // vec3(-0.798998, -0.013601, 0.191734),vec3(-0.229475, 0.020532, -0.938973) // }; const int primes[50] = { 2, 3, 5, 7, 11, 13, 17, 19, 23, 29, 31, 37, 41, 43, 47, 53, 59, 61, 67, 71, 73, 79, 83, 89, 97, 101, 103, 107, 109, 113, 127, 131, 137, 139, 149, 151, 157, 163, 167, 173, 179, 181, 191, 193, 197, 199, 211, 223, 227, 229}; in vec3 pos; in vec2 uv; in vec3 normal; layout (location = 0) out vec4 color; layout (location = 1) out vec3 light; layout (location = 2) out vec3 gi; layout (location = 3) out vec3 voxelPosition; // The stack used for ray-tracing ptype pointerStack[$max_level$ + 1]; vec3 positionStack[$max_level$ + 1]; struct Ray { vec3 p0; vec3 dir; vec3 invDir; vec3 p0invDir; vec3 dirSign; uvec3 dirSignNegMask; float coneCoeff; float coneBias; }; // Returned struct RayCastResult { int result; float time; ptype nodePointer; vec3 nodePosition; uint level; vec3 axis; ivec3 uLoc; uint loop; }; Ray getRay(vec3 p0, vec3 dir, float coneBias = 0) { dir = normalize(dir); vec3 dirSign = sign(dir); vec3 invDir = 1.0 / dir; return Ray( p0, dir, invDir, -p0 * invDir, dirSign, uvec3(-clamp(sign(dir), -1, 0)), invWidth * angle * sqrt(3), coneBias ); } vec3 sampleRay(Ray ray, float time) { return ray.dir * time + ray.p0; } //************************************ // Intersection test between ray and cube, also gives intersection points as p0 + tmin * ray and p0 + tmax * ray //************************************ bool rayCube(in Ray ray, in vec3 center, in float extent, out float tmin, out float tmax, out vec3 minAxis, out vec3 maxAxis) { // Translate ray origin based on cube center vec3 p0 = ray.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.dir, vec3(1., 0., 0.)); vec3 test1 = p0 + t1*ray.dir; float t2 = -(dot(p0, vec3(-1., 0., 0.)) - extent) / dot(ray.dir, vec3(-1., 0., 0.)); vec3 test2 = p0 + t2*ray.dir; float t3 = -(dot(p0, vec3(0., 1., 0.)) - extent) / dot(ray.dir, vec3(0., 1., 0.)); vec3 test3 = p0 + t3*ray.dir; float t4 = -(dot(p0, vec3(0., -1., 0.)) - extent) / dot(ray.dir, vec3(0., -1., 0.)); vec3 test4 = p0 + t4*ray.dir; float t5 = -(dot(p0, vec3(0., 0., 1.)) - extent) / dot(ray.dir, vec3(0., 0., 1.)); vec3 test5 = p0 + t5*ray.dir; float t6 = -(dot(p0, vec3(0., 0., -1.)) - extent) / dot(ray.dir, vec3(0., 0., -1.)); vec3 test6 = p0 + t6*ray.dir; // Check if t was not negative and that the ray-plane intersection falls within the cube face if (t1 < 0. || any(greaterThan(test1.yz, vec2(extent))) || any(lessThan(test1.yz, vec2(-extent)))) t1 = 0.; if (t2 < 0. || any(greaterThan(test2.yz, vec2(extent))) || any(lessThan(test2.yz, vec2(-extent)))) t2 = 0.; if (t3 < 0. || any(greaterThan(test3.xz, vec2(extent))) || any(lessThan(test3.xz, vec2(-extent)))) t3 = 0.; if (t4 < 0. || any(greaterThan(test4.xz, vec2(extent))) || any(lessThan(test4.xz, vec2(-extent)))) t4 = 0.; if (t5 < 0. || any(greaterThan(test5.xy, vec2(extent))) || any(lessThan(test5.xy, vec2(-extent)))) t5 = 0.; if (t6 < 0. || any(greaterThan(test6.xy, vec2(extent))) || any(lessThan(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; minAxis = vec3(1, 0, 0); } if (t2 > 0. && t2 < tmin) { tmin = t2; minAxis = vec3(1, 0, 0); } if (t3 > 0. && t3 < tmin) { tmin = t3; minAxis = vec3(0, 1, 0); } if (t4 > 0. && t4 < tmin) { tmin = t4; minAxis = vec3(0, 1, 0); } if (t5 > 0. && t5 < tmin) { tmin = t5; minAxis = vec3(0, 0, 1); } if (t6 > 0. && t6 < tmin) { tmin = t6; minAxis = vec3(0, 0, 1); } // Use the highest value of t that is not 0 for tmax if (t1 > 0. && t1 > tmax) { tmax = t1; maxAxis = vec3(1, 0, 0); } if (t2 > 0. && t2 > tmax) { tmax = t2; maxAxis = vec3(1, 0, 0); } if (t3 > 0. && t3 > tmax) { tmax = t3; maxAxis = vec3(0, 1, 0); } if (t4 > 0. && t4 > tmax) { tmax = t4; maxAxis = vec3(0, 1, 0); } if (t5 > 0. && t5 > tmax) { tmax = t5; maxAxis = vec3(0, 0, 1); } if (t6 > 0. && t6 > tmax) { tmax = t6; maxAxis = vec3(0, 0, 1); } // If tmin = tmax, the ray origin is within the cube, so set tmin to 0 if (tmin == tmax) tmin = 0.; // If tmax is not 0, an intersection was found return tmax > 0.; } // Assuming the ray hits the given cube, calculates the time at which it hits it void rayCubeExit(in Ray ray, in vec3 center, in float extent, out float t, out vec3 axis) { // Edges taking into account the direction of the ray vec3 voxelEdges = extent * ray.dirSign + center; // Calculate the times at which the planes that span on the edges of the cubes are hit vec3 axisTimes = ray.invDir * voxelEdges + ray.p0invDir; // The smallest of these times is side of the cube the ray hits (assuming it hits the cube) t = min(min(axisTimes.x, axisTimes.y), axisTimes.z); if (axisTimes.x == t) axis = vec3(1, 0, 0); else if (axisTimes.y == t) axis = vec3(0, 1, 0); else axis = vec3(0, 0, 1); //axis = vec3(equal(vec3(t), axisTimes)); } float rayCubeHitAxis(in Ray ray, in vec3 center, in float extent, in vec3 axis) { // Edges taking into account the direction of the ray vec3 voxelEdges = center - extent * ray.dirSign; vec3 axisTimes = ray.invDir * voxelEdges + ray.p0invDir; if (axis.x != 0) return axisTimes.x; else if (axis.y != 0) return axisTimes.y; else if (axis.z != 0) return axisTimes.z; } //uvec2 extractAdditionalNodeInfo(uint value) //{ // return uvec2((value & 0xFFF00000) >> 20, (value & 0x000FFF00) >> 8); //} ivec3 wrapPointer256(in uint pointer) { return ivec3(pointer & 0xFF, (pointer >> 8) & 0xFF, pointer >> 16); } ivec3 wrapPointer512(in uint pointer) { return ivec3(pointer & 0x1FF, (pointer >> 9) & 0x1FF, pointer >> 18); } ivec3 wrapPointer1024(in uint pointer) { return ivec3(pointer & 0x03FF, (pointer >> 10) & 0x03FF, pointer >> 20); } uint fetchSingleAt(in uint pointer, in usampler3D sampler) { return texelFetch(sampler, wrapPointer1024(pointer), 0).r; } uint fetchAt(in uint pointer, in uint valueSize, in usampler3D sampler) { uint res = 0; for (uint j = 0; j < valueSize; j++) res |= fetchSingleAt(pointer + j, sampler) << ((valueSize - j - 1) * 8); return res; } uint fetchPointerAt(in uint pointer, in uint valueSize, in usampler3D sampler) { // Since the pointer and result are both 32 bits, this is the same as fetchAt return fetchAt(pointer, valueSize, sampler); } // Mask should cover all bits smaller then some given value #define POINTER_MASK ((TEXTURE_SIZE * TEXTURE_SIZE * TEXTURE_BLOCK_SIZE) - 1) // This should be the log2 of the max texture size. findLSB given the log2 if we know that it is a power of 2 #define POINTER_SHIFT findLSB(TEXTURE_SIZE * TEXTURE_SIZE * TEXTURE_BLOCK_SIZE) uint fetchSingleAt(in uint pointer, in usampler3D samplers[$OctreeSamplerCount$] = octreeSamplers) { #if $OctreeSamplerCount$ == 1 return fetchSingleAt(pointer, samplers[0]); #else return fetchSingleAt(pointer & POINTER_MASK, samplers[pointer >> POINTER_SHIFT]); #endif } uint fetchAt(in uint pointer, in uint valueSize, in usampler3D samplers[$OctreeSamplerCount$] = octreeSamplers) { uint res = 0; for (uint j = 0; j < valueSize; j++) res |= fetchSingleAt(pointer + j, samplers) << ((valueSize - j - 1) * 8); return res; } uint fetchPointerAt(in uint pointer, in uint valueSize, in usampler3D samplers[$OctreeSamplerCount$] = octreeSamplers) { // Since the pointer and result are both 32 bits, this is the same as fetchAt return fetchAt(pointer, valueSize, samplers); } #if defined(TT_UNIQUEINDEX) || defined(TT_UNIQUEINDEXSHIFT) #if ($BlockSamplerCount$ != $OctreeSamplerCount$) uint fetchSingleAt(in uint pointer, in usampler3D samplers[$BlockSamplerCount$] = blockSamplers) { #if $BlockSamplerCount$ == 1 return fetchSingleAt(pointer, samplers[0]); #else return fetchSingleAt(pointer & POINTER_MASK, samplers[pointer >> POINTER_SHIFT]); #endif } uint fetchAt(in uint pointer, in uint valueSize, in usampler3D samplers[$BlockSamplerCount$] = blockSamplers) { uint res = 0; for (uint j = 0; j < valueSize; j++) res |= fetchSingleAt(pointer + j, samplers) << ((valueSize - j - 1) * 8); return res; } uint fetchPointerAt(in uint pointer, in uint valueSize, in usampler3D samplers[$BlockSamplerCount$] = blockSamplers) { // Since the pointer and result are both 32 bits, this is the same as fetchAt return fetchAt(pointer, valueSize, samplers); } #endif #endif #ifdef ENABLE_64_BIT uint64_t fetchBigAt(in uint pointer, in uint valueSize, in usampler3D sampler) { if (valueSize <= 4) return fetchAt(pointer, valueSize, sampler); uvec2 parts = uvec2( fetchAt(pointer + valueSize - 4, 4, sampler), fetchAt(pointer, valueSize - 4, sampler)); return packUint2x32(parts); } uint fetchSingleAt(in uint64_t pointer, in usampler3D samplers[$OctreeSamplerCount$] = octreeSamplers) { return fetchSingleAt(uint(pointer & POINTER_MASK), samplers[uint(pointer >> POINTER_SHIFT)]); } uint fetchAt(in uint64_t pointer, in uint valueSize, in usampler3D samplers[$OctreeSamplerCount$] = octreeSamplers) { uint res = 0; for (uint j = 0; j < valueSize; j++) res |= fetchSingleAt(pointer + j, samplers) << ((valueSize - j - 1) * 8); return res; } uint64_t fetchPointerAt(in uint64_t pointer, in uint valueSize, in usampler3D samplers[$OctreeSamplerCount$] = octreeSamplers) { if (valueSize <= 4) return fetchAt(pointer, valueSize, samplers); uvec2 parts = uvec2( fetchAt(pointer + valueSize - 4, 4, samplers), fetchAt(pointer, valueSize - 4, samplers)); return packUint2x32(parts); } uint64_t fetchBigAt(in uint64_t pointer, in uint valueSize, in usampler3D samplers[$OctreeSamplerCount$] = octreeSamplers) { return fetchPointerAt(pointer, valueSize, samplers); } #if ($BlockSamplerCount$ != $OctreeSamplerCount$) uint fetchSingleAt(in uint64_t pointer, in usampler3D samplers[$BlockSamplerCount$]) { return fetchSingleAt(uint(pointer & POINTER_MASK), samplers[uint(pointer >> POINTER_SHIFT)]); } //uint64_t fetchPointerAt(in uint64_t pointer, in uint valueSize, in usampler3D samplers[$BlockSamplerCount$]) //{ // uint64_t res = 0; // for (uint j = 0; j < valueSize; j++) // res |= uint64_t(fetchSingleAt(pointer + j, samplers)) << ((valueSize - j - 1) * 8); // return res; //} //uint fetchAt(in uint64_t pointer, in uint valueSize, in usampler3D samplers[$BlockSamplerCount$] ) //{ // uint res = 0; // for (uint j = 0; j < valueSize; j++) // res |= fetchSingleAt(pointer + j, samplers) << ((valueSize - j - 1) * 8); // return res; //} //uint64_t fetchBigAt(in uint64_t pointer, in uint valueSize, in usampler3D samplers[$BlockSamplerCount$]) //{ // return fetchPointerAt(pointer, valueSize, samplers); //} #endif #endif ivec3 wrapPointerFlexible(ptype index, uvec3 texSize) { return ivec3( index % texSize.x, (index / texSize.x) % texSize.y, index / (texSize.x * texSize.y)); } ptype fetchAtFlexible(in ptype pointer, in uint valueSize, in usampler3D sampler) { uvec3 texSize = uvec3(textureSize(sampler, 0)); uint res = 0; for (int j = 0; j < valueSize; j++) { uvec3 wrappedPointer = wrapPointerFlexible(pointer + j, texSize); res |= texelFetch(sampler, ivec3(wrappedPointer), 0).r << ((valueSize - j - 1) * 8); } return res; } //uvec3 extractPointer(in uint pointer) //{ // return uvec3((pointer & 0xFFE00000) >> 21, (pointer & 0x001FFC00) >> 10, pointer & 0x000003FF); //} uint getPointerSize(in uint level, in usampler3D samplers[$OctreeSamplerCount$] = octreeSamplers) { #if defined(PT_ORIGINAL) return 4; #elif defined(PT_STANDARD) | defined(PT_ADAPTIVELOOKUP) | defined(PT_ADAPTIVEDIRECT) | defined(PT_VIRTUALNODES) return texelFetch(samplers[0], wrapPointer1024(($max_level$ + 1) * 4 + level), 0).r; #endif } #if defined(PT_STANDARD) | defined(PT_ADAPTIVELOOKUP) | defined(PT_ADAPTIVEDIRECT) | defined(PT_VIRTUALNODES) ptype getLevelOffset(in uint level, in usampler3D samplers[$OctreeSamplerCount$] = octreeSamplers) { return fetchPointerAt(level * POINTER_BYTES, POINTER_BYTES, samplers); } #endif uint getHeaderSize() { #if defined(PT_STANDARD) return ($max_level$ + 1) * (POINTER_BYTES + 1); #elif defined(PT_VIRTUALNODES) return ($max_level$ + 1) * (POINTER_BYTES + 1) + 4; #elif defined(PT_ADAPTIVEDIRECT) return ($max_level$ + 1) * (POINTER_BYTES + 1) + 1; #elif defined(PT_ADAPTIVELOOKUP) return ($max_level$ + 1) * (POINTER_BYTES + 1) + 1 + (4 * $max_level$); // level offsets, pointer sizes, lookup table pointers #elif defined(PT_ORIGINAL) return 0; #endif } #if defined(TT_MULTIROOT) | defined(TT_BITTREES) ptype getRootPointer(uint rootIndex = 0) { ptype offset = getHeaderSize() + ($max_level$ + 1) * (0 #ifdef ADDITIONAL_NODE_INFO + 1 #endif #ifdef ADDITIONAL_POINTER_INFO + 1 #endif ); return fetchPointerAt(offset + (rootIndex * 2), 2); } #else ptype getRootPointer() { return #if defined(PT_STANDARD) | defined(PT_ADAPTIVELOOKUP) | defined(PT_ADAPTIVEDIRECT) | defined(PT_VIRTUALNODES) getLevelOffset(0); #elif defined(PT_ORIGINAL) #if defined(ADDITIONAL_NODE_INFO) && defined(ADDITIONAL_POINTER_INFO) ($max_level$ + 1) * 2; #elif defined(ADDITIONAL_NODE_INFO) | defined(ADDITIONAL_POINTER_INFO) $max_level$ + 1; #else 0; #endif #endif } #endif uint getAdditionalBytesPerNode(in uint level, in usampler3D samplers[$OctreeSamplerCount$] = octreeSamplers) { #if defined(ADDITIONAL_NODE_INFO) || defined(TT_ONLYMATERIAL) return fetchSingleAt(getHeaderSize() + level, samplers); #else return 0; #endif } uint getAdditionalBytesPerPointer(in uint level, in usampler3D samplers[$OctreeSamplerCount$] = octreeSamplers) { #if defined(ADDITIONAL_POINTER_INFO) #if OFFSET_SIZES_PER_LEVEL ptype offset = ptype(getHeaderSize()) #ifdef ADDITIONAL_NODE_INFO + ($max_level$ + 1) #endif ; return fetchSingleAt(offset + level, samplers); #else return 4; #endif #else return 0; #endif } bool hasChild(in uint childMask, in uint childIndex) { return (childMask & (1 << childIndex)) != 0; } ivec3 getNextULoc(in Ray ray, in float t, in ivec3 lastULoc, in vec3 hitAxis) { vec3 samplePoint = ray.p0 + t * ray.dir/* + 0.005 * ray.dirSign*/; vec3 hitDirMask = ray.dirSign * hitAxis; // Calculate the location of the first leaf node that the ray enters ivec3 nextULoc = ivec3(floor(samplePoint + hitDirMask)); return ivec3(ray.dirSign) * max(ivec3(ray.dirSign) * nextULoc, ivec3(ray.dirSign) * lastULoc); } uint getChildIndex(uvec3 uLoc, uint level) { uint range = 1 << ($max_level$ - level); return int((uLoc.x & range) == range) * 1 + int((uLoc.y & range) == range) * 2 + int((uLoc.z & range) == range) * 4; } //**************************************** // Gets the position and scale (extent) of the child voxel at the given index //**************************************** vec3 getChildPosition(vec3 parentPos, float childExtent, uint childIndex) { vec3 childOffset = vec3( ((childIndex & 1) == 1) ? childExtent : -childExtent, ((childIndex & 2) == 2) ? childExtent : -childExtent, ((childIndex & 4) == 4) ? childExtent : -childExtent); return parentPos + childOffset; } float getExtent(uint level) { return float(1 << ($max_level$ - level + 1)) * 0.5; } #ifdef PT_ADAPTIVELOOKUP ptype getLookupTablePointer(in uint level, in usampler3D samplers[$OctreeSamplerCount$] = octreeSamplers) { return fetchPointerAt(($max_level$ + 1) * (POINTER_BYTES + 1) + 1 + (level - 1) * 4, 4, samplers); } #endif #if defined(PT_ADAPTIVELOOKUP) | defined(PT_ADAPTIVEDIRECT) uint getMaskMask() { return (1 << pointerSizeMaskSize) - 1; } uint getPointerSizeDescriptor(in usampler3D samplers[$OctreeSamplerCount$] = octreeSamplers) { return fetchSingleAt(($max_level$ + 1) * (POINTER_BYTES + 1), samplers[0]); } uint getPointerOffset(in uint mask, in usampler3D samplers[$OctreeSamplerCount$] = octreeSamplers) { // Adaptive lookup 1: mask is only used to indicate that the pointer goes to the lookup table #if defined(PT_ADAPTIVELOOKUP1) return 0; // If the mask is 0, or 0x3 (for lookup tables) theere is no pointer offset, return 0. #elif defined(PT_ADAPTIVEDIRECT2) if (mask == 0) return 0; #elif defined(PT_ADAPTIVELOOKUP2) if (mask == 0 || mask == 0x3) return 0; #endif // Adaptive direct 1: mask "1" has an offset of the size of mask "0" uint pointerSizeDescr = getPointerSizeDescriptor(samplers); #if defined(PT_ADAPTIVEDIRECT1) if (mask == 0) return 0; else return 1 << (((pointerSizeDescr + 1) * 8) - 1); #endif #if defined(PT_ADAPTIVEDIRECT2) | defined(PT_ADAPTIVELOOKUP2) uint s1 = (0x03 & pointerSizeDescr) + 1; // 0x03 = 00000011 uint s2 = ((0x0c & pointerSizeDescr) >> 2) + 1; // 0x0c = 00001100 uint s3 = ((0x30 & pointerSizeDescr) >> 4) + 1; // 0x30 = 00110000 uint curOffset = 1 << (s1 * 8 - pointerSizeMaskSize); if (mask == 1) return curOffset; curOffset += 1 << (s2 * 8 - pointerSizeMaskSize); if (mask == 2) return curOffset; curOffset += 1 << (s3 * 8 - pointerSizeMaskSize); return curOffset; #endif } ptype getPointerToChildPointer(in ptype curNodePointer, in uint level, in int childPointerIndex, out uint mask, out uint curPointerSize, in usampler3D samplers[$OctreeSamplerCount$] = octreeSamplers) { uint pointerSize = getPointerSize(level + 1, samplers); ptype curIndex = curNodePointer + 1 + getAdditionalBytesPerNode(level, samplers); mask = 0; curPointerSize = 0; uint pointerSizeDescr = getPointerSizeDescriptor(samplers); uint s[(1 << POINTERSIZEMASKSIZE) - 1]; s[0] = (0x03 & pointerSizeDescr) + 1; // 0x03 = 00000011 #if (POINTERSIZEMASKSIZE == 2) s[1] = ((0x0c & pointerSizeDescr) >> 2) + 1; // 0x0c = 00001100 s[2] = ((0x30 & pointerSizeDescr) >> 4) + 1; // 0x30 = 00110000 #endif // Check the sizes of all pointers before the current one to determine the position of the current one for (int c = 0; c <= childPointerIndex; c++) { curIndex += curPointerSize; mask = fetchSingleAt(curIndex, samplers) >> 8 - pointerSizeMaskSize; if (mask == getMaskMask()) curPointerSize = pointerSize; else { #if (POINTERSIZEMASKSIZE == 1) curPointerSize = pointerSizeDescr + 1; #else curPointerSize = s[mask]; #endif } } return curIndex; } #endif #ifdef PT_VIRTUALNODES bool getLevelUsesVirtualNodes(in uint level, in usampler3D samplers[$OctreeSamplerCount$] = octreeSamplers) { uint levelsUsingVirtualNodesMask = fetchAt((POINTER_BYTES + 1) * ($max_level$ + 1), 4, samplers); return (levelsUsingVirtualNodesMask & (1 << level)) != 0; } uint getVirtualNodeSize(in uint level, in usampler3D samplers[$OctreeSamplerCount$] = octreeSamplers) { return getPointerSize(level - 1, samplers) + getAdditionalBytesPerPointer(level - 1, samplers); } uint getFullNodeSize(in uint level, in usampler3D samplers[$OctreeSamplerCount$] = octreeSamplers) { return 1 + getAdditionalBytesPerNode(level, samplers) + 1 + getPointerSize(level, samplers) + (level > 0 ? getAdditionalBytesPerPointer(level - 1, samplers) : 0); } uint getNormalNodeSize(in ptype nodePointer, in uint level, in usampler3D samplers[$OctreeSamplerCount$] = octreeSamplers) { uint childCount = bitCount(fetchSingleAt(nodePointer, samplers)); return 1 + getAdditionalBytesPerNode(level, samplers) + (getPointerSize(level, samplers) + getAdditionalBytesPerPointer(level, samplers)) * childCount + ((level > 0 && getLevelUsesVirtualNodes(level - 1)) ? getAdditionalBytesPerPointer(level - 1, samplers) : 0); } uint getSizeToSkip(in uint childMask, in uint vmask, in uint childIndex, in int childPointerIndex, in uint level, in ptype firstChildPointer, in usampler3D samplers[$OctreeSamplerCount$] = octreeSamplers) { uint virtualChildSize = getVirtualNodeSize(level + 1, samplers); uint sizeToSkip = 0; if (getLevelUsesVirtualNodes(level + 1)) { // If the next level uses virtual nodes, use the usual method to find the node pointer int virtualNodesToSkip = bitCount((vmask << 24) << (8 - childIndex)); int fullNodesToSkip = childPointerIndex - virtualNodesToSkip; sizeToSkip = virtualNodesToSkip * virtualChildSize + fullNodesToSkip * getFullNodeSize(level + 1, samplers); } else { // If it doesn't, find out how big the nodes that need to be skipped are for (uint c = 0; c < childIndex; c++) { if (hasChild(childMask, c)) { if (hasChild(vmask, c)) sizeToSkip += virtualChildSize; else sizeToSkip += getNormalNodeSize(firstChildPointer + sizeToSkip, level + 1, samplers); } } } return sizeToSkip; } #endif //************************************ // Given a pointer to a node, and it's childmask, and some child index // returns the nodePointer to the child at the given index. // Note that if the child doesn't exist, it still returns some pointer as if it would. //************************************ ptype getNodePointer(in ptype curNodePointer, in uint childMask, in uint childIndex, in uint level, in usampler3D samplers[$OctreeSamplerCount$] = octreeSamplers) { // Based on the childIndex and the childmask, find out what the index of the childpointer is // If one bitshift of size 32 is used, glsl uses the modulo of 32 (and thus a bitshift of 0 for childIndex 0 -> (32 - 0) % 32 = 0) int childPointerIndex = bitCount((childMask << 24) << (8 - childIndex)); //return childPointerIndex; #if defined(PT_STANDARD) uint pointerSize = getPointerSize(level, samplers); ptype nextLevelOffset = getLevelOffset(level + 1, samplers); // Fetch the pointer from the texture return nextLevelOffset + fetchPointerAt(curNodePointer + 1 + getAdditionalBytesPerNode(level) + childPointerIndex * pointerSize, pointerSize, samplers); #elif defined(PT_ADAPTIVELOOKUP) | defined(PT_ADAPTIVEDIRECT) uint pointerSize = getPointerSize(level + 1, samplers); ptype nextLevelOffset = getLevelOffset(level + 1, samplers); uint mask, curPointerSize; ptype curIndex = getPointerToChildPointer(curNodePointer, level, childPointerIndex, mask, curPointerSize, samplers); // Fetch the full pointer ptype fullPointer = fetchPointerAt(curIndex, curPointerSize, samplers) & (~(getMaskMask() << ((curPointerSize * 8) - pointerSizeMaskSize))); fullPointer += getPointerOffset(mask); #if defined(PT_ADAPTIVELOOKUP) if (mask == getMaskMask()) return nextLevelOffset + fullPointer; else { // Use the lookup table ptype lookupTablePointer = getLookupTablePointer(level + 1, samplers) + fullPointer * pointerSize; return nextLevelOffset + fetchPointerAt(lookupTablePointer, pointerSize, samplers); } #elif defined(PT_ADAPTIVEDIRECT) return nextLevelOffset + fullPointer; #endif #elif defined(PT_ORIGINAL) return fetchPointerAt(curNodePointer + 4 + childPointerIndex * 4, 4, samplers); #elif defined(PT_VIRTUALNODES) ptype nextLevelOffset = getLevelOffset(level + 1, samplers); uint pointerSize = getPointerSize(level, samplers); ptype pointer = nextLevelOffset; if (getLevelUsesVirtualNodes(level)) { uint vmask = fetchSingleAt(curNodePointer + 1 + getAdditionalBytesPerNode(level), samplers); // Add pointer to the first child pointer += fetchPointerAt(curNodePointer + 2 + getAdditionalBytesPerNode(level, samplers), pointerSize); //if(level == 1) return pointer; pointer += getSizeToSkip(childMask, vmask, childIndex, childPointerIndex, level, pointer, samplers); if (hasChild(vmask, childIndex)) pointer = nextLevelOffset + fetchPointerAt(pointer, getPointerSize(level), samplers); } else { pointer += fetchPointerAt(curNodePointer + 1 + getAdditionalBytesPerNode(level) + childPointerIndex * pointerSize, pointerSize, samplers); } return pointer; #endif } //#ifdef TT_ONLYMATERIAL //uint getMaterialPoolPointer(in uint curNodePointer, in uint childMask, in uint childIndex, in uint level) //{ // int childPointerIndex = bitCount(childMask << (32 - childIndex)); // #if defined(PT_STANDARD) // uint pointerSize = getPointerSize(level, materialOctreeSampler); // uint nextLevelOffset = getLevelOffset(level + 1, materialOctreeSampler); // // Fetch the pointer from the texture // return nextLevelOffset + fetchAt(curNodePointer + 1 + // getAdditionalBytesPerNode(level, materialOctreeSampler) + // (childPointerIndex - 1) * pointerSize, pointerSize, materialOctreeSampler); // #elif defined(PT_ORIGINAL) // return fetchAt(curNodePointer + 4 + getAdditionalBytesPerNode(level, materialOctreeSampler) + (childPointerIndex - 1) * 4, 4); // #endif //} //#endif // Fetches the childmask of the node at curNodePointer (note that this is not a mask for leaf nodes!) uint getChildMask(in ptype nodePointer, in usampler3D samplers[$OctreeSamplerCount$] = octreeSamplers) { return fetchSingleAt(nodePointer, samplers); } vec3 rainbow(float x) { //Target colors: Rainbow from red to yellow to green to blue purple? vec3 t1 = vec3(1.0, 0.0, 0.0); vec3 t2 = vec3(1.0, 0.5, 0.0); vec3 t3 = vec3(1.0, 1.0, 0.0); vec3 t4 = vec3(0.0, 0.5, 0.0); vec3 t5 = vec3(0.0, 0.0, 1.0); vec3 t6 = vec3(0.5, 0.0, 0.5); if (x < 0.2) return mix(t1, t2, (x / 0.2)); else if (x < 0.4) return mix(t2, t3, ((x - 0.2) / 0.2)); else if (x < 0.6) return mix(t3, t4, ((x - 0.4) / 0.2)); else if (x < 0.8) return mix(t4, t5, ((x - 0.6) / 0.2)); else return mix(t5, t6, ((x - 0.8) / 0.2)); } #if defined(TT_UNIQUEINDEX) || defined(TT_UNIQUEINDEXSHIFT) //************************************ // Read the shift from the nodepool //************************************ uint64_t getShift(in ptype curNodePointer, in uint childMask, in uint childIndex, in uint level) { // Based on the childIndex and the childmask, find out what the index of the childpointer is int childCount = bitCount(childMask); if (childCount == 1) return 0; // If it's the first child, the offset is always 0 int childPointerIndex = bitCount((childMask << 24) << (8 - childIndex)); #if FIRST_OFFSET_STORED_IMPLICITLY if (childPointerIndex == childCount - 1) return uint64_t(1); #endif uint shiftSize = getAdditionalBytesPerPointer(level); #if defined(PT_ORIGINAL) shiftSize += (4 - (shiftSize % 4)) % 4; #endif #if defined(PT_ORIGINAL) | defined(PT_STANDARD) | defined(PT_ADAPTIVELOOKUP) | defined(PT_ADAPTIVEDIRECT) #if defined(PT_ORIGINAL) | defined(PT_STANDARD) uint additionalNodeInfo = getAdditionalBytesPerNode(level); uint pointerSize = getPointerSize(level); ptype additionalPointerInfoStart = #if defined(PT_STANDARD) curNodePointer + 1 + additionalNodeInfo + childCount * pointerSize; #elif defined(PT_ORIGINAL) curNodePointer + 4 + additionalNodeInfo + childCount * pointerSize; #endif #elif defined(PT_ADAPTIVELOOKUP) | defined(PT_ADAPTIVEDIRECT) uint pointerSize, mask; uint additionalPointerInfoStart = getPointerToChildPointer(curNodePointer, level, childCount, mask, pointerSize); #endif additionalPointerInfoStart += childPointerIndex * shiftSize; #elif defined(PT_VIRTUALNODES) uint nextLevelOffset = getLevelOffset(level + 1); uint pointerSize = getPointerSize(level); uint additionalPointerInfoStart = nextLevelOffset; if (getLevelUsesVirtualNodes(level)) { uint vmask = fetchAt(curNodePointer + 1 + getAdditionalBytesPerNode(level), 1); // Add pointer to the first child additionalPointerInfoStart += fetchAt(curNodePointer + 2 + getAdditionalBytesPerNode(level), pointerSize); //if(level == 1) return pointer; additionalPointerInfoStart += getSizeToSkip(childMask, vmask, childIndex, childPointerIndex, level, additionalPointerInfoStart); if (hasChild(vmask, childIndex)) additionalPointerInfoStart += getPointerSize(level + 1); else { if (getLevelUsesVirtualNodes(level + 1)) additionalPointerInfoStart += 1 + getAdditionalBytesPerNode(level + 1) + 1 + getPointerSize(level + 1); else { uint childCount2 = bitCount(fetchAt(additionalPointerInfoStart, 1)); additionalPointerInfoStart += 1 + getAdditionalBytesPerNode(level + 1) + childCount2 * (getPointerSize(level + 1) + getAdditionalBytesPerPointer(level + 1)); } } } else { additionalPointerInfoStart = curNodePointer + 1 + getAdditionalBytesPerNode(level) + childCount * pointerSize + childPointerIndex * shiftSize; } #endif // Fetch the pointer from the texture return fetchBigAt(additionalPointerInfoStart, shiftSize, octreeSamplers); } #if defined(BLOCK_PACK) || defined(TIGHTBLOCK_PACK) bptype fetchBlocksIndex(uint64_t nodeIndex) { uint blockPointerIndex = uint(nodeIndex / $blockSize$); uint indexValue = fetchAt(blockPointerIndex * 4, 4, blockPointerSampler); bptype index = bptype(indexValue) * $blockSize$; index += uint(nodeIndex % $blockSize$); return index; } #endif #ifdef PALETTE_PACK uint getBlockArrayIndex(in uint64_t value) { // Based on "upper_bound" implementation in c++ std algorithm library uint count = $blockCount$; uint curStep = 0; uint first = 0; uint it = 0; uint nodeIndexSize = fetchSingleAt(0, blockPointerSampler); uint palettePointerSize = fetchSingleAt(1, blockPointerSampler); uint blockPointerSize = fetchSingleAt(2, blockPointerSampler); uint itemSize = nodeIndexSize + palettePointerSize + blockPointerSize; while (count > 0) { it = first; curStep = count / 2; it += curStep; uint itPointer = 3 + it * itemSize; uint64_t curValue = fetchBigAt(itPointer, nodeIndexSize, blockPointerSampler); if (!(value < curValue)) { first = ++it; count -= curStep + 1; } else count = curStep; } return first - 1; } #endif #if defined(TIGHT_PACK) || defined(TIGHTBLOCK_PACK) || defined(PALETTE_PACK) // Offset in an offset in bytes within the texture that indicates where the tightly packed info with the given mask begins uint fetchTightlyPacked(uint64_t index, uint mask = $mask$, uint64_t offset = 0) { uint result = 0; uint setBits = bitCount(mask); uint64_t curBitIndex = 0; // Extract all the bits for (uint i = 0; i < 32; i++) { uint bitMask = uint(0x80000000) >> i; if ((bitMask & mask) != 0) { // If this bit is set in the mask, extract its value from the texture uint64_t bitIndex = (offset * 8) + (index * setBits) + curBitIndex; bptype texelIndex = bptype(bitIndex / 8); // bitIndex / (8 * 4) == bitIndex / 32 == bitIndex >> 5 uint texel = fetchSingleAt(texelIndex, blockSamplers); uint texelBitIndex = uint(bitIndex % 8); // bitIndex % 8 == bitIndex & 0x1F uint texelBitMask = 0x80 >> texelBitIndex; if ((texel & texelBitMask) != 0) result |= bitMask; curBitIndex++; } } return result; } #endif uint fetchUniqueIndexNodeValue(in uint64_t nodeIndex) { #ifdef BASIC_PACK return fetchAt(nodeIndex * 4, 4, blockSamplers); #endif #ifdef TIGHT_PACK return fetchTightlyPacked(nodeIndex); #endif #ifdef BLOCK_PACK // Get the index of the block pointer bptype index = fetchBlocksIndex(nodeIndex); return fetchAt(index * 4, 4, blockSamplers); #endif #ifdef TIGHTBLOCK_PACK ptype index = fetchBlocksIndex(nodeIndex); return fetchTightlyPacked(index); #endif #ifdef PALETTE_PACK uint blockIndex = getBlockArrayIndex(nodeIndex); //return rainbow(blockIndex); //return uvec3(blockIndex * 8); //return uvec3(rainbow(float(blockIndex) / 1000.) * 255.); uint nodeIndexSize = fetchSingleAt(0, blockPointerSampler); uint palettePointerSize = fetchSingleAt(1, blockPointerSampler); uint blockPointerSize = fetchSingleAt(2, blockPointerSampler); uint itemSize = nodeIndexSize + palettePointerSize + blockPointerSize; // Fetch the pixel containing the palette and block pointer: uint64_t blockStart = fetchBigAt(3 + blockIndex * itemSize, nodeIndexSize, blockPointerSampler); bptype palettePointer = bptype(fetchBigAt(3 + blockIndex * itemSize + nodeIndexSize, palettePointerSize, blockPointerSampler)); bptype blockPointer = bptype(fetchBigAt(3 + blockIndex * itemSize + nodeIndexSize + palettePointerSize, blockPointerSize, blockPointerSampler)); // Find out how to draw it bptype palettePointerMask = bptype(-1) >> ((BLOCK_POINTER_SIZE - palettePointerSize) * 8); if (palettePointer == ((~bptype(0)) & palettePointerMask)) { // 0xFFFFFFF signals that the full palette is used return fetchTightlyPacked(nodeIndex - blockStart, $mask$, blockPointer); } else if (palettePointer == (bptype(-2) & palettePointerMask)) { // 0xFFFFFFE signals that a single color is used return uint(blockPointer); } else { uint paletteBitSize = uint((palettePointer >> (8 * palettePointerSize - 3)) + 1); uint blockMask = (uint(-1)) >> (32 - paletteBitSize); // Mask to grab as many bits as there are in a block uint inPaletteIndex = fetchTightlyPacked(nodeIndex - blockStart, blockMask, blockPointer); bptype paletteIndex = palettePointer & (palettePointerMask >> 3); return fetchTightlyPacked(paletteIndex + inPaletteIndex, $mask$); } #endif #ifdef DAGBASED_PACK uint maxDepth = fetchSingleAt(0, blockSamplers[0]); uint pointerSize = fetchSingleAt(1, blockSamplers[0]); uint depth = 0; bptype curPointer = 2; bptype lastPointer = 0; //return uvec3(maxDepth * 8); while(depth <= maxDepth) { if ((nodeIndex & (1 << (maxDepth - depth))) == 0) // Left child curPointer = fetchPointerAt(curPointer, pointerSize, blockSamplers); else // Right child curPointer = fetchPointerAt(curPointer + pointerSize, pointerSize, blockSamplers); if (curPointer == 0) break; lastPointer = curPointer; ++depth; } return fetchAt(curPointer + 2 * pointerSize, 4, blockSamplers); #endif } #ifdef TT_UNIQUEINDEXSHIFT int extractSignedInt(in uint value) { return ((int)(value & 0x7FFFFFFF)) * (((0x80000000 & value) != 0) ? -1 : 1); } #endif ivec2 fetchUniqueIndexMaterialPointer(in ptype nodePointer, in ivec3 uLoc, in uint hitLevel) { uint maxLevel = min($max_level$, hitLevel); uint64_t nodeIndex = 0; ptype childPointer = getRootPointer(); #ifdef TT_UNIQUEINDEXSHIFT int summedValue = extractSignedInt(fetchUniqueIndexNodeValue(0)); #endif // Use KD-restart in the material tree to find the correct material for (uint level = 0; level < maxLevel; level++) { uint childIndex = getChildIndex(uLoc, level); uint childMask = getChildMask(childPointer); nodeIndex += getShift(childPointer, childMask, childIndex, level); childPointer = getNodePointer(childPointer, childMask, childIndex, level); #ifdef TT_UNIQUEINDEXSHIFT summedValue += extractSignedInt(fetchUniqueIndexNodeValue(nodeIndex)); #endif } #ifdef TT_UNIQUEINDEXSHIFT ivec2 matLabSize = textureSize(materialTextureSampler, 0); //return ivec2(summedValue / matLabSize.x); return ivec2(summedValue % matLabSize.x, summedValue / matLabSize.x); //return ivec2(64, 64); #else uint result = fetchUniqueIndexNodeValue(nodeIndex); return ivec2(result >> 16, 0x0000FFFF & result); #endif } #endif #if defined(TT_MULTIROOT) | defined(TT_BITTREES) // Samples the octree, starting at the given root pointer and going down until the given level is reached // Returns a pointer to the found leaf node ptype sampleOctree(in uvec3 uLoc, in uint untilLevel, in uint rootIndex, out uint hitLevel, out ptype parentPointer) { ptype childPointer = getRootPointer(rootIndex); parentPointer = 0; // Use KD-restart in the material tree to find the correct material for (uint level = 0; level < untilLevel && level < $max_level$; level++) { uint childIndex = getChildIndex(uLoc, level); uint childMask = getChildMask(childPointer); if (!hasChild(childMask, childIndex)) { hitLevel = level; return childPointer; } parentPointer = childPointer; childPointer = getNodePointer(childPointer, childMask, childIndex, level); } hitLevel = untilLevel; return childPointer; } #endif #ifdef TT_BITTREES uint getBit(in uint rootIndex, in uvec3 uLoc, in uint untilLevel) { uint hitLevel = 0; ptype parentPointer = 0; ptype nodePointer = sampleOctree(uLoc, untilLevel, rootIndex + 1, hitLevel, parentPointer); uint childIndex = getChildIndex(uLoc, hitLevel - 1); // ChildIndex of the parent of the hit node, needed to find the bit to extract // hasChild here actually checks if the material bit is set, not the actual childmask. uint materialMask = #ifdef PT_ORIGINAL fetchSingleAt(parentPointer + 3); #else fetchSingleAt(parentPointer + 1); #endif return hasChild(materialMask, childIndex) ? 1 : 0; } #endif vec2 OctWrap( vec2 v ) { return ( 1.0 - abs( v.yx ) ) * sign( v.xy ); } vec3 DecodeNormal(vec2 encN) { encN = encN * 2.0 - 1.0; vec3 n; n.z = 1.0 - abs( encN.x ) - abs( encN.y ); n.xy = n.z >= 0.0 ? encN.xy : OctWrap( encN.xy ); n = normalize( n ); return n; } float rand(vec2 co){ return fract(sin(dot(co.xy ,vec2(12.9898,78.233))) * 43758.5453); } uvec3 getColorAndNormal(in ptype nodePointer, in ivec3 uLoc, in uint hitLevel, out vec3 normal, out float value) { //return uvec3(255); #ifdef MT_NONE //return uvec3(vec3(rand(uLoc.xy), rand(uLoc.yz), rand(uLoc.xz)) * 256); //return uvec3(vec3(256 * uLoc) / rangeF); return uvec3(255); #endif #ifdef MATERIAL_LIBRARY ivec2 materialPointer; #endif #ifdef TT_STANDARD return uvec3(255); #endif #ifdef TT_COLORED uint color = fetchAt(nodePointer + 1, 3); return uvec3((color & 0x00FF0000) >> 16, (color & 0x0000FF00) >> 8, color & 0xFF); #endif #ifdef TT_HIERARCHICAL uint materialPointerValue = fetchAt(nodePointer + 1, getAdditionalBytesPerNode(hitLevel)); materialPointer = ivec2((materialPointerValue & 0x00FFF000) >> 12, materialPointerValue & 0x0FFF); //return uvec3(materialPointer.x, materialPointer.y, 0); #endif #ifdef TT_RANDOM uint bit = fetchSingleAt(nodePointer + 1); if (bit == 0) return uvec3(30); else return uvec3(255); #endif #ifdef TT_HIERARCHICALSHIFT ivec3 color = ivec3(0); hitLevel = min($max_level$, hitLevel); // For some reason, it crashes without this line for(int i = 0; i <= hitLevel; i++) { uint materialTexturePointerValue = fetchAt(pointerStack[i] + 1, getAdditionalBytesPerNode(hitLevel)); ivec2 materialTexturePointer = ivec2((materialTexturePointerValue & 0x00FFF00) >> 12, materialTexturePointerValue & 0x0FFF); uvec3 shiftMaterial = texelFetch(materialTextureSampler, ivec2(materialTexturePointer), 0).rgb; ivec3 shift = ivec3(shiftMaterial) - 127; color += shift << 1; } return uvec3(clamp(color, ivec3(0), ivec3(255))); #endif #ifdef TT_ONLYMATERIAL hitLevel = min($max_level$, hitLevel); // For some reason, it crashes without this line uint childPointer = getRootPointer(); // Use KD-restart in the material tree to find the correct material for (uint level = 0; level < hitLevel; level++) { uint childIndex = getChildIndex(uLoc, level); uint childMask = getChildMask(childPointer, materialOctreeSampler); childPointer = getNodePointer(childPointer, childMask, childIndex, level, materialOctreeSampler); } uint materialPointerValue = fetchAt(childPointer + 1, getAdditionalBytesPerNode(hitLevel, materialOctreeSampler), materialOctreeSampler); materialPointer = ivec2((materialPointerValue & 0x00FFF000) >> 12, materialPointerValue & 0x0FFF); #endif #ifdef TT_MULTIROOT uvec3 color = uvec3(0); uint hitLevelR = 0, hitLevelG = 0, hitLevelB = 0; ptype parentPointer; for (uint i = 0; i < TreesPerChannel; i++) { // Get the colors per channel for this bit: uint rootPointerOffset = i + 1; ptype pointerR = sampleOctree(uvec3(uLoc), $max_level$, i + 1, hitLevelR, parentPointer); ptype pointerG = sampleOctree(uvec3(uLoc), $max_level$, TreesPerChannel + i + 1, hitLevelG, parentPointer); ptype pointerB = sampleOctree(uvec3(uLoc), $max_level$, 2 * TreesPerChannel + i + 1, hitLevelB, parentPointer); //sampleR = sampleG = sampleB = sampleOctree(uLoc, 0); //sampleR.b = sampleG.b = sampleB.b = 1; //sampleR.rgb = sampleG.rgb = sampleB.rgb = uvec3(1); //uint curBitShift = 8 - i * bitsPerTree - bitsPerTree; uint curBitShift = i * $BitsPerTree$; if (hitLevelR >= $max_level$) color.r += fetchSingleAt(pointerR + 1) << curBitShift; if (hitLevelG >= $max_level$) color.g += fetchSingleAt(pointerG + 1) << curBitShift; if (hitLevelB >= $max_level$) color.b += fetchSingleAt(pointerB + 1) << curBitShift; } return color; #endif #ifdef TT_BITTREES uint coordValue = 0; uint xBits = ($RootCount$ - 1) / 2; uint yBits = xBits; xBits += (($RootCount$ - 1) % 2) == 0 ? 0 : 1; uint xValue = 0; for (uint i = 0; i < xBits; i++) { uint bit = getBit(i, uLoc, hitLevel); xValue |= bit << (xBits - i - 1); } uint yValue = 0; for (uint i = 0; i < yBits; i++) { uint bit = getBit(xBits + i, uLoc, hitLevel); yValue |= bit << (yBits - i - 1); } //return uvec3(xValue, yValue, 0); materialPointer = ivec2(xValue, yValue); #endif #if defined(TT_UNIQUEINDEX) | defined(TT_UNIQUEINDEXSHIFT) materialPointer = fetchUniqueIndexMaterialPointer(nodePointer, uLoc, hitLevel); #ifdef MULTIROOTBASED_PACK // TODO: implement the actual multiroot based pack value extraction return uvec3(200); #endif #endif #ifdef MATERIAL_LIBRARY //return uvec3(materialPointer, 0); uvec3 col = uvec3(255); #if defined(MT_COLOR) | defined(MT_COLORNORMAL)| defined(MT_COLORNORMALREFLECTIVITY) | defined(MT_COLOROPACITY) col = texelFetch(materialTextureSampler, materialPointer, 0).rgb; #endif #if defined(MT_COLOROPACITY) value = float(texelFetch(materialTextureSampler, materialPointer, 0).a) / 255.; #endif #if (defined(MT_COLORNORMAL) & NORMALSIZE > 8) | defined(MT_COLORNORMALREFLECTIVITY) materialPointer = ivec2(materialPointer.x + 1, materialPointer.y); #endif #if defined(MT_NORMAL) | defined(MT_COLORNORMAL) | defined(MT_COLORNORMALREFLECTIVITY) // 12 bit normals: #if NORMALSIZE > 8 uint normalComponentScale = (1 << (NORMALSIZE / 2)) - 1; uvec4 encV = texelFetch(materialTextureSampler, materialPointer, 0); //return encV.rgb; //return encV.rgb; #ifdef MT_COLORNORMALREFLECTIVITY value = float(encV.b) / 255.; #endif #if NORMALSIZE <= 16 uint enc = encV.r << 8 | encV.g; #elif NORMALSIZE <= 24 uint enc = encV.r << 16 | encV.g << 8 | encV.b; #else uint enc = encV.r << 24 | encV.g << 16 | encV.b << 8 | encV.a; #endif //return uvec3((enc >> 16) & 0xFF, (enc >> 8) & 0xFF, enc & 0xFF); //return encV; uvec2 encUN = uvec2(enc >> (NORMALSIZE / 2), enc & normalComponentScale); vec2 encN = vec2(encUN) / float(normalComponentScale); //return uvec3(uvec2(encN * 255.), 0); //normal = vec3(encV) / 255.; return; #else // 8 bit normals: #ifdef MT_COLORNORMALREFLECTIVITY uvec2 encV = texelFetch(materialTextureSampler, materialPointer, 0).rg; value = float(encV.g) / 255.; uint enc = encV.r; #else uint enc = texelFetch(materialTextureSampler, materialPointer, 0).a; #endif vec2 encN = vec2(float(enc >> 4) / 15.0, float(enc & 0x0F) / 15.0); #endif //col = uvec3(255); normal = DecodeNormal(encN); #endif //col = uvec3(normal * 255.); return col; #endif } RayCastResult getRayCastResult() { RayCastResult res; res.result = 0; res.time = 0; res.nodePointer = 0; res.nodePosition = vec3(0); res.level = 0; res.axis = vec3(1, 0, 0); res.uLoc = ivec3(0); res.loop = 0; return res; } //*********************************************** // Casts a ray against the octree structure, which is assumed to be located at rootcenter, with leaf voxels a size of 1, 1, 1 // Returns 0 if no intersection, 1 if intersection, and -1 if it is unknown if there was an intersection due to the maximum loop being reached //*********************************************** RayCastResult castRay(in Ray ray, in float startTime, in uint maxLoop, in float maxTime, in uint renderDepth/*, out float t, out ptype hitNodePointer, out vec3 hitAxis, out vec3 hitNodePosition, out float hitNodeExtent, out ivec3 uLoc, out uint hitLevel, out uint loop*/) { // Initialize the stack uint stackPos = 0; // Find out if the ray intersects with the root node float tminRoot, tmaxRoot; vec3 minAxis, maxAxis; bool collision = rayCube(ray, rootCenter, rootExtent, tminRoot, tmaxRoot, minAxis, maxAxis); if (!collision) // Ray doesn't intersect with root. return getRayCastResult(); // Calculate the location of the first leaf node that the ray enters float t = max(tminRoot, startTime); maxTime = min(tmaxRoot, maxTime); ivec3 uLoc = getNextULoc(ray, t, ivec3(ray.p0), minAxis); // Keep testing ray box intersection, but prevent endless looping uint loop = 0; uvec3 rangeU = uvec3(range); while (stackPos < renderDepth && loop < maxLoop && t < maxTime #ifdef EARLY_RAY_TERMINATION && getExtent(stackPos) > (ray.coneCoeff * t + ray.coneBias) #endif ) { float preferredChildExtent = getExtent(stackPos + 1); uint preferredChild = getChildIndex(uvec3(uLoc), stackPos); vec3 preferredChildPosition = getChildPosition(positionStack[stackPos], preferredChildExtent, preferredChild); uint childMask = getChildMask(pointerStack[stackPos]); //************************************ // PUSH // If we have the preferred child, explore it //************************************ if (hasChild(childMask, preferredChild)) { stackPos++; pointerStack[stackPos] = getNodePointer(pointerStack[stackPos - 1], childMask, preferredChild, stackPos - 1); positionStack[stackPos] = preferredChildPosition; } //************************************* // ADVANCE/POP // If we didn't have the preferred child, find the next preferred child. //************************************* else { // Find the first child that the ray enters after it exits the current preferred child rayCubeExit(ray, preferredChildPosition, preferredChildExtent, t, maxAxis); ivec3 lastULoc = uLoc; uLoc = getNextULoc(ray, t - 0.05, lastULoc, maxAxis); if (any(lessThan(uLoc, uvec3(0))) || any(greaterThanEqual(uLoc, rangeU))) return getRayCastResult(); // Count the number of bits that were the same in the last uLoc (until they start not being the same) // Since lastULoc is in the current node, we know that this number of bits is the last common parent, so we pop up to that level. // Note that this level is indeed one higher, but we need that to advance to the next cell? ivec3 equalBits = $max_level$ - findMSB(lastULoc ^ uLoc); stackPos = min(min(equalBits.x, equalBits.y), equalBits.z); } ++loop; } RayCastResult res; res.result = loop == maxLoop || t > maxTime ? -1 : 1; res.nodePointer = pointerStack[stackPos]; res.nodePosition = positionStack[stackPos]; res.loop = loop; res.time = t; res.axis = maxAxis; res.level = uint(max(0, stackPos)); res.uLoc = uLoc; return res; } void InitStack() { for (int i = 0; i <= $max_level$; i++) { pointerStack[i] = getRootPointer(); positionStack[i] = rootCenter; } } //vec3 toonify(vec3 color) { // float levels = 8.; // float epsilon = 0.01; // vec3 qlo = floor(levels*(color - epsilon))/(levels - 1.); // vec3 qhi = floor(levels*(color + epsilon))/(levels - 1.); // vec3 qihi = qhi*(levels - 1.)/levels; // vec3 weights = smoothstep(0., 2.*epsilon, color + epsilon - qihi); // return mix(qlo, qhi, weights); //} // Cast a ray from position to the light source. If something is hit, return true, otherwise returns false. bool isOccluded(vec3 position, float angleOffset = 0, float raySkip = 0, uint untilDepth = renderDepth) { if (lightType == 0) return false; if (lightType == 1) { // Directional light Ray lightRay = getRay(position, -lightDirection, angleOffset); RayCastResult res = castRay(lightRay, raySkip, max(range / 1024, 256), 99999999.0, untilDepth); return res.result == 1 || res.result == -1; } if (lightType == 2) { // Point light vec3 toLight = ((lightDirection + offset) * scale) - position; vec3 lightDir = normalize(toLight); Ray lightRay = getRay(position, lightDir, angleOffset); RayCastResult res = castRay(lightRay, raySkip, max(range / 1024, 256), length(toLight), untilDepth); return res.result == 1; } } float getDiffuseLight(vec3 normal, vec3 position) { if (lightType == 0) return 1.0; else if (lightType == 1) return max(dot(-lightDirection, normal), 0); else if (lightType == 2) { vec3 toLight = ((lightDirection + offset) * scale) - position; vec3 lightDir = normalize(toLight); return max(dot(lightDir, normal), 0); } } float getEnvironmentLight() { return 0.4; } void getTangentAndBitangent(in vec3 normal, out vec3 tangent, out vec3 binormal) { vec3 c1 = cross(normal, vec3(0.0, 0.0, 1.0)); vec3 c2 = cross(normal, vec3(0.0, 1.0, 0.0)); if (length(c1)>length(c2)) tangent = c1; else tangent = c2; tangent = normalize(tangent); binormal = cross(normal, tangent); binormal = normalize(binormal); } vec3 rotateToNormal(in vec3 value, in vec3 normal, in vec3 tangent, in vec3 bitangent) { return mat3(tangent, bitangent, normal) * value; } vec3 sampleHemisphere(float u1, float u2) { float r = sqrt(1.0f - u1 * u1); float phi = 2 * pi * u2; return vec3(cos(phi) * r, sin(phi) * r, u1); } void main() { uint maxLoop = 4096; //#ifdef TT_MULTIROOT //uint value = getRootPointer(2); //color.rgb = vec3(float(value) / 50.); //return; //#endif // Set color using textures color = texture(textureSampler, uv); voxelPosition = vec3(0); // Scale the grid so that all leaf cells have a width of exactly 1: // Get ray and sample point vec3 p0 = (cameraPosition + offset) * scale; vec3 rayDir = normalize(pos - cameraPosition); Ray ray = getRay(p0, rayDir); InitStack(); //// DEBUG: //uint firstPointer = getLevelOffset(1) + (fetchAt(pointerStack[0] + 1, 1) & (~(0x03 << ((getPointerSize(0) * 8) - 2)))); //color.rgb = vec3(float(firstPointer) / 255.); //color.rgb = vec3(float(getAdditionalBytesPerNode(1)) / 255.); //return; // Cast the primary ray RayCastResult rcr = castRay(ray, 0, maxLoop, 99999999.0, renderDepth/*, time, hitNodePointer, hitAxis, hitNodePosition, uLoc, hitLevel, loop*/); if (rcr.result == 1) { voxelPosition = sampleRay(ray, rcr.time); //vec3 hitPos = sampleRay(ray, rcr.time); //color.rgb = hitPos / 10000.; //return; //color.rgb = vec3(1); return; vec3 normal = vec3(0); float reflectivity = 0; color.rgb = vec3(getColorAndNormal(rcr.nodePointer, rcr.uLoc, rcr.level, normal, reflectivity)) / 255.; //return; //uint toShow = pointerStack[2]; //color.rgb = vec3(float(toShow) / 255.); //return; #if AOEnabled uint giLevel = min(rcr.level, giRayDepth); vec3 giNodePosition = positionStack[giLevel]; // Store the position of the node at the secondary ray level #endif float nodeSize = getExtent(rcr.level) * 2.; float depth = rcr.time / (scale * 500000.0); light = vec3(1.f); if (depth > 0.99) depth = 0.99; gl_FragDepth = depth; //color.rgb = vec3(time / 1000000.0); //return; //color.rgb = normal; return; //color.rgb = normal * 0.5 + 0.5; return; if (normal != vec3(0)) { // Calculate reflections #ifdef MT_COLORNORMALREFLECTIVITY if (reflectivity > 0) { vec3 reflectionRayDir = rayDir - 2 * normal * dot(rayDir, normal); Ray reflectionRay = getRay(sampleRay(ray, rcr.time), reflectionRayDir, ray.coneCoeff * rcr.time + ray.coneBias); //color.rgb = reflectionRayDir * 0.5 + 0.5; //return; RayCastResult rrcr = castRay(reflectionRay, 2.0, maxLoop, 99999999.0, renderDepth); if (rrcr.result == 1) { vec3 reflectionRayHitNormal; float reflectionRayReflectivity; vec3 reflectedColor = vec3(getColorAndNormal(rrcr.nodePointer, rrcr.uLoc, rrcr.level, reflectionRayHitNormal, reflectionRayReflectivity)) / 255.; float reflectedLight = getDiffuseLight(reflectionRayHitNormal, sampleRay(reflectionRay, rrcr.time)); //reflectedColor = clamp(reflectedColor, vec3(0), vec3(1)); reflectedColor *= 0.5 + 0.8 * reflectedLight; color.rgb = color.rgb /** (1. - reflectivity)*/ + reflectedColor * reflectivity; } } #endif // Calculate color bleeding #if AOEnabled { vec3 tangent, bitangent; getTangentAndBitangent(normal, tangent, bitangent); // Calculate where the ray entered a node at the giLevel float giNodeSize = nodeSize; float giNodeExtent = getExtent(giLevel); float giEnterLevelTime = rayCubeHitAxis(ray, giNodePosition, giNodeExtent, rcr.axis); giNodeSize = giNodeExtent * 2.; vec3 giRayStartPos = sampleRay(ray, giEnterLevelTime); gi = vec3(0); uint ao = 0; vec3 giNormal = normal; if (dot(rayDir, giNormal) > 0) giNormal = -giNormal; uint samplesPerDirection = uint(sqrt(giSampleCount)); float sampleSpacing = 1. / float(samplesPerDirection); //ivec2 seed = ivec2(1); ivec2 seed = ivec2(uv * 4096.0); int randomSize = textureSize(randomSampler, 0).x; //vec3 randVec = normalize(rand(rayDir)); for (int i = 0; i < giSampleCount; i++) { vec2 randomVec = vec2(float(i % samplesPerDirection) * sampleSpacing, float(i / samplesPerDirection) * sampleSpacing); ivec2 curSeed = ivec2(seed.x * primes[i], seed.y* primes[i]); randomVec += texelFetch(randomSampler, curSeed % randomSize, 0).xy * sampleSpacing; vec3 randomDir = sampleHemisphere(randomVec.x, randomVec.y); randomDir = rotateToNormal(randomDir, giNormal, tangent, bitangent); //color.rgb = randomDir; //return; Ray giRay = getRay(giRayStartPos, randomDir, ray.coneCoeff * rcr.time + ray.coneBias); RayCastResult gircr = castRay(giRay, giRaySkip * sqrt(giNodeSize * giNodeSize * 3.0), giRaySteps1, giRayMaxDistance1, giLevel); if (gircr.result == 1) { //color.rgb = vec3(giRayHitTime / giRayMaxDistance1); return; // Gather AO: if (gircr.time < aoMaxOcclusionDistance) { ao++; } // Check if the hit node is occluded else if (gircr.time < giRayMaxDistance1) { vec3 giRayHitNormal; float giRayReflectivity; vec3 giColor = vec3(getColorAndNormal(gircr.nodePointer, gircr.uLoc, gircr.level, giRayHitNormal, giRayReflectivity)) / 255.; //color.rgb = abs(giRayHitNormal); return; float giLight = max(dot(randomDir, normal), 0); if (isOccluded(sampleRay(giRay, gircr.time), giRay.coneCoeff * gircr.time + giRay.coneBias, 0.2 * sqrt(giNodeSize * giNodeSize * 3.0), giLevel)) giLight *= environmentLight; else giLight *= lightStrength * getDiffuseLight(giRayHitNormal, sampleRay(giRay, gircr.time)); //giLight = 0.3 + 0.7 * giLight; giColor *= giLight; //float influence = pow((giRayMaxDistance1 - giRayHitTime) / giRayMaxDistance1, giCoeff); //giColor *= influence; //giColor = clamp(giColor, vec3(0), vec3(1)); gi += giColor; /** (1.0 - pow((giRayHitTime / giRayMaxDistance1), giCoeff))*/; } }/* else { gi += vec3(0.2); }*/ } gi /= float(giSampleCount); gi *= giStrength; //gi *= giStrength; //color.rgb = gi; return; vec3 giLight = gi;// - (float(ao) / float(giSampleCount)); if (isOccluded(sampleRay(ray, rcr.time), invWidth * angle * sqrt(3) * getExtent(rcr.level), 1.5 * sqrt(nodeSize * nodeSize * 3))) light *= environmentLight; else light *= lightStrength * getDiffuseLight(normal, sampleRay(ray, rcr.time)); //color.rgb = color.rgb * light + color.rgb * giLight; // <-- This is the correct one without post processing //color.rgb = giLight; //color.rgb += color.rgb * gi; //color.rgb *= .5; } #else light *= getDiffuseLight(normal, sampleRay(ray, rcr.time)); #endif // Calculate diffuse lighting } // Cast one shadow ray #if !AOEnabled if (isOccluded(sampleRay(ray, rcr.time), invWidth * angle * sqrt(3) * getExtent(rcr.level), 1.5 * sqrt(nodeSize * nodeSize * 3))) light *= 0.4; color.rgb *= 0.5 + 0.7 * light; #endif #ifdef MT_COLOROPACITY color.rgb *= reflectivity; #endif //// Ambient Occlusion //#if AOEnabled // int samplesHit = 0; // vec3 aoRayHitNodePosition, aoRayHitAxis; // float aoRayHitNodeExtent, aoRayHitTime; // uint aoRayHitNodePointer, aoRayHitLevel, aoRayLoop; // ivec3 aoRayuLoc; // float ao = 0; // //ivec2 seed = ivec2(1); // ivec2 seed = ivec2(uv * 4096.0); // int randomSize = textureSize(randomSampler, 0).x; // //vec3 randVec = normalize(rand(rayDir)); // for (int i = 0; i < aoSampleCount; i++) // { // ivec2 curSeed = ivec2(seed.x * primes[i], seed.y* primes[i]); // vec3 randVec = normalize(0.5 - texelFetch(randomSampler, curSeed % randomSize, 0).rgb);// normalize(rand(randVec)); // if (dot(randVec, hitAxis * rayDir) >= 0) randVec *= -1; // Ray aoRay = getRay(sampleRay(ray, time), randVec, 0); // int aoResult = castRay(aoRay, aoRayStepback * sqrt(nodeSize * nodeSize * 3), aoRaySteps, aoMaxOcclusionDistance + 0.5, hitLevel, // aoRayHitTime, aoRayHitNodePointer, aoRayHitAxis, aoRayHitNodePosition, aoRayHitNodeExtent, // aoRayuLoc, aoRayHitLevel, aoRayLoop); // if (aoResult == 1 && aoRayHitTime < aoMaxOcclusionDistance) // ao += min(aoMaxSingleRayOcclusion, 1.0 - pow((aoRayHitTime / aoMaxOcclusionDistance), aoCoeff)); // } // ao /= float(aoSampleCount); // color.rgb *= 1.0 - ao; //#endif } else { gl_FragDepth = 0.99999; } // Discard pixels that are transparent // WARNING! WARNING! WARNING! Don't uncomment the following line it causes the shader to crash! //if (color.r < 0.5) //discard; //color.rgb = toonify(color.rgb); }