diff --git a/src/refresh/vkpt/textures.c b/src/refresh/vkpt/textures.c index b9844b930..46bec8160 100644 --- a/src/refresh/vkpt/textures.c +++ b/src/refresh/vkpt/textures.c @@ -629,6 +629,102 @@ static void filter_float_image(float* pixels, int num_comps, const float kernel[ filter_one_dimension_float(pixels, num_comps, kernel, kernel_size, height, width, 1, width); } +struct bilerp_s +{ + int current_output_row; + float *current_input_data; + float *next_input_data; + float *output_data; +}; + +static void bilerp_init(struct bilerp_s* bilerp, int input_w) +{ + bilerp->current_output_row = -1; + bilerp->current_input_data = IMG_AllocPixels(input_w * sizeof(float) * 3); + bilerp->next_input_data = IMG_AllocPixels(input_w * sizeof(float) * 3); + bilerp->output_data = IMG_AllocPixels(input_w * 2 * sizeof(float) * 3); +} + +static void bilerp_free(struct bilerp_s* bilerp) +{ + Z_Free(bilerp->current_input_data); + Z_Free(bilerp->next_input_data); + Z_Free(bilerp->output_data); +} + +static inline void _bilerp_get_next_output_line(struct bilerp_s *bilerp, const float** output_line, const float* next_input, int input_w) +{ + if(bilerp->current_output_row == -1) { + memcpy(bilerp->next_input_data, next_input, input_w * sizeof(float) * 3); + bilerp->current_output_row = 0; + } + + if((bilerp->current_output_row & 1) == 0) { + // Even output line: use input lines + // Swap next_input_data into current_input_data + float *tmp = bilerp->next_input_data; + bilerp->next_input_data = bilerp->current_input_data; + bilerp->current_input_data = tmp; + } else { + // Odd output line: interpolate between input lines + float *color_dest = bilerp->next_input_data; + memcpy(bilerp->next_input_data, next_input, input_w * sizeof(float) * 3); + + float *color_ptr = bilerp->current_input_data; + float *next_color_ptr = bilerp->next_input_data; + for (int x = 0; x < input_w; x++) { + color_ptr[0] = (color_ptr[0] + next_color_ptr[0]) * 0.5f; + color_ptr[1] = (color_ptr[1] + next_color_ptr[1]) * 0.5f; + color_ptr[2] = (color_ptr[2] + next_color_ptr[2]) * 0.5f; + color_ptr += 3; + next_color_ptr += 3; + } + } + + float *out_ptr = bilerp->output_data; + float color[3]; + const float* color_ptr = bilerp->current_input_data; + for (int out_x = 0; out_x < input_w * 2 - 1; out_x++) { + if((out_x & 1) == 0) { + // Even output row: direct value + memcpy(color, color_ptr, 3 * sizeof(float)); + } else { + // Odd output row: interpolate between colors + color_ptr += 3; + color[0] = (color[0] + color_ptr[0]) * 0.5f; + color[1] = (color[1] + color_ptr[1]) * 0.5f; + color[2] = (color[2] + color_ptr[2]) * 0.5f; + } + memcpy(out_ptr, color, 3 * sizeof(float)); + out_ptr += 3; + } + + // Last row: interpolate between last and first pixel + color[0] = (color_ptr[0] + bilerp->current_input_data[0]) * 0.5f; + color[1] = (color_ptr[1] + bilerp->current_input_data[1]) * 0.5f; + color[2] = (color_ptr[2] + bilerp->current_input_data[2]) * 0.5f; + memcpy(out_ptr, color, 3 * sizeof(float)); + + *output_line = bilerp->output_data; + + bilerp->current_output_row++; +} + +static inline void bilerp_get_next_output_line_from_rgb_f32(struct bilerp_s *bilerp, const float** output_line, const float* input_data, int input_w, int input_h) +{ + const float *next_input = NULL; + if (bilerp->current_output_row == -1) { + next_input = input_data; + } else if ((bilerp->current_output_row & 1) != 0) { + int in_y = (bilerp->current_output_row + 1) >> 1; + // Wraparound last line + if(in_y >= input_h) + in_y = 0; + next_input = input_data + in_y * input_w * 3; + } + _bilerp_get_next_output_line(bilerp, output_line, next_input, input_w); +} + // Fake an emissive texture from a diffuse texture by using pixels brighter than a certain amount static void apply_fake_emissive_threshold(image_t *image) { @@ -677,7 +773,11 @@ static void apply_fake_emissive_threshold(image_t *image) // ...and use it to normalize max luminance to 1 float lum_scale = max_lum > 0 ? 1.0f / max_lum : 1.0f; - // Combine blurred "bright" mask with original image (to retain some colorization) + /* Combine blurred "bright" mask with original image (to retain some colorization). + Produce float output for upsampling pass */ + float *final = IMG_AllocPixels(w * h * 3 * sizeof(float)); + + float *out_final = final; current_bright_mask = bright_mask; byte *current_img_pixel = image->pix_data; for (int y = 0; y < h; y++) { @@ -694,20 +794,59 @@ static void apply_fake_emissive_threshold(image_t *image) float src_lum = LUMINANCE(color_img[0], color_img[1], color_img[2]); src_lum *= src_lum; float scale = *current_bright_mask * src_lum * lum_scale; - color_img[0] *= scale; - color_img[1] *= scale; - color_img[2] *= scale; - - current_img_pixel[0] = encode_srgb(color_img[0]); - current_img_pixel[1] = encode_srgb(color_img[1]); - current_img_pixel[2] = encode_srgb(color_img[2]); + out_final[0] = color_img[0] * scale; + out_final[1] = color_img[1] * scale; + out_final[2] = color_img[2] * scale; + out_final += 3; current_bright_mask++; current_img_pixel += 4; } } - Z_Free(bright_mask); + + // Interpolate final image to 2x size, apply a mild filter, to have it look less blocky + int width_2x = w * 2; + int height_2x = h * 2; + float *final_2x = IMG_AllocPixels(width_2x * height_2x * 3 * sizeof(float)); + + struct bilerp_s bilerp_final; + bilerp_init(&bilerp_final, w); + float *out_final_2x = final_2x; + for (int out_y = 0; out_y < height_2x; out_y++) { + float *img_line; + bilerp_get_next_output_line_from_rgb_f32(&bilerp_final, &img_line, final, w, h); + memcpy(out_final_2x, img_line, width_2x * 3 * sizeof(float)); + out_final_2x += width_2x * 3; + } + bilerp_free(&bilerp_final); + Z_Free(final); + + const float filter_final[] = { 0.157731, 0.684538, 0.157731 }; + filter_float_image(final_2x, 3, filter_final, sizeof(filter_final) / sizeof(filter_final[0]), width_2x, height_2x); + + // Final -> SRGB + int new_size = width_2x * height_2x * 4; + Z_Free(image->pix_data); + image->pix_data = IMG_AllocPixels(new_size); + image->upload_width = width_2x; + image->upload_height = height_2x; + + float* current_pixel = final_2x; + byte *out_pixel = image->pix_data; + for (int y = 0; y < height_2x; y++) { + for (int x = 0; x < width_2x; x++) { + out_pixel[0] = encode_srgb(current_pixel[0]); + out_pixel[1] = encode_srgb(current_pixel[1]); + out_pixel[2] = encode_srgb(current_pixel[2]); + out_pixel[3] = 255; + + current_pixel += 3; + out_pixel += 4; + } + } + + Z_Free(final_2x); } image_t *vkpt_fake_emissive_texture(image_t *image)