1732 lines
62 KiB
GLSL
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);
|
|
} |