shithub: 3dee

Download patch

ref: cdb4aca6643ebde7cfb7af332cb323ba2afacf15
parent: ec90599ffe6fedf2019fb2d0972497b5f9aa7229
author: rodri <rgl@antares-labs.eu>
date: Sat Sep 21 07:44:08 EDT 2024

put all the shaders in a single file.

--- a/med.c
+++ b/med.c
@@ -106,6 +106,8 @@
 static int showhud;
 Color (*tsampler)(Texture*,Point2);
 
+#include "shaders.inc"
+
 static Point3
 Vecquat(Quaternion q)
 {
@@ -248,205 +250,6 @@
 	c->scn = newscene(nil);
 	addbasis(c->scn);
 	placecamera(c->cam, c->scn, camcfg.p, center, Vec3(0,1,0));
-}
-
-Point3
-gouraudvshader(Shaderparams *sp)
-{
-	static double Ka = 0.1;	/* ambient factor */
-	static double Ks = 0.5;	/* specular factor */
-	double Kd;		/* diffuse factor */
-	double spec;
-	Point3 pos, lightdir, lookdir;
-	Material m;
-	Color ambient, diffuse, specular, lightc;
-
-	sp->v->n = model2world(sp->su->entity, sp->v->n);
-	sp->v->p = model2world(sp->su->entity, sp->v->p);
-	pos = sp->v->p;
-
-	if(sp->v->mtl != nil)
-		m = *sp->v->mtl;
-	else{
-		memset(&m, 0, sizeof m);
-		m.diffuse = sp->v->c;
-		m.specular = Pt3(1,1,1,1);
-		m.shininess = 1;
-	}
-
-	lightdir = normvec3(subpt3(light.p, pos));
-	lightc = getlightcolor(&light, lightdir);
-
-	ambient = mulpt3(lightc, Ka);
-	ambient = modulapt3(ambient, m.diffuse);
-
-	Kd = max(0, dotvec3(sp->v->n, lightdir));
-	diffuse = mulpt3(lightc, Kd);
-	diffuse = modulapt3(diffuse, m.diffuse);
-
-	lookdir = normvec3(subpt3(sp->su->camera->p, pos));
-	lightdir = qrotate(lightdir, sp->v->n, PI);
-	spec = pow(max(0, dotvec3(lookdir, lightdir)), m.shininess);
-	specular = mulpt3(lightc, spec*Ks);
-	specular = modulapt3(specular, m.specular);
-
-	sp->v->c = addpt3(ambient, addpt3(diffuse, specular));
-	sp->v->c.a = m.diffuse.a;
-	return world2clip(sp->su->camera, pos);
-}
- 
-Color
-gouraudshader(Shaderparams *sp)
-{
-	Color tc;
-
-	if(sp->su->entity->mdl->tex != nil && sp->v->uv.w != 0)
-		tc = sampletexture(sp->su->entity->mdl->tex, sp->v->uv, tsampler);
-	else if(sp->v->mtl != nil && sp->v->mtl->diffusemap != nil && sp->v->uv.w != 0)
-		tc = sampletexture(sp->v->mtl->diffusemap, sp->v->uv, tsampler);
-	else
-		tc = Pt3(1,1,1,1);
-
-	return modulapt3(sp->v->c, tc);
-}
-
-Point3
-phongvshader(Shaderparams *sp)
-{
-	Point3 pos;
-	Color a, d, s;
-	double ss;
-
-	sp->v->n = model2world(sp->su->entity, sp->v->n);
-	sp->v->p = model2world(sp->su->entity, sp->v->p);
-	pos = sp->v->p;
-	sp->setattr(sp, "pos", VAPoint, &pos);
-	if(sp->v->mtl != nil && sp->v->mtl->normalmap != nil && sp->v->uv.w != 0){
-		sp->v->tangent = model2world(sp->su->entity, sp->v->tangent);
-		sp->setattr(sp, "tangent", VAPoint, &sp->v->tangent);
-	}
-	if(sp->v->mtl != nil){
-		a = sp->v->mtl->ambient;
-		d = sp->v->mtl->diffuse;
-		s = sp->v->mtl->specular;
-		ss = sp->v->mtl->shininess;
-		sp->setattr(sp, "ambient", VAPoint, &a);
-		sp->setattr(sp, "diffuse", VAPoint, &d);
-		sp->setattr(sp, "specular", VAPoint, &s);
-		sp->setattr(sp, "shininess", VANumber, &ss);
-	}
-	return world2clip(sp->su->camera, pos);
-}
-
-Color
-phongshader(Shaderparams *sp)
-{
-	static double Ka = 0.1;	/* ambient factor */
-	static double Ks = 0.5;	/* specular factor */
-	double Kd;		/* diffuse factor */
-	double spec;
-	Color ambient, diffuse, specular, lightc, c;
-	Point3 pos, n, lightdir, lookdir;
-	Material m;
-	RFrame3 TBN;
-	Vertexattr *va;
-
-	va = sp->getattr(sp, "pos");
-	pos = va->p;
-
-	va = sp->getattr(sp, "ambient");
-	m.ambient = va != nil? va->p: Pt3(1,1,1,1);
-	va = sp->getattr(sp, "diffuse");
-	m.diffuse = va != nil? va->p: sp->v->c;
-	va = sp->getattr(sp, "specular");
-	m.specular = va != nil? va->p: Pt3(1,1,1,1);
-	va = sp->getattr(sp, "shininess");
-	m.shininess = va != nil? va->n: 1;
-
-	lightdir = normvec3(subpt3(light.p, pos));
-	lightc = getlightcolor(&light, lightdir);
-
-	/* normal mapping */
-	va = sp->getattr(sp, "tangent");
-	if(va == nil)
-		n = sp->v->n;
-	else{
-		/* TODO implement this on the VS instead and apply Gram-Schmidt here */
-		n = sampletexture(sp->v->mtl->normalmap, sp->v->uv, neartexsampler);
-		n = normvec3(subpt3(mulpt3(n, 2), Vec3(1,1,1)));
-
-		TBN.p = Pt3(0,0,0,1);
-		TBN.bx = va->p;				/* T */
-		TBN.bz = sp->v->n;			/* N */
-		TBN.by = crossvec3(TBN.bz, TBN.bx);	/* B */
-
-		n = normvec3(invrframexform3(n, TBN));
-		sp->v->n = n;
-	}
-
-	if(sp->su->entity->mdl->tex != nil && sp->v->uv.w != 0)
-		m.diffuse = sampletexture(sp->su->entity->mdl->tex, sp->v->uv, tsampler);
-	else if(sp->v->mtl != nil && sp->v->mtl->diffusemap != nil && sp->v->uv.w != 0)
-		m.diffuse = sampletexture(sp->v->mtl->diffusemap, sp->v->uv, tsampler);
-
-	ambient = mulpt3(lightc, Ka);
-	ambient = modulapt3(ambient, m.diffuse);
-
-	Kd = max(0, dotvec3(n, lightdir));
-	diffuse = mulpt3(lightc, Kd);
-	diffuse = modulapt3(diffuse, m.diffuse);
-
-	if(sp->v->mtl != nil && sp->v->mtl->specularmap != nil && sp->v->uv.w != 0)
-		m.specular = sampletexture(sp->v->mtl->specularmap, sp->v->uv, tsampler);
-
-	lookdir = normvec3(subpt3(sp->su->camera->p, pos));
-	lightdir = qrotate(lightdir, n, PI);
-	spec = pow(max(0, dotvec3(lookdir, lightdir)), m.shininess);
-	specular = mulpt3(lightc, spec*Ks);
-	specular = modulapt3(specular, m.specular);
-
-	c = addpt3(ambient, addpt3(diffuse, specular));
-	c.a = m.diffuse.a;
-	return c;
-}
-
-Point3
-identvshader(Shaderparams *sp)
-{
-	if(sp->v->mtl != nil)
-		sp->v->c = sp->v->mtl->diffuse;
-	return world2clip(sp->su->camera, model2world(sp->su->entity, sp->v->p));
-}
-
-Color
-identshader(Shaderparams *sp)
-{
-	Color tc;
-
-	if(sp->su->entity->mdl->tex != nil && sp->v->uv.w != 0)
-		tc = sampletexture(sp->su->entity->mdl->tex, sp->v->uv, tsampler);
-	else if(sp->v->mtl != nil && sp->v->mtl->diffusemap != nil && sp->v->uv.w != 0)
-		tc = sampletexture(sp->v->mtl->diffusemap, sp->v->uv, tsampler);
-	else
-		tc = Pt3(1,1,1,1);
-
-	return modulapt3(sp->v->c, tc);
-}
-
-Shadertab shadertab[] = {
-	{ "ident", identvshader, identshader },
-	{ "gouraud", gouraudvshader, gouraudshader },
-	{ "phong", phongvshader, phongshader },
-};
-Shadertab *
-getshader(char *name)
-{
-	int i;
-
-	for(i = 0; i < nelem(shadertab); i++)
-		if(strcmp(shadertab[i].name, name) == 0)
-			return &shadertab[i];
-	return nil;
 }
 
 void
--- /dev/null
+++ b/shaders.inc
@@ -1,0 +1,421 @@
+
+Point3
+gouraudvshader(Shaderparams *sp)
+{
+	static double Ka = 0.1;	/* ambient factor */
+	static double Ks = 0.5;	/* specular factor */
+	double Kd;		/* diffuse factor */
+	double spec;
+	Point3 pos, lightdir, lookdir;
+	Material m;
+	Color ambient, diffuse, specular, lightc;
+
+	sp->v->n = model2world(sp->su->entity, sp->v->n);
+	sp->v->p = model2world(sp->su->entity, sp->v->p);
+	pos = sp->v->p;
+
+	if(sp->v->mtl != nil)
+		m = *sp->v->mtl;
+	else{
+		memset(&m, 0, sizeof m);
+		m.diffuse = sp->v->c;
+		m.specular = Pt3(1,1,1,1);
+		m.shininess = 1;
+	}
+
+	lightdir = normvec3(subpt3(light.p, pos));
+	lightc = getlightcolor(&light, lightdir);
+
+	ambient = mulpt3(lightc, Ka);
+	ambient = modulapt3(ambient, m.diffuse);
+
+	Kd = max(0, dotvec3(sp->v->n, lightdir));
+	diffuse = mulpt3(lightc, Kd);
+	diffuse = modulapt3(diffuse, m.diffuse);
+
+	lookdir = normvec3(subpt3(sp->su->camera->p, pos));
+	lightdir = qrotate(lightdir, sp->v->n, PI);
+	spec = pow(max(0, dotvec3(lookdir, lightdir)), m.shininess);
+	specular = mulpt3(lightc, spec*Ks);
+	specular = modulapt3(specular, m.specular);
+
+	sp->v->c = addpt3(ambient, addpt3(diffuse, specular));
+	sp->v->c.a = m.diffuse.a;
+	return world2clip(sp->su->camera, pos);
+}
+
+Color
+gouraudshader(Shaderparams *sp)
+{
+	Color tc;
+
+	if(sp->su->entity->mdl->tex != nil && sp->v->uv.w != 0)
+		tc = sampletexture(sp->su->entity->mdl->tex, sp->v->uv, tsampler);
+	else if(sp->v->mtl != nil && sp->v->mtl->diffusemap != nil && sp->v->uv.w != 0)
+		tc = sampletexture(sp->v->mtl->diffusemap, sp->v->uv, tsampler);
+	else
+		tc = Pt3(1,1,1,1);
+
+	sp->v->n.w = 1;
+	sp->toraster(sp, "normals", &sp->v->n);
+
+	return modulapt3(sp->v->c, tc);
+}
+
+Point3
+phongvshader(Shaderparams *sp)
+{
+	Point3 pos;
+	Color a, d, s;
+	double ss;
+
+	sp->v->n = model2world(sp->su->entity, sp->v->n);
+	sp->v->p = model2world(sp->su->entity, sp->v->p);
+	pos = sp->v->p;
+	sp->setattr(sp, "pos", VAPoint, &pos);
+	if(sp->v->mtl != nil && sp->v->mtl->normalmap != nil && sp->v->uv.w != 0){
+		sp->v->tangent = model2world(sp->su->entity, sp->v->tangent);
+		sp->setattr(sp, "tangent", VAPoint, &sp->v->tangent);
+	}
+	if(sp->v->mtl != nil){
+		a = sp->v->mtl->ambient;
+		d = sp->v->mtl->diffuse;
+		s = sp->v->mtl->specular;
+		ss = sp->v->mtl->shininess;
+		sp->setattr(sp, "ambient", VAPoint, &a);
+		sp->setattr(sp, "diffuse", VAPoint, &d);
+		sp->setattr(sp, "specular", VAPoint, &s);
+		sp->setattr(sp, "shininess", VANumber, &ss);
+	}
+	return world2clip(sp->su->camera, pos);
+}
+
+Color
+phongshader(Shaderparams *sp)
+{
+	static double Ka = 0.1;	/* ambient factor */
+	static double Ks = 0.5;	/* specular factor */
+	double Kd;		/* diffuse factor */
+	double spec;
+	Color ambient, diffuse, specular, lightc, c;
+	Point3 pos, n, lightdir, lookdir;
+	Material m;
+	RFrame3 TBN;
+	Vertexattr *va;
+
+	va = sp->getattr(sp, "pos");
+	pos = va->p;
+	
+	va = sp->getattr(sp, "ambient");
+	m.ambient = va != nil? va->p: Pt3(1,1,1,1);
+	va = sp->getattr(sp, "diffuse");
+	m.diffuse = va != nil? va->p: sp->v->c;
+	va = sp->getattr(sp, "specular");
+	m.specular = va != nil? va->p: Pt3(1,1,1,1);
+	va = sp->getattr(sp, "shininess");
+	m.shininess = va != nil? va->n: 1;
+
+	lightdir = normvec3(subpt3(light.p, pos));
+	lightc = getlightcolor(&light, lightdir);
+
+	/* normal mapping */
+	va = sp->getattr(sp, "tangent");
+	if(va == nil)
+		n = sp->v->n;
+	else{
+		/* TODO implement this on the VS instead and apply Gram-Schmidt here */
+		n = sampletexture(sp->v->mtl->normalmap, sp->v->uv, neartexsampler);
+		n = normvec3(subpt3(mulpt3(n, 2), Vec3(1,1,1)));
+
+		TBN.p = Pt3(0,0,0,1);
+		TBN.bx = va->p;				/* T */
+		TBN.bz = sp->v->n;			/* N */
+		TBN.by = crossvec3(TBN.bz, TBN.bx);	/* B */
+
+		n = normvec3(invrframexform3(n, TBN));
+		sp->v->n = n;
+	}
+
+	if(sp->su->entity->mdl->tex != nil && sp->v->uv.w != 0)
+		m.diffuse = sampletexture(sp->su->entity->mdl->tex, sp->v->uv, tsampler);
+	else if(sp->v->mtl != nil && sp->v->mtl->diffusemap != nil && sp->v->uv.w != 0)
+		m.diffuse = sampletexture(sp->v->mtl->diffusemap, sp->v->uv, tsampler);
+
+	ambient = mulpt3(lightc, Ka);
+	ambient = modulapt3(ambient, m.diffuse);
+
+	Kd = max(0, dotvec3(n, lightdir));
+	diffuse = mulpt3(lightc, Kd);
+	diffuse = modulapt3(diffuse, m.diffuse);
+
+	if(sp->v->mtl != nil && sp->v->mtl->specularmap != nil && sp->v->uv.w != 0)
+		m.specular = sampletexture(sp->v->mtl->specularmap, sp->v->uv, tsampler);
+
+	lookdir = normvec3(subpt3(sp->su->camera->p, pos));
+	lightdir = qrotate(lightdir, n, PI);
+	spec = pow(max(0, dotvec3(lookdir, lightdir)), m.shininess);
+	specular = mulpt3(lightc, spec*Ks);
+	specular = modulapt3(specular, m.specular);
+
+	sp->v->n.w = 1;
+	sp->toraster(sp, "normals", &sp->v->n);
+
+	c = addpt3(ambient, addpt3(diffuse, specular));
+	c.a = m.diffuse.a;
+	return c;
+}
+
+Color
+blinnshader(Shaderparams *sp)
+{
+	static double Ka = 0.1;	/* ambient factor */
+	static double Ks = 0.5;	/* specular factor */
+	double Kd;		/* diffuse factor */
+	double spec;
+	Color ambient, diffuse, specular, lightc, c;
+	Point3 pos, n, lightdir, lookdir;
+	Material m;
+	RFrame3 TBN;
+	Vertexattr *va;
+
+	va = sp->getattr(sp, "pos");
+	pos = va->p;
+	
+	va = sp->getattr(sp, "ambient");
+	m.ambient = va != nil? va->p: Pt3(1,1,1,1);
+	va = sp->getattr(sp, "diffuse");
+	m.diffuse = va != nil? va->p: sp->v->c;
+	va = sp->getattr(sp, "specular");
+	m.specular = va != nil? va->p: Pt3(1,1,1,1);
+	va = sp->getattr(sp, "shininess");
+	m.shininess = va != nil? va->n: 1;
+
+	lightdir = normvec3(subpt3(light.p, pos));
+	lightc = getlightcolor(&light, lightdir);
+
+	/* normal mapping */
+	va = sp->getattr(sp, "tangent");
+	if(va == nil)
+		n = sp->v->n;
+	else{
+		/* TODO implement this on the VS instead and apply Gram-Schmidt here */
+		n = sampletexture(sp->v->mtl->normalmap, sp->v->uv, neartexsampler);
+		n = normvec3(subpt3(mulpt3(n, 2), Vec3(1,1,1)));
+
+		TBN.p = Pt3(0,0,0,1);
+		TBN.bx = va->p;				/* T */
+		TBN.bz = sp->v->n;			/* N */
+		TBN.by = crossvec3(TBN.bz, TBN.bx);	/* B */
+
+		n = normvec3(invrframexform3(n, TBN));
+		sp->v->n = n;
+	}
+
+	if(sp->su->entity->mdl->tex != nil && sp->v->uv.w != 0)
+		m.diffuse = sampletexture(sp->su->entity->mdl->tex, sp->v->uv, tsampler);
+	else if(sp->v->mtl != nil && sp->v->mtl->diffusemap != nil && sp->v->uv.w != 0)
+		m.diffuse = sampletexture(sp->v->mtl->diffusemap, sp->v->uv, tsampler);
+
+	ambient = mulpt3(lightc, Ka);
+	ambient = modulapt3(ambient, m.diffuse);
+
+	Kd = max(0, dotvec3(n, lightdir));
+	diffuse = mulpt3(lightc, Kd);
+	diffuse = modulapt3(diffuse, m.diffuse);
+
+	if(sp->v->mtl != nil && sp->v->mtl->specularmap != nil && sp->v->uv.w != 0)
+		m.specular = sampletexture(sp->v->mtl->specularmap, sp->v->uv, tsampler);
+
+	lookdir = normvec3(subpt3(sp->su->camera->p, pos));
+	lightdir = normvec3(addpt3(lookdir, lightdir));	/* half vector */
+	spec = pow(max(0, dotvec3(n, lightdir)), m.shininess);
+	specular = mulpt3(lightc, spec*Ks);
+	specular = modulapt3(specular, m.specular);
+
+	sp->v->n.w = 1;
+	sp->toraster(sp, "normals", &sp->v->n);
+
+	c = addpt3(ambient, addpt3(diffuse, specular));
+	c.a = m.diffuse.a;
+	return c;
+}
+
+Point3
+toonvshader(Shaderparams *sp)
+{
+	Point3 pos, lightdir;
+	double intens;
+
+	sp->v->n = model2world(sp->su->entity, sp->v->n);
+	pos = model2world(sp->su->entity, sp->v->p);
+	lightdir = normvec3(subpt3(light.p, pos));
+	intens = max(0, dotvec3(sp->v->n, lightdir));
+	sp->setattr(sp, "intensity", VANumber, &intens);
+	if(sp->v->mtl != nil)
+		sp->v->c = sp->v->mtl->diffuse;
+	return world2clip(sp->su->camera, pos);
+}
+
+Color
+toonshader(Shaderparams *sp)
+{
+	Vertexattr *va;
+	double intens;
+
+	va = sp->getattr(sp, "intensity");
+	intens = va->n;
+	intens = intens > 0.85? 1:
+		 intens > 0.60? 0.80:
+		 intens > 0.45? 0.60:
+		 intens > 0.30? 0.45:
+		 intens > 0.15? 0.30: 0.15;
+
+	sp->v->n.w = 1;
+	sp->toraster(sp, "normals", &sp->v->n);
+
+	return Pt3(intens, 0.6*intens, 0, 1);
+}
+
+Point3
+identvshader(Shaderparams *sp)
+{
+	if(sp->v->mtl != nil)
+		sp->v->c = sp->v->mtl->diffuse;
+	return world2clip(sp->su->camera, model2world(sp->su->entity, sp->v->p));
+}
+
+Color
+identshader(Shaderparams *sp)
+{
+	Color tc;
+
+	if(sp->su->entity->mdl->tex != nil && sp->v->uv.w != 0)
+		tc = sampletexture(sp->su->entity->mdl->tex, sp->v->uv, tsampler);
+	else if(sp->v->mtl != nil && sp->v->mtl->diffusemap != nil && sp->v->uv.w != 0)
+		tc = sampletexture(sp->v->mtl->diffusemap, sp->v->uv, tsampler);
+	else
+		tc = Pt3(1,1,1,1);
+
+	sp->v->n.w = 1;
+	sp->toraster(sp, "normals", &sp->v->n);
+
+	return modulapt3(sp->v->c, tc);
+}
+
+Point3
+ivshader(Shaderparams *sp)
+{
+	sp->v->n = model2world(sp->su->entity, sp->v->n);
+	sp->v->p = model2world(sp->su->entity, sp->v->p);
+	return world2clip(sp->su->camera, sp->v->p);
+}
+
+Color
+triangleshader(Shaderparams *sp)
+{
+	Triangle2 t;
+	Rectangle bbox;
+	Point3 bc;
+
+	t.p0 = Pt2(240,200,1);
+	t.p1 = Pt2(400,40,1);
+	t.p2 = Pt2(240,40,1);
+
+	bbox = Rect(
+		min(min(t.p0.x, t.p1.x), t.p2.x), min(min(t.p0.y, t.p1.y), t.p2.y),
+		max(max(t.p0.x, t.p1.x), t.p2.x), max(max(t.p0.y, t.p1.y), t.p2.y)
+	);
+	if(!ptinrect(sp->p, bbox))
+		return Vec3(0,0,0);
+
+	bc = barycoords(t, Pt2(sp->p.x,sp->p.y,1));
+	if(bc.x < 0 || bc.y < 0 || bc.z < 0)
+		return Vec3(0,0,0);
+
+	return Pt3(bc.x, bc.y, bc.z, 1);
+}
+
+Color
+circleshader(Shaderparams *sp)
+{
+	Point2 uv;
+	double r, d;
+
+	uv = Pt2(sp->p.x,sp->p.y,1);
+	uv.x /= Dx(sp->su->fb->r);
+	uv.y /= Dy(sp->su->fb->r);
+//	r = 0.3;
+	r = 0.3*fabs(sin(sp->su->uni_time/1e9));
+	d = vec2len(subpt2(uv, Vec2(0.5,0.5)));
+
+	if(d > r + r*0.05 || d < r - r*0.05)
+		return Vec3(0,0,0);
+
+	return Pt3(uv.x, uv.y, 0, 1);
+}
+
+/* some shaping functions from The Book of Shaders, Chapter 5 */
+Color
+sfshader(Shaderparams *sp)
+{
+	Point2 uv;
+	double y, pct;
+
+	uv = Pt2(sp->p.x,sp->p.y,1);
+	uv.x /= Dx(sp->su->fb->r);
+	uv.y /= Dy(sp->su->fb->r);
+	uv.y = 1 - uv.y;		/* make [0 0] the bottom-left corner */
+
+//	y = step(0.5, uv.x);
+//	y = pow(uv.x, 5);
+//	y = sin(uv.x);
+	y = sin(uv.x*sp->su->uni_time/1e8)/2.0 + 0.5;
+//	y = smoothstep(0.1, 0.9, uv.x);
+	pct = smoothstep(y-0.02, y, uv.y) - smoothstep(y, y+0.02, uv.y);
+
+	return Pt3(flerp(y, 0, pct), flerp(y, 1, pct), flerp(y, 0, pct), 1);
+}
+
+Color
+boxshader(Shaderparams *sp)
+{
+	Point2 uv, p;
+	Point2 r;
+
+	uv = Pt2(sp->p.x,sp->p.y,1);
+	uv.x /= Dx(sp->su->fb->r);
+	uv.y /= Dy(sp->su->fb->r);
+	r = Vec2(0.2,0.4);
+
+	p = Pt2(fabs(uv.x - 0.5), fabs(uv.y - 0.5), 1);
+	p = subpt2(p, r);
+	p.x = max(p.x, 0);
+	p.y = max(p.y, 0);
+
+	if(vec2len(p) > 0)
+		return Vec3(0,0,0);
+
+	return Pt3(uv.x, uv.y, smoothstep(0,1,uv.x+uv.y), 1);
+}
+
+Shadertab shadertab[] = {
+	{ "triangle", ivshader, triangleshader },
+	{ "circle", ivshader, circleshader },
+	{ "box", ivshader, boxshader },
+	{ "sf", ivshader, sfshader },
+	{ "toon", toonvshader, toonshader },
+	{ "ident", identvshader, identshader },
+	{ "gouraud", gouraudvshader, gouraudshader },
+	{ "phong", phongvshader, phongshader },
+	{ "blinn", phongvshader, blinnshader },
+};
+Shadertab *
+getshader(char *name)
+{
+	int i;
+
+	for(i = 0; i < nelem(shadertab); i++)
+		if(strcmp(shadertab[i].name, name) == 0)
+			return &shadertab[i];
+	return nil;
+}
--- a/vis.c
+++ b/vis.c
@@ -98,6 +98,8 @@
 static int abuffon;
 Color (*tsampler)(Texture*,Point2);
 
+#include "shaders.inc"
+
 static Point3
 Vecquat(Quaternion q)
 {
@@ -108,427 +110,6 @@
 Ptquat(Quaternion q, double w)
 {
 	return Pt3(q.i, q.j, q.k, w);
-}
-
-Point3
-gouraudvshader(Shaderparams *sp)
-{
-	static double Ka = 0.1;	/* ambient factor */
-	static double Ks = 0.5;	/* specular factor */
-	double Kd;		/* diffuse factor */
-	double spec;
-	Point3 pos, lightdir, lookdir;
-	Material m;
-	Color ambient, diffuse, specular, lightc;
-
-	sp->v->n = model2world(sp->su->entity, sp->v->n);
-	sp->v->p = model2world(sp->su->entity, sp->v->p);
-	pos = sp->v->p;
-
-	if(sp->v->mtl != nil)
-		m = *sp->v->mtl;
-	else{
-		memset(&m, 0, sizeof m);
-		m.diffuse = sp->v->c;
-		m.specular = Pt3(1,1,1,1);
-		m.shininess = 1;
-	}
-
-	lightdir = normvec3(subpt3(light.p, pos));
-	lightc = getlightcolor(&light, lightdir);
-
-	ambient = mulpt3(lightc, Ka);
-	ambient = modulapt3(ambient, m.diffuse);
-
-	Kd = max(0, dotvec3(sp->v->n, lightdir));
-	diffuse = mulpt3(lightc, Kd);
-	diffuse = modulapt3(diffuse, m.diffuse);
-
-	lookdir = normvec3(subpt3(sp->su->camera->p, pos));
-	lightdir = qrotate(lightdir, sp->v->n, PI);
-	spec = pow(max(0, dotvec3(lookdir, lightdir)), m.shininess);
-	specular = mulpt3(lightc, spec*Ks);
-	specular = modulapt3(specular, m.specular);
-
-	sp->v->c = addpt3(ambient, addpt3(diffuse, specular));
-	sp->v->c.a = m.diffuse.a;
-	return world2clip(sp->su->camera, pos);
-}
-
-Color
-gouraudshader(Shaderparams *sp)
-{
-	Color tc;
-
-	if(sp->su->entity->mdl->tex != nil && sp->v->uv.w != 0)
-		tc = sampletexture(sp->su->entity->mdl->tex, sp->v->uv, tsampler);
-	else if(sp->v->mtl != nil && sp->v->mtl->diffusemap != nil && sp->v->uv.w != 0)
-		tc = sampletexture(sp->v->mtl->diffusemap, sp->v->uv, tsampler);
-	else
-		tc = Pt3(1,1,1,1);
-
-	sp->v->n.w = 1;
-	sp->toraster(sp, "normals", &sp->v->n);
-
-	return modulapt3(sp->v->c, tc);
-}
-
-Point3
-phongvshader(Shaderparams *sp)
-{
-	Point3 pos;
-	Color a, d, s;
-	double ss;
-
-	sp->v->n = model2world(sp->su->entity, sp->v->n);
-	sp->v->p = model2world(sp->su->entity, sp->v->p);
-	pos = sp->v->p;
-	sp->setattr(sp, "pos", VAPoint, &pos);
-	if(sp->v->mtl != nil && sp->v->mtl->normalmap != nil && sp->v->uv.w != 0){
-		sp->v->tangent = model2world(sp->su->entity, sp->v->tangent);
-		sp->setattr(sp, "tangent", VAPoint, &sp->v->tangent);
-	}
-	if(sp->v->mtl != nil){
-		a = sp->v->mtl->ambient;
-		d = sp->v->mtl->diffuse;
-		s = sp->v->mtl->specular;
-		ss = sp->v->mtl->shininess;
-		sp->setattr(sp, "ambient", VAPoint, &a);
-		sp->setattr(sp, "diffuse", VAPoint, &d);
-		sp->setattr(sp, "specular", VAPoint, &s);
-		sp->setattr(sp, "shininess", VANumber, &ss);
-	}
-	return world2clip(sp->su->camera, pos);
-}
-
-Color
-phongshader(Shaderparams *sp)
-{
-	static double Ka = 0.1;	/* ambient factor */
-	static double Ks = 0.5;	/* specular factor */
-	double Kd;		/* diffuse factor */
-	double spec;
-	Color ambient, diffuse, specular, lightc, c;
-	Point3 pos, n, lightdir, lookdir;
-	Material m;
-	RFrame3 TBN;
-	Vertexattr *va;
-
-	va = sp->getattr(sp, "pos");
-	pos = va->p;
-	
-	va = sp->getattr(sp, "ambient");
-	m.ambient = va != nil? va->p: Pt3(1,1,1,1);
-	va = sp->getattr(sp, "diffuse");
-	m.diffuse = va != nil? va->p: sp->v->c;
-	va = sp->getattr(sp, "specular");
-	m.specular = va != nil? va->p: Pt3(1,1,1,1);
-	va = sp->getattr(sp, "shininess");
-	m.shininess = va != nil? va->n: 1;
-
-	lightdir = normvec3(subpt3(light.p, pos));
-	lightc = getlightcolor(&light, lightdir);
-
-	/* normal mapping */
-	va = sp->getattr(sp, "tangent");
-	if(va == nil)
-		n = sp->v->n;
-	else{
-		/* TODO implement this on the VS instead and apply Gram-Schmidt here */
-		n = sampletexture(sp->v->mtl->normalmap, sp->v->uv, neartexsampler);
-		n = normvec3(subpt3(mulpt3(n, 2), Vec3(1,1,1)));
-
-		TBN.p = Pt3(0,0,0,1);
-		TBN.bx = va->p;				/* T */
-		TBN.bz = sp->v->n;			/* N */
-		TBN.by = crossvec3(TBN.bz, TBN.bx);	/* B */
-
-		n = normvec3(invrframexform3(n, TBN));
-		sp->v->n = n;
-	}
-
-	if(sp->su->entity->mdl->tex != nil && sp->v->uv.w != 0)
-		m.diffuse = sampletexture(sp->su->entity->mdl->tex, sp->v->uv, tsampler);
-	else if(sp->v->mtl != nil && sp->v->mtl->diffusemap != nil && sp->v->uv.w != 0)
-		m.diffuse = sampletexture(sp->v->mtl->diffusemap, sp->v->uv, tsampler);
-
-	ambient = mulpt3(lightc, Ka);
-	ambient = modulapt3(ambient, m.diffuse);
-
-	Kd = max(0, dotvec3(n, lightdir));
-	diffuse = mulpt3(lightc, Kd);
-	diffuse = modulapt3(diffuse, m.diffuse);
-
-	if(sp->v->mtl != nil && sp->v->mtl->specularmap != nil && sp->v->uv.w != 0)
-		m.specular = sampletexture(sp->v->mtl->specularmap, sp->v->uv, tsampler);
-
-	lookdir = normvec3(subpt3(sp->su->camera->p, pos));
-	lightdir = qrotate(lightdir, n, PI);
-	spec = pow(max(0, dotvec3(lookdir, lightdir)), m.shininess);
-	specular = mulpt3(lightc, spec*Ks);
-	specular = modulapt3(specular, m.specular);
-
-	sp->v->n.w = 1;
-	sp->toraster(sp, "normals", &sp->v->n);
-
-	c = addpt3(ambient, addpt3(diffuse, specular));
-	c.a = m.diffuse.a;
-	return c;
-}
-
-Color
-blinnshader(Shaderparams *sp)
-{
-	static double Ka = 0.1;	/* ambient factor */
-	static double Ks = 0.5;	/* specular factor */
-	double Kd;		/* diffuse factor */
-	double spec;
-	Color ambient, diffuse, specular, lightc, c;
-	Point3 pos, n, lightdir, lookdir;
-	Material m;
-	RFrame3 TBN;
-	Vertexattr *va;
-
-	va = sp->getattr(sp, "pos");
-	pos = va->p;
-	
-	va = sp->getattr(sp, "ambient");
-	m.ambient = va != nil? va->p: Pt3(1,1,1,1);
-	va = sp->getattr(sp, "diffuse");
-	m.diffuse = va != nil? va->p: sp->v->c;
-	va = sp->getattr(sp, "specular");
-	m.specular = va != nil? va->p: Pt3(1,1,1,1);
-	va = sp->getattr(sp, "shininess");
-	m.shininess = va != nil? va->n: 1;
-
-	lightdir = normvec3(subpt3(light.p, pos));
-	lightc = getlightcolor(&light, lightdir);
-
-	/* normal mapping */
-	va = sp->getattr(sp, "tangent");
-	if(va == nil)
-		n = sp->v->n;
-	else{
-		/* TODO implement this on the VS instead and apply Gram-Schmidt here */
-		n = sampletexture(sp->v->mtl->normalmap, sp->v->uv, neartexsampler);
-		n = normvec3(subpt3(mulpt3(n, 2), Vec3(1,1,1)));
-
-		TBN.p = Pt3(0,0,0,1);
-		TBN.bx = va->p;				/* T */
-		TBN.bz = sp->v->n;			/* N */
-		TBN.by = crossvec3(TBN.bz, TBN.bx);	/* B */
-
-		n = normvec3(invrframexform3(n, TBN));
-		sp->v->n = n;
-	}
-
-	if(sp->su->entity->mdl->tex != nil && sp->v->uv.w != 0)
-		m.diffuse = sampletexture(sp->su->entity->mdl->tex, sp->v->uv, tsampler);
-	else if(sp->v->mtl != nil && sp->v->mtl->diffusemap != nil && sp->v->uv.w != 0)
-		m.diffuse = sampletexture(sp->v->mtl->diffusemap, sp->v->uv, tsampler);
-
-	ambient = mulpt3(lightc, Ka);
-	ambient = modulapt3(ambient, m.diffuse);
-
-	Kd = max(0, dotvec3(n, lightdir));
-	diffuse = mulpt3(lightc, Kd);
-	diffuse = modulapt3(diffuse, m.diffuse);
-
-	if(sp->v->mtl != nil && sp->v->mtl->specularmap != nil && sp->v->uv.w != 0)
-		m.specular = sampletexture(sp->v->mtl->specularmap, sp->v->uv, tsampler);
-
-	lookdir = normvec3(subpt3(sp->su->camera->p, pos));
-	lightdir = normvec3(addpt3(lookdir, lightdir));	/* half vector */
-	spec = pow(max(0, dotvec3(n, lightdir)), m.shininess);
-	specular = mulpt3(lightc, spec*Ks);
-	specular = modulapt3(specular, m.specular);
-
-	sp->v->n.w = 1;
-	sp->toraster(sp, "normals", &sp->v->n);
-
-	c = addpt3(ambient, addpt3(diffuse, specular));
-	c.a = m.diffuse.a;
-	return c;
-}
-
-Point3
-toonvshader(Shaderparams *sp)
-{
-	Point3 pos, lightdir;
-	double intens;
-
-	sp->v->n = model2world(sp->su->entity, sp->v->n);
-	pos = model2world(sp->su->entity, sp->v->p);
-	lightdir = normvec3(subpt3(light.p, pos));
-	intens = max(0, dotvec3(sp->v->n, lightdir));
-	sp->setattr(sp, "intensity", VANumber, &intens);
-	if(sp->v->mtl != nil)
-		sp->v->c = sp->v->mtl->diffuse;
-	return world2clip(sp->su->camera, pos);
-}
-
-Color
-toonshader(Shaderparams *sp)
-{
-	Vertexattr *va;
-	double intens;
-
-	va = sp->getattr(sp, "intensity");
-	intens = va->n;
-	intens = intens > 0.85? 1:
-		 intens > 0.60? 0.80:
-		 intens > 0.45? 0.60:
-		 intens > 0.30? 0.45:
-		 intens > 0.15? 0.30: 0.15;
-
-	sp->v->n.w = 1;
-	sp->toraster(sp, "normals", &sp->v->n);
-
-	return Pt3(intens, 0.6*intens, 0, 1);
-}
-
-Point3
-identvshader(Shaderparams *sp)
-{
-	if(sp->v->mtl != nil)
-		sp->v->c = sp->v->mtl->diffuse;
-	return world2clip(sp->su->camera, model2world(sp->su->entity, sp->v->p));
-}
-
-Color
-identshader(Shaderparams *sp)
-{
-	Color tc;
-
-	if(sp->su->entity->mdl->tex != nil && sp->v->uv.w != 0)
-		tc = sampletexture(sp->su->entity->mdl->tex, sp->v->uv, tsampler);
-	else if(sp->v->mtl != nil && sp->v->mtl->diffusemap != nil && sp->v->uv.w != 0)
-		tc = sampletexture(sp->v->mtl->diffusemap, sp->v->uv, tsampler);
-	else
-		tc = Pt3(1,1,1,1);
-
-	sp->v->n.w = 1;
-	sp->toraster(sp, "normals", &sp->v->n);
-
-	return modulapt3(sp->v->c, tc);
-}
-
-Point3
-ivshader(Shaderparams *sp)
-{
-	sp->v->n = model2world(sp->su->entity, sp->v->n);
-	sp->v->p = model2world(sp->su->entity, sp->v->p);
-	return world2clip(sp->su->camera, sp->v->p);
-}
-
-Color
-triangleshader(Shaderparams *sp)
-{
-	Triangle2 t;
-	Rectangle bbox;
-	Point3 bc;
-
-	t.p0 = Pt2(240,200,1);
-	t.p1 = Pt2(400,40,1);
-	t.p2 = Pt2(240,40,1);
-
-	bbox = Rect(
-		min(min(t.p0.x, t.p1.x), t.p2.x), min(min(t.p0.y, t.p1.y), t.p2.y),
-		max(max(t.p0.x, t.p1.x), t.p2.x), max(max(t.p0.y, t.p1.y), t.p2.y)
-	);
-	if(!ptinrect(sp->p, bbox))
-		return Vec3(0,0,0);
-
-	bc = barycoords(t, Pt2(sp->p.x,sp->p.y,1));
-	if(bc.x < 0 || bc.y < 0 || bc.z < 0)
-		return Vec3(0,0,0);
-
-	return Pt3(bc.x, bc.y, bc.z, 1);
-}
-
-Color
-circleshader(Shaderparams *sp)
-{
-	Point2 uv;
-	double r, d;
-
-	uv = Pt2(sp->p.x,sp->p.y,1);
-	uv.x /= Dx(sp->su->fb->r);
-	uv.y /= Dy(sp->su->fb->r);
-//	r = 0.3;
-	r = 0.3*fabs(sin(sp->su->uni_time/1e9));
-	d = vec2len(subpt2(uv, Vec2(0.5,0.5)));
-
-	if(d > r + r*0.05 || d < r - r*0.05)
-		return Vec3(0,0,0);
-
-	return Pt3(uv.x, uv.y, 0, 1);
-}
-
-/* some shaping functions from The Book of Shaders, Chapter 5 */
-Color
-sfshader(Shaderparams *sp)
-{
-	Point2 uv;
-	double y, pct;
-
-	uv = Pt2(sp->p.x,sp->p.y,1);
-	uv.x /= Dx(sp->su->fb->r);
-	uv.y /= Dy(sp->su->fb->r);
-	uv.y = 1 - uv.y;		/* make [0 0] the bottom-left corner */
-
-//	y = step(0.5, uv.x);
-//	y = pow(uv.x, 5);
-//	y = sin(uv.x);
-	y = sin(uv.x*sp->su->uni_time/1e8)/2.0 + 0.5;
-//	y = smoothstep(0.1, 0.9, uv.x);
-	pct = smoothstep(y-0.02, y, uv.y) - smoothstep(y, y+0.02, uv.y);
-
-	return Pt3(flerp(y, 0, pct), flerp(y, 1, pct), flerp(y, 0, pct), 1);
-}
-
-Color
-boxshader(Shaderparams *sp)
-{
-	Point2 uv, p;
-	Point2 r;
-
-	uv = Pt2(sp->p.x,sp->p.y,1);
-	uv.x /= Dx(sp->su->fb->r);
-	uv.y /= Dy(sp->su->fb->r);
-	r = Vec2(0.2,0.4);
-
-	p = Pt2(fabs(uv.x - 0.5), fabs(uv.y - 0.5), 1);
-	p = subpt2(p, r);
-	p.x = max(p.x, 0);
-	p.y = max(p.y, 0);
-
-	if(vec2len(p) > 0)
-		return Vec3(0,0,0);
-
-	return Pt3(uv.x, uv.y, smoothstep(0,1,uv.x+uv.y), 1);
-}
-
-Shadertab shadertab[] = {
-	{ "triangle", ivshader, triangleshader },
-	{ "circle", ivshader, circleshader },
-	{ "box", ivshader, boxshader },
-	{ "sf", ivshader, sfshader },
-	{ "toon", toonvshader, toonshader },
-	{ "ident", identvshader, identshader },
-	{ "gouraud", gouraudvshader, gouraudshader },
-	{ "phong", phongvshader, phongshader },
-	{ "blinn", phongvshader, blinnshader },
-};
-Shadertab *
-getshader(char *name)
-{
-	int i;
-
-	for(i = 0; i < nelem(shadertab); i++)
-		if(strcmp(shadertab[i].name, name) == 0)
-			return &shadertab[i];
-	return nil;
 }
 
 void