need help with a rather specific shader solution

A large gameplay element of my game is orbiting around planets with a ship, and I use grids that are distorted like a space time field to illustrate that to make orbiting easier for the player. Here is a screenshot of what I have so far…

Right now, however, this is done by shifting the vertices of several grid pieces, planes that are stitched together in the editor window. This is pretty inefficient, as even the planes that need to be updated (the ones that are unaffected lie dormant) have to run through each of their vertices every frame to see where they should be stretched.

A friend mentioned that this would be enormously faster if I did this with a fragment shader. Now, I’m fairly new to programming and haven’t really used shader lab or cg before. I would very greatly appreciate any help with this, if someone could walk me through it or had the time to even write a rudimentary shader for me.

The shader needs to expand the texture near gameObjects in the world, and should expand more or less based on the strength of the object, which would be assigned in a separate script attached to that gameObject. The alpha of the plane would need to increase (become less opaque) as the amount of stretch decreased, and would eventually fade to be completely transparent.

Depending how dense the plane is, you might be better doing this in the vertex shader.

I need to write a grass shader anyway, that my character can brush out the way as she walks past and I think the system’d be similar (pass in a world coordinate and a radius, make the verts avoid the point) so I’ll give it a go.

thank you so much… the plane should be about as dense as it is in that picture. (8x8 polygons each object) maybe a little more. also, it’s highly critical that I can control the number of points to be avoided and the radius of each from outside the shader.

if this helps at all, in the function in my script that I have now has 5 values…

r, the radius to be effected
amp, the amplitude of how much it should pull the vertices
exp, the exponent that factors how the pull looks (convex if exponent is lower than 1, concave if exponent is higher than 1) and how the steepness of the curve.
x, which is irrelevant to you, just determines the x coordinate of the point that is affecting the grid
z, which works the same way as x on the z axis

I hope that helps

The issue is that you’re likely to end up with a limited amount of points if you use a shader.

there shouldn’t need to be that many points in any plane at a given time…maybe 5 or 6? There are going to be a lot of small pieces to the grid instead of one large mesh. However, I don’t understand why there would be a limited amount of points, that doesn’t make sense

Well, you gotta pas varibles into the shader, position, radius, strength, etc.

As it’s not possible to pass arrays in, there’s only so many things you can pass in and loops you can run through before it gets silly.

alright. well, It’s worth a shot at least. I wouldn’t be able to tell you how to do it from there, because my script uses an array for every object on that current grid piece. I don’t think it will matter if its not able to detect an unlimited amount of items in front of it, but the current solution right now is as optimized as I believe it can be, so i think its definitely worth trying

Yep, will get a chance to play tomorrow lunch time, will let you know how it goes.

awesome, thanks man

Just a suggestion but what if you rendered a warp vector to a render texture via something like an additive sprite/plane then used that to warp your screen?

You could have as many influencing objects as you like until the fill-rate becomes too much. My guess is though you might get away with a render texture thats half your full screen rez or less.

no, i haven’t, and I’m really unsure how to do that…could you explicate on that?

Have you got Unity Pro?

If so, that could be an easier way to do it.

Here’s what I have so far…

Apply this to an object as a javascript.

public var gravityRadius : float = 2;

function Update () {
	Shader.SetGlobalVector( "_GravityPoint", Vector4(transform.position.x, transform.position.y, transform.position.z, 1.0) );
	Shader.SetGlobalFloat( "_GravityRadius", gravityRadius );
}

Apply this to your grid as a shader.

Shader "Vertex Point Avoidance" {
	Properties {
		_Color ("Main Color", Color) = (1,1,1,1)
		_MainTex ("Diffuse (RGB) Alpha (A)", 2D) = "white" {}
	}

	SubShader{
		Tags { "RenderType" = "Opaque" }

		CGPROGRAM
			#pragma surface surf Lambert vertex:vert

			struct Input
			{
				float2 uv_MainTex;
				float3 distToPoint;
			};

			float4x4 _WorldToObject;
			float4x4 _ObjectToWorld;
			
			float4 _GravityPoint;
			float _GravityRadius;
			
			void vert (inout appdata_full v, out Input o) {				
				float4 gravityPoint = mul(_WorldToObject, _GravityPoint);
				float4 vpos = v.vertex;
				
				float3 vVecToPoint = vpos.xyz - gravityPoint.xyz;
				float vDistToPoint = 1 - (min(length(vVecToPoint), _GravityRadius) / _GravityRadius);
				
				v.vertex = vpos + float4(normalize(vVecToPoint) * vDistToPoint * _GravityRadius, 0);
				
				v.normal += normalize(-vVecToPoint) * vDistToPoint;
				v.normal = normalize(v.normal);
				
				o.distToPoint = normalize(v.normal);
			}

			sampler2D _MainTex;

			void surf (Input IN, inout SurfaceOutput o)
			{
				fixed4 albedo = tex2D(_MainTex, IN.uv_MainTex);
				o.Albedo = albedo.rgb;
				o.Alpha = albedo.a;
			}
		ENDCG
	}
	FallBack "VertexLit"
}

Not that I am in position to teach you shaders, but I think you can get rid of 2 normalize()

			void vert (inout appdata_full v, out Input o) {				
				float4 gravityPoint = mul(_WorldToObject, _GravityPoint);
				float4 vpos = v.vertex;
				
				float3 vVecToPoint = vpos.xyz - gravityPoint.xyz;
				float vDistToPoint = 1 - (min(length(vVecToPoint), _GravityRadius) / _GravityRadius);
				
				v.vertex = vpos + float4(normalize(vVecToPoint) * vDistToPoint * _GravityRadius, 0);
				
				v.normal += -vVecToPoint * vDistToPoint;
				v.normal = normalize(v.normal);
				
				o.distToPoint = v.normal;
			}

Oh, that last one was a debug thing I forgot to remove. Just checking I’d edited the normals correctly for the shading.

First one’s well spotted, ta :slight_smile:

Think this is probably similar to what you’re after… only handles one object at the moment.

public var gravityRadius : float = 2;
public var gravityAmplitude : float = 2;
public var gravityExponent : float = 2;

function Update () {
	Shader.SetGlobalVector( "_GravityPoint", Vector4(transform.position.x, transform.position.y, transform.position.z, 1.0) );
	Shader.SetGlobalFloat( "_GravityRadius", gravityRadius );
	Shader.SetGlobalFloat( "_GravityAmplitude", gravityAmplitude );
	Shader.SetGlobalFloat( "_GravityExponent", gravityExponent );
}
Shader "Gravity Warp" {
	Properties {
		_Color ("Main Color", Color) = (1,1,1,1)
		_MainTex ("Diffuse (RGB) Alpha (A)", 2D) = "white" {}
		_MinAlpha ("Minimum Alpha", Range(0,1)) = 0.1
	}

	SubShader{
		Tags { "RenderType" = "Opaque" }

		CGPROGRAM
			#pragma surface surf Lambert vertex:vert alpha

			struct Input
			{
				float2 uv_MainTex;
				float3 vertexAlpha;
			};

			float4x4 _WorldToObject;
			
			float4 _GravityPoint;
			float _GravityRadius;
			float _GravityAmplitude;
			float _GravityExponent;
			float _MinAlpha;
			
			void vert (inout appdata_full v, out Input o) {				
				float4 gravityPoint = mul(_WorldToObject, _GravityPoint);
				float4 vpos = v.vertex;
				
				float3 vVecToPoint = float3(vpos.x - gravityPoint.x, 0, vpos.z - gravityPoint.z);
				
				float vDistToPoint = 1 - (min(length(vVecToPoint), _GravityRadius) / _GravityRadius);
				
				float3 offset = normalize(vVecToPoint) * _GravityAmplitude * _GravityRadius * vDistToPoint;
				
				v.vertex = vpos + float4(offset, 0);
				
				v.normal = normalize(v.normal + (normalize(-offset) * vDistToPoint * abs(_GravityAmplitude) * (1 - _GravityExponent)));
				
				o.vertexAlpha = vDistToPoint;
			}

			sampler2D _MainTex;

			void surf (Input IN, inout SurfaceOutput o)
			{
				fixed4 albedo = tex2D(_MainTex, IN.uv_MainTex);
				o.Albedo = albedo.rgb;
				o.Alpha = max(_MinAlpha, albedo.a * IN.vertexAlpha);
			}
		ENDCG
	}
	FallBack "VertexLit"
}

thanks for the help guys. And yes, I have pro. Which of these snippets would you guys recommend using with the grid? Is there any way I can get them to handle more than one object? That is crucial.

The latest one I posted is the best at the moment.

Yeah, I’ll add in support for multiple objects - just needed to make sure that 1 object works the way you want it first, before adding others.

If you have pro, however, there may be easier methods, especially if your game is orthographic top-down as it appears.

yup it sure is orthographic/top down. I’m going to give this a shot, we’ll see how it works

update: works perfectly. I only wish there was a way I could set the radius and falloff of the alpha…right now its a bit of a harsh line for something small like the player object.

What other options do I have, since I’m using pro and the game is orthographic?