dump state
This commit is contained in:
parent
65affe51aa
commit
2c98040c42
2
.gitattributes
vendored
Normal file
2
.gitattributes
vendored
Normal file
|
@ -0,0 +1,2 @@
|
|||
*.png filter=lfs diff=lfs merge=lfs -text
|
||||
*.JPG filter=lfs diff=lfs merge=lfs -text
|
1
.gitignore
vendored
1
.gitignore
vendored
|
@ -1,3 +1,4 @@
|
|||
*.o
|
||||
builddir
|
||||
.cache
|
||||
env
|
||||
|
|
BIN
P1000914.JPG
(Stored with Git LFS)
Normal file
BIN
P1000914.JPG
(Stored with Git LFS)
Normal file
Binary file not shown.
|
@ -25,6 +25,7 @@ void usage(const char *pname)
|
|||
|
||||
int main(int argc, char **argv)
|
||||
{
|
||||
|
||||
if (argc < 2) {
|
||||
fprintf(stderr, "Missing model name!\n");
|
||||
usage(argv[0]);
|
||||
|
@ -109,7 +110,7 @@ int pinhole_fn(int argc, char **argv)
|
|||
return 1;
|
||||
}
|
||||
|
||||
df_pinhole(in_image, focal, in_z, out_z, out_image);
|
||||
df_pinhole((df_pinhole_params){.orig_z = in_z, .new_z = out_z, .focal_length = focal}, in_image, out_image);
|
||||
|
||||
int error_code = 0;
|
||||
res = df_write_image(out_image, out_path);
|
||||
|
@ -126,6 +127,95 @@ int pinhole_fn(int argc, char **argv)
|
|||
|
||||
int thin_lense_fn(int argc, char **argv)
|
||||
{
|
||||
fprintf(stderr, "Not implemented yet");
|
||||
return 1;
|
||||
const char *in_path = NULL;
|
||||
const char *out_path = "out.png";
|
||||
const char *focal_str = NULL;
|
||||
const char *aperture_str = NULL;
|
||||
const char *in_z_str = NULL;
|
||||
const char *out_z_str = NULL;
|
||||
const char *samples_str = "64";
|
||||
|
||||
for (int i = 0; i < argc; ++i) {
|
||||
if ((strcmp(argv[i], "-i") == 0 || strcmp(argv[i], "--input") == 0) && i < argc - 1) {
|
||||
++i;
|
||||
in_path = argv[i];
|
||||
} else if ((strcmp(argv[i], "-o") == 0 || strcmp(argv[i], "--output") == 0) && i < argc - 1) {
|
||||
++i;
|
||||
out_path = argv[i];
|
||||
} else if ((strcmp(argv[i], "-f") == 0 || strcmp(argv[i], "--focal-length") == 0) && i < argc - 1) {
|
||||
++i;
|
||||
focal_str = argv[i];
|
||||
} else if ((strcmp(argv[i], "-iz") == 0 || strcmp(argv[i], "--input-z") == 0) && i < argc - 1) {
|
||||
++i;
|
||||
in_z_str = argv[i];
|
||||
} else if ((strcmp(argv[i], "-oz") == 0 || strcmp(argv[i], "--output-z") == 0) && i < argc - 1) {
|
||||
++i;
|
||||
out_z_str = argv[i];
|
||||
} else if ((strcmp(argv[i], "-a") == 0 || strcmp(argv[i], "--aperture") == 0) && i < argc - 1) {
|
||||
++i;
|
||||
aperture_str = argv[i];
|
||||
} else if ((strcmp(argv[i], "-s") == 0 || strcmp(argv[i], "--samples") == 0) && i < argc - 1) {
|
||||
++i;
|
||||
samples_str = argv[i];
|
||||
} else {
|
||||
in_path = argv[i];
|
||||
}
|
||||
}
|
||||
|
||||
if (!in_path || !focal_str || !in_z_str || !out_z_str) {
|
||||
fprintf(stderr, "Missing model parameters!\n");
|
||||
printf("Pinhole model parameters:\n");
|
||||
printf(" -i | --input <path>\t\tPath to the input image.\n");
|
||||
printf(" -o | --output <path>\t\tPath to the output image (Default: out.png).\n");
|
||||
printf(" -iz | --input-z <number>\t\tDistance to the input image plane.\n");
|
||||
printf(" -oz | --output-z <number>\t\tDistance to the output image plane.\n");
|
||||
printf(" -f | --focal-length <number>\t\tThe focal length of the camera lens.\n");
|
||||
printf(" -a | --aperture <number>\t\tThe size of the camera lens.\n");
|
||||
printf(" -s | --samples <number>\t\tThe number of samples per pixel (Default: 64).\n");
|
||||
return 1;
|
||||
}
|
||||
|
||||
float focal, in_z, out_z, aperture;
|
||||
int samples;
|
||||
focal = atof(focal_str);
|
||||
in_z = atof(in_z_str);
|
||||
out_z = atof(out_z_str);
|
||||
aperture = atof(aperture_str);
|
||||
samples = atoi(samples_str);
|
||||
|
||||
df_image *in_image, *out_image;
|
||||
|
||||
int w, h;
|
||||
df_result res = df_load_image(in_path, &w, &h, &in_image);
|
||||
if (res != df_result_success) {
|
||||
fprintf(stderr, "Failed to load %s! (%d)\n", in_path, (int)res);
|
||||
return 1;
|
||||
}
|
||||
|
||||
res = df_create_image(w, h, &out_image);
|
||||
if (res != df_result_success) {
|
||||
fprintf(stderr, "Failed to create output image (%d)\n", (int)res);
|
||||
df_release_image(in_image);
|
||||
return 1;
|
||||
}
|
||||
|
||||
df_thin_lense((df_thin_lense_params){.focal_distance = in_z,
|
||||
.out_distance = out_z,
|
||||
.focal_length = focal,
|
||||
.aperture = aperture,
|
||||
.sample_count = samples},
|
||||
in_image,
|
||||
out_image);
|
||||
|
||||
int error_code = 0;
|
||||
res = df_write_image(out_image, out_path);
|
||||
if (res != df_result_success) {
|
||||
fprintf(stderr, "Failed to write to output image %s (%d)\n", out_path, (int)res);
|
||||
error_code = 1;
|
||||
}
|
||||
|
||||
df_release_image(in_image);
|
||||
df_release_image(out_image);
|
||||
|
||||
return error_code;
|
||||
}
|
BIN
cam000046.png
(Stored with Git LFS)
Normal file
BIN
cam000046.png
(Stored with Git LFS)
Normal file
Binary file not shown.
|
@ -4,6 +4,8 @@
|
|||
#include <inttypes.h>
|
||||
#include <string.h>
|
||||
|
||||
#include <immintrin.h>
|
||||
|
||||
/** @file base.h
|
||||
* @brief basic utilities
|
||||
*/
|
||||
|
@ -14,14 +16,11 @@ typedef enum
|
|||
/** @brief operation successfull */
|
||||
df_result_success = 0,
|
||||
|
||||
/** @brief an opengl error occured */
|
||||
df_result_gl_error = 1,
|
||||
|
||||
/** @brief memory allocation failed */
|
||||
df_result_out_of_memory = 2,
|
||||
df_result_out_of_memory = 1,
|
||||
|
||||
/** @brief file i/o error */
|
||||
df_result_io_error = 3,
|
||||
df_result_io_error = 2,
|
||||
} df_result;
|
||||
|
||||
/** @brief Used to silence warnings about unused variables */
|
||||
|
@ -86,6 +85,12 @@ void df_log_impl(int level, const char *file, int line, const char *fmt, ...);
|
|||
|
||||
#endif
|
||||
|
||||
/* Math constants.
|
||||
*/
|
||||
#define DF_PI 3.1415926f
|
||||
#define DF_PI_OVER_2 1.570796f
|
||||
#define DF_PI_OVER_4 0.785398f
|
||||
|
||||
/* Basic types */
|
||||
|
||||
/** @brief RGBA color */
|
||||
|
@ -124,6 +129,24 @@ typedef union {
|
|||
float e[2];
|
||||
} df_v2;
|
||||
|
||||
/** @brief Add two 2d vectors */
|
||||
df_v2 df_add_v2(df_v2 a, df_v2 b);
|
||||
|
||||
/** @brief Subtract two 2d vectors */
|
||||
df_v2 df_sub_v2(df_v2 a, df_v2 b);
|
||||
|
||||
/** @brief Calculate the dot product of 2d vectors a and b.
|
||||
*/
|
||||
float df_dot_v2(df_v2 a, df_v2 b);
|
||||
|
||||
/** @brief Multiply a 2d vector with a scalar.
|
||||
*/
|
||||
df_v2 df_mul_v2(float t, df_v2 v);
|
||||
|
||||
/** @brief Returns the normalized version of a 2d vector v
|
||||
*/
|
||||
df_v2 df_normalize_v2(df_v2 v);
|
||||
|
||||
/** @brief 3d vector */
|
||||
typedef union {
|
||||
struct
|
||||
|
@ -200,4 +223,38 @@ typedef struct
|
|||
/** @brief Calculate the intersection between a line and a plane in 3d space */
|
||||
df_line_plane_intersection df_calc_line_plane_intersection(df_line line, df_plane plane);
|
||||
|
||||
/** @brief A 4x4 matrix.
|
||||
*
|
||||
* Stored in row major order.
|
||||
*/
|
||||
typedef struct df_m4
|
||||
{
|
||||
_Alignas(16) union {
|
||||
float e[16];
|
||||
__m128 vec[4];
|
||||
};
|
||||
} df_m4;
|
||||
|
||||
/** Accesses the element at the given row and column of a 4x4 matrix */
|
||||
#define DF_M4_AT(m, row, col) ((m).e[(row)*4 + (col)])
|
||||
|
||||
/** @brief Matrix multiply 4x4 matrix a and b */
|
||||
df_m4 df_mul_m4(df_m4 a, df_m4 b);
|
||||
|
||||
/** @brief Get a scale matrix */
|
||||
df_m4 df_scale(float x, float y, float z);
|
||||
|
||||
df_m4 df_translate(float x, float y, float z);
|
||||
|
||||
/** @brief Transform (i.e. multiply) a 3d vector v by the transformation matrix T */
|
||||
df_v3 df_transform_v3(df_m4 T, df_v3 v);
|
||||
|
||||
/** @brief Calculate the inverse of a non-scaling transform matrix.
|
||||
*
|
||||
* Special fast case.
|
||||
*/
|
||||
df_m4 df_inverse_transform_no_scale(df_m4 M);
|
||||
|
||||
/** @brief Calculate the inverse of a transform matrix */
|
||||
df_m4 df_inverse_transform(df_m4 M);
|
||||
#endif
|
50
include/defocus/camera.h
Normal file
50
include/defocus/camera.h
Normal file
|
@ -0,0 +1,50 @@
|
|||
#ifndef DEFOCUS_CAMERA_H
|
||||
#define DEFOCUS_CAMERA_H
|
||||
|
||||
#include "base.h"
|
||||
|
||||
/** @file camera.h
|
||||
* @brief basic camera functions
|
||||
*/
|
||||
|
||||
/** @brief Stores information for rays in a SIMD friendly way */
|
||||
typedef struct
|
||||
{
|
||||
/** Packs all SIMD data into a single buffer */
|
||||
float *simd_mem;
|
||||
|
||||
/** Source uvs for rays */
|
||||
df_v2 *ray_uvs;
|
||||
|
||||
/* Points into simd_mem */
|
||||
float *base_x;
|
||||
float *base_y;
|
||||
float *base_z;
|
||||
float *dir_x;
|
||||
float *dir_y;
|
||||
float *dir_z;
|
||||
|
||||
size_t ray_count;
|
||||
} df_ray_packet;
|
||||
|
||||
/** @brief Free ray packet memory */
|
||||
void df_release_ray_packet(df_ray_packet *rays);
|
||||
|
||||
/** @brief Interface for cameras. */
|
||||
typedef struct
|
||||
{
|
||||
/** Release the camera object */
|
||||
void (*release)(void *o);
|
||||
|
||||
/** Builds a ray packet of all rays that need to be evaluated to generate the image.
|
||||
*/
|
||||
df_ray_packet (*build_ray_packet)(void *o);
|
||||
|
||||
/** Opaque pointer to the camera object */
|
||||
void *o;
|
||||
} df_camera_i;
|
||||
|
||||
df_camera_i
|
||||
df_create_perspective_camera(float image_width, float image_height, float raster_width, float raster_height);
|
||||
|
||||
#endif
|
38
include/defocus/intrinsic_helper.h
Normal file
38
include/defocus/intrinsic_helper.h
Normal file
|
@ -0,0 +1,38 @@
|
|||
#ifndef DEFOCUS_INTRINSIC_HELPER_H
|
||||
#define DEFOCUS_INTRINSIC_HELPER_H
|
||||
|
||||
/** @file intrinsic_helper.h
|
||||
* @brief Helper macros for intrinsics
|
||||
*
|
||||
* Assumes that at least SS3 is available.
|
||||
*/
|
||||
|
||||
#include <immintrin.h>
|
||||
#include <pmmintrin.h>
|
||||
|
||||
/* Shuffle mask for _mm_shuffle_epi32 */
|
||||
#define DF_MAKE_SHUFFLE_MASK(x, y, z, w) (x | (y << 2) | (z << 4) | (w << 6))
|
||||
|
||||
#define DF_VEC_SWIZZLE_MASK(vec, mask) _mm_castsi128_ps(_mm_shuffle_epi32(_mm_castps_si128(vec), mask))
|
||||
|
||||
#define DF_VEC_SWIZZLE(vec, x, y, z, w) DF_VEC_SWIZZLE_MASK(vec, DF_MAKE_SHUFFLE_MASK(x, y, z, w))
|
||||
|
||||
// "broadcasts" element x [0, 1, 2, 3] to all elements of the result */
|
||||
#define DF_VEC_SWIZZLE1(vec, x) DF_VEC_SWIZZLE_MASK(vec, DF_MAKE_SHUFFLE_MASK(x, x, x, x))
|
||||
|
||||
/* returns [vec0, vec0, vec2, vec2] */
|
||||
#define DF_VEC_SWIZZLE_0022(vec) _mm_moveldup_ps(vec)
|
||||
|
||||
/* returns [vec1, vec1, vec3, vec3] */
|
||||
#define DF_VEC_SWIZZLE_1133(vec) _mm_movehdup_ps(vec)
|
||||
|
||||
/* returns [vec1[x], vec1[y], vec2[z], vec2[w]] */
|
||||
#define DF_VEC_SHUFFLE(vec1, vec2, x, y, z, w) _mm_shuffle_ps(vec1, vec2, DF_MAKE_SHUFFLE_MASK(x, y, z, w))
|
||||
|
||||
/* returns [vec1[0], vec1[1], vec2[0], vec2[1]] */
|
||||
#define DF_VEC_SHUFFLE_0101(vec1, vec2) _mm_movelh_ps(vec1, vec2)
|
||||
|
||||
/* returns [vec1[2], vec1[3], vec2[2], vec2[3]] */
|
||||
#define DF_VEC_SHUFFLE_2323(vec1, vec2) _mm_movehl_ps(vec2, vec1)
|
||||
|
||||
#endif
|
|
@ -7,34 +7,71 @@
|
|||
* Camera models are usually implemented as a function taking an input image and parameters required for the model
|
||||
* and produce an output image.
|
||||
*
|
||||
* Parameters are contained in a parameter struct, because this enables us to something similar to named parameters.
|
||||
* We can also do default parameters, by leaving some values set to zero.
|
||||
*
|
||||
* @code{c}
|
||||
* df_thin_lense((df_thin_lense){
|
||||
* .focal_length = 42.f,
|
||||
* .aperture = 21.f,
|
||||
* .focal_distance = 150.f,
|
||||
* .out_distance = 200.f},
|
||||
* in_image,
|
||||
* out_image);
|
||||
* @endcode
|
||||
*
|
||||
* All camera models use the same coordinate space: The camera looks along the positive z-axis in a right-handed
|
||||
* coordinate system. (-> positive y is down, positive x is right.)
|
||||
|
||||
*/
|
||||
|
||||
#include "image.h"
|
||||
|
||||
/** Parameters for the pinhole model. */
|
||||
typedef struct df_pinhole_params
|
||||
{
|
||||
float focal_length; /**< the cameras focal length in millimeters. */
|
||||
float orig_z; /**< the distance of the input image from the camera. */
|
||||
float new_z; /**< the distance of the generated output image. */
|
||||
} df_pinhole_params;
|
||||
|
||||
/** @brief Simple pinhole camera model.
|
||||
*
|
||||
* The simplest possible(?) camera model.
|
||||
* This function takes an input image at a known distance from the camera and moves it to a new distance.
|
||||
*
|
||||
* @param in_image input image
|
||||
* @param focal_length the cameras focal length in millimeters.
|
||||
* @param orig_z the distance of the input image from the camera.
|
||||
* @param new_z the distance of the generated output image.
|
||||
* @param params model parameters.
|
||||
* @param in_image input image.
|
||||
* @param out_image the output image.
|
||||
*/
|
||||
void df_pinhole(const df_image *in_image, float focal_length, float orig_z, float new_z, df_image *out_image);
|
||||
void df_pinhole(df_pinhole_params params, const df_image *in_image, df_image *out_image);
|
||||
|
||||
/** Parameters for the thin lense model.
|
||||
*/
|
||||
typedef struct df_thin_lense_params
|
||||
{
|
||||
/** The cameras focal length in millimeters. */
|
||||
float focal_length;
|
||||
|
||||
/** The aperture size in millimeters */
|
||||
float aperture;
|
||||
|
||||
/** The distance between the plane of focus and the camera lens, in millimeters */
|
||||
float focal_distance;
|
||||
|
||||
/** The distance between the output image and the camera lens, in millimeters */
|
||||
float out_distance;
|
||||
|
||||
/** The number of samples per pixel. Leave at 0 for the default value (64). */
|
||||
int sample_count;
|
||||
} df_thin_lense_params;
|
||||
|
||||
/** @brief Thin-lense model.
|
||||
*
|
||||
* @param params model parameters
|
||||
* @param in_image input image
|
||||
* @param focal_length the cameras focal length in millimeters.
|
||||
* @param aperture the aperture size in millimeters.
|
||||
* @param orig_z the distnce of the input image from the camera.
|
||||
* @param out_image the output image.
|
||||
*/
|
||||
void df_thin_lense(
|
||||
const df_image *in_image, float focal_length, float aperture, float orig_z, float new_z, df_image *out_image);
|
||||
void df_thin_lense(df_thin_lense_params params, const df_image *in_image, df_image *out_image);
|
||||
|
||||
#endif
|
||||
|
|
37
include/defocus/scene.h
Normal file
37
include/defocus/scene.h
Normal file
|
@ -0,0 +1,37 @@
|
|||
#ifndef DEFOCUS_SCENE_H
|
||||
#define DEFOCUS_SCENE_H
|
||||
|
||||
#include "base.h"
|
||||
#include "image.h"
|
||||
#include "camera.h"
|
||||
|
||||
/** @file scene.h
|
||||
* @brief scene structure */
|
||||
|
||||
/** Opaque scene structure */
|
||||
typedef struct df_scene df_scene;
|
||||
|
||||
df_scene *df_create_scene();
|
||||
|
||||
void df_release_scene(df_scene *scene);
|
||||
|
||||
void df_scene_add_plane(df_scene *scene, df_plane plane, df_image *texture);
|
||||
|
||||
df_image *df_scene_generate_image(df_scene *scene, df_camera_i camera);
|
||||
|
||||
#if 0
|
||||
/* Usage example */
|
||||
|
||||
df_camera_i camera = df_create_perspective_camera(...);
|
||||
df_scene* scene = df_create_scene();
|
||||
|
||||
df_scene_add_plane(..., in_image);
|
||||
|
||||
df_image *out_image = df_scene_generate_image(scene, camera);
|
||||
|
||||
df_release_scene(scene);
|
||||
camera->release(camera->o);
|
||||
|
||||
#endif
|
||||
|
||||
#endif
|
53
lib/camera.c
Normal file
53
lib/camera.c
Normal file
|
@ -0,0 +1,53 @@
|
|||
#include <defocus/camera.h>
|
||||
|
||||
#include <string.h>
|
||||
|
||||
/* ********************************************************
|
||||
*
|
||||
* Ray Packet Functions
|
||||
*
|
||||
* ********************************************************/
|
||||
|
||||
void df_release_ray_packet(df_ray_packet *rays)
|
||||
{
|
||||
free(rays->simd_mem);
|
||||
free(rays->ray_uvs);
|
||||
|
||||
memset(rays, 0, sizeof(*rays));
|
||||
}
|
||||
|
||||
/* ********************************************************
|
||||
*
|
||||
* Perspective Camrea
|
||||
*
|
||||
* ********************************************************/
|
||||
|
||||
typedef struct
|
||||
{
|
||||
float focal_dist;
|
||||
float lens_radius;
|
||||
|
||||
df_m4 screen_to_raster;
|
||||
df_m4 raster_to_screen;
|
||||
df_m4 raster_to_camera;
|
||||
|
||||
} df_perspective_camera;
|
||||
|
||||
static void pc_release(void *o) { df_perspective_camera *camera = o; }
|
||||
|
||||
static df_ray_packet pc_build_ray_packet(void *o) { df_perspective_camera *camera = o; }
|
||||
|
||||
df_camera_i df_create_perspective_camera(float image_width, float image_height, float raster_width, float raster_height)
|
||||
{
|
||||
df_perspective_camera *camera = malloc(sizeof(*camera));
|
||||
|
||||
camera->screen_to_raster = df_scale(raster_width, raster_height, 1.f);
|
||||
camera->screen_to_raster =
|
||||
df_mul_m4(camera->screen_to_raster, df_scale(1.f / image_width, -1.f / image_height, 1.f));
|
||||
camera->screen_to_raster = df_mul_m4(camera->screen_to_raster, df_translate(0.f, -image_height, 0.f));
|
||||
|
||||
camera->raster_to_screen = df_inverse_transform(camera->screen_to_raster);
|
||||
|
||||
df_camera_i iface = {.release = pc_release, .build_ray_packet = pc_build_ray_packet, .o = camera};
|
||||
return iface;
|
||||
}
|
179
lib/math.c
179
lib/math.c
|
@ -1,6 +1,37 @@
|
|||
#include <defocus/base.h>
|
||||
#include <defocus/intrinsic_helper.h>
|
||||
|
||||
#include <math.h>
|
||||
#include <immintrin.h>
|
||||
#include <pmmintrin.h>
|
||||
|
||||
df_v2 df_add_v2(df_v2 a, df_v2 b)
|
||||
{
|
||||
df_v2 v = {a.x + b.x, a.y + b.y};
|
||||
return v;
|
||||
}
|
||||
|
||||
df_v2 df_sub_v2(df_v2 a, df_v2 b)
|
||||
{
|
||||
df_v2 v = {a.x - b.x, a.y - b.y};
|
||||
return v;
|
||||
}
|
||||
|
||||
float df_dot_v2(df_v2 a, df_v2 b) { return a.x * b.x + a.y * b.y; }
|
||||
|
||||
df_v2 df_mul_v2(float t, df_v2 v)
|
||||
{
|
||||
df_v2 r = {t * v.x, t * v.y};
|
||||
return r;
|
||||
}
|
||||
|
||||
df_v2 df_normalize_v2(df_v2 v)
|
||||
{
|
||||
float len_square = df_dot_v2(v, v);
|
||||
float len = sqrtf(len_square);
|
||||
df_v2 n = {v.x / len, v.y / len};
|
||||
return n;
|
||||
}
|
||||
|
||||
df_v3 df_add_v3(df_v3 a, df_v3 b)
|
||||
{
|
||||
|
@ -57,4 +88,150 @@ df_line_plane_intersection df_calc_line_plane_intersection(df_line line, df_plan
|
|||
return intersection;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
df_m4 df_mul_m4(df_m4 a, df_m4 b)
|
||||
{
|
||||
/* Super simple, we could probably do it a lot better via SIMD. */
|
||||
df_m4 p;
|
||||
for (int row = 0; row < 4; ++row) {
|
||||
for (int col = 0; col < 4; ++col) {
|
||||
DF_M4_AT(p, row, col) = 0.f;
|
||||
for (int i = 0; i < 4; ++i) {
|
||||
DF_M4_AT(p, row, col) += DF_M4_AT(a, row, i) * DF_M4_AT(b, i, col);
|
||||
}
|
||||
}
|
||||
}
|
||||
return p;
|
||||
}
|
||||
|
||||
df_m4 df_scale(float x, float y, float z)
|
||||
{
|
||||
/* clang-format off */
|
||||
df_m4 s = {{
|
||||
x, 0.f, 0.f, 0.f,
|
||||
0.f, y, 0.f, 0.f,
|
||||
0.f, 0.f, z, 0.f,
|
||||
0.f, 0.f, 0.f, 1.f,
|
||||
}};
|
||||
/* clang-format on */
|
||||
return s;
|
||||
}
|
||||
|
||||
df_m4 df_translate(float x, float y, float z)
|
||||
{
|
||||
/* clang-format off */
|
||||
df_m4 t = {{
|
||||
1.f, 0.f, 0.f, x,
|
||||
0.f, 1.f, 0.f, y,
|
||||
0.f, 0.f, 1.f, z,
|
||||
0.f, 0.f, 0.f, 1.f,
|
||||
}};
|
||||
/* clang-format on */
|
||||
return t;
|
||||
}
|
||||
|
||||
/** SSE3 version of a horizontal sum:
|
||||
* computes the sum of 4 32bit floats inside v
|
||||
*/
|
||||
static float hsum(__m128 v)
|
||||
{
|
||||
/* v = [v0, v1, v2, v3] */
|
||||
|
||||
/* shuf = [v1, v1, v3, v3] */
|
||||
__m128 shuf = _mm_movehdup_ps(v);
|
||||
|
||||
/* sum = [v0 + v1, 2*v1, v2 + v3, 2*v3] */
|
||||
__m128 sum = _mm_add_ps(v, shuf);
|
||||
|
||||
/* shuf = [v2 + v3, 2*v3, v3, v3] */
|
||||
shuf = _mm_movehl_ps(shuf, sum);
|
||||
|
||||
/* sum = [v0 + v1 + v2 + v3, ...] */
|
||||
sum = _mm_add_ss(sum, shuf);
|
||||
|
||||
return _mm_cvtss_f32(sum);
|
||||
}
|
||||
|
||||
df_v3 df_transform_v3(df_m4 T, df_v3 v)
|
||||
{
|
||||
df_v3 transf;
|
||||
_Alignas(16) float tmp_v[4] = {v.x, v.y, v.z, 1.f};
|
||||
__m128 homov = _mm_load_ps(tmp_v);
|
||||
|
||||
__m128 row0 = _mm_load_ps(&T.e[0]);
|
||||
__m128 row1 = _mm_load_ps(&T.e[4]);
|
||||
__m128 row2 = _mm_load_ps(&T.e[8]);
|
||||
|
||||
__m128 prod = _mm_mul_ps(row0, homov);
|
||||
transf.x = hsum(prod);
|
||||
prod = _mm_mul_ps(row1, homov);
|
||||
transf.y = hsum(prod);
|
||||
prod = _mm_mul_ps(row2, homov);
|
||||
transf.z = hsum(prod);
|
||||
|
||||
return transf;
|
||||
}
|
||||
|
||||
/* Fast 4x4 matrix inverse via SIMD adapted from:
|
||||
* https://lxjk.github.io/2017/09/03/Fast-4x4-Matrix-Inverse-with-SSE-SIMD-Explained.html
|
||||
*/
|
||||
|
||||
/* Only works if M is a transform matrix without scale:
|
||||
*
|
||||
* | R T |
|
||||
* | 0 1 |
|
||||
*/
|
||||
df_m4 df_inverse_transform_no_scale(df_m4 M)
|
||||
{
|
||||
df_m4 I = {0.f};
|
||||
|
||||
/* transpose 3x3, we know that m03 = m13 = m23 = 0 */
|
||||
__m128 t0 = DF_VEC_SHUFFLE_0101(M.vec[0], M.vec[1]); /* 00, 01, 10, 11 */
|
||||
__m128 t1 = DF_VEC_SHUFFLE_2323(M.vec[0], M.vec[1]); /* 02, 03, 12, 13 */
|
||||
I.vec[0] = DF_VEC_SHUFFLE(t0, M.vec[2], 0, 2, 0, 3); /* 00, 01, 20, 23(=0) */
|
||||
I.vec[1] = DF_VEC_SHUFFLE(t0, M.vec[2], 1, 3, 1, 3); /* 01, 11, 21, 23(=0) */
|
||||
I.vec[2] = DF_VEC_SHUFFLE(t1, M.vec[2], 0, 2, 2, 3); /* 02, 12, 22, 23(=0) */
|
||||
|
||||
/* last */
|
||||
I.vec[3] = _mm_mul_ps(I.vec[0], DF_VEC_SWIZZLE1(M.vec[3], 0));
|
||||
I.vec[3] = _mm_add_ps(I.vec[3], _mm_mul_ps(I.vec[1], DF_VEC_SWIZZLE1(M.vec[3], 1)));
|
||||
I.vec[3] = _mm_add_ps(I.vec[3], _mm_mul_ps(I.vec[2], DF_VEC_SWIZZLE1(M.vec[3], 2)));
|
||||
I.vec[3] = _mm_sub_ps(_mm_setr_ps(0.f, 0.f, 0.f, 1.f), I.vec[3]);
|
||||
|
||||
return I;
|
||||
}
|
||||
|
||||
df_m4 df_inverse_transform(df_m4 M)
|
||||
{
|
||||
#define SMALL_NUMBER (1.e-8f)
|
||||
|
||||
df_m4 I = {0.f};
|
||||
|
||||
/* transpose 3x3, we know that m03 = m13 = m23 = 0 */
|
||||
__m128 t0 = DF_VEC_SHUFFLE_0101(M.vec[0], M.vec[1]); /* 00, 01, 10, 11 */
|
||||
__m128 t1 = DF_VEC_SHUFFLE_2323(M.vec[0], M.vec[1]); /* 02, 03, 12, 13 */
|
||||
I.vec[0] = DF_VEC_SHUFFLE(t0, M.vec[2], 0, 2, 0, 3); /* 00, 01, 20, 23(=0) */
|
||||
I.vec[1] = DF_VEC_SHUFFLE(t0, M.vec[2], 1, 3, 1, 3); /* 01, 11, 21, 23(=0) */
|
||||
I.vec[2] = DF_VEC_SHUFFLE(t1, M.vec[2], 0, 2, 2, 3); /* 02, 12, 22, 23(=0) */
|
||||
|
||||
/* divide by the squared scale */
|
||||
__m128 size_sqr = _mm_mul_ps(I.vec[0], I.vec[0]);
|
||||
size_sqr = _mm_add_ps(size_sqr, _mm_mul_ps(I.vec[1], I.vec[1]));
|
||||
size_sqr = _mm_add_ps(size_sqr, _mm_mul_ps(I.vec[2], I.vec[2]));
|
||||
|
||||
__m128 r_size_sqr = _mm_div_ps(_mm_set1_ps(1.f), size_sqr);
|
||||
|
||||
I.vec[0] = _mm_mul_ps(I.vec[0], r_size_sqr);
|
||||
I.vec[1] = _mm_mul_ps(I.vec[1], r_size_sqr);
|
||||
I.vec[2] = _mm_mul_ps(I.vec[2], r_size_sqr);
|
||||
|
||||
/* last */
|
||||
I.vec[3] = _mm_mul_ps(I.vec[0], DF_VEC_SWIZZLE1(M.vec[3], 0));
|
||||
I.vec[3] = _mm_add_ps(I.vec[3], _mm_mul_ps(I.vec[1], DF_VEC_SWIZZLE1(M.vec[3], 1)));
|
||||
I.vec[3] = _mm_add_ps(I.vec[3], _mm_mul_ps(I.vec[2], DF_VEC_SWIZZLE1(M.vec[3], 2)));
|
||||
I.vec[3] = _mm_sub_ps(_mm_setr_ps(0.f, 0.f, 0.f, 1.f), I.vec[3]);
|
||||
|
||||
return I;
|
||||
#undef SMALL_NUMBER
|
||||
}
|
||||
|
|
|
@ -4,7 +4,7 @@
|
|||
|
||||
#include <math.h>
|
||||
|
||||
void df_pinhole(const df_image *in_image, float focal_length, float orig_z, float new_z, df_image *out_image)
|
||||
void df_pinhole(df_pinhole_params params, const df_image *in_image, df_image *out_image)
|
||||
{
|
||||
/* orig_z is the z-coordinate of the original image plane (=> in_image is located there)
|
||||
* new_z is the z-coordinate of the output image plane (=> out_image is located there)
|
||||
|
@ -25,8 +25,8 @@ void df_pinhole(const df_image *in_image, float focal_length, float orig_z, floa
|
|||
int out_w, out_h;
|
||||
df_get_image_size(out_image, &out_w, &out_h);
|
||||
|
||||
double in_z_over_f = orig_z / focal_length;
|
||||
double out_f_over_z = focal_length / new_z;
|
||||
double in_z_over_f = params.orig_z / params.focal_length;
|
||||
double out_f_over_z = params.focal_length / params.new_z;
|
||||
|
||||
int prev_out_iy = -1;
|
||||
for (int iy = 0; iy < h; ++iy) {
|
||||
|
|
127
lib/thin_lense.c
127
lib/thin_lense.c
|
@ -1,6 +1,129 @@
|
|||
#include <defocus/base.h>
|
||||
#include <defocus/models.h>
|
||||
#include <defocus/image.h>
|
||||
|
||||
void df_thin_lense(
|
||||
const df_image *in_image, float focal_length, float aperture, float orig_z, float new_z, df_image *out_image)
|
||||
#include <math.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
/* The thin lense model, implemented as a quasi-raytracer. */
|
||||
|
||||
/** Map uv coordinates in [0, 1) to the unit circle centered at (0, 0). */
|
||||
static df_v2 concentric_map_disk(df_v2 in_p)
|
||||
{
|
||||
df_v2 offset = df_sub_v2(df_mul_v2(2.f, in_p), (df_v2){{1.f, 1.f}});
|
||||
if (offset.u == 0.f && offset.v == 0.f)
|
||||
return offset;
|
||||
|
||||
float theta, r;
|
||||
if (fabsf(offset.u) > fabsf(offset.v)) {
|
||||
r = offset.u;
|
||||
theta = DF_PI_OVER_4 * (offset.v / offset.u);
|
||||
} else {
|
||||
r = offset.v;
|
||||
theta = DF_PI_OVER_2 - DF_PI_OVER_4 * (offset.u / offset.v);
|
||||
}
|
||||
return df_mul_v2(r, (df_v2){{cosf(theta), sinf(theta)}});
|
||||
}
|
||||
|
||||
void df_thin_lense(df_thin_lense_params params, const df_image *in_image, df_image *out_image)
|
||||
{
|
||||
/* We do the following:
|
||||
* - Orthographic projection (i.e. don't do anything really except flip the y axis)
|
||||
* - For each pixel:
|
||||
* - Transform from raster space into camera space
|
||||
* - Generate ray from pixel to plane of focus through lense center
|
||||
* - For each sample:
|
||||
* - Choose random point on lense
|
||||
* - Trace ray from lense point through the above intersection point to the image.
|
||||
* - Sum all samples
|
||||
*/
|
||||
|
||||
int sample_count = (params.sample_count > 0) ? params.sample_count : 64;
|
||||
|
||||
int w, h;
|
||||
df_get_image_size(out_image, &w, &h);
|
||||
|
||||
/* TODO(Kevin): It would be pretty trivial to
|
||||
* a) parallelize this, and
|
||||
* b) use SIMD for multiple rays
|
||||
*/
|
||||
for (int y = 0; y < h; ++y) {
|
||||
float cam_y = (float)(y - h) / 2.f;
|
||||
for (int x = 0; x < w; ++x) {
|
||||
float cam_x = (float)(x - w) / 2.f;
|
||||
|
||||
df_v3 ray_dir = {{cam_x, cam_y, 1.f}};
|
||||
ray_dir = df_normalize_v3(ray_dir);
|
||||
ray_dir.z = 1.f;
|
||||
|
||||
/* Calculate the intersection with the plane of focus */
|
||||
df_v3 focus_p = df_mul_v3(params.focal_distance, ray_dir);
|
||||
|
||||
uint32_t color[4] = {0, 0, 0, 0};
|
||||
for (int sample_idx = 0; sample_idx < sample_count; ++sample_idx) {
|
||||
df_v2 lens_uv = {(float)(rand() % 1024) / 1024.f, (float)(rand() % 1024) / 1024.f};
|
||||
df_v2 lens_p = df_mul_v2(params.aperture, concentric_map_disk(lens_uv));
|
||||
|
||||
ray_dir.x = focus_p.x - lens_p.x;
|
||||
ray_dir.y = focus_p.y - lens_p.y;
|
||||
ray_dir.z = focus_p.z;
|
||||
ray_dir = df_normalize_v3(ray_dir);
|
||||
|
||||
df_v3 sample_p = df_mul_v3(params.focal_distance, ray_dir);
|
||||
|
||||
int img_x = (int)sample_p.x + w / 2;
|
||||
int img_y = (int)sample_p.y + h / 2;
|
||||
|
||||
df_color sample_color = df_get_image_pixel(in_image, img_x, img_y);
|
||||
|
||||
for (int i = 0; i < 4; ++i)
|
||||
color[i] += (uint32_t)sample_color.e[i];
|
||||
}
|
||||
|
||||
df_color pixel_color = {
|
||||
{color[0] / sample_count, color[1] / sample_count, color[2] / sample_count, color[3] / sample_count}};
|
||||
|
||||
df_set_image_pixel(out_image, x, y, pixel_color);
|
||||
}
|
||||
}
|
||||
|
||||
#if 0
|
||||
/* The guassian lens equation 1/z' - 1/z = 1/f
|
||||
* with z' := distance between film and lens,
|
||||
* z := focal distance, i.e. the distance between lens and the plane of focus
|
||||
* f := focal length of the lens
|
||||
*
|
||||
* gives us: z' = fz / (f + z) */
|
||||
float film_dist = (params.focal_length * params.focal_distance) / (params.focal_length + params.focal_distance);
|
||||
|
||||
int sample_count = (params.sample_count > 0) ? params.sample_count : 64;
|
||||
|
||||
int w, h;
|
||||
df_get_image_size(out_image, &w, &h);
|
||||
|
||||
for (int y = 0; y < h; ++y) {
|
||||
float v = (float)y / (float)h;
|
||||
for (int x = 0; x < w; ++x) {
|
||||
float u = (float)x / (float)w;
|
||||
df_v2 img_p = {u, v};
|
||||
float color[4] = {0, 0, 0, 0};
|
||||
|
||||
df_v3 focus_ray = {};
|
||||
focus_ray.z = 1.f;
|
||||
focus_ray.x = 0.f;
|
||||
focus_ray.y = 0.f;
|
||||
|
||||
for (int sample_idx = 0; sample_idx < sample_count; ++sample_idx) {
|
||||
/* Generate a random sample on the lense */
|
||||
df_v2 sample_p = {(float)(rand() % 1024) / 1024.f, (float)(rand() % 1024) / 1024.f};
|
||||
df_v2 lens_p = df_mul_v2(params.aperture, concentric_map_disk(sample_p));
|
||||
|
||||
/* Compute the intersection point on the plane of focus. */
|
||||
/* TODO: Use a ray from img_p to the center of the lens,
|
||||
* trace to focal_distance.
|
||||
*/
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif
|
||||
}
|
11
meson.build
11
meson.build
|
@ -5,9 +5,12 @@ incdir = include_directories('include', '3p')
|
|||
cc = meson.get_compiler('c')
|
||||
m_dep = cc.find_library('m', required: false)
|
||||
|
||||
add_project_arguments([ '-msse3', '-Wno-missing-braces' ], language: 'c')
|
||||
|
||||
lib = library('df',
|
||||
'lib/log.c',
|
||||
'lib/math.c',
|
||||
'lib/camera.c',
|
||||
'lib/pinhole.c',
|
||||
'lib/image.c',
|
||||
'lib/color.c',
|
||||
|
@ -16,4 +19,10 @@ lib = library('df',
|
|||
dependencies: m_dep,
|
||||
version: '0.1.0',
|
||||
soversion: '0')
|
||||
executable('defocus', 'bin/defocus.c', include_directories: incdir, link_with: lib)
|
||||
|
||||
# Command Line Executable
|
||||
executable('defocus', 'bin/defocus.c', include_directories: incdir, link_with: lib)
|
||||
|
||||
# Test driver
|
||||
munit_dep = dependency('munit', fallback: ['munit', 'munit_dep'])
|
||||
executable('tests', 'tests/tests.c', include_directories: incdir, link_with: lib, dependencies: munit_dep)
|
4
subprojects/munit.wrap
Normal file
4
subprojects/munit.wrap
Normal file
|
@ -0,0 +1,4 @@
|
|||
[wrap-git]
|
||||
directory=munit
|
||||
url=https://github.com/nemequ/munit/
|
||||
revision=head
|
67
tests/tests.c
Normal file
67
tests/tests.c
Normal file
|
@ -0,0 +1,67 @@
|
|||
#include "munit.h"
|
||||
#include <defocus/defocus.h>
|
||||
|
||||
static MunitResult test_transforms_identity(const MunitParameter params[], void *user_data)
|
||||
{
|
||||
/* Check that the identity matrix does what it's supposed to do. */
|
||||
/* clang-format off */
|
||||
df_m4 identity = {
|
||||
{1.f, 0.f, 0.f, 0.f,
|
||||
0.f, 1.f, 0.f, 0.f,
|
||||
0.f, 0.f, 1.f, 0.f,
|
||||
0.f, 0.f, 0.f, 1.f}
|
||||
};
|
||||
/* clang-format on */
|
||||
|
||||
df_v3 v = {1.f, 2.f, 3.f};
|
||||
|
||||
df_v3 identity_transform = df_transform_v3(identity, v);
|
||||
|
||||
munit_assert_double_equal(v.x, identity_transform.x, 6);
|
||||
munit_assert_double_equal(v.y, identity_transform.y, 6);
|
||||
munit_assert_double_equal(v.z, identity_transform.z, 6);
|
||||
|
||||
return MUNIT_OK;
|
||||
}
|
||||
|
||||
static MunitResult test_transforms_translation(const MunitParameter params[], void *user_data)
|
||||
{
|
||||
/* Check that the identity matrix does what it's supposed to do. */
|
||||
/* clang-format off */
|
||||
df_m4 translate = {
|
||||
{1.f, 0.f, 0.f, 1.f,
|
||||
0.f, 1.f, 0.f, -1.f,
|
||||
0.f, 0.f, 1.f, .5f,
|
||||
0.f, 0.f, 0.f, 1.f}
|
||||
};
|
||||
/* clang-format on */
|
||||
|
||||
df_v3 v = {1.f, 2.f, 3.f};
|
||||
|
||||
df_v3 tv = df_transform_v3(translate, v);
|
||||
|
||||
munit_assert_double_equal(tv.x, v.x + 1.f, 6);
|
||||
munit_assert_double_equal(tv.y, v.y - 1.f, 6);
|
||||
munit_assert_double_equal(tv.z, v.z + .5f, 6);
|
||||
|
||||
return MUNIT_OK;
|
||||
}
|
||||
|
||||
static MunitTest _tests[] = {
|
||||
{(char *)"/math/transforms/identity", test_transforms_identity, NULL, NULL, MUNIT_TEST_OPTION_NONE, NULL},
|
||||
{(char *)"/math/transforms/translation", test_transforms_translation, NULL, NULL, MUNIT_TEST_OPTION_NONE, NULL},
|
||||
{NULL, NULL, NULL, NULL, MUNIT_TEST_OPTION_NONE, NULL}};
|
||||
|
||||
static MunitSuite _test_suite = {
|
||||
"",
|
||||
_tests,
|
||||
NULL,
|
||||
1,
|
||||
MUNIT_SUITE_OPTION_NONE,
|
||||
};
|
||||
|
||||
int main(int argc, char **argv)
|
||||
{
|
||||
munit_suite_main(&_test_suite, NULL, argc, argv);
|
||||
return 0;
|
||||
}
|
Loading…
Reference in New Issue
Block a user