Simple Wind (vegetation) Shader

Wind is one way to improve the scenery with vegetation. Thus, my first shader focuses on horizontal force influence. The target is to move trees, plants, etc. horizontally (in a kinda wavy form) to simulate wind. (Empty template reused)

Fail 1

My first thought was to shift the texCoord in the vertex shader.

VertexShaderOutput VertexShaderLogic(float4 position : SV_POSITION, float4 color : COLOR0, float2 texCoord : TEXCOORD0)
{
	VertexShaderOutput output = (VertexShaderOutput)0;

	output.Position = position;

	output.Position = mul(output.Position, viewMatrix);
	output.Position = mul(output.Position, projectionMatrix);
	output.TexCoord = texCoord;
	output.Color = color;

	if (texCoord.y < bendEnd)
		output.TexCoord.x += minValue + sin(gameTime) * strength;

	return output;
}

The pixel start to disappear on the right side and they expand on the left side. The Texture2D has a width and height limitation. Therefore, it is impossible to draw outside of the verticies.

Fail 2

Well, so, maybe I move my logic into the PixelShader? Maybe the Texture2D will expand itself magically?

PixelShaderOutput PixelShaderFunction(VertexShaderOutput input) : COLOR0
{
	PixelShaderOutput output = (PixelShaderOutput)0;

	if (input.TexCoord.y < bendEnd)
		input.TexCoord.x += minValue + sin(gameTime) * strength;

	output.Color = tex2D(SpriteTextureSampler, input.TexCoord) * input.Color;

	return output;
}

No idea what was I expecting… However, the Vertex Shader is the way to go. (At least that is my last option)

Success

Instead of moving the texture coordinate, lets move the vertex positions.

VertexShaderOutput VertexShaderLogic(float4 position : SV_POSITION, float4 color : COLOR0, float2 texCoord : TEXCOORD0)
{
	VertexShaderOutput output = (VertexShaderOutput)0;

	output.Position = position;

	if (texCoord.y < bendEnd)
		output.Position.x += minValue + sin(gameTime) * strength;

	output.Position = mul(output.Position, viewMatrix);
	output.Position = mul(output.Position, projectionMatrix);
	output.TexCoord = texCoord;
	output.Color = color;

	return output;
}

The texCoord contains values (normalized) from 0..1. With the if statement, we transform all vertices below the bendEnd. But why is the tree bending, regardless of the bendEnd value? Lets look at the mesh of a Texture2D

The Texture2D consists of 2 triangles (T1 and T2) and made up by the verticies (V1, V2, V3 and V4). The only verticies we are able to modify are, therefore, only 4. V1 equals to texCoord 0 and V3 equals the texCoord 1. No matter what value bendEnd contains (0..0.99), we can only transform V1 and V2, leading to the visible effect above. In order to achieve an even better result, a mesh with a higher resolution would be required.

Source

#if OPENGL
	#define SV_POSITION POSITION
	#define VS_SHADERMODEL vs_3_0
	#define PS_SHADERMODEL ps_3_0
#else
	#define VS_SHADERMODEL vs_4_0_level_9_1
	#define PS_SHADERMODEL ps_4_0_level_9_1
#endif

Texture2D SpriteTexture;
sampler2D SpriteTextureSampler = sampler_state
{
	Texture = <SpriteTexture>;
};

float4x4 projectionMatrix;
float4x4 viewMatrix;

float gameTime = 1.0;
float bendEnd = 0.5;
float minValue = 0.1;
float strength = 0.005;

struct PixelShaderOutput
{
	float4 Color: COLOR0;
};

struct VertexShaderOutput
{
	float4 Position : SV_POSITION;
	float2 TexCoord : TEXCOORD0;
	float4 Color: COLOR0;
};

VertexShaderOutput VertexShaderLogic(float4 position : SV_POSITION, float4 color : COLOR0, float2 texCoord : TEXCOORD0)
{
	VertexShaderOutput output = (VertexShaderOutput)0;

	output.Position = position;

	if (texCoord.y < 1)
		output.Position.x += minValue + sin(gameTime) * strength;

	output.Position = mul(output.Position, viewMatrix);
	output.Position = mul(output.Position, projectionMatrix);
	output.TexCoord = texCoord;
	output.Color = color;

	return output;
}

PixelShaderOutput PixelShaderFunction(VertexShaderOutput input) : COLOR0
{
	PixelShaderOutput output = (PixelShaderOutput)0;

	output.Color = tex2D(SpriteTextureSampler, input.TexCoord) * input.Color;

	return output;
}

technique SpriteDrawing
{
	pass P0
	{
		VertexShader = compile VS_SHADERMODEL VertexShaderLogic();
		PixelShader = compile PS_SHADERMODEL PixelShaderFunction();
	}
};

C#

WindShader.Parameters["projectionMatrix"].SetValue(Program.GetComponent<CameraComponent>().ProjectionMatrix);
WindShader.Parameters["viewMatrix"].SetValue(Program.GetComponent<CameraComponent>().ViewMatrix);
WindShader.Parameters["gameTime"].SetValue(gameTime.ToShaderFloat());
WindShader.Parameters["bendEnd"].SetValue(0.5f);
WindShader.Parameters["strength"].SetValue(2f);
WindShader.Parameters["minValue"].SetValue(15f);

spriteBatch.Begin(SpriteSortMode.Deferred, effect: WindShader);
spriteBatch.Draw(Tree, new Vector2(100, 200), Color.White);
spriteBatch.Draw(Tree, new Vector2(400, 200), Color.White);
spriteBatch.End();

 

0 0 vote
Article Rating
Subscribe
Notify of
0 Comments
Inline Feedbacks
View all comments