Cycles microdisplacement: preserve smooth normals for linear subdivison

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
This commit is contained in:
Mai Lavelle 2016-04-13 01:17:34 +02:00 committed by Brecht Van Lommel
parent 068ee2cd98
commit c1a27a76cf
5 changed files with 76 additions and 22 deletions

@ -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++;

@ -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);

@ -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()

@ -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();
};

@ -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);