Cycles: implement pointiness geometry attribute
authorSergey Sharybin <sergey.vfx@gmail.com>
Fri, 6 Feb 2015 07:35:46 +0000 (12:35 +0500)
committerSergey Sharybin <sergey.vfx@gmail.com>
Tue, 10 Feb 2015 15:33:41 +0000 (20:33 +0500)
This attribute means how "pointy" the geometry surface is, which allows to do
effects like dirt maps and wear-off effects on render geometry. This means the
attribute is calculated for the final mesh which means no baking (which implies
UV unwrap) is needed. Apart from this the behavior is quite close to how vertex
dirty colors works.

The new attribute is available as an output socket of Geometry node.

There's no penalty for the render time, only some delay on scene preparation
(the delay is linear of the mesh complexity).

Reviewers: brecht, juicyfruit

Subscribers: eyecandy, venomgfx

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

intern/cycles/blender/blender_mesh.cpp
intern/cycles/kernel/kernel_types.h
intern/cycles/kernel/shaders/node_geometry.osl
intern/cycles/render/attribute.cpp
intern/cycles/render/nodes.cpp
source/blender/nodes/shader/nodes/node_shader_geometry.c

index 10b037ab4b18f54cb1267243946cf902eacfe21f..a02449a759457ff223b252daf5f10ccf5842e063 100644 (file)
@@ -28,6 +28,7 @@
 
 #include "util_foreach.h"
 #include "util_logging.h"
+#include "util_math.h"
 
 #include "mikktspace.h"
 
@@ -387,6 +388,68 @@ static void create_mesh(Scene *scene, Mesh *mesh, BL::Mesh b_mesh, const vector<
                }
        }
 
+       /* create vertex pointiness attributes */
+       /* TODO(sergey): Consider moving all the attribute creation into own
+        * functions for clarity.
+        */
+       {
+               if(mesh->need_attribute(scene, ATTR_STD_POINTINESS)) {
+                       Attribute *attr = mesh->attributes.add(ATTR_STD_POINTINESS);
+                       float *data = attr->data_float();
+                       int *counter = new int[numverts];
+                       float *raw_data = new float[numverts];
+                       float3 *edge_accum = new float3[numverts];
+
+                       /* Calculate pointiness using single ring neighborhood. */
+                       memset(counter, 0, sizeof(int) * numverts);
+                       memset(raw_data, 0, sizeof(float) * numverts);
+                       memset(edge_accum, 0, sizeof(float3) * numverts);
+                       BL::Mesh::edges_iterator e;
+                       i = 0;
+                       for(b_mesh.edges.begin(e); e != b_mesh.edges.end(); ++e, ++i) {
+                               int v0 = b_mesh.edges[i].vertices()[0],
+                                   v1 = b_mesh.edges[i].vertices()[1];
+                               float3 co0 = get_float3(b_mesh.vertices[v0].co()),
+                                      co1 = get_float3(b_mesh.vertices[v1].co());
+                               edge_accum[v0] += normalize(co1 - co0);
+                               edge_accum[v1] += normalize(co0 - co1);
+                               ++counter[v0];
+                               ++counter[v1];
+                       }
+                       i = 0;
+                       for(b_mesh.vertices.begin(v); v != b_mesh.vertices.end(); ++v, ++i) {
+                               if(counter[i] > 0) {
+                                       float3 normal = get_float3(b_mesh.vertices[i].normal());
+                                       float angle = safe_acosf(dot(normal, edge_accum[i] / counter[i]));
+                                       raw_data[i] = angle * M_1_PI_F;
+                               }
+                               else {
+                                       raw_data[i] = 0.0f;
+                               }
+                       }
+
+                       /* Blur vertices to approximate 2 ring neighborhood. */
+                       memset(counter, 0, sizeof(int) * numverts);
+                       memcpy(data, raw_data, sizeof(float) * numverts);
+                       i = 0;
+                       for(b_mesh.edges.begin(e); e != b_mesh.edges.end(); ++e, ++i) {
+                               int v0 = b_mesh.edges[i].vertices()[0],
+                                   v1 = b_mesh.edges[i].vertices()[1];
+                               data[v0] += raw_data[v1];
+                               data[v1] += raw_data[v0];
+                               ++counter[v0];
+                               ++counter[v1];
+                       }
+                       for(i = 0; i < numverts; ++i) {
+                               data[i] /= counter[i] + 1;
+                       }
+
+                       delete [] counter;
+                       delete [] raw_data;
+                       delete [] edge_accum;
+               }
+       }
+
        /* create uv map attributes */
        if (b_mesh.tessface_uv_textures.length() != 0) {
                BL::Mesh::tessface_uv_textures_iterator l;
index 77d3ea874d0d89e7ffbc0435d23b773b18bffb14..680094dcd0e3ba86bf133707cf164dc183913b3e 100644 (file)
@@ -534,6 +534,7 @@ typedef enum AttributeStandard {
        ATTR_STD_VOLUME_FLAME,
        ATTR_STD_VOLUME_HEAT,
        ATTR_STD_VOLUME_VELOCITY,
+       ATTR_STD_POINTINESS,
        ATTR_STD_NUM,
 
        ATTR_STD_NOT_FOUND = ~0
index 580ccba8238391c06ca2ffa470c5cb10d80c32e4..2bbaaff2133414a65d89ff4bab0ddb0375494d7d 100644 (file)
@@ -26,7 +26,8 @@ shader node_geometry(
        output normal TrueNormal = normal(0.0, 0.0, 0.0),
        output vector Incoming = vector(0.0, 0.0, 0.0),
        output point Parametric = point(0.0, 0.0, 0.0),
-       output float Backfacing = 0.0)
+       output float Backfacing = 0.0,
+       output float Pointiness = 0.0)
 {
        Position = P;
        Normal = NormalIn;
@@ -57,5 +58,7 @@ shader node_geometry(
                /* otherwise use surface derivatives */
                Tangent = normalize(dPdu);
        }
+
+       getattribute("geom:pointiness", Pointiness);
 }
 
index bf83c970bf85faa61c1610f0ab6d8a2d93152df4..656420f5dbc19fd453ecc80a8bb022a2f21d8dfb 100644 (file)
@@ -230,6 +230,8 @@ const char *Attribute::standard_name(AttributeStandard std)
                        return "heat";
                case ATTR_STD_VOLUME_VELOCITY:
                        return "velocity";
+               case ATTR_STD_POINTINESS:
+                       return "pointiness";
                case ATTR_STD_NOT_FOUND:
                case ATTR_STD_NONE:
                case ATTR_STD_NUM:
@@ -375,6 +377,9 @@ Attribute *AttributeSet::add(AttributeStandard std, ustring name)
                        case ATTR_STD_VOLUME_VELOCITY:
                                attr = add(name, TypeDesc::TypeVector, ATTR_ELEMENT_VOXEL);
                                break;
+                       case ATTR_STD_POINTINESS:
+                               attr = add(name, TypeDesc::TypeFloat, ATTR_ELEMENT_VERTEX);
+                               break;
                        default:
                                assert(0);
                                break;
@@ -395,6 +400,9 @@ Attribute *AttributeSet::add(AttributeStandard std, ustring name)
                        case ATTR_STD_GENERATED_TRANSFORM:
                                attr = add(name, TypeDesc::TypeMatrix, ATTR_ELEMENT_MESH);
                                break;
+                       case ATTR_STD_POINTINESS:
+                               attr = add(name, TypeDesc::TypeFloat, ATTR_ELEMENT_VERTEX);
+                               break;
                        default:
                                assert(0);
                                break;
index 3448209f101e8b0d68f3c7405c488c870f020502..46c962b16c3a189f5f48145607126aa1e980166d 100644 (file)
@@ -2171,13 +2171,18 @@ GeometryNode::GeometryNode()
        add_output("Incoming", SHADER_SOCKET_VECTOR);
        add_output("Parametric", SHADER_SOCKET_POINT);
        add_output("Backfacing", SHADER_SOCKET_FLOAT);
+       add_output("Pointiness", SHADER_SOCKET_FLOAT);
 }
 
 void GeometryNode::attributes(Shader *shader, AttributeRequestSet *attributes)
 {
        if(shader->has_surface) {
-               if(!output("Tangent")->links.empty())
+               if(!output("Tangent")->links.empty()) {
                        attributes->add(ATTR_STD_GENERATED);
+               }
+               if(!output("Pointiness")->links.empty()) {
+                       attributes->add(ATTR_STD_POINTINESS);
+               }
        }
 
        ShaderNode::attributes(shader, attributes);
@@ -2234,6 +2239,15 @@ void GeometryNode::compile(SVMCompiler& compiler)
                compiler.stack_assign(out);
                compiler.add_node(NODE_LIGHT_PATH, NODE_LP_backfacing, out->stack_offset);
        }
+
+       out = output("Pointiness");
+       if(!out->links.empty()) {
+               compiler.stack_assign(out);
+               compiler.add_node(NODE_ATTR,
+                                 ATTR_STD_POINTINESS,
+                                 out->stack_offset,
+                                 NODE_ATTR_FLOAT);
+       }
 }
 
 void GeometryNode::compile(OSLCompiler& compiler)
index 01851b51c27a607b5fcd6d6ac35fbc71c915530b..553ea65154f83855c4c23d764f003287de3c909b 100644 (file)
@@ -37,6 +37,7 @@ static bNodeSocketTemplate sh_node_geometry_out[] = {
        {       SOCK_VECTOR, 0, N_("Incoming"),                 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f},
        {       SOCK_VECTOR, 0, N_("Parametric"),               0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f},
        {       SOCK_FLOAT,  0, N_("Backfacing"),               0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f},
+       {       SOCK_FLOAT,  0, N_("Pointiness"),               0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f},
        {       -1, 0, ""       }
 };