shader.SetGlobalVector ?

Digging myself deeper and deeper into the ShaderLab hole…

I have a lot of objects with the same shader. The shader needs to know the position of another object.

First attempt has an exposed Vector property for the shader. This works, but with many objects it gets inconvenient:

//shader
Properties {
   _Obstacle ("Obstacle", Vector) = (0,0,0,0)
}

//Object that affects shader
other.renderer.material.SetVector("_Obstacle", transform.position);

Then I remembered global shader properties, but it seems SetGlobalVector is missing :frowning: . I can use SetGlobalFloat for x,y,z, that should work fine, I’m just wondering if there’s a ‘proper’ way of getting a position like this into the shader?

/P

For now use SetGlobalColor, we’ll add SetGlobalVector in the future release.

SetGlobalColor will just work; as vectors and colors internally are the same thing (four floating point numbers); and Colors can have components outside of the usual 0…1 range.

SetGlobalColor works great. Managed to hack the Windy Grass shader to make the grass bend to avoid an obstacle.

Still need to fix issues when the obstacle is inside the grass geometry as this will stretch the grass unnaturally. (Btw, I’m using geometry instead of billboards so I can have the camera close to the grass)

/P

Gee, Patrik, you’ll turn into a shader monster soon! Keep up the good work :slight_smile:

wow that’s really cool - i’m wondering how is the performance using regular meshes instead of billboards?

Looks awesome metervara!

Any chance you could share you code for that? Pretty please? I’d like to learn how to do this stuff in a shader instead of the mesh interface. :wink:

-Jeremy

Here is the most basic version. It has some problems when the obstacle is inside the geometry. One fix is to move the grass slightly away from the obstacle as it gets close, but it looks weird if the ground is visible. Works when you only see the top of the grass though.

It’s a few additions to the standard Windy Grass shader from the Nature pack:

Firts off there’s a global shader color: _Obstacle. Use Shader.SetGlobalColor to get the obstacle position to the shader.

Then the main block (/OBSTACLE AVOIDANCE CALC). The windy grass shader already calculates the world position for vertices so use that and compare with obstacle position to get bend direction vector. Also calculate distance falloff.

Finally we add the bend offset exponentially - v.vertex.xz += …

We could also swap v.texcoord.y for let’s say v.color.r and paint the ‘bendyness’ into the model using vertex colors to have some parts softer than others. Doesn’t really make sense for a simple thing like a grass straw though, but could be cool for more complex models.

Note:
And all this is not really bending, but stretching or skewing so the geometry can look strange sometimes. Feel free to fix :smile:

Shader "Nature/Windy Grass" {
	Properties {
		_Color ("Main Color", Color) = (.5, .5, .5, 0)
		_Color2 ("Fade Color", Color) = (1, .9, .8, 0)
		_MainTex ("Base (RGB) Alpha (A)", 2D) = "white" {}
		_Cutoff ("Base Alpha cutoff", Range (0,1)) = .5
	}
	SubShader {
		Tags { "Queue" = "Transparent" }
		BindChannels {
			Bind "Color", color
			Bind "Vertex", vertex
			Bind "TexCoord", texcoord
		}

		Pass {
			Lighting On
CGPROGRAM
// vertex Vertex

#include "waves.cginc"		// Get in the helper wave functions
#include "UnityCG.cginc"		// Get standard Unity constants

struct Appdata {
	float4 vertex;
	float3 normal; 
	float4 texcoord;
	float4 color;
};

struct v2f {
	float4 pos : POSITION;
	float4 color : COLOR0;
	float4 uv : TEXCOORD0;
	float fog : FOGC; 
}; 

uniform float4 _Color, _Color2, _SkyLight;
uniform float4 _Obstacle; // OBSTACLE AVOIDANCE

v2f Vertex(Appdata v) {
	v2f o;

	const float4 _waveXSize = float4(0.012, 0.02, -0.06, 0.048) * 2;
	const float4 _waveZSize = float4 (0.006, .02, -0.02, 0.1) * 2;
	const float4 waveSpeed = float4 (0.3, .3, .08, .07) * 4;

	float4 _waveXmove = _waveXSize * waveSpeed * 25;
	float4 _waveZmove = _waveZSize * waveSpeed * 25;
	
	// We model the wind as basic waves...

	// Calculate the wind input to leaves from their vertex positions....
	// for now, we transform them into world-space x/z positions... 
	// Later on, we should actually be able to do the whole calc's in post-projective space
	float3 worldPos = mul ((float3x4)_Object2World, v.vertex);
	
	// OBSTACLE AVOIDANCE CALC
	float3 bendDir = normalize (float3(worldPos.x,0,worldPos.z) - float3(_Obstacle.x,0,_Obstacle.z));//direction of obstacle bend
	float distLimit = 3;// how far away does obstacle reach
	float distMulti = (distLimit-min(distLimit,distance(float3(worldPos.x,0,worldPos.z),float3(_Obstacle.x,0,_Obstacle.z))))/distLimit; //distance falloff
	//OBSTACLE AVOIDANCE END
	
	// This is the input to the sinusiodal warp
	float4 waves;
	waves = worldPos.x * _waveXSize;
	waves += worldPos.z * _waveZSize;

	// Add in time to model them over time
	waves += _Time.x * waveSpeed;

	float4 s, c;
	waves = frac (waves);
	FastSinCos (waves, s,c);

	float waveAmount = v.texcoord.y;
	s *= waveAmount;

	// Faste winds move the grass more than slow winds
	s *= normalize (waveSpeed);

	s = s * s;
	float fade = dot (s, 1.3);
	s = s * s;
	float3 waveMove = float3 (0,0,0);
	waveMove.x = dot (s, _waveXmove);
	waveMove.z = dot (s, _waveZmove);
	
	v.vertex.xz -= mul ((float3x3)_World2Object, waveMove).xz;
	v.vertex.xz += bendDir.xz*distMulti*v.texcoord.y*v.texcoord.y*1.5; //ADD OBSTACLE BENDING
	
	o.pos = mul(glstate.matrix.mvp, v.vertex);
	o.fog = o.pos.w;
	o.uv = v.texcoord;
	o.color = lerp (_Color, _Color2, fade.xxxx);
//	o.color *= _SkyLight;
	return o;
}
ENDCG
			AlphaTest Greater [_Cutoff]
			Cull Off
			SetTexture [_MainTex] { combine texture * primary double, texture}
			
		}
	}	
	SubShader {	
		Tags { "Queue" = "Transparent" }
		Pass {
			Blend SrcAlpha oneMinusSrcAlpha
			AlphaTest Greater [_Cutoff]
			Cull Off
			color [_Color]
			ZTest Less
			SetTexture [_MainTex] { combine texture * primary DOUBLE, texture}
		}

	}
}

/P

1 Like

Awesome. Thanks a lot for sharing and also for explaining it. Much appreciated. :wink:

-Jeremy

Would love to see implementations or variations of it :slight_smile:

/P

Can someone make a unity package as I am unsure how to se this up… It looks awesome though!

Here’s a version that works in Unity 2.5. Please note, I customized it to my own situation, which means the texture has been removed.

Shader "Dynamic Grass"
{
	Properties
	{
		_Color ("Main Color", Color) = (.055, .094, .082, 1)
		_Color2 ("Fade Color", Color) = (.09, .157, .137, 1)
		_WaveX ("Wave X", Vector) = (.07, .01, .01, 0.01)
		_WaveZ ("Wave Z", Vector) = (-.07, .01, .01, 0.01)
		_WaveSpeed ("Wave Speed", Vector) = (1, 1.5, 1.1, .9)
	}
	SubShader
	{
		Tags { "Queue" = "Transparent" }
		
		Pass
		{
			Lighting On
			
			CGPROGRAM
			#pragma vertex Vertex
			
			#include "waves.cginc"      // Get in the helper wave functions 
			#include "UnityCG.cginc"    // Get standard Unity constants 
			
			struct v2f
			{
				float4 pos : POSITION; 
				float4 color : COLOR0; 
				float4 uv : TEXCOORD0; 
				float fog : FOGC; 
			}; 
			
			uniform float4 _Color, _Color2, _WaveX, _WaveZ, _WaveSpeed;
			uniform float3 _obstacle;
			uniform float _affectDist, _bendAmount;
			
			v2f Vertex(appdata_base v)
			{ 
				v2f o;
				
				const float4 _waveXSize = _WaveX * 2; 
				const float4 _waveZSize = _WaveZ * 2; 
				const float4 waveSpeed = _WaveSpeed * 4; 
				
				float4 _waveXmove = _waveXSize * waveSpeed * 25; 
				float4 _waveZmove = _waveZSize * waveSpeed * 25;
				
				// Calculate the wind input to leaves from their vertex positions.... 
				float3 worldPos = mul ((float3x4)_Object2World, v.vertex); 
				 
				// OBSTACLE AVOIDANCE CALC 
				float3 bendDir = normalize (float3(worldPos.x,0,worldPos.z) - float3(_obstacle.x,0,_obstacle.z));//direction of obstacle bend
				float distLimit = _affectDist;// how far away does obstacle reach 
				float distMulti = (distLimit-min(distLimit,distance(float3(worldPos.x,0,worldPos.z),float3(_obstacle.x,0,_obstacle.z))))/distLimit; //distance falloff 
				//OBSTACLE AVOIDANCE END 
				 
				// This is the input to the sinusiodal warp 
				float4 waves; 
				waves = worldPos.x * _waveXSize; 
				waves += worldPos.z * _waveZSize; 
				
				// Add in time to model them over time 
				waves += _Time.x * waveSpeed; 
				
				float4 s, c; 
				waves = frac (waves); 
				FastSinCos (waves, s,c); 
				
				float waveAmount = v.texcoord.y; 
				s *= waveAmount; 
				
				// Faste winds move the grass more than slow winds 
				s *= normalize (waveSpeed); 
				
				s = s * s;
				float fade = dot (v.texcoord.y * v.texcoord.y, 2); 
				s = s * s; 
				float3 waveMove = float3 (0,0,0); 
				waveMove.x = dot (s, _waveXmove); 
				waveMove.z = dot (s, _waveZmove); 
				 
				v.vertex.xz -= mul ((float3x3)_World2Object, waveMove).xz; 
				v.vertex.xz += bendDir.xz * distMulti * v.texcoord.y * v.texcoord.y * _bendAmount; //ADD OBSTACLE BENDING
				
				o.pos = mul(glstate.matrix.mvp, v.vertex); 
				o.fog = o.pos.w; 
				o.uv = v.texcoord; 
				o.color = lerp (_Color, _Color2, fade.xxxx);
				return o; 
			} 
			ENDCG
			
			AlphaTest Greater [_Cutoff]
			Cull Off
		}
	}
	
	SubShader
	{
		Tags { "Queue" = "Transparent" }
		Pass
		{
			Blend SrcAlpha oneMinusSrcAlpha
			AlphaTest Greater [_Cutoff]
			Cull Off
			color [_Color]
			ZTest Less
		}
	}
}

This script is attached to a cube that acts as my obstacle. I’ve rigged it up so that it increases how far it affects the grass and how much the grass bends when you’re pressing the Jump button.

#pragma strict

@script ExecuteInEditMode

var affectDist : float = 8;
var bendAmount : float = 5;

private var myTransform : Transform;

function Awake()
{
	SetBend();
	
	myTransform = transform;
}

function Update() : void
{
	Shader.SetGlobalVector("_obstacle", myTransform.position);
	
	if (Input.GetButton("Jump"))
	{
		if (affectDist < 22)
		{
			affectDist += 3;
			bendAmount += 0.3;
			
			SetBend(affectDist, bendAmount);
		}
		
		if (affectDist > 20)
		{
			affectDist = 20;
			bendAmount = 6.2;
			
			SetBend(affectDist, bendAmount);
		}
	}
	else
	{
		if (affectDist > 8)
		{
			affectDist -= 4;
			bendAmount -= 0.6;
			
			SetBend(affectDist, bendAmount);
		}
		else
		{
			SetBend();
		}
	}
}

function SetBend(_affectDist : float, _bendAmount : float) : void
{
	Shader.SetGlobalFloat("_affectDist", _affectDist);
	Shader.SetGlobalFloat("_bendAmount", _bendAmount);
}

function SetBend() : void
{
	SetBend(8, 5);
}

IMPORTANT: The values here are set to blades of grass that are approximately 1 unit wide and 8 units tall or so. It’s very important that the grass blade mesh has UV coordinates such that the grass blade looks “upright,” which a planar projection along the Z would get you. A screenshot is attached. Note, that cube is a default cube created by Unity.

171135--6151--$picture_1_591.png

I am having trouble getting the shader to work. When I use the shader on my planes no color is added and they remain invisible. Would it be possible by any chance for you to upload the Unity project?

many thanks,
Lars

Im also having problems with the shader aswell. I cant seem to get either of the shaders working either.

I’ve attached the waves.cginc file, which I have in my Materials folder along with the shader.

Dynamic Grass shader applied as a material to grass blade meshes with UV coordinates with optional bending on the y:

Shader "Dynamic Grass"
{
	Properties
	{
		_Color ("Main Color", Color) = (.055, .094, .082, 1)
		_Color2 ("Fade Color", Color) = (.09, .157, .137, 1)
		_WaveX ("Wave X", Vector) = (.07, .01, .01, 0.01)
		_WaveZ ("Wave Z", Vector) = (-.07, .01, .01, 0.01)
		_WaveSpeed ("Wave Speed", Vector) = (1, 1.5, 1.1, .9)
	}
	SubShader
	{
		Tags { "Queue" = "Transparent" }
		
		Pass
		{
			Lighting On
			
			CGPROGRAM
			#pragma vertex Vertex
			
			#include "waves.cginc"      // Get in the helper wave functions 
			#include "UnityCG.cginc"    // Get standard Unity constants 
			
			struct v2f
			{
				float4 pos : POSITION; 
				float4 color : COLOR0; 
				float4 uv : TEXCOORD0; 
				float fog : FOGC; 
			}; 
			
			uniform float4 _Color, _Color2, _WaveX, _WaveZ, _WaveSpeed;
			uniform float3 _obstacle;
			uniform float _affectDist, _bendAmount;
			
			v2f Vertex(appdata_base v)
			{ 
				v2f o;
				
				const float4 _waveXSize = _WaveX * 2; 
				const float4 _waveZSize = _WaveZ * 2; 
				const float4 waveSpeed = _WaveSpeed * 4; 
				
				float4 _waveXmove = _waveXSize * waveSpeed * 25; 
				float4 _waveZmove = _waveZSize * waveSpeed * 25;
				
				// Calculate the wind input to leaves from their vertex positions.... 
				float3 worldPos = mul ((float3x4)_Object2World, v.vertex); 
				 
				// OBSTACLE AVOIDANCE CALC 
				float3 bendDir = normalize (float3(worldPos.x,worldPos.y,worldPos.z) - float3(_obstacle.x,_obstacle.y,_obstacle.z));//direction of obstacle bend
				float distLimit = _affectDist;// how far away does obstacle reach 
				float distMulti = (distLimit-min(distLimit,distance(float3(worldPos.x,worldPos.y,worldPos.z),float3(_obstacle.x,_obstacle.y,_obstacle.z))))/distLimit; //distance falloff 
				//OBSTACLE AVOIDANCE END 
				 
				// This is the input to the sinusiodal warp 
				float4 waves; 
				waves = worldPos.x * _waveXSize; 
				waves += worldPos.z * _waveZSize; 
				
				// Add in time to model them over time 
				waves += _Time.x * waveSpeed; 
				
				float4 s, c; 
				waves = frac (waves); 
				FastSinCos (waves, s,c); 
				
				float waveAmount = v.texcoord.y; 
				s *= waveAmount; 
				
				// Faste winds move the grass more than slow winds 
				s *= normalize (waveSpeed); 
				
				s = s * s;
				float fade = dot (v.texcoord.y * v.texcoord.y, 2); 
				s = s * s; 
				float3 waveMove = float3 (0,0,0); 
				waveMove.x = dot (s, _waveXmove); 
				waveMove.z = dot (s, _waveZmove); 
				 
				v.vertex.xz -= mul ((float3x3)_World2Object, waveMove).xz; 
				v.vertex.xz += bendDir.xz * distMulti * v.texcoord.y * v.texcoord.y * _bendAmount; //ADD OBSTACLE BENDING
				
				//optional y bending
				//v.vertex.xyz += bendDir.xyz * distMulti * v.texcoord.y * v.texcoord.y * _bendAmount; //ADD OBSTACLE BENDING
				
				o.pos = mul(glstate.matrix.mvp, v.vertex); 
				o.fog = o.pos.w; 
				o.uv = v.texcoord; 
				o.color = lerp (_Color, _Color2, fade.xxxx);
				return o; 
			} 
			ENDCG
			
			AlphaTest Greater [_Cutoff]
			Cull Off
		}
	}
	
	SubShader
	{
		Tags { "Queue" = "Transparent" }
		Pass
		{
			Blend SrcAlpha oneMinusSrcAlpha
			AlphaTest Greater [_Cutoff]
			Cull Off
			color [_Color]
			ZTest Less
		}
	}
}

Bend affect script attached to obstacle object:

#pragma strict

@script ExecuteInEditMode

var affectDist : float = 20;
var bendAmount : float = 5;

private var myTransform : Transform;

function Awake()
{	
	myTransform = transform;
}

function Update()
{
	Shader.SetGlobalVector("_obstacle", myTransform.position);
	
	Shader.SetGlobalFloat("_affectDist", _affectDist);
	Shader.SetGlobalFloat("_bendAmount", _bendAmount);
}

186152–6591–$wavescginc_165.zip (1.21 KB)

Hello,

sorry for digging into this old thread.

But is there a way to add shadow receiving to this shader?

I found this: http://wiki.unity3d.com/index.php?title=TransparentShadowReceiver

but combining it with this shader results in shadow casting, no receiving and no deformation anymore.

Thanks.

if i see another float in a frag shader where id doesn’t need to be i think i will exorcist vomit

hi, sorry to necro this…
i’m trying to get collision info in a shader and the examples i see always rely on old unity versions
i’m on 5 and Wonder witch changes have been made in code to deal with collisions et setglobalvector

i use this code with no success :

collisionInfo.gameObject.GetComponent().material.SetVector(“_CollisionPoint”, contact.point);

thanks

This seems to not be working at all on unity 5 Terrain, sad face :cry:

Probably something is done differently on the Grass Shader itself, when you do it line by line, you see that everything gets weird when you multiply wordPos * waveXsizes instead of vertex * waveXsizes…

waves = worldPos.x * _waveXSize;    
waves += worldPos.z * _waveZSize;

    waves = vertex.x * _waveXSize;
    waves += vertex.z * _waveZSize;