Search Unity

Script-Generated Mesh is invisible at some angles and visible at others

Discussion in 'General Graphics' started by Hermanoid, Oct 13, 2019.

  1. Hermanoid

    Hermanoid

    Joined:
    Jun 2, 2016
    Posts:
    3
    Hello all,

    I'm trying to generate a tube structure around a waypoint line, comprised of an array of waypoints twisting and turning through space. The goal is to play a video of asteroids on the walls of this tube to, with the help of some actual asteroid models, create the effect of a complete asteroid field without having the render hundreds upon hundreds of asteroid models.
    I've done the linear algebra to find the points at each end of the tube cylinder, but when I draw in the triangles of the mesh, they turn out to be visible only from certain directions. Obviously, I need this tube to be completely visible all the time.

    Here is the excerpt from my code which actually spawns the mesh:
    Code (CSharp):
    1.             var mesh = new Mesh();
    2.             var verticies = new Vector3[numTubeVertices * 2]; //Two sets of verticies, one for each side of the tube.
    3.  
    4.             Vector3 ihat; //Vector algebra.  Ihat  points in the direction of "x"
    5.             Vector3 jhat; //same thing for the "y" direction.
    6.             //First circle
    7.             //My original code does some extra stuff here to handle bends in the tube, but I've cropped it out to keep things simple.
    8.                 ihat = segmentStuffs.rightVector;
    9.                 jhat = segmentStuffs.upVector;
    10.        
    11.             var step = Mathf.PI * 2 / numTubeVertices;
    12.             firstCircle = GizmosUtil.PointsOn3DArc(centerPoint, ihat, jhat, outerRadius, 0, Mathf.PI * 2 - step, step).ToArray(); //GizmosUtil is a custom helper utility.  I'm using it here to get a full circle of points.  I can certify that this works as I use the same method to generate a Gizmos Circle.
    13.             var secondCircle = GizmosUtil.PointsOn3DArc(newPoint, segmentStuffs.rightVector, segmentStuffs.upVector, outerRadius, 0, Mathf.PI * 2 -step, step).ToArray(); //upVector and rightVector are analogous to "ihat" and "jhat".  I hadn't learned about transformation matricies yet when I wrote this, but know that this hat business is basically doing the same thing as one of those.
    14.             mesh.vertices = firstCircle.Concat(secondCircle).ToArray();
    15.             var triangles = new int[numTubeVertices * 6]; //Connecting the edge verticies into a series of triangles takes twice the number of triangles as verticies (I drew this out), and this needs thrice that for the three points to every triangle. Hence, six.
    16.             for(int i = 0; i<numTubeVertices; i++)
    17.             {
    18.                 int startIndex = i * 6;
    19.                 triangles[startIndex + 3] = i; //Start with the iterated point (this will fall on first circle)
    20.                 triangles[startIndex + 4] = i + numTubeVertices; //Connect to same point on second circle;
    21.                 triangles[startIndex + 5] = i + numTubeVertices + 1; //Connect to next point over on second circle;
    22.  
    23. Here is a video I screenrecorded of the issue in Unity.  I used the exact code pasted above to generate the tube I'm showing.
    24.  
    25.                triangles[startIndex] = i; //Start again with iterated point
    26.                triangles[startIndex + 1] = i + 1; //Connect to next point over on first circle
    27.                triangles[startIndex + 2] = i + numTubeVertices + 1;//Connect to point corresponding to this next point, but on second circle
    28.                if (triangles[startIndex + 2] >= mesh.vertices.Length) triangles[startIndex + 2] -= numTubeVertices;
    29.                if (triangles[startIndex + 5] >= mesh.vertices.Length) triangles[startIndex + 5] -= numTubeVertices;
    30.            }
    31.            mesh.triangles = triangles;
    32.  
    33.            mesh.RecalculateNormals();
    34.  
    35.  
    36.            newTube.GetComponent<MeshFilter>().mesh = mesh;
    Just to explain a little:
    "centerPoint" and "newPoint" are the two points comprising the line segment I want to generate a cylindrical tube around. I call this function over and over again with different points to generate a series of tube segments along the waypoint line, so it can twist and turn as desired.
    These "Direction Vectors" I'm talking about just let me change some 2D numbers to 3D-rotated space. If you're trying to understand that part of the code (which I don't think will be needed) just imagine that they are <1,0,0> (for the x- or right-direction) and <0,1,0> (for the y- or up-direction)
    "GizmosUtil.PointsOn3DArc(...)" just get a circle with the desired number of points around it using the fancy direction vectors. This "3D arc" is just a normal 2D arc which has been rotated onto a 3D plane.

    OK, now for the actual problem: Rendering. Here is a video of the problem I screen recorded in Unity, using the exact code above to generate the tube:


    (You might notice my "Actualertube" models in the scene, they're artifacts from previous attempts at generating this tube)

    I've noticed a "culling" behavior similar to this before when importing a simple tube model from Blender. After a great deal of research, I can't seem to figure out the cause, or how to stop it. I've also tried to manually calculate the normals to be all pointing outward and then inward, but both of those attempts failed to change the occlusion.

    Has anyone seen this sort of thing before, and if so, how can I fix it?

    Thanks!
    Lucas N
     
    Last edited: Oct 15, 2019
  2. Hermanoid

    Hermanoid

    Joined:
    Jun 2, 2016
    Posts:
    3
    Turns out the fix isn't too bad, although finding it was terrible.
    Long story short, it was the shader's fault that my mesh was getting culled as it was. There's this fancy "Cull Off" function-type-thing that I'm not going to pretend I understand. What I can say is, I created a new Shader script, added this line that I had seen somewhere on the internet, magic happened.
    I duplicated the default "unlit shader" script in Unity (Just go to the folders tab, right-click, and navigate to Create>Shader>Unlit) and added the line "Cull Off" to a very particular part of the script. Suddenly, voila, it doesn't make half of my mesh disappear.

    Code (CSharp):
    1. Shader "Unlit/UnculledShader"
    2. {
    3.     Properties
    4.     {
    5.         _MainTex ("Texture", 2D) = "white" {}
    6.     }
    7.     SubShader
    8.     {
    9.         Tags { "RenderType"="Opaque" }
    10.         LOD 100
    11.  
    12.         Pass
    13.         {
    14.             Cull Off //** This is the only change I made
    15.  
    16.             CGPROGRAM
    17.             #pragma vertex vert
    18.             #pragma fragment frag
    19.             // make fog work
    20.             #pragma multi_compile_fog
    21.  
    22.             #include "UnityCG.cginc"
    23.  
    24.  
    25.             struct appdata
    26.             {
    27.                 float4 vertex : POSITION;
    28.                 float2 uv : TEXCOORD0;
    29.             };
    30.  
    31.             struct v2f
    32.             {
    33.                 float2 uv : TEXCOORD0;
    34.                 UNITY_FOG_COORDS(1)
    35.                 float4 vertex : SV_POSITION;
    36.             };
    37.  
    38.             sampler2D _MainTex;
    39.             float4 _MainTex_ST;
    40.  
    41.             v2f vert (appdata v)
    42.             {
    43.                 v2f o;
    44.                 o.vertex = UnityObjectToClipPos(v.vertex);
    45.                 o.uv = TRANSFORM_TEX(v.uv, _MainTex);
    46.                 UNITY_TRANSFER_FOG(o,o.vertex);
    47.                 return o;
    48.             }
    49.  
    50.             fixed4 frag (v2f i) : SV_Target
    51.             {
    52.                 // sample the texture
    53.                 fixed4 col = tex2D(_MainTex, i.uv);
    54.                 // apply fog
    55.                 UNITY_APPLY_FOG(i.fogCoord, col);
    56.                 return col;
    57.             }
    58.             ENDCG
    59.         }
    60.     }
    61. }
    62.  
     
  3. Shane_Michael

    Shane_Michael

    Joined:
    Jul 8, 2013
    Posts:
    158
    This is an issue with your winding order (i.e. the order in which you specify the vertex indices in the triangle array). One side of the triangle will be the clockwise-side, and one will be the counter-clockwise-side. Unity uses the clockwise side as the front though it's arbitrary which is which so conventions can vary between platforms, graphics APIs, programs, etc.

    It's usually best to keep back culling on because it's a pretty efficient way to cull triangles you aren't going to be able to see very early.
     
    Marco-Playable likes this.
  4. David2022

    David2022

    Joined:
    Sep 8, 2019
    Posts:
    1
    And dont forget to call RecalculateBounds on your scripted mesh. Otherwise the whole mesh will be culled at specific angles.
     
    Marco-Playable and slushieboy99 like this.