Welcome to mirror list, hosted at ThFree Co, Russian Federation.

git.blender.org/blender.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorClément Foucault <foucault.clem@gmail.com>2018-11-19 03:01:43 +0300
committerClément Foucault <foucault.clem@gmail.com>2018-11-19 17:48:17 +0300
commitee44dd1b2bcff6bbd99084a012a40fb4a7100b2c (patch)
tree36975c1ff675bf4b57f3750105bac4ae02eafa24 /source/blender/blenkernel/intern/studiolight.c
parent8d51e3c062dda1317586d744fcb8ff223ec0939a (diff)
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.
Diffstat (limited to 'source/blender/blenkernel/intern/studiolight.c')
-rw-r--r--source/blender/blenkernel/intern/studiolight.c930
1 files changed, 428 insertions, 502 deletions
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);
}
}
}