Clipping and backface culling

Culling isn't bad, clipping need a better approach to cut partial triangles.
This commit is contained in:
JackCarterSmith 2024-11-02 00:41:43 +01:00
parent 54507108b3
commit 098345409f
Signed by: JackCarterSmith
GPG Key ID: 832E52F4E23F8F24
3 changed files with 85 additions and 34 deletions

View File

@ -3,6 +3,8 @@
#include "../World/DbgCube.hpp"
#include "../World/Tank.hpp"
//#define DISABLE_INVISIBLE_VERTICES
// Rendering pipeline:
// model matrix (Object SRT) -> view matrix (camera matrix inverted) -> proj matrix -> clipping -> perspective divide -> viewport transformation -> Rasterizer (draw pixels inside projected triangles on 2D screen)
@ -23,6 +25,8 @@
// * https://en.wikipedia.org/wiki/Hidden-surface_determination#Occlusion_culling
// * https://en.wikipedia.org/wiki/Bounding_volume_hierarchy
static bool VertexIsInsideClipSpace(M3D_F4& V);
Graphic3DRenderer::Graphic3DRenderer() {
mRTSize = {1280.f, 324.f};
@ -109,20 +113,23 @@ void Graphic3DRenderer::Draw(sf::RenderTexture& context) {
// Object outside frustrum clipping
projAABB.Transform(projAABB, oTMat);
M3D_ContainmentType objInFrustrum = camFrustrum.Contains(projAABB);
if (objInFrustrum != DISJOINT) {
#ifndef DISABLE_INVISIBLE_VERTICES
if (objInFrustrum != DISJOINT)
#endif
{
size_t vCount = obj->GetObjectVerticesCount();
auto& oMesh = obj->GetObjectMesh();
M3D_F3 projVertices[vCount];
M3D_F4 projVertices[vCount];
// Vertices NDC space transformation
M3D_V3TransformPersDiv(
projVertices, sizeof(M3D_F3),
// Vertices homogeneous clip space transformation
M3D_V3Transform(
projVertices, sizeof(M3D_F4),
reinterpret_cast<const M3D_F3*>(oMesh.vertices.data()), sizeof(Vertex),
vCount,
oTMat * viewProjMat
);
// Draw the object indice triangles
// Draw the object indice triangles if visible or partially clipped
sf::Vertex v_tri[4];
for (auto& objPt : oMesh.parts) {
auto indicePtr = static_cast<const uint32_t*>(objPt.indices.data());
@ -134,35 +141,53 @@ void Graphic3DRenderer::Draw(sf::RenderTexture& context) {
// Triangle clipping
//TODO: implement complete Cohen-Sutherland algo or similar
M3D_VECTOR V1 = M3D_V4LoadF3(&projVertices[indicePtr[i]]);
M3D_VECTOR V2 = M3D_V4LoadF3(&projVertices[indicePtr[i+1]]);
M3D_VECTOR V3 = M3D_V4LoadF3(&projVertices[indicePtr[i+2]]);
if (VertexIsInsideClipSpace(projVertices[indicePtr[i]]) &&
VertexIsInsideClipSpace(projVertices[indicePtr[i+1]]) &&
VertexIsInsideClipSpace(projVertices[indicePtr[i+2]]))
{
M3D_VECTOR V1 = M3D_V4LoadF4(&projVertices[indicePtr[i]]);
M3D_VECTOR V2 = M3D_V4LoadF4(&projVertices[indicePtr[i+1]]);
M3D_VECTOR V3 = M3D_V4LoadF4(&projVertices[indicePtr[i+2]]);
// Face culling
M3D_VECTOR faceNormal = M3D_TNormal(V1,V2,V3);
if (M3D_V4GetX(M3D_V3Dot(V1, faceNormal)) >= 0) {
// Do the perspective divide
V1 = M3D_V4Divide(V1, M3D_V4SplatW(V1));
V2 = M3D_V4Divide(V2, M3D_V4SplatW(V2));
V3 = M3D_V4Divide(V3, M3D_V4SplatW(V3));
V1 = M3D_V3TransformNDCToViewport(V1, 0.f, 0.f, mRTSize.x, mRTSize.y, 1.f, 100.f);
V2 = M3D_V3TransformNDCToViewport(V2, 0.f, 0.f, mRTSize.x, mRTSize.y, 1.f, 100.f);
V3 = M3D_V3TransformNDCToViewport(V3, 0.f, 0.f, mRTSize.x, mRTSize.y, 1.f, 100.f);
sf::Color vColor;
if (objInFrustrum == DISJOINT)
vColor = sf::Color::Red;
else if (objInFrustrum == INTERSECTS)
vColor = sf::Color::Yellow;
else
vColor = oMesh.vertices[indicePtr[i]].color;
if (objInFrustrum == DISJOINT) {
v_tri[0].color = sf::Color::Red;
v_tri[1].color = sf::Color::Red;
v_tri[2].color = sf::Color::Red;
} else if (objInFrustrum == INTERSECTS) {
v_tri[0].color = sf::Color::Yellow;
v_tri[1].color = sf::Color::Yellow;
v_tri[2].color = sf::Color::Yellow;
} else {
v_tri[0].color = oMesh.vertices[indicePtr[i]].color;
v_tri[1].color = oMesh.vertices[indicePtr[i+1]].color;
v_tri[2].color = oMesh.vertices[indicePtr[i+2]].color;
}
v_tri[0].position = sf::Vector2f(M3D_V4GetX(V1), M3D_V4GetY(V1));
v_tri[0].color = vColor;
v_tri[3] = v_tri[0];
v_tri[1].position = sf::Vector2f(M3D_V4GetX(V2), M3D_V4GetY(V2));
v_tri[1].color = vColor;
v_tri[2].position = sf::Vector2f(M3D_V4GetX(V3), M3D_V4GetY(V3));
v_tri[2].color = vColor;
v_tri[3] = v_tri[0];
context.draw(v_tri, 4, sf::LineStrip, sRS);
//context.draw(v_tri, 3, sf::Triangles, sRS);
}
}
}
}
}
}
}
void Graphic3DRenderer::UpdateInternalTestObjects() {
@ -177,3 +202,10 @@ void Graphic3DRenderer::UpdateInternalTestObjects() {
mRenderList[2]->SetRotation(thetaAngle3, 0.f, thetaAngle3 * 0.5f);
mRenderList[3]->SetRotation(0.f, thetaAngle, 0.f);
}
inline static bool VertexIsInsideClipSpace(M3D_F4& V) {
return (V.x > -V.w && V.x < V.w &&
V.y > -V.w && V.y < V.w &&
V.z > 0 && V.z < V.w
);
}

View File

@ -401,6 +401,7 @@ M3D_VECTOR M3D_V4OrInt(M3D_VECTOR V1, M3D_VECTOR V2) noexcept;
M3D_VECTOR M3D_V4Reciprocal(M3D_VECTOR V) noexcept;
M3D_VECTOR M3D_V4Sqrt(M3D_VECTOR V) noexcept;
M3D_VECTOR M3D_V4ModAngles(M3D_VECTOR Angles) noexcept;
bool M3D_V3LessOrEqual(M3D_VECTOR V1, M3D_VECTOR V2) noexcept;
M3D_VECTOR M3D_V3Dot(M3D_VECTOR V1, M3D_VECTOR V2) noexcept;
M3D_VECTOR M3D_V3Cross(M3D_VECTOR V1, M3D_VECTOR V2) noexcept;
M3D_VECTOR M3D_V3LengthSq(M3D_VECTOR V) noexcept;
@ -524,6 +525,8 @@ inline M3D_VECTOR M3D_V4Swizzle(M3D_VECTOR V) noexcept {
M3D_VECTOR M3D_QMultiply(M3D_VECTOR Q1, M3D_VECTOR Q2) noexcept;
M3D_VECTOR M3D_QConjugate(M3D_VECTOR Q) noexcept;
M3D_VECTOR M3D_TNormal(M3D_VECTOR P1, M3D_VECTOR P2, M3D_VECTOR P3) noexcept;
void M3D_V4SinCos(M3D_VECTOR* pSin, M3D_VECTOR* pCos, M3D_VECTOR V) noexcept;

View File

@ -1048,6 +1048,15 @@ inline M3D_VECTOR M3D_V4ModAngles(M3D_VECTOR Angles) noexcept {
#endif
}
inline bool M3D_V3LessOrEqual(M3D_VECTOR V1, M3D_VECTOR V2) noexcept {
#ifdef DISABLE_INTRINSICS
return (((V1.v4f[0] <= V2.v4f[0]) && (V1.v4f[1] <= V2.v4f[1]) && (V1.v4f[2] <= V2.v4f[2])) != 0);
#else
M3D_VECTOR vTemp = _mm_cmple_ps(V1, V2);
return (((_mm_movemask_ps(vTemp) & 7) == 7) != 0);
#endif
}
inline M3D_VECTOR M3D_V3Dot(M3D_VECTOR V1, M3D_VECTOR V2) noexcept {
#ifdef DISABLE_INTRINSICS
float fValue = V1.v4f[0] * V2.v4f[0] + V1.v4f[1] * V2.v4f[1] + V1.v4f[2] * V2.v4f[2];
@ -1244,12 +1253,12 @@ inline M3D_VECTOR M3D_V3Normalize(M3D_VECTOR V) noexcept {
inline M3D_VECTOR M3D_QMultiply(M3D_VECTOR Q1, M3D_VECTOR Q2) noexcept {
#ifdef DISABLE_INTRINSICS
M3D_V4F32 Result = { { {
M3D_V4F32 Result = {{{
(Q2.v4f[3] * Q1.v4f[0]) + (Q2.v4f[0] * Q1.v4f[3]) + (Q2.v4f[1] * Q1.v4f[2]) - (Q2.v4f[2] * Q1.v4f[1]),
(Q2.v4f[3] * Q1.v4f[1]) - (Q2.v4f[0] * Q1.v4f[2]) + (Q2.v4f[1] * Q1.v4f[3]) + (Q2.v4f[2] * Q1.v4f[0]),
(Q2.v4f[3] * Q1.v4f[2]) + (Q2.v4f[0] * Q1.v4f[1]) - (Q2.v4f[1] * Q1.v4f[0]) + (Q2.v4f[2] * Q1.v4f[3]),
(Q2.v4f[3] * Q1.v4f[3]) - (Q2.v4f[0] * Q1.v4f[0]) - (Q2.v4f[1] * Q1.v4f[1]) - (Q2.v4f[2] * Q1.v4f[2])
} } };
}}};
return Result.v;
#else
static const M3D_V4F32 ControlWZYX = {{{1.0f, -1.0f, 1.0f, -1.0f}}};
@ -1304,24 +1313,31 @@ inline M3D_VECTOR M3D_QConjugate(M3D_VECTOR Q) noexcept {
#endif
}
inline M3D_VECTOR M3D_TNormal(M3D_VECTOR P1, M3D_VECTOR P2, M3D_VECTOR P3) noexcept {
M3D_VECTOR L1 = M3D_V4Subtract(P2, P1);
M3D_VECTOR L2 = M3D_V4Subtract(P3, P1);
return M3D_V3Normalize(M3D_V3Cross(L2, L1));
}
/* -------------------------------------------------------------------------------------------------------------------------- */
inline void M3D_V4SinCos(M3D_VECTOR* pSin, M3D_VECTOR* pCos, M3D_VECTOR V) noexcept {
#ifdef DISABLE_INTRINSICS
M3D_V4F32 Sin = { { {
M3D_V4F32 Sin = {{{
sinf(V.v4f[0]),
sinf(V.v4f[1]),
sinf(V.v4f[2]),
sinf(V.v4f[3])
} } };
}}};
M3D_V4F32 Cos = { { {
M3D_V4F32 Cos = {{{
cosf(V.v4f[0]),
cosf(V.v4f[1]),
cosf(V.v4f[2]),
cosf(V.v4f[3])
} } };
}}};
*pSin = Sin.v;
*pCos = Cos.v;