From 3cff39f02c54eb0f06835e1e0b6a2f6f02d0b5cb Mon Sep 17 00:00:00 2001 From: Tavian Barnes Date: Mon, 28 Nov 2011 21:15:44 -0500 Subject: Be more consistent about using sRGB in the client. Also, expose the sRGB C and C^-1 functions. --- libdimension-python/tests/color.py | 1 + libdimension-python/wrapper.pxd | 4 +- libdimension-python/wrapper.pyx | 23 +++++------ libdimension/color.c | 80 ++++++++++++++++++++++---------------- libdimension/dimension/color.h | 9 ++++- libdimension/tests/render.c | 16 ++++---- 6 files changed, 77 insertions(+), 56 deletions(-) diff --git a/libdimension-python/tests/color.py b/libdimension-python/tests/color.py index 47ac975..1299a88 100755 --- a/libdimension-python/tests/color.py +++ b/libdimension-python/tests/color.py @@ -50,3 +50,4 @@ assert Cyan == Color(0, 1, 1), Cyan assert Red + Blue == Magenta, Red + Blue assert 0.5*White == Color(0.5, 0.5, 0.5), 0.5*White +assert White/2 == Color(0.5, 0.5, 0.5), White/2 diff --git a/libdimension-python/wrapper.pxd b/libdimension-python/wrapper.pxd index 920f6f2..94966f3 100644 --- a/libdimension-python/wrapper.pxd +++ b/libdimension-python/wrapper.pxd @@ -155,8 +155,10 @@ cdef extern from "../libdimension/dimension.h": dmnsn_color dmnsn_new_color5(double R, double G, double B, double trans, double filter) - dmnsn_color dmnsn_color_from_sRGB(dmnsn_color color) + double dmnsn_sRGB_gamma(double Clinear) dmnsn_color dmnsn_color_to_sRGB(dmnsn_color color) + double dmnsn_sRGB_inverse_gamma(double CsRGB) + dmnsn_color dmnsn_color_from_sRGB(dmnsn_color color) double dmnsn_color_intensity(dmnsn_color color) dmnsn_color dmnsn_color_add(dmnsn_color color1, dmnsn_color color2) diff --git a/libdimension-python/wrapper.pyx b/libdimension-python/wrapper.pyx index 5431e75..2ed85e0 100644 --- a/libdimension-python/wrapper.pyx +++ b/libdimension-python/wrapper.pyx @@ -465,6 +465,8 @@ cdef class Color: return _sRGBColor(dmnsn_color_mul(rhs, (lhs)._sRGB)) else: return _sRGBColor(dmnsn_color_mul(lhs, (rhs)._sRGB)) + def __truediv__(Color lhs not None, double rhs): + return _sRGBColor(dmnsn_color_mul(1/rhs, lhs._sRGB)) def __richcmp__(lhs, rhs, int op): cdef clhs = Color(lhs) @@ -831,9 +833,9 @@ cdef class Diffuse(Finish): Keyword arguments: diffuse -- the intensity of the diffuse reflection """ - cdef dmnsn_color gray = dmnsn_color_mul(diffuse, dmnsn_white) - diffuse = dmnsn_color_intensity(dmnsn_color_from_sRGB(gray)) - self._finish.diffuse = dmnsn_new_lambertian(diffuse) + self._finish.diffuse = dmnsn_new_lambertian( + dmnsn_sRGB_inverse_gamma(diffuse) + ) cdef class Phong(Finish): """Phong specular highlight.""" @@ -841,7 +843,10 @@ cdef class Phong(Finish): """ Create a Phong highlight. """ - self._finish.specular = dmnsn_new_phong(strength, size) + self._finish.specular = dmnsn_new_phong( + dmnsn_sRGB_inverse_gamma(strength), + size + ) cdef class Reflection(Finish): """Reflective finish.""" @@ -857,10 +862,8 @@ cdef class Reflection(Finish): if max is None: max = min - # Use sRGB value because Reflection(0.5) should really mean "reflect half - # the light" - self._finish.reflection = dmnsn_new_basic_reflection(Color(min)._sRGB, - Color(max)._sRGB, + self._finish.reflection = dmnsn_new_basic_reflection(Color(min)._c, + Color(max)._c, falloff) ############ @@ -1317,9 +1320,7 @@ cdef class PointLight(Light): location -- the origin of the light rays color -- the color and intensity of the light """ - # Take the sRGB component because "color = 0.5*White" should really mean - # a half-intensity white light - self._light = dmnsn_new_point_light(Vector(location)._v, Color(color)._sRGB) + self._light = dmnsn_new_point_light(Vector(location)._v, Color(color)._c) Light.__init__(self) ########### diff --git a/libdimension/color.c b/libdimension/color.c index efa4f4a..39f9b3d 100644 --- a/libdimension/color.c +++ b/libdimension/color.c @@ -97,69 +97,83 @@ const dmnsn_color dmnsn_cyan = { .trans = 0.0, }; -/** Inverse function of sRGB's `C' function, for the reverse conversion. */ -static double -dmnsn_sRGB_C_inv(double CsRGB) +/* sRGB's `C' function. */ +static inline double +dmnsn_sRGB_C(double Clinear) { /* - * If C represents R, G, and B, then the Clinear values are now found as - * follows: + * If C represents R, G, and B, then the sRGB values are now found as follows: * - * { Csrgb/12.92, Csrgb <= 0.04045 - * Clinear = { 1/2.4 - * { ((Csrgb + 0.055)/1.055) , Csrgb > 0.04045 + * { 12.92*Clinear, Clinear <= 0.0031308 + * Csrgb = { 1/2.4 + * { (1.055)*Clinear - 0.055, Clinear > 0.0031308 */ - if (CsRGB == 1.0) { + if (Clinear == 1.0) { return 1.0; /* Map 1.0 to 1.0 instead of 0.9999999999999999 */ - } else if (CsRGB <= 0.040449936) { - return CsRGB/12.92; + } else if (Clinear > 0.0031308) { + return 1.055*pow(Clinear, 1.0/2.4) - 0.055; } else { - return pow((CsRGB + 0.055)/1.055, 2.4); + return 12.92*Clinear; } } -/* Convert from sRGB space */ +/* Export dmnsn_sRGB_C */ +double +dmnsn_sRGB_gamma(double Clinear) +{ + return dmnsn_sRGB_C(Clinear); +} + +/* Convert to sRGB space */ dmnsn_color -dmnsn_color_from_sRGB(dmnsn_color color) +dmnsn_color_to_sRGB(dmnsn_color color) { dmnsn_color ret = { - .R = dmnsn_sRGB_C_inv(color.R), - .G = dmnsn_sRGB_C_inv(color.G), - .B = dmnsn_sRGB_C_inv(color.B), + .R = dmnsn_sRGB_C(color.R), + .G = dmnsn_sRGB_C(color.G), + .B = dmnsn_sRGB_C(color.B), .trans = color.trans, .filter = color.filter, }; return ret; } -/** sRGB's `C' function. */ -static double -dmnsn_sRGB_C(double Clinear) +/* Inverse function of sRGB's `C' function, for the reverse conversion. */ +double +dmnsn_sRGB_C_inv(double CsRGB) { /* - * If C represents R, G, and B, then the sRGB values are now found as follows: + * If C represents R, G, and B, then the Clinear values are now found as + * follows: * - * { 12.92*Clinear, Clinear <= 0.0031308 - * Csrgb = { 1/2.4 - * { (1.055)*Clinear - 0.055, Clinear > 0.0031308 + * { Csrgb/12.92, Csrgb <= 0.04045 + * Clinear = { 1/2.4 + * { ((Csrgb + 0.055)/1.055) , Csrgb > 0.04045 */ - if (Clinear == 1.0) { + if (CsRGB == 1.0) { return 1.0; /* Map 1.0 to 1.0 instead of 0.9999999999999999 */ - } else if (Clinear <= 0.0031308) { - return 12.92*Clinear; + } else if (CsRGB <= 0.040449936) { + return CsRGB/12.92; } else { - return 1.055*pow(Clinear, 1.0/2.4) - 0.055; + return pow((CsRGB + 0.055)/1.055, 2.4); } } -/* Convert to sRGB space */ +/* Export dmnsn_sRGB_C_inv */ +double +dmnsn_sRGB_inverse_gamma(double CsRGB) +{ + return dmnsn_sRGB_C_inv(CsRGB); +} + +/* Convert from sRGB space */ dmnsn_color -dmnsn_color_to_sRGB(dmnsn_color color) +dmnsn_color_from_sRGB(dmnsn_color color) { dmnsn_color ret = { - .R = dmnsn_sRGB_C(color.R), - .G = dmnsn_sRGB_C(color.G), - .B = dmnsn_sRGB_C(color.B), + .R = dmnsn_sRGB_C_inv(color.R), + .G = dmnsn_sRGB_C_inv(color.G), + .B = dmnsn_sRGB_C_inv(color.B), .trans = color.trans, .filter = color.filter, }; diff --git a/libdimension/dimension/color.h b/libdimension/dimension/color.h index 8470957..295400b 100644 --- a/libdimension/dimension/color.h +++ b/libdimension/dimension/color.h @@ -85,10 +85,15 @@ dmnsn_color_saturate(dmnsn_color color) /* Perceptual color manipulation */ -/** Convert from sRGB space. */ -dmnsn_color dmnsn_color_from_sRGB(dmnsn_color color); +/** Apply sRGB gamma */ +double dmnsn_sRGB_gamma(double Clinear); /** Convert to sRGB space. */ dmnsn_color dmnsn_color_to_sRGB(dmnsn_color color); +/** Remove sRGB gamma */ +double dmnsn_sRGB_inverse_gamma(double CsRGB); +/** Convert from sRGB space. */ +dmnsn_color dmnsn_color_from_sRGB(dmnsn_color color); + /** Greyscale color intensity. */ double dmnsn_color_intensity(dmnsn_color color); /** Add two colors together. */ diff --git a/libdimension/tests/render.c b/libdimension/tests/render.c index ae4a644..abd88d6 100644 --- a/libdimension/tests/render.c +++ b/libdimension/tests/render.c @@ -31,11 +31,7 @@ dmnsn_test_scene_set_defaults(dmnsn_scene *scene) default_finish->ambient = dmnsn_new_basic_ambient( dmnsn_color_from_sRGB(dmnsn_color_mul(0.1, dmnsn_white)) ); - default_finish->diffuse = dmnsn_new_lambertian( - dmnsn_color_intensity( - dmnsn_color_from_sRGB(dmnsn_color_mul(0.7, dmnsn_white)) - ) - ); + default_finish->diffuse = dmnsn_new_lambertian(dmnsn_sRGB_inverse_gamma(0.7)); } static void @@ -130,9 +126,10 @@ dmnsn_test_scene_add_hollow_cube(dmnsn_scene *scene) cube->texture = dmnsn_new_texture(); cube->texture->pigment = dmnsn_new_solid_pigment(cube_color); - dmnsn_color reflect = dmnsn_color_mul(0.5, dmnsn_white); - cube->texture->finish.reflection - = dmnsn_new_basic_reflection(dmnsn_black, reflect, 1.0); + dmnsn_color reflect = + dmnsn_color_from_sRGB(dmnsn_color_mul(0.5, dmnsn_white)); + cube->texture->finish.reflection = + dmnsn_new_basic_reflection(dmnsn_black, reflect, 1.0); cube->interior = dmnsn_new_interior(); cube->interior->ior = 1.1; @@ -140,7 +137,8 @@ dmnsn_test_scene_add_hollow_cube(dmnsn_scene *scene) dmnsn_object *sphere = dmnsn_new_sphere(); sphere->texture = dmnsn_new_texture(); sphere->texture->pigment = dmnsn_new_solid_pigment(dmnsn_green); - sphere->texture->finish.specular = dmnsn_new_phong(0.2, 40.0); + sphere->texture->finish.specular = + dmnsn_new_phong(dmnsn_sRGB_inverse_gamma(0.2), 40.0); sphere->trans = dmnsn_scale_matrix(dmnsn_new_vector(1.25, 1.25, 1.25)); dmnsn_object *hollow_cube = dmnsn_new_csg_difference(cube, sphere); -- cgit v1.2.3