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. Megacity Metro Demo now available. Download now.
    Dismiss Notice
  3. Unity support for visionOS is now available. Learn more in our blog post.
    Dismiss Notice

Official Feedback Wanted: Mesh Compute Shader access

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

  1. Aras

    Aras

    Unity Technologies

    Joined:
    Nov 7, 2005
    Posts:
    4,770
    We've heard both internally & externally for a while, that ability to directly access Mesh & SkinnedMeshRenderer geometry data from a compute shader would be useful (one example: "GraphicsBuffer, Mesh vertices and Compute shaders" thread)

    So here's a google doc outlining our current proposal & thinking: Unity Mesh API Compute Shader Access

    (Everything described in the doc is out in Unity 2021.2 a19)

    Feedback or thoughts most welcome, in the doc or here on the forum!
     
    Last edited: Jun 9, 2021
  2. korzen303

    korzen303

    Joined:
    Oct 2, 2012
    Posts:
    223
    Hi Aras, looks good, thanks! I would be interested in "Ability to create a Mesh with your own GraphicsBuffers". My use case is GPU physics for surgical simulation. This includes mesh volumetric deformation computation, topological modifications (i.e. mesh cutting and tearing) and collision detection and respons between meshes. To make things fast I need to be able to render straight from the GPU (for now I modified the Standard Shader to read data from simulation ComputeBuffers, doing so for more shaders is a bit of a pain).

    Another application is to use DXR raytracing capabilities for very fast collision detection based on raycasts between deformable meshes. Specifically, I need to build and update (refit and/or reconstruct) RT acceleration structures (ideally, on the GPU without any readbacks) around deformable meshes. I have got it already working but I need to setup via Mesh each frame, which, I guess, mitigates the performance gains.

    Thanks and keep up the good work!

    PS: Do I need a custom Unity build to test it? On latest 2021.2.0.a14 I ma getting:
    Assets\NoiseBall\NoiseBall.cs(92,20): error CS1061: 'Mesh' does not contain a definition for 'vertexBufferTarget' and no accessible extension method 'vertexBufferTarget' accepting a first argument of type 'Mesh' could be found (are you missing a using directive or an assembly reference?)
     
    LooperVFX likes this.
  3. Aras

    Aras

    Unity Technologies

    Joined:
    Nov 7, 2005
    Posts:
    4,770
    It's not in any unity build yet.
     
  4. Ruchir

    Ruchir

    Joined:
    May 26, 2015
    Posts:
    934
    I've been waiting for this for so long, happy to know it's finally going to be added :)
     
    Egad_McDad likes this.
  5. vx4

    vx4

    Joined:
    Dec 11, 2012
    Posts:
    181
    Last edited: Apr 26, 2021
    korzen303 likes this.
  6. cecarlsen

    cecarlsen

    Joined:
    Jun 30, 2006
    Posts:
    858
    What I am interested in:

    "Ability to create a Mesh with your own GraphicsBuffers -- i.e. you create vertex/index buffers, and then create a Mesh that does not manage/own them at all, just “wraps” them into an object that looks like a Mesh to everything else in Unity. Conceptually this is similar to Texture2D.CreateExternalTexture"
    ... is placed at the very bottom of the document under "Open Questions / Future Considerations". In most cases, this is what I will need. If this feature existed, should I need to modify an existing mesh, I could simply upload it into my own GrapihcsBuffers, mess with the data and assign them to a new (gpu-only) mesh.

    I am super exited that anyone at Unity is working on this. I can't think of a more important feature for my work.
     
    Egad_McDad and LooperVFX like this.
  7. Ignifex

    Ignifex

    Joined:
    May 6, 2015
    Posts:
    11
    I think my use case is relatively basic. I would like to animate a mesh using a custom compute shader. The goal for me is to implement a more advanced skinning algorithm and real-time subdivision.

    The API addition for the SkinnedMeshRenderer seems a perfect fit, also to keep out of the box support for motion vectors. Would that then require some option to disable the default animation updates, if the user wants to do the animation entirely on their own?
     
  8. oldhighscore

    oldhighscore

    Joined:
    Nov 28, 2020
    Posts:
    79
    My feedback atm is only that I would like it asap <3 :D & TY !!!

    Hopefully it makes it in by 2021.2 release
     
    Ruchir and vx4 like this.
  9. Dimetrock

    Dimetrock

    Joined:
    Dec 9, 2012
    Posts:
    37
    Would be great to have this for the 2020 LTS version so we could use it together with ECS. Would be a huge improvement for my game, which contains runtime-generated custom terrain.

    I have one question though, would it be possible to generate collider data out of modified mesh?
     
    Last edited: May 12, 2021
  10. Aras

    Aras

    Unity Technologies

    Joined:
    Nov 7, 2005
    Posts:
    4,770
    Update: all of this should be in 2021.2 alpha 18 soon.
     
  11. oldhighscore

    oldhighscore

    Joined:
    Nov 28, 2020
    Posts:
    79
    Related but unrelated question, do you have any recommendations on experimenting with mesh shaders in Unity? I'm in the middle of spawning my first custom SRP but not seeing anything for the new graphics api.

    edit for reference: https://developer.nvidia.com/blog/introduction-turing-mesh-shaders/
     
    Egad_McDad, Invertex and Ruchir like this.
  12. Aras

    Aras

    Unity Technologies

    Joined:
    Nov 7, 2005
    Posts:
    4,770
    I don't think Unity supports mesh shaders right now. I don't know what the plans are; I'm not part of any graphics related teams, sorry!
     
    Ruchir and vx4 like this.
  13. Invertex

    Invertex

    Joined:
    Nov 7, 2013
    Posts:
    1,539
    Mesh shaders are on the roadmap for when D3D12 is out of beta, when that is... who knows. Likely won't be 2021 though since it's in the "Planned" section and not even "In Progress".

    https://portal.productboard.com/uni...visual-effects/c/201-directx12-out-of-preview

    As far as I'm aware they haven't created code to handle the Mesh shader stuff even if you have D3D12 enabled so far. As Mesh shaders are a D3D12_2 feature which is relatively new.
     
    oldhighscore and Ruchir like this.
  14. oldhighscore

    oldhighscore

    Joined:
    Nov 28, 2020
    Posts:
    79
    I was unaware that mesh shaders weren't standardly available across DX, Vulkan, etc. Makes sense that Unity hasn't tackled it yet then. Meanwhile Unreal is spitting down marketing junk terminology down peoples throats. This just happened to be a feature I wanted when first reading up on the next gen graphic APIs over a year ago so was curious.

    Thanks for the info! I've taken a glimpse at a product board before from Unity but always wondered where are ALL the product boards for the public? Do you have a link that lists all the product boards? Also seems odd they don't have a board for strictly SRP but w/e.
     
    Egad_McDad and Ruchir like this.
  15. Ruchir

    Ruchir

    Joined:
    May 26, 2015
    Posts:
    934
    https://unity.com/roadmap/unity-platform
     
    Egad_McDad and oldhighscore like this.
  16. Invertex

    Invertex

    Joined:
    Nov 7, 2013
    Posts:
    1,539
    It is part of the latest Vulkan, DX and OpenGL, but it was only added to those APIs in the past 2 years. And it being part of the API doesn't make it automatically available in a game engine. The engine generally has to develop new code for their shader/compute compilers to handle the functionality, do all kinds of testing to try and find conflicts, etc.., figure out how to make the engine interact with it nicely and with Mesh Shaders especially it's kind of a radical change in how meshes would be handled in the pipeline, so it's not a simple "we've updated the DirectX library version!".
    Not to mention the oldest hardware Mesh Shaders are supported on is Nvidia 2xxx/11xx (Turing) and the latest AMD GPU (RDNA2)... So it wouldn't be a big priority in that case due to the fairly small userbase support currently.
     
    Egad_McDad, Ruchir and oldhighscore like this.
  17. vx4

    vx4

    Joined:
    Dec 11, 2012
    Posts:
    181
    DXR have small user base and supported.
     
  18. Invertex

    Invertex

    Joined:
    Nov 7, 2013
    Posts:
    1,539
    Yes but it's also a much more publicized feature that people have been craving to reach realtime for decades... Unity not getting it for a long time would have been terrible publicity. Meanwhile barely anyone knows what mesh shaders are. It would have been a good media opportunity if Unity had implemented them when they implemented DXR and beat Unreal to the "look at all these polys" hype, but that ship has passed now.

    Hopefully it won't be years before they support that part of new hardware.

    (also, DXR is simpler to implement into the pipeline compared to creating a pathway for mesh shading)
     
    LooperVFX and vx4 like this.
  19. Invertex

    Invertex

    Joined:
    Nov 7, 2013
    Posts:
    1,539
    Alpha 19 is now out with the direct mesh access feature :)
     
    Egad_McDad, DanielZeller, vx4 and 2 others like this.
  20. oldhighscore

    oldhighscore

    Joined:
    Nov 28, 2020
    Posts:
    79
    Party time
     
    Walter_Hulsebos, DanielZeller and vx4 like this.
  21. Ignifex

    Ignifex

    Joined:
    May 6, 2015
    Posts:
    11
    I tried it out with a regular MeshRenderer first. Modifying the mesh vertex data using a Compute Shader is working very smoothly!

    I also tried to modify the vertex data of a Skinned Mesh Renderer, but I could net get any results. My compute shader is running during the SRP's beginFrameRendering callback. I assume the Skinned Mesh Renderer is still modifying the vertex data after I change it?

    The reason I would like to use a Skinned Mesh Renderer is to get motion vector support. Perhaps it just needs an option to disable its own updates? Another thing to consider here may be that motion vectors probably require a fixed mesh topology. If the user modifies the index buffer, it seems like this may fail.
     
    gentmo and LooperVFX like this.
  22. flyer19

    flyer19

    Joined:
    Aug 26, 2016
    Posts:
    121
    I try to test moving vertex position,below codes seems work not correct,it moving part of the vertexes,not total ...
    something wrong below ?:

    Code (CSharp):
    1. using System.Collections;
    2. using System.Collections.Generic;
    3. using UnityEngine;
    4.  
    5. public class TestGpuMove : MonoBehaviour
    6. {
    7.     public Mesh mesh;
    8.     public ComputeShader computeShader;
    9.     void OnEnable()
    10.     {
    11.  
    12.  
    13.         mesh = GetComponent<MeshFilter>().mesh;
    14.  
    15.      
    16.     }
    17.     float m_LocalTime;
    18.     GraphicsBuffer vertexBuffer;
    19.     void Update()
    20.     {
    21.         m_LocalTime += Time.deltaTime * 2.0f;
    22.         mesh.vertexBufferTarget |= GraphicsBuffer.Target.Raw;
    23.         vertexBuffer = mesh.GetVertexBuffer(0);
    24.         computeShader.SetFloat("gTime", m_LocalTime);
    25.         computeShader.SetInt("gVertexCount", mesh.vertexCount);
    26.        
    27.         computeShader.SetBuffer(0, "bufVertices", vertexBuffer);
    28.  
    29.        computeShader.Dispatch(0, (mesh.vertexCount + 255) / 255, 1, 1);
    30.        // Debug.Log("xxx");
    31.  
    32.     }
    33.     void OnDisable()
    34.     {
    35.         vertexBuffer?.Dispose();
    36.         vertexBuffer = null;
    37.     }
    38. }
    39.  
    Code (CSharp):
    1. // Each #kernel tells which function to compile; you can have many kernels
    2. #pragma kernel meshMove
    3.  
    4. // Create a RenderTexture with enableRandomWrite flag and set it
    5. // with cs.SetTexture
    6. //RWTexture2D<float4> Result;
    7. RWByteAddressBuffer bufVertices;
    8. float gTime;
    9. int gVertexCount;
    10.  
    11. [numthreads(256,1,1)]
    12. void meshMove(uint3 id : SV_DispatchThreadID)
    13. {
    14.     // TODO: insert actual code here!
    15.     if (id.x>= gVertexCount)
    16.         return;
    17.     uint vidx = id.x*3;
    18.     uint3 praw= bufVertices.Load3(vidx<<2);
    19.     float3 p = asfloat(praw);
    20.     p.x = p.x+sin( gTime)*0.001;
    21.     bufVertices.Store3(((vidx)<<2), asuint(p));
    22. }
    23.  
     
  23. Aras

    Aras

    Unity Technologies

    Joined:
    Nov 7, 2005
    Posts:
    4,770
    What is your vertex format? The compute shader code seems to expect that vertices are just a single float3 (i.e. just positions). Is the mesh vertex layout like that?
     
  24. dreamerflyer19_new

    dreamerflyer19_new

    Joined:
    Jun 27, 2021
    Posts:
    1
    my fault..it includes positon,uv,normal, so should be vidx=id.x*8,thank you!works~
     
    Last edited: Jun 27, 2021
  25. flyer19

    flyer19

    Joined:
    Aug 26, 2016
    Posts:
    121
    meet new problem for limite the length between vertexes,not work:

    1.save length in uv0.z:
    Code (CSharp):
    1.    for (var j = strandParticleBegin+1; j <= strandParticleEnd-1; j += 1)
    2.                     {
    3.                         // particleUVPtr[j] = new Vector2(j, curveIdx); // particle index + strand index
    4.                       //  if (j > strandParticleBegin&&j<=strandParticleEnd)
    5.                         {
    6.                             var p0 = vertices[j];
    7.                             var p1 = vertices[j - 1];
    8.                             float len = Vector3.Distance(p0, p1);
    9.                             particleUVPtr[j] = new Vector3((curveIdx + 0.0f) / strideArray.Length, (j - strandParticleBegin + 0.0f) / curvePointCount+0.01f ,len); // particle index + strand index+limited length
    10.  
    11.                         }
    12.                     }
    2.dispatch move and limited compute shader:
    Code (CSharp):
    1. // Each #kernel tells which function to compile; you can have many kernels
    2. #pragma kernel meshMove
    3. #pragma kernel limiteLength
    4.  
    5. // Create a RenderTexture with enableRandomWrite flag and set it
    6. // with cs.SetTexture
    7. //RWTexture2D<float4> Result;
    8. RWByteAddressBuffer bufVertices;
    9. float gTime;
    10. int gVertexCount;
    11.  
    12. [numthreads(256,1,1)]
    13. void meshMove(uint3 id : SV_DispatchThreadID)
    14. {
    15.     // TODO: insert actual code here!
    16.     if (id.x>= gVertexCount)
    17.         return;
    18.     uint vidx = id.x * 12;
    19.     uint vidUV = vidx+6 ;
    20.     uint3 uvRaw = bufVertices.Load3(vidUV << 2);
    21.     float3 uv = asfloat(uvRaw);
    22.     if (uv.y >0)
    23.     {
    24.  
    25.         uint3 praw = bufVertices.Load3(vidx << 2);
    26.         float3 p = asfloat(praw);
    27.  
    28.         p.x = p.x + sin(gTime) * 0.001;
    29.  
    30.         bufVertices.Store3(((vidx) << 2), asuint(p));
    31.     }
    32. }
    33.  
    34. [numthreads(256, 1, 1)]
    35. void limiteLength(uint3 id : SV_DispatchThreadID)
    36. {
    37.     // TODO: insert actual code here!
    38.     if (id.x >= gVertexCount)
    39.         return;
    40.     uint vidx = id.x * 12;
    41.     uint vidUV = vidx + 6;
    42.     uint3 uvRaw = bufVertices.Load3(vidUV << 2);
    43.     float3 uv = asfloat(uvRaw);
    44.     if (uv.y > 0)
    45.     {
    46.  
    47.         uint3 praw = bufVertices.Load3(vidx << 2);
    48.         float3 p = asfloat(praw);
    49.  
    50.         uint vidxUp = (id.x-1)*12;
    51.         uint3 prawUp= bufVertices.Load3(vidxUp << 2);
    52.         float3 pUp = asfloat(prawUp);
    53.         float dis = distance(pUp, p);
    54.         if (dis > uv.z)
    55.         {
    56.             float3 dir = normalize(p-pUp);
    57.             p = pUp + dir * dis;
    58.             bufVertices.Store3(((vidx) << 2), asuint(p));
    59.  
    60.         }
    61.  
    62.     }
    63.  
    64. }
    65.  
    Code (CSharp):
    1.   void Update()
    2.     {
    3.         m_LocalTime += Time.deltaTime * 2.0f;
    4.        // mesh.vertexBufferTarget |= GraphicsBuffer.Target.Raw;
    5.         mesh.vertexBufferTarget = mesh.vertexBufferTarget | GraphicsBuffer.Target.Raw;
    6.         vertexBuffer = mesh.GetVertexBuffer(0);
    7.         computeShader.SetFloat("gTime", m_LocalTime);
    8.         computeShader.SetInt("gVertexCount", mesh.vertexCount);
    9.    
    10.         computeShader.SetBuffer(0, "bufVertices", vertexBuffer);
    11.  
    12.        computeShader.Dispatch(0, (mesh.vertexCount + 255) / 255, 1, 1);
    13.  
    14.         vertexBuffer = mesh.GetVertexBuffer(0);
    15.         computeShader.SetBuffer(1, "bufVertices", vertexBuffer);
    16.  
    17.         computeShader.Dispatch(1, (mesh.vertexCount + 255) / 255, 1, 1);
    18.  
    19.  
    20.     }
    hair.png
    hair2.png
     
    Last edited: Jun 28, 2021
  26. Aras

    Aras

    Unity Technologies

    Joined:
    Nov 7, 2005
    Posts:
    4,770
    Your compute shader will be full of different GPU threads/lanes fighting over one another. Imagine your C# loop, where each loop iteration is running on another thread -- the results will be sometimes correct, but sometimes garbage because one thread will be writing vertex position that another thread will be reading right at the same time, so it will get half-written results. I suspect that's the issue you're seeing. You'd have to "somehow" ensure that on the GPU there's no read/write hazards or other synchronization issues.
     
    Walter_Hulsebos likes this.
  27. Przemyslaw_Zaworski

    Przemyslaw_Zaworski

    Joined:
    Jun 9, 2017
    Posts:
    327
    Mesh Compute Shader access is very nice feature !

    I made a quick test, here is full source code (may be useful for someone):

    Code (CSharp):
    1. using Unity.Collections;
    2. using UnityEngine;
    3. using UnityEngine.Rendering;
    4.  
    5. [RequireComponent(typeof(MeshFilter))]
    6. [RequireComponent(typeof(MeshRenderer))]
    7. public class ProceduralGrass : MonoBehaviour
    8. {
    9.     public ComputeShader ProceduralGrassCS;
    10.     public Shader ProceduralGrassPS;  
    11.     public Texture2D GrassTexture;
    12.     public int TriangleCount = 2000000;
    13.  
    14.     GraphicsBuffer _VertexBuffer;
    15.     GraphicsBuffer _NormalBuffer;    
    16.     GraphicsBuffer _TexcoordBuffer;
    17.     Material _Material;
    18.     Mesh _Mesh;
    19.  
    20.     void Update()
    21.     {
    22.         if (_Mesh && _Mesh.vertexCount != TriangleCount * 3)
    23.         {
    24.             Release();
    25.         }
    26.         if (_Mesh == null)
    27.         {
    28.             _Mesh = new Mesh();
    29.             _Mesh.name = "ProceduralGrass";
    30.             _Mesh.vertexBufferTarget |= GraphicsBuffer.Target.Raw;
    31.             VertexAttributeDescriptor[] attributes = new []
    32.             {
    33.                 new VertexAttributeDescriptor(VertexAttribute.Position, stream:0),
    34.                 new VertexAttributeDescriptor(VertexAttribute.Normal, stream:1),
    35.                 new VertexAttributeDescriptor(VertexAttribute.TexCoord0, stream:2)
    36.             };
    37.             _Mesh.SetVertexBufferParams(TriangleCount * 3, attributes);        
    38.             _Mesh.SetIndexBufferParams(TriangleCount * 3, IndexFormat.UInt32);
    39.             NativeArray<int> indexBuffer = new NativeArray<int>(TriangleCount * 3, Allocator.Temp);
    40.             for (int i = 0; i < TriangleCount * 3; ++i) indexBuffer[i] = i;
    41.             _Mesh.SetIndexBufferData(indexBuffer, 0, 0, indexBuffer.Length, MeshUpdateFlags.DontRecalculateBounds | MeshUpdateFlags.DontValidateIndices);
    42.             indexBuffer.Dispose();
    43.             SubMeshDescriptor submesh = new SubMeshDescriptor(0, TriangleCount * 3, MeshTopology.Triangles);
    44.             submesh.bounds = new Bounds(Vector3.zero, new Vector3(2000, 2, 2000));
    45.             _Mesh.SetSubMesh(0, submesh);
    46.             _Mesh.bounds = submesh.bounds;
    47.             GetComponent<MeshFilter>().sharedMesh = _Mesh;
    48.             _Material = new Material(ProceduralGrassPS);
    49.             _Material.mainTexture = GrassTexture;
    50.             GetComponent<MeshRenderer>().sharedMaterial = _Material;
    51.         }
    52.         _VertexBuffer ??= _Mesh.GetVertexBuffer(0);
    53.         _NormalBuffer ??= _Mesh.GetVertexBuffer(1);
    54.         _TexcoordBuffer ??= _Mesh.GetVertexBuffer(2);
    55.         ProceduralGrassCS.SetInt("_TriangleCount", TriangleCount);
    56.         ProceduralGrassCS.SetBuffer(0, "_VertexBuffer", _VertexBuffer);
    57.         ProceduralGrassCS.SetBuffer(0, "_TexcoordBuffer", _TexcoordBuffer);
    58.         ProceduralGrassCS.SetBuffer(0, "_NormalBuffer", _NormalBuffer);
    59.         ProceduralGrassCS.SetFloat("_Time", Time.time);
    60.         ProceduralGrassCS.Dispatch(0, (TriangleCount + 64 - 1) / 64, 1, 1);
    61.     }
    62.  
    63.     void Release()
    64.     {
    65.         Destroy(_Material);
    66.         Destroy(_Mesh);
    67.         _Mesh = null;
    68.         _VertexBuffer?.Dispose();
    69.         _VertexBuffer = null;
    70.         _TexcoordBuffer?.Dispose();
    71.         _TexcoordBuffer = null;
    72.         _NormalBuffer?.Dispose();
    73.         _NormalBuffer = null;
    74.     }
    75.  
    76.     void OnDestroy()
    77.     {
    78.         Release();
    79.     }  
    80. }
    Code (CSharp):
    1. #pragma kernel CSMain
    2.  
    3. RWByteAddressBuffer _VertexBuffer;
    4. RWByteAddressBuffer _NormalBuffer;
    5. RWByteAddressBuffer _TexcoordBuffer;
    6. int _TriangleCount;
    7. float _Time;
    8.  
    9. struct Vertex
    10. {
    11.     float3 position;
    12.     float2 texcoord;
    13. };
    14.  
    15. void Store2 (RWByteAddressBuffer buffer, int index, float2 v)
    16. {
    17.     uint2 data = asuint(v);
    18.     buffer.Store2((index*3)<<2, data);
    19. }
    20.  
    21. void Store3 (RWByteAddressBuffer buffer, int index, float3 v)
    22. {
    23.     uint3 data = asuint(v);
    24.     buffer.Store3((index*3) << 2, data);
    25. }
    26.  
    27. float Mod (float x, float y)
    28. {
    29.     return x - y * floor(x/y);
    30. }
    31.  
    32. float3x3 RotationY (float y)
    33. {
    34.     return float3x3(cos(y),0.0,-sin(y), 0.0,1.0,0.0, sin(y),0.0,cos(y));
    35. }
    36.  
    37. float Hash (float p)
    38. {
    39.     p = frac(p * .1031);
    40.     p *= p + 33.33;
    41.     p *= p + p;
    42.     return frac(p);
    43. }
    44.  
    45. Vertex GenerateQuad (uint id) // generate vertex for grid of quads, from 1D array index (branchless version)
    46. {
    47.     float instance = floor(id / 6.0); // index of current quad
    48.     float divider = sqrt(_TriangleCount * 0.5); // "divider" can be precalculated on the script side, for maximum performance
    49.     float3 center = float3(Mod(instance, divider), 0.0, floor(instance / divider)); // center of current quad
    50.     Vertex vertex;
    51.     float u = Mod(float(id), 2.0);
    52.     float v = sign(Mod(126.0, Mod(float(id), 6.0) + 6.0));
    53.     float3 localPos = float3(sign(u) - 0.5, sign(v) - 0.5, 0.0);
    54.     localPos.z += sign(v) * sin(_Time * Hash(instance + 123.0) * 0.5); // grass wind animation
    55.     vertex.position = mul(RotationY(radians(Hash(instance) * 180.0)), localPos) + center; // position with random rotation
    56.     vertex.texcoord = float2(u, v);
    57.     return vertex;
    58. }
    59.  
    60. [numthreads(64, 1, 1)]
    61. void CSMain(uint3 threadID : SV_DispatchThreadID)
    62. {
    63.     uint id = threadID.x;
    64.     if ((int)id >= _TriangleCount) return;
    65.     uint idx1 = id * 3;
    66.     uint idx2 = id * 3 + 1;
    67.     uint idx3 = id * 3 + 2;  
    68.     Vertex v1 = GenerateQuad(idx1);
    69.     Vertex v2 = GenerateQuad(idx2);
    70.     Vertex v3 = GenerateQuad(idx3);  
    71.     float3 p1 = v1.position;
    72.     float3 p2 = v2.position;
    73.     float3 p3 = v3.position;
    74.     float2 uv1 = v1.texcoord;
    75.     float2 uv2 = v2.texcoord;
    76.     float2 uv3 = v3.texcoord;
    77.     float3 normal = normalize(cross(p2 - p1, p3 - p2));  
    78.     if (Mod(float(id), 2.0) == 1.0)
    79.     {
    80.         Store3(_VertexBuffer, idx1, p1);
    81.         Store3(_VertexBuffer, idx2, p2);
    82.         Store3(_VertexBuffer, idx3, p3);
    83.         Store2(_TexcoordBuffer, idx1, uv1);
    84.         Store2(_TexcoordBuffer, idx2, uv2);
    85.         Store2(_TexcoordBuffer, idx3, uv3);      
    86.     }
    87.     else
    88.     {
    89.         Store3(_VertexBuffer, idx1, p2);
    90.         Store3(_VertexBuffer, idx2, p1);
    91.         Store3(_VertexBuffer, idx3, p3);
    92.         Store2(_TexcoordBuffer, idx1, uv2);
    93.         Store2(_TexcoordBuffer, idx2, uv1);
    94.         Store2(_TexcoordBuffer, idx3, uv3);
    95.         normal = -normal;      
    96.     }
    97.     Store3(_NormalBuffer, idx1, normal);
    98.     Store3(_NormalBuffer, idx2, normal);
    99.     Store3(_NormalBuffer, idx3, normal);  
    100. }
    Code (CSharp):
    1. Shader "ProceduralGrass"
    2. {
    3.     Properties
    4.     {
    5.         _MainTex ("MainTex ", 2D) = "white" {}
    6.     }
    7.     Subshader
    8.     {
    9.         Cull Off
    10.         CGPROGRAM
    11.         #pragma surface SurfaceShader Standard fullforwardshadows addshadow
    12.         #pragma target 5.0
    13.  
    14.         sampler2D _MainTex;
    15.  
    16.         struct Input
    17.         {
    18.             float2 uv_MainTex;
    19.         };
    20.  
    21.         void SurfaceShader (Input IN, inout SurfaceOutputStandard o)
    22.         {
    23.             float4 color = tex2D(_MainTex, IN.uv_MainTex);
    24.             if (color.a < 0.5) discard;
    25.             o.Albedo = color;
    26.         }
    27.  
    28.         ENDCG
    29.     }
    30. }
    Grid of 1000 x 1000 quads, every quad has two triangles (total 2 milions of triangles).

    With RTX 2070, I have 99-100 percents of GPU usage and results:

    with "addshadow" (156 FPS):

    upload_2021-7-11_23-29-15.png


    without "addshadow" (266 FPS)
    upload_2021-7-11_23-29-52.png
     
    ADNCG, hugokostic, sabint and 12 others like this.
  28. Ignifex

    Ignifex

    Joined:
    May 6, 2015
    Posts:
    11
    Any news on using this with a SkinnedMeshRenderer for motion vector support?
     
  29. ThisLove

    ThisLove

    Joined:
    Jun 27, 2019
    Posts:
    5
    Very excited. Just heard about Compute Shaders the other day and now find I can change the mesh on a graphics card. Just wanted to UpVote this with a post even though the code about looks like a foreign language to me.
     
    bb8_1 likes this.
  30. Aras

    Aras

    Unity Technologies

    Joined:
    Nov 7, 2005
    Posts:
    4,770
    Not sure I understood the question. The answer is one of "it works" or "please clarify what do you mean".
     
    Ruchir likes this.
  31. Ignifex

    Ignifex

    Joined:
    May 6, 2015
    Posts:
    11
    I have been having difficulty getting any results using SkinnedMeshRenderer instead of the regular MeshRenderer. The mesh appears unchanged. Perhaps I'm just missing something?

    I was also curious about the use of Dispose on the obtained GraphicsBuffer. I assume that simply releases the handle that is returned from GetVertexBuffer(), to reduce its user count?

    I have put together a simple test snippet. You can apply it on a simple object, such as a Cube, and it should grow the mesh as the ComputeShader writes to the vertex position data.

    Code (CSharp):
    1. using UnityEngine;
    2.  
    3. public class GrowMesh : MonoBehaviour
    4. {
    5.     public ComputeShader GrowMeshComputeShader;
    6.     public bool RunUsingkinnedMeshRenderer = true;
    7.  
    8.     SkinnedMeshRenderer skinnedMeshRenderer;
    9.     Mesh staticMeshCopy;
    10.  
    11.     void Start()
    12.     {
    13.         // Get a copy of the static mesh
    14.         staticMeshCopy = copyMesh(GetComponent<MeshFilter>().sharedMesh);
    15.         // Setup for writing in ComputeShader
    16.         staticMeshCopy.vertexBufferTarget |= GraphicsBuffer.Target.Raw;
    17.  
    18.         if (RunUsingkinnedMeshRenderer)
    19.         {
    20.             // Destroy regular MeshRenderer
    21.             Destroy(GetComponent<MeshRenderer>());
    22.             Destroy(GetComponent<MeshFilter>());
    23.  
    24.             // Replace by SkinnedMeshRenderer
    25.             skinnedMeshRenderer = gameObject.AddComponent<SkinnedMeshRenderer>();
    26.             // Assign for basic rendering
    27.             skinnedMeshRenderer.sharedMesh = staticMeshCopy;
    28.             skinnedMeshRenderer.bounds = staticMeshCopy.bounds;
    29.         }
    30.         else
    31.         {
    32.             GetComponent<MeshFilter>().sharedMesh = staticMeshCopy;
    33.         }
    34.     }
    35.  
    36.     void Update()
    37.     {
    38.         // Grab the mesh VertexBuffer
    39.         GraphicsBuffer vertexBuffer;
    40.         if (RunUsingkinnedMeshRenderer)
    41.             vertexBuffer = skinnedMeshRenderer.GetVertexBuffer();
    42.         else
    43.             vertexBuffer = staticMeshCopy.GetVertexBuffer(0);
    44.         // Safety null check (SkinnedMeshRenderer.GetVertexBuffer() returns null on first frame)
    45.         if (vertexBuffer == null)
    46.             return;
    47.  
    48.         // Run ComputeShader with the VertexBuffer to modify it
    49.         int growMeshKernel = GrowMeshComputeShader.FindKernel("GrowMesh");
    50.         GrowMeshComputeShader.SetBuffer(growMeshKernel, "VertexBuffer", vertexBuffer);
    51.         GrowMeshComputeShader.Dispatch(growMeshKernel, vertexBuffer.count, 1, 1);
    52.  
    53.         // Release the VertexBuffer handle
    54.         vertexBuffer.Dispose();
    55.     }
    56.  
    57.  
    58.  
    59.     Mesh copyMesh(Mesh mesh)
    60.     {
    61.         Mesh copy = new Mesh();
    62.         copy.name = mesh.name + " (Copy)";
    63.         copy.vertices = mesh.vertices;
    64.         copy.normals = mesh.normals;
    65.         copy.tangents = mesh.tangents;
    66.         copy.uv = mesh.uv;
    67.         copy.colors = mesh.colors;
    68.         copy.indexFormat = mesh.indexFormat;
    69.         copy.bounds = mesh.bounds;
    70.         copy.subMeshCount = mesh.subMeshCount;
    71.         for (int i = 0; i < mesh.subMeshCount; ++i)
    72.             copy.SetIndices(mesh.GetIndices(i), mesh.GetTopology(i), i);
    73.         return copy;
    74.     }
    75. }
    76.  
    Code (CSharp):
    1. #pragma kernel GrowMesh
    2.  
    3. RWByteAddressBuffer VertexBuffer;
    4.  
    5. [numthreads(1,1,1)]
    6. void GrowMesh(uint id : SV_DispatchThreadID)
    7. {
    8.     // Read
    9.     float3 position = asfloat(VertexBuffer.Load3(id * 48));
    10.     // Grow slowly
    11.     position *= 1.001;
    12.     // Write
    13.     VertexBuffer.Store3(id * 48, asuint(position));
    14. }
    15.  
     
  32. felbj694

    felbj694

    Joined:
    Oct 23, 2016
    Posts:
    35
    I assume this version of the Mesh Api does not allow a dynamic number of triangles? As the case when you use
    DrawProceduralIndirect providing it with an arguments buffer.
    (This refers to the "Ability to create a Mesh with your own GraphicsBuffers" in the google doc?)
    If that is the case I hope that addition will make the next version, being able to set SubMesh arguments using a
    IndirectArguments graphics buffer and separately set the bounding box.
     
  33. DanielZeller

    DanielZeller

    Joined:
    Nov 18, 2014
    Posts:
    17
    Hi, first off thank you for opening up this awesome API :)

    I got some issues when using SkinnedMeshRenderer.
    I'm using SkinnedMeshRenderer.GetVertexBuffer() to read vertex positions in a scripts LateUpdate.

    I've got two problems with this:
    1. The data returned from GetVertexBuffer() is not actually updated with the skinning(position) from the current frame. Instead I get the data from the previous frame. My guess is that the skinning operation is not actually done yet in LateUpdate, therefore we get the previous frame's data.
    2. It's not possible to write to a SkinnedMeshRenderers ByteBuffer(the one from GetVertexBuffer()). The written changes are always overwritten/ignored. My guess here is that its the skinning operation that is overwriting the data after LateUpdate.

    So I'm wondering if there is any kind of callback you can listen to for when the SkinneMeshRenderer has done its skinning?

    With these two issues I can't really use this api for my purpose.

    A side note is that if I use:
    Code (CSharp):
    1. SkinnedMeshRenderer.BakeMesh(mesh);
    2. var myBuffer = mesh.GetVertexBuffer(0);
    Then I get the current frames vertex data.
     
    Andresmonte likes this.
  34. Aras

    Aras

    Unity Technologies

    Joined:
    Nov 7, 2005
    Posts:
    4,770
    Yeah, both of your issues are most likely caused by the fact that skinning happens later on (this can be verified in the frame debugger - you would see the GPU skinning call being done after your own compute shader dispatch).

    In one test I tried that was using compute shaders to alter the built-in skinning code path in two different ways, I did this:

    1) In order to modify the source mesh before skinning happens (actual use case was: "sparse" modifications of the source mesh, kinda like custom blend shapes) -- just do it in LateUpdate. As you already found out, this happens before skinning.

    2) In order to modify skinned mesh after skinning already happened, I did it in Camera.onPreRender callback. That one happens after skinning is already done, but before rendering started.
     
    Walter_Hulsebos likes this.
  35. DanielZeller

    DanielZeller

    Joined:
    Nov 18, 2014
    Posts:
    17
    Awesome thanks, I'll try that! Cheers
     
    Walter_Hulsebos likes this.
  36. Ignifex

    Ignifex

    Joined:
    May 6, 2015
    Posts:
    11
    Thanks for your reply, Aras.

    I am definitely still missing something however. I tried moving the compute shader callback into the Camera.onPreRender callback and also tried the RenderPipelineManager.beginFrameRendering callback when using an SRP. Both work for the static MeshRenderer, but not for the SkinnedMeshRenderer. In the FrameDebugger, my compute shader call shows up after GPU Skinning.

    I also tried adding vertex weights and bone information to the mesh, in case SkinnedMeshRenderer disables some functionality if these are not available. I can deform the mesh as normal. No changes by the compute shader are visible.

    Any ideas?
     
    Walter_Hulsebos likes this.
  37. Przemyslaw_Zaworski

    Przemyslaw_Zaworski

    Joined:
    Jun 9, 2017
    Posts:
    327
    Another example: delete selected mesh triangles on demand:

    upload_2021-8-9_18-21-32.png

    Code written by Przemyslaw Zaworski

    Code (CSharp):
    1. using System.Collections;
    2. using System.Collections.Generic;
    3. using UnityEngine;
    4. using UnityEngine.Rendering;
    5.  
    6. // Version for meshes with 16-bit indices. Add component to scene, assign compute shader.
    7. // Add some objects to the scene (for example built-in Planes). Play.
    8. // Press left mouse click to remove selected triangle from mesh.
    9. public class DeleteTriangles : MonoBehaviour
    10. {
    11.     public ComputeShader DeleteTrianglesCS;
    12.  
    13.     private GraphicsBuffer _VertexBuffer, _IndexBuffer;
    14.     private int _Dimension = 0, _Count = 0;
    15.     private Mesh _Mesh;
    16.     private Renderer _Renderer;
    17.     private Ray _Ray;
    18.     private string _CurrentTargetName = "";
    19.     private bool _Enable = false;
    20.  
    21.     // Add colliders for raycasts. We want to use and remove triangles on mesh instances, so make mesh copies
    22.     void Start()
    23.     {
    24.         MeshFilter[] filters = FindObjectsOfType<MeshFilter>();
    25.         for (int i = 0; i < filters.Length; i++)
    26.         {
    27.             Mesh mesh = filters[i].sharedMesh;
    28.             filters[i].sharedMesh = Instantiate(mesh);
    29.             filters[i].gameObject.AddComponent<MeshCollider>();
    30.         }
    31.     }
    32.  
    33.     void Load(GameObject target)
    34.     {
    35.         _Dimension = 0;
    36.         _Mesh.vertexBufferTarget |= GraphicsBuffer.Target.Raw;
    37.         _Mesh.indexBufferTarget |= GraphicsBuffer.Target.Raw;
    38.         VertexAttributeDescriptor[] attributes = _Mesh.GetVertexAttributes();
    39.         for (int i = 0; i < attributes.Length; i++) _Dimension += attributes[i].dimension;
    40.         _Count = _Mesh.triangles.Length / 2 ; // 3 : 1.5  = 2
    41.         _Renderer = target.GetComponentInChildren<Renderer>();
    42.         if (_VertexBuffer != null) _VertexBuffer.Dispose();
    43.         _VertexBuffer = _Mesh.GetVertexBuffer(0);
    44.         if (_IndexBuffer != null) _IndexBuffer.Dispose();
    45.         _IndexBuffer = _Mesh.GetIndexBuffer();
    46.     }
    47.  
    48.     void Update()
    49.     {
    50.         if (Input.GetMouseButton(0))
    51.         {
    52.             _Enable = true;
    53.             _Ray = Camera.main.ScreenPointToRay(Input.mousePosition);
    54.         }
    55.         else
    56.         {
    57.             _Enable = false;
    58.         }
    59.         if (_Mesh == null || _Renderer == null) return;
    60.         if (_Mesh.indexFormat == IndexFormat.UInt16) // I will prepare version for 32-bit (UInt32) indices later
    61.         {
    62.             DeleteTrianglesCS.SetVector("_Origin", _Ray.origin);
    63.             DeleteTrianglesCS.SetVector("_Direction", _Ray.direction);
    64.             DeleteTrianglesCS.SetBuffer(0, "_VertexBuffer", _VertexBuffer);
    65.             DeleteTrianglesCS.SetBuffer(0, "_IndexBuffer", _IndexBuffer);
    66.             DeleteTrianglesCS.SetInt("_Count", _Count);
    67.             DeleteTrianglesCS.SetInt("_Dimension", _Dimension);
    68.             DeleteTrianglesCS.SetMatrix("_LocalToWorldMatrix", _Renderer.localToWorldMatrix);
    69.             DeleteTrianglesCS.Dispatch(0, (_Count + 64) / 64, 1, 1);
    70.         }
    71.     }
    72.  
    73.     void FixedUpdate()
    74.     {
    75.         if (_Enable)
    76.         {
    77.             RaycastHit hit;
    78.             if (Physics.Raycast(_Ray.origin, _Ray.direction, out hit))
    79.             {
    80.                 GameObject target = hit.collider.gameObject;
    81.                 if (target.name != _CurrentTargetName)
    82.                 {
    83.                     _CurrentTargetName = target.name;
    84.                     _Mesh = target.GetComponent<MeshFilter>().sharedMesh;
    85.                     Load(target);
    86.                 }
    87.             }
    88.         }
    89.     }
    90.  
    91.     void OnDestroy()
    92.     {
    93.         if (_VertexBuffer != null) _VertexBuffer.Dispose();
    94.         if (_IndexBuffer != null) _IndexBuffer.Dispose();
    95.         MeshFilter[] filters = FindObjectsOfType<MeshFilter>();
    96.         for (int i = 0; i < filters.Length; i++)
    97.         {
    98.             Destroy(filters[i].sharedMesh); // remove instances from memory
    99.         }
    100.     }
    101. }
    Code (CSharp):
    1. #pragma kernel CSMain
    2.  
    3. RWByteAddressBuffer _VertexBuffer, _IndexBuffer;
    4. int _Count, _Dimension;
    5. float3 _Origin, _Direction;
    6. float4x4 _LocalToWorldMatrix;
    7.  
    8. // line (ray) vs triangle intersection test
    9. // ro = ray origin
    10. // rd = ray direction
    11. // a,b,c = triangle vertices coordinates
    12. bool IntersectTriangle(float3 ro, float3 rd, float3 a, float3 b, float3 c)
    13. {
    14.     float3 ab = b - a;
    15.     float3 ac = c - a;
    16.     float3 n = cross(rd, ac);
    17.     float det = dot(ab, n);
    18.     if (abs(det) < 1e-5) return false;
    19.     float invDet = 1.0 / det;
    20.     float3 k = ro - a;
    21.     float u = dot(k, n) * invDet;
    22.     if (u < 0.0 || u > 1.0) return false;
    23.     float v = dot(rd, cross(k, ab)) * invDet;
    24.     if (v < 0.0 || u + v > 1.0) return false;
    25.     return true;
    26. }
    27.  
    28. // encode two 16-bit unsigned numbers to one 32-bit unsigned number
    29. uint AsUint32 (uint a, uint b)
    30. {
    31.     return (a << 16) | b;
    32. }
    33.  
    34. // decode one 32-bit unsigned number to two 16-bit unsigned numbers
    35. uint2 AsUint16 (uint a)
    36. {
    37.     uint x = a >> 16;
    38.     uint y = a & 0xFFFF;
    39.     return uint2(x, y);
    40. }
    41.  
    42. // modulo division
    43. float Mod (float x, float y)
    44. {
    45.     return x - y * floor(x/y);
    46. }
    47.  
    48. [numthreads(64,1,1)]
    49. void CSMain (uint3 id : SV_DispatchThreadID)
    50. {
    51.     if (id.x >= (uint)_Count) return;
    52.     uint2 src = _IndexBuffer.Load2(id.x << 2);
    53.     uint2 a = AsUint16 (src.x);
    54.     uint2 b = AsUint16 (src.y);
    55.     float remainder = Mod(float(id.x), 3.0);
    56.     if (remainder == 0.0)
    57.     {
    58.         float3 localPosA = asfloat(_VertexBuffer.Load3((a.x * _Dimension) << 2));
    59.         float3 localPosB = asfloat(_VertexBuffer.Load3((a.y * _Dimension) << 2));
    60.         float3 localPosC = asfloat(_VertexBuffer.Load3((b.y * _Dimension) << 2));
    61.         float3 worldPosA = mul(_LocalToWorldMatrix, float4(localPosA, 1.0)).xyz;
    62.         float3 worldPosB = mul(_LocalToWorldMatrix, float4(localPosB, 1.0)).xyz;
    63.         float3 worldPosC = mul(_LocalToWorldMatrix, float4(localPosC, 1.0)).xyz;
    64.         if (IntersectTriangle(_Origin, _Direction, worldPosA, worldPosB, worldPosC))
    65.         {
    66.             _IndexBuffer.Store2(id.x << 2, uint2(0u, AsUint32(b.x, 0u)));  
    67.         }
    68.     }
    69.     else if (remainder == 1.0)
    70.     {
    71.         float3 localPosA = asfloat(_VertexBuffer.Load3((a.x * _Dimension) << 2));
    72.         float3 localPosB = asfloat(_VertexBuffer.Load3((b.x * _Dimension) << 2));
    73.         float3 localPosC = asfloat(_VertexBuffer.Load3((b.y * _Dimension) << 2));
    74.         float3 worldPosA = mul(_LocalToWorldMatrix, float4(localPosA, 1.0)).xyz;
    75.         float3 worldPosB = mul(_LocalToWorldMatrix, float4(localPosB, 1.0)).xyz;
    76.         float3 worldPosC = mul(_LocalToWorldMatrix, float4(localPosC, 1.0)).xyz;
    77.         if (IntersectTriangle(_Origin, _Direction, worldPosA, worldPosB, worldPosC))
    78.         {
    79.             _IndexBuffer.Store2(id.x << 2, uint2(AsUint32(0, a.y), 0u));
    80.         }
    81.     }
    82. }
     
    hugokostic, bb8_1, cecarlsen and 3 others like this.
  38. Ignifex

    Ignifex

    Joined:
    May 6, 2015
    Posts:
    11
    I played around with this a bit more and found that using a SkinnedMeshRenderer does work, but the behaviour was not entirely as I expected. To summarize:

    If the mesh assigned to the SkinnedMeshRenderer does not contain skinning weights or blend shapes, the renderer will not initialize skinned rendering at all. This includes the swapping and rendering of the buffers obtained through SkinnedMeshRenderer.Get(Previous)VertexBuffer(), although these can buffers can still be obtained. Instead, the assigned mesh is rendered directly.

    Regular skinning will be calculated as normal, based on the assigned mesh. As Aras wrote above, you can either modify the assigned source mesh before skinning or modify the result mesh after skinning. For the latter option, you can use Camera.onPreRender or RenderPipelineManager.beginFrameRendering when using an SRP.

    If you want to modify the mesh after skinning, but do not want to use regular skinning at all, an easy option is to add vertex weights for a single bone. You can assign the transform of the rendered object itself as the only bone and rootBone for the SkinnedMeshRenderer.

    That leaves me with one minor issue. For me, SkinnedMeshRenderer.GetVertexBuffer() always returns null on the first frame, which means it cannot be modified before rendering the first frame. Is that a limitation of the current implementation?
     
    Last edited: Aug 16, 2021
  39. Trindenberg

    Trindenberg

    Joined:
    Dec 3, 2017
    Posts:
    395
    I'm trying to find a faster way than Mesh.SetVertexBufferData which still seems relatively slow. I tried accessing the GPU direct using GetVertexBuffer and SetData but nothing is updating? I'm probably doing it all wrong!

    Code (CSharp):
    1. var gb = meshes[i].GetVertexBuffer(0);
    2.                 gb.SetData(meshVerticesIN[i]);
    3.                 gb.Release();
     
    Last edited: Aug 15, 2021
  40. Trindenberg

    Trindenberg

    Joined:
    Dec 3, 2017
    Posts:
    395
    I also want to try something like this but I think it's also probably wrong don't want to break anything! Not sure if size means size of type or size of data in bytes. Unity docs say that size is size! meshVerticesIN is NativeArray<Vector3>

    Code (CSharp):
    1. var p = (float3*) meshes[i].GetNativeVertexBufferPtr(0);
    2.                
    3.                 UnsafeUtility.MemCpy(meshVerticesIN[i].GetUnsafePtr(), p, sizeof(float3));
     
  41. DanielZeller

    DanielZeller

    Joined:
    Nov 18, 2014
    Posts:
    17
    Thanks for posting your findings, that's usefully info :)
     
  42. chris_hellsten

    chris_hellsten

    Joined:
    May 5, 2014
    Posts:
    12
    Awesome!

    "Ability to create a Mesh with your own GraphicsBuffers -- i.e. you create vertex/index buffers, and then create a Mesh that does not manage/own them at all, just “wraps” them into an object that looks like a Mesh to everything else in Unity. Conceptually this is similar to Texture2D.CreateExternalTexture"
    +1 for me on this, I would get huge value from the ability to create meshes from my own buffers and have Unity not try and manage them at all. I'm excited that work is happening in this area!
     
    felbj694 and vx4 like this.
  43. burningmime

    burningmime

    Joined:
    Jan 25, 2014
    Posts:
    845
    Does SkinnedMeshRenderer.GetVertexBuffer() allocate a new managed unity object every time it is called? It seems I need to call Release() or Dispose() on it each frame. I haven't checked memory profiler yet, but seems like that should not be needed.

    Anyways, I got this working to generate stencil shadows* in a compute shader; will try to clean it up and share the project eventually:

    upload_2021-8-31_15-13-53.png

    * (yeah I know, 1998 called and wants their tech back)
     
    Last edited: Sep 1, 2021
    MaxEden, hippocoder and FernandoMK like this.
  44. Aras

    Aras

    Unity Technologies

    Joined:
    Nov 7, 2005
    Posts:
    4,770
    Yes
     
    burningmime and Walter_Hulsebos like this.
  45. Aras

    Aras

    Unity Technologies

    Joined:
    Nov 7, 2005
    Posts:
    4,770
    You could, but you'd have to keep in mind that if motion vectors are on, then you'd need to store two graphics buffer objects yourself and alternate between them each frame. If skinned motion vectors are not on, then it should be the same buffer always, for the same skinned mesh object.
     
    burningmime likes this.
  46. joshuacwilde

    joshuacwilde

    Joined:
    Feb 4, 2018
    Posts:
    725
    @Aras I would like to bind a vertex buffer and access it from a DrawProceduralIndirect() call. Is there anyway to do this? I am generating a variable amount of indices per frame. I am using a ComputeBuffer right now instead of a vertex buffer, but this leads to slow memory access in the shader (I assume because I can't take advantage of hardware vertex fetching).
     
    Last edited: Sep 9, 2021
  47. Przemyslaw_Zaworski

    Przemyslaw_Zaworski

    Joined:
    Jun 9, 2017
    Posts:
    327
    With Mesh.GetVertexBuffer(), you don't need to use DrawProceduralIndirect(), because you have direct access to mesh data. Also, documentation says:

    https://docs.unity3d.com/2021.2/Documentation/ScriptReference/Mesh.GetVertexBuffer.html

    that it is better to use ByteAddressBuffer / RWByteAddressBuffer than StructuredBuffer / RWStructuredBuffer.

    Differences between these approaches you can see with these examples (Marching Cubes algorithm):

    - previous approach, with Graphics.DrawProceduralIndirectNow to render geometry:
    https://github.com/przemyslawzaworski/Unity-Procedural-Rock-Generation

    - new approach, with no Graphics.DrawProceduralIndirect:
    https://github.com/keijiro/ComputeMarchingCubes
     
  48. joshuacwilde

    joshuacwilde

    Joined:
    Feb 4, 2018
    Posts:
    725

    Yes, but from what I understood, there is no way to set the index count from a compute shader when just using a mesh. Or is there?
     
  49. cecarlsen

    cecarlsen

    Joined:
    Jun 30, 2006
    Posts:
    858
    I'll like to know this too. From the examples I've seen both vertex and index buffers are accessed in ComputeShaders as RWByteAddressBuffer. In Keijiro's marching cube example the number of indices vary per frame, but from what I can tell he just sets the unused indices to zero. But this means they are still parsed when the mesh is rendered, right?
    https://github.com/keijiro/ComputeMarchingCubes/blob/main/Assets/MarchingCubes/MarchingCubes.compute
     
  50. joshuacwilde

    joshuacwilde

    Joined:
    Feb 4, 2018
    Posts:
    725
    Yes they are still "rendered". If the triangles are zero-area, they will get tossed out after the vertex shading stage though, so there will be no fragment time spent, but still vertex time spent of course.