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): Shader "Custom/NewShader" { Properties { _MainTex ("Base (RGB)", 2D) = "white" {} } SubShader { Tags { "RenderType"="Opaque" } LOD 200 CGPROGRAM #pragma surface surf Lambert vertex:vert #include "UnityCG.cginc" float4x4 tet[10]; // The x,y,z coordinate of the 4 nodes of a tetrahedron void vert (inout appdata_full v) { int i = int(v.vertex.w); float4 p = v.vertex; p.w = 1 - p.x - p.y - p.z; v.vertex = mul(tet[i], p); } sampler2D _MainTex; struct Input { float2 uv_MainTex; }; void surf (Input IN, inout SurfaceOutput o) { half4 c = tex2D (_MainTex, IN.uv_MainTex); o.Albedo = c.rgb; o.Alpha = c.a; } ENDCG } FallBack "Diffuse" } 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): float4x4 _tet0; float4x4 _tet1; float4x4 _tet2; ... static float4x4 arr[10] = {_tet0, _tet1, _tet2, ... }; 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): float4x4 mat; if (i == 0) mat = _tet0; if (i == 1) mat = _tet1; if (i == 2) mat = _tet2; ... 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.
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
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?
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.