star-sf

Set of surface and volume scattering functions
git clone git://git.meso-star.fr/star-sf.git
Log | Files | Refs | README | LICENSE

commit a8ebdb94c0b0ac38131818a4c6ddc8c569212541
parent b81ccffe8f2cb5674edce6e6f22c114129b003f7
Author: Vincent Forest <vincent.forest@meso-star.com>
Date:   Tue, 20 Sep 2016 16:05:44 +0200

Add and test the lambertian BRDF

Diffstat:
Mcmake/CMakeLists.txt | 2++
Msrc/ssf.h | 7+++++++
Asrc/ssf_lambertian_reflection.c | 115+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Msrc/test_ssf_bsdf_view.c | 6------
Asrc/test_ssf_lambertian_reflection.c | 100+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Msrc/test_ssf_utils.h | 6++++++
6 files changed, 230 insertions(+), 6 deletions(-)

diff --git a/cmake/CMakeLists.txt b/cmake/CMakeLists.txt @@ -58,6 +58,7 @@ set(SSF_FILES_SRC ssf_fresnel_dielectric_conductor.c ssf_fresnel_dielectric_dielectric.c ssf_fresnel_no_op.c + ssf_lambertian_reflection.c ssf_specular_reflection.c) rcmake_prepend_path(SSF_FILES_SRC ${SSF_SOURCE_DIR}) rcmake_prepend_path(SSF_FILES_INC ${SSF_SOURCE_DIR}) @@ -100,6 +101,7 @@ if(NOT NO_TEST) new_test(test_ssf_fresnel_dielectric_conductor) new_test(test_ssf_fresnel_dielectric_dielectric) new_test(test_ssf_fresnel_no_op) + new_test(test_ssf_lambertian_reflection) new_test(test_ssf_specular_reflection) endif() diff --git a/src/ssf.h b/src/ssf.h @@ -96,6 +96,8 @@ BEGIN_DECLS /* Reflects the incoming direction with respect to the surface normal */ SSF_API const struct ssf_bxdf_type ssf_specular_reflection; +/* Reflects the same intensity independent of the incoming direction */ +SSF_API const struct ssf_bxdf_type ssf_lambertian_reflection; /* Glossy reflections with respect to a microfacet distribution */ SSF_API const struct ssf_bxdf_type ssf_microfacet_reflection; @@ -270,6 +272,11 @@ ssf_specular_reflection_setup (struct ssf_bxdf* bxdf, const double reflectivity); +SSF_API res_T +ssf_lambertian_reflection_setup + (struct ssf_bxdf* bxdf, + const double reflectivity); + END_DECLS #endif /* SSF_H */ diff --git a/src/ssf_lambertian_reflection.c b/src/ssf_lambertian_reflection.c @@ -0,0 +1,115 @@ +/* Copyright (C) |Meso|Star> 2016 (contact@meso-star.com) + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. */ + +#include "ssf.h" +#include "ssf_bxdf_c.h" + +#include <rsys/double3.h> +#include <rsys/double33.h> + +struct lambertian_reflection { + double reflectivity; +}; + +/******************************************************************************* + * Private functions + ******************************************************************************/ +static res_T +lambertian_reflection_init(struct mem_allocator* allocator, void* bxdf) +{ + ASSERT(bxdf); + (void)allocator; + ((struct lambertian_reflection*)bxdf)->reflectivity = 0.0; + return RES_OK; +} + +static void +lambertian_reflection_release(void* bxdf) +{ (void)bxdf; } + +static double +lambertian_reflection_eval + (void* data, const double wi[3], const double N[3], const double wo[3]) +{ + struct lambertian_reflection* brdf = data; + double coso; + ASSERT(data && N && wo); + ASSERT(d3_is_normalized(N) && d3_is_normalized(wo)); + (void)wi; + coso = d3_dot(wo, N); + return coso <= 0.0 ? 0.0 : brdf->reflectivity / PI; +} + +static double +lambertian_reflection_sample + (void* data, + const double u, + const double v, + const double wi[3], + const double N[3], + double wo[4]) +{ + double basis[9]; + double dir[3]; + double cos_theta, sin_theta; + double phi; + ASSERT(data && u>=0 && u<1 && v>=0 && v<1 && wi && N && wo); + ASSERT(d3_is_normalized(wi) && d3_is_normalized(N)); + + phi = 2.0 * PI * u; + cos_theta = sqrt(v); + sin_theta = sqrt(1.0 - v); + dir[0] = cos(phi) * sin_theta; + dir[1] = sin(phi) * sin_theta; + dir[2] = cos_theta; + d33_muld3(wo, d33_basis(basis, N), dir); + wo[3] = cos_theta / PI; + return lambertian_reflection_eval(data, wi, N, wo); +} + +static double +lambertian_reflection_pdf + (void* data, const double wi[3], const double N[3], const double wo[3]) +{ + double coso; + ASSERT(data && wi && N && wo); + ASSERT(d3_is_normalized(N) && d3_is_normalized(wo)); + (void)data, (void)wi; + coso = d3_dot(wo, N); + return coso <= 0.0 ? 0.0 : coso / PI; +} + +/******************************************************************************* + * Exorted functions + ******************************************************************************/ +const struct ssf_bxdf_type ssf_lambertian_reflection = { + lambertian_reflection_init, + lambertian_reflection_release, + lambertian_reflection_sample, + lambertian_reflection_eval, + lambertian_reflection_pdf, + sizeof(struct lambertian_reflection), + ALIGNOF(struct lambertian_reflection) +}; + +res_T +ssf_lambertian_reflection_setup(struct ssf_bxdf* bxdf, const double reflectivity) +{ + if(!bxdf || reflectivity < 0 || reflectivity > 1) return RES_BAD_ARG; + if(!BXDF_TYPE_EQ(&bxdf->type, &ssf_lambertian_reflection)) return RES_BAD_ARG; + ((struct lambertian_reflection*)bxdf->data)->reflectivity = reflectivity; + return RES_OK; +} + diff --git a/src/test_ssf_bsdf_view.c b/src/test_ssf_bsdf_view.c @@ -18,12 +18,6 @@ #include <rsys/double3.h> -static INLINE double -rand_canonic(void) -{ - return (double)rand()/(double)RAND_MAX; -} - int main(int argc, char** argv) { diff --git a/src/test_ssf_lambertian_reflection.c b/src/test_ssf_lambertian_reflection.c @@ -0,0 +1,100 @@ +/* Copyright (C) |Meso|Star> 2016 (contact@meso-star.com) + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. */ + +#include "ssf.h" +#include "test_ssf_utils.h" + +#include <rsys/double3.h> + +int +main(int argc, char** argv) +{ + const size_t NSTEPS = 100000; + struct mem_allocator allocator; + struct ssf_bxdf* brdf; + struct ssf_bxdf* dummy; + double E, SE, V; + double sum; + double sum_sqr; + double wi[3]; + double N[3]; + double wo[4]; + size_t i; + (void)argc, (void)argv; + + mem_init_proxy_allocator(&allocator, &mem_default_allocator); + + CHECK(ssf_bxdf_create(&allocator, &ssf_lambertian_reflection, &brdf), RES_OK); + CHECK(ssf_bxdf_create(&allocator, &bxdf_dummy, &dummy), RES_OK); + + CHECK(ssf_lambertian_reflection_setup(NULL, -1.0), RES_BAD_ARG); + CHECK(ssf_lambertian_reflection_setup(brdf, -1.0), RES_BAD_ARG); + CHECK(ssf_lambertian_reflection_setup(NULL, 1.0), RES_BAD_ARG); + CHECK(ssf_lambertian_reflection_setup(brdf, 1.0), RES_OK); + CHECK(ssf_lambertian_reflection_setup(brdf, 0.0), RES_OK); + CHECK(ssf_lambertian_reflection_setup(brdf, 1.1), RES_BAD_ARG); + CHECK(ssf_lambertian_reflection_setup(dummy, 0.0), RES_BAD_ARG); + + d3(N, 0.0, 0.0, 1.0); + d3_normalize(wi, d3(wi, -1.0, 0.0, -1.0)); + CHECK(ssf_bxdf_sample(brdf, 0, 0, wi, N, wo), 0.0); + CHECK(eq_eps(wo[2]/PI, wo[3], 1.e-6), 1); + + CHECK(ssf_lambertian_reflection_setup(brdf, 0.7), RES_OK); + CHECK(ssf_bxdf_sample(brdf, 0.5, 0.5, wi, N, wo), 0.7/PI); + + sum = sum_sqr = 0; + FOR_EACH(i, 0, NSTEPS) { + const double u = rand_canonic(); + const double v = rand_canonic(); + const double R = ssf_bxdf_sample(brdf, u, v, wi, N, wo); + CHECK(eq_eps(R, 0.7/PI, 1.e-6), 1); + CHECK(eq_eps(d3_dot(wo, N), wo[2], 1.e-6), 1); + sum += wo[2]; + sum_sqr += wo[2]*wo[2]; + } + E = sum/(double)NSTEPS; + V = sum_sqr/(double)NSTEPS - E*E; + SE = sqrt(V/(double)NSTEPS); + CHECK(eq_eps(E, 2.0/3.0, SE), 1); + + sum = sum_sqr = 0; + FOR_EACH(i, 0, NSTEPS) { + const double u = rand_canonic(); + const double v = rand_canonic(); + double R = ssf_bxdf_sample(brdf, u, v, wi, N, wo); + double w; + CHECK(eq_eps(R, 0.7/PI, 1.e-6), 1); + CHECK(eq_eps(d3_dot(wo, N), wo[2], 1.e-6), 1); + w = wo[2]*wo[2]; + sum += w; + sum_sqr += w*w; + } + + E = sum/(double)NSTEPS; + V = sum_sqr/(double)NSTEPS - E*E; + SE = sqrt(V/(double)NSTEPS); + CHECK(eq_eps(E, 2.0/4.0, 2.0*SE), 1); + CHECK(eq_eps(SE, 1.0/sqrt((double)NSTEPS) * sqrt(1.0/3.0 - 1.0/4.0), 1.e-6), 1); + + CHECK(ssf_bxdf_ref_put(brdf), RES_OK); + CHECK(ssf_bxdf_ref_put(dummy), RES_OK); + + check_memory_allocator(&allocator); + mem_shutdown_proxy_allocator(&allocator); + CHECK(mem_allocated_size(), 0); + return 0; +} + diff --git a/src/test_ssf_utils.h b/src/test_ssf_utils.h @@ -114,6 +114,12 @@ static const struct ssf_fresnel_type fresnel_dummy = { /******************************************************************************* * Miscellaneous functions ******************************************************************************/ +static INLINE double +rand_canonic(void) +{ + return (double)rand()/(double)RAND_MAX; +} + static INLINE void check_memory_allocator(struct mem_allocator* allocator) {