shithub: libgraphics

Download patch

ref: 6b71bf694a9fa2e694f3e70a2067e0021b2c2f07
parent: e0baf147d655409b721e41b0e3effabd39a96b34
author: rodri <rgl@antares-labs.eu>
date: Sat Mar 2 19:40:51 EST 2024

add user-defined vertex attributes (varyings) and improve the interpolation code.

--- a/graphics.h
+++ b/graphics.h
@@ -11,7 +11,13 @@
 	LIGHT_SPOT,
 };
 
+enum {
+	VAPoint,
+	VANumber,
+};
+
 typedef struct Color Color;
+typedef struct Vertexattr Vertexattr;
 typedef struct Vertex Vertex;
 typedef struct LightSource LightSource;
 typedef struct Material Material;
@@ -32,16 +38,25 @@
 	double r, g, b, a;
 };
 
-struct Vertex
+struct Vertexattr
 {
-	Point3 p;	/* position */
-	Point3 n;	/* surface normal */
-	Color c;	/* shading color */
-	Point2 uv;	/* texture coordinate */
+	char *id;
+	int type;
+	union {
+		Point3 p;
+		double n;
+	};
+};
 
-	/* TODO these attributes should be replaced by a hash table */
-	double intensity;
-	Point3 pos;
+struct Vertex
+{
+	Point3 p;		/* position */
+	Point3 n;		/* surface normal */
+	Color c;		/* shading color */
+	Point2 uv;		/* texture coordinate */
+	/* TODO it'd be neat to use a dynamic hash table instead */
+	Vertexattr *attrs;	/* attributes (aka varyings) */
+	ulong nattrs;
 };
 
 typedef Vertex Triangle[3];
@@ -102,9 +117,9 @@
 {
 	SUparams *su;
 	Memimage *frag;
-	Point p;
-	Point3 bc;
 	uchar *cbuf;
+	Point p;
+	Vertex v;		/* only for the attributes (varyings) */
 };
 
 /* shader unit params */
@@ -117,10 +132,6 @@
 	/* TODO replace with a Scene */
 	Entity *entity;
 
-	double var_intensity;
-	Point3 var_normal;
-	Point3 var_pos;
-
 	uvlong uni_time;
 
 	Point3 (*vshader)(VSparams*);
@@ -207,6 +218,10 @@
 void delentity(Entity*);
 Scene *newscene(char*);
 void delscene(Scene*);
+
+/* vertex */
+void addvattr(Vertex*, char*, int, void*);
+Vertexattr *getvattr(Vertex*, char*);
 
 /* util */
 double fmin(double, double);
--- a/internal.h
+++ b/internal.h
@@ -12,6 +12,13 @@
 /* render */
 void shade(Framebuf*, Scene*, Shader*);
 
+/* vertex */
+Vertex dupvertex(Vertex*);
+void lerpvertex(Vertex*, Vertex*, Vertex*, double);
+void berpvertex(Vertex*, Vertex*, Vertex*, Vertex*, Point3);
+void delvattrs(Vertex*);
+void fprintvattrs(int, Vertex*);
+
 /* util */
 int min(int, int);
 int max(int, int);
--- a/mkfile
+++ b/mkfile
@@ -6,6 +6,7 @@
 	viewport.$O\
 	render.$O\
 	scene.$O\
+	vertex.$O\
 	alloc.$O\
 	fb.$O\
 	shadeop.$O\
--- a/render.c
+++ b/render.c
@@ -65,6 +65,16 @@
 	*b = tmp;
 }
 
+static void
+cleanpoly(Polygon *p)
+{
+	int i;
+
+	for(i = 0; i < p->n; i++)
+		delvattrs(&p->v[i]);
+	p->n = 0;
+}
+
 /*
  * references:
  * 	- James F. Blinn, Martin E. Newell, “Clipping Using Homogeneous Coordinates”,
@@ -96,7 +106,7 @@
 	for(i = 0; i < 3; i++)
 		addvert(&Vin, t[0][i]);
 
-	for(j = 0; j < 6; j++){
+	for(j = 0; j < 6 && Vin.n > 0; j++){
 		for(i = 0; i < Vin.n; i++){
 			v0 = &Vin.v[i];
 			v1 = &Vin.v[(i+1) % Vin.n];
@@ -114,31 +124,32 @@
 			d1 = (j&1) == 0? sd1[j]: -sd1[j];
 			perc = d0/(d0 - d1);
 
-			v.p = lerp3(v0->p, v1->p, perc);
-			v.n = lerp3(v0->n, v1->n, perc);
-			v.c = lerp3((Point3)v0->c, (Point3)v1->c, perc);
-			v.uv = lerp2(v0->uv, v1->uv, perc);
-			v.intensity = flerp(v0->intensity, v1->intensity, perc);
-			v.pos = lerp3(v0->pos, v1->pos, perc);
+			lerpvertex(&v, v0, v1, perc);
 			addvert(&Vout, v);
 
 			if(sd1[j] >= 0){
 allin:
-				addvert(&Vout, *v1);
+				addvert(&Vout, dupvertex(v1));
 			}
 		}
-		if(j < 6-1){
+		cleanpoly(&Vin);
+		if(j < 6-1)
 			swappoly(&Vin, &Vout);
-			Vout.n = 0;
-		}
 	}
 
 	/* triangulate */
-	if(Vout.n >= 3)
+	if(Vout.n < 3)
+		cleanpoly(&Vout);
+	else
 		for(i = 0; i < Vout.n-2; i++, nt++){
-			t[nt][0] = Vout.v[0];
+			/*
+			 * when performing fan triangulation, indices 0 and 2
+			 * are referenced on every triangle, so duplicate them
+			 * to avoid complications during rasterization.
+			 */
+			t[nt][0] = i < Vout.n-2-1? dupvertex(&Vout.v[0]): Vout.v[0];
 			t[nt][1] = Vout.v[i+1];
-			t[nt][2] = Vout.v[i+2];
+			t[nt][2] = i < Vout.n-2-1? dupvertex(&Vout.v[i+2]): Vout.v[i+2];
 		}
 	free(Vout.v);
 	free(Vin.v);
@@ -262,8 +273,7 @@
 rasterize(SUparams *params, Triangle t, Memimage *frag)
 {
 	FSparams fsp;
-	Triangle2 t₂, tt₂;
-	Triangle3 ct;
+	Triangle2 t₂;
 	Rectangle bbox;
 	Point p, tp;
 	Point3 bc;
@@ -286,24 +296,8 @@
 	fsp.su = params;
 	fsp.frag = frag;
 	fsp.cbuf = cbuf;
+	memset(&fsp.v, 0, sizeof fsp.v);
 
-	/* perspective-divide the attributes */
-	t[0].n = mulpt3(t[0].n, t[0].p.w);
-	t[1].n = mulpt3(t[1].n, t[1].p.w);
-	t[2].n = mulpt3(t[2].n, t[2].p.w);
-	t[0].c = mulpt3(t[0].c, t[0].p.w);
-	t[1].c = mulpt3(t[1].c, t[1].p.w);
-	t[2].c = mulpt3(t[2].c, t[2].p.w);
-	t[0].uv = mulpt2(t[0].uv, t[0].p.w);
-	t[1].uv = mulpt2(t[1].uv, t[1].p.w);
-	t[2].uv = mulpt2(t[2].uv, t[2].p.w);
-	t[0].intensity = t[0].intensity*t[0].p.w;
-	t[1].intensity = t[1].intensity*t[1].p.w;
-	t[2].intensity = t[2].intensity*t[2].p.w;
-	t[0].pos = mulpt3(t[0].pos, t[0].p.w);
-	t[1].pos = mulpt3(t[1].pos, t[1].p.w);
-	t[2].pos = mulpt3(t[2].pos, t[2].p.w);
-
 	for(p.y = bbox.min.y; p.y < bbox.max.y; p.y++)
 		for(p.x = bbox.min.x; p.x < bbox.max.x; p.x++){
 			bc = barycoords(t₂, Pt2(p.x,p.y,1));
@@ -324,14 +318,16 @@
 			z = t[0].p.w*bc.x + t[1].p.w*bc.y + t[2].p.w*bc.z;
 			z = 1.0/(z < 1e-5? 1e-5: z);
 
-			if((t[0].uv.w + t[1].uv.w + t[2].uv.w) != 0){
-				/* lerp attribute and dissolve perspective */
-				tt₂.p0 = mulpt2(t[0].uv, bc.x*z);
-				tt₂.p1 = mulpt2(t[1].uv, bc.y*z);
-				tt₂.p2 = mulpt2(t[2].uv, bc.z*z);
+			/* perspective-correct attribute interpolation  */
+			bc.x *= t[0].p.w;
+			bc.y *= t[1].p.w;
+			bc.z *= t[2].p.w;
+			bc = mulpt3(bc, z);
+			berpvertex(&fsp.v, &t[0], &t[1], &t[2], bc);
 
-				tp.x = (tt₂.p0.x + tt₂.p1.x + tt₂.p2.x)*Dx(params->entity->mdl->tex->r);
-				tp.y = (1 - (tt₂.p0.y + tt₂.p1.y + tt₂.p2.y))*Dy(params->entity->mdl->tex->r);
+			if(fsp.v.uv.w != 0){
+				tp.x = fsp.v.uv.x*Dx(params->entity->mdl->tex->r);
+				tp.y = (1 - fsp.v.uv.y)*Dy(params->entity->mdl->tex->r);
 
 				switch(params->entity->mdl->tex->chan){
 				case RGB24:
@@ -348,28 +344,15 @@
 					break;
 				}
 			}else{
-				/* lerp attribute and dissolve perspective */
-				ct.p0 = mulpt3(t[0].c, bc.x*z);
-				ct.p1 = mulpt3(t[1].c, bc.y*z);
-				ct.p2 = mulpt3(t[2].c, bc.z*z);
-				cbuf[0] = (ct.p0.w + ct.p1.w + ct.p2.w)*0xFF;
-				cbuf[1] = (ct.p0.z + ct.p1.z + ct.p2.z)*0xFF;
-				cbuf[2] = (ct.p0.y + ct.p1.y + ct.p2.y)*0xFF;
-				cbuf[3] = (ct.p0.x + ct.p1.x + ct.p2.x)*0xFF;
+				cbuf[0] = fsp.v.c.a*0xFF;
+				cbuf[1] = fsp.v.c.b*0xFF;
+				cbuf[2] = fsp.v.c.g*0xFF;
+				cbuf[3] = fsp.v.c.r*0xFF;
 			}
 
-			params->var_intensity = dotvec3(Vec3(t[0].intensity, t[1].intensity, t[2].intensity), bc)*z;
-			params->var_normal = normvec3(addpt3(addpt3(
-				mulpt3(t[0].n, bc.x*z),
-				mulpt3(t[1].n, bc.y*z)),
-				mulpt3(t[2].n, bc.z*z)));
-			params->var_pos = addpt3(addpt3(
-				mulpt3(t[0].pos, bc.x*z),
-				mulpt3(t[1].pos, bc.y*z)),
-				mulpt3(t[2].pos, bc.z*z));
 			fsp.p = p;
-			fsp.bc = bc;
 			pixel(params->fb->cb, p, params->fshader(&fsp));
+			delvattrs(&fsp.v);
 		}
 }
 
@@ -435,6 +418,8 @@
 			t[0][i].c.g = (*ep)->mtl != nil? (*ep)->mtl->Kd.g: 1;
 			t[0][i].c.b = (*ep)->mtl != nil? (*ep)->mtl->Kd.b: 1;
 			t[0][i].c.a = 1;
+			t[0][i].attrs = nil;
+			t[0][i].nattrs = 0;
 		}
 
 		vsp.v = &t[0][0];
@@ -456,6 +441,10 @@
 			t[nt][2].p = ndc2viewport(params->fb, clip2ndc(t[nt][2].p));
 
 			rasterize(params, t[nt], frag);
+
+			delvattrs(&t[nt][0]);
+			delvattrs(&t[nt][1]);
+			delvattrs(&t[nt][2]);
 		}
 	}
 
--- /dev/null
+++ b/vertex.c
@@ -1,0 +1,148 @@
+#include <u.h>
+#include <libc.h>
+#include <thread.h>
+#include <draw.h>
+#include <memdraw.h>
+#include <geometry.h>
+#include "libobj/obj.h"
+#include "graphics.h"
+#include "internal.h"
+
+static void
+_addvattr(Vertex *v, Vertexattr *va)
+{
+	int i;
+
+	for(i = 0; i < v->nattrs; i++)
+		if(strcmp(v->attrs[i].id, va->id) == 0){
+			v->attrs[i] = *va;
+			return;
+		}
+	v->attrs = erealloc(v->attrs, ++v->nattrs*sizeof(*va));
+	v->attrs[v->nattrs-1] = *va;
+}
+
+static void
+copyvattrs(Vertex *d, Vertex *s)
+{
+	int i;
+
+	for(i = 0; i < s->nattrs; i++)
+		_addvattr(d, &s->attrs[i]);
+}
+
+Vertex
+dupvertex(Vertex *v)
+{
+	Vertex nv;
+
+	nv = *v;
+	nv.attrs = nil;
+	nv.nattrs = 0;
+	copyvattrs(&nv, v);
+	return nv;
+}
+
+void
+lerpvertex(Vertex *v, Vertex *v0, Vertex *v1, double t)
+{
+	Vertexattr va;
+	int i;
+
+	v->p = lerp3(v0->p, v1->p, t);
+	v->n = lerp3(v0->n, v1->n, t);
+	v->c = lerp3(v0->c, v1->c, t);
+	v->uv = lerp2(v0->uv, v1->uv, t);
+	v->attrs = nil;
+	v->nattrs = 0;
+	for(i = 0; i < v0->nattrs; i++){
+		va.id = v0->attrs[i].id;
+		va.type = v0->attrs[i].type;
+		if(va.type == VAPoint)
+			va.p = lerp3(v0->attrs[i].p, v1->attrs[i].p, t);
+		else
+			va.n = flerp(v0->attrs[i].n, v1->attrs[i].n, t);
+		_addvattr(v, &va);
+	}
+}
+
+/*
+ * perspective-correct barycentric attribute interpolation
+ */
+void
+berpvertex(Vertex *v, Vertex *v0, Vertex *v1, Vertex *v2, Point3 bc)
+{
+	Vertexattr va;
+	int i;
+
+	v->n = addpt3(addpt3(
+		mulpt3(v0->n, bc.x),
+		mulpt3(v1->n, bc.y)),
+		mulpt3(v2->n, bc.z));
+	v->c = addpt3(addpt3(
+		mulpt3(v0->c, bc.x),
+		mulpt3(v1->c, bc.y)),
+		mulpt3(v2->c, bc.z));
+	v->uv = addpt2(addpt2(
+		mulpt2(v0->uv, bc.x),
+		mulpt2(v1->uv, bc.y)),
+		mulpt2(v2->uv, bc.z));
+	v->attrs = nil;
+	v->nattrs = 0;
+	for(i = 0; i < v0->nattrs; i++){
+		va.id = v0->attrs[i].id;
+		va.type = v0->attrs[i].type;
+		if(va.type == VAPoint)
+			va.p = addpt3(addpt3(
+				mulpt3(v0->attrs[i].p, bc.x),
+				mulpt3(v1->attrs[i].p, bc.y)),
+				mulpt3(v2->attrs[i].p, bc.z));
+		else
+			va.n = dotvec3(Vec3(v0->attrs[i].n, v1->attrs[i].n, v2->attrs[i].n), bc);
+		_addvattr(v, &va);
+	}
+}
+
+void
+addvattr(Vertex *v, char *id, int type, void *val)
+{
+	Vertexattr va;
+
+	va.id = id;
+	va.type = type;
+	switch(type){
+	case VAPoint: va.p = *(Point3*)val; break;
+	case VANumber: va.n = *(double*)val; break;
+	default: sysfatal("unknown vertex attribute type '%d'", type);
+	}
+	_addvattr(v, &va);
+}
+
+Vertexattr *
+getvattr(Vertex *v, char *id)
+{
+	int i;
+
+	for(i = 0; i < v->nattrs; i++)
+		if(strcmp(v->attrs[i].id, id) == 0)
+			return &v->attrs[i];
+	return nil;
+}
+
+void
+delvattrs(Vertex *v)
+{
+	free(v->attrs);
+	v->attrs= nil;
+	v->nattrs = 0;
+}
+
+void
+fprintvattrs(int fd, Vertex *v)
+{
+	int i;
+
+	for(i = 0; i < v->nattrs; i++)
+		fprint(fd, "id %s type %d v %g\n",
+			v->attrs[i].id, v->attrs[i].type, v->attrs[i].n);
+}