1
0
Fork 0
mirror of https://git.coom.tech/drummyfish/small3dlib.git synced 2024-11-23 20:59:58 +01:00

Fix rasterization bug (holes)

This commit is contained in:
Miloslav Číž 2019-06-13 03:29:17 +02:00
parent 70622f2ba4
commit 5f09e37291
3 changed files with 47 additions and 62 deletions

View file

@ -13,7 +13,7 @@
#define S3L_FLAT 0
#define S3L_STRICT_NEAR_CULLING 1
#define S3L_PERSPECTIVE_CORRECTION 0
#define S3L_PERSPECTIVE_CORRECTION 2
#define S3L_SORT 0
#define S3L_STENCIL_BUFFER 0
#define S3L_Z_BUFFER 1

View file

@ -557,9 +557,8 @@ static inline void S3L_mapProjectionPlaneToScreen(
S3L_ScreenCoord *screenY);
/** Draws a triangle according to given config. The vertices are specified in
projection-plane space (NOT screen space!) -- they wll be mapped to screen
space by thies function. If perspective correction is enabled, each vertex
has to have a depth (Z position in camera space) specified in the Z
Screen Space space (pixels). If perspective correction is enabled, each
vertex has to have a depth (Z position in camera space) specified in the Z
component. */
void S3L_drawTriangle(
S3L_Vec4 point0,
@ -1624,22 +1623,10 @@ void S3L_drawTriangle(
point2.z = point2.z >= S3L_NEAR ? point2.z : S3L_NEAR;
#endif
S3L_Vec4 *tPointPP, *lPointPP, *rPointPP; /* points in projction plane space
(in Units, normalized by
S3L_Vec4 *tPointSS, *lPointSS, *rPointSS; /* points in Screen Space (in
S3L_Units, normalized by
S3L_FRACTIONS_PER_UNIT) */
S3L_ScreenCoord x0, y0, x1, y1, x2, y2; /* points in screen space (pixel
coordinates) */
S3L_mapProjectionPlaneToScreen(point0,&x0,&y0);
S3L_mapProjectionPlaneToScreen(point1,&x1,&y1);
S3L_mapProjectionPlaneToScreen(point2,&x2,&y2);
S3L_ScreenCoord
tPointSx, tPointSy, // top point coords, in screen space
lPointSx, lPointSy, // left point coords, in screen space
rPointSx, rPointSy; // right point coords, in screen space
S3L_Unit *barycentric0; // bar. coord that gets higher from L to R
S3L_Unit *barycentric1; // bar. coord that gets higher from R to L
S3L_Unit *barycentric2; // bar. coord that gets higher from bottom up
@ -1648,38 +1635,33 @@ void S3L_drawTriangle(
#define assignPoints(t,a,b)\
{\
tPointSx = x##t;\
tPointSy = y##t;\
tPointPP = &point##t;\
tPointSS = &point##t;\
barycentric2 = &(p.barycentric[t]);\
if (S3L_triangleWinding(x##t,y##t,x##a,y##a,x##b,y##b) >= 0)\
if (S3L_triangleWinding(point##t.x,point##t.y,point##a.x,point##a.y,\
point##b.x,point##b.y) >= 0)\
{\
lPointSx = x##a; lPointSy = y##a;\
rPointSx = x##b; rPointSy = y##b;\
lPointPP = &point##a; rPointPP = &point##b;\
lPointSS = &point##a; rPointSS = &point##b;\
barycentric0 = &(p.barycentric[b]);\
barycentric1 = &(p.barycentric[a]);\
}\
else\
{\
lPointSx = x##b; lPointSy = y##b;\
rPointSx = x##a; rPointSy = y##a;\
lPointPP = &point##b; rPointPP = &point##a;\
lPointSS = &point##b; rPointSS = &point##a;\
barycentric0 = &(p.barycentric[a]);\
barycentric1 = &(p.barycentric[b]);\
}\
}
if (y0 <= y1)
if (point0.y <= point1.y)
{
if (y0 <= y2)
if (point0.y <= point2.y)
assignPoints(0,1,2)
else
assignPoints(2,0,1)
}
else
{
if (y1 <= y2)
if (point1.y <= point2.y)
assignPoints(1,0,2)
else
assignPoints(2,0,1)
@ -1688,14 +1670,15 @@ void S3L_drawTriangle(
#undef assignPoints
#if S3L_FLAT
p.depth = (tPointPP->z + lPointPP->z + rPointPP->z) / 3;
p.depth = (tPointSS->z + lPointSS->z + rPointSS->z) / 3;
*barycentric0 = S3L_FRACTIONS_PER_UNIT / 3;
*barycentric1 = S3L_FRACTIONS_PER_UNIT / 3;
*barycentric2 = S3L_FRACTIONS_PER_UNIT - 2 * (S3L_FRACTIONS_PER_UNIT / 3);
#endif
p.triangleSize[0] = rPointSx - lPointSx;
p.triangleSize[1] = (rPointSy > lPointSy ? rPointSy : lPointSy) - tPointSy;
p.triangleSize[0] = rPointSS->x - lPointSS->x;
p.triangleSize[1] = (rPointSS->y > lPointSS->y ? rPointSS->y : lPointSS->y)
- tPointSS->y;
// now draw the triangle line by line:
@ -1704,20 +1687,20 @@ void S3L_drawTriangle(
int splitOnLeft; /* whether splitY is the y coord. of left or right
point */
if (rPointSy <= lPointSy)
if (rPointSS->y <= lPointSS->y)
{
splitY = rPointSy;
splitY = rPointSS->y;
splitOnLeft = 0;
endY = lPointSy;
endY = lPointSS->y;
}
else
{
splitY = lPointSy;
splitY = lPointSS->y;
splitOnLeft = 1;
endY = rPointSy;
endY = rPointSS->y;
}
S3L_ScreenCoord currentY = tPointSy;
S3L_ScreenCoord currentY = tPointSS->y;
/* We'll be using an algorithm similar to Bresenham line algorithm. The
specifics of this algorithm are among others:
@ -1756,8 +1739,8 @@ void S3L_drawTriangle(
S3L_FastLerpState lDepthFLS, rDepthFLS;
#define initDepthFLS(s,p1,p2)\
s##DepthFLS.valueScaled = p1##PointPP->z << S3L_FAST_LERP_QUALITY;\
s##DepthFLS.stepScaled = ((p2##PointPP->z << S3L_FAST_LERP_QUALITY) -\
s##DepthFLS.valueScaled = p1##PointSS->z << S3L_FAST_LERP_QUALITY;\
s##DepthFLS.stepScaled = ((p2##PointSS->z << S3L_FAST_LERP_QUALITY) -\
s##DepthFLS.valueScaled) / (s##Dy != 0 ? s##Dy : 1);
#else
#define initDepthFLS(s,p1,p2) ;
@ -1769,9 +1752,9 @@ void S3L_drawTriangle(
p2 - point to (t, l or r)
down - whether the side coordinate goes top-down or vice versa */
#define initSide(s,p1,p2,down)\
s##X = p1##PointSx;\
s##Dx = p2##PointSx - p1##PointSx;\
s##Dy = p2##PointSy - p1##PointSy;\
s##X = p1##PointSS->x;\
s##Dx = p2##PointSS->x - p1##PointSS->x;\
s##Dy = p2##PointSS->y - p1##PointSS->y;\
initDepthFLS(s,p1,p2)\
s##SideFLS.stepScaled = (S3L_FRACTIONS_PER_UNIT << S3L_FAST_LERP_QUALITY)\
/ (s##Dy != 0 ? s##Dy : 1);\
@ -1813,13 +1796,13 @@ void S3L_drawTriangle(
the above after split. */
tPointRecipZ = (S3L_FRACTIONS_PER_UNIT * S3L_FRACTIONS_PER_UNIT)
/ S3L_nonZero(tPointPP->z);
/ S3L_nonZero(tPointSS->z);
lPointRecipZ = (S3L_FRACTIONS_PER_UNIT * S3L_FRACTIONS_PER_UNIT)
/ S3L_nonZero(lPointPP->z);
/ S3L_nonZero(lPointSS->z);
rPointRecipZ = (S3L_FRACTIONS_PER_UNIT * S3L_FRACTIONS_PER_UNIT)
/ S3L_nonZero(rPointPP->z);
/ S3L_nonZero(rPointSS->z);
lRecip0 = tPointRecipZ;
lRecip1 = lPointRecipZ;
@ -2237,10 +2220,9 @@ int8_t S3L_triangleWinding(
}
/**
Checks if given triangle (in Projection Plane space) is at least partially
visible, i.e. returns false if the triangle is either completely outside
the frustum (left, right, top, bottom, near) or is invisible due to
backface culling.
Checks if given triangle (in Screen Space) is at least partially visible,
i.e. returns false if the triangle is either completely outside the frustum
(left, right, top, bottom, near) or is invisible due to backface culling.
*/
static inline int8_t S3L_triangleIsVisible(
S3L_Vec4 p0,
@ -2252,17 +2234,16 @@ static inline int8_t S3L_triangleIsVisible(
(p0.c cmp (v) && p1.c cmp (v) && p2.c cmp (v))
if ( // outside frustum?
#if S3L_STRICT_NEAR_CULLING
p0.z <= S3L_NEAR || p1.z <= S3L_NEAR || p2.z <= S3L_NEAR ||
// ^ partially in front of NEAR?
#else
clipTest(z,<=,S3L_NEAR) || // completely in front of NEAR?
#endif
clipTest(x,<,-1 * S3L_FRACTIONS_PER_UNIT) ||
clipTest(x,>,S3L_FRACTIONS_PER_UNIT) ||
clipTest(y,<,-1 * S3L_PROJECTION_PLANE_HEIGHT / 2) ||
clipTest(y,>,S3L_PROJECTION_PLANE_HEIGHT / 2)
clipTest(x,<,0) ||
clipTest(x,>=,S3L_RESOLUTION_X) ||
clipTest(y,<,0) ||
clipTest(y,>,S3L_RESOLUTION_Y)
)
return 0;
@ -2273,8 +2254,8 @@ static inline int8_t S3L_triangleIsVisible(
int8_t winding =
S3L_triangleWinding(p0.x,p0.y,p1.x,p1.y,p2.x,p2.y);
if ((backfaceCulling == 1 && winding < 0) ||
(backfaceCulling == 2 && winding > 0))
if ((backfaceCulling == 1 && winding > 0) ||
(backfaceCulling == 2 && winding < 0))
return 0;
}
@ -2317,6 +2298,13 @@ void _S3L_projectVertex(
on NEAR, the triangle will be culled. */
S3L_perspectiveDivide(result,focalLength);
S3L_ScreenCoord sX, sY;
S3L_mapProjectionPlaneToScreen(*result,&sX,&sY);
result->x = sX;
result->y = sY;
}
void S3L_drawScene(S3L_Scene scene)

View file

@ -76,9 +76,6 @@ bugs:
- Any of them can exceed the range <0,511>
- b0 + b1 can be > 511, which causes b2 (= 511 - b0 - b1) to be negative!
- There still ocassionally appear holes between rasterized triangles -- can be
well seen in Pokitto demo when drawing with single color.
repeated:
- valgrind (and similar) checks