From 239a319b41474a35e4c9c4b7c6ae3c6e0b0b7185 Mon Sep 17 00:00:00 2001 From: rodri Date: Thu, 6 Jun 2024 17:35:09 +0000 Subject: add cubemaps. --- camera.c | 89 +++++++++++++++++++++++++++++++++++++++++++++- clip.c | 3 +- doc/libgraphics.ms | 9 +++++ graphics.h | 17 +++++++-- render.c | 1 + scene.c | 4 +++ texture.c | 102 +++++++++++++++++++++++++++++++++++++++++++++++++++++ 7 files changed, 220 insertions(+), 5 deletions(-) diff --git a/camera.c b/camera.c index 9b4c1ef..6c2be87 100644 --- a/camera.c +++ b/camera.c @@ -8,6 +8,73 @@ #include "graphics.h" #include "internal.h" +/* + * references: + * - https://learnopengl.com/Advanced-OpenGL/Cubemaps + */ +static Point3 +skyboxvs(VSparams *sp) +{ + Point3 p; + + addvattr(sp->v, "dir", VAPoint, &sp->v->p); + /* only rotate along with the camera */ + p = sp->v->p; p.w = 0; + p = world2vcs(sp->su->camera, p); p.w = 1; + p = vcs2clip(sp->su->camera, p); + /* force the cube to always be on the far plane */ + p.z = -p.w; + return p; +} + +static Color +skyboxfs(FSparams *sp) +{ + Vertexattr *va; + Color c; + + va = getvattr(&sp->v, "dir"); + c = cubemaptexture(sp->su->camera->scene->skybox, va->p, neartexsampler); + return c; +} + +static Model * +mkskyboxmodel(void) +{ + static Point3 axes[3] = {{0,1,0,0}, {1,0,0,0}, {0,0,1,0}}; + static Point3 center = {0,0,0,1}; + Model *m; + Primitive t[2]; + Point3 p, v1, v2; + int i, j, k; + + m = newmodel(); + memset(t, 0, sizeof t); + t[0].type = t[1].type = PTriangle; + + p = Vec3(-0.5,-0.5,0.5); + v1 = Vec3(1,0,0); + v2 = Vec3(0,1,0); + t[0].v[0].p = addpt3(center, p); + t[0].v[1].p = addpt3(center, addpt3(p, v1)); + t[0].v[2].p = addpt3(center, addpt3(p, addpt3(v1, v2))); + t[1].v[0] = t[0].v[0]; + t[1].v[1] = t[0].v[2]; + t[1].v[2].p = addpt3(center, addpt3(p, v2)); + + for(i = 0; i < 6; i++){ + for(j = 0; j < 2; j++) + for(k = 0; k < 3; k++) + if(i > 0) + t[j].v[k].p = qrotate(t[j].v[k].p, axes[i%3], PI/2); + + m->prims = erealloc(m->prims, (m->nprims += 2)*sizeof(*m->prims)); + m->prims[m->nprims-2] = t[0]; + m->prims[m->nprims-1] = t[1]; + } + return m; +} + static void updatestats(Camera *c, uvlong v) { @@ -91,13 +158,17 @@ reloadcamera(Camera *c) void shootcamera(Camera *c, Shadertab *s) { + static Scene *skyboxscene; + static Shadertab skyboxshader = { nil, skyboxvs, skyboxfs }; + Model *mdl; Renderjob *job; uvlong t0, t1; job = emalloc(sizeof *job); memset(job, 0, sizeof *job); job->fb = c->vp->fbctl->getbb(c->vp->fbctl); - job->scene = c->s; + job->camera = c; + job->scene = c->scene; job->shaders = s; job->donec = chancreate(sizeof(void*), 0); @@ -105,6 +176,22 @@ shootcamera(Camera *c, Shadertab *s) t0 = nanosec(); sendp(c->rctl->c, job); recvp(job->donec); + /* + * if the scene has a skybox, do another render pass, + * filling in the fragments left untouched by the z-buffer. + */ + if(c->scene->skybox != nil){ + if(skyboxscene == nil){ + skyboxscene = newscene("skybox"); + mdl = mkskyboxmodel(); + skyboxscene->addent(skyboxscene, newentity(mdl)); + } + skyboxscene->skybox = c->scene->skybox; + job->scene = skyboxscene; + job->shaders = &skyboxshader; + sendp(c->rctl->c, job); + recvp(job->donec); + } t1 = nanosec(); c->vp->fbctl->swap(c->vp->fbctl); diff --git a/clip.c b/clip.c index bcfcabf..4c5e245 100644 --- a/clip.c +++ b/clip.c @@ -70,8 +70,7 @@ eqpt3(Point3 a, Point3 b) /* * references: - * - James F. Blinn, Martin E. Newell, “Clipping Using Homogeneous Coordinates”, - * SIGGRAPH '78, pp. 245-251 + * - “Clipping Using Homogeneous Coordinates”, James F. Blinn, Martin E. Newell, SIGGRAPH '78, pp. 245-251 * - https://cs418.cs.illinois.edu/website/text/clipping.html * - https://github.com/aap/librw/blob/14dab85dcae6f3762fb2b1eda4d58d8e67541330/tools/playground/tl_tests.cpp#L522 */ diff --git a/doc/libgraphics.ms b/doc/libgraphics.ms index feb1480..209094d 100644 --- a/doc/libgraphics.ms +++ b/doc/libgraphics.ms @@ -152,3 +152,12 @@ arrow from Tiles.Tn.e to Raster.Rn.w .PE .B "Figure 3" : Raster task scheduling. +.SH +Frames of reference +.PP +Frames are right-handed throughout every stage. +.PS +.ps 7 + +.ps 10 +.PE diff --git a/graphics.h b/graphics.h index 7998d01..b7cfdce 100644 --- a/graphics.h +++ b/graphics.h @@ -23,6 +23,7 @@ enum { }; typedef struct Color Color; +typedef struct Cubemap Cubemap; typedef struct Vertexattr Vertexattr; typedef struct Vertex Vertex; typedef struct LightSource LightSource; @@ -48,6 +49,12 @@ struct Color double r, g, b, a; }; +struct Cubemap +{ + char *name; + Memimage *faces[6]; +}; + /* * a more general approach worth investigating. * it could be made to handle types other than double. @@ -124,7 +131,7 @@ struct Model { Primitive *prims; ulong nprims; - Memimage *tex; /* texture map */ + Memimage *tex; /* texture map (TODO get rid of it, use materials) */ Material *materials; ulong nmaterials; }; @@ -142,6 +149,7 @@ struct Scene char *name; Entity ents; ulong nents; + Cubemap *skybox; void (*addent)(Scene*, Entity*); void (*delent)(Scene*, Entity*); @@ -168,6 +176,7 @@ struct SUparams Framebuf *fb; Memimage *frag; Renderjob *job; + Camera *camera; Entity *entity; Primitive *eb, *ee; @@ -199,6 +208,7 @@ struct Renderjob Ref; uvlong id; Framebuf *fb; + Camera *camera; Scene *scene; Shadertab *shaders; Channel *donec; @@ -245,7 +255,7 @@ struct Camera { RFrame3; /* VCS */ Viewport *vp; - Scene *s; + Scene *scene; Renderer *rctl; double fov; /* vertical FOV */ struct { @@ -313,6 +323,9 @@ Vertexattr *getvattr(Vertex*, char*); Color neartexsampler(Memimage*, Point2); Color bilitexsampler(Memimage*, Point2); Color texture(Memimage*, Point2, Color(*)(Memimage*, Point2)); +Cubemap *readcubemap(char*[6]); +void freecubemap(Cubemap*); +Color cubemaptexture(Cubemap*, Point3, Color(*)(Memimage*, Point2)); /* util */ double fmin(double, double); diff --git a/render.c b/render.c index 4a0862a..91cf039 100644 --- a/render.c +++ b/render.c @@ -547,6 +547,7 @@ renderer(void *arg) memset(params, 0, sizeof *params); params->fb = job->fb; params->job = job; + params->camera = job->camera; params->entity = ent; params->uni_time = time; params->vshader = job->shaders->vshader; diff --git a/scene.c b/scene.c index 7cec60d..5cd547e 100644 --- a/scene.c +++ b/scene.c @@ -399,6 +399,7 @@ newscene(char *name) s->name = name == nil? nil: strdup(name); s->ents.prev = s->ents.next = &s->ents; s->nents = 0; + s->skybox = nil; s->addent = scene_addent; s->delent = scene_delent; return s; @@ -418,10 +419,13 @@ void clearscene(Scene *s) { Entity *e, *ne; + int i; for(e = s->ents.next; e != &s->ents; e = ne){ ne = e->next; s->delent(s, e); delentity(e); } + if(s->skybox != nil) + freecubemap(s->skybox); } diff --git a/texture.c b/texture.c index aadcc84..158bcb6 100644 --- a/texture.c +++ b/texture.c @@ -8,6 +8,15 @@ #include "graphics.h" #include "internal.h" +enum { + CUBEMAP_FACE_LEFT, /* -x */ + CUBEMAP_FACE_RIGHT, /* +x */ + CUBEMAP_FACE_BOTTOM, /* -y */ + CUBEMAP_FACE_TOP, /* +y */ + CUBEMAP_FACE_FRONT, /* -z */ + CUBEMAP_FACE_BACK, /* +z */ +}; + /* * uv-coords belong to the 1st quadrant (v grows bottom-up), * hence the need to reverse the v coord. @@ -108,3 +117,96 @@ texture(Memimage *i, Point2 uv, Color(*sampler)(Memimage*,Point2)) { return sampler(i, uv); } + +/* cubemap sampling */ + +Cubemap * +readcubemap(char *paths[6]) +{ + Cubemap *cm; + char **p; + int fd; + + cm = emalloc(sizeof *cm); + memset(cm, 0, sizeof *cm); + + for(p = paths; p < paths+6; p++){ + assert(*p != nil); + fd = open(*p, OREAD); + if(fd < 0) + sysfatal("open: %r"); + cm->faces[p-paths] = readmemimage(fd); + if(cm->faces[p-paths] == nil) + sysfatal("readmemimage: %r"); + close(fd); + } + return cm; +} + +void +freecubemap(Cubemap *cm) +{ + int i; + + for(i = 0; i < 6; i++) + freememimage(cm->faces[i]); + free(cm->name); + free(cm); +} + +/* + * references: + * - https://github.com/zauonlok/renderer/blob/9ed5082f0eda453f0b2a0d5ec37cf5a60f0207f6/renderer/core/texture.c#L206 + * - “Cubemap Texture Selection”, OpenGL ES 2.0 § 3.7.5, November 2010 + */ +Color +cubemaptexture(Cubemap *cm, Point3 d, Color(*sampler)(Memimage*,Point2)) +{ + Point2 uv; + double ax, ay, az, ma, sc, tc; + int face; + + ax = fabs(d.x); + ay = fabs(d.y); + az = fabs(d.z); + + if(ax > ay && ax > az){ + ma = ax; + if(d.x > 0){ + face = CUBEMAP_FACE_RIGHT; + sc = -d.z; + tc = -d.y; + }else{ + face = CUBEMAP_FACE_LEFT; + sc = d.z; + tc = -d.y; + } + }else if(ay > az){ + ma = ay; + if(d.y > 0){ + face = CUBEMAP_FACE_TOP; + sc = d.x; + tc = d.z; + }else{ + face = CUBEMAP_FACE_BOTTOM; + sc = d.x; + tc = -d.z; + } + }else{ + ma = az; + if(d.z > 0){ + face = CUBEMAP_FACE_BACK; + sc = d.x; + tc = -d.y; + }else{ + face = CUBEMAP_FACE_FRONT; + sc = -d.x; + tc = -d.y; + } + } + + uv.x = (sc/ma + 1)/2; + uv.y = 1 - (tc/ma + 1)/2; + uv.w = 1; + return sampler(cm->faces[face], uv); +} -- cgit v1.2.3