Search Unity

Vertex shader program indexing

Discussion in 'Shaders' started by hellcats2, Nov 18, 2011.

  1. hellcats2

    hellcats2

    Joined:
    Jan 4, 2011
    Posts:
    16
    Hi Unity Experts.

    I'm trying to write a vertex shader to deform meshes embedded in a deformer object. The deformer is a tetrahedral tiling where the node positions can be animated (either manually, or from say a finite element simulation). The Mesh is embedded inside the deformer so that nothing sticks out. I convert the Mesh positions to barycentric coordinates within a particular tetrahedron, and store the tetrahedron index in the 'w' component (sharp eyed readers may notice that barycentric coords. for tetrahedra require 4 components, and I'm only allowing 3 (the xyz position values). But the 4th component may be computed from 1-x-y-z, so I can use w as the tet index). So the problem I have is how to do indexing with the w value. There are two possibilities: vertex texture fetch, or indexing into a uniform array. The first is disallowed with Unity, but the second seems to work just fine.

    Code (csharp):
    1. Shader "Custom/NewShader" {
    2.     Properties {
    3.         _MainTex ("Base (RGB)", 2D) = "white" {}
    4.     }
    5.     SubShader {
    6.         Tags { "RenderType"="Opaque" }
    7.         LOD 200
    8.        
    9.         CGPROGRAM
    10.         #pragma surface surf Lambert vertex:vert
    11.         #include "UnityCG.cginc"
    12.        
    13.         float4x4 tet[10];    // The x,y,z coordinate of the 4 nodes of a tetrahedron
    14.  
    15.         void vert (inout appdata_full v)
    16.         {
    17.             int i = int(v.vertex.w);
    18.             float4 p = v.vertex;
    19.             p.w = 1 - p.x - p.y - p.z;
    20.             v.vertex = mul(tet[i], p);
    21.         }
    22.        
    23.         sampler2D _MainTex;
    24.  
    25.         struct Input {
    26.             float2 uv_MainTex;
    27.         };
    28.  
    29.         void surf (Input IN, inout SurfaceOutput o) {
    30.             half4 c = tex2D (_MainTex, IN.uv_MainTex);
    31.             o.Albedo = c.rgb;
    32.             o.Alpha = c.a;
    33.         }
    34.         ENDCG
    35.     }
    36.     FallBack "Diffuse"
    37. }
    38.  

    Except for one little problem: how to fill the uniform array "tet[10]" from the game engine with the animated deformer coordinates.

    Material.SetMatrix(string name, Material4x4 matrix) will assign a value for a uniform property, but it doesn't support arrays :(. I tried to create a separate uniform for each matrix and then initialize an array:

    Code (csharp):
    1. float4x4 _tet0;
    2. float4x4 _tet1;
    3. float4x4 _tet2; ...
    4.  
    5. static float4x4 arr[10] = {_tet0, _tet1, _tet2, ... };
    6.  
    But then I get a compilation error that "Profile requires arrays with non-constant indexes to be uniform". Fine. So I remove "static", but then I get "uniform initializers must be literal constants". Arggh. (note, I tried #pragma version 3.0 but no joy).

    So I am left with just one option: a bunch of if stmts

    Code (csharp):
    1. float4x4 mat;
    2. if (i == 0) mat = _tet0;
    3. if (i == 1) mat = _tet1;
    4. if (i == 2) mat = _tet2;
    5. ...
    6.  
    Can you say "ughh"?

    Yes this works, but wow is it slow. Probably faster to just do the deformation on the CPU and update the meshes every frame, but that isn't so great if the meshes are fairly complex.

    So I guess I'm strongly voting for either vertex texture fetch, or a way to set a uniform variable with an array argument.
     
  2. hellcats2

    hellcats2

    Joined:
    Jan 4, 2011
    Posts:
    16
    Further reading of the generated shader assembly code shows that array uniforms are emitted as a series of individual variables with an index appended (e.g. "arr0", "arr1", "arr2", etc). This means you can write to them from Unity (but only one at a time). It would be more efficient to have the ability to set uniform variables with an array, but this at least allows me to continue working :)
     
  3. thegreatzebadiah

    thegreatzebadiah

    Joined:
    Nov 22, 2012
    Posts:
    836
    Was there ever a better solution than this? And is unity actually sending the values one at a time to the graphics card, or if they are being batched somehow. And is this going to cause a performance hit?
     
  4. Dolkar

    Dolkar

    Joined:
    Jun 8, 2013
    Posts:
    576
    I'm pretty sure it just updates an internal array of sorts that it then passes to the GPU once the material is actually being used. Unless you're sending a huge array, it should be quite fast as well. All the textures are going through the same pipeline and they are infinitely bigger.