feat(webgpu-rt): any-hit + AABB (procedural) geometry support #14
4 changed files with 66 additions and 15 deletions
feat(webgpu-rt): add intersection stage, procedural hit group, AABB BLAS API
Extends the cross-backend RT type surface for procedural geometry + any-hit on the WebGPU path: - RTShaderGroupType::ProceduralHitGroup + RTShaderGroup::intersectionShader (mirror VK_RAY_TRACING_SHADER_GROUP_TYPE_PROCEDURAL_HIT_GROUP_KHR). - WebGPURTStage::Intersection for AABB intersection shaders. - Mesh::BuildProcedural(span<RTAabb>, opaque) — the WebGPU analog of a VK_GEOMETRY_TYPE_AABBS_KHR geometry. - wgpuRegisterMeshBLAS gains geomType / opaqueFlag / primCount. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
commit
321fe596a7
|
|
@ -87,6 +87,17 @@ export namespace Crafter {
|
|||
};
|
||||
static_assert(sizeof(BVHNode) == 32);
|
||||
|
||||
// One procedural primitive's axis-aligned bounding box, in object
|
||||
// space. The analog of VkAabbPositionsKHR — the BLAS stores these
|
||||
// instead of triangles and an intersection shader (registered in the
|
||||
// hit group as a ProceduralHitGroup) reports the actual surface hit
|
||||
// for each AABB the ray enters.
|
||||
struct RTAabb {
|
||||
float min[3];
|
||||
float max[3];
|
||||
};
|
||||
static_assert(sizeof(RTAabb) == 24);
|
||||
|
||||
class Mesh {
|
||||
public:
|
||||
// BLAS "handle": opaque identity that goes into
|
||||
|
|
@ -119,6 +130,19 @@ export namespace Crafter {
|
|||
// as `vertexAttribs : array<u32>` with a per-mesh u32-word offset.
|
||||
void Build(const ::Crafter::CompressedMeshAsset& asset,
|
||||
WebGPUCommandEncoderRef cmd = 0);
|
||||
|
||||
// Build an AABB (procedural) BLAS from a list of object-space boxes
|
||||
// — the WebGPU analog of a VK_GEOMETRY_TYPE_AABBS_KHR geometry. The
|
||||
// hit group bound to instances of this mesh must be a
|
||||
// ProceduralHitGroup carrying an intersection shader; that shader is
|
||||
// invoked for each box the ray enters and reports the surface hit.
|
||||
// `opaque` is the geometry's opaque bit: pass false to let any-hit
|
||||
// shaders run (the default for procedural geometry, which is usually
|
||||
// transparent / volumetric). The `cmd` parameter is unused on
|
||||
// WebGPU — kept for API symmetry with the triangle path.
|
||||
void BuildProcedural(std::span<const RTAabb> aabbs,
|
||||
bool opaque = false,
|
||||
WebGPUCommandEncoderRef cmd = 0);
|
||||
};
|
||||
}
|
||||
#endif // CRAFTER_GRAPHICS_WINDOW_DOM
|
||||
|
|
|
|||
|
|
@ -60,18 +60,26 @@ export namespace Crafter {
|
|||
inline constexpr std::uint8_t kRTGeometryInstanceForceOpaque = 0x4;
|
||||
inline constexpr std::uint8_t kRTGeometryInstanceForceNoOpaque = 0x8;
|
||||
|
||||
// Hit-group identification. Matches VkRayTracingShaderGroupTypeKHR for
|
||||
// the two types we actually support (general + triangles-hit).
|
||||
// Hit-group identification. Matches VkRayTracingShaderGroupTypeKHR.
|
||||
// General — raygen / miss / callable
|
||||
// TrianglesHitGroup — closest-hit/any-hit over triangle geometry
|
||||
// ProceduralHitGroup — closest-hit/any-hit + an intersection shader
|
||||
// over AABB (VK_GEOMETRY_TYPE_AABBS_KHR) geometry
|
||||
enum class RTShaderGroupType : std::uint8_t {
|
||||
General = 0, // raygen / miss / callable
|
||||
TrianglesHitGroup = 1,
|
||||
ProceduralHitGroup = 2,
|
||||
};
|
||||
|
||||
// Cross-backend description of one entry in the shader-group array
|
||||
// passed to PipelineRT::Init. Mirrors the meaningful subset of
|
||||
// VkRayTracingShaderGroupCreateInfoKHR: per group, the type and the
|
||||
// indices (into the SBT's shader array) for general / closestHit /
|
||||
// anyHit, with kRTShaderUnused == VK_SHADER_UNUSED_KHR for "none".
|
||||
// anyHit / intersection, with kRTShaderUnused == VK_SHADER_UNUSED_KHR
|
||||
// for "none". `intersectionShader` is only consulted for
|
||||
// ProceduralHitGroup; it names the shader run for each AABB the ray
|
||||
// enters (the analog of VkRayTracingShaderGroupCreateInfoKHR::
|
||||
// intersectionShader).
|
||||
inline constexpr std::uint32_t kRTShaderUnused = 0xFFFFFFFFu;
|
||||
|
||||
struct RTShaderGroup {
|
||||
|
|
@ -79,5 +87,6 @@ export namespace Crafter {
|
|||
std::uint32_t generalShader = kRTShaderUnused;
|
||||
std::uint32_t closestHitShader = kRTShaderUnused;
|
||||
std::uint32_t anyHitShader = kRTShaderUnused;
|
||||
std::uint32_t intersectionShader = kRTShaderUnused;
|
||||
};
|
||||
}
|
||||
|
|
|
|||
|
|
@ -23,6 +23,14 @@ export namespace Crafter {
|
|||
// buffer through unchanged. Signature:
|
||||
// fn <entryFn>(coord: vec2<u32>, hdr: vec4<f32>) -> vec4<f32>
|
||||
Resolve = 4,
|
||||
// Intersection shader for AABB (procedural) geometry. Run for each
|
||||
// AABB primitive the ray enters during TRACE; reports whether the
|
||||
// procedural surface is hit and at what distance. Signature:
|
||||
// fn <entryFn>(ray: RayDesc, aabbMin: vec3<f32>, aabbMax: vec3<f32>,
|
||||
// primitiveId: u32) -> IntersectionResult
|
||||
// `ray` is in object space. IntersectionResult{ hit, t, attribs,
|
||||
// hitKind } is declared by the library prelude.
|
||||
Intersection = 5,
|
||||
};
|
||||
|
||||
// One WGSL shader source + the function name PipelineRTWebGPU should
|
||||
|
|
@ -35,6 +43,9 @@ export namespace Crafter {
|
|||
// ClosestHit: fn <entryFn>(ray: RayDesc, hit: HitInfo, payload: ptr<function, Payload>)
|
||||
// AnyHit: fn <entryFn>(ray: RayDesc, hit: HitInfo, payload: ptr<function, Payload>) -> u32
|
||||
// returns RT_ANYHIT_ACCEPT / RT_ANYHIT_IGNORE / RT_ANYHIT_END_SEARCH.
|
||||
// Intersection: fn <entryFn>(ray: RayDesc, aabbMin: vec3<f32>, aabbMax: vec3<f32>,
|
||||
// primitiveId: u32) -> IntersectionResult
|
||||
// IntersectionResult{ hit: bool, t: f32, attribs: vec2<f32>, hitKind: u32 }.
|
||||
//
|
||||
// `RayDesc`, `HitInfo`, the `RT_*` flag/return constants, the `tlas` /
|
||||
// BLAS / mesh-record bindings, and the `traceRay` function are all
|
||||
|
|
|
|||
|
|
@ -167,6 +167,12 @@ namespace Crafter::WebGPU {
|
|||
// that gets appended to a global attribs heap and exposed to RT
|
||||
// closest-hit shaders as `vertexAttribs : array<u32>` at
|
||||
// @group(1) @binding(7). Pass (nullptr, 0) for positions-only meshes.
|
||||
// `geomType` selects the primitive kind: 0 = triangles (the
|
||||
// verticesPtr/indicesPtr streams), 1 = AABBs (VK_GEOMETRY_TYPE_AABBS) —
|
||||
// then verticesPtr holds 2 vec3 per primitive [min, max], indexCount is
|
||||
// 0, and an intersection shader supplies the hit. `opaqueFlag` is the
|
||||
// geometry's opaque bit (0 lets any-hit run). `primCount` is the
|
||||
// triangle / AABB primitive count.
|
||||
__attribute__((import_module("env"), import_name("wgpuRegisterMeshBLAS")))
|
||||
extern "C" std::uint32_t wgpuRegisterMeshBLAS(
|
||||
float minX, float minY, float minZ,
|
||||
|
|
@ -175,7 +181,8 @@ namespace Crafter::WebGPU {
|
|||
const void* indicesPtr, std::int32_t indexCount,
|
||||
const void* bvhNodesPtr, std::int32_t bvhNodeCount,
|
||||
const void* primRemapPtr, std::int32_t primRemapCount,
|
||||
const void* attribsPtr, std::int32_t attribsByteCount);
|
||||
const void* attribsPtr, std::int32_t attribsByteCount,
|
||||
std::int32_t geomType, std::int32_t opaqueFlag, std::int32_t primCount);
|
||||
|
||||
// RT pipeline build. The library composes WGSL by concatenating the
|
||||
// traversal library, generated hit-group switches, and the user-
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue