• Advertisement
Sign in to follow this  

Calculating smooth normales

This topic is 1238 days old which is more than the 365 day threshold we allow for new replies. Please post a new topic.

If you intended to correct an error in the post then please contact us.

Recommended Posts

Hello. I want to calculate smooth normals for any mesh and for this I do the following calculations:

Vector3[] normals = new Vector3[geometryData.Positions.Count];

            for (int i = 0; i < geometryData.IndexBuffer.Count; i+=3)
            {
                Vector3 v0 = geometryData.Positions[geometryData.IndexBuffer[i + 1]].Position - geometryData.Positions[geometryData.IndexBuffer[i]].Position;
                Vector3 v1 = geometryData.Positions[geometryData.IndexBuffer[i]].Position - geometryData.Positions[geometryData.IndexBuffer[i + 2]].Position;
                
                Vector3 normal = Vector3.Normalize(Vector3.Cross(v0, v1));
                
                normals[geometryData.IndexBuffer[i]] += normal;
                normals[geometryData.IndexBuffer[i + 1]] += normal;
                normals[geometryData.IndexBuffer[i + 2]] += normal;
                
            }

            for (int i = 0; i < normals.Length; i++)
            {
                normals[i].Normalize();
            }

Then I compare calculated normals to pre-calculated by 3ds max and found that some of my normals has opposite sign on some axis, also sometimes values are not the same.

 

Does someone could say me what the prblem here? I think I should set correct sign somehow, but I dont know whow exactly.

Edited by BlackJoker

Share this post


Link to post
Share on other sites
Advertisement

Assuming your vertex positions are in correct winding order, the vectors you cross should be p1-p0, and p2-p0. You have p1-p0 (good) and p0-p2 (bad). That last change of sign is probably what's messing you up.

 

EDIT: You're calculating a simple average of normals, without weighting. Do you know that's what 3ds does?

Edited by Buckeye

Share this post


Link to post
Share on other sites

Assuming your vertex positions are in correct winding order, the vectors you cross should be p1-p0, and p2-p0. You have p1-p0 (good) and p0-p2 (bad). That last change of sign is probably what's messing you up.

 

EDIT: You're calculating a simple average of normals, without weighting. Do you know that's what 3ds does?

Thanks for hint. Didn`t notice that. Now seems sign is correct, but not always values are exactly the same. For ex:

my normal -                                      X:0,1006071 Y:0,9944534 Z:0,03066822

pre-calculated normal from 3ds max - X:0,091591   Y:0,995782   Z:0,005412

 

I think that can be related to the weighted normals - its what you write me, but I don`t know how to calculate weighted normals.

Edited by BlackJoker

Share this post


Link to post
Share on other sites

I don`t know how to calculate weighted normals.

 

One way (I'm sure there are others) is to average the un-normalized crossproducts before normalizing the result. The crossproduct is the area of the triangle and, therefore, weights the normals proportional to the areas of the triangles that border it. I.e., they will "lean" toward the larger triangles. If your posted method (revised as suggested) doesn't look right to you, you might try that. BTW, I'm not familiar with 3ds so can't help you with regard to what/why 3ds does.

Edited by Buckeye

Share this post


Link to post
Share on other sites

One way (I'm sure there are others) is to average the un-normalized crossproducts before normalizing the result. The crossproduct is the area of the triangle and, therefore, weights the normals proportional to the areas of the triangles that border it. I.e., they will "lean" toward the larger triangles. If your posted method (revised as suggested) doesn't look right to you, you might try that. BTW, I'm not familiar with 3ds so can't help you with regard to what/why 3ds does.

 

Do you mean something like this:

Vector3[] normals = new Vector3[geometryData.Positions.Count];
            Int32[] count = new Int32[geometryData.Positions.Count];
            for (int i = 0; i < geometryData.IndexBuffer.Count; i+=3)
            {
                Vector3 v0 = geometryData.Positions[geometryData.IndexBuffer[i + 1]].Position - geometryData.Positions[geometryData.IndexBuffer[i]].Position;
                Vector3 v1 = geometryData.Positions[geometryData.IndexBuffer[i+2]].Position - geometryData.Positions[geometryData.IndexBuffer[i]].Position;
                
                Vector3 normal = Vector3.Cross(v0, v1);

                count[geometryData.IndexBuffer[i]] ++;
                count[geometryData.IndexBuffer[i+1]]++;
                count[geometryData.IndexBuffer[i+2]]++;

                normals[geometryData.IndexBuffer[i]] += normal;
                normals[geometryData.IndexBuffer[i + 1]] += normal;
                normals[geometryData.IndexBuffer[i + 2]] += normal;
                
            }

            for (int i = 0; i < normals.Length; i++)
            {
                normals[i] = Vector3.Normalize(normals[i]/count[i]);
            }

I create additional array to store count of normal for each index. Then I divide each normal on that count and after that normalize normal only once. Is that what you mean?

Edited by BlackJoker

Share this post


Link to post
Share on other sites

after that normalize normal only once. Is that what you mean?

 

Yes. Does that provide results closer to desired?

 

EDIT: Just FYI - I did a bit of googling (which you can do also) and found a large variety of weighting techniques. There is (as mentioned above) face-area weighting. Also mentioned is:

 

Vertex angle weighting: the normal is weighted by the subtended angle for each vertex. This would be done for each vertex in the face. As the cross-product is proportional to the sine of the subtended angle, I assume the weighting factor would be something like asin( v / |v| ) / pi, where v is the cross-product.

 

The link provided discusses others.

Edited by Buckeye

Share this post


Link to post
Share on other sites


Yes. Does that provide results closer to desired?

 

Results are not too much close that without weights. I was excpected to have a little bit closer results.

My results:

X:-0,9247413 Y:-0,3805353 Z:0 Reference normal
X:-0,936403  Y:-0,3509266 Z:1,676234E-06 with weigths
X:-0,9364029 Y:-0,3509266 Z:1,676234E-06 wthout weights

So, we have 0.01 on X, 0.03 on Y and we can think that Z = 0 here.

 

 

P.S. Could I ask you also about tangent space calculation?

Share this post


Link to post
Share on other sites


Results are not too much close that without weights.

 

You may not be calculating the weighted normals correctly, OR it may be that your triangles all have the same area.

 


Could I ask you also about tangent space calculation?

 

I'm not personally good with those calcs. However, this is a public forum (not a conversation between you and I wink.png ) and you should post any questions pertinent to the topic. Others here on gamedev may well be able to answer your questions.

Share this post


Link to post
Share on other sites
Sign in to follow this  

  • Advertisement