Advertisement

Occluder from AABB, near plane 'fix'?

Started by October 27, 2024 11:53 AM
12 comments, last by cozzie 1 day, 7 hours ago

Hey @all8up , I've updated the implementation so that it takes the 3 furthest silhoutte points to form the near plane.

The results I'm seeing:

Version taking 3 furthest silhoutte points:

  • seems to work the same as initial version when 1 plane faces the camera
  • no more occlusion when more then 1 plane faces the camera (bug?)
  • https://pastebin.com/aLcEVk3W

Version which makes each camera facing quad a plane:

  • works OK when >1 quads face the camera
  • doesn't seem to work when the ‘top’ or the ‘bottom’ quad of the occluder is facing the camera (no occlusion)
    • which seems superweird, as it works for all other quads
    • also checked the winding order, which might explain it; when I swap 1, 3, 7, 5 with 1, 5, 7, 3 → no occlusion at all (for any plane)
  • https://pastebin.com/8aMnbkbN

Both versions still suffer the issue where occlusion happens only when the camera is super close to the ‘near’ plane(s).

Starting to go a bit crazy on this perhaps small bug with big annoyance 🙂

Any input is appreciated.

Winding order for reference:

Crealysm game & engine development: http://www.crealysm.com

Looking for a passionate, disciplined and structured producer? PM me

Btw, i have just learned you can disable depth clipping on GPUs. Then the vertices are not clipped, but projected to the near / far plane.
This way the whole box should render even if it's intersecting the front clip plane. There are no holes.
They use this fro shadow maps for example, so very distant occluders can still cast shadow although they are outside the clipping volume.

Not sure if this is helpful here, but maybe.

Some Vulkan docs, but i guess DX11 is the same: https://docs.vulkan.org/samples/latest/samples/extensions/dynamic_primitive_clipping/README.html

Advertisement

For future reference/ if others ever want to try this out 🙂

I fixed the near plane, which is now a weighted average of the quads that are facing the camera. Here's the (not yet optimized) code:

std::vector<CR_VECTOR4> CMathHelper::CreateOccluderPlanes(const CR_VECTOR3 &pCameraPos, const CR_VECTOR3 *pCorners, const CR_VECTOR3 &pCenter, std::vector<CR_VECTOR3> &pPlanesSigns, std::vector<CR_VECTOR3> &pDebugPoints)
{
    assert(pCorners != nullptr);
    pPlanesSigns.clear();
    
    std::vector<int> cornerIds = { 0, 1, 2, 3,       3, 2, 6, 7,        7, 6, 5, 4,        4, 5, 1, 0,       0, 3, 7, 4,        1, 5, 6, 2 };    // 6*4 = 24
    
    float nearPlanesArea = 0.0f;
    std::vector<float>            nearQuadFactors;
    std::vector<CR_VECTOR3>        nearQuadNormals;
    std::vector<float>            nearQuadAreas;
    std::set<CR_VECTOR2I>        visibleEdges;
    std::vector<CR_VECTOR4>        planes;
    std::vector<unsigned int>    silhouetteCorners;
    // find quads facing the camera
    for(size_t verts=0;verts<cornerIds.size();verts+=4)
    {
        float factor = DotProductVec3(CalculateFaceNormal(pCorners[cornerIds[verts]], pCorners[cornerIds[verts+1]], pCorners[cornerIds[verts+2]]), pCameraPos - pCorners[cornerIds[verts+1]]);
        if(factor > 0)
        {
            nearQuadFactors.emplace_back(factor);
            nearQuadNormals.emplace_back(CalculateFaceNormal(pCorners[cornerIds[verts]], pCorners[cornerIds[verts+1]], pCorners[cornerIds[verts+2]]));
            float quadArea = GetQuadArea(pCorners[cornerIds[verts]], pCorners[cornerIds[verts+1]], pCorners[cornerIds[verts+2]], pCorners[cornerIds[verts+3]]) * factor;
            nearPlanesArea += quadArea;
            nearQuadAreas.emplace_back(quadArea);
            silhouetteCorners.emplace_back(cornerIds[verts]);
            silhouetteCorners.emplace_back(cornerIds[verts+1]);
            silhouetteCorners.emplace_back(cornerIds[verts+2]);
            silhouetteCorners.emplace_back(cornerIds[verts+3]);
            std::vector<CR_VECTOR2I> newEdges;
            newEdges.emplace_back(CR_VECTOR2I(cornerIds[verts], cornerIds[verts+1]));
            newEdges.emplace_back(CR_VECTOR2I(cornerIds[verts+1], cornerIds[verts+2]));
            newEdges.emplace_back(CR_VECTOR2I(cornerIds[verts+2], cornerIds[verts+3]));
            newEdges.emplace_back(CR_VECTOR2I(cornerIds[verts+3], cornerIds[verts]));
    
            for(auto && edge : newEdges)
            {
                edge.LowToHigh();
                auto it = visibleEdges.find(edge);
                if(it != visibleEdges.end())
                {
                    visibleEdges.erase(it);
                }
                else
                {
                    visibleEdges.insert(edge);
                }
            }
        }
    }
    if(silhouetteCorners.size() == 0)
    {
        return planes;
    }
    // calculate weighted near plane
    CR_VECTOR3 nearFaceNormal;
    for(size_t q=0;q<nearQuadAreas.size();++q)
    {
        nearFaceNormal += nearQuadNormals[q] * nearPlanesArea / (nearQuadAreas[q] * nearQuadFactors[q]); 
    }
    nearFaceNormal *= -1.0f;    // should face aways from camera
    nearFaceNormal.Normalize();
    // find furthest silhouette vertex
    unsigned int furthestVertex = 0;
    float maxDist = CoordToCoordDist(pCorners[silhouetteCorners[0]], pCameraPos);
    for(int s=1;s<silhouetteCorners.size();++s)
    {
        float newDist = CoordToCoordDist(pCorners[silhouetteCorners[s]], pCameraPos);
        if(newDist > maxDist)
        {
            furthestVertex = s;
        }
    }
    float d = -DotProductVec3(pCorners[silhouetteCorners[furthestVertex]], nearFaceNormal);
    planes.emplace_back(CR_VECTOR4(nearFaceNormal.x, nearFaceNormal.y, nearFaceNormal.z, d));
    pPlanesSigns.emplace_back(GetSigns(nearFaceNormal));
    // continue with 'sides' of the volume, based on the edges without duplicates
    for(auto && tEdge : visibleEdges)
    {    
        CR_VECTOR3 faceNormal = CalculateFaceNormal(pCameraPos, pCorners[tEdge.x], pCorners[tEdge.y]);
        if(!TriangleFacingPoint(faceNormal, pCameraPos, pCenter))
        {
            faceNormal *= -1.0f;
        }
        faceNormal.Normalize();
        float d = -DotProductVec3(pCameraPos, faceNormal);
        
        planes.emplace_back(CR_VECTOR4(faceNormal.x, faceNormal.y, faceNormal.z, d));
        pPlanesSigns.emplace_back(GetSigns(faceNormal));
    }
    return planes;
}

And a quick recording of the result:

Crealysm game & engine development: http://www.crealysm.com

Looking for a passionate, disciplined and structured producer? PM me

Advertisement