1
0
Fork 0
mirror of https://git.coom.tech/drummyfish/small3dlib.git synced 2025-01-07 03:56:19 +01:00

Add new near cross strategy

This commit is contained in:
Miloslav Ciz 2022-04-16 22:29:16 +02:00
parent ad34790ec3
commit 7670658db6
4 changed files with 125 additions and 24 deletions

View file

@ -83,8 +83,7 @@ And advantages at the same time :)
- There is **no far plane**. - There is **no far plane**.
- There is **no subpixel accuracy** (PS1 style graphics). - There is **no subpixel accuracy** (PS1 style graphics).
- There is **no antialiasing**, but you can still achieve it by supersampling (render in higher resolution and downscale) or filters like FXAA. - There is **no antialiasing**, but you can still achieve it by supersampling (render in higher resolution and downscale) or filters like FXAA.
- There is a near plane but a **proper culling by it (subdividing triangles) is missing**. You can either cull whole triangles completely or "push" them by the near plane. These options are okay when drawing a model not very close to the camera, but e.g. 3D environments may suffer from artifacts. - Due to the limitations of 32bit integer arithmetics some types of movement (particularly camera) **may look jerky, and artifact may appear** in specific situations.
- Due to the limitations of 32bit integer arithmetics, some types of movement (particularly camera) **may look jerky, and artifact may appear** in specific situations.
## how to use ## how to use

View file

@ -10,7 +10,7 @@
#include <time.h> #include <time.h>
#define S3L_FLAT 0 #define S3L_FLAT 0
#define S3L_NEAR_CROSS_STRATEGY 1 #define S3L_NEAR_CROSS_STRATEGY 3
#define S3L_PERSPECTIVE_CORRECTION 2 #define S3L_PERSPECTIVE_CORRECTION 2
#define S3L_SORT 0 #define S3L_SORT 0
#define S3L_STENCIL_BUFFER 0 #define S3L_STENCIL_BUFFER 0

View file

@ -11,7 +11,7 @@
#include <stdio.h> #include <stdio.h>
#include <time.h> #include <time.h>
#define S3L_NEAR_CROSS_STRATEGY 2 #define S3L_NEAR_CROSS_STRATEGY 3
#if TEXTURES #if TEXTURES
#define S3L_PERSPECTIVE_CORRECTION 2 #define S3L_PERSPECTIVE_CORRECTION 2

View file

@ -10,7 +10,7 @@
license: CC0 1.0 (public domain) license: CC0 1.0 (public domain)
found at https://creativecommons.org/publicdomain/zero/1.0/ found at https://creativecommons.org/publicdomain/zero/1.0/
+ additional waiver of all IP + additional waiver of all IP
version: 0.860d version: 0.900d
Before including the library, define S3L_PIXEL_FUNCTION to the name of the Before including the library, define S3L_PIXEL_FUNCTION to the name of the
function you'll be using to draw single pixels (this function will be called function you'll be using to draw single pixels (this function will be called
@ -203,8 +203,7 @@ typedef uint16_t S3L_Index;
expensive, but the results will be geometrically correct, even though expensive, but the results will be geometrically correct, even though
barycentric correction is not performed so texturing artifacts will barycentric correction is not performed so texturing artifacts will
appear. Can be ideal with S3L_FLAT. appear. Can be ideal with S3L_FLAT.
3: NOT IMPLEMENTED YET 3: Perform both geometrical and barycentric correction of triangle crossing
Perform both geometrical and barycentric correction of triangle crossing
the near plane. This is significantly more expensive but results in the near plane. This is significantly more expensive but results in
correct rendering. */ correct rendering. */
@ -249,7 +248,7 @@ typedef uint16_t S3L_Index;
#endif #endif
#if S3L_PERSPECTIVE_CORRECTION #if S3L_PERSPECTIVE_CORRECTION
#define S3L_COMPUTE_DEPTH 1 // PC inevitably computes depth, so enable it #define S3L_COMPUTE_DEPTH 1 // PC inevitably computes depth, so enable it
#endif #endif
#ifndef S3L_COMPUTE_DEPTH #ifndef S3L_COMPUTE_DEPTH
@ -556,7 +555,7 @@ typedef struct
S3L_Unit barycentric[3]; /**< Barycentric coords correspond to the three S3L_Unit barycentric[3]; /**< Barycentric coords correspond to the three
vertices. These serve to locate the pixel on a vertices. These serve to locate the pixel on a
triangle and interpolate values between it's triangle and interpolate values between its
three points. Each one goes from 0 to three points. Each one goes from 0 to
S3L_FRACTIONS_PER_UNIT (including), but due to S3L_FRACTIONS_PER_UNIT (including), but due to
rounding error may fall outside this range (you rounding error may fall outside this range (you
@ -1853,6 +1852,16 @@ void S3L_newFrame(void)
S3L_stencilBufferClear(); S3L_stencilBufferClear();
} }
/*
the following serves to communicate info about if the triangle has been split
and how the barycentrics should be remapped.
*/
uint8_t _S3L_projectedTriangleState = 0; // 0 = normal, 1 = cut, 2 = split
#if S3L_NEAR_CROSS_STRATEGY == 3
S3L_Vec4 _S3L_triangleRemapBarycentrics[6];
#endif
void S3L_drawTriangle( void S3L_drawTriangle(
S3L_Vec4 point0, S3L_Vec4 point0,
S3L_Vec4 point1, S3L_Vec4 point1,
@ -2337,6 +2346,37 @@ void S3L_drawTriangle(
*barycentric2 = *barycentric2 =
S3L_FRACTIONS_PER_UNIT - *barycentric0 - *barycentric1; S3L_FRACTIONS_PER_UNIT - *barycentric0 - *barycentric1;
#endif #endif
#if S3L_NEAR_CROSS_STRATEGY == 3
if (_S3L_projectedTriangleState != 0)
{
S3L_Unit newBarycentric[3];
newBarycentric[0] = S3L_interpolateBarycentric(
_S3L_triangleRemapBarycentrics[0].x,
_S3L_triangleRemapBarycentrics[1].x,
_S3L_triangleRemapBarycentrics[2].x,
p.barycentric);
newBarycentric[1] = S3L_interpolateBarycentric(
_S3L_triangleRemapBarycentrics[0].y,
_S3L_triangleRemapBarycentrics[1].y,
_S3L_triangleRemapBarycentrics[2].y,
p.barycentric);
newBarycentric[2] = S3L_interpolateBarycentric(
_S3L_triangleRemapBarycentrics[0].z,
_S3L_triangleRemapBarycentrics[1].z,
_S3L_triangleRemapBarycentrics[2].z,
p.barycentric);
p.barycentric[0] = newBarycentric[0];
p.barycentric[1] = newBarycentric[1];
p.barycentric[2] = newBarycentric[2];
}
#endif
S3L_PIXEL_FUNCTION(&p); S3L_PIXEL_FUNCTION(&p);
} // tests passed } // tests passed
@ -2569,9 +2609,10 @@ void _S3L_mapProjectedVertexToScreen(S3L_Vec4 *vertex, S3L_Unit focalLength)
/** /**
Projects a triangle to the screen. If enabled, a triangle can be potentially Projects a triangle to the screen. If enabled, a triangle can be potentially
subdivided into two if it crosses the near plane, in which case two projected subdivided into two if it crosses the near plane, in which case two projected
triangles are returned (return value will be 1). triangles are returned (the info about splitting or cutting the triangle is
passed in global variables, see above).
*/ */
uint8_t _S3L_projectTriangle( void _S3L_projectTriangle(
const S3L_Model3D *model, const S3L_Model3D *model,
S3L_Index triangleIndex, S3L_Index triangleIndex,
S3L_Mat4 matrix, S3L_Mat4 matrix,
@ -2582,9 +2623,9 @@ uint8_t _S3L_projectTriangle(
_S3L_projectVertex(model,triangleIndex,1,matrix,&(transformed[1])); _S3L_projectVertex(model,triangleIndex,1,matrix,&(transformed[1]));
_S3L_projectVertex(model,triangleIndex,2,matrix,&(transformed[2])); _S3L_projectVertex(model,triangleIndex,2,matrix,&(transformed[2]));
uint8_t result = 0; _S3L_projectedTriangleState = 0;
#if S3L_NEAR_CROSS_STRATEGY == 2 #if S3L_NEAR_CROSS_STRATEGY == 2 || S3L_NEAR_CROSS_STRATEGY == 3
uint8_t infront = 0; uint8_t infront = 0;
uint8_t behind = 0; uint8_t behind = 0;
uint8_t infrontI[3]; uint8_t infrontI[3];
@ -2602,6 +2643,15 @@ uint8_t _S3L_projectTriangle(
behind++; behind++;
} }
#if S3L_NEAR_CROSS_STRATEGY == 3
for (int i = 0; i < 3; ++i)
S3L_vec4Init(&(_S3L_triangleRemapBarycentrics[i]));
_S3L_triangleRemapBarycentrics[0].x = S3L_FRACTIONS_PER_UNIT;
_S3L_triangleRemapBarycentrics[1].y = S3L_FRACTIONS_PER_UNIT;
_S3L_triangleRemapBarycentrics[2].z = S3L_FRACTIONS_PER_UNIT;
#endif
#define interpolateVertex \ #define interpolateVertex \
S3L_Unit ratio =\ S3L_Unit ratio =\
((transformed[be].z - S3L_NEAR) * S3L_FRACTIONS_PER_UNIT) /\ ((transformed[be].z - S3L_NEAR) * S3L_FRACTIONS_PER_UNIT) /\
@ -2612,7 +2662,15 @@ uint8_t _S3L_projectTriangle(
transformed[in].y = transformed[be].y -\ transformed[in].y = transformed[be].y -\
((transformed[be].y - transformed[in].y) * ratio) /\ ((transformed[be].y - transformed[in].y) * ratio) /\
S3L_FRACTIONS_PER_UNIT;\ S3L_FRACTIONS_PER_UNIT;\
transformed[in].z = S3L_NEAR; transformed[in].z = S3L_NEAR;\
if (beI != 0) {\
beI->x = (beI->x * ratio) / S3L_FRACTIONS_PER_UNIT;\
beI->y = (beI->y * ratio) / S3L_FRACTIONS_PER_UNIT;\
beI->z = (beI->z * ratio) / S3L_FRACTIONS_PER_UNIT;\
ratio = S3L_FRACTIONS_PER_UNIT - ratio;\
beI->x += (beB->x * ratio) / S3L_FRACTIONS_PER_UNIT;\
beI->y += (beB->y * ratio) / S3L_FRACTIONS_PER_UNIT;\
beI->z += (beB->z * ratio) / S3L_FRACTIONS_PER_UNIT; }
if (infront == 2) if (infront == 2)
{ {
@ -2620,8 +2678,17 @@ uint8_t _S3L_projectTriangle(
for (uint8_t i = 0; i < 2; ++i) for (uint8_t i = 0; i < 2; ++i)
{ {
uint8_t be = behindI[0], in = infrontI[i]; uint8_t be = behindI[0], in = infrontI[i];
#if S3L_NEAR_CROSS_STRATEGY == 3
S3L_Vec4 *beI = &(_S3L_triangleRemapBarycentrics[in]),
*beB = &(_S3L_triangleRemapBarycentrics[be]);
#else
S3L_Vec4 *beI = 0, *beB = 0;
#endif
interpolateVertex interpolateVertex
_S3L_projectedTriangleState = 1;
} }
} }
else if (infront == 1) else if (infront == 1)
@ -2631,20 +2698,41 @@ uint8_t _S3L_projectTriangle(
transformed[4] = transformed[infrontI[0]]; transformed[4] = transformed[infrontI[0]];
transformed[5] = transformed[infrontI[0]]; transformed[5] = transformed[infrontI[0]];
#if S3L_NEAR_CROSS_STRATEGY == 3
_S3L_triangleRemapBarycentrics[3] =
_S3L_triangleRemapBarycentrics[behindI[1]];
_S3L_triangleRemapBarycentrics[4] =
_S3L_triangleRemapBarycentrics[infrontI[0]];
_S3L_triangleRemapBarycentrics[5] =
_S3L_triangleRemapBarycentrics[infrontI[0]];
#endif
for (uint8_t i = 0; i < 2; ++i) for (uint8_t i = 0; i < 2; ++i)
{ {
uint8_t be = behindI[i], in = i + 4; uint8_t be = behindI[i], in = i + 4;
#if S3L_NEAR_CROSS_STRATEGY == 3
S3L_Vec4 *beI = &(_S3L_triangleRemapBarycentrics[in]),
*beB = &(_S3L_triangleRemapBarycentrics[be]);
#else
S3L_Vec4 *beI = 0, *beB = 0;
#endif
interpolateVertex interpolateVertex
} }
#if S3L_NEAR_CROSS_STRATEGY == 3
_S3L_triangleRemapBarycentrics[infrontI[0]] =
_S3L_triangleRemapBarycentrics[4];
#endif
transformed[infrontI[0]] = transformed[4]; transformed[infrontI[0]] = transformed[4];
_S3L_mapProjectedVertexToScreen(&transformed[3],focalLength); _S3L_mapProjectedVertexToScreen(&transformed[3],focalLength);
_S3L_mapProjectedVertexToScreen(&transformed[4],focalLength); _S3L_mapProjectedVertexToScreen(&transformed[4],focalLength);
_S3L_mapProjectedVertexToScreen(&transformed[5],focalLength); _S3L_mapProjectedVertexToScreen(&transformed[5],focalLength);
result = 1; _S3L_projectedTriangleState = 2;
} }
#undef interpolateVertex #undef interpolateVertex
@ -2653,8 +2741,6 @@ uint8_t _S3L_projectTriangle(
_S3L_mapProjectedVertexToScreen(&transformed[0],focalLength); _S3L_mapProjectedVertexToScreen(&transformed[0],focalLength);
_S3L_mapProjectedVertexToScreen(&transformed[1],focalLength); _S3L_mapProjectedVertexToScreen(&transformed[1],focalLength);
_S3L_mapProjectedVertexToScreen(&transformed[2],focalLength); _S3L_mapProjectedVertexToScreen(&transformed[2],focalLength);
return result;
} }
void S3L_drawScene(S3L_Scene scene) void S3L_drawScene(S3L_Scene scene)
@ -2709,7 +2795,7 @@ void S3L_drawScene(S3L_Scene scene)
already projected vertices, but after some testing this was abandoned, already projected vertices, but after some testing this was abandoned,
no gain was seen. */ no gain was seen. */
uint8_t split = _S3L_projectTriangle(model,triangleIndex,matFinal, _S3L_projectTriangle(model,triangleIndex,matFinal,
scene.camera.focalLength,transformed); scene.camera.focalLength,transformed);
if (S3L_triangleIsVisible(transformed[0],transformed[1],transformed[2], if (S3L_triangleIsVisible(transformed[0],transformed[1],transformed[2],
@ -2720,11 +2806,18 @@ void S3L_drawScene(S3L_Scene scene)
S3L_drawTriangle(transformed[0],transformed[1],transformed[2],modelIndex, S3L_drawTriangle(transformed[0],transformed[1],transformed[2],modelIndex,
triangleIndex); triangleIndex);
if (split) // draw potential subtriangle if (_S3L_projectedTriangleState == 2) // draw potential subtriangle
{
#if S3L_NEAR_CROSS_STRATEGY == 3
_S3L_triangleRemapBarycentrics[0] = _S3L_triangleRemapBarycentrics[3];
_S3L_triangleRemapBarycentrics[1] = _S3L_triangleRemapBarycentrics[4];
_S3L_triangleRemapBarycentrics[2] = _S3L_triangleRemapBarycentrics[5];
#endif
S3L_drawTriangle(transformed[3],transformed[4],transformed[5], S3L_drawTriangle(transformed[3],transformed[4],transformed[5],
modelIndex, triangleIndex); modelIndex, triangleIndex);
}
#else #else
S3L_UNUSED(split);
if (S3L_sortArrayLength >= S3L_MAX_TRIANGES_DRAWN) if (S3L_sortArrayLength >= S3L_MAX_TRIANGES_DRAWN)
break; break;
@ -2799,15 +2892,24 @@ void S3L_drawScene(S3L_Scene scene)
require a lot of memory, which for small resolutions could be even require a lot of memory, which for small resolutions could be even
worse than z-bufer. So this seems to be the best way memory-wise. */ worse than z-bufer. So this seems to be the best way memory-wise. */
uint8_t split = _S3L_projectTriangle(model,triangleIndex,matFinal, _S3L_projectTriangle(model,triangleIndex,matFinal,scene.camera.focalLength,
scene.camera.focalLength,transformed); transformed);
S3L_drawTriangle(transformed[0],transformed[1],transformed[2],modelIndex, S3L_drawTriangle(transformed[0],transformed[1],transformed[2],modelIndex,
triangleIndex); triangleIndex);
if (split) if (_S3L_projectedTriangleState == 2)
{
#if S3L_NEAR_CROSS_STRATEGY == 3
_S3L_triangleRemapBarycentrics[0] = _S3L_triangleRemapBarycentrics[3];
_S3L_triangleRemapBarycentrics[1] = _S3L_triangleRemapBarycentrics[4];
_S3L_triangleRemapBarycentrics[2] = _S3L_triangleRemapBarycentrics[5];
#endif
S3L_drawTriangle(transformed[3],transformed[4],transformed[5], S3L_drawTriangle(transformed[3],transformed[4],transformed[5],
modelIndex, triangleIndex); modelIndex, triangleIndex);
}
} }
#endif #endif
} }