diff --git a/intern/cycles/kernel/closure/bssrdf.h b/intern/cycles/kernel/closure/bssrdf.h index d6f889f3285..4ceff655dd5 100644 --- a/intern/cycles/kernel/closure/bssrdf.h +++ b/intern/cycles/kernel/closure/bssrdf.h @@ -31,6 +31,7 @@ __device int bssrdf_setup(ShaderClosure *sc, ClosureType type) } else { sc->data1 = clamp(sc->data1, 0.0f, 1.0f); /* texture blur */ + sc->T.x = clamp(sc->T.x, 0.0f, 1.0f); /* sharpness */ sc->type = type; return SD_BSDF|SD_BSDF_HAS_EVAL|SD_BSSRDF; @@ -95,17 +96,49 @@ __device void bssrdf_gaussian_sample(ShaderClosure *sc, float xi, float *r, floa __device float bssrdf_cubic_eval(ShaderClosure *sc, float r) { - const float Rm = sc->data0; + const float sharpness = sc->T.x; - if(r >= Rm) - return 0.0f; - - /* integrate (2*pi*r * 10*(R - r)^3)/(pi * R^5) from 0 to R = 1 */ - const float Rm5 = (Rm*Rm) * (Rm*Rm) * Rm; - const float f = Rm - min(r, Rm); - const float f3 = f*f*f; + if(sharpness == 0.0f) { + const float Rm = sc->data0; - return (f3 * 10.0f) / (Rm5 * M_PI_F); + if(r >= Rm) + return 0.0f; + + /* integrate (2*pi*r * 10*(R - r)^3)/(pi * R^5) from 0 to R = 1 */ + const float Rm5 = (Rm*Rm) * (Rm*Rm) * Rm; + const float f = Rm - r; + const float num = f*f*f; + + return (10.0f * num) / (Rm5 * M_PI_F); + + } + else { + float Rm = sc->data0*(1.0f + sharpness); + + if(r >= Rm) + return 0.0f; + + /* custom variation with extra sharpness, to match the previous code */ + const float y = 1.0f/(1.0f + sharpness); + float Rmy, ry, ryinv; + + if(sharpness == 1.0f) { + Rmy = sqrtf(Rm); + ry = sqrtf(r); + ryinv = (ry > 0.0f)? 1.0f/ry: 0.0f; + } + else { + Rmy = powf(Rm, y); + ry = powf(r, y); + ryinv = (r > 0.0f)? powf(r, 2.0f*y - 2.0f): 0.0f; + } + + const float Rmy5 = (Rmy*Rmy) * (Rmy*Rmy) * Rmy; + const float f = Rmy - ry; + const float num = f*(f*f)*(y*ryinv); + + return (10.0f * num) / (Rmy5 * M_PI_F); + } } __device float bssrdf_cubic_pdf(ShaderClosure *sc, float r) @@ -143,9 +176,16 @@ __device float bssrdf_cubic_quintic_root_find(float xi) __device void bssrdf_cubic_sample(ShaderClosure *sc, float xi, float *r, float *h) { - const float Rm = sc->data0; - const float r_ = bssrdf_cubic_quintic_root_find(xi) * Rm; + float Rm = sc->data0; + float r_ = bssrdf_cubic_quintic_root_find(xi); + const float sharpness = sc->T.x; + if(sharpness != 0.0f) { + r_ = powf(r_, 1.0f + sharpness); + Rm *= (1.0f + sharpness); + } + + r_ *= Rm; *r = r_; /* h^2 + r^2 = Rm^2 */ diff --git a/intern/cycles/kernel/osl/osl_bssrdf.cpp b/intern/cycles/kernel/osl/osl_bssrdf.cpp index 7405b0be567..975967c0bbb 100644 --- a/intern/cycles/kernel/osl/osl_bssrdf.cpp +++ b/intern/cycles/kernel/osl/osl_bssrdf.cpp @@ -56,10 +56,8 @@ public: void setup() { - sc.type = CLOSURE_BSSRDF_COMPATIBLE_ID; - sc.prim = NULL; + sc.type = CLOSURE_BSSRDF_CUBIC_ID; sc.data0 = fabsf(average(radius)); - sc.data1 = 0.0f; // XXX texture blur } bool mergeable(const ClosurePrimitive *other) const @@ -85,6 +83,19 @@ ClosureParam *closure_bssrdf_cubic_params() return params; } +ClosureParam *closure_bssrdf_cubic_extended_params() +{ + static ClosureParam params[] = { + CLOSURE_FLOAT3_PARAM(CubicBSSRDFClosure, sc.N), + CLOSURE_FLOAT3_PARAM(CubicBSSRDFClosure, radius), + CLOSURE_FLOAT_PARAM(CubicBSSRDFClosure, sc.data1), + CLOSURE_FLOAT_PARAM(CubicBSSRDFClosure, sc.T.x), + CLOSURE_STRING_KEYPARAM("label"), + CLOSURE_FINISH_PARAM(CubicBSSRDFClosure) + }; + return params; +} + CLOSURE_PREPARE(closure_bssrdf_cubic_prepare, CubicBSSRDFClosure) /* Gaussian */ @@ -97,9 +108,7 @@ public: void setup() { sc.type = CLOSURE_BSSRDF_GAUSSIAN_ID; - sc.prim = NULL; sc.data0 = fabsf(average(radius)); - sc.data1 = 0.0f; // XXX texture blurring! } bool mergeable(const ClosurePrimitive *other) const @@ -125,6 +134,18 @@ ClosureParam *closure_bssrdf_gaussian_params() return params; } +ClosureParam *closure_bssrdf_gaussian_extended_params() +{ + static ClosureParam params[] = { + CLOSURE_FLOAT3_PARAM(GaussianBSSRDFClosure, sc.N), + CLOSURE_FLOAT3_PARAM(GaussianBSSRDFClosure, radius), + CLOSURE_FLOAT_PARAM(GaussianBSSRDFClosure, sc.data1), + CLOSURE_STRING_KEYPARAM("label"), + CLOSURE_FINISH_PARAM(GaussianBSSRDFClosure) + }; + return params; +} + CLOSURE_PREPARE(closure_bssrdf_gaussian_prepare, GaussianBSSRDFClosure) CCL_NAMESPACE_END diff --git a/intern/cycles/kernel/osl/osl_bssrdf.h b/intern/cycles/kernel/osl/osl_bssrdf.h index ee9fc7c4ac5..fc1a4c587cc 100644 --- a/intern/cycles/kernel/osl/osl_bssrdf.h +++ b/intern/cycles/kernel/osl/osl_bssrdf.h @@ -48,7 +48,7 @@ public: ShaderClosure sc; float3 radius; - CBSSRDFClosure() : OSL::ClosurePrimitive(BSSRDF) { } + CBSSRDFClosure() : OSL::ClosurePrimitive(BSSRDF) { memset(&sc, 0, sizeof(sc)); } ~CBSSRDFClosure() { } int scattering() const { return LABEL_DIFFUSE; } diff --git a/intern/cycles/kernel/osl/osl_closures.cpp b/intern/cycles/kernel/osl/osl_closures.cpp index c03e50d4313..6a3f27fb7b1 100644 --- a/intern/cycles/kernel/osl/osl_closures.cpp +++ b/intern/cycles/kernel/osl/osl_closures.cpp @@ -221,6 +221,10 @@ void OSLShader::register_closures(OSLShadingSystem *ss_) closure_bssrdf_cubic_params(), closure_bssrdf_cubic_prepare); register_closure(ss, "bssrdf_gaussian", id++, closure_bssrdf_gaussian_params(), closure_bssrdf_gaussian_prepare); + register_closure(ss, "bssrdf_cubic", id++, + closure_bssrdf_cubic_extended_params(), closure_bssrdf_cubic_prepare); + register_closure(ss, "bssrdf_gaussian", id++, + closure_bssrdf_gaussian_extended_params(), closure_bssrdf_gaussian_prepare); } CCL_NAMESPACE_END diff --git a/intern/cycles/kernel/osl/osl_closures.h b/intern/cycles/kernel/osl/osl_closures.h index e3a7e890597..6632c2c57e5 100644 --- a/intern/cycles/kernel/osl/osl_closures.h +++ b/intern/cycles/kernel/osl/osl_closures.h @@ -52,6 +52,8 @@ OSL::ClosureParam *closure_westin_backscatter_params(); OSL::ClosureParam *closure_westin_sheen_params(); OSL::ClosureParam *closure_bssrdf_cubic_params(); OSL::ClosureParam *closure_bssrdf_gaussian_params(); +OSL::ClosureParam *closure_bssrdf_cubic_extended_params(); +OSL::ClosureParam *closure_bssrdf_gaussian_extended_params(); void closure_emission_prepare(OSL::RendererServices *, int id, void *data); void closure_background_prepare(OSL::RendererServices *, int id, void *data); diff --git a/intern/cycles/kernel/osl/osl_shader.cpp b/intern/cycles/kernel/osl/osl_shader.cpp index c55f36bcc9d..d2ccb7da84c 100644 --- a/intern/cycles/kernel/osl/osl_shader.cpp +++ b/intern/cycles/kernel/osl/osl_shader.cpp @@ -239,6 +239,7 @@ static void flatten_surface_closure_tree(ShaderData *sd, int path_flag, sc.type = bssrdf->sc.type; sc.N = bssrdf->sc.N; sc.data1 = bssrdf->sc.data1; + sc.T.x = bssrdf->sc.T.x; sc.prim = NULL; /* disable in case of diffuse ancestor, can't see it well then and diff --git a/intern/cycles/kernel/shaders/node_subsurface_scattering.osl b/intern/cycles/kernel/shaders/node_subsurface_scattering.osl index 18864118172..5c1d1be0ee7 100644 --- a/intern/cycles/kernel/shaders/node_subsurface_scattering.osl +++ b/intern/cycles/kernel/shaders/node_subsurface_scattering.osl @@ -20,14 +20,15 @@ shader node_subsurface_scattering( color Color = 0.8, float Scale = 1.0, vector Radius = vector(0.1, 0.1, 0.1), - float TextureBlur = 0.0, // XXX use + float TextureBlur = 0.0, + float Sharpness = 0.0, string Falloff = "Cubic", normal Normal = N, output closure color BSSRDF = 0) { if(Falloff == "Gaussian") - BSSRDF = Color * bssrdf_gaussian(N, Scale * Radius); - else /* Cubic, hardcoded to compatible closure for now */ - BSSRDF = Color * bssrdf_cubic(N, Scale * Radius); + BSSRDF = Color * bssrdf_gaussian(N, Scale * Radius, TextureBlur); + else + BSSRDF = Color * bssrdf_cubic(N, Scale * Radius, TextureBlur, Sharpness); } diff --git a/intern/cycles/kernel/shaders/stdosl.h b/intern/cycles/kernel/shaders/stdosl.h index 7d1c2443ee7..3ad2bbc0588 100644 --- a/intern/cycles/kernel/shaders/stdosl.h +++ b/intern/cycles/kernel/shaders/stdosl.h @@ -465,11 +465,12 @@ closure color holdout() BUILTIN; closure color ambient_occlusion() BUILTIN; // BSSRDF -closure color bssrdf_cubic(normal N, vector radius) BUILTIN; -closure color bssrdf_gaussian(normal N, vector radius) BUILTIN; +closure color bssrdf_cubic(normal N, vector radius, float texture_blur, float sharpness) BUILTIN; +closure color bssrdf_gaussian(normal N, vector radius, float texture_blur) BUILTIN; // Backwards compatibility - +closure color bssrdf_cubic(normal N, vector radius) BUILTIN; +closure color bssrdf_gaussian(normal N, vector radius) BUILTIN; closure color specular_toon(normal N, float size, float smooth) BUILTIN; // Renderer state diff --git a/intern/cycles/kernel/svm/svm_closure.h b/intern/cycles/kernel/svm/svm_closure.h index 42d98092b41..9a9aefa5b29 100644 --- a/intern/cycles/kernel/svm/svm_closure.h +++ b/intern/cycles/kernel/svm/svm_closure.h @@ -108,13 +108,13 @@ __device void svm_node_closure_bsdf(KernelGlobals *kg, ShaderData *sd, float *st if(mix_weight == 0.0f) return; - float3 N = stack_valid(data_node.y)? stack_load_float3(stack, data_node.y): sd->N; + float3 N = stack_valid(data_node.x)? stack_load_float3(stack, data_node.x): sd->N; #else decode_node_uchar4(node.y, &type, ¶m1_offset, ¶m2_offset, NULL); float mix_weight = 1.0f; uint4 data_node = read_node(kg, offset); - float3 N = stack_valid(data_node.y)? stack_load_float3(stack, data_node.y): sd->N; + float3 N = stack_valid(data_node.x)? stack_load_float3(stack, data_node.x): sd->N; #endif float param1 = (stack_valid(param1_offset))? stack_load_float(stack, param1_offset): __uint_as_float(node.z); @@ -279,10 +279,10 @@ __device void svm_node_closure_bsdf(KernelGlobals *kg, ShaderData *sd, float *st sc->N = N; #ifdef __ANISOTROPIC__ - sc->T = stack_load_float3(stack, data_node.z); + sc->T = stack_load_float3(stack, data_node.y); /* rotate tangent */ - float rotation = stack_load_float(stack, data_node.w); + float rotation = stack_load_float(stack, data_node.z); if(rotation != 0.0f) sc->T = rotate_around_axis(sc->T, sc->N, rotation * M_2PI_F); @@ -353,7 +353,9 @@ __device void svm_node_closure_bsdf(KernelGlobals *kg, ShaderData *sd, float *st if(sample_weight > 1e-5f && sd->num_closure+2 < MAX_CLOSURE) { /* radius * scale */ - float3 radius = stack_load_float3(stack, data_node.w)*param1; + float3 radius = stack_load_float3(stack, data_node.z)*param1; + /* sharpness */ + float sharpness = stack_load_float(stack, data_node.w); /* texture color blur */ float texture_blur = param2; @@ -363,6 +365,7 @@ __device void svm_node_closure_bsdf(KernelGlobals *kg, ShaderData *sd, float *st sc->sample_weight = sample_weight; sc->data0 = radius.x; sc->data1 = texture_blur; + sc->T.x = sharpness; #ifdef __OSL__ sc->prim = NULL; #endif @@ -378,6 +381,7 @@ __device void svm_node_closure_bsdf(KernelGlobals *kg, ShaderData *sd, float *st sc->sample_weight = sample_weight; sc->data0 = radius.y; sc->data1 = texture_blur; + sc->T.x = sharpness; #ifdef __OSL__ sc->prim = NULL; #endif @@ -393,6 +397,7 @@ __device void svm_node_closure_bsdf(KernelGlobals *kg, ShaderData *sd, float *st sc->sample_weight = sample_weight; sc->data0 = radius.z; sc->data1 = texture_blur; + sc->T.x = sharpness; #ifdef __OSL__ sc->prim = NULL; #endif diff --git a/intern/cycles/render/nodes.cpp b/intern/cycles/render/nodes.cpp index a9c17c8bcd5..06acb9e749e 100644 --- a/intern/cycles/render/nodes.cpp +++ b/intern/cycles/render/nodes.cpp @@ -1375,7 +1375,7 @@ BsdfNode::BsdfNode(bool scattering_) } } -void BsdfNode::compile(SVMCompiler& compiler, ShaderInput *param1, ShaderInput *param2, ShaderInput *param3) +void BsdfNode::compile(SVMCompiler& compiler, ShaderInput *param1, ShaderInput *param2, ShaderInput *param3, ShaderInput *param4) { ShaderInput *color_in = input("Color"); ShaderInput *normal_in = input("Normal"); @@ -1394,6 +1394,8 @@ void BsdfNode::compile(SVMCompiler& compiler, ShaderInput *param1, ShaderInput * compiler.stack_assign(param2); if(param3) compiler.stack_assign(param3); + if(param4) + compiler.stack_assign(param4); if(normal_in->link) compiler.stack_assign(normal_in); @@ -1410,12 +1412,14 @@ void BsdfNode::compile(SVMCompiler& compiler, ShaderInput *param1, ShaderInput * __float_as_int((param2)? param2->value.x: 0.0f)); if(tangent_in) { - compiler.add_node(NODE_CLOSURE_BSDF, normal_in->stack_offset, tangent_in->stack_offset, - (param3)? param3->stack_offset: SVM_STACK_INVALID); + compiler.add_node(normal_in->stack_offset, tangent_in->stack_offset, + (param3)? param3->stack_offset: SVM_STACK_INVALID, + (param4)? param4->stack_offset: SVM_STACK_INVALID); } else { - compiler.add_node(NODE_CLOSURE_BSDF, normal_in->stack_offset, SVM_STACK_INVALID, - (param3)? param3->stack_offset: SVM_STACK_INVALID); + compiler.add_node(normal_in->stack_offset, SVM_STACK_INVALID, + (param3)? param3->stack_offset: SVM_STACK_INVALID, + (param4)? param4->stack_offset: SVM_STACK_INVALID); } } @@ -1707,12 +1711,13 @@ SubsurfaceScatteringNode::SubsurfaceScatteringNode() add_input("Scale", SHADER_SOCKET_FLOAT, 0.01f); add_input("Radius", SHADER_SOCKET_VECTOR, make_float3(0.1f, 0.1f, 0.1f)); + add_input("Sharpness", SHADER_SOCKET_FLOAT, 0.0f); add_input("Texture Blur", SHADER_SOCKET_FLOAT, 1.0f); } void SubsurfaceScatteringNode::compile(SVMCompiler& compiler) { - BsdfNode::compile(compiler, input("Scale"), input("Texture Blur"), input("Radius")); + BsdfNode::compile(compiler, input("Scale"), input("Texture Blur"), input("Radius"), input("Sharpness")); } void SubsurfaceScatteringNode::compile(OSLCompiler& compiler) diff --git a/intern/cycles/render/nodes.h b/intern/cycles/render/nodes.h index 1a2494d96ea..50338ddd5c0 100644 --- a/intern/cycles/render/nodes.h +++ b/intern/cycles/render/nodes.h @@ -203,7 +203,7 @@ public: BsdfNode(bool scattering = false); SHADER_NODE_BASE_CLASS(BsdfNode); - void compile(SVMCompiler& compiler, ShaderInput *param1, ShaderInput *param2, ShaderInput *param3 = NULL); + void compile(SVMCompiler& compiler, ShaderInput *param1, ShaderInput *param2, ShaderInput *param3 = NULL, ShaderInput *param4 = NULL); ClosureType closure; bool scattering; diff --git a/source/blender/gpu/shaders/gpu_shader_material.glsl b/source/blender/gpu/shaders/gpu_shader_material.glsl index 2aee5b4846a..644cf71d626 100644 --- a/source/blender/gpu/shaders/gpu_shader_material.glsl +++ b/source/blender/gpu/shaders/gpu_shader_material.glsl @@ -2087,7 +2087,7 @@ void node_bsdf_velvet(vec4 color, float sigma, vec3 N, out vec4 result) node_bsdf_diffuse(color, 0.0, N, result); } -void node_subsurface_scattering(vec4 color, float roughness, vec3 N, out vec4 result) +void node_subsurface_scattering(vec4 color, float scale, vec3 radius, float sharpen, float texture_blur, out vec4 result) { node_bsdf_diffuse(color, 0.0, N, result); } diff --git a/source/blender/makesrna/intern/rna_nodetree.c b/source/blender/makesrna/intern/rna_nodetree.c index 819d2613f29..eb18f791b04 100644 --- a/source/blender/makesrna/intern/rna_nodetree.c +++ b/source/blender/makesrna/intern/rna_nodetree.c @@ -2819,6 +2819,15 @@ static void rna_ShaderNodeScript_update(Main *bmain, Scene *scene, PointerRNA *p ED_node_tag_update_nodetree(bmain, ntree); } +static void rna_ShaderNodeSubsurface_update(Main *bmain, Scene *scene, PointerRNA *ptr) +{ + bNodeTree *ntree = (bNodeTree *)ptr->id.data; + bNode *node = (bNode *)ptr->data; + + nodeUpdate(ntree, node); + rna_Node_update(bmain, scene, ptr); +} + #else static EnumPropertyItem prop_image_layer_items[] = { @@ -3642,7 +3651,7 @@ static void def_sh_subsurface(StructRNA *srna) RNA_def_property_enum_sdna(prop, NULL, "custom1"); RNA_def_property_enum_items(prop, prop_subsurface_falloff_items); RNA_def_property_ui_text(prop, "Falloff", "Function to determine how much light nearby points contribute based on their distance to the shading point"); - RNA_def_property_update(prop, NC_NODE | NA_EDITED, "rna_Node_update"); + RNA_def_property_update(prop, NC_NODE | NA_EDITED, "rna_ShaderNodeSubsurface_update"); } static void def_sh_script(StructRNA *srna) diff --git a/source/blender/nodes/shader/nodes/node_shader_subsurface_scattering.c b/source/blender/nodes/shader/nodes/node_shader_subsurface_scattering.c index 8ad529d192e..fd864f1c7c6 100644 --- a/source/blender/nodes/shader/nodes/node_shader_subsurface_scattering.c +++ b/source/blender/nodes/shader/nodes/node_shader_subsurface_scattering.c @@ -33,7 +33,8 @@ static bNodeSocketTemplate sh_node_subsurface_scattering_in[] = { { SOCK_RGBA, 1, N_("Color"), 0.8f, 0.8f, 0.8f, 1.0f, 0.0f, 1.0f}, { SOCK_FLOAT, 1, N_("Scale"), 1.0, 0.0f, 0.0f, 0.0f, 0.0f, 1000.0f}, { SOCK_VECTOR, 1, N_("Radius"), 1.0f, 1.0f, 1.0f, 0.0f, 0.0f, 100.0f}, - { SOCK_FLOAT, 1, N_("Texture Blur"), 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f}, + { SOCK_FLOAT, 1, N_("Sharpness"), 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f, PROP_FACTOR}, + { SOCK_FLOAT, 1, N_("Texture Blur"), 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f, PROP_FACTOR}, { SOCK_VECTOR, 1, N_("Normal"), 0.0f, 0.0f, 0.0f, 1.0f, -1.0f, 1.0f, PROP_NONE, SOCK_HIDE_VALUE}, { -1, 0, "" } }; @@ -43,6 +44,20 @@ static bNodeSocketTemplate sh_node_subsurface_scattering_out[] = { { -1, 0, "" } }; +static void node_shader_init_subsurface_scattering(bNodeTree *UNUSED(ntree), bNode *node) +{ + /*bNodeSocket *sock;*/ + + node->custom1 = SHD_SUBSURFACE_CUBIC; + + /*for (sock = node->inputs.first; sock; sock = sock->next) { + if (strcmp(sock->name, "Sharpness") == 0) { + bNodeSocketValueFloat *dval = sock->default_value; + dval->value = 0.0f; + } + }*/ +} + static int node_shader_gpu_subsurface_scattering(GPUMaterial *mat, bNode *UNUSED(node), bNodeExecData *UNUSED(execdata), GPUNodeStack *in, GPUNodeStack *out) { if (!in[1].link) @@ -51,6 +66,22 @@ static int node_shader_gpu_subsurface_scattering(GPUMaterial *mat, bNode *UNUSED return GPU_stack_link(mat, "node_subsurface_scattering", in, out); } +static void node_shader_update_subsurface_scattering(bNodeTree *UNUSED(ntree), bNode *node) +{ + bNodeSocket *sock; + int falloff = node->custom1; + + for (sock = node->inputs.first; sock; sock = sock->next) { + if (strcmp(sock->name, "Sharpness") == 0) { + if (falloff == SHD_SUBSURFACE_CUBIC) + sock->flag &= ~SOCK_UNAVAIL; + else + sock->flag |= SOCK_UNAVAIL; + + } + } +} + /* node type definition */ void register_node_type_sh_subsurface_scattering(void) { @@ -60,9 +91,10 @@ void register_node_type_sh_subsurface_scattering(void) node_type_compatibility(&ntype, NODE_NEW_SHADING); node_type_socket_templates(&ntype, sh_node_subsurface_scattering_in, sh_node_subsurface_scattering_out); node_type_size_preset(&ntype, NODE_SIZE_MIDDLE); - node_type_init(&ntype, NULL); + node_type_init(&ntype, node_shader_init_subsurface_scattering); node_type_storage(&ntype, "", NULL, NULL); node_type_gpu(&ntype, node_shader_gpu_subsurface_scattering); + node_type_update(&ntype, node_shader_update_subsurface_scattering, NULL); nodeRegisterType(&ntype); }