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. Dismiss Notice

Question SetVertexBufferParams and separate arrays for Vertices, Normals, UVs, etc.

Discussion in 'General Graphics' started by cubrman, Jul 18, 2023.

  1. cubrman

    cubrman

    Joined:
    Jun 18, 2016
    Posts:
    403
    I have my mesh layed out in memory like so:

    Vector3[] Positions;

    Vector3[] Normals;

    Vector2[] UVs,

    And indices, obviously.
    Can I somehow feed this data to a Mesh class and render my mesh WITHOUT reorganizing data in memory?

    Documentation suggests I MUST create an array of structs, each holding
    Vector3 Position;
    Vector3 Normals;
    Vector2 UVs;

    Which would mean copying data in memory, which I am trying to avoid.
    Is there another way?
     
  2. c0d3_m0nk3y

    c0d3_m0nk3y

    Joined:
    Oct 21, 2021
    Posts:
    547
    You could use the legacy API and assign them to Mesh.vertices, Mesh. normals and Mesh.uv.

    However, the data will most likely be copied internally because all graphic APIs expect the data to be an array of structs. You can override that by using multiple vertex buffers internally (that's how UE does it with their VertexFactory) but as far as I am aware Unity uses the classic approach with one vertex buffer per mesh.
     
  3. cubrman

    cubrman

    Joined:
    Jun 18, 2016
    Posts:
    403
    @c0d3_m0nk3y this is very important to me: so are you sure that all graphic APIs expect and array of structs and there is no other way? Where can I read more about it AND about the approach UE uses?
     
  4. c0d3_m0nk3y

    c0d3_m0nk3y

    Joined:
    Oct 21, 2021
    Posts:
    547
    I didn't say there was no other way. You can bind multiple vertex buffers to simulate SOAs if you have full control of the engine. There is a way to interleave multiple vertex buffers. This is usually used for instancing. One vertex buffer has the instance positions and the other one has the vertex positions. Usually you'd configure it such that for every new instance you increase one element in the instance buffer but you can just step through them at the same rate to simulate SOAs.

    See InstanceDataStepRate and InputSlot
    https://learn.microsoft.com/en-us/windows/win32/api/d3d12/ns-d3d12-d3d12_input_element_desc
    https://learn.microsoft.com/en-us/w...g-guide-input-assembler-stage-getting-started
    (Disclaimer: I haven't tried this personally)

    This is a PIX capture from Unreal Engine. As you can see, it is binding 4 vertex buffers.
    upload_2023-7-18_9-41-26.png

    You find a lot of debates whether that is actually increasing performance but it does allow you to ignore certain attributes if you don't need them (like in the depth pre-pass).

    You can find UE's VertexFactory implementation here (have to sign up for github access first)
    https://github.com/EpicGames/Unreal...time/RenderCore/Private/VertexFactory.cpp#L22

    See also
    https://medium.com/realities-io/cre...-explanation-of-vertex-factories-4a6fd9fd58f2
    https://anteru.net/blog/2016/storing-vertex-data-to-interleave-or-not-to-interleave/
     
    Last edited: Jul 18, 2023
    cubrman likes this.
  5. cubrman

    cubrman

    Joined:
    Jun 18, 2016
    Posts:
    403
    @c0d3_m0nk3y thanks for a comprehensive reply, it was very helpful.
     
    c0d3_m0nk3y likes this.
  6. kenamis

    kenamis

    Joined:
    Feb 5, 2015
    Posts:
    386
    Each vertex buffer or stream, is interleaved with the attributes set for that stream on the mesh object. That's why it makes sense to use a struct, because the layout is the same, but you could manually write bit by bit to the vertex buffer if you know the right stride.

    Using the simple Mesh api for .vertices, .normals, etc automatically write the interleaved data for you. The advanced mesh api and MeshData api allow you to write to those buffers directly and if you are aware of your stride, you could write directly to the correct indices. If you really want each of those attributes to be contiguous, then you could set them to separate streams using MeshData

    But, if the original question is about your data in a certain layout to copy to a mesh, why isn't it already written and, presumably, serialized as a mesh object?
     
  7. cubrman

    cubrman

    Joined:
    Jun 18, 2016
    Posts:
    403
    @kenamis because we are receiving data from a different program in a form of a number of separate arrays in an unmanaged memory. Thanks for suggesting MeshData! Do they work with NativeArray-s?
     
  8. kenamis

    kenamis

    Joined:
    Feb 5, 2015
    Posts:
    386
  9. cubrman

    cubrman

    Joined:
    Jun 18, 2016
    Posts:
    403
    @kenamis hey, I saw the ability to set the stream index, but I can't quite find a way to specify to Unity which stream should be used as normal and which - as positions (for instance). Here is how I do it:


    Code (CSharp):
    1.                             if (meshToCreate.VerticesLength > 0)
    2.                             {
    3.                                 NativeArray<Vector4> vertices = ParseData<Vector4>((void*)meshToCreate.Vertices, meshToCreate.VerticesLength);
    4.  
    5.                                 MeshToRender.SetVertexBufferParams(vertices.Length, new VertexAttributeDescriptor(VertexAttribute.Position,
    6.                                     VertexAttributeFormat.Float32, 4, stream: 0));
    7.  
    8.                                 MeshToRender.SetVertexBufferData(vertices, 0, 0, vertices.Length, stream: 0,
    9.                                     MeshUpdateFlags.DontValidateIndices);
    10.                             }
    11.  
    12.                             if (meshToCreate.NormalsLength > 0)
    13.                             {
    14.                                 NativeArray<Vector4> normals = ParseData<Vector4>((void*)meshToCreate.Normals, meshToCreate.NormalsLength);
    15.  
    16.                                 MeshToRender.SetVertexBufferParams(normals.Length, new VertexAttributeDescriptor(VertexAttribute.Normal,
    17.                                                 VertexAttributeFormat.Float32, 4, stream:1));
    18.  
    19.                                 MeshToRender.SetVertexBufferData(data:normals, 0, 0, normals.Length, stream: 1, MeshUpdateFlags.DontValidateIndices);
    20.                             }
    Sadly with this code the mesh won't render at all.
     
  10. kdchabuk

    kdchabuk

    Joined:
    Feb 7, 2019
    Posts:
    47
    IIRC you need to set all the attributes in 1 function call. For example
    Code (CSharp):
    1. MeshToRender.SetVertexBufferParams(vertices.Length,
    2.   new VertexAttributeDescriptor(VertexAttribute.Position, VertexAttributeFormat.Float32, 4, stream: 0),
    3.   new VertexAttributeDescriptor(VertexAttribute.Normal, VertexAttributeFormat.Float32, 4, stream:1));
    4.  
     
    Last edited: Jul 19, 2023
    kenamis and cubrman like this.
  11. cubrman

    cubrman

    Joined:
    Jun 18, 2016
    Posts:
    403
    @kdchabuk wow that actually worked! Thanks!
     
    kenamis likes this.