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:
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;
+}