Files
2026-05-28 21:06:39 -07:00

420 lines
11 KiB
C++

#include "precompiled.h"
#pragma hdrstop
#include "tr_local.h"
#include <math.h>
static idVec3 RB_DXRMakeVec3(float x, float y, float z)
{
idVec3 v;
v.x = x;
v.y = y;
v.z = z;
return v;
}
static idVec3 RB_DXRSubVec3(const idVec3& a, const idVec3& b)
{
return RB_DXRMakeVec3(a.x - b.x, a.y - b.y, a.z - b.z);
}
static idVec3 RB_DXRScaleVec3(const idVec3& v, float s)
{
return RB_DXRMakeVec3(v.x * s, v.y * s, v.z * s);
}
static float RB_DXRDotVec3(const idVec3& a, const idVec3& b)
{
return a.x * b.x + a.y * b.y + a.z * b.z;
}
static idVec3 RB_DXRCrossVec3(const idVec3& a, const idVec3& b)
{
return RB_DXRMakeVec3(
a.y * b.z - a.z * b.y,
a.z * b.x - a.x * b.z,
a.x * b.y - a.y * b.x);
}
static float RB_DXRLengthVec3(const idVec3& v)
{
return sqrtf(v.x * v.x + v.y * v.y + v.z * v.z);
}
static idVec3 RB_DXRNormalizeVec3Safe(const idVec3& v, const idVec3& fallback)
{
const float len = RB_DXRLengthVec3(v);
if (len > 1e-20f)
{
const float invLen = 1.0f / len;
return RB_DXRMakeVec3(v.x * invLen, v.y * invLen, v.z * invLen);
}
return fallback;
}
static idVec3 RB_DXRTransformLightVector(const idMat3& axis, const idVec3& v)
{
return RB_DXRMakeVec3(
axis[0].x * v.x + axis[1].x * v.y + axis[2].x * v.z,
axis[0].y * v.x + axis[1].y * v.y + axis[2].y * v.z,
axis[0].z * v.x + axis[1].z * v.y + axis[2].z * v.z);
}
static void RB_DXRReportPathTraceProfile(void)
{
const int showProfile = r_showPathTraceProfile.GetInteger();
if (showProfile <= 0)
return;
static int lastPrintTime = 0;
if (showProfile == 1)
{
const int now = Sys_Milliseconds();
if (now - lastPrintTime < 1000)
return;
lastPrintTime = now;
}
glRaytracingProfileCounters_t profile;
if (!glRaytracingLightingGetProfileCounters(&profile))
return;
const uint32_t pixels = profile.width * profile.height;
common->Printf(
"pathTrace: %.2fms %ux%u px=%u lights=%u p/s/r=%u/%u/%u shadow=%u vol=%u spp=%u bounces=%u meshes=%u inst=%u rays/px direct=%u shadow=%u gi=%u giLight=%u gather=%u refl=%u volume=%u denoise=%u\n",
profile.gpuMsec,
profile.width,
profile.height,
pixels,
profile.lightCount,
profile.pointLightCount,
profile.spotLightCount,
profile.rectLightCount,
profile.shadowCastingLightCount,
profile.volumetricLightCount,
profile.samplesPerPixel,
profile.maxBounces,
profile.meshCount,
profile.instanceCount,
profile.directLightSamplesPerPixel,
profile.directShadowRaysPerPixel,
profile.indirectBounceRaysPerPixel,
profile.indirectLightEvalsPerPixel,
profile.finalGatherRaysPerPixel,
profile.reflectionRaysPerPixel,
profile.volumetricShadowRaysPerPixel,
profile.enableDenoiser);
}
static glRaytracingLight_t RB_DXRMakeSpotLightFromRenderLight(
const renderLight_t& srcLight,
const idVec3& worldOrigin,
float r, float g, float b,
float intensity)
{
const idVec3 targetW = RB_DXRTransformLightVector(srcLight.axis, srcLight.target);
const idVec3 rightW = RB_DXRTransformLightVector(srcLight.axis, srcLight.right);
const idVec3 upW = RB_DXRTransformLightVector(srcLight.axis, srcLight.up);
const idVec3 startW = RB_DXRTransformLightVector(srcLight.axis, srcLight.start);
const idVec3 endW = RB_DXRTransformLightVector(srcLight.axis, srcLight.end);
idVec3 fallbackDir = RB_DXRCrossVec3(rightW, upW);
fallbackDir = RB_DXRNormalizeVec3Safe(fallbackDir, RB_DXRMakeVec3(0.0f, 0.0f, -1.0f));
const idVec3 dir = RB_DXRNormalizeVec3Safe(targetW, fallbackDir);
float centerDepth = RB_DXRDotVec3(targetW, dir);
if (centerDepth <= 1e-4f)
{
centerDepth = RB_DXRLengthVec3(targetW);
}
if (centerDepth <= 1e-4f)
{
centerDepth = RB_DXRDotVec3(endW, dir);
}
if (centerDepth <= 1e-4f)
{
centerDepth = 64.0f;
}
const idVec3 rightPerp = RB_DXRSubVec3(
rightW,
RB_DXRScaleVec3(dir, RB_DXRDotVec3(rightW, dir)));
const idVec3 upPerp = RB_DXRSubVec3(
upW,
RB_DXRScaleVec3(dir, RB_DXRDotVec3(upW, dir)));
idVec3 axisU = RB_DXRCrossVec3(RB_DXRMakeVec3(0.0f, 0.0f, 1.0f), dir);
axisU = RB_DXRNormalizeVec3Safe(axisU, RB_DXRMakeVec3(1.0f, 0.0f, 0.0f));
axisU = RB_DXRNormalizeVec3Safe(rightPerp, axisU);
idVec3 axisV = RB_DXRCrossVec3(dir, axisU);
axisV = RB_DXRNormalizeVec3Safe(axisV, RB_DXRMakeVec3(0.0f, 1.0f, 0.0f));
if (RB_DXRDotVec3(axisV, upPerp) < 0.0f)
{
axisV = RB_DXRScaleVec3(axisV, -1.0f);
}
float tanHalfWidth = RB_DXRLengthVec3(rightPerp) / centerDepth;
float tanHalfHeight = RB_DXRLengthVec3(upPerp) / centerDepth;
float nearPlane = RB_DXRDotVec3(startW, dir);
if (nearPlane < 0.0f)
{
nearPlane = 0.0f;
}
float farPlane = RB_DXRDotVec3(endW, dir);
if (farPlane < centerDepth)
{
farPlane = centerDepth;
}
if (farPlane <= nearPlane)
{
farPlane = nearPlane + 64.0f;
}
glRaytracingLight_t light = glRaytracingLightingMakeSpotLight(
worldOrigin.x, worldOrigin.y, worldOrigin.z,
dir.x, dir.y, dir.z,
axisU.x, axisU.y, axisU.z,
axisV.x, axisV.y, axisV.z,
nearPlane,
farPlane,
tanHalfWidth,
tanHalfHeight,
r, g, b,
intensity,
1u);
light.samples = srcLight.noShadows ? 0u : 1u;
light.pointRadiusPad = srcLight.noSpecular ? 1.0f : 0.0f;
return light;
}
static void RB_DXRAddUniquePathTraceArea(idList<int>& areas, int areaNum)
{
if (areaNum < 0)
return;
if (areas.FindIndex(areaNum) >= 0)
return;
areas.Append(areaNum);
}
static int RB_DXRSingleReferencedLightArea(const idRenderLightLocal* light)
{
if (!light)
return -1;
int singleArea = -1;
for (const areaReference_t* ref = light->references; ref; ref = ref->ownerNext)
{
if (!ref->area)
continue;
const int areaNum = ref->area->areaNum;
if (singleArea < 0)
{
singleArea = areaNum;
continue;
}
if (singleArea != areaNum)
{
return -1;
}
}
return singleArea;
}
static bool RB_DXRBuildPathTraceLightAreaSet(idRenderWorldLocal* world, idList<int>& areas)
{
areas.Clear();
if (!r_pathTraceAreaLightCulling.GetBool())
return false;
if (!world || !backEnd.viewDef)
return false;
if (backEnd.viewDef->areaNum < 0)
return false;
if (world->pathTraceAreas.Num() != world->numPortalAreas)
return false;
const int depth = r_pathTraceAreaLightDepth.GetInteger();
RB_DXRAddUniquePathTraceArea(areas, backEnd.viewDef->areaNum);
int scanStart = 0;
for (int step = 0; step < depth; step++)
{
const int scanEnd = areas.Num();
for (int i = scanStart; i < scanEnd; i++)
{
const int areaNum = areas[i];
if (areaNum < 0 || areaNum >= world->pathTraceAreas.Num())
continue;
const idRenderWorldLocal::pathTraceArea_t& area = world->pathTraceAreas[areaNum];
if (!area.valid)
continue;
for (int neighborIndex = 0; neighborIndex < area.neighbors.Num(); neighborIndex++)
{
RB_DXRAddUniquePathTraceArea(areas, area.neighbors[neighborIndex]);
}
}
if (areas.Num() == scanEnd)
break;
scanStart = scanEnd;
}
return areas.Num() > 0;
}
static bool RB_DXRPathTraceLightTouchesAreaSet(const idRenderLightLocal* light, const idList<int>& areas)
{
if (!light)
return true;
if (light->areaNum >= 0 && areas.FindIndex(light->areaNum) >= 0)
return true;
for (const areaReference_t* ref = light->references; ref; ref = ref->ownerNext)
{
if (ref->area && areas.FindIndex(ref->area->areaNum) >= 0)
return true;
}
return false;
}
/*
====================
RB_DXDrawInteractions
====================
*/
void RB_DXDrawInteractions(void)
{
viewLight_t* vLight;
QD3D12_SetToneMapBrightness(r_brightness.GetFloat());
QD3D12_SetFrameGenerationMultiplier(r_frameGen.GetInteger());
QD3D12_SetDepthOfField(
r_depthOfField.GetBool() ? 1 : 0,
r_dofFocusDistance.GetFloat(),
r_dofFocusRange.GetFloat(),
r_dofMaxBlur.GetFloat(),
r_dofAmount.GetFloat(),
r_dofDebug.GetBool() ? 1 : 0);
idList<glRaytracingLight_t> screenParticleLights;
idList<int> pathTraceLightAreas;
const bool usePathTraceLightAreas = RB_DXRBuildPathTraceLightAreaSet(backEnd.viewDef ? backEnd.viewDef->renderWorld : NULL, pathTraceLightAreas);
for (vLight = backEnd.viewDef->viewLights; vLight; vLight = vLight->next)
{
backEnd.vLight = vLight;
// do fogging later
if (vLight->lightShader->IsFogLight())
{
continue;
}
if (vLight->lightShader->IsBlendLight())
{
continue;
}
if (!R_MaskedOcclusionCulling_LightIsVisible(backEnd.viewDef, vLight->lightDef))
{
continue;
}
if (usePathTraceLightAreas && !RB_DXRPathTraceLightTouchesAreaSet(vLight->lightDef, pathTraceLightAreas))
{
continue;
}
const renderLight_t& srcLight = vLight->lightDef->parms;
const float r = srcLight.shaderParms[SHADERPARM_RED];
const float g = srcLight.shaderParms[SHADERPARM_GREEN];
const float b = srcLight.shaderParms[SHADERPARM_BLUE];
const float intensity = 6.0f;
glRaytracingLight_t light = {};
bool supported = true;
if (srcLight.pointLight)
{
light = glRaytracingLightingMakePointLight(
vLight->globalLightOrigin.x,
vLight->globalLightOrigin.y,
vLight->globalLightOrigin.z,
srcLight.lightRadius[0] * 1.15f,
srcLight.lightRadius[1] * 1.15f,
srcLight.lightRadius[2] * 1.5f,
r, g, b,
intensity);
light.samples = srcLight.noShadows ? 0u : 1u;
light.pointRadiusPad = srcLight.noSpecular ? 1.0f : 0.0f;
light.areaNum = RB_DXRSingleReferencedLightArea(vLight->lightDef);
const idVec3 axisU = RB_DXRNormalizeVec3Safe(RB_DXRTransformLightVector(srcLight.axis, RB_DXRMakeVec3(1.0f, 0.0f, 0.0f)), RB_DXRMakeVec3(1.0f, 0.0f, 0.0f));
const idVec3 axisV = RB_DXRNormalizeVec3Safe(RB_DXRTransformLightVector(srcLight.axis, RB_DXRMakeVec3(0.0f, 1.0f, 0.0f)), RB_DXRMakeVec3(0.0f, 1.0f, 0.0f));
const idVec3 axisW = RB_DXRNormalizeVec3Safe(RB_DXRTransformLightVector(srcLight.axis, RB_DXRMakeVec3(0.0f, 0.0f, 1.0f)), RB_DXRMakeVec3(0.0f, 0.0f, 1.0f));
light.axisU.x = axisU.x;
light.axisU.y = axisU.y;
light.axisU.z = axisU.z;
light.axisV.x = axisV.x;
light.axisV.y = axisV.y;
light.axisV.z = axisV.z;
light.normal.x = axisW.x;
light.normal.y = axisW.y;
light.normal.z = axisW.z;
}
else if (!srcLight.parallel)
{
light = RB_DXRMakeSpotLightFromRenderLight(
srcLight,
vLight->globalLightOrigin,
r, g, b,
intensity);
light.areaNum = RB_DXRSingleReferencedLightArea(vLight->lightDef);
}
else
{
// Parallel projected lights are not spot lights.
// Handle them in a separate directional/orthographic path.
supported = false;
common->Warning("Parallel lights are not needed with raytracing! Just place a skybox.");
}
if (supported) {
if (vLight->iesImage) {
vLight->iesImage->Bind();
if (vLight->iesImage->texnum != idImage::TEXTURE_NOT_LOADED) {
light.iesTextureId = vLight->iesImage->texnum;
light.iesStrength = 1.0f;
}
}
glRaytracingLightingAddLight(&light);
screenParticleLights.Append(light);
}
}
if (backEnd.viewDef->renderWorld)
{
backEnd.viewDef->renderWorld->SubmitWorldDecalsToScreenSpace();
glSetScreenSpaceParticleLightsQD3D12(screenParticleLights.Ptr(), screenParticleLights.Num());
glRaytracingLightingSetGIAmount(r_pathTraceGIAmount.GetFloat());
glRaytracingLightingSetWorldGrime(r_pathTraceWorldGrime.GetFloat());
glRaytracingLightingSetSecondaryLightBudget(
r_pathTraceSecondaryLightBudget.GetInteger(),
r_pathTraceSecondaryLightCompensation.GetFloat());
glLightScene(backEnd.viewDef->renderWorld->dxrWorldId);
RB_DXRReportPathTraceProfile();
glRaytracingLightingClearLights(false);
}
}