Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
11 changes: 9 additions & 2 deletions code/def_files/data/effects/deferred-f.sdr
Original file line number Diff line number Diff line change
Expand Up @@ -65,6 +65,12 @@ uniform shadowCascadeParams {
float rtShadowBiasMax;

mat4 shadow_mv_matrix;

// shadow_ray_cull_mask/shadow_ray_world_offset: see shadow_cascade_static_data
// (uniform_structs.h) for what these mean and shadow_cascade_params_bind()
// (shadows.cpp) for how they're set.
int shadow_ray_cull_mask;
vec3 shadow_ray_world_offset;
mat4 shadow_proj_matrix[NUM_SHADOW_CASCADES];
vec4 cascade_distances[(NUM_SHADOW_CASCADES + 4 - 1) / 4];
vec4 smoothness_factors[(NUM_SHADOW_CASCADES + 4 - 1) / 4];
Expand Down Expand Up @@ -310,12 +316,13 @@ void main()

if (enable_shadows != 0) {
#ifdef RT_SHADOWS
vec3 worldPos = (inv_view_matrix * vec4(position, 1.0)).xyz;
// Zero outside the cockpit pass; see shadow_cascade_static_data (uniform_structs.h).
vec3 worldPos = (inv_view_matrix * vec4(position, 1.0)).xyz + shadow_ray_world_offset;
vec3 worldNormal = normalize((inv_view_matrix * vec4(normal, 0.0)).xyz);
vec3 worldLightDir = normalize((inv_view_matrix * vec4(lightDir, 0.0)).xyz);

float rtShadowBias = computeRtShadowBias(length(position), rtShadowBiasMin, rtShadowBiasMax);
attenuation *= traceShadowRay(shadow_tlas, worldPos, worldNormal, worldLightDir, lightDist, rtShadowBias);
attenuation *= traceShadowRay(shadow_tlas, worldPos, worldNormal, worldLightDir, lightDist, rtShadowBias, uint(shadow_ray_cull_mask));
#else
vec4 fragShadowPos = shadow_mv_matrix * inv_view_matrix * vec4(position, 1.0);
vec4 fragShadowUV[NUM_SHADOW_CASCADES];
Expand Down
12 changes: 10 additions & 2 deletions code/def_files/data/effects/main-f.sdr
Original file line number Diff line number Diff line change
Expand Up @@ -103,6 +103,12 @@ uniform shadowCascadeParams {
float rtShadowBiasMax;

mat4 shadow_mv_matrix;

// shadow_ray_cull_mask/shadow_ray_world_offset: see shadow_cascade_static_data
// (uniform_structs.h) for what these mean and shadow_cascade_params_bind()
// (shadows.cpp) for how they're set.
int shadow_ray_cull_mask;
vec3 shadow_ray_world_offset;
mat4 shadow_proj_matrix[NUM_SHADOW_CASCADES];
vec4 cascade_distances[(NUM_SHADOW_CASCADES + 4 - 1) / 4];
vec4 smoothness_factors[(NUM_SHADOW_CASCADES + 4 - 1) / 4];
Expand Down Expand Up @@ -237,7 +243,8 @@ vec3 CalculateLighting(vec3 normal, vec3 diffuseMaterial, vec3 specularMaterial,
rtShadowsActive = true;
#prereplace ENDIF_FLAG //MODEL_SDR_FLAG_SHADOWS
mat4 invView = inverse(viewMatrix);
vec3 worldPos = (invView * vertIn.position).xyz;
// Zero outside the cockpit pass; see shadow_cascade_static_data (uniform_structs.h).
vec3 worldPos = (invView * vertIn.position).xyz + shadow_ray_world_offset;
vec3 worldNormal = normalize((invView * vec4(normal, 0.0)).xyz);
int shadowedDirectionalCount = 0;
#endif
Expand All @@ -248,7 +255,7 @@ vec3 CalculateLighting(vec3 normal, vec3 diffuseMaterial, vec3 specularMaterial,
if (rtShadowsActive && lights[i].light_type == LT_DIRECTIONAL && shadowedDirectionalCount < MAX_RT_SHADOW_LIGHTS) {
vec3 worldSunDir = normalize((invView * vec4(lights[i].position.xyz, 0.0)).xyz);
float rtShadowBias = computeRtShadowBias(length(vertIn.position.xyz), rtShadowBiasMin, rtShadowBiasMax);
shadow = traceShadowRay(shadow_tlas, worldPos, worldNormal, worldSunDir, RT_SHADOW_MAX_DISTANCE, rtShadowBias);
shadow = traceShadowRay(shadow_tlas, worldPos, worldNormal, worldSunDir, RT_SHADOW_MAX_DISTANCE, rtShadowBias, uint(shadow_ray_cull_mask));
++shadowedDirectionalCount;
} else {
shadow = 1.0;
Expand All @@ -270,6 +277,7 @@ vec3 CalculateLighting(vec3 normal, vec3 diffuseMaterial, vec3 specularMaterial,
lightDiffuse += (lights[i].diffuse_color.rgb * diffuseFactor * NdotL * attenuation) * shadow;
lightSpecular += lights[i].diffuse_color.rgb * computeLighting(specularMaterial, diffuseMaterial, lightDir, normal, halfVec, eyeDir, roughness, fresnel, NdotL) * attenuation * shadow;
}

return diffuseMaterial * lightAmbient + lightSpecular;
}

Expand Down
6 changes: 6 additions & 0 deletions code/def_files/data/effects/main-v.sdr
Original file line number Diff line number Diff line change
Expand Up @@ -117,6 +117,12 @@ uniform shadowCascadeParams {
float rtShadowBiasMax;

mat4 shadow_mv_matrix;

// shadow_ray_cull_mask/shadow_ray_world_offset: see shadow_cascade_static_data
// (uniform_structs.h) for what these mean and shadow_cascade_params_bind()
// (shadows.cpp) for how they're set.
int shadow_ray_cull_mask;
vec3 shadow_ray_world_offset;
mat4 shadow_proj_matrix[NUM_SHADOW_CASCADES];
vec4 cascade_distances[(NUM_SHADOW_CASCADES + 4 - 1) / 4];
vec4 smoothness_factors[(NUM_SHADOW_CASCADES + 4 - 1) / 4];
Expand Down
6 changes: 6 additions & 0 deletions code/def_files/data/effects/shadow_map-g.sdr
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,12 @@ layout (std140) uniform shadowCascadeParams {
float rtShadowBiasMax;

mat4 shadow_mv_matrix;

// shadow_ray_cull_mask/shadow_ray_world_offset: see shadow_cascade_static_data
// (uniform_structs.h) for what these mean and shadow_cascade_params_bind()
// (shadows.cpp) for how they're set.
int shadow_ray_cull_mask;
vec3 shadow_ray_world_offset;
mat4 shadow_proj_matrix[NUM_SHADOW_CASCADES];
vec4 cascade_distances[(NUM_SHADOW_CASCADES + 4 - 1) / 4];
vec4 smoothness_factors[(NUM_SHADOW_CASCADES + 4 - 1) / 4];
Expand Down
6 changes: 6 additions & 0 deletions code/def_files/data/effects/shadow_map-v.sdr
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,12 @@ uniform shadowCascadeParams {
float rtShadowBiasMax;

mat4 shadow_mv_matrix;

// shadow_ray_cull_mask/shadow_ray_world_offset: see shadow_cascade_static_data
// (uniform_structs.h) for what these mean and shadow_cascade_params_bind()
// (shadows.cpp) for how they're set.
int shadow_ray_cull_mask;
vec3 shadow_ray_world_offset;
mat4 shadow_proj_matrix[NUM_SHADOW_CASCADES];
vec4 cascade_distances[(NUM_SHADOW_CASCADES + 4 - 1) / 4];
vec4 smoothness_factors[(NUM_SHADOW_CASCADES + 4 - 1) / 4];
Expand Down
10 changes: 8 additions & 2 deletions code/def_files/data/effects/shadows.sdr
Original file line number Diff line number Diff line change
Expand Up @@ -114,14 +114,20 @@ float computeRtShadowBias(float cameraDist, float biasMin, float biasMax)
//
// bias is a world-unit offset applied along worldNormal before tracing, to clear
// the source triangle -- see computeRtShadowBias() above for how callers derive it.
float traceShadowRay(accelerationStructureEXT tlas, vec3 worldPos, vec3 worldNormal, vec3 worldLightDir, float tMax, float bias)
//
// cullMask selects which TLAS instances this ray can hit (Vulkan spec: an instance
// is a candidate iff (cullMask & instance.mask) != 0). Callers should pass
// shadow_ray_cull_mask (shadowCascadeParams uniform block) rather than a literal --
// see SHADOW_RAY_CULL_MASK_EXCLUDE_VIEWER_HULL/TLAS_MASK_VIEWER_HULL (shadows.h)
// for how it excludes the viewer ship's own hull outside the cockpit pass.
float traceShadowRay(accelerationStructureEXT tlas, vec3 worldPos, vec3 worldNormal, vec3 worldLightDir, float tMax, float bias, uint cullMask)
{
vec3 origin = worldPos + worldNormal * bias;
vec3 direction = normalize(worldLightDir);

rayQueryEXT rq;
rayQueryInitializeEXT(rq, tlas, gl_RayFlagsOpaqueEXT | gl_RayFlagsTerminateOnFirstHitEXT,
0xFF, origin, 0.001, direction, tMax);
cullMask, origin, 0.001, direction, tMax);
while (rayQueryProceedEXT(rq)) {}

return (rayQueryGetIntersectionTypeEXT(rq, true) == gl_RayQueryCommittedIntersectionNoneEXT) ? 1.0 : 0.0;
Expand Down
4 changes: 1 addition & 3 deletions code/graphics/opengl/gropengldeferred.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -316,9 +316,7 @@ void gr_opengl_deferred_lighting_finish()
auto header = light_uniform_aligner.getHeader<deferred_global_data>();
if (Shadow_quality != ShadowQuality::Disabled) {
vm_inverse_matrix4(&header->inv_view_matrix, &Shadow_view_matrix_render);
int offset = (Lighting_mode == lighting_mode::COCKPIT) ? 0 : Num_cockpit_shadow_cascades;
int count = (Lighting_mode == lighting_mode::COCKPIT) ? Num_cockpit_shadow_cascades : Num_shadow_cascades;
shadow_cascade_params_bind(offset, count);
shadow_cascade_params_bind_deferred();
}

header->invScreenWidth = 1.0f / gr_screen.max_w;
Expand Down
38 changes: 29 additions & 9 deletions code/graphics/shadows.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -665,11 +665,11 @@ matrix shadows_start_render(matrix *eye_orient, vec3d *eye_pos, fov_t fov, fov_t

gr_shadow_map_start(&Shadow_view_matrix_light, &light_matrix, eye_pos, true);
if (cascade_distances_override)
shadow_cascade_params_bind(max_skip_override, num_cascades - max_skip_override);
shadow_cascade_params_bind(max_skip_override, num_cascades - max_skip_override, vmd_zero_vector);
else if (render_cockpit_cascades)
shadow_cascade_params_bind(0, num_cascades);
shadow_cascade_params_bind(0, num_cascades, vmd_zero_vector);
else
shadow_cascade_params_bind(Num_cockpit_shadow_cascades, Num_shadow_cascades);
shadow_cascade_params_bind(Num_cockpit_shadow_cascades, Num_shadow_cascades, vmd_zero_vector);

return light_matrix;
}
Expand Down Expand Up @@ -764,9 +764,9 @@ static void render_viewer_shadow(object* objp, const matrix* light_matrix,

gr_shadow_map_start(&dummy_view, light_matrix, &vmd_zero_vector, false);
if (casts_shadow_on_cockpit)
shadow_cascade_params_bind(0, Num_cockpit_shadow_cascades + Num_shadow_cascades);
shadow_cascade_params_bind(0, Num_cockpit_shadow_cascades + Num_shadow_cascades, vmd_zero_vector);
else
shadow_cascade_params_bind(Num_cockpit_shadow_cascades, Num_shadow_cascades);
shadow_cascade_params_bind(Num_cockpit_shadow_cascades, Num_shadow_cascades, vmd_zero_vector);

model_clear_instance(sip->model_num);
polymodel_instance* pmi = nullptr;
Expand All @@ -782,12 +782,12 @@ static void render_viewer_shadow(object* objp, const matrix* light_matrix,
viewer_list.render_all();
}

const bool renderCockpitModel = (Viewer_mode != VM_TOPDOWN) && sip->cockpit_model_num >= 0 && !Disable_cockpits;
const bool renderCockpitModel = ship_player_cockpit_model_would_render(sip);

if (renderCockpitModel && !Shadow_disable_overrides.disable_cockpit) {
matrix4 dummy_view;
gr_shadow_map_start(&dummy_view, light_matrix, &vmd_zero_vector, false);
shadow_cascade_params_bind(0, Num_cockpit_shadow_cascades);
shadow_cascade_params_bind(0, Num_cockpit_shadow_cascades, vmd_zero_vector);

vec3d cockpit_offset = sip->cockpit_offset;
vm_vec_unrotate(&cockpit_offset, &cockpit_offset, &objp->orient);
Expand Down Expand Up @@ -1035,7 +1035,8 @@ void shadow_cascade_params_shutdown() {
}

int Shadow_cascade_count = 0;
void shadow_cascade_params_bind(int cascade_offset, int cascade_count) {
void shadow_cascade_params_bind(int cascade_offset, int cascade_count, const vec3d& world_offset,
bool allow_viewer_self_shadow) {
if (!Shadow_cascade_params_buffer.isValid()) {
return;
}
Expand All @@ -1058,6 +1059,10 @@ void shadow_cascade_params_bind(int cascade_offset, int cascade_count) {
static_data.rtShadowBiasMax = Rt_shadow_bias_max;
static_data.shadow_mv_matrix = Shadow_view_matrix_light;

// See shadow_cascade_params_bind()'s declaration (shadows.h) for what these mean.
static_data.shadow_ray_cull_mask = allow_viewer_self_shadow ? 0xFF : SHADOW_RAY_CULL_MASK_EXCLUDE_VIEWER_HULL;
static_data.shadow_ray_world_offset = world_offset;

Shadow_cascade_count = cascade_count;

offset += sizeof(graphics::shadow_cascade_static_data);
Expand All @@ -1082,10 +1087,25 @@ void shadow_cascade_params_bind(int cascade_offset, int cascade_count) {
}
offset += sizeof(float) * padding;

gr_update_buffer_data_offset(Shadow_cascade_params_buffer, 0, required_size, buffer.data());
// Must be the full-replacement update, not _offset: this is called several times per
// frame with different content, and on Vulkan's streaming buffer _offset only bump-
// allocates fresh GPU memory on the first call, silently overwriting the same region
// on later calls -- so every draw this frame (command buffers are pre-recorded) would
// read whichever bind ran last, not the one current when it was recorded. This always
// bump-allocates fresh on Vulkan (matching deferred_global_data/deferred_light_data)
// and is a plain buffer-orphaning glBufferData() on OpenGL.
gr_update_buffer_data(Shadow_cascade_params_buffer, required_size, buffer.data());
gr_bind_uniform_buffer(uniform_block_type::ShadowCascadeParams, 0, required_size, Shadow_cascade_params_buffer);
}

void shadow_cascade_params_bind_deferred() {
int offset = (Lighting_mode == lighting_mode::COCKPIT) ? 0 : Num_cockpit_shadow_cascades;
int count = (Lighting_mode == lighting_mode::COCKPIT) ? Num_cockpit_shadow_cascades : Num_shadow_cascades;
vec3d world_offset = (Lighting_mode == lighting_mode::COCKPIT && Viewer_obj != nullptr) ? Viewer_obj->pos : vmd_zero_vector;
bool allow_viewer_self_shadow = Lighting_mode == lighting_mode::COCKPIT && ship_render_player_ship_casts_shadow_on_cockpit();
shadow_cascade_params_bind(offset, count, world_offset, allow_viewer_self_shadow);
}

shadow_render_list::shadow_render_list() {
reset();
}
Expand Down
27 changes: 26 additions & 1 deletion code/graphics/shadows.h
Original file line number Diff line number Diff line change
Expand Up @@ -95,14 +95,39 @@ extern SCP_vector<matrix4> Shadow_proj_matrix;
extern SCP_vector<float> Shadow_cascade_distances;
extern int Shadow_cascade_count;

// TLAS ray-cull mask bit reserved for the viewer ship's own hull instance
// (VulkanRaytracingManager::gatherShadowCasterInstances()'s OBJ_SHIP case), so shadow
// rays can selectively exclude it -- see shadow_cascade_params_bind()'s
// shadow_ray_cull_mask and traceShadowRay() (shadows.sdr).
constexpr uint8_t TLAS_MASK_VIEWER_HULL = 0x80;
// Default TLAS instance mask (visible to every ray) minus TLAS_MASK_VIEWER_HULL --
// the shadow_ray_cull_mask used everywhere the viewer's own hull must not self-shadow.
constexpr uint8_t SHADOW_RAY_CULL_MASK_EXCLUDE_VIEWER_HULL = static_cast<uint8_t>(~TLAS_MASK_VIEWER_HULL);

void shadows_construct_light_frustum(vec3d *min_out, vec3d *max_out, vec3d light_vec, matrix *orient, vec3d *pos, fov_t fov, float aspect, float z_near, float z_far);
bool shadows_obj_in_frustum(object *objp, vec3d *min, vec3d *max, matrix *light_orient);
void shadows_render_all(fov_t fov, matrix *eye_orient, vec3d *eye_pos,
const vec3d* cam_offset = nullptr, const matrix* rot_offset = nullptr, const fov_t* fov_override = nullptr);

void shadow_cascade_params_init();
void shadow_cascade_params_shutdown();
void shadow_cascade_params_bind(int cascade_offset, int cascade_count);

// world_offset: added to traceShadowRay()'s reconstructed world position before tracing.
// Zero for every pass rendering in true world space; nonzero only for the cockpit pass
// (see shadow_cascade_static_data::shadow_ray_world_offset, uniform_structs.h, for the
// derivation). A pass with draws in more than one internal frame -- e.g.
// ship_render_player_ship()'s hull vs. cockpit draws -- must rebind before each one.
// allow_viewer_self_shadow: lets the viewer ship's own hull (TLAS_MASK_VIEWER_HULL,
// shadows.h) cast shadows in this pass. True only for the cockpit's own shading.
void shadow_cascade_params_bind(int cascade_offset, int cascade_count, const vec3d& world_offset,
bool allow_viewer_self_shadow = false);

// Binds the cascade range/world_offset/self-shadow-mask appropriate for the current
// Lighting_mode, for callers that (unlike ship_render_player_ship()) only ever shade
// one frame per pass -- currently the deferred-lighting full-screen passes
// (gropengldeferred.cpp, VulkanPostProcessingLighting.cpp). See shadow_cascade_params_bind()
// above for what world_offset/allow_viewer_self_shadow mean and their known limitation here.
void shadow_cascade_params_bind_deferred();

matrix shadows_start_render(matrix *eye_orient, vec3d *eye_pos, fov_t fov, fov_t cockpit_fov, float aspect, const std::optional<SCP_vector<float>>& cascade_distances_override = std::nullopt);
void shadows_end_render();
Expand Down
24 changes: 24 additions & 0 deletions code/graphics/util/uniform_structs.h
Original file line number Diff line number Diff line change
Expand Up @@ -147,7 +147,31 @@ struct shadow_cascade_static_data {
float rtShadowBiasMin;
float rtShadowBiasMax;
matrix4 shadow_mv_matrix;

// Ray cull mask for raytraced shadow queries (traceShadowRay()/shadows.sdr). Excludes
// the viewer ship's own hull TLAS instance (TLAS_MASK_VIEWER_HULL, shadows.h) outside
// the cockpit pass, matching the rasterized path's exclusion of Viewer_obj from the
// main shadow cascades. Set in shadow_cascade_params_bind() (shadows.cpp).
int shadow_ray_cull_mask;
float pad[3]; // pad shadow_ray_world_offset's offset to 16-byte alignment (std140 vec3 rule)

// World-space correction added to the RT shadow ray's reconstructed position before
// tracing. Zero everywhere except the cockpit pass: ship_render_player_ship() renders
// the cockpit using a view matrix anchored at `leaning_position` and model positions
// offset in that same un-translated frame (never combined with objp->pos), so the
// reconstructed position isn't true world space like the shadow TLAS is. Adding the
// viewer ship's world position back here closes that gap -- see
// shadow_cascade_params_bind() (shadows.cpp) for where this is set.
vec3d shadow_ray_world_offset;
float pad2; // keep shadow_proj_matrix[]'s offset 16-byte aligned (std140)
};
// Must match the GLSL shadowCascadeParams block's implicit std140 padding
// exactly (16 [4 leading scalars] + 64 [matrix4] + 4 [shadow_ray_cull_mask]
// + 12 [pad[3]] + 12 [shadow_ray_world_offset] + 4 [pad2] = 112) --
// shadow_cascade_params_bind() packs shadow_proj_matrix[] immediately after
// this struct via sizeof(), so a mismatch here silently shifts every cascade
// matrix in the buffer, in both backends.
static_assert(sizeof(shadow_cascade_static_data) == 112, "shadow_cascade_static_data must match the GLSL shadowCascadeParams layout (see comment above)");

enum class NanoVGShaderType: int32_t {
FillGradient = 0, FillImage = 1, Simple = 2, Image = 3
Expand Down
5 changes: 1 addition & 4 deletions code/graphics/vulkan/VulkanPostProcessingLighting.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -507,10 +507,7 @@ void VulkanDeferredLighting::render(vk::CommandBuffer cmd)

if (m_shadow->isInitialized() && Shadow_quality != ShadowQuality::Disabled) {
vm_inverse_matrix4(&header->inv_view_matrix, &Shadow_view_matrix_render);

int offset = (Lighting_mode == lighting_mode::COCKPIT) ? 0 : Num_cockpit_shadow_cascades;
int count = (Lighting_mode == lighting_mode::COCKPIT) ? Num_cockpit_shadow_cascades : Num_shadow_cascades;
shadow_cascade_params_bind(offset, count);
shadow_cascade_params_bind_deferred();
}
}

Expand Down
24 changes: 21 additions & 3 deletions code/graphics/vulkan/VulkanRaytracing.h
Original file line number Diff line number Diff line change
Expand Up @@ -192,23 +192,41 @@ class VulkanRaytracingManager {

// Shared by walkSubmodelTree/addSingleSubmodelInstance: appends one TLAS
// instance referencing blasAddress, placed at the given world orient/pos.
// `mask` is the instance's ray-cull mask (vk::AccelerationStructureInstanceKHR::mask,
// see TLAS_MASK_VIEWER_HULL in shadows.h); defaults to 0xFF (visible to every ray).
static void pushInstance(SCP_vector<vk::AccelerationStructureInstanceKHR>& instances,
vk::DeviceAddress blasAddress,
const matrix& orient,
const vec3d& pos);
const vec3d& pos,
uint8_t mask = 0xFF);

void gatherShadowCasterInstances(SCP_vector<vk::AccelerationStructureInstanceKHR>& instances);
// Adds one instance walk for Viewer_obj's cockpit polymodel (sip->cockpit_model_num),
// which -- unlike ships/asteroids/debris -- has no backing `object` for
// gatherShadowCasterInstances() to discover it through. Mirrors the gating
// render_viewer_shadow()'s cockpit block uses (shadows.cpp) so the cockpit only
// gets a TLAS instance when it would also get a rasterized shadow-map pass.
void gatherCockpitShadowCasterInstance(SCP_vector<vk::AccelerationStructureInstanceKHR>& instances);
// `skipDetailBoxCheck`: the detail-box gate (submodelPassesDetailBox()) compares
// against the global `Eye_position`, which isn't correct for the cockpit model (its
// rasterized detail-box checks use a cockpit-relative eye position instead -- see
// shadows.cpp). gatherCockpitShadowCasterInstance() passes true to skip the check
// rather than derive that; cockpit models are small and sit right against the camera,
// so detail-box culling is unlikely to matter there. Every other caller defaults false.
void walkSubmodelTree(SCP_vector<vk::AccelerationStructureInstanceKHR>& instances,
transform_stack& stack,
const polymodel* pm,
const polymodel_instance* pmi,
int submodel_num);
int submodel_num,
uint8_t mask = 0xFF,
bool skipDetailBoxCheck = false);
void addSingleSubmodelInstance(SCP_vector<vk::AccelerationStructureInstanceKHR>& instances,
const polymodel* pm,
const polymodel_instance* pmi,
int submodel_num,
const matrix& orient,
const vec3d& pos);
const vec3d& pos,
uint8_t mask = 0xFF);

// One full set of grow-only TLAS resources per frame-in-flight slot, indexed
// by currentFrameIndex() -- NOT a single shared instance. buildTlas()
Expand Down
Loading
Loading