diff --git a/release/scripts/startup/bl_ui/properties_render.py b/release/scripts/startup/bl_ui/properties_render.py index 5c2e3e5d0c7..41516a74c9f 100644 --- a/release/scripts/startup/bl_ui/properties_render.py +++ b/release/scripts/startup/bl_ui/properties_render.py @@ -317,6 +317,13 @@ class RENDER_PT_post_processing(RenderButtonsPanel, Panel): sub.row().prop(rd, "field_order", expand=True) sub.prop(rd, "use_fields_still", text="Still") + col = split.column() + col.prop(rd, "use_edge_enhance") + sub = col.column() + sub.active = rd.use_edge_enhance + sub.prop(rd, "edge_threshold", text="Threshold", slider=True) + sub.prop(rd, "edge_color", text="") + class RENDER_PT_stamp(RenderButtonsPanel, Panel): bl_label = "Stamp" diff --git a/release/scripts/startup/bl_ui/properties_render_layer.py b/release/scripts/startup/bl_ui/properties_render_layer.py index 133b4f986aa..4444d8728eb 100644 --- a/release/scripts/startup/bl_ui/properties_render_layer.py +++ b/release/scripts/startup/bl_ui/properties_render_layer.py @@ -109,6 +109,7 @@ class RENDERLAYER_PT_layer_options(RenderLayerButtonsPanel, Panel): col = split.column() col.prop(rl, "use_sky") + col.prop(rl, "use_edge_enhance") col.prop(rl, "use_strand") if bpy.app.build_options.freestyle: row = col.row() diff --git a/source/blender/blenkernel/intern/scene.c b/source/blender/blenkernel/intern/scene.c index 584aab9f84a..0c64896b28c 100644 --- a/source/blender/blenkernel/intern/scene.c +++ b/source/blender/blenkernel/intern/scene.c @@ -451,6 +451,7 @@ Scene *BKE_scene_add(Main *bmain, const char *name) sce->r.blurfac = 0.5; sce->r.frs_sec = 24; sce->r.frs_sec_base = 1; + sce->r.edgeint = 10; sce->r.ocres = 128; /* OCIO_TODO: for forwards compatibility only, so if no tonecurve are used, diff --git a/source/blender/makesdna/DNA_scene_types.h b/source/blender/makesdna/DNA_scene_types.h index d36387b6664..076dc9c7f59 100644 --- a/source/blender/makesdna/DNA_scene_types.h +++ b/source/blender/makesdna/DNA_scene_types.h @@ -196,7 +196,7 @@ typedef struct SceneRenderLayer { #define SCE_LAY_SOLID 1 #define SCE_LAY_ZTRA 2 #define SCE_LAY_HALO 4 -// #define SCE_LAY_EDGE 8 /* deprecated */ +#define SCE_LAY_EDGE 8 #define SCE_LAY_SKY 16 #define SCE_LAY_STRAND 32 #define SCE_LAY_FRS 64 @@ -378,7 +378,7 @@ typedef struct RenderData { float framelen, blurfac; /** For UR edge rendering: give the edges this color */ - float edgeR DNA_DEPRECATED, edgeG DNA_DEPRECATED, edgeB DNA_DEPRECATED; + float edgeR, edgeG, edgeB; /* standalone player */ // XXX deprecated since 2.5 @@ -1237,7 +1237,7 @@ typedef struct Scene { #define R_GAMMA 0x0004 #define R_ORTHO 0x0008 #define R_ENVMAP 0x0010 -/*#define R_EDGE 0x0020 deprecated */ +#define R_EDGE 0x0020 #define R_FIELDS 0x0040 #define R_FIELDSTILL 0x0080 /*#define R_RADIO 0x0100 */ /* deprecated */ diff --git a/source/blender/makesrna/intern/rna_scene.c b/source/blender/makesrna/intern/rna_scene.c index 807b6d80a44..b378c09a325 100644 --- a/source/blender/makesrna/intern/rna_scene.c +++ b/source/blender/makesrna/intern/rna_scene.c @@ -2322,6 +2322,12 @@ void rna_def_render_layer_common(StructRNA *srna, int scene) if (scene) RNA_def_property_update(prop, NC_SCENE | ND_RENDER_OPTIONS, "rna_Scene_glsl_update"); else RNA_def_property_clear_flag(prop, PROP_EDITABLE); + prop = RNA_def_property(srna, "use_edge_enhance", PROP_BOOLEAN, PROP_NONE); + RNA_def_property_boolean_sdna(prop, NULL, "layflag", SCE_LAY_EDGE); + RNA_def_property_ui_text(prop, "Edge", "Render Edge-enhance in this Layer (only works for Solid faces)"); + if (scene) RNA_def_property_update(prop, NC_SCENE | ND_RENDER_OPTIONS, NULL); + else RNA_def_property_clear_flag(prop, PROP_EDITABLE); + prop = RNA_def_property(srna, "use_strand", PROP_BOOLEAN, PROP_NONE); RNA_def_property_boolean_sdna(prop, NULL, "layflag", SCE_LAY_STRAND); RNA_def_property_ui_text(prop, "Strand", "Render Strands in this Layer"); @@ -4341,6 +4347,23 @@ static void rna_def_scene_render_data(BlenderRNA *brna) RNA_def_property_ui_text(prop, "Textures", "Use textures to affect material properties"); RNA_def_property_update(prop, NC_SCENE | ND_RENDER_OPTIONS, NULL); + prop = RNA_def_property(srna, "use_edge_enhance", PROP_BOOLEAN, PROP_NONE); + RNA_def_property_boolean_sdna(prop, NULL, "mode", R_EDGE); + RNA_def_property_ui_text(prop, "Edge", "Create a toon outline around the edges of geometry"); + RNA_def_property_update(prop, NC_SCENE | ND_RENDER_OPTIONS, NULL); + + prop = RNA_def_property(srna, "edge_threshold", PROP_INT, PROP_NONE); + RNA_def_property_int_sdna(prop, NULL, "edgeint"); + RNA_def_property_range(prop, 0, 255); + RNA_def_property_ui_text(prop, "Edge Threshold", "Threshold for drawing outlines on geometry edges"); + RNA_def_property_update(prop, NC_SCENE | ND_RENDER_OPTIONS, NULL); + + prop = RNA_def_property(srna, "edge_color", PROP_FLOAT, PROP_COLOR); + RNA_def_property_float_sdna(prop, NULL, "edgeR"); + RNA_def_property_array(prop, 3); + RNA_def_property_ui_text(prop, "Edge Color", "Edge color"); + RNA_def_property_update(prop, NC_SCENE | ND_RENDER_OPTIONS, NULL); + prop = RNA_def_property(srna, "use_freestyle", PROP_BOOLEAN, PROP_NONE); RNA_def_property_boolean_sdna(prop, NULL, "mode", R_EDGE_FRS); RNA_def_property_ui_text(prop, "Edge", "Draw stylized strokes using Freestyle"); diff --git a/source/blender/render/intern/source/initrender.c b/source/blender/render/intern/source/initrender.c index 28932d0e93b..2fb723faa12 100644 --- a/source/blender/render/intern/source/initrender.c +++ b/source/blender/render/intern/source/initrender.c @@ -608,7 +608,7 @@ void RE_parts_init(Render *re, int do_crop) RenderPart *pa = MEM_callocN(sizeof(RenderPart), "new part"); /* Non-box filters need 2 pixels extra to work */ - if (do_crop && re->r.filtertype) { + if (do_crop && (re->r.filtertype || (re->r.mode & R_EDGE))) { pa->crop = 2; disprect.xmin -= pa->crop; disprect.ymin -= pa->crop; diff --git a/source/blender/render/intern/source/pipeline.c b/source/blender/render/intern/source/pipeline.c index 0577ee60842..ec2644e4d9b 100644 --- a/source/blender/render/intern/source/pipeline.c +++ b/source/blender/render/intern/source/pipeline.c @@ -2414,6 +2414,13 @@ int RE_is_rendering_allowed(Scene *scene, Object *camera_override, ReportList *r BKE_report(reports, RPT_ERROR, "Cannot save render buffers, check the temp default path"); return 0; } + + /* no fullsample and edge */ + if ((scemode & R_FULL_SAMPLE) && (scene->r.mode & R_EDGE)) { + BKE_report(reports, RPT_ERROR, "Full sample does not support edge enhance"); + return 0; + } + } if (scemode & R_DOCOMP) { diff --git a/source/blender/render/intern/source/rendercore.c b/source/blender/render/intern/source/rendercore.c index 2fb956ee3a6..1fb65a4782e 100644 --- a/source/blender/render/intern/source/rendercore.c +++ b/source/blender/render/intern/source/rendercore.c @@ -967,6 +967,25 @@ static void addps(ListBase *lb, intptr_t *rd, int obi, int facenr, int z, int ma ps->shadfac= 0; } +static void edge_enhance_add(RenderPart *pa, float *rectf, float *arect) +{ + float addcol[4]; + int pix; + + if (arect==NULL) + return; + + for (pix= pa->rectx*pa->recty; pix>0; pix--, arect++, rectf+=4) { + if (*arect != 0.0f) { + addcol[0]= *arect * R.r.edgeR; + addcol[1]= *arect * R.r.edgeG; + addcol[2]= *arect * R.r.edgeB; + addcol[3]= *arect; + addAlphaOverFloat(rectf, addcol); + } + } +} + /* clamp alpha and RGB to 0..1 and 0..inf, can go outside due to filter */ static void clamp_alpha_rgb_range(RenderPart *pa, RenderLayer *rl) { @@ -991,6 +1010,67 @@ static void clamp_alpha_rgb_range(RenderPart *pa, RenderLayer *rl) } } +/* adds only alpha values */ +static void edge_enhance_tile(RenderPart *pa, float *rectf, int *rectz) +{ + /* use zbuffer to define edges, add it to the image */ + int y, x, col, *rz, *rz1, *rz2, *rz3; + int zval1, zval2, zval3; + float *rf; + + /* shift values in zbuffer 4 to the right (anti overflows), for filter we need multiplying with 12 max */ + rz= rectz; + if (rz==NULL) return; + + for (y=0; yrecty; y++) + for (x=0; xrectx; x++, rz++) (*rz)>>= 4; + + rz1= rectz; + rz2= rz1+pa->rectx; + rz3= rz2+pa->rectx; + + rf= rectf+pa->rectx+1; + + for (y=0; yrecty-2; y++) { + for (x=0; xrectx-2; x++, rz1++, rz2++, rz3++, rf++) { + + /* prevent overflow with sky z values */ + zval1= rz1[0] + 2*rz1[1] + rz1[2]; + zval2= 2*rz2[0] + 2*rz2[2]; + zval3= rz3[0] + 2*rz3[1] + rz3[2]; + + col= ( 4*rz2[1] - (zval1 + zval2 + zval3)/3 ); + if (col<0) col= -col; + + col >>= 5; + if (col > (1<<16)) col= (1<<16); + else col= (R.r.edgeint*col)>>8; + + if (col>0) { + float fcol; + + if (col>255) fcol= 1.0f; + else fcol= (float)col/255.0f; + + if (R.osa) + *rf+= fcol/(float)R.osa; + else + *rf= fcol; + } + } + rz1+= 2; + rz2+= 2; + rz3+= 2; + rf+= 2; + } + + /* shift back zbuf values, we might need it still */ + rz= rectz; + for (y=0; yrecty; y++) + for (x=0; xrectx; x++, rz++) (*rz)<<= 4; + +} + static void reset_sky_speed(RenderPart *pa, RenderLayer *rl) { /* for all pixels with max speed, set to zero */ @@ -1069,6 +1149,7 @@ static void addAlphaOverFloatMask(float *dest, float *source, unsigned short dma typedef struct ZbufSolidData { RenderLayer *rl; ListBase *psmlist; + float *edgerect; } ZbufSolidData; static void make_pixelstructs(RenderPart *pa, ZSpan *zspan, int sample, void *data) @@ -1090,6 +1171,10 @@ static void make_pixelstructs(RenderPart *pa, ZSpan *zspan, int sample, void *da } } } + + if (sdata->rl->layflag & SCE_LAY_EDGE) + if (R.r.mode & R_EDGE) + edge_enhance_tile(pa, sdata->edgerect, zspan->rectz); } /* main call for shading Delta Accum, for OSA */ @@ -1099,6 +1184,7 @@ void zbufshadeDA_tile(RenderPart *pa) RenderResult *rr= pa->result; RenderLayer *rl; ListBase psmlist= {NULL, NULL}; + float *edgerect= NULL; /* allocate the necessary buffers */ /* zbuffer inits these rects */ @@ -1109,16 +1195,21 @@ void zbufshadeDA_tile(RenderPart *pa) if ((rl->layflag & SCE_LAY_ZMASK) && (rl->layflag & SCE_LAY_NEG_ZMASK)) pa->rectmask= MEM_mallocN(sizeof(int)*pa->rectx*pa->recty, "rectmask"); - /* initialize pixelstructs */ + /* initialize pixelstructs and edge buffer */ addpsmain(&psmlist); pa->rectdaps= MEM_callocN(sizeof(intptr_t)*pa->rectx*pa->recty+4, "zbufDArectd"); + if (rl->layflag & SCE_LAY_EDGE) + if (R.r.mode & R_EDGE) + edgerect= MEM_callocN(sizeof(float)*pa->rectx*pa->recty, "rectedge"); + /* always fill visibility */ for (pa->sample=0; pa->samplesample+=4) { ZbufSolidData sdata; sdata.rl= rl; sdata.psmlist= &psmlist; + sdata.edgerect= edgerect; zbuffer_solid(pa, rl, make_pixelstructs, &sdata); if (R.test_break(R.tbh)) break; } @@ -1185,12 +1276,18 @@ void zbufshadeDA_tile(RenderPart *pa) } /* sun/sky */ - if (rl->layflag & SCE_LAY_SKY) { + if (rl->layflag & SCE_LAY_SKY) atm_tile(pa, rl); + + /* sky before edge */ + if (rl->layflag & SCE_LAY_SKY) sky_tile(pa, rl); - } - /* extra layers */ + /* extra layers */ + if (rl->layflag & SCE_LAY_EDGE) + if (R.r.mode & R_EDGE) + edge_enhance_add(pa, rl->rectf, edgerect); + if (rl->passflag & SCE_PASS_VECTOR) reset_sky_speed(pa, rl); @@ -1200,6 +1297,9 @@ void zbufshadeDA_tile(RenderPart *pa) /* free stuff within loop! */ MEM_freeN(pa->rectdaps); pa->rectdaps= NULL; freeps(&psmlist); + + if (edgerect) MEM_freeN(edgerect); + edgerect= NULL; if (pa->rectmask) { MEM_freeN(pa->rectmask); @@ -1228,6 +1328,7 @@ void zbufshade_tile(RenderPart *pa) RenderResult *rr= pa->result; RenderLayer *rl; PixStr ps; + float *edgerect= NULL; /* fake pixel struct, to comply to osa render */ ps.next= NULL; @@ -1249,6 +1350,14 @@ void zbufshade_tile(RenderPart *pa) if (!R.test_break(R.tbh)) { /* NOTE: this if () is not consistent */ + /* edges only for solid part, ztransp doesn't support it yet anti-aliased */ + if (rl->layflag & SCE_LAY_EDGE) { + if (R.r.mode & R_EDGE) { + edgerect= MEM_callocN(sizeof(float)*pa->rectx*pa->recty, "rectedge"); + edge_enhance_tile(pa, edgerect, pa->rectz); + } + } + /* initialize scanline updates for main thread */ rr->renrect.ymin = 0; rr->renlay= rl; @@ -1329,13 +1438,24 @@ void zbufshade_tile(RenderPart *pa) } /* sun/sky */ - if (rl->layflag & SCE_LAY_SKY) { + if (rl->layflag & SCE_LAY_SKY) atm_tile(pa, rl); + + /* sky before edge */ + if (rl->layflag & SCE_LAY_SKY) sky_tile(pa, rl); + + if (!R.test_break(R.tbh)) { + if (rl->layflag & SCE_LAY_EDGE) + if (R.r.mode & R_EDGE) + edge_enhance_add(pa, rl->rectf, edgerect); } if (rl->passflag & SCE_PASS_VECTOR) reset_sky_speed(pa, rl); + + if (edgerect) MEM_freeN(edgerect); + edgerect= NULL; if (pa->rectmask) { MEM_freeN(pa->rectmask);