Initial commit: Final state of the master project
This commit is contained in:
340
Research/shaders/StackRaytrace.frag
Normal file
340
Research/shaders/StackRaytrace.frag
Normal file
@@ -0,0 +1,340 @@
|
||||
#version 440 core
|
||||
|
||||
in vec3 pos;
|
||||
in vec2 uv;
|
||||
in vec3 normal;
|
||||
|
||||
out vec4 color;
|
||||
|
||||
uniform sampler2D textureSampler;
|
||||
uniform usampler3D octreeSampler;
|
||||
|
||||
layout(location = $width$) uniform int width;
|
||||
layout(location = $height$) uniform int height;
|
||||
layout(location = $angle$) uniform float angle;
|
||||
layout(location = $aspect$) uniform float aspect;
|
||||
|
||||
layout(location = $lightDirection$) uniform vec3 lightDirection;
|
||||
layout(location = $cameraPosition$) uniform vec3 cameraPosition;
|
||||
|
||||
struct Ray
|
||||
{
|
||||
vec3 p0;
|
||||
vec3 dir;
|
||||
vec3 invDir;
|
||||
vec3 dirSign;
|
||||
uvec3 dirSignNegMask;
|
||||
};
|
||||
|
||||
ivec3 texSize;
|
||||
const uint range = 1 << ($max_level$ + 1);
|
||||
const vec3 rootCenter = vec3(range) * 0.5;
|
||||
const float rootExtent = float(range) * 0.5;
|
||||
|
||||
// Initialize the stack
|
||||
uvec3 pointerStack[$max_level$ + 1];
|
||||
vec3 positionStack[$max_level$ + 1];
|
||||
float extentStack[$max_level$ + 1];
|
||||
|
||||
uint stackPos;
|
||||
|
||||
Ray getRay(vec3 p0, vec3 dir)
|
||||
{
|
||||
dir = normalize(dir);
|
||||
vec3 dirSign = sign(dir);
|
||||
return Ray(
|
||||
p0,
|
||||
dir,
|
||||
1 / dir,
|
||||
dirSign,
|
||||
uvec3(-clamp(sign(dir), -1, 0)));
|
||||
}
|
||||
|
||||
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 = center + (extent * ray.dirSign);
|
||||
// Calculate the times at which the planes that span on the edges of the cubes are hit
|
||||
vec3 axisTimes = (voxelEdges - ray.p0) * ray.invDir;
|
||||
|
||||
// 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);
|
||||
}
|
||||
|
||||
ivec3 wrapNodePointer(in ivec3 samplePos)
|
||||
{
|
||||
// Make sure that the indices are wrapped if the pointer doesn't fit in the current texture
|
||||
if (samplePos.x >= texSize.x)
|
||||
{
|
||||
samplePos.x = samplePos.x % texSize.x;
|
||||
samplePos.y++;
|
||||
if (samplePos.y >= texSize.y)
|
||||
{
|
||||
samplePos.y = samplePos.y % texSize.y;
|
||||
samplePos.z++;
|
||||
}
|
||||
//return vec4(255, 255, 0, float($max_level$ + 1));
|
||||
}
|
||||
return samplePos;
|
||||
}
|
||||
|
||||
//************************************
|
||||
// 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.
|
||||
//************************************
|
||||
uvec3 getNodePointer(uvec3 curNodePointer, uint childMask, uint childIndex)
|
||||
{
|
||||
// Based on the childIndex and the childmask, find out what the index of the childpointer is
|
||||
int childPointerIndex = bitCount(childMask << (31 - childIndex));
|
||||
|
||||
// Find sample position in 3D texture corresponding to current node and locat
|
||||
ivec3 samplePos = ivec3(curNodePointer);
|
||||
samplePos.x += childPointerIndex;
|
||||
// Make sure that the indices are wrapped if the pointer doesn't fit in the current texture
|
||||
samplePos = wrapNodePointer(samplePos);
|
||||
|
||||
// Fetch the pointer from the texture
|
||||
return texelFetch(octreeSampler, samplePos, 0).rgb;
|
||||
}
|
||||
|
||||
// Fetches the childmask of the node at curNodePointer (note that this is not a mask for leaf nodes!)
|
||||
uint getChildMask(in uvec3 nodePointer)
|
||||
{
|
||||
return texelFetch(octreeSampler, ivec3(nodePointer), 0).b;
|
||||
}
|
||||
|
||||
uvec3 getColor(in uvec3 nodePointer)
|
||||
{
|
||||
return texelFetch(octreeSampler, ivec3(nodePointer), 0).rgb;
|
||||
}
|
||||
|
||||
bool hasChild(in uint childMask, in uint childIndex)
|
||||
{
|
||||
uint mask = 1 << childIndex;
|
||||
return (childMask & mask) == mask;
|
||||
}
|
||||
|
||||
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
|
||||
// HACK: Cast to int, and then to uint, because casting int to uint gets uint.maxValue if the value == -1,
|
||||
// but float to uint directly rounds and gives 0, meaning you will never get out of the cube!
|
||||
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;
|
||||
}
|
||||
|
||||
//***********************************************
|
||||
// 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
|
||||
//***********************************************
|
||||
int castRay(in Ray ray, in uint maxLoop, out float t, out uvec3 hitNodePointer, out vec3 hitAxis)
|
||||
{
|
||||
// Initialize out values
|
||||
t = 0;
|
||||
hitNodePointer = uvec3(0);
|
||||
hitAxis = vec3(0);
|
||||
|
||||
// Initialize the stack
|
||||
stackPos = 0;
|
||||
for (int i = 0; i <= $max_level$; i++)
|
||||
{
|
||||
pointerStack[i] = uvec3(0); // The root node is always at position 0, 0, 0 in the texture
|
||||
positionStack[i] = rootCenter;
|
||||
extentStack[i] = float(1 << ($max_level$ - i + 1)) * 0.5;
|
||||
}
|
||||
|
||||
// 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 0;
|
||||
|
||||
// Calculate the location of the first leaf node that the ray enters
|
||||
ivec3 uLoc = getNextULoc(ray, tminRoot, ivec3(ray.p0), minAxis);
|
||||
|
||||
// Keep testing ray box intersection, but prevent endless looping
|
||||
int loop = 0;
|
||||
uvec3 rangeU = uvec3(range);
|
||||
while (stackPos < $max_level$ && loop < maxLoop) {
|
||||
uint preferredChild = getChildIndex(uLoc, stackPos);
|
||||
float preferredChildExtent = extentStack[stackPos + 1];
|
||||
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))
|
||||
{
|
||||
pointerStack[stackPos + 1] = getNodePointer(pointerStack[stackPos], childMask, preferredChild);
|
||||
positionStack[stackPos + 1] = preferredChildPosition;
|
||||
stackPos++;
|
||||
}
|
||||
//*************************************
|
||||
// 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 0;
|
||||
|
||||
// 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;
|
||||
}
|
||||
hitAxis = maxAxis;
|
||||
hitNodePointer = pointerStack[stackPos];
|
||||
|
||||
// Check if the current node is a leaf node. If so, draw it:
|
||||
if (stackPos >= $max_level$)
|
||||
return 1;
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
void main() {
|
||||
|
||||
uint maxLoop = 4096;
|
||||
|
||||
texSize = textureSize(octreeSampler, 0);
|
||||
|
||||
// Set color using textures
|
||||
color = texture(textureSampler, uv);
|
||||
|
||||
// Scale the grid so that all leaf cells have a width of exactly 1:
|
||||
float extent = $extent$;
|
||||
float rangeF = float(range);
|
||||
float scale = rangeF / (extent * 2.);
|
||||
float offset = extent + 0.5 / scale;
|
||||
|
||||
// Get ray and sample point
|
||||
vec3 p0 = (cameraPosition + offset) * scale;
|
||||
vec3 rayDir = normalize(pos - cameraPosition);
|
||||
Ray ray = getRay(p0, rayDir);
|
||||
|
||||
// Cast the primary ray
|
||||
float time;
|
||||
vec3 hitAxis;
|
||||
uvec3 hitNodePointer;
|
||||
int primaryRayHit = castRay(ray, maxLoop, time, hitNodePointer, hitAxis);
|
||||
if (primaryRayHit == 1)
|
||||
{
|
||||
color.rgb = vec3(getColor(hitNodePointer)) / 255.;
|
||||
|
||||
// Cast one shadow ray
|
||||
if (lightDirection != vec3(0))
|
||||
{
|
||||
Ray lightRay = getRay(ray.dir * time + p0 - ray.dir * 0.01, -lightDirection);
|
||||
int shadowResult = castRay(lightRay, max(range / 1024, 1000), time, hitNodePointer, hitAxis);
|
||||
if (shadowResult == 1 || shadowResult == -1)
|
||||
color.rgb *= 0.5 + (0.5 - float(abs(shadowResult)) * 0.5);
|
||||
}
|
||||
}
|
||||
|
||||
// Discard pixels that are transparent
|
||||
if (color.a < 0.5)
|
||||
discard;
|
||||
|
||||
}
|
||||
Reference in New Issue
Block a user