Search Unity

  1. Unity Asset Manager is now available in public beta. Try it out now and join the conversation here in the forums.
    Dismiss Notice
  2. Unity 6 Preview is now available. To find out what's new, have a look at our Unity 6 Preview blog post.
    Dismiss Notice
  3. Unity is excited to announce that we will be collaborating with TheXPlace for a summer game jam from June 13 - June 19. Learn more.
    Dismiss Notice

Official Feedback Wanted: Mesh Compute Shader access

Discussion in 'Graphics Experimental Previews' started by Aras, Apr 20, 2021.

  1. Przemyslaw_Zaworski

    Przemyslaw_Zaworski

    Joined:
    Jun 9, 2017
    Posts:
    329
    https://docs.unity3d.com/ScriptReference/Physics.ClosestPoint.html

    Code (CSharp):
    1. The collider can only be BoxCollider, SphereCollider, CapsuleCollider or a convex MeshCollider.
    Naive solution for non-convex colliders. Suggested improvement: use parallel reduction to find minimum distance.

    Code (CSharp):
    1. using UnityEngine;
    2. using UnityEngine.Rendering;
    3.  
    4. public class MeshClosestPoint : MonoBehaviour
    5. {
    6.     [SerializeField] private ComputeShader _ComputeShader;
    7.     [SerializeField] private Transform _Point;
    8.     private GraphicsBuffer _VertexBuffer, _IndexBuffer;
    9.     private ComputeBuffer _ComputeBuffer;
    10.     private int _Dimension = 0, _Count = 0;
    11.     private Mesh _Mesh;
    12.     private SubMeshDescriptor _SubMeshDescriptor;
    13.     private Renderer _Renderer;
    14.     private Ray _Ray;
    15.     private string _CurrentTargetName = "";
    16.     private bool _Enable = false;
    17.     private Vector4[] _Vectors;
    18.     private Vector3 _MeshClosestPoint = new Vector3(0f, 0f, 0f);
    19.  
    20.     void Load(GameObject target)
    21.     {
    22.         if (_Mesh.isReadable == false) return;
    23.         _Dimension = 0;
    24.         _Mesh.vertexBufferTarget |= GraphicsBuffer.Target.Raw;
    25.         _Mesh.indexBufferTarget |= GraphicsBuffer.Target.Raw;
    26.         _SubMeshDescriptor = _Mesh.GetSubMesh(0);
    27.         VertexAttributeDescriptor[] attributes = _Mesh.GetVertexAttributes();
    28.         for (int i = 0; i < attributes.Length; i++) _Dimension += attributes[i].dimension;
    29.         _Count = _Mesh.triangles.Length / 3;
    30.         _Renderer = target.GetComponentInChildren<Renderer>();
    31.         if (_VertexBuffer != null) _VertexBuffer.Dispose();
    32.         _VertexBuffer = _Mesh.GetVertexBuffer(0);
    33.         if (_IndexBuffer != null) _IndexBuffer.Dispose();
    34.         _IndexBuffer = _Mesh.GetIndexBuffer();
    35.         if (_ComputeBuffer != null) _ComputeBuffer.Dispose();
    36.         _ComputeBuffer = new ComputeBuffer(_Count, 4 * sizeof(float));
    37.         _Vectors = new Vector4[_Count];
    38.     }
    39.  
    40.     void Update()
    41.     {
    42.         if (Input.GetMouseButton(0))
    43.         {
    44.             _Enable = true;
    45.             _Ray = Camera.main.ScreenPointToRay(Input.mousePosition);
    46.         }
    47.         else
    48.         {
    49.             _Enable = false;
    50.         }
    51.         if (_Mesh == null || _Renderer == null) return;  
    52.         _ComputeShader.SetInt("_Count", _Count);
    53.         _ComputeShader.SetInt("_Dimension", _Dimension);
    54.         _ComputeShader.SetInt("_IndexStart", _SubMeshDescriptor.indexStart);
    55.         _ComputeShader.SetInt("_BaseVertex", _SubMeshDescriptor.baseVertex);
    56.         _ComputeShader.SetInt("_IndexFormat", _Mesh.indexFormat == IndexFormat.UInt16 ? 16 : 32);
    57.         _ComputeShader.SetVector("_Point", _Point.position);
    58.         _ComputeShader.SetMatrix("_LocalToWorldMatrix", _Renderer.localToWorldMatrix);
    59.         _ComputeShader.SetBuffer(0, "_VertexBuffer", _VertexBuffer);
    60.         _ComputeShader.SetBuffer(0, "_IndexBuffer", _IndexBuffer);
    61.         _ComputeShader.SetBuffer(0, "_ComputeBuffer", _ComputeBuffer);
    62.         _ComputeShader.Dispatch(0, (_Count + 64) / 64, 1, 1);
    63.         _ComputeBuffer.GetData(_Vectors);
    64.         float minDistance = 1e9f;
    65.         int index = 0;
    66.         for (int i = 0; i < _Vectors.Length; i++)
    67.         {
    68.             float distance = _Vectors[i].w;
    69.             if (distance < minDistance)
    70.             {
    71.                 minDistance = distance;
    72.                 index = i;
    73.             }
    74.         }
    75.         Vector4 vector = _Vectors[index];
    76.         _MeshClosestPoint.x = vector.x;
    77.         _MeshClosestPoint.y = vector.y;
    78.         _MeshClosestPoint.z = vector.z;
    79.         Debug.DrawLine(_Point.position, _MeshClosestPoint, Color.blue);
    80.     }
    81.  
    82.     void FixedUpdate()
    83.     {
    84.         if (_Enable)
    85.         {
    86.             if (Physics.Raycast(_Ray.origin, _Ray.direction, out RaycastHit hit))
    87.             {
    88.                 GameObject target = hit.collider.gameObject;
    89.                 if (target.name != _CurrentTargetName)
    90.                 {
    91.                     _CurrentTargetName = target.name;
    92.                     _Mesh = target.GetComponent<MeshFilter>().sharedMesh;
    93.                     if (_Mesh != null) Load(target);
    94.                 }
    95.             }
    96.         }
    97.     }
    98.  
    99.     void OnDestroy()
    100.     {
    101.         if (_VertexBuffer != null) _VertexBuffer.Dispose();
    102.         if (_IndexBuffer != null) _IndexBuffer.Dispose();
    103.         if (_ComputeBuffer != null) _ComputeBuffer.Dispose();
    104.     }
    105. }
    Code (CSharp):
    1. #pragma kernel CSMain
    2.  
    3. ByteAddressBuffer _VertexBuffer, _IndexBuffer;
    4. int _Count, _Dimension;
    5. uint _IndexStart, _BaseVertex, _IndexFormat;
    6. float3 _Point;
    7. float4x4 _LocalToWorldMatrix;
    8. RWStructuredBuffer<float4> _ComputeBuffer;
    9.  
    10. float3 ClosestPointOnTriangle(float3 p, float3 a, float3 b, float3 c)
    11. {
    12.     float3 ba = b - a;
    13.     float3 pa = p - a;
    14.     float3 cb = c - b;
    15.     float3 pb = p - b;
    16.     float3 ac = a - c;
    17.     float3 pc = p - c;
    18.     float3 nm = cross(ba, ac);
    19.     float3 q = cross(nm, pa);
    20.     float d = 1.0 / dot(nm, nm);
    21.     float u = d * dot(q, ac);
    22.     float v = d * dot(q, ba);
    23.     float w = 1.0 - u - v;
    24.     if (u < 0.0)
    25.     {
    26.         w = clamp(dot(pc, ac) / dot(ac, ac), 0.0, 1.0);
    27.         u = 0.0;
    28.         v = 1.0 - w;
    29.     }
    30.     else if (v < 0.0f)
    31.     {
    32.         u = clamp(dot(pa, ba) / dot(ba, ba), 0.0, 1.0);
    33.         v = 0.0;
    34.         w = 1.0 - u;
    35.     }
    36.     else if (w < 0.0f)
    37.     {
    38.         v = clamp(dot(pb, cb) / dot(cb, cb), 0.0, 1.0);
    39.         w = 0.0;
    40.         u = 1.0 - v;
    41.     }
    42.     return u * b + v * c + w * a;
    43. }
    44.  
    45. uint3 GetTriangleIndices16 (ByteAddressBuffer indexBuffer, uint primitiveIndex, uint indexStart, uint baseVertex)
    46. {
    47.     const uint offsetInBytes = (indexStart + primitiveIndex * 3) << 1;
    48.     const uint dwordAlignedOffset = offsetInBytes & ~3;
    49.     const uint2 fourIndices = indexBuffer.Load2(dwordAlignedOffset);
    50.     bool IsAligned = dwordAlignedOffset == offsetInBytes;
    51.     uint x = IsAligned ? fourIndices.x & 0xffff : (fourIndices.x >> 16) & 0xffff;
    52.     uint y = IsAligned ? (fourIndices.x >> 16) & 0xffff : fourIndices.y & 0xffff;
    53.     uint z = IsAligned ? fourIndices.y & 0xffff : (fourIndices.y >> 16) & 0xffff;
    54.     return uint3(x, y, z) + baseVertex.xxx;
    55. }
    56.  
    57. uint3 GetTriangleIndices32 (ByteAddressBuffer indexBuffer, uint primitiveIndex, uint indexStart, uint baseVertex)
    58. {
    59.     const uint offsetInBytes = (indexStart + primitiveIndex * 3) << 2;
    60.     return indexBuffer.Load3(offsetInBytes) + baseVertex.xxx;
    61. }
    62.  
    63. [numthreads(64,1,1)]
    64. void CSMain (uint3 id : SV_DispatchThreadID)
    65. {
    66.     if (id.x >= (uint)_Count) return;
    67.     uint3 indices = _IndexFormat == 16u ?
    68.         GetTriangleIndices16 (_IndexBuffer, id.x, _IndexStart, _BaseVertex):
    69.         GetTriangleIndices32 (_IndexBuffer, id.x, _IndexStart, _BaseVertex);
    70.     float3 localPosA = asfloat(_VertexBuffer.Load3((indices.x * _Dimension) << 2));
    71.     float3 localPosB = asfloat(_VertexBuffer.Load3((indices.y * _Dimension) << 2));
    72.     float3 localPosC = asfloat(_VertexBuffer.Load3((indices.z * _Dimension) << 2));
    73.     float3 worldPosA = mul(_LocalToWorldMatrix, float4(localPosA, 1.0)).xyz;
    74.     float3 worldPosB = mul(_LocalToWorldMatrix, float4(localPosB, 1.0)).xyz;
    75.     float3 worldPosC = mul(_LocalToWorldMatrix, float4(localPosC, 1.0)).xyz;
    76.     float3 closestPoint = ClosestPointOnTriangle(_Point, worldPosA, worldPosB, worldPosC);
    77.     float spread = distance(_Point, closestPoint);
    78.     _ComputeBuffer[id.x] = float4(closestPoint, spread);
    79. }
     
    bb8_1 likes this.
  2. methusalah999

    methusalah999

    Joined:
    May 22, 2017
    Posts:
    645
    I've observed that too in at least 2021.3.25, the vertices from SMR.GetVertexBuffer seem to be in the "RootBone" space, which I don't understand, but most importantly, the scale is reversed.

    Currently my only way of making it work is to apply this matrix to my vertices
    Matrix4x4.TRS(SMR rootBone local position, SMR rootBone local rotation, SMR transform inverse lossy scale);
    which is the strangest thing.

    Any explanation on why it is like that?

    @Qleenie do you have more information to share about it after your experiments?
     
    Last edited: Jan 29, 2024
  3. nasos_333

    nasos_333

    Joined:
    Feb 13, 2013
    Posts:
    13,470
    The problem is what happens when need not to define the mesh from scratch, but augment and add to the existing scene geometry for all objects with arbitrary shapes and triangle counts. To this date have not found anything even remotly close to what geometry shaders can do in this regard. E.g. i can grow a flower in each triangle of each scene mesh with a replacement shader, i cant imagine how this would be possible with compute shaders.

    Hi,

    I would like to follow up on this, i have still not found any general purpose way to change scene wide polygon info using a replacement shader without using a geometry shader.

    Is there anything new on this front, that will operate on any and all meshes in thd scene directly, like geometry shaders can add extra triangles to all the scene objects ?

    Thanks

    So far seems geometry shaders are the only option to do something like that and are extremely fast and efficient in it.

    E.g. i use geometry shader to voxelize the scene at run time and use it for fluids or global illumination or terrain wide grass without add a single grass blade manually.
     
    Last edited: Apr 4, 2024