shithub: 3dee

Download patch

ref: 590c394f3cf8e8f6c8612ff8feb76a2c78a59613
parent: 9a4039805fa1d7a99929e7254a6bde3fb8e1dc2e
author: rodri <rgl@antares-labs.eu>
date: Thu Jul 24 12:25:42 EDT 2025

new tool: raymarch

--- a/mkfile
+++ b/mkfile
@@ -12,6 +12,7 @@
 	stl\
 	tostl\
 	plot3\
+	raymarch\
 
 OFILES=\
 	alloc.$O\
--- /dev/null
+++ b/raymarch.c
@@ -1,0 +1,321 @@
+/*
+ * based on kishimisu's “An Introduction to Raymarching”
+ * - https://www.youtube.com/watch?v=khblXafu7iA
+ */
+#include <u.h>
+#include <libc.h>
+#include <thread.h>
+#include <draw.h>
+#include <memdraw.h>
+#include <mouse.h>
+#include <keyboard.h>
+#include <geometry.h>
+#include "libgraphics/graphics.h"
+#include "fns.h"
+
+#define sd∪(a, b)	min((a), (b))
+#define sd∩(a, b)	max((a), (b))
+#define sdsub(a, b)	max(-(a), (b))
+
+Mousectl *mctl;
+Keyboardctl *kctl;
+Channel *drawc;
+Image *screenb;
+Camera *cam;
+Scene *scn;
+Entity *ent;
+Model *mdl;
+
+static int doprof;
+
+static Point3
+abspt3(Point3 p)
+{
+	return (Point3){
+		fabs(p.x),
+		fabs(p.y),
+		fabs(p.z),
+		fabs(p.w)
+	};
+}
+
+static double
+smin(double a, double b, double k)
+{
+	double h;
+
+	h = max(k - fabs(a - b), 0)/k;
+	return min(a, b) - h*h*h * k/6;
+}
+
+static double
+sdBox(Point3 p, Point3 span)
+{
+	double tmp;
+
+	p = subpt3(abspt3(p), span);
+	tmp = min(max(p.x, max(p.y, p.z)), 0);
+	return vec3len(maxpt3(p, ZP3)) + tmp;
+}
+
+static double
+sdSphere(Point3 p, double r)
+{
+	return vec3len(p) - r;
+}
+
+static double
+map(Point3 p, double dt)
+{
+	enum {
+		RADIUS	= 1,
+		SIDELEN	= 0.75,
+	};
+	Point3 sphp;
+	double sphere, box, gnd;
+
+	sphp = Pt3(sin(dt)*3, 0, 0, 1);
+	sphere = sdSphere(subpt3(p, sphp), RADIUS);
+	box = sdBox(p, Vec3(SIDELEN, SIDELEN, SIDELEN));
+	gnd = p.y + 0.75;
+
+	return sd∪(gnd, smin(sphere, box, 2));
+}
+
+static Point3
+vs(Shaderparams *sp)
+{
+	return sp->v->p;
+}
+
+static Color
+fs(Shaderparams *sp)
+{
+	Vertexattr *va;
+	Point2 uv, m;
+	Point3 mpt, ro, rd, p;
+	Color c;
+	double time, dt, t, d;
+	int i;
+
+	uv = Pt2(sp->p.x, sp->p.y, 1);
+	uv.x = 2*uv.x - Dx(sp->su->fb->r);
+	uv.y = Dy(sp->su->fb->r) - 2*uv.y;
+	uv = divpt2(uv, Dy(sp->su->fb->r));
+
+	va = sp->getuniform(sp, "time");
+	time = va == nil? 0: va->n;
+	dt = time/1e9;
+
+	va = sp->getuniform(sp, "mouse");
+	mpt = va == nil? ZP3: va->p;
+	m = Pt2(mpt.x, mpt.y, 1);
+	m.x = 2*m.x - Dx(sp->su->fb->r);
+	m.y = Dy(sp->su->fb->r) - 2*m.y;
+	m = divpt2(m, Dy(sp->su->fb->r)/2);
+
+	ro = Pt3(0, 0, 3, 1);
+	rd = normvec3(Vec3(uv.x, uv.y, -1));
+	t = 0;
+
+	ro = qrotate(qrotate(ro, Vec3(0,1,0), -m.x), Vec3(1,0,0), m.y);
+	rd = qrotate(qrotate(rd, Vec3(0,1,0), -m.x), Vec3(1,0,0), m.y);
+
+	for(i = 0; i < 20; i++){
+		p = addpt3(ro, mulpt3(rd, t));
+		d = map(p, dt);
+		t += d;
+
+		if(d < 0.001 || t > 100)
+			break;
+	}
+
+	c = mulpt3(Vec3(t, t, t), 0.2);
+	c.a = 1;
+	return srgb2linear(c);
+}
+
+Shadertab shaders = {
+	.vs = vs,
+	.fs = fs
+};
+
+void
+redraw(void)
+{
+	lockdisplay(display);
+	draw(screen, screen->r, screenb, nil, ZP);
+	flushimage(display, 1);
+	unlockdisplay(display);
+}
+
+void
+drawproc(void *)
+{
+	threadsetname("drawproc");
+
+	for(;;){
+		recv(drawc, nil);
+		redraw();
+	}
+}
+
+void
+renderproc(void *)
+{
+	Point3 mpt;
+	uvlong t0, Δt;
+	double time;
+
+	threadsetname("renderproc");
+
+	t0 = nanosec();
+	for(;;){
+		time = nanosec();
+		setuniform(&shaders, "time", VANumber, &time);
+		mpt = Vec3(mctl->xy.x, mctl->xy.y, 0);
+		setuniform(&shaders, "mouse", VAPoint, &mpt);
+		shootcamera(cam, &shaders);
+
+		Δt = nanosec() - t0;
+		if(Δt > HZ2NS(60)){
+			lockdisplay(display);
+			draw(screenb, screenb->r, display->black, nil, ZP);
+			cam->view->draw(cam->view, screenb, nil);
+			unlockdisplay(display);
+			nbsend(drawc, nil);
+			t0 = nanosec();
+		}else{
+			Δt = HZ2NS(60) - Δt;
+			if(Δt > 1000000ULL)
+				sleep(Δt/1000000ULL);
+			else
+				sleep(1);
+		}
+	}
+}
+
+void
+mouse(void)
+{
+	mctl->xy = subpt(mctl->xy, screen->r.min);
+}
+
+void
+resize(void)
+{
+	lockdisplay(display);
+	if(getwindow(display, Refnone) < 0)
+		fprint(2, "can't reattach to window\n");
+	unlockdisplay(display);
+	nbsend(drawc, nil);
+}
+
+void
+key(Rune r)
+{
+	switch(r){
+	case Kdel:
+	case 'q':
+		threadexitsall(nil);
+	}
+}
+
+static void
+confproc(void)
+{
+	char buf[64];
+	int fd;
+
+	snprint(buf, sizeof buf, "/proc/%d/ctl", getpid());
+	fd = open(buf, OWRITE);
+	if(fd < 0)
+		sysfatal("open: %r");
+
+	if(doprof)
+		fprint(fd, "profile\n");
+	close(fd);
+}
+
+void
+usage(void)
+{
+	fprint(2, "usage: %s\n", argv0);
+	exits("usage");
+}
+
+void
+threadmain(int argc, char *argv[])
+{
+	Renderer *rctl;
+	Primitive quad[2];
+	Vertex v;
+	Rune r;
+
+	ARGBEGIN{
+	case 'p': doprof++; break;
+	default: usage();
+	}ARGEND;
+	if(argc != 0)
+		usage();
+
+	confproc();
+
+	if(memimageinit() != 0)
+		sysfatal("memimageinit: %r");
+	if((rctl = initgraphics()) == nil)
+		sysfatal("initgraphics: %r");
+	if(initdraw(nil, nil, "raymarch") < 0)
+		sysfatal("initdraw: %r");
+	if((mctl = initmouse(nil, screen)) == nil)
+		sysfatal("initmouse: %r");
+	if((kctl = initkeyboard(nil)) == nil)
+		sysfatal("initkeyboard: %r");
+
+	rctl->doprof = doprof;
+
+	scn = newscene(nil);
+	mdl = newmodel();
+	ent = newentity(nil, mdl);
+
+	screenb = eallocimage(display, rectsubpt(screen->r, screen->r.min), XRGB32, 0, DNofill);
+	cam = Cam(screenb->r, rctl, ORTHOGRAPHIC, 40*DEG, 1, 10);
+	placecamera(cam, scn, Pt3(0,0,0,1), Vec3(0,0,-1), Vec3(0,1,0));
+
+	quad[0] = quad[1] = mkprim(PTriangle);
+	v = mkvert();
+	v.p = mdl->addposition(mdl, vcs2clip(cam, viewport2vcs(cam, Pt3(screenb->r.min.x, screenb->r.max.y, 1, 1))));
+	quad[0].v[0] = mdl->addvert(mdl, v);
+	v.p = mdl->addposition(mdl, vcs2clip(cam, viewport2vcs(cam, Pt3(screenb->r.max.x, screenb->r.min.y, 1, 1))));
+	quad[0].v[1] = mdl->addvert(mdl, v);
+	v.p = mdl->addposition(mdl, vcs2clip(cam, viewport2vcs(cam, Pt3(screenb->r.min.x, screenb->r.min.y, 1, 1))));
+	quad[0].v[2] = mdl->addvert(mdl, v);
+	quad[1].v[0] = quad[0].v[0];
+	v.p = mdl->addposition(mdl, vcs2clip(cam, viewport2vcs(cam, Pt3(screenb->r.max.x, screenb->r.max.y, 1, 1))));
+	quad[1].v[1] = mdl->addvert(mdl, v);
+	quad[1].v[2] = quad[0].v[1];
+	mdl->addprim(mdl, quad[0]);
+	mdl->addprim(mdl, quad[1]);
+	scn->addent(scn, ent);
+
+	drawc = chancreate(sizeof(void*), 1);
+	display->locking = 1;
+	unlockdisplay(display);
+
+	proccreate(renderproc, nil, mainstacksize);
+	proccreate(drawproc, nil, mainstacksize);
+
+	enum {MOUSE, RESIZE, KEY};
+	Alt a[] = {
+		{mctl->c, &mctl->Mouse, CHANRCV},
+		{mctl->resizec, nil, CHANRCV},
+		{kctl->c, &r, CHANRCV},
+		{nil, nil, CHANEND}
+	};
+	for(;;)
+		switch(alt(a)){
+		case MOUSE: mouse(); break;
+		case RESIZE: resize(); break;
+		case KEY: key(r); break;
+		}
+}
--