In a lot of 3D design programs such as Blender and 3D Studio Max you can select which surfaces of a shape you want smooth as opposed to hard and flat. This amounts to averaging (I assume) the normals of the vertices which share the same position as a particular vertex. I am trying to write something of the sort, but this is my first time doing this and the way I came up with must be really inefficient and I believe is n^2 time complexity. So given we have a 3D shape with 99 vertices, then the loop must run 99 x 99 times, almost 10,000 times.
With a ball it's easy as each vertex's normal is just the normalised position of the vertex (the unit vector of the position). I can do that easy as shown below:
I was wondering how these programs do it and if there's a more efficient way than this. My method is to loop through i from 0 to 99 and j from 0 to 99. I basically go through and check if another vertex has the same position, if so, I check whether the angle between its normal and the normal of the other vertex is under the desired angle threshold. If so, I add it to the sum of the vector, then I divide by the number of vertices that were matched. I'm not sure if this is correct.
#include <iostream>
#include <vector>
#include <math.h>
using namespace std;
float thresholdAngle = 60 /*degrees*/ / 180 * 3.14159; // In radians
struct vec3
{
float x, y, z;
bool hasSamePositionAs(vec3& other)
{
if (other.x == this->x &&
other.y == this->y &&
other.z == this->z)
return true;
return false;
}
bool angleIsUnderThresholdWith(vec3& other)
{//THIS SHOULD BE IF DOTPRODUCT > COS(THRESHOLDANGLE)
return dotProductWith(other) < thresholdAngle ? true : false;
}
float dotProductWith(vec3& other)
{
return x * other.x + y * other.y + z * other.z;
}
void operator +=(vec3 other) { x += other.x; y += other.y; z + other.z; }
int main()
{
// 33 triangles, 99 vertices
vector<vec3> vertices(99); // ALL UNIT VECTORS, NORMALS
vector<vec3> newNormals(99);
for (int i = 0; i < 99; i++)
{
vec3 normal = vertices[i];
for (int j = 0; j < 99; j++)
{
if (j == i) continue;
if (vertices[i].hasSamePositionAs(vertices[j]))
{
if (vertices[i].angleIsUnderThresholdWith(vertices[j]))
{
normal += vertices[j];
}
}
}
normalise(normal);
newNormals[i] = normal;
}
}
Edit: OK so using this code I managed to get what I was after, but the algorithm is still n^2. You can notice the blue lines, which are the normals, are separated for each face in flat shading, and merged together (averaged) when passing it through this code.