From ac531012148caf3787133a79b9e0b7665afa64ea Mon Sep 17 00:00:00 2001 From: gm-matthew <108370479+gm-matthew@users.noreply.github.com> Date: Thu, 16 Nov 2023 23:03:21 +0000 Subject: [PATCH] Implement LOD blending If two translucent polygons with opposing patterns overlap the result is always opaque Also the LOD scale calculation depends on Euclidean distance of x, y and z, not just z --- Src/Graphics/New3D/New3D.cpp | 63 ++++++++++++++++++-------- Src/Graphics/New3D/R3DFrameBuffers.cpp | 20 ++++---- 2 files changed, 54 insertions(+), 29 deletions(-) diff --git a/Src/Graphics/New3D/New3D.cpp b/Src/Graphics/New3D/New3D.cpp index f69b1d0..5ed5b19 100644 --- a/Src/Graphics/New3D/New3D.cpp +++ b/Src/Graphics/New3D/New3D.cpp @@ -714,46 +714,69 @@ void CNew3D::DescendCullingNode(UINT32 addr) } } - float LODscale = fBlendRadius * m_nodeAttribs.currentModelScale / std::abs(m_modelMat.currentMatrix[14]); + float LODscale; + if (m_nodeAttribs.currentDisableCulling) + LODscale = FLT_MAX; + else + { + float distance = std::hypot(m_modelMat.currentMatrix[12], m_modelMat.currentMatrix[13], m_modelMat.currentMatrix[14]); + LODscale = fBlendRadius * m_nodeAttribs.currentModelScale / distance; + } const LODFeatureType& lodTableEntry = m_LODBlendTable->table[lodTablePointer]; - if (m_nodeAttribs.currentDisableCulling) - { - m_nodeAttribs.currentModelAlpha = 1.0f; - } - else - { - float nodeAlpha = lodTableEntry.lod[3].blendFactor * (LODscale - lodTableEntry.lod[3].deleteSize); - nodeAlpha = std::clamp(nodeAlpha, 0.0f, 1.0f); - m_nodeAttribs.currentModelAlpha *= nodeAlpha; // alpha of each node multiples by the alpha of its parent - } - - if (m_nodeAttribs.currentClipStatus != Clip::OUTSIDE && m_nodeAttribs.currentModelAlpha > 0.0f) { + if (m_nodeAttribs.currentClipStatus != Clip::OUTSIDE && LODscale >= lodTableEntry.lod[3].deleteSize) { // Descend down first link if ((node[0x00] & 0x08)) // 4-element LOD table { lodPtr = TranslateCullingAddress(child1Ptr); - // determine which LOD to use; we do not currently blend between LODs - int modelLOD; - for (modelLOD = 0; modelLOD < 3; modelLOD++) + if (NULL != lodPtr) { - if (LODscale >= lodTableEntry.lod[modelLOD].deleteSize) - break; - } + int modelLOD; + for (modelLOD = 0; modelLOD < 3; modelLOD++) + { + if (LODscale >= lodTableEntry.lod[modelLOD].deleteSize && lodPtr[modelLOD] & 0x1000000) + break; + } - if (NULL != lodPtr) { + float tempAlpha = m_nodeAttribs.currentModelAlpha; + + float nodeAlpha = lodTableEntry.lod[modelLOD].blendFactor * (LODscale - lodTableEntry.lod[modelLOD].deleteSize); + nodeAlpha = std::clamp(nodeAlpha, 0.0f, 1.0f); + if (nodeAlpha > 15.0f / 16.0f) // shader discards pixels below 1/16 alpha + nodeAlpha = 1.0f; + else if (nodeAlpha < 1.0f / 16.0f) + nodeAlpha = 0.0f; + m_nodeAttribs.currentModelAlpha *= nodeAlpha; // alpha of each node multiples by the alpha of its parent + if ((node[0x03 - m_offset] & 0x20000000)) { DescendCullingNode(lodPtr[modelLOD] & 0xFFFFFF); + + if (nodeAlpha < 1.0f && modelLOD != 3) + { + m_nodeAttribs.currentModelAlpha = (1.0f - nodeAlpha) * tempAlpha; + DescendCullingNode(lodPtr[modelLOD+1] & 0xFFFFFF); + } } else { DrawModel(lodPtr[modelLOD] & 0xFFFFFF); + + if (nodeAlpha < 1.0f && modelLOD != 3) + { + m_nodeAttribs.currentModelAlpha = (1.0f - nodeAlpha) * tempAlpha; + DrawModel(lodPtr[modelLOD + 1] & 0xFFFFFF); + } } } } else { + + float nodeAlpha = lodTableEntry.lod[3].blendFactor * (LODscale - lodTableEntry.lod[3].deleteSize); + nodeAlpha = std::clamp(nodeAlpha, 0.0f, 1.0f); + m_nodeAttribs.currentModelAlpha *= nodeAlpha; // alpha of each node multiples by the alpha of its parent + DescendNodePtr(child1Ptr); } diff --git a/Src/Graphics/New3D/R3DFrameBuffers.cpp b/Src/Graphics/New3D/R3DFrameBuffers.cpp index b91fd7c..7461b01 100644 --- a/Src/Graphics/New3D/R3DFrameBuffers.cpp +++ b/Src/Graphics/New3D/R3DFrameBuffers.cpp @@ -274,16 +274,18 @@ void R3DFrameBuffers::AllocShaderTrans() { vec4 colTrans1 = texture(tex1, fsTexCoord); vec4 colTrans2 = texture(tex2, fsTexCoord); - - if(colTrans1.a+colTrans2.a > 0.0) { - vec3 col1 = colTrans1.rgb * colTrans1.a; - vec3 col2 = colTrans2.rgb * colTrans2.a; - - colTrans1 = vec4((col1+col2) / (colTrans1.a + colTrans2.a), // this is my best guess at the blending between the layers - colTrans1.a+colTrans2.a); + + // if both transparency layers overlap, the result is opaque + if (colTrans1.a * colTrans2.a > 0.0) { + vec3 mixCol = mix(colTrans1.rgb, colTrans2.rgb, (colTrans2.a + (1.0 - colTrans1.a)) / 2.0); + fragColor = vec4(mixCol, 1.0); + } + else if (colTrans1.a > 0.0) { + fragColor = colTrans1; + } + else { + fragColor = colTrans2; // if alpha is zero it will have no effect anyway } - - fragColor = colTrans1; } )glsl";