From 66700196074ad168f3322f2766846a0a07f7a00f Mon Sep 17 00:00:00 2001 From: Jeroen Bakker Date: Tue, 27 Aug 2019 08:42:50 +0200 Subject: Workbench: Specular Highlighting for MatCaps With Blender 2.80 we introduced a more flexible matcap system. One change we did was to multiply the matcap with the base color that was shaded. As matcaps contains diffuse and specular lighting in a single texture this lead to rendering artifacts. Artists were complaining that everything looked to metalic. We now support a separate `diffuse` and `specular` pass for matcaps. `shaded_color = diffuse_light * base_color + specular_light` For matcaps to support this feature they need to be multilayer openexr files with 2 renderpasses (named `diffuse` and `specular`). In the future we can change this to first pass/second pass in stead of this naming convention. Reviewed By: fclem, brecht Differential Revision: https://developer.blender.org/D5335 --- source/blender/blenkernel/BKE_studiolight.h | 12 + source/blender/blenkernel/intern/studiolight.c | 300 +++++++++++++++++---- .../shaders/workbench_deferred_composite_frag.glsl | 14 +- .../workbench_forward_transparent_accum_frag.glsl | 12 +- .../draw/engines/workbench/workbench_deferred.c | 14 +- .../draw/engines/workbench/workbench_forward.c | 24 +- .../draw/engines/workbench/workbench_materials.c | 12 +- .../draw/engines/workbench/workbench_private.h | 17 +- source/blender/gpu/intern/gpu_draw.c | 6 +- source/blender/imbuf/IMB_imbuf.h | 3 +- source/blender/imbuf/intern/allocimbuf.c | 4 +- source/blender/makesrna/intern/rna_userdef.c | 16 ++ 12 files changed, 346 insertions(+), 88 deletions(-) (limited to 'source') diff --git a/source/blender/blenkernel/BKE_studiolight.h b/source/blender/blenkernel/BKE_studiolight.h index d6fff528348..108e93d9caa 100644 --- a/source/blender/blenkernel/BKE_studiolight.h +++ b/source/blender/blenkernel/BKE_studiolight.h @@ -87,6 +87,11 @@ enum StudioLightFlag { STUDIOLIGHT_RADIANCE_BUFFERS_CALCULATED = (1 << 11), STUDIOLIGHT_USER_DEFINED = (1 << 12), STUDIOLIGHT_UI_EXPANDED = (1 << 13), + + STUDIOLIGHT_MATCAP_DIFFUSE_GPUTEXTURE = (1 << 14), + STUDIOLIGHT_MATCAP_SPECULAR_GPUTEXTURE = (1 << 15), + /* Is set for studio lights and matcaps with specular highlight pass. */ + STUDIOLIGHT_SPECULAR_HIGHLIGHT_PASS = (1 << 16), }; #define STUDIOLIGHT_FLAG_ALL (STUDIOLIGHT_INTERNAL | STUDIOLIGHT_EXTERNAL_FILE) @@ -97,6 +102,11 @@ enum StudioLightFlag { typedef void StudioLightFreeFunction(struct StudioLight *, void *data); +typedef struct StudioLightImage { + ImBuf *ibuf; + struct GPUTexture *gputexture; +} StudioLightImage; + typedef struct StudioLight { struct StudioLight *next, *prev; @@ -112,6 +122,8 @@ typedef struct StudioLight { int icon_id_matcap_flipped; float spherical_harmonics_coefs[STUDIOLIGHT_SH_EFFECTIVE_COEFS_LEN][3]; float light_direction[3]; + StudioLightImage matcap_diffuse; + StudioLightImage matcap_specular; ImBuf *equirect_radiance_buffer; ImBuf *equirect_irradiance_buffer; ImBuf *radiance_cubemap_buffers[6]; diff --git a/source/blender/blenkernel/intern/studiolight.c b/source/blender/blenkernel/intern/studiolight.c index 6e83f5d75e2..8466f9eaa98 100644 --- a/source/blender/blenkernel/intern/studiolight.c +++ b/source/blender/blenkernel/intern/studiolight.c @@ -46,13 +46,16 @@ #include "MEM_guardedalloc.h" +#include "intern/openexr/openexr_multi.h" + /* Statics */ static ListBase studiolights; static int last_studiolight_id = 0; #define STUDIOLIGHT_RADIANCE_CUBEMAP_SIZE 96 #define STUDIOLIGHT_IRRADIANCE_EQUIRECT_HEIGHT 32 #define STUDIOLIGHT_IRRADIANCE_EQUIRECT_WIDTH (STUDIOLIGHT_IRRADIANCE_EQUIRECT_HEIGHT * 2) - +#define STUDIOLIGHT_PASSNAME_DIFFUSE "diffuse" +#define STUDIOLIGHT_PASSNAME_SPECULAR "specular" /* * The method to calculate the irradiance buffers * The irradiance buffer is only shown in the background when in LookDev. @@ -152,6 +155,10 @@ static void studiolight_free(struct StudioLight *sl) GPU_TEXTURE_SAFE_FREE(sl->equirect_irradiance_gputexture); IMB_SAFE_FREE(sl->equirect_radiance_buffer); IMB_SAFE_FREE(sl->equirect_irradiance_buffer); + GPU_TEXTURE_SAFE_FREE(sl->matcap_diffuse.gputexture); + GPU_TEXTURE_SAFE_FREE(sl->matcap_specular.gputexture); + IMB_SAFE_FREE(sl->matcap_diffuse.ibuf); + IMB_SAFE_FREE(sl->matcap_specular.ibuf); MEM_SAFE_FREE(sl->path_irr_cache); MEM_SAFE_FREE(sl->path_sh_cache); MEM_SAFE_FREE(sl); @@ -342,72 +349,232 @@ static void cube_face_uv_to_direction(float r_dir[3], float x, float y, int face normalize_v3(r_dir); } +typedef struct MultilayerConvertContext { + int num_diffuse_channels; + float *diffuse_pass; + int num_specular_channels; + float *specular_pass; +} MultilayerConvertContext; + +static void *studiolight_multilayer_addview(void *UNUSED(base), const char *UNUSED(view_name)) +{ + return NULL; +} +static void *studiolight_multilayer_addlayer(void *base, const char *UNUSED(layer_name)) +{ + return base; +} + +/* Convert a multilayer pass to ImBuf channel 4 float buffer. + * NOTE: Parameter rect will become invalid. Do not use rect after calling this + * function */ +static float *studiolight_multilayer_convert_pass(ImBuf *ibuf, + float *rect, + const unsigned int channels) +{ + if (channels == 4) { + return rect; + } + else { + float *new_rect = MEM_callocN(sizeof(float[4]) * ibuf->x * ibuf->y, __func__); + + IMB_buffer_float_from_float(new_rect, + rect, + channels, + IB_PROFILE_LINEAR_RGB, + IB_PROFILE_LINEAR_RGB, + false, + ibuf->x, + ibuf->y, + ibuf->x, + ibuf->x); + + MEM_freeN(rect); + return new_rect; + } +} + +static void studiolight_multilayer_addpass(void *base, + void *UNUSED(lay), + const char *pass_name, + float *rect, + int num_channels, + const char *UNUSED(chan_id), + const char *UNUSED(view_name)) +{ + MultilayerConvertContext *ctx = base; + /* NOTE: This function must free pass pixels data if it is not used, this + * is how IMB_exr_multilayer_convert() is working. */ + /* If we've found a first combined pass, skip all the rest ones. */ + if (STREQ(pass_name, STUDIOLIGHT_PASSNAME_DIFFUSE)) { + ctx->diffuse_pass = rect; + ctx->num_diffuse_channels = num_channels; + } + else if (STREQ(pass_name, STUDIOLIGHT_PASSNAME_SPECULAR)) { + ctx->specular_pass = rect; + ctx->num_specular_channels = num_channels; + } + else { + MEM_freeN(rect); + } +} + static void studiolight_load_equirect_image(StudioLight *sl) { if (sl->flag & STUDIOLIGHT_EXTERNAL_FILE) { - ImBuf *ibuf = NULL; - ibuf = IMB_loadiffname(sl->path, 0, NULL); - if (ibuf == NULL) { - float *colbuf = MEM_mallocN(sizeof(float[4]), __func__); - copy_v4_fl4(colbuf, 1.0f, 0.0f, 1.0f, 1.0f); - ibuf = IMB_allocFromBuffer(NULL, colbuf, 1, 1); + ImBuf *ibuf = IMB_loadiffname(sl->path, IB_multilayer, NULL); + ImBuf *specular_ibuf = NULL; + ImBuf *diffuse_ibuf = NULL; + const bool failed = (ibuf == NULL); + + if (ibuf) { + if (ibuf->ftype == IMB_FTYPE_OPENEXR && ibuf->userdata) { + /* the read file is a multilayered openexr file (userdata != NULL) + * This file is currently only supported for MATCAPS where + * the first found 'diffuse' pass will be used for diffuse lighting + * and the first found 'specular' pass will be used for specular lighting */ + MultilayerConvertContext ctx = {}; + IMB_exr_multilayer_convert(ibuf->userdata, + &ctx, + &studiolight_multilayer_addview, + &studiolight_multilayer_addlayer, + &studiolight_multilayer_addpass); + + /* `ctx.diffuse_pass` and `ctx.specular_pass` can be freed inside + * `studiolight_multilayer_convert_pass` when conversion happens. + * When not converted we move the ownership of the buffer to the + * `converted_pass`. We only need to free `converted_pass` as it holds + * the unmodified allocation from the `ctx.*_pass` or the converted data. + */ + if (ctx.diffuse_pass != NULL) { + float *converted_pass = studiolight_multilayer_convert_pass( + ibuf, ctx.diffuse_pass, ctx.num_diffuse_channels); + diffuse_ibuf = IMB_allocFromBuffer( + NULL, converted_pass, ibuf->x, ibuf->y, ctx.num_diffuse_channels); + MEM_freeN(converted_pass); + } + + if (ctx.specular_pass != NULL) { + float *converted_pass = studiolight_multilayer_convert_pass( + ibuf, ctx.specular_pass, ctx.num_specular_channels); + specular_ibuf = IMB_allocFromBuffer( + NULL, converted_pass, ibuf->x, ibuf->y, ctx.num_specular_channels); + MEM_freeN(converted_pass); + } + + IMB_exr_close(ibuf->userdata); + ibuf->userdata = NULL; + IMB_freeImBuf(ibuf); + ibuf = NULL; + } + else { + /* read file is an single layer openexr file or the read file isn't + * an openexr file */ + IMB_float_from_rect(ibuf); + diffuse_ibuf = ibuf; + ibuf = NULL; + } + } + + if (diffuse_ibuf == NULL) { + /* Create 1x1 diffuse buffer, in case image failed to load or if there was + * only a specular pass in the multilayer file or no passes were found. */ + const float black[4] = {0.0f, 0.0f, 0.0f, 1.0f}; + const float magenta[4] = {1.0f, 0.0f, 1.0f, 1.0f}; + diffuse_ibuf = IMB_allocFromBuffer( + NULL, (failed || (specular_ibuf == NULL)) ? magenta : black, 1, 1, 4); + } + + if ((sl->flag & STUDIOLIGHT_TYPE_MATCAP)) { + sl->matcap_diffuse.ibuf = diffuse_ibuf; + sl->matcap_specular.ibuf = specular_ibuf; + if (specular_ibuf != NULL) { + sl->flag |= STUDIOLIGHT_SPECULAR_HIGHLIGHT_PASS; + } + } + else { + sl->equirect_radiance_buffer = diffuse_ibuf; + if (specular_ibuf != NULL) { + IMB_freeImBuf(specular_ibuf); + } } - IMB_float_from_rect(ibuf); - sl->equirect_radiance_buffer = ibuf; } + sl->flag |= STUDIOLIGHT_EXTERNAL_IMAGE_LOADED; } 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->equirect_radiance_buffer; - if (sl->flag & STUDIOLIGHT_TYPE_MATCAP) { - float *gpu_matcap_3components = MEM_callocN(sizeof(float[3]) * ibuf->x * ibuf->y, __func__); + sl->equirect_radiance_gputexture = GPU_texture_create_2d( + ibuf->x, ibuf->y, GPU_RGBA16F, ibuf->rect_float, NULL); + 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_EQUIRECT_RADIANCE_GPUTEXTURE; +} - 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); - } +static void studiolight_create_matcap_gputexture(StudioLightImage *sli) +{ + BLI_assert(sli->ibuf); + ImBuf *ibuf = sli->ibuf; + 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); + } + + sli->gputexture = GPU_texture_create_nD(ibuf->x, + ibuf->y, + 0, + 2, + gpu_matcap_3components, + GPU_R11F_G11F_B10F, + GPU_DATA_FLOAT, + 0, + false, + NULL); + MEM_SAFE_FREE(gpu_matcap_3components); +} - 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); +static void studiolight_create_matcap_diffuse_gputexture(StudioLight *sl) +{ + if (sl->flag & STUDIOLIGHT_EXTERNAL_FILE) { + if (sl->flag & STUDIOLIGHT_TYPE_MATCAP) { + BKE_studiolight_ensure_flag(sl, STUDIOLIGHT_EXTERNAL_IMAGE_LOADED); + studiolight_create_matcap_gputexture(&sl->matcap_diffuse); } - else { - sl->equirect_radiance_gputexture = GPU_texture_create_2d( - ibuf->x, ibuf->y, GPU_RGBA16F, ibuf->rect_float, error); - 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_MATCAP_DIFFUSE_GPUTEXTURE; +} +static void studiolight_create_matcap_specular_gputexture(StudioLight *sl) +{ + if (sl->flag & STUDIOLIGHT_EXTERNAL_FILE) { + if (sl->flag & STUDIOLIGHT_TYPE_MATCAP) { + BKE_studiolight_ensure_flag(sl, STUDIOLIGHT_EXTERNAL_IMAGE_LOADED); + if (sl->matcap_specular.ibuf) { + studiolight_create_matcap_gputexture(&sl->matcap_specular); + } } } - sl->flag |= STUDIOLIGHT_EQUIRECT_RADIANCE_GPUTEXTURE; + sl->flag |= STUDIOLIGHT_MATCAP_SPECULAR_GPUTEXTURE; } static void studiolight_create_equirect_irradiance_gputexture(StudioLight *sl) { if (sl->flag & STUDIOLIGHT_EXTERNAL_FILE) { - char error[256]; 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); + ibuf->x, ibuf->y, GPU_RGBA16F, ibuf->rect_float, NULL); GPUTexture *tex = sl->equirect_irradiance_gputexture; GPU_texture_bind(tex, 0); GPU_texture_filter_mode(tex, true); @@ -457,32 +624,32 @@ static void studiolight_calculate_radiance_cubemap_buffers(StudioLight *sl) /* front */ 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); + NULL, colbuf, STUDIOLIGHT_RADIANCE_CUBEMAP_SIZE, STUDIOLIGHT_RADIANCE_CUBEMAP_SIZE, 4); /* back */ 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); + NULL, colbuf, STUDIOLIGHT_RADIANCE_CUBEMAP_SIZE, STUDIOLIGHT_RADIANCE_CUBEMAP_SIZE, 4); /* left */ 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); + NULL, colbuf, STUDIOLIGHT_RADIANCE_CUBEMAP_SIZE, STUDIOLIGHT_RADIANCE_CUBEMAP_SIZE, 4); /* right */ 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); + NULL, colbuf, STUDIOLIGHT_RADIANCE_CUBEMAP_SIZE, STUDIOLIGHT_RADIANCE_CUBEMAP_SIZE, 4); /* top */ 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); + NULL, colbuf, STUDIOLIGHT_RADIANCE_CUBEMAP_SIZE, STUDIOLIGHT_RADIANCE_CUBEMAP_SIZE, 4); /* bottom */ 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); + NULL, colbuf, STUDIOLIGHT_RADIANCE_CUBEMAP_SIZE, STUDIOLIGHT_RADIANCE_CUBEMAP_SIZE, 4); #if 0 IMB_saveiff(sl->radiance_cubemap_buffers[STUDIOLIGHT_X_POS], @@ -603,7 +770,8 @@ static void studiolight_spherical_harmonics_calculate_coefficients(StudioLight * /* 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 */ + /* From Peter-Pike Sloan's Stupid SH Tricks http://www.ppsloan.org/publications/StupidSH36.pdf + */ float table_l[STUDIOLIGHT_SH_BANDS]; float table_b[STUDIOLIGHT_SH_BANDS]; @@ -720,7 +888,6 @@ BLI_INLINE void studiolight_spherical_harmonics_eval(StudioLight *sl, } return; #else - /* L0 */ mul_v3_v3fl(color, sl->spherical_harmonics_coefs[0], 0.282095f); # if STUDIOLIGHT_SH_BANDS > 1 /* L1 */ @@ -1045,7 +1212,8 @@ static void studiolight_calculate_irradiance_equirect_image(StudioLight *sl) sl->equirect_irradiance_buffer = IMB_allocFromBuffer(NULL, colbuf, STUDIOLIGHT_IRRADIANCE_EQUIRECT_WIDTH, - STUDIOLIGHT_IRRADIANCE_EQUIRECT_HEIGHT); + STUDIOLIGHT_IRRADIANCE_EQUIRECT_HEIGHT, + 4); MEM_freeN(colbuf); #ifdef STUDIOLIGHT_IRRADIANCE_METHOD_RADIANCE @@ -1196,7 +1364,8 @@ static void studiolight_matcap_preview(uint *icon_buffer, StudioLight *sl, bool { BKE_studiolight_ensure_flag(sl, STUDIOLIGHT_EXTERNAL_IMAGE_LOADED); - ImBuf *ibuf = sl->equirect_radiance_buffer; + ImBuf *diffuse_buffer = sl->matcap_diffuse.ibuf; + ImBuf *specular_buffer = sl->matcap_specular.ibuf; ITER_PIXELS (uint, icon_buffer, 1, STUDIOLIGHT_ICON_SIZE, STUDIOLIGHT_ICON_SIZE) { float dy = RESCALE_COORD(y); @@ -1206,7 +1375,15 @@ static void studiolight_matcap_preview(uint *icon_buffer, StudioLight *sl, bool } float color[4]; - nearest_interpolation_color(ibuf, NULL, color, dx * ibuf->x - 1.0f, dy * ibuf->y - 1.0f); + float u = dx * diffuse_buffer->x - 1.0f; + float v = dy * diffuse_buffer->y - 1.0f; + nearest_interpolation_color(diffuse_buffer, NULL, color, u, v); + + if (specular_buffer) { + float specular[4]; + nearest_interpolation_color(specular_buffer, NULL, specular, u, v); + add_v3_v3(color, specular); + } uint alphamask = alpha_circle_mask(dx, dy, 0.5f - texel_size[0], 0.5f); @@ -1303,9 +1480,9 @@ void BKE_studiolight_default(SolidLight lights[4], float light_ambient[4]) void BKE_studiolight_init(void) { /* Add default studio light */ - StudioLight *sl = studiolight_create(STUDIOLIGHT_INTERNAL | - STUDIOLIGHT_SPHERICAL_HARMONICS_COEFFICIENTS_CALCULATED | - STUDIOLIGHT_TYPE_STUDIO); + StudioLight *sl = studiolight_create( + STUDIOLIGHT_INTERNAL | STUDIOLIGHT_SPHERICAL_HARMONICS_COEFFICIENTS_CALCULATED | + STUDIOLIGHT_TYPE_STUDIO | STUDIOLIGHT_SPECULAR_HIGHLIGHT_PASS); BLI_strncpy(sl->name, "Default", FILE_MAXFILE); BLI_addtail(&studiolights, sl); @@ -1317,7 +1494,8 @@ void BKE_studiolight_init(void) if (!BKE_appdir_app_is_portable_install()) { studiolight_add_files_from_datafolder(BLENDER_USER_DATAFILES, STUDIOLIGHT_LIGHTS_FOLDER, - STUDIOLIGHT_TYPE_STUDIO | STUDIOLIGHT_USER_DEFINED); + STUDIOLIGHT_TYPE_STUDIO | STUDIOLIGHT_USER_DEFINED | + STUDIOLIGHT_SPECULAR_HIGHLIGHT_PASS); studiolight_add_files_from_datafolder(BLENDER_USER_DATAFILES, STUDIOLIGHT_WORLD_FOLDER, STUDIOLIGHT_TYPE_WORLD | STUDIOLIGHT_USER_DEFINED); @@ -1325,8 +1503,10 @@ void BKE_studiolight_init(void) STUDIOLIGHT_MATCAP_FOLDER, STUDIOLIGHT_TYPE_MATCAP | STUDIOLIGHT_USER_DEFINED); } - studiolight_add_files_from_datafolder( - BLENDER_SYSTEM_DATAFILES, STUDIOLIGHT_LIGHTS_FOLDER, STUDIOLIGHT_TYPE_STUDIO); + studiolight_add_files_from_datafolder(BLENDER_SYSTEM_DATAFILES, + STUDIOLIGHT_LIGHTS_FOLDER, + STUDIOLIGHT_TYPE_STUDIO | + STUDIOLIGHT_SPECULAR_HIGHLIGHT_PASS); studiolight_add_files_from_datafolder( BLENDER_SYSTEM_DATAFILES, STUDIOLIGHT_WORLD_FOLDER, STUDIOLIGHT_TYPE_WORLD); studiolight_add_files_from_datafolder( @@ -1456,6 +1636,12 @@ void BKE_studiolight_ensure_flag(StudioLight *sl, int flag) studiolight_calculate_irradiance_equirect_image(sl); } } + if ((flag & STUDIOLIGHT_MATCAP_DIFFUSE_GPUTEXTURE)) { + studiolight_create_matcap_diffuse_gputexture(sl); + } + if ((flag & STUDIOLIGHT_MATCAP_SPECULAR_GPUTEXTURE)) { + studiolight_create_matcap_specular_gputexture(sl); + } } /* diff --git a/source/blender/draw/engines/workbench/shaders/workbench_deferred_composite_frag.glsl b/source/blender/draw/engines/workbench/shaders/workbench_deferred_composite_frag.glsl index a1f80440404..fd4cea4279a 100644 --- a/source/blender/draw/engines/workbench/shaders/workbench_deferred_composite_frag.glsl +++ b/source/blender/draw/engines/workbench/shaders/workbench_deferred_composite_frag.glsl @@ -5,7 +5,8 @@ uniform sampler2D materialBuffer; uniform sampler2D normalBuffer; /* normalBuffer contains viewport normals */ uniform sampler2D cavityBuffer; -uniform sampler2D matcapImage; +uniform sampler2D matcapDiffuseImage; +uniform sampler2D matcapSpecularImage; uniform vec2 invertedViewportSize; uniform vec4 viewvecs[3]; @@ -55,8 +56,15 @@ void main() normal_viewport = (metallic > 0.0) ? normal_viewport : -normal_viewport; bool flipped = world_data.matcap_orientation != 0; vec2 matcap_uv = matcap_uv_compute(I_vs, normal_viewport, flipped); - vec3 matcap = textureLod(matcapImage, matcap_uv, 0.0).rgb; - vec3 shaded_color = matcap * base_color; + vec3 matcap_diffuse = textureLod(matcapDiffuseImage, matcap_uv, 0.0).rgb; + +# ifdef V3D_SHADING_SPECULAR_HIGHLIGHT + vec3 matcap_specular = textureLod(matcapSpecularImage, matcap_uv, 0.0).rgb; +# else + vec3 matcap_specular = vec3(0.0); +# endif + + vec3 shaded_color = matcap_diffuse * base_color + matcap_specular; #elif defined(V3D_LIGHTING_STUDIO) diff --git a/source/blender/draw/engines/workbench/shaders/workbench_forward_transparent_accum_frag.glsl b/source/blender/draw/engines/workbench/shaders/workbench_forward_transparent_accum_frag.glsl index c78b2182d04..3333dfeff38 100644 --- a/source/blender/draw/engines/workbench/shaders/workbench_forward_transparent_accum_frag.glsl +++ b/source/blender/draw/engines/workbench/shaders/workbench_forward_transparent_accum_frag.glsl @@ -27,7 +27,8 @@ in vec2 uv_interp; in vec3 vertexColor; #endif #ifdef V3D_LIGHTING_MATCAP -uniform sampler2D matcapImage; +uniform sampler2D matcapDiffuseImage; +uniform sampler2D matcapSpecularImage; #endif layout(std140) uniform world_block @@ -68,8 +69,13 @@ void main() #elif defined(V3D_LIGHTING_MATCAP) bool flipped = world_data.matcap_orientation != 0; vec2 matcap_uv = matcap_uv_compute(I_vs, nor, flipped); - vec3 matcap = textureLod(matcapImage, matcap_uv, 0.0).rgb; - vec3 shaded_color = matcap * diffuse_color.rgb; + vec3 matcap_diffuse = textureLod(matcapDiffuseImage, matcap_uv, 0.0).rgb; +# ifdef V3D_SHADING_SPECULAR_HIGHLIGHT + vec3 matcap_specular = textureLod(matcapSpecularImage, matcap_uv, 0.0).rgb; +# else + vec3 matcap_specular = vec3(0.0); +# endif + vec3 shaded_color = matcap_diffuse * diffuse_color.rgb + matcap_specular; #elif defined(V3D_LIGHTING_STUDIO) vec3 shaded_color = get_world_lighting( diff --git a/source/blender/draw/engines/workbench/workbench_deferred.c b/source/blender/draw/engines/workbench/workbench_deferred.c index add49462de1..c1c9d8b5a96 100644 --- a/source/blender/draw/engines/workbench/workbench_deferred.c +++ b/source/blender/draw/engines/workbench/workbench_deferred.c @@ -694,16 +694,22 @@ static void workbench_composite_uniforms(WORKBENCH_PrivateData *wpd, DRWShadingG if (CAVITY_ENABLED(wpd)) { DRW_shgroup_uniform_texture_ref(grp, "cavityBuffer", &e_data.cavity_buffer_tx); } - if (SPECULAR_HIGHLIGHT_ENABLED(wpd) || STUDIOLIGHT_TYPE_MATCAP_ENABLED(wpd)) { + if (workbench_is_specular_highlight_enabled(wpd) || STUDIOLIGHT_TYPE_MATCAP_ENABLED(wpd)) { DRW_shgroup_uniform_vec4(grp, "viewvecs[0]", (float *)wpd->viewvecs, 3); } - if (SPECULAR_HIGHLIGHT_ENABLED(wpd) || STUDIOLIGHT_TYPE_MATCAP_ENABLED(wpd)) { + if (workbench_is_specular_highlight_enabled(wpd) || STUDIOLIGHT_TYPE_MATCAP_ENABLED(wpd)) { DRW_shgroup_uniform_vec2(grp, "invertedViewportSize", DRW_viewport_invert_size_get(), 1); } if (STUDIOLIGHT_TYPE_MATCAP_ENABLED(wpd)) { - BKE_studiolight_ensure_flag(wpd->studio_light, STUDIOLIGHT_EQUIRECT_RADIANCE_GPUTEXTURE); + BKE_studiolight_ensure_flag(wpd->studio_light, + STUDIOLIGHT_MATCAP_DIFFUSE_GPUTEXTURE | + STUDIOLIGHT_MATCAP_SPECULAR_GPUTEXTURE); DRW_shgroup_uniform_texture( - grp, "matcapImage", wpd->studio_light->equirect_radiance_gputexture); + grp, "matcapDiffuseImage", wpd->studio_light->matcap_diffuse.gputexture); + if (workbench_is_specular_highlight_enabled(wpd)) { + DRW_shgroup_uniform_texture( + grp, "matcapSpecularImage", wpd->studio_light->matcap_specular.gputexture); + } } } diff --git a/source/blender/draw/engines/workbench/workbench_forward.c b/source/blender/draw/engines/workbench/workbench_forward.c index 4c1fce550e8..824c6e7bd76 100644 --- a/source/blender/draw/engines/workbench/workbench_forward.c +++ b/source/blender/draw/engines/workbench/workbench_forward.c @@ -186,11 +186,17 @@ WORKBENCH_MaterialData *workbench_forward_get_or_create_material_data(WORKBENCH_ DRW_shgroup_uniform_vec4(grp, "viewvecs[0]", (float *)wpd->viewvecs, 3); workbench_material_copy(material, &material_template); if (STUDIOLIGHT_TYPE_MATCAP_ENABLED(wpd)) { - BKE_studiolight_ensure_flag(wpd->studio_light, STUDIOLIGHT_EQUIRECT_RADIANCE_GPUTEXTURE); + BKE_studiolight_ensure_flag(wpd->studio_light, + STUDIOLIGHT_MATCAP_DIFFUSE_GPUTEXTURE | + STUDIOLIGHT_MATCAP_SPECULAR_GPUTEXTURE); DRW_shgroup_uniform_texture( - grp, "matcapImage", wpd->studio_light->equirect_radiance_gputexture); + grp, "matcapDiffuseImage", wpd->studio_light->matcap_diffuse.gputexture); + if (workbench_is_specular_highlight_enabled(wpd)) { + DRW_shgroup_uniform_texture( + grp, "matcapSpecularImage", wpd->studio_light->matcap_specular.gputexture); + } } - if (SPECULAR_HIGHLIGHT_ENABLED(wpd) || MATCAP_ENABLED(wpd)) { + if (workbench_is_specular_highlight_enabled(wpd) || MATCAP_ENABLED(wpd)) { DRW_shgroup_uniform_vec2(grp, "invertedViewportSize", DRW_viewport_invert_size_get(), 1); } if (SHADOW_ENABLED(wpd)) { @@ -528,11 +534,17 @@ 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_TYPE_MATCAP_ENABLED(wpd)) { - BKE_studiolight_ensure_flag(wpd->studio_light, STUDIOLIGHT_EQUIRECT_RADIANCE_GPUTEXTURE); + BKE_studiolight_ensure_flag(wpd->studio_light, + STUDIOLIGHT_MATCAP_DIFFUSE_GPUTEXTURE | + STUDIOLIGHT_MATCAP_SPECULAR_GPUTEXTURE); DRW_shgroup_uniform_texture( - shgrp, "matcapImage", wpd->studio_light->equirect_radiance_gputexture); + shgrp, "matcapDiffuseImage", wpd->studio_light->matcap_diffuse.gputexture); + if (workbench_is_specular_highlight_enabled(wpd)) { + DRW_shgroup_uniform_texture( + shgrp, "matcapSpecularImage", wpd->studio_light->matcap_specular.gputexture); + } } - if (SPECULAR_HIGHLIGHT_ENABLED(wpd) || MATCAP_ENABLED(wpd)) { + if (workbench_is_specular_highlight_enabled(wpd) || MATCAP_ENABLED(wpd)) { DRW_shgroup_uniform_vec2(shgrp, "invertedViewportSize", DRW_viewport_invert_size_get(), 1); } diff --git a/source/blender/draw/engines/workbench/workbench_materials.c b/source/blender/draw/engines/workbench/workbench_materials.c index 6699a1954ba..e050877187e 100644 --- a/source/blender/draw/engines/workbench/workbench_materials.c +++ b/source/blender/draw/engines/workbench/workbench_materials.c @@ -79,7 +79,7 @@ void workbench_material_update_data(WORKBENCH_PrivateData *wpd, /* V3D_SHADING_MATERIAL_COLOR or V3D_SHADING_TEXTURE_COLOR */ if (mat) { data->alpha *= mat->a; - if (SPECULAR_HIGHLIGHT_ENABLED(wpd)) { + if (workbench_is_specular_highlight_enabled(wpd)) { copy_v3_v3(data->base_color, &mat->r); mul_v3_v3fl(data->diffuse_color, &mat->r, 1.0f - mat->metallic); mul_v3_v3fl(data->specular_color, &mat->r, mat->metallic); @@ -121,7 +121,7 @@ char *workbench_material_build_defines(WORKBENCH_PrivateData *wpd, if (SSAO_ENABLED(wpd) || CURVATURE_ENABLED(wpd)) { BLI_dynstr_append(ds, "#define WB_CAVITY\n"); } - if (SPECULAR_HIGHLIGHT_ENABLED(wpd)) { + if (workbench_is_specular_highlight_enabled(wpd)) { BLI_dynstr_append(ds, "#define V3D_SHADING_SPECULAR_HIGHLIGHT\n"); } if (STUDIOLIGHT_ENABLED(wpd)) { @@ -195,11 +195,12 @@ int workbench_material_get_composite_shader_index(WORKBENCH_PrivateData *wpd) /* NOTE: change MAX_COMPOSITE_SHADERS accordingly when modifying this function. */ int index = 0; /* 2 bits FLAT/STUDIO/MATCAP + Specular highlight */ - index = SPECULAR_HIGHLIGHT_ENABLED(wpd) ? 3 : wpd->shading.light; + index = wpd->shading.light; SET_FLAG_FROM_TEST(index, wpd->shading.flag & V3D_SHADING_SHADOW, 1 << 2); SET_FLAG_FROM_TEST(index, wpd->shading.flag & V3D_SHADING_CAVITY, 1 << 3); SET_FLAG_FROM_TEST(index, wpd->shading.flag & V3D_SHADING_OBJECT_OUTLINE, 1 << 4); SET_FLAG_FROM_TEST(index, workbench_is_matdata_pass_enabled(wpd), 1 << 5); + SET_FLAG_FROM_TEST(index, workbench_is_specular_highlight_enabled(wpd), 1 << 6); BLI_assert(index < MAX_COMPOSITE_SHADERS); return index; } @@ -246,12 +247,13 @@ int workbench_material_get_accum_shader_index(WORKBENCH_PrivateData *wpd, /* NOTE: change MAX_ACCUM_SHADERS accordingly when modifying this function. */ int index = 0; /* 2 bits FLAT/STUDIO/MATCAP + Specular highlight */ - index = SPECULAR_HIGHLIGHT_ENABLED(wpd) ? 3 : wpd->shading.light; + index = wpd->shading.light; SET_FLAG_FROM_TEST(index, use_textures, 1 << 2); SET_FLAG_FROM_TEST(index, use_vertex_colors, 1 << 3); SET_FLAG_FROM_TEST(index, is_hair, 1 << 4); /* 1 bits SHADOWS (only facing factor) */ SET_FLAG_FROM_TEST(index, SHADOW_ENABLED(wpd), 1 << 5); + SET_FLAG_FROM_TEST(index, workbench_is_specular_highlight_enabled(wpd), 1 << 6); BLI_assert(index < MAX_ACCUM_SHADERS); return index; } @@ -333,7 +335,7 @@ void workbench_material_shgroup_uniform(WORKBENCH_PrivateData *wpd, 1); } - if (SPECULAR_HIGHLIGHT_ENABLED(wpd)) { + if (workbench_is_specular_highlight_enabled(wpd)) { if (use_metallic) { DRW_shgroup_uniform_float(grp, "materialMetallic", &material->metallic, 1); } diff --git a/source/blender/draw/engines/workbench/workbench_private.h b/source/blender/draw/engines/workbench/workbench_private.h index 255b036eebb..d880d5d58b5 100644 --- a/source/blender/draw/engines/workbench/workbench_private.h +++ b/source/blender/draw/engines/workbench/workbench_private.h @@ -36,9 +36,9 @@ #define WORKBENCH_ENGINE "BLENDER_WORKBENCH" #define M_GOLDEN_RATION_CONJUGATE 0.618033988749895 -#define MAX_COMPOSITE_SHADERS (1 << 6) +#define MAX_COMPOSITE_SHADERS (1 << 7) #define MAX_PREPASS_SHADERS (1 << 7) -#define MAX_ACCUM_SHADERS (1 << 6) +#define MAX_ACCUM_SHADERS (1 << 7) #define MAX_CAVITY_SHADERS (1 << 3) #define TEXTURE_DRAWING_ENABLED(wpd) (wpd->shading.color_type == V3D_SHADING_TEXTURE_COLOR) @@ -77,9 +77,6 @@ #define IS_NAVIGATING(wpd) \ ((DRW_context_state_get()->rv3d) && (DRW_context_state_get()->rv3d->rflag & RV3D_NAVIGATING)) -#define SPECULAR_HIGHLIGHT_ENABLED(wpd) \ - (STUDIOLIGHT_ENABLED(wpd) && (wpd->shading.flag & V3D_SHADING_SPECULAR_HIGHLIGHT) && \ - (!STUDIOLIGHT_TYPE_MATCAP_ENABLED(wpd))) #define OBJECT_OUTLINE_ENABLED(wpd) (wpd->shading.flag & V3D_SHADING_OBJECT_OUTLINE) #define OBJECT_ID_PASS_ENABLED(wpd) (OBJECT_OUTLINE_ENABLED(wpd) || CURVATURE_ENABLED(wpd)) #define NORMAL_VIEWPORT_COMP_PASS_ENABLED(wpd) \ @@ -316,6 +313,16 @@ typedef struct WORKBENCH_ObjectData { } WORKBENCH_ObjectData; /* inline helper functions */ +BLI_INLINE bool workbench_is_specular_highlight_enabled(WORKBENCH_PrivateData *wpd) +{ + if ((wpd->shading.flag & V3D_SHADING_SPECULAR_HIGHLIGHT)) { + if (STUDIOLIGHT_ENABLED(wpd) || MATCAP_ENABLED(wpd)) { + return (wpd->studio_light->flag & STUDIOLIGHT_SPECULAR_HIGHLIGHT_PASS) != 0; + } + } + return false; +} + BLI_INLINE bool workbench_is_taa_enabled(WORKBENCH_PrivateData *wpd) { if (DRW_state_is_image_render()) { diff --git a/source/blender/gpu/intern/gpu_draw.c b/source/blender/gpu/intern/gpu_draw.c index be3655648f5..859b1e4a10d 100644 --- a/source/blender/gpu/intern/gpu_draw.c +++ b/source/blender/gpu/intern/gpu_draw.c @@ -348,7 +348,7 @@ static void gpu_texture_update_scaled( } /* Scale pixels. */ - ImBuf *ibuf = IMB_allocFromBuffer((uint *)rect, rect_float, w, h); + ImBuf *ibuf = IMB_allocFromBuffer((uint *)rect, rect_float, w, h, 4); IMB_scaleImBuf(ibuf, sub_w, sub_h); if (ibuf->rect_float) { @@ -648,13 +648,13 @@ void GPU_create_gl_tex(uint *bind, recth = smaller_power_of_2_limit(recth); if (frect) { - ibuf = IMB_allocFromBuffer(NULL, frect, tpx, tpy); + ibuf = IMB_allocFromBuffer(NULL, frect, tpx, tpy, 4); IMB_scaleImBuf(ibuf, rectw, recth); frect = ibuf->rect_float; } else { - ibuf = IMB_allocFromBuffer(rect, NULL, tpx, tpy); + ibuf = IMB_allocFromBuffer(rect, NULL, tpx, tpy, 4); IMB_scaleImBuf(ibuf, rectw, recth); rect = ibuf->rect; diff --git a/source/blender/imbuf/IMB_imbuf.h b/source/blender/imbuf/IMB_imbuf.h index 571cac41399..7e7e489a6c3 100644 --- a/source/blender/imbuf/IMB_imbuf.h +++ b/source/blender/imbuf/IMB_imbuf.h @@ -145,7 +145,8 @@ bool IMB_initImBuf( struct ImBuf *IMB_allocFromBuffer(const unsigned int *rect, const float *rectf, unsigned int w, - unsigned int h); + unsigned int h, + unsigned int channels); /** * diff --git a/source/blender/imbuf/intern/allocimbuf.c b/source/blender/imbuf/intern/allocimbuf.c index 7d2f51af65e..75db3fd3c73 100644 --- a/source/blender/imbuf/intern/allocimbuf.c +++ b/source/blender/imbuf/intern/allocimbuf.c @@ -428,7 +428,8 @@ bool imb_addrectImBuf(ImBuf *ibuf) struct ImBuf *IMB_allocFromBuffer(const unsigned int *rect, const float *rectf, unsigned int w, - unsigned int h) + unsigned int h, + unsigned int channels) { ImBuf *ibuf = NULL; @@ -438,6 +439,7 @@ struct ImBuf *IMB_allocFromBuffer(const unsigned int *rect, ibuf = IMB_allocImBuf(w, h, 32, 0); + ibuf->channels = channels; if (rectf) { ibuf->rect_float = MEM_dupallocN(rectf); ibuf->flags |= IB_rectfloat; diff --git a/source/blender/makesrna/intern/rna_userdef.c b/source/blender/makesrna/intern/rna_userdef.c index 72a3455b120..64ed284eb1f 100644 --- a/source/blender/makesrna/intern/rna_userdef.c +++ b/source/blender/makesrna/intern/rna_userdef.c @@ -912,6 +912,13 @@ static bool rna_UserDef_studiolight_is_user_defined_get(PointerRNA *ptr) return (sl->flag & STUDIOLIGHT_USER_DEFINED) != 0; } +/* StudioLight.is_user_defined */ +static bool rna_UserDef_studiolight_has_specular_highlight_pass_get(PointerRNA *ptr) +{ + StudioLight *sl = (StudioLight *)ptr->data; + return sl->flag & STUDIOLIGHT_SPECULAR_HIGHLIGHT_PASS; +} + /* StudioLight.type */ static int rna_UserDef_studiolight_type_get(PointerRNA *ptr) @@ -3700,6 +3707,15 @@ static void rna_def_userdef_studiolight(BlenderRNA *brna) RNA_def_property_clear_flag(prop, PROP_EDITABLE); RNA_def_property_ui_text(prop, "User Defined", ""); + prop = RNA_def_property(srna, "has_specular_highlight_pass", PROP_BOOLEAN, PROP_NONE); + RNA_def_property_boolean_funcs( + prop, "rna_UserDef_studiolight_has_specular_highlight_pass_get", NULL); + RNA_def_property_clear_flag(prop, PROP_EDITABLE); + RNA_def_property_ui_text( + prop, + "Has Specular Highlight", + "Studio light image file has separate \"diffuse\" and \"specular\" passes"); + prop = RNA_def_property(srna, "type", PROP_ENUM, PROP_NONE); RNA_def_property_enum_items(prop, rna_enum_studio_light_type_items); RNA_def_property_enum_funcs(prop, "rna_UserDef_studiolight_type_get", NULL, NULL); -- cgit v1.2.3