[Bf-blender-cvs] [66700196074] master: Workbench: Specular Highlighting for MatCaps

Jeroen Bakker noreply at git.blender.org
Tue Aug 27 08:58:48 CEST 2019


Commit: 66700196074ad168f3322f2766846a0a07f7a00f
Author: Jeroen Bakker
Date:   Tue Aug 27 08:42:50 2019 +0200
Branches: master
https://developer.blender.org/rB66700196074ad168f3322f2766846a0a07f7a00f

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

===================================================================

M	release/scripts/startup/bl_ui/space_view3d.py
M	source/blender/blenkernel/BKE_studiolight.h
M	source/blender/blenkernel/intern/studiolight.c
M	source/blender/draw/engines/workbench/shaders/workbench_deferred_composite_frag.glsl
M	source/blender/draw/engines/workbench/shaders/workbench_forward_transparent_accum_frag.glsl
M	source/blender/draw/engines/workbench/workbench_deferred.c
M	source/blender/draw/engines/workbench/workbench_forward.c
M	source/blender/draw/engines/workbench/workbench_materials.c
M	source/blender/draw/engines/workbench/workbench_private.h
M	source/blender/gpu/intern/gpu_draw.c
M	source/blender/imbuf/IMB_imbuf.h
M	source/blender/imbuf/intern/allocimbuf.c
M	source/blender/makesrna/intern/rna_userdef.c

===================================================================

diff --git a/release/scripts/startup/bl_ui/space_view3d.py b/release/scripts/startup/bl_ui/space_view3d.py
index 20d3e83bf79..ae2a2ce3e7c 100644
--- a/release/scripts/startup/bl_ui/space_view3d.py
+++ b/release/scripts/startup/bl_ui/space_view3d.py
@@ -5251,8 +5251,10 @@ class VIEW3D_PT_shading_options(Panel):
             sub.active = shading.show_object_outline
             sub.prop(shading, "object_outline_color", text="")
 
+        if shading.type == 'SOLID':
             col = layout.column()
-            if (shading.light == 'STUDIO') and (shading.type != 'WIREFRAME'):
+            if (shading.light in ['STUDIO', 'MATCAP']):
+                col.active = shading.selected_studio_light.has_specular_highlight_pass
                 col.prop(shading, "show_specular_highlight", text="Specular Lighting")
 
 
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

@@ Diff output truncated at 10240 characters. @@



More information about the Bf-blender-cvs mailing list