Skip to content

Instantly share code, notes, and snippets.

Show Gist options
  • Save sjhalayka/1786fe3a81a19c54aa89a20f217c45c5 to your computer and use it in GitHub Desktop.
Save sjhalayka/1786fe3a81a19c54aa89a20f217c45c5 to your computer and use it in GitHub Desktop.
#version 460
#extension GL_EXT_ray_tracing : require
#extension GL_EXT_nonuniform_qualifier : enable
struct RayPayload {
vec3 color;
vec3 pure_color;
float distance;
vec3 normal;
float reflector;
float opacity;
vec3 pos;
vec3 wro;
vec3 wrd;
float hitt;
};
layout(location = 0) rayPayloadInEXT RayPayload rayPayload;
layout(location = 1) rayPayloadInEXT RayPayload rayPayload2;
layout(location = 2) rayPayloadEXT bool shadowed;
layout(binding = 0, set = 1) uniform sampler2D baseColorSampler;
layout(binding = 1, set = 1) uniform sampler2D normalSampler;
layout(binding = 2, set = 1) uniform sampler2D occlusionSampler;
hitAttributeEXT vec2 attribs;
const int max_lights = 2;
layout(binding = 0, set = 0) uniform accelerationStructureEXT topLevelAS;
layout(binding = 2, set = 0) uniform UBO
{
mat4 viewInverse;
mat4 projInverse;
mat4 transformation_matrix;
vec4 light_positions[max_lights];
vec4 light_colors[max_lights];
vec3 camera_pos;
int vertexSize;
} ubo;
layout(binding = 3, set = 0) buffer Vertices { vec4 v[]; } vertices;
layout(binding = 4, set = 0) buffer Indices { uint i[]; } indices;
struct Vertex
{
vec3 pos;
vec3 normal;
vec2 uv;
vec4 color;
vec4 _pad0;
vec4 _pad1;
};
Vertex unpack(uint index)
{
// Unpack the vertices from the SSBO using the glTF vertex structure
// The multiplier is the size of the vertex divided by four float components (=16 bytes)
const int m = ubo.vertexSize / 16;
vec4 d0 = vertices.v[m * index + 0];
vec4 d1 = vertices.v[m * index + 1];
vec4 d2 = vertices.v[m * index + 2];
Vertex v;
v.pos = d0.xyz;
v.normal = vec3(d0.w, d1.x, d1.y);
v.uv = vec2(d1.z, d1.w);
v.color = vec4(d2.x, d2.y, d2.z, 1.0);
return v;
}
// https://github.com/daw42/glslcookbook/blob/master/chapter07/shader/shadowmap.fs
vec3 phongModelDiffAndSpec(bool do_specular, float reflectivity, vec3 color, vec3 light_color, vec3 light_pos, vec3 frag_pos, vec3 frag_normal)
{
const vec3 MaterialKs = vec3(1.0, 0.5, 0.0);
const vec3 MaterialKa = vec3(0.0, 0.025, 0.075);
const float MaterialShininess = 10.0;
const vec3 n = normalize(frag_normal);
const vec3 s = normalize(light_pos - frag_pos);
const vec3 v = normalize(frag_pos);
const vec3 r = reflect( -s, n );
const float sDotN = max( dot(s,n), 0.0 ); // This second parameter affects the visibility of shadow edges
const vec3 diffuse = light_color * color * sDotN;
vec3 spec = vec3(0.0);
if(sDotN > 0.0)
spec = MaterialKs * pow( max( dot(r,v), 0.0 ), MaterialShininess );
vec3 ret = diffuse + MaterialKa;
if(do_specular)
ret = ret + spec;
return ret;
}
uint prng_state = 0;
// See: https://github.com/nvpro-samples/vk_mini_path_tracer/blob/main/vk_mini_path_tracer/shaders/raytrace.comp.glsl#L26
float stepAndOutputRNGFloat(inout uint rngState)
{
// Condensed version of pcg_output_rxs_m_xs_32_32, with simple conversion to floating-point [0,1].
rngState = rngState * 747796405 + 1;
uint word = ((rngState >> ((rngState >> 28) + 4)) ^ rngState) * 277803737;
word = (word >> 22) ^ word;
return float(word) / 4294967295.0f;
}
float get_caustic_float(const vec3 light_pos, const vec3 normal, float caustic_sharpness)
{
// Back up the payload before we trace some rays
RayPayload r = rayPayload;
vec3 lightVector = normalize(light_pos);
// Pseudorandomize the direction of the light
// in order to get blurry shadows
vec3 rdir = normalize(vec3(stepAndOutputRNGFloat(prng_state), stepAndOutputRNGFloat(prng_state), stepAndOutputRNGFloat(prng_state)));
// Stick to the correct hemisphere
if(dot(rdir, lightVector) < 0.0)
rdir = -rdir;
// Keep the shadows stay dynamic to some degree
// I mean, how blurry do you really need the edges to be?
if(caustic_sharpness < 0.5)
caustic_sharpness = 0.5;
lightVector = mix(rdir, lightVector, caustic_sharpness);
float caustic = 1.0;
vec3 first_origin = gl_WorldRayOriginEXT + gl_WorldRayDirectionEXT * gl_HitTEXT;
vec3 first_biased_origin = first_origin + normal * 0.01;
vec3 origin = gl_WorldRayOriginEXT + gl_WorldRayDirectionEXT * gl_HitTEXT;
vec3 biased_origin = origin + normal * 0.01;
if(dot(normal, lightVector) < 0.0)
{
caustic = 1.0;
}
else
{
// Shadow casting
origin = gl_WorldRayOriginEXT + gl_WorldRayDirectionEXT * gl_HitTEXT;
biased_origin = origin + normal * 0.01;
caustic = 0.0;
while(true)
{
traceRayEXT(topLevelAS, 0, 0xff, 0, 0, 0, biased_origin, 0.001, lightVector, 10000.0, 0);
if(rayPayload.distance == -1)
break;
biased_origin = biased_origin + lightVector*0.01;
float first_dot = dot(normalize(lightVector), normalize(rayPayload.normal));
float first_opacity = rayPayload.opacity;
float rating = abs(first_dot);
if(rating > caustic)
caustic = rating;
}
}
// Restore the payload after we've traced some rays
rayPayload = r;
return 2*caustic;
}
float get_shadow_float(const vec3 light_pos, const vec3 normal, float shadow_sharpness)
{
// Back up the payload before we trace some rays
RayPayload r = rayPayload;
vec3 lightVector = normalize(light_pos);
// Pseudorandomize the direction of the light
// in order to get blurry shadows
vec3 rdir = normalize(vec3(stepAndOutputRNGFloat(prng_state), stepAndOutputRNGFloat(prng_state), stepAndOutputRNGFloat(prng_state)));
// Stick to the correct hemisphere
if(dot(rdir, lightVector) < 0.0)
rdir = -rdir;
// Keep the shadows stay dynamic to some degree
// I mean, how blurry do you really need the edges to be?
if(shadow_sharpness < 0.5)
shadow_sharpness = 0.5;
lightVector = mix(rdir, lightVector, shadow_sharpness);
float shadow = 0.0;
vec3 first_origin = gl_WorldRayOriginEXT + gl_WorldRayDirectionEXT * gl_HitTEXT;
vec3 first_biased_origin = first_origin + normal * 0.01;
vec3 origin = gl_WorldRayOriginEXT + gl_WorldRayDirectionEXT * gl_HitTEXT;
vec3 biased_origin = origin + normal * 0.01;
if(dot(normal, lightVector) < 0.0)
{
shadow = 0.0;
}
else
{
// Shadow casting
origin = gl_WorldRayOriginEXT + gl_WorldRayDirectionEXT * gl_HitTEXT;
biased_origin = origin + normal * 0.01;
int num_intersections = 0;
float first_opacity = 0.0;
float first_dot = 0.0;
shadow = 0.0;
while(true)
{
uint rayFlags = gl_RayFlagsTerminateOnFirstHitEXT | gl_RayFlagsOpaqueEXT;// | gl_RayFlagsSkipClosestHitShaderEXT;
uint cullMask = 0xff;
float tmin = 0.001;
float tmax = 10000.0;
traceRayEXT(topLevelAS, rayFlags, cullMask, 0, 0, 0, biased_origin, tmin, lightVector, tmax, 0);
if(rayPayload.distance == -1)
break;
biased_origin = biased_origin + lightVector*0.01;
num_intersections++;
if(num_intersections % 2 != 0) // if odd
{
first_dot = dot(normalize(lightVector), normalize(rayPayload.normal));
first_opacity = rayPayload.opacity;
}
else // else even
{
float second_dot = dot(normalize(lightVector), normalize(rayPayload.normal));
float second_opacity = rayPayload.opacity;
float first_rating = 1.0 - abs(first_dot);
float second_rating = 1.0 - abs(second_dot);
float avg_rating = 0.5 * (first_rating + second_rating);
float avg_opacity = 0.5 * (first_opacity + second_opacity);
float t = 1e30;
if(avg_opacity > 0)
t = avg_rating/avg_opacity;
shadow = t;
}
}
shadow = 1 - shadow;
}
// Restore the payload after we've traced some rays
rayPayload = r;
return shadow;
}
bool get_shadow(const vec3 light_pos, const vec3 normal, float shadow_sharpness)
{
vec3 lightVector = normalize(light_pos);
// Pseudorandomize the direction of the light
// in order to get blurry shadows
vec3 rdir = normalize(vec3(stepAndOutputRNGFloat(prng_state), stepAndOutputRNGFloat(prng_state), stepAndOutputRNGFloat(prng_state)));
// Stick to the correct hemisphere
if(dot(rdir, lightVector) < 0.0)
rdir = -rdir;
// Keep the shadows stay dynamic to some degree
// I mean, how blurry do you really need the edges to be?
if(shadow_sharpness < 0.5)
shadow_sharpness = 0.5;
lightVector = mix(rdir, lightVector, shadow_sharpness);
bool shadow = false;
if(dot(normal, lightVector) < 0.0)
{
shadow = true;
}
else
{
// Shadow casting
float tmin = 0.001;
float tmax = 1000.0;
vec3 origin = gl_WorldRayOriginEXT + gl_WorldRayDirectionEXT * gl_HitTEXT;
vec3 biased_origin = origin + normal * 0.01;
shadowed = true; // Make sure to set this to the default before tracing the ray!
traceRayEXT(topLevelAS, gl_RayFlagsTerminateOnFirstHitEXT | gl_RayFlagsOpaqueEXT | gl_RayFlagsSkipClosestHitShaderEXT, 0xFF, 0, 0, 1, biased_origin, tmin, lightVector, tmax, 2);
if(shadowed)
shadow = true;
else
shadow = false;
}
return shadow;
}
void main()
{
const ivec2 pixel_pos = ivec2(gl_LaunchIDEXT.xy);
const ivec2 res = ivec2(gl_LaunchSizeEXT.xy);
prng_state = res.x * pixel_pos.y + pixel_pos.x; // Each pixel gets its own unique seed
ivec3 index = ivec3(indices.i[3 * gl_PrimitiveID], indices.i[3 * gl_PrimitiveID + 1], indices.i[3 * gl_PrimitiveID + 2]);
Vertex v0 = unpack(index.x);
Vertex v1 = unpack(index.y);
Vertex v2 = unpack(index.z);
// Interpolate
const vec3 barycentricCoords = vec3(1.0f - attribs.x - attribs.y, attribs.x, attribs.y);
vec3 normal = normalize(v0.normal * barycentricCoords.x + v1.normal * barycentricCoords.y + v2.normal * barycentricCoords.z);
const vec3 pos = v0.pos * barycentricCoords.x + v1.pos * barycentricCoords.y + v2.pos * barycentricCoords.z;
const vec2 uv = v0.uv * barycentricCoords.x + v1.uv * barycentricCoords.y + v2.uv * barycentricCoords.z;
vec4 n = ubo.transformation_matrix*vec4(normal, 0.0);
rayPayload.reflector = 0.75;
rayPayload.opacity = 0.25;//pow(length(texture(normalSampler, uv).rgb) / sqrt(3.0), 1.0);
vec3 color = texture(baseColorSampler, uv).rgb;
rayPayload.pure_color = color;
rayPayload.distance = gl_RayTmaxEXT;
rayPayload.normal = n.xyz;
rayPayload.wro = gl_WorldRayOriginEXT;
rayPayload.wrd = gl_WorldRayDirectionEXT;
rayPayload.hitt = gl_HitTEXT;
rayPayload.color = vec3(0, 0, 0);
for (int i = 0; i < max_lights; i++)
{
float s = get_shadow_float(ubo.light_positions[i].xyz, rayPayload.normal, rayPayload.reflector);
float c = get_caustic_float(ubo.light_positions[i].xyz, rayPayload.normal, rayPayload.reflector);
rayPayload.color += s*phongModelDiffAndSpec(true, rayPayload.reflector, color, ubo.light_colors[i].rgb, ubo.light_positions[i].xyz, pos, rayPayload.normal);
rayPayload.color += c*phongModelDiffAndSpec(true, rayPayload.reflector, color, ubo.light_colors[i].rgb, ubo.light_positions[i].xyz, pos, rayPayload.normal);;
}
/* for (int i = 0; i < max_lights; i++)
if(false == get_shadow(ubo.light_positions[i].xyz, rayPayload.normal, rayPayload.reflector))
rayPayload.color += phongModelDiffAndSpec(true, rayPayload.reflector, color, ubo.light_colors[i].rgb, ubo.light_positions[i].xyz, pos, rayPayload.normal);
*/
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment