1
0
Fork 0
mirror of https://git.coom.tech/drummyfish/small3dlib.git synced 2025-01-05 03:36: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 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 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

View file

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

View file

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

View file

@ -10,7 +10,7 @@
license: CC0 1.0 (public domain)
found at https://creativecommons.org/publicdomain/zero/1.0/
+ additional waiver of all IP
version: 0.860d
version: 0.900d
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
@ -203,8 +203,7 @@ typedef uint16_t S3L_Index;
expensive, but the results will be geometrically correct, even though
barycentric correction is not performed so texturing artifacts will
appear. Can be ideal with S3L_FLAT.
3: NOT IMPLEMENTED YET
Perform both geometrical and barycentric correction of triangle crossing
3: Perform both geometrical and barycentric correction of triangle crossing
the near plane. This is significantly more expensive but results in
correct rendering. */
@ -249,7 +248,7 @@ typedef uint16_t S3L_Index;
#endif
#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
#ifndef S3L_COMPUTE_DEPTH
@ -556,7 +555,7 @@ typedef struct
S3L_Unit barycentric[3]; /**< Barycentric coords correspond to the three
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
S3L_FRACTIONS_PER_UNIT (including), but due to
rounding error may fall outside this range (you
@ -1853,6 +1852,16 @@ void S3L_newFrame(void)
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(
S3L_Vec4 point0,
S3L_Vec4 point1,
@ -2337,6 +2346,37 @@ void S3L_drawTriangle(
*barycentric2 =
S3L_FRACTIONS_PER_UNIT - *barycentric0 - *barycentric1;
#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);
} // 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
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,
S3L_Index triangleIndex,
S3L_Mat4 matrix,
@ -2582,9 +2623,9 @@ uint8_t _S3L_projectTriangle(
_S3L_projectVertex(model,triangleIndex,1,matrix,&(transformed[1]));
_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 behind = 0;
uint8_t infrontI[3];
@ -2602,6 +2643,15 @@ uint8_t _S3L_projectTriangle(
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 \
S3L_Unit ratio =\
((transformed[be].z - S3L_NEAR) * S3L_FRACTIONS_PER_UNIT) /\
@ -2612,7 +2662,15 @@ uint8_t _S3L_projectTriangle(
transformed[in].y = transformed[be].y -\
((transformed[be].y - transformed[in].y) * ratio) /\
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)
{
@ -2620,8 +2678,17 @@ uint8_t _S3L_projectTriangle(
for (uint8_t i = 0; i < 2; ++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
_S3L_projectedTriangleState = 1;
}
}
else if (infront == 1)
@ -2631,20 +2698,41 @@ uint8_t _S3L_projectTriangle(
transformed[4] = 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)
{
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
}
#if S3L_NEAR_CROSS_STRATEGY == 3
_S3L_triangleRemapBarycentrics[infrontI[0]] =
_S3L_triangleRemapBarycentrics[4];
#endif
transformed[infrontI[0]] = transformed[4];
_S3L_mapProjectedVertexToScreen(&transformed[3],focalLength);
_S3L_mapProjectedVertexToScreen(&transformed[4],focalLength);
_S3L_mapProjectedVertexToScreen(&transformed[5],focalLength);
result = 1;
_S3L_projectedTriangleState = 2;
}
#undef interpolateVertex
@ -2653,8 +2741,6 @@ uint8_t _S3L_projectTriangle(
_S3L_mapProjectedVertexToScreen(&transformed[0],focalLength);
_S3L_mapProjectedVertexToScreen(&transformed[1],focalLength);
_S3L_mapProjectedVertexToScreen(&transformed[2],focalLength);
return result;
}
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,
no gain was seen. */
uint8_t split = _S3L_projectTriangle(model,triangleIndex,matFinal,
_S3L_projectTriangle(model,triangleIndex,matFinal,
scene.camera.focalLength,transformed);
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,
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],
modelIndex, triangleIndex);
}
#else
S3L_UNUSED(split);
if (S3L_sortArrayLength >= S3L_MAX_TRIANGES_DRAWN)
break;
@ -2799,15 +2892,24 @@ void S3L_drawScene(S3L_Scene scene)
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. */
uint8_t split = _S3L_projectTriangle(model,triangleIndex,matFinal,
scene.camera.focalLength,transformed);
_S3L_projectTriangle(model,triangleIndex,matFinal,scene.camera.focalLength,
transformed);
S3L_drawTriangle(transformed[0],transformed[1],transformed[2],modelIndex,
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],
modelIndex, triangleIndex);
}
}
#endif
}