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. How can we improve our URP documentation to cover your needs? Give us your feedback. Take our survey and let us know.
    Dismiss Notice

Can I send data as `W` via vertex data?

Discussion in 'Shaders' started by imaginaryhuman, Dec 2, 2011.

  1. imaginaryhuman

    imaginaryhuman

    Joined:
    Mar 21, 2010
    Posts:
    5,765
    It seems that mesh.vertices is a Float3 with xyz fields, but in the vertex shader I often see the `position` set up as a float4. I know there is a `w` field. It is possible to send values to the vertex shader via the W field by uploading Float4 vertex data somehow? I plan to use it to store extra data in an orthographic shader so the perspective divide etc doesn't matter.
     
  2. imaginaryhuman

    imaginaryhuman

    Joined:
    Mar 21, 2010
    Posts:
    5,765
    Does anyone know about this? Otherwise I guess I'll have to send in data via texcoord2 or something.
     
  3. aubergine

    aubergine

    Joined:
    Sep 12, 2009
    Posts:
    2,812
    w is already used by the application for scale. It is automatically calculated to get homogenous pos as x/w, y/w, z/w (Im not sure though but it should be)
    You better use shader.setfloat..etc
     
  4. Alesk

    Alesk

    Joined:
    Jul 15, 2010
    Posts:
    321
    Hi,

    I'm also looking for a way to add additionnal per vertex data to be used in a shader, is it possible ?
     
  5. Daniel_Brauer

    Daniel_Brauer

    Unity Technologies

    Joined:
    Aug 11, 2006
    Posts:
    3,356
    I think this is it for now:
    Code (csharp):
    1.  
    2. struct appdata_full {
    3.     float4 vertex : POSITION;
    4.     float4 tangent : TANGENT;
    5.     float3 normal : NORMAL;
    6.     float4 texcoord : TEXCOORD0;
    7.     float4 texcoord1 : TEXCOORD1;
    8.     fixed4 color : COLOR;
    9. };
     
  6. Cameron_SM

    Cameron_SM

    Joined:
    Jun 1, 2009
    Posts:
    912
    If you need all/most of those to begin with or you have more custom per-vertex data to pass through than you have spare fields to hijack you can often get away with encoding sets of data into single floats, unpacking on the GPU is usually only a few instructions (a dot or a frac). Keep in mind GPUs are parallel processors so a dot(vec3) is a single instruction, but would be multiple instruction on a CPU. You'll loose precision, but for most effects you can often get away with a bit of precision error.

    Here's some code (including test cases) I use for packing and unpacking Vec3 normals into a single float

    C#
    Code (csharp):
    1. // Packs a normalised direction vector into a single float
    2. float PackNormal(Vector3 normal) {
    3.  
    4.     // Scale  Bias values to 8 bit bytes in range of 0 to 255
    5.     byte x = ConvertByte(normal.x);
    6.     byte y = ConvertByte(normal.y);
    7.     byte z = ConvertByte(normal.z);
    8.  
    9.     uint  packedByte  = (uint)((x << 16) | (y << 8) | z);
    10.     float packedFloat = (float)(((double)packedByte) / ((double) (1 << 24)));
    11.  
    12.     return packedFloat;
    13.  
    14. }
    15.  
    16.  
    17.  
    18.  
    19. // UnPack normal from float
    20. Vector3 UnPackNormal(float src) {
    21.  
    22.     Vector3 output = Vector3.zero;
    23.  
    24.     // Unpack to 0...1 range
    25.     output.x = Frac(src);
    26.     output.y = Frac(src * 256.0f);
    27.     output.z = Frac(src * 65536.0f);
    28.  
    29.     // Bias to -1..1 range
    30.     output = (output * 2f) - Vector3.one;
    31.  
    32.     return output;
    33.  
    34. }
    35.  
    36.  
    37.  
    38. void TestPackNormal(Vector3 normal) {
    39.  
    40.     normal.Normalize();
    41.  
    42.     float f = PackNormal(normal);
    43.     Vector3 result = UnPackNormal(f).normalized;
    44.     Vector3 error  = normal - result;
    45.  
    46.     string s = "";
    47.  
    48.     s += "Testing: (" + normal.x + ", " + normal.y + ", " + normal.z + ")\n";
    49.     s += "Result : (" + result.x + ", " + result.y + ", " + result.z + ")\n";
    50.     s += "Error  : (" + error.x  + ", " + error.y  + ", " + error.z  + ")\n";
    51.  
    52.     Debug.Log(s);
    53.  
    54. }
    55.  
    56.  
    57. // Helper method that converts a float in the
    58. // range(-1..1) to an 8 bit byte in range(0..255)
    59. byte ConvertByte(float x) {
    60.  
    61.     x = (x + 1.0f) * 0.5f;   // Bias
    62.     return (byte)(x*255.0f); // Scale
    63.  
    64. }
    65.  
    66.  
    67.  
    68. // Helper method to emulate CG's frac(x)
    69. float Frac(float x) {
    70.  
    71.     return x - Mathf.Floor(x);
    72.  
    73. }

    And the CG code to unpack it:
    Code (csharp):
    1. inline float3 UnPackNormal(float src) {
    2.  
    3.     return (frac(float3(1.0f, 256.0f, 65536.0f) * src) * 2) - 1;
    4.  
    5. }
    Note, Vector3 MUST be normalized before packing. The error margin is fairly acceptable for most use cases, example tests:

    Code (csharp):
    1. [COLOR="#4169e1"]Testing: (0, 1, 0)
    2. Result : (-1.54383E-05, 0.9999692, -0.007843136)
    3. Error  : (1.54383E-05, 3.0756E-05, 0.007843136)
    4.  
    5. Testing: (0.7071068, 0, 0.7071068)
    6. Result : (0.7090818, -0.001206982, 0.7051252)
    7. Error  : (-0.00197506, 0.001206982, 0.001981616)
    8.  
    9. Testing: (0.5773503, 0.5773503, 0.5773503)
    10. Result : (0.5794141, 0.57939, 0.5732247)
    11. Error  : (-0.00206387, -0.00203979, 0.004125595)
    12.  
    13. [COLOR="#8b0000"]Testing: (0, 0, 0)
    14. Result : (-0.4090526, -0.4106318, -0.8148972)
    15. Error  : (0.4090526, 0.4106318, 0.8148972)[/COLOR]
    16.  
    17. Testing: (-0.7071068, -0.7071068, 0)
    18. Result : (-0.7084471, -0.7057208, -0.007797684)
    19. Error  : (0.00134033, -0.001385927, 0.007797684)[/COLOR]
    20.  
    21.  
    Note how the (0,0,0) test fails because it's not a unit length vector.

    Edit: Also note as Daniel Brauer posted, everything comes though into the vertex shader as 32bit floating point numbers except for vertex colors (please correct me if I'm wrong). So don't try to encode anything into a vertex colour as each component is an 8bit value (0-255 integer precision).
     
    Last edited: Dec 14, 2011
    Alex_May likes this.
  7. Alesk

    Alesk

    Joined:
    Jul 15, 2010
    Posts:
    321
    Ok thanks,

    So I could use the tangent for example to store the index of each vertex ?
     
  8. Cameron_SM

    Cameron_SM

    Joined:
    Jun 1, 2009
    Posts:
    912
    Yeah, you can put anything you want in there, as long as it's not used by the shader for something else - eg tangents are used for bump-mapping. Also, if you're doing simple quad bill-boarding transformations on the vertex shader you can probably infer vertex index from uv coords.
     
unityunity