star-camera

Camera models
git clone git://git.meso-star.fr/star-camera.git
Log | Files | Refs | README | LICENSE

commit 11aad71d1da267c450f985bc8d69f0ef89340cb7
parent 57605ea5785e9e384689d9519b280a5af8a7a0e2
Author: Vincent Forest <vincent.forest@meso-star.com>
Date:   Wed, 28 Jul 2021 16:58:36 +0200

Implement the orthographic camera model

Diffstat:
Mcmake/CMakeLists.txt | 1+
Msrc/scam.c | 3+++
Msrc/scam.h | 38+++++++++++++++++++++++++++++++++++---
Msrc/scam_c.h | 29+++++++++++++++++++++++++++++
Asrc/scam_orthographic.c | 230+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Msrc/scam_perspective.c | 12++++--------
6 files changed, 302 insertions(+), 11 deletions(-)

diff --git a/cmake/CMakeLists.txt b/cmake/CMakeLists.txt @@ -44,6 +44,7 @@ set(VERSION ${VERSION_MAJOR}.${VERSION_MINOR}.${VERSION_PATCH}) set(SCAM_FILES_SRC scam.c scam_log.c + scam_orthographic.c scam_perspective.c) set(SCAM_FILES_INC scam_c.h diff --git a/src/scam.c b/src/scam.c @@ -74,6 +74,9 @@ scam_generate_ray } switch(cam->type) { + case SCAM_ORTHOGRAPHIC: + orthographic_generate_ray(cam, sample, ray); + break; case SCAM_PERSPECTIVE: perspective_generate_ray(cam, sample, ray); break; diff --git a/src/scam.h b/src/scam.h @@ -37,6 +37,7 @@ #endif enum scam_type { + SCAM_ORTHOGRAPHIC, SCAM_PERSPECTIVE, SCAM_TYPES_COUNT__, SCAM_NONE = SCAM_TYPES_COUNT__ @@ -57,10 +58,33 @@ struct scam_ray { #define SCAM_RAY_NULL__ {{0,0,0},{0,0,0}} static const struct scam_ray SCAM_RAY_NULL = SCAM_RAY_NULL__; +struct scam_orthographic_args { + double position[3]; /* Lens position */ + double target[3]; /* Targeted point. target-position = image plane normal */ + double up[3]; /* Vector defining the upward orientation */ + double height; /* Height of the image plane */ + double aspect_ratio; /* Image plane aspect ratio (width / height) */ + double lens_radius; /* Radius of 0 <=> pinhole */ + + /* Distance to focus on. Used when lens_radius != 0 */ + double focal_distance; +}; +#define SCAM_ORTHOGRAPHIC_ARGS_DEFAULT__ { \ + {0,0,0}, /* Position */ \ + {0,1,0}, /* Target */ \ + {0,0,1}, /* Up */ \ + 1.0, /* Height of the image plane */ \ + 1.0, /* Aspect ratio */ \ + 0.0, /* Lens radius */ \ + 1.0 /* Focal distance. Unused for radius == 0 */ \ +} +static const struct scam_orthographic_args SCAM_ORTHOGRAPHIC_ARGS_DEFAULT = + SCAM_ORTHOGRAPHIC_ARGS_DEFAULT__; + struct scam_perspective_args { double position[3]; /* Lens position */ double target[3]; /* Targeted point. target-position = image plane normal */ - double up[3]; /* Vector defining the upward orientation */ + double up[3]; /* Vector defining the upward orientation */ double aspect_ratio; /* Image plane aspect ratio (width / height) */ double lens_radius; /* Radius of 0 <=> pinhole */ @@ -79,9 +103,9 @@ struct scam_perspective_args { 1.0, /* Aspect ratio */ \ 0.0, /* Lens radius */ \ 1.22173047639603070383, /* Fov ~70 degrees */ \ - 1.0, /* Focal distance. Unused for radius == 0 */ \ + 1.0 /* Focal distance. Unused for radius == 0 */ \ } -static const struct scam_perspective_args SCAM_PERSPECTIVE_ARGS_DEFAULT = +static const struct scam_perspective_args SCAM_PERSPECTIVE_ARGS_DEFAULT = SCAM_PERSPECTIVE_ARGS_DEFAULT__; /* Forward declaration of external data types */ @@ -105,6 +129,14 @@ scam_create_perspective struct scam** camera); SCAM_API res_T +scam_create_orthographic + (struct logger* logger, /* NULL <=> use builtin logger */ + struct mem_allocator* allocator, /* NULL <=> use default allocator */ + const int verbose, /* Verbosity level */ + struct scam_orthographic_args* args, + struct scam** camera); + +SCAM_API res_T scam_generate_ray (const struct scam* cam, const struct scam_sample* sample, diff --git a/src/scam_c.h b/src/scam_c.h @@ -43,10 +43,33 @@ struct perspective { } static const struct perspective PERSPECTIVE_DEFAULT = PERSPECTIVE_DEFAULT__; +struct orthographic { + double screen2world[9]; + double camera2world[9]; + + double position[3]; /* Lens position */ + + double height; /* Height of the image plane */ + double aspect_ratio; /* width / height */ + double lens_radius; /* 0 <=> pinhole camera */ + double focal_distance; /* Unused when lens_radius == 0 */ +}; +#define ORTHOGRAPHIC_DEFAULT__ { \ + {1,0,0, 0,1,0, 0,0,1}, /* Screen to world transformation */ \ + {1,0,0, 0,1,0, 0,0,1}, /* Camera to world transformation */ \ + {0,0,0}, /* Lens position */ \ + 1.0, /* Height */ \ + 1.0, /* Aspect ratio */ \ + 0.0, /* Lens radius */ \ + -1.0 /* Focal distance */ \ +} +static const struct orthographic ORTHOGRAPHIC_DEFAULT = ORTHOGRAPHIC_DEFAULT__; + struct scam { enum scam_type type; union { struct perspective persp; + struct orthographic ortho; } param; int verbose; @@ -66,6 +89,12 @@ camera_create struct scam** scam); extern LOCAL_SYM void +orthographic_generate_ray + (const struct scam* cam, + const struct scam_sample* sample, + struct scam_ray* ray); + +extern LOCAL_SYM void perspective_generate_ray (const struct scam* cam, const struct scam_sample* sample, diff --git a/src/scam_orthographic.c b/src/scam_orthographic.c @@ -0,0 +1,230 @@ +/* Copyright (C) 2021 |Meso|Star> (contact@meso-star.com) + * + * Tyhis 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 "scam_c.h" +#include "scam_log.h" + +#include <rsys/double3.h> +#include <rsys/double33.h> +#include <rsys/math.h> + +#include <math.h> + +/******************************************************************************* + * Helper functions + ******************************************************************************/ +static res_T +setup_orthographic(struct scam* cam, const struct scam_orthographic_args* args) +{ + double x[3], y[3], z[3]; + int pinhole = 0; + res_T res = RES_OK; + ASSERT(cam && args && cam->type == SCAM_ORTHOGRAPHIC); + + cam->param.ortho = ORTHOGRAPHIC_DEFAULT; + pinhole = args->lens_radius == 0; + + if(args->aspect_ratio <= 0) { + log_err(cam, + "orthographic camera: invalid aspect ratio: %g\n", + args->aspect_ratio); + res = RES_BAD_ARG; + goto error; + } + + if(args->height <= 0) { + log_err(cam, + "orthographic camera: invalid height: %g\n", + args->height); + res = RES_BAD_ARG; + goto error; + } + + if(args->lens_radius < 0) { + log_err(cam, + "orthographic camera: invalid negative lens radius: %g\n", + args->lens_radius); + res = RES_BAD_ARG; + goto error; + } + + if(!pinhole && args->focal_distance < 0) { + log_err(cam, + "orthographic camera: invalid negative focal distance: %g\n", + args->focal_distance); + res = RES_BAD_ARG; + goto error; + } + + if(d3_normalize(z, d3_sub(z, args->target, args->position)) <= 0 + || d3_normalize(x, d3_cross(x, z, args->up)) <= 0 + || d3_normalize(y, d3_cross(y, z, x)) <= 0) { + log_err(cam, + "orthographic camera: invalid point of view:\n" + " position = %g %g %g\n" + " target = %g %g %g\n" + " up = %g %g %g\n", + SPLIT3(args->position), SPLIT3(args->target), SPLIT3(args->up)); + res = RES_BAD_ARG; + goto error; + } + + cam->param.ortho.height = args->height; + cam->param.ortho.aspect_ratio = args->aspect_ratio; + cam->param.ortho.lens_radius= args->lens_radius; + cam->param.ortho.focal_distance = args->focal_distance; + + d3_set(cam->param.ortho.position, args->position); + + d3_muld(cam->param.ortho.screen2world+0, x, 0.5*args->height*args->aspect_ratio); + d3_muld(cam->param.ortho.screen2world+3, y, 0.5*args->height); + d3_set (cam->param.ortho.screen2world+6, z); + + d3_set(cam->param.ortho.camera2world+0, x); + d3_set(cam->param.ortho.camera2world+3, y); + d3_set(cam->param.ortho.camera2world+6, z); + +exit: + return res; +error: + goto exit; +} + +static INLINE void +pinhole_generate_ray + (const struct scam* cam, + const struct scam_sample* sample, + struct scam_ray* ray) +{ + double x[3], y[3]; + double pos[3]; + + ASSERT(cam && sample && ray); + ASSERT(cam->param.ortho.lens_radius == 0); + ASSERT(cam->type == SCAM_ORTHOGRAPHIC); + ASSERT(0 <= sample->film[0] && sample->film[0] < 1); + ASSERT(0 <= sample->film[1] && sample->film[1] < 1); + + /* Transform the sampled position in screen space */ + pos[0] = sample->film[0]*2-1; + pos[1] = sample->film[1]*2-1; + pos[2] = 0; + + /* Transform the sampled position in world space */ + d3_muld(x, cam->param.ortho.screen2world+0, pos[0]); + d3_muld(y, cam->param.ortho.screen2world+3, pos[1]); + d3_add(ray->org, x, y); + d3_add(ray->org, ray->org, cam->param.ortho.position); + + /* Setup the ray direction to the image plane normal, i.e. the z axis of the + * camera */ + d3_set(ray->dir, cam->param.ortho.camera2world+9); +} + +static INLINE void +thin_lens_generate_ray + (const struct scam* cam, + const struct scam_sample* sample, + struct scam_ray* ray) +{ + double focus_pt[3]; + double theta; + double len; + double r; + (void)len; + + ASSERT(cam && sample && ray); + ASSERT(cam->param.ortho.lens_radius > 0); + ASSERT(cam->type == SCAM_PERSPECTIVE); + ASSERT(0 <= sample->film[0] && sample->film[0] < 1); + ASSERT(0 <= sample->film[1] && sample->film[1] < 1); + ASSERT(0 <= sample->lens[0] && sample->lens[0] < 1); + ASSERT(0 <= sample->lens[1] && sample->lens[1] < 1); + + /* find the focus point by intersecting the image plane normal with the focus + * plane in camera space */ + focus_pt[0] = 0; + focus_pt[1] = 0; + focus_pt[2] = cam->param.ortho.focal_distance; + + /* Uniformly sample a position onto the lens in camera space */ + theta = 2 * PI * sample->lens[0]; + r = cam->param.ortho.lens_radius * sqrt(sample->lens[1]); + ray->org[0] = r * cos(theta); + ray->org[1] = r * sin(theta); + ray->org[2] = 0; + + /* Compute the ray direction in camera space */ + d3_sub(ray->dir, focus_pt, ray->org); + len = d3_normalize(ray->dir, ray->dir); + ASSERT(len >= 1.e-6); + + /* Transform the ray from camera space to world space */ + d33_muld3(ray->dir, cam->param.ortho.camera2world, ray->dir); + d33_muld3(ray->org, cam->param.ortho.camera2world, ray->org); + d3_add(ray->org, cam->param.persp.position, ray->org); +} + +/******************************************************************************* + * Exported function + ******************************************************************************/ +SCAM_API res_T +scam_create_orthographic + (struct logger* logger, /* NULL <=> use builtin logger */ + struct mem_allocator* allocator, /* NULL <=> use default allocator */ + const int verbose, /* Verbosity level */ + struct scam_orthographic_args* args, + struct scam** out_cam) +{ + struct scam* cam = NULL; + res_T res = RES_OK; + + if(!args || !out_cam) { + res = RES_BAD_ARG; + goto error; + } + + res = camera_create(logger, allocator, verbose, SCAM_ORTHOGRAPHIC, &cam); + if(res != RES_OK) goto error; + res = setup_orthographic(cam, args); + if(res != RES_OK) goto error; + +exit: + if(out_cam) *out_cam = cam; + return res; +error: + if(cam) { + SCAM(ref_put(cam)); + cam = NULL; + } + goto exit; +} + +/******************************************************************************* + * Local function + ******************************************************************************/ +void +orthographic_generate_ray + (const struct scam* cam, + const struct scam_sample* sample, + struct scam_ray* ray) +{ + ASSERT(cam && cam->type == SCAM_ORTHOGRAPHIC); + if(cam->param.ortho.lens_radius == 0) { + pinhole_generate_ray(cam, sample, ray); + } else { + thin_lens_generate_ray(cam, sample, ray); + } +} diff --git a/src/scam_perspective.c b/src/scam_perspective.c @@ -23,7 +23,7 @@ #include <math.h> /******************************************************************************* - * Helper function + * Helper functions ******************************************************************************/ static res_T setup_perspective(struct scam* cam, const struct scam_perspective_args* args) @@ -60,7 +60,7 @@ setup_perspective(struct scam* cam, const struct scam_perspective_args* args) goto error; } - if(pinhole && (args->field_of_view <= 0 || args->field_of_view >= PI)) { + if(args->field_of_view <= 0 || args->field_of_view >= PI) { log_err(cam, "perspective camera: invalid vertical field of view: %g\n", args->field_of_view); @@ -198,7 +198,7 @@ thin_lens_generate_ray } /******************************************************************************* - * Exported function + * Exported functions ******************************************************************************/ SCAM_API res_T scam_create_perspective @@ -265,11 +265,7 @@ perspective_generate_ray const struct scam_sample* sample, struct scam_ray* ray) { - ASSERT(cam && sample && ray); - ASSERT(cam->type == SCAM_PERSPECTIVE); - ASSERT(0 <= sample->film[0] && sample->film[0] < 1); - ASSERT(0 <= sample->film[1] && sample->film[1] < 1); - + ASSERT(cam && cam->type == SCAM_PERSPECTIVE); if(cam->param.persp.lens_radius == 0) { pinhole_generate_ray(cam, sample, ray); } else {