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.
This commit is contained in:
Clément Foucault
2018-11-19 01:01:43 +01:00
parent 8d51e3c062
commit ee44dd1b2b
11 changed files with 485 additions and 587 deletions

View File

@@ -61,43 +61,36 @@
#define STUDIOLIGHT_ICON_SIZE 96
#define STUDIOLIGHT_SPHERICAL_HARMONICS_LEVEL 2
#define STUDIOLIGHT_SPHERICAL_HARMONICS_MAX_COMPONENTS 18
/* Only 1 - 5 is supported */
#define STUDIOLIGHT_SH_BANDS 3
#if STUDIOLIGHT_SPHERICAL_HARMONICS_LEVEL == 0
# define STUDIOLIGHT_SPHERICAL_HARMONICS_COMPONENTS 1
#define STUDIOLIGHT_SH_COEFS_LEN (STUDIOLIGHT_SH_BANDS * STUDIOLIGHT_SH_BANDS)
#if STUDIOLIGHT_SH_BANDS > 3
/* Bypass L3 */
#define STUDIOLIGHT_SH_EFFECTIVE_COEFS_LEN (STUDIOLIGHT_SH_COEFS_LEN - 7)
#else
#define STUDIOLIGHT_SH_EFFECTIVE_COEFS_LEN STUDIOLIGHT_SH_COEFS_LEN
#endif
#if STUDIOLIGHT_SPHERICAL_HARMONICS_LEVEL == 1
# define STUDIOLIGHT_SPHERICAL_HARMONICS_COMPONENTS 4
#endif
#if STUDIOLIGHT_SPHERICAL_HARMONICS_LEVEL == 2
# define STUDIOLIGHT_SPHERICAL_HARMONICS_COMPONENTS 9
# define STUDIOLIGHT_SPHERICAL_HARMONICS_WINDOWING_TARGET_LAMPLACIAN 10.0f
#endif
#if STUDIOLIGHT_SPHERICAL_HARMONICS_LEVEL == 4
# define STUDIOLIGHT_SPHERICAL_HARMONICS_COMPONENTS 18
#endif
struct GPUTexture;
struct StudioLight;
/* StudioLight.flag */
enum StudioLightFlag {
STUDIOLIGHT_SPHERICAL_HARMONICS_COEFFICIENTS_CALCULATED = (1 << 0),
STUDIOLIGHT_LIGHT_DIRECTION_CALCULATED = (1 << 1),
/* STUDIOLIGHT_LIGHT_DIRECTION_CALCULATED = (1 << 1), */
STUDIOLIGHT_INTERNAL = (1 << 2),
STUDIOLIGHT_EXTERNAL_FILE = (1 << 3),
STUDIOLIGHT_USER_DEFINED = (1 << 12),
STUDIOLIGHT_ORIENTATION_CAMERA = (1 << 4),
STUDIOLIGHT_ORIENTATION_WORLD = (1 << 5),
STUDIOLIGHT_ORIENTATION_VIEWNORMAL = (1 << 6),
STUDIOLIGHT_EXTERNAL_IMAGE_LOADED = (1 << 7),
STUDIOLIGHT_EQUIRECTANGULAR_IRRADIANCE_IMAGE_CALCULATED = (1 << 8),
STUDIOLIGHT_EQUIRECTANGULAR_RADIANCE_GPUTEXTURE = (1 << 9),
STUDIOLIGHT_EQUIRECTANGULAR_IRRADIANCE_GPUTEXTURE = (1 << 10),
STUDIOLIGHT_EQUIRECT_IRRADIANCE_IMAGE_CALCULATED = (1 << 8),
STUDIOLIGHT_EQUIRECT_RADIANCE_GPUTEXTURE = (1 << 9),
STUDIOLIGHT_EQUIRECT_IRRADIANCE_GPUTEXTURE = (1 << 10),
STUDIOLIGHT_RADIANCE_BUFFERS_CALCULATED = (1 << 11),
STUDIOLIGHT_USER_DEFINED = (1 << 12),
STUDIOLIGHT_UI_EXPANDED = (1 << 13),
};
@@ -121,14 +114,13 @@ typedef struct StudioLight {
int icon_id_radiance;
int icon_id_matcap;
int icon_id_matcap_flipped;
float spherical_harmonics_coefs[STUDIOLIGHT_SPHERICAL_HARMONICS_COMPONENTS][3];
float spherical_harmonics_coefs[STUDIOLIGHT_SH_EFFECTIVE_COEFS_LEN][3];
float light_direction[3];
ImBuf *equirectangular_radiance_buffer;
ImBuf *equirectangular_irradiance_buffer;
ImBuf *equirect_radiance_buffer;
ImBuf *equirect_irradiance_buffer;
ImBuf *radiance_cubemap_buffers[6];
struct GPUTexture *equirectangular_radiance_gputexture;
struct GPUTexture *equirectangular_irradiance_gputexture;
float *gpu_matcap_3components; /* 3 channel buffer for GPU_R11F_G11F_B10F */
struct GPUTexture *equirect_radiance_gputexture;
struct GPUTexture *equirect_irradiance_gputexture;
/*
* Free function to clean up the running icons previews (wmJob) the usage is in

View File

@@ -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);
}
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)
{
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;
}
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_load_equirectangular_image(StudioLight *sl)
static void cube_face_uv_to_direction(float r_dir[3], float x, float y, int face)
{
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_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 *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 (*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);
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;
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;
}
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;
float *colbuf = MEM_mallocN(STUDIOLIGHT_IRRADIANCE_EQUIRECT_WIDTH * STUDIOLIGHT_IRRADIANCE_EQUIRECT_HEIGHT * sizeof(float[4]), __func__);
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);
#if STUDIOLIGHT_IRRADIANCE_METHOD == STUDIOLIGHT_IRRADIANCE_METHOD_RADIANCE
studiolight_calculate_specular_irradiance(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
#if STUDIOLIGHT_IRRADIANCE_METHOD == STUDIOLIGHT_IRRADIANCE_METHOD_SPHERICAL_HARMONICS
studiolight_sample_spherical_harmonics(sl, color, 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;
ITER_PIXELS(uint, icon_buffer, 1,
STUDIOLIGHT_ICON_SIZE,
STUDIOLIGHT_ICON_SIZE)
{
float dy = RESCALE_COORD(y);
float dx = RESCALE_COORD(x);
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 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];
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};
studiolight_calculate_radiance(sl->equirect_radiance_buffer, color, direction);
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;
*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;
ImBuf *ibuf = sl->equirect_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);
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;
ITER_PIXELS(uint, icon_buffer, 1,
STUDIOLIGHT_ICON_SIZE,
STUDIOLIGHT_ICON_SIZE)
{
float dy = RESCALE_COORD(y);
float dx = RESCALE_COORD(x);
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 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];
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]);
studiolight_spherical_harmonics_eval(sl, color, normal);
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;
*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_EQUIRECT_RADIANCE_GPUTEXTURE)) {
studiolight_create_equirect_radiance_gputexture(sl);
}
if ((flag & STUDIOLIGHT_LIGHT_DIRECTION_CALCULATED)) {
studiolight_calculate_light_direction(sl);
if ((flag & STUDIOLIGHT_EQUIRECT_IRRADIANCE_GPUTEXTURE)) {
studiolight_create_equirect_irradiance_gputexture(sl);
}
if ((flag & STUDIOLIGHT_EQUIRECTANGULAR_IRRADIANCE_GPUTEXTURE)) {
studiolight_create_equirectangular_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);
}
}
}

View File

@@ -123,12 +123,13 @@ void EEVEE_lookdev_cache_init(
if (!pinfo) {
/* Do not fadeout when doing probe rendering, only when drawing the background */
DRW_shgroup_uniform_float(*grp, "studioLightBackground", &v3d->shading.studiolight_background, 1);
BKE_studiolight_ensure_flag(sl, STUDIOLIGHT_EQUIRECTANGULAR_IRRADIANCE_GPUTEXTURE);
tex = sl->equirectangular_irradiance_gputexture;
BKE_studiolight_ensure_flag(sl, STUDIOLIGHT_EQUIRECT_IRRADIANCE_GPUTEXTURE);
tex = sl->equirect_irradiance_gputexture;
}
else {
BKE_studiolight_ensure_flag(sl, STUDIOLIGHT_EQUIRECTANGULAR_RADIANCE_GPUTEXTURE);
tex = sl->equirectangular_radiance_gputexture;
DRW_shgroup_uniform_float_copy(*grp, "studioLightBackground", 1.0f);
BKE_studiolight_ensure_flag(sl, STUDIOLIGHT_EQUIRECT_RADIANCE_GPUTEXTURE);
tex = sl->equirect_radiance_gputexture;
}
DRW_shgroup_uniform_texture(*grp, "image", tex);

View File

@@ -4,7 +4,7 @@ struct LightData {
};
struct WorldData {
vec3 spherical_harmonics_coefs[STUDIOLIGHT_SPHERICAL_HARMONICS_MAX_COMPONENTS];
vec3 spherical_harmonics_coefs[STUDIOLIGHT_SH_MAX_COMPONENTS];
vec4 background_color_low;
vec4 background_color_high;
vec4 object_outline_color;

View File

@@ -1,59 +1,52 @@
#define BLINN
vec3 spherical_harmonics(vec3 N, vec3 spherical_harmonics_coefs[STUDIOLIGHT_SPHERICAL_HARMONICS_MAX_COMPONENTS])
vec3 spherical_harmonics(vec3 N, vec3 sh_coefs[STUDIOLIGHT_SH_MAX_COMPONENTS])
{
vec3 sh = vec3(0.0);
vec3 sh = 0.282095 * sh_coefs[0];
sh += 0.282095 * spherical_harmonics_coefs[0];
#if STUDIOLIGHT_SPHERICAL_HARMONICS_LEVEL > 0
#if STUDIOLIGHT_SH_BANDS > 1
float nx = N.x;
float ny = N.y;
float nz = N.z;
sh += -0.488603 * nz * spherical_harmonics_coefs[1];
sh += 0.488603 * ny * spherical_harmonics_coefs[2];
sh += -0.488603 * nx * spherical_harmonics_coefs[3];
sh += -0.488603 * nz * sh_coefs[1];
sh += 0.488603 * ny * sh_coefs[2];
sh += -0.488603 * nx * sh_coefs[3];
#endif
#if STUDIOLIGHT_SPHERICAL_HARMONICS_LEVEL > 1
#if STUDIOLIGHT_SH_BANDS > 2
float nx2 = nx * nx;
float ny2 = ny * ny;
float nz2 = nz * nz;
sh += 1.092548 * nx * nz * spherical_harmonics_coefs[4];
sh += -1.092548 * nz * ny * spherical_harmonics_coefs[5];
sh += 0.315392 * (3.0 * ny2 - 1.0) * spherical_harmonics_coefs[6];
sh += -1.092548 * nx * ny * spherical_harmonics_coefs[7];
sh += 0.546274 * (nx2 - nz2) * spherical_harmonics_coefs[8];
sh += 1.092548 * nx * nz * sh_coefs[4];
sh += -1.092548 * nz * ny * sh_coefs[5];
sh += 0.315392 * (3.0 * ny2 - 1.0) * sh_coefs[6];
sh += -1.092548 * nx * ny * sh_coefs[7];
sh += 0.546274 * (nx2 - nz2) * sh_coefs[8];
#endif
#if STUDIOLIGHT_SPHERICAL_HARMONICS_LEVEL > 3
#if STUDIOLIGHT_SH_BANDS > 4
float nx4 = nx2 * nx2;
float ny4 = ny2 * ny2;
float nz4 = nz2 * nz2;
sh += (2.5033429417967046 * nx * nz * (nx2 - nz2)) * spherical_harmonics_coefs[9];
sh += (-1.7701307697799304 * nz * ny * (3.0 * nx2 - nz2)) * spherical_harmonics_coefs[10];
sh += (0.9461746957575601 * nz * nx * (-1.0 +7.0*ny2)) * spherical_harmonics_coefs[11];
sh += (-0.6690465435572892 * nz * ny * (-3.0 + 7.0 * ny2)) * spherical_harmonics_coefs[12];
sh += ((105.0*ny4-90.0*ny2+9.0)/28.359261614) * spherical_harmonics_coefs[13];
sh += (-0.6690465435572892 * nx * ny * (-3.0 + 7.0 * ny2)) * spherical_harmonics_coefs[14];
sh += (0.9461746957575601 * (nx2 - nz2) * (-1.0 + 7.0 * ny2)) * spherical_harmonics_coefs[15];
sh += (-1.7701307697799304 * nx * ny * (nx2 - 3.0 * nz2)) * spherical_harmonics_coefs[16];
sh += (0.6258357354491761 * (nx4 - 6.0 * nz2 * nx2 + nz4)) * spherical_harmonics_coefs[17];
sh += (2.5033429417967046 * nx * nz * (nx2 - nz2)) * sh_coefs[9];
sh += (-1.7701307697799304 * nz * ny * (3.0 * nx2 - nz2)) * sh_coefs[10];
sh += (0.9461746957575601 * nz * nx * (-1.0 +7.0*ny2)) * sh_coefs[11];
sh += (-0.6690465435572892 * nz * ny * (-3.0 + 7.0 * ny2)) * sh_coefs[12];
sh += ((105.0*ny4-90.0*ny2+9.0)/28.359261614) * sh_coefs[13];
sh += (-0.6690465435572892 * nx * ny * (-3.0 + 7.0 * ny2)) * sh_coefs[14];
sh += (0.9461746957575601 * (nx2 - nz2) * (-1.0 + 7.0 * ny2)) * sh_coefs[15];
sh += (-1.7701307697799304 * nx * ny * (nx2 - 3.0 * nz2)) * sh_coefs[16];
sh += (0.6258357354491761 * (nx4 - 6.0 * nz2 * nx2 + nz4)) * sh_coefs[17];
#endif
return sh;
}
vec3 get_world_diffuse_light(WorldData world_data, vec3 N)
{
return (spherical_harmonics(vec3(N.x, N.y, -N.z), world_data.spherical_harmonics_coefs));
return spherical_harmonics(N, world_data.spherical_harmonics_coefs);
}
vec3 get_camera_diffuse_light(WorldData world_data, vec3 N)
{
return (spherical_harmonics(vec3(N.x, -N.z, -N.y), world_data.spherical_harmonics_coefs));
return spherical_harmonics(vec3(N.x, -N.z, N.y), world_data.spherical_harmonics_coefs);
}
/* N And I are in View Space. */

View File

@@ -510,8 +510,8 @@ static void workbench_composite_uniforms(WORKBENCH_PrivateData *wpd, DRWShadingG
DRW_shgroup_uniform_vec2(grp, "invertedViewportSize", DRW_viewport_invert_size_get(), 1);
if (STUDIOLIGHT_ORIENTATION_VIEWNORMAL_ENABLED(wpd)) {
BKE_studiolight_ensure_flag(wpd->studio_light, STUDIOLIGHT_EQUIRECTANGULAR_RADIANCE_GPUTEXTURE);
DRW_shgroup_uniform_texture(grp, "matcapImage", wpd->studio_light->equirectangular_radiance_gputexture);
BKE_studiolight_ensure_flag(wpd->studio_light, STUDIOLIGHT_EQUIRECT_RADIANCE_GPUTEXTURE);
DRW_shgroup_uniform_texture(grp, "matcapImage", wpd->studio_light->equirect_radiance_gputexture );
}
workbench_material_set_normal_world_matrix(grp, wpd, e_data.normal_world_matrix);

View File

@@ -169,8 +169,8 @@ static WORKBENCH_MaterialData *get_or_create_material_data(
workbench_material_set_normal_world_matrix(grp, wpd, e_data.normal_world_matrix);
workbench_material_copy(material, &material_template);
if (STUDIOLIGHT_ORIENTATION_VIEWNORMAL_ENABLED(wpd)) {
BKE_studiolight_ensure_flag(wpd->studio_light, STUDIOLIGHT_EQUIRECTANGULAR_RADIANCE_GPUTEXTURE);
DRW_shgroup_uniform_texture(grp, "matcapImage", wpd->studio_light->equirectangular_radiance_gputexture);
BKE_studiolight_ensure_flag(wpd->studio_light, STUDIOLIGHT_EQUIRECT_RADIANCE_GPUTEXTURE);
DRW_shgroup_uniform_texture(grp, "matcapImage", wpd->studio_light->equirect_radiance_gputexture );
}
if (SPECULAR_HIGHLIGHT_ENABLED(wpd) || MATCAP_ENABLED(wpd)) {
DRW_shgroup_uniform_vec2(grp, "invertedViewportSize", DRW_viewport_invert_size_get(), 1);
@@ -456,8 +456,8 @@ static void workbench_forward_cache_populate_particles(WORKBENCH_Data *vedata, O
float hair_alpha = XRAY_ALPHA(wpd) * 0.33f;
DRW_shgroup_uniform_float_copy(shgrp, "alpha", hair_alpha);
if (STUDIOLIGHT_ORIENTATION_VIEWNORMAL_ENABLED(wpd)) {
BKE_studiolight_ensure_flag(wpd->studio_light, STUDIOLIGHT_EQUIRECTANGULAR_RADIANCE_GPUTEXTURE);
DRW_shgroup_uniform_texture(shgrp, "matcapImage", wpd->studio_light->equirectangular_radiance_gputexture);
BKE_studiolight_ensure_flag(wpd->studio_light, STUDIOLIGHT_EQUIRECT_RADIANCE_GPUTEXTURE);
DRW_shgroup_uniform_texture(shgrp, "matcapImage", wpd->studio_light->equirect_radiance_gputexture );
}
if (SPECULAR_HIGHLIGHT_ENABLED(wpd) || MATCAP_ENABLED(wpd)) {
DRW_shgroup_uniform_vec2(shgrp, "invertedViewportSize", DRW_viewport_invert_size_get(), 1);

View File

@@ -91,19 +91,8 @@ char *workbench_material_build_defines(WORKBENCH_PrivateData *wpd, bool use_text
BLI_dynstr_appendf(ds, "#define HAIR_SHADER\n");
}
#if STUDIOLIGHT_SPHERICAL_HARMONICS_LEVEL == 0
BLI_dynstr_appendf(ds, "#define STUDIOLIGHT_SPHERICAL_HARMONICS_LEVEL 0\n");
#endif
#if STUDIOLIGHT_SPHERICAL_HARMONICS_LEVEL == 1
BLI_dynstr_appendf(ds, "#define STUDIOLIGHT_SPHERICAL_HARMONICS_LEVEL 1\n");
#endif
#if STUDIOLIGHT_SPHERICAL_HARMONICS_LEVEL == 2
BLI_dynstr_appendf(ds, "#define STUDIOLIGHT_SPHERICAL_HARMONICS_LEVEL 2\n");
#endif
#if STUDIOLIGHT_SPHERICAL_HARMONICS_LEVEL == 4
BLI_dynstr_appendf(ds, "#define STUDIOLIGHT_SPHERICAL_HARMONICS_LEVEL 4\n");
#endif
BLI_dynstr_appendf(ds, "#define STUDIOLIGHT_SPHERICAL_HARMONICS_MAX_COMPONENTS 18\n");
BLI_dynstr_appendf(ds, "#define STUDIOLIGHT_SH_BANDS %d\n", STUDIOLIGHT_SH_BANDS);
BLI_dynstr_appendf(ds, "#define STUDIOLIGHT_SH_MAX_COMPONENTS %d\n", STUDIOLIGHT_SH_EFFECTIVE_COEFS_LEN);
str = BLI_dynstr_get_cstring(ds);
BLI_dynstr_free(ds);

View File

@@ -139,7 +139,7 @@ typedef struct WORKBENCH_UBO_Light {
} WORKBENCH_UBO_Light;
typedef struct WORKBENCH_UBO_World {
float spherical_harmonics_coefs[STUDIOLIGHT_SPHERICAL_HARMONICS_MAX_COMPONENTS][4];
float spherical_harmonics_coefs[STUDIOLIGHT_SH_EFFECTIVE_COEFS_LEN][4];
float background_color_low[4];
float background_color_high[4];
float object_outline_color[4];

View File

@@ -34,15 +34,12 @@
void studiolight_update_world(StudioLight *sl, WORKBENCH_UBO_World *wd)
{
int i;
BKE_studiolight_ensure_flag(sl, STUDIOLIGHT_SPHERICAL_HARMONICS_COEFFICIENTS_CALCULATED);
for (i = 0; i < STUDIOLIGHT_SPHERICAL_HARMONICS_COMPONENTS; i++) {
for (int i = 0; i < STUDIOLIGHT_SH_EFFECTIVE_COEFS_LEN; i++) {
/* Can't memcpy because of alignment */
copy_v3_v3(wd->spherical_harmonics_coefs[i], sl->spherical_harmonics_coefs[i]);
}
for (; i < STUDIOLIGHT_SPHERICAL_HARMONICS_MAX_COMPONENTS; i++) {
copy_v3_fl(wd->spherical_harmonics_coefs[i], 0.0);
}
}
static void compute_parallel_lines_nor_and_dist(const float v1[2], const float v2[2], const float v3[2], float r_line[2])

View File

@@ -726,7 +726,7 @@ static void rna_UserDef_studiolight_spherical_harmonics_coefficients_get(Pointer
{
StudioLight *sl = (StudioLight *)ptr->data;
float *value = values;
for (int i = 0; i < STUDIOLIGHT_SPHERICAL_HARMONICS_COMPONENTS; i++) {
for (int i = 0; i < STUDIOLIGHT_SH_EFFECTIVE_COEFS_LEN; i++) {
copy_v3_v3(value, sl->spherical_harmonics_coefs[i]);
value += 3;
}
@@ -3379,7 +3379,7 @@ static void rna_def_userdef_studiolight(BlenderRNA *brna)
RNA_def_property_ui_text(prop, "SH Cache Path", "Path where the spherical harmonics cache is stored");
RNA_def_property_clear_flag(prop, PROP_EDITABLE);
const int spherical_harmonics_dim[] = {STUDIOLIGHT_SPHERICAL_HARMONICS_COMPONENTS, 3};
const int spherical_harmonics_dim[] = {STUDIOLIGHT_SH_EFFECTIVE_COEFS_LEN, 3};
prop = RNA_def_property(srna, "spherical_harmonics_coefficients", PROP_FLOAT, PROP_COLOR);
RNA_def_property_multi_array(prop, 2, spherical_harmonics_dim);
RNA_def_property_clear_flag(prop, PROP_EDITABLE);