Fix T46915: Non-intuitive behavior of Vector Curve Mapping node
authorSergey Sharybin <sergey.vfx@gmail.com>
Tue, 15 Dec 2015 11:23:33 +0000 (16:23 +0500)
committerSergey Sharybin <sergey.vfx@gmail.com>
Thu, 31 Dec 2015 15:40:22 +0000 (20:40 +0500)
Vector mapping node was doing some weird mapping of both original and mapped
coordinates. Mapping of original coordinates was caused by the clamping nature
of the LUT generated from the node. Mapping of the mapped value again was quite
totally obscure -- one needed to constantly keep in mind that actual value will
be scaled up and moved down.

This commit makes it so values in the vector curve mapping are always absolute.
In fact, it is now behaving quite the same as RGB curve mapping node and the
code could be de-duplicated. Keeping the code duplicated for a bit so it's more
clear what exact parts of the node changed.

Reviewers: brecht

Subscribers: bassamk

Differential Revision: https://developer.blender.org/D1672

intern/cycles/blender/addon/version_update.py
intern/cycles/blender/blender_shader.cpp
intern/cycles/blender/blender_util.h
intern/cycles/kernel/shaders/node_vector_curves.osl
intern/cycles/kernel/svm/svm_ramp.h
intern/cycles/render/nodes.cpp
intern/cycles/render/nodes.h
source/blender/blenkernel/BKE_blender.h

index 2fbb01ba5b84ec902dd31d5a7603d08353710bae..eb6d5d957ad4217fb2e347c03b866241a4363f1a 100644 (file)
@@ -100,6 +100,19 @@ def mapping_node_order_flip(node):
         node.rotation = quat.to_euler('XYZ')
 
 
+def vector_curve_node_remap(node):
+    """
+    Remap values of vector curve node from normalized to absolute values
+    """
+    from mathutils import Vector
+    if node.bl_idname == 'ShaderNodeVectorCurve':
+        node.mapping.use_clip = False
+        for curve in node.mapping.curves:
+            for point in curve.points:
+                point.location.x = (point.location.x * 2.0) - 1.0
+                point.location.y = (point.location.y - 0.5) * 2.0
+        node.mapping.update()
+
 @persistent
 def do_versions(self):
     # We don't modify startup file because it assumes to
@@ -140,3 +153,6 @@ def do_versions(self):
     # Euler order was ZYX in previous versions.
     if bpy.data.version <= (2, 73, 4):
         foreach_cycles_node(mapping_node_order_flip)
+
+    if bpy.data.version <= (2, 76, 5):
+        foreach_cycles_node(vector_curve_node_remap)
index 74dee206c0ee79370dfdc6f8a43e5999552ed788..2b4d0e4af9cede43acbd11e148e445ad2e8e106f 100644 (file)
@@ -231,8 +231,13 @@ static ShaderNode *add_node(Scene *scene,
        }
        if(b_node.is_a(&RNA_ShaderNodeVectorCurve)) {
                BL::ShaderNodeVectorCurve b_curve_node(b_node);
+               BL::CurveMapping mapping(b_curve_node.mapping());
                VectorCurvesNode *curves = new VectorCurvesNode();
-               curvemapping_color_to_array(b_curve_node.mapping(), curves->curves, RAMP_TABLE_SIZE, false);
+               curvemapping_color_to_array(mapping,
+                                           curves->curves,
+                                           RAMP_TABLE_SIZE,
+                                           false);
+               curvemapping_minmax(mapping, false, &curves->min_x, &curves->max_x);
                node = curves;
        }
        else if(b_node.is_a(&RNA_ShaderNodeValToRGB)) {
index 029d0af0fd287cfe564f677b00ed3bff877eab61..a1508fc6a07e5a5c9ef2e60bd368cd83c76d1e0f 100644 (file)
@@ -101,21 +101,20 @@ static inline void curvemapping_color_to_array(BL::CurveMapping cumap,
                                                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);
-       }
+
+       /* 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();
index 7bbf97d95ea5fc811aa18674bee3672eaa7c5dd8..65a59dec9838a8f6325b0ed7b89529b922d1ec8f 100644 (file)
 
 float ramp_lookup(color ramp[RAMP_TABLE_SIZE], float at, int component)
 {
-       float f = clamp((at + 1.0) * 0.5, 0.0, 1.0) * (RAMP_TABLE_SIZE - 1);
+       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 */
        int i = (int)f;
@@ -32,19 +47,23 @@ float ramp_lookup(color ramp[RAMP_TABLE_SIZE], float at, int component)
        if (t > 0.0)
                result = (1.0 - t) * result + t * ramp[i + 1][component];
        
-       return result * 2.0 - 1.0;
+       return result;
 }
 
 shader node_vector_curves(
        color ramp[RAMP_TABLE_SIZE] = {0.0},
+       float min_x = 0.0,
+       float max_x = 1.0,
 
        vector VectorIn = vector(0.0, 0.0, 0.0),
        float Fac = 0.0,
        output vector VectorOut = vector(0.0, 0.0, 0.0))
 {
-       VectorOut[0] = ramp_lookup(ramp, VectorIn[0], 0);
-       VectorOut[1] = ramp_lookup(ramp, VectorIn[1], 1);
-       VectorOut[2] = ramp_lookup(ramp, VectorIn[2], 2);
+       color c = (VectorIn - vector(min_x, min_x, min_x)) / (max_x - min_x);
+
+       VectorOut[0] = ramp_lookup(ramp, c[0], 0);
+       VectorOut[1] = ramp_lookup(ramp, c[1], 1);
+       VectorOut[2] = ramp_lookup(ramp, c[2], 2);
 
        VectorOut = mix(VectorIn, VectorOut, Fac);
 }
index 48d969947f591e1a190bd412223a6d432f43e0b0..7e4106e212e8a458934bc0393e55e6472d8d167c 100644 (file)
@@ -101,18 +101,26 @@ ccl_device void svm_node_rgb_curves(KernelGlobals *kg, ShaderData *sd, float *st
 
 ccl_device void svm_node_vector_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 + 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;
+       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;
 
-       color = (1.0f - fac)*color + fac*make_float3(r*2.0f - 1.0f, g*2.0f - 1.0f, b*2.0f - 1.0f);
+       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);
 
        *offset += RAMP_TABLE_SIZE;
index 870efda7b80093e66a649ad9e2b3e829f821c47a..92ade79781a2a7024df28f7088b7fe6b256e07ce 100644 (file)
@@ -4443,6 +4443,9 @@ VectorCurvesNode::VectorCurvesNode()
        add_input("Fac", SHADER_SOCKET_FLOAT);
        add_input("Vector", SHADER_SOCKET_VECTOR);
        add_output("Vector", SHADER_SOCKET_VECTOR);
+
+       min_x = 0.0f;
+       max_x = 1.0f;
 }
 
 void VectorCurvesNode::compile(SVMCompiler& compiler)
@@ -4455,7 +4458,12 @@ void VectorCurvesNode::compile(SVMCompiler& compiler)
        compiler.stack_assign(vector_in);
        compiler.stack_assign(vector_out);
 
-       compiler.add_node(NODE_VECTOR_CURVES, fac_in->stack_offset, vector_in->stack_offset, vector_out->stack_offset);
+       compiler.add_node(NODE_VECTOR_CURVES,
+                         compiler.encode_uchar4(fac_in->stack_offset,
+                                                vector_in->stack_offset,
+                                                vector_out->stack_offset),
+                         __float_as_int(min_x),
+                         __float_as_int(max_x));
        compiler.add_array(curves, RAMP_TABLE_SIZE);
 }
 
@@ -4470,6 +4478,8 @@ void VectorCurvesNode::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_vector_curves");
 }
 
index 4eddc90be3fadd3f82ce049741c7a3b2fb42f88e..67943775b9226c4279ed7143f54bc7b811d1f56a 100644 (file)
@@ -946,6 +946,7 @@ public:
        virtual bool equals(const ShaderNode * /*other*/) { return false; }
 
        float4 curves[RAMP_TABLE_SIZE];
+       float min_x, max_x;
 };
 
 class RGBRampNode : public ShaderNode {
index 9efaad81feb316af5d10378ad1875bb1df013a83..aaf47b654702fef04db52ad767650f7dab366994 100644 (file)
@@ -45,7 +45,7 @@ extern "C" {
 #define BLENDER_SUBVERSION      5
 /* Several breakages with 270, e.g. constraint deg vs rad */
 #define BLENDER_MINVERSION      270
-#define BLENDER_MINSUBVERSION   5
+#define BLENDER_MINSUBVERSION   6
 
 /* used by packaging tools */
 /* can be left blank, otherwise a,b,c... etc with no quotes */