Fixed an issue with alpha blended textures not rendering properly.

This commit is contained in:
Justin Marshall
2026-05-08 12:56:20 -07:00
parent c1e344ca94
commit ff57e77888
3 changed files with 117 additions and 28 deletions
+13 -11
View File
@@ -2019,8 +2019,9 @@ void glRaytracingSetMeshMaterialFlags(glRaytracingMeshHandle_t meshHandle, uint3
mesh->materialFlags = materialFlags;
// The glass bit changes whether the BLAS geometry is opaque, so force a full
// BLAS rebuild. The TLAS is also rebuilt so InstanceID carries the material bit.
// The glass bit also represents shim-auto-tagged alpha-blended surfaces.
// It changes whether BLAS geometry is opaque, so force a full BLAS rebuild.
// The TLAS is also rebuilt so InstanceID carries the material bit.
mesh->blasBuilt = 0;
mesh->dirty = 1;
glRaytracingInvalidateInstancesForMesh(meshHandle, 0);
@@ -2645,10 +2646,10 @@ void ShadowMiss(inout ShadowPayload payload)
[shader("anyhit")]
void ShadowAnyHit(inout ShadowPayload payload, in BuiltInTriangleIntersectionAttributes attr)
{
// Glass should participate in the primary/raster image, but visibility rays
// must continue through it. Mark glass BLAS geometry non-opaque on the CPU
// side so this any-hit shader runs, then IgnoreHit() lets the ray keep going
// to whatever is behind the pane.
// Glass/alpha-blended surfaces should participate in the primary/raster image,
// but visibility rays must continue through them. Mark the BLAS geometry
// non-opaque on the CPU side so this any-hit shader runs, then IgnoreHit()
// lets the ray keep going to whatever is behind the pane/sprite.
if (CurrentRayHitIsGlass())
{
IgnoreHit();
@@ -2682,8 +2683,9 @@ void BounceMiss(inout BouncePayload payload)
[shader("anyhit")]
void BounceAnyHit(inout BouncePayload payload, in BuiltInTriangleIntersectionAttributes attr)
{
// Secondary diffuse rays should see through the same glass that shadow rays
// see through. This keeps a glass pane from killing all bounced light behind it.
// Secondary diffuse rays should see through the same transparent surfaces that
// shadow rays see through. This keeps a pane/sprite from killing all bounced
// light behind it.
if (CurrentRayHitIsGlass())
{
IgnoreHit();
@@ -2694,8 +2696,8 @@ void BounceAnyHit(inout BouncePayload payload, in BuiltInTriangleIntersectionAtt
[shader("closesthit")]
void BounceClosestHit(inout BouncePayload payload, in BuiltInTriangleIntersectionAttributes attr)
{
// Fallback for glass geometry that was accidentally built opaque. Correctly
// tagged glass is ignored in BounceAnyHit() above.
// Fallback for transparent geometry that was accidentally built opaque.
// Correctly tagged transparent surfaces are ignored in BounceAnyHit() above.
if (CurrentRayHitIsGlass())
{
payload.hit = 0;
@@ -2716,7 +2718,7 @@ float TraceShadow(float3 origin, float3 dir, float maxT)
RayDesc ray;
ray.Origin = origin;
ray.Direction = dir;
ray.TMin = 0.001;
ray.TMin = 5.0;
ray.TMax = maxT;
ShadowPayload payload;
+64 -17
View File
@@ -1691,17 +1691,49 @@ static inline uint32_t QD3D12_CurrentRayMaterialFlags()
return QD3D12_ClampRayMaterialFlags(g_gl.currentRayMaterialFlags);
}
static inline bool QD3D12_PipelineUsesAlphaBlend(PipelineMode mode)
{
return mode == PIPE_BLEND_TEX || mode == PIPE_BLEND_UNTEX;
}
static inline bool QD3D12_CurrentDrawUsesAlphaBlend()
{
if (!g_gl.blend)
return false;
// GL_BLEND with this pair is effectively a straight source overwrite. Treat
// it as opaque so old code that leaves GL_BLEND enabled by accident does not
// silently become ray-transparent.
if (g_gl.blendSrc == GL_ONE && g_gl.blendDst == GL_ZERO)
return false;
return true;
}
static inline uint32_t QD3D12_CurrentEffectiveRayMaterialFlags()
{
uint32_t flags = QD3D12_CurrentRayMaterialFlags();
// Alpha-blended fixed-function surfaces are visually transparent. Mirror that
// into the DXR material bits automatically so callers do not also have to tag
// the same draw as glass just to make shadow/GI rays continue through it.
if (QD3D12_CurrentDrawUsesAlphaBlend())
flags |= GL_RAYTRACING_MATERIAL_FLAG_GLASS;
return QD3D12_ClampRayMaterialFlags(flags);
}
static inline float QD3D12_CurrentEffectiveGeometryFlag()
{
uint32_t bits = (uint32_t)max(0.0f, floorf(g_gl.currentGeometryFlag + 0.5f));
if (QD3D12_RayMaterialFlagsHaveGlass(QD3D12_CurrentRayMaterialFlags()))
if (QD3D12_RayMaterialFlagsHaveGlass(QD3D12_CurrentEffectiveRayMaterialFlags()))
bits |= QD3D12_GEOMETRY_FLAG_GLASS_BIT;
return (float)bits;
}
static inline float QD3D12_CurrentEffectiveMaterialType()
{
if (QD3D12_RayMaterialFlagsHaveGlass(QD3D12_CurrentRayMaterialFlags()) &&
if (QD3D12_RayMaterialFlagsHaveGlass(QD3D12_CurrentEffectiveRayMaterialFlags()) &&
g_gl.currentMaterialType == 0.0f)
{
return QD3D12_MATERIAL_TYPE_GLASS;
@@ -2248,6 +2280,7 @@ cbuffer DrawCB : register(b0)
#define gGlowMapStrength gMotionPad.w
#define gUseSpecularMap gMaterialMapPad.x
#define gSpecularMapStrength gMaterialMapPad.y
#define gAlphaBlendPass gMaterialMapPad.z
Texture2D gTex0 : register(t0);
Texture2D gTex1 : register(t1);
@@ -2713,6 +2746,8 @@ PSOut PSMain(VSOut i)
{
PSOut o;
o.color = BuildTexturedColor(i);
if (gAlphaBlendPass > 0.5)
o.color.rgb += BuildGlowEmission(i).rgb;
o.normal = float4(BuildGBufferNormal(i), i.attr.y);
o.position = float4(i.worldPos, i.attr.x);
o.velocity = BuildVelocity(i);
@@ -2760,7 +2795,8 @@ float4 PSMainAlphaTestColorOnly(VSOut i) : SV_Target0
c.rgb += BuildGlowEmission(i).rgb;
return c;
}
)HLSL"
R"HLSL(
float4 PSMainUntexturedColorOnly(VSOut i) : SV_Target0
{
return ApplyFog(i.col, i.fogCoord);
@@ -5880,14 +5916,16 @@ static D3D12_GRAPHICS_PIPELINE_STATE_DESC BuildPSODesc(
d.RasterizerState.DepthClipEnable = TRUE;
d.RasterizerState.MultisampleEnable = (d.SampleDesc.Count > 1) ? TRUE : FALSE;
const bool alphaBlended = QD3D12_PipelineUsesAlphaBlend(mode);
d.BlendState.RenderTarget[0].RenderTargetWriteMask = key.colorWriteMask;
if (!nativeColorOnly)
{
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.RenderTarget[5].RenderTargetWriteMask = key.colorWriteMask ? D3D12_COLOR_WRITE_ENABLE_ALL : 0;
d.BlendState.RenderTarget[1].RenderTargetWriteMask = (key.colorWriteMask && !alphaBlended) ? D3D12_COLOR_WRITE_ENABLE_ALL : 0;
d.BlendState.RenderTarget[2].RenderTargetWriteMask = (key.colorWriteMask && !alphaBlended) ? D3D12_COLOR_WRITE_ENABLE_ALL : 0;
d.BlendState.RenderTarget[3].RenderTargetWriteMask = (key.colorWriteMask && !alphaBlended) ? D3D12_COLOR_WRITE_ENABLE_ALL : 0;
d.BlendState.RenderTarget[4].RenderTargetWriteMask = (key.colorWriteMask && !alphaBlended) ? D3D12_COLOR_WRITE_ENABLE_ALL : 0;
d.BlendState.RenderTarget[5].RenderTargetWriteMask = (key.colorWriteMask && !alphaBlended) ? D3D12_COLOR_WRITE_ENABLE_ALL : 0;
}
d.BlendState.AlphaToCoverageEnable = FALSE;
@@ -5907,7 +5945,7 @@ static D3D12_GRAPHICS_PIPELINE_STATE_DESC BuildPSODesc(
rt.LogicOp = D3D12_LOGIC_OP_NOOP;
}
if (mode == PIPE_BLEND_TEX || mode == PIPE_BLEND_UNTEX)
if (alphaBlended)
{
auto& rt = d.BlendState.RenderTarget[0];
rt.BlendEnable = TRUE;
@@ -5929,7 +5967,10 @@ static D3D12_GRAPHICS_PIPELINE_STATE_DESC BuildPSODesc(
//}
}
ApplyRasterDepthStencilState(d, key);
BatchKey depthStencilKey = key;
if (alphaBlended)
depthStencilKey.depthWrite = false;
ApplyRasterDepthStencilState(d, depthStencilKey);
return d;
}
@@ -6151,6 +6192,8 @@ static D3D12_GRAPHICS_PIPELINE_STATE_DESC BuildARBPSODesc(
d.RasterizerState.DepthClipEnable = TRUE;
d.RasterizerState.MultisampleEnable = (d.SampleDesc.Count > 1) ? TRUE : FALSE;
const bool alphaBlended = QD3D12_PipelineUsesAlphaBlend(key.pipeline);
d.BlendState.AlphaToCoverageEnable = FALSE;
d.BlendState.IndependentBlendEnable = nativeColorOnly ? FALSE : TRUE;
@@ -6169,7 +6212,7 @@ static D3D12_GRAPHICS_PIPELINE_STATE_DESC BuildARBPSODesc(
rt.RenderTargetWriteMask = (i == 0) ? key.colorWriteMask : 0;
}
if (key.pipeline == PIPE_BLEND_TEX || key.pipeline == PIPE_BLEND_UNTEX)
if (alphaBlended)
{
auto& rt = d.BlendState.RenderTarget[0];
rt.BlendEnable = TRUE;
@@ -6181,7 +6224,10 @@ static D3D12_GRAPHICS_PIPELINE_STATE_DESC BuildARBPSODesc(
rt.BlendOpAlpha = D3D12_BLEND_OP_ADD;
}
ApplyRasterDepthStencilState(d, key);
BatchKey depthStencilKey = key;
if (alphaBlended)
depthStencilKey.depthWrite = false;
ApplyRasterDepthStencilState(d, depthStencilKey);
return d;
}
@@ -7969,7 +8015,7 @@ static PipelineMode PickPipeline(bool useTex0, bool useTex1)
{
const bool textured = useTex0 || useTex1;
if (g_gl.blend)
if (QD3D12_CurrentDrawUsesAlphaBlend())
return textured ? PIPE_BLEND_TEX : PIPE_BLEND_UNTEX;
if (g_gl.alphaTest)
@@ -8514,7 +8560,7 @@ static void QD3D12_FlushQueuedBatches()
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[2] = QD3D12_PipelineUsesAlphaBlend(batch.key.pipeline) ? 1.0f : 0.0f;
dc->materialMapPad[3] = 0.0f;
if (batch.key.useARBPrograms)
@@ -14276,11 +14322,12 @@ void glUpdateBottomAccelStructure(bool opaque, uint32_t& meshHandle)
meshDesc.indexCount = (uint32_t)drawIndexes.size();
meshDesc.allowUpdate = 1;
const uint32_t materialFlags = QD3D12_CurrentRayMaterialFlags();
const uint32_t materialFlags = QD3D12_CurrentEffectiveRayMaterialFlags();
const bool isGlass = QD3D12_RayMaterialFlagsHaveGlass(materialFlags);
// Glass must be non-opaque in the BLAS so the DXR any-hit shader can run
// IgnoreHit() and let visibility/path rays continue through the pane.
// Glass/alpha-blended surfaces must be non-opaque in the BLAS so the DXR
// any-hit shader can run IgnoreHit() and let visibility/path rays continue
// through the pane/sprite without requiring a separate caller-side tag.
meshDesc.opaque = (opaque && !isGlass) ? 1 : 0;
if (meshHandle)
+40
View File
@@ -502,6 +502,35 @@ void RB_T_FillDepthBuffer( const drawSurf_t *surf ) {
// draw the entire surface solid
if (drawSolid && !shader->IsSky()) {
glColor4f(1.0f, 1.0f, 1.0f, 1.0f);
if (shader->Coverage() == MC_PERFORATED) {
for (stage = 0; stage < shader->GetNumStages(); stage++) {
pStage = shader->GetStage(stage);
if (regs[pStage->conditionRegister] == 0) {
continue;
}
if (!pStage->hasAlphaTest) {
continue;
}
// set the alpha modulate
color[3] = regs[pStage->color.registers[3]];
color[0] = 1;
color[1] = 1;
color[2] = 1;
glColor4fv(color);
glDisable(GL_BLEND);
glEnable(GL_ALPHA_TEST);
glAlphaFunc(GL_GREATER, regs[pStage->alphaTestRegister]);
break;
}
}
// bind the texture
GL_SelectTexture(0);
glEnable(GL_TEXTURE_2D);
@@ -557,6 +586,17 @@ void RB_T_FillDepthBuffer( const drawSurf_t *surf ) {
glBindNormalMapTexture(0);
globalImages->BindNull();
GL_SelectTexture(0);
if (shader->Coverage() == MC_PERFORATED) {
glDisable(GL_ALPHA_TEST);
glEnable(GL_BLEND);
color[0] = 1;
color[1] = 1;
color[2] = 1;
color[3] = 1;
glColor4fv(color);
}
}