From 34efcdbb29b75754fef5066c5999671bc2d2fb12 Mon Sep 17 00:00:00 2001 From: Charles McGarvey Date: Tue, 14 Feb 2012 14:20:46 -0700 Subject: [PATCH] add phong interpolation (lighting) --- Makefile | 2 +- animate.lua | 5 ++++- config.h | 20 ++++++++++++++----- mat.h | 18 ++++++----------- raster.c | 54 +++++++++++++++++++++++++++++++++++++------------- scene.c | 2 +- tri.h | 57 +++++++++++++++++++++++++++++++++++++++++++---------- vec.h | 31 +++++++++++++++++++++++++---- 8 files changed, 141 insertions(+), 48 deletions(-) diff --git a/Makefile b/Makefile index 7d119e4..aaac3cd 100644 --- a/Makefile +++ b/Makefile @@ -36,5 +36,5 @@ debug: $(PROJECT) -include $(DEPS) $(OBJS): Makefile -.PHONY: all clean distclean run debug +.PHONY: all clean distclean dist run debug diff --git a/animate.lua b/animate.lua index 476051f..9917cb1 100755 --- a/animate.lua +++ b/animate.lua @@ -15,7 +15,7 @@ local size = {w = 640, h = 480} -- Set the number of frames to be rendered for the animation. -local frames = 512 +local frames = 360 -- Define the code to calculate where the camera is, in world coordinates. local eye = function(frame) @@ -47,6 +47,7 @@ c 0.9 0.8 0.7 0.6 0.5 0.4 0.3 0.2 0.1 t 2 0 -2 s 1 1 1 g dragon.raw +M 0.3 0.3 0.3 5 c 0.7 0.3 0.2 0.8 0.2 0.1 0.9 0.2 0.2 t -2 -1 -2 s 2 2 2 @@ -55,10 +56,12 @@ c 0.1 0.2 0.7 0.1 0.3 0.9 0.2 0.1 0.8 t 2 -1.5 2 s 10 10 10 g bunny.raw +M 0.2 0.2 0.2 1 c 0.9 0.8 0.9 0.8 0.7 0.9 0.9 0.8 0.7 t -2 -1 2 s 10 10 10 g teapot2.raw +M 1 1 1 128 c 0 1 0 0 1 0 0 1 0 t 0 -1 0 s 0.6 0.6 0.6 diff --git a/config.h b/config.h index 9637dea..a1d7fb9 100644 --- a/config.h +++ b/config.h @@ -54,6 +54,17 @@ #define IF_DEPTH_TEST(X) #endif +/* + * DOUBLE_FLOAT + * If enabled, scalars will be of type double. This provides and insane level + * of precision at a performance cost. The default behavior just uses floats. + */ +#if DOUBLE_FLOAT +#define IF_DOUBLE_FLOAT(X) X +#else +#define IF_DOUBLE_FLOAT(X) +#endif + /* * EXPORT_BMP * If enabled, each scene rasterization will be saved as a BMP image file. @@ -101,11 +112,10 @@ * LIGHTING * If enabled, local lighting will be used to increase realism of the scene. * This option has a performance cost, but it can produce interesting visuals. - * The behavior of this option also depends on the SMOOTH_COLOR option and - * whether or not the model has unique vertex normals; if SMOOTH_COLOR is - * disabled, each triangle will be shaded a flat color. If it is enabled, - * Gouraud interpolation is used to smooth the lighting across the face of - * each triangle. See the PRE_NORMALS and SMOOTH_COLOR options. + * The behavior of this option is effected by its precise value: + * 1 Phong lighting model with flat (per-face) interpolation. + * 2 Phong lighting model with Gouraud (per-vertex) interpolation. + * 3 Phong lighting model with Phong (per-pixel) interpolation. */ #if LIGHTING #define IF_LIGHTING(X) X diff --git a/mat.h b/mat.h index 499d092..c188723 100644 --- a/mat.h +++ b/mat.h @@ -237,22 +237,16 @@ mat_t MAT_ROTATE_Z(scal_t theta) INLINE_MAYBE mat_t MAT_ROTATE(scal_t theta, scal_t x, scal_t y, scal_t z) { - /* - * This code is an implementation of an algorithm described by Glenn - * Murray at http://inside.mines.edu/~gmurray/ArbitraryAxisRotation/ - */ vec_t v = vec_normalize(vec_new(x, y, z)); x = v.x; y = v.y; z = v.z; - scal_t sin_a = scal_sin(theta); - scal_t cos_a = scal_cos(theta); - scal_t x2 = x * x; - scal_t y2 = y * y; - scal_t z2 = z * z; - return mat_new(x2+(S(1.0)-x2)*cos_a, x*y*(S(1.0)-cos_a)-z*sin_a, x*z*(S(1.0)-cos_a)+y*sin_a, S(0.0), - x*y*(S(1.0)-cos_a)+z*sin_a, y2+(S(1.0)-y2)*cos_a, y*z*(S(1.0)-cos_a)-y*sin_a, S(0.0), - x*z*(S(1.0)-cos_a)-y*sin_a, y*z*(S(1.0)-cos_a)+x*sin_a, z2+(S(1.0)-z2)*cos_a, S(0.0), + scal_t c = scal_cos(-theta); + scal_t s = scal_sin(-theta); + scal_t t = S(1.0) - c; + return mat_new(t * x * x + c, t * x * y + s * z, t * x * z - s * y, S(0.0), + t * x * y - s * z, t * y * y + c, t * y * z + s * x, S(0.0), + t * x * z + s * y, t * y * z - s * x, t * z * z + c, S(0.0), S(0.0), S(0.0), S(0.0), S(1.0)); } diff --git a/raster.c b/raster.c index aca2c5a..766e6ea 100644 --- a/raster.c +++ b/raster.c @@ -61,7 +61,7 @@ raster_t* raster_alloc(int width, int height, color_t fill) p->specular = COLOR_WHITE; p->shininess = S(1.0); #endif - /*= light_new(COLOR_WHITE, vec_new(S(-2.0), S(4.0), S(0.0)));*/ + p->zbuf = (scal_t*)mem_alloc(sizeof(scal_t) * size); for (size_t i = 0; i < size; ++i) { p->zbuf[i] = S(1.0); @@ -304,7 +304,7 @@ bool _try_cull_backface(tri_t t) * Determine what color is associated with the given vertex. */ INLINE_MAYBE -color_t _get_vertex_color(raster_t* p, vert_t vert) +color_t _do_phong_lighting(raster_t* p, vert_t vert) { #if LIGHTING color_t color = COLOR_BLACK; @@ -315,7 +315,7 @@ color_t _get_vertex_color(raster_t* p, vert_t vert) vec_t vpos = p->eye; vec_t n = vert.n; vec_t l = vec_normalize(vec_sub(lpos, mpos)); - vec_t r = vec_sub(vec_scale(n, S(2.0) * vec_dot(n, l)), l); + vec_t r = vec_normalize(vec_sub(vec_scale(n, S(2.0) * vec_dot(n, l)), l)); vec_t v = vec_normalize(vec_sub(vpos, mpos)); scal_t kd = scal_max(vec_dot(l, n), S(0.0)); @@ -364,17 +364,34 @@ void raster_draw_tri(raster_t* p, const tri_t* triangle) return; } - tri_t temp = tri_transform(*triangle, p->model); -#if LIGHTING && (!PRE_NORMALS || (!SMOOTH_COLOR && PRE_NORMALS >= 2)) - temp.a.n = temp.b.n = temp.c.n = vec_normalize(tri_normal(temp)); +#if LIGHTING >= 1 + tri_t tl = tri_transform(*triangle, p->model); +#if !PRE_NORMALS + tl.a.n = tl.b.n = tl.c.n = vec_normalize(tri_normal(tl)); #endif -#if !SMOOTH_COLOR - temp.a.c = tri_color(temp); #endif - color_t color1 = _get_vertex_color(p, temp.a); -#if SMOOTH_COLOR - color_t color2 = _get_vertex_color(p, temp.b); - color_t color3 = _get_vertex_color(p, temp.c); + +#if LIGHTING == 1 + vert_t tv = vert_new(tri_midpoint(tl)); + tv.n = vec_normalize(tri_normal(tl)); + tv.c = tri_color(tl); + color_t color = _do_phong_lighting(p, tv); +#elif LIGHTING == 2 && SMOOTH_COLOR + color_t color1 = _do_phong_lighting(p, tl.a); + color_t color2 = _do_phong_lighting(p, tl.b); + color_t color3 = _do_phong_lighting(p, tl.c); +#elif LIGHTING == 2 && !SMOOTH_COLOR + color_t c = tri_color(t); + tl.a.c = tl.b.c = tl.c.c = c; + color_t color1 = _do_phong_lighting(p, tl.a); + color_t color2 = _do_phong_lighting(p, tl.b); + color_t color3 = _do_phong_lighting(p, tl.c); +#elif !LIGHTING && SMOOTH_COLOR + color_t color1 = t.a.c; + color_t color2 = t.b.c; + color_t color3 = t.c.c; +#else + color_t color = tri_color(t); #endif for (int y = bottom; y < top; ++y) { @@ -388,11 +405,20 @@ void raster_draw_tri(raster_t* p, const tri_t* triangle) if (S(-1.0) < v.z && v.z < *n) { #endif color_t* c = p->pixels + y * p->w + x; -#if SMOOTH_COLOR + +#if LIGHTING == 2 || (!LIGHTING && SMOOTH_COLOR) *c = color_interp2(color1, color2, color3, b); +#elif LIGHTING == 3 && SMOOTH_COLOR + *c = _do_phong_lighting(p, tri_interp(tl, b)); +#elif LIGHTING == 3 && !SMOOTH_COLOR + vert_t d = vert_new(tri_point(t, b)); + d.c = tri_color(t); + d.n = tri_normal2(t, b); + *c = _do_phong_lighting(p, d); #else - *c = color1; + *c = color; #endif + #if DEPTH_TEST *n = v.z; } diff --git a/scene.c b/scene.c index be18511..326091f 100644 --- a/scene.c +++ b/scene.c @@ -100,13 +100,13 @@ typedef struct _group _group_t; */ static int _group_try_read_cache(const char* filename, list_t** l) { + int count = 0; char* cachename = mem_strcat(".", filename); FILE* file = fopen(cachename, "rb"); if (file == NULL) { goto fail; } - int count = 0; _CHECK_IO(fread(&count, sizeof(count), 1, file)); float x1, y1, z1, x2, y2, z2, x3, y3, z3; diff --git a/tri.h b/tri.h index de36ae6..ce0977b 100644 --- a/tri.h +++ b/tri.h @@ -71,6 +71,10 @@ tri_t tri_transform(tri_t t, mat_t m) t.a.v = mat_apply(m, t.a.v); t.b.v = mat_apply(m, t.b.v); t.c.v = mat_apply(m, t.c.v); + t.a.n.w = t.b.n.w = t.c.n.w = S(0.0); + t.a.n = vec_normalize(mat_apply(m, t.a.n)); + t.b.n = vec_normalize(mat_apply(m, t.b.n)); + t.c.n = vec_normalize(mat_apply(m, t.c.n)); return t; } @@ -93,7 +97,8 @@ tri_t tri_homodiv(tri_t t) INLINE_MAYBE vec_t tri_normal(tri_t t) { - return vec_cross(vec_sub(t.b.v, t.a.v), vec_sub(t.c.v, t.a.v)); + vec_t n = vec_cross(vec_sub(t.b.v, t.a.v), vec_sub(t.c.v, t.a.v)); + return n; } @@ -137,15 +142,6 @@ bool tri_barycentric(tri_t t, scal_t* b, vec_t v) } -/* - * Get an interpolated z-value at the barycentric coordinates. - */ -INLINE_MAYBE -scal_t tri_z(tri_t t, scal_t b[3]) -{ - return t.a.v.z * b[0] + t.b.v.z * b[1] + t.c.v.z * b[2]; -} - /* * Find the midpoint of the triangle. */ @@ -168,5 +164,46 @@ color_t tri_color(tri_t t) } +/* + * Get an interpolated z-value at the barycentric coordinates. + */ +INLINE_MAYBE +scal_t tri_z(tri_t t, scal_t b[3]) +{ + return t.a.v.z * b[0] + t.b.v.z * b[1] + t.c.v.z * b[2]; +} + +/* + * Calculate an interpolated point. + */ +INLINE_MAYBE +vec_t tri_point(tri_t t, scal_t b[3]) +{ + return vec_interp(t.a.v, t.b.v, t.c.v, b); +} + +/* + * Calculate an interpolated normal. + */ +INLINE_MAYBE +vec_t tri_normal2(tri_t t, scal_t b[3]) +{ + return vec_normalize(vec_interp(t.a.n, t.b.n, t.c.n, b)); +} + +/* + * Calculate an entirely new vertex within the triangle based on barycentric + * coordinates. + */ +INLINE_MAYBE +vert_t tri_interp(tri_t t, scal_t b[3]) +{ + vert_t v = vert_new(tri_point(t, b)); + v.c = color_interp2(t.a.c, t.b.c, t.c.c, b); + v.n = tri_normal2(t, b); + return v; +} + + #endif // _TRI_H_ diff --git a/vec.h b/vec.h index 614e31d..4863523 100644 --- a/vec.h +++ b/vec.h @@ -57,10 +57,11 @@ vec_t vec_new(scal_t x, scal_t y, scal_t z) return vec_new2(x, y, z, S(1.0)); } -#define VEC_ZERO vec_new(S(0.0), S(0.0), S(0.0)) -#define VEC_ORTHO_X vec_new(S(1.0), S(0.0), S(0.0)) -#define VEC_ORTHO_Y vec_new(S(0.0), S(1.0), S(0.0)) -#define VEC_ORTHO_Z vec_new(S(0.0), S(0.0), S(1.0)) +#define VEC_ZERO vec_new(S(0.0), S(0.0), S(0.0)) +#define VEC_ORTHO_X vec_new(S(1.0), S(0.0), S(0.0)) +#define VEC_ORTHO_Y vec_new(S(0.0), S(1.0), S(0.0)) +#define VEC_ORTHO_Z vec_new(S(0.0), S(0.0), S(1.0)) +#define VEC_ZERO_FREE vec_new2(S(0.0), S(0.0), S(0.0), S(0.0)) /* @@ -155,6 +156,15 @@ vec_t vec_add(vec_t a, vec_t b) return a; } +/* + * Add three vectors together. + */ +INLINE_MAYBE +vec_t vec_add2(vec_t a, vec_t b, vec_t c) +{ + return vec_add(vec_add(a, b), c); +} + /* * Subtract a vector from another vector. */ @@ -239,5 +249,18 @@ vec_t vec_homodiv(vec_t v) } +/* + * Interpolate smoothly between three vectors with barycentric coordinates. + */ +INLINE_MAYBE +vec_t vec_interp(vec_t v1, vec_t v2, vec_t v3, scal_t b[3]) +{ + return vec_new(v1.x * b[0] + v2.x * b[1] + v3.x * b[2], + v1.y * b[0] + v2.y * b[1] + v3.y * b[2], + v1.z * b[0] + v2.z * b[1] + v3.z * b[2]); +} + + + #endif // _VEC_H_ -- 2.43.0