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);
+}