test_scam_perspective_thin_lens.c (5875B)
1 /* Copyright (C) 2021-2023 |Méso|Star> (contact@meso-star.com) 2 * 3 * This program is free software: you can redistribute it and/or modify 4 * it under the terms of the GNU General Public License as published by 5 * the Free Software Foundation, either version 3 of the License, or 6 * (at your option) any later version. 7 * 8 * This program is distributed in the hope that it will be useful, 9 * but WITHOUT ANY WARRANTY; without even the implied warranty of 10 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 11 * GNU General Public License for more details. 12 * 13 * You should have received a copy of the GNU General Public License 14 * along with this program. If not, see <http://www.gnu.org/licenses/>. */ 15 16 #define _POSIX_C_SOURCE 200112L /* nextafterf */ 17 18 #include "scam.h" 19 #include "test_scam_utils.h" 20 21 #include <rsys/double2.h> 22 #include <rsys/double3.h> 23 #include <rsys/double33.h> 24 #include <rsys/math.h> 25 #include <rsys/mem_allocator.h> 26 27 int 28 main(int argc, char** argv) 29 { 30 struct scam* cam = NULL; 31 struct scam_perspective_args args = SCAM_PERSPECTIVE_ARGS_DEFAULT; 32 struct scam_sample sample = SCAM_SAMPLE_NULL; 33 struct scam_ray ray = SCAM_RAY_NULL; 34 const size_t nsamps = 10000; 35 size_t i; 36 double transform[9]; 37 double axis_x[3]; 38 double axis_y[3]; 39 double axis_z[3]; 40 double hori_hfov; /* horizontal half field of view */ 41 double sin_hori_hfov; /* Sinus of the horizontal half field of view */ 42 double sin_vert_hfov; /* Sinos of the vertical half field of view */ 43 double depth; 44 double fov; 45 double length; 46 double radius; 47 double ref; 48 (void)argc, (void)argv; 49 50 args.lens_radius = -1; 51 CHK(scam_create_perspective(NULL, NULL, 1, &args, &cam) == RES_BAD_ARG); 52 args.lens_radius = 1; 53 args.focal_distance = -1; 54 CHK(scam_create_perspective(NULL, NULL, 1, &args, &cam) == RES_BAD_ARG); 55 args.focal_distance = 1; 56 CHK(scam_create_perspective(NULL, NULL, 1, &args, &cam) == RES_OK); 57 CHK(scam_ref_put(cam) == RES_OK); 58 59 length = 1.2; 60 radius = 0.01; 61 CHK(scam_focal_length_to_field_of_view(-1, length, &fov) == RES_BAD_ARG); 62 CHK(scam_focal_length_to_field_of_view(radius, 0, &fov) == RES_BAD_ARG); 63 CHK(scam_focal_length_to_field_of_view(radius, length, &fov) == RES_OK); 64 65 ref = 2*atan(radius/length); 66 CHK(eq_eps(fov, ref, ref*1.e-6)); 67 68 ref = length; 69 CHK(scam_field_of_view_to_focal_length(-1, fov, &length) == RES_BAD_ARG); 70 CHK(scam_field_of_view_to_focal_length(radius, 0, &length) == RES_BAD_ARG); 71 CHK(scam_field_of_view_to_focal_length(radius, PI, &length) == RES_BAD_ARG); 72 CHK(scam_field_of_view_to_focal_length(radius, fov, &length) == RES_OK); 73 74 CHK(eq_eps(length, ref, ref*1.e-6)); 75 76 args.position[0] = rand_canonical(); 77 args.position[1] = rand_canonical(); 78 args.position[2] = rand_canonical(); 79 args.target[0] = rand_canonical(); 80 args.target[1] = rand_canonical(); 81 args.target[2] = rand_canonical(); 82 args.up[0] = rand_canonical(); 83 args.up[1] = rand_canonical(); 84 args.up[2] = rand_canonical(); 85 args.field_of_view = PI/2.0; 86 args.aspect_ratio = 4.0/3.0; 87 args.lens_radius = 0.01; 88 args.focal_distance = 12.34; 89 90 CHK(scam_create_perspective(NULL, NULL, 1, &args, &cam) == RES_OK); 91 92 /* Precompute some view frustum constants */ 93 d3_normalize(axis_z, d3_sub(axis_z, args.target, args.position)); 94 d3_normalize(axis_x, d3_cross(axis_x, axis_z, args.up)); 95 d3_normalize(axis_y, d3_cross(axis_y, axis_z, axis_x)); 96 depth = 1.0/tan(args.field_of_view*0.5); 97 hori_hfov = atan(args.aspect_ratio / depth); 98 sin_hori_hfov = sin(hori_hfov); 99 sin_vert_hfov = sin(args.field_of_view*0.5); 100 101 /* Compute the world to camera space transformation */ 102 d3_set(transform + 0, axis_x); 103 d3_set(transform + 3, axis_y); 104 d3_set(transform + 6, axis_z); 105 d33_transpose(transform, transform); 106 107 sample.lens[0] = nextafterf(0, -1); 108 CHK(scam_generate_ray(cam, &sample, &ray) == RES_BAD_ARG); 109 sample.lens[0] = 1; 110 CHK(scam_generate_ray(cam, &sample, &ray) == RES_BAD_ARG); 111 sample.lens[0] = 0; 112 sample.lens[1] = nextafterf(0, -1); 113 CHK(scam_generate_ray(cam, &sample, &ray) == RES_BAD_ARG); 114 sample.lens[1] = 1; 115 CHK(scam_generate_ray(cam, &sample, &ray) == RES_BAD_ARG); 116 sample.film[0] = sample.lens[0] = 0; 117 sample.film[1] = sample.lens[1] = nextafterf(1, 0); 118 CHK(scam_generate_ray(cam, &sample, &ray) == RES_OK); 119 120 FOR_EACH(i, 0, nsamps) { 121 double cos_ray_axis_x; 122 double cos_ray_axis_y; 123 double cos_ray_axis_z; 124 double dir[3]; 125 double pos[3]; 126 double dst; 127 128 sample.film[0] = rand_canonical(); 129 sample.film[1] = rand_canonical(); 130 sample.lens[0] = rand_canonical(); 131 sample.lens[1] = rand_canonical(); 132 CHK(scam_generate_ray(cam, &sample, &ray) == RES_OK); 133 134 /* Transform the position in camera space */ 135 d3_sub(pos, ray.org, args.position); 136 d33_muld3(pos, transform, pos); 137 138 /* Check that the sampled position lies onto the thin lens */ 139 dst = d2_len(pos); 140 CHK(eq_eps(pos[2], 0, 1.e-6)); 141 CHK(dst < args.lens_radius 142 || eq_eps(dst, args.lens_radius, args.lens_radius*1.e-6)); 143 144 /* Compute the ray starting from the lens center and intersecting the focal 145 * plane at the same position of the generated ray */ 146 pos[0] = ray.org[0] + ray.dir[0]*args.focal_distance; 147 pos[1] = ray.org[1] + ray.dir[1]*args.focal_distance; 148 pos[2] = ray.org[2] + ray.dir[2]*args.focal_distance; 149 CHK(d3_is_normalized(ray.dir)); 150 d3_add(pos, ray.org, ray.dir); 151 d3_sub(dir, pos, args.position); 152 d3_normalize(dir, dir); 153 154 /* Check that the computed direction is in the view frustum */ 155 cos_ray_axis_x = d3_dot(dir, axis_x); 156 cos_ray_axis_y = d3_dot(dir, axis_y); 157 cos_ray_axis_z = d3_dot(dir, axis_z); 158 CHK(cos_ray_axis_z >= 0); 159 CHK(cos_ray_axis_y <= sin_vert_hfov); 160 CHK(cos_ray_axis_x <= sin_hori_hfov); 161 } 162 163 CHK(scam_ref_put(cam) == RES_OK); 164 CHK(mem_allocated_size() == 0); 165 return 0; 166 }