webgpu-pt

monte carlo path tracer
Contents

any_hit.wgsl

4.7 kB
  1fn is_occluded(pos: vec3f, normal: vec3f, light_dir: vec3f, light_distance: f32) -> bool {
  2    var shadow_ray: Ray;
  3    shadow_ray.origin = offset_ray(pos, normal);
  4    shadow_ray.direction = light_dir;
  5    var shadow_hit = trace_any(shadow_ray, light_distance);
  6    return shadow_hit;
  7}
  8
  9fn trace_any(ray: Ray, t_max: f32) -> bool {
 10    var node_idx_stack: array<u32, 64>;
 11    var stack_ptr: i32 = 0;
 12    var current_node_idx: u32 = 0;
 13
 14    while (true) {
 15        let node = node_tree.nodes[current_node_idx];
 16
 17        let primitive_count = u32(node.primitive_count);
 18        let child_or_prim_idx = u32(node.left_child);
 19        if (primitive_count == 0u) {
 20            // internal node
 21            let left_child_idx = child_or_prim_idx;
 22            let right_child_idx = child_or_prim_idx + 1u;
 23
 24            // use t_max for pruning
 25            let hit1 = any_hit_aabb(ray, node_tree.nodes[left_child_idx].min_corner, node_tree.nodes[left_child_idx].max_corner, t_max);
 26            let hit2 = any_hit_aabb(ray, node_tree.nodes[right_child_idx].min_corner, node_tree.nodes[right_child_idx].max_corner, t_max);
 27
 28            var near_child_idx = left_child_idx;
 29            var far_child_idx = right_child_idx;
 30            var dist1_hit = hit1;
 31            var dist2_hit = hit2;
 32
 33            if (!hit1 && hit2) {
 34                near_child_idx = right_child_idx;
 35                far_child_idx = left_child_idx;
 36                dist1_hit = hit2;
 37                dist2_hit = hit1;
 38            }
 39
 40            if (dist1_hit) {
 41                current_node_idx = near_child_idx;
 42                if (dist2_hit) {
 43                    if (stack_ptr >= 64) {
 44                        break;
 45                    }
 46                    // overflow
 47                    node_idx_stack[stack_ptr] = far_child_idx;
 48                    stack_ptr += 1;
 49                }
 50                continue;
 51                // descend into near child
 52            }
 53            // neither child is relevant, fall through to pop
 54
 55        }
 56        else {
 57            // leaf node
 58            for (var i = 0u; i < primitive_count; i += 1u) {
 59                let prim_index = tri_lut.primitive_indices[child_or_prim_idx + i];
 60                let triangle = objects.triangles[i32(prim_index)];
 61
 62                // any_hit_triangle returns true if hit within range
 63                if (any_hit_triangle(ray, triangle, 0.001, t_max)) {
 64                    return true;
 65                    // found an occlusion, exit immediately
 66                }
 67            }
 68            // finished leaf without finding occlusion, fall through to pop
 69        }
 70
 71        // pop from stack or break if empty
 72        if (stack_ptr == 0) {
 73            break;
 74            // traversal finished without finding occlusion
 75        }
 76        else {
 77            stack_ptr -= 1;
 78            current_node_idx = node_idx_stack[stack_ptr];
 79        }
 80    }
 81    // kill sunlight 0.0
 82    let floor_z = 0.0;
 83    let denom = ray.direction.z;
 84    if (abs(denom) > 1e-6) {
 85        let t = (floor_z - ray.origin.z) / denom;
 86        if (t > 0.001 && t < t_max) {
 87            return true;
 88            // hit floor within range
 89        }
 90    }
 91    return false;
 92    // no occlusion found
 93}
 94
 95fn any_hit_aabb(ray: Ray, aabb_min: vec3f, aabb_max: vec3f, t_max: f32) -> bool {
 96    var inverse_dir: vec3<f32> = vec3(1.0) / ray.direction;
 97    var tmin = (aabb_min - ray.origin) * inverse_dir;
 98    var tmax = (aabb_max - ray.origin) * inverse_dir;
 99    var t1 = min(tmin, tmax);
100    var t2 = max(tmin, tmax);
101    var t_near = max(max(t1.x, t1.y), t1.z);
102    var t_far = min(min(t2.x, t2.y), t2.z);
103    return t_near <= t_far && t_far >= 0.001 && t_near <= t_max;
104}
105
106// lazy, just clean this up to only use hit_triangle and move all shading data post hit out
107fn any_hit_triangle(ray: Ray, tri: Triangle, dist_min: f32, dist_max: f32) -> bool {
108    let edge1 = tri.corner_b - tri.corner_a;
109    let edge2 = tri.corner_c - tri.corner_a;
110
111    let pvec = cross(ray.direction, edge2);
112    let determinant = dot(edge1, pvec);
113
114    // reject nearly parallel rays.
115    if abs(determinant) < EPSILON {
116        return false;
117    }
118
119    let inv_det = 1.0 / determinant;
120    let tvec = ray.origin - tri.corner_a;
121
122    // compute barycentric coordinate u.
123    let u = dot(tvec, pvec) * inv_det;
124    if (u < 0.0 || u > 1.0) {
125        return false;
126    }
127
128    // compute barycentric coordinate v.
129    let qvec = cross(tvec, edge1);
130    let v = dot(ray.direction, qvec) * inv_det;
131    if (v < 0.0 || (u + v) > 1.0) {
132        return false;
133    }
134
135    // calculate ray parameter (distance).
136    let dist = dot(edge2, qvec) * inv_det;
137    return dist > dist_min && dist < dist_max;
138}