Cycles microdisplacement: Improved automatic bump mapping
authorMai Lavelle <mai.lavelle@gmail.com>
Sun, 14 Aug 2016 15:44:25 +0000 (11:44 -0400)
committerMai Lavelle <mai.lavelle@gmail.com>
Fri, 2 Sep 2016 02:45:49 +0000 (22:45 -0400)
Object coordinates can now be used in the displacement shader and will give
correct results, where as before bump mapping was calculated from the displace
positions and resulted in incorrect shading.

This works by evaluating the shader in two parts, first bump then surface, and
setting the shader state to match what it would be if the surface was
undisplaced for the bump shader evaluation. Currently only `P` is set as if
undisplaced, but other shader variables could be set as well, such as `I` or
`time`. Since these aren't set to anything meaningful for displacement I left
them out of this patch, we can decide what to do with them separately.

Reviewed By: brecht

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

14 files changed:
intern/cycles/kernel/CMakeLists.txt
intern/cycles/kernel/kernel_types.h
intern/cycles/kernel/osl/osl_globals.h
intern/cycles/kernel/osl/osl_services.cpp
intern/cycles/kernel/osl/osl_services.h
intern/cycles/kernel/osl/osl_shader.cpp
intern/cycles/kernel/svm/svm.h
intern/cycles/kernel/svm/svm_bump.h [new file with mode: 0644]
intern/cycles/kernel/svm/svm_types.h
intern/cycles/render/graph.cpp
intern/cycles/render/osl.cpp
intern/cycles/render/shader.cpp
intern/cycles/render/svm.cpp
intern/cycles/render/svm.h

index 9317bfb..3c37bc0 100644 (file)
@@ -113,6 +113,7 @@ set(SRC_SVM_HEADERS
        svm/svm.h
        svm/svm_attribute.h
        svm/svm_blackbody.h
+       svm/svm_bump.h
        svm/svm_camera.h
        svm/svm_closure.h
        svm/svm_convert.h
index deebf87..0646148 100644 (file)
@@ -714,20 +714,21 @@ enum ShaderDataFlag {
        SD_VOLUME_MIS             = (1 << 19),  /* use multiple importance sampling */
        SD_VOLUME_CUBIC           = (1 << 20),  /* use cubic interpolation for voxels */
        SD_HAS_BUMP               = (1 << 21),  /* has data connected to the displacement input */
+       SD_HAS_DISPLACEMENT       = (1 << 22),  /* has true displacement */
 
        SD_SHADER_FLAGS = (SD_USE_MIS|SD_HAS_TRANSPARENT_SHADOW|SD_HAS_VOLUME|
                           SD_HAS_ONLY_VOLUME|SD_HETEROGENEOUS_VOLUME|
                           SD_HAS_BSSRDF_BUMP|SD_VOLUME_EQUIANGULAR|SD_VOLUME_MIS|
-                          SD_VOLUME_CUBIC|SD_HAS_BUMP),
+                          SD_VOLUME_CUBIC|SD_HAS_BUMP|SD_HAS_DISPLACEMENT),
 
        /* object flags */
-       SD_HOLDOUT_MASK             = (1 << 22),  /* holdout for camera rays */
-       SD_OBJECT_MOTION            = (1 << 23),  /* has object motion blur */
-       SD_TRANSFORM_APPLIED        = (1 << 24),  /* vertices have transform applied */
-       SD_NEGATIVE_SCALE_APPLIED   = (1 << 25),  /* vertices have negative scale applied */
-       SD_OBJECT_HAS_VOLUME        = (1 << 26),  /* object has a volume shader */
-       SD_OBJECT_INTERSECTS_VOLUME = (1 << 27),  /* object intersects AABB of an object with volume shader */
-       SD_OBJECT_HAS_VERTEX_MOTION = (1 << 28),  /* has position for motion vertices */
+       SD_HOLDOUT_MASK             = (1 << 23),  /* holdout for camera rays */
+       SD_OBJECT_MOTION            = (1 << 24),  /* has object motion blur */
+       SD_TRANSFORM_APPLIED        = (1 << 25),  /* vertices have transform applied */
+       SD_NEGATIVE_SCALE_APPLIED   = (1 << 26),  /* vertices have negative scale applied */
+       SD_OBJECT_HAS_VOLUME        = (1 << 27),  /* object has a volume shader */
+       SD_OBJECT_INTERSECTS_VOLUME = (1 << 28),  /* object intersects AABB of an object with volume shader */
+       SD_OBJECT_HAS_VERTEX_MOTION = (1 << 29),  /* has position for motion vertices */
 
        SD_OBJECT_FLAGS = (SD_HOLDOUT_MASK|SD_OBJECT_MOTION|SD_TRANSFORM_APPLIED|
                           SD_NEGATIVE_SCALE_APPLIED|SD_OBJECT_HAS_VOLUME|
index 8353c4e..65cb7ec 100644 (file)
@@ -54,6 +54,7 @@ struct OSLGlobals {
        vector<OSL::ShaderGroupRef> surface_state;
        vector<OSL::ShaderGroupRef> volume_state;
        vector<OSL::ShaderGroupRef> displacement_state;
+       vector<OSL::ShaderGroupRef> bump_state;
        OSL::ShaderGroupRef background_state;
 
        /* attributes */
index 422af32..f61a9ec 100644 (file)
@@ -93,6 +93,7 @@ ustring OSLRenderServices::u_geom_numpolyvertices("geom:numpolyvertices");
 ustring OSLRenderServices::u_geom_trianglevertices("geom:trianglevertices");
 ustring OSLRenderServices::u_geom_polyvertices("geom:polyvertices");
 ustring OSLRenderServices::u_geom_name("geom:name");
+ustring OSLRenderServices::u_geom_undisplaced("geom:undisplaced");
 ustring OSLRenderServices::u_is_smooth("geom:is_smooth");
 #ifdef __HAIR__
 ustring OSLRenderServices::u_is_curve("geom:is_curve");
index 2701abb..0f2e02c 100644 (file)
@@ -158,6 +158,7 @@ public:
        static ustring u_geom_trianglevertices;
        static ustring u_geom_polyvertices;
        static ustring u_geom_name;
+       static ustring u_geom_undisplaced;
        static ustring u_is_smooth;
        static ustring u_is_curve;
        static ustring u_curve_thickness;
index 20dd167..681ea3c 100644 (file)
@@ -184,6 +184,47 @@ void OSLShader::eval_surface(KernelGlobals *kg, ShaderData *sd, PathState *state
        OSL::ShadingContext *octx = tdata->context[(int)ctx];
        int shader = sd->shader & SHADER_MASK;
 
+       /* automatic bump shader */
+       if(kg->osl->bump_state[shader]) {
+               /* save state */
+               float3 P = sd->P;
+               float3 dPdx = sd->dP.dx;
+               float3 dPdy = sd->dP.dy;
+
+               /* set state as if undisplaced */
+               if(sd->flag & SD_HAS_DISPLACEMENT) {
+                       float data[9];
+                       bool found = kg->osl->services->get_attribute(sd, true, OSLRenderServices::u_empty, TypeDesc::TypeVector,
+                                                                     OSLRenderServices::u_geom_undisplaced, data);
+                       assert(found);
+
+                       memcpy(&sd->P, data, sizeof(float)*3);
+                       memcpy(&sd->dP.dx, data+3, sizeof(float)*3);
+                       memcpy(&sd->dP.dy, data+6, sizeof(float)*3);
+
+                       object_position_transform(kg, sd, &sd->P);
+                       object_dir_transform(kg, sd, &sd->dP.dx);
+                       object_dir_transform(kg, sd, &sd->dP.dy);
+
+                       globals->P = TO_VEC3(sd->P);
+                       globals->dPdx = TO_VEC3(sd->dP.dx);
+                       globals->dPdy = TO_VEC3(sd->dP.dy);
+               }
+
+               /* execute bump shader */
+               ss->execute(octx, *(kg->osl->bump_state[shader]), *globals);
+
+               /* reset state */
+               sd->P = P;
+               sd->dP.dx = dPdx;
+               sd->dP.dy = dPdy;
+
+               globals->P = TO_VEC3(P);
+               globals->dPdx = TO_VEC3(dPdx);
+               globals->dPdy = TO_VEC3(dPdy);
+       }
+
+       /* surface shader */
        if(kg->osl->surface_state[shader]) {
                ss->execute(octx, *(kg->osl->surface_state[shader]), *globals);
        }
index 502994e..998a32e 100644 (file)
@@ -181,6 +181,7 @@ CCL_NAMESPACE_END
 #include "svm_brick.h"
 #include "svm_vector_transform.h"
 #include "svm_voxel.h"
+#include "svm_bump.h"
 
 CCL_NAMESPACE_BEGIN
 
@@ -294,6 +295,12 @@ ccl_device_noinline void svm_eval_nodes(KernelGlobals *kg, ShaderData *sd, ccl_a
                        case NODE_CLOSURE_SET_NORMAL:
                                svm_node_set_normal(kg, sd, stack, node.y, node.z);
                                break;
+                       case NODE_ENTER_BUMP_EVAL:
+                               svm_node_enter_bump_eval(kg, sd, stack, node.y);
+                               break;
+                       case NODE_LEAVE_BUMP_EVAL:
+                               svm_node_leave_bump_eval(kg, sd, stack, node.y);
+                               break;
 #    endif  /* NODES_FEATURE(NODE_FEATURE_BUMP) */
                        case NODE_HSV:
                                svm_node_hsv(kg, sd, stack, node, &offset);
diff --git a/intern/cycles/kernel/svm/svm_bump.h b/intern/cycles/kernel/svm/svm_bump.h
new file mode 100644 (file)
index 0000000..04a8c7b
--- /dev/null
@@ -0,0 +1,54 @@
+/*
+ * Copyright 2011-2016 Blender Foundation
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+CCL_NAMESPACE_BEGIN
+
+/* Bump Eval Nodes */
+
+ccl_device void svm_node_enter_bump_eval(KernelGlobals *kg, ShaderData *sd, float *stack, uint offset)
+{
+       /* save state */
+       stack_store_float3(stack, offset+0, ccl_fetch(sd, P));
+       stack_store_float3(stack, offset+3, ccl_fetch(sd, dP).dx);
+       stack_store_float3(stack, offset+6, ccl_fetch(sd, dP).dy);
+
+       /* set state as if undisplaced */
+       const AttributeDescriptor desc = find_attribute(kg, sd, ATTR_STD_POSITION_UNDISPLACED);
+
+       if(desc.offset != ATTR_STD_NOT_FOUND) {
+               float3 P, dPdx, dPdy;
+               P = primitive_attribute_float3(kg, sd, desc, &dPdx, &dPdy);
+
+               object_position_transform(kg, sd, &P);
+               object_dir_transform(kg, sd, &dPdx);
+               object_dir_transform(kg, sd, &dPdy);
+
+               ccl_fetch(sd, P) = P;
+               ccl_fetch(sd, dP).dx = dPdx;
+               ccl_fetch(sd, dP).dy = dPdy;
+       }
+}
+
+ccl_device void svm_node_leave_bump_eval(KernelGlobals *kg, ShaderData *sd, float *stack, uint offset)
+{
+       /* restore state */
+       ccl_fetch(sd, P) = stack_load_float3(stack, offset+0);
+       ccl_fetch(sd, dP).dx = stack_load_float3(stack, offset+3);
+       ccl_fetch(sd, dP).dy = stack_load_float3(stack, offset+6);
+}
+
+CCL_NAMESPACE_END
+
index 51083c3..9cfa839 100644 (file)
@@ -26,6 +26,8 @@ CCL_NAMESPACE_BEGIN
 /* SVM stack offsets with this value indicate that it's not on the stack */
 #define SVM_STACK_INVALID 255 
 
+#define SVM_BUMP_EVAL_STATE_SIZE 9
+
 /* Nodes */
 
 /* Known frequencies of used nodes, used for selective nodes compilation
@@ -127,6 +129,8 @@ typedef enum ShaderNodeType {
        NODE_HAIR_INFO,
        NODE_UVMAP,
        NODE_TEX_VOXEL,
+       NODE_ENTER_BUMP_EVAL,
+       NODE_LEAVE_BUMP_EVAL,
 } ShaderNodeType;
 
 typedef enum NodeAttributeType {
@@ -374,7 +378,8 @@ typedef enum NodeTexVoxelSpace {
 typedef enum ShaderType {
        SHADER_TYPE_SURFACE,
        SHADER_TYPE_VOLUME,
-       SHADER_TYPE_DISPLACEMENT
+       SHADER_TYPE_DISPLACEMENT,
+       SHADER_TYPE_BUMP,
 } ShaderType;
 
 /* Closure */
index 6e795ef..57256ce 100644 (file)
@@ -856,27 +856,8 @@ void ShaderGraph::bump_from_displacement()
        /* connect the bump out to the set normal in: */
        connect(bump->output("Normal"), set_normal->input("Direction"));
 
-       /* connect bump output to normal input nodes that aren't set yet. actually
-        * this will only set the normal input to the geometry node that we created
-        * and connected to all other normal inputs already. */
-       foreach(ShaderNode *node, nodes) {
-               /* Don't connect normal to the bump node we're coming from,
-                * otherwise it'll be a cycle in graph.
-                */
-               if(node == bump) {
-                       continue;
-               }
-               foreach(ShaderInput *input, node->inputs) {
-                       if(!input->link && (input->flags() & SocketType::LINK_NORMAL))
-                               connect(set_normal->output("Normal"), input);
-               }
-       }
-
-       /* for displacement bump, clear the normal input in case the above loop
-        * connected the setnormal out to the bump normalin */
-       ShaderInput *bump_normal_in = bump->input("Normal");
-       if(bump_normal_in)
-               bump_normal_in->link = NULL;
+       /* connect to output node */
+       connect(set_normal->output("Normal"), output()->input("Normal"));
 
        /* finally, add the copied nodes to the graph. we can't do this earlier
         * because we would create dependency cycles in the above loop */
index 8d5e295..f83aab4 100644 (file)
@@ -609,7 +609,7 @@ bool OSLCompiler::node_skip_input(ShaderNode *node, ShaderInput *input)
                        return true;
                if(input->name() == "Displacement" && current_type != SHADER_TYPE_DISPLACEMENT)
                        return true;
-               if(input->name() == "Normal")
+               if(input->name() == "Normal" && current_type != SHADER_TYPE_BUMP)
                        return true;
        }
        else if(node->special_type == SHADER_SPECIAL_TYPE_BUMP) {
@@ -684,6 +684,8 @@ void OSLCompiler::add(ShaderNode *node, const char *name, bool isfilepath)
                ss->Shader("surface", name, id(node).c_str());
        else if(current_type == SHADER_TYPE_DISPLACEMENT)
                ss->Shader("displacement", name, id(node).c_str());
+       else if(current_type == SHADER_TYPE_BUMP)
+               ss->Shader("displacement", name, id(node).c_str());
        else
                assert(0);
        
@@ -1055,6 +1057,12 @@ OSL::ShaderGroupRef OSLCompiler::compile_type(Shader *shader, ShaderGraph *graph
                generate_nodes(dependencies);
                output->compile(*this);
        }
+       else if(type == SHADER_TYPE_BUMP) {
+               /* generate bump shader */
+               find_dependencies(dependencies, output->input("Normal"));
+               generate_nodes(dependencies);
+               output->compile(*this);
+       }
        else if(type == SHADER_TYPE_VOLUME) {
                /* generate volume shader */
                find_dependencies(dependencies, output->input("Volume"));
@@ -1116,10 +1124,10 @@ void OSLCompiler::compile(Scene *scene, OSLGlobals *og, Shader *shader)
                if(shader->used && graph && output->input("Surface")->link) {
                        shader->osl_surface_ref = compile_type(shader, shader->graph, SHADER_TYPE_SURFACE);
 
-                       if(shader->graph_bump)
-                               shader->osl_surface_bump_ref = compile_type(shader, shader->graph_bump, SHADER_TYPE_SURFACE);
+                       if(shader->graph_bump && shader->displacement_method != DISPLACE_TRUE)
+                               shader->osl_surface_bump_ref = compile_type(shader, shader->graph_bump, SHADER_TYPE_BUMP);
                        else
-                               shader->osl_surface_bump_ref = shader->osl_surface_ref;
+                               shader->osl_surface_bump_ref = OSL::ShaderGroupRef();
 
                        shader->has_surface = true;
                }
@@ -1146,15 +1154,10 @@ void OSLCompiler::compile(Scene *scene, OSLGlobals *og, Shader *shader)
        }
 
        /* push state to array for lookup */
-       if(shader->displacement_method == DISPLACE_TRUE || !shader->graph_bump) {
-               og->surface_state.push_back(shader->osl_surface_ref);
-       }
-       else {
-               og->surface_state.push_back(shader->osl_surface_bump_ref);
-       }
-
+       og->surface_state.push_back(shader->osl_surface_ref);
        og->volume_state.push_back(shader->osl_volume_ref);
        og->displacement_state.push_back(shader->osl_displacement_ref);
+       og->bump_state.push_back(shader->osl_surface_bump_ref);
 }
 
 #else
index 1876791..165a316 100644 (file)
@@ -407,6 +407,8 @@ void ShaderManager::device_update_common(Device *device,
                        flag |= SD_VOLUME_CUBIC;
                if(shader->graph_bump)
                        flag |= SD_HAS_BUMP;
+               if(shader->displacement_method != DISPLACE_BUMP)
+                       flag |= SD_HAS_DISPLACEMENT;
 
                /* shader with bump mapping */
                if(shader->displacement_method != DISPLACE_TRUE && shader->graph_bump)
index be87b35..069c3e3 100644 (file)
@@ -146,9 +146,8 @@ int SVMCompiler::stack_size(SocketType::Type type)
        return size;
 }
 
-int SVMCompiler::stack_find_offset(SocketType::Type type)
+int SVMCompiler::stack_find_offset(int size)
 {
-       int size = stack_size(type);
        int offset = -1;
        
        /* find free space in stack & mark as used */
@@ -175,6 +174,11 @@ int SVMCompiler::stack_find_offset(SocketType::Type type)
        return 0;
 }
 
+int SVMCompiler::stack_find_offset(SocketType::Type type)
+{
+       return stack_find_offset(stack_size(type));
+}
+
 void SVMCompiler::stack_clear_offset(SocketType::Type type, int offset)
 {
        int size = stack_size(type);
@@ -647,6 +651,9 @@ void SVMCompiler::compile_type(Shader *shader, ShaderGraph *graph, ShaderType ty
                case SHADER_TYPE_DISPLACEMENT:
                        clin = node->input("Displacement");
                        break;
+               case SHADER_TYPE_BUMP:
+                       clin = node->input("Normal");
+                       break;
                default:
                        assert(0);
                        break;
@@ -663,6 +670,13 @@ void SVMCompiler::compile_type(Shader *shader, ShaderGraph *graph, ShaderType ty
                        output->stack_offset = SVM_STACK_INVALID;
        }
 
+       /* for the bump shader we need add a node to store the shader state */
+       int bump_state_offset = SVM_STACK_INVALID;
+       if(type == SHADER_TYPE_BUMP) {
+               bump_state_offset = stack_find_offset(SVM_BUMP_EVAL_STATE_SIZE);
+               add_node(NODE_ENTER_BUMP_EVAL, bump_state_offset);
+       }
+
        if(shader->used) {
                if(clin->link) {
                        bool generate = false;
@@ -680,6 +694,9 @@ void SVMCompiler::compile_type(Shader *shader, ShaderGraph *graph, ShaderType ty
                                        generate = true;
                                        shader->has_displacement = true;
                                        break;
+                               case SHADER_TYPE_BUMP: /* generate bump shader */
+                                       generate = true;
+                                       break;
                                default:
                                        break;
                        }
@@ -696,13 +713,21 @@ void SVMCompiler::compile_type(Shader *shader, ShaderGraph *graph, ShaderType ty
                node->compile(*this);
        }
 
+       /* add node to restore state after bump shader has finished */
+       if(type == SHADER_TYPE_BUMP) {
+               add_node(NODE_LEAVE_BUMP_EVAL, bump_state_offset);
+       }
+
        /* if compile failed, generate empty shader */
        if(compile_failed) {
                svm_nodes.clear();
                compile_failed = false;
        }
 
-       add_node(NODE_END, 0, 0, 0);
+       /* for bump shaders we fall thru to the surface shader, but if this is any other kind of shader it ends here */
+       if(type != SHADER_TYPE_BUMP) {
+               add_node(NODE_END, 0, 0, 0);
+       }
 }
 
 void SVMCompiler::compile(Scene *scene,
@@ -752,17 +777,22 @@ void SVMCompiler::compile(Scene *scene,
        shader->has_object_dependency = false;
        shader->has_integrator_dependency = false;
 
-       /* generate surface shader */
-       if(shader->displacement_method == DISPLACE_TRUE || !shader->graph_bump) {
-               scoped_timer timer((summary != NULL)? &summary->time_generate_surface: NULL);
-               compile_type(shader, shader->graph, SHADER_TYPE_SURFACE);
+       /* generate bump shader */
+       if(shader->displacement_method != DISPLACE_TRUE && shader->graph_bump) {
+               scoped_timer timer((summary != NULL)? &summary->time_generate_bump: NULL);
+               compile_type(shader, shader->graph_bump, SHADER_TYPE_BUMP);
                global_svm_nodes[index].y = global_svm_nodes.size();
                global_svm_nodes.insert(global_svm_nodes.end(), svm_nodes.begin(), svm_nodes.end());
        }
-       else {
-               scoped_timer timer((summary != NULL)? &summary->time_generate_bump: NULL);
-               compile_type(shader, shader->graph_bump, SHADER_TYPE_SURFACE);
-               global_svm_nodes[index].y = global_svm_nodes.size();
+
+       /* generate surface shader */
+       {
+               scoped_timer timer((summary != NULL)? &summary->time_generate_surface: NULL);
+               compile_type(shader, shader->graph, SHADER_TYPE_SURFACE);
+               /* only set jump offset if there's no bump shader, as the bump shader will fall thru to this one if it exists */
+               if(shader->displacement_method == DISPLACE_TRUE || !shader->graph_bump) {
+                       global_svm_nodes[index].y = global_svm_nodes.size();
+               }
                global_svm_nodes.insert(global_svm_nodes.end(), svm_nodes.begin(), svm_nodes.end());
        }
 
index e14d57d..99e91ca 100644 (file)
@@ -99,6 +99,7 @@ public:
        int stack_assign(ShaderInput *input);
        int stack_assign_if_linked(ShaderInput *input);
        int stack_assign_if_linked(ShaderOutput *output);
+       int stack_find_offset(int size);
        int stack_find_offset(SocketType::Type type);
        void stack_clear_offset(SocketType::Type type, int offset);
        void stack_link(ShaderInput *input, ShaderOutput *output);