Cycles microdisplacement: preserve smooth normals for linear subdivison
authorMai Lavelle <mai.lavelle@gmail.com>
Tue, 12 Apr 2016 23:17:34 +0000 (01:17 +0200)
committerBrecht Van Lommel <brechtvanlommel@gmail.com>
Tue, 12 Apr 2016 23:37:33 +0000 (01:37 +0200)
This way we prevent cracks in the model due to discontinuous normals, by using
smooth normals for displacement instead of always getting flat normals after
linear subdivision.

Reviewed By: brecht

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

intern/cycles/render/mesh.cpp
intern/cycles/subd/subd_dice.cpp
intern/cycles/subd/subd_patch.cpp
intern/cycles/subd/subd_patch.h
intern/cycles/subd/subd_split.cpp

index aa9f773917a6d59fff390fa9d99e9d079fbc4b89..241a1c44ebf73310124b89049c190592ba621b0e 100644 (file)
@@ -1451,31 +1451,63 @@ void Mesh::tessellate(DiagSplit *split)
 {
        int num_faces = triangles.size();
 
+       add_face_normals();
+       add_vertex_normals();
+
+       Attribute *attr_fN = attributes.find(ATTR_STD_FACE_NORMAL);
+       float3 *fN = attr_fN->data_float3();
+
+       Attribute *attr_vN = attributes.find(ATTR_STD_VERTEX_NORMAL);
+       float3 *vN = attr_vN->data_float3();
+
        for(int f = 0; f < num_faces; f++) {
                if(!forms_quad[f]) {
                        /* triangle */
-                       LinearTrianglePatch* patch = new LinearTrianglePatch();
-                       float3 *hull = patch->hull;
+                       LinearTrianglePatch patch;
+                       float3 *hull = patch.hull;
+                       float3 *normals = patch.normals;
 
                        for(int i = 0; i < 3; i++) {
                                hull[i] = verts[triangles[f].v[i]];
                        }
 
-                       split->split_triangle(patch);
-                       delete patch;
+                       if(smooth[f]) {
+                               for(int i = 0; i < 3; i++) {
+                                       normals[i] = vN[triangles[f].v[i]];
+                               }
+                       }
+                       else {
+                               for(int i = 0; i < 3; i++) {
+                                       normals[i] = fN[f];
+                               }
+                       }
+
+                       split->split_triangle(&patch);
                }
                else {
                        /* quad */
-                       LinearQuadPatch* patch = new LinearQuadPatch();
-                       float3 *hull = patch->hull;
+                       LinearQuadPatch patch;
+                       float3 *hull = patch.hull;
+                       float3 *normals = patch.normals;
 
                        hull[0] = verts[triangles[f  ].v[0]];
                        hull[1] = verts[triangles[f  ].v[1]];
                        hull[3] = verts[triangles[f  ].v[2]];
                        hull[2] = verts[triangles[f+1].v[2]];
 
-                       split->split_quad(patch);
-                       delete patch;
+                       if(smooth[f]) {
+                               normals[0] = vN[triangles[f  ].v[0]];
+                               normals[1] = vN[triangles[f  ].v[1]];
+                               normals[3] = vN[triangles[f  ].v[2]];
+                               normals[2] = vN[triangles[f+1].v[2]];
+                       }
+                       else {
+                               for(int i = 0; i < 4; i++) {
+                                       normals[i] = fN[f];
+                               }
+                       }
+
+                       split->split_quad(&patch);
 
                        // consume second triangle in quad
                        f++;
index 301f30c8d95a3df4652947345cdb298462557207..4f27eaf7d1be404c883893e9049781320d54dee4 100644 (file)
@@ -58,10 +58,9 @@ void EdgeDice::reserve(int num_verts, int num_tris)
 
 int EdgeDice::add_vert(Patch *patch, float2 uv)
 {
-       float3 P, N, dPdu, dPdv;
+       float3 P, N;
 
-       patch->eval(&P, &dPdu, &dPdv, uv.x, uv.y);
-       N = normalize(cross(dPdu, dPdv));
+       patch->eval(&P, NULL, NULL, &N, uv.x, uv.y);
 
        assert(vert_offset < params.mesh->verts.size());
 
@@ -81,7 +80,7 @@ int EdgeDice::add_vert(Patch *patch, float2 uv)
 
 void EdgeDice::add_triangle(Patch *patch, int v0, int v1, int v2)
 {
-       params.mesh->add_triangle(v0, v1, v2, params.shader, params.smooth);
+       params.mesh->add_triangle(v0, v1, v2, params.shader, params.smooth, false);
 
        if(params.ptex) {
                Attribute *attr_ptex_face_id = params.mesh->attributes.add(ATTR_STD_PTEX_FACE_ID);
@@ -159,7 +158,7 @@ float3 QuadDice::eval_projected(SubPatch& sub, float u, float v)
        float2 uv = map_uv(sub, u, v);
        float3 P;
 
-       sub.patch->eval(&P, NULL, NULL, uv.x, uv.y);
+       sub.patch->eval(&P, NULL, NULL, NULL, uv.x, uv.y);
        if(params.camera)
                P = transform_perspective(&params.camera->worldtoraster, P);
 
index 0db46ec492de0b246936d1dbf95c884b72564370..3b0fb5b9f3a69cfffc0ba524e3f05f0e3ba8e90a 100644 (file)
@@ -57,7 +57,7 @@ static void decasteljau_bicubic(float3 *P, float3 *du, float3 *dv, const float3
 
 /* Linear Quad Patch */
 
-void LinearQuadPatch::eval(float3 *P, float3 *dPdu, float3 *dPdv, float u, float v)
+void LinearQuadPatch::eval(float3 *P, float3 *dPdu, float3 *dPdv, float3 *N, float u, float v)
 {
        float3 d0 = interp(hull[0], hull[1], u);
        float3 d1 = interp(hull[2], hull[3], u);
@@ -68,6 +68,10 @@ void LinearQuadPatch::eval(float3 *P, float3 *dPdu, float3 *dPdv, float u, float
                *dPdu = interp(hull[1] - hull[0], hull[3] - hull[2], v);
                *dPdv = interp(hull[2] - hull[0], hull[3] - hull[1], u);
        }
+
+       if(N) {
+               *N = normalize(interp(interp(normals[0], normals[1], u), interp(normals[2], normals[3], u), v));
+       }
 }
 
 BoundBox LinearQuadPatch::bound()
@@ -82,7 +86,7 @@ BoundBox LinearQuadPatch::bound()
 
 /* Linear Triangle Patch */
 
-void LinearTrianglePatch::eval(float3 *P, float3 *dPdu, float3 *dPdv, float u, float v)
+void LinearTrianglePatch::eval(float3 *P, float3 *dPdu, float3 *dPdv, float3 *N, float u, float v)
 {
        *P = u*hull[0] + v*hull[1] + (1.0f - u - v)*hull[2];
 
@@ -90,6 +94,10 @@ void LinearTrianglePatch::eval(float3 *P, float3 *dPdu, float3 *dPdv, float u, f
                *dPdu = hull[0] - hull[2];
                *dPdv = hull[1] - hull[2];
        }
+
+       if(N) {
+               *N = normalize(u*normals[0] + v*normals[1] + (1.0f - u - v)*normals[2]);
+       }
 }
 
 BoundBox LinearTrianglePatch::bound()
@@ -104,9 +112,22 @@ BoundBox LinearTrianglePatch::bound()
 
 /* Bicubic Patch */
 
-void BicubicPatch::eval(float3 *P, float3 *dPdu, float3 *dPdv, float u, float v)
+void BicubicPatch::eval(float3 *P, float3 *dPdu, float3 *dPdv, float3 *N, float u, float v)
 {
-       decasteljau_bicubic(P, dPdu, dPdv, hull, u, v);
+       if (N) {
+               float3 dPdu_, dPdv_;
+               decasteljau_bicubic(P, &dPdu_, &dPdv_, hull, u, v);
+
+               if (dPdu && dPdv) {
+                       *dPdu = dPdu_;
+                       *dPdv = dPdv_;
+               }
+
+               *N = normalize(cross(dPdu_, dPdv_));
+       }
+       else {
+               decasteljau_bicubic(P, dPdu, dPdv, hull, u, v);
+       }
 }
 
 BoundBox BicubicPatch::bound()
index 9be4606c248ab99e9bcbbb0e345cae4ff01d7136..bfa04412c6622c96997b539f175bffd61895243e 100644 (file)
@@ -25,7 +25,7 @@ CCL_NAMESPACE_BEGIN
 class Patch {
 public:
        virtual ~Patch() {}
-       virtual void eval(float3 *P, float3 *dPdu, float3 *dPdv, float u, float v) = 0;
+       virtual void eval(float3 *P, float3 *dPdu, float3 *dPdv, float3 *N, float u, float v) = 0;
        virtual bool is_triangle() { return false; }
        virtual BoundBox bound() = 0;
        virtual int ptex_face_id() { return -1; }
@@ -36,8 +36,9 @@ public:
 class LinearQuadPatch : public Patch {
 public:
        float3 hull[4];
+       float3 normals[4];
 
-       void eval(float3 *P, float3 *dPdu, float3 *dPdv, float u, float v);
+       void eval(float3 *P, float3 *dPdu, float3 *dPdv, float3 *N, float u, float v);
        bool is_triangle() { return false; }
        BoundBox bound();
 };
@@ -47,8 +48,9 @@ public:
 class LinearTrianglePatch : public Patch {
 public:
        float3 hull[3];
+       float3 normals[3];
 
-       void eval(float3 *P, float3 *dPdu, float3 *dPdv, float u, float v);
+       void eval(float3 *P, float3 *dPdu, float3 *dPdv, float3 *N, float u, float v);
        bool is_triangle() { return true; }
        BoundBox bound();
 };
@@ -59,7 +61,7 @@ class BicubicPatch : public Patch {
 public:
        float3 hull[16];
 
-       void eval(float3 *P, float3 *dPdu, float3 *dPdv, float u, float v);
+       void eval(float3 *P, float3 *dPdu, float3 *dPdv, float3 *N, float u, float v);
        bool is_triangle() { return false; }
        BoundBox bound();
 };
index 095eefeab22a7d8a039b998d7c44a3b5d7f59669..70081673597e3def42bd9027d2c03ad7dab941a8 100644 (file)
@@ -50,7 +50,7 @@ float3 DiagSplit::to_world(Patch *patch, float2 uv)
 {
        float3 P;
 
-       patch->eval(&P, NULL, NULL, uv.x, uv.y);
+       patch->eval(&P, NULL, NULL, NULL, uv.x, uv.y);
        if(params.camera)
                P = transform_point(&params.objecttoworld, P);