Fixed a bug with spec maps.

This commit is contained in:
Justin Marshall
2026-05-07 12:26:46 -07:00
parent 1a6bc0c447
commit 469be38dbe
6 changed files with 434 additions and 54 deletions
+80 -26
View File
@@ -2315,6 +2315,8 @@ struct glRaytracingLightingState_t
bool externalDenoiser;
ID3D12Resource* emissiveTexture;
DXGI_FORMAT emissiveFormat;
ID3D12Resource* specularTexture;
DXGI_FORMAT specularFormat;
bool uploadToCurrentFrameResource;
bool initialized;
@@ -2341,6 +2343,8 @@ struct glRaytracingLightingState_t
externalDenoiser = false;
emissiveTexture = nullptr;
emissiveFormat = DXGI_FORMAT_R16G16B16A16_FLOAT;
specularTexture = nullptr;
specularFormat = DXGI_FORMAT_R8G8B8A8_UNORM;
uploadToCurrentFrameResource = false;
initialized = false;
}
@@ -2364,14 +2368,15 @@ enum glRaytracingLightingDescriptorIndex_t
GLR_DESC_HISTORY_SRV = 9,
GLR_DESC_TEMPORAL_SRV = 10,
GLR_DESC_EMISSIVE_SRV = 11,
GLR_DESC_PATHTRACE_UAV = 12,
GLR_DESC_DENOISE_A_UAV = 13,
GLR_DESC_DENOISE_B_UAV = 14,
GLR_DESC_OUTPUT_UAV = 15,
GLR_DESC_TEMPORAL_UAV = 16,
GLR_DESC_HISTORY_UAV = 17,
GLR_DESC_COUNT = 18,
GLR_DESC_SRV_COUNT = 12,
GLR_DESC_SPECULAR_SRV = 12,
GLR_DESC_PATHTRACE_UAV = 13,
GLR_DESC_DENOISE_A_UAV = 14,
GLR_DESC_DENOISE_B_UAV = 15,
GLR_DESC_OUTPUT_UAV = 16,
GLR_DESC_TEMPORAL_UAV = 17,
GLR_DESC_HISTORY_UAV = 18,
GLR_DESC_COUNT = 19,
GLR_DESC_SRV_COUNT = 13,
GLR_DESC_UAV_COUNT = 6
};
@@ -2466,6 +2471,7 @@ Texture2D<float4> gNormalTex : register(t3);
Texture2D<float4> gPositionTex : register(t4);
RaytracingAccelerationStructure gSceneBVH : register(t5);
Texture2D<float4> gEmissiveTex : register(t11);
Texture2D<float4> gSpecularTex : register(t12);
RWTexture2D<float4> gOutputTex : register(u0);
static const uint GL_RAYTRACING_LIGHT_TYPE_POINT = 0;
@@ -3252,10 +3258,8 @@ float Doom3SpecularLookup(float x)
float3 Doom3PseudoSpecularMask(float3 baseAlbedo)
{
// Doom 3 normally uses a dedicated specular map.
// This pass does not have one bound, so DO NOT square diffuse albedo.
// Squaring albedo makes dark Doom 3 textures lose all specular response.
//
// Use luminance only as a weak hint, with a neutral floor.
// This fallback is only used for pixels whose specular G-buffer says no
// specular map was written by the raster pass.
float lum = dot(saturate(baseAlbedo), float3(0.299, 0.587, 0.114));
float specStrength = lerp(0.22, 0.72, saturate(lum * 1.35));
@@ -3268,6 +3272,18 @@ float3 Doom3PseudoSpecularMask(float3 baseAlbedo)
return max(tintedSpec, float3(0.18, 0.18, 0.18));
}
float3 LoadSceneSpecularAlbedo(uint2 pixel, float3 baseAlbedo)
{
float4 specSample = gSpecularTex.Load(int3(pixel, 0));
// The raster G-buffer writer stores alpha as a validity bit. This matters
// for black specular maps: black should mean zero specular, not "missing map".
if (specSample.a > 0.5)
return saturate(specSample.rgb);
return Doom3PseudoSpecularMask(baseAlbedo);
}
float3 ComputeSpecular(
float3 N,
float3 V,
@@ -3276,7 +3292,7 @@ float3 ComputeSpecular(
float lightIntensity,
float atten,
float shadow,
float3 baseAlbedo)
float3 specularAlbedo)
{
if (gEnableSpecular == 0)
return 0.0;
@@ -3301,7 +3317,7 @@ float3 ComputeSpecular(
float specTerm = Doom3SpecularLookup(NdotH);
float3 specMask = Doom3PseudoSpecularMask(baseAlbedo);
float3 specMask = saturate(specularAlbedo);
// Doom 3's interaction pass is strongly additive. Keep it punchy,
// but clamp enough to avoid fireflies with stochastic light sampling.
@@ -3611,7 +3627,7 @@ float3 EstimatePathTracedSky(float3 worldPos, float3 N, inout uint rng)
return accum * 0.55;
}
float3 PathTraceDirectPointLight(uint2 pixel, float3 worldPos, float3 N, float3 V, float3 baseAlbedo, Light Lgt, inout uint rng, out float3 specularOut)
float3 PathTraceDirectPointLight(uint2 pixel, float3 worldPos, float3 N, float3 V, float3 baseAlbedo, float3 specularAlbedo, Light Lgt, inout uint rng, out float3 specularOut)
{
specularOut = 0.0;
@@ -3663,7 +3679,7 @@ float3 PathTraceDirectPointLight(uint2 pixel, float3 worldPos, float3 N, float3
shadow = TraceVisibilityBiased(worldPos, N, L, dist);
if (Lgt.pointRadiusPad <= 0.5)
specAccum += ComputeSpecular(N, V, L, Lgt.color, Lgt.intensity, atten, shadow, baseAlbedo);
specAccum += ComputeSpecular(N, V, L, Lgt.color, Lgt.intensity, atten, shadow, specularAlbedo);
diffuseAccum += Lgt.color * (Lgt.intensity * atten * NdotLWrap * shadow);
}
@@ -3673,7 +3689,7 @@ float3 PathTraceDirectPointLight(uint2 pixel, float3 worldPos, float3 N, float3
return diffuseAccum * invSamples;
}
float3 PathTraceDirectSpotLight(float3 worldPos, float3 N, float3 V, float3 baseAlbedo, Light Lgt, inout uint rng, out float3 specularOut)
float3 PathTraceDirectSpotLight(float3 worldPos, float3 N, float3 V, float3 baseAlbedo, float3 specularAlbedo, Light Lgt, inout uint rng, out float3 specularOut)
{
specularOut = 0.0;
@@ -3693,12 +3709,12 @@ float3 PathTraceDirectSpotLight(float3 worldPos, float3 N, float3 V, float3 base
shadow = TraceVisibilityBiased(worldPos, N, L, dist);
if (Lgt.pointRadiusPad <= 0.5)
specularOut = ComputeSpecular(N, V, L, Lgt.color, Lgt.intensity, atten, shadow, baseAlbedo);
specularOut = ComputeSpecular(N, V, L, Lgt.color, Lgt.intensity, atten, shadow, specularAlbedo);
return Lgt.color * (Lgt.intensity * atten * NdotLWrap * shadow);
}
float3 PathTraceDirectRectLight(uint2 pixel, float3 worldPos, float3 N, float3 V, float3 baseAlbedo, Light Lgt, inout uint rng, out float3 specularOut)
float3 PathTraceDirectRectLight(uint2 pixel, float3 worldPos, float3 N, float3 V, float3 baseAlbedo, float3 specularAlbedo, Light Lgt, inout uint rng, out float3 specularOut)
{
specularOut = 0.0;
@@ -3764,7 +3780,7 @@ float3 PathTraceDirectRectLight(uint2 pixel, float3 worldPos, float3 N, float3 V
Lgt.intensity * faceTerm,
1.0,
shadow,
baseAlbedo) * atten;
specularAlbedo) * atten;
}
diffuseAccum += clamp(Lgt.color * (Lgt.intensity * NdotL * faceTerm * atten * shadow), 0.0, 4.0);
@@ -3961,21 +3977,22 @@ float3 EstimateShadowedBounceLight(uint2 hitPixel, float3 hitPos, float3 hitN, f
{
float3 spec = 0.0;
float3 diffuse = 0.0;
float3 hitSpecularAlbedo = LoadSceneSpecularAlbedo(hitPixel, hitAlbedo);
// Use the same visibility-capable direct-light samplers as the primary hit.
// This supplies proper next-event estimation at secondary hits instead of the
// old unoccluded light-list approximation.
if (Lgt.type == GL_RAYTRACING_LIGHT_TYPE_POINT)
{
diffuse = PathTraceDirectPointLight(hitPixel, hitPos, hitN, hitV, hitAlbedo, Lgt, rng, spec);
diffuse = PathTraceDirectPointLight(hitPixel, hitPos, hitN, hitV, hitAlbedo, hitSpecularAlbedo, Lgt, rng, spec);
}
else if (Lgt.type == GL_RAYTRACING_LIGHT_TYPE_SPOT)
{
diffuse = PathTraceDirectSpotLight(hitPos, hitN, hitV, hitAlbedo, Lgt, rng, spec);
diffuse = PathTraceDirectSpotLight(hitPos, hitN, hitV, hitAlbedo, hitSpecularAlbedo, Lgt, rng, spec);
}
else if (Lgt.type == GL_RAYTRACING_LIGHT_TYPE_RECT)
{
diffuse = PathTraceDirectRectLight(hitPixel, hitPos, hitN, hitV, hitAlbedo, Lgt, rng, spec);
diffuse = PathTraceDirectRectLight(hitPixel, hitPos, hitN, hitV, hitAlbedo, hitSpecularAlbedo, Lgt, rng, spec);
}
return clamp(diffuse, 0.0, 12.0);
@@ -4369,6 +4386,7 @@ float3 PathTraceDeterministicLighting(
float3 N,
float3 V,
float3 baseAlbedo,
float3 specularAlbedo,
bool isSkeletal,
float cavity,
float ao,
@@ -4411,15 +4429,15 @@ float3 PathTraceDeterministicLighting(
if (Lgt.type == GL_RAYTRACING_LIGHT_TYPE_POINT)
{
diffuse = PathTraceDirectPointLight(pixel, worldPos, N, V, baseAlbedo, Lgt, directRng, spec);
diffuse = PathTraceDirectPointLight(pixel, worldPos, N, V, baseAlbedo, specularAlbedo, Lgt, directRng, spec);
}
else if (Lgt.type == GL_RAYTRACING_LIGHT_TYPE_SPOT)
{
diffuse = PathTraceDirectSpotLight(worldPos, N, V, baseAlbedo, Lgt, directRng, spec);
diffuse = PathTraceDirectSpotLight(worldPos, N, V, baseAlbedo, specularAlbedo, Lgt, directRng, spec);
}
else if (Lgt.type == GL_RAYTRACING_LIGHT_TYPE_RECT)
{
diffuse = PathTraceDirectRectLight(pixel, worldPos, N, V, baseAlbedo, Lgt, directRng, spec);
diffuse = PathTraceDirectRectLight(pixel, worldPos, N, V, baseAlbedo, specularAlbedo, Lgt, directRng, spec);
}
lightingAccum += diffuse;
@@ -4452,6 +4470,7 @@ void RayGen()
}
float3 baseAlbedo = albedoSample.rgb;
float3 specularAlbedo = LoadSceneSpecularAlbedo(pixel, baseAlbedo);
float4 positionSample = gPositionTex.Load(int3(pixel, 0));
float3 worldPos = positionSample.xyz;
float4 normalSample = LoadSceneNormal(pixel);
@@ -4502,6 +4521,7 @@ void RayGen()
N,
V,
baseAlbedo,
specularAlbedo,
isSkeletal,
cavity,
ao,
@@ -5780,6 +5800,30 @@ static void glRaytracingLightingCreatePerPassDescriptors(
g_glRaytracingCmd.device->CreateShaderResourceView(g_glRaytracingLighting.emissiveTexture, &emissiveSrv,
glRaytracingOffsetCpu(base, g_glRaytracingLighting.descriptorStride, GLR_DESC_EMISSIVE_SRV));
D3D12_SHADER_RESOURCE_VIEW_DESC specularSrv = {};
specularSrv.ViewDimension = D3D12_SRV_DIMENSION_TEXTURE2D;
specularSrv.Texture2D.MipLevels = 1;
ID3D12Resource* specularResource = g_glRaytracingLighting.specularTexture;
if (specularResource)
{
specularSrv.Shader4ComponentMapping = D3D12_DEFAULT_SHADER_4_COMPONENT_MAPPING;
specularSrv.Format = g_glRaytracingLighting.specularFormat;
}
else
{
// Bind a harmless fallback descriptor when the shim has not provided a
// specular G-buffer. The shader sees alpha zero and uses the legacy fallback.
specularResource = pass->albedoTexture;
specularSrv.Shader4ComponentMapping = D3D12_ENCODE_SHADER_4_COMPONENT_MAPPING(
D3D12_SHADER_COMPONENT_MAPPING_FROM_MEMORY_COMPONENT_0,
D3D12_SHADER_COMPONENT_MAPPING_FROM_MEMORY_COMPONENT_1,
D3D12_SHADER_COMPONENT_MAPPING_FROM_MEMORY_COMPONENT_2,
D3D12_SHADER_COMPONENT_MAPPING_FORCE_VALUE_0);
specularSrv.Format = pass->albedoFormat;
}
g_glRaytracingCmd.device->CreateShaderResourceView(specularResource, &specularSrv,
glRaytracingOffsetCpu(base, g_glRaytracingLighting.descriptorStride, GLR_DESC_SPECULAR_SRV));
D3D12_UNORDERED_ACCESS_VIEW_DESC rayOutputUav = {};
rayOutputUav.ViewDimension = D3D12_UAV_DIMENSION_TEXTURE2D;
rayOutputUav.Format = GL_RAYTRACING_DENOISE_FORMAT;
@@ -6428,6 +6472,16 @@ void glRaytracingLightingSetEmissiveInput(ID3D12Resource* texture, DXGI_FORMAT f
: format;
}
void glRaytracingLightingSetSpecularInput(ID3D12Resource* texture, DXGI_FORMAT format)
{
std::lock_guard<std::mutex> lock(g_glRaytracingMutex);
g_glRaytracingLighting.specularTexture = texture;
g_glRaytracingLighting.specularFormat = (format == DXGI_FORMAT_UNKNOWN)
? DXGI_FORMAT_R8G8B8A8_UNORM
: format;
}
bool glRaytracingLightingExecuteForScene(const glRaytracingLightingPassDesc_t* pass, glRaytracingSceneHandle_t worldHandle)
{
std::lock_guard<std::mutex> lock(g_glRaytracingMutex);