From 59e8a8c4c3fb67075dc2e7adbc960b86d2a0c292 Mon Sep 17 00:00:00 2001 From: Ivan 'provod' Avdeev Date: Tue, 28 Feb 2023 17:50:50 -0800 Subject: [PATCH 1/2] rt: only overshoot additive for glow mode Add another flag to kusochki for glow geometry. Move flags to a new kusok.flags field. Fixes #231 (or at least makes it not stand out too much) --- ref/vk/shaders/light_common.glsl | 4 ++-- ref/vk/shaders/ray_interop.h | 7 +++++-- ref/vk/shaders/ray_primary_hit.glsl | 5 ++--- ref/vk/shaders/trace_additive.glsl | 6 +++++- ref/vk/vk_ray_model.c | 18 +++++++++--------- ref/vk/vk_render.h | 1 + ref/vk/vk_sprite.c | 2 +- 7 files changed, 25 insertions(+), 18 deletions(-) diff --git a/ref/vk/shaders/light_common.glsl b/ref/vk/shaders/light_common.glsl index dbfb7b16..35be81ab 100644 --- a/ref/vk/shaders/light_common.glsl +++ b/ref/vk/shaders/light_common.glsl @@ -98,8 +98,8 @@ bool shadowed(vec3 pos, vec3 dir, float dist, bool check_sky) { const int instance_kusochki_offset = rayQueryGetIntersectionInstanceCustomIndexEXT(rq, true); const int kusok_index = instance_kusochki_offset + rayQueryGetIntersectionGeometryIndexEXT(rq, true); - const uint tex_base_color = getKusok(kusok_index).tex_base_color; - if ((tex_base_color & KUSOK_MATERIAL_FLAG_SKYBOX) == 0) + const uint flags = getKusok(kusok_index).flags; + if ((flags & KUSOK_MATERIAL_FLAG_SKYBOX) == 0) return true; } } diff --git a/ref/vk/shaders/ray_interop.h b/ref/vk/shaders/ray_interop.h index 4e52f7d9..18cd96be 100644 --- a/ref/vk/shaders/ray_interop.h +++ b/ref/vk/shaders/ray_interop.h @@ -63,7 +63,8 @@ LIST_SPECIALIZATION_CONSTANTS(DECLARE_SPECIALIZATION_CONSTANT) #define SHADER_OFFSET_HIT_REGULAR_BASE 0 #define SHADER_OFFSET_HIT_SHADOW_BASE 3 -#define KUSOK_MATERIAL_FLAG_SKYBOX 0x80000000 +#define KUSOK_MATERIAL_FLAG_SKYBOX (1<<0) +#define KUSOK_MATERIAL_FLAG_FIXME_GLOW (1<<1) struct Kusok { uint index_offset; @@ -85,7 +86,9 @@ struct Kusok { float roughness; float metalness; - PAD(2) + uint flags; + + PAD(1) mat4 prev_transform; }; diff --git a/ref/vk/shaders/ray_primary_hit.glsl b/ref/vk/shaders/ray_primary_hit.glsl index 6377726a..e0f74522 100644 --- a/ref/vk/shaders/ray_primary_hit.glsl +++ b/ref/vk/shaders/ray_primary_hit.glsl @@ -28,13 +28,12 @@ void primaryRayHit(rayQueryEXT rq, inout RayPayloadPrimary payload) { payload.prev_pos_t = vec4(geom.prev_pos, 0.); const Kusok kusok = getKusok(geom.kusok_index); - const uint tex_base_color = kusok.tex_base_color; - if ((tex_base_color & KUSOK_MATERIAL_FLAG_SKYBOX) != 0) { + if ((kusok.flags & KUSOK_MATERIAL_FLAG_SKYBOX) != 0) { payload.emissive.rgb = SRGBtoLINEAR(texture(skybox, rayDirection).rgb); return; } else { - payload.base_color_a = sampleTexture(tex_base_color, geom.uv, geom.uv_lods); + payload.base_color_a = sampleTexture(kusok.tex_base_color, geom.uv, geom.uv_lods); payload.material_rmxx.r = (kusok.tex_roughness > 0) ? sampleTexture(kusok.tex_roughness, geom.uv, geom.uv_lods).r : kusok.roughness; payload.material_rmxx.g = (kusok.tex_metalness > 0) ? sampleTexture(kusok.tex_metalness, geom.uv, geom.uv_lods).r : kusok.metalness; diff --git a/ref/vk/shaders/trace_additive.glsl b/ref/vk/shaders/trace_additive.glsl index ddc22116..13b2a922 100644 --- a/ref/vk/shaders/trace_additive.glsl +++ b/ref/vk/shaders/trace_additive.glsl @@ -20,7 +20,11 @@ vec3 traceAdditive(vec3 pos, vec3 dir, float L) { const float hit_t = rayQueryGetIntersectionTEXT(rq, false); const float overshoot = hit_t - L; - ret += color * smoothstep(additive_soft_overshoot, 0., overshoot); + + if (overshoot < 0.) + ret += color; + else if ((kusok.flags & KUSOK_MATERIAL_FLAG_FIXME_GLOW) != 0) + ret += color * smoothstep(additive_soft_overshoot, 0., overshoot); } return ret; } diff --git a/ref/vk/vk_ray_model.c b/ref/vk/vk_ray_model.c index 8ef3a476..a82748eb 100644 --- a/ref/vk/vk_ray_model.c +++ b/ref/vk/vk_ray_model.c @@ -175,6 +175,8 @@ static void applyMaterialToKusok(vk_kusok_data_t* kusok, const vk_render_geometr kusok->roughness = mat->roughness; kusok->metalness = mat->metalness; + kusok->flags = 0; + // HACK until there is a proper mechanism for patching materials, see https://github.com/w23/xash3d-fwgs/issues/213 // FIXME also this erases previous roughness unconditionally if (HACK_reflective) { @@ -184,7 +186,10 @@ static void applyMaterialToKusok(vk_kusok_data_t* kusok, const vk_render_geometr } if (geom->material == kXVkMaterialSky) - kusok->tex_base_color |= KUSOK_MATERIAL_FLAG_SKYBOX; + kusok->flags |= KUSOK_MATERIAL_FLAG_SKYBOX; + + if (geom->material == kXVkMaterialEmissiveGlow) + kusok->flags |= KUSOK_MATERIAL_FLAG_FIXME_GLOW; { vec4_t gcolor; @@ -195,7 +200,7 @@ static void applyMaterialToKusok(vk_kusok_data_t* kusok, const vk_render_geometr Vector4Copy(gcolor, kusok->color); } - if (geom->material == kXVkMaterialEmissive) { + if (geom->material == kXVkMaterialEmissive || geom->material == kXVkMaterialEmissiveGlow) { VectorCopy(geom->emissive, kusok->emissive); } else { RT_GetEmissiveForTexture( kusok->emissive, geom->texture ); @@ -308,13 +313,8 @@ vk_ray_model_t* VK_RayModelCreate( vk_ray_model_init_t args ) { /* ); */ /* } */ - if (mg->material == kXVkMaterialSky) { - kusochki[i].tex_base_color |= KUSOK_MATERIAL_FLAG_SKYBOX; - } else { - kusochki[i].tex_base_color &= (~KUSOK_MATERIAL_FLAG_SKYBOX); - } - if (HACK_additive_emissive && mg->material != kXVkMaterialEmissive) { + if (HACK_additive_emissive && mg->material != kXVkMaterialEmissive && mg->material != kXVkMaterialEmissiveGlow) { mg->material = kXVkMaterialEmissive; VectorCopy(args.model->color, mg->emissive); } @@ -517,7 +517,7 @@ void VK_RayFrameAddModel( vk_ray_model_t *model, const vk_render_model_t *render vk_render_geometry_t *geom = render_model->geometries + i; // FIXME an impedance mismatch: render_type is per-model, while materials and emissive color are per-geom - if (HACK_additive_emissive && geom->material != kXVkMaterialEmissive) { + if (HACK_additive_emissive && geom->material != kXVkMaterialEmissive && geom->material != kXVkMaterialEmissiveGlow) { geom->material = kXVkMaterialEmissive; VectorCopy(render_model->color, geom->emissive); } diff --git a/ref/vk/vk_render.h b/ref/vk/vk_render.h index 96e17bdc..ca3a2d98 100644 --- a/ref/vk/vk_render.h +++ b/ref/vk/vk_render.h @@ -30,6 +30,7 @@ typedef enum { kXVkMaterialWater, kXVkMaterialSky, kXVkMaterialEmissive, + kXVkMaterialEmissiveGlow, kXVkMaterialConveyor, kXVkMaterialChrome, } XVkMaterialType; diff --git a/ref/vk/vk_sprite.c b/ref/vk/vk_sprite.c index 56d8705e..ab95902f 100644 --- a/ref/vk/vk_sprite.c +++ b/ref/vk/vk_sprite.c @@ -718,7 +718,7 @@ static void R_DrawSpriteQuad( const char *debug_name, mspriteframe_t *frame, vec { const vk_render_geometry_t geometry = { .texture = texture, - .material = kXVkMaterialEmissive, + .material = render_mode == kRenderGlow ? kXVkMaterialEmissiveGlow : kXVkMaterialEmissive, .max_vertex = 4, .vertex_offset = buffer.vertices.unit_offset, From 6b1e84308a7ef528a6f8c77026d6925d7c8d919a Mon Sep 17 00:00:00 2001 From: Ivan 'provod' Avdeev Date: Tue, 28 Feb 2023 18:50:07 -0800 Subject: [PATCH 2/2] rt: fix checking for skybox shadow intersection Doing tereminate-on-first-hit is incorrect, as we might accidentally hit the skybox geometry first, and consider this as no shadow. Unfortunately we have to explicitly find a closest hit and check whether that was a skybox. Maybe there's even a better way, e.g. querying for skybox-only geometry first, and only then checking for a terminate-on-first-hit for everything else. But it likely doesn't matter that much, and would need profiling anyway. Fixes #424 and #413 --- ref/vk/shaders/light.glsl | 11 ++++-- ref/vk/shaders/light_common.glsl | 63 ++++++++++++++++++++++++------- ref/vk/shaders/light_polygon.glsl | 6 +-- 3 files changed, 60 insertions(+), 20 deletions(-) diff --git a/ref/vk/shaders/light.glsl b/ref/vk/shaders/light.glsl index 69ac2bfe..14c9564d 100644 --- a/ref/vk/shaders/light.glsl +++ b/ref/vk/shaders/light.glsl @@ -104,9 +104,14 @@ void computePointLights(vec3 P, vec3 N, uint cluster_index, vec3 throughput, vec if (dot(combined,combined) < color_culling_threshold) continue; - // FIXME split environment and other lights - if (shadowed(P, light_dir_norm, light_dist + shadow_offset_fudge, is_environment)) - continue; + // TODO split environment and other lights + if (is_environment) { + if (shadowedSky(P, light_dir_norm)) + continue; + } else { + if (shadowed(P, light_dir_norm, light_dist + shadow_offset_fudge)) + continue; + } diffuse += ldiffuse; specular += lspecular; diff --git a/ref/vk/shaders/light_common.glsl b/ref/vk/shaders/light_common.glsl index 35be81ab..96e30aee 100644 --- a/ref/vk/shaders/light_common.glsl +++ b/ref/vk/shaders/light_common.glsl @@ -29,7 +29,7 @@ uint traceShadowRay(vec3 pos, vec3 dir, float dist, uint flags) { bool shadowTestAlphaMask(vec3 pos, vec3 dir, float dist) { rayQueryEXT rq; const uint flags = 0 - | gl_RayFlagsCullFrontFacingTrianglesEXT + //| gl_RayFlagsCullFrontFacingTrianglesEXT //| gl_RayFlagsNoOpaqueEXT | gl_RayFlagsTerminateOnFirstHitEXT ; @@ -71,7 +71,7 @@ bool shadowTestAlphaMask(vec3 pos, vec3 dir, float dist) { } #endif -bool shadowed(vec3 pos, vec3 dir, float dist, bool check_sky) { +bool shadowed(vec3 pos, vec3 dir, float dist) { #ifdef RAY_TRACE const uint flags = 0 //| gl_RayFlagsCullFrontFacingTrianglesEXT @@ -80,11 +80,11 @@ bool shadowed(vec3 pos, vec3 dir, float dist, bool check_sky) { | gl_RayFlagsSkipClosestHitShaderEXT ; const uint hit_type = traceShadowRay(pos, dir, dist, flags); - return check_sky ? payload_shadow.hit_type != SHADOW_SKY : payload_shadow.hit_type == SHADOW_HIT; + return payload_shadow.hit_type == SHADOW_HIT; #elif defined(RAY_QUERY) { const uint flags = 0 - | gl_RayFlagsCullFrontFacingTrianglesEXT + //| gl_RayFlagsCullFrontFacingTrianglesEXT | gl_RayFlagsOpaqueEXT | gl_RayFlagsTerminateOnFirstHitEXT ; @@ -92,16 +92,8 @@ bool shadowed(vec3 pos, vec3 dir, float dist, bool check_sky) { rayQueryInitializeEXT(rq, tlas, flags, GEOMETRY_BIT_OPAQUE, pos, 0., dir, dist - shadow_offset_fudge); while (rayQueryProceedEXT(rq)) {} - if (rayQueryGetIntersectionTypeEXT(rq, true) == gl_RayQueryCommittedIntersectionTriangleEXT) { - if (!check_sky) - return true; - - const int instance_kusochki_offset = rayQueryGetIntersectionInstanceCustomIndexEXT(rq, true); - const int kusok_index = instance_kusochki_offset + rayQueryGetIntersectionGeometryIndexEXT(rq, true); - const uint flags = getKusok(kusok_index).flags; - if ((flags & KUSOK_MATERIAL_FLAG_SKYBOX) == 0) - return true; - } + if (rayQueryGetIntersectionTypeEXT(rq, true) == gl_RayQueryCommittedIntersectionTriangleEXT) + return true; } return shadowTestAlphaMask(pos, dir, dist); @@ -111,6 +103,49 @@ bool shadowed(vec3 pos, vec3 dir, float dist, bool check_sky) { #endif } +bool shadowedSky(vec3 pos, vec3 dir) { +#ifdef RAY_TRACE + const uint flags = 0 + //| gl_RayFlagsCullFrontFacingTrianglesEXT + //| gl_RayFlagsOpaqueEXT + | gl_RayFlagsTerminateOnFirstHitEXT + | gl_RayFlagsSkipClosestHitShaderEXT + ; + const uint hit_type = traceShadowRay(pos, dir, dist, flags); + return payload_shadow.hit_type != SHADOW_SKY; +#elif defined(RAY_QUERY) + + rayQueryEXT rq; + const uint flags = 0 + | gl_RayFlagsCullFrontFacingTrianglesEXT + | gl_RayFlagsOpaqueEXT + //| gl_RayFlagsTerminateOnFirstHitEXT + //| gl_RayFlagsSkipClosestHitShaderEXT + ; + const float L = 10000.; // TODO Why 10k? + rayQueryInitializeEXT(rq, tlas, flags, GEOMETRY_BIT_OPAQUE, pos, 0., dir, L); + + // Find closest intersection, and then check whether that was a skybox + while (rayQueryProceedEXT(rq)) {} + + if (rayQueryGetIntersectionTypeEXT(rq, true) == gl_RayQueryCommittedIntersectionTriangleEXT) { + const uint instance_kusochki_offset = rayQueryGetIntersectionInstanceCustomIndexEXT(rq, true); + const uint geometry_index = rayQueryGetIntersectionGeometryIndexEXT(rq, true); + const uint kusok_index = instance_kusochki_offset + geometry_index; + const Kusok kusok = getKusok(kusok_index); + if ((kusok.flags & KUSOK_MATERIAL_FLAG_SKYBOX) == 0) + return true; + } + + // check for alpha-masked surfaces separately + const float hit_t = rayQueryGetIntersectionTEXT(rq, true); + return shadowTestAlphaMask(pos, dir, hit_t); + +#else +#error RAY_TRACE or RAY_QUERY +#endif +} + // This is an entry point for evaluation of all other BRDFs based on selected configuration (for direct light) void evalSplitBRDF(vec3 N, vec3 L, vec3 V, MaterialProperties material, out vec3 diffuse, out vec3 specular) { // Prepare data needed for BRDF evaluation - unpack material properties and evaluate commonly used terms (e.g. Fresnel, NdotL, ...) diff --git a/ref/vk/shaders/light_polygon.glsl b/ref/vk/shaders/light_polygon.glsl index 8998bade..728df6fe 100644 --- a/ref/vk/shaders/light_polygon.glsl +++ b/ref/vk/shaders/light_polygon.glsl @@ -184,7 +184,7 @@ void sampleSinglePolygonLight(in vec3 P, in vec3 N, in vec3 view_dir, in SampleC const float dist = - dot(vec4(P, 1.f), poly.plane) / dot(light_sample_dir.xyz, poly.plane.xyz); - if (shadowed(P, light_sample_dir.xyz, dist, false)) + if (shadowed(P, light_sample_dir.xyz, dist)) return; vec3 poly_diffuse = vec3(0.), poly_specular = vec3(0.); @@ -251,7 +251,7 @@ void sampleEmissiveSurfaces(vec3 P, vec3 N, vec3 throughput, vec3 view_dir, Mate const float dist = - plane_dist / dot(light_sample_dir.xyz, poly.plane.xyz); const vec3 emissive = poly.emissive; - if (!shadowed(P, light_sample_dir.xyz, dist, false)) { + if (!shadowed(P, light_sample_dir.xyz, dist)) { //const float estimate = total_contrib; const float estimate = light_sample_dir.w; vec3 poly_diffuse = vec3(0.), poly_specular = vec3(0.); @@ -327,7 +327,7 @@ void sampleEmissiveSurfaces(vec3 P, vec3 N, vec3 throughput, vec3 view_dir, Mate const vec3 emissive = poly.emissive; //if (true) {//!shadowed(P, light_sample_dir.xyz, dist)) { - if (!shadowed(P, light_sample_dir.xyz, dist, false)) { + if (!shadowed(P, light_sample_dir.xyz, dist)) { //const float estimate = total_contrib; const float estimate = light_sample_dir.w; vec3 poly_diffuse = vec3(0.), poly_specular = vec3(0.);