From 18b89c6edc29a009b1419e6d34bc3eef2dd911b9 Mon Sep 17 00:00:00 2001 From: Tavian Barnes Date: Fri, 20 May 2011 18:59:18 -0600 Subject: Switch to Blender model of filtered transparency. --- libdimension-python/Color.c | 46 +++++++++++++------------- libdimension-python/tests/color.py | 10 +++--- libdimension/ambient.c | 2 +- libdimension/color.c | 67 +++++++++++++++++++++++++------------- libdimension/dimension/color.h | 10 +++--- libdimension/gl.c | 18 +++++----- libdimension/png.c | 14 ++++---- libdimension/raytrace.c | 9 ++--- libdimension/tests/render.c | 6 ++-- 9 files changed, 99 insertions(+), 83 deletions(-) diff --git a/libdimension-python/Color.c b/libdimension-python/Color.c index ae8de2f..c147603 100644 --- a/libdimension-python/Color.c +++ b/libdimension-python/Color.c @@ -23,12 +23,12 @@ bool dmnsn_py_Color_args(dmnsn_color *c, PyObject *args, PyObject *kwds) { - c->filter = 0.0; c->trans = 0.0; + c->filter = 0.0; - static char *kwlist[] = { "red", "green", "blue", "filter", "trans", NULL }; + static char *kwlist[] = { "red", "green", "blue", "trans", "filter", NULL }; if (PyArg_ParseTupleAndKeywords(args, kwds, "ddd|dd", kwlist, - &c->R, &c->G, &c->B, &c->filter, &c->trans)) { + &c->R, &c->G, &c->B, &c->trans, &c->filter)) { return true; } else { if (kwds) @@ -57,12 +57,12 @@ dmnsn_py_Color_repr(dmnsn_py_Color *self) PyObject *R = PyFloat_FromDouble(self->c.R); PyObject *G = PyFloat_FromDouble(self->c.G); PyObject *B = PyFloat_FromDouble(self->c.B); - PyObject *filter = PyFloat_FromDouble(self->c.filter); PyObject *trans = PyFloat_FromDouble(self->c.trans); + PyObject *filter = PyFloat_FromDouble(self->c.filter); - if (!R || !G || !B || !filter || !trans) { - Py_XDECREF(trans); + if (!R || !G || !B || !trans || !filter) { Py_XDECREF(filter); + Py_XDECREF(trans); Py_XDECREF(B); Py_XDECREF(G); Py_XDECREF(R); @@ -70,9 +70,9 @@ dmnsn_py_Color_repr(dmnsn_py_Color *self) } PyObject *repr = PyUnicode_FromFormat("dimension.Color(%R, %R, %R, %R, %R)", - R, G, B, filter, trans); - Py_XDECREF(trans); + R, G, B, trans, filter); Py_XDECREF(filter); + Py_XDECREF(trans); Py_XDECREF(B); Py_XDECREF(G); Py_XDECREF(R); @@ -85,12 +85,12 @@ dmnsn_py_Color_str(dmnsn_py_Color *self) PyObject *R = PyFloat_FromDouble(self->c.R); PyObject *G = PyFloat_FromDouble(self->c.G); PyObject *B = PyFloat_FromDouble(self->c.B); - PyObject *filter = PyFloat_FromDouble(self->c.filter); PyObject *trans = PyFloat_FromDouble(self->c.trans); + PyObject *filter = PyFloat_FromDouble(self->c.filter); - if (!R || !G || !B || !filter || !trans) { - Py_XDECREF(trans); + if (!R || !G || !B || !trans || !filter) { Py_XDECREF(filter); + Py_XDECREF(trans); Py_XDECREF(B); Py_XDECREF(G); Py_XDECREF(R); @@ -103,11 +103,11 @@ dmnsn_py_Color_str(dmnsn_py_Color *self) R, G, B); } else { str = PyUnicode_FromFormat("", - R, G, B, filter, trans); + " trans = %S, filter = %S>", + R, G, B, trans, filter); } - Py_XDECREF(trans); Py_XDECREF(filter); + Py_XDECREF(trans); Py_XDECREF(B); Py_XDECREF(G); Py_XDECREF(R); @@ -131,11 +131,11 @@ dmnsn_py_Color_richcompare(PyObject *lhs, PyObject *rhs, int op) double rdiff = (clhs->c.R - crhs->c.R)*(clhs->c.R - crhs->c.R); double gdiff = (clhs->c.G - crhs->c.G)*(clhs->c.G - crhs->c.G); double bdiff = (clhs->c.B - crhs->c.B)*(clhs->c.B - crhs->c.B); - double fdiff = (clhs->c.filter - crhs->c.filter) - * (clhs->c.filter - crhs->c.filter); double tdiff = (clhs->c.trans - crhs->c.trans) * (clhs->c.trans - crhs->c.trans); - bool equal = sqrt(rdiff + gdiff + bdiff + fdiff + tdiff) < dmnsn_epsilon; + double fdiff = (clhs->c.filter - crhs->c.filter) + * (clhs->c.filter - crhs->c.filter); + bool equal = sqrt(rdiff + gdiff + bdiff + tdiff + fdiff) < dmnsn_epsilon; PyObject *result; switch (op) { @@ -235,15 +235,15 @@ dmnsn_py_Color_get_blue(dmnsn_py_Color *self, void *closure) } static PyObject * -dmnsn_py_Color_get_filter(dmnsn_py_Color *self, void *closure) +dmnsn_py_Color_get_trans(dmnsn_py_Color *self, void *closure) { - return PyFloat_FromDouble(self->c.filter); + return PyFloat_FromDouble(self->c.trans); } static PyObject * -dmnsn_py_Color_get_trans(dmnsn_py_Color *self, void *closure) +dmnsn_py_Color_get_filter(dmnsn_py_Color *self, void *closure) { - return PyFloat_FromDouble(self->c.trans); + return PyFloat_FromDouble(self->c.filter); } static PyGetSetDef dmnsn_py_Color_getsetters[] = { @@ -253,10 +253,10 @@ static PyGetSetDef dmnsn_py_Color_getsetters[] = { "Green componant", NULL }, { "blue", (getter)dmnsn_py_Color_get_blue, NULL, "Blue componant", NULL }, - { "filter", (getter)dmnsn_py_Color_get_filter, NULL, - "Filter component", NULL }, { "trans", (getter)dmnsn_py_Color_get_trans, NULL, "Transmittance component", NULL }, + { "filter", (getter)dmnsn_py_Color_get_filter, NULL, + "Filter component", NULL }, { NULL } }; diff --git a/libdimension-python/tests/color.py b/libdimension-python/tests/color.py index 72056d1..2d8545d 100755 --- a/libdimension-python/tests/color.py +++ b/libdimension-python/tests/color.py @@ -24,15 +24,15 @@ from dimension import * # Treat warnings as errors for tests dieOnWarnings(True) -c = Color(0, 0.5, 1, filter = 0.25, trans = 0.35) -assert repr(c) == 'dimension.Color(0.0, 0.5, 1.0, 0.25, 0.35)', repr(c) +c = Color(0, 0.5, 1, trans = 0.5, filter = 0.35) +assert repr(c) == 'dimension.Color(0.0, 0.5, 1.0, 0.5, 0.35)', repr(c) assert str(c) == '', str(c) +trans = 0.5, filter = 0.35>', str(c) assert c.red == 0, c.red assert c.green == 0.5, c.green assert c.blue == 1, c.blue -assert c.filter == 0.25, c.filter -assert c.trans == 0.35, c.trans +assert c.trans == 0.5, c.filter +assert c.filter == 0.35, c.trans c = Color(1, 0.5, 0) assert str(c) == '', str(c) diff --git a/libdimension/ambient.c b/libdimension/ambient.c index 45dca3f..180889b 100644 --- a/libdimension/ambient.c +++ b/libdimension/ambient.c @@ -33,8 +33,8 @@ dmnsn_ambient_finish_fn(const dmnsn_finish *finish, dmnsn_color pigment) { dmnsn_color *ambient = finish->ptr; dmnsn_color ret = dmnsn_color_illuminate(*ambient, pigment); - ret.filter = 0.0; ret.trans = 0.0; + ret.filter = 0.0; return ret; } diff --git a/libdimension/color.c b/libdimension/color.c index 24a4574..07d8ed8 100644 --- a/libdimension/color.c +++ b/libdimension/color.c @@ -30,71 +30,71 @@ const dmnsn_color dmnsn_black = { .R = 0.0, .G = 0.0, .B = 0.0, + .trans = 0.0, .filter = 0.0, - .trans = 0.0 }; const dmnsn_color dmnsn_white = { .R = 1.0, .G = 1.0, .B = 1.0, + .trans = 0.0, .filter = 0.0, - .trans = 0.0 }; const dmnsn_color dmnsn_clear = { .R = 0.0, .G = 0.0, .B = 0.0, + .trans = 1.0, .filter = 0.0, - .trans = 1.0 }; const dmnsn_color dmnsn_red = { .R = 1.0, .G = 0.0, .B = 0.0, + .trans = 0.0, .filter = 0.0, - .trans = 0.0 }; const dmnsn_color dmnsn_green = { .R = 0.0, .G = 1.0, .B = 0.0, + .trans = 0.0, .filter = 0.0, - .trans = 0.0 }; const dmnsn_color dmnsn_blue = { .R = 0.0, .G = 0.0, .B = 1.0, + .trans = 0.0, .filter = 0.0, - .trans = 0.0 }; const dmnsn_color dmnsn_magenta = { .R = 1.0, .G = 0.0, .B = 1.0, + .trans = 0.0, .filter = 0.0, - .trans = 0.0 }; const dmnsn_color dmnsn_orange = { .R = 1.0, .G = 0.5, .B = 0.0, + .trans = 0.0, .filter = 0.0, - .trans = 0.0 }; const dmnsn_color dmnsn_yellow = { .R = 1.0, .G = 1.0, .B = 0.0, + .trans = 0.0, .filter = 0.0, - .trans = 0.0 }; const dmnsn_color dmnsn_cyan = { .R = 0.0, .G = 1.0, .B = 1.0, .filter = 0.0, - .trans = 0.0 + .trans = 0.0, }; /* Greyscale color intensity */ @@ -111,11 +111,19 @@ dmnsn_color_add(dmnsn_color c1, dmnsn_color c2) { dmnsn_color ret = dmnsn_new_color(c1.R + c2.R, c1.G + c2.G, c1.B + c2.B); + /* Switch back into absolute filter and transmittance space */ double n1 = dmnsn_color_intensity(c1), n2 = dmnsn_color_intensity(c2); - if (n1 + n2 != 0.0) { - ret.filter = (n1*c1.filter + n2*c2.filter)/(n1 + n2); - } - ret.trans = c1.trans + c2.trans; + double f1 = c1.filter*c1.trans, f2 = c2.filter*c2.trans; + double t1 = c1.trans - f1, t2 = c2.trans - f2; + double f = 0.0; + if (n1 + n2 >= dmnsn_epsilon) + f = (n1*f1 + n2*f2)/(n1 + n2); + double t = t1 + t2; + + /* Switch back */ + ret.trans = f + t; + if (ret.trans >= dmnsn_epsilon) + ret.filter = f/ret.trans; return ret; } @@ -138,8 +146,8 @@ dmnsn_color_gradient(dmnsn_color c1, dmnsn_color c2, double n) n*(c2.R - c1.R) + c1.R, n*(c2.G - c1.G) + c1.G, n*(c2.B - c1.B) + c1.B, - n*(c2.filter - c1.filter) + c1.filter, - n*(c2.trans - c1.trans) + c1.trans + n*(c2.trans - c1.trans) + c1.trans, + n*(c2.filter - c1.filter) + c1.filter ); } @@ -147,15 +155,28 @@ dmnsn_color_gradient(dmnsn_color c1, dmnsn_color c2, double n) dmnsn_color dmnsn_filter_light(dmnsn_color light, dmnsn_color filter) { - dmnsn_color transmitted = dmnsn_color_mul(filter.trans, light); + dmnsn_color transmitted = dmnsn_color_mul( + (1.0 - filter.filter)*filter.trans, + light + ); dmnsn_color filtered = dmnsn_color_mul( - filter.filter, + filter.filter*filter.trans, dmnsn_color_illuminate(filter, light) ); dmnsn_color ret = dmnsn_color_add(transmitted, filtered); - ret.filter = light.filter*dmnsn_color_intensity(filtered) - + filter.filter*light.trans + filter.trans*light.filter; - ret.trans = filter.trans*light.trans; + + /* Switch back into absolute filter and transmittance space */ + double lf = light.filter*light.trans, ff = filter.filter*filter.trans; + double lt = light.trans - lf, ft = filter.trans - ff; + double f = lf*(dmnsn_color_intensity(filtered) + ft) + lt*ff; + double t = ft*lt; + + /* Switch back */ + ret.trans = f + t; + ret.filter = 0.0; + if (ret.trans >= dmnsn_epsilon) + ret.filter = f/ret.trans; + return ret; } @@ -164,11 +185,11 @@ dmnsn_color dmnsn_apply_translucency(dmnsn_color filtered, dmnsn_color filter) { dmnsn_color ret = dmnsn_color_add( - dmnsn_color_mul(1.0 - (filter.filter + filter.trans), filter), + dmnsn_color_mul(1.0 - filter.trans, filter), filtered ); - ret.filter = filtered.filter; ret.trans = filtered.trans; + ret.filter = filtered.filter; return ret; } diff --git a/libdimension/dimension/color.h b/libdimension/dimension/color.h index 504ec47..be5dc3d 100644 --- a/libdimension/dimension/color.h +++ b/libdimension/dimension/color.h @@ -31,10 +31,8 @@ typedef struct { double G; /**< sRGB green value. */ double B; /**< sRGB blue value. */ - /** Filtered transparency. */ - double filter; - /** Unfiltered transparency; filter + trans should be <= 1. */ - double trans; + double trans; /**< Translucency. */ + double filter; /**< Degree of filtering. */ } dmnsn_color; /* Standard colors */ @@ -61,9 +59,9 @@ dmnsn_new_color(double R, double G, double B) /** Construct a new color with transparent components. */ DMNSN_INLINE dmnsn_color -dmnsn_new_color5(double R, double G, double B, double filter, double trans) +dmnsn_new_color5(double R, double G, double B, double trans, double filter) { - dmnsn_color ret = { R, G, B, filter, trans }; + dmnsn_color ret = { R, G, B, trans, filter }; return ret; } diff --git a/libdimension/gl.c b/libdimension/gl.c index 587038f..7fd3cb9 100644 --- a/libdimension/gl.c +++ b/libdimension/gl.c @@ -110,13 +110,12 @@ dmnsn_gl_write_canvas(const dmnsn_canvas *canvas) pixel[2] = color.B*UINT16_MAX; } - double alpha = dmnsn_color_intensity(color)*color.filter + color.trans; - if (alpha <= 0.0) { + if (color.trans <= 0.0) { pixel[3] = 0; - } else if (alpha >= 1.0) { + } else if (color.trans >= 1.0) { pixel[3] = UINT16_MAX; } else { - pixel[3] = alpha*UINT16_MAX; + pixel[3] = color.trans*UINT16_MAX; } } } @@ -152,8 +151,8 @@ dmnsn_gl_read_canvas(size_t x0, size_t y0, dmnsn_color color = dmnsn_new_color5((double)pixel[0]/UINT16_MAX, (double)pixel[1]/UINT16_MAX, (double)pixel[2]/UINT16_MAX, - 0.0, - (double)pixel[3]/UINT16_MAX); + (double)pixel[3]/UINT16_MAX, + 0.0); dmnsn_set_pixel(canvas, x, y, color); } } @@ -196,12 +195,11 @@ dmnsn_gl_optimizer_fn(const dmnsn_canvas *canvas, pixel[2] = color.B*UINT16_MAX; } - double alpha = dmnsn_color_intensity(color)*color.filter + color.trans; - if (alpha <= 0.0) { + if (color.trans <= 0.0) { pixel[3] = 0; - } else if (alpha >= 1.0) { + } else if (color.trans >= 1.0) { pixel[3] = UINT16_MAX; } else { - pixel[3] = alpha*UINT16_MAX; + pixel[3] = color.trans*UINT16_MAX; } } diff --git a/libdimension/png.c b/libdimension/png.c index d80ecc0..3920e7e 100644 --- a/libdimension/png.c +++ b/libdimension/png.c @@ -92,13 +92,12 @@ dmnsn_png_optimizer_fn(const dmnsn_canvas *canvas, pixel[2] = color.B*UINT16_MAX; } - double alpha = dmnsn_color_intensity(color)*color.filter + color.trans; - if (alpha <= 0.0) { + if (color.trans <= 0.0) { pixel[3] = 0; - } else if (alpha >= 1.0) { + } else if (color.trans >= 1.0) { pixel[3] = UINT16_MAX; } else { - pixel[3] = alpha*UINT16_MAX; + pixel[3] = color.trans*UINT16_MAX; } } @@ -299,13 +298,12 @@ dmnsn_png_write_canvas_thread(void *ptr) row[4*x + 2] = color.B*UINT16_MAX; } - double alpha = color.filter + color.trans; - if (alpha <= 0.0) { + if (color.trans <= 0.0) { row[4*x + 3] = 0; - } else if (alpha >= 1.0) { + } else if (color.trans >= 1.0) { row[4*x + 3] = UINT16_MAX; } else { - row[4*x + 3] = alpha*UINT16_MAX; + row[4*x + 3] = color.trans*UINT16_MAX; } } diff --git a/libdimension/raytrace.c b/libdimension/raytrace.c index 87e0b8f..deb0080 100644 --- a/libdimension/raytrace.c +++ b/libdimension/raytrace.c @@ -273,7 +273,7 @@ dmnsn_raytrace_light_ray(const dmnsn_raytrace_state *state, dmnsn_raytrace_pigment(&shadow_state); if ((state->scene->quality & DMNSN_RENDER_TRANSLUCENCY) - && (shadow_state.pigment.filter || shadow_state.pigment.trans)) + && shadow_state.pigment.trans >= dmnsn_epsilon) { color = dmnsn_filter_light(color, shadow_state.pigment); shadow_ray.x0 = dmnsn_line_point(shadow_ray, shadow_caster.t); @@ -328,7 +328,8 @@ dmnsn_raytrace_lighting(dmnsn_raytrace_state *state) state->additional = dmnsn_color_add(specular, state->additional); } else { state->diffuse = state->pigment; - state->diffuse.filter = state->diffuse.trans = 0.0; + state->diffuse.trans = 0.0; + state->diffuse.filter = 0.0; } } } @@ -355,8 +356,8 @@ dmnsn_raytrace_reflection(const dmnsn_raytrace_state *state) state, finish, reflection_fn, dmnsn_black, rec, state->pigment, state->reflected, state->intersection->normal ); - reflected.filter = 0.0; reflected.trans = 0.0; + reflected.filter = 0.0; } return reflected; @@ -366,7 +367,7 @@ dmnsn_raytrace_reflection(const dmnsn_raytrace_state *state) static void dmnsn_raytrace_translucency(dmnsn_raytrace_state *state) { - if (state->pigment.filter || state->pigment.trans) { + if (state->pigment.trans >= dmnsn_epsilon) { dmnsn_line trans_ray = dmnsn_new_line(state->r, state->intersection->ray.n); trans_ray = dmnsn_line_add_epsilon(trans_ray); diff --git a/libdimension/tests/render.c b/libdimension/tests/render.c index f35f5bb..a84ba63 100644 --- a/libdimension/tests/render.c +++ b/libdimension/tests/render.c @@ -73,7 +73,7 @@ dmnsn_new_test_scene(void) dmnsn_pattern *sky_gradient = dmnsn_new_gradient_pattern(dmnsn_y); dmnsn_map *sky_gradient_color_map = dmnsn_new_color_map(); dmnsn_add_map_entry(sky_gradient_color_map, 0.0, &dmnsn_orange); - dmnsn_color background = dmnsn_new_color5(0.0, 0.1, 0.2, 0.1, 0.0); + dmnsn_color background = dmnsn_new_color5(0.0, 0.1, 0.2, 0.1, 1.0); dmnsn_add_map_entry(sky_gradient_color_map, 0.35, &background); dmnsn_pigment *sky_pigment = dmnsn_new_color_map_pigment(sky_gradient, sky_gradient_color_map); @@ -95,8 +95,8 @@ dmnsn_new_test_scene(void) cube->texture = dmnsn_new_texture(); dmnsn_color cube_color = dmnsn_blue; - cube_color.filter = 0.25; - cube_color.trans = 0.5; + cube_color.trans = 0.75; + cube_color.filter = 1.0/3.0; cube->texture->pigment = dmnsn_new_solid_pigment(cube_color); dmnsn_color reflect = dmnsn_color_mul(0.5, dmnsn_white); -- cgit v1.2.3