New to shaders questions (non uniform scaling)

I started trying to use shaders yesterday wanting to implement non uniform scaling in a shader, the following is what works in unityscript, every frame the object’s scale either goes up or down a step.

var scale : float = 1.0;
var step : float = 0.1;
var lowerBound : float = 1.0;
var upperBound : float = 5.0;

function Update () {
	var randomchoiceAnim : float = Random.value;
	if (randomchoiceAnim < 0.5)
	{																
		scale += step;
	}
	else
	{
		scale -= step;
	}
	scale = Mathf.Clamp(scale,lowerBound,upperBound);		
	transform.localScale.y = scale;	  
}

I have read a lot of posts but wanted to clarify a few questions I have about shaders.

  1. Is there a way to declare a variable outside the shader then update it every time the shader runs?

as in UnityScript

var myInt : int = 1;

Update()
{
	myInt +=1;
	Debug.Log(myInt); // myInt keeps getting bigger
}
  1. Alternatively is there a way to get hold of the most recently updated vertex positions? So v.vertex.y *= 1.1 would keep scaling everything up. In my attempts it seems that the shader is passed the original vertex positions when it starts up and only has access to those- is this correct?

  2. If neither of the above are possible is there a third way - I really don’t want to have to run my logic in Unityscript and send the scale value using renderer.material.setFloat… this is running on 10k objects and the appeal of shaders was their speed.

  3. If none of the above work _Time seems like the only variable which a shader has access to that is constantly changing. I have found a pseudo random number generator but can’t figure out the correct combination of _Time, possibly fmod and or lerp to produce the desired results- does anyone have any ideas or at least know if this is logically possible? -I’m happy to bang my head against a wall if I know it’s logically possible.

Here is what I’ve got so far

Shader "VertexInputSimple" {
 Properties {
 	_ScaleInput ("_ScaleInput", Float) = 1 // sets up a variable in the inspector that can be accessed from unityscript
    }
  SubShader {

    Pass {
      CGPROGRAM
      #pragma vertex vert
      #pragma fragment frag
      #include "UnityCG.cginc"
	
	  float _ScaleInput;	// sets up variable to be used in the shader

      struct v2f {
          float4 pos : SV_POSITION;
          fixed4 color : COLOR;
      };
      
      float rand_1_05(in float2 uv) // pass it 2 floats and you get a pseudo random float between 0 and 1
	  {
		  float2 noise = (frac(sin(dot(uv ,float2(12.9898,78.233)*2.0)) * 43758.5453));
		  return abs(noise.x + noise.y) * 0.5;
	  }
      
      
      v2f vert (appdata_base v)
      {
          v2f o;
          
          float2 seed = ( _Time , _Time * 100 ); // create a float2 as a seed
          float random = rand_1_05( seed ); // pass the float2 to the function rand_1_05, which returns between 0 and 1
		  
	  float mod = fmod(_Time.y,4.0) + 1; // Time.y is the time in seconds, +1 to bring into range 1 to 5
          
          float value;
                    
          if ( random > 0.5 )
          {
              value = smoothstep(1,5,mod)+1;
          }
          else
          {
              value = smoothstep(5,1,mod+1);
          }
          
          v.vertex.y *= mod;
          //v.vertex.y *= value;
          
          //v.vertex.y *= _ScaleInput; //This way scales according to the value in the inspector ( and is very slow 50ms vs 10ms on 10k objects )
          o.pos = mul (UNITY_MATRIX_MVP, v.vertex);
          o.color.xyz = v.normal * 0.5 + 0.5;
          o.color.w = 1.0;
          return o;
      }

      fixed4 frag (v2f i) : COLOR0 { return i.color; }
      ENDCG
    }
  } 
}

You can pass variables declared/altered in a unity script through the material by making use of renderer.material.SetFloat(incrementedValue) and apply that to your vertices in your shader.

One way or the other, to transform the vertices you will need some incrementing value as transformations applied in the vertex shader are not saved, they’re simply applied to whatever vertex data there is. _Time might also be an option, but if you want to do some other stuff with the incrementing value, passing it yourself is probably your best option.

Thanks Rld_,

Its a shame, I was hoping all the logic could run on the shader however passing a shader a value using setFloat seems to be a little quicker than animating a skinned mesh renderer in unity script.

Another issue with this code is that the pseudo random number generates the same random number for each object.

In case anyone is interested here is the code that calls setFloat()

#pragma strict


// This builds the objects
// Then every update sends setFloat the current scale

var ini_x : int= 100;
var ini_z : int = 10;

var prefab : GameObject;

private var array : Material[];
private var scaleArray : float[];	// store the scale floats

function Start () {

	array = new Material[ini_x * ini_z];	// Stores yet to be instantiated object's material for fast access to the renderer.material.SetFloat
	
	scaleArray = new float[ini_x * ini_z];	// An array of scales for each object
		
	var counter : int = 0;
	
	for ( var x : int = -(ini_x/2); x < (ini_x/2); x++)
	{
		for ( var z : int = -(ini_z/2); z < (ini_z/2); z++)
		{
			var position : Vector3 = Vector3(x,0,z);
			var object = GameObject.Instantiate(prefab, position, Quaternion.identity);
			array[counter] = object.renderer.material;	// place the objects material into an array ( this is quicker than saving the gameobject)
			scaleArray[counter] = 1.0;
			counter +=1;	
		}
	}
}

var step : float = 0.1;
var minBound : float = 1.0;
var maxBound : float = 5.0;

function Update () {
	for (var i : int = 0; i < array.Length; i++)
	{
		var mat : Material = array[i];

		var scale : float = scaleArray[i];	//read from the scale array
		
		var randomchoiceAnim : float = Random.value;
		
		if (randomchoiceAnim < 0.5)
		{																
			scale += step;
		}
		else
		{
			scale -= step;
		}
		
		scale = Mathf.Clamp(scale,minBound,maxBound);		
		
		mat.SetFloat("_ScaleInput", scale);	// set the materials float ( this gets passed into the shader code )

		scaleArray[i] = scale;				//write back to the scale array
	}
}

You could just pass a seed per setFloat to your shader. The same way you’re doing it for scaling.
This way the shader generates different random numbers for each object.

Thanks Cjreek, that makes sense as a way of getting a seed in.

I guess I’d have to send the current scale value along with a seed both using set float, then use get float to get the updated scale back out, this would move the random number generating, if else’ing and Clamp’ing inside the shader… which could(?) be faster…