Importance sampling
This commit is contained in:
+15
-4
@@ -21,8 +21,8 @@ SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
#include "camera.h"
|
||||
|
||||
static bool first_mouse = true;
|
||||
static float yaw = -90.0f;
|
||||
static float pitch = 0.0f;
|
||||
static float yaw = 0;
|
||||
static float pitch = -3.5;
|
||||
static float last_x = 800.0f / 2.0;
|
||||
static float last_y = 600.0f / 2.0;
|
||||
static float fov = 30.0f;
|
||||
@@ -30,10 +30,21 @@ static float fov = 30.0f;
|
||||
static float delta_time = 0.0f;
|
||||
static float last_frame = 0.0f;
|
||||
|
||||
static Vector3 camera_pos = {5, 5, 5};
|
||||
static Vector3 camera_front = {-1, -1, -1};
|
||||
static Vector3 camera_pos = {-3, 2.5, 2.5};
|
||||
static Vector3 camera_front = {1, 0, 0};
|
||||
static Vector3 camera_up = {0, 1, 0};
|
||||
|
||||
#include <stdio.h>
|
||||
void print_camera(void)
|
||||
{
|
||||
fprintf(stderr, "camera:\n");
|
||||
fprintf(stderr, " pos={%f %f %f}\n", camera_pos.x, camera_pos.y, camera_pos.z);
|
||||
fprintf(stderr, " front={%f %f %f}\n", camera_front.x, camera_front.y, camera_front.z);
|
||||
fprintf(stderr, " up={%f %f %f}\n", camera_up.x, camera_up.y, camera_up.z);
|
||||
fprintf(stderr, " yaw=%f\n", yaw);
|
||||
fprintf(stderr, " pitch=%f\n", pitch);
|
||||
}
|
||||
|
||||
Vector3 get_camera_pos(void)
|
||||
{
|
||||
return camera_pos;
|
||||
|
||||
+386
-114
@@ -46,11 +46,14 @@ typedef struct {
|
||||
float roughness;
|
||||
} PixelInfo;
|
||||
|
||||
typedef void (*FilterFunc)(Vector3*, Vector3*, float*, float*, Vector3*, int, int, int, int, int);
|
||||
/////////////////////////////////////////////////////////////////////////////
|
||||
/// GLOBAL VARIABLES ///
|
||||
/////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
#define MAX_COLUMNS 32
|
||||
#define INIT_SMOOTH_LIMIT 10
|
||||
#define SMOOTH_RATIO 1.5
|
||||
|
||||
// Parameters. These are set at startup and are
|
||||
// considered constant after that.
|
||||
@@ -64,7 +67,8 @@ int color_buffer_frames = 0;
|
||||
Vector3 *color_buffer;
|
||||
float *depth_buffer;
|
||||
Vector3 *normal_buffer;
|
||||
Vector3 *filter_buffer;
|
||||
Vector3 *filter_buffer_A;
|
||||
Vector3 *filter_buffer_B;
|
||||
float *roughness_buffer;
|
||||
|
||||
// Size of the accumulation and frame buffers
|
||||
@@ -87,14 +91,18 @@ uint64_t global_cycle_pixel_sum = 0;
|
||||
uint64_t global_pixel_count = 0;
|
||||
|
||||
uint64_t smooth_index = 0;
|
||||
uint64_t smooth_limit = 10;
|
||||
uint64_t smooth_limit = INIT_SMOOTH_LIMIT;
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////
|
||||
/// FUNCTION PROTOTYPES ///
|
||||
/////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
void start_workers(void);
|
||||
void stop_workers(void);
|
||||
void start_filtering_workers(void);
|
||||
void stop_filtering_workers(void);
|
||||
void filter(Vector3 *input, Vector3 *output, FilterFunc func);
|
||||
|
||||
void start_ray_tracing_workers(void);
|
||||
void stop_ray_tracing_workers(void);
|
||||
|
||||
bool quitting(void);
|
||||
void screenshot(void);
|
||||
@@ -119,7 +127,7 @@ void invalidate_accumulation(void)
|
||||
memset(roughness_buffer, 0, sizeof(float) * frame_w * frame_h);
|
||||
color_buffer_frames = 0;
|
||||
smooth_index = 0;
|
||||
smooth_limit = 10;
|
||||
smooth_limit = INIT_SMOOTH_LIMIT;
|
||||
os_mutex_unlock(&frame_mutex);
|
||||
}
|
||||
|
||||
@@ -128,6 +136,24 @@ Vector3 fresnel_schlick(float u, Vector3 f0)
|
||||
return combine(f0, combine(vec_from_scalar(1.0), f0, 1, -1), 1, pow(1.0 - u, 5.0));
|
||||
}
|
||||
|
||||
Vector3 cosine_sample(Vector3 n)
|
||||
{
|
||||
float eps = 0.001;
|
||||
Vector3 u = {1, 0, 0};
|
||||
if (absf(n.y) < 1 - eps)
|
||||
u = normalize(cross(n, (Vector3) {0, 1, 0}));
|
||||
Vector3 v = cross(n, u);
|
||||
float phi = 2.0 * M_PI * random_float();
|
||||
float ay = sqrt(random_float());
|
||||
float ax = sqrt(1.0 - ay*ay);
|
||||
|
||||
Vector3 result;
|
||||
result.x = ax * (cos(phi) * u.x + sin(phi) * v.x) + ay * n.x;
|
||||
result.y = ax * (cos(phi) * u.y + sin(phi) * v.y) + ay * n.y;
|
||||
result.z = ax * (cos(phi) * u.z + sin(phi) * v.z) + ay * n.z;
|
||||
return result;
|
||||
}
|
||||
|
||||
static _Thread_local uint64_t wyhash64_x = 0;
|
||||
|
||||
static uint64_t wyhash64(void) {
|
||||
@@ -140,12 +166,18 @@ static uint64_t wyhash64(void) {
|
||||
return m2;
|
||||
}
|
||||
|
||||
uint64_t rand64(void)
|
||||
{
|
||||
return wyhash64();
|
||||
}
|
||||
|
||||
PixelInfo pixel_inner(Ray in_ray)
|
||||
{
|
||||
// Find a light source. This is kind of lazy as we should
|
||||
// sample every light source in the scene.
|
||||
float light_sample_weight = 0.05;
|
||||
Vector3 weighted_light_emission = {0, 0, 0};
|
||||
Vector3 light_emission = {0, 0, 0};
|
||||
Object *light_object = NULL;
|
||||
Vector3 light_origin;
|
||||
for (int i = 0; i < scene.num_objects; i++) {
|
||||
@@ -154,6 +186,9 @@ PixelInfo pixel_inner(Ray in_ray)
|
||||
weighted_light_emission.x += material.emission_color.x * material.emission_power * light_sample_weight;
|
||||
weighted_light_emission.y += material.emission_color.y * material.emission_power * light_sample_weight;
|
||||
weighted_light_emission.z += material.emission_color.z * material.emission_power * light_sample_weight;
|
||||
light_emission.x += material.emission_color.x * material.emission_power;
|
||||
light_emission.y += material.emission_color.y * material.emission_power;
|
||||
light_emission.z += material.emission_color.z * material.emission_power;
|
||||
light_object = &scene.objects[i];
|
||||
light_origin = origin_of(scene.objects[i]);
|
||||
break;
|
||||
@@ -168,7 +203,7 @@ PixelInfo pixel_inner(Ray in_ray)
|
||||
Vector3 result = {0, 0, 0};
|
||||
|
||||
// Maximum number of bounces of the ray
|
||||
int bounces = 5;
|
||||
int bounces = 6;
|
||||
|
||||
PixelInfo info;
|
||||
info.depth = 1000000;
|
||||
@@ -186,9 +221,9 @@ PixelInfo pixel_inner(Ray in_ray)
|
||||
// The sky is sampled here. You can change the sky color here
|
||||
// if you want:
|
||||
// Vector3 sky_color = {0.6, 0.7, 0.9};
|
||||
// Vector3 sky_color = {0, 0, 0};
|
||||
Vector3 sky_color = {0, 0, 0};
|
||||
// Vector3 sky_color = {1, 1, 1};
|
||||
Vector3 sky_color = sample_cubemap(&skybox, normalize(in_ray.direction));
|
||||
//Vector3 sky_color = sample_cubemap(&skybox, normalize(in_ray.direction));
|
||||
result = combine(result, mulv(sky_color, contrib), 1, 1);
|
||||
break;
|
||||
}
|
||||
@@ -201,7 +236,7 @@ PixelInfo pixel_inner(Ray in_ray)
|
||||
info.roughness = material.roughness;
|
||||
}
|
||||
|
||||
uint64_t rand_bucket_0 = wyhash64();
|
||||
uint64_t rand_bucket_0 = rand64();
|
||||
|
||||
Vector3 v = in_ray.direction;
|
||||
Vector3 n = hit.normal;
|
||||
@@ -219,7 +254,7 @@ PixelInfo pixel_inner(Ray in_ray)
|
||||
uint64_t rand_1 = (rand_bucket_0 >> 20) & 0xFFFFF;
|
||||
uint64_t rand_2 = (rand_bucket_0 >> 40) & 0xFFFFF;
|
||||
|
||||
uint64_t rand_bucket_1 = wyhash64();
|
||||
uint64_t rand_bucket_1 = rand64();
|
||||
|
||||
result.x += contrib.x * material.emission_color.x * material.emission_power;
|
||||
result.y += contrib.y * material.emission_color.y * material.emission_power;
|
||||
@@ -239,6 +274,7 @@ PixelInfo pixel_inner(Ray in_ray)
|
||||
Vector3 rand_dir;
|
||||
float rand_dir_factor;
|
||||
{
|
||||
#if 0
|
||||
rand_dir_factor = (float) 2 / 0xFFFFF;
|
||||
|
||||
rand_dir.x = (float) rand_0 - 0.5 * 0xFFFFF;
|
||||
@@ -247,10 +283,26 @@ PixelInfo pixel_inner(Ray in_ray)
|
||||
|
||||
if (rand_dir.x * n.x + rand_dir.y * n.y + rand_dir.z * n.z < 0)
|
||||
rand_dir_factor = -rand_dir_factor;
|
||||
#else
|
||||
Vector3 dir_to_light;
|
||||
dir_to_light.x = light_origin.x - o.x;
|
||||
dir_to_light.y = light_origin.y - o.y;
|
||||
dir_to_light.z = light_origin.z - o.z;
|
||||
|
||||
rand_dir = cosine_sample(dir_to_light);
|
||||
float p = dotv(rand_dir, n);
|
||||
assert(p > 0);
|
||||
rand_dir_factor = 1;
|
||||
|
||||
contrib.x /= p;
|
||||
contrib.y /= p;
|
||||
contrib.z /= p;
|
||||
|
||||
#endif
|
||||
}
|
||||
|
||||
Vector3 out_dir;
|
||||
uint64_t rand_6 = wyhash64();
|
||||
uint64_t rand_6 = rand64();
|
||||
if (material.metallic > 0.001 || rand_6 <= F * UINT64_MAX) {
|
||||
|
||||
// Specular ray
|
||||
@@ -270,6 +322,28 @@ PixelInfo pixel_inner(Ray in_ray)
|
||||
|
||||
// Diffuse ray
|
||||
|
||||
if (false) {
|
||||
|
||||
Vector3 dir_to_light;
|
||||
dir_to_light.x = light_origin.x - o.x;
|
||||
dir_to_light.y = light_origin.y - o.y;
|
||||
dir_to_light.z = light_origin.z - o.z;
|
||||
|
||||
Vector3 sample_dir = cosine_sample(dir_to_light);
|
||||
|
||||
float p = dotv(sample_dir, dir_to_light);
|
||||
assert(p < 1.1);
|
||||
|
||||
out_dir.x = sample_dir.x;
|
||||
out_dir.y = sample_dir.y;
|
||||
out_dir.z = sample_dir.z;
|
||||
|
||||
contrib.x = (1 - material.metallic) * contrib.x * material.albedo.x / p;
|
||||
contrib.y = (1 - material.metallic) * contrib.y * material.albedo.y / p;
|
||||
contrib.z = (1 - material.metallic) * contrib.z * material.albedo.z / p;
|
||||
|
||||
} else {
|
||||
|
||||
out_dir.x = rand_dir_factor * rand_dir.x;
|
||||
out_dir.y = rand_dir_factor * rand_dir.y;
|
||||
out_dir.z = rand_dir_factor * rand_dir.z;
|
||||
@@ -278,6 +352,7 @@ PixelInfo pixel_inner(Ray in_ray)
|
||||
contrib.y = (1 - material.metallic) * contrib.y * material.albedo.y;
|
||||
contrib.z = (1 - material.metallic) * contrib.z * material.albedo.z;
|
||||
}
|
||||
}
|
||||
|
||||
Ray out_ray;
|
||||
out_ray.direction = out_dir;
|
||||
@@ -285,33 +360,21 @@ PixelInfo pixel_inner(Ray in_ray)
|
||||
out_ray.origin.y = hit.point.y + out_dir.y * 0.001;
|
||||
out_ray.origin.z = hit.point.z + out_dir.z * 0.001;
|
||||
|
||||
if (light_object) {
|
||||
|
||||
float spread = 0.5;
|
||||
if (false) {
|
||||
|
||||
Vector3 dir_to_light;
|
||||
dir_to_light.x = light_origin.x - o.x;
|
||||
dir_to_light.y = light_origin.y - o.y;
|
||||
dir_to_light.z = light_origin.z - o.z;
|
||||
|
||||
uint64_t rand_3 = (rand_bucket_1 >> 0) & 0xFFFFF;
|
||||
uint64_t rand_4 = (rand_bucket_1 >> 20) & 0xFFFFF;
|
||||
uint64_t rand_5 = (rand_bucket_1 >> 40) & 0xFFFFF;
|
||||
if (dir_to_light.x*n.x +
|
||||
dir_to_light.y*n.y +
|
||||
dir_to_light.z*n.z > 0) {
|
||||
#if 1
|
||||
Vector3 sample_dir = cosine_sample(dir_to_light);
|
||||
|
||||
Vector3 rand_dir;
|
||||
rand_dir.x = (float) rand_3 - 0.5 / 0xFFFFF;
|
||||
rand_dir.y = (float) rand_4 - 0.5 / 0xFFFFF;
|
||||
rand_dir.z = (float) rand_5 - 0.5 / 0xFFFFF;
|
||||
|
||||
float rand_dir_factor = spread * 2 / 0xFFFFF;
|
||||
|
||||
float dot = rand_dir.x * n.x + rand_dir.y * n.y + rand_dir.z * n.z;
|
||||
if (dot < 0) rand_dir_factor = -rand_dir_factor;
|
||||
|
||||
Vector3 sample_dir;
|
||||
sample_dir.x = rand_dir_factor * rand_dir.x + dir_to_light.x;
|
||||
sample_dir.y = rand_dir_factor * rand_dir.y + dir_to_light.y;
|
||||
sample_dir.z = rand_dir_factor * rand_dir.z + dir_to_light.z;
|
||||
float p = dotv(sample_dir, dir_to_light);
|
||||
assert(p < 1.1);
|
||||
|
||||
Ray sample_ray;
|
||||
float eps = 0.001;
|
||||
@@ -323,14 +386,58 @@ PixelInfo pixel_inner(Ray in_ray)
|
||||
HitInfo hit2 = trace_ray(sample_ray, &scene);
|
||||
if (hit2.object == light_object - scene.objects) {
|
||||
|
||||
result.x += contrib.x * weighted_light_emission.x;
|
||||
result.y += contrib.y * weighted_light_emission.y;
|
||||
result.z += contrib.z * weighted_light_emission.z;
|
||||
result.x += contrib.x * light_emission.x / p;
|
||||
result.y += contrib.y * light_emission.y / p;
|
||||
result.z += contrib.z * light_emission.z / p;
|
||||
/*
|
||||
contrib.x *= 1 - p;
|
||||
contrib.y *= 1 - p;
|
||||
contrib.z *= 1 - p;
|
||||
*/
|
||||
}
|
||||
#else
|
||||
float spread = 0;
|
||||
|
||||
uint64_t rand_3 = (rand_bucket_1 >> 0) & 0xFFFFF;
|
||||
uint64_t rand_4 = (rand_bucket_1 >> 20) & 0xFFFFF;
|
||||
uint64_t rand_5 = (rand_bucket_1 >> 40) & 0xFFFFF;
|
||||
|
||||
Vector3 rand_dir;
|
||||
rand_dir.x = (float) rand_3 - 0.5 / 0xFFFFF;
|
||||
rand_dir.y = (float) rand_4 - 0.5 / 0xFFFFF;
|
||||
rand_dir.z = (float) rand_5 - 0.5 / 0xFFFFF;
|
||||
|
||||
float rand_dir_factor = spread * 2 / 0xFFFFF;
|
||||
float sign = 1;
|
||||
|
||||
float dot = rand_dir.x * n.x + rand_dir.y * n.y + rand_dir.z * n.z;
|
||||
if (dot < 0) sign = -1;
|
||||
|
||||
Vector3 sample_dir;
|
||||
sample_dir.x = sign * rand_dir_factor * rand_dir.x + dir_to_light.x;
|
||||
sample_dir.y = sign * rand_dir_factor * rand_dir.y + dir_to_light.y;
|
||||
sample_dir.z = sign * rand_dir_factor * rand_dir.z + dir_to_light.z;
|
||||
|
||||
Ray sample_ray;
|
||||
float eps = 0.001;
|
||||
sample_ray.direction = sample_dir;
|
||||
sample_ray.origin.x = o.x + eps * sample_dir.x;
|
||||
sample_ray.origin.y = o.y + eps * sample_dir.y;
|
||||
sample_ray.origin.z = o.z + eps * sample_dir.z;
|
||||
|
||||
HitInfo hit2 = trace_ray(sample_ray, &scene);
|
||||
if (hit2.object == light_object - scene.objects) {
|
||||
|
||||
result.x += contrib.x * weighted_light_emission.x / light_distance2;
|
||||
result.y += contrib.y * weighted_light_emission.y / light_distance2;
|
||||
result.z += contrib.z * weighted_light_emission.z / light_distance2;
|
||||
|
||||
contrib.x *= 1 - light_sample_weight;
|
||||
contrib.y *= 1 - light_sample_weight;
|
||||
contrib.z *= 1 - light_sample_weight;
|
||||
}
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
in_ray = out_ray;
|
||||
@@ -350,7 +457,7 @@ PixelInfo pixel(float x, float y, float aspect_ratio)
|
||||
return pixel_inner(in_ray);
|
||||
}
|
||||
|
||||
os_threadreturn worker(void *arg)
|
||||
os_threadreturn ray_tracing_worker(void *arg)
|
||||
{
|
||||
// The actual pixels
|
||||
Vector3 *local_color_buffer = NULL;
|
||||
@@ -500,9 +607,16 @@ void realloc_frame_buffer(void)
|
||||
abort();
|
||||
}
|
||||
|
||||
free(filter_buffer);
|
||||
filter_buffer = malloc(sizeof(Vector3) * frame_w * frame_h);
|
||||
if (!filter_buffer) {
|
||||
free(filter_buffer_A);
|
||||
filter_buffer_A = malloc(sizeof(Vector3) * frame_w * frame_h);
|
||||
if (!filter_buffer_A) {
|
||||
printf("OUT OF MEMORY\n");
|
||||
abort();
|
||||
}
|
||||
|
||||
free(filter_buffer_B);
|
||||
filter_buffer_B = malloc(sizeof(Vector3) * frame_w * frame_h);
|
||||
if (!filter_buffer_B) {
|
||||
printf("OUT OF MEMORY\n");
|
||||
abort();
|
||||
}
|
||||
@@ -530,14 +644,17 @@ static int compare_vector_luminosity(const void *a, const void *b)
|
||||
return -1;
|
||||
}
|
||||
|
||||
void median_filter()
|
||||
void median_filter(Vector3 *src, Vector3 *dst,
|
||||
float *depth_buffer, float *roughness_buffer,
|
||||
Vector3 *normal_buffer, int frame_w, int frame_h,
|
||||
int region_x, int region_w, int region_h)
|
||||
{
|
||||
for (int j = 0; j < frame_h; j++)
|
||||
for (int i = 0; i < frame_w; i++) {
|
||||
for (int j = 0; j < region_h; j++)
|
||||
for (int i = region_x; i - region_x < region_w; i++) {
|
||||
|
||||
int center_pixel_location = j * frame_w + i;
|
||||
|
||||
Vector3 central_color = color_buffer[center_pixel_location];
|
||||
Vector3 center_color = src[center_pixel_location];
|
||||
bool center_roughness = roughness_buffer[center_pixel_location];
|
||||
/*
|
||||
if (center_roughness < 0.8) {
|
||||
@@ -545,7 +662,7 @@ void median_filter()
|
||||
continue;
|
||||
}
|
||||
*/
|
||||
#define WINDOW_SIZE 3
|
||||
#define WINDOW_SIZE 5
|
||||
|
||||
Vector3 samples[WINDOW_SIZE*WINDOW_SIZE];
|
||||
int num_samples = 0;
|
||||
@@ -568,14 +685,14 @@ void median_filter()
|
||||
if (absf(neighbor_roughness - center_roughness) > 0.2)
|
||||
continue;
|
||||
|
||||
samples[num_samples++] = color_buffer[neighbor_pixel_location];
|
||||
samples[num_samples++] = src[neighbor_pixel_location];
|
||||
}
|
||||
|
||||
qsort(samples, num_samples, sizeof(Vector3), compare_vector_luminosity);
|
||||
|
||||
filter_buffer[center_pixel_location] = samples[num_samples/2];
|
||||
dst[center_pixel_location] = samples[num_samples/2];
|
||||
/*
|
||||
float error = (absf(central_color.x - result.x) + absf(central_color.y - result.y) + absf(central_color.z - result.z)) / 3;
|
||||
float error = (absf(center_color.x - result.x) + absf(center_color.y - result.y) + absf(center_color.z - result.z)) / 3;
|
||||
if (error > 0.05)
|
||||
filter_buffer[center_pixel_location] = result;
|
||||
else
|
||||
@@ -585,30 +702,45 @@ void median_filter()
|
||||
//memcpy(color_buffer, filter_buffer, sizeof(Vector3) * frame_w * frame_h);
|
||||
}
|
||||
|
||||
void smooth_filter()
|
||||
void smooth_filter(Vector3 *src, Vector3 *dst,
|
||||
float *depth_buffer, float *roughness_buffer,
|
||||
Vector3 *normal_buffer, int frame_w, int frame_h,
|
||||
int region_x, int region_w, int region_h)
|
||||
{
|
||||
for (int j = 0; j < frame_h; j++)
|
||||
for (int i = 0; i < frame_w; i++) {
|
||||
Vector3 samples = {0, 0, 0};
|
||||
int num_samples = 0;
|
||||
float weight_sum = 0;
|
||||
for (int j = 0; j < region_h; j++)
|
||||
for (int i = region_x; i - region_x < region_w; i++) {
|
||||
|
||||
int center_pixel_location = j * frame_w + i;
|
||||
|
||||
Vector3 central_color = color_buffer[center_pixel_location];
|
||||
Vector3 center_color = src[center_pixel_location];
|
||||
bool center_roughness = roughness_buffer[center_pixel_location];
|
||||
/*
|
||||
if (center_roughness < 0.8) {
|
||||
filter_buffer[center_pixel_location] = color_buffer[center_pixel_location];
|
||||
continue;
|
||||
}
|
||||
*/
|
||||
int window_size = 9;
|
||||
for (int u = 0; u < window_size; u++)
|
||||
for (int v = 0; v < window_size; v++) {
|
||||
|
||||
int g = j + u - window_size / 2;
|
||||
int t = i + v - window_size / 2;
|
||||
float min_distance = 1;
|
||||
float max_distance = 8;
|
||||
float distance = clamp(depth_buffer[center_pixel_location], min_distance, max_distance) / max_distance;
|
||||
float sigma = 0.5 + (0.5 + 10 * center_roughness) / distance;
|
||||
|
||||
// roughness=0, distance=0 -> sigma=0.5
|
||||
// roughness=1, distance=0 -> sigma=10
|
||||
// roughness=0, distance=15 -> sigma=0.5
|
||||
// roughness=1, distance=15 -> sigma=0.5
|
||||
|
||||
//int min_winsize = 5;
|
||||
//int max_winsize = 15;
|
||||
//int winsize = (min_winsize + center_roughness * (max_winsize - min_winsize)) | 1;
|
||||
|
||||
int winsize = 5;
|
||||
|
||||
#define MAX_WINSIZE 32
|
||||
Vector3 samples[MAX_WINSIZE * MAX_WINSIZE];
|
||||
float weights[MAX_WINSIZE * MAX_WINSIZE];
|
||||
int num_samples = 0;
|
||||
|
||||
for (int u = 0; u < winsize; u++)
|
||||
for (int v = 0; v < winsize; v++) {
|
||||
|
||||
int g = j + u - winsize / 2;
|
||||
int t = i + v - winsize / 2;
|
||||
|
||||
if (g < 0 || t < 0 || t >= frame_w || g >= frame_h)
|
||||
continue;
|
||||
@@ -625,36 +757,66 @@ void smooth_filter()
|
||||
Vector3 center_normal = normal_buffer[center_pixel_location];
|
||||
Vector3 neighbor_normal = normal_buffer[neighbor_pixel_location];
|
||||
float simil = dotv(center_normal, neighbor_normal);
|
||||
if (simil < 0.9)
|
||||
if (simil < 0.8)
|
||||
continue;
|
||||
float weight;
|
||||
if (t == i && g == j)
|
||||
weight = 1;
|
||||
else
|
||||
weight = maxf(center_roughness, 0.01);
|
||||
|
||||
samples.x += weight * color_buffer[neighbor_pixel_location].x;
|
||||
samples.y += weight * color_buffer[neighbor_pixel_location].y;
|
||||
samples.z += weight * color_buffer[neighbor_pixel_location].z;
|
||||
float weight = exp(- (float) ((t - i)*(t - i) + (g - j)*(g - j)) / (2 * sigma * sigma));
|
||||
|
||||
weight_sum += weight;
|
||||
weights[num_samples] = weight;
|
||||
samples[num_samples] = src[neighbor_pixel_location];
|
||||
num_samples++;
|
||||
}
|
||||
|
||||
Vector3 result;
|
||||
result.x = samples.x / weight_sum;
|
||||
result.y = samples.y / weight_sum;
|
||||
result.z = samples.z / weight_sum;
|
||||
|
||||
filter_buffer[center_pixel_location] = result;
|
||||
int head = 0;
|
||||
/*
|
||||
float error = (absf(central_color.x - result.x) + absf(central_color.y - result.y) + absf(central_color.z - result.z)) / 3;
|
||||
if (error > 0.05)
|
||||
filter_buffer[center_pixel_location] = result;
|
||||
else
|
||||
filter_buffer[center_pixel_location] = color_buffer[center_pixel_location];
|
||||
*/
|
||||
if (num_samples > 2) {
|
||||
qsort(samples, num_samples, sizeof(Vector3), compare_vector_luminosity);
|
||||
head++;
|
||||
num_samples -= 2;
|
||||
}
|
||||
*/
|
||||
Vector3 result = {0, 0, 0};
|
||||
float weight_sum = 0;
|
||||
for (int i = head; i < num_samples; i++) {
|
||||
result.x += weights[i] * samples[i].x;
|
||||
result.y += weights[i] * samples[i].y;
|
||||
result.z += weights[i] * samples[i].z;
|
||||
weight_sum += weights[i];
|
||||
}
|
||||
|
||||
result.x /= weight_sum;
|
||||
result.y /= weight_sum;
|
||||
result.z /= weight_sum;
|
||||
|
||||
dst[center_pixel_location] = result;
|
||||
}
|
||||
}
|
||||
|
||||
void clamp_filter(Vector3 *src, Vector3 *dst,
|
||||
float *depth_buffer, float *roughness_buffer,
|
||||
Vector3 *normal_buffer, int frame_w, int frame_h,
|
||||
int region_x, int region_w, int region_h)
|
||||
{
|
||||
for (int j = 0; j < region_h; j++)
|
||||
for (int i = region_x; i - region_x < region_w; i++) {
|
||||
dst[j * frame_w + i].x = clamp(src[j * frame_w + i].x, 0, 1);
|
||||
dst[j * frame_w + i].y = clamp(src[j * frame_w + i].y, 0, 1);
|
||||
dst[j * frame_w + i].z = clamp(src[j * frame_w + i].z, 0, 1);
|
||||
|
||||
#if 1
|
||||
// HDR tonemapping
|
||||
dst[j * frame_w + i].x = dst[j * frame_w + i].x / (dst[j * frame_w + i].x + 1);
|
||||
dst[j * frame_w + i].y = dst[j * frame_w + i].y / (dst[j * frame_w + i].y + 1);
|
||||
dst[j * frame_w + i].z = dst[j * frame_w + i].z / (dst[j * frame_w + i].z + 1);
|
||||
#endif
|
||||
|
||||
#if 0
|
||||
// gamma correct
|
||||
dst[j * frame_w + i].x = pow(dst[j * frame_w + i].x, 1.0/2.2);
|
||||
dst[j * frame_w + i].y = pow(dst[j * frame_w + i].y, 1.0/2.2);
|
||||
dst[j * frame_w + i].z = pow(dst[j * frame_w + i].z, 1.0/2.2);
|
||||
#endif
|
||||
}
|
||||
memcpy(color_buffer, filter_buffer, sizeof(Vector3) * frame_w * frame_h);
|
||||
}
|
||||
|
||||
uint64_t start_time_ns;
|
||||
@@ -664,12 +826,10 @@ void update_frame(void)
|
||||
{
|
||||
uint64_t frame_start = __rdtsc();
|
||||
|
||||
os_mutex_lock(&frame_mutex);
|
||||
|
||||
if (frame_buffer_size_doesnt_match_window())
|
||||
realloc_frame_buffer();
|
||||
|
||||
int column_w = frame_w / num_columns;
|
||||
os_mutex_lock(&frame_mutex);
|
||||
|
||||
completed = 0;
|
||||
global_frame_index++;
|
||||
@@ -679,38 +839,39 @@ void update_frame(void)
|
||||
while (completed < num_columns)
|
||||
os_condvar_wait(&completed_work, &frame_mutex, -1);
|
||||
|
||||
os_mutex_unlock(&frame_mutex);
|
||||
|
||||
color_buffer_frames++;
|
||||
|
||||
bool smooth = false;
|
||||
if (smooth_index == smooth_limit) {
|
||||
smooth = true;
|
||||
smooth_index = 0;
|
||||
smooth_limit++;
|
||||
smooth_limit = (smooth_limit + 1) * SMOOTH_RATIO;
|
||||
printf("smoothing!\n");
|
||||
}
|
||||
smooth_index++;
|
||||
|
||||
if (smooth) {
|
||||
smooth_filter();
|
||||
for (int i = 0; i < frame_w * frame_h; i++) {
|
||||
filter_buffer[i].x = clamp(filter_buffer[i].x, 0, 1);
|
||||
filter_buffer[i].y = clamp(filter_buffer[i].y, 0, 1);
|
||||
filter_buffer[i].z = clamp(filter_buffer[i].z, 0, 1);
|
||||
}
|
||||
#if 1
|
||||
filter(color_buffer, filter_buffer_B, smooth_filter);
|
||||
memcpy(color_buffer, filter_buffer_B, frame_w * frame_h * sizeof(Vector3));
|
||||
filter(filter_buffer_B, filter_buffer_A, clamp_filter);
|
||||
#else
|
||||
filter(color_buffer, filter_buffer_A, median_filter);
|
||||
filter(filter_buffer_A, filter_buffer_B, smooth_filter);
|
||||
memcpy(color_buffer, filter_buffer_B, frame_w * frame_h * sizeof(Vector3));
|
||||
filter(filter_buffer_B, filter_buffer_A, clamp_filter);
|
||||
#endif
|
||||
} else {
|
||||
for (int i = 0; i < frame_w * frame_h; i++) {
|
||||
filter_buffer[i].x = clamp(color_buffer[i].x, 0, 1);
|
||||
filter_buffer[i].y = clamp(color_buffer[i].y, 0, 1);
|
||||
filter_buffer[i].z = clamp(color_buffer[i].z, 0, 1);
|
||||
}
|
||||
filter(color_buffer, filter_buffer_A, clamp_filter);
|
||||
}
|
||||
|
||||
move_frame_to_the_gpu(frame_w, frame_h, filter_buffer);
|
||||
move_frame_to_the_gpu(frame_w, frame_h, filter_buffer_A);
|
||||
//move_frame_to_the_gpu(frame_w, frame_h, normal_buffer);
|
||||
|
||||
uint64_t cycles_per_pixel = global_cycle_pixel_sum / global_pixel_count;
|
||||
|
||||
os_mutex_unlock(&frame_mutex);
|
||||
|
||||
uint64_t cycles_per_frame = __rdtsc() - frame_start;
|
||||
|
||||
uint64_t current_time_cycles = __rdtsc();
|
||||
@@ -721,6 +882,8 @@ void update_frame(void)
|
||||
printf("frame -> %llu cycles (%f ns)\n", cycles_per_frame, cycles_per_frame * cy2ns);
|
||||
}
|
||||
|
||||
void print_camera(void);
|
||||
|
||||
int main(int argc, char **argv)
|
||||
{
|
||||
fprintf(stderr, "Started\n");
|
||||
@@ -768,11 +931,12 @@ int main(int argc, char **argv)
|
||||
}
|
||||
#endif
|
||||
|
||||
startup_window_and_opengl_context_or_exit(2 * 640, 2 * 480, "Ray Tracing");
|
||||
startup_window_and_opengl_context_or_exit(256*4, 256*4, "Ray Tracing");
|
||||
|
||||
fprintf(stderr, "Started windows and opengl context\n");
|
||||
|
||||
start_workers();
|
||||
start_ray_tracing_workers();
|
||||
start_filtering_workers();
|
||||
|
||||
fprintf(stderr, "Workers started\n");
|
||||
|
||||
@@ -781,6 +945,8 @@ int main(int argc, char **argv)
|
||||
|
||||
for (bool exit = false; !exit; ) {
|
||||
|
||||
//print_camera();
|
||||
|
||||
for (;;) {
|
||||
|
||||
double mouse_x;
|
||||
@@ -838,7 +1004,8 @@ int main(int argc, char **argv)
|
||||
// Tell workers to stop evaluating frames
|
||||
invalidate_accumulation();
|
||||
|
||||
stop_workers();
|
||||
stop_filtering_workers();
|
||||
stop_ray_tracing_workers();
|
||||
free_cubemap(&skybox);
|
||||
cleanup_window_and_opengl_context();
|
||||
return 0;
|
||||
@@ -914,9 +1081,9 @@ void screenshot(void)
|
||||
fprintf(stderr, "Couldn't take screenshot (out of memory)\n");
|
||||
}
|
||||
for (int i = 0; i < frame_w * frame_h; i++) {
|
||||
converted[i * 3 + 0] = filter_buffer[i].x * 255;
|
||||
converted[i * 3 + 1] = filter_buffer[i].y * 255;
|
||||
converted[i * 3 + 2] = filter_buffer[i].z * 255;
|
||||
converted[i * 3 + 0] = filter_buffer_A[i].x * 255;
|
||||
converted[i * 3 + 1] = filter_buffer_A[i].y * 255;
|
||||
converted[i * 3 + 2] = filter_buffer_A[i].z * 255;
|
||||
}
|
||||
|
||||
stbi_flip_vertically_on_write(1);
|
||||
@@ -942,7 +1109,7 @@ bool quitting(void)
|
||||
return workers_should_stop;
|
||||
}
|
||||
|
||||
void start_workers(void)
|
||||
void start_ray_tracing_workers(void)
|
||||
{
|
||||
workers_should_stop = false;
|
||||
|
||||
@@ -952,10 +1119,10 @@ void start_workers(void)
|
||||
os_condvar_create(&completed_work);
|
||||
|
||||
for (int i = 0; i < num_columns; i++)
|
||||
os_thread_create(&workers[i], (void*) i, worker);
|
||||
os_thread_create(&workers[i], (void*) i, ray_tracing_worker);
|
||||
}
|
||||
|
||||
void stop_workers(void)
|
||||
void stop_ray_tracing_workers(void)
|
||||
{
|
||||
os_mutex_lock(&frame_mutex);
|
||||
workers_should_stop = true;
|
||||
@@ -968,3 +1135,108 @@ void stop_workers(void)
|
||||
os_condvar_delete(&start_work);
|
||||
os_condvar_delete(&completed_work);
|
||||
}
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
bool filtering_workers_should_stop;
|
||||
FilterFunc filtering_func;
|
||||
Vector3 *filtering_input;
|
||||
Vector3 *filtering_output;
|
||||
int filtering_completed;
|
||||
os_mutex_t filtering_mutex;
|
||||
os_condvar_t start_filtering;
|
||||
os_condvar_t completed_filtering;
|
||||
uint64_t global_filter_index = 0;
|
||||
os_thread filtering_workers[MAX_COLUMNS];
|
||||
|
||||
os_threadreturn filtering_worker(void *arg);
|
||||
|
||||
void start_filtering_workers(void)
|
||||
{
|
||||
filtering_workers_should_stop = false;
|
||||
|
||||
os_mutex_create(&filtering_mutex);
|
||||
|
||||
os_condvar_create(&start_filtering);
|
||||
os_condvar_create(&completed_filtering);
|
||||
|
||||
for (int i = 0; i < num_columns; i++)
|
||||
os_thread_create(&filtering_workers[i], (void*) i, filtering_worker);
|
||||
}
|
||||
|
||||
void stop_filtering_workers(void)
|
||||
{
|
||||
os_mutex_lock(&filtering_mutex);
|
||||
filtering_workers_should_stop = true;
|
||||
for (int i = 0; i < num_columns; i++)
|
||||
os_condvar_signal(&start_filtering);
|
||||
os_mutex_unlock(&filtering_mutex);
|
||||
for (int i = 0; i < num_columns; i++)
|
||||
os_thread_join(workers[i]);
|
||||
|
||||
os_condvar_delete(&start_work);
|
||||
os_condvar_delete(&completed_work);
|
||||
}
|
||||
|
||||
void filter(Vector3 *input, Vector3 *output, FilterFunc func)
|
||||
{
|
||||
filtering_func = func;
|
||||
filtering_input = input;
|
||||
filtering_output = output;
|
||||
|
||||
os_mutex_lock(&filtering_mutex);
|
||||
|
||||
filtering_completed = 0;
|
||||
global_filter_index++;
|
||||
for (int i = 0; i < num_columns; i++)
|
||||
os_condvar_signal(&start_filtering);
|
||||
|
||||
while (filtering_completed < num_columns)
|
||||
os_condvar_wait(&completed_filtering, &filtering_mutex, -1);
|
||||
|
||||
os_mutex_unlock(&filtering_mutex);
|
||||
}
|
||||
|
||||
os_threadreturn filtering_worker(void *arg)
|
||||
{
|
||||
int column_i = (int) arg;
|
||||
|
||||
uint64_t local_filter_index = 0;
|
||||
|
||||
os_mutex_lock(&filtering_mutex);
|
||||
for (;;) {
|
||||
|
||||
while (local_filter_index == global_filter_index && !filtering_workers_should_stop)
|
||||
os_condvar_wait(&start_filtering, &filtering_mutex, -1);
|
||||
local_filter_index = global_filter_index;
|
||||
|
||||
if (filtering_workers_should_stop) {
|
||||
filtering_completed++;
|
||||
os_condvar_signal(&completed_filtering);
|
||||
break;
|
||||
}
|
||||
|
||||
os_mutex_unlock(&filtering_mutex);
|
||||
|
||||
int column_w = frame_w / num_columns;
|
||||
int column_x = column_w * column_i;
|
||||
|
||||
filtering_func(
|
||||
filtering_input,
|
||||
filtering_output,
|
||||
depth_buffer,
|
||||
roughness_buffer,
|
||||
normal_buffer,
|
||||
frame_w,
|
||||
frame_h,
|
||||
column_x,
|
||||
column_w,
|
||||
frame_h);
|
||||
|
||||
// Now we try publishing the changes
|
||||
os_mutex_lock(&filtering_mutex);
|
||||
filtering_completed++;
|
||||
os_condvar_signal(&completed_filtering);
|
||||
}
|
||||
os_mutex_unlock(&filtering_mutex);
|
||||
}
|
||||
Reference in New Issue
Block a user