| … | … | @@ -726,9 +726,14 @@ void RenderRaytracing::_populate_surface_blas( |
| 726 | 726 | } |
| 727 | 727 | for (int ci = 0; ci < RSE::ARRAY_CUSTOM_COUNT; ci++) { |
| 728 | 728 | const uint32_t fmt_shift[RSE::ARRAY_CUSTOM_COUNT] = { RSE::ARRAY_FORMAT_CUSTOM0_SHIFT, RSE::ARRAY_FORMAT_CUSTOM1_SHIFT, RSE::ARRAY_FORMAT_CUSTOM2_SHIFT, RSE::ARRAY_FORMAT_CUSTOM3_SHIFT }; |
| 729 | geom.custom_packed[ci] = RT_OFFSET_NONE; | |
| 729 | 730 | if (surface_format & (1ULL << (RSE::ARRAY_CUSTOM0 + ci))) { |
| 730 | 731 | uint32_t fmt = (surface_format >> fmt_shift[ci]) & RSE::ARRAY_FORMAT_CUSTOM_MASK; |
| 731 | 732 | const uint32_t fmtsize[RSE::ARRAY_CUSTOM_MAX] = { 4, 4, 4, 8, 4, 8, 12, 16 }; |
| 733 | // The offset was already being walked to size the stride; record it so | |
| 734 | // hit shaders can actually read CUSTOM0-3 (shaders referencing them | |
| 735 | // previously failed to compile on 'custom0_attrib' undeclared). | |
| 736 | geom.custom_packed[ci] = (attrib_offset & 0x00FFFFFFu) | (fmt << 24); | |
| 732 | 737 | attrib_offset += fmtsize[fmt]; |
| 733 | 738 | } |
| 734 | 739 | } |
| … | … | @@ -83,7 +83,13 @@ struct alignas(16) RT_GeometryData { |
| 83 | 83 | // For deformed geometry: previous-frame position buffer used for motion vectors. |
| 84 | 84 | uint32_t prev_vertex_buffer_address_lo; |
| 85 | 85 | uint32_t prev_vertex_buffer_address_hi; |
| 86 | uint32_t _pad[5]; | |
| 86 | // CUSTOM0-3 attribute location, one packed word each: | |
| 87 | // bits 0-23 byte offset inside attribute_stride | |
| 88 | // bits 24-31 ARRAY_CUSTOM_* format | |
| 89 | // RT_OFFSET_NONE (all bits set) when the surface lacks that custom array -- | |
| 90 | // unambiguous because format 0xFF is not a valid ARRAY_CUSTOM_* value. | |
| 91 | uint32_t custom_packed[4]; | |
| 92 | uint32_t _pad[1]; | |
| 87 | 93 | }; |
| 88 | 94 | static_assert(sizeof(RT_GeometryData) == 128, "RT_GeometryData must be 128 bytes for std430"); |
| 89 | 95 |
| … | … | @@ -20,6 +20,7 @@ struct HitData { |
| 20 | 20 | vec3 bitangent; // World space. |
| 21 | 21 | vec2 uv; // Raw UV (no material scale/offset applied). |
| 22 | 22 | vec4 color; // Vertex color (white if not present). |
| 23 | vec4 custom[4]; // CUSTOM0-3 (zero if not present); only filled for custom HGs. | |
| 23 | 24 | bool is_front_face; |
| 24 | 25 | uint geometry_idx; |
| 25 | 26 | }; |
| … | … | @@ -40,6 +41,7 @@ HitData compute_hit_data() { |
| 40 | 41 | #endif |
| 41 | 42 | h.uv = attrs.uv; |
| 42 | 43 | h.color = attrs.color; |
| 44 | h.custom = attrs.custom; | |
| 43 | 45 | |
| 44 | 46 | mat3 model_rotation = mat3(gl_ObjectToWorldEXT); |
| 45 | 47 | mat3 normal_matrix = mat3( |
| … | … | @@ -9,6 +9,7 @@ |
| 9 | 9 | // vec3 rt_tangent -- world-space tangent |
| 10 | 10 | // vec3 rt_bitangent -- world-space bitangent |
| 11 | 11 | // bool rt_front_face -- true if front-face hit |
| 12 | // vec4 rt_custom0..3 -- CUSTOM0-3 vertex attributes (vec4(0) if absent) | |
| 12 | 13 | // |
| 13 | 14 | // Required bindings/types: |
| 14 | 15 | // materials[], CustomMaterialUniforms, scene_data_block |
| … | … | @@ -47,6 +48,10 @@ binormal = mat3(rt_world_to_object_decomp) * rt_bitangent; |
| 47 | 48 | uv_interp = rt_uv; |
| 48 | 49 | uv2_interp = rt_uv; |
| 49 | 50 | color_interp = rt_color; |
| 51 | custom0_attrib = rt_custom0; | |
| 52 | custom1_attrib = rt_custom1; | |
| 53 | custom2_attrib = rt_custom2; | |
| 54 | custom3_attrib = rt_custom3; | |
| 50 | 55 | view = -gl_WorldRayDirectionEXT; |
| 51 | 56 | rt_front_facing = rt_front_face; |
| 52 | 57 | rt_screen_uv = vec2(gl_LaunchIDEXT.xy) / vec2(gl_LaunchSizeEXT.xy); |
| … | … | @@ -17,6 +17,14 @@ vec3 binormal = vec3(0.0, 1.0, 0.0); |
| 17 | 17 | vec2 uv_interp = vec2(0.0); |
| 18 | 18 | vec2 uv2_interp = vec2(0.0); |
| 19 | 19 | vec4 color_interp = vec4(1.0); |
| 20 | // ShaderCompiler renames CUSTOM0-3 to these (see actions.renames in | |
| 21 | // SceneShaderRaytracing), so they must exist even when the surface has no such | |
| 22 | // array -- otherwise any shader touching CUSTOM0 fails to compile and takes its | |
| 23 | // whole hit group down. | |
| 24 | vec4 custom0_attrib = vec4(0.0); | |
| 25 | vec4 custom1_attrib = vec4(0.0); | |
| 26 | vec4 custom2_attrib = vec4(0.0); | |
| 27 | vec4 custom3_attrib = vec4(0.0); | |
| 20 | 28 | vec3 view = vec3(0.0, 0.0, -1.0); |
| 21 | 29 | mat4 read_model_matrix = mat4(1.0); |
| 22 | 30 | mat4 read_view_matrix = mat4(1.0); |
| … | … | @@ -48,7 +48,10 @@ struct GeometryData { |
| 48 | 48 | uint prev_vertex_address_lo; |
| 49 | 49 | uint prev_vertex_address_hi; |
| 50 | 50 | |
| 51 | uint _pad[5]; | |
| 51 | // CUSTOM0-3 location: bits 0-23 byte offset in attribute_stride, bits 24-31 | |
| 52 | // ARRAY_CUSTOM_* format. OFFSET_NONE when the surface has no such array. | |
| 53 | uint custom_packed[4]; | |
| 54 | uint _pad[1]; | |
| 52 | 55 | }; |
| 53 | 56 | |
| 54 | 57 | void get_aabb_compression_xforms(GeometryData geom, out mat4 aabb_xform, out mat4 inv_aabb_xform) { |
| … | … | @@ -12,6 +12,7 @@ struct VertexAttributes { |
| 12 | 12 | vec3 tangent; |
| 13 | 13 | float bitangent_sign; |
| 14 | 14 | vec4 color; |
| 15 | vec4 custom[4]; // CUSTOM0-3; vec4(0) when the surface has no such array. | |
| 15 | 16 | bool has_uv; |
| 16 | 17 | bool has_normal; |
| 17 | 18 | bool has_tangent; |
| … | … | @@ -130,6 +131,59 @@ vec4 fetch_color(in GeometryData geom, uint i0, uint i1, uint i2, vec3 bary) { |
| 130 | 131 | return bary.x * col0 + bary.y * col1 + bary.z * col2; |
| 131 | 132 | } |
| 132 | 133 | |
| 134 | // ============================================================================ | |
| 135 | // CUSTOM0-3 FETCHING | |
| 136 | // ============================================================================ | |
| 137 | // Decodes one vertex's ARRAY_CUSTOM_* value to vec4. Formats and their sizes | |
| 138 | // match the fmtsize table in RenderRaytracing::process_surface. | |
| 139 | vec4 decode_custom_vertex(in GeometryData geom, uint fmt, uint word_base) { | |
| 140 | Uint32Buffer attr = Uint32Buffer(geom.attribute_address); | |
| 141 | FloatBuffer attr_f = FloatBuffer(geom.attribute_address); | |
| 142 | ||
| 143 | switch (fmt) { | |
| 144 | case 0u: // RGBA8_UNORM | |
| 145 | return unpackUnorm4x8(attr.v[word_base]); | |
| 146 | case 1u: // RGBA8_SNORM | |
| 147 | return unpackSnorm4x8(attr.v[word_base]); | |
| 148 | case 2u: // RG_HALF | |
| 149 | return vec4(unpackHalf2x16(attr.v[word_base]), 0.0, 0.0); | |
| 150 | case 3u: // RGBA_HALF | |
| 151 | return vec4(unpackHalf2x16(attr.v[word_base]), unpackHalf2x16(attr.v[word_base + 1u])); | |
| 152 | case 4u: // R_FLOAT | |
| 153 | return vec4(attr_f.v[word_base], 0.0, 0.0, 0.0); | |
| 154 | case 5u: // RG_FLOAT | |
| 155 | return vec4(attr_f.v[word_base], attr_f.v[word_base + 1u], 0.0, 0.0); | |
| 156 | case 6u: // RGB_FLOAT | |
| 157 | return vec4(attr_f.v[word_base], attr_f.v[word_base + 1u], attr_f.v[word_base + 2u], 0.0); | |
| 158 | default: // 7u RGBA_FLOAT | |
| 159 | return vec4(attr_f.v[word_base], attr_f.v[word_base + 1u], | |
| 160 | attr_f.v[word_base + 2u], attr_f.v[word_base + 3u]); | |
| 161 | } | |
| 162 | } | |
| 163 | ||
| 164 | // Barycentric, matching fetch_uv/fetch_color. A shader may declare its | |
| 165 | // CUSTOM-derived varying `flat` (per-face data), but there is no rasterizer | |
| 166 | // interpolation in the RT path -- the hit shader runs once at the hit point -- | |
| 167 | // and per-face data stored on a face's own vertices interpolates back to that | |
| 168 | // same constant. | |
| 169 | vec4 fetch_custom(in GeometryData geom, uint custom_index, uint i0, uint i1, uint i2, vec3 bary) { | |
| 170 | uint packed = geom.custom_packed[custom_index]; | |
| 171 | if (packed == OFFSET_NONE || geom.attribute_address == 0ul) { | |
| 172 | return vec4(0.0); | |
| 173 | } | |
| 174 | ||
| 175 | uint byte_off = packed & 0x00FFFFFFu; | |
| 176 | uint fmt = packed >> 24; | |
| 177 | uint stride_words = geom.attribute_stride >> 2; | |
| 178 | uint word_off = byte_off >> 2; | |
| 179 | ||
| 180 | vec4 c0 = decode_custom_vertex(geom, fmt, i0 * stride_words + word_off); | |
| 181 | vec4 c1 = decode_custom_vertex(geom, fmt, i1 * stride_words + word_off); | |
| 182 | vec4 c2 = decode_custom_vertex(geom, fmt, i2 * stride_words + word_off); | |
| 183 | ||
| 184 | return bary.x * c0 + bary.y * c1 + bary.z * c2; | |
| 185 | } | |
| 186 | ||
| 133 | 187 | // ============================================================================ |
| 134 | 188 | // TBN FETCHING |
| 135 | 189 | // ============================================================================ |
| … | … | @@ -231,7 +285,8 @@ TBNResult fetch_tbn(in GeometryData geom, uint i0, uint i1, uint i2, vec3 bary) |
| 231 | 285 | #define FETCH_UV (1u << 0) |
| 232 | 286 | #define FETCH_TBN (1u << 1) |
| 233 | 287 | #define FETCH_COLOR (1u << 2) |
| 234 | #define FETCH_ALL (FETCH_UV | FETCH_TBN | FETCH_COLOR) | |
| 288 | #define FETCH_CUSTOM (1u << 3) | |
| 289 | #define FETCH_ALL (FETCH_UV | FETCH_TBN | FETCH_COLOR | FETCH_CUSTOM) | |
| 235 | 290 | |
| 236 | 291 | #ifdef RT_HIT_ATTRIBS_DECLARED |
| 237 | 292 | VertexAttributes fetch_vertex_attributes(in GeometryData geom, vec2 hit_attribs, uint fetch_flags) { |
| … | … | @@ -241,6 +296,10 @@ VertexAttributes fetch_vertex_attributes(in GeometryData geom, vec2 hit_attribs, |
| 241 | 296 | attrs.tangent = vec3(1.0, 0.0, 0.0); |
| 242 | 297 | attrs.bitangent_sign = 1.0; |
| 243 | 298 | attrs.color = vec4(1.0); |
| 299 | attrs.custom[0] = vec4(0.0); | |
| 300 | attrs.custom[1] = vec4(0.0); | |
| 301 | attrs.custom[2] = vec4(0.0); | |
| 302 | attrs.custom[3] = vec4(0.0); | |
| 244 | 303 | attrs.has_uv = false; |
| 245 | 304 | attrs.has_normal = false; |
| 246 | 305 | attrs.has_tangent = false; |
| … | … | @@ -261,6 +320,15 @@ VertexAttributes fetch_vertex_attributes(in GeometryData geom, vec2 hit_attribs, |
| 261 | 320 | attrs.has_color = true; |
| 262 | 321 | } |
| 263 | 322 | |
| 323 | // fetch_custom early-outs per index, so a surface with no custom arrays pays | |
| 324 | // four compares rather than four buffer reads. | |
| 325 | if ((fetch_flags & FETCH_CUSTOM) != 0u) { | |
| 326 | attrs.custom[0] = fetch_custom(geom, 0u, i0, i1, i2, bary); | |
| 327 | attrs.custom[1] = fetch_custom(geom, 1u, i0, i1, i2, bary); | |
| 328 | attrs.custom[2] = fetch_custom(geom, 2u, i0, i1, i2, bary); | |
| 329 | attrs.custom[3] = fetch_custom(geom, 3u, i0, i1, i2, bary); | |
| 330 | } | |
| 331 | ||
| 264 | 332 | if ((fetch_flags & FETCH_TBN) != 0u && geom.normal_byte_offset != OFFSET_NONE) { |
| 265 | 333 | TBNResult tbn = fetch_tbn(geom, i0, i1, i2, bary); |
| 266 | 334 | attrs.normal = tbn.normal; |
| … | … | @@ -327,6 +327,10 @@ void main() { |
| 327 | 327 | vec3 rt_hit_pos = h.hit_pos; |
| 328 | 328 | vec2 rt_uv = h.uv; |
| 329 | 329 | vec4 rt_color = h.color; |
| 330 | vec4 rt_custom0 = h.custom[0]; | |
| 331 | vec4 rt_custom1 = h.custom[1]; | |
| 332 | vec4 rt_custom2 = h.custom[2]; | |
| 333 | vec4 rt_custom3 = h.custom[3]; | |
| 330 | 334 | vec3 rt_normal = h.geometry_normal; |
| 331 | 335 | vec3 rt_tangent = h.tangent; |
| 332 | 336 | vec3 rt_bitangent = h.bitangent; |
| … | … | @@ -525,6 +529,10 @@ void main() { |
| 525 | 529 | |
| 526 | 530 | vec3 rt_hit_pos = gl_WorldRayOriginEXT + gl_WorldRayDirectionEXT * gl_HitTEXT; |
| 527 | 531 | vec4 rt_color = fetch_color(geom, i0, i1, i2, bary); |
| 532 | vec4 rt_custom0 = fetch_custom(geom, 0u, i0, i1, i2, bary); | |
| 533 | vec4 rt_custom1 = fetch_custom(geom, 1u, i0, i1, i2, bary); | |
| 534 | vec4 rt_custom2 = fetch_custom(geom, 2u, i0, i1, i2, bary); | |
| 535 | vec4 rt_custom3 = fetch_custom(geom, 3u, i0, i1, i2, bary); | |
| 528 | 536 | |
| 529 | 537 | #include "raytracing_custom_fragment_inc.glsl" |
| 530 | 538 |