Нормальное усреднение карты высот

у меня есть следующий код для расчета нормалей карты высот

void CalcMapNormals(HeightMap * map, Vec3f normals[])
{
int     dst, i, j, right, bottom;
Vec3f   p0, p1, p2;
Vec3f   n0;

/* Avoid writing map->rows|cols - 1 all the time */
right = map->cols - 1;
bottom = map->rows - 1;

dst = 0;
for (i = 0; i < map->rows; i++) {
for (j = 0; j < map->cols; j++) {
Vec3Set(normals[dst], 0, 0, 0);
/* Vertex can have 2, 3, or 4 neighbours horizontally and vertically */
if (i < bottom && j < right) {
/* Right and below */
GetHeightPoint(map, i, j, p0);
GetHeightPoint(map, i + 1, j, p1);
GetHeightPoint(map, i + 1, j + 1, p2);
CalcTriNormal(n0, p0, p1, p2);
VecAdd(normals[dst], normals[dst], n0);
}
/*  TODO: the other three possibilities */
VecNormalize(normals[dst]);
dst += 1;
}
}
/* Sanity check */
if (dst != map->rows * map->cols)
Fail("Internal error in CalcMapNormals: normals count mismatch");
}

Я понимаю, что код получает три вершины треугольника, вычисляет его нормаль, а затем добавляет их и нормализует их, чтобы получить усредненную нормаль. Но я не знаю, как вы можете получить другие три возможности, я делал что-то вроде следующего:

void CalcMapNormals(HeightMap * map, Vec3f normals[])
{
int     dst, i, j, right, bottom;
Vec3f   p0, p1, p2;
Vec3f   n0;
Vec3f   p3, p4, p5;
Vec3f   n1;
Vec3f   p6, p7, p8;
Vec3f   n2;
Vec3f   p9, p10, p11;
Vec3f   n3;
/* Avoid writing map->rows|cols - 1 all the time */
right = map->cols - 1;
bottom = map->rows - 1;

dst = 0;
for (i = 0; i < map->rows; i++) {
for (j = 0; j < map->cols; j++) {
Vec3Set(normals[dst], 0, 0, 0);
/* Vertex can have 2, 3, or 4 neighbours horizontally and vertically */
if (i < bottom && j < right) {
/* Right and below */
GetHeightPoint(map, i, j, p0);
GetHeightPoint(map, i + 1, j, p1);
GetHeightPoint(map, i + 1, j + 1, p2);
CalcTriNormal(n0, p0, p1, p2);
VecAdd(normals[dst], normals[dst], n0);
}
if ( i > bottom && j > 0)
{
GetHeightPoint(map, i, j, p3);
GetHeightPoint(map, i + 1, j, p4);
GetHeightPoint(map, i, j -1, p5);
CalcTriNormal(n1, p3, p4, p5);
VecAdd(normals[dst], normals[dst], n1);
}
if ( i > 0 && j > 0)
{
GetHeightPoint(map, i, j, p6);
GetHeightPoint(map, i, j - 1, p7);
GetHeightPoint(map, i - 1, j, p8);
CalcTriNormal(n2, p6, p7, p8);
VecAdd(normals[dst], normals[dst], n2);

}
if ( i > bottom && j < right)
{

GetHeightPoint(map, i, j, p9);
GetHeightPoint(map, i-1, j, p10);
GetHeightPoint(map, i, j+1, p11);
CalcTriNormal(n3, p9, p10, p11);
VecAdd(normals[dst], normals[dst], n3);
}
/*  TODO: the other three possibilities */
VecNormalize(normals[dst]);
dst += 1;
}
}
/* Sanity check */
if (dst != map->rows * map->cols)
Fail("Internal error in CalcMapNormals: normals count mismatch");
}

Но я не думаю, что это дает мне результат, которого я хотел, я понимаю концепцию нормального усреднения, но не могу понять код.

0

Решение

Привет, Yzwboy, вот один способ, которым я бы попытался сделать «сглаженные» нормали (усредненные по соседним треугольникам):

Чтобы вычислить «сглаженные» нормали, вам нужно будет присвоить каждой вершине нормаль, которая усредняется по нормалям треугольников, смежных с вершиной.

Я бы рассчитал средневзвешенное значение на основе угла между двумя ребрами, смежными с рассматриваемой вершиной (перекрестное произведение, которое легко вычисляется):

псевдокод:

Vec3F faceNormal(int face_id, int vertex_id) // assumes C-->B-->A is clockwise
{
Vec3f A = triangleMesh.face[face_id].vertex[vertex_id];       // A
Vec3f B = triangleMesh.face[face_id].vertex[(vertex_id+1)%3]; // B
Vec3f C = triangleMesh.face[face_id].vertex[(vertex_id+2)%3]; // C
Vec3f BA = B-A;
Vec3f CA = C-A;
Vec3f Normal = BA.cross(CA);

float sin_alpha = length(Normal) / (BA.len() * CA.len() );  // depending on your implementation of Vec3f it could be .magnitude() or .length() instead of .len()
return (Normal.normalize() * asin(sin_alpha);)
}

А затем изменить нормаль по вершине:

void computeNormals() {
for (vertex v in triangleMesh)
{
Vec3f Normal (0,0,0);
for (int i = 0;i < TriangleCount;i++)
if (triangleMesh.face[i].contains(v) )
{
int vertID = vertexPositionInTriangle(i,v); //Can be 0,1 or 2.  Use an enum to make A = 0, B=1, C=2 if that is easier to read:)
Normal = Normal + faceNormal(i,vertID);
}
addNormalToVertexV(Normal.normalize(),v); // this is a function to set the normal for a vertex, the vertex class must have a member for normal though and the arguments for the function are Vec3f, Vec3f
}
}

Вы также можете вычислить площадь каждого треугольника для использования в качестве веса, хотя я считаю, что использование углов лучше всего подходит для внешности в большинстве случаев.

Я пытался использовать имена, которые соответствуют спецификации Vec3f, а также встроенные функции, чтобы сохранить работу, но вам нужно будет сделать некоторое кодирование, чтобы заставить работать псевдокод (у меня нет доступа к среде GL Test здесь).

Надеюсь это поможет 🙂

1

Другие решения

Других решений пока нет …

По вопросам рекламы [email protected]