From d3516b11559657935d70829c672f29393e857b8e Mon Sep 17 00:00:00 2001 From: rodri Date: Mon, 20 May 2024 20:20:48 +0000 Subject: fix the perspective projection and add inverse xform functions. --- xform.c | 167 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 167 insertions(+) create mode 100644 xform.c (limited to 'xform.c') diff --git a/xform.c b/xform.c new file mode 100644 index 0000000..aba2089 --- /dev/null +++ b/xform.c @@ -0,0 +1,167 @@ +#include +#include +#include +#include +#include +#include +#include "libobj/obj.h" +#include "graphics.h" +#include "internal.h" + +/* + * transforms p from e's reference frame into + * the world. + */ +Point3 +model2world(Entity *e, Point3 p) +{ + return invrframexform3(p, *e); +} + +/* + * transforms p from the world reference frame + * to c's one (aka Viewing Coordinate System). + */ +Point3 +world2vcs(Camera *c, Point3 p) +{ + return rframexform3(p, *c); +} + +/* + * projects p from the VCS to clip space, placing + * p.[xyz] ∈ (-∞,-w)∪[-w,w]∪(w,∞) where [-w,w] + * represents the visibility volume. + * + * the clipping planes are: + * + * | -w | w | + * +----------------+ + * | left | right | + * | bottom | top | + * | far | near | + */ +Point3 +vcs2clip(Camera *c, Point3 p) +{ + return xform3(p, c->proj); +} + +Point3 +world2clip(Camera *c, Point3 p) +{ + return vcs2clip(c, world2vcs(c, p)); +} + +/* + * performs the perspective division, placing + * p.[xyz] ∈ [-1,1] and p.w = 1/z + * (aka Normalized Device Coordinates). + * + * p.w is kept as z⁻¹ so we can later do + * perspective-correct attribute interpolation. + */ +Point3 +clip2ndc(Point3 p) +{ + p.w = p.w == 0? 1: 1.0/p.w; + p.x *= p.w; + p.y *= p.w; + p.z *= p.w; + return p; +} + +/* + * scales p to fit the destination viewport, + * placing p.x ∈ [0,width], p.y ∈ [0,height], + * p.z ∈ [0,1] and leaving p.w intact. + */ +Point3 +ndc2viewport(Framebuf *fb, Point3 p) +{ + Matrix3 view = { + Dx(fb->r)/2.0, 0, 0, Dx(fb->r)/2.0, + 0, -Dy(fb->r)/2.0, 0, Dy(fb->r)/2.0, + 0, 0, 1.0/2.0, 1.0/2.0, + 0, 0, 0, 1, + }; + double w; + + w = p.w; + p.w = 1; + p = xform3(p, view); + p.w = w; + return p; +} + +Point3 +viewport2ndc(Framebuf *fb, Point3 p) +{ + p.x = 2*p.x/Dx(fb->r) - 1; + p.y = 1 - 2*p.y/Dy(fb->r); + p.z = 2*p.z - 1; + p.w = 1; + return p; +} + +Point3 +ndc2vcs(Camera *c, Point3 p) +{ + Matrix3 invproj; + Point3 np; + + memmove(invproj, c->proj, 4*4*sizeof(double)); + invm3(invproj); + np = xform3(p, invproj); + np.w = np.w == 0? 0: 1.0/np.w; + np.x *= np.w; + np.y *= np.w; + np.z *= np.w; + np.w = 1; + return np; +} + +Point3 +viewport2vcs(Camera *c, Point3 p) +{ + return ndc2vcs(c, viewport2ndc(c->vp->getfb(c->vp), p)); +} + +Point3 +vcs2world(Camera *c, Point3 p) +{ + return invrframexform3(p, *c); +} + +Point3 +viewport2world(Camera *c, Point3 p) +{ + return vcs2world(c, viewport2vcs(c, p)); +} + +void +perspective(Matrix3 m, double fovy, double a, double n, double f) +{ + double cotan; + + cotan = 1/tan(fovy/2); + identity3(m); + m[0][0] = cotan/a; + m[1][1] = cotan; + m[2][2] = (f+n)/(f-n); + m[2][3] = 2*f*n/(f-n); + m[3][2] = -1; + m[3][3] = 0; +} + +void +orthographic(Matrix3 m, double l, double r, double b, double t, double n, double f) +{ + identity3(m); + m[0][0] = 2/(r-l); + m[1][1] = 2/(t-b); + m[2][2] = 2/(f-n); + m[0][3] = -(r+l)/(r-l); + m[1][3] = -(t+b)/(t-b); + m[2][3] = -(f+n)/(f-n); +} -- cgit v1.2.3