diff --git a/examples/src/bin/nv-ray-tracing.rs b/examples/src/bin/nv-ray-tracing.rs index b63e25c4e6..8a002010b0 100644 --- a/examples/src/bin/nv-ray-tracing.rs +++ b/examples/src/bin/nv-ray-tracing.rs @@ -40,6 +40,7 @@ use winit::event_loop::{ControlFlow, EventLoop}; use winit::window::WindowBuilder; use std::sync::Arc; +use std::time::Instant; fn main() { let required_extensions = vulkano_win::required_extensions(); @@ -68,6 +69,7 @@ fn main() { khr_swapchain: true, khr_get_memory_requirements2: true, nv_ray_tracing: true, + khr_storage_buffer_storage_class: true, ..DeviceExtensions::none() }; let (device, mut queues) = Device::new( @@ -133,7 +135,10 @@ fn main() { ] .iter() .cloned(), - BufferUsage::ray_tracing(), + BufferUsage { + storage_buffer: true, + ..BufferUsage::none() + }, queue.clone(), ) .unwrap() @@ -153,7 +158,13 @@ fn main() { layout(set = 0, binding = 0, rgba8) uniform image2D result; layout(set = 0, binding = 1) uniform accelerationStructureNV scene; -layout(location = 0) rayPayloadNV vec4 payload; + +struct payload_t { + vec3 color; + uint recursion_depth; +}; + +layout(location = 0) rayPayloadNV payload_t payload; void main() { ivec2 coord = ivec2(gl_LaunchIDNV); @@ -167,8 +178,9 @@ void main() { vec3 origin = vec3(0.0, 0.0, 0.0); vec3 direction = normalize(lower_left_corner + inUV.x * horizontal + inUV.y * vertical); + payload.recursion_depth = 0; traceNV(scene, gl_RayFlagsOpaqueNV, 0xFF, 0, 0, 0, origin, 0.001, direction, 1000.0, 0); - imageStore(result, coord, payload); + imageStore(result, coord, vec4(payload.color, 1.0)); } " } @@ -181,12 +193,17 @@ void main() { src: "#version 460 core #extension GL_NV_ray_tracing : enable -layout(location = 0) rayPayloadInNV vec4 payload; +struct payload_t { + vec3 color; + uint recursion_depth; +}; + +layout(location = 0) rayPayloadInNV payload_t payload; void main() { vec3 unit_direction = normalize(gl_WorldRayDirectionNV); float t = 0.5 * (unit_direction.y + 1.0); - payload = vec4(mix(vec3(0.5, 0.7, 1.0), vec3(1.0, 1.0, 1.0), t), 1.0f); + payload.color = mix(vec3(0.5, 0.7, 1.0), vec3(1.0, 1.0, 1.0), t); } " } @@ -199,10 +216,29 @@ void main() { src: "#version 460 core #extension GL_NV_ray_tracing : enable -layout(location = 0) rayPayloadInNV vec4 payload; +layout(set = 0, binding = 1) uniform accelerationStructureNV scene; + +struct payload_t { + vec3 color; + uint recursion_depth; +}; + +layout(location = 0) rayPayloadInNV payload_t payload; + +struct hit_record_t { + vec3 position; + vec3 normal; +}; +hitAttributeNV hit_record_t hit_record; void main() { - payload = vec4(1, 1, 0, 1); + payload.recursion_depth++; + if (payload.recursion_depth < 15) { + vec3 target = reflect(gl_WorldRayDirectionNV, hit_record.normal); + vec3 origin = hit_record.position + 0.001 * hit_record.normal; + traceNV(scene, gl_RayFlagsOpaqueNV, 0xFF, 0, 0, 0, origin, 0.001, target, 1000.0, 0); + payload.color *= 0.9f; + } } " } @@ -215,10 +251,101 @@ void main() { src: "#version 460 core #extension GL_NV_ray_tracing : enable -hitAttributeNV bool _unused_but_required; +struct Aabb { + float min_x; + float min_y; + float min_z; + float max_x; + float max_y; + float max_z; +}; + +layout(set = 0, binding = 2) readonly buffer AabbArray { Aabb[] aabbs; }; + +layout(push_constant) uniform PushConstants { + float time; +} push_constants; + +struct hit_record_t { + vec3 position; + vec3 normal; +}; +hitAttributeNV hit_record_t hit_record; + +const uint MAX_STEPS = 100; +const uint MAX_REFINE_STEPS = 4; +const float MIN_DISTANCE = 1e-6f; +const float MIN_STEP_SIZE = 0.5f; +float CUBE_SIDES = 0.45f; +float SPHERE_RADIUS = 0.55f; +float GRAD_STEP = 0.01f; + +float sphere_sdf(vec3 point, float radius) { + return length(point) - radius; +} + +float box_sdf(vec3 point, vec3 sides) { + vec3 q = abs(point) - sides; + return length(max(q, 0.0)) + min(max(q.x, max(q.y, q.z)), 0.0); +} + +vec3 twist_point(vec3 point, float amount) { + float c = cos(amount * point.y); + float s = sin(amount * point.y); + mat2 m = mat2(c, -s, s, c); + return vec3(m * point.xz, point.y); +} + +float signed_distance_function(vec3 point, vec3 size) { + vec3 twisted = twist_point(point, sin(push_constants.time) * (2 * gl_PrimitiveID - 1)); + return max(box_sdf(twisted, size * CUBE_SIDES), -sphere_sdf(point, min(size.x, min(size.y, size.z)) * SPHERE_RADIUS)); +} void main() { - reportIntersectionNV(0.01, 0); + vec3 aabb_min = vec3(aabbs[gl_PrimitiveID].min_x, aabbs[gl_PrimitiveID].min_y, aabbs[gl_PrimitiveID].min_z); + vec3 aabb_max = vec3(aabbs[gl_PrimitiveID].max_x, aabbs[gl_PrimitiveID].max_y, aabbs[gl_PrimitiveID].max_z); + vec3 center = (aabb_min + aabb_max) * 0.5; + vec3 size = aabb_max - aabb_min; + + float t = gl_RayTminNV; // TODO: start stepping at bounding box + float t_max = gl_RayTmaxNV; // TODO: stop stepping when out of the bb + float distance = gl_RayTmaxNV; + float step_size = 0.0f; + // Ray march + for (uint i = 0; i < MAX_STEPS; ++i) { + distance = signed_distance_function(gl_ObjectRayOriginNV + gl_ObjectRayDirectionNV * t - center, size); + // Ray has marched close enough to object, register a hit here + if (distance < MIN_DISTANCE) { + break; + } + // Not close enough, step forward + step_size = min(abs(distance), MIN_STEP_SIZE); + t += step_size; + // Ray has marched too far without any hits, register no hits + if (t > t_max) { + break; + } + } + // Ray has hit something + if (distance < MIN_DISTANCE) { + // refine the value for t by stepping back and taking smaller steps + t -= step_size; + for (uint i = 0; i < MAX_REFINE_STEPS; ++i) { + step_size *= 0.5; + distance = signed_distance_function(gl_ObjectRayOriginNV + gl_ObjectRayDirectionNV * t - center, size); + if (distance >= MIN_DISTANCE) { + t += step_size; + } + } + vec3 dx = vec3(GRAD_STEP, 0.0, 0.0); + vec3 dy = vec3(0.0, GRAD_STEP, 0.0); + vec3 dz = vec3(0.0, 0.0, GRAD_STEP); + hit_record.position = gl_ObjectRayOriginNV + gl_ObjectRayDirectionNV * t - center; + hit_record.normal = normalize(vec3(signed_distance_function(hit_record.position + dx, size) - signed_distance_function(hit_record.position - dx, size), + signed_distance_function(hit_record.position + dy, size) - signed_distance_function(hit_record.position - dy, size), + signed_distance_function(hit_record.position + dz, size) - signed_distance_function(hit_record.position - dz, size))); + reportIntersectionNV(t, 0); + } } " } @@ -226,7 +353,7 @@ void main() { let is = is::Shader::load(device.clone()).unwrap(); // We set a limit to the recursion of a ray so that the shader does not run infinitely - let max_recursion_depth = 5; + let max_recursion_depth = 15; let pipeline = Arc::new( RayTracingPipeline::nv(max_recursion_depth) @@ -260,6 +387,8 @@ void main() { .unwrap() .add_acceleration_structure(acceleration_structure.clone()) .unwrap() + .add_buffer(aabb_buffer.clone()) + .unwrap() .build() .unwrap(), ) @@ -313,6 +442,7 @@ void main() { let mut recreate_swapchain = false; let mut previous_frame_end = Some(Box::new(sync::now(device.clone())) as Box); + let time_start = Instant::now(); event_loop.run(move |event, _, control_flow| match event { Event::WindowEvent { event: WindowEvent::CloseRequested, @@ -349,6 +479,8 @@ void main() { .unwrap() .add_acceleration_structure(acceleration_structure.clone()) .unwrap() + .add_buffer(aabb_buffer.clone()) + .unwrap() .build() .unwrap(), ) @@ -384,7 +516,12 @@ void main() { callable_shader_binding_table.clone(), [swapchain.dimensions()[0], swapchain.dimensions()[1], 1], sets[image_num].clone(), - (), + is::ty::PushConstants { + time: { + let elapsed = time_start.elapsed(); + elapsed.as_secs() as f32 + elapsed.subsec_nanos() as f32 / 1e9 + }, + }, ) .unwrap() .build()