You cannot select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
raymarcher.h/marcher.c

168 lines
4.2 KiB
C

#include <math.h>
#include <stdlib.h>
#include <stdio.h>
#include "images/src/images.h"
#include "src/scene.h"
#include "src/camera.h"
#include "src/point.h"
#define SCENE_MOD 2
/*
Infinite circle scene object
This circle repeats every SCENE_MOD units in all directions
It has some basic shading, calculation the hit angle and making shallower impacts darker
*/
double circle_dist(struct point x, struct scene_object *self) {
double r = self->args[0];
return pt_dist(pt_mod(x, SCENE_MOD), self->location) - r;
}
Color circle_color(struct point hit, struct point direction, struct scene_object *self) {
struct point obj_direction = self->location;
obj_direction = pt_sub(obj_direction, pt_mod(hit, SCENE_MOD));
double angle = pt_angle(direction, obj_direction) / M_PI * 180;
Color color = self->color;
if (angle > 90) angle = 180 - angle ; // clamp angle to 0-90
color = color_mix(color, color_new(0,0,0), 1 - (angle / (double) 120));
return color;
}
// constructs the scene object
struct scene_object circle_new(struct point loc, double radius) {
double * args = malloc(sizeof (double) * 2);
args[0] = radius;
return (struct scene_object) {
.location = loc,
.args = args,
.distance = circle_dist,
.get_color = circle_color,
.color = color_new(255, 255, 255),
};
}
/*
Mandelbulb scene object
Currently cannot be set at a specific location, always resides at origin (0,0,0)
Color function is just a flat shader, detail is displayed with ambient occlusion
*/
double mandelbulb_dist(struct point pt, struct scene_object *self) {
int iters = self->args[0];
double power = self->args[1];
struct point z = pt;
float dr = 1.0;
float r = 0.0;
for (int i = 0; i < iters ; i++) {
r = pt_length(z);
if (r>2) {
break;
}
// convert to polar coordinates
float theta = acos(z.z/r);
float phi = atan2(z.y,z.x);
dr = pow(r, power-1.0)*power*dr + 1.0;
// scale and rotate the point
float zr = pow(r, power);
theta = theta*power;
phi = phi*power;
// convert back to cartesian coordinates, add zr and the old pt
z = (struct point) {
.x = sin(theta)*cos(phi) * zr + pt.x,
.y = sin(phi)*sin(theta) * zr + pt.y,
.z = cos(theta) * zr + pt.z
};
}
return 0.5*log(r)*r/dr;
}
Color mandelbulb_color(struct point hit, struct point direction, struct scene_object *self) {
return self->color;
}
// constructs the scene object
struct scene_object mandelbulb_new(struct point location, int iters, double power) {
double * args = malloc(sizeof(double) * 3);
args[0] = iters;
args[1] = power;
args[2] = -1;
return (struct scene_object) {
.location= location,
.args= args,
.distance= mandelbulb_dist,
.get_color= mandelbulb_color,
.color= color_new(255,255,255),
};
}
int main(int argc, char* argv[]) {
float dpi = 200;
int threads = 32;
int size = dpi * 27.56f; // 400dpi by 70cm size
float pow = 3;
float cam_position = 1.35;
int steps = 1000;
int iters = 500;
float threshold = 0.001;
char path[80];
// get params from cli
if (argc < 5) {
return 1;
}
pow = atof(argv[1]);
steps = atoi(argv[2]);
iters = atoi(argv[3]);
threshold = atof(argv[4]);
sprintf(path, "tries/pow%.2f-%ist-%iit-%.5fth-%.0fdpi.bmp", pow, steps, iters, threshold, dpi);
printf("Rendering to %s\n", path);
struct camera cam;
cam.fov = 90;
camera_set_looking_at(&cam, (struct point){.x=cam_position, .y= 0, .z = cam_position}, PT_ZERO);
// create basic scene with up to 10 objects
struct scene scene = scene_new(size, size, 1);
scene.perf_opts.max_steps = steps;
scene.perf_opts.threshold = threshold;
scene.perf_opts.speed_cutoff = 10;
scene.background = color_new(0,0,0);
//scene_add_obj(&scene, circle_new(pt_new(SCENE_MOD / 2.0, SCENE_MOD/ 2.0, SCENE_MOD / 2.0), .2));
scene_add_obj(&scene, mandelbulb_new(PT_ZERO, iters, pow));
Image *img = render_scene(&scene, &cam, threads);
image_save_bmp(*img, path);
image_destroy(*img);
scene_destroy(scene);
return 0;
}