ADD: SSR with RT
This commit is contained in:
@@ -1,5 +1,5 @@
|
|||||||
# CopernicusEngine
|
# CopernicusEngine
|
||||||
Multi-purpose Vulkan render engine specialized for physics simulation and solar system visualization
|
Multipurpose Vulkan render engine specialized for physics simulation and solar system visualization
|
||||||
|
|
||||||
## Introduction
|
## Introduction
|
||||||
Work-In-Progress Vulkan render engine
|
Work-In-Progress Vulkan render engine
|
||||||
|
|||||||
@@ -6,8 +6,6 @@
|
|||||||
layout(location = 0) in vec2 inUV;
|
layout(location = 0) in vec2 inUV;
|
||||||
layout(location = 0) out vec4 outColor;
|
layout(location = 0) out vec4 outColor;
|
||||||
|
|
||||||
// Set 0: scene data (see input_structures.glsl)
|
|
||||||
|
|
||||||
// Set 1: SSR inputs
|
// Set 1: SSR inputs
|
||||||
layout(set = 1, binding = 0) uniform sampler2D hdrColor;
|
layout(set = 1, binding = 0) uniform sampler2D hdrColor;
|
||||||
layout(set = 1, binding = 1) uniform sampler2D posTex;
|
layout(set = 1, binding = 1) uniform sampler2D posTex;
|
||||||
@@ -16,21 +14,25 @@ layout(set = 1, binding = 3) uniform sampler2D albedoTex;
|
|||||||
|
|
||||||
vec3 getCameraWorldPosition()
|
vec3 getCameraWorldPosition()
|
||||||
{
|
{
|
||||||
mat4 invView = inverse(sceneData.view);
|
mat3 rotT = mat3(sceneData.view); // R^T
|
||||||
return vec3(invView[3]);
|
mat3 rot = transpose(rotT); // R
|
||||||
|
vec3 T = sceneData.view[3].xyz; // -R^T * C
|
||||||
|
return -rot * T; // C = -R * T
|
||||||
}
|
}
|
||||||
|
|
||||||
vec3 projectToScreen(vec3 worldPos)
|
vec3 projectToScreen(vec3 worldPos)
|
||||||
{
|
{
|
||||||
vec4 clip = sceneData.viewproj * vec4(worldPos, 1.0);
|
vec4 clip = sceneData.viewproj * vec4(worldPos, 1.0);
|
||||||
|
|
||||||
if (clip.w <= 0.0)
|
if (clip.w <= 0.0)
|
||||||
{
|
return vec3(0.0, 0.0, -1.0);
|
||||||
return vec3(0.0, 0.0, -1.0);
|
|
||||||
}
|
float invW = 1.0 / clip.w;
|
||||||
vec3 ndc = clip.xyz / clip.w;
|
vec3 ndc = clip.xyz * invW;
|
||||||
|
|
||||||
if (ndc.x < -1.0 || ndc.x > 1.0 ||
|
if (ndc.x < -1.0 || ndc.x > 1.0 ||
|
||||||
ndc.y < -1.0 || ndc.y > 1.0 ||
|
ndc.y < -1.0 || ndc.y > 1.0 ||
|
||||||
ndc.z < 0.0 || ndc.z > 1.0)
|
ndc.z < 0.0 || ndc.z > 1.0)
|
||||||
{
|
{
|
||||||
return vec3(0.0, 0.0, -1.0);
|
return vec3(0.0, 0.0, -1.0);
|
||||||
}
|
}
|
||||||
@@ -53,27 +55,21 @@ void main()
|
|||||||
vec3 worldPos = posSample.xyz;
|
vec3 worldPos = posSample.xyz;
|
||||||
|
|
||||||
vec4 normSample = texture(normalTex, inUV);
|
vec4 normSample = texture(normalTex, inUV);
|
||||||
vec3 N = normalize(normSample.xyz);
|
vec3 N = normalize(normSample.xyz);
|
||||||
float roughness = clamp(normSample.w, 0.04, 1.0);
|
float roughness = clamp(normSample.w, 0.04, 1.0);
|
||||||
|
|
||||||
vec4 albSample = texture(albedoTex, inUV);
|
vec4 albSample = texture(albedoTex, inUV);
|
||||||
vec3 albedo = albSample.rgb;
|
float metallic = clamp(albSample.a, 0.0, 1.0);
|
||||||
float metallic = clamp(albSample.a, 0.0, 1.0);
|
|
||||||
|
|
||||||
vec3 camPos = getCameraWorldPosition();
|
vec3 camPos = getCameraWorldPosition();
|
||||||
vec3 V = normalize(camPos - worldPos);
|
vec3 V = normalize(camPos - worldPos);
|
||||||
vec3 R = reflect(-V, N);
|
vec3 R = reflect(-V, N);
|
||||||
|
|
||||||
float gloss = 1.0 - roughness;
|
float gloss = 1.0 - roughness;
|
||||||
float reflectivity = gloss * mix(0.04, 1.0, metallic);
|
float F0 = mix(0.04, 1.0, metallic);
|
||||||
|
float reflectivity = gloss * F0;
|
||||||
|
|
||||||
if (reflectivity <= 0.05)
|
if (reflectivity <= 0.05 || dot(R, V) <= 0.0)
|
||||||
{
|
|
||||||
outColor = vec4(baseColor, 1.0);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (dot(R, V) <= 0.0)
|
|
||||||
{
|
{
|
||||||
outColor = vec4(baseColor, 1.0);
|
outColor = vec4(baseColor, 1.0);
|
||||||
return;
|
return;
|
||||||
@@ -84,17 +80,15 @@ void main()
|
|||||||
const float MAX_DISTANCE = 50.0; // clamp ray length
|
const float MAX_DISTANCE = 50.0; // clamp ray length
|
||||||
const float THICKNESS = 3.0; // world-space thickness tolerance
|
const float THICKNESS = 3.0; // world-space thickness tolerance
|
||||||
|
|
||||||
int maxSteps = int(mix(8.0, float(MAX_STEPS), reflectivity));
|
int maxSteps = int(mix(8.0, float(MAX_STEPS), reflectivity));
|
||||||
|
bool hit = false;
|
||||||
bool hit = false;
|
vec2 hitUV = vec2(0.0);
|
||||||
vec2 hitUV = vec2(0.0);
|
|
||||||
|
|
||||||
float t = STEP_LENGTH;
|
float t = STEP_LENGTH;
|
||||||
for (int i = 0; i < maxSteps; ++i)
|
for (int i = 0; i < maxSteps && t <= MAX_DISTANCE; ++i, t += STEP_LENGTH)
|
||||||
{
|
{
|
||||||
if (t > MAX_DISTANCE) break;
|
|
||||||
|
|
||||||
vec3 samplePos = worldPos + R * t;
|
vec3 samplePos = worldPos + R * t;
|
||||||
|
|
||||||
vec3 proj = projectToScreen(samplePos);
|
vec3 proj = projectToScreen(samplePos);
|
||||||
if (proj.z < 0.0)
|
if (proj.z < 0.0)
|
||||||
{
|
{
|
||||||
@@ -105,28 +99,23 @@ void main()
|
|||||||
vec4 scenePosSample = texture(posTex, uv);
|
vec4 scenePosSample = texture(posTex, uv);
|
||||||
if (scenePosSample.w == 0.0)
|
if (scenePosSample.w == 0.0)
|
||||||
{
|
{
|
||||||
t += STEP_LENGTH;
|
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Compare distances along view direction as a simple intersection test.
|
|
||||||
vec3 viewSample = (sceneData.view * vec4(samplePos, 1.0)).xyz;
|
vec3 viewSample = (sceneData.view * vec4(samplePos, 1.0)).xyz;
|
||||||
vec3 viewScene = (sceneData.view * vec4(scenePosSample.xyz, 1.0)).xyz;
|
vec3 viewScene = (sceneData.view * vec4(scenePosSample.xyz, 1.0)).xyz;
|
||||||
|
|
||||||
float depthRay = -viewSample.z;
|
float depthRay = -viewSample.z;
|
||||||
float depthScene = -viewScene.z;
|
float depthScene = -viewScene.z;
|
||||||
|
float depthDiff = depthRay - depthScene;
|
||||||
float depthDiff = depthRay - depthScene;
|
|
||||||
|
|
||||||
if (depthRay > 0.0 && depthScene > 0.0 &&
|
if (depthRay > 0.0 && depthScene > 0.0 &&
|
||||||
depthDiff > 0.0 && depthDiff < THICKNESS)
|
depthDiff > 0.0 && depthDiff < THICKNESS)
|
||||||
{
|
{
|
||||||
hit = true;
|
hit = true;
|
||||||
hitUV = uv;
|
hitUV = uv;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
t += STEP_LENGTH;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
vec3 result = baseColor;
|
vec3 result = baseColor;
|
||||||
@@ -135,16 +124,12 @@ void main()
|
|||||||
vec3 reflColor = texture(hdrColor, hitUV).rgb;
|
vec3 reflColor = texture(hdrColor, hitUV).rgb;
|
||||||
|
|
||||||
float NoV = clamp(dot(N, V), 0.0, 1.0);
|
float NoV = clamp(dot(N, V), 0.0, 1.0);
|
||||||
float F0 = mix(0.04, 1.0, metallic);
|
|
||||||
float F = F0 + (1.0 - F0) * pow(1.0 - NoV, 5.0); // Schlick
|
float F = F0 + (1.0 - F0) * pow(1.0 - NoV, 5.0); // Schlick
|
||||||
float gloss = 1.0 - roughness;
|
|
||||||
|
|
||||||
float ssrVisibility = gloss;
|
float ssrVisibility = gloss;
|
||||||
float weight = clamp(F * ssrVisibility, 0.0, 1.0);
|
|
||||||
|
|
||||||
|
float weight = clamp(F * ssrVisibility, 0.0, 1.0);
|
||||||
result = mix(baseColor, reflColor, weight);
|
result = mix(baseColor, reflColor, weight);
|
||||||
}
|
}
|
||||||
|
|
||||||
outColor = vec4(result, 1.0);
|
outColor = vec4(result, 1.0);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
209
shaders/ssr_rt.frag
Normal file
209
shaders/ssr_rt.frag
Normal file
@@ -0,0 +1,209 @@
|
|||||||
|
#version 460
|
||||||
|
#extension GL_GOOGLE_include_directive : require
|
||||||
|
#extension GL_EXT_ray_query : require
|
||||||
|
|
||||||
|
#include "input_structures.glsl"
|
||||||
|
|
||||||
|
layout(location = 0) in vec2 inUV;
|
||||||
|
layout(location = 0) out vec4 outColor;
|
||||||
|
|
||||||
|
// Set 0: SceneData UBO (binding=0, declared in input_structures.glsl)
|
||||||
|
// + optional TLAS for ray queries (binding=1).
|
||||||
|
layout(set = 0, binding = 1) uniform accelerationStructureEXT topLevelAS;
|
||||||
|
|
||||||
|
// Set 1: SSR inputs
|
||||||
|
layout(set = 1, binding = 0) uniform sampler2D hdrColor;
|
||||||
|
layout(set = 1, binding = 1) uniform sampler2D posTex;
|
||||||
|
layout(set = 1, binding = 2) uniform sampler2D normalTex;
|
||||||
|
layout(set = 1, binding = 3) uniform sampler2D albedoTex;
|
||||||
|
|
||||||
|
vec3 getCameraWorldPosition()
|
||||||
|
{
|
||||||
|
mat3 rotT = mat3(sceneData.view); // R^T
|
||||||
|
mat3 rot = transpose(rotT); // R
|
||||||
|
vec3 T = sceneData.view[3].xyz; // -R^T * C
|
||||||
|
return -rot * T; // C = -R * T
|
||||||
|
}
|
||||||
|
|
||||||
|
vec3 projectToScreen(vec3 worldPos)
|
||||||
|
{
|
||||||
|
vec4 clip = sceneData.viewproj * vec4(worldPos, 1.0);
|
||||||
|
|
||||||
|
if (clip.w <= 0.0)
|
||||||
|
{
|
||||||
|
return vec3(0.0, 0.0, -1.0);
|
||||||
|
}
|
||||||
|
|
||||||
|
float invW = 1.0 / clip.w;
|
||||||
|
vec3 ndc = clip.xyz * invW;
|
||||||
|
|
||||||
|
if (ndc.x < -1.0 || ndc.x > 1.0 ||
|
||||||
|
ndc.y < -1.0 || ndc.y > 1.0 ||
|
||||||
|
ndc.z < 0.0 || ndc.z > 1.0)
|
||||||
|
{
|
||||||
|
return vec3(0.0, 0.0, -1.0);
|
||||||
|
}
|
||||||
|
|
||||||
|
vec2 uv = ndc.xy * 0.5 + 0.5;
|
||||||
|
return vec3(uv, ndc.z);
|
||||||
|
}
|
||||||
|
|
||||||
|
void main()
|
||||||
|
{
|
||||||
|
vec3 baseColor = texture(hdrColor, inUV).rgb;
|
||||||
|
|
||||||
|
vec4 posSample = texture(posTex, inUV);
|
||||||
|
if (posSample.w == 0.0)
|
||||||
|
{
|
||||||
|
outColor = vec4(baseColor, 1.0);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
vec3 worldPos = posSample.xyz;
|
||||||
|
|
||||||
|
vec4 normSample = texture(normalTex, inUV);
|
||||||
|
vec3 N = normalize(normSample.xyz);
|
||||||
|
float roughness = clamp(normSample.w, 0.04, 1.0);
|
||||||
|
|
||||||
|
vec4 albSample = texture(albedoTex, inUV);
|
||||||
|
float metallic = clamp(albSample.a, 0.0, 1.0);
|
||||||
|
|
||||||
|
vec3 camPos = getCameraWorldPosition();
|
||||||
|
vec3 V = normalize(camPos - worldPos);
|
||||||
|
vec3 R = reflect(-V, N);
|
||||||
|
|
||||||
|
float gloss = 1.0 - roughness;
|
||||||
|
float F0 = mix(0.04, 1.0, metallic);
|
||||||
|
float reflectivity = gloss * F0;
|
||||||
|
|
||||||
|
if (reflectivity <= 0.05 || dot(R, V) <= 0.0)
|
||||||
|
{
|
||||||
|
outColor = vec4(baseColor, 1.0);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Reflection mode (encoded in rtOptions.w):
|
||||||
|
// 0 = SSR only, 1 = SSR + RT fallback, 2 = RT only
|
||||||
|
uint reflMode = sceneData.rtOptions.w;
|
||||||
|
bool useSSR = (reflMode == 0u || reflMode == 1u);
|
||||||
|
bool useRT = (reflMode >= 1u); // hybrid or RT-only
|
||||||
|
|
||||||
|
vec3 result = baseColor;
|
||||||
|
|
||||||
|
// -------------------------------------------------------------------------
|
||||||
|
// 1) Screen-space reflections (SSR) via depth ray-march (optional).
|
||||||
|
// -------------------------------------------------------------------------
|
||||||
|
bool ssrHit = false;
|
||||||
|
vec2 ssrUV = vec2(0.0);
|
||||||
|
|
||||||
|
if (useSSR)
|
||||||
|
{
|
||||||
|
const int MAX_STEPS_SSR = 64;
|
||||||
|
const float STEP_LENGTH_SSR = 0.5; // world units per step
|
||||||
|
const float MAX_DISTANCE_SSR = 50.0; // clamp ray length
|
||||||
|
const float THICKNESS_SSR = 3.0; // world-space thickness tolerance
|
||||||
|
|
||||||
|
int maxSteps = int(mix(8.0, float(MAX_STEPS_SSR), reflectivity));
|
||||||
|
float t = STEP_LENGTH_SSR;
|
||||||
|
for (int i = 0; i < maxSteps && t <= MAX_DISTANCE_SSR; ++i, t += STEP_LENGTH_SSR)
|
||||||
|
{
|
||||||
|
vec3 samplePos = worldPos + R * t;
|
||||||
|
|
||||||
|
vec3 proj = projectToScreen(samplePos);
|
||||||
|
if (proj.z < 0.0)
|
||||||
|
{
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
vec2 uv = proj.xy;
|
||||||
|
vec4 scenePosSample = texture(posTex, uv);
|
||||||
|
if (scenePosSample.w == 0.0)
|
||||||
|
{
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
vec3 viewSample = (sceneData.view * vec4(samplePos, 1.0)).xyz;
|
||||||
|
vec3 viewScene = (sceneData.view * vec4(scenePosSample.xyz, 1.0)).xyz;
|
||||||
|
|
||||||
|
float depthRay = -viewSample.z;
|
||||||
|
float depthScene = -viewScene.z;
|
||||||
|
float depthDiff = depthRay - depthScene;
|
||||||
|
|
||||||
|
if (depthRay > 0.0 && depthScene > 0.0 &&
|
||||||
|
depthDiff > 0.0 && depthDiff < THICKNESS_SSR)
|
||||||
|
{
|
||||||
|
ssrHit = true;
|
||||||
|
ssrUV = uv;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// If SSR hits and we are not in RT-only mode, use it as the primary reflection.
|
||||||
|
if (ssrHit && reflMode != 2u)
|
||||||
|
{
|
||||||
|
vec3 reflColor = texture(hdrColor, ssrUV).rgb;
|
||||||
|
|
||||||
|
float NoV = clamp(dot(N, V), 0.0, 1.0);
|
||||||
|
float F = F0 + (1.0 - F0) * pow(1.0 - NoV, 5.0); // Schlick
|
||||||
|
float ssrVisibility = gloss;
|
||||||
|
|
||||||
|
float weight = clamp(F * ssrVisibility, 0.0, 1.0);
|
||||||
|
result = mix(baseColor, reflColor, weight);
|
||||||
|
outColor = vec4(result, 1.0);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// If RT is disabled for reflections, we are done.
|
||||||
|
if (!useRT)
|
||||||
|
{
|
||||||
|
outColor = vec4(result, 1.0);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// -------------------------------------------------------------------------
|
||||||
|
// 2) Ray-traced reflections using TLAS and ray queries (1 ray / pixel).
|
||||||
|
// -------------------------------------------------------------------------
|
||||||
|
const float RT_TMIN = 0.05;
|
||||||
|
const float RT_TMAX = 50.0;
|
||||||
|
const float RT_ORIGIN_BIAS = 0.05;
|
||||||
|
|
||||||
|
vec3 origin = worldPos + N * RT_ORIGIN_BIAS;
|
||||||
|
|
||||||
|
rayQueryEXT rq;
|
||||||
|
rayQueryInitializeEXT(
|
||||||
|
rq,
|
||||||
|
topLevelAS,
|
||||||
|
gl_RayFlagsTerminateOnFirstHitEXT | gl_RayFlagsOpaqueEXT,
|
||||||
|
0xFF,
|
||||||
|
origin,
|
||||||
|
RT_TMIN,
|
||||||
|
R,
|
||||||
|
RT_TMAX
|
||||||
|
);
|
||||||
|
|
||||||
|
while (rayQueryProceedEXT(rq)) { }
|
||||||
|
bool rtHit = (rayQueryGetIntersectionTypeEXT(rq, true) != gl_RayQueryCommittedIntersectionNoneEXT);
|
||||||
|
|
||||||
|
if (rtHit)
|
||||||
|
{
|
||||||
|
float tHit = rayQueryGetIntersectionTEXT(rq, true);
|
||||||
|
vec3 hitPos = origin + R * tHit;
|
||||||
|
|
||||||
|
vec3 proj = projectToScreen(hitPos);
|
||||||
|
if (proj.z >= 0.0)
|
||||||
|
{
|
||||||
|
vec2 hitUV = proj.xy;
|
||||||
|
vec3 reflColor = texture(hdrColor, hitUV).rgb;
|
||||||
|
|
||||||
|
float NoV = clamp(dot(N, V), 0.0, 1.0);
|
||||||
|
float F = F0 + (1.0 - F0) * pow(1.0 - NoV, 5.0); // Schlick
|
||||||
|
float rtVisibility = gloss;
|
||||||
|
|
||||||
|
float weight = clamp(F * rtVisibility, 0.0, 1.0);
|
||||||
|
result = mix(baseColor, reflColor, weight);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
outColor = vec4(result, 1.0);
|
||||||
|
}
|
||||||
@@ -76,6 +76,9 @@ public:
|
|||||||
// Runtime settings visible to passes/shaders
|
// Runtime settings visible to passes/shaders
|
||||||
ShadowSettings shadowSettings{};
|
ShadowSettings shadowSettings{};
|
||||||
bool enableSSR = false; // optional screen-space reflections toggle
|
bool enableSSR = false; // optional screen-space reflections toggle
|
||||||
|
// Reflection mode for SSR/RT reflections; encoded into sceneData.rtOptions.w
|
||||||
|
// 0 = SSR only, 1 = SSR + RT fallback, 2 = RT only
|
||||||
|
uint32_t reflectionMode = 0;
|
||||||
|
|
||||||
// Ray tracing manager (optional, nullptr if unsupported)
|
// Ray tracing manager (optional, nullptr if unsupported)
|
||||||
RayTracingManager* ray = nullptr;
|
RayTracingManager* ray = nullptr;
|
||||||
|
|||||||
@@ -569,8 +569,12 @@ void VulkanEngine::draw()
|
|||||||
//now that we are sure that the commands finished executing, we can safely reset the command buffer to begin recording again.
|
//now that we are sure that the commands finished executing, we can safely reset the command buffer to begin recording again.
|
||||||
VK_CHECK(vkResetCommandBuffer(get_current_frame()._mainCommandBuffer, 0));
|
VK_CHECK(vkResetCommandBuffer(get_current_frame()._mainCommandBuffer, 0));
|
||||||
|
|
||||||
// Build or update TLAS for current frame now that the previous frame is idle
|
// Build or update TLAS for current frame now that the previous frame is idle.
|
||||||
if (_rayManager && _context->shadowSettings.mode != 0u)
|
// TLAS is used for hybrid/full RT shadows and RT-assisted SSR reflections.
|
||||||
|
// For reflections, only build TLAS when RT is actually enabled (reflectionMode != 0).
|
||||||
|
if (_rayManager &&
|
||||||
|
(_context->shadowSettings.mode != 0u ||
|
||||||
|
(_context->enableSSR && _context->reflectionMode != 0u)))
|
||||||
{
|
{
|
||||||
_rayManager->buildTLASFromDrawContext(_context->getMainDrawContext(), get_current_frame()._deletionQueue);
|
_rayManager->buildTLASFromDrawContext(_context->getMainDrawContext(), get_current_frame()._deletionQueue);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -602,6 +602,34 @@ namespace
|
|||||||
static void ui_postfx(VulkanEngine *eng)
|
static void ui_postfx(VulkanEngine *eng)
|
||||||
{
|
{
|
||||||
if (!eng) return;
|
if (!eng) return;
|
||||||
|
if (!eng->_context) return;
|
||||||
|
|
||||||
|
EngineContext *ctx = eng->_context.get();
|
||||||
|
|
||||||
|
ImGui::TextUnformatted("Reflections");
|
||||||
|
bool ssrEnabled = ctx->enableSSR;
|
||||||
|
if (ImGui::Checkbox("Enable Screen-Space Reflections", &ssrEnabled))
|
||||||
|
{
|
||||||
|
ctx->enableSSR = ssrEnabled;
|
||||||
|
}
|
||||||
|
|
||||||
|
int reflMode = static_cast<int>(ctx->reflectionMode);
|
||||||
|
ImGui::TextUnformatted("Reflection Mode");
|
||||||
|
ImGui::RadioButton("SSR only", &reflMode, 0);
|
||||||
|
ImGui::SameLine();
|
||||||
|
ImGui::RadioButton("SSR + RT fallback", &reflMode, 1);
|
||||||
|
ImGui::SameLine();
|
||||||
|
ImGui::RadioButton("RT only", &reflMode, 2);
|
||||||
|
|
||||||
|
const bool rq = eng->_deviceManager->supportsRayQuery();
|
||||||
|
const bool as = eng->_deviceManager->supportsAccelerationStructure();
|
||||||
|
if (!(rq && as) && reflMode != 0)
|
||||||
|
{
|
||||||
|
reflMode = 0; // guard for unsupported HW
|
||||||
|
}
|
||||||
|
ctx->reflectionMode = static_cast<uint32_t>(reflMode);
|
||||||
|
|
||||||
|
ImGui::Separator();
|
||||||
if (auto *tm = eng->_renderPassManager ? eng->_renderPassManager->getPass<TonemapPass>() : nullptr)
|
if (auto *tm = eng->_renderPassManager ? eng->_renderPassManager->getPass<TonemapPass>() : nullptr)
|
||||||
{
|
{
|
||||||
float exp = tm->exposure();
|
float exp = tm->exposure();
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
#include "ssr.h"
|
#include "ssr.h"
|
||||||
|
|
||||||
|
#include "raytracing.h"
|
||||||
#include "core/frame/resources.h"
|
#include "core/frame/resources.h"
|
||||||
#include "core/descriptor/manager.h"
|
#include "core/descriptor/manager.h"
|
||||||
#include "core/descriptor/descriptors.h"
|
#include "core/descriptor/descriptors.h"
|
||||||
@@ -38,16 +39,15 @@ void SSRPass::init(EngineContext *context)
|
|||||||
VK_DESCRIPTOR_SET_LAYOUT_CREATE_UPDATE_AFTER_BIND_POOL_BIT);
|
VK_DESCRIPTOR_SET_LAYOUT_CREATE_UPDATE_AFTER_BIND_POOL_BIT);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Graphics pipeline: fullscreen triangle, no depth, HDR color attachment.
|
// Graphics pipelines: fullscreen triangle, no depth, HDR color attachment.
|
||||||
GraphicsPipelineCreateInfo info{};
|
GraphicsPipelineCreateInfo baseInfo{};
|
||||||
info.vertexShaderPath = _context->getAssets()->shaderPath("fullscreen.vert.spv");
|
baseInfo.vertexShaderPath = _context->getAssets()->shaderPath("fullscreen.vert.spv");
|
||||||
info.fragmentShaderPath = _context->getAssets()->shaderPath("ssr.frag.spv");
|
baseInfo.setLayouts = {
|
||||||
info.setLayouts = {
|
_context->getDescriptorLayouts()->gpuSceneDataLayout(), // set = 0 (sceneData UBO + optional TLAS)
|
||||||
_context->getDescriptorLayouts()->gpuSceneDataLayout(), // set = 0 (sceneData UBO)
|
|
||||||
_inputSetLayout // set = 1 (HDR + GBuffer)
|
_inputSetLayout // set = 1 (HDR + GBuffer)
|
||||||
};
|
};
|
||||||
|
|
||||||
info.configure = [this](PipelineBuilder &b)
|
baseInfo.configure = [this](PipelineBuilder &b)
|
||||||
{
|
{
|
||||||
b.set_input_topology(VK_PRIMITIVE_TOPOLOGY_TRIANGLE_LIST);
|
b.set_input_topology(VK_PRIMITIVE_TOPOLOGY_TRIANGLE_LIST);
|
||||||
b.set_polygon_mode(VK_POLYGON_MODE_FILL);
|
b.set_polygon_mode(VK_POLYGON_MODE_FILL);
|
||||||
@@ -61,7 +61,15 @@ void SSRPass::init(EngineContext *context)
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
_context->pipelines->createGraphicsPipeline("ssr", info);
|
// Non-RT variant (pure screen-space reflections).
|
||||||
|
GraphicsPipelineCreateInfo infoNoRT = baseInfo;
|
||||||
|
infoNoRT.fragmentShaderPath = _context->getAssets()->shaderPath("ssr.frag.spv");
|
||||||
|
_context->pipelines->createGraphicsPipeline("ssr.nort", infoNoRT);
|
||||||
|
|
||||||
|
// RT-assisted variant (SSR + ray-query fallback using TLAS).
|
||||||
|
GraphicsPipelineCreateInfo infoRT = baseInfo;
|
||||||
|
infoRT.fragmentShaderPath = _context->getAssets()->shaderPath("ssr_rt.frag.spv");
|
||||||
|
_context->pipelines->createGraphicsPipeline("ssr.rt", infoRT);
|
||||||
}
|
}
|
||||||
|
|
||||||
void SSRPass::cleanup()
|
void SSRPass::cleanup()
|
||||||
@@ -148,10 +156,21 @@ void SSRPass::draw_ssr(VkCommandBuffer cmd,
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Fetch (or refresh) pipeline for hot-reload support.
|
// Choose RT variant only if TLAS is valid; otherwise fall back to non-RT.
|
||||||
if (!pipelineManager->getGraphics("ssr", _pipeline, _pipelineLayout))
|
const bool haveRTFeatures = deviceManager->supportsAccelerationStructure();
|
||||||
|
const VkAccelerationStructureKHR tlas = (ctxLocal->ray ? ctxLocal->ray->tlas() : VK_NULL_HANDLE);
|
||||||
|
const VkDeviceAddress tlasAddr = (ctxLocal->ray ? ctxLocal->ray->tlasAddress() : 0);
|
||||||
|
const bool useRT = haveRTFeatures && (tlas != VK_NULL_HANDLE) && (tlasAddr != 0);
|
||||||
|
|
||||||
|
const char *pipeName = useRT ? "ssr.rt" : "ssr.nort";
|
||||||
|
if (!pipelineManager->getGraphics(pipeName, _pipeline, _pipelineLayout))
|
||||||
{
|
{
|
||||||
return;
|
// Try the other variant as a fallback.
|
||||||
|
const char *fallback = useRT ? "ssr.nort" : "ssr.rt";
|
||||||
|
if (!pipelineManager->getGraphics(fallback, _pipeline, _pipelineLayout))
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Scene UBO (set=0, binding=0) – mirror LightingPass behavior.
|
// Scene UBO (set=0, binding=0) – mirror LightingPass behavior.
|
||||||
@@ -175,6 +194,10 @@ void SSRPass::draw_ssr(VkCommandBuffer cmd,
|
|||||||
{
|
{
|
||||||
DescriptorWriter writer;
|
DescriptorWriter writer;
|
||||||
writer.write_buffer(0, sceneBuf.buffer, sizeof(GPUSceneData), 0, VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER);
|
writer.write_buffer(0, sceneBuf.buffer, sizeof(GPUSceneData), 0, VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER);
|
||||||
|
if (useRT)
|
||||||
|
{
|
||||||
|
writer.write_acceleration_structure(1, tlas);
|
||||||
|
}
|
||||||
writer.update_set(deviceManager->device(), globalSet);
|
writer.update_set(deviceManager->device(), globalSet);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -347,7 +347,10 @@ void SceneManager::update_scene()
|
|||||||
{
|
{
|
||||||
const auto &ss = _context->shadowSettings;
|
const auto &ss = _context->shadowSettings;
|
||||||
const uint32_t rtEnabled = (ss.mode != 0) ? 1u : 0u;
|
const uint32_t rtEnabled = (ss.mode != 0) ? 1u : 0u;
|
||||||
sceneData.rtOptions = glm::uvec4(rtEnabled, ss.hybridRayCascadesMask, ss.mode, 0u);
|
const uint32_t reflMode = _context->reflectionMode;
|
||||||
|
// rtOptions.x = RT shadows enabled, y = cascade mask, z = shadow mode, w = reflection mode (SSR/RT)
|
||||||
|
sceneData.rtOptions = glm::uvec4(rtEnabled, ss.hybridRayCascadesMask, ss.mode, reflMode);
|
||||||
|
// rtParams.x = N·L threshold for hybrid shadows; remaining components reserved
|
||||||
sceneData.rtParams = glm::vec4(ss.hybridRayNoLThreshold, 0.0f, 0.0f, 0.0f);
|
sceneData.rtParams = glm::vec4(ss.hybridRayNoLThreshold, 0.0f, 0.0f, 0.0f);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user