浏览代码

fix and improve polygon clipping functions

pull/4832/head
Bigfoot71 3 周前
父节点
当前提交
793d56c701
共有 1 个文件被更改,包括 122 次插入115 次删除
  1. +122
    -115
      src/external/rlsw.h

+ 122
- 115
src/external/rlsw.h 查看文件

@ -1320,144 +1320,151 @@ static inline void sw_project_ndc_to_screen(float screen[2], const float ndc[4])
/* === Triangle Rendering Part === */ /* === Triangle Rendering Part === */
static inline bool sw_triangle_clip_w(sw_vertex_t polygon[SW_MAX_CLIPPED_POLYGON_VERTICES], int* vertexCounter) #define DEFINE_CLIP_FUNC(name, FUNC_IS_INSIDE, FUNC_COMPUTE_T) \
{ static inline int sw_clip_##name( \
sw_vertex_t input[SW_MAX_CLIPPED_POLYGON_VERTICES]; sw_vertex_t output[SW_MAX_CLIPPED_POLYGON_VERTICES], \
for (int i = 0; i < SW_MAX_CLIPPED_POLYGON_VERTICES; i++) { const sw_vertex_t input[SW_MAX_CLIPPED_POLYGON_VERTICES], \
input[i] = polygon[i]; int n) \
} { \
const sw_vertex_t *prev = &input[n - 1]; \
int inputCounter = *vertexCounter; int prevInside = FUNC_IS_INSIDE(prev->homogeneous); \
*vertexCounter = 0; int outputCount = 0; \
\
const sw_vertex_t *prevVt = &input[inputCounter-1]; for (int i = 0; i < n; i++) { \
char prevDot = (prevVt->homogeneous[3] < SW_CLIP_EPSILON) ? -1 : 1; const sw_vertex_t *curr = &input[i]; \
int currInside = FUNC_IS_INSIDE(curr->homogeneous); \
for (int i = 0; i < inputCounter; i++) { \
char currDot = (input[i].homogeneous[3] < SW_CLIP_EPSILON) ? -1 : 1; /* If transition between interior/exterior, calculate intersection point */ \
if (prevDot*currDot < 0) { if (prevInside != currInside) { \
polygon[(*vertexCounter)++] = sw_lerp_vertex_PNTCH(prevVt, &input[i], float t = FUNC_COMPUTE_T(prev->homogeneous, curr->homogeneous); \
(SW_CLIP_EPSILON - prevVt->homogeneous[3]) / (input[i].homogeneous[3] - prevVt->homogeneous[3])); output[outputCount++] = sw_lerp_vertex_PNTCH(prev, curr, t); \
} } \
if (currDot > 0) { \
polygon[(*vertexCounter)++] = input[i]; /* If current vertex inside, add it */ \
} if (currInside) { \
prevDot = currDot; output[outputCount++] = *curr; \
prevVt = &input[i]; } \
\
prev = curr; \
prevInside = currInside; \
} \
\
return outputCount; \
}
#define IS_INSIDE_PLANE_W(h) ((h)[3] >= SW_CLIP_EPSILON)
#define IS_INSIDE_PLANE_X_POS(h) ((h)[0] <= (h)[3])
#define IS_INSIDE_PLANE_X_NEG(h) (-(h)[0] <= (h)[3])
#define IS_INSIDE_PLANE_Y_POS(h) ((h)[1] <= (h)[3])
#define IS_INSIDE_PLANE_Y_NEG(h) (-(h)[1] <= (h)[3])
#define IS_INSIDE_PLANE_Z_POS(h) ((h)[2] <= (h)[3])
#define IS_INSIDE_PLANE_Z_NEG(h) (-(h)[2] <= (h)[3])
#define COMPUTE_T_PLANE_W(hPrev, hCurr) ((SW_CLIP_EPSILON - (hPrev)[3]) / ((hCurr)[3] - (hPrev)[3]))
#define COMPUTE_T_PLANE_X_POS(hPrev, hCurr) (((hPrev)[3] - (hPrev)[0]) / (((hPrev)[3] - (hPrev)[0]) - ((hCurr)[3] - (hCurr)[0])))
#define COMPUTE_T_PLANE_X_NEG(hPrev, hCurr) (((hPrev)[3] + (hPrev)[0]) / (((hPrev)[3] + (hPrev)[0]) - ((hCurr)[3] + (hCurr)[0])))
#define COMPUTE_T_PLANE_Y_POS(hPrev, hCurr) (((hPrev)[3] - (hPrev)[1]) / (((hPrev)[3] - (hPrev)[1]) - ((hCurr)[3] - (hCurr)[1])))
#define COMPUTE_T_PLANE_Y_NEG(hPrev, hCurr) (((hPrev)[3] + (hPrev)[1]) / (((hPrev)[3] + (hPrev)[1]) - ((hCurr)[3] + (hCurr)[1])))
#define COMPUTE_T_PLANE_Z_POS(hPrev, hCurr) (((hPrev)[3] - (hPrev)[2]) / (((hPrev)[3] - (hPrev)[2]) - ((hCurr)[3] - (hCurr)[2])))
#define COMPUTE_T_PLANE_Z_NEG(hPrev, hCurr) (((hPrev)[3] + (hPrev)[2]) / (((hPrev)[3] + (hPrev)[2]) - ((hCurr)[3] + (hCurr)[2])))
DEFINE_CLIP_FUNC(w, IS_INSIDE_PLANE_W, COMPUTE_T_PLANE_W)
DEFINE_CLIP_FUNC(x_pos, IS_INSIDE_PLANE_X_POS, COMPUTE_T_PLANE_X_POS)
DEFINE_CLIP_FUNC(x_neg, IS_INSIDE_PLANE_X_NEG, COMPUTE_T_PLANE_X_NEG)
DEFINE_CLIP_FUNC(y_pos, IS_INSIDE_PLANE_Y_POS, COMPUTE_T_PLANE_Y_POS)
DEFINE_CLIP_FUNC(y_neg, IS_INSIDE_PLANE_Y_NEG, COMPUTE_T_PLANE_Y_NEG)
DEFINE_CLIP_FUNC(z_pos, IS_INSIDE_PLANE_Z_POS, COMPUTE_T_PLANE_Z_POS)
DEFINE_CLIP_FUNC(z_neg, IS_INSIDE_PLANE_Z_NEG, COMPUTE_T_PLANE_Z_NEG)
static inline bool sw_triangle_clip(sw_vertex_t polygon[SW_MAX_CLIPPED_POLYGON_VERTICES], int* vertexCounter)
{
sw_vertex_t tmp[SW_MAX_CLIPPED_POLYGON_VERTICES];
int n = *vertexCounter;
#define CLIP_AGAINST_PLANE(FUNC_CLIP) \
{ \
n = FUNC_CLIP(tmp, polygon, n); \
if (n == 0) return false; \
for (int i = 0; i < n; i++) { \
polygon[i] = tmp[i]; \
} \
} }
return *vertexCounter > 0; CLIP_AGAINST_PLANE(sw_clip_w);
} CLIP_AGAINST_PLANE(sw_clip_x_pos);
CLIP_AGAINST_PLANE(sw_clip_x_neg);
static inline bool sw_triangle_clip_xyz(sw_vertex_t polygon[SW_MAX_CLIPPED_POLYGON_VERTICES], int* vertexCounter) CLIP_AGAINST_PLANE(sw_clip_y_pos);
{ CLIP_AGAINST_PLANE(sw_clip_y_neg);
for (int iAxis = 0; iAxis < 3; iAxis++) CLIP_AGAINST_PLANE(sw_clip_z_pos);
{ CLIP_AGAINST_PLANE(sw_clip_z_neg);
if (*vertexCounter == 0) return false;
sw_vertex_t input[SW_MAX_CLIPPED_POLYGON_VERTICES];
int inputCounter;
const sw_vertex_t *prevVt;
char prevDot;
// Clip against first plane
for (int i = 0; i < SW_MAX_CLIPPED_POLYGON_VERTICES; i++) {
input[i] = polygon[i];
}
inputCounter = *vertexCounter;
*vertexCounter = 0;
prevVt = &input[inputCounter-1];
prevDot = (prevVt->homogeneous[iAxis] <= prevVt->homogeneous[3]) ? 1 : -1;
for (int i = 0; i < inputCounter; i++) {
char currDot = (input[i].homogeneous[iAxis] <= input[i].homogeneous[3]) ? 1 : -1;
if (prevDot * currDot <= 0) {
polygon[(*vertexCounter)++] = sw_lerp_vertex_PNTCH(prevVt, &input[i], (prevVt->homogeneous[3] - prevVt->homogeneous[iAxis]) /
((prevVt->homogeneous[3] - prevVt->homogeneous[iAxis]) - (input[i].homogeneous[3] - input[i].homogeneous[iAxis])));
}
if (currDot > 0) {
polygon[(*vertexCounter)++] = input[i];
}
prevDot = currDot;
prevVt = &input[i];
}
if (*vertexCounter == 0) return false;
// Clip against opposite plane
for (int i = 0; i < SW_MAX_CLIPPED_POLYGON_VERTICES; i++) {
input[i] = polygon[i];
}
inputCounter = *vertexCounter;
*vertexCounter = 0;
prevVt = &input[inputCounter-1];
prevDot = (-prevVt->homogeneous[iAxis] <= prevVt->homogeneous[3]) ? 1 : -1;
for (int i = 0; i < inputCounter; i++) { *vertexCounter = n;
char currDot = (-input[i].homogeneous[iAxis] <= input[i].homogeneous[3]) ? 1 : -1;
if (prevDot*currDot <= 0) {
polygon[(*vertexCounter)++] = sw_lerp_vertex_PNTCH(prevVt, &input[i], (prevVt->homogeneous[3] + prevVt->homogeneous[iAxis]) /
((prevVt->homogeneous[3] + prevVt->homogeneous[iAxis]) - (input[i].homogeneous[3] + input[i].homogeneous[iAxis])));
}
if (currDot > 0) {
polygon[(*vertexCounter)++] = input[i];
}
prevDot = currDot;
prevVt = &input[i];
}
}
return o">*vertexCounter > 0; return n > 0;
} }
static inline void sw_triangle_project_and_clip(sw_vertex_t polygon[SW_MAX_CLIPPED_POLYGON_VERTICES], int* vertexCounter) static inline void sw_triangle_project_and_clip(sw_vertex_t polygon[SW_MAX_CLIPPED_POLYGON_VERTICES], int* vertexCounter)
{ {
// Step 1: MVP projection for all vertices
for (int i = 0; i < *vertexCounter; i++) { for (int i = 0; i < *vertexCounter; i++) {
sw_vertex_t *v = polygon + i; sw_vec4_transform(polygon[i].homogeneous, polygon[i].position, RLSW.matMVP);
sw_vec4_transform(v->homogeneous, v->position, RLSW.matMVP);
} }
// Step 2: Face culling - discard triangles facing away
if (RLSW.stateFlags & SW_STATE_CULL_FACE) { if (RLSW.stateFlags & SW_STATE_CULL_FACE) {
float x0 = polygon[0].homogeneous[0], y0 = polygon[0].homogeneous[1];
float x1 = polygon[1].homogeneous[0], y1 = polygon[1].homogeneous[1];
float x2 = polygon[2].homogeneous[0], y2 = polygon[2].homogeneous[1];
float sgnArea = (x1 - x0) * (y2 - y0) - (x2 - x0) * (y1 - y0); // NOTE: Face culling is done before clipping to avoid unnecessary computations.
if ((RLSW.cullFace == SW_FRONT && sgnArea >= 0) || (RLSW.cullFace == SW_BACK && sgnArea <= 0)) { // However, culling requires NDC coordinates, while clipping must be done
// in homogeneous space to correctly interpolate newly generated vertices.
// This means we need to compute 1/W twice:
// - Once before clipping for face culling.
// - Again after clipping for the new vertices.
const float invW0 = 1.0f / polygon[0].homogeneous[3];
const float invW1 = 1.0f / polygon[1].homogeneous[3];
const float invW2 = 1.0f / polygon[2].homogeneous[3];
// Compute the signed 2D area (cross product in Z)
const float x0 = polygon[0].homogeneous[0] * invW0, y0 = polygon[0].homogeneous[1] * invW0;
const float x1 = polygon[1].homogeneous[0] * invW1, y1 = polygon[1].homogeneous[1] * invW1;
const float x2 = polygon[2].homogeneous[0] * invW2, y2 = polygon[2].homogeneous[1] * invW2;
const float sgnArea = (x1 - x0) * (y2 - y0) - (x2 - x0) * (y1 - y0);
// Discard the triangle if it faces the culled direction
if ((RLSW.cullFace == SW_FRONT) ? (sgnArea >= 0) : (sgnArea <= 0)) {
*vertexCounter = 0; *vertexCounter = 0;
return; return;
} }
} }
// Step 3: Clipping and perspective projection
if (sw_triangle_clip(polygon, vertexCounter) && *vertexCounter >= 3) {
if (sw_triangle_clip_w(polygon, vertexCounter) && sw_triangle_clip_xyz(polygon, vertexCounter)) { // Transformation to screen space and normalization
for (int i = 0; i < *vertexCounter; i++) { for (int i = 0; i < *vertexCounter; i++) {
sw_vertex_t *v = polygon + i; sw_vertex_t *v = o">&polygon[i]; // Use &polygon[i] instead of polygon + i
// Calculation of the reciprocal of W for normalization // Calculation of the reciprocal of W for normalization
// as well as perspective correct attributes // as well as perspective-correct attributes
v->homogeneous[3] = 1.0f / v->homogeneous[3]; const float invW = 1.0f / v->homogeneous[3];
v->homogeneous[3] = invW;
// Division of XYZ coordinates by weight // Division of XYZ coordinates by weight
v->homogeneous[0] *= v->homogeneous[3]; v->homogeneous[0] *= invW;
v->homogeneous[1] *= v->homogeneous[3]; v->homogeneous[1] *= invW;
v->homogeneous[2] *= v->homogeneous[3]; v->homogeneous[2] *= invW;
// Division of texture coordinates (perspective class="o">-correct)
// Division of texture coordinates (perspective correct) v->texcoord[0] *= invW;
v->texcoord[0] *= v->homogeneous[3]; v->texcoord[1] *= invW;
v->texcoord[1] *= v->homogeneous[3]; // Division of colors (perspective class="o">-correct)
v->color[0] *= invW;
// Division of colors (perspective correct) v->color[1] *= invW;
v->color[0] *= v->homogeneous[3]; v->color[2] *= invW;
v->color[1] *= v->homogeneous[3]; v->color[3] *= invW;
v->color[2] *= v->homogeneous[3]; // Transformation to screen space
v->color[3] *= v->homogeneous[3];
// Transform to screen space
sw_project_ndc_to_screen(v->screen, v->homogeneous); sw_project_ndc_to_screen(v->screen, v->homogeneous);
} }
} }

||||||
x
 
000:0
正在加载...
取消
保存