Cycles: initial subsurface multiple scattering support. It's not working as
authorBrecht Van Lommel <brechtvanlommel@pandora.be>
Mon, 1 Apr 2013 20:26:52 +0000 (20:26 +0000)
committerBrecht Van Lommel <brechtvanlommel@pandora.be>
Mon, 1 Apr 2013 20:26:52 +0000 (20:26 +0000)
well as I would like, but it works, just add a subsurface scattering node and
you can use it like any other BSDF.

It is using fully raytraced sampling compatible with progressive rendering
and other more advanced rendering algorithms we might used in the future, and
it uses no extra memory so it's suitable for complex scenes.

Disadvantage is that it can be quite noisy and slow. Two limitations that will
be solved are that it does not work with bump mapping yet, and that the falloff
function used is a simple cubic function, it's not using the real BSSRDF
falloff function yet.

The node has a color input, along with a scattering radius for each RGB color
channel along with an overall scale factor for the radii.

There is also no GPU support yet, will test if I can get that working later.

Node Documentation:
http://wiki.blender.org/index.php/Doc:2.6/Manual/Render/Cycles/Nodes/Shaders#BSSRDF

Implementation notes:
http://wiki.blender.org/index.php/Dev:2.6/Source/Render/Cycles/Subsurface_Scattering

55 files changed:
intern/cycles/blender/addon/properties.py
intern/cycles/blender/addon/ui.py
intern/cycles/blender/blender_shader.cpp
intern/cycles/blender/blender_sync.cpp
intern/cycles/kernel/CMakeLists.txt
intern/cycles/kernel/closure/bsdf.h
intern/cycles/kernel/closure/bssrdf.h [new file with mode: 0644]
intern/cycles/kernel/kernel_bvh.h
intern/cycles/kernel/kernel_camera.h
intern/cycles/kernel/kernel_compat_cpu.h
intern/cycles/kernel/kernel_compat_cuda.h
intern/cycles/kernel/kernel_compat_opencl.h
intern/cycles/kernel/kernel_globals.h
intern/cycles/kernel/kernel_path.h
intern/cycles/kernel/kernel_random.h
intern/cycles/kernel/kernel_shader.h
intern/cycles/kernel/kernel_subsurface.h [new file with mode: 0644]
intern/cycles/kernel/kernel_types.h
intern/cycles/kernel/osl/CMakeLists.txt
intern/cycles/kernel/osl/osl_bssrdf.cpp [new file with mode: 0644]
intern/cycles/kernel/osl/osl_bssrdf.h [new file with mode: 0644]
intern/cycles/kernel/osl/osl_closures.cpp
intern/cycles/kernel/osl/osl_closures.h
intern/cycles/kernel/osl/osl_shader.cpp
intern/cycles/kernel/shaders/CMakeLists.txt
intern/cycles/kernel/shaders/node_subsurface_scattering.osl [new file with mode: 0644]
intern/cycles/kernel/shaders/stdosl.h
intern/cycles/kernel/svm/svm_closure.h
intern/cycles/kernel/svm/svm_types.h
intern/cycles/render/CMakeLists.txt
intern/cycles/render/bssrdf.cpp [new file with mode: 0644]
intern/cycles/render/bssrdf.h [new file with mode: 0644]
intern/cycles/render/film.cpp
intern/cycles/render/graph.h
intern/cycles/render/integrator.cpp
intern/cycles/render/integrator.h
intern/cycles/render/nodes.cpp
intern/cycles/render/nodes.h
intern/cycles/render/osl.cpp
intern/cycles/render/osl.h
intern/cycles/render/scene.cpp
intern/cycles/render/shader.cpp
intern/cycles/render/shader.h
intern/cycles/render/svm.cpp
intern/cycles/render/svm.h
intern/cycles/render/tables.cpp
intern/cycles/render/tables.h
intern/cycles/util/util_math.h
source/blender/blenkernel/BKE_node.h
source/blender/blenkernel/intern/node.c
source/blender/gpu/shaders/gpu_shader_material.glsl
source/blender/nodes/CMakeLists.txt
source/blender/nodes/NOD_shader.h
source/blender/nodes/NOD_static_types.h
source/blender/nodes/shader/nodes/node_shader_subsurface_scattering.c [new file with mode: 0644]

index 039fd39fc7dec219c5a58e4d91d92578a82e931f..ac0a1d7bdb7996c2510f13975ed2f2caeea87cd1 100644 (file)
@@ -205,6 +205,13 @@ class CyclesRenderSettings(bpy.types.PropertyGroup):
                 default=1,
                 )
 
+        cls.subsurface_samples = IntProperty(
+                name="Subsurface Samples",
+                description="Number of subsurface scattering samples to render for each AA sample",
+                min=1, max=10000,
+                default=1,
+                )
+
         cls.no_caustics = BoolProperty(
                 name="No Caustics",
                 description="Leave out caustics, resulting in a darker image with less noise",
index 4c3061e13a9342b97ba24e4a3684f8b9032b62e7..6a9b242c32f279f835ccfcce3fcf668c084b4013 100644 (file)
@@ -83,6 +83,7 @@ class CyclesRender_PT_sampling(CyclesButtonsPanel, Panel):
             sub.prop(cscene, "transmission_samples", text="Transmission")
             sub.prop(cscene, "ao_samples", text="AO")
             sub.prop(cscene, "mesh_light_samples", text="Mesh Light")
+            sub.prop(cscene, "subsurface_samples", text="Subsurface")
 
 
 class CyclesRender_PT_light_paths(CyclesButtonsPanel, Panel):
index b451764c347c6677046a531e92d093f90d6add4c..7749f164b90452cb6764e3083a8b13865f7fae38 100644 (file)
@@ -252,7 +252,6 @@ static ShaderNode *add_node(Scene *scene, BL::BlendData b_data, BL::Scene b_scen
        else if (b_node.is_a(&RNA_ShaderNodeNormal)) {
                BL::Node::outputs_iterator out_it;
                b_node.outputs.begin(out_it);
-               BL::NodeSocket vec_sock(*out_it);
                
                NormalNode *norm = new NormalNode();
                norm->direction = get_node_output_vector(b_node, "Normal");
@@ -302,6 +301,9 @@ static ShaderNode *add_node(Scene *scene, BL::BlendData b_data, BL::Scene b_scen
        else if (b_node.is_a(&RNA_ShaderNodeBsdfDiffuse)) {
                node = new DiffuseBsdfNode();
        }
+       else if (b_node.is_a(&RNA_ShaderNodeSubsurfaceScattering)) {
+               node = new SubsurfaceScatteringNode();
+       }
        else if (b_node.is_a(&RNA_ShaderNodeBsdfGlossy)) {
                BL::ShaderNodeBsdfGlossy b_glossy_node(b_node);
                GlossyBsdfNode *glossy = new GlossyBsdfNode();
index 66401d80a2e88af10e663d0947cf8e9ca742a863..721eaeefc08c56b5f33a22bedbeeac44ee6aa728 100644 (file)
@@ -196,6 +196,7 @@ void BlenderSync::sync_integrator()
        integrator->transmission_samples = get_int(cscene, "transmission_samples");
        integrator->ao_samples = get_int(cscene, "ao_samples");
        integrator->mesh_light_samples = get_int(cscene, "mesh_light_samples");
+       integrator->subsurface_samples = get_int(cscene, "subsurface_samples");
        integrator->progressive = get_boolean(cscene, "progressive");
 
        if(integrator->modified(previntegrator))
index e83756b7c8a648f80e3d0ebaae49f0b496f767c5..fbaba1da0949c14b10fc5acced714d6f6432538f 100644 (file)
@@ -42,6 +42,7 @@ set(SRC_HEADERS
        kernel_projection.h
        kernel_random.h
        kernel_shader.h
+       kernel_subsurface.h
        kernel_textures.h
        kernel_triangle.h
        kernel_types.h
@@ -62,6 +63,7 @@ set(SRC_CLOSURE_HEADERS
        closure/bsdf_util.h
        closure/bsdf_ward.h
        closure/bsdf_westin.h
+       closure/bssrdf.h
        closure/emissive.h
        closure/volume.h
 )
index f26aefe7fd34559c34925693b827981ca54fd401..6403606c2dfd8d68efe2825e332cc469ceee3389 100644 (file)
@@ -29,6 +29,7 @@
 #include "../closure/bsdf_ward.h"
 #endif
 #include "../closure/bsdf_westin.h"
+#include "../closure/bssrdf.h"
 
 CCL_NAMESPACE_BEGIN
 
diff --git a/intern/cycles/kernel/closure/bssrdf.h b/intern/cycles/kernel/closure/bssrdf.h
new file mode 100644 (file)
index 0000000..1327fbd
--- /dev/null
@@ -0,0 +1,154 @@
+/*
+ * Copyright 2013, Blender Foundation.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+
+#ifndef __KERNEL_BSSRDF_H__
+#define __KERNEL_BSSRDF_H__
+
+CCL_NAMESPACE_BEGIN
+
+__device int bssrdf_setup(ShaderClosure *sc)
+{
+       if(sc->data0 < BSSRDF_MIN_RADIUS) {
+               /* revert to diffuse BSDF if radius too small */
+               sc->data0 = 0.0f;
+               sc->data1 = 0.0f;
+               return bsdf_diffuse_setup(sc);
+       }
+       else {
+               /* radius + IOR params */
+               sc->data0 = max(sc->data0, 0.0f);
+               sc->data1 = max(sc->data1, 1.0f);
+               sc->type = CLOSURE_BSSRDF_ID;
+
+               return SD_BSDF|SD_BSDF_HAS_EVAL|SD_BSSRDF;
+       }
+}
+
+/* Simple Cubic BSSRDF falloff */
+
+__device float bssrdf_cubic(float ld, float r)
+{
+       if(ld == 0.0f)
+               return (r == 0.0f)? 1.0f: 0.0f;
+
+       return powf(ld - min(r, ld), 3.0f) * 4.0f/powf(ld, 4.0f);
+}
+
+/* Original BSSRDF fallof function */
+
+typedef struct BSSRDFParams {
+       float eta;              /* index of refraction */
+       float sigma_t_; /* reduced extinction coefficient */
+       float sigma_tr; /* effective extinction coefficient */
+       float Fdr;              /* diffuse fresnel reflectance */
+       float D;                /* diffusion constant */
+       float A;
+       float alpha_;   /* reduced albedo */
+       float zr;               /* distance of virtual lightsource above surface */
+       float zv;               /* distance of virtual lightsource below surface */
+       float ld;               /* mean free path */
+       float ro;               /* diffuse reflectance */
+} BSSRDFParams;
+
+__device float bssrdf_reduced_albedo_Rd(float alpha_, float A, float ro)
+{
+       float sq;
+
+       sq = sqrt(3.0f*(1.0f - alpha_));
+       return (alpha_/2.0f)*(1.0f + expf((-4.0f/3.0f)*A*sq))*expf(-sq) - ro;
+}
+
+__device float bssrdf_compute_reduced_albedo(float A, float ro)
+{
+       const float tolerance = 1e-8;
+       const int max_iteration_count = 20;
+       float d, fsub, xn_1 = 0.0f, xn = 1.0f, fxn, fxn_1;
+       int i;
+
+       /* use secant method to compute reduced albedo using Rd function inverse
+        * with a given reflectance */
+       fxn = bssrdf_reduced_albedo_Rd(xn, A, ro);
+       fxn_1 = bssrdf_reduced_albedo_Rd(xn_1, A, ro);
+
+       for (i= 0; i < max_iteration_count; i++) {
+               fsub = (fxn - fxn_1);
+               if (fabsf(fsub) < tolerance)
+                       break;
+               d = ((xn - xn_1)/fsub)*fxn;
+               if (fabsf(d) < tolerance)
+                       break;
+
+               xn_1 = xn;
+               fxn_1 = fxn;
+               xn = xn - d;
+
+               if (xn > 1.0f) xn = 1.0f;
+               if (xn_1 > 1.0f) xn_1 = 1.0f;
+               
+               fxn = bssrdf_reduced_albedo_Rd(xn, A, ro);
+       }
+
+       /* avoid division by zero later */
+       if (xn <= 0.0f)
+               xn = 0.00001f;
+
+       return xn;
+}
+
+__device void bssrdf_setup_params(BSSRDFParams *ss, float refl, float radius, float ior)
+{
+       ss->eta = ior;
+       ss->Fdr = -1.440f/ior*ior + 0.710f/ior + 0.668f + 0.0636f*ior;
+       ss->A = (1.0f + ss->Fdr)/(1.0f - ss->Fdr);
+       ss->ld = radius;
+       ss->ro = min(refl, 0.999f);
+
+       ss->alpha_ = bssrdf_compute_reduced_albedo(ss->A, ss->ro);
+
+       ss->sigma_tr = 1.0f/ss->ld;
+       ss->sigma_t_ = ss->sigma_tr/sqrtf(3.0f*(1.0f - ss->alpha_));
+
+       ss->D = 1.0f/(3.0f*ss->sigma_t_);
+
+       ss->zr = 1.0f/ss->sigma_t_;
+       ss->zv = ss->zr + 4.0f*ss->A*ss->D;
+}
+
+/* exponential falloff function */
+
+__device float bssrdf_original(const BSSRDFParams *ss, float r)
+{
+       if(ss->ld == 0.0f)
+               return (r == 0.0f)? 1.0f: 0.0f;
+
+       float rr = r*r;
+       float sr, sv, Rdr, Rdv;
+
+       sr = sqrt(rr + ss->zr*ss->zr);
+       sv = sqrt(rr + ss->zv*ss->zv);
+
+       Rdr = ss->zr*(1.0f + ss->sigma_tr*sr)*expf(-ss->sigma_tr*sr)/(sr*sr*sr);
+       Rdv = ss->zv*(1.0f + ss->sigma_tr*sv)*expf(-ss->sigma_tr*sv)/(sv*sv*sv);
+
+       return ss->alpha_*(1.0f/(4.0f*(float)M_PI))*(Rdr + Rdv);
+}
+
+CCL_NAMESPACE_END
+
+#endif /* __KERNEL_BSSRDF_H__ */
+
index 2b9ebf35d0cfc935d5aa6ad83742ce894832dea2..ace48f4a1a2f3b5adf86515715dedb18c9b0f2a8 100644 (file)
@@ -923,6 +923,322 @@ __device_inline bool scene_intersect(KernelGlobals *kg, const Ray *ray, const ui
 #endif
 }
 
+/* Special ray intersection routines for subsurface scattering. In that case we
+ * only want to intersect with primitives in the same object, and if case of
+ * multiple hits we pick a single random primitive as the intersection point. */
+
+__device_inline void bvh_triangle_intersect_subsurface(KernelGlobals *kg, Intersection *isect,
+       float3 P, float3 idir, int object, int triAddr, float tmax, int *num_hits, float subsurface_random)
+{
+       /* compute and check intersection t-value */
+       float4 v00 = kernel_tex_fetch(__tri_woop, triAddr*TRI_NODE_SIZE+0);
+       float4 v11 = kernel_tex_fetch(__tri_woop, triAddr*TRI_NODE_SIZE+1);
+       float3 dir = 1.0f/idir;
+
+       float Oz = v00.w - P.x*v00.x - P.y*v00.y - P.z*v00.z;
+       float invDz = 1.0f/(dir.x*v00.x + dir.y*v00.y + dir.z*v00.z);
+       float t = Oz * invDz;
+
+       if(t > 0.0f && t < tmax) {
+               /* compute and check barycentric u */
+               float Ox = v11.w + P.x*v11.x + P.y*v11.y + P.z*v11.z;
+               float Dx = dir.x*v11.x + dir.y*v11.y + dir.z*v11.z;
+               float u = Ox + t*Dx;
+
+               if(u >= 0.0f) {
+                       /* compute and check barycentric v */
+                       float4 v22 = kernel_tex_fetch(__tri_woop, triAddr*TRI_NODE_SIZE+2);
+                       float Oy = v22.w + P.x*v22.x + P.y*v22.y + P.z*v22.z;
+                       float Dy = dir.x*v22.x + dir.y*v22.y + dir.z*v22.z;
+                       float v = Oy + t*Dy;
+
+                       if(v >= 0.0f && u + v <= 1.0f) {
+                               (*num_hits)++;
+
+                               if(subsurface_random * (*num_hits) <= 1.0f) {
+                                       /* record intersection */
+                                       isect->prim = triAddr;
+                                       isect->object = object;
+                                       isect->u = u;
+                                       isect->v = v;
+                                       isect->t = t;
+                               }
+                       }
+               }
+       }
+}
+
+__device_inline int bvh_intersect_subsurface(KernelGlobals *kg, const Ray *ray, Intersection *isect, int subsurface_object, float subsurface_random)
+{
+       /* traversal stack in CUDA thread-local memory */
+       int traversalStack[BVH_STACK_SIZE];
+       traversalStack[0] = ENTRYPOINT_SENTINEL;
+
+       /* traversal variables in registers */
+       int stackPtr = 0;
+       int nodeAddr = kernel_data.bvh.root;
+
+       /* ray parameters in registers */
+       const float tmax = ray->t;
+       float3 P = ray->P;
+       float3 idir = bvh_inverse_direction(ray->D);
+       int object = ~0;
+
+       int num_hits = 0;
+
+       isect->t = tmax;
+       isect->object = ~0;
+       isect->prim = ~0;
+       isect->u = 0.0f;
+       isect->v = 0.0f;
+
+       /* traversal loop */
+       do {
+               do
+               {
+                       /* traverse internal nodes */
+                       while(nodeAddr >= 0 && nodeAddr != ENTRYPOINT_SENTINEL)
+                       {
+                               bool traverseChild0, traverseChild1, closestChild1;
+                               int nodeAddrChild1;
+
+                               bvh_node_intersect(kg, &traverseChild0, &traverseChild1,
+                                       &closestChild1, &nodeAddr, &nodeAddrChild1,
+                                       P, idir, isect->t, ~0, nodeAddr);
+
+                               if(traverseChild0 != traverseChild1) {
+                                       /* one child was intersected */
+                                       if(traverseChild1) {
+                                               nodeAddr = nodeAddrChild1;
+                                       }
+                               }
+                               else {
+                                       if(!traverseChild0) {
+                                               /* neither child was intersected */
+                                               nodeAddr = traversalStack[stackPtr];
+                                               --stackPtr;
+                                       }
+                                       else {
+                                               /* both children were intersected, push the farther one */
+                                               if(closestChild1) {
+                                                       int tmp = nodeAddr;
+                                                       nodeAddr = nodeAddrChild1;
+                                                       nodeAddrChild1 = tmp;
+                                               }
+
+                                               ++stackPtr;
+                                               traversalStack[stackPtr] = nodeAddrChild1;
+                                       }
+                               }
+                       }
+
+                       /* if node is leaf, fetch triangle list */
+                       if(nodeAddr < 0) {
+                               float4 leaf = kernel_tex_fetch(__bvh_nodes, (-nodeAddr-1)*BVH_NODE_SIZE+(BVH_NODE_SIZE-1));
+                               int primAddr = __float_as_int(leaf.x);
+
+#ifdef __INSTANCING__
+                               if(primAddr >= 0) {
+#endif
+                                       int primAddr2 = __float_as_int(leaf.y);
+
+                                       /* pop */
+                                       nodeAddr = traversalStack[stackPtr];
+                                       --stackPtr;
+
+                                       /* primitive intersection */
+                                       while(primAddr < primAddr2) {
+                                               /* only primitives from the same object */
+                                               uint tri_object = (object == ~0)? kernel_tex_fetch(__prim_object, primAddr): object;
+
+                                               if(tri_object == subsurface_object) {
+                                                       /* intersect ray against primitive */
+#ifdef __HAIR__
+                                                       uint segment = kernel_tex_fetch(__prim_segment, primAddr);
+                                                       if(segment == ~0) /* ignore hair for sss */
+#endif
+                                                               bvh_triangle_intersect_subsurface(kg, isect, P, idir, object, primAddr, tmax, &num_hits, subsurface_random);
+                                               }
+
+                                               primAddr++;
+                                       }
+#ifdef __INSTANCING__
+                               }
+                               else {
+                                       /* instance push */
+                                       if(subsurface_object == kernel_tex_fetch(__prim_object, -primAddr-1)) {
+                                               object = subsurface_object;
+                                               bvh_instance_push(kg, object, ray, &P, &idir, &isect->t, tmax);
+
+                                               ++stackPtr;
+                                               traversalStack[stackPtr] = ENTRYPOINT_SENTINEL;
+
+                                               nodeAddr = kernel_tex_fetch(__object_node, object);
+                                       }
+                               }
+#endif
+                       }
+               } while(nodeAddr != ENTRYPOINT_SENTINEL);
+
+#ifdef __INSTANCING__
+               if(stackPtr >= 0) {
+                       kernel_assert(object != ~0);
+
+                       /* instance pop */
+                       bvh_instance_pop(kg, object, ray, &P, &idir, &isect->t, tmax);
+                       object = ~0;
+                       nodeAddr = traversalStack[stackPtr];
+                       --stackPtr;
+               }
+#endif
+       } while(nodeAddr != ENTRYPOINT_SENTINEL);
+
+       return num_hits;
+}
+
+#ifdef __OBJECT_MOTION__
+__device bool bvh_intersect_motion_subsurface(KernelGlobals *kg, const Ray *ray, Intersection *isect, int subsurface_object, float subsurface_random)
+{
+       /* traversal stack in CUDA thread-local memory */
+       int traversalStack[BVH_STACK_SIZE];
+       traversalStack[0] = ENTRYPOINT_SENTINEL;
+
+       /* traversal variables in registers */
+       int stackPtr = 0;
+       int nodeAddr = kernel_data.bvh.root;
+
+       /* ray parameters in registers */
+       const float tmax = ray->t;
+       float3 P = ray->P;
+       float3 idir = bvh_inverse_direction(ray->D);
+       int object = ~0;
+
+       int num_hits = 0;
+
+       Transform ob_tfm;
+
+       isect->t = tmax;
+       isect->object = ~0;
+       isect->prim = ~0;
+       isect->u = 0.0f;
+       isect->v = 0.0f;
+
+       /* traversal loop */
+       do {
+               do
+               {
+                       /* traverse internal nodes */
+                       while(nodeAddr >= 0 && nodeAddr != ENTRYPOINT_SENTINEL)
+                       {
+                               bool traverseChild0, traverseChild1, closestChild1;
+                               int nodeAddrChild1;
+
+                               bvh_node_intersect(kg, &traverseChild0, &traverseChild1,
+                                       &closestChild1, &nodeAddr, &nodeAddrChild1,
+                                       P, idir, isect->t, ~0, nodeAddr);
+
+                               if(traverseChild0 != traverseChild1) {
+                                       /* one child was intersected */
+                                       if(traverseChild1) {
+                                               nodeAddr = nodeAddrChild1;
+                                       }
+                               }
+                               else {
+                                       if(!traverseChild0) {
+                                               /* neither child was intersected */
+                                               nodeAddr = traversalStack[stackPtr];
+                                               --stackPtr;
+                                       }
+                                       else {
+                                               /* both children were intersected, push the farther one */
+                                               if(closestChild1) {
+                                                       int tmp = nodeAddr;
+                                                       nodeAddr = nodeAddrChild1;
+                                                       nodeAddrChild1 = tmp;
+                                               }
+
+                                               ++stackPtr;
+                                               traversalStack[stackPtr] = nodeAddrChild1;
+                                       }
+                               }
+                       }
+
+                       /* if node is leaf, fetch triangle list */
+                       if(nodeAddr < 0) {
+                               float4 leaf = kernel_tex_fetch(__bvh_nodes, (-nodeAddr-1)*BVH_NODE_SIZE+(BVH_NODE_SIZE-1));
+                               int primAddr = __float_as_int(leaf.x);
+
+                               if(primAddr >= 0) {
+                                       int primAddr2 = __float_as_int(leaf.y);
+
+                                       /* pop */
+                                       nodeAddr = traversalStack[stackPtr];
+                                       --stackPtr;
+
+                                       /* primitive intersection */
+                                       while(primAddr < primAddr2) {
+                                               /* only primitives from the same object */
+                                               uint tri_object = (object == ~0)? kernel_tex_fetch(__prim_object, primAddr): object;
+
+                                               if(tri_object == subsurface_object) {
+                                                       /* intersect ray against primitive */
+#ifdef __HAIR__
+                                                       uint segment = kernel_tex_fetch(__prim_segment, primAddr);
+                                                       if(segment == ~0) /* ignore hair for sss */
+#endif
+                                                               bvh_triangle_intersect_subsurface(kg, isect, P, idir, object, primAddr, tmax, &num_hits, subsurface_random);
+                                               }
+
+                                               primAddr++;
+                                       }
+                               }
+                               else {
+                                       /* instance push */
+                                       if(subsurface_object == kernel_tex_fetch(__prim_object, -primAddr-1)) {
+                                               object = subsurface_object;
+                                               object = kernel_tex_fetch(__prim_object, -primAddr-1);
+                                               bvh_instance_motion_push(kg, object, ray, &P, &idir, &isect->t, &ob_tfm, tmax);
+
+                                               ++stackPtr;
+                                               traversalStack[stackPtr] = ENTRYPOINT_SENTINEL;
+
+                                               nodeAddr = kernel_tex_fetch(__object_node, object);
+                                       }
+                               }
+                       }
+               } while(nodeAddr != ENTRYPOINT_SENTINEL);
+
+               if(stackPtr >= 0) {
+                       kernel_assert(object != ~0);
+
+                       /* instance pop */
+                       bvh_instance_motion_pop(kg, object, ray, &P, &idir, &isect->t, &ob_tfm, tmax);
+                       object = ~0;
+                       nodeAddr = traversalStack[stackPtr];
+                       --stackPtr;
+               }
+       } while(nodeAddr != ENTRYPOINT_SENTINEL);
+
+       return num_hits;
+}
+#endif
+
+__device_inline int scene_intersect_subsurface(KernelGlobals *kg, const Ray *ray, Intersection *isect, int subsurface_object, float subsurface_random)
+{
+#ifdef __OBJECT_MOTION__
+       if(kernel_data.bvh.have_motion)
+               return bvh_intersect_motion_subsurface(kg, ray, isect, subsurface_object, subsurface_random);
+       else
+               return bvh_intersect_subsurface(kg, ray, isect, subsurface_object, subsurface_random);
+#else
+       return bvh_intersect_subsurface(kg, ray, isect, subsurface_object, subsurface_random);
+#endif
+}
+
+
+
+/* Ray offset to avoid self intersection */
+
 __device_inline float3 ray_offset(float3 P, float3 Ng)
 {
 #ifdef __INTERSECTION_REFINE__
@@ -971,6 +1287,10 @@ __device_inline float3 ray_offset(float3 P, float3 Ng)
 #endif
 }
 
+/* Refine triangle intersection to more precise hit point. For rays that travel
+ * far the precision is often not so good, this reintersects the primitive from
+ * a closer distance. */
+
 __device_inline float3 bvh_triangle_refine(KernelGlobals *kg, ShaderData *sd, const Intersection *isect, const Ray *ray)
 {
        float3 P = ray->P;
index 1d081b5468177448de0055ba2a017f997d9f0b1b..e26addd19e450296389aa61295d90397b5669aa5 100644 (file)
@@ -225,8 +225,8 @@ __device void camera_sample(KernelGlobals *kg, int x, int y, float filter_u, flo
 {
        /* pixel filter */
        int filter_table_offset = kernel_data.film.filter_table_offset;
-       float raster_x = x + kernel_tex_lookup(__lookup_table, filter_u, filter_table_offset, FILTER_TABLE_SIZE);
-       float raster_y = y + kernel_tex_lookup(__lookup_table, filter_v, filter_table_offset, FILTER_TABLE_SIZE);
+       float raster_x = x + lookup_table_read(kg, filter_u, filter_table_offset, FILTER_TABLE_SIZE);
+       float raster_y = y + lookup_table_read(kg, filter_v, filter_table_offset, FILTER_TABLE_SIZE);
 
 #ifdef __CAMERA_MOTION__
        /* motion blur */
index b7df7f86bf62c78ee6096eabd182f09f8f8bde1f..9972a63bfbb651562ff0562f24e8a883a0331211 100644 (file)
@@ -57,19 +57,6 @@ template<typename T> struct texture  {
        }
 #endif
 
-       float lookup(float x, int offset, int size)
-       {
-               kernel_assert(size == width);
-
-               x = clamp(x, 0.0f, 1.0f)*width;
-
-               int index = min((int)x, width-1);
-               int nindex = min(index+1, width-1);
-               float t = x - index;
-
-               return (1.0f - t)*data[index + offset] + t*data[nindex + offset];
-       }
-
        T *data;
        int width;
 };
index fdee59e225e04f7239c408d5078704802ddd5d5b..a11f8f403cdf6206bdbb1484942eb5d02ce172e5 100644 (file)
@@ -58,7 +58,6 @@ typedef texture<uchar4, 2, cudaReadModeNormalizedFloat> texture_image_uchar4;
 /* Macros to handle different memory storage on different devices */
 
 #define kernel_tex_fetch(t, index) tex1Dfetch(t, index)
-#define kernel_tex_lookup(t, x, offset, size) tex1D(t, x) // XXX broken!
 #define kernel_tex_image_interp(t, x, y) tex2D(t, x, y)
 
 #define kernel_data __data
index dcbaf8fdbd25b7204319f4712d7035a6a8d86cf5..999820891b255878f28b5c5c011c7e29db23e71d 100644 (file)
 /* no assert in opencl */
 #define kernel_assert(cond)
 
-/* manual implementation of interpolated 1D lookup */
-__device float kernel_tex_lookup_(__global float *data, int offset, int width, float x)
-{
-       x = clamp(x, 0.0f, 1.0f)*width;
-
-       int index = min((int)x, width-1);
-       int nindex = min(index+1, width-1);
-       float t = x - index;
-
-       return (1.0f - t)*data[index + offset] + t*data[nindex + offset];
-}
-
 /* make_type definitions with opencl style element initializers */
 #ifdef make_float2
 #undef make_float2
index 529b7b8768f1ee73275a898662d5e122c7be727b..abf1f5b4cb0dcc6aaa1589f2209f855779d7abb2 100644 (file)
@@ -88,5 +88,39 @@ typedef struct KernelGlobals {
 
 #endif
 
+/* Interpolated lookup table access */
+
+__device float lookup_table_read(KernelGlobals *kg, float x, int offset, int size)
+{
+       x = clamp(x, 0.0f, 1.0f)*(size-1);
+
+       int index = min((int)x, size-1);
+       int nindex = min(index+1, size-1);
+       float t = x - index;
+
+       float data0 = kernel_tex_fetch(__lookup_table, index + offset);
+       if(t == 0.0f)
+               return data0;
+
+       float data1 = kernel_tex_fetch(__lookup_table, nindex + offset);
+       return (1.0f - t)*data0 + t*data1;
+}
+
+__device float lookup_table_read_2D(KernelGlobals *kg, float x, float y, int offset, int xsize, int ysize)
+{
+       y = clamp(y, 0.0f, 1.0f)*(ysize-1);
+
+       int index = min((int)y, ysize-1);
+       int nindex = min(index+1, ysize-1);
+       float t = y - index;
+
+       float data0 = lookup_table_read(kg, x, offset + xsize*index, xsize);
+       if(t == 0.0f)
+               return data0;
+
+       float data1 = lookup_table_read(kg, x, offset + xsize*nindex, xsize);
+       return (1.0f - t)*data0 + t*data1;
+}
+
 CCL_NAMESPACE_END
 
index 865ba7ca676908494353ee91f82fc0d289628cc4..bfa449301089b0ddf222339a603dae8a66aded29 100644 (file)
 #include "kernel_random.h"
 #include "kernel_passes.h"
 
+#ifdef __SUBSURFACE__
+#include "kernel_subsurface.h"
+#endif
+
 CCL_NAMESPACE_BEGIN
 
 typedef struct PathState {
@@ -149,7 +153,7 @@ __device_inline float path_state_terminate_probability(KernelGlobals *kg, PathSt
        }
 
        /* probalistic termination */
-       return average(throughput);
+       return average(throughput); /* todo: try using max here */
 }
 
 __device_inline bool shadow_blocked(KernelGlobals *kg, PathState *state, Ray *ray, float3 *shadow)
@@ -352,6 +356,24 @@ __device float4 kernel_path_progressive(KernelGlobals *kg, RNG *rng, int sample,
 
                throughput /= probability;
 
+#ifdef __SUBSURFACE__
+               /* bssrdf scatter to a different location on the same object, replacing
+                * the closures with a diffuse BSDF */
+               if(sd.flag & SD_BSSRDF) {
+                       float bssrdf_probability;
+                       ShaderClosure *sc = subsurface_scatter_pick_closure(kg, &sd, &bssrdf_probability);
+
+                       /* modify throughput for picking bssrdf or bsdf */
+                       throughput *= bssrdf_probability;
+
+                       /* do bssrdf scatter step if we picked a bssrdf closure */
+                       if(sc) {
+                               uint lcg_state = lcg_init(rbsdf);
+                               subsurface_scatter_step(kg, &sd, state.flag, sc, &lcg_state);
+                       }
+               }
+#endif
+
 #ifdef __AO__
                /* ambient occlusion */
                if(kernel_data.integrator.use_ambient_occlusion || (sd.flag & SD_AO)) {
@@ -481,7 +503,7 @@ __device float4 kernel_path_progressive(KernelGlobals *kg, RNG *rng, int sample,
 #ifdef __NON_PROGRESSIVE__
 
 __device void kernel_path_indirect(KernelGlobals *kg, RNG *rng, int sample, Ray ray, __global float *buffer,
-       float3 throughput, float throughput_normalize,
+       float3 throughput, float num_samples_adjust,
        float min_ray_pdf, float ray_pdf, PathState state, int rng_offset, PathRadiance *L)
 {
 #ifdef __LAMP_MIS__
@@ -554,7 +576,7 @@ __device void kernel_path_indirect(KernelGlobals *kg, RNG *rng, int sample, Ray
                /* path termination. this is a strange place to put the termination, it's
                 * mainly due to the mixed in MIS that we use. gives too many unneeded
                 * shader evaluations, only need emission if we are going to terminate */
-               float probability = path_state_terminate_probability(kg, &state, throughput*throughput_normalize);
+               float probability = path_state_terminate_probability(kg, &state, throughput*num_samples_adjust);
                float terminate = path_rng(kg, rng, sample, rng_offset + PRNG_TERMINATE);
 
                if(terminate >= probability) {
@@ -564,6 +586,24 @@ __device void kernel_path_indirect(KernelGlobals *kg, RNG *rng, int sample, Ray
 
                throughput /= probability;
 
+#ifdef __SUBSURFACE__
+               /* bssrdf scatter to a different location on the same object, replacing
+                * the closures with a diffuse BSDF */
+               if(sd.flag & SD_BSSRDF) {
+                       float bssrdf_probability;
+                       ShaderClosure *sc = subsurface_scatter_pick_closure(kg, &sd, &bssrdf_probability);
+
+                       /* modify throughput for picking bssrdf or bsdf */
+                       throughput *= bssrdf_probability;
+
+                       /* do bssrdf scatter step if we picked a bssrdf closure */
+                       if(sc) {
+                               uint lcg_state = lcg_init(rbsdf);
+                               subsurface_scatter_step(kg, &sd, state.flag, sc, &lcg_state);
+                       }
+               }
+#endif
+
 #ifdef __AO__
                /* ambient occlusion */
                if(kernel_data.integrator.use_ambient_occlusion || (sd.flag & SD_AO)) {
@@ -676,6 +716,193 @@ __device void kernel_path_indirect(KernelGlobals *kg, RNG *rng, int sample, Ray
        }
 }
 
+__device_noinline void kernel_path_non_progressive_lighting(KernelGlobals *kg, RNG *rng, int sample,
+       ShaderData *sd, float3 throughput, float num_samples_adjust,
+       float min_ray_pdf, float ray_pdf, PathState state,
+       int rng_offset, PathRadiance *L, __global float *buffer)
+{
+#ifdef __AO__
+       /* ambient occlusion */
+       if(kernel_data.integrator.use_ambient_occlusion || (sd->flag & SD_AO)) {
+               int num_samples = ceil(kernel_data.integrator.ao_samples*num_samples_adjust);
+               float num_samples_inv = num_samples_adjust/num_samples;
+               float ao_factor = kernel_data.background.ao_factor;
+               float3 ao_N;
+               float3 ao_bsdf = shader_bsdf_ao(kg, sd, ao_factor, &ao_N);
+
+               for(int j = 0; j < num_samples; j++) {
+                       /* todo: solve correlation */
+                       float bsdf_u = path_rng(kg, rng, sample*num_samples + j, rng_offset + PRNG_BSDF_U);
+                       float bsdf_v = path_rng(kg, rng, sample*num_samples + j, rng_offset + PRNG_BSDF_V);
+
+                       float3 ao_D;
+                       float ao_pdf;
+
+                       sample_cos_hemisphere(ao_N, bsdf_u, bsdf_v, &ao_D, &ao_pdf);
+
+                       if(dot(sd->Ng, ao_D) > 0.0f && ao_pdf != 0.0f) {
+                               Ray light_ray;
+                               float3 ao_shadow;
+
+                               light_ray.P = ray_offset(sd->P, sd->Ng);
+                               light_ray.D = ao_D;
+                               light_ray.t = kernel_data.background.ao_distance;
+#ifdef __OBJECT_MOTION__
+                               light_ray.time = sd->time;
+#endif
+
+                               if(!shadow_blocked(kg, &state, &light_ray, &ao_shadow))
+                                       path_radiance_accum_ao(L, throughput*num_samples_inv, ao_bsdf, ao_shadow, state.bounce);
+                       }
+               }
+       }
+#endif
+
+
+#ifdef __EMISSION__
+       /* sample illumination from lights to find path contribution */
+       if(sd->flag & SD_BSDF_HAS_EVAL) {
+               Ray light_ray;
+               BsdfEval L_light;
+               bool is_lamp;
+
+#ifdef __OBJECT_MOTION__
+               light_ray.time = sd->time;
+#endif
+
+               /* lamp sampling */
+               for(int i = 0; i < kernel_data.integrator.num_all_lights; i++) {
+                       int num_samples = ceil(num_samples_adjust*light_select_num_samples(kg, i));
+                       float num_samples_inv = num_samples_adjust/(num_samples*kernel_data.integrator.num_all_lights);
+
+                       if(kernel_data.integrator.pdf_triangles != 0.0f)
+                               num_samples_inv *= 0.5f;
+
+                       for(int j = 0; j < num_samples; j++) {
+                               float light_u = path_rng(kg, rng, sample*num_samples + j, rng_offset + PRNG_LIGHT_U);
+                               float light_v = path_rng(kg, rng, sample*num_samples + j, rng_offset + PRNG_LIGHT_V);
+
+                               if(direct_emission(kg, sd, i, 0.0f, 0.0f, light_u, light_v, &light_ray, &L_light, &is_lamp)) {
+                                       /* trace shadow ray */
+                                       float3 shadow;
+
+                                       if(!shadow_blocked(kg, &state, &light_ray, &shadow)) {
+                                               /* accumulate */
+                                               path_radiance_accum_light(L, throughput*num_samples_inv, &L_light, shadow, num_samples_inv, state.bounce, is_lamp);
+                                       }
+                               }
+                       }
+               }
+
+               /* mesh light sampling */
+               if(kernel_data.integrator.pdf_triangles != 0.0f) {
+                       int num_samples = ceil(num_samples_adjust*kernel_data.integrator.mesh_light_samples);
+                       float num_samples_inv = num_samples_adjust/num_samples;
+
+                       if(kernel_data.integrator.num_all_lights)
+                               num_samples_inv *= 0.5f;
+
+                       for(int j = 0; j < num_samples; j++) {
+                               float light_t = path_rng(kg, rng, sample*num_samples + j, rng_offset + PRNG_LIGHT);
+                               float light_u = path_rng(kg, rng, sample*num_samples + j, rng_offset + PRNG_LIGHT_U);
+                               float light_v = path_rng(kg, rng, sample*num_samples + j, rng_offset + PRNG_LIGHT_V);
+
+                               /* only sample triangle lights */
+                               if(kernel_data.integrator.num_all_lights)
+                                       light_t = 0.5f*light_t;
+
+                               if(direct_emission(kg, sd, -1, light_t, 0.0f, light_u, light_v, &light_ray, &L_light, &is_lamp)) {
+                                       /* trace shadow ray */
+                                       float3 shadow;
+
+                                       if(!shadow_blocked(kg, &state, &light_ray, &shadow)) {
+                                               /* accumulate */
+                                               path_radiance_accum_light(L, throughput*num_samples_inv, &L_light, shadow, num_samples_inv, state.bounce, is_lamp);
+                                       }
+                               }
+                       }
+               }
+       }
+#endif
+
+       for(int i = 0; i< sd->num_closure; i++) {
+               const ShaderClosure *sc = &sd->closure[i];
+
+               if(!CLOSURE_IS_BSDF(sc->type))
+                       continue;
+               /* transparency is not handled here, but in outer loop */
+               if(sc->type == CLOSURE_BSDF_TRANSPARENT_ID)
+                       continue;
+
+               int num_samples;
+
+               if(CLOSURE_IS_BSDF_DIFFUSE(sc->type))
+                       num_samples = kernel_data.integrator.diffuse_samples;
+               else if(CLOSURE_IS_BSDF_GLOSSY(sc->type))
+                       num_samples = kernel_data.integrator.glossy_samples;
+               else
+                       num_samples = kernel_data.integrator.transmission_samples;
+
+               num_samples = ceil(num_samples_adjust*num_samples);
+
+               float num_samples_inv = num_samples_adjust/num_samples;
+
+               for(int j = 0; j < num_samples; j++) {
+                       /* sample BSDF */
+                       float bsdf_pdf;
+                       BsdfEval bsdf_eval;
+                       float3 bsdf_omega_in;
+                       differential3 bsdf_domega_in;
+                       float bsdf_u = path_rng(kg, rng, sample*num_samples + j, rng_offset + PRNG_BSDF_U);
+                       float bsdf_v = path_rng(kg, rng, sample*num_samples + j, rng_offset + PRNG_BSDF_V);
+                       int label;
+
+                       label = shader_bsdf_sample_closure(kg, sd, sc, bsdf_u, bsdf_v, &bsdf_eval,
+                               &bsdf_omega_in, &bsdf_domega_in, &bsdf_pdf);
+
+                       if(bsdf_pdf == 0.0f || bsdf_eval_is_zero(&bsdf_eval))
+                               continue;
+
+                       /* modify throughput */
+                       float3 tp = throughput;
+                       path_radiance_bsdf_bounce(L, &tp, &bsdf_eval, bsdf_pdf, state.bounce, label);
+
+                       /* set labels */
+                       float min_ray_pdf = FLT_MAX;
+
+                       if(!(label & LABEL_TRANSPARENT))
+                               min_ray_pdf = fminf(bsdf_pdf, min_ray_pdf);
+
+                       /* modify path state */
+                       PathState ps = state;
+                       path_state_next(kg, &ps, label);
+
+                       /* setup ray */
+                       Ray bsdf_ray;
+
+                       bsdf_ray.P = ray_offset(sd->P, (label & LABEL_TRANSMIT)? -sd->Ng: sd->Ng);
+                       bsdf_ray.D = bsdf_omega_in;
+                       bsdf_ray.t = FLT_MAX;
+#ifdef __RAY_DIFFERENTIALS__
+                       bsdf_ray.dP = sd->dP;
+                       bsdf_ray.dD = bsdf_domega_in;
+#endif
+#ifdef __OBJECT_MOTION__
+                       bsdf_ray.time = sd->time;
+#endif
+
+                       kernel_path_indirect(kg, rng, sample*num_samples + j, bsdf_ray, buffer,
+                               tp*num_samples_inv, num_samples,
+                               min_ray_pdf, bsdf_pdf, ps, rng_offset+PRNG_BOUNCE_NUM, L);
+
+                       /* for render passes, sum and reset indirect light pass variables
+                        * for the next samples */
+                       path_radiance_sum_indirect(L);
+                       path_radiance_reset_indirect(L);
+               }
+       }
+}
+
 __device float4 kernel_path_non_progressive(KernelGlobals *kg, RNG *rng, int sample, Ray ray, __global float *buffer)
 {
        /* initialize */
@@ -771,183 +998,38 @@ __device float4 kernel_path_non_progressive(KernelGlobals *kg, RNG *rng, int sam
                        throughput /= probability;
                }
 
-#ifdef __AO__
-               /* ambient occlusion */
-               if(kernel_data.integrator.use_ambient_occlusion || (sd.flag & SD_AO)) {
-                       int num_samples = kernel_data.integrator.ao_samples;
-                       float num_samples_inv = 1.0f/num_samples;
-                       float ao_factor = kernel_data.background.ao_factor;
-                       float3 ao_N;
-                       float3 ao_bsdf = shader_bsdf_ao(kg, &sd, ao_factor, &ao_N);
-
-                       for(int j = 0; j < num_samples; j++) {
-                               /* todo: solve correlation */
-                               float bsdf_u = path_rng(kg, rng, sample*num_samples + j, rng_offset + PRNG_BSDF_U);
-                               float bsdf_v = path_rng(kg, rng, sample*num_samples + j, rng_offset + PRNG_BSDF_V);
-
-                               float3 ao_D;
-                               float ao_pdf;
-
-                               sample_cos_hemisphere(ao_N, bsdf_u, bsdf_v, &ao_D, &ao_pdf);
-
-                               if(dot(sd.Ng, ao_D) > 0.0f && ao_pdf != 0.0f) {
-                                       Ray light_ray;
-                                       float3 ao_shadow;
+#ifdef __SUBSURFACE__
+               /* bssrdf scatter to a different location on the same object */
+               if(sd.flag & SD_BSSRDF) {
+                       for(int i = 0; i< sd.num_closure; i++) {
+                               ShaderClosure *sc = &sd.closure[i];
 
-                                       light_ray.P = ray_offset(sd.P, sd.Ng);
-                                       light_ray.D = ao_D;
-                                       light_ray.t = kernel_data.background.ao_distance;
-#ifdef __OBJECT_MOTION__
-                                       light_ray.time = sd.time;
-#endif
-
-                                       if(!shadow_blocked(kg, &state, &light_ray, &ao_shadow))
-                                               path_radiance_accum_ao(&L, throughput*num_samples_inv, ao_bsdf, ao_shadow, state.bounce);
-                               }
-                       }
-               }
-#endif
-
-#ifdef __EMISSION__
-               /* sample illumination from lights to find path contribution */
-               if(sd.flag & SD_BSDF_HAS_EVAL) {
-                       Ray light_ray;
-                       BsdfEval L_light;
-                       bool is_lamp;
-
-#ifdef __OBJECT_MOTION__
-                       light_ray.time = sd.time;
-#endif
-
-                       /* lamp sampling */
-                       for(int i = 0; i < kernel_data.integrator.num_all_lights; i++) {
-                               int num_samples = light_select_num_samples(kg, i);
-                               float num_samples_inv = 1.0f/(num_samples*kernel_data.integrator.num_all_lights);
-
-                               if(kernel_data.integrator.pdf_triangles != 0.0f)
-                                       num_samples_inv *= 0.5f;
-
-                               for(int j = 0; j < num_samples; j++) {
-                                       float light_u = path_rng(kg, rng, sample*num_samples + j, rng_offset + PRNG_LIGHT_U);
-                                       float light_v = path_rng(kg, rng, sample*num_samples + j, rng_offset + PRNG_LIGHT_V);
-
-                                       if(direct_emission(kg, &sd, i, 0.0f, 0.0f, light_u, light_v, &light_ray, &L_light, &is_lamp)) {
-                                               /* trace shadow ray */
-                                               float3 shadow;
-
-                                               if(!shadow_blocked(kg, &state, &light_ray, &shadow)) {
-                                                       /* accumulate */
-                                                       path_radiance_accum_light(&L, throughput*num_samples_inv, &L_light, shadow, num_samples_inv, state.bounce, is_lamp);
-                                               }
-                                       }
-                               }
-                       }
+                               if(!CLOSURE_IS_BSSRDF(sc->type))
+                                       continue;
 
-                       /* mesh light sampling */
-                       if(kernel_data.integrator.pdf_triangles != 0.0f) {
-                               int num_samples = kernel_data.integrator.mesh_light_samples;
+                               /* set up random number generator */
+                               uint lcg_state = lcg_init(rbsdf);
+                               int num_samples = kernel_data.integrator.subsurface_samples;
                                float num_samples_inv = 1.0f/num_samples;
 
-                               if(kernel_data.integrator.num_all_lights)
-                                       num_samples_inv *= 0.5f;
-
+                               /* do subsurface scatter step with copy of shader data, this will
+                                * replace the BSSRDF with a diffuse BSDF closure */
                                for(int j = 0; j < num_samples; j++) {
-                                       float light_t = path_rng(kg, rng, sample*num_samples + j, rng_offset + PRNG_LIGHT);
-                                       float light_u = path_rng(kg, rng, sample*num_samples + j, rng_offset + PRNG_LIGHT_U);
-                                       float light_v = path_rng(kg, rng, sample*num_samples + j, rng_offset + PRNG_LIGHT_V);
-
-                                       /* only sample triangle lights */
-                                       if(kernel_data.integrator.num_all_lights)
-                                               light_t = 0.5f*light_t;
-
-                                       if(direct_emission(kg, &sd, -1, light_t, 0.0f, light_u, light_v, &light_ray, &L_light, &is_lamp)) {
-                                               /* trace shadow ray */
-                                               float3 shadow;
-
-                                               if(!shadow_blocked(kg, &state, &light_ray, &shadow)) {
-                                                       /* accumulate */
-                                                       path_radiance_accum_light(&L, throughput*num_samples_inv, &L_light, shadow, num_samples_inv, state.bounce, is_lamp);
-                                               }
-                                       }
+                                       ShaderData bssrdf_sd = sd;
+                                       subsurface_scatter_step(kg, &bssrdf_sd, state.flag, sc, &lcg_state);
+
+                                       /* compute lighting with the BSDF closure */
+                                       kernel_path_non_progressive_lighting(kg, rng, sample*num_samples + j,
+                                               &bssrdf_sd, throughput, num_samples_inv,
+                                               ray_pdf, ray_pdf, state, rng_offset, &L, buffer);
                                }
                        }
                }
 #endif
 
-               for(int i = 0; i< sd.num_closure; i++) {
-                       const ShaderClosure *sc = &sd.closure[i];
-
-                       if(!CLOSURE_IS_BSDF(sc->type))
-                               continue;
-                       /* transparency is not handled here, but in outer loop */
-                       if(sc->type == CLOSURE_BSDF_TRANSPARENT_ID)
-                               continue;
-
-                       int num_samples;
-
-                       if(CLOSURE_IS_BSDF_DIFFUSE(sc->type))
-                               num_samples = kernel_data.integrator.diffuse_samples;
-                       else if(CLOSURE_IS_BSDF_GLOSSY(sc->type))
-                               num_samples = kernel_data.integrator.glossy_samples;
-                       else
-                               num_samples = kernel_data.integrator.transmission_samples;
-
-                       float num_samples_inv = 1.0f/num_samples;
-
-                       for(int j = 0; j < num_samples; j++) {
-                               /* sample BSDF */
-                               float bsdf_pdf;
-                               BsdfEval bsdf_eval;
-                               float3 bsdf_omega_in;
-                               differential3 bsdf_domega_in;
-                               float bsdf_u = path_rng(kg, rng, sample*num_samples + j, rng_offset + PRNG_BSDF_U);
-                               float bsdf_v = path_rng(kg, rng, sample*num_samples + j, rng_offset + PRNG_BSDF_V);
-                               int label;
-
-                               label = shader_bsdf_sample_closure(kg, &sd, sc, bsdf_u, bsdf_v, &bsdf_eval,
-                                       &bsdf_omega_in, &bsdf_domega_in, &bsdf_pdf);
-
-                               if(bsdf_pdf == 0.0f || bsdf_eval_is_zero(&bsdf_eval))
-                                       continue;
-
-                               /* modify throughput */
-                               float3 tp = throughput;
-                               path_radiance_bsdf_bounce(&L, &tp, &bsdf_eval, bsdf_pdf, state.bounce, label);
-
-                               /* set labels */
-                               float min_ray_pdf = FLT_MAX;
-
-                               if(!(label & LABEL_TRANSPARENT))
-                                       min_ray_pdf = fminf(bsdf_pdf, min_ray_pdf);
-
-                               /* modify path state */
-                               PathState ps = state;
-                               path_state_next(kg, &ps, label);
-
-                               /* setup ray */
-                               Ray bsdf_ray;
-
-                               bsdf_ray.P = ray_offset(sd.P, (label & LABEL_TRANSMIT)? -sd.Ng: sd.Ng);
-                               bsdf_ray.D = bsdf_omega_in;
-                               bsdf_ray.t = FLT_MAX;
-#ifdef __RAY_DIFFERENTIALS__
-                               bsdf_ray.dP = sd.dP;
-                               bsdf_ray.dD = bsdf_domega_in;
-#endif
-#ifdef __OBJECT_MOTION__
-                               bsdf_ray.time = sd.time;
-#endif
-
-                               kernel_path_indirect(kg, rng, sample*num_samples + j, bsdf_ray, buffer,
-                                       tp*num_samples_inv, num_samples,
-                                       min_ray_pdf, bsdf_pdf, ps, rng_offset+PRNG_BOUNCE_NUM, &L);
-
-                               /* for render passes, sum and reset indirect light pass variables
-                                * for the next samples */
-                               path_radiance_sum_indirect(&L);
-                               path_radiance_reset_indirect(&L);
-                       }
-               }
+               /* lighting */
+               kernel_path_non_progressive_lighting(kg, rng, sample, &sd, throughput,
+                       1.0f, ray_pdf, ray_pdf, state, rng_offset, &L, buffer);
 
                /* continue in case of transparency */
                throughput *= shader_bsdf_transparency(kg, &sd);
index 9083b7cbfd7e97814b391d029c2366687983ab5f..e2eb8d5db834de9cd6ce2dc1fca775b1f93538a2 100644 (file)
@@ -200,5 +200,19 @@ __device void path_rng_end(KernelGlobals *kg, __global uint *rng_state, RNG rng)
 
 #endif
 
+__device float lcg_step(uint *rng)
+{
+       /* implicit mod 2^32 */
+       *rng = (1103515245*(*rng) + 12345);
+       return (float)*rng * (1.0f/(float)0xFFFFFFFF);
+}
+
+__device uint lcg_init(float seed)
+{
+       uint rng = __float_as_int(seed);
+       lcg_step(&rng);
+       return rng;
+}
+
 CCL_NAMESPACE_END
 
index df86b352697334e741035fc4882c346ac9f3c899..572255f938d6c09f028e4f8e0af77e54795155bf 100644 (file)
@@ -158,6 +158,103 @@ __device_noinline void shader_setup_from_ray(KernelGlobals *kg, ShaderData *sd,
 #endif
 }
 
+/* ShaderData setup from BSSRDF scatter */
+
+#ifdef __SUBSURFACE__
+__device_inline void shader_setup_from_subsurface(KernelGlobals *kg, ShaderData *sd,
+       const Intersection *isect, const Ray *ray)
+{
+       bool backfacing = sd->flag & SD_BACKFACING;
+
+       /* object, matrices, time, ray_length stay the same */
+       sd->flag = kernel_tex_fetch(__object_flag, sd->object);
+       sd->prim = kernel_tex_fetch(__prim_index, isect->prim);
+
+#ifdef __HAIR__
+       if(kernel_tex_fetch(__prim_segment, isect->prim) != ~0) {
+               /* Strand Shader setting*/
+               float4 curvedata = kernel_tex_fetch(__curves, sd->prim);
+
+               sd->shader = __float_as_int(curvedata.z);
+               sd->segment = isect->segment;
+
+               float tcorr = isect->t;
+               if(kernel_data.curve_kernel_data.curveflags & CURVE_KN_POSTINTERSECTCORRECTION)
+                       tcorr = (isect->u < 0)? tcorr + sqrtf(isect->v) : tcorr - sqrtf(isect->v);
+
+               sd->P = bvh_curve_refine(kg, sd, isect, ray, tcorr);
+       }
+       else {
+#endif
+               /* fetch triangle data */
+               float4 Ns = kernel_tex_fetch(__tri_normal, sd->prim);
+               float3 Ng = make_float3(Ns.x, Ns.y, Ns.z);
+               sd->shader = __float_as_int(Ns.w);
+
+#ifdef __HAIR__
+               sd->segment = ~0;
+#endif
+
+#ifdef __UV__
+               sd->u = isect->u;
+               sd->v = isect->v;
+#endif
+
+               /* vectors */
+               sd->P = bvh_triangle_refine(kg, sd, isect, ray);
+               sd->Ng = Ng;
+               sd->N = Ng;
+               
+               /* smooth normal */
+               if(sd->shader & SHADER_SMOOTH_NORMAL)
+                       sd->N = triangle_smooth_normal(kg, sd->prim, sd->u, sd->v);
+
+#ifdef __DPDU__
+               /* dPdu/dPdv */
+               triangle_dPdudv(kg, &sd->dPdu, &sd->dPdv, sd->prim);
+#endif
+
+#ifdef __HAIR__
+       }
+#endif
+
+       sd->flag |= kernel_tex_fetch(__shader_flag, (sd->shader & SHADER_MASK)*2);
+
+#ifdef __INSTANCING__
+       if(isect->object != ~0) {
+               /* instance transform */
+               object_normal_transform(kg, sd, &sd->N);
+               object_normal_transform(kg, sd, &sd->Ng);
+#ifdef __DPDU__
+               object_dir_transform(kg, sd, &sd->dPdu);
+               object_dir_transform(kg, sd, &sd->dPdv);
+#endif
+       }
+#endif
+
+       /* backfacing test */
+       if(backfacing) {
+               sd->flag |= SD_BACKFACING;
+               sd->Ng = -sd->Ng;
+               sd->N = -sd->N;
+#ifdef __DPDU__
+               sd->dPdu = -sd->dPdu;
+               sd->dPdv = -sd->dPdv;
+#endif
+       }
+
+       /* should not get used in principle as the shading will only use a diffuse
+        * BSDF, but the shader might still access it */
+       sd->I = sd->N;
+
+#ifdef __RAY_DIFFERENTIALS__
+       /* differentials */
+       differential_dudv(&sd->du, &sd->dv, sd->dPdu, sd->dPdv, sd->dP, sd->Ng);
+       /* don't modify dP and dI */
+#endif
+}
+#endif
+
 /* ShaderData setup from position sampled on mesh */
 
 __device_noinline void shader_setup_from_sample(KernelGlobals *kg, ShaderData *sd,
@@ -283,11 +380,9 @@ __device_noinline void shader_setup_from_sample(KernelGlobals *kg, ShaderData *s
 
 /* ShaderData setup for displacement */
 
-__device_noinline void shader_setup_from_displace(KernelGlobals *kg, ShaderData *sd,
+__device void shader_setup_from_displace(KernelGlobals *kg, ShaderData *sd,
        int object, int prim, float u, float v)
 {
-       /* Note: no OSLShader::init call here, this is done in shader_setup_from_sample! */
-
        float3 P, Ng, I = make_float3(0.0f, 0.0f, 0.0f);
        int shader;
 
@@ -418,7 +513,7 @@ __device int shader_bsdf_sample(KernelGlobals *kg, const ShaderData *sd,
                        const ShaderClosure *sc = &sd->closure[sampled];
                        
                        if(CLOSURE_IS_BSDF(sc->type)) {
-                               sum += sd->closure[sampled].sample_weight;
+                               sum += sc->sample_weight;
 
                                if(r <= sum)
                                        break;
diff --git a/intern/cycles/kernel/kernel_subsurface.h b/intern/cycles/kernel/kernel_subsurface.h
new file mode 100644 (file)
index 0000000..7dd5c2d
--- /dev/null
@@ -0,0 +1,224 @@
+/*
+ * Copyright 2013, Blender Foundation.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+
+CCL_NAMESPACE_BEGIN
+
+#define BSSRDF_MULTI_EVAL
+#define BSSRDF_SKIP_NO_HIT
+
+__device float bssrdf_sample_distance(KernelGlobals *kg, float radius, float refl, float u)
+{
+       int table_offset = kernel_data.bssrdf.table_offset;
+       float r = lookup_table_read_2D(kg, u, refl, table_offset, BSSRDF_RADIUS_TABLE_SIZE, BSSRDF_REFL_TABLE_SIZE);
+
+       return r*radius;
+}
+
+#ifdef BSSRDF_MULTI_EVAL
+__device float bssrdf_pdf(KernelGlobals *kg, float radius, float refl, float r)
+{
+       if(r >= radius)
+               return 0.0f;
+
+       /* todo: when we use the real BSSRDF this will need to be divided by the maximum
+        * radius instead of the average radius */
+       float t = r/radius;
+
+       int table_offset = kernel_data.bssrdf.table_offset + BSSRDF_PDF_TABLE_OFFSET;
+       float pdf = lookup_table_read_2D(kg, t, refl, table_offset, BSSRDF_RADIUS_TABLE_SIZE, BSSRDF_REFL_TABLE_SIZE);
+
+       pdf /= radius;
+
+       return pdf;
+}
+#endif
+
+__device ShaderClosure *subsurface_scatter_pick_closure(KernelGlobals *kg, ShaderData *sd, float *probability)
+{
+       /* sum sample weights of bssrdf and bsdf */
+       float bsdf_sum = 0.0f;
+       float bssrdf_sum = 0.0f;
+
+       for(int i = 0; i < sd->num_closure; i++) {
+               ShaderClosure *sc = &sd->closure[i];
+               
+               if(CLOSURE_IS_BSDF(sc->type))
+                       bsdf_sum += sc->sample_weight;
+               else if(CLOSURE_IS_BSSRDF(sc->type))
+                       bssrdf_sum += sc->sample_weight;
+       }
+
+       /* pick a random bsdf or bssrdf */
+       float r = sd->randb_closure*(bsdf_sum + bssrdf_sum);
+       float sum = 0.0f;
+
+       int sampled;
+
+       for(sampled = 0; sampled < sd->num_closure; sampled++) {
+               ShaderClosure *sc = &sd->closure[sampled];
+               
+               if(CLOSURE_IS_BSDF(sc->type) || CLOSURE_IS_BSSRDF(sc->type)) {
+                       sum += sc->sample_weight;
+
+                       if(r <= sum) {
+                               /* if we picked a bssrdf, return the closure. also return probability
+                                * to adjust throughput depending if we picked a bsdf or bssrdf */
+                               if(CLOSURE_IS_BSSRDF(sc->type)) {
+                                       sd->randb_closure = 0.0f; /* not needed anymore */
+#ifdef BSSRDF_MULTI_EVAL
+                                       *probability = (bssrdf_sum > 0.0f)? (bsdf_sum + bssrdf_sum)/bssrdf_sum: 1.0f;
+#else
+                                       *probability = (bssrdf_sum > 0.0f)? (bsdf_sum + bssrdf_sum)/sc->sample_weight: 1.0f;
+#endif
+                                       return sc;
+                               }
+                               else {
+                                       /* reuse randb for picking a bsdf */
+                                       sd->randb_closure = (sd->randb_closure - sum - sc->sample_weight)/sc->sample_weight;
+                                       *probability = (bsdf_sum > 0.0f)? (bsdf_sum + bssrdf_sum)/bsdf_sum: 1.0f;
+                                       return NULL;
+                               }
+                       }
+               }
+       }
+
+       *probability = 1.0f;
+       return NULL;
+}
+
+#ifdef BSSRDF_MULTI_EVAL
+__device float3 subsurface_scatter_multi_eval(KernelGlobals *kg, ShaderData *sd, bool hit, float refl, float *r, int num_r)
+{
+       /* compute pdf */
+       float3 eval_sum = make_float3(0.0f, 0.0f, 0.0f);
+       float pdf_sum = 0.0f;
+       float sample_weight_sum = 0.0f;
+
+       for(int i = 0; i < sd->num_closure; i++) {
+               ShaderClosure *sc = &sd->closure[i];
+               
+               if(CLOSURE_IS_BSSRDF(sc->type)) {
+                       float pdf = 1.0f;
+                       for(int i = 0; i < num_r; i++)
+                               pdf *= bssrdf_pdf(kg, sc->data0, refl, r[i]);
+                       //float pdf = bssrdf_pdf(kg, sc->data0, refl, r[num_r-1]);
+
+                       eval_sum += sc->weight*pdf;
+                       pdf_sum += sc->sample_weight*pdf;
+
+                       sample_weight_sum += sc->sample_weight;
+               }
+       }
+
+       float inv_pdf_sum = (pdf_sum > 0.0f)? sample_weight_sum/pdf_sum: 0.0f;
+       float3 weight = eval_sum * inv_pdf_sum;
+
+       return weight;
+}
+#endif
+
+/* replace closures with a single diffuse bsdf closure after scatter step */
+__device void subsurface_scatter_setup_diffuse_bsdf(ShaderData *sd, float3 weight)
+{
+       ShaderClosure *sc = &sd->closure[0];
+       sd->num_closure = 1;
+
+       sc->weight = weight;
+       sc->sample_weight = 1.0f;
+       sc->data0 = 0.0f;
+       sc->data1 = 0.0f;
+       sc->N = sd->N;
+       sd->flag |= bsdf_diffuse_setup(sc);
+       sd->randb_closure = 0.0f;
+
+       /* todo: evaluate shading to get blurred textures and bump mapping */
+       /* shader_eval_surface(kg, sd, 0.0f, state_flag, SHADER_CONTEXT_SSS); */
+}
+
+/* subsurface scattering step, from a point on the surface to another nearby point on the same object */
+__device void subsurface_scatter_step(KernelGlobals *kg, ShaderData *sd, int state_flag, ShaderClosure *sc, uint *lcg_state)
+{
+       float radius = sc->data0;
+       float refl = max(average(sc->weight)*3.0f, 0.0f);
+       float r = 0.0f;
+       bool hit = false;
+       float3 weight = make_float3(1.0f, 1.0f, 1.0f);
+#ifdef BSSRDF_MULTI_EVAL
+       float r_attempts[BSSRDF_MAX_ATTEMPTS];
+#endif
+       int num_attempts;
+
+       /* attempt to find a hit a given number of times before giving up */
+       for(num_attempts = 0; num_attempts < kernel_data.bssrdf.num_attempts; num_attempts++) {
+               /* random numbers for sampling */
+               float u1 = lcg_step(lcg_state);
+               float u2 = lcg_step(lcg_state);
+               float u3 = lcg_step(lcg_state);
+               float u4 = lcg_step(lcg_state);
+               float u5 = lcg_step(lcg_state);
+               float u6 = lcg_step(lcg_state);
+
+               r = bssrdf_sample_distance(kg, radius, refl, u5);
+#ifdef BSSRDF_MULTI_EVAL
+               r_attempts[num_attempts] = r;
+#endif
+
+               float3 p1 = sd->P + sample_uniform_sphere(u1, u2)*r;
+               float3 p2 = sd->P + sample_uniform_sphere(u3, u4)*r;
+
+               /* create ray */
+               Ray ray;
+               ray.P = p1;
+               ray.D = normalize_len(p2 - p1, &ray.t);
+               ray.dP = sd->dP;
+               ray.dD.dx = make_float3(0.0f, 0.0f, 0.0f);
+               ray.dD.dy = make_float3(0.0f, 0.0f, 0.0f);
+               ray.time = sd->time;
+
+               /* intersect with the same object. if multiple intersections are
+                * found it will randomly pick one of them */
+               Intersection isect;
+               if(scene_intersect_subsurface(kg, &ray, &isect, sd->object, u6) == 0)
+                       continue;
+
+               /* setup new shading point */
+               shader_setup_from_subsurface(kg, sd, &isect, &ray);
+
+               hit = true;
+               num_attempts++;
+               break;
+       }
+
+       /* evaluate subsurface scattering closures */
+#ifdef BSSRDF_MULTI_EVAL
+       weight *= subsurface_scatter_multi_eval(kg, sd, hit, refl, r_attempts, num_attempts);
+#else
+       weight *= sc->weight;
+#endif
+
+#ifdef BSSRDF_SKIP_NO_HIT
+       if(!hit)
+               weight = make_float3(0.0f, 0.0f, 0.0f);
+#endif
+
+       /* replace closures with a single diffuse BSDF */
+       subsurface_scatter_setup_diffuse_bsdf(sd, weight);
+}
+
+CCL_NAMESPACE_END
+
index f6b8a1b8b826775e5817f025dd142c426dea9f40..9c126074e837dac2a559bc7bdf6296a3cd2bb9d4 100644 (file)
@@ -37,6 +37,13 @@ CCL_NAMESPACE_BEGIN
 #define PARTICLE_SIZE          5
 #define TIME_INVALID           FLT_MAX
 
+#define BSSRDF_RADIUS_TABLE_SIZE       1024
+#define BSSRDF_REFL_TABLE_SIZE         256
+#define BSSRDF_PDF_TABLE_OFFSET                (BSSRDF_RADIUS_TABLE_SIZE*BSSRDF_REFL_TABLE_SIZE)
+#define BSSRDF_LOOKUP_TABLE_SIZE       (BSSRDF_RADIUS_TABLE_SIZE*BSSRDF_REFL_TABLE_SIZE*2)
+#define BSSRDF_MIN_RADIUS                      1e-8f
+#define BSSRDF_MAX_ATTEMPTS                    8
+
 #define TEX_NUM_FLOAT_IMAGES   5
 
 /* device capabilities */
@@ -48,6 +55,7 @@ CCL_NAMESPACE_BEGIN
 #ifdef WITH_OSL
 #define __OSL__
 #endif
+#define __SUBSURFACE__
 #endif
 
 #ifdef __KERNEL_CUDA__
@@ -423,7 +431,8 @@ typedef enum ShaderContext {
        SHADER_CONTEXT_INDIRECT = 1,
        SHADER_CONTEXT_EMISSION = 2,
        SHADER_CONTEXT_SHADOW = 3,
-       SHADER_CONTEXT_NUM = 4
+       SHADER_CONTEXT_SSS = 4,
+       SHADER_CONTEXT_NUM = 5
 } ShaderContext;
 
 /* Shader Data
@@ -438,20 +447,21 @@ enum ShaderDataFlag {
        SD_BSDF = 4,                    /* have bsdf closure? */
        SD_BSDF_HAS_EVAL = 8,   /* have non-singular bsdf closure? */
        SD_BSDF_GLOSSY = 16,    /* have glossy bsdf */
-       SD_HOLDOUT = 32,                /* have holdout closure? */
-       SD_VOLUME = 64,                 /* have volume closure? */
-       SD_AO = 128,                    /* have ao closure? */
+       SD_BSSRDF = 32,                 /* have bssrdf */
+       SD_HOLDOUT = 64,                /* have holdout closure? */
+       SD_VOLUME = 128,                /* have volume closure? */
+       SD_AO = 256,                    /* have ao closure? */
 
        /* shader flags */
-       SD_SAMPLE_AS_LIGHT = 256,                       /* direct light sample */
-       SD_HAS_SURFACE_TRANSPARENT = 512,       /* has surface transparency */
-       SD_HAS_VOLUME = 1024,                           /* has volume shader */
-       SD_HOMOGENEOUS_VOLUME = 2048,           /* has homogeneous volume */
+       SD_SAMPLE_AS_LIGHT = 512,                       /* direct light sample */
+       SD_HAS_SURFACE_TRANSPARENT = 1024,      /* has surface transparency */
+       SD_HAS_VOLUME = 2048,                           /* has volume shader */
+       SD_HOMOGENEOUS_VOLUME = 4096,           /* has homogeneous volume */
 
        /* object flags */
-       SD_HOLDOUT_MASK = 4096,                         /* holdout for camera rays */
-       SD_OBJECT_MOTION = 8192,                        /* has object motion blur */
-       SD_TRANSFORM_APPLIED = 16384            /* vertices have transform applied */
+       SD_HOLDOUT_MASK = 8192,                         /* holdout for camera rays */
+       SD_OBJECT_MOTION = 16384,                       /* has object motion blur */
+       SD_TRANSFORM_APPLIED = 32768            /* vertices have transform applied */
 };
 
 typedef struct ShaderData {
@@ -681,6 +691,9 @@ typedef struct KernelIntegrator {
        int ao_samples;
        int mesh_light_samples;
        int use_lamp_mis;
+       int subsurface_samples;
+
+       int pad1, pad2, pad3;
 } KernelIntegrator;
 
 typedef struct KernelBVH {
@@ -712,9 +725,14 @@ typedef struct KernelCurves {
        float encasing_ratio;
        int curveflags;
        int subdivisions;
-
 } KernelCurves;
 
+typedef struct KernelBSSRDF {
+       int table_offset;
+       int num_attempts;
+       int pad1, pad2;
+} KernelBSSRDF;
+
 typedef struct KernelData {
        KernelCamera cam;
        KernelFilm film;
@@ -723,6 +741,7 @@ typedef struct KernelData {
        KernelIntegrator integrator;
        KernelBVH bvh;
        KernelCurves curve_kernel_data;
+       KernelBSSRDF bssrdf;
 } KernelData;
 
 CCL_NAMESPACE_END
index 5a27f7823e45412ae667128badd1c097bcbb7a69..0ce40eda4df143725b84bc39813340431251749f 100644 (file)
@@ -18,12 +18,14 @@ set(SRC
        bsdf_phong_ramp.cpp
        bsdf_toon.cpp
        emissive.cpp
+       osl_bssrdf.cpp
        osl_closures.cpp
        osl_services.cpp
        osl_shader.cpp
 )
 
 set(HEADER_SRC
+       osl_bssrdf.h
        osl_closures.h
        osl_globals.h
        osl_services.h
diff --git a/intern/cycles/kernel/osl/osl_bssrdf.cpp b/intern/cycles/kernel/osl/osl_bssrdf.cpp
new file mode 100644 (file)
index 0000000..ba9b131
--- /dev/null
@@ -0,0 +1,90 @@
+/*
+ * Adapted from Open Shading Language with this license:
+ *
+ * Copyright (c) 2009-2010 Sony Pictures Imageworks Inc., et al.
+ * All Rights Reserved.
+ *
+ * Modifications Copyright 2011, Blender Foundation.
+ * 
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ * * Redistributions of source code must retain the above copyright
+ *   notice, this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above copyright
+ *   notice, this list of conditions and the following disclaimer in the
+ *   documentation and/or other materials provided with the distribution.
+ * * Neither the name of Sony Pictures Imageworks nor the names of its
+ *   contributors may be used to endorse or promote products derived from
+ *   this software without specific prior written permission.
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include <OpenImageIO/fmath.h>
+
+#include <OSL/genclosure.h>
+
+#include "osl_bssrdf.h"
+#include "osl_closures.h"
+
+#include "kernel_types.h"
+#include "kernel_montecarlo.h"
+
+#include "closure/bsdf_diffuse.h"
+#include "closure/bssrdf.h"
+
+CCL_NAMESPACE_BEGIN
+
+using namespace OSL;
+
+class BSSRDFClosure : public CBSSRDFClosure {
+public:
+       size_t memsize() const { return sizeof(*this); }
+       const char *name() const { return "bssrdf_cubic"; }
+
+       void setup()
+       {
+               sc.prim = NULL;
+               sc.data0 = fabsf(average(radius));
+               sc.data1 = 1.3f;
+
+               m_shaderdata_flag = bssrdf_setup(&sc);
+       }
+
+       bool mergeable(const ClosurePrimitive *other) const
+       {
+               return false;
+       }
+
+       void print_on(std::ostream &out) const
+       {
+               out << name() << " ((" << sc.N[0] << ", " << sc.N[1] << ", " << sc.N[2] << "))";
+       }
+};
+
+ClosureParam *closure_bssrdf_params()
+{
+       static ClosureParam params[] = {
+               CLOSURE_FLOAT3_PARAM(BSSRDFClosure, sc.N),
+               CLOSURE_FLOAT3_PARAM(BSSRDFClosure, radius),
+               //CLOSURE_FLOAT_PARAM(BSSRDFClosure, sc.data1),
+           CLOSURE_STRING_KEYPARAM("label"),
+           CLOSURE_FINISH_PARAM(BSSRDFClosure)
+       };
+       return params;
+}
+
+CLOSURE_PREPARE(closure_bssrdf_prepare, BSSRDFClosure)
+
+CCL_NAMESPACE_END
+
diff --git a/intern/cycles/kernel/osl/osl_bssrdf.h b/intern/cycles/kernel/osl/osl_bssrdf.h
new file mode 100644 (file)
index 0000000..54df055
--- /dev/null
@@ -0,0 +1,65 @@
+/*
+ * Adapted from Open Shading Language with this license:
+ *
+ * Copyright (c) 2009-2010 Sony Pictures Imageworks Inc., et al.
+ * All Rights Reserved.
+ *
+ * Modifications Copyright 2011, Blender Foundation.
+ * 
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ * * Redistributions of source code must retain the above copyright
+ *   notice, this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above copyright
+ *   notice, this list of conditions and the following disclaimer in the
+ *   documentation and/or other materials provided with the distribution.
+ * * Neither the name of Sony Pictures Imageworks nor the names of its
+ *   contributors may be used to endorse or promote products derived from
+ *   this software without specific prior written permission.
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef __OSL_BSSRDF_H__
+#define __OSL_BSSRDF_H__
+
+#include <OSL/oslclosure.h>
+#include <OSL/oslexec.h>
+#include <OSL/genclosure.h>
+
+#include "kernel_types.h"
+
+#include "util_types.h"
+
+CCL_NAMESPACE_BEGIN
+
+class CBSSRDFClosure : public OSL::ClosurePrimitive {
+public:
+       ShaderClosure sc;
+       float3 radius;
+
+       CBSSRDFClosure() : OSL::ClosurePrimitive(BSSRDF),
+         m_shaderdata_flag(0) { }
+       ~CBSSRDFClosure() { }
+
+       int scattering() const { return LABEL_DIFFUSE; }
+       int shaderdata_flag() const { return m_shaderdata_flag; }
+
+protected:
+       int m_shaderdata_flag;
+};
+
+CCL_NAMESPACE_END
+
+#endif /* __OSL_BSSRDF_H__ */
+
index 9e65cda1e8fcbab394b7aa90985622a0774b1f4f..9ce11ca120713999e8d496f88d3931fb055ea3e6 100644 (file)
@@ -201,6 +201,8 @@ void OSLShader::register_closures(OSLShadingSystem *ss_)
                closure_bsdf_diffuse_toon_params(), closure_bsdf_diffuse_toon_prepare);
        register_closure(ss, "specular_toon", id++,
                closure_bsdf_specular_toon_params(), closure_bsdf_specular_toon_prepare);
+       register_closure(ss, "bssrdf_cubic", id++,
+               closure_bssrdf_params(), closure_bssrdf_prepare);
 }
 
 CCL_NAMESPACE_END
index daccc03ede2e61ea0db0e06cae5ac39a712a2f17..d0e25bb2b0c45659a4704580166b12498d99cda7 100644 (file)
@@ -51,6 +51,7 @@ OSL::ClosureParam *closure_bsdf_diffuse_ramp_params();
 OSL::ClosureParam *closure_bsdf_phong_ramp_params();
 OSL::ClosureParam *closure_bsdf_diffuse_toon_params();
 OSL::ClosureParam *closure_bsdf_specular_toon_params();
+OSL::ClosureParam *closure_bssrdf_params();
 
 void closure_emission_prepare(OSL::RendererServices *, int id, void *data);
 void closure_background_prepare(OSL::RendererServices *, int id, void *data);
@@ -60,6 +61,7 @@ void closure_bsdf_diffuse_ramp_prepare(OSL::RendererServices *, int id, void *da
 void closure_bsdf_phong_ramp_prepare(OSL::RendererServices *, int id, void *data);
 void closure_bsdf_diffuse_toon_prepare(OSL::RendererServices *, int id, void *data);
 void closure_bsdf_specular_toon_prepare(OSL::RendererServices *, int id, void *data);
+void closure_bssrdf_prepare(OSL::RendererServices *, int id, void *data);
 
 enum {
        AmbientOcclusion = 100
index a32c526a2bee03e3dd47c8d6b017abbe1ad1daa0..555edf598f1988f49af1fa46f2138a88ab8eeb63 100644 (file)
@@ -21,6 +21,7 @@
 #include "kernel_globals.h"
 #include "kernel_object.h"
 
+#include "osl_bssrdf.h"
 #include "osl_closures.h"
 #include "osl_globals.h"
 #include "osl_services.h"
@@ -201,7 +202,7 @@ static void flatten_surface_closure_tree(ShaderData *sd, bool no_glossy,
                                        }
                                        break;
                                }
-                               case OSL::ClosurePrimitive::Holdout:
+                               case OSL::ClosurePrimitive::Holdout: {
                                        sc.sample_weight = 0.0f;
                                        sc.type = CLOSURE_HOLDOUT_ID;
                                        sc.prim = NULL;
@@ -211,7 +212,43 @@ static void flatten_surface_closure_tree(ShaderData *sd, bool no_glossy,
                                                sd->flag |= SD_HOLDOUT;
                                        }
                                        break;
-                               case OSL::ClosurePrimitive::BSSRDF:
+                               }
+                               case OSL::ClosurePrimitive::BSSRDF: {
+                                       CBSSRDFClosure *bssrdf = (CBSSRDFClosure *)prim;
+                                       float sample_weight = fabsf(average(weight));
+
+                                       if(sample_weight > 1e-5f && sd->num_closure+2 < MAX_CLOSURE) {
+                                               sc.sample_weight = sample_weight;
+
+                                               sc.type = bssrdf->sc.type;
+                                               sc.N = bssrdf->sc.N;
+                                               sc.data1 = bssrdf->sc.data1;
+                                               sc.prim = NULL;
+
+                                               /* create one closure for each color channel */
+                                               if(fabsf(weight.x) > 0.0f) {
+                                                       sc.weight = make_float3(weight.x, 0.0f, 0.0f);
+                                                       sc.data0 = bssrdf->radius.x;
+                                                       sd->closure[sd->num_closure++] = sc;
+                                                       sd->flag |= bssrdf->shaderdata_flag();
+                                               }
+
+                                               if(fabsf(weight.y) > 0.0f) {
+                                                       sc.weight = make_float3(0.0f, weight.y, 0.0f);
+                                                       sc.data0 = bssrdf->radius.y;
+                                                       sd->closure[sd->num_closure++] = sc;
+                                                       sd->flag |= bssrdf->shaderdata_flag();
+                                               }
+
+                                               if(fabsf(weight.z) > 0.0f) {
+                                                       sc.weight = make_float3(0.0f, 0.0f, weight.z);
+                                                       sc.data0 = bssrdf->radius.z;
+                                                       sd->closure[sd->num_closure++] = sc;
+                                                       sd->flag |= bssrdf->shaderdata_flag();
+                                               }
+                                       }
+                                       break;
+                               }
                                case OSL::ClosurePrimitive::Debug:
                                        break; /* not implemented */
                                case OSL::ClosurePrimitive::Background:
index acae46f1615ac365d868b39ed94ab9faf3a74075..0cff264d8e19f7e13d095a6ed1de838217ad6202 100644 (file)
@@ -55,6 +55,7 @@ set(SRC_OSL
        node_separate_rgb.osl
        node_set_normal.osl
        node_sky_texture.osl
+       node_subsurface_scattering.osl
        node_tangent.osl
        node_texture_coordinate.osl
        node_translucent_bsdf.osl
diff --git a/intern/cycles/kernel/shaders/node_subsurface_scattering.osl b/intern/cycles/kernel/shaders/node_subsurface_scattering.osl
new file mode 100644 (file)
index 0000000..5c25c44
--- /dev/null
@@ -0,0 +1,33 @@
+/*
+ * Copyright 2011, Blender Foundation.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+
+#include "stdosl.h"
+
+shader node_subsurface_scattering(
+       color Color = 0.8,
+       float Scale = 1.0,
+       vector Radius = vector(0.1, 0.1, 0.1),
+       float IOR = 1.3,
+       normal Normal = N,
+       output closure color BSSRDF = 0)
+{
+       float eta = max(IOR, 1.0 + 1e-5);
+
+       BSSRDF = Color * bssrdf_cubic(N, Scale * Radius);
+}
+
index f340eaff95f10b4876d0ee58428c76d684093075..010d6ddd200e62264d20cdedba48517d89b62ac4 100644 (file)
@@ -461,6 +461,7 @@ closure color emission() BUILTIN;
 closure color background() BUILTIN;
 closure color holdout() BUILTIN;
 closure color ambient_occlusion() BUILTIN;
+closure color bssrdf_cubic(normal N, vector radius) BUILTIN;
 
 // Renderer state
 int raytype (string typename) BUILTIN;
index b5bd2b42cb40209e2d85c3b3ae3004e68fa6816f..a37646beb2e8d7bad8f4308dd00e5016e4f6fc55 100644 (file)
@@ -306,6 +306,59 @@ __device void svm_node_closure_bsdf(KernelGlobals *kg, ShaderData *sd, float *st
                        }
                        break;
                }
+#ifdef __SUBSURFACE__
+               case CLOSURE_BSSRDF_ID: {
+                       ShaderClosure *sc = &sd->closure[sd->num_closure];
+                       float3 weight = sc->weight * mix_weight;
+                       float sample_weight = fabsf(average(weight));
+
+                       if(sample_weight > 1e-5f && sd->num_closure+2 < MAX_CLOSURE) {
+                               /* radius * scale */
+                               float3 radius = stack_load_float3(stack, data_node.w)*param1;
+                               /* index of refraction */
+                               float eta = fmaxf(param2, 1.0f + 1e-5f);
+
+                               /* create one closure per color channel */
+                               if(fabsf(weight.x) > 0.0f) {
+                                       sc->weight = make_float3(weight.x, 0.0f, 0.0f);
+                                       sc->sample_weight = sample_weight;
+                                       sc->data0 = radius.x;
+                                       sc->data1 = eta;
+                                       sc->N = N;
+                                       sd->flag |= bssrdf_setup(sc);
+
+                                       sd->num_closure++;
+                                       sc++;
+                               }
+
+                               if(fabsf(weight.y) > 0.0f) {
+                                       sc->weight = make_float3(0.0f, weight.y, 0.0f);
+                                       sc->sample_weight = sample_weight;
+                                       sc->data0 = radius.y;
+                                       sc->data1 = eta;
+                                       sc->N = N;
+                                       sd->flag |= bssrdf_setup(sc);
+
+                                       sd->num_closure++;
+                                       sc++;
+                               }
+
+                               if(fabsf(weight.z) > 0.0f) {
+                                       sc->weight = make_float3(0.0f, 0.0f, weight.z);
+                                       sc->sample_weight = sample_weight;
+                                       sc->data0 = radius.z;
+                                       sc->data1 = eta;
+                                       sc->N = N;
+                                       sd->flag |= bssrdf_setup(sc);
+
+                                       sd->num_closure++;
+                                       sc++;
+                               }
+                       }
+
+                       break;
+               }
+#endif
                default:
                        break;
        }
index 57177eec48faa2493c89db9920fd62980eb3e5dd..70d73f98498dbc367764578e18a4f77f3afa4557 100644 (file)
@@ -346,12 +346,11 @@ typedef enum ClosureType {
 
        CLOSURE_BSDF_TRANSPARENT_ID,
 
-       CLOSURE_BSSRDF_CUBIC_ID,
+       CLOSURE_BSSRDF_ID,
        CLOSURE_EMISSION_ID,
        CLOSURE_DEBUG_ID,
        CLOSURE_BACKGROUND_ID,
        CLOSURE_HOLDOUT_ID,
-       CLOSURE_SUBSURFACE_ID,
        CLOSURE_AMBIENT_OCCLUSION_ID,
 
        CLOSURE_VOLUME_ID,
@@ -366,6 +365,7 @@ typedef enum ClosureType {
 #define CLOSURE_IS_BSDF_DIFFUSE(type) (type >= CLOSURE_BSDF_DIFFUSE_ID && type <= CLOSURE_BSDF_OREN_NAYAR_ID)
 #define CLOSURE_IS_BSDF_GLOSSY(type) (type >= CLOSURE_BSDF_GLOSSY_ID && type <= CLOSURE_BSDF_PHONG_RAMP_ID)
 #define CLOSURE_IS_BSDF_TRANSMISSION(type) (type >= CLOSURE_BSDF_TRANSMISSION_ID && type <= CLOSURE_BSDF_SHARP_GLASS_ID)
+#define CLOSURE_IS_BSSRDF(type) (type == CLOSURE_BSSRDF_ID)
 #define CLOSURE_IS_VOLUME(type) (type >= CLOSURE_VOLUME_ID && type <= CLOSURE_VOLUME_ISOTROPIC_ID)
 #define CLOSURE_IS_EMISSION(type) (type == CLOSURE_EMISSION_ID)
 #define CLOSURE_IS_HOLDOUT(type) (type == CLOSURE_HOLDOUT_ID)
index 0f45f63d78a420ff794b7805181950f1f21fa587..e06364c6715f00cd35c81132fdf7031c29fb2826 100644 (file)
@@ -17,6 +17,7 @@ set(SRC
        attribute.cpp
        background.cpp
        buffers.cpp
+       bssrdf.cpp
        camera.cpp
        film.cpp
        # film_response.cpp (code unused)
@@ -44,6 +45,7 @@ set(SRC_HEADERS
        attribute.h
        background.h
        buffers.h
+       bssrdf.h
        camera.h
        film.h
        # film_response.h (code unused)
diff --git a/intern/cycles/render/bssrdf.cpp b/intern/cycles/render/bssrdf.cpp
new file mode 100644 (file)
index 0000000..91064fe
--- /dev/null
@@ -0,0 +1,139 @@
+/*
+ * Copyright 2011, Blender Foundation.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+
+#include "bssrdf.h"
+
+#include "util_algorithm.h"
+#include "util_math.h"
+#include "util_types.h"
+
+#include "kernel_types.h"
+#include "kernel_montecarlo.h"
+
+#include "closure/bsdf_diffuse.h"
+#include "closure/bssrdf.h"
+
+CCL_NAMESPACE_BEGIN
+
+/* Cumulative density function utilities */
+
+static float cdf_lookup_inverse(const vector<float>& table, float2 range, float x)
+{
+       int index = upper_bound(table.begin(), table.end(), x) - table.begin();
+
+       if(index == 0)
+               return range[0];
+       else
+               index--;
+       
+       float t = (x - table[index])/(table[index+1] - table[index]);
+       float y = ((index + t)/(table.size() - 1));
+
+       return y*(range[1] - range[0]) + range[0];
+}
+
+static void cdf_invert(vector<float>& to, float2 to_range, const vector<float>& from, float2 from_range)
+{
+       float step = 1.0f/(float)(to.size() - 1);
+
+       for(int i = 0; i < to.size(); i++) {
+               float x = (i*step)*(from_range[1] - from_range[0]) + from_range[0];
+               to[i] = cdf_lookup_inverse(from, to_range, x);
+       }
+}
+
+/* BSSRDF */
+
+static float bssrdf_lookup_table_max_radius(const BSSRDFParams *ss)
+{
+       /* todo: adjust when we use the real BSSRDF */
+       return ss->ld;
+}
+
+static void bssrdf_lookup_table_create(const BSSRDFParams *ss, vector<float>& sample_table, vector<float>& pdf_table)
+{
+       const int size = BSSRDF_RADIUS_TABLE_SIZE;
+       vector<float> cdf(size);
+       vector<float> pdf(size);
+       float step = 1.0f/(float)(size - 1);
+       float max_radius = bssrdf_lookup_table_max_radius(ss);
+       float pdf_sum = 0.0f;
+
+       /* compute the probability density function */
+       for(int i = 0; i < pdf.size(); i++) {
+               float x = (i*step)*max_radius;
+               pdf[i] = bssrdf_cubic(ss->ld, x);
+               pdf_sum += pdf[i];
+       }
+
+       /* adjust for area covered by each distance */
+       for(int i = 0; i < pdf.size(); i++) {
+               float x = (i*step)*max_radius;
+               pdf[i] *= 2*M_PI_F*x;
+       }
+
+       /* normalize pdf, we multiply in reflectance later */
+       if(pdf_sum > 0.0f)
+               for(int i = 0; i < pdf.size(); i++)
+                       pdf[i] /= pdf_sum;
+
+       /* sum to account for sampling which uses overlapping sphere */
+       for(int i = pdf.size() - 2; i >= 0; i--)
+               pdf[i] = pdf[i] + pdf[i+1];
+
+       /* compute the cumulative density function */
+       cdf[0] = 0.0f;
+
+       for(int i = 1; i < size; i++)
+               cdf[i] = cdf[i-1] + 0.5f*(pdf[i-1] + pdf[i])*step*max_radius;
+       
+       /* invert cumulative density function for importance sampling */
+       float2 cdf_range = make_float2(0.0f, cdf[size - 1]);
+       float2 table_range = make_float2(0.0f, max_radius);
+
+       cdf_invert(sample_table, table_range, cdf, cdf_range);
+
+       /* copy pdf table */
+       for(int i = 0; i < pdf.size(); i++)
+               pdf_table[i] = pdf[i];
+}
+
+void bssrdf_table_build(vector<float>& table)
+{
+       vector<float> sample_table(BSSRDF_RADIUS_TABLE_SIZE);
+       vector<float> pdf_table(BSSRDF_RADIUS_TABLE_SIZE);
+
+       table.resize(BSSRDF_LOOKUP_TABLE_SIZE);
+
+       /* create a 2D lookup table, for reflection x sample radius */
+       for(int i = 0; i < BSSRDF_REFL_TABLE_SIZE; i++) {
+               float refl = (float)i/(float)(BSSRDF_REFL_TABLE_SIZE-1);
+               float ior = 1.3f;
+               float radius = 1.0f;
+
+               BSSRDFParams ss;
+               bssrdf_setup_params(&ss, refl, radius, ior);
+               bssrdf_lookup_table_create(&ss, sample_table, pdf_table);
+
+               memcpy(&table[i*BSSRDF_RADIUS_TABLE_SIZE], &sample_table[0], BSSRDF_RADIUS_TABLE_SIZE*sizeof(float));
+               memcpy(&table[BSSRDF_PDF_TABLE_OFFSET + i*BSSRDF_RADIUS_TABLE_SIZE], &pdf_table[0], BSSRDF_RADIUS_TABLE_SIZE*sizeof(float));
+       }
+}
+
+CCL_NAMESPACE_END
+
diff --git a/intern/cycles/render/bssrdf.h b/intern/cycles/render/bssrdf.h
new file mode 100644 (file)
index 0000000..975ac0b
--- /dev/null
@@ -0,0 +1,31 @@
+/*
+ * Copyright 2011, Blender Foundation.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+
+#ifndef __BSSRDF_H__
+#define __BSSRDF_H__
+
+#include "util_vector.h"
+
+CCL_NAMESPACE_BEGIN
+
+void bssrdf_table_build(vector<float>& table);
+
+CCL_NAMESPACE_END
+
+#endif /* __BSSRDF_H__ */
+
index 7dcbfa2278c5e0c3fb31c43327f615bcfe6fe529..9fc6e867166d76bcc212d6055f0c6fa7e0d64911 100644 (file)
@@ -250,7 +250,7 @@ Film::Film()
 
        filter_type = FILTER_BOX;
        filter_width = 1.0f;
-       filter_table_offset = -1;
+       filter_table_offset = TABLE_OFFSET_INVALID;
 
        need_update = true;
 }
@@ -371,8 +371,10 @@ void Film::device_update(Device *device, DeviceScene *dscene, Scene *scene)
 
 void Film::device_free(Device *device, DeviceScene *dscene, Scene *scene)
 {
-       if(filter_table_offset != -1)
+       if(filter_table_offset != TABLE_OFFSET_INVALID) {
                scene->lookup_tables->remove_table(filter_table_offset);
+               filter_table_offset = TABLE_OFFSET_INVALID;
+       }
 }
 
 bool Film::modified(const Film& film)
index c6b9ae08508bf31482f1e376fde4fc67e82ea6a0..46043cf85d2a32c87382fd3ea57c4b7566d7d060 100644 (file)
@@ -187,6 +187,7 @@ public:
 
        virtual bool has_surface_emission() { return false; }
        virtual bool has_surface_transparent() { return false; }
+       virtual bool has_surface_bssrdf() { return false; }
 
        vector<ShaderInput*> inputs;
        vector<ShaderOutput*> outputs;
index 699e697999093cc11d2be4cd1d7182ada51a9220..00039170733cbcc623bf06d5c4a790841be86464 100644 (file)
@@ -54,6 +54,7 @@ Integrator::Integrator()
        transmission_samples = 1;
        ao_samples = 1;
        mesh_light_samples = 1;
+       subsurface_samples = 1;
        progressive = true;
 
        need_update = true;
@@ -108,6 +109,7 @@ void Integrator::device_update(Device *device, DeviceScene *dscene, Scene *scene
        kintegrator->transmission_samples = transmission_samples;
        kintegrator->ao_samples = ao_samples;
        kintegrator->mesh_light_samples = mesh_light_samples;
+       kintegrator->subsurface_samples = subsurface_samples;
 
        /* sobol directions table */
        int max_samples = 1;
@@ -163,6 +165,7 @@ bool Integrator::modified(const Integrator& integrator)
                transmission_samples == integrator.transmission_samples &&
                ao_samples == integrator.ao_samples &&
                mesh_light_samples == integrator.mesh_light_samples &&
+               subsurface_samples == integrator.subsurface_samples &&
                motion_blur == integrator.motion_blur);
 }
 
index 8fb341182b74ccd882c1801b7a1f114c11b197f9..9867e310d4d170c46e6fd6580ac8583a84568277 100644 (file)
@@ -54,6 +54,7 @@ public:
        int transmission_samples;
        int ao_samples;
        int mesh_light_samples;
+       int subsurface_samples;
 
        bool progressive;
 
index a4ffc2518fba6a82d12b24b4d4f72345ca020e85..b4ff6e3152b164bd90a75a6bec6dc2b8165d478c 100644 (file)
@@ -1262,16 +1262,19 @@ void ProxyNode::compile(OSLCompiler& compiler)
 
 /* BSDF Closure */
 
-BsdfNode::BsdfNode()
-: ShaderNode("bsdf")
+BsdfNode::BsdfNode(bool scattering_)
+: ShaderNode("subsurface_scattering"), scattering(scattering_)
 {
-       closure = ccl::CLOSURE_BSDF_DIFFUSE_ID;
+       closure = ccl::CLOSURE_BSSRDF_ID;
 
        add_input("Color", SHADER_SOCKET_COLOR, make_float3(0.8f, 0.8f, 0.8f));
        add_input("Normal", SHADER_SOCKET_NORMAL, ShaderInput::NORMAL);
        add_input("SurfaceMixWeight", SHADER_SOCKET_FLOAT, 0.0f, ShaderInput::USE_SVM);
 
-       add_output("BSDF", SHADER_SOCKET_CLOSURE);
+       if(scattering)
+               add_output("BSSRDF", SHADER_SOCKET_CLOSURE);
+       else
+               add_output("BSDF", SHADER_SOCKET_CLOSURE);
 }
 
 void BsdfNode::compile(SVMCompiler& compiler, ShaderInput *param1, ShaderInput *param2, ShaderInput *param3)
@@ -1313,7 +1316,8 @@ void BsdfNode::compile(SVMCompiler& compiler, ShaderInput *param1, ShaderInput *
                        (param3)? param3->stack_offset: SVM_STACK_INVALID);
        }
        else {
-               compiler.add_node(NODE_CLOSURE_BSDF, normal_in->stack_offset);
+               compiler.add_node(NODE_CLOSURE_BSDF, normal_in->stack_offset, SVM_STACK_INVALID,
+                       (param3)? param3->stack_offset: SVM_STACK_INVALID);
        }
 }
 
@@ -1548,6 +1552,29 @@ void TransparentBsdfNode::compile(OSLCompiler& compiler)
        compiler.add(this, "node_transparent_bsdf");
 }
 
+/* Subsurface Scattering Closure */
+
+SubsurfaceScatteringNode::SubsurfaceScatteringNode()
+: BsdfNode(true)
+{
+       name = "subsurface_scattering";
+       closure = CLOSURE_BSSRDF_ID;
+
+       add_input("Scale", SHADER_SOCKET_FLOAT, 0.01f);
+       add_input("Radius", SHADER_SOCKET_VECTOR, make_float3(0.1f, 0.1f, 0.1f));
+       add_input("IOR", SHADER_SOCKET_FLOAT, 1.3f);
+}
+
+void SubsurfaceScatteringNode::compile(SVMCompiler& compiler)
+{
+       BsdfNode::compile(compiler, input("Scale"), input("IOR"), input("Radius"));
+}
+
+void SubsurfaceScatteringNode::compile(OSLCompiler& compiler)
+{
+       compiler.add(this, "node_subsurface_scattering");
+}
+
 /* Emissive Closure */
 
 EmissionNode::EmissionNode()
index 1efe4ae076df1ae6b1558e577d2322874a507392..0d9f84327d069ab96a1c116d7dea1fd7e1040cb3 100644 (file)
@@ -198,11 +198,13 @@ public:
 
 class BsdfNode : public ShaderNode {
 public:
-       SHADER_NODE_CLASS(BsdfNode)
+       BsdfNode(bool scattering = false);
+       SHADER_NODE_BASE_CLASS(BsdfNode);
 
        void compile(SVMCompiler& compiler, ShaderInput *param1, ShaderInput *param2, ShaderInput *param3 = NULL);
 
        ClosureType closure;
+       bool scattering;
 };
 
 class WardBsdfNode : public BsdfNode {
@@ -257,6 +259,12 @@ public:
        static ShaderEnum distribution_enum;
 };
 
+class SubsurfaceScatteringNode : public BsdfNode {
+public:
+       SHADER_NODE_CLASS(SubsurfaceScatteringNode)
+       bool has_surface_bssrdf() { return true; }
+};
+
 class EmissionNode : public ShaderNode {
 public:
        SHADER_NODE_CLASS(EmissionNode)
index f5585babf5ccdb92b54a289d672305cb0c99b839..cefb6315725908a5e4fd77a4a1472f16b12795cb 100644 (file)
@@ -73,7 +73,7 @@ void OSLShaderManager::device_update(Device *device, DeviceScene *dscene, Scene
        if(!need_update)
                return;
 
-       device_free(device, dscene);
+       device_free(device, dscene, scene);
 
        /* determine which shaders are in use */
        device_update_shaders_used(scene);
@@ -114,11 +114,11 @@ void OSLShaderManager::device_update(Device *device, DeviceScene *dscene, Scene
        device_update_common(device, dscene, scene, progress);
 }
 
-void OSLShaderManager::device_free(Device *device, DeviceScene *dscene)
+void OSLShaderManager::device_free(Device *device, DeviceScene *dscene, Scene *scene)
 {
        OSLGlobals *og = (OSLGlobals*)device->osl_memory();
 
-       device_free_common(device, dscene);
+       device_free_common(device, dscene, scene);
 
        /* clear shader engine */
        og->use = false;
@@ -328,6 +328,7 @@ const char *OSLShaderManager::shader_load_bytecode(const string& hash, const str
        OSLShaderInfo info;
        info.has_surface_emission = (bytecode.find("\"emission\"") != string::npos);
        info.has_surface_transparent = (bytecode.find("\"transparent\"") != string::npos);
+       info.has_surface_bssrdf = (bytecode.find("\"bssrdf\"") != string::npos);
        loaded_shaders[hash] = info;
 
        return loaded_shaders.find(hash)->first.c_str();
@@ -511,6 +512,8 @@ void OSLCompiler::add(ShaderNode *node, const char *name, bool isfilepath)
                        current_shader->has_surface_emission = true;
                if(info->has_surface_transparent)
                        current_shader->has_surface_transparent = true;
+               if(info->has_surface_bssrdf)
+                       current_shader->has_surface_bssrdf = true;
        }
 }
 
@@ -671,6 +674,8 @@ void OSLCompiler::generate_nodes(const set<ShaderNode*>& nodes)
                                                current_shader->has_surface_emission = true;
                                        if(node->has_surface_transparent())
                                                current_shader->has_surface_transparent = true;
+                                       if(node->has_surface_bssrdf())
+                                               current_shader->has_surface_bssrdf = true;
                                }
                                else
                                        nodes_done = false;
@@ -736,6 +741,7 @@ void OSLCompiler::compile(OSLGlobals *og, Shader *shader)
                shader->has_surface = false;
                shader->has_surface_emission = false;
                shader->has_surface_transparent = false;
+               shader->has_surface_bssrdf = false;
                shader->has_volume = false;
                shader->has_displacement = false;
 
index 4b4ed6cba0038a9a14b39967012ff7ff50db69df..2d3996df0eb087403ae37bf1f208d1d76285cb94 100644 (file)
@@ -50,11 +50,13 @@ class ShaderOutput;
 
 struct OSLShaderInfo {
        OSLShaderInfo()
-       : has_surface_emission(false), has_surface_transparent(false)
+       : has_surface_emission(false), has_surface_transparent(false),
+         has_surface_bssrdf(false)
        {}
 
        bool has_surface_emission;
        bool has_surface_transparent;
+       bool has_surface_bssrdf;
 };
 
 /* Shader Manage */
@@ -69,7 +71,7 @@ public:
        bool use_osl() { return true; }
 
        void device_update(Device *device, DeviceScene *dscene, Scene *scene, Progress& progress);
-       void device_free(Device *device, DeviceScene *dscene);
+       void device_free(Device *device, DeviceScene *dscene, Scene *scene);
 
        /* osl compile and query */
        static bool osl_compile(const string& inputfile, const string& outputfile);
index 2b0609fdf1f5fb5884193d77d6aa85292aff2659..a6dca62ffd0ada5fc4eec5fe3e2e0191d7834e38 100644 (file)
@@ -99,7 +99,7 @@ void Scene::free_memory(bool final)
 
                object_manager->device_free(device, &dscene);
                mesh_manager->device_free(device, &dscene);
-               shader_manager->device_free(device, &dscene);
+               shader_manager->device_free(device, &dscene, this);
                light_manager->device_free(device, &dscene);
 
                particle_system_manager->device_free(device, &dscene);
@@ -187,7 +187,6 @@ void Scene::device_update(Device *device_, Progress& progress)
        progress.set_status("Updating Particle Systems");
        particle_system_manager->device_update(device, &dscene, this, progress);
 
-
        if(progress.get_cancel()) return;
 
        progress.set_status("Updating Film");
index b9b49bf29896f6a37cbbfe5ab6f7ea2599236652..c7f39b4151aeb93705ff9292dc9e91946f8209bd 100644 (file)
@@ -16,6 +16,7 @@
  * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
  */
 
+#include "bssrdf.h"
 #include "device.h"
 #include "graph.h"
 #include "light.h"
@@ -25,6 +26,7 @@
 #include "scene.h"
 #include "shader.h"
 #include "svm.h"
+#include "tables.h"
 
 #include "util_foreach.h"
 
@@ -46,6 +48,7 @@ Shader::Shader()
        has_surface = false;
        has_surface_transparent = false;
        has_surface_emission = false;
+       has_surface_bssrdf = false;
        has_volume = false;
        has_displacement = false;
 
@@ -115,6 +118,7 @@ void Shader::tag_used(Scene *scene)
 ShaderManager::ShaderManager()
 {
        need_update = true;
+       bssrdf_table_offset = TABLE_OFFSET_INVALID;
 }
 
 ShaderManager::~ShaderManager()
@@ -196,7 +200,8 @@ void ShaderManager::device_update_shaders_used(Scene *scene)
 
 void ShaderManager::device_update_common(Device *device, DeviceScene *dscene, Scene *scene, Progress& progress)
 {
-       device_free_common(device, dscene);
+       device->tex_free(dscene->shader_flag);
+       dscene->shader_flag.clear();
 
        if(scene->shaders.size() == 0)
                return;
@@ -204,6 +209,7 @@ void ShaderManager::device_update_common(Device *device, DeviceScene *dscene, Sc
        uint shader_flag_size = scene->shaders.size()*4;
        uint *shader_flag = dscene->shader_flag.resize(shader_flag_size);
        uint i = 0;
+       bool has_surface_bssrdf = false;
 
        foreach(Shader *shader, scene->shaders) {
                uint flag = 0;
@@ -216,6 +222,8 @@ void ShaderManager::device_update_common(Device *device, DeviceScene *dscene, Sc
                        flag |= SD_HAS_VOLUME;
                if(shader->homogeneous_volume)
                        flag |= SD_HOMOGENEOUS_VOLUME;
+               if(shader->has_surface_bssrdf)
+                       has_surface_bssrdf = true;
 
                shader_flag[i++] = flag;
                shader_flag[i++] = shader->pass_id;
@@ -224,10 +232,32 @@ void ShaderManager::device_update_common(Device *device, DeviceScene *dscene, Sc
        }
 
        device->tex_alloc("__shader_flag", dscene->shader_flag);
+
+       /* bssrdf lookup table */
+       KernelBSSRDF *kbssrdf = &dscene->data.bssrdf;
+
+       if(has_surface_bssrdf && bssrdf_table_offset == TABLE_OFFSET_INVALID) {
+               vector<float> table;
+
+               bssrdf_table_build(table);
+               bssrdf_table_offset = scene->lookup_tables->add_table(dscene, table);
+
+               kbssrdf->table_offset = (int)bssrdf_table_offset;
+               kbssrdf->num_attempts = BSSRDF_MAX_ATTEMPTS;
+       }
+       else if(!has_surface_bssrdf && bssrdf_table_offset != TABLE_OFFSET_INVALID) {
+               scene->lookup_tables->remove_table(bssrdf_table_offset);
+               bssrdf_table_offset = TABLE_OFFSET_INVALID;
+       }
 }
 
-void ShaderManager::device_free_common(Device *device, DeviceScene *dscene)
+void ShaderManager::device_free_common(Device *device, DeviceScene *dscene, Scene *scene)
 {
+       if(bssrdf_table_offset != TABLE_OFFSET_INVALID) {
+               scene->lookup_tables->remove_table(bssrdf_table_offset);
+               bssrdf_table_offset = TABLE_OFFSET_INVALID;
+       }
+
        device->tex_free(dscene->shader_flag);
        dscene->shader_flag.clear();
 }
index b38e098e3cbef4e93d78392f34d631eaff840244..2a9f119846754d1a935e5f4341ee6b5c492028ea 100644 (file)
@@ -75,6 +75,7 @@ public:
        bool has_surface_transparent;
        bool has_volume;
        bool has_displacement;
+       bool has_surface_bssrdf;
 
        /* requested mesh attributes */
        AttributeRequestSet attributes;
@@ -116,11 +117,11 @@ public:
 
        /* device update */
        virtual void device_update(Device *device, DeviceScene *dscene, Scene *scene, Progress& progress) = 0;
-       virtual void device_free(Device *device, DeviceScene *dscene) = 0;
+       virtual void device_free(Device *device, DeviceScene *dscene, Scene *scene) = 0;
 
        void device_update_shaders_used(Scene *scene);
        void device_update_common(Device *device, DeviceScene *dscene, Scene *scene, Progress& progress);
-       void device_free_common(Device *device, DeviceScene *dscene);
+       void device_free_common(Device *device, DeviceScene *dscene, Scene *scene);
 
        /* get globally unique id for a type of attribute */
        uint get_attribute_id(ustring name);
@@ -138,6 +139,8 @@ protected:
 
        typedef unordered_map<ustring, uint, ustringHash> AttributeIDMap;
        AttributeIDMap unique_attribute_id;
+
+       size_t bssrdf_table_offset;
 };
 
 CCL_NAMESPACE_END
index 5cb11a4ec1aa3c0a83d26dbef127b5db19d323f2..ea2fe4991db72304e539327144db384a9ea9daf0 100644 (file)
@@ -50,7 +50,7 @@ void SVMShaderManager::device_update(Device *device, DeviceScene *dscene, Scene
                return;
 
        /* test if we need to update */
-       device_free(device, dscene);
+       device_free(device, dscene, scene);
 
        /* determine which shaders are in use */
        device_update_shaders_used(scene);
@@ -99,9 +99,9 @@ void SVMShaderManager::device_update(Device *device, DeviceScene *dscene, Scene
        need_update = false;
 }
 
-void SVMShaderManager::device_free(Device *device, DeviceScene *dscene)
+void SVMShaderManager::device_free(Device *device, DeviceScene *dscene, Scene *scene)
 {
-       device_free_common(device, dscene);
+       device_free_common(device, dscene, scene);
 
        device->tex_free(dscene->svm_nodes);
        dscene->svm_nodes.clear();
@@ -486,6 +486,8 @@ void SVMCompiler::generate_closure(ShaderNode *node, set<ShaderNode*>& done)
                        current_shader->has_surface_emission = true;
                if(node->has_surface_transparent())
                        current_shader->has_surface_transparent = true;
+               if(node->has_surface_bssrdf())
+                       current_shader->has_surface_bssrdf = true;
 
                /* end node is added outside of this */
        }
@@ -546,6 +548,8 @@ void SVMCompiler::generate_multi_closure(ShaderNode *node, set<ShaderNode*>& don
                        current_shader->has_surface_emission = true;
                if(node->has_surface_transparent())
                        current_shader->has_surface_transparent = true;
+               if(node->has_surface_bssrdf())
+                       current_shader->has_surface_bssrdf = true;
        }
 
        done.insert(node);
@@ -654,6 +658,7 @@ void SVMCompiler::compile(Shader *shader, vector<int4>& global_svm_nodes, int in
        shader->has_surface = false;
        shader->has_surface_emission = false;
        shader->has_surface_transparent = false;
+       shader->has_surface_bssrdf = false;
        shader->has_volume = false;
        shader->has_displacement = false;
 
index c1ce619e12a8824dee0d14d476d575cac13711ed..e09144a4e76edeb4e52bc671aa8e40054d5da873 100644 (file)
@@ -48,7 +48,7 @@ public:
        void reset(Scene *scene);
 
        void device_update(Device *device, DeviceScene *dscene, Scene *scene, Progress& progress);
-       void device_free(Device *device, DeviceScene *dscene);
+       void device_free(Device *device, DeviceScene *dscene, Scene *scene);
 };
 
 /* Graph Compiler */
index fecdd52c60c0d58418cd94adcf98cf1966294b98..c7c86f6896089f0bf70457c0b69b25e1cf1f30c0 100644 (file)
@@ -24,6 +24,8 @@
 
 CCL_NAMESPACE_BEGIN
 
+/* Lookup Tables */
+
 LookupTables::LookupTables()
 {
        need_update = true;
@@ -39,7 +41,7 @@ void LookupTables::device_update(Device *device, DeviceScene *dscene)
        if(!need_update)
                return;
 
-       device->tex_alloc("__lookup_table", dscene->lookup_table, true); // XXX interpolation
+       device->tex_alloc("__lookup_table", dscene->lookup_table);
 
        need_update = false;
 }
@@ -73,6 +75,8 @@ size_t LookupTables::add_table(DeviceScene *dscene, vector<float>& data)
                        lookup_tables.insert(table, new_table);
                        break;
                }
+               else
+                       new_table.offset = table->offset + table->size;
        }
 
        if(table == lookup_tables.end()) {
index 5fa5136ae798886fa01a32e299613853190cf73f..605efd3747f0fe5c697fb716fee0b16aeeec55de 100644 (file)
@@ -28,6 +28,7 @@ class DeviceScene;
 class Scene;
 
 enum { TABLE_CHUNK_SIZE = 256 };
+enum { TABLE_OFFSET_INVALID = -1 };
 
 class LookupTables {
 public:
index c37fa1a4dc634d6b0e7daca4e9114c81391bb3d3..f2e814527fd15cebe22264b5bd3eab90ae9bdd2a 100644 (file)
@@ -1151,14 +1151,7 @@ __device float safe_logf(float a, float b)
 
 __device float safe_divide(float a, float b)
 {
-       float result;
-
-       if(b == 0.0f)
-               result = 0.0f;
-       else
-               result = a/b;
-       
-       return result;
+       return (b != 0.0f)? a/b: 0.0f;
 }
 
 /* Ray Intersection */
index fc1cd6e45d571bf63857dc03c0dc51b8182b864c..1d94c1a0b8ab7425b26afe226ae146807c5e4df5 100644 (file)
@@ -752,6 +752,7 @@ struct ShadeResult;
 #define SH_NODE_TANGENT                                        174
 #define SH_NODE_NORMAL_MAP                             175
 #define SH_NODE_HAIR_INFO                              176
+#define SH_NODE_SUBSURFACE_SCATTERING  177
 
 /* custom defines options for Material node */
 #define SH_NODE_MAT_DIFF   1
index 5cf8758aa1b19e9bc6a39baf9661655daf97874a..b6fa1dacd0f534173cda1ddf8a62da46413a9e56 100644 (file)
@@ -3438,6 +3438,7 @@ static void registerShaderNodes(void)
        register_node_type_sh_holdout();
        //register_node_type_sh_volume_transparent();
        //register_node_type_sh_volume_isotropic();
+       register_node_type_sh_subsurface_scattering();
        register_node_type_sh_mix_shader();
        register_node_type_sh_add_shader();
 
index 147d002475b7844f1b7bdd13c0c5a8f7a6fada5e..08747b19df255dd2806f561fa1d03bc0cad4d7e4 100644 (file)
@@ -2074,6 +2074,11 @@ 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)
+{
+       node_bsdf_diffuse(color, 0.0, N, result);
+}
+
 /* emission */
 
 void node_emission(vec4 color, float strength, vec3 N, out vec4 result)
index f3b3d6b1f53d5d8df7b7eaa47077d0afe6be2add..f2ce9665e2b5cd90741a560ed47b4bbee2151233 100644 (file)
@@ -175,6 +175,7 @@ set(SRC
        shader/nodes/node_shader_output_world.c
        shader/nodes/node_shader_particle_info.c
        shader/nodes/node_shader_script.c
+       shader/nodes/node_shader_subsurface_scattering.c
        shader/nodes/node_shader_tangent.c
        shader/nodes/node_shader_tex_brick.c
        shader/nodes/node_shader_tex_checker.c
index 9561fe004093f7928b57bd65fe5442d80d8543aa..eb324182e3981c9612a6c7d4dcc66ec6b98dcc5a 100644 (file)
@@ -101,6 +101,7 @@ void register_node_type_sh_emission(void);
 void register_node_type_sh_holdout(void);
 void register_node_type_sh_volume_transparent(void);
 void register_node_type_sh_volume_isotropic(void);
+void register_node_type_sh_subsurface_scattering(void);
 void register_node_type_sh_mix_shader(void);
 void register_node_type_sh_add_shader(void);
 
index 9d044772274df07a3a5860acdb9f7fb13fe5f4c3..811cd3f3b4a9d7b3af703c57aeda499ff7adb923 100644 (file)
@@ -84,6 +84,7 @@ DefNode( ShaderNode,     SH_NODE_BSDF_REFRACTION,    def_glossy,             "BS
 DefNode( ShaderNode,     SH_NODE_BSDF_TRANSLUCENT,   0,                      "BSDF_TRANSLUCENT",   BsdfTranslucent,  "Translucent BSDF",  ""       )
 DefNode( ShaderNode,     SH_NODE_BSDF_TRANSPARENT,   0,                      "BSDF_TRANSPARENT",   BsdfTransparent,  "Transparent BSDF",  ""       )
 DefNode( ShaderNode,     SH_NODE_BSDF_VELVET,        0,                      "BSDF_VELVET",        BsdfVelvet,       "Velvet BSDF",       ""       )
+DefNode( ShaderNode,     SH_NODE_SUBSURFACE_SCATTERING, 0,                   "SUBSURFACE_SCATTERING",SubsurfaceScattering,"Subsurface Scattering","")
 DefNode( ShaderNode,     SH_NODE_VOLUME_TRANSPARENT, 0,                      "VOLUME_TRANSPARENT", VolumeTransparent,"Transparent Volume",""       )
 DefNode( ShaderNode,     SH_NODE_VOLUME_ISOTROPIC,   0,                      "VOLUME_ISOTROPIC",   VolumeIsotropic,  "Isotropic Volume",  ""       )
 DefNode( ShaderNode,     SH_NODE_EMISSION,           0,                      "EMISSION",           Emission,         "Emission",          ""       )
diff --git a/source/blender/nodes/shader/nodes/node_shader_subsurface_scattering.c b/source/blender/nodes/shader/nodes/node_shader_subsurface_scattering.c
new file mode 100644 (file)
index 0000000..cde6ec8
--- /dev/null
@@ -0,0 +1,69 @@
+/*
+ * ***** BEGIN GPL LICENSE BLOCK *****
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version. 
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ * The Original Code is Copyright (C) 2005 Blender Foundation.
+ * All rights reserved.
+ *
+ * The Original Code is: all of this file.
+ *
+ * Contributor(s): none yet.
+ *
+ * ***** END GPL LICENSE BLOCK *****
+ */
+
+#include "../node_shader_util.h"
+
+/* **************** OUTPUT ******************** */
+
+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, 1000000.0f},
+       {       SOCK_VECTOR, 1, N_("Radius"),   1.0f, 1.0f, 1.0f, 0.0f, 0.0f, 100.0f},
+       //{     SOCK_FLOAT, 1, N_("IOR"),               1.3f, 0.0f, 0.0f, 0.0f, 1.0f, 1000.0f},
+       {       SOCK_VECTOR, 1, N_("Normal"),   0.0f, 0.0f, 0.0f, 1.0f, -1.0f, 1.0f, PROP_NONE, SOCK_HIDE_VALUE},
+       {       -1, 0, ""       }
+};
+
+static bNodeSocketTemplate sh_node_subsurface_scattering_out[] = {
+       {       SOCK_SHADER, 0, N_("BSSRDF")},
+       {       -1, 0, ""       }
+};
+
+static int node_shader_gpu_subsurface_scattering(GPUMaterial *mat, bNode *UNUSED(node), bNodeExecData *UNUSED(execdata), GPUNodeStack *in, GPUNodeStack *out)
+{
+       if (!in[1].link)
+               in[1].link = GPU_builtin(GPU_VIEW_NORMAL);
+
+       return GPU_stack_link(mat, "node_subsurface_scattering", in, out);
+}
+
+/* node type definition */
+void register_node_type_sh_subsurface_scattering(void)
+{
+       static bNodeType ntype;
+
+       sh_node_type_base(&ntype, SH_NODE_SUBSURFACE_SCATTERING, "Subsurface Scattering", NODE_CLASS_SHADER, 0);
+       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(&ntype, 150, 60, 200);
+       node_type_init(&ntype, NULL);
+       node_type_storage(&ntype, "", NULL, NULL);
+       node_type_gpu(&ntype, node_shader_gpu_subsurface_scattering);
+
+       nodeRegisterType(&ntype);
+}
+