ref: 98978f5594cb28a60f8f3db8259922871992ce67
parent: 10d114ef7415da437bd27fc6d637db257bb27634
author: rodri <rgl@antares-labs.eu>
date: Fri Apr 11 15:21:55 EDT 2025
incremental triangle rasterization part 1 (barycentric coordinates) use the barycoords gradient over the triangle bbox to step it, without the need to recompute the coordinates at every pixel. this alleviates the hot loop a little, and should be more noticeable when we do the same with the vertex attributes. part 2 incoming.
--- a/internal.h
+++ b/internal.h
@@ -8,6 +8,8 @@
typedef struct Tilerparam Tilerparam;
typedef struct Rasterparam Rasterparam;
typedef struct Rastertask Rastertask;
+typedef struct pGradient pGradient;
+typedef struct vGradient vGradient;
struct Polygon
{
@@ -46,6 +48,20 @@
Primitive p;
};
+struct pGradient
+{
+ Point3 p0;
+ Point3 dx;
+ Point3 dy;
+};
+
+struct vGradient
+{
+ Vertex v0;
+ Vertex dx;
+ Vertex dy;
+};
+
/* alloc */
void *_emalloc(ulong);
void *_erealloc(void*, ulong);
@@ -74,7 +90,8 @@
Vertex _dupvertex(Vertex*);
void _lerpvertex(Vertex*, Vertex*, Vertex*, double);
void _berpvertex(Vertex*, Vertex*, Vertex*, Vertex*, Point3);
-void _perspdiv(Vertex*, double);
+void _addvertex(Vertex*, Vertex*);
+void _mulvertex(Vertex*, double);
void _delvattrs(Vertex*);
void _fprintvattrs(int, Vertex*);
void _addvattr(Vertexattrs*, char*, int, void*);
--- a/render.c
+++ b/render.c
@@ -86,14 +86,24 @@
static int
isfacingback(Primitive *p)
{
- double sa; /* signed area */
+ double sa; /* signed double area */
sa = p->v[0].p.x * p->v[1].p.y - p->v[0].p.y * p->v[1].p.x +
p->v[1].p.x * p->v[2].p.y - p->v[1].p.y * p->v[2].p.x +
p->v[2].p.x * p->v[0].p.y - p->v[2].p.y * p->v[0].p.x;
- return sa <= 0;
+ return sa <= 0; /* 0 - CCW, 1 - CW */
}
+static int
+istoporleft(Point2 *e0, Point2 *e1)
+{
+ Point2 e01;
+
+ e01 = subpt2(*e1, *e0);
+ return e01.y > 0 /* left */
+ || (e01.y == 0 && e01.x < 0); /* top */
+}
+
static void
pushtoAbuf(Framebuf *fb, Point p, Color c, float z)
{
@@ -149,21 +159,6 @@
}
}
-static Point3
-_barycoords(Triangle2 t, Point2 p)
-{
- Point2 p0p1 = subpt2(t.p1, t.p0);
- Point2 p0p2 = subpt2(t.p2, t.p0);
- Point2 pp0 = subpt2(t.p0, p);
-
- Point3 v = crossvec3(Vec3(p0p2.x, p0p1.x, pp0.x), Vec3(p0p2.y, p0p1.y, pp0.y));
-
- /* handle degenerate triangles—i.e. the ones where every point lies on the same line */
- if(fabs(v.z) < ε1)
- return Pt3(-1,-1,-1,1);
- return Pt3(1 - (v.x + v.y)/v.z, v.y/v.z, v.x/v.z, 1);
-}
-
static void
rasterizept(Rastertask *task)
{
@@ -306,6 +301,22 @@
}
}
+static Point3
+_barycoords(Triangle2 t, Point2 p)
+{
+ Point2 p0p1 = subpt2(t.p1, t.p0);
+ Point2 p0p2 = subpt2(t.p2, t.p0);
+ Point2 pp0 = subpt2(t.p0, p);
+
+ Point3 v = crossvec3(Vec3(p0p2.x, p0p1.x, pp0.x), Vec3(p0p2.y, p0p1.y, pp0.y));
+
+ /* handle degenerate triangles—i.e. the ones where every point lies on the same line */
+ if(fabs(v.z) < ε1)
+ return Pt3(-1,-1,-1,1);
+ /* barycoords and signed double area */
+ return Pt3(1 - (v.x + v.y)/v.z, v.y/v.z, v.x/v.z, v.z);
+}
+
static void
rasterizetri(Rastertask *task)
{
@@ -312,6 +323,7 @@
SUparams *params;
Raster *cr, *zr;
Primitive *prim;
+ pGradient ∇bc;
Triangle2 t;
Point p;
Point3 bc;
@@ -331,19 +343,41 @@
t.p1 = (Point2){prim->v[1].p.x, prim->v[1].p.y, 1};
t.p2 = (Point2){prim->v[2].p.x, prim->v[2].p.y, 1};
- _perspdiv(prim->v+0, prim->v[0].p.w);
- _perspdiv(prim->v+1, prim->v[1].p.w);
- _perspdiv(prim->v+2, prim->v[2].p.w);
+ ∇bc.p0 = _barycoords(t, (Point2){task->wr.min.x+0.5, task->wr.min.y+0.5, 1});
+ ∇bc.dx = divpt3((Point3){t.p2.y - t.p1.y, t.p0.y - t.p2.y, t.p1.y - t.p0.y, 0}, ∇bc.p0.w);
+ ∇bc.dy = divpt3((Point3){t.p1.x - t.p2.x, t.p2.x - t.p0.x, t.p0.x - t.p1.x, 0}, ∇bc.p0.w);
- for(p.y = task->wr.min.y; p.y < task->wr.max.y; p.y++)
+ /* TODO find a good method to apply the fill rule */
+// if(istoporleft(&t.p1, &t.p2)){
+// ∇bc.p0.x -= 1/∇bc.p0.w;
+// ∇bc.dx.x -= 1/∇bc.p0.w;
+// ∇bc.dy.x -= 1/∇bc.p0.w;
+// }
+// if(istoporleft(&t.p2, &t.p0)){
+// ∇bc.p0.y -= 1/∇bc.p0.w;
+// ∇bc.dx.y -= 1/∇bc.p0.w;
+// ∇bc.dy.y -= 1/∇bc.p0.w;
+// }
+// if(istoporleft(&t.p0, &t.p1)){
+// ∇bc.p0.z -= 1/∇bc.p0.w;
+// ∇bc.dx.z -= 1/∇bc.p0.w;
+// ∇bc.dy.z -= 1/∇bc.p0.w;
+// }
+
+ /* perspective divide vertex attributes */
+ _mulvertex(prim->v+0, prim->v[0].p.w);
+ _mulvertex(prim->v+1, prim->v[1].p.w);
+ _mulvertex(prim->v+2, prim->v[2].p.w);
+
+ for(p.y = task->wr.min.y; p.y < task->wr.max.y; p.y++){
+ bc = ∇bc.p0;
for(p.x = task->wr.min.x; p.x < task->wr.max.x; p.x++){
- bc = _barycoords(t, (Point2){p.x+0.5,p.y+0.5,1});
if(bc.x < 0 || bc.y < 0 || bc.z < 0)
- continue;
+ goto discard;
z = fberp(prim->v[0].p.z, prim->v[1].p.z, prim->v[2].p.z, bc);
if((ropts & RODepth) && z <= getdepth(zr, p))
- continue;
+ goto discard;
/* interpolate z⁻¹ and get actual z */
pcz = fberp(prim->v[0].p.w, prim->v[1].p.w, prim->v[2].p.w, bc);
@@ -350,13 +384,12 @@
pcz = 1.0/(pcz < ε1? ε1: pcz);
/* perspective-correct attribute interpolation */
- bc = mulpt3(bc, pcz);
- _berpvertex(task->fsp->v, prim->v+0, prim->v+1, prim->v+2, bc);
+ _berpvertex(task->fsp->v, prim->v+0, prim->v+1, prim->v+2, mulpt3(bc, pcz));
task->fsp->p = p;
c = params->stab->fs(task->fsp);
if(c.a == 0) /* discard non-colors */
- continue;
+ goto discard;
if(ropts & RODepth)
putdepth(zr, p, z);
if(ropts & ROAbuff)
@@ -371,7 +404,11 @@
task->clipr->min = minpt(task->clipr->min, p);
task->clipr->max = maxpt(task->clipr->max, addpt(p, (Point){1,1}));
}
+discard:
+ bc = addpt3(bc, ∇bc.dx);
}
+ ∇bc.p0 = addpt3(∇bc.p0, ∇bc.dy);
+ }
}
static void
@@ -619,8 +656,14 @@
/* culling */
switch(params->camera->cullmode){
- case CullFront: if(!isfacingback(p)) goto skiptri; break;
- case CullBack: if(isfacingback(p)) goto skiptri; break;
+ case CullFront:
+ if(!isfacingback(p))
+ goto skiptri;
+ break;
+ case CullBack:
+ if(isfacingback(p))
+ goto skiptri;
+ break;
}
p->v[0].p = ndc2viewport(params->fb, p->v[0].p);
--- a/vertex.c
+++ b/vertex.c
@@ -96,23 +96,38 @@
}
}
-/*
- * perspective divide vertex attributes
- */
void
-_perspdiv(Vertex *v, double z⁻¹)
+_addvertex(Vertex *a, Vertex *b)
{
+ Vertexattr *va, *vb;
+
+ a->n = addpt3(a->n, b->n);
+ a->c = addpt3(a->c, b->c);
+ a->uv = addpt2(a->uv, b->uv);
+ a->tangent = addpt3(a->tangent, b->tangent);
+ for(va = a->attrs; va < a->attrs + a->nattrs; va++){
+ vb = b->attrs + (va - a->attrs);
+ if(va->type == VAPoint)
+ va->p = addpt3(va->p, vb->p);
+ else
+ va->n += vb->n;
+ }
+}
+
+void
+_mulvertex(Vertex *v, double s)
+{
Vertexattr *va;
- v->n = mulpt3(v->n, z⁻¹);
- v->c = mulpt3(v->c, z⁻¹);
- v->uv = mulpt2(v->uv, z⁻¹);
- v->tangent = mulpt3(v->tangent, z⁻¹);
+ v->n = mulpt3(v->n, s);
+ v->c = mulpt3(v->c, s);
+ v->uv = mulpt2(v->uv, s);
+ v->tangent = mulpt3(v->tangent, s);
for(va = v->attrs; va < v->attrs + v->nattrs; va++){
if(va->type == VAPoint)
- va->p = mulpt3(va->p, z⁻¹);
+ va->p = mulpt3(va->p, s);
else
- va->n *= z⁻¹;
+ va->n *= s;
}
}