How does a cube's vertex normals differ when made with 8 vertices vs 24 vertices?

Hi,

I have huge confusion.
A Cube has 8 vertices but as each vertex is part of three faces with different normals, we duplicate the vertices and make them 24.

There is a method add_vertex_normals in Cycles Mesh class. It is taking normals from the faces and computing the vertex normals.
When I am reading an FBX, I am building the mesh only with unique vertices. I am blindly discarding normals and calculating them using the same logic of this add_vertex_normals.

So, what is wrong in defining a cube only with 8 vertices and compute vertex normals using this method? How is this Cube mesh different from 24 vertices Cube mesh?

When I add a triangle to a mesh, I need to pass “smooth” flag. What is this for? What I thought is, if ATTR_STD_VERTEX_NORMAL attribute is defined in my mesh, then smooth is true. else false.

Thanks a lot in advance.

Some info about 3N vertices is at
https://web.archive.org/web/20161204152348/http://www.dcs.ed.ac.uk/teaching/cs4/www/graphics/Web/ply.html

What if a program does not know the adjacency between faces and vertices (so-called unshared vertices)? This is where each triangle (say) is purely a collection of three positions in space, with no notion whether some triangles have common vertices. This is a fairly common situation. Assuming there are N triangles in a given object, then 3N vertices should be written to the file, followed by N faces that simply connect up these vertices. We anticipate that a utility will be written that converts between unshared and shared vertex files.

So if your program can deal with such isolated triangles, then you can discard duplicate vertices.
you may read io_mesh_ply in addon folder that also deals with 3N vertices.

1 Like

Hi @ankitm,
Thanks a lot for your response.

So, I have generated a mesh in such a way that

I have a struct like this.
struct Vertex
{
float[3] position;
float[3] normal;
//Some methods here for bool comparison and stuff
};

define a unordered_map<Vertex, int> unique_map.
For every triangle, calculate the triangle’s normal. Triangle’s vertices are v0, v1, v2.
So, for each triangle, we will have three Vertex objects. {v0, normal}, {v1, normal} {v2, normal}

I am inserting them into map;

In this way i will have unique combination of position and normal.

From this map, I am populating mesh-> verts and VERTEX_NORMAL attribute and mesh->triangles

My problem is, when I import this mesh into blender and do a “shade smooth”, nothing happens.

Do I need to change my logic?

Should I change the logic in such a way that if I have N triangles, there will be 3N vertices and 3N normals in VERTEX_NORMAL attribute?

or

Should I change the logic in such a way that if I have P unique positions, there will be P vertices and P normals in VERTEX_NORMAL attribute and run the add_vertex_normals kind of logic to populate/update VERTEX_NORMAL attribute?

Thanks in advance.

I got it fixed. Thank you @ankitm

Now I’m curious.
could you add what you did ?

Hi @ankitm,

Here is my code to smooth the normals. Please note that I have renamed ccl::Mesh->triangles field to “indices”. Please let me know if my approach is wrong.
BTW, when I smooth normals with this approach (The same way blender/cycles does), I am facing an issue which is explained at Odd triangle in my mesh

Please let me know if any improvements can be done.

static void smooth_normals(ccl::Mesh &mesh)
  {
Attribute *attr_vN = mesh.attributes.find(ATTR_STD_VERTEX_NORMAL);
if (!attr_vN) {
  return;
}
float3 *vN = attr_vN->data_float3();

int indices_count = mesh.indices.size();
int vertices_count = mesh.verts.size();

memset(vN, 0, vertices_count * sizeof(float3));
bool flip = mesh.transform_negative_scaled;
std::unordered_map<float3, size_t, Float3Hash, Float3Equals> unique_positions_map;
std::vector<float3> normals;
normals.resize(0);
Transform ntfm;
if (mesh.transform_applied) {
  ntfm = transform_inverse(mesh.transform_normal);
}
for (int i = 0, t = 0; i < indices_count; i += 3, t++) {
  int i0 = mesh.indices[i];
  int i1 = mesh.indices[i + 1];
  int i2 = mesh.indices[i + 2];

  float3 &v0 = mesh.verts[i0];
  float3 &v1 = mesh.verts[i1];
  float3 &v2 = mesh.verts[i2];
  float3 &face_normal = GeometryUtils::compute_normal(v0, v1, v2);
  if (mesh.transform_applied) {
    face_normal = normalize(transform_direction(&ntfm, face_normal));
  }
  size_t ni0 = 0;
  size_t ni1 = 0;
  size_t ni2 = 0;
  if (unique_positions_map.find(v0) == unique_positions_map.end()) {
    ni0 = unique_positions_map.size();
    unique_positions_map.insert(std::pair<float3, size_t>(v0, ni0));
    normals.push_back(make_float3(0, 0, 0));
  }
  else {
    ni0 = unique_positions_map.at(v0);
  }

  if (unique_positions_map.find(v1) == unique_positions_map.end()) {
    ni1 = unique_positions_map.size();
    unique_positions_map.insert(std::pair<float3, size_t>(v1, ni1));
    normals.push_back(make_float3(0, 0, 0));
  }
  else {
    ni1 = unique_positions_map.at(v1);
  }

  if (unique_positions_map.find(v2) == unique_positions_map.end()) {
    ni2 = unique_positions_map.size();
    unique_positions_map.insert(std::pair<float3, size_t>(v2, ni2));
    normals.push_back(make_float3(0, 0, 0));
  }
  else {
    ni2 = unique_positions_map.at(v2);
  }
  normals[ni0] += face_normal;
  normals[ni1] += face_normal;
  normals[ni2] += face_normal;
}

int normals_count = normals.size();
for (size_t i = 0; i < normals_count; i++) {
  normals[i] = normalize(normals[i]);
  if (flip) {
    normals[i] = -normals[i];
  }
}

for (size_t i = 0; i < vertices_count; i++) {
  float3 &position = mesh.verts[i];
  size_t normal_index = unique_positions_map.at(position);
  vN[i] = normals[normal_index];
}
  }

Hi @ankitm

Looks like I need to improve my logic. Thinking of grouping triangles and build smooth groups. Let me try. Will let you know the result