From 345e6bfc81a8df2e18b69ac8d0f8459e89e6709f Mon Sep 17 00:00:00 2001 From: Justin Marshall Date: Thu, 21 May 2026 18:58:43 -0700 Subject: [PATCH] Fixed MD5 models. GI now bounces off textures properly. --- neo/doom3/game/gamesys/GameTypeInfo.h | 1 + neo/engine/doomdll.vcxproj.user | 4 +- neo/engine/framework/Console.cpp | 4 +- neo/engine/models/Model.cpp | 29 ++- neo/engine/models/Model_md5.cpp | 6 +- neo/engine/opengl/gl_d3d12raylight.cpp | 270 ++++++++++++++++++++----- neo/engine/opengl/gl_d3d12shim.cpp | 71 +++++-- neo/engine/opengl/opengl.h | 4 + neo/engine/renderer/Image.h | 4 +- neo/engine/renderer/Image_load.cpp | 61 ++++++ 10 files changed, 384 insertions(+), 70 deletions(-) diff --git a/neo/doom3/game/gamesys/GameTypeInfo.h b/neo/doom3/game/gamesys/GameTypeInfo.h index cba585e5..7b0760ea 100644 --- a/neo/doom3/game/gamesys/GameTypeInfo.h +++ b/neo/doom3/game/gamesys/GameTypeInfo.h @@ -4015,6 +4015,7 @@ static classVariableInfo_t glRaytracingMeshDesc_t_typeInfo[] = { { "uint32_t", "indexCount", (intptr_t)(&((glRaytracingMeshDesc_t *)0)->indexCount), sizeof( ((glRaytracingMeshDesc_t *)0)->indexCount ) }, { "int", "allowUpdate", (intptr_t)(&((glRaytracingMeshDesc_t *)0)->allowUpdate), sizeof( ((glRaytracingMeshDesc_t *)0)->allowUpdate ) }, { "int", "opaque", (intptr_t)(&((glRaytracingMeshDesc_t *)0)->opaque), sizeof( ((glRaytracingMeshDesc_t *)0)->opaque ) }, + { "float[4]", "averageColor", (intptr_t)(&((glRaytracingMeshDesc_t *)0)->averageColor), sizeof( ((glRaytracingMeshDesc_t *)0)->averageColor ) }, { NULL, 0 } }; diff --git a/neo/engine/doomdll.vcxproj.user b/neo/engine/doomdll.vcxproj.user index b9fcfa12..f834c0ec 100644 --- a/neo/engine/doomdll.vcxproj.user +++ b/neo/engine/doomdll.vcxproj.user @@ -15,9 +15,9 @@ D:\projects\Doom3\Doom3.exe WindowsLocalDebugger - +set r_fullscreen 0 +set r_mode 9 +set sv_pure 0 + +set r_fullscreen 0 +set r_mode 11 +set sv_pure 0 D:\projects\Doom3 - +set r_fullscreen 0 +set r_mode 9|+set r_fullscreen 1 +set r_mode 9 +set sv_pure 0 +set fs_game e3|+set r_fullscreen 0 +set r_mode 9 +set sv_pure 0|+set r_fullscreen 0 +set r_mode 9 +set sv_pure 0 +set fs_game e3|+set r_fullscreen 0 +set r_mode 9 +set sv_pure 0 | + +set r_fullscreen 1 +set r_mode 9 +set sv_pure 0 +set fs_game e3|+set r_fullscreen 0 +set r_mode 9 +set sv_pure 0|+set r_fullscreen 0 +set r_mode 9 +set sv_pure 0 +set fs_game e3|+set r_fullscreen 0 +set r_mode 9 +set sv_pure 0 |+set r_fullscreen 0 +set r_mode 11 +set sv_pure 0 | D:\projects\Doom3\Quake4.exe diff --git a/neo/engine/framework/Console.cpp b/neo/engine/framework/Console.cpp index 2e698299..c14dd8b4 100644 --- a/neo/engine/framework/Console.cpp +++ b/neo/engine/framework/Console.cpp @@ -31,6 +31,7 @@ If you have questions concerning this license or the applicable additional terms void SCR_DrawTextLeftAlign( float &y, const char *text, ... ) id_attribute((format(printf,2,3))); void SCR_DrawTextRightAlign( float &y, const char *text, ... ) id_attribute((format(printf,2,3))); +extern int QD3D12_GetActiveFrameGenerationMultiplier( void ); #define LINE_WIDTH 78 #define NUM_CON_TIMES 4 @@ -219,7 +220,8 @@ float SCR_DrawFPS( float y ) { fps = (fps + 5)/10; const int averageFrameTime = ( total + ( FPS_FRAMES / 2 ) ) / FPS_FRAMES; - const int frameGenMultiplier = idMath::ClampInt( 1, 4, cvarSystem->GetCVarInteger( "r_frameGen" ) ); + const int requestedFrameGenMultiplier = idMath::ClampInt( 1, 4, cvarSystem->GetCVarInteger( "r_frameGen" ) ); + const int frameGenMultiplier = ( requestedFrameGenMultiplier >= 2 ) ? idMath::ClampInt( 1, 4, QD3D12_GetActiveFrameGenerationMultiplier() ) : 1; const int displayFps = fps * frameGenMultiplier; const int displayFrameTime = ( averageFrameTime + ( frameGenMultiplier / 2 ) ) / frameGenMultiplier; const idVec4 &frameColor = SCR_FrameTimeColor( displayFrameTime ); diff --git a/neo/engine/models/Model.cpp b/neo/engine/models/Model.cpp index 6ec24cad..019e3822 100644 --- a/neo/engine/models/Model.cpp +++ b/neo/engine/models/Model.cpp @@ -568,10 +568,12 @@ void idRenderModelStatic::UpdateDXR(uint32_t& dxrBottomAcel, int onlySurface) continue; modelSurface_t* surf = &surfaces[i]; + if (!surf->geometry || surf->geometry->numVerts <= 0 || surf->geometry->numIndexes <= 0) + continue; if (onlySurface == -1) { - if (!surf->shader->IsLitMaterial()) { + if (!surf->shader || !surf->shader->IsLitMaterial()) { continue; } } @@ -591,14 +593,21 @@ void idRenderModelStatic::UpdateDXR(uint32_t& dxrBottomAcel, int onlySurface) continue; modelSurface_t* surf = &surfaces[i]; + if (!surf->geometry || surf->geometry->numVerts <= 0 || surf->geometry->numIndexes <= 0) + continue; if (onlySurface == -1) { - if (!surf->shader->IsLitMaterial()) { + if (!surf->shader || !surf->shader->IsLitMaterial()) { continue; } } + if (!surf->geometry->tangentsCalculated) + { + R_DeriveTangents(surf->geometry); + } + for (int d = 0; d < surf->geometry->numIndexes; ++d) { indices[indexId++] = vertexId + surf->geometry->indexes[d]; @@ -606,9 +615,15 @@ void idRenderModelStatic::UpdateDXR(uint32_t& dxrBottomAcel, int onlySurface) for (int d = 0; d < surf->geometry->numVerts; ++d) { - vertices[vertexId].xyz[0] = surf->geometry->verts[d].xyz.x; - vertices[vertexId].xyz[1] = surf->geometry->verts[d].xyz.y; - vertices[vertexId].xyz[2] = surf->geometry->verts[d].xyz.z; + const idDrawVert& src = surf->geometry->verts[d]; + vertices[vertexId].xyz[0] = src.xyz.x; + vertices[vertexId].xyz[1] = src.xyz.y; + vertices[vertexId].xyz[2] = src.xyz.z; + vertices[vertexId].normal[0] = src.normal.x; + vertices[vertexId].normal[1] = src.normal.y; + vertices[vertexId].normal[2] = src.normal.z; + vertices[vertexId].st[0] = src.st.x; + vertices[vertexId].st[1] = src.st.y; vertexId++; } } @@ -625,6 +640,10 @@ void idRenderModelStatic::UpdateDXR(uint32_t& dxrBottomAcel, int onlySurface) desc.indexCount = (uint32_t)numDXRIndexes; desc.allowUpdate = 1; desc.opaque = 1; + desc.averageColor[0] = 1.0f; + desc.averageColor[1] = 1.0f; + desc.averageColor[2] = 1.0f; + desc.averageColor[3] = 1.0f; if (!dxrBottomAcel) { diff --git a/neo/engine/models/Model_md5.cpp b/neo/engine/models/Model_md5.cpp index 00731638..5ffb4f7d 100644 --- a/neo/engine/models/Model_md5.cpp +++ b/neo/engine/models/Model_md5.cpp @@ -262,9 +262,9 @@ Special transform to make the mesh seem fat or skinny. May be used for zombie d ==================== */ void idMD5Mesh::TransformScaledVerts( idDrawVert *verts, const idJointMat *entJoints, float scale ) { - idVec4 *scaledWeights = (idVec4 *) _alloca16( numWeights * sizeof( scaledWeights[0] ) ); - SIMDProcessor->Mul( scaledWeights[0].ToFloatPtr(), scale, scaledWeights[0].ToFloatPtr(), numWeights * 4 ); - SIMDProcessor->TransformVerts( verts, texCoords.Num(), entJoints, scaledWeights, weightIndex, numWeights ); + idVec4 *scaledWeightsLocal = (idVec4 *) _alloca16( numWeights * sizeof( scaledWeightsLocal[0] ) ); + SIMDProcessor->Mul( scaledWeightsLocal[0].ToFloatPtr(), scale, scaledWeights[0].ToFloatPtr(), numWeights * 4 ); + SIMDProcessor->TransformVerts( verts, texCoords.Num(), entJoints, scaledWeightsLocal, weightIndex, numWeights ); } /* diff --git a/neo/engine/opengl/gl_d3d12raylight.cpp b/neo/engine/opengl/gl_d3d12raylight.cpp index 703ac935..3ebfac2c 100644 --- a/neo/engine/opengl/gl_d3d12raylight.cpp +++ b/neo/engine/opengl/gl_d3d12raylight.cpp @@ -796,6 +796,7 @@ struct glRaytracingMeshRecord_t UINT64 blasBuildFenceValue; int currentBlasIndex; uint32_t materialFlags; + float averageColor[4]; glRaytracingMeshRecord_t() { @@ -809,6 +810,7 @@ struct glRaytracingMeshRecord_t blasBuildFenceValue = 0; currentBlasIndex = 0; materialFlags = 0; + averageColor[0] = averageColor[1] = averageColor[2] = averageColor[3] = 1.0f; } }; @@ -1055,6 +1057,27 @@ static void glRaytracingMarkAllWorldsNeedRebuild(void) glRaytracingLightingResetDenoiseHistory(); } +static void glRaytracingCopyMeshAverageColor(float outAverageColor[4], const glRaytracingMeshDesc_t* desc) +{ + if (!outAverageColor) + return; + + outAverageColor[0] = outAverageColor[1] = outAverageColor[2] = outAverageColor[3] = 1.0f; + if (!desc) + return; + + const bool hasExplicitAverage = + desc->averageColor[0] != 0.0f || + desc->averageColor[1] != 0.0f || + desc->averageColor[2] != 0.0f || + desc->averageColor[3] != 0.0f; + if (!hasExplicitAverage) + return; + + for (int i = 0; i < 4; ++i) + outAverageColor[i] = glRaytracingClamp(desc->averageColor[i], 0.0f, 1.0f); +} + static uint32_t glRaytracingCountAliveInstances(const glRaytracingRenderWorld_t* world) { if (!world) @@ -1330,19 +1353,20 @@ static int glRaytracingEnsureMeshResultBuffers(glRaytracingMeshRecord_t* mesh, U static inline void glRaytracingBuildInstanceDesc( D3D12_RAYTRACING_INSTANCE_DESC* outDesc, const glRaytracingInstanceRecord_t& inst, + uint32_t meshHandle, uint32_t meshMaterialFlags, D3D12_GPU_VIRTUAL_ADDRESS blasGpuVA) { memcpy(outDesc->Transform, inst.descCpu.transform, sizeof(float) * 12); - // InstanceID is 24-bit in D3D12_RAYTRACING_INSTANCE_DESC. Preserve the - // caller's lower 16 bits, then pack material flags into bits 16-23 so the + // InstanceID is 24-bit in D3D12_RAYTRACING_INSTANCE_DESC. The lower 16 bits + // carry the mesh metadata index, and bits 16-23 carry material flags so the // any-hit shader can decide whether visibility rays pass through the hit. // // Accept both sources: // - meshMaterialFlags set by glRaytracingSetMeshMaterialFlags()/shim mesh tags // - already-encoded high InstanceID bits for direct low-level callers - const uint32_t userInstanceId = inst.descCpu.instanceID & GL_RAYTRACING_INSTANCE_USER_ID_MASK; + const uint32_t userInstanceId = meshHandle & GL_RAYTRACING_INSTANCE_USER_ID_MASK; const uint32_t instanceMaterialFlags = (inst.descCpu.instanceID >> GL_RAYTRACING_INSTANCE_MATERIAL_SHIFT) & GL_RAYTRACING_INSTANCE_MATERIAL_MASK; const uint32_t combinedMaterialFlags = @@ -1382,7 +1406,7 @@ static int glRaytracingResolveInstanceDesc( return 0; if (outDesc) - glRaytracingBuildInstanceDesc(outDesc, *inst, mesh->materialFlags, blas->gpuVA); + glRaytracingBuildInstanceDesc(outDesc, *inst, mesh->handle, mesh->materialFlags, blas->gpuVA); if (outBlasGpuVA) *outBlasGpuVA = blas->gpuVA; return 1; @@ -2015,6 +2039,7 @@ glRaytracingMeshHandle_t glRaytracingCreateMesh(const glRaytracingMeshDesc_t* de mesh.indicesCpu.assign(desc->indices, desc->indices + desc->indexCount); mesh.descCpu.vertices = nullptr; mesh.descCpu.indices = nullptr; + glRaytracingCopyMeshAverageColor(mesh.averageColor, desc); mesh.dirty = 1; g_glRaytracingScene.meshes.push_back(mesh); @@ -2052,9 +2077,19 @@ int glRaytracingUpdateMesh(glRaytracingMeshHandle_t meshHandle, const glRaytraci mesh->descCpu.indexCount == desc->indexCount && mesh->descCpu.allowUpdate == desc->allowUpdate && mesh->descCpu.opaque == desc->opaque; + float descAverageColor[4]; + glRaytracingCopyMeshAverageColor(descAverageColor, desc); + const bool sameAverageColor = memcmp(mesh->averageColor, descAverageColor, sizeof(mesh->averageColor)) == 0; if (sameDesc && sameVertexData && sameIndexData) + { + if (!sameAverageColor) + { + memcpy(mesh->averageColor, descAverageColor, sizeof(mesh->averageColor)); + glRaytracingLightingResetDenoiseHistory(); + } return 1; + } const bool canUpdateExistingBuffers = desc->allowUpdate != 0 && @@ -2073,6 +2108,7 @@ int glRaytracingUpdateMesh(glRaytracingMeshHandle_t meshHandle, const glRaytraci mesh->indicesCpu.assign(desc->indices, desc->indices + desc->indexCount); mesh->descCpu.vertices = nullptr; mesh->descCpu.indices = nullptr; + memcpy(mesh->averageColor, descAverageColor, sizeof(mesh->averageColor)); if (canUpdateExistingBuffers) { @@ -2152,6 +2188,29 @@ void glRaytracingSetMeshGlass(glRaytracingMeshHandle_t meshHandle, int isGlass) glRaytracingSetMeshMaterialFlags(meshHandle, flags); } +void glRaytracingSetMeshAverageColor(glRaytracingMeshHandle_t meshHandle, float r, float g, float b, float a) +{ + std::lock_guard lock(g_glRaytracingMutex); + + glRaytracingMeshRecord_t* mesh = glRaytracingFindMesh(meshHandle); + if (!mesh) + return; + + const float averageColor[4] = + { + glRaytracingClamp(r, 0.0f, 1.0f), + glRaytracingClamp(g, 0.0f, 1.0f), + glRaytracingClamp(b, 0.0f, 1.0f), + glRaytracingClamp(a, 0.0f, 1.0f) + }; + + if (memcmp(mesh->averageColor, averageColor, sizeof(averageColor)) == 0) + return; + + memcpy(mesh->averageColor, averageColor, sizeof(mesh->averageColor)); + glRaytracingLightingResetDenoiseHistory(); +} + void glRaytracingDeleteMesh(glRaytracingMeshHandle_t meshHandle) { std::lock_guard lock(g_glRaytracingMutex); @@ -2404,6 +2463,11 @@ struct glRaytracingLightingConstants_t float bumpStrength; }; +struct glRaytracingMeshMetadata_t +{ + float averageColor[4]; +}; + struct glRaytracingLightingState_t { std::vector cpuLights; @@ -2415,12 +2479,16 @@ struct glRaytracingLightingState_t glRaytracingBuffer_t constantBuffer; glRaytracingBuffer_t lightBuffer; + glRaytracingBuffer_t meshMetadataBuffer; void* constantBufferMapped; void* lightBufferMapped; + void* meshMetadataBufferMapped; glRaytracingBuffer_t constantBufferRing[GL_RAYTRACING_CMD_RING_SIZE]; glRaytracingBuffer_t lightBufferRing[GL_RAYTRACING_CMD_RING_SIZE]; + glRaytracingBuffer_t meshMetadataBufferRing[GL_RAYTRACING_CMD_RING_SIZE]; void* constantBufferMappedRing[GL_RAYTRACING_CMD_RING_SIZE]; void* lightBufferMappedRing[GL_RAYTRACING_CMD_RING_SIZE]; + void* meshMetadataBufferMappedRing[GL_RAYTRACING_CMD_RING_SIZE]; ComPtr globalRootSig; ComPtr localRootSig; @@ -2462,10 +2530,12 @@ struct glRaytracingLightingState_t descriptorStride = 0; constantBufferMapped = nullptr; lightBufferMapped = nullptr; + meshMetadataBufferMapped = nullptr; for (UINT frame = 0; frame < GL_RAYTRACING_CMD_RING_SIZE; ++frame) { constantBufferMappedRing[frame] = nullptr; lightBufferMappedRing[frame] = nullptr; + meshMetadataBufferMappedRing[frame] = nullptr; for (int i = 0; i < 3; ++i) denoiseConstantBufferMappedRing[frame][i] = nullptr; } @@ -2506,14 +2576,15 @@ enum glRaytracingLightingDescriptorIndex_t GLR_DESC_TEMPORAL_SRV = 10, GLR_DESC_EMISSIVE_SRV = 11, 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_MESH_METADATA_SRV = 13, + GLR_DESC_PATHTRACE_UAV = 14, + GLR_DESC_DENOISE_A_UAV = 15, + GLR_DESC_DENOISE_B_UAV = 16, + GLR_DESC_OUTPUT_UAV = 17, + GLR_DESC_TEMPORAL_UAV = 18, + GLR_DESC_HISTORY_UAV = 19, + GLR_DESC_COUNT = 20, + GLR_DESC_SRV_COUNT = 14, GLR_DESC_UAV_COUNT = 6 }; @@ -2562,6 +2633,11 @@ struct Light float pointRadiusPad; // non-zero disables specular for this light }; +struct MeshMetadata +{ + float4 averageColor; +}; + struct ShadowPayload { uint hit; @@ -2572,7 +2648,7 @@ struct BouncePayload uint hit; float hitT; uint materialFlags; - uint pad0; + uint meshIndex; }; cbuffer LightingCB : register(b0) @@ -2609,6 +2685,7 @@ Texture2D gPositionTex : register(t4); RaytracingAccelerationStructure gSceneBVH : register(t5); Texture2D gEmissiveTex : register(t11); Texture2D gSpecularTex : register(t12); +StructuredBuffer gMeshMetadata : register(t13); RWTexture2D gOutputTex : register(u0); static const uint GL_RAYTRACING_LIGHT_TYPE_POINT = 0; @@ -2639,6 +2716,12 @@ bool CurrentRayHitIsGlass() return (DecodeInstanceMaterialFlags() & GL_RAYTRACING_MATERIAL_FLAG_GLASS) != 0u; } +float3 GetMeshAverageColor(uint meshIndex) +{ + float3 c = gMeshMetadata[meshIndex].averageColor.rgb; + return max(saturate(c), float3(0.04, 0.04, 0.04)); +} + uint DecodeGeometryFlag(float geoFlag) { // position.w comes from a render target / buffer path, so do not require exact @@ -2689,10 +2772,16 @@ float BumpLuminance(float3 c) return dot(c, float3(0.299, 0.587, 0.114)); } -float3 EnhanceBumpNormal(uint2 pixel, float3 worldPos, float3 baseAlbedo) +float3 EnhanceBumpNormal(uint2 pixel, float3 worldPos, float3 baseAlbedo, bool isSkeletal) { float3 N = SafeNormalizeLocal(LoadSceneNormal(pixel).xyz, float3(0.0, 0.0, 1.0)); + // Character albedo has skin, cloth, and painted detail that should not be + // interpreted as height. The synthetic bump pass looks good on old walls, + // but it makes animated models crawl and self-shadow like noisy relief maps. + if (isSkeletal) + return N; + float strength = max(gBumpStrength, 0.0); if (strength <= 0.001) return N; @@ -2796,7 +2885,7 @@ void BounceMiss(inout BouncePayload payload) payload.hit = 0; payload.hitT = 0.0; payload.materialFlags = 0; - payload.pad0 = 0; + payload.meshIndex = 0; } [shader("anyhit")] @@ -2822,14 +2911,14 @@ void BounceClosestHit(inout BouncePayload payload, in BuiltInTriangleIntersectio payload.hit = 0; payload.hitT = 0.0; payload.materialFlags = DecodeInstanceMaterialFlags(); - payload.pad0 = 0; + payload.meshIndex = InstanceID() & GL_RAYTRACING_INSTANCE_USER_ID_MASK; return; } payload.hit = 1; payload.hitT = RayTCurrent(); payload.materialFlags = DecodeInstanceMaterialFlags(); - payload.pad0 = 0; + payload.meshIndex = InstanceID() & GL_RAYTRACING_INSTANCE_USER_ID_MASK; } [shader("closesthit")] @@ -2841,7 +2930,7 @@ void ReflectionClosestHit(inout BouncePayload payload, in BuiltInTriangleInterse payload.hit = 1; payload.hitT = RayTCurrent(); payload.materialFlags = DecodeInstanceMaterialFlags(); - payload.pad0 = 0; + payload.meshIndex = InstanceID() & GL_RAYTRACING_INSTANCE_USER_ID_MASK; } float TraceShadow(float3 origin, float3 dir, float maxT) @@ -2868,7 +2957,7 @@ 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) +bool TraceBounce(float3 origin, float3 dir, float maxT, out float hitT, out uint materialFlags, out uint meshIndex) { RayDesc ray; ray.Origin = origin; @@ -2880,7 +2969,7 @@ bool TraceBounce(float3 origin, float3 dir, float maxT, out float hitT, out uint payload.hit = 0; payload.hitT = 0.0; payload.materialFlags = 0; - payload.pad0 = 0; + payload.meshIndex = 0; TraceRay( gSceneBVH, @@ -2894,10 +2983,11 @@ bool TraceBounce(float3 origin, float3 dir, float maxT, out float hitT, out uint hitT = payload.hitT; materialFlags = payload.materialFlags; + meshIndex = payload.meshIndex; return payload.hit != 0; } -bool TraceSpecularReflection(float3 origin, float3 dir, float maxT, out float hitT, out uint materialFlags) +bool TraceSpecularReflection(float3 origin, float3 dir, float maxT, out float hitT, out uint materialFlags, out uint meshIndex) { RayDesc ray; ray.Origin = origin; @@ -2909,7 +2999,7 @@ bool TraceSpecularReflection(float3 origin, float3 dir, float maxT, out float hi payload.hit = 0; payload.hitT = 0.0; payload.materialFlags = 0; - payload.pad0 = 0; + payload.meshIndex = 0; TraceRay( gSceneBVH, @@ -2923,6 +3013,7 @@ bool TraceSpecularReflection(float3 origin, float3 dir, float maxT, out float hi hitT = payload.hitT; materialFlags = payload.materialFlags; + meshIndex = payload.meshIndex; return payload.hit != 0; } @@ -3252,10 +3343,10 @@ float RectLightShadow(float3 worldPos, float3 N, Light Lgt, uint2 pixel) return visibility / (float)sampleCount; } -float ComputeAmbientOcclusion(float3 worldPos, float3 N, uint2 pixel) +float ComputeAmbientOcclusion(float3 worldPos, float3 N, uint2 pixel, bool isSkeletal) { const uint AO_SAMPLES = 8; - const float AO_RADIUS = 32.0; + float aoRadius = isSkeletal ? 18.0 : 32.0; float3 tangent, bitangent; BuildOrthonormalBasis(N, tangent, bitangent); @@ -3277,14 +3368,17 @@ float ComputeAmbientOcclusion(float3 worldPos, float3 N, uint2 pixel) aoDir = normalize(aoDir); - float3 aoOrigin = worldPos + N * (gShadowBias * 0.15); + float3 aoOrigin = worldPos + N * (gShadowBias * (isSkeletal ? 0.75 : 0.15)); - visibility += TraceShadow(aoOrigin, aoDir, AO_RADIUS); + visibility += TraceShadow(aoOrigin, aoDir, aoRadius); } visibility /= (float)AO_SAMPLES; visibility = saturate(pow(visibility, 1.5)); + if (isSkeletal) + visibility = lerp(visibility, 1.0, 0.45); + return visibility; } @@ -4562,7 +4656,8 @@ float3 TraceOneIndirectBouncePath(uint2 pixel, float3 worldPos, float3 N, float3 float hitT = 0.0; uint materialFlags = 0; - bool hit = TraceBounce(bounceOrigin, bounceDir, BOUNCE_TMAX, hitT, materialFlags); + uint meshIndex = 0; + bool hit = TraceBounce(bounceOrigin, bounceDir, BOUNCE_TMAX, hitT, materialFlags, meshIndex); if (!hit) { @@ -4580,6 +4675,7 @@ float3 TraceOneIndirectBouncePath(uint2 pixel, float3 worldPos, float3 N, float3 float3 hitNormal = SafeNormalizeOr(-bounceDir, pathNormal); float3 hitAlbedo = float3(0.55, 0.55, 0.55); uint hitGeoFlag = GEOMETRY_FLAG_NONE; + float3 meshAverageColor = GetMeshAverageColor(meshIndex); bool hasGBufferMaterial = TryFetchGBufferAtRayHit( rayHitPos, @@ -4614,6 +4710,7 @@ float3 TraceOneIndirectBouncePath(uint2 pixel, float3 worldPos, float3 N, float3 bouncedRadiance += skyTint * 0.045; } + bouncedRadiance *= meshAverageColor * 1.12; bouncedRadiance = CompressEmissiveRadiance(bouncedRadiance, depth == 0u ? 8.0 : 5.0); accum += throughput * bouncedRadiance; @@ -4624,7 +4721,7 @@ float3 TraceOneIndirectBouncePath(uint2 pixel, float3 worldPos, float3 N, float3 // the remaining throughput is just the secondary surface albedo. Use a // conservative energy scale because material data for off-screen hits is a // G-buffer approximation. - throughput *= saturate(hitAlbedo) * 0.72; + throughput *= saturate(hitAlbedo) * 0.84; float continuation = clamp(max(max(throughput.x, throughput.y), throughput.z), 0.12, 0.92); if (depth >= 1u) @@ -4661,6 +4758,12 @@ float3 EstimatePathTracedIndirectBounce(uint2 pixel, float3 worldPos, float3 N, float3 ApplyPrimaryDiffusePost(float3 lightingAccum, float ao, float microShadow, bool isSkeletal) { + if (isSkeletal) + { + ao = lerp(ao, 1.0, 0.35); + microShadow = lerp(microShadow, 1.0, 0.65); + } + lightingAccum *= ao; lightingAccum *= microShadow; @@ -4676,6 +4779,8 @@ float3 ApplyPrimarySpecularPost(float3 specularAccum, float ao, bool isSkeletal) // directly made highlights disappear on creases, props, and normal-mapped // surfaces. Keep a mild occlusion tint, but let direct-light specular read. float specAo = lerp(0.58, 1.0, saturate(ao)); + if (isSkeletal) + specAo = lerp(specAo, 1.0, 0.50); specularAccum *= specAo; //if (isSkeletal) @@ -4927,7 +5032,8 @@ float3 EstimateRayTracedSpecularReflection( float hitT = 0.0; uint materialFlags = 0u; - bool hit = TraceSpecularReflection(reflectionOrigin, R, reflectionMaxT, hitT, materialFlags); + uint meshIndex = 0u; + bool hit = TraceSpecularReflection(reflectionOrigin, R, reflectionMaxT, hitT, materialFlags, meshIndex); float3 reflectedRadiance = 0.0; float distanceFade = 1.0; @@ -4989,6 +5095,7 @@ float3 EstimateRayTracedSpecularReflection( } reflectedRadiance = CompressEmissiveRadiance(reflectedRadiance, hasSpecularMap ? 5.0 : 3.5); + reflectedRadiance *= hit ? GetMeshAverageColor(meshIndex) : 1.0; float cavityFade = lerp(0.42, 1.0, saturate(cavity)); float reflectionStrength = hasSpecularMap @@ -5055,13 +5162,12 @@ void RayGen() float3 specularAlbedo = LoadSceneSpecularAlbedo(pixel, baseAlbedo); float4 positionSample = gPositionTex.Load(int3(pixel, 0)); float3 worldPos = positionSample.xyz; - float4 normalSample = LoadSceneNormal(pixel); - float3 N = EnhanceBumpNormal(pixel, worldPos, baseAlbedo); - float3 V = normalize(gCameraPos.xyz - worldPos); - uint geoFlag = DecodeGeometryFlag(positionSample.w); bool isSkeletal = (geoFlag & GEOMETRY_FLAG_SKELETAL) != 0u; bool isUnlit = (geoFlag & GEOMETRY_FLAG_UNLIT) != 0u; + float4 normalSample = LoadSceneNormal(pixel); + float3 N = EnhanceBumpNormal(pixel, worldPos, baseAlbedo, isSkeletal); + float3 V = normalize(gCameraPos.xyz - worldPos); if (isUnlit) { @@ -5075,7 +5181,9 @@ void RayGen() // All four of these are deterministic for this pixel. Compute them once, // then reuse them for every stochastic GI sample. float cavity = ComputeCavity(pixel, worldPos, N); - float ao = ComputeAmbientOcclusion(worldPos, N, pixel); + if (isSkeletal) + cavity = lerp(cavity, 1.0, 0.65); + float ao = ComputeAmbientOcclusion(worldPos, N, pixel, isSkeletal); float skyVis = 0; //ComputeSkyVisibility(worldPos, N, pixel); float ambientSkyVis = 0; //TraceStraightUpToSky(worldPos, N); float microShadow = lerp(0.75, 1.0, cavity); @@ -5160,7 +5268,8 @@ void RayGen() finalColor += baseAlbedo * reactiveFinalGather; } - finalColor *= clamp(ao + 0.6, 0.0, 1.0); + float outputAo = isSkeletal ? lerp(ao, 1.0, 0.55) : ao; + finalColor *= clamp(outputAo + 0.6, 0.0, 1.0); // Volumetric light scattering is radiance in the camera ray, not surface // reflectance, so add it after surface albedo/specular composition. Because @@ -5806,8 +5915,10 @@ static void glRaytracingLightingSelectFrameResources(UINT frameSlot) g_glRaytracingLighting.descriptorHeap = g_glRaytracingLighting.descriptorHeapRing[frameSlot]; g_glRaytracingLighting.constantBuffer = g_glRaytracingLighting.constantBufferRing[frameSlot]; g_glRaytracingLighting.lightBuffer = g_glRaytracingLighting.lightBufferRing[frameSlot]; + g_glRaytracingLighting.meshMetadataBuffer = g_glRaytracingLighting.meshMetadataBufferRing[frameSlot]; g_glRaytracingLighting.constantBufferMapped = g_glRaytracingLighting.constantBufferMappedRing[frameSlot]; g_glRaytracingLighting.lightBufferMapped = g_glRaytracingLighting.lightBufferMappedRing[frameSlot]; + g_glRaytracingLighting.meshMetadataBufferMapped = g_glRaytracingLighting.meshMetadataBufferMappedRing[frameSlot]; for (int i = 0; i < 3; ++i) { @@ -5836,8 +5947,16 @@ static int glRaytracingLightingCreateBuffers(void) D3D12_RESOURCE_STATE_GENERIC_READ, D3D12_RESOURCE_FLAG_NONE); + g_glRaytracingLighting.meshMetadataBufferRing[frame] = glRaytracingCreateBuffer( + g_glRaytracingCmd.device.Get(), + sizeof(glRaytracingMeshMetadata_t) * 65536ull, + D3D12_HEAP_TYPE_UPLOAD, + D3D12_RESOURCE_STATE_GENERIC_READ, + D3D12_RESOURCE_FLAG_NONE); + if (!g_glRaytracingLighting.constantBufferRing[frame].resource || - !g_glRaytracingLighting.lightBufferRing[frame].resource) + !g_glRaytracingLighting.lightBufferRing[frame].resource || + !g_glRaytracingLighting.meshMetadataBufferRing[frame].resource) { return 0; } @@ -5873,6 +5992,13 @@ static int glRaytracingLightingCreateBuffers(void) return 0; } + if (!glRaytracingMapUploadBufferPersistent( + g_glRaytracingLighting.meshMetadataBufferRing[frame], + &g_glRaytracingLighting.meshMetadataBufferMappedRing[frame])) + { + return 0; + } + for (int i = 0; i < 3; ++i) { if (!glRaytracingMapUploadBufferPersistent( @@ -5955,6 +6081,41 @@ static void glRaytracingLightingUpdateLights(void) bytes); } +static void glRaytracingLightingUpdateMeshMetadata(void) +{ + if (!g_glRaytracingLighting.uploadToCurrentFrameResource) + return; + + if (!g_glRaytracingLighting.meshMetadataBuffer.resource) + return; + + glRaytracingMeshMetadata_t fallback = {}; + fallback.averageColor[0] = fallback.averageColor[1] = fallback.averageColor[2] = fallback.averageColor[3] = 1.0f; + + void* mapped = g_glRaytracingLighting.meshMetadataBufferMapped; + if (!mapped) + { + glRaytracingMapCopy( + g_glRaytracingLighting.meshMetadataBuffer.resource.Get(), + &fallback, + sizeof(fallback)); + return; + } + + glRaytracingMeshMetadata_t* metadata = (glRaytracingMeshMetadata_t*)mapped; + metadata[0] = fallback; + + for (size_t i = 0; i < g_glRaytracingScene.meshes.size(); ++i) + { + const glRaytracingMeshRecord_t& mesh = g_glRaytracingScene.meshes[i]; + if (!mesh.alive) + continue; + + const uint32_t metadataIndex = mesh.handle & GL_RAYTRACING_INSTANCE_USER_ID_MASK; + memcpy(metadata[metadataIndex].averageColor, mesh.averageColor, sizeof(metadata[metadataIndex].averageColor)); + } +} + static void glRaytracingLightingUnmapUploadBuffers(void) { for (UINT frame = 0; frame < GL_RAYTRACING_CMD_RING_SIZE; ++frame) @@ -5973,6 +6134,13 @@ static void glRaytracingLightingUnmapUploadBuffers(void) g_glRaytracingLighting.lightBufferMappedRing[frame] = nullptr; } + if (g_glRaytracingLighting.meshMetadataBufferRing[frame].resource && + g_glRaytracingLighting.meshMetadataBufferMappedRing[frame]) + { + g_glRaytracingLighting.meshMetadataBufferRing[frame].resource->Unmap(0, nullptr); + g_glRaytracingLighting.meshMetadataBufferMappedRing[frame] = nullptr; + } + for (int i = 0; i < 3; ++i) { if (g_glRaytracingLighting.denoiseConstantBufferRing[frame][i].resource && @@ -5986,35 +6154,44 @@ static void glRaytracingLightingUnmapUploadBuffers(void) g_glRaytracingLighting.constantBufferMapped = nullptr; g_glRaytracingLighting.lightBufferMapped = nullptr; + g_glRaytracingLighting.meshMetadataBufferMapped = nullptr; for (int i = 0; i < 3; ++i) g_glRaytracingLighting.denoiseConstantBufferMapped[i] = nullptr; } static void glRaytracingLightingCreatePersistentLightSRV(void) { - D3D12_SHADER_RESOURCE_VIEW_DESC srv = {}; - srv.ViewDimension = D3D12_SRV_DIMENSION_BUFFER; - srv.Shader4ComponentMapping = D3D12_DEFAULT_SHADER_4_COMPONENT_MAPPING; - srv.Format = DXGI_FORMAT_UNKNOWN; - srv.Buffer.FirstElement = 0; - srv.Buffer.NumElements = GL_RAYTRACING_MAX_LIGHTS; - srv.Buffer.StructureByteStride = sizeof(glRaytracingLight_t); - srv.Buffer.Flags = D3D12_BUFFER_SRV_FLAG_NONE; - for (UINT frame = 0; frame < GL_RAYTRACING_CMD_RING_SIZE; ++frame) { if (!g_glRaytracingLighting.descriptorHeapRing[frame] || - !g_glRaytracingLighting.lightBufferRing[frame].resource) + !g_glRaytracingLighting.lightBufferRing[frame].resource || + !g_glRaytracingLighting.meshMetadataBufferRing[frame].resource) { continue; } D3D12_CPU_DESCRIPTOR_HANDLE base = g_glRaytracingLighting.descriptorHeapRing[frame]->GetCPUDescriptorHandleForHeapStart(); + + D3D12_SHADER_RESOURCE_VIEW_DESC srv = {}; + srv.ViewDimension = D3D12_SRV_DIMENSION_BUFFER; + srv.Shader4ComponentMapping = D3D12_DEFAULT_SHADER_4_COMPONENT_MAPPING; + srv.Format = DXGI_FORMAT_UNKNOWN; + srv.Buffer.FirstElement = 0; + srv.Buffer.NumElements = GL_RAYTRACING_MAX_LIGHTS; + srv.Buffer.StructureByteStride = sizeof(glRaytracingLight_t); + srv.Buffer.Flags = D3D12_BUFFER_SRV_FLAG_NONE; g_glRaytracingCmd.device->CreateShaderResourceView( g_glRaytracingLighting.lightBufferRing[frame].resource.Get(), &srv, glRaytracingOffsetCpu(base, g_glRaytracingLighting.descriptorStride, GLR_DESC_LIGHTS_SRV)); + + srv.Buffer.NumElements = 65536; + srv.Buffer.StructureByteStride = sizeof(glRaytracingMeshMetadata_t); + g_glRaytracingCmd.device->CreateShaderResourceView( + g_glRaytracingLighting.meshMetadataBufferRing[frame].resource.Get(), + &srv, + glRaytracingOffsetCpu(base, g_glRaytracingLighting.descriptorStride, GLR_DESC_MESH_METADATA_SRV)); } } @@ -6514,6 +6691,7 @@ static bool glRaytracingLightingExecuteInternal( g_glRaytracingLighting.uploadToCurrentFrameResource = true; glRaytracingLightingUpdateLights(); + glRaytracingLightingUpdateMeshMetadata(); glRaytracingLightingUpdateConstants(); for (uint32_t passIndex = 0; passIndex < 3u; ++passIndex) diff --git a/neo/engine/opengl/gl_d3d12shim.cpp b/neo/engine/opengl/gl_d3d12shim.cpp index a3fd56f9..02e024ae 100644 --- a/neo/engine/opengl/gl_d3d12shim.cpp +++ b/neo/engine/opengl/gl_d3d12shim.cpp @@ -287,6 +287,7 @@ void APIENTRY glTextureSpecularMap(GLuint texture, GLboolean isSpecularMap); void APIENTRY glBindSpecularMapTexture(GLuint texture); void APIENTRY glSpecularMapTexture(GLuint texture); void APIENTRY glSpecularMapStrengthf(GLfloat strength); +void APIENTRY glTextureAverageColorQD3D12(GLuint texture, GLfloat r, GLfloat g, GLfloat b, GLfloat a); void APIENTRY glRaytracingMaterialFlagsQD3D12(GLuint flags); void APIENTRY glRaytracingMaterialFlagQD3D12(GLuint flag, GLboolean enable); @@ -301,6 +302,7 @@ void glRaytracingLightingSetSpecularInput(ID3D12Resource* texture, DXGI_FORMAT f void glRaytracingSetMeshMaterialFlags(glRaytracingMeshHandle_t meshHandle, uint32_t materialFlags); void glRaytracingSetMeshGlass(glRaytracingMeshHandle_t meshHandle, int isGlass); uint32_t glRaytracingGetMeshMaterialFlags(glRaytracingMeshHandle_t meshHandle); +void glRaytracingSetMeshAverageColor(glRaytracingMeshHandle_t meshHandle, float r, float g, float b, float a); // Fast TLAS instance visibility controls are implemented in gl_raytracing.cpp. // They flip the D3D12_RAYTRACING_INSTANCE_DESC InstanceMask via the existing @@ -334,6 +336,7 @@ void QD3D12_SetToneMapBrightness(float brightness); float QD3D12_GetToneMapBrightness(void); void QD3D12_SetFrameGenerationMultiplier(int multiplier); int QD3D12_GetFrameGenerationMultiplier(void); +int QD3D12_GetActiveFrameGenerationMultiplier(void); void APIENTRY glToneMapBrightnessQD3D12(GLfloat brightness); void APIENTRY glToneMapBrightnessfQD3D12(GLfloat brightness); void APIENTRY glTonemapBrightnessQD3D12(GLfloat brightness); @@ -628,6 +631,7 @@ struct TextureResource bool isNormalMap = false; bool isGlowMap = false; bool isSpecularMap = false; + float averageColor[4] = { 1.0f, 1.0f, 1.0f, 1.0f }; ComPtr texture; D3D12_RESOURCE_STATES state = D3D12_RESOURCE_STATE_COPY_DEST; @@ -1504,7 +1508,7 @@ struct GLState bool motionHistoryReset = true; QD3D12UpscalerBackend upscalerBackend = QD3D12_UPSCALER_DLSS; - QD3D12UpscalerQuality upscalerQuality = QD3D12_QUALITY_DLAA; + QD3D12UpscalerQuality upscalerQuality = QD3D12_QUALITY_QUALITY; bool enableInternalTAA = false; bool enableRayAIDenoise = false; bool enableDLSSRayReconstruction = false; @@ -1512,7 +1516,7 @@ struct GLState uint32_t frameGenerationMultiplier = 2; uint32_t pathTracingSamplesPerPixel = 1; uint32_t pathTracingFallbackSamplesPerPixel = 2; - uint32_t pathTracingMaxBounces = 3; + uint32_t pathTracingMaxBounces = 2; float jitterX = 0.0f; float jitterY = 0.0f; @@ -6764,6 +6768,7 @@ struct QD3D12StreamlineState uint32_t lastDlssGEligibilityMaskLogged = 0xffffffffu; uint32_t lastDlssGFramesToGenerateLogged = 0xffffffffu; uint32_t lastDlssGPresentedFramesLogged = 0xffffffffu; + uint32_t dlssGActiveFrameMultiplier = 1; uint32_t dlssGConfiguredFramesToGenerate = 0xffffffffu; uint32_t dlssGConfiguredRenderWidth = 0xffffffffu; uint32_t dlssGConfiguredRenderHeight = 0xffffffffu; @@ -6941,7 +6946,7 @@ static sl::DLSSMode QD3D12_MapDLSSMode(QD3D12UpscalerQuality quality) { switch (quality) { - case QD3D12_QUALITY_QUALITY: return sl::DLSSMode::eUltraQuality; + case QD3D12_QUALITY_QUALITY: return sl::DLSSMode::eMaxQuality; case QD3D12_QUALITY_BALANCED: return sl::DLSSMode::eBalanced; case QD3D12_QUALITY_PERFORMANCE: return sl::DLSSMode::eMaxPerformance; case QD3D12_QUALITY_ULTRA_PERFORMANCE: return sl::DLSSMode::eUltraPerformance; @@ -7219,15 +7224,15 @@ static void QD3D12_LogDLSSFrameGenerationEligibility(const QD3D12Window& w, uint static void QD3D12_SetDLSSFrameGenerationOff() { - if (!g_qd3d12Sl.deviceBound || !g_qd3d12Sl.dlssGSupported || !g_qd3d12Sl.dlssGActive) - return; - - sl::DLSSGOptions options{}; - options.mode = sl::DLSSGMode::eOff; - options.flags = sl::DLSSGFlags::eRetainResourcesWhenOff; - const sl::Result result = slDLSSGSetOptions(g_qd3d12Sl.viewport, options); - if (result != sl::Result::eOk) - QD3D12_Log("slDLSSGSetOptions(off) failed (%d).", int(result)); + if (g_qd3d12Sl.deviceBound && g_qd3d12Sl.dlssGSupported && g_qd3d12Sl.dlssGActive) + { + sl::DLSSGOptions options{}; + options.mode = sl::DLSSGMode::eOff; + options.flags = sl::DLSSGFlags::eRetainResourcesWhenOff; + const sl::Result result = slDLSSGSetOptions(g_qd3d12Sl.viewport, options); + if (result != sl::Result::eOk) + QD3D12_Log("slDLSSGSetOptions(off) failed (%d).", int(result)); + } if (g_qd3d12Sl.reflexSupported) { @@ -7237,6 +7242,7 @@ static void QD3D12_SetDLSSFrameGenerationOff() } g_qd3d12Sl.dlssGActive = false; + g_qd3d12Sl.dlssGActiveFrameMultiplier = 1; g_qd3d12Sl.dlssGConfiguredFramesToGenerate = 0xffffffffu; g_qd3d12Sl.dlssGConfiguredRenderWidth = 0xffffffffu; g_qd3d12Sl.dlssGConfiguredRenderHeight = 0xffffffffu; @@ -7421,6 +7427,7 @@ static void QD3D12_ConfigureDLSSFrameGeneration(QD3D12Window& w, sl::FrameToken* } g_qd3d12Sl.dlssGActive = true; + g_qd3d12Sl.dlssGActiveFrameMultiplier = options.numFramesToGenerate + 1; } static void QD3D12_LogDLSSFrameGenerationPresentResult() @@ -7537,6 +7544,13 @@ static void QD3D12_SelectRenderResolution(QD3D12Window& w, UINT outputWidth, UIN w.renderWidth = outputWidth; w.renderHeight = outputHeight; + if (outputWidth == 0 || outputHeight == 0) + { + w.renderWidth = 1; + w.renderHeight = 1; + return; + } + if (g_gl.upscalerBackend == QD3D12_UPSCALER_NONE || g_gl.upscalerQuality == QD3D12_QUALITY_NATIVE) return; @@ -13154,6 +13168,19 @@ void APIENTRY glSpecularMapStrengthf(GLfloat strength) g_gl.currentSpecularMapStrength = (strength < 0.0f) ? 0.0f : strength; } +void APIENTRY glTextureAverageColorQD3D12(GLuint texture, GLfloat r, GLfloat g, GLfloat b, GLfloat a) +{ + if (texture == 0) + return; + + TextureResource& tex = QD3D12_EnsureTextureName(texture); + tex.averageColor[0] = ClampValue(r, 0.0f, 1.0f); + tex.averageColor[1] = ClampValue(g, 0.0f, 1.0f); + tex.averageColor[2] = ClampValue(b, 0.0f, 1.0f); + tex.averageColor[3] = ClampValue(a, 0.0f, 1.0f); + tex.cpuGeneration++; +} + void APIENTRY glRaytracingMaterialFlagsQD3D12(GLuint flags) { g_gl.currentRayMaterialFlags = QD3D12_ClampRayMaterialFlags((uint32_t)flags); @@ -17237,6 +17264,15 @@ int QD3D12_GetFrameGenerationMultiplier(void) return (int)g_gl.frameGenerationMultiplier; } +int QD3D12_GetActiveFrameGenerationMultiplier(void) +{ +#if defined(QD3D12_ENABLE_STREAMLINE) + if (g_qd3d12Sl.dlssGActive && g_qd3d12Sl.dlssGActiveFrameMultiplier >= 2) + return (int)g_qd3d12Sl.dlssGActiveFrameMultiplier; +#endif + return 1; +} + void QD3D12_EnableDLAA(int enabled) { const QD3D12UpscalerBackend oldBackend = g_gl.upscalerBackend; @@ -18818,6 +18854,11 @@ void glUpdateBottomAccelStructure(bool opaque, uint32_t& meshHandle) meshDesc.indices = &drawIndexes[0]; meshDesc.indexCount = (uint32_t)drawIndexes.size(); meshDesc.allowUpdate = 1; + meshDesc.averageColor[0] = meshDesc.averageColor[1] = meshDesc.averageColor[2] = meshDesc.averageColor[3] = 1.0f; + + TextureResource* diffuseTexture = QD3D12_FindTextureResource(g_gl.boundTexture[0]); + if (diffuseTexture) + memcpy(meshDesc.averageColor, diffuseTexture->averageColor, sizeof(meshDesc.averageColor)); const uint32_t materialFlags = QD3D12_CurrentEffectiveRayMaterialFlags(); const bool isGlass = QD3D12_RayMaterialFlagsHaveGlass(materialFlags); @@ -18836,6 +18877,12 @@ void glUpdateBottomAccelStructure(bool opaque, uint32_t& meshHandle) { g_qd3d12RaytracingMeshMaterialFlags[meshHandle] = materialFlags; glRaytracingSetMeshMaterialFlags((glRaytracingMeshHandle_t)meshHandle, materialFlags); + glRaytracingSetMeshAverageColor( + (glRaytracingMeshHandle_t)meshHandle, + meshDesc.averageColor[0], + meshDesc.averageColor[1], + meshDesc.averageColor[2], + meshDesc.averageColor[3]); } } diff --git a/neo/engine/opengl/opengl.h b/neo/engine/opengl/opengl.h index 72b6963e..1e5f6432 100644 --- a/neo/engine/opengl/opengl.h +++ b/neo/engine/opengl/opengl.h @@ -1639,6 +1639,7 @@ typedef struct glRaytracingMeshDesc_s uint32_t indexCount; int allowUpdate; int opaque; + float averageColor[4]; } glRaytracingMeshDesc_t; typedef struct glRaytracingInstanceDesc_s @@ -2227,6 +2228,7 @@ void APIENTRY glTextureSpecularMap(GLuint texture, GLboolean isSpecularMap); void APIENTRY glBindSpecularMapTexture(GLuint texture); void APIENTRY glSpecularMapTexture(GLuint texture); void APIENTRY glSpecularMapStrengthf(GLfloat strength); +void APIENTRY glTextureAverageColorQD3D12(GLuint texture, GLfloat r, GLfloat g, GLfloat b, GLfloat a); void glUpdateTopLevelAceelStructure( glRaytracingSceneHandle_t scene, @@ -2272,6 +2274,7 @@ extern "C" { uint32_t glRaytracingGetMeshMaterialFlags(glRaytracingMeshHandle_t meshHandle); void glRaytracingSetMeshMaterialFlags(glRaytracingMeshHandle_t meshHandle, uint32_t materialFlags); void glRaytracingSetMeshGlass(glRaytracingMeshHandle_t meshHandle, int isGlass); + void glRaytracingSetMeshAverageColor(glRaytracingMeshHandle_t meshHandle, float r, float g, float b, float a); // For callers that create raytracing instances directly instead of using the // shim's glUpdateTopLevelAceelStructure() helper. Glass meshes must also use @@ -2328,6 +2331,7 @@ void QD3D12_SetUpscalerSharpness(float sharpness); void QD3D12_SetToneMapBrightness(float brightness); void QD3D12_SetFrameGenerationMultiplier(int multiplier); int QD3D12_GetFrameGenerationMultiplier(void); +int QD3D12_GetActiveFrameGenerationMultiplier(void); void QD3D12_SetCurrentWindowDLSSAllowed(int allowed); void QD3D12_EnableTAA(int enabled); diff --git a/neo/engine/renderer/Image.h b/neo/engine/renderer/Image.h index a23db065..74e1475f 100644 --- a/neo/engine/renderer/Image.h +++ b/neo/engine/renderer/Image.h @@ -192,6 +192,7 @@ public: void Reload( bool checkPrecompressed, bool force ); void AddReference() { refCount++; }; + const float* GetAverageColor() const { return averageColor; }; //========================================================== @@ -250,6 +251,7 @@ public: // data for listImages int uploadWidth, uploadHeight, uploadDepth; // after power of two, downsample, and MAX_TEXTURE_SIZE int internalFormat; + float averageColor[4]; // normalized average source pixel color idImage *cacheUsagePrev, *cacheUsageNext; // for dynamic cache purging of old images @@ -286,6 +288,7 @@ ID_INLINE idImage::idImage() { bindCount = 0; uploadWidth = uploadHeight = uploadDepth = 0; internalFormat = 0; + averageColor[0] = averageColor[1] = averageColor[2] = averageColor[3] = 1.0f; cacheUsagePrev = cacheUsageNext = NULL; hashNext = NULL; isMonochrome = false; @@ -499,4 +502,3 @@ IMAGEPROGRAM void R_LoadImageProgram( const char *name, byte **pic, int *width, int *height, ID_TIME_T *timestamp, textureDepth_t *depth = NULL ); const char *R_ParsePastImageProgram( idLexer &src ); - diff --git a/neo/engine/renderer/Image_load.cpp b/neo/engine/renderer/Image_load.cpp index 6312d135..54d8a161 100644 --- a/neo/engine/renderer/Image_load.cpp +++ b/neo/engine/renderer/Image_load.cpp @@ -43,6 +43,61 @@ static bool FormatIsDXT( int internalFormat ) { return true; } +static void R_SetImageAverageColor( idImage *image, const byte *pic, int width, int height ) { + if ( !image || !pic || width <= 0 || height <= 0 ) { + return; + } + + uint64_t sum[4] = { 0, 0, 0, 0 }; + const int pixelCount = width * height; + for ( int i = 0; i < pixelCount; i++ ) { + const byte *rgba = pic + i * 4; + sum[0] += rgba[0]; + sum[1] += rgba[1]; + sum[2] += rgba[2]; + sum[3] += rgba[3]; + } + + const float scale = 1.0f / ( 255.0f * (float)pixelCount ); + image->averageColor[0] = sum[0] * scale; + image->averageColor[1] = sum[1] * scale; + image->averageColor[2] = sum[2] * scale; + image->averageColor[3] = sum[3] * scale; +} + +static void R_SetCubeImageAverageColor( idImage *image, const byte *pic[6], int size ) { + if ( !image || !pic || size <= 0 ) { + return; + } + + uint64_t sum[4] = { 0, 0, 0, 0 }; + int faceCount = 0; + const int facePixels = size * size; + for ( int face = 0; face < 6; face++ ) { + if ( !pic[face] ) { + continue; + } + faceCount++; + for ( int i = 0; i < facePixels; i++ ) { + const byte *rgba = pic[face] + i * 4; + sum[0] += rgba[0]; + sum[1] += rgba[1]; + sum[2] += rgba[2]; + sum[3] += rgba[3]; + } + } + + if ( faceCount == 0 ) { + return; + } + + const float scale = 1.0f / ( 255.0f * (float)( facePixels * faceCount ) ); + image->averageColor[0] = sum[0] * scale; + image->averageColor[1] = sum[1] * scale; + image->averageColor[2] = sum[2] * scale; + image->averageColor[3] = sum[3] * scale; +} + int MakePowerOfTwo( int num ) { int pot; for (pot = 1 ; pot < num ; pot<<=1) { @@ -463,6 +518,7 @@ void idImage::GenerateImage( const byte *pic, int width, int height, allowDownSize = allowDownSizeParm; repeat = repeatParm; depth = depthParm; + R_SetImageAverageColor( this, pic, width, height ); // if we don't have a rendering context, just return after we // have filled in the parms. We must have the values set, or @@ -494,6 +550,7 @@ void idImage::GenerateImage( const byte *pic, int width, int height, // generate the texture number glGenTextures( 1, &texnum ); + glTextureAverageColorQD3D12( texnum, averageColor[0], averageColor[1], averageColor[2], averageColor[3] ); // select proper internal format before we resample internalFormat = SelectInternalFormat( &pic, 1, width, height, depth, &isMonochrome ); @@ -670,6 +727,7 @@ void idImage::Generate3DImage( const byte *pic, int width, int height, int picDe allowDownSize = allowDownSizeParm; repeat = repeatParm; depth = minDepthParm; + R_SetImageAverageColor( this, pic, width, height * picDepth ); // if we don't have a rendering context, just return after we // have filled in the parms. We must have the values set, or @@ -691,6 +749,7 @@ void idImage::Generate3DImage( const byte *pic, int width, int height, int picDe // generate the texture number glGenTextures( 1, &texnum ); + glTextureAverageColorQD3D12( texnum, averageColor[0], averageColor[1], averageColor[2], averageColor[3] ); // select proper internal format before we resample // this function doesn't need to know it is 3D, so just make it very "tall" @@ -807,6 +866,7 @@ void idImage::GenerateCubeImage( const byte *pic[6], int size, filter = filterParm; allowDownSize = allowDownSizeParm; depth = depthParm; + R_SetCubeImageAverageColor( this, pic, size ); type = TT_CUBIC; @@ -826,6 +886,7 @@ void idImage::GenerateCubeImage( const byte *pic[6], int size, // generate the texture number glGenTextures( 1, &texnum ); + glTextureAverageColorQD3D12( texnum, averageColor[0], averageColor[1], averageColor[2], averageColor[3] ); // select proper internal format before we resample internalFormat = SelectInternalFormat( pic, 6, width, height, depth, &isMonochrome );