Wayback Machinekoobas.hobune.stream
May JUN Jul
Previous capture 13 Next capture
2021 2022 2023
2 captures
13 Jun 22 - 14 Jun 22
sparklines
Close Help
  • Products
  • Solutions
  • Made with Unity
  • Learning
  • Support & Services
  • Community
  • Asset Store
  • Get Unity

UNITY ACCOUNT

You need a Unity Account to shop in the Online and Asset Stores, participate in the Unity Community and manage your license portfolio. Login Create account
  • Blog
  • Forums
  • Answers
  • Evangelists
  • User Groups
  • Beta Program
  • Advisory Panel

Navigation

  • Home
  • Products
  • Solutions
  • Made with Unity
  • Learning
  • Support & Services
  • Community
    • Blog
    • Forums
    • Answers
    • Evangelists
    • User Groups
    • Beta Program
    • Advisory Panel

Unity account

You need a Unity Account to shop in the Online and Asset Stores, participate in the Unity Community and manage your license portfolio. Login Create account

Language

  • Chinese
  • Spanish
  • Japanese
  • Korean
  • Portuguese
  • Ask a question
  • Spaces
    • Default
    • Help Room
    • META
    • Moderators
    • Topics
    • Questions
    • Users
    • Badges
  • Home /
avatar image
7
Question by Kilometers · Nov 20, 2011 at 11:03 AM · meshnormalsprocedural meshbad

Procedural mesh's normals are reversed (Solved)

So I am trying to generate a procedural mesh, but I am encountering problems with the normals.

Where's the cream filling!?

I find this very strange, since the mesh itself seems to be of the right shape. Here is my attempt to explain the code, if anyone ends up wanting to help me:

  1. Based on a variable called "mapsize", I populate an array, blobVerts[], with the position of each vertex. If mapsize is set to 10, then the result is a 10x10 units mesh. (Note that mapverts is just mapsize + 1, because there's one more vertex per row than there are squares)

  2. On alternating rows, the x-value is shifted over by 0.5 units. This gets the zigzag pattern

  3. The triangles are generated in batches of two, with six indices handled in each batch. The "CurrentRow" switch statement is used to alternate the pattern of the batch. Within each batch, the six indices are calculated based on a pivotal vertex. this central vertex is held by the "g" variable (Which is actually being reused from the earlier Vertex section... ugh).

That's about all I can think of to explain it for now. If anyone has questions, such as "Jesus dude, where did you learn to program? The Internet!?", please ask. So here's the code:

         blobVerts = new BlobVertex[mapverts * mapverts];
         blobVertsArray = new Vector3[mapverts * mapverts];
         blobUV = new Vector2[mapverts * mapverts];
         blobTriangles = new int[(mapsize * mapsize * 6)];
 
         //Populate the blobVerts class array
         for (int i = 0; i < blobVerts.Length; i++)
         {
             blobVerts[i] = new BlobVertex();
         }
 
 
 
 
 
 
         //VERTICES
         int g = 0;
         int row = 3;
         //Set the vertices to fill the map grid
         for (int i = 0; i < mapverts; i++)
         {
 
             for (int h = 0; h < mapverts; h++)
             {
 
                 blobVerts[g].position = new Vector3(h - ((row % 2) * .5f), 0, i);
 
                 g = g + 1;
             }
             row++; 
         }
 
         //Transfers positions from blobVerts[].position to blobVertsArray
         UpdateBlobVertsArray();
         //END VERTICES
 
 
 
 
 
 
 
         //UV
         Debug.Log("blobUV.Length: " + blobUV.Length);
         // Set the UV verts
         for (int i = 0; i < blobUV.Length; i++)
         {
                 //Debug.Log("i = " i);
                 blobUV[i].x = 0;
                 blobUV[i].y = 0;
         }
         //END UV
 
 
 
 
 
 
         //TRIANGLES
 
         // Set the triangles
         // Interger "h" corresponds with the batch of triangles (form a square)
         // Interger "g" is reused as a holder for the real starting vertex in each batch
         g = 0;
         row = 2;
 
         for (int h = 0; h < (mapsize * mapsize); h++)
         {
             //this is my attempt at skipping the last vertex in each row, since it does not have a square
             if ((g + 1) >= mapverts && (g + 1) % mapverts == 0)
                  g++;
 
             //Cycle through the six vertices for a pair of triangles (parallelogram)
             for (int i = 0; i < 6; i++)
             {
                 int triIndex = (h * 6) + i;
 
                 switch((CurrentRow(g) + 2) % 2)
                 {
                     case 0:
                         switch (i)
                         {
                             case 5:
                                 blobTriangles[triIndex] = g + mapsize + 2;
                                 break;
                             case 4:
                                 blobTriangles[triIndex] = g + mapsize + 1;
                                 break;
                             case 3:
                                 blobTriangles[triIndex] = g + 1;
                                 break;
                             case 2:
                                 blobTriangles[triIndex] = g + mapsize + 1;
                                 break;
                             case 1:
                                 blobTriangles[triIndex] = g + 1;
                                 break;
                             case 0:
                                 blobTriangles[triIndex] = g;
                                 break;
                         }
                         break;
                     
                     case 1:
                         switch (i)
                         {
                             
                             case 5:
                                 blobTriangles[triIndex] = g + mapsize + 2;
                                 break;
                             case 4:
                                 blobTriangles[triIndex] = g + 1;
                                 break;
                             case 3:
                                 blobTriangles[triIndex] = g;
                                 break;
                             case 2:
                                 blobTriangles[triIndex] = g + mapsize + 2;
                                 break;
                             case 1:
                                 blobTriangles[triIndex] = g + mapsize + 1;
                                 break;
                             case 0:
                                 blobTriangles[triIndex] = g;
                                 break;
                         }
                         break;
                 }
             }
             
                 g++;
                 row++;
                 
         }
         //END TRIANGLES



EDIT: Here's a few more images to demonstrate what is happening.

With a texture (viewed from the bottom) A a-a-a a-al co hol, baby

Comment
Add comment · Show 12
10 |3000 characters needed characters left characters exceeded
▼
  • Viewable by all users
  • Viewable by moderators
  • Viewable by moderators and the original poster
  • Advanced visibility
Viewable by all users
avatar image CHPedersen · Nov 20, 2011 at 11:07 AM 0
Share

Err... Here's something that might be a silly question since I might've just missed it, but... Where are you actually setting normals? I don't see you creating a Vector3[] with normal vectors in it anywhere, and I don't see you assign anything to the mesh either?

avatar image Kilometers · Nov 20, 2011 at 11:11 AM 0
Share

Well this is enlightening. I hadn't seen any mention of setting normals. Should I avoid the "RecalculateNormals()" function then and go for something more manual? Do you have a link to documentation on this? Thanks for the reply. :D

avatar image Kilometers · Nov 20, 2011 at 11:16 AM 0
Share

The answer to my issue may lie in this post: http://answers.unity3d.com/questions/32100/how-do-i-manually-calculate-normals-for-my-meshes.html

:P I'll let you folks know if it works for me.

avatar image CHPedersen · Nov 20, 2011 at 11:24 AM 0
Share

I'll readily admit to being a little confused here, myself. I usually create normals manually, but the ReCalculateNormals method should assign an array of normals for you, as well. Looking at the picture above, I don't understand why part of the mesh doesn't have its wireframe showing. Is that because the normals point the wrong way in that area? What does this look like if you assign a material?

avatar image Kilometers · Nov 20, 2011 at 11:34 AM 0
Share

I've added an image of it with a texture. It acts the way it should, considering the normals. Perhaps I should just get a shader without Culling enabled, but I've heard that the "reverse" side often has lighting issues.

Show more comments

2 Replies

· Add your reply
  • Sort: 
avatar image
15
Best Answer

Answer by aldonaletto · Nov 20, 2011 at 02:03 PM

It seems your problem is already solved, but just to let you know about this triangle winding thing, here goes a drawing:

alt text

The winding order informs which's the front side: if the vertices are in clockwise order, you're looking to the front face; if in counter-clockwise order, it's the back face and you will see nothing (unless the shader is double sided). In the drawing above, triangle 1 must be composed by vertices [1,2,4] (or [2,4,1] or [4,1,2]), and triangle 2 by vertices [2,3,4] (or [3,4,2] or [4,2,3]). Since vertices 2 and 4 in this drawing are common to both triangles, usually we calculate their indices and store in two variables, then assign the six vertices based on these variables:

    int v2 = ...; // calculate index of vertex 2
    int v4 = ...; // calculate index of vertex 4
    verts[i++] = v2-1;
    verts[i++] = v2;
    verts[i++] = v4;
    verts[i++] = v2;
    verts[i++] = v2+1;
    verts[i++] = v4;
NOTE: The pseudo code above is based on the vertices 1..4 showed in the drawing above; it must be changed if the vertices are stored in a different order.
Comment
Add comment · Show 1 · Share
10 |3000 characters needed characters left characters exceeded
▼
  • Viewable by all users
  • Viewable by moderators
  • Viewable by moderators and the original poster
  • Advanced visibility
Viewable by all users
avatar image Kilometers · Nov 24, 2011 at 11:09 PM 0
Share

Thanks for the in depth explanation. Hopefully this will prove to be helpful to the next person who finds this.

avatar image
5

Answer by Kilometers · Nov 20, 2011 at 01:27 PM

I narrowed it down to two things. First was the blob.Optimize() function. When I comment it out of the code, I get a correct-looking mesh:

alt text

However, this only got me halfway there. When I added a texture, things got interesting again: alt text

So I decided to start messing around with the triangle batches. By switching the order in which the vertices where added to the blobTriangle array, I finally got it to work: alt text



Thanks for the help, Christian. I'm still not sure exactly why this worked. There seems to be something about the order in which the vertices are assigned to triangles that affects their (and the surrounding triangles') face normals. Really weird.

EDIT: blobOptimize() was not part of the cause. It was just covering up the problem with the triangles. Also, Christian pointed out that when assigning vertices to the triangles, the order of assignment (clockwise vs counter-clockwise) affects the orientation of the mesh. That was my problem.

Comment
Add comment · Show 1 · Share
10 |3000 characters needed characters left characters exceeded
▼
  • Viewable by all users
  • Viewable by moderators
  • Viewable by moderators and the original poster
  • Advanced visibility
Viewable by all users
avatar image CHPedersen · Nov 21, 2011 at 07:36 AM 2
Share

I'm +1'ing your question and answer just because you've been great at providing additional information and demonstrative images. :) I wish everyone took their own questions this seriously.

If you're interested in more background information on winding order and clockwise/counterclockwise face culling, I think one of the most exhaustive and well-known resources is the OpenGL Program$$anonymous$$g Guide, the so called "Red Book". It is available as a PDF here:

http://paginas.fe.up.pt/~jbarbosa/ensino/SG/2005-2006/OpenGL%20Program$$anonymous$$g%20Guide.pdf

That page lets you save a copy as well. Do a search for the term "clockwise". It will lead you to relevant parts of the book that explain this concept. In summary, it has to do with the OpenGL convention, that vertices which appear in counterclockwise order in a polygon on the screen are considered front-facing. If they appear in clockwise order, the rendering system considers that polygon to be back-facing, and culls it (causing those gaps you've seen). Unity's mesh class and its triangle array is essentially a wrapper around this same functionality; it draws polygons in the order you declare their vertices in. If some are counterclockwise while others are clockwise, you'll get triangles culled in unexpected ways.

Your answer

Hint: You can notify a user about this post by typing @username

Up to 2 attachments (including images) can be used with a maximum of 524.3 kB each and 1.0 MB total.

Follow this Question

Answers Answers and Comments

4 People are following this question.

avatar image avatar image avatar image avatar image

Related Questions

Custom Mesh Normals Problem 1 Answer

How do I combine normals on procedural mesh with multiple tiles? 2 Answers

Are normals from Mesh.RecalculateNormals() normalized? 1 Answer

Unity Mesh Rendering Issue 0 Answers

Holes in procedural mesh 0 Answers


Enterprise
Social Q&A

Social
Subscribe on YouTube social-youtube Follow on LinkedIn social-linkedin Follow on Twitter social-twitter Follow on Facebook social-facebook Follow on Instagram social-instagram

Footer

  • Purchase
    • Products
    • Subscription
    • Asset Store
    • Unity Gear
    • Resellers
  • Education
    • Students
    • Educators
    • Certification
    • Learn
    • Center of Excellence
  • Download
    • Unity
    • Beta Program
  • Unity Labs
    • Labs
    • Publications
  • Resources
    • Learn platform
    • Community
    • Documentation
    • Unity QA
    • FAQ
    • Services Status
    • Connect
  • About Unity
    • About Us
    • Blog
    • Events
    • Careers
    • Contact
    • Press
    • Partners
    • Affiliates
    • Security
Copyright © 2020 Unity Technologies
  • Legal
  • Privacy Policy
  • Cookies
  • Do Not Sell My Personal Information
  • Cookies Settings
"Unity", Unity logos, and other Unity trademarks are trademarks or registered trademarks of Unity Technologies or its affiliates in the U.S. and elsewhere (more info here). Other names or brands are trademarks of their respective owners.
  • Anonymous
  • Sign in
  • Create
  • Ask a question
  • Spaces
  • Default
  • Help Room
  • META
  • Moderators
  • Explore
  • Topics
  • Questions
  • Users
  • Badges