diff --git a/README.md b/README.md index 0ac9b2a..1466de2 100644 --- a/README.md +++ b/README.md @@ -12,13 +12,15 @@ An experimental pathtracer, implemented using WebGPU via the [Dawn](https://dawn - [Ray Tracing: The Rest of Your Life](https://raytracing.github.io/books/RayTracingTheRestOfYourLife.html) - Cosine-weighted hemisphere sampling method - [scratchapixel/moller-trumbore-ray-triangle-intersection](https://www.scratchapixel.com/lessons/3d-basic-rendering/ray-tracing-rendering-a-triangle/moller-trumbore-ray-triangle-intersection.html) -- _The Reference Path Tracer, Ray Tracing Gems II_ and associated code sample [boksajak/referencePT](https://github.com/boksajak/referencePT/) +- _The Reference Path Tracer_, _Ray Tracing Gems II_ and associated code sample [boksajak/referencePT](https://github.com/boksajak/referencePT/) - Rng initialization - [Crash Course in BRDF implementation](https://boksajak.github.io/files/CrashCourseBRDF.pdf) - BRDF interface - Lambertian BRDF, `sampleLambertian`, `evalLambertian` - [Building an Orthonormal Basis, Revisited](https://www.jcgt.org/published/0006/01/01/paper-lowres.pdf) - Robust orthonormal axis, implemented in the `pixarOnb` function +- _A Fast and Robust Method for Avoiding Self-Intersection_, _Ray Tracing Gems_ + - Method for the `offsetRay` function, for preventing ray self-intersections. - [ACES Filmic Tone Mapping Curve](https://knarkowicz.wordpress.com/2016/01/06/aces-filmic-tone-mapping-curve/) - filmic tonemapping function diff --git a/src/pt/raytracer.wgsl b/src/pt/raytracer.wgsl index 3e572da..a58f0e4 100644 --- a/src/pt/raytracer.wgsl +++ b/src/pt/raytracer.wgsl @@ -317,22 +317,18 @@ fn rayIntersectBvh(ray: Ray, rayTMax: f32, hit: ptr) -> var trihit: TriangleHit; if rayIntersectTriangle(ray, triangle, tmax, &trihit) { tmax = trihit.t; + didIntersect = true; let b = trihit.b; - - let p = trihit.p; - let triangleIdx = node.trianglesOffset + idx; let vert = vertexAttributes[triangleIdx]; + let p = trihit.p; let n = b[0] * vert.n0 + b[1] * vert.n1 + b[2] * vert.n2; - let uv = b[0] * vert.uv0 + b[1] * vert.uv1 + b[2] * vert.uv2; - let textureDescriptorIdx = vertexAttributes[triangleIdx].textureDescriptorIdx; *hit = Intersection(p, n, uv, textureDescriptorIdx); - didIntersect = true; } } if toVisitOffset == 0u { @@ -445,14 +441,35 @@ fn rayIntersectTriangle(ray: Ray, tri: Positions, tmax: f32, hit: ptr p = v0 + u * e1 + v * e2 let p = tri.p0 + u * e1 + v * e2; + let n = normalize(cross(e1, e2)); let b = vec3f(1f - u - v, u, v); - *hit = TriangleHit(p, b, t); + *hit = TriangleHit(offsetRay(p, n), b, t); return true; } else { return false; } } +const ORIGIN = 1f / 32f; +const FLOAT_SCALE = 1f / 65536f; +const INT_SCALE = 256f; + +fn offsetRay(p: vec3f, n: vec3f) -> vec3f { + // Source: A Fast and Robust Method for Avoiding Self-Intersection, Ray Tracing Gems + let offset = vec3i(i32(INT_SCALE * n.x), i32(INT_SCALE * n.y), i32(INT_SCALE * n.z)); + // Offset added straight into the mantissa bits to ensure the offset is scale-invariant, + // except for when close to the origin, where we use FLOAT_SCALE as a small epsilon. + let po = vec3f( + bitcast(bitcast(p.x) + select(offset.x, -offset.x, (p.x < 0))), + bitcast(bitcast(p.y) + select(offset.y, -offset.y, (p.y < 0))), + bitcast(bitcast(p.z) + select(offset.z, -offset.z, (p.z < 0))) + ); + + return vec3f(select(po.x, p.x + FLOAT_SCALE * n.x, (abs(p.x) < ORIGIN)), + select(po.y, p.y + FLOAT_SCALE * n.y, (abs(p.y) < ORIGIN)), + select(po.z, p.z + FLOAT_SCALE * n.z, (abs(p.z) < ORIGIN))); +} + struct TextureDescriptor { width: u32, height: u32,