#include "precompiled.h" #pragma hdrstop #include "tr_local.h" #include 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& 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& 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& 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 screenParticleLights; idList 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); } }