diff --git a/interfaces/Crafter.Graphics-Mesh.cppm b/interfaces/Crafter.Graphics-Mesh.cppm index 4146912..4bb32b0 100644 --- a/interfaces/Crafter.Graphics-Mesh.cppm +++ b/interfaces/Crafter.Graphics-Mesh.cppm @@ -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` 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 aabbs, + bool opaque = false, + WebGPUCommandEncoderRef cmd = 0); }; } #endif // CRAFTER_GRAPHICS_WINDOW_DOM diff --git a/interfaces/Crafter.Graphics-RT.cppm b/interfaces/Crafter.Graphics-RT.cppm index 77be96d..8ae8fdd 100644 --- a/interfaces/Crafter.Graphics-RT.cppm +++ b/interfaces/Crafter.Graphics-RT.cppm @@ -60,24 +60,33 @@ 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, + 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 { - RTShaderGroupType type = RTShaderGroupType::General; - std::uint32_t generalShader = kRTShaderUnused; - std::uint32_t closestHitShader = kRTShaderUnused; - std::uint32_t anyHitShader = kRTShaderUnused; + RTShaderGroupType type = RTShaderGroupType::General; + std::uint32_t generalShader = kRTShaderUnused; + std::uint32_t closestHitShader = kRTShaderUnused; + std::uint32_t anyHitShader = kRTShaderUnused; + std::uint32_t intersectionShader = kRTShaderUnused; }; } diff --git a/interfaces/Crafter.Graphics-ShaderBindingTableWebGPU.cppm b/interfaces/Crafter.Graphics-ShaderBindingTableWebGPU.cppm index 73c2285..c161879 100644 --- a/interfaces/Crafter.Graphics-ShaderBindingTableWebGPU.cppm +++ b/interfaces/Crafter.Graphics-ShaderBindingTableWebGPU.cppm @@ -14,15 +14,23 @@ import std; export namespace Crafter { enum class WebGPURTStage : std::uint8_t { - Raygen = 0, - Miss = 1, - ClosestHit = 2, - AnyHit = 3, + Raygen = 0, + Miss = 1, + ClosestHit = 2, + AnyHit = 3, // Wavefront RESOLVE-stage tonemap/output hook. Optional: if no // Resolve shader is registered, RESOLVE writes the linear accum // buffer through unchanged. Signature: // fn (coord: vec2, hdr: vec4) -> vec4 - Resolve = 4, + 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 (ray: RayDesc, aabbMin: vec3, aabbMax: vec3, + // 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 (ray: RayDesc, hit: HitInfo, payload: ptr) // AnyHit: fn (ray: RayDesc, hit: HitInfo, payload: ptr) -> u32 // returns RT_ANYHIT_ACCEPT / RT_ANYHIT_IGNORE / RT_ANYHIT_END_SEARCH. + // Intersection: fn (ray: RayDesc, aabbMin: vec3, aabbMax: vec3, + // primitiveId: u32) -> IntersectionResult + // IntersectionResult{ hit: bool, t: f32, attribs: vec2, hitKind: u32 }. // // `RayDesc`, `HitInfo`, the `RT_*` flag/return constants, the `tlas` / // BLAS / mesh-record bindings, and the `traceRay` function are all diff --git a/interfaces/Crafter.Graphics-WebGPU.cppm b/interfaces/Crafter.Graphics-WebGPU.cppm index 9089bbd..2122cba 100644 --- a/interfaces/Crafter.Graphics-WebGPU.cppm +++ b/interfaces/Crafter.Graphics-WebGPU.cppm @@ -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` 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-