Calculating face and vertex normals

How do I calculate both flat and smooth clockwise vertex normals for triangles and quads?

Hi,

If you are not interested in the math stuff, then here you have two subroutines which compute the triangle normals and the vertex normals of a structured 3D mesh of points. They are written in Fortran95 and with comments in spanish, but the code is the same :slight_smile: (it is taken from a project in which I’m working now):

!-----------------------------------------------------------------------!
! NormalCW !
! !
! Esta función se encarga de calcular el vector normal a una superfi- !
! cie. Para ello obtiene las coordenadas de tres puntos situados en !
! sentido de las agujas del reloj de la cara a ver de la superficie. !
! !
! Entradas: !
! PuntoA(3): coordenadas del primer punto. !
! PuntoB(3): coordenadas del segundo punto. !
! PuntoC(3): coordenadas del tercer punto. !
! !
! Salidas: !
! NormalCW(3): vector normal. !
! !
! Sintáxis: !
! real(8)(3) NormalCW(PuntoA, PuntoB, PuntoC) !
! !
!-----------------------------------------------------------------------!
function NormalCW(puntoA, puntoB, puntoC)
implicit none
real(8) :: NormalCW(1:3)
real(8) :: puntoA(1:3), puntoB(1:3), puntoC(1:3)

real(8) :: length, v1(3), v2(3), Normal(1:3)

v1(1:3) = puntoC(1:3) - puntoA(1:3)
v2(1:3) = puntoB(1:3) - puntoA(1:3)

Normal(1) = v1(2) * v2(3) - v1(3) * v2(2)
Normal(2) = v1(3) * v2(1) - v1(1) * v2(3)
Normal(3) = v1(1) * v2(2) - v1(2) * v2(1)

length = sqrt( Normal(1) ** 2 + Normal(2) ** 2 + Normal(3) ** 2 )

Normal(1:3) = Normal(1:3) / length

NormalCW = Normal

end function NormalCW
!-----------------------------------------------------------------------!
! CalcVertexNormals !
! !
! Esta subrutina calcula los vectores normales de los vértices que com- !
! ponen la malla estructurada de dimensiones Nx por Ny. Para ello de- !
! termina los vectores normales a los triángulos y calcula los valores !
! medios para obtener los correspondientes a los vértices. !
! !
! Entradas: !
! MeshCoord(Nx, Ny, 3): coordenadas tridimensionales de los puntos !
! de la malla estructurada. !
! Nx: número de vértices según el índice i. !
! Ny: número de vértices según el índice j. !
! !
! Salidas: !
! VertexNormals(Nx, Ny, 3): vectores normales de los vértices. !
! !
! Sintáxis: !
! call CalcVertexNormals(MeshCoord, VertexNormals, Nx, Ny) !
! !
!-----------------------------------------------------------------------!
subroutine CalcVertexNormals(MeshCoord, VertexNormals, Nx, Ny)
implicit none
integer :: Nx, Ny
real(8) :: MeshCoord(1:Nx, 1:Ny, 3)
real(8) :: VertexNormals(1:Nx, 1:Ny, 3)

integer :: i, j, indexi, indexj, num
real(8) :: p1(3), p2(3), p3(3), n(3)
real(8) :: TriangleNormals(1 Nx-1)*2, 1:Ny-1, 1:3)

!Calcula los vectores normales a las superficies triangulares
do j = 1, Ny-1
do i = 1, (Nx-1)*2, 2
indexi = (i + 1) / 2
indexj = j
p1(1:3) = MeshCoord(indexi, indexj, 1:3)
p2(1:3) = MeshCoord(indexi+1, indexj+1, 1:3)
p3(1:3) = MeshCoord(indexi, indexj+1, 1:3)
TriangleNormals(i, j, 1:3) = NormalCW(p1, p2, p3)
enddo
do i = 2, (Nx-1)*2, 2
indexi = i / 2
indexj = j
p1(1:3) = MeshCoord(indexi, indexj, 1:3)
p2(1:3) = MeshCoord(indexi+1, indexj, 1:3)
p3(1:3) = MeshCoord(indexi+1, indexj+1, 1:3)
TriangleNormals(i, j, 1:3) = NormalCW(p1, p2, p3)
enddo
enddo

!Calcula los vectores normales de los vértices del escenario
do j = 1, Ny
do i = 1, Nx
n = 0d0
indexi = 2 * (i - 1) - 1
indexj = j - 1
!Suma los vectores normales por bloques
if((i > 1).AND.(j > 1)) then
n(1:3) = n(1:3) + &
TriangleNormals(indexi, indexj, 1:3) + &
TriangleNormals(indexi+1, indexj, 1:3)
endif
if((i < Nx).AND.(j > 1)) then
n(1:3) = n(1:3) + TriangleNormals(indexi+2, indexj, 1:3)
endif
if((i > 1).AND.(j < Ny)) then
n(1:3) = n(1:3) + TriangleNormals(indexi+1, indexj+1, 1:3)
endif
if((i < Nx).AND.(j < Ny)) then
n(1:3) = n(1:3) + &
TriangleNormals(indexi+2, indexj+1, 1:3) + &
TriangleNormals(indexi+3, indexj+1, 1:3)
endif
n = n / sqrt(n(1) ** 2 + n(2) ** 2 + n(3) ** 2)
VertexNormals(i, j, 1:3) = n(1:3)
enddo
enddo

end subroutine CalcVertexNormals

Hope it helps you

Miguel

Ups,

there where the smilie is, you should read the following:

real(8) :: TriangleNormals(1:(Nx-1)*2, 1:Ny-1, 1:3)

Miguel

Or to make it short. The facenormal is obtained by taking the crossproduct of the two vectors building the face. And vertexnormals is obtained by averaging the normals of the faces sharing the vertex.

And to add a little to what Bob said:
When you do vertex normals, averaging all the face face normals will result in smooth edges everywhere. If you want to preserve sharp edges, then you need to keep vertex normals associated with each face and when you do the averaging around a vertex, incorporate some sort of threshold angle into the averaging process.
That may be more information than you wanted though

Thanks for the help, but I’m still lost. I don’t have any knowledge of Fortran so all of that code went way over my head. I do understand C++ and Pascal. I would like a routine that calculates sharp edged face normals when all the routine has for input data is 3 or 4 position vertices.

Thanks in advance.