From 96eb8b3c74e8d95579dbd8a3727b7c25f4d49ba2 Mon Sep 17 00:00:00 2001 From: rodri Date: Wed, 6 Mar 2024 12:41:00 +0000 Subject: add a texture sampler with nearest and bilinear routines. pass the material reference along with the vertices. also implemented back-face culling, but it's disabled for now. --- graphics.h | 28 +++++++++++++++++ mkfile | 1 + render.c | 100 +++++++++++++++++++++++++++++++++------------------------- texture.c | 104 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 4 files changed, 191 insertions(+), 42 deletions(-) create mode 100644 texture.c diff --git a/graphics.h b/graphics.h index 3477b33..85b60ae 100644 --- a/graphics.h +++ b/graphics.h @@ -38,6 +38,28 @@ struct Color double r, g, b, a; }; +/* + * a more general approach worth investigating. + * it could be made to handle types other than double. + * + * examples: + * double intens; + * addvattr(v, "intensity", 1, &intens); + * + * Point3 p; + * addvattr(v, "normal", 3, &p); + * + * Matrix3 m; + * addvattr(v, "proj", 4*4, m); + */ +//struct Vertexattr +//{ +// char *id; +// int type; +// ulong len; +// double val[]; +//}; + struct Vertexattr { char *id; @@ -54,6 +76,7 @@ struct Vertex Point3 n; /* surface normal */ Color c; /* shading color */ Point2 uv; /* texture coordinate */ + OBJMaterial *mtl; /* TODO it'd be neat to use a dynamic hash table instead */ Vertexattr *attrs; /* attributes (aka varyings) */ ulong nattrs; @@ -223,6 +246,11 @@ void delscene(Scene*); void addvattr(Vertex*, char*, int, void*); Vertexattr *getvattr(Vertex*, char*); +/* texture */ +Color neartexsampler(Memimage*, Point2); +Color bilitexsampler(Memimage*, Point2); +Color texture(Memimage*, Point2, Color(*)(Memimage*, Point2)); + /* util */ double fmin(double, double); double fmax(double, double); diff --git a/mkfile b/mkfile index 8964317..7a05817 100644 --- a/mkfile +++ b/mkfile @@ -7,6 +7,7 @@ OFILES=\ render.$O\ scene.$O\ vertex.$O\ + texture.$O\ alloc.$O\ fb.$O\ shadeop.$O\ diff --git a/render.c b/render.c index 5ebb45d..c4e5ab5 100644 --- a/render.c +++ b/render.c @@ -30,6 +30,17 @@ isvisible(Point3 p) return 1; } +static int +isfacingback(Triangle t) +{ + double sa; /* signed area */ + + sa = t[0].p.x * t[1].p.y - t[0].p.y * t[1].p.x + + t[1].p.x * t[2].p.y - t[1].p.y * t[2].p.x + + t[2].p.x * t[0].p.y - t[2].p.y * t[0].p.x; + return sa <= 0; +} + static void mulsdm(double r[6], double m[6][4], Point3 p) { @@ -213,7 +224,7 @@ world2clip(Camera *c, Point3 p) static Point3 clip2ndc(Point3 p) { - p.w = 1.0/p.w; + p.w = p.w == 0? 1: 1.0/p.w; p.x *= p.w; p.y *= p.w; p.z *= p.w; @@ -275,7 +286,7 @@ rasterize(SUparams *params, Triangle t, Memimage *frag) FSparams fsp; Triangle2 tâ‚‚; Rectangle bbox; - Point p, tp; + Point p; Point3 bc; double z, depth; uchar cbuf[4]; @@ -325,30 +336,10 @@ rasterize(SUparams *params, Triangle t, Memimage *frag) bc = mulpt3(bc, z); berpvertex(&fsp.v, &t[0], &t[1], &t[2], bc); - 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: - unloadmemimage(params->entity->mdl->tex, rectaddpt(UR, tp), cbuf+1, sizeof cbuf - 1); - cbuf[0] = 0xFF; - break; - case RGBA32: - unloadmemimage(params->entity->mdl->tex, rectaddpt(UR, tp), cbuf, sizeof cbuf); - break; - case XRGB32: - unloadmemimage(params->entity->mdl->tex, rectaddpt(UR, tp), cbuf, sizeof cbuf); - memmove(cbuf+1, cbuf, 3); - cbuf[0] = 0xFF; - break; - } - }else{ - 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; - } + 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; fsp.p = p; pixel(params->fb->cb, p, params->fshader(&fsp)); @@ -386,17 +377,32 @@ shaderunit(void *arg) nt = 1; /* start with one. after clipping it might change */ idxtab = &(*ep)->indextab[OBJVGeometric]; - t[0][0].p = Pt3(verts[idxtab->indices[0]].x,verts[idxtab->indices[0]].y,verts[idxtab->indices[0]].z,verts[idxtab->indices[0]].w); - t[0][1].p = Pt3(verts[idxtab->indices[1]].x,verts[idxtab->indices[1]].y,verts[idxtab->indices[1]].z,verts[idxtab->indices[1]].w); - t[0][2].p = Pt3(verts[idxtab->indices[2]].x,verts[idxtab->indices[2]].y,verts[idxtab->indices[2]].z,verts[idxtab->indices[2]].w); + t[0][0].p = Pt3(verts[idxtab->indices[0]].x, + verts[idxtab->indices[0]].y, + verts[idxtab->indices[0]].z, + verts[idxtab->indices[0]].w); + t[0][1].p = Pt3(verts[idxtab->indices[1]].x, + verts[idxtab->indices[1]].y, + verts[idxtab->indices[1]].z, + verts[idxtab->indices[1]].w); + t[0][2].p = Pt3(verts[idxtab->indices[2]].x, + verts[idxtab->indices[2]].y, + verts[idxtab->indices[2]].z, + verts[idxtab->indices[2]].w); idxtab = &(*ep)->indextab[OBJVNormal]; if(idxtab->nindex == 3){ - t[0][0].n = Vec3(nverts[idxtab->indices[0]].i, nverts[idxtab->indices[0]].j, nverts[idxtab->indices[0]].k); + t[0][0].n = Vec3(nverts[idxtab->indices[0]].i, + nverts[idxtab->indices[0]].j, + nverts[idxtab->indices[0]].k); t[0][0].n = normvec3(t[0][0].n); - t[0][1].n = Vec3(nverts[idxtab->indices[1]].i, nverts[idxtab->indices[1]].j, nverts[idxtab->indices[1]].k); + t[0][1].n = Vec3(nverts[idxtab->indices[1]].i, + nverts[idxtab->indices[1]].j, + nverts[idxtab->indices[1]].k); t[0][1].n = normvec3(t[0][1].n); - t[0][2].n = Vec3(nverts[idxtab->indices[2]].i, nverts[idxtab->indices[2]].j, nverts[idxtab->indices[2]].k); + t[0][2].n = Vec3(nverts[idxtab->indices[2]].i, + nverts[idxtab->indices[2]].j, + nverts[idxtab->indices[2]].k); t[0][2].n = normvec3(t[0][2].n); }else{ /* TODO build a list of per-vertex normals earlier */ @@ -406,18 +412,19 @@ shaderunit(void *arg) idxtab = &(*ep)->indextab[OBJVTexture]; if(params->entity->mdl->tex != nil && idxtab->nindex == 3){ - t[0][0].uv = Pt2(tverts[idxtab->indices[0]].u, tverts[idxtab->indices[0]].v, 1); - t[0][1].uv = Pt2(tverts[idxtab->indices[1]].u, tverts[idxtab->indices[1]].v, 1); - t[0][2].uv = Pt2(tverts[idxtab->indices[2]].u, tverts[idxtab->indices[2]].v, 1); + t[0][0].uv = Pt2(tverts[idxtab->indices[0]].u, + tverts[idxtab->indices[0]].v, 1); + t[0][1].uv = Pt2(tverts[idxtab->indices[1]].u, + tverts[idxtab->indices[1]].v, 1); + t[0][2].uv = Pt2(tverts[idxtab->indices[2]].u, + tverts[idxtab->indices[2]].v, 1); }else{ t[0][0].uv = t[0][1].uv = t[0][2].uv = Vec2(0,0); } for(i = 0; i < 3; i++){ - t[0][i].c.r = (*ep)->mtl != nil? (*ep)->mtl->Kd.r: 1; - 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].c = Pt3(1,1,1,1); + t[0][i].mtl = (*ep)->mtl; t[0][i].attrs = nil; t[0][i].nattrs = 0; } @@ -436,12 +443,21 @@ shaderunit(void *arg) nt = cliptriangle(t); while(nt--){ - t[nt][0].p = ndc2viewport(params->fb, clip2ndc(t[nt][0].p)); - t[nt][1].p = ndc2viewport(params->fb, clip2ndc(t[nt][1].p)); - t[nt][2].p = ndc2viewport(params->fb, clip2ndc(t[nt][2].p)); + t[nt][0].p = clip2ndc(t[nt][0].p); + t[nt][1].p = clip2ndc(t[nt][1].p); + t[nt][2].p = clip2ndc(t[nt][2].p); + + /* culling */ +// if(isfacingback(t[nt])) +// goto skiptri; + + t[nt][0].p = ndc2viewport(params->fb, t[nt][0].p); + t[nt][1].p = ndc2viewport(params->fb, t[nt][1].p); + t[nt][2].p = ndc2viewport(params->fb, t[nt][2].p); rasterize(params, t[nt], frag); +//skiptri: delvattrs(&t[nt][0]); delvattrs(&t[nt][1]); delvattrs(&t[nt][2]); diff --git a/texture.c b/texture.c new file mode 100644 index 0000000..40539db --- /dev/null +++ b/texture.c @@ -0,0 +1,104 @@ +#include +#include +#include +#include +#include +#include +#include "libobj/obj.h" +#include "graphics.h" +#include "internal.h" + +/* + * uv-coords belong to the 4th quadrant (v grows bottom-up), + * hence the need to reverse the v coord. + */ +static Point +uv2tp(Point2 uv, Memimage *i) +{ + assert(uv.x >= 0 && uv.x <= 1 && uv.y >= 0 && uv.y <= 1); + return Pt(uv.x*Dx(i->r), (1 - uv.y)*Dy(i->r)); +} + +static Color +ul2col(ulong l) +{ + Color c; + + c.a = (l & 0xff)/255.0; + c.b = (l>>8 & 0xff)/255.0; + c.g = (l>>16 & 0xff)/255.0; + c.r = (l>>24 & 0xff)/255.0; + return c; +} + +static Color +cbuf2col(uchar b[4]) +{ + Color c; + + c.a = b[0] / 255.0; + c.b = b[1] / 255.0; + c.g = b[2] / 255.0; + c.r = b[3] / 255.0; + return c; +} + +static Color +_memreadpixel(Memimage *i, Point sp) +{ + uchar cbuf[4]; + + switch(i->chan){ + case RGB24: + unloadmemimage(i, rectaddpt(UR, sp), cbuf+1, sizeof cbuf - 1); + cbuf[0] = 0xFF; + break; + case RGBA32: + unloadmemimage(i, rectaddpt(UR, sp), cbuf, sizeof cbuf); + break; + case XRGB32: + unloadmemimage(i, rectaddpt(UR, sp), cbuf, sizeof cbuf); + memmove(cbuf+1, cbuf, 3); + cbuf[0] = 0xFF; + break; + } + + return cbuf2col(cbuf); +} + +Color +neartexsampler(Memimage *i, Point2 uv) +{ + return _memreadpixel(i, uv2tp(uv, i)); +} + +Color +bilitexsampler(Memimage *i, Point2 uv) +{ + Rectangle r; + Color c1, c2; + + r = rectaddpt(UR, uv2tp(uv, i)); + if(r.min.x < i->r.min.x){ + r.min.x++; + r.max.x++; + }if(r.min.y < i->r.min.y){ + r.min.y++; + r.max.y++; + }if(r.max.x >= i->r.max.x){ + r.min.x--; + r.max.x--; + }if(r.max.y >= i->r.max.y){ + r.min.y--; + r.max.y--; + } + c1 = lerp3(_memreadpixel(i, r.min), _memreadpixel(i, Pt(r.max.x, r.min.y)), 0.5); + c2 = lerp3(_memreadpixel(i, Pt(r.min.x, r.max.y)), _memreadpixel(i, r.max), 0.5); + return lerp3(c1, c2, 0.5); +} + +Color +texture(Memimage *i, Point2 uv, Color(*sampler)(Memimage*,Point2)) +{ + return sampler(i, uv); +} -- cgit v1.2.3