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){