Search Unity

  1. Welcome to the Unity Forums! Please take the time to read our Code of Conduct to familiarize yourself with the forum rules and how to post constructively.
  2. We have updated the language to the Editor Terms based on feedback from our employees and community. Learn more.
    Dismiss Notice

Resolved Mesh from compute shader always uspide-down

Discussion in 'Editor & General Support' started by ripefruits22, Mar 18, 2023.

  1. ripefruits22

    ripefruits22

    Joined:
    Nov 26, 2021
    Posts:
    17
    Hi, I'm trying to make a mesh from a compute shader using a marching squares algorithm, the mesh is being used in a 2D game so has to have some extra triangles as walls for 3D colliders to work. However, the mesh always seems to end up flipped upside-down, if I make a change at the top of the 2D float array that determines the map, it always effects the bottom. I have tried to change the way that the compute shader reads the threads y position, so that it reads the float array upside-down, but the mesh is still upside-down despite that.

    The code is still WIP and messy but here is the compute shader:
    Code (CSharp):
    1. // Each #kernel tells which function to compile; you can have many kernels
    2. #pragma kernel CSMain
    3.  
    4. #define TOPLEFT float3(pos.x, pos.y, z1)
    5. #define TOPRIGHT float3(pos.x + 1, pos.y, z1)
    6. #define BOTTOMLEFT float3(pos.x, pos.y + 1, z1)
    7. #define BOTTOMRIGHT float3(pos.x + 1, pos.y + 1, z1)
    8.  
    9. #define TOPPOINT float3(tPoint, z1)
    10. #define RIGHTPOINT float3(rPoint, z1)
    11. #define BOTTOMPOINT float3(bPoint, z1)
    12. #define LEFTPOINT float3(lPoint, z1)
    13.  
    14. #define ALT_TOPPOINT float3(tPoint, z2)
    15. #define ALT_RIGHTPOINT float3(rPoint, z2)
    16. #define ALT_BOTTOMPOINT float3(bPoint, z2)
    17. #define ALT_LEFTPOINT float3(lPoint, z2)
    18.  
    19. int worldSize;
    20. float worldScale;
    21. float threshold;
    22. bool interpolate;
    23. bool plateau;
    24.  
    25. RWStructuredBuffer<float> mapData;
    26. RWStructuredBuffer<float3> vertices;
    27.  
    28. [numthreads(16, 16, 1)]
    29. void CSMain(uint3 dispatchThreadId : SV_DispatchThreadID, uint3 groupThreadId : SV_GroupThreadID, uint3 groupId : SV_GroupID, uint groupIndex : SV_GroupIndex)
    30. {
    31.     // Compute the position of the current thread in the two-dimensional array
    32.     // int2 pos = int2(dispatchThreadId.x, worldSize - 1 - dispatchThreadId.y); // This doesn't work either
    33.     int2 pos = dispatchThreadId.xy;
    34.  
    35.     // Check to see if the thread ID is out of the array bounds
    36.     if (pos.x > worldSize - 2 || pos.x < 0 || pos.y > worldSize - 2 || pos.y < 0) {
    37.         return;
    38.     }
    39.  
    40.     // Determine the values of the four corners of the current square
    41.     float tl = mapData[pos.x + pos.y * worldSize];
    42.     float tr = mapData[pos.x + 1 + pos.y * worldSize];
    43.     float bl = mapData[pos.x + (pos.y + 1) * worldSize];
    44.     float br = mapData[pos.x + 1 + (pos.y + 1) * worldSize];
    45.  
    46.     // Determine the configuration of the current square
    47.     uint config = 0;
    48.     if (tl > threshold) config |= 1;
    49.     if (tr > threshold) config |= 2;
    50.     if (br > threshold) config |= 4;
    51.     if (bl > threshold) config |= 8;
    52.  
    53.     // Compute the intersection points of the square edges with the isovalue
    54.     float2 tPoint;
    55.     float2 rPoint;
    56.     float2 bPoint;
    57.     float2 lPoint;
    58.     if (interpolate) {
    59.         tPoint = lerp(float2(pos.x, pos.y), float2(pos.x + 1, pos.y), (threshold - tl) / (tr - tl));
    60.         rPoint = lerp(float2(pos.x + 1, pos.y), float2(pos.x + 1, pos.y + 1), (threshold - tr) / (br - tr));
    61.         bPoint = lerp(float2(pos.x, pos.y + 1), float2(pos.x + 1, pos.y + 1), (threshold - bl) / (br - bl));
    62.         lPoint = lerp(float2(pos.x, pos.y), float2(pos.x, pos.y + 1), (threshold - tl) / (bl - tl));
    63.     }
    64.     else {
    65.         tPoint = float2(pos.x + 0.5f, pos.y);
    66.         rPoint = float2(pos.x + 1.0f, pos.y + 0.5f);
    67.         bPoint = float2(pos.x + 0.5f, pos.y + 1.0f);
    68.         lPoint = float2(pos.x, pos.y + 0.5f);
    69.     }
    70.  
    71.     // Compute the index of where we should write to in the vertex buffer
    72.     int index = (pos.x + pos.y * worldSize) * 18;
    73.  
    74.     float z1;
    75.     float z2;
    76.  
    77.     // Construct the triangles
    78.     if (plateau) {
    79.         z1 = -1.0f;
    80.         z2 = 1.0f;
    81.         switch (config) {
    82.         case 1:
    83.             vertices[index] = TOPLEFT;
    84.             vertices[index + 1] = LEFTPOINT;
    85.             vertices[index + 2] = TOPPOINT;
    86.  
    87.             vertices[index + 3] = TOPPOINT;
    88.             vertices[index + 4] = LEFTPOINT;
    89.             vertices[index + 5] = ALT_TOPPOINT;
    90.  
    91.             vertices[index + 6] = LEFTPOINT;
    92.             vertices[index + 7] = ALT_LEFTPOINT;
    93.             vertices[index + 8] = ALT_TOPPOINT;
    94.  
    95.             break;
    96.         case 2:
    97.             vertices[index] = TOPRIGHT;
    98.             vertices[index + 1] = TOPPOINT;
    99.             vertices[index + 2] = RIGHTPOINT;
    100.  
    101.             vertices[index + 3] = RIGHTPOINT;
    102.             vertices[index + 4] = TOPPOINT;
    103.             vertices[index + 5] = ALT_RIGHTPOINT;
    104.  
    105.             vertices[index + 6] = TOPPOINT;
    106.             vertices[index + 7] = ALT_TOPPOINT;
    107.             vertices[index + 8] = ALT_RIGHTPOINT;
    108.  
    109.             break;
    110.         case 3:
    111.             break;
    112.         case 4:
    113.             vertices[index] = BOTTOMRIGHT;
    114.             vertices[index + 1] = RIGHTPOINT;
    115.             vertices[index + 2] = BOTTOMPOINT;
    116.  
    117.             vertices[index + 3] = BOTTOMPOINT;
    118.             vertices[index + 4] = RIGHTPOINT;
    119.             vertices[index + 5] = ALT_BOTTOMPOINT;
    120.  
    121.             vertices[index + 6] = RIGHTPOINT;
    122.             vertices[index + 7] = ALT_RIGHTPOINT;
    123.             vertices[index + 8] = ALT_BOTTOMPOINT;
    124.  
    125.             break;
    126.         case 5:
    127.             break;
    128.         case 6:
    129.             break;
    130.         case 7:
    131.             break;
    132.         case 8:
    133.            vertices[index] = BOTTOMLEFT;
    134.            vertices[index + 1] = BOTTOMPOINT;
    135.            vertices[index + 2] = LEFTPOINT;
    136.  
    137.            vertices[index + 3] = LEFTPOINT;
    138.            vertices[index + 4] = BOTTOMPOINT;
    139.            vertices[index + 5] = ALT_LEFTPOINT;
    140.  
    141.            vertices[index + 6] = BOTTOMPOINT;
    142.            vertices[index + 7] = ALT_BOTTOMPOINT;
    143.            vertices[index + 8] = ALT_LEFTPOINT;
    144.  
    145.             break;
    146.         case 9:
    147.             break;
    148.         case 10:
    149.             break;
    150.         case 11:
    151.             break;
    152.         case 12:
    153.             break;
    154.         case 13:
    155.             break;
    156.         case 14:
    157.             break;
    158.         case 15:
    159.             break;
    160.         default:
    161.             break;
    162.         }
    163.     }
    164.     else {
    165.         z1 = 1.0f;
    166.         z2 = -1.0f;
    167.         switch (config) {
    168.         case 1:
    169.             break;
    170.         case 2:
    171.             break;
    172.         case 3:
    173.             break;
    174.         case 4:
    175.             break;
    176.         case 5:
    177.             break;
    178.         case 6:
    179.             break;
    180.         case 7:
    181.             break;
    182.         case 8:
    183.             break;
    184.         case 9:
    185.             break;
    186.         case 10:
    187.             break;
    188.         case 11:
    189.             break;
    190.         case 12:
    191.             break;
    192.         case 13:
    193.             break;
    194.         case 14:
    195.             break;
    196.         case 15:
    197.             break;
    198.         default:
    199.             break;
    200.         }
    201.     }
    202. }
    203.  
    204.  
    And here is the mesh generation script:
    Code (CSharp):
    1. using UnityEngine;
    2.  
    3. public class CavesFloor : Floor
    4. {
    5.  
    6.     ComputeShader marchingSquaresCS;
    7.  
    8.     ComputeBuffer mapInfo;
    9.     ComputeBuffer vertexInfo;
    10.  
    11.     private Vector2Int worldSize = new Vector2Int(4, 4);
    12.     private float worldScale = 1.0f;
    13.     private float threshold = 0.5f;
    14.     private bool interpolate = true;
    15.     private bool plateau = true;
    16.  
    17.     public CavesFloor(ComputeShader computeShader)
    18.     {
    19.         marchingSquaresCS = computeShader;
    20.  
    21.         //worldSize = new Vector2Int(3, 3);
    22.         //worldScale = 1.0f;
    23.         //threshold = 0.5f;
    24.         //interpolate = false;
    25.         //plateau = false;
    26.  
    27.         meshData = new MeshData(0);
    28.         mesh = new Mesh();
    29.     }
    30.  
    31.     public override void GenerateMesh()
    32.     {
    33.         // Generate the buffers
    34.         mapInfo = new ComputeBuffer(16, sizeof(float));
    35.         vertexInfo = new ComputeBuffer(mapInfo.count * 18, sizeof(float) * 3);
    36.  
    37.         // Create a test map of floats
    38.         float[] mapData = new float[16] {
    39.             1.0f, 0.25f, 0.0f, 1.0f, // If interpolate is true, this will make the bottom left triangle
    40.  // change as you would expect the top left triangle to change, see the attached picture.
    41.             0.0f, 0.0f, 0.0f, 0.0f,
    42.             0.0f, 0.0f, 0.0f, 0.0f,
    43.             1.0f, 0.0f, 0.0f, 1.0f
    44.         };
    45.         mapInfo.SetData(mapData);
    46.  
    47.         // Set values in the compute shader
    48.         marchingSquaresCS.SetInt("worldSize", worldSize.x);
    49.         marchingSquaresCS.SetFloat("worldScale", worldScale);
    50.         marchingSquaresCS.SetFloat("threshold", threshold);
    51.         marchingSquaresCS.SetBool("interpolate", interpolate);
    52.         marchingSquaresCS.SetBool("plateau", plateau);
    53.  
    54.         marchingSquaresCS.SetBuffer(0, "mapData", mapInfo);
    55.         marchingSquaresCS.SetBuffer(0, "vertices", vertexInfo);
    56.  
    57.         marchingSquaresCS.Dispatch(0, 1, 1, 1);
    58.  
    59.         // Retrieve the vertex data from the compute shader
    60.         Vector3[] vertices = new Vector3[vertexInfo.count];
    61.         vertexInfo.GetData(vertices);
    62.  
    63.         // Store the created triangles in a list of vertices and indices in the MeshData struct
    64.         int index = 0;
    65.         for (int i = 0; i < vertexInfo.count; i++)
    66.         {
    67.             Vector3 v = vertices[i];
    68.             if(v.z != 0.0f)
    69.             {
    70.                 meshData.vertices.Add(v);
    71.                 meshData.indices.Add(index);
    72.                 index++;
    73.             }
    74.         }
    75.  
    76.         // Set the mesh data
    77.         mesh.vertices = meshData.vertices.ToArray();
    78.         mesh.triangles = meshData.indices.ToArray();
    79.  
    80.         // Release the buffers
    81.         mapInfo.Release();
    82.         vertexInfo.Release();
    83.     }
    84. }
    85.  
    The only way I've found to fix the problem is to flip the game object in the y direction, but that isn't a very good fix as the game object will generate other types of terrains, and those terrains would be flipped instead. Any help on the matter would be greatly appreciated.
     

    Attached Files:

  2. ripefruits22

    ripefruits22

    Joined:
    Nov 26, 2021
    Posts:
    17
    I managed to fix the issue by flipping the way that the compute shader reads the float array, and the way it determines the values of the four corners, here is the changed part:
    Code (CSharp):
    1. // Compute the position of the current thread in the two-dimensional array
    2.     int2 samplePos = int2(dispatchThreadId.x, worldSizeY - 1 - dispatchThreadId.y);
    3.     int2 pos = dispatchThreadId.xy;
    4.  
    5.     // Check to see if the thread ID is out of the array bounds
    6.     if (pos.x > worldSizeX - 2 || pos.x < 0 || pos.y > worldSizeY - 2 || pos.y < 0) {
    7.         return;
    8.     }
    9.  
    10.     // Determine the values of the four corners of the current square
    11.     float tl = mapData[samplePos.x + samplePos.y * worldSizeX];
    12.     float tr = mapData[samplePos.x + 1 + samplePos.y * worldSizeX];
    13.     float bl = mapData[samplePos.x + (samplePos.y - 1) * worldSizeX];
    14.     float br = mapData[samplePos.x + 1 + (samplePos.y - 1) * worldSizeX];