shithub: libgraphics

Download patch

ref: 239a319b41474a35e4c9c4b7c6ae3c6e0b0b7185
parent: dc597a2c65278119b7d11f83218b860c0c5da051
author: rodri <rgl@antares-labs.eu>
date: Thu Jun 6 13:35:09 EDT 2024

add cubemaps.

--- 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,6 +158,9 @@
 void
 shootcamera(Camera *c, Shadertab *s)
 {
+	static Scene *skyboxscene;
+	static Shadertab skyboxshader = { nil, skyboxvs, skyboxfs };
+	Model *mdl;
 	Renderjob *job;
 	uvlong t0, t1;
 
@@ -97,7 +167,8 @@
 	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 @@
 	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);
 
--- a/clip.c
+++ b/clip.c
@@ -70,8 +70,7 @@
 
 /*
  * 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
  */
--- a/doc/libgraphics.ms
+++ b/doc/libgraphics.ms
@@ -152,3 +152,12 @@
 .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
--- a/graphics.h
+++ b/graphics.h
@@ -23,6 +23,7 @@
 };
 
 typedef struct Color Color;
+typedef struct Cubemap Cubemap;
 typedef struct Vertexattr Vertexattr;
 typedef struct Vertex Vertex;
 typedef struct LightSource LightSource;
@@ -48,6 +49,12 @@
 	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 @@
 {
 	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 @@
 	char *name;
 	Entity ents;
 	ulong nents;
+	Cubemap *skybox;
 
 	void (*addent)(Scene*, Entity*);
 	void (*delent)(Scene*, Entity*);
@@ -168,6 +176,7 @@
 	Framebuf *fb;
 	Memimage *frag;
 	Renderjob *job;
+	Camera *camera;
 	Entity *entity;
 	Primitive *eb, *ee;
 
@@ -199,6 +208,7 @@
 	Ref;
 	uvlong id;
 	Framebuf *fb;
+	Camera *camera;
 	Scene *scene;
 	Shadertab *shaders;
 	Channel *donec;
@@ -245,7 +255,7 @@
 {
 	RFrame3;		/* VCS */
 	Viewport *vp;
-	Scene *s;
+	Scene *scene;
 	Renderer *rctl;
 	double fov;		/* vertical FOV */
 	struct {
@@ -313,6 +323,9 @@
 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);
--- a/render.c
+++ b/render.c
@@ -547,6 +547,7 @@
 			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;
--- a/scene.c
+++ b/scene.c
@@ -399,6 +399,7 @@
 	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,6 +419,7 @@
 clearscene(Scene *s)
 {
 	Entity *e, *ne;
+	int i;
 
 	for(e = s->ents.next; e != &s->ents; e = ne){
 		ne = e->next;
@@ -424,4 +426,6 @@
 		s->delent(s, e);
 		delentity(e);
 	}
+	if(s->skybox != nil)
+		freecubemap(s->skybox);
 }
--- 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.
@@ -107,4 +116,97 @@
 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);
 }