Fixed and sped up secondary bounce.

This commit is contained in:
Justin Marshall
2026-05-02 10:11:02 -07:00
parent bb0f110570
commit e8c6f824a8
2 changed files with 605 additions and 30 deletions
+604 -29
View File
@@ -72,6 +72,90 @@ static T glRaytracingClamp(T v, T lo, T hi)
return (v < lo) ? lo : ((v > hi) ? hi : v);
}
static void glRaytracingSetIdentity4x4(float* m)
{
if (!m)
return;
memset(m, 0, sizeof(float) * 16);
m[0] = 1.0f;
m[5] = 1.0f;
m[10] = 1.0f;
m[15] = 1.0f;
}
static int glRaytracingInvertMatrix4x4(const float* m, float* out)
{
if (!m || !out)
return 0;
float a[4][8];
for (int r = 0; r < 4; ++r)
{
for (int c = 0; c < 4; ++c)
a[r][c] = m[r * 4 + c];
for (int c = 0; c < 4; ++c)
a[r][4 + c] = (r == c) ? 1.0f : 0.0f;
}
for (int col = 0; col < 4; ++col)
{
int pivot = col;
float best = a[col][col] < 0.0f ? -a[col][col] : a[col][col];
for (int r = col + 1; r < 4; ++r)
{
const float v = a[r][col] < 0.0f ? -a[r][col] : a[r][col];
if (v > best)
{
best = v;
pivot = r;
}
}
if (best <= 1.0e-8f)
return 0;
if (pivot != col)
{
for (int c = 0; c < 8; ++c)
{
const float tmp = a[col][c];
a[col][c] = a[pivot][c];
a[pivot][c] = tmp;
}
}
const float invPivot = 1.0f / a[col][col];
for (int c = 0; c < 8; ++c)
a[col][c] *= invPivot;
for (int r = 0; r < 4; ++r)
{
if (r == col)
continue;
const float f = a[r][col];
if (f == 0.0f)
continue;
for (int c = 0; c < 8; ++c)
a[r][c] -= f * a[col][c];
}
}
for (int r = 0; r < 4; ++r)
{
for (int c = 0; c < 4; ++c)
out[r * 4 + c] = a[r][4 + c];
}
return 1;
}
static DXGI_FORMAT glRaytracingGetSrvFormatForDepth(DXGI_FORMAT fmt)
{
switch (fmt)
@@ -1987,6 +2071,7 @@ struct glRaytracingLightingConstants_t
{
float invViewProj[16];
float invViewMatrix[16];
float viewProj[16];
float cameraPos[4];
float ambientColor[4];
float screenSize[4];
@@ -2127,10 +2212,19 @@ struct ShadowPayload
uint hit;
};
struct BouncePayload
{
uint hit;
float hitT;
uint materialFlags;
uint pad0;
};
cbuffer LightingCB : register(b0)
{
float4x4 gInvViewProj;
float4x4 gInvViewMatrix;
float4x4 gViewProj;
float4 gCameraPos;
float4 gAmbientColor;
float4 gScreenSize;
@@ -2256,6 +2350,47 @@ void ShadowClosestHit(inout ShadowPayload payload, in BuiltInTriangleIntersectio
payload.hit = 1;
}
[shader("miss")]
void BounceMiss(inout BouncePayload payload)
{
payload.hit = 0;
payload.hitT = 0.0;
payload.materialFlags = 0;
payload.pad0 = 0;
}
[shader("anyhit")]
void BounceAnyHit(inout BouncePayload payload, in BuiltInTriangleIntersectionAttributes attr)
{
// Secondary diffuse rays should see through the same glass that shadow rays
// see through. This keeps a glass pane from killing all bounced light behind it.
if (CurrentRayHitIsGlass())
{
IgnoreHit();
return;
}
}
[shader("closesthit")]
void BounceClosestHit(inout BouncePayload payload, in BuiltInTriangleIntersectionAttributes attr)
{
// Fallback for glass geometry that was accidentally built opaque. Correctly
// tagged glass is ignored in BounceAnyHit() above.
if (CurrentRayHitIsGlass())
{
payload.hit = 0;
payload.hitT = 0.0;
payload.materialFlags = DecodeInstanceMaterialFlags();
payload.pad0 = 0;
return;
}
payload.hit = 1;
payload.hitT = RayTCurrent();
payload.materialFlags = DecodeInstanceMaterialFlags();
payload.pad0 = 0;
}
float TraceShadow(float3 origin, float3 dir, float maxT)
{
RayDesc ray;
@@ -2280,6 +2415,35 @@ float TraceShadow(float3 origin, float3 dir, float maxT)
return (payload.hit != 0) ? 0.0 : 1.0;
}
bool TraceBounce(float3 origin, float3 dir, float maxT, out float hitT, out uint materialFlags)
{
RayDesc ray;
ray.Origin = origin;
ray.Direction = dir;
ray.TMin = 0.001;
ray.TMax = maxT;
BouncePayload payload;
payload.hit = 0;
payload.hitT = 0.0;
payload.materialFlags = 0;
payload.pad0 = 0;
TraceRay(
gSceneBVH,
RAY_FLAG_NONE,
0xFF,
1,
0,
1,
ray,
payload);
hitT = payload.hitT;
materialFlags = payload.materialFlags;
return payload.hit != 0;
}
float Hash12(float2 p)
{
float3 p3 = frac(float3(p.xyx) * 0.1031);
@@ -2821,6 +2985,132 @@ float TraceVisibilityBiased(float3 worldPos, float3 N, float3 dir, float maxT)
return TraceShadow(origin, dir, max(maxT - gShadowBias * 0.5, 0.001));
}
float3 SafeNormalizeOr(float3 v, float3 fallback)
{
float lenSq = dot(v, v);
if (lenSq <= 1e-8)
return fallback;
return v * rsqrt(lenSq);
}
bool ProjectClipToGBufferCandidate(
float4 clipPos,
bool flipY,
float3 rayHitPos,
inout float bestDistSq,
inout uint2 bestPixel,
inout float3 bestPos,
inout float3 bestNormal,
inout float3 bestAlbedo,
inout uint bestGeoFlag)
{
if (abs(clipPos.w) <= 1e-6)
return false;
float3 ndc = clipPos.xyz / clipPos.w;
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 false;
}
float2 uv;
uv.x = ndc.x * 0.5 + 0.5;
uv.y = flipY ? (0.5 - ndc.y * 0.5) : (ndc.y * 0.5 + 0.5);
if (uv.x < 0.0 || uv.x > 1.0 || uv.y < 0.0 || uv.y > 1.0)
return false;
int2 ip = int2(uv * gScreenSize.xy);
ip = clamp(ip, int2(0, 0), int2((int)gScreenSize.x - 1, (int)gScreenSize.y - 1));
float depth = gDepthTex.Load(int3(ip, 0));
if (depth <= 0.0 || depth >= 1.0)
return false;
float4 posSample = gPositionTex.Load(int3(ip, 0));
float3 gbufPos = posSample.xyz;
float3 delta = gbufPos - rayHitPos;
float distSq = dot(delta, delta);
if (distSq >= bestDistSq)
return false;
float4 normalSample = LoadSceneNormal(uint2(ip));
float3 gbufNormal = SafeNormalizeOr(normalSample.xyz, float3(0.0, 0.0, 1.0));
float3 gbufAlbedo = saturate(gAlbedoTex.Load(int3(ip, 0)).rgb);
bestDistSq = distSq;
bestPixel = uint2(ip);
bestPos = gbufPos;
bestNormal = gbufNormal;
bestAlbedo = gbufAlbedo;
bestGeoFlag = DecodeGeometryFlag(posSample.w);
return true;
}
bool TryFetchGBufferAtRayHit(
float3 rayHitPos,
out uint2 hitPixel,
out float3 hitPos,
out float3 hitNormal,
out float3 hitAlbedo,
out uint hitGeoFlag)
{
float bestDistSq = 1.0e30;
uint2 bestPixel = uint2(0, 0);
float3 bestPos = rayHitPos;
float3 bestNormal = float3(0.0, 0.0, 1.0);
float3 bestAlbedo = float3(0.5, 0.5, 0.5);
uint bestGeoFlag = GEOMETRY_FLAG_NONE;
// CPU computes gViewProj from the inverse view-projection matrix. Try both
// matrix-vector orders and both Y conventions so this remains tolerant of
// row/column-major engine uploads and render-target origin conventions.
float4 wpos = float4(rayHitPos, 1.0);
float4 clipA = mul(wpos, gViewProj);
float4 clipB = mul(gViewProj, wpos);
bool found = false;
found = ProjectClipToGBufferCandidate(clipA, true, rayHitPos, bestDistSq, bestPixel, bestPos, bestNormal, bestAlbedo, bestGeoFlag) || found;
found = ProjectClipToGBufferCandidate(clipA, false, rayHitPos, bestDistSq, bestPixel, bestPos, bestNormal, bestAlbedo, bestGeoFlag) || found;
found = ProjectClipToGBufferCandidate(clipB, true, rayHitPos, bestDistSq, bestPixel, bestPos, bestNormal, bestAlbedo, bestGeoFlag) || found;
found = ProjectClipToGBufferCandidate(clipB, false, rayHitPos, bestDistSq, bestPixel, bestPos, bestNormal, bestAlbedo, bestGeoFlag) || found;
if (!found)
{
hitPixel = uint2(0, 0);
hitPos = rayHitPos;
hitNormal = bestNormal;
hitAlbedo = bestAlbedo;
hitGeoFlag = GEOMETRY_FLAG_NONE;
return false;
}
// The TLAS hit is exact, but the material data comes from the camera G-buffer.
// Reject projections that land on an unrelated visible surface.
float viewDist = length(rayHitPos - gCameraPos.xyz);
float maxPositionError = max(24.0, viewDist * 0.035);
if (bestDistSq > maxPositionError * maxPositionError)
{
hitPixel = bestPixel;
hitPos = bestPos;
hitNormal = bestNormal;
hitAlbedo = bestAlbedo;
hitGeoFlag = bestGeoFlag;
return false;
}
hitPixel = bestPixel;
hitPos = bestPos;
hitNormal = bestNormal;
hitAlbedo = bestAlbedo;
hitGeoFlag = bestGeoFlag;
return true;
}
float3 EstimatePathTracedSky(float3 worldPos, float3 N, inout uint rng)
{
const float SKY_TMAX = 1000000.0;
@@ -2968,6 +3258,256 @@ float3 PathTraceDirectRectLight(float3 worldPos, float3 N, float3 V, float3 base
return clamp(Lgt.color * (Lgt.intensity * NdotL * faceTerm * atten * shadow), 0.0, 4.0);
}
float3 EstimateFastBounceLight(float3 hitPos, float3 hitN, Light Lgt)
{
// Secondary-bounce lighting needs to be cheap. The primary pass already casts
// detailed visibility rays. Here we evaluate every active light analytically
// without extra shadow rays so all lights still contribute to GI, but the
// indirect path no longer explodes into many TraceRay() calls per pixel.
if (Lgt.type == GL_RAYTRACING_LIGHT_TYPE_POINT)
{
float3 toLight = Lgt.position - hitPos;
float dist = length(toLight);
if (dist <= 0.01)
return 0.0;
float3 L = toLight / dist;
float atten = ComputePointLightAttenuation(hitPos, Lgt);
if (atten <= 0.0)
return 0.0;
float wrap = 0.35;
float nDotL = saturate((dot(hitN, L) + wrap) / (1.0 + wrap));
return Lgt.color * (Lgt.intensity * atten * nDotL);
}
else if (Lgt.type == GL_RAYTRACING_LIGHT_TYPE_SPOT)
{
float3 toLight = Lgt.position - hitPos;
float dist = length(toLight);
if (dist <= 0.01)
return 0.0;
float3 L = toLight / dist;
float atten = ComputeSpotLightAttenuation(hitPos, Lgt);
if (atten <= 0.0)
return 0.0;
float wrap = 0.35;
float nDotL = saturate((dot(hitN, L) + wrap) / (1.0 + wrap));
return Lgt.color * (Lgt.intensity * atten * nDotL);
}
else if (Lgt.type == GL_RAYTRACING_LIGHT_TYPE_RECT)
{
// Use the rect center for the bounce estimate. It is stable and avoids
// spending another random sample plus visibility ray on secondary hits.
float3 toCenter = Lgt.position - hitPos;
float centerDist = length(toCenter);
if (centerDist <= 0.01)
return 0.0;
float attenRadius = max(Lgt.radius, 1e-4);
float atten = saturate((attenRadius - centerDist) / attenRadius);
atten = atten * atten;
if (atten <= 0.0)
return 0.0;
float3 L = toCenter / centerDist;
float nDotL = saturate(dot(hitN, L));
if (nDotL <= 0.0)
return 0.0;
float faceTerm = (Lgt.twoSided != 0)
? abs(dot(-L, Lgt.normal))
: saturate(dot(-L, Lgt.normal));
if (faceTerm <= 0.0)
return 0.0;
return clamp(Lgt.color * (Lgt.intensity * nDotL * faceTerm * atten), 0.0, 4.0);
}
return 0.0;
}
float3 EstimateDirectLightingForBounceHit(uint2 hitPixel, float3 hitPos, float3 hitN, float3 hitV, float3 hitAlbedo, uint hitGeoFlag, inout uint rng)
{
bool hitIsSkeletal = (hitGeoFlag & GEOMETRY_FLAG_SKELETAL) != 0u;
bool hitIsUnlit = (hitGeoFlag & GEOMETRY_FLAG_UNLIT) != 0u;
// Treat unlit G-buffer surfaces as emissive-ish for bounce purposes. This is
// useful for light cards / bright UI-like surfaces that deliberately bypass
// the regular lighting pass.
if (hitIsUnlit)
return hitAlbedo;
// Cheap environment term. The expensive sky visibility probes stay in the
// primary lighting path; doing them again for every secondary hit was a major
// source of the framerate drop.
float upness = saturate(hitN.z * 0.5 + 0.5);
float3 lighting = gAmbientColor.rgb * (gAmbientColor.a * 0.025);
lighting += GetSkyRadiance(hitN) * (0.06 + 0.10 * upness);
// Still walk the full light list, but do not cast secondary-hit shadow rays.
// That preserves the "all lights bounce" behavior while making the cost
// roughly one extra bounce TraceRay() per indirect path instead of one bounce
// TraceRay() plus many more visibility TraceRay() calls.
[loop]
for (uint i = 0; i < gLightCount; ++i)
{
lighting += EstimateFastBounceLight(hitPos, hitN, gLights[i]);
}
if (hitIsSkeletal)
lighting *= 1.10;
// Return outgoing diffuse radiance from the bounce surface. The caller adds
// this as incoming indirect light at the primary surface; primary albedo is
// applied later in RayGen just like direct lighting.
return max(hitAlbedo * max(lighting, 0.0), 0.0);
}
)"
R"(
float3 TraceOneIndirectBouncePath(uint2 pixel, float3 worldPos, float3 N, float3 V, float3 baseAlbedo, inout uint rng)
{
const float BOUNCE_TMAX = 1000000.0;
uint maxIndirectDepth = (gMaxBounces > 1u) ? 1u : 0u;
// Extra diffuse depths are very expensive because each depth launches another
// DXR ray. Keep the common 1-2 SPP mode to one indirect hit. Higher SPP can
// opt into one additional diffuse depth, capped at two total secondary hits.
if (gSamplesPerPixel >= 4u)
maxIndirectDepth = min(gMaxBounces - 1u, 2u);
float3 accum = 0.0;
float3 throughput = 1.0;
float3 pathPos = worldPos;
float3 pathNormal = N;
float3 pathView = V;
uint2 pathPixel = pixel;
[loop]
for (uint depth = 0; depth < maxIndirectDepth; ++depth)
{
float3 bounceDir = SampleCosineWorld(pathNormal, rng);
float NoD = saturate(dot(pathNormal, bounceDir));
float normalBias = lerp(gShadowBias * 3.0, gShadowBias * 0.75, NoD);
float3 bounceOrigin =
pathPos +
pathNormal * normalBias +
bounceDir * (gShadowBias * 0.5);
float hitT = 0.0;
uint materialFlags = 0;
bool hit = TraceBounce(bounceOrigin, bounceDir, BOUNCE_TMAX, hitT, materialFlags);
if (!hit)
{
// A miss is ordinary environment lighting for the path. Keep this
// modest because the main pass already has stable direct sky terms.
accum += throughput * GetSkyRadiance(bounceDir) * 0.18;
break;
}
float3 rayHitPos = bounceOrigin + bounceDir * hitT;
uint2 hitPixel = pathPixel;
float3 hitPos = rayHitPos;
float3 hitNormal = SafeNormalizeOr(-bounceDir, pathNormal);
float3 hitAlbedo = float3(0.55, 0.55, 0.55);
uint hitGeoFlag = GEOMETRY_FLAG_NONE;
bool hasGBufferMaterial = TryFetchGBufferAtRayHit(
rayHitPos,
hitPixel,
hitPos,
hitNormal,
hitAlbedo,
hitGeoFlag);
// If the G-buffer normal points away from the incoming bounce ray, flip it
// so direct lighting at the secondary hit is evaluated on the side that
// the ray actually reached.
if (dot(hitNormal, -bounceDir) < 0.0)
hitNormal = -hitNormal;
float3 hitV = SafeNormalizeOr(-bounceDir, pathView);
float3 bouncedRadiance = EstimateDirectLightingForBounceHit(
hitPixel,
hitPos,
hitNormal,
hitV,
hitAlbedo,
hitGeoFlag,
rng);
if (!hasGBufferMaterial)
{
// IMPORTANT: this is the path that made some lights look like they
// were not bouncing. The TLAS can hit an off-screen or camera-hidden
// surface, and the old code returned only a tiny sky fallback because
// it could not fetch albedo/normal from the G-buffer. Still shade the
// real ray hit against every active light using a neutral diffuse
// material so point, spot, and rect lights all contribute to bounce.
float3 skyTint = GetSkyRadiance(SafeNormalizeOr(reflect(bounceDir, pathNormal), float3(0.0, 0.0, 1.0)));
bouncedRadiance += skyTint * 0.035;
}
accum += throughput * bouncedRadiance;
if ((hitGeoFlag & GEOMETRY_FLAG_UNLIT) != 0u)
break;
// Cosine-weighted diffuse sampling cancels the Lambertian cosine/pdf term,
// so the path throughput is the surface albedo plus a conservative energy
// scale to keep multiple G-buffer-assisted bounces stable.
throughput *= saturate(hitAlbedo) * 0.68;
if (max(max(throughput.x, throughput.y), throughput.z) < 0.02)
break;
pathPos = hitPos;
pathNormal = hitNormal;
pathView = hitV;
pathPixel = hitPixel;
}
return accum;
}
float3 EstimatePathTracedIndirectBounce(uint2 pixel, float3 worldPos, float3 N, float3 V, float3 baseAlbedo, inout uint rng)
{
if (gMaxBounces <= 1u)
return 0.0;
// The previous version forced up to four secondary rays per lighting sample,
// which made the pass scale badly with light count and SPP. One indirect ray
// is enough in the common realtime mode because each secondary hit is now
// shaded against every active light. Higher SPP can buy a little more GI
// coverage without tanking the default framerate.
uint indirectRayCount = 1u;
if (gSamplesPerPixel >= 6u && gLightCount > 2u)
indirectRayCount = 2u;
if (gSamplesPerPixel >= 8u && gLightCount > 5u)
indirectRayCount = 3u;
indirectRayCount = min(indirectRayCount, 3u);
float3 accum = 0.0;
[loop]
for (uint r = 0; r < indirectRayCount; ++r)
{
accum += TraceOneIndirectBouncePath(pixel, worldPos, N, V, baseAlbedo, rng);
}
accum /= (float)indirectRayCount;
const float INDIRECT_STRENGTH = 0.65;
return accum * INDIRECT_STRENGTH;
}
float3 PathTraceLightingSample(uint2 pixel, float3 worldPos, float3 N, float3 V, float3 baseAlbedo, bool isSkeletal, inout uint rng, out float3 specularAccum)
{
specularAccum = 0.0;
@@ -2993,9 +3533,9 @@ float3 PathTraceLightingSample(uint2 pixel, float3 worldPos, float3 N, float3 V,
lightingAccum += skyColor * (0.70 * skyVis);
lightingAccum += ambientSkyVis * (skyColorRGB * 0.15);
if (gMaxBounces > 1u && gSamplesPerPixel > 1u)
if (gMaxBounces > 1u)
{
lightingAccum += EstimatePathTracedSky(worldPos, N, rng) * 0.20;
lightingAccum += EstimatePathTracedIndirectBounce(pixel, worldPos, N, V, baseAlbedo, rng);
}
if (isSkeletal)
@@ -3109,6 +3649,7 @@ cbuffer LightingCB : register(b0)
{
float4x4 gInvViewProj;
float4x4 gInvViewMatrix;
float4x4 gViewProj;
float4 gCameraPos;
float4 gAmbientColor;
float4 gScreenSize;
@@ -3664,11 +4205,14 @@ static int glRaytracingLightingCreateStateObject(void)
if (!dxil)
return 0;
D3D12_EXPORT_DESC exports[4] = {};
D3D12_EXPORT_DESC exports[7] = {};
exports[0].Name = L"RayGen";
exports[1].Name = L"ShadowMiss";
exports[2].Name = L"ShadowAnyHit";
exports[3].Name = L"ShadowClosestHit";
exports[4].Name = L"BounceMiss";
exports[5].Name = L"BounceAnyHit";
exports[6].Name = L"BounceClosestHit";
D3D12_DXIL_LIBRARY_DESC libDesc = {};
D3D12_SHADER_BYTECODE libBytecode = {};
@@ -3678,14 +4222,19 @@ static int glRaytracingLightingCreateStateObject(void)
libDesc.NumExports = _countof(exports);
libDesc.pExports = exports;
D3D12_HIT_GROUP_DESC hitGroup = {};
hitGroup.HitGroupExport = L"ShadowHitGroup";
hitGroup.AnyHitShaderImport = L"ShadowAnyHit";
hitGroup.ClosestHitShaderImport = L"ShadowClosestHit";
hitGroup.Type = D3D12_HIT_GROUP_TYPE_TRIANGLES;
D3D12_HIT_GROUP_DESC hitGroups[2] = {};
hitGroups[0].HitGroupExport = L"ShadowHitGroup";
hitGroups[0].AnyHitShaderImport = L"ShadowAnyHit";
hitGroups[0].ClosestHitShaderImport = L"ShadowClosestHit";
hitGroups[0].Type = D3D12_HIT_GROUP_TYPE_TRIANGLES;
hitGroups[1].HitGroupExport = L"BounceHitGroup";
hitGroups[1].AnyHitShaderImport = L"BounceAnyHit";
hitGroups[1].ClosestHitShaderImport = L"BounceClosestHit";
hitGroups[1].Type = D3D12_HIT_GROUP_TYPE_TRIANGLES;
D3D12_RAYTRACING_SHADER_CONFIG shaderConfig = {};
shaderConfig.MaxPayloadSizeInBytes = sizeof(uint32_t);
shaderConfig.MaxPayloadSizeInBytes = 16; // BouncePayload: uint + float + uint + uint.
shaderConfig.MaxAttributeSizeInBytes = 8;
D3D12_GLOBAL_ROOT_SIGNATURE globalRS = {};
@@ -3702,7 +4251,11 @@ static int glRaytracingLightingCreateStateObject(void)
++sub;
subobjects[sub].Type = D3D12_STATE_SUBOBJECT_TYPE_HIT_GROUP;
subobjects[sub].pDesc = &hitGroup;
subobjects[sub].pDesc = &hitGroups[0];
++sub;
subobjects[sub].Type = D3D12_STATE_SUBOBJECT_TYPE_HIT_GROUP;
subobjects[sub].pDesc = &hitGroups[1];
++sub;
subobjects[sub].Type = D3D12_STATE_SUBOBJECT_TYPE_RAYTRACING_SHADER_CONFIG;
@@ -3717,9 +4270,17 @@ static int glRaytracingLightingCreateStateObject(void)
subobjects[sub].pDesc = &localRS;
++sub;
LPCWSTR localExports[] = { L"RayGen", L"ShadowMiss", L"ShadowHitGroup" };
LPCWSTR localExports[] =
{
L"RayGen",
L"ShadowMiss",
L"ShadowHitGroup",
L"BounceMiss",
L"BounceHitGroup"
};
D3D12_SUBOBJECT_TO_EXPORTS_ASSOCIATION assoc = {};
assoc.pSubobjectToAssociate = &subobjects[4];
assoc.pSubobjectToAssociate = &subobjects[5];
assoc.NumExports = _countof(localExports);
assoc.pExports = localExports;
@@ -3747,10 +4308,12 @@ static int glRaytracingLightingCreateStateObject(void)
static int glRaytracingLightingCreateShaderTables(void)
{
void* raygenId = g_glRaytracingLighting.rtStateProps->GetShaderIdentifier(L"RayGen");
void* missId = g_glRaytracingLighting.rtStateProps->GetShaderIdentifier(L"ShadowMiss");
void* hitId = g_glRaytracingLighting.rtStateProps->GetShaderIdentifier(L"ShadowHitGroup");
void* shadowMissId = g_glRaytracingLighting.rtStateProps->GetShaderIdentifier(L"ShadowMiss");
void* bounceMissId = g_glRaytracingLighting.rtStateProps->GetShaderIdentifier(L"BounceMiss");
void* shadowHitId = g_glRaytracingLighting.rtStateProps->GetShaderIdentifier(L"ShadowHitGroup");
void* bounceHitId = g_glRaytracingLighting.rtStateProps->GetShaderIdentifier(L"BounceHitGroup");
if (!raygenId || !missId || !hitId)
if (!raygenId || !shadowMissId || !bounceMissId || !shadowHitId || !bounceHitId)
{
glRaytracingFatal("Failed to fetch shader identifiers");
return 0;
@@ -3758,6 +4321,8 @@ static int glRaytracingLightingCreateShaderTables(void)
const UINT shaderIdSize = D3D12_SHADER_IDENTIFIER_SIZE_IN_BYTES;
const UINT recordSize = (UINT)glRaytracingAlignUp(shaderIdSize, D3D12_RAYTRACING_SHADER_RECORD_BYTE_ALIGNMENT);
const UINT missTableSize = recordSize * 2u;
const UINT hitTableSize = recordSize * 2u;
g_glRaytracingLighting.raygenTable = glRaytracingCreateBuffer(
g_glRaytracingCmd.device.Get(),
@@ -3768,14 +4333,14 @@ static int glRaytracingLightingCreateShaderTables(void)
g_glRaytracingLighting.missTable = glRaytracingCreateBuffer(
g_glRaytracingCmd.device.Get(),
recordSize,
missTableSize,
D3D12_HEAP_TYPE_UPLOAD,
D3D12_RESOURCE_STATE_GENERIC_READ,
D3D12_RESOURCE_FLAG_NONE);
g_glRaytracingLighting.hitTable = glRaytracingCreateBuffer(
g_glRaytracingCmd.device.Get(),
recordSize,
hitTableSize,
D3D12_HEAP_TYPE_UPLOAD,
D3D12_RESOURCE_STATE_GENERIC_READ,
D3D12_RESOURCE_FLAG_NONE);
@@ -3787,19 +4352,22 @@ static int glRaytracingLightingCreateShaderTables(void)
return 0;
}
uint8_t temp[256] = {};
std::vector<uint8_t> temp;
temp.resize((size_t)max(recordSize, max(missTableSize, hitTableSize)), 0);
memset(temp, 0, sizeof(temp));
memcpy(temp, raygenId, shaderIdSize);
glRaytracingMapCopy(g_glRaytracingLighting.raygenTable.resource.Get(), temp, recordSize);
memset(temp.data(), 0, temp.size());
memcpy(temp.data(), raygenId, shaderIdSize);
glRaytracingMapCopy(g_glRaytracingLighting.raygenTable.resource.Get(), temp.data(), recordSize);
memset(temp, 0, sizeof(temp));
memcpy(temp, missId, shaderIdSize);
glRaytracingMapCopy(g_glRaytracingLighting.missTable.resource.Get(), temp, recordSize);
memset(temp.data(), 0, temp.size());
memcpy(temp.data(), shadowMissId, shaderIdSize);
memcpy(temp.data() + recordSize, bounceMissId, shaderIdSize);
glRaytracingMapCopy(g_glRaytracingLighting.missTable.resource.Get(), temp.data(), missTableSize);
memset(temp, 0, sizeof(temp));
memcpy(temp, hitId, shaderIdSize);
glRaytracingMapCopy(g_glRaytracingLighting.hitTable.resource.Get(), temp, recordSize);
memset(temp.data(), 0, temp.size());
memcpy(temp.data(), shadowHitId, shaderIdSize);
memcpy(temp.data() + recordSize, bounceHitId, shaderIdSize);
glRaytracingMapCopy(g_glRaytracingLighting.hitTable.resource.Get(), temp.data(), hitTableSize);
return 1;
}
@@ -4053,10 +4621,10 @@ static bool glRaytracingLightingExecuteInternal(
rays.RayGenerationShaderRecord.StartAddress = g_glRaytracingLighting.raygenTable.gpuVA;
rays.RayGenerationShaderRecord.SizeInBytes = shaderRecordSize;
rays.MissShaderTable.StartAddress = g_glRaytracingLighting.missTable.gpuVA;
rays.MissShaderTable.SizeInBytes = shaderRecordSize;
rays.MissShaderTable.SizeInBytes = shaderRecordSize * 2u;
rays.MissShaderTable.StrideInBytes = shaderRecordSize;
rays.HitGroupTable.StartAddress = g_glRaytracingLighting.hitTable.gpuVA;
rays.HitGroupTable.SizeInBytes = shaderRecordSize;
rays.HitGroupTable.SizeInBytes = shaderRecordSize * 2u;
rays.HitGroupTable.StrideInBytes = shaderRecordSize;
rays.Width = pass->width;
rays.Height = pass->height;
@@ -4201,6 +4769,9 @@ bool glRaytracingLightingInit(void)
return false;
memset(&g_glRaytracingLighting.constants, 0, sizeof(g_glRaytracingLighting.constants));
glRaytracingSetIdentity4x4(g_glRaytracingLighting.constants.invViewProj);
glRaytracingSetIdentity4x4(g_glRaytracingLighting.constants.invViewMatrix);
glRaytracingSetIdentity4x4(g_glRaytracingLighting.constants.viewProj);
g_glRaytracingLighting.constants.ambientColor[0] = 0.08f;
g_glRaytracingLighting.constants.ambientColor[1] = 0.08f;
g_glRaytracingLighting.constants.ambientColor[2] = 0.09f;
@@ -4344,6 +4915,10 @@ void glRaytracingLightingSetInvViewProjMatrix(const float* m16)
glRaytracingLightingResetDenoiseHistory();
memcpy(g_glRaytracingLighting.constants.invViewProj, m16, sizeof(float) * 16);
if (!glRaytracingInvertMatrix4x4(m16, g_glRaytracingLighting.constants.viewProj))
glRaytracingSetIdentity4x4(g_glRaytracingLighting.constants.viewProj);
glRaytracingLightingUpdateConstants();
}
+1 -1
View File
@@ -943,7 +943,7 @@ struct GLState
bool motionHistoryReset = true;
QD3D12UpscalerBackend upscalerBackend = QD3D12_UPSCALER_DLSS;
QD3D12UpscalerQuality upscalerQuality = QD3D12_QUALITY_BALANCED;
QD3D12UpscalerQuality upscalerQuality = QD3D12_QUALITY_QUALITY;
bool enableRayAIDenoise = false;
bool enableDLSSRayReconstruction = true;
bool enableFSRRayRegeneration = false;