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
This commit is contained in:
gm-matthew 2023-11-16 23:03:21 +00:00
parent a065df24b8
commit ac53101214
2 changed files with 54 additions and 29 deletions

View file

@ -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
if (NULL != lodPtr)
{
int modelLOD;
for (modelLOD = 0; modelLOD < 3; modelLOD++)
{
if (LODscale >= lodTableEntry.lod[modelLOD].deleteSize)
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);
}

View file

@ -275,16 +275,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
}
}
)glsl";