[Code] How do you compute weighted vertex normals?
-
Reference:
http://www.bytehazard.com/code/vertnorm.html
http://meshlabstuff.blogspot.com/2009/04/on-computation-of-vertex-normals.htmlHow can you compute weighted vertex normals for vertices in SketchUp?
Compute vertex normals so these two boxes produces similar normals.
Adding up all connected face normals results in a normal that lean towards the triangulated side - which not what I want.
Current Solution
The current solution that appear to yield the best results:<span class="syntaxdefault"><br /> def self</span><span class="syntaxkeyword">.</span><span class="syntaxdefault">vertex_normal</span><span class="syntaxkeyword">(</span><span class="syntaxdefault"> vertex </span><span class="syntaxkeyword">)<br /></span><span class="syntaxdefault"> faces </span><span class="syntaxkeyword">=</span><span class="syntaxdefault"> vertex</span><span class="syntaxkeyword">.</span><span class="syntaxdefault">faces<br /> edges </span><span class="syntaxkeyword">=</span><span class="syntaxdefault"> vertex</span><span class="syntaxkeyword">.</span><span class="syntaxdefault">edges<br /> point </span><span class="syntaxkeyword">=</span><span class="syntaxdefault"> vertex</span><span class="syntaxkeyword">.</span><span class="syntaxdefault">position<br /> normal </span><span class="syntaxkeyword">=</span><span class="syntaxdefault"> Geom</span><span class="syntaxkeyword">;;</span><span class="syntaxdefault">Vector3d</span><span class="syntaxkeyword">.new(</span><span class="syntaxdefault"> 0</span><span class="syntaxkeyword">,</span><span class="syntaxdefault"> 0</span><span class="syntaxkeyword">,</span><span class="syntaxdefault"> 0 </span><span class="syntaxkeyword">)<br /></span><span class="syntaxdefault"> until faces</span><span class="syntaxkeyword">.empty?<br /></span><span class="syntaxdefault"> face </span><span class="syntaxkeyword">=</span><span class="syntaxdefault"> faces</span><span class="syntaxkeyword">.</span><span class="syntaxdefault">shift<br /> e1</span><span class="syntaxkeyword">,</span><span class="syntaxdefault"> e2 </span><span class="syntaxkeyword">=</span><span class="syntaxdefault"> face</span><span class="syntaxkeyword">.</span><span class="syntaxdefault">edges </span><span class="syntaxkeyword">&</span><span class="syntaxdefault"> edges<br /> </span><span class="syntaxcomment"># Ensure the vectors are in the order of the loop.<br /> </span><span class="syntaxdefault">test_vertex </span><span class="syntaxkeyword">= </span><span class="syntaxdefault">e1</span><span class="syntaxkeyword">.</span><span class="syntaxdefault">reversed_in</span><span class="syntaxkeyword">?( </span><span class="syntaxdefault">face </span><span class="syntaxkeyword">) ? </span><span class="syntaxdefault">e1</span><span class="syntaxkeyword">.</span><span class="syntaxdefault">start </span><span class="syntaxkeyword">; </span><span class="syntaxdefault">e1</span><span class="syntaxkeyword">.</span><span class="syntaxdefault">end<br /> </span><span class="syntaxkeyword">if </span><span class="syntaxdefault">test_vertex </span><span class="syntaxkeyword">== </span><span class="syntaxdefault">vertex<br /> e1</span><span class="syntaxkeyword">, </span><span class="syntaxdefault">e2 </span><span class="syntaxkeyword">= [ </span><span class="syntaxdefault">e2</span><span class="syntaxkeyword">, </span><span class="syntaxdefault">e1 </span><span class="syntaxkeyword">]<br /> </span><span class="syntaxdefault">end<br /> </span><span class="syntaxcomment"># Now the full angle can be calculated.<br /></span><span class="syntaxdefault"> pt1 </span><span class="syntaxkeyword">=</span><span class="syntaxdefault"> e1</span><span class="syntaxkeyword">.</span><span class="syntaxdefault">other_vertex</span><span class="syntaxkeyword">(</span><span class="syntaxdefault"> vertex </span><span class="syntaxkeyword">).</span><span class="syntaxdefault">position<br /> pt2 </span><span class="syntaxkeyword">=</span><span class="syntaxdefault"> e2</span><span class="syntaxkeyword">.</span><span class="syntaxdefault">other_vertex</span><span class="syntaxkeyword">(</span><span class="syntaxdefault"> vertex </span><span class="syntaxkeyword">).</span><span class="syntaxdefault">position<br /> v1 </span><span class="syntaxkeyword">=</span><span class="syntaxdefault"> point</span><span class="syntaxkeyword">.</span><span class="syntaxdefault">vector_to</span><span class="syntaxkeyword">(</span><span class="syntaxdefault"> pt1 </span><span class="syntaxkeyword">)<br /></span><span class="syntaxdefault"> v2 </span><span class="syntaxkeyword">=</span><span class="syntaxdefault"> point</span><span class="syntaxkeyword">.</span><span class="syntaxdefault">vector_to</span><span class="syntaxkeyword">(</span><span class="syntaxdefault"> pt2 </span><span class="syntaxkeyword">)<br /></span><span class="syntaxdefault"> angle </span><span class="syntaxkeyword">=</span><span class="syntaxdefault"> self</span><span class="syntaxkeyword">.</span><span class="syntaxdefault">full_angle_between</span><span class="syntaxkeyword">(</span><span class="syntaxdefault"> v1</span><span class="syntaxkeyword">,</span><span class="syntaxdefault"> v2</span><span class="syntaxkeyword">,</span><span class="syntaxdefault"> face</span><span class="syntaxkeyword">.</span><span class="syntaxdefault">normal </span><span class="syntaxkeyword">)<br /></span><span class="syntaxdefault"> face_normal </span><span class="syntaxkeyword">=</span><span class="syntaxdefault"> face</span><span class="syntaxkeyword">.</span><span class="syntaxdefault">normal<br /> face_normal</span><span class="syntaxkeyword">.</span><span class="syntaxdefault">length </span><span class="syntaxkeyword">=</span><span class="syntaxdefault"> angle<br /> normal </span><span class="syntaxkeyword">+=</span><span class="syntaxdefault"> face_normal<br /> end<br /> normal</span><span class="syntaxkeyword">.</span><span class="syntaxdefault">normalize</span><span class="syntaxkeyword">!<br /></span><span class="syntaxdefault"> end<br /> <br /> def self</span><span class="syntaxkeyword">.</span><span class="syntaxdefault">full_angle_between</span><span class="syntaxkeyword">(</span><span class="syntaxdefault"> vector1</span><span class="syntaxkeyword">,</span><span class="syntaxdefault"> vector2</span><span class="syntaxkeyword">,</span><span class="syntaxdefault"> normal </span><span class="syntaxkeyword">)<br /></span><span class="syntaxdefault"> cross </span><span class="syntaxkeyword">=</span><span class="syntaxdefault"> vector1 </span><span class="syntaxkeyword">*</span><span class="syntaxdefault"> vector2<br /> direction </span><span class="syntaxkeyword">=</span><span class="syntaxdefault"> cross </span><span class="syntaxkeyword">%</span><span class="syntaxdefault"> normal<br /> angle </span><span class="syntaxkeyword">=</span><span class="syntaxdefault"> vector1</span><span class="syntaxkeyword">.</span><span class="syntaxdefault">angle_between</span><span class="syntaxkeyword">(</span><span class="syntaxdefault"> vector2 </span><span class="syntaxkeyword">)<br /></span><span class="syntaxdefault"> angle </span><span class="syntaxkeyword">=</span><span class="syntaxdefault"> 360</span><span class="syntaxkeyword">.</span><span class="syntaxdefault">degrees </span><span class="syntaxkeyword">-</span><span class="syntaxdefault"> angle if direction </span><span class="syntaxkeyword">></span><span class="syntaxdefault"> 0.0<br /> angle<br /> end<br /></span>
-
Perhaps something like
faces = vertex.faces return nil if not faces[0] norm = faces[0].normal faces.shift faces.each{|face| norm = norm ****+**** face.normal } return norm
???
EDIT: Fixed typo * >>> + TIG. -
That's the naive version I used. But it doesn't always produce the normal one want. The linked articles describe the issues that method has, and further explains one has to weigh the contribution of the faces.
-
Well then try perhaps 'weight' by each face's surface area...
faces = vertex.faces return nil if not faces[0] norm = faces[0].normal norm.length = faces[0].area faces.shift faces.each{|face| fnor = face.normal fnor.length = face.area norm = norm ****+**** fnor } norm.normalize! return norm
OR get relative angles between the normals and make adjustments ???
EDIT: typo fixed * >>> + TIG. -
That snippet gave me zero length vectors for a vertex with three connected faces at an corner.
-
Wouldn't it have something to do with the angle between the 2 edges of the face at that vertex * that face's normal. Something so that a small sliver of a face - only 5 degrees for example, would get its normal * 5. Then a face with 90 degrees would get its normal weight * 90. Then combine all those normals, and the vector will be greater than 1, so normalize it to scale it back down.
Would that give an appropriate weight to each face in the overall vertex normal?
-
@chris fullmer said:
Wouldn't it have something to do with the angle between the 2 edges of the face at that vertex * that face's normal. Something so that a small sliver of a face - only 5 degrees for example, would get its normal * 5. Then a face with 90 degrees would get its normal weight * 90. Then combine all those normals, and the vector will be greater than 1, so normalize it to scale it back down.
Would that give an appropriate weight to each face in the overall vertex normal?
What you describe is one of the methods in the links in my OP. The problem is that I don't fully understand how to implement the weighting...
-
This appear to work:
<span class="syntaxdefault"><br />def self</span><span class="syntaxkeyword">.</span><span class="syntaxdefault">vertex_normal</span><span class="syntaxkeyword">( </span><span class="syntaxdefault">vertex </span><span class="syntaxkeyword">)<br /> </span><span class="syntaxdefault">faces </span><span class="syntaxkeyword">= </span><span class="syntaxdefault">vertex</span><span class="syntaxkeyword">.</span><span class="syntaxdefault">faces<br /> edges </span><span class="syntaxkeyword">= </span><span class="syntaxdefault">vertex</span><span class="syntaxkeyword">.</span><span class="syntaxdefault">edges<br /> point </span><span class="syntaxkeyword">= </span><span class="syntaxdefault">vertex</span><span class="syntaxkeyword">.</span><span class="syntaxdefault">position<br /> </span><span class="syntaxkeyword">return </span><span class="syntaxdefault">nil </span><span class="syntaxkeyword">if </span><span class="syntaxdefault">faces</span><span class="syntaxkeyword">.empty?<br /> </span><span class="syntaxdefault">normal </span><span class="syntaxkeyword">= </span><span class="syntaxdefault">Geom</span><span class="syntaxkeyword">;;</span><span class="syntaxdefault">Vector3d</span><span class="syntaxkeyword">.new( </span><span class="syntaxdefault">0</span><span class="syntaxkeyword">, </span><span class="syntaxdefault">0</span><span class="syntaxkeyword">, </span><span class="syntaxdefault">0 </span><span class="syntaxkeyword">)<br /> </span><span class="syntaxdefault">until faces</span><span class="syntaxkeyword">.empty?<br /> </span><span class="syntaxdefault">face </span><span class="syntaxkeyword">= </span><span class="syntaxdefault">faces</span><span class="syntaxkeyword">.</span><span class="syntaxdefault">shift<br /> e1</span><span class="syntaxkeyword">, </span><span class="syntaxdefault">e2 </span><span class="syntaxkeyword">= </span><span class="syntaxdefault">face</span><span class="syntaxkeyword">.</span><span class="syntaxdefault">edges </span><span class="syntaxkeyword">& </span><span class="syntaxdefault">edges<br /> pt1 </span><span class="syntaxkeyword">= </span><span class="syntaxdefault">e1</span><span class="syntaxkeyword">.</span><span class="syntaxdefault">other_vertex</span><span class="syntaxkeyword">( </span><span class="syntaxdefault">vertex </span><span class="syntaxkeyword">).</span><span class="syntaxdefault">position<br /> pt2 </span><span class="syntaxkeyword">= </span><span class="syntaxdefault">e2</span><span class="syntaxkeyword">.</span><span class="syntaxdefault">other_vertex</span><span class="syntaxkeyword">( </span><span class="syntaxdefault">vertex </span><span class="syntaxkeyword">).</span><span class="syntaxdefault">position<br /> v1 </span><span class="syntaxkeyword">= </span><span class="syntaxdefault">point</span><span class="syntaxkeyword">.</span><span class="syntaxdefault">vector_to</span><span class="syntaxkeyword">( </span><span class="syntaxdefault">pt1 </span><span class="syntaxkeyword">)<br /> </span><span class="syntaxdefault">v2 </span><span class="syntaxkeyword">= </span><span class="syntaxdefault">point</span><span class="syntaxkeyword">.</span><span class="syntaxdefault">vector_to</span><span class="syntaxkeyword">( </span><span class="syntaxdefault">pt2 </span><span class="syntaxkeyword">)<br /> </span><span class="syntaxdefault">angle </span><span class="syntaxkeyword">= </span><span class="syntaxdefault">v1</span><span class="syntaxkeyword">.</span><span class="syntaxdefault">angle_between</span><span class="syntaxkeyword">( </span><span class="syntaxdefault">v2 </span><span class="syntaxkeyword">)<br /> </span><span class="syntaxdefault">face_normal </span><span class="syntaxkeyword">= </span><span class="syntaxdefault">face</span><span class="syntaxkeyword">.</span><span class="syntaxdefault">normal<br /> face_normal</span><span class="syntaxkeyword">.</span><span class="syntaxdefault">length </span><span class="syntaxkeyword">= </span><span class="syntaxdefault">angle<br /> normal </span><span class="syntaxkeyword">+= </span><span class="syntaxdefault">face_normal<br /> end<br /> normal</span><span class="syntaxkeyword">.</span><span class="syntaxdefault">normalize</span><span class="syntaxkeyword">!<br /> </span><span class="syntaxdefault">end<br /></span>
-
Though, the linked article seem to also take into account the face area... wonder what importance that has...
-
hmm... it must be flawed. if a face is connected to the vertex then vector1.angle_between( vector2 ) won't give the correct angle, as it only return angles between 0 - 180 degrees. Either I need to deal with each triangle in each face, or I calculate the full angle.
-
Stupid typo is my code should be
norm ****+**** fnor
not '*********
'... I've corrected the original - it should now work as expected - weighting vertex-normals dependent on the face's areas! -
This seem to account for wide angle corners:
<span class="syntaxdefault"><br /> def self</span><span class="syntaxkeyword">.</span><span class="syntaxdefault">vertex_normal</span><span class="syntaxkeyword">( </span><span class="syntaxdefault">vertex </span><span class="syntaxkeyword">)<br /> </span><span class="syntaxdefault">faces </span><span class="syntaxkeyword">= </span><span class="syntaxdefault">vertex</span><span class="syntaxkeyword">.</span><span class="syntaxdefault">faces<br /> edges </span><span class="syntaxkeyword">= </span><span class="syntaxdefault">vertex</span><span class="syntaxkeyword">.</span><span class="syntaxdefault">edges<br /> point </span><span class="syntaxkeyword">= </span><span class="syntaxdefault">vertex</span><span class="syntaxkeyword">.</span><span class="syntaxdefault">position<br /> normal </span><span class="syntaxkeyword">= </span><span class="syntaxdefault">Geom</span><span class="syntaxkeyword">;;</span><span class="syntaxdefault">Vector3d</span><span class="syntaxkeyword">.new( </span><span class="syntaxdefault">0</span><span class="syntaxkeyword">, </span><span class="syntaxdefault">0</span><span class="syntaxkeyword">, </span><span class="syntaxdefault">0 </span><span class="syntaxkeyword">)<br /> </span><span class="syntaxdefault">until faces</span><span class="syntaxkeyword">.empty?<br /> </span><span class="syntaxdefault">face </span><span class="syntaxkeyword">= </span><span class="syntaxdefault">faces</span><span class="syntaxkeyword">.</span><span class="syntaxdefault">shift<br /> e1</span><span class="syntaxkeyword">, </span><span class="syntaxdefault">e2 </span><span class="syntaxkeyword">= </span><span class="syntaxdefault">face</span><span class="syntaxkeyword">.</span><span class="syntaxdefault">edges </span><span class="syntaxkeyword">& </span><span class="syntaxdefault">edges<br /> pt1 </span><span class="syntaxkeyword">= </span><span class="syntaxdefault">e1</span><span class="syntaxkeyword">.</span><span class="syntaxdefault">other_vertex</span><span class="syntaxkeyword">( </span><span class="syntaxdefault">vertex </span><span class="syntaxkeyword">).</span><span class="syntaxdefault">position<br /> pt2 </span><span class="syntaxkeyword">= </span><span class="syntaxdefault">e2</span><span class="syntaxkeyword">.</span><span class="syntaxdefault">other_vertex</span><span class="syntaxkeyword">( </span><span class="syntaxdefault">vertex </span><span class="syntaxkeyword">).</span><span class="syntaxdefault">position<br /> v1 </span><span class="syntaxkeyword">= </span><span class="syntaxdefault">point</span><span class="syntaxkeyword">.</span><span class="syntaxdefault">vector_to</span><span class="syntaxkeyword">( </span><span class="syntaxdefault">pt1 </span><span class="syntaxkeyword">)<br /> </span><span class="syntaxdefault">v2 </span><span class="syntaxkeyword">= </span><span class="syntaxdefault">point</span><span class="syntaxkeyword">.</span><span class="syntaxdefault">vector_to</span><span class="syntaxkeyword">( </span><span class="syntaxdefault">pt2 </span><span class="syntaxkeyword">)<br /> </span><span class="syntaxdefault">angle </span><span class="syntaxkeyword">= </span><span class="syntaxdefault">self</span><span class="syntaxkeyword">.</span><span class="syntaxdefault">full_angle_between</span><span class="syntaxkeyword">( </span><span class="syntaxdefault">v1</span><span class="syntaxkeyword">, </span><span class="syntaxdefault">v2</span><span class="syntaxkeyword">, </span><span class="syntaxdefault">face</span><span class="syntaxkeyword">.</span><span class="syntaxdefault">normal </span><span class="syntaxkeyword">)<br /> </span><span class="syntaxdefault">face_normal </span><span class="syntaxkeyword">= </span><span class="syntaxdefault">face</span><span class="syntaxkeyword">.</span><span class="syntaxdefault">normal<br /> face_normal</span><span class="syntaxkeyword">.</span><span class="syntaxdefault">length </span><span class="syntaxkeyword">= </span><span class="syntaxdefault">angle<br /> normal </span><span class="syntaxkeyword">+= </span><span class="syntaxdefault">face_normal<br /> end<br /> normal</span><span class="syntaxkeyword">.</span><span class="syntaxdefault">normalize</span><span class="syntaxkeyword">!<br /> </span><span class="syntaxdefault">end<br /> <br /> def self</span><span class="syntaxkeyword">.</span><span class="syntaxdefault">full_angle_between</span><span class="syntaxkeyword">( </span><span class="syntaxdefault">vector1</span><span class="syntaxkeyword">, </span><span class="syntaxdefault">vector2</span><span class="syntaxkeyword">, </span><span class="syntaxdefault">normal </span><span class="syntaxkeyword">)<br /> </span><span class="syntaxdefault">cross </span><span class="syntaxkeyword">= </span><span class="syntaxdefault">vector1 </span><span class="syntaxkeyword">* </span><span class="syntaxdefault">vector2<br /> direction </span><span class="syntaxkeyword">= </span><span class="syntaxdefault">cross </span><span class="syntaxkeyword">% </span><span class="syntaxdefault">normal<br /> angle </span><span class="syntaxkeyword">= </span><span class="syntaxdefault">vector1</span><span class="syntaxkeyword">.</span><span class="syntaxdefault">angle_between</span><span class="syntaxkeyword">( </span><span class="syntaxdefault">vector2 </span><span class="syntaxkeyword">)<br /> </span><span class="syntaxdefault">angle </span><span class="syntaxkeyword">= </span><span class="syntaxdefault">360.degrees </span><span class="syntaxkeyword">- </span><span class="syntaxdefault">angle </span><span class="syntaxkeyword">if </span><span class="syntaxdefault">direction </span><span class="syntaxkeyword">> </span><span class="syntaxdefault">0.0<br /> angle<br /> end<br /></span>
-
@tig said:
Stupid typo is my code should be
norm ****+**** fnorm
not '*********
'... I've corrected the original - it should now work as expected !!!!I'll revisit your code again. http://www.bytehazard.com/code/vertnorm.html mentions that using the area still makes some faces weigh too much, but that it's not that notable. But since it looks to require less computing that checking the angles then it might be preferable.
-
hm... I added face area to the equations, but that yielded deviating normals depending how I divided the large face.
removing the face area appear to yield the exact same result.
Weighted by angle:- Vector3d(0.419345, 0.0772543, 0.904534)
- Vector3d(0.419345, 0.0772543, 0.904534)
- Vector3d(0.419345, 0.0772543, 0.904534)
Weighted by area * angle:
- Vector3d(0.324298, 0.116468, 0.938758)
- Vector3d(0.706956, 0.253896, 0.660114)
- Vector3d(0.770689, 0.276785, 0.573959)
-
I wonder if the linked article uses the face area because they computer the normals for lighting...
-
Several coplanar faces at a vertex are the same as one face of the same area as the bits? So the both area-weight-adjusted vertex-normals will be the same.
I think it is more for lighting... -
@tig said:
Several coplanar faces at a vertex are the same as one face of the same area as the bits? So the both area-weight-adjusted vertex-normals will be the same.
But if a vertex is connected to three sides, one large and two small, then using the area would make the normal lean toward the large area, would it not?
-
Not if the two small areas were equivalent to the large one... then it'd be 'balanced'...
The large area pulls it over then the 1st small are pulls it back and then the 2nd small area pulls it back again.
IF there's a large and a small area the large one 'wins'. -
By computer you mean compute?
-
@voljanko said:
By computer you mean compute?
A simple typo by tt in the original title - but we understood what he meant...
Advertisement