From ee44dd1b2bcff6bbd99084a012a40fb4a7100b2c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Cl=C3=A9ment=20Foucault?= Date: Mon, 19 Nov 2018 01:01:43 +0100 Subject: Studio Lights: Big Cleanups * Less Lengthy enum/macro names. * Optimize computation of Spherical Harmonics. * Reduce radiance cubemap size a bit. Higher resolution is not necessary. * Remove STUDIOLIGHT_LIGHT_DIRECTION_CALCULATED (was not used). * Do windowing on each component separately instead of using luminance. * Use ITER_PIXELS to iterate on each pixels, using pixel center coords. * Remove gpu_matcap_3components as it is only needed when creating the gputex. * Fix a lot of confusion in axis denomination/swizzle. These changes should not affect functionallity. --- source/blender/blenkernel/intern/studiolight.c | 930 ++++++++++++------------- 1 file changed, 428 insertions(+), 502 deletions(-) (limited to 'source/blender/blenkernel/intern/studiolight.c') diff --git a/source/blender/blenkernel/intern/studiolight.c b/source/blender/blenkernel/intern/studiolight.c index 0aeb6d78d44..d04c14299dc 100644 --- a/source/blender/blenkernel/intern/studiolight.c +++ b/source/blender/blenkernel/intern/studiolight.c @@ -58,27 +58,23 @@ /* Statics */ static ListBase studiolights; static int last_studiolight_id = 0; -#define STUDIOLIGHT_RADIANCE_CUBEMAP_SIZE 128 -#define STUDIOLIGHT_IRRADIANCE_EQUIRECTANGULAR_HEIGHT 32 -#define STUDIOLIGHT_IRRADIANCE_EQUIRECTANGULAR_WIDTH (STUDIOLIGHT_IRRADIANCE_EQUIRECTANGULAR_HEIGHT * 2) +#define STUDIOLIGHT_RADIANCE_CUBEMAP_SIZE 96 +#define STUDIOLIGHT_IRRADIANCE_EQUIRECT_HEIGHT 32 +#define STUDIOLIGHT_IRRADIANCE_EQUIRECT_WIDTH (STUDIOLIGHT_IRRADIANCE_EQUIRECT_HEIGHT * 2) -#define STUDIOLIGHT_IRRADIANCE_METHOD_RADIANCE 0 -#define STUDIOLIGHT_IRRADIANCE_METHOD_SPHERICAL_HARMONICS 1 /* * The method to calculate the irradiance buffers * The irradiance buffer is only shown in the background when in LookDev. * * STUDIOLIGHT_IRRADIANCE_METHOD_RADIANCE is very slow, but very accurate * STUDIOLIGHT_IRRADIANCE_METHOD_SPHERICAL_HARMONICS is faster but has artifacts + * Cannot have both enabled at the same time!!! */ -// #define STUDIOLIGHT_IRRADIANCE_METHOD STUDIOLIGHT_IRRADIANCE_METHOD_RADIANCE -#define STUDIOLIGHT_IRRADIANCE_METHOD STUDIOLIGHT_IRRADIANCE_METHOD_SPHERICAL_HARMONICS +// #define STUDIOLIGHT_IRRADIANCE_METHOD_RADIANCE +#define STUDIOLIGHT_IRRADIANCE_METHOD_SPHERICAL_HARMONICS -#if 0 /* Temporarily disabled due to the creation of textures with -nan(ind)s */ -#if STUDIOLIGHT_SPHERICAL_HARMONICS_LEVEL == 2 -# define STUDIOLIGHT_SPHERICAL_HARMONICS_WINDOWING -#endif -#endif +/* Temporarily disabled due to the creation of textures with -nan(ind)s */ +#define STUDIOLIGHT_SH_WINDOWING 0.0f /* 0.0 is disabled */ /* * Disable this option so caches are not loaded from disk @@ -90,6 +86,34 @@ static const char *STUDIOLIGHT_CAMERA_FOLDER = "studiolights/camera/"; static const char *STUDIOLIGHT_WORLD_FOLDER = "studiolights/world/"; static const char *STUDIOLIGHT_MATCAP_FOLDER = "studiolights/matcap/"; +/* ITER MACRO */ + +/** Iter on all pixel giving texel center position and pixel pointer. + * Arguments + * type : type of src. + * src : source buffer. + * channels : number of channels per pixel. + * + * Others + * x, y : normalized UV coordinate [0..1] of the current pixel center. + * texel_size[2] : UV size of a pixel in this texture. + * pixel[] : pointer to the current pixel. + */ +#define ITER_PIXELS(type, src, channels, width, height) \ +{ \ + float texel_size[2]; \ + texel_size[0] = 1.0f / width; \ + texel_size[1] = 1.0f / height; \ + type (*pixel_)[channels] = (type (*)[channels])src; \ + for (float y = 0.5 * texel_size[1]; y < 1.0; y += texel_size[1]) { \ + for (float x = 0.5 * texel_size[0]; x < 1.0; x += texel_size[0], pixel_++) { \ + type *pixel = *pixel_; + +#define ITER_PIXELS_END \ + } \ + } \ +} ((void)0) + /* FUNCTIONS */ #define IMB_SAFE_FREE(p) do { \ if (p) { \ @@ -125,13 +149,12 @@ static void studiolight_free(struct StudioLight *sl) for (int index = 0; index < 6; index++) { IMB_SAFE_FREE(sl->radiance_cubemap_buffers[index]); } - GPU_TEXTURE_SAFE_FREE(sl->equirectangular_radiance_gputexture); - GPU_TEXTURE_SAFE_FREE(sl->equirectangular_irradiance_gputexture); - IMB_SAFE_FREE(sl->equirectangular_radiance_buffer); - IMB_SAFE_FREE(sl->equirectangular_irradiance_buffer); + GPU_TEXTURE_SAFE_FREE(sl->equirect_radiance_gputexture); + GPU_TEXTURE_SAFE_FREE(sl->equirect_irradiance_gputexture); + IMB_SAFE_FREE(sl->equirect_radiance_buffer); + IMB_SAFE_FREE(sl->equirect_irradiance_buffer); MEM_SAFE_FREE(sl->path_irr_cache); MEM_SAFE_FREE(sl->path_sh_cache); - MEM_SAFE_FREE(sl->gpu_matcap_3components); MEM_SAFE_FREE(sl); } @@ -161,13 +184,13 @@ static struct StudioLight *studiolight_create(int flag) return sl; } -static void direction_to_equirectangular(float r[2], const float dir[3]) +static void direction_to_equirect(float r[2], const float dir[3]) { r[0] = (atan2f(dir[1], dir[0]) - M_PI) / -(M_PI * 2); r[1] = (acosf(dir[2] / 1.0) - M_PI) / -M_PI; } -static void equirectangular_to_direction(float r[3], float u, float v) +static void equirect_to_direction(float r[3], float u, float v) { float phi = (-(M_PI * 2)) * u + M_PI; float theta = -M_PI * v + M_PI; @@ -177,38 +200,47 @@ static void equirectangular_to_direction(float r[3], float u, float v) r[2] = cosf(theta); } -static void studiolight_calculate_radiance(ImBuf *ibuf, float color[4], const float direction[3]) +static void direction_to_cube_face_uv(float r_uv[2], int *r_face, const float dir[3]) { - float uv[2]; - direction_to_equirectangular(uv, direction); - nearest_interpolation_color_wrap(ibuf, NULL, color, uv[0] * ibuf->x, uv[1] * ibuf->y); + if (fabsf(dir[0]) > fabsf(dir[1]) && fabsf(dir[0]) > fabsf(dir[2])) { + bool is_pos = (dir[0] > 0.0f); + *r_face = is_pos ? STUDIOLIGHT_X_POS : STUDIOLIGHT_X_NEG; + r_uv[0] = dir[2] / fabsf(dir[0]) * (is_pos ? 1 : -1); + r_uv[1] = dir[1] / fabsf(dir[0]) * (is_pos ? -1 : -1); + } + else if (fabsf(dir[1]) > fabsf(dir[0]) && fabsf(dir[1]) > fabsf(dir[2])) { + bool is_pos = (dir[1] > 0.0f); + *r_face = is_pos ? STUDIOLIGHT_Y_POS : STUDIOLIGHT_Y_NEG; + r_uv[0] = dir[0] / fabsf(dir[1]) * (is_pos ? 1 : 1); + r_uv[1] = dir[2] / fabsf(dir[1]) * (is_pos ? -1 : 1); + } + else { + bool is_pos = (dir[2] > 0.0f); + *r_face = is_pos ? STUDIOLIGHT_Z_NEG : STUDIOLIGHT_Z_POS; + r_uv[0] = dir[0] / fabsf(dir[2]) * (is_pos ? -1 : 1); + r_uv[1] = dir[1] / fabsf(dir[2]) * (is_pos ? -1 : -1); + } + r_uv[0] = r_uv[0] * 0.5f + 0.5f; + r_uv[1] = r_uv[1] * 0.5f + 0.5f; } -static void studiolight_calculate_radiance_buffer( - ImBuf *ibuf, float *colbuf, - const float start_x, const float add_x, - const float start_y, const float add_y, const float z, - const int index_x, const int index_y, const int index_z) +static void cube_face_uv_to_direction(float r_dir[3], float x, float y, int face) { - float direction[3]; - float yf = start_y; - float xf; - float *color = colbuf; - - for (int y = 0; y < STUDIOLIGHT_RADIANCE_CUBEMAP_SIZE; y++, yf += add_y) { - xf = start_x; - for (int x = 0; x < STUDIOLIGHT_RADIANCE_CUBEMAP_SIZE; x++, xf += add_x) { - direction[index_x] = xf; - direction[index_y] = yf; - direction[index_z] = z; - normalize_v3(direction); - studiolight_calculate_radiance(ibuf, color, direction); - color += 4; - } - } + const float conversion_matrices[6][3][3] = { + {{ 0.0f, 0.0f, 1.0f}, {0.0f, -1.0f, 0.0f}, { 1.0f, 0.0f, 0.0f}}, + {{ 0.0f, 0.0f, -1.0f}, {0.0f, -1.0f, 0.0f}, {-1.0f, 0.0f, 0.0f}}, + {{ 1.0f, 0.0f, 0.0f}, {0.0f, 0.0f, -1.0f}, { 0.0f, 1.0f, 0.0f}}, + {{ 1.0f, 0.0f, 0.0f}, {0.0f, 0.0f, 1.0f}, { 0.0f, -1.0f, 0.0f}}, + {{ 1.0f, 0.0f, 0.0f}, {0.0f, -1.0f, 0.0f}, { 0.0f, 0.0f, -1.0f}}, + {{-1.0f, 0.0f, 0.0f}, {0.0f, -1.0f, 0.0f}, { 0.0f, 0.0f, 1.0f}} + }; + + copy_v3_fl3(r_dir, x * 2.0f - 1.0f, y * 2.0f - 1.0f, 1.0f); + mul_m3_v3(conversion_matrices[face], r_dir); + normalize_v3(r_dir); } -static void studiolight_load_equirectangular_image(StudioLight *sl) +static void studiolight_load_equirect_image(StudioLight *sl) { if (sl->flag & STUDIOLIGHT_EXTERNAL_FILE) { ImBuf *ibuf = NULL; @@ -219,98 +251,123 @@ static void studiolight_load_equirectangular_image(StudioLight *sl) ibuf = IMB_allocFromBuffer(NULL, colbuf, 1, 1); } IMB_float_from_rect(ibuf); - sl->equirectangular_radiance_buffer = ibuf; + sl->equirect_radiance_buffer = ibuf; } sl->flag |= STUDIOLIGHT_EXTERNAL_IMAGE_LOADED; } -static void studiolight_create_equirectangular_radiance_gputexture(StudioLight *sl) +static void studiolight_create_equirect_radiance_gputexture(StudioLight *sl) { if (sl->flag & STUDIOLIGHT_EXTERNAL_FILE) { char error[256]; BKE_studiolight_ensure_flag(sl, STUDIOLIGHT_EXTERNAL_IMAGE_LOADED); - ImBuf *ibuf = sl->equirectangular_radiance_buffer; + ImBuf *ibuf = sl->equirect_radiance_buffer; if (sl->flag & STUDIOLIGHT_ORIENTATION_VIEWNORMAL) { - sl->gpu_matcap_3components = MEM_callocN(sizeof(float[3]) * ibuf->x * ibuf->y, __func__); - - float *offset4 = ibuf->rect_float; - float *offset3 = sl->gpu_matcap_3components; - for (int i = 0; i < ibuf->x * ibuf->y; i++) { - copy_v3_v3(offset3, offset4); - offset3 += 3; - offset4 += 4; + float *gpu_matcap_3components = MEM_callocN(sizeof(float[3]) * ibuf->x * ibuf->y, __func__); + + float (*offset4)[4] = (float (*)[4])ibuf->rect_float; + float (*offset3)[3] = (float (*)[3])gpu_matcap_3components; + for (int i = 0; i < ibuf->x * ibuf->y; i++, offset4++, offset3++) { + copy_v3_v3(*offset3, *offset4); } - sl->equirectangular_radiance_gputexture = GPU_texture_create_nD( - ibuf->x, ibuf->y, 0, 2, sl->gpu_matcap_3components, GPU_R11F_G11F_B10F, GPU_DATA_FLOAT, 0, false, error); + + sl->equirect_radiance_gputexture = GPU_texture_create_nD( + ibuf->x, ibuf->y, 0, 2, gpu_matcap_3components, GPU_R11F_G11F_B10F, GPU_DATA_FLOAT, 0, false, error); + + MEM_SAFE_FREE(gpu_matcap_3components); } else { - sl->equirectangular_radiance_gputexture = GPU_texture_create_2D( + sl->equirect_radiance_gputexture = GPU_texture_create_2D( ibuf->x, ibuf->y, GPU_RGBA16F, ibuf->rect_float, error); - GPUTexture *tex = sl->equirectangular_radiance_gputexture; + GPUTexture *tex = sl->equirect_radiance_gputexture; GPU_texture_bind(tex, 0); GPU_texture_filter_mode(tex, true); GPU_texture_wrap_mode(tex, true); GPU_texture_unbind(tex); } } - sl->flag |= STUDIOLIGHT_EQUIRECTANGULAR_RADIANCE_GPUTEXTURE; + sl->flag |= STUDIOLIGHT_EQUIRECT_RADIANCE_GPUTEXTURE; } -static void studiolight_create_equirectangular_irradiance_gputexture(StudioLight *sl) +static void studiolight_create_equirect_irradiance_gputexture(StudioLight *sl) { if (sl->flag & STUDIOLIGHT_EXTERNAL_FILE) { char error[256]; - BKE_studiolight_ensure_flag(sl, STUDIOLIGHT_EQUIRECTANGULAR_IRRADIANCE_IMAGE_CALCULATED); - ImBuf *ibuf = sl->equirectangular_irradiance_buffer; - sl->equirectangular_irradiance_gputexture = GPU_texture_create_2D( + BKE_studiolight_ensure_flag(sl, STUDIOLIGHT_EQUIRECT_IRRADIANCE_IMAGE_CALCULATED); + ImBuf *ibuf = sl->equirect_irradiance_buffer; + sl->equirect_irradiance_gputexture = GPU_texture_create_2D( ibuf->x, ibuf->y, GPU_RGBA16F, ibuf->rect_float, error); - GPUTexture *tex = sl->equirectangular_irradiance_gputexture; + GPUTexture *tex = sl->equirect_irradiance_gputexture; GPU_texture_bind(tex, 0); GPU_texture_filter_mode(tex, true); GPU_texture_wrap_mode(tex, true); GPU_texture_unbind(tex); } - sl->flag |= STUDIOLIGHT_EQUIRECTANGULAR_IRRADIANCE_GPUTEXTURE; + sl->flag |= STUDIOLIGHT_EQUIRECT_IRRADIANCE_GPUTEXTURE; +} + +static void studiolight_calculate_radiance(ImBuf *ibuf, float color[4], const float direction[3]) +{ + float uv[2]; + direction_to_equirect(uv, direction); + nearest_interpolation_color_wrap(ibuf, NULL, color, uv[0] * ibuf->x, uv[1] * ibuf->y); +} + +static void studiolight_calculate_radiance_buffer( + ImBuf *ibuf, float *colbuf, + const int index_x, const int index_y, const int index_z, + const float xsign, const float ysign, const float zsign) +{ + ITER_PIXELS(float, colbuf, 4, + STUDIOLIGHT_RADIANCE_CUBEMAP_SIZE, + STUDIOLIGHT_RADIANCE_CUBEMAP_SIZE) + { + float direction[3]; + direction[index_x] = xsign * (x - 0.5f); + direction[index_y] = ysign * (y - 0.5f); + direction[index_z] = zsign * 0.5f; + normalize_v3(direction); + studiolight_calculate_radiance(ibuf, pixel, direction); + } + ITER_PIXELS_END; } static void studiolight_calculate_radiance_cubemap_buffers(StudioLight *sl) { if (sl->flag & STUDIOLIGHT_EXTERNAL_FILE) { BKE_studiolight_ensure_flag(sl, STUDIOLIGHT_EXTERNAL_IMAGE_LOADED); - ImBuf *ibuf = sl->equirectangular_radiance_buffer; + ImBuf *ibuf = sl->equirect_radiance_buffer; if (ibuf) { float *colbuf = MEM_mallocN(SQUARE(STUDIOLIGHT_RADIANCE_CUBEMAP_SIZE) * sizeof(float[4]), __func__); - const float add = 1.0f / (STUDIOLIGHT_RADIANCE_CUBEMAP_SIZE + 1); - const float start = ((1.0f / STUDIOLIGHT_RADIANCE_CUBEMAP_SIZE) * 0.5f) - 0.5f; /* front */ - studiolight_calculate_radiance_buffer(ibuf, colbuf, start, add, start, add, 0.5f, 0, 2, 1); + studiolight_calculate_radiance_buffer(ibuf, colbuf, 0, 2, 1, 1, -1, 1); sl->radiance_cubemap_buffers[STUDIOLIGHT_Y_POS] = IMB_allocFromBuffer( NULL, colbuf, STUDIOLIGHT_RADIANCE_CUBEMAP_SIZE, STUDIOLIGHT_RADIANCE_CUBEMAP_SIZE); /* back */ - studiolight_calculate_radiance_buffer(ibuf, colbuf, -start, -add, start, add, -0.5f, 0, 2, 1); + studiolight_calculate_radiance_buffer(ibuf, colbuf, 0, 2, 1, 1, 1, -1); sl->radiance_cubemap_buffers[STUDIOLIGHT_Y_NEG] = IMB_allocFromBuffer( NULL, colbuf, STUDIOLIGHT_RADIANCE_CUBEMAP_SIZE, STUDIOLIGHT_RADIANCE_CUBEMAP_SIZE); /* left */ - studiolight_calculate_radiance_buffer(ibuf, colbuf, -start, -add, start, add, 0.5f, 1, 2, 0); + studiolight_calculate_radiance_buffer(ibuf, colbuf, 2, 1, 0, 1, -1, 1); sl->radiance_cubemap_buffers[STUDIOLIGHT_X_POS] = IMB_allocFromBuffer( NULL, colbuf, STUDIOLIGHT_RADIANCE_CUBEMAP_SIZE, STUDIOLIGHT_RADIANCE_CUBEMAP_SIZE); /* right */ - studiolight_calculate_radiance_buffer(ibuf, colbuf, start, add, start, add, -0.5f, 1, 2, 0); + studiolight_calculate_radiance_buffer(ibuf, colbuf, 2, 1, 0, -1, -1, -1); sl->radiance_cubemap_buffers[STUDIOLIGHT_X_NEG] = IMB_allocFromBuffer( NULL, colbuf, STUDIOLIGHT_RADIANCE_CUBEMAP_SIZE, STUDIOLIGHT_RADIANCE_CUBEMAP_SIZE); /* top */ - studiolight_calculate_radiance_buffer(ibuf, colbuf, start, add, start, add, -0.5f, 0, 1, 2); + studiolight_calculate_radiance_buffer(ibuf, colbuf, 0, 1, 2, -1, -1, 1); sl->radiance_cubemap_buffers[STUDIOLIGHT_Z_NEG] = IMB_allocFromBuffer( NULL, colbuf, STUDIOLIGHT_RADIANCE_CUBEMAP_SIZE, STUDIOLIGHT_RADIANCE_CUBEMAP_SIZE); /* bottom */ - studiolight_calculate_radiance_buffer(ibuf, colbuf, start, add, -start, -add, 0.5f, 0, 1, 2); + studiolight_calculate_radiance_buffer(ibuf, colbuf, 0, 1, 2, 1, -1, -1); sl->radiance_cubemap_buffers[STUDIOLIGHT_Z_POS] = IMB_allocFromBuffer( NULL, colbuf, STUDIOLIGHT_RADIANCE_CUBEMAP_SIZE, STUDIOLIGHT_RADIANCE_CUBEMAP_SIZE); @@ -328,243 +385,131 @@ static void studiolight_calculate_radiance_cubemap_buffers(StudioLight *sl) sl->flag |= STUDIOLIGHT_RADIANCE_BUFFERS_CALCULATED; } -BLI_INLINE void studiolight_evaluate_radiance_buffer( - ImBuf *radiance_buffer, const float normal[3], float color[3], int *hits, - int xoffset, int yoffset, int zoffset, float zvalue) -{ - if (radiance_buffer == NULL) { - return; - } - float angle; - float *radiance_color = radiance_buffer->rect_float; - float direction[3]; - for (int y = 0; y < STUDIOLIGHT_RADIANCE_CUBEMAP_SIZE; y++) { - for (int x = 0; x < STUDIOLIGHT_RADIANCE_CUBEMAP_SIZE; x++) { - // calculate light direction; - direction[zoffset] = zvalue; - direction[xoffset] = (x / (float)STUDIOLIGHT_RADIANCE_CUBEMAP_SIZE) - 0.5f; - direction[yoffset] = (y / (float)STUDIOLIGHT_RADIANCE_CUBEMAP_SIZE) - 0.5f; - normalize_v3(direction); - angle = fmax(0.0f, dot_v3v3(direction, normal)); - madd_v3_v3fl(color, radiance_color, angle); - (*hits)++; - radiance_color += 4; - } - } - -} - /* * Spherical Harmonics */ -BLI_INLINE float studiolight_area_element(float x, float y) +BLI_INLINE float area_element(float x, float y) { return atan2(x * y, sqrtf(x * x + y * y + 1)); } -BLI_INLINE float studiolight_texel_solid_angle(float x, float y, float halfpix) +BLI_INLINE float texel_solid_angle(float x, float y, float halfpix) { float v1x = (x - halfpix) * 2.0f - 1.0f; float v1y = (y - halfpix) * 2.0f - 1.0f; float v2x = (x + halfpix) * 2.0f - 1.0f; float v2y = (y + halfpix) * 2.0f - 1.0f; - return studiolight_area_element(v1x, v1y) - studiolight_area_element(v1x, v2y) - studiolight_area_element(v2x, v1y) + studiolight_area_element(v2x, v2y); + return area_element(v1x, v1y) - area_element(v1x, v2y) - area_element(v2x, v1y) + area_element(v2x, v2y); } static void studiolight_calculate_cubemap_vector_weight(float normal[3], float *weight, int face, float x, float y) { - copy_v3_fl3(normal, x * 2.0f - 1.0f, y * 2.0f - 1.0f, 1.0f); - const float conversion_matrices[6][3][3] = { - { - {0.0f, 0.0f, 1.0f}, - {0.0f, -1.0f, 0.0f}, - {1.0f, 0.0f, 0.0f}, - }, - { - {0.0f, 0.0f, -1.0f}, - {0.0f, -1.0f, 0.0f}, - {-1.0f, 0.0f, 0.0f}, - }, - { - {1.0f, 0.0f, 0.0f}, - {0.0f, 0.0f, -1.0f}, - {0.0f, 1.0f, 0.0f}, - }, - { - {1.0f, 0.0f, 0.0f}, - {0.0f, 0.0f, 1.0f}, - {0.0f, -1.0f, 0.0f}, - }, - { - {1.0f, 0.0f, 0.0f}, - {0.0f, -1.0f, 0.0f}, - {0.0f, 0.0f, -1.0f}, - }, - { - {-1.0f, 0.0f, 0.0f}, - {0.0f, -1.0f, 0.0f}, - {0.0f, 0.0f, 1.0f}, - } - }; - - mul_m3_v3(conversion_matrices[face], normal); - normalize_v3(normal); - const float halfpix = 1.0f / (2.0f * STUDIOLIGHT_RADIANCE_CUBEMAP_SIZE); - *weight = studiolight_texel_solid_angle(x + halfpix, y + halfpix, halfpix); + const float halfpix = 0.5f / STUDIOLIGHT_RADIANCE_CUBEMAP_SIZE; + cube_face_uv_to_direction(normal, x, y, face); + *weight = texel_solid_angle(x, y, halfpix); } -static void studiolight_calculate_spherical_harmonics_coefficient(StudioLight *sl, int sh_component) +static void studiolight_spherical_harmonics_calculate_coefficients(StudioLight *sl, float (*sh)[3]) { - const float M_4PI = M_PI * 4.0f; - float weight_accum = 0.0f; - float sh[3] = {0.0f, 0.0f, 0.0f}; + memset(sh, 0, sizeof(float) * 3 * STUDIOLIGHT_SH_COEFS_LEN); + for (int face = 0; face < 6; face++) { - float *color; - color = sl->radiance_cubemap_buffers[face]->rect_float; - for (int y = 0; y < STUDIOLIGHT_RADIANCE_CUBEMAP_SIZE; y++) { - float yf = y / (float)STUDIOLIGHT_RADIANCE_CUBEMAP_SIZE; - for (int x = 0; x < STUDIOLIGHT_RADIANCE_CUBEMAP_SIZE; x++) { - float xf = x / (float)STUDIOLIGHT_RADIANCE_CUBEMAP_SIZE; - float weight, coef; - float cubevec[3]; - studiolight_calculate_cubemap_vector_weight(cubevec, &weight, face, xf, yf); - - const float nx = cubevec[0]; - const float ny = cubevec[1]; - const float nz = cubevec[2]; - const float nx2 = SQUARE(nx); - const float ny2 = SQUARE(ny); - const float nz2 = SQUARE(nz); - const float nx4 = SQUARE(nx2); - const float ny4 = SQUARE(ny2); - const float nz4 = SQUARE(nz2); - - switch (sh_component) { - /* L0 */ - case 0: - coef = 0.2822095f; - break; - /* L1 */ - case 1: - coef = -0.488603f * nz * 2.0f / 3.0f; - break; - case 2: - coef = 0.488603f * ny * 2.0f / 3.0f; - break; - case 3: - coef = -0.488603f * nx * 2.0f / 3.0f; - break; - /* L2 */ - case 4: - coef = 1.092548f * nx * nz * 1.0f / 4.0f; - break; - case 5: - coef = -1.092548f * nz * ny * 1.0f / 4.0f; - break; - case 6: - coef = 0.315392f * (3.0f * ny2 - 1.0f) * 1.0f / 4.0f; - break; - case 7: - coef = 1.092548f * nx * ny * 1.0f / 4.0f; - break; - case 8: - coef = 0.546274f * (nx2 - nz2) * 1.0f / 4.0f; - break; - /* L4 */ - case 9: - coef = (2.5033429417967046f * nx * nz * (nx2 - nz2)) / -24.0f; - break; - case 10: - coef = (-1.7701307697799304f * nz * ny * (3.0f * nx2 - nz2)) / -24.0f; - break; - case 11: - coef = (0.9461746957575601f * nz * nx * (-1.0f + 7.0f * ny2)) / -24.0f; - break; - case 12: - coef = (-0.6690465435572892f * nz * ny * (-3.0f + 7.0f * ny2)) / -24.0f; - break; - case 13: - coef = ((105.0f * ny4 - 90.0f * ny2 + 9.0f) / 28.359261614f) / -24.0f; - break; - case 14: - coef = (-0.6690465435572892f * nx * ny * (-3.0f + 7.0f * ny2)) / -24.0f; - break; - case 15: - coef = (0.9461746957575601f * (nx2 - nz2) * (-1.0f + 7.0f * ny2)) / -24.0f; - break; - case 16: - coef = (-1.7701307697799304f * nx * ny * (nx2 - 3.0f * nz2)) / -24.0f; - break; - case 17: - coef = (0.6258357354491761f * (nx4 - 6.0f * nz2 * nx2 + nz4)) / -24.0f; - break; - default: - coef = 0.0f; - } - - madd_v3_v3fl(sh, color, coef * weight); - weight_accum += weight; - color += 4; - } + ITER_PIXELS(float, sl->radiance_cubemap_buffers[face]->rect_float, 4, + STUDIOLIGHT_RADIANCE_CUBEMAP_SIZE, + STUDIOLIGHT_RADIANCE_CUBEMAP_SIZE) + { + float color[3], cubevec[3], weight; + studiolight_calculate_cubemap_vector_weight(cubevec, &weight, face, x, y); + mul_v3_v3fl(color, pixel, weight); + weight_accum += weight; + + int i = 0; + /* L0 */ + madd_v3_v3fl(sh[i++], color, 0.2822095f); +#if STUDIOLIGHT_SH_BANDS > 1 /* L1 */ + const float nx = cubevec[0]; + const float ny = cubevec[1]; + const float nz = cubevec[2]; + madd_v3_v3fl(sh[i++], color, -0.488603f * nz); + madd_v3_v3fl(sh[i++], color, 0.488603f * ny); + madd_v3_v3fl(sh[i++], color, -0.488603f * nx); +#endif +#if STUDIOLIGHT_SH_BANDS > 2 /* L2 */ + const float nx2 = SQUARE(nx); + const float ny2 = SQUARE(ny); + const float nz2 = SQUARE(nz); + madd_v3_v3fl(sh[i++], color, 1.092548f * nx * nz); + madd_v3_v3fl(sh[i++], color, -1.092548f * nz * ny); + madd_v3_v3fl(sh[i++], color, 0.315392f * (3.0f * ny2 - 1.0f)); + madd_v3_v3fl(sh[i++], color, 1.092548f * nx * ny); + madd_v3_v3fl(sh[i++], color, 0.546274f * (nx2 - nz2)); +#endif + /* Bypass L3 Because final irradiance does not need it. */ +#if STUDIOLIGHT_SH_BANDS > 4 /* L4 */ + const float nx4 = SQUARE(nx2); + const float ny4 = SQUARE(ny2); + const float nz4 = SQUARE(nz2); + madd_v3_v3fl(sh[i++], color, 2.5033429417967046f * nx * nz * (nx2 - nz2)); + madd_v3_v3fl(sh[i++], color, -1.7701307697799304f * nz * ny * (3.0f * nx2 - nz2)); + madd_v3_v3fl(sh[i++], color, 0.9461746957575601f * nz * nx * (-1.0f + 7.0f * ny2)); + madd_v3_v3fl(sh[i++], color, -0.6690465435572892f * nz * ny * (-3.0f + 7.0f * ny2)); + madd_v3_v3fl(sh[i++], color, (105.0f * ny4 - 90.0f * ny2 + 9.0f) / 28.359261614f); + madd_v3_v3fl(sh[i++], color, -0.6690465435572892f * nx * ny * (-3.0f + 7.0f * ny2)); + madd_v3_v3fl(sh[i++], color, 0.9461746957575601f * (nx2 - nz2) * (-1.0f + 7.0f * ny2)); + madd_v3_v3fl(sh[i++], color, -1.7701307697799304f * nx * ny * (nx2 - 3.0f * nz2)); + madd_v3_v3fl(sh[i++], color, 0.6258357354491761f * (nx4 - 6.0f * nz2 * nx2 + nz4)); +#endif } + ITER_PIXELS_END; } - mul_v3_fl(sh, M_4PI / weight_accum); - copy_v3_v3(sl->spherical_harmonics_coefs[sh_component], sh); -} - -#ifdef STUDIOLIGHT_SPHERICAL_HARMONICS_WINDOWING -static void studiolight_calculate_spherical_harmonics_luminance(StudioLight *sl, float luminance[STUDIOLIGHT_SPHERICAL_HARMONICS_COMPONENTS]) -{ - for (int index = 0; index < STUDIOLIGHT_SPHERICAL_HARMONICS_COMPONENTS; index++) { - luminance[index] = rgb_to_grayscale(sl->spherical_harmonics_coefs[index]); + /* The sum of solid angle should be equal to the solid angle of the sphere (4 PI), + * so normalize in order to make our weightAccum exactly match 4 PI. */ + for (int i = 0; i < STUDIOLIGHT_SH_COEFS_LEN; ++i) { + mul_v3_fl(sh[i], M_PI * 4.0f / weight_accum); } } -static void studiolight_apply_spherical_harmonics_windowing(StudioLight *sl, float max_lamplacian) +/* Take monochrome SH as input */ +static float studiolight_spherical_harmonics_lambda_get(float *sh, float max_laplacian) { /* From Peter-Pike Sloan's Stupid SH Tricks http://www.ppsloan.org/publications/StupidSH36.pdf */ - float table_l[STUDIOLIGHT_SPHERICAL_HARMONICS_LEVEL + 1]; - float table_b[STUDIOLIGHT_SPHERICAL_HARMONICS_LEVEL + 1]; + float table_l[STUDIOLIGHT_SH_BANDS]; + float table_b[STUDIOLIGHT_SH_BANDS]; + + float lambda = 0.0f; table_l[0] = 0.0f; table_b[0] = 0.0f; - - /* convert to luminance */ - float luminance[STUDIOLIGHT_SPHERICAL_HARMONICS_COMPONENTS]; - studiolight_calculate_spherical_harmonics_luminance(sl, luminance); - int index = 1; - for (int level = 1; level <= STUDIOLIGHT_SPHERICAL_HARMONICS_LEVEL; level++) { + for (int level = 1; level < STUDIOLIGHT_SH_BANDS; level++) { table_l[level] = (float)(SQUARE(level) * SQUARE(level + 1)); float b = 0.0f; for (int m = -1; m <= level; m++) { - b += SQUARE(luminance[index++]); + b += SQUARE(sh[index++]); } table_b[level] = b; } float squared_lamplacian = 0.0f; - for (int level = 1; level <= STUDIOLIGHT_SPHERICAL_HARMONICS_LEVEL; level++) { + for (int level = 1; level < STUDIOLIGHT_SH_BANDS; level++) { squared_lamplacian += table_l[level] * table_b[level]; } - const float target_squared_laplacian = max_lamplacian * max_lamplacian; + const float target_squared_laplacian = max_laplacian * max_laplacian; if (squared_lamplacian <= target_squared_laplacian) { - return; + return lambda; } - float lambda = 0.0f; - const int no_iterations = 10000000; for (int i = 0; i < no_iterations; ++i) { float f = 0.0f; float fd = 0.0f; - for (int level = 1; level <= (int)STUDIOLIGHT_SPHERICAL_HARMONICS_LEVEL; ++level) { + for (int level = 1; level < STUDIOLIGHT_SH_BANDS; ++level) { f += table_l[level] * table_b[level] / SQUARE(1.0f + lambda * table_l[level]); fd += (2.0f * SQUARE(table_l[level]) * table_b[level]) / CUBE(1.0f + lambda * table_l[level]); } @@ -579,36 +524,53 @@ static void studiolight_apply_spherical_harmonics_windowing(StudioLight *sl, flo } } + return lambda; +} + +static void studiolight_spherical_harmonics_apply_windowing(float (*sh)[3], float max_laplacian) +{ + if (max_laplacian <= 0.0f) + return; + + float sh_r[STUDIOLIGHT_SH_COEFS_LEN]; + float sh_g[STUDIOLIGHT_SH_COEFS_LEN]; + float sh_b[STUDIOLIGHT_SH_COEFS_LEN]; + for (int i = 0; i < STUDIOLIGHT_SH_COEFS_LEN; i++) { + sh_r[i] = sh[i][0]; + sh_g[i] = sh[i][1]; + sh_b[i] = sh[i][2]; + } + float lambda_r = studiolight_spherical_harmonics_lambda_get(sh_r, max_laplacian); + float lambda_g = studiolight_spherical_harmonics_lambda_get(sh_g, max_laplacian); + float lambda_b = studiolight_spherical_harmonics_lambda_get(sh_b, max_laplacian); + /* Apply windowing lambda */ - index = 0; - for (int level = 0; level <= STUDIOLIGHT_SPHERICAL_HARMONICS_LEVEL; level++) { - float s = 1.0f / (1.0f + lambda * SQUARE(level) * SQUARE(level + 1.0f)); + int index = 0; + for (int level = 0; level < STUDIOLIGHT_SH_BANDS; level++) { + float s[3]; + s[0] = 1.0f / (1.0f + lambda_r * SQUARE(level) * SQUARE(level + 1.0f)); + s[1] = 1.0f / (1.0f + lambda_g * SQUARE(level) * SQUARE(level + 1.0f)); + s[2] = 1.0f / (1.0f + lambda_b * SQUARE(level) * SQUARE(level + 1.0f)); for (int m = -1; m <= level; m++) { - mul_v3_fl(sl->spherical_harmonics_coefs[index++], s); + mul_v3_v3(sh[index++], s); } } } -#endif -BLI_INLINE void studiolight_sample_spherical_harmonics(StudioLight *sl, float color[3], float normal[3]) +BLI_INLINE void studiolight_spherical_harmonics_eval(StudioLight *sl, float color[3], float normal[3]) { + /* L0 */ + mul_v3_v3fl(color, sl->spherical_harmonics_coefs[0], 0.282095f); +#if STUDIOLIGHT_SH_BANDS > 1 /* L1 */ const float nx = normal[0]; const float ny = normal[1]; const float nz = normal[2]; - - copy_v3_fl(color, 0.0f); - madd_v3_v3fl(color, sl->spherical_harmonics_coefs[0], 0.282095f); - -#if STUDIOLIGHT_SPHERICAL_HARMONICS_LEVEL > 0 - /* Spherical Harmonics L1 */ madd_v3_v3fl(color, sl->spherical_harmonics_coefs[1], -0.488603f * nz); madd_v3_v3fl(color, sl->spherical_harmonics_coefs[2], 0.488603f * ny); madd_v3_v3fl(color, sl->spherical_harmonics_coefs[3], -0.488603f * nx); #endif - -#if STUDIOLIGHT_SPHERICAL_HARMONICS_LEVEL > 1 - /* Spherical Harmonics L2 */ +#if STUDIOLIGHT_SH_BANDS > 2 /* L2 */ const float nx2 = SQUARE(nx); const float ny2 = SQUARE(ny); const float nz2 = SQUARE(nz); @@ -618,9 +580,8 @@ BLI_INLINE void studiolight_sample_spherical_harmonics(StudioLight *sl, float co madd_v3_v3fl(color, sl->spherical_harmonics_coefs[7], -1.092548 * nx * ny); madd_v3_v3fl(color, sl->spherical_harmonics_coefs[8], 0.546274 * (nx2 - nz2)); #endif - -#if STUDIOLIGHT_SPHERICAL_HARMONICS_LEVEL > 3 - /* Spherical Harmonics L4 */ + /* L3 coefs are 0 */ +#if STUDIOLIGHT_SH_BANDS > 4 /* L4 */ const float nx4 = SQUARE(nx2); const float ny4 = SQUARE(ny2); const float nz4 = SQUARE(nz2); @@ -634,7 +595,29 @@ BLI_INLINE void studiolight_sample_spherical_harmonics(StudioLight *sl, float co madd_v3_v3fl(color, sl->spherical_harmonics_coefs[16], -1.7701307697799304f * nx * ny * (nx2 - 3.0f * nz2)); madd_v3_v3fl(color, sl->spherical_harmonics_coefs[17], 0.6258357354491761f * (nx4 - 6.0f * nz2 * nx2 + nz4)); #endif +} +/* This modify the radiance into irradiance. */ +static void studiolight_spherical_harmonics_apply_band_factors(StudioLight *sl, float (*sh)[3]) +{ + static float sl_sh_band_factors[5] = { + 1.0f, + 2.0f / 3.0f, + 1.0f / 4.0f, + 0.0f, + -1.0f / 24.0f + }; + + int index = 0, dst_idx = 0; + for (int band = 0; band < STUDIOLIGHT_SH_BANDS; band++) { + for (int m = -1; m <= band; m++) { + /* Skip L3 */ + if (band != 3) { + mul_v3_v3fl(sl->spherical_harmonics_coefs[dst_idx++], sh[index], sl_sh_band_factors[band]); + } + index++; + } + } } static void studiolight_calculate_diffuse_light(StudioLight *sl) @@ -643,13 +626,10 @@ static void studiolight_calculate_diffuse_light(StudioLight *sl) if (sl->flag & STUDIOLIGHT_EXTERNAL_FILE) { BKE_studiolight_ensure_flag(sl, STUDIOLIGHT_RADIANCE_BUFFERS_CALCULATED); - for (int comp = 0; comp < STUDIOLIGHT_SPHERICAL_HARMONICS_COMPONENTS; comp++) { - studiolight_calculate_spherical_harmonics_coefficient(sl, comp); - } - -#ifdef STUDIOLIGHT_SPHERICAL_HARMONICS_WINDOWING - studiolight_apply_spherical_harmonics_windowing(sl, STUDIOLIGHT_SPHERICAL_HARMONICS_WINDOWING_TARGET_LAMPLACIAN); -#endif + float sh_coefs[STUDIOLIGHT_SH_COEFS_LEN][3]; + studiolight_spherical_harmonics_calculate_coefficients(sl, sh_coefs); + studiolight_spherical_harmonics_apply_windowing(sh_coefs, STUDIOLIGHT_SH_WINDOWING); + studiolight_spherical_harmonics_apply_band_factors(sl, sh_coefs); if (sl->flag & STUDIOLIGHT_USER_DEFINED) { FILE *fp = BLI_fopen(sl->path_sh_cache, "wb"); @@ -662,81 +642,70 @@ static void studiolight_calculate_diffuse_light(StudioLight *sl) sl->flag |= STUDIOLIGHT_SPHERICAL_HARMONICS_COEFFICIENTS_CALCULATED; } -static float texel_coord_solid_angle(float a_U, float a_V, int a_Size) -{ - //scale up to [-1, 1] range (inclusive), offset by 0.5 to point to texel center. - float u = (2.0f * ((float)a_U + 0.5f) / (float)a_Size) - 1.0f; - float v = (2.0f * ((float)a_V + 0.5f) / (float)a_Size) - 1.0f; - - float resolution_inv = 1.0f / a_Size; - - // U and V are the -1..1 texture coordinate on the current face. - // Get projected area for this texel - float x0 = u - resolution_inv; - float y0 = v - resolution_inv; - float x1 = u + resolution_inv; - float y1 = v + resolution_inv; - return studiolight_area_element(x0, y0) - studiolight_area_element(x0, y1) - studiolight_area_element(x1, y0) + studiolight_area_element(x1, y1); -} - BLI_INLINE void studiolight_evaluate_specular_radiance_buffer( ImBuf *radiance_buffer, const float normal[3], float color[3], - int xoffset, int yoffset, int zoffset, float zvalue) + int xoffset, int yoffset, int zoffset, float zsign) { if (radiance_buffer == NULL) { return; } - float angle; - float *radiance_color = radiance_buffer->rect_float; - float direction[3]; - for (int y = 0; y < STUDIOLIGHT_RADIANCE_CUBEMAP_SIZE; y++) { - for (int x = 0; x < STUDIOLIGHT_RADIANCE_CUBEMAP_SIZE; x++) { - // calculate light direction; - float u = (x / (float)STUDIOLIGHT_RADIANCE_CUBEMAP_SIZE) - 0.5f; - float v = (y / (float)STUDIOLIGHT_RADIANCE_CUBEMAP_SIZE) - 0.5f; - direction[zoffset] = zvalue; - direction[xoffset] = u; - direction[yoffset] = v; - normalize_v3(direction); - angle = fmax(0.0f, dot_v3v3(direction, normal)) * texel_coord_solid_angle(x, y, STUDIOLIGHT_RADIANCE_CUBEMAP_SIZE); - madd_v3_v3fl(color, radiance_color, angle); - radiance_color += 4; - } + + float accum[3] = {0.0f, 0.0f, 0.0f}; + float accum_weight = 0.00001f; + ITER_PIXELS(float, radiance_buffer->rect_float, 4, + STUDIOLIGHT_RADIANCE_CUBEMAP_SIZE, + STUDIOLIGHT_RADIANCE_CUBEMAP_SIZE) + { + float direction[3]; + direction[zoffset] = zsign * 0.5f; + direction[xoffset] = x - 0.5f; + direction[yoffset] = y - 0.5f; + normalize_v3(direction); + float weight = dot_v3v3(direction, normal) > 0.95f ? 1.0f : 0.0f; + // float solid_angle = texel_solid_angle(x, y, texel_size[0] * 0.5f); + madd_v3_v3fl(accum, pixel, weight); + accum_weight += weight; } + ITER_PIXELS_END; + madd_v3_v3fl(color, accum, 1.0f / accum_weight); } -#if STUDIOLIGHT_IRRADIANCE_METHOD == STUDIOLIGHT_IRRADIANCE_METHOD_RADIANCE -static void studiolight_calculate_specular_irradiance(StudioLight *sl, float color[3], const float normal[3]) +#ifdef STUDIOLIGHT_IRRADIANCE_METHOD_RADIANCE +static void studiolight_irradiance_eval(StudioLight *sl, float color[3], const float normal[3]) { copy_v3_fl(color, 0.0f); + /* XXX: This is madness, iterating over all cubemap pixels for each destination pixels + * even if their weight is 0.0f. + * It should use hemisphere, cosine sampling at least. */ + /* back */ studiolight_evaluate_specular_radiance_buffer( - sl->radiance_cubemap_buffers[STUDIOLIGHT_Y_POS], normal, color, 0, 2, 1, 0.5); + sl->radiance_cubemap_buffers[STUDIOLIGHT_Y_POS], normal, color, 0, 2, 1, 1); /* front */ studiolight_evaluate_specular_radiance_buffer( - sl->radiance_cubemap_buffers[STUDIOLIGHT_Y_NEG], normal, color, 0, 2, 1, -0.5); + sl->radiance_cubemap_buffers[STUDIOLIGHT_Y_NEG], normal, color, 0, 2, 1, -1); /* left */ studiolight_evaluate_specular_radiance_buffer( - sl->radiance_cubemap_buffers[STUDIOLIGHT_X_POS], normal, color, 1, 2, 0, 0.5); + sl->radiance_cubemap_buffers[STUDIOLIGHT_X_POS], normal, color, 1, 2, 0, 1); /* right */ studiolight_evaluate_specular_radiance_buffer( - sl->radiance_cubemap_buffers[STUDIOLIGHT_X_NEG], normal, color, 1, 2, 0, -0.5); + sl->radiance_cubemap_buffers[STUDIOLIGHT_X_NEG], normal, color, 1, 2, 0, -1); /* top */ studiolight_evaluate_specular_radiance_buffer( - sl->radiance_cubemap_buffers[STUDIOLIGHT_Z_POS], normal, color, 0, 1, 2, 0.5); + sl->radiance_cubemap_buffers[STUDIOLIGHT_Z_POS], normal, color, 0, 1, 2, 1); /* bottom */ studiolight_evaluate_specular_radiance_buffer( - sl->radiance_cubemap_buffers[STUDIOLIGHT_Z_NEG], normal, color, 0, 1, 2, -0.5); + sl->radiance_cubemap_buffers[STUDIOLIGHT_Z_NEG], normal, color, 0, 1, 2, -1); mul_v3_fl(color, 1.0 / M_PI); } #endif -static bool studiolight_load_irradiance_equirectangular_image(StudioLight *sl) +static bool studiolight_load_irradiance_equirect_image(StudioLight *sl) { #ifdef STUDIOLIGHT_LOAD_CACHED_FILES if (sl->flag & STUDIOLIGHT_EXTERNAL_FILE) { @@ -744,11 +713,13 @@ static bool studiolight_load_irradiance_equirectangular_image(StudioLight *sl) ibuf = IMB_loadiffname(sl->path_irr_cache, 0, NULL); if (ibuf) { IMB_float_from_rect(ibuf); - sl->equirectangular_irradiance_buffer = ibuf; - sl->flag |= STUDIOLIGHT_EQUIRECTANGULAR_IRRADIANCE_IMAGE_CALCULATED; + sl->equirect_irradiance_buffer = ibuf; + sl->flag |= STUDIOLIGHT_EQUIRECT_IRRADIANCE_IMAGE_CALCULATED; return true; } } +#else + UNUSED_VARS(sl); #endif return false; } @@ -767,92 +738,54 @@ static bool studiolight_load_spherical_harmonics_coefficients(StudioLight *sl) fclose(fp); } } +#else + UNUSED_VARS(sl); #endif return false; } -static void studiolight_calculate_irradiance_equirectangular_image(StudioLight *sl) +static void studiolight_calculate_irradiance_equirect_image(StudioLight *sl) { if (sl->flag & STUDIOLIGHT_EXTERNAL_FILE) { -#if STUDIOLIGHT_IRRADIANCE_METHOD == STUDIOLIGHT_IRRADIANCE_METHOD_RADIANCE +#ifdef STUDIOLIGHT_IRRADIANCE_METHOD_RADIANCE BKE_studiolight_ensure_flag(sl, STUDIOLIGHT_RADIANCE_BUFFERS_CALCULATED); -#endif -#if STUDIOLIGHT_IRRADIANCE_METHOD == STUDIOLIGHT_IRRADIANCE_METHOD_SPHERICAL_HARMONICS +#else BKE_studiolight_ensure_flag(sl, STUDIOLIGHT_SPHERICAL_HARMONICS_COEFFICIENTS_CALCULATED); #endif - float *colbuf = MEM_mallocN(STUDIOLIGHT_IRRADIANCE_EQUIRECTANGULAR_WIDTH * STUDIOLIGHT_IRRADIANCE_EQUIRECTANGULAR_HEIGHT * sizeof(float[4]), __func__); - float *color = colbuf; - for (int y = 0; y < STUDIOLIGHT_IRRADIANCE_EQUIRECTANGULAR_HEIGHT; y++) { - float yf = y / (float)STUDIOLIGHT_IRRADIANCE_EQUIRECTANGULAR_HEIGHT; - - for (int x = 0; x < STUDIOLIGHT_IRRADIANCE_EQUIRECTANGULAR_WIDTH; x++) { - float xf = x / (float)STUDIOLIGHT_IRRADIANCE_EQUIRECTANGULAR_WIDTH; - float dir[3]; - equirectangular_to_direction(dir, xf, yf); + float *colbuf = MEM_mallocN(STUDIOLIGHT_IRRADIANCE_EQUIRECT_WIDTH * STUDIOLIGHT_IRRADIANCE_EQUIRECT_HEIGHT * sizeof(float[4]), __func__); -#if STUDIOLIGHT_IRRADIANCE_METHOD == STUDIOLIGHT_IRRADIANCE_METHOD_RADIANCE - studiolight_calculate_specular_irradiance(sl, color, dir); -#endif -#if STUDIOLIGHT_IRRADIANCE_METHOD == STUDIOLIGHT_IRRADIANCE_METHOD_SPHERICAL_HARMONICS - studiolight_sample_spherical_harmonics(sl, color, dir); + ITER_PIXELS(float, colbuf, 4, + STUDIOLIGHT_IRRADIANCE_EQUIRECT_WIDTH, + STUDIOLIGHT_IRRADIANCE_EQUIRECT_HEIGHT) + { + float dir[3]; + equirect_to_direction(dir, x, y); +#ifdef STUDIOLIGHT_IRRADIANCE_METHOD_RADIANCE + studiolight_irradiance_eval(sl, pixel, dir); +#else + studiolight_spherical_harmonics_eval(sl, pixel, dir); #endif - - color[3] = 1.0f; - color += 4; - } + pixel[3] = 1.0f; } + ITER_PIXELS_END; - sl->equirectangular_irradiance_buffer = IMB_allocFromBuffer( + sl->equirect_irradiance_buffer = IMB_allocFromBuffer( NULL, colbuf, - STUDIOLIGHT_IRRADIANCE_EQUIRECTANGULAR_WIDTH, - STUDIOLIGHT_IRRADIANCE_EQUIRECTANGULAR_HEIGHT); + STUDIOLIGHT_IRRADIANCE_EQUIRECT_WIDTH, + STUDIOLIGHT_IRRADIANCE_EQUIRECT_HEIGHT); MEM_freeN(colbuf); -#if STUDIOLIGHT_IRRADIANCE_METHOD == STUDIOLIGHT_IRRADIANCE_METHOD_RADIANCE +#ifdef STUDIOLIGHT_IRRADIANCE_METHOD_RADIANCE /* * Only store cached files when using STUDIOLIGHT_IRRADIANCE_METHOD_RADIANCE */ if (sl->flag & STUDIOLIGHT_USER_DEFINED) { - IMB_saveiff(sl->equirectangular_irradiance_buffer, sl->path_irr_cache, IB_rectfloat); + IMB_saveiff(sl->equirect_irradiance_buffer, sl->path_irr_cache, IB_rectfloat); } #endif } - sl->flag |= STUDIOLIGHT_EQUIRECTANGULAR_IRRADIANCE_IMAGE_CALCULATED; -} - -static void studiolight_calculate_light_direction(StudioLight *sl) -{ - float best_light = 0.0; - sl->light_direction[0] = 0.0f; - sl->light_direction[1] = 0.0f; - sl->light_direction[2] = -1.0f; - - if ((sl->flag & STUDIOLIGHT_EXTERNAL_FILE) && (sl->flag & STUDIOLIGHT_ORIENTATION_WORLD)) { - BKE_studiolight_ensure_flag(sl, STUDIOLIGHT_EQUIRECTANGULAR_IRRADIANCE_IMAGE_CALCULATED); - ImBuf *ibuf = sl->equirectangular_irradiance_buffer; - if (ibuf) { - /* go over every pixel, determine light, if higher calc direction off the light */ - float new_light; - float *color = ibuf->rect_float; - for (int y = 0; y < STUDIOLIGHT_IRRADIANCE_EQUIRECTANGULAR_HEIGHT; y++) { - for (int x = 0; x < STUDIOLIGHT_IRRADIANCE_EQUIRECTANGULAR_WIDTH; x++) { - new_light = color[0] + color[1] + color[2]; - if (new_light > best_light) { - float u = x / (float)STUDIOLIGHT_IRRADIANCE_EQUIRECTANGULAR_WIDTH; - float v = y / (float)STUDIOLIGHT_IRRADIANCE_EQUIRECTANGULAR_HEIGHT; - equirectangular_to_direction(sl->light_direction, u, v); - SWAP(float, sl->light_direction[0], sl->light_direction[1]); - normalize_v3(sl->light_direction); - negate_v3(sl->light_direction); - best_light = new_light; - } - color += 4; - } - } - } - } - sl->flag |= STUDIOLIGHT_LIGHT_DIRECTION_CALCULATED; + sl->flag |= STUDIOLIGHT_EQUIRECT_IRRADIANCE_IMAGE_CALCULATED; } static StudioLight *studiolight_add_file(const char *path, int flag) @@ -931,119 +864,115 @@ static uint alpha_circle_mask(float u, float v, float inner_edge, float outer_ed return mask << 24; } +/* Percentage of the icon that the preview sphere covers. */ #define STUDIOLIGHT_DIAMETER 0.95f +/* Rescale coord around (0.5, 0.5) by STUDIOLIGHT_DIAMETER. */ +#define RESCALE_COORD(x) (x / STUDIOLIGHT_DIAMETER - (1.0f - STUDIOLIGHT_DIAMETER) / 2.0f) + +/* Remaps normalized UV [0..1] to a sphere normal around (0.5, 0.5) */ +static void sphere_normal_from_uv(float normal[3], float u, float v) +{ + normal[0] = u * 2.0f - 1.0f; + normal[1] = v * 2.0f - 1.0f; + float dist = len_v2(normal); + normal[2] = sqrtf(1.0f - SQUARE(dist)); +} static void studiolight_radiance_preview(uint *icon_buffer, StudioLight *sl) { BKE_studiolight_ensure_flag(sl, STUDIOLIGHT_EXTERNAL_IMAGE_LOADED); - float pixel_size = 1.0f / (float)STUDIOLIGHT_ICON_SIZE; - - int offset = 0; - for (int y = 0; y < STUDIOLIGHT_ICON_SIZE; y++) { - float dy = (y + 0.5f) / (float)STUDIOLIGHT_ICON_SIZE; - dy = dy / STUDIOLIGHT_DIAMETER - (1.0f - STUDIOLIGHT_DIAMETER) / 2.0f; - for (int x = 0; x < STUDIOLIGHT_ICON_SIZE; x++) { - float dx = (x + 0.5f) / (float)STUDIOLIGHT_ICON_SIZE; - dx = dx / STUDIOLIGHT_DIAMETER - (1.0f - STUDIOLIGHT_DIAMETER) / 2.0f; - - uint pixelresult = 0x0; - uint alphamask = alpha_circle_mask(dx, dy, 0.5f - pixel_size, 0.5f); - if (alphamask != 0) { - float incoming[3] = {0.0f, 0.0f, -1.0f}; - - float normal[3]; - normal[0] = dx * 2.0f - 1.0f; - normal[1] = dy * 2.0f - 1.0f; - float dist = len_v2(normal); - normal[2] = sqrtf(1.0f - SQUARE(dist)); - - float direction[3]; - reflect_v3_v3v3(direction, incoming, normal); - - /* We want to see horizon not poles. */ - SWAP(float, direction[1], direction[2]); - direction[1] = -direction[1]; - - float color[4]; - studiolight_calculate_radiance(sl->equirectangular_radiance_buffer, color, direction); - - pixelresult = rgb_to_cpack( - linearrgb_to_srgb(color[0]), - linearrgb_to_srgb(color[1]), - linearrgb_to_srgb(color[2])) | alphamask; - } - icon_buffer[offset++] = pixelresult; + ITER_PIXELS(uint, icon_buffer, 1, + STUDIOLIGHT_ICON_SIZE, + STUDIOLIGHT_ICON_SIZE) + { + float dy = RESCALE_COORD(y); + float dx = RESCALE_COORD(x); + + uint alphamask = alpha_circle_mask(dx, dy, 0.5f - texel_size[0], 0.5f); + if (alphamask != 0) { + float normal[3], direction[3], color[4]; + float incoming[3] = {0.0f, 0.0f, -1.0f}; + sphere_normal_from_uv(normal, dx, dy); + reflect_v3_v3v3(direction, incoming, normal); + /* We want to see horizon not poles. */ + SWAP(float, direction[1], direction[2]); + direction[1] = -direction[1]; + + studiolight_calculate_radiance(sl->equirect_radiance_buffer, color, direction); + + *pixel = rgb_to_cpack( + linearrgb_to_srgb(color[0]), + linearrgb_to_srgb(color[1]), + linearrgb_to_srgb(color[2])) | alphamask; + } + else { + *pixel = 0x0; } } + ITER_PIXELS_END; } static void studiolight_matcap_preview(uint *icon_buffer, StudioLight *sl, bool flipped) { BKE_studiolight_ensure_flag(sl, STUDIOLIGHT_EXTERNAL_IMAGE_LOADED); - float color[4]; - float fx, fy; - float pixel_size = 1.0f / (float)STUDIOLIGHT_ICON_SIZE; - int offset = 0; - ImBuf *ibuf = sl->equirectangular_radiance_buffer; - - for (int y = 0; y < STUDIOLIGHT_ICON_SIZE; y++) { - fy = (y + 0.5f) / (float)STUDIOLIGHT_ICON_SIZE; - fy = fy / STUDIOLIGHT_DIAMETER - (1.0f - STUDIOLIGHT_DIAMETER) / 2.0f; - for (int x = 0; x < STUDIOLIGHT_ICON_SIZE; x++) { - fx = (x + 0.5f) / (float)STUDIOLIGHT_ICON_SIZE; - fx = fx / STUDIOLIGHT_DIAMETER - (1.0f - STUDIOLIGHT_DIAMETER) / 2.0f; - if (flipped) { - fx = 1.0f - fx; - } - nearest_interpolation_color(ibuf, NULL, color, fx * ibuf->x - 1.0f, fy * ibuf->y - 1.0f); - - uint alphamask = alpha_circle_mask(fx, fy, 0.5f - pixel_size, 0.5f); + ImBuf *ibuf = sl->equirect_radiance_buffer; - icon_buffer[offset++] = rgb_to_cpack( - linearrgb_to_srgb(color[0]), - linearrgb_to_srgb(color[1]), - linearrgb_to_srgb(color[2])) | alphamask; + ITER_PIXELS(uint, icon_buffer, 1, + STUDIOLIGHT_ICON_SIZE, + STUDIOLIGHT_ICON_SIZE) + { + float dy = RESCALE_COORD(y); + float dx = RESCALE_COORD(x); + if (flipped) { + dx = 1.0f - dx; } + + float color[4]; + nearest_interpolation_color(ibuf, NULL, color, dx * ibuf->x - 1.0f, dy * ibuf->y - 1.0f); + + uint alphamask = alpha_circle_mask(dx, dy, 0.5f - texel_size[0], 0.5f); + + *pixel = rgb_to_cpack( + linearrgb_to_srgb(color[0]), + linearrgb_to_srgb(color[1]), + linearrgb_to_srgb(color[2])) | alphamask; } + ITER_PIXELS_END; } static void studiolight_irradiance_preview(uint *icon_buffer, StudioLight *sl) { BKE_studiolight_ensure_flag(sl, STUDIOLIGHT_SPHERICAL_HARMONICS_COEFFICIENTS_CALCULATED); - float pixel_size = 1.0f / (float)STUDIOLIGHT_ICON_SIZE; - - int offset = 0; - for (int y = 0; y < STUDIOLIGHT_ICON_SIZE; y++) { - float dy = (y + 0.5f) / (float)STUDIOLIGHT_ICON_SIZE; - dy = dy / STUDIOLIGHT_DIAMETER - (1.0f - STUDIOLIGHT_DIAMETER) / 2.0f; - for (int x = 0; x < STUDIOLIGHT_ICON_SIZE; x++) { - float dx = (x + 0.5f) / (float)STUDIOLIGHT_ICON_SIZE; - dx = dx / STUDIOLIGHT_DIAMETER - (1.0f - STUDIOLIGHT_DIAMETER) / 2.0f; - - uint pixelresult = 0x0; - uint alphamask = alpha_circle_mask(dx, dy, 0.5f - pixel_size, 0.5f); - if (alphamask != 0) { - /* calculate normal */ - float normal[3]; - normal[0] = dx * 2.0f - 1.0f; - normal[1] = -(dy * 2.0f - 1.0f); - float dist = len_v2(normal); - normal[2] = -sqrtf(1.0f - SQUARE(dist)); - SWAP(float, normal[1], normal[2]); - - float color[3]; - studiolight_sample_spherical_harmonics(sl, color, normal); - pixelresult = rgb_to_cpack( - linearrgb_to_srgb(color[0]), - linearrgb_to_srgb(color[1]), - linearrgb_to_srgb(color[2])) | alphamask; - } - icon_buffer[offset++] = pixelresult; + ITER_PIXELS(uint, icon_buffer, 1, + STUDIOLIGHT_ICON_SIZE, + STUDIOLIGHT_ICON_SIZE) + { + float dy = RESCALE_COORD(y); + float dx = RESCALE_COORD(x); + + uint alphamask = alpha_circle_mask(dx, dy, 0.5f - texel_size[0], 0.5f); + if (alphamask != 0) { + float normal[3], color[3]; + sphere_normal_from_uv(normal, dx, dy); + /* We want to see horizon not poles. */ + SWAP(float, normal[1], normal[2]); + normal[1] = -normal[1]; + + studiolight_spherical_harmonics_eval(sl, color, normal); + + *pixel = rgb_to_cpack( + linearrgb_to_srgb(color[0]), + linearrgb_to_srgb(color[1]), + linearrgb_to_srgb(color[2])) | alphamask; + } + else { + *pixel = 0x0; } } + ITER_PIXELS_END; } /* API */ @@ -1057,19 +986,19 @@ void BKE_studiolight_init(void) sl = studiolight_create(STUDIOLIGHT_INTERNAL | STUDIOLIGHT_SPHERICAL_HARMONICS_COEFFICIENTS_CALCULATED | STUDIOLIGHT_ORIENTATION_CAMERA); BLI_strncpy(sl->name, "Default", FILE_MAXFILE); - - copy_v3_fl3(sl->spherical_harmonics_coefs[0], 1.03271556f, 1.07163882f, 1.11193657f); -#if STUDIOLIGHT_SPHERICAL_HARMONICS_LEVEL > 0 - copy_v3_fl3(sl->spherical_harmonics_coefs[1], -0.00480952f, 0.05290511f, 0.16394117f); - copy_v3_fl3(sl->spherical_harmonics_coefs[2], -0.29686999f, -0.27378261f, -0.24797194f); - copy_v3_fl3(sl->spherical_harmonics_coefs[3], 0.47932500f, 0.48242140f, 0.47190312f); + int i = 0; + copy_v3_fl3(sl->spherical_harmonics_coefs[i++], 1.03271556f, 1.07163882f, 1.11193657f); +#if STUDIOLIGHT_SH_BANDS > 1 + copy_v3_fl3(sl->spherical_harmonics_coefs[i++], -0.00480952f, 0.05290511f, 0.16394117f); + copy_v3_fl3(sl->spherical_harmonics_coefs[i++], -0.29686999f, -0.27378261f, -0.24797194f); + copy_v3_fl3(sl->spherical_harmonics_coefs[i++], 0.47932500f, 0.48242140f, 0.47190312f); #endif -#if STUDIOLIGHT_SPHERICAL_HARMONICS_LEVEL > 1 - copy_v3_fl3(sl->spherical_harmonics_coefs[4], -0.00576984f, 0.00504886f, 0.01640534f); - copy_v3_fl3(sl->spherical_harmonics_coefs[5], 0.15500379f, 0.15415503f, 0.16244425f); - copy_v3_fl3(sl->spherical_harmonics_coefs[6], -0.02483751f, -0.02245096f, -0.00536885f); - copy_v3_fl3(sl->spherical_harmonics_coefs[7], 0.11155496f, 0.11005443f, 0.10839636f); - copy_v3_fl3(sl->spherical_harmonics_coefs[8], 0.01363425f, 0.01278363f, -0.00159006f); +#if STUDIOLIGHT_SH_BANDS > 2 + copy_v3_fl3(sl->spherical_harmonics_coefs[i++], -0.00576984f, 0.00504886f, 0.01640534f); + copy_v3_fl3(sl->spherical_harmonics_coefs[i++], 0.15500379f, 0.15415503f, 0.16244425f); + copy_v3_fl3(sl->spherical_harmonics_coefs[i++], -0.02483751f, -0.02245096f, -0.00536885f); + copy_v3_fl3(sl->spherical_harmonics_coefs[i++], 0.11155496f, 0.11005443f, 0.10839636f); + copy_v3_fl3(sl->spherical_harmonics_coefs[i++], 0.01363425f, 0.01278363f, -0.00159006f); #endif BLI_addtail(&studiolights, sl); @@ -1171,7 +1100,7 @@ void BKE_studiolight_ensure_flag(StudioLight *sl, int flag) } if ((flag & STUDIOLIGHT_EXTERNAL_IMAGE_LOADED)) { - studiolight_load_equirectangular_image(sl); + studiolight_load_equirect_image(sl); } if ((flag & STUDIOLIGHT_RADIANCE_BUFFERS_CALCULATED)) { studiolight_calculate_radiance_cubemap_buffers(sl); @@ -1181,18 +1110,15 @@ void BKE_studiolight_ensure_flag(StudioLight *sl, int flag) studiolight_calculate_diffuse_light(sl); } } - if ((flag & STUDIOLIGHT_EQUIRECTANGULAR_RADIANCE_GPUTEXTURE)) { - studiolight_create_equirectangular_radiance_gputexture(sl); - } - if ((flag & STUDIOLIGHT_LIGHT_DIRECTION_CALCULATED)) { - studiolight_calculate_light_direction(sl); + if ((flag & STUDIOLIGHT_EQUIRECT_RADIANCE_GPUTEXTURE)) { + studiolight_create_equirect_radiance_gputexture(sl); } - if ((flag & STUDIOLIGHT_EQUIRECTANGULAR_IRRADIANCE_GPUTEXTURE)) { - studiolight_create_equirectangular_irradiance_gputexture(sl); + if ((flag & STUDIOLIGHT_EQUIRECT_IRRADIANCE_GPUTEXTURE)) { + studiolight_create_equirect_irradiance_gputexture(sl); } - if ((flag & STUDIOLIGHT_EQUIRECTANGULAR_IRRADIANCE_IMAGE_CALCULATED)) { - if (!studiolight_load_irradiance_equirectangular_image(sl)) { - studiolight_calculate_irradiance_equirectangular_image(sl); + if ((flag & STUDIOLIGHT_EQUIRECT_IRRADIANCE_IMAGE_CALCULATED)) { + if (!studiolight_load_irradiance_equirect_image(sl)) { + studiolight_calculate_irradiance_equirect_image(sl); } } } -- cgit v1.2.3