Search Unity

Passing extra vertex data to a shader

Discussion in 'Shaders' started by ZoltanErdokovy, Jul 14, 2013.

  1. ZoltanErdokovy

    ZoltanErdokovy

    Joined:
    Aug 23, 2012
    Posts:
    102
    In UDK there is a thing called "Dynamic Parameter" for particles which acts as a second
    vertex color: It's generated by the particle system and passed down to the vertex and fragment
    shaders where it can be used freely. Can I do something similar in Unity? I'm generating a mesh
    with this extra vertex data but I'm not sure how to pass it to a custom shader. ShaderLab's
    appdata_full seems to have a fixed set of parameters.
     
  2. brianasu

    brianasu

    Joined:
    Mar 9, 2010
    Posts:
    369
    You can define whatever structure you need but you are limited to what you can pass in via the Mesh class which is vertex position, 2 texture coordinates and 1 set of vertex colors.
     
  3. hippocoder

    hippocoder

    Digital Ape

    Joined:
    Apr 11, 2010
    Posts:
    29,723
    Would tangents work?
     
  4. ZoltanErdokovy

    ZoltanErdokovy

    Joined:
    Aug 23, 2012
    Posts:
    102
    So I must use the existing dataset... As I see it these are the options:

    - Encode two float values in each vertex color channel. (Lost precision in both vertex color and dynamic parameter data.)
    - Encode two float values in each UV2 coordinate. (Lost dyna param precision and no second UV.)
    - Store data in vertex tangent. (Shader has to calculate tangents if necessary.)

    I guess I can implement all of them and use the one best fit to a given situation.

    Thanks guys!
     
  5. Farfarer

    Farfarer

    Joined:
    Aug 17, 2010
    Posts:
    2,249
    You shouldn't lose precision with UV2, just you're down to 2 channels.
     
  6. ZoltanErdokovy

    ZoltanErdokovy

    Joined:
    Aug 23, 2012
    Posts:
    102
    Indeed, #2 is either lossless RG or lossy RGBA or lossy UV2 and lossy RG... and I thought I had no options... :)
    Btw, it's a shame that the UVs arrive at the shader as float4's with the last two components left unused.
     
  7. Stephan-B

    Stephan-B

    Joined:
    Feb 23, 2011
    Posts:
    2,269
    I am trying to pass data to a shader using either the normal or tangent but it seems like Unity is altering these values before I get to read them in the Shader. Is there a way to get this data into the Shader but to not have Unity somehow alters these values?
     
    landon912 likes this.
  8. ZoltanErdokovy

    ZoltanErdokovy

    Joined:
    Aug 23, 2012
    Posts:
    102
    Are you generating the geometry yourself or trying to change an imported asset?
    My use case was creating a mesh from scratch and I had no trouble setting normals
    and tangents to anything I wanted.
     
  9. Stephan-B

    Stephan-B

    Joined:
    Feb 23, 2011
    Posts:
    2,269
    I discovered that Unity alters the values in the normal / tangent channel when Dynamic Batching is enabled. If I disable dynamic batching, then my values are not altered.

    Right now my Shader doesn't use normal or tangents (yet) so I was hoping to be able to pass some value to the shader inside of those and then strip that value inside and put that back to zero.

    In addition, since the mesh I create is only (X, Y), I was hoping that alternatively I could use Z and then similarly get that value in the shader and then reset it to zero but again Unity alters this value. I realize in the case of this Z, my Z inside the script is zero but that is in local space vs. that vertex.z inside the shader is now in worldspace but even (I think) I account for that, Unity seems to be altering the values (i suspect this is also related to dynamic batching).

    Maybe I am doing something wrong.
     
    landon912 likes this.
  10. Marco-Sperling

    Marco-Sperling

    Joined:
    Mar 5, 2012
    Posts:
    620
    I am looking for a way to get exactly this: the dynamic parameters of the UDK Cascade (particle) editor inside Unity's Shuriken ParticleSystem.
    How on earth can I get to the core of a particle - its quad mesh and inject additional data?
    Is that even possible? Or is this stuff hidden and locked away?
    Can we expand the Shuriken editor with custom modules that control the additional data with curves/constants/randoms?

    The thread above seems to have nothing more to do with Cascade than the initial idea of adding a secondary vertex color set to an arbitrary mesh - not to particles in the first place as the term "dynamic parameters" implies.

    So the question still stands: what can be done in terms of mimicing UDK Cascade's dynamic Parameters inside Shuriken?

    edit:
    This question apparantly pops up from time to time.

    edit2:
    Since the particle quads seem to be hidden inside the core I am going another route now. I use the individual channels of the color property to drive the information needed inside the custom particle shader. And I use one channel to make a lookup inside a color gradient texture.
    While this is not ideal it should work. Sadly Shuriken offers no method to modifiy the individual color channels via curves. That would be an awesome addition - as would be a set of 4 additional dynamic parameters.
     
    Last edited: Feb 15, 2014
  11. landon912

    landon912

    Joined:
    Nov 8, 2011
    Posts:
    1,579
    I know this is a necro. But it seems stupid to state exactly this in a new thread. I've hit the same road block. Everyone says that you can use the tangents channel. Yet, my values go out of wack. Curiously, the shader works in the scene view, but has awful artifacts in the game view.

    Turning off batching is simply not an option. I can't force my customers to disable such important features.

    Am I missing something? Is it just impossible until Unity 5.0 ships?
     
    Last edited: Nov 8, 2014
  12. Stephan-B

    Stephan-B

    Joined:
    Feb 23, 2011
    Posts:
    2,269
    In order for objects to batch, the need to have uniform scale. So you can tweak the scale (slighly) in order to break batching and I believe that may result in your values passing in Tangent channel to not be modified by Unity.

    Would be nice to have a new property on the renderer like renderer.isBatching = false; That way we could specify which mesh are to be excluded instead of having to resort to funky stuff.

    I am certainly looking forward to having more per vertex streams in 5.0.
     
    landon912 likes this.
  13. landon912

    landon912

    Joined:
    Nov 8, 2011
    Posts:
    1,579
    Thanks for the little hack. I'll check it out.
     
  14. Alex_May

    Alex_May

    Joined:
    Dec 29, 2013
    Posts:
    198
    Can anyone explain why the data gets mangled in the dynamic batching case? I really want to put some data in the tangent but I just can't. I was packing data into floats, but with tangent and normal I can't even use regular floats. It's like those data slots are basically useless for me. Incredibly frustrating.
     
  15. IzzySoft

    IzzySoft

    Joined:
    Feb 11, 2013
    Posts:
    376
    bump!

    same issue. how to reliably prevent unity from updating the tangents/normals?
     
  16. jbooth

    jbooth

    Joined:
    Jan 6, 2014
    Posts:
    5,461
    As of Unity 5.3, the UV channels can be a float2, float3, or float4 type, allowing you to pack far more information into them than in previous versions.
     
    Alex_May, JibbSmart and IzzySoft like this.
  17. Stephan-B

    Stephan-B

    Joined:
    Feb 23, 2011
    Posts:
    2,269
    I had not notices the changes to the Mesh.SetUVs() function. I wish it made it possible to also use something like Mesh.SetUVs(int channel, Vector4[] uvs);
     
    Last edited: May 22, 2016
  18. Selmar

    Selmar

    Joined:
    Sep 13, 2011
    Posts:
    59
  19. Alex_May

    Alex_May

    Joined:
    Dec 29, 2013
    Posts:
    198
    It'd be great to just completely configure the whole structure, and mark various streams as being normalised or saturated or whatever you need by the time the data hits the shader.
     
    a436t4ataf, Stephan-B and AcidArrow like this.
  20. grizzly

    grizzly

    Joined:
    Dec 5, 2012
    Posts:
    357
    A couple of pointers that may resolve some of the issues you guys are facing.

    Firstly, dynamic batching combines mesh into a "super" mesh where any previous object space data (eg vertex) is lost as it's translated into world space based on the sum of all the other meshes it combines with. This is particularly noticeable when batching things like billboards. If you wish to disable batching however, there's no need for fancy workarounds such as offset scaling. This can be achieved with the DisableBatching tag.
    Code (CSharp):
    1. Tags{ "DisableBatching"="True" }

    Secondly, for those working with older versions of Unity (<5) and targeting IOS, normals and tangents may be automatically normalized. To prevent this you'll need to add the following pragma to your CG block.
    Code (CSharp):
    1. #pragma glsl_no_auto_normalization

    And lastly, by default Unity is set to automatically compress vertex data. This optimization can adjusted under the "Edit > Project Settings > Player > Other Settings > Vertex Compression" menu.
     
    a436t4ataf and Alex_May like this.
  21. quizcanners

    quizcanners

    Joined:
    Feb 6, 2015
    Posts:
    109
    Hello, I've made an asset which lets me package edit some data into mesh. Used it to make a Bevel Shader, and Mesh Baked Shadows. I know it's a super old thread, but every now and then I try to check if what I'm doing is not totally unnecessary and outdated.
    Right now I would love a way to make some sort of custom batching, or an option to transform some parameters stored in UV.xyz to Wolrd space as if they were vectors or position. Anyone tried to make custom batching?

    Asset name is Playtime Painter if anyone is interested. Free on GitHub: https://github.com/quizcanners/Tools
     
    Last edited: Nov 4, 2019
  22. a436t4ataf

    a436t4ataf

    Joined:
    May 19, 2013
    Posts:
    1,933
    Custom Vertex Attributes work fine - you can avoid the pain and suffering of trying to second-guess Unity's mangling of your data.

    This was a top hit on google for "unity custom vertex attributes", and made it sound impossible, but it's not :). After some digging and trial and error, here's my simple example that worked first time.

    Purpose: attach custom vertex attributes to a single material, and then use them in the shader.


    Firstly, you need to write some C# script to populate the buffer. e.g.

    Code (CSharp):
    1.  
    2. public class PopulateVertexAttributes : MonoBehaviour
    3. {
    4. [ContextMenu( "PopulateMaterials" )]
    5. public void DEBUG_PopulateMaterials()
    6. {
    7.   int numVerticesTotal = ... // how many verts in your mesh
    8.  
    9.   int bytesPerFloat = 4;
    10.  
    11.   int floatsInCustomAttribute = 3; ... // how many floats in your struct. I use 3 in this example
    12.   int structSize = bytesPerFloat * floatsInCustomAttribute;
    13.   ComputeBuffer cb = new ComputeBuffer( numVerticesTotal, structSize );
    14.   float[,] values = new float[numVerticesTotal,floatesInCustomAttribute];
    15.   for( int i = 0; i < values.GetLength( 0 ); i++ )
    16.   {
    17.    for( int structFloat=0; structFloat<floatsInCustomAttribute; structFloat++)
    18. {
    19. // Fill in your data here. I'm putting [..,0] = 1, [..,1] = 0, [..,2] = 0
    20. // i.e. a float3( 1,0,0 )
    21. // ... which the shader will read as "the color red"
    22.  
    23. if( structFloat == 0 )
    24.    values[ i, structFloat ] = 1f;
    25. else
    26.    values[ i, structFloat ] = 0f;
    27. }
    28.   }
    29.   cb.SetData( values );
    30.   Debug.Log( "Generated data for "+values.Length+" vertices" );
    31.  
    32.   MeshRenderer mr = .. // your mesh renderer
    33.   foreach( Material subMaterial in mr.materials ) // will leak, but works for doing a debug / test
    34.   {
    35.    subMaterial.SetBuffer( "customAttributes", cb );
    36.    Debug.Log( "Added vertex-attributes to material = "+subMaterial );
    37.   }
    38. }
    39. }
    40.  
    Note: I'm leaking materials there, you shoudln't use this code for anything except a basic test.

    Then you need a shader that reads the per-vertex data, something like this:

    Code (CSharp):
    1.  
    2. Shader "CustomVertexAttributes"
    3. {
    4. Properties
    5. {
    6. }
    7. SubShader
    8. {
    9.     Tags
    10.     {
    11.         "Queue"="Geometry"
    12.     }
    13.        
    14.     Pass
    15.     {
    16.         Tags {"LightMode" = "ForwardBase"}
    17.  
    18.         CGPROGRAM
    19.         #pragma target 3.5
    20.         #pragma vertex vert
    21.         #pragma fragment frag
    22.                
    23.         struct CustomVertexAttribute
    24.         {
    25.             float3 testColor; // NB: since this is a float3, we had to specify 3 x floats in the C# code above
    26.         };
    27.         #ifdef SHADER_API_D3D11
    28.             StructuredBuffer<CustomVertexAttribute> customAttributes;
    29.         #endif
    30.        
    31.         struct v2f
    32.         {
    33.             float4 pos : SV_POSITION;
    34.             float3 vertexColor : TEXCOORD0; // so we can do something with the custom color
    35.         };
    36.        
    37.         fixed _ShadowStrength;
    38.         v2f vert (
    39.         uint id : SV_VertexID, // note that we ADD this extra parameter to the vertex shader
    40.         appdata v
    41.         )
    42.         {
    43.             v2f o;
    44.             o.pos = UnityObjectToClipPos (v.vertex);
    45.             o.vertexColor = customAttributes[id].testColor;
    46.             return o;
    47.         }
    48.            
    49.         fixed4 frag (v2f i) : COLOR
    50.         {
    51.             float4 c;
    52.             c.xyz = i.vertexColour.xyz;
    53.             c.a = 1;
    54.             return c;
    55.         }
    56.         ENDCG
    57.     }
    58. }
    59.  
    Populate your material using the C# script, and you should immediately see it render all vertes as red (i.e. using the custom attribute data).
     
    ph3rin and quizcanners like this.
  23. quizcanners

    quizcanners

    Joined:
    Feb 6, 2015
    Posts:
    109
    Thanks. Really useful. It's only for compute shaders and doesn't support batching, am I right?
     
  24. a436t4ataf

    a436t4ataf

    Joined:
    May 19, 2013
    Posts:
    1,933
    This example was specificially for *vertex and fragment* shaders ... it was a little confusing that the terminology used (e.g. "ComputeBuffer") makes it sound like it's compute-shader specific, but it isn't - those appear to be just names that Unity chose becuase their primary reason for adding this support was to facillitate compute shaders.

    Batching is irrelevant for me right now so I didn't test that -- BUT in passing I noticed some solid examples from Unity docs on how to do instanced rendering with manual DrawMeshInstanced etc calls, combined with custom attributes, so I'd probably investigate instancing rather than batching.
     
  25. a436t4ataf

    a436t4ataf

    Joined:
    May 19, 2013
    Posts:
    1,933
    Minor update: for anyone who doesn't care about backwards compatibility, Unity is finally implementing VBO's (only took 15 years...) in 2019.3 - https://docs.google.com/document/d/...WNMxTZq3FZQs5KqPc/edit#heading=h.vyksohcynwk5

    (I'd been watching the thread for a while, but since I have many projects on 2017 and 2018, I needed something that's compatible with the majority of actual projects + developers :)).

    But for posterity, sooner or later anyone finding this thread should be able to go "Hey, I'll just use a VBO!".
     
    ph3rin, LouisHong and hungrybelome like this.
  26. X-BLaZeKiLL-X

    X-BLaZeKiLL-X

    Joined:
    Apr 9, 2013
    Posts:
    6
    I don't know if I am understanding this right but the above API allows you to change the layouts of the pre defined attributes in https://docs.unity3d.com/ScriptReference/Rendering.VertexAttribute.html (e.g changing VertexAttribute.Position to be int instead of float). In my use case I am pre computing 2 bit AO value per vertex, which should be passed to the shader. I don't think I can create a custom vertex attribute of type in

    for this case should I be using the SetBuffer() on a material like mentioned above or is there a way to achieve this using the new mesh api's ?
     
  27. a436t4ataf

    a436t4ataf

    Joined:
    May 19, 2013
    Posts:
    1,933
    One point of the new API is that you can put any combination of anything in any order - exactly as VBOs provided for almost 10+ years, but Unity's engineers did not allow us to do until now. If you can't get it to do that, it's probably a bug, and I suggest you ask about in one of the threads dedicated to the new Mesh system.
     
    quizcanners likes this.