Added emissive texture support

This commit is contained in:
Justin Marshall
2026-05-05 21:14:31 -07:00
parent 7066f5c415
commit baeae00185
6 changed files with 614 additions and 333 deletions
+118 -15
View File
@@ -2287,6 +2287,8 @@ struct glRaytracingLightingState_t
DXGI_FORMAT denoiseFormat;
uint32_t frameCounter;
bool externalDenoiser;
ID3D12Resource* emissiveTexture;
DXGI_FORMAT emissiveFormat;
bool uploadToCurrentFrameResource;
bool initialized;
@@ -2311,6 +2313,8 @@ struct glRaytracingLightingState_t
currentHistoryIndex = 0;
frameCounter = 0;
externalDenoiser = false;
emissiveTexture = nullptr;
emissiveFormat = DXGI_FORMAT_R16G16B16A16_FLOAT;
uploadToCurrentFrameResource = false;
initialized = false;
}
@@ -2333,14 +2337,15 @@ enum glRaytracingLightingDescriptorIndex_t
GLR_DESC_DENOISE_B_SRV = 8,
GLR_DESC_HISTORY_SRV = 9,
GLR_DESC_TEMPORAL_SRV = 10,
GLR_DESC_PATHTRACE_UAV = 11,
GLR_DESC_DENOISE_A_UAV = 12,
GLR_DESC_DENOISE_B_UAV = 13,
GLR_DESC_OUTPUT_UAV = 14,
GLR_DESC_TEMPORAL_UAV = 15,
GLR_DESC_HISTORY_UAV = 16,
GLR_DESC_COUNT = 17,
GLR_DESC_SRV_COUNT = 11,
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_UAV_COUNT = 6
};
@@ -2434,6 +2439,7 @@ Texture2D<float> gDepthTex : register(t2);
Texture2D<float4> gNormalTex : register(t3);
Texture2D<float4> gPositionTex : register(t4);
RaytracingAccelerationStructure gSceneBVH : register(t5);
Texture2D<float4> gEmissiveTex : register(t11);
RWTexture2D<float4> gOutputTex : register(u0);
static const uint GL_RAYTRACING_LIGHT_TYPE_POINT = 0;
@@ -3373,6 +3379,61 @@ float TraceVisibilityBiased(float3 worldPos, float3 N, float3 dir, float maxT)
return TraceShadow(origin, dir, max(maxT - gShadowBias * 0.5, 0.001));
}
float3 CompressEmissiveRadiance(float3 e, float peakLimit)
{
e = max(e, 0.0);
float peak = max(max(e.r, e.g), e.b);
if (peak > peakLimit && peak > 1e-5)
e *= peakLimit / peak;
return e;
}
float3 LoadEmissiveRadianceClamped(int2 p)
{
int2 maxPixel = int2((int)gScreenSize.x - 1, (int)gScreenSize.y - 1);
p = clamp(p, int2(0, 0), maxPixel);
return CompressEmissiveRadiance(gEmissiveTex.Load(int3(p, 0)).rgb, 7.50);
}
float3 EstimateEmissiveBloomAtPixel(uint2 pixel)
{
int2 p = int2(pixel);
// Strong threshold-free bloom. This stays direct/bloom-only: no extra
// TraceRay calls, and no emissive lighting contribution. The goal is to make
// visible glow maps read as hot emissive surfaces in the DXR output.
float3 bloom = 0.0;
bloom += LoadEmissiveRadianceClamped(p) * 0.180;
bloom += LoadEmissiveRadianceClamped(p + int2( 1, 0)) * 0.220;
bloom += LoadEmissiveRadianceClamped(p + int2(-1, 0)) * 0.220;
bloom += LoadEmissiveRadianceClamped(p + int2( 0, 1)) * 0.220;
bloom += LoadEmissiveRadianceClamped(p + int2( 0, -1)) * 0.220;
bloom += LoadEmissiveRadianceClamped(p + int2( 2, 2)) * 0.135;
bloom += LoadEmissiveRadianceClamped(p + int2(-2, 2)) * 0.135;
bloom += LoadEmissiveRadianceClamped(p + int2( 2, -2)) * 0.135;
bloom += LoadEmissiveRadianceClamped(p + int2(-2, -2)) * 0.135;
bloom += LoadEmissiveRadianceClamped(p + int2( 4, 0)) * 0.090;
bloom += LoadEmissiveRadianceClamped(p + int2(-4, 0)) * 0.090;
bloom += LoadEmissiveRadianceClamped(p + int2( 0, 4)) * 0.090;
bloom += LoadEmissiveRadianceClamped(p + int2( 0, -4)) * 0.090;
bloom += LoadEmissiveRadianceClamped(p + int2( 8, 0)) * 0.060;
bloom += LoadEmissiveRadianceClamped(p + int2(-8, 0)) * 0.060;
bloom += LoadEmissiveRadianceClamped(p + int2( 0, 8)) * 0.060;
bloom += LoadEmissiveRadianceClamped(p + int2( 0, -8)) * 0.060;
bloom += LoadEmissiveRadianceClamped(p + int2( 14, 0)) * 0.035;
bloom += LoadEmissiveRadianceClamped(p + int2(-14, 0)) * 0.035;
bloom += LoadEmissiveRadianceClamped(p + int2( 0, 14)) * 0.035;
bloom += LoadEmissiveRadianceClamped(p + int2( 0, -14)) * 0.035;
return bloom * 0.78;
}
float3 SafeNormalizeOr(float3 v, float3 fallback)
{
float lenSq = dot(v, v);
@@ -3717,7 +3778,8 @@ float EstimateVolumeDensityFromLight(Light Lgt)
return clamp(2.25 / max(range, 32.0), 0.0015, 0.035);
}
)"
R"(
float3 EstimateSingleLightVolumetricScattering(uint2 pixel, float3 cameraPos, float3 worldPos, Light Lgt, inout uint rng)
{
if (Lgt.volumetricScattering <= 0.0)
@@ -3927,8 +3989,8 @@ float3 EstimateDirectLightingForBounceHit(uint2 hitPixel, float3 hitPos, float3
hitV = SafeNormalizeOr(hitV, -hitN);
hitAlbedo = saturate(hitAlbedo);
// Treat unlit G-buffer surfaces as simple emissive bounce cards. Clamp so a
// white UI/light-card cannot become an uncontrolled firefly in the GI path.
// Treat unlit G-buffer surfaces as simple bounce cards. Glow-map emissive is
// intentionally excluded here so visible emissive no longer casts GI/light.
if (hitIsUnlit)
return clamp(hitAlbedo * 2.0 + GetSkyRadiance(hitN) * 0.04, 0.0, 6.0);
@@ -3939,6 +4001,10 @@ float3 EstimateDirectLightingForBounceHit(uint2 hitPixel, float3 hitPos, float3
// used a fixed sky term, so corners/cavities received too much indirect light.
lighting += EstimateBounceSkyLighting(hitPos, hitN, rng) * (0.14 + 0.10 * upness);
// Glow-map emissive no longer participates in secondary GI. It remains a
// direct visible/bloom-only effect until real material-space emissive lighting
// is implemented.
// Baseline: every light contributes to bounced radiance, so small dynamic
// lights do not vanish just because they were not chosen by the stochastic
// next-event-estimation budget.
@@ -4299,6 +4365,9 @@ float3 PathTraceDeterministicLighting(
lightingAccum += skyColor * (0.70 * skyVis);
lightingAccum += ambientSkyVis * (skyColorRGB * 0.15);
// Glow-map emissive is direct/bloom-only for now. It is added after lighting
// in RayGen and is intentionally not injected into lightingAccum.
//if (isSkeletal)
// lightingAccum += 0.1;
@@ -4344,11 +4413,15 @@ void RayGen()
return;
float4 albedoSample = gAlbedoTex.Load(int3(pixel, 0));
float4 emissiveSample = gEmissiveTex.Load(int3(pixel, 0));
float3 emissiveSurface = CompressEmissiveRadiance(emissiveSample.rgb, 6.50);
float depthSample = gDepthTex.Load(int3(pixel, 0));
float3 emissiveBloom = EstimateEmissiveBloomAtPixel(pixel);
if (depthSample <= 0.0 || depthSample >= 1.0)
{
gOutputTex[pixel] = albedoSample;
gOutputTex[pixel] = float4(albedoSample.rgb + emissiveSurface + emissiveBloom, albedoSample.a);
return;
}
@@ -4365,7 +4438,7 @@ void RayGen()
if (isUnlit)
{
gOutputTex[pixel] = float4(baseAlbedo, albedoSample.a);
gOutputTex[pixel] = float4(baseAlbedo + emissiveSurface + emissiveBloom, albedoSample.a);
return;
}
@@ -4454,6 +4527,12 @@ void RayGen()
uint volumeRng = InitRng(pixel, 0u, 0x51u);
finalColor += EstimatePathTracedVolumetricScattering(pixel, worldPos, volumeRng);
// Glow-map emission is direct radiance from the primary surface. It is added
// after lighting so it remains visible in darkness and under ray-traced shadows.
// emissiveBloom is the threshold-free halo, so emissive always blooms even if
// the eventual swap-chain/backbuffer is LDR.
finalColor += emissiveSurface + emissiveBloom;
gOutputTex[pixel] = float4(max(finalColor, 0.0), albedoSample.a);
}
)";
@@ -4623,7 +4702,10 @@ void DenoiseCS(uint3 dispatchThreadId : SV_DispatchThreadID)
if (depthSample <= 0.0 || depthSample >= 1.0)
{
StoreDenoiseOutput(pixel, albedoSample);
// Preserve raygen's emissive bloom on background/no-depth pixels.
// Returning albedo here would erase the halo whenever the internal
// temporal/a-trous denoiser is active.
StoreDenoiseOutput(pixel, centerSource);
return;
}
@@ -4867,7 +4949,9 @@ static ComPtr<IDxcBlob> glRaytracingLightingCompileLibrary(const char* src)
L"-Zi",
L"-Qembed_debug",
#endif
L"-O3",
// Keep the DXR library smaller to avoid long driver-side linking during
// CreateStateObject(). Compute/post shaders below still compile with O3.
L"-O1",
L"-all_resources_bound"
};
@@ -5662,6 +5746,14 @@ static void glRaytracingLightingCreatePerPassDescriptors(
g_glRaytracingCmd.device->CreateShaderResourceView(temporalTexture, &denoiseSrv,
glRaytracingOffsetCpu(base, g_glRaytracingLighting.descriptorStride, GLR_DESC_TEMPORAL_SRV));
D3D12_SHADER_RESOURCE_VIEW_DESC emissiveSrv = {};
emissiveSrv.Shader4ComponentMapping = D3D12_DEFAULT_SHADER_4_COMPONENT_MAPPING;
emissiveSrv.ViewDimension = D3D12_SRV_DIMENSION_TEXTURE2D;
emissiveSrv.Format = g_glRaytracingLighting.emissiveFormat;
emissiveSrv.Texture2D.MipLevels = 1;
g_glRaytracingCmd.device->CreateShaderResourceView(g_glRaytracingLighting.emissiveTexture, &emissiveSrv,
glRaytracingOffsetCpu(base, g_glRaytracingLighting.descriptorStride, GLR_DESC_EMISSIVE_SRV));
D3D12_UNORDERED_ACCESS_VIEW_DESC rayOutputUav = {};
rayOutputUav.ViewDimension = D3D12_UAV_DIMENSION_TEXTURE2D;
rayOutputUav.Format = GL_RAYTRACING_DENOISE_FORMAT;
@@ -6299,6 +6391,17 @@ void glRaytracingLightingUseExternalDenoiser(int enabled)
glRaytracingLightingSetExternalDenoiser(enabled);
}
void glRaytracingLightingSetEmissiveInput(ID3D12Resource* texture, DXGI_FORMAT format)
{
std::lock_guard<std::mutex> lock(g_glRaytracingMutex);
g_glRaytracingLighting.emissiveTexture = texture;
g_glRaytracingLighting.emissiveFormat = (format == DXGI_FORMAT_UNKNOWN)
? DXGI_FORMAT_R16G16B16A16_FLOAT
: format;
}
bool glRaytracingLightingExecuteForScene(const glRaytracingLightingPassDesc_t* pass, glRaytracingSceneHandle_t worldHandle)
{
std::lock_guard<std::mutex> lock(g_glRaytracingMutex);
+435 -38
View File
@@ -129,6 +129,9 @@ using Microsoft::WRL::ComPtr;
#ifndef GL_NORMAL_MAP_BINDING_QD3D12
#define GL_NORMAL_MAP_BINDING_QD3D12 0x6002
#endif
#ifndef GL_GLOW_MAP_BINDING_QD3D12
#define GL_GLOW_MAP_BINDING_QD3D12 0x6005
#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.
@@ -186,6 +189,11 @@ void APIENTRY glBinormal3f(GLfloat x, GLfloat y, GLfloat z);
void APIENTRY glBinormal3fv(const GLfloat* v);
void APIENTRY glGlassMaterialQD3D12(GLboolean enable);
void APIENTRY glMaterialGlassQD3D12(GLboolean enable);
void APIENTRY glTagTextureGlowMap(GLuint texture, GLboolean isGlowMap);
void APIENTRY glTextureGlowMap(GLuint texture, GLboolean isGlowMap);
void APIENTRY glBindGlowMapTexture(GLuint texture);
void APIENTRY glGlowMapTexture(GLuint texture);
void APIENTRY glGlowMapStrengthf(GLfloat strength);
void APIENTRY glRaytracingMaterialFlagsQD3D12(GLuint flags);
void APIENTRY glRaytracingMaterialFlagQD3D12(GLuint flag, GLboolean enable);
@@ -195,6 +203,7 @@ void APIENTRY glRaytracingMaterialFlagQD3D12(GLuint flag, GLboolean enable);
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 glRaytracingSetMeshMaterialFlags(glRaytracingMeshHandle_t meshHandle, uint32_t materialFlags);
void glRaytracingSetMeshGlass(glRaytracingMeshHandle_t meshHandle, int isGlass);
uint32_t glRaytracingGetMeshMaterialFlags(glRaytracingMeshHandle_t meshHandle);
@@ -312,15 +321,18 @@ enum QD3D12RTVSlotGroup
QD3D12_RTV_NORMAL_RENDER = 1,
QD3D12_RTV_POSITION_RENDER = 2,
QD3D12_RTV_VELOCITY_RENDER = 3,
QD3D12_RTV_SCENE_RESOLVED = 4,
QD3D12_RTV_NORMAL_RESOLVED = 5,
QD3D12_RTV_POSITION_RESOLVED = 6,
QD3D12_RTV_VELOCITY_RESOLVED = 7,
QD3D12_RTV_BACKBUFFER = 8,
QD3D12_RTV_GROUP_COUNT = 9
QD3D12_RTV_EMISSIVE_RENDER = 4,
QD3D12_RTV_SCENE_RESOLVED = 5,
QD3D12_RTV_NORMAL_RESOLVED = 6,
QD3D12_RTV_POSITION_RESOLVED = 7,
QD3D12_RTV_VELOCITY_RESOLVED = 8,
QD3D12_RTV_EMISSIVE_RESOLVED = 9,
QD3D12_RTV_BACKBUFFER = 10,
QD3D12_RTV_GROUP_COUNT = 11
};
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_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;
@@ -444,11 +456,12 @@ struct TextureResource
bool gpuValid = false;
D3D12_RESOURCE_STATES state = D3D12_RESOURCE_STATE_COPY_DEST;
// Optional material role tag. Tagged textures are not treated as diffuse
// inputs by default; they are selected as tangent-space normal maps for the
// fixed-function G-buffer path when bound through glNormalMapTexture() or any
// active texture unit.
// Optional material role tags. Tagged textures are not treated as diffuse
// inputs by default; they are selected as tangent-space normal/glow maps for
// the fixed-function G-buffer path when explicitly bound or auto-discovered
// from any active texture unit.
bool isNormalMap = false;
bool isGlowMap = false;
};
static void QD3D12_ShutdownMipWorkers();
@@ -523,7 +536,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_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_glass_material GL_QD3D12_volumetric_light";
enum TexEnvModeShader
{
@@ -568,6 +581,9 @@ struct BatchKey
float useNormalMap = 0.0f;
float normalMapStrength = 1.0f;
float normalMapYSign = 1.0f;
UINT glowMapSrvIndex = 0;
float useGlowMap = 0.0f;
float glowMapStrength = 1.0f;
bool useARBPrograms = false;
GLuint arbVertexProgram = 0;
@@ -676,6 +692,9 @@ static bool BatchKeyEquals(const BatchKey& a, const BatchKey& b)
a.useNormalMap == b.useNormalMap &&
a.normalMapStrength == b.normalMapStrength &&
a.normalMapYSign == b.normalMapYSign &&
a.glowMapSrvIndex == b.glowMapSrvIndex &&
a.useGlowMap == b.useGlowMap &&
a.glowMapStrength == b.glowMapStrength &&
a.alphaRef == b.alphaRef &&
a.alphaFunc == b.alphaFunc &&
a.useTex0 == b.useTex0 &&
@@ -779,6 +798,9 @@ struct QD3D12Window
std::array<ComPtr<ID3D12Resource>, QD3D12_FrameCount> velocityBuffers;
D3D12_RESOURCE_STATES velocityBufferState[QD3D12_FrameCount] = {};
std::array<ComPtr<ID3D12Resource>, QD3D12_FrameCount> emissiveBuffers;
D3D12_RESOURCE_STATES emissiveBufferState[QD3D12_FrameCount] = {};
std::array<ComPtr<ID3D12Resource>, QD3D12_FrameCount> sceneColorMsaaBuffers;
D3D12_RESOURCE_STATES sceneColorMsaaState[QD3D12_FrameCount] = {};
@@ -791,6 +813,9 @@ struct QD3D12Window
std::array<ComPtr<ID3D12Resource>, QD3D12_FrameCount> velocityMsaaBuffers;
D3D12_RESOURCE_STATES velocityMsaaState[QD3D12_FrameCount] = {};
std::array<ComPtr<ID3D12Resource>, QD3D12_FrameCount> emissiveMsaaBuffers;
D3D12_RESOURCE_STATES emissiveMsaaState[QD3D12_FrameCount] = {};
std::array<ComPtr<ID3D12Resource>, QD3D12_FrameCount> backBuffers;
D3D12_RESOURCE_STATES backBufferState[QD3D12_FrameCount] = {};
@@ -824,6 +849,10 @@ struct QD3D12Window
D3D12_CPU_DESCRIPTOR_HANDLE velocitySrvCpu[QD3D12_FrameCount]{};
D3D12_GPU_DESCRIPTOR_HANDLE velocitySrvGpu[QD3D12_FrameCount]{};
UINT emissiveSrvIndex[QD3D12_FrameCount] = { UINT_MAX, UINT_MAX };
D3D12_CPU_DESCRIPTOR_HANDLE emissiveSrvCpu[QD3D12_FrameCount]{};
D3D12_GPU_DESCRIPTOR_HANDLE emissiveSrvGpu[QD3D12_FrameCount]{};
UINT slOutputSrvIndex[QD3D12_FrameCount] = { UINT_MAX, UINT_MAX };
D3D12_CPU_DESCRIPTOR_HANDLE slOutputSrvCpu[QD3D12_FrameCount]{};
D3D12_GPU_DESCRIPTOR_HANDLE slOutputSrvGpu[QD3D12_FrameCount]{};
@@ -848,6 +877,10 @@ struct QD3D12Window
D3D12_CPU_DESCRIPTOR_HANDLE velocityMsaaSrvCpu[QD3D12_FrameCount]{};
D3D12_GPU_DESCRIPTOR_HANDLE velocityMsaaSrvGpu[QD3D12_FrameCount]{};
UINT emissiveMsaaSrvIndex[QD3D12_FrameCount] = { UINT_MAX, UINT_MAX };
D3D12_CPU_DESCRIPTOR_HANDLE emissiveMsaaSrvCpu[QD3D12_FrameCount]{};
D3D12_GPU_DESCRIPTOR_HANDLE emissiveMsaaSrvGpu[QD3D12_FrameCount]{};
D3D12_VIEWPORT viewport{};
D3D12_RECT scissor{};
@@ -1119,6 +1152,8 @@ struct GLState
GLuint currentNormalMapTexture = 0;
float currentNormalMapStrength = 1.0f;
float currentNormalMapYSign = 1.0f;
GLuint currentGlowMapTexture = 0;
float currentGlowMapStrength = 1.0f;
ImmediateVertexBuffer immediateVerts;
GLenum matrixMode = GL_MODELVIEW;
@@ -1703,6 +1738,12 @@ static D3D12_CPU_DESCRIPTOR_HANDLE CurrentVelocityRTV()
return QD3D12_RtvAt(w, QD3D12_RTV_VELOCITY_RENDER, w.frameIndex);
}
static D3D12_CPU_DESCRIPTOR_HANDLE CurrentEmissiveRTV()
{
QD3D12Window& w = *g_currentWindow;
return QD3D12_RtvAt(w, QD3D12_RTV_EMISSIVE_RENDER, w.frameIndex);
}
static D3D12_CPU_DESCRIPTOR_HANDLE CurrentResolvedSceneColorRTV()
{
QD3D12Window& w = *g_currentWindow;
@@ -1727,6 +1768,12 @@ static D3D12_CPU_DESCRIPTOR_HANDLE CurrentResolvedVelocityRTV()
return QD3D12_RtvAt(w, QD3D12_RTV_VELOCITY_RESOLVED, w.frameIndex);
}
static D3D12_CPU_DESCRIPTOR_HANDLE CurrentResolvedEmissiveRTV()
{
QD3D12Window& w = *g_currentWindow;
return QD3D12_RtvAt(w, QD3D12_RTV_EMISSIVE_RESOLVED, w.frameIndex);
}
static D3D12_CPU_DESCRIPTOR_HANDLE CurrentBackBufferRTV()
{
QD3D12Window& w = *g_currentWindow;
@@ -1815,6 +1862,12 @@ static bool QD3D12_IsTextureTaggedNormalMap(GLuint id)
return tex && tex->isNormalMap;
}
static bool QD3D12_IsTextureTaggedGlowMap(GLuint id)
{
TextureResource* tex = QD3D12_FindTextureResource(id);
return tex && tex->isGlowMap;
}
static TextureResource* QD3D12_SelectNormalMapTexture(TextureResource* const* allTextures)
{
// Explicit material binding wins. This lets callers bind a normal map even
@@ -1847,6 +1900,45 @@ static TextureResource* QD3D12_SelectNormalMapTexture(TextureResource* const* al
return nullptr;
}
static TextureResource* QD3D12_SelectGlowMapTexture(TextureResource* const* allTextures)
{
// Explicit material binding wins. Tagging enables automatic discovery from
// arbitrary texture units without consuming the diffuse/lightmap slots.
if (g_gl.currentGlowMapTexture != 0)
{
TextureResource* explicitGlow = QD3D12_FindTextureResource(g_gl.currentGlowMapTexture);
if (explicitGlow)
{
// Common integration mistake: passing shader->GetBumpImage()->texnum
// to glBindGlowMapTexture(). Never treat a texture explicitly tagged
// as a normal map as emissive unless it was also explicitly tagged glow.
if (explicitGlow->isNormalMap && !explicitGlow->isGlowMap)
return nullptr;
return explicitGlow;
}
}
if (allTextures)
{
for (UINT i = 0; i < QD3D12_MaxTextureUnits; ++i)
{
TextureResource* tex = allTextures[i];
if (tex && tex != &g_gl.whiteTexture && tex->isGlowMap)
return tex;
}
}
for (UINT i = 0; i < QD3D12_MaxTextureUnits; ++i)
{
TextureResource* tex = QD3D12_FindTextureResource(g_gl.boundTexture[i]);
if (tex && tex->isGlowMap)
return tex;
}
return nullptr;
}
static const uint8_t* QD3D12_ResolveArrayPointer(const void* ptr)
{
if (g_gl.boundArrayBuffer != 0)
@@ -2005,14 +2097,18 @@ cbuffer DrawCB : register(b0)
#define gUseNormalMap gMotionPad.x
#define gNormalMapStrength gMotionPad.y
#define gNormalMapYSign gMotionPad.z
#define gParallaxScale gMotionPad.w
#define gParallaxScale 0.0
#define gUseGlowMap gMotionPad.w
#define gGlowMapStrength gMotionPad.w
Texture2D gTex0 : register(t0);
Texture2D gTex1 : register(t1);
Texture2D gNormalMap : register(t2);
Texture2D gGlowMap : register(t3);
SamplerState gSamp0 : register(s0);
SamplerState gSamp1 : register(s1);
SamplerState gSamp2 : register(s2);
SamplerState gSamp3 : register(s3);
struct VSIn
{
@@ -2048,6 +2144,7 @@ struct PSOut
float4 normal : SV_Target1;
float4 position : SV_Target2;
float4 velocity : SV_Target3;
float4 emissive : SV_Target4;
};
float4 QD3D12_GetTexEnvSource(float source, float4 texel, float4 primary, float4 previous, float4 constantColor)
@@ -2306,21 +2403,61 @@ float3 BuildGBufferNormal(VSOut i)
n);
}
float2 QD3D12_BuildMaterialUV0(VSOut i)
{
float2 uv0 = i.uv0;
float n = TinyNoise(int2(i.pos.xy)) * 0.0005;
uv0 += float2(n, -n);
if (gUseNormalMap > 0.5)
uv0 = QD3D12_ComputeParallaxUV(i, uv0);
return uv0;
}
float4 QD3D12_SampleGlow(VSOut i)
{
if (gUseGlowMap <= 0.0)
return float4(0.0, 0.0, 0.0, 0.0);
float4 glow = gGlowMap.Sample(gSamp3, QD3D12_BuildMaterialUV0(i));
// RGB-only glow maps are uploaded with an opaque alpha channel. Treat an
// all-white/forced alpha as "no alpha mask" and derive coverage from RGB;
// otherwise black areas of an RGB glow texture make the base material vanish.
float rgbMask = saturate(max(max(glow.r, glow.g), glow.b));
bool alphaLooksForcedOpaque = (glow.a >= 0.999);
float glowMask = alphaLooksForcedOpaque ? rgbMask : saturate(max(glow.a, rgbMask));
float emissionMask = alphaLooksForcedOpaque ? 1.0 : glowMask;
// Store bright HDR radiance in the FP16 emissive G-buffer. This is still
// direct/bloom-only; emissive lighting has intentionally stayed disabled.
// The higher baseline lets normal idTech glow maps read as obvious emission
// without every material having to call glGlowMapStrengthf(3+).
const float kDefaultGlowBloomRadiance = 3.25;
float strength = max(gGlowMapStrength, 0.0);
// Low-value glow maps looked too weak after the no-light hotfix. Give the
// emissive RGB a mild artist-friendly lift while preserving black texels.
float3 glowRgb = max(glow.rgb, 0.0);
float3 glowLift = sqrt(saturate(glowRgb));
float3 emissionColor = max(glowRgb, glowLift * 0.55);
float3 emission = emissionColor * emissionMask * strength * kDefaultGlowBloomRadiance;
return float4(emission, glowMask);
}
float4 BuildTexturedColor(VSOut i)
{
float4 primary = i.col;
float4 outColor = primary;
float2 uv0 = i.uv0;
float2 uv0 = QD3D12_BuildMaterialUV0(i);
float2 uv1 = i.uv1;
float n = TinyNoise(int2(i.pos.xy)) * 0.0005;
uv0 += float2(n, -n);
uv1 += float2(-n, n);
if (gUseNormalMap > 0.5)
uv0 = QD3D12_ComputeParallaxUV(i, uv0);
if (gUseTex0 > 0.5)
{
float4 tex0 = gTex0.Sample(gSamp0, uv0);
@@ -2335,12 +2472,23 @@ float4 BuildTexturedColor(VSOut i)
gTexComb1RGB, gTexComb1Alpha, gTexComb1Operand, gTexEnvColor1);
}
if (gUseGlowMap > 0.0)
{
float glowMask = saturate(QD3D12_SampleGlow(i).a);
outColor.rgb *= (1.0 - glowMask);
}
// outColor.xyz = ApplySoftwareRendererLook(outColor.xyz);
outColor = ApplyFog(outColor, i.fogCoord);
return outColor;
}
float4 BuildGlowEmission(VSOut i)
{
return QD3D12_SampleGlow(i);
}
float2 ClipToUv(float4 clipPos)
{
float2 ndc = clipPos.xy / max(abs(clipPos.w), 1e-6);
@@ -2397,6 +2545,7 @@ PSOut PSMain(VSOut i)
o.normal = float4(BuildGBufferNormal(i), i.attr.y);
o.position = float4(i.worldPos, i.attr.x);
o.velocity = BuildVelocity(i);
o.emissive = BuildGlowEmission(i);
return o;
}
@@ -2408,6 +2557,7 @@ PSOut PSMainAlphaTest(VSOut i)
o.normal = float4(BuildGBufferNormal(i), i.attr.y);
o.position = float4(i.worldPos, i.attr.x);
o.velocity = BuildVelocity(i);
o.emissive = BuildGlowEmission(i);
return o;
}
@@ -2418,18 +2568,22 @@ PSOut PSMainUntextured(VSOut i)
o.normal = float4(BuildGBufferNormal(i), i.attr.y);
o.position = float4(i.worldPos, i.attr.x);
o.velocity = BuildVelocity(i);
o.emissive = float4(0.0, 0.0, 0.0, 0.0);
return o;
}
float4 PSMainColorOnly(VSOut i) : SV_Target0
{
return BuildTexturedColor(i);
float4 c = BuildTexturedColor(i);
c.rgb += BuildGlowEmission(i).rgb;
return c;
}
float4 PSMainAlphaTestColorOnly(VSOut i) : SV_Target0
{
float4 c = BuildTexturedColor(i);
QD3D12_AlphaTest(c.a);
c.rgb += BuildGlowEmission(i).rgb;
return c;
}
@@ -2445,6 +2599,7 @@ Texture2DMS<float> gDepthMS : register(t1);
Texture2DMS<float4> gNormalMS : register(t2);
Texture2DMS<float4> gPositionMS : register(t3);
Texture2DMS<float4> gVelocityMS : register(t4);
Texture2DMS<float4> gEmissiveMS : register(t5);
SamplerState gSamp0 : register(s0);
struct VSOut
@@ -2471,9 +2626,61 @@ float4 PSCopy(VSOut i) : SV_Target0
return gTex0.Sample(gSamp0, i.uv);
}
float3 QD3D12_LoadPostRadiance(float2 uv)
{
// Clamp bloom taps so a single bad HDR edge sample cannot flood the LDR
// scene-color composite. The direct center emissive value is still added by
// PSAdd below, so this only limits the halo.
return min(max(gTex0.Sample(gSamp0, saturate(uv)).rgb, 0.0), 12.0);
}
float3 QD3D12_EmissiveBloom(float2 uv)
{
uint srcW, srcH;
gTex0.GetDimensions(srcW, srcH);
float2 texel = 1.0 / max(float2((float)srcW, (float)srcH), float2(1.0, 1.0));
// Strong threshold-free bloom kernel. Direct emissive is still added by
// PSAdd; this wider halo makes glow maps read even when the final color
// buffer is LDR and clamps the swap-chain output.
float3 bloom = 0.0;
bloom += QD3D12_LoadPostRadiance(uv) * 0.180;
bloom += QD3D12_LoadPostRadiance(uv + texel * float2( 1.0, 0.0)) * 0.220;
bloom += QD3D12_LoadPostRadiance(uv + texel * float2(-1.0, 0.0)) * 0.220;
bloom += QD3D12_LoadPostRadiance(uv + texel * float2( 0.0, 1.0)) * 0.220;
bloom += QD3D12_LoadPostRadiance(uv + texel * float2( 0.0, -1.0)) * 0.220;
bloom += QD3D12_LoadPostRadiance(uv + texel * float2( 2.0, 2.0)) * 0.135;
bloom += QD3D12_LoadPostRadiance(uv + texel * float2(-2.0, 2.0)) * 0.135;
bloom += QD3D12_LoadPostRadiance(uv + texel * float2( 2.0, -2.0)) * 0.135;
bloom += QD3D12_LoadPostRadiance(uv + texel * float2(-2.0, -2.0)) * 0.135;
bloom += QD3D12_LoadPostRadiance(uv + texel * float2( 4.0, 0.0)) * 0.090;
bloom += QD3D12_LoadPostRadiance(uv + texel * float2(-4.0, 0.0)) * 0.090;
bloom += QD3D12_LoadPostRadiance(uv + texel * float2( 0.0, 4.0)) * 0.090;
bloom += QD3D12_LoadPostRadiance(uv + texel * float2( 0.0, -4.0)) * 0.090;
bloom += QD3D12_LoadPostRadiance(uv + texel * float2( 8.0, 0.0)) * 0.060;
bloom += QD3D12_LoadPostRadiance(uv + texel * float2(-8.0, 0.0)) * 0.060;
bloom += QD3D12_LoadPostRadiance(uv + texel * float2( 0.0, 8.0)) * 0.060;
bloom += QD3D12_LoadPostRadiance(uv + texel * float2( 0.0, -8.0)) * 0.060;
bloom += QD3D12_LoadPostRadiance(uv + texel * float2( 14.0, 0.0)) * 0.035;
bloom += QD3D12_LoadPostRadiance(uv + texel * float2(-14.0, 0.0)) * 0.035;
bloom += QD3D12_LoadPostRadiance(uv + texel * float2( 0.0, 14.0)) * 0.035;
bloom += QD3D12_LoadPostRadiance(uv + texel * float2( 0.0, -14.0)) * 0.035;
return min(bloom * 0.62, 5.0);
}
float4 PSAdd(VSOut i) : SV_Target0
{
return gTex0.Sample(gSamp0, i.uv);
float4 center = gTex0.Sample(gSamp0, i.uv);
float3 direct = max(center.rgb, 0.0);
float3 bloom = QD3D12_EmissiveBloom(i.uv);
return float4(direct + bloom, saturate(center.a));
}
float PSDepthCopy(VSOut i) : SV_Depth
@@ -2531,6 +2738,7 @@ struct PSGBufferPointResolveOut
float4 normal : SV_Target0;
float4 position : SV_Target1;
float4 velocity : SV_Target2;
float4 emissive : SV_Target3;
};
PSGBufferPointResolveOut PSGBufferPointResolveMS(VSOut i)
@@ -2553,6 +2761,7 @@ PSGBufferPointResolveOut PSGBufferPointResolveMS(VSOut i)
o.normal = gNormalMS.Load(int2(srcPixel), sampleIndex);
o.position = gPositionMS.Load(int2(srcPixel), sampleIndex);
o.velocity = gVelocityMS.Load(int2(srcPixel), sampleIndex);
o.emissive = gEmissiveMS.Load(int2(srcPixel), sampleIndex);
return o;
}
@@ -2971,6 +3180,19 @@ static BatchKey BuildCurrentBatchKey(GLenum originalMode, const TextureResource*
key.normalMapStrength = g_gl.currentNormalMapStrength;
key.normalMapYSign = g_gl.currentNormalMapYSign;
TextureResource* glowMap = QD3D12_SelectGlowMapTexture(allTextures);
if (glowMap && glowMap->texture && glowMap->srvIndex != UINT_MAX)
{
key.glowMapSrvIndex = glowMap->srvIndex;
key.useGlowMap = 1.0f;
}
else
{
key.glowMapSrvIndex = g_gl.whiteTexture.srvIndex;
key.useGlowMap = 0.0f;
}
key.glowMapStrength = g_gl.currentGlowMapStrength;
key.useARBPrograms = QD3D12ARB_IsActive();
if (key.useARBPrograms)
{
@@ -4105,6 +4327,39 @@ static void QD3D12_PostFullscreenPass(ID3D12GraphicsCommandList* cl,
cl->DrawInstanced(3, 1, 0, 0);
}
static void QD3D12_CompositeEmissiveIntoSceneColor(QD3D12Window& w)
{
ID3D12GraphicsCommandList* cl = g_gl.cmdList.Get();
const UINT frame = w.frameIndex;
if (!cl || !g_gl.postAdditivePSO || !w.sceneColorBuffers[frame] || !w.emissiveBuffers[frame])
return;
QD3D12_TransitionResource(cl, w.sceneColorBuffers[frame].Get(), w.sceneColorState[frame], D3D12_RESOURCE_STATE_RENDER_TARGET);
QD3D12_TransitionResource(cl, w.emissiveBuffers[frame].Get(), w.emissiveBufferState[frame], D3D12_RESOURCE_STATE_PIXEL_SHADER_RESOURCE);
D3D12_VIEWPORT viewport{};
viewport.TopLeftX = 0.0f;
viewport.TopLeftY = 0.0f;
viewport.Width = (float)w.renderWidth;
viewport.Height = (float)w.renderHeight;
viewport.MinDepth = 0.0f;
viewport.MaxDepth = 1.0f;
D3D12_RECT scissor{};
scissor.left = 0;
scissor.top = 0;
scissor.right = (LONG)w.renderWidth;
scissor.bottom = (LONG)w.renderHeight;
QD3D12_PostFullscreenPass(
cl,
g_gl.postAdditivePSO.Get(),
w.emissiveSrvGpu[frame],
CurrentResolvedSceneColorRTV(),
viewport,
scissor);
}
static ID3D12Resource* QD3D12_PrepareStreamlineOutputForWrite(QD3D12Window& w)
{
ID3D12GraphicsCommandList* cl = g_gl.cmdList.Get();
@@ -4773,6 +5028,25 @@ static void QD3D12_CreateRTVsForWindow(QD3D12Window& w)
}
}
for (UINT i = 0; i < QD3D12_FrameCount; ++i)
{
if (useMsaa)
{
CreateTexture2D(w.emissiveMsaaBuffers[i], w.emissiveMsaaState[i], QD3D12_EmissiveFormat, velocityClear, true,
w.renderWidth, w.renderHeight, msaaSamples, D3D12_RESOURCE_FLAG_ALLOW_RENDER_TARGET, D3D12_RESOURCE_STATE_RENDER_TARGET,
QD3D12_RtvAt(w, QD3D12_RTV_EMISSIVE_RENDER, i), true);
CreateTexture2D(w.emissiveBuffers[i], w.emissiveBufferState[i], QD3D12_EmissiveFormat, velocityClear, false,
w.renderWidth, w.renderHeight, 1, D3D12_RESOURCE_FLAG_ALLOW_RENDER_TARGET, D3D12_RESOURCE_STATE_PIXEL_SHADER_RESOURCE,
QD3D12_RtvAt(w, QD3D12_RTV_EMISSIVE_RESOLVED, i), true);
}
else
{
CreateTexture2D(w.emissiveBuffers[i], w.emissiveBufferState[i], QD3D12_EmissiveFormat, velocityClear, true,
w.renderWidth, w.renderHeight, 1, D3D12_RESOURCE_FLAG_ALLOW_RENDER_TARGET, D3D12_RESOURCE_STATE_RENDER_TARGET,
QD3D12_RtvAt(w, QD3D12_RTV_EMISSIVE_RENDER, i), true);
}
}
for (UINT i = 0; i < QD3D12_FrameCount; ++i)
{
D3D12_CPU_DESCRIPTOR_HANDLE backBufferRtv = QD3D12_RtvAt(w, QD3D12_RTV_BACKBUFFER, i);
@@ -4862,6 +5136,9 @@ static void QD3D12_CreateRTVsForWindow(QD3D12Window& w)
for (UINT i = 0; i < QD3D12_FrameCount; ++i)
CreateTextureSrv(w.velocityBuffers[i].Get(), QD3D12_VelocityFormat, w.velocitySrvIndex[i], w.velocitySrvCpu[i], w.velocitySrvGpu[i]);
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.slOutputBuffers[i].Get(), QD3D12_StreamlineOutputFormat, w.slOutputSrvIndex[i], w.slOutputSrvCpu[i], w.slOutputSrvGpu[i]);
@@ -4875,6 +5152,9 @@ static void QD3D12_CreateRTVsForWindow(QD3D12Window& w)
for (UINT i = 0; i < QD3D12_FrameCount; ++i)
CreateTextureMsaaSrv(w.velocityMsaaBuffers[i].Get(), QD3D12_VelocityFormat, w.velocityMsaaSrvIndex[i], w.velocityMsaaSrvCpu[i], w.velocityMsaaSrvGpu[i]);
for (UINT i = 0; i < QD3D12_FrameCount; ++i)
CreateTextureMsaaSrv(w.emissiveMsaaBuffers[i].Get(), QD3D12_EmissiveFormat, w.emissiveMsaaSrvIndex[i], w.emissiveMsaaSrvCpu[i], w.emissiveMsaaSrvGpu[i]);
}
}
@@ -5196,7 +5476,7 @@ static ComPtr<ID3DBlob> CompileShaderVariant(const char* entry, const char* targ
static void QD3D12_CreatePostRootSignature()
{
D3D12_DESCRIPTOR_RANGE ranges[5]{};
D3D12_DESCRIPTOR_RANGE ranges[6]{};
for (UINT i = 0; i < _countof(ranges); ++i)
{
ranges[i].RangeType = D3D12_DESCRIPTOR_RANGE_TYPE_SRV;
@@ -5206,7 +5486,7 @@ static void QD3D12_CreatePostRootSignature()
ranges[i].OffsetInDescriptorsFromTableStart = 0;
}
D3D12_ROOT_PARAMETER params[5]{};
D3D12_ROOT_PARAMETER params[6]{};
for (UINT i = 0; i < _countof(params); ++i)
{
params[i].ParameterType = D3D12_ROOT_PARAMETER_TYPE_DESCRIPTOR_TABLE;
@@ -5363,13 +5643,14 @@ static D3D12_GRAPHICS_PIPELINE_STATE_DESC BuildPSODesc(
d.PrimitiveTopologyType = topoType;
d.NumRenderTargets = nativeColorOnly ? 1u : 4u;
d.NumRenderTargets = nativeColorOnly ? 1u : 5u;
d.RTVFormats[0] = DXGI_FORMAT_R8G8B8A8_UNORM;
if (!nativeColorOnly)
{
d.RTVFormats[1] = DXGI_FORMAT_R16G16B16A16_FLOAT;
d.RTVFormats[2] = DXGI_FORMAT_R16G16B16A16_FLOAT;
d.RTVFormats[3] = QD3D12_VelocityFormat;
d.RTVFormats[4] = QD3D12_EmissiveFormat;
}
d.DSVFormat = QD3D12_DepthDsvFormat;
@@ -5387,12 +5668,13 @@ static D3D12_GRAPHICS_PIPELINE_STATE_DESC BuildPSODesc(
d.BlendState.RenderTarget[1].RenderTargetWriteMask = key.colorWriteMask ? D3D12_COLOR_WRITE_ENABLE_ALL : 0;
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.AlphaToCoverageEnable = FALSE;
d.BlendState.IndependentBlendEnable = nativeColorOnly ? FALSE : TRUE;
for (int i = 0; i < 4; ++i)
for (int i = 0; i < (nativeColorOnly ? 1 : 5); ++i)
{
auto& rt = d.BlendState.RenderTarget[i];
rt.BlendEnable = FALSE;
@@ -5500,16 +5782,17 @@ static void QD3D12_CreatePSOs()
gbufferResolveDesc.PrimitiveTopologyType = D3D12_PRIMITIVE_TOPOLOGY_TYPE_TRIANGLE;
gbufferResolveDesc.SampleDesc.Count = 1;
gbufferResolveDesc.SampleMask = UINT_MAX;
gbufferResolveDesc.NumRenderTargets = 3;
gbufferResolveDesc.NumRenderTargets = 4;
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.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 < 3; ++rt)
for (UINT rt = 0; rt < 4; ++rt)
{
gbufferResolveDesc.BlendState.RenderTarget[rt].BlendEnable = FALSE;
gbufferResolveDesc.BlendState.RenderTarget[rt].LogicOpEnable = FALSE;
@@ -5628,13 +5911,14 @@ static D3D12_GRAPHICS_PIPELINE_STATE_DESC BuildARBPSODesc(
d.InputLayout.NumElements = _countof(kGLVertexInputLayoutARB);
d.PrimitiveTopologyType = topoType;
d.NumRenderTargets = nativeColorOnly ? 1u : 4u;
d.NumRenderTargets = nativeColorOnly ? 1u : 5u;
d.RTVFormats[0] = DXGI_FORMAT_R8G8B8A8_UNORM;
if (!nativeColorOnly)
{
d.RTVFormats[1] = DXGI_FORMAT_R16G16B16A16_FLOAT;
d.RTVFormats[2] = DXGI_FORMAT_R16G16B16A16_FLOAT;
d.RTVFormats[3] = QD3D12_VelocityFormat;
d.RTVFormats[4] = QD3D12_EmissiveFormat;
}
d.DSVFormat = QD3D12_DepthDsvFormat;
@@ -5649,7 +5933,7 @@ static D3D12_GRAPHICS_PIPELINE_STATE_DESC BuildARBPSODesc(
d.BlendState.AlphaToCoverageEnable = FALSE;
d.BlendState.IndependentBlendEnable = nativeColorOnly ? FALSE : TRUE;
for (int i = 0; i < 4; ++i)
for (int i = 0; i < (nativeColorOnly ? 1 : 5); ++i)
{
auto& rt = d.BlendState.RenderTarget[i];
rt.BlendEnable = FALSE;
@@ -6023,9 +6307,11 @@ void QD3D12_BeginFrame()
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 emissiveClear[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.frameOpen = true;
g_gl.frameOwner = &w;
@@ -6053,6 +6339,8 @@ void QD3D12_EndFrame()
if (!g_gl.sceneResolvedThisFrame)
{
QD3D12_ResolveGBufferForCurrentFrame(w);
if (!g_gl.raytracedLightingReadyThisFrame)
QD3D12_CompositeEmissiveIntoSceneColor(w);
QD3D12_TransitionResource(g_gl.cmdList.Get(), w.sceneColorBuffers[w.frameIndex].Get(), w.sceneColorState[w.frameIndex], D3D12_RESOURCE_STATE_PIXEL_SHADER_RESOURCE);
QD3D12_TransitionResource(g_gl.cmdList.Get(), w.normalBuffers[w.frameIndex].Get(), w.normalBufferState[w.frameIndex], D3D12_RESOURCE_STATE_PIXEL_SHADER_RESOURCE);
@@ -7544,8 +7832,12 @@ static void FlushImmediate(GLenum mode, const GLVertex* src, size_t n)
const bool explicitNormalUnit = (!arbProgramsActive) &&
(g_gl.currentNormalMapTexture != 0) &&
(g_gl.boundTexture[unit] == g_gl.currentNormalMapTexture);
const bool taggedGlowUnit = (!arbProgramsActive) && QD3D12_IsTextureTaggedGlowMap(g_gl.boundTexture[unit]);
const bool explicitGlowUnit = (!arbProgramsActive) &&
(g_gl.currentGlowMapTexture != 0) &&
(g_gl.boundTexture[unit] == g_gl.currentGlowMapTexture);
const bool wantsUnit = arbUnit || fixedColorUnit || taggedNormalUnit || explicitNormalUnit;
const bool wantsUnit = arbUnit || fixedColorUnit || taggedNormalUnit || explicitNormalUnit || taggedGlowUnit || explicitGlowUnit;
if (!wantsUnit)
continue;
@@ -7571,6 +7863,13 @@ static void FlushImmediate(GLenum mode, const GLVertex* src, size_t n)
UploadTexture(*normalMapTex);
}
TextureResource* glowMapTex = QD3D12_SelectGlowMapTexture(boundTextures);
if (glowMapTex && glowMapTex != &g_gl.whiteTexture && !glowMapTex->gpuValid)
{
EnsureTextureResource(*glowMapTex);
UploadTexture(*glowMapTex);
}
TextureResource* tex0 = boundTextures[0];
TextureResource* tex1 = boundTextures[1];
@@ -7876,9 +8175,11 @@ static void QD3D12_FlushQueuedBatches()
D3D12_GPU_DESCRIPTOR_HANDLE lastTex0{};
D3D12_GPU_DESCRIPTOR_HANDLE lastTex1{};
D3D12_GPU_DESCRIPTOR_HANDLE lastNormalMap{};
D3D12_GPU_DESCRIPTOR_HANDLE lastGlowMap{};
bool haveLastTex0 = false;
bool haveLastTex1 = false;
bool haveLastNormalMap = false;
bool haveLastGlowMap = false;
D3D12_PRIMITIVE_TOPOLOGY lastTopo = D3D_PRIMITIVE_TOPOLOGY_UNDEFINED;
for (size_t i = 0; i < g_gl.queuedBatches.size(); ++i)
@@ -7904,6 +8205,7 @@ static void QD3D12_FlushQueuedBatches()
haveLastTex0 = false;
haveLastTex1 = false;
haveLastNormalMap = false;
haveLastGlowMap = false;
lastTopo = D3D_PRIMITIVE_TOPOLOGY_UNDEFINED;
}
@@ -7971,6 +8273,7 @@ static void QD3D12_FlushQueuedBatches()
dc->_motionPad[0] = batch.key.useNormalMap;
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;
if (batch.key.useARBPrograms)
{
@@ -8026,6 +8329,7 @@ static void QD3D12_FlushQueuedBatches()
haveLastTex0 = false;
haveLastTex1 = false;
haveLastNormalMap = false;
haveLastGlowMap = false;
}
else
{
@@ -8062,6 +8366,18 @@ static void QD3D12_FlushQueuedBatches()
lastNormalMap = normalMapGpu;
haveLastNormalMap = true;
}
UINT glowSrvIndex = batch.key.glowMapSrvIndex;
if (glowSrvIndex == UINT_MAX)
glowSrvIndex = g_gl.whiteTexture.srvIndex;
D3D12_GPU_DESCRIPTOR_HANDLE glowMapGpu = QD3D12_SrvGpu(glowSrvIndex);
if (!haveLastGlowMap || glowMapGpu.ptr != lastGlowMap.ptr)
{
g_gl.cmdList->SetGraphicsRootDescriptorTable(4, glowMapGpu);
lastGlowMap = glowMapGpu;
haveLastGlowMap = true;
}
}
g_gl.cmdList->SetGraphicsRootConstantBufferView(0, cbAlloc.gpu);
@@ -8192,11 +8508,13 @@ void APIENTRY glClear(GLbitfield mask)
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 emissiveClear[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);
}
}
@@ -8543,6 +8861,9 @@ void APIENTRY glDeleteTextures(GLsizei n, const GLuint* textures)
if (g_gl.currentNormalMapTexture == deadId)
g_gl.currentNormalMapTexture = 0;
if (g_gl.currentGlowMapTexture == deadId)
g_gl.currentGlowMapTexture = 0;
auto it = g_gl.textures.find(deadId);
if (it != g_gl.textures.end())
{
@@ -8606,6 +8927,49 @@ void APIENTRY glNormalMapYSignf(GLfloat sign)
g_gl.currentNormalMapYSign = (sign < 0.0f) ? -1.0f : 1.0f;
}
void APIENTRY glTagTextureGlowMap(GLuint texture, GLboolean isGlowMap)
{
if (texture == 0)
{
g_gl.lastError = GL_INVALID_VALUE;
return;
}
TextureResource& tex = QD3D12_EnsureTextureName(texture);
tex.isGlowMap = (isGlowMap != GL_FALSE);
}
void APIENTRY glTextureGlowMap(GLuint texture, GLboolean isGlowMap)
{
glTagTextureGlowMap(texture, isGlowMap);
}
void APIENTRY glBindGlowMapTexture(GLuint texture)
{
if (texture != 0)
{
TextureResource& tex = QD3D12_EnsureTextureName(texture);
if (tex.isNormalMap && !tex.isGlowMap)
{
QD3D12_Log("glBindGlowMapTexture(%u) ignored: texture is tagged as a normal map. Did you pass the bump image texnum instead of the glow image texnum?", texture);
g_gl.currentGlowMapTexture = 0;
return;
}
}
g_gl.currentGlowMapTexture = texture;
}
void APIENTRY glGlowMapTexture(GLuint texture)
{
glBindGlowMapTexture(texture);
}
void APIENTRY glGlowMapStrengthf(GLfloat strength)
{
g_gl.currentGlowMapStrength = (strength < 0.0f) ? 0.0f : strength;
}
void APIENTRY glRaytracingMaterialFlagsQD3D12(GLuint flags)
{
g_gl.currentRayMaterialFlags = QD3D12_ClampRayMaterialFlags((uint32_t)flags);
@@ -8742,6 +9106,10 @@ void APIENTRY glGetIntegerv(GLenum pname, GLint* params)
*params = (GLint)g_gl.currentNormalMapTexture;
break;
case GL_GLOW_MAP_BINDING_QD3D12:
*params = (GLint)g_gl.currentGlowMapTexture;
break;
case GL_QD3D12_MATERIAL_FLAGS:
*params = (GLint)QD3D12_CurrentRayMaterialFlags();
break;
@@ -11301,6 +11669,11 @@ PROC WINAPI qd3d12_wglGetProcAddress(LPCSTR name) {
{ "glNormalMapTexture", (PROC)glNormalMapTexture },
{ "glNormalMapStrengthf", (PROC)glNormalMapStrengthf },
{ "glNormalMapYSignf", (PROC)glNormalMapYSignf },
{ "glTagTextureGlowMap", (PROC)glTagTextureGlowMap },
{ "glTextureGlowMap", (PROC)glTextureGlowMap },
{ "glBindGlowMapTexture", (PROC)glBindGlowMapTexture },
{ "glGlowMapTexture", (PROC)glGlowMapTexture },
{ "glGlowMapStrengthf", (PROC)glGlowMapStrengthf },
{ "glGlassMaterialQD3D12", (PROC)glGlassMaterialQD3D12 },
{ "glMaterialGlassQD3D12", (PROC)glMaterialGlassQD3D12 },
{ "glRaytracingMaterialFlagsQD3D12", (PROC)glRaytracingMaterialFlagsQD3D12 },
@@ -11560,8 +11933,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.normalBuffers[frame] || !w.positionBuffers[frame] || !w.velocityBuffers[frame])
!w.normalMsaaBuffers[frame] || !w.positionMsaaBuffers[frame] || !w.velocityMsaaBuffers[frame] || !w.emissiveMsaaBuffers[frame] ||
!w.normalBuffers[frame] || !w.positionBuffers[frame] || !w.velocityBuffers[frame] || !w.emissiveBuffers[frame])
{
return;
}
@@ -11570,16 +11943,19 @@ static void QD3D12_PointResolveMsaaGBufferToSingleSample(QD3D12Window& w)
QD3D12_TransitionResource(cl, w.normalMsaaBuffers[frame].Get(), w.normalMsaaState[frame], D3D12_RESOURCE_STATE_PIXEL_SHADER_RESOURCE);
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.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);
D3D12_CPU_DESCRIPTOR_HANDLE rtvs[3] =
D3D12_CPU_DESCRIPTOR_HANDLE rtvs[4] =
{
CurrentResolvedNormalRTV(),
CurrentResolvedPositionRTV(),
CurrentResolvedVelocityRTV()
CurrentResolvedVelocityRTV(),
CurrentResolvedEmissiveRTV()
};
D3D12_VIEWPORT viewport{};
@@ -11600,17 +11976,18 @@ 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(3, rtvs, FALSE, nullptr);
cl->OMSetRenderTargets(4, rtvs, FALSE, nullptr);
cl->RSSetViewports(1, &viewport);
cl->RSSetScissorRects(1, &scissor);
cl->IASetPrimitiveTopology(D3D_PRIMITIVE_TOPOLOGY_TRIANGLELIST);
// t1..t4: depth + discontinuous G-buffer attributes. The shader uses
// t1..t5: 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->DrawInstanced(3, 1, 0, 0);
}
@@ -11686,6 +12063,7 @@ static void QD3D12_BindLowResSceneTargets(QD3D12Window& w)
QD3D12_TransitionResource(cl, w.normalMsaaBuffers[w.frameIndex].Get(), w.normalMsaaState[w.frameIndex], D3D12_RESOURCE_STATE_RENDER_TARGET);
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.depthMsaaBuffer.Get(), w.depthMsaaState, D3D12_RESOURCE_STATE_DEPTH_WRITE);
}
else
@@ -11694,18 +12072,20 @@ static void QD3D12_BindLowResSceneTargets(QD3D12Window& w)
QD3D12_TransitionResource(cl, w.normalBuffers[w.frameIndex].Get(), w.normalBufferState[w.frameIndex], D3D12_RESOURCE_STATE_RENDER_TARGET);
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.depthBuffer.Get(), w.depthState, D3D12_RESOURCE_STATE_DEPTH_WRITE);
}
D3D12_CPU_DESCRIPTOR_HANDLE rtvs[4] =
D3D12_CPU_DESCRIPTOR_HANDLE rtvs[5] =
{
CurrentRTV(),
CurrentNormalRTV(),
CurrentPositionRTV(),
CurrentVelocityRTV()
CurrentVelocityRTV(),
CurrentEmissiveRTV()
};
D3D12_CPU_DESCRIPTOR_HANDLE dsv = CurrentActiveSceneDepthDSV();
cl->OMSetRenderTargets(4, rtvs, FALSE, &dsv);
cl->OMSetRenderTargets(5, rtvs, FALSE, &dsv);
cl->RSSetViewports(1, &w.viewport);
cl->RSSetScissorRects(1, &w.scissor);
}
@@ -11795,6 +12175,8 @@ static void QD3D12_ResolveSceneToOutputAndEnterNativePhase(QD3D12Window& w)
QD3D12_ResolveGBufferForCurrentFrame(w);
const bool useLightingUpscaleInput = QD3D12_UseLightingTextureAsUpscaleInput(w);
if (!g_gl.raytracedLightingReadyThisFrame)
QD3D12_CompositeEmissiveIntoSceneColor(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);
@@ -11862,9 +12244,10 @@ void glLightScene(glRaytracingSceneHandle_t sceneHandle)
ID3D12Resource* sceneColor = window->sceneColorBuffers[frameIndex].Get();
ID3D12Resource* sceneNormal = window->normalBuffers[frameIndex].Get();
ID3D12Resource* scenePosition = window->positionBuffers[frameIndex].Get();
ID3D12Resource* sceneEmissive = window->emissiveBuffers[frameIndex].Get();
ID3D12Resource* sceneDepth = window->depthBuffer.Get();
if (!sceneColor || !sceneNormal || !scenePosition || !sceneDepth)
if (!sceneColor || !sceneNormal || !scenePosition || !sceneEmissive || !sceneDepth)
return;
QD3D12_TransitionResource(
@@ -11885,6 +12268,12 @@ void glLightScene(glRaytracingSceneHandle_t sceneHandle)
window->positionBufferState[frameIndex],
D3D12_RESOURCE_STATE_NON_PIXEL_SHADER_RESOURCE);
QD3D12_TransitionResource(
cl,
sceneEmissive,
window->emissiveBufferState[frameIndex],
D3D12_RESOURCE_STATE_NON_PIXEL_SHADER_RESOURCE);
QD3D12_TransitionResource(
cl,
sceneDepth,
@@ -11926,6 +12315,7 @@ void glLightScene(glRaytracingSceneHandle_t sceneHandle)
activeMaxBounces,
useDLSSRayReconstruction ? 0 : 1,
useDLSSRayReconstruction ? 0.0f : 1.0f);
glRaytracingLightingSetEmissiveInput(sceneEmissive, QD3D12_EmissiveFormat);
glRaytracingLightingPassDesc_t pass = {};
pass.albedoTexture = sceneColor;
@@ -11954,6 +12344,7 @@ void glLightScene(glRaytracingSceneHandle_t sceneHandle)
QD3D12_TransitionResource(cl, window->normalMsaaBuffers[frameIndex].Get(), window->normalMsaaState[frameIndex], D3D12_RESOURCE_STATE_RENDER_TARGET);
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->depthMsaaBuffer.Get(), window->depthMsaaState, D3D12_RESOURCE_STATE_DEPTH_WRITE);
}
else
@@ -11976,6 +12367,12 @@ void glLightScene(glRaytracingSceneHandle_t sceneHandle)
window->positionBufferState[frameIndex],
D3D12_RESOURCE_STATE_RENDER_TARGET);
QD3D12_TransitionResource(
cl,
sceneEmissive,
window->emissiveBufferState[frameIndex],
D3D12_RESOURCE_STATE_RENDER_TARGET);
QD3D12_TransitionResource(
cl,
sceneDepth,
+14 -1
View File
@@ -2254,4 +2254,17 @@ extern "C" {
}
#endif
void glRaytracingLightingSetVolumetricScattering(glRaytracingLight_t* light, float strength);
void glRaytracingLightingSetVolumetricScattering(glRaytracingLight_t* light, float strength);
// QD3D12 glow-map extension additions for opengl.h
#ifndef GL_GLOW_MAP_BINDING_QD3D12
#define GL_GLOW_MAP_BINDING_QD3D12 0x6005
#endif
// Glow-map material tagging / binding. Tagging is optional but lets the shim
// auto-discover a glow map from bound texture units. Explicit binding wins.
void APIENTRY glTagTextureGlowMap(GLuint texture, GLboolean isGlowMap);
void APIENTRY glTextureGlowMap(GLuint texture, GLboolean isGlowMap); // alias
void APIENTRY glBindGlowMapTexture(GLuint texture); // 0 disables explicit glow map
void APIENTRY glGlowMapTexture(GLuint texture); // alias
void APIENTRY glGlowMapStrengthf(GLfloat strength); // default 1.0; >1 allows overbright emission
+27 -2
View File
@@ -931,6 +931,11 @@ void idMaterial::ParseBlend( idLexer &src, shaderStage_t *stage ) {
stage->lighting = SL_DIFFUSE;
return;
}
if (!token.Icmp("glowmap")) {
stage->lighting = SL_GLOWMAP;
return;
}
if ( !token.Icmp( "specularmap" ) ) {
stage->lighting = SL_SPECULAR;
return;
@@ -1895,6 +1900,20 @@ idImage* idMaterial::GetBumpImage(void) const {
return GetEditorImage();
}
/*
===================
idMaterial::GetGlowImage
===================
*/
idImage* idMaterial::GetGlowImage(void) const {
for (int i = 0; i < numStages; i++) {
if ((stages[i].lighting == SL_GLOWMAP || stages[i].isGlow) && stages[i].texture.image) {
return stages[i].texture.image;
}
}
return NULL;
}
/*
==============
idMaterial::AddImplicitStages
@@ -2254,12 +2273,18 @@ void idMaterial::ParseMaterial(idLexer& src) {
src.ParseRestOfLine(renderBump);
continue;
}
#ifdef PREY
else if (!token.Icmp("glowmap")) {
src.ReadTokenOnLine(&token);
str = R_ParsePastImageProgram(src);
idStr::snPrintf(buffer, sizeof(buffer), "blend glowmap\nmap %s\n}\n", str);
newSrc.LoadMemory(buffer, strlen(buffer), "glowmap");
newSrc.SetFlags(LEXFL_NOFATALERRORS | LEXFL_NOSTRINGCONCAT | LEXFL_NOSTRINGESCAPECHARS | LEXFL_ALLOWPATHNAMES);
ParseStage(newSrc, trpDefault);
newSrc.FreeSource();
continue;
}
#ifdef PREY
else if (!token.Icmp("highres")) {
continue;
}
+3 -1
View File
@@ -194,7 +194,8 @@ typedef enum {
SL_AMBIENT, // execute after lighting
SL_BUMP,
SL_DIFFUSE,
SL_SPECULAR
SL_SPECULAR,
SL_GLOWMAP
#ifdef PREY
, SL_SHADER
#endif
@@ -506,6 +507,7 @@ public:
// jmarshall
idImage* GetDiffuseImage(void) const;
idImage* GetBumpImage(void) const;
idImage* GetGlowImage(void) const;
bool IsSky(void) const;
// jmarshall end
// returns true if the material will generate interactions with normal lights
+17 -276
View File
@@ -512,6 +512,16 @@ void RB_T_FillDepthBuffer( const drawSurf_t *surf ) {
shader->GetBumpImage()->Bind();
glBindNormalMapTexture(shader->GetBumpImage()->texnum);
idImage* glowMapImage = shader->GetGlowImage();
if (glowMapImage)
{
GL_SelectTexture(2);
glEnable(GL_TEXTURE_2D);
glowMapImage->Bind();
glBindGlowMapTexture(glowMapImage->texnum);
glGlowMapStrengthf(0.5f);
}
if (!surf->geo->isSkeletal)
{
glGeometryFlagf(GEOMETRY_FLAG_NONE);
@@ -527,9 +537,15 @@ void RB_T_FillDepthBuffer( const drawSurf_t *surf ) {
// draw it
RB_DrawElementsWithCounters(tri);
if (glowMapImage)
{
GL_SelectTexture(2);
glBindGlowMapTexture(0);
globalImages->BindNull();
}
glBindNormalMapTexture(0);
GL_SelectTexture(1);
glBindNormalMapTexture(0);
globalImages->BindNull();
GL_SelectTexture(0);
}
@@ -1073,205 +1089,6 @@ int RB_STD_DrawShaderPasses( drawSurf_t **drawSurfs, int numDrawSurfs ) {
return i;
}
/*
==============================================================================
BACK END RENDERING OF STENCIL SHADOWS
==============================================================================
*/
/*
=====================
RB_T_Shadow
the shadow volumes face INSIDE
=====================
*/
static void RB_T_Shadow( const drawSurf_t *surf ) {
const srfTriangles_t *tri;
// set the light position if we are using a vertex program to project the rear surfaces
if ( tr.backEndRendererHasVertexPrograms && r_useShadowVertexProgram.GetBool()
&& surf->space != backEnd.currentSpace ) {
idVec4 localLight;
R_GlobalPointToLocal( surf->space->modelMatrix, backEnd.vLight->globalLightOrigin, localLight.ToVec3() );
localLight.w = 0.0f;
glProgramEnvParameter4fvARB( GL_VERTEX_PROGRAM_ARB, PP_LIGHT_ORIGIN, localLight.ToFloatPtr() );
}
tri = surf->geo;
if ( !tri->shadowCache ) {
return;
}
glVertexPointer( 4, GL_FLOAT, sizeof( shadowCache_t ), vertexCache.Position(tri->shadowCache) );
// we always draw the sil planes, but we may not need to draw the front or rear caps
int numIndexes;
bool external = false;
if ( !r_useExternalShadows.GetInteger() ) {
numIndexes = tri->numIndexes;
} else if ( r_useExternalShadows.GetInteger() == 2 ) { // force to no caps for testing
numIndexes = tri->numShadowIndexesNoCaps;
} else if ( !(surf->dsFlags & DSF_VIEW_INSIDE_SHADOW) ) {
// if we aren't inside the shadow projection, no caps are ever needed needed
numIndexes = tri->numShadowIndexesNoCaps;
external = true;
} else if ( !backEnd.vLight->viewInsideLight && !(surf->geo->shadowCapPlaneBits & SHADOW_CAP_INFINITE) ) {
// if we are inside the shadow projection, but outside the light, and drawing
// a non-infinite shadow, we can skip some caps
if ( backEnd.vLight->viewSeesShadowPlaneBits & surf->geo->shadowCapPlaneBits ) {
// we can see through a rear cap, so we need to draw it, but we can skip the
// caps on the actual surface
numIndexes = tri->numShadowIndexesNoFrontCaps;
} else {
// we don't need to draw any caps
numIndexes = tri->numShadowIndexesNoCaps;
}
external = true;
} else {
// must draw everything
numIndexes = tri->numIndexes;
}
// set depth bounds
if( glConfig.depthBoundsTestAvailable && r_useDepthBoundsTest.GetBool() ) {
glDepthBoundsEXT( surf->scissorRect.zmin, surf->scissorRect.zmax );
}
// debug visualization
if ( r_showShadows.GetInteger() ) {
if ( r_showShadows.GetInteger() == 3 ) {
if ( external ) {
glColor3f( 0.1/backEnd.overBright, 1/backEnd.overBright, 0.1/backEnd.overBright );
} else {
// these are the surfaces that require the reverse
glColor3f( 1/backEnd.overBright, 0.1/backEnd.overBright, 0.1/backEnd.overBright );
}
} else {
// draw different color for turboshadows
if ( surf->geo->shadowCapPlaneBits & SHADOW_CAP_INFINITE ) {
if ( numIndexes == tri->numIndexes ) {
glColor3f( 1/backEnd.overBright, 0.1/backEnd.overBright, 0.1/backEnd.overBright );
} else {
glColor3f( 1/backEnd.overBright, 0.4/backEnd.overBright, 0.1/backEnd.overBright );
}
} else {
if ( numIndexes == tri->numIndexes ) {
glColor3f( 0.1/backEnd.overBright, 1/backEnd.overBright, 0.1/backEnd.overBright );
} else if ( numIndexes == tri->numShadowIndexesNoFrontCaps ) {
glColor3f( 0.1/backEnd.overBright, 1/backEnd.overBright, 0.6/backEnd.overBright );
} else {
glColor3f( 0.6/backEnd.overBright, 1/backEnd.overBright, 0.1/backEnd.overBright );
}
}
}
glStencilOp( GL_KEEP, GL_KEEP, GL_KEEP );
glDisable( GL_STENCIL_TEST );
GL_Cull( CT_TWO_SIDED );
RB_DrawShadowElementsWithCounters( tri, numIndexes );
GL_Cull( CT_FRONT_SIDED );
glEnable( GL_STENCIL_TEST );
return;
}
// patent-free work around
if ( !external ) {
// "preload" the stencil buffer with the number of volumes
// that get clipped by the near or far clip plane
glStencilOp( GL_KEEP, tr.stencilDecr, tr.stencilDecr );
GL_Cull( CT_FRONT_SIDED );
RB_DrawShadowElementsWithCounters( tri, numIndexes );
glStencilOp( GL_KEEP, tr.stencilIncr, tr.stencilIncr );
GL_Cull( CT_BACK_SIDED );
RB_DrawShadowElementsWithCounters( tri, numIndexes );
}
// traditional depth-pass stencil shadows
glStencilOp( GL_KEEP, GL_KEEP, tr.stencilIncr );
GL_Cull( CT_FRONT_SIDED );
RB_DrawShadowElementsWithCounters( tri, numIndexes );
glStencilOp( GL_KEEP, GL_KEEP, tr.stencilDecr );
GL_Cull( CT_BACK_SIDED );
RB_DrawShadowElementsWithCounters( tri, numIndexes );
}
/*
=====================
RB_StencilShadowPass
Stencil test should already be enabled, and the stencil buffer should have
been set to 128 on any surfaces that might receive shadows
=====================
*/
void RB_StencilShadowPass( const drawSurf_t *drawSurfs ) {
if ( !r_shadows.GetBool() ) {
return;
}
if ( !drawSurfs ) {
return;
}
RB_LogComment( "---------- RB_StencilShadowPass ----------\n" );
globalImages->BindNull();
glDisableClientState( GL_TEXTURE_COORD_ARRAY );
// for visualizing the shadows
if ( r_showShadows.GetInteger() ) {
if ( r_showShadows.GetInteger() == 2 ) {
// draw filled in
GL_State( GLS_DEPTHMASK | GLS_SRCBLEND_ONE | GLS_DSTBLEND_ONE | GLS_DEPTHFUNC_LESS );
} else {
// draw as lines, filling the depth buffer
GL_State( GLS_SRCBLEND_ONE | GLS_DSTBLEND_ZERO | GLS_POLYMODE_LINE | GLS_DEPTHFUNC_ALWAYS );
}
} else {
// don't write to the color buffer, just the stencil buffer
GL_State( GLS_DEPTHMASK | GLS_COLORMASK | GLS_ALPHAMASK | GLS_DEPTHFUNC_LESS );
}
if ( r_shadowPolygonFactor.GetFloat() || r_shadowPolygonOffset.GetFloat() ) {
glPolygonOffset( r_shadowPolygonFactor.GetFloat(), -r_shadowPolygonOffset.GetFloat() );
glEnable( GL_POLYGON_OFFSET_FILL );
}
glStencilFunc( GL_ALWAYS, 1, 255 );
if ( glConfig.depthBoundsTestAvailable && r_useDepthBoundsTest.GetBool() ) {
glEnable( GL_DEPTH_BOUNDS_TEST_EXT );
}
RB_RenderDrawSurfChainWithFunction( drawSurfs, RB_T_Shadow );
GL_Cull( CT_FRONT_SIDED );
if ( r_shadowPolygonFactor.GetFloat() || r_shadowPolygonOffset.GetFloat() ) {
glDisable( GL_POLYGON_OFFSET_FILL );
}
if ( glConfig.depthBoundsTestAvailable && r_useDepthBoundsTest.GetBool() ) {
glDisable( GL_DEPTH_BOUNDS_TEST_EXT );
}
glEnableClientState( GL_TEXTURE_COORD_ARRAY );
glStencilFunc( GL_GEQUAL, 128, 255 );
glStencilOp( GL_KEEP, GL_KEEP, GL_KEEP );
}
/*
=============================================================================================
@@ -1627,76 +1444,6 @@ void RB_STD_FogAllLights( void ) {
//=========================================================================================
/*
==================
RB_STD_LightScale
Perform extra blending passes to multiply the entire buffer by
a floating point value
==================
*/
void RB_STD_LightScale( void ) {
float v, f;
if ( backEnd.overBright == 1.0f ) {
return;
}
if ( r_skipLightScale.GetBool() ) {
return;
}
RB_LogComment( "---------- RB_STD_LightScale ----------\n" );
// the scissor may be smaller than the viewport for subviews
if ( r_useScissor.GetBool() ) {
glScissor( backEnd.viewDef->viewport.x1 + backEnd.viewDef->scissor.x1,
backEnd.viewDef->viewport.y1 + backEnd.viewDef->scissor.y1,
backEnd.viewDef->scissor.x2 - backEnd.viewDef->scissor.x1 + 1,
backEnd.viewDef->scissor.y2 - backEnd.viewDef->scissor.y1 + 1 );
backEnd.currentScissor = backEnd.viewDef->scissor;
}
// full screen blends
glLoadIdentity();
glMatrixMode( GL_PROJECTION );
glPushMatrix();
glLoadIdentity();
glOrtho( 0, 1, 0, 1, -1, 1 );
GL_State( GLS_SRCBLEND_DST_COLOR | GLS_DSTBLEND_SRC_COLOR );
GL_Cull( CT_TWO_SIDED ); // so mirror views also get it
globalImages->BindNull();
glDisable( GL_DEPTH_TEST );
glDisable( GL_STENCIL_TEST );
v = 1;
while ( idMath::Fabs( v - backEnd.overBright ) > 0.01 ) { // a little extra slop
f = backEnd.overBright / v;
f /= 2;
if ( f > 1 ) {
f = 1;
}
glColor3f( f, f, f );
v = v * f * 2;
glBegin( GL_QUADS );
glVertex2f( 0,0 );
glVertex2f( 0,1 );
glVertex2f( 1,1 );
glVertex2f( 1,0 );
glEnd();
}
glPopMatrix();
glEnable( GL_DEPTH_TEST );
glMatrixMode( GL_MODELVIEW );
GL_Cull( CT_FRONT_SIDED );
}
//=========================================================================================
/*
=============
RB_STD_DrawView
@@ -1717,9 +1464,6 @@ void RB_STD_DrawView( void ) {
// clear the z buffer, set the projection matrix, etc
RB_BeginDrawingView();
// decide how much overbrighting we are going to do
RB_DetermineLightScale();
// fill the depth buffer and clear color buffer to black except on
// subviews
RB_STD_FillDepthBuffer( drawSurfs, numDrawSurfs );
@@ -1730,9 +1474,6 @@ void RB_STD_DrawView( void ) {
// disable stencil shadow test
glStencilFunc( GL_ALWAYS, 128, 255 );
// uplight the entire screen to crutch up not having better blending range
RB_STD_LightScale();
// now draw any non-light dependent shading passes
int processed = RB_STD_DrawShaderPasses( drawSurfs, numDrawSurfs );