Files
CDAG/Research/shaders/CombinedStackRaytrace.frag

1732 lines
62 KiB
GLSL

#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);
}