star-mc

Parallel estimation of Monte Carlo integrators
git clone git://git.meso-star.fr/star-mc.git
Log | Files | Refs | README | LICENSE

commit ac0e6a010b1acb8685d0a7b7fd56f2b25311d7d8
parent 06a979444b015abc862abb8c6b0955b0f13c293c
Author: Vincent Forest <vincent.forest@meso-star.com>
Date:   Fri,  3 Apr 2015 09:28:20 +0200

Begin the implementation of light path integration test

Not functional yet

Diffstat:
Mcmake/CMakeLists.txt | 16++++++++++++++--
Asrc/test_smc_light_path.c | 371+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
2 files changed, 385 insertions(+), 2 deletions(-)

diff --git a/cmake/CMakeLists.txt b/cmake/CMakeLists.txt @@ -42,6 +42,7 @@ option(NO_TEST "Disable the test" OFF) find_package(RCMake 0.1 REQUIRED) find_package(RSys 0.1.1 REQUIRED) find_package(StarSP REQUIRED) +find_package(Star3D) set(CMAKE_MODULE_PATH ${CMAKE_MODULE_PATH} ${RCMAKE_SOURCE_DIR}) include(rcmake) @@ -99,8 +100,19 @@ if(NOT NO_TEST) add_test(${_name} ${_name}) endfunction(new_test) - new_test(test_smc_device) - new_test(test_smc_solve m) + new_test(test_smc_device) + new_test(test_smc_solve m) + + if(NOT TARGET Star3D) + message(STATUS + "The Star-3D library is not found: cannot generate the light path test") + else() + new_test(test_smc_light_path) + target_link_libraries(test_smc_light_path Star3D) + set_target_properties(test_smc_light_path PROPERTIES + INCLUDE_DIRECTORIES ${Star3D_INCLUDE_DIR}) + endif() + endif() ################################################################################ diff --git a/src/test_smc_light_path.c b/src/test_smc_light_path.c @@ -0,0 +1,371 @@ +/* Copyright (C) |Meso|Star> 2015 (contact@meso-star.com) + * + * This software is a computer program whose purpose is to manage the + * statistical estimation of a function. + * + * This software is governed by the CeCILL license under French law and + * abiding by the rules of distribution of free software. You can use, + * modify and/or redistribute the software under the terms of the CeCILL + * license as circulated by CEA, CNRS and INRIA at the following URL + * "http://www.cecill.info". + * + * As a counterpart to the access to the source code and rights to copy, + * modify and redistribute granted by the license, users are provided only + * with a limited warranty and the software's author, the holder of the + * economic rights, and the successive licensors have only limited + * liability. + * + * In this respect, the user's attention is drawn to the risks associated + * with loading, using, modifying and/or developing or reproducing the + * software by the user in light of its specific status of free software, + * that may mean that it is complicated to manipulate, and that also + * therefore means that it is reserved for developers and experienced + * professionals having in-depth computer knowledge. Users are therefore + * encouraged to load and test the software's suitability as regards their + * requirements in conditions enabling the security of their systems and/or + * data to be ensured and, more generally, to use and operate it in the + * same conditions as regards security. + * + * The fact that you are presently reading this means that you have had + * knowledge of the CeCILL license and that you accept its terms. */ + +#include "smc.h" +#include "test_smc_utils.h" + +#include <star/s3d.h> +#include <star/ssp.h> + +#include <rsys/float2.h> +#include <rsys/float3.h> +#include <rsys/image.h> +#include <rsys/math.h> +#include <rsys/mem_allocator.h> + +#define IMG_WIDTH 640 +#define IMG_HEIGHT 480 +#define LIGHT_PATH_DEPTH 4 +#define SKY_RADIANCE 1.f +#define ALBEDO 1.f + +struct cbox_desc{ + const float* vertices; + const unsigned* indices; +}; + +/******************************************************************************* + * Box walls data + ******************************************************************************/ +static const float cbox_walls[] = { + 552.f, 0.f, 0.f, + 0.f, 0.f, 0.f, + 0.f, 559.f, 0.f, + 552.f, 559.f, 0.f, + 552.f, 0.f, 548.f, + 0.f, 0.f, 548.f, + 0.f, 559.f, 548.f, + 552.f, 559.f, 548.f +}; + +const unsigned cbox_walls_ids[] = { + 0, 1, 2, 2, 3, 0, /* Bottom */ + 4, 5, 6, 6, 7, 4, /* Top */ + 1, 2, 6, 6, 5, 1, /* Left */ + 0, 3, 7, 7, 4, 0, /* Right */ + 2, 3, 7, 7, 6, 2 /* Back */ +}; + +static const unsigned cbox_walls_ntris = sizeof(cbox_walls_ids)/sizeof(unsigned[3]); +static const unsigned cbox_walls_nverts = sizeof(cbox_walls)/sizeof(float[3]); +static struct cbox_desc cbox_walls_desc = { cbox_walls, cbox_walls_ids }; + +/******************************************************************************* + * Short/tall blocks data + ******************************************************************************/ +static const float cbox_short_block[] = { + 130.f, 65.f, 0.f, + 82.f, 225.f, 0.f, + 240.f, 272.f, 0.f, + 290.f, 114.f, 0.f, + 130.f, 65.f, 165.f, + 82.f, 225.f, 165.f, + 240.f, 272.f, 165.f, + 290.f, 114.f, 165.f +}; + +static const float cbox_tall_block[] = { + 423.0f, 247.0f, 0.f, + 265.0f, 296.0f, 0.f, + 314.0f, 456.0f, 0.f, + 472.0f, 406.0f, 0.f, + 423.0f, 247.0f, 330.f, + 265.0f, 296.0f, 330.f, + 314.0f, 456.0f, 330.f, + 472.0f, 406.0f, 330.f +}; + +static const unsigned cbox_block_ids[] = { + 4, 5, 6, 6, 7, 4, + 1, 2, 6, 6, 5, 1, + 0, 3, 7, 7, 4, 0, + 2, 3, 7, 7, 6, 2, + 0, 1, 5, 5, 4, 0 +}; + +static const unsigned cbox_block_ntris = sizeof(cbox_block_ids)/sizeof(unsigned[3]); + +static const unsigned cbox_short_block_nverts = + sizeof(cbox_short_block)/sizeof(float[3]); +static struct cbox_desc cbox_short_block_desc = + { cbox_short_block, cbox_block_ids }; + +static const unsigned cbox_tall_block_nverts = + sizeof(cbox_tall_block)/sizeof(float[3]); +static struct cbox_desc cbox_tall_block_desc = + { cbox_tall_block, cbox_block_ids }; + +/******************************************************************************* + * Cornell box callbacks + ******************************************************************************/ +static INLINE void +cbox_get_ids(const unsigned itri, unsigned ids[3], void* data) +{ + const unsigned id = itri * 3; + struct cbox_desc* desc = data; + NCHECK(desc, NULL); + ids[0] = desc->indices[id + 0]; + ids[1] = desc->indices[id + 1]; + ids[2] = desc->indices[id + 2]; +} + +static INLINE void +cbox_get_position(const unsigned ivert, float position[3], void* data) +{ + struct cbox_desc* desc = data; + NCHECK(desc, NULL); + position[0] = desc->vertices[ivert*3 + 0]; + position[1] = desc->vertices[ivert*3 + 1]; + position[2] = desc->vertices[ivert*3 + 2]; +} + +/******************************************************************************* + * Camera + ******************************************************************************/ +struct camera { + float pos[3]; + float x[3], y[3], z[3]; /* Basis */ +}; + +static void +camera_init(struct camera* cam) +{ + const float pos[3] = { 178.f, -1000.f, 273.f }; + const float tgt[3] = { 178.f, 0.f, 273.f }; + const float up[3] = { 0.f, 0.f, 1.f }; + const float proj_ratio = (float)IMG_WIDTH/(float)IMG_HEIGHT; + const float fov_x = (float)PI * 0.25f; + float f = 0.f; + ASSERT(cam); + + f3_set(cam->pos, pos); + f = f3_normalize(cam->z, f3_sub(cam->z, tgt, pos)); NCHECK(f, 0); + f = f3_normalize(cam->x, f3_cross(cam->x, cam->z, up)); NCHECK(f, 0); + f = f3_normalize(cam->y, f3_cross(cam->y, cam->z, cam->x)); NCHECK(f, 0); + f3_divf(cam->z, cam->z, (float)tan(fov_x*0.5f)); + f3_divf(cam->y, cam->y, proj_ratio); +} + +static void +camera_ray + (const struct camera* cam, + const float pixel[2], + float org[3], + float dir[3]) +{ + float x[3], y[3], f; + ASSERT(cam && pixel && org && dir); + + f3_mulf(x, cam->x, pixel[0]*2.f - 1.f); + f3_mulf(y, cam->y, pixel[1]*2.f - 1.f); + f3_add(dir, f3_add(dir, x, y), cam->z); + f = f3_normalize(dir, dir); NCHECK(f, 0); + f3_set(org, cam->pos); +} + +/******************************************************************************* + * Integrator + ******************************************************************************/ +struct integrator_context { + struct s3d_scene* scn; + struct camera cam; + float pixel_size[2]; /* Normalized pixel size */ + size_t ipixel[2]; /* Image space pixel coordinates */ +}; + +static void +light_path_integrator(void* value, struct ssp_rng* rng, void* data) +{ + struct integrator_context* ctx = data; + float pix_lower[2], pix_upper[2]; /* Pixel AABB */ + float pix_samp[2]; /* Pixel sample */ + float ray_org[3], ray_dir[3], ray_range[2]; + float L = 0.f; + float throughput = 1.f; + int idepth; + + NCHECK(value, NULL); + NCHECK(rng, NULL); + NCHECK(data, NULL); + + /* Compute the pixel bound */ + pix_lower[0] = (float)ctx->ipixel[0] / (float)IMG_WIDTH; + pix_lower[1] = (float)ctx->ipixel[1] / (float)IMG_HEIGHT; + f2_add(pix_upper, pix_lower, ctx->pixel_size); + + /* Randomly sample the pixel quad */ + pix_samp[0] = (float)ssp_rng_uniform_double(rng, pix_lower[0], pix_upper[0]); + pix_samp[1] = (float)ssp_rng_uniform_double(rng, pix_lower[1], pix_upper[1]); + + /* Build a camera ray */ + camera_ray(&ctx->cam, pix_samp, ray_org, ray_dir); + ray_range[0] = 0.f; + ray_range[1] = FLT_MAX; + + FOR_EACH(idepth, 0, LIGHT_PATH_DEPTH) { + struct s3d_hit hit = S3D_HIT_NULL; + float cos_theta; + float offset = 1.e-6f; + float pdf; + float wi[3]; + float N[3]; + + CHECK(s3d_scene_trace_ray + (ctx->scn, ray_org, ray_dir, ray_range, &hit), RES_OK); + + if(!S3D_HIT_NONE(&hit)) { /* Skydome lighting */ + L += throughput * SKY_RADIANCE; + break; + } + + /* TODO sample the surface hemisphere at the hit position */ + + /* New ray */ + f3_normalize(N, hit.normal); + cos_theta = f3_dot(N, wi); + if(cos_theta < 0.f) f3_dot(f3_minus(N, N), wi); + f3_mulf(ray_org, ray_dir, hit.distance); + f3_set(ray_dir, wi); + ray_range[0] = offset; + throughput *= ALBEDO / pdf * cos_theta; + } + *(float*)value = L; +} + +/******************************************************************************* + * Application + ******************************************************************************/ +int +main(int argc, char** argv) +{ + struct mem_allocator allocator; + struct integrator_context ctx; + struct s3d_device* dev; + struct s3d_scene* scn; + struct s3d_shape* shape; + struct s3d_vertex_data attribs[2]; + struct smc_device* smc; + unsigned char* img; + size_t ix, iy; + float pos[3]; + (void)argc, (void)argv; + + mem_init_proxy_allocator(&allocator, &mem_default_allocator); + + if(argc > 1) { + img = MEM_ALLOC(&allocator, 3 * IMG_WIDTH * IMG_HEIGHT); + NCHECK(img, NULL); + } + + CHECK(s3d_device_create(NULL, &allocator, &dev), RES_OK); + CHECK(s3d_scene_create(dev, &scn), RES_OK); + + attribs[0].usage = S3D_POSITION; + attribs[0].type = S3D_FLOAT3; + attribs[0].get = cbox_get_position; + attribs[1] = S3D_VERTEX_DATA_NULL; + + CHECK(s3d_shape_create_mesh(dev, &shape), RES_OK); + CHECK(s3d_mesh_setup_indexed_vertices(shape, cbox_walls_ntris, cbox_get_ids, + cbox_walls_nverts, attribs, &cbox_walls_desc), RES_OK); + CHECK(s3d_scene_attach_shape(scn, shape), RES_OK); + CHECK(s3d_shape_ref_put(shape), RES_OK); + + CHECK(s3d_shape_create_mesh(dev, &shape), RES_OK); + CHECK(s3d_mesh_setup_indexed_vertices(shape, cbox_block_ntris, cbox_get_ids, + cbox_short_block_nverts, attribs, &cbox_short_block_desc), RES_OK); + CHECK(s3d_scene_attach_shape(scn, shape), RES_OK); + CHECK(s3d_shape_ref_put(shape), RES_OK); + + CHECK(s3d_shape_create_mesh(dev, &shape), RES_OK); + CHECK(s3d_mesh_setup_indexed_vertices(shape, cbox_block_ntris, cbox_get_ids, + cbox_tall_block_nverts, attribs, &cbox_tall_block_desc), RES_OK); + CHECK(s3d_scene_attach_shape(scn, shape), RES_OK); + CHECK(s3d_shape_ref_put(shape), RES_OK); + + CHECK(s3d_scene_instantiate(scn, &shape), RES_OK); + CHECK(s3d_scene_ref_put(scn), RES_OK); + CHECK(s3d_instance_set_position(shape, f3(pos, -100.f, 0.f, -2.f)), RES_OK); + + CHECK(s3d_scene_create(dev, &scn), RES_OK); + CHECK(s3d_scene_attach_shape(scn, shape), RES_OK); + CHECK(s3d_shape_ref_put(shape), RES_OK); + + CHECK(s3d_scene_begin_trace(scn), RES_OK); + + CHECK(smc_device_create(NULL, &allocator, &smc), RES_OK); + + camera_init(&ctx.cam); + ctx.scn = scn; + ctx.pixel_size[0] = 1.f / (float)IMG_WIDTH; + ctx.pixel_size[1] = 1.f / (float)IMG_HEIGHT; + + FOR_EACH(iy, 0, IMG_HEIGHT) { + ctx.ipixel[1] = iy; + FOR_EACH(ix, 0, IMG_WIDTH) { + struct smc_estimator* estimator; + struct smc_estimator_status status; + ctx.ipixel[0] = ix; + CHECK(smc_solve + (smc, light_path_integrator, &smc_float, &ctx, &estimator), RES_OK); + + if(img) { + const size_t ipix = (iy*IMG_WIDTH + ix) * 3/*RGB*/; + float col; + unsigned char colu; + + CHECK(smc_estimator_get_status(estimator, &status), RES_OK); + col = CLAMP(SMC_FLOAT(status.E), 0.f, 1.f); + colu = (unsigned char)(col * 255.f); + img[ipix + 0] = colu; + img[ipix + 1] = colu; + img[ipix + 2] = colu; + } + CHECK(smc_estimator_ref_put(estimator), RES_OK); + } + } + + if(argc > 1) { + CHECK(image_ppm_write(argv[1], IMG_WIDTH, IMG_HEIGHT, 3, img), RES_OK); + MEM_FREE(&allocator, img); + } + + CHECK(s3d_scene_end_trace(scn), RES_OK); + + CHECK(s3d_device_ref_put(dev), RES_OK); + CHECK(s3d_scene_ref_put(scn), RES_OK); + CHECK(smc_device_ref_put(smc), RES_OK); + + check_memory_allocator(&allocator); + mem_shutdown_proxy_allocator(&allocator); + CHECK(mem_allocated_size(), 0); + return 0; +}