shithub: libgraphics

Download patch

ref: d3b48593b0fee862ea60687336031fd9374ab667
parent: 6eab9793d68feb3282714083b355d38c6c36400b
author: rodri <rgl@antares-labs.eu>
date: Sat Aug 10 13:57:02 EDT 2024

experimental A-buffer implementation.

very simple, without anti-aliasing, made for getting
OIT (order independent transparency) rendering of
arbitrary objects.

also added switches for blending, depth testing and the
A-buffer to the camera.

--- a/camera.c
+++ b/camera.c
@@ -126,6 +126,7 @@
 
 	c = emalloc(sizeof *c);
 	memset(c, 0, sizeof *c);
+	c->enabledepth = 1;
 	return c;
 }
 
--- a/fb.c
+++ b/fb.c
@@ -212,6 +212,16 @@
 }
 
 static void
+resetAbuf(Abuf *buf)
+{
+	while(buf->nact--)
+		free(buf->act[buf->nact]->items);
+	free(buf->act);
+	free(buf->stk);
+	memset(buf, 0, sizeof *buf);
+}
+
+static void
 framebufctl_reset(Framebufctl *ctl)
 {
 	Framebuf *fb;
@@ -218,6 +228,7 @@
 
 	/* address the back buffer—resetting the front buffer is VERBOTEN */
 	fb = ctl->getbb(ctl);
+	resetAbuf(&fb->abuf);
 	memset(fb->nb, 0, Dx(fb->r)*Dy(fb->r)*4);
 	memsetf(fb->zb, Inf(-1), Dx(fb->r)*Dy(fb->r));
 	memset(fb->cb, 0, Dx(fb->r)*Dy(fb->r)*4);
--- a/graphics.h
+++ b/graphics.h
@@ -54,6 +54,9 @@
 typedef struct Renderer Renderer;
 typedef struct Rendertime Rendertime;
 typedef struct Renderjob Renderjob;
+typedef struct Fragment Fragment;
+typedef struct Astk Astk;
+typedef struct Abuf Abuf;
 typedef struct Framebuf Framebuf;
 typedef struct Framebufctl Framebufctl;
 typedef struct Viewport Viewport;
@@ -246,11 +249,34 @@
 	Renderjob *next;
 };
 
+struct Fragment
+{
+	Color c;
+	float z;
+};
+
+struct Astk
+{
+	Point p;
+	Fragment *items;
+	ulong size;
+	int active;
+};
+
+struct Abuf
+{
+	QLock;
+	Astk *stk;	/* framebuffer fragment stacks */
+	Astk **act;	/* active fragment stacks */
+	ulong nact;
+};
+
 struct Framebuf
 {
 	ulong *cb;	/* color buffer */
 	float *zb;	/* z/depth buffer */
 	ulong *nb;	/* normals buffer (DBG only) */
+	Abuf abuf;	/* A-buffer */
 	Rectangle r;
 };
 
@@ -300,6 +326,9 @@
 	Matrix3 proj;		/* VCS to clip space xform */
 	Projection projtype;
 	int cullmode;
+	int enableblend;
+	int enabledepth;
+	int enableAbuff;
 
 	struct {
 		uvlong min, avg, max, acc, n, v;
--- a/render.c
+++ b/render.c
@@ -35,12 +35,14 @@
 }
 
 static void
-pixel(Framebuf *fb, Point p, Color c)
+pixel(Framebuf *fb, Point p, Color c, int blend)
 {
 	Color dc;
 
-	dc = srgb2linear(ul2col(getpixel(fb, p)));
-	c = lerp3(dc, c, c.a);	/* SoverD */
+	if(blend){
+		dc = srgb2linear(ul2col(getpixel(fb, p)));
+		c = lerp3(dc, c, c.a);	/* SoverD */
+	}
 	putpixel(fb, p, col2ul(linear2srgb(c)));
 }
 
@@ -75,6 +77,57 @@
 	return sa <= 0;
 }
 
+static void
+pushtoAbuf(Framebuf *fb, Point p, Color c, float z)
+{
+	Abuf *buf;
+	Astk *stk;
+	int i;
+
+	buf = &fb->abuf;
+	stk = &buf->stk[p.y*Dx(fb->r) + p.x];
+	stk->items = erealloc(stk->items, ++stk->size*sizeof(*stk->items));
+
+//fprint(2, "stk %#p items %#p size %lud (%d bytes)\n", stk, stk->items, stk->size, sizeof(*stk));
+
+	for(i = 0; i < stk->size; i++)
+		if(z < stk->items[i].z)
+			break;
+
+	if(i < stk->size){
+		memmove(&stk->items[i+1], &stk->items[i], (stk->size-1 - i)*sizeof(*stk->items));
+		stk->items[i] = (Fragment){c, z};
+	}else
+		stk->items[stk->size-1] = (Fragment){c, z};
+
+	if(!stk->active){
+		stk->active++;
+		stk->p = p;
+		qlock(buf);
+		buf->act = erealloc(buf->act, ++buf->nact*sizeof(*buf->act));
+		buf->act[buf->nact-1] = stk;
+		qunlock(buf);
+//fprint(2, "act %#p nact %lud (%d bytes)\n", buf->act, buf->nact, sizeof(*buf->act));
+	}
+}
+
+static void
+squashAbuf(Framebuf *fb, int blend)
+{
+	Abuf *buf;
+	Astk *stk;
+	int i;
+
+	buf = &fb->abuf;
+	for(i = 0; i < buf->nact; i++){
+		stk = buf->act[i];
+		while(stk->size--)
+			pixel(fb, stk->p, stk->items[stk->size].c, blend);
+		/* write to the depth buffer as well */
+//		fb->zb[stk->p.x + stk->p.y*Dx(fb->r)] = stk->items[stk->size].z;
+	}
+}
+
 static Point3
 _barycoords(Triangle2 t, Point2 p)
 {
@@ -115,14 +168,19 @@
 		p = Pt(prim.v[0].p.x, prim.v[0].p.y);
 
 		z = fclamp(prim.v[0].p.z, 0, 1);
-		if(z <= params->fb->zb[p.x + p.y*Dx(params->fb->r)])
-			break;
-		params->fb->zb[p.x + p.y*Dx(params->fb->r)] = z;
+		if(params->camera->enabledepth){
+			if(z <= params->fb->zb[p.x + p.y*Dx(params->fb->r)])
+				break;
+			params->fb->zb[p.x + p.y*Dx(params->fb->r)] = z;
+		}
 
 		fsp.v = dupvertex(&prim.v[0]);
 		fsp.p = p;
 		c = params->fshader(&fsp);
-		pixel(params->fb, p, c);
+		if(params->camera->enableAbuff)
+			pushtoAbuf(params->fb, p, c, z);
+		else
+			pixel(params->fb, p, c, params->camera->enableblend);
 		delvattrs(&fsp.v);
 		break;
 	case PLine:
@@ -159,9 +217,11 @@
 
 			z = flerp(prim.v[0].p.z, prim.v[1].p.z, perc);
 			/* TODO get rid of the bounds check and make sure the clipping doesn't overflow */
-			if(!ptinrect(p, params->fb->r) || z <= params->fb->zb[p.x + p.y*Dx(params->fb->r)])
-				goto discard;
-			params->fb->zb[p.x + p.y*Dx(params->fb->r)] = z;
+			if(params->camera->enabledepth){
+				if(!ptinrect(p, params->fb->r) || z <= params->fb->zb[p.x + p.y*Dx(params->fb->r)])
+					goto discard;
+				params->fb->zb[p.x + p.y*Dx(params->fb->r)] = z;
+			}
 
 			/* interpolate z⁻¹ and get actual z */
 			z = flerp(prim.v[0].p.w, prim.v[1].p.w, perc);
@@ -173,7 +233,10 @@
 
 			fsp.p = p;
 			c = params->fshader(&fsp);
-			pixel(params->fb, p, c);
+			if(params->camera->enableAbuff)
+				pushtoAbuf(params->fb, p, c, z);
+			else
+				pixel(params->fb, p, c, params->camera->enableblend);
 			delvattrs(&fsp.v);
 discard:
 			if(steep) SWAP(int, &p.x, &p.y);
@@ -206,9 +269,11 @@
 					continue;
 
 				z = fberp(prim.v[0].p.z, prim.v[1].p.z, prim.v[2].p.z, bc);
-				if(z <= params->fb->zb[p.x + p.y*Dx(params->fb->r)])
-					continue;
-				params->fb->zb[p.x + p.y*Dx(params->fb->r)] = z;
+				if(params->camera->enabledepth){
+					if(z <= params->fb->zb[p.x + p.y*Dx(params->fb->r)])
+						continue;
+					params->fb->zb[p.x + p.y*Dx(params->fb->r)] = z;
+				}
 
 				/* interpolate z⁻¹ and get actual z */
 				z = fberp(prim.v[0].p.w, prim.v[1].p.w, prim.v[2].p.w, bc);
@@ -220,7 +285,10 @@
 
 				fsp.p = p;
 				c = params->fshader(&fsp);
-				pixel(params->fb, p, c);
+				if(params->camera->enableAbuff)
+					pushtoAbuf(params->fb, p, c, z);
+				else
+					pixel(params->fb, p, c, params->camera->enableblend);
 				pixeln(params->fb, p, fsp.v.n);
 				delvattrs(&fsp.v);
 			}
@@ -249,6 +317,8 @@
 		/* end of job */
 		if(params->entity == nil){
 			if(decref(params->job) < 1){
+				if(params->job->camera->enableAbuff)
+					squashAbuf(params->job->fb, params->job->camera->enableblend);
 				nbsend(params->job->donec, nil);
 				free(params);
 			}
@@ -569,6 +639,12 @@
 		if(sc->nents < 1){
 			nbsend(job->donec, nil);
 			continue;
+		}
+
+		/* initialize the A-buffer */
+		if(job->camera->enableAbuff){
+			job->fb->abuf.stk = emalloc(Dx(job->fb->r)*Dy(job->fb->r)*sizeof(Astk));
+			memset(job->fb->abuf.stk, 0, Dx(job->fb->r)*Dy(job->fb->r)*sizeof(Astk));
 		}
 
 		for(ent = sc->ents.next; ent != &sc->ents; ent = ent->next){