diff --git a/neo/opengl/gl_d3d12raylight.cpp b/neo/opengl/gl_d3d12raylight.cpp index 7fb8c0c8..1580e20d 100644 --- a/neo/opengl/gl_d3d12raylight.cpp +++ b/neo/opengl/gl_d3d12raylight.cpp @@ -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 gNormalTex : register(t3); Texture2D gPositionTex : register(t4); RaytracingAccelerationStructure gSceneBVH : register(t5); Texture2D gEmissiveTex : register(t11); +Texture2D gSpecularTex : register(t12); RWTexture2D 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 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 lock(g_glRaytracingMutex); diff --git a/neo/opengl/gl_d3d12shim.cpp b/neo/opengl/gl_d3d12shim.cpp index b1e98b1a..562386dc 100644 --- a/neo/opengl/gl_d3d12shim.cpp +++ b/neo/opengl/gl_d3d12shim.cpp @@ -158,6 +158,9 @@ using Microsoft::WRL::ComPtr; #ifndef GL_GLOW_MAP_BINDING_QD3D12 #define GL_GLOW_MAP_BINDING_QD3D12 0x6005 #endif +#ifndef GL_SPECULAR_MAP_BINDING_QD3D12 +#define GL_SPECULAR_MAP_BINDING_QD3D12 0x6006 +#endif // Optional material/ray-visibility tags for the DXR path. These are private // shim enums; they deliberately live next to the normal-map compatibility enums. @@ -220,6 +223,11 @@ void APIENTRY glTextureGlowMap(GLuint texture, GLboolean isGlowMap); void APIENTRY glBindGlowMapTexture(GLuint texture); void APIENTRY glGlowMapTexture(GLuint texture); void APIENTRY glGlowMapStrengthf(GLfloat strength); +void APIENTRY glTagTextureSpecularMap(GLuint texture, GLboolean isSpecularMap); +void APIENTRY glTextureSpecularMap(GLuint texture, GLboolean isSpecularMap); +void APIENTRY glBindSpecularMapTexture(GLuint texture); +void APIENTRY glSpecularMapTexture(GLuint texture); +void APIENTRY glSpecularMapStrengthf(GLfloat strength); void APIENTRY glRaytracingMaterialFlagsQD3D12(GLuint flags); void APIENTRY glRaytracingMaterialFlagQD3D12(GLuint flag, GLboolean enable); @@ -230,6 +238,7 @@ void glRaytracingLightingSetExternalDenoiser(int enable); void glRaytracingLightingSetPathTracingOptions(uint32_t samplesPerPixel, uint32_t maxBounces, int enableDenoiser, float denoiseStrength); void glRaytracingLightingSetVolumetricScattering(glRaytracingLight_t* light, float strength); void glRaytracingLightingSetEmissiveInput(ID3D12Resource* texture, DXGI_FORMAT format); +void glRaytracingLightingSetSpecularInput(ID3D12Resource* texture, DXGI_FORMAT format); void glRaytracingSetMeshMaterialFlags(glRaytracingMeshHandle_t meshHandle, uint32_t materialFlags); void glRaytracingSetMeshGlass(glRaytracingMeshHandle_t meshHandle, int isGlass); uint32_t glRaytracingGetMeshMaterialFlags(glRaytracingMeshHandle_t meshHandle); @@ -354,11 +363,14 @@ enum QD3D12RTVSlotGroup QD3D12_RTV_VELOCITY_RESOLVED = 8, QD3D12_RTV_EMISSIVE_RESOLVED = 9, QD3D12_RTV_BACKBUFFER = 10, - QD3D12_RTV_GROUP_COUNT = 11 + QD3D12_RTV_SPECULAR_RENDER = 11, + QD3D12_RTV_SPECULAR_RESOLVED = 12, + QD3D12_RTV_GROUP_COUNT = 13 }; static const DXGI_FORMAT QD3D12_SceneColorFormat = DXGI_FORMAT_R8G8B8A8_UNORM; static const DXGI_FORMAT QD3D12_EmissiveFormat = DXGI_FORMAT_R16G16B16A16_FLOAT; +static const DXGI_FORMAT QD3D12_SpecularAlbedoFormat = DXGI_FORMAT_R8G8B8A8_UNORM; static const DXGI_FORMAT QD3D12_StreamlineOutputFormat = DXGI_FORMAT_R16G16B16A16_FLOAT; static const DXGI_FORMAT QD3D12_VelocityFormat = DXGI_FORMAT_R16G16B16A16_FLOAT; static const DXGI_FORMAT QD3D12_DepthFormat = DXGI_FORMAT_D32_FLOAT_S8X24_UINT; @@ -488,6 +500,7 @@ struct TextureResource // from any active texture unit. bool isNormalMap = false; bool isGlowMap = false; + bool isSpecularMap = false; }; static void QD3D12_ShutdownMipWorkers(); @@ -529,6 +542,7 @@ struct DrawConstants float jitterPixels[2]; float prevJitterPixels[2]; float _motionPad[4]; + float materialMapPad[4]; float texComb0RGB[4]; float texComb0Alpha[4]; @@ -562,7 +576,7 @@ struct GLBufferObject const char* vendor = "Justin Marshall"; const char* renderer = "Quake D3D12 Wrapper"; const char* version = "1.1-quake-d3d12"; -const char* extensions = "GL_SGIS_multitexture GL_ARB_multitexture GL_EXT_texture_env_add GL_ARB_texture_env_combine GL_ARB_texture_compression GL_EXT_texture_compression_s3tc GL_ARB_vertex_program GL_ARB_fragment_program GL_EXT_texture_cube_map GL_EXT_depth_bounds_test GL_EXT_stencil_two_side GL_ATI_separate_stencil GL_QD3D12_normal_map GL_QD3D12_glow_map GL_QD3D12_glass_material GL_QD3D12_volumetric_light"; +const char* extensions = "GL_SGIS_multitexture GL_ARB_multitexture GL_EXT_texture_env_add GL_ARB_texture_env_combine GL_ARB_texture_compression GL_EXT_texture_compression_s3tc GL_ARB_vertex_program GL_ARB_fragment_program GL_EXT_texture_cube_map GL_EXT_depth_bounds_test GL_EXT_stencil_two_side GL_ATI_separate_stencil GL_QD3D12_normal_map GL_QD3D12_glow_map GL_QD3D12_specular_map GL_QD3D12_glass_material GL_QD3D12_volumetric_light"; enum TexEnvModeShader { @@ -610,6 +624,9 @@ struct BatchKey UINT glowMapSrvIndex = 0; float useGlowMap = 0.0f; float glowMapStrength = 1.0f; + UINT specularMapSrvIndex = 0; + float useSpecularMap = 0.0f; + float specularMapStrength = 1.0f; bool useARBPrograms = false; GLuint arbVertexProgram = 0; @@ -721,6 +738,9 @@ static bool BatchKeyEquals(const BatchKey& a, const BatchKey& b) a.glowMapSrvIndex == b.glowMapSrvIndex && a.useGlowMap == b.useGlowMap && a.glowMapStrength == b.glowMapStrength && + a.specularMapSrvIndex == b.specularMapSrvIndex && + a.useSpecularMap == b.useSpecularMap && + a.specularMapStrength == b.specularMapStrength && a.alphaRef == b.alphaRef && a.alphaFunc == b.alphaFunc && a.useTex0 == b.useTex0 && @@ -827,6 +847,9 @@ struct QD3D12Window std::array, QD3D12_FrameCount> emissiveBuffers; D3D12_RESOURCE_STATES emissiveBufferState[QD3D12_FrameCount] = {}; + std::array, QD3D12_FrameCount> specularBuffers; + D3D12_RESOURCE_STATES specularBufferState[QD3D12_FrameCount] = {}; + std::array, QD3D12_FrameCount> sceneColorMsaaBuffers; D3D12_RESOURCE_STATES sceneColorMsaaState[QD3D12_FrameCount] = {}; @@ -842,6 +865,9 @@ struct QD3D12Window std::array, QD3D12_FrameCount> emissiveMsaaBuffers; D3D12_RESOURCE_STATES emissiveMsaaState[QD3D12_FrameCount] = {}; + std::array, QD3D12_FrameCount> specularMsaaBuffers; + D3D12_RESOURCE_STATES specularMsaaState[QD3D12_FrameCount] = {}; + std::array, QD3D12_FrameCount> backBuffers; D3D12_RESOURCE_STATES backBufferState[QD3D12_FrameCount] = {}; @@ -879,6 +905,10 @@ struct QD3D12Window D3D12_CPU_DESCRIPTOR_HANDLE emissiveSrvCpu[QD3D12_FrameCount]{}; D3D12_GPU_DESCRIPTOR_HANDLE emissiveSrvGpu[QD3D12_FrameCount]{}; + UINT specularSrvIndex[QD3D12_FrameCount] = { UINT_MAX, UINT_MAX }; + D3D12_CPU_DESCRIPTOR_HANDLE specularSrvCpu[QD3D12_FrameCount]{}; + D3D12_GPU_DESCRIPTOR_HANDLE specularSrvGpu[QD3D12_FrameCount]{}; + UINT slOutputSrvIndex[QD3D12_FrameCount] = { UINT_MAX, UINT_MAX }; D3D12_CPU_DESCRIPTOR_HANDLE slOutputSrvCpu[QD3D12_FrameCount]{}; D3D12_GPU_DESCRIPTOR_HANDLE slOutputSrvGpu[QD3D12_FrameCount]{}; @@ -907,6 +937,10 @@ struct QD3D12Window D3D12_CPU_DESCRIPTOR_HANDLE emissiveMsaaSrvCpu[QD3D12_FrameCount]{}; D3D12_GPU_DESCRIPTOR_HANDLE emissiveMsaaSrvGpu[QD3D12_FrameCount]{}; + UINT specularMsaaSrvIndex[QD3D12_FrameCount] = { UINT_MAX, UINT_MAX }; + D3D12_CPU_DESCRIPTOR_HANDLE specularMsaaSrvCpu[QD3D12_FrameCount]{}; + D3D12_GPU_DESCRIPTOR_HANDLE specularMsaaSrvGpu[QD3D12_FrameCount]{}; + D3D12_VIEWPORT viewport{}; D3D12_RECT scissor{}; @@ -1180,6 +1214,8 @@ struct GLState float currentNormalMapYSign = 1.0f; GLuint currentGlowMapTexture = 0; float currentGlowMapStrength = 1.0f; + GLuint currentSpecularMapTexture = 0; + float currentSpecularMapStrength = 1.0f; ImmediateVertexBuffer immediateVerts; GLenum matrixMode = GL_MODELVIEW; @@ -1770,6 +1806,12 @@ static D3D12_CPU_DESCRIPTOR_HANDLE CurrentEmissiveRTV() return QD3D12_RtvAt(w, QD3D12_RTV_EMISSIVE_RENDER, w.frameIndex); } +static D3D12_CPU_DESCRIPTOR_HANDLE CurrentSpecularRTV() +{ + QD3D12Window& w = *g_currentWindow; + return QD3D12_RtvAt(w, QD3D12_RTV_SPECULAR_RENDER, w.frameIndex); +} + static D3D12_CPU_DESCRIPTOR_HANDLE CurrentResolvedSceneColorRTV() { QD3D12Window& w = *g_currentWindow; @@ -1800,6 +1842,12 @@ static D3D12_CPU_DESCRIPTOR_HANDLE CurrentResolvedEmissiveRTV() return QD3D12_RtvAt(w, QD3D12_RTV_EMISSIVE_RESOLVED, w.frameIndex); } +static D3D12_CPU_DESCRIPTOR_HANDLE CurrentResolvedSpecularRTV() +{ + QD3D12Window& w = *g_currentWindow; + return QD3D12_RtvAt(w, QD3D12_RTV_SPECULAR_RESOLVED, w.frameIndex); +} + static D3D12_CPU_DESCRIPTOR_HANDLE CurrentBackBufferRTV() { QD3D12Window& w = *g_currentWindow; @@ -1894,6 +1942,12 @@ static bool QD3D12_IsTextureTaggedGlowMap(GLuint id) return tex && tex->isGlowMap; } +static bool QD3D12_IsTextureTaggedSpecularMap(GLuint id) +{ + TextureResource* tex = QD3D12_FindTextureResource(id); + return tex && tex->isSpecularMap; +} + static TextureResource* QD3D12_SelectNormalMapTexture(TextureResource* const* allTextures) { // Explicit material binding wins. This lets callers bind a normal map even @@ -1965,6 +2019,45 @@ static TextureResource* QD3D12_SelectGlowMapTexture(TextureResource* const* allT return nullptr; } +static TextureResource* QD3D12_SelectSpecularMapTexture(TextureResource* const* allTextures) +{ + // Explicit material binding wins. Tagged specular maps can also be discovered + // from any fixed-function texture unit without consuming the diffuse/lightmap slots. + if (g_gl.currentSpecularMapTexture != 0) + { + TextureResource* explicitSpecular = QD3D12_FindTextureResource(g_gl.currentSpecularMapTexture); + if (explicitSpecular) + { + // Avoid common cross-role binding mistakes. A texture tagged only as a + // normal or glow map should not silently become specular albedo. + if ((explicitSpecular->isNormalMap && !explicitSpecular->isSpecularMap) || + (explicitSpecular->isGlowMap && !explicitSpecular->isSpecularMap)) + return nullptr; + + return explicitSpecular; + } + } + + if (allTextures) + { + for (UINT i = 0; i < QD3D12_MaxTextureUnits; ++i) + { + TextureResource* tex = allTextures[i]; + if (tex && tex != &g_gl.whiteTexture && tex->isSpecularMap) + return tex; + } + } + + for (UINT i = 0; i < QD3D12_MaxTextureUnits; ++i) + { + TextureResource* tex = QD3D12_FindTextureResource(g_gl.boundTexture[i]); + if (tex && tex->isSpecularMap) + return tex; + } + + return nullptr; +} + static const uint8_t* QD3D12_ResolveArrayPointer(const void* ptr) { if (g_gl.boundArrayBuffer != 0) @@ -2109,6 +2202,7 @@ cbuffer DrawCB : register(b0) float2 gJitterPixels; float2 gPrevJitterPixels; float4 gMotionPad; + float4 gMaterialMapPad; float4 gTexComb0RGB; float4 gTexComb0Alpha; @@ -2126,15 +2220,19 @@ cbuffer DrawCB : register(b0) #define gParallaxScale 0.0 #define gUseGlowMap gMotionPad.w #define gGlowMapStrength gMotionPad.w +#define gUseSpecularMap gMaterialMapPad.x +#define gSpecularMapStrength gMaterialMapPad.y Texture2D gTex0 : register(t0); Texture2D gTex1 : register(t1); Texture2D gNormalMap : register(t2); Texture2D gGlowMap : register(t3); +Texture2D gSpecularMap : register(t4); SamplerState gSamp0 : register(s0); SamplerState gSamp1 : register(s1); SamplerState gSamp2 : register(s2); SamplerState gSamp3 : register(s3); +SamplerState gSamp4 : register(s4); struct VSIn { @@ -2171,6 +2269,7 @@ struct PSOut float4 position : SV_Target2; float4 velocity : SV_Target3; float4 emissive : SV_Target4; + float4 specular : SV_Target5; }; float4 QD3D12_GetTexEnvSource(float source, float4 texel, float4 primary, float4 previous, float4 constantColor) @@ -2515,6 +2614,26 @@ float4 BuildGlowEmission(VSOut i) return QD3D12_SampleGlow(i); } +float4 BuildSpecularAlbedo(VSOut i) +{ + if (gUseSpecularMap <= 0.0) + return float4(0.0, 0.0, 0.0, 0.0); + + float4 spec = gSpecularMap.Sample(gSamp4, QD3D12_BuildMaterialUV0(i)); + float strength = max(gSpecularMapStrength, 0.0); + + // RGB-only legacy spec maps are uploaded with forced opaque alpha. For real + // RGBA spec maps, alpha is treated as an artist mask over RGB. The alpha we + // write to the G-buffer is not the brightness mask; it is a presence bit so + // the DXR pass can distinguish "no spec map bound" from an intentional + // black spec-map texel. + bool alphaLooksForcedOpaque = (spec.a >= 0.999); + float alphaMask = alphaLooksForcedOpaque ? 1.0 : saturate(spec.a); + float3 specRgb = saturate(spec.rgb) * alphaMask * strength; + + return float4(specRgb, 1.0); +} + float2 ClipToUv(float4 clipPos) { float2 ndc = clipPos.xy / max(abs(clipPos.w), 1e-6); @@ -2572,6 +2691,7 @@ PSOut PSMain(VSOut i) o.position = float4(i.worldPos, i.attr.x); o.velocity = BuildVelocity(i); o.emissive = BuildGlowEmission(i); + o.specular = BuildSpecularAlbedo(i); return o; } @@ -2584,6 +2704,7 @@ PSOut PSMainAlphaTest(VSOut i) o.position = float4(i.worldPos, i.attr.x); o.velocity = BuildVelocity(i); o.emissive = BuildGlowEmission(i); + o.specular = BuildSpecularAlbedo(i); return o; } @@ -2595,6 +2716,7 @@ PSOut PSMainUntextured(VSOut i) o.position = float4(i.worldPos, i.attr.x); o.velocity = BuildVelocity(i); o.emissive = float4(0.0, 0.0, 0.0, 0.0); + o.specular = BuildSpecularAlbedo(i); return o; } @@ -2626,6 +2748,7 @@ Texture2DMS gNormalMS : register(t2); Texture2DMS gPositionMS : register(t3); Texture2DMS gVelocityMS : register(t4); Texture2DMS gEmissiveMS : register(t5); +Texture2DMS gSpecularMS : register(t6); SamplerState gSamp0 : register(s0); struct VSOut @@ -2765,6 +2888,7 @@ struct PSGBufferPointResolveOut float4 position : SV_Target1; float4 velocity : SV_Target2; float4 emissive : SV_Target3; + float4 specular : SV_Target4; }; PSGBufferPointResolveOut PSGBufferPointResolveMS(VSOut i) @@ -2788,6 +2912,7 @@ PSGBufferPointResolveOut PSGBufferPointResolveMS(VSOut i) o.position = gPositionMS.Load(int2(srcPixel), sampleIndex); o.velocity = gVelocityMS.Load(int2(srcPixel), sampleIndex); o.emissive = gEmissiveMS.Load(int2(srcPixel), sampleIndex); + o.specular = gSpecularMS.Load(int2(srcPixel), sampleIndex); return o; } @@ -3219,6 +3344,19 @@ static BatchKey BuildCurrentBatchKey(GLenum originalMode, const TextureResource* } key.glowMapStrength = g_gl.currentGlowMapStrength; + TextureResource* specularMap = QD3D12_SelectSpecularMapTexture(allTextures); + if (specularMap && specularMap->texture && specularMap->srvIndex != UINT_MAX) + { + key.specularMapSrvIndex = specularMap->srvIndex; + key.useSpecularMap = 1.0f; + } + else + { + key.specularMapSrvIndex = g_gl.whiteTexture.srvIndex; + key.useSpecularMap = 0.0f; + } + key.specularMapStrength = g_gl.currentSpecularMapStrength; + key.useARBPrograms = QD3D12ARB_IsActive(); if (key.useARBPrograms) { @@ -4546,7 +4684,7 @@ static void QD3D12_RunUpscalerOrBlit(QD3D12Window& w) sl::Resource mvec = { sl::ResourceType::eTex2d, w.velocityBuffers[w.frameIndex].Get(), nullptr, nullptr, uint32_t(D3D12_RESOURCE_STATE_PIXEL_SHADER_RESOURCE) }; sl::Resource specularMvec = { sl::ResourceType::eTex2d, w.velocityBuffers[w.frameIndex].Get(), nullptr, nullptr, uint32_t(D3D12_RESOURCE_STATE_PIXEL_SHADER_RESOURCE) }; sl::Resource diffuseAlbedo = { sl::ResourceType::eTex2d, w.sceneColorBuffers[w.frameIndex].Get(), nullptr, nullptr, uint32_t(D3D12_RESOURCE_STATE_PIXEL_SHADER_RESOURCE) }; - sl::Resource specularAlbedo = { sl::ResourceType::eTex2d, w.sceneColorBuffers[w.frameIndex].Get(), nullptr, nullptr, uint32_t(D3D12_RESOURCE_STATE_PIXEL_SHADER_RESOURCE) }; + sl::Resource specularAlbedo = { sl::ResourceType::eTex2d, w.specularBuffers[w.frameIndex].Get(), nullptr, nullptr, uint32_t(D3D12_RESOURCE_STATE_PIXEL_SHADER_RESOURCE) }; sl::Resource normalRoughness = { sl::ResourceType::eTex2d, w.normalBuffers[w.frameIndex].Get(), nullptr, nullptr, uint32_t(D3D12_RESOURCE_STATE_PIXEL_SHADER_RESOURCE) }; sl::ResourceTag tags[] = @@ -4795,6 +4933,7 @@ static bool QD3D12_CanUseGBufferSampleCount(UINT sampleCount) // QD3D12_PointResolveMsaaGBufferToSingleSample() point-loads one sample. QD3D12_FormatSupportsSampleCount(DXGI_FORMAT_R16G16B16A16_FLOAT, sampleCount) && QD3D12_FormatSupportsSampleCount(QD3D12_VelocityFormat, sampleCount) && + QD3D12_FormatSupportsSampleCount(QD3D12_SpecularAlbedoFormat, sampleCount) && QD3D12_FormatSupportsSampleCount(QD3D12_DepthDsvFormat, sampleCount); } @@ -4938,6 +5077,7 @@ static void QD3D12_CreateRTVsForWindow(QD3D12Window& w) const float normalClear[4] = { 0.0f, 0.0f, 1.0f, 0.5f }; const float positionClear[4] = { 0.0f, 0.0f, 0.0f, 0.0f }; const float velocityClear[4] = { 0.0f, 0.0f, 0.0f, 0.0f }; + const float specularClear[4] = { 0.0f, 0.0f, 0.0f, 0.0f }; const bool useMsaa = QD3D12_GBufferMsaaEnabled(); const UINT msaaSamples = QD3D12_GBufferSampleCount(); @@ -5073,6 +5213,25 @@ static void QD3D12_CreateRTVsForWindow(QD3D12Window& w) } } + for (UINT i = 0; i < QD3D12_FrameCount; ++i) + { + if (useMsaa) + { + CreateTexture2D(w.specularMsaaBuffers[i], w.specularMsaaState[i], QD3D12_SpecularAlbedoFormat, specularClear, true, + w.renderWidth, w.renderHeight, msaaSamples, D3D12_RESOURCE_FLAG_ALLOW_RENDER_TARGET, D3D12_RESOURCE_STATE_RENDER_TARGET, + QD3D12_RtvAt(w, QD3D12_RTV_SPECULAR_RENDER, i), true); + CreateTexture2D(w.specularBuffers[i], w.specularBufferState[i], QD3D12_SpecularAlbedoFormat, specularClear, false, + w.renderWidth, w.renderHeight, 1, D3D12_RESOURCE_FLAG_ALLOW_RENDER_TARGET, D3D12_RESOURCE_STATE_PIXEL_SHADER_RESOURCE, + QD3D12_RtvAt(w, QD3D12_RTV_SPECULAR_RESOLVED, i), true); + } + else + { + CreateTexture2D(w.specularBuffers[i], w.specularBufferState[i], QD3D12_SpecularAlbedoFormat, specularClear, true, + w.renderWidth, w.renderHeight, 1, D3D12_RESOURCE_FLAG_ALLOW_RENDER_TARGET, D3D12_RESOURCE_STATE_RENDER_TARGET, + QD3D12_RtvAt(w, QD3D12_RTV_SPECULAR_RENDER, i), true); + } + } + for (UINT i = 0; i < QD3D12_FrameCount; ++i) { D3D12_CPU_DESCRIPTOR_HANDLE backBufferRtv = QD3D12_RtvAt(w, QD3D12_RTV_BACKBUFFER, i); @@ -5165,6 +5324,9 @@ static void QD3D12_CreateRTVsForWindow(QD3D12Window& w) for (UINT i = 0; i < QD3D12_FrameCount; ++i) CreateTextureSrv(w.emissiveBuffers[i].Get(), QD3D12_EmissiveFormat, w.emissiveSrvIndex[i], w.emissiveSrvCpu[i], w.emissiveSrvGpu[i]); + for (UINT i = 0; i < QD3D12_FrameCount; ++i) + CreateTextureSrv(w.specularBuffers[i].Get(), QD3D12_SpecularAlbedoFormat, w.specularSrvIndex[i], w.specularSrvCpu[i], w.specularSrvGpu[i]); + for (UINT i = 0; i < QD3D12_FrameCount; ++i) CreateTextureSrv(w.slOutputBuffers[i].Get(), QD3D12_StreamlineOutputFormat, w.slOutputSrvIndex[i], w.slOutputSrvCpu[i], w.slOutputSrvGpu[i]); @@ -5181,6 +5343,9 @@ static void QD3D12_CreateRTVsForWindow(QD3D12Window& w) for (UINT i = 0; i < QD3D12_FrameCount; ++i) CreateTextureMsaaSrv(w.emissiveMsaaBuffers[i].Get(), QD3D12_EmissiveFormat, w.emissiveMsaaSrvIndex[i], w.emissiveMsaaSrvCpu[i], w.emissiveMsaaSrvGpu[i]); + + for (UINT i = 0; i < QD3D12_FrameCount; ++i) + CreateTextureMsaaSrv(w.specularMsaaBuffers[i].Get(), QD3D12_SpecularAlbedoFormat, w.specularMsaaSrvIndex[i], w.specularMsaaSrvCpu[i], w.specularMsaaSrvGpu[i]); } } @@ -5502,7 +5667,7 @@ static ComPtr CompileShaderVariant(const char* entry, const char* targ static void QD3D12_CreatePostRootSignature() { - D3D12_DESCRIPTOR_RANGE ranges[6]{}; + D3D12_DESCRIPTOR_RANGE ranges[7]{}; for (UINT i = 0; i < _countof(ranges); ++i) { ranges[i].RangeType = D3D12_DESCRIPTOR_RANGE_TYPE_SRV; @@ -5512,7 +5677,7 @@ static void QD3D12_CreatePostRootSignature() ranges[i].OffsetInDescriptorsFromTableStart = 0; } - D3D12_ROOT_PARAMETER params[6]{}; + D3D12_ROOT_PARAMETER params[7]{}; for (UINT i = 0; i < _countof(params); ++i) { params[i].ParameterType = D3D12_ROOT_PARAMETER_TYPE_DESCRIPTOR_TABLE; @@ -5669,7 +5834,7 @@ static D3D12_GRAPHICS_PIPELINE_STATE_DESC BuildPSODesc( d.PrimitiveTopologyType = topoType; - d.NumRenderTargets = nativeColorOnly ? 1u : 5u; + d.NumRenderTargets = nativeColorOnly ? 1u : 6u; d.RTVFormats[0] = DXGI_FORMAT_R8G8B8A8_UNORM; if (!nativeColorOnly) { @@ -5677,6 +5842,7 @@ static D3D12_GRAPHICS_PIPELINE_STATE_DESC BuildPSODesc( d.RTVFormats[2] = DXGI_FORMAT_R16G16B16A16_FLOAT; d.RTVFormats[3] = QD3D12_VelocityFormat; d.RTVFormats[4] = QD3D12_EmissiveFormat; + d.RTVFormats[5] = QD3D12_SpecularAlbedoFormat; } d.DSVFormat = QD3D12_DepthDsvFormat; @@ -5695,12 +5861,13 @@ static D3D12_GRAPHICS_PIPELINE_STATE_DESC BuildPSODesc( d.BlendState.RenderTarget[2].RenderTargetWriteMask = key.colorWriteMask ? D3D12_COLOR_WRITE_ENABLE_ALL : 0; d.BlendState.RenderTarget[3].RenderTargetWriteMask = key.colorWriteMask ? D3D12_COLOR_WRITE_ENABLE_ALL : 0; d.BlendState.RenderTarget[4].RenderTargetWriteMask = key.colorWriteMask ? D3D12_COLOR_WRITE_ENABLE_ALL : 0; + d.BlendState.RenderTarget[5].RenderTargetWriteMask = key.colorWriteMask ? D3D12_COLOR_WRITE_ENABLE_ALL : 0; } d.BlendState.AlphaToCoverageEnable = FALSE; d.BlendState.IndependentBlendEnable = nativeColorOnly ? FALSE : TRUE; - for (int i = 0; i < (nativeColorOnly ? 1 : 5); ++i) + for (int i = 0; i < (nativeColorOnly ? 1 : 6); ++i) { auto& rt = d.BlendState.RenderTarget[i]; rt.BlendEnable = FALSE; @@ -5808,17 +5975,18 @@ static void QD3D12_CreatePSOs() gbufferResolveDesc.PrimitiveTopologyType = D3D12_PRIMITIVE_TOPOLOGY_TYPE_TRIANGLE; gbufferResolveDesc.SampleDesc.Count = 1; gbufferResolveDesc.SampleMask = UINT_MAX; - gbufferResolveDesc.NumRenderTargets = 4; + gbufferResolveDesc.NumRenderTargets = 5; gbufferResolveDesc.RTVFormats[0] = DXGI_FORMAT_R16G16B16A16_FLOAT; gbufferResolveDesc.RTVFormats[1] = DXGI_FORMAT_R16G16B16A16_FLOAT; gbufferResolveDesc.RTVFormats[2] = QD3D12_VelocityFormat; gbufferResolveDesc.RTVFormats[3] = QD3D12_EmissiveFormat; + gbufferResolveDesc.RTVFormats[4] = QD3D12_SpecularAlbedoFormat; gbufferResolveDesc.RasterizerState.FillMode = D3D12_FILL_MODE_SOLID; gbufferResolveDesc.RasterizerState.CullMode = D3D12_CULL_MODE_NONE; gbufferResolveDesc.RasterizerState.DepthClipEnable = TRUE; gbufferResolveDesc.BlendState.AlphaToCoverageEnable = FALSE; gbufferResolveDesc.BlendState.IndependentBlendEnable = FALSE; - for (UINT rt = 0; rt < 4; ++rt) + for (UINT rt = 0; rt < 5; ++rt) { gbufferResolveDesc.BlendState.RenderTarget[rt].BlendEnable = FALSE; gbufferResolveDesc.BlendState.RenderTarget[rt].LogicOpEnable = FALSE; @@ -5937,7 +6105,7 @@ static D3D12_GRAPHICS_PIPELINE_STATE_DESC BuildARBPSODesc( d.InputLayout.NumElements = _countof(kGLVertexInputLayoutARB); d.PrimitiveTopologyType = topoType; - d.NumRenderTargets = nativeColorOnly ? 1u : 5u; + d.NumRenderTargets = nativeColorOnly ? 1u : 6u; d.RTVFormats[0] = DXGI_FORMAT_R8G8B8A8_UNORM; if (!nativeColorOnly) { @@ -5945,6 +6113,7 @@ static D3D12_GRAPHICS_PIPELINE_STATE_DESC BuildARBPSODesc( d.RTVFormats[2] = DXGI_FORMAT_R16G16B16A16_FLOAT; d.RTVFormats[3] = QD3D12_VelocityFormat; d.RTVFormats[4] = QD3D12_EmissiveFormat; + d.RTVFormats[5] = QD3D12_SpecularAlbedoFormat; } d.DSVFormat = QD3D12_DepthDsvFormat; @@ -5959,7 +6128,7 @@ static D3D12_GRAPHICS_PIPELINE_STATE_DESC BuildARBPSODesc( d.BlendState.AlphaToCoverageEnable = FALSE; d.BlendState.IndependentBlendEnable = nativeColorOnly ? FALSE : TRUE; - for (int i = 0; i < (nativeColorOnly ? 1 : 5); ++i) + for (int i = 0; i < (nativeColorOnly ? 1 : 6); ++i) { auto& rt = d.BlendState.RenderTarget[i]; rt.BlendEnable = FALSE; @@ -6334,10 +6503,12 @@ void QD3D12_BeginFrame() const float positionClear[4] = { 0.0f, 0.0f, 0.0f, 0.0f }; const float velocityClear[4] = { 0.0f, 0.0f, 0.0f, 0.0f }; const float emissiveClear[4] = { 0.0f, 0.0f, 0.0f, 0.0f }; + const float specularClear[4] = { 0.0f, 0.0f, 0.0f, 0.0f }; g_gl.cmdList->ClearRenderTargetView(CurrentNormalRTV(), normalClear, 0, nullptr); g_gl.cmdList->ClearRenderTargetView(CurrentPositionRTV(), positionClear, 0, nullptr); g_gl.cmdList->ClearRenderTargetView(CurrentVelocityRTV(), velocityClear, 0, nullptr); g_gl.cmdList->ClearRenderTargetView(CurrentEmissiveRTV(), emissiveClear, 0, nullptr); + g_gl.cmdList->ClearRenderTargetView(CurrentSpecularRTV(), specularClear, 0, nullptr); g_gl.frameOpen = true; g_gl.frameOwner = &w; @@ -6372,6 +6543,7 @@ void QD3D12_EndFrame() QD3D12_TransitionResource(g_gl.cmdList.Get(), w.normalBuffers[w.frameIndex].Get(), w.normalBufferState[w.frameIndex], D3D12_RESOURCE_STATE_PIXEL_SHADER_RESOURCE); QD3D12_TransitionResource(g_gl.cmdList.Get(), w.positionBuffers[w.frameIndex].Get(), w.positionBufferState[w.frameIndex], D3D12_RESOURCE_STATE_PIXEL_SHADER_RESOURCE); QD3D12_TransitionResource(g_gl.cmdList.Get(), w.velocityBuffers[w.frameIndex].Get(), w.velocityBufferState[w.frameIndex], D3D12_RESOURCE_STATE_PIXEL_SHADER_RESOURCE); + QD3D12_TransitionResource(g_gl.cmdList.Get(), w.specularBuffers[w.frameIndex].Get(), w.specularBufferState[w.frameIndex], D3D12_RESOURCE_STATE_PIXEL_SHADER_RESOURCE); QD3D12_TransitionResource(g_gl.cmdList.Get(), w.depthBuffer.Get(), w.depthState, D3D12_RESOURCE_STATE_PIXEL_SHADER_RESOURCE); QD3D12_TransitionResource(g_gl.cmdList.Get(), w.backBuffers[w.frameIndex].Get(), w.backBufferState[w.frameIndex], D3D12_RESOURCE_STATE_RENDER_TARGET); @@ -7862,8 +8034,12 @@ static void FlushImmediate(GLenum mode, const GLVertex* src, size_t n) const bool explicitGlowUnit = (!arbProgramsActive) && (g_gl.currentGlowMapTexture != 0) && (g_gl.boundTexture[unit] == g_gl.currentGlowMapTexture); + const bool taggedSpecularUnit = (!arbProgramsActive) && QD3D12_IsTextureTaggedSpecularMap(g_gl.boundTexture[unit]); + const bool explicitSpecularUnit = (!arbProgramsActive) && + (g_gl.currentSpecularMapTexture != 0) && + (g_gl.boundTexture[unit] == g_gl.currentSpecularMapTexture); - const bool wantsUnit = arbUnit || fixedColorUnit || taggedNormalUnit || explicitNormalUnit || taggedGlowUnit || explicitGlowUnit; + const bool wantsUnit = arbUnit || fixedColorUnit || taggedNormalUnit || explicitNormalUnit || taggedGlowUnit || explicitGlowUnit || taggedSpecularUnit || explicitSpecularUnit; if (!wantsUnit) continue; @@ -7896,6 +8072,13 @@ static void FlushImmediate(GLenum mode, const GLVertex* src, size_t n) UploadTexture(*glowMapTex); } + TextureResource* specularMapTex = QD3D12_SelectSpecularMapTexture(boundTextures); + if (specularMapTex && specularMapTex != &g_gl.whiteTexture && !specularMapTex->gpuValid) + { + EnsureTextureResource(*specularMapTex); + UploadTexture(*specularMapTex); + } + TextureResource* tex0 = boundTextures[0]; TextureResource* tex1 = boundTextures[1]; @@ -8202,10 +8385,12 @@ static void QD3D12_FlushQueuedBatches() D3D12_GPU_DESCRIPTOR_HANDLE lastTex1{}; D3D12_GPU_DESCRIPTOR_HANDLE lastNormalMap{}; D3D12_GPU_DESCRIPTOR_HANDLE lastGlowMap{}; + D3D12_GPU_DESCRIPTOR_HANDLE lastSpecularMap{}; bool haveLastTex0 = false; bool haveLastTex1 = false; bool haveLastNormalMap = false; bool haveLastGlowMap = false; + bool haveLastSpecularMap = false; D3D12_PRIMITIVE_TOPOLOGY lastTopo = D3D_PRIMITIVE_TOPOLOGY_UNDEFINED; for (size_t i = 0; i < g_gl.queuedBatches.size(); ++i) @@ -8232,6 +8417,7 @@ static void QD3D12_FlushQueuedBatches() haveLastTex1 = false; haveLastNormalMap = false; haveLastGlowMap = false; + haveLastSpecularMap = false; lastTopo = D3D_PRIMITIVE_TOPOLOGY_UNDEFINED; } @@ -8300,6 +8486,10 @@ static void QD3D12_FlushQueuedBatches() dc->_motionPad[1] = batch.key.normalMapStrength; dc->_motionPad[2] = batch.key.normalMapYSign; dc->_motionPad[3] = (batch.key.useGlowMap > 0.5f) ? batch.key.glowMapStrength : 0.0f; + dc->materialMapPad[0] = batch.key.useSpecularMap; + dc->materialMapPad[1] = batch.key.specularMapStrength; + dc->materialMapPad[2] = 0.0f; + dc->materialMapPad[3] = 0.0f; if (batch.key.useARBPrograms) { @@ -8356,6 +8546,7 @@ static void QD3D12_FlushQueuedBatches() haveLastTex1 = false; haveLastNormalMap = false; haveLastGlowMap = false; + haveLastSpecularMap = false; } else { @@ -8404,6 +8595,18 @@ static void QD3D12_FlushQueuedBatches() lastGlowMap = glowMapGpu; haveLastGlowMap = true; } + + UINT specularSrvIndex = batch.key.specularMapSrvIndex; + if (specularSrvIndex == UINT_MAX) + specularSrvIndex = g_gl.whiteTexture.srvIndex; + + D3D12_GPU_DESCRIPTOR_HANDLE specularMapGpu = QD3D12_SrvGpu(specularSrvIndex); + if (!haveLastSpecularMap || specularMapGpu.ptr != lastSpecularMap.ptr) + { + g_gl.cmdList->SetGraphicsRootDescriptorTable(5, specularMapGpu); + lastSpecularMap = specularMapGpu; + haveLastSpecularMap = true; + } } g_gl.cmdList->SetGraphicsRootConstantBufferView(0, cbAlloc.gpu); @@ -8535,12 +8738,14 @@ void APIENTRY glClear(GLbitfield mask) const float positionClear[4] = { 0.0f, 0.0f, 0.0f, 0.0f }; const float velocityClear[4] = { 0.0f, 0.0f, 0.0f, 0.0f }; const float emissiveClear[4] = { 0.0f, 0.0f, 0.0f, 0.0f }; + const float specularClear[4] = { 0.0f, 0.0f, 0.0f, 0.0f }; g_gl.cmdList->ClearRenderTargetView(CurrentRTV(), cc, 1, &clearRect); g_gl.cmdList->ClearRenderTargetView(CurrentNormalRTV(), normalClear, 1, &clearRect); g_gl.cmdList->ClearRenderTargetView(CurrentPositionRTV(), positionClear, 1, &clearRect); g_gl.cmdList->ClearRenderTargetView(CurrentVelocityRTV(), velocityClear, 1, &clearRect); g_gl.cmdList->ClearRenderTargetView(CurrentEmissiveRTV(), emissiveClear, 1, &clearRect); + g_gl.cmdList->ClearRenderTargetView(CurrentSpecularRTV(), specularClear, 1, &clearRect); } } @@ -8890,6 +9095,9 @@ void APIENTRY glDeleteTextures(GLsizei n, const GLuint* textures) if (g_gl.currentGlowMapTexture == deadId) g_gl.currentGlowMapTexture = 0; + if (g_gl.currentSpecularMapTexture == deadId) + g_gl.currentSpecularMapTexture = 0; + auto it = g_gl.textures.find(deadId); if (it != g_gl.textures.end()) { @@ -8996,6 +9204,49 @@ void APIENTRY glGlowMapStrengthf(GLfloat strength) g_gl.currentGlowMapStrength = (strength < 0.0f) ? 0.0f : strength; } +void APIENTRY glTagTextureSpecularMap(GLuint texture, GLboolean isSpecularMap) +{ + if (texture == 0) + { + g_gl.lastError = GL_INVALID_VALUE; + return; + } + + TextureResource& tex = QD3D12_EnsureTextureName(texture); + tex.isSpecularMap = (isSpecularMap != GL_FALSE); +} + +void APIENTRY glTextureSpecularMap(GLuint texture, GLboolean isSpecularMap) +{ + glTagTextureSpecularMap(texture, isSpecularMap); +} + +void APIENTRY glBindSpecularMapTexture(GLuint texture) +{ + if (texture != 0) + { + TextureResource& tex = QD3D12_EnsureTextureName(texture); + if ((tex.isNormalMap && !tex.isSpecularMap) || (tex.isGlowMap && !tex.isSpecularMap)) + { + QD3D12_Log("glBindSpecularMapTexture(%u) ignored: texture is tagged as a different material map role.", texture); + g_gl.currentSpecularMapTexture = 0; + return; + } + } + + g_gl.currentSpecularMapTexture = texture; +} + +void APIENTRY glSpecularMapTexture(GLuint texture) +{ + glBindSpecularMapTexture(texture); +} + +void APIENTRY glSpecularMapStrengthf(GLfloat strength) +{ + g_gl.currentSpecularMapStrength = (strength < 0.0f) ? 0.0f : strength; +} + void APIENTRY glRaytracingMaterialFlagsQD3D12(GLuint flags) { g_gl.currentRayMaterialFlags = QD3D12_ClampRayMaterialFlags((uint32_t)flags); @@ -9136,6 +9387,10 @@ void APIENTRY glGetIntegerv(GLenum pname, GLint* params) *params = (GLint)g_gl.currentGlowMapTexture; break; + case GL_SPECULAR_MAP_BINDING_QD3D12: + *params = (GLint)g_gl.currentSpecularMapTexture; + break; + case GL_QD3D12_MATERIAL_FLAGS: *params = (GLint)QD3D12_CurrentRayMaterialFlags(); break; @@ -10138,10 +10393,14 @@ void QD3D12_ReleaseWindowSizeResources(QD3D12Window& w) w.normalBuffers[i].Reset(); w.positionBuffers[i].Reset(); w.velocityBuffers[i].Reset(); + w.emissiveBuffers[i].Reset(); + w.specularBuffers[i].Reset(); w.sceneColorMsaaBuffers[i].Reset(); w.normalMsaaBuffers[i].Reset(); w.positionMsaaBuffers[i].Reset(); w.velocityMsaaBuffers[i].Reset(); + w.emissiveMsaaBuffers[i].Reset(); + w.specularMsaaBuffers[i].Reset(); w.slOutputBuffers[i].Reset(); w.sceneColorState[i] = D3D12_RESOURCE_STATE_COMMON; @@ -10149,10 +10408,14 @@ void QD3D12_ReleaseWindowSizeResources(QD3D12Window& w) w.normalBufferState[i] = D3D12_RESOURCE_STATE_COMMON; w.positionBufferState[i] = D3D12_RESOURCE_STATE_COMMON; w.velocityBufferState[i] = D3D12_RESOURCE_STATE_COMMON; + w.emissiveBufferState[i] = D3D12_RESOURCE_STATE_COMMON; + w.specularBufferState[i] = D3D12_RESOURCE_STATE_COMMON; w.sceneColorMsaaState[i] = D3D12_RESOURCE_STATE_COMMON; w.normalMsaaState[i] = D3D12_RESOURCE_STATE_COMMON; w.positionMsaaState[i] = D3D12_RESOURCE_STATE_COMMON; w.velocityMsaaState[i] = D3D12_RESOURCE_STATE_COMMON; + w.emissiveMsaaState[i] = D3D12_RESOURCE_STATE_COMMON; + w.specularMsaaState[i] = D3D12_RESOURCE_STATE_COMMON; w.slOutputState[i] = D3D12_RESOURCE_STATE_COMMON; } @@ -11700,6 +11963,11 @@ PROC WINAPI qd3d12_wglGetProcAddress(LPCSTR name) { { "glBindGlowMapTexture", (PROC)glBindGlowMapTexture }, { "glGlowMapTexture", (PROC)glGlowMapTexture }, { "glGlowMapStrengthf", (PROC)glGlowMapStrengthf }, + { "glTagTextureSpecularMap", (PROC)glTagTextureSpecularMap }, + { "glTextureSpecularMap", (PROC)glTextureSpecularMap }, + { "glBindSpecularMapTexture", (PROC)glBindSpecularMapTexture }, + { "glSpecularMapTexture", (PROC)glSpecularMapTexture }, + { "glSpecularMapStrengthf", (PROC)glSpecularMapStrengthf }, { "glGlassMaterialQD3D12", (PROC)glGlassMaterialQD3D12 }, { "glMaterialGlassQD3D12", (PROC)glMaterialGlassQD3D12 }, { "glRaytracingMaterialFlagsQD3D12", (PROC)glRaytracingMaterialFlagsQD3D12 }, @@ -11959,8 +12227,8 @@ static void QD3D12_PointResolveMsaaGBufferToSingleSample(QD3D12Window& w) const UINT frame = w.frameIndex; if (!w.depthMsaaBuffer || - !w.normalMsaaBuffers[frame] || !w.positionMsaaBuffers[frame] || !w.velocityMsaaBuffers[frame] || !w.emissiveMsaaBuffers[frame] || - !w.normalBuffers[frame] || !w.positionBuffers[frame] || !w.velocityBuffers[frame] || !w.emissiveBuffers[frame]) + !w.normalMsaaBuffers[frame] || !w.positionMsaaBuffers[frame] || !w.velocityMsaaBuffers[frame] || !w.emissiveMsaaBuffers[frame] || !w.specularMsaaBuffers[frame] || + !w.normalBuffers[frame] || !w.positionBuffers[frame] || !w.velocityBuffers[frame] || !w.emissiveBuffers[frame] || !w.specularBuffers[frame]) { return; } @@ -11970,18 +12238,21 @@ static void QD3D12_PointResolveMsaaGBufferToSingleSample(QD3D12Window& w) QD3D12_TransitionResource(cl, w.positionMsaaBuffers[frame].Get(), w.positionMsaaState[frame], D3D12_RESOURCE_STATE_PIXEL_SHADER_RESOURCE); QD3D12_TransitionResource(cl, w.velocityMsaaBuffers[frame].Get(), w.velocityMsaaState[frame], D3D12_RESOURCE_STATE_PIXEL_SHADER_RESOURCE); QD3D12_TransitionResource(cl, w.emissiveMsaaBuffers[frame].Get(), w.emissiveMsaaState[frame], D3D12_RESOURCE_STATE_PIXEL_SHADER_RESOURCE); + QD3D12_TransitionResource(cl, w.specularMsaaBuffers[frame].Get(), w.specularMsaaState[frame], D3D12_RESOURCE_STATE_PIXEL_SHADER_RESOURCE); QD3D12_TransitionResource(cl, w.normalBuffers[frame].Get(), w.normalBufferState[frame], D3D12_RESOURCE_STATE_RENDER_TARGET); QD3D12_TransitionResource(cl, w.positionBuffers[frame].Get(), w.positionBufferState[frame], D3D12_RESOURCE_STATE_RENDER_TARGET); QD3D12_TransitionResource(cl, w.velocityBuffers[frame].Get(), w.velocityBufferState[frame], D3D12_RESOURCE_STATE_RENDER_TARGET); QD3D12_TransitionResource(cl, w.emissiveBuffers[frame].Get(), w.emissiveBufferState[frame], D3D12_RESOURCE_STATE_RENDER_TARGET); + QD3D12_TransitionResource(cl, w.specularBuffers[frame].Get(), w.specularBufferState[frame], D3D12_RESOURCE_STATE_RENDER_TARGET); - D3D12_CPU_DESCRIPTOR_HANDLE rtvs[4] = + D3D12_CPU_DESCRIPTOR_HANDLE rtvs[5] = { CurrentResolvedNormalRTV(), CurrentResolvedPositionRTV(), CurrentResolvedVelocityRTV(), - CurrentResolvedEmissiveRTV() + CurrentResolvedEmissiveRTV(), + CurrentResolvedSpecularRTV() }; D3D12_VIEWPORT viewport{}; @@ -12002,18 +12273,19 @@ static void QD3D12_PointResolveMsaaGBufferToSingleSample(QD3D12Window& w) cl->SetDescriptorHeaps(_countof(heaps), heaps); cl->SetGraphicsRootSignature(g_gl.postRootSig.Get()); cl->SetPipelineState(g_gl.postGBufferPointResolveMsaaPSO.Get()); - cl->OMSetRenderTargets(4, rtvs, FALSE, nullptr); + cl->OMSetRenderTargets(5, rtvs, FALSE, nullptr); cl->RSSetViewports(1, &viewport); cl->RSSetScissorRects(1, &scissor); cl->IASetPrimitiveTopology(D3D_PRIMITIVE_TOPOLOGY_TRIANGLELIST); - // t1..t5: depth + discontinuous G-buffer attributes. The shader uses + // t1..t6: depth + discontinuous G-buffer attributes. The shader uses // Texture2DMS.Load(), selects a single depth-nearest sample, and never averages. cl->SetGraphicsRootDescriptorTable(1, w.depthMsaaSrvGpu); cl->SetGraphicsRootDescriptorTable(2, w.normalMsaaSrvGpu[frame]); cl->SetGraphicsRootDescriptorTable(3, w.positionMsaaSrvGpu[frame]); cl->SetGraphicsRootDescriptorTable(4, w.velocityMsaaSrvGpu[frame]); cl->SetGraphicsRootDescriptorTable(5, w.emissiveMsaaSrvGpu[frame]); + cl->SetGraphicsRootDescriptorTable(6, w.specularMsaaSrvGpu[frame]); cl->DrawInstanced(3, 1, 0, 0); } @@ -12090,6 +12362,7 @@ static void QD3D12_BindLowResSceneTargets(QD3D12Window& w) QD3D12_TransitionResource(cl, w.positionMsaaBuffers[w.frameIndex].Get(), w.positionMsaaState[w.frameIndex], D3D12_RESOURCE_STATE_RENDER_TARGET); QD3D12_TransitionResource(cl, w.velocityMsaaBuffers[w.frameIndex].Get(), w.velocityMsaaState[w.frameIndex], D3D12_RESOURCE_STATE_RENDER_TARGET); QD3D12_TransitionResource(cl, w.emissiveMsaaBuffers[w.frameIndex].Get(), w.emissiveMsaaState[w.frameIndex], D3D12_RESOURCE_STATE_RENDER_TARGET); + QD3D12_TransitionResource(cl, w.specularMsaaBuffers[w.frameIndex].Get(), w.specularMsaaState[w.frameIndex], D3D12_RESOURCE_STATE_RENDER_TARGET); QD3D12_TransitionResource(cl, w.depthMsaaBuffer.Get(), w.depthMsaaState, D3D12_RESOURCE_STATE_DEPTH_WRITE); } else @@ -12099,19 +12372,21 @@ static void QD3D12_BindLowResSceneTargets(QD3D12Window& w) QD3D12_TransitionResource(cl, w.positionBuffers[w.frameIndex].Get(), w.positionBufferState[w.frameIndex], D3D12_RESOURCE_STATE_RENDER_TARGET); QD3D12_TransitionResource(cl, w.velocityBuffers[w.frameIndex].Get(), w.velocityBufferState[w.frameIndex], D3D12_RESOURCE_STATE_RENDER_TARGET); QD3D12_TransitionResource(cl, w.emissiveBuffers[w.frameIndex].Get(), w.emissiveBufferState[w.frameIndex], D3D12_RESOURCE_STATE_RENDER_TARGET); + QD3D12_TransitionResource(cl, w.specularBuffers[w.frameIndex].Get(), w.specularBufferState[w.frameIndex], D3D12_RESOURCE_STATE_RENDER_TARGET); QD3D12_TransitionResource(cl, w.depthBuffer.Get(), w.depthState, D3D12_RESOURCE_STATE_DEPTH_WRITE); } - D3D12_CPU_DESCRIPTOR_HANDLE rtvs[5] = + D3D12_CPU_DESCRIPTOR_HANDLE rtvs[6] = { CurrentRTV(), CurrentNormalRTV(), CurrentPositionRTV(), CurrentVelocityRTV(), - CurrentEmissiveRTV() + CurrentEmissiveRTV(), + CurrentSpecularRTV() }; D3D12_CPU_DESCRIPTOR_HANDLE dsv = CurrentActiveSceneDepthDSV(); - cl->OMSetRenderTargets(5, rtvs, FALSE, &dsv); + cl->OMSetRenderTargets(6, rtvs, FALSE, &dsv); cl->RSSetViewports(1, &w.viewport); cl->RSSetScissorRects(1, &w.scissor); } @@ -12206,6 +12481,7 @@ static void QD3D12_ResolveSceneToOutputAndEnterNativePhase(QD3D12Window& w) QD3D12_TransitionResource(cl, w.sceneColorBuffers[w.frameIndex].Get(), w.sceneColorState[w.frameIndex], D3D12_RESOURCE_STATE_PIXEL_SHADER_RESOURCE); QD3D12_TransitionResource(cl, w.velocityBuffers[w.frameIndex].Get(), w.velocityBufferState[w.frameIndex], D3D12_RESOURCE_STATE_PIXEL_SHADER_RESOURCE); + QD3D12_TransitionResource(cl, w.specularBuffers[w.frameIndex].Get(), w.specularBufferState[w.frameIndex], D3D12_RESOURCE_STATE_PIXEL_SHADER_RESOURCE); QD3D12_TransitionResource(cl, w.depthBuffer.Get(), w.depthState, D3D12_RESOURCE_STATE_PIXEL_SHADER_RESOURCE); if (useLightingUpscaleInput) @@ -12271,9 +12547,10 @@ void glLightScene(glRaytracingSceneHandle_t sceneHandle) ID3D12Resource* sceneNormal = window->normalBuffers[frameIndex].Get(); ID3D12Resource* scenePosition = window->positionBuffers[frameIndex].Get(); ID3D12Resource* sceneEmissive = window->emissiveBuffers[frameIndex].Get(); + ID3D12Resource* sceneSpecular = window->specularBuffers[frameIndex].Get(); ID3D12Resource* sceneDepth = window->depthBuffer.Get(); - if (!sceneColor || !sceneNormal || !scenePosition || !sceneEmissive || !sceneDepth) + if (!sceneColor || !sceneNormal || !scenePosition || !sceneEmissive || !sceneSpecular || !sceneDepth) return; QD3D12_TransitionResource( @@ -12300,6 +12577,12 @@ void glLightScene(glRaytracingSceneHandle_t sceneHandle) window->emissiveBufferState[frameIndex], D3D12_RESOURCE_STATE_NON_PIXEL_SHADER_RESOURCE); + QD3D12_TransitionResource( + cl, + sceneSpecular, + window->specularBufferState[frameIndex], + D3D12_RESOURCE_STATE_NON_PIXEL_SHADER_RESOURCE); + QD3D12_TransitionResource( cl, sceneDepth, @@ -12342,6 +12625,7 @@ void glLightScene(glRaytracingSceneHandle_t sceneHandle) useDLSSRayReconstruction ? 0 : 1, useDLSSRayReconstruction ? 0.0f : 1.0f); glRaytracingLightingSetEmissiveInput(sceneEmissive, QD3D12_EmissiveFormat); + glRaytracingLightingSetSpecularInput(sceneSpecular, QD3D12_SpecularAlbedoFormat); glRaytracingLightingPassDesc_t pass = {}; pass.albedoTexture = sceneColor; @@ -12371,6 +12655,7 @@ void glLightScene(glRaytracingSceneHandle_t sceneHandle) QD3D12_TransitionResource(cl, window->positionMsaaBuffers[frameIndex].Get(), window->positionMsaaState[frameIndex], D3D12_RESOURCE_STATE_RENDER_TARGET); QD3D12_TransitionResource(cl, window->velocityMsaaBuffers[frameIndex].Get(), window->velocityMsaaState[frameIndex], D3D12_RESOURCE_STATE_RENDER_TARGET); QD3D12_TransitionResource(cl, window->emissiveMsaaBuffers[frameIndex].Get(), window->emissiveMsaaState[frameIndex], D3D12_RESOURCE_STATE_RENDER_TARGET); + QD3D12_TransitionResource(cl, window->specularMsaaBuffers[frameIndex].Get(), window->specularMsaaState[frameIndex], D3D12_RESOURCE_STATE_RENDER_TARGET); QD3D12_TransitionResource(cl, window->depthMsaaBuffer.Get(), window->depthMsaaState, D3D12_RESOURCE_STATE_DEPTH_WRITE); } else @@ -12399,6 +12684,12 @@ void glLightScene(glRaytracingSceneHandle_t sceneHandle) window->emissiveBufferState[frameIndex], D3D12_RESOURCE_STATE_RENDER_TARGET); + QD3D12_TransitionResource( + cl, + sceneSpecular, + window->specularBufferState[frameIndex], + D3D12_RESOURCE_STATE_RENDER_TARGET); + QD3D12_TransitionResource( cl, sceneDepth, diff --git a/neo/opengl/opengl.h b/neo/opengl/opengl.h index 77803f40..770aea4e 100644 --- a/neo/opengl/opengl.h +++ b/neo/opengl/opengl.h @@ -2222,6 +2222,12 @@ void APIENTRY glNormalMapTexture(GLuint texture); // alia void APIENTRY glNormalMapStrengthf(GLfloat strength); // default 1.0 void APIENTRY glNormalMapYSignf(GLfloat sign); // default +1, pass -1 to flip green/Y +void APIENTRY glTagTextureSpecularMap(GLuint texture, GLboolean isSpecularMap); +void APIENTRY glTextureSpecularMap(GLuint texture, GLboolean isSpecularMap); +void APIENTRY glBindSpecularMapTexture(GLuint texture); +void APIENTRY glSpecularMapTexture(GLuint texture); +void APIENTRY glSpecularMapStrengthf(GLfloat strength); + void glUpdateTopLevelAceelStructure( glRaytracingSceneHandle_t scene, uint32_t mesh, diff --git a/neo/renderer/Material.cpp b/neo/renderer/Material.cpp index 8e9bac44..5b82e260 100644 --- a/neo/renderer/Material.cpp +++ b/neo/renderer/Material.cpp @@ -1900,6 +1900,20 @@ idImage* idMaterial::GetBumpImage(void) const { return GetEditorImage(); } +/* +=================== +idMaterial::GetSpecImage +=================== +*/ +idImage* idMaterial::GetSpecImage(void) const { + for (int i = 0; i < numStages; i++) { + if (stages[i].lighting == SL_SPECULAR && stages[i].texture.image) { + return stages[i].texture.image; + } + } + return globalImages->blackImage; +} + /* =================== idMaterial::GetGlowImage diff --git a/neo/renderer/Material.h b/neo/renderer/Material.h index dcd5353d..e2638a70 100644 --- a/neo/renderer/Material.h +++ b/neo/renderer/Material.h @@ -507,6 +507,7 @@ public: // jmarshall idImage* GetDiffuseImage(void) const; idImage* GetBumpImage(void) const; + idImage* GetSpecImage(void) const; idImage* GetGlowImage(void) const; bool IsSky(void) const; // jmarshall end diff --git a/neo/renderer/draw_common.cpp b/neo/renderer/draw_common.cpp index d81a16d9..77b0ebb9 100644 --- a/neo/renderer/draw_common.cpp +++ b/neo/renderer/draw_common.cpp @@ -442,10 +442,10 @@ void RB_T_FillDepthBuffer( const drawSurf_t *surf ) { bool drawSolid = false; - if ( shader->Coverage() == MC_OPAQUE ) { + //if ( shader->Coverage() == MC_OPAQUE ) { drawSolid = true; - } - + //} +#if 0 // we may have multiple alpha tested stages if ( shader->Coverage() == MC_PERFORATED ) { // if the only alpha tested stages are condition register omitted, @@ -497,7 +497,7 @@ void RB_T_FillDepthBuffer( const drawSurf_t *surf ) { drawSolid = true; } } - +#endif // draw the entire surface solid // draw the entire surface solid if (drawSolid && !shader->IsSky()) { @@ -512,10 +512,15 @@ void RB_T_FillDepthBuffer( const drawSurf_t *surf ) { shader->GetBumpImage()->Bind(); glBindNormalMapTexture(shader->GetBumpImage()->texnum); + GL_SelectTexture(2); + glEnable(GL_TEXTURE_2D); + shader->GetSpecImage()->Bind(); + glBindSpecularMapTexture(shader->GetSpecImage()->texnum); + idImage* glowMapImage = shader->GetGlowImage(); if (glowMapImage) { - GL_SelectTexture(2); + GL_SelectTexture(3); glEnable(GL_TEXTURE_2D); glowMapImage->Bind(); glBindGlowMapTexture(glowMapImage->texnum); @@ -539,11 +544,15 @@ void RB_T_FillDepthBuffer( const drawSurf_t *surf ) { if (glowMapImage) { - GL_SelectTexture(2); + GL_SelectTexture(3); glBindGlowMapTexture(0); globalImages->BindNull(); } + GL_SelectTexture(2); + glBindSpecularMapTexture(0); + globalImages->BindNull(); + GL_SelectTexture(1); glBindNormalMapTexture(0); globalImages->BindNull(); @@ -940,6 +949,11 @@ void RB_STD_T_RenderShaderPasses( const drawSurf_t *surf ) { continue; } + // check the stage enable condition + if (regs[pStage->conditionRegister] == 0) { + continue; + } + // select the vertex color source if ( pStage->vertexColor == SVC_IGNORE ) { glColor4fv( color );