Skip to content

Instantly share code, notes, and snippets.

@BelmuTM
Last active April 2, 2024 19:16
Show Gist options
  • Save BelmuTM/af0fe99ee5aab386b149a53775fe94a3 to your computer and use it in GitHub Desktop.
Save BelmuTM/af0fe99ee5aab386b149a53775fe94a3 to your computer and use it in GitHub Desktop.
GLSL Screen Space Raytracer - Free to use for learning purposes
#define BINARY_REFINEMENT 1
#define BINARY_COUNT 4
#define BINARY_DECREASE 0.5
vec3 diagonal(mat4 mat) { return vec3(mat[0].x, mat[1].y, mat[2].z); }
vec3 projectionOrthogonal(mat4 mat, vec3 v) { return diagonal(mat) * v + mat[3].xyz; }
vec3 viewToScreen(vec3 viewPos) {
return (projectionOrthogonal(gbufferProjection, viewPosition) / -viewPosition.z) * 0.5 + 0.5;
}
// Takes the minimum of 3 values
float minOf(vec3 x) { return min(x.x, min(x.y, x.z)); }
void binarySearch(inout vec3 rayPosition, vec3 rayDirection) {
for(int i = 0; i < BINARY_COUNT; i++) {
rayPos += sign(texture(depthtex0, rayPosition.xy).r - rayPosition.z) * rayDirection;
// Going back and forth using the delta of the 2 different depths as a parameter for sign()
rayDir *= BINARY_DECREASE;
// Decreasing the step length (to slowly tend towards the intersection)
}
}
// The favorite raytracer of your favorite raytracer
bool raytrace(vec3 viewPosition, vec3 rayDirection, int stepCount, float jitter, out vec3 rayPosition) {
// "out vec3 rayPosition" is our ray's position, we use it as an "out" parameter to be able to output both the intersection check and the hit position
rayPosition = viewToScreen(viewPosition);
// Starting position in screen space, it's better to perform space conversions OUTSIDE of the loop to increase performance
rayDirection = viewToScreen(viewPosition + rayDirection) - rayPosition;
rayDirection *= minOf((sign(rayDirection) - rayPosition) / rayDirection) * (1.0 / stepCount);
// Calculating the ray's direction in screen space, we multiply it by a "step size" that depends on a few factors from the DDA algorithm
bool intersect = false;
// Our intersection isn't found by default
rayPosition += rayDirection * jitter;
// We settle the ray's starting point and jitter it
// Jittering reduces the banding caused by a low amount of steps, it's basically multiplying the direction by a random value (like noise)
for(int i = 0; i <= stepCount && !intersect; i++, rayPosition += rayDirection) {
// Loop until we reach the max amount of steps OR if an intersection is found, add 1 at each iteration AND march the ray (position += direction)
if(clamp01(rayPosition.xy) != rayPosition.xy) return false;
// Checking if the ray goes outside of the screen (if clamping the coordinates to [0;1] returns a different value, then we're outside)
// There's no need to continue ray marching if the ray goes outside of the screen
float depth = texture(depthtex0, rayPosition.xy).r;
// Sampling the depth at the ray's screen space position
intersect = rayPosition.z > depth;
// If the ray's depth is bigger than the geometry depth, then our ray has hit the geometry
}
#if BINARY_REFINEMENT == 1
binarySearch(rayPosition, rayDirection);
// Binary search for some extra accuracy
#endif
return intersect;
// Outputting the boolean
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment