diff --git a/intern/cycles/blender/blender_shader.cpp b/intern/cycles/blender/blender_shader.cpp index 38e019eb0c1..74dee206c0e 100644 --- a/intern/cycles/blender/blender_shader.cpp +++ b/intern/cycles/blender/blender_shader.cpp @@ -220,8 +220,13 @@ static ShaderNode *add_node(Scene *scene, /* existing blender nodes */ if(b_node.is_a(&RNA_ShaderNodeRGBCurve)) { BL::ShaderNodeRGBCurve b_curve_node(b_node); + BL::CurveMapping mapping(b_curve_node.mapping()); RGBCurvesNode *curves = new RGBCurvesNode(); - curvemapping_color_to_array(b_curve_node.mapping(), curves->curves, RAMP_TABLE_SIZE, true); + curvemapping_color_to_array(mapping, + curves->curves, + RAMP_TABLE_SIZE, + true); + curvemapping_minmax(mapping, true, &curves->min_x, &curves->max_x); node = curves; } if(b_node.is_a(&RNA_ShaderNodeVectorCurve)) { diff --git a/intern/cycles/blender/blender_util.h b/intern/cycles/blender/blender_util.h index 2e9e9266404..029d0af0fd2 100644 --- a/intern/cycles/blender/blender_util.h +++ b/intern/cycles/blender/blender_util.h @@ -62,6 +62,29 @@ static inline void colorramp_to_array(BL::ColorRamp ramp, float4 *data, int size } } +static inline void curvemap_minmax_curve(/*const*/ BL::CurveMap& curve, + float *min_x, + float *max_x) +{ + *min_x = min(*min_x, curve.points[0].location()[0]); + *max_x = max(*max_x, curve.points[curve.points.length() - 1].location()[0]); +} + +static inline void curvemapping_minmax(/*const*/ BL::CurveMapping& cumap, + bool rgb_curve, + float *min_x, + float *max_x) +{ + /* const int num_curves = cumap.curves.length(); */ /* Gives linking error so far. */ + const int num_curves = rgb_curve? 4: 3; + *min_x = FLT_MAX; + *max_x = -FLT_MAX; + for(int i = 0; i < num_curves; ++i) { + BL::CurveMap map(cumap.curves[i]); + curvemap_minmax_curve(map, min_x, max_x); + } +} + static inline void curvemapping_to_array(BL::CurveMapping cumap, float *data, int size) { cumap.update(); @@ -72,8 +95,29 @@ static inline void curvemapping_to_array(BL::CurveMapping cumap, float *data, in } } -static inline void curvemapping_color_to_array(BL::CurveMapping cumap, float4 *data, int size, bool rgb_curve) +static inline void curvemapping_color_to_array(BL::CurveMapping cumap, + float4 *data, + int size, + bool rgb_curve) { + float min_x = 0.0f, max_x = 1.0f; + /* TODO(sergey): Vector curve mapping is still clipping to 0..1. */ + if(rgb_curve) { + /* TODO(sergey): There is no easy way to automatically guess what is + * the range to be used here for the case when mapping is applied on + * top of another mapping (i.e. R curve applied on top of common + * one). + * + * Using largest possible range form all curves works correct for the + * cases like vector curves and should be good enough heuristic for + * the color curves as well. + * + * There might be some better estimations here tho. + */ + curvemapping_minmax(cumap, rgb_curve, &min_x, &max_x); + } + const float range_x = max_x - min_x; + cumap.update(); BL::CurveMap mapR = cumap.curves[0]; @@ -84,7 +128,7 @@ static inline void curvemapping_color_to_array(BL::CurveMapping cumap, float4 *d BL::CurveMap mapI = cumap.curves[3]; for(int i = 0; i < size; i++) { - float t = (float)i/(float)(size-1); + float t = min_x + (float)i/(float)(size-1) * range_x; data[i][0] = mapR.evaluate(mapI.evaluate(t)); data[i][1] = mapG.evaluate(mapI.evaluate(t)); @@ -93,7 +137,7 @@ static inline void curvemapping_color_to_array(BL::CurveMapping cumap, float4 *d } else { for(int i = 0; i < size; i++) { - float t = (float)i/(float)(size-1); + float t = min_x + (float)i/(float)(size-1) * range_x; data[i][0] = mapR.evaluate(t); data[i][1] = mapG.evaluate(t); diff --git a/intern/cycles/kernel/shaders/node_rgb_curves.osl b/intern/cycles/kernel/shaders/node_rgb_curves.osl index 60cb273ba98..0427c001f6d 100644 --- a/intern/cycles/kernel/shaders/node_rgb_curves.osl +++ b/intern/cycles/kernel/shaders/node_rgb_curves.osl @@ -19,6 +19,21 @@ float ramp_lookup(color ramp[RAMP_TABLE_SIZE], float at, int component) { + if (at < 0.0 || at > 1.0) { + float t0, dy; + if(at < 0.0) { + t0 = ramp[0][component]; + dy = t0 - ramp[1][component]; + at = -at; + } + else { + t0 = ramp[RAMP_TABLE_SIZE - 1][component]; + dy = t0 - ramp[RAMP_TABLE_SIZE - 2][component]; + at = at - 1.0; + } + return t0 + dy * at * (RAMP_TABLE_SIZE - 1); + } + float f = clamp(at, 0.0, 1.0) * (RAMP_TABLE_SIZE - 1); /* clamp int as well in case of NaN */ @@ -37,14 +52,18 @@ float ramp_lookup(color ramp[RAMP_TABLE_SIZE], float at, int component) shader node_rgb_curves( color ramp[RAMP_TABLE_SIZE] = {0.0}, + float min_x = 0.0, + float max_x = 1.0, color ColorIn = 0.0, float Fac = 0.0, output color ColorOut = 0.0) { - ColorOut[0] = ramp_lookup(ramp, ColorIn[0], 0); - ColorOut[1] = ramp_lookup(ramp, ColorIn[1], 1); - ColorOut[2] = ramp_lookup(ramp, ColorIn[2], 2); + color c = (ColorIn - color(min_x, min_x, min_x)) / (max_x - min_x); + + ColorOut[0] = ramp_lookup(ramp, c[0], 0); + ColorOut[1] = ramp_lookup(ramp, c[1], 1); + ColorOut[2] = ramp_lookup(ramp, c[2], 2); ColorOut = mix(ColorIn, ColorOut, Fac); } diff --git a/intern/cycles/kernel/svm/svm_ramp.h b/intern/cycles/kernel/svm/svm_ramp.h index 062ab013b1f..48d969947f5 100644 --- a/intern/cycles/kernel/svm/svm_ramp.h +++ b/intern/cycles/kernel/svm/svm_ramp.h @@ -19,8 +19,27 @@ CCL_NAMESPACE_BEGIN -ccl_device float4 rgb_ramp_lookup(KernelGlobals *kg, int offset, float f, bool interpolate) +ccl_device float4 rgb_ramp_lookup(KernelGlobals *kg, + int offset, + float f, + bool interpolate, + bool extrapolate) { + if((f < 0.0f || f > 1.0f) && extrapolate) { + float4 t0, dy; + if(f < 0.0f) { + t0 = fetch_node_float(kg, offset); + dy = t0 - fetch_node_float(kg, offset + 1); + f = -f; + } + else { + t0 = fetch_node_float(kg, offset + RAMP_TABLE_SIZE - 1); + dy = t0 - fetch_node_float(kg, offset + RAMP_TABLE_SIZE - 2); + f = f - 1.0f; + } + return t0 + dy * f * (RAMP_TABLE_SIZE-1); + } + f = saturate(f)*(RAMP_TABLE_SIZE-1); /* clamp int as well in case of NaN */ @@ -43,7 +62,7 @@ ccl_device void svm_node_rgb_ramp(KernelGlobals *kg, ShaderData *sd, float *stac decode_node_uchar4(node.y, &fac_offset, &color_offset, &alpha_offset, NULL); float fac = stack_load_float(stack, fac_offset); - float4 color = rgb_ramp_lookup(kg, *offset, fac, interpolate); + float4 color = rgb_ramp_lookup(kg, *offset, fac, interpolate, false); if(stack_valid(color_offset)) stack_store_float3(stack, color_offset, float4_to_float3(color)); @@ -55,16 +74,24 @@ ccl_device void svm_node_rgb_ramp(KernelGlobals *kg, ShaderData *sd, float *stac ccl_device void svm_node_rgb_curves(KernelGlobals *kg, ShaderData *sd, float *stack, uint4 node, int *offset) { - uint fac_offset = node.y; - uint color_offset = node.z; - uint out_offset = node.w; + uint fac_offset, color_offset, out_offset; + decode_node_uchar4(node.y, + &fac_offset, + &color_offset, + &out_offset, + NULL); float fac = stack_load_float(stack, fac_offset); float3 color = stack_load_float3(stack, color_offset); - float r = rgb_ramp_lookup(kg, *offset, color.x, true).x; - float g = rgb_ramp_lookup(kg, *offset, color.y, true).y; - float b = rgb_ramp_lookup(kg, *offset, color.z, true).z; + const float min_x = __int_as_float(node.z), + max_x = __int_as_float(node.w); + const float range_x = max_x - min_x; + color = (color - make_float3(min_x, min_x, min_x)) / range_x; + + float r = rgb_ramp_lookup(kg, *offset, color.x, true, true).x; + float g = rgb_ramp_lookup(kg, *offset, color.y, true, true).y; + float b = rgb_ramp_lookup(kg, *offset, color.z, true, true).z; color = (1.0f - fac)*color + fac*make_float3(r, g, b); stack_store_float3(stack, out_offset, color); @@ -81,9 +108,9 @@ ccl_device void svm_node_vector_curves(KernelGlobals *kg, ShaderData *sd, float float fac = stack_load_float(stack, fac_offset); float3 color = stack_load_float3(stack, color_offset); - float r = rgb_ramp_lookup(kg, *offset, (color.x + 1.0f)*0.5f, true).x; - float g = rgb_ramp_lookup(kg, *offset, (color.y + 1.0f)*0.5f, true).y; - float b = rgb_ramp_lookup(kg, *offset, (color.z + 1.0f)*0.5f, true).z; + float r = rgb_ramp_lookup(kg, *offset, (color.x + 1.0f)*0.5f, true, false).x; + float g = rgb_ramp_lookup(kg, *offset, (color.y + 1.0f)*0.5f, true, false).y; + float b = rgb_ramp_lookup(kg, *offset, (color.z + 1.0f)*0.5f, true, false).z; color = (1.0f - fac)*color + fac*make_float3(r*2.0f - 1.0f, g*2.0f - 1.0f, b*2.0f - 1.0f); stack_store_float3(stack, out_offset, color); diff --git a/intern/cycles/render/nodes.cpp b/intern/cycles/render/nodes.cpp index cece0c37f59..fd8410ce71b 100644 --- a/intern/cycles/render/nodes.cpp +++ b/intern/cycles/render/nodes.cpp @@ -4316,6 +4316,9 @@ RGBCurvesNode::RGBCurvesNode() add_input("Fac", SHADER_SOCKET_FLOAT); add_input("Color", SHADER_SOCKET_COLOR); add_output("Color", SHADER_SOCKET_COLOR); + + min_x = 0.0f; + max_x = 1.0f; } void RGBCurvesNode::compile(SVMCompiler& compiler) @@ -4328,7 +4331,12 @@ void RGBCurvesNode::compile(SVMCompiler& compiler) compiler.stack_assign(color_in); compiler.stack_assign(color_out); - compiler.add_node(NODE_RGB_CURVES, fac_in->stack_offset, color_in->stack_offset, color_out->stack_offset); + compiler.add_node(NODE_RGB_CURVES, + compiler.encode_uchar4(fac_in->stack_offset, + color_in->stack_offset, + color_out->stack_offset), + __float_as_int(min_x), + __float_as_int(max_x)); compiler.add_array(curves, RAMP_TABLE_SIZE); } @@ -4343,6 +4351,8 @@ void RGBCurvesNode::compile(OSLCompiler& compiler) } compiler.parameter_color_array("ramp", ramp, RAMP_TABLE_SIZE); + compiler.parameter("min_x", min_x); + compiler.parameter("max_x", max_x); compiler.add(this, "node_rgb_curves"); } diff --git a/intern/cycles/render/nodes.h b/intern/cycles/render/nodes.h index 259936c0b7c..1fa4b67c6b9 100644 --- a/intern/cycles/render/nodes.h +++ b/intern/cycles/render/nodes.h @@ -703,6 +703,7 @@ public: virtual int get_group() { return NODE_GROUP_LEVEL_3; } float4 curves[RAMP_TABLE_SIZE]; + float min_x, max_x; }; class VectorCurvesNode : public ShaderNode {