Derivative map baker

Added support for derivative map baking, which
is accessable as a dedicated baker type. Works
pretty much the same as displacement map baker,
but gives you derivative map.

In fact, inernally this baker is just a filter
which applies on the result of displacement map.

Both regular and multires baking are supported.

Patch by Morten Mikkelsen and self.
This commit is contained in:
Sergey Sharybin 2013-10-09 15:51:14 +00:00
parent 1255b1e82d
commit d917bdb095
10 changed files with 266 additions and 34 deletions

@ -501,7 +501,7 @@ class RENDER_PT_bake(RenderButtonsPanel, Panel):
layout.prop(rd, "bake_type")
multires_bake = False
if rd.bake_type in ['NORMALS', 'DISPLACEMENT', 'AO']:
if rd.bake_type in ['NORMALS', 'DISPLACEMENT', 'DERIVATIVE', 'AO']:
layout.prop(rd, "use_bake_multires")
multires_bake = rd.use_bake_multires
@ -542,11 +542,20 @@ class RENDER_PT_bake(RenderButtonsPanel, Panel):
if rd.bake_type == 'DISPLACEMENT':
col = split.column()
col.prop(rd, "use_bake_lores_mesh")
if rd.bake_type == 'AO':
col = split.column()
col.prop(rd, "bake_bias")
col.prop(rd, "bake_samples")
if rd.bake_type == 'DERIVATIVE':
row = layout.row()
row.prop(rd, "use_bake_user_scale", text="")
sub = row.column()
sub.active = rd.use_bake_user_scale
sub.prop(rd, "bake_user_scale", text="User Scale")
if __name__ == "__main__": # only for live edit.
bpy.utils.register_module(__name__)

@ -91,20 +91,24 @@
typedef struct MultiresBakerJobData {
struct MultiresBakerJobData *next, *prev;
DerivedMesh *lores_dm, *hires_dm;
int simple, lvl, tot_lvl;
bool simple;
int lvl, tot_lvl;
ListBase images;
} MultiresBakerJobData;
/* data passing to multires-baker job */
typedef struct {
ListBase data;
int bake_clear, bake_filter;
short mode, use_lores_mesh;
int number_of_rays;
float bias;
int raytrace_structure;
int octree_resolution;
int threads;
bool bake_clear; /* Clear the images before baking */
int bake_filter; /* Bake-filter, aka margin */
short mode; /* mode of baking (displacement, normals, AO) */
bool use_lores_mesh; /* Use low-resolution mesh when baking displacement maps */
int number_of_rays; /* Number of rays to be cast when doing AO baking */
float bias; /* Bias between object and start ray point when doing AO baking */
int raytrace_structure; /* Optimization structure to be used for AO baking */
int octree_resolution; /* Reslution of octotree when using octotree optimization structure */
int threads; /* Number of threads to be used for baking */
float user_scale; /* User scale used to scale displacement when baking derivative map. */
} MultiresBakeJob;
static bool multiresbake_check(bContext *C, wmOperator *op)
@ -236,7 +240,7 @@ static DerivedMesh *multiresbake_create_loresdm(Scene *scene, Object *ob, int *l
return dm;
}
static DerivedMesh *multiresbake_create_hiresdm(Scene *scene, Object *ob, int *lvl, int *simple)
static DerivedMesh *multiresbake_create_hiresdm(Scene *scene, Object *ob, int *lvl, bool *simple)
{
Mesh *me = (Mesh *)ob->data;
MultiresModifierData *mmd = get_multires_modifier(scene, ob, 0);
@ -253,7 +257,7 @@ static DerivedMesh *multiresbake_create_hiresdm(Scene *scene, Object *ob, int *l
CustomData_set_only_copy(&cddm->polyData, CD_MASK_BAREMESH);
*lvl = mmd->totlvl;
*simple = mmd->simple;
*simple = mmd->simple != 0;
tmp_mmd.lvl = mmd->totlvl;
tmp_mmd.sculptlvl = mmd->totlvl;
@ -349,7 +353,7 @@ static int multiresbake_image_exec_locked(bContext *C, wmOperator *op)
if (scene->r.bake_mode == RE_BAKE_NORMALS) {
clear_flag = CLEAR_TANGENT_NORMAL;
}
else if (scene->r.bake_mode == RE_BAKE_DISPLACEMENT) {
else if (ELEM(scene->r.bake_mode, RE_BAKE_DISPLACEMENT, RE_BAKE_DERIVATIVE)) {
clear_flag = CLEAR_DISPLACEMENT;
}
@ -376,6 +380,8 @@ static int multiresbake_image_exec_locked(bContext *C, wmOperator *op)
bkr.raytrace_structure = scene->r.raytrace_structure;
bkr.octree_resolution = scene->r.ocres;
bkr.threads = BKE_scene_num_threads(scene);
bkr.user_scale = (scene->r.bake_flag & R_BAKE_USERSCALE) ? scene->r.bake_user_scale : -1.0f;
//bkr.reports= op->reports;
/* create low-resolution DM (to bake to) and hi-resolution DM (to bake from) */
bkr.hires_dm = multiresbake_create_hiresdm(scene, ob, &bkr.tot_lvl, &bkr.simple);
@ -414,6 +420,8 @@ static void init_multiresbake_job(bContext *C, MultiresBakeJob *bkj)
bkj->raytrace_structure = scene->r.raytrace_structure;
bkj->octree_resolution = scene->r.ocres;
bkj->threads = BKE_scene_num_threads(scene);
bkj->user_scale = (scene->r.bake_flag & R_BAKE_USERSCALE) ? scene->r.bake_user_scale : -1.0f;
//bkj->reports = op->reports;
CTX_DATA_BEGIN (C, Base *, base, selected_editable_bases)
{
@ -453,7 +461,7 @@ static void multiresbake_startjob(void *bkv, short *stop, short *do_update, floa
if (bkj->mode == RE_BAKE_NORMALS) {
clear_flag = CLEAR_TANGENT_NORMAL;
}
else if (bkj->mode == RE_BAKE_DISPLACEMENT) {
else if (ELEM(bkj->mode, RE_BAKE_DISPLACEMENT, RE_BAKE_DERIVATIVE)) {
clear_flag = CLEAR_DISPLACEMENT;
}
@ -468,6 +476,8 @@ static void multiresbake_startjob(void *bkv, short *stop, short *do_update, floa
bkr.bake_filter = bkj->bake_filter;
bkr.mode = bkj->mode;
bkr.use_lores_mesh = bkj->use_lores_mesh;
bkr.user_scale = bkj->user_scale;
//bkr.reports = bkj->reports;
/* create low-resolution DM (to bake to) and hi-resolution DM (to bake from) */
bkr.lores_dm = data->lores_dm;
@ -773,7 +783,7 @@ static int objects_bake_render_modal(bContext *C, wmOperator *UNUSED(op), const
static int is_multires_bake(Scene *scene)
{
if (ELEM3(scene->r.bake_mode, RE_BAKE_NORMALS, RE_BAKE_DISPLACEMENT, RE_BAKE_AO))
if (ELEM4(scene->r.bake_mode, RE_BAKE_NORMALS, RE_BAKE_DISPLACEMENT, RE_BAKE_DERIVATIVE, RE_BAKE_AO))
return scene->r.bake_flag & R_BAKE_MULTIRES;
return 0;

@ -511,6 +511,7 @@ typedef struct RenderData {
short bake_normal_space, bake_quad_split;
float bake_maxdist, bake_biasdist;
short bake_samples, bake_pad;
float bake_user_scale, bake_pad1;
/* path to render output */
char pic[1024]; /* 1024 = FILE_MAX */
@ -1353,6 +1354,7 @@ typedef struct Scene {
#define R_BAKE_MULTIRES 16
#define R_BAKE_LORES_MESH 32
#define R_BAKE_VCOL 64
#define R_BAKE_USERSCALE 128
/* bake_normal_space */
#define R_BAKE_SPACE_CAMERA 0

@ -4088,6 +4088,7 @@ static void rna_def_scene_render_data(BlenderRNA *brna)
{RE_BAKE_NORMALS, "NORMALS", 0, "Normals", "Bake normals"},
{RE_BAKE_TEXTURE, "TEXTURE", 0, "Textures", "Bake textures"},
{RE_BAKE_DISPLACEMENT, "DISPLACEMENT", 0, "Displacement", "Bake displacement"},
{RE_BAKE_DERIVATIVE, "DERIVATIVE", 0, "Derivative", "Bake derivative map"},
{RE_BAKE_EMIT, "EMIT", 0, "Emission", "Bake Emit values (glow)"},
{RE_BAKE_ALPHA, "ALPHA", 0, "Alpha", "Bake Alpha values (transparency)"},
{RE_BAKE_MIRROR_INTENSITY, "MIRROR_INTENSITY", 0, "Mirror Intensity", "Bake Mirror values"},
@ -4674,6 +4675,17 @@ static void rna_def_scene_render_data(BlenderRNA *brna)
"Bake to vertex colors instead of to a UV-mapped image");
RNA_def_property_update(prop, NC_SCENE | ND_RENDER_OPTIONS, NULL);
prop = RNA_def_property(srna, "use_bake_user_scale", PROP_BOOLEAN, PROP_NONE);
RNA_def_property_boolean_sdna(prop, NULL, "bake_flag", R_BAKE_USERSCALE);
RNA_def_property_ui_text(prop, "User scale", "Use a user scale for the derivative map");
prop = RNA_def_property(srna, "bake_user_scale", PROP_FLOAT, PROP_NONE);
RNA_def_property_float_sdna(prop, NULL, "bake_user_scale");
RNA_def_property_range(prop, 0.0, 1000.0);
RNA_def_property_ui_text(prop, "Scale",
"Instead of automatically normalizing to 0..1, "
"apply a user scale to the derivative map.");
/* stamp */
prop = RNA_def_property(srna, "use_stamp_time", PROP_BOOLEAN, PROP_NONE);

@ -37,20 +37,25 @@ struct MultiresBakeRender;
typedef struct MultiresBakeRender {
DerivedMesh *lores_dm, *hires_dm;
int simple, lvl, tot_lvl, bake_filter;
short mode, use_lores_mesh;
bool simple;
int bake_filter; /* Bake-filter, aka margin */
int lvl, tot_lvl;
short mode;
bool use_lores_mesh; /* Use low-resolution mesh when baking displacement maps */
int number_of_rays;
float bias;
int number_of_rays; /* Number of rays to be cast when doing AO baking */
float bias; /* Bias between object and start ray point when doing AO baking */
int tot_obj, tot_image;
ListBase image;
int baked_objects, baked_faces;
int raytrace_structure;
int octree_resolution;
int threads;
int raytrace_structure; /* Optimization structure to be used for AO baking */
int octree_resolution; /* Reslution of octotree when using octotree optimization structure */
int threads; /* Number of threads to be used for baking */
float user_scale; /* User scale used to scale displacement when baking derivative map. */
short *stop;
short *do_update;

@ -281,6 +281,7 @@ int RE_seq_render_active(struct Scene *scene, struct RenderData *rd);
#define RE_BAKE_MIRROR_INTENSITY 10
#define RE_BAKE_ALPHA 11
#define RE_BAKE_EMIT 12
#define RE_BAKE_DERIVATIVE 13
void RE_Database_Baking(struct Render *re, struct Main *bmain, struct Scene *scene, unsigned int lay, const int type, struct Object *actob);

@ -213,6 +213,9 @@ int RE_bake_shade_all_selected(struct Render *re, int type, struct Object *actob
struct Image *RE_bake_shade_get_image(void);
void RE_bake_ibuf_filter(struct ImBuf *ibuf, char *mask, const int filter);
void RE_bake_ibuf_normalize_displacement(struct ImBuf *ibuf, float *displacement, char *mask, float displacement_min, float displacement_max);
float RE_bake_make_derivative(struct ImBuf *ibuf, float *heights_buffer, const char *mask,
const float height_min, const float height_max,
const float fmult);
#define BAKE_RESULT_OK 0
#define BAKE_RESULT_NO_OBJECTS 1

@ -553,7 +553,7 @@ static void do_bake_shade(void *handle, int x, int y, float u, float v)
}
}
if (bs->type == RE_BAKE_DISPLACEMENT) {
if (ELEM(bs->type, RE_BAKE_DISPLACEMENT, RE_BAKE_DERIVATIVE)) {
if (hit)
bake_displacement(handle, shi, (dir == -1) ? mindist : -mindist, x, y);
else
@ -688,7 +688,7 @@ static int get_next_bake_face(BakeShade *bs)
if (R.r.bake_flag & R_BAKE_CLEAR) {
if (R.r.bake_mode == RE_BAKE_NORMALS && R.r.bake_normal_space == R_BAKE_SPACE_TANGENT)
IMB_rectfill(ibuf, (ibuf->planes == R_IMF_PLANES_RGBA) ? nor_alpha : nor_solid);
else if (R.r.bake_mode == RE_BAKE_DISPLACEMENT)
else if (ELEM(R.r.bake_mode, RE_BAKE_DISPLACEMENT, RE_BAKE_DERIVATIVE))
IMB_rectfill(ibuf, (ibuf->planes == R_IMF_PLANES_RGBA) ? disp_alpha : disp_solid);
else
IMB_rectfill(ibuf, (ibuf->planes == R_IMF_PLANES_RGBA) ? vec_alpha : vec_solid);
@ -984,8 +984,10 @@ int RE_bake_shade_all_selected(Render *re, int type, Object *actob, short *do_up
use_mask = true;
/* do we need buffer to store displacements */
if (type == RE_BAKE_DISPLACEMENT) {
if ((R.r.bake_flag & R_BAKE_NORMALIZE) && R.r.bake_maxdist == 0.0f) {
if (ELEM(type, RE_BAKE_DISPLACEMENT, RE_BAKE_DERIVATIVE)) {
if (((R.r.bake_flag & R_BAKE_NORMALIZE) && R.r.bake_maxdist == 0.0f) ||
(type == RE_BAKE_DERIVATIVE))
{
use_displacement_buffer = true;
use_mask = true;
}
@ -1089,8 +1091,15 @@ int RE_bake_shade_all_selected(Render *re, int type, Object *actob, short *do_up
userdata = (BakeImBufuserData *)ibuf->userdata;
if (userdata) {
if (use_displacement_buffer) {
RE_bake_ibuf_normalize_displacement(ibuf, userdata->displacement_buffer, userdata->mask_buffer,
displacement_min, displacement_max);
if (type == RE_BAKE_DERIVATIVE) {
float user_scale = (R.r.bake_flag & R_BAKE_USERSCALE) ? R.r.bake_user_scale : -1.0f;
RE_bake_make_derivative(ibuf, userdata->displacement_buffer, userdata->mask_buffer,
displacement_min, displacement_max, user_scale);
}
else {
RE_bake_ibuf_normalize_displacement(ibuf, userdata->displacement_buffer, userdata->mask_buffer,
displacement_min, displacement_max);
}
}
RE_bake_ibuf_filter(ibuf, userdata->mask_buffer, re->r.bake_filter);
@ -1124,3 +1133,176 @@ struct Image *RE_bake_shade_get_image(void)
return R.bakebuf;
}
/* **************** Derivative Maps Baker **************** */
static void add_single_heights_margin(const ImBuf *ibuf, const char *mask, float *heights_buffer)
{
int x ,y;
for (y = 0; y < ibuf->y; y++) {
for (x = 0; x < ibuf->x; x++) {
int index = ibuf->x * y + x;
/* If unassigned pixel, look for neighbors. */
if (mask[index] != FILTER_MASK_USED) {
float height_acc = 0;
int denom = 0;
int i, j;
for (j = -1; j <= 1; j++)
for (i = -1; i <= 1; i++) {
int w = (i == 0 ? 1 : 0) + (j == 0 ? 1 : 0) + 1;
if (i != 0 || j != 0) {
int index2 = 0;
int x0 = x + i;
int y0 = y + j;
CLAMP(x0, 0, ibuf->x - 1);
CLAMP(y0, 0, ibuf->y - 1);
index2 = ibuf->x * y0 + x0;
if (mask[index2] == FILTER_MASK_USED) {
height_acc += w * heights_buffer[index2];
denom += w;
}
}
}
/* Insert final value. */
if (denom > 0) {
heights_buffer[index] = height_acc / denom;
}
}
}
}
}
/* returns user-scale */
float RE_bake_make_derivative(ImBuf *ibuf, float *heights_buffer, const char *mask,
const float height_min, const float height_max,
const float fmult)
{
const float delta_height = height_max - height_min;
const float denom = delta_height > 0.0f ? (8 * delta_height) : 1.0f;
bool auto_range_fit = fmult <= 0.0f;
float max_num_deriv = -1.0f;
int x, y, index;
/* Need a single margin to calculate good derivatives. */
add_single_heights_margin(ibuf, mask, heights_buffer);
if (auto_range_fit) {
/* If automatic range fitting is enabled. */
for (y = 0; y < ibuf->y; y++) {
const int Yu = y == (ibuf->y - 1) ? (ibuf->y - 1) : (y+1);
const int Yc = y;
const int Yd = y == 0 ? 0 : (y - 1);
for (x= 0; x < ibuf->x; x++) {
const int Xl = x == 0 ? 0 : (x - 1);
const int Xc = x;
const int Xr = x == (ibuf->x - 1) ? (ibuf->x - 1) : (x + 1);
const float Hcy = heights_buffer[Yc * ibuf->x + Xr] - heights_buffer[Yc * ibuf->x + Xl];
const float Hu = heights_buffer[Yu * ibuf->x + Xr] - heights_buffer[Yu * ibuf->x + Xl];
const float Hd = heights_buffer[Yd * ibuf->x + Xr] - heights_buffer[Yd * ibuf->x + Xl];
const float Hl = heights_buffer[Yu * ibuf->x + Xl] - heights_buffer[Yd * ibuf->x + Xl];
const float Hcx = heights_buffer[Yu * ibuf->x + Xc] - heights_buffer[Yd * ibuf->x + Xc];
const float Hr = heights_buffer[Yu * ibuf->x + Xr] - heights_buffer[Yd * ibuf->x + Xr];
/* This corresponds to using the sobel kernel on the heights buffer
* to obtain the derivative multiplied by 8.
*/
const float deriv_x = Hu + 2 * Hcy + Hd;
const float deriv_y = Hr + 2 * Hcx + Hl;
/* early out */
index = ibuf->x * y + x;
if (mask[index] != FILTER_MASK_USED) {
continue;
}
/* Widen bound. */
if (fabsf(deriv_x) > max_num_deriv) {
max_num_deriv = fabsf(deriv_x);
}
if (fabsf(deriv_y) > max_num_deriv) {
max_num_deriv = fabsf(deriv_y);
}
}
}
}
/* Output derivatives. */
auto_range_fit &= (max_num_deriv > 0);
for (y = 0; y < ibuf->y; y++) {
const int Yu= y==(ibuf->y-1) ? (ibuf->y-1) : (y+1);
const int Yc= y;
const int Yd= y==0 ? 0 : (y-1);
for(x= 0; x<ibuf->x; x++) {
const int Xl = x == 0 ? 0 : (x - 1);
const int Xc = x;
const int Xr = x == (ibuf->x - 1) ? (ibuf->x - 1) : (x + 1);
const float Hcy = heights_buffer[Yc * ibuf->x + Xr] - heights_buffer[Yc * ibuf->x + Xl];
const float Hu = heights_buffer[Yu * ibuf->x + Xr] - heights_buffer[Yu * ibuf->x + Xl];
const float Hd = heights_buffer[Yd * ibuf->x + Xr] - heights_buffer[Yd * ibuf->x + Xl];
const float Hl = heights_buffer[Yu * ibuf->x + Xl] - heights_buffer[Yd * ibuf->x + Xl];
const float Hcx = heights_buffer[Yu * ibuf->x + Xc] - heights_buffer[Yd * ibuf->x + Xc];
const float Hr = heights_buffer[Yu * ibuf->x + Xr] - heights_buffer[Yd * ibuf->x + Xr];
/* This corresponds to using the sobel kernel on the heights buffer
* to obtain the derivative multiplied by 8.
*/
float deriv_x = Hu + 2 * Hcy + Hd;
float deriv_y = Hr + 2 * Hcx + Hl;
/* Early out. */
index = ibuf->x * y + x;
if (mask[index] != FILTER_MASK_USED){
continue;
}
if (auto_range_fit) {
deriv_x /= max_num_deriv;
deriv_y /= max_num_deriv;
} else {
deriv_x *= (fmult / denom);
deriv_y *= (fmult / denom);
}
deriv_x = deriv_x * 0.5f + 0.5f;
deriv_y = deriv_y * 0.5f + 0.5f;
/* Clamp. */
CLAMP(deriv_x, 0.0f, 1.0f);
CLAMP(deriv_y, 0.0f, 1.0f);
/* Write out derivatives. */
if (ibuf->rect_float) {
float *rrgbf = ibuf->rect_float + index * 4;
rrgbf[0] = deriv_x;
rrgbf[1] = deriv_y;
rrgbf[2] = 0.0f;
rrgbf[3] = 1.0f;
} else {
char *rrgb = (char*)ibuf->rect + index * 4;
rrgb[0] = FTOCHAR(deriv_x);
rrgb[1] = FTOCHAR(deriv_y);
rrgb[2] = 0;
rrgb[3] = 255;
}
}
}
/* Eeturn user-scale (for rendering). */
return auto_range_fit ? (max_num_deriv / denom) : (fmult > 0.0f ? (1.0f / fmult) : 0.0f);
}

@ -5983,6 +5983,7 @@ void RE_Database_FromScene_Vectors(Render *re, Main *bmain, Scene *sce, unsigned
* RE_BAKE_AO: for baking, no lamps, but all objects
* RE_BAKE_TEXTURE:for baking, no lamps, only selected objects
* RE_BAKE_DISPLACEMENT:for baking, no lamps, only selected objects
* RE_BAKE_DERIVATIVE:for baking, no lamps, only selected objects
* RE_BAKE_SHADOW: for baking, only shadows, but all objects
*/
void RE_Database_Baking(Render *re, Main *bmain, Scene *scene, unsigned int lay, const int type, Object *actob)
@ -5991,7 +5992,7 @@ void RE_Database_Baking(Render *re, Main *bmain, Scene *scene, unsigned int lay,
float mat[4][4];
float amb[3];
const short onlyselected= !ELEM4(type, RE_BAKE_LIGHT, RE_BAKE_ALL, RE_BAKE_SHADOW, RE_BAKE_AO);
const short nolamps= ELEM3(type, RE_BAKE_NORMALS, RE_BAKE_TEXTURE, RE_BAKE_DISPLACEMENT);
const short nolamps= ELEM4(type, RE_BAKE_NORMALS, RE_BAKE_TEXTURE, RE_BAKE_DISPLACEMENT, RE_BAKE_DERIVATIVE);
re->main= bmain;
re->scene= scene;
@ -6010,7 +6011,7 @@ void RE_Database_Baking(Render *re, Main *bmain, Scene *scene, unsigned int lay,
if (type==RE_BAKE_NORMALS && re->r.bake_normal_space==R_BAKE_SPACE_TANGENT)
re->flag |= R_NEED_TANGENT;
if (!actob && ELEM4(type, RE_BAKE_LIGHT, RE_BAKE_NORMALS, RE_BAKE_TEXTURE, RE_BAKE_DISPLACEMENT)) {
if (!actob && ELEM5(type, RE_BAKE_LIGHT, RE_BAKE_NORMALS, RE_BAKE_TEXTURE, RE_BAKE_DISPLACEMENT, RE_BAKE_DERIVATIVE)) {
re->r.mode &= ~R_SHADOW;
re->r.mode &= ~R_RAYTRACE;
}

@ -124,7 +124,7 @@ typedef struct {
const int *orig_index_mp_to_orig;
} MAOBakeData;
static void multiresbake_get_normal(const MResolvePixelData *data, float norm[], const int face_num, const int vert_index)
static void multiresbake_get_normal(const MResolvePixelData *data, float norm[],const int face_num, const int vert_index)
{
unsigned int indices[] = {data->mface[face_num].v1, data->mface[face_num].v2,
data->mface[face_num].v3, data->mface[face_num].v4};
@ -1231,6 +1231,7 @@ static void bake_images(MultiresBakeRender *bkr, MultiresBakeResult *result)
do_multires_bake(bkr, ima, TRUE, apply_tangmat_callback, init_normal_data, free_normal_data, result);
break;
case RE_BAKE_DISPLACEMENT:
case RE_BAKE_DERIVATIVE:
do_multires_bake(bkr, ima, FALSE, apply_heights_callback, init_heights_data, free_heights_data, result);
break;
case RE_BAKE_AO:
@ -1248,7 +1249,7 @@ static void bake_images(MultiresBakeRender *bkr, MultiresBakeResult *result)
static void finish_images(MultiresBakeRender *bkr, MultiresBakeResult *result)
{
LinkData *link;
int use_displacement_buffer = bkr->mode == RE_BAKE_DISPLACEMENT;
bool use_displacement_buffer = ELEM(bkr->mode, RE_BAKE_DISPLACEMENT, RE_BAKE_DERIVATIVE);
for (link = bkr->image.first; link; link = link->next) {
Image *ima = (Image *)link->data;
@ -1259,8 +1260,14 @@ static void finish_images(MultiresBakeRender *bkr, MultiresBakeResult *result)
continue;
if (use_displacement_buffer) {
RE_bake_ibuf_normalize_displacement(ibuf, userdata->displacement_buffer, userdata->mask_buffer,
result->height_min, result->height_max);
if (bkr->mode == RE_BAKE_DERIVATIVE) {
RE_bake_make_derivative(ibuf, userdata->displacement_buffer, userdata->mask_buffer,
result->height_min, result->height_max, bkr->user_scale);
}
else {
RE_bake_ibuf_normalize_displacement(ibuf, userdata->displacement_buffer, userdata->mask_buffer,
result->height_min, result->height_max);
}
}
RE_bake_ibuf_filter(ibuf, userdata->mask_buffer, bkr->bake_filter);